diff --git a/src/MinecraftServer.ts b/src/MinecraftServer.ts index 4d2cdde..0ac3299 100644 --- a/src/MinecraftServer.ts +++ b/src/MinecraftServer.ts @@ -1,12 +1,14 @@ import { PacketManager } from "./utils/PacketManager"; import {PlayerManager} from "./utils/PlayerManager"; -import {Server} from "node:net"; +import {Server} from "node:net"; // TODO: Replace with https://bun.sh/docs/api/tcp import {PacketDisconnectKick} from "./packet/impl/player/PacketDisconnectKick"; +import {Player} from "./Player"; export class MinecraftServer { public server: Server = new Server(); public static debug: boolean = true; + public static PlayerManagers = new Map(); // EntityMap = new Map(); public async start() { @@ -18,7 +20,7 @@ export class MinecraftServer { this.server.on('connection', socket => { if(MinecraftServer.debug) console.log('New connection!'); - PlayerManager.handleConnection(socket); + new PlayerManager("Unknown", socket).handleConnection(); }); this.server.on('close', this.stop); diff --git a/src/Player.ts b/src/Player.ts index ee70bfb..17e0001 100644 --- a/src/Player.ts +++ b/src/Player.ts @@ -1,8 +1,12 @@ import {Socket} from "node:net"; import {IEntity} from "./IEntity"; +import {PlayerManager} from "./utils/PlayerManager"; export class Player implements IEntity { public entityID: Number; + public username: String; + public socket: Socket; + public playerManager: PlayerManager; public xPosition: number = 0; public yPosition: number = 100; @@ -12,10 +16,9 @@ export class Player implements IEntity { public pitch: number = 0; public onGround: boolean = false; - constructor(public options: IPlayerOption) {} -} - -export interface IPlayerOption { - client: Socket, - username: String + constructor(username: String, socket: Socket, playerManager: PlayerManager) { + this.username = username; + this.socket = socket; + this.playerManager = playerManager; + } } \ No newline at end of file diff --git a/src/packet/Packet.ts b/src/packet/Packet.ts index bac4f01..4fa03ac 100644 --- a/src/packet/Packet.ts +++ b/src/packet/Packet.ts @@ -18,5 +18,4 @@ export interface IPacketOption { player?: Player, packetID: PacketEnum, name?: string, - kickReason?: string } \ No newline at end of file diff --git a/src/packet/impl/PacketKeepAlive.ts b/src/packet/impl/PacketKeepAlive.ts index b72a670..a86bf45 100644 --- a/src/packet/impl/PacketKeepAlive.ts +++ b/src/packet/impl/PacketKeepAlive.ts @@ -12,7 +12,7 @@ export class PacketKeepAlive extends Packet { } readData(reader: IReader, player: Player) { - PlayerManager.sendPacket(player, this); + player.playerManager.sendPacket(this); } writeData() { diff --git a/src/packet/impl/login/PacketHandshake.ts b/src/packet/impl/login/PacketHandshake.ts index cc59636..d69d3d1 100644 --- a/src/packet/impl/login/PacketHandshake.ts +++ b/src/packet/impl/login/PacketHandshake.ts @@ -4,9 +4,11 @@ import {createWriter, Endian, IReader} from "bufferstuff"; import {Player} from "../../../Player"; import {randomInt} from "node:crypto"; import {PlayerManager} from "../../../utils/PlayerManager"; +import {MinecraftServer} from "../../../MinecraftServer"; +import {Socket} from "node:net"; export class PacketHandshake extends Packet { - constructor() { + constructor(public playerManager: PlayerManager) { super({ packetID: PacketEnum.Handshake }) @@ -14,22 +16,27 @@ export class PacketHandshake extends Packet { readData(reader: IReader, player: Player) { const username = reader.readString16(); + + player = this.playerManager.player; + this.playerManager.username = username; + player.username = username; + if(PlayerManager.getPlayer(username)) { - PlayerManager.kickPlayer(player, `Player with same username is already on the server.`); + player.playerManager.kickPlayer(`Player with same username is already on the server.`); return; } if(username.length > 16) { - PlayerManager.kickPlayer(player, `Your username is too long!`); + player.playerManager.kickPlayer(`Your username is too long!`); return; } - player.options.username = username; + player.username = username; player.entityID = randomInt(0, 100); - PlayerManager.SocketMap.set(username, player.options.client); + MinecraftServer.PlayerManagers.set(username, player.playerManager); - PlayerManager.sendPacket(player, this); + player.playerManager.sendPacket(this); } writeData() { diff --git a/src/packet/impl/login/PacketLogin.ts b/src/packet/impl/login/PacketLogin.ts index 719fac8..6459006 100644 --- a/src/packet/impl/login/PacketLogin.ts +++ b/src/packet/impl/login/PacketLogin.ts @@ -7,8 +7,10 @@ import {PacketChat} from "../player/PacketChat"; import {PlayerManager} from "../../../utils/PlayerManager"; import {PacketPreChunk} from "../world/PacketPreChunk"; import {PacketMapChunk} from "../world/PacketMapChunk"; +import {PacketPosition} from "../player/PacketPosition"; +import {PacketPositionLook} from "../player/PacketPositionLook"; -export class PacketHandshake extends Packet { +export class PacketLogin extends Packet { constructor() { super({ packetID: PacketEnum.Login @@ -17,18 +19,18 @@ export class PacketHandshake extends Packet { readData(reader: IReader, player: Player) { const protocol = reader.readInt(); - if(protocol > 14) PlayerManager.kickPlayer(player, `Server is outdated!`); - else if (protocol < 14) PlayerManager.kickPlayer(player, `Client is outdated!`); + if(protocol > 14) player.playerManager.kickPlayer(`Server is outdated!`); + else if (protocol < 14) player.playerManager.kickPlayer(`Client is outdated!`); const username = reader.readString16(); const seed = reader.readLong(); const dimension = reader.readByte(); - PlayerManager.sendPacket(player, this); - PlayerManager.sendPacket(player, PacketManager.getPacket(PacketEnum.PositionLook)); + player.playerManager.sendPacket(this); + player.playerManager.sendPacket(new PacketPositionLook()); PlayerManager.sendPacketToAll(new PacketChat(`§e<${username}> has joined the game.`)); - PlayerManager.sendPacket(player, new PacketPreChunk()); - PlayerManager.sendPacket(player, new PacketMapChunk()); + player.playerManager.sendPacket(new PacketPreChunk()); + player.playerManager.sendPacket(new PacketMapChunk()); } writeData() { diff --git a/src/packet/impl/login/PacketServerList.ts b/src/packet/impl/login/PacketServerList.ts index 0c2dd60..342806d 100644 --- a/src/packet/impl/login/PacketServerList.ts +++ b/src/packet/impl/login/PacketServerList.ts @@ -12,7 +12,7 @@ export class PacketServerList extends Packet { } readData(reader: IReader, player: Player) { - PlayerManager.kickPlayer(player, `Beta 1.7.3 Server§0§0`); + player.playerManager.kickPlayer(`Beta 1.7.3 Server§0§0`); } writeData() { diff --git a/src/packet/impl/player/PacketChat.ts b/src/packet/impl/player/PacketChat.ts index d5709e0..14befea 100644 --- a/src/packet/impl/player/PacketChat.ts +++ b/src/packet/impl/player/PacketChat.ts @@ -13,7 +13,7 @@ export class PacketChat extends Packet { } readData(reader: IReader, player: Player) { - this.message = `<${player.options.username}> ${reader.readString16()}` + this.message = `<${player.username}> ${reader.readString16()}` PlayerManager.sendPacketToAll(this); } diff --git a/src/packet/impl/player/PacketDisconnectKick.ts b/src/packet/impl/player/PacketDisconnectKick.ts index 0a5ad39..b86bdb3 100644 --- a/src/packet/impl/player/PacketDisconnectKick.ts +++ b/src/packet/impl/player/PacketDisconnectKick.ts @@ -12,7 +12,7 @@ export class PacketDisconnectKick extends Packet { } readData(reader: IReader, player: Player) { - PlayerManager.playerDisconnected(player.options.client); + player.playerManager.playerDisconnected(); } writeData() { diff --git a/src/packet/impl/player/PacketPositionLook.ts b/src/packet/impl/player/PacketPositionLook.ts index a1a63fb..29d144b 100644 --- a/src/packet/impl/player/PacketPositionLook.ts +++ b/src/packet/impl/player/PacketPositionLook.ts @@ -3,7 +3,7 @@ import {PacketEnum} from "../../../utils/PacketEnum"; import {createWriter, Endian, IReader} from "bufferstuff"; import {Player} from "../../../Player"; -export class PacketKick extends Packet { +export class PacketPositionLook extends Packet { constructor() { super({ packetID: PacketEnum.PositionLook diff --git a/src/utils/PlayerManager.ts b/src/utils/PlayerManager.ts index c82c7dd..f24bac3 100644 --- a/src/utils/PlayerManager.ts +++ b/src/utils/PlayerManager.ts @@ -6,80 +6,74 @@ import {Socket} from "node:net"; import {createReader, Endian} from "bufferstuff"; import {PacketManager} from "./PacketManager"; import {MinecraftServer} from "../MinecraftServer"; +import {PacketEnum} from "./PacketEnum"; +import {PacketHandshake} from "../packet/impl/login/PacketHandshake"; export class PlayerManager { - static PlayerMap = new Map(); - static SocketMap = new Map(); + public player: Player; - public static handleConnection(socket: Socket) { - if(!PlayerManager.PlayerMap.has(socket)) PlayerManager.PlayerMap.set(socket, new Player({ - client: socket, - username: "Unknown" - })); + constructor(public username: string, public socket: Socket) { + this.player = new Player(username, socket, this); + } - socket.on('data', data => { + public handleConnection() { + this.socket.on('data', data => { const reader = createReader(Endian.BE, data); const packetID = reader.readUByte(); - let packet: Packet; - packet = PacketManager.getPacket(packetID); - - if(packet === undefined) { - if(MinecraftServer.debug) console.log(`Received Unknown packet: ${packetID}. Kicking the player.`); - PlayerManager.kickPlayer(PlayerManager.PlayerMap.get(socket), `Sent unknown packet ${packetID}`); + if(packetID == PacketEnum.Handshake) { + new PacketHandshake(this).readData(reader, null); return; } - if(MinecraftServer.debug) console.log(`Received Packet: ${packet.options.name} (${packetID})`); + let packet: Packet = PacketManager.getPacket(packetID); - packet.readData(reader, PlayerManager.PlayerMap.get(socket)); + if(packet === undefined) { + if(MinecraftServer.debug) console.log(`Received Unknown packet: ${packetID}. Kicking the player.`); + this.kickPlayer(`Sent unknown packet ${packetID}`); + return; + } + + if(MinecraftServer.debug && packetID != PacketEnum.Position && packetID != PacketEnum.PositionLook) + console.log(`Received Packet: ${packet.options.name} (${packetID})`); + + packet.readData(reader, this.player); }); - socket.on('close', PlayerManager.playerDisconnected); - socket.on('timeout', PlayerManager.playerDisconnected); + this.socket.on('close', () => this.playerDisconnected()); + this.socket.on('timeout', () => this.playerDisconnected()); } - public static sendPacket(player: Player, packet: Packet) { - if(!packet || !player) { - console.log(`Tried to sent packet to player where either player or packet is null!`) + public sendPacket(packet: Packet) { + if(!packet) { + console.log(`Tried to sent null packet!`) return; } - if(MinecraftServer.debug) console.log(`Sending Packet: ${packet.options.name} (${packet.options.packetID}) to ${player.options.username}`); - packet.options.player = player; + if(MinecraftServer.debug) console.log(`Sending Packet: ${packet.options.name} (${packet.options.packetID}) to ${this.username}`); + packet.options.player = this.player; - player.options.client.write(packet.writeData()); + this.player.socket.write(packet.writeData()); } public static sendPacketToAll(packet: Packet) { - PlayerManager.PlayerMap.forEach(player => { - PlayerManager.sendPacket(player, packet); + MinecraftServer.PlayerManagers.forEach(manager => { + manager.sendPacket(packet); }) } - public static kickPlayer(player: Player, reason: string) { - if(!player) { - console.log(`Tried to kick null player!`); - return; - } - - PlayerManager.sendPacket(player, new PacketDisconnectKick(reason)); - this.playerDisconnected(player.options.client); + public kickPlayer(reason: string) { + this.sendPacket(new PacketDisconnectKick(reason)); } - public static playerDisconnected(socket: Socket) { - const player = PlayerManager.PlayerMap.get(socket); - if(!player) return; + public playerDisconnected() { + if(MinecraftServer.debug) console.log(`Player ${this.username} left. Deleting from the map!`); + MinecraftServer.PlayerManagers.delete(this.username); - const username = player.options.username; - if(MinecraftServer.debug) console.log(`Player ${username} left. Deleting from the map!`); - PlayerManager.SocketMap.delete(username); - PlayerManager.PlayerMap.delete(socket); - - PlayerManager.sendPacketToAll(new PacketChat(`§e<${username}> left the game.`)); + PlayerManager.sendPacketToAll(new PacketChat(`§e<${this.username}> left the game.`)); } public static getPlayer(username: String) { - return PlayerManager.PlayerMap.get(PlayerManager.SocketMap.get(username)); + return MinecraftServer.PlayerManagers.get(username)?.player; } } \ No newline at end of file