diff --git a/package-lock.json b/package-lock.json index 146c06a..8fe8c39 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,7 +9,7 @@ "version": "1.0.0", "license": "MIT", "dependencies": { - "bufferstuff": "^1.5.1", + "bufferstuff": "^1.7.1", "dyetty": "^1.0.1", "funky-array": "^1.0.0", "hsconsole": "^1.0.2" @@ -556,9 +556,9 @@ "peer": true }, "node_modules/bufferstuff": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/bufferstuff/-/bufferstuff-1.5.1.tgz", - "integrity": "sha512-IQF03UD+569MX80y70eOrFBhAhavEIOL12WJlVCKDSjOhCVueEY/MjwJPhW4Z1mbd9HcqEeZChldRwbqJP8k1w==", + "version": "1.7.1", + "resolved": "https://registry.npmjs.org/bufferstuff/-/bufferstuff-1.7.1.tgz", + "integrity": "sha512-CAnj456eQCXkuUfuK1up3hICxnidz0+h8CtRghrVNo1wB0Z/zySwDWcP7vzrAjoF4xmBWMpa7e/YK8Vl2p8Jjw==", "license": "MIT" }, "node_modules/caniuse-lite": { diff --git a/package.json b/package.json index 41c4f85..b82f2d5 100644 --- a/package.json +++ b/package.json @@ -21,7 +21,7 @@ }, "homepage": "https://github.com/tgpholly/mc-beta-server#readme", "dependencies": { - "bufferstuff": "^1.5.1", + "bufferstuff": "^1.7.1", "dyetty": "^1.0.1", "funky-array": "^1.0.0", "hsconsole": "^1.0.2" diff --git a/server/MPClient.ts b/server/MPClient.ts index 9a07696..9e4a840 100644 --- a/server/MPClient.ts +++ b/server/MPClient.ts @@ -27,6 +27,10 @@ import PlayerInventory from "./inventories/PlayerInventory"; import SoundEffects from "./enums/SoundEffects"; import Vec3 from "./Vec3"; import TextColorParser from "./TextColorParser"; +import FunkyArray from "funky-array"; +import Window from "./windows/Window"; +import WindowChest from "./windows/WindowChest"; +import TileEntityChest from "./tileentities/TileEntityChest"; export default class MPClient { private readonly mcServer:MinecraftServer; @@ -38,6 +42,8 @@ export default class MPClient { private holdingIndex:number = 36; // First hotbar slot. private diggingAt:Vec3; + private windows: FunkyArray; + public constructor(mcServer:MinecraftServer, socket:Socket, entity:Player) { this.mcServer = mcServer; this.socket = socket; @@ -46,6 +52,7 @@ export default class MPClient { this.dimension = 0; this.diggingAt = new Vec3(); + this.windows = new FunkyArray(); } private mapCoordsFromFace(pos:Vec3, face:number) { @@ -265,6 +272,20 @@ export default class MPClient { this.diggingAt.set(packet.x, packet.y, packet.z); this.mapCoordsFromFace(this.diggingAt, packet.face); + const blockClicked = Block.blocks[this.entity.world.getBlockId(packet.x, packet.y, packet.z)]; + if (!this.entity.crouching && blockClicked && blockClicked.behaviour.interactable()) { + if (blockClicked.is(Block.chest)) { + const tileEntity = this.entity.world.getChunk(packet.x >> 4, packet.z >> 4).getTileEntity(packet.x, packet.y, packet.z); + if (tileEntity && tileEntity instanceof TileEntityChest) { + const window = new WindowChest(tileEntity.inventory); + this.windows.set(window.windowId, window); + window.openWindow(this); + } + } + + return; + } + if (this.entity.entityAABB.intersects(AABB.getAABB(this.diggingAt.x, this.diggingAt.y, this.diggingAt.z, this.diggingAt.x + 1, this.diggingAt.y + 1, this.diggingAt.z + 1))) { return; } diff --git a/server/blocks/Block.ts b/server/blocks/Block.ts index ec1049e..2cc5ef5 100644 --- a/server/blocks/Block.ts +++ b/server/blocks/Block.ts @@ -159,6 +159,11 @@ export default class Block { return this.behaviour.getBoundingBox(x, y, z); } + // Comparison + public is(otherBlock: Block) { + return this.blockId === otherBlock.blockId; + } + // Define statics here static readonly stone = new Block(1).setHardness(1.5).setBehaviour(Behaviour.stone).setBlockName("Stone"); static readonly grass = new Block(2).setHardness(0.6).setBehaviour(Behaviour.grass).setBlockName("Grass"); diff --git a/server/blocks/BlockBehaviour.ts b/server/blocks/BlockBehaviour.ts index 05aaaae..5bcf04e 100644 --- a/server/blocks/BlockBehaviour.ts +++ b/server/blocks/BlockBehaviour.ts @@ -9,6 +9,7 @@ export default class BlockBehaviour implements IBlockBehaviour { public placed(world:World, x:number, y:number, z:number) {} public destroyed(world:World, x:number, y:number, z:number) {} + public interactable() { return false; } public neighborBlockChange(world:World, x:number, y:number, z:number, blockId:number) {} public droppedItem(blockId:number) { return blockId; } public droppedCount(blockId:number) { return 1; } diff --git a/server/blocks/BlockBehaviourChest.ts b/server/blocks/BlockBehaviourChest.ts index aadd0e8..0009cc1 100644 --- a/server/blocks/BlockBehaviourChest.ts +++ b/server/blocks/BlockBehaviourChest.ts @@ -13,4 +13,8 @@ export default class BlockBehaviourChest extends BlockBehaviour { const chunk = world.getChunk(x >> 4, z >> 4); chunk.removeTileEntity(x, y, z); } + + public interactable() { + return true; + } } \ No newline at end of file diff --git a/server/blocks/IBlockBehaviour.ts b/server/blocks/IBlockBehaviour.ts index 3140be4..bd8fed4 100644 --- a/server/blocks/IBlockBehaviour.ts +++ b/server/blocks/IBlockBehaviour.ts @@ -8,6 +8,7 @@ export default interface IBlockBehaviour { placed(world:World, x:number, y:number, z:number): void, destroyed(world:World, x:number, y:number, z:number): void, + interactable(): boolean, neighborBlockChange(world:World, x:number, y:number, z:number, blockId:number): void, droppedItem: (blockId:number) => number, droppedCount: (blockId:number) => number, diff --git a/server/enums/InventoryType.ts b/server/enums/InventoryType.ts new file mode 100644 index 0000000..b6d3e93 --- /dev/null +++ b/server/enums/InventoryType.ts @@ -0,0 +1,8 @@ +enum InventoryType { + Chest = 0, + Workbench = 1, + Furnace = 2, + Dispenser = 3 +} + +export default InventoryType; \ No newline at end of file diff --git a/server/enums/Packet.ts b/server/enums/Packet.ts index 75d0373..bd18673 100644 --- a/server/enums/Packet.ts +++ b/server/enums/Packet.ts @@ -43,6 +43,7 @@ enum Packet { EntityLookRelativeMove = 0x21, EntityTeleport = 0x22, + OpenWindow = 0x64, CloseWindow = 0x65, WindowClick = 0x66, SetSlot = 0x67, diff --git a/server/packets/OpenWindow.ts b/server/packets/OpenWindow.ts new file mode 100644 index 0000000..4edbcd2 --- /dev/null +++ b/server/packets/OpenWindow.ts @@ -0,0 +1,39 @@ +import { createWriter, Endian, IReader } from "bufferstuff"; +import InventoryType from "../enums/InventoryType"; +import Packet from "../enums/Packet"; +import IPacket from "./IPacket"; + +export default class PacketOpenWindow implements IPacket { + public packetId = Packet.OpenWindow; + public windowId:number; + public inventoryType:InventoryType; + public windowTitle: string; + public numberOfSlots: number; + + public constructor(windowId?: number, inventoryType?: InventoryType, windowTitle?: string, numberOfSlots?: number) { + if (typeof(windowId) === "number" && typeof(inventoryType) === "number" && typeof(windowTitle) === "string" && typeof(numberOfSlots) === "number") { + this.windowId = windowId; + this.inventoryType = inventoryType; + this.windowTitle = windowTitle; + this.numberOfSlots = numberOfSlots; + } else { + this.windowId = Number.MIN_VALUE; + this.inventoryType = Number.MIN_VALUE; + this.windowTitle = ""; + this.numberOfSlots = Number.MIN_VALUE; + } + } + + public readData(reader: IReader) { + this.windowId = reader.readByte(); + this.inventoryType = reader.readByte(); + this.windowTitle = reader.readString(); + this.numberOfSlots = reader.readByte(); + + return this; + } + + public writeData() { + return createWriter(Endian.BE).writeUByte(this.packetId).writeByte(this.windowId).writeByte(this.inventoryType).writeJavaUTF(this.windowTitle).writeByte(this.numberOfSlots).toBuffer(); + } +} \ No newline at end of file diff --git a/server/windows/Window.ts b/server/windows/Window.ts new file mode 100644 index 0000000..30fa5f5 --- /dev/null +++ b/server/windows/Window.ts @@ -0,0 +1,24 @@ +import InventoryType from "../enums/InventoryType"; +import Inventory from "../inventories/Inventory"; +import MPClient from "../MPClient"; +import PacketOpenWindow from "../packets/OpenWindow"; + +export default abstract class Window { + public static WINDOW_GLOBAL_COUNTER = 1; + + public windowId = Window.WINDOW_GLOBAL_COUNTER++; + public inventoryType: InventoryType; + public inventory: Inventory; + + public constructor(inventoryType: InventoryType, inventory: Inventory) { + this.inventoryType = inventoryType; + this.inventory = inventory; + } + + openWindow(mpClient: MPClient) { + const windowPacket = new PacketOpenWindow(this.windowId, this.inventoryType, this.inventory.getInventoryName(), this.inventory.getInventorySize()).writeData(); + //const inventoryDataPayload = this.inventory.constructInventoryPayload(); + //mpClient.send(Buffer.concat([ windowPacket, inventoryDataPayload ], windowPacket.length + inventoryDataPayload.length)); + mpClient.send(windowPacket); + } +} \ No newline at end of file diff --git a/server/windows/WindowChest.ts b/server/windows/WindowChest.ts new file mode 100644 index 0000000..440cfd5 --- /dev/null +++ b/server/windows/WindowChest.ts @@ -0,0 +1,9 @@ +import InventoryType from "../enums/InventoryType"; +import Inventory from "../inventories/Inventory"; +import Window from "./Window"; + +export default class WindowChest extends Window { + public constructor(inventory: Inventory) { + super(InventoryType.Chest, inventory); + } +} \ No newline at end of file