diff --git a/proxy/src/main/java/org/dragonet/proxy/network/session/cache/WindowCache.java b/proxy/src/main/java/org/dragonet/proxy/network/session/cache/WindowCache.java index 22a811474..36c1b1e7a 100644 --- a/proxy/src/main/java/org/dragonet/proxy/network/session/cache/WindowCache.java +++ b/proxy/src/main/java/org/dragonet/proxy/network/session/cache/WindowCache.java @@ -25,6 +25,8 @@ import lombok.Getter; import org.dragonet.proxy.data.window.BedrockWindowType; import org.dragonet.proxy.network.session.cache.object.CachedWindow; +import org.dragonet.proxy.network.translator.misc.inventory.IInventoryTranslator; +import org.dragonet.proxy.network.translator.misc.inventory.PlayerInventoryTranslator; import java.util.HashMap; import java.util.Map; @@ -36,9 +38,10 @@ public class WindowCache implements Cache { private AtomicInteger javaActionIdAllocator = new AtomicInteger(1); private AtomicInteger localWindowIdAllocator = new AtomicInteger(1000); + private AtomicInteger transactionIdCounter = new AtomicInteger(0); public WindowCache() { - windows.put(0, new CachedWindow(0, null, 45)); + windows.put(0, new CachedWindow(0, new PlayerInventoryTranslator(45))); } public CachedWindow getPlayerInventory() { @@ -52,8 +55,8 @@ public CachedWindow getById(int windowId) { return null; } - public CachedWindow newWindow(BedrockWindowType windowType, int windowId) { - CachedWindow window = new CachedWindow(windowId, windowType, 50); + public CachedWindow newWindow(IInventoryTranslator inventoryTranslator, int windowId) { + CachedWindow window = new CachedWindow(windowId, inventoryTranslator); windows.put(window.getWindowId(), window); return window; } diff --git a/proxy/src/main/java/org/dragonet/proxy/network/session/cache/object/CachedWindow.java b/proxy/src/main/java/org/dragonet/proxy/network/session/cache/object/CachedWindow.java index d622506e0..c25902588 100644 --- a/proxy/src/main/java/org/dragonet/proxy/network/session/cache/object/CachedWindow.java +++ b/proxy/src/main/java/org/dragonet/proxy/network/session/cache/object/CachedWindow.java @@ -23,14 +23,24 @@ import com.nukkitx.math.vector.Vector3i; import com.nukkitx.nbt.tag.CompoundTag; import com.nukkitx.protocol.bedrock.data.EntityData; +import com.nukkitx.protocol.bedrock.data.ItemData; import com.nukkitx.protocol.bedrock.packet.*; +import it.unimi.dsi.fastutil.ints.Int2BooleanMap; +import it.unimi.dsi.fastutil.ints.Int2BooleanOpenHashMap; +import it.unimi.dsi.fastutil.ints.Int2ObjectMap; +import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; import lombok.Data; import lombok.extern.log4j.Log4j2; import org.dragonet.proxy.data.entity.BedrockEntityType; import org.dragonet.proxy.data.window.BedrockWindowType; import org.dragonet.proxy.network.session.ProxySession; +import org.dragonet.proxy.network.translator.ItemTranslatorRegistry; import org.dragonet.proxy.network.translator.misc.BlockEntityTranslator; import org.dragonet.proxy.network.translator.misc.BlockTranslator; +import org.dragonet.proxy.network.translator.misc.inventory.IInventoryTranslator; +import org.dragonet.proxy.network.translator.misc.inventory.action.SlotChangeAction; + +import java.util.concurrent.atomic.AtomicInteger; @Data @Log4j2 @@ -38,6 +48,7 @@ public class CachedWindow { private final int windowId; private ItemStack[] items; + private IInventoryTranslator inventoryTranslator; private BedrockWindowType windowType; private String name; @@ -45,10 +56,14 @@ public class CachedWindow { private Vector3i fakeBlockPosition = null; - public CachedWindow(int windowId, BedrockWindowType windowType, int size) { + private AtomicInteger transactionIdCounter = new AtomicInteger(1); + private Int2BooleanMap transactions = new Int2BooleanOpenHashMap(); + + public CachedWindow(int windowId, IInventoryTranslator inventoryTranslator) { this.windowId = windowId; - this.windowType = windowType; - this.items = new ItemStack[size]; + this.inventoryTranslator = inventoryTranslator; + this.items = new ItemStack[inventoryTranslator.getSize()]; + this.windowType = inventoryTranslator.getBedrockWindowType(); } public void open(ProxySession session) { @@ -96,6 +111,27 @@ public void close(ProxySession session) { // TODO: should we remove the window from the cache at this point? i'll leave it for now. } + public void sendInventory(ProxySession session) { + inventoryTranslator.updateInventory(session, this); + } + + public void sendSlot(ProxySession session, int slot) { + inventoryTranslator.updateSlot(session, this, slot); + } + + public boolean setItem(int slot, ItemData item) { + if(slot > items.length) { + log.warn("set item"); + return false; + } + this.items[slot] = ItemTranslatorRegistry.translateToJava(item); + return true; + } + + public ItemData getItem(int slot) { + return ItemTranslatorRegistry.translateSlotToBedrock(items[slot]); + } + private void sendFakeEntity(ProxySession session, Vector3i position) { long entityId = session.getEntityCache().getNextClientEntityId().getAndIncrement(); diff --git a/proxy/src/main/java/org/dragonet/proxy/network/translator/bedrock/PEInventoryTransactionTranslator.java b/proxy/src/main/java/org/dragonet/proxy/network/translator/bedrock/PEInventoryTransactionTranslator.java index d902f85e5..5b7fe0291 100644 --- a/proxy/src/main/java/org/dragonet/proxy/network/translator/bedrock/PEInventoryTransactionTranslator.java +++ b/proxy/src/main/java/org/dragonet/proxy/network/translator/bedrock/PEInventoryTransactionTranslator.java @@ -18,27 +18,38 @@ */ package org.dragonet.proxy.network.translator.bedrock; +import com.github.steveice10.mc.protocol.data.game.entity.metadata.ItemStack; import com.github.steveice10.mc.protocol.data.game.entity.metadata.Position; import com.github.steveice10.mc.protocol.data.game.entity.player.GameMode; import com.github.steveice10.mc.protocol.data.game.entity.player.Hand; import com.github.steveice10.mc.protocol.data.game.entity.player.InteractAction; import com.github.steveice10.mc.protocol.data.game.entity.player.PlayerAction; +import com.github.steveice10.mc.protocol.data.game.window.ClickItemParam; +import com.github.steveice10.mc.protocol.data.game.window.WindowAction; import com.github.steveice10.mc.protocol.data.game.world.block.BlockFace; import com.github.steveice10.mc.protocol.packet.ingame.client.player.ClientPlayerActionPacket; import com.github.steveice10.mc.protocol.packet.ingame.client.player.ClientPlayerInteractEntityPacket; import com.github.steveice10.mc.protocol.packet.ingame.client.player.ClientPlayerUseItemPacket; import com.github.steveice10.mc.protocol.packet.ingame.client.window.ClientCreativeInventoryActionPacket; +import com.github.steveice10.mc.protocol.packet.ingame.client.window.ClientRenameItemPacket; +import com.github.steveice10.mc.protocol.packet.ingame.client.window.ClientWindowActionPacket; import com.nukkitx.math.vector.Vector3f; +import com.nukkitx.protocol.bedrock.data.ContainerId; import com.nukkitx.protocol.bedrock.data.InventoryActionData; import com.nukkitx.protocol.bedrock.data.InventorySource; +import com.nukkitx.protocol.bedrock.data.ItemData; import com.nukkitx.protocol.bedrock.packet.InventoryTransactionPacket; import lombok.extern.log4j.Log4j2; import org.dragonet.proxy.network.session.ProxySession; import org.dragonet.proxy.network.session.cache.object.CachedEntity; +import org.dragonet.proxy.network.session.cache.object.CachedWindow; import org.dragonet.proxy.network.translator.ItemTranslatorRegistry; import org.dragonet.proxy.network.translator.misc.PacketTranslator; -import org.dragonet.proxy.util.registry.PacketRegisterInfo; +import org.dragonet.proxy.network.translator.misc.inventory.action.DropItemAction; +import org.dragonet.proxy.network.translator.misc.inventory.action.IInventoryAction; +import org.dragonet.proxy.network.translator.misc.inventory.action.SlotChangeAction; import org.dragonet.proxy.util.TextFormat; +import org.dragonet.proxy.util.registry.PacketRegisterInfo; @Log4j2 @PacketRegisterInfo(packet = InventoryTransactionPacket.class) @@ -55,7 +66,7 @@ public void translate(ProxySession session, InventoryTransactionPacket packet) { switch(source.getType()) { case WORLD_INTERACTION: - session.sendRemotePacket(new ClientPlayerActionPacket(PlayerAction.DROP_ITEM, new Position(0, 0, 0), BlockFace.UP)); + executeAction(session, new DropItemAction(action)); break; case CREATIVE: switch(action.getSlot()) { @@ -68,6 +79,36 @@ public void translate(ProxySession session, InventoryTransactionPacket packet) { break; } break; + case CONTAINER: + CachedWindow inventory = session.getWindowCache().getById(source.getContainerId()); + if(inventory == null) { + log.warn("inventory translator: inventory is null"); + return; + } + + // TODO + executeAction(session, new SlotChangeAction(inventory, action, WindowAction.CLICK_ITEM, ClickItemParam.LEFT_CLICK)); + break; + case NON_IMPLEMENTED_TODO: + if(source.getContainerId() >= ContainerId.ANVIL_OUTPUT && source.getContainerId() <= -10 /* anvil input */) { + if (source.getContainerId() == ContainerId.ANVIL_RESULT) { + ItemData item = action.getFromItem(); + String name = item.getTag() != null ? item.getTag().getCompound("display").getString("Name") : ""; + + session.sendRemotePacket(new ClientRenameItemPacket(name)); + } + } + + if(source.getContainerId() == ContainerId.BEACON) { + CachedWindow beacon = session.getWindowCache().getById(source.getContainerId()); + if(beacon == null) { + log.warn("inventory translator: beacon inventory is null"); + return; + } + + executeAction(session, new SlotChangeAction(beacon, action, WindowAction.CLICK_ITEM, ClickItemParam.LEFT_CLICK)); + } + break; } } break; @@ -111,4 +152,13 @@ public void translate(ProxySession session, InventoryTransactionPacket packet) { break; } } + + private void executeAction(ProxySession session, IInventoryAction action) { + if(action.isValid(session) && action.execute(session)) { + action.onSuccess(session); + } else { + log.info("inventory action failed: " + action.getClass().getSimpleName()); + action.onFail(session); + } + } } diff --git a/proxy/src/main/java/org/dragonet/proxy/network/translator/bedrock/player/PEPlayerActionTranslator.java b/proxy/src/main/java/org/dragonet/proxy/network/translator/bedrock/player/PEPlayerActionTranslator.java index 9da7a667f..081b9861c 100644 --- a/proxy/src/main/java/org/dragonet/proxy/network/translator/bedrock/player/PEPlayerActionTranslator.java +++ b/proxy/src/main/java/org/dragonet/proxy/network/translator/bedrock/player/PEPlayerActionTranslator.java @@ -96,9 +96,9 @@ public void translate(ProxySession session, PlayerActionPacket packet) { // Open command block window, as its handled client side on Java edition if(session.getChunkCache().getBlockAt(packet.getBlockPosition()) == BlockTranslator.BEDROCK_COMMAND_BLOCK_ID) { - CachedWindow cachedWindow = session.getWindowCache().newWindow(BedrockWindowType.COMMAND_BLOCK, session.getWindowCache().getLocalWindowIdAllocator().getAndIncrement()); - cachedWindow.setName("Command Block"); - cachedWindow.open(session); +// CachedWindow cachedWindow = session.getWindowCache().newWindow(session.getWindowCache().getLocalWindowIdAllocator().getAndIncrement()); +// cachedWindow.setName("Command Block"); +// cachedWindow.open(session); return; } diff --git a/proxy/src/main/java/org/dragonet/proxy/network/translator/java/window/PCConfirmTransactionTranslator.java b/proxy/src/main/java/org/dragonet/proxy/network/translator/java/window/PCConfirmTransactionTranslator.java index 179b75d3d..6b1eeeb29 100644 --- a/proxy/src/main/java/org/dragonet/proxy/network/translator/java/window/PCConfirmTransactionTranslator.java +++ b/proxy/src/main/java/org/dragonet/proxy/network/translator/java/window/PCConfirmTransactionTranslator.java @@ -22,6 +22,7 @@ import com.github.steveice10.mc.protocol.packet.ingame.server.window.ServerConfirmTransactionPacket; import lombok.extern.log4j.Log4j2; import org.dragonet.proxy.network.session.ProxySession; +import org.dragonet.proxy.network.session.cache.object.CachedWindow; import org.dragonet.proxy.network.translator.misc.PacketTranslator; import org.dragonet.proxy.util.registry.PacketRegisterInfo; @@ -32,6 +33,15 @@ public class PCConfirmTransactionTranslator extends PacketTranslator { - private static final Map windowMap = new HashMap<>(); + private static final Map windowMap = new HashMap<>(); static { - windowMap.put(WindowType.ANVIL, BedrockWindowType.ANVIL); - windowMap.put(WindowType.BEACON, BedrockWindowType.BEACON); - windowMap.put(WindowType.ENCHANTMENT, BedrockWindowType.ENCHANT_TABLE); - windowMap.put(WindowType.BREWING_STAND, BedrockWindowType.BREWING_STAND); - windowMap.put(WindowType.FURNACE, BedrockWindowType.FURNACE); - windowMap.put(WindowType.HOPPER, BedrockWindowType.HOPPER); - windowMap.put(WindowType.CRAFTING, BedrockWindowType.CRAFTING_TABLE); - //windowMap.put(WindowType.MERCHANT, BedrockWindowType.TRADING); - windowMap.put(WindowType.BLAST_FURNACE, BedrockWindowType.BLAST_FURNACE); - windowMap.put(WindowType.SMOKER, BedrockWindowType.SMOKER); - windowMap.put(WindowType.STONECUTTER, BedrockWindowType.STONECUTTER); - // TODO: chest style inventories + windowMap.put(WindowType.GENERIC_9X1, new SingleChestInventoryTranslator(27, 1)); + windowMap.put(WindowType.GENERIC_9X2, new SingleChestInventoryTranslator(27, 2)); + windowMap.put(WindowType.GENERIC_9X3, new SingleChestInventoryTranslator(27, 3)); + windowMap.put(WindowType.GENERIC_9X4, new DoubleChestInventoryTranslator(54, 4)); + windowMap.put(WindowType.GENERIC_9X5, new DoubleChestInventoryTranslator(54, 5)); + windowMap.put(WindowType.GENERIC_9X6, new DoubleChestInventoryTranslator(54, 6)); + windowMap.put(WindowType.SHULKER_BOX, new SingleChestInventoryTranslator(BedrockWindowType.SHULKER_BOX, 27, 3)); + windowMap.put(WindowType.GENERIC_3X3, new DispenserInventoryTranslator(9)); + windowMap.put(WindowType.ANVIL, new AnvilInventoryTranslator(3)); + windowMap.put(WindowType.FURNACE, new FurnaceInventoryTranslator(3)); + windowMap.put(WindowType.BEACON, new BeaconInventoryTranslator(1)); } @Override public void translate(ProxySession session, ServerOpenWindowPacket packet) { - BedrockWindowType bedrockWindowType = windowMap.get(packet.getType()); - if(bedrockWindowType == null) { - log.info(TextFormat.GRAY + "(debug) Unhandled window type: " + packet.getType().name() + TextFormat.AQUA + " It is not supported yet."); + IInventoryTranslator bedrockWindowTranslator = windowMap.get(packet.getType()); + if(bedrockWindowTranslator == null) { + log.info(TextFormat.GRAY + "(debug) Unhandled window type: " + packet.getType().name() + ". It is not supported yet."); + + // Close the window + session.sendRemotePacket(new ClientCloseWindowPacket(packet.getWindowId())); return; } - //log.warn("WINDOW: " + packet.getWindowId() + " - " + packet.getType().name()); - - CachedWindow cachedWindow = session.getWindowCache().newWindow(bedrockWindowType, packet.getWindowId()); + CachedWindow cachedWindow = session.getWindowCache().newWindow(bedrockWindowTranslator, packet.getWindowId()); cachedWindow.setName(MessageTranslator.translate(packet.getName())); cachedWindow.open(session); diff --git a/proxy/src/main/java/org/dragonet/proxy/network/translator/java/window/PCSetSlotTranslator.java b/proxy/src/main/java/org/dragonet/proxy/network/translator/java/window/PCSetSlotTranslator.java index ac264c7e6..fc4977c63 100644 --- a/proxy/src/main/java/org/dragonet/proxy/network/translator/java/window/PCSetSlotTranslator.java +++ b/proxy/src/main/java/org/dragonet/proxy/network/translator/java/window/PCSetSlotTranslator.java @@ -20,14 +20,13 @@ import com.github.steveice10.mc.protocol.data.game.entity.metadata.ItemStack; import com.github.steveice10.mc.protocol.packet.ingame.server.window.ServerSetSlotPacket; +import com.nukkitx.protocol.bedrock.data.ContainerId; import lombok.extern.log4j.Log4j2; import org.dragonet.proxy.network.session.ProxySession; import org.dragonet.proxy.network.session.cache.WindowCache; import org.dragonet.proxy.network.session.cache.object.CachedWindow; import org.dragonet.proxy.network.translator.misc.PacketTranslator; import org.dragonet.proxy.util.registry.PacketRegisterInfo; -import org.dragonet.proxy.network.translator.misc.InventoryTranslator; - @Log4j2 @PacketRegisterInfo(packet = ServerSetSlotPacket.class) @@ -41,7 +40,6 @@ public void translate(ProxySession session, ServerSetSlotPacket packet) { return; } CachedWindow window = windowCache.getWindows().get(packet.getWindowId()); -// log.warn("Set slot translator: " + packet.getWindowId()); if(packet.getWindowId() != 0 && window.getWindowType() == null) { return; } @@ -54,13 +52,14 @@ public void translate(ProxySession session, ServerSetSlotPacket packet) { items[packet.getSlot()] = packet.getItem(); window.setItems(items); - InventoryTranslator.sendPlayerInventory(session); + //InventoryTranslator.sendPlayerInventory(session); } - if(window.isOpen()) { - // update slot + if(window.isOpen() || window.getWindowId() == ContainerId.INVENTORY) { + window.getItems()[packet.getSlot()] = packet.getItem(); + window.sendSlot(session, packet.getSlot()); } else { - // cache packet + log.warn("tried to set slot of closed inventory"); } } } diff --git a/proxy/src/main/java/org/dragonet/proxy/network/translator/java/window/PCWindowItemsTranslator.java b/proxy/src/main/java/org/dragonet/proxy/network/translator/java/window/PCWindowItemsTranslator.java index f284ee789..a9941caee 100644 --- a/proxy/src/main/java/org/dragonet/proxy/network/translator/java/window/PCWindowItemsTranslator.java +++ b/proxy/src/main/java/org/dragonet/proxy/network/translator/java/window/PCWindowItemsTranslator.java @@ -19,14 +19,13 @@ package org.dragonet.proxy.network.translator.java.window; import com.github.steveice10.mc.protocol.packet.ingame.server.window.ServerWindowItemsPacket; +import com.nukkitx.protocol.bedrock.data.ContainerId; import lombok.extern.log4j.Log4j2; import org.dragonet.proxy.network.session.ProxySession; import org.dragonet.proxy.network.session.cache.WindowCache; import org.dragonet.proxy.network.session.cache.object.CachedWindow; import org.dragonet.proxy.network.translator.misc.PacketTranslator; import org.dragonet.proxy.util.registry.PacketRegisterInfo; -import org.dragonet.proxy.network.translator.misc.InventoryTranslator; - @Log4j2 @PacketRegisterInfo(packet = ServerWindowItemsPacket.class) @@ -46,12 +45,11 @@ public void translate(ProxySession session, ServerWindowItemsPacket packet) { return; } - if(packet.getWindowId() == 0) { - if(packet.getItems().length < 40) { - return; // TODO: check this? - } + if(window.isOpen() || window.getWindowId() == ContainerId.INVENTORY) { window.setItems(packet.getItems()); - InventoryTranslator.sendPlayerInventory(session); + window.sendInventory(session); + } else { + log.warn("tried to set items of closed inventory"); } } } diff --git a/proxy/src/main/java/org/dragonet/proxy/network/translator/misc/InventoryTranslator.java b/proxy/src/main/java/org/dragonet/proxy/network/translator/misc/InventoryTranslator.java index 82d4f0662..e1cb7cdc3 100644 --- a/proxy/src/main/java/org/dragonet/proxy/network/translator/misc/InventoryTranslator.java +++ b/proxy/src/main/java/org/dragonet/proxy/network/translator/misc/InventoryTranslator.java @@ -18,63 +18,6 @@ */ package org.dragonet.proxy.network.translator.misc; -import com.github.steveice10.mc.protocol.data.game.window.WindowType; -import com.github.steveice10.mc.protocol.packet.ingame.server.window.ServerSetSlotPacket; -import com.nukkitx.protocol.bedrock.data.ContainerId; -import com.nukkitx.protocol.bedrock.data.ItemData; -import com.nukkitx.protocol.bedrock.packet.InventoryContentPacket; -import org.dragonet.proxy.network.session.ProxySession; -import org.dragonet.proxy.network.session.cache.object.CachedWindow; -import org.dragonet.proxy.network.translator.ItemTranslatorRegistry; -import org.dragonet.proxy.network.translator.misc.inventory.GenericInventoryTranslator; - -import java.util.HashMap; -import java.util.Map; - public class InventoryTranslator { - public static final Map TRANSLATORS = new HashMap<>(); - - static { - TRANSLATORS.put(WindowType.GENERIC_9X1, new GenericInventoryTranslator()); - TRANSLATORS.put(WindowType.GENERIC_9X2, new GenericInventoryTranslator()); - TRANSLATORS.put(WindowType.GENERIC_9X3, new GenericInventoryTranslator()); - TRANSLATORS.put(WindowType.GENERIC_9X4, new GenericInventoryTranslator()); - TRANSLATORS.put(WindowType.GENERIC_9X5, new GenericInventoryTranslator()); - TRANSLATORS.put(WindowType.GENERIC_9X6, new GenericInventoryTranslator()); - } - - /** - * Sends the items from the players cached inventory to the client - */ - public static void sendPlayerInventory(ProxySession session) { - CachedWindow cachedWindow = session.getWindowCache().getPlayerInventory(); - - InventoryContentPacket inventoryContentPacket = new InventoryContentPacket(); - inventoryContentPacket.setContainerId(ContainerId.INVENTORY); - - ItemData[] contents = new ItemData[40]; - - // Hotbar - for(int i = 36; i < 45; i++) { - contents[i - 36] = ItemTranslatorRegistry.translateToBedrock(cachedWindow.getItems()[i]); - } - - // Inventory - for(int i = 9; i < 36; i++) { - contents[i] = ItemTranslatorRegistry.translateToBedrock(cachedWindow.getItems()[i]); - } - - // Armour - for(int i = 5; i < 9; i++) { - contents[i + 31] = ItemTranslatorRegistry.translateToBedrock(cachedWindow.getItems()[i]); - } - - inventoryContentPacket.setContents(contents); - // TODO: fixes issues on cubecraft and hypixel with crashing clients. Need to investate. - session.sendPacket(inventoryContentPacket); - } - - public static void updateSlot(ProxySession session, ServerSetSlotPacket packet) { - - } + } diff --git a/proxy/src/main/java/org/dragonet/proxy/network/translator/misc/inventory/AnvilInventoryTranslator.java b/proxy/src/main/java/org/dragonet/proxy/network/translator/misc/inventory/AnvilInventoryTranslator.java new file mode 100644 index 000000000..5b7514640 --- /dev/null +++ b/proxy/src/main/java/org/dragonet/proxy/network/translator/misc/inventory/AnvilInventoryTranslator.java @@ -0,0 +1,27 @@ +package org.dragonet.proxy.network.translator.misc.inventory; + +import com.nukkitx.protocol.bedrock.data.ContainerId; +import org.dragonet.proxy.data.window.BedrockWindowType; +import org.dragonet.proxy.network.session.ProxySession; +import org.dragonet.proxy.network.session.cache.object.CachedWindow; + +public class AnvilInventoryTranslator extends ContainerInventoryTranslator { + + public AnvilInventoryTranslator(int size) { + super(BedrockWindowType.ANVIL, size); + + slotMappings.put(1, 0); + slotMappings.put(2, 1); + slotMappings.put(50, 2); + } + + @Override + public void updateInventory(ProxySession session, CachedWindow window) { + + } + + @Override + public void updateSlot(ProxySession session, CachedWindow window, int slot) { + + } +} diff --git a/proxy/src/main/java/org/dragonet/proxy/network/translator/misc/inventory/BeaconInventoryTranslator.java b/proxy/src/main/java/org/dragonet/proxy/network/translator/misc/inventory/BeaconInventoryTranslator.java new file mode 100644 index 000000000..543f8ffba --- /dev/null +++ b/proxy/src/main/java/org/dragonet/proxy/network/translator/misc/inventory/BeaconInventoryTranslator.java @@ -0,0 +1,32 @@ +package org.dragonet.proxy.network.translator.misc.inventory; + +import com.nukkitx.protocol.bedrock.data.ContainerId; +import com.nukkitx.protocol.bedrock.data.ItemData; +import com.nukkitx.protocol.bedrock.packet.InventoryContentPacket; +import com.nukkitx.protocol.bedrock.packet.InventorySlotPacket; +import org.dragonet.proxy.data.window.BedrockWindowType; +import org.dragonet.proxy.network.session.ProxySession; +import org.dragonet.proxy.network.session.cache.object.CachedWindow; + +public class BeaconInventoryTranslator extends ContainerInventoryTranslator { + + public BeaconInventoryTranslator(int size) { + super(BedrockWindowType.BEACON, size); + } + + @Override + public void updateInventory(ProxySession session, CachedWindow window) { + ItemData[] bedrockItems = new ItemData[size]; + + + InventoryContentPacket contentPacket = new InventoryContentPacket(); + contentPacket.setContainerId(window.getWindowId()); + contentPacket.setContents(bedrockItems); + session.sendPacket(contentPacket); + } + + @Override + public boolean isSlotValid(int slot) { + return true; // TODO + } +} diff --git a/proxy/src/main/java/org/dragonet/proxy/network/translator/misc/inventory/ContainerInventoryTranslator.java b/proxy/src/main/java/org/dragonet/proxy/network/translator/misc/inventory/ContainerInventoryTranslator.java new file mode 100644 index 000000000..3a42cf071 --- /dev/null +++ b/proxy/src/main/java/org/dragonet/proxy/network/translator/misc/inventory/ContainerInventoryTranslator.java @@ -0,0 +1,38 @@ +package org.dragonet.proxy.network.translator.misc.inventory; + +import com.nukkitx.protocol.bedrock.data.ItemData; +import com.nukkitx.protocol.bedrock.packet.InventoryContentPacket; +import com.nukkitx.protocol.bedrock.packet.InventorySlotPacket; +import org.dragonet.proxy.data.window.BedrockWindowType; +import org.dragonet.proxy.network.session.ProxySession; +import org.dragonet.proxy.network.session.cache.object.CachedWindow; + +public class ContainerInventoryTranslator extends IInventoryTranslator { + + public ContainerInventoryTranslator(BedrockWindowType windowType, int size) { + super(windowType, size); + } + + @Override + public void updateInventory(ProxySession session, CachedWindow window) { + ItemData[] bedrockItems = new ItemData[size]; + + for (int i = 0; i < bedrockItems.length; i++) { + bedrockItems[i] = window.getItem(i); + } + + InventoryContentPacket contentPacket = new InventoryContentPacket(); + contentPacket.setContainerId(window.getWindowId()); + contentPacket.setContents(bedrockItems); + session.sendPacket(contentPacket); + } + + @Override + public void updateSlot(ProxySession session, CachedWindow window, int slot) { + InventorySlotPacket inventorySlotPacket = new InventorySlotPacket(); + inventorySlotPacket.setContainerId(window.getWindowId()); + inventorySlotPacket.setItem(window.getItem(slot)); + inventorySlotPacket.setSlot(slot); + session.sendPacket(inventorySlotPacket); + } +} diff --git a/proxy/src/main/java/org/dragonet/proxy/network/translator/misc/inventory/DispenserInventoryTranslator.java b/proxy/src/main/java/org/dragonet/proxy/network/translator/misc/inventory/DispenserInventoryTranslator.java new file mode 100644 index 000000000..c8d53d60b --- /dev/null +++ b/proxy/src/main/java/org/dragonet/proxy/network/translator/misc/inventory/DispenserInventoryTranslator.java @@ -0,0 +1,10 @@ +package org.dragonet.proxy.network.translator.misc.inventory; + +import org.dragonet.proxy.data.window.BedrockWindowType; + +public class DispenserInventoryTranslator extends ContainerInventoryTranslator { + + public DispenserInventoryTranslator(int size) { + super(BedrockWindowType.DISPENSER, size); + } +} diff --git a/proxy/src/main/java/org/dragonet/proxy/network/translator/misc/inventory/DoubleChestInventoryTranslator.java b/proxy/src/main/java/org/dragonet/proxy/network/translator/misc/inventory/DoubleChestInventoryTranslator.java new file mode 100644 index 000000000..6b674e9a6 --- /dev/null +++ b/proxy/src/main/java/org/dragonet/proxy/network/translator/misc/inventory/DoubleChestInventoryTranslator.java @@ -0,0 +1,10 @@ +package org.dragonet.proxy.network.translator.misc.inventory; + +import org.dragonet.proxy.data.window.BedrockWindowType; + +public class DoubleChestInventoryTranslator extends SingleChestInventoryTranslator { + + public DoubleChestInventoryTranslator(int size, int rows) { + super(BedrockWindowType.DOUBLE_CHEST, size, rows); + } +} diff --git a/proxy/src/main/java/org/dragonet/proxy/network/translator/misc/inventory/FurnaceInventoryTranslator.java b/proxy/src/main/java/org/dragonet/proxy/network/translator/misc/inventory/FurnaceInventoryTranslator.java new file mode 100644 index 000000000..6b548b047 --- /dev/null +++ b/proxy/src/main/java/org/dragonet/proxy/network/translator/misc/inventory/FurnaceInventoryTranslator.java @@ -0,0 +1,22 @@ +package org.dragonet.proxy.network.translator.misc.inventory; + +import org.dragonet.proxy.data.window.BedrockWindowType; +import org.dragonet.proxy.network.session.ProxySession; +import org.dragonet.proxy.network.session.cache.object.CachedWindow; + +public class FurnaceInventoryTranslator extends ContainerInventoryTranslator { + + public FurnaceInventoryTranslator(int size) { + super(BedrockWindowType.FURNACE, size); + } + + @Override + public void updateInventory(ProxySession session, CachedWindow window) { + + } + + @Override + public void updateSlot(ProxySession session, CachedWindow window, int slot) { + + } +} diff --git a/proxy/src/main/java/org/dragonet/proxy/network/translator/misc/inventory/GenericInventoryTranslator.java b/proxy/src/main/java/org/dragonet/proxy/network/translator/misc/inventory/GenericInventoryTranslator.java deleted file mode 100644 index 9f6d38fc4..000000000 --- a/proxy/src/main/java/org/dragonet/proxy/network/translator/misc/inventory/GenericInventoryTranslator.java +++ /dev/null @@ -1,22 +0,0 @@ -/* - * DragonProxy - * Copyright (C) 2016-2020 Dragonet Foundation - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You can view the LICENSE file for more details. - * - * https://github.com/DragonetMC/DragonProxy - */ -package org.dragonet.proxy.network.translator.misc.inventory; - -public class GenericInventoryTranslator { -} diff --git a/proxy/src/main/java/org/dragonet/proxy/network/translator/misc/inventory/IInventoryTranslator.java b/proxy/src/main/java/org/dragonet/proxy/network/translator/misc/inventory/IInventoryTranslator.java new file mode 100644 index 000000000..aa5156841 --- /dev/null +++ b/proxy/src/main/java/org/dragonet/proxy/network/translator/misc/inventory/IInventoryTranslator.java @@ -0,0 +1,42 @@ +package org.dragonet.proxy.network.translator.misc.inventory; + +import com.google.common.collect.BiMap; +import com.google.common.collect.HashBiMap; +import com.nukkitx.nbt.CompoundTagBuilder; +import com.nukkitx.protocol.bedrock.data.InventoryActionData; +import com.nukkitx.protocol.bedrock.data.ItemData; +import lombok.Getter; +import lombok.RequiredArgsConstructor; +import org.dragonet.proxy.data.window.BedrockWindowType; +import org.dragonet.proxy.network.session.ProxySession; +import org.dragonet.proxy.network.session.cache.object.CachedWindow; + +@Getter +@RequiredArgsConstructor +public abstract class IInventoryTranslator { + protected static ItemData UNUSABLE_INVENTORY_SPACE_BLOCK; + + protected final BedrockWindowType bedrockWindowType; + protected final int size; + + protected BiMap slotMappings = HashBiMap.create(); + + static { + CompoundTagBuilder root = CompoundTagBuilder.builder(); + CompoundTagBuilder display = CompoundTagBuilder.builder(); + + display.stringTag("Name", "Unusuable inventory space"); + root.tag(display.build("display")); + + // Barrier block (248 update block) + UNUSABLE_INVENTORY_SPACE_BLOCK = ItemData.of(-161, (short) 0, 1, root.buildRootTag()); + } + + public abstract void updateInventory(ProxySession session, CachedWindow window); + + public abstract void updateSlot(ProxySession session, CachedWindow window, int slot); + + public boolean isSlotValid(int slot) { + return true; + } +} diff --git a/proxy/src/main/java/org/dragonet/proxy/network/translator/misc/inventory/PlayerInventoryTranslator.java b/proxy/src/main/java/org/dragonet/proxy/network/translator/misc/inventory/PlayerInventoryTranslator.java new file mode 100644 index 000000000..4495ec9b9 --- /dev/null +++ b/proxy/src/main/java/org/dragonet/proxy/network/translator/misc/inventory/PlayerInventoryTranslator.java @@ -0,0 +1,90 @@ +package org.dragonet.proxy.network.translator.misc.inventory; + +import com.github.steveice10.mc.protocol.data.game.entity.player.GameMode; +import com.nukkitx.protocol.bedrock.data.ContainerId; +import com.nukkitx.protocol.bedrock.data.ItemData; +import com.nukkitx.protocol.bedrock.packet.InventoryContentPacket; +import com.nukkitx.protocol.bedrock.packet.InventorySlotPacket; +import org.dragonet.proxy.data.window.BedrockWindowType; +import org.dragonet.proxy.network.session.ProxySession; +import org.dragonet.proxy.network.session.cache.object.CachedWindow; +import org.dragonet.proxy.network.translator.ItemTranslatorRegistry; + +public class PlayerInventoryTranslator extends IInventoryTranslator { + + public PlayerInventoryTranslator(int size) { + super(BedrockWindowType.CHEST, size); // TODO: add a player window type, but im lazy + } + + @Override + public void updateInventory(ProxySession session, CachedWindow window) { + sendInventory(session, window); + sendArmor(session, window); + sendOffhand(session, window); + sendCrafting(session, window); + } + + @Override + public void updateSlot(ProxySession session, CachedWindow window, int slot) { + updateInventory(session, window); // TODO: Dont send entire inventory + } + + public void sendInventory(ProxySession session, CachedWindow window) { + InventoryContentPacket inventoryContentPacket = new InventoryContentPacket(); + inventoryContentPacket.setContainerId(ContainerId.INVENTORY); + ItemData[] contents = new ItemData[36]; + + // Hotbar + for(int i = 36; i < 45; i++) { + contents[i - 36] = window.getItem(i); + } + + // Inventory + for(int i = 9; i < 36; i++) { + if(session.getCachedEntity().getGameMode() == GameMode.SPECTATOR) { + contents[i] = UNUSABLE_INVENTORY_SPACE_BLOCK; + } else { + contents[i] = window.getItem(i); + } + } + + inventoryContentPacket.setContents(contents); + session.sendPacket(inventoryContentPacket); + } + + public void sendArmor(ProxySession session, CachedWindow window) { + InventoryContentPacket inventoryContentPacket = new InventoryContentPacket(); + inventoryContentPacket.setContainerId(ContainerId.ARMOR); + ItemData[] contents = new ItemData[4]; + + for(int i = 5; i < 9; i++) { + contents[i - 5] = window.getItem(i); + } + + inventoryContentPacket.setContents(contents); + session.sendPacket(inventoryContentPacket); + } + + public void sendOffhand(ProxySession session, CachedWindow window) { + InventoryContentPacket inventoryContentPacket = new InventoryContentPacket(); + inventoryContentPacket.setContainerId(ContainerId.OFFHAND); + inventoryContentPacket.setContents(new ItemData[]{window.getItem(45)}); + session.sendPacket(inventoryContentPacket); + } + + public void sendCrafting(ProxySession session, CachedWindow window) { + for(int i = 0; i < 5; i++) { + InventorySlotPacket inventorySlotPacket = new InventorySlotPacket(); + inventorySlotPacket.setContainerId(ContainerId.CURSOR); + inventorySlotPacket.setSlot(i + 27); + + if(session.getCachedEntity().getGameMode() == GameMode.CREATIVE || session.getCachedEntity().getGameMode() == GameMode.SPECTATOR) { + inventorySlotPacket.setItem(UNUSABLE_INVENTORY_SPACE_BLOCK); + } else { + inventorySlotPacket.setItem(window.getItem(i)); + } + session.sendPacket(inventorySlotPacket); + } + } + +} diff --git a/proxy/src/main/java/org/dragonet/proxy/network/translator/misc/inventory/SingleChestInventoryTranslator.java b/proxy/src/main/java/org/dragonet/proxy/network/translator/misc/inventory/SingleChestInventoryTranslator.java new file mode 100644 index 000000000..bd1958007 --- /dev/null +++ b/proxy/src/main/java/org/dragonet/proxy/network/translator/misc/inventory/SingleChestInventoryTranslator.java @@ -0,0 +1,50 @@ +package org.dragonet.proxy.network.translator.misc.inventory; + +import com.github.steveice10.mc.protocol.data.game.entity.metadata.ItemStack; +import com.nukkitx.nbt.CompoundTagBuilder; +import com.nukkitx.protocol.bedrock.data.ContainerId; +import com.nukkitx.protocol.bedrock.data.ItemData; +import com.nukkitx.protocol.bedrock.packet.InventoryContentPacket; +import com.nukkitx.protocol.bedrock.packet.InventorySlotPacket; +import lombok.RequiredArgsConstructor; +import lombok.extern.log4j.Log4j2; +import org.dragonet.proxy.data.window.BedrockWindowType; +import org.dragonet.proxy.network.session.ProxySession; +import org.dragonet.proxy.network.session.cache.object.CachedWindow; +@Log4j2 +public class SingleChestInventoryTranslator extends ContainerInventoryTranslator { + protected final int rows; + + public SingleChestInventoryTranslator(int size, int rows) { + this(BedrockWindowType.CHEST, size, rows); + } + + public SingleChestInventoryTranslator(BedrockWindowType windowType, int size, int rows) { + super(windowType, size); + this.rows = rows; + } + + @Override + public void updateInventory(ProxySession session, CachedWindow window) { + ItemData[] bedrockItems = new ItemData[size]; + int length = (9 * rows); + + for (int i = 0; i < bedrockItems.length; i++) { + if(i < length) { + bedrockItems[i] = window.getItem(i); + } else { + bedrockItems[i] = UNUSABLE_INVENTORY_SPACE_BLOCK; + } + } + + InventoryContentPacket contentPacket = new InventoryContentPacket(); + contentPacket.setContainerId(window.getWindowId()); + contentPacket.setContents(bedrockItems); + session.sendPacket(contentPacket); + } + + @Override + public boolean isSlotValid(int slot) { + return slot < (9 * rows); + } +} diff --git a/proxy/src/main/java/org/dragonet/proxy/network/translator/misc/inventory/action/DropItemAction.java b/proxy/src/main/java/org/dragonet/proxy/network/translator/misc/inventory/action/DropItemAction.java new file mode 100644 index 000000000..888f47437 --- /dev/null +++ b/proxy/src/main/java/org/dragonet/proxy/network/translator/misc/inventory/action/DropItemAction.java @@ -0,0 +1,48 @@ +package org.dragonet.proxy.network.translator.misc.inventory.action; + +import com.github.steveice10.mc.protocol.data.game.entity.metadata.Position; +import com.github.steveice10.mc.protocol.data.game.entity.player.PlayerAction; +import com.github.steveice10.mc.protocol.data.game.world.block.BlockFace; +import com.github.steveice10.mc.protocol.packet.ingame.client.player.ClientPlayerActionPacket; +import com.nukkitx.protocol.bedrock.data.InventoryActionData; +import lombok.Getter; +import lombok.RequiredArgsConstructor; +import lombok.extern.log4j.Log4j2; +import org.dragonet.proxy.network.session.ProxySession; +import org.dragonet.proxy.network.session.cache.object.CachedWindow; + +@Getter +@RequiredArgsConstructor +@Log4j2 +public class DropItemAction implements IInventoryAction { + private final InventoryActionData actionData; + + @Override + public boolean isValid(ProxySession session) { + return actionData.getFromItem() != null; + } + + @Override + public boolean execute(ProxySession session) { + CachedWindow inventory = session.getWindowCache().getPlayerInventory(); + + return inventory.setItem(actionData.getSlot(), actionData.getFromItem()); + } + + @Override + public void onSuccess(ProxySession session) { + CachedWindow inventory = session.getWindowCache().getPlayerInventory(); + inventory.sendSlot(session, actionData.getSlot()); + + PlayerAction actionType = PlayerAction.DROP_ITEM; + session.sendRemotePacket(new ClientPlayerActionPacket(actionType, new Position(0, 0, 0), BlockFace.UP)); + } + + @Override + public void onFail(ProxySession session) { + CachedWindow inventory = session.getWindowCache().getPlayerInventory(); + + inventory.setItem(actionData.getSlot(), actionData.getToItem()); + inventory.sendSlot(session, actionData.getSlot()); + } +} diff --git a/proxy/src/main/java/org/dragonet/proxy/network/translator/misc/inventory/action/IInventoryAction.java b/proxy/src/main/java/org/dragonet/proxy/network/translator/misc/inventory/action/IInventoryAction.java new file mode 100644 index 000000000..d99aadb72 --- /dev/null +++ b/proxy/src/main/java/org/dragonet/proxy/network/translator/misc/inventory/action/IInventoryAction.java @@ -0,0 +1,14 @@ +package org.dragonet.proxy.network.translator.misc.inventory.action; + +import org.dragonet.proxy.network.session.ProxySession; + +public interface IInventoryAction { + + boolean isValid(ProxySession session); + + boolean execute(ProxySession session); + + void onSuccess(ProxySession session); + + void onFail(ProxySession session); +} diff --git a/proxy/src/main/java/org/dragonet/proxy/network/translator/misc/inventory/action/SlotChangeAction.java b/proxy/src/main/java/org/dragonet/proxy/network/translator/misc/inventory/action/SlotChangeAction.java new file mode 100644 index 000000000..16f89b257 --- /dev/null +++ b/proxy/src/main/java/org/dragonet/proxy/network/translator/misc/inventory/action/SlotChangeAction.java @@ -0,0 +1,49 @@ +package org.dragonet.proxy.network.translator.misc.inventory.action; + +import com.github.steveice10.mc.protocol.data.game.window.WindowAction; +import com.github.steveice10.mc.protocol.data.game.window.WindowActionParam; +import com.github.steveice10.mc.protocol.packet.ingame.client.window.ClientWindowActionPacket; +import com.nukkitx.protocol.bedrock.data.InventoryActionData; +import lombok.Getter; +import lombok.RequiredArgsConstructor; +import org.dragonet.proxy.network.session.ProxySession; +import org.dragonet.proxy.network.session.cache.object.CachedWindow; +import org.dragonet.proxy.network.translator.ItemTranslatorRegistry; + +@Getter +@RequiredArgsConstructor +public class SlotChangeAction implements IInventoryAction { + private final CachedWindow inventory; + private final InventoryActionData actionData; + private final WindowAction windowAction; + private final WindowActionParam windowActionParam; + + @Override + public boolean isValid(ProxySession session) { + return inventory.getItem(actionData.getSlot()) != null; //&& inventory.getItem(actionData.getSlot()).getId() == actionData.getFromItem().getId() + // && inventory.getInventoryTranslator().isSlotValid(actionData.getSlot()); + } + + @Override + public boolean execute(ProxySession session) { + return inventory.setItem(actionData.getSlot(), actionData.getToItem()); + } + + @Override + public void onSuccess(ProxySession session) { + inventory.sendSlot(session, actionData.getSlot()); + + int transactionId = inventory.getTransactionIdCounter().getAndIncrement(); + + session.sendRemotePacket(new ClientWindowActionPacket(inventory.getWindowId(), transactionId, + actionData.getSlot(), ItemTranslatorRegistry.translateToJava(actionData.getToItem()), windowAction, windowActionParam)); + + inventory.getTransactions().put(transactionId, true); + } + + @Override + public void onFail(ProxySession session) { + inventory.sendInventory(session); + inventory.getTransactions().put(inventory.getTransactionIdCounter().getAndIncrement(), false); + } +}