implement disconnect and teleport packet, improve packet splitter, fix bulk chunk packet, version 1.1.6

This commit is contained in:
LabyStudio
2022-06-19 18:29:06 +02:00
parent 7266a89f5a
commit b8408a9a8e
15 changed files with 250 additions and 57 deletions
+10 -6
View File
@@ -26,7 +26,7 @@ import PlayerControllerMultiplayer from "./network/controller/PlayerControllerMu
export default class Minecraft {
static VERSION = "1.1.5"
static VERSION = "1.1.6"
static URL_GITHUB = "https://github.com/labystudio/js-minecraft";
static PROTOCOL_VERSION = 47; //758;
@@ -116,14 +116,18 @@ export default class Minecraft {
// Disconnect from server
if (this.playerController instanceof PlayerControllerMultiplayer) {
let networkHandler = this.playerController.getNetworkHandler();
networkHandler.getNetworkManager().close();
if (networkHandler.getNetworkManager().isConnected()) {
networkHandler.getNetworkManager().close();
}
}
this.playerController = null;
this.world.getChunkProvider().getChunks().clear();
this.world = null;
this.player = null;
this.loadingScreen = null;
if (this.world !== null) {
this.world.getChunkProvider().getChunks().clear();
this.world = null;
this.player = null;
this.loadingScreen = null;
}
this.displayScreen(new GuiMainMenu());
} else {
// Display loading screen
@@ -80,6 +80,11 @@ export default class Entity {
this.prevZ = this.z;
}
setRotation(yaw, pitch) {
this.rotationYaw = yaw;
this.rotationPitch = pitch;
}
onUpdate() {
this.onEntityUpdate();
}
@@ -42,13 +42,7 @@ export default class GuiChat extends GuiScreen {
// Add message to sent history
this.minecraft.ingameOverlay.chatOverlay.addMessageToSentHistory(message);
// Handle message
if (message.startsWith("/")) {
this.minecraft.commandHandler.handleMessage(message.substring(1));
} else {
this.minecraft.playerController.sendChatMessage(message);
}
this.minecraft.playerController.sendChatMessage(message);
return;
}
@@ -21,6 +21,8 @@ export default class NetworkManager {
this.pako = require("pako");
this.compressionThreshold = 0;
this.carryBuffer = [];
}
setNetworkHandler(networkHandler) {
@@ -138,9 +140,15 @@ export default class NetworkManager {
}
// Packet Sizer
let bufferIn = new ByteBuf(new Int8Array(data));
let bufferIn = new ByteBuf(new Int8Array([]));
bufferIn.write(this.carryBuffer);
bufferIn.write(data);
bufferIn.setPosition(0);
this.carryBuffer = [];
while (bufferIn.readableBytes() > 0) {
let three = [0, 0, 0];
let start = bufferIn.getPosition();
for (let i = 0; i < three.length; i++) {
three[i] = bufferIn.readByte();
if (three[i] >= 0) {
@@ -150,7 +158,9 @@ export default class NetworkManager {
}
if (bufferIn.readableBytes() < length) {
break;
bufferIn.setPosition(start);
this.carryBuffer = bufferIn.getSlicedArray();
return;
} else {
this.handlePacket(new ByteBuf(bufferIn.getSlicedArray(length)));
bufferIn.skipBytes(length);
@@ -161,6 +171,7 @@ export default class NetworkManager {
}
} catch (e) {
console.error(e);
console.log(e.stack);
}
}
@@ -213,7 +224,7 @@ export default class NetworkManager {
_onClose(event) {
if (this.connected) {
this.networkHandler.onDisconnect("Disconnected from server");
this.networkHandler.onDisconnect();
}
this.connected = false;
@@ -222,6 +233,11 @@ export default class NetworkManager {
close() {
this.connected = false;
this.socket.close();
this.networkHandler.onDisconnect();
}
isConnected() {
return this.connected;
}
flushPacketQueue() {
@@ -20,6 +20,8 @@ import ServerChunkDataPacket from "./packet/play/server/ServerChunkDataPacket.js
import ServerMultiChunkDataPacket from "./packet/play/server/ServerMultiChunkDataPacket.js";
import ServerBlockChangePacket from "./packet/play/server/ServerBlockChangePacket.js";
import ServerChatPacket from "./packet/play/server/ServerChatPacket.js";
import ServerDisconnectPacket from "./packet/play/server/ServerDisconnectPacket.js";
import ServerPlayerPositionRotationPacket from "./packet/play/server/ServerPlayerPositionRotationPacket.js";
export default class PacketRegistry {
@@ -47,9 +49,11 @@ export default class PacketRegistry {
this.registerServer(ProtocolState.PLAY, 0x00, ServerKeepAlivePacket);
this.registerServer(ProtocolState.PLAY, 0x01, ServerJoinGamePacket);
this.registerServer(ProtocolState.PLAY, 0x02, ServerChatPacket);
this.registerServer(ProtocolState.PLAY, 0x08, ServerPlayerPositionRotationPacket);
this.registerServer(ProtocolState.PLAY, 0x21, ServerChunkDataPacket);
this.registerServer(ProtocolState.PLAY, 0x23, ServerBlockChangePacket);
this.registerServer(ProtocolState.PLAY, 0x26, ServerMultiChunkDataPacket);
this.registerServer(ProtocolState.PLAY, 0x40, ServerDisconnectPacket);
this.registerClient(ProtocolState.PLAY, 0x00, ClientKeepAlivePacket);
this.registerClient(ProtocolState.PLAY, 0x01, ClientChatPacket);
@@ -11,6 +11,11 @@ export default class PlayerController {
}
sendChatMessage(message) {
this.minecraft.addMessageToChat("<" + this.minecraft.player.username + "> " + message);
// Handle message
if (message.startsWith("/")) {
this.minecraft.commandHandler.handleMessage(message.substring(1));
} else {
this.minecraft.addMessageToChat("<" + this.minecraft.player.username + "> " + message);
}
}
}
@@ -3,6 +3,7 @@ import GuiDisconnected from "../../gui/screens/GuiDisconnected.js";
import WorldClient from "../../world/WorldClient.js";
import ClientKeepAlivePacket from "../packet/play/client/ClientKeepAlivePacket.js";
import PlayerControllerMultiplayer from "../controller/PlayerControllerMultiplayer.js";
import ClientPlayerPositionRotationPacket from "../packet/play/client/ClientPlayerPositionRotationPacket.js";
export default class NetworkPlayHandler extends PacketHandler {
@@ -25,14 +26,57 @@ export default class NetworkPlayHandler extends PacketHandler {
}
handleServerChat(packet) {
this.minecraft.ingameOverlay.chatOverlay.addMessage(packet.getMessage());
if (packet.getType() !== 2) {
this.minecraft.ingameOverlay.chatOverlay.addMessage(packet.getMessage());
}
}
handleServerPlayerPositionRotation(packet) {
let player = this.minecraft.player;
let x = packet.getX();
let y = packet.getY();
let z = packet.getZ();
let yaw = packet.getYaw();
let pitch = packet.getPitch();
if (packet.hasFlag(0x01)) {
x += player.x;
} else {
player.motionX = 0;
}
if (packet.hasFlag(0x02)) {
y += player.y;
} else {
player.motionY = 0;
}
if (packet.hasFlag(0x04)) {
z += player.z;
} else {
player.motionZ = 0;
}
if (packet.hasFlag(0x08)) {
yaw += player.rotationYaw;
}
if (packet.hasFlag(0x10)) {
pitch += player.rotationPitch;
}
player.setPosition(x, y, z);
player.setRotation(yaw, pitch);
this.networkManager.sendPacket(new ClientPlayerPositionRotationPacket(player.x, player.y, player.z, player.rotationYaw, player.rotationPitch, player.onGround));
}
handleChunkData(packet) {
let provider = this.minecraft.world.getChunkProvider();
if (packet.isFullChunk()) {
if (packet.getDataSize() === 0) {
if (packet.getMask() === 0) {
provider.unloadChunk(packet.getX(), packet.getZ());
return;
}
@@ -41,7 +85,7 @@ export default class NetworkPlayHandler extends PacketHandler {
}
let chunk = this.minecraft.world.getChunkAt(packet.getX(), packet.getZ());
chunk.fillChunk(packet.getData(), packet.getDataSize(), packet.isFullChunk());
chunk.fillChunk(packet.getData(), packet.getMask(), packet.isFullChunk());
}
handleMultiChunkData(packet) {
@@ -59,10 +103,14 @@ export default class NetworkPlayHandler extends PacketHandler {
this.minecraft.world.setBlockAt(position.getX(), position.getY(), position.getZ(), typeId);
}
handleDisconnect(packet) {
this.minecraft.loadWorld(null);
this.minecraft.displayScreen(new GuiDisconnected(packet.getReason()));
}
onDisconnect() {
if (this.minecraft.isInGame()) {
this.minecraft.displayScreen(new GuiDisconnected("Disconnected from server"));
}
this.minecraft.loadWorld(null);
this.minecraft.displayScreen(new GuiDisconnected("Disconnected from server"));
}
getNetworkManager() {
@@ -11,9 +11,7 @@ export default class ServerChatPacket extends Packet {
}
read(buffer) {
this.message = format(JSON.parse(buffer.readString(32767)), {
useAnsiCodes: true
});
this.message = format(JSON.parse(buffer.readString(32767)));
this.type = buffer.readByte();
}
@@ -2,21 +2,21 @@ import Packet from "../../../Packet.js";
export default class ServerChunkDataPacket extends Packet {
constructor() {
constructor(x, y, mask, data) {
super();
this.x = 0;
this.z = 0;
this.fullChunk = false;
this.dataSize = 0;
this.data = [];
this.x = x;
this.z = y;
this.fullChunk = true;
this.mask = mask;
this.data = data;
}
read(buffer) {
this.x = buffer.readInt();
this.z = buffer.readInt();
this.fullChunk = buffer.readBoolean();
this.dataSize = buffer.readShort();
this.mask = buffer.readShort();
this.data = buffer.readByteArray();
}
@@ -24,6 +24,10 @@ export default class ServerChunkDataPacket extends Packet {
handler.handleChunkData(this);
}
setData(data) {
this.data = data;
}
getX() {
return this.x;
}
@@ -36,11 +40,19 @@ export default class ServerChunkDataPacket extends Packet {
return this.fullChunk;
}
getDataSize() {
return this.dataSize;
getMask() {
return this.mask;
}
getData() {
return this.data;
}
static _calculateLength(bits, isOverworld, isFullChunk) {
let x = bits * 2 * 16 * 16 * 16;
let z = bits * 16 * 16 * 16 / 2;
let overworld = isOverworld ? bits * 16 * 16 * 16 / 2 : 0;
let fullChunk = isFullChunk ? 256 : 0;
return x + z + overworld + fullChunk;
}
}
@@ -0,0 +1,23 @@
import Packet from "../../../Packet.js";
import {format} from "../../../../../../../../../libraries/chat.js";
export default class ServerDisconnectPacket extends Packet {
constructor() {
super();
this.reason = "";
}
read(buffer) {
this.reason = format(JSON.parse(buffer.readString(32767)));
}
handle(handler) {
handler.handleDisconnect(this);
}
getReason() {
return this.reason;
}
}
@@ -17,10 +17,14 @@ export default class ServerMultiChunkDataPacket extends Packet {
for (let i = 0; i < amount; i++) {
let x = buffer.readInt();
let y = buffer.readInt();
let dataSize = buffer.readShort() & 65535;
let data = buffer.readByteArray();
let mask = buffer.readShort() & 65535;
let length = ServerChunkDataPacket._calculateLength(ServerMultiChunkDataPacket._bitCount(mask), this.overworld, true);
this.chunkData.push(new ServerChunkDataPacket(x, y, mask, new Uint8Array(length)));
}
this.chunkData.push(new ServerChunkDataPacket(x, y, dataSize, data));
for (let i = 0; i < amount; i++) {
let data = this.chunkData[i].getData();
buffer.read(data, data.length);
}
}
@@ -35,4 +39,13 @@ export default class ServerMultiChunkDataPacket extends Packet {
isOverworld() {
return this.overworld;
}
static _bitCount(bits) {
bits = bits - ((bits >>> 1) & 0x55555555);
bits = (bits & 0x33333333) + ((bits >>> 2) & 0x33333333);
bits = (bits + (bits >>> 4)) & 0x0f0f0f0f;
bits = bits + (bits >>> 8);
bits = bits + (bits >>> 16);
return bits & 0x3f;
}
}
@@ -0,0 +1,60 @@
import Packet from "../../../Packet.js";
export default class ServerPlayerPositionRotationPacket extends Packet {
constructor() {
super();
this.x = 0;
this.y = 0;
this.z = 0;
this.yaw = 0;
this.pitch = 0;
this.flags = 0;
}
read(buffer) {
this.x = buffer.readDouble();
this.y = buffer.readDouble();
this.z = buffer.readDouble();
this.yaw = buffer.readFloat();
this.pitch = buffer.readFloat();
this.flags = buffer.readByte();
}
handle(handler) {
handler.handleServerPlayerPositionRotation(this);
}
getX() {
return this.x;
}
getY() {
return this.y;
}
getZ() {
return this.z;
}
getYaw() {
return this.yaw;
}
getPitch() {
return this.pitch;
}
getFlags() {
return this.flags;
}
hasFlag(bit) {
return (this.flags & bit) === bit;
}
}
@@ -36,30 +36,35 @@ export default class ByteBuf {
return this.array[this.pos++];
}
readUnsignedByte(pos = this.pos) {
return this.readByte(pos) & 0xFF;
}
readShort() {
return this.array[this.pos++] << 8 | this.array[this.pos++];
}
readInt() {
return this.array[this.pos++] << 24
| this.array[this.pos++] << 16
| this.array[this.pos++] << 8
| this.array[this.pos++];
return this.readUnsignedByte() << 24
| this.readUnsignedByte() << 16
| this.readUnsignedByte() << 8
| this.readUnsignedByte();
}
readLong() {
return Long.fromNumber(this.array[this.pos++]).shiftLeft(56)
.or(Long.fromNumber(this.array[this.pos++]).shiftLeft(48))
.or(Long.fromNumber(this.array[this.pos++]).shiftLeft(40))
.or(Long.fromNumber(this.array[this.pos++]).shiftLeft(32))
.or(Long.fromNumber(this.array[this.pos++]).shiftLeft(24))
.or(Long.fromNumber(this.array[this.pos++]).shiftLeft(16))
.or(Long.fromNumber(this.array[this.pos++]).shiftLeft(8))
.or(Long.fromNumber(this.array[this.pos++]));
return Long.fromNumber(this.readUnsignedByte()).shiftLeft(56)
.or(Long.fromNumber(this.readUnsignedByte()).shiftLeft(48))
.or(Long.fromNumber(this.readUnsignedByte()).shiftLeft(40))
.or(Long.fromNumber(this.readUnsignedByte()).shiftLeft(32))
.or(Long.fromNumber(this.readUnsignedByte()).shiftLeft(24))
.or(Long.fromNumber(this.readUnsignedByte()).shiftLeft(16))
.or(Long.fromNumber(this.readUnsignedByte()).shiftLeft(8))
.or(Long.fromNumber(this.readUnsignedByte()).shiftLeft(0));
}
readFloat() {
return new Float32Array(new Uint32Array([this.readInt(), 0, 0, 0, 0, 0, 0, 0]).buffer)[0];
let num = this.readInt();
return new Float32Array(new Uint32Array([num, 0, 0, 0, 0, 0, 0, 0]).buffer)[0];
}
readDouble() {
@@ -161,7 +166,9 @@ export default class ByteBuf {
extendIfNeeded(bytes) {
if (this.pos + bytes > this.array.length) {
let newArray = new Uint8Array(this.array.length + bytes);
let length = this.array.length + bytes;
let newArray = this.array instanceof Uint8Array ? new Uint8Array(length) : new Int8Array(length);
newArray.set(this.array);
this.array = newArray;
}
+8 -4
View File
@@ -252,6 +252,10 @@ export default class Chunk {
}
setBlockAt(x, y, z, typeId, data = 0) {
if (y < 0 || y >= World.TOTAL_HEIGHT) {
return;
}
let section = this.getSection(y >> 4);
let yInSection = y & 15;
@@ -274,7 +278,7 @@ export default class Chunk {
// Update height map
let block = Block.getById(typeId);
if (typeId !== 0 && block.isSolid()) {
if (typeId !== 0 && block !== null && block.isSolid()) {
if (y >= height) {
this.updateHeightMap(x, y + 1, z);
}
@@ -293,17 +297,17 @@ export default class Chunk {
this.notifyNeighbors(x, z);
// Handle block abilities
if (typeId !== 0) {
if (typeId !== 0 && block !== null) {
block.onBlockAdded(this.world, totalX, y, totalZ);
}
return true;
}
fillChunk(data, size, fullChunk) {
fillChunk(data, mask, fullChunk) {
let i = 0;
for (let layer = 0; layer < Chunk.SECTION_AMOUNT; layer++) {
if ((size & 1 << layer) !== 0) {
if ((mask & 1 << layer) !== 0) {
let section = this.getSection(layer);
for (let k = 0; k < ChunkSection.SIZE * ChunkSection.SIZE * ChunkSection.SIZE; k++) {
@@ -56,7 +56,7 @@ export default class MetadataChunkBlock {
level = 15;
}
} else if (this.type === EnumSkyBlock.BLOCK) {
level = typeId === 0 ? 0 : block.getLightValue();
level = typeId === 0 || block === null ? 0 : block.getLightValue();
}
if (opacity >= 15 && level === 0) {