implement packet compression, implement player controller, implement join server authentication, add cobblestone, implement chunk provider, implement block position, implement session, implement movement, chunk, chat and block update packets, version 1.1.5
This commit is contained in:
@@ -5,7 +5,6 @@ import WorldRenderer from "./render/WorldRenderer.js";
|
||||
import ScreenRenderer from "./render/gui/ScreenRenderer.js";
|
||||
import ItemRenderer from "./render/gui/ItemRenderer.js";
|
||||
import IngameOverlay from "./gui/overlay/IngameOverlay.js";
|
||||
import PlayerEntity from "./entity/PlayerEntity.js";
|
||||
import SoundManager from "./sound/SoundManager.js";
|
||||
import Block from "./world/block/Block.js";
|
||||
import BoundingBox from "../util/BoundingBox.js";
|
||||
@@ -22,12 +21,14 @@ import GuiContainerCreative from "./gui/screens/container/GuiContainerCreative.j
|
||||
import GameProfile from "../util/GameProfile.js";
|
||||
import UUID from "../util/UUID.js";
|
||||
import FocusStateType from "../util/FocusStateType.js";
|
||||
import Session from "../util/Session.js";
|
||||
import PlayerControllerMultiplayer from "./network/controller/PlayerControllerMultiplayer.js";
|
||||
|
||||
export default class Minecraft {
|
||||
|
||||
static VERSION = "1.1.4"
|
||||
static VERSION = "1.1.5"
|
||||
static URL_GITHUB = "https://github.com/labystudio/js-minecraft";
|
||||
static PROTOCOL_VERSION = 758;
|
||||
static PROTOCOL_VERSION = 47; //758;
|
||||
|
||||
// TODO Add to settings
|
||||
static PROXY = {
|
||||
@@ -45,12 +46,13 @@ export default class Minecraft {
|
||||
this.loadingScreen = null;
|
||||
this.world = null;
|
||||
this.player = null;
|
||||
|
||||
this.playerController = null;
|
||||
this.fps = 0;
|
||||
this.maxFps = 0;
|
||||
|
||||
let username = "Player" + Math.floor(Math.random() * 100);
|
||||
this.profile = new GameProfile(username, UUID.randomUUID());
|
||||
let profile = new GameProfile(username, UUID.randomUUID());
|
||||
this.session = new Session(profile, "");
|
||||
|
||||
// Tick timer
|
||||
this.timer = new Timer(20);
|
||||
@@ -111,7 +113,14 @@ export default class Minecraft {
|
||||
this.worldRenderer.reset();
|
||||
this.itemRenderer.reset();
|
||||
|
||||
this.world.chunks.clear();
|
||||
// Disconnect from server
|
||||
if (this.playerController instanceof PlayerControllerMultiplayer) {
|
||||
let networkHandler = this.playerController.getNetworkHandler();
|
||||
networkHandler.getNetworkManager().close();
|
||||
}
|
||||
this.playerController = null;
|
||||
|
||||
this.world.getChunkProvider().getChunks().clear();
|
||||
this.world = null;
|
||||
this.player = null;
|
||||
this.loadingScreen = null;
|
||||
@@ -127,12 +136,11 @@ export default class Minecraft {
|
||||
this.worldRenderer.scene.add(this.world.group);
|
||||
|
||||
// Create player
|
||||
this.player = new PlayerEntity(this, this.world);
|
||||
this.player.username = this.profile.username;
|
||||
this.player = this.playerController.createPlayer(this.world);
|
||||
this.player.username = this.session.getProfile().getUsername();
|
||||
this.world.addEntity(this.player);
|
||||
|
||||
// Load spawn chunks and respawn player
|
||||
this.world.findSpawn();
|
||||
this.world.loadSpawnChunks();
|
||||
this.player.respawn();
|
||||
}
|
||||
@@ -192,7 +200,7 @@ export default class Minecraft {
|
||||
onRender(partialTicks) {
|
||||
if (this.isInGame()) {
|
||||
// Player rotation
|
||||
if (!this.isPaused()) {
|
||||
if (this.hasInGameFocus()) {
|
||||
let deltaX = this.window.pullMouseMotionX();
|
||||
let deltaY = this.window.pullMouseMotionY();
|
||||
this.player.turn(deltaX, deltaY);
|
||||
@@ -283,8 +291,8 @@ export default class Minecraft {
|
||||
let cameraChunkZ = Math.floor(this.player.z) >> 4;
|
||||
|
||||
let renderDistance = this.settings.viewDistance;
|
||||
let requiredChunks = Math.pow(renderDistance * 2 - 1, 2);
|
||||
let loadedChunks = this.world.chunks.size;
|
||||
let requiredChunks = this.isSingleplayer() ? Math.pow(renderDistance * 2 - 1, 2) : 1;
|
||||
let loadedChunks = this.world.getChunkProvider().getChunks().size;
|
||||
|
||||
// Load chunks and count
|
||||
setTimeout(() => {
|
||||
@@ -448,7 +456,19 @@ export default class Minecraft {
|
||||
}
|
||||
|
||||
isPaused() {
|
||||
return !this.hasInGameFocus() && this.loadingScreen === null;
|
||||
return !this.hasInGameFocus() && this.loadingScreen === null && this.isSingleplayer();
|
||||
}
|
||||
|
||||
setSession(session) {
|
||||
this.session = session;
|
||||
}
|
||||
|
||||
getSession() {
|
||||
return this.session;
|
||||
}
|
||||
|
||||
isSingleplayer() {
|
||||
return this.isInGame() && !(this.playerController instanceof PlayerControllerMultiplayer);
|
||||
}
|
||||
|
||||
stop() {
|
||||
|
||||
@@ -8,8 +8,7 @@ export default class Entity {
|
||||
this.minecraft = minecraft;
|
||||
this.world = world;
|
||||
this.random = new Random();
|
||||
|
||||
this.renderer = this.minecraft.worldRenderer.entityRenderManager.createEntityRendererByEntity(this);
|
||||
this.renderer = null;
|
||||
|
||||
this.x = 0;
|
||||
this.y = 0;
|
||||
@@ -48,6 +47,13 @@ export default class Entity {
|
||||
this.setPosition(this.x, this.y, this.z);
|
||||
}
|
||||
|
||||
initRenderer() {
|
||||
this.renderer = this.minecraft.worldRenderer.entityRenderManager.createEntityRendererByEntity(this);
|
||||
if (this.renderer === null) {
|
||||
throw new Error("No entity renderer for entity " + this.constructor.name + " found!");
|
||||
}
|
||||
}
|
||||
|
||||
setPosition(x, y, z) {
|
||||
// Update position
|
||||
this.x = x;
|
||||
|
||||
@@ -0,0 +1,69 @@
|
||||
import PlayerEntity from "./PlayerEntity.js";
|
||||
import ClientPlayerMovementPacket from "../network/packet/play/client/ClientPlayerMovementPacket.js";
|
||||
import ClientPlayerRotationPacket from "../network/packet/play/client/ClientPlayerRotationPacket.js";
|
||||
import ClientPlayerPositionPacket from "../network/packet/play/client/ClientPlayerPositionPacket.js";
|
||||
import ClientPlayerPositionRotationPacket from "../network/packet/play/client/ClientPlayerPositionRotationPacket.js";
|
||||
|
||||
export default class PlayerEntityMultiplayer extends PlayerEntity {
|
||||
|
||||
constructor(minecraft, world, networkHandler) {
|
||||
super(minecraft, world);
|
||||
|
||||
this.networkHandler = networkHandler;
|
||||
|
||||
this.positionUpdateTicks = 0;
|
||||
|
||||
this.lastReportedX = 0;
|
||||
this.lastReportedY = 0;
|
||||
this.lastReportedZ = 0;
|
||||
|
||||
this.lastReportedYaw = 0;
|
||||
this.lastReportedPitch = 0;
|
||||
}
|
||||
|
||||
onUpdate() {
|
||||
super.onUpdate();
|
||||
this.onUpdateWalkingPlayer();
|
||||
}
|
||||
|
||||
onUpdateWalkingPlayer() {
|
||||
let movementX = this.x - this.lastReportedX;
|
||||
let movementY = this.y - this.lastReportedY;
|
||||
let movementZ = this.z - this.lastReportedZ;
|
||||
|
||||
let movementYaw = this.rotationYaw - this.lastReportedYaw;
|
||||
let movementPitch = this.rotationPitch - this.lastReportedPitch;
|
||||
|
||||
let reportPosition = movementX * movementX + movementY * movementY + movementZ * movementZ > 9.0E-4 || this.positionUpdateTicks >= 20;
|
||||
let reportRotation = movementYaw !== 0.0 || movementPitch !== 0.0;
|
||||
|
||||
if (reportPosition && reportRotation) {
|
||||
this.networkHandler.sendPacket(new ClientPlayerPositionRotationPacket(this.onGround, this.x, this.y, this.z, this.rotationYaw, this.rotationPitch));
|
||||
} else if (reportPosition) {
|
||||
this.networkHandler.sendPacket(new ClientPlayerPositionPacket(this.onGround, this.x, this.y, this.z));
|
||||
} else if (reportRotation) {
|
||||
this.networkHandler.sendPacket(new ClientPlayerRotationPacket(this.onGround, this.rotationYaw, this.rotationPitch));
|
||||
} else {
|
||||
this.networkHandler.sendPacket(new ClientPlayerMovementPacket(this.onGround));
|
||||
}
|
||||
|
||||
this.positionUpdateTicks++;
|
||||
|
||||
if (reportPosition) {
|
||||
this.lastReportedX = this.x;
|
||||
this.lastReportedY = this.y;
|
||||
this.lastReportedZ = this.z;
|
||||
this.positionUpdateTicks = 0;
|
||||
}
|
||||
|
||||
if (reportRotation) {
|
||||
this.lastReportedYaw = this.rotationYaw;
|
||||
this.lastReportedPitch = this.rotationPitch;
|
||||
}
|
||||
}
|
||||
|
||||
getNetworkHandler() {
|
||||
return this.networkHandler;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -129,7 +129,7 @@ export default class IngameOverlay extends Gui {
|
||||
|
||||
let visibleChunks = 0;
|
||||
let loadedChunks = 0;
|
||||
for (let [index, chunk] of world.chunks) {
|
||||
for (let [index, chunk] of world.getChunkProvider().getChunks()) {
|
||||
for (let y in chunk.sections) {
|
||||
let chunkSection = chunk.sections[y];
|
||||
if (chunkSection.group.visible) {
|
||||
|
||||
@@ -47,7 +47,7 @@ export default class GuiChat extends GuiScreen {
|
||||
if (message.startsWith("/")) {
|
||||
this.minecraft.commandHandler.handleMessage(message.substring(1));
|
||||
} else {
|
||||
this.minecraft.addMessageToChat("<" + this.minecraft.player.username + "> " + message);
|
||||
this.minecraft.playerController.sendChatMessage(message);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -34,7 +34,7 @@ export default class GuiConnecting extends GuiScreen {
|
||||
|
||||
// Send Minecraft protocol handshake
|
||||
this.networkManager.sendPacket(new HandshakePacket(Minecraft.PROTOCOL_VERSION, ProtocolState.LOGIN));
|
||||
this.networkManager.sendPacket(new LoginStartPacket(this.minecraft.profile.username));
|
||||
this.networkManager.sendPacket(new LoginStartPacket(this.minecraft.getSession().getProfile().getUsername()));
|
||||
}
|
||||
|
||||
init() {
|
||||
@@ -66,7 +66,7 @@ export default class GuiConnecting extends GuiScreen {
|
||||
onClose() {
|
||||
super.onClose();
|
||||
|
||||
if (this.networkManager !== null) {
|
||||
if (this.networkManager !== null && this.networkManager.getState() !== ProtocolState.PLAY) {
|
||||
this.networkManager.close();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,8 @@ import World from "../../world/World.js";
|
||||
import GuiTextField from "../widgets/GuiTextField.js";
|
||||
import Random from "../../../util/Random.js";
|
||||
import Long from "../../../../../../../libraries/long.js";
|
||||
import ChunkProviderGenerate from "../../world/provider/ChunkProviderGenerate.js";
|
||||
import PlayerController from "../../network/controller/PlayerController.js";
|
||||
|
||||
export default class GuiCreateWorld extends GuiScreen {
|
||||
|
||||
@@ -33,7 +35,14 @@ export default class GuiCreateWorld extends GuiScreen {
|
||||
}
|
||||
seed = Long.fromNumber(h);
|
||||
}
|
||||
this.minecraft.loadWorld(new World(this.minecraft, seed));
|
||||
|
||||
// Load world
|
||||
let world = new World(this.minecraft);
|
||||
world.setChunkProvider(new ChunkProviderGenerate(world, seed));
|
||||
world.getChunkProvider().findSpawn();
|
||||
|
||||
this.minecraft.playerController = new PlayerController(this.minecraft);
|
||||
this.minecraft.loadWorld(world);
|
||||
}));
|
||||
this.buttonList.push(new GuiButton("Cancel", this.width / 2 + 5, y + 110, 150, 20, () => {
|
||||
this.minecraft.displayScreen(this.previousScreen);
|
||||
|
||||
@@ -1,10 +1,13 @@
|
||||
import ByteBuf from "./util/ByteBuf.js";
|
||||
import PacketRegistry from "./PacketRegistry.js";
|
||||
import ProtocolState from "./ProtocolState.js";
|
||||
import {aesjs} from "../../../../../../libraries/aes.js";
|
||||
import {require} from "../../../../Start.js";
|
||||
|
||||
export default class NetworkManager {
|
||||
|
||||
static DEBUG = false;
|
||||
static MAX_COMPRESSION = 2097152;
|
||||
|
||||
constructor(minecraft) {
|
||||
this.minecraft = minecraft;
|
||||
this.socket = null;
|
||||
@@ -14,10 +17,10 @@ export default class NetworkManager {
|
||||
this.registry = new PacketRegistry();
|
||||
this.protocolState = ProtocolState.HANDSHAKE;
|
||||
|
||||
this.readBuffer = new ByteBuf();
|
||||
this.expectedLength = -1;
|
||||
|
||||
this.queue = [];
|
||||
|
||||
this.pako = require("pako");
|
||||
this.compressionThreshold = 0;
|
||||
}
|
||||
|
||||
setNetworkHandler(networkHandler) {
|
||||
@@ -41,11 +44,10 @@ export default class NetworkManager {
|
||||
this.connected = true;
|
||||
|
||||
// Send proxy handshake
|
||||
let object = {
|
||||
"address": this.address,
|
||||
this.sendProxyPacket(0, {
|
||||
"host": this.address,
|
||||
"port": this.port,
|
||||
};
|
||||
this.socket.send(JSON.stringify(object));
|
||||
});
|
||||
|
||||
// Handle connect event
|
||||
this.networkHandler.onConnect();
|
||||
@@ -54,6 +56,14 @@ export default class NetworkManager {
|
||||
this.flushPacketQueue();
|
||||
}
|
||||
|
||||
sendProxyPacket(id, payload) {
|
||||
let object = {
|
||||
"id": id,
|
||||
"payload": payload
|
||||
};
|
||||
this.socket.send(JSON.stringify(object));
|
||||
}
|
||||
|
||||
sendPacket(packet) {
|
||||
if (this.connected) {
|
||||
this._sendPacketImmediately(packet);
|
||||
@@ -67,81 +77,138 @@ export default class NetworkManager {
|
||||
let packetState = this.registry.getPacketState(packet);
|
||||
if (packetState !== this.protocolState) {
|
||||
if (packetState === null) {
|
||||
console.error("[Network] Tried to send unknown packet: " + packet);
|
||||
console.error("[Network] Tried to send unknown packet: " + packet.constructor.name);
|
||||
return;
|
||||
}
|
||||
|
||||
console.log("[Network] Switching protocol state from " + ProtocolState.getName(this.protocolState) + " to " + ProtocolState.getName(packetState));
|
||||
this.protocolState = packetState;
|
||||
this.setState(packetState);
|
||||
}
|
||||
|
||||
// Write packet to buffer
|
||||
// Packet Codec
|
||||
let buffer = new ByteBuf();
|
||||
buffer.writeByte(this.registry.getClientBoundPacketId(this.protocolState, packet));
|
||||
packet.write(buffer);
|
||||
buffer.setPosition(0);
|
||||
|
||||
// Write chunk header
|
||||
let array = buffer.getArray();
|
||||
// Packet Compression
|
||||
if (this.compressionThreshold !== 0) {
|
||||
let length = buffer.length();
|
||||
if (length > this.compressionThreshold) {
|
||||
let compressed = this.pako.deflate(buffer.getArray(), {
|
||||
chunkSize: 8192
|
||||
});
|
||||
|
||||
buffer = new ByteBuf();
|
||||
buffer.writeVarInt(length);
|
||||
buffer.write(compressed);
|
||||
} else {
|
||||
let copy = buffer.getArray();
|
||||
buffer = new ByteBuf();
|
||||
buffer.writeVarInt(0);
|
||||
buffer.write(copy);
|
||||
}
|
||||
buffer.setPosition(0);
|
||||
}
|
||||
|
||||
// Packet Sizer
|
||||
let wrapper = new ByteBuf();
|
||||
wrapper.writeVarInt(array.length);
|
||||
wrapper.write(array);
|
||||
let chunk = wrapper.getArray().buffer;
|
||||
wrapper.writeVarInt(buffer.length());
|
||||
wrapper.write(buffer.getArray());
|
||||
|
||||
// Encrypt chunk
|
||||
// Packet Compression
|
||||
if (this.isEncrypted) {
|
||||
chunk = this.encryption.encrypt(new Uint8Array(chunk));
|
||||
wrapper = new ByteBuf(this.encryption.encrypt(wrapper.getArray()));
|
||||
}
|
||||
|
||||
// Send chunk
|
||||
this.socket.send(chunk);
|
||||
this.socket.send(wrapper.getArray());
|
||||
|
||||
console.log("[Network] [OUT] " + packet.constructor.name);
|
||||
if (NetworkManager.DEBUG) {
|
||||
console.log("[Network] [OUT] " + packet.constructor.name);
|
||||
}
|
||||
}
|
||||
|
||||
_onMessage(event) {
|
||||
let chunk = new Int8Array(event.data);
|
||||
try {
|
||||
let data = new Uint8Array(event.data);
|
||||
|
||||
// Decrypt chunk
|
||||
if (this.isEncrypted) {
|
||||
chunk = this.encryption.decrypt(new Uint8Array(event.data));
|
||||
}
|
||||
// Packet Compression
|
||||
if (this.isEncrypted) {
|
||||
data = this.decryption.decrypt(data);
|
||||
}
|
||||
|
||||
// Read packet header
|
||||
if (this.expectedLength === -1) {
|
||||
let buf = new ByteBuf(chunk);
|
||||
this.expectedLength = buf.readVarInt(); // Read packet length
|
||||
this.readBuffer.setPosition(0);
|
||||
chunk = chunk.slice(buf.getPosition());
|
||||
}
|
||||
// Packet Sizer
|
||||
let bufferIn = new ByteBuf(new Int8Array(data));
|
||||
while (bufferIn.readableBytes() > 0) {
|
||||
let three = [0, 0, 0];
|
||||
for (let i = 0; i < three.length; i++) {
|
||||
three[i] = bufferIn.readByte();
|
||||
if (three[i] >= 0) {
|
||||
let length = new ByteBuf(three).readVarInt();
|
||||
if (length === 0) {
|
||||
throw new Error("Empty Packet!");
|
||||
}
|
||||
|
||||
// Fill packet content
|
||||
this.readBuffer.write(chunk);
|
||||
|
||||
// Handle packet
|
||||
if (this.readBuffer.getPosition() >= this.expectedLength) {
|
||||
this.expectedLength = -1;
|
||||
this.readBuffer.setPosition(0);
|
||||
this.onPacketBufferReceived(this.readBuffer);
|
||||
if (bufferIn.readableBytes() < length) {
|
||||
break;
|
||||
} else {
|
||||
this.handlePacket(new ByteBuf(bufferIn.getSlicedArray(length)));
|
||||
bufferIn.skipBytes(length);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
}
|
||||
}
|
||||
|
||||
onPacketBufferReceived(buffer) {
|
||||
handlePacket(buffer) {
|
||||
// Packet Compression
|
||||
if (this.compressionThreshold !== 0) {
|
||||
let uncompressedLength = buffer.readVarInt();
|
||||
|
||||
if (uncompressedLength !== 0) {
|
||||
if (uncompressedLength < this.compressionThreshold) {
|
||||
throw new Error("Badly compressed packet - size of " + uncompressedLength + " is below server threshold of " + this.compressionThreshold);
|
||||
}
|
||||
if (uncompressedLength > NetworkManager.MAX_COMPRESSION) {
|
||||
throw new Error("Badly compressed packet - size of " + uncompressedLength + " is larger than protocol maximum of " + NetworkManager.MAX_COMPRESSION);
|
||||
}
|
||||
|
||||
// Decompress
|
||||
buffer = new ByteBuf(this.pako.inflate(new Uint8Array(buffer.getSlicedArray()), {
|
||||
chunkSize: 8192
|
||||
}));
|
||||
|
||||
if (buffer.length() !== uncompressedLength) {
|
||||
throw new Error("Badly compressed packet - decompressed size of " + buffer.length() + " is not equal to original size of " + uncompressedLength);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Packet Codec
|
||||
let packetId = buffer.readByte(); // Read packet id
|
||||
let clazz = this.registry.getServerBoundById(this.protocolState, packetId);
|
||||
if (clazz === null) {
|
||||
console.log("[Network] Unknown packet id: " + packetId);
|
||||
if (NetworkManager.DEBUG) {
|
||||
console.log("[Network] [IN] Unknown packet id: " + packetId + " (0x" + packetId.toString(16) + ")");
|
||||
}
|
||||
return;
|
||||
} else {
|
||||
if (NetworkManager.DEBUG) {
|
||||
console.log("[Network] [IN] " + clazz.name);
|
||||
}
|
||||
}
|
||||
|
||||
let packet = new clazz;
|
||||
console.log("[Network] [IN] " + packet.constructor.name);
|
||||
|
||||
packet.read(buffer, buffer.length);
|
||||
packet.handle(this.networkHandler);
|
||||
}
|
||||
|
||||
_onError(event) {
|
||||
|
||||
console.error("[Network] Error: " + event.data);
|
||||
}
|
||||
|
||||
_onClose(event) {
|
||||
@@ -164,6 +231,26 @@ export default class NetworkManager {
|
||||
|
||||
enableEncryption(secretKey) {
|
||||
this.isEncrypted = true;
|
||||
this.encryption = new aesjs.ModeOfOperation.cfb(secretKey, secretKey, 1);
|
||||
this.decryption = new (require("aesjs").ModeOfOperation).cfb(secretKey, secretKey, 1);
|
||||
this.encryption = new (require("aesjs").ModeOfOperation).cfb(secretKey, secretKey, 1);
|
||||
}
|
||||
|
||||
setState(packetState) {
|
||||
console.log("[Network] Switching protocol state from " + this.protocolState.getName() + " to " + packetState.getName());
|
||||
this.protocolState = packetState;
|
||||
}
|
||||
|
||||
getState() {
|
||||
return this.protocolState;
|
||||
}
|
||||
|
||||
setCompressionThreshold(threshold) {
|
||||
console.log("[Network] Set compression threshold to " + threshold);
|
||||
|
||||
if (threshold >= 0) {
|
||||
this.compressionThreshold = threshold;
|
||||
} else {
|
||||
this.compressionThreshold = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -6,6 +6,20 @@ import StatusResponsePacket from "./packet/status/server/StatusResponsePacket.js
|
||||
import EncryptionRequestPacket from "./packet/login/server/EncryptionRequestPacket.js";
|
||||
import EncryptionResponsePacket from "./packet/login/client/EncryptionResponsePacket.js";
|
||||
import LoginDisconnectPacket from "./packet/login/server/LoginDisconnectPacket.js";
|
||||
import LoginSuccessPacket from "./packet/login/server/LoginSuccessPacket.js";
|
||||
import EnableCompressionPacket from "./packet/login/server/EnableCompressionPacket.js";
|
||||
import ServerKeepAlivePacket from "./packet/play/server/ServerKeepAlivePacket.js";
|
||||
import ServerJoinGamePacket from "./packet/play/server/ServerJoinGamePacket.js";
|
||||
import ClientKeepAlivePacket from "./packet/play/client/ClientKeepAlivePacket.js";
|
||||
import ClientChatPacket from "./packet/play/client/ClientChatPacket.js";
|
||||
import ClientPlayerMovementPacket from "./packet/play/client/ClientPlayerMovementPacket.js";
|
||||
import ClientPlayerRotationPacket from "./packet/play/client/ClientPlayerRotationPacket.js";
|
||||
import ClientPlayerPositionPacket from "./packet/play/client/ClientPlayerPositionPacket.js";
|
||||
import ClientPlayerPositionRotationPacket from "./packet/play/client/ClientPlayerPositionRotationPacket.js";
|
||||
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";
|
||||
|
||||
export default class PacketRegistry {
|
||||
|
||||
@@ -23,9 +37,26 @@ export default class PacketRegistry {
|
||||
// Register login
|
||||
this.registerServer(ProtocolState.LOGIN, 0x00, LoginDisconnectPacket);
|
||||
this.registerServer(ProtocolState.LOGIN, 0x01, EncryptionRequestPacket);
|
||||
this.registerServer(ProtocolState.LOGIN, 0x02, LoginSuccessPacket);
|
||||
this.registerServer(ProtocolState.LOGIN, 0x03, EnableCompressionPacket);
|
||||
|
||||
this.registerClient(ProtocolState.LOGIN, 0x00, LoginStartPacket);
|
||||
this.registerClient(ProtocolState.LOGIN, 0x01, EncryptionResponsePacket);
|
||||
|
||||
// Register play
|
||||
this.registerServer(ProtocolState.PLAY, 0x00, ServerKeepAlivePacket);
|
||||
this.registerServer(ProtocolState.PLAY, 0x01, ServerJoinGamePacket);
|
||||
this.registerServer(ProtocolState.PLAY, 0x02, ServerChatPacket);
|
||||
this.registerServer(ProtocolState.PLAY, 0x21, ServerChunkDataPacket);
|
||||
this.registerServer(ProtocolState.PLAY, 0x23, ServerBlockChangePacket);
|
||||
this.registerServer(ProtocolState.PLAY, 0x26, ServerMultiChunkDataPacket);
|
||||
|
||||
this.registerClient(ProtocolState.PLAY, 0x00, ClientKeepAlivePacket);
|
||||
this.registerClient(ProtocolState.PLAY, 0x01, ClientChatPacket);
|
||||
this.registerClient(ProtocolState.PLAY, 0x03, ClientPlayerMovementPacket);
|
||||
this.registerClient(ProtocolState.PLAY, 0x04, ClientPlayerPositionPacket);
|
||||
this.registerClient(ProtocolState.PLAY, 0x05, ClientPlayerRotationPacket);
|
||||
this.registerClient(ProtocolState.PLAY, 0x06, ClientPlayerPositionRotationPacket);
|
||||
}
|
||||
|
||||
registerClient(state, id, packet) {
|
||||
@@ -37,29 +68,29 @@ export default class PacketRegistry {
|
||||
}
|
||||
|
||||
_register(registry, state, id, packet) {
|
||||
if (typeof registry[state] === "undefined") {
|
||||
registry[state] = [];
|
||||
if (typeof registry[state.getId()] === "undefined") {
|
||||
registry[state.getId()] = [];
|
||||
}
|
||||
registry[state][id] = packet;
|
||||
registry[state.getId()][id] = packet;
|
||||
}
|
||||
|
||||
getServerBoundById(state, id) {
|
||||
if (typeof this.packetsServer[state][id] === "undefined") {
|
||||
if (typeof this.packetsServer[state.getId()][id] === "undefined") {
|
||||
return null;
|
||||
}
|
||||
return this.packetsServer[state][id];
|
||||
return this.packetsServer[state.getId()][id];
|
||||
}
|
||||
|
||||
getClientBoundById(state, id) {
|
||||
if (typeof this.packetsClient[state][id] === "undefined") {
|
||||
if (typeof this.packetsClient[state.getId()][id] === "undefined") {
|
||||
return null;
|
||||
}
|
||||
return this.packetsClient[state][id];
|
||||
return this.packetsClient[state.getId()][id];
|
||||
}
|
||||
|
||||
getClientBoundPacketId(state, packet) {
|
||||
for (let id in this.packetsClient[state]) {
|
||||
if (this.packetsClient[state][id] === packet.constructor) {
|
||||
for (let id in this.packetsClient[state.getId()]) {
|
||||
if (this.packetsClient[state.getId()][id] === packet.constructor) {
|
||||
return id;
|
||||
}
|
||||
}
|
||||
@@ -68,7 +99,7 @@ export default class PacketRegistry {
|
||||
|
||||
getServerBoundPacketId(state, packet) {
|
||||
for (let id in this.packetsServer[state]) {
|
||||
if (this.packetsServer[state][id] === packet.constructor) {
|
||||
if (this.packetsServer[state.getId()][id] === packet.constructor) {
|
||||
return id;
|
||||
}
|
||||
}
|
||||
@@ -79,14 +110,14 @@ export default class PacketRegistry {
|
||||
for (const [state, value] of Object.entries(this.packetsClient)) {
|
||||
for (let id in value) {
|
||||
if (value[id] === packet.constructor) {
|
||||
return parseInt(state);
|
||||
return ProtocolState.fromId(parseInt(state));
|
||||
}
|
||||
}
|
||||
}
|
||||
for (const [state, value] of Object.entries(this.packetsServer)) {
|
||||
for (let id in value) {
|
||||
if (value[id] === packet.constructor) {
|
||||
return parseInt(state);
|
||||
return ProtocolState.fromId(parseInt(state));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,11 +1,19 @@
|
||||
export default class ProtocolState {
|
||||
static HANDSHAKE = -1;
|
||||
static PLAY = 0;
|
||||
static STATUS = 1;
|
||||
static LOGIN = 2;
|
||||
static HANDSHAKE = new ProtocolState(-1);
|
||||
static PLAY = new ProtocolState(0);
|
||||
static STATUS = new ProtocolState(1);
|
||||
static LOGIN = new ProtocolState(2);
|
||||
|
||||
static getName(state) {
|
||||
switch (state) {
|
||||
constructor(id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
getId() {
|
||||
return this.id;
|
||||
}
|
||||
|
||||
getName() {
|
||||
switch (this) {
|
||||
case ProtocolState.HANDSHAKE:
|
||||
return "HANDSHAKE";
|
||||
case ProtocolState.LOGIN:
|
||||
@@ -18,4 +26,22 @@ export default class ProtocolState {
|
||||
return "UNKNOWN";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static fromId(id) {
|
||||
for (let state of this.values()) {
|
||||
if (state.getId() === id) {
|
||||
return state;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static values() {
|
||||
return [
|
||||
ProtocolState.HANDSHAKE,
|
||||
ProtocolState.LOGIN,
|
||||
ProtocolState.PLAY,
|
||||
ProtocolState.STATUS
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
import PlayerEntity from "../../entity/PlayerEntity.js";
|
||||
|
||||
export default class PlayerController {
|
||||
|
||||
constructor(minecraft) {
|
||||
this.minecraft = minecraft;
|
||||
}
|
||||
|
||||
createPlayer(world) {
|
||||
return new PlayerEntity(this.minecraft, world);
|
||||
}
|
||||
|
||||
sendChatMessage(message) {
|
||||
this.minecraft.addMessageToChat("<" + this.minecraft.player.username + "> " + message);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
import PlayerController from "./PlayerController.js";
|
||||
import PlayerEntityMultiplayer from "../../entity/PlayerEntityMultiplayer.js";
|
||||
import ClientChatPacket from "../packet/play/client/ClientChatPacket.js";
|
||||
|
||||
export default class PlayerControllerMultiplayer extends PlayerController {
|
||||
|
||||
constructor(minecraft, networkHandler) {
|
||||
super(minecraft);
|
||||
|
||||
this.networkHandler = networkHandler;
|
||||
}
|
||||
|
||||
createPlayer(world) {
|
||||
return new PlayerEntityMultiplayer(this.minecraft, world, this.networkHandler);
|
||||
}
|
||||
|
||||
sendChatMessage(message) {
|
||||
this.networkHandler.sendPacket(new ClientChatPacket(message));
|
||||
}
|
||||
|
||||
getNetworkHandler() {
|
||||
return this.networkHandler;
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,10 @@
|
||||
import PacketHandler from "../PacketHandler.js";
|
||||
import PacketHandler from "./PacketHandler.js";
|
||||
import EncryptionResponsePacket from "../packet/login/client/EncryptionResponsePacket.js";
|
||||
import CryptManager from "../util/CryptManager.js";
|
||||
import GuiDisconnected from "../../gui/screens/GuiDisconnected.js";
|
||||
import Authentication from "../util/Authentication.js";
|
||||
import NetworkPlayHandler from "./NetworkPlayHandler.js";
|
||||
import ProtocolState from "../ProtocolState.js";
|
||||
|
||||
export default class NetworkLoginHandler extends PacketHandler {
|
||||
|
||||
@@ -9,10 +12,19 @@ export default class NetworkLoginHandler extends PacketHandler {
|
||||
super();
|
||||
|
||||
this.networkManager = networkManager;
|
||||
this.authentication = new Authentication(networkManager);
|
||||
}
|
||||
|
||||
handleEncryptionRequest(packet) {
|
||||
// Create an AES key for the packet encryption
|
||||
let secretKey = CryptManager.createNewSharedKey();
|
||||
|
||||
// Send join server request to Mojang
|
||||
let session = this.networkManager.minecraft.getSession();
|
||||
let serverId = this.authentication.createServerHash(packet.serverId, secretKey, packet.publicKey);
|
||||
this.authentication.joinServer(session.getProfile(), session.getAccessToken(), serverId);
|
||||
|
||||
// Send encryption response
|
||||
this.networkManager.sendPacket(new EncryptionResponsePacket(secretKey, packet.publicKey, packet.verifyToken));
|
||||
|
||||
// Enable encryption
|
||||
@@ -24,8 +36,19 @@ export default class NetworkLoginHandler extends PacketHandler {
|
||||
this.networkManager.minecraft.displayScreen(new GuiDisconnected(packet.message));
|
||||
}
|
||||
|
||||
onDisconnect() {
|
||||
handleLoginSuccess(packet) {
|
||||
this.networkManager.setState(ProtocolState.PLAY);
|
||||
this.networkManager.setNetworkHandler(new NetworkPlayHandler(this.networkManager, packet.profile));
|
||||
}
|
||||
|
||||
handleEnableCompression(packet) {
|
||||
this.networkManager.setCompressionThreshold(packet.getCompressionThreshold());
|
||||
}
|
||||
|
||||
onDisconnect() {
|
||||
if (this.networkManager.minecraft.isInGame()) {
|
||||
this.networkManager.minecraft.displayScreen(new GuiDisconnected("Disconnected from server"));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
import PacketHandler from "./PacketHandler.js";
|
||||
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";
|
||||
|
||||
export default class NetworkPlayHandler extends PacketHandler {
|
||||
|
||||
constructor(networkManager, profile) {
|
||||
super();
|
||||
|
||||
this.minecraft = networkManager.minecraft;
|
||||
this.networkManager = networkManager;
|
||||
this.profile = profile;
|
||||
}
|
||||
|
||||
handleKeepAlive(packet) {
|
||||
this.networkManager.sendPacket(new ClientKeepAlivePacket(packet.getId()));
|
||||
}
|
||||
|
||||
handleJoinGame(packet) {
|
||||
this.minecraft.playerController = new PlayerControllerMultiplayer(this.minecraft, this);
|
||||
let world = new WorldClient(this.minecraft);
|
||||
this.minecraft.loadWorld(world);
|
||||
}
|
||||
|
||||
handleServerChat(packet) {
|
||||
this.minecraft.ingameOverlay.chatOverlay.addMessage(packet.getMessage());
|
||||
}
|
||||
|
||||
handleChunkData(packet) {
|
||||
let provider = this.minecraft.world.getChunkProvider();
|
||||
|
||||
if (packet.isFullChunk()) {
|
||||
if (packet.getDataSize() === 0) {
|
||||
provider.unloadChunk(packet.getX(), packet.getZ());
|
||||
return;
|
||||
}
|
||||
|
||||
provider.loadChunk(packet.getX(), packet.getZ());
|
||||
}
|
||||
|
||||
let chunk = this.minecraft.world.getChunkAt(packet.getX(), packet.getZ());
|
||||
chunk.fillChunk(packet.getData(), packet.getDataSize(), packet.isFullChunk());
|
||||
}
|
||||
|
||||
handleMultiChunkData(packet) {
|
||||
for (let chunkData of packet.getChunkData()) {
|
||||
this.handleChunkData(chunkData);
|
||||
}
|
||||
}
|
||||
|
||||
handleBlockChange(packet) {
|
||||
let position = packet.getBlockPosition();
|
||||
|
||||
let blockState = packet.getBlockState();
|
||||
let typeId = blockState >> 4;
|
||||
|
||||
this.minecraft.world.setBlockAt(position.getX(), position.getY(), position.getZ(), typeId);
|
||||
}
|
||||
|
||||
onDisconnect() {
|
||||
if (this.minecraft.isInGame()) {
|
||||
this.minecraft.displayScreen(new GuiDisconnected("Disconnected from server"));
|
||||
}
|
||||
}
|
||||
|
||||
getNetworkManager() {
|
||||
return this.networkManager;
|
||||
}
|
||||
|
||||
sendPacket(packet) {
|
||||
this.networkManager.sendPacket(packet);
|
||||
}
|
||||
|
||||
}
|
||||
-8
@@ -4,14 +4,6 @@ export default class PacketHandler {
|
||||
|
||||
}
|
||||
|
||||
handleStatusResponse(packet) {
|
||||
|
||||
}
|
||||
|
||||
handleEncryptionRequest(packet) {
|
||||
|
||||
}
|
||||
|
||||
onDisconnect() {
|
||||
|
||||
}
|
||||
@@ -13,7 +13,7 @@ export default class HandshakePacket extends Packet {
|
||||
buffer.writeVarInt(this.version); // Protocol version
|
||||
buffer.writeString("localhost"); // Server address
|
||||
buffer.writeShort(25565); // Server port
|
||||
buffer.writeVarInt(this.nextState); // Next state
|
||||
buffer.writeVarInt(this.nextState.getId()); // Next state
|
||||
}
|
||||
|
||||
read(buffer) {
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
import Packet from "../../../Packet.js";
|
||||
|
||||
export default class EnableCompressionPacket extends Packet {
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
this.compressionThreshold = 0;
|
||||
}
|
||||
|
||||
write(buffer) {
|
||||
|
||||
}
|
||||
|
||||
read(buffer) {
|
||||
this.compressionThreshold = buffer.readVarInt();
|
||||
}
|
||||
|
||||
handle(handler) {
|
||||
handler.handleEnableCompression(this);
|
||||
}
|
||||
|
||||
getCompressionThreshold() {
|
||||
return this.compressionThreshold;
|
||||
}
|
||||
}
|
||||
@@ -14,7 +14,7 @@ export default class LoginDisconnectPacket extends Packet {
|
||||
}
|
||||
|
||||
read(buffer) {
|
||||
this.message = format(JSON.parse(buffer.readString()));
|
||||
this.message = format(JSON.parse(buffer.readString(32767)));
|
||||
}
|
||||
|
||||
handle(handler) {
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
import Packet from "../../../Packet.js";
|
||||
import UUID from "../../../../../util/UUID.js";
|
||||
import GameProfile from "../../../../../util/GameProfile.js";
|
||||
|
||||
export default class LoginSuccessPacket extends Packet {
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
this.profile = null;
|
||||
}
|
||||
|
||||
write(buffer) {
|
||||
|
||||
}
|
||||
|
||||
read(buffer) {
|
||||
let uuid = UUID.fromString(buffer.readString());
|
||||
let username = buffer.readString();
|
||||
|
||||
this.profile = new GameProfile(uuid, username);
|
||||
}
|
||||
|
||||
handle(handler) {
|
||||
handler.handleLoginSuccess(this);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
import Packet from "../../../Packet.js";
|
||||
|
||||
export default class ClientChatPacket extends Packet {
|
||||
|
||||
constructor(message) {
|
||||
super();
|
||||
|
||||
this.message = message;
|
||||
}
|
||||
|
||||
write(buffer) {
|
||||
buffer.writeString(this.message);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
import Packet from "../../../Packet.js";
|
||||
|
||||
export default class ClientKeepAlivePacket extends Packet {
|
||||
|
||||
constructor(id = 0) {
|
||||
super();
|
||||
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
write(buffer) {
|
||||
buffer.writeVarInt(this.id);
|
||||
}
|
||||
|
||||
getId() {
|
||||
return this.id;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
import Packet from "../../../Packet.js";
|
||||
|
||||
export default class ClientPlayerMovementPacket extends Packet {
|
||||
|
||||
constructor(onGround) {
|
||||
super();
|
||||
|
||||
this.position = false;
|
||||
this.rotation = false;
|
||||
|
||||
this.onGround = onGround;
|
||||
|
||||
this.x = 0;
|
||||
this.y = 0;
|
||||
this.z = 0;
|
||||
|
||||
this.yaw = 0;
|
||||
this.pitch = 0;
|
||||
}
|
||||
|
||||
write(buffer) {
|
||||
if (this.position) {
|
||||
buffer.writeDouble(this.x);
|
||||
buffer.writeDouble(this.y);
|
||||
buffer.writeDouble(this.z);
|
||||
}
|
||||
|
||||
if (this.rotation) {
|
||||
buffer.writeFloat(this.yaw);
|
||||
buffer.writeFloat(this.pitch);
|
||||
}
|
||||
|
||||
buffer.writeBoolean(this.onGround);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
import ClientPlayerMovementPacket from "./ClientPlayerMovementPacket.js";
|
||||
|
||||
export default class ClientPlayerPositionPacket extends ClientPlayerMovementPacket {
|
||||
|
||||
constructor(onGround, x, y, z) {
|
||||
super(onGround);
|
||||
|
||||
this.position = true;
|
||||
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
this.z = z;
|
||||
}
|
||||
}
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
import ClientPlayerMovementPacket from "./ClientPlayerMovementPacket.js";
|
||||
|
||||
export default class ClientPlayerPositionRotationPacket extends ClientPlayerMovementPacket {
|
||||
|
||||
constructor(onGround, x, y, z, yaw, pitch) {
|
||||
super(onGround);
|
||||
|
||||
this.position = true;
|
||||
this.rotation = true;
|
||||
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
this.z = z;
|
||||
|
||||
this.yaw = yaw;
|
||||
this.pitch = pitch;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
import ClientPlayerMovementPacket from "./ClientPlayerMovementPacket.js";
|
||||
|
||||
export default class ClientPlayerRotationPacket extends ClientPlayerMovementPacket {
|
||||
|
||||
constructor(onGround, yaw, pitch) {
|
||||
super(onGround);
|
||||
|
||||
this.rotation = true;
|
||||
|
||||
this.yaw = yaw;
|
||||
this.pitch = pitch;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
import Packet from "../../../Packet.js";
|
||||
|
||||
export default class ServerBlockChangePacket extends Packet {
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
this.blockPosition = null;
|
||||
}
|
||||
|
||||
read(buffer) {
|
||||
this.blockPosition = buffer.readBlockPosition();
|
||||
this.blockState = buffer.readVarInt();
|
||||
}
|
||||
|
||||
handle(handler) {
|
||||
handler.handleBlockChange(this);
|
||||
}
|
||||
|
||||
getBlockPosition() {
|
||||
return this.blockPosition;
|
||||
}
|
||||
|
||||
getBlockState() {
|
||||
return this.blockState;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
import Packet from "../../../Packet.js";
|
||||
import {format} from "../../../../../../../../../libraries/chat.js";
|
||||
|
||||
export default class ServerChatPacket extends Packet {
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
this.message = "";
|
||||
this.type = 0;
|
||||
}
|
||||
|
||||
read(buffer) {
|
||||
this.message = format(JSON.parse(buffer.readString(32767)), {
|
||||
useAnsiCodes: true
|
||||
});
|
||||
this.type = buffer.readByte();
|
||||
}
|
||||
|
||||
handle(handler) {
|
||||
handler.handleServerChat(this);
|
||||
}
|
||||
|
||||
getMessage() {
|
||||
return this.message;
|
||||
}
|
||||
|
||||
getType() {
|
||||
return this.type;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
import Packet from "../../../Packet.js";
|
||||
|
||||
export default class ServerChunkDataPacket extends Packet {
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
this.x = 0;
|
||||
this.z = 0;
|
||||
this.fullChunk = false;
|
||||
this.dataSize = 0;
|
||||
this.data = [];
|
||||
}
|
||||
|
||||
read(buffer) {
|
||||
this.x = buffer.readInt();
|
||||
this.z = buffer.readInt();
|
||||
this.fullChunk = buffer.readBoolean();
|
||||
this.dataSize = buffer.readShort();
|
||||
this.data = buffer.readByteArray();
|
||||
}
|
||||
|
||||
handle(handler) {
|
||||
handler.handleChunkData(this);
|
||||
}
|
||||
|
||||
getX() {
|
||||
return this.x;
|
||||
}
|
||||
|
||||
getZ() {
|
||||
return this.z;
|
||||
}
|
||||
|
||||
isFullChunk() {
|
||||
return this.fullChunk;
|
||||
}
|
||||
|
||||
getDataSize() {
|
||||
return this.dataSize;
|
||||
}
|
||||
|
||||
getData() {
|
||||
return this.data;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
import Packet from "../../../Packet.js";
|
||||
|
||||
export default class ServerJoinGamePacket extends Packet {
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
this.entityId = 0;
|
||||
this.hardcoreMode = false;
|
||||
this.gameType = 0;
|
||||
this.dimension = 0;
|
||||
this.difficulty = 0;
|
||||
this.maxPlayers = 0;
|
||||
this.worldType = "";
|
||||
this.reducedDebugInfo = false;
|
||||
}
|
||||
|
||||
write(buffer) {
|
||||
|
||||
}
|
||||
|
||||
read(buffer) {
|
||||
this.entityId = buffer.readVarInt();
|
||||
let bits = buffer.readByte();
|
||||
this.hardcoreMode = (bits & 8) === 8;
|
||||
this.gameType = bits & -9;
|
||||
this.dimension = buffer.readByte();
|
||||
this.difficulty = buffer.readByte();
|
||||
this.maxPlayers = buffer.readByte();
|
||||
this.worldType = buffer.readString();
|
||||
this.reducedDebugInfo = buffer.readBoolean();
|
||||
}
|
||||
|
||||
handle(handler) {
|
||||
handler.handleJoinGame(this);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
import Packet from "../../../Packet.js";
|
||||
|
||||
export default class ServerKeepAlivePacket extends Packet {
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
this.id = 0;
|
||||
}
|
||||
|
||||
read(buffer) {
|
||||
this.id = buffer.readVarInt();
|
||||
}
|
||||
|
||||
handle(handler) {
|
||||
handler.handleKeepAlive(this);
|
||||
}
|
||||
|
||||
getId() {
|
||||
return this.id;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
import Packet from "../../../Packet.js";
|
||||
import ServerChunkDataPacket from "./ServerChunkDataPacket.js";
|
||||
|
||||
export default class ServerMultiChunkDataPacket extends Packet {
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
this.overworld = false;
|
||||
this.chunkData = [];
|
||||
}
|
||||
|
||||
read(buffer) {
|
||||
this.overworld = buffer.readBoolean();
|
||||
|
||||
let amount = buffer.readVarInt();
|
||||
for (let i = 0; i < amount; i++) {
|
||||
let x = buffer.readInt();
|
||||
let y = buffer.readInt();
|
||||
let dataSize = buffer.readShort() & 65535;
|
||||
let data = buffer.readByteArray();
|
||||
|
||||
this.chunkData.push(new ServerChunkDataPacket(x, y, dataSize, data));
|
||||
}
|
||||
}
|
||||
|
||||
handle(handler) {
|
||||
handler.handleMultiChunkData(this);
|
||||
}
|
||||
|
||||
getChunkData() {
|
||||
return this.chunkData;
|
||||
}
|
||||
|
||||
isOverworld() {
|
||||
return this.overworld;
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
import PacketHandler from "../PacketHandler.js";
|
||||
import PacketHandler from "../handler/PacketHandler.js";
|
||||
import GuiDisconnected from "../../gui/screens/GuiDisconnected.js";
|
||||
|
||||
export default class NetworkStatusHandler extends PacketHandler {
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
import ByteBuf from "./ByteBuf.js";
|
||||
import {require} from "../../../../../Start.js";
|
||||
|
||||
export default class Authentication {
|
||||
|
||||
constructor(networkManager) {
|
||||
this.networkManager = networkManager;
|
||||
}
|
||||
|
||||
joinServer(profile, accessToken, serverId) {
|
||||
this.networkManager.sendProxyPacket(1, {
|
||||
"accessToken": accessToken,
|
||||
"selectedProfile": profile.getCompactUUID(),
|
||||
"serverId": serverId
|
||||
})
|
||||
}
|
||||
|
||||
createServerHash(serverId, secretKey, publicKey) {
|
||||
// Create hash
|
||||
let bytes = require("sha1").create()
|
||||
.update(new TextEncoder().encode(serverId))
|
||||
.update(secretKey)
|
||||
.update(new Uint8Array(publicKey))
|
||||
.digest()
|
||||
|
||||
// Convert to hex string
|
||||
let buffer = new ByteBuf(new Int8Array(bytes));
|
||||
let sign = '';
|
||||
|
||||
// Handle negative hashes
|
||||
if (buffer.readByte() < 0) {
|
||||
let carry = true
|
||||
for (let pos = buffer.length() - 1; pos >= 0; --pos) {
|
||||
let value = buffer.readByte(pos);
|
||||
let newValue = ~value & 0xff
|
||||
|
||||
if (carry) {
|
||||
carry = newValue === 0xff
|
||||
buffer.writeByte(carry ? 0 : newValue + 1, pos);
|
||||
} else {
|
||||
buffer.writeByte(newValue, pos)
|
||||
}
|
||||
}
|
||||
sign = '-';
|
||||
}
|
||||
|
||||
// Convert to hex string
|
||||
return sign + buffer.toString().replace(/^0+/g, '');
|
||||
}
|
||||
}
|
||||
@@ -1,3 +1,6 @@
|
||||
import Long from "../../../../../../../libraries/long.js";
|
||||
import BlockPosition from "../../../util/BlockPosition.js";
|
||||
|
||||
export default class ByteBuf {
|
||||
|
||||
static SEGMENT_BITS = 0x7F;
|
||||
@@ -24,7 +27,12 @@ export default class ByteBuf {
|
||||
return this.array;
|
||||
}
|
||||
|
||||
readByte() {
|
||||
getSlicedArray(length = this.array.length - this.pos) {
|
||||
return this.array.slice(this.pos, this.pos + length);
|
||||
}
|
||||
|
||||
readByte(pos = this.pos) {
|
||||
this.pos = pos;
|
||||
return this.array[this.pos++];
|
||||
}
|
||||
|
||||
@@ -40,32 +48,42 @@ export default class ByteBuf {
|
||||
}
|
||||
|
||||
readLong() {
|
||||
return this.array[this.pos++] << 56
|
||||
| this.array[this.pos++] << 48
|
||||
| this.array[this.pos++] << 40
|
||||
| this.array[this.pos++] << 32
|
||||
| this.array[this.pos++] << 24
|
||||
| this.array[this.pos++] << 16
|
||||
| this.array[this.pos++] << 8
|
||||
| this.array[this.pos++];
|
||||
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++]));
|
||||
}
|
||||
|
||||
readFloat() {
|
||||
return this.readInt() / (1 << 24);
|
||||
return new Float32Array(new Uint32Array([this.readInt(), 0, 0, 0, 0, 0, 0, 0]).buffer)[0];
|
||||
}
|
||||
|
||||
readDouble() {
|
||||
return this.readLong() / (1 << 53);
|
||||
let lng = this.readLong();
|
||||
return new Float64Array(new Uint32Array([lng.low >>> 0, lng.high >>> 0, 0, 0, 0, 0, 0, 0]).buffer)[0];
|
||||
}
|
||||
|
||||
readString() {
|
||||
readBoolean() {
|
||||
return this.readByte() !== 0;
|
||||
}
|
||||
|
||||
readString(maxLength) {
|
||||
let len = this.readVarInt();
|
||||
if (len > maxLength * 4) {
|
||||
throw new Error("Trying to read string longer than max length (" + len + " > " + (maxLength * 4) + ")");
|
||||
}
|
||||
|
||||
let array = new Uint8Array(len);
|
||||
this.read(array, len);
|
||||
return new TextDecoder().decode(array);
|
||||
}
|
||||
|
||||
writeByte(value) {
|
||||
writeByte(value, pos = this.pos) {
|
||||
this.pos = pos;
|
||||
this.extendIfNeeded(1);
|
||||
this.array[this.pos++] = value;
|
||||
}
|
||||
@@ -86,22 +104,24 @@ export default class ByteBuf {
|
||||
|
||||
writeLong(value) {
|
||||
this.extendIfNeeded(8);
|
||||
this.array[this.pos++] = value >> 56;
|
||||
this.array[this.pos++] = value >> 48;
|
||||
this.array[this.pos++] = value >> 40;
|
||||
this.array[this.pos++] = value >> 32;
|
||||
this.array[this.pos++] = value >> 24;
|
||||
this.array[this.pos++] = value >> 16;
|
||||
this.array[this.pos++] = value >> 8;
|
||||
this.array[this.pos++] = value;
|
||||
this.array[this.pos++] = value.shiftRightUnsigned(56).toInt() & 0xFF;
|
||||
this.array[this.pos++] = value.shiftRightUnsigned(48).toInt() & 0xFF;
|
||||
this.array[this.pos++] = value.shiftRightUnsigned(40).toInt() & 0xFF;
|
||||
this.array[this.pos++] = value.shiftRightUnsigned(32).toInt() & 0xFF;
|
||||
this.array[this.pos++] = value.shiftRightUnsigned(24).toInt() & 0xFF;
|
||||
this.array[this.pos++] = value.shiftRightUnsigned(16).toInt() & 0xFF;
|
||||
this.array[this.pos++] = value.shiftRightUnsigned(8).toInt() & 0xFF;
|
||||
this.array[this.pos++] = value.toInt() & 0xFF;
|
||||
}
|
||||
|
||||
writeFloat(value) {
|
||||
this.writeInt(value * (1 << 24));
|
||||
let buffer = new Uint32Array(new Float32Array([value, 0, 0, 0, 0, 0, 0, 0]).buffer);
|
||||
this.writeInt(buffer[0]);
|
||||
}
|
||||
|
||||
writeDouble(value) {
|
||||
this.writeLong(value * (1 << 53));
|
||||
let buffer = new Uint32Array(new Float64Array([value, 0, 0, 0, 0, 0, 0, 0]).buffer);
|
||||
this.writeLong(Long.fromBits(buffer[0], buffer[1]));
|
||||
}
|
||||
|
||||
writeString(value) {
|
||||
@@ -110,6 +130,10 @@ export default class ByteBuf {
|
||||
this.write(array);
|
||||
}
|
||||
|
||||
writeBoolean(value) {
|
||||
this.writeByte(value ? 1 : 0);
|
||||
}
|
||||
|
||||
writeVarInt(value) {
|
||||
while (true) {
|
||||
if ((value & ~ByteBuf.SEGMENT_BITS) === 0) {
|
||||
@@ -131,6 +155,10 @@ export default class ByteBuf {
|
||||
}
|
||||
}
|
||||
|
||||
skipBytes(length) {
|
||||
this.pos += length;
|
||||
}
|
||||
|
||||
extendIfNeeded(bytes) {
|
||||
if (this.pos + bytes > this.array.length) {
|
||||
let newArray = new Uint8Array(this.array.length + bytes);
|
||||
@@ -169,4 +197,22 @@ export default class ByteBuf {
|
||||
this.writeVarInt(array.length);
|
||||
this.write(array);
|
||||
}
|
||||
|
||||
writeBlockPosition(blockPosition) {
|
||||
this.writeLong(blockPosition.toLong());
|
||||
}
|
||||
|
||||
readBlockPosition() {
|
||||
return BlockPosition.fromLong(this.readLong());
|
||||
}
|
||||
|
||||
readableBytes() {
|
||||
return this.array.length - this.pos;
|
||||
}
|
||||
|
||||
toString() {
|
||||
return Array.from(this.array, byte => {
|
||||
return ('0' + (byte & 0xFF).toString(16)).slice(-2);
|
||||
}).join('');
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,6 @@
|
||||
import Random from "../../../util/Random.js";
|
||||
import ByteBuf from "./ByteBuf.js";
|
||||
import {bigIntToBytes, bytesToBigInt, modPow} from "../../../../../../../libraries/modpow.js";
|
||||
import {parseAsn1} from "../../../../../../../libraries/asn1.js";
|
||||
import {require} from "../../../../../Start.js";
|
||||
|
||||
export default class CryptManager {
|
||||
|
||||
@@ -13,11 +12,12 @@ export default class CryptManager {
|
||||
|
||||
static encryptRSA(publicKey, data) {
|
||||
// Parse asn1 public key
|
||||
let asn1 = parseAsn1(new Uint8Array(publicKey))
|
||||
let asn1 = require("ASN1").parse(new Uint8Array(publicKey))
|
||||
let bigintModArith = require("bigint-mod-arith");
|
||||
|
||||
// Extract n an e of the public key
|
||||
let n = bytesToBigInt(asn1.children[1].children[0].children[0].value);
|
||||
let e = bytesToBigInt(asn1.children[1].children[0].children[1].value);
|
||||
// Extract n and e of the public key
|
||||
let n = bigintModArith.bytesToBigInt(asn1.children[1].children[0].children[0].value);
|
||||
let e = bigintModArith.bytesToBigInt(asn1.children[1].children[0].children[1].value);
|
||||
|
||||
// Check length of public key
|
||||
let length = (n.toString(2).length + 7) >> 3;
|
||||
@@ -44,13 +44,13 @@ export default class CryptManager {
|
||||
let reversed = buffer.getArray().reverse();
|
||||
|
||||
// Convert to bigint
|
||||
let bigInt = bytesToBigInt(reversed);
|
||||
let bigInt = bigintModArith.bytesToBigInt(reversed);
|
||||
|
||||
// Encrypt
|
||||
bigInt = modPow(bigInt, e, n);
|
||||
bigInt = bigintModArith.modPow(bigInt, e, n);
|
||||
|
||||
// Convert to bytes
|
||||
return bigIntToBytes(bigInt);
|
||||
return bigintModArith.bigIntToBytes(bigInt);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -558,7 +558,7 @@ export default class WorldRenderer {
|
||||
let renderDistance = this.minecraft.settings.viewDistance;
|
||||
|
||||
// Update chunks
|
||||
for (let [index, chunk] of world.chunks) {
|
||||
for (let [index, chunk] of world.getChunkProvider().getChunks()) {
|
||||
let distanceX = Math.abs(cameraChunkX - chunk.x);
|
||||
let distanceZ = Math.abs(cameraChunkZ - chunk.z);
|
||||
|
||||
@@ -599,7 +599,7 @@ export default class WorldRenderer {
|
||||
|
||||
// TODO Implement chunk unloading
|
||||
//let index = chunk.x + (chunk.z << 16);
|
||||
//world.chunks.delete(index);
|
||||
//world.getChunkProvider().getChunks().delete(index);
|
||||
//world.group.remove(chunk.group);
|
||||
}
|
||||
}
|
||||
@@ -637,7 +637,7 @@ export default class WorldRenderer {
|
||||
|
||||
rebuildAll() {
|
||||
let world = this.minecraft.world;
|
||||
for (let [index, chunk] of world.chunks) {
|
||||
for (let [index, chunk] of world.getChunkProvider().getChunks()) {
|
||||
chunk.setModifiedAllSections();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import PlayerRenderer from "./entity/PlayerRenderer.js";
|
||||
import PlayerEntity from "../../entity/PlayerEntity.js";
|
||||
import PlayerEntityMultiplayer from "../../entity/PlayerEntityMultiplayer.js";
|
||||
|
||||
export default class EntityRenderManager {
|
||||
|
||||
@@ -8,6 +9,7 @@ export default class EntityRenderManager {
|
||||
|
||||
this.renderers = [];
|
||||
this.push(PlayerEntity, PlayerRenderer);
|
||||
this.push(PlayerEntityMultiplayer, PlayerRenderer);
|
||||
}
|
||||
|
||||
push(entityType, entityRenderer) {
|
||||
@@ -18,6 +20,6 @@ export default class EntityRenderManager {
|
||||
if (!(entity.constructor.name in this.renderers)) {
|
||||
return null;
|
||||
}
|
||||
return new this.renderers[entity.constructor.name].prototype.constructor(this.worldRenderer);
|
||||
return new this.renderers[entity.constructor.name]["prototype"]["constructor"](this.worldRenderer);
|
||||
}
|
||||
}
|
||||
@@ -300,6 +300,34 @@ export default class Chunk {
|
||||
return true;
|
||||
}
|
||||
|
||||
fillChunk(data, size, fullChunk) {
|
||||
let i = 0;
|
||||
for (let layer = 0; layer < Chunk.SECTION_AMOUNT; layer++) {
|
||||
if ((size & 1 << layer) !== 0) {
|
||||
let section = this.getSection(layer);
|
||||
|
||||
for (let k = 0; k < ChunkSection.SIZE * ChunkSection.SIZE * ChunkSection.SIZE; k++) {
|
||||
let x = k & 15;
|
||||
let z = (k >> 4) & 15;
|
||||
let y = (k >> 8) & 15;
|
||||
|
||||
let value = (((data[i] & 0xFF) | (data[i + 1] & 0xFF) << 8));
|
||||
let typeId = value >> 4;
|
||||
let meta = value & 0xF; // TODO handle meta of block
|
||||
|
||||
// TODO support more blocks
|
||||
if (typeId !== 0 && Block.getById(typeId) === null) {
|
||||
typeId = 1;
|
||||
}
|
||||
|
||||
section.setBlockAt(x, y, z, typeId);
|
||||
|
||||
i += 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
getBlockID(x, y, z) {
|
||||
return this.getBlockAt(x, y, z);
|
||||
}
|
||||
@@ -317,7 +345,7 @@ export default class Chunk {
|
||||
}
|
||||
|
||||
rebuild(renderer) {
|
||||
for (let y = 0; y < this.sections.length; y++) {
|
||||
for (let y = 0; y < Chunk.SECTION_AMOUNT; y++) {
|
||||
this.sections[y].rebuild(renderer);
|
||||
}
|
||||
}
|
||||
@@ -331,7 +359,7 @@ export default class Chunk {
|
||||
}
|
||||
|
||||
setModifiedAllSections() {
|
||||
for (let y = 0; y < this.sections.length; y++) {
|
||||
for (let y = 0; y < Chunk.SECTION_AMOUNT; y++) {
|
||||
this.sections[y].isModified = true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -66,7 +66,7 @@ export default class ChunkSection {
|
||||
let absoluteZ = this.z * ChunkSection.SIZE + z;
|
||||
|
||||
let block = Block.getById(typeId);
|
||||
if (block.isTranslucent() !== isTranslucentRenderPhase) {
|
||||
if (block === null || block.isTranslucent() !== isTranslucentRenderPhase) {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import ChunkSection from "./ChunkSection.js";
|
||||
import WorldGenerator from "./generator/WorldGenerator.js";
|
||||
import MathHelper from "../../util/MathHelper.js";
|
||||
import BoundingBox from "../../util/BoundingBox.js";
|
||||
import EnumSkyBlock from "../../util/EnumSkyBlock.js";
|
||||
@@ -9,13 +8,12 @@ import Vector3 from "../../util/Vector3.js";
|
||||
import Vector4 from "../../util/Vector4.js";
|
||||
import MetadataChunkBlock from "../../util/MetadataChunkBlock.js";
|
||||
import * as THREE from "../../../../../../libraries/three.module.js";
|
||||
import Random from "../../util/Random.js";
|
||||
|
||||
export default class World {
|
||||
|
||||
static TOTAL_HEIGHT = ChunkSection.SIZE * 8 - 1; // ChunkSection.SIZE * 16 - 1;
|
||||
|
||||
constructor(minecraft, seed) {
|
||||
constructor(minecraft) {
|
||||
this.minecraft = minecraft;
|
||||
|
||||
this.entities = [];
|
||||
@@ -23,14 +21,12 @@ export default class World {
|
||||
this.group = new THREE.Object3D();
|
||||
this.group.matrixAutoUpdate = false;
|
||||
|
||||
this.chunks = new Map();
|
||||
this.lightUpdateQueue = [];
|
||||
this.chunkProvider = null;
|
||||
|
||||
this.time = 0;
|
||||
this.spawn = new Vector3(0, 0, 0);
|
||||
|
||||
this.setSeed(seed);
|
||||
|
||||
// Update lights async
|
||||
let scope = this;
|
||||
setInterval(function () {
|
||||
@@ -42,14 +38,8 @@ export default class World {
|
||||
}, 0);
|
||||
}
|
||||
|
||||
setSeed(seed) {
|
||||
this.seed = seed;
|
||||
this.generator = new WorldGenerator(this, seed);
|
||||
this.random = new Random(seed);
|
||||
}
|
||||
|
||||
getSeed() {
|
||||
return this.seed;
|
||||
setChunkProvider(chunkProvider) {
|
||||
this.chunkProvider = chunkProvider;
|
||||
}
|
||||
|
||||
onTick() {
|
||||
@@ -67,44 +57,7 @@ export default class World {
|
||||
}
|
||||
|
||||
getChunkAt(x, z) {
|
||||
let index = x + (z << 16);
|
||||
let chunk = this.chunks.get(index);
|
||||
if (typeof chunk === 'undefined') {
|
||||
// Generate new chunk
|
||||
chunk = this.generator.newChunk(this, x, z);
|
||||
|
||||
// Register and mark as loaded
|
||||
chunk.loaded = true;
|
||||
this.chunks.set(index, chunk);
|
||||
|
||||
// Populate the chunk
|
||||
if (!chunk.isTerrainPopulated && this.chunkExists(x + 1, z + 1) && this.chunkExists(x, z + 1) && this.chunkExists(x + 1, z)) {
|
||||
this.populate(x, z);
|
||||
}
|
||||
if (this.chunkExists(x - 1, z) && !this.getChunkAt(x - 1, z).isTerrainPopulated && this.chunkExists(x - 1, z + 1) && this.chunkExists(x, z + 1) && this.chunkExists(x - 1, z)) {
|
||||
this.populate(x - 1, z);
|
||||
}
|
||||
if (this.chunkExists(x, z - 1) && !this.getChunkAt(x, z - 1).isTerrainPopulated && this.chunkExists(x + 1, z - 1) && this.chunkExists(x, z - 1) && this.chunkExists(x + 1, z)) {
|
||||
this.populate(x, z - 1);
|
||||
}
|
||||
if (this.chunkExists(x - 1, z - 1) && !this.getChunkAt(x - 1, z - 1).isTerrainPopulated && this.chunkExists(x - 1, z - 1) && this.chunkExists(x, z - 1) && this.chunkExists(x - 1, z)) {
|
||||
this.populate(x - 1, z - 1);
|
||||
}
|
||||
|
||||
// Register in three.js
|
||||
this.group.add(chunk.group);
|
||||
}
|
||||
return chunk;
|
||||
}
|
||||
|
||||
populate(x, z) {
|
||||
let chunk = this.getChunkAt(x, z);
|
||||
if (!chunk.isTerrainPopulated) {
|
||||
chunk.isTerrainPopulated = true;
|
||||
|
||||
// Populate chunk
|
||||
this.generator.populateChunk(chunk.x, chunk.z);
|
||||
}
|
||||
return this.chunkProvider.getChunkAt(x, z);
|
||||
}
|
||||
|
||||
getChunkAtBlock(x, y, z) {
|
||||
@@ -207,9 +160,7 @@ export default class World {
|
||||
}
|
||||
|
||||
chunkExists(chunkX, chunkZ) {
|
||||
let index = chunkX + (chunkZ << 16);
|
||||
let chunk = this.chunks.get(index);
|
||||
return typeof chunk !== 'undefined';
|
||||
return this.chunkProvider !== null && this.chunkProvider.chunkExists(chunkX, chunkZ);
|
||||
}
|
||||
|
||||
neighborLightPropagationChanged(sourceType, x, y, z, level) {
|
||||
@@ -302,7 +253,12 @@ export default class World {
|
||||
|
||||
isSolidBlockAt(x, y, z) {
|
||||
let typeId = this.getBlockAt(x, y, z);
|
||||
return typeId !== 0 && Block.getById(typeId).isSolid();
|
||||
if (typeId === 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
let block = Block.getById(typeId);
|
||||
return block !== null && block.isSolid();
|
||||
}
|
||||
|
||||
isTranslucentBlockAt(x, y, z) {
|
||||
@@ -604,6 +560,7 @@ export default class World {
|
||||
|
||||
addEntity(entity) {
|
||||
this.entities.push(entity);
|
||||
entity.initRenderer();
|
||||
this.group.add(entity.renderer.group);
|
||||
}
|
||||
|
||||
@@ -631,25 +588,6 @@ export default class World {
|
||||
this.spawn = new Vector3(x, y + 8, z);
|
||||
}
|
||||
|
||||
findSpawn() {
|
||||
if (this.spawn.y <= 0) {
|
||||
this.spawn.y = 64;
|
||||
}
|
||||
|
||||
while (this.getBlockAboveSeaLevel(this.spawn.x, this.spawn.z) === 0) {
|
||||
this.spawn.x += this.random.nextInt(8) - this.random.nextInt(8);
|
||||
this.spawn.z += this.random.nextInt(8) - this.random.nextInt(8);
|
||||
}
|
||||
}
|
||||
|
||||
getBlockAboveSeaLevel(x, z) {
|
||||
let y = this.generator.seaLevel;
|
||||
while (this.getBlockAt(x, y + 1, z) !== 0) {
|
||||
y++;
|
||||
}
|
||||
return this.getBlockAt(x, y, z);
|
||||
}
|
||||
|
||||
loadSpawnChunks() {
|
||||
let viewDistance = this.minecraft.settings.viewDistance;
|
||||
for (let x = -viewDistance; x <= viewDistance; x++) {
|
||||
@@ -660,4 +598,8 @@ export default class World {
|
||||
this.spawn.y = this.getHeightAt(this.spawn.x, this.spawn.z) + 8;
|
||||
}
|
||||
|
||||
getChunkProvider() {
|
||||
return this.chunkProvider;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
import World from "./World.js";
|
||||
import ChunkProviderClient from "./provider/ChunkProviderClient.js";
|
||||
|
||||
export default class WorldClient extends World {
|
||||
|
||||
constructor(minecraft) {
|
||||
super(minecraft);
|
||||
|
||||
// Set chunk provider to remote chunk loader
|
||||
this.setChunkProvider(new ChunkProviderClient(this))
|
||||
}
|
||||
}
|
||||
@@ -49,7 +49,12 @@ export default class Block {
|
||||
|
||||
shouldRenderFace(world, x, y, z, face) {
|
||||
let typeId = world.getBlockAtFace(x, y, z, face);
|
||||
return typeId === 0 || Block.getById(typeId).isTranslucent();
|
||||
if (typeId === 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
let block = Block.getById(typeId);
|
||||
return block === null || block.isTranslucent();
|
||||
}
|
||||
|
||||
getColor(world, x, y, z, face) {
|
||||
@@ -208,7 +213,8 @@ export default class Block {
|
||||
}
|
||||
|
||||
static getById(typeId) {
|
||||
return Block.blocks.get(typeId);
|
||||
let block = Block.blocks.get(typeId);
|
||||
return typeof block === "undefined" ? null : block;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -13,6 +13,7 @@ import BlockBedrock from "./type/BlockBedrock.js";
|
||||
import BlockGlass from "./type/BlockGlass.js";
|
||||
import SoundGlass from "./sound/SoundGlass.js";
|
||||
import BlockGravel from "./type/BlockGravel.js";
|
||||
import BlockCobblestone from "./type/BlockCobblestone.js";
|
||||
|
||||
export class BlockRegistry {
|
||||
|
||||
@@ -30,6 +31,7 @@ export class BlockRegistry {
|
||||
BlockRegistry.STONE = new BlockStone(1, 0);
|
||||
BlockRegistry.GRASS = new BlockGrass(2, 1);
|
||||
BlockRegistry.DIRT = new BlockDirt(3, 2);
|
||||
BlockRegistry.COBBLE_STONE = new BlockCobblestone(4, 14);
|
||||
BlockRegistry.WOOD = new BlockWood(5, 10);
|
||||
BlockRegistry.BEDROCK = new BlockBedrock(7, 11);
|
||||
BlockRegistry.GRAVEL = new BlockGravel(13, 13);
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
import Block from "../Block.js";
|
||||
|
||||
export default class BlockCobblestone extends Block {
|
||||
|
||||
constructor(id, textureSlotId) {
|
||||
super(id, textureSlotId);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -7,6 +7,8 @@ export default class Generator {
|
||||
this.world = world;
|
||||
this.seed = seed;
|
||||
this.random = new Random(seed);
|
||||
|
||||
this.seaLevel = 64;
|
||||
}
|
||||
|
||||
generateInChunk(chunkX, chunkZ, primer) {
|
||||
@@ -35,4 +37,12 @@ export default class Generator {
|
||||
let {seedX, seedZ} = this.generateSeedOffset();
|
||||
this.setSeedOffset(chunkX, chunkZ, seedX, seedZ);
|
||||
}
|
||||
|
||||
getSeed() {
|
||||
return this.seed;
|
||||
}
|
||||
|
||||
getSeaLevel() {
|
||||
return this.seaLevel;
|
||||
}
|
||||
}
|
||||
@@ -13,8 +13,6 @@ export default class WorldGenerator extends Generator {
|
||||
constructor(world, seed) {
|
||||
super(world, seed);
|
||||
|
||||
this.seaLevel = 64;
|
||||
|
||||
this.caveGenerator = new CaveGenerator(world, seed);
|
||||
|
||||
this.terrainGenerator4 = new NoiseGeneratorOctaves(this.random, 16);
|
||||
|
||||
@@ -0,0 +1,61 @@
|
||||
import Chunk from "../Chunk.js";
|
||||
|
||||
export default class ChunkProvider {
|
||||
|
||||
constructor(world) {
|
||||
this.world = world;
|
||||
this.chunks = new Map();
|
||||
}
|
||||
|
||||
chunkExists(x, z) {
|
||||
let index = x + (z << 16);
|
||||
let chunk = this.chunks.get(index);
|
||||
return typeof chunk !== 'undefined';
|
||||
}
|
||||
|
||||
getChunkAt(x, z) {
|
||||
let index = x + (z << 16);
|
||||
let chunk = this.chunks.get(index);
|
||||
if (typeof chunk === 'undefined') {
|
||||
chunk = this.loadChunk(x, z);
|
||||
}
|
||||
return chunk;
|
||||
}
|
||||
|
||||
generateChunk(x, z) {
|
||||
let chunk = new Chunk(this.world, x, z);
|
||||
chunk.generateSkylightMap();
|
||||
chunk.generateBlockLightMap();
|
||||
return chunk;
|
||||
}
|
||||
|
||||
populateChunk(chunk) {
|
||||
|
||||
}
|
||||
|
||||
loadChunk(x, z) {
|
||||
let index = x + (z << 16);
|
||||
let chunk = this.generateChunk(x, z)
|
||||
|
||||
// Register and mark as loaded
|
||||
chunk.loaded = true;
|
||||
this.chunks.set(index, chunk);
|
||||
|
||||
this.populateChunk(chunk);
|
||||
|
||||
// Register in three.js
|
||||
this.world.group.add(chunk.group);
|
||||
|
||||
return chunk;
|
||||
}
|
||||
|
||||
unloadChunk(x, z) {
|
||||
let index = x + (z << 16);
|
||||
this.chunks.delete(index);
|
||||
}
|
||||
|
||||
getChunks() {
|
||||
return this.chunks;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
import ChunkProvider from "./ChunkProvider.js";
|
||||
import Chunk from "../Chunk.js";
|
||||
|
||||
export default class ChunkProviderClient extends ChunkProvider {
|
||||
|
||||
constructor(world) {
|
||||
super(world);
|
||||
|
||||
this.emptyChunk = new Chunk(world, 0, 0);
|
||||
this.emptyChunk.generateSkylightMap();
|
||||
this.emptyChunk.generateBlockLightMap();
|
||||
}
|
||||
|
||||
getChunkAt(x, z) {
|
||||
let index = x + (z << 16);
|
||||
let chunk = this.chunks.get(index);
|
||||
return typeof chunk === 'undefined' ? this.emptyChunk : chunk;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
import ChunkProvider from "./ChunkProvider.js";
|
||||
import WorldGenerator from "../generator/WorldGenerator.js";
|
||||
import Random from "../../../util/Random.js";
|
||||
|
||||
export default class ChunkProviderGenerate extends ChunkProvider {
|
||||
|
||||
constructor(world, seed) {
|
||||
super(world);
|
||||
|
||||
this.generator = new WorldGenerator(world, seed);
|
||||
}
|
||||
|
||||
generateChunk(x, z) {
|
||||
return this.generator.newChunk(this.world, x, z);
|
||||
}
|
||||
|
||||
populateChunk(chunk) {
|
||||
let x = chunk.x;
|
||||
let z = chunk.z;
|
||||
|
||||
// Populate the chunk
|
||||
if (!chunk.isTerrainPopulated && this.chunkExists(x + 1, z + 1) && this.chunkExists(x, z + 1) && this.chunkExists(x + 1, z)) {
|
||||
this._populateChunkAt(x, z);
|
||||
}
|
||||
if (this.chunkExists(x - 1, z) && !this.getChunkAt(x - 1, z).isTerrainPopulated && this.chunkExists(x - 1, z + 1) && this.chunkExists(x, z + 1) && this.chunkExists(x - 1, z)) {
|
||||
this._populateChunkAt(x - 1, z);
|
||||
}
|
||||
if (this.chunkExists(x, z - 1) && !this.getChunkAt(x, z - 1).isTerrainPopulated && this.chunkExists(x + 1, z - 1) && this.chunkExists(x, z - 1) && this.chunkExists(x + 1, z)) {
|
||||
this._populateChunkAt(x, z - 1);
|
||||
}
|
||||
if (this.chunkExists(x - 1, z - 1) && !this.getChunkAt(x - 1, z - 1).isTerrainPopulated && this.chunkExists(x - 1, z - 1) && this.chunkExists(x, z - 1) && this.chunkExists(x - 1, z)) {
|
||||
this._populateChunkAt(x - 1, z - 1);
|
||||
}
|
||||
}
|
||||
|
||||
_populateChunkAt(x, z) {
|
||||
let chunk = this.getChunkAt(x, z);
|
||||
if (!chunk.isTerrainPopulated) {
|
||||
chunk.isTerrainPopulated = true;
|
||||
|
||||
// Populate chunk
|
||||
this.generator.populateChunk(chunk.x, chunk.z);
|
||||
}
|
||||
}
|
||||
|
||||
findSpawn() {
|
||||
let spawn = this.world.spawn;
|
||||
if (spawn.y <= 0) {
|
||||
spawn.y = 64;
|
||||
}
|
||||
|
||||
let random = new Random(this.generator.getSeed());
|
||||
while (this.getBlockAboveSeaLevel(spawn.x, spawn.z) === 0) {
|
||||
spawn.x += random.nextInt(8) - random.nextInt(8);
|
||||
spawn.z += random.nextInt(8) - random.nextInt(8);
|
||||
}
|
||||
}
|
||||
|
||||
getBlockAboveSeaLevel(x, z) {
|
||||
let y = this.generator.getSeaLevel();
|
||||
while (this.world.getBlockAt(x, y + 1, z) !== 0) {
|
||||
y++;
|
||||
}
|
||||
return this.world.getBlockAt(x, y, z);
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user