From e2e7d67013299c367fe7c421ecf1ac5ed451f0bc Mon Sep 17 00:00:00 2001 From: TheMode Date: Tue, 13 Aug 2019 17:52:09 +0200 Subject: [PATCH] Enhanced inventory creation & listener --- .../fr/themode/minestom/entity/Player.java | 40 +++ .../themode/minestom/instance/BlockBatch.java | 4 +- .../fr/themode/minestom/instance/Chunk.java | 2 +- .../themode/minestom/inventory/Inventory.java | 235 ++++++++++++++++++ .../inventory/InventoryClickHandler.java | 21 ++ .../minestom/inventory/InventoryType.java | 45 ++++ .../minestom/inventory/PlayerInventory.java | 76 ++++-- .../fr/themode/minestom/item/ItemStack.java | 28 ++- .../handler/ClientPlayPacketsHandler.java | 3 + .../packet/client/login/LoginStartPacket.java | 13 +- .../client/play/ClientClickWindowPacket.java | 90 +++++++ .../packet/client/play/ClientCloseWindow.java | 26 ++ .../play/ClientConfirmTransactionPacket.java | 22 ++ .../ClientPlayerBlockPlacementPacket.java | 6 +- .../packet/server/play/BlockChangePacket.java | 23 ++ .../packet/server/play/ChatMessagePacket.java | 2 +- .../packet/server/play/CloseWindowPacket.java | 20 ++ .../server/play/ConfirmTransactionPacket.java | 24 ++ .../server/play/MultiBlockChangePacket.java | 40 +++ .../packet/server/play/OpenWindowPacket.java | 24 ++ .../net/packet/server/play/SetSlotPacket.java | 25 ++ .../packet/server/play/WindowItemsPacket.java | 13 +- .../java/fr/themode/minestom/utils/Utils.java | 12 + 23 files changed, 752 insertions(+), 42 deletions(-) create mode 100644 src/main/java/fr/themode/minestom/inventory/Inventory.java create mode 100644 src/main/java/fr/themode/minestom/inventory/InventoryClickHandler.java create mode 100644 src/main/java/fr/themode/minestom/inventory/InventoryType.java create mode 100644 src/main/java/fr/themode/minestom/net/packet/client/play/ClientClickWindowPacket.java create mode 100644 src/main/java/fr/themode/minestom/net/packet/client/play/ClientCloseWindow.java create mode 100644 src/main/java/fr/themode/minestom/net/packet/client/play/ClientConfirmTransactionPacket.java create mode 100644 src/main/java/fr/themode/minestom/net/packet/server/play/BlockChangePacket.java create mode 100644 src/main/java/fr/themode/minestom/net/packet/server/play/CloseWindowPacket.java create mode 100644 src/main/java/fr/themode/minestom/net/packet/server/play/ConfirmTransactionPacket.java create mode 100644 src/main/java/fr/themode/minestom/net/packet/server/play/MultiBlockChangePacket.java create mode 100644 src/main/java/fr/themode/minestom/net/packet/server/play/OpenWindowPacket.java create mode 100644 src/main/java/fr/themode/minestom/net/packet/server/play/SetSlotPacket.java diff --git a/src/main/java/fr/themode/minestom/entity/Player.java b/src/main/java/fr/themode/minestom/entity/Player.java index bed8701e0..adccd783c 100644 --- a/src/main/java/fr/themode/minestom/entity/Player.java +++ b/src/main/java/fr/themode/minestom/entity/Player.java @@ -1,6 +1,7 @@ package fr.themode.minestom.entity; import fr.themode.minestom.Main; +import fr.themode.minestom.inventory.Inventory; import fr.themode.minestom.inventory.PlayerInventory; import fr.themode.minestom.item.ItemStack; import fr.themode.minestom.net.packet.server.play.*; @@ -21,6 +22,7 @@ public class Player extends LivingEntity { private GameMode gameMode; private PlayerInventory inventory; private short heldSlot; + private Inventory openInventory; // TODO set proper UUID public Player(UUID uuid, String username, PlayerConnection playerConnection) { @@ -102,6 +104,40 @@ public class Player extends LivingEntity { return inventory.getItemStack(heldSlot); } + public Inventory getOpenInventory() { + return openInventory; + } + + public void openInventory(Inventory inventory) { + if (inventory == null) + throw new IllegalArgumentException("Inventory cannot be null, use Player#closeInventory() to close current"); + + if (getOpenInventory() != null) { + getOpenInventory().removeViewer(this); + } + + OpenWindowPacket openWindowPacket = new OpenWindowPacket(); + openWindowPacket.windowId = inventory.getUniqueId(); + openWindowPacket.windowType = inventory.getInventoryType().getWindowType(); + openWindowPacket.title = inventory.getTitle(); + playerConnection.sendPacket(openWindowPacket); + inventory.addViewer(this); + refreshOpenInventory(inventory); + } + + public void closeInventory() { + Inventory openInventory = getOpenInventory(); + CloseWindowPacket closeWindowPacket = new CloseWindowPacket(); + if (openInventory == null) { + closeWindowPacket.windowId = 0; + } else { + closeWindowPacket.windowId = openInventory.getUniqueId(); + openInventory.removeViewer(this); + refreshOpenInventory(null); + } + playerConnection.sendPacket(closeWindowPacket); + } + public void refreshGameMode(GameMode gameMode) { this.gameMode = gameMode; } @@ -131,6 +167,10 @@ public class Player extends LivingEntity { this.heldSlot = slot; } + public void refreshOpenInventory(Inventory openInventory) { + this.openInventory = openInventory; + } + public long getLastKeepAlive() { return lastKeepAlive; } diff --git a/src/main/java/fr/themode/minestom/instance/BlockBatch.java b/src/main/java/fr/themode/minestom/instance/BlockBatch.java index a525a152b..f1a552211 100644 --- a/src/main/java/fr/themode/minestom/instance/BlockBatch.java +++ b/src/main/java/fr/themode/minestom/instance/BlockBatch.java @@ -47,7 +47,7 @@ public class BlockBatch { for (BlockData data : dataList) { data.apply(chunk); } - instance.sendChunkUpdate(chunk); + instance.sendChunkUpdate(chunk); // TODO partial chunk data }); } } @@ -59,7 +59,7 @@ public class BlockBatch { private Block block; public void apply(Chunk chunk) { - chunk.setBlock(x % 16, y, z % 16, block); + chunk.setBlock(x, y, z, block); } } diff --git a/src/main/java/fr/themode/minestom/instance/Chunk.java b/src/main/java/fr/themode/minestom/instance/Chunk.java index 84262e102..e32033fe7 100644 --- a/src/main/java/fr/themode/minestom/instance/Chunk.java +++ b/src/main/java/fr/themode/minestom/instance/Chunk.java @@ -23,7 +23,7 @@ public class Chunk { this.chunkZ = chunkZ; } - public void setBlock(int x, int y, int z, Block block) { + protected void setBlock(int x, int y, int z, Block block) { short index = (short) (x & 0x000F); index |= (y << 4) & 0x0FF0; index |= (z << 12) & 0xF000; diff --git a/src/main/java/fr/themode/minestom/inventory/Inventory.java b/src/main/java/fr/themode/minestom/inventory/Inventory.java new file mode 100644 index 000000000..ef44875fe --- /dev/null +++ b/src/main/java/fr/themode/minestom/inventory/Inventory.java @@ -0,0 +1,235 @@ +package fr.themode.minestom.inventory; + +import fr.themode.minestom.entity.Player; +import fr.themode.minestom.item.ItemStack; +import fr.themode.minestom.net.packet.server.play.WindowItemsPacket; + +import java.util.Arrays; +import java.util.Collections; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.CopyOnWriteArraySet; +import java.util.concurrent.atomic.AtomicInteger; + +public class Inventory implements InventoryClickHandler { + + private static AtomicInteger lastInventoryId = new AtomicInteger(); + + private int id; + private InventoryType inventoryType; + private String title; + + private int offset; + + private ItemStack[] itemStacks; + private Set viewers = new CopyOnWriteArraySet<>(); + private ConcurrentHashMap cursorPlayersItem = new ConcurrentHashMap<>(); + + public Inventory(InventoryType inventoryType, String title) { + this.id = generateId(); + this.inventoryType = inventoryType; + this.title = title; + + this.offset = inventoryType.getAdditionalSlot(); + + this.itemStacks = new ItemStack[inventoryType.getAdditionalSlot()]; + Arrays.fill(itemStacks, ItemStack.AIR_ITEM); + } + + private static int generateId() { + return lastInventoryId.incrementAndGet(); + } + + public InventoryType getInventoryType() { + return inventoryType; + } + + public String getTitle() { + return title; + } + + public int getUniqueId() { + return id; + } + + public void setItemStack(int slot, ItemStack itemStack) { + if (slot < 0 || slot > inventoryType.getAdditionalSlot()) + throw new IllegalArgumentException(inventoryType.toString() + " does not have slot " + slot); + + safeItemInsert(slot, itemStack); + } + + public ItemStack getItemStack(int slot) { + return itemStacks[slot]; + } + + public void updateItems() { + WindowItemsPacket windowItemsPacket = getWindowItemsPacket(); + getViewers().forEach(p -> p.getPlayerConnection().sendPacket(windowItemsPacket)); + } + + public Set getViewers() { + return Collections.unmodifiableSet(viewers); + } + + public void addViewer(Player player) { + this.viewers.add(player); + WindowItemsPacket windowItemsPacket = getWindowItemsPacket(); + player.getPlayerConnection().sendPacket(windowItemsPacket); + } + + public void removeViewer(Player player) { + this.viewers.remove(player); + } + + public ItemStack getCursorItem(Player player) { + return cursorPlayersItem.getOrDefault(player, ItemStack.AIR_ITEM); + } + + private void safeItemInsert(int slot, ItemStack itemStack) { + synchronized (this) { + this.itemStacks[slot] = itemStack == null ? ItemStack.AIR_ITEM : itemStack; + } + } + + private WindowItemsPacket getWindowItemsPacket() { + WindowItemsPacket windowItemsPacket = new WindowItemsPacket(); + windowItemsPacket.windowId = getUniqueId(); + windowItemsPacket.count = (short) itemStacks.length; + windowItemsPacket.items = itemStacks; + return windowItemsPacket; + } + + private void setCursorPlayerItem(Player player, ItemStack itemStack) { + this.cursorPlayersItem.put(player, itemStack); + } + + private boolean isClickInWindow(int slot) { + return slot < inventoryType.getAdditionalSlot(); + } + + @Override + public void leftClick(Player player, int slot) { + PlayerInventory playerInventory = player.getInventory(); + ItemStack cursorItem = getCursorItem(player); + boolean isInWindow = isClickInWindow(slot); + ItemStack clicked = isInWindow ? getItemStack(slot) : playerInventory.getItemStack(slot, offset); + + if (cursorItem.isAir() && clicked.isAir()) + return; + + ItemStack resultCursor; + ItemStack resultClicked; + + if (cursorItem.isSimilar(clicked)) { + resultCursor = cursorItem.clone(); + resultClicked = clicked.clone(); + int amount = cursorItem.getAmount() + clicked.getAmount(); + if (amount > 100) { + resultCursor.setAmount((byte) (amount - 100)); + resultClicked.setAmount((byte) 100); + } else { + resultCursor = ItemStack.AIR_ITEM; + resultClicked.setAmount((byte) amount); + } + } else { + resultCursor = clicked.clone(); + resultClicked = cursorItem.clone(); + } + + if (isInWindow) { + setItemStack(slot, resultClicked); + setCursorPlayerItem(player, resultCursor); + updateItems(); + } else { + playerInventory.setItemStack(slot, offset, resultClicked); + setCursorPlayerItem(player, resultCursor); + playerInventory.update(); + } + } + + @Override + public void rightClick(Player player, int slot) { + PlayerInventory playerInventory = player.getInventory(); + ItemStack cursorItem = getCursorItem(player); + boolean isInWindow = isClickInWindow(slot); + ItemStack clicked = isInWindow ? getItemStack(slot) : playerInventory.getItemStack(slot, offset); + + if (cursorItem.isAir() && clicked.isAir()) + return; + + ItemStack resultCursor; + ItemStack resultClicked; + + if (cursorItem.isSimilar(clicked)) { + resultCursor = cursorItem.clone(); + resultClicked = clicked.clone(); + int amount = clicked.getAmount() + 1; + if (amount > 100) { + return; + } else { + resultCursor = cursorItem.clone(); + resultCursor.setAmount((byte) (resultCursor.getAmount() - 1)); + if (resultCursor.getAmount() < 1) + resultCursor = ItemStack.AIR_ITEM; + resultClicked.setAmount((byte) amount); + } + } else { + if (cursorItem.isAir()) { + int amount = (int) Math.ceil((double) clicked.getAmount() / 2d); + resultCursor = clicked.clone(); + resultCursor.setAmount((byte) amount); + resultClicked = clicked.clone(); + resultClicked.setAmount((byte) (clicked.getAmount() / 2)); + } else { + if (clicked.isAir()) { + int amount = cursorItem.getAmount(); + resultCursor = cursorItem.clone(); + resultCursor.setAmount((byte) (amount - 1)); + if (resultCursor.getAmount() < 1) + resultCursor = ItemStack.AIR_ITEM; + resultClicked = cursorItem.clone(); + resultClicked.setAmount((byte) 1); + } else { + resultCursor = clicked.clone(); + resultClicked = cursorItem.clone(); + } + } + } + + if (isInWindow) { + setItemStack(slot, resultClicked); + setCursorPlayerItem(player, resultCursor); + updateItems(); + } else { + playerInventory.setItemStack(slot, offset, resultClicked); + setCursorPlayerItem(player, resultCursor); + playerInventory.update(); + } + } + + @Override + public void shiftClick(Player player, int slot) { + + } + + @Override + public void changeHeld(Player player, int slot, int key) { + + } + + @Override + public void middleClick(Player player, int slot) { + + } + + @Override + public void dropOne(Player player, int slot) { + + } + + @Override + public void dropItemStack(Player player, int slot) { + + } +} diff --git a/src/main/java/fr/themode/minestom/inventory/InventoryClickHandler.java b/src/main/java/fr/themode/minestom/inventory/InventoryClickHandler.java new file mode 100644 index 000000000..6d3683773 --- /dev/null +++ b/src/main/java/fr/themode/minestom/inventory/InventoryClickHandler.java @@ -0,0 +1,21 @@ +package fr.themode.minestom.inventory; + +import fr.themode.minestom.entity.Player; + +public interface InventoryClickHandler { + + void leftClick(Player player, int slot); + + void rightClick(Player player, int slot); + + void shiftClick(Player player, int slot); // shift + left/right click have the same behavior + + void changeHeld(Player player, int slot, int key); + + void middleClick(Player player, int slot); + + void dropOne(Player player, int slot); + + void dropItemStack(Player player, int slot); + +} diff --git a/src/main/java/fr/themode/minestom/inventory/InventoryType.java b/src/main/java/fr/themode/minestom/inventory/InventoryType.java new file mode 100644 index 000000000..ea33415c2 --- /dev/null +++ b/src/main/java/fr/themode/minestom/inventory/InventoryType.java @@ -0,0 +1,45 @@ +package fr.themode.minestom.inventory; + +public enum InventoryType { + + CHEST_1_ROW(0, 9), + CHEST_2_ROW(1, 18), + CHEST_3_ROW(2, 27), + CHEST_4_ROW(3, 36), + CHEST_5_ROW(4, 45), + CHEST_6_ROW(5, 54), + WINDOW_3X3(6, 9), + ANVIL(7, 3), + BEACON(8, 1), + BLAST_FURNACE(9, 3), + BREWING_STAND(10, 5), + CRAFTING(11, 10), + ENCHANTMENT(12, 2), + FURNACE(13, 3), + GRINDSTONE(14, 3), + HOPPER(15, 5), + LECTERN(16, 0), + LOOM(17, 4), + MERCHANT(18, 3), + SHULKER_BOX(19, 27), + SMOKER(20, 3), + CARTOGRAPHY(21, 3), + STONE_CUTTER(22, 2); + + private int windowType; + private int slot; + + InventoryType(int windowType, int slot) { + this.windowType = windowType; + this.slot = slot; + } + + public int getWindowType() { + return windowType; + } + + public int getAdditionalSlot() { + return slot; + } + +} diff --git a/src/main/java/fr/themode/minestom/inventory/PlayerInventory.java b/src/main/java/fr/themode/minestom/inventory/PlayerInventory.java index 14e0c7d91..596fb90f0 100644 --- a/src/main/java/fr/themode/minestom/inventory/PlayerInventory.java +++ b/src/main/java/fr/themode/minestom/inventory/PlayerInventory.java @@ -5,6 +5,8 @@ import fr.themode.minestom.item.ItemStack; import fr.themode.minestom.net.packet.server.play.WindowItemsPacket; import fr.themode.minestom.net.player.PlayerConnection; +import java.util.Arrays; + public class PlayerInventory { private Player player; @@ -12,51 +14,87 @@ public class PlayerInventory { public PlayerInventory(Player player) { this.player = player; + + Arrays.fill(items, ItemStack.AIR_ITEM); } public ItemStack getItemStack(int slot) { - ItemStack item = this.items[convertSlot(slot)]; - return item != null ? item : ItemStack.AIR_ITEM; + return this.items[slot]; } public void setItemStack(int slot, ItemStack itemStack) { - this.items[convertSlot(slot)] = itemStack; + safeItemInsert(slot, itemStack); } public void setHelmet(ItemStack itemStack) { - this.items[5] = itemStack; + safeItemInsert(5, itemStack); } public void setChestplate(ItemStack itemStack) { - this.items[6] = itemStack; + safeItemInsert(6, itemStack); } public void setLeggings(ItemStack itemStack) { - this.items[7] = itemStack; + safeItemInsert(7, itemStack); } public void setBoots(ItemStack itemStack) { - this.items[8] = itemStack; + safeItemInsert(8, itemStack); } public void update() { PlayerConnection playerConnection = player.getPlayerConnection(); - - WindowItemsPacket windowItemsPacket = new WindowItemsPacket(); - windowItemsPacket.windowId = 0; // player inventory ID - windowItemsPacket.count = (short) items.length; - windowItemsPacket.items = items; - playerConnection.sendPacket(windowItemsPacket); + playerConnection.sendPacket(createWindowItemsPacket()); } - private int convertSlot(int slot) { - if (slot >= 0 && slot <= 9 * 4) { - int row = slot / 9; - int place = slot % 9; - int converted = 9 * (4 - 1 - row) + place + 9; - return converted; + private void safeItemInsert(int slot, ItemStack itemStack) { + synchronized (this) { + this.items[slot] = itemStack == null ? ItemStack.AIR_ITEM : itemStack; } + } + + protected void setItemStack(int slot, int offset, ItemStack itemStack) { + slot = convertSlot(slot, offset); + // System.out.println("NEWSLOT: "+slot +" : "+itemStack.getItemId()); + safeItemInsert(slot, itemStack); + } + + protected ItemStack getItemStack(int slot, int offset) { + slot = convertSlot(slot, offset); + return this.items[slot]; + } + + protected int convertSlot(int slot, int offset) { + final int rowSize = 9; + slot -= offset; + if (slot >= rowSize * 3 && slot < rowSize * 4) { + slot = slot % 9; + } else { + slot = slot + rowSize; + } + System.out.println("DEBUG: " + slot); return slot; } + private WindowItemsPacket createWindowItemsPacket() { + ItemStack[] convertedSlots = new ItemStack[45]; + Arrays.fill(convertedSlots, ItemStack.AIR_ITEM); // TODO armor and craft + + // Hotbar + for (int i = 0; i < 9; i++) { + convertedSlots[36 + i] = items[i]; + } + + // Inventory + for (int i = 10; i < 9 + 9 * 3; i++) { + convertedSlots[i] = items[i]; + } + + WindowItemsPacket windowItemsPacket = new WindowItemsPacket(); + windowItemsPacket.windowId = 0; + windowItemsPacket.count = 45; + windowItemsPacket.items = convertedSlots; + return windowItemsPacket; + } + } diff --git a/src/main/java/fr/themode/minestom/item/ItemStack.java b/src/main/java/fr/themode/minestom/item/ItemStack.java index a31220a90..14bab5d76 100644 --- a/src/main/java/fr/themode/minestom/item/ItemStack.java +++ b/src/main/java/fr/themode/minestom/item/ItemStack.java @@ -1,22 +1,38 @@ package fr.themode.minestom.item; -public class ItemStack { +public class ItemStack implements Cloneable { public static final ItemStack AIR_ITEM = new ItemStack(0, (byte) 1); private int itemId; - private byte count; + private byte amount; - public ItemStack(int itemId, byte count) { + public ItemStack(int itemId, byte amount) { this.itemId = itemId; - this.count = count; + this.amount = amount; + } + + public boolean isAir() { + return itemId == 0; + } + + public boolean isSimilar(ItemStack itemStack) { + return itemStack.getItemId() == itemId; + } + + public byte getAmount() { + return amount; } public int getItemId() { return itemId; } - public byte getCount() { - return count; + public void setAmount(byte amount) { + this.amount = amount; + } + + public ItemStack clone() { + return new ItemStack(itemId, amount); } } diff --git a/src/main/java/fr/themode/minestom/net/packet/client/handler/ClientPlayPacketsHandler.java b/src/main/java/fr/themode/minestom/net/packet/client/handler/ClientPlayPacketsHandler.java index 0a1938f92..9a2d496c7 100644 --- a/src/main/java/fr/themode/minestom/net/packet/client/handler/ClientPlayPacketsHandler.java +++ b/src/main/java/fr/themode/minestom/net/packet/client/handler/ClientPlayPacketsHandler.java @@ -21,5 +21,8 @@ public class ClientPlayPacketsHandler extends ClientPacketsHandler { register(0x1A, ClientPlayerDiggingPacket.class); register(0x2C, ClientPlayerBlockPlacementPacket.class); register(0x23, ClientHeldItemChangePacket.class); + register(0x09, ClientClickWindowPacket.class); + register(0x0A, ClientCloseWindow.class); + register(0x07, ClientConfirmTransactionPacket.class); } } diff --git a/src/main/java/fr/themode/minestom/net/packet/client/login/LoginStartPacket.java b/src/main/java/fr/themode/minestom/net/packet/client/login/LoginStartPacket.java index 8e6875296..0cee77cd1 100644 --- a/src/main/java/fr/themode/minestom/net/packet/client/login/LoginStartPacket.java +++ b/src/main/java/fr/themode/minestom/net/packet/client/login/LoginStartPacket.java @@ -7,6 +7,8 @@ import fr.themode.minestom.entity.Player; import fr.themode.minestom.entity.demo.ChickenCreature; import fr.themode.minestom.instance.Block; import fr.themode.minestom.instance.Instance; +import fr.themode.minestom.inventory.Inventory; +import fr.themode.minestom.inventory.InventoryType; import fr.themode.minestom.inventory.PlayerInventory; import fr.themode.minestom.item.ItemStack; import fr.themode.minestom.net.ConnectionManager; @@ -56,7 +58,7 @@ public class LoginStartPacket implements ClientPreplayPacket { connection.setConnectionState(ConnectionState.PLAY); connectionManager.createPlayer(uuids.get(username), username, connection); Player player = connectionManager.getPlayer(connection); - GameMode gameMode = GameMode.CREATIVE; + GameMode gameMode = GameMode.SURVIVAL; player.refreshGameMode(gameMode); @@ -141,9 +143,16 @@ public class LoginStartPacket implements ClientPreplayPacket { //System.out.println("HAHAHAHHAHHAH " + player.getUuid()); PlayerInventory inventory = player.getInventory(); - inventory.setItemStack(1, new ItemStack(1, (byte) 1)); + inventory.setItemStack(1, new ItemStack(1, (byte) 32)); + inventory.setItemStack(2, new ItemStack(1, (byte) 32)); inventory.update(); + Inventory inv = new Inventory(InventoryType.WINDOW_3X3, "Salut je suis le titre"); + inv.setItemStack(0, new ItemStack(1, (byte) 1)); + player.openInventory(inv); + inv.setItemStack(1, new ItemStack(1, (byte) 2)); + inv.updateItems(); + } @Override diff --git a/src/main/java/fr/themode/minestom/net/packet/client/play/ClientClickWindowPacket.java b/src/main/java/fr/themode/minestom/net/packet/client/play/ClientClickWindowPacket.java new file mode 100644 index 000000000..148e9e762 --- /dev/null +++ b/src/main/java/fr/themode/minestom/net/packet/client/play/ClientClickWindowPacket.java @@ -0,0 +1,90 @@ +package fr.themode.minestom.net.packet.client.play; + +import fr.adamaq01.ozao.net.Buffer; +import fr.themode.minestom.entity.Player; +import fr.themode.minestom.inventory.Inventory; +import fr.themode.minestom.item.ItemStack; +import fr.themode.minestom.net.packet.client.ClientPlayPacket; +import fr.themode.minestom.net.packet.server.play.ConfirmTransactionPacket; +import fr.themode.minestom.net.packet.server.play.SetSlotPacket; +import fr.themode.minestom.utils.Utils; + +public class ClientClickWindowPacket implements ClientPlayPacket { + + public byte windowId; + public short slot; + public byte button; + public short actionNumber; + public int mode; + // TODO clicked item + + @Override + public void process(Player player) { + Inventory inventory = player.getOpenInventory(); + System.out.println("Window id: " + windowId + " | slot: " + slot + " | button: " + button + " | mode: " + mode); + + ConfirmTransactionPacket confirmTransactionPacket = new ConfirmTransactionPacket(); + confirmTransactionPacket.windowId = windowId; + confirmTransactionPacket.actionNumber = actionNumber; + confirmTransactionPacket.accepted = true; // Change depending on output + + switch (mode) { + case 0: + switch (button) { + case 0: + // Left click + inventory.leftClick(player, slot); + break; + case 1: + // Right click + inventory.rightClick(player, slot); + break; + } + break; + case 1: + switch (button) { + case 0: + // Shift + left click + break; + case 1: + // Shift + right click + break; + } + break; + case 2: + // Number key 1-9 + break; + case 3: + // Middle click (only creative players in non-player inventories) + break; + case 4: + // Dropping functions + break; + case 5: + // Dragging + break; + case 6: + // Double click (merge similar items) + break; + } + + ItemStack cursorItem = inventory.getCursorItem(player); + SetSlotPacket setSlotPacket = new SetSlotPacket(); + setSlotPacket.windowId = -1; + setSlotPacket.slot = -1; + setSlotPacket.itemStack = cursorItem; + + player.getPlayerConnection().sendPacket(setSlotPacket); + player.getPlayerConnection().sendPacket(confirmTransactionPacket); + } + + @Override + public void read(Buffer buffer) { + this.windowId = buffer.getByte(); + this.slot = buffer.getShort(); + this.button = buffer.getByte(); + this.actionNumber = buffer.getShort(); + this.mode = Utils.readVarInt(buffer); + // TODO read clicked item + } +} diff --git a/src/main/java/fr/themode/minestom/net/packet/client/play/ClientCloseWindow.java b/src/main/java/fr/themode/minestom/net/packet/client/play/ClientCloseWindow.java new file mode 100644 index 000000000..47ced2f04 --- /dev/null +++ b/src/main/java/fr/themode/minestom/net/packet/client/play/ClientCloseWindow.java @@ -0,0 +1,26 @@ +package fr.themode.minestom.net.packet.client.play; + +import fr.adamaq01.ozao.net.Buffer; +import fr.themode.minestom.entity.Player; +import fr.themode.minestom.inventory.Inventory; +import fr.themode.minestom.net.packet.client.ClientPlayPacket; +import fr.themode.minestom.utils.Utils; + +public class ClientCloseWindow implements ClientPlayPacket { + + public int windowId; + + @Override + public void process(Player player) { + System.out.println("CLOSED: " + windowId); + Inventory openInventory = player.getOpenInventory(); + // if windowId == 0 then it is player's inventory, meaning that they hadn't been any open inventory packet + if (openInventory != null) + player.closeInventory(); + } + + @Override + public void read(Buffer buffer) { + this.windowId = Utils.readVarInt(buffer); + } +} diff --git a/src/main/java/fr/themode/minestom/net/packet/client/play/ClientConfirmTransactionPacket.java b/src/main/java/fr/themode/minestom/net/packet/client/play/ClientConfirmTransactionPacket.java new file mode 100644 index 000000000..da83f4a9b --- /dev/null +++ b/src/main/java/fr/themode/minestom/net/packet/client/play/ClientConfirmTransactionPacket.java @@ -0,0 +1,22 @@ +package fr.themode.minestom.net.packet.client.play; + +import fr.adamaq01.ozao.net.Buffer; +import fr.themode.minestom.entity.Player; +import fr.themode.minestom.net.packet.client.ClientPlayPacket; + +public class ClientConfirmTransactionPacket implements ClientPlayPacket { + + public int windowId; + public short actionNumber; + public boolean accepted; + + @Override + public void process(Player player) { + + } + + @Override + public void read(Buffer buffer) { + + } +} diff --git a/src/main/java/fr/themode/minestom/net/packet/client/play/ClientPlayerBlockPlacementPacket.java b/src/main/java/fr/themode/minestom/net/packet/client/play/ClientPlayerBlockPlacementPacket.java index 60d3d0fba..cc617d1c4 100644 --- a/src/main/java/fr/themode/minestom/net/packet/client/play/ClientPlayerBlockPlacementPacket.java +++ b/src/main/java/fr/themode/minestom/net/packet/client/play/ClientPlayerBlockPlacementPacket.java @@ -9,6 +9,8 @@ import fr.themode.minestom.net.packet.client.ClientPlayPacket; import fr.themode.minestom.utils.Position; import fr.themode.minestom.utils.Utils; +import java.util.Random; + public class ClientPlayerBlockPlacementPacket implements ClientPlayPacket { public Hand hand; @@ -23,10 +25,12 @@ public class ClientPlayerBlockPlacementPacket implements ClientPlayPacket { if (instance == null) return; + Random random = new Random(); BlockBatch blockBatch = instance.createBlockBatch(); for (int x = -64; x < 64; x++) for (int z = -64; z < 64; z++) { - blockBatch.setBlock(x, position.getY() + 1, z, new Block(1)); + if (random.nextInt(100) > 75) + blockBatch.setBlock(x, position.getY() + 1, z, new Block(1)); } blockBatch.flush(); } diff --git a/src/main/java/fr/themode/minestom/net/packet/server/play/BlockChangePacket.java b/src/main/java/fr/themode/minestom/net/packet/server/play/BlockChangePacket.java new file mode 100644 index 000000000..03468612b --- /dev/null +++ b/src/main/java/fr/themode/minestom/net/packet/server/play/BlockChangePacket.java @@ -0,0 +1,23 @@ +package fr.themode.minestom.net.packet.server.play; + +import fr.adamaq01.ozao.net.Buffer; +import fr.themode.minestom.net.packet.server.ServerPacket; +import fr.themode.minestom.utils.Position; +import fr.themode.minestom.utils.Utils; + +public class BlockChangePacket implements ServerPacket { + + public Position position; + public int blockId; + + @Override + public void write(Buffer buffer) { + Utils.writePosition(buffer, position.getX(), position.getY(), position.getZ()); + Utils.writeVarInt(buffer, blockId); + } + + @Override + public int getId() { + return 0x0B; + } +} diff --git a/src/main/java/fr/themode/minestom/net/packet/server/play/ChatMessagePacket.java b/src/main/java/fr/themode/minestom/net/packet/server/play/ChatMessagePacket.java index 63fd6ea84..42df308c8 100644 --- a/src/main/java/fr/themode/minestom/net/packet/server/play/ChatMessagePacket.java +++ b/src/main/java/fr/themode/minestom/net/packet/server/play/ChatMessagePacket.java @@ -25,7 +25,7 @@ public class ChatMessagePacket implements ServerPacket { return 0x0E; } - public static enum Position { + public enum Position { CHAT, SYSTEM_MESSAGE, diff --git a/src/main/java/fr/themode/minestom/net/packet/server/play/CloseWindowPacket.java b/src/main/java/fr/themode/minestom/net/packet/server/play/CloseWindowPacket.java new file mode 100644 index 000000000..51bde3661 --- /dev/null +++ b/src/main/java/fr/themode/minestom/net/packet/server/play/CloseWindowPacket.java @@ -0,0 +1,20 @@ +package fr.themode.minestom.net.packet.server.play; + +import fr.adamaq01.ozao.net.Buffer; +import fr.themode.minestom.net.packet.server.ServerPacket; +import fr.themode.minestom.utils.Utils; + +public class CloseWindowPacket implements ServerPacket { + + public int windowId; + + @Override + public void write(Buffer buffer) { + Utils.writeVarInt(buffer, windowId); + } + + @Override + public int getId() { + return 0x13; + } +} diff --git a/src/main/java/fr/themode/minestom/net/packet/server/play/ConfirmTransactionPacket.java b/src/main/java/fr/themode/minestom/net/packet/server/play/ConfirmTransactionPacket.java new file mode 100644 index 000000000..30e755d6f --- /dev/null +++ b/src/main/java/fr/themode/minestom/net/packet/server/play/ConfirmTransactionPacket.java @@ -0,0 +1,24 @@ +package fr.themode.minestom.net.packet.server.play; + +import fr.adamaq01.ozao.net.Buffer; +import fr.themode.minestom.net.packet.server.ServerPacket; +import fr.themode.minestom.utils.Utils; + +public class ConfirmTransactionPacket implements ServerPacket { + + public int windowId; + public short actionNumber; + public boolean accepted; + + @Override + public void write(Buffer buffer) { + Utils.writeVarInt(buffer, windowId); + buffer.putShort(actionNumber); + buffer.putBoolean(accepted); + } + + @Override + public int getId() { + return 0x12; + } +} diff --git a/src/main/java/fr/themode/minestom/net/packet/server/play/MultiBlockChangePacket.java b/src/main/java/fr/themode/minestom/net/packet/server/play/MultiBlockChangePacket.java new file mode 100644 index 000000000..e33a2b086 --- /dev/null +++ b/src/main/java/fr/themode/minestom/net/packet/server/play/MultiBlockChangePacket.java @@ -0,0 +1,40 @@ +package fr.themode.minestom.net.packet.server.play; + +import fr.adamaq01.ozao.net.Buffer; +import fr.themode.minestom.net.packet.server.ServerPacket; +import fr.themode.minestom.utils.Utils; + +public class MultiBlockChangePacket implements ServerPacket { + + public int chunkX; + public int chunkZ; + public BlockChange[] blockChanges; + + @Override + public void write(Buffer buffer) { + buffer.putInt(chunkX); + buffer.putInt(chunkZ); + Utils.writeVarInt(buffer, blockChanges == null ? 0 : blockChanges.length); + + if (blockChanges != null) { + for (int i = 0; i < blockChanges.length; i++) { + BlockChange blockChange = blockChanges[i]; + buffer.putByte(blockChange.positionXZ); + buffer.putByte(blockChange.positionY); + Utils.writeVarInt(buffer, blockChange.newBlockId); + } + } + } + + @Override + public int getId() { + return 0x0F; + } + + public static class BlockChange { + public byte positionXZ; + public byte positionY; + public int newBlockId; + + } +} diff --git a/src/main/java/fr/themode/minestom/net/packet/server/play/OpenWindowPacket.java b/src/main/java/fr/themode/minestom/net/packet/server/play/OpenWindowPacket.java new file mode 100644 index 000000000..fba8285b1 --- /dev/null +++ b/src/main/java/fr/themode/minestom/net/packet/server/play/OpenWindowPacket.java @@ -0,0 +1,24 @@ +package fr.themode.minestom.net.packet.server.play; + +import fr.adamaq01.ozao.net.Buffer; +import fr.themode.minestom.net.packet.server.ServerPacket; +import fr.themode.minestom.utils.Utils; + +public class OpenWindowPacket implements ServerPacket { + + public int windowId; + public int windowType; + public String title; + + @Override + public void write(Buffer buffer) { + Utils.writeVarInt(buffer, windowId); + Utils.writeVarInt(buffer, windowType); + Utils.writeString(buffer, "{\"text\": \"" + title + " \"}"); + } + + @Override + public int getId() { + return 0x2E; + } +} diff --git a/src/main/java/fr/themode/minestom/net/packet/server/play/SetSlotPacket.java b/src/main/java/fr/themode/minestom/net/packet/server/play/SetSlotPacket.java new file mode 100644 index 000000000..6d3f18a71 --- /dev/null +++ b/src/main/java/fr/themode/minestom/net/packet/server/play/SetSlotPacket.java @@ -0,0 +1,25 @@ +package fr.themode.minestom.net.packet.server.play; + +import fr.adamaq01.ozao.net.Buffer; +import fr.themode.minestom.item.ItemStack; +import fr.themode.minestom.net.packet.server.ServerPacket; +import fr.themode.minestom.utils.Utils; + +public class SetSlotPacket implements ServerPacket { + + public byte windowId; + public short slot; + public ItemStack itemStack; + + @Override + public void write(Buffer buffer) { + buffer.putByte(windowId); + buffer.putShort(slot); + Utils.writeItemStack(buffer, itemStack); + } + + @Override + public int getId() { + return 0x16; + } +} diff --git a/src/main/java/fr/themode/minestom/net/packet/server/play/WindowItemsPacket.java b/src/main/java/fr/themode/minestom/net/packet/server/play/WindowItemsPacket.java index 644c913b7..ef25f25c7 100644 --- a/src/main/java/fr/themode/minestom/net/packet/server/play/WindowItemsPacket.java +++ b/src/main/java/fr/themode/minestom/net/packet/server/play/WindowItemsPacket.java @@ -7,7 +7,7 @@ import fr.themode.minestom.utils.Utils; public class WindowItemsPacket implements ServerPacket { - public byte windowId; + public int windowId; public short count; public ItemStack[] items; @@ -15,21 +15,14 @@ public class WindowItemsPacket implements ServerPacket { @Override public void write(Buffer buffer) { - buffer.putByte(windowId); + Utils.writeVarInt(buffer, windowId); buffer.putShort(count); if (items == null) return; for (int i = 0; i < items.length; i++) { ItemStack item = items[i]; - if (item == null) { - buffer.putBoolean(false); - } else { - buffer.putBoolean(true); - Utils.writeVarInt(buffer, item.getItemId()); - buffer.putByte(item.getCount()); - buffer.putByte((byte) 0); // End nbt TODO - } + Utils.writeItemStack(buffer, item); } } diff --git a/src/main/java/fr/themode/minestom/utils/Utils.java b/src/main/java/fr/themode/minestom/utils/Utils.java index 02ea63fcc..0453f4e51 100644 --- a/src/main/java/fr/themode/minestom/utils/Utils.java +++ b/src/main/java/fr/themode/minestom/utils/Utils.java @@ -2,6 +2,7 @@ package fr.themode.minestom.utils; import fr.adamaq01.ozao.net.Buffer; import fr.themode.minestom.instance.Block; +import fr.themode.minestom.item.ItemStack; import java.io.UnsupportedEncodingException; import java.util.Arrays; @@ -132,6 +133,17 @@ public class Utils { return new Position(x, y, z); } + public static void writeItemStack(Buffer buffer, ItemStack itemStack) { + if (itemStack == null) { + buffer.putBoolean(false); + } else { + buffer.putBoolean(true); + Utils.writeVarInt(buffer, itemStack.getItemId()); + buffer.putByte(itemStack.getAmount()); + buffer.putByte((byte) 0); // End nbt TODO + } + } + public static void writeBlocks(Buffer buffer, Block[] blocks, int bitsPerEntry) { buffer.putShort((short) Arrays.stream(blocks).filter(customBlock -> customBlock.getType() != 0).collect(Collectors.toList()).size()); buffer.putByte((byte) bitsPerEntry);