> IGNORED_PACKETS = new ObjectArrayList<>();
+
+ static {
+ Reflections ref = new Reflections("org.geysermc.connector.network.translators");
+
+ for (Class> clazz : ref.getTypesAnnotatedWith(Translator.class)) {
+ Class> packet = clazz.getAnnotation(Translator.class).packet();
+
+ GeyserConnector.getInstance().getLogger().debug("Found annotated translator: " + clazz.getCanonicalName() + " : " + packet.getSimpleName());
+
+ try {
+ if (Packet.class.isAssignableFrom(packet)) {
+ Class extends Packet> targetPacket = (Class extends Packet>) packet;
+ PacketTranslator extends Packet> translator = (PacketTranslator extends Packet>) clazz.newInstance();
+
+ JAVA_TRANSLATOR.translators.put(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();
+
+ BEDROCK_TRANSLATOR.translators.put(targetPacket, translator);
+ } else {
+ GeyserConnector.getInstance().getLogger().error("Class " + clazz.getCanonicalName() + " is annotated as a translator but has an invalid target packet.");
+ }
+ } catch (InstantiationException | IllegalAccessException e) {
+ GeyserConnector.getInstance().getLogger().error("Could not instantiate annotated translator " + clazz.getCanonicalName() + ".");
+ }
+ }
+
+ IGNORED_PACKETS.add(ServerKeepAlivePacket.class); // Handled by MCProtocolLib
+ IGNORED_PACKETS.add(ServerUpdateLightPacket.class); // Light is handled on Bedrock for us
+ }
+
+ private PacketTranslatorRegistry() {
+ }
+
+ public static void init() {
+ // no-op
+ }
+
+ @SuppressWarnings("unchecked")
+ public boolean translate(Class extends P> clazz, P packet, GeyserSession session) {
+ if (!session.getUpstream().isClosed() && !session.isClosed()) {
+ try {
+ if (translators.containsKey(clazz)) {
+ ((PacketTranslator
) translators.get(clazz)).translate(packet, session);
+ return true;
+ } else {
+ if (!IGNORED_PACKETS.contains(clazz))
+ GeyserConnector.getInstance().getLogger().debug("Could not find packet for " + (packet.toString().length() > 25 ? packet.getClass().getSimpleName() : packet));
+ }
+ } catch (Throwable ex) {
+ GeyserConnector.getInstance().getLogger().error("Could not translate packet " + packet.getClass().getSimpleName(), ex);
+ ex.printStackTrace();
+ }
+ }
+ return false;
+ }
+}
diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/Registry.java b/connector/src/main/java/org/geysermc/connector/network/translators/Registry.java
deleted file mode 100644
index 70201ba8..00000000
--- a/connector/src/main/java/org/geysermc/connector/network/translators/Registry.java
+++ /dev/null
@@ -1,68 +0,0 @@
-/*
- * 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;
-
-import java.util.HashMap;
-import java.util.Map;
-
-import org.geysermc.connector.GeyserConnector;
-import org.geysermc.connector.network.session.GeyserSession;
-
-import com.github.steveice10.packetlib.packet.Packet;
-import com.nukkitx.protocol.bedrock.BedrockPacket;
-
-public class Registry {
- private final Map, PacketTranslator extends T>> MAP = new HashMap<>();
-
- public static final Registry JAVA = new Registry<>();
- public static final Registry BEDROCK = new Registry<>();
-
- public static void registerJava(Class extends Packet> targetPacket, PacketTranslator extends Packet> translator) {
- JAVA.MAP.put(targetPacket, translator);
- }
-
- public static void registerBedrock(Class extends BedrockPacket> targetPacket, PacketTranslator extends BedrockPacket> translator) {
- BEDROCK.MAP.put(targetPacket, translator);
- }
-
- @SuppressWarnings("unchecked")
- public boolean translate(Class extends P> clazz, P packet, GeyserSession session) {
- if (!session.getUpstream().isClosed() && !session.isClosed()) {
- try {
- if (MAP.containsKey(clazz)) {
- ((PacketTranslator
) MAP.get(clazz)).translate(packet, session);
- return true;
- } else {
- GeyserConnector.getInstance().getLogger().debug("Could not find packet for " + (packet.toString().length() > 25 ? packet.getClass().getSimpleName() : packet));
- }
- } catch (Throwable ex) {
- GeyserConnector.getInstance().getLogger().error("Could not translate packet " + packet.getClass().getSimpleName(), ex);
- ex.printStackTrace();
- }
- }
- return false;
- }
-}
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
deleted file mode 100644
index f0a3fd28..00000000
--- a/connector/src/main/java/org/geysermc/connector/network/translators/Translators.java
+++ /dev/null
@@ -1,159 +0,0 @@
-/*
- * 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;
-
-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.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;
-
-import com.github.steveice10.packetlib.packet.Packet;
-import com.nukkitx.nbt.CompoundTagBuilder;
-import com.nukkitx.nbt.NbtUtils;
-import com.nukkitx.nbt.stream.NBTOutputStream;
-import com.nukkitx.nbt.tag.CompoundTag;
-import com.nukkitx.protocol.bedrock.BedrockPacket;
-
-import lombok.Getter;
-
-public class Translators {
-
- @Getter
- private static ItemTranslator itemTranslator;
-
- @Getter
- 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;
-
- static {
- try (ByteArrayOutputStream outputStream = new ByteArrayOutputStream()) {
- outputStream.write(new byte[258]); // Biomes + Border Size + Extra Data Size
-
- try (NBTOutputStream stream = NbtUtils.createNetworkWriter(outputStream)) {
- stream.write(EMPTY_TAG);
- }
-
- EMPTY_LEVEL_CHUNK_DATA = outputStream.toByteArray();
- }catch (IOException e) {
- throw new AssertionError("Unable to generate empty level chunk data");
- }
- }
-
- @SuppressWarnings("unchecked")
- public static void start() {
- Reflections ref = new Reflections("org.geysermc.connector.network.translators");
-
- for (Class> clazz : ref.getTypesAnnotatedWith(Translator.class)) {
- Class> packet = clazz.getAnnotation(Translator.class).packet();
-
- GeyserConnector.getInstance().getLogger().debug("Found annotated translator: " + clazz.getCanonicalName() + " : " + packet.getSimpleName());
-
- try {
- 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.");
- }
- } catch (InstantiationException | IllegalAccessException e) {
- GeyserConnector.getInstance().getLogger().error("Could not instantiate annotated translator " + clazz.getCanonicalName() + ".");
- }
- }
-
- itemTranslator = new ItemTranslator();
- itemTranslator.init();
- 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(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/BedrockActionTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockActionTranslator.java
index 7ab71389..2c44e4fd 100644
--- a/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockActionTranslator.java
+++ b/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockActionTranslator.java
@@ -27,6 +27,8 @@ package org.geysermc.connector.network.translators.bedrock;
import java.util.concurrent.TimeUnit;
+import com.nukkitx.protocol.bedrock.data.LevelEventType;
+import com.nukkitx.protocol.bedrock.packet.LevelEventPacket;
import org.geysermc.connector.entity.Entity;
import org.geysermc.connector.network.session.GeyserSession;
import org.geysermc.connector.network.translators.PacketTranslator;
@@ -41,6 +43,7 @@ import com.github.steveice10.mc.protocol.packet.ingame.client.player.ClientPlaye
import com.nukkitx.math.vector.Vector3i;
import com.nukkitx.protocol.bedrock.packet.PlayStatusPacket;
import com.nukkitx.protocol.bedrock.packet.PlayerActionPacket;
+import org.geysermc.connector.network.translators.world.block.BlockTranslator;
@Translator(packet = PlayerActionPacket.class)
public class BedrockActionTranslator extends PacketTranslator {
@@ -61,42 +64,44 @@ public class BedrockActionTranslator extends PacketTranslator {
+
+ @Override
+ public void translate(AdventureSettingsPacket packet, GeyserSession session) {
+ // Only canFly and flying are used by the server
+ // https://wiki.vg/Protocol#Player_Abilities_.28serverbound.29
+ boolean canFly = packet.getFlags().contains(AdventureSettingsPacket.Flag.MAY_FLY);
+ boolean flying = packet.getFlags().contains(AdventureSettingsPacket.Flag.FLYING);
+ boolean creative = session.getGameMode() == GameMode.CREATIVE;
+ ClientPlayerAbilitiesPacket abilitiesPacket = new ClientPlayerAbilitiesPacket(
+ false, canFly, flying, creative, 0.05f, 0.1f
+ );
+ session.sendDownstreamPacket(abilitiesPacket);
+ }
+}
diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockAnimateTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockAnimateTranslator.java
index af41c9e0..012582da 100644
--- a/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockAnimateTranslator.java
+++ b/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockAnimateTranslator.java
@@ -31,6 +31,7 @@ import org.geysermc.connector.network.translators.Translator;
import com.github.steveice10.mc.protocol.data.game.entity.player.Hand;
import com.github.steveice10.mc.protocol.packet.ingame.client.player.ClientPlayerSwingArmPacket;
+import com.github.steveice10.mc.protocol.packet.ingame.client.world.ClientSteerBoatPacket;
import com.nukkitx.protocol.bedrock.packet.AnimatePacket;
import java.util.concurrent.TimeUnit;
@@ -38,6 +39,9 @@ import java.util.concurrent.TimeUnit;
@Translator(packet = AnimatePacket.class)
public class BedrockAnimateTranslator extends PacketTranslator {
+ private boolean isSteeringLeft;
+ private boolean isSteeringRight;
+
@Override
public void translate(AnimatePacket packet, GeyserSession session) {
// Stop the player sending animations before they have fully spawned into the server
@@ -49,11 +53,23 @@ public class BedrockAnimateTranslator extends PacketTranslator {
case SWING_ARM:
// Delay so entity damage can be processed first
session.getConnector().getGeneralThreadPool().schedule(() ->
- session.getDownstream().getSession().send(new ClientPlayerSwingArmPacket(Hand.MAIN_HAND)),
+ session.sendDownstreamPacket(new ClientPlayerSwingArmPacket(Hand.MAIN_HAND)),
25,
TimeUnit.MILLISECONDS
);
break;
+ // These two might need to be flipped, but my recommendation is getting moving working first
+ case ROW_LEFT:
+ // Packet value is a float of how long one has been rowing, so we convert that into a boolean
+ isSteeringLeft = packet.getRowingTime() > 0.0;
+ ClientSteerBoatPacket steerLeftPacket = new ClientSteerBoatPacket(isSteeringRight, isSteeringLeft);
+ session.sendDownstreamPacket(steerLeftPacket);
+ break;
+ case ROW_RIGHT:
+ isSteeringRight = packet.getRowingTime() > 0.0;
+ ClientSteerBoatPacket steerRightPacket = new ClientSteerBoatPacket(isSteeringRight, isSteeringLeft);
+ session.sendDownstreamPacket(steerRightPacket);
+ break;
}
}
}
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
index dc076a91..9fe62bb4 100644
--- 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
@@ -79,7 +79,7 @@ public class BedrockBlockEntityDataTranslator extends PacketTranslator {
+
+ @Override
+ public void translate(BlockPickRequestPacket packet, GeyserSession session) {
+ Vector3i vector = packet.getBlockPosition();
+ BlockState blockToPick = session.getConnector().getWorldManager().getBlockAt(session, vector.getX(), vector.getY(), vector.getZ());
+
+ // Block is air - chunk caching is probably off
+ if (blockToPick.getId() == 0) {
+ return;
+ }
+
+ // Get the inventory to choose a slot to pick
+ Inventory inventory = session.getInventoryCache().getOpenInventory();
+ if (inventory == null) {
+ inventory = session.getInventory();
+ }
+
+ String targetIdentifier = BlockTranslator.getJavaIdBlockMap().inverse().get(blockToPick).split("\\[")[0];
+
+ // Check hotbar for item
+ for (int i = 36; i < 45; i++) {
+ if (inventory.getItem(i) == null) {
+ continue;
+ }
+ ItemEntry item = ItemRegistry.getItem(inventory.getItem(i));
+ // If this isn't the item we're looking for
+ if (!item.getJavaIdentifier().equals(targetIdentifier)) {
+ continue;
+ }
+
+ PlayerHotbarPacket hotbarPacket = new PlayerHotbarPacket();
+ hotbarPacket.setContainerId(0);
+ // Java inventory slot to hotbar slot ID
+ hotbarPacket.setSelectedHotbarSlot(i - 36);
+ hotbarPacket.setSelectHotbarSlot(true);
+ session.sendUpstreamPacket(hotbarPacket);
+ session.getInventory().setHeldItemSlot(i - 36);
+ // Don't check inventory if item was in hotbar
+ return;
+ }
+
+ // Check inventory for item
+ for (int i = 9; i < 36; i++) {
+ if (inventory.getItem(i) == null) {
+ continue;
+ }
+ ItemEntry item = ItemRegistry.getItem(inventory.getItem(i));
+ // If this isn't the item we're looking for
+ if (!item.getJavaIdentifier().equals(targetIdentifier)) {
+ continue;
+ }
+
+ ClientMoveItemToHotbarPacket packetToSend = new ClientMoveItemToHotbarPacket(i); // https://wiki.vg/Protocol#Pick_Item
+ session.sendDownstreamPacket(packetToSend);
+ return;
+ }
+ }
+}
diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockCommandRequestTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockCommandRequestTranslator.java
index c8e11741..1f31367c 100644
--- a/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockCommandRequestTranslator.java
+++ b/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockCommandRequestTranslator.java
@@ -53,7 +53,7 @@ public class BedrockCommandRequestTranslator extends PacketTranslator {
+
+ @Override
+ public void translate(EntityEventPacket packet, GeyserSession session) {
+ switch (packet.getType()) {
+ // Resend the packet so we get the eating sounds
+ case EATING_ITEM:
+ session.sendUpstreamPacket(packet);
+ return;
+ }
+ session.getConnector().getLogger().debug("Did not translate incoming EntityEventPacket: " + packet.toString());
+ }
+}
diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockInteractTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockInteractTranslator.java
index 0d1c08b2..bfd4a90a 100644
--- a/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockInteractTranslator.java
+++ b/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockInteractTranslator.java
@@ -25,6 +25,8 @@
package org.geysermc.connector.network.translators.bedrock;
+import com.nukkitx.protocol.bedrock.data.EntityData;
+import com.nukkitx.protocol.bedrock.data.EntityFlag;
import org.geysermc.connector.entity.Entity;
import org.geysermc.connector.network.session.GeyserSession;
import org.geysermc.connector.network.translators.PacketTranslator;
@@ -32,9 +34,11 @@ import org.geysermc.connector.network.translators.Translator;
import com.github.steveice10.mc.protocol.data.game.entity.player.Hand;
import com.github.steveice10.mc.protocol.data.game.entity.player.InteractAction;
+import com.github.steveice10.mc.protocol.data.game.entity.player.PlayerState;
import com.github.steveice10.mc.protocol.packet.ingame.client.player.ClientPlayerInteractEntityPacket;
+import com.github.steveice10.mc.protocol.packet.ingame.client.player.ClientPlayerStatePacket;
import com.nukkitx.protocol.bedrock.packet.InteractPacket;
-import org.geysermc.connector.network.translators.item.ItemTranslator;
+import org.geysermc.connector.network.translators.item.ItemRegistry;
@Translator(packet = InteractPacket.class)
public class BedrockInteractTranslator extends PacketTranslator {
@@ -47,17 +51,69 @@ public class BedrockInteractTranslator extends PacketTranslator
switch (packet.getAction()) {
case INTERACT:
- if (session.getInventory().getItem(session.getInventory().getHeldItemSlot() + 36).getId() == ItemTranslator.SHIELD) {
+ if (session.getInventory().getItem(session.getInventory().getHeldItemSlot() + 36).getId() == ItemRegistry.SHIELD) {
break;
}
ClientPlayerInteractEntityPacket interactPacket = new ClientPlayerInteractEntityPacket((int) entity.getEntityId(),
InteractAction.INTERACT, Hand.MAIN_HAND);
- session.getDownstream().getSession().send(interactPacket);
+ session.sendDownstreamPacket(interactPacket);
break;
case DAMAGE:
ClientPlayerInteractEntityPacket attackPacket = new ClientPlayerInteractEntityPacket((int) entity.getEntityId(),
InteractAction.ATTACK, Hand.MAIN_HAND);
- session.getDownstream().getSession().send(attackPacket);
+ session.sendDownstreamPacket(attackPacket);
+ break;
+ case LEAVE_VEHICLE:
+ ClientPlayerStatePacket sneakPacket = new ClientPlayerStatePacket((int) entity.getEntityId(), PlayerState.START_SNEAKING);
+ session.sendDownstreamPacket(sneakPacket);
+ session.setRidingVehicleEntity(null);
+ break;
+ case MOUSEOVER:
+ // Handle the buttons for mobile - "Mount", etc; and the suggestions for console - "ZL: Mount", etc
+ if (packet.getRuntimeEntityId() != 0) {
+ Entity interactEntity = session.getEntityCache().getEntityByGeyserId(packet.getRuntimeEntityId());
+ if (interactEntity == null)
+ return;
+
+ String interactiveTag;
+ switch (interactEntity.getEntityType()) {
+ case PIG:
+ if (interactEntity.getMetadata().getFlags().getFlag(EntityFlag.SADDLED)) {
+ interactiveTag = "action.interact.mount";
+ } else interactiveTag = "";
+ break;
+ case HORSE:
+ case SKELETON_HORSE:
+ case ZOMBIE_HORSE:
+ case DONKEY:
+ case MULE:
+ case LLAMA:
+ case TRADER_LLAMA:
+ if (interactEntity.getMetadata().getFlags().getFlag(EntityFlag.TAMED)) {
+ interactiveTag = "action.interact.ride.horse";
+ } else {
+ interactiveTag = "action.interact.mount";
+ }
+ break;
+ case BOAT:
+ interactiveTag = "action.interact.ride.boat";
+ break;
+ case MINECART:
+ interactiveTag = "action.interact.ride.minecart";
+ break;
+ default:
+ return; // No need to process any further since there is no interactive tag
+ }
+ session.getPlayerEntity().getMetadata().put(EntityData.INTERACTIVE_TAG, interactiveTag);
+ session.getPlayerEntity().updateBedrockMetadata(session);
+ } else {
+ if (!(session.getPlayerEntity().getMetadata().get(EntityData.INTERACTIVE_TAG) == null) ||
+ !(session.getPlayerEntity().getMetadata().get(EntityData.INTERACTIVE_TAG) == "")) {
+ // No interactive tag should be sent
+ session.getPlayerEntity().getMetadata().remove(EntityData.INTERACTIVE_TAG);
+ session.getPlayerEntity().updateBedrockMetadata(session);
+ }
+ }
break;
}
}
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 3f6eba55..8f96b800 100644
--- a/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockInventoryTransactionTranslator.java
+++ b/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockInventoryTransactionTranslator.java
@@ -25,17 +25,7 @@
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.network.translators.item.ItemTranslator;
-import org.geysermc.connector.utils.InventoryUtils;
-
-import com.nukkitx.math.vector.Vector3f;
+import com.github.steveice10.mc.protocol.data.game.entity.metadata.ItemStack;
import com.github.steveice10.mc.protocol.data.game.entity.metadata.Position;
import com.github.steveice10.mc.protocol.data.game.entity.player.GameMode;
import com.github.steveice10.mc.protocol.data.game.entity.player.Hand;
@@ -44,9 +34,28 @@ import com.github.steveice10.mc.protocol.data.game.entity.player.PlayerAction;
import com.github.steveice10.mc.protocol.data.game.world.block.BlockFace;
import com.github.steveice10.mc.protocol.packet.ingame.client.player.ClientPlayerActionPacket;
import com.github.steveice10.mc.protocol.packet.ingame.client.player.ClientPlayerInteractEntityPacket;
+import com.github.steveice10.mc.protocol.packet.ingame.client.player.ClientPlayerPlaceBlockPacket;
import com.github.steveice10.mc.protocol.packet.ingame.client.player.ClientPlayerUseItemPacket;
+import com.github.steveice10.mc.protocol.data.game.world.block.BlockState;
+import com.nukkitx.math.vector.Vector3i;
+import com.nukkitx.math.vector.Vector3f;
+import com.nukkitx.protocol.bedrock.data.LevelEventType;
+import com.nukkitx.protocol.bedrock.packet.LevelEventPacket;
import com.nukkitx.protocol.bedrock.packet.InventoryTransactionPacket;
+import org.geysermc.connector.entity.Entity;
+import org.geysermc.connector.entity.ItemFrameEntity;
+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.network.translators.item.ItemEntry;
+import org.geysermc.connector.network.translators.item.ItemRegistry;
+import org.geysermc.connector.network.translators.sound.EntitySoundInteractionHandler;
+import org.geysermc.connector.network.translators.world.block.BlockTranslator;
+import org.geysermc.connector.utils.InventoryUtils;
+
@Translator(packet = InventoryTransactionPacket.class)
public class BedrockInventoryTransactionTranslator extends PacketTranslator {
@@ -56,37 +65,111 @@ public class BedrockInventoryTransactionTranslator extends PacketTranslator {
+
+ @Override
+ public void translate(ItemFrameDropItemPacket packet, GeyserSession session) {
+ // I hope that, when we die, God (or whoever is waiting for us) tells us exactly why this code exists
+ // The packet sends the Y coordinate (and just the Y coordinate) divided by two, and it's negative if it needs to be subtracted by one
+ int y;
+ if (packet.getBlockPosition().getY() > 0) {
+ y = packet.getBlockPosition().getY() * 2;
+ } else {
+ y = (packet.getBlockPosition().getY() * -2) - 1;
+ }
+ Vector3i position = Vector3i.from(packet.getBlockPosition().getX(), y, packet.getBlockPosition().getZ());
+ ClientPlayerInteractEntityPacket interactPacket = new ClientPlayerInteractEntityPacket((int) ItemFrameEntity.getItemFrameEntityId(session, position),
+ InteractAction.ATTACK, Hand.MAIN_HAND);
+ session.sendDownstreamPacket(interactPacket);
+ }
+
+}
diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockLevelSoundEventTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockLevelSoundEventTranslator.java
new file mode 100644
index 00000000..6395f0a1
--- /dev/null
+++ b/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockLevelSoundEventTranslator.java
@@ -0,0 +1,41 @@
+/*
+ * 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.nukkitx.protocol.bedrock.packet.LevelSoundEventPacket;
+import org.geysermc.connector.network.session.GeyserSession;
+import org.geysermc.connector.network.translators.PacketTranslator;
+import org.geysermc.connector.network.translators.Translator;
+
+@Translator(packet = LevelSoundEventPacket.class)
+public class BedrockLevelSoundEventTranslator extends PacketTranslator {
+
+ @Override
+ public void translate(LevelSoundEventPacket packet, GeyserSession session) {
+ // lol what even :thinking:
+ session.sendUpstreamPacket(packet);
+ }
+}
diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockMobEquipmentTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockMobEquipmentTranslator.java
index 3427a99f..5fc7f41a 100644
--- a/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockMobEquipmentTranslator.java
+++ b/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockMobEquipmentTranslator.java
@@ -46,6 +46,6 @@ public class BedrockMobEquipmentTranslator extends PacketTranslator {
+
+ @Override
+ public void translate(MoveEntityAbsolutePacket packet, GeyserSession session) {
+
+ ClientVehicleMovePacket clientVehicleMovePacket = new ClientVehicleMovePacket(
+ packet.getPosition().getX(), packet.getPosition().getY(), packet.getPosition().getZ(),
+ packet.getRotation().getY() - 90, packet.getRotation().getX()
+ );
+ session.sendDownstreamPacket(clientVehicleMovePacket);
+ }
+}
diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockMovePlayerTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockMovePlayerTranslator.java
index 0ae3d88a..14d597fd 100644
--- a/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockMovePlayerTranslator.java
+++ b/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockMovePlayerTranslator.java
@@ -25,6 +25,7 @@
package org.geysermc.connector.network.translators.bedrock;
+import com.nukkitx.math.vector.Vector3d;
import org.geysermc.common.ChatColor;
import org.geysermc.connector.entity.Entity;
import org.geysermc.connector.entity.PlayerEntity;
@@ -34,7 +35,6 @@ import org.geysermc.connector.network.translators.PacketTranslator;
import org.geysermc.connector.network.translators.Translator;
import com.github.steveice10.mc.protocol.packet.ingame.client.player.ClientPlayerPositionRotationPacket;
-import com.nukkitx.math.GenericMath;
import com.nukkitx.math.vector.Vector3f;
import com.nukkitx.protocol.bedrock.packet.MoveEntityAbsolutePacket;
import com.nukkitx.protocol.bedrock.packet.MovePlayerPacket;
@@ -55,7 +55,19 @@ public class BedrockMovePlayerTranslator extends PacketTranslator {
+
+ @Override
+ public void translate(PlayerInputPacket packet, GeyserSession session) {
+ ClientSteerVehiclePacket clientSteerVehiclePacket = new ClientSteerVehiclePacket(
+ packet.getInputMotion().getX(), packet.getInputMotion().getY(), packet.isJumping(), packet.isSneaking()
+ );
+
+ session.sendDownstreamPacket(clientSteerVehiclePacket);
+ }
+}
diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockRespawnTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockRespawnTranslator.java
index 3c7e85ec..8fc377ab 100644
--- a/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockRespawnTranslator.java
+++ b/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockRespawnTranslator.java
@@ -44,10 +44,10 @@ public class BedrockRespawnTranslator extends PacketTranslator {
respawnPacket.setRuntimeEntityId(0);
respawnPacket.setPosition(Vector3f.ZERO);
respawnPacket.setState(RespawnPacket.State.SERVER_SEARCHING);
- session.getUpstream().sendPacket(respawnPacket);
+ session.sendUpstreamPacket(respawnPacket);
ClientRequestPacket javaRespawnPacket = new ClientRequestPacket(ClientRequest.RESPAWN);
- session.getDownstream().getSession().send(javaRespawnPacket);
+ session.sendDownstreamPacket(javaRespawnPacket);
}
}
}
diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockShowCreditsTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockShowCreditsTranslator.java
index 161397b6..4f0af78c 100644
--- a/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockShowCreditsTranslator.java
+++ b/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockShowCreditsTranslator.java
@@ -40,7 +40,7 @@ public class BedrockShowCreditsTranslator extends PacketTranslator {
@Override
public void translate(TextPacket packet, GeyserSession session) {
- if (packet.getMessage().charAt(0) == '.') {
- String message = packet.getMessage().replace(".", "/").trim();
-
- if (MessageUtils.isTooLong(message, session)) {
- return;
- }
-
- ClientChatPacket chatPacket = new ClientChatPacket(message);
- session.getDownstream().getSession().send(chatPacket);
- return;
- }
-
- String message = packet.getMessage().trim();
+ String message = packet.getMessage().replaceAll("^\\.", "/").trim();
if (MessageUtils.isTooLong(message, session)) {
return;
}
ClientChatPacket chatPacket = new ClientChatPacket(message);
- session.getDownstream().getSession().send(chatPacket);
+ session.sendDownstreamPacket(chatPacket);
}
}
diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/block/BlockStateValues.java b/connector/src/main/java/org/geysermc/connector/network/translators/block/BlockStateValues.java
deleted file mode 100644
index 25d6070f..00000000
--- a/connector/src/main/java/org/geysermc/connector/network/translators/block/BlockStateValues.java
+++ /dev/null
@@ -1,127 +0,0 @@
-/*
- * 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;
-
-import com.fasterxml.jackson.databind.JsonNode;
-import com.github.steveice10.mc.protocol.data.game.world.block.BlockState;
-import it.unimi.dsi.fastutil.objects.Object2ByteMap;
-import it.unimi.dsi.fastutil.objects.Object2ByteOpenHashMap;
-import it.unimi.dsi.fastutil.objects.Object2IntMap;
-import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap;
-
-import java.util.Map;
-
-/**
- * Used for block entities if the Java block state contains Bedrock block information.
- */
-public class BlockStateValues {
-
- private static final Object2IntMap 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 to 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/common/src/main/java/org/geysermc/common/command/ICommandManager.java b/connector/src/main/java/org/geysermc/connector/network/translators/effect/Effect.java
similarity index 78%
rename from common/src/main/java/org/geysermc/common/command/ICommandManager.java
rename to connector/src/main/java/org/geysermc/connector/network/translators/effect/Effect.java
index f46dfafc..4c58235a 100644
--- a/common/src/main/java/org/geysermc/common/command/ICommandManager.java
+++ b/connector/src/main/java/org/geysermc/connector/network/translators/effect/Effect.java
@@ -23,16 +23,21 @@
* @link https://github.com/GeyserMC/Geyser
*/
-package org.geysermc.common.command;
+package org.geysermc.connector.network.translators.effect;
-public interface ICommandManager {
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+import lombok.Setter;
- /**
- * Returns the description of the given command
- *
- * @param command Command to get the description for
- *
- * @return Command description
- */
- String getDescription(String command);
-}
+@Getter
+@Setter
+@AllArgsConstructor
+public class Effect {
+
+ private String javaName;
+ private String bedrockName;
+ private String type;
+ private int data;
+ private String identifier;
+
+}
\ No newline at end of file
diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/effect/EffectRegistry.java b/connector/src/main/java/org/geysermc/connector/network/translators/effect/EffectRegistry.java
new file mode 100644
index 00000000..50888090
--- /dev/null
+++ b/connector/src/main/java/org/geysermc/connector/network/translators/effect/EffectRegistry.java
@@ -0,0 +1,123 @@
+/*
+ * 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.effect;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import com.github.steveice10.mc.protocol.data.game.world.particle.ParticleType;
+import com.nukkitx.protocol.bedrock.data.LevelEventType;
+import com.nukkitx.protocol.bedrock.data.SoundEvent;
+import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
+import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
+import lombok.NonNull;
+import org.geysermc.connector.GeyserConnector;
+import org.geysermc.connector.utils.FileUtils;
+
+import java.io.InputStream;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+
+/**
+ * Registry for particles and effects.
+ */
+public class EffectRegistry {
+
+ public static final Map EFFECTS = new HashMap<>();
+ public static final Int2ObjectMap RECORDS = new Int2ObjectOpenHashMap<>();
+
+ private static Map particleTypeMap = new HashMap<>();
+ private static Map particleStringMap = new HashMap<>();
+
+ public static void init() {
+ // no-op
+ }
+
+ static {
+ /* Load particles */
+ InputStream particleStream = FileUtils.getResource("mappings/particles.json");
+ JsonNode particleEntries;
+ try {
+ particleEntries = GeyserConnector.JSON_MAPPER.readTree(particleStream);
+ } catch (Exception e) {
+ throw new AssertionError("Unable to load particle map", e);
+ }
+
+ Iterator> particlesIterator = particleEntries.fields();
+ while (particlesIterator.hasNext()) {
+ Map.Entry entry = particlesIterator.next();
+ try {
+ particleTypeMap.put(ParticleType.valueOf(entry.getKey().toUpperCase()), LevelEventType.valueOf(entry.getValue().asText().toUpperCase()));
+ } catch (IllegalArgumentException e1) {
+ try {
+ particleStringMap.put(ParticleType.valueOf(entry.getKey().toUpperCase()), entry.getValue().asText());
+ GeyserConnector.getInstance().getLogger().debug("Force to map particle "
+ + entry.getKey()
+ + "=>"
+ + entry.getValue().asText()
+ + ", it will take effect.");
+ } catch (IllegalArgumentException e2){
+ GeyserConnector.getInstance().getLogger().warning("Fail to map particle " + entry.getKey() + "=>" + entry.getValue().asText());
+ }
+ }
+ }
+
+ /* Load effects */
+ InputStream effectsStream = FileUtils.getResource("mappings/effects.json");
+ JsonNode effects;
+ try {
+ effects = GeyserConnector.JSON_MAPPER.readTree(effectsStream);
+ } catch (Exception e) {
+ throw new AssertionError("Unable to load effects mappings", e);
+ }
+
+ Iterator> effectsIterator = effects.fields();
+ while (effectsIterator.hasNext()) {
+ Map.Entry entry = effectsIterator.next();
+ // Separate records database since they're handled differently between the two versions
+ if (entry.getValue().has("records")) {
+ JsonNode records = entry.getValue().get("records");
+ Iterator> recordsIterator = records.fields();
+ while (recordsIterator.hasNext()) {
+ Map.Entry recordEntry = recordsIterator.next();
+ RECORDS.put(Integer.parseInt(recordEntry.getKey()), SoundEvent.valueOf(recordEntry.getValue().asText()));
+ }
+ }
+ String identifier = (entry.getValue().has("identifier")) ? entry.getValue().get("identifier").asText() : "";
+ int data = (entry.getValue().has("data")) ? entry.getValue().get("data").asInt() : -1;
+ Effect effect = new Effect(entry.getKey(), entry.getValue().get("name").asText(), entry.getValue().get("type").asText(), data, identifier);
+ EFFECTS.put(entry.getKey(), effect);
+ }
+ }
+
+ public static LevelEventType getParticleLevelEventType(@NonNull ParticleType type) {
+ return particleTypeMap.getOrDefault(type, null);
+ }
+
+ public static String getParticleString(@NonNull ParticleType type){
+ return particleStringMap.getOrDefault(type, null);
+ }
+}
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
index 8df72d36..f301d2b5 100644
--- 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
@@ -108,7 +108,7 @@ public class AnvilInventoryTranslator extends BlockInventoryTranslator {
rename = "";
}
ClientRenameItemPacket renameItemPacket = new ClientRenameItemPacket(rename);
- session.getDownstream().getSession().send(renameItemPacket);
+ session.sendDownstreamPacket(renameItemPacket);
}
if (anvilResult != null) {
//client will send another packet to grab anvil output
@@ -138,7 +138,7 @@ public class AnvilInventoryTranslator extends BlockInventoryTranslator {
rename = "";
}
ClientRenameItemPacket renameItemPacket = new ClientRenameItemPacket(rename);
- session.getDownstream().getSession().send(renameItemPacket);
+ session.sendDownstreamPacket(renameItemPacket);
}
}
super.updateSlot(session, inventory, slot);
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
index 5f6274f0..ab410ea8 100644
--- 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
@@ -29,7 +29,7 @@ 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.world.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;
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
index bd143698..acda7c91 100644
--- 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
@@ -44,7 +44,7 @@ public class BrewingInventoryTranslator extends BlockInventoryTranslator {
dataPacket.setWindowId((byte) inventory.getId());
dataPacket.setProperty(ContainerSetDataPacket.BREWING_STAND_FUEL_TOTAL);
dataPacket.setValue(20);
- session.getUpstream().sendPacket(dataPacket);
+ session.sendUpstreamPacket(dataPacket);
}
@Override
@@ -62,7 +62,7 @@ public class BrewingInventoryTranslator extends BlockInventoryTranslator {
return;
}
dataPacket.setValue(value);
- session.getUpstream().sendPacket(dataPacket);
+ session.sendUpstreamPacket(dataPacket);
}
@Override
diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/inventory/ChestInventoryTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/ChestInventoryTranslator.java
new file mode 100644
index 00000000..13684a60
--- /dev/null
+++ b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/ChestInventoryTranslator.java
@@ -0,0 +1,69 @@
+/*
+ * 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.InventoryActionData;
+import org.geysermc.connector.inventory.Inventory;
+import org.geysermc.connector.network.session.GeyserSession;
+import org.geysermc.connector.network.translators.inventory.updater.ChestInventoryUpdater;
+import org.geysermc.connector.network.translators.inventory.updater.InventoryUpdater;
+import org.geysermc.connector.utils.InventoryUtils;
+
+import java.util.List;
+
+public abstract class ChestInventoryTranslator extends BaseInventoryTranslator {
+ private final InventoryUpdater updater;
+
+ public ChestInventoryTranslator(int size, int paddedSize) {
+ super(size);
+ this.updater = new ChestInventoryUpdater(paddedSize);
+ }
+
+ @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 void translateActions(GeyserSession session, Inventory inventory, List actions) {
+ for (InventoryActionData action : actions) {
+ if (action.getSource().getContainerId() == inventory.getId()) {
+ if (action.getSlot() >= size) {
+ 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/CraftingInventoryTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/CraftingInventoryTranslator.java
index 92a1d90e..e31eb1e3 100644
--- 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
@@ -59,7 +59,7 @@ public class CraftingInventoryTranslator extends BaseInventoryTranslator {
containerOpenPacket.setType((byte) ContainerType.WORKBENCH.id());
containerOpenPacket.setBlockPosition(inventory.getHolderPosition());
containerOpenPacket.setUniqueEntityId(inventory.getHolderId());
- session.getUpstream().sendPacket(containerOpenPacket);
+ session.sendUpstreamPacket(containerOpenPacket);
}
@Override
@@ -92,7 +92,10 @@ public class CraftingInventoryTranslator extends BaseInventoryTranslator {
@Override
public int javaSlotToBedrock(int slot) {
- return slot == 0 ? 50 : slot + 31;
+ if (slot < size) {
+ return slot == 0 ? 50 : slot + 31;
+ }
+ return super.javaSlotToBedrock(slot);
}
@Override
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
index c70a8995..6d6cadd7 100644
--- 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
@@ -35,19 +35,17 @@ 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.world.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 {
+public class DoubleChestInventoryTranslator extends ChestInventoryTranslator {
private final int blockId;
- private final InventoryUpdater updater;
public DoubleChestInventoryTranslator(int size) {
- super(size);
+ super(size, 54);
BlockState javaBlockState = BlockTranslator.getJavaBlockState("minecraft:chest[facing=north,type=single,waterlogged=false]");
this.blockId = BlockTranslator.getBedrockBlockId(javaBlockState);
- this.updater = new ChestInventoryUpdater(54);
}
@Override
@@ -60,7 +58,7 @@ public class DoubleChestInventoryTranslator extends BaseInventoryTranslator {
blockPacket.setBlockPosition(position);
blockPacket.setRuntimeId(blockId);
blockPacket.getFlags().addAll(UpdateBlockPacket.FLAG_ALL_PRIORITY);
- session.getUpstream().sendPacket(blockPacket);
+ session.sendUpstreamPacket(blockPacket);
CompoundTag tag = CompoundTag.builder()
.stringTag("id", "Chest")
@@ -73,14 +71,14 @@ public class DoubleChestInventoryTranslator extends BaseInventoryTranslator {
BlockEntityDataPacket dataPacket = new BlockEntityDataPacket();
dataPacket.setData(tag);
dataPacket.setBlockPosition(position);
- session.getUpstream().sendPacket(dataPacket);
+ session.sendUpstreamPacket(dataPacket);
blockPacket = new UpdateBlockPacket();
blockPacket.setDataLayer(0);
blockPacket.setBlockPosition(pairPosition);
blockPacket.setRuntimeId(blockId);
blockPacket.getFlags().addAll(UpdateBlockPacket.FLAG_ALL_PRIORITY);
- session.getUpstream().sendPacket(blockPacket);
+ session.sendUpstreamPacket(blockPacket);
tag = CompoundTag.builder()
.stringTag("id", "Chest")
@@ -93,7 +91,7 @@ public class DoubleChestInventoryTranslator extends BaseInventoryTranslator {
dataPacket = new BlockEntityDataPacket();
dataPacket.setData(tag);
dataPacket.setBlockPosition(pairPosition);
- session.getUpstream().sendPacket(dataPacket);
+ session.sendUpstreamPacket(dataPacket);
inventory.setHolderPosition(position);
}
@@ -105,37 +103,27 @@ public class DoubleChestInventoryTranslator extends BaseInventoryTranslator {
containerOpenPacket.setType((byte) ContainerType.CONTAINER.id());
containerOpenPacket.setBlockPosition(inventory.getHolderPosition());
containerOpenPacket.setUniqueEntityId(inventory.getHolderId());
- session.getUpstream().sendPacket(containerOpenPacket);
+ session.sendUpstreamPacket(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);
+ BlockState realBlock = session.getConnector().getWorldManager().getBlockAt(session, pos.getX(), pos.getY(), pos.getZ());
UpdateBlockPacket blockPacket = new UpdateBlockPacket();
blockPacket.setDataLayer(0);
blockPacket.setBlockPosition(holderPos);
blockPacket.setRuntimeId(BlockTranslator.getBedrockBlockId(realBlock));
- session.getUpstream().sendPacket(blockPacket);
+ session.sendUpstreamPacket(blockPacket);
holderPos = holderPos.add(Vector3i.UNIT_X);
pos = new Position(holderPos.getX(), holderPos.getY(), holderPos.getZ());
- realBlock = session.getChunkCache().getBlockAt(pos);
+ realBlock = session.getConnector().getWorldManager().getBlockAt(session, pos.getX(), pos.getY(), pos.getZ());
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);
+ session.sendUpstreamPacket(blockPacket);
}
}
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
index 9b45201e..5c6de0e8 100644
--- 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
@@ -58,7 +58,7 @@ public class FurnaceInventoryTranslator extends BlockInventoryTranslator {
return;
}
dataPacket.setValue(value);
- session.getUpstream().sendPacket(dataPacket);
+ session.sendUpstreamPacket(dataPacket);
}
@Override
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 2a5afb8c..7b4b26e6 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,15 +25,50 @@
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.data.InventoryActionData;
import lombok.AllArgsConstructor;
import org.geysermc.connector.inventory.Inventory;
import org.geysermc.connector.network.session.GeyserSession;
+import org.geysermc.connector.network.translators.inventory.updater.ContainerInventoryUpdater;
+import org.geysermc.connector.network.translators.inventory.updater.InventoryUpdater;
+import java.util.HashMap;
import java.util.List;
+import java.util.Map;
@AllArgsConstructor
public abstract class InventoryTranslator {
+
+ public static final Map INVENTORY_TRANSLATORS = new HashMap() {
+ {
+ put(null, new PlayerInventoryTranslator()); //player inventory
+ put(WindowType.GENERIC_9X1, new SingleChestInventoryTranslator(9));
+ put(WindowType.GENERIC_9X2, new SingleChestInventoryTranslator(18));
+ put(WindowType.GENERIC_9X3, new SingleChestInventoryTranslator(27));
+ put(WindowType.GENERIC_9X4, new DoubleChestInventoryTranslator(36));
+ put(WindowType.GENERIC_9X5, new DoubleChestInventoryTranslator(45));
+ put(WindowType.GENERIC_9X6, new DoubleChestInventoryTranslator(54));
+ put(WindowType.BREWING_STAND, new BrewingInventoryTranslator());
+ put(WindowType.ANVIL, new AnvilInventoryTranslator());
+ put(WindowType.CRAFTING, new CraftingInventoryTranslator());
+ put(WindowType.GRINDSTONE, new GrindstoneInventoryTranslator());
+ //put(WindowType.ENCHANTMENT, new EnchantmentInventoryTranslator()); //TODO
+
+ InventoryTranslator furnace = new FurnaceInventoryTranslator();
+ put(WindowType.FURNACE, furnace);
+ put(WindowType.BLAST_FURNACE, furnace);
+ put(WindowType.SMOKER, furnace);
+
+ InventoryUpdater containerUpdater = new ContainerInventoryUpdater();
+ put(WindowType.GENERIC_3X3, new BlockInventoryTranslator(9, "minecraft:dispenser[facing=north,triggered=false]", ContainerType.DISPENSER, containerUpdater));
+ put(WindowType.HOPPER, new BlockInventoryTranslator(5, "minecraft:hopper[enabled=false,facing=down]", ContainerType.HOPPER, containerUpdater));
+ put(WindowType.SHULKER_BOX, new BlockInventoryTranslator(27, "minecraft:shulker_box[facing=north]", ContainerType.CONTAINER, containerUpdater));
+ //put(WindowType.BEACON, new BlockInventoryTranslator(1, "minecraft:beacon", ContainerType.BEACON)); //TODO
+ }
+ };
+
public final int size;
public abstract void prepareInventory(GeyserSession session, Inventory inventory);
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
index 668612c5..28986e58 100644
--- 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
@@ -34,17 +34,17 @@ import com.nukkitx.protocol.bedrock.data.InventorySource;
import com.nukkitx.protocol.bedrock.data.ItemData;
import com.nukkitx.protocol.bedrock.packet.InventoryContentPacket;
import com.nukkitx.protocol.bedrock.packet.InventorySlotPacket;
-import it.unimi.dsi.fastutil.longs.LongArraySet;
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.network.translators.item.ItemTranslator;
import org.geysermc.connector.utils.InventoryUtils;
-import org.geysermc.connector.utils.Toolbox;
import java.util.List;
public class PlayerInventoryTranslator extends InventoryTranslator {
+ private static final ItemData UNUSUABLE_CRAFTING_SPACE_BLOCK = InventoryUtils.createUnusableSpaceBlock(
+ "The creative crafting grid is\nunavailable in Java Edition");
public PlayerInventoryTranslator() {
super(46);
@@ -59,30 +59,30 @@ public class PlayerInventoryTranslator extends InventoryTranslator {
ItemData[] contents = new ItemData[36];
// Inventory
for (int i = 9; i < 36; i++) {
- contents[i] = Translators.getItemTranslator().translateToBedrock(session, inventory.getItem(i));
+ contents[i] = ItemTranslator.translateToBedrock(session, inventory.getItem(i));
}
// Hotbar
for (int i = 36; i < 45; i++) {
- contents[i - 36] = Translators.getItemTranslator().translateToBedrock(session, inventory.getItem(i));
+ contents[i - 36] = ItemTranslator.translateToBedrock(session, inventory.getItem(i));
}
inventoryContentPacket.setContents(contents);
- session.getUpstream().sendPacket(inventoryContentPacket);
+ session.sendUpstreamPacket(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(session, inventory.getItem(i));
+ contents[i - 5] = ItemTranslator.translateToBedrock(session, inventory.getItem(i));
}
armorContentPacket.setContents(contents);
- session.getUpstream().sendPacket(armorContentPacket);
+ session.sendUpstreamPacket(armorContentPacket);
// Offhand
InventoryContentPacket offhandPacket = new InventoryContentPacket();
offhandPacket.setContainerId(ContainerId.OFFHAND);
- offhandPacket.setContents(new ItemData[]{Translators.getItemTranslator().translateToBedrock(session, inventory.getItem(45))});
- session.getUpstream().sendPacket(offhandPacket);
+ offhandPacket.setContents(new ItemData[]{ItemTranslator.translateToBedrock(session, inventory.getItem(45))});
+ session.sendUpstreamPacket(offhandPacket);
}
/**
@@ -98,12 +98,12 @@ public class PlayerInventoryTranslator extends InventoryTranslator {
slotPacket.setSlot(i + 27);
if (session.getGameMode() == GameMode.CREATIVE) {
- slotPacket.setItem(Translators.getItemTranslator().translateToBedrock(session, new ItemStack(Toolbox.BARRIER_INDEX)));
+ slotPacket.setItem(UNUSUABLE_CRAFTING_SPACE_BLOCK);
}else{
- slotPacket.setItem(Translators.getItemTranslator().translateToBedrock(session, inventory.getItem(i)));
+ slotPacket.setItem(ItemTranslator.translateToBedrock(session, inventory.getItem(i)));
}
- session.getUpstream().sendPacket(slotPacket);
+ session.sendUpstreamPacket(slotPacket);
}
}
@@ -125,13 +125,13 @@ public class PlayerInventoryTranslator extends InventoryTranslator {
slotPacket.setContainerId(ContainerId.CURSOR);
slotPacket.setSlot(slot + 27);
}
- slotPacket.setItem(Translators.getItemTranslator().translateToBedrock(session, inventory.getItem(slot)));
- session.getUpstream().sendPacket(slotPacket);
+ slotPacket.setItem(ItemTranslator.translateToBedrock(session, inventory.getItem(slot)));
+ session.sendUpstreamPacket(slotPacket);
} else if (slot == 45) {
InventoryContentPacket offhandPacket = new InventoryContentPacket();
offhandPacket.setContainerId(ContainerId.OFFHAND);
- offhandPacket.setContents(new ItemData[]{Translators.getItemTranslator().translateToBedrock(session, inventory.getItem(slot))});
- session.getUpstream().sendPacket(offhandPacket);
+ offhandPacket.setContents(new ItemData[]{ItemTranslator.translateToBedrock(session, inventory.getItem(slot))});
+ session.sendUpstreamPacket(offhandPacket);
}
}
@@ -201,23 +201,23 @@ public class PlayerInventoryTranslator extends InventoryTranslator {
if (action.getToItem().getId() == 0) {
javaItem = new ItemStack(-1, 0, null);
} else {
- javaItem = Translators.getItemTranslator().translateToJava(session, action.getToItem());
+ javaItem = ItemTranslator.translateToJava(action.getToItem());
}
ClientCreativeInventoryActionPacket creativePacket = new ClientCreativeInventoryActionPacket(javaSlot, javaItem);
- session.getDownstream().getSession().send(creativePacket);
+ session.sendDownstreamPacket(creativePacket);
inventory.setItem(javaSlot, javaItem);
break;
case ContainerId.CURSOR:
if (action.getSlot() == 0) {
- session.getInventory().setCursor(Translators.getItemTranslator().translateToJava(session, action.getToItem()));
+ session.getInventory().setCursor(ItemTranslator.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(session, action.getToItem());
+ javaItem = ItemTranslator.translateToJava(action.getToItem());
ClientCreativeInventoryActionPacket creativeDropPacket = new ClientCreativeInventoryActionPacket(-1, javaItem);
- session.getDownstream().getSession().send(creativeDropPacket);
+ session.sendDownstreamPacket(creativeDropPacket);
}
break;
}
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
index 5c99b012..3f1a58f4 100644
--- 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
@@ -25,11 +25,35 @@
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.network.translators.inventory.updater.ChestInventoryUpdater;
+import org.geysermc.connector.inventory.Inventory;
+import org.geysermc.connector.network.session.GeyserSession;
+import org.geysermc.connector.network.translators.inventory.holder.BlockInventoryHolder;
+import org.geysermc.connector.network.translators.inventory.holder.InventoryHolder;
+import org.geysermc.connector.network.translators.world.block.BlockTranslator;
+
+public class SingleChestInventoryTranslator extends ChestInventoryTranslator {
+ private final InventoryHolder holder;
-public class SingleChestInventoryTranslator extends BlockInventoryTranslator {
public SingleChestInventoryTranslator(int size) {
- super(size, "minecraft:chest[facing=north,type=single,waterlogged=false]", ContainerType.CONTAINER, new ChestInventoryUpdater(27));
+ super(size, 27);
+ BlockState javaBlockState = BlockTranslator.getJavaBlockState("minecraft:chest[facing=north,type=single,waterlogged=false]");
+ this.holder = new BlockInventoryHolder(BlockTranslator.getBedrockBlockId(javaBlockState), ContainerType.CONTAINER);
+ }
+
+ @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);
}
}
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
index cdc42f96..a9c1eddc 100644
--- 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
@@ -104,8 +104,8 @@ class ClickPlan {
break;
}
}
- session.getDownstream().getSession().send(clickPacket);
- session.getDownstream().getSession().send(new ClientConfirmTransactionPacket(inventory.getId(), actionId, true));
+ session.sendDownstreamPacket(clickPacket);
+ session.sendDownstreamPacket(new ClientConfirmTransactionPacket(inventory.getId(), actionId, true));
}
/*if (refresh) {
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
index 370d4177..209df074 100644
--- 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
@@ -38,9 +38,9 @@ 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.network.translators.item.ItemTranslator;
import org.geysermc.connector.utils.InventoryUtils;
import java.util.*;
@@ -61,13 +61,13 @@ public class InventoryActionDataTranslator {
worldAction = action;
} else if (action.getSource().getContainerId() == ContainerId.CURSOR && action.getSlot() == 0) {
cursorAction = action;
- ItemData translatedCursor = Translators.getItemTranslator().translateToBedrock(session, session.getInventory().getCursor());
+ ItemData translatedCursor = ItemTranslator.translateToBedrock(session, session.getInventory().getCursor());
if (!translatedCursor.equals(action.getFromItem())) {
refresh = true;
}
} else {
containerAction = action;
- ItemData translatedItem = Translators.getItemTranslator().translateToBedrock(session, inventory.getItem(translator.bedrockSlotToJava(action)));
+ ItemData translatedItem = ItemTranslator.translateToBedrock(session, inventory.getItem(translator.bedrockSlotToJava(action)));
if (!translatedItem.equals(action.getFromItem())) {
refresh = true;
}
@@ -94,7 +94,7 @@ public class InventoryActionDataTranslator {
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);
+ session.sendDownstreamPacket(actionPacket);
ItemStack item = session.getInventory().getItem(heldSlot);
if (item != null) {
session.getInventory().setItem(heldSlot, new ItemStack(item.getId(), item.getAmount() - 1, item.getNbt()));
@@ -110,14 +110,14 @@ public class InventoryActionDataTranslator {
inventory.getTransactionId().getAndIncrement(),
javaSlot, null, WindowAction.DROP_ITEM,
DropItemParam.DROP_SELECTED_STACK);
- session.getDownstream().getSession().send(dropPacket);
+ session.sendDownstreamPacket(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);
+ session.sendDownstreamPacket(dropPacket);
}
}
ItemStack item = session.getInventory().getItem(javaSlot);
@@ -129,7 +129,7 @@ public class InventoryActionDataTranslator {
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);
+ session.sendDownstreamPacket(dropPacket);
ItemStack cursor = session.getInventory().getCursor();
if (cursor != null) {
session.getInventory().setCursor(new ItemStack(cursor.getId(), dropAmount > 1 ? 0 : cursor.getAmount() - 1, cursor.getNbt()));
@@ -180,18 +180,19 @@ public class InventoryActionDataTranslator {
inventory.getTransactionId().getAndIncrement(),
javaSlot, InventoryUtils.REFRESH_ITEM, WindowAction.SHIFT_CLICK_ITEM,
ShiftClickItemParam.LEFT_CLICK);
- session.getDownstream().getSession().send(shiftClickPacket);
+ session.sendDownstreamPacket(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));
+ int cursorSlot = findTempSlot(inventory, session.getInventory().getCursor(), Collections.singletonList(javaSlot), false);
if (cursorSlot != -1) {
plan.add(Click.LEFT, cursorSlot);
} else {
translator.updateInventory(session, inventory);
+ InventoryUtils.updateCursor(session);
return;
}
plan.add(Click.LEFT, javaSlot);
@@ -245,11 +246,15 @@ public class InventoryActionDataTranslator {
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));
+ cursorSlot = findTempSlot(inventory,
+ session.getInventory().getCursor(),
+ Arrays.asList(fromSlot, toSlot),
+ translator.getSlotType(fromSlot) == SlotType.OUTPUT);
if (cursorSlot != -1) {
plan.add(Click.LEFT, cursorSlot);
} else {
translator.updateInventory(session, inventory);
+ InventoryUtils.updateCursor(session);
return;
}
}
@@ -266,7 +271,7 @@ public class InventoryActionDataTranslator {
inventory.getTransactionId().getAndIncrement(),
fromSlot, InventoryUtils.REFRESH_ITEM, WindowAction.SHIFT_CLICK_ITEM,
ShiftClickItemParam.LEFT_CLICK);
- session.getDownstream().getSession().send(shiftClickPacket);
+ session.sendDownstreamPacket(shiftClickPacket);
translator.updateInventory(session, inventory);
return;
} else if (translator.getSlotType(fromSlot) == SlotType.OUTPUT) {
@@ -298,7 +303,7 @@ public class InventoryActionDataTranslator {
InventoryUtils.updateCursor(session);
}
- private static int findTempSlot(Inventory inventory, ItemStack item, List slotBlacklist) {
+ private static int findTempSlot(Inventory inventory, ItemStack item, List slotBlacklist, boolean emptyOnly) {
/*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*/
@@ -314,6 +319,9 @@ public class InventoryActionDataTranslator {
ItemStack testItem = inventory.getItem(i);
boolean acceptable = true;
if (testItem != null) {
+ if (emptyOnly) {
+ continue;
+ }
for (ItemStack blacklistItem : itemBlacklist) {
if (InventoryUtils.canStack(testItem, blacklistItem)) {
acceptable = false;
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
index a3235445..67ce2ce1 100644
--- 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
@@ -36,7 +36,7 @@ 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.world.block.BlockTranslator;
import org.geysermc.connector.network.translators.inventory.InventoryTranslator;
import org.geysermc.connector.utils.LocaleUtils;
@@ -54,7 +54,7 @@ public class BlockInventoryHolder extends InventoryHolder {
blockPacket.setBlockPosition(position);
blockPacket.setRuntimeId(blockId);
blockPacket.getFlags().addAll(UpdateBlockPacket.FLAG_ALL_PRIORITY);
- session.getUpstream().sendPacket(blockPacket);
+ session.sendUpstreamPacket(blockPacket);
inventory.setHolderPosition(position);
CompoundTag tag = CompoundTag.builder()
@@ -65,7 +65,7 @@ public class BlockInventoryHolder extends InventoryHolder {
BlockEntityDataPacket dataPacket = new BlockEntityDataPacket();
dataPacket.setData(tag);
dataPacket.setBlockPosition(position);
- session.getUpstream().sendPacket(dataPacket);
+ session.sendUpstreamPacket(dataPacket);
}
@Override
@@ -75,18 +75,18 @@ public class BlockInventoryHolder extends InventoryHolder {
containerOpenPacket.setType((byte) containerType.id());
containerOpenPacket.setBlockPosition(inventory.getHolderPosition());
containerOpenPacket.setUniqueEntityId(inventory.getHolderId());
- session.getUpstream().sendPacket(containerOpenPacket);
+ session.sendUpstreamPacket(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);
+ BlockState realBlock = session.getConnector().getWorldManager().getBlockAt(session, pos.getX(), pos.getY(), pos.getZ());
UpdateBlockPacket blockPacket = new UpdateBlockPacket();
blockPacket.setDataLayer(0);
blockPacket.setBlockPosition(holderPos);
blockPacket.setRuntimeId(BlockTranslator.getBedrockBlockId(realBlock));
- session.getUpstream().sendPacket(blockPacket);
+ session.sendUpstreamPacket(blockPacket);
}
}
diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/inventory/updater/ChestInventoryUpdater.java b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/updater/ChestInventoryUpdater.java
index e8e0fc45..6ec8d481 100644
--- a/connector/src/main/java/org/geysermc/connector/network/translators/inventory/updater/ChestInventoryUpdater.java
+++ b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/updater/ChestInventoryUpdater.java
@@ -31,11 +31,15 @@ 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;
+import org.geysermc.connector.network.translators.item.ItemTranslator;
+import org.geysermc.connector.utils.InventoryUtils;
@AllArgsConstructor
public class ChestInventoryUpdater extends InventoryUpdater {
+ private static final ItemData UNUSUABLE_SPACE_BLOCK = InventoryUtils.createUnusableSpaceBlock(
+ "This slot does not exist in the inventory\non Java Edition, as there is less\nrows than possible in Bedrock");
+
private final int paddedSize;
@Override
@@ -44,17 +48,17 @@ public class ChestInventoryUpdater extends InventoryUpdater {
ItemData[] bedrockItems = new ItemData[paddedSize];
for (int i = 0; i < bedrockItems.length; i++) {
- if (i <= translator.size) {
- bedrockItems[i] = Translators.getItemTranslator().translateToBedrock(session, inventory.getItem(i));
+ if (i < translator.size) {
+ bedrockItems[i] = ItemTranslator.translateToBedrock(session, inventory.getItem(i));
} else {
- bedrockItems[i] = ItemData.AIR;
+ bedrockItems[i] = UNUSUABLE_SPACE_BLOCK;
}
}
InventoryContentPacket contentPacket = new InventoryContentPacket();
contentPacket.setContainerId(inventory.getId());
contentPacket.setContents(bedrockItems);
- session.getUpstream().sendPacket(contentPacket);
+ session.sendUpstreamPacket(contentPacket);
}
@Override
@@ -65,8 +69,8 @@ public class ChestInventoryUpdater extends InventoryUpdater {
InventorySlotPacket slotPacket = new InventorySlotPacket();
slotPacket.setContainerId(inventory.getId());
slotPacket.setSlot(translator.javaSlotToBedrock(javaSlot));
- slotPacket.setItem(Translators.getItemTranslator().translateToBedrock(session, inventory.getItem(javaSlot)));
- session.getUpstream().sendPacket(slotPacket);
+ slotPacket.setItem(ItemTranslator.translateToBedrock(session, inventory.getItem(javaSlot)));
+ session.sendUpstreamPacket(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
index d187ffd9..ec6175c3 100644
--- 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
@@ -30,8 +30,8 @@ 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;
+import org.geysermc.connector.network.translators.item.ItemTranslator;
public class ContainerInventoryUpdater extends InventoryUpdater {
@Override
@@ -40,13 +40,13 @@ public class ContainerInventoryUpdater extends InventoryUpdater {
ItemData[] bedrockItems = new ItemData[translator.size];
for (int i = 0; i < bedrockItems.length; i++) {
- bedrockItems[translator.javaSlotToBedrock(i)] = Translators.getItemTranslator().translateToBedrock(session, inventory.getItem(i));
+ bedrockItems[translator.javaSlotToBedrock(i)] = ItemTranslator.translateToBedrock(session, inventory.getItem(i));
}
InventoryContentPacket contentPacket = new InventoryContentPacket();
contentPacket.setContainerId(inventory.getId());
contentPacket.setContents(bedrockItems);
- session.getUpstream().sendPacket(contentPacket);
+ session.sendUpstreamPacket(contentPacket);
}
@Override
@@ -57,8 +57,8 @@ public class ContainerInventoryUpdater extends InventoryUpdater {
InventorySlotPacket slotPacket = new InventorySlotPacket();
slotPacket.setContainerId(inventory.getId());
slotPacket.setSlot(translator.javaSlotToBedrock(javaSlot));
- slotPacket.setItem(Translators.getItemTranslator().translateToBedrock(session, inventory.getItem(javaSlot)));
- session.getUpstream().sendPacket(slotPacket);
+ slotPacket.setItem(ItemTranslator.translateToBedrock(session, inventory.getItem(javaSlot)));
+ session.sendUpstreamPacket(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
index e3dc1864..adbbdbac 100644
--- 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
@@ -29,8 +29,8 @@ 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;
+import org.geysermc.connector.network.translators.item.ItemTranslator;
public class CursorInventoryUpdater extends InventoryUpdater {
@Override
@@ -44,8 +44,8 @@ public class CursorInventoryUpdater extends InventoryUpdater {
InventorySlotPacket slotPacket = new InventorySlotPacket();
slotPacket.setContainerId(ContainerId.CURSOR);
slotPacket.setSlot(bedrockSlot);
- slotPacket.setItem(Translators.getItemTranslator().translateToBedrock(session, inventory.getItem(i)));
- session.getUpstream().sendPacket(slotPacket);
+ slotPacket.setItem(ItemTranslator.translateToBedrock(session, inventory.getItem(i)));
+ session.sendUpstreamPacket(slotPacket);
}
}
@@ -57,8 +57,8 @@ public class CursorInventoryUpdater extends InventoryUpdater {
InventorySlotPacket slotPacket = new InventorySlotPacket();
slotPacket.setContainerId(ContainerId.CURSOR);
slotPacket.setSlot(translator.javaSlotToBedrock(javaSlot));
- slotPacket.setItem(Translators.getItemTranslator().translateToBedrock(session, inventory.getItem(javaSlot)));
- session.getUpstream().sendPacket(slotPacket);
+ slotPacket.setItem(ItemTranslator.translateToBedrock(session, inventory.getItem(javaSlot)));
+ session.sendUpstreamPacket(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
index 2f139e27..88157df0 100644
--- 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
@@ -31,20 +31,20 @@ 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;
+import org.geysermc.connector.network.translators.item.ItemTranslator;
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(session, inventory.getItem(translator.size + i + offset));
+ bedrockItems[i] = ItemTranslator.translateToBedrock(session, inventory.getItem(translator.size + i + offset));
}
InventoryContentPacket contentPacket = new InventoryContentPacket();
contentPacket.setContainerId(ContainerId.INVENTORY);
contentPacket.setContents(bedrockItems);
- session.getUpstream().sendPacket(contentPacket);
+ session.sendUpstreamPacket(contentPacket);
}
public boolean updateSlot(InventoryTranslator translator, GeyserSession session, Inventory inventory, int javaSlot) {
@@ -52,8 +52,8 @@ public abstract class InventoryUpdater {
InventorySlotPacket slotPacket = new InventorySlotPacket();
slotPacket.setContainerId(ContainerId.INVENTORY);
slotPacket.setSlot(translator.javaSlotToBedrock(javaSlot));
- slotPacket.setItem(Translators.getItemTranslator().translateToBedrock(session, inventory.getItem(javaSlot)));
- session.getUpstream().sendPacket(slotPacket);
+ slotPacket.setItem(ItemTranslator.translateToBedrock(session, inventory.getItem(javaSlot)));
+ session.sendUpstreamPacket(slotPacket);
return true;
}
return false;
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 e579c20e..9c072ad1 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
@@ -32,14 +32,15 @@ import lombok.Getter;
@AllArgsConstructor
public class ItemEntry {
- public static ItemEntry AIR = new ItemEntry("minecraft:air", 0, 0, 0);
+ public static ItemEntry AIR = new ItemEntry("minecraft:air", 0, 0, 0, false);
private final String javaIdentifier;
private final int javaId;
-
private final int bedrockId;
private final int bedrockData;
+ private final boolean block;
+
@Override
public boolean equals(Object obj) {
return obj == this || (obj instanceof ItemEntry && ((ItemEntry) obj).getBedrockId() == this.getBedrockId() && ((ItemEntry) obj).getJavaIdentifier().equals(this.getJavaIdentifier()));
diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/item/ItemRegistry.java b/connector/src/main/java/org/geysermc/connector/network/translators/item/ItemRegistry.java
new file mode 100644
index 00000000..ed99ece3
--- /dev/null
+++ b/connector/src/main/java/org/geysermc/connector/network/translators/item/ItemRegistry.java
@@ -0,0 +1,213 @@
+/*
+ * 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 com.fasterxml.jackson.core.type.TypeReference;
+import com.fasterxml.jackson.databind.JsonNode;
+import com.github.steveice10.mc.protocol.data.game.entity.metadata.ItemStack;
+import com.nukkitx.nbt.NbtUtils;
+import com.nukkitx.protocol.bedrock.data.ItemData;
+import com.nukkitx.protocol.bedrock.packet.StartGamePacket;
+import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
+import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
+import org.geysermc.connector.GeyserConnector;
+import org.geysermc.connector.utils.FileUtils;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.Base64;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Registry for anything item related.
+ */
+public class ItemRegistry {
+
+ private static final Map JAVA_IDENTIFIER_MAP = new HashMap<>();
+
+ public static final ItemData[] CREATIVE_ITEMS;
+
+ public static final List ITEMS = new ArrayList<>();
+ public static final Int2ObjectMap ITEM_ENTRIES = new Int2ObjectOpenHashMap<>();
+
+ // Shield ID, used in Entity.java
+ public static final int SHIELD = 829;
+ // Boat ID, used in BedrockInventoryTransactionTranslator.java
+ public static final int BOAT = 333;
+
+ public static int BARRIER_INDEX = 0;
+
+ public static void init() {
+ // no-op
+ }
+
+ static {
+ /* Load item palette */
+ InputStream stream = FileUtils.getResource("bedrock/items.json");
+
+ TypeReference> itemEntriesType = new TypeReference>() {
+ };
+
+ List itemEntries;
+ try {
+ itemEntries = GeyserConnector.JSON_MAPPER.readValue(stream, itemEntriesType);
+ } catch (Exception e) {
+ throw new AssertionError("Unable to load Bedrock runtime item IDs", e);
+ }
+
+ for (JsonNode entry : itemEntries) {
+ ITEMS.add(new StartGamePacket.ItemEntry(entry.get("name").textValue(), (short) entry.get("id").intValue()));
+ }
+
+ stream = FileUtils.getResource("mappings/items.json");
+
+ JsonNode items;
+ try {
+ items = GeyserConnector.JSON_MAPPER.readTree(stream);
+ } catch (Exception e) {
+ throw new AssertionError("Unable to load Java runtime item IDs", e);
+ }
+
+ int itemIndex = 0;
+ Iterator> iterator = items.fields();
+ while (iterator.hasNext()) {
+ Map.Entry entry = iterator.next();
+ 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(),
+ entry.getValue().get("is_block").booleanValue()));
+ } 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(),
+ "",
+ entry.getValue().get("is_block").booleanValue()));
+ }
+ } else {
+ ITEM_ENTRIES.put(itemIndex, new ItemEntry(
+ entry.getKey(), itemIndex,
+ entry.getValue().get("bedrock_id").intValue(),
+ entry.getValue().get("bedrock_data").intValue(),
+ entry.getValue().get("is_block").booleanValue()));
+ }
+ if (entry.getKey().equals("minecraft:barrier")) {
+ BARRIER_INDEX = itemIndex;
+ }
+
+ itemIndex++;
+ }
+
+ /* Load creative items */
+ stream = FileUtils.getResource("bedrock/creative_items.json");
+
+ JsonNode creativeItemEntries;
+ try {
+ creativeItemEntries = GeyserConnector.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]);
+ }
+
+ /**
+ * Gets an {@link ItemEntry} from the given {@link ItemStack}.
+ *
+ * @param stack the item stack
+ * @return an item entry from the given item stack
+ */
+ public static ItemEntry getItem(ItemStack stack) {
+ return ITEM_ENTRIES.get(stack.getId());
+ }
+
+ /**
+ * Gets an {@link ItemEntry} from the given {@link ItemData}.
+ *
+ * @param data the item data
+ * @return an item entry from the given item data
+ */
+ public static ItemEntry getItem(ItemData data) {
+ for (ItemEntry itemEntry : ITEM_ENTRIES.values()) {
+ 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 : ITEM_ENTRIES.values()) {
+ if (itemEntry.getBedrockId() == data.getId()) {
+ return itemEntry;
+ }
+ }
+
+ GeyserConnector.getInstance().getLogger().debug("Missing mapping for bedrock item " + data.getId() + ":" + data.getDamage());
+ return ItemEntry.AIR;
+ }
+
+ /**
+ * Gets an {@link ItemEntry} from the given Minecraft: Java Edition
+ * block state identifier.
+ *
+ * @param javaIdentifier the block state identifier
+ * @return an item entry from the given java edition identifier
+ */
+ public static ItemEntry getItemEntry(String javaIdentifier) {
+ return JAVA_IDENTIFIER_MAP.computeIfAbsent(javaIdentifier, key -> ITEM_ENTRIES.values()
+ .stream().filter(itemEntry -> itemEntry.getJavaIdentifier().equals(key)).findFirst().orElse(null));
+ }
+}
diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/item/ItemTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/item/ItemTranslator.java
index 5e0361c0..273226fe 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
@@ -1,55 +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:
+ * 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 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.
+ * 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
*
- * @author GeyserMC
- * @link https://github.com/GeyserMC/Geyser
*/
package org.geysermc.connector.network.translators.item;
import com.github.steveice10.mc.protocol.data.game.entity.metadata.ItemStack;
+import com.github.steveice10.mc.protocol.data.message.Message;
+import com.nukkitx.nbt.CompoundTagBuilder;
+import com.github.steveice10.opennbt.tag.builtin.*;
+import com.nukkitx.nbt.tag.CompoundTag;
+import com.nukkitx.nbt.tag.Tag;
import com.nukkitx.protocol.bedrock.data.ItemData;
-
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
import org.geysermc.connector.GeyserConnector;
-
import org.geysermc.connector.network.session.GeyserSession;
-import org.geysermc.connector.network.translators.*;
-import org.geysermc.connector.utils.Toolbox;
+import org.geysermc.connector.network.translators.ItemRemapper;
+import org.geysermc.connector.utils.MessageUtils;
import org.reflections.Reflections;
-import java.util.*;
+import java.util.ArrayList;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
import java.util.stream.Collectors;
-public class ItemTranslator {
+public abstract class ItemTranslator {
- private Int2ObjectMap itemTranslators = new Int2ObjectOpenHashMap();
- private List nbtItemTranslators;
- private Map javaIdentifierMap = new HashMap<>();
-
- // Shield ID, used in Entity.java
- public static final int SHIELD = 829;
+ private static final Int2ObjectMap ITEM_STACK_TRANSLATORS = new Int2ObjectOpenHashMap<>();
+ private static final List NBT_TRANSLATORS;
- public void init() {
+ protected ItemTranslator() {
+ }
+
+ public static void init() {
+ // no-op
+ }
+
+ static {
+ /* Load item translators */
Reflections ref = new Reflections("org.geysermc.connector.network.translators.item");
Map loadedNbtItemTranslators = new HashMap<>();
@@ -64,35 +76,33 @@ public class ItemTranslator {
loadedNbtItemTranslators.put(nbtItemTranslator, priority);
continue;
}
- ItemStackTranslator itemStackTranslator = (ItemStackTranslator) clazz.newInstance();
+ ItemTranslator itemStackTranslator = (ItemTranslator) clazz.newInstance();
List appliedItems = itemStackTranslator.getAppliedItems();
for (ItemEntry item : appliedItems) {
- ItemStackTranslator registered = itemTranslators.get(item.getJavaId());
+ ItemTranslator registered = ITEM_STACK_TRANSLATORS.get(item.getJavaId());
if (registered != null) {
GeyserConnector.getInstance().getLogger().error("Could not instantiate annotated item translator " + clazz.getCanonicalName() + "." +
" Item translator " + registered.getClass().getCanonicalName() + " is already registered for the item " + item.getJavaIdentifier());
continue;
}
- itemTranslators.put(item.getJavaId(), itemStackTranslator);
+ ITEM_STACK_TRANSLATORS.put(item.getJavaId(), itemStackTranslator);
}
} catch (InstantiationException | IllegalAccessException e) {
GeyserConnector.getInstance().getLogger().error("Could not instantiate annotated item translator " + clazz.getCanonicalName() + ".");
}
}
- nbtItemTranslators = loadedNbtItemTranslators.keySet().stream()
- .sorted(Comparator.comparingInt(value -> loadedNbtItemTranslators.get(value))).collect(Collectors.toList());
+ NBT_TRANSLATORS = loadedNbtItemTranslators.keySet().stream().sorted(Comparator.comparingInt(loadedNbtItemTranslators::get)).collect(Collectors.toList());
}
- public ItemStack translateToJava(GeyserSession session, ItemData data) {
+ public static ItemStack translateToJava(ItemData data) {
if (data == null) {
return new ItemStack(0);
}
- ItemEntry javaItem = getItem(data);
+ ItemEntry javaItem = ItemRegistry.getItem(data);
ItemStack itemStack;
-
- ItemStackTranslator itemStackTranslator = itemTranslators.get(javaItem.getJavaId());
+ ItemTranslator itemStackTranslator = ITEM_STACK_TRANSLATORS.get(javaItem.getJavaId());
if (itemStackTranslator != null) {
itemStack = itemStackTranslator.translateToJava(data, javaItem);
} else {
@@ -100,7 +110,7 @@ public class ItemTranslator {
}
if (itemStack != null && itemStack.getNbt() != null) {
- for (NbtItemStackTranslator translator : nbtItemTranslators) {
+ for (NbtItemStackTranslator translator : NBT_TRANSLATORS) {
if (translator.acceptItem(javaItem)) {
translator.translateToJava(itemStack.getNbt(), javaItem);
}
@@ -109,60 +119,271 @@ public class ItemTranslator {
return itemStack;
}
- public ItemData translateToBedrock(GeyserSession session, ItemStack stack) {
+ public static ItemData translateToBedrock(GeyserSession session, ItemStack stack) {
if (stack == null) {
return ItemData.AIR;
}
- ItemEntry bedrockItem = getItem(stack);
+ ItemEntry bedrockItem = ItemRegistry.getItem(stack);
- if (stack != null && stack.getNbt() != null) {
- for (NbtItemStackTranslator translator : nbtItemTranslators) {
+ ItemStack itemStack = new ItemStack(stack.getId(), stack.getAmount(), stack.getNbt() != null ? stack.getNbt().clone() : null);
+
+ if (itemStack.getNbt() != null) {
+ for (NbtItemStackTranslator translator : NBT_TRANSLATORS) {
if (translator.acceptItem(bedrockItem)) {
- translator.translateToBedrock(stack.getNbt(), bedrockItem);
+ translator.translateToBedrock(itemStack.getNbt(), bedrockItem);
}
}
}
- ItemStackTranslator itemStackTranslator = itemTranslators.get(bedrockItem.getJavaId());
+ ItemData itemData;
+ ItemTranslator itemStackTranslator = ITEM_STACK_TRANSLATORS.get(bedrockItem.getJavaId());
if (itemStackTranslator != null) {
- return itemStackTranslator.translateToBedrock(stack, bedrockItem);
+ itemData = itemStackTranslator.translateToBedrock(itemStack, bedrockItem);
} else {
- return DEFAULT_TRANSLATOR.translateToBedrock(stack, bedrockItem);
+ itemData = DEFAULT_TRANSLATOR.translateToBedrock(itemStack, bedrockItem);
}
- }
- public ItemEntry getItem(ItemStack stack) {
- return Toolbox.ITEM_ENTRIES.get(stack.getId());
- }
- public ItemEntry getItem(ItemData data) {
- for (ItemEntry itemEntry : Toolbox.ITEM_ENTRIES.values()) {
- 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;
+ // Get the display name of the item
+ CompoundTag tag = itemData.getTag();
+ if (tag != null) {
+ CompoundTag display = tag.getCompound("display");
+ if (display != null) {
+ String name = display.getString("Name");
+
+ // Check if its a message to translate
+ if (MessageUtils.isMessage(name)) {
+ // Get the translated name
+ name = MessageUtils.getTranslatedBedrockMessage(Message.fromString(name), session.getClientData().getLanguageCode());
+
+ // Build the new display tag
+ CompoundTagBuilder displayBuilder = display.toBuilder();
+ displayBuilder.stringTag("Name", name);
+
+ // Build the new root tag
+ CompoundTagBuilder builder = tag.toBuilder();
+ builder.tag(displayBuilder.build("display"));
+
+ // Create a new item with the original data + updated name
+ itemData = ItemData.of(itemData.getId(), itemData.getDamage(), itemData.getCount(), builder.buildRootTag());
+ }
}
}
- GeyserConnector.getInstance().getLogger().debug("Missing mapping for bedrock item " + data.getId() + ":" + data.getDamage());
- return ItemEntry.AIR;
+ return itemData;
}
- 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 static final ItemStackTranslator DEFAULT_TRANSLATOR = new ItemStackTranslator() {
+ private static final ItemTranslator DEFAULT_TRANSLATOR = new ItemTranslator() {
@Override
public List getAppliedItems() {
return null;
}
};
+
+ public ItemData translateToBedrock(ItemStack itemStack, ItemEntry itemEntry) {
+ if (itemStack == null) {
+ return ItemData.AIR;
+ }
+ if (itemStack.getNbt() == null) {
+ return ItemData.of(itemEntry.getBedrockId(), (short) itemEntry.getBedrockData(), itemStack.getAmount());
+ }
+ return ItemData.of(itemEntry.getBedrockId(), (short) itemEntry.getBedrockData(), itemStack.getAmount(), this.translateNbtToBedrock(itemStack.getNbt()));
+ }
+
+ public ItemStack translateToJava(ItemData itemData, ItemEntry itemEntry) {
+ if (itemData == null) return null;
+ if (itemData.getTag() == null) {
+ return new ItemStack(itemEntry.getJavaId(), itemData.getCount(), new com.github.steveice10.opennbt.tag.builtin.CompoundTag(""));
+ }
+ return new ItemStack(itemEntry.getJavaId(), itemData.getCount(), this.translateToJavaNBT(itemData.getTag()));
+ }
+
+ public abstract List getAppliedItems();
+
+ public CompoundTag translateNbtToBedrock(com.github.steveice10.opennbt.tag.builtin.CompoundTag tag) {
+ Map> javaValue = new HashMap<>();
+ if (tag.getValue() != null && !tag.getValue().isEmpty()) {
+ for (String str : tag.getValue().keySet()) {
+ com.github.steveice10.opennbt.tag.builtin.Tag javaTag = tag.get(str);
+ com.nukkitx.nbt.tag.Tag> translatedTag = translateToBedrockNBT(javaTag);
+ if (translatedTag == null)
+ continue;
+
+ javaValue.put(translatedTag.getName(), translatedTag);
+ }
+ }
+
+ return new CompoundTag(tag.getName(), javaValue);
+ }
+
+ private Tag> translateToBedrockNBT(com.github.steveice10.opennbt.tag.builtin.Tag tag) {
+ if (tag instanceof ByteArrayTag) {
+ ByteArrayTag byteArrayTag = (ByteArrayTag) tag;
+ return new com.nukkitx.nbt.tag.ByteArrayTag(byteArrayTag.getName(), byteArrayTag.getValue());
+ }
+
+ if (tag instanceof ByteTag) {
+ ByteTag byteTag = (ByteTag) tag;
+ return new com.nukkitx.nbt.tag.ByteTag(byteTag.getName(), byteTag.getValue());
+ }
+
+ if (tag instanceof DoubleTag) {
+ DoubleTag doubleTag = (DoubleTag) tag;
+ return new com.nukkitx.nbt.tag.DoubleTag(doubleTag.getName(), doubleTag.getValue());
+ }
+
+ if (tag instanceof FloatTag) {
+ FloatTag floatTag = (FloatTag) tag;
+ return new com.nukkitx.nbt.tag.FloatTag(floatTag.getName(), floatTag.getValue());
+ }
+
+ if (tag instanceof IntArrayTag) {
+ IntArrayTag intArrayTag = (IntArrayTag) tag;
+ return new com.nukkitx.nbt.tag.IntArrayTag(intArrayTag.getName(), intArrayTag.getValue());
+ }
+
+ if (tag instanceof IntTag) {
+ IntTag intTag = (IntTag) tag;
+ return new com.nukkitx.nbt.tag.IntTag(intTag.getName(), intTag.getValue());
+ }
+
+ if (tag instanceof LongArrayTag) {
+ LongArrayTag longArrayTag = (LongArrayTag) tag;
+ return new com.nukkitx.nbt.tag.LongArrayTag(longArrayTag.getName(), longArrayTag.getValue());
+ }
+
+ if (tag instanceof LongTag) {
+ LongTag longTag = (LongTag) tag;
+ return new com.nukkitx.nbt.tag.LongTag(longTag.getName(), longTag.getValue());
+ }
+
+ if (tag instanceof ShortTag) {
+ ShortTag shortTag = (ShortTag) tag;
+ return new com.nukkitx.nbt.tag.ShortTag(shortTag.getName(), shortTag.getValue());
+ }
+
+ if (tag instanceof StringTag) {
+ StringTag stringTag = (StringTag) tag;
+ return new com.nukkitx.nbt.tag.StringTag(stringTag.getName(), stringTag.getValue());
+ }
+
+ if (tag instanceof ListTag) {
+ ListTag listTag = (ListTag) tag;
+
+ List> tagList = new ArrayList<>();
+ for (com.github.steveice10.opennbt.tag.builtin.Tag value : listTag) {
+ tagList.add(translateToBedrockNBT(value));
+ }
+ Class> clazz = CompoundTag.class;
+ if (!tagList.isEmpty()) {
+ clazz = tagList.get(0).getClass();
+ }
+ return new com.nukkitx.nbt.tag.ListTag(listTag.getName(), clazz, tagList);
+ }
+
+ if (tag instanceof com.github.steveice10.opennbt.tag.builtin.CompoundTag) {
+ com.github.steveice10.opennbt.tag.builtin.CompoundTag compoundTag = (com.github.steveice10.opennbt.tag.builtin.CompoundTag) tag;
+
+ return translateNbtToBedrock(compoundTag);
+ }
+
+ return null;
+ }
+
+ public com.github.steveice10.opennbt.tag.builtin.CompoundTag translateToJavaNBT(com.nukkitx.nbt.tag.CompoundTag tag) {
+ com.github.steveice10.opennbt.tag.builtin.CompoundTag javaTag = new com.github.steveice10.opennbt.tag.builtin.CompoundTag(tag.getName());
+ Map javaValue = javaTag.getValue();
+ if (tag.getValue() != null && !tag.getValue().isEmpty()) {
+ for (String str : tag.getValue().keySet()) {
+ Tag> bedrockTag = tag.get(str);
+ com.github.steveice10.opennbt.tag.builtin.Tag translatedTag = translateToJavaNBT(bedrockTag);
+ if (translatedTag == null)
+ continue;
+
+ javaValue.put(translatedTag.getName(), translatedTag);
+ }
+ }
+
+ javaTag.setValue(javaValue);
+ return javaTag;
+ }
+
+ private com.github.steveice10.opennbt.tag.builtin.Tag translateToJavaNBT(com.nukkitx.nbt.tag.Tag> tag) {
+ if (tag instanceof com.nukkitx.nbt.tag.ByteArrayTag) {
+ com.nukkitx.nbt.tag.ByteArrayTag byteArrayTag = (com.nukkitx.nbt.tag.ByteArrayTag) tag;
+ return new ByteArrayTag(byteArrayTag.getName(), byteArrayTag.getValue());
+ }
+
+ if (tag instanceof com.nukkitx.nbt.tag.ByteTag) {
+ com.nukkitx.nbt.tag.ByteTag byteTag = (com.nukkitx.nbt.tag.ByteTag) tag;
+ return new ByteTag(byteTag.getName(), byteTag.getValue());
+ }
+
+ if (tag instanceof com.nukkitx.nbt.tag.DoubleTag) {
+ com.nukkitx.nbt.tag.DoubleTag doubleTag = (com.nukkitx.nbt.tag.DoubleTag) tag;
+ return new DoubleTag(doubleTag.getName(), doubleTag.getValue());
+ }
+
+ if (tag instanceof com.nukkitx.nbt.tag.FloatTag) {
+ com.nukkitx.nbt.tag.FloatTag floatTag = (com.nukkitx.nbt.tag.FloatTag) tag;
+ return new FloatTag(floatTag.getName(), floatTag.getValue());
+ }
+
+ if (tag instanceof com.nukkitx.nbt.tag.IntArrayTag) {
+ com.nukkitx.nbt.tag.IntArrayTag intArrayTag = (com.nukkitx.nbt.tag.IntArrayTag) tag;
+ return new IntArrayTag(intArrayTag.getName(), intArrayTag.getValue());
+ }
+
+ if (tag instanceof com.nukkitx.nbt.tag.IntTag) {
+ com.nukkitx.nbt.tag.IntTag intTag = (com.nukkitx.nbt.tag.IntTag) tag;
+ return new IntTag(intTag.getName(), intTag.getValue());
+ }
+
+ if (tag instanceof com.nukkitx.nbt.tag.LongArrayTag) {
+ com.nukkitx.nbt.tag.LongArrayTag longArrayTag = (com.nukkitx.nbt.tag.LongArrayTag) tag;
+ return new LongArrayTag(longArrayTag.getName(), longArrayTag.getValue());
+ }
+
+ if (tag instanceof com.nukkitx.nbt.tag.LongTag) {
+ com.nukkitx.nbt.tag.LongTag longTag = (com.nukkitx.nbt.tag.LongTag) tag;
+ return new LongTag(longTag.getName(), longTag.getValue());
+ }
+
+ if (tag instanceof com.nukkitx.nbt.tag.ShortTag) {
+ com.nukkitx.nbt.tag.ShortTag shortTag = (com.nukkitx.nbt.tag.ShortTag) tag;
+ return new ShortTag(shortTag.getName(), shortTag.getValue());
+ }
+
+ if (tag instanceof com.nukkitx.nbt.tag.StringTag) {
+ com.nukkitx.nbt.tag.StringTag stringTag = (com.nukkitx.nbt.tag.StringTag) tag;
+ return new StringTag(stringTag.getName(), stringTag.getValue());
+ }
+
+ if (tag instanceof com.nukkitx.nbt.tag.ListTag) {
+ com.nukkitx.nbt.tag.ListTag> listTag = (com.nukkitx.nbt.tag.ListTag>) tag;
+
+ List tags = new ArrayList<>();
+
+ for (Object value : listTag.getValue()) {
+ if (!(value instanceof com.nukkitx.nbt.tag.Tag))
+ continue;
+
+ com.nukkitx.nbt.tag.Tag> tagValue = (com.nukkitx.nbt.tag.Tag>) value;
+ com.github.steveice10.opennbt.tag.builtin.Tag javaTag = translateToJavaNBT(tagValue);
+ if (javaTag != null)
+ tags.add(javaTag);
+ }
+ return new ListTag(listTag.getName(), tags);
+ }
+
+ if (tag instanceof com.nukkitx.nbt.tag.CompoundTag) {
+ com.nukkitx.nbt.tag.CompoundTag compoundTag = (com.nukkitx.nbt.tag.CompoundTag) tag;
+ return translateToJavaNBT(compoundTag);
+ }
+
+ return null;
+ }
+
+
}
diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/item/NbtItemStackTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/item/NbtItemStackTranslator.java
new file mode 100644
index 00000000..d8fcbc4a
--- /dev/null
+++ b/connector/src/main/java/org/geysermc/connector/network/translators/item/NbtItemStackTranslator.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.item;
+
+import com.github.steveice10.opennbt.tag.builtin.CompoundTag;
+
+public class NbtItemStackTranslator {
+
+ public void translateToBedrock(CompoundTag itemTag, ItemEntry itemEntry) {
+
+ }
+
+ public void translateToJava(CompoundTag itemTag, ItemEntry itemEntry) {
+
+ }
+
+ public boolean acceptItem(ItemEntry itemEntry) {
+ return true;
+ }
+
+}
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
index 5d1ddd26..cfc05a4a 100644
--- 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
@@ -7,8 +7,8 @@ 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);
+ public ToolItemEntry(String javaIdentifier, int javaId, int bedrockId, int bedrockData, String toolType, String toolTier, boolean isBlock) {
+ super(javaIdentifier, javaId, bedrockId, bedrockData, isBlock);
this.toolType = toolType;
this.toolTier = toolTier;
}
diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/item/translators/BannerTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/item/translators/BannerTranslator.java
new file mode 100644
index 00000000..f4f545ff
--- /dev/null
+++ b/connector/src/main/java/org/geysermc/connector/network/translators/item/translators/BannerTranslator.java
@@ -0,0 +1,167 @@
+/*
+ * 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.translators;
+
+import com.github.steveice10.mc.protocol.data.game.entity.metadata.ItemStack;
+import com.github.steveice10.opennbt.tag.builtin.CompoundTag;
+import com.github.steveice10.opennbt.tag.builtin.IntTag;
+import com.github.steveice10.opennbt.tag.builtin.ListTag;
+import com.github.steveice10.opennbt.tag.builtin.StringTag;
+import com.github.steveice10.opennbt.tag.builtin.Tag;
+import com.nukkitx.nbt.CompoundTagBuilder;
+import com.nukkitx.protocol.bedrock.data.ItemData;
+import org.geysermc.connector.network.translators.ItemRemapper;
+import org.geysermc.connector.network.translators.item.ItemRegistry;
+import org.geysermc.connector.network.translators.item.ItemTranslator;
+import org.geysermc.connector.network.translators.item.ItemEntry;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+
+@ItemRemapper
+public class BannerTranslator extends ItemTranslator {
+
+ private List appliedItems;
+
+ public BannerTranslator() {
+ appliedItems = ItemRegistry.ITEM_ENTRIES.values().stream().filter(entry -> entry.getJavaIdentifier().endsWith("banner")).collect(Collectors.toList());
+ }
+
+ @Override
+ public ItemData translateToBedrock(ItemStack itemStack, ItemEntry itemEntry) {
+ if (itemStack.getNbt() == null) return super.translateToBedrock(itemStack, itemEntry);
+
+ ItemData itemData = super.translateToBedrock(itemStack, itemEntry);
+
+ CompoundTag blockEntityTag = itemStack.getNbt().get("BlockEntityTag");
+ if (blockEntityTag.contains("Patterns")) {
+ ListTag patterns = blockEntityTag.get("Patterns");
+
+ CompoundTagBuilder builder = itemData.getTag().toBuilder();
+ builder.tag(convertBannerPattern(patterns));
+
+ itemData = ItemData.of(itemData.getId(), itemData.getDamage(), itemData.getCount(), builder.buildRootTag());
+ }
+
+ return itemData;
+ }
+
+ @Override
+ public ItemStack translateToJava(ItemData itemData, ItemEntry itemEntry) {
+ if (itemData.getTag() == null) return super.translateToJava(itemData, itemEntry);
+
+ ItemStack itemStack = super.translateToJava(itemData, itemEntry);
+
+ com.nukkitx.nbt.tag.CompoundTag nbtTag = itemData.getTag();
+ if (nbtTag.contains("Patterns")) {
+ com.nukkitx.nbt.tag.ListTag> patterns = nbtTag.get("Patterns");
+
+ CompoundTag blockEntityTag = new CompoundTag("BlockEntityTag");
+ blockEntityTag.put(convertBannerPattern(patterns));
+
+ itemStack.getNbt().put(blockEntityTag);
+ }
+
+ return itemStack;
+ }
+
+ @Override
+ public List getAppliedItems() {
+ return appliedItems;
+ }
+
+ /**
+ * Convert a list of patterns from Java nbt to Bedrock nbt
+ *
+ * @param patterns The patterns to convert
+ * @return The new converted patterns
+ */
+ public static com.nukkitx.nbt.tag.ListTag convertBannerPattern(ListTag patterns) {
+ List tagsList = new ArrayList<>();
+ for (com.github.steveice10.opennbt.tag.builtin.Tag patternTag : patterns.getValue()) {
+ com.nukkitx.nbt.tag.CompoundTag newPatternTag = getBedrockBannerPattern((CompoundTag) patternTag);
+ if (newPatternTag != null) {
+ tagsList.add(newPatternTag);
+ }
+ }
+
+ return new com.nukkitx.nbt.tag.ListTag<>("Patterns", com.nukkitx.nbt.tag.CompoundTag.class, tagsList);
+ }
+
+ /**
+ * Convert the Java edition banner pattern nbt to Bedrock edition, null if the pattern doesn't exist
+ *
+ * @param pattern Java edition pattern nbt
+ * @return The Bedrock edition format pattern nbt
+ */
+ public static com.nukkitx.nbt.tag.CompoundTag getBedrockBannerPattern(CompoundTag pattern) {
+ String patternName = (String) pattern.get("Pattern").getValue();
+
+ // Return null if its the globe pattern as it doesn't exist on bedrock
+ if (patternName.equals("glb")) {
+ return null;
+ }
+
+ return CompoundTagBuilder.builder()
+ .intTag("Color", 15 - (int) pattern.get("Color").getValue())
+ .stringTag("Pattern", (String) pattern.get("Pattern").getValue())
+ .stringTag("Pattern", patternName)
+ .buildRootTag();
+ }
+
+ /**
+ * Convert a list of patterns from Bedrock nbt to Java nbt
+ *
+ * @param patterns The patterns to convert
+ * @return The new converted patterns
+ */
+ public static ListTag convertBannerPattern(com.nukkitx.nbt.tag.ListTag> patterns) {
+ List tagsList = new ArrayList<>();
+ for (Object patternTag : patterns.getValue()) {
+ CompoundTag newPatternTag = getJavaBannerPattern((com.nukkitx.nbt.tag.CompoundTag) patternTag);
+ tagsList.add(newPatternTag);
+ }
+
+ return new ListTag("Patterns", tagsList);
+ }
+
+ /**
+ * Convert the Bedrock edition banner pattern nbt to Java edition
+ *
+ * @param pattern Bedorck edition pattern nbt
+ * @return The Java edition format pattern nbt
+ */
+ public static CompoundTag getJavaBannerPattern(com.nukkitx.nbt.tag.CompoundTag pattern) {
+ Map tags = new HashMap<>();
+ tags.put("Color", new IntTag("Color", 15 - pattern.getInt("Color")));
+ tags.put("Pattern", new StringTag("Pattern", pattern.getString("Pattern")));
+
+ return new CompoundTag("", tags);
+ }
+}
diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/item/translators/PotionTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/item/translators/PotionTranslator.java
index e528b448..2fdde31d 100644
--- a/connector/src/main/java/org/geysermc/connector/network/translators/item/translators/PotionTranslator.java
+++ b/connector/src/main/java/org/geysermc/connector/network/translators/item/translators/PotionTranslator.java
@@ -30,22 +30,22 @@ import com.github.steveice10.opennbt.tag.builtin.StringTag;
import com.github.steveice10.opennbt.tag.builtin.Tag;
import com.nukkitx.protocol.bedrock.data.ItemData;
import org.geysermc.connector.GeyserConnector;
-import org.geysermc.connector.network.translators.ItemStackTranslator;
+import org.geysermc.connector.network.translators.item.ItemRegistry;
+import org.geysermc.connector.network.translators.item.ItemTranslator;
import org.geysermc.connector.network.translators.ItemRemapper;
import org.geysermc.connector.network.translators.item.ItemEntry;
import org.geysermc.connector.network.translators.item.Potion;
-import org.geysermc.connector.utils.Toolbox;
import java.util.List;
import java.util.stream.Collectors;
@ItemRemapper
-public class PotionTranslator extends ItemStackTranslator {
+public class PotionTranslator extends ItemTranslator {
private List appliedItems;
public PotionTranslator() {
- appliedItems = Toolbox.ITEM_ENTRIES.values().stream().filter(entry -> entry.getJavaIdentifier().endsWith("potion")).collect(Collectors.toList());
+ appliedItems = ItemRegistry.ITEM_ENTRIES.values().stream().filter(entry -> entry.getJavaIdentifier().endsWith("potion")).collect(Collectors.toList());
}
@Override
diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/item/translators/nbt/BasicItemTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/item/translators/nbt/BasicItemTranslator.java
new file mode 100644
index 00000000..776cec72
--- /dev/null
+++ b/connector/src/main/java/org/geysermc/connector/network/translators/item/translators/nbt/BasicItemTranslator.java
@@ -0,0 +1,127 @@
+/*
+ * 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.translators.nbt;
+
+import com.github.steveice10.opennbt.tag.builtin.CompoundTag;
+import com.github.steveice10.opennbt.tag.builtin.ListTag;
+import com.github.steveice10.opennbt.tag.builtin.StringTag;
+import com.github.steveice10.opennbt.tag.builtin.Tag;
+import net.kyori.text.Component;
+import net.kyori.text.TextComponent;
+import net.kyori.text.serializer.gson.GsonComponentSerializer;
+import net.kyori.text.serializer.legacy.LegacyComponentSerializer;
+import org.geysermc.connector.network.translators.ItemRemapper;
+import org.geysermc.connector.network.translators.item.NbtItemStackTranslator;
+import org.geysermc.connector.network.translators.item.ItemEntry;
+import org.geysermc.connector.utils.MessageUtils;
+
+import java.util.ArrayList;
+import java.util.List;
+
+@ItemRemapper(priority = -1)
+public class BasicItemTranslator extends NbtItemStackTranslator {
+
+ @Override
+ public void translateToBedrock(CompoundTag itemTag, ItemEntry itemEntry) {
+ if (!itemTag.contains("display")) {
+ return;
+ }
+ CompoundTag displayTag = itemTag.get("display");
+ if (displayTag.contains("Name")) {
+ StringTag nameTag = displayTag.get("Name");
+ try {
+ displayTag.put(new StringTag("Name", toBedrockMessage(nameTag)));
+ } catch (Exception ex) {
+ }
+ }
+
+ if (displayTag.contains("Lore")) {
+ ListTag loreTag = displayTag.get("Lore");
+ List lore = new ArrayList<>();
+ for (Tag tag : loreTag.getValue()) {
+ if (!(tag instanceof StringTag)) return;
+ try {
+ lore.add(new StringTag("", toBedrockMessage((StringTag) tag)));
+ } catch (Exception ex) {
+ }
+ }
+ displayTag.put(new ListTag("Lore", lore));
+ }
+ }
+
+ @Override
+ public void translateToJava(CompoundTag itemTag, ItemEntry itemEntry) {
+ if (!itemTag.contains("display")) {
+ return;
+ }
+ CompoundTag displayTag = itemTag.get("display");
+ if (displayTag.contains("Name")) {
+ StringTag nameTag = displayTag.get("Name");
+ displayTag.put(new StringTag("Name", toJavaMessage(nameTag)));
+ }
+
+ if (displayTag.contains("Lore")) {
+ ListTag loreTag = displayTag.get("Lore");
+ List lore = new ArrayList<>();
+ for (Tag tag : loreTag.getValue()) {
+ if (!(tag instanceof StringTag)) return;
+ lore.add(new StringTag("", "§r" + toJavaMessage((StringTag) tag)));
+ }
+ displayTag.put(new ListTag("Lore", lore));
+ }
+ }
+
+ private String toJavaMessage(StringTag tag) {
+ String message = tag.getValue();
+ if (message == null) return null;
+ if (message.startsWith("§r")) {
+ message = message.replaceFirst("§r", "");
+ }
+ Component component = TextComponent.of(message);
+ return GsonComponentSerializer.INSTANCE.serialize(component);
+ }
+
+ private String toBedrockMessage(StringTag tag) {
+ String message = tag.getValue();
+ if (message == null) return null;
+ TextComponent component = (TextComponent) MessageUtils.phraseJavaMessage(message);
+ String legacy = LegacyComponentSerializer.legacy().serialize(component);
+ if (hasFormatting(LegacyComponentSerializer.legacy().deserialize(legacy))) {
+ return "§r" + legacy;
+ }
+ return legacy;
+ }
+
+ private boolean hasFormatting(Component component) {
+ if (component.hasStyling()) return true;
+ for (Component child : component.children()) {
+ if (hasFormatting(child)) {
+ return true;
+ }
+ }
+ return false;
+ }
+}
diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/item/translators/nbt/BookPagesTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/item/translators/nbt/BookPagesTranslator.java
index f29f16fe..e802f017 100644
--- a/connector/src/main/java/org/geysermc/connector/network/translators/item/translators/nbt/BookPagesTranslator.java
+++ b/connector/src/main/java/org/geysermc/connector/network/translators/item/translators/nbt/BookPagesTranslator.java
@@ -30,7 +30,7 @@ import com.github.steveice10.opennbt.tag.builtin.ListTag;
import com.github.steveice10.opennbt.tag.builtin.StringTag;
import com.github.steveice10.opennbt.tag.builtin.Tag;
import org.geysermc.connector.network.translators.ItemRemapper;
-import org.geysermc.connector.network.translators.NbtItemStackTranslator;
+import org.geysermc.connector.network.translators.item.NbtItemStackTranslator;
import org.geysermc.connector.network.translators.item.ItemEntry;
import org.geysermc.connector.utils.MessageUtils;
@@ -42,43 +42,45 @@ public class BookPagesTranslator extends NbtItemStackTranslator {
@Override
public void translateToBedrock(CompoundTag itemTag, ItemEntry itemEntry) {
- if (itemTag.contains("pages")) {
- List pages = new ArrayList<>();
- ListTag pagesTag = itemTag.get("pages");
- for (Tag tag : pagesTag.getValue()) {
- if (!(tag instanceof StringTag))
- continue;
-
- StringTag textTag = (StringTag) tag;
-
- CompoundTag pageTag = new CompoundTag("");
- pageTag.put(new StringTag("photoname", ""));
- pageTag.put(new StringTag("text", MessageUtils.getBedrockMessage(textTag.getValue())));
- pages.add(pageTag);
- }
-
- itemTag.remove("pages");
- itemTag.put(new ListTag("pages", pages));
+ if (!itemTag.contains("pages")) {
+ return;
}
+ List pages = new ArrayList<>();
+ ListTag pagesTag = itemTag.get("pages");
+ for (Tag tag : pagesTag.getValue()) {
+ if (!(tag instanceof StringTag))
+ continue;
+
+ StringTag textTag = (StringTag) tag;
+
+ CompoundTag pageTag = new CompoundTag("");
+ pageTag.put(new StringTag("photoname", ""));
+ pageTag.put(new StringTag("text", MessageUtils.getBedrockMessageLenient(textTag.getValue())));
+ pages.add(pageTag);
+ }
+
+ itemTag.remove("pages");
+ itemTag.put(new ListTag("pages", pages));
}
@Override
public void translateToJava(CompoundTag itemTag, ItemEntry itemEntry) {
- if (itemTag.contains("pages")) {
- List pages = new ArrayList<>();
- ListTag pagesTag = itemTag.get("pages");
- for (Tag tag : pagesTag.getValue()) {
- if (!(tag instanceof CompoundTag))
- continue;
-
- CompoundTag pageTag = (CompoundTag) tag;
-
- StringTag textTag = pageTag.get("text");
- pages.add(new StringTag(MessageUtils.getJavaMessage(textTag.getValue())));
- }
-
- itemTag.remove("pages");
- itemTag.put(new ListTag("pages", pages));
+ if (!itemTag.contains("pages")) {
+ return;
}
+ List pages = new ArrayList<>();
+ ListTag pagesTag = itemTag.get("pages");
+ for (Tag tag : pagesTag.getValue()) {
+ if (!(tag instanceof CompoundTag))
+ continue;
+
+ CompoundTag pageTag = (CompoundTag) tag;
+
+ StringTag textTag = pageTag.get("text");
+ pages.add(new StringTag(MessageUtils.getJavaMessage(textTag.getValue())));
+ }
+
+ itemTag.remove("pages");
+ itemTag.put(new ListTag("pages", pages));
}
}
diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/item/translators/nbt/CrossbowTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/item/translators/nbt/CrossbowTranslator.java
new file mode 100644
index 00000000..3c5271eb
--- /dev/null
+++ b/connector/src/main/java/org/geysermc/connector/network/translators/item/translators/nbt/CrossbowTranslator.java
@@ -0,0 +1,78 @@
+/*
+ * 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.translators.nbt;
+
+import com.github.steveice10.opennbt.tag.builtin.ByteTag;
+import com.github.steveice10.opennbt.tag.builtin.CompoundTag;
+import com.github.steveice10.opennbt.tag.builtin.ListTag;
+import com.github.steveice10.opennbt.tag.builtin.StringTag;
+import org.geysermc.connector.network.translators.ItemRemapper;
+import org.geysermc.connector.network.translators.item.NbtItemStackTranslator;
+import org.geysermc.connector.network.translators.item.ItemEntry;
+
+@ItemRemapper
+public class CrossbowTranslator extends NbtItemStackTranslator {
+
+ @Override
+ public void translateToBedrock(CompoundTag itemTag, ItemEntry itemEntry) {
+ if (itemTag.get("ChargedProjectiles") != null) {
+ ListTag chargedProjectiles = itemTag.get("ChargedProjectiles");
+ if (!chargedProjectiles.getValue().isEmpty()) {
+ CompoundTag projectile = (CompoundTag) chargedProjectiles.getValue().get(0);
+
+ CompoundTag newProjectile = new CompoundTag("chargedItem");
+ newProjectile.put(new ByteTag("Count", (byte) projectile.get("Count").getValue()));
+ newProjectile.put(new StringTag("Name", (String) projectile.get("id").getValue()));
+
+ // Not sure what this is for
+ newProjectile.put(new ByteTag("Damage", (byte) 0));
+
+ itemTag.put(newProjectile);
+ }
+ }
+ }
+
+ @Override
+ public void translateToJava(CompoundTag itemTag, ItemEntry itemEntry) {
+ if (itemTag.get("chargedItem") != null) {
+ CompoundTag chargedItem = itemTag.get("chargedItem");
+
+ CompoundTag newProjectile = new CompoundTag("");
+ newProjectile.put(new ByteTag("Count", (byte) chargedItem.get("Count").getValue()));
+ newProjectile.put(new StringTag("id", (String) chargedItem.get("Name").getValue()));
+
+ ListTag chargedProjectiles = new ListTag("ChargedProjectiles");
+ chargedProjectiles.add(newProjectile);
+
+ itemTag.put(chargedProjectiles);
+ }
+ }
+
+ @Override
+ public boolean acceptItem(ItemEntry itemEntry) {
+ return "minecraft:crossbow".equals(itemEntry.getJavaIdentifier());
+ }
+}
diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/item/translators/nbt/EnchantedBookTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/item/translators/nbt/EnchantedBookTranslator.java
index 2ca6e35c..63ac6c51 100644
--- a/connector/src/main/java/org/geysermc/connector/network/translators/item/translators/nbt/EnchantedBookTranslator.java
+++ b/connector/src/main/java/org/geysermc/connector/network/translators/item/translators/nbt/EnchantedBookTranslator.java
@@ -29,7 +29,7 @@ import com.github.steveice10.opennbt.tag.builtin.CompoundTag;
import com.github.steveice10.opennbt.tag.builtin.ListTag;
import com.github.steveice10.opennbt.tag.builtin.Tag;
import org.geysermc.connector.network.translators.ItemRemapper;
-import org.geysermc.connector.network.translators.NbtItemStackTranslator;
+import org.geysermc.connector.network.translators.item.NbtItemStackTranslator;
import org.geysermc.connector.network.translators.item.ItemEntry;
@ItemRemapper(priority = 1)
@@ -37,25 +37,27 @@ public class EnchantedBookTranslator extends NbtItemStackTranslator {
@Override
public void translateToBedrock(CompoundTag itemTag, ItemEntry itemEntry) {
- if (itemTag.contains("StoredEnchantments")) {
- Tag enchTag = itemTag.get("StoredEnchantments");
- if (enchTag instanceof ListTag) {
- enchTag = new ListTag("Enchantments", ((ListTag) enchTag).getValue());
- itemTag.remove("StoredEnchantments");
- itemTag.put(enchTag);
- }
+ if (!itemTag.contains("StoredEnchantments")) {
+ return;
+ }
+ Tag enchTag = itemTag.get("StoredEnchantments");
+ if (enchTag instanceof ListTag) {
+ enchTag = new ListTag("Enchantments", ((ListTag) enchTag).getValue());
+ itemTag.remove("StoredEnchantments");
+ itemTag.put(enchTag);
}
}
@Override
public void translateToJava(CompoundTag itemTag, ItemEntry itemEntry) {
- if (itemTag.contains("Enchantments")) {
- Tag enchTag = itemTag.get("Enchantments");
- if (enchTag instanceof ListTag) {
- enchTag = new ListTag("StoredEnchantments", ((ListTag) enchTag).getValue());
- itemTag.remove("Enchantments");
- itemTag.put(enchTag);
- }
+ if (!itemTag.contains("Enchantments")) {
+ return;
+ }
+ Tag enchTag = itemTag.get("Enchantments");
+ if (enchTag instanceof ListTag) {
+ enchTag = new ListTag("StoredEnchantments", ((ListTag) enchTag).getValue());
+ itemTag.remove("Enchantments");
+ itemTag.put(enchTag);
}
}
diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/item/translators/nbt/EnchantmentTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/item/translators/nbt/EnchantmentTranslator.java
index 41e1ae36..404d7824 100644
--- a/connector/src/main/java/org/geysermc/connector/network/translators/item/translators/nbt/EnchantmentTranslator.java
+++ b/connector/src/main/java/org/geysermc/connector/network/translators/item/translators/nbt/EnchantmentTranslator.java
@@ -28,7 +28,7 @@ package org.geysermc.connector.network.translators.item.translators.nbt;
import com.github.steveice10.opennbt.tag.builtin.*;
import org.geysermc.connector.GeyserConnector;
import org.geysermc.connector.network.translators.ItemRemapper;
-import org.geysermc.connector.network.translators.NbtItemStackTranslator;
+import org.geysermc.connector.network.translators.item.NbtItemStackTranslator;
import org.geysermc.connector.network.translators.item.Enchantment;
import org.geysermc.connector.network.translators.item.ItemEntry;
@@ -73,48 +73,50 @@ public class EnchantmentTranslator extends NbtItemStackTranslator {
@Override
public void translateToJava(CompoundTag itemTag, ItemEntry itemEntry) {
- if (itemTag.contains("ench")) {
- ListTag enchantmentTag = itemTag.get("ench");
- List enchantments = new ArrayList<>();
- List storedEnchantments = new ArrayList<>();
- for (Tag value : enchantmentTag.getValue()) {
- if (!(value instanceof CompoundTag))
- continue;
-
- CompoundTag tagValue = (CompoundTag) value;
- ShortTag bedrockId = tagValue.get("id");
- if (bedrockId == null) continue;
-
- ShortTag geyserStoredEnchantmentTag = tagValue.get("GeyserStoredEnchantment");
-
- Enchantment enchantment = Enchantment.getByBedrockId(bedrockId.getValue());
- if (enchantment != null) {
- CompoundTag javaTag = new CompoundTag("");
- Map javaValue = javaTag.getValue();
- javaValue.put("id", new StringTag("id", enchantment.getJavaIdentifier()));
- ShortTag levelTag = tagValue.get("lvl");
- javaValue.put("lvl", new IntTag("lvl", levelTag != null ? levelTag.getValue() : 1));
- javaTag.setValue(javaValue);
-
-
- if (geyserStoredEnchantmentTag != null) {
- tagValue.remove("GeyserStoredEnchantment");
- storedEnchantments.add(javaTag);
- } else {
- enchantments.add(javaTag);
- }
- } else {
- GeyserConnector.getInstance().getLogger().debug("Unknown bedrock enchantment: " + bedrockId);
- }
- }
- if (!enchantments.isEmpty()) {
- itemTag.put(new ListTag("Enchantments", enchantments));
- }
- if (!storedEnchantments.isEmpty()) {
- itemTag.put(new ListTag("StoredEnchantments", enchantments));
- }
- itemTag.remove("ench");
+ if (!itemTag.contains("ench")) {
+ return;
}
+
+ ListTag enchantmentTag = itemTag.get("ench");
+ List enchantments = new ArrayList<>();
+ List storedEnchantments = new ArrayList<>();
+ for (Tag value : enchantmentTag.getValue()) {
+ if (!(value instanceof CompoundTag))
+ continue;
+
+ CompoundTag tagValue = (CompoundTag) value;
+ ShortTag bedrockId = tagValue.get("id");
+ if (bedrockId == null) continue;
+
+ ShortTag geyserStoredEnchantmentTag = tagValue.get("GeyserStoredEnchantment");
+
+ Enchantment enchantment = Enchantment.getByBedrockId(bedrockId.getValue());
+ if (enchantment != null) {
+ CompoundTag javaTag = new CompoundTag("");
+ Map javaValue = javaTag.getValue();
+ javaValue.put("id", new StringTag("id", enchantment.getJavaIdentifier()));
+ ShortTag levelTag = tagValue.get("lvl");
+ javaValue.put("lvl", new IntTag("lvl", levelTag != null ? levelTag.getValue() : 1));
+ javaTag.setValue(javaValue);
+
+
+ if (geyserStoredEnchantmentTag != null) {
+ tagValue.remove("GeyserStoredEnchantment");
+ storedEnchantments.add(javaTag);
+ } else {
+ enchantments.add(javaTag);
+ }
+ } else {
+ GeyserConnector.getInstance().getLogger().debug("Unknown bedrock enchantment: " + bedrockId);
+ }
+ }
+ if (!enchantments.isEmpty()) {
+ itemTag.put(new ListTag("Enchantments", enchantments));
+ }
+ if (!storedEnchantments.isEmpty()) {
+ itemTag.put(new ListTag("StoredEnchantments", enchantments));
+ }
+ itemTag.remove("ench");
}
diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/item/translators/nbt/FireworkTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/item/translators/nbt/FireworkTranslator.java
new file mode 100644
index 00000000..5b5182a5
--- /dev/null
+++ b/connector/src/main/java/org/geysermc/connector/network/translators/item/translators/nbt/FireworkTranslator.java
@@ -0,0 +1,157 @@
+/*
+ * 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.translators.nbt;
+
+import com.github.steveice10.opennbt.tag.builtin.*;
+import org.geysermc.connector.network.translators.ItemRemapper;
+import org.geysermc.connector.network.translators.item.ItemEntry;
+import org.geysermc.connector.network.translators.item.NbtItemStackTranslator;
+import org.geysermc.connector.utils.FireworkColor;
+import org.geysermc.connector.utils.MathUtils;
+
+@ItemRemapper
+public class FireworkTranslator extends NbtItemStackTranslator {
+
+ @Override
+ public void translateToBedrock(CompoundTag itemTag, ItemEntry itemEntry) {
+ if (!itemTag.contains("Fireworks")) {
+ return;
+ }
+
+ CompoundTag fireworks = itemTag.get("Fireworks");
+ if (fireworks.get("Flight") != null) {
+ fireworks.put(new ByteTag("Flight", MathUtils.convertByte(fireworks.get("Flight").getValue())));
+ }
+
+ ListTag explosions = fireworks.get("Explosions");
+ if (explosions == null) {
+ return;
+ }
+ for (Tag effect : explosions.getValue()) {
+ CompoundTag effectData = (CompoundTag) effect;
+
+ CompoundTag newEffectData = new CompoundTag("");
+
+ if (effectData.get("Type") != null) {
+ newEffectData.put(new ByteTag("FireworkType", MathUtils.convertByte(effectData.get("Type").getValue())));
+ }
+
+ if (effectData.get("Colors") != null) {
+ int[] oldColors = (int[]) effectData.get("Colors").getValue();
+ byte[] colors = new byte[oldColors.length];
+
+ int i = 0;
+ for (int color : oldColors) {
+ colors[i++] = FireworkColor.fromJavaID(color).getBedrockID();
+ }
+
+ newEffectData.put(new ByteArrayTag("FireworkColor", colors));
+ }
+
+ if (effectData.get("FadeColors") != null) {
+ int[] oldColors = (int[]) effectData.get("FadeColors").getValue();
+ byte[] colors = new byte[oldColors.length];
+
+ int i = 0;
+ for (int color : oldColors) {
+ colors[i++] = FireworkColor.fromJavaID(color).getBedrockID();
+ }
+
+ newEffectData.put(new ByteArrayTag("FireworkFade", colors));
+ }
+
+ if (effectData.get("Trail") != null) {
+ newEffectData.put(new ByteTag("FireworkTrail", MathUtils.convertByte(effectData.get("Trail").getValue())));
+ }
+
+ if (effectData.get("Flicker") != null) {
+ newEffectData.put(new ByteTag("FireworkFlicker", MathUtils.convertByte(effectData.get("Flicker").getValue())));
+ }
+
+ explosions.remove(effect);
+ explosions.add(newEffectData);
+ }
+ }
+
+ @Override
+ public void translateToJava(CompoundTag itemTag, ItemEntry itemEntry) {
+ CompoundTag fireworks = itemTag.get("Fireworks");
+ if (fireworks.get("Flight") != null) {
+ fireworks.put(new ByteTag("Flight", MathUtils.convertByte(fireworks.get("Flight").getValue())));
+ }
+
+ ListTag explosions = fireworks.get("Explosions");
+ for (Tag effect : explosions.getValue()) {
+ CompoundTag effectData = (CompoundTag) effect;
+
+ CompoundTag newEffectData = new CompoundTag("");
+
+ if (effectData.get("FireworkType") != null) {
+ newEffectData.put(new ByteTag("Type", MathUtils.convertByte(effectData.get("FireworkType").getValue())));
+ }
+
+ if (effectData.get("FireworkColor") != null) {
+ byte[] oldColors = (byte[]) effectData.get("FireworkColor").getValue();
+ int[] colors = new int[oldColors.length];
+
+ int i = 0;
+ for (byte color : oldColors) {
+ colors[i++] = FireworkColor.fromBedrockID(color).getJavaID();
+ }
+
+ newEffectData.put(new IntArrayTag("Colors", colors));
+ }
+
+ if (effectData.get("FireworkFade") != null) {
+ byte[] oldColors = (byte[]) effectData.get("FireworkFade").getValue();
+ int[] colors = new int[oldColors.length];
+
+ int i = 0;
+ for (byte color : oldColors) {
+ colors[i++] = FireworkColor.fromBedrockID(color).getJavaID();
+ }
+
+ newEffectData.put(new IntArrayTag("FadeColors", colors));
+ }
+
+ if (effectData.get("FireworkTrail") != null) {
+ newEffectData.put(new ByteTag("Trail", MathUtils.convertByte(effectData.get("FireworkTrail").getValue())));
+ }
+
+ if (effectData.get("FireworkFlicker") != null) {
+ newEffectData.put(new ByteTag("Flicker", MathUtils.convertByte(effectData.get("FireworkFlicker").getValue())));
+ }
+
+ explosions.remove(effect);
+ explosions.add(newEffectData);
+ }
+ }
+
+ @Override
+ public boolean acceptItem(ItemEntry itemEntry) {
+ return "minecraft:firework_rocket".equals(itemEntry.getJavaIdentifier());
+ }
+}
diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/item/translators/nbt/LeatherArmorTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/item/translators/nbt/LeatherArmorTranslator.java
index 4b01d912..9f864ccf 100644
--- a/connector/src/main/java/org/geysermc/connector/network/translators/item/translators/nbt/LeatherArmorTranslator.java
+++ b/connector/src/main/java/org/geysermc/connector/network/translators/item/translators/nbt/LeatherArmorTranslator.java
@@ -28,7 +28,7 @@ package org.geysermc.connector.network.translators.item.translators.nbt;
import com.github.steveice10.opennbt.tag.builtin.CompoundTag;
import com.github.steveice10.opennbt.tag.builtin.IntTag;
import org.geysermc.connector.network.translators.ItemRemapper;
-import org.geysermc.connector.network.translators.NbtItemStackTranslator;
+import org.geysermc.connector.network.translators.item.NbtItemStackTranslator;
import org.geysermc.connector.network.translators.item.ItemEntry;
@ItemRemapper
@@ -38,29 +38,31 @@ public class LeatherArmorTranslator extends NbtItemStackTranslator {
@Override
public void translateToBedrock(CompoundTag itemTag, ItemEntry itemEntry) {
- if (itemTag.contains("display")) {
- CompoundTag displayTag = itemTag.get("display");
- if (displayTag.contains("color")) {
- IntTag color = displayTag.get("color");
- if (color != null) {
- itemTag.put(new IntTag("customColor", color.getValue()));
- displayTag.remove("color");
- }
+ if (!itemTag.contains("display")) {
+ return;
+ }
+ CompoundTag displayTag = itemTag.get("display");
+ if (displayTag.contains("color")) {
+ IntTag color = displayTag.get("color");
+ if (color != null) {
+ itemTag.put(new IntTag("customColor", color.getValue()));
+ displayTag.remove("color");
}
}
}
@Override
public void translateToJava(CompoundTag itemTag, ItemEntry itemEntry) {
- if (itemTag.contains("customColor")) {
- IntTag color = itemTag.get("customColor");
- CompoundTag displayTag = itemTag.get("display");
- if (displayTag == null) {
- displayTag = new CompoundTag("display");
- }
- displayTag.put(color);
- itemTag.remove("customColor");
+ if (!itemTag.contains("customColor")) {
+ return;
}
+ IntTag color = itemTag.get("customColor");
+ CompoundTag displayTag = itemTag.get("display");
+ if (displayTag == null) {
+ displayTag = new CompoundTag("display");
+ }
+ displayTag.put(color);
+ itemTag.remove("customColor");
}
@Override
diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/item/translators/nbt/MapItemTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/item/translators/nbt/MapItemTranslator.java
index cdf272ec..8c418c0f 100644
--- a/connector/src/main/java/org/geysermc/connector/network/translators/item/translators/nbt/MapItemTranslator.java
+++ b/connector/src/main/java/org/geysermc/connector/network/translators/item/translators/nbt/MapItemTranslator.java
@@ -25,11 +25,12 @@
package org.geysermc.connector.network.translators.item.translators.nbt;
+import com.github.steveice10.opennbt.tag.builtin.ByteTag;
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 org.geysermc.connector.network.translators.ItemRemapper;
-import org.geysermc.connector.network.translators.NbtItemStackTranslator;
+import org.geysermc.connector.network.translators.item.NbtItemStackTranslator;
import org.geysermc.connector.network.translators.item.ItemEntry;
@ItemRemapper
@@ -42,6 +43,7 @@ public class MapItemTranslator extends NbtItemStackTranslator {
if (mapId != null) {
itemTag.put(new StringTag("map_uuid", mapId.getValue().toString()));
itemTag.put(new IntTag("map_name_index", mapId.getValue()));
+ itemTag.put(new ByteTag("map_display_players", (byte) 1));
itemTag.remove("map");
}
}
diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/java/JavaBossBarTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/java/JavaBossBarTranslator.java
index 2c32ef6f..3da76a22 100644
--- a/connector/src/main/java/org/geysermc/connector/network/translators/java/JavaBossBarTranslator.java
+++ b/connector/src/main/java/org/geysermc/connector/network/translators/java/JavaBossBarTranslator.java
@@ -32,8 +32,6 @@ import org.geysermc.connector.network.translators.Translator;
import com.github.steveice10.mc.protocol.packet.ingame.server.ServerBossBarPacket;
-import java.awt.*;
-
@Translator(packet = ServerBossBarPacket.class)
public class JavaBossBarTranslator extends PacketTranslator {
@Override
diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/java/JavaChatTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/java/JavaChatTranslator.java
index a527866c..53ce6811 100644
--- a/connector/src/main/java/org/geysermc/connector/network/translators/java/JavaChatTranslator.java
+++ b/connector/src/main/java/org/geysermc/connector/network/translators/java/JavaChatTranslator.java
@@ -76,6 +76,6 @@ public class JavaChatTranslator extends PacketTranslator {
textPacket.setMessage(MessageUtils.getTranslatedBedrockMessage(packet.getMessage(), locale, false));
}
- session.getUpstream().sendPacket(textPacket);
+ session.sendUpstreamPacket(textPacket);
}
}
diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/java/JavaServerDeclareCommandsTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/java/JavaDeclareCommandsTranslator.java
similarity index 96%
rename from connector/src/main/java/org/geysermc/connector/network/translators/java/JavaServerDeclareCommandsTranslator.java
rename to connector/src/main/java/org/geysermc/connector/network/translators/java/JavaDeclareCommandsTranslator.java
index d57b8948..053630d5 100644
--- a/connector/src/main/java/org/geysermc/connector/network/translators/java/JavaServerDeclareCommandsTranslator.java
+++ b/connector/src/main/java/org/geysermc/connector/network/translators/java/JavaDeclareCommandsTranslator.java
@@ -43,9 +43,14 @@ import org.geysermc.connector.network.translators.Translator;
import java.util.*;
@Translator(packet = ServerDeclareCommandsPacket.class)
-public class JavaServerDeclareCommandsTranslator extends PacketTranslator {
+public class JavaDeclareCommandsTranslator extends PacketTranslator {
@Override
public void translate(ServerDeclareCommandsPacket packet, GeyserSession session) {
+ // Don't send command suggestions if they are disabled
+ if (!session.getConnector().getConfig().isCommandSuggestions()) {
+ session.getConnector().getLogger().debug("Not sending command suggestions as they are disabled.");
+ return;
+ }
List commandData = new ArrayList<>();
Int2ObjectMap commands = new Int2ObjectOpenHashMap<>();
Int2ObjectMap> commandArgs = new Int2ObjectOpenHashMap<>();
@@ -59,6 +64,7 @@ public class JavaServerDeclareCommandsTranslator extends PacketTranslator= 1) {
@@ -99,7 +105,7 @@ public class JavaServerDeclareCommandsTranslator extends PacketTranslator> groupedByIds = Arrays.stream(ingredient.getOptions())
- .map(item -> Translators.getItemTranslator().translateToBedrock(session, item))
+ .map(item -> ItemTranslator.translateToBedrock(session, 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()) {
@@ -111,7 +111,7 @@ public class JavaDeclareRecipesTranslator extends PacketTranslator 0) {
- translatedItems[i] = Translators.getItemTranslator().translateToBedrock(session, ingredients[i].getOptions()[0]);
+ translatedItems[i] = ItemTranslator.translateToBedrock(session, ingredients[i].getOptions()[0]);
} else {
translatedItems[i] = ItemData.AIR;
}
diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/java/JavaDifficultyTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/java/JavaDifficultyTranslator.java
index 1abf8e46..7e4d9ca8 100644
--- a/connector/src/main/java/org/geysermc/connector/network/translators/java/JavaDifficultyTranslator.java
+++ b/connector/src/main/java/org/geysermc/connector/network/translators/java/JavaDifficultyTranslator.java
@@ -39,6 +39,6 @@ public class JavaDifficultyTranslator extends PacketTranslator {
@@ -49,24 +56,30 @@ public class JavaJoinGameTranslator extends PacketTranslator skinParts = Arrays.asList(SkinPart.values());
+ ClientSettingsPacket clientSettingsPacket = new ClientSettingsPacket(locale, (byte) session.getRenderDistance(), ChatVisibility.FULL, true, skinParts, Hand.MAIN_HAND);
+ session.sendDownstreamPacket(clientSettingsPacket);
+
if (DimensionUtils.javaToBedrock(packet.getDimension()) != entity.getDimension()) {
DimensionUtils.switchDimension(session, packet.getDimension());
}
diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/java/JavaLoginPluginMessageTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/java/JavaLoginPluginMessageTranslator.java
index 1ce17fe9..f2a2bf02 100644
--- a/connector/src/main/java/org/geysermc/connector/network/translators/java/JavaLoginPluginMessageTranslator.java
+++ b/connector/src/main/java/org/geysermc/connector/network/translators/java/JavaLoginPluginMessageTranslator.java
@@ -12,7 +12,7 @@ public class JavaLoginPluginMessageTranslator extends PacketTranslator
SetPlayerGameTypePacket playerGameTypePacket = new SetPlayerGameTypePacket();
playerGameTypePacket.setGamemode(packet.getGamemode().ordinal());
- session.getUpstream().sendPacket(playerGameTypePacket);
+ session.sendUpstreamPacket(playerGameTypePacket);
session.setGameMode(packet.getGamemode());
LevelEventPacket stopRainPacket = new LevelEventPacket();
stopRainPacket.setType(LevelEventType.STOP_RAIN);
stopRainPacket.setData(ThreadLocalRandom.current().nextInt(50000) + 10000);
stopRainPacket.setPosition(Vector3f.ZERO);
- session.getUpstream().sendPacket(stopRainPacket);
+ session.sendUpstreamPacket(stopRainPacket);
if (entity.getDimension() != DimensionUtils.javaToBedrock(packet.getDimension())) {
DimensionUtils.switchDimension(session, packet.getDimension());
diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/java/JavaTitleTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/java/JavaTitleTranslator.java
index 7334f8f8..8ecf4e30 100644
--- a/connector/src/main/java/org/geysermc/connector/network/translators/java/JavaTitleTranslator.java
+++ b/connector/src/main/java/org/geysermc/connector/network/translators/java/JavaTitleTranslator.java
@@ -65,6 +65,6 @@ public class JavaTitleTranslator extends PacketTranslator {
break;
}
- session.getUpstream().sendPacket(titlePacket);
+ session.sendUpstreamPacket(titlePacket);
}
}
diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/java/entity/JavaEntityAnimationTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/java/entity/JavaEntityAnimationTranslator.java
index 9d8853e7..4f2fe022 100644
--- a/connector/src/main/java/org/geysermc/connector/network/translators/java/entity/JavaEntityAnimationTranslator.java
+++ b/connector/src/main/java/org/geysermc/connector/network/translators/java/entity/JavaEntityAnimationTranslator.java
@@ -62,6 +62,6 @@ public class JavaEntityAnimationTranslator extends PacketTranslator {
+
+ @Override
+ public void translate(ServerEntityAttachPacket packet, GeyserSession session) {
+
+ Entity holderId;
+ if (packet.getEntityId() == session.getPlayerEntity().getEntityId()) {
+ holderId = session.getPlayerEntity();
+ } else {
+ holderId = session.getEntityCache().getEntityByJavaId(packet.getEntityId());
+ if (holderId == null) {
+ return;
+ }
+ }
+
+ Entity attachedToId;
+ if (packet.getAttachedToId() == session.getPlayerEntity().getEntityId()) {
+ attachedToId = session.getPlayerEntity();
+ } else {
+ attachedToId = session.getEntityCache().getEntityByJavaId(packet.getAttachedToId());
+ if ((attachedToId == null || packet.getAttachedToId() == 0)) {
+ // Is not being leashed
+ holderId.getMetadata().getFlags().setFlag(EntityFlag.LEASHED, false);
+ holderId.getMetadata().put(EntityData.LEAD_HOLDER_EID, 0);
+ holderId.updateBedrockMetadata(session);
+ EntityEventPacket eventPacket = new EntityEventPacket();
+ eventPacket.setRuntimeEntityId(holderId.getGeyserId());
+ eventPacket.setType(EntityEventType.REMOVE_LEASH);
+ eventPacket.setData(0);
+ session.sendUpstreamPacket(eventPacket);
+ return;
+ }
+ }
+
+ holderId.getMetadata().getFlags().setFlag(EntityFlag.LEASHED, true);
+ holderId.getMetadata().put(EntityData.LEAD_HOLDER_EID, attachedToId.getGeyserId());
+ holderId.updateBedrockMetadata(session);
+ }
+}
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 88e0969e..5905b1ec 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
@@ -55,6 +55,6 @@ public class JavaEntityEffectTranslator extends PacketTranslator {
@@ -55,7 +55,7 @@ public class JavaEntityEquipmentTranslator extends PacketTranslator {
+
+ @Override
+ public void translate(ServerEntitySetPassengersPacket packet, GeyserSession session) {
+ Entity entity = session.getEntityCache().getEntityByJavaId(packet.getEntityId());
+ if (entity == null) return;
+
+ LongOpenHashSet passengers = entity.getPassengers().clone();
+ boolean rider = true;
+ for (long passengerId : packet.getPassengerIds()) {
+ Entity passenger = session.getEntityCache().getEntityByJavaId(passengerId);
+ if (passengerId == session.getPlayerEntity().getEntityId()) {
+ passenger = session.getPlayerEntity();
+ session.setRidingVehicleEntity(entity);
+ }
+ // Passenger hasn't loaded in and entity link needs to be set later
+ if (passenger == null && passengerId != 0) {
+ session.getEntityCache().addCachedPlayerEntityLink(passengerId, packet.getEntityId());
+ }
+ if (passenger == null) {
+ continue;
+ }
+
+ EntityLink.Type type = rider ? EntityLink.Type.RIDER : EntityLink.Type.PASSENGER;
+ SetEntityLinkPacket linkPacket = new SetEntityLinkPacket();
+ linkPacket.setEntityLink(new EntityLink(entity.getGeyserId(), passenger.getGeyserId(), type, false));
+ session.sendUpstreamPacket(linkPacket);
+ passengers.add(passengerId);
+
+ // Head rotation on boats
+ if (entity.getEntityType() == EntityType.BOAT) {
+ passenger.getMetadata().put(EntityData.RIDER_ROTATION_LOCKED, (byte) 1);
+ passenger.getMetadata().put(EntityData.RIDER_MAX_ROTATION, 90f);
+ passenger.getMetadata().put(EntityData.RIDER_MIN_ROTATION, !passengers.isEmpty() ? -90f : 0f);
+ } else {
+ passenger.getMetadata().remove(EntityData.RIDER_ROTATION_LOCKED);
+ passenger.getMetadata().remove(EntityData.RIDER_MAX_ROTATION);
+ passenger.getMetadata().remove(EntityData.RIDER_MIN_ROTATION);
+ }
+
+ passenger.updateBedrockMetadata(session);
+ this.updateOffset(passenger, entity.getEntityType(), session, rider, true, (passengers.size() > 1));
+ rider = false;
+ }
+
+ entity.setPassengers(passengers);
+
+ for (long passengerId : entity.getPassengers()) {
+ Entity passenger = session.getEntityCache().getEntityByJavaId(passengerId);
+ if (passenger == null) {
+ continue;
+ }
+ if (Arrays.stream(packet.getPassengerIds()).noneMatch(id -> id == passengerId)) {
+ SetEntityLinkPacket linkPacket = new SetEntityLinkPacket();
+ linkPacket.setEntityLink(new EntityLink(entity.getGeyserId(), passenger.getGeyserId(), EntityLink.Type.REMOVE, false));
+ session.sendUpstreamPacket(linkPacket);
+ passengers.remove(passenger.getEntityId());
+
+ this.updateOffset(passenger, entity.getEntityType(), session, false, false, (passengers.size() > 1));
+ }
+ }
+
+ if (entity.getEntityType() == EntityType.HORSE) {
+ entity.getMetadata().put(EntityData.RIDER_SEAT_POSITION, Vector3f.from(0.0f, 2.3200102f, -0.2f));
+ entity.getMetadata().put(EntityData.RIDER_MAX_ROTATION, 181.0f);
+
+ entity.updateBedrockMetadata(session);
+ }
+ }
+
+ private void updateOffset(Entity passenger, EntityType mountType, GeyserSession session, boolean rider, boolean riding, boolean moreThanOneEntity) {
+ // Without the Y offset, Bedrock players will find themselves in the floor when mounting
+ float yOffset = 0;
+ switch (mountType) {
+ case BOAT:
+ yOffset = passenger.getEntityType() == EntityType.PLAYER ? 1.02001f : -0.2f;
+ break;
+ case MINECART:
+ yOffset = passenger.getEntityType() == EntityType.PLAYER ? 1.02001f : 0f;
+ break;
+ case DONKEY:
+ yOffset = 2.1f;
+ break;
+ case HORSE:
+ case SKELETON_HORSE:
+ case ZOMBIE_HORSE:
+ case MULE:
+ yOffset = 2.3f;
+ break;
+ case LLAMA:
+ case TRADER_LLAMA:
+ yOffset = 2.5f;
+ break;
+ case PIG:
+ yOffset = 1.85001f;
+ break;
+ case ARMOR_STAND:
+ yOffset = 1.3f;
+ break;
+ }
+ Vector3f offset = Vector3f.from(0f, yOffset, 0f);
+ // Without the X offset, more than one entity on a boat is stacked on top of each other
+ if (rider && moreThanOneEntity) {
+ offset = offset.add(Vector3f.from(0.2, 0, 0));
+ } else if (moreThanOneEntity) {
+ offset = offset.add(Vector3f.from(-0.6, 0, 0));
+ }
+ passenger.getMetadata().getFlags().setFlag(EntityFlag.RIDING, riding);
+ if (riding) {
+ passenger.getMetadata().put(EntityData.RIDER_SEAT_POSITION, offset);
+ }
+ passenger.updateBedrockMetadata(session);
+ }
+}
diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/java/entity/JavaEntityStatusTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/java/entity/JavaEntityStatusTranslator.java
index d3e3973c..b8675dbf 100644
--- a/connector/src/main/java/org/geysermc/connector/network/translators/java/entity/JavaEntityStatusTranslator.java
+++ b/connector/src/main/java/org/geysermc/connector/network/translators/java/entity/JavaEntityStatusTranslator.java
@@ -49,6 +49,8 @@ public class JavaEntityStatusTranslator extends PacketTranslator playerFlags = new ObjectOpenHashSet<>();
playerFlags.add(AdventureSettingsPacket.Flag.AUTO_JUMP);
@@ -73,6 +73,6 @@ public class JavaPlayerAbilitiesTranslator extends PacketTranslator {
@Override
public void translate(ServerPlayerListEntryPacket packet, GeyserSession session) {
- if (packet.getAction() != PlayerListEntryAction.ADD_PLAYER && packet.getAction() != PlayerListEntryAction.REMOVE_PLAYER) return;
+ if (packet.getAction() != PlayerListEntryAction.ADD_PLAYER && packet.getAction() != PlayerListEntryAction.REMOVE_PLAYER)
+ return;
PlayerListPacket translate = new PlayerListPacket();
translate.setAction(packet.getAction() == PlayerListEntryAction.ADD_PLAYER ? PlayerListPacket.Action.ADD : PlayerListPacket.Action.REMOVE);
for (PlayerListEntry entry : packet.getEntries()) {
- if (packet.getAction() == PlayerListEntryAction.ADD_PLAYER) {
- boolean self = entry.getProfile().getId().equals(session.getPlayerEntity().getUuid());
+ switch (packet.getAction()) {
+ case ADD_PLAYER:
+ PlayerEntity playerEntity;
+ boolean self = entry.getProfile().getId().equals(session.getPlayerEntity().getUuid());
- PlayerEntity playerEntity = session.getPlayerEntity();
- if (self) playerEntity.setProfile(entry.getProfile());
- else {
- playerEntity = new PlayerEntity(
- entry.getProfile(),
- -1,
- session.getEntityCache().getNextEntityId().incrementAndGet(),
- Vector3f.ZERO,
- Vector3f.ZERO,
- Vector3f.ZERO
- );
- }
+ if (self) {
+ // Entity is ourself
+ playerEntity = session.getPlayerEntity();
+ SkinUtils.requestAndHandleSkinAndCape(playerEntity, session, skinAndCape -> {
+ GeyserConnector.getInstance().getLogger().debug("Loading Local Bedrock Java Skin Data");
+ });
+ } else {
+ playerEntity = session.getEntityCache().getPlayerEntity(entry.getProfile().getId());
+ }
- session.getEntityCache().addPlayerEntity(playerEntity);
- playerEntity.setPlayerList(true);
+ if (playerEntity == null) {
+ // It's a new player
+ playerEntity = new PlayerEntity(
+ entry.getProfile(),
+ -1,
+ session.getEntityCache().getNextEntityId().incrementAndGet(),
+ Vector3f.ZERO,
+ Vector3f.ZERO,
+ Vector3f.ZERO
+ );
+ }
- translate.getEntries().add(SkinUtils.buildCachedEntry(entry.getProfile(), playerEntity.getGeyserId()));
- } else {
- PlayerEntity entity = session.getEntityCache().getPlayerEntity(entry.getProfile().getId());
- if (entity != null && entity.isValid()) {
- // remove from tablist but player entity is still there
- entity.setPlayerList(false);
- } else {
- // just remove it from caching
- session.getEntityCache().removePlayerEntity(entry.getProfile().getId());
- }
- translate.getEntries().add(new PlayerListPacket.Entry(entry.getProfile().getId()));
+ session.getEntityCache().addPlayerEntity(playerEntity);
+
+ playerEntity.setProfile(entry.getProfile());
+ playerEntity.setPlayerList(true);
+ playerEntity.setValid(true);
+
+ PlayerListPacket.Entry playerListEntry = SkinUtils.buildCachedEntry(entry.getProfile(), playerEntity.getGeyserId());
+ if (self) {
+ // Copy the entry with our identity instead.
+ PlayerListPacket.Entry copy = new PlayerListPacket.Entry(session.getAuthData().getUUID());
+ copy.setName(playerListEntry.getName());
+ copy.setEntityId(playerListEntry.getEntityId());
+ copy.setSkin(playerListEntry.getSkin());
+ copy.setXuid(playerListEntry.getXuid());
+ copy.setPlatformChatId(playerListEntry.getPlatformChatId());
+ copy.setTeacher(playerListEntry.isTeacher());
+ playerListEntry = copy;
+ }
+
+ translate.getEntries().add(playerListEntry);
+ break;
+ case REMOVE_PLAYER:
+ PlayerEntity entity = session.getEntityCache().getPlayerEntity(entry.getProfile().getId());
+ if (entity != null && entity.isValid()) {
+ // remove from tablist but player entity is still there
+ entity.setPlayerList(false);
+ } else {
+ // just remove it from caching
+ if (entity == null) {
+ session.getEntityCache().removePlayerEntity(entry.getProfile().getId());
+ } else {
+ entity.setPlayerList(false);
+ session.getEntityCache().removeEntity(entity, false);
+ }
+ }
+ translate.getEntries().add(new PlayerListPacket.Entry(entry.getProfile().getId()));
+ break;
}
}
if (packet.getAction() == PlayerListEntryAction.REMOVE_PLAYER || session.getUpstream().isInitialized()) {
- session.getUpstream().sendPacket(translate);
+ session.sendUpstreamPacket(translate);
}
}
}
diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/java/entity/player/JavaPlayerPositionRotationTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/java/entity/player/JavaPlayerPositionRotationTranslator.java
index cfeb2371..a44b200e 100644
--- a/connector/src/main/java/org/geysermc/connector/network/translators/java/entity/player/JavaPlayerPositionRotationTranslator.java
+++ b/connector/src/main/java/org/geysermc/connector/network/translators/java/entity/player/JavaPlayerPositionRotationTranslator.java
@@ -25,13 +25,7 @@
package org.geysermc.connector.network.translators.java.entity.player;
-import org.geysermc.connector.entity.Entity;
-import org.geysermc.connector.entity.type.EntityType;
-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.ChunkUtils;
-
+import com.github.steveice10.mc.protocol.data.game.entity.player.PositionElement;
import com.github.steveice10.mc.protocol.packet.ingame.client.world.ClientTeleportConfirmPacket;
import com.github.steveice10.mc.protocol.packet.ingame.server.entity.player.ServerPlayerPositionRotationPacket;
import com.nukkitx.math.vector.Vector3f;
@@ -40,13 +34,20 @@ import com.nukkitx.protocol.bedrock.packet.EntityEventPacket;
import com.nukkitx.protocol.bedrock.packet.MovePlayerPacket;
import com.nukkitx.protocol.bedrock.packet.RespawnPacket;
import com.nukkitx.protocol.bedrock.packet.SetEntityDataPacket;
+import org.geysermc.connector.entity.PlayerEntity;
+import org.geysermc.connector.entity.type.EntityType;
+import org.geysermc.connector.network.session.GeyserSession;
+import org.geysermc.connector.network.session.cache.TeleportCache;
+import org.geysermc.connector.network.translators.PacketTranslator;
+import org.geysermc.connector.network.translators.Translator;
+import org.geysermc.connector.utils.ChunkUtils;
@Translator(packet = ServerPlayerPositionRotationPacket.class)
public class JavaPlayerPositionRotationTranslator extends PacketTranslator {
@Override
public void translate(ServerPlayerPositionRotationPacket packet, GeyserSession session) {
- Entity entity = session.getPlayerEntity();
+ PlayerEntity entity = session.getPlayerEntity();
if (entity == null)
return;
@@ -54,7 +55,7 @@ public class JavaPlayerPositionRotationTranslator extends PacketTranslator 1.5 || (yDis < 1.45 || yDis > (session.isJumping() ? 4.3 : (session.isSprinting() ? 2.5 : 1.9))) || zDis > 1.5) {
- entity.moveAbsolute(session, Vector3f.from(packet.getX(), packet.getY(), packet.getZ()), packet.getYaw(), packet.getPitch(), true);
+ if (!(xDis > 1.5 || (yDis < 1.45 || yDis > (session.isJumping() ? 4.3 : (session.isSprinting() ? 2.5 : 1.9))) || zDis > 1.5)) {
+ // Fake confirm the teleport but don't send it to the client
+ ClientTeleportConfirmPacket teleportConfirmPacket = new ClientTeleportConfirmPacket(packet.getTeleportId());
+ session.sendDownstreamPacket(teleportConfirmPacket);
+ return;
}
}
- ClientTeleportConfirmPacket teleportConfirmPacket = new ClientTeleportConfirmPacket(packet.getTeleportId());
- session.getDownstream().getSession().send(teleportConfirmPacket);
+ // If coordinates are relative, then add to the existing coordinate
+ double newX = packet.getX() +
+ (packet.getRelative().contains(PositionElement.X) ? entity.getPosition().getX() : 0);
+ double newY = packet.getY() +
+ (packet.getRelative().contains(PositionElement.Y) ? entity.getPosition().getY() - EntityType.PLAYER.getOffset() : 0);
+ double newZ = packet.getZ() +
+ (packet.getRelative().contains(PositionElement.Z) ? entity.getPosition().getZ() : 0);
+
+ double newPitch = packet.getPitch() +
+ (packet.getRelative().contains(PositionElement.PITCH) ? entity.getBedrockRotation().getX() : 0);
+ double newYaw = packet.getYaw() +
+ (packet.getRelative().contains(PositionElement.YAW) ? entity.getBedrockRotation().getY() : 0);
+
+
+ session.setTeleportCache(new TeleportCache(newX, newY, newZ, packet.getTeleportId()));
+ entity.moveAbsolute(session, Vector3f.from(newX, newY, newZ), (float) newYaw, (float) newPitch, true, true);
}
}
diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/java/entity/player/JavaPlayerStopSoundTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/java/entity/player/JavaPlayerStopSoundTranslator.java
new file mode 100644
index 00000000..906c68db
--- /dev/null
+++ b/connector/src/main/java/org/geysermc/connector/network/translators/java/entity/player/JavaPlayerStopSoundTranslator.java
@@ -0,0 +1,74 @@
+/*
+ * 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.data.game.world.sound.BuiltinSound;
+import com.github.steveice10.mc.protocol.data.game.world.sound.CustomSound;
+import com.github.steveice10.mc.protocol.packet.ingame.server.ServerStopSoundPacket;
+import com.nukkitx.protocol.bedrock.packet.StopSoundPacket;
+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.sound.SoundRegistry;
+
+@Translator(packet = ServerStopSoundPacket.class)
+public class JavaPlayerStopSoundTranslator extends PacketTranslator {
+
+ @Override
+ public void translate(ServerStopSoundPacket packet, GeyserSession session) {
+ String packetSound;
+ if(packet.getSound() instanceof BuiltinSound) {
+ packetSound = ((BuiltinSound) packet.getSound()).getName();
+ } else if(packet.getSound() instanceof CustomSound) {
+ packetSound = ((CustomSound) packet.getSound()).getName();
+ } else {
+ session.getConnector().getLogger().debug("Unknown sound packet, we were unable to map this. " + packet.toString());
+ return;
+ }
+ SoundRegistry.SoundMapping soundMapping = SoundRegistry.fromJava(packetSound);
+ session.getConnector().getLogger()
+ .debug("[StopSound] Sound mapping " + packetSound + " -> "
+ + soundMapping + (soundMapping == null ? "[not found]" : "")
+ + " - " + packet.toString());
+ String playsound;
+ if(soundMapping == null || soundMapping.getPlaysound() == null) {
+ // no mapping
+ session.getConnector().getLogger()
+ .debug("[StopSound] Defaulting to sound server gave us.");
+ playsound = packetSound;
+ } else {
+ playsound = soundMapping.getPlaysound();
+ }
+
+ StopSoundPacket stopSoundPacket = new StopSoundPacket();
+ stopSoundPacket.setSoundName(playsound);
+ // packet not mapped in the library
+ stopSoundPacket.setStoppingAllSound(false);
+
+ session.sendUpstreamPacket(stopSoundPacket);
+ session.getConnector().getLogger().debug("[StopSound] Packet sent - " + packet.toString() + " --> " + stopSoundPacket);
+ }
+}
diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/java/entity/spawn/JavaSpawnObjectTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/java/entity/spawn/JavaSpawnEntityTranslator.java
similarity index 66%
rename from connector/src/main/java/org/geysermc/connector/network/translators/java/entity/spawn/JavaSpawnObjectTranslator.java
rename to connector/src/main/java/org/geysermc/connector/network/translators/java/entity/spawn/JavaSpawnEntityTranslator.java
index c3998f87..920969a7 100644
--- a/connector/src/main/java/org/geysermc/connector/network/translators/java/entity/spawn/JavaSpawnObjectTranslator.java
+++ b/connector/src/main/java/org/geysermc/connector/network/translators/java/entity/spawn/JavaSpawnEntityTranslator.java
@@ -25,35 +25,35 @@
package org.geysermc.connector.network.translators.java.entity.spawn;
-import java.lang.reflect.Constructor;
-import java.lang.reflect.InvocationTargetException;
-
-import com.github.steveice10.mc.protocol.data.game.entity.type.object.FallingBlockData;
+import com.github.steveice10.mc.protocol.data.game.entity.object.FallingBlockData;
+import com.github.steveice10.mc.protocol.data.game.entity.object.HangingDirection;
+import com.github.steveice10.mc.protocol.data.game.entity.object.ProjectileData;
+import com.github.steveice10.mc.protocol.data.game.entity.type.EntityType;
+import com.github.steveice10.mc.protocol.packet.ingame.server.entity.spawn.ServerSpawnEntityPacket;
+import com.nukkitx.math.vector.Vector3f;
import org.geysermc.connector.entity.Entity;
import org.geysermc.connector.entity.FallingBlockEntity;
-import org.geysermc.connector.entity.type.EntityType;
+import org.geysermc.connector.entity.FishingHookEntity;
+import org.geysermc.connector.entity.ItemFrameEntity;
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.EntityUtils;
-import com.github.steveice10.mc.protocol.data.game.entity.type.object.ObjectType;
-import com.github.steveice10.mc.protocol.packet.ingame.server.entity.spawn.ServerSpawnObjectPacket;
-import com.nukkitx.math.vector.Vector3f;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
-@Translator(packet = ServerSpawnObjectPacket.class)
-public class JavaSpawnObjectTranslator extends PacketTranslator {
+@Translator(packet = ServerSpawnEntityPacket.class)
+public class JavaSpawnEntityTranslator extends PacketTranslator {
@Override
- public void translate(ServerSpawnObjectPacket packet, GeyserSession session) {
- if (packet.getType() == ObjectType.ITEM_FRAME)
- return;
+ public void translate(ServerSpawnEntityPacket packet, GeyserSession session) {
Vector3f position = Vector3f.from(packet.getX(), packet.getY(), packet.getZ());
Vector3f motion = Vector3f.from(packet.getMotionX(), packet.getMotionY(), packet.getMotionZ());
Vector3f rotation = Vector3f.from(packet.getYaw(), packet.getPitch(), 0);
- EntityType type = EntityUtils.toBedrockEntity(packet.getType());
+ org.geysermc.connector.entity.type.EntityType type = EntityUtils.toBedrockEntity(packet.getType());
if (type == null) {
session.getConnector().getLogger().warning("Entity type " + packet.getType() + " was null.");
return;
@@ -62,11 +62,19 @@ public class JavaSpawnObjectTranslator extends PacketTranslator entityClass = type.getEntityClass();
try {
Entity entity;
- if (packet.getType() == ObjectType.FALLING_BLOCK) {
+ if (packet.getType() == EntityType.FALLING_BLOCK) {
entity = new FallingBlockEntity(packet.getEntityId(), session.getEntityCache().getNextEntityId().incrementAndGet(),
type, position, motion, rotation, ((FallingBlockData) packet.getData()).getId());
+ } else if (packet.getType() == EntityType.ITEM_FRAME) {
+ // Item frames need the hanging direction
+ entity = new ItemFrameEntity(packet.getEntityId(), session.getEntityCache().getNextEntityId().incrementAndGet(),
+ type, position, motion, rotation, (HangingDirection) packet.getData());
+ } else if (packet.getType() == EntityType.FISHING_BOBBER) {
+ // Fishing bobbers need the owner for the line
+ entity = new FishingHookEntity(packet.getEntityId(), session.getEntityCache().getNextEntityId().incrementAndGet(),
+ type, position, motion, rotation, (ProjectileData) packet.getData());
} else {
- Constructor extends Entity> entityConstructor = entityClass.getConstructor(long.class, long.class, EntityType.class,
+ Constructor extends Entity> entityConstructor = entityClass.getConstructor(long.class, long.class, org.geysermc.connector.entity.type.EntityType.class,
Vector3f.class, Vector3f.class, Vector3f.class);
entity = entityConstructor.newInstance(packet.getEntityId(), session.getEntityCache().getNextEntityId().incrementAndGet(),
diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/java/entity/spawn/JavaSpawnMobTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/java/entity/spawn/JavaSpawnLivingEntityTranslator.java
similarity index 91%
rename from connector/src/main/java/org/geysermc/connector/network/translators/java/entity/spawn/JavaSpawnMobTranslator.java
rename to connector/src/main/java/org/geysermc/connector/network/translators/java/entity/spawn/JavaSpawnLivingEntityTranslator.java
index d955ecfd..8c246e51 100644
--- a/connector/src/main/java/org/geysermc/connector/network/translators/java/entity/spawn/JavaSpawnMobTranslator.java
+++ b/connector/src/main/java/org/geysermc/connector/network/translators/java/entity/spawn/JavaSpawnLivingEntityTranslator.java
@@ -25,9 +25,8 @@
package org.geysermc.connector.network.translators.java.entity.spawn;
-import java.lang.reflect.Constructor;
-import java.lang.reflect.InvocationTargetException;
-
+import com.github.steveice10.mc.protocol.packet.ingame.server.entity.spawn.ServerSpawnLivingEntityPacket;
+import com.nukkitx.math.vector.Vector3f;
import org.geysermc.connector.entity.Entity;
import org.geysermc.connector.entity.type.EntityType;
import org.geysermc.connector.network.session.GeyserSession;
@@ -35,14 +34,14 @@ import org.geysermc.connector.network.translators.PacketTranslator;
import org.geysermc.connector.network.translators.Translator;
import org.geysermc.connector.utils.EntityUtils;
-import com.github.steveice10.mc.protocol.packet.ingame.server.entity.spawn.ServerSpawnMobPacket;
-import com.nukkitx.math.vector.Vector3f;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
-@Translator(packet = ServerSpawnMobPacket.class)
-public class JavaSpawnMobTranslator extends PacketTranslator {
+@Translator(packet = ServerSpawnLivingEntityPacket.class)
+public class JavaSpawnLivingEntityTranslator extends PacketTranslator {
@Override
- public void translate(ServerSpawnMobPacket packet, GeyserSession session) {
+ public void translate(ServerSpawnLivingEntityPacket packet, GeyserSession session) {
Vector3f position = Vector3f.from(packet.getX(), packet.getY(), packet.getZ());
Vector3f motion = Vector3f.from(packet.getMotionX(), packet.getMotionY(), packet.getMotionZ());
Vector3f rotation = Vector3f.from(packet.getYaw(), packet.getPitch(), packet.getHeadYaw());
diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/java/entity/spawn/JavaSpawnPlayerTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/java/entity/spawn/JavaSpawnPlayerTranslator.java
index 331eb094..e01b95e9 100644
--- a/connector/src/main/java/org/geysermc/connector/network/translators/java/entity/spawn/JavaSpawnPlayerTranslator.java
+++ b/connector/src/main/java/org/geysermc/connector/network/translators/java/entity/spawn/JavaSpawnPlayerTranslator.java
@@ -25,23 +25,21 @@
package org.geysermc.connector.network.translators.java.entity.spawn;
+import com.github.steveice10.mc.protocol.packet.ingame.server.entity.spawn.ServerSpawnPlayerPacket;
+import com.nukkitx.math.vector.Vector3f;
import org.geysermc.connector.GeyserConnector;
import org.geysermc.connector.entity.PlayerEntity;
-import org.geysermc.connector.entity.type.EntityType;
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.SkinUtils;
-import com.github.steveice10.mc.protocol.packet.ingame.server.entity.spawn.ServerSpawnPlayerPacket;
-import com.nukkitx.math.vector.Vector3f;
-
@Translator(packet = ServerSpawnPlayerPacket.class)
public class JavaSpawnPlayerTranslator extends PacketTranslator {
@Override
public void translate(ServerSpawnPlayerPacket packet, GeyserSession session) {
- Vector3f position = Vector3f.from(packet.getX(), packet.getY() - EntityType.PLAYER.getOffset(), packet.getZ());
+ Vector3f position = Vector3f.from(packet.getX(), packet.getY(), packet.getZ());
Vector3f rotation = Vector3f.from(packet.getYaw(), packet.getPitch(), packet.getYaw());
PlayerEntity entity = session.getEntityCache().getPlayerEntity(packet.getUuid());
diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/java/entity/spawn/JavaSpawnGlobalEntityTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/java/entity/spawn/JavaSpawnWeatherEntityTranslator.java
similarity index 85%
rename from connector/src/main/java/org/geysermc/connector/network/translators/java/entity/spawn/JavaSpawnGlobalEntityTranslator.java
rename to connector/src/main/java/org/geysermc/connector/network/translators/java/entity/spawn/JavaSpawnWeatherEntityTranslator.java
index da2e59af..c50686a0 100644
--- a/connector/src/main/java/org/geysermc/connector/network/translators/java/entity/spawn/JavaSpawnGlobalEntityTranslator.java
+++ b/connector/src/main/java/org/geysermc/connector/network/translators/java/entity/spawn/JavaSpawnWeatherEntityTranslator.java
@@ -31,17 +31,17 @@ import org.geysermc.connector.network.session.GeyserSession;
import org.geysermc.connector.network.translators.PacketTranslator;
import org.geysermc.connector.network.translators.Translator;
-import com.github.steveice10.mc.protocol.packet.ingame.server.entity.spawn.ServerSpawnGlobalEntityPacket;
+import com.github.steveice10.mc.protocol.packet.ingame.server.entity.spawn.ServerSpawnWeatherEntityPacket;
import com.nukkitx.math.vector.Vector3f;
-@Translator(packet = ServerSpawnGlobalEntityPacket.class)
-public class JavaSpawnGlobalEntityTranslator extends PacketTranslator {
+@Translator(packet = ServerSpawnWeatherEntityPacket.class)
+public class JavaSpawnWeatherEntityTranslator extends PacketTranslator {
@Override
- public void translate(ServerSpawnGlobalEntityPacket packet, GeyserSession session) {
+ public void translate(ServerSpawnWeatherEntityPacket packet, GeyserSession session) {
Vector3f position = Vector3f.from(packet.getX(), packet.getY(), packet.getZ());
- // Currently GlobalEntityType only has a lightning bolt
+ // Currently WeatherEntityType only has a lightning bolt
Entity entity = new Entity(
packet.getEntityId(), session.getEntityCache().getNextEntityId().incrementAndGet(),
EntityType.LIGHTNING_BOLT, position, Vector3f.ZERO, Vector3f.ZERO
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
index 8162b82a..93cfa08e 100644
--- 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
@@ -26,7 +26,6 @@
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;
@@ -37,9 +36,7 @@ public class JavaCloseWindowTranslator extends PacketTranslator {
@@ -41,7 +38,7 @@ public class JavaConfirmTransactionTranslator extends PacketTranslator {
@@ -49,17 +46,17 @@ public class JavaOpenWindowTranslator extends PacketTranslator InventoryUtils.openInventory(session, newInventory), 500, TimeUnit.MILLISECONDS);
+ session.getInventoryCache().setOpenInventory(newInventory);
+ //The new window will be opened when the bedrock client sends the
+ //window close confirmation in BedrockContainerCloseTranslator
return;
}
}
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 6fafa2a4..19d7db21 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
@@ -29,7 +29,6 @@ import com.github.steveice10.mc.protocol.packet.ingame.server.window.ServerSetSl
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.Translators;
import org.geysermc.connector.network.translators.inventory.InventoryTranslator;
import org.geysermc.connector.network.translators.Translator;
import org.geysermc.connector.utils.InventoryUtils;
@@ -42,8 +41,6 @@ public class JavaSetSlotTranslator extends PacketTranslator
@Override
public void translate(ServerSetSlotPacket packet, GeyserSession session) {
if (packet.getWindowId() == 255 && packet.getSlot() == -1) { //cursor
- if (Objects.equals(session.getInventory().getCursor(), packet.getItem()))
- return;
if (session.getCraftSlot() != 0)
return;
@@ -56,7 +53,7 @@ public class JavaSetSlotTranslator extends PacketTranslator
if (inventory == null || (packet.getWindowId() != 0 && inventory.getWindowType() == null))
return;
- InventoryTranslator translator = Translators.getInventoryTranslators().get(inventory.getWindowType());
+ InventoryTranslator translator = InventoryTranslator.INVENTORY_TRANSLATORS.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 eab57a64..2cc392f5 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
@@ -30,7 +30,6 @@ 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 java.util.Arrays;
@@ -50,7 +49,7 @@ public class JavaWindowItemsTranslator extends PacketTranslator {
+
+ @Override
+ public void translate(ServerBlockBreakAnimPacket packet, GeyserSession session) {
+ BlockState state = session.getConnector().getWorldManager().getBlockAt(session, packet.getPosition().getX(), packet.getPosition().getY(), packet.getPosition().getZ());
+ int breakTime = (int) (65535 / Math.ceil(BlockUtils.getBreakTime(BlockTranslator.JAVA_RUNTIME_ID_TO_HARDNESS.get(state.getId()), state.getId(), ItemEntry.AIR, new CompoundTag(""), null) * 20));
+ LevelEventPacket levelEventPacket = new LevelEventPacket();
+ levelEventPacket.setPosition(Vector3f.from(
+ packet.getPosition().getX(),
+ packet.getPosition().getY(),
+ packet.getPosition().getZ()
+ ));
+ levelEventPacket.setType(LevelEventType.BLOCK_START_BREAK);
+
+ switch (packet.getStage()) {
+ case STAGE_1:
+ levelEventPacket.setData(breakTime);
+ break;
+ case STAGE_2:
+ levelEventPacket.setData(breakTime * 2);
+ break;
+ case STAGE_3:
+ levelEventPacket.setData(breakTime * 3);
+ break;
+ case STAGE_4:
+ levelEventPacket.setData(breakTime * 4);
+ break;
+ case STAGE_5:
+ levelEventPacket.setData(breakTime * 5);
+ break;
+ case STAGE_6:
+ levelEventPacket.setData(breakTime * 6);
+ break;
+ case STAGE_7:
+ levelEventPacket.setData(breakTime * 7);
+ break;
+ case STAGE_8:
+ levelEventPacket.setData(breakTime * 8);
+ break;
+ case STAGE_9:
+ levelEventPacket.setData(breakTime * 9);
+ break;
+ case STAGE_10:
+ levelEventPacket.setData(breakTime * 10);
+ break;
+ case RESET:
+ levelEventPacket.setType(LevelEventType.BLOCK_STOP_BREAK);
+ levelEventPacket.setData(0);
+ break;
+ }
+ session.sendUpstreamPacket(levelEventPacket);
+ }
+}
diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/java/world/JavaBlockChangeTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/java/world/JavaBlockChangeTranslator.java
index b79525a9..e09b9248 100644
--- a/connector/src/main/java/org/geysermc/connector/network/translators/java/world/JavaBlockChangeTranslator.java
+++ b/connector/src/main/java/org/geysermc/connector/network/translators/java/world/JavaBlockChangeTranslator.java
@@ -25,9 +25,16 @@
package org.geysermc.connector.network.translators.java.world;
+import com.github.steveice10.mc.protocol.data.game.entity.metadata.Position;
+import com.nukkitx.math.vector.Vector3i;
+import com.nukkitx.protocol.bedrock.data.SoundEvent;
+import com.nukkitx.protocol.bedrock.packet.LevelSoundEventPacket;
+import org.geysermc.common.PlatformType;
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.sound.BlockSoundInteractionHandler;
+import org.geysermc.connector.network.translators.world.block.BlockTranslator;
import org.geysermc.connector.utils.ChunkUtils;
import com.github.steveice10.mc.protocol.packet.ingame.server.world.ServerBlockChangePacket;
@@ -37,6 +44,65 @@ public class JavaBlockChangeTranslator extends PacketTranslator {
@@ -47,11 +53,100 @@ public class JavaBlockValueTranslator extends PacketTranslator 0 ? 1 : 0);
- session.getUpstream().sendPacket(blockEventPacket);
+ session.sendUpstreamPacket(blockEventPacket);
}
if (packet.getValue() instanceof EndGatewayValue) {
blockEventPacket.setEventType(1);
- session.getUpstream().sendPacket(blockEventPacket);
+ session.sendUpstreamPacket(blockEventPacket);
+ }
+ if (packet.getValue() instanceof NoteBlockValue) {
+ NoteblockBlockEntityTranslator.translate(session, packet.getPosition());
+ return;
+ }
+ if (packet.getValue() instanceof PistonValue) {
+ PistonValueType type = (PistonValueType) packet.getType();
+
+ // Unlike everything else, pistons need a block entity packet to convey motion
+ // TODO: Doesn't register on chunk load; needs to be interacted with first
+ Vector3i position = Vector3i.from(packet.getPosition().getX(), packet.getPosition().getY(), packet.getPosition().getZ());
+ if (type == PistonValueType.PUSHING) {
+ extendPiston(session, position, 0.0f, 0.0f);
+ } else {
+ retractPiston(session, position, 1.0f, 1.0f);
+ }
+ }
+ if (packet.getValue() instanceof BeaconValue) {
+ blockEventPacket.setEventType(1);
+ session.sendUpstreamPacket(blockEventPacket);
+ }
+ if (packet.getValue() instanceof MobSpawnerValue) {
+ blockEventPacket.setEventType(1);
+ session.sendUpstreamPacket(blockEventPacket);
+ }
+ if (packet.getValue() instanceof EndGatewayValue) {
+ blockEventPacket.setEventType(1);
+ session.sendUpstreamPacket(blockEventPacket);
}
}
+
+ /**
+ * Emulating a piston extending
+ * @param session GeyserSession
+ * @param position Block position
+ * @param progress How far the piston is
+ * @param lastProgress How far the piston last was
+ */
+ private void extendPiston(GeyserSession session, Vector3i position, float progress, float lastProgress) {
+ BlockEntityDataPacket blockEntityDataPacket = new BlockEntityDataPacket();
+ blockEntityDataPacket.setBlockPosition(position);
+ byte state = (byte) ((progress == 1.0f && lastProgress == 1.0f) ? 2 : 1);
+ blockEntityDataPacket.setData(buildPistonTag(position, progress, lastProgress, state));
+ session.sendUpstreamPacket(blockEntityDataPacket);
+ if (lastProgress != 1.0f) {
+ session.getConnector().getGeneralThreadPool().schedule(() ->
+ extendPiston(session, position, (progress >= 1.0f) ? 1.0f : progress + 0.5f, progress),
+ 20, TimeUnit.MILLISECONDS);
+ }
+ }
+
+ /**
+ * Emulate a piston retracting.
+ * @param session GeyserSession
+ * @param position Block position
+ * @param progress Current progress of piston
+ * @param lastProgress Last progress of piston
+ */
+ private void retractPiston(GeyserSession session, Vector3i position, float progress, float lastProgress) {
+ BlockEntityDataPacket blockEntityDataPacket = new BlockEntityDataPacket();
+ blockEntityDataPacket.setBlockPosition(position);
+ byte state = (byte) ((progress == 0.0f && lastProgress == 0.0f) ? 0 : 3);
+ blockEntityDataPacket.setData(buildPistonTag(position, progress, lastProgress, state));
+ session.sendUpstreamPacket(blockEntityDataPacket);
+ if (lastProgress != 0.0f) {
+ session.getConnector().getGeneralThreadPool().schedule(() ->
+ retractPiston(session, position, (progress <= 0.0f) ? 0.0f : progress - 0.5f, progress),
+ 20, TimeUnit.MILLISECONDS);
+ }
+ }
+
+ /**
+ * Build a piston tag
+ * @param position Piston position
+ * @param progress Current progress of piston
+ * @param lastProgress Last progress of piston
+ * @param state
+ * @return Bedrock CompoundTag of piston
+ */
+ private CompoundTag buildPistonTag(Vector3i position, float progress, float lastProgress, byte state) {
+ CompoundTagBuilder builder = CompoundTag.EMPTY.toBuilder();
+ builder.intTag("x", position.getX())
+ .intTag("y", position.getY())
+ .intTag("z", position.getZ())
+ .floatTag("Progress", progress)
+ .floatTag("LastProgress", lastProgress)
+ .stringTag("id", "PistonArm")
+ .byteTag("NewState", state)
+ .byteTag("State", state);
+ return builder.buildRootTag();
+ }
}
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 e72038c5..2af7bb5b 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
@@ -44,9 +44,7 @@ import org.geysermc.connector.network.translators.BiomeTranslator;
import org.geysermc.connector.network.translators.PacketTranslator;
import org.geysermc.connector.network.translators.Translator;
import org.geysermc.connector.utils.ChunkUtils;
-import org.geysermc.connector.world.chunk.ChunkSection;
-
-import java.util.Map;
+import org.geysermc.connector.network.translators.world.chunk.ChunkSection;
@Translator(packet = ServerChunkDataPacket.class)
public class JavaChunkDataTranslator extends PacketTranslator {
@@ -100,7 +98,7 @@ public class JavaChunkDataTranslator extends PacketTranslator blockEntityEntry : chunkData.getLoadBlockEntitiesLater().object2IntEntrySet()) {
@@ -110,7 +108,7 @@ public class JavaChunkDataTranslator extends PacketTranslator {
+
+ @Override
+ public void translate(ServerExplosionPacket packet, GeyserSession session) {
+ for (ExplodedBlockRecord record : packet.getExploded()) {
+ Vector3f pos = Vector3f.from(packet.getX() + record.getX(), packet.getY() + record.getY(), packet.getZ() + record.getZ());
+ ChunkUtils.updateBlock(session, BlockTranslator.AIR, pos.toInt());
+ }
+
+ Vector3f pos = Vector3f.from(packet.getX(), packet.getY(), packet.getZ());
+ // Since bedrock does not play an explosion sound and particles sound, we have to manually do so
+ LevelEventPacket levelEventPacket = new LevelEventPacket();
+ levelEventPacket.setType(packet.getRadius() >= 2.0f ? LevelEventType.PARTICLE_HUGE_EXPLODE : LevelEventType.PARTICLE_LARGE_EXPLOSION);
+ levelEventPacket.setData(0);
+ levelEventPacket.setPosition(pos.toFloat());
+ session.sendUpstreamPacket(levelEventPacket);
+
+ LevelSoundEventPacket levelSoundEventPacket = new LevelSoundEventPacket();
+ levelSoundEventPacket.setRelativeVolumeDisabled(false);
+ levelSoundEventPacket.setBabySound(false);
+ levelSoundEventPacket.setExtraData(-1);
+ levelSoundEventPacket.setSound(SoundEvent.EXPLODE);
+ levelSoundEventPacket.setIdentifier(":");
+ levelSoundEventPacket.setPosition(Vector3f.from(packet.getX(), packet.getY(), packet.getZ()));
+ session.sendUpstreamPacket(levelSoundEventPacket);
+ }
+}
diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/java/world/JavaMapDataTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/java/world/JavaMapDataTranslator.java
index 78681f8f..c8be3a56 100644
--- a/connector/src/main/java/org/geysermc/connector/network/translators/java/world/JavaMapDataTranslator.java
+++ b/connector/src/main/java/org/geysermc/connector/network/translators/java/world/JavaMapDataTranslator.java
@@ -26,11 +26,15 @@
package org.geysermc.connector.network.translators.java.world;
import com.github.steveice10.mc.protocol.data.game.world.map.MapData;
+import com.github.steveice10.mc.protocol.data.game.world.map.MapIcon;
import com.github.steveice10.mc.protocol.packet.ingame.server.world.ServerMapDataPacket;
+import com.nukkitx.protocol.bedrock.data.MapDecoration;
+import com.nukkitx.protocol.bedrock.data.MapTrackedObject;
import com.nukkitx.protocol.bedrock.packet.ClientboundMapItemDataPacket;
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.BedrockMapIcon;
import org.geysermc.connector.utils.MapColor;
@Translator(packet = ServerMapDataPacket.class)
@@ -62,6 +66,16 @@ public class JavaMapDataTranslator extends PacketTranslator
mapItemDataPacket.setColors(colors);
}
+ // Bedrock needs an entity id to display an icon
+ int id = 0;
+ for (MapIcon icon : packet.getIcons()) {
+ BedrockMapIcon bedrockMapIcon = BedrockMapIcon.fromType(icon.getIconType());
+
+ mapItemDataPacket.getTrackedObjects().add(new MapTrackedObject(id));
+ mapItemDataPacket.getDecorations().add(new MapDecoration(bedrockMapIcon.getIconID(), icon.getIconRotation(), icon.getCenterX(), icon.getCenterZ(), "", bedrockMapIcon.toARGB()));
+ id++;
+ }
+
session.getUpstream().getSession().sendPacket(mapItemDataPacket);
}
}
diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/java/world/JavaNotifyClientTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/java/world/JavaNotifyClientTranslator.java
index 3c11d87b..781018b2 100644
--- a/connector/src/main/java/org/geysermc/connector/network/translators/java/world/JavaNotifyClientTranslator.java
+++ b/connector/src/main/java/org/geysermc/connector/network/translators/java/world/JavaNotifyClientTranslator.java
@@ -31,10 +31,7 @@ import com.github.steveice10.mc.protocol.data.game.world.notify.EnterCreditsValu
import com.github.steveice10.mc.protocol.packet.ingame.client.ClientRequestPacket;
import com.github.steveice10.mc.protocol.packet.ingame.server.world.ServerNotifyClientPacket;
import com.nukkitx.math.vector.Vector3f;
-import com.nukkitx.protocol.bedrock.data.EntityDataMap;
-import com.nukkitx.protocol.bedrock.data.EntityFlag;
-import com.nukkitx.protocol.bedrock.data.LevelEventType;
-import com.nukkitx.protocol.bedrock.data.PlayerPermission;
+import com.nukkitx.protocol.bedrock.data.*;
import com.nukkitx.protocol.bedrock.packet.*;
import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet;
import org.geysermc.connector.entity.Entity;
@@ -45,6 +42,7 @@ import org.geysermc.connector.network.translators.inventory.PlayerInventoryTrans
import java.util.Set;
import java.util.concurrent.ThreadLocalRandom;
+import java.util.concurrent.TimeUnit;
@Translator(packet = ServerNotifyClientPacket.class)
public class JavaNotifyClientTranslator extends PacketTranslator {
@@ -61,14 +59,14 @@ public class JavaNotifyClientTranslator extends PacketTranslator playerFlags = new ObjectOpenHashSet<>();
@@ -83,47 +81,57 @@ public class JavaNotifyClientTranslator extends PacketTranslator {
+ AdventureSettingsPacket adventureSettingsPacket = new AdventureSettingsPacket();
+ adventureSettingsPacket.setPlayerPermission(PlayerPermission.MEMBER);
+ adventureSettingsPacket.setCommandPermission(CommandPermission.NORMAL);
+ adventureSettingsPacket.setUniqueEntityId(entity.getGeyserId());
+ adventureSettingsPacket.getFlags().addAll(playerFlags);
+ session.sendUpstreamPacket(adventureSettingsPacket);
+ }, 50, TimeUnit.MILLISECONDS);
EntityDataMap metadata = entity.getMetadata();
- metadata.getFlags().setFlag(EntityFlag.CAN_FLY, gameMode == GameMode.CREATIVE || gameMode == GameMode.SPECTATOR);
+ metadata.getFlags().setFlag(EntityFlag.CAN_FLY, gameMode == GameMode.CREATIVE);
SetEntityDataPacket entityDataPacket = new SetEntityDataPacket();
entityDataPacket.setRuntimeEntityId(entity.getGeyserId());
entityDataPacket.getMetadata().putAll(metadata);
- session.getUpstream().sendPacket(entityDataPacket);
+ session.sendUpstreamPacket(entityDataPacket);
// Update the crafting grid to add/remove barriers for creative inventory
PlayerInventoryTranslator.updateCraftingGrid(session, session.getInventory());
-
break;
case ENTER_CREDITS:
switch ((EnterCreditsValue) packet.getValue()) {
case SEEN_BEFORE:
ClientRequestPacket javaRespawnPacket = new ClientRequestPacket(ClientRequest.RESPAWN);
- session.getDownstream().getSession().send(javaRespawnPacket);
+ session.sendDownstreamPacket(javaRespawnPacket);
break;
case FIRST_TIME:
ShowCreditsPacket showCreditsPacket = new ShowCreditsPacket();
showCreditsPacket.setStatus(ShowCreditsPacket.Status.START_CREDITS);
showCreditsPacket.setRuntimeEntityId(entity.getGeyserId());
- session.getUpstream().sendPacket(showCreditsPacket);
+ session.sendUpstreamPacket(showCreditsPacket);
break;
}
break;
+ case AFFECTED_BY_ELDER_GUARDIAN:
+ EntityEventPacket eventPacket = new EntityEventPacket();
+ eventPacket.setType(EntityEventType.ELDER_GUARDIAN_CURSE);
+ eventPacket.setData(0);
+ eventPacket.setRuntimeEntityId(entity.getGeyserId());
+ session.sendUpstreamPacket(eventPacket);
default:
break;
}
diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/java/world/JavaPlayBuiltinSoundTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/java/world/JavaPlayBuiltinSoundTranslator.java
new file mode 100644
index 00000000..f51e35fe
--- /dev/null
+++ b/connector/src/main/java/org/geysermc/connector/network/translators/java/world/JavaPlayBuiltinSoundTranslator.java
@@ -0,0 +1,95 @@
+/*
+ * 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.ServerPlayBuiltinSoundPacket;
+import com.nukkitx.math.vector.Vector3f;
+import com.nukkitx.protocol.bedrock.data.LevelEventType;
+import com.nukkitx.protocol.bedrock.data.SoundEvent;
+import com.nukkitx.protocol.bedrock.packet.LevelEventPacket;
+import com.nukkitx.protocol.bedrock.packet.LevelSoundEventPacket;
+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.world.block.BlockTranslator;
+import org.geysermc.connector.network.translators.sound.SoundRegistry;
+
+@Translator(packet = ServerPlayBuiltinSoundPacket.class)
+public class JavaPlayBuiltinSoundTranslator extends PacketTranslator {
+
+ @Override
+ public void translate(ServerPlayBuiltinSoundPacket packet, GeyserSession session) {
+ String packetSound = packet.getSound().getName();
+
+ SoundRegistry.SoundMapping soundMapping = SoundRegistry.fromJava(packetSound);
+ if (soundMapping == null) {
+ session.getConnector().getLogger().debug("[Builtin] Sound mapping " + packetSound + " not found - " + packet.toString());
+ return;
+ }
+
+ if (soundMapping.isLevelEvent()) {
+ LevelEventPacket levelEventPacket = new LevelEventPacket();
+ levelEventPacket.setPosition(Vector3f.from(packet.getX(), packet.getY(), packet.getZ()));
+ levelEventPacket.setData(0);
+ levelEventPacket.setType(LevelEventType.valueOf(soundMapping.getBedrock()));
+ session.sendUpstreamPacket(levelEventPacket);
+ return;
+ }
+ LevelSoundEventPacket soundPacket = new LevelSoundEventPacket();
+ SoundEvent sound = SoundRegistry.toSoundEvent(soundMapping.getBedrock());
+ if (sound == null) {
+ sound = SoundRegistry.toSoundEvent(soundMapping.getBedrock());
+ }
+ if (sound == null) {
+ sound = SoundRegistry.toSoundEvent(packetSound);
+ }
+ if (sound == null) {
+ session.getConnector().getLogger().debug("[Builtin] Sound for original " + packetSound + " to mappings " + soundPacket
+ + " was not a playable level sound, or has yet to be mapped to an enum in "
+ + "NukkitX SoundEvent ");
+
+ }
+ soundPacket.setSound(sound);
+ soundPacket.setPosition(Vector3f.from(packet.getX(), packet.getY(), packet.getZ()));
+ soundPacket.setIdentifier(soundMapping.getIdentifier());
+ if (sound == SoundEvent.NOTE) {
+ // Minecraft Wiki: 2^(x/12) = Java pitch where x is -12 to 12
+ // Java sends the note value as above starting with -12 and ending at 12
+ // Bedrock has a number for each type of note, then proceeds up the scale by adding to that number
+ soundPacket.setExtraData(soundMapping.getExtraData() + (int)(Math.round((Math.log10(packet.getPitch()) / Math.log10(2)) * 12)) + 12);
+ } else if (sound == SoundEvent.PLACE && soundMapping.getExtraData() == -1) {
+ soundPacket.setExtraData(BlockTranslator.getBedrockBlockId(BlockTranslator.getJavaBlockState(soundMapping.getIdentifier())));
+ soundPacket.setIdentifier(":");
+ } else {
+ soundPacket.setExtraData(soundMapping.getExtraData());
+ }
+
+
+ soundPacket.setBabySound(false); // might need to adjust this in the future
+ soundPacket.setRelativeVolumeDisabled(false);
+ session.sendUpstreamPacket(soundPacket);
+ }
+}
diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/java/world/JavaPlayEffectTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/java/world/JavaPlayEffectTranslator.java
new file mode 100644
index 00000000..da23adb5
--- /dev/null
+++ b/connector/src/main/java/org/geysermc/connector/network/translators/java/world/JavaPlayEffectTranslator.java
@@ -0,0 +1,166 @@
+/*
+ * 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.effect.ParticleEffect;
+import com.github.steveice10.mc.protocol.data.game.world.effect.*;
+import com.github.steveice10.mc.protocol.packet.ingame.server.world.ServerPlayEffectPacket;
+import com.nukkitx.math.vector.Vector3f;
+import com.nukkitx.protocol.bedrock.data.LevelEventType;
+import com.nukkitx.protocol.bedrock.data.SoundEvent;
+import com.nukkitx.protocol.bedrock.packet.LevelEventPacket;
+import com.nukkitx.protocol.bedrock.packet.LevelSoundEventPacket;
+import com.nukkitx.protocol.bedrock.packet.TextPacket;
+import org.geysermc.connector.GeyserConnector;
+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.world.block.BlockTranslator;
+import org.geysermc.connector.network.translators.effect.Effect;
+import org.geysermc.connector.network.translators.effect.EffectRegistry;
+import org.geysermc.connector.utils.LocaleUtils;
+
+import java.util.ArrayList;
+import java.util.List;
+
+@Translator(packet = ServerPlayEffectPacket.class)
+public class JavaPlayEffectTranslator extends PacketTranslator {
+
+ @Override
+ public void translate(ServerPlayEffectPacket packet, GeyserSession session) {
+ LevelEventPacket effect = new LevelEventPacket();
+ // Some things here are particles, others are not
+ if (packet.getEffect() instanceof ParticleEffect) {
+ ParticleEffect particleEffect = (ParticleEffect) packet.getEffect();
+ Effect geyserEffect = EffectRegistry.EFFECTS.get(particleEffect.name());
+ if (geyserEffect != null) {
+ String name = geyserEffect.getBedrockName();
+ effect.setType(LevelEventType.valueOf(name));
+ } else {
+ switch (particleEffect) {
+ // TODO: BREAK_SPLASH_POTION has additional data
+ case BONEMEAL_GROW:
+ effect.setType(LevelEventType.BONEMEAL);
+ BonemealGrowEffectData growEffectData = (BonemealGrowEffectData) packet.getData();
+ effect.setData(growEffectData.getParticleCount());
+ break;
+ //TODO: Block break particles when under fire
+ case BREAK_BLOCK:
+ effect.setType(LevelEventType.DESTROY);
+ BreakBlockEffectData breakBlockEffectData = (BreakBlockEffectData) packet.getData();
+ effect.setData(BlockTranslator.getBedrockBlockId(breakBlockEffectData.getBlockState()));
+ break;
+ case EXPLOSION:
+ effect.setType(LevelEventType.PARTICLE_LARGE_EXPLOSION);
+ break;
+ case MOB_SPAWN:
+ effect.setType(LevelEventType.ENTITY_SPAWN);
+ break;
+ // Done with a dispenser
+ case SMOKE:
+ // Might need to be SHOOT
+ effect.setType(LevelEventType.PARTICLE_SMOKE);
+ break;
+ case COMPOSTER:
+ effect.setType(LevelEventType.BONEMEAL);
+
+ ComposterEffectData composterEffectData = (ComposterEffectData) packet.getData();
+ LevelSoundEventPacket soundEvent = new LevelSoundEventPacket();
+ soundEvent.setSound(SoundEvent.valueOf("COMPOSTER_" + composterEffectData.name()));
+ soundEvent.setPosition(Vector3f.from(packet.getPosition().getX(), packet.getPosition().getY(), packet.getPosition().getZ()));
+ soundEvent.setIdentifier(":");
+ soundEvent.setExtraData(-1);
+ soundEvent.setBabySound(false);
+ soundEvent.setRelativeVolumeDisabled(false);
+ session.sendUpstreamPacket(soundEvent);
+ break;
+ case BLOCK_LAVA_EXTINGUISH:
+ effect.setType(LevelEventType.SHOOT);
+ effect.setPosition(Vector3f.from(packet.getPosition().getX(), packet.getPosition().getY() + 1, packet.getPosition().getZ()));
+ session.sendUpstreamPacket(effect);
+
+ LevelSoundEventPacket soundEventPacket = new LevelSoundEventPacket();
+ soundEventPacket.setSound(SoundEvent.EXTINGUISH_FIRE);
+ soundEventPacket.setPosition(Vector3f.from(packet.getPosition().getX(), packet.getPosition().getY(), packet.getPosition().getZ()));
+ soundEventPacket.setIdentifier(":");
+ soundEventPacket.setExtraData(-1);
+ soundEventPacket.setBabySound(false);
+ soundEventPacket.setRelativeVolumeDisabled(false);
+ session.sendUpstreamPacket(soundEventPacket);
+ return;
+ default:
+ GeyserConnector.getInstance().getLogger().debug("No effect handling for particle effect: " + packet.getEffect());
+ }
+ }
+ effect.setPosition(Vector3f.from(packet.getPosition().getX(), packet.getPosition().getY(), packet.getPosition().getZ()));
+ session.sendUpstreamPacket(effect);
+ } else if (packet.getEffect() instanceof SoundEffect) {
+ SoundEffect soundEffect = (SoundEffect) packet.getEffect();
+ Effect geyserEffect = EffectRegistry.EFFECTS.get(soundEffect.name());
+ if (geyserEffect != null) {
+ // Some events are LevelEventTypes, some are SoundEvents.
+ if (geyserEffect.getType().equals("soundLevel")) {
+ effect.setType(LevelEventType.valueOf(geyserEffect.getBedrockName()));
+ } else if (geyserEffect.getType().equals("soundEvent")) {
+ LevelSoundEventPacket soundEvent = new LevelSoundEventPacket();
+ // Separate case since each RecordEffectData in Java is an individual track in Bedrock
+ if (geyserEffect.getJavaName().equals("RECORD")) {
+ RecordEffectData recordEffectData = (RecordEffectData) packet.getData();
+ soundEvent.setSound(EffectRegistry.RECORDS.get(recordEffectData.getRecordId()));
+ if (EffectRegistry.RECORDS.get(recordEffectData.getRecordId()) != SoundEvent.STOP_RECORD) {
+ // Send text packet as it seems to be handled in Java Edition client-side.
+ TextPacket textPacket = new TextPacket();
+ textPacket.setType(TextPacket.Type.JUKEBOX_POPUP);
+ textPacket.setNeedsTranslation(true);
+ textPacket.setXuid("");
+ textPacket.setPlatformChatId("");
+ textPacket.setSourceName(null);
+ textPacket.setMessage("record.nowPlaying");
+ List params = new ArrayList<>();
+ String recordString = "%item." + EffectRegistry.RECORDS.get(recordEffectData.getRecordId()).name().toLowerCase() + ".desc";
+ params.add(LocaleUtils.getLocaleString(recordString, session.getClientData().getLanguageCode()));
+ textPacket.setParameters(params);
+ session.sendUpstreamPacket(textPacket);
+ }
+ } else {
+ soundEvent.setSound(SoundEvent.valueOf(geyserEffect.getBedrockName()));
+ }
+ soundEvent.setExtraData(geyserEffect.getData());
+ soundEvent.setIdentifier(geyserEffect.getIdentifier());
+ soundEvent.setPosition(Vector3f.from(packet.getPosition().getX(), packet.getPosition().getY(), packet.getPosition().getZ()));
+ session.sendUpstreamPacket(soundEvent);
+ }
+ } else {
+ GeyserConnector.getInstance().getLogger().debug("No effect handling for sound effect: " + packet.getEffect());
+ }
+ }
+ if (effect.getType() != null) {
+ effect.setPosition(Vector3f.from(packet.getPosition().getX(), packet.getPosition().getY(), packet.getPosition().getZ()));
+ session.sendUpstreamPacket(effect);
+ }
+
+ }
+}
\ No newline at end of file
diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/java/world/JavaPlayerPlaySoundTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/java/world/JavaPlayerPlaySoundTranslator.java
new file mode 100644
index 00000000..c99de3e3
--- /dev/null
+++ b/connector/src/main/java/org/geysermc/connector/network/translators/java/world/JavaPlayerPlaySoundTranslator.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.java.world;
+
+import com.github.steveice10.mc.protocol.data.game.world.sound.BuiltinSound;
+import com.github.steveice10.mc.protocol.data.game.world.sound.CustomSound;
+import com.github.steveice10.mc.protocol.packet.ingame.server.world.ServerPlaySoundPacket;
+import com.nukkitx.math.vector.Vector3f;
+import com.nukkitx.protocol.bedrock.packet.*;
+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.sound.SoundRegistry;
+
+@Translator(packet = ServerPlaySoundPacket.class)
+public class JavaPlayerPlaySoundTranslator extends PacketTranslator {
+
+ @Override
+ public void translate(ServerPlaySoundPacket packet, GeyserSession session) {
+ String packetSound;
+ if(packet.getSound() instanceof BuiltinSound) {
+ packetSound = ((BuiltinSound) packet.getSound()).getName();
+ } else if(packet.getSound() instanceof CustomSound) {
+ packetSound = ((CustomSound) packet.getSound()).getName();
+ } else {
+ session.getConnector().getLogger().debug("Unknown sound packet, we were unable to map this. " + packet.toString());
+ return;
+ }
+
+ SoundRegistry.SoundMapping soundMapping = SoundRegistry.fromJava(packetSound.replace("minecraft:", ""));
+ String playsound;
+ if(soundMapping == null || soundMapping.getPlaysound() == null) {
+ // no mapping
+ session.getConnector().getLogger()
+ .debug("[PlaySound] Defaulting to sound server gave us for " + packet.toString());
+ playsound = packetSound.replace("minecraft:", "");
+ } else {
+ playsound = soundMapping.getPlaysound();
+ }
+
+ PlaySoundPacket playSoundPacket = new PlaySoundPacket();
+ playSoundPacket.setSound(playsound);
+ playSoundPacket.setPosition(Vector3f.from(packet.getX(), packet.getY(), packet.getZ()));
+ playSoundPacket.setVolume(packet.getVolume());
+ playSoundPacket.setPitch(packet.getPitch());
+
+ session.sendUpstreamPacket(playSoundPacket);
+ }
+}
diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/java/world/JavaSpawnParticleTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/java/world/JavaSpawnParticleTranslator.java
new file mode 100644
index 00000000..63512047
--- /dev/null
+++ b/connector/src/main/java/org/geysermc/connector/network/translators/java/world/JavaSpawnParticleTranslator.java
@@ -0,0 +1,105 @@
+/*
+ * 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.entity.metadata.ItemStack;
+import com.github.steveice10.mc.protocol.data.game.world.particle.*;
+import com.nukkitx.protocol.bedrock.data.ItemData;
+import com.nukkitx.protocol.bedrock.data.LevelEventType;
+import com.nukkitx.protocol.bedrock.packet.LevelEventPacket;
+import com.nukkitx.protocol.bedrock.packet.SpawnParticleEffectPacket;
+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.item.ItemTranslator;
+import org.geysermc.connector.network.translators.world.block.BlockTranslator;
+
+import com.github.steveice10.mc.protocol.packet.ingame.server.world.ServerSpawnParticlePacket;
+import com.nukkitx.math.vector.Vector3f;
+import org.geysermc.connector.network.translators.effect.EffectRegistry;
+
+@Translator(packet = ServerSpawnParticlePacket.class)
+public class JavaSpawnParticleTranslator extends PacketTranslator {
+
+ @Override
+ public void translate(ServerSpawnParticlePacket packet, GeyserSession session) {
+ LevelEventPacket particle = new LevelEventPacket();
+ switch (packet.getParticle().getType()) {
+ case BLOCK:
+ particle.setType(LevelEventType.DESTROY);
+ particle.setPosition(Vector3f.from(packet.getX(), packet.getY(), packet.getZ()));
+ particle.setData(BlockTranslator.getBedrockBlockId(((BlockParticleData) packet.getParticle().getData()).getBlockState()));
+ session.sendUpstreamPacket(particle);
+ break;
+ case FALLING_DUST:
+ //In fact, FallingDustParticle should have data like DustParticle,
+ //but in MCProtocol, its data is BlockState(1).
+ particle.setType(LevelEventType.PARTICLE_FALLING_DUST);
+ particle.setData(BlockTranslator.getBedrockBlockId(((FallingDustParticleData)packet.getParticle().getData()).getBlockState()));
+ particle.setPosition(Vector3f.from(packet.getX(), packet.getY(), packet.getZ()));
+ session.sendUpstreamPacket(particle);
+ break;
+ case ITEM:
+ ItemStack javaItem = ((ItemParticleData)packet.getParticle().getData()).getItemStack();
+ ItemData bedrockItem = ItemTranslator.translateToBedrock(session, javaItem);
+ int id = bedrockItem.getId();
+ short damage = bedrockItem.getDamage();
+ particle.setType(LevelEventType.PARTICLE_ITEM_BREAK);
+ particle.setData(id << 16 | damage);
+ particle.setPosition(Vector3f.from(packet.getX(), packet.getY(), packet.getZ()));
+ session.sendUpstreamPacket(particle);
+ break;
+ case DUST:
+ DustParticleData data = (DustParticleData)packet.getParticle().getData();
+ int r = (int) (data.getRed()*255);
+ int g = (int) (data.getGreen()*255);
+ int b = (int) (data.getBlue()*255);
+ particle.setType(LevelEventType.PARTICLE_FALLING_DUST);
+ particle.setData(((0xff) << 24) | ((r & 0xff) << 16) | ((g & 0xff) << 8) | (b & 0xff));
+ particle.setPosition(Vector3f.from(packet.getX(), packet.getY(), packet.getZ()));
+ session.sendUpstreamPacket(particle);
+ break;
+ default:
+ LevelEventType typeParticle = EffectRegistry.getParticleLevelEventType(packet.getParticle().getType());
+ if (typeParticle != null) {
+ particle.setType(typeParticle);
+ particle.setPosition(Vector3f.from(packet.getX(), packet.getY(), packet.getZ()));
+ session.sendUpstreamPacket(particle);
+ } else {
+ String stringParticle = EffectRegistry.getParticleString(packet.getParticle().getType());
+ if (stringParticle != null) {
+ SpawnParticleEffectPacket stringPacket = new SpawnParticleEffectPacket();
+ stringPacket.setIdentifier(stringParticle);
+ stringPacket.setDimensionId(session.getPlayerEntity().getDimension());
+ stringPacket.setPosition(Vector3f.from(packet.getX(), packet.getY(), packet.getZ()));
+ session.sendUpstreamPacket(stringPacket);
+ }
+ }
+ break;
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/java/world/JavaSpawnPositionTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/java/world/JavaSpawnPositionTranslator.java
index d518a7ae..a59c71ea 100644
--- a/connector/src/main/java/org/geysermc/connector/network/translators/java/world/JavaSpawnPositionTranslator.java
+++ b/connector/src/main/java/org/geysermc/connector/network/translators/java/world/JavaSpawnPositionTranslator.java
@@ -42,6 +42,6 @@ public class JavaSpawnPositionTranslator 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());
@@ -52,11 +46,6 @@ public class JavaUpdateTileEntityTranslator extends PacketTranslator
- 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/network/translators/java/world/JavaUpdateTimeTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/java/world/JavaUpdateTimeTranslator.java
index 36533844..188e960d 100644
--- a/connector/src/main/java/org/geysermc/connector/network/translators/java/world/JavaUpdateTimeTranslator.java
+++ b/connector/src/main/java/org/geysermc/connector/network/translators/java/world/JavaUpdateTimeTranslator.java
@@ -55,7 +55,7 @@ public class JavaUpdateTimeTranslator extends PacketTranslator("dodaylightcycle", doCycle));
- session.getUpstream().sendPacket(gameRulesChangedPacket);
+ session.sendUpstreamPacket(gameRulesChangedPacket);
}
}
diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/java/world/JavaVehicleMoveTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/java/world/JavaVehicleMoveTranslator.java
new file mode 100644
index 00000000..1bcd9919
--- /dev/null
+++ b/connector/src/main/java/org/geysermc/connector/network/translators/java/world/JavaVehicleMoveTranslator.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.world;
+
+import com.github.steveice10.mc.protocol.packet.ingame.server.entity.ServerVehicleMovePacket;
+import com.nukkitx.math.vector.Vector3f;
+import org.geysermc.connector.entity.Entity;
+import org.geysermc.connector.network.session.GeyserSession;
+import org.geysermc.connector.network.translators.PacketTranslator;
+import org.geysermc.connector.network.translators.Translator;
+
+@Translator(packet = ServerVehicleMovePacket.class)
+public class JavaVehicleMoveTranslator extends PacketTranslator {
+
+ @Override
+ public void translate(ServerVehicleMovePacket packet, GeyserSession session) {
+ Entity entity = session.getRidingVehicleEntity();
+ if (entity == null) return;
+
+ entity.moveAbsolute(session, Vector3f.from(packet.getX(), packet.getY(), packet.getZ()), packet.getYaw(), packet.getPitch(), false, false);
+
+ }
+}
diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/sound/BlockSoundInteractionHandler.java b/connector/src/main/java/org/geysermc/connector/network/translators/sound/BlockSoundInteractionHandler.java
new file mode 100644
index 00000000..5a03b218
--- /dev/null
+++ b/connector/src/main/java/org/geysermc/connector/network/translators/sound/BlockSoundInteractionHandler.java
@@ -0,0 +1,87 @@
+/*
+ * 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.sound;
+
+import com.github.steveice10.mc.protocol.data.game.entity.metadata.ItemStack;
+import com.nukkitx.math.vector.Vector3f;
+import org.geysermc.connector.network.session.GeyserSession;
+import org.geysermc.connector.network.translators.item.ItemRegistry;
+
+import java.util.Map;
+
+/**
+ * Sound interaction handler for when a block is right-clicked.
+ */
+public interface BlockSoundInteractionHandler extends SoundInteractionHandler {
+
+ /**
+ * Handles the block interaction when a player
+ * right-clicks a block.
+ *
+ * @param session the session interacting with the block
+ * @param position the position of the block
+ * @param identifier the identifier of the block
+ */
+ static void handleBlockInteraction(GeyserSession session, Vector3f position, String identifier) {
+ for (Map.Entry> interactionEntry : SoundHandlerRegistry.INTERACTION_HANDLERS.entrySet()) {
+ if (!(interactionEntry.getValue() instanceof BlockSoundInteractionHandler)) {
+ continue;
+ }
+ if (interactionEntry.getKey().blocks().length != 0) {
+ boolean contains = false;
+ for (String blockIdentifier : interactionEntry.getKey().blocks()) {
+ if (identifier.contains(blockIdentifier)) {
+ contains = true;
+ break;
+ }
+ }
+ if (!contains) continue;
+ }
+ ItemStack itemInHand = session.getInventory().getItemInHand();
+ if (interactionEntry.getKey().items().length != 0) {
+ if (itemInHand == null || itemInHand.getId() == 0) {
+ continue;
+ }
+ String handIdentifier = ItemRegistry.getItem(session.getInventory().getItemInHand()).getJavaIdentifier();
+ boolean contains = false;
+ for (String itemIdentifier : interactionEntry.getKey().items()) {
+ if (handIdentifier.contains(itemIdentifier)) {
+ contains = true;
+ break;
+ }
+ }
+ if (!contains) continue;
+ }
+ if (session.isSneaking() && !interactionEntry.getKey().ignoreSneakingWhileHolding()) {
+ if (session.getInventory().getItemInHand() != null && session.getInventory().getItemInHand().getId() != 0) {
+ continue;
+ }
+ }
+ ((BlockSoundInteractionHandler) interactionEntry.getValue()).handleInteraction(session, position, identifier);
+ }
+ }
+}
diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/sound/EntitySoundInteractionHandler.java b/connector/src/main/java/org/geysermc/connector/network/translators/sound/EntitySoundInteractionHandler.java
new file mode 100644
index 00000000..138791b1
--- /dev/null
+++ b/connector/src/main/java/org/geysermc/connector/network/translators/sound/EntitySoundInteractionHandler.java
@@ -0,0 +1,88 @@
+/*
+ * 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.sound;
+
+import com.github.steveice10.mc.protocol.data.game.entity.metadata.ItemStack;
+import com.nukkitx.math.vector.Vector3f;
+import org.geysermc.connector.entity.Entity;
+import org.geysermc.connector.network.session.GeyserSession;
+import org.geysermc.connector.network.translators.item.ItemRegistry;
+
+import java.util.Map;
+
+/**
+ * Sound interaction handler for when an entity is right-clicked.
+ */
+public interface EntitySoundInteractionHandler extends SoundInteractionHandler {
+
+ /**
+ * Handles the block interaction when a player
+ * right-clicks an entity.
+ *
+ * @param session the session interacting with the block
+ * @param position the position of the block
+ * @param entity the entity interacted with
+ */
+ static void handleEntityInteraction(GeyserSession session, Vector3f position, Entity entity) {
+ for (Map.Entry> interactionEntry : SoundHandlerRegistry.INTERACTION_HANDLERS.entrySet()) {
+ if (!(interactionEntry.getValue() instanceof EntitySoundInteractionHandler)) {
+ continue;
+ }
+ if (interactionEntry.getKey().entities().length != 0) {
+ boolean contains = false;
+ for (String entityIdentifier : interactionEntry.getKey().entities()) {
+ if (entity.getEntityType().name().toLowerCase().contains(entityIdentifier)) {
+ contains = true;
+ break;
+ }
+ }
+ if (!contains) continue;
+ }
+ ItemStack itemInHand = session.getInventory().getItemInHand();
+ if (interactionEntry.getKey().items().length != 0) {
+ if (itemInHand == null || itemInHand.getId() == 0) {
+ continue;
+ }
+ String handIdentifier = ItemRegistry.getItem(session.getInventory().getItemInHand()).getJavaIdentifier();
+ boolean contains = false;
+ for (String itemIdentifier : interactionEntry.getKey().items()) {
+ if (handIdentifier.contains(itemIdentifier)) {
+ contains = true;
+ break;
+ }
+ }
+ if (!contains) continue;
+ }
+ if (session.isSneaking() && !interactionEntry.getKey().ignoreSneakingWhileHolding()) {
+ if (session.getInventory().getItemInHand() != null && session.getInventory().getItemInHand().getId() != 0) {
+ continue;
+ }
+ }
+ ((EntitySoundInteractionHandler) interactionEntry.getValue()).handleInteraction(session, position, entity);
+ }
+ }
+}
diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/sound/SoundHandler.java b/connector/src/main/java/org/geysermc/connector/network/translators/sound/SoundHandler.java
new file mode 100644
index 00000000..52a76aa3
--- /dev/null
+++ b/connector/src/main/java/org/geysermc/connector/network/translators/sound/SoundHandler.java
@@ -0,0 +1,78 @@
+/*
+ * 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.sound;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * Marks if a class should be handled as a
+ * {@link SoundInteractionHandler}.
+ */
+@Retention(value = RetentionPolicy.RUNTIME)
+public @interface SoundHandler {
+
+ /**
+ * The identifier(s) that the placed block must contain
+ * one of. Leave empty to ignore.
+ *
+ * Only applies to interaction handlers that are an
+ * instance of {@link BlockSoundInteractionHandler}.
+ *
+ * @return the value the interacted block must contain
+ */
+ String[] blocks() default {};
+
+ /**
+ * The identifier(s) that the player's hand item
+ * must contain one of. Leave empty to ignore.
+ *
+ * @return the value the item in the player's hand must contain
+ */
+ String[] items() default {};
+
+ /**
+ * The identifier(s) that the interacted entity must have.
+ * Leave empty to ignore.
+ *
+ * Only applies to interaction handlers that are an
+ * instance of {@link BlockSoundInteractionHandler}.
+ *
+ * @return the value the item in the player's hand must contain
+ */
+ String[] entities() default {};
+
+ /**
+ * Controls if the interaction should still be
+ * called even if the player is sneaking while
+ * holding something in their hand.
+ *
+ * @return if the interaction should continue when player
+ * is holding something in their hand
+ */
+ boolean ignoreSneakingWhileHolding() default false;
+}
diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/sound/SoundHandlerRegistry.java b/connector/src/main/java/org/geysermc/connector/network/translators/sound/SoundHandlerRegistry.java
new file mode 100644
index 00000000..260efb41
--- /dev/null
+++ b/connector/src/main/java/org/geysermc/connector/network/translators/sound/SoundHandlerRegistry.java
@@ -0,0 +1,69 @@
+/*
+ * 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.sound;
+
+import org.reflections.Reflections;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Registry that holds {@link SoundInteractionHandler}s.
+ */
+public class SoundHandlerRegistry {
+
+ static final Map> INTERACTION_HANDLERS = new HashMap<>();
+
+ static {
+ Reflections ref = new Reflections("org.geysermc.connector.network.translators.sound");
+ for (Class> clazz : ref.getTypesAnnotatedWith(SoundHandler.class)) {
+ try {
+ SoundInteractionHandler> interactionHandler = (SoundInteractionHandler>) clazz.newInstance();
+ SoundHandler annotation = clazz.getAnnotation(SoundHandler.class);
+ INTERACTION_HANDLERS.put(annotation, interactionHandler);
+ } catch (InstantiationException | IllegalAccessException ex) {
+ ex.printStackTrace();
+ }
+ }
+ }
+
+ private SoundHandlerRegistry() {
+ }
+
+ public static void init() {
+ // no-op
+ }
+
+ /**
+ * Returns a map of the interaction handlers
+ *
+ * @return a map of the interaction handlers
+ */
+ public static Map> getInteractionHandlers() {
+ return INTERACTION_HANDLERS;
+ }
+}
diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/sound/SoundInteractionHandler.java b/connector/src/main/java/org/geysermc/connector/network/translators/sound/SoundInteractionHandler.java
new file mode 100644
index 00000000..e68061ef
--- /dev/null
+++ b/connector/src/main/java/org/geysermc/connector/network/translators/sound/SoundInteractionHandler.java
@@ -0,0 +1,53 @@
+/*
+ * 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.sound;
+
+import com.nukkitx.math.vector.Vector3f;
+
+import org.geysermc.connector.network.session.GeyserSession;
+
+/**
+ * Handler for playing sounds when right-clicking
+ * various objects. Due to Minecraft: Bedrock Edition
+ * expecting interaction sounds to be played serverside
+ * and Minecraft: Java Edition handling them clientside,
+ * this had to be made to handle scenarios like that.
+ *
+ * @param the value
+ */
+public interface SoundInteractionHandler {
+
+ /**
+ * Handles the interaction when a player
+ * right-clicks a block.
+ *
+ * @param session the session interacting with the block
+ * @param position the position of the block
+ * @param value the value
+ */
+ void handleInteraction(GeyserSession session, Vector3f position, T value);
+}
diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/sound/SoundRegistry.java b/connector/src/main/java/org/geysermc/connector/network/translators/sound/SoundRegistry.java
new file mode 100644
index 00000000..01b03476
--- /dev/null
+++ b/connector/src/main/java/org/geysermc/connector/network/translators/sound/SoundRegistry.java
@@ -0,0 +1,124 @@
+/*
+ * 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.sound;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import com.nukkitx.protocol.bedrock.data.SoundEvent;
+import lombok.Data;
+import lombok.ToString;
+import org.geysermc.connector.GeyserConnector;
+import org.geysermc.connector.utils.FileUtils;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+
+public class SoundRegistry {
+
+ private static final Map SOUNDS;
+
+ private SoundRegistry() {
+ }
+
+ public static void init() {
+ // no-op
+ }
+
+ static {
+ /* Load sound mappings */
+ InputStream stream = FileUtils.getResource("mappings/sounds.json");
+ JsonNode soundsTree;
+ try {
+ soundsTree = GeyserConnector.JSON_MAPPER.readTree(stream);
+ } catch (IOException e) {
+ throw new AssertionError("Unable to load sound mappings", e);
+ }
+
+ Map soundMappings = new HashMap<>();
+ Iterator> soundsIterator = soundsTree.fields();
+ while(soundsIterator.hasNext()) {
+ Map.Entry next = soundsIterator.next();
+ JsonNode brMap = next.getValue();
+
+ soundMappings.put(next.getKey(), new SoundMapping(
+ next.getKey(),
+ brMap.has("bedrock_mapping") && brMap.get("bedrock_mapping").isTextual() ? brMap.get("bedrock_mapping").asText() : null,
+ brMap.has("playsound_mapping") && brMap.get("playsound_mapping").isTextual() ? brMap.get("playsound_mapping").asText() : null,
+ brMap.has("extra_data") && brMap.get("extra_data").isInt() ? brMap.get("extra_data").asInt() : -1,
+ brMap.has("identifier") && brMap.get("identifier").isTextual() ? brMap.get("identifier").asText() : null,
+ brMap.has("level_event") && brMap.get("level_event").isBoolean() ? brMap.get("level_event").asBoolean() : false
+ )
+ );
+ }
+ SOUNDS = soundMappings;
+ }
+
+ /**
+ * Get's the sound mapping for a Java edition sound identifier
+ * @param java Java edition sound identifier
+ * @return SoundMapping object with information for bedrock, nukkit, java, etc. null if not found
+ */
+ public static SoundMapping fromJava(String java) {
+ return SOUNDS.get(java);
+ }
+
+ /**
+ * Maps a sound name to a sound event, null if one
+ * does not exist.
+ *
+ * @param sound the sound name
+ * @return a sound event from the given sound
+ */
+ public static SoundEvent toSoundEvent(String sound) {
+ try {
+ return SoundEvent.valueOf(sound.toUpperCase().replaceAll("\\.", "_"));
+ } catch (Exception ex) {
+ return null;
+ }
+ }
+
+ @Data
+ @ToString
+ public static class SoundMapping {
+ private final String java;
+ private final String bedrock;
+ private final String playsound;
+ private final int extraData;
+ private String identifier;
+ private boolean levelEvent;
+
+ public SoundMapping(String java, String bedrock, String playsound, int extraData, String identifier, boolean levelEvent) {
+ this.java = java;
+ this.bedrock = bedrock == null || bedrock.equalsIgnoreCase("") ? null : bedrock;
+ this.playsound = playsound == null || playsound.equalsIgnoreCase("") ? null : playsound;
+ this.extraData = extraData;
+ this.identifier = identifier == null || identifier.equalsIgnoreCase("") ? ":" : identifier;
+ this.levelEvent = levelEvent;
+ }
+ }
+}
diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/sound/block/BucketSoundInteractionHandler.java b/connector/src/main/java/org/geysermc/connector/network/translators/sound/block/BucketSoundInteractionHandler.java
new file mode 100644
index 00000000..367f9beb
--- /dev/null
+++ b/connector/src/main/java/org/geysermc/connector/network/translators/sound/block/BucketSoundInteractionHandler.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.sound.block;
+
+import com.nukkitx.math.vector.Vector3f;
+import com.nukkitx.protocol.bedrock.data.SoundEvent;
+import com.nukkitx.protocol.bedrock.packet.LevelSoundEventPacket;
+import org.geysermc.connector.network.session.GeyserSession;
+import org.geysermc.connector.network.translators.item.ItemRegistry;
+import org.geysermc.connector.network.translators.sound.BlockSoundInteractionHandler;
+import org.geysermc.connector.network.translators.sound.SoundHandler;
+
+@SoundHandler(items = "bucket")
+public class BucketSoundInteractionHandler implements BlockSoundInteractionHandler {
+
+ @Override
+ public void handleInteraction(GeyserSession session, Vector3f position, String identifier) {
+ String handItemIdentifier = ItemRegistry.getItem(session.getInventory().getItemInHand()).getJavaIdentifier();
+ LevelSoundEventPacket soundEventPacket = new LevelSoundEventPacket();
+ soundEventPacket.setPosition(position);
+ soundEventPacket.setIdentifier(":");
+ soundEventPacket.setRelativeVolumeDisabled(false);
+ soundEventPacket.setBabySound(false);
+ soundEventPacket.setExtraData(-1);
+ SoundEvent soundEvent = null;
+ switch (handItemIdentifier) {
+ case "minecraft:bucket":
+ if (identifier.contains("water[")) {
+ soundEvent = SoundEvent.BUCKET_FILL_WATER;
+ } else if (identifier.contains("lava[")) {
+ soundEvent = SoundEvent.BUCKET_FILL_LAVA;
+ }
+ break;
+ case "minecraft:lava_bucket":
+ soundEvent = SoundEvent.BUCKET_EMPTY_LAVA;
+ break;
+ case "minecraft:fish_bucket":
+ soundEvent = SoundEvent.BUCKET_EMPTY_FISH;
+ break;
+ case "minecraft:water_bucket":
+ soundEvent = SoundEvent.BUCKET_EMPTY_WATER;
+ break;
+ }
+ if (soundEvent != null) {
+ soundEventPacket.setSound(soundEvent);
+ session.sendUpstreamPacket(soundEventPacket);
+ }
+ }
+}
diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/sound/block/ComparatorSoundInteractHandler.java b/connector/src/main/java/org/geysermc/connector/network/translators/sound/block/ComparatorSoundInteractHandler.java
new file mode 100644
index 00000000..4b74a678
--- /dev/null
+++ b/connector/src/main/java/org/geysermc/connector/network/translators/sound/block/ComparatorSoundInteractHandler.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright (c) 2019-2020 GeyserMC. http://geysermc.org
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ * @author GeyserMC
+ * @link https://github.com/GeyserMC/Geyser
+ *
+ */
+
+package org.geysermc.connector.network.translators.sound.block;
+
+import com.nukkitx.math.vector.Vector3f;
+import com.nukkitx.protocol.bedrock.data.LevelEventType;
+import com.nukkitx.protocol.bedrock.packet.LevelEventPacket;
+import org.geysermc.connector.network.session.GeyserSession;
+import org.geysermc.connector.network.translators.sound.BlockSoundInteractionHandler;
+import org.geysermc.connector.network.translators.sound.SoundHandler;
+
+@SoundHandler(blocks = "comparator")
+public class ComparatorSoundInteractHandler implements BlockSoundInteractionHandler {
+
+ @Override
+ public void handleInteraction(GeyserSession session, Vector3f position, String identifier) {
+ boolean powered = identifier.contains("mode=compare");
+ LevelEventPacket levelEventPacket = new LevelEventPacket();
+ levelEventPacket.setPosition(position);
+ levelEventPacket.setType(LevelEventType.REDSTONE_TRIGGER);
+ levelEventPacket.setData(powered ? 500 : 550);
+ session.sendUpstreamPacket(levelEventPacket);
+ }
+}
diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/sound/block/DoorSoundInteractionHandler.java b/connector/src/main/java/org/geysermc/connector/network/translators/sound/block/DoorSoundInteractionHandler.java
new file mode 100644
index 00000000..39a07c3a
--- /dev/null
+++ b/connector/src/main/java/org/geysermc/connector/network/translators/sound/block/DoorSoundInteractionHandler.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.sound.block;
+
+import com.nukkitx.math.vector.Vector3f;
+import com.nukkitx.protocol.bedrock.data.LevelEventType;
+import com.nukkitx.protocol.bedrock.packet.LevelEventPacket;
+import org.geysermc.connector.network.session.GeyserSession;
+import org.geysermc.connector.network.translators.sound.BlockSoundInteractionHandler;
+import org.geysermc.connector.network.translators.sound.SoundHandler;
+
+@SoundHandler(blocks = {"door", "fence_gate"})
+public class DoorSoundInteractionHandler implements BlockSoundInteractionHandler {
+
+ @Override
+ public void handleInteraction(GeyserSession session, Vector3f position, String identifier) {
+ LevelEventPacket levelEventPacket = new LevelEventPacket();
+ levelEventPacket.setType(LevelEventType.SOUND_DOOR);
+ levelEventPacket.setPosition(position);
+ levelEventPacket.setData(0);
+ session.sendUpstreamPacket(levelEventPacket);
+ }
+}
diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/sound/block/FlintAndSteelInteractionHandler.java b/connector/src/main/java/org/geysermc/connector/network/translators/sound/block/FlintAndSteelInteractionHandler.java
new file mode 100644
index 00000000..290aa7bd
--- /dev/null
+++ b/connector/src/main/java/org/geysermc/connector/network/translators/sound/block/FlintAndSteelInteractionHandler.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.sound.block;
+
+import com.nukkitx.math.vector.Vector3f;
+import com.nukkitx.protocol.bedrock.data.SoundEvent;
+import com.nukkitx.protocol.bedrock.packet.LevelSoundEventPacket;
+import org.geysermc.connector.network.session.GeyserSession;
+import org.geysermc.connector.network.translators.sound.BlockSoundInteractionHandler;
+import org.geysermc.connector.network.translators.sound.SoundHandler;
+
+@SoundHandler(items = "flint_and_steel", ignoreSneakingWhileHolding = true)
+public class FlintAndSteelInteractionHandler implements BlockSoundInteractionHandler {
+
+ @Override
+ public void handleInteraction(GeyserSession session, Vector3f position, String identifier) {
+ LevelSoundEventPacket levelSoundEventPacket = new LevelSoundEventPacket();
+ levelSoundEventPacket.setPosition(position);
+ levelSoundEventPacket.setBabySound(false);
+ levelSoundEventPacket.setRelativeVolumeDisabled(false);
+ levelSoundEventPacket.setIdentifier(":");
+ levelSoundEventPacket.setSound(SoundEvent.IGNITE);
+ levelSoundEventPacket.setExtraData(-1);
+ session.sendUpstreamPacket(levelSoundEventPacket);
+ }
+}
diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/sound/block/GrassPathInteractionHandler.java b/connector/src/main/java/org/geysermc/connector/network/translators/sound/block/GrassPathInteractionHandler.java
new file mode 100644
index 00000000..e5445e9d
--- /dev/null
+++ b/connector/src/main/java/org/geysermc/connector/network/translators/sound/block/GrassPathInteractionHandler.java
@@ -0,0 +1,51 @@
+/*
+ * 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.sound.block;
+
+import com.nukkitx.math.vector.Vector3f;
+import com.nukkitx.protocol.bedrock.data.SoundEvent;
+import com.nukkitx.protocol.bedrock.packet.LevelSoundEventPacket;
+import org.geysermc.connector.network.session.GeyserSession;
+import org.geysermc.connector.network.translators.sound.BlockSoundInteractionHandler;
+import org.geysermc.connector.network.translators.sound.SoundHandler;
+import org.geysermc.connector.network.translators.world.block.BlockTranslator;
+
+@SoundHandler(blocks = "grass_path", items = "shovel", ignoreSneakingWhileHolding = true)
+public class GrassPathInteractionHandler implements BlockSoundInteractionHandler {
+
+ @Override
+ public void handleInteraction(GeyserSession session, Vector3f position, String identifier) {
+ LevelSoundEventPacket levelSoundEventPacket = new LevelSoundEventPacket();
+ levelSoundEventPacket.setPosition(position);
+ levelSoundEventPacket.setBabySound(false);
+ levelSoundEventPacket.setRelativeVolumeDisabled(false);
+ levelSoundEventPacket.setIdentifier(":");
+ levelSoundEventPacket.setSound(SoundEvent.ITEM_USE_ON);
+ levelSoundEventPacket.setExtraData(BlockTranslator.getBedrockBlockId(BlockTranslator.getJavaBlockState(identifier)));
+ session.sendUpstreamPacket(levelSoundEventPacket);
+ }
+}
diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/sound/block/HoeInteractionHandler.java b/connector/src/main/java/org/geysermc/connector/network/translators/sound/block/HoeInteractionHandler.java
new file mode 100644
index 00000000..17d346ae
--- /dev/null
+++ b/connector/src/main/java/org/geysermc/connector/network/translators/sound/block/HoeInteractionHandler.java
@@ -0,0 +1,51 @@
+/*
+ * 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.sound.block;
+
+import com.nukkitx.math.vector.Vector3f;
+import com.nukkitx.protocol.bedrock.data.SoundEvent;
+import com.nukkitx.protocol.bedrock.packet.LevelSoundEventPacket;
+import org.geysermc.connector.network.session.GeyserSession;
+import org.geysermc.connector.network.translators.sound.BlockSoundInteractionHandler;
+import org.geysermc.connector.network.translators.sound.SoundHandler;
+import org.geysermc.connector.network.translators.world.block.BlockTranslator;
+
+@SoundHandler(blocks = "farmland", items = "hoe", ignoreSneakingWhileHolding = true)
+public class HoeInteractionHandler implements BlockSoundInteractionHandler {
+
+ @Override
+ public void handleInteraction(GeyserSession session, Vector3f position, String identifier) {
+ LevelSoundEventPacket levelSoundEventPacket = new LevelSoundEventPacket();
+ levelSoundEventPacket.setPosition(position);
+ levelSoundEventPacket.setBabySound(false);
+ levelSoundEventPacket.setRelativeVolumeDisabled(false);
+ levelSoundEventPacket.setIdentifier(":");
+ levelSoundEventPacket.setSound(SoundEvent.ITEM_USE_ON);
+ levelSoundEventPacket.setExtraData(BlockTranslator.getBedrockBlockId(BlockTranslator.getJavaBlockState(identifier)));
+ session.sendUpstreamPacket(levelSoundEventPacket);
+ }
+}
diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/sound/block/LeverSoundInteractionHandler.java b/connector/src/main/java/org/geysermc/connector/network/translators/sound/block/LeverSoundInteractionHandler.java
new file mode 100644
index 00000000..fb39d4ac
--- /dev/null
+++ b/connector/src/main/java/org/geysermc/connector/network/translators/sound/block/LeverSoundInteractionHandler.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright (c) 2019-2020 GeyserMC. http://geysermc.org
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ * @author GeyserMC
+ * @link https://github.com/GeyserMC/Geyser
+ *
+ */
+
+package org.geysermc.connector.network.translators.sound.block;
+
+import com.nukkitx.math.vector.Vector3f;
+import com.nukkitx.protocol.bedrock.data.LevelEventType;
+import com.nukkitx.protocol.bedrock.packet.LevelEventPacket;
+import org.geysermc.connector.network.session.GeyserSession;
+import org.geysermc.connector.network.translators.sound.BlockSoundInteractionHandler;
+import org.geysermc.connector.network.translators.sound.SoundHandler;
+
+@SoundHandler(blocks = "lever")
+public class LeverSoundInteractionHandler implements BlockSoundInteractionHandler {
+
+ @Override
+ public void handleInteraction(GeyserSession session, Vector3f position, String identifier) {
+ boolean powered = identifier.contains("powered=true");
+ LevelEventPacket levelEventPacket = new LevelEventPacket();
+ levelEventPacket.setPosition(position);
+ levelEventPacket.setType(LevelEventType.REDSTONE_TRIGGER);
+ levelEventPacket.setData(powered ? 600 : 500);
+ session.sendUpstreamPacket(levelEventPacket);
+ }
+}
diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/sound/entity/MilkCowSoundInteractionHandler.java b/connector/src/main/java/org/geysermc/connector/network/translators/sound/entity/MilkCowSoundInteractionHandler.java
new file mode 100644
index 00000000..d33d68af
--- /dev/null
+++ b/connector/src/main/java/org/geysermc/connector/network/translators/sound/entity/MilkCowSoundInteractionHandler.java
@@ -0,0 +1,55 @@
+/*
+ * 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.sound.entity;
+
+import com.nukkitx.math.vector.Vector3f;
+import com.nukkitx.protocol.bedrock.data.SoundEvent;
+import com.nukkitx.protocol.bedrock.packet.LevelSoundEventPacket;
+import org.geysermc.connector.entity.Entity;
+import org.geysermc.connector.network.session.GeyserSession;
+import org.geysermc.connector.network.translators.item.ItemRegistry;
+import org.geysermc.connector.network.translators.sound.EntitySoundInteractionHandler;
+import org.geysermc.connector.network.translators.sound.SoundHandler;
+
+@SoundHandler(entities = "cow", items = "bucket")
+public class MilkCowSoundInteractionHandler implements EntitySoundInteractionHandler {
+
+ @Override
+ public void handleInteraction(GeyserSession session, Vector3f position, Entity value) {
+ if (!ItemRegistry.getItem(session.getInventory().getItemInHand()).getJavaIdentifier().equals("minecraft:bucket")) {
+ return;
+ }
+ LevelSoundEventPacket levelSoundEventPacket = new LevelSoundEventPacket();
+ levelSoundEventPacket.setPosition(position);
+ levelSoundEventPacket.setBabySound(false);
+ levelSoundEventPacket.setRelativeVolumeDisabled(false);
+ levelSoundEventPacket.setIdentifier(":");
+ levelSoundEventPacket.setSound(SoundEvent.MILK);
+ levelSoundEventPacket.setExtraData(-1);
+ session.sendUpstreamPacket(levelSoundEventPacket);
+ }
+}
diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/world/CachedChunkManager.java b/connector/src/main/java/org/geysermc/connector/network/translators/world/CachedChunkManager.java
new file mode 100644
index 00000000..740e2df9
--- /dev/null
+++ b/connector/src/main/java/org/geysermc/connector/network/translators/world/CachedChunkManager.java
@@ -0,0 +1,39 @@
+/*
+ * 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.world;
+
+import com.github.steveice10.mc.protocol.data.game.entity.metadata.Position;
+import com.github.steveice10.mc.protocol.data.game.world.block.BlockState;
+import org.geysermc.connector.network.session.GeyserSession;
+
+public class CachedChunkManager extends WorldManager {
+
+ @Override
+ public BlockState getBlockAt(GeyserSession session, int x, int y, int z) {
+ return session.getChunkCache().getBlockAt(new Position(x, y, z));
+ }
+}
diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/world/WorldManager.java b/connector/src/main/java/org/geysermc/connector/network/translators/world/WorldManager.java
new file mode 100644
index 00000000..d92d8454
--- /dev/null
+++ b/connector/src/main/java/org/geysermc/connector/network/translators/world/WorldManager.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.world;
+
+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 org.geysermc.connector.network.session.GeyserSession;
+
+/**
+ * Class that manages or retrieves various information
+ * from the world. Everything in this class should be
+ * safe to return null or an empty value in the event
+ * that chunk caching or anything of the sort is disabled
+ * on the standalone version of Geyser.
+ */
+public abstract class WorldManager {
+
+ /**
+ * Gets the {@link BlockState} at the specified location
+ *
+ * @param session the session
+ * @param position the position
+ * @return the block state at the specified location
+ */
+ public BlockState getBlockAt(GeyserSession session, Position position) {
+ return this.getBlockAt(session, position.getX(), position.getY(), position.getZ());
+ }
+
+ /**
+ * Gets the {@link BlockState} at the specified location
+ *
+ * @param session the session
+ * @param vector the position
+ * @return the block state at the specified location
+ */
+ public BlockState getBlockAt(GeyserSession session, Vector3i vector) {
+ return this.getBlockAt(session, vector.getX(), vector.getY(), vector.getZ());
+ }
+
+ /**
+ * Gets the {@link BlockState} at the specified location
+ *
+ * @param session the session
+ * @param x the x coordinate to get the block at
+ * @param y the y coordinate to get the block at
+ * @param z the z coordinate to get the block at
+ * @return the block state at the specified location
+ */
+ public abstract BlockState getBlockAt(GeyserSession session, int x, int y, int z);
+}
diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/world/block/BlockStateValues.java b/connector/src/main/java/org/geysermc/connector/network/translators/world/block/BlockStateValues.java
new file mode 100644
index 00000000..070a0592
--- /dev/null
+++ b/connector/src/main/java/org/geysermc/connector/network/translators/world/block/BlockStateValues.java
@@ -0,0 +1,241 @@
+/*
+ * 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.world.block;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import com.github.steveice10.mc.protocol.data.game.world.block.BlockState;
+import com.nukkitx.nbt.tag.CompoundTag;
+import it.unimi.dsi.fastutil.ints.Int2BooleanMap;
+import it.unimi.dsi.fastutil.ints.Int2BooleanOpenHashMap;
+import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
+import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
+import it.unimi.dsi.fastutil.objects.Object2ByteMap;
+import it.unimi.dsi.fastutil.objects.Object2ByteOpenHashMap;
+import it.unimi.dsi.fastutil.objects.Object2IntMap;
+import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Used for block entities if the Java block state contains Bedrock block information.
+ */
+public class BlockStateValues {
+
+ private static final Object2IntMap BANNER_COLORS = new Object2IntOpenHashMap<>();
+ private static final Object2ByteMap BED_COLORS = new Object2ByteOpenHashMap<>();
+ private static final Int2ObjectMap DOUBLE_CHEST_VALUES = new Int2ObjectOpenHashMap<>();
+ private static final Int2ObjectMap FLOWER_POT_VALUES = new Int2ObjectOpenHashMap<>();
+ private static final Map FLOWER_POT_BLOCKS = new HashMap<>();
+ private static final Object2IntMap NOTEBLOCK_PITCHES = new Object2IntOpenHashMap<>();
+ private static final Int2BooleanMap IS_STICKY_PISTON = new Int2BooleanOpenHashMap();
+ private static final Int2BooleanMap PISTON_VALUES = new Int2BooleanOpenHashMap();
+ private static final Object2ByteMap SKULL_VARIANTS = new Object2ByteOpenHashMap<>();
+ private static final Object2ByteMap SKULL_ROTATIONS = new Object2ByteOpenHashMap<>();
+ private static final Object2ByteMap SHULKERBOX_DIRECTIONS = new Object2ByteOpenHashMap<>();
+
+ /**
+ * Determines if the block state contains Bedrock block information
+ * @param entry The String to 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) {
+ 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) {
+ BED_COLORS.put(javaBlockState, (byte) bedColor.intValue());
+ return;
+ }
+
+ if (entry.getValue().get("double_chest_position") != null) {
+ boolean isX = (entry.getValue().get("x") != null);
+ boolean isDirectionPositive = ((entry.getValue().get("x") != null && entry.getValue().get("x").asBoolean()) ||
+ (entry.getValue().get("z") != null && entry.getValue().get("z").asBoolean()));
+ boolean isLeft = (entry.getValue().get("double_chest_position").asText().contains("left"));
+ DOUBLE_CHEST_VALUES.put(javaBlockState.getId(), new DoubleChestValue(isX, isDirectionPositive, isLeft));
+ return;
+ }
+
+ if (entry.getKey().contains("potted_")) {
+ FLOWER_POT_VALUES.put(javaBlockState.getId(), entry.getKey().replace("potted_", ""));
+ return;
+ }
+
+ JsonNode notePitch = entry.getValue().get("note_pitch");
+ if (notePitch != null) {
+ NOTEBLOCK_PITCHES.put(javaBlockState, entry.getValue().get("note_pitch").intValue());
+ return;
+ }
+
+ if (entry.getKey().contains("piston")) {
+ // True if extended, false if not
+ PISTON_VALUES.put(javaBlockState.getId(), entry.getKey().contains("extended=true"));
+ IS_STICKY_PISTON.put(javaBlockState.getId(), entry.getKey().contains("sticky"));
+ return;
+ }
+
+ JsonNode skullVariation = entry.getValue().get("variation");
+ if(skullVariation != null) {
+ SKULL_VARIANTS.put(javaBlockState, (byte) skullVariation.intValue());
+ }
+
+ JsonNode skullRotation = entry.getValue().get("skull_rotation");
+ if (skullRotation != null) {
+ SKULL_ROTATIONS.put(javaBlockState, (byte) skullRotation.intValue());
+ }
+
+ JsonNode shulkerDirection = entry.getValue().get("shulker_direction");
+ if (shulkerDirection != null) {
+ BlockStateValues.SHULKERBOX_DIRECTIONS.put(javaBlockState, (byte) shulkerDirection.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;
+ }
+
+ /**
+ * All double chest values are part of the block state in Java and part of the block entity tag in Bedrock.
+ * This gives the DoubleChestValue that can be calculated into the final tag.
+ * @return The map of all DoubleChestValues.
+ */
+ public static Int2ObjectMap getDoubleChestValues() {
+ return DOUBLE_CHEST_VALUES;
+ }
+
+ /**
+ * Get the Int2ObjectMap of flower pot block states to containing plant
+ * @return Int2ObjectMap of flower pot values
+ */
+ public static Int2ObjectMap getFlowerPotValues() {
+ return FLOWER_POT_VALUES;
+ }
+
+ /**
+ * Get the map of contained flower pot plants to Bedrock CompoundTag
+ * @return Map of flower pot blocks.
+ */
+ public static Map getFlowerPotBlocks() {
+ return FLOWER_POT_BLOCKS;
+ }
+
+ /**
+ * The note that noteblocks output when hit is part of the block state in Java but sent as a BlockEventPacket in Bedrock.
+ * This gives an integer pitch that Bedrock can use.
+ * @param state BlockState of the block
+ * @return note block note integer or -1 if not present
+ */
+ public static int getNoteblockPitch(BlockState state) {
+ if (NOTEBLOCK_PITCHES.containsKey(state)) {
+ return NOTEBLOCK_PITCHES.getInt(state);
+ }
+ return -1;
+ }
+
+ /**
+ * Get the Int2BooleanMap showing if a piston block state is extended or not.
+ * @return the Int2BooleanMap of piston extensions.
+ */
+ public static Int2BooleanMap getPistonValues() {
+ return PISTON_VALUES;
+ }
+
+ public static boolean isStickyPiston(BlockState blockState) {
+ return IS_STICKY_PISTON.get(blockState.getId());
+ }
+
+ /**
+ * 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;
+ }
+
+ /**
+ * Skull rotations are part of the namespaced ID in Java Edition, but part of the block entity tag in Bedrock.
+ * This gives a byte rotation that Bedrock can use.
+ *
+ * @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;
+ }
+
+
+ /**
+ * Shulker box directions are part of the namespaced ID in Java Edition, but part of the block entity tag in Bedrock.
+ * This gives a byte direction that Bedrock can use.
+ *
+ * @param state BlockState of the block
+ * @return Shulker direction value or -1 if no value
+ */
+ public static byte getShulkerBoxDirection(BlockState state) {
+ if (SHULKERBOX_DIRECTIONS.containsKey(state)) {
+ return SHULKERBOX_DIRECTIONS.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/world/block/BlockTranslator.java
similarity index 80%
rename from connector/src/main/java/org/geysermc/connector/network/translators/block/BlockTranslator.java
rename to connector/src/main/java/org/geysermc/connector/network/translators/world/block/BlockTranslator.java
index cf0da17b..0c2cab2c 100644
--- a/connector/src/main/java/org/geysermc/connector/network/translators/block/BlockTranslator.java
+++ b/connector/src/main/java/org/geysermc/connector/network/translators/world/block/BlockTranslator.java
@@ -23,10 +23,12 @@
* @link https://github.com/GeyserMC/Geyser
*/
-package org.geysermc.connector.network.translators.block;
+package org.geysermc.connector.network.translators.world.block;
import com.fasterxml.jackson.databind.JsonNode;
import com.github.steveice10.mc.protocol.data.game.world.block.BlockState;
+import com.google.common.collect.BiMap;
+import com.google.common.collect.HashBiMap;
import com.nukkitx.nbt.CompoundTagBuilder;
import com.nukkitx.nbt.NbtUtils;
import com.nukkitx.nbt.stream.NBTInputStream;
@@ -36,8 +38,8 @@ import it.unimi.dsi.fastutil.ints.*;
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.geysermc.connector.network.translators.world.block.entity.BlockEntity;
+import org.geysermc.connector.utils.FileUtils;
import org.reflections.Reflections;
import java.io.InputStream;
@@ -50,8 +52,9 @@ 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 BiMap JAVA_ID_BLOCK_MAP = HashBiMap.create();
private static final IntSet WATERLOGGED = new IntOpenHashSet();
+ private static final Object2IntMap ITEM_FRAMES = new Object2IntOpenHashMap<>();
// Bedrock carpet ID, used in LlamaEntity.java for decoration
public static final int CARPET = 171;
@@ -66,11 +69,14 @@ public class BlockTranslator {
public static final IntSet JAVA_RUNTIME_WOOL_IDS = new IntOpenHashSet();
public static final int JAVA_RUNTIME_COBWEB_ID;
+ public static final int JAVA_RUNTIME_FURNACE_ID;
+ public static final int JAVA_RUNTIME_FURNACE_LIT_ID;
+
private static final int BLOCK_STATE_VERSION = 17760256;
static {
/* Load block palette */
- InputStream stream = Toolbox.getResource("bedrock/runtime_block_states.dat");
+ InputStream stream = FileUtils.getResource("bedrock/runtime_block_states.dat");
ListTag blocksTag;
try (NBTInputStream nbtInputStream = NbtUtils.createNetworkReader(stream)) {
@@ -88,10 +94,10 @@ public class BlockTranslator {
}
}
- stream = Toolbox.getResource("mappings/blocks.json");
+ stream = FileUtils.getResource("mappings/blocks.json");
JsonNode blocks;
try {
- blocks = Toolbox.JSON_MAPPER.readTree(stream);
+ blocks = GeyserConnector.JSON_MAPPER.readTree(stream);
} catch (Exception e) {
throw new AssertionError("Unable to load Java block mappings", e);
}
@@ -99,13 +105,15 @@ public class BlockTranslator {
addedStatesMap.defaultReturnValue(-1);
List paletteList = new ArrayList<>();
- Reflections ref = new Reflections("org.geysermc.connector.network.translators.block.entity");
+ Reflections ref = new Reflections("org.geysermc.connector.network.translators.world.block.entity");
ref.getTypesAnnotatedWith(BlockEntity.class);
int waterRuntimeId = -1;
int javaRuntimeId = -1;
int bedrockRuntimeId = 0;
int cobwebRuntimeId = -1;
+ int furnaceRuntimeId = -1;
+ int furnaceLitRuntimeId = -1;
Iterator> blocksIterator = blocks.fields();
while (blocksIterator.hasNext()) {
javaRuntimeId++;
@@ -151,6 +159,11 @@ public class BlockTranslator {
BlockStateValues.storeBlockStateValues(entry, javaBlockState);
+ // Get the tag needed for non-empty flower pots
+ if (entry.getValue().get("pottable") != null) {
+ BlockStateValues.getFlowerPotBlocks().put(entry.getKey().split("\\[")[0], buildBedrockState(entry.getValue()));
+ }
+
if ("minecraft:water[level=0]".equals(javaId)) {
waterRuntimeId = bedrockRuntimeId;
}
@@ -179,6 +192,14 @@ public class BlockTranslator {
}
JAVA_TO_BEDROCK_BLOCK_MAP.put(javaRuntimeId, bedrockRuntimeId);
+ if (javaId.startsWith("minecraft:furnace[facing=north")) {
+ if (javaId.contains("lit=true")) {
+ furnaceLitRuntimeId = javaRuntimeId;
+ } else {
+ furnaceRuntimeId = javaRuntimeId;
+ }
+ }
+
bedrockRuntimeId++;
}
@@ -187,6 +208,16 @@ public class BlockTranslator {
}
JAVA_RUNTIME_COBWEB_ID = cobwebRuntimeId;
+ if (furnaceRuntimeId == -1) {
+ throw new AssertionError("Unable to find furnace in palette");
+ }
+ JAVA_RUNTIME_FURNACE_ID = furnaceRuntimeId;
+
+ if (furnaceLitRuntimeId == -1) {
+ throw new AssertionError("Unable to find lit furnace in palette");
+ }
+ JAVA_RUNTIME_FURNACE_LIT_ID = furnaceLitRuntimeId;
+
if (waterRuntimeId == -1) {
throw new AssertionError("Unable to find water in palette");
}
@@ -194,6 +225,16 @@ public class BlockTranslator {
paletteList.addAll(blockStateMap.values()); // Add any missing mappings that could crash the client
+ // Loop around again to find all item frame runtime IDs
+ int frameRuntimeId = 0;
+ for (CompoundTag tag : paletteList) {
+ CompoundTag blockTag = tag.getCompound("block");
+ if (blockTag.getString("name").equals("minecraft:frame")) {
+ ITEM_FRAMES.put(tag, frameRuntimeId);
+ }
+ frameRuntimeId++;
+ }
+
BLOCKS = new ListTag<>("", CompoundTag.class, paletteList);
}
@@ -245,6 +286,18 @@ public class BlockTranslator {
return BEDROCK_TO_JAVA_BLOCK_MAP.get(bedrockId);
}
+ public static int getItemFrame(CompoundTag tag) {
+ return ITEM_FRAMES.getOrDefault(tag, -1);
+ }
+
+ public static boolean isItemFrame(int bedrockBlockRuntimeId) {
+ return ITEM_FRAMES.values().contains(bedrockBlockRuntimeId);
+ }
+
+ public static int getBlockStateVersion() {
+ return BLOCK_STATE_VERSION;
+ }
+
public static BlockState getJavaBlockState(String javaId) {
return JAVA_ID_BLOCK_MAP.get(javaId);
}
@@ -257,6 +310,10 @@ public class BlockTranslator {
return WATERLOGGED.contains(state.getId());
}
+ public static BiMap getJavaIdBlockMap() {
+ return JAVA_ID_BLOCK_MAP;
+ }
+
public static BlockState getJavaWaterloggedState(int bedrockId) {
return BEDROCK_TO_JAVA_BLOCK_MAP.get(1 << 31 | bedrockId);
}
diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/world/block/DoubleChestValue.java b/connector/src/main/java/org/geysermc/connector/network/translators/world/block/DoubleChestValue.java
new file mode 100644
index 00000000..5bd21724
--- /dev/null
+++ b/connector/src/main/java/org/geysermc/connector/network/translators/world/block/DoubleChestValue.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.world.block;
+
+import lombok.AllArgsConstructor;
+
+/**
+ * This stores all values of double chests that are part of the Java block state.
+ */
+@AllArgsConstructor
+public class DoubleChestValue {
+
+ /**
+ * If true, then chest is facing east/west; if false, south/north
+ */
+ public boolean isFacingEast;
+
+ /**
+ * If true, direction is positive (east/south); if false, direction is negative (west/north)
+ */
+ public boolean isDirectionPositive;
+
+ /**
+ * If true, chest is the left of a pair; if false, chest is the right of a pair.
+ */
+ public boolean isLeft;
+
+}
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/world/block/entity/BannerBlockEntityTranslator.java
similarity index 63%
rename from connector/src/main/java/org/geysermc/connector/network/translators/block/entity/BannerBlockEntityTranslator.java
rename to connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/BannerBlockEntityTranslator.java
index e81191a3..3e2c0a95 100644
--- a/connector/src/main/java/org/geysermc/connector/network/translators/block/entity/BannerBlockEntityTranslator.java
+++ b/connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/BannerBlockEntityTranslator.java
@@ -23,7 +23,7 @@
* @link https://github.com/GeyserMC/Geyser
*/
-package org.geysermc.connector.network.translators.block.entity;
+package org.geysermc.connector.network.translators.world.block.entity;
import com.github.steveice10.mc.protocol.data.game.world.block.BlockState;
import com.github.steveice10.opennbt.tag.builtin.CompoundTag;
@@ -32,12 +32,13 @@ 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 org.geysermc.connector.network.translators.item.translators.BannerTranslator;
+import org.geysermc.connector.network.translators.world.block.BlockStateValues;
import java.util.ArrayList;
import java.util.List;
-@BlockEntity(name = "Banner", delay = false, regex = "banner")
+@BlockEntity(name = "Banner", regex = "banner")
public class BannerBlockEntityTranslator extends BlockEntityTranslator implements RequiresBlockState {
@Override
@@ -48,26 +49,21 @@ public class BannerBlockEntityTranslator extends BlockEntityTranslator implement
@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()) {
- com.nukkitx.nbt.tag.CompoundTag newPatternTag = getPattern((CompoundTag) patternTag);
- if (newPatternTag != null) {
- tagsList.add(newPatternTag);
- }
- }
- com.nukkitx.nbt.tag.ListTag bedrockPatterns =
- new com.nukkitx.nbt.tag.ListTag<>("Patterns", com.nukkitx.nbt.tag.CompoundTag.class, tagsList);
- tags.add(bedrockPatterns);
+ ListTag patterns = tag.get("Patterns");
+ tags.add(BannerTranslator.convertBannerPattern(patterns));
}
+
if (tag.contains("CustomName")) {
tags.add(new StringTag("CustomName", (String) tag.get("CustomName").getValue()));
}
+
return tags;
}
@@ -84,25 +80,4 @@ public class BannerBlockEntityTranslator extends BlockEntityTranslator implement
tagBuilder.listTag("Patterns", com.nukkitx.nbt.tag.CompoundTag.class, new ArrayList<>());
return tagBuilder.buildRootTag();
}
-
- /**
- * Convert the Java edition pattern nbt to Bedrock edition, null if the pattern doesn't exist
- *
- * @param pattern Java edition pattern nbt
- * @return The Bedrock edition format pattern nbt
- */
- protected com.nukkitx.nbt.tag.CompoundTag getPattern(CompoundTag pattern) {
- String patternName = (String) pattern.get("Pattern").getValue();
-
- // Return null if its the globe pattern as it doesn't exist on bedrock
- if (patternName.equals("glb")) {
- return null;
- }
-
- return CompoundTagBuilder.builder()
- .intTag("Color", 15 - (int) pattern.get("Color").getValue())
- .stringTag("Pattern", (String) pattern.get("Pattern").getValue())
- .stringTag("Pattern", patternName)
- .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/world/block/entity/BedBlockEntityTranslator.java
similarity index 92%
rename from connector/src/main/java/org/geysermc/connector/network/translators/block/entity/BedBlockEntityTranslator.java
rename to connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/BedBlockEntityTranslator.java
index a8dae253..5f0b1cc0 100644
--- a/connector/src/main/java/org/geysermc/connector/network/translators/block/entity/BedBlockEntityTranslator.java
+++ b/connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/BedBlockEntityTranslator.java
@@ -23,19 +23,19 @@
* @link https://github.com/GeyserMC/Geyser
*/
-package org.geysermc.connector.network.translators.block.entity;
+package org.geysermc.connector.network.translators.world.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 org.geysermc.connector.network.translators.world.block.BlockStateValues;
import java.util.ArrayList;
import java.util.List;
-@BlockEntity(name = "Bed", delay = false, regex = "bed")
+@BlockEntity(name = "Bed", regex = "bed")
public class BedBlockEntityTranslator extends BlockEntityTranslator implements RequiresBlockState {
@Override
diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/BedrockOnlyBlockEntity.java b/connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/BedrockOnlyBlockEntity.java
new file mode 100644
index 00000000..5b325eba
--- /dev/null
+++ b/connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/BedrockOnlyBlockEntity.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.world.block.entity;
+
+import com.github.steveice10.mc.protocol.data.game.world.block.BlockState;
+import com.nukkitx.math.vector.Vector3i;
+import com.nukkitx.nbt.tag.CompoundTag;
+import org.geysermc.connector.network.session.GeyserSession;
+
+/**
+ * Implemented only if a block is a block entity in Bedrock and not Java Edition.
+ */
+public interface BedrockOnlyBlockEntity {
+
+ /**
+ * Update the block on Bedrock Edition.
+ * @param session GeyserSession.
+ * @param blockState The Java block state.
+ * @param position The Bedrock block position.
+ */
+ void updateBlock(GeyserSession session, BlockState blockState, Vector3i position);
+
+ /**
+ * Get the tag of the Bedrock-only block entity
+ * @param position Bedrock position of block.
+ * @param blockState Java BlockState of block.
+ * @return Bedrock tag, or null if not a Bedrock-only Block Entity
+ */
+ static CompoundTag getTag(Vector3i position, BlockState blockState) {
+ if (new FlowerPotBlockEntityTranslator().isBlock(blockState)) {
+ return FlowerPotBlockEntityTranslator.getTag(blockState, position);
+ } else if (PistonBlockEntityTranslator.isBlock(blockState)) {
+ return PistonBlockEntityTranslator.getTag(blockState, position);
+ }
+ return null;
+ }
+}
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/world/block/entity/BlockEntity.java
similarity index 88%
rename from connector/src/main/java/org/geysermc/connector/network/translators/block/entity/BlockEntity.java
rename to connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/BlockEntity.java
index 47cbbaf3..11bfe0ea 100644
--- a/connector/src/main/java/org/geysermc/connector/network/translators/block/entity/BlockEntity.java
+++ b/connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/BlockEntity.java
@@ -23,7 +23,7 @@
* @link https://github.com/GeyserMC/Geyser
*/
-package org.geysermc.connector.network.translators.block.entity;
+package org.geysermc.connector.network.translators.world.block.entity;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -31,12 +31,6 @@ import java.lang.annotation.RetentionPolicy;
@Retention(value = RetentionPolicy.RUNTIME)
public @interface BlockEntity {
- /**
- * Whether to delay the sending of the block entity
- * @return the delay for when sending the block entity
- */
- boolean delay();
-
/**
* The block entity name
* @return the name of the block entity
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/world/block/entity/BlockEntityTranslator.java
similarity index 65%
rename from connector/src/main/java/org/geysermc/connector/network/translators/block/entity/BlockEntityTranslator.java
rename to connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/BlockEntityTranslator.java
index f2825789..3d663926 100644
--- a/connector/src/main/java/org/geysermc/connector/network/translators/block/entity/BlockEntityTranslator.java
+++ b/connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/BlockEntityTranslator.java
@@ -23,7 +23,7 @@
* @link https://github.com/GeyserMC/Geyser
*/
-package org.geysermc.connector.network.translators.block.entity;
+package org.geysermc.connector.network.translators.world.block.entity;
import com.github.steveice10.mc.protocol.data.game.world.block.BlockState;
import com.github.steveice10.opennbt.tag.builtin.CompoundTag;
@@ -32,12 +32,49 @@ import com.github.steveice10.opennbt.tag.builtin.StringTag;
import com.nukkitx.nbt.CompoundTagBuilder;
import com.nukkitx.nbt.tag.Tag;
+import it.unimi.dsi.fastutil.objects.ObjectArrayList;
+import org.geysermc.connector.GeyserConnector;
import org.geysermc.connector.utils.BlockEntityUtils;
+import org.reflections.Reflections;
+import java.util.HashMap;
import java.util.List;
+import java.util.Map;
public abstract class BlockEntityTranslator {
+ public static final Map BLOCK_ENTITY_TRANSLATORS = new HashMap<>();
+ public static ObjectArrayList REQUIRES_BLOCK_STATE_LIST = new ObjectArrayList<>();
+
+ protected BlockEntityTranslator() {
+ }
+
+ public static void init() {
+ // no-op
+ }
+
+ static {
+ Reflections ref = new Reflections("org.geysermc.connector.network.translators.world.block.entity");
+ for (Class> clazz : ref.getTypesAnnotatedWith(BlockEntity.class)) {
+ GeyserConnector.getInstance().getLogger().debug("Found annotated block entity: " + clazz.getCanonicalName());
+
+ try {
+ BLOCK_ENTITY_TRANSLATORS.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() + ".");
+ }
+ }
+ for (Class> clazz : ref.getSubTypesOf(RequiresBlockState.class)) {
+ GeyserConnector.getInstance().getLogger().debug("Found block entity that requires block state: " + clazz.getCanonicalName());
+
+ try {
+ REQUIRES_BLOCK_STATE_LIST.add((RequiresBlockState) clazz.newInstance());
+ } catch (InstantiationException | IllegalAccessException e) {
+ GeyserConnector.getInstance().getLogger().error("Could not instantiate required block state " + clazz.getCanonicalName() + ".");
+ }
+ }
+ }
+
public abstract List> translateTag(CompoundTag tag, BlockState blockState);
public abstract CompoundTag getDefaultJavaTag(String javaId, int x, int y, int z);
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/world/block/entity/CampfireBlockEntityTranslator.java
similarity index 91%
rename from connector/src/main/java/org/geysermc/connector/network/translators/block/entity/CampfireBlockEntityTranslator.java
rename to connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/CampfireBlockEntityTranslator.java
index 11b7e864..cd31636c 100644
--- a/connector/src/main/java/org/geysermc/connector/network/translators/block/entity/CampfireBlockEntityTranslator.java
+++ b/connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/CampfireBlockEntityTranslator.java
@@ -23,22 +23,21 @@
* @link https://github.com/GeyserMC/Geyser
*/
-package org.geysermc.connector.network.translators.block.entity;
+package org.geysermc.connector.network.translators.world.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 org.geysermc.connector.network.translators.item.ItemRegistry;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
-@BlockEntity(name = "Campfire", delay = false, regex = "campfire")
+@BlockEntity(name = "Campfire", regex = "campfire")
public class CampfireBlockEntityTranslator extends BlockEntityTranslator {
@Override
@@ -71,7 +70,7 @@ public class CampfireBlockEntityTranslator extends BlockEntityTranslator {
}
protected com.nukkitx.nbt.tag.CompoundTag getItem(CompoundTag tag) {
- ItemEntry entry = Translators.getItemTranslator().getItemEntry((String) tag.get("id").getValue());
+ ItemEntry entry = ItemRegistry.getItemEntry((String) tag.get("id").getValue());
CompoundTagBuilder tagBuilder = CompoundTagBuilder.builder()
.shortTag("id", (short) entry.getBedrockId())
.byteTag("Count", (byte) tag.get("Count").getValue())
diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/DoubleChestBlockEntityTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/DoubleChestBlockEntityTranslator.java
new file mode 100644
index 00000000..f5599832
--- /dev/null
+++ b/connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/DoubleChestBlockEntityTranslator.java
@@ -0,0 +1,108 @@
+/*
+ * 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.world.block.entity;
+
+import com.github.steveice10.mc.protocol.data.game.world.block.BlockState;
+import com.github.steveice10.opennbt.tag.builtin.CompoundTag;
+import com.nukkitx.math.vector.Vector3i;
+import com.nukkitx.nbt.CompoundTagBuilder;
+import com.nukkitx.nbt.tag.ByteTag;
+import com.nukkitx.nbt.tag.IntTag;
+import com.nukkitx.nbt.tag.Tag;
+import org.geysermc.connector.network.session.GeyserSession;
+import org.geysermc.connector.network.translators.world.block.BlockStateValues;
+import org.geysermc.connector.network.translators.world.block.DoubleChestValue;
+import org.geysermc.connector.utils.BlockEntityUtils;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Chests have more block entity properties in Bedrock, which is solved by implementing the BedrockOnlyBlockEntity
+ */
+@BlockEntity(name = "Chest", regex = "chest")
+public class DoubleChestBlockEntityTranslator extends BlockEntityTranslator implements BedrockOnlyBlockEntity, RequiresBlockState {
+
+ @Override
+ public boolean isBlock(BlockState blockState) {
+ return BlockStateValues.getDoubleChestValues().containsKey(blockState.getId());
+ }
+
+ @Override
+ public void updateBlock(GeyserSession session, BlockState blockState, Vector3i position) {
+ CompoundTag javaTag = getConstantJavaTag("chest", position.getX(), position.getY(), position.getZ());
+ CompoundTagBuilder tagBuilder = getConstantBedrockTag(BlockEntityUtils.getBedrockBlockEntityId("chest"), position.getX(), position.getY(), position.getZ()).toBuilder();
+ translateTag(javaTag, blockState).forEach(tagBuilder::tag);
+ BlockEntityUtils.updateBlockEntity(session, tagBuilder.buildRootTag(), position);
+ }
+
+ @Override
+ public List> translateTag(CompoundTag tag, BlockState blockState) {
+ List> tags = new ArrayList<>();
+ if (blockState != null && BlockStateValues.getDoubleChestValues().containsKey(blockState.getId())) {
+ DoubleChestValue chestValues = BlockStateValues.getDoubleChestValues().get(blockState.getId());
+ if (chestValues != null) {
+ int x = (int) tag.getValue().get("x").getValue();
+ int z = (int) tag.getValue().get("z").getValue();
+ // Calculate the position of the other chest based on the Java block state
+ if (chestValues.isFacingEast) {
+ if (chestValues.isDirectionPositive) {
+ // East
+ z = z + (chestValues.isLeft ? 1 : -1);
+ } else {
+ // West
+ z = z + (chestValues.isLeft ? -1 : 1);
+ }
+ } else {
+ if (chestValues.isDirectionPositive) {
+ // South
+ x = x + (chestValues.isLeft ? -1 : 1);
+ } else {
+ // North
+ x = x + (chestValues.isLeft ? 1 : -1);
+ }
+ }
+ tags.add(new IntTag("pairx", x));
+ tags.add(new IntTag("pairz", z));
+ if (!chestValues.isLeft) {
+ tags.add(new ByteTag("pairlead", (byte) 1));
+ }
+ }
+ }
+ 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) {
+ return null;
+ }
+}
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/world/block/entity/EmptyBlockEntityTranslator.java
similarity index 94%
rename from connector/src/main/java/org/geysermc/connector/network/translators/block/entity/EmptyBlockEntityTranslator.java
rename to connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/EmptyBlockEntityTranslator.java
index e4601400..d1068277 100644
--- a/connector/src/main/java/org/geysermc/connector/network/translators/block/entity/EmptyBlockEntityTranslator.java
+++ b/connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/EmptyBlockEntityTranslator.java
@@ -23,7 +23,7 @@
* @link https://github.com/GeyserMC/Geyser
*/
-package org.geysermc.connector.network.translators.block.entity;
+package org.geysermc.connector.network.translators.world.block.entity;
import com.github.steveice10.mc.protocol.data.game.world.block.BlockState;
import com.github.steveice10.opennbt.tag.builtin.CompoundTag;
@@ -32,7 +32,7 @@ import com.nukkitx.nbt.tag.Tag;
import java.util.ArrayList;
import java.util.List;
-@BlockEntity(name = "Empty", delay = false, regex = "")
+@BlockEntity(name = "Empty", regex = "")
public class EmptyBlockEntityTranslator extends BlockEntityTranslator {
@Override
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/world/block/entity/EndGatewayBlockEntityTranslator.java
similarity index 96%
rename from connector/src/main/java/org/geysermc/connector/network/translators/block/entity/EndGatewayBlockEntityTranslator.java
rename to connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/EndGatewayBlockEntityTranslator.java
index de5868a4..4cd2eaa9 100644
--- a/connector/src/main/java/org/geysermc/connector/network/translators/block/entity/EndGatewayBlockEntityTranslator.java
+++ b/connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/EndGatewayBlockEntityTranslator.java
@@ -23,7 +23,7 @@
* @link https://github.com/GeyserMC/Geyser
*/
-package org.geysermc.connector.network.translators.block.entity;
+package org.geysermc.connector.network.translators.world.block.entity;
import com.github.steveice10.mc.protocol.data.game.world.block.BlockState;
import com.github.steveice10.opennbt.tag.builtin.CompoundTag;
@@ -36,7 +36,7 @@ import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
-@BlockEntity(name = "EndGateway", delay = true, regex = "end_gateway")
+@BlockEntity(name = "EndGateway", regex = "end_gateway")
public class EndGatewayBlockEntityTranslator extends BlockEntityTranslator {
@Override
diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/FlowerPotBlockEntityTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/FlowerPotBlockEntityTranslator.java
new file mode 100644
index 00000000..c4748c82
--- /dev/null
+++ b/connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/FlowerPotBlockEntityTranslator.java
@@ -0,0 +1,84 @@
+/*
+ * 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.world.block.entity;
+
+import com.github.steveice10.mc.protocol.data.game.world.block.BlockState;
+import com.nukkitx.math.vector.Vector3i;
+import com.nukkitx.nbt.CompoundTagBuilder;
+import com.nukkitx.nbt.tag.CompoundTag;
+import com.nukkitx.protocol.bedrock.packet.UpdateBlockPacket;
+import org.geysermc.connector.network.session.GeyserSession;
+import org.geysermc.connector.network.translators.world.block.BlockStateValues;
+import org.geysermc.connector.network.translators.world.block.BlockTranslator;
+import org.geysermc.connector.utils.BlockEntityUtils;
+
+public class FlowerPotBlockEntityTranslator implements BedrockOnlyBlockEntity, RequiresBlockState {
+
+ @Override
+ public boolean isBlock(BlockState blockState) {
+ return (BlockStateValues.getFlowerPotValues().containsKey(blockState.getId()));
+ }
+
+ @Override
+ public void updateBlock(GeyserSession session, BlockState blockState, Vector3i position) {
+ BlockEntityUtils.updateBlockEntity(session, getTag(blockState, position), position);
+ UpdateBlockPacket updateBlockPacket = new UpdateBlockPacket();
+ updateBlockPacket.setDataLayer(0);
+ updateBlockPacket.setRuntimeId(BlockTranslator.getBedrockBlockId(blockState));
+ updateBlockPacket.setBlockPosition(position);
+ updateBlockPacket.getFlags().add(UpdateBlockPacket.Flag.PRIORITY);
+ updateBlockPacket.getFlags().add(UpdateBlockPacket.Flag.NONE);
+ updateBlockPacket.getFlags().add(UpdateBlockPacket.Flag.NEIGHBORS);
+ session.sendUpstreamPacket(updateBlockPacket);
+ }
+
+ /**
+ * Get the Nukkit CompoundTag of the flower pot.
+ * @param blockState Java BlockState of flower pot.
+ * @param position Bedrock position of flower pot.
+ * @return Bedrock tag of flower pot.
+ */
+ public static CompoundTag getTag(BlockState blockState, Vector3i position) {
+ CompoundTagBuilder tagBuilder = CompoundTagBuilder.builder()
+ .intTag("x", position.getX())
+ .intTag("y", position.getY())
+ .intTag("z", position.getZ())
+ .byteTag("isMovable", (byte) 1)
+ .stringTag("id", "FlowerPot");
+ // Get the Java name of the plant inside. e.g. minecraft:oak_sapling
+ String name = BlockStateValues.getFlowerPotValues().get(blockState.getId());
+ if (name != null) {
+ // Get the Bedrock CompoundTag of the block.
+ // This is where we need to store the *Java* name because Bedrock has six minecraft:sapling blocks with different block states.
+ CompoundTag plant = BlockStateValues.getFlowerPotBlocks().get(name);
+ if (plant != null) {
+ tagBuilder.tag(plant.toBuilder().build("PlantBlock"));
+ }
+ }
+ return tagBuilder.buildRootTag();
+ }
+}
diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/NoteblockBlockEntityTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/NoteblockBlockEntityTranslator.java
new file mode 100644
index 00000000..168015f6
--- /dev/null
+++ b/connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/NoteblockBlockEntityTranslator.java
@@ -0,0 +1,58 @@
+/*
+ * 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.world.block.entity;
+
+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.protocol.bedrock.packet.BlockEventPacket;
+import org.geysermc.connector.network.session.GeyserSession;
+import org.geysermc.connector.network.translators.world.block.BlockStateValues;
+import org.geysermc.connector.utils.ChunkUtils;
+
+/**
+ * Does not implement BlockEntityTranslator because it's only a block entity in Bedrock
+ */
+public class NoteblockBlockEntityTranslator implements RequiresBlockState {
+
+ @Override
+ public boolean isBlock(BlockState blockState) {
+ return BlockStateValues.getNoteblockPitch(blockState) != -1;
+ }
+
+ public static void translate(GeyserSession session, Position position) {
+ BlockState blockState = ChunkUtils.CACHED_BLOCK_ENTITIES.get(position);
+ BlockEventPacket blockEventPacket = new BlockEventPacket();
+ blockEventPacket.setBlockPosition(Vector3i.from(position.getX(), position.getY(), position.getZ()));
+ blockEventPacket.setEventType(0);
+ blockEventPacket.setEventData(BlockStateValues.getNoteblockPitch(blockState));
+ session.sendUpstreamPacket(blockEventPacket);
+
+ ChunkUtils.CACHED_BLOCK_ENTITIES.remove(position);
+ }
+
+}
diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/PistonBlockEntityTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/PistonBlockEntityTranslator.java
new file mode 100644
index 00000000..2dffce24
--- /dev/null
+++ b/connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/PistonBlockEntityTranslator.java
@@ -0,0 +1,72 @@
+/*
+ * 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.world.block.entity;
+
+import com.github.steveice10.mc.protocol.data.game.world.block.BlockState;
+import com.nukkitx.math.vector.Vector3i;
+import com.nukkitx.nbt.CompoundTagBuilder;
+import com.nukkitx.nbt.tag.CompoundTag;
+import org.geysermc.connector.network.translators.world.block.BlockStateValues;
+
+/**
+ * Pistons are a special case where they are only a block entity on Bedrock.
+ */
+public class PistonBlockEntityTranslator {
+
+ /**
+ * Used in ChunkUtils to determine if the block is a piston.
+ * @param blockState Java BlockState of block.
+ * @return if block is a piston or not.
+ */
+ public static boolean isBlock(BlockState blockState) {
+ return BlockStateValues.getPistonValues().containsKey(blockState.getId());
+ }
+
+ /**
+ * Calculates the Nukkit CompoundTag to send to the client on chunk
+ * @param blockState Java BlockState of block.
+ * @param position Bedrock position of piston.
+ * @return Bedrock tag of piston.
+ */
+ public static CompoundTag getTag(BlockState blockState, Vector3i position) {
+ CompoundTagBuilder tagBuilder = CompoundTagBuilder.builder()
+ .intTag("x", position.getX())
+ .intTag("y", position.getY())
+ .intTag("z", position.getZ())
+ .byteTag("isMovable", (byte) 1)
+ .stringTag("id", "PistonArm");
+ if (BlockStateValues.getPistonValues().containsKey(blockState.getId())) {
+ boolean extended = BlockStateValues.getPistonValues().get(blockState.getId());
+ // 1f if extended, otherwise 0f
+ tagBuilder.floatTag("Progress", (extended) ? 1.0f : 0.0f);
+ // 1 if sticky, 0 if not
+ tagBuilder.byteTag("Sticky", (byte)((BlockStateValues.isStickyPiston(blockState)) ? 1 : 0));
+ }
+ return tagBuilder.buildRootTag();
+ }
+
+}
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/world/block/entity/RequiresBlockState.java
similarity index 95%
rename from connector/src/main/java/org/geysermc/connector/network/translators/block/entity/RequiresBlockState.java
rename to connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/RequiresBlockState.java
index ed8e6ede..4df7292a 100644
--- a/connector/src/main/java/org/geysermc/connector/network/translators/block/entity/RequiresBlockState.java
+++ b/connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/RequiresBlockState.java
@@ -23,7 +23,7 @@
* @link https://github.com/GeyserMC/Geyser
*/
-package org.geysermc.connector.network.translators.block.entity;
+package org.geysermc.connector.network.translators.world.block.entity;
import com.github.steveice10.mc.protocol.data.game.world.block.BlockState;
diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/ShulkerBoxBlockEntityTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/ShulkerBoxBlockEntityTranslator.java
new file mode 100644
index 00000000..373b963e
--- /dev/null
+++ b/connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/ShulkerBoxBlockEntityTranslator.java
@@ -0,0 +1,65 @@
+/*
+ * 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.world.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.world.block.BlockStateValues;
+
+import java.util.ArrayList;
+import java.util.List;
+
+@BlockEntity(name = "ShulkerBox", regex = "shulker_box")
+public class ShulkerBoxBlockEntityTranslator extends BlockEntityTranslator {
+
+ @Override
+ public List> translateTag(CompoundTag tag, BlockState blockState) {
+ List> tags = new ArrayList<>();
+
+ byte direction = BlockStateValues.getShulkerBoxDirection(blockState);
+ // Just in case...
+ if (direction == -1) direction = 1;
+ tags.add(new ByteTag("facing", direction));
+
+ 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("facing", (byte)1);
+ return tagBuilder.buildRootTag();
+ }
+}
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/world/block/entity/SignBlockEntityTranslator.java
similarity index 77%
rename from connector/src/main/java/org/geysermc/connector/network/translators/block/entity/SignBlockEntityTranslator.java
rename to connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/SignBlockEntityTranslator.java
index 74dcdd13..6c170462 100644
--- a/connector/src/main/java/org/geysermc/connector/network/translators/block/entity/SignBlockEntityTranslator.java
+++ b/connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/SignBlockEntityTranslator.java
@@ -23,7 +23,7 @@
* @link https://github.com/GeyserMC/Geyser
*/
-package org.geysermc.connector.network.translators.block.entity;
+package org.geysermc.connector.network.translators.world.block.entity;
import com.github.steveice10.mc.protocol.data.game.world.block.BlockState;
import com.github.steveice10.mc.protocol.data.message.Message;
@@ -31,29 +31,35 @@ 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 io.netty.util.internal.StringUtil;
import org.geysermc.connector.utils.MessageUtils;
import java.util.ArrayList;
import java.util.List;
-@BlockEntity(name = "Sign", delay = true, regex = "sign")
+@BlockEntity(name = "Sign", 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"), "");
+ StringBuilder signText = new StringBuilder();
+ for(int i = 0; i < 4; i++) {
+ int currentLine = i+1;
+ String signLine = getOrDefault(tag.getValue().get("Text" + currentLine), "");
+ signLine = MessageUtils.getBedrockMessage(Message.fromString(signLine));
- 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))
- ));
+ //Java allows up to 16+ characters on certain symbols.
+ if(signLine.length() >= 15 && (signLine.contains("-") || signLine.contains("="))) {
+ signLine = signLine.substring(0, 14);
+ }
+ signText.append(signLine);
+ signText.append("\n");
+ }
+
+ tags.add(new StringTag("Text", MessageUtils.getBedrockMessage(Message.fromString(signText.toString()))));
return tags;
}
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/world/block/entity/SkullBlockEntityTranslator.java
similarity index 91%
rename from connector/src/main/java/org/geysermc/connector/network/translators/block/entity/SkullBlockEntityTranslator.java
rename to connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/SkullBlockEntityTranslator.java
index 2380663b..9393f7bb 100644
--- a/connector/src/main/java/org/geysermc/connector/network/translators/block/entity/SkullBlockEntityTranslator.java
+++ b/connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/SkullBlockEntityTranslator.java
@@ -23,7 +23,7 @@
* @link https://github.com/GeyserMC/Geyser
*/
-package org.geysermc.connector.network.translators.block.entity;
+package org.geysermc.connector.network.translators.world.block.entity;
import com.github.steveice10.mc.protocol.data.game.world.block.BlockState;
import com.nukkitx.nbt.CompoundTagBuilder;
@@ -31,12 +31,12 @@ 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 org.geysermc.connector.network.translators.world.block.BlockStateValues;
import java.util.ArrayList;
import java.util.List;
-@BlockEntity(name = "Skull", delay = false, regex = "skull")
+@BlockEntity(name = "Skull", regex = "skull")
public class SkullBlockEntityTranslator extends BlockEntityTranslator implements RequiresBlockState {
@Override
diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/SpawnerBlockEntityTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/SpawnerBlockEntityTranslator.java
new file mode 100644
index 00000000..100dbddd
--- /dev/null
+++ b/connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/SpawnerBlockEntityTranslator.java
@@ -0,0 +1,105 @@
+/*
+ * 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.world.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.*;
+import org.geysermc.connector.entity.type.EntityType;
+
+import java.util.ArrayList;
+import java.util.List;
+
+@BlockEntity(name = "MobSpawner", regex = "mob_spawner")
+public class SpawnerBlockEntityTranslator extends BlockEntityTranslator {
+
+ @Override
+ public List> translateTag(CompoundTag tag, BlockState blockState) {
+ List> tags = new ArrayList<>();
+
+ if (tag.get("MaxNearbyEntities") != null) {
+ tags.add(new ShortTag("MaxNearbyEntities", (short) tag.get("MaxNearbyEntities").getValue()));
+ }
+
+ if (tag.get("RequiredPlayerRange") != null) {
+ tags.add(new ShortTag("RequiredPlayerRange", (short) tag.get("RequiredPlayerRange").getValue()));
+ }
+
+ if (tag.get("SpawnCount") != null) {
+ tags.add(new ShortTag("SpawnCount", (short) tag.get("SpawnCount").getValue()));
+ }
+
+ if (tag.get("MaxSpawnDelay") != null) {
+ tags.add(new ShortTag("MaxSpawnDelay", (short) tag.get("MaxSpawnDelay").getValue()));
+ }
+
+ if (tag.get("Delay") != null) {
+ tags.add(new ShortTag("Delay", (short) tag.get("Delay").getValue()));
+ }
+
+ if (tag.get("SpawnRange") != null) {
+ tags.add(new ShortTag("SpawnRange", (short) tag.get("SpawnRange").getValue()));
+ }
+
+ if (tag.get("MinSpawnDelay") != null) {
+ tags.add(new ShortTag("MinSpawnDelay", (short) tag.get("MinSpawnDelay").getValue()));
+ }
+
+ if (tag.get("SpawnData") != null) {
+ CompoundTag spawnData = tag.get("SpawnData");
+ String entityID = (String) spawnData.get("id").getValue();
+ tags.add(new StringTag("EntityIdentifier", entityID));
+
+ EntityType type = EntityType.getFromIdentifier(entityID);
+ if (type != null) {
+ tags.add(new FloatTag("DisplayEntityWidth", type.getWidth()));
+ tags.add(new FloatTag("DisplayEntityHeight", type.getHeight()));
+ tags.add(new FloatTag("DisplayEntityScale", 1.0f));
+ }
+ }
+
+ tags.add(new StringTag("id", "MobSpawner"));
+ tags.add(new ByteTag("isMovable", (byte) 1));
+
+ 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("isMovable", (byte) 1)
+ .stringTag("id", "MobSpawner");
+
+ return tagBuilder.buildRootTag();
+ }
+}
diff --git a/connector/src/main/java/org/geysermc/connector/world/chunk/BlockStorage.java b/connector/src/main/java/org/geysermc/connector/network/translators/world/chunk/BlockStorage.java
similarity index 68%
rename from connector/src/main/java/org/geysermc/connector/world/chunk/BlockStorage.java
rename to connector/src/main/java/org/geysermc/connector/network/translators/world/chunk/BlockStorage.java
index 360fdea4..5995ecf9 100644
--- a/connector/src/main/java/org/geysermc/connector/world/chunk/BlockStorage.java
+++ b/connector/src/main/java/org/geysermc/connector/network/translators/world/chunk/BlockStorage.java
@@ -1,21 +1,37 @@
/*
* Copyright (c) 2019-2020 GeyserMC. http://geysermc.org
*
- * This code in this file is derived from NukkitX and permission has
- * been granted to us allowing the usage of it in Geyser.
+ * 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
*
- * Copyright (C) 2020 The NukkitX Project
- * https://github.com/NukkitX/Nukkit
*/
-package org.geysermc.connector.world.chunk;
+package org.geysermc.connector.network.translators.world.chunk;
import com.nukkitx.network.VarInts;
import io.netty.buffer.ByteBuf;
import it.unimi.dsi.fastutil.ints.IntArrayList;
import it.unimi.dsi.fastutil.ints.IntList;
-import org.geysermc.connector.world.chunk.bitarray.BitArray;
-import org.geysermc.connector.world.chunk.bitarray.BitArrayVersion;
+import org.geysermc.connector.network.translators.world.chunk.bitarray.BitArray;
+import org.geysermc.connector.network.translators.world.chunk.bitarray.BitArrayVersion;
import java.util.function.IntConsumer;
diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/world/chunk/ChunkPosition.java b/connector/src/main/java/org/geysermc/connector/network/translators/world/chunk/ChunkPosition.java
new file mode 100644
index 00000000..a51755d0
--- /dev/null
+++ b/connector/src/main/java/org/geysermc/connector/network/translators/world/chunk/ChunkPosition.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.world.chunk;
+
+import com.github.steveice10.mc.protocol.data.game.entity.metadata.Position;
+import lombok.AllArgsConstructor;
+import lombok.EqualsAndHashCode;
+import lombok.Getter;
+import lombok.Setter;
+
+@Getter
+@Setter
+@AllArgsConstructor
+@EqualsAndHashCode
+public class ChunkPosition {
+
+ private int x;
+ private int z;
+
+ public Position getBlock(int x, int y, int z) {
+ return new Position((this.x << 4) + x, y, (this.z << 4) + z);
+ }
+
+ public Position getChunkBlock(int x, int y, int z) {
+ int chunkX = x & 15;
+ int chunkY = y & 15;
+ int chunkZ = z & 15;
+ return new Position(chunkX, chunkY, chunkZ);
+ }
+}
diff --git a/connector/src/main/java/org/geysermc/connector/world/chunk/ChunkSection.java b/connector/src/main/java/org/geysermc/connector/network/translators/world/chunk/ChunkSection.java
similarity index 77%
rename from connector/src/main/java/org/geysermc/connector/world/chunk/ChunkSection.java
rename to connector/src/main/java/org/geysermc/connector/network/translators/world/chunk/ChunkSection.java
index c160d11b..4ede0c25 100644
--- a/connector/src/main/java/org/geysermc/connector/world/chunk/ChunkSection.java
+++ b/connector/src/main/java/org/geysermc/connector/network/translators/world/chunk/ChunkSection.java
@@ -1,14 +1,30 @@
/*
* Copyright (c) 2019-2020 GeyserMC. http://geysermc.org
*
- * This code in this file is derived from NukkitX and permission has
- * been granted to us allowing the usage of it in Geyser.
+ * 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
*
- * Copyright (C) 2020 The NukkitX Project
- * https://github.com/NukkitX/Nukkit
*/
-package org.geysermc.connector.world.chunk;
+package org.geysermc.connector.network.translators.world.chunk;
import com.nukkitx.network.util.Preconditions;
import io.netty.buffer.ByteBuf;
diff --git a/connector/src/main/java/org/geysermc/connector/world/chunk/NibbleArray.java b/connector/src/main/java/org/geysermc/connector/network/translators/world/chunk/NibbleArray.java
similarity index 62%
rename from connector/src/main/java/org/geysermc/connector/world/chunk/NibbleArray.java
rename to connector/src/main/java/org/geysermc/connector/network/translators/world/chunk/NibbleArray.java
index 8da068f7..08303e18 100644
--- a/connector/src/main/java/org/geysermc/connector/world/chunk/NibbleArray.java
+++ b/connector/src/main/java/org/geysermc/connector/network/translators/world/chunk/NibbleArray.java
@@ -1,14 +1,30 @@
/*
* Copyright (c) 2019-2020 GeyserMC. http://geysermc.org
*
- * This code in this file is derived from NukkitX and permission has
- * been granted to us allowing the usage of it in Geyser.
+ * 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
*
- * Copyright (C) 2020 The NukkitX Project
- * https://github.com/NukkitX/Nukkit
*/
-package org.geysermc.connector.world.chunk;
+package org.geysermc.connector.network.translators.world.chunk;
import com.nukkitx.network.util.Preconditions;
diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/world/chunk/bitarray/BitArray.java b/connector/src/main/java/org/geysermc/connector/network/translators/world/chunk/bitarray/BitArray.java
new file mode 100644
index 00000000..728fe237
--- /dev/null
+++ b/connector/src/main/java/org/geysermc/connector/network/translators/world/chunk/bitarray/BitArray.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.world.chunk.bitarray;
+
+public interface BitArray {
+
+ void set(int index, int value);
+
+ int get(int index);
+
+ int size();
+
+ int[] getWords();
+
+ BitArrayVersion getVersion();
+
+ BitArray copy();
+}
\ No newline at end of file
diff --git a/connector/src/main/java/org/geysermc/connector/world/chunk/bitarray/BitArrayVersion.java b/connector/src/main/java/org/geysermc/connector/network/translators/world/chunk/bitarray/BitArrayVersion.java
similarity index 60%
rename from connector/src/main/java/org/geysermc/connector/world/chunk/bitarray/BitArrayVersion.java
rename to connector/src/main/java/org/geysermc/connector/network/translators/world/chunk/bitarray/BitArrayVersion.java
index f6e3ef5e..be91f13d 100644
--- a/connector/src/main/java/org/geysermc/connector/world/chunk/bitarray/BitArrayVersion.java
+++ b/connector/src/main/java/org/geysermc/connector/network/translators/world/chunk/bitarray/BitArrayVersion.java
@@ -1,14 +1,30 @@
/*
* Copyright (c) 2019-2020 GeyserMC. http://geysermc.org
*
- * This code in this file is derived from NukkitX and permission has
- * been granted to us allowing the usage of it in Geyser.
+ * 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
*
- * Copyright (C) 2020 The NukkitX Project
- * https://github.com/NukkitX/Nukkit
*/
-package org.geysermc.connector.world.chunk.bitarray;
+package org.geysermc.connector.network.translators.world.chunk.bitarray;
import org.geysermc.connector.utils.MathUtils;
diff --git a/connector/src/main/java/org/geysermc/connector/world/chunk/bitarray/PaddedBitArray.java b/connector/src/main/java/org/geysermc/connector/network/translators/world/chunk/bitarray/PaddedBitArray.java
similarity index 65%
rename from connector/src/main/java/org/geysermc/connector/world/chunk/bitarray/PaddedBitArray.java
rename to connector/src/main/java/org/geysermc/connector/network/translators/world/chunk/bitarray/PaddedBitArray.java
index d2f9393a..36a97a30 100644
--- a/connector/src/main/java/org/geysermc/connector/world/chunk/bitarray/PaddedBitArray.java
+++ b/connector/src/main/java/org/geysermc/connector/network/translators/world/chunk/bitarray/PaddedBitArray.java
@@ -1,14 +1,30 @@
/*
* Copyright (c) 2019-2020 GeyserMC. http://geysermc.org
*
- * This code in this file is derived from NukkitX and permission has
- * been granted to us allowing the usage of it in Geyser.
+ * 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
*
- * Copyright (C) 2020 The NukkitX Project
- * https://github.com/NukkitX/Nukkit
*/
-package org.geysermc.connector.world.chunk.bitarray;
+package org.geysermc.connector.network.translators.world.chunk.bitarray;
import com.nukkitx.network.util.Preconditions;
import org.geysermc.connector.utils.MathUtils;
diff --git a/connector/src/main/java/org/geysermc/connector/world/chunk/bitarray/Pow2BitArray.java b/connector/src/main/java/org/geysermc/connector/network/translators/world/chunk/bitarray/Pow2BitArray.java
similarity index 67%
rename from connector/src/main/java/org/geysermc/connector/world/chunk/bitarray/Pow2BitArray.java
rename to connector/src/main/java/org/geysermc/connector/network/translators/world/chunk/bitarray/Pow2BitArray.java
index bcb878a7..c9bd2764 100644
--- a/connector/src/main/java/org/geysermc/connector/world/chunk/bitarray/Pow2BitArray.java
+++ b/connector/src/main/java/org/geysermc/connector/network/translators/world/chunk/bitarray/Pow2BitArray.java
@@ -1,14 +1,30 @@
/*
* Copyright (c) 2019-2020 GeyserMC. http://geysermc.org
*
- * This code in this file is derived from NukkitX and permission has
- * been granted to us allowing the usage of it in Geyser.
+ * 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
*
- * Copyright (C) 2020 The NukkitX Project
- * https://github.com/NukkitX/Nukkit
*/
-package org.geysermc.connector.world.chunk.bitarray;
+package org.geysermc.connector.network.translators.world.chunk.bitarray;
import com.nukkitx.network.util.Preconditions;
import org.geysermc.connector.utils.MathUtils;
diff --git a/connector/src/main/java/org/geysermc/connector/ping/GeyserLegacyPingPassthrough.java b/connector/src/main/java/org/geysermc/connector/ping/GeyserLegacyPingPassthrough.java
new file mode 100644
index 00000000..ea0d67c2
--- /dev/null
+++ b/connector/src/main/java/org/geysermc/connector/ping/GeyserLegacyPingPassthrough.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.ping;
+
+import com.github.steveice10.mc.protocol.MinecraftConstants;
+import com.github.steveice10.mc.protocol.MinecraftProtocol;
+import com.github.steveice10.mc.protocol.data.SubProtocol;
+import com.github.steveice10.mc.protocol.data.status.handler.ServerInfoHandler;
+import com.github.steveice10.packetlib.Client;
+import com.github.steveice10.packetlib.tcp.TcpSessionFactory;
+import org.geysermc.common.ping.GeyserPingInfo;
+import org.geysermc.connector.GeyserConfiguration;
+import org.geysermc.connector.GeyserConnector;
+import org.geysermc.connector.GeyserLogger;
+
+import java.util.concurrent.TimeUnit;
+
+public class GeyserLegacyPingPassthrough implements IGeyserPingPassthrough, Runnable {
+
+ private GeyserConnector connector;
+
+ public GeyserLegacyPingPassthrough(GeyserConnector connector) {
+ this.connector = connector;
+ this.pingInfo = new GeyserPingInfo(null, 0, 0);
+ }
+
+ private GeyserPingInfo pingInfo;
+
+ private Client client;
+
+ /**
+ * Start legacy ping passthrough thread
+ * @param connector GeyserConnector
+ * @return GeyserPingPassthrough, or null if not initialized
+ */
+ public static IGeyserPingPassthrough init(GeyserConnector connector) {
+ if (connector.getConfig().isPassthroughMotd() || connector.getConfig().isPassthroughPlayerCounts()) {
+ GeyserLegacyPingPassthrough pingPassthrough = new GeyserLegacyPingPassthrough(connector);
+ // Ensure delay is not zero
+ int interval = (connector.getConfig().getPingPassthroughInterval() == 0) ? 1 : connector.getConfig().getPingPassthroughInterval();
+ connector.getLogger().debug("Scheduling ping passthrough at an interval of " + interval + " second(s).");
+ connector.getGeneralThreadPool().scheduleAtFixedRate(pingPassthrough, 1, interval, TimeUnit.SECONDS);
+ return pingPassthrough;
+ }
+ return null;
+ }
+
+ @Override
+ public GeyserPingInfo getPingInformation() {
+ return pingInfo;
+ }
+
+ @Override
+ public void run() {
+ try {
+ this.client = new Client(connector.getConfig().getRemote().getAddress(), connector.getConfig().getRemote().getPort(), new MinecraftProtocol(SubProtocol.STATUS), new TcpSessionFactory());
+ this.client.getSession().setFlag(MinecraftConstants.SERVER_INFO_HANDLER_KEY, (ServerInfoHandler) (session, info) -> {
+ this.pingInfo = new GeyserPingInfo(info.getDescription().getFullText(), info.getPlayerInfo().getOnlinePlayers(), info.getPlayerInfo().getMaxPlayers());
+ this.client.getSession().disconnect(null);
+ });
+
+ client.getSession().connect();
+ } catch (Exception ex) {
+ ex.printStackTrace();
+ }
+ }
+}
diff --git a/connector/src/main/java/org/geysermc/connector/ping/IGeyserPingPassthrough.java b/connector/src/main/java/org/geysermc/connector/ping/IGeyserPingPassthrough.java
new file mode 100644
index 00000000..7bc842df
--- /dev/null
+++ b/connector/src/main/java/org/geysermc/connector/ping/IGeyserPingPassthrough.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.ping;
+
+import org.geysermc.common.ping.GeyserPingInfo;
+
+/**
+ * Interface that retrieves ping passthrough information from the Java server
+ */
+public interface IGeyserPingPassthrough {
+
+ /**
+ * Get the MOTD of the server displayed on the multiplayer screen
+ * @return string of the MOTD
+ */
+ GeyserPingInfo getPingInformation();
+
+}
diff --git a/connector/src/main/java/org/geysermc/connector/scoreboard/Scoreboard.java b/connector/src/main/java/org/geysermc/connector/scoreboard/Scoreboard.java
index 03ccb2fd..59d9b25f 100644
--- a/connector/src/main/java/org/geysermc/connector/scoreboard/Scoreboard.java
+++ b/connector/src/main/java/org/geysermc/connector/scoreboard/Scoreboard.java
@@ -187,7 +187,7 @@ public class Scoreboard {
if (objective.getUpdateType() == REMOVE || update) {
RemoveObjectivePacket removeObjectivePacket = new RemoveObjectivePacket();
removeObjectivePacket.setObjectiveId(objective.getObjectiveName());
- session.getUpstream().sendPacket(removeObjectivePacket);
+ session.sendUpstreamPacket(removeObjectivePacket);
if (objective.getUpdateType() == REMOVE) {
objectives.remove(objective.getObjectiveName()); // now we can deregister
}
@@ -199,7 +199,7 @@ public class Scoreboard {
displayObjectivePacket.setCriteria("dummy");
displayObjectivePacket.setDisplaySlot(objective.getDisplaySlot());
displayObjectivePacket.setSortOrder(1); // ??
- session.getUpstream().sendPacket(displayObjectivePacket);
+ session.sendUpstreamPacket(displayObjectivePacket);
}
objective.setUpdateType(NOTHING);
}
@@ -208,21 +208,21 @@ public class Scoreboard {
SetScorePacket setScorePacket = new SetScorePacket();
setScorePacket.setAction(SetScorePacket.Action.REMOVE);
setScorePacket.setInfos(removeScores);
- session.getUpstream().sendPacket(setScorePacket);
+ session.sendUpstreamPacket(setScorePacket);
}
if (!addScores.isEmpty()) {
SetScorePacket setScorePacket = new SetScorePacket();
setScorePacket.setAction(SetScorePacket.Action.SET);
setScorePacket.setInfos(addScores);
- session.getUpstream().sendPacket(setScorePacket);
+ session.sendUpstreamPacket(setScorePacket);
}
}
public void despawnObjective(Objective objective) {
RemoveObjectivePacket removeObjectivePacket = new RemoveObjectivePacket();
removeObjectivePacket.setObjectiveId(objective.getObjectiveName());
- session.getUpstream().sendPacket(removeObjectivePacket);
+ session.sendUpstreamPacket(removeObjectivePacket);
objectives.remove(objective.getDisplayName());
List toRemove = new ArrayList<>();
@@ -238,7 +238,7 @@ public class Scoreboard {
SetScorePacket setScorePacket = new SetScorePacket();
setScorePacket.setAction(SetScorePacket.Action.REMOVE);
setScorePacket.setInfos(toRemove);
- session.getUpstream().sendPacket(setScorePacket);
+ session.sendUpstreamPacket(setScorePacket);
}
}
diff --git a/connector/src/main/java/org/geysermc/connector/thread/PingPassthroughThread.java b/connector/src/main/java/org/geysermc/connector/thread/PingPassthroughThread.java
deleted file mode 100644
index fa87809b..00000000
--- a/connector/src/main/java/org/geysermc/connector/thread/PingPassthroughThread.java
+++ /dev/null
@@ -1,65 +0,0 @@
-/*
- * 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.thread;
-
-import com.github.steveice10.mc.protocol.MinecraftConstants;
-import com.github.steveice10.mc.protocol.MinecraftProtocol;
-import com.github.steveice10.mc.protocol.data.SubProtocol;
-import com.github.steveice10.mc.protocol.data.status.ServerStatusInfo;
-import com.github.steveice10.mc.protocol.data.status.handler.ServerInfoHandler;
-import com.github.steveice10.packetlib.Client;
-import com.github.steveice10.packetlib.tcp.TcpSessionFactory;
-import lombok.Getter;
-import org.geysermc.connector.GeyserConnector;
-
-public class PingPassthroughThread implements Runnable {
-
- private GeyserConnector connector;
-
- public PingPassthroughThread(GeyserConnector connector) {
- this.connector = connector;
- }
-
- @Getter
- private ServerStatusInfo info;
-
- private Client client;
-
- @Override
- public void run() {
- try {
- this.client = new Client(connector.getConfig().getRemote().getAddress(), connector.getConfig().getRemote().getPort(), new MinecraftProtocol(SubProtocol.STATUS), new TcpSessionFactory());
- this.client.getSession().setFlag(MinecraftConstants.SERVER_INFO_HANDLER_KEY, (ServerInfoHandler) (session, info) -> {
- this.info = info;
- this.client.getSession().disconnect(null);
- });
-
- client.getSession().connect();
- } catch (Exception ex) {
- ex.printStackTrace();
- }
- }
-}
diff --git a/connector/src/main/java/org/geysermc/connector/utils/BedrockMapIcon.java b/connector/src/main/java/org/geysermc/connector/utils/BedrockMapIcon.java
new file mode 100644
index 00000000..f3ee956b
--- /dev/null
+++ b/connector/src/main/java/org/geysermc/connector/utils/BedrockMapIcon.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.utils;
+
+import com.github.steveice10.mc.protocol.data.game.world.map.MapIconType;
+import lombok.Getter;
+
+public enum BedrockMapIcon {
+ ICON_WHITE_ARROW(MapIconType.WHITE_ARROW, 0),
+ ICON_ITEM_FRAME(MapIconType.GREEN_ARROW, 7),
+ ICON_RED_ARROW(MapIconType.RED_ARROW, 2),
+ ICON_BLUE_ARROW(MapIconType.BLUE_ARROW, 3),
+ ICON_TREASURE_MARKER(MapIconType.TREASURE_MARKER, 4),
+ ICON_RED_POINTER(MapIconType.RED_POINTER, 5),
+ ICON_WHITE_CIRCLE(MapIconType.WHITE_CIRCLE, 6),
+ ICON_SMALL_WHITE_CIRCLE(MapIconType.SMALL_WHITE_CIRCLE, 13),
+ ICON_MANSION(MapIconType.MANSION, 14),
+ ICON_TEMPLE(MapIconType.TEMPLE, 15),
+ ICON_WHITE_BANNER(MapIconType.WHITE_BANNER, 13, 255, 255, 255),
+ ICON_ORANGE_BANNER(MapIconType.ORANGE_BANNER, 13, 249, 128, 29),
+ ICON_MAGENTA_BANNER(MapIconType.MAGENTA_BANNER, 13, 199, 78, 189),
+ ICON_LIGHT_BLUE_BANNER(MapIconType.LIGHT_BLUE_BANNER, 13, 58, 179, 218),
+ ICON_YELLOW_BANNER(MapIconType.YELLOW_BANNER, 13, 254, 216, 61),
+ ICON_LIME_BANNER(MapIconType.LIME_BANNER, 13, 128, 199, 31),
+ ICON_PINK_BANNER(MapIconType.PINK_BANNER, 13, 243, 139, 170),
+ ICON_GRAY_BANNER(MapIconType.GRAY_BANNER, 13, 71, 79, 82),
+ ICON_LIGHT_GRAY_BANNER(MapIconType.LIGHT_GRAY_BANNER, 13, 157, 157, 151),
+ ICON_CYAN_BANNER(MapIconType.CYAN_BANNER, 13, 22, 156, 156),
+ ICON_PURPLE_BANNER(MapIconType.PURPLE_BANNER, 13, 137, 50, 184),
+ ICON_BLUE_BANNER(MapIconType.BLUE_BANNER, 13, 60, 68, 170),
+ ICON_BROWN_BANNER(MapIconType.BROWN_BANNER, 13, 131, 84, 50),
+ ICON_GREEN_BANNER(MapIconType.GREEN_BANNER, 13, 94, 124, 22),
+ ICON_RED_BANNER(MapIconType.RED_BANNER, 13, 176, 46, 38),
+ ICON_BLACK_BANNER(MapIconType.BLACK_BANNER, 13, 29, 29, 33);
+
+ private static final BedrockMapIcon[] VALUES = values();
+
+ private MapIconType iconType;
+
+ @Getter
+ private int iconID;
+
+ private int red;
+ private int green;
+ private int blue;
+
+ BedrockMapIcon(MapIconType iconType, int iconID) {
+ this.iconType = iconType;
+ this.iconID = iconID;
+
+ this.red = 255;
+ this.green = 255;
+ this.blue = 255;
+ }
+
+ BedrockMapIcon(MapIconType iconType, int iconID, int red, int green, int blue) {
+ this.iconType = iconType;
+ this.iconID = iconID;
+
+ this.red = red;
+ this.green = green;
+ this.blue = blue;
+ }
+
+ /**
+ * Get the BedrockMapIcon for the Java MapIconType
+ *
+ * @param iconType A MapIconType
+ * @return The mapping for a BedrockMapIcon
+ */
+ public static BedrockMapIcon fromType(MapIconType iconType) {
+ for (BedrockMapIcon icon : VALUES) {
+ if (icon.iconType.equals(iconType)) {
+ return icon;
+ }
+ }
+
+ return null;
+ }
+
+ /**
+ * Get the ARGB value of a BedrockMapIcon
+ *
+ * @return ARGB as an int
+ */
+ public int toARGB() {
+ int alpha = 255;
+
+ return ((alpha & 0xFF) << 24) |
+ ((red & 0xFF) << 16) |
+ ((green & 0xFF) << 8) |
+ ((blue & 0xFF) << 0);
+ }
+}
diff --git a/connector/src/main/java/org/geysermc/connector/utils/BlockEntityUtils.java b/connector/src/main/java/org/geysermc/connector/utils/BlockEntityUtils.java
index 0dcd13ad..038084c3 100644
--- a/connector/src/main/java/org/geysermc/connector/utils/BlockEntityUtils.java
+++ b/connector/src/main/java/org/geysermc/connector/utils/BlockEntityUtils.java
@@ -3,20 +3,28 @@ 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;
+import org.geysermc.connector.network.translators.world.block.entity.BlockEntityTranslator;
public class BlockEntityUtils {
- private static final BlockEntityTranslator EMPTY_TRANSLATOR = Translators.getBlockEntityTranslators().get("Empty");
+ private static final BlockEntityTranslator EMPTY_TRANSLATOR = BlockEntityTranslator.BLOCK_ENTITY_TRANSLATORS.get("Empty");
public static String getBedrockBlockEntityId(String id) {
- // This is the only exception when it comes to block entity ids
+ // These are the only exceptions when it comes to block entity ids
if (id.contains("piston_head"))
return "PistonArm";
+ if (id.contains("trapped_chest"))
+ return "Chest";
+
+ if (id.contains("EnderChest"))
+ return "EnderChest";
+
+ if (id.contains("enchanting_table")) {
+ return "EnchantTable";
+ }
+
id = id.toLowerCase()
.replace("minecraft:", "")
.replace("_", " ");
@@ -30,7 +38,7 @@ public class BlockEntityUtils {
}
public static BlockEntityTranslator getBlockEntityTranslator(String name) {
- BlockEntityTranslator blockEntityTranslator = Translators.getBlockEntityTranslators().get(name);
+ BlockEntityTranslator blockEntityTranslator = BlockEntityTranslator.BLOCK_ENTITY_TRANSLATORS.get(name);
if (blockEntityTranslator == null) {
return EMPTY_TRANSLATOR;
}
@@ -39,9 +47,13 @@ public class BlockEntityUtils {
}
public static void updateBlockEntity(GeyserSession session, com.nukkitx.nbt.tag.CompoundTag blockEntity, Position position) {
+ updateBlockEntity(session, blockEntity, Vector3i.from(position.getX(), position.getY(), position.getZ()));
+ }
+
+ public static void updateBlockEntity(GeyserSession session, com.nukkitx.nbt.tag.CompoundTag blockEntity, Vector3i position) {
BlockEntityDataPacket blockEntityPacket = new BlockEntityDataPacket();
- blockEntityPacket.setBlockPosition(Vector3i.from(position.getX(), position.getY(), position.getZ()));
+ blockEntityPacket.setBlockPosition(position);
blockEntityPacket.setData(blockEntity);
- session.getUpstream().sendPacket(blockEntityPacket);
+ session.sendUpstreamPacket(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
index 34287073..6b9ab8ca 100644
--- a/connector/src/main/java/org/geysermc/connector/utils/BlockUtils.java
+++ b/connector/src/main/java/org/geysermc/connector/utils/BlockUtils.java
@@ -26,12 +26,15 @@
package org.geysermc.connector.utils;
import com.github.steveice10.mc.protocol.data.game.entity.Effect;
+import com.github.steveice10.mc.protocol.data.game.entity.metadata.ItemStack;
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.session.GeyserSession;
+import org.geysermc.connector.network.translators.world.block.BlockTranslator;
import org.geysermc.connector.network.translators.item.ItemEntry;
import org.geysermc.connector.network.translators.item.ToolItemEntry;
+import java.util.Optional;
+
public class BlockUtils {
private static boolean correctTool(String blockToolType, String itemToolType) {
@@ -63,8 +66,8 @@ public class BlockUtils {
//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*/) {
+ String toolType, boolean isWoolBlock, boolean isCobweb, int toolEfficiencyLevel, int hasteLevel, int miningFatigueLevel,
+ boolean insideOfWaterWithoutAquaAffinity, boolean outOfWaterButNotOnGround, boolean insideWaterAndNotOnGround) {
double baseTime = ((correctTool || canHarvestWithHand) ? 1.5 : 5.0) * blockHardness;
double speed = 1.0 / baseTime;
@@ -93,13 +96,13 @@ public class BlockUtils {
break;
}
- //if (insideOfWaterWithoutAquaAffinity) speed *= 0.2;
- //if (outOfWaterButNotOnGround) speed *= 0.2;
- // else if insideWaterAndNotOnGround speed *= 0.2;
+ if (insideOfWaterWithoutAquaAffinity) speed *= 0.2;
+ if (outOfWaterButNotOnGround) speed *= 0.2;
+ if (insideWaterAndNotOnGround) speed *= 0.2;
return 1.0 / speed;
}
- public static double getBreakTime(double blockHardness, int blockId, ItemEntry item, CompoundTag nbtData, PlayerEntity player) {
+ public static double getBreakTime(double blockHardness, int blockId, ItemEntry item, CompoundTag nbtData, GeyserSession session) {
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, "");
@@ -114,15 +117,25 @@ public class BlockUtils {
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);
+ int hasteLevel = 0;
+ int miningFatigueLevel = 0;
- // 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);
+ if (session == null) {
+ return calculateBreakTime(blockHardness, toolTier, canHarvestWithHand, correctTool, toolType, isWoolBlock, isCobweb, toolEfficiencyLevel, hasteLevel, miningFatigueLevel, false, false, false);
+ }
+
+ hasteLevel = session.getPlayerEntity().getEffectCache().getEffectLevel(Effect.FASTER_DIG);
+ miningFatigueLevel = session.getPlayerEntity().getEffectCache().getEffectLevel(Effect.SLOWER_DIG);
+
+ boolean isInWater = session.getConnector().getConfig().isCacheChunks()
+ && BlockTranslator.getBedrockBlockId(session.getConnector().getWorldManager().getBlockAt(session, session.getPlayerEntity().getPosition().toInt())) == BlockTranslator.BEDROCK_WATER_ID;
+
+ boolean insideOfWaterWithoutAquaAffinity = isInWater &&
+ ItemUtils.getEnchantmentLevel(Optional.ofNullable(session.getInventory().getItem(5)).map(ItemStack::getNbt).orElse(null), "minecraft:aqua_affinity") < 1;
+
+ boolean outOfWaterButNotOnGround = (!isInWater) && (!session.getPlayerEntity().isOnGround());
+ boolean insideWaterNotOnGround = isInWater && !session.getPlayerEntity().isOnGround();
+ return calculateBreakTime(blockHardness, toolTier, canHarvestWithHand, correctTool, toolType, isWoolBlock, isCobweb, toolEfficiencyLevel, hasteLevel, miningFatigueLevel, insideOfWaterWithoutAquaAffinity, outOfWaterButNotOnGround, insideWaterNotOnGround);
}
}
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 d496215a..010a87af 100644
--- a/connector/src/main/java/org/geysermc/connector/utils/ChunkUtils.java
+++ b/connector/src/main/java/org/geysermc/connector/utils/ChunkUtils.java
@@ -32,26 +32,31 @@ 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 com.nukkitx.nbt.CompoundTagBuilder;
+import com.nukkitx.nbt.NbtUtils;
+import com.nukkitx.nbt.stream.NBTOutputStream;
+import com.nukkitx.protocol.bedrock.packet.*;
import it.unimi.dsi.fastutil.objects.Object2IntMap;
import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap;
-
+import it.unimi.dsi.fastutil.objects.ObjectArrayList;
import lombok.Getter;
import org.geysermc.connector.GeyserConnector;
+import org.geysermc.connector.entity.Entity;
+import org.geysermc.connector.entity.ItemFrameEntity;
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 org.geysermc.connector.network.translators.world.block.BlockStateValues;
+import org.geysermc.connector.network.translators.world.block.entity.*;
+import org.geysermc.connector.network.translators.world.block.BlockTranslator;
+import org.geysermc.connector.network.translators.world.chunk.ChunkPosition;
+import org.geysermc.connector.network.translators.world.chunk.ChunkSection;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
-import static org.geysermc.connector.network.translators.block.BlockTranslator.BEDROCK_WATER_ID;
+import static org.geysermc.connector.network.translators.world.block.BlockTranslator.AIR;
+import static org.geysermc.connector.network.translators.world.block.BlockTranslator.BEDROCK_WATER_ID;
public class ChunkUtils {
@@ -60,6 +65,23 @@ public class ChunkUtils {
*/
public static final Map CACHED_BLOCK_ENTITIES = new HashMap<>();
+ private static final com.nukkitx.nbt.tag.CompoundTag EMPTY_TAG = CompoundTagBuilder.builder().buildRootTag();
+ public static final byte[] EMPTY_LEVEL_CHUNK_DATA;
+
+ static {
+ try (ByteArrayOutputStream outputStream = new ByteArrayOutputStream()) {
+ outputStream.write(new byte[258]); // Biomes + Border Size + Extra Data Size
+
+ try (NBTOutputStream stream = NbtUtils.createNetworkWriter(outputStream)) {
+ stream.write(EMPTY_TAG);
+ }
+
+ EMPTY_LEVEL_CHUNK_DATA = outputStream.toByteArray();
+ }catch (IOException e) {
+ throw new AssertionError("Unable to generate empty level chunk data");
+ }
+ }
+
public static ChunkData translateToBedrock(Column column) {
ChunkData chunkData = new ChunkData();
Chunk[] chunks = column.getChunks();
@@ -69,6 +91,9 @@ public class ChunkUtils {
// Temporarily stores positions of BlockState values per chunk load
Map blockEntityPositions = new HashMap<>();
+ // Temporarily stores compound tags of Bedrock-only block entities
+ ObjectArrayList bedrockOnlyBlockEntities = new ObjectArrayList<>();
+
for (int chunkY = 0; chunkY < chunks.length; chunkY++) {
chunkData.sections[chunkY] = new ChunkSection();
Chunk chunk = chunks[chunkY];
@@ -85,19 +110,17 @@ public class ChunkUtils {
// 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);
+ }
+
+ section.getBlockStorageArray()[0].setFullBlock(ChunkSection.blockPosition(x, y, z), id);
+
+ // Check if block is piston or flower - only block entities in Bedrock
+ if (BlockStateValues.getFlowerPotValues().containsKey(blockState.getId()) ||
+ BlockStateValues.getPistonValues().containsKey(blockState.getId())) {
+ Position pos = new ChunkPosition(column.getX(), column.getZ()).getBlock(x, (chunkY << 4) + y, z);
+ bedrockOnlyBlockEntities.add(BedrockOnlyBlockEntity.getTag(Vector3i.from(pos.getX(), pos.getY(), pos.getZ()), blockState));
}
if (BlockTranslator.isWaterlogged(blockState)) {
@@ -109,8 +132,9 @@ 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++) {
+ com.nukkitx.nbt.tag.CompoundTag[] bedrockBlockEntities = new com.nukkitx.nbt.tag.CompoundTag[blockEntities.length + bedrockOnlyBlockEntities.size()];
+ int i = 0;
+ while (i < blockEntities.length) {
CompoundTag tag = blockEntities[i];
String tagName;
if (!tag.contains("id")) {
@@ -122,8 +146,14 @@ public class ChunkUtils {
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()));
+ Position pos = new Position((int) tag.get("x").getValue(), (int) tag.get("y").getValue(), (int) tag.get("z").getValue());
+ BlockState blockState = blockEntityPositions.get(pos);
bedrockBlockEntities[i] = blockEntityTranslator.getBlockEntityTag(tagName, tag, blockState);
+ i++;
+ }
+ for (com.nukkitx.nbt.tag.CompoundTag tag : bedrockOnlyBlockEntities) {
+ bedrockBlockEntities[i] = tag;
+ i++;
}
chunkData.blockEntities = bedrockBlockEntities;
@@ -138,7 +168,7 @@ public class ChunkUtils {
NetworkChunkPublisherUpdatePacket chunkPublisherUpdatePacket = new NetworkChunkPublisherUpdatePacket();
chunkPublisherUpdatePacket.setPosition(position);
chunkPublisherUpdatePacket.setRadius(session.getRenderDistance() << 4);
- session.getUpstream().sendPacket(chunkPublisherUpdatePacket);
+ session.sendUpstreamPacket(chunkPublisherUpdatePacket);
session.setLastChunkPosition(newChunkPos);
}
@@ -150,6 +180,19 @@ public class ChunkUtils {
}
public static void updateBlock(GeyserSession session, BlockState blockState, Vector3i position) {
+ // Checks for item frames so they aren't tripped up and removed
+ if (ItemFrameEntity.positionContainsItemFrame(session, position) && blockState.equals(AIR)) {
+ ((ItemFrameEntity) session.getEntityCache().getEntityByJavaId(ItemFrameEntity.getItemFrameEntityId(session, position))).updateBlock(session);
+ return;
+ } else if (ItemFrameEntity.positionContainsItemFrame(session, position)) {
+ Entity entity = session.getEntityCache().getEntityByJavaId(ItemFrameEntity.getItemFrameEntityId(session, position));
+ if (entity != null) {
+ session.getEntityCache().removeEntity(entity, false);
+ } else {
+ ItemFrameEntity.removePosition(session, position);
+ }
+ }
+
int blockId = BlockTranslator.getBedrockBlockId(blockState);
UpdateBlockPacket updateBlockPacket = new UpdateBlockPacket();
@@ -157,7 +200,7 @@ public class ChunkUtils {
updateBlockPacket.setBlockPosition(position);
updateBlockPacket.setRuntimeId(blockId);
updateBlockPacket.getFlags().add(UpdateBlockPacket.Flag.NEIGHBORS);
- session.getUpstream().sendPacket(updateBlockPacket);
+ session.sendUpstreamPacket(updateBlockPacket);
UpdateBlockPacket waterPacket = new UpdateBlockPacket();
waterPacket.setDataLayer(1);
@@ -167,20 +210,23 @@ public class ChunkUtils {
} else {
waterPacket.setRuntimeId(0);
}
- session.getUpstream().sendPacket(waterPacket);
+ session.sendUpstreamPacket(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
+ for (RequiresBlockState requiresBlockState : BlockEntityTranslator.REQUIRES_BLOCK_STATE_LIST) {
+ if (requiresBlockState.isBlock(blockState)) {
+ // Flower pots are block entities only in Bedrock and are not updated anywhere else like note blocks
+ if (requiresBlockState instanceof BedrockOnlyBlockEntity) {
+ ((BedrockOnlyBlockEntity) requiresBlockState).updateBlock(session, blockState, position);
+ break;
}
+ CACHED_BLOCK_ENTITIES.put(new Position(position.getX(), position.getY(), position.getZ()), blockState);
+ break; //No block will be a part of two classes
}
}
+ session.getChunkCache().updateBlock(new Position(position.getX(), position.getY(), position.getZ()), blockState);
}
public static void sendEmptyChunks(GeyserSession session, Vector3i position, int radius, boolean forceUpdate) {
@@ -192,9 +238,9 @@ public class ChunkUtils {
data.setChunkX(chunkX + x);
data.setChunkZ(chunkZ + z);
data.setSubChunksLength(0);
- data.setData(Translators.EMPTY_LEVEL_CHUNK_DATA);
+ data.setData(EMPTY_LEVEL_CHUNK_DATA);
data.setCachingEnabled(false);
- session.getUpstream().sendPacket(data);
+ session.sendUpstreamPacket(data);
if (forceUpdate) {
Vector3i pos = Vector3i.from(chunkX + x << 4, 80, chunkZ + z << 4);
@@ -202,7 +248,7 @@ public class ChunkUtils {
blockPacket.setBlockPosition(pos);
blockPacket.setDataLayer(0);
blockPacket.setRuntimeId(1);
- session.getUpstream().sendPacket(blockPacket);
+ session.sendUpstreamPacket(blockPacket);
}
}
}
diff --git a/connector/src/main/java/org/geysermc/connector/utils/DimensionUtils.java b/connector/src/main/java/org/geysermc/connector/utils/DimensionUtils.java
index 9d874f13..6c8d9f94 100644
--- a/connector/src/main/java/org/geysermc/connector/utils/DimensionUtils.java
+++ b/connector/src/main/java/org/geysermc/connector/utils/DimensionUtils.java
@@ -25,12 +25,17 @@
package org.geysermc.connector.utils;
+import com.github.steveice10.mc.protocol.data.game.entity.Effect;
import com.nukkitx.math.vector.Vector3i;
import com.nukkitx.protocol.bedrock.packet.*;
import org.geysermc.connector.entity.Entity;
import org.geysermc.connector.network.session.GeyserSession;
public class DimensionUtils {
+
+ // Changes if the above-bedrock Nether building workaround is applied
+ private static int BEDROCK_NETHER_ID = 1;
+
public static void switchDimension(GeyserSession session, int javaDimension) {
int bedrockDimension = javaToBedrock(javaDimension);
Entity player = session.getPlayerEntity();
@@ -38,6 +43,7 @@ public class DimensionUtils {
return;
session.getEntityCache().removeAllEntities();
+ session.getItemFrameCache().clear();
if (session.getPendingDimSwitches().getAndIncrement() > 0) {
ChunkUtils.sendEmptyChunks(session, player.getPosition().toInt(), 3, true);
}
@@ -48,17 +54,27 @@ public class DimensionUtils {
changeDimensionPacket.setDimension(bedrockDimension);
changeDimensionPacket.setRespawn(true);
changeDimensionPacket.setPosition(pos.toFloat());
- session.getUpstream().sendPacket(changeDimensionPacket);
+ session.sendUpstreamPacket(changeDimensionPacket);
player.setDimension(bedrockDimension);
player.setPosition(pos.toFloat());
session.setSpawned(false);
session.setLastChunkPosition(null);
+ for (Effect effect : session.getPlayerEntity().getEffectCache().getEntityEffects().keySet()) {
+ MobEffectPacket mobEffectPacket = new MobEffectPacket();
+ mobEffectPacket.setEvent(MobEffectPacket.Event.REMOVE);
+ mobEffectPacket.setRuntimeEntityId(session.getPlayerEntity().getGeyserId());
+ mobEffectPacket.setEffectId(EntityUtils.toBedrockEffectId(effect));
+ session.sendUpstreamPacket(mobEffectPacket);
+ }
+ // Effects are re-sent from server
+ session.getPlayerEntity().getEffectCache().getEntityEffects().clear();
+
//let java server handle portal travel sound
StopSoundPacket stopSoundPacket = new StopSoundPacket();
stopSoundPacket.setStoppingAllSound(true);
stopSoundPacket.setSoundName("");
- session.getUpstream().sendPacket(stopSoundPacket);
+ session.sendUpstreamPacket(stopSoundPacket);
}
/**
@@ -70,11 +86,16 @@ public class DimensionUtils {
public static int javaToBedrock(int javaDimension) {
switch (javaDimension) {
case -1:
- return 1;
+ return BEDROCK_NETHER_ID;
case 1:
return 2;
default:
return javaDimension;
}
}
+
+ public static void changeBedrockNetherId() {
+ // Change dimension ID to the End to allow for building above Bedrock
+ BEDROCK_NETHER_ID = 2;
+ }
}
diff --git a/connector/src/main/java/org/geysermc/connector/utils/DockerCheck.java b/connector/src/main/java/org/geysermc/connector/utils/DockerCheck.java
new file mode 100644
index 00000000..0a5a2278
--- /dev/null
+++ b/connector/src/main/java/org/geysermc/connector/utils/DockerCheck.java
@@ -0,0 +1,58 @@
+/*
+ * 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 org.geysermc.connector.bootstrap.GeyserBootstrap;
+
+import java.net.InetAddress;
+import java.nio.file.Files;
+import java.nio.file.Paths;
+
+public class DockerCheck {
+ public static void check(GeyserBootstrap bootstrap) {
+ try {
+ String OS = System.getProperty("os.name").toLowerCase();
+ String ipAddress = InetAddress.getLocalHost().getHostAddress();
+
+ // Check if the user is already using the recommended IP
+ if (ipAddress.equals(bootstrap.getGeyserConfig().getRemote().getAddress())) {
+ return;
+ }
+
+ if (OS.indexOf("nix") >= 0 || OS.indexOf("nux") >= 0 || OS.indexOf("aix") > 0) {
+ bootstrap.getGeyserLogger().debug("We are on a Unix system, checking for Docker...");
+
+ String output = new String(Files.readAllBytes(Paths.get("/proc/1/cgroup")));
+
+ if (output.contains("docker")) {
+ bootstrap.getGeyserLogger().warning("You are most likely in a Docker container, this may cause connection issues from Geyser to the Java server");
+ bootstrap.getGeyserLogger().warning("We recommended using the following IP as the remote address: " + ipAddress);
+ }
+ }
+ } catch (Exception e) { } // Ignore any errors, inc ip failed to fetch, process could not run or access denied
+ }
+}
diff --git a/connector/src/main/java/org/geysermc/connector/utils/EntityUtils.java b/connector/src/main/java/org/geysermc/connector/utils/EntityUtils.java
index 79ce9e8b..b5033c94 100644
--- a/connector/src/main/java/org/geysermc/connector/utils/EntityUtils.java
+++ b/connector/src/main/java/org/geysermc/connector/utils/EntityUtils.java
@@ -26,8 +26,6 @@
package org.geysermc.connector.utils;
import com.github.steveice10.mc.protocol.data.game.entity.Effect;
-import com.github.steveice10.mc.protocol.data.game.entity.type.MobType;
-import com.github.steveice10.mc.protocol.data.game.entity.type.object.ObjectType;
import org.geysermc.connector.entity.type.EntityType;
public class EntityUtils {
@@ -64,21 +62,7 @@ public class EntityUtils {
* @param type The MobType to convert
* @return Converted EntityType
*/
- public static EntityType toBedrockEntity(MobType type) {
- try {
- return EntityType.valueOf(type.name());
- } catch (IllegalArgumentException ex) {
- return null;
- }
- }
-
- /**
- * Converts a ObjectType to a Bedrock edition EntityType, returns null if the EntityType is not found
- *
- * @param type The ObjectType to convert
- * @return Converted EntityType
- */
- public static EntityType toBedrockEntity(ObjectType type) {
+ public static EntityType toBedrockEntity(com.github.steveice10.mc.protocol.data.game.entity.type.EntityType type) {
try {
return EntityType.valueOf(type.name());
} catch (IllegalArgumentException ex) {
diff --git a/connector/src/main/java/org/geysermc/connector/utils/FileUtils.java b/connector/src/main/java/org/geysermc/connector/utils/FileUtils.java
index 5144628d..aedfb840 100644
--- a/connector/src/main/java/org/geysermc/connector/utils/FileUtils.java
+++ b/connector/src/main/java/org/geysermc/connector/utils/FileUtils.java
@@ -138,4 +138,18 @@ public class FileUtils {
public static void writeFile(String name, char[] data) throws IOException {
writeFile(new File(name), data);
}
+
+ /**
+ * Get an InputStream for the given resource path, throws AssertionError if resource is not found
+ *
+ * @param resource Resource to get
+ * @return InputStream of the given resource
+ */
+ public static InputStream getResource(String resource) {
+ InputStream stream = FileUtils.class.getClassLoader().getResourceAsStream(resource);
+ if (stream == null) {
+ throw new AssertionError("Unable to find resource: " + resource);
+ }
+ return stream;
+ }
}
diff --git a/connector/src/main/java/org/geysermc/connector/utils/FireworkColor.java b/connector/src/main/java/org/geysermc/connector/utils/FireworkColor.java
new file mode 100644
index 00000000..d729e3a1
--- /dev/null
+++ b/connector/src/main/java/org/geysermc/connector/utils/FireworkColor.java
@@ -0,0 +1,101 @@
+/*
+ * 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 lombok.Getter;
+
+public enum FireworkColor {
+ // Vanilla colors
+ BLACK((byte) 0, 1973019),
+ RED((byte) 1, 11743532),
+ GREEN((byte) 2, 3887386),
+ BROWN((byte) 3, 5320730),
+ BLUE((byte) 4, 2437522),
+ PURPLE((byte) 5, 8073150),
+ CYAN((byte) 6, 2651799),
+ LIGHT_GRAY((byte) 7, 11250603),
+ GRAY((byte) 8, 4408131),
+ PINK((byte) 9, 14188952),
+ LIME((byte) 10, 4312372),
+ YELLOW((byte) 11, 14602026),
+ LIGHT_BLUE((byte) 12, 6719955),
+ MAGENTA((byte) 13, 12801229),
+ ORANGE((byte) 14, 15435844),
+ WHITE((byte) 15, 15790320),
+
+ // Bukkit colors
+ // https://hub.spigotmc.org/javadocs/spigot/org/bukkit/Color.html
+ BUKKIT_WHITE((byte) 15, 0xFFFFFF),
+ BUKKIT_SILVER((byte) 7, 0xC0C0C0),
+ BUKKIT_GRAY((byte) 8, 0x808080),
+ BUKKIT_BLACK((byte) 0, 0x000000),
+ BUKKIT_RED((byte) 1, 0xFF0000),
+ BUKKIT_MAROON((byte) 1, 0x800000), // No perfect map but this is as close as it can be
+ BUKKIT_YELLOW((byte) 11, 0xFFFF00),
+ BUKKIT_OLIVE((byte) 2, 0x808000), // No perfect map but this is as close as it can be
+ BUKKIT_LIME((byte) 10, 0x00FF00),
+ BUKKIT_GREEN((byte) 2, 0x008000),
+ BUKKIT_AQUA((byte) 12, 0x00FFFF),
+ BUKKIT_TEAL((byte) 6, 0x008080),
+ BUKKIT_BLUE((byte) 4, 0x0000FF),
+ BUKKIT_NAVY((byte) 4, 0x000080), // No perfect map but this is as close as it can be
+ BUKKIT_FUCHSIA((byte) 9, 0xFF00FF), // No perfect map but this is as close as it can be
+ BUKKIT_PURPLE((byte) 5, 0x800080),
+ BUKKIT_ORANGE((byte) 14, 0xFFA500);
+
+ private static final FireworkColor[] VALUES = values();
+
+ @Getter
+ private byte bedrockID;
+ @Getter
+ private int javaID;
+
+ FireworkColor(byte bedrockID, int javaID) {
+ this.bedrockID = bedrockID;
+ this.javaID = javaID;
+ }
+
+ public static FireworkColor fromJavaID(int id) {
+ for (FireworkColor color : VALUES) {
+ if (color.javaID == id) {
+ return color;
+ }
+ }
+
+ return WHITE;
+ }
+
+ public static FireworkColor fromBedrockID(int id) {
+ for (FireworkColor color : VALUES) {
+ if (color.bedrockID == id) {
+ return color;
+ }
+ }
+
+ return WHITE;
+ }
+}
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 3717b432..5574a3a9 100644
--- a/connector/src/main/java/org/geysermc/connector/utils/InventoryUtils.java
+++ b/connector/src/main/java/org/geysermc/connector/utils/InventoryUtils.java
@@ -27,16 +27,22 @@ package org.geysermc.connector.utils;
import com.github.steveice10.mc.protocol.data.game.entity.metadata.ItemStack;
import com.github.steveice10.opennbt.tag.builtin.CompoundTag;
+import com.nukkitx.nbt.CompoundTagBuilder;
+import com.nukkitx.nbt.tag.StringTag;
import com.nukkitx.protocol.bedrock.data.ContainerId;
import com.nukkitx.protocol.bedrock.data.ItemData;
+import com.nukkitx.protocol.bedrock.packet.ContainerClosePacket;
import com.nukkitx.protocol.bedrock.packet.InventorySlotPacket;
+import org.geysermc.common.ChatColor;
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 org.geysermc.connector.network.translators.item.ItemRegistry;
+import org.geysermc.connector.network.translators.item.ItemTranslator;
+import java.util.Collections;
import java.util.Objects;
import java.util.concurrent.TimeUnit;
@@ -44,7 +50,7 @@ public class InventoryUtils {
public static final ItemStack REFRESH_ITEM = new ItemStack(1, 127, new CompoundTag("")); //TODO: stop using this
public static void openInventory(GeyserSession session, Inventory inventory) {
- InventoryTranslator translator = Translators.getInventoryTranslators().get(inventory.getWindowType());
+ InventoryTranslator translator = InventoryTranslator.INVENTORY_TRANSLATORS.get(inventory.getWindowType());
if (translator != null) {
session.getInventoryCache().setOpenInventory(inventory);
translator.prepareInventory(session, inventory);
@@ -64,27 +70,42 @@ public class InventoryUtils {
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());
+ Inventory openInventory = session.getInventoryCache().getOpenInventory();
+ session.getInventoryCache().uncacheInventory(windowId);
+ if (inventory != null && openInventory != null && inventory.getId() == openInventory.getId()) {
+ InventoryTranslator translator = InventoryTranslator.INVENTORY_TRANSLATORS.get(inventory.getWindowType());
translator.closeInventory(session, inventory);
- session.getInventoryCache().uncacheInventory(windowId);
session.getInventoryCache().setOpenInventory(null);
+ } else {
+ return;
}
} else {
Inventory inventory = session.getInventory();
- InventoryTranslator translator = Translators.getInventoryTranslators().get(inventory.getWindowType());
+ InventoryTranslator translator = InventoryTranslator.INVENTORY_TRANSLATORS.get(inventory.getWindowType());
translator.updateInventory(session, inventory);
}
+
session.setCraftSlot(0);
session.getInventory().setCursor(null);
+ updateCursor(session);
+ }
+
+ public static void closeWindow(GeyserSession session, int windowId) {
+ //Spamming close window packets can bug the client
+ if (System.currentTimeMillis() - session.getLastWindowCloseTime() > 500) {
+ ContainerClosePacket closePacket = new ContainerClosePacket();
+ closePacket.setWindowId((byte) windowId);
+ session.sendUpstreamPacket(closePacket);
+ session.setLastWindowCloseTime(System.currentTimeMillis());
+ }
}
public static void updateCursor(GeyserSession session) {
InventorySlotPacket cursorPacket = new InventorySlotPacket();
cursorPacket.setContainerId(ContainerId.CURSOR);
cursorPacket.setSlot(0);
- cursorPacket.setItem(Translators.getItemTranslator().translateToBedrock(session, session.getInventory().getCursor()));
- session.getUpstream().sendPacket(cursorPacket);
+ cursorPacket.setItem(ItemTranslator.translateToBedrock(session, session.getInventory().getCursor()));
+ session.sendUpstreamPacket(cursorPacket);
}
public static boolean canStack(ItemStack item1, ItemStack item2) {
@@ -98,4 +119,19 @@ public class InventoryUtils {
return false;
return item1.equals(item2, false, true, true);
}
+
+ /**
+ * Returns a barrier block with custom name and lore to explain why
+ * part of the inventory is unusable.
+ */
+ public static ItemData createUnusableSpaceBlock(String description) {
+ CompoundTagBuilder root = CompoundTagBuilder.builder();
+ CompoundTagBuilder display = CompoundTagBuilder.builder();
+
+ display.stringTag("Name", ChatColor.RESET + "Unusable inventory space");
+ display.listTag("Lore", StringTag.class, Collections.singletonList(new StringTag("", ChatColor.RESET + ChatColor.DARK_PURPLE + description)));
+
+ root.tag(display.build("display"));
+ return ItemData.of(ItemRegistry.ITEM_ENTRIES.get(ItemRegistry.BARRIER_INDEX).getBedrockId(), (short) 0, 1, root.buildRootTag());
+ }
}
diff --git a/connector/src/main/java/org/geysermc/connector/utils/ItemUtils.java b/connector/src/main/java/org/geysermc/connector/utils/ItemUtils.java
index bb3cf0ed..b9600222 100644
--- a/connector/src/main/java/org/geysermc/connector/utils/ItemUtils.java
+++ b/connector/src/main/java/org/geysermc/connector/utils/ItemUtils.java
@@ -26,6 +26,12 @@
package org.geysermc.connector.utils;
import com.github.steveice10.opennbt.tag.builtin.*;
+import com.nukkitx.nbt.CompoundTagBuilder;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
public class ItemUtils {
diff --git a/connector/src/main/java/org/geysermc/connector/utils/LocaleUtils.java b/connector/src/main/java/org/geysermc/connector/utils/LocaleUtils.java
index 726ad1c1..f55cb261 100644
--- a/connector/src/main/java/org/geysermc/connector/utils/LocaleUtils.java
+++ b/connector/src/main/java/org/geysermc/connector/utils/LocaleUtils.java
@@ -63,7 +63,7 @@ public class LocaleUtils {
private static void generateAssetCache() {
try {
// Get the version manifest from Mojang
- VersionManifest versionManifest = Toolbox.JSON_MAPPER.readValue(WebUtils.getBody("https://launchermeta.mojang.com/mc/game/version_manifest.json"), VersionManifest.class);
+ VersionManifest versionManifest = GeyserConnector.JSON_MAPPER.readValue(WebUtils.getBody("https://launchermeta.mojang.com/mc/game/version_manifest.json"), VersionManifest.class);
// Get the url for the latest version of the games manifest
String latestInfoURL = "";
@@ -80,7 +80,7 @@ public class LocaleUtils {
}
// Get the individual version manifest
- VersionInfo versionInfo = Toolbox.JSON_MAPPER.readValue(WebUtils.getBody(latestInfoURL), VersionInfo.class);
+ VersionInfo versionInfo = GeyserConnector.JSON_MAPPER.readValue(WebUtils.getBody(latestInfoURL), VersionInfo.class);
// Get the smallest jar for use when downloading the en_us locale, will be either the server or client
int currentSize = Integer.MAX_VALUE;
@@ -92,13 +92,13 @@ public class LocaleUtils {
}
// Get the assets list
- JsonNode assets = Toolbox.JSON_MAPPER.readTree(WebUtils.getBody(versionInfo.getAssetIndex().getUrl())).get("objects");
+ JsonNode assets = GeyserConnector.JSON_MAPPER.readTree(WebUtils.getBody(versionInfo.getAssetIndex().getUrl())).get("objects");
// Put each asset into an array for use later
Iterator> assetIterator = assets.fields();
while (assetIterator.hasNext()) {
Map.Entry entry = assetIterator.next();
- Asset asset = Toolbox.JSON_MAPPER.treeToValue(entry.getValue(), Asset.class);
+ Asset asset = GeyserConnector.JSON_MAPPER.treeToValue(entry.getValue(), Asset.class);
ASSET_MAP.put(entry.getKey(), asset);
}
} catch (Exception e) {
@@ -173,7 +173,7 @@ public class LocaleUtils {
// Parse the file as json
JsonNode localeObj;
try {
- localeObj = Toolbox.JSON_MAPPER.readTree(localeStream);
+ localeObj = GeyserConnector.JSON_MAPPER.readTree(localeStream);
} catch (Exception e) {
throw new AssertionError("Unable to load Java edition lang map for " + locale, e);
}
diff --git a/connector/src/main/java/org/geysermc/connector/utils/LoginEncryptionUtils.java b/connector/src/main/java/org/geysermc/connector/utils/LoginEncryptionUtils.java
index 300294a2..f9a7fec2 100644
--- a/connector/src/main/java/org/geysermc/connector/utils/LoginEncryptionUtils.java
+++ b/connector/src/main/java/org/geysermc/connector/utils/LoginEncryptionUtils.java
@@ -34,9 +34,6 @@ import com.nukkitx.network.util.Preconditions;
import com.nukkitx.protocol.bedrock.packet.LoginPacket;
import com.nukkitx.protocol.bedrock.packet.ServerToClientHandshakePacket;
import com.nukkitx.protocol.bedrock.util.EncryptionUtils;
-
-import net.minidev.json.JSONObject;
-
import org.geysermc.common.window.CustomFormBuilder;
import org.geysermc.common.window.CustomFormWindow;
import org.geysermc.common.window.FormWindow;
@@ -152,7 +149,7 @@ public class LoginEncryptionUtils {
ServerToClientHandshakePacket packet = new ServerToClientHandshakePacket();
packet.setJwt(EncryptionUtils.createHandshakeJwt(serverKeyPair, token).serialize());
- session.getUpstream().sendPacketImmediately(packet);
+ session.sendUpstreamPacketImmediately(packet);
}
private static int AUTH_FORM_ID = 1336;
@@ -195,18 +192,22 @@ public class LoginEncryptionUtils {
String password = response.getInputResponses().get(2);
session.authenticate(email, password);
+ } else {
+ showLoginDetailsWindow(session);
}
// Clear windows so authentication data isn't accidentally cached
windowCache.getWindows().clear();
} else if (formId == AUTH_FORM_ID && window instanceof SimpleFormWindow) {
SimpleFormResponse response = (SimpleFormResponse) window.getResponse();
- if(response != null) {
- if(response.getClickedButtonId() == 0) {
+ if (response != null) {
+ if (response.getClickedButtonId() == 0) {
showLoginDetailsWindow(session);
- } else if(response.getClickedButtonId() == 1) {
+ } else if (response.getClickedButtonId() == 1) {
session.disconnect("Login is required");
}
+ } else {
+ showLoginWindow(session);
}
}
}
diff --git a/connector/src/main/java/org/geysermc/connector/utils/MapColor.java b/connector/src/main/java/org/geysermc/connector/utils/MapColor.java
index b011edc7..4328d758 100644
--- a/connector/src/main/java/org/geysermc/connector/utils/MapColor.java
+++ b/connector/src/main/java/org/geysermc/connector/utils/MapColor.java
@@ -1,3 +1,29 @@
+/*
+ * 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;
public enum MapColor {
diff --git a/connector/src/main/java/org/geysermc/connector/utils/MathUtils.java b/connector/src/main/java/org/geysermc/connector/utils/MathUtils.java
index 27a3fdd3..050078e5 100644
--- a/connector/src/main/java/org/geysermc/connector/utils/MathUtils.java
+++ b/connector/src/main/java/org/geysermc/connector/utils/MathUtils.java
@@ -39,13 +39,28 @@ public class MathUtils {
}
public static double constrain(double num, double min, double max) {
- if(num > max) {
+ if (num > max) {
num = max;
}
- if(num < min) {
+ if (num < min) {
num = min;
}
return num;
}
+
+ /**
+ * Converts the given object from an int or byte to byte.
+ * This is used for NBT data that might be either an int
+ * or byte and bedrock only takes it as an byte
+ *
+ * @param value The value to convert
+ * @return The converted byte
+ */
+ public static Byte convertByte(Object value) {
+ if (value instanceof Integer) {
+ return ((Integer) value).byteValue();
+ }
+ return (Byte) value;
+ }
}
diff --git a/connector/src/main/java/org/geysermc/connector/utils/MessageUtils.java b/connector/src/main/java/org/geysermc/connector/utils/MessageUtils.java
index ac111c71..d79cdab8 100644
--- a/connector/src/main/java/org/geysermc/connector/utils/MessageUtils.java
+++ b/connector/src/main/java/org/geysermc/connector/utils/MessageUtils.java
@@ -27,17 +27,15 @@ package org.geysermc.connector.utils;
import com.github.steveice10.mc.protocol.data.game.scoreboard.TeamColor;
import com.github.steveice10.mc.protocol.data.message.*;
-import com.google.gson.JsonArray;
-import com.google.gson.JsonElement;
-import com.google.gson.JsonObject;
-import com.google.gson.JsonParser;
-import com.google.gson.JsonPrimitive;
+import com.google.gson.*;
import net.kyori.text.Component;
import net.kyori.text.serializer.gson.GsonComponentSerializer;
import net.kyori.text.serializer.legacy.LegacyComponentSerializer;
import org.geysermc.connector.network.session.GeyserSession;
-import java.util.*;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@@ -110,9 +108,20 @@ public class MessageUtils {
builder.append(getColorOrParent(msg.getStyle()));
if (!(msg.getText() == null)) {
boolean isTranslationMessage = (msg instanceof TranslationMessage);
- builder.append(getTranslatedBedrockMessage(msg, locale, isTranslationMessage));
+ String extraText = "";
+
+ if (isTranslationMessage) {
+ List paramsTranslated = getTranslationParams(((TranslationMessage) msg).getTranslationParams(), locale);
+ extraText = insertParams(getTranslatedBedrockMessage(msg, locale, isTranslationMessage), paramsTranslated);
+ } else {
+ extraText = getTranslatedBedrockMessage(msg, locale, isTranslationMessage);
+ }
+
+ builder.append(extraText);
+ builder.append("\u00a7r");
}
}
+
return builder.toString();
}
@@ -121,20 +130,39 @@ public class MessageUtils {
}
public static String getBedrockMessage(Message message) {
- Component component;
if (isMessage(message.getText())) {
- component = GsonComponentSerializer.INSTANCE.deserialize(message.getText());
+ return getBedrockMessage(message.getText());
} else {
- component = GsonComponentSerializer.INSTANCE.deserialize(message.toJsonString());
+ return getBedrockMessage(message.toJsonString());
+ }
+ }
+
+ /**
+ * Verifies the message is valid JSON in case it's plaintext. Works around GsonComponentSeraializer not using lenient mode.
+ * See https://wiki.vg/Chat for messages sent in lenient mode, and for a description on leniency.
+ *
+ * @param message Potentially lenient JSON message
+ * @return Bedrock formatted message
+ */
+ public static String getBedrockMessageLenient(String message) {
+ if (isMessage(message)) {
+ return getBedrockMessage(message);
+ } else {
+ final JsonObject obj = new JsonObject();
+ obj.addProperty("text", message);
+ return getBedrockMessage(obj.toString());
}
- return LegacyComponentSerializer.legacy().serialize(component);
}
public static String getBedrockMessage(String message) {
- Component component = GsonComponentSerializer.INSTANCE.deserialize(message);
+ Component component = phraseJavaMessage(message);
return LegacyComponentSerializer.legacy().serialize(component);
}
+ public static Component phraseJavaMessage(String message) {
+ return GsonComponentSerializer.INSTANCE.deserialize(message);
+ }
+
public static String getJavaMessage(String message) {
Component component = LegacyComponentSerializer.legacy().deserialize(message);
return GsonComponentSerializer.INSTANCE.serialize(component);
@@ -161,9 +189,11 @@ public class MessageUtils {
}
for (String text : params) {
- newMessage = newMessage.replaceFirst("%s", text);
+ newMessage = newMessage.replaceFirst("%s", text.replaceAll("%s", "%r"));
}
+ newMessage = newMessage.replaceAll("%r", "MISSING!");
+
return newMessage;
}
diff --git a/connector/src/main/java/org/geysermc/connector/utils/PaintingType.java b/connector/src/main/java/org/geysermc/connector/utils/PaintingType.java
index 00993522..63f1119c 100644
--- a/connector/src/main/java/org/geysermc/connector/utils/PaintingType.java
+++ b/connector/src/main/java/org/geysermc/connector/utils/PaintingType.java
@@ -57,7 +57,7 @@ public enum PaintingType {
DONKEY_KONG("DonkeyKong", 4, 3),
POINTER("Pointer", 4, 4),
PIG_SCENE("Pigscene", 4, 4),
- BURNING_SKULL("Flaming Skull", 4, 4); // burning skull on java edition, flaming skull on bedrock
+ BURNING_SKULL("BurningSkull", 4, 4);
private static final PaintingType[] VALUES = values();
private String bedrockName;
diff --git a/connector/src/main/java/org/geysermc/connector/utils/SkinProvider.java b/connector/src/main/java/org/geysermc/connector/utils/SkinProvider.java
index ade03c54..aea9ba18 100644
--- a/connector/src/main/java/org/geysermc/connector/utils/SkinProvider.java
+++ b/connector/src/main/java/org/geysermc/connector/utils/SkinProvider.java
@@ -25,16 +25,22 @@
package org.geysermc.connector.utils;
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.AllArgsConstructor;
import lombok.Getter;
-
+import lombok.NoArgsConstructor;
import org.geysermc.connector.GeyserConnector;
import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.image.BufferedImage;
-import java.io.ByteArrayOutputStream;
+import java.io.*;
import java.net.URL;
+import java.nio.charset.Charset;
+import java.nio.charset.StandardCharsets;
+import java.util.Arrays;
+import java.util.Base64;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.*;
@@ -45,6 +51,8 @@ public class SkinProvider {
public static final byte[] STEVE_SKIN = new ProvidedSkin("bedrock/skin/skin_steve.png").getSkin();
public static final Skin EMPTY_SKIN = new Skin(-1, "steve", STEVE_SKIN);
+ public static final byte[] ALEX_SKIN = new ProvidedSkin("bedrock/skin/skin_alex.png").getSkin();
+ public static final Skin EMPTY_SKIN_ALEX = new Skin(-1, "alex", ALEX_SKIN);
private static Map cachedSkins = new ConcurrentHashMap<>();
private static Map> requestedSkins = new ConcurrentHashMap<>();
@@ -52,8 +60,49 @@ public class SkinProvider {
private static Map cachedCapes = new ConcurrentHashMap<>();
private static Map> requestedCapes = new ConcurrentHashMap<>();
+ public static final SkinGeometry EMPTY_GEOMETRY = SkinProvider.SkinGeometry.getLegacy(false);
+ private static Map cachedGeometry = new ConcurrentHashMap<>();
+
+ public static final boolean ALLOW_THIRD_PARTY_EARS = GeyserConnector.getInstance().getConfig().isAllowThirdPartyEars();
+ public static String EARS_GEOMETRY;
+ public static String EARS_GEOMETRY_SLIM;
+
+ private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();
private static final int CACHE_INTERVAL = 8 * 60 * 1000; // 8 minutes
+ static {
+ /* Load in the normal ears geometry */
+ InputStream earsStream = FileUtils.getResource("bedrock/skin/geometry.humanoid.ears.json");
+
+ StringBuilder earsDataBuilder = new StringBuilder();
+ try (Reader reader = new BufferedReader(new InputStreamReader(earsStream, Charset.forName(StandardCharsets.UTF_8.name())))) {
+ int c = 0;
+ while ((c = reader.read()) != -1) {
+ earsDataBuilder.append((char) c);
+ }
+ } catch (IOException e) {
+ throw new AssertionError("Unable to load ears geometry", e);
+ }
+
+ EARS_GEOMETRY = earsDataBuilder.toString();
+
+
+ /* Load in the slim ears geometry */
+ earsStream = FileUtils.getResource("bedrock/skin/geometry.humanoid.earsSlim.json");
+
+ earsDataBuilder = new StringBuilder();
+ try (Reader reader = new BufferedReader(new InputStreamReader(earsStream, Charset.forName(StandardCharsets.UTF_8.name())))) {
+ int c = 0;
+ while ((c = reader.read()) != -1) {
+ earsDataBuilder.append((char) c);
+ }
+ } catch (IOException e) {
+ throw new AssertionError("Unable to load ears geometry", e);
+ }
+
+ EARS_GEOMETRY_SLIM = earsDataBuilder.toString();
+ }
+
public static boolean hasSkinCached(UUID uuid) {
return cachedSkins.containsKey(uuid);
}
@@ -74,9 +123,10 @@ public class SkinProvider {
return CompletableFuture.supplyAsync(() -> {
long time = System.currentTimeMillis();
+ CapeProvider provider = capeUrl != null ? CapeProvider.MINECRAFT : null;
SkinAndCape skinAndCape = new SkinAndCape(
getOrDefault(requestSkin(playerId, skinUrl, false), EMPTY_SKIN, 5),
- getOrDefault(requestCape(capeUrl, false), EMPTY_CAPE, 5)
+ getOrDefault(requestCape(capeUrl, provider, false), EMPTY_CAPE, 5)
);
GeyserConnector.getInstance().getLogger().debug("Took " + (System.currentTimeMillis() - time) + "ms for " + playerId);
@@ -112,11 +162,11 @@ public class SkinProvider {
return future;
}
- public static CompletableFuture requestCape(String capeUrl, boolean newThread) {
+ public static CompletableFuture requestCape(String capeUrl, CapeProvider provider, boolean newThread) {
if (capeUrl == null || capeUrl.isEmpty()) return CompletableFuture.completedFuture(EMPTY_CAPE);
if (requestedCapes.containsKey(capeUrl)) return requestedCapes.get(capeUrl); // already requested
- boolean officialCape = capeUrl.startsWith("https://textures.minecraft.net");
+ boolean officialCape = provider == CapeProvider.MINECRAFT;
boolean validCache = (System.currentTimeMillis() - CACHE_INTERVAL) < cachedCapes.getOrDefault(capeUrl, EMPTY_CAPE).getRequestedOn();
if ((cachedCapes.containsKey(capeUrl) && officialCape) || validCache) {
@@ -126,14 +176,14 @@ public class SkinProvider {
CompletableFuture future;
if (newThread) {
- future = CompletableFuture.supplyAsync(() -> supplyCape(capeUrl), EXECUTOR_SERVICE)
+ future = CompletableFuture.supplyAsync(() -> supplyCape(capeUrl, provider), EXECUTOR_SERVICE)
.whenCompleteAsync((cape, throwable) -> {
cachedCapes.put(capeUrl, cape);
requestedCapes.remove(capeUrl);
});
requestedCapes.put(capeUrl, future);
} else {
- Cape cape = supplyCape(capeUrl); // blocking
+ Cape cape = supplyCape(capeUrl, provider); // blocking
future = CompletableFuture.completedFuture(cape);
cachedCapes.put(capeUrl, cape);
}
@@ -143,9 +193,9 @@ public class SkinProvider {
public static CompletableFuture requestUnofficialCape(Cape officialCape, UUID playerId,
String username, boolean newThread) {
if (officialCape.isFailed() && ALLOW_THIRD_PARTY_CAPES) {
- for (UnofficalCape cape : UnofficalCape.VALUES) {
+ for (CapeProvider provider : CapeProvider.VALUES) {
Cape cape1 = getOrDefault(
- requestCape(cape.getUrlFor(playerId, username), newThread),
+ requestCape(provider.getUrlFor(playerId, username), provider, newThread),
EMPTY_CAPE, 4
);
if (!cape1.isFailed()) {
@@ -156,18 +206,100 @@ public class SkinProvider {
return CompletableFuture.completedFuture(officialCape);
}
+ public static CompletableFuture requestEars(String earsUrl, EarsProvider provider, boolean newThread, Skin skin) {
+ if (earsUrl == null || earsUrl.isEmpty()) return CompletableFuture.completedFuture(skin);
+
+ CompletableFuture