diff --git a/server/MPClient.ts b/server/MPClient.ts index 9226df5..5e0f57b 100644 --- a/server/MPClient.ts +++ b/server/MPClient.ts @@ -33,6 +33,7 @@ import WindowChest from "./windows/WindowChest"; import TileEntityChest from "./tileentities/TileEntityChest"; import WindowCrafting from "./windows/WindowCrafting"; import PacketWindowClick from "./packets/WindowClick"; +import PlayerCombinedInventory from "./inventories/PlayerCombinedInventory"; export default class MPClient { private readonly mcServer:MinecraftServer; @@ -280,12 +281,12 @@ export default class MPClient { 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); + const window = new WindowChest(PlayerCombinedInventory.FromExisting(this, tileEntity.inventory, tileEntity.inventory.name)); this.windows.set(window.windowId, window); window.openWindow(this); } } else if (blockClicked.is(Block.craftingTable)) { - const window = new WindowCrafting(this); + const window = new WindowCrafting(new PlayerCombinedInventory(this, 10, "Crafting")); this.windows.set(window.windowId, window); window.openWindow(this); } diff --git a/server/MinecraftServer.ts b/server/MinecraftServer.ts index 85ec223..4904fb0 100644 --- a/server/MinecraftServer.ts +++ b/server/MinecraftServer.ts @@ -248,7 +248,7 @@ export default class MinecraftServer { socket.write(new PacketPlayerPositionLook(clientEntity.position.x, clientEntity.position.y, clientEntity.position.y + 0.62, clientEntity.position.z, 0, 0, false).writeData()); const playerInventory = clientEntity.inventory; - socket.write(new PacketWindowItems(0, playerInventory.getInventorySize(), playerInventory.constructInventoryPayload()).writeData()); + socket.write(new PacketWindowItems(0, playerInventory.getInventorySize(), playerInventory.constructInventoryPayload(0)).writeData()); } else { socket.write(new PacketDisconnectKick("Failed to find world to put player in.").writeData()); } diff --git a/server/inventories/Inventory.ts b/server/inventories/Inventory.ts index 2829789..5140e3d 100644 --- a/server/inventories/Inventory.ts +++ b/server/inventories/Inventory.ts @@ -9,8 +9,8 @@ export default class Inventory implements IInventory { public changeHandlers:FunkyArray void>; public itemStacks:Array; - private size:number; - private name:string; + public readonly size:number; + public readonly name:string; public constructor(size:number, name:string) { this.changeHandlers = new FunkyArray void>(); @@ -55,18 +55,13 @@ export default class Inventory implements IInventory { } addItemStack(itemStack:ItemStack) { - throw new Error("Adding items to non player inventories is unimplemented."); - // Check bottom inventory row (hotbar) first. - /*let workingItemStack:ItemStack | null; - for (let slotId = 9; slotId <= 35; slotId++) { + for (let slotId = 0; slotId < this.itemStacks.length; slotId++) { if (itemStack.size === 0) { break; } - if ((workingItemStack = this.itemStacks[slotId]) != null) { - workingItemStack.insert(itemStack); - } - }*/ + this.itemStacks[slotId]?.insert(itemStack); + } } getInventoryName() { @@ -100,9 +95,10 @@ export default class Inventory implements IInventory { return this; } - private calculateInventoryPayloadSize() { + public calculateInventoryPayloadSize(skip: number) { let bufferSize = 0; - for (const stack of this.itemStacks) { + for (let i = skip; i < this.itemStacks.length; i++) { + const stack = this.itemStacks[i]; if (stack) { bufferSize += 5; // short + byte + short } else { @@ -112,9 +108,10 @@ export default class Inventory implements IInventory { return bufferSize; } - constructInventoryPayload() { - const writer = createWriter(Endian.BE, this.calculateInventoryPayloadSize()); - for (const stack of this.itemStacks) { + constructInventoryPayload(skip: number) { + const writer = createWriter(Endian.BE, this.calculateInventoryPayloadSize(skip)); + for (let i = skip; i < this.itemStacks.length; i++) { + const stack = this.itemStacks[i]; writer.writeShort(stack == null ? -1 : stack.itemID); if (stack != null) { writer.writeByte(stack.size); diff --git a/server/inventories/PlayerCombinedInventory.ts b/server/inventories/PlayerCombinedInventory.ts index dfc3655..bc65b04 100644 --- a/server/inventories/PlayerCombinedInventory.ts +++ b/server/inventories/PlayerCombinedInventory.ts @@ -1,16 +1,93 @@ +import { createWriter, Endian } from "bufferstuff"; +import MPClient from "../MPClient"; import Inventory from "./Inventory"; import ItemStack from "./ItemStack"; export default class PlayerCombinedInventory extends Inventory { - private combinedInventoryChangeHandlerHandle: number; + private static PLAYER_INVENTORY_OFFSET = 10; - public constructor(size: number, name: string) { + private mpClient: MPClient; + + public constructor(mpClient:MPClient, size: number, name: string) { super(size, name); - this.combinedInventoryChangeHandlerHandle = this.registerChangeHandler(this.onInventoryChange); + this.mpClient = mpClient; } - private onInventoryChange(itemStack: ItemStack) { - + private getSlotId(slotId: number) { + if (slotId > this.size - 1) { + return slotId + PlayerCombinedInventory.PLAYER_INVENTORY_OFFSET; + } else { + return slotId; + } + } + + static FromExisting(mpClient: MPClient, inventory: Inventory, name: string) { + const linkedInventory = new PlayerCombinedInventory(mpClient, inventory.size, name); + linkedInventory.itemStacks = inventory.itemStacks; + return linkedInventory; + } + + addItemStack(itemStack:ItemStack) { + for (let slotId = 0; slotId < this.itemStacks.length; slotId++) { + if (itemStack.size === 0) { + break; + } + + this.itemStacks[slotId]?.insert(itemStack); + } + + if (itemStack.size === 0) { + return; + } + + for (let slotId = 0; slotId < this.mpClient.entity.inventory.itemStacks.length; slotId++) { + if (itemStack.size === 0) { + break; + } + + this.mpClient.entity.inventory.itemStacks[slotId]?.insert(itemStack); + } + } + + // getInventorySize() { + // return this.itemStacks.length + this.mpClient.entity.inventory.itemStacks.length; + // } + + getSlotItemStack(slotId:number) { + return this.itemStacks[slotId] ?? this.mpClient.entity.inventory.itemStacks[this.getSlotId(slotId)]; + } + + dropEmptyItemStacks() { + super.dropEmptyItemStacks(); + this.mpClient.entity.inventory.dropEmptyItemStacks(); + } + + setSlotItemStack(slotId:number, itemStack: ItemStack | null) { + if (slotId > this.size - 1) { + this.mpClient.entity.inventory.setSlotItemStack(this.getSlotId(slotId), itemStack); + } else { + super.setSlotItemStack(slotId, itemStack); + } + + return this; + } + + constructInventoryPayload() { + const thisInventoryPayload = super.constructInventoryPayload(0); + const playerInventoryPayload = this.mpClient.entity.inventory.constructInventoryPayload(PlayerCombinedInventory.PLAYER_INVENTORY_OFFSET); + return Buffer.concat([thisInventoryPayload, playerInventoryPayload], thisInventoryPayload.length + playerInventoryPayload.length); + } + + constructInventorySinglePayload(slotId:number) { + const stack = this.itemStacks[slotId] ?? this.mpClient.entity.inventory.itemStacks[this.getSlotId(slotId)]; + const writer = createWriter(Endian.BE, stack == null ? 2 : 5); + writer.writeShort(stack == null ? -1 : stack.itemID); + if (stack != null) { + writer.writeByte(stack.size); + writer.writeShort(stack.damage); + } + + return writer.toBuffer(); } } \ No newline at end of file diff --git a/server/inventories/PlayerInventory.ts b/server/inventories/PlayerInventory.ts index acf7fb4..de3d2d5 100644 --- a/server/inventories/PlayerInventory.ts +++ b/server/inventories/PlayerInventory.ts @@ -3,6 +3,7 @@ import ItemStack from "./ItemStack"; import PacketSetSlot from "../packets/SetSlot"; import PacketWindowItems from "../packets/WindowItems"; import Player from "../entities/Player"; +import { createWriter, Endian } from "bufferstuff"; export default class PlayerInventory extends Inventory { private player:Player; diff --git a/server/windows/Window.ts b/server/windows/Window.ts index 29d231b..58cbfc2 100644 --- a/server/windows/Window.ts +++ b/server/windows/Window.ts @@ -1,27 +1,44 @@ import InventoryType from "../enums/InventoryType"; import Inventory from "../inventories/Inventory"; import ItemStack from "../inventories/ItemStack"; +import PlayerCombinedInventory from "../inventories/PlayerCombinedInventory"; import MPClient from "../MPClient"; import PacketOpenWindow from "../packets/OpenWindow"; +import PacketWindowItems from "../packets/WindowItems"; export default abstract class Window { public static WINDOW_GLOBAL_COUNTER = 1; + public readonly inventorySize: number; + public windowId = Window.WINDOW_GLOBAL_COUNTER++; public inventoryType: InventoryType; - public inventory: Inventory; + public inventory: PlayerCombinedInventory; - public cursorItemStack?: ItemStack; + public cursorItemStack: ItemStack | null; + + public constructor(inventoryType: InventoryType, inventory: PlayerCombinedInventory, inventorySize: number) { + this.inventorySize = inventorySize; - public constructor(inventoryType: InventoryType, inventory: Inventory) { this.inventoryType = inventoryType; this.inventory = inventory; + + this.cursorItemStack = null; } 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); + const windowItems = new PacketWindowItems(this.windowId, this.inventorySize, this.inventory.constructInventoryPayload()).writeData(); + mpClient.send(Buffer.concat([ windowPacket, windowItems ], windowPacket.length + windowItems.length)); + //mpClient.send(windowPacket); + //mpClient.send(inventoryDataPayload); + } + + clickedWindow(slotId: number, rightClick: boolean) { + if (this.cursorItemStack) { + + } else { + this.cursorItemStack = this.inventory.getSlotItemStack(slotId); + } } } \ No newline at end of file diff --git a/server/windows/WindowChest.ts b/server/windows/WindowChest.ts index 440cfd5..b123a8d 100644 --- a/server/windows/WindowChest.ts +++ b/server/windows/WindowChest.ts @@ -1,9 +1,10 @@ import InventoryType from "../enums/InventoryType"; import Inventory from "../inventories/Inventory"; +import PlayerCombinedInventory from "../inventories/PlayerCombinedInventory"; import Window from "./Window"; export default class WindowChest extends Window { - public constructor(inventory: Inventory) { - super(InventoryType.Chest, inventory); + public constructor(inventory: PlayerCombinedInventory) { + super(InventoryType.Chest, inventory, 62); } } \ No newline at end of file diff --git a/server/windows/WindowCrafting.ts b/server/windows/WindowCrafting.ts index ac3de72..7df006c 100644 --- a/server/windows/WindowCrafting.ts +++ b/server/windows/WindowCrafting.ts @@ -1,10 +1,11 @@ import InventoryType from "../enums/InventoryType"; import Inventory from "../inventories/Inventory"; +import PlayerCombinedInventory from "../inventories/PlayerCombinedInventory"; import MPClient from "../MPClient"; import Window from "./Window"; export default class WindowCrafting extends Window { - public constructor(mpClient: MPClient) { - super(InventoryType.CraftingTable, new Inventory(45, "Crafting")); + public constructor(inventory: PlayerCombinedInventory) { + super(InventoryType.CraftingTable, inventory, 45); } } \ No newline at end of file