diff --git a/connector/pom.xml b/connector/pom.xml
index cff4c87a..ee0680d9 100644
--- a/connector/pom.xml
+++ b/connector/pom.xml
@@ -79,7 +79,7 @@
com.github.steveice10
opennbt
- 1.3-SNAPSHOT
+ 1.4-SNAPSHOT
compile
diff --git a/connector/src/main/java/org/geysermc/connector/inventory/Inventory.java b/connector/src/main/java/org/geysermc/connector/inventory/Inventory.java
index 801f670c..24ec4a3c 100644
--- a/connector/src/main/java/org/geysermc/connector/inventory/Inventory.java
+++ b/connector/src/main/java/org/geysermc/connector/inventory/Inventory.java
@@ -27,9 +27,12 @@ package org.geysermc.connector.inventory;
import com.github.steveice10.mc.protocol.data.game.entity.metadata.ItemStack;
import com.github.steveice10.mc.protocol.data.game.window.WindowType;
+import com.nukkitx.math.vector.Vector3i;
import lombok.Getter;
import lombok.Setter;
+import java.util.concurrent.atomic.AtomicInteger;
+
public class Inventory {
@Getter
@@ -43,16 +46,26 @@ public class Inventory {
protected WindowType windowType;
@Getter
- protected int size;
+ protected final int size;
@Getter
@Setter
protected String title;
- @Getter
@Setter
protected ItemStack[] items;
+ @Getter
+ @Setter
+ protected Vector3i holderPosition = Vector3i.ZERO;
+
+ @Getter
+ @Setter
+ protected long holderId = -1;
+
+ @Getter
+ protected AtomicInteger transactionId = new AtomicInteger(1);
+
public Inventory(int id, WindowType windowType, int size) {
this("Inventory", id, windowType, size);
}
@@ -62,11 +75,16 @@ public class Inventory {
this.id = id;
this.windowType = windowType;
this.size = size;
-
this.items = new ItemStack[size];
}
public ItemStack getItem(int slot) {
return items[slot];
}
+
+ public void setItem(int slot, ItemStack item) {
+ if (item != null && (item.getId() == 0 || item.getAmount() < 1))
+ item = null;
+ items[slot] = item;
+ }
}
diff --git a/connector/src/main/java/org/geysermc/connector/inventory/PlayerInventory.java b/connector/src/main/java/org/geysermc/connector/inventory/PlayerInventory.java
index 424570b9..a11ce856 100644
--- a/connector/src/main/java/org/geysermc/connector/inventory/PlayerInventory.java
+++ b/connector/src/main/java/org/geysermc/connector/inventory/PlayerInventory.java
@@ -35,12 +35,20 @@ public class PlayerInventory extends Inventory {
@Setter
private int heldItemSlot;
- public PlayerInventory() {
- super(0, null, 45);
+ @Getter
+ private ItemStack cursor;
+ public PlayerInventory() {
+ super(0, null, 46);
heldItemSlot = 0;
}
+ public void setCursor(ItemStack stack) {
+ if (stack != null && (stack.getId() == 0 || stack.getAmount() < 1))
+ stack = null;
+ cursor = stack;
+ }
+
public ItemStack getItemInHand() {
return items[heldItemSlot];
}
diff --git a/connector/src/main/java/org/geysermc/connector/network/session/GeyserSession.java b/connector/src/main/java/org/geysermc/connector/network/session/GeyserSession.java
index 9984249c..43967670 100644
--- a/connector/src/main/java/org/geysermc/connector/network/session/GeyserSession.java
+++ b/connector/src/main/java/org/geysermc/connector/network/session/GeyserSession.java
@@ -44,6 +44,7 @@ import com.nukkitx.math.vector.Vector3f;
import com.nukkitx.math.vector.Vector3i;
import com.nukkitx.nbt.tag.CompoundTag;
import com.nukkitx.protocol.bedrock.BedrockServerSession;
+import com.nukkitx.protocol.bedrock.data.ContainerId;
import com.nukkitx.protocol.bedrock.data.GamePublishSetting;
import com.nukkitx.protocol.bedrock.data.GameRuleData;
import com.nukkitx.protocol.bedrock.data.PlayerPermission;
@@ -111,6 +112,9 @@ public class GeyserSession implements Player {
private boolean manyDimPackets = false;
private ServerRespawnPacket lastDimPacket = null;
+ @Setter
+ private int craftSlot = 0;
+
public GeyserSession(GeyserConnector connector, BedrockServerSession bedrockServerSession) {
this.connector = connector;
this.upstream = new UpstreamSession(bedrockServerSession);
@@ -150,6 +154,11 @@ public class GeyserSession implements Player {
entityPacket.setTag(CompoundTag.EMPTY);
upstream.sendPacket(entityPacket);
+ InventoryContentPacket creativePacket = new InventoryContentPacket();
+ creativePacket.setContainerId(ContainerId.CREATIVE);
+ creativePacket.setContents(Toolbox.CREATIVE_ITEMS);
+ upstream.sendPacket(creativePacket);
+
PlayStatusPacket playStatusPacket = new PlayStatusPacket();
playStatusPacket.setStatus(PlayStatusPacket.Status.PLAYER_SPAWN);
upstream.sendPacket(playStatusPacket);
diff --git a/connector/src/main/java/org/geysermc/connector/network/session/cache/InventoryCache.java b/connector/src/main/java/org/geysermc/connector/network/session/cache/InventoryCache.java
index 7a505878..8734c710 100644
--- a/connector/src/main/java/org/geysermc/connector/network/session/cache/InventoryCache.java
+++ b/connector/src/main/java/org/geysermc/connector/network/session/cache/InventoryCache.java
@@ -25,15 +25,12 @@
package org.geysermc.connector.network.session.cache;
-import com.github.steveice10.packetlib.packet.Packet;
import lombok.Getter;
import lombok.Setter;
import org.geysermc.connector.inventory.Inventory;
import org.geysermc.connector.network.session.GeyserSession;
-import java.util.ArrayList;
import java.util.HashMap;
-import java.util.List;
import java.util.Map;
public class InventoryCache {
@@ -47,9 +44,6 @@ public class InventoryCache {
@Getter
private Map inventories = new HashMap();
- @Getter
- private Map> cachedPackets = new HashMap>();
-
public InventoryCache(GeyserSession session) {
this.session = session;
}
@@ -65,10 +59,4 @@ public class InventoryCache {
public void uncacheInventory(int id) {
inventories.remove(id);
}
-
- public void cachePacket(int id, Packet packet) {
- List packets = cachedPackets.getOrDefault(id, new ArrayList());
- packets.add(packet);
- cachedPackets.put(id, packets);
- }
}
diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/TranslatorsInit.java b/connector/src/main/java/org/geysermc/connector/network/translators/TranslatorsInit.java
index 1268e327..93d07891 100644
--- a/connector/src/main/java/org/geysermc/connector/network/translators/TranslatorsInit.java
+++ b/connector/src/main/java/org/geysermc/connector/network/translators/TranslatorsInit.java
@@ -25,6 +25,7 @@
package org.geysermc.connector.network.translators;
+import com.github.steveice10.mc.protocol.data.game.window.WindowType;
import com.github.steveice10.mc.protocol.packet.ingame.server.*;
import com.github.steveice10.mc.protocol.packet.ingame.server.entity.*;
import com.github.steveice10.mc.protocol.packet.ingame.server.entity.player.*;
@@ -33,37 +34,36 @@ import com.github.steveice10.mc.protocol.packet.ingame.server.scoreboard.ServerD
import com.github.steveice10.mc.protocol.packet.ingame.server.scoreboard.ServerScoreboardObjectivePacket;
import com.github.steveice10.mc.protocol.packet.ingame.server.scoreboard.ServerTeamPacket;
import com.github.steveice10.mc.protocol.packet.ingame.server.scoreboard.ServerUpdateScorePacket;
-import com.github.steveice10.mc.protocol.packet.ingame.server.window.ServerOpenWindowPacket;
-import com.github.steveice10.mc.protocol.packet.ingame.server.window.ServerSetSlotPacket;
-import com.github.steveice10.mc.protocol.packet.ingame.server.window.ServerWindowItemsPacket;
+import com.github.steveice10.mc.protocol.packet.ingame.server.window.*;
import com.github.steveice10.mc.protocol.packet.ingame.server.world.*;
import com.nukkitx.nbt.CompoundTagBuilder;
import com.nukkitx.nbt.NbtUtils;
import com.nukkitx.nbt.stream.NBTOutputStream;
import com.nukkitx.nbt.tag.CompoundTag;
+import com.nukkitx.protocol.bedrock.data.ContainerType;
import com.nukkitx.protocol.bedrock.packet.*;
import lombok.Getter;
import org.geysermc.connector.network.translators.bedrock.*;
import org.geysermc.connector.network.translators.block.BlockTranslator;
-import org.geysermc.connector.network.translators.inventory.GenericInventoryTranslator;
-import org.geysermc.connector.network.translators.inventory.InventoryTranslator;
+import org.geysermc.connector.network.translators.inventory.*;
+import org.geysermc.connector.network.translators.inventory.updater.ContainerInventoryUpdater;
+import org.geysermc.connector.network.translators.inventory.updater.InventoryUpdater;
import org.geysermc.connector.network.translators.item.ItemTranslator;
import org.geysermc.connector.network.translators.java.*;
import org.geysermc.connector.network.translators.java.entity.*;
import org.geysermc.connector.network.translators.java.entity.player.*;
import org.geysermc.connector.network.translators.java.entity.spawn.*;
-import org.geysermc.connector.network.translators.java.inventory.OpenWindowPacketTranslator;
import org.geysermc.connector.network.translators.java.scoreboard.JavaDisplayScoreboardTranslator;
import org.geysermc.connector.network.translators.java.scoreboard.JavaScoreboardObjectiveTranslator;
import org.geysermc.connector.network.translators.java.scoreboard.JavaTeamTranslator;
import org.geysermc.connector.network.translators.java.scoreboard.JavaUpdateScoreTranslator;
-import org.geysermc.connector.network.translators.java.window.JavaOpenWindowTranslator;
-import org.geysermc.connector.network.translators.java.window.JavaSetSlotTranslator;
-import org.geysermc.connector.network.translators.java.window.JavaWindowItemsTranslator;
+import org.geysermc.connector.network.translators.java.window.*;
import org.geysermc.connector.network.translators.java.world.*;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
+import java.util.HashMap;
+import java.util.Map;
public class TranslatorsInit {
@@ -71,7 +71,7 @@ public class TranslatorsInit {
private static ItemTranslator itemTranslator;
@Getter
- private static InventoryTranslator inventoryTranslator = new GenericInventoryTranslator();
+ private static Map inventoryTranslators = new HashMap<>();
private static final CompoundTag EMPTY_TAG = CompoundTagBuilder.builder().buildRootTag();
public static final byte[] EMPTY_LEVEL_CHUNK_DATA;
@@ -99,6 +99,7 @@ public class TranslatorsInit {
Registry.registerJava(ServerRespawnPacket.class, new JavaRespawnTranslator());
Registry.registerJava(ServerSpawnPositionPacket.class, new JavaSpawnPositionTranslator());
Registry.registerJava(ServerDifficultyPacket.class, new JavaDifficultyTranslator());
+ Registry.registerJava(ServerDeclareRecipesPacket.class, new JavaDeclareRecipesTranslator());
Registry.registerJava(ServerEntityAnimationPacket.class, new JavaEntityAnimationTranslator());
Registry.registerJava(ServerEntityPositionPacket.class, new JavaEntityPositionTranslator());
@@ -125,15 +126,13 @@ public class TranslatorsInit {
Registry.registerJava(ServerPlayerSetExperiencePacket.class, new JavaPlayerSetExperienceTranslator());
Registry.registerJava(ServerPlayerHealthPacket.class, new JavaPlayerHealthTranslator());
Registry.registerJava(ServerPlayerActionAckPacket.class, new JavaPlayerActionAckTranslator());
+ Registry.registerJava(ServerPlayerChangeHeldItemPacket.class, new JavaPlayerChangeHeldItemTranslator());
Registry.registerJava(ServerPlayerAbilitiesPacket.class, new JavaPlayerAbilitiesTranslator());
Registry.registerJava(ServerNotifyClientPacket.class, new JavaNotifyClientTranslator());
Registry.registerJava(ServerChunkDataPacket.class, new JavaChunkDataTranslator());
Registry.registerJava(ServerEntityDestroyPacket.class, new JavaEntityDestroyTranslator());
- Registry.registerJava(ServerWindowItemsPacket.class, new JavaWindowItemsTranslator());
- Registry.registerJava(ServerOpenWindowPacket.class, new JavaOpenWindowTranslator());
- Registry.registerJava(ServerSetSlotPacket.class, new JavaSetSlotTranslator());
Registry.registerJava(ServerScoreboardObjectivePacket.class, new JavaScoreboardObjectiveTranslator());
Registry.registerJava(ServerDisplayScoreboardPacket.class, new JavaDisplayScoreboardTranslator());
Registry.registerJava(ServerUpdateScorePacket.class, new JavaUpdateScoreTranslator());
@@ -142,11 +141,16 @@ public class TranslatorsInit {
Registry.registerJava(ServerMultiBlockChangePacket.class, new JavaMultiBlockChangeTranslator());
Registry.registerJava(ServerUnloadChunkPacket.class, new JavaUnloadChunkTranslator());
+ Registry.registerJava(ServerWindowItemsPacket.class, new JavaWindowItemsTranslator());
+ Registry.registerJava(ServerOpenWindowPacket.class, new JavaOpenWindowTranslator());
+ Registry.registerJava(ServerSetSlotPacket.class, new JavaSetSlotTranslator());
+ Registry.registerJava(ServerCloseWindowPacket.class, new JavaCloseWindowTranslator());
+ Registry.registerJava(ServerConfirmTransactionPacket.class, new JavaConfirmTransactionTranslator());
+ Registry.registerJava(ServerWindowPropertyPacket.class, new JavaWindowPropertyTranslator());
+
Registry.registerJava(ServerUpdateViewPositionPacket.class, new JavaUpdateViewPositionTranslator());
Registry.registerJava(ServerUpdateViewDistancePacket.class, new JavaUpdateViewDistanceTranslator());
- Registry.registerJava(ServerOpenWindowPacket.class, new OpenWindowPacketTranslator());
-
Registry.registerBedrock(AnimatePacket.class, new BedrockAnimateTranslator());
Registry.registerBedrock(CommandRequestPacket.class, new BedrockCommandRequestTranslator());
Registry.registerBedrock(InventoryTransactionPacket.class, new BedrockInventoryTransactionTranslator());
@@ -156,6 +160,7 @@ public class TranslatorsInit {
Registry.registerBedrock(SetLocalPlayerAsInitializedPacket.class, new BedrockPlayerInitializedTranslator());
Registry.registerBedrock(InteractPacket.class, new BedrockInteractTranslator());
Registry.registerBedrock(TextPacket.class, new BedrockTextTranslator());
+ Registry.registerBedrock(ContainerClosePacket.class, new BedrockContainerCloseTranslator());
Registry.registerBedrock(RespawnPacket.class, new BedrockRespawnTranslator());
Registry.registerBedrock(ShowCreditsPacket.class, new BedrockShowCreditsTranslator());
@@ -166,11 +171,27 @@ public class TranslatorsInit {
}
private static void registerInventoryTranslators() {
- /*inventoryTranslators.put(WindowType.GENERIC_9X1, new GenericInventoryTranslator());
- inventoryTranslators.put(WindowType.GENERIC_9X2, new GenericInventoryTranslator());
- inventoryTranslators.put(WindowType.GENERIC_9X3, new GenericInventoryTranslator());
- inventoryTranslators.put(WindowType.GENERIC_9X4, new GenericInventoryTranslator());
- inventoryTranslators.put(WindowType.GENERIC_9X5, new GenericInventoryTranslator());
- inventoryTranslators.put(WindowType.GENERIC_9X6, new GenericInventoryTranslator());*/
+ inventoryTranslators.put(null, new PlayerInventoryTranslator()); //player inventory
+ inventoryTranslators.put(WindowType.GENERIC_9X1, new SingleChestInventoryTranslator(9));
+ inventoryTranslators.put(WindowType.GENERIC_9X2, new SingleChestInventoryTranslator(18));
+ inventoryTranslators.put(WindowType.GENERIC_9X3, new SingleChestInventoryTranslator(27));
+ inventoryTranslators.put(WindowType.GENERIC_9X4, new DoubleChestInventoryTranslator(36));
+ inventoryTranslators.put(WindowType.GENERIC_9X5, new DoubleChestInventoryTranslator(45));
+ inventoryTranslators.put(WindowType.GENERIC_9X6, new DoubleChestInventoryTranslator(54));
+ inventoryTranslators.put(WindowType.BREWING_STAND, new BrewingInventoryTranslator());
+ inventoryTranslators.put(WindowType.ANVIL, new AnvilInventoryTranslator());
+ inventoryTranslators.put(WindowType.CRAFTING, new CraftingInventoryTranslator());
+ //inventoryTranslators.put(WindowType.ENCHANTMENT, new EnchantmentInventoryTranslator()); //TODO
+
+ InventoryTranslator furnace = new FurnaceInventoryTranslator();
+ inventoryTranslators.put(WindowType.FURNACE, furnace);
+ inventoryTranslators.put(WindowType.BLAST_FURNACE, furnace);
+ inventoryTranslators.put(WindowType.SMOKER, furnace);
+
+ InventoryUpdater containerUpdater = new ContainerInventoryUpdater();
+ inventoryTranslators.put(WindowType.GENERIC_3X3, new BlockInventoryTranslator(9, "minecraft:dispenser[facing=north,triggered=false]", ContainerType.DISPENSER, containerUpdater));
+ inventoryTranslators.put(WindowType.HOPPER, new BlockInventoryTranslator(5, "minecraft:hopper[enabled=false,facing=down]", ContainerType.HOPPER, containerUpdater));
+ inventoryTranslators.put(WindowType.SHULKER_BOX, new BlockInventoryTranslator(27, "minecraft:shulker_box[facing=north]", ContainerType.CONTAINER, containerUpdater));
+ //inventoryTranslators.put(WindowType.BEACON, new BlockInventoryTranslator(1, "minecraft:beacon", ContainerType.BEACON)); //TODO
}
}
diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockContainerCloseTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockContainerCloseTranslator.java
new file mode 100644
index 00000000..01da6d5f
--- /dev/null
+++ b/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockContainerCloseTranslator.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright (c) 2019 GeyserMC. http://geysermc.org
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ * @author GeyserMC
+ * @link https://github.com/GeyserMC/Geyser
+ */
+
+package org.geysermc.connector.network.translators.bedrock;
+
+import com.github.steveice10.mc.protocol.packet.ingame.client.window.ClientCloseWindowPacket;
+import com.nukkitx.protocol.bedrock.packet.ContainerClosePacket;
+import org.geysermc.connector.inventory.Inventory;
+import org.geysermc.connector.network.session.GeyserSession;
+import org.geysermc.connector.network.translators.PacketTranslator;
+import org.geysermc.connector.network.translators.TranslatorsInit;
+import org.geysermc.connector.network.translators.inventory.InventoryTranslator;
+import org.geysermc.connector.utils.InventoryUtils;
+
+public class BedrockContainerCloseTranslator extends PacketTranslator {
+
+ @Override
+ public void translate(ContainerClosePacket packet, GeyserSession session) {
+ byte windowId = packet.getWindowId();
+ if (windowId == -1) { //player inventory or crafting table
+ Inventory openInventory = session.getInventoryCache().getOpenInventory();
+ if (openInventory != null) {
+ windowId = (byte) openInventory.getId();
+ } else {
+ windowId = 0;
+ }
+ }
+ ClientCloseWindowPacket closeWindowPacket = new ClientCloseWindowPacket(windowId);
+ session.getDownstream().getSession().send(closeWindowPacket);
+ InventoryUtils.closeInventory(session, windowId);
+ }
+}
diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockInventoryTransactionTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockInventoryTransactionTranslator.java
index 8b4364a7..ee12b5aa 100644
--- a/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockInventoryTransactionTranslator.java
+++ b/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockInventoryTransactionTranslator.java
@@ -25,6 +25,7 @@
package org.geysermc.connector.network.translators.bedrock;
+import com.nukkitx.math.vector.Vector3f;
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;
@@ -34,17 +35,32 @@ 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.nukkitx.math.vector.Vector3f;
import com.nukkitx.protocol.bedrock.packet.InventoryTransactionPacket;
import org.geysermc.connector.entity.Entity;
+import org.geysermc.connector.inventory.Inventory;
import org.geysermc.connector.network.session.GeyserSession;
import org.geysermc.connector.network.translators.PacketTranslator;
+import org.geysermc.connector.network.translators.TranslatorsInit;
+import org.geysermc.connector.utils.InventoryUtils;
+
+import java.util.*;
public class BedrockInventoryTransactionTranslator extends PacketTranslator {
@Override
public void translate(InventoryTransactionPacket packet, GeyserSession session) {
switch (packet.getTransactionType()) {
+ case NORMAL:
+ Inventory inventory = session.getInventoryCache().getOpenInventory();
+ if (inventory == null) inventory = session.getInventory();
+ TranslatorsInit.getInventoryTranslators().get(inventory.getWindowType()).translateActions(session, inventory, packet.getActions());
+ break;
+ case INVENTORY_MISMATCH:
+ Inventory inv = session.getInventoryCache().getOpenInventory();
+ if (inv == null) inv = session.getInventory();
+ TranslatorsInit.getInventoryTranslators().get(inv.getWindowType()).updateInventory(session, inv);
+ InventoryUtils.updateCursor(session);
+ break;
case ITEM_USE:
if (packet.getActionType() == 1) {
ClientPlayerUseItemPacket useItemPacket = new ClientPlayerUseItemPacket(Hand.MAIN_HAND);
diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/block/BlockTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/block/BlockTranslator.java
index 61a55b42..2e1c4ffe 100644
--- a/connector/src/main/java/org/geysermc/connector/network/translators/block/BlockTranslator.java
+++ b/connector/src/main/java/org/geysermc/connector/network/translators/block/BlockTranslator.java
@@ -48,6 +48,7 @@ public class BlockTranslator {
private static final Int2IntMap JAVA_TO_BEDROCK_BLOCK_MAP = new Int2IntOpenHashMap();
private static final Int2ObjectMap BEDROCK_TO_JAVA_BLOCK_MAP = new Int2ObjectOpenHashMap<>();
+ private static final Map JAVA_ID_BLOCK_MAP = new HashMap<>();
private static final IntSet WATERLOGGED = new IntOpenHashSet();
private static final int BLOCK_STATE_VERSION = 17760256;
@@ -89,18 +90,21 @@ public class BlockTranslator {
javaRuntimeId++;
Map.Entry entry = blocksIterator.next();
String javaId = entry.getKey();
+ BlockState javaBlockState = new BlockState(javaRuntimeId);
CompoundTag blockTag = buildBedrockState(entry.getValue());
+ JAVA_ID_BLOCK_MAP.put(javaId, javaBlockState);
+
if ("minecraft:water[level=0]".equals(javaId)) {
waterRuntimeId = bedrockRuntimeId;
}
boolean waterlogged = entry.getValue().has("waterlogged") && entry.getValue().get("waterlogged").booleanValue();
if (waterlogged) {
- BEDROCK_TO_JAVA_BLOCK_MAP.putIfAbsent(bedrockRuntimeId | 1 << 31, new BlockState(javaRuntimeId));
+ BEDROCK_TO_JAVA_BLOCK_MAP.putIfAbsent(bedrockRuntimeId | 1 << 31, javaBlockState);
WATERLOGGED.add(javaRuntimeId);
} else {
- BEDROCK_TO_JAVA_BLOCK_MAP.putIfAbsent(bedrockRuntimeId, new BlockState(javaRuntimeId));
+ BEDROCK_TO_JAVA_BLOCK_MAP.putIfAbsent(bedrockRuntimeId, javaBlockState);
}
CompoundTag runtimeTag = blockStateMap.remove(blockTag);
@@ -175,6 +179,10 @@ public class BlockTranslator {
return BEDROCK_TO_JAVA_BLOCK_MAP.get(bedrockId);
}
+ public static BlockState getJavaBlockState(String javaId) {
+ return JAVA_ID_BLOCK_MAP.get(javaId);
+ }
+
public static boolean isWaterlogged(BlockState state) {
return WATERLOGGED.contains(state.getId());
}
diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/inventory/AnvilInventoryTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/AnvilInventoryTranslator.java
new file mode 100644
index 00000000..60700ba2
--- /dev/null
+++ b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/AnvilInventoryTranslator.java
@@ -0,0 +1,117 @@
+/*
+ * Copyright (c) 2019-2020 GeyserMC. http://geysermc.org
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ * @author GeyserMC
+ * @link https://github.com/GeyserMC/Geyser
+ */
+
+package org.geysermc.connector.network.translators.inventory;
+
+import com.github.steveice10.mc.protocol.packet.ingame.client.window.ClientRenameItemPacket;
+import com.nukkitx.protocol.bedrock.data.ContainerId;
+import com.nukkitx.protocol.bedrock.data.ContainerType;
+import com.nukkitx.protocol.bedrock.data.InventoryActionData;
+import com.nukkitx.protocol.bedrock.data.ItemData;
+import org.geysermc.connector.inventory.Inventory;
+import org.geysermc.connector.network.session.GeyserSession;
+import org.geysermc.connector.network.translators.inventory.updater.CursorInventoryUpdater;
+
+import java.util.List;
+
+public class AnvilInventoryTranslator extends BlockInventoryTranslator {
+ public AnvilInventoryTranslator() {
+ super(3, "minecraft:anvil[facing=north]", ContainerType.ANVIL, new CursorInventoryUpdater());
+ }
+
+ @Override
+ public int bedrockSlotToJava(InventoryActionData action) {
+ if (action.getSource().getContainerId() == ContainerId.CURSOR) {
+ switch (action.getSlot()) {
+ case 1:
+ return 0;
+ case 2:
+ return 1;
+ case 50:
+ return 2;
+ }
+ }
+ return super.bedrockSlotToJava(action);
+ }
+
+ @Override
+ public int javaSlotToBedrock(int slot) {
+ switch (slot) {
+ case 0:
+ return 1;
+ case 1:
+ return 2;
+ case 2:
+ return 50;
+ }
+ return super.javaSlotToBedrock(slot);
+ }
+
+ @Override
+ public SlotType getSlotType(int javaSlot) {
+ if (javaSlot == 2)
+ return SlotType.OUTPUT;
+ return SlotType.NORMAL;
+ }
+
+ @Override
+ public void translateActions(GeyserSession session, Inventory inventory, List actions) {
+ InventoryActionData anvilResult = null;
+ InventoryActionData anvilInput = null;
+ for (InventoryActionData action : actions) {
+ if (action.getSource().getContainerId() == ContainerId.ANVIL_MATERIAL) {
+ //useless packet
+ return;
+ } else if (action.getSource().getContainerId() == ContainerId.ANVIL_RESULT) {
+ anvilResult = action;
+ } else if (bedrockSlotToJava(action) == 0) {
+ anvilInput = action;
+ }
+ }
+ ItemData itemName = null;
+ if (anvilResult != null) {
+ itemName = anvilResult.getFromItem();
+ } else if (anvilInput != null) {
+ itemName = anvilInput.getToItem();
+ }
+ if (itemName != null) {
+ String rename;
+ com.nukkitx.nbt.tag.CompoundTag tag = itemName.getTag();
+ if (tag != null) {
+ rename = tag.getCompound("display").getString("Name");
+ } else {
+ rename = "";
+ }
+ ClientRenameItemPacket renameItemPacket = new ClientRenameItemPacket(rename);
+ session.getDownstream().getSession().send(renameItemPacket);
+ }
+ if (anvilResult != null) {
+ //client will send another packet to grab anvil output
+ return;
+ }
+
+ super.translateActions(session, inventory, actions);
+ }
+}
diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/inventory/BaseInventoryTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/BaseInventoryTranslator.java
new file mode 100644
index 00000000..5deb0370
--- /dev/null
+++ b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/BaseInventoryTranslator.java
@@ -0,0 +1,82 @@
+/*
+ * Copyright (c) 2019-2020 GeyserMC. http://geysermc.org
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ * @author GeyserMC
+ * @link https://github.com/GeyserMC/Geyser
+ */
+
+package org.geysermc.connector.network.translators.inventory;
+
+import com.nukkitx.protocol.bedrock.data.ContainerId;
+import com.nukkitx.protocol.bedrock.data.InventoryActionData;
+import org.geysermc.connector.inventory.Inventory;
+import org.geysermc.connector.network.session.GeyserSession;
+import org.geysermc.connector.network.translators.inventory.action.InventoryActionDataTranslator;
+
+import java.util.List;
+
+public abstract class BaseInventoryTranslator extends InventoryTranslator{
+ BaseInventoryTranslator(int size) {
+ super(size);
+ }
+
+ @Override
+ public void updateProperty(GeyserSession session, Inventory inventory, int key, int value) {
+ //
+ }
+
+ @Override
+ public int bedrockSlotToJava(InventoryActionData action) {
+ int slotnum = action.getSlot();
+ if (action.getSource().getContainerId() == ContainerId.INVENTORY) {
+ //hotbar
+ if (slotnum >= 9) {
+ return slotnum + this.size - 9;
+ } else {
+ return slotnum + this.size + 27;
+ }
+ }
+ return slotnum;
+ }
+
+ @Override
+ public int javaSlotToBedrock(int slot) {
+ if (slot >= this.size) {
+ final int tmp = slot - this.size;
+ if (tmp < 27) {
+ return tmp + 9;
+ } else {
+ return tmp - 27;
+ }
+ }
+ return slot;
+ }
+
+ @Override
+ public SlotType getSlotType(int javaSlot) {
+ return SlotType.NORMAL;
+ }
+
+ @Override
+ public void translateActions(GeyserSession session, Inventory inventory, List actions) {
+ InventoryActionDataTranslator.translate(this, session, inventory, actions);
+ }
+}
diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/inventory/BlockInventoryTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/BlockInventoryTranslator.java
new file mode 100644
index 00000000..5f6274f0
--- /dev/null
+++ b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/BlockInventoryTranslator.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright (c) 2019-2020 GeyserMC. http://geysermc.org
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ * @author GeyserMC
+ * @link https://github.com/GeyserMC/Geyser
+ */
+
+package org.geysermc.connector.network.translators.inventory;
+
+import com.github.steveice10.mc.protocol.data.game.world.block.BlockState;
+import com.nukkitx.protocol.bedrock.data.ContainerType;
+import org.geysermc.connector.inventory.Inventory;
+import org.geysermc.connector.network.session.GeyserSession;
+import org.geysermc.connector.network.translators.block.BlockTranslator;
+import org.geysermc.connector.network.translators.inventory.holder.BlockInventoryHolder;
+import org.geysermc.connector.network.translators.inventory.holder.InventoryHolder;
+import org.geysermc.connector.network.translators.inventory.updater.InventoryUpdater;
+
+public class BlockInventoryTranslator extends BaseInventoryTranslator {
+ private final InventoryHolder holder;
+ private final InventoryUpdater updater;
+
+ public BlockInventoryTranslator(int size, String javaBlockIdentifier, ContainerType containerType, InventoryUpdater updater) {
+ super(size);
+ BlockState javaBlockState = BlockTranslator.getJavaBlockState(javaBlockIdentifier);
+ int blockId = BlockTranslator.getBedrockBlockId(javaBlockState);
+ this.holder = new BlockInventoryHolder(blockId, containerType);
+ this.updater = updater;
+ }
+
+ @Override
+ public void prepareInventory(GeyserSession session, Inventory inventory) {
+ holder.prepareInventory(this, session, inventory);
+ }
+
+ @Override
+ public void openInventory(GeyserSession session, Inventory inventory) {
+ holder.openInventory(this, session, inventory);
+ }
+
+ @Override
+ public void closeInventory(GeyserSession session, Inventory inventory) {
+ holder.closeInventory(this, session, inventory);
+ }
+
+ @Override
+ public void updateInventory(GeyserSession session, Inventory inventory) {
+ updater.updateInventory(this, session, inventory);
+ }
+
+ @Override
+ public void updateSlot(GeyserSession session, Inventory inventory, int slot) {
+ updater.updateSlot(this, session, inventory, slot);
+ }
+}
diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/inventory/BrewingInventoryTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/BrewingInventoryTranslator.java
new file mode 100644
index 00000000..bd143698
--- /dev/null
+++ b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/BrewingInventoryTranslator.java
@@ -0,0 +1,99 @@
+/*
+ * Copyright (c) 2019-2020 GeyserMC. http://geysermc.org
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ * @author GeyserMC
+ * @link https://github.com/GeyserMC/Geyser
+ */
+
+package org.geysermc.connector.network.translators.inventory;
+
+import com.nukkitx.protocol.bedrock.data.ContainerType;
+import com.nukkitx.protocol.bedrock.data.InventoryActionData;
+import com.nukkitx.protocol.bedrock.packet.ContainerSetDataPacket;
+import org.geysermc.connector.inventory.Inventory;
+import org.geysermc.connector.network.session.GeyserSession;
+import org.geysermc.connector.network.translators.inventory.updater.ContainerInventoryUpdater;
+
+public class BrewingInventoryTranslator extends BlockInventoryTranslator {
+ public BrewingInventoryTranslator() {
+ super(5, "minecraft:brewing_stand[has_bottle_0=false,has_bottle_1=false,has_bottle_2=false]", ContainerType.BREWING_STAND, new ContainerInventoryUpdater());
+ }
+
+ @Override
+ public void openInventory(GeyserSession session, Inventory inventory) {
+ super.openInventory(session, inventory);
+ ContainerSetDataPacket dataPacket = new ContainerSetDataPacket();
+ dataPacket.setWindowId((byte) inventory.getId());
+ dataPacket.setProperty(ContainerSetDataPacket.BREWING_STAND_FUEL_TOTAL);
+ dataPacket.setValue(20);
+ session.getUpstream().sendPacket(dataPacket);
+ }
+
+ @Override
+ public void updateProperty(GeyserSession session, Inventory inventory, int key, int value) {
+ ContainerSetDataPacket dataPacket = new ContainerSetDataPacket();
+ dataPacket.setWindowId((byte) inventory.getId());
+ switch (key) {
+ case 0:
+ dataPacket.setProperty(ContainerSetDataPacket.BREWING_STAND_BREW_TIME);
+ break;
+ case 1:
+ dataPacket.setProperty(ContainerSetDataPacket.BREWING_STAND_FUEL_AMOUNT);
+ break;
+ default:
+ return;
+ }
+ dataPacket.setValue(value);
+ session.getUpstream().sendPacket(dataPacket);
+ }
+
+ @Override
+ public int bedrockSlotToJava(InventoryActionData action) {
+ final int slot = super.bedrockSlotToJava(action);
+ switch (slot) {
+ case 0:
+ return 3;
+ case 1:
+ return 0;
+ case 2:
+ return 1;
+ case 3:
+ return 2;
+ default:
+ return slot;
+ }
+ }
+
+ @Override
+ public int javaSlotToBedrock(int slot) {
+ switch (slot) {
+ case 0:
+ return 1;
+ case 1:
+ return 2;
+ case 2:
+ return 3;
+ case 3:
+ return 0;
+ }
+ return super.javaSlotToBedrock(slot);
+ }
+}
diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/inventory/CraftingInventoryTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/CraftingInventoryTranslator.java
new file mode 100644
index 00000000..92a1d90e
--- /dev/null
+++ b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/CraftingInventoryTranslator.java
@@ -0,0 +1,118 @@
+/*
+ * Copyright (c) 2019-2020 GeyserMC. http://geysermc.org
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ * @author GeyserMC
+ * @link https://github.com/GeyserMC/Geyser
+ */
+
+package org.geysermc.connector.network.translators.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.ContainerType;
+import com.nukkitx.protocol.bedrock.data.InventoryActionData;
+import com.nukkitx.protocol.bedrock.data.InventorySource;
+import com.nukkitx.protocol.bedrock.packet.ContainerOpenPacket;
+import org.geysermc.connector.inventory.Inventory;
+import org.geysermc.connector.network.session.GeyserSession;
+import org.geysermc.connector.network.translators.inventory.updater.CursorInventoryUpdater;
+import org.geysermc.connector.network.translators.inventory.updater.InventoryUpdater;
+import org.geysermc.connector.utils.InventoryUtils;
+
+import java.util.List;
+
+public class CraftingInventoryTranslator extends BaseInventoryTranslator {
+ private final InventoryUpdater updater;
+
+ public CraftingInventoryTranslator() {
+ super(10);
+ this.updater = new CursorInventoryUpdater();
+ }
+
+ @Override
+ public void prepareInventory(GeyserSession session, Inventory inventory) {
+ //
+ }
+
+ @Override
+ public void openInventory(GeyserSession session, Inventory inventory) {
+ ContainerOpenPacket containerOpenPacket = new ContainerOpenPacket();
+ containerOpenPacket.setWindowId((byte) inventory.getId());
+ containerOpenPacket.setType((byte) ContainerType.WORKBENCH.id());
+ containerOpenPacket.setBlockPosition(inventory.getHolderPosition());
+ containerOpenPacket.setUniqueEntityId(inventory.getHolderId());
+ session.getUpstream().sendPacket(containerOpenPacket);
+ }
+
+ @Override
+ public void closeInventory(GeyserSession session, Inventory inventory) {
+ //
+ }
+
+ @Override
+ public void updateInventory(GeyserSession session, Inventory inventory) {
+ updater.updateInventory(this, session, inventory);
+ }
+
+ @Override
+ public void updateSlot(GeyserSession session, Inventory inventory, int slot) {
+ updater.updateSlot(this, session, inventory, slot);
+ }
+
+ @Override
+ public int bedrockSlotToJava(InventoryActionData action) {
+ if (action.getSource().getContainerId() == ContainerId.CURSOR) {
+ int slotnum = action.getSlot();
+ if (slotnum >= 32 && 42 >= slotnum) {
+ return slotnum - 31;
+ } else if (slotnum == 50) {
+ return 0;
+ }
+ }
+ return super.bedrockSlotToJava(action);
+ }
+
+ @Override
+ public int javaSlotToBedrock(int slot) {
+ return slot == 0 ? 50 : slot + 31;
+ }
+
+ @Override
+ public SlotType getSlotType(int javaSlot) {
+ if (javaSlot == 0)
+ return SlotType.OUTPUT;
+ return SlotType.NORMAL;
+ }
+
+ @Override
+ public void translateActions(GeyserSession session, Inventory inventory, List actions) {
+ if (session.getGameMode() == GameMode.CREATIVE) {
+ for (InventoryActionData action : actions) {
+ if (action.getSource().getType() == InventorySource.Type.CREATIVE) {
+ updateInventory(session, inventory);
+ InventoryUtils.updateCursor(session);
+ return;
+ }
+ }
+ }
+ super.translateActions(session, inventory, actions);
+ }
+}
diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/inventory/DoubleChestInventoryTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/DoubleChestInventoryTranslator.java
new file mode 100644
index 00000000..c70a8995
--- /dev/null
+++ b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/DoubleChestInventoryTranslator.java
@@ -0,0 +1,141 @@
+/*
+ * Copyright (c) 2019-2020 GeyserMC. http://geysermc.org
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ * @author GeyserMC
+ * @link https://github.com/GeyserMC/Geyser
+ */
+
+package org.geysermc.connector.network.translators.inventory;
+
+import com.github.steveice10.mc.protocol.data.game.entity.metadata.Position;
+import com.github.steveice10.mc.protocol.data.game.world.block.BlockState;
+import com.nukkitx.math.vector.Vector3i;
+import com.nukkitx.nbt.tag.CompoundTag;
+import com.nukkitx.protocol.bedrock.data.ContainerType;
+import com.nukkitx.protocol.bedrock.packet.BlockEntityDataPacket;
+import com.nukkitx.protocol.bedrock.packet.ContainerOpenPacket;
+import com.nukkitx.protocol.bedrock.packet.UpdateBlockPacket;
+import org.geysermc.connector.inventory.Inventory;
+import org.geysermc.connector.network.session.GeyserSession;
+import org.geysermc.connector.network.translators.block.BlockTranslator;
+import org.geysermc.connector.network.translators.inventory.updater.ChestInventoryUpdater;
+import org.geysermc.connector.network.translators.inventory.updater.InventoryUpdater;
+
+public class DoubleChestInventoryTranslator extends BaseInventoryTranslator {
+ private final int blockId;
+ private final InventoryUpdater updater;
+
+ public DoubleChestInventoryTranslator(int size) {
+ super(size);
+ BlockState javaBlockState = BlockTranslator.getJavaBlockState("minecraft:chest[facing=north,type=single,waterlogged=false]");
+ this.blockId = BlockTranslator.getBedrockBlockId(javaBlockState);
+ this.updater = new ChestInventoryUpdater(54);
+ }
+
+ @Override
+ public void prepareInventory(GeyserSession session, Inventory inventory) {
+ Vector3i position = session.getPlayerEntity().getPosition().toInt().add(Vector3i.UP);
+ Vector3i pairPosition = position.add(Vector3i.UNIT_X);
+
+ UpdateBlockPacket blockPacket = new UpdateBlockPacket();
+ blockPacket.setDataLayer(0);
+ blockPacket.setBlockPosition(position);
+ blockPacket.setRuntimeId(blockId);
+ blockPacket.getFlags().addAll(UpdateBlockPacket.FLAG_ALL_PRIORITY);
+ session.getUpstream().sendPacket(blockPacket);
+
+ CompoundTag tag = CompoundTag.builder()
+ .stringTag("id", "Chest")
+ .intTag("x", position.getX())
+ .intTag("y", position.getY())
+ .intTag("z", position.getZ())
+ .intTag("pairx", pairPosition.getX())
+ .intTag("pairz", pairPosition.getZ())
+ .stringTag("CustomName", inventory.getTitle()).buildRootTag();
+ BlockEntityDataPacket dataPacket = new BlockEntityDataPacket();
+ dataPacket.setData(tag);
+ dataPacket.setBlockPosition(position);
+ session.getUpstream().sendPacket(dataPacket);
+
+ blockPacket = new UpdateBlockPacket();
+ blockPacket.setDataLayer(0);
+ blockPacket.setBlockPosition(pairPosition);
+ blockPacket.setRuntimeId(blockId);
+ blockPacket.getFlags().addAll(UpdateBlockPacket.FLAG_ALL_PRIORITY);
+ session.getUpstream().sendPacket(blockPacket);
+
+ tag = CompoundTag.builder()
+ .stringTag("id", "Chest")
+ .intTag("x", pairPosition.getX())
+ .intTag("y", pairPosition.getY())
+ .intTag("z", pairPosition.getZ())
+ .intTag("pairx", position.getX())
+ .intTag("pairz", position.getZ())
+ .stringTag("CustomName", inventory.getTitle()).buildRootTag();
+ dataPacket = new BlockEntityDataPacket();
+ dataPacket.setData(tag);
+ dataPacket.setBlockPosition(pairPosition);
+ session.getUpstream().sendPacket(dataPacket);
+
+ inventory.setHolderPosition(position);
+ }
+
+ @Override
+ public void openInventory(GeyserSession session, Inventory inventory) {
+ ContainerOpenPacket containerOpenPacket = new ContainerOpenPacket();
+ containerOpenPacket.setWindowId((byte) inventory.getId());
+ containerOpenPacket.setType((byte) ContainerType.CONTAINER.id());
+ containerOpenPacket.setBlockPosition(inventory.getHolderPosition());
+ containerOpenPacket.setUniqueEntityId(inventory.getHolderId());
+ session.getUpstream().sendPacket(containerOpenPacket);
+ }
+
+ @Override
+ public void closeInventory(GeyserSession session, Inventory inventory) {
+ Vector3i holderPos = inventory.getHolderPosition();
+ Position pos = new Position(holderPos.getX(), holderPos.getY(), holderPos.getZ());
+ BlockState realBlock = session.getChunkCache().getBlockAt(pos);
+ UpdateBlockPacket blockPacket = new UpdateBlockPacket();
+ blockPacket.setDataLayer(0);
+ blockPacket.setBlockPosition(holderPos);
+ blockPacket.setRuntimeId(BlockTranslator.getBedrockBlockId(realBlock));
+ session.getUpstream().sendPacket(blockPacket);
+
+ holderPos = holderPos.add(Vector3i.UNIT_X);
+ pos = new Position(holderPos.getX(), holderPos.getY(), holderPos.getZ());
+ realBlock = session.getChunkCache().getBlockAt(pos);
+ blockPacket = new UpdateBlockPacket();
+ blockPacket.setDataLayer(0);
+ blockPacket.setBlockPosition(holderPos);
+ blockPacket.setRuntimeId(BlockTranslator.getBedrockBlockId(realBlock));
+ session.getUpstream().sendPacket(blockPacket);
+ }
+
+ @Override
+ public void updateInventory(GeyserSession session, Inventory inventory) {
+ updater.updateInventory(this, session, inventory);
+ }
+
+ @Override
+ public void updateSlot(GeyserSession session, Inventory inventory, int slot) {
+ updater.updateSlot(this, session, inventory, slot);
+ }
+}
diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/inventory/EnchantmentInventoryTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/EnchantmentInventoryTranslator.java
new file mode 100644
index 00000000..ba7f8cc7
--- /dev/null
+++ b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/EnchantmentInventoryTranslator.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright (c) 2019-2020 GeyserMC. http://geysermc.org
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ * @author GeyserMC
+ * @link https://github.com/GeyserMC/Geyser
+ */
+
+package org.geysermc.connector.network.translators.inventory;
+
+import com.nukkitx.protocol.bedrock.data.ContainerType;
+import org.geysermc.connector.inventory.Inventory;
+import org.geysermc.connector.network.session.GeyserSession;
+import org.geysermc.connector.network.translators.inventory.updater.ContainerInventoryUpdater;
+
+public class EnchantmentInventoryTranslator extends BlockInventoryTranslator {
+ public EnchantmentInventoryTranslator() {
+ super(2, "minecraft:enchanting_table", ContainerType.ENCHANTMENT, new ContainerInventoryUpdater());
+ }
+
+ @Override
+ public void updateProperty(GeyserSession session, Inventory inventory, int key, int value) {
+
+ }
+}
diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/inventory/FurnaceInventoryTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/FurnaceInventoryTranslator.java
new file mode 100644
index 00000000..9b45201e
--- /dev/null
+++ b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/FurnaceInventoryTranslator.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright (c) 2019-2020 GeyserMC. http://geysermc.org
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ * @author GeyserMC
+ * @link https://github.com/GeyserMC/Geyser
+ */
+
+package org.geysermc.connector.network.translators.inventory;
+
+import com.github.steveice10.mc.protocol.data.game.window.WindowType;
+import com.nukkitx.protocol.bedrock.data.ContainerType;
+import com.nukkitx.protocol.bedrock.packet.ContainerSetDataPacket;
+import org.geysermc.connector.inventory.Inventory;
+import org.geysermc.connector.network.session.GeyserSession;
+import org.geysermc.connector.network.translators.inventory.updater.ContainerInventoryUpdater;
+
+public class FurnaceInventoryTranslator extends BlockInventoryTranslator {
+ public FurnaceInventoryTranslator() {
+ super(3, "minecraft:furnace[facing=north,lit=false]", ContainerType.FURNACE, new ContainerInventoryUpdater());
+ }
+
+ @Override
+ public void updateProperty(GeyserSession session, Inventory inventory, int key, int value) {
+ ContainerSetDataPacket dataPacket = new ContainerSetDataPacket();
+ dataPacket.setWindowId((byte) inventory.getId());
+ switch (key) {
+ case 0:
+ dataPacket.setProperty(ContainerSetDataPacket.FURNACE_LIT_TIME);
+ break;
+ case 1:
+ dataPacket.setProperty(ContainerSetDataPacket.FURNACE_LIT_DURATION);
+ break;
+ case 2:
+ dataPacket.setProperty(ContainerSetDataPacket.FURNACE_TICK_COUNT);
+ if (inventory.getWindowType() == WindowType.BLAST_FURNACE || inventory.getWindowType() == WindowType.SMOKER) {
+ value *= 2;
+ }
+ break;
+ default:
+ return;
+ }
+ dataPacket.setValue(value);
+ session.getUpstream().sendPacket(dataPacket);
+ }
+
+ @Override
+ public SlotType getSlotType(int javaSlot) {
+ if (javaSlot == 2)
+ return SlotType.FURNACE_OUTPUT;
+ return SlotType.NORMAL;
+ }
+}
diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/inventory/InventoryTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/InventoryTranslator.java
index f17aba57..2a5afb8c 100644
--- a/connector/src/main/java/org/geysermc/connector/network/translators/inventory/InventoryTranslator.java
+++ b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/InventoryTranslator.java
@@ -25,14 +25,25 @@
package org.geysermc.connector.network.translators.inventory;
+import com.nukkitx.protocol.bedrock.data.InventoryActionData;
+import lombok.AllArgsConstructor;
import org.geysermc.connector.inventory.Inventory;
import org.geysermc.connector.network.session.GeyserSession;
+import java.util.List;
+
+@AllArgsConstructor
public abstract class InventoryTranslator {
+ public final int size;
public abstract void prepareInventory(GeyserSession session, Inventory inventory);
public abstract void openInventory(GeyserSession session, Inventory inventory);
+ public abstract void closeInventory(GeyserSession session, Inventory inventory);
+ public abstract void updateProperty(GeyserSession session, Inventory inventory, int key, int value);
public abstract void updateInventory(GeyserSession session, Inventory inventory);
public abstract void updateSlot(GeyserSession session, Inventory inventory, int slot);
-
+ public abstract int bedrockSlotToJava(InventoryActionData action);
+ public abstract int javaSlotToBedrock(int slot);
+ public abstract SlotType getSlotType(int javaSlot);
+ public abstract void translateActions(GeyserSession session, Inventory inventory, List actions);
}
diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/inventory/PlayerInventoryTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/PlayerInventoryTranslator.java
new file mode 100644
index 00000000..555e80a0
--- /dev/null
+++ b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/PlayerInventoryTranslator.java
@@ -0,0 +1,225 @@
+/*
+ * Copyright (c) 2019-2020 GeyserMC. http://geysermc.org
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ * @author GeyserMC
+ * @link https://github.com/GeyserMC/Geyser
+ */
+
+package org.geysermc.connector.network.translators.inventory;
+
+import com.github.steveice10.mc.protocol.data.game.entity.metadata.ItemStack;
+import com.github.steveice10.mc.protocol.data.game.entity.player.GameMode;
+import com.github.steveice10.mc.protocol.packet.ingame.client.window.ClientCreativeInventoryActionPacket;
+import com.nukkitx.protocol.bedrock.data.*;
+import com.nukkitx.protocol.bedrock.packet.InventoryContentPacket;
+import com.nukkitx.protocol.bedrock.packet.InventorySlotPacket;
+import org.geysermc.connector.inventory.Inventory;
+import org.geysermc.connector.network.session.GeyserSession;
+import org.geysermc.connector.network.translators.TranslatorsInit;
+import org.geysermc.connector.network.translators.inventory.action.InventoryActionDataTranslator;
+import org.geysermc.connector.utils.InventoryUtils;
+
+import java.util.List;
+
+public class PlayerInventoryTranslator extends InventoryTranslator {
+ public PlayerInventoryTranslator() {
+ super(46);
+ }
+
+ @Override
+ public void updateInventory(GeyserSession session, Inventory inventory) {
+ // Crafting grid
+ for (int i = 1; i < 5; i++) {
+ InventorySlotPacket slotPacket = new InventorySlotPacket();
+ slotPacket.setContainerId(ContainerId.CURSOR);
+ slotPacket.setSlot(i + 27);
+ slotPacket.setItem(TranslatorsInit.getItemTranslator().translateToBedrock(inventory.getItem(i)));
+ session.getUpstream().sendPacket(slotPacket);
+ }
+
+ InventoryContentPacket inventoryContentPacket = new InventoryContentPacket();
+ inventoryContentPacket.setContainerId(ContainerId.INVENTORY);
+ ItemData[] contents = new ItemData[36];
+ // Inventory
+ for (int i = 9; i < 36; i++) {
+ contents[i] = TranslatorsInit.getItemTranslator().translateToBedrock(inventory.getItem(i));
+ }
+ // Hotbar
+ for (int i = 36; i < 45; i++) {
+ contents[i - 36] = TranslatorsInit.getItemTranslator().translateToBedrock(inventory.getItem(i));
+ }
+ inventoryContentPacket.setContents(contents);
+ session.getUpstream().sendPacket(inventoryContentPacket);
+
+ // Armor
+ InventoryContentPacket armorContentPacket = new InventoryContentPacket();
+ armorContentPacket.setContainerId(ContainerId.ARMOR);
+ contents = new ItemData[4];
+ for (int i = 5; i < 9; i++) {
+ contents[i - 5] = TranslatorsInit.getItemTranslator().translateToBedrock(inventory.getItem(i));
+ }
+ armorContentPacket.setContents(contents);
+ session.getUpstream().sendPacket(armorContentPacket);
+
+ // Offhand
+ InventoryContentPacket offhandPacket = new InventoryContentPacket();
+ offhandPacket.setContainerId(ContainerId.OFFHAND);
+ offhandPacket.setContents(new ItemData[]{TranslatorsInit.getItemTranslator().translateToBedrock(inventory.getItem(45))});
+ session.getUpstream().sendPacket(offhandPacket);
+ }
+
+ @Override
+ public void updateSlot(GeyserSession session, Inventory inventory, int slot) {
+ if (slot >= 1 && slot <= 44) {
+ InventorySlotPacket slotPacket = new InventorySlotPacket();
+ if (slot >= 9) {
+ slotPacket.setContainerId(ContainerId.INVENTORY);
+ if (slot >= 36) {
+ slotPacket.setSlot(slot - 36);
+ } else {
+ slotPacket.setSlot(slot);
+ }
+ } else if (slot >= 5) {
+ slotPacket.setContainerId(ContainerId.ARMOR);
+ slotPacket.setSlot(slot - 5);
+ } else {
+ slotPacket.setContainerId(ContainerId.CURSOR);
+ slotPacket.setSlot(slot + 27);
+ }
+ slotPacket.setItem(TranslatorsInit.getItemTranslator().translateToBedrock(inventory.getItem(slot)));
+ session.getUpstream().sendPacket(slotPacket);
+ } else if (slot == 45) {
+ InventoryContentPacket offhandPacket = new InventoryContentPacket();
+ offhandPacket.setContainerId(ContainerId.OFFHAND);
+ offhandPacket.setContents(new ItemData[]{TranslatorsInit.getItemTranslator().translateToBedrock(inventory.getItem(slot))});
+ session.getUpstream().sendPacket(offhandPacket);
+ }
+ }
+
+ @Override
+ public int bedrockSlotToJava(InventoryActionData action) {
+ int slotnum = action.getSlot();
+ switch (action.getSource().getContainerId()) {
+ case ContainerId.INVENTORY:
+ // Inventory
+ if (slotnum >= 9 && slotnum <= 35) {
+ return slotnum;
+ }
+ // Hotbar
+ if (slotnum >= 0 && slotnum <= 8) {
+ return slotnum + 36;
+ }
+ break;
+ case ContainerId.ARMOR:
+ if (slotnum >= 0 && slotnum <= 3) {
+ return slotnum + 5;
+ }
+ break;
+ case ContainerId.OFFHAND:
+ return 45;
+ case ContainerId.CURSOR:
+ if (slotnum >= 28 && 31 >= slotnum) {
+ return slotnum - 27;
+ } else if (slotnum == 50) {
+ return 0;
+ }
+ break;
+ }
+ return slotnum;
+ }
+
+ @Override
+ public int javaSlotToBedrock(int slot) {
+ return slot;
+ }
+
+ @Override
+ public SlotType getSlotType(int javaSlot) {
+ if (javaSlot == 0)
+ return SlotType.OUTPUT;
+ return SlotType.NORMAL;
+ }
+
+ @Override
+ public void translateActions(GeyserSession session, Inventory inventory, List actions) {
+ if (session.getGameMode() == GameMode.CREATIVE) {
+ //crafting grid is not visible in creative mode in java edition
+ for (InventoryActionData action : actions) {
+ if (action.getSource().getContainerId() == ContainerId.CURSOR && (action.getSlot() >= 28 && 31 >= action.getSlot())) {
+ updateInventory(session, inventory);
+ InventoryUtils.updateCursor(session);
+ return;
+ }
+ }
+
+ ItemStack javaItem;
+ for (InventoryActionData action : actions) {
+ switch (action.getSource().getContainerId()) {
+ case ContainerId.INVENTORY:
+ case ContainerId.ARMOR:
+ case ContainerId.OFFHAND:
+ int javaSlot = bedrockSlotToJava(action);
+ if (action.getToItem().getId() == 0) {
+ javaItem = new ItemStack(-1, 0, null);
+ } else {
+ javaItem = TranslatorsInit.getItemTranslator().translateToJava(action.getToItem());
+ }
+ ClientCreativeInventoryActionPacket creativePacket = new ClientCreativeInventoryActionPacket(javaSlot, javaItem);
+ session.getDownstream().getSession().send(creativePacket);
+ inventory.setItem(javaSlot, javaItem);
+ break;
+ case ContainerId.CURSOR:
+ if (action.getSlot() == 0) {
+ session.getInventory().setCursor(TranslatorsInit.getItemTranslator().translateToJava(action.getToItem()));
+ }
+ break;
+ case ContainerId.NONE:
+ if (action.getSource().getType() == InventorySource.Type.WORLD_INTERACTION
+ && action.getSource().getFlag() == InventorySource.Flag.DROP_ITEM) {
+ javaItem = TranslatorsInit.getItemTranslator().translateToJava(action.getToItem());
+ ClientCreativeInventoryActionPacket creativeDropPacket = new ClientCreativeInventoryActionPacket(-1, javaItem);
+ session.getDownstream().getSession().send(creativeDropPacket);
+ }
+ break;
+ }
+ }
+ return;
+ }
+
+ InventoryActionDataTranslator.translate(this, session, inventory, actions);
+ }
+
+ @Override
+ public void prepareInventory(GeyserSession session, Inventory inventory) {
+ }
+
+ @Override
+ public void openInventory(GeyserSession session, Inventory inventory) {
+ }
+
+ @Override
+ public void closeInventory(GeyserSession session, Inventory inventory) {
+ }
+
+ @Override
+ public void updateProperty(GeyserSession session, Inventory inventory, int key, int value) {
+ }
+}
diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/inventory/SingleChestInventoryTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/SingleChestInventoryTranslator.java
new file mode 100644
index 00000000..5c99b012
--- /dev/null
+++ b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/SingleChestInventoryTranslator.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright (c) 2019-2020 GeyserMC. http://geysermc.org
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ * @author GeyserMC
+ * @link https://github.com/GeyserMC/Geyser
+ */
+
+package org.geysermc.connector.network.translators.inventory;
+
+import com.nukkitx.protocol.bedrock.data.ContainerType;
+import org.geysermc.connector.network.translators.inventory.updater.ChestInventoryUpdater;
+
+public class SingleChestInventoryTranslator extends BlockInventoryTranslator {
+ public SingleChestInventoryTranslator(int size) {
+ super(size, "minecraft:chest[facing=north,type=single,waterlogged=false]", ContainerType.CONTAINER, new ChestInventoryUpdater(27));
+ }
+}
diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/inventory/SlotType.java b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/SlotType.java
new file mode 100644
index 00000000..045adbd3
--- /dev/null
+++ b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/SlotType.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright (c) 2019-2020 GeyserMC. http://geysermc.org
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ * @author GeyserMC
+ * @link https://github.com/GeyserMC/Geyser
+ */
+
+package org.geysermc.connector.network.translators.inventory;
+
+public enum SlotType {
+ NORMAL,
+ OUTPUT,
+ FURNACE_OUTPUT
+}
diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/inventory/action/Click.java b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/action/Click.java
new file mode 100644
index 00000000..1fdfa364
--- /dev/null
+++ b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/action/Click.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright (c) 2019-2020 GeyserMC. http://geysermc.org
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ * @author GeyserMC
+ * @link https://github.com/GeyserMC/Geyser
+ */
+
+package org.geysermc.connector.network.translators.inventory.action;
+
+import com.github.steveice10.mc.protocol.data.game.window.ClickItemParam;
+import com.github.steveice10.mc.protocol.data.game.window.WindowActionParam;
+import lombok.AllArgsConstructor;
+
+@AllArgsConstructor
+enum Click {
+ LEFT(ClickItemParam.LEFT_CLICK),
+ RIGHT(ClickItemParam.RIGHT_CLICK);
+
+ public final WindowActionParam actionParam;
+}
diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/inventory/action/ClickPlan.java b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/action/ClickPlan.java
new file mode 100644
index 00000000..cdc42f96
--- /dev/null
+++ b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/action/ClickPlan.java
@@ -0,0 +1,125 @@
+/*
+ * Copyright (c) 2019-2020 GeyserMC. http://geysermc.org
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ * @author GeyserMC
+ * @link https://github.com/GeyserMC/Geyser
+ */
+
+package org.geysermc.connector.network.translators.inventory.action;
+
+import com.github.steveice10.mc.protocol.data.game.entity.metadata.ItemStack;
+import com.github.steveice10.mc.protocol.data.game.window.WindowAction;
+import com.github.steveice10.mc.protocol.packet.ingame.client.window.ClientConfirmTransactionPacket;
+import com.github.steveice10.mc.protocol.packet.ingame.client.window.ClientWindowActionPacket;
+import org.geysermc.connector.inventory.Inventory;
+import org.geysermc.connector.inventory.PlayerInventory;
+import org.geysermc.connector.network.session.GeyserSession;
+import org.geysermc.connector.network.translators.inventory.InventoryTranslator;
+import org.geysermc.connector.network.translators.inventory.SlotType;
+import org.geysermc.connector.utils.InventoryUtils;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.ListIterator;
+
+class ClickPlan {
+ private final List plan = new ArrayList<>();
+
+ public void add(Click click, int slot) {
+ plan.add(new ClickAction(click, slot));
+ }
+
+ public void execute(GeyserSession session, InventoryTranslator translator, Inventory inventory, boolean refresh) {
+ PlayerInventory playerInventory = session.getInventory();
+ ListIterator planIter = plan.listIterator();
+ while (planIter.hasNext()) {
+ final ClickAction action = planIter.next();
+ final ItemStack cursorItem = playerInventory.getCursor();
+ final ItemStack clickedItem = inventory.getItem(action.slot);
+ final short actionId = (short) inventory.getTransactionId().getAndIncrement();
+
+ //TODO: stop relying on refreshing the inventory for crafting to work properly
+ if (translator.getSlotType(action.slot) != SlotType.NORMAL)
+ refresh = true;
+
+ ClientWindowActionPacket clickPacket = new ClientWindowActionPacket(inventory.getId(),
+ actionId, action.slot, !planIter.hasNext() && refresh ? InventoryUtils.REFRESH_ITEM : clickedItem,
+ WindowAction.CLICK_ITEM, action.click.actionParam);
+
+ if (translator.getSlotType(action.slot) == SlotType.OUTPUT) {
+ if (cursorItem == null && clickedItem != null) {
+ playerInventory.setCursor(clickedItem);
+ } else if (InventoryUtils.canStack(cursorItem, clickedItem)) {
+ playerInventory.setCursor(new ItemStack(cursorItem.getId(),
+ cursorItem.getAmount() + clickedItem.getAmount(), cursorItem.getNbt()));
+ }
+ } else {
+ switch (action.click) {
+ case LEFT:
+ if (!InventoryUtils.canStack(cursorItem, clickedItem)) {
+ playerInventory.setCursor(clickedItem);
+ inventory.setItem(action.slot, cursorItem);
+ } else {
+ playerInventory.setCursor(null);
+ inventory.setItem(action.slot, new ItemStack(clickedItem.getId(),
+ clickedItem.getAmount() + cursorItem.getAmount(), clickedItem.getNbt()));
+ }
+ break;
+ case RIGHT:
+ if (cursorItem == null && clickedItem != null) {
+ ItemStack halfItem = new ItemStack(clickedItem.getId(),
+ clickedItem.getAmount() / 2, clickedItem.getNbt());
+ inventory.setItem(action.slot, halfItem);
+ playerInventory.setCursor(new ItemStack(clickedItem.getId(),
+ clickedItem.getAmount() - halfItem.getAmount(), clickedItem.getNbt()));
+ } else if (cursorItem != null && clickedItem == null) {
+ playerInventory.setCursor(new ItemStack(cursorItem.getId(),
+ cursorItem.getAmount() - 1, cursorItem.getNbt()));
+ inventory.setItem(action.slot, new ItemStack(cursorItem.getId(),
+ 1, cursorItem.getNbt()));
+ } else if (InventoryUtils.canStack(cursorItem, clickedItem)) {
+ playerInventory.setCursor(new ItemStack(cursorItem.getId(),
+ cursorItem.getAmount() - 1, cursorItem.getNbt()));
+ inventory.setItem(action.slot, new ItemStack(clickedItem.getId(),
+ clickedItem.getAmount() + 1, clickedItem.getNbt()));
+ }
+ break;
+ }
+ }
+ session.getDownstream().getSession().send(clickPacket);
+ session.getDownstream().getSession().send(new ClientConfirmTransactionPacket(inventory.getId(), actionId, true));
+ }
+
+ /*if (refresh) {
+ translator.updateInventory(session, inventory);
+ InventoryUtils.updateCursor(session);
+ }*/
+ }
+
+ private static class ClickAction {
+ final Click click;
+ final int slot;
+ ClickAction(Click click, int slot) {
+ this.click = click;
+ this.slot = slot;
+ }
+ }
+}
diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/inventory/action/InventoryActionDataTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/action/InventoryActionDataTranslator.java
new file mode 100644
index 00000000..a94b6242
--- /dev/null
+++ b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/action/InventoryActionDataTranslator.java
@@ -0,0 +1,330 @@
+/*
+ * Copyright (c) 2019-2020 GeyserMC. http://geysermc.org
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ * @author GeyserMC
+ * @link https://github.com/GeyserMC/Geyser
+ */
+
+package org.geysermc.connector.network.translators.inventory.action;
+
+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.PlayerAction;
+import com.github.steveice10.mc.protocol.data.game.window.*;
+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.window.ClientWindowActionPacket;
+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 org.geysermc.connector.inventory.Inventory;
+import org.geysermc.connector.network.session.GeyserSession;
+import org.geysermc.connector.network.translators.TranslatorsInit;
+import org.geysermc.connector.network.translators.inventory.InventoryTranslator;
+import org.geysermc.connector.network.translators.inventory.SlotType;
+import org.geysermc.connector.utils.InventoryUtils;
+
+import java.util.*;
+
+public class InventoryActionDataTranslator {
+ public static void translate(InventoryTranslator translator, GeyserSession session, Inventory inventory, List actions) {
+ if (actions.size() != 2)
+ return;
+
+ InventoryActionData worldAction = null;
+ InventoryActionData cursorAction = null;
+ InventoryActionData containerAction = null;
+ boolean refresh = false;
+ for (InventoryActionData action : actions) {
+ if (action.getSource().getContainerId() == ContainerId.CRAFTING_USE_INGREDIENT || action.getSource().getContainerId() == ContainerId.CRAFTING_RESULT) {
+ return;
+ } else if (action.getSource().getType() == InventorySource.Type.WORLD_INTERACTION) {
+ worldAction = action;
+ } else if (action.getSource().getContainerId() == ContainerId.CURSOR && action.getSlot() == 0) {
+ cursorAction = action;
+ ItemData translatedCursor = TranslatorsInit.getItemTranslator().translateToBedrock(session.getInventory().getCursor());
+ if (!translatedCursor.equals(action.getFromItem())) {
+ refresh = true;
+ }
+ } else {
+ containerAction = action;
+ ItemData translatedItem = TranslatorsInit.getItemTranslator().translateToBedrock(inventory.getItem(translator.bedrockSlotToJava(action)));
+ if (!translatedItem.equals(action.getFromItem())) {
+ refresh = true;
+ }
+ }
+ }
+
+ final int craftSlot = session.getCraftSlot();
+ session.setCraftSlot(0);
+
+ if (worldAction != null) {
+ InventoryActionData sourceAction;
+ if (cursorAction != null) {
+ sourceAction = cursorAction;
+ } else {
+ sourceAction = containerAction;
+ }
+
+ if (sourceAction != null) {
+ if (worldAction.getSource().getFlag() == InventorySource.Flag.DROP_ITEM) {
+ //quick dropping from hotbar?
+ if (session.getInventoryCache().getOpenInventory() == null && sourceAction.getSource().getContainerId() == ContainerId.INVENTORY) {
+ int heldSlot = session.getInventory().getHeldItemSlot();
+ if (sourceAction.getSlot() == heldSlot) {
+ ClientPlayerActionPacket actionPacket = new ClientPlayerActionPacket(
+ sourceAction.getToItem().getCount() == 0 ? PlayerAction.DROP_ITEM_STACK : PlayerAction.DROP_ITEM,
+ new Position(0, 0, 0), BlockFace.DOWN);
+ session.getDownstream().getSession().send(actionPacket);
+ ItemStack item = session.getInventory().getItem(heldSlot);
+ if (item != null) {
+ session.getInventory().setItem(heldSlot, new ItemStack(item.getId(), item.getAmount() - 1, item.getNbt()));
+ }
+ return;
+ }
+ }
+ int dropAmount = sourceAction.getFromItem().getCount() - sourceAction.getToItem().getCount();
+ if (sourceAction != cursorAction) { //dropping directly from inventory
+ int javaSlot = translator.bedrockSlotToJava(sourceAction);
+ if (dropAmount == sourceAction.getFromItem().getCount()) {
+ ClientWindowActionPacket dropPacket = new ClientWindowActionPacket(inventory.getId(),
+ inventory.getTransactionId().getAndIncrement(),
+ javaSlot, null, WindowAction.DROP_ITEM,
+ DropItemParam.DROP_SELECTED_STACK);
+ session.getDownstream().getSession().send(dropPacket);
+ } else {
+ for (int i = 0; i < dropAmount; i++) {
+ ClientWindowActionPacket dropPacket = new ClientWindowActionPacket(inventory.getId(),
+ inventory.getTransactionId().getAndIncrement(),
+ javaSlot, null, WindowAction.DROP_ITEM,
+ DropItemParam.DROP_FROM_SELECTED);
+ session.getDownstream().getSession().send(dropPacket);
+ }
+ }
+ ItemStack item = session.getInventory().getItem(javaSlot);
+ if (item != null) {
+ session.getInventory().setItem(javaSlot, new ItemStack(item.getId(), item.getAmount() - dropAmount, item.getNbt()));
+ }
+ return;
+ } else { //clicking outside of inventory
+ ClientWindowActionPacket dropPacket = new ClientWindowActionPacket(inventory.getId(), inventory.getTransactionId().getAndIncrement(),
+ -999, null, WindowAction.CLICK_ITEM,
+ dropAmount > 1 ? ClickItemParam.LEFT_CLICK : ClickItemParam.RIGHT_CLICK);
+ session.getDownstream().getSession().send(dropPacket);
+ ItemStack cursor = session.getInventory().getCursor();
+ if (cursor != null) {
+ session.getInventory().setCursor(new ItemStack(cursor.getId(), dropAmount > 1 ? 0 : cursor.getAmount() - 1, cursor.getNbt()));
+ }
+ return;
+ }
+ }
+ }
+ } else if (cursorAction != null && containerAction != null) {
+ //left/right click
+ ClickPlan plan = new ClickPlan();
+ int javaSlot = translator.bedrockSlotToJava(containerAction);
+ if (cursorAction.getFromItem().equals(containerAction.getToItem())
+ && containerAction.getFromItem().equals(cursorAction.getToItem())
+ && !InventoryUtils.canStack(cursorAction.getFromItem(), containerAction.getFromItem())) { //simple swap
+ plan.add(Click.LEFT, javaSlot);
+ } else if (cursorAction.getFromItem().getCount() > cursorAction.getToItem().getCount()) { //release
+ if (cursorAction.getToItem().getCount() == 0) {
+ plan.add(Click.LEFT, javaSlot);
+ } else {
+ int difference = cursorAction.getFromItem().getCount() - cursorAction.getToItem().getCount();
+ for (int i = 0; i < difference; i++) {
+ plan.add(Click.RIGHT, javaSlot);
+ }
+ }
+ } else { //pickup
+ if (cursorAction.getFromItem().getCount() == 0) {
+ if (containerAction.getToItem().getCount() == 0) { //pickup all
+ plan.add(Click.LEFT, javaSlot);
+ } else { //pickup some
+ if (translator.getSlotType(javaSlot) == SlotType.FURNACE_OUTPUT
+ || containerAction.getToItem().getCount() == containerAction.getFromItem().getCount() / 2) { //right click
+ plan.add(Click.RIGHT, javaSlot);
+ } else {
+ plan.add(Click.LEFT, javaSlot);
+ int difference = containerAction.getFromItem().getCount() - cursorAction.getToItem().getCount();
+ for (int i = 0; i < difference; i++) {
+ plan.add(Click.RIGHT, javaSlot);
+ }
+ }
+ }
+ } else { //pickup into non-empty cursor
+ if (translator.getSlotType(javaSlot) == SlotType.FURNACE_OUTPUT) {
+ if (containerAction.getToItem().getCount() == 0) {
+ plan.add(Click.LEFT, javaSlot);
+ } else {
+ ClientWindowActionPacket shiftClickPacket = new ClientWindowActionPacket(inventory.getId(),
+ inventory.getTransactionId().getAndIncrement(),
+ javaSlot, InventoryUtils.REFRESH_ITEM, WindowAction.SHIFT_CLICK_ITEM,
+ ShiftClickItemParam.LEFT_CLICK);
+ session.getDownstream().getSession().send(shiftClickPacket);
+ translator.updateInventory(session, inventory);
+ return;
+ }
+ } else if (translator.getSlotType(javaSlot) == SlotType.OUTPUT) {
+ plan.add(Click.LEFT, javaSlot);
+ } else {
+ int cursorSlot = findTempSlot(inventory, session.getInventory().getCursor(), Collections.singletonList(javaSlot));
+ if (cursorSlot != -1) {
+ plan.add(Click.LEFT, cursorSlot);
+ } else {
+ translator.updateInventory(session, inventory);
+ return;
+ }
+ plan.add(Click.LEFT, javaSlot);
+ int difference = cursorAction.getToItem().getCount() - cursorAction.getFromItem().getCount();
+ for (int i = 0; i < difference; i++) {
+ plan.add(Click.RIGHT, cursorSlot);
+ }
+ plan.add(Click.LEFT, javaSlot);
+ plan.add(Click.LEFT, cursorSlot);
+ }
+ }
+ }
+ plan.execute(session, translator, inventory, refresh);
+ return;
+ } else {
+ ClickPlan plan = new ClickPlan();
+ InventoryActionData fromAction;
+ InventoryActionData toAction;
+ if (actions.get(0).getFromItem().getCount() >= actions.get(0).getToItem().getCount()) {
+ fromAction = actions.get(0);
+ toAction = actions.get(1);
+ } else {
+ fromAction = actions.get(1);
+ toAction = actions.get(0);
+ }
+ int fromSlot = translator.bedrockSlotToJava(fromAction);
+ int toSlot = translator.bedrockSlotToJava(toAction);
+
+ if (translator.getSlotType(fromSlot) == SlotType.OUTPUT) {
+ if ((craftSlot != 0 && craftSlot != -2) && (inventory.getItem(toSlot) == null
+ || InventoryUtils.canStack(session.getInventory().getCursor(), inventory.getItem(toSlot)))) {
+ if (fromAction.getToItem().getCount() == 0) {
+ refresh = true;
+ plan.add(Click.LEFT, toSlot);
+ if (craftSlot != -1) {
+ plan.add(Click.LEFT, craftSlot);
+ }
+ } else {
+ int difference = toAction.getToItem().getCount() - toAction.getFromItem().getCount();
+ for (int i = 0; i < difference; i++) {
+ plan.add(Click.RIGHT, toSlot);
+ }
+ session.setCraftSlot(craftSlot);
+ }
+ plan.execute(session, translator, inventory, refresh);
+ return;
+ } else {
+ session.setCraftSlot(-2);
+ }
+ }
+
+ int cursorSlot = -1;
+ if (session.getInventory().getCursor() != null) { //move cursor contents to a temporary slot
+ cursorSlot = findTempSlot(inventory, session.getInventory().getCursor(), Arrays.asList(fromSlot, toSlot));
+ if (cursorSlot != -1) {
+ plan.add(Click.LEFT, cursorSlot);
+ } else {
+ translator.updateInventory(session, inventory);
+ return;
+ }
+ }
+ if ((fromAction.getFromItem().equals(toAction.getToItem()) && !InventoryUtils.canStack(fromAction.getFromItem(), toAction.getFromItem()))
+ || fromAction.getToItem().getId() == 0) { //slot swap
+ plan.add(Click.LEFT, fromSlot);
+ plan.add(Click.LEFT, toSlot);
+ if (fromAction.getToItem().getId() != 0) {
+ plan.add(Click.LEFT, fromSlot);
+ }
+ } else if (InventoryUtils.canStack(fromAction.getFromItem(), toAction.getToItem())) { //partial item move
+ if (translator.getSlotType(fromSlot) == SlotType.FURNACE_OUTPUT) {
+ ClientWindowActionPacket shiftClickPacket = new ClientWindowActionPacket(inventory.getId(),
+ inventory.getTransactionId().getAndIncrement(),
+ fromSlot, InventoryUtils.REFRESH_ITEM, WindowAction.SHIFT_CLICK_ITEM,
+ ShiftClickItemParam.LEFT_CLICK);
+ session.getDownstream().getSession().send(shiftClickPacket);
+ translator.updateInventory(session, inventory);
+ return;
+ } else if (translator.getSlotType(fromSlot) == SlotType.OUTPUT) {
+ session.setCraftSlot(cursorSlot);
+ plan.add(Click.LEFT, fromSlot);
+ int difference = toAction.getToItem().getCount() - toAction.getFromItem().getCount();
+ for (int i = 0; i < difference; i++) {
+ plan.add(Click.RIGHT, toSlot);
+ }
+ //client will send additional packets later to finish transferring crafting output
+ //translator will know how to handle this using the craftSlot variable
+ } else {
+ plan.add(Click.LEFT, fromSlot);
+ int difference = toAction.getToItem().getCount() - toAction.getFromItem().getCount();
+ for (int i = 0; i < difference; i++) {
+ plan.add(Click.RIGHT, toSlot);
+ }
+ plan.add(Click.LEFT, fromSlot);
+ }
+ }
+ if (cursorSlot != -1) {
+ plan.add(Click.LEFT, cursorSlot);
+ }
+ plan.execute(session, translator, inventory, refresh);
+ return;
+ }
+
+ translator.updateInventory(session, inventory);
+ InventoryUtils.updateCursor(session);
+ }
+
+ private static int findTempSlot(Inventory inventory, ItemStack item, List slotBlacklist) {
+ /*try and find a slot that can temporarily store the given item
+ only look in the main inventory and hotbar
+ only slots that are empty or contain a different type of item are valid*/
+ int offset = inventory.getId() == 0 ? 1 : 0; //offhand is not a viable slot (some servers disable it)
+ List itemBlacklist = new ArrayList<>(slotBlacklist.size() + 1);
+ itemBlacklist.add(item);
+ for (int slot : slotBlacklist) {
+ ItemStack blacklistItem = inventory.getItem(slot);
+ if (blacklistItem != null)
+ itemBlacklist.add(blacklistItem);
+ }
+ for (int i = inventory.getSize() - (36 + offset); i < inventory.getSize() - offset; i++) {
+ ItemStack testItem = inventory.getItem(i);
+ boolean acceptable = true;
+ if (testItem != null) {
+ for (ItemStack blacklistItem : itemBlacklist) {
+ if (InventoryUtils.canStack(testItem, blacklistItem)) {
+ acceptable = false;
+ break;
+ }
+ }
+ }
+ if (acceptable && !slotBlacklist.contains(i))
+ return i;
+ }
+ //could not find a viable temp slot
+ return -1;
+ }
+}
diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/inventory/holder/BlockInventoryHolder.java b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/holder/BlockInventoryHolder.java
new file mode 100644
index 00000000..a9b0d4ad
--- /dev/null
+++ b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/holder/BlockInventoryHolder.java
@@ -0,0 +1,91 @@
+/*
+ * Copyright (c) 2019-2020 GeyserMC. http://geysermc.org
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ * @author GeyserMC
+ * @link https://github.com/GeyserMC/Geyser
+ */
+
+package org.geysermc.connector.network.translators.inventory.holder;
+
+import com.github.steveice10.mc.protocol.data.game.entity.metadata.Position;
+import com.github.steveice10.mc.protocol.data.game.world.block.BlockState;
+import com.nukkitx.math.vector.Vector3i;
+import com.nukkitx.nbt.tag.CompoundTag;
+import com.nukkitx.protocol.bedrock.data.ContainerType;
+import com.nukkitx.protocol.bedrock.packet.BlockEntityDataPacket;
+import com.nukkitx.protocol.bedrock.packet.ContainerOpenPacket;
+import com.nukkitx.protocol.bedrock.packet.UpdateBlockPacket;
+import lombok.AllArgsConstructor;
+import org.geysermc.connector.inventory.Inventory;
+import org.geysermc.connector.network.session.GeyserSession;
+import org.geysermc.connector.network.translators.block.BlockTranslator;
+import org.geysermc.connector.network.translators.inventory.InventoryTranslator;
+
+@AllArgsConstructor
+public class BlockInventoryHolder extends InventoryHolder {
+ private final int blockId;
+ private final ContainerType containerType;
+
+ @Override
+ public void prepareInventory(InventoryTranslator translator, GeyserSession session, Inventory inventory) {
+ Vector3i position = session.getPlayerEntity().getPosition().toInt();
+ position = position.add(Vector3i.UP);
+ UpdateBlockPacket blockPacket = new UpdateBlockPacket();
+ blockPacket.setDataLayer(0);
+ blockPacket.setBlockPosition(position);
+ blockPacket.setRuntimeId(blockId);
+ blockPacket.getFlags().addAll(UpdateBlockPacket.FLAG_ALL_PRIORITY);
+ session.getUpstream().sendPacket(blockPacket);
+ inventory.setHolderPosition(position);
+
+ CompoundTag tag = CompoundTag.builder()
+ .intTag("x", position.getX())
+ .intTag("y", position.getY())
+ .intTag("z", position.getZ())
+ .stringTag("CustomName", inventory.getTitle()).buildRootTag();
+ BlockEntityDataPacket dataPacket = new BlockEntityDataPacket();
+ dataPacket.setData(tag);
+ dataPacket.setBlockPosition(position);
+ session.getUpstream().sendPacket(dataPacket);
+ }
+
+ @Override
+ public void openInventory(InventoryTranslator translator, GeyserSession session, Inventory inventory) {
+ ContainerOpenPacket containerOpenPacket = new ContainerOpenPacket();
+ containerOpenPacket.setWindowId((byte) inventory.getId());
+ containerOpenPacket.setType((byte) containerType.id());
+ containerOpenPacket.setBlockPosition(inventory.getHolderPosition());
+ containerOpenPacket.setUniqueEntityId(inventory.getHolderId());
+ session.getUpstream().sendPacket(containerOpenPacket);
+ }
+
+ @Override
+ public void closeInventory(InventoryTranslator translator, GeyserSession session, Inventory inventory) {
+ Vector3i holderPos = inventory.getHolderPosition();
+ Position pos = new Position(holderPos.getX(), holderPos.getY(), holderPos.getZ());
+ BlockState realBlock = session.getChunkCache().getBlockAt(pos);
+ UpdateBlockPacket blockPacket = new UpdateBlockPacket();
+ blockPacket.setDataLayer(0);
+ blockPacket.setBlockPosition(holderPos);
+ blockPacket.setRuntimeId(BlockTranslator.getBedrockBlockId(realBlock));
+ session.getUpstream().sendPacket(blockPacket);
+ }
+}
diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/inventory/holder/InventoryHolder.java b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/holder/InventoryHolder.java
new file mode 100644
index 00000000..5a9e736e
--- /dev/null
+++ b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/holder/InventoryHolder.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright (c) 2019-2020 GeyserMC. http://geysermc.org
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ * @author GeyserMC
+ * @link https://github.com/GeyserMC/Geyser
+ */
+
+package org.geysermc.connector.network.translators.inventory.holder;
+
+import org.geysermc.connector.inventory.Inventory;
+import org.geysermc.connector.network.session.GeyserSession;
+import org.geysermc.connector.network.translators.inventory.InventoryTranslator;
+
+public abstract class InventoryHolder {
+ public abstract void prepareInventory(InventoryTranslator translator, GeyserSession session, Inventory inventory);
+ public abstract void openInventory(InventoryTranslator translator, GeyserSession session, Inventory inventory);
+ public abstract void closeInventory(InventoryTranslator translator, GeyserSession session, Inventory inventory);
+}
diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/inventory/GenericInventoryTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/updater/ChestInventoryUpdater.java
similarity index 64%
rename from connector/src/main/java/org/geysermc/connector/network/translators/inventory/GenericInventoryTranslator.java
rename to connector/src/main/java/org/geysermc/connector/network/translators/inventory/updater/ChestInventoryUpdater.java
index 754d05c6..ab45cdc4 100644
--- a/connector/src/main/java/org/geysermc/connector/network/translators/inventory/GenericInventoryTranslator.java
+++ b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/updater/ChestInventoryUpdater.java
@@ -23,38 +23,32 @@
* @link https://github.com/GeyserMC/Geyser
*/
-package org.geysermc.connector.network.translators.inventory;
+package org.geysermc.connector.network.translators.inventory.updater;
-import com.nukkitx.math.vector.Vector3i;
import com.nukkitx.protocol.bedrock.data.ItemData;
-import com.nukkitx.protocol.bedrock.packet.ContainerOpenPacket;
import com.nukkitx.protocol.bedrock.packet.InventoryContentPacket;
import com.nukkitx.protocol.bedrock.packet.InventorySlotPacket;
+import lombok.AllArgsConstructor;
import org.geysermc.connector.inventory.Inventory;
import org.geysermc.connector.network.session.GeyserSession;
import org.geysermc.connector.network.translators.TranslatorsInit;
+import org.geysermc.connector.network.translators.inventory.InventoryTranslator;
-public class GenericInventoryTranslator extends InventoryTranslator {
+@AllArgsConstructor
+public class ChestInventoryUpdater extends InventoryUpdater {
+ private final int paddedSize;
@Override
- public void prepareInventory(GeyserSession session, Inventory inventory) {
- // TODO: Add code here
- }
+ public void updateInventory(InventoryTranslator translator, GeyserSession session, Inventory inventory) {
+ super.updateInventory(translator, session, inventory);
- @Override
- public void openInventory(GeyserSession session, Inventory inventory) {
- ContainerOpenPacket containerOpenPacket = new ContainerOpenPacket();
- containerOpenPacket.setWindowId((byte) inventory.getId());
- containerOpenPacket.setType((byte) 0);
- containerOpenPacket.setBlockPosition(Vector3i.ZERO);
- session.getUpstream().sendPacket(containerOpenPacket);
- }
-
- @Override
- public void updateInventory(GeyserSession session, Inventory inventory) {
- ItemData[] bedrockItems = new ItemData[inventory.getItems().length];
+ ItemData[] bedrockItems = new ItemData[paddedSize];
for (int i = 0; i < bedrockItems.length; i++) {
- bedrockItems[i] = TranslatorsInit.getItemTranslator().translateToBedrock(inventory.getItems()[i]);
+ if (i <= translator.size) {
+ bedrockItems[i] = TranslatorsInit.getItemTranslator().translateToBedrock(inventory.getItem(i));
+ } else {
+ bedrockItems[i] = ItemData.AIR;
+ }
}
InventoryContentPacket contentPacket = new InventoryContentPacket();
@@ -64,11 +58,15 @@ public class GenericInventoryTranslator extends InventoryTranslator {
}
@Override
- public void updateSlot(GeyserSession session, Inventory inventory, int slot) {
+ public boolean updateSlot(InventoryTranslator translator, GeyserSession session, Inventory inventory, int javaSlot) {
+ if (super.updateSlot(translator, session, inventory, javaSlot))
+ return true;
+
InventorySlotPacket slotPacket = new InventorySlotPacket();
slotPacket.setContainerId(inventory.getId());
- slotPacket.setItem(TranslatorsInit.getItemTranslator().translateToBedrock(inventory.getItems()[slot]));
- slotPacket.setSlot(slot);
+ slotPacket.setSlot(translator.javaSlotToBedrock(javaSlot));
+ slotPacket.setItem(TranslatorsInit.getItemTranslator().translateToBedrock(inventory.getItem(javaSlot)));
session.getUpstream().sendPacket(slotPacket);
+ return true;
}
}
diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/inventory/updater/ContainerInventoryUpdater.java b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/updater/ContainerInventoryUpdater.java
new file mode 100644
index 00000000..3efe1cee
--- /dev/null
+++ b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/updater/ContainerInventoryUpdater.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright (c) 2019-2020 GeyserMC. http://geysermc.org
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ * @author GeyserMC
+ * @link https://github.com/GeyserMC/Geyser
+ */
+
+package org.geysermc.connector.network.translators.inventory.updater;
+
+import com.nukkitx.protocol.bedrock.data.ItemData;
+import com.nukkitx.protocol.bedrock.packet.InventoryContentPacket;
+import com.nukkitx.protocol.bedrock.packet.InventorySlotPacket;
+import org.geysermc.connector.inventory.Inventory;
+import org.geysermc.connector.network.session.GeyserSession;
+import org.geysermc.connector.network.translators.TranslatorsInit;
+import org.geysermc.connector.network.translators.inventory.InventoryTranslator;
+
+public class ContainerInventoryUpdater extends InventoryUpdater {
+ @Override
+ public void updateInventory(InventoryTranslator translator, GeyserSession session, Inventory inventory) {
+ super.updateInventory(translator, session, inventory);
+
+ ItemData[] bedrockItems = new ItemData[translator.size];
+ for (int i = 0; i < bedrockItems.length; i++) {
+ bedrockItems[translator.javaSlotToBedrock(i)] = TranslatorsInit.getItemTranslator().translateToBedrock(inventory.getItem(i));
+ }
+
+ InventoryContentPacket contentPacket = new InventoryContentPacket();
+ contentPacket.setContainerId(inventory.getId());
+ contentPacket.setContents(bedrockItems);
+ session.getUpstream().sendPacket(contentPacket);
+ }
+
+ @Override
+ public boolean updateSlot(InventoryTranslator translator, GeyserSession session, Inventory inventory, int javaSlot) {
+ if (super.updateSlot(translator, session, inventory, javaSlot))
+ return true;
+
+ InventorySlotPacket slotPacket = new InventorySlotPacket();
+ slotPacket.setContainerId(inventory.getId());
+ slotPacket.setSlot(translator.javaSlotToBedrock(javaSlot));
+ slotPacket.setItem(TranslatorsInit.getItemTranslator().translateToBedrock(inventory.getItem(javaSlot)));
+ session.getUpstream().sendPacket(slotPacket);
+ return true;
+ }
+}
diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/inventory/updater/CursorInventoryUpdater.java b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/updater/CursorInventoryUpdater.java
new file mode 100644
index 00000000..13b8554b
--- /dev/null
+++ b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/updater/CursorInventoryUpdater.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright (c) 2019-2020 GeyserMC. http://geysermc.org
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ * @author GeyserMC
+ * @link https://github.com/GeyserMC/Geyser
+ */
+
+package org.geysermc.connector.network.translators.inventory.updater;
+
+import com.nukkitx.protocol.bedrock.data.ContainerId;
+import com.nukkitx.protocol.bedrock.packet.InventorySlotPacket;
+import org.geysermc.connector.inventory.Inventory;
+import org.geysermc.connector.network.session.GeyserSession;
+import org.geysermc.connector.network.translators.TranslatorsInit;
+import org.geysermc.connector.network.translators.inventory.InventoryTranslator;
+
+public class CursorInventoryUpdater extends InventoryUpdater {
+ @Override
+ public void updateInventory(InventoryTranslator translator, GeyserSession session, Inventory inventory) {
+ super.updateInventory(translator, session, inventory);
+
+ for (int i = 0; i < translator.size; i++) {
+ final int bedrockSlot = translator.javaSlotToBedrock(i);
+ if (bedrockSlot == 50)
+ continue;
+ InventorySlotPacket slotPacket = new InventorySlotPacket();
+ slotPacket.setContainerId(ContainerId.CURSOR);
+ slotPacket.setSlot(bedrockSlot);
+ slotPacket.setItem(TranslatorsInit.getItemTranslator().translateToBedrock(inventory.getItem(i)));
+ session.getUpstream().sendPacket(slotPacket);
+ }
+ }
+
+ @Override
+ public boolean updateSlot(InventoryTranslator translator, GeyserSession session, Inventory inventory, int javaSlot) {
+ if (super.updateSlot(translator, session, inventory, javaSlot))
+ return true;
+
+ InventorySlotPacket slotPacket = new InventorySlotPacket();
+ slotPacket.setContainerId(ContainerId.CURSOR);
+ slotPacket.setSlot(translator.javaSlotToBedrock(javaSlot));
+ slotPacket.setItem(TranslatorsInit.getItemTranslator().translateToBedrock(inventory.getItem(javaSlot)));
+ session.getUpstream().sendPacket(slotPacket);
+ return true;
+ }
+}
diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/inventory/updater/InventoryUpdater.java b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/updater/InventoryUpdater.java
new file mode 100644
index 00000000..888b14b1
--- /dev/null
+++ b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/updater/InventoryUpdater.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright (c) 2019-2020 GeyserMC. http://geysermc.org
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ * @author GeyserMC
+ * @link https://github.com/GeyserMC/Geyser
+ */
+
+package org.geysermc.connector.network.translators.inventory.updater;
+
+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.geysermc.connector.inventory.Inventory;
+import org.geysermc.connector.network.session.GeyserSession;
+import org.geysermc.connector.network.translators.TranslatorsInit;
+import org.geysermc.connector.network.translators.inventory.InventoryTranslator;
+
+public abstract class InventoryUpdater {
+ public void updateInventory(InventoryTranslator translator, GeyserSession session, Inventory inventory) {
+ ItemData[] bedrockItems = new ItemData[36];
+ for (int i = 0; i < 36; i++) {
+ final int offset = i < 9 ? 27 : -9;
+ bedrockItems[i] = TranslatorsInit.getItemTranslator().translateToBedrock(inventory.getItem(translator.size + i + offset));
+ }
+ InventoryContentPacket contentPacket = new InventoryContentPacket();
+ contentPacket.setContainerId(ContainerId.INVENTORY);
+ contentPacket.setContents(bedrockItems);
+ session.getUpstream().sendPacket(contentPacket);
+ }
+
+ public boolean updateSlot(InventoryTranslator translator, GeyserSession session, Inventory inventory, int javaSlot) {
+ if (javaSlot >= translator.size) {
+ InventorySlotPacket slotPacket = new InventorySlotPacket();
+ slotPacket.setContainerId(ContainerId.INVENTORY);
+ slotPacket.setSlot(translator.javaSlotToBedrock(javaSlot));
+ slotPacket.setItem(TranslatorsInit.getItemTranslator().translateToBedrock(inventory.getItem(javaSlot)));
+ session.getUpstream().sendPacket(slotPacket);
+ return true;
+ }
+ return false;
+ }
+}
diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/item/Enchantment.java b/connector/src/main/java/org/geysermc/connector/network/translators/item/Enchantment.java
new file mode 100644
index 00000000..c5c152a2
--- /dev/null
+++ b/connector/src/main/java/org/geysermc/connector/network/translators/item/Enchantment.java
@@ -0,0 +1,92 @@
+/*
+ * Copyright (c) 2019-2020 GeyserMC. http://geysermc.org
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ * @author GeyserMC
+ * @link https://github.com/GeyserMC/Geyser
+ */
+
+package org.geysermc.connector.network.translators.item;
+
+import lombok.Getter;
+
+import java.util.Locale;
+
+@Getter
+enum Enchantment {
+ PROTECTION,
+ FIRE_PROTECTION,
+ FEATHER_FALLING,
+ BLAST_PROTECTION,
+ PROJECTILE_PROTECTION,
+ THORNS,
+ RESPIRATION,
+ DEPTH_STRIDER,
+ AQUA_AFFINITY,
+ SHARPNESS,
+ SMITE,
+ BANE_OF_ARTHROPODS,
+ KNOCKBACK,
+ FIRE_ASPECT,
+ LOOTING,
+ EFFICIENCY,
+ SILK_TOUCH,
+ UNBREAKING,
+ FORTUNE,
+ POWER,
+ PUNCH,
+ FLAME,
+ INFINITY,
+ LUCK_OF_THE_SEA,
+ LURE,
+ FROST_WALKER,
+ MENDING,
+ BINDING_CURSE,
+ VANISHING_CURSE,
+ IMPALING,
+ RIPTIDE,
+ LOYALTY,
+ CHANNELING,
+ MULTISHOT,
+ PIERCING,
+ QUICK_CHARGE;
+
+ private final String javaIdentifier;
+
+ Enchantment() {
+ this.javaIdentifier = "minecraft:" + this.name().toLowerCase(Locale.ENGLISH);
+ }
+
+ public static Enchantment getByJavaIdentifier(String javaIdentifier) {
+ for (Enchantment enchantment : Enchantment.values()) {
+ if (enchantment.javaIdentifier.equals(javaIdentifier)) {
+ return enchantment;
+ }
+ }
+ return null;
+ }
+
+ public static Enchantment getByBedrockId(int bedrockId) {
+ if (bedrockId >= 0 && bedrockId < Enchantment.values().length) {
+ return Enchantment.values()[bedrockId];
+ }
+ return null;
+ }
+}
diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/item/ItemTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/item/ItemTranslator.java
index 9029ccd5..446b25db 100644
--- a/connector/src/main/java/org/geysermc/connector/network/translators/item/ItemTranslator.java
+++ b/connector/src/main/java/org/geysermc/connector/network/translators/item/ItemTranslator.java
@@ -41,6 +41,7 @@ import com.github.steveice10.opennbt.tag.builtin.ShortTag;
import com.github.steveice10.opennbt.tag.builtin.StringTag;
import com.github.steveice10.opennbt.tag.builtin.Tag;
import com.nukkitx.protocol.bedrock.data.ItemData;
+import org.geysermc.api.Geyser;
import org.geysermc.connector.console.GeyserLogger;
import org.geysermc.connector.utils.MessageUtils;
import org.geysermc.connector.utils.Toolbox;
@@ -57,19 +58,38 @@ public class ItemTranslator {
if (data.getTag() == null) {
return new ItemStack(javaItem.getJavaId(), data.getCount());
+ } else if (javaItem.getJavaIdentifier().equals("minecraft:enchanted_book")) {
+ CompoundTag javaTag = translateToJavaNBT(data.getTag());
+ Map javaValue = javaTag.getValue();
+ Tag enchTag = javaValue.get("Enchantments");
+ if (enchTag instanceof ListTag) {
+ enchTag = new ListTag("StoredEnchantments", ((ListTag) enchTag).getValue());
+ javaValue.remove("Enchantments");
+ javaValue.put("StoredEnchantments", enchTag);
+ javaTag.setValue(javaValue);
+ }
+ return new ItemStack(javaItem.getJavaId(), data.getCount(), javaTag);
}
return new ItemStack(javaItem.getJavaId(), data.getCount(), translateToJavaNBT(data.getTag()));
}
public ItemData translateToBedrock(ItemStack stack) {
- // Most likely dirt if null
if (stack == null) {
- return ItemData.of(3, (short)0, 0);
+ return ItemData.AIR;
}
ItemEntry bedrockItem = getItem(stack);
if (stack.getNbt() == null) {
return ItemData.of(bedrockItem.getBedrockId(), (short) bedrockItem.getBedrockData(), stack.getAmount());
+ } else if (bedrockItem.getJavaIdentifier().endsWith("potion")) {
+ Tag potionTag = stack.getNbt().get("Potion");
+ if (potionTag instanceof StringTag) {
+ Potion potion = Potion.getByJavaIdentifier(((StringTag) potionTag).getValue());
+ if (potion != null) {
+ return ItemData.of(bedrockItem.getBedrockId(), potion.getBedrockId(), stack.getAmount(), translateToBedrockNBT(stack.getNbt()));
+ }
+ Geyser.getLogger().debug("Unknown java potion: " + potionTag.getValue());
+ }
}
return ItemData.of(bedrockItem.getBedrockId(), (short) bedrockItem.getBedrockData(), stack.getAmount(), translateToBedrockNBT(stack.getNbt()));
}
@@ -80,7 +100,7 @@ public class ItemTranslator {
public ItemEntry getItem(ItemData data) {
for (ItemEntry itemEntry : Toolbox.ITEM_ENTRIES.values()) {
- if (itemEntry.getBedrockId() == data.getId() && itemEntry.getBedrockData() == data.getDamage()) {
+ if (itemEntry.getBedrockId() == data.getId() && (itemEntry.getBedrockData() == data.getDamage() || itemEntry.getJavaIdentifier().endsWith("potion"))) {
return itemEntry;
}
}
@@ -99,10 +119,11 @@ public class ItemTranslator {
if (translatedTag == null)
continue;
- javaValue.put(str, translatedTag);
+ javaValue.put(translatedTag.getName(), translatedTag);
}
}
+ javaTag.setValue(javaValue);
return javaTag;
}
@@ -161,6 +182,29 @@ public class ItemTranslator {
com.nukkitx.nbt.tag.ListTag listTag = (com.nukkitx.nbt.tag.ListTag) tag;
List tags = new ArrayList<>();
+
+ if (tag.getName().equals("ench")) {
+ for (Object value : listTag.getValue()) {
+ if (!(value instanceof com.nukkitx.nbt.tag.CompoundTag))
+ continue;
+
+ com.nukkitx.nbt.tag.CompoundTag tagValue = (com.nukkitx.nbt.tag.CompoundTag) value;
+ int bedrockId = tagValue.getShort("id", (short) -1);
+ Enchantment enchantment = Enchantment.getByBedrockId(bedrockId);
+ if (enchantment != null) {
+ CompoundTag javaTag = new CompoundTag("");
+ Map javaValue = javaTag.getValue();
+ javaValue.put("id", new StringTag("id", enchantment.getJavaIdentifier()));
+ javaValue.put("lvl", new IntTag("lvl", tagValue.getShort("lvl", (short) 1)));
+ javaTag.setValue(javaValue);
+ tags.add(javaTag);
+ } else {
+ Geyser.getLogger().debug("Unknown bedrock enchantment: " + bedrockId);
+ }
+ }
+ return new ListTag("Enchantments", tags);
+ }
+
for (Object value : listTag.getValue()) {
if (!(value instanceof com.nukkitx.nbt.tag.Tag))
continue;
@@ -189,7 +233,7 @@ public class ItemTranslator {
if (translatedTag == null)
continue;
- javaValue.put(str, translatedTag);
+ javaValue.put(translatedTag.getName(), translatedTag);
}
}
@@ -250,7 +294,33 @@ public class ItemTranslator {
if (tag instanceof ListTag) {
ListTag listTag = (ListTag) tag;
- if (listTag.getName().equalsIgnoreCase("Lore")) {
+ if (listTag.getName().equalsIgnoreCase("Enchantments") || listTag.getName().equalsIgnoreCase("StoredEnchantments")) {
+ List tags = new ArrayList<>();
+ for (Object value : listTag.getValue()) {
+ if (!(value instanceof CompoundTag))
+ continue;
+
+ Tag javaEnchLvl = ((CompoundTag) value).get("lvl");
+ if (!(javaEnchLvl instanceof ShortTag))
+ continue;
+
+ Tag javaEnchId = ((CompoundTag) value).get("id");
+ if (!(javaEnchId instanceof StringTag))
+ continue;
+
+ Enchantment enchantment = Enchantment.getByJavaIdentifier(((StringTag) javaEnchId).getValue());
+ if (enchantment == null) {
+ Geyser.getLogger().debug("Unknown java enchantment: " + javaEnchId.getValue());
+ continue;
+ }
+
+ com.nukkitx.nbt.CompoundTagBuilder builder = com.nukkitx.nbt.tag.CompoundTag.EMPTY.toBuilder();
+ builder.shortTag("lvl", ((ShortTag) javaEnchLvl).getValue());
+ builder.shortTag("id", (short) enchantment.ordinal());
+ tags.add(builder.buildRootTag());
+ }
+ return new com.nukkitx.nbt.tag.ListTag<>("ench", com.nukkitx.nbt.tag.CompoundTag.class, tags);
+ } else if (listTag.getName().equalsIgnoreCase("Lore")) {
List tags = new ArrayList<>();
for (Object value : listTag.getValue()) {
if (!(value instanceof Tag))
diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/item/Potion.java b/connector/src/main/java/org/geysermc/connector/network/translators/item/Potion.java
new file mode 100644
index 00000000..f711d3ea
--- /dev/null
+++ b/connector/src/main/java/org/geysermc/connector/network/translators/item/Potion.java
@@ -0,0 +1,102 @@
+/*
+ * Copyright (c) 2019-2020 GeyserMC. http://geysermc.org
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ * @author GeyserMC
+ * @link https://github.com/GeyserMC/Geyser
+ */
+
+package org.geysermc.connector.network.translators.item;
+
+import lombok.Getter;
+
+import java.util.Locale;
+
+@Getter
+enum Potion {
+ WATER(0),
+ MUNDANE(1),
+ THICK(3),
+ AWKWARD(4),
+ NIGHT_VISION(5),
+ LONG_NIGHT_VISION(6),
+ INVISIBILITY(7),
+ LONG_INVISIBILITY(8),
+ LEAPING(9),
+ STRONG_LEAPING(11),
+ LONG_LEAPING(10),
+ FIRE_RESISTANCE(12),
+ LONG_FIRE_RESISTANCE(13),
+ SWIFTNESS(14),
+ STRONG_SWIFTNESS(16),
+ LONG_SWIFTNESS(15),
+ SLOWNESS(17),
+ STRONG_SLOWNESS(18), //does not exist
+ LONG_SLOWNESS(18),
+ WATER_BREATHING(19),
+ LONG_WATER_BREATHING(20),
+ HEALING(21),
+ STRONG_HEALING(22),
+ HARMING(23),
+ STRONG_HARMING(24),
+ POISON(25),
+ STRONG_POISON(27),
+ LONG_POISON(26),
+ REGENERATION(28),
+ STRONG_REGENERATION(30),
+ LONG_REGENERATION(29),
+ STRENGTH(31),
+ STRONG_STRENGTH(33),
+ LONG_STRENGTH(32),
+ WEAKNESS(34),
+ LONG_WEAKNESS(35),
+ LUCK(2), //does not exist
+ TURTLE_MASTER(37),
+ STRONG_TURTLE_MASTER(39),
+ LONG_TURTLE_MASTER(38),
+ SLOW_FALLING(40),
+ LONG_SLOW_FALLING(41);
+
+ private final String javaIdentifier;
+ private final short bedrockId;
+
+ Potion(int bedrockId) {
+ this.javaIdentifier = "minecraft:" + this.name().toLowerCase(Locale.ENGLISH);
+ this.bedrockId = (short) bedrockId;
+ }
+
+ public static Potion getByJavaIdentifier(String javaIdentifier) {
+ for (Potion potion : Potion.values()) {
+ if (potion.javaIdentifier.equals(javaIdentifier)) {
+ return potion;
+ }
+ }
+ return null;
+ }
+
+ public static Potion getByBedrockId(short bedrockId) {
+ for (Potion potion : Potion.values()) {
+ if (potion.bedrockId == bedrockId) {
+ return potion;
+ }
+ }
+ return null;
+ }
+}
diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/java/JavaDeclareRecipesTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/java/JavaDeclareRecipesTranslator.java
new file mode 100644
index 00000000..9399d1dd
--- /dev/null
+++ b/connector/src/main/java/org/geysermc/connector/network/translators/java/JavaDeclareRecipesTranslator.java
@@ -0,0 +1,172 @@
+/*
+ * Copyright (c) 2019-2020 GeyserMC. http://geysermc.org
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ * @author GeyserMC
+ * @link https://github.com/GeyserMC/Geyser
+ */
+
+package org.geysermc.connector.network.translators.java;
+
+import com.github.steveice10.mc.protocol.data.game.recipe.Ingredient;
+import com.github.steveice10.mc.protocol.data.game.recipe.Recipe;
+import com.github.steveice10.mc.protocol.data.game.recipe.data.ShapedRecipeData;
+import com.github.steveice10.mc.protocol.data.game.recipe.data.ShapelessRecipeData;
+import com.github.steveice10.mc.protocol.packet.ingame.server.ServerDeclareRecipesPacket;
+import com.nukkitx.nbt.tag.CompoundTag;
+import com.nukkitx.protocol.bedrock.data.CraftingData;
+import com.nukkitx.protocol.bedrock.data.ItemData;
+import com.nukkitx.protocol.bedrock.data.PotionMixData;
+import com.nukkitx.protocol.bedrock.packet.CraftingDataPacket;
+import it.unimi.dsi.fastutil.ints.IntOpenHashSet;
+import it.unimi.dsi.fastutil.ints.IntSet;
+import lombok.AllArgsConstructor;
+import lombok.EqualsAndHashCode;
+import org.geysermc.connector.network.session.GeyserSession;
+import org.geysermc.connector.network.translators.PacketTranslator;
+import org.geysermc.connector.network.translators.TranslatorsInit;
+import org.geysermc.connector.network.translators.item.ItemEntry;
+import org.geysermc.connector.utils.Toolbox;
+
+import java.util.*;
+import java.util.stream.Collectors;
+
+public class JavaDeclareRecipesTranslator extends PacketTranslator {
+ private static final Collection POTION_MIXES =
+ Arrays.stream(new int[]{372, 331, 348, 376, 289, 437, 353, 414, 382, 375, 462, 378, 396, 377, 370, 469, 470})
+ .mapToObj(ingredient -> new PotionMixData(0, ingredient, 0))
+ .collect(Collectors.toList());
+
+ @Override
+ public void translate(ServerDeclareRecipesPacket packet, GeyserSession session) {
+ CraftingDataPacket craftingDataPacket = new CraftingDataPacket();
+ craftingDataPacket.setCleanRecipes(true);
+ for (Recipe recipe : packet.getRecipes()) {
+ switch (recipe.getType()) {
+ case CRAFTING_SHAPELESS: {
+ ShapelessRecipeData shapelessRecipeData = (ShapelessRecipeData) recipe.getData();
+ ItemData output = TranslatorsInit.getItemTranslator().translateToBedrock(shapelessRecipeData.getResult());
+ output = ItemData.of(output.getId(), output.getDamage(), output.getCount()); //strip NBT
+ ItemData[][] inputCombinations = combinations(shapelessRecipeData.getIngredients());
+ for (ItemData[] inputs : inputCombinations) {
+ UUID uuid = UUID.randomUUID();
+ craftingDataPacket.getCraftingData().add(CraftingData.fromShapeless(uuid.toString(),
+ inputs, new ItemData[]{output}, uuid, "crafting_table", 0));
+ }
+ break;
+ }
+ case CRAFTING_SHAPED: {
+ ShapedRecipeData shapedRecipeData = (ShapedRecipeData) recipe.getData();
+ ItemData output = TranslatorsInit.getItemTranslator().translateToBedrock(shapedRecipeData.getResult());
+ output = ItemData.of(output.getId(), output.getDamage(), output.getCount()); //strip NBT
+ ItemData[][] inputCombinations = combinations(shapedRecipeData.getIngredients());
+ for (ItemData[] inputs : inputCombinations) {
+ UUID uuid = UUID.randomUUID();
+ craftingDataPacket.getCraftingData().add(CraftingData.fromShaped(uuid.toString(),
+ shapedRecipeData.getWidth(), shapedRecipeData.getHeight(), inputs,
+ new ItemData[]{output}, uuid, "crafting_table", 0));
+ }
+ break;
+ }
+ }
+ }
+ craftingDataPacket.getPotionMixData().addAll(POTION_MIXES);
+ session.getUpstream().sendPacket(craftingDataPacket);
+ }
+
+ //TODO: rewrite
+ private ItemData[][] combinations(Ingredient[] ingredients) {
+ Map, IntSet> squashedOptions = new HashMap<>();
+ for (int i = 0; i < ingredients.length; i++) {
+ if (ingredients[i].getOptions().length == 0) {
+ squashedOptions.computeIfAbsent(Collections.singleton(ItemData.AIR), k -> new IntOpenHashSet()).add(i);
+ continue;
+ }
+ Ingredient ingredient = ingredients[i];
+ Map> groupedByIds = Arrays.stream(ingredient.getOptions())
+ .map(item -> TranslatorsInit.getItemTranslator().translateToBedrock(item))
+ .collect(Collectors.groupingBy(item -> new GroupedItem(item.getId(), item.getCount(), item.getTag())));
+ Set optionSet = new HashSet<>(groupedByIds.size());
+ for (Map.Entry> entry : groupedByIds.entrySet()) {
+ if (entry.getValue().size() > 1) {
+ GroupedItem groupedItem = entry.getKey();
+ int idCount = 0;
+ //not optimal
+ for (ItemEntry itemEntry : Toolbox.ITEM_ENTRIES.values()) {
+ if (itemEntry.getBedrockId() == groupedItem.id) {
+ idCount++;
+ }
+ }
+ if (entry.getValue().size() < idCount) {
+ optionSet.addAll(entry.getValue());
+ } else {
+ optionSet.add(ItemData.of(groupedItem.id, (short) -1, groupedItem.count, groupedItem.tag));
+ }
+ } else {
+ ItemData item = entry.getValue().get(0);
+ optionSet.add(item);
+ }
+ }
+ squashedOptions.computeIfAbsent(optionSet, k -> new IntOpenHashSet()).add(i);
+ }
+ int totalCombinations = 1;
+ for (Set optionSet : squashedOptions.keySet()) {
+ totalCombinations *= optionSet.size();
+ }
+ if (totalCombinations > 500) {
+ ItemData[] translatedItems = new ItemData[ingredients.length];
+ for (int i = 0; i < ingredients.length; i++) {
+ if (ingredients[i].getOptions().length > 0) {
+ translatedItems[i] = TranslatorsInit.getItemTranslator().translateToBedrock(ingredients[i].getOptions()[0]);
+ } else {
+ translatedItems[i] = ItemData.AIR;
+ }
+ }
+ return new ItemData[][]{translatedItems};
+ }
+ List> sortedSets = new ArrayList<>(squashedOptions.keySet());
+ sortedSets.sort(Comparator.comparing(Set::size, Comparator.reverseOrder()));
+ ItemData[][] combinations = new ItemData[totalCombinations][ingredients.length];
+ int x = 1;
+ for (Set set : sortedSets) {
+ IntSet slotSet = squashedOptions.get(set);
+ int i = 0;
+ for (ItemData item : set) {
+ for (int j = 0; j < totalCombinations / set.size(); j++) {
+ final int comboIndex = (i * x) + (j % x) + ((j / x) * set.size() * x);
+ for (int slot : slotSet) {
+ combinations[comboIndex][slot] = item;
+ }
+ }
+ i++;
+ }
+ x *= set.size();
+ }
+ return combinations;
+ }
+
+ @EqualsAndHashCode
+ @AllArgsConstructor
+ private static class GroupedItem {
+ int id;
+ int count;
+ CompoundTag tag;
+ }
+}
diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/java/JavaRespawnTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/java/JavaRespawnTranslator.java
index 285f9d85..0cc7156a 100644
--- a/connector/src/main/java/org/geysermc/connector/network/translators/java/JavaRespawnTranslator.java
+++ b/connector/src/main/java/org/geysermc/connector/network/translators/java/JavaRespawnTranslator.java
@@ -45,6 +45,8 @@ public class JavaRespawnTranslator extends PacketTranslator
// Max health must be divisible by two in bedrock
entity.getAttributes().put(AttributeType.HEALTH, AttributeType.HEALTH.getAttribute(maxHealth, (maxHealth % 2 == 1 ? maxHealth + 1 : maxHealth)));
+ session.getInventoryCache().setOpenInventory(null);
+
SetPlayerGameTypePacket playerGameTypePacket = new SetPlayerGameTypePacket();
playerGameTypePacket.setGamemode(packet.getGamemode().ordinal());
session.getUpstream().sendPacket(playerGameTypePacket);
diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/java/entity/player/JavaPlayerChangeHeldItemTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/java/entity/player/JavaPlayerChangeHeldItemTranslator.java
new file mode 100644
index 00000000..90042e32
--- /dev/null
+++ b/connector/src/main/java/org/geysermc/connector/network/translators/java/entity/player/JavaPlayerChangeHeldItemTranslator.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright (c) 2019-2020 GeyserMC. http://geysermc.org
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ * @author GeyserMC
+ * @link https://github.com/GeyserMC/Geyser
+ */
+
+package org.geysermc.connector.network.translators.java.entity.player;
+
+import com.github.steveice10.mc.protocol.packet.ingame.server.entity.player.ServerPlayerChangeHeldItemPacket;
+import com.nukkitx.protocol.bedrock.packet.PlayerHotbarPacket;
+import org.geysermc.connector.network.session.GeyserSession;
+import org.geysermc.connector.network.translators.PacketTranslator;
+
+public class JavaPlayerChangeHeldItemTranslator extends PacketTranslator {
+
+ @Override
+ public void translate(ServerPlayerChangeHeldItemPacket packet, GeyserSession session) {
+ PlayerHotbarPacket hotbarPacket = new PlayerHotbarPacket();
+ hotbarPacket.setContainerId(0);
+ hotbarPacket.setSelectedHotbarSlot(packet.getSlot());
+ hotbarPacket.setSelectHotbarSlot(true);
+ session.getUpstream().sendPacket(hotbarPacket);
+
+ session.getInventory().setHeldItemSlot(packet.getSlot());
+ }
+}
diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/java/inventory/OpenWindowPacketTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/java/inventory/OpenWindowPacketTranslator.java
deleted file mode 100644
index 1b6ab8dc..00000000
--- a/connector/src/main/java/org/geysermc/connector/network/translators/java/inventory/OpenWindowPacketTranslator.java
+++ /dev/null
@@ -1,22 +0,0 @@
-package org.geysermc.connector.network.translators.java.inventory;
-
-import com.github.steveice10.mc.protocol.packet.ingame.server.window.ServerOpenWindowPacket;
-import com.nukkitx.protocol.bedrock.data.ContainerId;
-import com.nukkitx.protocol.bedrock.packet.ContainerOpenPacket;
-import com.nukkitx.protocol.bedrock.packet.InventoryContentPacket;
-import org.geysermc.connector.inventory.Inventory;
-import org.geysermc.connector.network.session.GeyserSession;
-import org.geysermc.connector.network.translators.PacketTranslator;
-import org.geysermc.connector.network.translators.TranslatorsInit;
-import org.geysermc.connector.network.translators.inventory.InventoryTranslator;
-
-public class OpenWindowPacketTranslator extends PacketTranslator {
- @Override
- public void translate(ServerOpenWindowPacket packet, GeyserSession session) {
- System.out.println("debug: " + packet.getType());
- InventoryTranslator translator = TranslatorsInit.getInventoryTranslator();
-
- translator.openInventory(session, new Inventory(packet.getName(), packet.getWindowId(), packet.getType(), 54));
-
- }
-}
diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/java/window/JavaCloseWindowTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/java/window/JavaCloseWindowTranslator.java
new file mode 100644
index 00000000..cb532e60
--- /dev/null
+++ b/connector/src/main/java/org/geysermc/connector/network/translators/java/window/JavaCloseWindowTranslator.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright (c) 2019-2020 GeyserMC. http://geysermc.org
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ * @author GeyserMC
+ * @link https://github.com/GeyserMC/Geyser
+ */
+
+package org.geysermc.connector.network.translators.java.window;
+
+import com.github.steveice10.mc.protocol.packet.ingame.server.window.ServerCloseWindowPacket;
+import com.nukkitx.protocol.bedrock.packet.ContainerClosePacket;
+import org.geysermc.connector.network.session.GeyserSession;
+import org.geysermc.connector.network.translators.PacketTranslator;
+import org.geysermc.connector.utils.InventoryUtils;
+
+public class JavaCloseWindowTranslator extends PacketTranslator {
+
+ @Override
+ public void translate(ServerCloseWindowPacket packet, GeyserSession session) {
+ ContainerClosePacket closePacket = new ContainerClosePacket();
+ closePacket.setWindowId((byte)packet.getWindowId());
+ session.getUpstream().sendPacket(closePacket);
+ InventoryUtils.closeInventory(session, packet.getWindowId());
+ }
+}
diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/java/window/JavaConfirmTransactionTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/java/window/JavaConfirmTransactionTranslator.java
new file mode 100644
index 00000000..10c85de4
--- /dev/null
+++ b/connector/src/main/java/org/geysermc/connector/network/translators/java/window/JavaConfirmTransactionTranslator.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright (c) 2019-2020 GeyserMC. http://geysermc.org
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ * @author GeyserMC
+ * @link https://github.com/GeyserMC/Geyser
+ */
+
+package org.geysermc.connector.network.translators.java.window;
+
+import com.github.steveice10.mc.protocol.packet.ingame.client.window.ClientConfirmTransactionPacket;
+import com.github.steveice10.mc.protocol.packet.ingame.server.window.ServerConfirmTransactionPacket;
+import org.geysermc.connector.inventory.Inventory;
+import org.geysermc.connector.network.session.GeyserSession;
+import org.geysermc.connector.network.translators.PacketTranslator;
+import org.geysermc.connector.network.translators.inventory.InventoryTranslator;
+import org.geysermc.connector.utils.InventoryUtils;
+
+public class JavaConfirmTransactionTranslator extends PacketTranslator {
+
+ @Override
+ public void translate(ServerConfirmTransactionPacket packet, GeyserSession session) {
+ if (!packet.isAccepted()) {
+ ClientConfirmTransactionPacket confirmPacket = new ClientConfirmTransactionPacket(packet.getWindowId(), packet.getActionId(), true);
+ session.getDownstream().getSession().send(confirmPacket);
+ }
+ }
+}
diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/java/window/JavaOpenWindowTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/java/window/JavaOpenWindowTranslator.java
index a22998d5..4190ac5a 100644
--- a/connector/src/main/java/org/geysermc/connector/network/translators/java/window/JavaOpenWindowTranslator.java
+++ b/connector/src/main/java/org/geysermc/connector/network/translators/java/window/JavaOpenWindowTranslator.java
@@ -25,15 +25,66 @@
package org.geysermc.connector.network.translators.java.window;
+import com.github.steveice10.mc.protocol.packet.ingame.client.window.ClientCloseWindowPacket;
import com.github.steveice10.mc.protocol.packet.ingame.server.window.ServerOpenWindowPacket;
+import com.google.gson.JsonObject;
+import com.google.gson.JsonParser;
+import com.nukkitx.protocol.bedrock.packet.ContainerClosePacket;
+import org.geysermc.api.Geyser;
+import org.geysermc.connector.inventory.Inventory;
import org.geysermc.connector.network.session.GeyserSession;
import org.geysermc.connector.network.translators.PacketTranslator;
+import org.geysermc.connector.network.translators.TranslatorsInit;
+import org.geysermc.connector.network.translators.inventory.InventoryTranslator;
import org.geysermc.connector.utils.InventoryUtils;
+import java.util.concurrent.TimeUnit;
+
public class JavaOpenWindowTranslator extends PacketTranslator {
@Override
public void translate(ServerOpenWindowPacket packet, GeyserSession session) {
- InventoryUtils.openInventory(session, packet);
+ if (packet.getWindowId() == 0) {
+ return;
+ }
+ InventoryTranslator newTranslator = TranslatorsInit.getInventoryTranslators().get(packet.getType());
+ Inventory openInventory = session.getInventoryCache().getOpenInventory();
+ if (newTranslator == null) {
+ if (openInventory != null) {
+ ContainerClosePacket closePacket = new ContainerClosePacket();
+ closePacket.setWindowId((byte)openInventory.getId());
+ session.getUpstream().sendPacket(closePacket);
+ TranslatorsInit.getInventoryTranslators().get(openInventory.getWindowType()).closeInventory(session, openInventory);
+ }
+ ClientCloseWindowPacket closeWindowPacket = new ClientCloseWindowPacket(packet.getWindowId());
+ session.getDownstream().getSession().send(closeWindowPacket);
+ return;
+ }
+
+ String name = packet.getName();
+ try {
+ JsonParser parser = new JsonParser();
+ JsonObject jsonObject = parser.parse(packet.getName()).getAsJsonObject();
+ if (jsonObject.has("text")) {
+ name = jsonObject.get("text").getAsString();
+ } else if (jsonObject.has("translate")) {
+ name = jsonObject.get("translate").getAsString();
+ }
+ } catch (Exception e) {
+ Geyser.getLogger().debug("JavaOpenWindowTranslator: " + e.toString());
+ }
+
+ Inventory newInventory = new Inventory(name, packet.getWindowId(), packet.getType(), newTranslator.size + 36);
+ session.getInventoryCache().cacheInventory(newInventory);
+ if (openInventory != null) {
+ InventoryTranslator openTranslator = TranslatorsInit.getInventoryTranslators().get(openInventory.getWindowType());
+ if (!openTranslator.getClass().equals(newTranslator.getClass())) {
+ InventoryUtils.closeInventory(session, openInventory.getId());
+ Geyser.getGeneralThreadPool().schedule(() -> InventoryUtils.openInventory(session, newInventory), 500, TimeUnit.MILLISECONDS);
+ return;
+ }
+ }
+
+ InventoryUtils.openInventory(session, newInventory);
}
}
diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/java/window/JavaSetSlotTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/java/window/JavaSetSlotTranslator.java
index 18e309d8..21c7217a 100644
--- a/connector/src/main/java/org/geysermc/connector/network/translators/java/window/JavaSetSlotTranslator.java
+++ b/connector/src/main/java/org/geysermc/connector/network/translators/java/window/JavaSetSlotTranslator.java
@@ -25,44 +25,39 @@
package org.geysermc.connector.network.translators.java.window;
-import com.github.steveice10.mc.protocol.data.game.entity.metadata.ItemStack;
import com.github.steveice10.mc.protocol.packet.ingame.server.window.ServerSetSlotPacket;
import org.geysermc.connector.inventory.Inventory;
import org.geysermc.connector.network.session.GeyserSession;
-import org.geysermc.connector.network.session.cache.InventoryCache;
import org.geysermc.connector.network.translators.PacketTranslator;
+import org.geysermc.connector.network.translators.TranslatorsInit;
+import org.geysermc.connector.network.translators.inventory.InventoryTranslator;
import org.geysermc.connector.utils.InventoryUtils;
+import java.util.Objects;
+
public class JavaSetSlotTranslator extends PacketTranslator {
@Override
public void translate(ServerSetSlotPacket packet, GeyserSession session) {
- InventoryCache inventoryCache = session.getInventoryCache();
- if (!inventoryCache.getInventories().containsKey(packet.getWindowId())) {
- inventoryCache.cachePacket(packet.getWindowId(), packet);
+ if (packet.getWindowId() == 255 && packet.getSlot() == -1) { //cursor
+ if (Objects.equals(session.getInventory().getCursor(), packet.getItem()))
+ return;
+ if (session.getCraftSlot() != 0)
+ return;
+
+ session.getInventory().setCursor(packet.getItem());
+ InventoryUtils.updateCursor(session);
return;
}
- Inventory inventory = inventoryCache.getInventories().get(packet.getWindowId());
- if (packet.getWindowId() != 0 && inventory.getWindowType() == null)
+ Inventory inventory = session.getInventoryCache().getInventories().get(packet.getWindowId());
+ if (inventory == null || (packet.getWindowId() != 0 && inventory.getWindowType() == null))
return;
- // Player inventory
- if (packet.getWindowId() == 0) {
- if (packet.getSlot() >= inventory.getItems().length)
- return; // Most likely not a player inventory
-
- ItemStack[] items = inventory.getItems();
- items[packet.getSlot()] = packet.getItem();
- inventory.setItems(items);
-
- InventoryUtils.refreshPlayerInventory(session, inventory);
-
- if (inventory.isOpen()) {
- InventoryUtils.updateSlot(session, packet);
- } else {
- inventoryCache.cachePacket(packet.getWindowId(), packet);
- }
+ InventoryTranslator translator = TranslatorsInit.getInventoryTranslators().get(inventory.getWindowType());
+ if (translator != null) {
+ inventory.setItem(packet.getSlot(), packet.getItem());
+ translator.updateSlot(session, inventory, packet.getSlot());
}
}
}
diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/java/window/JavaWindowItemsTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/java/window/JavaWindowItemsTranslator.java
index b5cd605e..801ab5d6 100644
--- a/connector/src/main/java/org/geysermc/connector/network/translators/java/window/JavaWindowItemsTranslator.java
+++ b/connector/src/main/java/org/geysermc/connector/network/translators/java/window/JavaWindowItemsTranslator.java
@@ -28,28 +28,29 @@ package org.geysermc.connector.network.translators.java.window;
import com.github.steveice10.mc.protocol.packet.ingame.server.window.ServerWindowItemsPacket;
import org.geysermc.connector.inventory.Inventory;
import org.geysermc.connector.network.session.GeyserSession;
-import org.geysermc.connector.network.session.cache.InventoryCache;
import org.geysermc.connector.network.translators.PacketTranslator;
-import org.geysermc.connector.utils.InventoryUtils;
+import org.geysermc.connector.network.translators.TranslatorsInit;
+import org.geysermc.connector.network.translators.inventory.InventoryTranslator;
+
+import java.util.Arrays;
public class JavaWindowItemsTranslator extends PacketTranslator {
@Override
public void translate(ServerWindowItemsPacket packet, GeyserSession session) {
- InventoryCache inventoryCache = session.getInventoryCache();
- if (!inventoryCache.getInventories().containsKey(packet.getWindowId())) {
- inventoryCache.cachePacket(packet.getWindowId(), packet);
+ Inventory inventory = session.getInventoryCache().getInventories().get(packet.getWindowId());
+ if (inventory == null || (packet.getWindowId() != 0 && inventory.getWindowType() == null))
return;
- }
- Inventory inventory = inventoryCache.getInventories().get(packet.getWindowId());
- // Player inventory
- if (packet.getWindowId() == 0) {
+ if (packet.getItems().length < inventory.getSize()) {
+ inventory.setItems(Arrays.copyOf(packet.getItems(), inventory.getSize()));
+ } else {
inventory.setItems(packet.getItems());
- InventoryUtils.refreshPlayerInventory(session, inventory);
- return;
}
- InventoryUtils.updateInventory(session, packet);
+ InventoryTranslator translator = TranslatorsInit.getInventoryTranslators().get(inventory.getWindowType());
+ if (translator != null) {
+ translator.updateInventory(session, inventory);
+ }
}
}
diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/java/window/JavaWindowPropertyTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/java/window/JavaWindowPropertyTranslator.java
new file mode 100644
index 00000000..d7e2292e
--- /dev/null
+++ b/connector/src/main/java/org/geysermc/connector/network/translators/java/window/JavaWindowPropertyTranslator.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright (c) 2019-2020 GeyserMC. http://geysermc.org
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ * @author GeyserMC
+ * @link https://github.com/GeyserMC/Geyser
+ */
+
+package org.geysermc.connector.network.translators.java.window;
+
+import com.github.steveice10.mc.protocol.packet.ingame.server.window.ServerWindowPropertyPacket;
+import org.geysermc.connector.inventory.Inventory;
+import org.geysermc.connector.network.session.GeyserSession;
+import org.geysermc.connector.network.translators.PacketTranslator;
+import org.geysermc.connector.network.translators.TranslatorsInit;
+import org.geysermc.connector.network.translators.inventory.InventoryTranslator;
+
+public class JavaWindowPropertyTranslator extends PacketTranslator {
+
+ @Override
+ public void translate(ServerWindowPropertyPacket packet, GeyserSession session) {
+ Inventory inventory = session.getInventoryCache().getInventories().get(packet.getWindowId());
+ if (inventory == null || (packet.getWindowId() != 0 && inventory.getWindowType() == null))
+ return;
+
+ InventoryTranslator translator = TranslatorsInit.getInventoryTranslators().get(inventory.getWindowType());
+ if (translator != null) {
+ translator.updateProperty(session, inventory, packet.getRawProperty(), packet.getValue());
+ }
+ }
+}
diff --git a/connector/src/main/java/org/geysermc/connector/utils/InventoryUtils.java b/connector/src/main/java/org/geysermc/connector/utils/InventoryUtils.java
index 8db3bc24..8eb407d0 100644
--- a/connector/src/main/java/org/geysermc/connector/utils/InventoryUtils.java
+++ b/connector/src/main/java/org/geysermc/connector/utils/InventoryUtils.java
@@ -26,113 +26,76 @@
package org.geysermc.connector.utils;
import com.github.steveice10.mc.protocol.data.game.entity.metadata.ItemStack;
-import com.github.steveice10.mc.protocol.packet.ingame.client.window.ClientCloseWindowPacket;
-import com.github.steveice10.mc.protocol.packet.ingame.server.window.ServerOpenWindowPacket;
-import com.github.steveice10.mc.protocol.packet.ingame.server.window.ServerSetSlotPacket;
-import com.github.steveice10.mc.protocol.packet.ingame.server.window.ServerWindowItemsPacket;
-import com.github.steveice10.packetlib.packet.Packet;
+import com.github.steveice10.opennbt.tag.builtin.CompoundTag;
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.geysermc.api.Geyser;
import org.geysermc.connector.inventory.Inventory;
import org.geysermc.connector.network.session.GeyserSession;
import org.geysermc.connector.network.translators.TranslatorsInit;
+import org.geysermc.connector.network.translators.inventory.DoubleChestInventoryTranslator;
import org.geysermc.connector.network.translators.inventory.InventoryTranslator;
-import java.util.List;
+import java.util.Objects;
import java.util.concurrent.TimeUnit;
public class InventoryUtils {
+ public static final ItemStack REFRESH_ITEM = new ItemStack(1, 127, new CompoundTag("")); //TODO: stop using this
- public static void refreshPlayerInventory(GeyserSession session, Inventory inventory) {
- InventoryContentPacket inventoryContentPacket = new InventoryContentPacket();
- inventoryContentPacket.setContainerId(ContainerId.INVENTORY);
-
- ItemData[] contents = new ItemData[40];
- // Inventory
- for (int i = 9; i < 36; i++) {
- contents[i] = TranslatorsInit.getItemTranslator().translateToBedrock(inventory.getItems()[i]);
+ public static void openInventory(GeyserSession session, Inventory inventory) {
+ InventoryTranslator translator = TranslatorsInit.getInventoryTranslators().get(inventory.getWindowType());
+ if (translator != null) {
+ session.getInventoryCache().setOpenInventory(inventory);
+ translator.prepareInventory(session, inventory);
+ //TODO: find better way to handle double chest delay
+ if (translator instanceof DoubleChestInventoryTranslator) {
+ Geyser.getGeneralThreadPool().schedule(() -> {
+ translator.openInventory(session, inventory);
+ translator.updateInventory(session, inventory);
+ }, 200, TimeUnit.MILLISECONDS);
+ } else {
+ translator.openInventory(session, inventory);
+ translator.updateInventory(session, inventory);
+ }
}
-
- // Hotbar
- for (int i = 36; i < 45; i++) {
- contents[i - 36] = TranslatorsInit.getItemTranslator().translateToBedrock(inventory.getItems()[i]);
- }
-
- // Armor
- for (int i = 5; i < 9; i++) {
- contents[i + 31] = TranslatorsInit.getItemTranslator().translateToBedrock(inventory.getItems()[i]);
- }
-
- inventoryContentPacket.setContents(contents);
- session.getUpstream().sendPacket(inventoryContentPacket);
}
- public static void openInventory(GeyserSession session, ServerOpenWindowPacket packet) {
- Inventory inventory = new Inventory(packet.getWindowId(), packet.getType(), 45); // TODO: Find a way to set this value
- session.getInventoryCache().getInventories().put(packet.getWindowId(), inventory);
- session.getInventoryCache().setOpenInventory(inventory);
-
- InventoryTranslator translator = TranslatorsInit.getInventoryTranslator();
- translator.prepareInventory(session, inventory);
- Geyser.getGeneralThreadPool().schedule(() -> {
- List packets = session.getInventoryCache().getCachedPackets().get(inventory.getId());
- packets.forEach(itemPacket -> {
- if (itemPacket != null) {
- if (ServerWindowItemsPacket.class.isAssignableFrom(itemPacket.getClass())) {
- updateInventory(session, (ServerWindowItemsPacket) itemPacket);
- }
- }
- });
- }, 200, TimeUnit.MILLISECONDS);
+ public static void closeInventory(GeyserSession session, int windowId) {
+ if (windowId != 0) {
+ Inventory inventory = session.getInventoryCache().getInventories().get(windowId);
+ if (inventory != null) {
+ InventoryTranslator translator = TranslatorsInit.getInventoryTranslators().get(inventory.getWindowType());
+ translator.closeInventory(session, inventory);
+ session.getInventoryCache().uncacheInventory(windowId);
+ session.getInventoryCache().setOpenInventory(null);
+ }
+ } else {
+ Inventory inventory = session.getInventory();
+ InventoryTranslator translator = TranslatorsInit.getInventoryTranslators().get(inventory.getWindowType());
+ translator.updateInventory(session, inventory);
+ }
+ session.setCraftSlot(0);
+ session.getInventory().setCursor(null);
}
- public static void updateInventory(GeyserSession session, ServerWindowItemsPacket packet) {
- if (packet.getWindowId() == 0)
- return;
-
- if (session.getInventoryCache().getOpenInventory() == null || !session.getInventoryCache().getInventories().containsKey(packet.getWindowId()))
- return;
-
- Inventory openInventory = session.getInventoryCache().getOpenInventory();
- if (packet.getWindowId() != openInventory.getId())
- return;
-
- InventoryTranslator translator = TranslatorsInit.getInventoryTranslator();
- if (translator == null) {
- session.getDownstream().getSession().send(new ClientCloseWindowPacket(packet.getWindowId()));
- return;
- }
-
- openInventory.setItems(packet.getItems());
- translator.updateInventory(session, openInventory);
+ public static void updateCursor(GeyserSession session) {
+ InventorySlotPacket cursorPacket = new InventorySlotPacket();
+ cursorPacket.setContainerId(ContainerId.CURSOR);
+ cursorPacket.setSlot(0);
+ cursorPacket.setItem(TranslatorsInit.getItemTranslator().translateToBedrock(session.getInventory().getCursor()));
+ session.getUpstream().sendPacket(cursorPacket);
}
- public static void updateSlot(GeyserSession session, ServerSetSlotPacket packet) {
- if (packet.getWindowId() == 0)
- return;
+ public static boolean canStack(ItemStack item1, ItemStack item2) {
+ if (item1 == null || item2 == null)
+ return false;
+ return item1.getId() == item2.getId() && Objects.equals(item1.getNbt(), item2.getNbt());
+ }
- if (session.getInventoryCache().getOpenInventory() == null || !session.getInventoryCache().getInventories().containsKey(packet.getWindowId()))
- return;
-
- Inventory openInventory = session.getInventoryCache().getOpenInventory();
- if (packet.getWindowId() != openInventory.getId())
- return;
-
- InventoryTranslator translator = TranslatorsInit.getInventoryTranslator();
- if (translator == null) {
- session.getDownstream().getSession().send(new ClientCloseWindowPacket(packet.getWindowId()));
- return;
- }
-
- if (packet.getSlot() >= openInventory.getSize()) {
- session.getDownstream().getSession().send(new ClientCloseWindowPacket(packet.getWindowId()));
- return;
- }
-
- ItemStack[] items = openInventory.getItems();
- items[packet.getSlot()] = packet.getItem();
- translator.updateSlot(session, openInventory, packet.getSlot());
+ public static boolean canStack(ItemData item1, ItemData item2) {
+ if (item1 == null || item2 == null)
+ return false;
+ return item1.equals(item2, false, true, true);
}
}
diff --git a/connector/src/main/java/org/geysermc/connector/utils/Toolbox.java b/connector/src/main/java/org/geysermc/connector/utils/Toolbox.java
index 5dba02b6..f9900c66 100644
--- a/connector/src/main/java/org/geysermc/connector/utils/Toolbox.java
+++ b/connector/src/main/java/org/geysermc/connector/utils/Toolbox.java
@@ -32,6 +32,7 @@ import com.fasterxml.jackson.databind.ObjectMapper;
import com.nukkitx.nbt.NbtUtils;
import com.nukkitx.nbt.stream.NBTInputStream;
import com.nukkitx.nbt.tag.CompoundTag;
+import com.nukkitx.protocol.bedrock.data.ItemData;
import com.nukkitx.protocol.bedrock.packet.StartGamePacket;
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
@@ -39,6 +40,8 @@ import org.geysermc.connector.GeyserConnector;
import org.geysermc.connector.console.GeyserLogger;
import org.geysermc.connector.network.translators.item.ItemEntry;
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
import java.io.InputStream;
import java.util.*;
@@ -46,6 +49,7 @@ public class Toolbox {
public static final ObjectMapper JSON_MAPPER = new ObjectMapper().disable(DeserializationFeature.FAIL_ON_IGNORED_PROPERTIES);
public static final CompoundTag BIOMES;
+ public static final ItemData[] CREATIVE_ITEMS;
public static final Collection ITEMS = new ArrayList<>();
@@ -102,6 +106,36 @@ public class Toolbox {
entry.getValue().get("bedrock_id").intValue(), entry.getValue().get("bedrock_data").intValue()));
itemIndex++;
}
+
+ stream = getResource("bedrock/creative_items.json");
+
+ JsonNode creativeItemEntries;
+ try {
+ creativeItemEntries = JSON_MAPPER.readTree(stream).get("items");
+ } catch (Exception e) {
+ throw new AssertionError("Unable to load creative items", e);
+ }
+
+ List creativeItems = new ArrayList<>();
+ for (JsonNode itemNode : creativeItemEntries) {
+ short damage = 0;
+ if (itemNode.has("damage")) {
+ damage = itemNode.get("damage").numberValue().shortValue();
+ }
+ if (itemNode.has("nbt_b64")) {
+ byte[] bytes = Base64.getDecoder().decode(itemNode.get("nbt_b64").asText());
+ ByteArrayInputStream bais = new ByteArrayInputStream(bytes);
+ try {
+ com.nukkitx.nbt.tag.CompoundTag tag = (com.nukkitx.nbt.tag.CompoundTag) NbtUtils.createReaderLE(bais).readTag();
+ creativeItems.add(ItemData.of(itemNode.get("id").asInt(), damage, 1, tag));
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ } else {
+ creativeItems.add(ItemData.of(itemNode.get("id").asInt(), damage, 1));
+ }
+ }
+ CREATIVE_ITEMS = creativeItems.toArray(new ItemData[0]);
}
public static InputStream getResource(String resource) {
diff --git a/connector/src/main/java/org/geysermc/connector/world/chunk/ChunkPosition.java b/connector/src/main/java/org/geysermc/connector/world/chunk/ChunkPosition.java
index cafb7ab0..26748dff 100644
--- a/connector/src/main/java/org/geysermc/connector/world/chunk/ChunkPosition.java
+++ b/connector/src/main/java/org/geysermc/connector/world/chunk/ChunkPosition.java
@@ -31,6 +31,8 @@ import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.Setter;
+import java.util.Objects;
+
@Getter
@Setter
@AllArgsConstructor
diff --git a/connector/src/main/resources/bedrock/creative_items.json b/connector/src/main/resources/bedrock/creative_items.json
index d55839ba..8045b219 100644
--- a/connector/src/main/resources/bedrock/creative_items.json
+++ b/connector/src/main/resources/bedrock/creative_items.json
@@ -3008,21 +3008,6 @@
{
"id" : -198
},
- {
- "id" : 238,
- "damage" : 8
- },
- {
- "id" : 238
- },
- {
- "id" : 238,
- "damage" : 12
- },
- {
- "id" : 238,
- "damage" : 4
- },
{
"id" : 379
},
@@ -3419,363 +3404,6 @@
{
"id" : 386
},
- {
- "id" : 36
- },
- {
- "id" : -12
- },
- {
- "id" : -13
- },
- {
- "id" : -14
- },
- {
- "id" : -15
- },
- {
- "id" : -16
- },
- {
- "id" : -17
- },
- {
- "id" : -18
- },
- {
- "id" : -19
- },
- {
- "id" : -20
- },
- {
- "id" : -21
- },
- {
- "id" : -22
- },
- {
- "id" : -23
- },
- {
- "id" : -24
- },
- {
- "id" : -25
- },
- {
- "id" : -26
- },
- {
- "id" : -27
- },
- {
- "id" : -28
- },
- {
- "id" : -29
- },
- {
- "id" : -30
- },
- {
- "id" : -31
- },
- {
- "id" : -32
- },
- {
- "id" : -33
- },
- {
- "id" : -34
- },
- {
- "id" : -35
- },
- {
- "id" : -36
- },
- {
- "id" : -37
- },
- {
- "id" : -38
- },
- {
- "id" : -39
- },
- {
- "id" : -40
- },
- {
- "id" : -41
- },
- {
- "id" : -42
- },
- {
- "id" : -43
- },
- {
- "id" : -44
- },
- {
- "id" : -45
- },
- {
- "id" : -46
- },
- {
- "id" : -47
- },
- {
- "id" : -48
- },
- {
- "id" : -49
- },
- {
- "id" : -50
- },
- {
- "id" : -51
- },
- {
- "id" : -52
- },
- {
- "id" : -53
- },
- {
- "id" : -54
- },
- {
- "id" : -55
- },
- {
- "id" : -56
- },
- {
- "id" : -57
- },
- {
- "id" : -58
- },
- {
- "id" : -59
- },
- {
- "id" : -60
- },
- {
- "id" : -61
- },
- {
- "id" : -62
- },
- {
- "id" : -63
- },
- {
- "id" : -64
- },
- {
- "id" : -65
- },
- {
- "id" : -66
- },
- {
- "id" : -67
- },
- {
- "id" : -68
- },
- {
- "id" : -69
- },
- {
- "id" : -70
- },
- {
- "id" : -71
- },
- {
- "id" : -72
- },
- {
- "id" : -73
- },
- {
- "id" : -74
- },
- {
- "id" : -75
- },
- {
- "id" : -76
- },
- {
- "id" : -77
- },
- {
- "id" : -78
- },
- {
- "id" : -79
- },
- {
- "id" : -80
- },
- {
- "id" : -81
- },
- {
- "id" : -82
- },
- {
- "id" : -83
- },
- {
- "id" : -84
- },
- {
- "id" : -85
- },
- {
- "id" : -86
- },
- {
- "id" : -87
- },
- {
- "id" : -88
- },
- {
- "id" : -89
- },
- {
- "id" : -90
- },
- {
- "id" : -91
- },
- {
- "id" : -92
- },
- {
- "id" : -93
- },
- {
- "id" : -94
- },
- {
- "id" : -95
- },
- {
- "id" : -96
- },
- {
- "id" : -97
- },
- {
- "id" : -98
- },
- {
- "id" : -99
- },
- {
- "id" : -100
- },
- {
- "id" : -101
- },
- {
- "id" : -102
- },
- {
- "id" : -103
- },
- {
- "id" : -104
- },
- {
- "id" : -105
- },
- {
- "id" : -106
- },
- {
- "id" : -107
- },
- {
- "id" : -108
- },
- {
- "id" : -109
- },
- {
- "id" : -110
- },
- {
- "id" : -111
- },
- {
- "id" : -112
- },
- {
- "id" : -113
- },
- {
- "id" : -114
- },
- {
- "id" : -115
- },
- {
- "id" : -116
- },
- {
- "id" : -117
- },
- {
- "id" : -118
- },
- {
- "id" : -119
- },
- {
- "id" : -120
- },
- {
- "id" : -121
- },
- {
- "id" : -122
- },
- {
- "id" : -123
- },
- {
- "id" : -124
- },
- {
- "id" : -125
- },
- {
- "id" : -126
- },
- {
- "id" : -127
- },
- {
- "id" : -128
- },
- {
- "id" : -129
- },
{
"id" : 403,
"nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgMAbHZsAQACAgBpZAAAAAA="