diff --git a/README.md b/README.md
index 056a83ca..f11b9bfb 100644
--- a/README.md
+++ b/README.md
@@ -28,12 +28,14 @@ Take a look [here](https://github.com/GeyserMC/Geyser/wiki#Setup) for how to set
- Donate: https://patreon.com/GeyserMC
## What's Left to be Added/Fixed
-- Inventories ([`inventory`](https://github.com/GeyserMC/Geyser/tree/inventory))
-- Crafting ([`inventory`](https://github.com/GeyserMC/Geyser/tree/inventory))
-- Creative Mode ([`inventory`](https://github.com/GeyserMC/Geyser/tree/inventory))
+- The Following Inventories
+ - [ ] Enchantment Table
+ - [ ] Beacon
+ - [ ] Cartography Table
+ - [ ] Stonecutter
+ - [ ] Villager Trading
- Sounds
- Block Particles
-- Block Entities ([`inventory`](https://github.com/GeyserMC/Geyser/tree/inventory))
- Some Entity Flags
## Compiling
diff --git a/connector/pom.xml b/connector/pom.xml
index 5830ce26..9e223d4e 100644
--- a/connector/pom.xml
+++ b/connector/pom.xml
@@ -66,10 +66,34 @@
8.3.1
compile
+
+ com.nukkitx.fastutil
+ fastutil-int-double-maps
+ 8.3.1
+ compile
+
+
+ com.nukkitx.fastutil
+ fastutil-int-boolean-maps
+ 8.3.1
+ compile
+
+
+ com.nukkitx.fastutil
+ fastutil-object-int-maps
+ 8.3.1
+ compile
+
+
+ com.nukkitx.fastutil
+ fastutil-object-byte-maps
+ 8.3.1
+ compile
+
com.github.steveice10
opennbt
- 1.3-SNAPSHOT
+ 1.4-SNAPSHOT
compile
diff --git a/connector/src/main/java/org/geysermc/connector/GeyserConnector.java b/connector/src/main/java/org/geysermc/connector/GeyserConnector.java
index 278e4859..2679857c 100644
--- a/connector/src/main/java/org/geysermc/connector/GeyserConnector.java
+++ b/connector/src/main/java/org/geysermc/connector/GeyserConnector.java
@@ -32,6 +32,7 @@ import com.nukkitx.protocol.bedrock.v390.Bedrock_v390;
import lombok.Getter;
import org.geysermc.common.AuthType;
+import org.geysermc.common.IGeyserConfiguration;
import org.geysermc.common.PlatformType;
import org.geysermc.common.bootstrap.IGeyserBootstrap;
import org.geysermc.common.logger.IGeyserLogger;
@@ -43,7 +44,6 @@ import org.geysermc.connector.network.session.GeyserSession;
import org.geysermc.connector.network.translators.Translators;
import org.geysermc.connector.thread.PingPassthroughThread;
import org.geysermc.connector.utils.Toolbox;
-import org.geysermc.common.IGeyserConfiguration;
import java.net.InetSocketAddress;
import java.text.DecimalFormat;
diff --git a/connector/src/main/java/org/geysermc/connector/entity/PlayerEntity.java b/connector/src/main/java/org/geysermc/connector/entity/PlayerEntity.java
index fb48871b..d0ac2aeb 100644
--- a/connector/src/main/java/org/geysermc/connector/entity/PlayerEntity.java
+++ b/connector/src/main/java/org/geysermc/connector/entity/PlayerEntity.java
@@ -44,6 +44,7 @@ import org.geysermc.connector.entity.type.EntityType;
import org.geysermc.connector.network.session.GeyserSession;
import org.geysermc.connector.scoreboard.Team;
import org.geysermc.connector.utils.MessageUtils;
+import org.geysermc.connector.network.session.cache.EntityEffectCache;
import org.geysermc.connector.utils.SkinUtils;
import java.util.UUID;
@@ -55,6 +56,7 @@ public class PlayerEntity extends LivingEntity {
private String username;
private long lastSkinUpdate = -1;
private boolean playerList = true;
+ private final EntityEffectCache effectCache;
public PlayerEntity(GameProfile gameProfile, long entityId, long geyserId, Vector3f position, Vector3f motion, Vector3f rotation) {
super(entityId, geyserId, EntityType.PLAYER, position, motion, rotation);
@@ -62,6 +64,7 @@ public class PlayerEntity extends LivingEntity {
profile = gameProfile;
uuid = gameProfile.getId();
username = gameProfile.getName();
+ effectCache = new EntityEffectCache();
if (geyserId == 1) valid = true;
}
diff --git a/connector/src/main/java/org/geysermc/connector/entity/living/AgeableEntity.java b/connector/src/main/java/org/geysermc/connector/entity/living/AgeableEntity.java
index f205c060..634f0674 100644
--- a/connector/src/main/java/org/geysermc/connector/entity/living/AgeableEntity.java
+++ b/connector/src/main/java/org/geysermc/connector/entity/living/AgeableEntity.java
@@ -26,7 +26,6 @@
package org.geysermc.connector.entity.living;
import com.github.steveice10.mc.protocol.data.game.entity.metadata.EntityMetadata;
-import com.github.steveice10.mc.protocol.data.game.entity.metadata.MetadataType;
import com.nukkitx.math.vector.Vector3f;
import com.nukkitx.protocol.bedrock.data.EntityData;
import com.nukkitx.protocol.bedrock.data.EntityFlag;
diff --git a/connector/src/main/java/org/geysermc/connector/entity/living/animal/horse/HorseEntity.java b/connector/src/main/java/org/geysermc/connector/entity/living/animal/horse/HorseEntity.java
index 95d91ab8..b6a50520 100644
--- a/connector/src/main/java/org/geysermc/connector/entity/living/animal/horse/HorseEntity.java
+++ b/connector/src/main/java/org/geysermc/connector/entity/living/animal/horse/HorseEntity.java
@@ -45,4 +45,4 @@ public class HorseEntity extends AbstractHorseEntity {
super.updateBedrockMetadata(entityMetadata, session);
}
-}
+}
\ No newline at end of file
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 d969f970..539fe1e2 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 a1db2fcd..432ca827 100644
--- a/connector/src/main/java/org/geysermc/connector/inventory/PlayerInventory.java
+++ b/connector/src/main/java/org/geysermc/connector/inventory/PlayerInventory.java
@@ -35,13 +35,21 @@ 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];
+ return items[36 + 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 83a94529..12ae39bf 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,7 +44,13 @@ 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.packet.AvailableEntityIdentifiersPacket;
+import com.nukkitx.protocol.bedrock.packet.BiomeDefinitionListPacket;
+import com.nukkitx.protocol.bedrock.packet.PlayStatusPacket;
+import com.nukkitx.protocol.bedrock.packet.StartGamePacket;
+import com.nukkitx.protocol.bedrock.packet.TextPacket;
import com.nukkitx.protocol.bedrock.data.GameRuleData;
import com.nukkitx.protocol.bedrock.data.PlayerPermission;
import com.nukkitx.protocol.bedrock.packet.*;
@@ -125,6 +131,9 @@ public class GeyserSession implements CommandSender {
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);
@@ -160,6 +169,11 @@ public class GeyserSession implements CommandSender {
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/EntityEffectCache.java b/connector/src/main/java/org/geysermc/connector/network/session/cache/EntityEffectCache.java
new file mode 100644
index 00000000..a16ef690
--- /dev/null
+++ b/connector/src/main/java/org/geysermc/connector/network/session/cache/EntityEffectCache.java
@@ -0,0 +1,54 @@
+/*
+ * 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.session.cache;
+
+import com.github.steveice10.mc.protocol.data.game.entity.Effect;
+import it.unimi.dsi.fastutil.objects.Object2IntMap;
+import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap;
+import lombok.Getter;
+
+public class EntityEffectCache {
+
+ @Getter
+ private final Object2IntMap entityEffects = new Object2IntOpenHashMap<>();
+
+ public void addEffect(Effect effect, int effectAmplifier) {
+ if (effect != null) {
+ entityEffects.putIfAbsent(effect, effectAmplifier + 1);
+ }
+ }
+
+ public void removeEffect(Effect effect) {
+ if (entityEffects.containsKey(effect)) {
+ int effectLevel = entityEffects.getInt(effect);
+ entityEffects.remove(effect, effectLevel);
+ }
+ }
+
+ public int getEffectLevel(Effect effect) {
+ return entityEffects.getOrDefault(effect, 0);
+ }
+}
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/Translators.java b/connector/src/main/java/org/geysermc/connector/network/translators/Translators.java
index d4e5dae8..cffb85f1 100644
--- a/connector/src/main/java/org/geysermc/connector/network/translators/Translators.java
+++ b/connector/src/main/java/org/geysermc/connector/network/translators/Translators.java
@@ -27,11 +27,17 @@ package org.geysermc.connector.network.translators;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
+import java.util.HashMap;
+import java.util.Map;
+import com.github.steveice10.mc.protocol.data.game.window.WindowType;
+import com.nukkitx.protocol.bedrock.data.ContainerType;
import org.geysermc.connector.GeyserConnector;
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.block.entity.*;
+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.reflections.Reflections;
@@ -50,7 +56,10 @@ public class Translators {
private static ItemTranslator itemTranslator;
@Getter
- private static InventoryTranslator inventoryTranslator = new GenericInventoryTranslator();
+ private static Map inventoryTranslators = new HashMap<>();
+
+ @Getter
+ private static Map blockEntityTranslators = new HashMap<>();
private static final CompoundTag EMPTY_TAG = CompoundTagBuilder.builder().buildRootTag();
public static final byte[] EMPTY_LEVEL_CHUNK_DATA;
@@ -82,15 +91,15 @@ public class Translators {
if (Packet.class.isAssignableFrom(packet)) {
Class extends Packet> targetPacket = (Class extends Packet>) packet;
PacketTranslator extends Packet> translator = (PacketTranslator extends Packet>) clazz.newInstance();
-
+
Registry.registerJava(targetPacket, translator);
-
+
} else if (BedrockPacket.class.isAssignableFrom(packet)) {
Class extends BedrockPacket> targetPacket = (Class extends BedrockPacket>) packet;
PacketTranslator extends BedrockPacket> translator = (PacketTranslator extends BedrockPacket>) clazz.newInstance();
-
+
Registry.registerBedrock(targetPacket, translator);
-
+
} else {
GeyserConnector.getInstance().getLogger().error("Class " + clazz.getCanonicalName() + " is annotated as a translator but has an invalid target packet.");
}
@@ -102,15 +111,48 @@ public class Translators {
itemTranslator = new ItemTranslator();
BlockTranslator.init();
+ registerBlockEntityTranslators();
registerInventoryTranslators();
}
+ private static void registerBlockEntityTranslators() {
+ Reflections ref = new Reflections("org.geysermc.connector.network.translators.block.entity");
+
+ for (Class> clazz : ref.getTypesAnnotatedWith(BlockEntity.class)) {
+
+ GeyserConnector.getInstance().getLogger().debug("Found annotated block entity: " + clazz.getCanonicalName());
+
+ try {
+ blockEntityTranslators.put(clazz.getAnnotation(BlockEntity.class).name(), (BlockEntityTranslator) clazz.newInstance());
+ } catch (InstantiationException | IllegalAccessException e) {
+ GeyserConnector.getInstance().getLogger().error("Could not instantiate annotated block entity " + clazz.getCanonicalName() + ".");
+ }
+ }
+ }
+
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.GRINDSTONE, new GrindstoneInventoryTranslator());
+ //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/BedrockBlockEntityDataTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockBlockEntityDataTranslator.java
new file mode 100644
index 00000000..dc076a91
--- /dev/null
+++ b/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockBlockEntityDataTranslator.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.bedrock;
+
+import com.github.steveice10.mc.protocol.data.game.entity.metadata.Position;
+import com.github.steveice10.mc.protocol.packet.ingame.client.world.ClientUpdateSignPacket;
+import com.nukkitx.nbt.tag.CompoundTag;
+import com.nukkitx.protocol.bedrock.packet.BlockEntityDataPacket;
+import org.geysermc.connector.network.session.GeyserSession;
+import org.geysermc.connector.network.translators.PacketTranslator;
+import org.geysermc.connector.network.translators.Translator;
+
+import java.util.HashMap;
+import java.util.Map;
+
+@Translator(packet = BlockEntityDataPacket.class)
+public class BedrockBlockEntityDataTranslator extends PacketTranslator {
+
+ // In case two people are editing signs at the same time this array holds the temporary messages to be sent
+ // Position -> Message being held
+ protected static Map lastMessages = new HashMap<>();
+
+ @Override
+ public void translate(BlockEntityDataPacket packet, GeyserSession session) {
+ if (packet.getData() instanceof CompoundTag) {
+ CompoundTag tag = (CompoundTag) packet.getData();
+ if (tag.getString("id").equals("Sign")) {
+ // This is the reason why this all works - Bedrock sends packets every time you update the sign, Java only wants the final packet
+ // But Bedrock sends one final packet when you're done editing the sign, which should be equal to the last message since there's no edits
+ // So if the latest update does not match the last cached update then it's still being edited
+ Position pos = new Position(tag.getInt("x"), tag.getInt("y"), tag.getInt("z"));
+ if (!tag.getString("Text").equals(lastMessages.get(pos))) {
+ lastMessages.put(pos, tag.getString("Text"));
+ return;
+ }
+ // Otherwise the two messages are identical and we can get to work deconstructing
+ StringBuilder newMessage = new StringBuilder();
+ // While Bedrock's sign lines are one string, Java's is an array of each line
+ // (Initialized all with empty strings because it complains about null)
+ String[] lines = new String[] {"", "", "", ""};
+ int iterator = 0;
+ // This converts the message into the array'd message Java wants
+ for (char character : tag.getString("Text").toCharArray()) {
+ // If we get a return in Bedrock, that signals to use the next line.
+ if (character == '\n') {
+ lines[iterator] = newMessage.toString();
+ iterator++;
+ // Bedrock, for whatever reason, can hold a message out of bounds
+ // We don't care about that so we discard that
+ if (iterator > lines.length - 1) {
+ break;
+ }
+ newMessage = new StringBuilder();
+ } else newMessage.append(character);
+ }
+ // Put the final line on since it isn't done in the for loop
+ if (iterator < lines.length) lines[iterator] = newMessage.toString();
+ ClientUpdateSignPacket clientUpdateSignPacket = new ClientUpdateSignPacket(pos, lines);
+ session.getDownstream().getSession().send(clientUpdateSignPacket);
+ //TODO (potentially): originally I was going to update the sign blocks so Bedrock and Java users would match visually
+ // However Java can still store a lot per-line and visuals are still messed up so that doesn't work
+
+ // We remove the sign position from map to indicate there is no work-in-progress sign
+ lastMessages.remove(pos);
+ }
+ }
+
+ }
+}
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..8d036bfa
--- /dev/null
+++ b/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockContainerCloseTranslator.java
@@ -0,0 +1,54 @@
+/*
+ * 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.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.Translator;
+import org.geysermc.connector.utils.InventoryUtils;
+
+@Translator(packet = ContainerClosePacket.class)
+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 0f61925b..fd23cd3a 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
@@ -27,10 +27,14 @@ package org.geysermc.connector.network.translators.bedrock;
import com.github.steveice10.mc.protocol.packet.ingame.client.player.ClientPlayerPlaceBlockPacket;
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.Translator;
+import org.geysermc.connector.network.translators.Translators;
+import org.geysermc.connector.utils.InventoryUtils;
+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;
@@ -40,7 +44,6 @@ 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;
@Translator(packet = InventoryTransactionPacket.class)
@@ -49,6 +52,17 @@ public class BedrockInventoryTransactionTranslator extends PacketTranslator BANNER_COLORS = new Object2IntOpenHashMap<>();
+ private static final Object2ByteMap BED_COLORS = new Object2ByteOpenHashMap<>();
+ private static final Object2ByteMap SKULL_VARIANTS = new Object2ByteOpenHashMap<>();
+ private static final Object2ByteMap SKULL_ROTATIONS = new Object2ByteOpenHashMap<>();
+
+ /**
+ * Determines if the block state contains Bedrock block information
+ * @param entry The String -> JsonNode map used in BlockTranslator
+ * @param javaBlockState the Java Block State of the block
+ */
+ public static void storeBlockStateValues(Map.Entry entry, BlockState javaBlockState) {
+ JsonNode bannerColor = entry.getValue().get("banner_color");
+ if (bannerColor != null) {
+ BlockStateValues.BANNER_COLORS.put(javaBlockState, (byte) bannerColor.intValue());
+ return; // There will never be a banner color and a skull variant
+ }
+
+ JsonNode bedColor = entry.getValue().get("bed_color");
+ if (bedColor != null) {
+ BlockStateValues.BED_COLORS.put(javaBlockState, (byte) bedColor.intValue());
+ return;
+ }
+
+ JsonNode skullVariation = entry.getValue().get("variation");
+ if(skullVariation != null) {
+ BlockStateValues.SKULL_VARIANTS.put(javaBlockState, (byte) skullVariation.intValue());
+ }
+
+ JsonNode skullRotation = entry.getValue().get("skull_rotation");
+ if (skullRotation != null) {
+ BlockStateValues.SKULL_ROTATIONS.put(javaBlockState, (byte) skullRotation.intValue());
+ }
+ }
+
+ /**
+ * Banner colors are part of the namespaced ID in Java Edition, but part of the block entity tag in Bedrock.
+ * This gives an integer color that Bedrock can use.
+ * @param state BlockState of the block
+ * @return banner color integer or -1 if no color
+ */
+ public static int getBannerColor(BlockState state) {
+ if (BANNER_COLORS.containsKey(state)) {
+ return BANNER_COLORS.getInt(state);
+ }
+ return -1;
+ }
+
+ /**
+ * Bed colors are part of the namespaced ID in Java Edition, but part of the block entity tag in Bedrock.
+ * This gives a byte color that Bedrock can use - Bedrock needs a byte in the final tag.
+ * @param state BlockState of the block
+ * @return bed color byte or -1 if no color
+ */
+ public static byte getBedColor(BlockState state) {
+ if (BED_COLORS.containsKey(state)) {
+ return BED_COLORS.getByte(state);
+ }
+ return -1;
+ }
+
+ /**
+ * Skull variations are part of the namespaced ID in Java Edition, but part of the block entity tag in Bedrock.
+ * This gives a byte variant ID that Bedrock can use.
+ * @param state BlockState of the block
+ * @return skull variant byte or -1 if no variant
+ */
+ public static byte getSkullVariant(BlockState state) {
+ if (SKULL_VARIANTS.containsKey(state)) {
+ return SKULL_VARIANTS.getByte(state);
+ }
+ return -1;
+ }
+
+ /**
+ *
+ * @param state BlockState of the block
+ * @return skull rotation value or -1 if no value
+ */
+ public static byte getSkullRotation(BlockState state) {
+ if (SKULL_ROTATIONS.containsKey(state)) {
+ return SKULL_ROTATIONS.getByte(state);
+ }
+ return -1;
+ }
+
+}
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 58f6ff4e..5b5d5c9c 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
@@ -32,6 +32,10 @@ import com.nukkitx.nbt.NbtUtils;
import com.nukkitx.nbt.stream.NBTInputStream;
import com.nukkitx.nbt.tag.CompoundTag;
import com.nukkitx.nbt.tag.ListTag;
+import it.unimi.dsi.fastutil.ints.Int2BooleanMap;
+import it.unimi.dsi.fastutil.ints.Int2BooleanOpenHashMap;
+import it.unimi.dsi.fastutil.ints.Int2DoubleMap;
+import it.unimi.dsi.fastutil.ints.Int2DoubleOpenHashMap;
import it.unimi.dsi.fastutil.ints.Int2IntMap;
import it.unimi.dsi.fastutil.ints.Int2IntOpenHashMap;
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
@@ -41,7 +45,9 @@ import it.unimi.dsi.fastutil.ints.IntSet;
import it.unimi.dsi.fastutil.objects.Object2IntMap;
import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap;
import org.geysermc.connector.GeyserConnector;
+import org.geysermc.connector.network.translators.block.entity.BlockEntity;
import org.geysermc.connector.utils.Toolbox;
+import org.reflections.Reflections;
import java.io.InputStream;
import java.util.*;
@@ -53,11 +59,22 @@ 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();
// Bedrock carpet ID, used in LlamaEntity.java for decoration
public static final int CARPET = 171;
+ private static final Map JAVA_ID_TO_BLOCK_ENTITY_MAP = new HashMap<>();
+
+ public static final Int2DoubleMap JAVA_RUNTIME_ID_TO_HARDNESS = new Int2DoubleOpenHashMap();
+ public static final Int2BooleanMap JAVA_RUNTIME_ID_TO_CAN_HARVEST_WITH_HAND = new Int2BooleanOpenHashMap();
+ public static final Int2ObjectMap JAVA_RUNTIME_ID_TO_TOOL_TYPE = new Int2ObjectOpenHashMap<>();
+
+ // For block breaking animation math
+ public static final IntSet JAVA_RUNTIME_WOOL_IDS = new IntOpenHashSet();
+ public static final int JAVA_RUNTIME_COBWEB_ID;
+
private static final int BLOCK_STATE_VERSION = 17760256;
static {
@@ -90,16 +107,58 @@ public class BlockTranslator {
addedStatesMap.defaultReturnValue(-1);
List paletteList = new ArrayList<>();
+ Reflections ref = new Reflections("org.geysermc.connector.network.translators.block.entity");
+ ref.getTypesAnnotatedWith(BlockEntity.class);
+
int waterRuntimeId = -1;
int javaRuntimeId = -1;
int bedrockRuntimeId = 0;
+ int cobwebRuntimeId = -1;
Iterator> blocksIterator = blocks.fields();
while (blocksIterator.hasNext()) {
javaRuntimeId++;
Map.Entry entry = blocksIterator.next();
String javaId = entry.getKey();
+ BlockState javaBlockState = new BlockState(javaRuntimeId);
CompoundTag blockTag = buildBedrockState(entry.getValue());
+ // TODO fix this, (no block should have a null hardness)
+ JsonNode hardnessNode = entry.getValue().get("block_hardness");
+ if (hardnessNode != null) {
+ JAVA_RUNTIME_ID_TO_HARDNESS.put(javaRuntimeId, hardnessNode.doubleValue());
+ }
+
+ JAVA_RUNTIME_ID_TO_CAN_HARVEST_WITH_HAND.put(javaRuntimeId, entry.getValue().get("can_break_with_hand").booleanValue());
+
+ JsonNode toolTypeNode = entry.getValue().get("tool_type");
+ if (toolTypeNode != null) {
+ JAVA_RUNTIME_ID_TO_TOOL_TYPE.put(javaRuntimeId, toolTypeNode.textValue());
+ }
+
+ if (javaId.contains("wool")) {
+ JAVA_RUNTIME_WOOL_IDS.add(javaRuntimeId);
+ }
+
+ if (javaId.contains("cobweb")) {
+ cobwebRuntimeId = javaRuntimeId;
+ }
+
+ JAVA_ID_BLOCK_MAP.put(javaId, javaBlockState);
+
+ // Used for adding all "special" Java block states to block state map
+ String identifier;
+ String bedrock_identifer = entry.getValue().get("bedrock_identifier").asText();
+ for (Class> clazz : ref.getTypesAnnotatedWith(BlockEntity.class)) {
+ identifier = clazz.getAnnotation(BlockEntity.class).regex();
+ // Endswith, or else the block bedrock gets picked up for bed
+ if (bedrock_identifer.endsWith(identifier) && !identifier.equals("")) {
+ JAVA_ID_TO_BLOCK_ENTITY_MAP.put(javaBlockState, clazz.getAnnotation(BlockEntity.class).name());
+ break;
+ }
+ }
+
+ BlockStateValues.storeBlockStateValues(entry, javaBlockState);
+
if ("minecraft:water[level=0]".equals(javaId)) {
waterRuntimeId = bedrockRuntimeId;
}
@@ -107,10 +166,10 @@ public class BlockTranslator {
|| javaId.contains("minecraft:bubble_column") || javaId.contains("minecraft:kelp") || javaId.contains("seagrass");
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);
@@ -118,7 +177,7 @@ public class BlockTranslator {
addedStatesMap.put(blockTag, bedrockRuntimeId);
paletteList.add(runtimeTag);
} else {
- int duplicateRuntimeId = addedStatesMap.get(blockTag);
+ int duplicateRuntimeId = addedStatesMap.getOrDefault(blockTag, -1);
if (duplicateRuntimeId == -1) {
GeyserConnector.getInstance().getLogger().debug("Mapping " + javaId + " was not found for bedrock edition!");
} else {
@@ -131,6 +190,11 @@ public class BlockTranslator {
bedrockRuntimeId++;
}
+ if (cobwebRuntimeId == -1) {
+ throw new AssertionError("Unable to find cobwebs in palette");
+ }
+ JAVA_RUNTIME_COBWEB_ID = cobwebRuntimeId;
+
if (waterRuntimeId == -1) {
throw new AssertionError("Unable to find water in palette");
}
@@ -189,6 +253,14 @@ 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 String getBlockEntityString(BlockState javaId) {
+ return JAVA_ID_TO_BLOCK_ENTITY_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/block/entity/BannerBlockEntityTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/block/entity/BannerBlockEntityTranslator.java
new file mode 100644
index 00000000..22d26cb4
--- /dev/null
+++ b/connector/src/main/java/org/geysermc/connector/network/translators/block/entity/BannerBlockEntityTranslator.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.block.entity;
+
+import com.github.steveice10.mc.protocol.data.game.world.block.BlockState;
+import com.github.steveice10.opennbt.tag.builtin.CompoundTag;
+import com.github.steveice10.opennbt.tag.builtin.ListTag;
+import com.nukkitx.nbt.CompoundTagBuilder;
+import com.nukkitx.nbt.tag.IntTag;
+import com.nukkitx.nbt.tag.StringTag;
+import com.nukkitx.nbt.tag.Tag;
+import org.geysermc.connector.network.translators.block.BlockStateValues;
+
+import java.util.ArrayList;
+import java.util.List;
+
+@BlockEntity(name = "Banner", delay = false, regex = "banner")
+public class BannerBlockEntityTranslator extends BlockEntityTranslator implements RequiresBlockState {
+
+ @Override
+ public boolean isBlock(BlockState blockState) {
+ return BlockStateValues.getBannerColor(blockState) != -1;
+ }
+
+ @Override
+ public List> translateTag(CompoundTag tag, BlockState blockState) {
+ List> tags = new ArrayList<>();
+ int bannerColor = BlockStateValues.getBannerColor(blockState);
+ if (bannerColor != -1) {
+ tags.add(new IntTag("Base", 15 - bannerColor));
+ }
+ ListTag patterns = tag.get("Patterns");
+ List tagsList = new ArrayList<>();
+ if (tag.contains("Patterns")) {
+ for (com.github.steveice10.opennbt.tag.builtin.Tag patternTag : patterns.getValue()) {
+ tagsList.add(getPattern((CompoundTag) patternTag));
+ }
+ com.nukkitx.nbt.tag.ListTag bedrockPatterns =
+ new com.nukkitx.nbt.tag.ListTag<>("Patterns", com.nukkitx.nbt.tag.CompoundTag.class, tagsList);
+ tags.add(bedrockPatterns);
+ }
+ if (tag.contains("CustomName")) {
+ tags.add(new StringTag("CustomName", (String) tag.get("CustomName").getValue()));
+ }
+ return tags;
+ }
+
+ @Override
+ public CompoundTag getDefaultJavaTag(String javaId, int x, int y, int z) {
+ CompoundTag tag = getConstantJavaTag(javaId, x, y, z);
+ tag.put(new ListTag("Patterns"));
+ return tag;
+ }
+
+ @Override
+ public com.nukkitx.nbt.tag.CompoundTag getDefaultBedrockTag(String bedrockId, int x, int y, int z) {
+ CompoundTagBuilder tagBuilder = getConstantBedrockTag(bedrockId, x, y, z).toBuilder();
+ tagBuilder.listTag("Patterns", com.nukkitx.nbt.tag.CompoundTag.class, new ArrayList<>());
+ return tagBuilder.buildRootTag();
+ }
+
+ protected com.nukkitx.nbt.tag.CompoundTag getPattern(CompoundTag pattern) {
+ return CompoundTagBuilder.builder()
+ .intTag("Color", 15 - (int) pattern.get("Color").getValue())
+ .stringTag("Pattern", (String) pattern.get("Pattern").getValue())
+ .buildRootTag();
+ }
+}
diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/block/entity/BedBlockEntityTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/block/entity/BedBlockEntityTranslator.java
new file mode 100644
index 00000000..a8dae253
--- /dev/null
+++ b/connector/src/main/java/org/geysermc/connector/network/translators/block/entity/BedBlockEntityTranslator.java
@@ -0,0 +1,67 @@
+/*
+ * 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.block.entity;
+
+import com.github.steveice10.mc.protocol.data.game.world.block.BlockState;
+import com.github.steveice10.opennbt.tag.builtin.CompoundTag;
+import com.nukkitx.nbt.CompoundTagBuilder;
+import com.nukkitx.nbt.tag.ByteTag;
+import com.nukkitx.nbt.tag.Tag;
+import org.geysermc.connector.network.translators.block.BlockStateValues;
+
+import java.util.ArrayList;
+import java.util.List;
+
+@BlockEntity(name = "Bed", delay = false, regex = "bed")
+public class BedBlockEntityTranslator extends BlockEntityTranslator implements RequiresBlockState {
+
+ @Override
+ public boolean isBlock(BlockState blockState) {
+ return BlockStateValues.getBedColor(blockState) != -1;
+ }
+
+ @Override
+ public List> translateTag(CompoundTag tag, BlockState blockState) {
+ List> tags = new ArrayList<>();
+ byte bedcolor = BlockStateValues.getBedColor(blockState);
+ // Just in case...
+ if (bedcolor == -1) bedcolor = 0;
+ tags.add(new ByteTag("color", bedcolor));
+ return tags;
+ }
+
+ @Override
+ public CompoundTag getDefaultJavaTag(String javaId, int x, int y, int z) {
+ return null;
+ }
+
+ @Override
+ public com.nukkitx.nbt.tag.CompoundTag getDefaultBedrockTag(String bedrockId, int x, int y, int z) {
+ CompoundTagBuilder tagBuilder = getConstantBedrockTag(bedrockId, x, y, z).toBuilder();
+ tagBuilder.byteTag("color", (byte) 0);
+ return tagBuilder.buildRootTag();
+ }
+}
diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/block/entity/BlockEntity.java b/connector/src/main/java/org/geysermc/connector/network/translators/block/entity/BlockEntity.java
new file mode 100644
index 00000000..0321f2c1
--- /dev/null
+++ b/connector/src/main/java/org/geysermc/connector/network/translators/block/entity/BlockEntity.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.block.entity;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+@Retention(value = RetentionPolicy.RUNTIME)
+public @interface BlockEntity {
+ /**
+ * Whether to delay the sending of the block entity
+ */
+ boolean delay();
+ /**
+ * The block entity name
+ */
+ String name();
+ /**
+ * The search term used in BlockTranslator
+ */
+ String regex();
+}
diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/block/entity/BlockEntityTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/block/entity/BlockEntityTranslator.java
new file mode 100644
index 00000000..f2825789
--- /dev/null
+++ b/connector/src/main/java/org/geysermc/connector/network/translators/block/entity/BlockEntityTranslator.java
@@ -0,0 +1,79 @@
+/*
+ * 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.block.entity;
+
+import com.github.steveice10.mc.protocol.data.game.world.block.BlockState;
+import com.github.steveice10.opennbt.tag.builtin.CompoundTag;
+import com.github.steveice10.opennbt.tag.builtin.IntTag;
+import com.github.steveice10.opennbt.tag.builtin.StringTag;
+import com.nukkitx.nbt.CompoundTagBuilder;
+import com.nukkitx.nbt.tag.Tag;
+
+import org.geysermc.connector.utils.BlockEntityUtils;
+
+import java.util.List;
+
+public abstract class BlockEntityTranslator {
+
+ public abstract List> translateTag(CompoundTag tag, BlockState blockState);
+
+ public abstract CompoundTag getDefaultJavaTag(String javaId, int x, int y, int z);
+
+ public abstract com.nukkitx.nbt.tag.CompoundTag getDefaultBedrockTag(String bedrockId, int x, int y, int z);
+
+ public com.nukkitx.nbt.tag.CompoundTag getBlockEntityTag(String id, CompoundTag tag, BlockState blockState) {
+ int x = Integer.parseInt(String.valueOf(tag.getValue().get("x").getValue()));
+ int y = Integer.parseInt(String.valueOf(tag.getValue().get("y").getValue()));
+ int z = Integer.parseInt(String.valueOf(tag.getValue().get("z").getValue()));
+
+ CompoundTagBuilder tagBuilder = getConstantBedrockTag(BlockEntityUtils.getBedrockBlockEntityId(id), x, y, z).toBuilder();
+ translateTag(tag, blockState).forEach(tagBuilder::tag);
+ return tagBuilder.buildRootTag();
+ }
+
+ protected CompoundTag getConstantJavaTag(String javaId, int x, int y, int z) {
+ CompoundTag tag = new CompoundTag("");
+ tag.put(new IntTag("x", x));
+ tag.put(new IntTag("y", y));
+ tag.put(new IntTag("z", z));
+ tag.put(new StringTag("id", javaId));
+ return tag;
+ }
+
+ protected com.nukkitx.nbt.tag.CompoundTag getConstantBedrockTag(String bedrockId, int x, int y, int z) {
+ CompoundTagBuilder tagBuilder = CompoundTagBuilder.builder()
+ .intTag("x", x)
+ .intTag("y", y)
+ .intTag("z", z)
+ .stringTag("id", bedrockId);
+ return tagBuilder.buildRootTag();
+ }
+
+ @SuppressWarnings("unchecked")
+ protected T getOrDefault(com.github.steveice10.opennbt.tag.builtin.Tag tag, T defaultValue) {
+ return (tag != null && tag.getValue() != null) ? (T) tag.getValue() : defaultValue;
+ }
+}
diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/block/entity/CampfireBlockEntityTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/block/entity/CampfireBlockEntityTranslator.java
new file mode 100644
index 00000000..11b7e864
--- /dev/null
+++ b/connector/src/main/java/org/geysermc/connector/network/translators/block/entity/CampfireBlockEntityTranslator.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.block.entity;
+
+import com.github.steveice10.mc.protocol.data.game.world.block.BlockState;
+import com.github.steveice10.opennbt.tag.builtin.CompoundTag;
+import com.github.steveice10.opennbt.tag.builtin.ListTag;
+import com.nukkitx.nbt.CompoundTagBuilder;
+import com.nukkitx.nbt.tag.Tag;
+
+import org.geysermc.connector.network.translators.Translators;
+import org.geysermc.connector.network.translators.item.ItemEntry;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+
+@BlockEntity(name = "Campfire", delay = false, regex = "campfire")
+public class CampfireBlockEntityTranslator extends BlockEntityTranslator {
+
+ @Override
+ public List> translateTag(CompoundTag tag, BlockState blockState) {
+ List> tags = new ArrayList<>();
+ ListTag items = tag.get("Items");
+ int i = 1;
+ for (com.github.steveice10.opennbt.tag.builtin.Tag itemTag : items.getValue()) {
+ tags.add(getItem((CompoundTag) itemTag).toBuilder().build("Item" + i));
+ i++;
+ }
+ return tags;
+ }
+
+ @Override
+ public CompoundTag getDefaultJavaTag(String javaId, int x, int y, int z) {
+ CompoundTag tag = getConstantJavaTag(javaId, x, y, z);
+ tag.put(new ListTag("Items"));
+ return tag;
+ }
+
+ @Override
+ public com.nukkitx.nbt.tag.CompoundTag getDefaultBedrockTag(String bedrockId, int x, int y, int z) {
+ CompoundTagBuilder tagBuilder = getConstantBedrockTag(bedrockId, x, y, z).toBuilder();
+ tagBuilder.tag(new com.nukkitx.nbt.tag.CompoundTag("Item1", new HashMap<>()));
+ tagBuilder.tag(new com.nukkitx.nbt.tag.CompoundTag("Item2", new HashMap<>()));
+ tagBuilder.tag(new com.nukkitx.nbt.tag.CompoundTag("Item3", new HashMap<>()));
+ tagBuilder.tag(new com.nukkitx.nbt.tag.CompoundTag("Item4", new HashMap<>()));
+ return tagBuilder.buildRootTag();
+ }
+
+ protected com.nukkitx.nbt.tag.CompoundTag getItem(CompoundTag tag) {
+ ItemEntry entry = Translators.getItemTranslator().getItemEntry((String) tag.get("id").getValue());
+ CompoundTagBuilder tagBuilder = CompoundTagBuilder.builder()
+ .shortTag("id", (short) entry.getBedrockId())
+ .byteTag("Count", (byte) tag.get("Count").getValue())
+ .shortTag("Damage", (short) entry.getBedrockData())
+ .tag(CompoundTagBuilder.builder().build("tag"));
+ return tagBuilder.buildRootTag();
+ }
+}
diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/block/entity/EmptyBlockEntityTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/block/entity/EmptyBlockEntityTranslator.java
new file mode 100644
index 00000000..e4601400
--- /dev/null
+++ b/connector/src/main/java/org/geysermc/connector/network/translators/block/entity/EmptyBlockEntityTranslator.java
@@ -0,0 +1,52 @@
+/*
+ * 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.block.entity;
+
+import com.github.steveice10.mc.protocol.data.game.world.block.BlockState;
+import com.github.steveice10.opennbt.tag.builtin.CompoundTag;
+import com.nukkitx.nbt.tag.Tag;
+
+import java.util.ArrayList;
+import java.util.List;
+
+@BlockEntity(name = "Empty", delay = false, regex = "")
+public class EmptyBlockEntityTranslator extends BlockEntityTranslator {
+
+ @Override
+ public List> translateTag(CompoundTag tag, BlockState blockState) {
+ return new ArrayList<>();
+ }
+
+ @Override
+ public CompoundTag getDefaultJavaTag(String javaId, int x, int y, int z) {
+ return getConstantJavaTag(javaId, x, y, z);
+ }
+
+ @Override
+ public com.nukkitx.nbt.tag.CompoundTag getDefaultBedrockTag(String bedrockId, int x, int y, int z) {
+ return getConstantBedrockTag(bedrockId, x, y, z);
+ }
+}
diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/block/entity/EndGatewayBlockEntityTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/block/entity/EndGatewayBlockEntityTranslator.java
new file mode 100644
index 00000000..de5868a4
--- /dev/null
+++ b/connector/src/main/java/org/geysermc/connector/network/translators/block/entity/EndGatewayBlockEntityTranslator.java
@@ -0,0 +1,85 @@
+/*
+ * 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.block.entity;
+
+import com.github.steveice10.mc.protocol.data.game.world.block.BlockState;
+import com.github.steveice10.opennbt.tag.builtin.CompoundTag;
+import com.github.steveice10.opennbt.tag.builtin.LongTag;
+import com.nukkitx.nbt.CompoundTagBuilder;
+import com.nukkitx.nbt.tag.IntTag;
+import com.nukkitx.nbt.tag.Tag;
+
+import java.util.ArrayList;
+import java.util.LinkedHashMap;
+import java.util.List;
+
+@BlockEntity(name = "EndGateway", delay = true, regex = "end_gateway")
+public class EndGatewayBlockEntityTranslator extends BlockEntityTranslator {
+
+ @Override
+ public List> translateTag(CompoundTag tag, BlockState blockState) {
+ List> tags = new ArrayList<>();
+ tags.add(new IntTag("Age", (int) (long) tag.get("Age").getValue()));
+ // Java sometimes does not provide this tag, but Bedrock crashes if it doesn't exist
+ // Linked coordinates
+ List tagsList = new ArrayList<>();
+ // Yes, the axis letters are capitalized
+ tagsList.add(new IntTag("", getExitPortalCoordinate(tag, "X")));
+ tagsList.add(new IntTag("", getExitPortalCoordinate(tag, "Y")));
+ tagsList.add(new IntTag("", getExitPortalCoordinate(tag, "Z")));
+ com.nukkitx.nbt.tag.ListTag exitPortal =
+ new com.nukkitx.nbt.tag.ListTag<>("ExitPortal", IntTag.class, tagsList);
+ tags.add(exitPortal);
+ return tags;
+ }
+
+ @Override
+ public CompoundTag getDefaultJavaTag(String javaId, int x, int y, int z) {
+ CompoundTag tag = getConstantJavaTag(javaId, x, y, z);
+ tag.put(new LongTag("Age"));
+ return tag;
+ }
+
+ @Override
+ public com.nukkitx.nbt.tag.CompoundTag getDefaultBedrockTag(String bedrockId, int x, int y, int z) {
+ CompoundTagBuilder tagBuilder = getConstantBedrockTag(bedrockId, x, y, z).toBuilder();
+ List tagsList = new ArrayList<>();
+ tagsList.add(new IntTag("", 0));
+ tagsList.add(new IntTag("", 0));
+ tagsList.add(new IntTag("", 0));
+ tagBuilder.listTag("ExitPortal", IntTag.class, tagsList);
+ return tagBuilder.buildRootTag();
+ }
+
+ private int getExitPortalCoordinate(CompoundTag tag, String axis) {
+ // Return 0 if it doesn't exist, otherwise give proper value
+ if (tag.get("ExitPortal") != null) {
+ LinkedHashMap compoundTag = (LinkedHashMap) tag.get("ExitPortal").getValue();
+ com.github.steveice10.opennbt.tag.builtin.IntTag intTag = (com.github.steveice10.opennbt.tag.builtin.IntTag) compoundTag.get(axis);
+ return intTag.getValue();
+ } return 0;
+ }
+}
diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/block/entity/RequiresBlockState.java b/connector/src/main/java/org/geysermc/connector/network/translators/block/entity/RequiresBlockState.java
new file mode 100644
index 00000000..ed8e6ede
--- /dev/null
+++ b/connector/src/main/java/org/geysermc/connector/network/translators/block/entity/RequiresBlockState.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.block.entity;
+
+import com.github.steveice10.mc.protocol.data.game.world.block.BlockState;
+
+/**
+ * Implemented in block entities if their Java block state is required for additional values in Bedrock
+ */
+public interface RequiresBlockState {
+
+ /**
+ * Determines if block is part of class
+ * @param blockState BlockState to be compared
+ * @return true if part of the class
+ */
+ boolean isBlock(BlockState blockState);
+
+}
diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/block/entity/SignBlockEntityTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/block/entity/SignBlockEntityTranslator.java
new file mode 100644
index 00000000..74dcdd13
--- /dev/null
+++ b/connector/src/main/java/org/geysermc/connector/network/translators/block/entity/SignBlockEntityTranslator.java
@@ -0,0 +1,76 @@
+/*
+ * 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.block.entity;
+
+import com.github.steveice10.mc.protocol.data.game.world.block.BlockState;
+import com.github.steveice10.mc.protocol.data.message.Message;
+import com.github.steveice10.opennbt.tag.builtin.CompoundTag;
+import com.nukkitx.nbt.CompoundTagBuilder;
+import com.nukkitx.nbt.tag.StringTag;
+import com.nukkitx.nbt.tag.Tag;
+import org.geysermc.connector.utils.MessageUtils;
+
+import java.util.ArrayList;
+import java.util.List;
+
+@BlockEntity(name = "Sign", delay = true, regex = "sign")
+public class SignBlockEntityTranslator extends BlockEntityTranslator {
+
+ @Override
+ public List> translateTag(CompoundTag tag, BlockState blockState) {
+ List> tags = new ArrayList<>();
+
+ String line1 = getOrDefault(tag.getValue().get("Text1"), "");
+ String line2 = getOrDefault(tag.getValue().get("Text2"), "");
+ String line3 = getOrDefault(tag.getValue().get("Text3"), "");
+ String line4 = getOrDefault(tag.getValue().get("Text4"), "");
+
+ tags.add(new StringTag("Text", MessageUtils.getBedrockMessage(Message.fromString(line1))
+ + "\n" + MessageUtils.getBedrockMessage(Message.fromString(line2))
+ + "\n" + MessageUtils.getBedrockMessage(Message.fromString(line3))
+ + "\n" + MessageUtils.getBedrockMessage(Message.fromString(line4))
+ ));
+
+ return tags;
+ }
+
+ @Override
+ public CompoundTag getDefaultJavaTag(String javaId, int x, int y, int z) {
+ CompoundTag tag = getConstantJavaTag(javaId, x, y, z);
+ tag.put(new com.github.steveice10.opennbt.tag.builtin.StringTag("Text1", "{\"text\":\"\"}"));
+ tag.put(new com.github.steveice10.opennbt.tag.builtin.StringTag("Text2", "{\"text\":\"\"}"));
+ tag.put(new com.github.steveice10.opennbt.tag.builtin.StringTag("Text3", "{\"text\":\"\"}"));
+ tag.put(new com.github.steveice10.opennbt.tag.builtin.StringTag("Text4", "{\"text\":\"\"}"));
+ return tag;
+ }
+
+ @Override
+ public com.nukkitx.nbt.tag.CompoundTag getDefaultBedrockTag(String bedrockId, int x, int y, int z) {
+ CompoundTagBuilder tagBuilder = getConstantBedrockTag(bedrockId, x, y, z).toBuilder();
+ tagBuilder.stringTag("Text", "");
+ return tagBuilder.buildRootTag();
+ }
+}
diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/block/entity/SkullBlockEntityTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/block/entity/SkullBlockEntityTranslator.java
new file mode 100644
index 00000000..2380663b
--- /dev/null
+++ b/connector/src/main/java/org/geysermc/connector/network/translators/block/entity/SkullBlockEntityTranslator.java
@@ -0,0 +1,71 @@
+/*
+ * 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.block.entity;
+
+import com.github.steveice10.mc.protocol.data.game.world.block.BlockState;
+import com.nukkitx.nbt.CompoundTagBuilder;
+import com.nukkitx.nbt.tag.ByteTag;
+import com.nukkitx.nbt.tag.CompoundTag;
+import com.nukkitx.nbt.tag.FloatTag;
+import com.nukkitx.nbt.tag.Tag;
+import org.geysermc.connector.network.translators.block.BlockStateValues;
+
+import java.util.ArrayList;
+import java.util.List;
+
+@BlockEntity(name = "Skull", delay = false, regex = "skull")
+public class SkullBlockEntityTranslator extends BlockEntityTranslator implements RequiresBlockState {
+
+ @Override
+ public boolean isBlock(BlockState blockState) {
+ return BlockStateValues.getSkullVariant(blockState) != -1;
+ }
+
+ @Override
+ public List> translateTag(com.github.steveice10.opennbt.tag.builtin.CompoundTag tag, BlockState blockState) {
+ List> tags = new ArrayList<>();
+ byte skullVariant = BlockStateValues.getSkullVariant(blockState);
+ float rotation = BlockStateValues.getSkullRotation(blockState) * 22.5f;
+ // Just in case...
+ if (skullVariant == -1) skullVariant = 0;
+ tags.add(new FloatTag("Rotation", rotation));
+ tags.add(new ByteTag("SkullType", skullVariant));
+ return tags;
+ }
+
+ @Override
+ public com.github.steveice10.opennbt.tag.builtin.CompoundTag getDefaultJavaTag(String javaId, int x, int y, int z) {
+ return null;
+ }
+
+ @Override
+ public CompoundTag getDefaultBedrockTag(String bedrockId, int x, int y, int z) {
+ CompoundTagBuilder tagBuilder = getConstantBedrockTag(bedrockId, x, y, z).toBuilder();
+ tagBuilder.floatTag("Rotation", 0);
+ tagBuilder.byteTag("SkullType", (byte) 0);
+ return tagBuilder.buildRootTag();
+ }
+}
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..8df72d36
--- /dev/null
+++ b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/AnvilInventoryTranslator.java
@@ -0,0 +1,146 @@
+/*
+ * 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.message.Message;
+import com.github.steveice10.mc.protocol.packet.ingame.client.window.ClientRenameItemPacket;
+import com.github.steveice10.opennbt.tag.builtin.CompoundTag;
+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);
+ }
+
+ @Override
+ public void updateSlot(GeyserSession session, Inventory inventory, int slot) {
+ if (slot >= 0 && slot <= 2) {
+ ItemStack item = inventory.getItem(slot);
+ if (item != null) {
+ String rename;
+ CompoundTag tag = item.getNbt();
+ if (tag != null) {
+ CompoundTag displayTag = tag.get("display");
+ if (displayTag != null) {
+ String itemName = displayTag.get("Name").getValue().toString();
+ Message message = Message.fromString(itemName);
+ rename = message.getText();
+ } else {
+ rename = "";
+ }
+ } else {
+ rename = "";
+ }
+ ClientRenameItemPacket renameItemPacket = new ClientRenameItemPacket(rename);
+ session.getDownstream().getSession().send(renameItemPacket);
+ }
+ }
+ super.updateSlot(session, inventory, slot);
+ }
+}
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/GrindstoneInventoryTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/GrindstoneInventoryTranslator.java
new file mode 100644
index 00000000..174cfbc1
--- /dev/null
+++ b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/GrindstoneInventoryTranslator.java
@@ -0,0 +1,68 @@
+/*
+ * 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 org.geysermc.connector.network.translators.inventory.updater.CursorInventoryUpdater;
+
+public class GrindstoneInventoryTranslator extends BlockInventoryTranslator {
+
+ public GrindstoneInventoryTranslator() {
+ super(3, "minecraft:grindstone[face=floor,facing=north]", ContainerType.GRINDSTONE, new CursorInventoryUpdater());
+ }
+
+ @Override
+ public int bedrockSlotToJava(InventoryActionData action) {
+ final int slot = super.bedrockSlotToJava(action);
+ if (action.getSource().getContainerId() == 124) {
+ switch (slot) {
+ case 16:
+ return 0;
+ case 17:
+ return 1;
+ case 50:
+ return 2;
+ default:
+ return slot;
+ }
+ } return slot;
+ }
+
+ @Override
+ public int javaSlotToBedrock(int slot) {
+ switch (slot) {
+ case 0:
+ return 16;
+ case 1:
+ return 17;
+ case 2:
+ return 50;
+ }
+ return super.javaSlotToBedrock(slot);
+ }
+
+}
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..440d01dc
--- /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.Translators;
+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(Translators.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] = Translators.getItemTranslator().translateToBedrock(inventory.getItem(i));
+ }
+ // Hotbar
+ for (int i = 36; i < 45; i++) {
+ contents[i - 36] = Translators.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] = Translators.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[]{Translators.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(Translators.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[]{Translators.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 = Translators.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(Translators.getItemTranslator().translateToJava(action.getToItem()));
+ }
+ break;
+ case ContainerId.NONE:
+ if (action.getSource().getType() == InventorySource.Type.WORLD_INTERACTION
+ && action.getSource().getFlag() == InventorySource.Flag.DROP_ITEM) {
+ javaItem = Translators.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..750de5c8
--- /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.Translators;
+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 = Translators.getItemTranslator().translateToBedrock(session.getInventory().getCursor());
+ if (!translatedCursor.equals(action.getFromItem())) {
+ refresh = true;
+ }
+ } else {
+ containerAction = action;
+ ItemData translatedItem = Translators.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 f7be13c3..dcffcd35 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.Translators;
+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] = Translators.getItemTranslator().translateToBedrock(inventory.getItems()[i]);
+ if (i <= translator.size) {
+ bedrockItems[i] = Translators.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(Translators.getItemTranslator().translateToBedrock(inventory.getItems()[slot]));
- slotPacket.setSlot(slot);
+ slotPacket.setSlot(translator.javaSlotToBedrock(javaSlot));
+ slotPacket.setItem(Translators.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..812ce8f8
--- /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.Translators;
+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)] = Translators.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(Translators.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..f0c1c760
--- /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.Translators;
+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(Translators.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(Translators.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..32005685
--- /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.Translators;
+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] = Translators.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(Translators.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/ItemEntry.java b/connector/src/main/java/org/geysermc/connector/network/translators/item/ItemEntry.java
index fd4f0b02..e579c20e 100644
--- a/connector/src/main/java/org/geysermc/connector/network/translators/item/ItemEntry.java
+++ b/connector/src/main/java/org/geysermc/connector/network/translators/item/ItemEntry.java
@@ -34,11 +34,11 @@ public class ItemEntry {
public static ItemEntry AIR = new ItemEntry("minecraft:air", 0, 0, 0);
- private String javaIdentifier;
- private int javaId;
+ private final String javaIdentifier;
+ private final int javaId;
- private int bedrockId;
- private int bedrockData;
+ private final int bedrockId;
+ private final int bedrockData;
@Override
public boolean equals(Object obj) {
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 68f88bb2..0bcbf4b7 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
@@ -43,6 +43,7 @@ import com.github.steveice10.opennbt.tag.builtin.Tag;
import com.nukkitx.protocol.bedrock.data.ItemData;
import org.geysermc.connector.GeyserConnector;
+
import org.geysermc.connector.utils.MessageUtils;
import org.geysermc.connector.utils.Toolbox;
@@ -53,24 +54,45 @@ import java.util.Map;
public class ItemTranslator {
+ private Map javaIdentifierMap = new HashMap<>();
+
public ItemStack translateToJava(ItemData data) {
ItemEntry javaItem = getItem(data);
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()));
+ }
+ GeyserConnector.getInstance().getLogger().debug("Unknown java potion: " + potionTag.getValue());
+ }
}
// TODO: Create proper transformers instead of shoving everything here
@@ -92,7 +114,14 @@ 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;
+ }
+ }
+ // If item find was unsuccessful first time, we try again while ignoring damage
+ // Fixes piston, sticky pistons, dispensers and droppers turning into air from creative inventory
+ for (ItemEntry itemEntry : Toolbox.ITEM_ENTRIES.values()) {
+ if (itemEntry.getBedrockId() == data.getId()) {
return itemEntry;
}
}
@@ -101,6 +130,11 @@ public class ItemTranslator {
return ItemEntry.AIR;
}
+ public ItemEntry getItemEntry(String javaIdentifier) {
+ return javaIdentifierMap.computeIfAbsent(javaIdentifier, key -> Toolbox.ITEM_ENTRIES.values()
+ .stream().filter(itemEntry -> itemEntry.getJavaIdentifier().equals(key)).findFirst().orElse(null));
+ }
+
private CompoundTag translateToJavaNBT(com.nukkitx.nbt.tag.CompoundTag tag) {
CompoundTag javaTag = new CompoundTag(tag.getName());
Map javaValue = javaTag.getValue();
@@ -111,10 +145,11 @@ public class ItemTranslator {
if (translatedTag == null)
continue;
- javaValue.put(str, translatedTag);
+ javaValue.put(translatedTag.getName(), translatedTag);
}
}
+ javaTag.setValue(javaValue);
return javaTag;
}
@@ -173,6 +208,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 {
+ GeyserConnector.getInstance().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;
@@ -201,7 +259,7 @@ public class ItemTranslator {
if (translatedTag == null)
continue;
- javaValue.put(str, translatedTag);
+ javaValue.put(translatedTag.getName(), translatedTag);
}
}
@@ -262,7 +320,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) {
+ GeyserConnector.getInstance().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/item/ToolItemEntry.java b/connector/src/main/java/org/geysermc/connector/network/translators/item/ToolItemEntry.java
new file mode 100644
index 00000000..5d1ddd26
--- /dev/null
+++ b/connector/src/main/java/org/geysermc/connector/network/translators/item/ToolItemEntry.java
@@ -0,0 +1,15 @@
+package org.geysermc.connector.network.translators.item;
+
+import lombok.Getter;
+
+@Getter
+public class ToolItemEntry extends ItemEntry {
+ private final String toolType;
+ private final String toolTier;
+
+ public ToolItemEntry(String javaIdentifier, int javaId, int bedrockId, int bedrockData, String toolType, String toolTier) {
+ super(javaIdentifier, javaId, bedrockId, bedrockData);
+ this.toolType = toolType;
+ this.toolTier = toolTier;
+ }
+}
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..18b155e0
--- /dev/null
+++ b/connector/src/main/java/org/geysermc/connector/network/translators/java/JavaDeclareRecipesTranslator.java
@@ -0,0 +1,174 @@
+/*
+ * 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.Translator;
+import org.geysermc.connector.network.translators.Translators;
+import org.geysermc.connector.network.translators.item.ItemEntry;
+import org.geysermc.connector.utils.Toolbox;
+
+import java.util.*;
+import java.util.stream.Collectors;
+
+@Translator(packet = ServerDeclareRecipesPacket.class)
+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 = Translators.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 = Translators.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 -> Translators.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] = Translators.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 b28e9010..cc2c540f 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
@@ -53,6 +53,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/JavaEntityEffectTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/java/entity/JavaEntityEffectTranslator.java
index be54e967..88e0969e 100644
--- a/connector/src/main/java/org/geysermc/connector/network/translators/java/entity/JavaEntityEffectTranslator.java
+++ b/connector/src/main/java/org/geysermc/connector/network/translators/java/entity/JavaEntityEffectTranslator.java
@@ -26,6 +26,7 @@
package org.geysermc.connector.network.translators.java.entity;
import org.geysermc.connector.entity.Entity;
+import org.geysermc.connector.entity.PlayerEntity;
import org.geysermc.connector.network.session.GeyserSession;
import org.geysermc.connector.network.translators.PacketTranslator;
import org.geysermc.connector.network.translators.Translator;
@@ -42,6 +43,7 @@ public class JavaEntityEffectTranslator extends PacketTranslator {
@Override
public void translate(ServerPlayerActionAckPacket packet, GeyserSession session) {
+ LevelEventPacket levelEvent = new LevelEventPacket();
switch (packet.getAction()) {
case FINISH_DIGGING:
ChunkUtils.updateBlock(session, packet.getNewState(), packet.getPosition());
break;
+
+ case START_DIGGING:
+ levelEvent.setType(LevelEventType.BLOCK_START_BREAK);
+ levelEvent.setPosition(Vector3f.from(
+ packet.getPosition().getX(),
+ packet.getPosition().getY(),
+ packet.getPosition().getZ()
+ ));
+ double blockHardness = BlockTranslator.JAVA_RUNTIME_ID_TO_HARDNESS.get(packet.getNewState().getId());
+ PlayerInventory inventory = session.getInventory();
+ ItemStack item = inventory.getItemInHand();
+ ItemEntry itemEntry = null;
+ CompoundTag nbtData = new CompoundTag("");
+ if (item != null) {
+ itemEntry = Translators.getItemTranslator().getItem(item);
+ nbtData = item.getNbt();
+ }
+ double breakTime = Math.ceil(BlockUtils.getBreakTime(blockHardness, packet.getNewState().getId(), itemEntry, nbtData, session.getPlayerEntity()) * 20);
+ levelEvent.setData((int) (65535 / breakTime));
+ session.getUpstream().sendPacket(levelEvent);
+ break;
+
+ case CANCEL_DIGGING:
+ levelEvent.setType(LevelEventType.BLOCK_STOP_BREAK);
+ levelEvent.setPosition(Vector3f.from(
+ packet.getPosition().getX(),
+ packet.getPosition().getY(),
+ packet.getPosition().getZ()
+ ));
+ levelEvent.setData(0);
+ session.getUpstream().sendPacket(levelEvent);
+ break;
}
}
-}
+}
\ No newline at end of file
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..c4c1a3af
--- /dev/null
+++ b/connector/src/main/java/org/geysermc/connector/network/translators/java/entity/player/JavaPlayerChangeHeldItemTranslator.java
@@ -0,0 +1,47 @@
+/*
+ * 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;
+import org.geysermc.connector.network.translators.Translator;
+
+@Translator(packet = ServerPlayerChangeHeldItemPacket.class)
+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 2719a72a..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 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.Translator;
-import org.geysermc.connector.network.translators.Translators;
-import org.geysermc.connector.network.translators.inventory.InventoryTranslator;
-
-import com.github.steveice10.mc.protocol.packet.ingame.server.window.ServerOpenWindowPacket;
-
-@Translator(packet = ServerOpenWindowPacket.class)
-public class OpenWindowPacketTranslator extends PacketTranslator {
- @Override
- public void translate(ServerOpenWindowPacket packet, GeyserSession session) {
- System.out.println("debug: " + packet.getType());
- InventoryTranslator translator = Translators.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..8162b82a
--- /dev/null
+++ b/connector/src/main/java/org/geysermc/connector/network/translators/java/window/JavaCloseWindowTranslator.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.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.network.translators.Translator;
+import org.geysermc.connector.utils.InventoryUtils;
+
+@Translator(packet = ServerCloseWindowPacket.class)
+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..9ecf4559
--- /dev/null
+++ b/connector/src/main/java/org/geysermc/connector/network/translators/java/window/JavaConfirmTransactionTranslator.java
@@ -0,0 +1,47 @@
+/*
+ * 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.Translator;
+import org.geysermc.connector.network.translators.inventory.InventoryTranslator;
+import org.geysermc.connector.utils.InventoryUtils;
+
+@Translator(packet = ServerConfirmTransactionPacket.class)
+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 9495bed4..38276193 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,18 +25,68 @@
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.connector.GeyserConnector;
+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.Translator;
+import org.geysermc.connector.network.translators.Translators;
+import org.geysermc.connector.network.translators.inventory.InventoryTranslator;
import org.geysermc.connector.utils.InventoryUtils;
-import com.github.steveice10.mc.protocol.packet.ingame.server.window.ServerOpenWindowPacket;
+import java.util.concurrent.TimeUnit;
@Translator(packet = ServerOpenWindowPacket.class)
public class JavaOpenWindowTranslator extends PacketTranslator {
@Override
public void translate(ServerOpenWindowPacket packet, GeyserSession session) {
- InventoryUtils.openInventory(session, packet);
+ if (packet.getWindowId() == 0) {
+ return;
+ }
+ InventoryTranslator newTranslator = Translators.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);
+ Translators.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) {
+ GeyserConnector.getInstance().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 = Translators.getInventoryTranslators().get(openInventory.getWindowType());
+ if (!openTranslator.getClass().equals(newTranslator.getClass())) {
+ InventoryUtils.closeInventory(session, openInventory.getId());
+ GeyserConnector.getInstance().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 93df65e8..6fafa2a4 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,47 +25,41 @@
package org.geysermc.connector.network.translators.java.window;
+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.Translators;
+import org.geysermc.connector.network.translators.inventory.InventoryTranslator;
import org.geysermc.connector.network.translators.Translator;
import org.geysermc.connector.utils.InventoryUtils;
-import com.github.steveice10.mc.protocol.data.game.entity.metadata.ItemStack;
-import com.github.steveice10.mc.protocol.packet.ingame.server.window.ServerSetSlotPacket;
+import java.util.Objects;
@Translator(packet = ServerSetSlotPacket.class)
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 = Translators.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 f2287f6d..eab57a64 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
@@ -25,34 +25,34 @@
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.network.translators.Translator;
-import org.geysermc.connector.utils.InventoryUtils;
+import org.geysermc.connector.network.translators.Translators;
+import org.geysermc.connector.network.translators.inventory.InventoryTranslator;
-import com.github.steveice10.mc.protocol.packet.ingame.server.window.ServerWindowItemsPacket;
+import java.util.Arrays;
@Translator(packet = ServerWindowItemsPacket.class)
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 = Translators.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..827da7b7
--- /dev/null
+++ b/connector/src/main/java/org/geysermc/connector/network/translators/java/window/JavaWindowPropertyTranslator.java
@@ -0,0 +1,50 @@
+/*
+ * 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.Translator;
+import org.geysermc.connector.network.translators.Translators;
+import org.geysermc.connector.network.translators.inventory.InventoryTranslator;
+
+@Translator(packet = ServerWindowPropertyPacket.class)
+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 = Translators.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/network/translators/java/world/JavaBlockValueTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/java/world/JavaBlockValueTranslator.java
new file mode 100644
index 00000000..f4a4d9ef
--- /dev/null
+++ b/connector/src/main/java/org/geysermc/connector/network/translators/java/world/JavaBlockValueTranslator.java
@@ -0,0 +1,57 @@
+/*
+ * 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.world;
+
+import com.github.steveice10.mc.protocol.data.game.world.block.value.ChestValue;
+import com.github.steveice10.mc.protocol.data.game.world.block.value.EndGatewayValue;
+import com.github.steveice10.mc.protocol.packet.ingame.server.world.ServerBlockValuePacket;
+import com.nukkitx.math.vector.Vector3i;
+import com.nukkitx.protocol.bedrock.packet.BlockEventPacket;
+
+import org.geysermc.connector.network.session.GeyserSession;
+import org.geysermc.connector.network.translators.PacketTranslator;
+import org.geysermc.connector.network.translators.Translator;
+
+@Translator(packet = ServerBlockValuePacket.class)
+public class JavaBlockValueTranslator extends PacketTranslator {
+
+ @Override
+ public void translate(ServerBlockValuePacket packet, GeyserSession session) {
+ BlockEventPacket blockEventPacket = new BlockEventPacket();
+ blockEventPacket.setBlockPosition(Vector3i.from(packet.getPosition().getX(),
+ packet.getPosition().getY(), packet.getPosition().getZ()));
+ if (packet.getValue() instanceof ChestValue) {
+ ChestValue value = (ChestValue) packet.getValue() ;
+ blockEventPacket.setEventType(1);
+ blockEventPacket.setEventData(value.getViewers() > 0 ? 1 : 0);
+ session.getUpstream().sendPacket(blockEventPacket);
+ }
+ if (packet.getValue() instanceof EndGatewayValue) {
+ blockEventPacket.setEventType(1);
+ session.getUpstream().sendPacket(blockEventPacket);
+ }
+ }
+}
diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/java/world/JavaChunkDataTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/java/world/JavaChunkDataTranslator.java
index bb73c5f0..e72038c5 100644
--- a/connector/src/main/java/org/geysermc/connector/network/translators/java/world/JavaChunkDataTranslator.java
+++ b/connector/src/main/java/org/geysermc/connector/network/translators/java/world/JavaChunkDataTranslator.java
@@ -25,6 +25,19 @@
package org.geysermc.connector.network.translators.java.world;
+import com.github.steveice10.mc.protocol.data.game.world.block.BlockState;
+import com.github.steveice10.mc.protocol.data.game.entity.metadata.Position;
+import com.github.steveice10.mc.protocol.packet.ingame.server.world.ServerChunkDataPacket;
+import com.nukkitx.nbt.NbtUtils;
+import com.nukkitx.nbt.stream.NBTOutputStream;
+import com.nukkitx.nbt.tag.CompoundTag;
+import com.nukkitx.network.VarInts;
+import com.nukkitx.protocol.bedrock.packet.LevelChunkPacket;
+
+import io.netty.buffer.ByteBuf;
+import io.netty.buffer.ByteBufOutputStream;
+import io.netty.buffer.Unpooled;
+import it.unimi.dsi.fastutil.objects.Object2IntMap;
import org.geysermc.connector.GeyserConnector;
import org.geysermc.connector.network.session.GeyserSession;
import org.geysermc.connector.network.translators.BiomeTranslator;
@@ -33,14 +46,7 @@ import org.geysermc.connector.network.translators.Translator;
import org.geysermc.connector.utils.ChunkUtils;
import org.geysermc.connector.world.chunk.ChunkSection;
-import com.github.steveice10.mc.protocol.data.game.chunk.Chunk;
-import com.github.steveice10.mc.protocol.data.game.world.block.BlockState;
-import com.github.steveice10.mc.protocol.packet.ingame.server.world.ServerChunkDataPacket;
-import com.nukkitx.network.VarInts;
-import com.nukkitx.protocol.bedrock.packet.LevelChunkPacket;
-
-import io.netty.buffer.ByteBuf;
-import io.netty.buffer.Unpooled;
+import java.util.Map;
@Translator(packet = ServerChunkDataPacket.class)
public class JavaChunkDataTranslator extends PacketTranslator {
@@ -54,7 +60,6 @@ public class JavaChunkDataTranslator extends PacketTranslator {
try {
ChunkUtils.ChunkData chunkData = ChunkUtils.translateToBedrock(packet.getColumn());
@@ -78,6 +83,14 @@ public class JavaChunkDataTranslator extends PacketTranslator blockEntityEntry : chunkData.getLoadBlockEntitiesLater().object2IntEntrySet()) {
+ int x = blockEntityEntry.getKey().getInt("x");
+ int y = blockEntityEntry.getKey().getInt("y");
+ int z = blockEntityEntry.getKey().getInt("z");
+ ChunkUtils.updateBlock(session, new BlockState(blockEntityEntry.getIntValue()), new Position(x, y, z));
+ }
+ chunkData.getLoadBlockEntitiesLater().clear();
+
} catch (Exception ex) {
ex.printStackTrace();
}
diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/java/world/JavaUpdateTileEntityTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/java/world/JavaUpdateTileEntityTranslator.java
new file mode 100644
index 00000000..b1158ef1
--- /dev/null
+++ b/connector/src/main/java/org/geysermc/connector/network/translators/java/world/JavaUpdateTileEntityTranslator.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.java.world;
+
+import com.github.steveice10.mc.protocol.packet.ingame.server.world.ServerUpdateTileEntityPacket;
+
+import org.geysermc.connector.network.session.GeyserSession;
+import org.geysermc.connector.network.translators.PacketTranslator;
+import org.geysermc.connector.network.translators.Translator;
+import org.geysermc.connector.network.translators.block.entity.BlockEntity;
+import org.geysermc.connector.network.translators.block.entity.BlockEntityTranslator;
+import org.geysermc.connector.utils.BlockEntityUtils;
+import org.geysermc.connector.utils.ChunkUtils;
+
+import java.util.concurrent.TimeUnit;
+
+@Translator(packet = ServerUpdateTileEntityPacket.class)
+public class JavaUpdateTileEntityTranslator extends PacketTranslator {
+
+ // This should be modified if sign text is not showing up
+ private static final int DELAY = 500;
+
+ @Override
+ public void translate(ServerUpdateTileEntityPacket packet, GeyserSession session) {
+ String id = BlockEntityUtils.getBedrockBlockEntityId(packet.getType().name());
+ BlockEntityTranslator translator = BlockEntityUtils.getBlockEntityTranslator(id);
+ // If not null then the BlockState is used in BlockEntityTranslator.translateTag()
+ if (ChunkUtils.CACHED_BLOCK_ENTITIES.get(packet.getPosition()) != null) {
+ BlockEntityUtils.updateBlockEntity(session, translator.getBlockEntityTag(id, packet.getNbt(),
+ ChunkUtils.CACHED_BLOCK_ENTITIES.get(packet.getPosition())), packet.getPosition());
+ ChunkUtils.CACHED_BLOCK_ENTITIES.remove(packet.getPosition());
+ } else if (translator.getClass().getAnnotation(BlockEntity.class).delay()) {
+ // Delay so chunks can finish sending
+ session.getConnector().getGeneralThreadPool().schedule(() ->
+ BlockEntityUtils.updateBlockEntity(session, translator.getBlockEntityTag(id, packet.getNbt(), null), packet.getPosition()),
+ DELAY, TimeUnit.MILLISECONDS);
+ } else {
+ BlockEntityUtils.updateBlockEntity(session, translator.getBlockEntityTag(id, packet.getNbt(), null), packet.getPosition());
+ }
+ }
+}
diff --git a/connector/src/main/java/org/geysermc/connector/utils/BlockEntityUtils.java b/connector/src/main/java/org/geysermc/connector/utils/BlockEntityUtils.java
new file mode 100644
index 00000000..0dcd13ad
--- /dev/null
+++ b/connector/src/main/java/org/geysermc/connector/utils/BlockEntityUtils.java
@@ -0,0 +1,47 @@
+package org.geysermc.connector.utils;
+
+import com.github.steveice10.mc.protocol.data.game.entity.metadata.Position;
+import com.nukkitx.math.vector.Vector3i;
+import com.nukkitx.protocol.bedrock.packet.BlockEntityDataPacket;
+
+import org.geysermc.connector.network.session.GeyserSession;
+import org.geysermc.connector.network.translators.Translators;
+import org.geysermc.connector.network.translators.block.entity.BlockEntityTranslator;
+
+public class BlockEntityUtils {
+
+ private static final BlockEntityTranslator EMPTY_TRANSLATOR = Translators.getBlockEntityTranslators().get("Empty");
+
+ public static String getBedrockBlockEntityId(String id) {
+ // This is the only exception when it comes to block entity ids
+ if (id.contains("piston_head"))
+ return "PistonArm";
+
+ id = id.toLowerCase()
+ .replace("minecraft:", "")
+ .replace("_", " ");
+ String[] words = id.split(" ");
+ for (int i = 0; i < words.length; i++) {
+ words[i] = words[i].substring(0, 1).toUpperCase() + words[i].substring(1).toLowerCase();
+ }
+
+ id = String.join(" ", words);
+ return id.replace(" ", "");
+ }
+
+ public static BlockEntityTranslator getBlockEntityTranslator(String name) {
+ BlockEntityTranslator blockEntityTranslator = Translators.getBlockEntityTranslators().get(name);
+ if (blockEntityTranslator == null) {
+ return EMPTY_TRANSLATOR;
+ }
+
+ return blockEntityTranslator;
+ }
+
+ public static void updateBlockEntity(GeyserSession session, com.nukkitx.nbt.tag.CompoundTag blockEntity, Position position) {
+ BlockEntityDataPacket blockEntityPacket = new BlockEntityDataPacket();
+ blockEntityPacket.setBlockPosition(Vector3i.from(position.getX(), position.getY(), position.getZ()));
+ blockEntityPacket.setData(blockEntity);
+ session.getUpstream().sendPacket(blockEntityPacket);
+ }
+}
diff --git a/connector/src/main/java/org/geysermc/connector/utils/BlockUtils.java b/connector/src/main/java/org/geysermc/connector/utils/BlockUtils.java
new file mode 100644
index 00000000..34287073
--- /dev/null
+++ b/connector/src/main/java/org/geysermc/connector/utils/BlockUtils.java
@@ -0,0 +1,128 @@
+/*
+ * 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.utils;
+
+import com.github.steveice10.mc.protocol.data.game.entity.Effect;
+import com.github.steveice10.opennbt.tag.builtin.CompoundTag;
+import org.geysermc.connector.entity.PlayerEntity;
+import org.geysermc.connector.network.translators.block.BlockTranslator;
+import org.geysermc.connector.network.translators.item.ItemEntry;
+import org.geysermc.connector.network.translators.item.ToolItemEntry;
+
+public class BlockUtils {
+
+ private static boolean correctTool(String blockToolType, String itemToolType) {
+ return (blockToolType.equals("sword") && itemToolType.equals("sword")) ||
+ (blockToolType.equals("shovel") && itemToolType.equals("shovel")) ||
+ (blockToolType.equals("pickaxe") && itemToolType.equals("pickaxe")) ||
+ (blockToolType.equals("axe") && itemToolType.equals("axe")) ||
+ (blockToolType.equals("shears") && itemToolType.equals("shears"));
+ }
+
+ private static double toolBreakTimeBonus(String toolType, String toolTier, boolean isWoolBlock) {
+ if (toolType.equals("shears")) return isWoolBlock ? 5.0 : 15.0;
+ if (toolType.equals("")) return 1.0;
+ switch (toolTier) {
+ case "wooden":
+ return 2.0;
+ case "stone":
+ return 4.0;
+ case "iron":
+ return 6.0;
+ case "diamond":
+ return 8.0;
+ case "golden":
+ return 12.0;
+ default:
+ return 1.0;
+ }
+ }
+
+ //http://minecraft.gamepedia.com/Breaking
+ private static double calculateBreakTime(double blockHardness, String toolTier, boolean canHarvestWithHand, boolean correctTool,
+ String toolType, boolean isWoolBlock, boolean isCobweb, int toolEfficiencyLevel, int hasteLevel, int miningFatigueLevel
+ /*boolean insideOfWaterWithoutAquaAffinity, boolean outOfWaterButNotOnGround*/) {
+ double baseTime = ((correctTool || canHarvestWithHand) ? 1.5 : 5.0) * blockHardness;
+ double speed = 1.0 / baseTime;
+
+ if (correctTool) {
+ speed *= toolBreakTimeBonus(toolType, toolTier, isWoolBlock);
+ speed += toolEfficiencyLevel == 0 ? 0 : toolEfficiencyLevel * toolEfficiencyLevel + 1;
+ } else if (toolType.equals("sword")) {
+ speed*= (isCobweb ? 15.0 : 1.5);
+ }
+ speed *= 1.0 + (0.2 * hasteLevel);
+
+ switch (miningFatigueLevel) {
+ case 0:
+ break;
+ case 1:
+ speed -= (speed * 0.7);
+ break;
+ case 2:
+ speed -= (speed * 0.91);
+ break;
+ case 3:
+ speed -= (speed * 0.9973);
+ break;
+ default:
+ speed -= (speed * 0.99919);
+ break;
+ }
+
+ //if (insideOfWaterWithoutAquaAffinity) speed *= 0.2;
+ //if (outOfWaterButNotOnGround) speed *= 0.2;
+ // else if insideWaterAndNotOnGround speed *= 0.2;
+ return 1.0 / speed;
+ }
+
+ public static double getBreakTime(double blockHardness, int blockId, ItemEntry item, CompoundTag nbtData, PlayerEntity player) {
+ boolean isWoolBlock = BlockTranslator.JAVA_RUNTIME_WOOL_IDS.contains(blockId);
+ boolean isCobweb = blockId == BlockTranslator.JAVA_RUNTIME_COBWEB_ID;
+ String blockToolType = BlockTranslator.JAVA_RUNTIME_ID_TO_TOOL_TYPE.getOrDefault(blockId, "");
+ boolean canHarvestWithHand = BlockTranslator.JAVA_RUNTIME_ID_TO_CAN_HARVEST_WITH_HAND.get(blockId);
+ String toolType = "";
+ String toolTier = "";
+ boolean correctTool = false;
+ if (item instanceof ToolItemEntry) {
+ ToolItemEntry toolItem = (ToolItemEntry) item;
+ toolType = toolItem.getToolType();
+ toolTier = toolItem.getToolTier();
+ correctTool = correctTool(blockToolType, toolType);
+ }
+ int toolEfficiencyLevel = ItemUtils.getEnchantmentLevel(nbtData, "minecraft:efficiency");
+ int hasteLevel = player.getEffectCache().getEffectLevel(Effect.FASTER_DIG);
+ int miningFatigueLevel = player.getEffectCache().getEffectLevel(Effect.SLOWER_DIG);
+
+ // TODO implement these checks and material check if possible
+ //boolean insideOfWaterWithoutAquaAffinity = player.isInsideOfWater() &&
+ // Optional.ofNullable(player.getInventory().getHelmet().getEnchantment(Enchantment.ID_WATER_WORKER))
+ // .map(Enchantment::getLevel).map(l -> l >= 1).orElse(false);
+ //boolean outOfWaterButNotOnGround = (!player.isInsideOfWater()) && (!player.isOnGround());
+ return calculateBreakTime(blockHardness, toolTier, canHarvestWithHand, correctTool, toolType, isWoolBlock, isCobweb, toolEfficiencyLevel, hasteLevel, miningFatigueLevel);
+ }
+
+}
diff --git a/connector/src/main/java/org/geysermc/connector/utils/ChunkUtils.java b/connector/src/main/java/org/geysermc/connector/utils/ChunkUtils.java
index a35b2cc5..d496215a 100644
--- a/connector/src/main/java/org/geysermc/connector/utils/ChunkUtils.java
+++ b/connector/src/main/java/org/geysermc/connector/utils/ChunkUtils.java
@@ -29,27 +29,47 @@ import com.github.steveice10.mc.protocol.data.game.chunk.Chunk;
import com.github.steveice10.mc.protocol.data.game.chunk.Column;
import com.github.steveice10.mc.protocol.data.game.entity.metadata.Position;
import com.github.steveice10.mc.protocol.data.game.world.block.BlockState;
+import com.github.steveice10.opennbt.tag.builtin.CompoundTag;
import com.nukkitx.math.vector.Vector2i;
import com.nukkitx.math.vector.Vector3i;
import com.nukkitx.protocol.bedrock.packet.LevelChunkPacket;
import com.nukkitx.protocol.bedrock.packet.NetworkChunkPublisherUpdatePacket;
import com.nukkitx.protocol.bedrock.packet.UpdateBlockPacket;
+
+import it.unimi.dsi.fastutil.objects.Object2IntMap;
+import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap;
+
+import lombok.Getter;
+import org.geysermc.connector.GeyserConnector;
import org.geysermc.connector.network.session.GeyserSession;
+import org.geysermc.connector.network.translators.block.entity.*;
import org.geysermc.connector.network.translators.Translators;
import org.geysermc.connector.network.translators.block.BlockTranslator;
+import org.geysermc.connector.world.chunk.ChunkPosition;
import org.geysermc.connector.world.chunk.ChunkSection;
+import java.util.HashMap;
+import java.util.Map;
+
import static org.geysermc.connector.network.translators.block.BlockTranslator.BEDROCK_WATER_ID;
public class ChunkUtils {
+
+ /**
+ * Temporarily stores positions of BlockState values that are needed for certain block entities actively
+ */
+ public static final Map CACHED_BLOCK_ENTITIES = new HashMap<>();
+
public static ChunkData translateToBedrock(Column column) {
ChunkData chunkData = new ChunkData();
-
Chunk[] chunks = column.getChunks();
- int chunkSectionCount = chunks.length;
- chunkData.sections = new ChunkSection[chunkSectionCount];
+ chunkData.sections = new ChunkSection[chunks.length];
- for (int chunkY = 0; chunkY < chunkSectionCount; chunkY++) {
+ CompoundTag[] blockEntities = column.getTileEntities();
+ // Temporarily stores positions of BlockState values per chunk load
+ Map blockEntityPositions = new HashMap<>();
+
+ for (int chunkY = 0; chunkY < chunks.length; chunkY++) {
chunkData.sections[chunkY] = new ChunkSection();
Chunk chunk = chunks[chunkY];
@@ -57,14 +77,28 @@ public class ChunkUtils {
continue;
ChunkSection section = chunkData.sections[chunkY];
-
for (int x = 0; x < 16; x++) {
for (int y = 0; y < 16; y++) {
for (int z = 0; z < 16; z++) {
BlockState blockState = chunk.get(x, y, z);
int id = BlockTranslator.getBedrockBlockId(blockState);
- section.getBlockStorageArray()[0].setFullBlock(ChunkSection.blockPosition(x, y, z), id);
+ // Check to see if the name is in BlockTranslator.getBlockEntityString, and therefore must be handled differently
+ if (BlockTranslator.getBlockEntityString(blockState) != null) {
+ // Get the block entity translator
+ BlockEntityTranslator blockEntityTranslator = BlockEntityUtils.getBlockEntityTranslator(BlockTranslator.getBlockEntityString(blockState));
+ Position pos = new ChunkPosition(column.getX(), column.getZ()).getBlock(x, (chunkY << 4) + y, z);
+ blockEntityPositions.put(pos, blockState);
+ // If there is a delay required for the block, allow it.
+ if (blockEntityTranslator.getClass().getAnnotation(BlockEntity.class).delay()) {
+ chunkData.loadBlockEntitiesLater.put(blockEntityTranslator.getDefaultBedrockTag(BlockEntityUtils.getBedrockBlockEntityId(BlockTranslator.getBlockEntityString(blockState)),
+ pos.getX(), pos.getY(), pos.getZ()), blockState.getId());
+ } else {
+ section.getBlockStorageArray()[0].setFullBlock(ChunkSection.blockPosition(x, y, z), id);
+ }
+ } else {
+ section.getBlockStorageArray()[0].setFullBlock(ChunkSection.blockPosition(x, y, z), id);
+ }
if (BlockTranslator.isWaterlogged(blockState)) {
section.getBlockStorageArray()[1].setFullBlock(ChunkSection.blockPosition(x, y, z), BEDROCK_WATER_ID);
@@ -72,7 +106,27 @@ public class ChunkUtils {
}
}
}
+
}
+
+ com.nukkitx.nbt.tag.CompoundTag[] bedrockBlockEntities = new com.nukkitx.nbt.tag.CompoundTag[blockEntities.length];
+ for (int i = 0; i < blockEntities.length; i++) {
+ CompoundTag tag = blockEntities[i];
+ String tagName;
+ if (!tag.contains("id")) {
+ GeyserConnector.getInstance().getLogger().debug("Got tag with no id: " + tag.getValue());
+ tagName = "Empty";
+ } else {
+ tagName = (String) tag.get("id").getValue();
+ }
+
+ String id = BlockEntityUtils.getBedrockBlockEntityId(tagName);
+ BlockEntityTranslator blockEntityTranslator = BlockEntityUtils.getBlockEntityTranslator(id);
+ BlockState blockState = blockEntityPositions.get(new Position((int) tag.get("x").getValue(), (int) tag.get("y").getValue(), (int) tag.get("z").getValue()));
+ bedrockBlockEntities[i] = blockEntityTranslator.getBlockEntityTag(tagName, tag, blockState);
+ }
+
+ chunkData.blockEntities = bedrockBlockEntities;
return chunkData;
}
@@ -114,6 +168,19 @@ public class ChunkUtils {
waterPacket.setRuntimeId(0);
}
session.getUpstream().sendPacket(waterPacket);
+
+ // Since Java stores bed colors/skull information as part of the namespaced ID and Bedrock stores it as a tag
+ // This is the only place I could find that interacts with the Java block state and block updates
+ // Iterates through all block entity translators and determines if the block state needs to be saved
+ for (Map.Entry entry : Translators.getBlockEntityTranslators().entrySet()) {
+ if (entry.getValue() instanceof RequiresBlockState) {
+ RequiresBlockState requiresBlockState = (RequiresBlockState) entry.getValue();
+ if (requiresBlockState.isBlock(blockState)) {
+ CACHED_BLOCK_ENTITIES.put(new Position(position.getX(), position.getY(), position.getZ()), blockState);
+ break; //No block will be a part of two classes
+ }
+ }
+ }
}
public static void sendEmptyChunks(GeyserSession session, Vector3i position, int radius, boolean forceUpdate) {
@@ -144,6 +211,9 @@ public class ChunkUtils {
public static final class ChunkData {
public ChunkSection[] sections;
- public byte[] blockEntities = new byte[0];
+ @Getter
+ private com.nukkitx.nbt.tag.CompoundTag[] blockEntities = new com.nukkitx.nbt.tag.CompoundTag[0];
+ @Getter
+ private Object2IntMap loadBlockEntitiesLater = new Object2IntOpenHashMap<>();
}
}
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 045b81d1..69886851 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.connector.GeyserConnector;
import org.geysermc.connector.inventory.Inventory;
import org.geysermc.connector.network.session.GeyserSession;
import org.geysermc.connector.network.translators.Translators;
+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] = Translators.getItemTranslator().translateToBedrock(inventory.getItems()[i]);
+ public static void openInventory(GeyserSession session, Inventory inventory) {
+ InventoryTranslator translator = Translators.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) {
+ GeyserConnector.getInstance().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] = Translators.getItemTranslator().translateToBedrock(inventory.getItems()[i]);
- }
-
- // Armor
- for (int i = 5; i < 9; i++) {
- contents[i + 31] = Translators.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 = Translators.getInventoryTranslator();
- translator.prepareInventory(session, inventory);
- GeyserConnector.getInstance().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 = Translators.getInventoryTranslators().get(inventory.getWindowType());
+ translator.closeInventory(session, inventory);
+ session.getInventoryCache().uncacheInventory(windowId);
+ session.getInventoryCache().setOpenInventory(null);
+ }
+ } else {
+ Inventory inventory = session.getInventory();
+ InventoryTranslator translator = Translators.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 = Translators.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(Translators.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 = Translators.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/ItemUtils.java b/connector/src/main/java/org/geysermc/connector/utils/ItemUtils.java
new file mode 100644
index 00000000..bb3cf0ed
--- /dev/null
+++ b/connector/src/main/java/org/geysermc/connector/utils/ItemUtils.java
@@ -0,0 +1,47 @@
+/*
+ * 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.utils;
+
+import com.github.steveice10.opennbt.tag.builtin.*;
+
+public class ItemUtils {
+
+ public static int getEnchantmentLevel(CompoundTag itemNBTData, String enchantmentId) {
+ ListTag enchantments = (itemNBTData == null ? null : itemNBTData.get("Enchantments"));
+ if (enchantments != null) {
+ int enchantmentLevel = 0;
+ for (Tag tag : enchantments) {
+ CompoundTag enchantment = (CompoundTag) tag;
+ StringTag enchantId = enchantment.get("id");
+ if (enchantId.getValue().equals(enchantmentId)) {
+ enchantmentLevel = (int) ((ShortTag) enchantment.get("lvl")).getValue();
+ }
+ }
+ return enchantmentLevel;
+ }
+ return 0;
+ }
+}
diff --git a/connector/src/main/java/org/geysermc/connector/utils/SkinUtils.java b/connector/src/main/java/org/geysermc/connector/utils/SkinUtils.java
index 9480cffd..012bd73a 100644
--- a/connector/src/main/java/org/geysermc/connector/utils/SkinUtils.java
+++ b/connector/src/main/java/org/geysermc/connector/utils/SkinUtils.java
@@ -191,7 +191,6 @@ public class SkinUtils {
if (skinAndCapeConsumer != null) skinAndCapeConsumer.accept(skinAndCape);
});
-
});
}
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 989f9a3a..a2978e9d 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;
@@ -39,6 +40,7 @@ import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
import org.geysermc.connector.GeyserConnector;
import org.geysermc.connector.network.translators.item.ItemEntry;
+import org.geysermc.connector.network.translators.item.ToolItemEntry;
import java.io.*;
import java.util.*;
@@ -47,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 List ITEMS = new ArrayList<>();
@@ -101,13 +104,63 @@ public class Toolbox {
Iterator> iterator = items.fields();
while (iterator.hasNext()) {
Map.Entry entry = iterator.next();
- ITEM_ENTRIES.put(itemIndex, new ItemEntry(entry.getKey(), itemIndex,
- entry.getValue().get("bedrock_id").intValue(), entry.getValue().get("bedrock_data").intValue()));
+ if (entry.getValue().has("tool_type")) {
+ if (entry.getValue().has("tool_tier")) {
+ ITEM_ENTRIES.put(itemIndex, new ToolItemEntry(
+ entry.getKey(), itemIndex,
+ entry.getValue().get("bedrock_id").intValue(),
+ entry.getValue().get("bedrock_data").intValue(),
+ entry.getValue().get("tool_type").textValue(),
+ entry.getValue().get("tool_tier").textValue()));
+ } else {
+ ITEM_ENTRIES.put(itemIndex, new ToolItemEntry(
+ entry.getKey(), itemIndex,
+ entry.getValue().get("bedrock_id").intValue(),
+ entry.getValue().get("bedrock_data").intValue(),
+ entry.getValue().get("tool_type").textValue(),
+ ""));
+ }
+ } else {
+ ITEM_ENTRIES.put(itemIndex, new ItemEntry(
+ entry.getKey(), itemIndex,
+ entry.getValue().get("bedrock_id").intValue(),
+ entry.getValue().get("bedrock_data").intValue()));
+ }
itemIndex++;
}
// Load the locale data
LocaleUtils.init();
+
+ 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]);
}
/**
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..c45a7c94 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
@@ -48,7 +48,6 @@ public class ChunkPosition {
int chunkX = x & 15;
int chunkY = y & 15;
int chunkZ = z & 15;
-
return new Position(chunkX, chunkY, chunkZ);
}
}
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="
diff --git a/connector/src/main/resources/mappings b/connector/src/main/resources/mappings
index efc9db6b..a6a790d9 160000
--- a/connector/src/main/resources/mappings
+++ b/connector/src/main/resources/mappings
@@ -1 +1 @@
-Subproject commit efc9db6b7d51bdf145230933ac23b321ac1c132d
+Subproject commit a6a790d944eb153c93d0c414813d8a0880fd5d26