implement dimension switching

This commit is contained in:
Holly Stubbs 2023-09-04 23:42:38 +01:00
parent 9feafc46c7
commit 74374cb0d6
Signed by: tgpholly
GPG key ID: B8583C4B7D18119E
8 changed files with 67 additions and 17 deletions

View file

@ -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;
}

View file

@ -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();
}

View file

@ -33,7 +33,7 @@ export class MinecraftServer {
private readonly serverClock:NodeJS.Timeout;
private tickCounter:number = 0;
private clients:FunkyArray<string, MPClient>;
private worlds:FunkyArray<number, World>;
public worlds:FunkyArray<number, World>;
public saveManager:WorldSaveManager;
private overworld:World;
private nether:World;

View file

@ -61,6 +61,10 @@ export class World {
}
}
}
// Clear player chunk list (they may be switching dimensions)
entity.loadedChunks = new Array<number>();
entity.justUnloaded = new Array<number>();
this.players.remove(entity.entityId);
}

View file

@ -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;
}
}
}

View file

@ -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;
}

View file

@ -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);

View file

@ -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();
}
}