implement disconnect and teleport packet, improve packet splitter, fix bulk chunk packet, version 1.1.6
This commit is contained in:
@@ -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;
|
||||
}
|
||||
}
|
||||
+16
-3
@@ -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;
|
||||
}
|
||||
}
|
||||
+60
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
|
||||
Reference in New Issue
Block a user