> 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 03042e3a..00000000
--- a/connector/src/main/java/org/geysermc/connector/network/translators/Translators.java
+++ /dev/null
@@ -1,175 +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 it.unimi.dsi.fastutil.objects.ObjectArrayList;
-import org.geysermc.connector.GeyserConnector;
-import org.geysermc.connector.network.translators.world.block.BlockTranslator;
-import org.geysermc.connector.network.translators.world.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<>();
-
- @Getter
- private static ObjectArrayList requiresBlockStateMap = new ObjectArrayList<>();
-
- 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.world.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() + ".");
- }
- }
-
- for (Class> clazz : ref.getSubTypesOf(RequiresBlockState.class)) {
-
- GeyserConnector.getInstance().getLogger().debug("Found block entity that requires block state: " + clazz.getCanonicalName());
-
- try {
- requiresBlockStateMap.add((RequiresBlockState) clazz.newInstance());
- } catch (InstantiationException | IllegalAccessException e) {
- GeyserConnector.getInstance().getLogger().error("Could not instantiate required block state " + 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 d7248aa3..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
@@ -64,44 +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 {
@@ -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 ed928959..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,6 +25,7 @@
package org.geysermc.connector.network.translators.bedrock;
+import com.github.steveice10.mc.protocol.data.game.entity.metadata.ItemStack;
import com.github.steveice10.mc.protocol.data.game.entity.metadata.Position;
import com.github.steveice10.mc.protocol.data.game.entity.player.GameMode;
import com.github.steveice10.mc.protocol.data.game.entity.player.Hand;
@@ -48,9 +49,9 @@ import org.geysermc.connector.inventory.Inventory;
import org.geysermc.connector.network.session.GeyserSession;
import org.geysermc.connector.network.translators.PacketTranslator;
import org.geysermc.connector.network.translators.Translator;
-import org.geysermc.connector.network.translators.Translators;
+import org.geysermc.connector.network.translators.inventory.InventoryTranslator;
import org.geysermc.connector.network.translators.item.ItemEntry;
-import org.geysermc.connector.network.translators.item.ItemTranslator;
+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;
@@ -64,12 +65,12 @@ public class BedrockInventoryTransactionTranslator 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 49b0b7fc..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
@@ -55,7 +55,7 @@ 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 {
}
ClientChatPacket chatPacket = new ClientChatPacket(message);
- session.getDownstream().getSession().send(chatPacket);
+ session.sendDownstreamPacket(chatPacket);
}
}
diff --git a/connector/src/main/java/org/geysermc/connector/utils/EffectUtils.java b/connector/src/main/java/org/geysermc/connector/network/translators/effect/EffectRegistry.java
similarity index 64%
rename from connector/src/main/java/org/geysermc/connector/utils/EffectUtils.java
rename to connector/src/main/java/org/geysermc/connector/network/translators/effect/EffectRegistry.java
index 7c1690e4..50888090 100644
--- a/connector/src/main/java/org/geysermc/connector/utils/EffectUtils.java
+++ b/connector/src/main/java/org/geysermc/connector/network/translators/effect/EffectRegistry.java
@@ -1,49 +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:
+ * 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.utils;
+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.network.translators.effect.Effect;
+import org.geysermc.connector.utils.FileUtils;
import java.io.InputStream;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
-public class EffectUtils {
+/**
+ * Registry for particles and effects.
+ */
+public class EffectRegistry {
public static final Map EFFECTS = new HashMap<>();
public static final Int2ObjectMap RECORDS = new Int2ObjectOpenHashMap<>();
@@ -57,10 +58,10 @@ public class EffectUtils {
static {
/* Load particles */
- InputStream particleStream = Toolbox.getResource("mappings/particles.json");
+ InputStream particleStream = FileUtils.getResource("mappings/particles.json");
JsonNode particleEntries;
try {
- particleEntries = Toolbox.JSON_MAPPER.readTree(particleStream);
+ particleEntries = GeyserConnector.JSON_MAPPER.readTree(particleStream);
} catch (Exception e) {
throw new AssertionError("Unable to load particle map", e);
}
@@ -69,10 +70,10 @@ public class EffectUtils {
while (particlesIterator.hasNext()) {
Map.Entry entry = particlesIterator.next();
try {
- setIdentifier(ParticleType.valueOf(entry.getKey().toUpperCase()), LevelEventType.valueOf(entry.getValue().asText().toUpperCase()));
+ particleTypeMap.put(ParticleType.valueOf(entry.getKey().toUpperCase()), LevelEventType.valueOf(entry.getValue().asText().toUpperCase()));
} catch (IllegalArgumentException e1) {
try {
- setIdentifier(ParticleType.valueOf(entry.getKey().toUpperCase()), entry.getValue().asText());
+ particleStringMap.put(ParticleType.valueOf(entry.getKey().toUpperCase()), entry.getValue().asText());
GeyserConnector.getInstance().getLogger().debug("Force to map particle "
+ entry.getKey()
+ "=>"
@@ -85,10 +86,10 @@ public class EffectUtils {
}
/* Load effects */
- InputStream effectsStream = Toolbox.getResource("mappings/effects.json");
+ InputStream effectsStream = FileUtils.getResource("mappings/effects.json");
JsonNode effects;
try {
- effects = Toolbox.JSON_MAPPER.readTree(effectsStream);
+ effects = GeyserConnector.JSON_MAPPER.readTree(effectsStream);
} catch (Exception e) {
throw new AssertionError("Unable to load effects mappings", e);
}
@@ -112,14 +113,6 @@ public class EffectUtils {
}
}
- public static void setIdentifier(ParticleType type, LevelEventType identifier) {
- particleTypeMap.put(type, identifier);
- }
-
- public static void setIdentifier(ParticleType type, String identifier) {
- particleStringMap.put(type, identifier);
- }
-
public static LevelEventType getParticleLevelEventType(@NonNull ParticleType type) {
return particleTypeMap.getOrDefault(type, null);
}
@@ -127,5 +120,4 @@ public class EffectUtils {
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/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 720280b4..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
@@ -39,15 +39,13 @@ 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,7 +103,7 @@ 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
@@ -117,7 +115,7 @@ public class DoubleChestInventoryTranslator extends BaseInventoryTranslator {
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());
@@ -126,16 +124,6 @@ public class DoubleChestInventoryTranslator extends BaseInventoryTranslator {
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 a6e5b2dd..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
@@ -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,7 +75,7 @@ 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
@@ -87,6 +87,6 @@ public class BlockInventoryHolder extends InventoryHolder {
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/utils/Toolbox.java b/connector/src/main/java/org/geysermc/connector/network/translators/item/ItemRegistry.java
similarity index 52%
rename from connector/src/main/java/org/geysermc/connector/utils/Toolbox.java
rename to connector/src/main/java/org/geysermc/connector/network/translators/item/ItemRegistry.java
index 953b70a8..ed99ece3 100644
--- a/connector/src/main/java/org/geysermc/connector/utils/Toolbox.java
+++ b/connector/src/main/java/org/geysermc/connector/network/translators/item/ItemRegistry.java
@@ -1,91 +1,85 @@
/*
* Copyright (c) 2019-2020 GeyserMC. http://geysermc.org
*
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
+ * 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.utils;
+package org.geysermc.connector.network.translators.item;
import com.fasterxml.jackson.core.type.TypeReference;
-import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.JsonNode;
-import com.fasterxml.jackson.databind.ObjectMapper;
+import com.github.steveice10.mc.protocol.data.game.entity.metadata.ItemStack;
import com.nukkitx.nbt.NbtUtils;
-import com.nukkitx.nbt.stream.NBTInputStream;
-import com.nukkitx.nbt.tag.CompoundTag;
import com.nukkitx.protocol.bedrock.data.ItemData;
import com.nukkitx.protocol.bedrock.packet.StartGamePacket;
-
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
-
import org.geysermc.connector.GeyserConnector;
-import org.geysermc.connector.network.translators.item.ItemEntry;
-import org.geysermc.connector.network.translators.item.ToolItemEntry;
-import org.geysermc.connector.network.translators.sound.SoundHandlerRegistry;
+import org.geysermc.connector.utils.FileUtils;
-import java.io.*;
-import java.util.*;
+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;
-public class Toolbox {
+/**
+ * Registry for anything item related.
+ */
+public class ItemRegistry {
+
+ private static final Map JAVA_IDENTIFIER_MAP = new HashMap<>();
- public static final ObjectMapper JSON_MAPPER = new ObjectMapper().disable(DeserializationFeature.FAIL_ON_IGNORED_PROPERTIES);
- public static final CompoundTag BIOMES;
public static final ItemData[] CREATIVE_ITEMS;
public static final List ITEMS = new ArrayList<>();
-
public static final Int2ObjectMap ITEM_ENTRIES = new Int2ObjectOpenHashMap<>();
- public static CompoundTag ENTITY_IDENTIFIERS;
+ // 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 biomes */
- InputStream biomestream = GeyserConnector.class.getClassLoader().getResourceAsStream("bedrock/biome_definitions.dat");
- if (biomestream == null) {
- throw new AssertionError("Unable to find bedrock/biome_definitions.dat");
- }
-
- CompoundTag biomesTag;
-
- try (NBTInputStream biomenbtInputStream = NbtUtils.createNetworkReader(biomestream)){
- biomesTag = (CompoundTag) biomenbtInputStream.readTag();
- BIOMES = biomesTag;
- } catch (Exception ex) {
- GeyserConnector.getInstance().getLogger().warning("Failed to get biomes from biome definitions, is there something wrong with the file?");
- throw new AssertionError(ex);
- }
-
/* Load item palette */
- InputStream stream = getResource("bedrock/items.json");
+ InputStream stream = FileUtils.getResource("bedrock/items.json");
TypeReference> itemEntriesType = new TypeReference>() {
};
List itemEntries;
try {
- itemEntries = JSON_MAPPER.readValue(stream, itemEntriesType);
+ itemEntries = GeyserConnector.JSON_MAPPER.readValue(stream, itemEntriesType);
} catch (Exception e) {
throw new AssertionError("Unable to load Bedrock runtime item IDs", e);
}
@@ -94,11 +88,11 @@ public class Toolbox {
ITEMS.add(new StartGamePacket.ItemEntry(entry.get("name").textValue(), (short) entry.get("id").intValue()));
}
- stream = getResource("mappings/items.json");
+ stream = FileUtils.getResource("mappings/items.json");
JsonNode items;
try {
- items = JSON_MAPPER.readTree(stream);
+ items = GeyserConnector.JSON_MAPPER.readTree(stream);
} catch (Exception e) {
throw new AssertionError("Unable to load Java runtime item IDs", e);
}
@@ -139,22 +133,12 @@ public class Toolbox {
itemIndex++;
}
- // Load particle/effect mappings
- EffectUtils.init();
- // Load sound mappings
- SoundUtils.init();
- // Load the locale data
- LocaleUtils.init();
-
- // Load sound handlers
- SoundHandlerRegistry.init();
-
/* Load creative items */
- stream = getResource("bedrock/creative_items.json");
+ stream = FileUtils.getResource("bedrock/creative_items.json");
JsonNode creativeItemEntries;
try {
- creativeItemEntries = JSON_MAPPER.readTree(stream).get("items");
+ creativeItemEntries = GeyserConnector.JSON_MAPPER.readTree(stream).get("items");
} catch (Exception e) {
throw new AssertionError("Unable to load creative items", e);
}
@@ -179,33 +163,51 @@ public class Toolbox {
}
}
CREATIVE_ITEMS = creativeItems.toArray(new ItemData[0]);
-
-
- /* Load entity identifiers */
- stream = Toolbox.getResource("bedrock/entity_identifiers.dat");
-
- try (NBTInputStream nbtInputStream = NbtUtils.createNetworkReader(stream)) {
- ENTITY_IDENTIFIERS = (CompoundTag) nbtInputStream.readTag();
- } catch (Exception e) {
- throw new AssertionError("Unable to get entities from entity identifiers", e);
- }
}
/**
- * Get an InputStream for the given resource path, throws AssertionError if resource is not found
+ * Gets an {@link ItemEntry} from the given {@link ItemStack}.
*
- * @param resource Resource to get
- * @return InputStream of the given resource
+ * @param stack the item stack
+ * @return an item entry from the given item stack
*/
- public static InputStream getResource(String resource) {
- InputStream stream = Toolbox.class.getClassLoader().getResourceAsStream(resource);
- if (stream == null) {
- throw new AssertionError("Unable to find resource: " + resource);
- }
- return stream;
+ public static ItemEntry getItem(ItemStack stack) {
+ return ITEM_ENTRIES.get(stack.getId());
}
- public static void init() {
- // no-op
+ /**
+ * 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;
}
-}
\ No newline at end of file
+
+ /**
+ * 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 f59b82ba..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<>();
+ private static final Int2ObjectMap ITEM_STACK_TRANSLATORS = new Int2ObjectOpenHashMap<>();
+ private static final List NBT_TRANSLATORS;
- // Shield ID, used in Entity.java
- public static final int SHIELD = 829;
+ protected ItemTranslator() {
+ }
- public void init() {
+ 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,34 +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 {
@@ -99,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);
}
@@ -108,62 +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);
ItemStack itemStack = new ItemStack(stack.getId(), stack.getAmount(), stack.getNbt() != null ? stack.getNbt().clone() : null);
if (itemStack.getNbt() != null) {
- for (NbtItemStackTranslator translator : nbtItemTranslators) {
+ for (NbtItemStackTranslator translator : NBT_TRANSLATORS) {
if (translator.acceptItem(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(itemStack, bedrockItem);
+ itemData = itemStackTranslator.translateToBedrock(itemStack, bedrockItem);
} else {
- return DEFAULT_TRANSLATOR.translateToBedrock(itemStack, 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/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
index 9458ec4f..776cec72 100644
--- 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
@@ -34,7 +34,7 @@ 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.NbtItemStackTranslator;
+import org.geysermc.connector.network.translators.item.NbtItemStackTranslator;
import org.geysermc.connector.network.translators.item.ItemEntry;
import org.geysermc.connector.utils.MessageUtils;
@@ -46,49 +46,51 @@ public class BasicItemTranslator extends NbtItemStackTranslator {
@Override
public void translateToBedrock(CompoundTag itemTag, ItemEntry itemEntry) {
- if (itemTag.contains("display")) {
- CompoundTag displayTag = itemTag.get("display");
- if (displayTag.contains("Name")) {
- StringTag nameTag = displayTag.get("Name");
+ 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 {
- displayTag.put(new StringTag("Name", toBedrockMessage(nameTag)));
+ lore.add(new StringTag("", toBedrockMessage((StringTag) tag)));
} 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));
- }
+ displayTag.put(new ListTag("Lore", lore));
}
}
@Override
public void translateToJava(CompoundTag itemTag, ItemEntry itemEntry) {
- if (itemTag.contains("display")) {
- CompoundTag displayTag = itemTag.get("display");
- if (displayTag.contains("Name")) {
- StringTag nameTag = displayTag.get("Name");
- displayTag.put(new StringTag("Name", toJavaMessage(nameTag)));
- }
+ 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));
+ 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));
}
}
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 0e329669..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<>();
@@ -100,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 skinParts = Arrays.asList(SkinPart.values());
ClientSettingsPacket clientSettingsPacket = new ClientSettingsPacket(locale, (byte) session.getRenderDistance(), ChatVisibility.FULL, true, skinParts, Hand.MAIN_HAND);
- session.getDownstream().getSession().send(clientSettingsPacket);
+ 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 31bfcf7c..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,6 +25,7 @@
package org.geysermc.connector.network.translators.java.entity.player;
+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;
@@ -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) {
- session.setTeleportCache(new TeleportCache(packet.getX(), packet.getY(), packet.getZ(), packet.getTeleportId()));
- entity.moveAbsolute(session, Vector3f.from(packet.getX(), packet.getY(), packet.getZ()), packet.getYaw(), packet.getPitch(), true, true);
- } else {
+ 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.getDownstream().getSession().send(teleportConfirmPacket);
+ session.sendDownstreamPacket(teleportConfirmPacket);
+ return;
}
}
+
+ // 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
index 3fc974c6..906c68db 100644
--- 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
@@ -32,7 +32,7 @@ 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.utils.SoundUtils;
+import org.geysermc.connector.network.translators.sound.SoundRegistry;
@Translator(packet = ServerStopSoundPacket.class)
public class JavaPlayerStopSoundTranslator extends PacketTranslator {
@@ -48,7 +48,7 @@ public class JavaPlayerStopSoundTranslator extends PacketTranslator "
+ soundMapping + (soundMapping == null ? "[not found]" : "")
@@ -68,7 +68,7 @@ public class JavaPlayerStopSoundTranslator extends PacketTranslator " + 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 73%
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 d544e24b..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.type.object.HangingDirection;
+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.FishingHookEntity;
import org.geysermc.connector.entity.ItemFrameEntity;
-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.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) {
+ 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,15 +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() == ObjectType.ITEM_FRAME) {
+ } 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/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,15 @@ public class JavaOpenWindowTranslator extends PacketTranslator InventoryUtils.openInventory(session, newInventory), 500, TimeUnit.MILLISECONDS);
- 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 ea197097..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
@@ -29,6 +29,7 @@ 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;
@@ -46,7 +47,7 @@ public class JavaBlockChangeTranslator 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());
@@ -77,15 +77,15 @@ public class JavaBlockValueTranslator extends PacketTranslator
extendPiston(session, position, (progress >= 1.0f) ? 1.0f : progress + 0.5f, progress),
@@ -121,7 +121,7 @@ public class JavaBlockValueTranslator extends PacketTranslator
retractPiston(session, position, (progress <= 0.0f) ? 0.0f : progress - 0.5f, progress),
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 9e2d2ae7..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
@@ -98,7 +98,7 @@ public class JavaChunkDataTranslator extends PacketTranslator blockEntityEntry : chunkData.getLoadBlockEntitiesLater().object2IntEntrySet()) {
diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/java/world/JavaCollectItemTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/java/world/JavaCollectItemTranslator.java
index b4287b08..31379bd2 100644
--- a/connector/src/main/java/org/geysermc/connector/network/translators/java/world/JavaCollectItemTranslator.java
+++ b/connector/src/main/java/org/geysermc/connector/network/translators/java/world/JavaCollectItemTranslator.java
@@ -50,6 +50,6 @@ public class JavaCollectItemTranslator extends PacketTranslator= 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);
@@ -62,6 +64,6 @@ public class JavaExplosionTranslator 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
index aa4ade4a..f51e35fe 100644
--- 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
@@ -35,7 +35,7 @@ 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.utils.SoundUtils;
+import org.geysermc.connector.network.translators.sound.SoundRegistry;
@Translator(packet = ServerPlayBuiltinSoundPacket.class)
public class JavaPlayBuiltinSoundTranslator extends PacketTranslator {
@@ -44,11 +44,9 @@ public class JavaPlayBuiltinSoundTranslator extends PacketTranslator "
- + soundMapping + (soundMapping == null ? "[not found]" : "")
- + " - " + packet.toString());
+ SoundRegistry.SoundMapping soundMapping = SoundRegistry.fromJava(packetSound);
if (soundMapping == null) {
+ session.getConnector().getLogger().debug("[Builtin] Sound mapping " + packetSound + " not found - " + packet.toString());
return;
}
@@ -57,16 +55,16 @@ public class JavaPlayBuiltinSoundTranslator extends PacketTranslator " + soundPacket.toString());
+ 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
index e22db97e..da23adb5 100644
--- 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
@@ -33,13 +33,18 @@ 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.utils.EffectUtils;
+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 {
@@ -50,7 +55,7 @@ public class JavaPlayEffectTranslator extends PacketTranslator 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.getUpstream().sendPacket(soundEvent);
+ session.sendUpstreamPacket(soundEvent);
}
} else {
GeyserConnector.getInstance().getLogger().debug("No effect handling for sound effect: " + packet.getEffect());
@@ -139,7 +159,7 @@ public class JavaPlayEffectTranslator extends PacketTranslator {
@@ -51,17 +51,13 @@ public class JavaPlayerPlaySoundTranslator extends PacketTranslator "
- + soundMapping + (soundMapping == null ? "[not found]" : "")
- + " - " + packet.toString());
+ 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.");
- playsound = packetSound;
+ .debug("[PlaySound] Defaulting to sound server gave us for " + packet.toString());
+ playsound = packetSound.replace("minecraft:", "");
} else {
playsound = soundMapping.getPlaysound();
}
@@ -72,7 +68,6 @@ public class JavaPlayerPlaySoundTranslator extends PacketTranslator " + playSoundPacket);
+ 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
index 1f9bcba3..63512047 100644
--- 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
@@ -34,12 +34,12 @@ 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.Translators;
+import org.geysermc.connector.network.translators.item.ItemTranslator;
import org.geysermc.connector.network.translators.world.block.BlockTranslator;
-import org.geysermc.connector.utils.EffectUtils;
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 {
@@ -52,7 +52,7 @@ public class JavaSpawnParticleTranslator 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/java/world/JavaWorldBorderTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/java/world/JavaWorldBorderTranslator.java
index 6795e78f..a58650eb 100644
--- a/connector/src/main/java/org/geysermc/connector/network/translators/java/world/JavaWorldBorderTranslator.java
+++ b/connector/src/main/java/org/geysermc/connector/network/translators/java/world/JavaWorldBorderTranslator.java
@@ -32,7 +32,7 @@ import com.nukkitx.math.vector.Vector2f;
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.world.WorldBorder;
+import org.geysermc.connector.network.translators.world.WorldBorder;
@Translator(packet = ServerWorldBorderPacket.class)
public class JavaWorldBorderTranslator extends PacketTranslator {
@@ -42,28 +42,30 @@ public class JavaWorldBorderTranslator extends PacketTranslator SOUNDS;
+ private SoundRegistry() {
+ }
+
public static void init() {
// no-op
}
static {
/* Load sound mappings */
- InputStream stream = Toolbox.getResource("mappings/sounds.json");
+ InputStream stream = FileUtils.getResource("mappings/sounds.json");
JsonNode soundsTree;
try {
- soundsTree = Toolbox.JSON_MAPPER.readTree(stream);
+ soundsTree = GeyserConnector.JSON_MAPPER.readTree(stream);
} catch (IOException e) {
throw new AssertionError("Unable to load sound mappings", e);
}
@@ -83,6 +87,13 @@ public class SoundUtils {
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("\\.", "_"));
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
index 00269cc7..367f9beb 100644
--- 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
@@ -30,7 +30,7 @@ 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.Translators;
+import org.geysermc.connector.network.translators.item.ItemRegistry;
import org.geysermc.connector.network.translators.sound.BlockSoundInteractionHandler;
import org.geysermc.connector.network.translators.sound.SoundHandler;
@@ -39,7 +39,7 @@ public class BucketSoundInteractionHandler implements BlockSoundInteractionHandl
@Override
public void handleInteraction(GeyserSession session, Vector3f position, String identifier) {
- String handItemIdentifier = Translators.getItemTranslator().getItem(session.getInventory().getItemInHand()).getJavaIdentifier();
+ String handItemIdentifier = ItemRegistry.getItem(session.getInventory().getItemInHand()).getJavaIdentifier();
LevelSoundEventPacket soundEventPacket = new LevelSoundEventPacket();
soundEventPacket.setPosition(position);
soundEventPacket.setIdentifier(":");
@@ -67,7 +67,7 @@ public class BucketSoundInteractionHandler implements BlockSoundInteractionHandl
}
if (soundEvent != null) {
soundEventPacket.setSound(soundEvent);
- session.getUpstream().sendPacket(soundEventPacket);
+ 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
index 260ad814..4b74a678 100644
--- 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
@@ -43,6 +43,6 @@ public class ComparatorSoundInteractHandler implements BlockSoundInteractionHand
levelEventPacket.setPosition(position);
levelEventPacket.setType(LevelEventType.REDSTONE_TRIGGER);
levelEventPacket.setData(powered ? 500 : 550);
- session.getUpstream().sendPacket(levelEventPacket);
+ 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
index b8e2854d..39a07c3a 100644
--- 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
@@ -42,6 +42,6 @@ public class DoorSoundInteractionHandler implements BlockSoundInteractionHandler
levelEventPacket.setType(LevelEventType.SOUND_DOOR);
levelEventPacket.setPosition(position);
levelEventPacket.setData(0);
- session.getUpstream().sendPacket(levelEventPacket);
+ 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
index b28133b3..290aa7bd 100644
--- 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
@@ -45,6 +45,6 @@ public class FlintAndSteelInteractionHandler implements BlockSoundInteractionHan
levelSoundEventPacket.setIdentifier(":");
levelSoundEventPacket.setSound(SoundEvent.IGNITE);
levelSoundEventPacket.setExtraData(-1);
- session.getUpstream().sendPacket(levelSoundEventPacket);
+ 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
index e2957103..e5445e9d 100644
--- 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
@@ -46,6 +46,6 @@ public class GrassPathInteractionHandler implements BlockSoundInteractionHandler
levelSoundEventPacket.setIdentifier(":");
levelSoundEventPacket.setSound(SoundEvent.ITEM_USE_ON);
levelSoundEventPacket.setExtraData(BlockTranslator.getBedrockBlockId(BlockTranslator.getJavaBlockState(identifier)));
- session.getUpstream().sendPacket(levelSoundEventPacket);
+ 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
index c9d4299b..17d346ae 100644
--- 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
@@ -46,6 +46,6 @@ public class HoeInteractionHandler implements BlockSoundInteractionHandler {
levelSoundEventPacket.setIdentifier(":");
levelSoundEventPacket.setSound(SoundEvent.ITEM_USE_ON);
levelSoundEventPacket.setExtraData(BlockTranslator.getBedrockBlockId(BlockTranslator.getJavaBlockState(identifier)));
- session.getUpstream().sendPacket(levelSoundEventPacket);
+ 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
index cfbe7727..fb39d4ac 100644
--- 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
@@ -43,6 +43,6 @@ public class LeverSoundInteractionHandler implements BlockSoundInteractionHandle
levelEventPacket.setPosition(position);
levelEventPacket.setType(LevelEventType.REDSTONE_TRIGGER);
levelEventPacket.setData(powered ? 600 : 500);
- session.getUpstream().sendPacket(levelEventPacket);
+ 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
index d4046eea..d33d68af 100644
--- 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
@@ -31,7 +31,7 @@ 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.Translators;
+import org.geysermc.connector.network.translators.item.ItemRegistry;
import org.geysermc.connector.network.translators.sound.EntitySoundInteractionHandler;
import org.geysermc.connector.network.translators.sound.SoundHandler;
@@ -40,7 +40,7 @@ public class MilkCowSoundInteractionHandler implements EntitySoundInteractionHan
@Override
public void handleInteraction(GeyserSession session, Vector3f position, Entity value) {
- if (!Translators.getItemTranslator().getItem(session.getInventory().getItemInHand()).getJavaIdentifier().equals("minecraft:bucket")) {
+ if (!ItemRegistry.getItem(session.getInventory().getItemInHand()).getJavaIdentifier().equals("minecraft:bucket")) {
return;
}
LevelSoundEventPacket levelSoundEventPacket = new LevelSoundEventPacket();
@@ -50,6 +50,6 @@ public class MilkCowSoundInteractionHandler implements EntitySoundInteractionHan
levelSoundEventPacket.setIdentifier(":");
levelSoundEventPacket.setSound(SoundEvent.MILK);
levelSoundEventPacket.setExtraData(-1);
- session.getUpstream().sendPacket(levelSoundEventPacket);
+ session.sendUpstreamPacket(levelSoundEventPacket);
}
}
diff --git a/connector/src/main/java/org/geysermc/connector/world/WorldBorder.java b/connector/src/main/java/org/geysermc/connector/network/translators/world/WorldBorder.java
similarity index 81%
rename from connector/src/main/java/org/geysermc/connector/world/WorldBorder.java
rename to connector/src/main/java/org/geysermc/connector/network/translators/world/WorldBorder.java
index f7ea173b..a06dd0fd 100644
--- a/connector/src/main/java/org/geysermc/connector/world/WorldBorder.java
+++ b/connector/src/main/java/org/geysermc/connector/network/translators/world/WorldBorder.java
@@ -24,11 +24,10 @@
*
*/
-package org.geysermc.connector.world;
+package org.geysermc.connector.network.translators.world;
import com.nukkitx.math.vector.Vector2f;
import com.nukkitx.math.vector.Vector3f;
-import com.nukkitx.protocol.bedrock.packet.TextPacket;
import lombok.Getter;
import lombok.NonNull;
import lombok.RequiredArgsConstructor;
@@ -38,18 +37,23 @@ import org.geysermc.connector.entity.Entity;
import org.geysermc.connector.entity.PlayerEntity;
import org.geysermc.connector.network.session.GeyserSession;
+import java.util.concurrent.ScheduledFuture;
+import java.util.concurrent.TimeUnit;
+
@Getter
@Setter
@RequiredArgsConstructor
public class WorldBorder {
private @NonNull Vector2f center;
- private @NonNull double radius;
private @NonNull double oldRadius;
private @NonNull double newRadius;
private @NonNull long speed;
private @NonNull int warningTime;
private @NonNull int warningBlocks;
+ // Runs the onTick method
+ private ScheduledFuture> worldBorderTask;
+
private double minX;
private double minZ;
private double maxX;
@@ -64,13 +68,24 @@ public class WorldBorder {
/**
* Updates the min and max positions of the world border.
- * This should be called every time there is a modifcation to either the center coordinates or the radius.
+ * This should be called every time there is a modification to either the center coordinates or the radius.
*/
- public void update() {
+ public void update(GeyserSession session) {
this.minX = Math.max(center.getX() - newRadius / 2.0D, -newRadius);
this.minZ = Math.max(center.getY() - newRadius / 2.0D, -newRadius);
this.maxX = Math.min(center.getX() + newRadius / 2.0D, newRadius);
this.maxZ = Math.min(center.getY() + newRadius / 2.0D, newRadius);
+ if (worldBorderTask != null) {
+ worldBorderTask.cancel(false);
+ }
+
+ // If world border is at that number then it's 'disabled', no need to run tasks for it
+ // https://minecraft.gamepedia.com/World_border#Commands
+ if (!(newRadius >= 59999967)) {
+ worldBorderTask = session.getConnector().getGeneralThreadPool().scheduleAtFixedRate(() ->
+ onTick(session), 1, 50, TimeUnit.MILLISECONDS
+ );
+ }
}
/**
@@ -82,10 +97,7 @@ public class WorldBorder {
float entityDistance = (float) getDistanceToEdge(entity);
- if ((double) entityDistance < distance) {
- return true;
- }
- return false;
+ return (double) entityDistance < distance;
}
/**
@@ -113,4 +125,4 @@ public class WorldBorder {
return Math.min(Math.min(Math.min(minPosX, maxPosX), minPosZ), maxPosZ);
}
-}
\ No newline at end of file
+}
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
index 0890aed6..d92d8454 100644
--- 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
@@ -26,8 +26,10 @@
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;
/**
@@ -39,6 +41,28 @@ import org.geysermc.connector.network.session.GeyserSession;
*/
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
*
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
index de08b7e8..070a0592 100644
--- 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
@@ -47,6 +47,7 @@ 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<>();
@@ -74,6 +75,15 @@ public class BlockStateValues {
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;
@@ -136,6 +146,15 @@ public class BlockStateValues {
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
diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/world/block/BlockTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/world/block/BlockTranslator.java
index e614900c..d6f446f0 100644
--- a/connector/src/main/java/org/geysermc/connector/network/translators/world/block/BlockTranslator.java
+++ b/connector/src/main/java/org/geysermc/connector/network/translators/world/block/BlockTranslator.java
@@ -39,7 +39,7 @@ 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.world.block.entity.BlockEntity;
-import org.geysermc.connector.utils.Toolbox;
+import org.geysermc.connector.utils.FileUtils;
import org.reflections.Reflections;
import java.io.InputStream;
@@ -69,11 +69,16 @@ 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;
+
+ public static final int JAVA_RUNTIME_SPAWNER_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)) {
@@ -90,10 +95,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);
}
@@ -108,6 +113,9 @@ public class BlockTranslator {
int javaRuntimeId = -1;
int bedrockRuntimeId = 0;
int cobwebRuntimeId = -1;
+ int furnaceRuntimeId = -1;
+ int furnaceLitRuntimeId = -1;
+ int spawnerRuntimeId = -1;
Iterator> blocksIterator = blocks.fields();
while (blocksIterator.hasNext()) {
javaRuntimeId++;
@@ -186,6 +194,18 @@ 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;
+ }
+ }
+
+ if (javaId.startsWith("minecraft:spawner")) {
+ spawnerRuntimeId = javaRuntimeId;
+ }
+
bedrockRuntimeId++;
}
@@ -194,6 +214,21 @@ 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 (spawnerRuntimeId == -1) {
+ throw new AssertionError("Unable to find spawner in palette");
+ }
+ JAVA_RUNTIME_SPAWNER_ID = spawnerRuntimeId;
+
if (waterRuntimeId == -1) {
throw new AssertionError("Unable to find water in palette");
}
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/world/block/entity/BannerBlockEntityTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/BannerBlockEntityTranslator.java
index 2034b3d5..3e2c0a95 100644
--- a/connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/BannerBlockEntityTranslator.java
+++ b/connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/BannerBlockEntityTranslator.java
@@ -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.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/world/block/entity/BedBlockEntityTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/BedBlockEntityTranslator.java
index 543828e8..5f0b1cc0 100644
--- a/connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/BedBlockEntityTranslator.java
+++ b/connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/BedBlockEntityTranslator.java
@@ -35,7 +35,7 @@ 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/BlockEntity.java b/connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/BlockEntity.java
index d08ab561..11bfe0ea 100644
--- a/connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/BlockEntity.java
+++ b/connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/BlockEntity.java
@@ -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/world/block/entity/BlockEntityTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/BlockEntityTranslator.java
index 4545aed5..3d663926 100644
--- a/connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/BlockEntityTranslator.java
+++ b/connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/BlockEntityTranslator.java
@@ -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/world/block/entity/CampfireBlockEntityTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/CampfireBlockEntityTranslator.java
index d91e47c2..cd31636c 100644
--- a/connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/CampfireBlockEntityTranslator.java
+++ b/connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/CampfireBlockEntityTranslator.java
@@ -30,15 +30,14 @@ 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/world/block/entity/EmptyBlockEntityTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/EmptyBlockEntityTranslator.java
index f95cb89e..d1068277 100644
--- a/connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/EmptyBlockEntityTranslator.java
+++ b/connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/EmptyBlockEntityTranslator.java
@@ -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/world/block/entity/EndGatewayBlockEntityTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/EndGatewayBlockEntityTranslator.java
index 10de9d32..4cd2eaa9 100644
--- a/connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/EndGatewayBlockEntityTranslator.java
+++ b/connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/EndGatewayBlockEntityTranslator.java
@@ -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
index 6861a953..c4748c82 100644
--- 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
@@ -53,7 +53,7 @@ public class FlowerPotBlockEntityTranslator implements BedrockOnlyBlockEntity, R
updateBlockPacket.getFlags().add(UpdateBlockPacket.Flag.PRIORITY);
updateBlockPacket.getFlags().add(UpdateBlockPacket.Flag.NONE);
updateBlockPacket.getFlags().add(UpdateBlockPacket.Flag.NEIGHBORS);
- session.getUpstream().sendPacket(updateBlockPacket);
+ session.sendUpstreamPacket(updateBlockPacket);
}
/**
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
index c538f09e..168015f6 100644
--- 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
@@ -50,7 +50,7 @@ public class NoteblockBlockEntityTranslator implements RequiresBlockState {
blockEventPacket.setBlockPosition(Vector3i.from(position.getX(), position.getY(), position.getZ()));
blockEventPacket.setEventType(0);
blockEventPacket.setEventData(BlockStateValues.getNoteblockPitch(blockState));
- session.getUpstream().sendPacket(blockEventPacket);
+ session.sendUpstreamPacket(blockEventPacket);
ChunkUtils.CACHED_BLOCK_ENTITIES.remove(position);
}
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
index b0b8fa3d..373b963e 100644
--- 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
@@ -36,7 +36,7 @@ import org.geysermc.connector.network.translators.world.block.BlockStateValues;
import java.util.ArrayList;
import java.util.List;
-@BlockEntity(name = "ShulkerBox", delay = false, regex = "shulker_box")
+@BlockEntity(name = "ShulkerBox", regex = "shulker_box")
public class ShulkerBoxBlockEntityTranslator extends BlockEntityTranslator {
@Override
diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/SignBlockEntityTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/SignBlockEntityTranslator.java
index 0dde3307..6c170462 100644
--- a/connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/SignBlockEntityTranslator.java
+++ b/connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/SignBlockEntityTranslator.java
@@ -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/world/block/entity/SkullBlockEntityTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/SkullBlockEntityTranslator.java
index 7e73c846..9393f7bb 100644
--- a/connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/SkullBlockEntityTranslator.java
+++ b/connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/SkullBlockEntityTranslator.java
@@ -1,71 +1,71 @@
-/*
- * Copyright (c) 2019-2020 GeyserMC. http://geysermc.org
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE.
- *
- * @author GeyserMC
- * @link https://github.com/GeyserMC/Geyser
- */
-
-package org.geysermc.connector.network.translators.world.block.entity;
-
-import com.github.steveice10.mc.protocol.data.game.world.block.BlockState;
-import com.nukkitx.nbt.CompoundTagBuilder;
-import com.nukkitx.nbt.tag.ByteTag;
-import com.nukkitx.nbt.tag.CompoundTag;
-import com.nukkitx.nbt.tag.FloatTag;
-import com.nukkitx.nbt.tag.Tag;
-import org.geysermc.connector.network.translators.world.block.BlockStateValues;
-
-import java.util.ArrayList;
-import java.util.List;
-
-@BlockEntity(name = "Skull", delay = false, regex = "skull")
-public class SkullBlockEntityTranslator extends BlockEntityTranslator implements RequiresBlockState {
-
- @Override
- public boolean isBlock(BlockState blockState) {
- return BlockStateValues.getSkullVariant(blockState) != -1;
- }
-
- @Override
- public List> translateTag(com.github.steveice10.opennbt.tag.builtin.CompoundTag tag, BlockState blockState) {
- List> tags = new ArrayList<>();
- byte skullVariant = BlockStateValues.getSkullVariant(blockState);
- float rotation = BlockStateValues.getSkullRotation(blockState) * 22.5f;
- // Just in case...
- if (skullVariant == -1) skullVariant = 0;
- tags.add(new FloatTag("Rotation", rotation));
- tags.add(new ByteTag("SkullType", skullVariant));
- return tags;
- }
-
- @Override
- public com.github.steveice10.opennbt.tag.builtin.CompoundTag getDefaultJavaTag(String javaId, int x, int y, int z) {
- return null;
- }
-
- @Override
- public CompoundTag getDefaultBedrockTag(String bedrockId, int x, int y, int z) {
- CompoundTagBuilder tagBuilder = getConstantBedrockTag(bedrockId, x, y, z).toBuilder();
- tagBuilder.floatTag("Rotation", 0);
- tagBuilder.byteTag("SkullType", (byte) 0);
- return tagBuilder.buildRootTag();
- }
-}
+/*
+ * 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.nbt.CompoundTagBuilder;
+import com.nukkitx.nbt.tag.ByteTag;
+import com.nukkitx.nbt.tag.CompoundTag;
+import com.nukkitx.nbt.tag.FloatTag;
+import com.nukkitx.nbt.tag.Tag;
+import org.geysermc.connector.network.translators.world.block.BlockStateValues;
+
+import java.util.ArrayList;
+import java.util.List;
+
+@BlockEntity(name = "Skull", regex = "skull")
+public class SkullBlockEntityTranslator extends BlockEntityTranslator implements RequiresBlockState {
+
+ @Override
+ public boolean isBlock(BlockState blockState) {
+ return BlockStateValues.getSkullVariant(blockState) != -1;
+ }
+
+ @Override
+ public List> translateTag(com.github.steveice10.opennbt.tag.builtin.CompoundTag tag, BlockState blockState) {
+ List> tags = new ArrayList<>();
+ byte skullVariant = BlockStateValues.getSkullVariant(blockState);
+ float rotation = BlockStateValues.getSkullRotation(blockState) * 22.5f;
+ // Just in case...
+ if (skullVariant == -1) skullVariant = 0;
+ tags.add(new FloatTag("Rotation", rotation));
+ tags.add(new ByteTag("SkullType", skullVariant));
+ return tags;
+ }
+
+ @Override
+ public com.github.steveice10.opennbt.tag.builtin.CompoundTag getDefaultJavaTag(String javaId, int x, int y, int z) {
+ return null;
+ }
+
+ @Override
+ public CompoundTag getDefaultBedrockTag(String bedrockId, int x, int y, int z) {
+ CompoundTagBuilder tagBuilder = getConstantBedrockTag(bedrockId, x, y, z).toBuilder();
+ tagBuilder.floatTag("Rotation", 0);
+ tagBuilder.byteTag("SkullType", (byte) 0);
+ return tagBuilder.buildRootTag();
+ }
+}
diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/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/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 b0794e20..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,23 +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.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("_", " ");
@@ -33,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;
}
@@ -49,6 +54,6 @@ public class BlockEntityUtils {
BlockEntityDataPacket blockEntityPacket = new BlockEntityDataPacket();
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 3a9ecb86..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.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 fcc596eb..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,6 +32,9 @@ 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.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;
@@ -43,11 +46,12 @@ import org.geysermc.connector.entity.ItemFrameEntity;
import org.geysermc.connector.network.session.GeyserSession;
import org.geysermc.connector.network.translators.world.block.BlockStateValues;
import org.geysermc.connector.network.translators.world.block.entity.*;
-import org.geysermc.connector.network.translators.Translators;
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;
@@ -61,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();
@@ -89,21 +110,12 @@ 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())) {
@@ -156,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);
}
@@ -188,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);
@@ -198,12 +210,12 @@ 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 (RequiresBlockState requiresBlockState : Translators.getRequiresBlockStateMap()) {
+ 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) {
@@ -226,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);
@@ -236,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 6dd182a7..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();
@@ -49,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);
}
/**
@@ -71,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 dea42728..04b6ecc4 100644
--- a/connector/src/main/java/org/geysermc/connector/utils/FileUtils.java
+++ b/connector/src/main/java/org/geysermc/connector/utils/FileUtils.java
@@ -125,4 +125,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..ded47723 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,16 +50,22 @@ 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);
+ //Ensure at least half a second passes between closing and opening a new window
+ //The client will not open the new window if it is still closing the old one
+ long delay = 500 - (System.currentTimeMillis() - session.getLastWindowCloseTime());
//TODO: find better way to handle double chest delay
if (translator instanceof DoubleChestInventoryTranslator) {
+ delay = Math.max(delay, 200);
+ }
+ if (delay > 0) {
GeyserConnector.getInstance().getGeneralThreadPool().schedule(() -> {
translator.openInventory(session, inventory);
translator.updateInventory(session, inventory);
- }, 200, TimeUnit.MILLISECONDS);
+ }, delay, TimeUnit.MILLISECONDS);
} else {
translator.openInventory(session, inventory);
translator.updateInventory(session, inventory);
@@ -64,27 +76,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 +125,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 4e8497c6..f66e869d 100644
--- a/connector/src/main/java/org/geysermc/connector/utils/MathUtils.java
+++ b/connector/src/main/java/org/geysermc/connector/utils/MathUtils.java
@@ -37,4 +37,19 @@ public class MathUtils {
int truncated = (int) floatNumber;
return floatNumber > truncated ? truncated + 1 : truncated;
}
+
+ /**
+ * 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 3c924912..d79cdab8 100644
--- a/connector/src/main/java/org/geysermc/connector/utils/MessageUtils.java
+++ b/connector/src/main/java/org/geysermc/connector/utils/MessageUtils.java
@@ -137,6 +137,23 @@ public class MessageUtils {
}
}
+ /**
+ * 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());
+ }
+ }
+
public static String getBedrockMessage(String message) {
Component component = phraseJavaMessage(message);
return LegacyComponentSerializer.legacy().serialize(component);
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 7c2b7fc0..aea9ba18 100644
--- a/connector/src/main/java/org/geysermc/connector/utils/SkinProvider.java
+++ b/connector/src/main/java/org/geysermc/connector/utils/SkinProvider.java
@@ -35,10 +35,10 @@ import org.geysermc.connector.GeyserConnector;
import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.image.BufferedImage;
-import java.io.ByteArrayInputStream;
-import java.io.ByteArrayOutputStream;
-import java.io.IOException;
+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;
@@ -51,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<>();
@@ -58,9 +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);
}
@@ -164,12 +206,94 @@ 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 future;
+ if (newThread) {
+ future = CompletableFuture.supplyAsync(() -> supplyEars(skin, earsUrl, provider), EXECUTOR_SERVICE)
+ .whenCompleteAsync((outSkin, throwable) -> { });
+ } else {
+ Skin ears = supplyEars(skin, earsUrl, provider); // blocking
+ future = CompletableFuture.completedFuture(ears);
+ }
+ return future;
+ }
+
+ /**
+ * Try and find an ear texture for a Java player
+ *
+ * @param officialSkin The current players skin
+ * @param playerId The players UUID
+ * @param username The players username
+ * @param newThread Should we start in a new thread
+ * @return The updated skin with ears
+ */
+ public static CompletableFuture requestUnofficialEars(Skin officialSkin, UUID playerId, String username, boolean newThread) {
+ for (EarsProvider provider : EarsProvider.VALUES) {
+ Skin skin1 = getOrDefault(
+ requestEars(provider.getUrlFor(playerId, username), provider, newThread, officialSkin),
+ officialSkin, 4
+ );
+ if (skin1.isEars()) {
+ return CompletableFuture.completedFuture(skin1);
+ }
+ }
+
+ return CompletableFuture.completedFuture(officialSkin);
+ }
+
+ public static CompletableFuture requestBedrockCape(UUID playerID, boolean newThread) {
+ Cape bedrockCape = cachedCapes.getOrDefault(playerID.toString() + ".Bedrock", EMPTY_CAPE);
+ return CompletableFuture.completedFuture(bedrockCape);
+ }
+
+ public static CompletableFuture requestBedrockGeometry(SkinGeometry currentGeometry, UUID playerID, boolean newThread) {
+ SkinGeometry bedrockGeometry = cachedGeometry.getOrDefault(playerID, currentGeometry);
+ return CompletableFuture.completedFuture(bedrockGeometry);
+ }
+
+ public static void storeBedrockSkin(UUID playerID, String skinID, byte[] skinData) {
+ Skin skin = new Skin(playerID, skinID, skinData, System.currentTimeMillis(), true, false);
+ cachedSkins.put(playerID, skin);
+ }
+
+ public static void storeBedrockCape(UUID playerID, byte[] capeData) {
+ Cape cape = new Cape(playerID.toString() + ".Bedrock", playerID.toString(), capeData, System.currentTimeMillis(), false);
+ cachedCapes.put(playerID.toString() + ".Bedrock", cape);
+ }
+
+ public static void storeBedrockGeometry(UUID playerID, byte[] geometryName, byte[] geometryData) {
+ SkinGeometry geometry = new SkinGeometry(new String(geometryName), new String(geometryData), false);
+ cachedGeometry.put(playerID, geometry);
+ }
+
+ /**
+ * Stores the ajusted skin with the ear texture to the cache
+ *
+ * @param playerID The UUID to cache it against
+ * @param skin The skin to cache
+ */
+ public static void storeEarSkin(UUID playerID, Skin skin) {
+ cachedSkins.put(playerID, skin);
+ }
+
+ /**
+ * Stores the geometry for a Java player with ears
+ *
+ * @param playerID The UUID to cache it against
+ * @param isSlim If the player is using an slim base
+ */
+ public static void storeEarGeometry(UUID playerID, boolean isSlim) {
+ cachedGeometry.put(playerID, SkinGeometry.getEars(isSlim));
+ }
+
private static Skin supplySkin(UUID uuid, String textureUrl) {
byte[] skin = EMPTY_SKIN.getSkinData();
try {
skin = requestImage(textureUrl, null);
} catch (Exception ignored) {} // just ignore I guess
- return new Skin(uuid, textureUrl, skin, System.currentTimeMillis(), false);
+ return new Skin(uuid, textureUrl, skin, System.currentTimeMillis(), false, false);
}
private static Cape supplyCape(String capeUrl, CapeProvider provider) {
@@ -189,33 +313,67 @@ public class SkinProvider {
);
}
+ /**
+ * Get the ears texture and place it on the skin from the given URL
+ *
+ * @param existingSkin The players current skin
+ * @param earsUrl The URL to get the ears texture from
+ * @param provider The ears texture provider
+ * @return The updated skin with ears
+ */
+ private static Skin supplyEars(Skin existingSkin, String earsUrl, EarsProvider provider) {
+ try {
+ // Get the ears texture
+ BufferedImage ears = ImageIO.read(new URL(earsUrl));
+ if (ears == null) throw new NullPointerException();
+
+ // Convert the skin data to a BufferedImage
+ int height = (existingSkin.getSkinData().length / 4 / 64);
+ BufferedImage skinImage = imageDataToBufferedImage(existingSkin.getSkinData(), 64, height);
+
+ // Create a new image with the ears texture over it
+ BufferedImage newSkin = new BufferedImage(skinImage.getWidth(), skinImage.getHeight(), BufferedImage.TYPE_INT_ARGB);
+ Graphics2D g = (Graphics2D) newSkin.getGraphics();
+ g.drawImage(skinImage, 0, 0, null);
+ g.drawImage(ears, 24, 0, null);
+
+ // Turn the buffered image back into an array of bytes
+ byte[] data = bufferedImageToImageData(newSkin);
+ skinImage.flush();
+
+ // Create a new skin object with the new infomation
+ return new Skin(
+ existingSkin.getSkinOwner(),
+ existingSkin.getTextureUrl(),
+ data,
+ System.currentTimeMillis(),
+ true,
+ true
+ );
+ } catch (Exception ignored) {} // just ignore I guess
+
+ return existingSkin;
+ }
+
private static byte[] requestImage(String imageUrl, CapeProvider provider) throws Exception {
BufferedImage image = downloadImage(imageUrl, provider);
GeyserConnector.getInstance().getLogger().debug("Downloaded " + imageUrl);
// if the requested image is an cape
if (provider != null) {
- image = image.getWidth() > 64 ? scale(image) : image;
- BufferedImage newImage = new BufferedImage(64, 32, BufferedImage.TYPE_INT_RGB);
+ while(image.getWidth() > 64) {
+ image = scale(image);
+ }
+ BufferedImage newImage = new BufferedImage(64, 32, BufferedImage.TYPE_INT_ARGB);
Graphics g = newImage.createGraphics();
g.drawImage(image, 0, 0, image.getWidth(), image.getHeight(), null);
g.dispose();
image = newImage;
}
- try (ByteArrayOutputStream outputStream = new ByteArrayOutputStream(image.getWidth() * 4 + image.getHeight() * 4)) {
- for (int y = 0; y < image.getHeight(); y++) {
- for (int x = 0; x < image.getWidth(); x++) {
- int rgba = image.getRGB(x, y);
- outputStream.write((rgba >> 16) & 0xFF);
- outputStream.write((rgba >> 8) & 0xFF);
- outputStream.write(rgba & 0xFF);
- outputStream.write((rgba >> 24) & 0xFF);
- }
- }
- image.flush();
- return outputStream.toByteArray();
- }
+ byte[] data = bufferedImageToImageData(image);
+ image.flush();
+ return data;
}
private static BufferedImage downloadImage(String imageUrl, CapeProvider provider) throws IOException {
@@ -237,7 +395,7 @@ public class SkinProvider {
}
private static BufferedImage scale(BufferedImage bufferedImage) {
- BufferedImage resized = new BufferedImage(bufferedImage.getWidth() / 2, bufferedImage.getHeight() / 2, BufferedImage.TYPE_INT_RGB);
+ BufferedImage resized = new BufferedImage(bufferedImage.getWidth() / 2, bufferedImage.getHeight() / 2, BufferedImage.TYPE_INT_ARGB);
Graphics2D g2 = resized.createGraphics();
g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
g2.drawImage(bufferedImage, 0, 0, bufferedImage.getWidth() / 2, bufferedImage.getHeight() / 2, null);
@@ -245,6 +403,60 @@ public class SkinProvider {
return resized;
}
+ /**
+ * Get the RGBA int for a given index in some image data
+ *
+ * @param index Index to get
+ * @param data Image data to find in
+ * @return An int representing RGBA
+ */
+ private static int getRGBA(int index, byte[] data) {
+ return (data[index] & 0xFF) << 16 | (data[index + 1] & 0xFF) << 8 |
+ data[index + 2] & 0xFF | (data[index + 3] & 0xFF) << 24;
+ }
+
+ /**
+ * Convert a byte[] to a BufferedImage
+ *
+ * @param imageData The byte[] to convert
+ * @param imageWidth The width of the target image
+ * @param imageHeight The height of the target image
+ * @return The converted BufferedImage
+ */
+ public static BufferedImage imageDataToBufferedImage(byte[] imageData, int imageWidth, int imageHeight) {
+ BufferedImage image = new BufferedImage(imageWidth, imageHeight, BufferedImage.TYPE_INT_ARGB);
+ int index = 0;
+ for (int y = 0; y < imageHeight; y++) {
+ for (int x = 0; x < imageWidth; x++) {
+ image.setRGB(x, y, getRGBA(index, imageData));
+ index += 4;
+ }
+ }
+
+ return image;
+ }
+
+ /**
+ * Convert a BufferedImage to a byte[]
+ *
+ * @param image The BufferedImage to convert
+ * @return The converted byte[]
+ */
+ public static byte[] bufferedImageToImageData(BufferedImage image) {
+ ByteArrayOutputStream outputStream = new ByteArrayOutputStream(image.getWidth() * 4 + image.getHeight() * 4);
+ for (int y = 0; y < image.getHeight(); y++) {
+ for (int x = 0; x < image.getWidth(); x++) {
+ int rgba = image.getRGB(x, y);
+ outputStream.write((rgba >> 16) & 0xFF);
+ outputStream.write((rgba >> 8) & 0xFF);
+ outputStream.write(rgba & 0xFF);
+ outputStream.write((rgba >> 24) & 0xFF);
+ }
+ }
+
+ return outputStream.toByteArray();
+ }
+
public static T getOrDefault(CompletableFuture future, T defaultValue, int timeoutInSeconds) {
try {
return future.get(timeoutInSeconds, TimeUnit.SECONDS);
@@ -267,6 +479,7 @@ public class SkinProvider {
private byte[] skinData;
private long requestedOn;
private boolean updated;
+ private boolean ears;
private Skin(long requestedOn, String textureUrl, byte[] skinData) {
this.requestedOn = requestedOn;
@@ -285,6 +498,34 @@ public class SkinProvider {
private boolean failed;
}
+ @AllArgsConstructor
+ @Getter
+ public static class SkinGeometry {
+ private String geometryName;
+ private String geometryData;
+ private boolean failed;
+
+ /**
+ * Generate generic geometry
+ *
+ * @param isSlim Should it be the alex model
+ * @return The generic geometry object
+ */
+ public static SkinGeometry getLegacy(boolean isSlim) {
+ return new SkinProvider.SkinGeometry("{\"geometry\" :{\"default\" :\"geometry.humanoid.custom" + (isSlim ? "Slim" : "") + "\"}}", "", true);
+ }
+
+ /**
+ * Generate basic geometry with ears
+ *
+ * @param isSlim Should it be the alex model
+ * @return The generated geometry for the ears model
+ */
+ public static SkinGeometry getEars(boolean isSlim) {
+ return new SkinProvider.SkinGeometry("{\"geometry\" :{\"default\" :\"geometry.humanoid.ears" + (isSlim ? "Slim" : "") + "\"}}", (isSlim ? EARS_GEOMETRY_SLIM : EARS_GEOMETRY), false);
+ }
+ }
+
/*
* Sorted by 'priority'
*/
@@ -324,4 +565,34 @@ public class SkinProvider {
UUID,
UUID_DASHED
}
+
+ /*
+ * Sorted by 'priority'
+ */
+ @AllArgsConstructor
+ @NoArgsConstructor
+ @Getter
+ public enum EarsProvider {
+ MINECRAFTCAPES("https://www.minecraftcapes.co.uk/getEars/%s", CapeUrlType.UUID);
+
+ public static final EarsProvider[] VALUES = values();
+ private String url;
+ private CapeUrlType type;
+
+ public String getUrlFor(String type) {
+ return String.format(url, type);
+ }
+
+ public String getUrlFor(UUID uuid, String username) {
+ return getUrlFor(toRequestedType(type, uuid, username));
+ }
+
+ public static String toRequestedType(CapeUrlType type, UUID uuid, String username) {
+ switch (type) {
+ case UUID: return uuid.toString().replace("-", "");
+ case UUID_DASHED: return uuid.toString();
+ default: return username;
+ }
+ }
+ }
}
diff --git a/connector/src/main/java/org/geysermc/connector/utils/SkinUtils.java b/connector/src/main/java/org/geysermc/connector/utils/SkinUtils.java
index 9ce025e7..48e4c4c8 100644
--- a/connector/src/main/java/org/geysermc/connector/utils/SkinUtils.java
+++ b/connector/src/main/java/org/geysermc/connector/utils/SkinUtils.java
@@ -39,6 +39,7 @@ import org.geysermc.common.AuthType;
import org.geysermc.connector.GeyserConnector;
import org.geysermc.connector.entity.PlayerEntity;
import org.geysermc.connector.network.session.GeyserSession;
+import org.geysermc.connector.network.session.auth.BedrockClientData;
import java.nio.charset.StandardCharsets;
import java.util.Base64;
@@ -52,6 +53,8 @@ public class SkinUtils {
GameProfileData data = GameProfileData.from(profile);
SkinProvider.Cape cape = SkinProvider.getCachedCape(data.getCapeUrl());
+ SkinProvider.SkinGeometry geometry = SkinProvider.SkinGeometry.getLegacy(data.isAlex());
+
return buildEntryManually(
profile.getId(),
profile.getName(),
@@ -60,8 +63,8 @@ public class SkinUtils {
SkinProvider.getCachedSkin(profile.getId()).getSkinData(),
cape.getCapeId(),
cape.getCapeData(),
- getLegacySkinGeometry("geometry.humanoid.custom" + (data.isAlex() ? "Slim" : "")),
- ""
+ geometry.getGeometryName(),
+ geometry.getGeometryData()
);
}
@@ -74,8 +77,8 @@ public class SkinUtils {
SkinProvider.STEVE_SKIN,
SkinProvider.EMPTY_CAPE.getCapeId(),
SkinProvider.EMPTY_CAPE.getCapeData(),
- getLegacySkinGeometry("geometry.humanoid"),
- ""
+ SkinProvider.EMPTY_GEOMETRY.getGeometryName(),
+ SkinProvider.EMPTY_GEOMETRY.getGeometryData()
);
}
@@ -85,7 +88,7 @@ public class SkinUtils {
String geometryName, String geometryData) {
SerializedSkin serializedSkin = SerializedSkin.of(
skinId, geometryName, ImageData.of(skinData), Collections.emptyList(),
- ImageData.of(capeData), geometryData, "", true, false, false, capeId, uuid.toString()
+ ImageData.of(capeData), geometryData, "", true, false, !capeId.equals(SkinProvider.EMPTY_CAPE.getCapeId()), capeId, uuid.toString()
);
PlayerListPacket.Entry entry = new PlayerListPacket.Entry(uuid);
@@ -112,6 +115,9 @@ public class SkinUtils {
* @return The built GameProfileData
*/
public static GameProfileData from(GameProfile profile) {
+ // Fallback to the offline mode of working it out
+ boolean isAlex = ((profile.getId().hashCode() % 2) == 1);
+
try {
GameProfile.Property skinProperty = profile.getProperty("textures");
@@ -121,7 +127,7 @@ public class SkinUtils {
JsonNode skinTexture = textures.get("SKIN");
String skinUrl = skinTexture.get("url").asText();
- boolean isAlex = skinTexture.has("metadata");
+ isAlex = skinTexture.has("metadata");
String capeUrl = null;
if (textures.has("CAPE")) {
@@ -135,7 +141,7 @@ public class SkinUtils {
GeyserConnector.getInstance().getLogger().debug("Got invalid texture data for " + profile.getName() + " " + exception.getMessage());
}
// return default skin with default cape when texture data is invalid
- return new GameProfileData(SkinProvider.EMPTY_SKIN.getTextureUrl(), SkinProvider.EMPTY_CAPE.getTextureUrl(), false);
+ return new GameProfileData((isAlex ? SkinProvider.EMPTY_SKIN_ALEX.getTextureUrl() : SkinProvider.EMPTY_SKIN.getTextureUrl()), SkinProvider.EMPTY_CAPE.getTextureUrl(), isAlex);
}
}
}
@@ -151,6 +157,12 @@ public class SkinUtils {
SkinProvider.Skin skin = skinAndCape.getSkin();
SkinProvider.Cape cape = skinAndCape.getCape();
+ if (cape.isFailed()) {
+ cape = SkinProvider.getOrDefault(SkinProvider.requestBedrockCape(
+ entity.getUuid(), false
+ ), SkinProvider.EMPTY_CAPE, 3);
+ }
+
if (cape.isFailed() && SkinProvider.ALLOW_THIRD_PARTY_CAPES) {
cape = SkinProvider.getOrDefault(SkinProvider.requestUnofficialCape(
cape, entity.getUuid(),
@@ -158,6 +170,38 @@ public class SkinUtils {
), SkinProvider.EMPTY_CAPE, SkinProvider.CapeProvider.VALUES.length * 3);
}
+ SkinProvider.SkinGeometry geometry = SkinProvider.SkinGeometry.getLegacy(data.isAlex());
+ geometry = SkinProvider.getOrDefault(SkinProvider.requestBedrockGeometry(
+ geometry, entity.getUuid(), false
+ ), geometry, 3);
+
+ // Not a bedrock player check for ears
+ if (geometry.isFailed() && SkinProvider.ALLOW_THIRD_PARTY_EARS) {
+ boolean isEars = false;
+
+ // Its deadmau5, gotta support his skin :)
+ if (entity.getUuid().toString().equals("1e18d5ff-643d-45c8-b509-43b8461d8614")) {
+ isEars = true;
+ } else {
+ // Get the ears texture for the player
+ skin = SkinProvider.getOrDefault(SkinProvider.requestUnofficialEars(
+ skin, entity.getUuid(), entity.getUsername(), false
+ ), skin, 3);
+
+ isEars = skin.isEars();
+ }
+
+ // Does the skin have an ears texture
+ if (isEars) {
+ // Get the new geometry
+ geometry = SkinProvider.SkinGeometry.getEars(data.isAlex());
+
+ // Store the skin and geometry for the ears
+ SkinProvider.storeEarSkin(entity.getUuid(), skin);
+ SkinProvider.storeEarGeometry(entity.getUuid(), data.isAlex());
+ }
+ }
+
if (entity.getLastSkinUpdate() < skin.getRequestedOn()) {
entity.setLastSkinUpdate(skin.getRequestedOn());
@@ -170,19 +214,36 @@ public class SkinUtils {
skin.getSkinData(),
cape.getCapeId(),
cape.getCapeData(),
- getLegacySkinGeometry("geometry.humanoid.custom" + (data.isAlex() ? "Slim" : "")),
- ""
+ geometry.getGeometryName(),
+ geometry.getGeometryData()
);
+ // If it is our skin we replace the UUID with the authdata UUID
+ if (session.getPlayerEntity() == entity) {
+ // Copy the entry with our identity instead.
+ PlayerListPacket.Entry copy = new PlayerListPacket.Entry(session.getAuthData().getUUID());
+ copy.setName(updatedEntry.getName());
+ copy.setEntityId(updatedEntry.getEntityId());
+ copy.setSkin(updatedEntry.getSkin());
+ copy.setXuid(updatedEntry.getXuid());
+ copy.setPlatformChatId(updatedEntry.getPlatformChatId());
+ copy.setTeacher(updatedEntry.isTeacher());
+ updatedEntry = copy;
+ }
+
PlayerListPacket playerRemovePacket = new PlayerListPacket();
playerRemovePacket.setAction(PlayerListPacket.Action.REMOVE);
playerRemovePacket.getEntries().add(updatedEntry);
- session.getUpstream().sendPacket(playerRemovePacket);
+ session.sendUpstreamPacket(playerRemovePacket);
PlayerListPacket playerAddPacket = new PlayerListPacket();
playerAddPacket.setAction(PlayerListPacket.Action.ADD);
playerAddPacket.getEntries().add(updatedEntry);
- session.getUpstream().sendPacket(playerAddPacket);
+ session.sendUpstreamPacket(playerAddPacket);
+
+ if(entity.getUuid().equals(session.getPlayerEntity().getUuid())) {
+ session.fetchOurSkin(updatedEntry);
+ }
}
}
} catch (Exception e) {
@@ -194,13 +255,31 @@ public class SkinUtils {
});
}
- /**
- * Create a basic geometry json for the given name
- *
- * @param geometryName Geometry name to use
- * @return Geometry data as a json string
- */
- private static String getLegacySkinGeometry(String geometryName) {
- return "{\"geometry\" :{\"default\" :\"" + geometryName + "\"}}";
+ public static void handleBedrockSkin(PlayerEntity playerEntity, BedrockClientData clientData) {
+ GameProfileData data = GameProfileData.from(playerEntity.getProfile());
+
+ GeyserConnector.getInstance().getLogger().info("Registering bedrock skin for " + playerEntity.getUsername() + " (" + playerEntity.getUuid() + ")");
+
+ try {
+ byte[] skinBytes = com.github.steveice10.mc.auth.util.Base64.decode(clientData.getSkinData().getBytes("UTF-8"));
+ byte[] capeBytes = clientData.getCapeData();
+
+ byte[] geometryNameBytes = Base64.getDecoder().decode(clientData.getGeometryName().getBytes("UTF-8"));
+ byte[] geometryBytes = Base64.getDecoder().decode(clientData.getGeometryData().getBytes("UTF-8"));
+
+ if (skinBytes.length <= (128 * 128 * 4) && !clientData.isPersonaSkin()) {
+ SkinProvider.storeBedrockSkin(playerEntity.getUuid(), data.getSkinUrl(), skinBytes);
+ SkinProvider.storeBedrockGeometry(playerEntity.getUuid(), geometryNameBytes, geometryBytes);
+ } else {
+ GeyserConnector.getInstance().getLogger().info("Unable to load bedrock skin for '" + playerEntity.getUsername() + "' as they are likely using a customised skin");
+ GeyserConnector.getInstance().getLogger().debug("The size of '" + playerEntity.getUsername() + "' skin is: " + clientData.getSkinImageWidth() + "x" + clientData.getSkinImageHeight());
+ }
+
+ if (!clientData.getCapeId().equals("")) {
+ SkinProvider.storeBedrockCape(playerEntity.getUuid(), capeBytes);
+ }
+ } catch (Exception e) {
+ throw new AssertionError("Failed to cache skin for bedrock user (" + playerEntity.getUsername() + "): ", e);
+ }
}
}
diff --git a/connector/src/main/resources/bedrock/skin/geometry.humanoid.ears.json b/connector/src/main/resources/bedrock/skin/geometry.humanoid.ears.json
new file mode 100644
index 00000000..5571655b
--- /dev/null
+++ b/connector/src/main/resources/bedrock/skin/geometry.humanoid.ears.json
@@ -0,0 +1,249 @@
+{
+ "format_version": "1.14.0",
+ "minecraft:geometry": [
+ {
+ "bones": [
+ {
+ "name" : "root",
+ "pivot" : [ 0.0, 0.0, 0.0 ]
+ },
+
+ {
+ "name" : "waist",
+ "parent" : "root",
+ "pivot" : [ 0.0, 12.0, 0.0 ],
+ "rotation" : [ 0.0, 0.0, 0.0 ],
+ "cubes" : []
+ },
+
+
+ {
+ "name": "body",
+ "parent" : "waist",
+ "pivot": [ 0.0, 24.0, 0.0 ],
+ "rotation" : [ 0.0, 0.0, 0.0 ],
+ "cubes": [
+ {
+ "origin": [ -4.0, 12.0, -2.0 ],
+ "size": [ 8, 12, 4 ],
+ "uv": [ 16, 16 ]
+ }
+ ]
+ },
+
+ {
+ "name": "jacket",
+ "parent" : "body",
+ "pivot": [ 0.0, 24.0, 0.0 ],
+ "rotation" : [ 0.0, 0.0, 0.0 ],
+ "cubes": [
+ {
+ "origin": [ -4.0, 12.0, -2.0 ],
+ "size": [ 8, 12, 4 ],
+ "uv": [ 16, 32 ],
+ "inflate": 0.25
+ }
+ ]
+ },
+
+
+ {
+ "name": "head",
+ "parent" : "body",
+ "pivot": [ 0.0, 24.0, 0.0 ],
+ "rotation" : [ 0.0, 0.0, 0.0 ],
+ "cubes": [
+ {
+ "origin": [ -4.0, 24.0, -4.0 ],
+ "size": [ 8, 8, 8 ],
+ "uv": [ 0, 0 ]
+ }
+ ]
+ },
+
+ {
+ "name": "hat",
+ "parent" : "head",
+ "pivot": [ 0.0, 24.0, 0.0 ],
+ "rotation" : [ 0.0, 0.0, 0.0 ],
+ "cubes": [
+ {
+ "origin": [ -4.0, 24.0, -4.0 ],
+ "size": [ 8, 8, 8 ],
+ "uv": [ 32, 0 ],
+ "inflate": 0.5
+ }
+ ]
+ },
+
+
+ {
+ "name": "leftArm",
+ "parent" : "body",
+ "pivot": [ 5.0, 22.0, 0.0 ],
+ "rotation" : [ 0.0, 0.0, 0.0 ],
+ "cubes": [
+ {
+ "origin": [ 4.0, 12.0, -2.0 ],
+ "size": [ 4, 12, 4 ],
+ "uv": [ 32, 48 ]
+ }
+ ]
+ },
+ {
+ "name": "rightArm",
+ "parent" : "body",
+ "pivot": [ -5.0, 22.0, 0.0 ],
+ "rotation" : [ 0.0, 0.0, 0.0 ],
+ "cubes": [
+ {
+ "origin": [ -8.0, 12.0, -2.0 ],
+ "size": [ 4, 12, 4 ],
+ "uv": [ 40, 16 ]
+ }
+ ]
+ },
+
+ {
+ "name": "leftSleeve",
+ "parent" : "leftArm",
+ "pivot": [ 5.0, 22.0, 0.0 ],
+ "rotation" : [ 0.0, 0.0, 0.0 ],
+ "cubes": [
+ {
+ "origin": [ 4.0, 12.0, -2.0 ],
+ "size": [ 4, 12, 4 ],
+ "uv": [ 48, 48 ],
+ "inflate": 0.25
+ }
+ ]
+ },
+
+ {
+ "name": "rightSleeve",
+ "parent" : "rightArm",
+ "pivot": [ -5.0, 22.0, 0.0 ],
+ "rotation" : [ 0.0, 0.0, 0.0 ],
+ "cubes": [
+ {
+ "origin": [ -8.0, 12.0, -2.0 ],
+ "size": [ 4, 12, 4 ],
+ "uv": [ 40, 32 ],
+ "inflate": 0.25
+ }
+ ]
+ },
+
+
+ {
+ "name": "leftLeg",
+ "parent" : "root",
+ "pivot": [ 1.9, 12.0, 0.0 ],
+ "rotation" : [ 0.0, 0.0, 0.0 ],
+ "cubes": [
+ {
+ "origin": [ -0.1, 0.0, -2.0 ],
+ "size": [ 4, 12, 4 ],
+ "uv": [ 0, 16 ]
+ }
+ ]
+ },
+
+ {
+ "name": "rightLeg",
+ "parent" : "root",
+ "pivot": [ -1.9, 12.0, 0.0 ],
+ "rotation" : [ 0.0, 0.0, 0.0 ],
+ "cubes": [
+ {
+ "origin": [ -3.9, 0.0, -2.0 ],
+ "size": [ 4, 12, 4 ],
+ "uv": [ 0, 16 ]
+ }
+ ]
+ },
+
+ {
+ "name": "leftPants",
+ "parent" : "leftLeg",
+ "pivot": [1.9, 12.0, 0.0],
+ "rotation" : [ 0.0, 0.0, 0.0 ],
+ "cubes": [
+ {
+ "origin": [ -0.1, 0.0, -2.0 ],
+ "size": [ 4, 12, 4 ],
+ "uv": [ 0, 48 ],
+ "inflate": 0.25
+ }
+ ]
+ },
+
+ {
+ "name": "rightPants",
+ "parent" : "rightLeg",
+ "pivot": [ -1.9, 12.0, 0.0 ],
+ "rotation" : [ 0.0, 0.0, 0.0 ],
+ "cubes": [
+ {
+ "origin": [ -3.9, 0.0, -2.0] ,
+ "size": [ 4, 12, 4 ],
+ "uv": [ 0, 32],
+ "inflate": 0.25
+ }
+ ]
+ },
+
+
+ {
+ "name" : "rightItem",
+ "parent" : "rightArm",
+ "pivot" : [ -6.0, 15.0, 1.0 ],
+ "rotation" : [ 0.0, 0.0, 0.0 ],
+ "cubes" : []
+ },
+
+ {
+ "name" : "leftItem",
+ "parent" : "leftArm",
+ "pivot" : [ 6.0, 15.0, 1.0 ],
+ "rotation" : [ 0.0, 0.0, 0.0 ],
+ "cubes" : []
+ },
+
+
+ {
+ "name": "leftEar",
+ "parent" : "head",
+ "pivot": [ -1.9, 12.0, 0.0 ],
+ "cubes": [
+ {
+ "origin": [ 3.0, 31.0, -0.5 ],
+ "size": [ 6, 6, 1 ],
+ "uv": [ 24, 0 ],
+ "inflate": 0.5
+ }
+ ]
+ },
+
+ {
+ "name": "rightEar",
+ "parent" : "head",
+ "pivot": [ -1.9, 12.0, 0.0 ],
+ "cubes": [
+ {
+ "origin": [ -9.0, 31.0, -0.5 ],
+ "size": [ 6, 6, 1 ],
+ "uv": [ 24, 0 ],
+ "inflate": 0.5
+ }
+ ]
+ }
+ ],
+ "description": {
+ "identifier": "geometry.humanoid.ears",
+ "texture_height": 64,
+ "texture_width": 64
+ }
+ }
+ ]
+}
\ No newline at end of file
diff --git a/connector/src/main/resources/bedrock/skin/geometry.humanoid.earsSlim.json b/connector/src/main/resources/bedrock/skin/geometry.humanoid.earsSlim.json
new file mode 100644
index 00000000..70c44f85
--- /dev/null
+++ b/connector/src/main/resources/bedrock/skin/geometry.humanoid.earsSlim.json
@@ -0,0 +1,249 @@
+{
+ "format_version": "1.14.0",
+ "minecraft:geometry": [
+ {
+ "bones": [
+ {
+ "name" : "root",
+ "pivot" : [ 0.0, 0.0, 0.0 ]
+ },
+
+ {
+ "name" : "waist",
+ "parent" : "root",
+ "pivot" : [ 0.0, 12.0, 0.0 ],
+ "rotation" : [ 0.0, 0.0, 0.0 ],
+ "cubes" : []
+ },
+
+
+ {
+ "name": "body",
+ "parent" : "waist",
+ "pivot": [ 0.0, 24.0, 0.0 ],
+ "rotation" : [ 0.0, 0.0, 0.0 ],
+ "cubes": [
+ {
+ "origin": [ -4.0, 12.0, -2.0 ],
+ "size": [ 8, 12, 4 ],
+ "uv": [ 16, 16 ]
+ }
+ ]
+ },
+
+ {
+ "name": "jacket",
+ "parent" : "body",
+ "pivot": [ 0.0, 24.0, 0.0 ],
+ "rotation" : [ 0.0, 0.0, 0.0 ],
+ "cubes": [
+ {
+ "origin": [ -4.0, 12.0, -2.0 ],
+ "size": [ 8, 12, 4 ],
+ "uv": [ 16, 32 ],
+ "inflate": 0.25
+ }
+ ]
+ },
+
+
+ {
+ "name": "head",
+ "parent" : "body",
+ "pivot": [ 0.0, 24.0, 0.0 ],
+ "rotation" : [ 0.0, 0.0, 0.0 ],
+ "cubes": [
+ {
+ "origin": [ -4.0, 24.0, -4.0 ],
+ "size": [ 8, 8, 8 ],
+ "uv": [ 0, 0 ]
+ }
+ ]
+ },
+
+ {
+ "name": "hat",
+ "parent" : "head",
+ "pivot": [ 0.0, 24.0, 0.0 ],
+ "rotation" : [ 0.0, 0.0, 0.0 ],
+ "cubes": [
+ {
+ "origin": [ -4.0, 24.0, -4.0 ],
+ "size": [ 8, 8, 8 ],
+ "uv": [ 32, 0 ],
+ "inflate": 0.5
+ }
+ ]
+ },
+
+
+ {
+ "name": "leftArm",
+ "parent" : "body",
+ "pivot": [ 5.0, 21.5, 0.0 ],
+ "rotation" : [ 0.0, 0.0, 0.0 ],
+ "cubes": [
+ {
+ "origin": [ 4.0, 12, -2.0 ],
+ "size": [ 3, 12, 4 ],
+ "uv": [ 32, 48 ]
+ }
+ ]
+ },
+ {
+ "name": "rightArm",
+ "parent" : "body",
+ "pivot": [ -5.0, 21.5, 0.0 ],
+ "rotation" : [ 0.0, 0.0, 0.0 ],
+ "cubes": [
+ {
+ "origin": [ -7.0, 12, -2.0 ],
+ "size": [ 3, 12, 4 ],
+ "uv": [ 40, 16 ]
+ }
+ ]
+ },
+
+ {
+ "name": "leftSleeve",
+ "parent" : "leftArm",
+ "pivot": [ 5.0, 21.5, 0.0 ],
+ "rotation" : [ 0.0, 0.0, 0.0 ],
+ "cubes": [
+ {
+ "origin": [ 4.0, 11.5, -2.0 ],
+ "size": [ 3, 12, 4 ],
+ "uv": [ 48, 48 ],
+ "inflate": 0.25
+ }
+ ]
+ },
+
+ {
+ "name": "rightSleeve",
+ "parent" : "rightArm",
+ "pivot": [ -5.0, 21.5, 0.0 ],
+ "rotation" : [ 0.0, 0.0, 0.0 ],
+ "cubes": [
+ {
+ "origin": [ -7.0, 11.5, -2.0 ],
+ "size": [ 3, 12, 4 ],
+ "uv": [ 40, 32 ],
+ "inflate": 0.25
+ }
+ ]
+ },
+
+
+ {
+ "name": "leftLeg",
+ "parent" : "root",
+ "pivot": [ 1.9, 12.0, 0.0 ],
+ "rotation" : [ 0.0, 0.0, 0.0 ],
+ "cubes": [
+ {
+ "origin": [ -0.1, 0.0, -2.0 ],
+ "size": [ 4, 12, 4 ],
+ "uv": [ 0, 16 ]
+ }
+ ]
+ },
+
+ {
+ "name": "rightLeg",
+ "parent" : "root",
+ "pivot": [ -1.9, 12.0, 0.0 ],
+ "rotation" : [ 0.0, 0.0, 0.0 ],
+ "cubes": [
+ {
+ "origin": [ -3.9, 0.0, -2.0 ],
+ "size": [ 4, 12, 4 ],
+ "uv": [ 0, 16 ]
+ }
+ ]
+ },
+
+ {
+ "name": "leftPants",
+ "parent" : "leftLeg",
+ "pivot": [1.9, 12.0, 0.0],
+ "rotation" : [ 0.0, 0.0, 0.0 ],
+ "cubes": [
+ {
+ "origin": [ -0.1, 0.0, -2.0 ],
+ "size": [ 4, 12, 4 ],
+ "uv": [ 0, 48 ],
+ "inflate": 0.25
+ }
+ ]
+ },
+
+ {
+ "name": "rightPants",
+ "parent" : "rightLeg",
+ "pivot": [ -1.9, 12.0, 0.0 ],
+ "rotation" : [ 0.0, 0.0, 0.0 ],
+ "cubes": [
+ {
+ "origin": [ -3.9, 0.0, -2.0] ,
+ "size": [ 4, 12, 4 ],
+ "uv": [ 0, 32],
+ "inflate": 0.25
+ }
+ ]
+ },
+
+
+ {
+ "name" : "rightItem",
+ "parent" : "rightArm",
+ "pivot" : [ -6.0, 15.0, 1.0 ],
+ "rotation" : [ 0.0, 0.0, 0.0 ],
+ "cubes" : []
+ },
+
+ {
+ "name" : "leftItem",
+ "parent" : "leftArm",
+ "pivot" : [ 6.0, 15.0, 1.0 ],
+ "rotation" : [ 0.0, 0.0, 0.0 ],
+ "cubes" : []
+ },
+
+
+ {
+ "name": "leftEar",
+ "parent" : "head",
+ "pivot": [ -1.9, 12.0, 0.0 ],
+ "cubes": [
+ {
+ "origin": [ 3.0, 31.0, -0.5 ],
+ "size": [ 6, 6, 1 ],
+ "uv": [ 24, 0 ],
+ "inflate": 0.5
+ }
+ ]
+ },
+
+ {
+ "name": "rightEar",
+ "parent" : "head",
+ "pivot": [ -1.9, 12.0, 0.0 ],
+ "cubes": [
+ {
+ "origin": [ -9.0, 31.0, -0.5 ],
+ "size": [ 6, 6, 1 ],
+ "uv": [ 24, 0 ],
+ "inflate": 0.5
+ }
+ ]
+ }
+ ],
+ "description": {
+ "identifier": "geometry.humanoid.earsSlim",
+ "texture_height": 64,
+ "texture_width": 64
+ }
+ }
+ ]
+}
\ No newline at end of file
diff --git a/connector/src/main/resources/bedrock/skin/skin_alex.png b/connector/src/main/resources/bedrock/skin/skin_alex.png
new file mode 100644
index 00000000..ffd8e071
Binary files /dev/null and b/connector/src/main/resources/bedrock/skin/skin_alex.png differ
diff --git a/connector/src/main/resources/config.yml b/connector/src/main/resources/config.yml
index b1e1e2f9..931e0a8d 100644
--- a/connector/src/main/resources/config.yml
+++ b/connector/src/main/resources/config.yml
@@ -12,7 +12,7 @@ bedrock:
address: 0.0.0.0
# The port that will listen for connections
port: 19132
- # The MOTD that will be broadcasted to Minecraft: Bedrock Edition clients
+ # The MOTD that will be broadcasted to Minecraft: Bedrock Edition clients. Irrelevant if "passthrough-motd" is set to true
motd1: "GeyserMC"
motd2: "Another GeyserMC forced host."
remote:
@@ -41,8 +41,21 @@ floodgate-key-file: public-key.pem
# email: herpderp@derpherp.com
# password: dooooo
-# Relay the MOTD, player count and max players from the remote server
-ping-passthrough: false
+# Bedrock clients can freeze when opening up the command prompt for the first time if given a lot of commands.
+# Disabling this will prevent command suggestions from being sent and solve freezing for Bedrock clients.
+command-suggestions: true
+
+# The following two options enable "ping passthrough" - the MOTD and/or player count gets retrieved from the Java server.
+# Relay the MOTD from the remote server to Bedrock players.
+passthrough-motd: false
+# Relay the player count and max players from the remote server to Bedrock players.
+passthrough-player-counts: false
+# Enable LEGACY ping passthrough. There is no need to enable this unless your MOTD or player count does not appear properly.
+# This option does nothing on standalone.
+legacy-ping-passthrough: false
+# How often to ping the remote server, in seconds. Only relevant for standalone or legacy ping passthrough.
+# Increase if you are getting BrokenPipe errors.
+ping-passthrough-interval: 3
# Maximum amount of players that can connect
max-players: 100
@@ -57,6 +70,10 @@ general-thread-pool: 32
# OptiFine capes, LabyMod capes, 5Zig capes and MinecraftCapes
allow-third-party-capes: true
+# Allow third party deadmau5 ears to be visible. Currently allowing:
+# MinecraftCapes
+allow-third-party-ears: false
+
# The default locale if we dont have the one the client requested
default-locale: en_us
@@ -69,6 +86,12 @@ default-locale: en_us
# Geyser has direct access to the server itself.
cache-chunks: false
+# Bedrock prevents building and displaying blocks above Y127 in the Nether -
+# enabling this config option works around that by changing the Nether dimension ID
+# to the End ID. The main downside to this is that the sky will resemble that of
+# the end sky in the nether, but ultimately it's the only way for this feature to work.
+above-bedrock-nether-building: false
+
# bStats is a stat tracker that is entirely anonymous and tracks only basic information
# about Geyser, such as how many people are online, how many servers are using Geyser,
# what OS is being used, etc. You can learn more about bStats here: https://bstats.org/.
@@ -78,3 +101,6 @@ metrics:
enabled: true
# UUID of server, don't change!
uuid: generateduuid
+
+# DO NOT TOUCH!
+config-version: 3
diff --git a/connector/src/main/resources/mappings b/connector/src/main/resources/mappings
index 5b3a9ad1..a67cc940 160000
--- a/connector/src/main/resources/mappings
+++ b/connector/src/main/resources/mappings
@@ -1 +1 @@
-Subproject commit 5b3a9ad1d2ef76105fb318e63126a096844b3195
+Subproject commit a67cc940c0d47874c833ffeb58f38e33eabfcc33
diff --git a/pom.xml b/pom.xml
index fc1ead33..3e119eb1 100644
--- a/pom.xml
+++ b/pom.xml
@@ -38,14 +38,8 @@
- CodeMC-repo
- https://repo.codemc.org/repository/maven-public
-
- true
-
-
- true
-
+ jitpack.io
+ https://jitpack.io
nukkitx-release-repo
@@ -67,6 +61,10 @@
true
+
+ viaversion-repo
+ https://repo.viaversion.com
+