implement dimension switching
This commit is contained in:
parent
9feafc46c7
commit
74374cb0d6
8 changed files with 67 additions and 17 deletions
|
@ -57,8 +57,6 @@ export class Chunk {
|
||||||
public getTopBlockY(x:number, z:number) {
|
public getTopBlockY(x:number, z:number) {
|
||||||
let castY = this.MAX_HEIGHT;
|
let castY = this.MAX_HEIGHT;
|
||||||
while (castY-- > 0) {
|
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) {
|
if (this.getBlockId(x >>> 0, castY, z >>> 0) !== 0) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,11 +13,13 @@ import { PacketPlayerDigging } from "./packets/PlayerDigging";
|
||||||
import { Player } from "./entities/Player";
|
import { Player } from "./entities/Player";
|
||||||
import { Socket } from "net";
|
import { Socket } from "net";
|
||||||
import { Vec3 } from "./Vec3";
|
import { Vec3 } from "./Vec3";
|
||||||
|
import { PacketRespawn } from "./packets/Respawn";
|
||||||
|
import { PacketSpawnPosition } from "./packets/SpawnPosition";
|
||||||
|
|
||||||
export class MPClient {
|
export class MPClient {
|
||||||
private readonly mcServer:MinecraftServer;
|
private readonly mcServer:MinecraftServer;
|
||||||
private readonly socket:Socket;
|
private readonly socket:Socket;
|
||||||
public readonly entity:Player;
|
public entity:Player;
|
||||||
|
|
||||||
private diggingAt:Vec3;
|
private diggingAt:Vec3;
|
||||||
|
|
||||||
|
@ -77,15 +79,25 @@ export class MPClient {
|
||||||
if (message[0].startsWith("/")) {
|
if (message[0].startsWith("/")) {
|
||||||
packet.message = "";
|
packet.message = "";
|
||||||
if (message[0] === "/tp") {
|
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]}`);
|
Console.printInfo(packet.message = `Teleported ${this.entity.username} to ${message[1]} ${message[2]} ${message[3]}`);
|
||||||
} else if (message[0] === "/csay") {
|
} else if (message[0] === "/csay") {
|
||||||
this.mcServer.sendChatMessage(`[CONSOLE] ${message.slice(1, message.length).join(" ")}`);
|
this.mcServer.sendChatMessage(`[CONSOLE] ${message.slice(1, message.length).join(" ")}`);
|
||||||
} else if (message[0] === "/top") {
|
} else if (message[0] === "/top") {
|
||||||
packet.message = `Woosh!`;
|
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);
|
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());
|
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 !== "") {
|
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() {
|
private handleDisconnectKick() {
|
||||||
this.socket.end();
|
this.socket.end();
|
||||||
}
|
}
|
||||||
|
|
|
@ -33,7 +33,7 @@ export class MinecraftServer {
|
||||||
private readonly serverClock:NodeJS.Timeout;
|
private readonly serverClock:NodeJS.Timeout;
|
||||||
private tickCounter:number = 0;
|
private tickCounter:number = 0;
|
||||||
private clients:FunkyArray<string, MPClient>;
|
private clients:FunkyArray<string, MPClient>;
|
||||||
private worlds:FunkyArray<number, World>;
|
public worlds:FunkyArray<number, World>;
|
||||||
public saveManager:WorldSaveManager;
|
public saveManager:WorldSaveManager;
|
||||||
private overworld:World;
|
private overworld:World;
|
||||||
private nether:World;
|
private nether:World;
|
||||||
|
|
|
@ -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);
|
this.players.remove(entity.entityId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -28,6 +28,8 @@ export class Entity implements IEntity {
|
||||||
private lastCrouchState:boolean;
|
private lastCrouchState:boolean;
|
||||||
private lastFireState:boolean;
|
private lastFireState:boolean;
|
||||||
|
|
||||||
|
private queuedChunkUpdate:boolean;
|
||||||
|
|
||||||
public constructor(world:World) {
|
public constructor(world:World) {
|
||||||
this.entityId = Entity.nextEntityId++;
|
this.entityId = Entity.nextEntityId++;
|
||||||
|
|
||||||
|
@ -35,7 +37,7 @@ export class Entity implements IEntity {
|
||||||
|
|
||||||
this.world = world;
|
this.world = world;
|
||||||
this.x = this.y = this.z = this.lastX = this.lastY = this.lastZ = 0;
|
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);
|
this.chunk = world.getChunk(this.x >> 4, this.z >> 4);
|
||||||
|
|
||||||
|
@ -92,8 +94,13 @@ export class Entity implements IEntity {
|
||||||
updateEntityChunk() {
|
updateEntityChunk() {
|
||||||
const bitX = this.x >> 4;
|
const bitX = this.x >> 4;
|
||||||
const bitZ = this.z >> 4;
|
const bitZ = this.z >> 4;
|
||||||
if (bitX != this.lastX >> 4 || bitZ != this.lastZ >> 4) {
|
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.chunk = this.world.getChunk(bitX, bitZ);
|
||||||
|
this.queuedChunkUpdate = false;
|
||||||
|
} else {
|
||||||
|
this.queuedChunkUpdate = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -101,7 +101,6 @@ export class EntityLiving extends Entity {
|
||||||
|
|
||||||
// Drowning
|
// Drowning
|
||||||
if (this.isInWater()) {
|
if (this.isInWater()) {
|
||||||
console.log("in water!");
|
|
||||||
if (this.timeInWater == Number.MIN_SAFE_INTEGER) {
|
if (this.timeInWater == Number.MIN_SAFE_INTEGER) {
|
||||||
this.timeInWater = 320;
|
this.timeInWater = 320;
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,6 +7,8 @@ import { EntityLiving } from "./EntityLiving";
|
||||||
import { PacketPreChunk } from "../packets/PreChunk";
|
import { PacketPreChunk } from "../packets/PreChunk";
|
||||||
import { PacketUpdateHealth } from "../packets/UpdateHealth";
|
import { PacketUpdateHealth } from "../packets/UpdateHealth";
|
||||||
|
|
||||||
|
const CHUNK_LOAD_RANGE = 5;
|
||||||
|
|
||||||
export class Player extends EntityLiving {
|
export class Player extends EntityLiving {
|
||||||
public username:string;
|
public username:string;
|
||||||
private server:MinecraftServer;
|
private server:MinecraftServer;
|
||||||
|
@ -32,6 +34,10 @@ export class Player extends EntityLiving {
|
||||||
this.lastHealth = this.health;
|
this.lastHealth = this.health;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public forceUpdatePlayerChunks() {
|
||||||
|
this.firstUpdate = true;
|
||||||
|
}
|
||||||
|
|
||||||
private async updatePlayerChunks() {
|
private async updatePlayerChunks() {
|
||||||
const bitX = this.x >> 4;
|
const bitX = this.x >> 4;
|
||||||
const bitZ = this.z >> 4;
|
const bitZ = this.z >> 4;
|
||||||
|
@ -47,8 +53,8 @@ export class Player extends EntityLiving {
|
||||||
|
|
||||||
// Load or keep any chunks we need
|
// Load or keep any chunks we need
|
||||||
const currentLoads = [];
|
const currentLoads = [];
|
||||||
for (let x = bitX - 10; x < bitX + 10; x++) {
|
for (let x = bitX - CHUNK_LOAD_RANGE; x < bitX + CHUNK_LOAD_RANGE; x++) {
|
||||||
for (let z = bitZ - 10; z < bitZ + 10; z++) {
|
for (let z = bitZ - CHUNK_LOAD_RANGE; z < bitZ + CHUNK_LOAD_RANGE; z++) {
|
||||||
const coordPair = Chunk.CreateCoordPair(x, z);
|
const coordPair = Chunk.CreateCoordPair(x, z);
|
||||||
if (!this.loadedChunks.includes(coordPair)) {
|
if (!this.loadedChunks.includes(coordPair)) {
|
||||||
const chunk = await this.world.getChunkSafe(x, z);
|
const chunk = await this.world.getChunkSafe(x, z);
|
||||||
|
|
|
@ -4,19 +4,23 @@ import { Packet } from "../enums/Packet";
|
||||||
|
|
||||||
export class PacketRespawn implements IPacket {
|
export class PacketRespawn implements IPacket {
|
||||||
public packetId = Packet.Respawn;
|
public packetId = Packet.Respawn;
|
||||||
public health:number;
|
public dimension:number;
|
||||||
|
|
||||||
public constructor(health:number) {
|
public constructor(dimension?:number) {
|
||||||
this.health = health;
|
if (typeof(dimension) == "number") {
|
||||||
|
this.dimension = dimension;
|
||||||
|
} else {
|
||||||
|
this.dimension = Number.MIN_VALUE;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public readData(reader:IReader) {
|
public readData(reader:IReader) {
|
||||||
this.health = reader.readShort();
|
this.dimension = reader.readByte();
|
||||||
|
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public writeData() {
|
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();
|
||||||
}
|
}
|
||||||
}
|
}
|
Loading…
Reference in a new issue