From 10383d31ab9f2bdc3f3d58bf4984fa3a52c205db Mon Sep 17 00:00:00 2001 From: Camotoy <20743703+Camotoy@users.noreply.github.com> Date: Tue, 22 Mar 2022 23:01:00 -0400 Subject: [PATCH 01/17] Replace show coordinates string Use the one built into Bedrock for less maintenance. --- .../main/java/org/geysermc/geyser/util/SettingsUtils.java | 6 +++++- core/src/main/resources/languages | 2 +- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/core/src/main/java/org/geysermc/geyser/util/SettingsUtils.java b/core/src/main/java/org/geysermc/geyser/util/SettingsUtils.java index ea3412451..5e2fe9944 100644 --- a/core/src/main/java/org/geysermc/geyser/util/SettingsUtils.java +++ b/core/src/main/java/org/geysermc/geyser/util/SettingsUtils.java @@ -62,7 +62,7 @@ public class SettingsUtils { // Client can only see its coordinates if reducedDebugInfo is disabled and coordinates are enabled in geyser config. if (session.getPreferencesCache().isAllowShowCoordinates()) { - builder.toggle("geyser.settings.option.coordinates", session.getPreferencesCache().isPrefersShowCoordinates()); + builder.toggle("%createWorldScreen.showCoordinates", session.getPreferencesCache().isPrefersShowCoordinates()); } if (CooldownUtils.getDefaultShowCooldown() != CooldownUtils.CooldownType.DISABLED) { @@ -175,6 +175,10 @@ public class SettingsUtils { } private static String translateEntry(String key, String locale) { + if (key.startsWith("%")) { + // Bedrock will translate + return key; + } if (key.startsWith("geyser.")) { return GeyserLocale.getPlayerLocaleString(key, locale); } diff --git a/core/src/main/resources/languages b/core/src/main/resources/languages index 51e0ae2b5..f073cf2b9 160000 --- a/core/src/main/resources/languages +++ b/core/src/main/resources/languages @@ -1 +1 @@ -Subproject commit 51e0ae2b527e3548ef82b65f33541f5eaeba2308 +Subproject commit f073cf2b9e62d1a9da45ac23448d59ca71074339 From 780218d39d7074ef893ace6f30f2c66b4e95ab0d Mon Sep 17 00:00:00 2001 From: Camotoy <20743703+Camotoy@users.noreply.github.com> Date: Tue, 22 Mar 2022 23:03:37 -0400 Subject: [PATCH 02/17] Consolidate NoteblockBlockEntityTranslator behavior It was only used in one place that could better use existing code. --- .../NoteblockBlockEntityTranslator.java | 48 ------------------- .../java/level/JavaBlockEventTranslator.java | 27 +++++------ 2 files changed, 13 insertions(+), 62 deletions(-) delete mode 100644 core/src/main/java/org/geysermc/geyser/translator/level/block/entity/NoteblockBlockEntityTranslator.java diff --git a/core/src/main/java/org/geysermc/geyser/translator/level/block/entity/NoteblockBlockEntityTranslator.java b/core/src/main/java/org/geysermc/geyser/translator/level/block/entity/NoteblockBlockEntityTranslator.java deleted file mode 100644 index a345d8fdb..000000000 --- a/core/src/main/java/org/geysermc/geyser/translator/level/block/entity/NoteblockBlockEntityTranslator.java +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright (c) 2019-2022 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.geyser.translator.level.block.entity; - -import com.github.steveice10.mc.protocol.data.game.entity.metadata.Position; -import com.nukkitx.math.vector.Vector3i; -import com.nukkitx.protocol.bedrock.packet.BlockEventPacket; -import org.geysermc.geyser.session.GeyserSession; -import org.geysermc.geyser.level.block.BlockStateValues; - -/** - * Does not implement BlockEntityTranslator because it's only a block entity in Bedrock - */ -public class NoteblockBlockEntityTranslator { - - public static void translate(GeyserSession session, Position position) { - int blockState = session.getGeyser().getWorldManager().getBlockAt(session, position); - BlockEventPacket blockEventPacket = new BlockEventPacket(); - blockEventPacket.setBlockPosition(Vector3i.from(position.getX(), position.getY(), position.getZ())); - blockEventPacket.setEventType(0); - blockEventPacket.setEventData(BlockStateValues.getNoteblockPitch(blockState)); - session.sendUpstreamPacket(blockEventPacket); - } - -} diff --git a/core/src/main/java/org/geysermc/geyser/translator/protocol/java/level/JavaBlockEventTranslator.java b/core/src/main/java/org/geysermc/geyser/translator/protocol/java/level/JavaBlockEventTranslator.java index c7553020b..6adf1e00f 100644 --- a/core/src/main/java/org/geysermc/geyser/translator/protocol/java/level/JavaBlockEventTranslator.java +++ b/core/src/main/java/org/geysermc/geyser/translator/protocol/java/level/JavaBlockEventTranslator.java @@ -35,14 +35,13 @@ import com.nukkitx.protocol.bedrock.packet.BlockEntityDataPacket; import com.nukkitx.protocol.bedrock.packet.BlockEventPacket; import it.unimi.dsi.fastutil.objects.Object2IntMaps; import org.geysermc.common.PlatformType; +import org.geysermc.geyser.level.block.BlockStateValues; +import org.geysermc.geyser.level.physics.Direction; import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.session.cache.PistonCache; +import org.geysermc.geyser.translator.level.block.entity.PistonBlockEntity; import org.geysermc.geyser.translator.protocol.PacketTranslator; import org.geysermc.geyser.translator.protocol.Translator; -import org.geysermc.geyser.level.block.BlockStateValues; -import org.geysermc.geyser.translator.level.block.entity.NoteblockBlockEntityTranslator; -import org.geysermc.geyser.translator.level.block.entity.PistonBlockEntity; -import org.geysermc.geyser.level.physics.Direction; @Translator(packet = ClientboundBlockEventPacket.class) public class JavaBlockEventTranslator extends PacketTranslator { @@ -50,8 +49,9 @@ public class JavaBlockEventTranslator extends PacketTranslator 0 ? 1 : 0); @@ -60,11 +60,12 @@ public class JavaBlockEventTranslator extends PacketTranslator new PistonBlockEntity(session, pos, direction, true, true)); + PistonBlockEntity blockEntity = pistonCache.getPistons().computeIfAbsent(vector, pos -> new PistonBlockEntity(session, pos, direction, true, true)); if (blockEntity.getAction() != action) { blockEntity.setAction(action, Object2IntMaps.emptyMap()); } } } else { - PistonBlockEntity blockEntity = pistonCache.getPistons().computeIfAbsent(position, pos -> { + PistonBlockEntity blockEntity = pistonCache.getPistons().computeIfAbsent(vector, pos -> { int blockId = session.getGeyser().getWorldManager().getBlockAt(session, position); boolean sticky = BlockStateValues.isStickyPiston(blockId); boolean extended = action != PistonValueType.PUSHING; @@ -106,10 +107,8 @@ public class JavaBlockEventTranslator extends PacketTranslator Date: Wed, 23 Mar 2022 13:57:25 -0400 Subject: [PATCH 03/17] Change banner item translator into NBT-specific translator Since we don't need to change any other item properties, this removes a builder hack that had to be implemented. --- .../inventory/LoomInventoryTranslator.java | 6 +- .../inventory/item/ItemTranslator.java | 7 +- .../item/NbtItemStackTranslator.java | 4 +- .../item/{ => nbt}/BannerTranslator.java | 100 +++++++----------- .../entity/BannerBlockEntityTranslator.java | 11 +- 5 files changed, 53 insertions(+), 75 deletions(-) rename core/src/main/java/org/geysermc/geyser/translator/inventory/item/{ => nbt}/BannerTranslator.java (70%) diff --git a/core/src/main/java/org/geysermc/geyser/translator/inventory/LoomInventoryTranslator.java b/core/src/main/java/org/geysermc/geyser/translator/inventory/LoomInventoryTranslator.java index a862a7e0d..a7b736d72 100644 --- a/core/src/main/java/org/geysermc/geyser/translator/inventory/LoomInventoryTranslator.java +++ b/core/src/main/java/org/geysermc/geyser/translator/inventory/LoomInventoryTranslator.java @@ -41,13 +41,13 @@ import com.nukkitx.protocol.bedrock.data.inventory.stackrequestactions.StackRequ import com.nukkitx.protocol.bedrock.packet.ItemStackResponsePacket; import it.unimi.dsi.fastutil.objects.Object2IntMap; import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap; +import org.geysermc.geyser.inventory.BedrockContainerSlot; import org.geysermc.geyser.inventory.GeyserItemStack; import org.geysermc.geyser.inventory.Inventory; -import org.geysermc.geyser.session.GeyserSession; -import org.geysermc.geyser.inventory.BedrockContainerSlot; import org.geysermc.geyser.inventory.SlotType; import org.geysermc.geyser.inventory.updater.UIInventoryUpdater; -import org.geysermc.geyser.translator.inventory.item.BannerTranslator; +import org.geysermc.geyser.session.GeyserSession; +import org.geysermc.geyser.translator.inventory.item.nbt.BannerTranslator; import java.util.Collections; import java.util.List; diff --git a/core/src/main/java/org/geysermc/geyser/translator/inventory/item/ItemTranslator.java b/core/src/main/java/org/geysermc/geyser/translator/inventory/item/ItemTranslator.java index b8a7b60e2..f8acc0973 100644 --- a/core/src/main/java/org/geysermc/geyser/translator/inventory/item/ItemTranslator.java +++ b/core/src/main/java/org/geysermc/geyser/translator/inventory/item/ItemTranslator.java @@ -47,6 +47,7 @@ import org.geysermc.geyser.translator.text.MessageTranslator; import org.geysermc.geyser.util.FileUtils; import javax.annotation.Nonnull; +import java.lang.reflect.InvocationTargetException; import java.util.*; import java.util.stream.Collectors; @@ -71,11 +72,11 @@ public abstract class ItemTranslator { try { if (NbtItemStackTranslator.class.isAssignableFrom(clazz)) { - NbtItemStackTranslator nbtItemTranslator = (NbtItemStackTranslator) clazz.newInstance(); + NbtItemStackTranslator nbtItemTranslator = (NbtItemStackTranslator) clazz.getDeclaredConstructor().newInstance(); loadedNbtItemTranslators.put(nbtItemTranslator, priority); continue; } - ItemTranslator itemStackTranslator = (ItemTranslator) clazz.newInstance(); + ItemTranslator itemStackTranslator = (ItemTranslator) clazz.getDeclaredConstructor().newInstance(); List appliedItems = itemStackTranslator.getAppliedItems(); for (ItemMapping item : appliedItems) { ItemTranslator registered = ITEM_STACK_TRANSLATORS.get(item.getJavaId()); @@ -87,7 +88,7 @@ public abstract class ItemTranslator { } ITEM_STACK_TRANSLATORS.put(item.getJavaId(), itemStackTranslator); } - } catch (InstantiationException | IllegalAccessException e) { + } catch (InstantiationException | InvocationTargetException | IllegalAccessException | NoSuchMethodException e) { GeyserImpl.getInstance().getLogger().error("Could not instantiate annotated item translator " + clazz.getCanonicalName()); } } diff --git a/core/src/main/java/org/geysermc/geyser/translator/inventory/item/NbtItemStackTranslator.java b/core/src/main/java/org/geysermc/geyser/translator/inventory/item/NbtItemStackTranslator.java index 31b0aa70e..bfa7ebc2e 100644 --- a/core/src/main/java/org/geysermc/geyser/translator/inventory/item/NbtItemStackTranslator.java +++ b/core/src/main/java/org/geysermc/geyser/translator/inventory/item/NbtItemStackTranslator.java @@ -29,12 +29,12 @@ import com.github.steveice10.opennbt.tag.builtin.CompoundTag; import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.registry.type.ItemMapping; -public class NbtItemStackTranslator { +public abstract class NbtItemStackTranslator { /** * Translate the item NBT to Bedrock * @param session the client's current session - * @param itemTag the item's CompoundTag + * @param itemTag the item's CompoundTag (cloned from Geyser's cached copy) * @param mapping Geyser's item mapping */ public void translateToBedrock(GeyserSession session, CompoundTag itemTag, ItemMapping mapping) { diff --git a/core/src/main/java/org/geysermc/geyser/translator/inventory/item/BannerTranslator.java b/core/src/main/java/org/geysermc/geyser/translator/inventory/item/nbt/BannerTranslator.java similarity index 70% rename from core/src/main/java/org/geysermc/geyser/translator/inventory/item/BannerTranslator.java rename to core/src/main/java/org/geysermc/geyser/translator/inventory/item/nbt/BannerTranslator.java index 15f7c57ce..3da157cfc 100644 --- a/core/src/main/java/org/geysermc/geyser/translator/inventory/item/BannerTranslator.java +++ b/core/src/main/java/org/geysermc/geyser/translator/inventory/item/nbt/BannerTranslator.java @@ -23,19 +23,18 @@ * @link https://github.com/GeyserMC/Geyser */ -package org.geysermc.geyser.translator.inventory.item; +package org.geysermc.geyser.translator.inventory.item.nbt; -import com.github.steveice10.mc.protocol.data.game.entity.metadata.ItemStack; import com.github.steveice10.opennbt.tag.builtin.*; import com.nukkitx.nbt.NbtList; import com.nukkitx.nbt.NbtMap; -import com.nukkitx.nbt.NbtMapBuilder; import com.nukkitx.nbt.NbtType; -import com.nukkitx.protocol.bedrock.data.inventory.ItemData; import org.geysermc.geyser.network.MinecraftProtocol; import org.geysermc.geyser.registry.Registries; import org.geysermc.geyser.registry.type.ItemMapping; -import org.geysermc.geyser.registry.type.ItemMappings; +import org.geysermc.geyser.session.GeyserSession; +import org.geysermc.geyser.translator.inventory.item.ItemRemapper; +import org.geysermc.geyser.translator.inventory.item.NbtItemStackTranslator; import javax.annotation.Nonnull; import java.util.ArrayList; @@ -45,7 +44,7 @@ import java.util.Map; import java.util.stream.Collectors; @ItemRemapper -public class BannerTranslator extends ItemTranslator { +public class BannerTranslator extends NbtItemStackTranslator { /** * Holds what a Java ominous banner pattern looks like. * @@ -117,21 +116,6 @@ public class BannerTranslator extends ItemTranslator { .build(); } - /** - * 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(List patterns) { - List tagsList = new ArrayList<>(); - for (NbtMap patternTag : patterns) { - tagsList.add(getJavaBannerPattern(patternTag)); - } - - return new ListTag("Patterns", tagsList); - } - /** * Convert the Bedrock edition banner pattern nbt to Java edition * @@ -146,62 +130,54 @@ public class BannerTranslator extends ItemTranslator { return new CompoundTag("", tags); } - @Override - protected ItemData.Builder translateToBedrock(ItemStack itemStack, ItemMapping mapping, ItemMappings mappings) { - if (itemStack.getNbt() == null) { - return super.translateToBedrock(itemStack, mapping, mappings); + /** + * Convert a list of patterns from Java nbt to Bedrock nbt, or vice versa (we just need to invert the color) + * + * @param patterns The patterns to convert + */ + private void invertBannerColors(ListTag patterns) { + for (Tag patternTag : patterns.getValue()) { + IntTag color = ((CompoundTag) patternTag).get("Color"); + color.setValue(15 - color.getValue()); } - - ItemData.Builder builder = super.translateToBedrock(itemStack, mapping, mappings); - - CompoundTag blockEntityTag = itemStack.getNbt().get("BlockEntityTag"); - if (blockEntityTag != null && blockEntityTag.get("Patterns") instanceof ListTag patterns) { - NbtMapBuilder nbtBuilder = builder.build().getTag().toBuilder(); //TODO fix ugly hack - if (patterns.equals(OMINOUS_BANNER_PATTERN)) { - // Remove the current patterns and set the ominous banner type - nbtBuilder.remove("Patterns"); - nbtBuilder.putInt("Type", 1); - } else { - nbtBuilder.put("Patterns", convertBannerPattern(patterns)); - } - - builder.tag(nbtBuilder.build()); - } - - return builder; } @Override - public ItemStack translateToJava(ItemData itemData, ItemMapping mapping, ItemMappings mappings) { - if (itemData.getTag() == null) { - return super.translateToJava(itemData, mapping, mappings); + public void translateToBedrock(GeyserSession session, CompoundTag itemTag, ItemMapping mapping) { + CompoundTag blockEntityTag = itemTag.get("BlockEntityTag"); + if (blockEntityTag != null && blockEntityTag.get("Patterns") instanceof ListTag patterns) { + if (patterns.equals(OMINOUS_BANNER_PATTERN)) { + // Remove the current patterns and set the ominous banner type + itemTag.put(new IntTag("Type", 1)); + } else { + invertBannerColors(patterns); + itemTag.put(patterns); + } + itemTag.remove("BlockEntityTag"); } + } - ItemStack itemStack = super.translateToJava(itemData, mapping, mappings); - - NbtMap nbtTag = itemData.getTag(); - if (nbtTag.containsKey("Type", NbtType.INT) && nbtTag.getInt("Type") == 1) { + @Override + public void translateToJava(CompoundTag itemTag, ItemMapping mapping) { + if (itemTag.get("Type") instanceof IntTag type && type.getValue() == 1) { // Ominous banner pattern - itemStack.getNbt().remove("Type"); + itemTag.remove("Type"); CompoundTag blockEntityTag = new CompoundTag("BlockEntityTag"); blockEntityTag.put(OMINOUS_BANNER_PATTERN); - itemStack.getNbt().put(blockEntityTag); - } else if (nbtTag.containsKey("Patterns", NbtType.LIST)) { - List patterns = nbtTag.getList("Patterns", NbtType.COMPOUND); - + itemTag.put(blockEntityTag); + } else if (itemTag.get("Patterns") instanceof ListTag patterns) { CompoundTag blockEntityTag = new CompoundTag("BlockEntityTag"); - blockEntityTag.put(convertBannerPattern(patterns)); + invertBannerColors(patterns); + blockEntityTag.put(patterns); - itemStack.getNbt().put(blockEntityTag); - itemStack.getNbt().remove("Patterns"); // Remove the old Bedrock patterns list + itemTag.put(blockEntityTag); + itemTag.remove("Patterns"); // Remove the old Bedrock patterns list } - - return itemStack; } @Override - public List getAppliedItems() { - return appliedItems; + public boolean acceptItem(ItemMapping mapping) { + return appliedItems.contains(mapping); } } diff --git a/core/src/main/java/org/geysermc/geyser/translator/level/block/entity/BannerBlockEntityTranslator.java b/core/src/main/java/org/geysermc/geyser/translator/level/block/entity/BannerBlockEntityTranslator.java index cca103cb3..725a17e7a 100644 --- a/core/src/main/java/org/geysermc/geyser/translator/level/block/entity/BannerBlockEntityTranslator.java +++ b/core/src/main/java/org/geysermc/geyser/translator/level/block/entity/BannerBlockEntityTranslator.java @@ -28,9 +28,10 @@ package org.geysermc.geyser.translator.level.block.entity; import com.github.steveice10.mc.protocol.data.game.level.block.BlockEntityType; 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 com.nukkitx.nbt.NbtMapBuilder; -import org.geysermc.geyser.translator.inventory.item.BannerTranslator; import org.geysermc.geyser.level.block.BlockStateValues; +import org.geysermc.geyser.translator.inventory.item.nbt.BannerTranslator; @BlockEntity(type = BlockEntityType.BANNER) public class BannerBlockEntityTranslator extends BlockEntityTranslator implements RequiresBlockState { @@ -45,8 +46,7 @@ public class BannerBlockEntityTranslator extends BlockEntityTranslator implement return; } - if (tag.contains("Patterns")) { - ListTag patterns = tag.get("Patterns"); + if (tag.get("Patterns") instanceof ListTag patterns) { if (patterns.equals(BannerTranslator.OMINOUS_BANNER_PATTERN)) { // This is an ominous banner; don't try to translate the raw patterns (it doesn't translate correctly) // and tell the Bedrock client that this is an ominous banner @@ -56,8 +56,9 @@ public class BannerBlockEntityTranslator extends BlockEntityTranslator implement } } - if (tag.contains("CustomName")) { - builder.put("CustomName", tag.get("CustomName").getValue()); + Tag customName = tag.get("CustomName"); + if (customName != null) { + builder.put("CustomName", customName.getValue()); } } } From b7de1b668f394e4f84464bfa691d1bc53ad363cb Mon Sep 17 00:00:00 2001 From: Camotoy <20743703+Camotoy@users.noreply.github.com> Date: Wed, 23 Mar 2022 13:57:58 -0400 Subject: [PATCH 04/17] Remove unused NibbleArray class --- .../geyser/level/chunk/NibbleArray.java | 92 ------------------- 1 file changed, 92 deletions(-) delete mode 100644 core/src/main/java/org/geysermc/geyser/level/chunk/NibbleArray.java diff --git a/core/src/main/java/org/geysermc/geyser/level/chunk/NibbleArray.java b/core/src/main/java/org/geysermc/geyser/level/chunk/NibbleArray.java deleted file mode 100644 index 6ee53f992..000000000 --- a/core/src/main/java/org/geysermc/geyser/level/chunk/NibbleArray.java +++ /dev/null @@ -1,92 +0,0 @@ -/* - * Copyright (c) 2019-2022 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.geyser.level.chunk; - -import com.nukkitx.network.util.Preconditions; - -public class NibbleArray implements Cloneable { - - private final byte[] data; - - public NibbleArray(int length) { - data = new byte[length / 2]; - } - - public NibbleArray(byte[] array) { - data = array; - } - - public byte get(int index) { - Preconditions.checkElementIndex(index, data.length * 2); - byte val = data[index / 2]; - if ((index & 1) == 0) { - return (byte) (val & 0x0f); - } else { - return (byte) ((val & 0xf0) >>> 4); - } - } - - public void set(int index, byte value) { - Preconditions.checkArgument(value >= 0 && value < 16, "Nibbles must have a value between 0 and 15."); - Preconditions.checkElementIndex(index, data.length * 2); - value &= 0xf; - int half = index / 2; - byte previous = data[half]; - if ((index & 1) == 0) { - data[half] = (byte) (previous & 0xf0 | value); - } else { - data[half] = (byte) (previous & 0x0f | value << 4); - } - } - - public void fill(byte value) { - Preconditions.checkArgument(value >= 0 && value < 16, "Nibbles must have a value between 0 and 15."); - value &= 0xf; - for (int i = 0; i < data.length; i++) { - data[i] = (byte) ((value << 4) | value); - } - } - - public void copyFrom(byte[] bytes) { - Preconditions.checkNotNull(bytes, "bytes"); - Preconditions.checkArgument(bytes.length == data.length, "length of provided byte array is %s but expected %s", bytes.length, - data.length); - System.arraycopy(bytes, 0, data, 0, data.length); - } - - public void copyFrom(NibbleArray array) { - Preconditions.checkNotNull(array, "array"); - copyFrom(array.data); - } - - public byte[] getData() { - return data; - } - - public NibbleArray copy() { - return new NibbleArray(getData().clone()); - } -} From 877301a500fae74dc2c9bcc1857b2a0718f65a3e Mon Sep 17 00:00:00 2001 From: Camotoy <20743703+Camotoy@users.noreply.github.com> Date: Wed, 23 Mar 2022 16:21:04 -0400 Subject: [PATCH 05/17] Remove locator map from creative menu; show some map colors Java allows any map color but Bedrock only allows a few, so we take what we can get. Fixes #2617 --- .../populator/ItemRegistryPopulator.java | 3 + .../inventory/item/CompassTranslator.java | 18 ++--- .../inventory/item/FilledMapTranslator.java | 68 +++++++++++++++++++ .../inventory/item/ItemTranslator.java | 7 +- .../inventory/item/PotionTranslator.java | 18 ++--- .../inventory/item/TippedArrowTranslator.java | 21 ++---- 6 files changed, 95 insertions(+), 40 deletions(-) create mode 100644 core/src/main/java/org/geysermc/geyser/translator/inventory/item/FilledMapTranslator.java diff --git a/core/src/main/java/org/geysermc/geyser/registry/populator/ItemRegistryPopulator.java b/core/src/main/java/org/geysermc/geyser/registry/populator/ItemRegistryPopulator.java index 9614e9da8..534c68776 100644 --- a/core/src/main/java/org/geysermc/geyser/registry/populator/ItemRegistryPopulator.java +++ b/core/src/main/java/org/geysermc/geyser/registry/populator/ItemRegistryPopulator.java @@ -166,6 +166,9 @@ public class ItemRegistryPopulator { if (identifier.equals("minecraft:debug_stick")) { // Just shows an empty texture; either way it doesn't exist in the creative menu on Java continue; + } else if (identifier.equals("minecraft:empty_map") && damage == 2) { + // Bedrock-only as its own item + continue; } StartGamePacket.ItemEntry entry = entries.get(identifier); int id = -1; diff --git a/core/src/main/java/org/geysermc/geyser/translator/inventory/item/CompassTranslator.java b/core/src/main/java/org/geysermc/geyser/translator/inventory/item/CompassTranslator.java index 9637f1aa9..b8ef85f81 100644 --- a/core/src/main/java/org/geysermc/geyser/translator/inventory/item/CompassTranslator.java +++ b/core/src/main/java/org/geysermc/geyser/translator/inventory/item/CompassTranslator.java @@ -41,17 +41,6 @@ import java.util.stream.Collectors; @ItemRemapper public class CompassTranslator extends ItemTranslator { - private final List appliedItems; - - public CompassTranslator() { - appliedItems = Registries.ITEMS.forVersion(MinecraftProtocol.DEFAULT_BEDROCK_CODEC.getProtocolVersion()) - .getItems() - .values() - .stream() - .filter(entry -> entry.getJavaIdentifier().endsWith("compass")) - .collect(Collectors.toList()); - } - @Override protected ItemData.Builder translateToBedrock(ItemStack itemStack, ItemMapping mapping, ItemMappings mappings) { if (isLodestoneCompass(itemStack.getNbt())) { @@ -89,6 +78,11 @@ public class CompassTranslator extends ItemTranslator { @Override public List getAppliedItems() { - return appliedItems; + return Registries.ITEMS.forVersion(MinecraftProtocol.DEFAULT_BEDROCK_CODEC.getProtocolVersion()) + .getItems() + .values() + .stream() + .filter(entry -> entry.getJavaIdentifier().endsWith("compass")) + .collect(Collectors.toList()); } } diff --git a/core/src/main/java/org/geysermc/geyser/translator/inventory/item/FilledMapTranslator.java b/core/src/main/java/org/geysermc/geyser/translator/inventory/item/FilledMapTranslator.java new file mode 100644 index 000000000..3dfa2d82f --- /dev/null +++ b/core/src/main/java/org/geysermc/geyser/translator/inventory/item/FilledMapTranslator.java @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2019-2022 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.geyser.translator.inventory.item; + +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.Tag; +import com.nukkitx.protocol.bedrock.data.inventory.ItemData; +import org.geysermc.geyser.network.MinecraftProtocol; +import org.geysermc.geyser.registry.Registries; +import org.geysermc.geyser.registry.type.ItemMapping; +import org.geysermc.geyser.registry.type.ItemMappings; + +import java.util.Collections; +import java.util.List; + +@ItemRemapper +public class FilledMapTranslator extends ItemTranslator { + + @Override + protected ItemData.Builder translateToBedrock(ItemStack itemStack, ItemMapping mapping, ItemMappings mappings) { + ItemData.Builder builder = super.translateToBedrock(itemStack, mapping, mappings); + CompoundTag nbt = itemStack.getNbt(); + if (nbt != null && nbt.get("display") instanceof CompoundTag display) { + // Note: damage 5 treasure map, 6 ??? + Tag mapColor = display.get("MapColor"); + if (mapColor != null && mapColor.getValue() instanceof Number color) { + // Java Edition allows any color; Bedrock only allows some. So let's take what colors we can get + switch (color.intValue()) { + case 3830373 -> builder.damage(3); // Ocean Monument + case 5393476 -> builder.damage(4); // Woodland explorer + } + } + } + return builder; + } + + @Override + public List getAppliedItems() { + return Collections.singletonList( + Registries.ITEMS.forVersion(MinecraftProtocol.DEFAULT_BEDROCK_CODEC.getProtocolVersion()) + .getMapping("minecraft:filled_map") + ); + } +} diff --git a/core/src/main/java/org/geysermc/geyser/translator/inventory/item/ItemTranslator.java b/core/src/main/java/org/geysermc/geyser/translator/inventory/item/ItemTranslator.java index f8acc0973..539d20207 100644 --- a/core/src/main/java/org/geysermc/geyser/translator/inventory/item/ItemTranslator.java +++ b/core/src/main/java/org/geysermc/geyser/translator/inventory/item/ItemTranslator.java @@ -303,13 +303,16 @@ public abstract class ItemTranslator { return new ItemStack(mapping.getJavaId(), itemData.getCount(), this.translateToJavaNBT("", itemData.getTag())); } + /** + * Used for initialization only and only called once. + */ public abstract List getAppliedItems(); protected ItemMapping getItemMapping(int javaId, CompoundTag nbt, ItemMappings mappings) { return mappings.getMapping(javaId); } - public NbtMap translateNbtToBedrock(CompoundTag tag) { + protected NbtMap translateNbtToBedrock(CompoundTag tag) { NbtMapBuilder builder = NbtMap.builder(); if (tag.getValue() != null && !tag.getValue().isEmpty()) { for (String str : tag.getValue().keySet()) { @@ -388,7 +391,7 @@ public abstract class ItemTranslator { return null; } - public CompoundTag translateToJavaNBT(String name, NbtMap tag) { + private CompoundTag translateToJavaNBT(String name, NbtMap tag) { CompoundTag javaTag = new CompoundTag(name); Map javaValue = javaTag.getValue(); if (tag != null && !tag.isEmpty()) { diff --git a/core/src/main/java/org/geysermc/geyser/translator/inventory/item/PotionTranslator.java b/core/src/main/java/org/geysermc/geyser/translator/inventory/item/PotionTranslator.java index 54a6deadb..04183e095 100644 --- a/core/src/main/java/org/geysermc/geyser/translator/inventory/item/PotionTranslator.java +++ b/core/src/main/java/org/geysermc/geyser/translator/inventory/item/PotionTranslator.java @@ -42,17 +42,6 @@ import java.util.stream.Collectors; @ItemRemapper public class PotionTranslator extends ItemTranslator { - private final List appliedItems; - - public PotionTranslator() { - appliedItems = Registries.ITEMS.forVersion(MinecraftProtocol.DEFAULT_BEDROCK_CODEC.getProtocolVersion()) - .getItems() - .values() - .stream() - .filter(entry -> entry.getJavaIdentifier().endsWith("potion")) - .collect(Collectors.toList()); - } - @Override protected ItemData.Builder translateToBedrock(ItemStack itemStack, ItemMapping mapping, ItemMappings mappings) { if (itemStack.getNbt() == null) return super.translateToBedrock(itemStack, mapping, mappings); @@ -84,6 +73,11 @@ public class PotionTranslator extends ItemTranslator { @Override public List getAppliedItems() { - return appliedItems; + return Registries.ITEMS.forVersion(MinecraftProtocol.DEFAULT_BEDROCK_CODEC.getProtocolVersion()) + .getItems() + .values() + .stream() + .filter(entry -> entry.getJavaIdentifier().endsWith("potion")) + .collect(Collectors.toList()); } } diff --git a/core/src/main/java/org/geysermc/geyser/translator/inventory/item/TippedArrowTranslator.java b/core/src/main/java/org/geysermc/geyser/translator/inventory/item/TippedArrowTranslator.java index 35e8baa07..b3aecb668 100644 --- a/core/src/main/java/org/geysermc/geyser/translator/inventory/item/TippedArrowTranslator.java +++ b/core/src/main/java/org/geysermc/geyser/translator/inventory/item/TippedArrowTranslator.java @@ -41,23 +41,10 @@ import java.util.stream.Collectors; @ItemRemapper public class TippedArrowTranslator extends ItemTranslator { - - private final List appliedItems; - private static final int TIPPED_ARROW_JAVA_ID = Registries.ITEMS.forVersion(MinecraftProtocol.DEFAULT_BEDROCK_CODEC.getProtocolVersion()) .getMapping("minecraft:tipped_arrow") .getJavaId(); - public TippedArrowTranslator() { - appliedItems = Registries.ITEMS.forVersion(MinecraftProtocol.DEFAULT_BEDROCK_CODEC.getProtocolVersion()) - .getItems() - .values() - .stream() - .filter(entry -> entry.getJavaIdentifier().contains("arrow") - && !entry.getJavaIdentifier().contains("spectral")) - .collect(Collectors.toList()); - } - @Override protected ItemData.Builder translateToBedrock(ItemStack itemStack, ItemMapping mapping, ItemMappings mappings) { if (!mapping.getJavaIdentifier().equals("minecraft:tipped_arrow") || itemStack.getNbt() == null) { @@ -93,6 +80,12 @@ public class TippedArrowTranslator extends ItemTranslator { @Override public List getAppliedItems() { - return appliedItems; + return Registries.ITEMS.forVersion(MinecraftProtocol.DEFAULT_BEDROCK_CODEC.getProtocolVersion()) + .getItems() + .values() + .stream() + .filter(entry -> entry.getJavaIdentifier().contains("arrow") + && !entry.getJavaIdentifier().contains("spectral")) + .collect(Collectors.toList()); } } From 80b6d14ceefdfe5ad41116a040993b636ff98cb9 Mon Sep 17 00:00:00 2001 From: Camotoy <20743703+Camotoy@users.noreply.github.com> Date: Thu, 24 Mar 2022 17:39:35 -0400 Subject: [PATCH 06/17] Spigot: enable command completions for /geyser --- bootstrap/spigot/pom.xml | 22 +++++ .../platform/spigot/GeyserSpigotPlugin.java | 29 ++++++- .../command/GeyserPaperCommandListener.java | 87 +++++++++++++++++++ .../command/GeyserSpigotCommandManager.java | 21 +++-- 4 files changed, 152 insertions(+), 7 deletions(-) create mode 100644 bootstrap/spigot/src/main/java/org/geysermc/geyser/platform/spigot/command/GeyserPaperCommandListener.java diff --git a/bootstrap/spigot/pom.xml b/bootstrap/spigot/pom.xml index da8b184e9..26f9c7083 100644 --- a/bootstrap/spigot/pom.xml +++ b/bootstrap/spigot/pom.xml @@ -19,6 +19,11 @@ viaversion-repo https://repo.viaversion.com + + + minecraft-repo + https://libraries.minecraft.net/ + @@ -34,6 +39,12 @@ 1.18.1-R0.1-SNAPSHOT provided + + io.papermc.paper + paper-mojangapi + 1.18.1-R0.1-SNAPSHOT + provided + com.viaversion viaversion @@ -45,6 +56,12 @@ spigot-all 1.4-SNAPSHOT + + me.lucko + commodore + 1.13 + compile + ${outputName}-Spigot @@ -95,6 +112,10 @@ org.objectweb.asm org.geysermc.geyser.platform.spigot.shaded.asm + + me.lucko.commodore + org.geysermc.geyser.platform.spigot.shaded.commodore + @@ -118,6 +139,7 @@ io.netty:netty-codec-dns:* io.netty:netty-resolver-dns:* io.netty:netty-resolver-dns-native-macos:* + com.mojang:* diff --git a/bootstrap/spigot/src/main/java/org/geysermc/geyser/platform/spigot/GeyserSpigotPlugin.java b/bootstrap/spigot/src/main/java/org/geysermc/geyser/platform/spigot/GeyserSpigotPlugin.java index aae6c599a..b09aafd24 100644 --- a/bootstrap/spigot/src/main/java/org/geysermc/geyser/platform/spigot/GeyserSpigotPlugin.java +++ b/bootstrap/spigot/src/main/java/org/geysermc/geyser/platform/spigot/GeyserSpigotPlugin.java @@ -25,11 +25,15 @@ package org.geysermc.geyser.platform.spigot; +import com.mojang.brigadier.builder.LiteralArgumentBuilder; import com.viaversion.viaversion.api.Via; import com.viaversion.viaversion.api.data.MappingData; import com.viaversion.viaversion.api.protocol.ProtocolPathEntry; import com.viaversion.viaversion.api.protocol.version.ProtocolVersion; +import me.lucko.commodore.Commodore; +import me.lucko.commodore.CommodoreProvider; import org.bukkit.Bukkit; +import org.bukkit.command.PluginCommand; import org.bukkit.plugin.java.JavaPlugin; import org.geysermc.common.PlatformType; import org.geysermc.geyser.Constants; @@ -43,6 +47,7 @@ import org.geysermc.geyser.level.WorldManager; import org.geysermc.geyser.network.MinecraftProtocol; import org.geysermc.geyser.ping.GeyserLegacyPingPassthrough; import org.geysermc.geyser.ping.IGeyserPingPassthrough; +import org.geysermc.geyser.platform.spigot.command.GeyserPaperCommandListener; import org.geysermc.geyser.platform.spigot.command.GeyserSpigotCommandExecutor; import org.geysermc.geyser.platform.spigot.command.GeyserSpigotCommandManager; import org.geysermc.geyser.platform.spigot.command.SpigotCommandSender; @@ -234,7 +239,29 @@ public class GeyserSpigotPlugin extends JavaPlugin implements GeyserBootstrap { Bukkit.getServer().getPluginManager().registerEvents(new GeyserPistonListener(geyser, this.geyserWorldManager), this); - this.getCommand("geyser").setExecutor(new GeyserSpigotCommandExecutor(geyser)); + PluginCommand pluginCommand = this.getCommand("geyser"); + pluginCommand.setExecutor(new GeyserSpigotCommandExecutor(geyser)); + + boolean brigadierSupported = CommodoreProvider.isSupported(); + geyserLogger.debug("Brigadier supported? " + brigadierSupported); + if (brigadierSupported) { + // Enable command completions if supported + // This is beneficial because this is sent over the network and Bedrock can see it + Commodore commodore = CommodoreProvider.getCommodore(this); + LiteralArgumentBuilder builder = LiteralArgumentBuilder.literal("geyser"); + for (String command : geyserCommandManager.getCommands().keySet()) { + builder.then(LiteralArgumentBuilder.literal(command)); + } + commodore.register(pluginCommand, builder); + + try { + Class.forName("com.destroystokyo.paper.event.brigadier.AsyncPlayerSendCommandsEvent"); + Bukkit.getServer().getPluginManager().registerEvents(new GeyserPaperCommandListener(), this); + geyserLogger.debug("Successfully registered AsyncPlayerSendCommandsEvent listener."); + } catch (ClassNotFoundException e) { + geyserLogger.debug("Not registering AsyncPlayerSendCommandsEvent listener."); + } + } // Check to ensure the current setup can support the protocol version Geyser uses GeyserSpigotVersionChecker.checkForSupportedProtocol(geyserLogger, isViaVersion); diff --git a/bootstrap/spigot/src/main/java/org/geysermc/geyser/platform/spigot/command/GeyserPaperCommandListener.java b/bootstrap/spigot/src/main/java/org/geysermc/geyser/platform/spigot/command/GeyserPaperCommandListener.java new file mode 100644 index 000000000..00c1ba58d --- /dev/null +++ b/bootstrap/spigot/src/main/java/org/geysermc/geyser/platform/spigot/command/GeyserPaperCommandListener.java @@ -0,0 +1,87 @@ +/* + * Copyright (c) 2019-2022 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.geyser.platform.spigot.command; + +import com.destroystokyo.paper.event.brigadier.AsyncPlayerSendCommandsEvent; +import com.mojang.brigadier.tree.CommandNode; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.Listener; +import org.geysermc.geyser.GeyserImpl; +import org.geysermc.geyser.command.GeyserCommand; + +import java.net.InetSocketAddress; +import java.util.Iterator; +import java.util.Map; + +public final class GeyserPaperCommandListener implements Listener { + + @EventHandler + @SuppressWarnings("deprecation") // Used to indicate an unstable event + public void onCommandSend(AsyncPlayerSendCommandsEvent event) { + // Documentation says to check (event.isAsynchronous() || !event.hasFiredAsync()), but as of Paper 1.18.2 + // event.hasFiredAsync is never true + if (event.isAsynchronous()) { + CommandNode geyserBrigadier = event.getCommandNode().getChild("geyser"); + if (geyserBrigadier != null) { + Player player = event.getPlayer(); + boolean isJavaPlayer = isProbablyJavaPlayer(player); + Map commands = GeyserImpl.getInstance().getCommandManager().getCommands(); + Iterator> it = geyserBrigadier.getChildren().iterator(); + + while (it.hasNext()) { + CommandNode subnode = it.next(); + GeyserCommand command = commands.get(subnode.getName()); + if (command != null) { + if ((command.isBedrockOnly() && isJavaPlayer) || !player.hasPermission(command.getPermission())) { + // Remove this from the node as we don't have permission to use it + it.remove(); + } + } + } + } + } + } + + /** + * This early on, there is a rare chance that Geyser has yet to process the connection. We'll try to minimize that + * chance, though. + */ + private boolean isProbablyJavaPlayer(Player player) { + if (GeyserImpl.getInstance().connectionByUuid(player.getUniqueId()) != null) { + // For sure this is a Bedrock player + return false; + } + + if (GeyserImpl.getInstance().getConfig().isUseDirectConnection()) { + InetSocketAddress address = player.getAddress(); + if (address != null) { + return address.getPort() != 0; + } + } + return true; + } +} diff --git a/bootstrap/spigot/src/main/java/org/geysermc/geyser/platform/spigot/command/GeyserSpigotCommandManager.java b/bootstrap/spigot/src/main/java/org/geysermc/geyser/platform/spigot/command/GeyserSpigotCommandManager.java index 103390ab8..6107d5b47 100644 --- a/bootstrap/spigot/src/main/java/org/geysermc/geyser/platform/spigot/command/GeyserSpigotCommandManager.java +++ b/bootstrap/spigot/src/main/java/org/geysermc/geyser/platform/spigot/command/GeyserSpigotCommandManager.java @@ -26,6 +26,7 @@ package org.geysermc.geyser.platform.spigot.command; import org.bukkit.Bukkit; +import org.bukkit.Server; import org.bukkit.command.Command; import org.bukkit.command.CommandMap; import org.geysermc.geyser.GeyserImpl; @@ -35,16 +36,24 @@ import java.lang.reflect.Field; public class GeyserSpigotCommandManager extends CommandManager { - private static CommandMap COMMAND_MAP; + private static final CommandMap COMMAND_MAP; static { + CommandMap commandMap = null; try { - Field cmdMapField = Bukkit.getServer().getClass().getDeclaredField("commandMap"); - cmdMapField.setAccessible(true); - COMMAND_MAP = (CommandMap) cmdMapField.get(Bukkit.getServer()); - } catch (NoSuchFieldException | IllegalAccessException ex) { - ex.printStackTrace(); + // Paper-only + Server.class.getMethod("getCommandMap"); + commandMap = Bukkit.getServer().getCommandMap(); + } catch (NoSuchMethodException e) { + try { + Field cmdMapField = Bukkit.getServer().getClass().getDeclaredField("commandMap"); + cmdMapField.setAccessible(true); + commandMap = (CommandMap) cmdMapField.get(Bukkit.getServer()); + } catch (NoSuchFieldException | IllegalAccessException ex) { + ex.printStackTrace(); + } } + COMMAND_MAP = commandMap; } public GeyserSpigotCommandManager(GeyserImpl geyser) { From c610e98f4c510de3920e46c879677ee7e95ae8d4 Mon Sep 17 00:00:00 2001 From: Camotoy <20743703+Camotoy@users.noreply.github.com> Date: Fri, 25 Mar 2022 14:04:16 -0400 Subject: [PATCH 07/17] Spigot: fix loading on 1.12 --- .../platform/spigot/GeyserSpigotPlugin.java | 21 +------ .../command/GeyserBrigadierSupport.java | 61 +++++++++++++++++++ 2 files changed, 63 insertions(+), 19 deletions(-) create mode 100644 bootstrap/spigot/src/main/java/org/geysermc/geyser/platform/spigot/command/GeyserBrigadierSupport.java diff --git a/bootstrap/spigot/src/main/java/org/geysermc/geyser/platform/spigot/GeyserSpigotPlugin.java b/bootstrap/spigot/src/main/java/org/geysermc/geyser/platform/spigot/GeyserSpigotPlugin.java index b09aafd24..6e490bfca 100644 --- a/bootstrap/spigot/src/main/java/org/geysermc/geyser/platform/spigot/GeyserSpigotPlugin.java +++ b/bootstrap/spigot/src/main/java/org/geysermc/geyser/platform/spigot/GeyserSpigotPlugin.java @@ -25,12 +25,10 @@ package org.geysermc.geyser.platform.spigot; -import com.mojang.brigadier.builder.LiteralArgumentBuilder; import com.viaversion.viaversion.api.Via; import com.viaversion.viaversion.api.data.MappingData; import com.viaversion.viaversion.api.protocol.ProtocolPathEntry; import com.viaversion.viaversion.api.protocol.version.ProtocolVersion; -import me.lucko.commodore.Commodore; import me.lucko.commodore.CommodoreProvider; import org.bukkit.Bukkit; import org.bukkit.command.PluginCommand; @@ -47,7 +45,7 @@ import org.geysermc.geyser.level.WorldManager; import org.geysermc.geyser.network.MinecraftProtocol; import org.geysermc.geyser.ping.GeyserLegacyPingPassthrough; import org.geysermc.geyser.ping.IGeyserPingPassthrough; -import org.geysermc.geyser.platform.spigot.command.GeyserPaperCommandListener; +import org.geysermc.geyser.platform.spigot.command.GeyserBrigadierSupport; import org.geysermc.geyser.platform.spigot.command.GeyserSpigotCommandExecutor; import org.geysermc.geyser.platform.spigot.command.GeyserSpigotCommandManager; import org.geysermc.geyser.platform.spigot.command.SpigotCommandSender; @@ -245,22 +243,7 @@ public class GeyserSpigotPlugin extends JavaPlugin implements GeyserBootstrap { boolean brigadierSupported = CommodoreProvider.isSupported(); geyserLogger.debug("Brigadier supported? " + brigadierSupported); if (brigadierSupported) { - // Enable command completions if supported - // This is beneficial because this is sent over the network and Bedrock can see it - Commodore commodore = CommodoreProvider.getCommodore(this); - LiteralArgumentBuilder builder = LiteralArgumentBuilder.literal("geyser"); - for (String command : geyserCommandManager.getCommands().keySet()) { - builder.then(LiteralArgumentBuilder.literal(command)); - } - commodore.register(pluginCommand, builder); - - try { - Class.forName("com.destroystokyo.paper.event.brigadier.AsyncPlayerSendCommandsEvent"); - Bukkit.getServer().getPluginManager().registerEvents(new GeyserPaperCommandListener(), this); - geyserLogger.debug("Successfully registered AsyncPlayerSendCommandsEvent listener."); - } catch (ClassNotFoundException e) { - geyserLogger.debug("Not registering AsyncPlayerSendCommandsEvent listener."); - } + GeyserBrigadierSupport.loadBrigadier(this, pluginCommand); } // Check to ensure the current setup can support the protocol version Geyser uses diff --git a/bootstrap/spigot/src/main/java/org/geysermc/geyser/platform/spigot/command/GeyserBrigadierSupport.java b/bootstrap/spigot/src/main/java/org/geysermc/geyser/platform/spigot/command/GeyserBrigadierSupport.java new file mode 100644 index 000000000..61900174c --- /dev/null +++ b/bootstrap/spigot/src/main/java/org/geysermc/geyser/platform/spigot/command/GeyserBrigadierSupport.java @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2019-2022 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.geyser.platform.spigot.command; + +import com.mojang.brigadier.builder.LiteralArgumentBuilder; +import me.lucko.commodore.Commodore; +import me.lucko.commodore.CommodoreProvider; +import org.bukkit.Bukkit; +import org.bukkit.command.PluginCommand; +import org.geysermc.geyser.platform.spigot.GeyserSpigotPlugin; + +/** + * Needs to be a separate class so pre-1.13 loads correctly. + */ +public final class GeyserBrigadierSupport { + + public static void loadBrigadier(GeyserSpigotPlugin plugin, PluginCommand pluginCommand) { + // Enable command completions if supported + // This is beneficial because this is sent over the network and Bedrock can see it + Commodore commodore = CommodoreProvider.getCommodore(plugin); + LiteralArgumentBuilder builder = LiteralArgumentBuilder.literal("geyser"); + for (String command : plugin.getGeyserCommandManager().getCommands().keySet()) { + builder.then(LiteralArgumentBuilder.literal(command)); + } + commodore.register(pluginCommand, builder); + + try { + Class.forName("com.destroystokyo.paper.event.brigadier.AsyncPlayerSendCommandsEvent"); + Bukkit.getServer().getPluginManager().registerEvents(new GeyserPaperCommandListener(), plugin); + plugin.getGeyserLogger().debug("Successfully registered AsyncPlayerSendCommandsEvent listener."); + } catch (ClassNotFoundException e) { + plugin.getGeyserLogger().debug("Not registering AsyncPlayerSendCommandsEvent listener."); + } + } + + private GeyserBrigadierSupport() { + } +} From f639be6362d68409b0b249810abefae2895f7ce7 Mon Sep 17 00:00:00 2001 From: Camotoy <20743703+Camotoy@users.noreply.github.com> Date: Fri, 25 Mar 2022 20:22:39 -0400 Subject: [PATCH 08/17] Better handling of fake cooldown Because of Bedrock limitations, if a player has text background opacity enabled, they'll see an empty section where the title is usually displayed as the fake cooldown is shown. This commit minimizes the time that is shown by clearing the text as soon as possible. Reference issue: https://github.com/GeyserMC/Geyser/issues/1710 This commit also removes starting the fake cooldown process if the client switches to an inventory slot with the same Java ID. --- .../geyser/session/cache/WorldCache.java | 67 ++++++++++++++++++- .../BedrockMobEquipmentTranslator.java | 21 ++++-- .../java/title/JavaClearTitlesTranslator.java | 5 +- .../title/JavaSetTitleTextTranslator.java | 2 + .../JavaSetTitlesAnimationTranslator.java | 11 ++- .../geysermc/geyser/util/CooldownUtils.java | 22 +++--- 6 files changed, 105 insertions(+), 23 deletions(-) diff --git a/core/src/main/java/org/geysermc/geyser/session/cache/WorldCache.java b/core/src/main/java/org/geysermc/geyser/session/cache/WorldCache.java index aa4d10d04..0ef5427ea 100644 --- a/core/src/main/java/org/geysermc/geyser/session/cache/WorldCache.java +++ b/core/src/main/java/org/geysermc/geyser/session/cache/WorldCache.java @@ -26,24 +26,36 @@ package org.geysermc.geyser.session.cache; import com.github.steveice10.mc.protocol.data.game.setting.Difficulty; +import com.nukkitx.protocol.bedrock.packet.SetTitlePacket; import lombok.Getter; import lombok.Setter; -import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.scoreboard.Scoreboard; import org.geysermc.geyser.scoreboard.ScoreboardUpdater.ScoreboardSession; +import org.geysermc.geyser.session.GeyserSession; -@Getter -public class WorldCache { +public final class WorldCache { private final GeyserSession session; + @Getter private final ScoreboardSession scoreboardSession; + @Getter private Scoreboard scoreboard; + @Getter @Setter private Difficulty difficulty = Difficulty.EASY; + /** + * Whether our cooldown changed the title time, and the true title times need to be re-sent. + */ + private boolean titleTimesNeedReset = false; + private int trueTitleFadeInTime; + private int trueTitleStayTime; + private int trueTitleFadeOutTime; + public WorldCache(GeyserSession session) { this.session = session; this.scoreboard = new Scoreboard(session); scoreboardSession = new ScoreboardSession(session); + resetTitleTimes(false); } public void removeScoreboard() { @@ -58,4 +70,53 @@ public class WorldCache { int pps = scoreboardSession.getPacketsPerSecond(); return Math.max(pps, pendingPps); } + + public void markTitleTimesAsIncorrect() { + titleTimesNeedReset = true; + } + + /** + * Store the true active title times. + */ + public void setTitleTimes(int fadeInTime, int stayTime, int fadeOutTime) { + trueTitleFadeInTime = fadeInTime; + trueTitleStayTime = stayTime; + trueTitleFadeOutTime = fadeOutTime; + } + + /** + * If needed, ensure that the Bedrock client will use the correct timings for titles. + */ + public void synchronizeCorrectTitleTimes() { + if (titleTimesNeedReset) { + forceSyncCorrectTitleTimes(); + titleTimesNeedReset = false; + } + } + + private void forceSyncCorrectTitleTimes() { + SetTitlePacket titlePacket = new SetTitlePacket(); + titlePacket.setType(SetTitlePacket.Type.TIMES); + titlePacket.setText(""); + titlePacket.setFadeInTime(trueTitleFadeInTime); + titlePacket.setStayTime(trueTitleStayTime); + titlePacket.setFadeOutTime(trueTitleFadeOutTime); + titlePacket.setPlatformOnlineId(""); + titlePacket.setXuid(""); + + session.sendUpstreamPacket(titlePacket); + } + + /** + * Reset the true active title times to the (Java Edition 1.18.2) defaults. + */ + public void resetTitleTimes(boolean clientSync) { + trueTitleFadeInTime = 10; + trueTitleStayTime = 70; + trueTitleFadeOutTime = 20; + + if (clientSync) { + forceSyncCorrectTitleTimes(); + } + } } \ No newline at end of file diff --git a/core/src/main/java/org/geysermc/geyser/translator/protocol/bedrock/BedrockMobEquipmentTranslator.java b/core/src/main/java/org/geysermc/geyser/translator/protocol/bedrock/BedrockMobEquipmentTranslator.java index 5c551dce4..2bbae4a49 100644 --- a/core/src/main/java/org/geysermc/geyser/translator/protocol/bedrock/BedrockMobEquipmentTranslator.java +++ b/core/src/main/java/org/geysermc/geyser/translator/protocol/bedrock/BedrockMobEquipmentTranslator.java @@ -30,6 +30,7 @@ import com.github.steveice10.mc.protocol.packet.ingame.serverbound.player.Server import com.github.steveice10.mc.protocol.packet.ingame.serverbound.player.ServerboundUseItemPacket; import com.nukkitx.protocol.bedrock.data.inventory.ContainerId; import com.nukkitx.protocol.bedrock.packet.MobEquipmentPacket; +import org.geysermc.geyser.inventory.GeyserItemStack; import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.translator.protocol.PacketTranslator; import org.geysermc.geyser.translator.protocol.Translator; @@ -42,8 +43,9 @@ public class BedrockMobEquipmentTranslator extends PacketTranslator 8 || - packet.getContainerId() != ContainerId.INVENTORY || session.getPlayerInventory().getHeldItemSlot() == packet.getHotbarSlot()) { + int newSlot = packet.getHotbarSlot(); + if (!session.isSpawned() || newSlot > 8 || packet.getContainerId() != ContainerId.INVENTORY + || session.getPlayerInventory().getHeldItemSlot() == newSlot) { // For the last condition - Don't update the slot if the slot is the same - not Java Edition behavior and messes with plugins such as Grief Prevention return; } @@ -51,12 +53,15 @@ public class BedrockMobEquipmentTranslator extends PacketTranslator 20) return; // 0.0 usually happens on login and causes issues with visuals; anything above 20 means a plugin like OldCombatMechanics is being used - // Needs to be sent or no subtitle packet is recognized by the client + // Set the times to stay a bit with no fade in nor out SetTitlePacket titlePacket = new SetTitlePacket(); + titlePacket.setType(SetTitlePacket.Type.TIMES); + titlePacket.setStayTime(1000); + titlePacket.setText(""); + titlePacket.setXuid(""); + titlePacket.setPlatformOnlineId(""); + session.sendUpstreamPacket(titlePacket); + + session.getWorldCache().markTitleTimesAsIncorrect(); + + // Needs to be sent or no subtitle packet is recognized by the client + titlePacket = new SetTitlePacket(); titlePacket.setType(SetTitlePacket.Type.TITLE); titlePacket.setText(" "); titlePacket.setXuid(""); @@ -85,9 +96,6 @@ public class CooldownUtils { titlePacket.setType(SetTitlePacket.Type.SUBTITLE); } titlePacket.setText(getTitle(session)); - titlePacket.setFadeInTime(0); - titlePacket.setFadeOutTime(5); - titlePacket.setStayTime(2); titlePacket.setXuid(""); titlePacket.setPlatformOnlineId(""); session.sendUpstreamPacket(titlePacket); @@ -96,11 +104,7 @@ public class CooldownUtils { computeCooldown(session, sessionPreference, lastHitTime), 50, TimeUnit.MILLISECONDS); // Updated per tick. 1000 divided by 20 ticks equals 50 } else { SetTitlePacket removeTitlePacket = new SetTitlePacket(); - if (sessionPreference == CooldownType.ACTIONBAR) { - removeTitlePacket.setType(SetTitlePacket.Type.ACTIONBAR); - } else { - removeTitlePacket.setType(SetTitlePacket.Type.SUBTITLE); - } + removeTitlePacket.setType(SetTitlePacket.Type.CLEAR); removeTitlePacket.setText(" "); removeTitlePacket.setXuid(""); removeTitlePacket.setPlatformOnlineId(""); From 238be40c6a9f2e0eeddf094a4519895a941b49ef Mon Sep 17 00:00:00 2001 From: Camotoy <20743703+Camotoy@users.noreply.github.com> Date: Fri, 25 Mar 2022 20:30:33 -0400 Subject: [PATCH 09/17] No need to reset when a set times packet is just about to be sent --- .../java/org/geysermc/geyser/session/cache/WorldCache.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/core/src/main/java/org/geysermc/geyser/session/cache/WorldCache.java b/core/src/main/java/org/geysermc/geyser/session/cache/WorldCache.java index 0ef5427ea..17679ad3e 100644 --- a/core/src/main/java/org/geysermc/geyser/session/cache/WorldCache.java +++ b/core/src/main/java/org/geysermc/geyser/session/cache/WorldCache.java @@ -82,6 +82,8 @@ public final class WorldCache { trueTitleFadeInTime = fadeInTime; trueTitleStayTime = stayTime; trueTitleFadeOutTime = fadeOutTime; + // The translator will sync this for us + titleTimesNeedReset = false; } /** @@ -90,7 +92,6 @@ public final class WorldCache { public void synchronizeCorrectTitleTimes() { if (titleTimesNeedReset) { forceSyncCorrectTitleTimes(); - titleTimesNeedReset = false; } } @@ -105,6 +106,7 @@ public final class WorldCache { titlePacket.setXuid(""); session.sendUpstreamPacket(titlePacket); + titleTimesNeedReset = false; } /** From 08051edad1b3c389e26a05f03855d5d9f09e469f Mon Sep 17 00:00:00 2001 From: rtm516 Date: Sat, 26 Mar 2022 15:49:02 +0000 Subject: [PATCH 10/17] Update Jackson dependency --- core/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/pom.xml b/core/pom.xml index c5d80db8a..cba63d355 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -13,7 +13,7 @@ 4.9.3 8.5.2 - 2.12.4 + 2.13.2.1 4.1.66.Final From 0067ba5bb9654a07e9e16dfd1dabaa7c6350fa96 Mon Sep 17 00:00:00 2001 From: rtm516 Date: Sat, 26 Mar 2022 15:56:36 +0000 Subject: [PATCH 11/17] Fix jackson versions causing build to fail --- core/pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/pom.xml b/core/pom.xml index cba63d355..d40b7dcc4 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -13,7 +13,7 @@ 4.9.3 8.5.2 - 2.13.2.1 + 2.13.2 4.1.66.Final @@ -52,7 +52,7 @@ com.fasterxml.jackson.core jackson-databind - ${jackson.version} + ${jackson.version}.1 compile From f78d2d3d2a691dc5f4cbfbf05e75e017c379475f Mon Sep 17 00:00:00 2001 From: Camotoy <20743703+Camotoy@users.noreply.github.com> Date: Tue, 29 Mar 2022 14:36:58 -0400 Subject: [PATCH 12/17] Fix ghost items when taking items out of a furnace --- .../java/org/geysermc/geyser/inventory/click/ClickPlan.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/core/src/main/java/org/geysermc/geyser/inventory/click/ClickPlan.java b/core/src/main/java/org/geysermc/geyser/inventory/click/ClickPlan.java index b0cca53d9..575294a5b 100644 --- a/core/src/main/java/org/geysermc/geyser/inventory/click/ClickPlan.java +++ b/core/src/main/java/org/geysermc/geyser/inventory/click/ClickPlan.java @@ -152,6 +152,7 @@ public final class ClickPlan { clickedItemStack, changedItems ); + System.out.println(clickPacket); session.sendDownstreamPacket(clickPacket); } @@ -391,7 +392,7 @@ public final class ClickPlan { public IntSet getAffectedSlots() { IntSet affectedSlots = new IntOpenHashSet(); for (ClickAction action : plan) { - if (translator.getSlotType(action.slot) == SlotType.NORMAL && action.slot != Click.OUTSIDE_SLOT) { + if (translator.getSlotType(action.slot) != SlotType.OUTPUT && action.slot != Click.OUTSIDE_SLOT) { affectedSlots.add(action.slot); if (action.click.actionType == ContainerActionType.MOVE_TO_HOTBAR_SLOT) { //TODO won't work if offhand is added From 7a5321b78fc1af44e8e3b45960fcb67e9a168458 Mon Sep 17 00:00:00 2001 From: Camotoy <20743703+Camotoy@users.noreply.github.com> Date: Tue, 29 Mar 2022 14:38:15 -0400 Subject: [PATCH 13/17] Hmm what print line? --- .../main/java/org/geysermc/geyser/inventory/click/ClickPlan.java | 1 - 1 file changed, 1 deletion(-) diff --git a/core/src/main/java/org/geysermc/geyser/inventory/click/ClickPlan.java b/core/src/main/java/org/geysermc/geyser/inventory/click/ClickPlan.java index 575294a5b..ec36645da 100644 --- a/core/src/main/java/org/geysermc/geyser/inventory/click/ClickPlan.java +++ b/core/src/main/java/org/geysermc/geyser/inventory/click/ClickPlan.java @@ -152,7 +152,6 @@ public final class ClickPlan { clickedItemStack, changedItems ); - System.out.println(clickPacket); session.sendDownstreamPacket(clickPacket); } From 2a05dd57ffe8208fcf0ed903baa379d3e415e557 Mon Sep 17 00:00:00 2001 From: Camotoy <20743703+Camotoy@users.noreply.github.com> Date: Wed, 30 Mar 2022 22:30:49 -0400 Subject: [PATCH 14/17] Don't store GameProfile class of players This stores repetitive information, and also we don't currently use the signature, so it's wasted memory. --- .../entity/type/player/PlayerEntity.java | 20 +++++++---- .../type/player/SessionPlayerEntity.java | 3 +- .../entity/type/player/SkullPlayerEntity.java | 7 ++-- .../geyser/skin/FakeHeadProvider.java | 18 ++-------- .../org/geysermc/geyser/skin/SkinManager.java | 36 +++++++++---------- .../geysermc/geyser/skin/SkinProvider.java | 12 ++----- .../geyser/skin/SkullSkinManager.java | 2 +- .../entity/SkullBlockEntityTranslator.java | 28 +++++---------- .../player/JavaPlayerInfoTranslator.java | 18 +++++++--- .../entity/spawn/JavaAddPlayerTranslator.java | 4 ++- 10 files changed, 68 insertions(+), 80 deletions(-) diff --git a/core/src/main/java/org/geysermc/geyser/entity/type/player/PlayerEntity.java b/core/src/main/java/org/geysermc/geyser/entity/type/player/PlayerEntity.java index 70b5ede99..58f04a756 100644 --- a/core/src/main/java/org/geysermc/geyser/entity/type/player/PlayerEntity.java +++ b/core/src/main/java/org/geysermc/geyser/entity/type/player/PlayerEntity.java @@ -25,7 +25,6 @@ package org.geysermc.geyser.entity.type.player; -import com.github.steveice10.mc.auth.data.GameProfile; import com.github.steveice10.mc.protocol.data.game.entity.metadata.EntityMetadata; import com.github.steveice10.mc.protocol.data.game.entity.metadata.Pose; import com.github.steveice10.mc.protocol.data.game.entity.metadata.Position; @@ -61,15 +60,21 @@ import org.geysermc.geyser.translator.text.MessageTranslator; import javax.annotation.Nullable; import java.util.Collections; import java.util.Optional; +import java.util.UUID; import java.util.concurrent.TimeUnit; @Getter @Setter public class PlayerEntity extends LivingEntity { public static final float SNEAKING_POSE_HEIGHT = 1.5f; - private GameProfile profile; private String username; - private boolean playerList = true; // Player is in the player list + private boolean playerList = true; // Player is in the player list + + /** + * The textures property from the GameProfile. + */ + @Nullable + private String texturesProperty; private Vector3i bedPosition; @@ -82,11 +87,12 @@ public class PlayerEntity extends LivingEntity { */ private ParrotEntity rightParrot; - public PlayerEntity(GeyserSession session, int entityId, long geyserId, GameProfile gameProfile, Vector3f position, Vector3f motion, float yaw, float pitch, float headYaw) { - super(session, entityId, geyserId, gameProfile.getId(), EntityDefinitions.PLAYER, position, motion, yaw, pitch, headYaw); + public PlayerEntity(GeyserSession session, int entityId, long geyserId, UUID uuid, Vector3f position, + Vector3f motion, float yaw, float pitch, float headYaw, String username, @Nullable String texturesProperty) { + super(session, entityId, geyserId, uuid, EntityDefinitions.PLAYER, position, motion, yaw, pitch, headYaw); - profile = gameProfile; - username = gameProfile.getName(); + this.username = username; + this.texturesProperty = texturesProperty; } @Override diff --git a/core/src/main/java/org/geysermc/geyser/entity/type/player/SessionPlayerEntity.java b/core/src/main/java/org/geysermc/geyser/entity/type/player/SessionPlayerEntity.java index 077f82171..ae8d23810 100644 --- a/core/src/main/java/org/geysermc/geyser/entity/type/player/SessionPlayerEntity.java +++ b/core/src/main/java/org/geysermc/geyser/entity/type/player/SessionPlayerEntity.java @@ -25,7 +25,6 @@ package org.geysermc.geyser.entity.type.player; -import com.github.steveice10.mc.auth.data.GameProfile; import com.github.steveice10.mc.protocol.data.game.entity.attribute.Attribute; import com.github.steveice10.mc.protocol.data.game.entity.attribute.AttributeType; import com.github.steveice10.mc.protocol.data.game.entity.metadata.Pose; @@ -71,7 +70,7 @@ public class SessionPlayerEntity extends PlayerEntity { private int fakeTradeXp; public SessionPlayerEntity(GeyserSession session) { - super(session, -1, 1, new GameProfile(UUID.randomUUID(), "unknown"), Vector3f.ZERO, Vector3f.ZERO, 0, 0, 0); + super(session, -1, 1, UUID.randomUUID(), Vector3f.ZERO, Vector3f.ZERO, 0, 0, 0, "unknown", null); valid = true; } diff --git a/core/src/main/java/org/geysermc/geyser/entity/type/player/SkullPlayerEntity.java b/core/src/main/java/org/geysermc/geyser/entity/type/player/SkullPlayerEntity.java index 847abf2a9..ce1615816 100644 --- a/core/src/main/java/org/geysermc/geyser/entity/type/player/SkullPlayerEntity.java +++ b/core/src/main/java/org/geysermc/geyser/entity/type/player/SkullPlayerEntity.java @@ -25,7 +25,6 @@ package org.geysermc.geyser.entity.type.player; -import com.github.steveice10.mc.auth.data.GameProfile; import com.nukkitx.math.vector.Vector3f; import com.nukkitx.math.vector.Vector3i; import com.nukkitx.protocol.bedrock.data.PlayerPermission; @@ -36,6 +35,8 @@ import com.nukkitx.protocol.bedrock.packet.AddPlayerPacket; import lombok.Getter; import org.geysermc.geyser.session.GeyserSession; +import java.util.UUID; + /** * A wrapper to handle skulls more effectively - skulls have to be treated as entities since there are no * custom player skulls in Bedrock. @@ -48,8 +49,8 @@ public class SkullPlayerEntity extends PlayerEntity { @Getter private final int blockState; - public SkullPlayerEntity(GeyserSession session, long geyserId, GameProfile gameProfile, Vector3f position, float rotation, int blockState) { - super(session, 0, geyserId, gameProfile, position, Vector3f.ZERO, rotation, 0, rotation); + public SkullPlayerEntity(GeyserSession session, long geyserId, Vector3f position, float rotation, int blockState, String texturesProperty) { + super(session, 0, geyserId, UUID.randomUUID(), position, Vector3f.ZERO, rotation, 0, rotation, "", texturesProperty); this.blockState = blockState; setPlayerList(false); } diff --git a/core/src/main/java/org/geysermc/geyser/skin/FakeHeadProvider.java b/core/src/main/java/org/geysermc/geyser/skin/FakeHeadProvider.java index 66354b494..6794af498 100644 --- a/core/src/main/java/org/geysermc/geyser/skin/FakeHeadProvider.java +++ b/core/src/main/java/org/geysermc/geyser/skin/FakeHeadProvider.java @@ -25,7 +25,6 @@ package org.geysermc.geyser.skin; -import com.github.steveice10.mc.auth.data.GameProfile; import com.github.steveice10.opennbt.tag.builtin.CompoundTag; import com.google.common.cache.CacheBuilder; import com.google.common.cache.CacheLoader; @@ -106,7 +105,7 @@ public class FakeHeadProvider { session.getPlayerWithCustomHeads().add(entity.getUuid()); - GameProfile.Property texturesProperty = entity.getProfile().getProperty("textures"); + String texturesProperty = entity.getTexturesProperty(); SkinProvider.EXECUTOR_SERVICE.execute(() -> { try { @@ -182,7 +181,7 @@ public class FakeHeadProvider { @Getter @Setter private static class FakeHeadEntry { - private final GameProfile.Property texturesProperty; + private final String texturesProperty; private final String fakeHeadSkinUrl; private PlayerEntity entity; @@ -192,18 +191,7 @@ public class FakeHeadProvider { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; FakeHeadEntry that = (FakeHeadEntry) o; - return equals(texturesProperty, that.texturesProperty) && Objects.equals(fakeHeadSkinUrl, that.fakeHeadSkinUrl); - } - - private boolean equals(GameProfile.Property a, GameProfile.Property b) { - //TODO actually fix this in MCAuthLib - if (a == b) { - return true; - } - if (a == null || b == null) { - return false; - } - return Objects.equals(a.getName(), b.getName()) && Objects.equals(a.getValue(), b.getValue()) && Objects.equals(a.getSignature(), b.getSignature()); + return Objects.equals(texturesProperty, that.texturesProperty) && Objects.equals(fakeHeadSkinUrl, that.fakeHeadSkinUrl); } @Override diff --git a/core/src/main/java/org/geysermc/geyser/skin/SkinManager.java b/core/src/main/java/org/geysermc/geyser/skin/SkinManager.java index 4269110f5..4eb92c3ac 100644 --- a/core/src/main/java/org/geysermc/geyser/skin/SkinManager.java +++ b/core/src/main/java/org/geysermc/geyser/skin/SkinManager.java @@ -26,7 +26,6 @@ package org.geysermc.geyser.skin; import com.fasterxml.jackson.databind.JsonNode; -import com.github.steveice10.mc.auth.data.GameProfile; import com.github.steveice10.opennbt.tag.builtin.CompoundTag; import com.github.steveice10.opennbt.tag.builtin.ListTag; import com.github.steveice10.opennbt.tag.builtin.StringTag; @@ -34,9 +33,9 @@ import com.nukkitx.protocol.bedrock.data.skin.ImageData; import com.nukkitx.protocol.bedrock.data.skin.SerializedSkin; import com.nukkitx.protocol.bedrock.packet.PlayerListPacket; import org.geysermc.geyser.GeyserImpl; -import org.geysermc.geyser.session.auth.AuthType; import org.geysermc.geyser.entity.type.player.PlayerEntity; import org.geysermc.geyser.session.GeyserSession; +import org.geysermc.geyser.session.auth.AuthType; import org.geysermc.geyser.session.auth.BedrockClientData; import org.geysermc.geyser.text.GeyserLocale; @@ -54,7 +53,7 @@ public class SkinManager { * Builds a Bedrock player list entry from our existing, cached Bedrock skin information */ public static PlayerListPacket.Entry buildCachedEntry(GeyserSession session, PlayerEntity playerEntity) { - GameProfileData data = GameProfileData.from(playerEntity.getProfile()); + GameProfileData data = GameProfileData.from(playerEntity); SkinProvider.Cape cape = SkinProvider.getCachedCape(data.capeUrl()); SkinProvider.SkinGeometry geometry = SkinProvider.SkinGeometry.getLegacy(data.isAlex()); @@ -65,8 +64,8 @@ public class SkinManager { return buildEntryManually( session, - playerEntity.getProfile().getId(), - playerEntity.getProfile().getName(), + playerEntity.getUuid(), + playerEntity.getUsername(), playerEntity.getGeyserId(), skin.getTextureUrl(), skin.getSkinData(), @@ -227,31 +226,31 @@ public class SkinManager { } /** - * Generate the GameProfileData from the given GameProfile + * Generate the GameProfileData from the given player entity * - * @param profile GameProfile to build the GameProfileData from + * @param entity entity to build the GameProfileData from * @return The built GameProfileData */ - public static GameProfileData from(GameProfile profile) { + public static GameProfileData from(PlayerEntity entity) { try { - GameProfile.Property skinProperty = profile.getProperty("textures"); + String texturesProperty = entity.getTexturesProperty(); - if (skinProperty == null) { + if (texturesProperty == null) { // Likely offline mode - return loadBedrockOrOfflineSkin(profile); + return loadBedrockOrOfflineSkin(entity); } - GameProfileData data = loadFromJson(skinProperty.getValue()); + GameProfileData data = loadFromJson(texturesProperty); if (data != null) { return data; } else { - return loadBedrockOrOfflineSkin(profile); + return loadBedrockOrOfflineSkin(entity); } } catch (IOException exception) { - GeyserImpl.getInstance().getLogger().debug("Something went wrong while processing skin for " + profile.getName()); + GeyserImpl.getInstance().getLogger().debug("Something went wrong while processing skin for " + entity.getUsername()); if (GeyserImpl.getInstance().getConfig().isDebugMode()) { exception.printStackTrace(); } - return loadBedrockOrOfflineSkin(profile); + return loadBedrockOrOfflineSkin(entity); } } @@ -280,14 +279,15 @@ public class SkinManager { * @return default skin with default cape when texture data is invalid, or the Bedrock player's skin if this * is a Bedrock player. */ - private static GameProfileData loadBedrockOrOfflineSkin(GameProfile profile) { + private static GameProfileData loadBedrockOrOfflineSkin(PlayerEntity entity) { // Fallback to the offline mode of working it out - boolean isAlex = (Math.abs(profile.getId().hashCode() % 2) == 1); + UUID uuid = entity.getUuid(); + boolean isAlex = (Math.abs(uuid.hashCode() % 2) == 1); String skinUrl = isAlex ? SkinProvider.EMPTY_SKIN_ALEX.getTextureUrl() : SkinProvider.EMPTY_SKIN.getTextureUrl(); String capeUrl = SkinProvider.EMPTY_CAPE.getTextureUrl(); if (("steve".equals(skinUrl) || "alex".equals(skinUrl)) && GeyserImpl.getInstance().getConfig().getRemote().getAuthType() != AuthType.ONLINE) { - GeyserSession session = GeyserImpl.getInstance().connectionByUuid(profile.getId()); + GeyserSession session = GeyserImpl.getInstance().connectionByUuid(uuid); if (session != null) { skinUrl = session.getClientData().getSkinId(); diff --git a/core/src/main/java/org/geysermc/geyser/skin/SkinProvider.java b/core/src/main/java/org/geysermc/geyser/skin/SkinProvider.java index 282f6875a..43cf30b47 100644 --- a/core/src/main/java/org/geysermc/geyser/skin/SkinProvider.java +++ b/core/src/main/java/org/geysermc/geyser/skin/SkinProvider.java @@ -27,7 +27,6 @@ package org.geysermc.geyser.skin; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; -import com.github.steveice10.mc.auth.data.GameProfile; import com.github.steveice10.opennbt.tag.builtin.CompoundTag; import com.github.steveice10.opennbt.tag.builtin.IntArrayTag; import com.github.steveice10.opennbt.tag.builtin.Tag; @@ -53,7 +52,6 @@ import java.io.IOException; import java.net.HttpURLConnection; import java.net.URL; import java.nio.charset.StandardCharsets; -import java.util.List; import java.util.*; import java.util.concurrent.*; import java.util.function.Predicate; @@ -157,7 +155,7 @@ public class SkinProvider { } public static CompletableFuture requestSkinData(PlayerEntity entity) { - SkinManager.GameProfileData data = SkinManager.GameProfileData.from(entity.getProfile()); + SkinManager.GameProfileData data = SkinManager.GameProfileData.from(entity); return requestSkinAndCape(entity.getUuid(), data.skinUrl(), data.capeUrl()) .thenApplyAsync(skinAndCape -> { @@ -546,12 +544,11 @@ public class SkinProvider { * @param skullOwner the CompoundTag of the skull with no textures * @return a completable GameProfile with textures included */ - public static CompletableFuture requestTexturesFromUsername(CompoundTag skullOwner) { + public static CompletableFuture requestTexturesFromUsername(CompoundTag skullOwner) { return CompletableFuture.supplyAsync(() -> { Tag uuidTag = skullOwner.get("Id"); String uuidToString = ""; JsonNode node; - GameProfile gameProfile = new GameProfile(UUID.randomUUID(), ""); boolean retrieveUuidFromInternet = !(uuidTag instanceof IntArrayTag); // also covers null check if (!retrieveUuidFromInternet) { @@ -577,15 +574,12 @@ public class SkinProvider { // Get textures from UUID node = WebUtils.getJson("https://sessionserver.mojang.com/session/minecraft/profile/" + uuidToString); - List profileProperties = new ArrayList<>(); JsonNode properties = node.get("properties"); if (properties == null) { GeyserImpl.getInstance().getLogger().debug("No properties found in Mojang response for " + uuidToString); return null; } - profileProperties.add(new GameProfile.Property("textures", node.get("properties").get(0).get("value").asText())); - gameProfile.setProperties(profileProperties); - return gameProfile; + return node.get("properties").get(0).get("value").asText(); } catch (Exception e) { if (GeyserImpl.getInstance().getConfig().isDebugMode()) { e.printStackTrace(); diff --git a/core/src/main/java/org/geysermc/geyser/skin/SkullSkinManager.java b/core/src/main/java/org/geysermc/geyser/skin/SkullSkinManager.java index 009d98c87..58054e9c5 100644 --- a/core/src/main/java/org/geysermc/geyser/skin/SkullSkinManager.java +++ b/core/src/main/java/org/geysermc/geyser/skin/SkullSkinManager.java @@ -50,7 +50,7 @@ public class SkullSkinManager extends SkinManager { public static void requestAndHandleSkin(PlayerEntity entity, GeyserSession session, Consumer skinConsumer) { - GameProfileData data = GameProfileData.from(entity.getProfile()); + GameProfileData data = GameProfileData.from(entity); SkinProvider.requestSkin(entity.getUuid(), data.skinUrl(), true) .whenCompleteAsync((skin, throwable) -> { diff --git a/core/src/main/java/org/geysermc/geyser/translator/level/block/entity/SkullBlockEntityTranslator.java b/core/src/main/java/org/geysermc/geyser/translator/level/block/entity/SkullBlockEntityTranslator.java index 8fc8732f7..50d79c10f 100644 --- a/core/src/main/java/org/geysermc/geyser/translator/level/block/entity/SkullBlockEntityTranslator.java +++ b/core/src/main/java/org/geysermc/geyser/translator/level/block/entity/SkullBlockEntityTranslator.java @@ -25,7 +25,6 @@ package org.geysermc.geyser.translator.level.block.entity; -import com.github.steveice10.mc.auth.data.GameProfile; import com.github.steveice10.mc.protocol.data.game.level.block.BlockEntityType; import com.github.steveice10.opennbt.tag.builtin.CompoundTag; import com.github.steveice10.opennbt.tag.builtin.ListTag; @@ -35,15 +34,12 @@ import com.nukkitx.math.vector.Vector3i; import com.nukkitx.nbt.NbtMapBuilder; import com.nukkitx.protocol.bedrock.data.entity.EntityFlag; import org.geysermc.geyser.entity.type.player.SkullPlayerEntity; -import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.level.block.BlockStateValues; +import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.skin.SkinProvider; import org.geysermc.geyser.skin.SkullSkinManager; -import java.util.ArrayList; import java.util.LinkedHashMap; -import java.util.List; -import java.util.UUID; import java.util.concurrent.CompletableFuture; import java.util.concurrent.TimeUnit; @@ -62,7 +58,7 @@ public class SkullBlockEntityTranslator extends BlockEntityTranslator implements builder.put("SkullType", skullVariant); } - public static CompletableFuture getProfile(CompoundTag tag) { + private static CompletableFuture getTextures(CompoundTag tag) { CompoundTag owner = tag.get("SkullOwner"); if (owner != null) { CompoundTag properties = owner.get("Properties"); @@ -73,13 +69,7 @@ public class SkullBlockEntityTranslator extends BlockEntityTranslator implements ListTag textures = properties.get("textures"); LinkedHashMap tag1 = (LinkedHashMap) textures.get(0).getValue(); StringTag texture = (StringTag) tag1.get("Value"); - - List profileProperties = new ArrayList<>(); - - GameProfile gameProfile = new GameProfile(UUID.randomUUID(), ""); - profileProperties.add(new GameProfile.Property("textures", texture.getValue())); - gameProfile.setProperties(profileProperties); - return CompletableFuture.completedFuture(gameProfile); + return CompletableFuture.completedFuture(texture.getValue()); } return CompletableFuture.completedFuture(null); } @@ -108,21 +98,21 @@ public class SkullBlockEntityTranslator extends BlockEntityTranslator implements Vector3i blockPosition = Vector3i.from(posX, posY, posZ); Vector3f entityPosition = Vector3f.from(x, y, z); - getProfile(tag).whenComplete((gameProfile, throwable) -> { - if (gameProfile == null) { + getTextures(tag).whenComplete((texturesProperty, throwable) -> { + if (texturesProperty == null) { session.getGeyser().getLogger().debug("Custom skull with invalid SkullOwner tag: " + blockPosition + " " + tag); return; } if (session.getEventLoop().inEventLoop()) { - spawnPlayer(session, gameProfile, blockPosition, entityPosition, rotation, blockState); + spawnPlayer(session, texturesProperty, blockPosition, entityPosition, rotation, blockState); } else { - session.executeInEventLoop(() -> spawnPlayer(session, gameProfile, blockPosition, entityPosition, rotation, blockState)); + session.executeInEventLoop(() -> spawnPlayer(session, texturesProperty, blockPosition, entityPosition, rotation, blockState)); } }); } - private static void spawnPlayer(GeyserSession session, GameProfile profile, Vector3i blockPosition, + private static void spawnPlayer(GeyserSession session, String texturesProperty, Vector3i blockPosition, Vector3f entityPosition, float rotation, int blockState) { long geyserId = session.getEntityCache().getNextEntityId().incrementAndGet(); @@ -132,7 +122,7 @@ public class SkullBlockEntityTranslator extends BlockEntityTranslator implements existingSkull.despawnEntity(blockPosition); } - SkullPlayerEntity player = new SkullPlayerEntity(session, geyserId, profile, entityPosition, rotation, blockState); + SkullPlayerEntity player = new SkullPlayerEntity(session, geyserId, entityPosition, rotation, blockState, texturesProperty); // Cache entity session.getSkullCache().put(blockPosition, player); diff --git a/core/src/main/java/org/geysermc/geyser/translator/protocol/java/entity/player/JavaPlayerInfoTranslator.java b/core/src/main/java/org/geysermc/geyser/translator/protocol/java/entity/player/JavaPlayerInfoTranslator.java index fd9e5887d..993da7746 100644 --- a/core/src/main/java/org/geysermc/geyser/translator/protocol/java/entity/player/JavaPlayerInfoTranslator.java +++ b/core/src/main/java/org/geysermc/geyser/translator/protocol/java/entity/player/JavaPlayerInfoTranslator.java @@ -25,6 +25,7 @@ package org.geysermc.geyser.translator.protocol.java.entity.player; +import com.github.steveice10.mc.auth.data.GameProfile; import com.github.steveice10.mc.protocol.data.game.PlayerListEntry; import com.github.steveice10.mc.protocol.data.game.PlayerListEntryAction; import com.github.steveice10.mc.protocol.packet.ingame.clientbound.ClientboundPlayerInfoPacket; @@ -50,31 +51,38 @@ public class JavaPlayerInfoTranslator extends PacketTranslator { + GameProfile profile = entry.getProfile(); PlayerEntity playerEntity; - boolean self = entry.getProfile().getId().equals(session.getPlayerEntity().getUuid()); + boolean self = profile.getId().equals(session.getPlayerEntity().getUuid()); if (self) { // Entity is ourself playerEntity = session.getPlayerEntity(); } else { - playerEntity = session.getEntityCache().getPlayerEntity(entry.getProfile().getId()); + playerEntity = session.getEntityCache().getPlayerEntity(profile.getId()); } + GameProfile.Property textures = profile.getProperty("textures"); + String texturesProperty = textures == null ? null : textures.getValue(); + if (playerEntity == null) { // It's a new player playerEntity = new PlayerEntity( session, -1, session.getEntityCache().getNextEntityId().incrementAndGet(), - entry.getProfile(), + profile.getId(), Vector3f.ZERO, Vector3f.ZERO, - 0, 0, 0 + 0, 0, 0, + profile.getName(), + texturesProperty ); session.getEntityCache().addPlayerEntity(playerEntity); } else { - playerEntity.setProfile(entry.getProfile()); + playerEntity.setUsername(profile.getName()); + playerEntity.setTexturesProperty(texturesProperty); } playerEntity.setPlayerList(true); diff --git a/core/src/main/java/org/geysermc/geyser/translator/protocol/java/entity/spawn/JavaAddPlayerTranslator.java b/core/src/main/java/org/geysermc/geyser/translator/protocol/java/entity/spawn/JavaAddPlayerTranslator.java index f0b1b4874..c54b75f4f 100644 --- a/core/src/main/java/org/geysermc/geyser/translator/protocol/java/entity/spawn/JavaAddPlayerTranslator.java +++ b/core/src/main/java/org/geysermc/geyser/translator/protocol/java/entity/spawn/JavaAddPlayerTranslator.java @@ -48,7 +48,9 @@ public class JavaAddPlayerTranslator extends PacketTranslator Date: Fri, 1 Apr 2022 15:20:30 -0400 Subject: [PATCH 15/17] Be more resilient with different enchantment NBT types Fixes #2911 --- .../item/nbt/EnchantmentTranslator.java | 9 ++++----- .../java/org/geysermc/geyser/util/ItemUtils.java | 16 +++++++++++----- 2 files changed, 15 insertions(+), 10 deletions(-) diff --git a/core/src/main/java/org/geysermc/geyser/translator/inventory/item/nbt/EnchantmentTranslator.java b/core/src/main/java/org/geysermc/geyser/translator/inventory/item/nbt/EnchantmentTranslator.java index 155435c79..847a70a27 100644 --- a/core/src/main/java/org/geysermc/geyser/translator/inventory/item/nbt/EnchantmentTranslator.java +++ b/core/src/main/java/org/geysermc/geyser/translator/inventory/item/nbt/EnchantmentTranslator.java @@ -121,10 +121,6 @@ public class EnchantmentTranslator extends NbtItemStackTranslator { private CompoundTag remapEnchantment(CompoundTag tag) { - Tag javaEnchLvl = tag.get("lvl"); - if (!(javaEnchLvl instanceof ShortTag || javaEnchLvl instanceof IntTag)) - return null; - Tag javaEnchId = tag.get("id"); if (!(javaEnchId instanceof StringTag)) return null; @@ -135,9 +131,12 @@ public class EnchantmentTranslator extends NbtItemStackTranslator { return null; } + Tag javaEnchLvl = tag.get("lvl"); + CompoundTag bedrockTag = new CompoundTag(""); bedrockTag.put(new ShortTag("id", (short) enchantment.ordinal())); - bedrockTag.put(new ShortTag("lvl", ((Number) javaEnchLvl.getValue()).shortValue())); + // If the tag cannot parse, Java Edition 1.18.2 sets to 0 + bedrockTag.put(new ShortTag("lvl", javaEnchLvl != null && javaEnchLvl.getValue() instanceof Number lvl ? lvl.shortValue() : 0)); return bedrockTag; } diff --git a/core/src/main/java/org/geysermc/geyser/util/ItemUtils.java b/core/src/main/java/org/geysermc/geyser/util/ItemUtils.java index f05d702a0..37c4609fe 100644 --- a/core/src/main/java/org/geysermc/geyser/util/ItemUtils.java +++ b/core/src/main/java/org/geysermc/geyser/util/ItemUtils.java @@ -29,21 +29,27 @@ import com.github.steveice10.opennbt.tag.builtin.*; import it.unimi.dsi.fastutil.ints.Int2IntMap; import org.geysermc.geyser.session.GeyserSession; +import javax.annotation.Nullable; + public class ItemUtils { private static Int2IntMap DYE_COLORS = null; - public static int getEnchantmentLevel(CompoundTag itemNBTData, String enchantmentId) { - ListTag enchantments = (itemNBTData == null ? null : itemNBTData.get("Enchantments")); + public static int getEnchantmentLevel(@Nullable CompoundTag itemNBTData, String enchantmentId) { + if (itemNBTData == null) { + return 0; + } + ListTag enchantments = itemNBTData.get("Enchantments"); if (enchantments != null) { - int enchantmentLevel = 0; for (Tag tag : enchantments) { CompoundTag enchantment = (CompoundTag) tag; StringTag enchantId = enchantment.get("id"); if (enchantId.getValue().equals(enchantmentId)) { - enchantmentLevel = (int) ((ShortTag) enchantment.get("lvl")).getValue(); + Tag lvl = enchantment.get("lvl"); + if (lvl != null && lvl.getValue() instanceof Number number) { + return number.intValue(); + } } } - return enchantmentLevel; } return 0; } From d6cb5bd52db067f8b62725e7dea4f2e64b64c233 Mon Sep 17 00:00:00 2001 From: Camotoy <20743703+Camotoy@users.noreply.github.com> Date: Mon, 4 Apr 2022 14:08:35 -0400 Subject: [PATCH 16/17] ItemMappings: use array for ItemMapping class --- .../inventory/item/StoredItemMappings.java | 2 -- .../populator/ItemRegistryPopulator.java | 19 +++++++------ .../geyser/registry/type/ItemMappings.java | 28 ++++++++++++++----- .../inventory/item/CompassTranslator.java | 11 ++++---- .../inventory/item/PotionTranslator.java | 7 ++--- .../inventory/item/TippedArrowTranslator.java | 7 ++--- .../inventory/item/nbt/BannerTranslator.java | 11 ++------ ...BedrockInventoryTransactionTranslator.java | 2 +- .../java/JavaUpdateRecipesTranslator.java | 5 ++-- 9 files changed, 48 insertions(+), 44 deletions(-) diff --git a/core/src/main/java/org/geysermc/geyser/inventory/item/StoredItemMappings.java b/core/src/main/java/org/geysermc/geyser/inventory/item/StoredItemMappings.java index e4296c2d4..c787f87a1 100644 --- a/core/src/main/java/org/geysermc/geyser/inventory/item/StoredItemMappings.java +++ b/core/src/main/java/org/geysermc/geyser/inventory/item/StoredItemMappings.java @@ -52,7 +52,6 @@ public class StoredItemMappings { private final int goldIngot; private final int ironIngot; private final int lead; - private final ItemMapping lodestoneCompass; private final ItemMapping milkBucket; private final int nameTag; private final ItemMapping powderSnowBucket; @@ -80,7 +79,6 @@ public class StoredItemMappings { this.goldIngot = load(itemMappings, "gold_ingot").getJavaId(); this.ironIngot = load(itemMappings, "iron_ingot").getJavaId(); this.lead = load(itemMappings, "lead").getJavaId(); - this.lodestoneCompass = load(itemMappings, "lodestone_compass"); this.milkBucket = load(itemMappings, "milk_bucket"); this.nameTag = load(itemMappings, "name_tag").getJavaId(); this.powderSnowBucket = load(itemMappings, "powder_snow_bucket"); diff --git a/core/src/main/java/org/geysermc/geyser/registry/populator/ItemRegistryPopulator.java b/core/src/main/java/org/geysermc/geyser/registry/populator/ItemRegistryPopulator.java index 534c68776..0e12669e3 100644 --- a/core/src/main/java/org/geysermc/geyser/registry/populator/ItemRegistryPopulator.java +++ b/core/src/main/java/org/geysermc/geyser/registry/populator/ItemRegistryPopulator.java @@ -128,7 +128,7 @@ public class ItemRegistryPopulator { IntList spawnEggs = new IntArrayList(); List carpets = new ObjectArrayList<>(); - Int2ObjectMap mappings = new Int2ObjectOpenHashMap<>(); + List mappings = new ObjectArrayList<>(); // Temporary mapping to create stored items Map identifierToMapping = new Object2ObjectOpenHashMap<>(); @@ -243,6 +243,8 @@ public class ItemRegistryPopulator { if (usingFurnaceMinecart && javaIdentifier.equals("minecraft:furnace_minecart")) { javaFurnaceMinecartId = itemIndex; itemIndex++; + // Will be added later + mappings.add(null); continue; } @@ -419,7 +421,7 @@ public class ItemRegistryPopulator { spawnEggs.add(mapping.getBedrockId()); } - mappings.put(itemIndex, mapping); + mappings.add(mapping); identifierToMapping.put(javaIdentifier, mapping); itemNames.add(javaIdentifier); @@ -440,16 +442,14 @@ public class ItemRegistryPopulator { // Add the lodestone compass since it doesn't exist on java but we need it for item conversion ItemMapping lodestoneEntry = ItemMapping.builder() - .javaIdentifier("minecraft:lodestone_compass") + .javaIdentifier("") .bedrockIdentifier("minecraft:lodestone_compass") - .javaId(itemIndex) + .javaId(-1) .bedrockId(lodestoneCompassId) .bedrockData(0) .bedrockBlockId(-1) .stackSize(1) .build(); - mappings.put(itemIndex, lodestoneEntry); - identifierToMapping.put(lodestoneEntry.getJavaIdentifier(), lodestoneEntry); ComponentItemData furnaceMinecartData = null; if (usingFurnaceMinecart) { @@ -458,7 +458,7 @@ public class ItemRegistryPopulator { entries.put("geysermc:furnace_minecart", new StartGamePacket.ItemEntry("geysermc:furnace_minecart", (short) furnaceMinecartId, true)); - mappings.put(javaFurnaceMinecartId, ItemMapping.builder() + mappings.set(javaFurnaceMinecartId, ItemMapping.builder() .javaIdentifier("minecraft:furnace_minecart") .bedrockIdentifier("geysermc:furnace_minecart") .javaId(javaFurnaceMinecartId) @@ -509,9 +509,9 @@ public class ItemRegistryPopulator { } ItemMappings itemMappings = ItemMappings.builder() - .items(mappings) + .items(mappings.toArray(new ItemMapping[0])) .creativeItems(creativeItems.toArray(new ItemData[0])) - .itemEntries(new ArrayList<>(entries.values())) + .itemEntries(List.copyOf(entries.values())) .itemNames(itemNames.toArray(new String[0])) .storedItems(new StoredItemMappings(identifierToMapping)) .javaOnlyItems(javaOnlyItems) @@ -520,6 +520,7 @@ public class ItemRegistryPopulator { .spawnEggIds(spawnEggs) .carpets(carpets) .furnaceMinecartData(furnaceMinecartData) + .lodestoneCompass(lodestoneEntry) .build(); Registries.ITEMS.register(palette.getValue().protocolVersion(), itemMappings); diff --git a/core/src/main/java/org/geysermc/geyser/registry/type/ItemMappings.java b/core/src/main/java/org/geysermc/geyser/registry/type/ItemMappings.java index a4953b05b..3072568f3 100644 --- a/core/src/main/java/org/geysermc/geyser/registry/type/ItemMappings.java +++ b/core/src/main/java/org/geysermc/geyser/registry/type/ItemMappings.java @@ -29,13 +29,13 @@ import com.github.steveice10.mc.protocol.data.game.entity.metadata.ItemStack; import com.nukkitx.protocol.bedrock.data.inventory.ComponentItemData; import com.nukkitx.protocol.bedrock.data.inventory.ItemData; import com.nukkitx.protocol.bedrock.packet.StartGamePacket; -import it.unimi.dsi.fastutil.ints.Int2ObjectMap; import it.unimi.dsi.fastutil.ints.IntList; import lombok.Builder; import lombok.Value; import org.geysermc.geyser.GeyserImpl; import org.geysermc.geyser.inventory.item.StoredItemMappings; +import javax.annotation.Nonnull; import javax.annotation.Nullable; import java.util.List; import java.util.Map; @@ -48,7 +48,12 @@ public class ItemMappings { Map cachedJavaMappings = new WeakHashMap<>(); - Int2ObjectMap items; + ItemMapping[] items; + + /** + * A unique exception as this is an item in Bedrock, but not in Java. + */ + ItemMapping lodestoneCompass; ItemData[] creativeItems; List itemEntries; @@ -70,6 +75,7 @@ public class ItemMappings { * @param itemStack the itemstack * @return an item entry from the given java edition identifier */ + @Nonnull public ItemMapping getMapping(ItemStack itemStack) { return this.getMapping(itemStack.getId()); } @@ -81,8 +87,9 @@ public class ItemMappings { * @param javaId the id * @return an item entry from the given java edition identifier */ + @Nonnull public ItemMapping getMapping(int javaId) { - return this.items.get(javaId); + return javaId >= 0 && javaId < this.items.length ? this.items[javaId] : ItemMapping.AIR; } /** @@ -94,7 +101,7 @@ public class ItemMappings { */ public ItemMapping getMapping(String javaIdentifier) { return this.cachedJavaMappings.computeIfAbsent(javaIdentifier, key -> { - for (ItemMapping mapping : this.items.values()) { + for (ItemMapping mapping : this.items) { if (mapping.getJavaIdentifier().equals(key)) { return mapping; } @@ -110,11 +117,18 @@ public class ItemMappings { * @return an item entry from the given item data */ public ItemMapping getMapping(ItemData data) { + int id = data.getId(); + if (id == 0) { + return ItemMapping.AIR; + } else if (id == lodestoneCompass.getBedrockId()) { + return lodestoneCompass; + } + boolean isBlock = data.getBlockRuntimeId() != 0; boolean hasDamage = data.getDamage() != 0; - for (ItemMapping mapping : this.items.values()) { - if (mapping.getBedrockId() == data.getId()) { + for (ItemMapping mapping : this.items) { + if (mapping.getBedrockId() == id) { if (isBlock && !hasDamage) { // Pre-1.16.220 will not use block runtime IDs at all, so we shouldn't check either if (data.getBlockRuntimeId() != mapping.getBedrockBlockId()) { continue; @@ -135,7 +149,7 @@ public class ItemMappings { } // This will hide the message when the player clicks with an empty hand - if (data.getId() != 0 && data.getDamage() != 0) { + if (id != 0 && data.getDamage() != 0) { GeyserImpl.getInstance().getLogger().debug("Missing mapping for bedrock item " + data.getId() + ":" + data.getDamage()); } return ItemMapping.AIR; diff --git a/core/src/main/java/org/geysermc/geyser/translator/inventory/item/CompassTranslator.java b/core/src/main/java/org/geysermc/geyser/translator/inventory/item/CompassTranslator.java index b8ef85f81..4c2978082 100644 --- a/core/src/main/java/org/geysermc/geyser/translator/inventory/item/CompassTranslator.java +++ b/core/src/main/java/org/geysermc/geyser/translator/inventory/item/CompassTranslator.java @@ -35,6 +35,7 @@ import org.geysermc.geyser.registry.Registries; import org.geysermc.geyser.registry.type.ItemMapping; import org.geysermc.geyser.registry.type.ItemMappings; +import java.util.Arrays; import java.util.List; import java.util.stream.Collectors; @@ -45,7 +46,7 @@ public class CompassTranslator extends ItemTranslator { protected ItemData.Builder translateToBedrock(ItemStack itemStack, ItemMapping mapping, ItemMappings mappings) { if (isLodestoneCompass(itemStack.getNbt())) { // NBT will be translated in nbt/LodestoneCompassTranslator if applicable - return super.translateToBedrock(itemStack, mappings.getStoredItems().lodestoneCompass(), mappings); + return super.translateToBedrock(itemStack, mappings.getLodestoneCompass(), mappings); } return super.translateToBedrock(itemStack, mapping, mappings); } @@ -53,7 +54,7 @@ public class CompassTranslator extends ItemTranslator { @Override protected ItemMapping getItemMapping(int javaId, CompoundTag nbt, ItemMappings mappings) { if (isLodestoneCompass(nbt)) { - return mappings.getStoredItems().lodestoneCompass(); + return mappings.getLodestoneCompass(); } return super.getItemMapping(javaId, nbt, mappings); } @@ -78,10 +79,8 @@ public class CompassTranslator extends ItemTranslator { @Override public List getAppliedItems() { - return Registries.ITEMS.forVersion(MinecraftProtocol.DEFAULT_BEDROCK_CODEC.getProtocolVersion()) - .getItems() - .values() - .stream() + return Arrays.stream(Registries.ITEMS.forVersion(MinecraftProtocol.DEFAULT_BEDROCK_CODEC.getProtocolVersion()) + .getItems()) .filter(entry -> entry.getJavaIdentifier().endsWith("compass")) .collect(Collectors.toList()); } diff --git a/core/src/main/java/org/geysermc/geyser/translator/inventory/item/PotionTranslator.java b/core/src/main/java/org/geysermc/geyser/translator/inventory/item/PotionTranslator.java index 04183e095..bf16af38f 100644 --- a/core/src/main/java/org/geysermc/geyser/translator/inventory/item/PotionTranslator.java +++ b/core/src/main/java/org/geysermc/geyser/translator/inventory/item/PotionTranslator.java @@ -36,6 +36,7 @@ import org.geysermc.geyser.registry.Registries; import org.geysermc.geyser.registry.type.ItemMapping; import org.geysermc.geyser.registry.type.ItemMappings; +import java.util.Arrays; import java.util.List; import java.util.stream.Collectors; @@ -73,10 +74,8 @@ public class PotionTranslator extends ItemTranslator { @Override public List getAppliedItems() { - return Registries.ITEMS.forVersion(MinecraftProtocol.DEFAULT_BEDROCK_CODEC.getProtocolVersion()) - .getItems() - .values() - .stream() + return Arrays.stream(Registries.ITEMS.forVersion(MinecraftProtocol.DEFAULT_BEDROCK_CODEC.getProtocolVersion()) + .getItems()) .filter(entry -> entry.getJavaIdentifier().endsWith("potion")) .collect(Collectors.toList()); } diff --git a/core/src/main/java/org/geysermc/geyser/translator/inventory/item/TippedArrowTranslator.java b/core/src/main/java/org/geysermc/geyser/translator/inventory/item/TippedArrowTranslator.java index b3aecb668..d831ce586 100644 --- a/core/src/main/java/org/geysermc/geyser/translator/inventory/item/TippedArrowTranslator.java +++ b/core/src/main/java/org/geysermc/geyser/translator/inventory/item/TippedArrowTranslator.java @@ -36,6 +36,7 @@ import org.geysermc.geyser.registry.Registries; import org.geysermc.geyser.registry.type.ItemMapping; import org.geysermc.geyser.registry.type.ItemMappings; +import java.util.Arrays; import java.util.List; import java.util.stream.Collectors; @@ -80,10 +81,8 @@ public class TippedArrowTranslator extends ItemTranslator { @Override public List getAppliedItems() { - return Registries.ITEMS.forVersion(MinecraftProtocol.DEFAULT_BEDROCK_CODEC.getProtocolVersion()) - .getItems() - .values() - .stream() + return Arrays.stream(Registries.ITEMS.forVersion(MinecraftProtocol.DEFAULT_BEDROCK_CODEC.getProtocolVersion()) + .getItems()) .filter(entry -> entry.getJavaIdentifier().contains("arrow") && !entry.getJavaIdentifier().contains("spectral")) .collect(Collectors.toList()); diff --git a/core/src/main/java/org/geysermc/geyser/translator/inventory/item/nbt/BannerTranslator.java b/core/src/main/java/org/geysermc/geyser/translator/inventory/item/nbt/BannerTranslator.java index 3da157cfc..ed4865411 100644 --- a/core/src/main/java/org/geysermc/geyser/translator/inventory/item/nbt/BannerTranslator.java +++ b/core/src/main/java/org/geysermc/geyser/translator/inventory/item/nbt/BannerTranslator.java @@ -37,10 +37,7 @@ import org.geysermc.geyser.translator.inventory.item.ItemRemapper; import org.geysermc.geyser.translator.inventory.item.NbtItemStackTranslator; import javax.annotation.Nonnull; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; +import java.util.*; import java.util.stream.Collectors; @ItemRemapper @@ -79,10 +76,8 @@ public class BannerTranslator extends NbtItemStackTranslator { } public BannerTranslator() { - appliedItems = Registries.ITEMS.forVersion(MinecraftProtocol.DEFAULT_BEDROCK_CODEC.getProtocolVersion()) - .getItems() - .values() - .stream() + appliedItems = Arrays.stream(Registries.ITEMS.forVersion(MinecraftProtocol.DEFAULT_BEDROCK_CODEC.getProtocolVersion()) + .getItems()) .filter(entry -> entry.getJavaIdentifier().endsWith("banner")) .collect(Collectors.toList()); } diff --git a/core/src/main/java/org/geysermc/geyser/translator/protocol/bedrock/BedrockInventoryTransactionTranslator.java b/core/src/main/java/org/geysermc/geyser/translator/protocol/bedrock/BedrockInventoryTransactionTranslator.java index f120e4a19..073917293 100644 --- a/core/src/main/java/org/geysermc/geyser/translator/protocol/bedrock/BedrockInventoryTransactionTranslator.java +++ b/core/src/main/java/org/geysermc/geyser/translator/protocol/bedrock/BedrockInventoryTransactionTranslator.java @@ -284,7 +284,7 @@ public class BedrockInventoryTransactionTranslator extends PacketTranslator - session.getItemMappings().getItems() - .getOrDefault(stoneCuttingRecipeData.getResult().getId(), ItemMapping.AIR) + session.getItemMappings().getMapping(stoneCuttingRecipeData.getResult()) .getJavaIdentifier()))); // Now that it's sorted, let's translate these recipes @@ -229,7 +228,7 @@ public class JavaUpdateRecipesTranslator extends PacketTranslator Date: Mon, 4 Apr 2022 21:03:43 -0400 Subject: [PATCH 17/17] Add recent Paper check and new vanilla check for block placement Fixes #2917 --- ...BedrockInventoryTransactionTranslator.java | 43 +++++++++++++++---- 1 file changed, 35 insertions(+), 8 deletions(-) diff --git a/core/src/main/java/org/geysermc/geyser/translator/protocol/bedrock/BedrockInventoryTransactionTranslator.java b/core/src/main/java/org/geysermc/geyser/translator/protocol/bedrock/BedrockInventoryTransactionTranslator.java index 073917293..a5787c1c6 100644 --- a/core/src/main/java/org/geysermc/geyser/translator/protocol/bedrock/BedrockInventoryTransactionTranslator.java +++ b/core/src/main/java/org/geysermc/geyser/translator/protocol/bedrock/BedrockInventoryTransactionTranslator.java @@ -139,7 +139,8 @@ public class BedrockInventoryTransactionTranslator extends PacketTranslator { - Vector3i blockPos = BlockUtils.getBlockPosition(packet.getBlockPosition(), packet.getBlockFace()); + final Vector3i packetBlockPosition = packet.getBlockPosition(); + Vector3i blockPos = BlockUtils.getBlockPosition(packetBlockPosition, packet.getBlockFace()); if (session.getGeyser().getConfig().isDisableBedrockScaffolding()) { float yaw = session.getPlayerEntity().getYaw(); @@ -159,8 +160,8 @@ public class BedrockInventoryTransactionTranslator extends PacketTranslator - (session.getGameMode().equals(GameMode.CREATIVE) ? CREATIVE_EYE_HEIGHT_PLACE_DISTANCE : SURVIVAL_EYE_HEIGHT_PLACE_DISTANCE)) { + (creative ? CREATIVE_EYE_HEIGHT_PLACE_DISTANCE : SURVIVAL_EYE_HEIGHT_PLACE_DISTANCE)) { restoreCorrectBlock(session, blockPos, packet); return; } + double clickPositionFullX = (double) packetBlockPosition.getX() + (double) packet.getClickPosition().getX(); + double clickPositionFullY = (double) packetBlockPosition.getY() + (double) packet.getClickPosition().getY(); + double clickPositionFullZ = (double) packetBlockPosition.getZ() + (double) packet.getClickPosition().getZ(); + + // More recent Paper check - https://github.com/PaperMC/Paper/blob/87e11bf7fdf48ecdf3e1cae383c368b9b61d7df9/patches/server/0470-Move-range-check-for-block-placing-up.patch + double clickDiffX = playerPosition.getX() - clickPositionFullX; + double clickDiffY = playerPosition.getY() - clickPositionFullY; + double clickDiffZ = playerPosition.getZ() - clickPositionFullZ; + if (((clickDiffX * clickDiffX) + (clickDiffY * clickDiffY) + (clickDiffZ * clickDiffZ)) > + (creative ? CREATIVE_EYE_HEIGHT_PLACE_DISTANCE : SURVIVAL_EYE_HEIGHT_PLACE_DISTANCE)) { + restoreCorrectBlock(session, blockPos, packet); + return; + } + + Vector3f blockCenter = Vector3f.from(packetBlockPosition.getX() + 0.5f, packetBlockPosition.getY() + 0.5f, packetBlockPosition.getZ() + 0.5f); // Vanilla check if (!(session.getPlayerEntity().getPosition().sub(0, EntityDefinitions.PLAYER.offset(), 0) - .distanceSquared(packet.getBlockPosition().toFloat().add(0.5f, 0.5f, 0.5f)) < MAXIMUM_BLOCK_PLACING_DISTANCE)) { + .distanceSquared(blockCenter) < MAXIMUM_BLOCK_PLACING_DISTANCE)) { // The client thinks that its blocks have been successfully placed. Restore the server's blocks instead. restoreCorrectBlock(session, blockPos, packet); return; } + + // More recent vanilla check (as of 1.18.2) + double clickDistanceX = clickPositionFullX - blockCenter.getX(); + double clickDistanceY = clickPositionFullY - blockCenter.getY(); + double clickDistanceZ = clickPositionFullZ - blockCenter.getZ(); + if (!(Math.abs(clickDistanceX) < 1.0000001D && Math.abs(clickDistanceY) < 1.0000001D && Math.abs(clickDistanceZ) < 1.0000001D)) { + restoreCorrectBlock(session, blockPos, packet); + return; + } /* Block place checks end - client is good to go */