working inventories
All checks were successful
Node.js Build / build (20.x) (push) Successful in 5m16s
All checks were successful
Node.js Build / build (20.x) (push) Successful in 5m16s
This commit is contained in:
parent
7c59d531ae
commit
2e7535bf89
9 changed files with 149 additions and 41 deletions
|
@ -34,6 +34,7 @@ import TileEntityChest from "./tileentities/TileEntityChest";
|
|||
import WindowCrafting from "./windows/WindowCrafting";
|
||||
import PacketWindowClick from "./packets/WindowClick";
|
||||
import PlayerCombinedInventory from "./inventories/PlayerCombinedInventory";
|
||||
import PacketCloseWindow from "./packets/CloseWindow";
|
||||
|
||||
export default class MPClient {
|
||||
private readonly mcServer:MinecraftServer;
|
||||
|
@ -98,6 +99,7 @@ export default class MPClient {
|
|||
//case Packets.UseBed: break;
|
||||
case Packet.Animation: this.handlePacketAnimation(new PacketAnimation().readData(reader)); break;
|
||||
case Packet.EntityAction: this.handlePacketEntityAction(new PacketEntityAction().readData(reader)); break;
|
||||
case Packet.CloseWindow: this.handleWindowClose(new PacketCloseWindow().readData(reader)); break;
|
||||
case Packet.WindowClick: this.handleWindowClick(new PacketWindowClick().readData(reader)); break;
|
||||
case Packet.DisconnectKick: this.handleDisconnectKick(); break;
|
||||
default: return Console.printWarn(`UNIMPLEMENTED PACKET: ${Packet[packetId]} 0x${packetId < 10 ? `0${packetId.toString(16).toUpperCase()}` : packetId.toString(16).toUpperCase()}`);
|
||||
|
@ -221,6 +223,26 @@ export default class MPClient {
|
|||
}
|
||||
}
|
||||
|
||||
private throwItemStack(itemStack: ItemStack) {
|
||||
const itemEntity = new EntityItem(this.entity.world, new ItemStack(itemStack.itemID, 1, itemStack.damage));
|
||||
itemEntity.pickupDelay = 10;
|
||||
itemEntity.position.set(this.entity.position.x, this.entity.position.y + 1.50, this.entity.position.z);
|
||||
itemEntity.motion.set(
|
||||
-Math.sin((this.entity.rotation.yaw / 180) * Math.PI) * Math.cos((this.entity.rotation.pitch / 180) * Math.PI) * 0.3,
|
||||
-Math.sin((this.entity.rotation.pitch / 180) * Math.PI) * 0.3 + 0.1,
|
||||
Math.cos((this.entity.rotation.yaw / 180) * Math.PI) * Math.cos((this.entity.rotation.pitch / 180) * Math.PI) * 0.3
|
||||
);
|
||||
// Add random motion vector
|
||||
const twoPIRandomised = Math.random() * Math.PI * 2;
|
||||
const rngMult = 0.02 * Math.random();
|
||||
itemEntity.motion.add(
|
||||
Math.cos(twoPIRandomised) * rngMult,
|
||||
(Math.random() - Math.random()) * 0.1,
|
||||
Math.sin(twoPIRandomised) * rngMult
|
||||
);
|
||||
this.entity.world.addEntity(itemEntity);
|
||||
}
|
||||
|
||||
// TODO: Cap how far away a player is able to break blocks
|
||||
private handlePacketPlayerDigging(packet:PacketPlayerDigging) {
|
||||
|
||||
|
@ -228,24 +250,7 @@ export default class MPClient {
|
|||
if (packet.status === 4) {
|
||||
const itemStack = this.getHeldItemStack();
|
||||
if (itemStack !== null && itemStack.size > 0) {
|
||||
itemStack.size--;
|
||||
const itemEntity = new EntityItem(this.entity.world, new ItemStack(itemStack.itemID, 1, itemStack.damage));
|
||||
itemEntity.pickupDelay = 10;
|
||||
itemEntity.position.set(this.entity.position.x, this.entity.position.y + 1.50, this.entity.position.z);
|
||||
itemEntity.motion.set(
|
||||
-Math.sin((this.entity.rotation.yaw / 180) * Math.PI) * Math.cos((this.entity.rotation.pitch / 180) * Math.PI) * 0.3,
|
||||
-Math.sin((this.entity.rotation.pitch / 180) * Math.PI) * 0.3 + 0.1,
|
||||
Math.cos((this.entity.rotation.yaw / 180) * Math.PI) * Math.cos((this.entity.rotation.pitch / 180) * Math.PI) * 0.3
|
||||
);
|
||||
// Add random motion vector
|
||||
const twoPIRandomised = Math.random() * Math.PI * 2;
|
||||
const rngMult = 0.02 * Math.random();
|
||||
itemEntity.motion.add(
|
||||
Math.cos(twoPIRandomised) * rngMult,
|
||||
(Math.random() - Math.random()) * 0.1,
|
||||
Math.sin(twoPIRandomised) * rngMult
|
||||
);
|
||||
this.entity.world.addEntity(itemEntity);
|
||||
this.throwItemStack(itemStack.split(1));
|
||||
|
||||
this.inventory.dropEmptyItemStacks();
|
||||
this.inventory.sendUpdatedStacks([this.holdingIndex]);
|
||||
|
@ -281,14 +286,14 @@ 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(PlayerCombinedInventory.FromExisting(this, tileEntity.inventory, tileEntity.inventory.name));
|
||||
const window = new WindowChest(this, PlayerCombinedInventory.FromExisting(this, tileEntity.inventory, tileEntity.inventory.name));
|
||||
this.windows.set(window.windowId, window);
|
||||
window.openWindow(this);
|
||||
window.openWindow();
|
||||
}
|
||||
} else if (blockClicked.is(Block.craftingTable)) {
|
||||
const window = new WindowCrafting(new PlayerCombinedInventory(this, 10, "Crafting"));
|
||||
const window = new WindowCrafting(this, new PlayerCombinedInventory(this, 10, "Crafting"));
|
||||
this.windows.set(window.windowId, window);
|
||||
window.openWindow(this);
|
||||
window.openWindow();
|
||||
}
|
||||
|
||||
return;
|
||||
|
@ -359,7 +364,28 @@ export default class MPClient {
|
|||
}
|
||||
|
||||
private handleWindowClick(windowClick: PacketWindowClick) {
|
||||
console.log(windowClick);
|
||||
const window = this.windows.get(windowClick.windowId);
|
||||
if (!window) {
|
||||
return this.send(new PacketDisconnectKick("Attempted to perform action on window that does not exist.").writeData());
|
||||
}
|
||||
|
||||
window.clickedWindow(windowClick.slot, windowClick.rightClick);
|
||||
}
|
||||
|
||||
private handleWindowClose(closeWindow: PacketCloseWindow) {
|
||||
if (closeWindow.windowId === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
const window = this.windows.get(closeWindow.windowId);
|
||||
if (window) {
|
||||
window.closeWindow();
|
||||
if (window.cursorItemStack && window.cursorItemStack.size > 0) {
|
||||
this.throwItemStack(window.cursorItemStack);
|
||||
window.cursorItemStack = null;
|
||||
}
|
||||
this.windows.remove(window.windowId);
|
||||
}
|
||||
}
|
||||
|
||||
private handleDisconnectKick() {
|
||||
|
|
|
@ -128,7 +128,7 @@ export default class MinecraftServer {
|
|||
this.worlds = new FunkyArray<number, World>();
|
||||
//this.worlds.set(0, new World(this.saveManager, 0, worldSeed, new NewOverworld(worldSeed)));
|
||||
this.worlds.set(0, new World(this.saveManager, 0, worldSeed, new HillyGenerator(worldSeed)));
|
||||
this.worlds.set(-1, new World(this.saveManager, -1, worldSeed, new NetherGenerator(worldSeed)));
|
||||
//this.worlds.set(-1, new World(this.saveManager, -1, worldSeed, new NetherGenerator(worldSeed)));
|
||||
|
||||
(async () => {
|
||||
const generateStartTime = Date.now();
|
||||
|
|
|
@ -6,14 +6,14 @@ import ItemStack from "./ItemStack";
|
|||
export default class Inventory implements IInventory {
|
||||
private static CHANGE_HANDLER_ROLLING_HANDLE_ID = 0;
|
||||
|
||||
public changeHandlers:FunkyArray<number, (itemStack: ItemStack) => void>;
|
||||
public changeHandlers:FunkyArray<number, (itemStack: number) => void>;
|
||||
public itemStacks:Array<ItemStack | null>;
|
||||
|
||||
public readonly size:number;
|
||||
public readonly name:string;
|
||||
|
||||
public constructor(size:number, name:string) {
|
||||
this.changeHandlers = new FunkyArray<number, (itemStack: ItemStack) => void>();
|
||||
this.changeHandlers = new FunkyArray<number, (itemStack: number) => void>();
|
||||
this.itemStacks = new Array<ItemStack | null>();
|
||||
for (let i = 0; i < size; i++) {
|
||||
this.itemStacks.push(null);
|
||||
|
@ -23,7 +23,7 @@ export default class Inventory implements IInventory {
|
|||
this.name = name;
|
||||
}
|
||||
|
||||
registerChangeHandler(changeHandler: (itemStack: ItemStack) => void) {
|
||||
registerChangeHandler(changeHandler: (slotId: number) => void) {
|
||||
const changeHandlerHandle = Inventory.CHANGE_HANDLER_ROLLING_HANDLE_ID++;
|
||||
this.changeHandlers.set(changeHandlerHandle, changeHandler);
|
||||
return changeHandlerHandle;
|
||||
|
@ -54,6 +54,10 @@ export default class Inventory implements IInventory {
|
|||
}
|
||||
}
|
||||
|
||||
sendSlotUpdate(slotId: number) {
|
||||
this.changeHandlers.forEach(handler => handler(slotId));
|
||||
}
|
||||
|
||||
addItemStack(itemStack:ItemStack) {
|
||||
for (let slotId = 0; slotId < this.itemStacks.length; slotId++) {
|
||||
if (itemStack.size === 0) {
|
||||
|
@ -61,6 +65,7 @@ export default class Inventory implements IInventory {
|
|||
}
|
||||
|
||||
this.itemStacks[slotId]?.insert(itemStack);
|
||||
this.changeHandlers.forEach(handler => handler(slotId));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -81,6 +86,7 @@ export default class Inventory implements IInventory {
|
|||
const itemStack = this.itemStacks[i];
|
||||
if (itemStack?.size === 0) {
|
||||
this.itemStacks[i] = null;
|
||||
this.changeHandlers.forEach(handler => handler(i));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -91,6 +97,7 @@ export default class Inventory implements IInventory {
|
|||
}
|
||||
|
||||
this.itemStacks[slotId] = itemStack;
|
||||
this.changeHandlers.forEach(handler => handler(slotId));
|
||||
|
||||
return this;
|
||||
}
|
||||
|
|
|
@ -4,7 +4,7 @@ import Inventory from "./Inventory";
|
|||
import ItemStack from "./ItemStack";
|
||||
|
||||
export default class PlayerCombinedInventory extends Inventory {
|
||||
private static PLAYER_INVENTORY_OFFSET = 10;
|
||||
private static PLAYER_INVENTORY_OFFSET = 9;
|
||||
|
||||
private mpClient: MPClient;
|
||||
|
||||
|
@ -16,7 +16,7 @@ export default class PlayerCombinedInventory extends Inventory {
|
|||
|
||||
private getSlotId(slotId: number) {
|
||||
if (slotId > this.size - 1) {
|
||||
return slotId + PlayerCombinedInventory.PLAYER_INVENTORY_OFFSET;
|
||||
return (slotId - this.size) + PlayerCombinedInventory.PLAYER_INVENTORY_OFFSET;
|
||||
} else {
|
||||
return slotId;
|
||||
}
|
||||
|
@ -25,6 +25,7 @@ export default class PlayerCombinedInventory extends Inventory {
|
|||
static FromExisting(mpClient: MPClient, inventory: Inventory, name: string) {
|
||||
const linkedInventory = new PlayerCombinedInventory(mpClient, inventory.size, name);
|
||||
linkedInventory.itemStacks = inventory.itemStacks;
|
||||
linkedInventory.changeHandlers = inventory.changeHandlers;
|
||||
return linkedInventory;
|
||||
}
|
||||
|
||||
|
@ -55,7 +56,11 @@ export default class PlayerCombinedInventory extends Inventory {
|
|||
// }
|
||||
|
||||
getSlotItemStack(slotId:number) {
|
||||
return this.itemStacks[slotId] ?? this.mpClient.entity.inventory.itemStacks[this.getSlotId(slotId)];
|
||||
if (slotId > this.size - 1) {
|
||||
return this.mpClient.entity.inventory.itemStacks[this.getSlotId(slotId)];
|
||||
}
|
||||
|
||||
return this.itemStacks[slotId];
|
||||
}
|
||||
|
||||
dropEmptyItemStacks() {
|
||||
|
|
|
@ -24,6 +24,7 @@ export default class PlayerInventory extends Inventory {
|
|||
} else {
|
||||
buffer = new PacketSetSlot(0, slotId, slotItem.itemID, slotItem.size, slotItem.damage).writeData();
|
||||
}
|
||||
this.changeHandlers.forEach(handler => handler(slotId));
|
||||
|
||||
updateBuffer = Buffer.concat([updateBuffer, buffer], updateBuffer.length + buffer.length);
|
||||
}
|
||||
|
|
22
server/packets/CloseWindow.ts
Normal file
22
server/packets/CloseWindow.ts
Normal file
|
@ -0,0 +1,22 @@
|
|||
import { createWriter, IReader, Endian } from "bufferstuff";
|
||||
import IPacket from "./IPacket";
|
||||
import Packet from "../enums/Packet";
|
||||
|
||||
export default class PacketCloseWindow implements IPacket {
|
||||
public packetId = Packet.CloseWindow;
|
||||
public windowId:number;
|
||||
|
||||
public constructor(windowId?:number) {
|
||||
this.windowId = windowId ?? Number.MIN_VALUE;
|
||||
}
|
||||
|
||||
public readData(reader:IReader) {
|
||||
this.windowId = reader.readByte();
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public writeData() {
|
||||
return createWriter(Endian.BE, 2).writeUByte(this.packetId).writeByte(this.windowId).toBuffer();
|
||||
}
|
||||
}
|
|
@ -4,6 +4,7 @@ import ItemStack from "../inventories/ItemStack";
|
|||
import PlayerCombinedInventory from "../inventories/PlayerCombinedInventory";
|
||||
import MPClient from "../MPClient";
|
||||
import PacketOpenWindow from "../packets/OpenWindow";
|
||||
import PacketSetSlot from "../packets/SetSlot";
|
||||
import PacketWindowItems from "../packets/WindowItems";
|
||||
|
||||
export default abstract class Window {
|
||||
|
@ -14,31 +15,76 @@ export default abstract class Window {
|
|||
public windowId = Window.WINDOW_GLOBAL_COUNTER++;
|
||||
public inventoryType: InventoryType;
|
||||
public inventory: PlayerCombinedInventory;
|
||||
private readonly mpClient: MPClient;
|
||||
|
||||
private readonly inventoryUpdateHandle: number;
|
||||
|
||||
public cursorItemStack: ItemStack | null;
|
||||
|
||||
public constructor(inventoryType: InventoryType, inventory: PlayerCombinedInventory, inventorySize: number) {
|
||||
public constructor(inventoryType: InventoryType, inventory: PlayerCombinedInventory, mpClient: MPClient, inventorySize: number) {
|
||||
this.inventorySize = inventorySize;
|
||||
|
||||
this.inventoryType = inventoryType;
|
||||
this.inventory = inventory;
|
||||
this.mpClient = mpClient;
|
||||
|
||||
this.inventoryUpdateHandle = this.inventory.registerChangeHandler((slotId) => {
|
||||
const slotItem = this.inventory.getSlotItemStack(slotId);
|
||||
if (slotItem == null) {
|
||||
this.mpClient.send(new PacketSetSlot(this.windowId, slotId, -1).writeData());
|
||||
} else {
|
||||
this.mpClient.send(new PacketSetSlot(this.windowId, slotId, slotItem.itemID, slotItem.size, slotItem.damage).writeData());
|
||||
}
|
||||
});
|
||||
|
||||
this.cursorItemStack = null;
|
||||
}
|
||||
|
||||
openWindow(mpClient: MPClient) {
|
||||
openWindow() {
|
||||
const windowPacket = new PacketOpenWindow(this.windowId, this.inventoryType, this.inventory.getInventoryName(), this.inventory.getInventorySize()).writeData();
|
||||
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);
|
||||
this.mpClient.send(Buffer.concat([ windowPacket, windowItems ], windowPacket.length + windowItems.length));
|
||||
}
|
||||
|
||||
clickedWindow(slotId: number, rightClick: boolean) {
|
||||
const slotItemStack = this.inventory.getSlotItemStack(slotId);
|
||||
if (this.cursorItemStack) {
|
||||
|
||||
if (rightClick) {
|
||||
if (slotItemStack) {
|
||||
slotItemStack.insert(this.cursorItemStack.split(1));
|
||||
this.inventory.sendSlotUpdate(slotId);
|
||||
} else {
|
||||
this.inventory.setSlotItemStack(slotId, this.cursorItemStack.split(1));
|
||||
}
|
||||
if (this.cursorItemStack.size === 0) {
|
||||
this.cursorItemStack = null;
|
||||
}
|
||||
} else {
|
||||
if (slotItemStack) {
|
||||
slotItemStack.insert(this.cursorItemStack);
|
||||
this.inventory.sendSlotUpdate(slotId);
|
||||
if (this.cursorItemStack.size === 0) {
|
||||
this.cursorItemStack = null;
|
||||
}
|
||||
} else {
|
||||
this.inventory.setSlotItemStack(slotId, this.cursorItemStack);
|
||||
this.cursorItemStack = null;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
this.cursorItemStack = this.inventory.getSlotItemStack(slotId);
|
||||
if (slotItemStack) {
|
||||
if (rightClick) {
|
||||
this.cursorItemStack = slotItemStack.split(Math.ceil(slotItemStack.size / 2));
|
||||
this.inventory.sendSlotUpdate(slotId);
|
||||
} else {
|
||||
this.cursorItemStack = slotItemStack;
|
||||
this.inventory.setSlotItemStack(slotId, null);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
closeWindow() {
|
||||
this.inventory.unregisterChangeHandler(this.inventoryUpdateHandle);
|
||||
}
|
||||
}
|
|
@ -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 WindowChest extends Window {
|
||||
public constructor(inventory: PlayerCombinedInventory) {
|
||||
super(InventoryType.Chest, inventory, 62);
|
||||
public constructor(mpClient: MPClient, inventory: PlayerCombinedInventory) {
|
||||
super(InventoryType.Chest, inventory, mpClient, 62);
|
||||
}
|
||||
}
|
|
@ -5,7 +5,7 @@ import MPClient from "../MPClient";
|
|||
import Window from "./Window";
|
||||
|
||||
export default class WindowCrafting extends Window {
|
||||
public constructor(inventory: PlayerCombinedInventory) {
|
||||
super(InventoryType.CraftingTable, inventory, 45);
|
||||
public constructor(mpClient: MPClient, inventory: PlayerCombinedInventory) {
|
||||
super(InventoryType.CraftingTable, inventory, mpClient, 45);
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue