From 74374cb0d657e125c0562e83d5e60f2ada562850 Mon Sep 17 00:00:00 2001 From: Holly Date: Mon, 4 Sep 2023 23:42:38 +0100 Subject: [PATCH] implement dimension switching --- server/Chunk.ts | 2 -- server/MPClient.ts | 38 ++++++++++++++++++++++++++++++--- server/MinecraftServer.ts | 2 +- server/World.ts | 4 ++++ server/entities/Entity.ts | 13 ++++++++--- server/entities/EntityLiving.ts | 1 - server/entities/Player.ts | 10 +++++++-- server/packets/Respawn.ts | 14 +++++++----- 8 files changed, 67 insertions(+), 17 deletions(-) diff --git a/server/Chunk.ts b/server/Chunk.ts index ec6bcdf..ea5e077 100644 --- a/server/Chunk.ts +++ b/server/Chunk.ts @@ -57,8 +57,6 @@ export class Chunk { public getTopBlockY(x:number, z:number) { let castY = this.MAX_HEIGHT; while (castY-- > 0) { - const blockId = this.getBlockId(x >>> 0, castY, z >>> 0); - console.log(blockId === 0 ? "Air" : Block.blocks[blockId].blockName); if (this.getBlockId(x >>> 0, castY, z >>> 0) !== 0) { break; } diff --git a/server/MPClient.ts b/server/MPClient.ts index 697f68d..36ac5d8 100644 --- a/server/MPClient.ts +++ b/server/MPClient.ts @@ -13,11 +13,13 @@ import { PacketPlayerDigging } from "./packets/PlayerDigging"; import { Player } from "./entities/Player"; import { Socket } from "net"; import { Vec3 } from "./Vec3"; +import { PacketRespawn } from "./packets/Respawn"; +import { PacketSpawnPosition } from "./packets/SpawnPosition"; export class MPClient { private readonly mcServer:MinecraftServer; private readonly socket:Socket; - public readonly entity:Player; + public entity:Player; private diggingAt:Vec3; @@ -77,15 +79,25 @@ export class MPClient { if (message[0].startsWith("/")) { packet.message = ""; if (message[0] === "/tp") { - this.send(new PacketPlayerPositionLook(parseFloat(message[1]), parseFloat(message[2]), parseFloat(message[2]) + 0.62, parseFloat(message[3]), 0, 0, false).writeData()); + const x = this.entity.x = parseFloat(message[1]); + const y = this.entity.y = parseFloat(message[2]); + const z = this.entity.z = parseFloat(message[3]); + this.send(new PacketPlayerPositionLook(x, y, y + 0.62, z, 0, 0, false).writeData()); Console.printInfo(packet.message = `Teleported ${this.entity.username} to ${message[1]} ${message[2]} ${message[3]}`); } else if (message[0] === "/csay") { this.mcServer.sendChatMessage(`[CONSOLE] ${message.slice(1, message.length).join(" ")}`); } else if (message[0] === "/top") { packet.message = `Woosh!`; const topBlock = this.entity.world.getChunk(this.entity.x >> 4, this.entity.z >> 4).getTopBlockY(this.entity.x & 0xf, this.entity.z & 0xf); - console.log(topBlock); this.send(new PacketPlayerPosition(this.entity.x, topBlock + 3, topBlock + 3.62, this.entity.z, false).writeData()); + } else if (message[0] === "/tpx") { + const dimension = parseInt(message[1]); + if (this.mcServer.worlds.has(dimension)) { + packet.message = "\u00a76Switching dimensions..."; + this.switchDimension(dimension); + } else { + packet.message = `\u00a7cNo dimension by id "${dimension} exists!"`; + } } if (packet.message !== "") { @@ -157,6 +169,26 @@ export class MPClient { } } + private switchDimension(dimension:number) { + const world = this.mcServer.worlds.get(dimension); + if (world == undefined) { + return; + } + + this.entity.world.removeEntity(this.entity); + this.entity.world = world; + world.addEntity(this.entity); + + this.send(new PacketRespawn(dimension).writeData()); + //this.send(new PacketSpawnPosition(8, 64, 8).writeData()); + this.entity.x = 8; + this.entity.y = 70; + this.entity.z = 8; + this.send(new PacketPlayerPositionLook(8, 70, 70.62, 8, 0, 0, false).writeData()); + + this.entity.forceUpdatePlayerChunks(); + } + private handleDisconnectKick() { this.socket.end(); } diff --git a/server/MinecraftServer.ts b/server/MinecraftServer.ts index 3c6d5bb..1a9af02 100644 --- a/server/MinecraftServer.ts +++ b/server/MinecraftServer.ts @@ -33,7 +33,7 @@ export class MinecraftServer { private readonly serverClock:NodeJS.Timeout; private tickCounter:number = 0; private clients:FunkyArray; - private worlds:FunkyArray; + public worlds:FunkyArray; public saveManager:WorldSaveManager; private overworld:World; private nether:World; diff --git a/server/World.ts b/server/World.ts index 9747e59..a209153 100644 --- a/server/World.ts +++ b/server/World.ts @@ -61,6 +61,10 @@ export class World { } } } + // Clear player chunk list (they may be switching dimensions) + entity.loadedChunks = new Array(); + entity.justUnloaded = new Array(); + this.players.remove(entity.entityId); } diff --git a/server/entities/Entity.ts b/server/entities/Entity.ts index bd49b09..0588119 100644 --- a/server/entities/Entity.ts +++ b/server/entities/Entity.ts @@ -28,6 +28,8 @@ export class Entity implements IEntity { private lastCrouchState:boolean; private lastFireState:boolean; + private queuedChunkUpdate:boolean; + public constructor(world:World) { this.entityId = Entity.nextEntityId++; @@ -35,7 +37,7 @@ export class Entity implements IEntity { this.world = world; this.x = this.y = this.z = this.lastX = this.lastY = this.lastZ = 0; - this.crouching = this.lastCrouchState = this.lastFireState = false; + this.crouching = this.lastCrouchState = this.lastFireState = this.queuedChunkUpdate = false; this.chunk = world.getChunk(this.x >> 4, this.z >> 4); @@ -92,8 +94,13 @@ export class Entity implements IEntity { updateEntityChunk() { const bitX = this.x >> 4; const bitZ = this.z >> 4; - if (bitX != this.lastX >> 4 || bitZ != this.lastZ >> 4) { - this.chunk = this.world.getChunk(bitX, bitZ); + if (bitX != this.lastX >> 4 || bitZ != this.lastZ >> 4 || this.queuedChunkUpdate) { + if (this.world.chunkExists(bitX, bitZ)) { + this.chunk = this.world.getChunk(bitX, bitZ); + this.queuedChunkUpdate = false; + } else { + this.queuedChunkUpdate = true; + } } } diff --git a/server/entities/EntityLiving.ts b/server/entities/EntityLiving.ts index bfa028d..937c75d 100644 --- a/server/entities/EntityLiving.ts +++ b/server/entities/EntityLiving.ts @@ -101,7 +101,6 @@ export class EntityLiving extends Entity { // Drowning if (this.isInWater()) { - console.log("in water!"); if (this.timeInWater == Number.MIN_SAFE_INTEGER) { this.timeInWater = 320; } diff --git a/server/entities/Player.ts b/server/entities/Player.ts index a8cad27..4559052 100644 --- a/server/entities/Player.ts +++ b/server/entities/Player.ts @@ -7,6 +7,8 @@ import { EntityLiving } from "./EntityLiving"; import { PacketPreChunk } from "../packets/PreChunk"; import { PacketUpdateHealth } from "../packets/UpdateHealth"; +const CHUNK_LOAD_RANGE = 5; + export class Player extends EntityLiving { public username:string; private server:MinecraftServer; @@ -32,6 +34,10 @@ export class Player extends EntityLiving { this.lastHealth = this.health; } + public forceUpdatePlayerChunks() { + this.firstUpdate = true; + } + private async updatePlayerChunks() { const bitX = this.x >> 4; const bitZ = this.z >> 4; @@ -47,8 +53,8 @@ export class Player extends EntityLiving { // Load or keep any chunks we need const currentLoads = []; - for (let x = bitX - 10; x < bitX + 10; x++) { - for (let z = bitZ - 10; z < bitZ + 10; z++) { + for (let x = bitX - CHUNK_LOAD_RANGE; x < bitX + CHUNK_LOAD_RANGE; x++) { + for (let z = bitZ - CHUNK_LOAD_RANGE; z < bitZ + CHUNK_LOAD_RANGE; z++) { const coordPair = Chunk.CreateCoordPair(x, z); if (!this.loadedChunks.includes(coordPair)) { const chunk = await this.world.getChunkSafe(x, z); diff --git a/server/packets/Respawn.ts b/server/packets/Respawn.ts index b2fbea9..1ffe08f 100644 --- a/server/packets/Respawn.ts +++ b/server/packets/Respawn.ts @@ -4,19 +4,23 @@ import { Packet } from "../enums/Packet"; export class PacketRespawn implements IPacket { public packetId = Packet.Respawn; - public health:number; + public dimension:number; - public constructor(health:number) { - this.health = health; + public constructor(dimension?:number) { + if (typeof(dimension) == "number") { + this.dimension = dimension; + } else { + this.dimension = Number.MIN_VALUE; + } } public readData(reader:IReader) { - this.health = reader.readShort(); + this.dimension = reader.readByte(); return this; } public writeData() { - return createWriter(Endian.BE, 3).writeUByte(this.packetId).writeShort(this.health).toBuffer(); + return createWriter(Endian.BE, 2).writeUByte(this.packetId).writeByte(this.dimension).toBuffer(); } } \ No newline at end of file