From 6a6a601efc3a73281210842e7f9a39fc8ef21db2 Mon Sep 17 00:00:00 2001 From: Camotoy <20743703+Camotoy@users.noreply.github.com> Date: Wed, 15 Feb 2023 00:01:33 -0500 Subject: [PATCH 01/59] Notify on disconnect when a new Geyser update is available --- .../geyser/network/UpstreamPacketHandler.java | 12 +++++++++++- .../org/geysermc/geyser/util/VersionCheckUtils.java | 9 +++++++++ core/src/main/resources/languages | 2 +- 3 files changed, 21 insertions(+), 2 deletions(-) diff --git a/core/src/main/java/org/geysermc/geyser/network/UpstreamPacketHandler.java b/core/src/main/java/org/geysermc/geyser/network/UpstreamPacketHandler.java index 227f0ed5a..a72d8ab9e 100644 --- a/core/src/main/java/org/geysermc/geyser/network/UpstreamPacketHandler.java +++ b/core/src/main/java/org/geysermc/geyser/network/UpstreamPacketHandler.java @@ -31,6 +31,7 @@ import com.nukkitx.protocol.bedrock.data.ExperimentData; import com.nukkitx.protocol.bedrock.data.PacketCompressionAlgorithm; import com.nukkitx.protocol.bedrock.data.ResourcePackType; import com.nukkitx.protocol.bedrock.packet.*; +import org.geysermc.geyser.Constants; import org.geysermc.geyser.GeyserImpl; import org.geysermc.geyser.api.network.AuthType; import org.geysermc.geyser.configuration.GeyserConfiguration; @@ -43,11 +44,13 @@ import org.geysermc.geyser.session.PendingMicrosoftAuthentication; import org.geysermc.geyser.text.GeyserLocale; import org.geysermc.geyser.util.LoginEncryptionUtils; import org.geysermc.geyser.util.MathUtils; +import org.geysermc.geyser.util.VersionCheckUtils; import java.io.FileInputStream; import java.io.InputStream; import java.util.ArrayDeque; import java.util.Deque; +import java.util.OptionalInt; public class UpstreamPacketHandler extends LoggingPacketHandler { @@ -74,7 +77,14 @@ public class UpstreamPacketHandler extends LoggingPacketHandler { String supportedVersions = GameProtocol.getAllSupportedBedrockVersions(); if (protocolVersion > GameProtocol.DEFAULT_BEDROCK_CODEC.getProtocolVersion()) { // Too early to determine session locale - session.disconnect(GeyserLocale.getLocaleStringLog("geyser.network.outdated.server", supportedVersions)); + String disconnectMessage = GeyserLocale.getLocaleStringLog("geyser.network.outdated.server", supportedVersions); + // If the latest release matches this version, then let the user know. + OptionalInt latestRelease = VersionCheckUtils.getLatestBedrockRelease(); + if (latestRelease.isPresent() && latestRelease.getAsInt() == protocolVersion) { + // Random note: don't make the disconnect message too long or Bedrock will cut it off on smaller screens + disconnectMessage += "\n" + GeyserLocale.getLocaleStringLog("geyser.version.new.on_disconnect", Constants.GEYSER_DOWNLOAD_LOCATION); + } + session.disconnect(disconnectMessage); return false; } else if (protocolVersion < GameProtocol.DEFAULT_BEDROCK_CODEC.getProtocolVersion()) { session.disconnect(GeyserLocale.getLocaleStringLog("geyser.network.outdated.client", supportedVersions)); diff --git a/core/src/main/java/org/geysermc/geyser/util/VersionCheckUtils.java b/core/src/main/java/org/geysermc/geyser/util/VersionCheckUtils.java index 049d78619..dc0edd37a 100644 --- a/core/src/main/java/org/geysermc/geyser/util/VersionCheckUtils.java +++ b/core/src/main/java/org/geysermc/geyser/util/VersionCheckUtils.java @@ -38,10 +38,13 @@ import org.geysermc.geyser.command.GeyserCommandSource; import org.geysermc.geyser.network.GameProtocol; import org.geysermc.geyser.text.GeyserLocale; +import javax.annotation.Nonnull; +import java.util.OptionalInt; import java.util.concurrent.CompletableFuture; import java.util.function.Supplier; public final class VersionCheckUtils { + private static @Nonnull OptionalInt LATEST_BEDROCK_RELEASE = OptionalInt.empty(); public static void checkForOutdatedFloodgate(GeyserLogger logger) { try { @@ -61,10 +64,12 @@ public final class VersionCheckUtils { JsonNode bedrock = json.get("bedrock").get("protocol"); int protocolVersion = bedrock.get("id").asInt(); if (GameProtocol.getBedrockCodec(protocolVersion) != null) { + LATEST_BEDROCK_RELEASE = OptionalInt.empty(); // We support the latest version! No need to print a message. return; } + LATEST_BEDROCK_RELEASE = OptionalInt.of(protocolVersion); final String newBedrockVersion = bedrock.get("name").asText(); // Delayed for two reasons: save unnecessary processing, and wait to load locale if this is on join. @@ -89,6 +94,10 @@ public final class VersionCheckUtils { }); } + public static @Nonnull OptionalInt getLatestBedrockRelease() { + return LATEST_BEDROCK_RELEASE; + } + private VersionCheckUtils() { } } diff --git a/core/src/main/resources/languages b/core/src/main/resources/languages index f6685c4cc..24be9ef7f 160000 --- a/core/src/main/resources/languages +++ b/core/src/main/resources/languages @@ -1 +1 @@ -Subproject commit f6685c4ccc6e77b07402d45cb41213559004b7d6 +Subproject commit 24be9ef7f850f7d180650a65792c266c709cadf5 From ee754c529ba1072d07cb684336204b5251661ff1 Mon Sep 17 00:00:00 2001 From: Camotoy <20743703+Camotoy@users.noreply.github.com> Date: Wed, 15 Feb 2023 00:17:14 -0500 Subject: [PATCH 02/59] Add implementation for ClientEmoteEvent Also, a few random changes I've stored since forever. --- ...rockEmoteEvent.java => ClientEmoteEvent.java} | 4 ++-- .../geyser/command/defaults/OffhandCommand.java | 8 +------- .../geyser/item/components/ToolTier.java | 2 +- .../geyser/level/GeyserWorldManager.java | 8 +------- .../geyser/level/physics/PistonBehavior.java | 2 +- .../geyser/registry/type/ParticleMapping.java | 3 ++- .../geysermc/geyser/session/GeyserSession.java | 8 +++++++- .../geyser/session/cache/LodestoneCache.java | 3 +-- .../inventory/EnchantingInventoryTranslator.java | 3 ++- .../entity/player/BedrockEmoteTranslator.java | 16 +++++++++------- 10 files changed, 27 insertions(+), 30 deletions(-) rename api/src/main/java/org/geysermc/geyser/api/event/bedrock/{BedrockEmoteEvent.java => ClientEmoteEvent.java} (92%) diff --git a/api/src/main/java/org/geysermc/geyser/api/event/bedrock/BedrockEmoteEvent.java b/api/src/main/java/org/geysermc/geyser/api/event/bedrock/ClientEmoteEvent.java similarity index 92% rename from api/src/main/java/org/geysermc/geyser/api/event/bedrock/BedrockEmoteEvent.java rename to api/src/main/java/org/geysermc/geyser/api/event/bedrock/ClientEmoteEvent.java index efe3f12d6..35b6a9e73 100644 --- a/api/src/main/java/org/geysermc/geyser/api/event/bedrock/BedrockEmoteEvent.java +++ b/api/src/main/java/org/geysermc/geyser/api/event/bedrock/ClientEmoteEvent.java @@ -33,11 +33,11 @@ import org.geysermc.geyser.api.event.connection.ConnectionEvent; /** * Called whenever a Bedrock player performs an emote on their end, before it is broadcasted to the rest of the server. */ -public final class BedrockEmoteEvent extends ConnectionEvent implements Cancellable { +public final class ClientEmoteEvent extends ConnectionEvent implements Cancellable { private final String emoteId; private boolean cancelled; - public BedrockEmoteEvent(@NonNull GeyserConnection connection, @NonNull String emoteId) { + public ClientEmoteEvent(@NonNull GeyserConnection connection, @NonNull String emoteId) { super(connection); this.emoteId = emoteId; } diff --git a/core/src/main/java/org/geysermc/geyser/command/defaults/OffhandCommand.java b/core/src/main/java/org/geysermc/geyser/command/defaults/OffhandCommand.java index 0015149be..6188e6924 100644 --- a/core/src/main/java/org/geysermc/geyser/command/defaults/OffhandCommand.java +++ b/core/src/main/java/org/geysermc/geyser/command/defaults/OffhandCommand.java @@ -25,10 +25,6 @@ package org.geysermc.geyser.command.defaults; -import com.github.steveice10.mc.protocol.data.game.entity.object.Direction; -import com.github.steveice10.mc.protocol.data.game.entity.player.PlayerAction; -import com.github.steveice10.mc.protocol.packet.ingame.serverbound.player.ServerboundPlayerActionPacket; -import com.nukkitx.math.vector.Vector3i; import org.geysermc.geyser.GeyserImpl; import org.geysermc.geyser.command.GeyserCommand; import org.geysermc.geyser.command.GeyserCommandSource; @@ -46,9 +42,7 @@ public class OffhandCommand extends GeyserCommand { return; } - ServerboundPlayerActionPacket releaseItemPacket = new ServerboundPlayerActionPacket(PlayerAction.SWAP_HANDS, Vector3i.ZERO, - Direction.DOWN, 0); - session.sendDownstreamPacket(releaseItemPacket); + session.requestOffhandSwap(); } @Override diff --git a/core/src/main/java/org/geysermc/geyser/item/components/ToolTier.java b/core/src/main/java/org/geysermc/geyser/item/components/ToolTier.java index 37e581682..724b197fb 100644 --- a/core/src/main/java/org/geysermc/geyser/item/components/ToolTier.java +++ b/core/src/main/java/org/geysermc/geyser/item/components/ToolTier.java @@ -37,7 +37,7 @@ public enum ToolTier { DIAMOND(8), NETHERITE(9); - public static final ToolTier[] VALUES = values(); + private static final ToolTier[] VALUES = values(); private final int speed; diff --git a/core/src/main/java/org/geysermc/geyser/level/GeyserWorldManager.java b/core/src/main/java/org/geysermc/geyser/level/GeyserWorldManager.java index f19060c65..5df97e8d7 100644 --- a/core/src/main/java/org/geysermc/geyser/level/GeyserWorldManager.java +++ b/core/src/main/java/org/geysermc/geyser/level/GeyserWorldManager.java @@ -29,9 +29,7 @@ import com.nukkitx.nbt.NbtMap; import com.nukkitx.nbt.NbtMapBuilder; import it.unimi.dsi.fastutil.objects.Object2ObjectMap; import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap; -import org.geysermc.geyser.level.block.BlockStateValues; import org.geysermc.geyser.session.GeyserSession; -import org.geysermc.geyser.session.cache.ChunkCache; import org.geysermc.geyser.translator.inventory.LecternInventoryTranslator; public class GeyserWorldManager extends WorldManager { @@ -39,11 +37,7 @@ public class GeyserWorldManager extends WorldManager { @Override public int getBlockAt(GeyserSession session, int x, int y, int z) { - ChunkCache chunkCache = session.getChunkCache(); - if (chunkCache != null) { // Chunk cache can be null if the session is closed asynchronously - return chunkCache.getBlockAt(x, y, z); - } - return BlockStateValues.JAVA_AIR_ID; + return session.getChunkCache().getBlockAt(x, y, z); } @Override diff --git a/core/src/main/java/org/geysermc/geyser/level/physics/PistonBehavior.java b/core/src/main/java/org/geysermc/geyser/level/physics/PistonBehavior.java index bf943134b..a6b25d01e 100644 --- a/core/src/main/java/org/geysermc/geyser/level/physics/PistonBehavior.java +++ b/core/src/main/java/org/geysermc/geyser/level/physics/PistonBehavior.java @@ -33,7 +33,7 @@ public enum PistonBehavior { DESTROY, PUSH_ONLY; - public static final PistonBehavior[] VALUES = values(); + private static final PistonBehavior[] VALUES = values(); public static PistonBehavior getByName(String name) { String upperCase = name.toUpperCase(Locale.ROOT); diff --git a/core/src/main/java/org/geysermc/geyser/registry/type/ParticleMapping.java b/core/src/main/java/org/geysermc/geyser/registry/type/ParticleMapping.java index 71feee3de..dcafcd0bf 100644 --- a/core/src/main/java/org/geysermc/geyser/registry/type/ParticleMapping.java +++ b/core/src/main/java/org/geysermc/geyser/registry/type/ParticleMapping.java @@ -28,12 +28,13 @@ package org.geysermc.geyser.registry.type; import com.nukkitx.protocol.bedrock.data.LevelEventType; import org.geysermc.geyser.session.GeyserSession; +import javax.annotation.Nonnull; import javax.annotation.ParametersAreNullableByDefault; @ParametersAreNullableByDefault public record ParticleMapping(LevelEventType levelEventType, String identifier) { - public int getParticleId(GeyserSession session) { + public int getParticleId(@Nonnull GeyserSession session) { if (this.levelEventType == null) { return -1; } diff --git a/core/src/main/java/org/geysermc/geyser/session/GeyserSession.java b/core/src/main/java/org/geysermc/geyser/session/GeyserSession.java index 33655beda..bc49e8945 100644 --- a/core/src/main/java/org/geysermc/geyser/session/GeyserSession.java +++ b/core/src/main/java/org/geysermc/geyser/session/GeyserSession.java @@ -180,7 +180,7 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource { private final AdvancementsCache advancementsCache; private final BookEditCache bookEditCache; - private final ChunkCache chunkCache; + private @org.checkerframework.checker.nullness.qual.NonNull final ChunkCache chunkCache; private final EntityCache entityCache; private final EntityEffectCache effectCache; private final FormCache formCache; @@ -1354,6 +1354,12 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource { return false; } + public void requestOffhandSwap() { + ServerboundPlayerActionPacket swapHandsPacket = new ServerboundPlayerActionPacket(PlayerAction.SWAP_HANDS, Vector3i.ZERO, + Direction.DOWN, 0); + sendDownstreamPacket(swapHandsPacket); + } + /** * Will be overwritten for GeyserConnect. */ diff --git a/core/src/main/java/org/geysermc/geyser/session/cache/LodestoneCache.java b/core/src/main/java/org/geysermc/geyser/session/cache/LodestoneCache.java index 05c2628df..41f73863e 100644 --- a/core/src/main/java/org/geysermc/geyser/session/cache/LodestoneCache.java +++ b/core/src/main/java/org/geysermc/geyser/session/cache/LodestoneCache.java @@ -73,8 +73,7 @@ public final class LodestoneCache { } } - for (Int2ObjectMap.Entry entry : this.lodestones.int2ObjectEntrySet()) { - LodestonePos pos = entry.getValue(); + for (LodestonePos pos : this.lodestones.values()) { if (pos.equals(x, y, z, dim)) { // Use this existing position instead this.activeLodestones.put(itemStack, pos); diff --git a/core/src/main/java/org/geysermc/geyser/translator/inventory/EnchantingInventoryTranslator.java b/core/src/main/java/org/geysermc/geyser/translator/inventory/EnchantingInventoryTranslator.java index 97946b59c..8ae88c9bb 100644 --- a/core/src/main/java/org/geysermc/geyser/translator/inventory/EnchantingInventoryTranslator.java +++ b/core/src/main/java/org/geysermc/geyser/translator/inventory/EnchantingInventoryTranslator.java @@ -43,6 +43,7 @@ import org.geysermc.geyser.inventory.updater.UIInventoryUpdater; import org.geysermc.geyser.session.GeyserSession; import java.util.Arrays; +import java.util.Locale; public class EnchantingInventoryTranslator extends AbstractBlockInventoryTranslator { public EnchantingInventoryTranslator() { @@ -71,7 +72,7 @@ public class EnchantingInventoryTranslator extends AbstractBlockInventoryTransla // The Bedrock index might need changed, so let's look it up and see. int bedrockIndex = value; if (bedrockIndex != -1) { - Enchantment enchantment = Enchantment.getByJavaIdentifier("minecraft:" + Enchantment.JavaEnchantment.of(bedrockIndex).name().toLowerCase()); + Enchantment enchantment = Enchantment.getByJavaIdentifier("minecraft:" + Enchantment.JavaEnchantment.of(bedrockIndex).name().toLowerCase(Locale.ROOT)); if (enchantment != null) { // Convert the Java enchantment index to Bedrock's bedrockIndex = enchantment.ordinal(); diff --git a/core/src/main/java/org/geysermc/geyser/translator/protocol/bedrock/entity/player/BedrockEmoteTranslator.java b/core/src/main/java/org/geysermc/geyser/translator/protocol/bedrock/entity/player/BedrockEmoteTranslator.java index 43d2d314c..6be0be20f 100644 --- a/core/src/main/java/org/geysermc/geyser/translator/protocol/bedrock/entity/player/BedrockEmoteTranslator.java +++ b/core/src/main/java/org/geysermc/geyser/translator/protocol/bedrock/entity/player/BedrockEmoteTranslator.java @@ -25,11 +25,8 @@ package org.geysermc.geyser.translator.protocol.bedrock.entity.player; -import com.github.steveice10.mc.protocol.data.game.entity.object.Direction; -import com.github.steveice10.mc.protocol.data.game.entity.player.PlayerAction; -import com.github.steveice10.mc.protocol.packet.ingame.serverbound.player.ServerboundPlayerActionPacket; -import com.nukkitx.math.vector.Vector3i; import com.nukkitx.protocol.bedrock.packet.EmotePacket; +import org.geysermc.geyser.api.event.bedrock.ClientEmoteEvent; import org.geysermc.geyser.configuration.EmoteOffhandWorkaroundOption; import org.geysermc.geyser.entity.type.Entity; import org.geysermc.geyser.session.GeyserSession; @@ -43,15 +40,20 @@ public class BedrockEmoteTranslator extends PacketTranslator { public void translate(GeyserSession session, EmotePacket packet) { if (session.getGeyser().getConfig().getEmoteOffhandWorkaround() != EmoteOffhandWorkaroundOption.DISABLED) { // Activate the workaround - we should trigger the offhand now - ServerboundPlayerActionPacket swapHandsPacket = new ServerboundPlayerActionPacket(PlayerAction.SWAP_HANDS, Vector3i.ZERO, - Direction.DOWN, 0); - session.sendDownstreamPacket(swapHandsPacket); + session.requestOffhandSwap(); if (session.getGeyser().getConfig().getEmoteOffhandWorkaround() == EmoteOffhandWorkaroundOption.NO_EMOTES) { return; } } + // For the future: could have a method that exposes which players will see the emote + ClientEmoteEvent event = new ClientEmoteEvent(session, packet.getEmoteId()); + session.getGeyser().eventBus().fire(event); + if (event.isCancelled()) { + return; + } + int javaId = session.getPlayerEntity().getEntityId(); for (GeyserSession otherSession : session.getGeyser().getSessionManager().getSessions().values()) { if (otherSession != session) { From 30277d54114de100175b20b4257a55314409723b Mon Sep 17 00:00:00 2001 From: Camotoy <20743703+Camotoy@users.noreply.github.com> Date: Wed, 15 Feb 2023 16:17:38 -0500 Subject: [PATCH 03/59] Anticipate support for the patch version of 1.19.60 --- .../java/org/geysermc/geyser/network/GameProtocol.java | 4 ++++ .../org/geysermc/geyser/network/UpstreamPacketHandler.java | 7 +++++++ gradle/libs.versions.toml | 2 +- 3 files changed, 12 insertions(+), 1 deletion(-) diff --git a/core/src/main/java/org/geysermc/geyser/network/GameProtocol.java b/core/src/main/java/org/geysermc/geyser/network/GameProtocol.java index 804187075..099a2305a 100644 --- a/core/src/main/java/org/geysermc/geyser/network/GameProtocol.java +++ b/core/src/main/java/org/geysermc/geyser/network/GameProtocol.java @@ -75,6 +75,10 @@ public final class GameProtocol { .minecraftVersion("1.19.50/1.19.51") .build()); SUPPORTED_BEDROCK_CODECS.add(Bedrock_v567.V567_CODEC); + // So the version checker will work + SUPPORTED_BEDROCK_CODECS.add(Bedrock_v567.V567_CODEC.toBuilder() + .protocolVersion(568) + .build()); } /** diff --git a/core/src/main/java/org/geysermc/geyser/network/UpstreamPacketHandler.java b/core/src/main/java/org/geysermc/geyser/network/UpstreamPacketHandler.java index a72d8ab9e..26c463bb0 100644 --- a/core/src/main/java/org/geysermc/geyser/network/UpstreamPacketHandler.java +++ b/core/src/main/java/org/geysermc/geyser/network/UpstreamPacketHandler.java @@ -31,6 +31,8 @@ import com.nukkitx.protocol.bedrock.data.ExperimentData; import com.nukkitx.protocol.bedrock.data.PacketCompressionAlgorithm; import com.nukkitx.protocol.bedrock.data.ResourcePackType; import com.nukkitx.protocol.bedrock.packet.*; +import com.nukkitx.protocol.bedrock.v567.Bedrock_v567; +import com.nukkitx.protocol.bedrock.v567.Bedrock_v567patch; import org.geysermc.geyser.Constants; import org.geysermc.geyser.GeyserImpl; import org.geysermc.geyser.api.network.AuthType; @@ -141,6 +143,11 @@ public class UpstreamPacketHandler extends LoggingPacketHandler { return true; } + // Hack for... whatever this is + if (loginPacket.getProtocolVersion() == Bedrock_v567.V567_CODEC.getProtocolVersion() && !session.getClientData().getGameVersion().equals("1.19.60")) { + session.getUpstream().getSession().setPacketCodec(Bedrock_v567patch.BEDROCK_V567PATCH); + } + PlayStatusPacket playStatus = new PlayStatusPacket(); playStatus.setStatus(PlayStatusPacket.Status.LOGIN_SUCCESS); session.sendUpstreamPacket(playStatus); diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 14ca47155..986fae748 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -8,7 +8,7 @@ netty = "4.1.80.Final" guava = "29.0-jre" gson = "2.3.1" # Provided by Spigot 1.8.8 websocket = "1.5.1" -protocol = "2.9.16-20230205.181702-1" +protocol = "2.9.16-20230215.204900-2" raknet = "1.6.28-20220125.214016-6" mcauthlib = "d9d773e" mcprotocollib = "1.19.3-20230107.194116-10" From 406dfcb22caf51f1cec3559e11599362e856192e Mon Sep 17 00:00:00 2001 From: Camotoy <20743703+Camotoy@users.noreply.github.com> Date: Thu, 16 Feb 2023 20:01:51 -0500 Subject: [PATCH 04/59] Fix error on 1.19.62 --- .../java/org/geysermc/geyser/network/GameProtocol.java | 7 ++++--- gradle/libs.versions.toml | 2 +- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/core/src/main/java/org/geysermc/geyser/network/GameProtocol.java b/core/src/main/java/org/geysermc/geyser/network/GameProtocol.java index 099a2305a..c76fd7b61 100644 --- a/core/src/main/java/org/geysermc/geyser/network/GameProtocol.java +++ b/core/src/main/java/org/geysermc/geyser/network/GameProtocol.java @@ -48,7 +48,7 @@ public final class GameProtocol { * Default Bedrock codec that should act as a fallback. Should represent the latest available * release of the game that Geyser supports. */ - public static final BedrockPacketCodec DEFAULT_BEDROCK_CODEC = Bedrock_v560.V560_CODEC; + public static final BedrockPacketCodec DEFAULT_BEDROCK_CODEC = Bedrock_v567.V567_CODEC; /** * A list of all supported Bedrock versions that can join Geyser */ @@ -71,13 +71,14 @@ public final class GameProtocol { SUPPORTED_BEDROCK_CODECS.add(Bedrock_v557.V557_CODEC.toBuilder() .minecraftVersion("1.19.40/1.19.41") .build()); - SUPPORTED_BEDROCK_CODECS.add(DEFAULT_BEDROCK_CODEC.toBuilder() + SUPPORTED_BEDROCK_CODECS.add(Bedrock_v560.V560_CODEC.toBuilder() .minecraftVersion("1.19.50/1.19.51") .build()); - SUPPORTED_BEDROCK_CODECS.add(Bedrock_v567.V567_CODEC); + SUPPORTED_BEDROCK_CODECS.add(DEFAULT_BEDROCK_CODEC); // So the version checker will work SUPPORTED_BEDROCK_CODECS.add(Bedrock_v567.V567_CODEC.toBuilder() .protocolVersion(568) + .minecraftVersion("1.19.62") .build()); } diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 986fae748..f62faa54f 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -8,7 +8,7 @@ netty = "4.1.80.Final" guava = "29.0-jre" gson = "2.3.1" # Provided by Spigot 1.8.8 websocket = "1.5.1" -protocol = "2.9.16-20230215.204900-2" +protocol = "2.9.17-20230217.002312-1" raknet = "1.6.28-20220125.214016-6" mcauthlib = "d9d773e" mcprotocollib = "1.19.3-20230107.194116-10" From cfd7aeb927d0816a0d7943f370334ec6a876f16c Mon Sep 17 00:00:00 2001 From: Kas-tle <26531652+Kas-tle@users.noreply.github.com> Date: Fri, 17 Feb 2023 11:37:01 -0800 Subject: [PATCH 05/59] Update PR Build Action for API (#3535) * Use API from use making PR if exists * Update CONTRIBUTING.md * Address @Tim203's review * Fix path * Build the api before doing anything with the Geyser repo --------- Co-authored-by: Tim203 --- .github/workflows/pullrequest.yml | 41 ++++++++++++++++++++++++------- CONTRIBUTING.md | 4 ++- 2 files changed, 35 insertions(+), 10 deletions(-) diff --git a/.github/workflows/pullrequest.yml b/.github/workflows/pullrequest.yml index 39e9fe188..6781d7483 100644 --- a/.github/workflows/pullrequest.yml +++ b/.github/workflows/pullrequest.yml @@ -4,21 +4,44 @@ on: [pull_request] jobs: build: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v2 - name: Set up JDK 17 uses: actions/setup-java@v1 with: - distribution: 'temurin' java-version: 17 - cache: 'gradle' - - name: submodules-init - uses: snickerbockers/submodules-init@v4 - - name: Build with Gradle - run: ./gradlew build + distribution: temurin + cache: gradle + + - name: Check if the author has forked the API repo + uses: Kas-tle/ForkFinder@v1.0.1 + id: find_forks + with: + owner: GeyserMC + repo: api + token: ${{ secrets.GITHUB_TOKEN }} + + - name: Use author's API repo if it exists + if: steps.find_forks.outputs.target_branch_found == 'true' + env: + API_FORK_URL: ${{ steps.find_forks.outputs.user_fork_url }} + API_FORK_BRANCH: ${{ github.event.pull_request.head.ref }} + run: | + git clone "${API_FORK_URL}" --single-branch --branch "${API_FORK_BRANCH}" api + cd api + ./gradlew publishToMavenLocal + + - name: Checkout repository and submodules + uses: actions/checkout@v3 + with: + submodules: recursive + path: geyser + + - name: Build Geyser + uses: gradle/gradle-build-action@v2 + with: + arguments: build + build-root-directory: geyser - name: Archive artifacts (Geyser Fabric) uses: actions/upload-artifact@v2 diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index ce6894845..dac8a9a07 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -48,4 +48,6 @@ Make sure to comment your code where possible. The nature of our software requires a lot of arrays and maps to be stored - where possible, use Fastutil's specialized maps. For example, if you're storing block state translations, use an `Int2IntMap`. -We have a rundown of all the tools you need to develop over on our [wiki](https://github.com/GeyserMC/Geyser/wiki/Developer-Guide). If you have any questions, please feel free to reach out to our [Discord](https://discord.gg/geysermc)! +We have a rundown of all the tools you need to develop over on our [wiki](https://wiki.geysermc.org/other/developer-guide/). If you have any questions, please feel free to reach out to our [Discord](https://discord.gg/geysermc)! + +If you're making a pull request that also depends on changes to [the base API](https://github.com/GeyserMC/api), simply fork the API repo and create a branch with the same name as your Geyser PR. The pull request [action](https://github.com/GeyserMC/Geyser/blob/master/.github/workflows/pullrequest.yml) will automatically use your API changes while building your changes to Geyser. \ No newline at end of file From e7b8b4ef29beec7d3696a3bd759df298286083ee Mon Sep 17 00:00:00 2001 From: Camotoy <20743703+Camotoy@users.noreply.github.com> Date: Fri, 17 Feb 2023 18:55:40 -0500 Subject: [PATCH 06/59] Update supported versions in README --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index b36235e36..6996086b9 100644 --- a/README.md +++ b/README.md @@ -17,7 +17,7 @@ The ultimate goal of this project is to allow Minecraft: Bedrock Edition users t Special thanks to the DragonProxy project for being a trailblazer in protocol translation and for all the team members who have joined us here! -### Currently supporting Minecraft Bedrock 1.19.20 - 1.19.60 and Minecraft Java 1.19.3. +### Currently supporting Minecraft Bedrock 1.19.20 - 1.19.62 and Minecraft Java 1.19.3. ## Setting Up Take a look [here](https://wiki.geysermc.org/geyser/setup/) for how to set up Geyser. From e42c2d8ed52751d454d17727ccd6faa19f7f6864 Mon Sep 17 00:00:00 2001 From: Jackson_57 <49173011+jackson-57@users.noreply.github.com> Date: Wed, 22 Feb 2023 06:11:06 -0800 Subject: [PATCH 07/59] Fix artifact paths (#3570) --- .github/workflows/pullrequest.yml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/workflows/pullrequest.yml b/.github/workflows/pullrequest.yml index 6781d7483..8d5f5c03c 100644 --- a/.github/workflows/pullrequest.yml +++ b/.github/workflows/pullrequest.yml @@ -48,34 +48,34 @@ jobs: if: success() with: name: Geyser Fabric - path: bootstrap/fabric/build/libs/Geyser-Fabric.jar + path: geyser/bootstrap/fabric/build/libs/Geyser-Fabric.jar - name: Archive artifacts (Geyser Standalone) uses: actions/upload-artifact@v2 if: success() with: name: Geyser Standalone - path: bootstrap/standalone/build/libs/Geyser-Standalone.jar + path: geyser/bootstrap/standalone/build/libs/Geyser-Standalone.jar - name: Archive artifacts (Geyser Spigot) uses: actions/upload-artifact@v2 if: success() with: name: Geyser Spigot - path: bootstrap/spigot/build/libs/Geyser-Spigot.jar + path: geyser/bootstrap/spigot/build/libs/Geyser-Spigot.jar - name: Archive artifacts (Geyser BungeeCord) uses: actions/upload-artifact@v2 if: success() with: name: Geyser BungeeCord - path: bootstrap/bungeecord/build/libs/Geyser-BungeeCord.jar + path: geyser/bootstrap/bungeecord/build/libs/Geyser-BungeeCord.jar - name: Archive artifacts (Geyser Sponge) uses: actions/upload-artifact@v2 if: success() with: name: Geyser Sponge - path: bootstrap/sponge/build/libs/Geyser-Sponge.jar + path: geyser/bootstrap/sponge/build/libs/Geyser-Sponge.jar - name: Archive artifacts (Geyser Velocity) uses: actions/upload-artifact@v2 if: success() with: name: Geyser Velocity - path: bootstrap/velocity/build/libs/Geyser-Velocity.jar + path: geyser/bootstrap/velocity/build/libs/Geyser-Velocity.jar From e50226132febee19831e95c06b1ab99538ecb625 Mon Sep 17 00:00:00 2001 From: Camotoy <20743703+Camotoy@users.noreply.github.com> Date: Thu, 23 Feb 2023 13:11:18 -0500 Subject: [PATCH 08/59] Fix more issues of chunks not appearing --- .../java/org/geysermc/geyser/session/GeyserSession.java | 8 +++----- .../main/java/org/geysermc/geyser/util/ChunkUtils.java | 5 ++++- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/core/src/main/java/org/geysermc/geyser/session/GeyserSession.java b/core/src/main/java/org/geysermc/geyser/session/GeyserSession.java index bc49e8945..11467253e 100644 --- a/core/src/main/java/org/geysermc/geyser/session/GeyserSession.java +++ b/core/src/main/java/org/geysermc/geyser/session/GeyserSession.java @@ -180,7 +180,7 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource { private final AdvancementsCache advancementsCache; private final BookEditCache bookEditCache; - private @org.checkerframework.checker.nullness.qual.NonNull final ChunkCache chunkCache; + private final ChunkCache chunkCache; private final EntityCache entityCache; private final EntityEffectCache effectCache; private final FormCache formCache; @@ -1410,10 +1410,6 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource { } public void setServerRenderDistance(int renderDistance) { - // +1 is for Fabric and Spigot - // Without the client misses loading some chunks per https://github.com/GeyserMC/Geyser/issues/3490 - // Fog still appears essentially normally - renderDistance = renderDistance + 1; this.serverRenderDistance = renderDistance; ChunkRadiusUpdatedPacket chunkRadiusUpdatedPacket = new ChunkRadiusUpdatedPacket(); @@ -1425,11 +1421,13 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource { return this.upstream.getAddress(); } + @Override public boolean sendForm(@NonNull Form form) { formCache.showForm(form); return true; } + @Override public boolean sendForm(@NonNull FormBuilder formBuilder) { formCache.showForm(formBuilder.build()); return true; diff --git a/core/src/main/java/org/geysermc/geyser/util/ChunkUtils.java b/core/src/main/java/org/geysermc/geyser/util/ChunkUtils.java index 501087f78..aacf4f4d9 100644 --- a/core/src/main/java/org/geysermc/geyser/util/ChunkUtils.java +++ b/core/src/main/java/org/geysermc/geyser/util/ChunkUtils.java @@ -25,6 +25,7 @@ package org.geysermc.geyser.util; +import com.nukkitx.math.GenericMath; import com.nukkitx.math.vector.Vector2i; import com.nukkitx.math.vector.Vector3i; import com.nukkitx.protocol.bedrock.packet.LevelChunkPacket; @@ -91,7 +92,9 @@ public class ChunkUtils { if (chunkPos == null || !chunkPos.equals(newChunkPos)) { NetworkChunkPublisherUpdatePacket chunkPublisherUpdatePacket = new NetworkChunkPublisherUpdatePacket(); chunkPublisherUpdatePacket.setPosition(position); - chunkPublisherUpdatePacket.setRadius(session.getServerRenderDistance() << 4); + // Mitigates chunks not loading on 1.17.1 Paper and 1.19.3 Fabric. As of Bedrock 1.19.60. + // https://github.com/GeyserMC/Geyser/issues/3490 + chunkPublisherUpdatePacket.setRadius(GenericMath.ceil((session.getServerRenderDistance() + 1) * MathUtils.SQRT_OF_TWO) << 4); session.sendUpstreamPacket(chunkPublisherUpdatePacket); session.setLastChunkPosition(newChunkPos); From 65319c58597e73b8b8f0d8162f60fa48fc6aa8a9 Mon Sep 17 00:00:00 2001 From: Camotoy <20743703+Camotoy@users.noreply.github.com> Date: Fri, 24 Feb 2023 12:40:29 -0500 Subject: [PATCH 09/59] Prepare for an actual 568 release --- .../main/java/org/geysermc/geyser/network/GameProtocol.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/src/main/java/org/geysermc/geyser/network/GameProtocol.java b/core/src/main/java/org/geysermc/geyser/network/GameProtocol.java index c76fd7b61..8a3d068f9 100644 --- a/core/src/main/java/org/geysermc/geyser/network/GameProtocol.java +++ b/core/src/main/java/org/geysermc/geyser/network/GameProtocol.java @@ -34,6 +34,7 @@ import com.nukkitx.protocol.bedrock.v554.Bedrock_v554; import com.nukkitx.protocol.bedrock.v557.Bedrock_v557; import com.nukkitx.protocol.bedrock.v560.Bedrock_v560; import com.nukkitx.protocol.bedrock.v567.Bedrock_v567; +import com.nukkitx.protocol.bedrock.v567.Bedrock_v567patch; import org.geysermc.geyser.session.GeyserSession; import java.util.ArrayList; @@ -75,8 +76,7 @@ public final class GameProtocol { .minecraftVersion("1.19.50/1.19.51") .build()); SUPPORTED_BEDROCK_CODECS.add(DEFAULT_BEDROCK_CODEC); - // So the version checker will work - SUPPORTED_BEDROCK_CODECS.add(Bedrock_v567.V567_CODEC.toBuilder() + SUPPORTED_BEDROCK_CODECS.add(Bedrock_v567patch.BEDROCK_V567PATCH.toBuilder() .protocolVersion(568) .minecraftVersion("1.19.62") .build()); From 50104c95ec52ddf1032ee6db11cf5acca9a5f15f Mon Sep 17 00:00:00 2001 From: Camotoy <20743703+Camotoy@users.noreply.github.com> Date: Fri, 24 Feb 2023 13:32:46 -0500 Subject: [PATCH 10/59] Remove deploying Jenkinsfile step --- Jenkinsfile | 34 ---------------------------------- 1 file changed, 34 deletions(-) diff --git a/Jenkinsfile b/Jenkinsfile index 072f99154..a61ba6b3a 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -24,40 +24,6 @@ pipeline { } } } - - stage ('Deploy') { - when { - anyOf { - branch "master" - } - } - - steps { - rtGradleDeployer( - id: "GRADLE_DEPLOYER", - serverId: "opencollab-artifactory", - releaseRepo: "maven-releases", - snapshotRepo: "maven-snapshots" - ) - rtGradleResolver( - id: "GRADLE_RESOLVER", - serverId: "opencollab-artifactory" - ) - rtGradleRun( - usesPlugin: true, - tool: 'Gradle 7', - rootDir: "", - useWrapper: true, - buildFile: 'build.gradle.kts', - tasks: 'artifactoryPublish', - deployerId: "GRADLE_DEPLOYER", - resolverId: "GRADLE_RESOLVER" - ) - rtPublishBuildInfo( - serverId: "opencollab-artifactory" - ) - } - } } post { From a72e49527ddb48885b5bdaf57c766de8070d4c4b Mon Sep 17 00:00:00 2001 From: Redned Date: Fri, 24 Feb 2023 20:05:15 -0600 Subject: [PATCH 11/59] Simplify publish logic and move to GitHub Actions (#3579) Co-authored-by: Tim203 Co-authored-by: rtm516 --- .github/workflows/build.yml | 53 +++++++++++++++ Jenkinsfile | 37 ---------- build-logic/build.gradle.kts | 11 +-- build-logic/src/main/kotlin/extensions.kt | 3 - .../kotlin/geyser.base-conventions.gradle.kts | 25 ++++--- .../geyser.publish-conventions.gradle.kts | 32 ++------- .../chat/MessageTranslatorTest.java | 34 +++++----- .../inventory/item/CustomItemsTest.java | 14 ++-- .../collection/GeyserCollectionsTest.java | 68 ++++++++++--------- gradle/libs.versions.toml | 4 +- 10 files changed, 135 insertions(+), 146 deletions(-) create mode 100644 .github/workflows/build.yml diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml new file mode 100644 index 000000000..160191d46 --- /dev/null +++ b/.github/workflows/build.yml @@ -0,0 +1,53 @@ +name: Build + +on: [push] + +jobs: + build: + runs-on: ubuntu-latest + steps: + - name: Checkout repository and submodules + uses: actions/checkout@v3 + with: + submodules: recursive + + - uses: actions/setup-java@v3 + with: + java-version: 17 + distribution: temurin + cache: gradle + + - name: Build + uses: gradle/gradle-build-action@v2 + with: + arguments: build + + - name: Publish to Maven Repository + if: ${{ job.status == 'success' && github.repository == 'GeyserMC/Geyser' && github.ref_name == 'master' }} + uses: gradle/gradle-build-action@v2 + env: + ORG_GRADLE_PROJECT_geysermcUsername: ${{ vars.DEPLOY_USER }} + ORG_GRADLE_PROJECT_geysermcPassword: ${{ secrets.DEPLOY_PASS }} + with: + arguments: publish + + - name: Publish to Downloads API + if: ${{ github.ref_name == 'master' && job.status == 'success' && github.repository == 'GeyserMC/Geyser' }} + shell: bash + run: | + # Save the private key to a file + echo "$DOWNLOADS_PRIVATE_KEY" > id_ecdsa + chmod 600 id_ecdsa + # Get the version from gradle.properties + version=$(cat gradle.properties | grep -o "version=[0-9\\.]*" | cut -d"=" -f2) + # Copy over artifacts + scp -B -o StrictHostKeyChecking=no -i id_ecdsa bootstrap/**/build/libs/Geyser-*.jar $DOWNLOADS_USERNAME@$DOWNLOADS_SERVER_IP:~/files/ + # Run the build script + ssh -o StrictHostKeyChecking=no -i id_ecdsa $DOWNLOADS_USERNAME@$DOWNLOADS_SERVER_IP ./handleBuild.sh geyser $version $GITHUB_RUN_ID $GITHUB_SHA + + - name: Notify Discord + if: ${{ github.repository == 'GeyserMC/Geyser' }} + uses: Tim203/actions-git-discord-webhook@main + with: + webhook_url: ${{ secrets.DISCORD_WEBHOOK }} + status: ${{ job.status }} \ No newline at end of file diff --git a/Jenkinsfile b/Jenkinsfile index a61ba6b3a..5c2eada3d 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -27,43 +27,6 @@ pipeline { } post { - always { - script { - def changeLogSets = currentBuild.changeSets - def message = "**Changes:**" - - if (changeLogSets.size() == 0) { - message += "\n*No changes.*" - } else { - def repositoryUrl = scm.userRemoteConfigs[0].url.replace(".git", "") - def count = 0; - def extra = 0; - for (int i = 0; i < changeLogSets.size(); i++) { - def entries = changeLogSets[i].items - for (int j = 0; j < entries.length; j++) { - if (count <= 10) { - def entry = entries[j] - def commitId = entry.commitId.substring(0, 6) - message += "\n - [`${commitId}`](${repositoryUrl}/commit/${entry.commitId}) ${entry.msg}" - count++ - } else { - extra++; - } - } - } - - if (extra != 0) { - message += "\n - ${extra} more commits" - } - } - - env.changes = message - } - deleteDir() - withCredentials([string(credentialsId: 'geyser-discord-webhook', variable: 'DISCORD_WEBHOOK')]) { - discordSend description: "**Build:** [${currentBuild.id}](${env.BUILD_URL})\n**Status:** [${currentBuild.currentResult}](${env.BUILD_URL})\n${changes}\n\n[**Artifacts on Jenkins**](https://ci.opencollab.dev/job/GeyserMC/job/Geyser)", footer: 'Open Collaboration Jenkins', link: env.BUILD_URL, successful: currentBuild.resultIsBetterOrEqualTo('SUCCESS'), title: "${env.JOB_NAME} #${currentBuild.id}", webhookURL: DISCORD_WEBHOOK - } - } success { script { if (env.BRANCH_NAME == 'master') { diff --git a/build-logic/build.gradle.kts b/build-logic/build.gradle.kts index e21806660..3d1fb47f7 100644 --- a/build-logic/build.gradle.kts +++ b/build-logic/build.gradle.kts @@ -1,5 +1,3 @@ -import org.jetbrains.kotlin.gradle.tasks.KotlinCompile - plugins { `kotlin-dsl` } @@ -10,17 +8,10 @@ repositories { } dependencies { - implementation("net.kyori", "indra-common", "2.0.6") - implementation("org.jfrog.buildinfo", "build-info-extractor-gradle", "4.26.1") + implementation("net.kyori", "indra-common", "3.0.1") implementation("com.github.johnrengelman", "shadow", "7.1.3-SNAPSHOT") // Within the gradle plugin classpath, there is a version conflict between loom and some other // plugin for databind. This fixes it: minimum 2.13.2 is required by loom. implementation("com.fasterxml.jackson.core:jackson-databind:2.14.0") } - -tasks.withType { - kotlinOptions { - jvmTarget = "16" - } -} diff --git a/build-logic/src/main/kotlin/extensions.kt b/build-logic/src/main/kotlin/extensions.kt index 0c01913d2..1e1732852 100644 --- a/build-logic/src/main/kotlin/extensions.kt +++ b/build-logic/src/main/kotlin/extensions.kt @@ -30,9 +30,6 @@ import org.gradle.api.artifacts.ProjectDependency import org.gradle.api.provider.Provider import org.gradle.kotlin.dsl.named -fun Project.isSnapshot(): Boolean = - version.toString().endsWith("-SNAPSHOT") - fun Project.relocate(pattern: String) { tasks.named("shadowJar") { relocate(pattern, "org.geysermc.geyser.shaded.$pattern") diff --git a/build-logic/src/main/kotlin/geyser.base-conventions.gradle.kts b/build-logic/src/main/kotlin/geyser.base-conventions.gradle.kts index 44a74db3d..709867300 100644 --- a/build-logic/src/main/kotlin/geyser.base-conventions.gradle.kts +++ b/build-logic/src/main/kotlin/geyser.base-conventions.gradle.kts @@ -1,12 +1,25 @@ plugins { `java-library` - `maven-publish` + id("net.kyori.indra") } dependencies { compileOnly("org.checkerframework", "checker-qual", "3.19.0") } +indra { + github("GeyserMC", "Geyser") { + ci(true) + issues(true) + scm(true) + } + mitLicense() + + javaVersions { + target(16) + } +} + tasks { processResources { // Spigot, BungeeCord, Velocity, Sponge, Fabric @@ -21,14 +34,4 @@ tasks { ) } } - compileJava { - options.encoding = Charsets.UTF_8.name() - } -} - -java { - sourceCompatibility = JavaVersion.VERSION_16 - targetCompatibility = JavaVersion.VERSION_16 - - withSourcesJar() } diff --git a/build-logic/src/main/kotlin/geyser.publish-conventions.gradle.kts b/build-logic/src/main/kotlin/geyser.publish-conventions.gradle.kts index 7525f97fa..036ee803c 100644 --- a/build-logic/src/main/kotlin/geyser.publish-conventions.gradle.kts +++ b/build-logic/src/main/kotlin/geyser.publish-conventions.gradle.kts @@ -1,33 +1,9 @@ plugins { id("geyser.shadow-conventions") - id("com.jfrog.artifactory") - id("maven-publish") + id("net.kyori.indra.publishing") } -publishing { - publications { - create("mavenJava") { - groupId = project.group as String - artifactId = project.name - version = project.version as String - - from(components["java"]) - } - } -} - -artifactory { - setContextUrl("https://repo.opencollab.dev/artifactory") - publish { - repository { - setRepoKey(if (isSnapshot()) "maven-snapshots" else "maven-releases") - setMavenCompatible(true) - } - defaults { - publications("mavenJava") - setPublishArtifacts(true) - setPublishPom(true) - setPublishIvy(false) - } - } +indra { + publishSnapshotsTo("geysermc", "https://repo.opencollab.dev/maven-snapshots") + publishReleasesTo("geysermc", "https://repo.opencollab.dev/maven-releases") } diff --git a/core/src/test/java/org/geysermc/geyser/network/translators/chat/MessageTranslatorTest.java b/core/src/test/java/org/geysermc/geyser/network/translators/chat/MessageTranslatorTest.java index e83c6f73d..a804916fa 100644 --- a/core/src/test/java/org/geysermc/geyser/network/translators/chat/MessageTranslatorTest.java +++ b/core/src/test/java/org/geysermc/geyser/network/translators/chat/MessageTranslatorTest.java @@ -27,18 +27,20 @@ package org.geysermc.geyser.network.translators.chat; import com.github.steveice10.mc.protocol.data.DefaultComponentSerializer; import org.geysermc.geyser.translator.text.MessageTranslator; -import org.junit.Assert; -import org.junit.Before; -import org.junit.Test; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestInstance; import java.util.HashMap; import java.util.Map; +@TestInstance(TestInstance.Lifecycle.PER_CLASS) public class MessageTranslatorTest { private Map messages = new HashMap<>(); - @Before + @BeforeAll public void setUp() throws Exception { messages.put("{\"text\":\"\",\"extra\":[{\"text\":\"DoctorMad9952 joined the game\",\"color\":\"yellow\"}]}", "§r§eDoctorMad9952 joined the game"); @@ -70,27 +72,27 @@ public class MessageTranslatorTest { public void convertMessage() { for (Map.Entry entry : messages.entrySet()) { String bedrockMessage = MessageTranslator.convertMessage(entry.getKey(), "en_US"); - Assert.assertEquals("Translation of messages is incorrect", entry.getValue(), bedrockMessage); + Assertions.assertEquals(entry.getValue(), bedrockMessage, "Translation of messages is incorrect"); } } @Test public void convertMessageLenient() { - Assert.assertEquals("All newline message is not handled properly", "\n\n\n\n", MessageTranslator.convertMessageLenient("\n\n\n\n")); - Assert.assertEquals("Empty message is not handled properly", "", MessageTranslator.convertMessageLenient("")); - Assert.assertEquals("Reset before message is not handled properly", "§r§eGame Selector", MessageTranslator.convertMessageLenient("§r§eGame Selector")); - Assert.assertEquals("Unimplemented formatting chars not stripped", "Bold Underline", MessageTranslator.convertMessageLenient("§m§nBold Underline")); + Assertions.assertEquals("\n\n\n\n", MessageTranslator.convertMessageLenient("\n\n\n\n"), "All newline message is not handled properly"); + Assertions.assertEquals("", MessageTranslator.convertMessageLenient(""), "Empty message is not handled properly"); + Assertions.assertEquals("§r§eGame Selector", MessageTranslator.convertMessageLenient("§r§eGame Selector"), "Reset before message is not handled properly"); + Assertions.assertEquals("Bold Underline", MessageTranslator.convertMessageLenient("§m§nBold Underline"), "Unimplemented formatting chars not stripped"); } @Test public void convertToPlainText() { - Assert.assertEquals("JSON message is not handled properly", "Many colors here", MessageTranslator.convertToPlainText("{\"extra\":[{\"color\":\"red\",\"text\":\"M\"},{\"color\":\"gold\",\"text\":\"a\"},{\"color\":\"yellow\",\"text\":\"n\"},{\"color\":\"green\",\"text\":\"y \"},{\"color\":\"aqua\",\"text\":\"c\"},{\"color\":\"dark_purple\",\"text\":\"o\"},{\"color\":\"red\",\"text\":\"l\"},{\"color\":\"gold\",\"text\":\"o\"},{\"color\":\"yellow\",\"text\":\"r\"},{\"color\":\"green\",\"text\":\"s \"},{\"color\":\"aqua\",\"text\":\"h\"},{\"color\":\"dark_purple\",\"text\":\"e\"},{\"color\":\"red\",\"text\":\"r\"},{\"color\":\"gold\",\"text\":\"e\"}],\"text\":\"\"}", "en_US")); - Assert.assertEquals("Legacy formatted message is not handled properly (Colors)", "Many colors here", MessageTranslator.convertToPlainText("§cM§6a§en§ay §bc§5o§cl§6o§er§as §bh§5e§cr§6e")); - Assert.assertEquals("Legacy formatted message is not handled properly (Colors)", "Many colors here", MessageTranslator.convertToPlainText("§cM§6a§en§ay §bc§5o§cl§6o§er§as §bh§5e§cr§6e", "en_US")); - Assert.assertEquals("Legacy formatted message is not handled properly (Style)", "Obf Bold Strikethrough Underline Italic Reset", MessageTranslator.convertToPlainText("§kObf §lBold §mStrikethrough §nUnderline §oItalic §rReset", "en_US")); - Assert.assertEquals("Valid lenient JSON is not handled properly", "Strange", MessageTranslator.convertToPlainText("§rStrange", "en_US")); - Assert.assertEquals("Empty message is not handled properly", "", MessageTranslator.convertToPlainText("", "en_US")); - Assert.assertEquals("Whitespace is not preserved", " ", MessageTranslator.convertToPlainText(" ", "en_US")); + Assertions.assertEquals("Many colors here", MessageTranslator.convertToPlainText("{\"extra\":[{\"color\":\"red\",\"text\":\"M\"},{\"color\":\"gold\",\"text\":\"a\"},{\"color\":\"yellow\",\"text\":\"n\"},{\"color\":\"green\",\"text\":\"y \"},{\"color\":\"aqua\",\"text\":\"c\"},{\"color\":\"dark_purple\",\"text\":\"o\"},{\"color\":\"red\",\"text\":\"l\"},{\"color\":\"gold\",\"text\":\"o\"},{\"color\":\"yellow\",\"text\":\"r\"},{\"color\":\"green\",\"text\":\"s \"},{\"color\":\"aqua\",\"text\":\"h\"},{\"color\":\"dark_purple\",\"text\":\"e\"},{\"color\":\"red\",\"text\":\"r\"},{\"color\":\"gold\",\"text\":\"e\"}],\"text\":\"\"}", "en_US"), "JSON message is not handled properly"); + Assertions.assertEquals("Many colors here", MessageTranslator.convertToPlainText("§cM§6a§en§ay §bc§5o§cl§6o§er§as §bh§5e§cr§6e"), "Legacy formatted message is not handled properly (Colors)"); + Assertions.assertEquals("Many colors here", MessageTranslator.convertToPlainText("§cM§6a§en§ay §bc§5o§cl§6o§er§as §bh§5e§cr§6e", "en_US"), "Legacy formatted message is not handled properly (Colors)"); + Assertions.assertEquals("Obf Bold Strikethrough Underline Italic Reset", MessageTranslator.convertToPlainText("§kObf §lBold §mStrikethrough §nUnderline §oItalic §rReset", "en_US"), "Legacy formatted message is not handled properly (Style)"); + Assertions.assertEquals("Strange", MessageTranslator.convertToPlainText("§rStrange", "en_US"), "Valid lenient JSON is not handled properly"); + Assertions.assertEquals("", MessageTranslator.convertToPlainText("", "en_US"), "Empty message is not handled properly"); + Assertions.assertEquals(" ", MessageTranslator.convertToPlainText(" ", "en_US"), "Whitespace is not preserved"); } @Test diff --git a/core/src/test/java/org/geysermc/geyser/translator/inventory/item/CustomItemsTest.java b/core/src/test/java/org/geysermc/geyser/translator/inventory/item/CustomItemsTest.java index 145f46369..806ec4542 100644 --- a/core/src/test/java/org/geysermc/geyser/translator/inventory/item/CustomItemsTest.java +++ b/core/src/test/java/org/geysermc/geyser/translator/inventory/item/CustomItemsTest.java @@ -36,20 +36,22 @@ import org.geysermc.geyser.api.item.custom.CustomItemOptions; import org.geysermc.geyser.api.util.TriState; import org.geysermc.geyser.item.GeyserCustomItemOptions; import org.geysermc.geyser.registry.type.ItemMapping; -import org.junit.Assert; -import org.junit.Before; -import org.junit.Test; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestInstance; import java.util.List; import java.util.OptionalInt; +@TestInstance(TestInstance.Lifecycle.PER_CLASS) public class CustomItemsTest { private ItemMapping testMappingWithDamage; private Object2IntMap tagToCustomItemWithDamage; private ItemMapping testMappingWithNoDamage; private Object2IntMap tagToCustomItemWithNoDamage; - @Before + @BeforeAll public void setup() { CustomItemOptions a = new GeyserCustomItemOptions(TriState.TRUE, OptionalInt.of(2), OptionalInt.empty()); CustomItemOptions b = new GeyserCustomItemOptions(TriState.FALSE, OptionalInt.of(5), OptionalInt.empty()); @@ -147,12 +149,12 @@ public class CustomItemsTest { public void testCustomItems() { for (Object2IntMap.Entry entry : this.tagToCustomItemWithDamage.object2IntEntrySet()) { int id = CustomItemTranslator.getCustomItem(entry.getKey(), this.testMappingWithDamage); - Assert.assertEquals(entry.getKey() + " did not produce the correct custom item", entry.getIntValue(), id); + Assertions.assertEquals(entry.getIntValue(), id, entry.getKey() + " did not produce the correct custom item"); } for (Object2IntMap.Entry entry : this.tagToCustomItemWithNoDamage.object2IntEntrySet()) { int id = CustomItemTranslator.getCustomItem(entry.getKey(), this.testMappingWithNoDamage); - Assert.assertEquals(entry.getKey() + " did not produce the correct custom item", entry.getIntValue(), id); + Assertions.assertEquals(entry.getIntValue(), id, entry.getKey() + " did not produce the correct custom item"); } } } diff --git a/core/src/test/java/org/geysermc/geyser/util/collection/GeyserCollectionsTest.java b/core/src/test/java/org/geysermc/geyser/util/collection/GeyserCollectionsTest.java index a2a9f98f6..639c79331 100644 --- a/core/src/test/java/org/geysermc/geyser/util/collection/GeyserCollectionsTest.java +++ b/core/src/test/java/org/geysermc/geyser/util/collection/GeyserCollectionsTest.java @@ -25,9 +25,11 @@ package org.geysermc.geyser.util.collection; -import org.junit.Assert; -import org.junit.Test; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestInstance; +@TestInstance(TestInstance.Lifecycle.PER_CLASS) public class GeyserCollectionsTest { private final byte[] bytes = new byte[] {(byte) 5, (byte) 4, (byte) 3, (byte) 2, (byte) 2, (byte) 1}; private final boolean[] booleans = new boolean[] {true, false, false, true}; @@ -51,35 +53,35 @@ public class GeyserCollectionsTest { int lastKey = index; // Easy, understandable out-of-bounds checks - Assert.assertFalse("Map contains key bigger by one!", map.containsKey(lastKey)); - Assert.assertTrue("Map doesn't contain final key!", map.containsKey(lastKey - 1)); + Assertions.assertFalse(map.containsKey(lastKey), "Map contains key bigger by one!"); + Assertions.assertTrue(map.containsKey(lastKey - 1), "Map doesn't contain final key!"); // Ensure the first and last values do not throw an exception on get, and test getOrDefault map.get(start - 1); map.get(lastKey); - Assert.assertEquals(map.getOrDefault(start - 1, Byte.MAX_VALUE), Byte.MAX_VALUE); - Assert.assertEquals(map.getOrDefault(lastKey, Byte.MAX_VALUE), Byte.MAX_VALUE); - Assert.assertEquals(map.getOrDefault(lastKey, Byte.MIN_VALUE), Byte.MIN_VALUE); + Assertions.assertEquals(map.getOrDefault(start - 1, Byte.MAX_VALUE), Byte.MAX_VALUE); + Assertions.assertEquals(map.getOrDefault(lastKey, Byte.MAX_VALUE), Byte.MAX_VALUE); + Assertions.assertEquals(map.getOrDefault(lastKey, Byte.MIN_VALUE), Byte.MIN_VALUE); - Assert.assertEquals(map.size(), bytes.length); + Assertions.assertEquals(map.size(), bytes.length); for (int i = start; i < bytes.length; i++) { - Assert.assertTrue(map.containsKey(i)); - Assert.assertEquals(map.get(i), bytes[i - start]); + Assertions.assertTrue(map.containsKey(i)); + Assertions.assertEquals(map.get(i), bytes[i - start]); } for (int i = start - 1; i >= (start - 6); i--) { // Lower than expected check - Assert.assertFalse(i + " is in a map that starts with " + start, map.containsKey(i)); + Assertions.assertFalse(map.containsKey(i), i + " is in a map that starts with " + start); } for (int i = bytes.length + start; i < bytes.length + 5 + start; i++) { // Higher than expected check - Assert.assertFalse(i + " is in a map that ends with " + (start + bytes.length), map.containsKey(i)); + Assertions.assertFalse(map.containsKey(i), i + " is in a map that ends with " + (start + bytes.length)); } for (byte b : bytes) { - Assert.assertTrue(map.containsValue(b)); + Assertions.assertTrue(map.containsValue(b)); } } @@ -99,33 +101,33 @@ public class GeyserCollectionsTest { int lastKey = index; // Easy, understandable out-of-bounds checks - Assert.assertFalse("Map contains key bigger by one!", map.containsKey(lastKey)); - Assert.assertTrue("Map doesn't contain final key!", map.containsKey(lastKey - 1)); + Assertions.assertFalse(map.containsKey(lastKey), "Map contains key bigger by one!"); + Assertions.assertTrue(map.containsKey(lastKey - 1), "Map doesn't contain final key!"); // Ensure the first and last values do not throw an exception on get map.get(start - 1); map.get(lastKey); - Assert.assertTrue(map.getOrDefault(lastKey, true)); + Assertions.assertTrue(map.getOrDefault(lastKey, true)); - Assert.assertEquals(map.size(), booleans.length); + Assertions.assertEquals(map.size(), booleans.length); for (int i = start; i < booleans.length; i++) { - Assert.assertTrue(map.containsKey(i)); - Assert.assertEquals(map.get(i), booleans[i - start]); + Assertions.assertTrue(map.containsKey(i)); + Assertions.assertEquals(map.get(i), booleans[i - start]); } for (int i = start - 1; i >= (start - 6); i--) { // Lower than expected check - Assert.assertFalse(i + " is in a map that starts with " + start, map.containsKey(i)); + Assertions.assertFalse(map.containsKey(i), i + " is in a map that starts with " + start); } for (int i = booleans.length + start; i < booleans.length + start + 5; i++) { // Higher than expected check - Assert.assertFalse(i + " is in a map that ends with " + (start + booleans.length), map.containsKey(i)); + Assertions.assertFalse(map.containsKey(i), i + " is in a map that ends with " + (start + booleans.length)); } for (boolean b : booleans) { - Assert.assertTrue(map.containsValue(b)); + Assertions.assertTrue(map.containsValue(b)); } } @@ -145,35 +147,35 @@ public class GeyserCollectionsTest { int lastKey = index; // Easy, understandable out-of-bounds checks - Assert.assertFalse("Map contains key bigger by one!", map.containsKey(lastKey)); - Assert.assertTrue("Map doesn't contain final key!", map.containsKey(lastKey - 1)); + Assertions.assertFalse(map.containsKey(lastKey), "Map contains key bigger by one!"); + Assertions.assertTrue(map.containsKey(lastKey - 1), "Map doesn't contain final key!"); // Ensure the first and last values do not throw an exception on get, and test getOrDefault map.get(start - 1); map.get(lastKey); - Assert.assertEquals(map.getOrDefault(start - 1, Integer.MAX_VALUE), Integer.MAX_VALUE); - Assert.assertEquals(map.getOrDefault(lastKey, Integer.MAX_VALUE), Integer.MAX_VALUE); - Assert.assertEquals(map.getOrDefault(lastKey, Integer.MIN_VALUE), Integer.MIN_VALUE); + Assertions.assertEquals(map.getOrDefault(start - 1, Integer.MAX_VALUE), Integer.MAX_VALUE); + Assertions.assertEquals(map.getOrDefault(lastKey, Integer.MAX_VALUE), Integer.MAX_VALUE); + Assertions.assertEquals(map.getOrDefault(lastKey, Integer.MIN_VALUE), Integer.MIN_VALUE); - Assert.assertEquals(map.size(), ints.length); + Assertions.assertEquals(map.size(), ints.length); for (int i = start; i < ints.length; i++) { - Assert.assertTrue(map.containsKey(i)); - Assert.assertEquals(map.get(i), ints[i - start]); + Assertions.assertTrue(map.containsKey(i)); + Assertions.assertEquals(map.get(i), ints[i - start]); } for (int i = start - 1; i >= (start - 6); i--) { // Lower than expected check - Assert.assertFalse(i + " is in a map that starts with " + start, map.containsKey(i)); + Assertions.assertFalse(map.containsKey(i), i + " is in a map that starts with " + start); } for (int i = ints.length + start; i < ints.length + 5 + start; i++) { // Higher than expected check - Assert.assertFalse(i + " is in a map that ends with " + (start + ints.length), map.containsKey(i)); + Assertions.assertFalse(map.containsKey(i), i + " is in a map that ends with " + (start + ints.length)); } for (int i : ints) { - Assert.assertTrue(map.containsValue(i)); + Assertions.assertTrue(map.containsValue(i)); } } } diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index f62faa54f..8858edc68 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -15,7 +15,7 @@ mcprotocollib = "1.19.3-20230107.194116-10" packetlib = "3.0.1" adventure = "4.12.0-20220629.025215-9" adventure-platform = "4.1.2" -junit = "4.13.1" +junit = "5.9.2" checkerframework = "3.19.0" log4j = "2.17.1" jline = "3.21.0" @@ -81,7 +81,7 @@ checker-qual = { group = "org.checkerframework", name = "checker-qual", version. commodore = { group = "me.lucko", name = "commodore", version.ref = "commodore" } guava = { group = "com.google.guava", name = "guava", version.ref = "guava" } gson = { group = "com.google.code.gson", name = "gson", version.ref = "gson" } -junit = { group = "junit", name = "junit", version.ref = "junit" } +junit = { group = "org.junit.jupiter", name = "junit-jupiter", version.ref = "junit" } mcauthlib = { group = "com.github.GeyserMC", name = "MCAuthLib", version.ref = "mcauthlib" } mcprotocollib = { group = "com.github.steveice10", name = "mcprotocollib", version.ref = "mcprotocollib" } packetlib = { group = "com.github.steveice10", name = "packetlib", version.ref = "packetlib" } From c9b58e154fe58a24fd8ef23ba522c81d67d54ba1 Mon Sep 17 00:00:00 2001 From: rtm516 Date: Sat, 25 Feb 2023 02:36:51 +0000 Subject: [PATCH 12/59] Allow build notifcation on success or failure --- .github/workflows/build.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 160191d46..553e2c5b2 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -46,8 +46,8 @@ jobs: ssh -o StrictHostKeyChecking=no -i id_ecdsa $DOWNLOADS_USERNAME@$DOWNLOADS_SERVER_IP ./handleBuild.sh geyser $version $GITHUB_RUN_ID $GITHUB_SHA - name: Notify Discord - if: ${{ github.repository == 'GeyserMC/Geyser' }} + if: ${{ (success() || failure()) && github.repository == 'GeyserMC/Geyser' }} uses: Tim203/actions-git-discord-webhook@main with: webhook_url: ${{ secrets.DISCORD_WEBHOOK }} - status: ${{ job.status }} \ No newline at end of file + status: ${{ job.status }} From 806ce6c9303ae5a2673f4bf2013eb3a9e87afebb Mon Sep 17 00:00:00 2001 From: rtm516 Date: Sat, 25 Feb 2023 02:57:43 +0000 Subject: [PATCH 13/59] Import build secrets as env vars --- .github/workflows/build.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 553e2c5b2..a8e1ec152 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -34,6 +34,9 @@ jobs: - name: Publish to Downloads API if: ${{ github.ref_name == 'master' && job.status == 'success' && github.repository == 'GeyserMC/Geyser' }} shell: bash + env: + DOWNLOADS_PRIVATE_KEY: ${{ secrets.DOWNLOADS_PRIVATE_KEY }} + DOWNLOADS_SERVER_IP: ${{ secrets.DOWNLOADS_SERVER_IP }} run: | # Save the private key to a file echo "$DOWNLOADS_PRIVATE_KEY" > id_ecdsa From 9b77d43fa20ea501c4fcce0b69f533be43f070e7 Mon Sep 17 00:00:00 2001 From: rtm516 Date: Sat, 25 Feb 2023 03:08:02 +0000 Subject: [PATCH 14/59] Testing in production --- .github/workflows/build.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index a8e1ec152..d493ffe94 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -35,6 +35,7 @@ jobs: if: ${{ github.ref_name == 'master' && job.status == 'success' && github.repository == 'GeyserMC/Geyser' }} shell: bash env: + DOWNLOADS_USERNAME: ${{ vars.DOWNLOADS_USERNAME }} DOWNLOADS_PRIVATE_KEY: ${{ secrets.DOWNLOADS_PRIVATE_KEY }} DOWNLOADS_SERVER_IP: ${{ secrets.DOWNLOADS_SERVER_IP }} run: | From b0c42086ad70c0b010bbfd3084a9205bc85d05d4 Mon Sep 17 00:00:00 2001 From: rtm516 Date: Sat, 25 Feb 2023 04:03:42 +0000 Subject: [PATCH 15/59] Fix build IDs for publishing --- .github/workflows/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index d493ffe94..f83b9a901 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -47,7 +47,7 @@ jobs: # Copy over artifacts scp -B -o StrictHostKeyChecking=no -i id_ecdsa bootstrap/**/build/libs/Geyser-*.jar $DOWNLOADS_USERNAME@$DOWNLOADS_SERVER_IP:~/files/ # Run the build script - ssh -o StrictHostKeyChecking=no -i id_ecdsa $DOWNLOADS_USERNAME@$DOWNLOADS_SERVER_IP ./handleBuild.sh geyser $version $GITHUB_RUN_ID $GITHUB_SHA + ssh -o StrictHostKeyChecking=no -i id_ecdsa $DOWNLOADS_USERNAME@$DOWNLOADS_SERVER_IP ./handleBuild.sh geyser $version $GITHUB_RUN_NUMBER $GITHUB_SHA - name: Notify Discord if: ${{ (success() || failure()) && github.repository == 'GeyserMC/Geyser' }} From fca726caa4e408a84707dce0f027ff82aec6be4d Mon Sep 17 00:00:00 2001 From: onebeastchris Date: Mon, 27 Feb 2023 20:09:07 +0100 Subject: [PATCH 16/59] version bump (#3585) seems like aternos depends on this to show the latest version iirc; should be bumped either way --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 6996086b9..2fd09eb8f 100644 --- a/README.md +++ b/README.md @@ -17,7 +17,7 @@ The ultimate goal of this project is to allow Minecraft: Bedrock Edition users t Special thanks to the DragonProxy project for being a trailblazer in protocol translation and for all the team members who have joined us here! -### Currently supporting Minecraft Bedrock 1.19.20 - 1.19.62 and Minecraft Java 1.19.3. +### Currently supporting Minecraft Bedrock 1.19.20 - 1.19.63 and Minecraft Java 1.19.3. ## Setting Up Take a look [here](https://wiki.geysermc.org/geyser/setup/) for how to set up Geyser. From 95d10fb7fc499cedc38b05b3b58ba1523109a0ca Mon Sep 17 00:00:00 2001 From: Camotoy <20743703+Camotoy@users.noreply.github.com> Date: Thu, 2 Mar 2023 18:40:40 -0500 Subject: [PATCH 17/59] Don't throw an AssertionError on failed locale download Fixes #3589 --- core/src/main/java/org/geysermc/geyser/util/WebUtils.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/main/java/org/geysermc/geyser/util/WebUtils.java b/core/src/main/java/org/geysermc/geyser/util/WebUtils.java index c0889f1c5..e4a98b3fc 100644 --- a/core/src/main/java/org/geysermc/geyser/util/WebUtils.java +++ b/core/src/main/java/org/geysermc/geyser/util/WebUtils.java @@ -91,7 +91,7 @@ public class WebUtils { InputStream in = con.getInputStream(); Files.copy(in, Paths.get(fileLocation), StandardCopyOption.REPLACE_EXISTING); } catch (Exception e) { - throw new AssertionError("Unable to download and save file: " + fileLocation + " (" + reqURL + ")", e); + throw new RuntimeException("Unable to download and save file: " + fileLocation + " (" + reqURL + ")", e); } } From 10c2e51da42ea65bf1402ee24c606b58c31e78cf Mon Sep 17 00:00:00 2001 From: apex_ Date: Fri, 3 Mar 2023 15:09:52 +0100 Subject: [PATCH 18/59] Fix closing inventory confirmation behavior (#3587) --- .../main/java/org/geysermc/geyser/inventory/Inventory.java | 4 ++++ .../main/java/org/geysermc/geyser/util/InventoryUtils.java | 4 +++- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/core/src/main/java/org/geysermc/geyser/inventory/Inventory.java b/core/src/main/java/org/geysermc/geyser/inventory/Inventory.java index f01d242ad..624d0df27 100644 --- a/core/src/main/java/org/geysermc/geyser/inventory/Inventory.java +++ b/core/src/main/java/org/geysermc/geyser/inventory/Inventory.java @@ -89,6 +89,10 @@ public abstract class Inventory { @Setter private boolean pending = false; + @Getter + @Setter + private boolean displayed = false; + protected Inventory(int id, int size, ContainerType containerType) { this("Inventory", id, size, containerType); } diff --git a/core/src/main/java/org/geysermc/geyser/util/InventoryUtils.java b/core/src/main/java/org/geysermc/geyser/util/InventoryUtils.java index 1f31d917b..1c07a0b1e 100644 --- a/core/src/main/java/org/geysermc/geyser/util/InventoryUtils.java +++ b/core/src/main/java/org/geysermc/geyser/util/InventoryUtils.java @@ -95,6 +95,7 @@ public class InventoryUtils { if (openInv != null && openInv.getJavaId() == inventory.getJavaId()) { translator.openInventory(session, inventory); translator.updateInventory(session, inventory); + openInv.setDisplayed(true); } else if (openInv != null && openInv.isPending()) { // Presumably, this inventory is no longer relevant, and the client doesn't care about it displayInventory(session, openInv); @@ -103,6 +104,7 @@ public class InventoryUtils { } else { translator.openInventory(session, inventory); translator.updateInventory(session, inventory); + inventory.setDisplayed(true); } } else { session.setOpenInventory(null); @@ -117,7 +119,7 @@ public class InventoryUtils { if (inventory != null) { InventoryTranslator translator = session.getInventoryTranslator(); translator.closeInventory(session, inventory); - if (confirm && !inventory.isPending() && !(translator instanceof LecternInventoryTranslator)) { + if (confirm && inventory.isDisplayed() && !inventory.isPending() && !(translator instanceof LecternInventoryTranslator)) { session.setClosingInventory(true); } } From 1be2a1ccac7dd78d42843ac85203757f891ea562 Mon Sep 17 00:00:00 2001 From: Camotoy <20743703+Camotoy@users.noreply.github.com> Date: Mon, 6 Mar 2023 20:53:54 -0500 Subject: [PATCH 19/59] Fix sleeping on vanilla/Fabric Fixes #3595 --- .../org/geysermc/geyser/entity/type/player/PlayerEntity.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) 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 74a55144e..78456646f 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 @@ -239,7 +239,10 @@ public class PlayerEntity extends LivingEntity { @Override public Vector3i setBedPosition(EntityMetadata, ?> entityMetadata) { - return bedPosition = super.setBedPosition(entityMetadata); + bedPosition = super.setBedPosition(entityMetadata); + // Fixes https://github.com/GeyserMC/Geyser/issues/3595 on vanilla 1.19.3 servers - did not happen on Paper + entityMetadata.getValue().ifPresent(pos -> this.setPosition(pos.toFloat())); + return bedPosition; } public void setAbsorptionHearts(FloatEntityMetadata entityMetadata) { From 81f0cbe5eded030ce1a1d745308040c2bf0c114a Mon Sep 17 00:00:00 2001 From: YouHaveTrouble Date: Tue, 7 Mar 2023 18:54:41 +0100 Subject: [PATCH 20/59] Assign permission to the command (#3599) Assigning permission allows people to possibly deny that permission to hide the command from tab complete list. Permission is defaulted to true, so command keeps current behavior by default. --- bootstrap/spigot/src/main/resources/plugin.yml | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/bootstrap/spigot/src/main/resources/plugin.yml b/bootstrap/spigot/src/main/resources/plugin.yml index e28b8981d..d8bc264a9 100644 --- a/bootstrap/spigot/src/main/resources/plugin.yml +++ b/bootstrap/spigot/src/main/resources/plugin.yml @@ -8,4 +8,8 @@ api-version: 1.13 commands: geyser: description: The main command for Geyser. - usage: /geyser \ No newline at end of file + usage: /geyser + permission: geyser.command +permissions: + geyser.command: + default: true From a5a4f5c3eaa633ac6a1b2b569084602ee2a72958 Mon Sep 17 00:00:00 2001 From: Kas-tle <26531652+Kas-tle@users.noreply.github.com> Date: Tue, 7 Mar 2023 18:59:21 -0800 Subject: [PATCH 21/59] Properly Cache Build Dependencies, Optimize Build Action, and Save Build Action Artifacts (#3593) * Attempt cache gradle properly * Save artifacts on standard build * Dont build on bad path and standardize pr action * Don't do caching for PR builds * Correct exclusion paths * Allow manual trigger of build action --- .github/workflows/build.yml | 70 +++++++++++++++++++++++++++++-- .github/workflows/pullrequest.yml | 31 ++++++++++---- 2 files changed, 90 insertions(+), 11 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index f83b9a901..2d1c84670 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -1,6 +1,18 @@ name: Build -on: [push] +on: + workflow_dispatch: + push: + paths-ignore: + - '.github/ISSUE_TEMPLATE/*.yml' + - '.github/actions/pullrequest.yml' + - '.idea/copyright/*.xml' + - '.gitignore' + - 'CONTRIBUTING.md' + - 'LICENSE' + - 'Jenkinsfile ' + - 'README.md' + - 'licenseheader.txt' jobs: build: @@ -15,12 +27,64 @@ jobs: with: java-version: 17 distribution: temurin - cache: gradle + + - name: Cache Gradle Packages + uses: actions/cache@v3 + with: + path: | + ~/.m2 + ~/.gradle/caches + ~/.gradle/wrapper + key: ${{ github.ref_name }}-gradle-${{ hashFiles('*.gradle.kts', 'gradle.properties', 'gradlew', 'gradle/*', 'gradle/**/*', 'build-logic/*', 'build-logic/**/**/**/*', '**/*.gradle.kts', '**/**/*.gradle.kts') }} + restore-keys: ${{ github.ref_name }}-gradle- - name: Build uses: gradle/gradle-build-action@v2 with: - arguments: build + arguments: build --no-daemon + + - name: Archive artifacts (Geyser Fabric) + uses: actions/upload-artifact@v3 + if: success() + with: + name: Geyser Fabric + path: bootstrap/fabric/build/libs/Geyser-Fabric.jar + if-no-files-found: error + - name: Archive artifacts (Geyser Standalone) + uses: actions/upload-artifact@v3 + if: success() + with: + name: Geyser Standalone + path: bootstrap/standalone/build/libs/Geyser-Standalone.jar + if-no-files-found: error + - name: Archive artifacts (Geyser Spigot) + uses: actions/upload-artifact@v3 + if: success() + with: + name: Geyser Spigot + path: bootstrap/spigot/build/libs/Geyser-Spigot.jar + if-no-files-found: error + - name: Archive artifacts (Geyser BungeeCord) + uses: actions/upload-artifact@v3 + if: success() + with: + name: Geyser BungeeCord + path: bootstrap/bungeecord/build/libs/Geyser-BungeeCord.jar + if-no-files-found: error + - name: Archive artifacts (Geyser Sponge) + uses: actions/upload-artifact@v3 + if: success() + with: + name: Geyser Sponge + path: bootstrap/sponge/build/libs/Geyser-Sponge.jar + if-no-files-found: error + - name: Archive artifacts (Geyser Velocity) + uses: actions/upload-artifact@v3 + if: success() + with: + name: Geyser Velocity + path: bootstrap/velocity/build/libs/Geyser-Velocity.jar + if-no-files-found: error - name: Publish to Maven Repository if: ${{ job.status == 'success' && github.repository == 'GeyserMC/Geyser' && github.ref_name == 'master' }} diff --git a/.github/workflows/pullrequest.yml b/.github/workflows/pullrequest.yml index 8d5f5c03c..9567fb414 100644 --- a/.github/workflows/pullrequest.yml +++ b/.github/workflows/pullrequest.yml @@ -1,6 +1,16 @@ name: Build Pull Request -on: [pull_request] +on: + pull_request: + paths-ignore: + - '.github/ISSUE_TEMPLATE/*.yml' + - '.idea/copyright/*.xml' + - '.gitignore' + - 'CONTRIBUTING.md' + - 'LICENSE' + - 'Jenkinsfile ' + - 'README.md' + - 'licenseheader.txt' jobs: build: @@ -11,7 +21,6 @@ jobs: with: java-version: 17 distribution: temurin - cache: gradle - name: Check if the author has forked the API repo uses: Kas-tle/ForkFinder@v1.0.1 @@ -44,38 +53,44 @@ jobs: build-root-directory: geyser - name: Archive artifacts (Geyser Fabric) - uses: actions/upload-artifact@v2 + uses: actions/upload-artifact@v3 if: success() with: name: Geyser Fabric path: geyser/bootstrap/fabric/build/libs/Geyser-Fabric.jar + if-no-files-found: error - name: Archive artifacts (Geyser Standalone) - uses: actions/upload-artifact@v2 + uses: actions/upload-artifact@v3 if: success() with: name: Geyser Standalone path: geyser/bootstrap/standalone/build/libs/Geyser-Standalone.jar + if-no-files-found: error - name: Archive artifacts (Geyser Spigot) - uses: actions/upload-artifact@v2 + uses: actions/upload-artifact@v3 if: success() with: name: Geyser Spigot path: geyser/bootstrap/spigot/build/libs/Geyser-Spigot.jar + if-no-files-found: error - name: Archive artifacts (Geyser BungeeCord) - uses: actions/upload-artifact@v2 + uses: actions/upload-artifact@v3 if: success() with: name: Geyser BungeeCord path: geyser/bootstrap/bungeecord/build/libs/Geyser-BungeeCord.jar + if-no-files-found: error - name: Archive artifacts (Geyser Sponge) - uses: actions/upload-artifact@v2 + uses: actions/upload-artifact@v3 if: success() with: name: Geyser Sponge path: geyser/bootstrap/sponge/build/libs/Geyser-Sponge.jar + if-no-files-found: error - name: Archive artifacts (Geyser Velocity) - uses: actions/upload-artifact@v2 + uses: actions/upload-artifact@v3 if: success() with: name: Geyser Velocity path: geyser/bootstrap/velocity/build/libs/Geyser-Velocity.jar + if-no-files-found: error From 2f23e5cb9c4c1d2f50b0dd341df0dfbac05f8bdf Mon Sep 17 00:00:00 2001 From: nils Date: Fri, 10 Mar 2023 06:13:20 +0100 Subject: [PATCH 22/59] fix: Fix legacy ping passthough (#3601) --- .../geysermc/geyser/ping/GeyserLegacyPingPassthrough.java | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/core/src/main/java/org/geysermc/geyser/ping/GeyserLegacyPingPassthrough.java b/core/src/main/java/org/geysermc/geyser/ping/GeyserLegacyPingPassthrough.java index a69d9bc3e..bdbe2f760 100644 --- a/core/src/main/java/org/geysermc/geyser/ping/GeyserLegacyPingPassthrough.java +++ b/core/src/main/java/org/geysermc/geyser/ping/GeyserLegacyPingPassthrough.java @@ -79,7 +79,8 @@ public class GeyserLegacyPingPassthrough implements IGeyserPingPassthrough, Runn try (Socket socket = new Socket()) { String address = geyser.getConfig().getRemote().address(); int port = geyser.getConfig().getRemote().port(); - socket.connect(new InetSocketAddress(address, port), 5000); + InetSocketAddress endpoint = new InetSocketAddress(address, port); + socket.connect(endpoint, 5000); ByteArrayOutputStream byteArrayStream = new ByteArrayOutputStream(); try (DataOutputStream handshake = new DataOutputStream(byteArrayStream)) { @@ -103,7 +104,8 @@ public class GeyserLegacyPingPassthrough implements IGeyserPingPassthrough, Runn HAProxyProxiedProtocol.TCP4.byteValue() : HAProxyProxiedProtocol.TCP6.byteValue()); byte[] srcAddrBytes = NetUtil.createByteArrayFromIpAddressString( ((InetSocketAddress) socket.getLocalSocketAddress()).getAddress().getHostAddress()); - byte[] dstAddrBytes = NetUtil.createByteArrayFromIpAddressString(address); + byte[] dstAddrBytes = NetUtil.createByteArrayFromIpAddressString( + endpoint.getAddress().getHostAddress()); dataOutputStream.writeShort(srcAddrBytes.length + dstAddrBytes.length + 4); dataOutputStream.write(srcAddrBytes); dataOutputStream.write(dstAddrBytes); From b3f1c64249b576a75643ed05fcba3d9bb52478c3 Mon Sep 17 00:00:00 2001 From: Camotoy <20743703+Camotoy@users.noreply.github.com> Date: Fri, 10 Mar 2023 20:51:51 -0500 Subject: [PATCH 23/59] Base changes for Java 1.19.4 support --- core/build.gradle.kts | 4 --- .../geyser/entity/EntityDefinitions.java | 2 +- .../type/living/monster/EndermanEntity.java | 10 +++----- .../geyser/level/physics/Direction.java | 20 +++++++-------- .../java/JavaUpdateRecipesTranslator.java | 4 +-- .../entity/JavaSetPassengersTranslator.java | 10 ++++++++ .../player/JavaPlayerPositionTranslator.java | 25 ------------------- .../java/level/JavaBlockEventTranslator.java | 10 ++++---- .../java/level/JavaLevelEventTranslator.java | 2 +- gradle/libs.versions.toml | 4 +-- 10 files changed, 32 insertions(+), 59 deletions(-) diff --git a/core/build.gradle.kts b/core/build.gradle.kts index 4265a9c35..9ba399674 100644 --- a/core/build.gradle.kts +++ b/core/build.gradle.kts @@ -31,10 +31,6 @@ dependencies { exclude("com.github.GeyserMC", "mcauthlib") } - api(libs.packetlib) { - exclude("io.netty", "netty-all") - } - implementation(libs.raknet) { exclude("io.netty", "*"); } diff --git a/core/src/main/java/org/geysermc/geyser/entity/EntityDefinitions.java b/core/src/main/java/org/geysermc/geyser/entity/EntityDefinitions.java index b97e23847..3271a962f 100644 --- a/core/src/main/java/org/geysermc/geyser/entity/EntityDefinitions.java +++ b/core/src/main/java/org/geysermc/geyser/entity/EntityDefinitions.java @@ -486,7 +486,7 @@ public final class EntityDefinitions { ENDERMAN = EntityDefinition.inherited(EndermanEntity::new, mobEntityBase) .type(EntityType.ENDERMAN) .height(2.9f).width(0.6f) - .addTranslator(MetadataType.BLOCK_STATE, EndermanEntity::setCarriedBlock) + .addTranslator(MetadataType.OPTIONAL_BLOCK_STATE, EndermanEntity::setCarriedBlock) .addTranslator(MetadataType.BOOLEAN, EndermanEntity::setScreaming) .addTranslator(MetadataType.BOOLEAN, EndermanEntity::setAngry) .build(); diff --git a/core/src/main/java/org/geysermc/geyser/entity/type/living/monster/EndermanEntity.java b/core/src/main/java/org/geysermc/geyser/entity/type/living/monster/EndermanEntity.java index 04b46997d..1d1f61af1 100644 --- a/core/src/main/java/org/geysermc/geyser/entity/type/living/monster/EndermanEntity.java +++ b/core/src/main/java/org/geysermc/geyser/entity/type/living/monster/EndermanEntity.java @@ -28,6 +28,7 @@ package org.geysermc.geyser.entity.type.living.monster; import com.github.steveice10.mc.protocol.data.game.entity.metadata.EntityMetadata; import com.github.steveice10.mc.protocol.data.game.entity.metadata.OptionalIntMetadataType; import com.github.steveice10.mc.protocol.data.game.entity.metadata.type.BooleanEntityMetadata; +import com.github.steveice10.mc.protocol.data.game.entity.metadata.type.IntEntityMetadata; import com.nukkitx.math.vector.Vector3f; import com.nukkitx.protocol.bedrock.data.SoundEvent; import com.nukkitx.protocol.bedrock.data.entity.EntityData; @@ -45,13 +46,8 @@ public class EndermanEntity extends MonsterEntity { super(session, entityId, geyserId, uuid, definition, position, motion, yaw, pitch, headYaw); } - public void setCarriedBlock(EntityMetadata entityMetadata) { - int bedrockBlockId; - if (entityMetadata.getValue().isPresent()) { - bedrockBlockId = session.getBlockMappings().getBedrockBlockId(entityMetadata.getValue().getAsInt()); - } else { - bedrockBlockId = session.getBlockMappings().getBedrockAirId(); - } + public void setCarriedBlock(IntEntityMetadata entityMetadata) { + int bedrockBlockId = session.getBlockMappings().getBedrockBlockId(entityMetadata.getPrimitiveValue());; dirtyMetadata.put(EntityData.CARRIED_BLOCK, bedrockBlockId); } diff --git a/core/src/main/java/org/geysermc/geyser/level/physics/Direction.java b/core/src/main/java/org/geysermc/geyser/level/physics/Direction.java index 48c50bc69..95c0cd119 100644 --- a/core/src/main/java/org/geysermc/geyser/level/physics/Direction.java +++ b/core/src/main/java/org/geysermc/geyser/level/physics/Direction.java @@ -25,19 +25,18 @@ package org.geysermc.geyser.level.physics; -import com.github.steveice10.mc.protocol.data.game.level.block.value.PistonValue; import com.nukkitx.math.vector.Vector3i; import lombok.Getter; import javax.annotation.Nonnull; public enum Direction { - DOWN(1, Vector3i.from(0, -1, 0), Axis.Y, PistonValue.DOWN), - UP(0, Vector3i.UNIT_Y, Axis.Y, PistonValue.UP), - NORTH(3, Vector3i.from(0, 0, -1), Axis.Z, PistonValue.NORTH), - SOUTH(2, Vector3i.UNIT_Z, Axis.Z, PistonValue.SOUTH), - WEST(5, Vector3i.from(-1, 0, 0), Axis.X, PistonValue.WEST), - EAST(4, Vector3i.UNIT_X, Axis.X, PistonValue.EAST); + DOWN(1, Vector3i.from(0, -1, 0), Axis.Y, com.github.steveice10.mc.protocol.data.game.entity.object.Direction.DOWN), + UP(0, Vector3i.UNIT_Y, Axis.Y, com.github.steveice10.mc.protocol.data.game.entity.object.Direction.UP), + NORTH(3, Vector3i.from(0, 0, -1), Axis.Z, com.github.steveice10.mc.protocol.data.game.entity.object.Direction.NORTH), + SOUTH(2, Vector3i.UNIT_Z, Axis.Z, com.github.steveice10.mc.protocol.data.game.entity.object.Direction.SOUTH), + WEST(5, Vector3i.from(-1, 0, 0), Axis.X, com.github.steveice10.mc.protocol.data.game.entity.object.Direction.WEST), + EAST(4, Vector3i.UNIT_X, Axis.X, com.github.steveice10.mc.protocol.data.game.entity.object.Direction.EAST); public static final Direction[] VALUES = values(); @@ -46,10 +45,9 @@ public enum Direction { private final Vector3i unitVector; @Getter private final Axis axis; - @Getter - private final PistonValue pistonValue; + private final com.github.steveice10.mc.protocol.data.game.entity.object.Direction pistonValue; - Direction(int reversedId, Vector3i unitVector, Axis axis, PistonValue pistonValue) { + Direction(int reversedId, Vector3i unitVector, Axis axis, com.github.steveice10.mc.protocol.data.game.entity.object.Direction pistonValue) { this.reversedId = reversedId; this.unitVector = unitVector; this.axis = axis; @@ -69,7 +67,7 @@ public enum Direction { } @Nonnull - public static Direction fromPistonValue(PistonValue pistonValue) { + public static Direction fromPistonValue(com.github.steveice10.mc.protocol.data.game.entity.object.Direction pistonValue) { for (Direction direction : VALUES) { if (direction.pistonValue == pistonValue) { return direction; diff --git a/core/src/main/java/org/geysermc/geyser/translator/protocol/java/JavaUpdateRecipesTranslator.java b/core/src/main/java/org/geysermc/geyser/translator/protocol/java/JavaUpdateRecipesTranslator.java index 6159e9dd5..9923c0a16 100644 --- a/core/src/main/java/org/geysermc/geyser/translator/protocol/java/JavaUpdateRecipesTranslator.java +++ b/core/src/main/java/org/geysermc/geyser/translator/protocol/java/JavaUpdateRecipesTranslator.java @@ -29,9 +29,9 @@ import com.github.steveice10.mc.protocol.data.game.entity.metadata.ItemStack; import com.github.steveice10.mc.protocol.data.game.recipe.Ingredient; import com.github.steveice10.mc.protocol.data.game.recipe.Recipe; import com.github.steveice10.mc.protocol.data.game.recipe.RecipeType; +import com.github.steveice10.mc.protocol.data.game.recipe.data.LegacyUpgradeRecipeData; import com.github.steveice10.mc.protocol.data.game.recipe.data.ShapedRecipeData; import com.github.steveice10.mc.protocol.data.game.recipe.data.ShapelessRecipeData; -import com.github.steveice10.mc.protocol.data.game.recipe.data.SmithingRecipeData; import com.github.steveice10.mc.protocol.data.game.recipe.data.StoneCuttingRecipeData; import com.github.steveice10.mc.protocol.packet.ingame.clientbound.ClientboundUpdateRecipesPacket; import com.nukkitx.protocol.bedrock.data.inventory.CraftingData; @@ -137,7 +137,7 @@ public class JavaUpdateRecipesTranslator extends PacketTranslator { // Required to translate these as of 1.18.10, or else they cannot be crafted - SmithingRecipeData recipeData = (SmithingRecipeData) recipe.getData(); + LegacyUpgradeRecipeData recipeData = (LegacyUpgradeRecipeData) recipe.getData(); ItemData output = ItemTranslator.translateToBedrock(session, recipeData.getResult()); for (ItemStack base : recipeData.getBase().getOptions()) { ItemDescriptorWithCount bedrockBase = ItemDescriptorWithCount.fromItem(ItemTranslator.translateToBedrock(session, base)); diff --git a/core/src/main/java/org/geysermc/geyser/translator/protocol/java/entity/JavaSetPassengersTranslator.java b/core/src/main/java/org/geysermc/geyser/translator/protocol/java/entity/JavaSetPassengersTranslator.java index 63985d9ae..8fa528cb7 100644 --- a/core/src/main/java/org/geysermc/geyser/translator/protocol/java/entity/JavaSetPassengersTranslator.java +++ b/core/src/main/java/org/geysermc/geyser/translator/protocol/java/entity/JavaSetPassengersTranslator.java @@ -91,6 +91,16 @@ public class JavaSetPassengersTranslator extends PacketTranslator 1)); // Force an update to the passenger metadata passenger.updateBedrockMetadata(); + + if (passenger == session.getPlayerEntity()) { + //TODO test + if (session.getMountVehicleScheduledFuture() != null) { + // Cancel this task as it is now unnecessary. + // Note that this isn't present in JavaSetPassengersTranslator as that code is not called for players + // as of Java 1.19.3, but the scheduled future checks for the vehicle being null anyway. + session.getMountVehicleScheduledFuture().cancel(false); + } + } } } diff --git a/core/src/main/java/org/geysermc/geyser/translator/protocol/java/entity/player/JavaPlayerPositionTranslator.java b/core/src/main/java/org/geysermc/geyser/translator/protocol/java/entity/player/JavaPlayerPositionTranslator.java index 5f182805e..ff745a546 100644 --- a/core/src/main/java/org/geysermc/geyser/translator/protocol/java/entity/player/JavaPlayerPositionTranslator.java +++ b/core/src/main/java/org/geysermc/geyser/translator/protocol/java/entity/player/JavaPlayerPositionTranslator.java @@ -30,20 +30,16 @@ import com.github.steveice10.mc.protocol.packet.ingame.clientbound.entity.player import com.github.steveice10.mc.protocol.packet.ingame.serverbound.level.ServerboundAcceptTeleportationPacket; import com.github.steveice10.mc.protocol.packet.ingame.serverbound.player.ServerboundMovePlayerPosRotPacket; import com.nukkitx.math.vector.Vector3f; -import com.nukkitx.protocol.bedrock.data.entity.EntityLinkData; import com.nukkitx.protocol.bedrock.packet.ChunkRadiusUpdatedPacket; import com.nukkitx.protocol.bedrock.packet.MovePlayerPacket; import com.nukkitx.protocol.bedrock.packet.RespawnPacket; -import com.nukkitx.protocol.bedrock.packet.SetEntityLinkPacket; import org.geysermc.geyser.entity.EntityDefinitions; -import org.geysermc.geyser.entity.type.Entity; import org.geysermc.geyser.entity.type.player.SessionPlayerEntity; import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.session.cache.TeleportCache; import org.geysermc.geyser.translator.protocol.PacketTranslator; import org.geysermc.geyser.translator.protocol.Translator; import org.geysermc.geyser.util.ChunkUtils; -import org.geysermc.geyser.util.EntityUtils; @Translator(packet = ClientboundPlayerPositionPacket.class) public class JavaPlayerPositionTranslator extends PacketTranslator { @@ -101,27 +97,6 @@ public class JavaPlayerPositionTranslator extends PacketTranslator 1); - entity.updateBedrockMetadata(); - - if (session.getMountVehicleScheduledFuture() != null) { - // Cancel this task as it is now unnecessary. - // Note that this isn't present in JavaSetPassengersTranslator as that code is not called for players - // as of Java 1.19.3, but the scheduled future checks for the vehicle being null anyway. - session.getMountVehicleScheduledFuture().cancel(false); - } - } - // If coordinates are relative, then add to the existing coordinate double newX = packet.getX() + (packet.getRelative().contains(PositionElement.X) ? entity.getPosition().getX() : 0); 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 8824f88c4..4d35db562 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 @@ -57,13 +57,13 @@ public class JavaBlockEventTranslator extends PacketTranslator { data = 4; pos = pos.add(0, -0.9f, 0); diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 8858edc68..d83aaa8f4 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -11,8 +11,7 @@ websocket = "1.5.1" protocol = "2.9.17-20230217.002312-1" raknet = "1.6.28-20220125.214016-6" mcauthlib = "d9d773e" -mcprotocollib = "1.19.3-20230107.194116-10" -packetlib = "3.0.1" +mcprotocollib = "1.19.4-SNAPSHOT" adventure = "4.12.0-20220629.025215-9" adventure-platform = "4.1.2" junit = "5.9.2" @@ -84,7 +83,6 @@ gson = { group = "com.google.code.gson", name = "gson", version.ref = "gson" } junit = { group = "org.junit.jupiter", name = "junit-jupiter", version.ref = "junit" } mcauthlib = { group = "com.github.GeyserMC", name = "MCAuthLib", version.ref = "mcauthlib" } mcprotocollib = { group = "com.github.steveice10", name = "mcprotocollib", version.ref = "mcprotocollib" } -packetlib = { group = "com.github.steveice10", name = "packetlib", version.ref = "packetlib" } protocol = { group = "com.nukkitx.protocol", name = "bedrock-v567", version.ref = "protocol" } raknet = { group = "com.nukkitx.network", name = "raknet", version.ref = "raknet" } sponge-api = { group = "org.spongepowered", name = "spongeapi", version.ref = "sponge" } From 777c69a21e629822a6317b91a9d39bfc992346bc Mon Sep 17 00:00:00 2001 From: Camotoy <20743703+Camotoy@users.noreply.github.com> Date: Sun, 12 Mar 2023 13:06:13 -0400 Subject: [PATCH 24/59] Remove armor quick change config This is now in vanilla 1.19.4. --- .../configuration/GeyserConfiguration.java | 2 - .../GeyserJacksonConfiguration.java | 3 -- ...BedrockInventoryTransactionTranslator.java | 37 ------------------- core/src/main/resources/config.yml | 4 -- core/src/main/resources/languages | 2 +- core/src/main/resources/mappings | 2 +- 6 files changed, 2 insertions(+), 48 deletions(-) diff --git a/core/src/main/java/org/geysermc/geyser/configuration/GeyserConfiguration.java b/core/src/main/java/org/geysermc/geyser/configuration/GeyserConfiguration.java index 4843df72b..ea4c31876 100644 --- a/core/src/main/java/org/geysermc/geyser/configuration/GeyserConfiguration.java +++ b/core/src/main/java/org/geysermc/geyser/configuration/GeyserConfiguration.java @@ -83,8 +83,6 @@ public interface GeyserConfiguration { boolean isDisableBedrockScaffolding(); - boolean isAlwaysQuickChangeArmor(); - EmoteOffhandWorkaroundOption getEmoteOffhandWorkaround(); String getDefaultLocale(); diff --git a/core/src/main/java/org/geysermc/geyser/configuration/GeyserJacksonConfiguration.java b/core/src/main/java/org/geysermc/geyser/configuration/GeyserJacksonConfiguration.java index dc675319b..bbfa37ec2 100644 --- a/core/src/main/java/org/geysermc/geyser/configuration/GeyserJacksonConfiguration.java +++ b/core/src/main/java/org/geysermc/geyser/configuration/GeyserJacksonConfiguration.java @@ -111,9 +111,6 @@ public abstract class GeyserJacksonConfiguration implements GeyserConfiguration @JsonProperty("disable-bedrock-scaffolding") private boolean disableBedrockScaffolding = false; - @JsonProperty("always-quick-change-armor") - private boolean alwaysQuickChangeArmor = false; - @JsonDeserialize(using = EmoteOffhandWorkaroundOption.Deserializer.class) @JsonProperty("emote-offhand-workaround") private EmoteOffhandWorkaroundOption emoteOffhandWorkaround = EmoteOffhandWorkaroundOption.DISABLED; 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 6992dada4..c770b070f 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 @@ -356,43 +356,6 @@ public class BedrockInventoryTransactionTranslator extends PacketTranslator legacySlots = packet.getLegacySlots(); - if (packet.getActions().size() == 1 && legacySlots.size() > 0) { - InventoryActionData actionData = packet.getActions().get(0); - LegacySetItemSlotData slotData = legacySlots.get(0); - if (slotData.getContainerId() == 6 && actionData.getToItem().getId() != 0) { - // The player is trying to swap out an armor piece that already has an item in it - if (session.getGeyser().getConfig().isAlwaysQuickChangeArmor()) { - // Java doesn't know when a player is in its own inventory and not, so we - // can abuse this feature to send a swap inventory packet - int bedrockHotbarSlot = packet.getHotbarSlot(); - Click click = InventoryUtils.getClickForHotbarSwap(bedrockHotbarSlot); - if (click != null && slotData.getSlots().length != 0) { - Inventory playerInventory = session.getPlayerInventory(); - // Bedrock sends us the index of the slot in the armor container; armor in Java - // Edition is offset by 5 in the player inventory - int armorSlot = slotData.getSlots()[0] + 5; - GeyserItemStack armorSlotItem = playerInventory.getItem(armorSlot); - GeyserItemStack hotbarItem = playerInventory.getItem(playerInventory.getOffsetForHotbar(bedrockHotbarSlot)); - playerInventory.setItem(armorSlot, hotbarItem, session); - playerInventory.setItem(bedrockHotbarSlot, armorSlotItem, session); - - Int2ObjectMap changedSlots = new Int2ObjectOpenHashMap<>(2); - changedSlots.put(armorSlot, hotbarItem.getItemStack()); - changedSlots.put(bedrockHotbarSlot, armorSlotItem.getItemStack()); - - ServerboundContainerClickPacket clickPacket = new ServerboundContainerClickPacket( - playerInventory.getJavaId(), playerInventory.getStateId(), armorSlot, - click.actionType, click.action, null, changedSlots); - session.sendDownstreamPacket(clickPacket); - } - } else { - // Disallowed; let's revert - session.getInventoryTranslator().updateInventory(session, session.getPlayerInventory()); - } - } - } } case 2 -> { int blockState = session.getGameMode() == GameMode.CREATIVE ? diff --git a/core/src/main/resources/config.yml b/core/src/main/resources/config.yml index eb5b7e73c..421ad4c1c 100644 --- a/core/src/main/resources/config.yml +++ b/core/src/main/resources/config.yml @@ -130,10 +130,6 @@ show-coordinates: true # Whether Bedrock players are blocked from performing their scaffolding-style bridging. disable-bedrock-scaffolding: false -# Whether Bedrock players can right-click outside of their inventory to replace armor in their inventory, even if the -# armor slot is already occupied (which Java Edition doesn't allow) -always-quick-change-armor: false - # If set, when a Bedrock player performs any emote, it will swap the offhand and mainhand items, just like the Java Edition keybind # There are three options this can be set to: # disabled - the default/fallback, which doesn't apply this workaround diff --git a/core/src/main/resources/languages b/core/src/main/resources/languages index 24be9ef7f..f6685c4cc 160000 --- a/core/src/main/resources/languages +++ b/core/src/main/resources/languages @@ -1 +1 @@ -Subproject commit 24be9ef7f850f7d180650a65792c266c709cadf5 +Subproject commit f6685c4ccc6e77b07402d45cb41213559004b7d6 diff --git a/core/src/main/resources/mappings b/core/src/main/resources/mappings index 677c5b087..eab643ddb 160000 --- a/core/src/main/resources/mappings +++ b/core/src/main/resources/mappings @@ -1 +1 @@ -Subproject commit 677c5b0872d2f0c99ad834c0ca49a0ae3b45fde3 +Subproject commit eab643ddbaf31c4d76531376838f8fd30633bb8e From 2436b2b1bebfec1823f87e79b0725b02f042765b Mon Sep 17 00:00:00 2001 From: Camotoy <20743703+Camotoy@users.noreply.github.com> Date: Sun, 12 Mar 2023 23:51:51 -0400 Subject: [PATCH 25/59] Damage will now show again. --- .../org/geysermc/geyser/level/DamageType.java | 29 ++++++++++ .../geyser/session/GeyserSession.java | 5 +- .../protocol/java/JavaLoginTranslator.java | 16 ++++++ .../entity/JavaEntityEventTranslator.java | 9 ---- .../spawn/JavaDamageEventTranslator.java | 54 +++++++++++++++++++ .../java/level/JavaBlockEventTranslator.java | 8 +-- 6 files changed, 107 insertions(+), 14 deletions(-) create mode 100644 core/src/main/java/org/geysermc/geyser/level/DamageType.java create mode 100644 core/src/main/java/org/geysermc/geyser/translator/protocol/java/entity/spawn/JavaDamageEventTranslator.java diff --git a/core/src/main/java/org/geysermc/geyser/level/DamageType.java b/core/src/main/java/org/geysermc/geyser/level/DamageType.java new file mode 100644 index 000000000..50848da90 --- /dev/null +++ b/core/src/main/java/org/geysermc/geyser/level/DamageType.java @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2019-2023 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; + +public class DamageType { +} diff --git a/core/src/main/java/org/geysermc/geyser/session/GeyserSession.java b/core/src/main/java/org/geysermc/geyser/session/GeyserSession.java index 11467253e..80e2be5cd 100644 --- a/core/src/main/java/org/geysermc/geyser/session/GeyserSession.java +++ b/core/src/main/java/org/geysermc/geyser/session/GeyserSession.java @@ -116,6 +116,7 @@ import org.geysermc.geyser.inventory.Inventory; import org.geysermc.geyser.inventory.PlayerInventory; import org.geysermc.geyser.inventory.recipe.GeyserRecipe; import org.geysermc.geyser.inventory.recipe.GeyserStonecutterData; +import org.geysermc.geyser.level.DamageType; import org.geysermc.geyser.level.JavaDimension; import org.geysermc.geyser.level.WorldManager; import org.geysermc.geyser.level.physics.CollisionManager; @@ -345,6 +346,8 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource { private final Int2ObjectMap chatTypes = new Int2ObjectOpenHashMap<>(7); + private final Int2ObjectMap damageTypes = new Int2ObjectOpenHashMap<>(); + @Setter private int breakingBlock; @@ -1012,7 +1015,7 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource { // Server is offline, probably disconnectMessage = GeyserLocale.getPlayerLocaleString("geyser.network.remote.server_offline", locale()); } else { - disconnectMessage = MessageTranslator.convertMessageLenient(event.getReason()); + disconnectMessage = MessageTranslator.convertMessage(event.getReason()); } if (downstream instanceof LocalSession) { diff --git a/core/src/main/java/org/geysermc/geyser/translator/protocol/java/JavaLoginTranslator.java b/core/src/main/java/org/geysermc/geyser/translator/protocol/java/JavaLoginTranslator.java index 09d117087..c88c9aae2 100644 --- a/core/src/main/java/org/geysermc/geyser/translator/protocol/java/JavaLoginTranslator.java +++ b/core/src/main/java/org/geysermc/geyser/translator/protocol/java/JavaLoginTranslator.java @@ -27,6 +27,7 @@ package org.geysermc.geyser.translator.protocol.java; import com.github.steveice10.mc.protocol.packet.ingame.clientbound.ClientboundLoginPacket; import com.github.steveice10.mc.protocol.packet.ingame.serverbound.ServerboundCustomPayloadPacket; +import com.github.steveice10.opennbt.SNBTIO; import com.github.steveice10.opennbt.tag.builtin.CompoundTag; import com.github.steveice10.opennbt.tag.builtin.IntTag; import com.nukkitx.protocol.bedrock.data.GameRuleData; @@ -38,6 +39,7 @@ import it.unimi.dsi.fastutil.ints.Int2ObjectMap; import org.geysermc.floodgate.pluginmessage.PluginMessageChannels; import org.geysermc.geyser.api.network.AuthType; import org.geysermc.geyser.entity.type.player.SessionPlayerEntity; +import org.geysermc.geyser.level.DamageType; import org.geysermc.geyser.level.JavaDimension; import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.text.TextDecoration; @@ -49,6 +51,7 @@ import org.geysermc.geyser.util.DimensionUtils; import org.geysermc.geyser.util.JavaCodecUtil; import org.geysermc.geyser.util.PluginMessageUtils; +import java.io.IOException; import java.util.Map; @Translator(packet = ClientboundLoginPacket.class) @@ -64,6 +67,12 @@ public class JavaLoginTranslator extends PacketTranslator chatTypes = session.getChatTypes(); chatTypes.clear(); for (CompoundTag tag : JavaCodecUtil.iterateAsTag(packet.getRegistry().get("minecraft:chat_type"))) { @@ -78,6 +87,13 @@ public class JavaLoginTranslator extends PacketTranslator damageTypes = session.getDamageTypes(); + damageTypes.clear(); + for (CompoundTag tag : JavaCodecUtil.iterateAsTag(packet.getRegistry().get("minecraft:damage_type"))) { + int id = ((IntTag) tag.get("id")).getValue(); + CompoundTag element = tag.get("element"); + } + // If the player is already initialized and a join game packet is sent, they // are swapping servers if (session.isSpawned()) { diff --git a/core/src/main/java/org/geysermc/geyser/translator/protocol/java/entity/JavaEntityEventTranslator.java b/core/src/main/java/org/geysermc/geyser/translator/protocol/java/entity/JavaEntityEventTranslator.java index 3d44ce2fd..8b492027b 100644 --- a/core/src/main/java/org/geysermc/geyser/translator/protocol/java/entity/JavaEntityEventTranslator.java +++ b/core/src/main/java/org/geysermc/geyser/translator/protocol/java/entity/JavaEntityEventTranslator.java @@ -83,15 +83,6 @@ public class JavaEntityEventTranslator extends PacketTranslator { + + @Override + public void translate(GeyserSession session, ClientboundDamageEventPacket packet) { + Entity entity = session.getEntityCache().getEntityByJavaId(packet.getEntityId()); + if (entity == null) { + return; + } + + EntityEventPacket entityEventPacket = new EntityEventPacket(); + entityEventPacket.setRuntimeEntityId(entity.getGeyserId()); + entityEventPacket.setType(EntityEventType.HURT); + DamageType damageType = session.getDamageTypes().get(packet.getSourceTypeId()); + + session.sendUpstreamPacket(entityEventPacket); + } +} 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 4d35db562..149e8356e 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 @@ -57,10 +57,10 @@ public class JavaBlockEventTranslator extends PacketTranslator Date: Tue, 14 Mar 2023 07:44:52 -0400 Subject: [PATCH 26/59] Fix nameTagVisibility nullability --- .../geysermc/geyser/entity/EntityDefinitions.java | 1 - .../java/org/geysermc/geyser/scoreboard/Team.java | 13 ++++++++++++- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/core/src/main/java/org/geysermc/geyser/entity/EntityDefinitions.java b/core/src/main/java/org/geysermc/geyser/entity/EntityDefinitions.java index 3271a962f..41a88f64f 100644 --- a/core/src/main/java/org/geysermc/geyser/entity/EntityDefinitions.java +++ b/core/src/main/java/org/geysermc/geyser/entity/EntityDefinitions.java @@ -858,7 +858,6 @@ public final class EntityDefinitions { { EntityDefinition abstractHorseEntityBase = EntityDefinition.inherited(AbstractHorseEntity::new, ageableEntityBase) .addTranslator(MetadataType.BYTE, AbstractHorseEntity::setHorseFlags) - .addTranslator(null) // UUID of owner .build(); CAMEL = EntityDefinition.inherited(CamelEntity::new, abstractHorseEntityBase) .type(EntityType.CAMEL) diff --git a/core/src/main/java/org/geysermc/geyser/scoreboard/Team.java b/core/src/main/java/org/geysermc/geyser/scoreboard/Team.java index 7738f5f42..b2e9043b5 100644 --- a/core/src/main/java/org/geysermc/geyser/scoreboard/Team.java +++ b/core/src/main/java/org/geysermc/geyser/scoreboard/Team.java @@ -33,7 +33,9 @@ import lombok.Getter; import lombok.Setter; import lombok.experimental.Accessors; +import javax.annotation.Nullable; import java.util.HashSet; +import java.util.Objects; import java.util.Set; @Getter @@ -44,7 +46,7 @@ public final class Team { @Getter(AccessLevel.PACKAGE) private final Set entities; - @Setter private NameTagVisibility nameTagVisibility; + @Setter @Nullable private NameTagVisibility nameTagVisibility; @Setter private TeamColor color; private final TeamData currentData; @@ -187,6 +189,11 @@ public final class Team { } public boolean isVisibleFor(String entity) { + if (nameTagVisibility == null) { + // Null - normal behavior + return true; + } + return switch (nameTagVisibility) { case HIDE_FOR_OTHER_TEAMS -> { // Player must be in a team in order for HIDE_FOR_OTHER_TEAMS to be triggered @@ -199,6 +206,10 @@ public final class Team { }; } + public NameTagVisibility getNameTagVisibility() { + return Objects.requireNonNullElse(this.nameTagVisibility, NameTagVisibility.ALWAYS); + } + @Override public int hashCode() { return id.hashCode(); From 03c076796594c32b9567a482c6aa7c4c40a00ebb Mon Sep 17 00:00:00 2001 From: Camotoy <20743703+Camotoy@users.noreply.github.com> Date: Tue, 14 Mar 2023 15:37:57 -0400 Subject: [PATCH 27/59] Finish 1.19.4 support and add Bedrock 1.19.70 support --- README.md | 2 +- .../org/geysermc/geyser/level/DamageType.java | 29 - .../geysermc/geyser/network/GameProtocol.java | 14 +- .../populator/BlockRegistryPopulator.java | 10 + .../populator/ItemRegistryPopulator.java | 6 + .../geyser/session/GeyserSession.java | 3 - ...BedrockInventoryTransactionTranslator.java | 13 +- .../protocol/java/JavaLoginTranslator.java | 16 - .../JavaDamageEventTranslator.java | 6 +- .../bedrock/block_palette.1_19_70.nbt | Bin 0 -> 75552 bytes .../bedrock/creative_items.1_19_70.json | 5452 +++++++++++++++++ .../bedrock/runtime_item_states.1_19_70.json | 4786 +++++++++++++++ gradle/libs.versions.toml | 2 +- 13 files changed, 10267 insertions(+), 72 deletions(-) delete mode 100644 core/src/main/java/org/geysermc/geyser/level/DamageType.java rename core/src/main/java/org/geysermc/geyser/translator/protocol/java/entity/{spawn => }/JavaDamageEventTranslator.java (92%) create mode 100644 core/src/main/resources/bedrock/block_palette.1_19_70.nbt create mode 100644 core/src/main/resources/bedrock/creative_items.1_19_70.json create mode 100644 core/src/main/resources/bedrock/runtime_item_states.1_19_70.json diff --git a/README.md b/README.md index 2fd09eb8f..cfc99a8ba 100644 --- a/README.md +++ b/README.md @@ -17,7 +17,7 @@ The ultimate goal of this project is to allow Minecraft: Bedrock Edition users t Special thanks to the DragonProxy project for being a trailblazer in protocol translation and for all the team members who have joined us here! -### Currently supporting Minecraft Bedrock 1.19.20 - 1.19.63 and Minecraft Java 1.19.3. +### Currently supporting Minecraft Bedrock 1.19.30 - 1.19.70 and Minecraft Java 1.19.4. ## Setting Up Take a look [here](https://wiki.geysermc.org/geyser/setup/) for how to set up Geyser. diff --git a/core/src/main/java/org/geysermc/geyser/level/DamageType.java b/core/src/main/java/org/geysermc/geyser/level/DamageType.java deleted file mode 100644 index 50848da90..000000000 --- a/core/src/main/java/org/geysermc/geyser/level/DamageType.java +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Copyright (c) 2019-2023 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; - -public class DamageType { -} diff --git a/core/src/main/java/org/geysermc/geyser/network/GameProtocol.java b/core/src/main/java/org/geysermc/geyser/network/GameProtocol.java index 8a3d068f9..16f4abd04 100644 --- a/core/src/main/java/org/geysermc/geyser/network/GameProtocol.java +++ b/core/src/main/java/org/geysermc/geyser/network/GameProtocol.java @@ -28,8 +28,6 @@ package org.geysermc.geyser.network; import com.github.steveice10.mc.protocol.codec.MinecraftCodec; import com.github.steveice10.mc.protocol.codec.PacketCodec; import com.nukkitx.protocol.bedrock.BedrockPacketCodec; -import com.nukkitx.protocol.bedrock.v544.Bedrock_v544; -import com.nukkitx.protocol.bedrock.v545.Bedrock_v545; import com.nukkitx.protocol.bedrock.v554.Bedrock_v554; import com.nukkitx.protocol.bedrock.v557.Bedrock_v557; import com.nukkitx.protocol.bedrock.v560.Bedrock_v560; @@ -49,7 +47,10 @@ public final class GameProtocol { * Default Bedrock codec that should act as a fallback. Should represent the latest available * release of the game that Geyser supports. */ - public static final BedrockPacketCodec DEFAULT_BEDROCK_CODEC = Bedrock_v567.V567_CODEC; + public static final BedrockPacketCodec DEFAULT_BEDROCK_CODEC = Bedrock_v567patch.BEDROCK_V567PATCH.toBuilder() + .protocolVersion(575) + .minecraftVersion("1.19.70") + .build(); /** * A list of all supported Bedrock versions that can join Geyser */ @@ -62,10 +63,6 @@ public final class GameProtocol { private static final PacketCodec DEFAULT_JAVA_CODEC = MinecraftCodec.CODEC; static { - SUPPORTED_BEDROCK_CODECS.add(Bedrock_v544.V544_CODEC); - SUPPORTED_BEDROCK_CODECS.add(Bedrock_v545.V545_CODEC.toBuilder() - .minecraftVersion("1.19.21/1.19.22") - .build()); SUPPORTED_BEDROCK_CODECS.add(Bedrock_v554.V554_CODEC.toBuilder() .minecraftVersion("1.19.30/1.19.31") .build()); @@ -75,11 +72,12 @@ public final class GameProtocol { SUPPORTED_BEDROCK_CODECS.add(Bedrock_v560.V560_CODEC.toBuilder() .minecraftVersion("1.19.50/1.19.51") .build()); - SUPPORTED_BEDROCK_CODECS.add(DEFAULT_BEDROCK_CODEC); + SUPPORTED_BEDROCK_CODECS.add(Bedrock_v567.V567_CODEC); SUPPORTED_BEDROCK_CODECS.add(Bedrock_v567patch.BEDROCK_V567PATCH.toBuilder() .protocolVersion(568) .minecraftVersion("1.19.62") .build()); + SUPPORTED_BEDROCK_CODECS.add(DEFAULT_BEDROCK_CODEC); } /** diff --git a/core/src/main/java/org/geysermc/geyser/registry/populator/BlockRegistryPopulator.java b/core/src/main/java/org/geysermc/geyser/registry/populator/BlockRegistryPopulator.java index bac15377d..b931750e1 100644 --- a/core/src/main/java/org/geysermc/geyser/registry/populator/BlockRegistryPopulator.java +++ b/core/src/main/java/org/geysermc/geyser/registry/populator/BlockRegistryPopulator.java @@ -77,6 +77,16 @@ public final class BlockRegistryPopulator { .put(ObjectIntPair.of("1_19_20", Bedrock_v544.V544_CODEC.getProtocolVersion()), emptyMapper) .put(ObjectIntPair.of("1_19_50", Bedrock_v560.V560_CODEC.getProtocolVersion()), emptyMapper) .put(ObjectIntPair.of("1_19_60", Bedrock_v567.V567_CODEC.getProtocolVersion()), emptyMapper) + .put(ObjectIntPair.of("1_19_70", 575), (bedrockIdentifier, statesBuilder) -> { + if (bedrockIdentifier.equals("minecraft:wool")) { + String color = (String) statesBuilder.remove("color"); + if ("silver".equals(color)) { + color = "light_gray"; + } + return "minecraft:" + color + "_wool"; + } + return null; + }) .build(); for (Map.Entry, BiFunction> palette : blockMappers.entrySet()) { 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 2d35c0f1d..9b4ae99a2 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 @@ -76,6 +76,7 @@ public class ItemRegistryPopulator { paletteVersions.put("1_19_20", new PaletteVersion(Bedrock_v544.V544_CODEC.getProtocolVersion(), Collections.emptyMap())); paletteVersions.put("1_19_50", new PaletteVersion(Bedrock_v560.V560_CODEC.getProtocolVersion(), Collections.emptyMap())); paletteVersions.put("1_19_60", new PaletteVersion(Bedrock_v567.V567_CODEC.getProtocolVersion(), Collections.emptyMap())); + paletteVersions.put("1_19_70", new PaletteVersion(575, Collections.emptyMap())); GeyserBootstrap bootstrap = GeyserImpl.getInstance().getBootstrap(); @@ -305,6 +306,11 @@ public class ItemRegistryPopulator { mappingItem = entry.getValue(); } + // 1.19.70+ + if (palette.getValue().protocolVersion() >= 575 && mappingItem.getBedrockIdentifier().equals("minecraft:wool")) { + mappingItem.setBedrockIdentifier(javaIdentifier); + } + if (customItemsAllowed && javaIdentifier.equals("minecraft:furnace_minecart")) { javaFurnaceMinecartId = itemIndex; itemIndex++; diff --git a/core/src/main/java/org/geysermc/geyser/session/GeyserSession.java b/core/src/main/java/org/geysermc/geyser/session/GeyserSession.java index 80e2be5cd..9b7d334fc 100644 --- a/core/src/main/java/org/geysermc/geyser/session/GeyserSession.java +++ b/core/src/main/java/org/geysermc/geyser/session/GeyserSession.java @@ -116,7 +116,6 @@ import org.geysermc.geyser.inventory.Inventory; import org.geysermc.geyser.inventory.PlayerInventory; import org.geysermc.geyser.inventory.recipe.GeyserRecipe; import org.geysermc.geyser.inventory.recipe.GeyserStonecutterData; -import org.geysermc.geyser.level.DamageType; import org.geysermc.geyser.level.JavaDimension; import org.geysermc.geyser.level.WorldManager; import org.geysermc.geyser.level.physics.CollisionManager; @@ -346,8 +345,6 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource { private final Int2ObjectMap chatTypes = new Int2ObjectOpenHashMap<>(7); - private final Int2ObjectMap damageTypes = new Int2ObjectOpenHashMap<>(); - @Setter private int breakingBlock; 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 c770b070f..9e348e704 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 @@ -32,12 +32,7 @@ import com.github.steveice10.mc.protocol.data.game.entity.player.Hand; import com.github.steveice10.mc.protocol.data.game.entity.player.InteractAction; import com.github.steveice10.mc.protocol.data.game.entity.player.PlayerAction; import com.github.steveice10.mc.protocol.packet.ingame.serverbound.inventory.ServerboundContainerClickPacket; -import com.github.steveice10.mc.protocol.packet.ingame.serverbound.player.ServerboundInteractPacket; -import com.github.steveice10.mc.protocol.packet.ingame.serverbound.player.ServerboundMovePlayerPosRotPacket; -import com.github.steveice10.mc.protocol.packet.ingame.serverbound.player.ServerboundPlayerActionPacket; -import com.github.steveice10.mc.protocol.packet.ingame.serverbound.player.ServerboundSwingPacket; -import com.github.steveice10.mc.protocol.packet.ingame.serverbound.player.ServerboundUseItemOnPacket; -import com.github.steveice10.mc.protocol.packet.ingame.serverbound.player.ServerboundUseItemPacket; +import com.github.steveice10.mc.protocol.packet.ingame.serverbound.player.*; import com.nukkitx.math.vector.Vector3d; import com.nukkitx.math.vector.Vector3f; import com.nukkitx.math.vector.Vector3i; @@ -46,14 +41,12 @@ import com.nukkitx.protocol.bedrock.data.inventory.ContainerType; import com.nukkitx.protocol.bedrock.data.inventory.InventoryActionData; import com.nukkitx.protocol.bedrock.data.inventory.InventorySource; import com.nukkitx.protocol.bedrock.data.inventory.ItemData; -import com.nukkitx.protocol.bedrock.data.inventory.LegacySetItemSlotData; import com.nukkitx.protocol.bedrock.packet.ContainerOpenPacket; import com.nukkitx.protocol.bedrock.packet.InventoryTransactionPacket; import com.nukkitx.protocol.bedrock.packet.LevelEventPacket; import com.nukkitx.protocol.bedrock.packet.UpdateBlockPacket; import it.unimi.dsi.fastutil.ints.Int2ObjectMap; import it.unimi.dsi.fastutil.ints.Int2ObjectMaps; -import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; import org.geysermc.geyser.entity.EntityDefinitions; import org.geysermc.geyser.entity.type.Entity; import org.geysermc.geyser.entity.type.ItemFrameEntity; @@ -75,9 +68,7 @@ import org.geysermc.geyser.util.BlockUtils; import org.geysermc.geyser.util.CooldownUtils; import org.geysermc.geyser.util.EntityUtils; import org.geysermc.geyser.util.InteractionResult; -import org.geysermc.geyser.util.InventoryUtils; -import java.util.List; import java.util.concurrent.TimeUnit; /** @@ -494,6 +485,7 @@ public class BedrockInventoryTransactionTranslator extends PacketTranslator chatTypes = session.getChatTypes(); chatTypes.clear(); for (CompoundTag tag : JavaCodecUtil.iterateAsTag(packet.getRegistry().get("minecraft:chat_type"))) { @@ -87,13 +78,6 @@ public class JavaLoginTranslator extends PacketTranslator damageTypes = session.getDamageTypes(); - damageTypes.clear(); - for (CompoundTag tag : JavaCodecUtil.iterateAsTag(packet.getRegistry().get("minecraft:damage_type"))) { - int id = ((IntTag) tag.get("id")).getValue(); - CompoundTag element = tag.get("element"); - } - // If the player is already initialized and a join game packet is sent, they // are swapping servers if (session.isSpawned()) { diff --git a/core/src/main/java/org/geysermc/geyser/translator/protocol/java/entity/spawn/JavaDamageEventTranslator.java b/core/src/main/java/org/geysermc/geyser/translator/protocol/java/entity/JavaDamageEventTranslator.java similarity index 92% rename from core/src/main/java/org/geysermc/geyser/translator/protocol/java/entity/spawn/JavaDamageEventTranslator.java rename to core/src/main/java/org/geysermc/geyser/translator/protocol/java/entity/JavaDamageEventTranslator.java index 52cda8517..4553ed97c 100644 --- a/core/src/main/java/org/geysermc/geyser/translator/protocol/java/entity/spawn/JavaDamageEventTranslator.java +++ b/core/src/main/java/org/geysermc/geyser/translator/protocol/java/entity/JavaDamageEventTranslator.java @@ -23,13 +23,12 @@ * @link https://github.com/GeyserMC/Geyser */ -package org.geysermc.geyser.translator.protocol.java.entity.spawn; +package org.geysermc.geyser.translator.protocol.java.entity; import com.github.steveice10.mc.protocol.packet.ingame.clientbound.entity.ClientboundDamageEventPacket; import com.nukkitx.protocol.bedrock.data.entity.EntityEventType; import com.nukkitx.protocol.bedrock.packet.EntityEventPacket; import org.geysermc.geyser.entity.type.Entity; -import org.geysermc.geyser.level.DamageType; import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.translator.protocol.PacketTranslator; import org.geysermc.geyser.translator.protocol.Translator; @@ -44,11 +43,10 @@ public class JavaDamageEventTranslator extends PacketTranslatorl0-#7KnW^23P=uufaIJr3KB#@lc5DADT0E~B8n`BuxEbH3xebMe35#rZGJTx@pN3VW@pT2)U~Er&h~|Bqk)U0SO4hI1;K za+&>X9TA#oA2y`62(hj^XDDlR<>l{*CfUz?hPb; zI4_kne*Sy_!)u1N4@+UB{vQ~`ZnD2F`>=P>y0fEop~h8=qwq+IKD=I{U}^i+Yp?a~dzaX&xb2dBVAs_NC0F-l&wmw0mxPPExtrW!V0~ zu^Nw=?NahlU$2sYiJkcI?BT9|r_9v7M;*>r-%L(GAFa;hhrC(x|K1GE7qb^Al&WS_ zdpVLmCVtZRfL3cth$}+M#{0;-bt)NCYnR~vWU9lE&fhO?-6?^#HnBwCXyDqhT50Vt zjYr(!zVLU#yz3Gxcl})=OyGpE-B!b^4oAg5=QCV zRS=hcdssUMFj5i!2HYYgDSUN#cN$mOcX<6DwXDX*)+S}DD&P*%Cv8k3l zw5q`sYnYdG8`b`6HLfW;cS%Sps?A^}soEo!Y2JeydzqXO{>>Ige&R-<-Mx^m#o{Qp zg@iQk&Rl;?p!u`mz>YUgYkP%s$Uq_o7R9dYl+n%7F|Td<|?1w=WI9KjO|(sljB78QH2>O<@1c@QI-DlZ;3gN>W5h>e*^GxQs7W) zc4P?#*Cd<&>n~e%jc_&nexsJpGZ!DJjhD!+gUMx|_2Z*SQswz=xloMk%Cb>$3G8;F zzs;q#Z+^4J{+=_{zUbP;sB0$ssmc$XF~)hVu1=j5jhbCfa`Q$xh%KKHl+&j44Bh@D z|LN7SeSOz*Q4c<4Y)`{|l!R(MDMMn_xvnuNoI|p#T&U8@ca~<$Ryl)i%6pcoOFP z$?8*Y^Fx#0`c_+w=%c^HB|;1#&gBDds)sO-g(`L+MdItG+(On{o29AA;fKp*U*K<& zBE+SZu6vIuORd>{*IuZ1to=f_?K5PyYe+b_b=j@`!4GeG>ANCdJQ?jE_B>h4nL7dY zkOh@YReDi(@_#nFXJ`w7Uf@sKhjRT(fmYSghNqTNhRLrF=IQria!waKYpS0-kk6}6 zBS+@s-Zpy5_2xmEoo|tIlau6Pus@&QO!>t7H=Ba~KWWm}orUK`QF--IX8qC*Ou-}% zIYtTt`;`ut7)rX-#a`tK^hbS8q|0}eb71K!zgZH!XmWW7<(~Iyd0JHetDf0xxu+gN zn2yMIpe5nike8In+@NvUL1Lf(I(f3+V&3j66ZM$^<~n5YkQt)*u}AkrPh@S?s^WvB z%|R*D_xU+5-X)JoJr={&G0x%bvP6%lF5`=zaD)bUEx5{1JLjI%s|u3$kdAI8xXL*T ziRfSSj0P{(?M{d>ldE6!S@Nq`(%!`RxNsON#%4_VAAaHS#RMjAMwuMlPqT_V=BBnI z4z;~%P1!N@I_fdUyie5D#uRsw*S6CQ@7+^})ruX{vb*ik-;OavlRGic^>e4b&U+Eb zJ46GSj~3s>3>!v#syfPjpl5d2dy6BP^3wx7Nsbmd?n7AziNe4ql}p_P+btw{%Z2I< zy|)rgh8m?s9lwbU%TUqpH|39Re>C?i8t?u%QoyYLCAfe&t{6P}ToDD<$x6#1Ul2ou z>Zu-$?5@5jy?%^<=rfN%-<^n`Y^xQe`!D;e7|#o@S`9D!8c8dO=ft9LE2%~AOR=VCCBHk1>`MCM8HyBfz9p73#YB=_&c8kDVH@*HP(`nULYO^82 zbnlbUU5^`>D7$a5rM3I(J6zDkb|_O*F~Aan%0= z&u`b-2|Dq}^$uxCv_qEbicg2qu&EjKJcNs;DSg32>NTHDs^;*toVPK>o7Af8*YcWv ziSh!kH=+gH(*zK6$az^FRTdvR(N5*=P_y@HTU~CcaT)K-HpFz8-QV1(`R*}JS-bw~ z@aRXPV~p=(p7rIr!E+vmUKY!LR6dg^@OVBVNzWQ{A&pqa9b&;#=V(p_L%%>7Rf>Dx zg7AGm5s*5nA3f%I))aau2`v@6_PyDAM6;?y7LJI)5VyCZH*hnDC0Y--iosJ94%y{g zbViR9eQIR$>?kC`Hg$J@P?}4EVTjerePdayy#uvZk}!&f$cbTl!uG*3*AjJ|PI>RO z`@5A*nI~H#aj}lQFrtTD69rec<5-|bMZWj!_}3Rs*34_SYNh6jtkHgf#bPF5T}!7< z=jm58Y=yTrYx|)`i}^?Kh{9yki_93hN_-WxgvaH0*l`e959{H8-1wSkmgY`T*gfwX zCgs5v5~38IMyPDZOkd{1xE(Jy51Y2DL8MX=H3zeP$6S@%0;Mgw0%>NIV%5u=Yik1t z&tbQpPOG9Sj=?wHcCA=UUCV(FLx44ef^nmCBB)f|-Ubxw!?E)4h8A zrjREbOFQa8Vtu)d|LS5o*J}xy7G+AU8)j&_E03cUpNP-Nc8@MpJ*Yiitvz|y0k!MH zLk$y!xWFV}Kp$qjPca=yC%sRcp0&bpM52!KzN|O)&TTO`E-im$Npo?s;%RtMDbd5t>O{w;D8!GH;F!LM-%djn{L0OUBFj8dk+l>@hv!*1h zuu&I0zJtdGOlw~QO|FEw&!)~2Ly8nn&;dl9UqnSL@fMaCDzFRdnwNi~SX*=)6i7I_ zRv34Lz>0fxbvq@MwBC7WGuytylvv%=LU(JkXtw*LQ zUZpCxkP!VQd`QyR8O>4y8G4fB+v+cAFc{k!a>sIi$xyP%Q<=)ceD?|I=~Sy(w)YDb z{|09{PCd03d9Rw#Y(FSI8;IZxqPMlygCsfn!i=#kB@lq|9%8QA>SG}tAb5PK@v5ZP z73cc#6AQ~+!muMB_J!3EG=irTwhLt?Ky{xXmx^N+n|3$PXkgecQ1|ClrHAiUu_*8p z{QR>0vpaTp9Ve@{m@Vhx>R#sw<7AJYEc6F)iV3?Rt%^=BKQ;xQ?vnE;NV>(v#Zu2( z@fdc@@F`@N)tI_fwiB9pkH#1yxW6~Q_`bui>|^9=!W0pQ@p%5p?dAG(nupeE`jwo@;2Cs-Ay+GmXS?Bovc7pybTRGWf<>|<@pxK9sS?CxYm z!zlwrQNLc=_kSlG{zl;5`rfTZnY8rS_5`5^XKUQHU8H`fs+3k|7)U`(0`5pH4*9WOQrjLArcF} zEySnow}os`{{ETS-#J3J``h*;#(tmV{O^k=|9w`f-muY`jjOv$vnfCY9j&}b(?cwi?Tm790 z2N!<-^6po(a4#?R+EC& zU)E=5iZ*p=re7(|k%xYI^4NdBLjB|$|0pcFeFEiVgs{DeFqMsPIC+7>vAvVgskzp0 zCngSfLo`RvLlsPu{hbWVV#)kO#R&Gr<6C@e{?Ls_Mm3I};dc92;zq@H2|l;(^f4@G zioUHKK}2P~z7jWr;h9~1Z`_^v)S~D!5B1l?hE-JS{kvzReas|zgaJ<1_r zt2?t1`nv}P4HJGK9gr+i0`^k6E#`bpWMan&tjkgj6MWWN$8n0)xvcjfrq zdfp>=%lEyR@|`G5_Gj*S=$4=VVLB~(R^_3yb>3}U{h7HE<3N_o3dBl6S+{+}gIo{3 zYhUL{oS;0_GxTOV2YofuPB^W+BzIV;JYHUd?VbaVQexU`=-7wXA3FO;z_^-)zgj>f z9Dcr#9~rg!x~{bov7{cG!7IM)NPpZPS99|IddNRQVfurnTM1Ps%4yrb(nlpDaPGqw z#?@0#oqSgGJ*q#$Ywr$f@!02=^hR4PSoc|<`Ym%fU56gLt9%Rdy{O*hfQ&F+&KjF< z+OKykcUs>_F56)|jq-IKsNBz)$e2%9O`y(seffRwtv165UMGo$0rB|wTp#a(YnJUk zLHnjRO=A5+%p;e*TelBdd92YlvnIUoFF~67LGr@$$~L-lk{O@i(()eoyqfy%WVJwJWPF%YM4| z4WHJpdVej#89OOI|Do!K={XIhI*LD#!e7t9o=weOdS3bD!&0TB{vWWudwzmrQD~s^tWByH37qhbO0-5Mo9uC*e+yu2m;Vq7wN` zJQt3)A!!bK>GZ2lObVxX4irU=I!|vK@KmR@t^>YPU z5!M*9lw1^T7PcAOw%WjY=mg$8==Zc~iHYuiEI+~#Z$R=i66Xht~8dT=x7&ND5A*5a{!nHix^a^N>3|Mi|L%i!B; zsPU#HW~Pht+ajhdOElGS#H`hnvxEue(Nsm}kbR?W(#M+j!ivWBAI=D6VFtiV{MWnA zV@IyAe!v-AbYm={?b!gkh}Q!UJ9jmw2J{_;Lz}`QYv1J7h?uOrSz22|2{0tp$Nb=IFC zzN32!SLXR-`?rw9W4hZgbRV1^fk) zNpBihbh}giuB|j$x@*xLu#{p!d`hw4JHh?O26ICw0fG`?a}Cd{U01-q;;BiVyaW4c zyN>ro1-mcxOLhr6V&*MJAzu{0Z>x|%qtxSes&2MWr~>uvKbM!m|NpqWn1Sl4UbkrN z5w%^FVLSJzbuAvpFW=HQ8!X5&bHjs?*$MvByEU7KyL+9x@I>z-7h4!n;TV&T$8wN% z`$}rA^Llo880X;QSJ|CXr_GCw&{K7(wc)vsoq2;aPXRJ%j3L-_?Q0pZb7(xF5~^8> z<~f*DMP?UXQAPEH=Gr!DdXU&j96tm(T-q%`c)ymNZkS#>jTv`ie&i&>Vw~2AFLJp-&t3CA50pTtmc*^Gc(S#FiSSoy7V& z-fHHy+I-9E{aN%#XK`}i&PVs? z^Lpg6ITSlGwrsLq;OjR*G2$sv)_!=o+?KhX<`l-<0E^VjDa= zY+Q-b^rAj@Dt7<}m1gI-YVtqQ-PpXhP`OK2cEH3a{rt>#1L# z+*IN4`5x6Fj)Aci9(6NnqRLinq4XiMYEjR|L%KuSeI{<2H>o?XPjHr@QvnzO>RmC~ zTWY7Jj~}fLIHRAm3Dz(R*N8YnH^=uV>(;pfrn=`#37)HGnqP#99^V}Ao~JsOtX7M^ z=XM}hm)X5~{SQfm-{rOWt+2oJ+5F?rPCVy-@5BAa?-TvAPbL0eCi>^c=>GVh*6?fS z?)kr;k@S!MYsLTk5O%?1$A7;@x>ebD{(fth|9)!` zf4{ZFp|Pyt8REjzK3d7fXp;&kPhe5d?9Gap?~$nFX4gTJn2A4pfR`y>6W|Av0+Z|G_N6a5Ljc~S3) z`Qc$S#hyj`sqT27$f^Dd(wQT39Le}i|6j!159XP(iF!YP004<&j_MqJ+kP#@*}?g5 zOXmM~?)*cq>*1%y(m;;^=K+)dskearW0!!H!gD=w!U3iWsP*Dg|3aYUPQSXAVowba z#p3d5@Nn(w7PQ;vOyRKu|9t(wKjCzCim+e%@9X8Y|CtXzB~Eu^jrT&*ip8#3btZ|g z@a#_N@faqZ-#aF)?Z}ln%EL>#P?qoF{9}1LwU`32KtlVyo8%!JLN*o7-h89 zXW6?1vmUb>Z=?K%7j91>Ifo0VxG+;AE8QcrHFjk_x{{yI>ba^{a)#eOWr1%f3CBKi z<}!TKeUw$|zwxEC`kT{RbBLo(ll@ay=bV;V2NFpq*EO8PK%S4dQt$hWJw#@{)imA+ zsojz?_dM`Yv&0k3ALyKy9yS=>^CY~mYO0pz=V|q5?v~9ez6reFP=}vB0n1VlJ4BQ`6J2OcyAj?k-67Nb$>d9zvRxs!dc!1y=G#VNd#1~`^ElBgYCj98 zoQNy0AQ8O~9vTwkz^sD0+S#{Gz6a#o=2Aix_GN@6AwwqO#*2zwt)%Wh?;DMshV;Hv zUlQ%vU7$~j>vHtf-Nt|R^AY`*Svh*>&mtwM&qGA^ljDPSo=@YqVi?V~%I1nPjifYu z%pi=gqB2_!SsGHjPM=ze?I8|S&;3Q+{L0PY#yj|YH z;JNoD7up6$c%8)R2ClO*-Hf^5dv3-7FMf=v4r6?-H%)us29xAng^&TMA(RF0(}h*9 zaXI?x-I}zHf$bG9asM$%#7J7y&O*<<{Rw#HOz3_*}Y?u(NZ z6?6~MVuVLrtgE#*FqJh^#acS zTsb;no~gXW>D@M-XSoom?^<4bo>64uz2yV zr)6Fv0~WsO2|gCOp@ws`?R2#1y6Qc)JA0n)25HDhdwMuLVFT49IVM~}=h?0HP_Od~ ztzP-ZA>tfp{X>AvpN47lXCx1^Hw?Dt-u$!-Uf5j|IYQq?Ik>cR@xP>T!uy~2ZMl^6W`d6RVUnmGMq1hU4=i?57uj%nZ!;WM(ZrA z3$lqEMkA&iOditK+6t059X*UB+Qwxq4t?Kq<+_@iOIg&hE2WmbONNDr={4)qr{LWw zm7D)#E$@9JujBTsT$wL8tCKQc$NuD{G_U1%_mTSf{bqVo-=b_iA5BZwq9+(g{AglZ zR8xSy=n&$zi6KU4<@4jTW}|$rDrh}bqu|WJRn}rq{35I^%tGG0vFQEL){xlx+>V5P zaTRXiMc*X$_n8Eb2zJD@3*aGze}LZxdKV1+z_-8uyjDdra{GcPQj+CTtY%tiigbF< zq)I)+enV8?6>Kn(6&^?MaAI3byBLmJxaE_?Zlu>}0L6|(UHrV~gJ3m~zhLMCesgA8 za#oqKyq(7MizzPO416Rj#$>&t|7u--Y4y z@0xUO(6n4x0!vO}4rl6L1>dGYp9jA!42iAw!G{I(Z*{%EiPWjp#%cYiK}acQl-o|h zna^ixaMcb>ENo8Ra&yTy&(@pxG4+n5VR2hXA=zOP$NNSCE5;ph@L}8{BJkV5?7m@; zggV9dDFp%BbGLskf)BBGr^vn2NyJX95KSqk#npbbeIV%b(XVaZH+=rAgMusn1!p}F z&diergMeS#OyR6c%Dfc&^Ij|vmYnO|o5_s`=NFFlpH#1MrtxtZ5wQU|6u{9?k~jwB z4)dj1>i~oQLCkWleA1jXW`OIR_hQXzfjI515$5-WrtV)$;2HN(&+bbI!02S*g)wFe zg3sxu@Qh#iU$SpQ#AKCiTST)U99?XF=S;rrn<6Fw{|otkY@5R|CO1XJzUt0Jnsu2W z-eZ)L+(z7!YYuA>iAejbu+or)^pSdJ*qhQuXQ$)LILWOzZfJ8}Ei>iZPTg6UIeyq57Q69cTCFop3H9DAv*`pa-w`&UFS zt6Fy!B3!<+r|gM;-$6pmtUu$o!_U#0l|$yE56wJ>ipbSaQ3z)=Z5nupA;H5q6FhwF zR#49O#g^Au2?Z!7kP#UwH16Z79WEGx0W6Cd#XN9pOYQxh~gg zCjhTlS~(bIG23%hKbS+f(yh!*zs!AO?%4mac~lL`N3$z0_{gD5Fz1|Fv)!2eb8uBp zAE)e7K|I{OXTV~_O|(ndY`-BH`h&J6vXYS=YkbvqY~Ouy64$l5^EUU}NO5$$-JrAs za{9(sy*zwyMURRI)vp?{ulLm>$_ZS|121|rV!H*ATm+fFGW4nvm&cx3uGP|2Y!oiK z67B;Z`?M+(b#2)$X=d^5Sh4sC9J4;l72zN2AKe$^zE>*vi{MEuH7nScdIEVrOt!if z?pxYRFEn}_OX~02XEUr^8nU?KG)GdYT&>dX!opRhQf+*g!?WsV4Nc}F{l1JM-?O!c zomNdd@8)(It*q4atNADn8km^RzkN6%y?fhE{@qrVZ(k~=OzL&eONk$g-1*@~%GIBR zi4&RCyn2GoJXLAt8$Jw~$I!Xnyl*G}a>Ps!F3`0zudqLELNRXvC-Ir|E_9CJ=0*a01-EvO>#e6Xn z)J=?8&Z-H0?YF0rYxPw0el2%0tA{H6i7hq=r{tgiV9C?rZ2lT7IWHX#3FF7>C~p&z z!q;|mnv~>US-J0J%eE`#x&_CpDE3Vf_33Ri89r_wUOXQD8MpI8YsGzXoSM|l4s=_uAyi1RMdgrUcDDgM~~aDk9-M*ZsKrbBw0w3TvF5<$TEf}+v;7p zH@{zNfDI)ISLqW%EdRR1>m%7XM2fNN!hy$N1?ur7A zwz?5%F^|k(C+-qZNN@||f9dIH$-SDC`8H`=gUV~n^+AX42j!~S?6kuY-Z-yXup{p* zLhw7#M&bu%WVNl+G_hs2gJt+0a(MP@JwJChU&}wFJljElmGYVyS4%Lqrz_oD*O^>g zT>wAbGAXAg|3u}zAN*j{Zo(7XqurGbk0{68+QjBkp{QB^$g{?6)I zNRc_SA33dfs17}CD);6ti8hN8r0mnLNb7I!_l*)*;4!9wt{N3z-+k~7(fKRDp2 z%;l)|t=g>6(Pj-C>F-xof~8Aq?d|5k4cDik%OJdYV^83D=+;vjCVHA zL{%`UQO0!(mk8`YX*?b7@Rd_hhgZjPCD=jkdz8+}DQiEHIvsO7%<(Tf?VfFR6eM{Q zCB0KA>u&=C_IS7a;ghxRKmBCT3E_#F@C;2c>Nly`!a~Qr561jciK=>{)dC7v#y?k@|PA_#f5q zAWpZ!TqhzPtJ5bsy7!x|JdhZ9^TSMmv`A-^Y&lqG^eXmo4g0vh96UTFIc>JfxLZzI zKK88T!peO*H~Njp!^oE-FU2P%r!@m#j=$M;I7Z!n_FiQqX?4He|FEuX|7)L~`|E%^ zDrVKaZ&IpoE;Vb6ZeD!E>I4b61M~c%T=K9u8i_Qk@|LC?JMce6_)q3YZ6Olr;(V1| z-KId~H}d4{JbPEKzvSN?K3Fkz3k-Wi&RtWf=PaU$5sRXBPvzdL^GsDUcri#|j=d4Zwr@6+A1N_Tz`mD+f_-!g87r zne-}FF=Cm=FgWfVA*J>}_zfl&ndXS7Xa+k4RCc6i`gG7MBP|H`v^bl{<$w$roWM|D zNiC#|i)l9SYaAq20Y$*LhE$Q$g3w%pvgs$l;P_tVXgyY@%m8#;SBw&>{KFd3s!0pN zYzt+xE{DNMJKIg)Ju+xyi-T-3_LjGP573$AzDo!}RQ3qqd#q?}GW3)ye}7>Mh~$)_ zp4t16%frYFnF80i_jsswQ~R4iVxHOMk*k0h*cb|-R4CsEoKV4NI#S}pS@m6DiFj@- zXgwOt;{a4>7lnLzHsFkvwWcH3KdZGSERp093%k1+UmhEeom?Z!*OsvMKZuzh3qWbw zhHdx6uMKvth4QYYO+;HbC^TlKrr#n?CUaG$k)#Sq5`f-RQMKDsfjJ{5qBYxNLptz6 zd2l%2g{b4d;0b1n3Q@lh$b-Wk9io1*iYJ&I7NU+5!_&kb6Vibb%p=DR`d@s*)5H-M z((y8jhv~FE8t0Kx`>S|<>se{^`>0KZSyfahnLy^W3VyjZgi>~yO@xZ_h)v{HA#Mzu zNcyExdr-O@Q>ILRT2v9ElNO4=(j_|`qYaV2X2Py}E(VTwDNadGhRNpq7_y5(6IDJA z$%b0%K=_1B*o_%u;Fsv)(1uD(EV3ov;tm2e1bv{{1n(BtF&WaIzQw)At*0}$r@QS7 z%?^!RSVuBTgI@11(Mr_x1mW-QX}K~EOew1d*+%f(CGP@6D}%XpH zwH>h2TXzJ=39P>MDsxbnFlb~yYn%d&EUBPSpW>>y z0lLfsrGJjoHICHA%k=3y{67_Z*U?ske%ZR7LZhf;LdTZGALMgj)z*fq4pQ*e^q>i9V-+EO=m>-3iHIqw1%=Zyu}F7CMrkvy0!_dCC4JhY z#ajz<^P(!72zg;13{K=?tke$F0K@q%id<$~IpKBsv;gm(RvsTXFKachTn!~Ppan>d zm?&5231!r4N4=Gc$W(jISLSoeis4bndRd94X=vYnI^$B7S1kgCN zo~-q3Kr6BClfVj`Fj)XU9 zLuBxwY})g{tm2xZb%mK&&X2|w#;BsQKWre~?6vpZ)D{hOi1t4Sn|IuB%UI{tZJu9t4HUg# zbxpJfH1(vm|3SedzCcu<*`D712O}ppMFCXwLO5@kPW<@=qP2SyS}&Bc`0Fm#C7d(y;oVv)~6;goJ6!*XuLX-MFyQ!NX|H`Gmt=5 z{BcPK-&@y%M)o_q7&YZEyBJfUM+|~kx)!Y#oNj47E7SkEu$$4zaDX~x{Zl$p*F%X_ zoI}?o27yn~p`>SKof%M9)J0)1fMVGE1ZC3m5L6K7Fn$+YxJEM$5Vw5~ zvK8VLqUZud2ZK4>8D^jfQIvWptS0DK>q4-aV5k`b2b!Ryuf>GbgbfNE6jl=)Fgg&d zCfMAGfs<{z+PQ5KP2SSB?m!YGkrlbxHDuP$nY*<=S|&fSS!hk>|V38SVP_m?p_OhzKhhb z?q0)cgn?Z58rDoIFm~7HJbhT<51{X_ z3?g_$h_wJgpamR)O@@9z4AKB6g8q61zzqu)v|I$-uqIN^PQZ;h9?oiD zDd5I_4bG}#DBy-g3l^GS+-*b_%FoC`o7+haMeuGTbx8m}K@&prb(mFz6ygEk4z-k$ zT0lA@aLl8?1>Ce(M3qNwrccY_zXzbl%9Krnda?w-9xNMfp0R;K@r(`Dz>#3tkn$%R z*w5HNp?t;$j21*vR?O3LOpS2q2bUXwM*k_i^fCW9WX&=Y@DVB#UNuMb!{VWQV`hEZ zn~;vclpDm!_!p#TBng^B1fc9nqFrkQcS_SITrKAm8(A89U?Cl{1o||R)EEc5J!s^f zR^1i5y&$qZt@xXEdst8TfzNJFw(d|XOU7la}3{iN>B*`a=MJkDaK?Y-IEmMYFVEg#eegO1`0*ymNl)S zU6t6uxyD07PWvK*MRi0_bE1Y(H{F8rc5#_}KtDUBULsF6aa( z>6J4;NmNk)CCR?p+0)`@909}-;5F##)N9+h-OF&QuFs|vM8<*v^4khs5lq|3HuM#JbD6%M>bh3DJD5<@I zQCVk7w}b;{IU|qqjx0)>mTlS7gj~5z%qBvy@fHRr(^OV!4~DaVkm3g{il1>&3CN`s zIgM3=_A6{6j2CNQ*$PT(05)UTqoc^rttt;(LQZ3~ph63v->WsSPXif2N?2myQw2(I*1PpBNss^ApI*oNCD7@1RR;{go0n7$^qafiU5G0#1%kf zLC!b~Kvggl5N}*pgB9IFf5)pKw`Pxk6x|z_Eq|RhEdD`Ev!shvh+IVtmdA@$#R(*5 z;s}F~*H6l_g{2O=#fxc<=oxN~6hBji6Eh1wX(IvO{ANm3&}k{f{@e9f8?5 z0K3SgXv7I-LIk|n(V|`B1ZHB>m4kRUE^5qpwR@t7BG;{F<>0<4)Q~GFC;3e?f}P$PUzZX zuu>iHCfm$dl5I)Q_F%ReW)mZf+JPYmz5UVcud<}AS?;%|6!J6LY7S&aZf7kyyh_yZ zkTC$BZy$!Zj2nm6xo185q5%ns(HKam^UQXT)A8UgGhsKSjzJKSfY?F-IPh;styrxA zg3HV6cl(109s0Sp_8{apd#9Au0(F2(Acg>!AdbHSh>ShJPXvI7L5PCV2UZh0Lu zBU1oe0zL|yBIY3ACs2{AfQStwDb>#cNu>5!AUW}8ASrkjNK&Yt1(HnKXMtoru&9sB zKk^Xatv}0(YIeiDb_bGCR2LX}7jcfRt!kc#%+glP<~hLyPtgmU*M_>O$5Yd#0Iq5y zULcCJoYMeJ+F_y>bYN=$6UmE2i(UvSDATFim?z=|p3?FXpk4@8#$ius{{y&6ijS{E zFPJ!&>wMw^!-21ScL^XRoCxJ64kW-@soXKgu9}n%01rKsj7h?0OT(g8ZGc*Td@R&v zoYoz%BfFckWM$x-vjl4y)b$X&cno}WR0u#mmfz57FRQGrZGxJMqGI(1*izO$=``tj z+!Pk)F!To4_I!tuzJ)bQU|Z2fjKRPF!}=!&IXw^VSK=Ht2{DMv1CACet4ad^@;sK! zqm75-eFk8O?jit7p^-~~DDx43B^=Ppk=w!Tf-C>cVd6H`5Gw$d$WsAW(op~is>m`0 zboyl-4bH4%jP99rl+!)4j*5R;M~yS<7^8h=9h-o4Z0kUFe&>G`p%NnjFpx}JJCYz7 zHw|E*%=$Hee!O|H;`cwI)Gh`B`H)7F31u&30W>mq&pv*v!It zO7%u8W@vz^HD&@kd7eV85sRT^VpJQksfC9Ws*PCCa5F}w(FPdGNeY$5+525_h5lkn zqw$6OH!&*n-eiRtOX9D*wLNZ5x3P(l^R2)TIEudL_TVZhaAtZw6h<@70$nF7&RWuG z@zL=R{HhAD4<~m_5ft5yAE<22{-JOaAm?(WtR|y{o$K-{h)o@=bGjXIB zY6INNrkuSL1WY}|pA#(u^qujcM9s8-0su?h41H6r-vccG@v;m!8Mm>$v@KJ>As`z7 zhk*McI0QgY>zKzfWq?Su4~SSVVY3db=XrCqjwlm$_Yev-6xJd*SZhPDUc$5!1_xe3 zNso^SyLl~271TcOF0w+RquwIea{*;zwinJdH@V9jMKA* z^_L1vJ!@FoU?A7ChD|V7Sof@9L9|A2&l=WYK1YIa@7%JP51muEDyvkHnX>524RS^+ zo%v8Q*N{eCm62wuDW0HuxOO@0^vrLgT_uKQkv>9y;=F219KW(|aGS>5v zw*@+G7n5}5QU_Yc1j!Q6SOkgDpU-Ak`{aP5=fl?`&S4rFlSF(4#1>Y_*?^WJtysf( z>dVWxqt6bxi~UP~5UaGmG7#bwBh?zIn-*_zzYtB%*bj(78bZ7xgh2nNO+c`~{44H& z6M--T!VJO;R5sZPAl|P5#|az?w4nhwo%Kk*7~KIZ3rys6J+P3$SP>jwZV*mrfYaHC zjEd17z;XcA1dM99>Bk!T>&-S9N`pOyAHKF^wqLx=EbmVFir4jHQC(1qE&_+0sA_@p zhlPWl&5Q6TM|VorJEb{Abs;I{ph<;zk<(eT;dA@>8yL<-PF|TQ!*j|xMfV7TZGzeI z+RtCUoborp;yxPqsw=L1+?U*APxK0jdLS^&l9`-mD` z=QKVSn?%Hn*J>C5L|@_*f3&G1co2eOd$&mYm__E3Q#>5MzQz*p`7i{e7Z+%mGo?W< zI1%l$#%UP-E^%}bWXGu)thI#{3U10p=BSOmy5(45>$n6foxn_l{)a>*kCww zyR>B{3=>}GY{@!%>E&&(OHf=)ipi~0QpZiOL2`WVDw3WwM7{pBWkTkyhvwsuxP?{s zF?ivB?ohOuespxrTR#~oa+9XnDP+D2yoI>^p1?K39uQu;Wz}z5*lPr`G<;3QRSzha zAWnK+xFX;sqZHo_9L07ebL8gio%BF)7@WSRFE$YFz+t0F5@+MXh+)gS3)bAh}TjKbJAg1_Q z#U^$cG6O>pP#J@jF#4BZ-CE^1>B7UYur}SGdl-ALl+$Qifap5&IGG)_pMk&yy zz6BQ29+1L9jKI}arV%A&ek%ZCY+}1P71%aNGHEuv4N(iurX@z;J>sVky=+}&J7)qb zPEF~qpOC#wn4YrvVpY}!otA=)FyFlfBc89wepX6L;jf>Tt-q9(qFp~On}SYF!G@UU z!oWDjdD%BR-IMPj|FHCwMw25s!Xa1=pwOwqvIWAL=u}pr8_jAu=7+=+jcQD}wtGR>ygR8s)PD^RQ4dU@ zB~He>FGeGI8P`t$dL^6~o>n!z(>^e)mHg%n)UNK-C**0=|%)8J+1x0>}7eq8O@# z4J454AEK~<1WE}cr9dE2ag$vH8%X4bD`5i(YmW3NY#pxS}I^j!w%`}J2Kv&3(i0uBY~05}xv z7eV^TI})4}W|=ZTB-{r?Z1UOW1;|=KSbLLn3n%CW^s2c)JWHur352}=aI zHYGhu40AwJs1~JM`8=;@YDI2gbETbA9>cBo8-;oR>$Fwb0>qTiiA79lSj6-Iiyl za8x>F;&{ycx~W9AIE;BL{SjDwoN{3GX@0o^vIPK|?iB+f0U_`g&=%k?v>AV0@2~=( z>2d=gnzR7WgoTVEgz3Z3aWTz|4OSt*0$7eP`ul|na%+w^5G-Pxzmztt!z-*=SjZ{_ zH~=eTMXSF2O#ZXa@BUtaHsGTqqr90Lt}XAKG*FS4k;kU}-U3wgf}S|};-mXCk`zog zK=Cc!7(gCSL!BVEv{!1hNe$v4P7a5Z2tctBwM8f>^5H|-RNjFrQ6Qq$NC4NKKt!#R z0;+piP&U16kT?XMQtPt-6dO^Sm4k7z&)D`olQ^$|%Sd10ph~UPi@iYOgmuIsz!A3~ zqKX*o)luaI=|kY^i$SQuZ{qRNbxxP*$NtO>fCWRIC;#G5q&B(|`6lQ$=!} znA0+sfaB zePvbpipn!`HKR)OGv>-ZyRNe&)vS)Wy21PRG#9OUR(9fy5snq1>7PA^&6imgpLt%U z5}aA)9+nqH1RD=m2@W?6RsDcBCDJop&@CQ%D!2G`-J-uWWDsVQ6CKXKUQ#G!^#0P( zmrY!%livK=1(Wf|QyP>Eho-6iOtC@XfM>FQD)eT{>B5uW3<$$Kg4 z(84XNuQyp|ovk<;e4^YknpH@o)(0a-Ho8n!NdNEuTec`6-*x0jJ%h0nOLu=?zZA8V zt1d!4Rd-i&mk4jZ>_#PNRawt>s|m{nb-LX+UpnS07JD1zd()C@HK1%+a^3X*u=mz+ zRW04axJrqn(j_1Q0!sHGML?vxySuwYluqeR3F*!QN=r&NTsjU1q#J(wpgzyN_qlw3 zpZEFmeZT*#J+o%bs+l=w@0q>V(v)NvcNiXLn>}!lDx7;{FcSiE@?*TQ;JC3w|ck<+!;002G)-dmEDxlt#$<@36cbZhu z-$dkpr*XM^_wS5;(<}<#E({2$@J~9xx;En7&e8M!pEdq1=tkk4+jTs-a}TiZFVz2X z0n)?2Tl6o_@V&qDPtJfjY*+Vo)532z%>(;((`LrRR{ZB>j_}UmOlq4aNCjFi+pq2r zZZjMqeYf#0;|Ios;~}ixp#TgQ&=?O49YBdM-)?{Z4%#uE{%1h*g)2P?V0>9;v_tY2 zUE$yQod+{1qTU5KFy#Oqxaq!o@7`bgWB%;!^6>6o`U7sg>E4KR3mzD#Kg;_UJ;p6~ z(tpN#+_?phc?A+RqBkMnHfAE%b4Ug{5VzPGO zodA2UvKu6h!q$<=*aKhy0F*5P;P5uC=Gu2RQ|y0RHvbFn{GQjz`@wX1_`jU|?|BQj zUqcZ1xw7qNC{F)5&n_+Bvd?I5VZa9I5VmSyLjeYsg*LK{JF7XY^lN;66@RXkpFC<{ z%jSQJ#s8xpAgS6OySQJn!5=${qD;$YV4v*eeDOSO_BtS+uRTWU_n98&fH3f2Ml=EB>jGnBmbZ8THm>wlveBrn0i3-0qaM&HH^T82UWeJWe?A$P-k7)oK zX0oM-B-s;M&Y4ysp;jvzc;DB@dhIOJ%*_46Qc7hh`R;OiKG_Ws}y3 zh)roF1i3$uz0|$4{pspxS=S~s+H!@ zh1`?|4qQ23*bU(!}k({`j53Kpl_cF=lx!#Fhj4)F>`* zrIzX#k;pa%uV|&VyFi)NYFOS2$F^uq`Qf-5P>4ANk1EZ;ZZ{1VY3gX4)0 z6Vdw13f-R~pfC|ID=vSD@cSh~6yPih6CwUf1obZw*g}d9R;{^qJ>30QCcuCki=MwX zspX%de${$8$VX1N9e#u?o4Za0IRA3+Mo29`;f)t}I|Ic`g zTliz#!XM)n{usCL55I+f_&@Ql#0uzBJI_k7SWpsp#m%pF_u)d0bPxyoW?ohfyqNJj&+>c;Bb|e-8rV?O@Dm;eBNbz`6#*sK0{Mmf*IN0> zff{ zvB!FyE)Gh_u*sXnqE~^4j|9gih><_*X4q-}+_c-KRr-ZrPxadvl)jIYZ_ zppK%sL`27dS!-9`bnc8+CA2%ThgpBW5t&|Mozlu3bo!pOC)O7<_grLJ&>A-jn$8gn z!?l}$*8B7-Ne+ydrqJEwMH-&sz1W=hP$w3ib%@7sy`~tr7+qb!;C*tOy7ue-z*tW! zt{#Z+Sq95;^r!+4Z;awRsbZ)ENHKI^Z5Np|w^tajMK%!Bg$@)7d-p%pab%eiAFD&3 zdolE1m2n@ccw8IvZdmc}SBW-1!E@tNr;k=GV&(Gp4g}-_9)+vvI-k+RQT<^A%o}Dl(TVbsg zg~Tn*0C5}qxHr1}<+PB1d>o5FVh#l$L&u6a3y_K9*a$!|^aEM6=97VZT20(h7@s_x zsYu_P?nwZNIiLvuA>@Y87)JOVMwqS{eax~nXLi)Z8041gpXCvcq;b5(ap;}3TQsBJ zs^>?kDnFOBiDtgoH1%yrj$zKlDT#1@kblf`LvjGfGxucj{ZbUzz_WhFsjqeZvP+%k z^uT&8?Jg{fRm$VG&~`y){AX#VW*E)XV$*c9Vc^{?trCz{1^d-ii~-L#DUNbHJ=SeU zq@pbbbrf3@mRBD|E>|YhqJ5KO$CT?S+y&7s5D6U43yxSLlY^wylV5_w&Xc@#UUayE zt#%yfxF!jU+KPynBir7Dt|h|znTs*2nQm+?vvJHW4jeb{2D>9@57pk&8M4vCc`Jqy zq;r0wP(fJ?e~%VJEU zA~6j2wnd*sGyCpk5U;-3I0Z7B@HPWkQP$!xJ{?>Do=nx|iyqqV@t1S+I+b}dz(D#R zL^b_Y5!6ec#8Py4xTkHV2#+#9<*R7BYz@gb*X$OYKHigM?(&KYVSgN-{7QE2Q`nA7 z&o>}f$r>vrG<2WBm3}?l*^HV4CNNV zxIqrnuItjkdrdff@4yk=bil{#E2Ab1*?ZlUcyg{WQz#n?L5hhe?&L94ty-@?O_t)x znR0J^P)Njpq1pfxK^cI014CiKP?XAWB*&oK3YpdzTWhv`bn$Qb`JCP-DN)X6^Uc@o znsi<@GlaIV5W1m~rQkepJyh^SFwNsqd~Uz0#M@(>d|;Z;4(b3(#U9yP;^DlkI`;5% z2w&Ub?k}5Jf{KU_jXcx#T1|3~o7v5rVS&z>aLqAlpA&ZWA2A*-t{aFz5;)J3h^}}1 zzU`j}R$Q~42ZKk)Og>F(L5-2kpBr3H`3qLm?v_aXK}=XInYwLr!UVtkF;gqr=$atl z35$T!Gqaash0KKh%O}w)Bm(p_d(XF)js6yiQyvKZp-kD#|beZgaNDb#vO32_Nd26?6{rR&rj6PwG zf*K%r!0J{@5k5qgds46}rOEvIQgWbSADUKeHy@T!B=Gi2?(^b0SW9flpe2)QTSb&HujsIU(YK*D@>{V*B*r;`wPi zDNM%TF&J^9wrJYn;Jf$fSw6?yNzTB?d>t)z_d=nBQ}Kfj%#*N+!P;k;XHh8yFEjoR zk#g)G*3jQUch>$a7ueFSM_aE;_=Nv0%!YccA)_|;s1O+_^^d;W=NX3n_9BF49iK2DB zvsbHD)r%meo{XfcmjkCLygLk)RO3_)!Y@{)oJ=H(E&a!qr<~Sb7AIXfK$#Dco4@a0 zy)?Ijs*KYM-CRU67-kl>o99ya?4W1TBqWW;ifhe9neMSD3p%(|vkYuP@eWL*s2L8O zK-j`!75eu>Xgwcf>UHWc-yGB7z1(nHdHHO%SmXYuo*0x2hZ-1>5Mx?Mc4P zU6i7Og&B-u;UDlMqalUr4m2)yc-I}XX-|)GJf0-1!JB1te7Vt@dOSzn2x$g`uG{m3 zz}xa2vp_}=3=z6|cWEH3-*J*#EA0-`p`YTM@+MkAW84T3%KADF2%VQIYpt^4nK@V5 z?Jfi(^o0@L1qh4JVc-x3c3@xw2A1kD-Xvcdq-~NN`Gc3sw9k^78`gmk>cUrKM}CX$ zdWpUhMCwRobjntcJ4x$z*1q5E&w|qsK&lNum&skEx_#>#*f##t_WxhD#|J2G-=2Sa z`>yzt+l9H`e!4Ihd^<C0P>IZhaFH%;_*)A)jJ zHOHAGn)V0=w?!E z47l@`gMr`Y3Uj`<;Dc|$``>~O|7Sek+gtFr=mQ+Y{G*EuywU%AN80g!TYz7Y|8wvBI-0mds-&W;s#qDp>RObCA4Y#Ax z$G*QMeqThNe%{lqx4E~YaT$57V`iLlBz#d(%zTU+J^^3F+IW+wg!97YIKus*wV3;Thd3AzO~**^WkAgzMPgX z4uK%R9i*!8!>{hK_`@4LMh>}3G<~AU683eygrHOBU_3;h@zXfL3X6ZZV+U3I5F|tr zR!`D-Q^Imp3{d>OR%^3JX0H*1>i~tvUoD!Ac-{?av&^L#|NBYQi;9lBE+`ZyidML5sD1yAouC6ooGpJ$>qA3q?*zlINo9Y&gMw;8V;54_3CjO{a zN7hgfQHejh76lNp>s*uQYjVHDsmO!~<4S=Z(N|mzM0E!Oi|`&DcTL3UHRcJgxUcB7 zs7~JvONHotNt_6_0(x{r(H`LS0MEzj3yYEbzzz`q{(I16Tk(hBo*KldkRrc}E)L@6 zKXfk-I)a>qcVBm_XSzIHxs>7gxM%e2v6w40c6fFpT9ByR99%mEjrCh#|HYXfMwSMU zc}fFhIn)4ICl)|98wij=fK3@|2j`if%Eu`|prVrzG6JJ(3s?%Pa^354+lCDr+I4IG zZLTGZ>l018zLyD##7-dFRl#wNdCp#rpJ6Q}I7Tz>$j;4bz{$>CzyMLvUDHeriU(xd zB@YoWkH-V<1?&;lJv(;b1Cpcz0jsOnwt!G@w)3Z-+uKR6jv)KIe!ucOYwHJx6!)&W zs%NEz3{`23>@0U)4r-tOh@_rBuO{s+DqSn0oLw+Ub{a5H5HS4eQ096nbQ)CEwp~G7sN_XQ(@KiyP^bSPUn!BU5 z#J#sC^&sVVmC%79^@6Hl@c2AQke$#`-IkTukiuAh>rZKs4B|unnik0bgY*pKYQv%k zJRcg>b839M;=%$#4l7m`(ub5^vjb%g`CP71?Igh$&0e_=;zng~bq4G|^q7;0m(J(c zgG)AZ9*#WPa?CN|g2g5V0UZ&`FWgdSk_#q1CF@TgbV_|52ciOx8puANEMi{^idFV? zq=~LnOQuaNK{j}J#G_o>coWO_xrULDY5?y5PX7Wt5bHMuh)L|L>Ic+clw>?_0c5Y) zqRxG^D)&iINMjN1$sZISV*F@k3j{@Ga}_5Pk%RVO55U6wo~6)gC;?%9fu8iRFu$;# zurR-1SWIRh%&!+LCNmJ`w}j<`J=-HqwIsr~HEc&YL9vPn>@UJ9r;_Q^w1l~6w&MnSRi z30m|)*}yocDH%3Z_e3bTv$Wf@>puU7VQx81WQ^4*0laSd31bL2F8HY$LXoU76 zIbBkZLKFteX-5i$BbAZ>aRuLzoCy9)_nl4^UuT~s)2K-qo>e~kIleKcQ}jar#rP_& z9t0I76jQb?Y*_Td!U%FLSSZapaI&qWU3L5j4L>P6PD`?@uyx?U2YyQmq6&{WUZ+?S ztA{t3Qky!~Z%{P?aUGi;o~-HggWkD1-%p$?fkU|GKkjqSrxhaL1LU$^R%&wkC;}e) zmX`+P;+^-T{UJWqWY^$r;<2Fd$7#o4Uz+r%`bo~F)7+@L>~VSDK22I|LE2j|CQn!{TuFt+z`9ygc4r3wh3-6AP`zS^%+9D*v(#XER?Rk2ioqMyDGfwSzGM z@LN9?;QeFPFt~{OOE3Vu(}~PttqqDO^ zQ^!nMhohD0$Uc5X*Ih$~Y=2c+ZOciNe2+LohUItX5y47A?6cZsAiqlY_HF#3ZJY-E z*A2>BVdPLP0I8r=hPOO#ckG|VY1!V>BY$_^`*4~F<2-BMKBfw*F0GnfJ3t!usAI9C z8HDV}c+5U0`K8ZW`%ME>k@~K6nX01=$&m}Yj62&lCx(aD&CZiiq;&<3>rG0kt&$hH zvdy5WeD-~P2JWLO2;VVu07dBykT;LMrTEUXI0GP69!e$W+j2!Y~W;UVriKX z)&klF+KsL5#NNV<4M7DbY(ZI7Lt8;%VH;%ynTNt@f1GK2xvcZqsY`!85W`M`Y)Bb+*$?*S4N$QyO(deC#r_ey*&EF;wT^O zy0jkhfJ(PsSjG1YtlHDHJhLG}^KX7L`i_cuJX&;GZ=~jviJRxN8SE-Cf28pWMNRB$ zMNP+bfcE)r?QvF@ROVHuXor`)+VRH}ED&-5HMCs4Bxpq!O@-=G&qx_frT5k6yTNt6 zgKyh4=C@K+>wl-R0cU!Lu@pIdQkA$XQbkcPj(P)*on-|D^Y!y$v>Z-Bw*y&Qy`&RvAk7#$t z!;9dZ8Ci}faB>p3?XW&xbXK^=o-02FmQJmhSFL$GYi}7}iaRi!w30^v{P@jFN}g-) zLA4({jt|fuJ{rCv8EUq&9z?6U%6gcc2}OLK-sv#u@LIvI7fR&xekq&7&)MTgddCf{b-T};T8#L((DkjFU=^=y4-7_U%jrS7)bXwl327h2rRm54`DBLAp**vleIxYHOVPcr?qHLXpnzEU_(`Ze4sbSQuH>u*4 z0!k}tni~FUi_8mDd&xXpzdB+7rGfnU0|65piy5L(FKRC{3u8~;myjeMRb51*PXzIb zMmkg~jP8z;2%*psx+0QKV`;gia|-SQ{o_qrp~!_KOZ=uC4Ao=O?M~EH~Uztbt*6^y&}to!U!h zUc7BA~J#9dfn7OQ!mZT%C}{|M_x>v0o32TQ#sC=CWRWcf|L6& zJoKbl!OlKko-t^$Y!9*Q0b*GYVj1^hd$g~gi)q_tK%7Wn*i8YCrNrbg0aK_o-!S+Ubf5GG5VBs3^lpGsDceHZPPo zqI9WFinsipu{_J;E~VCr9;v=#+7}FQdpw3qJ7)R=?k%})Q}Z;6L}Ok-;hoaKF-1)klMYt7bX%{v}7JFQ>4G2?~Ed?JVip;$*^@8e41&rXr~&rd$5 zN>jRO@hf`F2%fEIA68pk&W)#zAM48^k=qS9UDfrrb#D{9lr(0mU7f14^G*y1o?3Qp zn^%M;rnq&LsO|e86}z;aC7oaYQPjb+Ul2{W-y8nyd=KNRKgp-767GX|mNChl%j#De z6?3(^5peb+1j}D|%#9=9_78x#TbFZ9x)HvEyWe+6h!MFMJ6pYiiH(RGV#4$vtr!`^ zT#8Wi_i4WNSfm3rZ*k!9XEF-LKmAJlkV=fv|Gn5Fqs~{&Vl7XREA%txS#>|c+e>Uk zs>n#zbo0>pO#dKuFSlPj`kZu4kZL>JOEhzur=?;vgVVBV+6cX8 z-ZFhJv_?}$(nURcw;H9eOHx>u!X%cYU?gO5L-^5v*CN4oL9=%^9@d7Q<+-vFg-wgi zm0Kp?&e33)#9Bp{VXj+*@HWq=?{mAu{Kp27Aa|B0Yu`H9;U>RK7?Ba-Poo;Z=_mPE z=vc5sBT@N4Jrq?L*6h~B!(syny^B5PK%g0^ckK`N! zKJ9BA4bGDuSC1cE@6slzu8I~|7$uMvJW504tvU2`yJ9Ifmge|)KZcj@I7Z*g^VN>^ zP~cv_OFnoKS>E%6*57+&v(xh|&7UfuxDdoLHuJgBbMp&*r#(9;O?}iv6(eiRc;87x zd(J{Zja1b2UH=3AYN#4J?LECK7Njbg>#B!3r)jxi&ExVv5T8&d*fIdf)2fSt{x@Z52Mo1@&+VFnwU^c4Y^4Xzj?jn0`(aF3>)g)tobKeM* zs9!sb==ytN4GASECz>Kq!=(T*kgFq_f~hmn?&)g0d{`T?I}!I>RY+?>RFam%n&;c`8qpYDq`Vnw6-Q+u^{nr17ln5`XboUZKlK3;v!T4N(>26R} z(6@d{jJ8jA+CJTH`@pYt##NT<-Wb2qLdsxN_oFjdmELm=FLD-5{#2MEu4zA51Xd5# zIO}IHSZaR`U{EGqn0h|hH#$5{&2T3jwA~+<-{%*Svj<M6sSD@<+#RjPPV7R(;Kib{WX$#zNZPQ~uGIoE%pXxx(phpU;va7g|%4BsM^YVKy z!_x;D=SA2<7?yhPkJah2NvGQG@6@Qh=4Z8kzf*c2iddHa$vSGvn9q4Z{F?JBeI`tp zzaXhlVg#CIY?x_v%tMFe=)UIGDaKzw2wrM?isjg_>ed-3Mz!hG8@h&N2@u8-4NyLz?zP_M#HftXO$0^-UkKN&)WXHrr3g)-aC1{Y=1SITD$9^_X8{h6s>^b z%Y(U$lr$M8*%}lpo;1dULTv-NeMrB>WE#DrRn!}aM07YnELv_RK)@0ND z`u+GE_XAIA@Q-z@5?Pm-NlvU)3ikf@bs*DZWcvy^(kQQRNh0Hyj8bR3YL);h`6<1K zipjfKsMCYv4-hNw%@VC$seXvWrpOb;lk#K1{FXQ{<{KQ~c0|T+k-^Tmx&OqxGY*X1 zEpt(|)T4#+iQ)?Tq#RFZBY+>FX=@Z9vR1eTFf~GyK}A!Z5wTkOi%sh}zRg$A605B` zX0Zd}yorib^&(-V_CP(qUFPnhPVYxt`D#|j5WHra7_1_RTGlcfCi#Ns(Ic1rH3SPrruqHekhY@q$dVSYX?KJ*OFmSUgyqTLxTF5ll_{T{E~GKpY@}((7H#{Rj%$kk;nQLj3)7qiS9}JGCs@c6uJAi z`ollhU+U`*7*Iw7-vrSrl_R-r+MgaoY3-I>H|^wS@zxY$jnh~)=rGz4N?lOrZwwwx zB6;e$Oa{vLT9dWUe75v9RwN!sU6FLc@mZx2K*3HIe(=4R38$`C!BxW1*C{mL3aE~v zO|j)X;pn6d|97=su%Sa?IZ%Qc$tl)z+#+EYjb+<0FB@KXsLLzlE#O7 z>Pm4!%MF*eEDMD|W=}-sfrw0yh|KEJ6T573cMaAo?2vS9_w=XrldOrup)?$d)KZ#|c(`RbP&Cqo_D$5BSqA`>qz|&vwC^ZZRXLYtdh=Lz9nzmj&!!N&AMYv91Gg zSc|6VyZgFm&+Q!vQ;hR{hnt%rNyb(MG=%|-qCNyq?6Kpy*+qTwUD2@5irg4Qzb>n+ z*YtlSMu@yE!W;Ndb=1*X;?oI!2X_aFB4@X4-MILjI0RS~U(v zmJZNUYSIiW3nyC9r{t|{<`@sIuHyL*SoZ%|&9tdWn*R|rGw-Av|L8y{__+bAAd7#9 zmq}!3K;iex9K_4-RC=@4Zr?HbLLBujCkmxUFC7Nre<&7QyLF^ld8iH?M0ByB%-6~J zS-GD(hjX4GoFWM6*Xa_QMt+~cSs_rvH(gi3)ECP3x$>^PXbhjTdk8I+P3E}4G`x8(HTMOM;W>?S zle7a8O@VF(U;cDXc$5@)OJ($=L_RjHap10W-Em*q8a8oQ!=clB>(EE@)q|`*J%*x% za7+FBF?8Pzk5*?8*SU2aLGIuQ4L9}n>Y)|>&C}=(9@qeQ2KC+!&@SjOFtuJ>-E811 zJvYDJ&1~z{{Xin@RM%mBBGU`S2aOgguRf>eE#TwEpk`(~#O%$&dY+E;JQIufB{gS= zR7CC~&ppA^AoBZr3aEk+z(;ZKcyNR;m-F%8kx92B!2h|13GYhlN7e;=MhA2VzW?Xi zU%F$E|L*^n9xxi75P`qT_)W8W|8_mHZWk7QyRb%7AHas+{BN}Wt>@y(|G<&rR`nb2 z-Oe%Qc8(=4e|zn>{C~OG<$vJ#YU8Qq-AWx#CKAD~XL{W4-W|3(z#~9{H|{?e!_%L8 z6d_9XOy%al57q>H2K2MKc<&yJ9yl7r#4fz~jP7R@@8M0q&-m}s0iM8TazCps_kI3S z|K03=3pxBhlZ)O$j{48!apZqh?w78+cJP0(1@P02X^(F)73V(8A;719f-$-j z_R(elT5?_nQvwSJXeoPtvzRQZ*$QAKpld8}vMZQ;)&A&a^^{GH6~amYCzY3g!nS94 z;(0f#%}BlC4Xkv-SO_NXIgB;W|NTN>^BI_zf4BVCtp1y)V6*l2x&XX7xd|)?z{~wg z+99suZvpQD7}$&e#=^Gxuj}9^`QI7=KPMOcoIJo-7#I9pGXIZ~Rva;a*~@t71ZM$zL{as78`T zo6`(4xXe1w*S8`ZzTZe$^)hgLZ&|6D_W7K}5lcmgWp|13-tvUeTR(^w=4c36J8mx- z4n_0V7FBS-@DDn5PlgY++o>zL%@AiITt*coC$&nsBO)~F(l@Evsc$CTcHDO63OGpS zhR2G7t9dxe@8v!c@tQ5WAfv4AqlPlu4R!1!j}6bI1|Q)nmGk#l7q0NXQfKpcsf(7USni2kVNEr zIuk!{GqofOa!GyED1fY$4Zqf=VNhN0V)g7R7t=UKe_);04@#y1`OTTKtLo)ilPO(UXAi|vo8W_ZlCdM6ek4jdIi9w&a1kDw9 zIK+1kvS&gHPYoq_OldyJJU-p@@xGk)QGUuHw>YYJ0pec_Qz^>pHLkX{b6l%im~jd> zj?0}A|19@M$2C(R>;A-##&MEb!;(~KrZPUuQ<-WA4g(v!k(L|OJgtuBx~B~j-}0K) z0#dwg`&e0I-xa)y;?{?En3(2$)GL^^ItgLc{%+p%%*so)RBT_}$V$a~Tw2RhLmGi{ zFM+5n!QtCgM=Z$8X}?qY>PH|ibSr*>#tgpC&b)m&m*#&>={rebcu6}i~#GE~tX zqL`~+&Rc`6J~6ytRJWzEC!p~_KqE*%<79~vgH@~74tO}O04hJ9q*=}%E&qJBCagP1 z(Pp&+9*iSG0fOI8+x@#AkrIJN>i_N;JHRFZ^`dTA*NUquh6U1q(d4?;;Fa=+Tx9|J z&awb*mRdY>^PVvi?H+Wl!k3e8w>iYDLf0TBF(w)cx^fz$O!AJDp4fJq8pn?4%Eh7- z=yl0MWX8=RmltS#o__evnpKeE@i$o=j##7EqH8Zm^96&42PO46gX>OqIOB%!jt3;+ z6+=WOp4gy!TS@{!NSh{$TX?Ox$j(FOzV(#z#|my7;aX(E3?DO(0~!RuNuof5uiFli z=USV>=)SSq{=0+;55n^kjO6o5(zx_t zKo$o06%4#zRb0yU9d)u!Yv)W}*#RZCY3+>3t2cc|%BI!)~qgsj-iwtj(qbE33pE+r|b)imyASty2_K zVzcp-mT_|KDlPZHz=t@5>iKdCaB02%v~@xxu=8jUg>)C%7goh}Rbtv}&Ev4`Pdeqe z^D0M89&O6BXkqfOYqJcg5LIxUQJ)Il$R?V6Jsi$nEg#i~t4zV~OR^fRkd7363^`+N z9I&g4B>`+{(d010uRfJ0xM!aTBO8Q~nZd}|W5^4~!b(LzEo#vFAQMrQToPSTpkyjK zp$clDpL#e}f$5|oS1wx~C&6+Z(OhbG#^$|Kw=OwoFl^FMH;zMUh&?HuWD=cp;%|I2ItlK!b z{#h#ZKY47^C~iQ3zo4NWc>62+`nfO3>uXg+P{dUQsuS6zF~OF=*}xl04;zP%Ac#mH-#Nm0=gEy2Zq4sAT8$m&8k9knu>;%08Wnpg^`B5m0sSgJGKc^ zd06R&F|Zl|c`(*XrvKLq`8_ZHX8G?~{Wnj+R>Nm^1lXDYyxOM(EC#^K7S(5}UlN@G zw^xkbOn$(BlNNuP6hBAjKO2DoU|0XSPJXhxnNGk!{=Z0S2<30Eikz)nI(4K2Qo21Y zQ7)lrJb5?oZ8CtMCE*y>#$%MJR(v_%n~`u>q=Q{#f^FILY^qeY@?n-qR{h|f$YCtU z=bA=Y@Ji_B3u^^y*DSXlqEYp^ytxM=hf0i}Yu?F%aY8nWbQG)+vfSwKN7cXP%>@BX zte=%(ca(Dok}mDkn{&og6yR4O66gsGZo z599;R(+;-b!mc~KPy(KtpVO-)mDO4Wi9H~ur50rVJzS50bC21O^oUugo=c%O2e#T~2%O>#nyKoI! zqk)XvmdYt^2IlWow(CaK3}PB{#zfg5E~)%k4|Tija~<6nM{FSS+)!mWcO_%^m*c^b zkb`s{^KnPF?Fq}O99-TF2fevPY38r}$*;uJyA>~p; zLOpAUP>~9PuZpm*iV$L%ADZ3^0{>p+WIx+HoWB!u;S4dNSLxxP>fxa2p%JtZ;&M9#+wK8kVPcJtIjsPwYxRzHFqvZv$pu@m={wLW8qjHi^%6l;3R9{c8X zT#K!9;CT;`7&1Q-v_P9U|(DN zl1h16B?eP@zeblT|8$5&mkJkY6-d$^v`+R^LyaG;iQknERjP&35sNkvXfcKLBYUbY z$5A#$3y%6;?Sn=vCC8t!Nb~Un;*)f4k`F<70#677-kKd?SyVCKr-_@8Eet>x8TxdD zO_u`~=>)J=t3iD^1&E?e^^J501bVCsWCNngl<4mfB|}B;5=*_|$jGFrZ05SNFtSdq zz$47giA0usHUqC-f-3M2N|q_n!A01J&Aty>706as1GM3~|1EYzval*BC`%5S5e`-v zTm$%wCdW*n-}^i)n-@UKMPEe%uaK3dQxGHgx~?ruPuPL+BYjn_3KK~{rTEsfv3f(y zA^M^;nO)+LY@n|23hBwp&{<%WovfW!5Knq#*h^uBL7#jmS+*MQhoJmK7Wrwx*N)r| zs+|Y|+QgwWN)kv<4?i{p;f#KL-87P_SEPsdXP}8?j@6Md&ZfDIKE-`bh9* zQO$jJq}{Ema4Eqav)kdUL~CrcC|czb&{!9}BZqItMPj*HPK9O{qBOpPUpu|m-w&6l zGph8yD5w>jj?}pDzS##@J*jyTJrH_|mfIg1OS{EG?&O2n@H(I`ki&k0FrdcCLaMP! z-+r-(#9gd$|NR+zkS19h{Ubg5MN74Lkut7b_9AQYtxLSJ9ySgKp4>+Rq&ysj?bjL0 zlPmd*LCP33geCkgP)81^i^@RV*E8X198i3@hkJL>bqiWcbX@~DP+IFE7lhlpV*hKe zV{xET)SRitt;5zg$of+UJ`Ur)=_Vq*%w4=HxV3z}-aBKY`iP}h_$+S|nC5uzZgGZw z5AIWogmBsCQz3hG^fynO<)mHiXn*PUKi@>!$J_)LooG7Cc0I?Y_!0G?hMe_7ebDQ`gfY#wb#xgu zszY2mw(OitIEvcr?I$A2imshUkF-**o^&Y7R>6v)+j_FeclX=pbLq( zL_A_GYmT7Hy8GsPCD6+`eTb#3IbfG0dszZPzdZF?%obV1h6XGgQk3~73QAc^I4c}d zoY(>@o;9{>JVcXH%W=eGuJ?_O54`*kvUBH?k(x94xR7sDq8QK%N=6CPA7+6BOcO@b zyGA2|q6|Ebl!+w(T*2zI$s??v8h$e^hf+5Z&bDh{pvl<%G&UwRpTsZZ2N|V_o;SI& z2wZf!DIk1X;FGbQ_roR7>u-SNT~(1EmkgdQd^*HqY6|L)vgE%zw#lgXew3JI+Us87 zuy*h0AlWAqGk`fI#UZ=OeSD~22DPv zrQahhxf=y*|E*wP#V-X(Z&6V6Rt1y7&NuzIBIcfSDD}BkK&gH?F)6GDHqyBE!mOO8 zFkAjQ&L03*NSOhn%2W+F_r~-)pA6-!$(4o7=s7y>)8{t?3@SRLTQne-NC6HhShrh( zQvR1dObTOqC%*AQ&tf#0a@y)$!5}cFKG$uyxFZ+ySu-iW@{Ni$o&6B94GbqR7)v05 ze3-(A-~Y%q`j2TCOEB#iJg3A9LM`&sbe5BP?0Vk8U&Q6r_b%$WI1%oP7;~$27pZnN zm$x**gK4R!1>U^71iUYbrBy6M%4$OWuvE&xa@{khq~~8=?5Li$A=sbJ1a(=rHSeGL zDCwu38aPYDAHz*o-0Wi}^6lsKgwL&gY;8)$=U;-R#d;24*a=aVY%JUB<8&6=@XFp9 z1@2gk(nSdS(iaH!3h+5`gsl3wQ`ZLYAB=yD%&^C#CmDQEc~~zK{I9o5j0WOu$QU%#%OYmxcFwFgSaichO2_XBF7_noXZO7nI)G zkeog=`@`C5=Uu-fnjE~B_n!BK?%t<5c%RXz-I*+{DmjWT*bz+O@pQ7I0=8ef=U(w=b(luCCXY%{pMuE#~cP@hmR)u;<^08xO zA2~Sb_l-IY=hka`lStxRE0&n&=WAz-AAX--RGKN0YG3pq2SJ9$H4c|JB(IBiFl>Ts z<99Bq0=c4>Uis{ZiZvKpCwurguaNFYG!~02)=Wz%EcL0BV(1deTB8`pnJa4JQt#=kLnHuy}NhgWR3}?a!T??T)L0`qka#aR~ z$~;!9gu-r&72({+Xr~W@*4nP)-#~w)UMABq)Nk&xFuXSC%j^{ErVU{c1=2YytM-K^ zyEf27snhdcST^upQV068z}{|{d-+HC0BXcIn#^}JnGv*zVUp!WtGDm0-s)kzZ7Y_# z;E7%_Pk1WSZ$sT!A($*neH{g!H$|e=$k=t%#u9Uk%<6n$7JT3A5L)Xc#b!q zC-APVw*~7OLO7J#!?sS9uRk9(W4c~%-oxg~{EQuO+BAyrY%f*uL6=4oU zL8|@1lHuY7!FZVAVjcgPo6GEprnFowL=QfNi#;@*MYQmt!z!mz@Z646$U_Gq+Z|`= zj{Dp!+wL!1WfgfF4jsAd-hH7zmo=c?9RMw~>nlU+b_dHD2uO5#^EU73Bg!Jh55_ZT z^KgHOw@zyl^gp>~kfx-|Dw6vnHdoG#b7uqz3Xz^Y8TK-gg<{eDeDU5nZv4t& zcYh#ZmAIG083N_7K7VzxxM4Fp;repx9=TH_W)hZlBtdbpi+Sg2n>vV{dTy#Lt_>ue zXPq^H@N(-3Ie^JIw8RF;hR<+YwB~(gi)xoHz)cPoCo`hxSTqHZ&lh{%7N>hK!_~jr z`-Zyypqu)zU2{KAUWPB9VLNdUCt8{|sKMZ2T4rq$5Dkql|Jn8`47nM6vs)hbB26N_ zbM=XNN2r++q|n8z&im@?+VyT>44zl*njOEbGVr0N6V~AA!1;zWwcmDg7O6NCE=-(% zxv|Mb1RCz1d;#^UCIep3Tu{It&K=)58ubajY*z<4b-cK*xjPn?VvF+9urXB6c;=l90bnsf4VgpxbY)YhUXO*~Q)!2j;Ub7$ z%E-w2VRMtY8zg8Z?NK}g*ULn*o+*0G!l_mnjkb+(0cqbn{w3e9C9j2Qqxi=ft9?pj@q6zS3^U(vF&_=DGFn)JoulqAvx`|dZ5!c{dmT=}R1 z+_D_vlF0JYFUQ(fJ>!qBv;#ZY$Ie0xl}WT{BRTSYC|P8?k_=W6T%&qf$*e!-ySYY5 zMD`>Zn5oS@m2cUjN~m$~PBNHVq_O_!s-D{@wpx@7Lt4X-#nQ+s}&1Jii*zOf#)mDe&!glB8c4k^gh+eT)f zL_ScyspzG~70o;cE79@HS!`Ced8otNKw>y$L@GN}4TAVCCL2SYC!Jqne3+Cw1Hp0a z1?X-9sdY~6md*|5-@?Q^BYGqAF+Otf9SSmw=JUN?RT)L1vBR=5Hg+tjt5>xv^EZvbIO#deqEgYOmfF3)(j*NpkK9OZp^A6miqM0w;Eg@iYlImHU^ z7-O;@y{}XL3;=f+NP~e^7+8Y=lmY-Sz<@jqIKw~^3^c>Q5)2>}0)P$%q+!4w2I63# z0S4v^AH6a_s@K@}HJYfi-|L9%r`Hm1L;ce!)vYupo zqspFS6d1TN1i&r~EW^M!40OW)n9_+aCn@IWu|r&e_?*ki@fAdsU4IN6^G3kC2oBmKj^~+LC{Q}Zza|_{m z4zqwFuZiNaQH*J}>N8z{?7AS`+~Tm+Df9+!*s@)7pGfN5t6;mX^Tk_hk7ZtOlIu1G zXL-&TcCmK}UnM^3yy^S-zP@SA&-eGEY@?pO+U|AYF`mj79V15~O@;^CtO%797G5oA z@zzK*Bywj5=Yv-yNT%9H`0R4fA$ps#b~)@$OjRYx`K=A{Q_}q-4>cvA491(tni9Nf z{UedUMWla37Pu&NkC2Nawzih#H(Wn8-uzCVvmM9c_!>{%JC?=K2Dn7B9KNT|c^tvw z_zF)RqdS4SF*x6US;BHlZ&Q~`Uet-{@HjQ+NXjCwUlXmBSI24LN}E%E-;%)_ymk8O zg-P#tF)r)sxgrYTzVK&+(9;BQl*HGq!;BARL?A)$Pm>;*!9J9zr(9@p=K1k?--`PW z-@w+5CA9JVtuN&@wYu~JTkfB5je-f{i|%P>ANLeI ze_e%bIl^J}-&aptjy&i6^>V!Q?d0UmslzwsdZm^BAA4^dRb}`6i`s~Uf*{h;-Q5BL zN_RKXC2TsC21)7e?ru<0>F$nAY`Sa1eYWWNz3=a<_uO&D9pjF1&i!ZYwdVS)x#o&T zAJ+4kb5)KDG2vhKN+m^Ni)rsFBV8FZGM)}Mk%fqU40I%B43fV`%oq(zQh`L)H2uNb zj5%TW(6_xosIS86ut{DMRTvxyREH7lbOj9<9TcS%1PM8A;Sstjq|l^SL5kd57v%{CFraR9m2M#Wi1*7 z1?M0VJQ33Ink>=+NKT`#;VZ!k_en+~l#n8%hmBaIdw|1Ka&WgK589z^HeSGai`>(8 zm(^Tf-KL`vK?W`7&D0n=-eUlk41-DWpp{cA00}!Rc>_zNVTn2{F@hyFu;e2w34|pv z3Opl4;1oN+9pW3#$PPH=gdVOkn z`pBC`AZaOPINLyu9KBwCo*y_P;SW=$XqLB}cr;L*2I=T(vbf#47RyAOA_@Fl3cGw> z9fVP0dwj3XHG52$oKZ$V-~49#3p+LUvX9I-!q}Y4%xB+ZPUn-Z2j8cTik&r*^I!OI z!Kady^gWh|COR?m9HTAt%~pBdI&SlT%iL8}?hBuD z4LKUb!}u(q(Wy8L7z+b3z<@e1U^5J82LsN^!ho4DpePI&2?J8VfHI`PhYDNTT?g9~ zrW=DNpF5k+DlE6Jg7i3q9_FjM>y7Fv8#blrzq9(R>`aSpb|7u`MA|Gs+U#tZ^eKyl zc==)o_ApTBV2*D(40=luQ0tN&DKUR8Oa&_|vbwHew^-@L0TQ1NGcg9k92T4i(G>G? z5oVm7NF?!nR)Rl#_$=1f*dhQWK5lM;KeBdR^v-yM{`o7&ev7#1VHosflP^PebPIhdgRa zYeiV+A3JC4zztVr&sj`Xdk&zKLPiYb?A+B{&Mlj-lPVGHR9MGor^q>i6|}5xhSkOb zci5dacI=siGmUHjM$jmtnU7q`z*gVhmV%vs(zFQ|_5OL{7qzwqo;aB(?m!?EW>Uu`#LNW#ye*lb1n&KMK8D9@QN0SR3EPjpuSL^E zeCQ*x-!OU+Q1UQ&5#SXd>O4*gBS1_&?Ru7lCtz=k<^Fin$ZShMk0_+0!<>n$vivbT z9@QGCEh`K6jhZn#Vt!oR_cJXEVbN?UM!nQ_{$pmz&KTwlq@5V%wp2(A^D3XEcwCoM zV+V0~C$bnaw|5gHbI<)K7Ag6n+w`Vz%_^vN8JRaJ32-$cD zu0QY{gg=l^5cw-txaQ+u$9dQ2<(X77;+b78e0n z$1%Y$LMkpmi9h@^8Cm`HpK02Jeuf6=yoGo$JM3=etrXE^y^;D6E1_-Ed= zX7KBO5vzamte5{hRDd7 z`oHMe0A0GgQ01RI?BDF`q=4QkgUEmIuzz!k9VuC-;}rhM!~Vs-Udf#>GgX8KNc6vX z*uN;=fd9h(_MQB4ANX%0|Gg7_GuD626zplB`uGC&OaQaWt_FJ;ezB)5;pwO2SO5vk z+u#>R8;DQ#o8tcq{@*Hrc`hIQ%?DpHyY+1NKNPAxcHQnwjf#j<&AS1&`m%Tl_GwNf z<7LvtQF6lu+kD;oPXyiI3!(V%r00>QG(%w;2SZ?sn#~j*Nam$;+_dwc`j7H%Ie<)2 z-9b!5_IRrdpD#UQQY`5#eQ`RZVY}jCdEkmJTG07fWeR#=8hTI~5~dKgK`L&EAFH0F zzs?nnMZ5W8WPF&yPZG9nx5cw8?Hj$}M$6iZ#B9@H@BdzOw@x>%VBz>^#_03B=fj)-5^PP3lvxgRl>ZM!O387}) zhxzaJ#GXzZeOgOiNG`d<8-;CIlVk`N)!2>%I#;>>QVmhtOkaoF2_=0idPzk{w+R(4 z1@i`*>Y6SSZR$sIE%dA-UOm)04LMJ$1w>eimty%qV{ ztPq9%ihQE&(`=oQ(g0j>6;dC$xvR&0;eiX>?YBQa9`~KHd7P@ZC00R3zs~o}CDw2C z=J(Ima-ZKZwWp2Vu;u|nX6^`mgB+7nJkcx>c_*_Mz#|&q5xpfNi`HNe@`X@p&=E1f zLunhhHpDA!!PXhLz$2C1VHGbg4dEr)!{S-kwIV{_@=t+VLZ>(lc#g6ac^!^ZcqG^vzpG0XiN( z`lZ12wq66M7lCy!`AfmcZT%=vkJk!x;QC9!;;;HMpaaZDfQ47L7IN|dSB*%cH4Hsf z80>~Ag4_a?E^ieby8((i0Y&Y<6dnGmhXRTo0*WBlw~D%cDLMfZQNa{JfAx5Cs|fug za9N2rzsw%i>)RfmJOeIr0H*Bz8-=3>fFi29GC$``^;bO>Q19;{1!lowKOT%gdk9IV zpm;JSA}5Ph5R#Qau8c47E-ZVU8)$g}yDmi<5W}uTxh-PQlLJ`bUI?!=ZCBW>DYwNs zKYp1KUf5+4FnvG!`&kqO*6yF>U)4N;e})tN1s?EcxZa=PjfkHAGVZsn(R%+L%depw zvHy9f#a~l^$$;Gy6X02@;8P|h|0dJw{m}p@aB4XrIHiGK#zyiG4#HUFGtN?0A(2xYevW7Y;BRS9H0pmFxYQ|PgMX|6UPA7*=I_y+)u?=Ua#6I!KRd?AnE^)jwh`zaiV{16zQo^hK zO3gY7e8?R#HJbtv8Qirq_;~vPH_hVX`O9?#YfZM-{A{mF;lA?1=U(Wrn|g<7*%!b* z_$}R-)$_;PySyCuaTDjV4M8y?{ogBDeFPn>xNQ)4D$WN>#%SDSul7 zvk2w!%NgGaRs0tY?_f;QBgnq8ZI;|K-=>96eDf?WEgBY`l5mwzUa zv~*oOEItEO-Z)T}_95n=LVEmm#&meDMXu<1nzdo9`?Lp`GzxHED_c-1tc!8gc_F2OcqNRbo6M0-gx%P+~pAZ zG#%qV|46a-RL&EcS3g&FePPO0Y(TT1H{C58?&(6!2K%;kV;KJH3+do1yCnKKu~LZ+ zXNsyH4s`9Tn7eovFJcYWhpJ^@pHKn7Q&;R;F>3$~)-E(0G6&7K@Bg;UnxMjS%Yk%{_2376ZlA`#voeUBSiwjUYDHh^!>PIfH<0~EtlGyoCy)iQpB%ycjFSq>?pbXW z+vz+#IZeEo6SK6@Ujj^!){4#jCL+dDgbRNksPZ=3J4$x+BOgS<+wW<@ z)7K9bv}d$Nl;r4_6N;2{&GZx{^BS&yCz0h@;1nePrs5Pjjti|PdR`HQ$J8@$7F&xm zA}L+6%>=lXXY~VK_lT05Z)eUsPVwOa#OV94Szb5o9zRat-_qkPx^j3 zn@>z({`9z>=G4op@dFMDGMRpQl~;jacenC?Qv_gm0MJ4S-V)qGxDk zv#?;*9vuPvy))1rI2AGL1@^8 zZ`+gn>Rzuyejj$8Q5bV8NBSk#2IMgR^6ltb`7z_Ixf+0+9T-+r_icX`zs#l4%X4@L z$XS2Mb$<<)3Xqck^0BU4yy7oBB_Kx!Bax+ILPYmpHEtVund24i0AhD^EpoIU*H~phQn6j5AOEd zK7WRH1B-d<+5N9mAqNg%lK$$5gz3XujaO8PHPf54yr|35qWf8!MF*UtGJBH{PZ++WX{Vv6pl!j4ygr~t52ARE|OAIv+y zf#j>1>DYgs3;kCRf&aq~jeboS>>sB;ADgLMV(bD={vbU{rlwsIV~z6b5_?=H^J8a( z3S}QV4QBa+xL^gq8@%{9gn~@J_Ct*0{|Xk#7h94P~@>h!#UFIFDvmx0b99Q$Aq{4jBWuz5fCHIYK%Hk<>1GMVnK zi$eAxK{ST(Vy2wVb#@Lz@wrrvkrHl_>}n5%L8p9r0!P;MN)H1%nPG4)0|!W4;YY0A z=G(rR7*$aBNSs>H*i4NohziK%j?GY3f=QLZW5ii}cF$@gLz&O|T$Abd-Rm{aAa10F zurLPUNqox>6gl>=DV+Ofb|Kl#>j4@W*<3>%LRtTJNvyF`h|n@{nltK<`s~K4amc*! zxM*$r?8Ehz+WeP0+FJX%FGF&HxL)F)j9TPf5SY5;b39O4Hhc&N_J~(!0xd6P>FoN1OfUSBtwSxQihlE;-+YohfA!!C>pYa}a@Cy|cqu!FB|8>lNl#(ga~dAO0-hG>!aAHK1YqD$07my#MbE$$s7#>M z4~i|d->SnA#cC{?V_#fen>{^C`g}F&&!gO5B$auBSHyM0M&^{y$5pDWPhD-+aZ07g zJpI91Xg+C3+m6`mL2p_5w}THqg7Bk|yS7g&kh|?BY3r*Vb?d>r8Q2NL3u+S`cn9}9 zzn>KRcy&Ghg&gp^4t1S739-iuec9A4+r8joVZOARsN%~dBJCAN#!nNP^5UIzeW${E zF-cc-+jP5-e2bACEB&;c*qP%*T7aneIDy{mQ84=O_)73t{^AVI!^tDu5`2P8> z#eUbf69*>UW9dhYx_z0Wf(&F+0L`KN13+`2r~_yYR~kHMAnRhh0FFbYr|s%5a(s1* zFab6M1{wg%M*?7(egKS92!NrK(=ym2&3u?bUb;ytb8oGtWv8=04tah!j*H=L9Le3x zJ(|=VteEhwenaR{ijV5B!F_h3%&#<34AJKPL>|NYx}|uN1%ej4=Wh>dvu9oB6$i^f zu{2CttN!f)YZLK_Bf6`uWC_--$|AdtPllo%YrF#2`NS*t*rp=1tH(nG7})X-xpVt^Q2JLQ9K%C*F7F<_D3Jsu-RpC?T$%Lt?YYa1Od~>6y)VS{+ckugV>s7=%Az z;9HOT1&Q49$!$ow946tOZ+V=;aUVJLGT?y{EYmjgQ4W_#;&md!(1Q`opBdd9^}OIP z(v-fJ#`iV@e`Xk}T@*UsK9@EOWWH=N>#z27 zNlk}Z36{}6X%aI|qp@03!GO;W?@cmU90+b`>e8ygR?3dn@fGdESF}Q}Xs>{X5?H?@ zJznkWMv2XzdvlzUey97d5EMo$L-uY2*Z>L$O7Xn|2nm@8i(cWLx;(_8xn5%vjKK-$ z5#Rv?K-9>QL!b!6h)mxl`qyvSEdo%5Qd^R>IjOU`Jab&!c=D~7C-cZV34aY0xwocJ z_BGXB>qzk6F&{f+%@)yR4UYs#lOU%WR%zsW8J(e}OC=ub;e2~8leFyiPLl7FYP!y| z-RMNqNV0Ry?#Isf9DCQ(c;X3u0m&xoimHoo#t7vxYCOv|{OuU^)L*&L4Q6`fbli66 zrMzqNi^D|m3=8OUqyy0(p$C+Vw zTrkhBQuWnoCo5${F^WytCGK(R=b4k7r?b*CmyuOJ3E4{P!UM5ZQd{R$;I^?UciDT)H1E$@AcfG#4`38uz&u>Y8{5rcKt&?2co7%!_86qh3 z;0zmwl6zs>B4_Vob7gAF{a(riOOu>C3p+;ScNWYI$sJ!eGzQM1j)sEX<&(tHQnfwB zJ4kq{GG3r_U#%Lx@SV*cBNlq~X-~4HZI> zFPEa9CS9((yMP&*r{X?tubt5M*2rHo8g>D}RYC82YkIIKfu!ygkko$%La@@24hMEO zWQsG<9(FMT!pm-$kO?N7gbCxA03laDO!!)CD}~x3(yn>!#N);mD~1wGECN=tn4%gRVrgQP?S5YiHUOm%|%~M+w%>s^#5l7cZRjR&C2?5*uH`z34LYoX zFk!E4j~cpdUIktB%sAE`#nv8HuQP$hIi}9z4`T~K_TX~WHS=ymciIM?^EUre>jg<) zx5n<%MeOK$=UK<=NOW&9Mqs$(+`x>P;`i1Nq9etAxa4t6#Z+>3x#TfV#f-+BZ!=%e zL8}2=iu(7!wBnWA&2jhmj%di5oVSB8NEzGy5m}#U-tP%R)yuCFT`dz7r6D5;7UH#q zFFA(aEwkZX7HPQ@Ird8H&NVnOCYf}~ZIPV%!B-57d3EAk0&f?3n!r0nfoY50&KmG0 z^3NovwHtd(Yp&$_z~?xct)RI<2fB&`p|ezWcWk|&dO4=`t>pTN&#@AHL9>Sr6de&# ztgY<+5U9c$(;6(f4gji{3!2k_DkMm;k+Qo`>jgGrK{LVMepm~db%476{2_pDN3!#k zjGo|3o3Le;k@C1_+0M&8IiGar8sq@?!;zlkt#Tro2izj$1axz5G+^MZLAtBwPS;!eY2HGpvQhxx2;417j?zW0>+ zL*M{H|8ZnCFQm5DX2|)>GCtU~O@&&-08>o;Yx%@NU##7|`tr_M-=`N#_H=I5w)Rr# z@IF6-omWmJy2*8DLOm^$uYJ4hov_4kCQZh$w{f2FO>Fq!PRLxi4Lo&U)pK?^q_E5< zOET-kEplTrVJ7MZRcE%~f^Q@ysD(ah1lLTyBZ%cjjlG8&ONffO$UtN*JTno)z%T63 z6Z49Q+dw>Y1Bb~mgIn-z7O3ah7ogzv-m4Q)B8h+)s5LqeZ+?$j?NKdxV@Kh5y~hU4 zZ#w!`tdX!VAhPeKc-h!>Qa-h-P?9|ARxIl()UT!H;vBg~<#au@bxQd|D_NcXVXyT_ z9#=wr7@zlkdZz~Tv*Gpuqz$FgR?KbAVvh%{cw2}PF+$t>#2~D7T(pFH{sqTC!9CNA zEjZiM!xPE@;n@p=X;2WyxJk^jH`Emx7=sB;nT^uh);mW;5Ig71@Tf*}*~N47?eF67 zCL#tqkBxx@F;{Ts6sOwm{e7#QQ)=MXAVG;z=OxEM;nf8|% zj#PVyZQD4kP$DzVqXA^0!^a`cYqa{UKGyxu4%AKScA>>3&5WY zstevEz9mei7ZukfSrJWFs`b~TzE3Zz2TFoJgf)C^p|^BNVU1*<%}>UputqhI6+BBy zOP-f5lNq;7B%Y_ABo0nz#o^jZD4F5>&VzY2TH8JMrGVF|BP+yW!m9Q1xz?SBmU=P) zEe#<;6i*I?qg+wp9H0h2Km{kW4cmsePIemuXaf(iEsnHo!B+4oEQitO9ejzGx+b`B zYLlF=o=+X0@HF4LD)Q}}XQx(3$JR>~Vuo5P&pw8|!RihxF0kdZ$g)0%X9Zr(Jsg=q zTIX*k3t~@NKa5dcW=_0P_SwYIcWQ&-gtrC^-8qH^ z3}U{KDm`V|?_ZP?JL$h@4aumF%Ej}yX2q^AaU>1t?Jyq}qbYv@&k2wL#p(m&Bf3hK zFvEL>$*ThzMx^H6cf9)?kX4%ztYfMuG^1l0B|5HSdS<-mQ@D6EW;zkzl1)B2OftV~ zopqGHd)fN1|C;}LcS>>v!k=}hvNBQzY&f}YAmY6w4TS#%M`Z#EN2|XJ53tYde4d{> zg(3xRBa$5e8?w_bEx+@sLwn-!l|e+_{mZk(>Yb8Omw9v^NExa|>+(&lZh2F-+ttj} zH#V)4Y2BN7A0VVvGXj;Mw%c>bh!6eq&qkf#$jsfBB8Krfw?C!26YJ8jI`>$pX6(7d zZ;&XRyXqch`D!*^$&H`jD*)br`|{fHm_Pi5ruF$s8Y4zS+(A&ce0vTOW7{1O#WV$F zUN%LmttV_*v9DDnz&4&er}D!{!zKh-vypa!{M$MzsLomI@*SE8ZwnDIeI{6Bc0AcA!65EvM*yvwD2V zbKa*#ELn=aoj06>OZPmEHlKlxwI-vNiLA+_hsm(o8hq%K43#|$_($T(54dHRXzu@y zWM$I14{RefD23OsvkljDP^EzyYo@y+;@>`;SDR0Z!xdP7Nf!8ptjLxhxpE-zPV9jZ zd*FBk3(;F!K6=lJyvY*qS8Bb!LO+6_;I>ha=X^=DS-XD*IZ41U-21L{ z3~?7|6vWw$?5z!ovAL{@LjT;Re8G44d-Yo)GA2lSxnzkqxx225g_&(wDa9d1oL}G6 zm66}qL%t7u@K^`Ffw(;zzT0v2=wxCA!-IyQCs41N*SnJSTO2^zS<3?=(Vy_zN-nE$ zH5WK3&s)m#eCR=mmENB553cnJE4#X0;IoF~+be1-cyGIuF>qB_f^Ilu;nd@-I+}vm zpC~u9_0BAndzF5-TEUZ1v8Xn6dBX?OI5|W26Y=h*Iq+w^p3tXRWmUeb4MX*Wo1=|O zsHFg(7*%7md~9|$oYNqe>5N3$Z@*qz8}FK`e>(m)9-a_U0^#d^$GQVNetXk@hO|5N zY;s;2e%EUUDJ-HGwu1=oW{Q0n3ZJiRVRD+?IpkCwjB%3z+@o`2Vnz#Z zd&nf*%y7}TF5j17%8jSORJGRIf8y+}t>B$V)<*kG7Xuy$<6w~Gejjl6>-GN?@qy0Y zK1mt~@POboAtV6AfRKPplmB#dr145*KbYBkjd9T2G_zGq%e|jtn@tL~*Tr`pY4LL? zKMKyKo%gYH8xA?W`QEr}NY1UG6L9DJxi)RMIEprHWW@9J(+55j_rM*mo7Wh{QJ!Jm zAN}et_v*Cu+|Z6%)BfgI$>rV2d+!&_Ve5H<&*3vUnai#8c1(@<^(@{K_gTvWf7&-6 z5@y@}c;j5S4VS9~HM~B_i@x3{8rK*)d@R!f*8#vQXyxO#%q?nU_fecKW6-@8dA;Z!adW=q4yi>c71SlZxRzH2HT=j(RXdqI zT@$%+SzhEpDpY4)IUkMizkgc4ALh*KDy2(W*K$g!cvXHL9^1r2;?k(`C?V|R6UuYx zR!|x1(#TI&_a*sic7|d&d3-Wl>DlFs_S_w-p}V6rl06Xg(av{p@~7;^R(Z||Z&CmU zJhYKzeD`G{KDU+Bq+BK|jo>w5#dMC@7;Cvxhk5IspkY%!<|{ZbqIj}#*rg}21k))? zS-gRT0ddP}*6EHV$H_xBUh8?V<>_Ho`RqvGc2u}j14Z<-g9{(2sHD?C!ogX*wRieB z2t1WL-K(KuetIYlhZpT0LOyyG$i-v{niZr z%_jOvm{Jp>eZ4` ziQbC$2t^z!P@i$c6h?mhu42yefN?~{h(y~0@qJGDfaph#N2qG;tJ+nzXmfP!k2JU{ z%NmcpN6C2IZ{Ug`v5KfH5cKTPaN+=5le63_=sAY#`IM^&;HS`ZiX{IMW8*oQxq1eA zLWPyVV|xjC@Mt$nJzizY%SjTt<1gn|iOH_}+4k-u{mva9l4tuPOFH(-wVO8cpDO^uwz1iD{quR-Pr_xO=Ol_m^1V1uWfc-xSkBK(VAUi{F$k71N3*($91${$HRh2;XVArGr!L}H8CA_I=jQO-FC8}dnPzD;tu zsQ3|$>rYMJ#OB(77ETMYY=!+bRMej&=1=q{C0oQdfbP6cT;QmCuv~qD%D-pre)LvJ z?*0nNyDK(T!MohyxG=<8I$RTmSJ5T`VcN+v`HISx+g&1qS5pC$%@W{fjKk{!>$7C7 zsEcTxhSG-vh8wc1Emf7jhS*@S4v7YLm>Yf?TW8wKGZ_E^V8K*1tVnFLSQNm~x<2O$ zs&B%oIJ#9u=lGlp347y{*euXkzS5KZ#9O08JbRzOd$Y_mJaPtRDn6 zlAMfBqTWbc^#{yTI|eIQ2s4PMFFsIQpUjN7?BsKSX7@KiKnOc)AgZ4nBR=`o-jW>jX0a{)L~i0EfmQ z)X7TL=$h~j%lW-JDd-BB5^wJKS=U;Wh+8FA^Bo7er|8m-AnAu7X(5obAUMURmp@Y^ z<-5GV;Ny`e3iNOa%y2C4kxi5lO%w=6IXAppw&8ZJ0+y8c_OjXpbZTLDRIqN3q|Yc{ z(Lo|#R>i21hM|n!M*xc)fAyG@G5VKe-B#>bGmh*}%!7<;#XkB;f%ai{Y7tm$4R9Xrzqbcf|J zHM{A^)eehqK2M9lt|q8vYI*u{*7UKw^LyuJXCy#N?nM3gtCH&m>LVEXtL(&!FQ!(u->KSU0D4JjFk#PUI=_Jd5L2m0^SnT7anOXPlK zYlNaNA6lfN+%)0T45w;Uw|tM4)X`)*j45p9+R5>eHlDRIfweuc}uH@$ul7~rU2c*1dN~ZKcJ@?Cd z9B}R`#xvr_ks||=)&pYq#Cob)Ae+qKj64BJQ}yx}GQzQZ^O$l(*&H!wWN{BRujSuN z8HlDkpFj|v$D563|IR#UR-ve>*q)r*-@6r1`!g`QOR0UdrZU)BoR0c1F$*K=+)eI5 ze6~N$eMNgS%#u32?vMH%cDg84SQ9!}J3}Bm?&}&VzNtg)d7m~E{(8K%;NIZT$5%cY z=1!7bOUZO@Yk0d&;3cp7)2su!)6WsyhYuPz_a6BIffBwB!2Q5MyLc6QUjUyNB%6Pa z(bDbA#&?9%rB2WUmv@$+5;=J%v}ZA#eOI=sTZSpJwTbb%JF zHC{Hpb5D-wl%9|HP|~J@A5O(qCmyJh!Z@ThBch^Hoeij>MJCy1Q2O}gB&M7DopVoR zLIw}mLt+RIq(BP5jzp8}Efm2o9QT`pfso!2Wesao_?2q`{WK7c_-W$}erTWnx)Sfv z0-43J+kQz)WP+l+fyG!rSI>f~Osu0Ol1Xzudtpx8^3*PtBo!r7(T^uh1_OTC;{W=q zE^iMKJV+BSdAUuRd%7rQ*@3$b3()Ed$WE>DB7!s@bLzNw zuod?Q}V-QdH9MDnh&@mlb?28SEoM$mXM z6zrJYa5frJN5+tg4U03M{E5mbAy&!GtHI98!_F(s&g-1r&n2_x1zgbMhue?a@g6%9 zuS`CUTdge^;Dswa;`~?)-I?@lu<(UTiryek1iqeMGdwRMP?CZ5U2e#!tk+X zv)gg7rOEj@Q*6ee>wNRguCvJKWwVG23!#6=+2$Km{Qc1YV&0{EjCU?=N=#qQ{E97z z_ffRA-!2g~U4*p(K(xs_?B}e!S_mz+_JJn-mwZmC=L2+vhf5EGq%alw51{^O2-l#R z_g3XwOwsMVW&7$c*t#WcRA#8vhV=oVf>dwf@I)W{jOY}{C$)E6WFM%E*B)4qY$tr} zJrHPTppwh4bUTteRQOyye=(MU>)3*#f=BNwR(_$9=B8~{;E2w2-DBIS-;dK^JX|=x z?D~`e{H;|rukAaBHOGu{lHp_0=%=6;hW3JaBGbDi4zpn z75}6S_8n!!6(7(AUE>wPQ~QHA#ib}I>vg4ZpOa^7rk|eht=e0De!x?SW@`bRe@V1> zI=~J`JkGM9SlHFJyEfXxt;vbus0TGSwEg=;;l!8~IF-|qhrX4WiA~vmnQK$aiZVyxn&;h{ z=uJKkz;GmDskbU3bB(@d&W7J=%6R?+wD+Wb5&h&V)t0x?1YB__cooM>ATF8#ZKxqI zH59Cm=*>*0N1ow`DM--UJ^Sp5v1W(#+e1d5|Bg9*{TuZ1zhh<){zK7kj5Yq>J7aqF zM;l;GcYOb71I$jxI|6{AzxDsi&Q<=${~XM}FoptuHJ=;d?;0onJ|X>|{%#}f-$#A; z_fZGS>zApcrB%DBqziOh=m$N)ghaDYzAw-k%Ly%o3FD$c&tO9SMquem-7rUhdH7kJ zLHc+3cQwyn;J||Yv-xkh>!0D=_XP;_po}@drePeT^K=5X%Fp5@!N1GDs$nbiCpg?+ z;G|x#(Mkim5E$yCV_->t7W+Mg9m@@xRpG_CkpUmK~+b&nY1#e^BQhoAPY^THiw;Mm;s zU`7X^-T!Pl)KGAyr~*?9GvuF5yC=wrZcz(NP6HlYaiOaO~8JWA)8B$RTdIB9dcQGofk zuKV|v`UQjSU0|j+Hh(@tz{;)uXA{5}4vpLGe%s5>=L|4SQt5U(0+W}Wf4fuv_fi{k za=j`f;q8_R4#bM5;VLriZdXcnyy1F8W_xTyLo=T6l~sU6F}%2FaM#2^2`RiTrY@4< zm|SmDp<^UF(AbAD%S~VWepk$+NQPed4rVdTVKaSkPy|D-0&oC_Iqae@eo3LHni6Q7 z%9th874w2hZ%PYu7*$^!j$BVQFL2{EkfYG!RK^^piePAz#T+K+iirXQ@|eRqT``)O zfyT9rSxQuToa&gvrV$Ldsev1B^~H-7Iw}M(hh@5Af)zWCIQ7Ll0d2W~#*vI!qg^pR z5e$Vg9n1xR#)XVowp}q}K&L5z8^>KSbHIa#S({3wjpne%a}c}Ig?F0K~lMw7=c z-I`>@okHwIcFLXmxDHp&!z_JV$a8ghqfR3T&;53Lfj7SSq<6p@pI|uf#urWsyc+UK z0I!CHalor#Jqz$^$Yut-8tzWQfK@Oc2nIZa0k2^|ni-ivS84+tX5xan;O&rI39A;q zz5CU=n2!C(?$~;KcCGMaytP}wlEM=WDt+~|Bxy_U>XpnL7D;7@3f}TYp(qmLcR!~3 z5~A;g{-cwzC16DlQ%C5aG_Zv5Tv=E-%!T@Y3iBh{x|V1P3g%K=v+C(Jh*;}G*YlmG zvZNDnFyK0=WvMqpD>R`Oz}{*5`doyKVXy6*I3l;z7V3Q|oIIF_6cBxS3y5Y=0FePq zgbs-K-vFYXXMl(hCVBvf?!HFkE^XEt@?t|U_{JLKLh~3yM&_#C-MTf&JJXUbIN|}d zkxN&OQ`9collyg#NtmZ%{L{T`43_Q?CdRT?y)`67<{FCiC;Gx}AnO>zYVgoJUcN6g zNQHlzG*Zi0kF;vLUBaLfIWUN&Soei4UEYr4Vzb5d%yTk#8b(T+5g%`cXVZD~k96bF zI$oPOx4iT-7k_!bl!oRrw*qddYd3uS`}Vx%QgMebPP;w3joWn5v-L&W;Uj)*q)ABC zPRn!dwVOwu`~t^?SnM7-@~)swd^HT&dHZtXK&lGT_wT_x@lWD@~Jo@u&x%`*^`KWn?a zBkJ;i$}Vz8^|q8TTd0JKBLEQW;&}NLjvru#|-9fIuPneX1O`wZOn*IaXwWHOz z*M)@R4@}T&Nu|qi9!Ved$WH7kXUYa1re@C_$1kS38&3YoyvK&xH}cR zch_6ZKY>M}+!p&aw`vcd(Ku{tRa8}GMxPn!7*#M!dtN=VUKPY}ISPnXd~d~>zEZ1q zbm}$Z*M=8|9X7FVO@w+WXYpo<`3rU!JI{(Rans~7ko0iTb2`om%-Ff!*y2<4@oWw^ zn@=}I3*(0>>rX#zC=}KPCr*4gD33M0&}?3v4Uud?vZ0APup*jJMUqhZ&^@|;dhn1g zaQxV*?oli$dw}h;#tdb+!B0TOl}c5F<4Ucc6}PoKpwc6=(6wSPwcvjwd9KtmY+#HT z(KGu>k0x3#{(wBimRCXwf;ypTd4-6a22K0rSuX_8Qt~vOJrd>*LfO|Z}{v> z*kbPXo1R)mTqs<`bvMmc!6a5(EBD<&y-@s#>(<)bs6#J>V;HUYOsGd`gdx-+-%A@1 zm>U5D;=+K0Fd+IC3^)!07JVipLWYKEJism#ZFs$e0TxNR*s8B?raQ=S`_NF$5{nJe0@Vh=+p;hC{LG)O9_->`+lM=MJQ1e*(xqW6a`yRk=Ayk= z$F8xb`)eEfIaF5n|&*jH^SH72>rp`CE+1^63~3Y`v`I3T{%D^~pJ!=9@9Le$(+cqXXmt zKJ0=1nFPUxy2gG|32j3q+@=SuRVsxTN|H&MChe@GJT6^*Tsv1&4c=kLodtQ@>{*5} zYG%z;)d~-Pyg(rL^P@$zjp*H3i_wUCb+aB6P)|&gr<7#r3CMAd}a@ndV1ATm*#x3 zlKkG!MX>?$LIWq3M<#C4RHi9OGF6`!qYFRPvRPhmI$U-|DGl-CSgLnT*_bZN3eOJJ zL@-Dht9LnMv(iSO(PGlHJELj9^(hc?&*(8^w2oYjEQ8qe;=)h8TuI0rXCC%=cs zI$rV`GFwQmZ(<|dv_LlEY6tCbT1#2gkj*)%f@E9++bHsxg_npGu!#0GGJu@vJLAAM z*S&i$ogwyNIm{x2sp6Kzs9hDpK*kWrFvBuhFo}?B1?3n*Gv!Wnc|h!Bzua-nkgH-&wFcNF+WGEIxvdUNkdDWeP_ zS?Oz7A_+^>VTlPWaeyU0up|POq{$GjxgaH68ZW4)d;+CD>J!3Z$1q)((Yn8eij)9n zw4mNq0(>_6R*2$YP`TbwoxsCccCrkoeh?sqiU1GNxNk9?x5Y%K)LmpysIc~;MIcxnW;40q4G9{)B^dLs&*hj-7Q zE?(fYfRdOkIC+04lL;DhZ`l33%dtV_E2O!J9PIAaV1;C}J%1)b6X^u4>@p%5N%3#8 z?>V@yF%y*=RA0R0ba($H=HyC~+OY84^BRTN5spUlL;bIYTGB(S5=Zk(Q{)I zE9&O6RPDJKz9U?ARFLuZfx}8${uy<14Ma9WHH z!;a&7sPm2O;dxl^gFrX~Kf+gdQ3L@W9i)X4 z%|!yN50Y4}bHu`jjm=oRtjklo+a!bYTB*lj-i@J<$3+ zk~W#w(C{`B7ZulVtDkSf5j% zcmsUQxEPwgq%(}|{tF4QD7klIs4DS?KHhL-vBf037p0vot>I$haXc4`-AmqgP zHG~!+ISBMlNTKTZ^31ILJxf9Kp4NjR$p=qMzdZpHu&RFJZu6mv@ttrW;?^Uw)0V)g z#k*$2V*0VHb-nUA1iH)?5SA3<^g&NlJ?i-qyq}bft6hI&z3^dyXf29A76uDS;RjVM z+s3apx*v<{oI86P?V%debJ{Wmmz053IGHE;Nz-fF7ZXb%wnN3t2KPG7W>JMHu0$BV z%I$Ei9N|v0%AQd7lu-qNjIgOnz%Q^SXE=gpX0d9WnXTmxdWY7!yQ*z0M4K`}atAbt z&Z12qENEdwy&92yh%g{lxKyKHBbm>G_O>-P{J@pu2?YP}OHwdPi053L>(+vP7$%$J zyqY*n>|vnpq*ROis$9wFW7Gc~a9{cGU8yEik>r(YB!aQ+y`k}%P#@ILqBQ~4@Q*h4 zF@`?e8wyf?Yx+F2sn5HD_U;C@`dbK4_~BhaaCd_gFC&#=emLvdMhRC<=orbXSwK#D zcY~HTWAQ;oeQl^B*{j)BZ{~=PHgp(6T@NyntqBT6U)O~o7`xsZ5>$VSjT(vv=2zu; zXw3XZ)eRkE=;A>}zZDE~p+uVz3IBHd0|63XoBvPSD%Ib%!oR)g_}ND{Uoc{^qhRCfxQ?%PNV|-Bgg$- z($E|MQwh}B6?N96M)~y1QOIB=;f#j-j1s4=!VVK#4~F(V*Fz0uaA;1+JoA~4EL%?z z2+f0PdAKdC(oM@J`NCrBruPx_XY@|?G(9hXE9Q8)))$ph#}}_3L1FWzVyp|NcalE7 z2yy`&%6&UZYwleQWP!?jgof}qNIJ+Ta0g=5omMhWf94}|`(ZIlc(;38D}N%<)|#Ok`w- zgKYI3)Wz^o<1Gc=Ql9xRobTBY6kQ*^u!!mcHrz0)eRhTYpCaSu@JgE1DPaxYA_lc(i=?!~kyC(zwaL!jy=y;oJC41?s zAPy9;Z#B!@cAcE;l<^gF# zNt&1powS&6m-hF3_HX>fLOWdA`E)w-@2e?E!Q+B&xJU|L40DTFwRIQ^S}qUTZ@r$| zuqxP4&Xrti7IsQrfTO`e;^}L0o=cZ1-=p5GrK)0>rFUz_1P`|eZDK!b2w-Kxo| zzn=B&P6rb=s@*vLcz>k;cKJ{qnBreaMQZ1N0S#3~BdE?$YRDFdq^EOuK zc$2$f^0BE34X|maZj^bAX(G=jhtsz%`W{z&Cc}1tw$S_k4UYXdnc#`FImwH5h)H|2 zthFOR3`vuJOPOQ=hYLt!R!$0VwZS;WC{Z!S8|B7aP*ic&T~^!zd2K^om0)GFCl%b% z(Xvo4FR>faa4;~hVsKa11ADP$_W{47geF{)g!WuK(pN-^^Zj0FEssPd9o8>`cQ7?VE2$G@!5a=*VLYs z&B$u!N5Ge~c=)A!hnkw^Zl5`Yw~sfdvgcUU6BD52sQE5{#ogV))xK<@f(H`^ODCBWml4C zdzdZO))p#*r`{^9x8lR!PNr3l{_Yak>hvwgI}s$zbc6J-g$=Itw_lS{EMa&D?Q4Iq zlBC;mj38koo7XF@RKn1&yXeE9FC4azw2zTsj@us#`l9DPe9v2XeIel-<2BmMLEMX+ zi|N=4J~qOap6j|iRO*Ij`41+?;(iQTa$+NgciS_&4tBR&J{PPYV_Q$kPQL!yY9cRo zR+n^s>ax;b?t`|1!`zxp^-qlYB8 zVFkD+Q=?zw9$QB9Sr09D?|6CcwKlPnq;|JRepfv-W&-wmR&7}t`!(`b39-LyZ#Vz4 zqm+c_b}4|jPiH%hW~l9JOh3g(z2;kwnUKA65cO~%V^z<{GW&plkh1B!vboJWS$bHS zC?amxC$dR5IkDfMRq-j3Fq1ltfDTzw7&)Yfa7vkE3q$&R3~DG)nGC(z`>o9`r9={} zb|`5iUzTu2L&=uNSax5Q{`z0T18W89iG+w4j6J$j80m!OfI4S!?35Pg!EIV=D1|Yo zVT^4A$$PHObxg4lA%oH9d|61V$*nr#j#y%`BslnL=%>`@9uX*_?=Jn&Ds?DsH;OW9mT@JC zH_pMl$EtqjSVJ=jaTeYkWx_=6> zE9YGUA?D8Ag=wt#73vVtQG+ciMvc{lMUZ^{%DAuSH_pj;k5vQ11Ez3Fy<|%aW_}qp zUKgfD@|ml11CDWJD$9||T6l82K3)W&J^Dur-;t`nE@Ed&9X7>#4j6M#DvwwJ2|F@O zmN{BjBtiBB(MuB3C{;&>i&4XY$`O`^XJGbq2YFU}a0ieQR=j|lIRr3L2xs+f|KmgD z+|$%xXbHCw!H_;%j2b*5skzRDNxF zj*fe;5J>`1htCPiD=@@BB4R$qwVM@`zz~CX9dILLuG=p8c(Nxlj>A_qxjfFo9XRiN zSPjni-E}DG1%zR^Qnw7_|Wt6Gec z9<`a?aM<2bTJIuZ#5_L5pJkh(_MEoR;b3uHVCM09H=mK-z|-lg5}b!$z^$vU>Aw8p zr6zaPE2`DO(cIgYKCcPx%oujW>;1TuZKELfJtsIiZA}o-R*)*1Z4(z9UArbYmv94p zTkvd_<=@7)F1f~nX8MVT`+K#U53B#FR3APk_X=aBNTSd1m=R5Wr_pfvQ}BKfq#zg~ zVbgU+{xLh_srps<)AAI~=@90F{ylyT={~ZMqG^b9FYd5VPeE+XnMI5}#veWY%gQR( zVx3QTA$v>TkT<7?=uUGx5W@J3UqN-pA(|__+EHQrGh7j%WhYJ8;j68|c3yW%Kex zxE-1U=U}4Mp1sfQjM4?W7 zgWxb?sN@pkz$?$Y4a?`VcVCU|yD!bG8noesRpmXVowE<|C4T!;6WoXQRaWjpPj`IN z7%IAD{7t_2Qe!B-04XLlVW6XN1p^+VPCReD&R2g?Q+rUJxs&cve*6~!(J)#$r!#!N zHHnW)-PuHq+qAF0LD%w0I5|-V_)ss>nPrb0Q?fhr>Di5RTBU$}{?Pc;#yrEvC;R7% zno}vwzAAS^+WhJdI`3rdQ>v7mW(co;k+0-hp|O`&1gF=!>i-C=M+i?`fcu}*4Y)5p zGRz#FsqPwFKvxcnyhi-MM={YP=O(&1cSgn~CO|*f8G7?4Q}^r=0z4M_-x-S+WzFtN zc1aX%z&w-wyr`X6KIq$kE}vffU>tC0oQ-R7C}R7c6YS>Gyf8!#(dgw#T2| zt_1tT?Jg&!cgglWX$jfdg$S!voqzPcQmuMl6Sbiy_N%hf`AbvPyE|5jr*w65FKNfH z-)^z#OwUjgM6;E}F<@moxgg&IsjZ>8&Y47Q4HlV%@kl~UOOdXZx@I}~nIbf!9B1y{ zlmqKDQ7@6S>O;LrRfbp_6ieE4UwI{?@5Kp$BU*FdtUft2x3wyoBCSnzL8p{_3FHcN z$~w^Vyg{b~wsCJkr=-LJ1XRs@$$*&!of7!AR6PUJW9I)ZbApakmea^rpdSrUW5C#o zJ7@yPM0$4Lm81`G00IyzGt&|nRjjah@@4U*c0t2T!vBOAspGRF~LrWDK!2L9}%`4N9yqZBzWy;09 z3tzT7p;a*+uZS0M6dtM6!uO>ru8SbH{jGzWQ( z<2F@uP%R9Yd7wGKv87?u-~wHeT?tdzoLPr%wNtWP8c!Gk-in|OqnH|T?<@K(!9q#u zCB~6qaEU=(sBVuXg6JLN@MP;F!yFXchFVNrG_x0j&%SJ^MtLV-O$31*@zE-8id6I# znPXpYO0o$!-iXVT=+_4nNVafLijF99L6NBx!0e0gWKJ>12q@Y3h@zpTLIPb(og1_F z8uWfNfy#$s&``c%qJ0Dd#$HiCUN4sqRFm&)QprzqZ>m^<>j+t@SQQh=S0t7=g)A(q z#)0z;!Z{0XP#>LU;5x*n&p0XGHV0G>)T*1|)ljokrh$k!wUA(Rft-0}kG_;43+|)( z{<|XeO?A_jiV1WKn8_Gae;_X%$+Jqvx#ajoXs`#=Y5*z>A>K$$erI@S-xqyycjmJ1 zoZCGge~G~hJ#T~GIE6+4w?L15oL-E zZ&Hh#KV|LbOp7yRkFq&fUAc8$M1Exyd;QEr$ewcZe|rA*!cwWRn($9ERZ-+GfBFa7 zu<@E?gTIoa@6dQ_`MivKp%(TFg<4s){WoB9Ro$mTX9Eyl8EK&H+f(UJL)K3o`u{O} zlX1^--C}U*P>|1-!#g|yh2^8@Ym%{v#jjPW$~5O{RFR(+TYh)#zHHc(59oIo_HEY2 zC1(m0j(d=HC0;T?AE& zQ57utle2VUY^|G%-0z-UH+YEacG`;TTUN=piK?G{evv{BCbLC9Hi-7@wX(UhzFOt^ zop;Y6*uO2M1MBa4izdU-o9Q1Fi&w`4hRtZDe^g!n5+xPe>2~kq@=Kl4C3yYgb&*{2 z?5XC~tRS(?=E-p#riy?!jTrY7q5MW^MC6o>dH(4s8xC<5&vFL#cQt0s^!g8lMkX0v zJlsE9OLxhOQD_G9As&fV!VR6Rox3rE!Nw!GqbTwGDOde{Yi4e7+T#t@MoH+&n!FqR z#C12A&u)x%0}I@NR5ToC8yHdh{qILFi{vsF{cjbq)-p^u8YzqN?ImJ*^V6-*ne%cz wzUY7ld@cEGH9STrJ1kRbfBn^#A|> literal 0 HcmV?d00001 diff --git a/core/src/main/resources/bedrock/creative_items.1_19_70.json b/core/src/main/resources/bedrock/creative_items.1_19_70.json new file mode 100644 index 000000000..e47467f23 --- /dev/null +++ b/core/src/main/resources/bedrock/creative_items.1_19_70.json @@ -0,0 +1,5452 @@ +{ + "items" : [ + { + "id" : "minecraft:planks", + "blockRuntimeId" : 9885 + }, + { + "id" : "minecraft:planks", + "blockRuntimeId" : 9886 + }, + { + "id" : "minecraft:planks", + "blockRuntimeId" : 9887 + }, + { + "id" : "minecraft:planks", + "blockRuntimeId" : 9888 + }, + { + "id" : "minecraft:planks", + "blockRuntimeId" : 9889 + }, + { + "id" : "minecraft:planks", + "blockRuntimeId" : 9890 + }, + { + "id" : "minecraft:mangrove_planks", + "blockRuntimeId" : 1639 + }, + { + "id" : "minecraft:crimson_planks", + "blockRuntimeId" : 7466 + }, + { + "id" : "minecraft:warped_planks", + "blockRuntimeId" : 1612 + }, + { + "id" : "minecraft:cobblestone_wall", + "blockRuntimeId" : 1883 + }, + { + "id" : "minecraft:cobblestone_wall", + "blockRuntimeId" : 1884 + }, + { + "id" : "minecraft:cobblestone_wall", + "blockRuntimeId" : 1885 + }, + { + "id" : "minecraft:cobblestone_wall", + "blockRuntimeId" : 1886 + }, + { + "id" : "minecraft:cobblestone_wall", + "blockRuntimeId" : 1887 + }, + { + "id" : "minecraft:cobblestone_wall", + "blockRuntimeId" : 1888 + }, + { + "id" : "minecraft:cobblestone_wall", + "blockRuntimeId" : 1895 + }, + { + "id" : "minecraft:cobblestone_wall", + "blockRuntimeId" : 1890 + }, + { + "id" : "minecraft:cobblestone_wall", + "blockRuntimeId" : 1891 + }, + { + "id" : "minecraft:cobblestone_wall", + "blockRuntimeId" : 1889 + }, + { + "id" : "minecraft:cobblestone_wall", + "blockRuntimeId" : 1892 + }, + { + "id" : "minecraft:cobblestone_wall", + "blockRuntimeId" : 1896 + }, + { + "id" : "minecraft:cobblestone_wall", + "blockRuntimeId" : 1893 + }, + { + "id" : "minecraft:cobblestone_wall", + "blockRuntimeId" : 1894 + }, + { + "id" : "minecraft:blackstone_wall", + "blockRuntimeId" : 5774 + }, + { + "id" : "minecraft:polished_blackstone_wall", + "blockRuntimeId" : 10580 + }, + { + "id" : "minecraft:polished_blackstone_brick_wall", + "blockRuntimeId" : 1664 + }, + { + "id" : "minecraft:cobbled_deepslate_wall", + "blockRuntimeId" : 12346 + }, + { + "id" : "minecraft:deepslate_tile_wall", + "blockRuntimeId" : 7703 + }, + { + "id" : "minecraft:polished_deepslate_wall", + "blockRuntimeId" : 12080 + }, + { + "id" : "minecraft:deepslate_brick_wall", + "blockRuntimeId" : 727 + }, + { + "id" : "minecraft:mud_brick_wall", + "blockRuntimeId" : 1422 + }, + { + "id" : "minecraft:fence", + "blockRuntimeId" : 11627 + }, + { + "id" : "minecraft:fence", + "blockRuntimeId" : 11628 + }, + { + "id" : "minecraft:fence", + "blockRuntimeId" : 11629 + }, + { + "id" : "minecraft:fence", + "blockRuntimeId" : 11630 + }, + { + "id" : "minecraft:fence", + "blockRuntimeId" : 11631 + }, + { + "id" : "minecraft:fence", + "blockRuntimeId" : 11632 + }, + { + "id" : "minecraft:mangrove_fence", + "blockRuntimeId" : 10485 + }, + { + "id" : "minecraft:nether_brick_fence", + "blockRuntimeId" : 6138 + }, + { + "id" : "minecraft:crimson_fence", + "blockRuntimeId" : 12259 + }, + { + "id" : "minecraft:warped_fence", + "blockRuntimeId" : 8889 + }, + { + "id" : "minecraft:fence_gate", + "blockRuntimeId" : 79 + }, + { + "id" : "minecraft:spruce_fence_gate", + "blockRuntimeId" : 10436 + }, + { + "id" : "minecraft:birch_fence_gate", + "blockRuntimeId" : 5237 + }, + { + "id" : "minecraft:jungle_fence_gate", + "blockRuntimeId" : 8014 + }, + { + "id" : "minecraft:acacia_fence_gate", + "blockRuntimeId" : 11849 + }, + { + "id" : "minecraft:dark_oak_fence_gate", + "blockRuntimeId" : 6017 + }, + { + "id" : "minecraft:mangrove_fence_gate", + "blockRuntimeId" : 6473 + }, + { + "id" : "minecraft:crimson_fence_gate", + "blockRuntimeId" : 6893 + }, + { + "id" : "minecraft:warped_fence_gate", + "blockRuntimeId" : 8049 + }, + { + "id" : "minecraft:normal_stone_stairs", + "blockRuntimeId" : 932 + }, + { + "id" : "minecraft:stone_stairs", + "blockRuntimeId" : 5166 + }, + { + "id" : "minecraft:mossy_cobblestone_stairs", + "blockRuntimeId" : 5936 + }, + { + "id" : "minecraft:oak_stairs", + "blockRuntimeId" : 280 + }, + { + "id" : "minecraft:spruce_stairs", + "blockRuntimeId" : 132 + }, + { + "id" : "minecraft:birch_stairs", + "blockRuntimeId" : 10865 + }, + { + "id" : "minecraft:jungle_stairs", + "blockRuntimeId" : 10829 + }, + { + "id" : "minecraft:acacia_stairs", + "blockRuntimeId" : 10030 + }, + { + "id" : "minecraft:dark_oak_stairs", + "blockRuntimeId" : 7695 + }, + { + "id" : "minecraft:mangrove_stairs", + "blockRuntimeId" : 6443 + }, + { + "id" : "minecraft:stone_brick_stairs", + "blockRuntimeId" : 1623 + }, + { + "id" : "minecraft:mossy_stone_brick_stairs", + "blockRuntimeId" : 9303 + }, + { + "id" : "minecraft:sandstone_stairs", + "blockRuntimeId" : 5042 + }, + { + "id" : "minecraft:smooth_sandstone_stairs", + "blockRuntimeId" : 5085 + }, + { + "id" : "minecraft:red_sandstone_stairs", + "blockRuntimeId" : 7999 + }, + { + "id" : "minecraft:smooth_red_sandstone_stairs", + "blockRuntimeId" : 8197 + }, + { + "id" : "minecraft:granite_stairs", + "blockRuntimeId" : 4608 + }, + { + "id" : "minecraft:polished_granite_stairs", + "blockRuntimeId" : 5994 + }, + { + "id" : "minecraft:diorite_stairs", + "blockRuntimeId" : 6239 + }, + { + "id" : "minecraft:polished_diorite_stairs", + "blockRuntimeId" : 10566 + }, + { + "id" : "minecraft:andesite_stairs", + "blockRuntimeId" : 7956 + }, + { + "id" : "minecraft:polished_andesite_stairs", + "blockRuntimeId" : 10890 + }, + { + "id" : "minecraft:brick_stairs", + "blockRuntimeId" : 10382 + }, + { + "id" : "minecraft:nether_brick_stairs", + "blockRuntimeId" : 109 + }, + { + "id" : "minecraft:red_nether_brick_stairs", + "blockRuntimeId" : 10454 + }, + { + "id" : "minecraft:end_brick_stairs", + "blockRuntimeId" : 10220 + }, + { + "id" : "minecraft:quartz_stairs", + "blockRuntimeId" : 6999 + }, + { + "id" : "minecraft:smooth_quartz_stairs", + "blockRuntimeId" : 11963 + }, + { + "id" : "minecraft:purpur_stairs", + "blockRuntimeId" : 12018 + }, + { + "id" : "minecraft:prismarine_stairs", + "blockRuntimeId" : 11526 + }, + { + "id" : "minecraft:dark_prismarine_stairs", + "blockRuntimeId" : 11693 + }, + { + "id" : "minecraft:prismarine_bricks_stairs", + "blockRuntimeId" : 211 + }, + { + "id" : "minecraft:crimson_stairs", + "blockRuntimeId" : 10118 + }, + { + "id" : "minecraft:warped_stairs", + "blockRuntimeId" : 5176 + }, + { + "id" : "minecraft:blackstone_stairs", + "blockRuntimeId" : 10881 + }, + { + "id" : "minecraft:polished_blackstone_stairs", + "blockRuntimeId" : 6145 + }, + { + "id" : "minecraft:polished_blackstone_brick_stairs", + "blockRuntimeId" : 6325 + }, + { + "id" : "minecraft:cut_copper_stairs", + "blockRuntimeId" : 6452 + }, + { + "id" : "minecraft:exposed_cut_copper_stairs", + "blockRuntimeId" : 6435 + }, + { + "id" : "minecraft:weathered_cut_copper_stairs", + "blockRuntimeId" : 6153 + }, + { + "id" : "minecraft:oxidized_cut_copper_stairs", + "blockRuntimeId" : 648 + }, + { + "id" : "minecraft:waxed_cut_copper_stairs", + "blockRuntimeId" : 691 + }, + { + "id" : "minecraft:waxed_exposed_cut_copper_stairs", + "blockRuntimeId" : 5746 + }, + { + "id" : "minecraft:waxed_weathered_cut_copper_stairs", + "blockRuntimeId" : 9997 + }, + { + "id" : "minecraft:waxed_oxidized_cut_copper_stairs", + "blockRuntimeId" : 8876 + }, + { + "id" : "minecraft:cobbled_deepslate_stairs", + "blockRuntimeId" : 152 + }, + { + "id" : "minecraft:deepslate_tile_stairs", + "blockRuntimeId" : 6885 + }, + { + "id" : "minecraft:polished_deepslate_stairs", + "blockRuntimeId" : 589 + }, + { + "id" : "minecraft:deepslate_brick_stairs", + "blockRuntimeId" : 11685 + }, + { + "id" : "minecraft:mud_brick_stairs", + "blockRuntimeId" : 8173 + }, + { + "id" : "minecraft:wooden_door" + }, + { + "id" : "minecraft:spruce_door" + }, + { + "id" : "minecraft:birch_door" + }, + { + "id" : "minecraft:jungle_door" + }, + { + "id" : "minecraft:acacia_door" + }, + { + "id" : "minecraft:dark_oak_door" + }, + { + "id" : "minecraft:mangrove_door" + }, + { + "id" : "minecraft:iron_door" + }, + { + "id" : "minecraft:crimson_door" + }, + { + "id" : "minecraft:warped_door" + }, + { + "id" : "minecraft:trapdoor", + "blockRuntimeId" : 235 + }, + { + "id" : "minecraft:spruce_trapdoor", + "blockRuntimeId" : 10404 + }, + { + "id" : "minecraft:birch_trapdoor", + "blockRuntimeId" : 10502 + }, + { + "id" : "minecraft:jungle_trapdoor", + "blockRuntimeId" : 8030 + }, + { + "id" : "minecraft:acacia_trapdoor", + "blockRuntimeId" : 8240 + }, + { + "id" : "minecraft:dark_oak_trapdoor", + "blockRuntimeId" : 11765 + }, + { + "id" : "minecraft:mangrove_trapdoor", + "blockRuntimeId" : 6333 + }, + { + "id" : "minecraft:iron_trapdoor", + "blockRuntimeId" : 616 + }, + { + "id" : "minecraft:crimson_trapdoor", + "blockRuntimeId" : 6181 + }, + { + "id" : "minecraft:warped_trapdoor", + "blockRuntimeId" : 6965 + }, + { + "id" : "minecraft:iron_bars", + "blockRuntimeId" : 7033 + }, + { + "id" : "minecraft:glass", + "blockRuntimeId" : 9994 + }, + { + "id" : "minecraft:stained_glass", + "blockRuntimeId" : 1826 + }, + { + "id" : "minecraft:stained_glass", + "blockRuntimeId" : 1834 + }, + { + "id" : "minecraft:stained_glass", + "blockRuntimeId" : 1833 + }, + { + "id" : "minecraft:stained_glass", + "blockRuntimeId" : 1841 + }, + { + "id" : "minecraft:stained_glass", + "blockRuntimeId" : 1838 + }, + { + "id" : "minecraft:stained_glass", + "blockRuntimeId" : 1840 + }, + { + "id" : "minecraft:stained_glass", + "blockRuntimeId" : 1827 + }, + { + "id" : "minecraft:stained_glass", + "blockRuntimeId" : 1830 + }, + { + "id" : "minecraft:stained_glass", + "blockRuntimeId" : 1831 + }, + { + "id" : "minecraft:stained_glass", + "blockRuntimeId" : 1839 + }, + { + "id" : "minecraft:stained_glass", + "blockRuntimeId" : 1835 + }, + { + "id" : "minecraft:stained_glass", + "blockRuntimeId" : 1829 + }, + { + "id" : "minecraft:stained_glass", + "blockRuntimeId" : 1837 + }, + { + "id" : "minecraft:stained_glass", + "blockRuntimeId" : 1836 + }, + { + "id" : "minecraft:stained_glass", + "blockRuntimeId" : 1828 + }, + { + "id" : "minecraft:stained_glass", + "blockRuntimeId" : 1832 + }, + { + "id" : "minecraft:tinted_glass", + "blockRuntimeId" : 9395 + }, + { + "id" : "minecraft:glass_pane", + "blockRuntimeId" : 7865 + }, + { + "id" : "minecraft:stained_glass_pane", + "blockRuntimeId" : 7468 + }, + { + "id" : "minecraft:stained_glass_pane", + "blockRuntimeId" : 7476 + }, + { + "id" : "minecraft:stained_glass_pane", + "blockRuntimeId" : 7475 + }, + { + "id" : "minecraft:stained_glass_pane", + "blockRuntimeId" : 7483 + }, + { + "id" : "minecraft:stained_glass_pane", + "blockRuntimeId" : 7480 + }, + { + "id" : "minecraft:stained_glass_pane", + "blockRuntimeId" : 7482 + }, + { + "id" : "minecraft:stained_glass_pane", + "blockRuntimeId" : 7469 + }, + { + "id" : "minecraft:stained_glass_pane", + "blockRuntimeId" : 7472 + }, + { + "id" : "minecraft:stained_glass_pane", + "blockRuntimeId" : 7473 + }, + { + "id" : "minecraft:stained_glass_pane", + "blockRuntimeId" : 7481 + }, + { + "id" : "minecraft:stained_glass_pane", + "blockRuntimeId" : 7477 + }, + { + "id" : "minecraft:stained_glass_pane", + "blockRuntimeId" : 7471 + }, + { + "id" : "minecraft:stained_glass_pane", + "blockRuntimeId" : 7479 + }, + { + "id" : "minecraft:stained_glass_pane", + "blockRuntimeId" : 7478 + }, + { + "id" : "minecraft:stained_glass_pane", + "blockRuntimeId" : 7470 + }, + { + "id" : "minecraft:stained_glass_pane", + "blockRuntimeId" : 7474 + }, + { + "id" : "minecraft:ladder", + "blockRuntimeId" : 12528 + }, + { + "id" : "minecraft:scaffolding", + "blockRuntimeId" : 5026 + }, + { + "id" : "minecraft:stone_block_slab", + "blockRuntimeId" : 6116 + }, + { + "id" : "minecraft:stone_block_slab4", + "blockRuntimeId" : 8474 + }, + { + "id" : "minecraft:stone_block_slab", + "blockRuntimeId" : 6119 + }, + { + "id" : "minecraft:stone_block_slab2", + "blockRuntimeId" : 8445 + }, + { + "id" : "minecraft:wooden_slab", + "blockRuntimeId" : 7918 + }, + { + "id" : "minecraft:wooden_slab", + "blockRuntimeId" : 7919 + }, + { + "id" : "minecraft:wooden_slab", + "blockRuntimeId" : 7920 + }, + { + "id" : "minecraft:wooden_slab", + "blockRuntimeId" : 7921 + }, + { + "id" : "minecraft:wooden_slab", + "blockRuntimeId" : 7922 + }, + { + "id" : "minecraft:wooden_slab", + "blockRuntimeId" : 7923 + }, + { + "id" : "minecraft:mangrove_slab", + "blockRuntimeId" : 1842 + }, + { + "id" : "minecraft:stone_block_slab", + "blockRuntimeId" : 6121 + }, + { + "id" : "minecraft:stone_block_slab4", + "blockRuntimeId" : 8472 + }, + { + "id" : "minecraft:stone_block_slab", + "blockRuntimeId" : 6117 + }, + { + "id" : "minecraft:stone_block_slab4", + "blockRuntimeId" : 8475 + }, + { + "id" : "minecraft:stone_block_slab2", + "blockRuntimeId" : 8446 + }, + { + "id" : "minecraft:stone_block_slab2", + "blockRuntimeId" : 8440 + }, + { + "id" : "minecraft:stone_block_slab4", + "blockRuntimeId" : 8476 + }, + { + "id" : "minecraft:stone_block_slab3", + "blockRuntimeId" : 8457 + }, + { + "id" : "minecraft:stone_block_slab3", + "blockRuntimeId" : 8462 + }, + { + "id" : "minecraft:stone_block_slab3", + "blockRuntimeId" : 8463 + }, + { + "id" : "minecraft:stone_block_slab3", + "blockRuntimeId" : 8460 + }, + { + "id" : "minecraft:stone_block_slab3", + "blockRuntimeId" : 8461 + }, + { + "id" : "minecraft:stone_block_slab3", + "blockRuntimeId" : 8459 + }, + { + "id" : "minecraft:stone_block_slab3", + "blockRuntimeId" : 8458 + }, + { + "id" : "minecraft:stone_block_slab", + "blockRuntimeId" : 6120 + }, + { + "id" : "minecraft:stone_block_slab", + "blockRuntimeId" : 6123 + }, + { + "id" : "minecraft:stone_block_slab2", + "blockRuntimeId" : 8447 + }, + { + "id" : "minecraft:stone_block_slab3", + "blockRuntimeId" : 8456 + }, + { + "id" : "minecraft:stone_block_slab", + "blockRuntimeId" : 6122 + }, + { + "id" : "minecraft:stone_block_slab4", + "blockRuntimeId" : 8473 + }, + { + "id" : "minecraft:stone_block_slab2", + "blockRuntimeId" : 8441 + }, + { + "id" : "minecraft:stone_block_slab2", + "blockRuntimeId" : 8442 + }, + { + "id" : "minecraft:stone_block_slab2", + "blockRuntimeId" : 8443 + }, + { + "id" : "minecraft:stone_block_slab2", + "blockRuntimeId" : 8444 + }, + { + "id" : "minecraft:crimson_slab", + "blockRuntimeId" : 9320 + }, + { + "id" : "minecraft:warped_slab", + "blockRuntimeId" : 10334 + }, + { + "id" : "minecraft:blackstone_slab", + "blockRuntimeId" : 1602 + }, + { + "id" : "minecraft:polished_blackstone_slab", + "blockRuntimeId" : 9823 + }, + { + "id" : "minecraft:polished_blackstone_brick_slab", + "blockRuntimeId" : 6038 + }, + { + "id" : "minecraft:cut_copper_slab", + "blockRuntimeId" : 7867 + }, + { + "id" : "minecraft:exposed_cut_copper_slab", + "blockRuntimeId" : 10452 + }, + { + "id" : "minecraft:weathered_cut_copper_slab", + "blockRuntimeId" : 9866 + }, + { + "id" : "minecraft:oxidized_cut_copper_slab", + "blockRuntimeId" : 7930 + }, + { + "id" : "minecraft:waxed_cut_copper_slab", + "blockRuntimeId" : 12078 + }, + { + "id" : "minecraft:waxed_exposed_cut_copper_slab", + "blockRuntimeId" : 255 + }, + { + "id" : "minecraft:waxed_weathered_cut_copper_slab", + "blockRuntimeId" : 10397 + }, + { + "id" : "minecraft:waxed_oxidized_cut_copper_slab", + "blockRuntimeId" : 1392 + }, + { + "id" : "minecraft:cobbled_deepslate_slab", + "blockRuntimeId" : 11573 + }, + { + "id" : "minecraft:polished_deepslate_slab", + "blockRuntimeId" : 295 + }, + { + "id" : "minecraft:deepslate_tile_slab", + "blockRuntimeId" : 6139 + }, + { + "id" : "minecraft:deepslate_brick_slab", + "blockRuntimeId" : 5174 + }, + { + "id" : "minecraft:mud_brick_slab", + "blockRuntimeId" : 5754 + }, + { + "id" : "minecraft:brick_block", + "blockRuntimeId" : 6997 + }, + { + "id" : "minecraft:chiseled_nether_bricks", + "blockRuntimeId" : 11512 + }, + { + "id" : "minecraft:cracked_nether_bricks", + "blockRuntimeId" : 6400 + }, + { + "id" : "minecraft:quartz_bricks", + "blockRuntimeId" : 10189 + }, + { + "id" : "minecraft:stonebrick", + "blockRuntimeId" : 10399 + }, + { + "id" : "minecraft:stonebrick", + "blockRuntimeId" : 10400 + }, + { + "id" : "minecraft:stonebrick", + "blockRuntimeId" : 10401 + }, + { + "id" : "minecraft:stonebrick", + "blockRuntimeId" : 10402 + }, + { + "id" : "minecraft:end_bricks", + "blockRuntimeId" : 288 + }, + { + "id" : "minecraft:prismarine", + "blockRuntimeId" : 9917 + }, + { + "id" : "minecraft:polished_blackstone_bricks", + "blockRuntimeId" : 6912 + }, + { + "id" : "minecraft:cracked_polished_blackstone_bricks", + "blockRuntimeId" : 11461 + }, + { + "id" : "minecraft:gilded_blackstone", + "blockRuntimeId" : 6434 + }, + { + "id" : "minecraft:chiseled_polished_blackstone", + "blockRuntimeId" : 7694 + }, + { + "id" : "minecraft:deepslate_tiles", + "blockRuntimeId" : 6429 + }, + { + "id" : "minecraft:cracked_deepslate_tiles", + "blockRuntimeId" : 6004 + }, + { + "id" : "minecraft:deepslate_bricks", + "blockRuntimeId" : 8114 + }, + { + "id" : "minecraft:cracked_deepslate_bricks", + "blockRuntimeId" : 8013 + }, + { + "id" : "minecraft:chiseled_deepslate", + "blockRuntimeId" : 7866 + }, + { + "id" : "minecraft:cobblestone", + "blockRuntimeId" : 5073 + }, + { + "id" : "minecraft:mossy_cobblestone", + "blockRuntimeId" : 258 + }, + { + "id" : "minecraft:cobbled_deepslate", + "blockRuntimeId" : 10522 + }, + { + "id" : "minecraft:smooth_stone", + "blockRuntimeId" : 6430 + }, + { + "id" : "minecraft:sandstone", + "blockRuntimeId" : 5111 + }, + { + "id" : "minecraft:sandstone", + "blockRuntimeId" : 5112 + }, + { + "id" : "minecraft:sandstone", + "blockRuntimeId" : 5113 + }, + { + "id" : "minecraft:sandstone", + "blockRuntimeId" : 5114 + }, + { + "id" : "minecraft:red_sandstone", + "blockRuntimeId" : 10432 + }, + { + "id" : "minecraft:red_sandstone", + "blockRuntimeId" : 10433 + }, + { + "id" : "minecraft:red_sandstone", + "blockRuntimeId" : 10434 + }, + { + "id" : "minecraft:red_sandstone", + "blockRuntimeId" : 10435 + }, + { + "id" : "minecraft:coal_block", + "blockRuntimeId" : 8047 + }, + { + "id" : "minecraft:dried_kelp_block", + "blockRuntimeId" : 12242 + }, + { + "id" : "minecraft:gold_block", + "blockRuntimeId" : 330 + }, + { + "id" : "minecraft:iron_block", + "blockRuntimeId" : 12527 + }, + { + "id" : "minecraft:copper_block", + "blockRuntimeId" : 6883 + }, + { + "id" : "minecraft:exposed_copper", + "blockRuntimeId" : 891 + }, + { + "id" : "minecraft:weathered_copper", + "blockRuntimeId" : 12510 + }, + { + "id" : "minecraft:oxidized_copper", + "blockRuntimeId" : 5008 + }, + { + "id" : "minecraft:waxed_copper", + "blockRuntimeId" : 11997 + }, + { + "id" : "minecraft:waxed_exposed_copper", + "blockRuntimeId" : 1378 + }, + { + "id" : "minecraft:waxed_weathered_copper", + "blockRuntimeId" : 1391 + }, + { + "id" : "minecraft:waxed_oxidized_copper", + "blockRuntimeId" : 11805 + }, + { + "id" : "minecraft:cut_copper", + "blockRuntimeId" : 6921 + }, + { + "id" : "minecraft:exposed_cut_copper", + "blockRuntimeId" : 9996 + }, + { + "id" : "minecraft:weathered_cut_copper", + "blockRuntimeId" : 11444 + }, + { + "id" : "minecraft:oxidized_cut_copper", + "blockRuntimeId" : 8128 + }, + { + "id" : "minecraft:waxed_cut_copper", + "blockRuntimeId" : 11556 + }, + { + "id" : "minecraft:waxed_exposed_cut_copper", + "blockRuntimeId" : 5269 + }, + { + "id" : "minecraft:waxed_weathered_cut_copper", + "blockRuntimeId" : 7467 + }, + { + "id" : "minecraft:waxed_oxidized_cut_copper", + "blockRuntimeId" : 219 + }, + { + "id" : "minecraft:emerald_block", + "blockRuntimeId" : 1852 + }, + { + "id" : "minecraft:diamond_block", + "blockRuntimeId" : 279 + }, + { + "id" : "minecraft:lapis_block", + "blockRuntimeId" : 6132 + }, + { + "id" : "minecraft:raw_iron_block", + "blockRuntimeId" : 12525 + }, + { + "id" : "minecraft:raw_copper_block", + "blockRuntimeId" : 7917 + }, + { + "id" : "minecraft:raw_gold_block", + "blockRuntimeId" : 658 + }, + { + "id" : "minecraft:quartz_block", + "blockRuntimeId" : 5154 + }, + { + "id" : "minecraft:quartz_block", + "blockRuntimeId" : 5156 + }, + { + "id" : "minecraft:quartz_block", + "blockRuntimeId" : 5155 + }, + { + "id" : "minecraft:quartz_block", + "blockRuntimeId" : 5157 + }, + { + "id" : "minecraft:prismarine", + "blockRuntimeId" : 9915 + }, + { + "id" : "minecraft:prismarine", + "blockRuntimeId" : 9916 + }, + { + "id" : "minecraft:slime", + "blockRuntimeId" : 6079 + }, + { + "id" : "minecraft:honey_block", + "blockRuntimeId" : 1584 + }, + { + "id" : "minecraft:honeycomb_block", + "blockRuntimeId" : 6324 + }, + { + "id" : "minecraft:hay_block", + "blockRuntimeId" : 1379 + }, + { + "id" : "minecraft:bone_block", + "blockRuntimeId" : 6080 + }, + { + "id" : "minecraft:nether_brick", + "blockRuntimeId" : 11535 + }, + { + "id" : "minecraft:red_nether_brick", + "blockRuntimeId" : 151 + }, + { + "id" : "minecraft:netherite_block", + "blockRuntimeId" : 5234 + }, + { + "id" : "minecraft:lodestone", + "blockRuntimeId" : 12523 + }, + { + "id" : "minecraft:white_wool", + "blockRuntimeId" : 8048 + }, + { + "id" : "minecraft:light_gray_wool", + "blockRuntimeId" : 12318 + }, + { + "id" : "minecraft:gray_wool", + "blockRuntimeId" : 228 + }, + { + "id" : "minecraft:black_wool", + "blockRuntimeId" : 659 + }, + { + "id" : "minecraft:brown_wool", + "blockRuntimeId" : 262 + }, + { + "id" : "minecraft:red_wool", + "blockRuntimeId" : 119 + }, + { + "id" : "minecraft:orange_wool", + "blockRuntimeId" : 1360 + }, + { + "id" : "minecraft:yellow_wool", + "blockRuntimeId" : 142 + }, + { + "id" : "minecraft:lime_wool", + "blockRuntimeId" : 9812 + }, + { + "id" : "minecraft:green_wool", + "blockRuntimeId" : 5185 + }, + { + "id" : "minecraft:cyan_wool", + "blockRuntimeId" : 7992 + }, + { + "id" : "minecraft:light_blue_wool", + "blockRuntimeId" : 10944 + }, + { + "id" : "minecraft:blue_wool", + "blockRuntimeId" : 8129 + }, + { + "id" : "minecraft:purple_wool", + "blockRuntimeId" : 12526 + }, + { + "id" : "minecraft:magenta_wool", + "blockRuntimeId" : 1657 + }, + { + "id" : "minecraft:pink_wool", + "blockRuntimeId" : 5235 + }, + { + "id" : "minecraft:carpet", + "blockRuntimeId" : 1641 + }, + { + "id" : "minecraft:carpet", + "blockRuntimeId" : 1649 + }, + { + "id" : "minecraft:carpet", + "blockRuntimeId" : 1648 + }, + { + "id" : "minecraft:carpet", + "blockRuntimeId" : 1656 + }, + { + "id" : "minecraft:carpet", + "blockRuntimeId" : 1653 + }, + { + "id" : "minecraft:carpet", + "blockRuntimeId" : 1655 + }, + { + "id" : "minecraft:carpet", + "blockRuntimeId" : 1642 + }, + { + "id" : "minecraft:carpet", + "blockRuntimeId" : 1645 + }, + { + "id" : "minecraft:carpet", + "blockRuntimeId" : 1646 + }, + { + "id" : "minecraft:carpet", + "blockRuntimeId" : 1654 + }, + { + "id" : "minecraft:carpet", + "blockRuntimeId" : 1650 + }, + { + "id" : "minecraft:carpet", + "blockRuntimeId" : 1644 + }, + { + "id" : "minecraft:carpet", + "blockRuntimeId" : 1652 + }, + { + "id" : "minecraft:carpet", + "blockRuntimeId" : 1651 + }, + { + "id" : "minecraft:carpet", + "blockRuntimeId" : 1643 + }, + { + "id" : "minecraft:carpet", + "blockRuntimeId" : 1647 + }, + { + "id" : "minecraft:concrete_powder", + "blockRuntimeId" : 10102 + }, + { + "id" : "minecraft:concrete_powder", + "blockRuntimeId" : 10110 + }, + { + "id" : "minecraft:concrete_powder", + "blockRuntimeId" : 10109 + }, + { + "id" : "minecraft:concrete_powder", + "blockRuntimeId" : 10117 + }, + { + "id" : "minecraft:concrete_powder", + "blockRuntimeId" : 10114 + }, + { + "id" : "minecraft:concrete_powder", + "blockRuntimeId" : 10116 + }, + { + "id" : "minecraft:concrete_powder", + "blockRuntimeId" : 10103 + }, + { + "id" : "minecraft:concrete_powder", + "blockRuntimeId" : 10106 + }, + { + "id" : "minecraft:concrete_powder", + "blockRuntimeId" : 10107 + }, + { + "id" : "minecraft:concrete_powder", + "blockRuntimeId" : 10115 + }, + { + "id" : "minecraft:concrete_powder", + "blockRuntimeId" : 10111 + }, + { + "id" : "minecraft:concrete_powder", + "blockRuntimeId" : 10105 + }, + { + "id" : "minecraft:concrete_powder", + "blockRuntimeId" : 10113 + }, + { + "id" : "minecraft:concrete_powder", + "blockRuntimeId" : 10112 + }, + { + "id" : "minecraft:concrete_powder", + "blockRuntimeId" : 10104 + }, + { + "id" : "minecraft:concrete_powder", + "blockRuntimeId" : 10108 + }, + { + "id" : "minecraft:concrete", + "blockRuntimeId" : 1343 + }, + { + "id" : "minecraft:concrete", + "blockRuntimeId" : 1351 + }, + { + "id" : "minecraft:concrete", + "blockRuntimeId" : 1350 + }, + { + "id" : "minecraft:concrete", + "blockRuntimeId" : 1358 + }, + { + "id" : "minecraft:concrete", + "blockRuntimeId" : 1355 + }, + { + "id" : "minecraft:concrete", + "blockRuntimeId" : 1357 + }, + { + "id" : "minecraft:concrete", + "blockRuntimeId" : 1344 + }, + { + "id" : "minecraft:concrete", + "blockRuntimeId" : 1347 + }, + { + "id" : "minecraft:concrete", + "blockRuntimeId" : 1348 + }, + { + "id" : "minecraft:concrete", + "blockRuntimeId" : 1356 + }, + { + "id" : "minecraft:concrete", + "blockRuntimeId" : 1352 + }, + { + "id" : "minecraft:concrete", + "blockRuntimeId" : 1346 + }, + { + "id" : "minecraft:concrete", + "blockRuntimeId" : 1354 + }, + { + "id" : "minecraft:concrete", + "blockRuntimeId" : 1353 + }, + { + "id" : "minecraft:concrete", + "blockRuntimeId" : 1345 + }, + { + "id" : "minecraft:concrete", + "blockRuntimeId" : 1349 + }, + { + "id" : "minecraft:clay", + "blockRuntimeId" : 10987 + }, + { + "id" : "minecraft:hardened_clay", + "blockRuntimeId" : 940 + }, + { + "id" : "minecraft:stained_hardened_clay", + "blockRuntimeId" : 10006 + }, + { + "id" : "minecraft:stained_hardened_clay", + "blockRuntimeId" : 10014 + }, + { + "id" : "minecraft:stained_hardened_clay", + "blockRuntimeId" : 10013 + }, + { + "id" : "minecraft:stained_hardened_clay", + "blockRuntimeId" : 10021 + }, + { + "id" : "minecraft:stained_hardened_clay", + "blockRuntimeId" : 10018 + }, + { + "id" : "minecraft:stained_hardened_clay", + "blockRuntimeId" : 10020 + }, + { + "id" : "minecraft:stained_hardened_clay", + "blockRuntimeId" : 10007 + }, + { + "id" : "minecraft:stained_hardened_clay", + "blockRuntimeId" : 10010 + }, + { + "id" : "minecraft:stained_hardened_clay", + "blockRuntimeId" : 10011 + }, + { + "id" : "minecraft:stained_hardened_clay", + "blockRuntimeId" : 10019 + }, + { + "id" : "minecraft:stained_hardened_clay", + "blockRuntimeId" : 10015 + }, + { + "id" : "minecraft:stained_hardened_clay", + "blockRuntimeId" : 10009 + }, + { + "id" : "minecraft:stained_hardened_clay", + "blockRuntimeId" : 10017 + }, + { + "id" : "minecraft:stained_hardened_clay", + "blockRuntimeId" : 10016 + }, + { + "id" : "minecraft:stained_hardened_clay", + "blockRuntimeId" : 10008 + }, + { + "id" : "minecraft:stained_hardened_clay", + "blockRuntimeId" : 10012 + }, + { + "id" : "minecraft:white_glazed_terracotta", + "blockRuntimeId" : 8224 + }, + { + "id" : "minecraft:silver_glazed_terracotta", + "blockRuntimeId" : 4602 + }, + { + "id" : "minecraft:gray_glazed_terracotta", + "blockRuntimeId" : 12517 + }, + { + "id" : "minecraft:black_glazed_terracotta", + "blockRuntimeId" : 8870 + }, + { + "id" : "minecraft:brown_glazed_terracotta", + "blockRuntimeId" : 5002 + }, + { + "id" : "minecraft:red_glazed_terracotta", + "blockRuntimeId" : 6011 + }, + { + "id" : "minecraft:orange_glazed_terracotta", + "blockRuntimeId" : 1844 + }, + { + "id" : "minecraft:yellow_glazed_terracotta", + "blockRuntimeId" : 1605 + }, + { + "id" : "minecraft:lime_glazed_terracotta", + "blockRuntimeId" : 229 + }, + { + "id" : "minecraft:green_glazed_terracotta", + "blockRuntimeId" : 10462 + }, + { + "id" : "minecraft:cyan_glazed_terracotta", + "blockRuntimeId" : 8007 + }, + { + "id" : "minecraft:light_blue_glazed_terracotta", + "blockRuntimeId" : 8121 + }, + { + "id" : "minecraft:blue_glazed_terracotta", + "blockRuntimeId" : 8115 + }, + { + "id" : "minecraft:purple_glazed_terracotta", + "blockRuntimeId" : 10873 + }, + { + "id" : "minecraft:magenta_glazed_terracotta", + "blockRuntimeId" : 1658 + }, + { + "id" : "minecraft:pink_glazed_terracotta", + "blockRuntimeId" : 10391 + }, + { + "id" : "minecraft:purpur_block", + "blockRuntimeId" : 11977 + }, + { + "id" : "minecraft:purpur_block", + "blockRuntimeId" : 11979 + }, + { + "id" : "minecraft:packed_mud", + "blockRuntimeId" : 290 + }, + { + "id" : "minecraft:mud_bricks", + "blockRuntimeId" : 10745 + }, + { + "id" : "minecraft:nether_wart_block", + "blockRuntimeId" : 6141 + }, + { + "id" : "minecraft:warped_wart_block", + "blockRuntimeId" : 9325 + }, + { + "id" : "minecraft:shroomlight", + "blockRuntimeId" : 7677 + }, + { + "id" : "minecraft:crimson_nylium", + "blockRuntimeId" : 6035 + }, + { + "id" : "minecraft:warped_nylium", + "blockRuntimeId" : 10187 + }, + { + "id" : "minecraft:basalt", + "blockRuntimeId" : 6197 + }, + { + "id" : "minecraft:polished_basalt", + "blockRuntimeId" : 24 + }, + { + "id" : "minecraft:smooth_basalt", + "blockRuntimeId" : 1850 + }, + { + "id" : "minecraft:soul_soil", + "blockRuntimeId" : 8482 + }, + { + "id" : "minecraft:dirt", + "blockRuntimeId" : 8403 + }, + { + "id" : "minecraft:dirt", + "blockRuntimeId" : 8404 + }, + { + "id" : "minecraft:farmland", + "blockRuntimeId" : 5756 + }, + { + "id" : "minecraft:grass", + "blockRuntimeId" : 10837 + }, + { + "id" : "minecraft:grass_path", + "blockRuntimeId" : 12345 + }, + { + "id" : "minecraft:podzol", + "blockRuntimeId" : 6882 + }, + { + "id" : "minecraft:mycelium", + "blockRuntimeId" : 5141 + }, + { + "id" : "minecraft:mud", + "blockRuntimeId" : 10536 + }, + { + "id" : "minecraft:stone", + "blockRuntimeId" : 1336 + }, + { + "id" : "minecraft:iron_ore", + "blockRuntimeId" : 6922 + }, + { + "id" : "minecraft:gold_ore", + "blockRuntimeId" : 1604 + }, + { + "id" : "minecraft:diamond_ore", + "blockRuntimeId" : 6209 + }, + { + "id" : "minecraft:lapis_ore", + "blockRuntimeId" : 11962 + }, + { + "id" : "minecraft:redstone_ore", + "blockRuntimeId" : 6135 + }, + { + "id" : "minecraft:coal_ore", + "blockRuntimeId" : 6133 + }, + { + "id" : "minecraft:copper_ore", + "blockRuntimeId" : 5009 + }, + { + "id" : "minecraft:emerald_ore", + "blockRuntimeId" : 11610 + }, + { + "id" : "minecraft:quartz_ore", + "blockRuntimeId" : 6349 + }, + { + "id" : "minecraft:nether_gold_ore", + "blockRuntimeId" : 27 + }, + { + "id" : "minecraft:ancient_debris", + "blockRuntimeId" : 9937 + }, + { + "id" : "minecraft:deepslate_iron_ore", + "blockRuntimeId" : 11536 + }, + { + "id" : "minecraft:deepslate_gold_ore", + "blockRuntimeId" : 9936 + }, + { + "id" : "minecraft:deepslate_diamond_ore", + "blockRuntimeId" : 12301 + }, + { + "id" : "minecraft:deepslate_lapis_ore", + "blockRuntimeId" : 11525 + }, + { + "id" : "minecraft:deepslate_redstone_ore", + "blockRuntimeId" : 10468 + }, + { + "id" : "minecraft:deepslate_emerald_ore", + "blockRuntimeId" : 10188 + }, + { + "id" : "minecraft:deepslate_coal_ore", + "blockRuntimeId" : 11443 + }, + { + "id" : "minecraft:deepslate_copper_ore", + "blockRuntimeId" : 108 + }, + { + "id" : "minecraft:gravel", + "blockRuntimeId" : 12553 + }, + { + "id" : "minecraft:stone", + "blockRuntimeId" : 1337 + }, + { + "id" : "minecraft:stone", + "blockRuntimeId" : 1339 + }, + { + "id" : "minecraft:stone", + "blockRuntimeId" : 1341 + }, + { + "id" : "minecraft:blackstone", + "blockRuntimeId" : 11848 + }, + { + "id" : "minecraft:deepslate", + "blockRuntimeId" : 259 + }, + { + "id" : "minecraft:stone", + "blockRuntimeId" : 1338 + }, + { + "id" : "minecraft:stone", + "blockRuntimeId" : 1340 + }, + { + "id" : "minecraft:stone", + "blockRuntimeId" : 1342 + }, + { + "id" : "minecraft:polished_blackstone", + "blockRuntimeId" : 5140 + }, + { + "id" : "minecraft:polished_deepslate", + "blockRuntimeId" : 12017 + }, + { + "id" : "minecraft:sand", + "blockRuntimeId" : 6041 + }, + { + "id" : "minecraft:sand", + "blockRuntimeId" : 6042 + }, + { + "id" : "minecraft:cactus", + "blockRuntimeId" : 10848 + }, + { + "id" : "minecraft:log", + "blockRuntimeId" : 10524 + }, + { + "id" : "minecraft:stripped_oak_log", + "blockRuntimeId" : 11806 + }, + { + "id" : "minecraft:log", + "blockRuntimeId" : 10525 + }, + { + "id" : "minecraft:stripped_spruce_log", + "blockRuntimeId" : 10126 + }, + { + "id" : "minecraft:log", + "blockRuntimeId" : 10526 + }, + { + "id" : "minecraft:stripped_birch_log", + "blockRuntimeId" : 9392 + }, + { + "id" : "minecraft:log", + "blockRuntimeId" : 10527 + }, + { + "id" : "minecraft:stripped_jungle_log", + "blockRuntimeId" : 1325 + }, + { + "id" : "minecraft:log2", + "blockRuntimeId" : 5674 + }, + { + "id" : "minecraft:stripped_acacia_log", + "blockRuntimeId" : 8884 + }, + { + "id" : "minecraft:log2", + "blockRuntimeId" : 5675 + }, + { + "id" : "minecraft:stripped_dark_oak_log", + "blockRuntimeId" : 221 + }, + { + "id" : "minecraft:mangrove_log", + "blockRuntimeId" : 645 + }, + { + "id" : "minecraft:stripped_mangrove_log", + "blockRuntimeId" : 12550 + }, + { + "id" : "minecraft:crimson_stem", + "blockRuntimeId" : 9317 + }, + { + "id" : "minecraft:stripped_crimson_stem", + "blockRuntimeId" : 10810 + }, + { + "id" : "minecraft:warped_stem", + "blockRuntimeId" : 10336 + }, + { + "id" : "minecraft:stripped_warped_stem", + "blockRuntimeId" : 11663 + }, + { + "id" : "minecraft:wood", + "blockRuntimeId" : 4161 + }, + { + "id" : "minecraft:wood", + "blockRuntimeId" : 4167 + }, + { + "id" : "minecraft:wood", + "blockRuntimeId" : 4162 + }, + { + "id" : "minecraft:wood", + "blockRuntimeId" : 4168 + }, + { + "id" : "minecraft:wood", + "blockRuntimeId" : 4163 + }, + { + "id" : "minecraft:wood", + "blockRuntimeId" : 4169 + }, + { + "id" : "minecraft:wood", + "blockRuntimeId" : 4164 + }, + { + "id" : "minecraft:wood", + "blockRuntimeId" : 4170 + }, + { + "id" : "minecraft:wood", + "blockRuntimeId" : 4165 + }, + { + "id" : "minecraft:wood", + "blockRuntimeId" : 4171 + }, + { + "id" : "minecraft:wood", + "blockRuntimeId" : 4166 + }, + { + "id" : "minecraft:wood", + "blockRuntimeId" : 4172 + }, + { + "id" : "minecraft:mangrove_wood", + "blockRuntimeId" : 6005 + }, + { + "id" : "minecraft:stripped_mangrove_wood", + "blockRuntimeId" : 6075 + }, + { + "id" : "minecraft:crimson_hyphae", + "blockRuntimeId" : 6142 + }, + { + "id" : "minecraft:stripped_crimson_hyphae", + "blockRuntimeId" : 10349 + }, + { + "id" : "minecraft:warped_hyphae", + "blockRuntimeId" : 9322 + }, + { + "id" : "minecraft:stripped_warped_hyphae", + "blockRuntimeId" : 8230 + }, + { + "id" : "minecraft:leaves", + "blockRuntimeId" : 9920 + }, + { + "id" : "minecraft:leaves", + "blockRuntimeId" : 9921 + }, + { + "id" : "minecraft:leaves", + "blockRuntimeId" : 9922 + }, + { + "id" : "minecraft:leaves", + "blockRuntimeId" : 9923 + }, + { + "id" : "minecraft:leaves2", + "blockRuntimeId" : 6201 + }, + { + "id" : "minecraft:leaves2", + "blockRuntimeId" : 6202 + }, + { + "id" : "minecraft:mangrove_leaves", + "blockRuntimeId" : 10518 + }, + { + "id" : "minecraft:azalea_leaves", + "blockRuntimeId" : 11973 + }, + { + "id" : "minecraft:azalea_leaves_flowered", + "blockRuntimeId" : 10177 + }, + { + "id" : "minecraft:sapling", + "blockRuntimeId" : 1396 + }, + { + "id" : "minecraft:sapling", + "blockRuntimeId" : 1397 + }, + { + "id" : "minecraft:sapling", + "blockRuntimeId" : 1398 + }, + { + "id" : "minecraft:sapling", + "blockRuntimeId" : 1399 + }, + { + "id" : "minecraft:sapling", + "blockRuntimeId" : 1400 + }, + { + "id" : "minecraft:sapling", + "blockRuntimeId" : 1401 + }, + { + "id" : "minecraft:mangrove_propagule", + "blockRuntimeId" : 10838 + }, + { + "id" : "minecraft:bee_nest", + "blockRuntimeId" : 8406 + }, + { + "id" : "minecraft:wheat_seeds" + }, + { + "id" : "minecraft:pumpkin_seeds" + }, + { + "id" : "minecraft:melon_seeds" + }, + { + "id" : "minecraft:beetroot_seeds" + }, + { + "id" : "minecraft:wheat" + }, + { + "id" : "minecraft:beetroot" + }, + { + "id" : "minecraft:potato" + }, + { + "id" : "minecraft:poisonous_potato" + }, + { + "id" : "minecraft:carrot" + }, + { + "id" : "minecraft:golden_carrot" + }, + { + "id" : "minecraft:apple" + }, + { + "id" : "minecraft:golden_apple" + }, + { + "id" : "minecraft:enchanted_golden_apple" + }, + { + "id" : "minecraft:melon_block", + "blockRuntimeId" : 690 + }, + { + "id" : "minecraft:melon_slice" + }, + { + "id" : "minecraft:glistering_melon_slice" + }, + { + "id" : "minecraft:sweet_berries" + }, + { + "id" : "minecraft:glow_berries" + }, + { + "id" : "minecraft:pumpkin", + "blockRuntimeId" : 6425 + }, + { + "id" : "minecraft:carved_pumpkin", + "blockRuntimeId" : 11641 + }, + { + "id" : "minecraft:lit_pumpkin", + "blockRuntimeId" : 10537 + }, + { + "id" : "minecraft:honeycomb" + }, + { + "id" : "minecraft:tallgrass", + "blockRuntimeId" : 1621 + }, + { + "id" : "minecraft:double_plant", + "blockRuntimeId" : 8105 + }, + { + "id" : "minecraft:tallgrass", + "blockRuntimeId" : 1620 + }, + { + "id" : "minecraft:double_plant", + "blockRuntimeId" : 8104 + }, + { + "id" : "minecraft:nether_sprouts" + }, + { + "id" : "minecraft:coral", + "blockRuntimeId" : 10342 + }, + { + "id" : "minecraft:coral", + "blockRuntimeId" : 10340 + }, + { + "id" : "minecraft:coral", + "blockRuntimeId" : 10341 + }, + { + "id" : "minecraft:coral", + "blockRuntimeId" : 10339 + }, + { + "id" : "minecraft:coral", + "blockRuntimeId" : 10343 + }, + { + "id" : "minecraft:coral", + "blockRuntimeId" : 10347 + }, + { + "id" : "minecraft:coral", + "blockRuntimeId" : 10345 + }, + { + "id" : "minecraft:coral", + "blockRuntimeId" : 10346 + }, + { + "id" : "minecraft:coral", + "blockRuntimeId" : 10344 + }, + { + "id" : "minecraft:coral", + "blockRuntimeId" : 10348 + }, + { + "id" : "minecraft:coral_fan", + "blockRuntimeId" : 6464 + }, + { + "id" : "minecraft:coral_fan", + "blockRuntimeId" : 6462 + }, + { + "id" : "minecraft:coral_fan", + "blockRuntimeId" : 6463 + }, + { + "id" : "minecraft:coral_fan", + "blockRuntimeId" : 6461 + }, + { + "id" : "minecraft:coral_fan", + "blockRuntimeId" : 6465 + }, + { + "id" : "minecraft:coral_fan_dead", + "blockRuntimeId" : 72 + }, + { + "id" : "minecraft:coral_fan_dead", + "blockRuntimeId" : 70 + }, + { + "id" : "minecraft:coral_fan_dead", + "blockRuntimeId" : 71 + }, + { + "id" : "minecraft:coral_fan_dead", + "blockRuntimeId" : 69 + }, + { + "id" : "minecraft:coral_fan_dead", + "blockRuntimeId" : 73 + }, + { + "id" : "minecraft:crimson_roots", + "blockRuntimeId" : 11836 + }, + { + "id" : "minecraft:warped_roots", + "blockRuntimeId" : 6210 + }, + { + "id" : "minecraft:yellow_flower", + "blockRuntimeId" : 597 + }, + { + "id" : "minecraft:red_flower", + "blockRuntimeId" : 5074 + }, + { + "id" : "minecraft:red_flower", + "blockRuntimeId" : 5075 + }, + { + "id" : "minecraft:red_flower", + "blockRuntimeId" : 5076 + }, + { + "id" : "minecraft:red_flower", + "blockRuntimeId" : 5077 + }, + { + "id" : "minecraft:red_flower", + "blockRuntimeId" : 5078 + }, + { + "id" : "minecraft:red_flower", + "blockRuntimeId" : 5079 + }, + { + "id" : "minecraft:red_flower", + "blockRuntimeId" : 5080 + }, + { + "id" : "minecraft:red_flower", + "blockRuntimeId" : 5081 + }, + { + "id" : "minecraft:red_flower", + "blockRuntimeId" : 5082 + }, + { + "id" : "minecraft:red_flower", + "blockRuntimeId" : 5083 + }, + { + "id" : "minecraft:red_flower", + "blockRuntimeId" : 5084 + }, + { + "id" : "minecraft:double_plant", + "blockRuntimeId" : 8102 + }, + { + "id" : "minecraft:double_plant", + "blockRuntimeId" : 8103 + }, + { + "id" : "minecraft:double_plant", + "blockRuntimeId" : 8106 + }, + { + "id" : "minecraft:double_plant", + "blockRuntimeId" : 8107 + }, + { + "id" : "minecraft:wither_rose", + "blockRuntimeId" : 9995 + }, + { + "id" : "minecraft:white_dye" + }, + { + "id" : "minecraft:light_gray_dye" + }, + { + "id" : "minecraft:gray_dye" + }, + { + "id" : "minecraft:black_dye" + }, + { + "id" : "minecraft:brown_dye" + }, + { + "id" : "minecraft:red_dye" + }, + { + "id" : "minecraft:orange_dye" + }, + { + "id" : "minecraft:yellow_dye" + }, + { + "id" : "minecraft:lime_dye" + }, + { + "id" : "minecraft:green_dye" + }, + { + "id" : "minecraft:cyan_dye" + }, + { + "id" : "minecraft:light_blue_dye" + }, + { + "id" : "minecraft:blue_dye" + }, + { + "id" : "minecraft:purple_dye" + }, + { + "id" : "minecraft:magenta_dye" + }, + { + "id" : "minecraft:pink_dye" + }, + { + "id" : "minecraft:ink_sac" + }, + { + "id" : "minecraft:glow_ink_sac" + }, + { + "id" : "minecraft:cocoa_beans" + }, + { + "id" : "minecraft:lapis_lazuli" + }, + { + "id" : "minecraft:bone_meal" + }, + { + "id" : "minecraft:vine", + "blockRuntimeId" : 1586 + }, + { + "id" : "minecraft:weeping_vines", + "blockRuntimeId" : 8130 + }, + { + "id" : "minecraft:twisting_vines", + "blockRuntimeId" : 8343 + }, + { + "id" : "minecraft:waterlily", + "blockRuntimeId" : 1851 + }, + { + "id" : "minecraft:seagrass", + "blockRuntimeId" : 252 + }, + { + "id" : "minecraft:kelp" + }, + { + "id" : "minecraft:deadbush", + "blockRuntimeId" : 6909 + }, + { + "id" : "minecraft:bamboo", + "blockRuntimeId" : 5142 + }, + { + "id" : "minecraft:snow", + "blockRuntimeId" : 6040 + }, + { + "id" : "minecraft:ice", + "blockRuntimeId" : 10541 + }, + { + "id" : "minecraft:packed_ice", + "blockRuntimeId" : 289 + }, + { + "id" : "minecraft:blue_ice", + "blockRuntimeId" : 10889 + }, + { + "id" : "minecraft:snow_layer", + "blockRuntimeId" : 160 + }, + { + "id" : "minecraft:pointed_dripstone", + "blockRuntimeId" : 11679 + }, + { + "id" : "minecraft:dripstone_block", + "blockRuntimeId" : 1585 + }, + { + "id" : "minecraft:moss_carpet", + "blockRuntimeId" : 293 + }, + { + "id" : "minecraft:moss_block", + "blockRuntimeId" : 10390 + }, + { + "id" : "minecraft:dirt_with_roots", + "blockRuntimeId" : 8046 + }, + { + "id" : "minecraft:hanging_roots", + "blockRuntimeId" : 210 + }, + { + "id" : "minecraft:mangrove_roots", + "blockRuntimeId" : 10005 + }, + { + "id" : "minecraft:muddy_mangrove_roots", + "blockRuntimeId" : 640 + }, + { + "id" : "minecraft:big_dripleaf", + "blockRuntimeId" : 9400 + }, + { + "id" : "minecraft:small_dripleaf_block", + "blockRuntimeId" : 6168 + }, + { + "id" : "minecraft:spore_blossom", + "blockRuntimeId" : 11575 + }, + { + "id" : "minecraft:azalea", + "blockRuntimeId" : 10744 + }, + { + "id" : "minecraft:flowering_azalea", + "blockRuntimeId" : 8127 + }, + { + "id" : "minecraft:glow_lichen", + "blockRuntimeId" : 8336 + }, + { + "id" : "minecraft:amethyst_block", + "blockRuntimeId" : 329 + }, + { + "id" : "minecraft:budding_amethyst", + "blockRuntimeId" : 10864 + }, + { + "id" : "minecraft:amethyst_cluster", + "blockRuntimeId" : 12073 + }, + { + "id" : "minecraft:large_amethyst_bud", + "blockRuntimeId" : 6960 + }, + { + "id" : "minecraft:medium_amethyst_bud", + "blockRuntimeId" : 6224 + }, + { + "id" : "minecraft:small_amethyst_bud", + "blockRuntimeId" : 599 + }, + { + "id" : "minecraft:tuff", + "blockRuntimeId" : 644 + }, + { + "id" : "minecraft:calcite", + "blockRuntimeId" : 220 + }, + { + "id" : "minecraft:chicken" + }, + { + "id" : "minecraft:porkchop" + }, + { + "id" : "minecraft:beef" + }, + { + "id" : "minecraft:mutton" + }, + { + "id" : "minecraft:rabbit" + }, + { + "id" : "minecraft:cod" + }, + { + "id" : "minecraft:salmon" + }, + { + "id" : "minecraft:tropical_fish" + }, + { + "id" : "minecraft:pufferfish" + }, + { + "id" : "minecraft:brown_mushroom", + "blockRuntimeId" : 5001 + }, + { + "id" : "minecraft:red_mushroom", + "blockRuntimeId" : 6433 + }, + { + "id" : "minecraft:crimson_fungus", + "blockRuntimeId" : 12016 + }, + { + "id" : "minecraft:warped_fungus", + "blockRuntimeId" : 294 + }, + { + "id" : "minecraft:brown_mushroom_block", + "blockRuntimeId" : 11625 + }, + { + "id" : "minecraft:red_mushroom_block", + "blockRuntimeId" : 5069 + }, + { + "id" : "minecraft:brown_mushroom_block", + "blockRuntimeId" : 11626 + }, + { + "id" : "minecraft:brown_mushroom_block", + "blockRuntimeId" : 11611 + }, + { + "id" : "minecraft:egg" + }, + { + "id" : "minecraft:sugar_cane" + }, + { + "id" : "minecraft:sugar" + }, + { + "id" : "minecraft:rotten_flesh" + }, + { + "id" : "minecraft:bone" + }, + { + "id" : "minecraft:web", + "blockRuntimeId" : 10565 + }, + { + "id" : "minecraft:spider_eye" + }, + { + "id" : "minecraft:mob_spawner", + "blockRuntimeId" : 699 + }, + { + "id" : "minecraft:monster_egg", + "blockRuntimeId" : 5988 + }, + { + "id" : "minecraft:monster_egg", + "blockRuntimeId" : 5989 + }, + { + "id" : "minecraft:monster_egg", + "blockRuntimeId" : 5990 + }, + { + "id" : "minecraft:monster_egg", + "blockRuntimeId" : 5991 + }, + { + "id" : "minecraft:monster_egg", + "blockRuntimeId" : 5992 + }, + { + "id" : "minecraft:monster_egg", + "blockRuntimeId" : 5993 + }, + { + "id" : "minecraft:infested_deepslate", + "blockRuntimeId" : 6873 + }, + { + "id" : "minecraft:dragon_egg", + "blockRuntimeId" : 11534 + }, + { + "id" : "minecraft:turtle_egg", + "blockRuntimeId" : 12260 + }, + { + "id" : "minecraft:frog_spawn", + "blockRuntimeId" : 6247 + }, + { + "id" : "minecraft:pearlescent_froglight", + "blockRuntimeId" : 10273 + }, + { + "id" : "minecraft:verdant_froglight", + "blockRuntimeId" : 10331 + }, + { + "id" : "minecraft:ochre_froglight", + "blockRuntimeId" : 4581 + }, + { + "id" : "minecraft:chicken_spawn_egg" + }, + { + "id" : "minecraft:bee_spawn_egg" + }, + { + "id" : "minecraft:cow_spawn_egg" + }, + { + "id" : "minecraft:pig_spawn_egg" + }, + { + "id" : "minecraft:sheep_spawn_egg" + }, + { + "id" : "minecraft:wolf_spawn_egg" + }, + { + "id" : "minecraft:polar_bear_spawn_egg" + }, + { + "id" : "minecraft:ocelot_spawn_egg" + }, + { + "id" : "minecraft:cat_spawn_egg" + }, + { + "id" : "minecraft:mooshroom_spawn_egg" + }, + { + "id" : "minecraft:bat_spawn_egg" + }, + { + "id" : "minecraft:parrot_spawn_egg" + }, + { + "id" : "minecraft:rabbit_spawn_egg" + }, + { + "id" : "minecraft:llama_spawn_egg" + }, + { + "id" : "minecraft:horse_spawn_egg" + }, + { + "id" : "minecraft:donkey_spawn_egg" + }, + { + "id" : "minecraft:mule_spawn_egg" + }, + { + "id" : "minecraft:skeleton_horse_spawn_egg" + }, + { + "id" : "minecraft:zombie_horse_spawn_egg" + }, + { + "id" : "minecraft:tropical_fish_spawn_egg" + }, + { + "id" : "minecraft:cod_spawn_egg" + }, + { + "id" : "minecraft:pufferfish_spawn_egg" + }, + { + "id" : "minecraft:salmon_spawn_egg" + }, + { + "id" : "minecraft:dolphin_spawn_egg" + }, + { + "id" : "minecraft:turtle_spawn_egg" + }, + { + "id" : "minecraft:panda_spawn_egg" + }, + { + "id" : "minecraft:fox_spawn_egg" + }, + { + "id" : "minecraft:creeper_spawn_egg" + }, + { + "id" : "minecraft:enderman_spawn_egg" + }, + { + "id" : "minecraft:silverfish_spawn_egg" + }, + { + "id" : "minecraft:skeleton_spawn_egg" + }, + { + "id" : "minecraft:wither_skeleton_spawn_egg" + }, + { + "id" : "minecraft:stray_spawn_egg" + }, + { + "id" : "minecraft:slime_spawn_egg" + }, + { + "id" : "minecraft:spider_spawn_egg" + }, + { + "id" : "minecraft:zombie_spawn_egg" + }, + { + "id" : "minecraft:zombie_pigman_spawn_egg" + }, + { + "id" : "minecraft:husk_spawn_egg" + }, + { + "id" : "minecraft:drowned_spawn_egg" + }, + { + "id" : "minecraft:squid_spawn_egg" + }, + { + "id" : "minecraft:glow_squid_spawn_egg" + }, + { + "id" : "minecraft:cave_spider_spawn_egg" + }, + { + "id" : "minecraft:witch_spawn_egg" + }, + { + "id" : "minecraft:guardian_spawn_egg" + }, + { + "id" : "minecraft:elder_guardian_spawn_egg" + }, + { + "id" : "minecraft:endermite_spawn_egg" + }, + { + "id" : "minecraft:magma_cube_spawn_egg" + }, + { + "id" : "minecraft:strider_spawn_egg" + }, + { + "id" : "minecraft:hoglin_spawn_egg" + }, + { + "id" : "minecraft:piglin_spawn_egg" + }, + { + "id" : "minecraft:zoglin_spawn_egg" + }, + { + "id" : "minecraft:piglin_brute_spawn_egg" + }, + { + "id" : "minecraft:goat_spawn_egg" + }, + { + "id" : "minecraft:axolotl_spawn_egg" + }, + { + "id" : "minecraft:warden_spawn_egg" + }, + { + "id" : "minecraft:allay_spawn_egg" + }, + { + "id" : "minecraft:frog_spawn_egg" + }, + { + "id" : "minecraft:tadpole_spawn_egg" + }, + { + "id" : "minecraft:trader_llama_spawn_egg" + }, + { + "id" : "minecraft:ghast_spawn_egg" + }, + { + "id" : "minecraft:blaze_spawn_egg" + }, + { + "id" : "minecraft:shulker_spawn_egg" + }, + { + "id" : "minecraft:vindicator_spawn_egg" + }, + { + "id" : "minecraft:evoker_spawn_egg" + }, + { + "id" : "minecraft:vex_spawn_egg" + }, + { + "id" : "minecraft:villager_spawn_egg" + }, + { + "id" : "minecraft:wandering_trader_spawn_egg" + }, + { + "id" : "minecraft:zombie_villager_spawn_egg" + }, + { + "id" : "minecraft:phantom_spawn_egg" + }, + { + "id" : "minecraft:pillager_spawn_egg" + }, + { + "id" : "minecraft:ravager_spawn_egg" + }, + { + "id" : "minecraft:iron_golem_spawn_egg" + }, + { + "id" : "minecraft:snow_golem_spawn_egg" + }, + { + "id" : "minecraft:obsidian", + "blockRuntimeId" : 726 + }, + { + "id" : "minecraft:crying_obsidian", + "blockRuntimeId" : 10574 + }, + { + "id" : "minecraft:bedrock", + "blockRuntimeId" : 10879 + }, + { + "id" : "minecraft:soul_sand", + "blockRuntimeId" : 8483 + }, + { + "id" : "minecraft:netherrack", + "blockRuntimeId" : 10899 + }, + { + "id" : "minecraft:magma", + "blockRuntimeId" : 12272 + }, + { + "id" : "minecraft:nether_wart" + }, + { + "id" : "minecraft:end_stone", + "blockRuntimeId" : 5680 + }, + { + "id" : "minecraft:chorus_flower", + "blockRuntimeId" : 6378 + }, + { + "id" : "minecraft:chorus_plant", + "blockRuntimeId" : 8156 + }, + { + "id" : "minecraft:chorus_fruit" + }, + { + "id" : "minecraft:popped_chorus_fruit" + }, + { + "id" : "minecraft:sponge", + "blockRuntimeId" : 927 + }, + { + "id" : "minecraft:sponge", + "blockRuntimeId" : 928 + }, + { + "id" : "minecraft:coral_block", + "blockRuntimeId" : 7869 + }, + { + "id" : "minecraft:coral_block", + "blockRuntimeId" : 7870 + }, + { + "id" : "minecraft:coral_block", + "blockRuntimeId" : 7871 + }, + { + "id" : "minecraft:coral_block", + "blockRuntimeId" : 7872 + }, + { + "id" : "minecraft:coral_block", + "blockRuntimeId" : 7873 + }, + { + "id" : "minecraft:coral_block", + "blockRuntimeId" : 7874 + }, + { + "id" : "minecraft:coral_block", + "blockRuntimeId" : 7875 + }, + { + "id" : "minecraft:coral_block", + "blockRuntimeId" : 7876 + }, + { + "id" : "minecraft:coral_block", + "blockRuntimeId" : 7877 + }, + { + "id" : "minecraft:coral_block", + "blockRuntimeId" : 7878 + }, + { + "id" : "minecraft:sculk", + "blockRuntimeId" : 10898 + }, + { + "id" : "minecraft:sculk_vein", + "blockRuntimeId" : 11379 + }, + { + "id" : "minecraft:sculk_catalyst", + "blockRuntimeId" : 5071 + }, + { + "id" : "minecraft:sculk_shrieker", + "blockRuntimeId" : 224 + }, + { + "id" : "minecraft:sculk_sensor", + "blockRuntimeId" : 6237 + }, + { + "id" : "minecraft:reinforced_deepslate", + "blockRuntimeId" : 8868 + }, + { + "id" : "minecraft:leather_helmet" + }, + { + "id" : "minecraft:chainmail_helmet" + }, + { + "id" : "minecraft:iron_helmet" + }, + { + "id" : "minecraft:golden_helmet" + }, + { + "id" : "minecraft:diamond_helmet" + }, + { + "id" : "minecraft:netherite_helmet" + }, + { + "id" : "minecraft:leather_chestplate" + }, + { + "id" : "minecraft:chainmail_chestplate" + }, + { + "id" : "minecraft:iron_chestplate" + }, + { + "id" : "minecraft:golden_chestplate" + }, + { + "id" : "minecraft:diamond_chestplate" + }, + { + "id" : "minecraft:netherite_chestplate" + }, + { + "id" : "minecraft:leather_leggings" + }, + { + "id" : "minecraft:chainmail_leggings" + }, + { + "id" : "minecraft:iron_leggings" + }, + { + "id" : "minecraft:golden_leggings" + }, + { + "id" : "minecraft:diamond_leggings" + }, + { + "id" : "minecraft:netherite_leggings" + }, + { + "id" : "minecraft:leather_boots" + }, + { + "id" : "minecraft:chainmail_boots" + }, + { + "id" : "minecraft:iron_boots" + }, + { + "id" : "minecraft:golden_boots" + }, + { + "id" : "minecraft:diamond_boots" + }, + { + "id" : "minecraft:netherite_boots" + }, + { + "id" : "minecraft:wooden_sword" + }, + { + "id" : "minecraft:stone_sword" + }, + { + "id" : "minecraft:iron_sword" + }, + { + "id" : "minecraft:golden_sword" + }, + { + "id" : "minecraft:diamond_sword" + }, + { + "id" : "minecraft:netherite_sword" + }, + { + "id" : "minecraft:wooden_axe" + }, + { + "id" : "minecraft:stone_axe" + }, + { + "id" : "minecraft:iron_axe" + }, + { + "id" : "minecraft:golden_axe" + }, + { + "id" : "minecraft:diamond_axe" + }, + { + "id" : "minecraft:netherite_axe" + }, + { + "id" : "minecraft:wooden_pickaxe" + }, + { + "id" : "minecraft:stone_pickaxe" + }, + { + "id" : "minecraft:iron_pickaxe" + }, + { + "id" : "minecraft:golden_pickaxe" + }, + { + "id" : "minecraft:diamond_pickaxe" + }, + { + "id" : "minecraft:netherite_pickaxe" + }, + { + "id" : "minecraft:wooden_shovel" + }, + { + "id" : "minecraft:stone_shovel" + }, + { + "id" : "minecraft:iron_shovel" + }, + { + "id" : "minecraft:golden_shovel" + }, + { + "id" : "minecraft:diamond_shovel" + }, + { + "id" : "minecraft:netherite_shovel" + }, + { + "id" : "minecraft:wooden_hoe" + }, + { + "id" : "minecraft:stone_hoe" + }, + { + "id" : "minecraft:iron_hoe" + }, + { + "id" : "minecraft:golden_hoe" + }, + { + "id" : "minecraft:diamond_hoe" + }, + { + "id" : "minecraft:netherite_hoe" + }, + { + "id" : "minecraft:bow" + }, + { + "id" : "minecraft:crossbow" + }, + { + "id" : "minecraft:arrow" + }, + { + "id" : "minecraft:arrow", + "damage" : 6 + }, + { + "id" : "minecraft:arrow", + "damage" : 7 + }, + { + "id" : "minecraft:arrow", + "damage" : 8 + }, + { + "id" : "minecraft:arrow", + "damage" : 9 + }, + { + "id" : "minecraft:arrow", + "damage" : 10 + }, + { + "id" : "minecraft:arrow", + "damage" : 11 + }, + { + "id" : "minecraft:arrow", + "damage" : 12 + }, + { + "id" : "minecraft:arrow", + "damage" : 13 + }, + { + "id" : "minecraft:arrow", + "damage" : 14 + }, + { + "id" : "minecraft:arrow", + "damage" : 15 + }, + { + "id" : "minecraft:arrow", + "damage" : 16 + }, + { + "id" : "minecraft:arrow", + "damage" : 17 + }, + { + "id" : "minecraft:arrow", + "damage" : 18 + }, + { + "id" : "minecraft:arrow", + "damage" : 19 + }, + { + "id" : "minecraft:arrow", + "damage" : 20 + }, + { + "id" : "minecraft:arrow", + "damage" : 21 + }, + { + "id" : "minecraft:arrow", + "damage" : 22 + }, + { + "id" : "minecraft:arrow", + "damage" : 23 + }, + { + "id" : "minecraft:arrow", + "damage" : 24 + }, + { + "id" : "minecraft:arrow", + "damage" : 25 + }, + { + "id" : "minecraft:arrow", + "damage" : 26 + }, + { + "id" : "minecraft:arrow", + "damage" : 27 + }, + { + "id" : "minecraft:arrow", + "damage" : 28 + }, + { + "id" : "minecraft:arrow", + "damage" : 29 + }, + { + "id" : "minecraft:arrow", + "damage" : 30 + }, + { + "id" : "minecraft:arrow", + "damage" : 31 + }, + { + "id" : "minecraft:arrow", + "damage" : 32 + }, + { + "id" : "minecraft:arrow", + "damage" : 33 + }, + { + "id" : "minecraft:arrow", + "damage" : 34 + }, + { + "id" : "minecraft:arrow", + "damage" : 35 + }, + { + "id" : "minecraft:arrow", + "damage" : 36 + }, + { + "id" : "minecraft:arrow", + "damage" : 37 + }, + { + "id" : "minecraft:arrow", + "damage" : 38 + }, + { + "id" : "minecraft:arrow", + "damage" : 39 + }, + { + "id" : "minecraft:arrow", + "damage" : 40 + }, + { + "id" : "minecraft:arrow", + "damage" : 41 + }, + { + "id" : "minecraft:arrow", + "damage" : 42 + }, + { + "id" : "minecraft:arrow", + "damage" : 43 + }, + { + "id" : "minecraft:shield" + }, + { + "id" : "minecraft:cooked_chicken" + }, + { + "id" : "minecraft:cooked_porkchop" + }, + { + "id" : "minecraft:cooked_beef" + }, + { + "id" : "minecraft:cooked_mutton" + }, + { + "id" : "minecraft:cooked_rabbit" + }, + { + "id" : "minecraft:cooked_cod" + }, + { + "id" : "minecraft:cooked_salmon" + }, + { + "id" : "minecraft:bread" + }, + { + "id" : "minecraft:mushroom_stew" + }, + { + "id" : "minecraft:beetroot_soup" + }, + { + "id" : "minecraft:rabbit_stew" + }, + { + "id" : "minecraft:baked_potato" + }, + { + "id" : "minecraft:cookie" + }, + { + "id" : "minecraft:pumpkin_pie" + }, + { + "id" : "minecraft:cake" + }, + { + "id" : "minecraft:dried_kelp" + }, + { + "id" : "minecraft:fishing_rod" + }, + { + "id" : "minecraft:carrot_on_a_stick" + }, + { + "id" : "minecraft:warped_fungus_on_a_stick" + }, + { + "id" : "minecraft:snowball" + }, + { + "id" : "minecraft:shears" + }, + { + "id" : "minecraft:flint_and_steel" + }, + { + "id" : "minecraft:lead" + }, + { + "id" : "minecraft:clock" + }, + { + "id" : "minecraft:compass" + }, + { + "id" : "minecraft:recovery_compass" + }, + { + "id" : "minecraft:goat_horn" + }, + { + "id" : "minecraft:goat_horn", + "damage" : 1 + }, + { + "id" : "minecraft:goat_horn", + "damage" : 2 + }, + { + "id" : "minecraft:goat_horn", + "damage" : 3 + }, + { + "id" : "minecraft:goat_horn", + "damage" : 4 + }, + { + "id" : "minecraft:goat_horn", + "damage" : 5 + }, + { + "id" : "minecraft:goat_horn", + "damage" : 6 + }, + { + "id" : "minecraft:goat_horn", + "damage" : 7 + }, + { + "id" : "minecraft:empty_map" + }, + { + "id" : "minecraft:empty_map", + "damage" : 2 + }, + { + "id" : "minecraft:saddle" + }, + { + "id" : "minecraft:leather_horse_armor" + }, + { + "id" : "minecraft:iron_horse_armor" + }, + { + "id" : "minecraft:golden_horse_armor" + }, + { + "id" : "minecraft:diamond_horse_armor" + }, + { + "id" : "minecraft:trident" + }, + { + "id" : "minecraft:turtle_helmet" + }, + { + "id" : "minecraft:elytra" + }, + { + "id" : "minecraft:totem_of_undying" + }, + { + "id" : "minecraft:glass_bottle" + }, + { + "id" : "minecraft:experience_bottle" + }, + { + "id" : "minecraft:potion" + }, + { + "id" : "minecraft:potion", + "damage" : 1 + }, + { + "id" : "minecraft:potion", + "damage" : 2 + }, + { + "id" : "minecraft:potion", + "damage" : 3 + }, + { + "id" : "minecraft:potion", + "damage" : 4 + }, + { + "id" : "minecraft:potion", + "damage" : 5 + }, + { + "id" : "minecraft:potion", + "damage" : 6 + }, + { + "id" : "minecraft:potion", + "damage" : 7 + }, + { + "id" : "minecraft:potion", + "damage" : 8 + }, + { + "id" : "minecraft:potion", + "damage" : 9 + }, + { + "id" : "minecraft:potion", + "damage" : 10 + }, + { + "id" : "minecraft:potion", + "damage" : 11 + }, + { + "id" : "minecraft:potion", + "damage" : 12 + }, + { + "id" : "minecraft:potion", + "damage" : 13 + }, + { + "id" : "minecraft:potion", + "damage" : 14 + }, + { + "id" : "minecraft:potion", + "damage" : 15 + }, + { + "id" : "minecraft:potion", + "damage" : 16 + }, + { + "id" : "minecraft:potion", + "damage" : 17 + }, + { + "id" : "minecraft:potion", + "damage" : 18 + }, + { + "id" : "minecraft:potion", + "damage" : 19 + }, + { + "id" : "minecraft:potion", + "damage" : 20 + }, + { + "id" : "minecraft:potion", + "damage" : 21 + }, + { + "id" : "minecraft:potion", + "damage" : 22 + }, + { + "id" : "minecraft:potion", + "damage" : 23 + }, + { + "id" : "minecraft:potion", + "damage" : 24 + }, + { + "id" : "minecraft:potion", + "damage" : 25 + }, + { + "id" : "minecraft:potion", + "damage" : 26 + }, + { + "id" : "minecraft:potion", + "damage" : 27 + }, + { + "id" : "minecraft:potion", + "damage" : 28 + }, + { + "id" : "minecraft:potion", + "damage" : 29 + }, + { + "id" : "minecraft:potion", + "damage" : 30 + }, + { + "id" : "minecraft:potion", + "damage" : 31 + }, + { + "id" : "minecraft:potion", + "damage" : 32 + }, + { + "id" : "minecraft:potion", + "damage" : 33 + }, + { + "id" : "minecraft:potion", + "damage" : 34 + }, + { + "id" : "minecraft:potion", + "damage" : 35 + }, + { + "id" : "minecraft:potion", + "damage" : 36 + }, + { + "id" : "minecraft:potion", + "damage" : 37 + }, + { + "id" : "minecraft:potion", + "damage" : 38 + }, + { + "id" : "minecraft:potion", + "damage" : 39 + }, + { + "id" : "minecraft:potion", + "damage" : 40 + }, + { + "id" : "minecraft:potion", + "damage" : 41 + }, + { + "id" : "minecraft:potion", + "damage" : 42 + }, + { + "id" : "minecraft:splash_potion" + }, + { + "id" : "minecraft:splash_potion", + "damage" : 1 + }, + { + "id" : "minecraft:splash_potion", + "damage" : 2 + }, + { + "id" : "minecraft:splash_potion", + "damage" : 3 + }, + { + "id" : "minecraft:splash_potion", + "damage" : 4 + }, + { + "id" : "minecraft:splash_potion", + "damage" : 5 + }, + { + "id" : "minecraft:splash_potion", + "damage" : 6 + }, + { + "id" : "minecraft:splash_potion", + "damage" : 7 + }, + { + "id" : "minecraft:splash_potion", + "damage" : 8 + }, + { + "id" : "minecraft:splash_potion", + "damage" : 9 + }, + { + "id" : "minecraft:splash_potion", + "damage" : 10 + }, + { + "id" : "minecraft:splash_potion", + "damage" : 11 + }, + { + "id" : "minecraft:splash_potion", + "damage" : 12 + }, + { + "id" : "minecraft:splash_potion", + "damage" : 13 + }, + { + "id" : "minecraft:splash_potion", + "damage" : 14 + }, + { + "id" : "minecraft:splash_potion", + "damage" : 15 + }, + { + "id" : "minecraft:splash_potion", + "damage" : 16 + }, + { + "id" : "minecraft:splash_potion", + "damage" : 17 + }, + { + "id" : "minecraft:splash_potion", + "damage" : 18 + }, + { + "id" : "minecraft:splash_potion", + "damage" : 19 + }, + { + "id" : "minecraft:splash_potion", + "damage" : 20 + }, + { + "id" : "minecraft:splash_potion", + "damage" : 21 + }, + { + "id" : "minecraft:splash_potion", + "damage" : 22 + }, + { + "id" : "minecraft:splash_potion", + "damage" : 23 + }, + { + "id" : "minecraft:splash_potion", + "damage" : 24 + }, + { + "id" : "minecraft:splash_potion", + "damage" : 25 + }, + { + "id" : "minecraft:splash_potion", + "damage" : 26 + }, + { + "id" : "minecraft:splash_potion", + "damage" : 27 + }, + { + "id" : "minecraft:splash_potion", + "damage" : 28 + }, + { + "id" : "minecraft:splash_potion", + "damage" : 29 + }, + { + "id" : "minecraft:splash_potion", + "damage" : 30 + }, + { + "id" : "minecraft:splash_potion", + "damage" : 31 + }, + { + "id" : "minecraft:splash_potion", + "damage" : 32 + }, + { + "id" : "minecraft:splash_potion", + "damage" : 33 + }, + { + "id" : "minecraft:splash_potion", + "damage" : 34 + }, + { + "id" : "minecraft:splash_potion", + "damage" : 35 + }, + { + "id" : "minecraft:splash_potion", + "damage" : 36 + }, + { + "id" : "minecraft:splash_potion", + "damage" : 37 + }, + { + "id" : "minecraft:splash_potion", + "damage" : 38 + }, + { + "id" : "minecraft:splash_potion", + "damage" : 39 + }, + { + "id" : "minecraft:splash_potion", + "damage" : 40 + }, + { + "id" : "minecraft:splash_potion", + "damage" : 41 + }, + { + "id" : "minecraft:splash_potion", + "damage" : 42 + }, + { + "id" : "minecraft:lingering_potion" + }, + { + "id" : "minecraft:lingering_potion", + "damage" : 1 + }, + { + "id" : "minecraft:lingering_potion", + "damage" : 2 + }, + { + "id" : "minecraft:lingering_potion", + "damage" : 3 + }, + { + "id" : "minecraft:lingering_potion", + "damage" : 4 + }, + { + "id" : "minecraft:lingering_potion", + "damage" : 5 + }, + { + "id" : "minecraft:lingering_potion", + "damage" : 6 + }, + { + "id" : "minecraft:lingering_potion", + "damage" : 7 + }, + { + "id" : "minecraft:lingering_potion", + "damage" : 8 + }, + { + "id" : "minecraft:lingering_potion", + "damage" : 9 + }, + { + "id" : "minecraft:lingering_potion", + "damage" : 10 + }, + { + "id" : "minecraft:lingering_potion", + "damage" : 11 + }, + { + "id" : "minecraft:lingering_potion", + "damage" : 12 + }, + { + "id" : "minecraft:lingering_potion", + "damage" : 13 + }, + { + "id" : "minecraft:lingering_potion", + "damage" : 14 + }, + { + "id" : "minecraft:lingering_potion", + "damage" : 15 + }, + { + "id" : "minecraft:lingering_potion", + "damage" : 16 + }, + { + "id" : "minecraft:lingering_potion", + "damage" : 17 + }, + { + "id" : "minecraft:lingering_potion", + "damage" : 18 + }, + { + "id" : "minecraft:lingering_potion", + "damage" : 19 + }, + { + "id" : "minecraft:lingering_potion", + "damage" : 20 + }, + { + "id" : "minecraft:lingering_potion", + "damage" : 21 + }, + { + "id" : "minecraft:lingering_potion", + "damage" : 22 + }, + { + "id" : "minecraft:lingering_potion", + "damage" : 23 + }, + { + "id" : "minecraft:lingering_potion", + "damage" : 24 + }, + { + "id" : "minecraft:lingering_potion", + "damage" : 25 + }, + { + "id" : "minecraft:lingering_potion", + "damage" : 26 + }, + { + "id" : "minecraft:lingering_potion", + "damage" : 27 + }, + { + "id" : "minecraft:lingering_potion", + "damage" : 28 + }, + { + "id" : "minecraft:lingering_potion", + "damage" : 29 + }, + { + "id" : "minecraft:lingering_potion", + "damage" : 30 + }, + { + "id" : "minecraft:lingering_potion", + "damage" : 31 + }, + { + "id" : "minecraft:lingering_potion", + "damage" : 32 + }, + { + "id" : "minecraft:lingering_potion", + "damage" : 33 + }, + { + "id" : "minecraft:lingering_potion", + "damage" : 34 + }, + { + "id" : "minecraft:lingering_potion", + "damage" : 35 + }, + { + "id" : "minecraft:lingering_potion", + "damage" : 36 + }, + { + "id" : "minecraft:lingering_potion", + "damage" : 37 + }, + { + "id" : "minecraft:lingering_potion", + "damage" : 38 + }, + { + "id" : "minecraft:lingering_potion", + "damage" : 39 + }, + { + "id" : "minecraft:lingering_potion", + "damage" : 40 + }, + { + "id" : "minecraft:lingering_potion", + "damage" : 41 + }, + { + "id" : "minecraft:lingering_potion", + "damage" : 42 + }, + { + "id" : "minecraft:spyglass" + }, + { + "id" : "minecraft:stick" + }, + { + "id" : "minecraft:bed" + }, + { + "id" : "minecraft:bed", + "damage" : 8 + }, + { + "id" : "minecraft:bed", + "damage" : 7 + }, + { + "id" : "minecraft:bed", + "damage" : 15 + }, + { + "id" : "minecraft:bed", + "damage" : 12 + }, + { + "id" : "minecraft:bed", + "damage" : 14 + }, + { + "id" : "minecraft:bed", + "damage" : 1 + }, + { + "id" : "minecraft:bed", + "damage" : 4 + }, + { + "id" : "minecraft:bed", + "damage" : 5 + }, + { + "id" : "minecraft:bed", + "damage" : 13 + }, + { + "id" : "minecraft:bed", + "damage" : 9 + }, + { + "id" : "minecraft:bed", + "damage" : 3 + }, + { + "id" : "minecraft:bed", + "damage" : 11 + }, + { + "id" : "minecraft:bed", + "damage" : 10 + }, + { + "id" : "minecraft:bed", + "damage" : 2 + }, + { + "id" : "minecraft:bed", + "damage" : 6 + }, + { + "id" : "minecraft:torch", + "blockRuntimeId" : 1416 + }, + { + "id" : "minecraft:soul_torch", + "blockRuntimeId" : 6876 + }, + { + "id" : "minecraft:sea_pickle", + "blockRuntimeId" : 8891 + }, + { + "id" : "minecraft:lantern", + "blockRuntimeId" : 10936 + }, + { + "id" : "minecraft:soul_lantern", + "blockRuntimeId" : 8401 + }, + { + "id" : "minecraft:candle", + "blockRuntimeId" : 11666 + }, + { + "id" : "minecraft:white_candle", + "blockRuntimeId" : 7948 + }, + { + "id" : "minecraft:orange_candle", + "blockRuntimeId" : 660 + }, + { + "id" : "minecraft:magenta_candle", + "blockRuntimeId" : 716 + }, + { + "id" : "minecraft:light_blue_candle", + "blockRuntimeId" : 6417 + }, + { + "id" : "minecraft:yellow_candle", + "blockRuntimeId" : 10022 + }, + { + "id" : "minecraft:lime_candle", + "blockRuntimeId" : 10206 + }, + { + "id" : "minecraft:pink_candle", + "blockRuntimeId" : 11633 + }, + { + "id" : "minecraft:gray_candle", + "blockRuntimeId" : 1631 + }, + { + "id" : "minecraft:light_gray_candle", + "blockRuntimeId" : 10062 + }, + { + "id" : "minecraft:cyan_candle", + "blockRuntimeId" : 11989 + }, + { + "id" : "minecraft:purple_candle", + "blockRuntimeId" : 10900 + }, + { + "id" : "minecraft:blue_candle" + }, + { + "id" : "minecraft:brown_candle", + "blockRuntimeId" : 9295 + }, + { + "id" : "minecraft:green_candle", + "blockRuntimeId" : 1370 + }, + { + "id" : "minecraft:red_candle", + "blockRuntimeId" : 6913 + }, + { + "id" : "minecraft:black_candle", + "blockRuntimeId" : 176 + }, + { + "id" : "minecraft:crafting_table", + "blockRuntimeId" : 8890 + }, + { + "id" : "minecraft:cartography_table", + "blockRuntimeId" : 12554 + }, + { + "id" : "minecraft:fletching_table", + "blockRuntimeId" : 8869 + }, + { + "id" : "minecraft:smithing_table", + "blockRuntimeId" : 5184 + }, + { + "id" : "minecraft:beehive", + "blockRuntimeId" : 9938 + }, + { + "id" : "minecraft:campfire" + }, + { + "id" : "minecraft:soul_campfire" + }, + { + "id" : "minecraft:furnace", + "blockRuntimeId" : 12065 + }, + { + "id" : "minecraft:blast_furnace", + "blockRuntimeId" : 11830 + }, + { + "id" : "minecraft:smoker", + "blockRuntimeId" : 1330 + }, + { + "id" : "minecraft:respawn_anchor", + "blockRuntimeId" : 1365 + }, + { + "id" : "minecraft:brewing_stand" + }, + { + "id" : "minecraft:anvil", + "blockRuntimeId" : 10486 + }, + { + "id" : "minecraft:anvil", + "blockRuntimeId" : 10490 + }, + { + "id" : "minecraft:anvil", + "blockRuntimeId" : 10494 + }, + { + "id" : "minecraft:grindstone", + "blockRuntimeId" : 12302 + }, + { + "id" : "minecraft:enchanting_table", + "blockRuntimeId" : 10579 + }, + { + "id" : "minecraft:bookshelf", + "blockRuntimeId" : 10523 + }, + { + "id" : "minecraft:lectern", + "blockRuntimeId" : 10802 + }, + { + "id" : "minecraft:cauldron" + }, + { + "id" : "minecraft:composter", + "blockRuntimeId" : 8065 + }, + { + "id" : "minecraft:chest", + "blockRuntimeId" : 10978 + }, + { + "id" : "minecraft:trapped_chest", + "blockRuntimeId" : 8234 + }, + { + "id" : "minecraft:ender_chest", + "blockRuntimeId" : 6217 + }, + { + "id" : "minecraft:barrel", + "blockRuntimeId" : 6366 + }, + { + "id" : "minecraft:undyed_shulker_box", + "blockRuntimeId" : 5139 + }, + { + "id" : "minecraft:shulker_box", + "blockRuntimeId" : 7964 + }, + { + "id" : "minecraft:shulker_box", + "blockRuntimeId" : 7972 + }, + { + "id" : "minecraft:shulker_box", + "blockRuntimeId" : 7971 + }, + { + "id" : "minecraft:shulker_box", + "blockRuntimeId" : 7979 + }, + { + "id" : "minecraft:shulker_box", + "blockRuntimeId" : 7976 + }, + { + "id" : "minecraft:shulker_box", + "blockRuntimeId" : 7978 + }, + { + "id" : "minecraft:shulker_box", + "blockRuntimeId" : 7965 + }, + { + "id" : "minecraft:shulker_box", + "blockRuntimeId" : 7968 + }, + { + "id" : "minecraft:shulker_box", + "blockRuntimeId" : 7969 + }, + { + "id" : "minecraft:shulker_box", + "blockRuntimeId" : 7977 + }, + { + "id" : "minecraft:shulker_box", + "blockRuntimeId" : 7973 + }, + { + "id" : "minecraft:shulker_box", + "blockRuntimeId" : 7967 + }, + { + "id" : "minecraft:shulker_box", + "blockRuntimeId" : 7975 + }, + { + "id" : "minecraft:shulker_box", + "blockRuntimeId" : 7974 + }, + { + "id" : "minecraft:shulker_box", + "blockRuntimeId" : 7966 + }, + { + "id" : "minecraft:shulker_box", + "blockRuntimeId" : 7970 + }, + { + "id" : "minecraft:armor_stand" + }, + { + "id" : "minecraft:noteblock", + "blockRuntimeId" : 643 + }, + { + "id" : "minecraft:jukebox", + "blockRuntimeId" : 7490 + }, + { + "id" : "minecraft:music_disc_13" + }, + { + "id" : "minecraft:music_disc_cat" + }, + { + "id" : "minecraft:music_disc_blocks" + }, + { + "id" : "minecraft:music_disc_chirp" + }, + { + "id" : "minecraft:music_disc_far" + }, + { + "id" : "minecraft:music_disc_mall" + }, + { + "id" : "minecraft:music_disc_mellohi" + }, + { + "id" : "minecraft:music_disc_stal" + }, + { + "id" : "minecraft:music_disc_strad" + }, + { + "id" : "minecraft:music_disc_ward" + }, + { + "id" : "minecraft:music_disc_11" + }, + { + "id" : "minecraft:music_disc_wait" + }, + { + "id" : "minecraft:music_disc_otherside" + }, + { + "id" : "minecraft:music_disc_5" + }, + { + "id" : "minecraft:music_disc_pigstep" + }, + { + "id" : "minecraft:disc_fragment_5" + }, + { + "id" : "minecraft:glowstone_dust" + }, + { + "id" : "minecraft:glowstone", + "blockRuntimeId" : 5729 + }, + { + "id" : "minecraft:redstone_lamp", + "blockRuntimeId" : 257 + }, + { + "id" : "minecraft:sea_lantern", + "blockRuntimeId" : 11809 + }, + { + "id" : "minecraft:oak_sign" + }, + { + "id" : "minecraft:spruce_sign" + }, + { + "id" : "minecraft:birch_sign" + }, + { + "id" : "minecraft:jungle_sign" + }, + { + "id" : "minecraft:acacia_sign" + }, + { + "id" : "minecraft:dark_oak_sign" + }, + { + "id" : "minecraft:mangrove_sign" + }, + { + "id" : "minecraft:crimson_sign" + }, + { + "id" : "minecraft:warped_sign" + }, + { + "id" : "minecraft:painting" + }, + { + "id" : "minecraft:frame" + }, + { + "id" : "minecraft:glow_frame" + }, + { + "id" : "minecraft:honey_bottle" + }, + { + "id" : "minecraft:flower_pot" + }, + { + "id" : "minecraft:bowl" + }, + { + "id" : "minecraft:bucket" + }, + { + "id" : "minecraft:milk_bucket" + }, + { + "id" : "minecraft:water_bucket" + }, + { + "id" : "minecraft:lava_bucket" + }, + { + "id" : "minecraft:cod_bucket" + }, + { + "id" : "minecraft:salmon_bucket" + }, + { + "id" : "minecraft:tropical_fish_bucket" + }, + { + "id" : "minecraft:pufferfish_bucket" + }, + { + "id" : "minecraft:powder_snow_bucket" + }, + { + "id" : "minecraft:axolotl_bucket" + }, + { + "id" : "minecraft:tadpole_bucket" + }, + { + "id" : "minecraft:skull", + "damage" : 3 + }, + { + "id" : "minecraft:skull", + "damage" : 2 + }, + { + "id" : "minecraft:skull", + "damage" : 4 + }, + { + "id" : "minecraft:skull", + "damage" : 5 + }, + { + "id" : "minecraft:skull" + }, + { + "id" : "minecraft:skull", + "damage" : 1 + }, + { + "id" : "minecraft:beacon", + "blockRuntimeId" : 150 + }, + { + "id" : "minecraft:bell", + "blockRuntimeId" : 10770 + }, + { + "id" : "minecraft:conduit", + "blockRuntimeId" : 6078 + }, + { + "id" : "minecraft:stonecutter_block", + "blockRuntimeId" : 11837 + }, + { + "id" : "minecraft:end_portal_frame", + "blockRuntimeId" : 9891 + }, + { + "id" : "minecraft:coal" + }, + { + "id" : "minecraft:charcoal" + }, + { + "id" : "minecraft:diamond" + }, + { + "id" : "minecraft:iron_nugget" + }, + { + "id" : "minecraft:raw_iron" + }, + { + "id" : "minecraft:raw_gold" + }, + { + "id" : "minecraft:raw_copper" + }, + { + "id" : "minecraft:copper_ingot" + }, + { + "id" : "minecraft:iron_ingot" + }, + { + "id" : "minecraft:netherite_scrap" + }, + { + "id" : "minecraft:netherite_ingot" + }, + { + "id" : "minecraft:gold_nugget" + }, + { + "id" : "minecraft:gold_ingot" + }, + { + "id" : "minecraft:emerald" + }, + { + "id" : "minecraft:quartz" + }, + { + "id" : "minecraft:clay_ball" + }, + { + "id" : "minecraft:brick" + }, + { + "id" : "minecraft:netherbrick" + }, + { + "id" : "minecraft:prismarine_shard" + }, + { + "id" : "minecraft:amethyst_shard" + }, + { + "id" : "minecraft:prismarine_crystals" + }, + { + "id" : "minecraft:nautilus_shell" + }, + { + "id" : "minecraft:heart_of_the_sea" + }, + { + "id" : "minecraft:scute" + }, + { + "id" : "minecraft:phantom_membrane" + }, + { + "id" : "minecraft:string" + }, + { + "id" : "minecraft:feather" + }, + { + "id" : "minecraft:flint" + }, + { + "id" : "minecraft:gunpowder" + }, + { + "id" : "minecraft:leather" + }, + { + "id" : "minecraft:rabbit_hide" + }, + { + "id" : "minecraft:rabbit_foot" + }, + { + "id" : "minecraft:fire_charge" + }, + { + "id" : "minecraft:blaze_rod" + }, + { + "id" : "minecraft:blaze_powder" + }, + { + "id" : "minecraft:magma_cream" + }, + { + "id" : "minecraft:fermented_spider_eye" + }, + { + "id" : "minecraft:echo_shard" + }, + { + "id" : "minecraft:dragon_breath" + }, + { + "id" : "minecraft:shulker_shell" + }, + { + "id" : "minecraft:ghast_tear" + }, + { + "id" : "minecraft:slime_ball" + }, + { + "id" : "minecraft:ender_pearl" + }, + { + "id" : "minecraft:ender_eye" + }, + { + "id" : "minecraft:nether_star" + }, + { + "id" : "minecraft:end_rod", + "blockRuntimeId" : 9311 + }, + { + "id" : "minecraft:lightning_rod", + "blockRuntimeId" : 1877 + }, + { + "id" : "minecraft:end_crystal" + }, + { + "id" : "minecraft:paper" + }, + { + "id" : "minecraft:book" + }, + { + "id" : "minecraft:writable_book" + }, + { + "id" : "minecraft:enchanted_book", + "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgIAaWQAAAIDAGx2bAEAAAA=" + }, + { + "id" : "minecraft:enchanted_book", + "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgIAaWQAAAIDAGx2bAIAAAA=" + }, + { + "id" : "minecraft:enchanted_book", + "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgIAaWQAAAIDAGx2bAMAAAA=" + }, + { + "id" : "minecraft:enchanted_book", + "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgIAaWQAAAIDAGx2bAQAAAA=" + }, + { + "id" : "minecraft:enchanted_book", + "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgIAaWQBAAIDAGx2bAEAAAA=" + }, + { + "id" : "minecraft:enchanted_book", + "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgIAaWQBAAIDAGx2bAIAAAA=" + }, + { + "id" : "minecraft:enchanted_book", + "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgIAaWQBAAIDAGx2bAMAAAA=" + }, + { + "id" : "minecraft:enchanted_book", + "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgIAaWQBAAIDAGx2bAQAAAA=" + }, + { + "id" : "minecraft:enchanted_book", + "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgIAaWQCAAIDAGx2bAEAAAA=" + }, + { + "id" : "minecraft:enchanted_book", + "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgIAaWQCAAIDAGx2bAIAAAA=" + }, + { + "id" : "minecraft:enchanted_book", + "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgIAaWQCAAIDAGx2bAMAAAA=" + }, + { + "id" : "minecraft:enchanted_book", + "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgIAaWQCAAIDAGx2bAQAAAA=" + }, + { + "id" : "minecraft:enchanted_book", + "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgIAaWQDAAIDAGx2bAEAAAA=" + }, + { + "id" : "minecraft:enchanted_book", + "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgIAaWQDAAIDAGx2bAIAAAA=" + }, + { + "id" : "minecraft:enchanted_book", + "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgIAaWQDAAIDAGx2bAMAAAA=" + }, + { + "id" : "minecraft:enchanted_book", + "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgIAaWQDAAIDAGx2bAQAAAA=" + }, + { + "id" : "minecraft:enchanted_book", + "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgIAaWQEAAIDAGx2bAEAAAA=" + }, + { + "id" : "minecraft:enchanted_book", + "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgIAaWQEAAIDAGx2bAIAAAA=" + }, + { + "id" : "minecraft:enchanted_book", + "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgIAaWQEAAIDAGx2bAMAAAA=" + }, + { + "id" : "minecraft:enchanted_book", + "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgIAaWQEAAIDAGx2bAQAAAA=" + }, + { + "id" : "minecraft:enchanted_book", + "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgIAaWQFAAIDAGx2bAEAAAA=" + }, + { + "id" : "minecraft:enchanted_book", + "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgIAaWQFAAIDAGx2bAIAAAA=" + }, + { + "id" : "minecraft:enchanted_book", + "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgIAaWQFAAIDAGx2bAMAAAA=" + }, + { + "id" : "minecraft:enchanted_book", + "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgIAaWQGAAIDAGx2bAEAAAA=" + }, + { + "id" : "minecraft:enchanted_book", + "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgIAaWQGAAIDAGx2bAIAAAA=" + }, + { + "id" : "minecraft:enchanted_book", + "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgIAaWQGAAIDAGx2bAMAAAA=" + }, + { + "id" : "minecraft:enchanted_book", + "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgIAaWQHAAIDAGx2bAEAAAA=" + }, + { + "id" : "minecraft:enchanted_book", + "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgIAaWQHAAIDAGx2bAIAAAA=" + }, + { + "id" : "minecraft:enchanted_book", + "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgIAaWQHAAIDAGx2bAMAAAA=" + }, + { + "id" : "minecraft:enchanted_book", + "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgIAaWQIAAIDAGx2bAEAAAA=" + }, + { + "id" : "minecraft:enchanted_book", + "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgIAaWQJAAIDAGx2bAEAAAA=" + }, + { + "id" : "minecraft:enchanted_book", + "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgIAaWQJAAIDAGx2bAIAAAA=" + }, + { + "id" : "minecraft:enchanted_book", + "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgIAaWQJAAIDAGx2bAMAAAA=" + }, + { + "id" : "minecraft:enchanted_book", + "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgIAaWQJAAIDAGx2bAQAAAA=" + }, + { + "id" : "minecraft:enchanted_book", + "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgIAaWQJAAIDAGx2bAUAAAA=" + }, + { + "id" : "minecraft:enchanted_book", + "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgIAaWQKAAIDAGx2bAEAAAA=" + }, + { + "id" : "minecraft:enchanted_book", + "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgIAaWQKAAIDAGx2bAIAAAA=" + }, + { + "id" : "minecraft:enchanted_book", + "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgIAaWQKAAIDAGx2bAMAAAA=" + }, + { + "id" : "minecraft:enchanted_book", + "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgIAaWQKAAIDAGx2bAQAAAA=" + }, + { + "id" : "minecraft:enchanted_book", + "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgIAaWQKAAIDAGx2bAUAAAA=" + }, + { + "id" : "minecraft:enchanted_book", + "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgIAaWQLAAIDAGx2bAEAAAA=" + }, + { + "id" : "minecraft:enchanted_book", + "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgIAaWQLAAIDAGx2bAIAAAA=" + }, + { + "id" : "minecraft:enchanted_book", + "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgIAaWQLAAIDAGx2bAMAAAA=" + }, + { + "id" : "minecraft:enchanted_book", + "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgIAaWQLAAIDAGx2bAQAAAA=" + }, + { + "id" : "minecraft:enchanted_book", + "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgIAaWQLAAIDAGx2bAUAAAA=" + }, + { + "id" : "minecraft:enchanted_book", + "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgIAaWQMAAIDAGx2bAEAAAA=" + }, + { + "id" : "minecraft:enchanted_book", + "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgIAaWQMAAIDAGx2bAIAAAA=" + }, + { + "id" : "minecraft:enchanted_book", + "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgIAaWQNAAIDAGx2bAEAAAA=" + }, + { + "id" : "minecraft:enchanted_book", + "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgIAaWQNAAIDAGx2bAIAAAA=" + }, + { + "id" : "minecraft:enchanted_book", + "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgIAaWQOAAIDAGx2bAEAAAA=" + }, + { + "id" : "minecraft:enchanted_book", + "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgIAaWQOAAIDAGx2bAIAAAA=" + }, + { + "id" : "minecraft:enchanted_book", + "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgIAaWQOAAIDAGx2bAMAAAA=" + }, + { + "id" : "minecraft:enchanted_book", + "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgIAaWQPAAIDAGx2bAEAAAA=" + }, + { + "id" : "minecraft:enchanted_book", + "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgIAaWQPAAIDAGx2bAIAAAA=" + }, + { + "id" : "minecraft:enchanted_book", + "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgIAaWQPAAIDAGx2bAMAAAA=" + }, + { + "id" : "minecraft:enchanted_book", + "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgIAaWQPAAIDAGx2bAQAAAA=" + }, + { + "id" : "minecraft:enchanted_book", + "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgIAaWQPAAIDAGx2bAUAAAA=" + }, + { + "id" : "minecraft:enchanted_book", + "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgIAaWQQAAIDAGx2bAEAAAA=" + }, + { + "id" : "minecraft:enchanted_book", + "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgIAaWQRAAIDAGx2bAEAAAA=" + }, + { + "id" : "minecraft:enchanted_book", + "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgIAaWQRAAIDAGx2bAIAAAA=" + }, + { + "id" : "minecraft:enchanted_book", + "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgIAaWQRAAIDAGx2bAMAAAA=" + }, + { + "id" : "minecraft:enchanted_book", + "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgIAaWQSAAIDAGx2bAEAAAA=" + }, + { + "id" : "minecraft:enchanted_book", + "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgIAaWQSAAIDAGx2bAIAAAA=" + }, + { + "id" : "minecraft:enchanted_book", + "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgIAaWQSAAIDAGx2bAMAAAA=" + }, + { + "id" : "minecraft:enchanted_book", + "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgIAaWQTAAIDAGx2bAEAAAA=" + }, + { + "id" : "minecraft:enchanted_book", + "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgIAaWQTAAIDAGx2bAIAAAA=" + }, + { + "id" : "minecraft:enchanted_book", + "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgIAaWQTAAIDAGx2bAMAAAA=" + }, + { + "id" : "minecraft:enchanted_book", + "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgIAaWQTAAIDAGx2bAQAAAA=" + }, + { + "id" : "minecraft:enchanted_book", + "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgIAaWQTAAIDAGx2bAUAAAA=" + }, + { + "id" : "minecraft:enchanted_book", + "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgIAaWQUAAIDAGx2bAEAAAA=" + }, + { + "id" : "minecraft:enchanted_book", + "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgIAaWQUAAIDAGx2bAIAAAA=" + }, + { + "id" : "minecraft:enchanted_book", + "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgIAaWQVAAIDAGx2bAEAAAA=" + }, + { + "id" : "minecraft:enchanted_book", + "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgIAaWQWAAIDAGx2bAEAAAA=" + }, + { + "id" : "minecraft:enchanted_book", + "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgIAaWQXAAIDAGx2bAEAAAA=" + }, + { + "id" : "minecraft:enchanted_book", + "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgIAaWQXAAIDAGx2bAIAAAA=" + }, + { + "id" : "minecraft:enchanted_book", + "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgIAaWQXAAIDAGx2bAMAAAA=" + }, + { + "id" : "minecraft:enchanted_book", + "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgIAaWQYAAIDAGx2bAEAAAA=" + }, + { + "id" : "minecraft:enchanted_book", + "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgIAaWQYAAIDAGx2bAIAAAA=" + }, + { + "id" : "minecraft:enchanted_book", + "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgIAaWQYAAIDAGx2bAMAAAA=" + }, + { + "id" : "minecraft:enchanted_book", + "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgIAaWQZAAIDAGx2bAEAAAA=" + }, + { + "id" : "minecraft:enchanted_book", + "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgIAaWQZAAIDAGx2bAIAAAA=" + }, + { + "id" : "minecraft:enchanted_book", + "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgIAaWQaAAIDAGx2bAEAAAA=" + }, + { + "id" : "minecraft:enchanted_book", + "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgIAaWQbAAIDAGx2bAEAAAA=" + }, + { + "id" : "minecraft:enchanted_book", + "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgIAaWQcAAIDAGx2bAEAAAA=" + }, + { + "id" : "minecraft:enchanted_book", + "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgIAaWQdAAIDAGx2bAEAAAA=" + }, + { + "id" : "minecraft:enchanted_book", + "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgIAaWQdAAIDAGx2bAIAAAA=" + }, + { + "id" : "minecraft:enchanted_book", + "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgIAaWQdAAIDAGx2bAMAAAA=" + }, + { + "id" : "minecraft:enchanted_book", + "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgIAaWQdAAIDAGx2bAQAAAA=" + }, + { + "id" : "minecraft:enchanted_book", + "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgIAaWQdAAIDAGx2bAUAAAA=" + }, + { + "id" : "minecraft:enchanted_book", + "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgIAaWQeAAIDAGx2bAEAAAA=" + }, + { + "id" : "minecraft:enchanted_book", + "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgIAaWQeAAIDAGx2bAIAAAA=" + }, + { + "id" : "minecraft:enchanted_book", + "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgIAaWQeAAIDAGx2bAMAAAA=" + }, + { + "id" : "minecraft:enchanted_book", + "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgIAaWQfAAIDAGx2bAEAAAA=" + }, + { + "id" : "minecraft:enchanted_book", + "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgIAaWQfAAIDAGx2bAIAAAA=" + }, + { + "id" : "minecraft:enchanted_book", + "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgIAaWQfAAIDAGx2bAMAAAA=" + }, + { + "id" : "minecraft:enchanted_book", + "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgIAaWQgAAIDAGx2bAEAAAA=" + }, + { + "id" : "minecraft:enchanted_book", + "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgIAaWQhAAIDAGx2bAEAAAA=" + }, + { + "id" : "minecraft:enchanted_book", + "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgIAaWQiAAIDAGx2bAEAAAA=" + }, + { + "id" : "minecraft:enchanted_book", + "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgIAaWQiAAIDAGx2bAIAAAA=" + }, + { + "id" : "minecraft:enchanted_book", + "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgIAaWQiAAIDAGx2bAMAAAA=" + }, + { + "id" : "minecraft:enchanted_book", + "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgIAaWQiAAIDAGx2bAQAAAA=" + }, + { + "id" : "minecraft:enchanted_book", + "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgIAaWQjAAIDAGx2bAEAAAA=" + }, + { + "id" : "minecraft:enchanted_book", + "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgIAaWQjAAIDAGx2bAIAAAA=" + }, + { + "id" : "minecraft:enchanted_book", + "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgIAaWQjAAIDAGx2bAMAAAA=" + }, + { + "id" : "minecraft:enchanted_book", + "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgIAaWQkAAIDAGx2bAEAAAA=" + }, + { + "id" : "minecraft:enchanted_book", + "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgIAaWQkAAIDAGx2bAIAAAA=" + }, + { + "id" : "minecraft:enchanted_book", + "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgIAaWQkAAIDAGx2bAMAAAA=" + }, + { + "id" : "minecraft:enchanted_book", + "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgIAaWQlAAIDAGx2bAEAAAA=" + }, + { + "id" : "minecraft:enchanted_book", + "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgIAaWQlAAIDAGx2bAIAAAA=" + }, + { + "id" : "minecraft:enchanted_book", + "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgIAaWQlAAIDAGx2bAMAAAA=" + }, + { + "id" : "minecraft:oak_boat" + }, + { + "id" : "minecraft:spruce_boat" + }, + { + "id" : "minecraft:birch_boat" + }, + { + "id" : "minecraft:jungle_boat" + }, + { + "id" : "minecraft:acacia_boat" + }, + { + "id" : "minecraft:dark_oak_boat" + }, + { + "id" : "minecraft:mangrove_boat" + }, + { + "id" : "minecraft:oak_chest_boat" + }, + { + "id" : "minecraft:spruce_chest_boat" + }, + { + "id" : "minecraft:birch_chest_boat" + }, + { + "id" : "minecraft:jungle_chest_boat" + }, + { + "id" : "minecraft:acacia_chest_boat" + }, + { + "id" : "minecraft:dark_oak_chest_boat" + }, + { + "id" : "minecraft:mangrove_chest_boat" + }, + { + "id" : "minecraft:rail", + "blockRuntimeId" : 5764 + }, + { + "id" : "minecraft:golden_rail", + "blockRuntimeId" : 7980 + }, + { + "id" : "minecraft:detector_rail", + "blockRuntimeId" : 5976 + }, + { + "id" : "minecraft:activator_rail", + "blockRuntimeId" : 604 + }, + { + "id" : "minecraft:minecart" + }, + { + "id" : "minecraft:chest_minecart" + }, + { + "id" : "minecraft:hopper_minecart" + }, + { + "id" : "minecraft:tnt_minecart" + }, + { + "id" : "minecraft:redstone" + }, + { + "id" : "minecraft:redstone_block", + "blockRuntimeId" : 5236 + }, + { + "id" : "minecraft:redstone_torch", + "blockRuntimeId" : 4596 + }, + { + "id" : "minecraft:lever", + "blockRuntimeId" : 10364 + }, + { + "id" : "minecraft:wooden_button", + "blockRuntimeId" : 10229 + }, + { + "id" : "minecraft:spruce_button", + "blockRuntimeId" : 6169 + }, + { + "id" : "minecraft:birch_button", + "blockRuntimeId" : 12029 + }, + { + "id" : "minecraft:jungle_button", + "blockRuntimeId" : 120 + }, + { + "id" : "minecraft:acacia_button", + "blockRuntimeId" : 11494 + }, + { + "id" : "minecraft:dark_oak_button", + "blockRuntimeId" : 96 + }, + { + "id" : "minecraft:mangrove_button", + "blockRuntimeId" : 10924 + }, + { + "id" : "minecraft:stone_button", + "blockRuntimeId" : 894 + }, + { + "id" : "minecraft:crimson_button", + "blockRuntimeId" : 6280 + }, + { + "id" : "minecraft:warped_button", + "blockRuntimeId" : 11513 + }, + { + "id" : "minecraft:polished_blackstone_button", + "blockRuntimeId" : 12053 + }, + { + "id" : "minecraft:tripwire_hook", + "blockRuntimeId" : 9334 + }, + { + "id" : "minecraft:wooden_pressure_plate", + "blockRuntimeId" : 12327 + }, + { + "id" : "minecraft:spruce_pressure_plate", + "blockRuntimeId" : 5218 + }, + { + "id" : "minecraft:birch_pressure_plate", + "blockRuntimeId" : 5010 + }, + { + "id" : "minecraft:jungle_pressure_plate", + "blockRuntimeId" : 5093 + }, + { + "id" : "minecraft:acacia_pressure_plate", + "blockRuntimeId" : 7879 + }, + { + "id" : "minecraft:dark_oak_pressure_plate", + "blockRuntimeId" : 9376 + }, + { + "id" : "minecraft:mangrove_pressure_plate", + "blockRuntimeId" : 5713 + }, + { + "id" : "minecraft:crimson_pressure_plate", + "blockRuntimeId" : 12534 + }, + { + "id" : "minecraft:warped_pressure_plate", + "blockRuntimeId" : 263 + }, + { + "id" : "minecraft:stone_pressure_plate", + "blockRuntimeId" : 5730 + }, + { + "id" : "minecraft:light_weighted_pressure_plate", + "blockRuntimeId" : 5123 + }, + { + "id" : "minecraft:heavy_weighted_pressure_plate", + "blockRuntimeId" : 1861 + }, + { + "id" : "minecraft:polished_blackstone_pressure_plate", + "blockRuntimeId" : 10070 + }, + { + "id" : "minecraft:observer", + "blockRuntimeId" : 4584 + }, + { + "id" : "minecraft:daylight_detector", + "blockRuntimeId" : 6043 + }, + { + "id" : "minecraft:repeater" + }, + { + "id" : "minecraft:comparator" + }, + { + "id" : "minecraft:hopper" + }, + { + "id" : "minecraft:dropper", + "blockRuntimeId" : 11648 + }, + { + "id" : "minecraft:dispenser", + "blockRuntimeId" : 12276 + }, + { + "id" : "minecraft:piston", + "blockRuntimeId" : 1614 + }, + { + "id" : "minecraft:sticky_piston", + "blockRuntimeId" : 6212 + }, + { + "id" : "minecraft:tnt", + "blockRuntimeId" : 10559 + }, + { + "id" : "minecraft:name_tag" + }, + { + "id" : "minecraft:loom", + "blockRuntimeId" : 5670 + }, + { + "id" : "minecraft:banner" + }, + { + "id" : "minecraft:banner", + "damage" : 8 + }, + { + "id" : "minecraft:banner", + "damage" : 7 + }, + { + "id" : "minecraft:banner", + "damage" : 15 + }, + { + "id" : "minecraft:banner", + "damage" : 12 + }, + { + "id" : "minecraft:banner", + "damage" : 14 + }, + { + "id" : "minecraft:banner", + "damage" : 1 + }, + { + "id" : "minecraft:banner", + "damage" : 4 + }, + { + "id" : "minecraft:banner", + "damage" : 5 + }, + { + "id" : "minecraft:banner", + "damage" : 13 + }, + { + "id" : "minecraft:banner", + "damage" : 9 + }, + { + "id" : "minecraft:banner", + "damage" : 3 + }, + { + "id" : "minecraft:banner", + "damage" : 11 + }, + { + "id" : "minecraft:banner", + "damage" : 10 + }, + { + "id" : "minecraft:banner", + "damage" : 2 + }, + { + "id" : "minecraft:banner", + "damage" : 6 + }, + { + "id" : "minecraft:banner", + "damage" : 15, + "nbt_b64" : "CgAAAwQAVHlwZQEAAAAA" + }, + { + "id" : "minecraft:creeper_banner_pattern" + }, + { + "id" : "minecraft:skull_banner_pattern" + }, + { + "id" : "minecraft:flower_banner_pattern" + }, + { + "id" : "minecraft:mojang_banner_pattern" + }, + { + "id" : "minecraft:field_masoned_banner_pattern" + }, + { + "id" : "minecraft:bordure_indented_banner_pattern" + }, + { + "id" : "minecraft:piglin_banner_pattern" + }, + { + "id" : "minecraft:globe_banner_pattern" + }, + { + "id" : "minecraft:firework_rocket", + "nbt_b64" : "CgAACgkARmlyZXdvcmtzCQoARXhwbG9zaW9ucwAAAAAAAQYARmxpZ2h0AQAA" + }, + { + "id" : "minecraft:firework_rocket", + "nbt_b64" : "CgAACgkARmlyZXdvcmtzCQoARXhwbG9zaW9ucwoBAAAABw0ARmlyZXdvcmtDb2xvcgEAAAAABwwARmlyZXdvcmtGYWRlAAAAAAEPAEZpcmV3b3JrRmxpY2tlcgABDQBGaXJld29ya1RyYWlsAAEMAEZpcmV3b3JrVHlwZQAAAQYARmxpZ2h0AQAA" + }, + { + "id" : "minecraft:firework_rocket", + "nbt_b64" : "CgAACgkARmlyZXdvcmtzCQoARXhwbG9zaW9ucwoBAAAABw0ARmlyZXdvcmtDb2xvcgEAAAAIBwwARmlyZXdvcmtGYWRlAAAAAAEPAEZpcmV3b3JrRmxpY2tlcgABDQBGaXJld29ya1RyYWlsAAEMAEZpcmV3b3JrVHlwZQAAAQYARmxpZ2h0AQAA" + }, + { + "id" : "minecraft:firework_rocket", + "nbt_b64" : "CgAACgkARmlyZXdvcmtzCQoARXhwbG9zaW9ucwoBAAAABw0ARmlyZXdvcmtDb2xvcgEAAAAHBwwARmlyZXdvcmtGYWRlAAAAAAEPAEZpcmV3b3JrRmxpY2tlcgABDQBGaXJld29ya1RyYWlsAAEMAEZpcmV3b3JrVHlwZQAAAQYARmxpZ2h0AQAA" + }, + { + "id" : "minecraft:firework_rocket", + "nbt_b64" : "CgAACgkARmlyZXdvcmtzCQoARXhwbG9zaW9ucwoBAAAABw0ARmlyZXdvcmtDb2xvcgEAAAAPBwwARmlyZXdvcmtGYWRlAAAAAAEPAEZpcmV3b3JrRmxpY2tlcgABDQBGaXJld29ya1RyYWlsAAEMAEZpcmV3b3JrVHlwZQAAAQYARmxpZ2h0AQAA" + }, + { + "id" : "minecraft:firework_rocket", + "nbt_b64" : "CgAACgkARmlyZXdvcmtzCQoARXhwbG9zaW9ucwoBAAAABw0ARmlyZXdvcmtDb2xvcgEAAAAMBwwARmlyZXdvcmtGYWRlAAAAAAEPAEZpcmV3b3JrRmxpY2tlcgABDQBGaXJld29ya1RyYWlsAAEMAEZpcmV3b3JrVHlwZQAAAQYARmxpZ2h0AQAA" + }, + { + "id" : "minecraft:firework_rocket", + "nbt_b64" : "CgAACgkARmlyZXdvcmtzCQoARXhwbG9zaW9ucwoBAAAABw0ARmlyZXdvcmtDb2xvcgEAAAAOBwwARmlyZXdvcmtGYWRlAAAAAAEPAEZpcmV3b3JrRmxpY2tlcgABDQBGaXJld29ya1RyYWlsAAEMAEZpcmV3b3JrVHlwZQAAAQYARmxpZ2h0AQAA" + }, + { + "id" : "minecraft:firework_rocket", + "nbt_b64" : "CgAACgkARmlyZXdvcmtzCQoARXhwbG9zaW9ucwoBAAAABw0ARmlyZXdvcmtDb2xvcgEAAAABBwwARmlyZXdvcmtGYWRlAAAAAAEPAEZpcmV3b3JrRmxpY2tlcgABDQBGaXJld29ya1RyYWlsAAEMAEZpcmV3b3JrVHlwZQAAAQYARmxpZ2h0AQAA" + }, + { + "id" : "minecraft:firework_rocket", + "nbt_b64" : "CgAACgkARmlyZXdvcmtzCQoARXhwbG9zaW9ucwoBAAAABw0ARmlyZXdvcmtDb2xvcgEAAAAEBwwARmlyZXdvcmtGYWRlAAAAAAEPAEZpcmV3b3JrRmxpY2tlcgABDQBGaXJld29ya1RyYWlsAAEMAEZpcmV3b3JrVHlwZQAAAQYARmxpZ2h0AQAA" + }, + { + "id" : "minecraft:firework_rocket", + "nbt_b64" : "CgAACgkARmlyZXdvcmtzCQoARXhwbG9zaW9ucwoBAAAABw0ARmlyZXdvcmtDb2xvcgEAAAAFBwwARmlyZXdvcmtGYWRlAAAAAAEPAEZpcmV3b3JrRmxpY2tlcgABDQBGaXJld29ya1RyYWlsAAEMAEZpcmV3b3JrVHlwZQAAAQYARmxpZ2h0AQAA" + }, + { + "id" : "minecraft:firework_rocket", + "nbt_b64" : "CgAACgkARmlyZXdvcmtzCQoARXhwbG9zaW9ucwoBAAAABw0ARmlyZXdvcmtDb2xvcgEAAAANBwwARmlyZXdvcmtGYWRlAAAAAAEPAEZpcmV3b3JrRmxpY2tlcgABDQBGaXJld29ya1RyYWlsAAEMAEZpcmV3b3JrVHlwZQAAAQYARmxpZ2h0AQAA" + }, + { + "id" : "minecraft:firework_rocket", + "nbt_b64" : "CgAACgkARmlyZXdvcmtzCQoARXhwbG9zaW9ucwoBAAAABw0ARmlyZXdvcmtDb2xvcgEAAAAJBwwARmlyZXdvcmtGYWRlAAAAAAEPAEZpcmV3b3JrRmxpY2tlcgABDQBGaXJld29ya1RyYWlsAAEMAEZpcmV3b3JrVHlwZQAAAQYARmxpZ2h0AQAA" + }, + { + "id" : "minecraft:firework_rocket", + "nbt_b64" : "CgAACgkARmlyZXdvcmtzCQoARXhwbG9zaW9ucwoBAAAABw0ARmlyZXdvcmtDb2xvcgEAAAADBwwARmlyZXdvcmtGYWRlAAAAAAEPAEZpcmV3b3JrRmxpY2tlcgABDQBGaXJld29ya1RyYWlsAAEMAEZpcmV3b3JrVHlwZQAAAQYARmxpZ2h0AQAA" + }, + { + "id" : "minecraft:firework_rocket", + "nbt_b64" : "CgAACgkARmlyZXdvcmtzCQoARXhwbG9zaW9ucwoBAAAABw0ARmlyZXdvcmtDb2xvcgEAAAALBwwARmlyZXdvcmtGYWRlAAAAAAEPAEZpcmV3b3JrRmxpY2tlcgABDQBGaXJld29ya1RyYWlsAAEMAEZpcmV3b3JrVHlwZQAAAQYARmxpZ2h0AQAA" + }, + { + "id" : "minecraft:firework_rocket", + "nbt_b64" : "CgAACgkARmlyZXdvcmtzCQoARXhwbG9zaW9ucwoBAAAABw0ARmlyZXdvcmtDb2xvcgEAAAAKBwwARmlyZXdvcmtGYWRlAAAAAAEPAEZpcmV3b3JrRmxpY2tlcgABDQBGaXJld29ya1RyYWlsAAEMAEZpcmV3b3JrVHlwZQAAAQYARmxpZ2h0AQAA" + }, + { + "id" : "minecraft:firework_rocket", + "nbt_b64" : "CgAACgkARmlyZXdvcmtzCQoARXhwbG9zaW9ucwoBAAAABw0ARmlyZXdvcmtDb2xvcgEAAAACBwwARmlyZXdvcmtGYWRlAAAAAAEPAEZpcmV3b3JrRmxpY2tlcgABDQBGaXJld29ya1RyYWlsAAEMAEZpcmV3b3JrVHlwZQAAAQYARmxpZ2h0AQAA" + }, + { + "id" : "minecraft:firework_rocket", + "nbt_b64" : "CgAACgkARmlyZXdvcmtzCQoARXhwbG9zaW9ucwoBAAAABw0ARmlyZXdvcmtDb2xvcgEAAAAGBwwARmlyZXdvcmtGYWRlAAAAAAEPAEZpcmV3b3JrRmxpY2tlcgABDQBGaXJld29ya1RyYWlsAAEMAEZpcmV3b3JrVHlwZQAAAQYARmxpZ2h0AQAA" + }, + { + "id" : "minecraft:firework_star", + "nbt_b64" : "CgAACg0ARmlyZXdvcmtzSXRlbQcNAEZpcmV3b3JrQ29sb3IBAAAAAAcMAEZpcmV3b3JrRmFkZQAAAAABDwBGaXJld29ya0ZsaWNrZXIAAQ0ARmlyZXdvcmtUcmFpbAABDABGaXJld29ya1R5cGUAAAMLAGN1c3RvbUNvbG9yIR0d/wA=" + }, + { + "id" : "minecraft:firework_star", + "damage" : 8, + "nbt_b64" : "CgAACg0ARmlyZXdvcmtzSXRlbQcNAEZpcmV3b3JrQ29sb3IBAAAACAcMAEZpcmV3b3JrRmFkZQAAAAABDwBGaXJld29ya0ZsaWNrZXIAAQ0ARmlyZXdvcmtUcmFpbAABDABGaXJld29ya1R5cGUAAAMLAGN1c3RvbUNvbG9yUk9H/wA=" + }, + { + "id" : "minecraft:firework_star", + "damage" : 7, + "nbt_b64" : "CgAACg0ARmlyZXdvcmtzSXRlbQcNAEZpcmV3b3JrQ29sb3IBAAAABwcMAEZpcmV3b3JrRmFkZQAAAAABDwBGaXJld29ya0ZsaWNrZXIAAQ0ARmlyZXdvcmtUcmFpbAABDABGaXJld29ya1R5cGUAAAMLAGN1c3RvbUNvbG9yl52d/wA=" + }, + { + "id" : "minecraft:firework_star", + "damage" : 15, + "nbt_b64" : "CgAACg0ARmlyZXdvcmtzSXRlbQcNAEZpcmV3b3JrQ29sb3IBAAAADwcMAEZpcmV3b3JrRmFkZQAAAAABDwBGaXJld29ya0ZsaWNrZXIAAQ0ARmlyZXdvcmtUcmFpbAABDABGaXJld29ya1R5cGUAAAMLAGN1c3RvbUNvbG9y8PDw/wA=" + }, + { + "id" : "minecraft:firework_star", + "damage" : 12, + "nbt_b64" : "CgAACg0ARmlyZXdvcmtzSXRlbQcNAEZpcmV3b3JrQ29sb3IBAAAADAcMAEZpcmV3b3JrRmFkZQAAAAABDwBGaXJld29ya0ZsaWNrZXIAAQ0ARmlyZXdvcmtUcmFpbAABDABGaXJld29ya1R5cGUAAAMLAGN1c3RvbUNvbG9y2rM6/wA=" + }, + { + "id" : "minecraft:firework_star", + "damage" : 14, + "nbt_b64" : "CgAACg0ARmlyZXdvcmtzSXRlbQcNAEZpcmV3b3JrQ29sb3IBAAAADgcMAEZpcmV3b3JrRmFkZQAAAAABDwBGaXJld29ya0ZsaWNrZXIAAQ0ARmlyZXdvcmtUcmFpbAABDABGaXJld29ya1R5cGUAAAMLAGN1c3RvbUNvbG9yHYD5/wA=" + }, + { + "id" : "minecraft:firework_star", + "damage" : 1, + "nbt_b64" : "CgAACg0ARmlyZXdvcmtzSXRlbQcNAEZpcmV3b3JrQ29sb3IBAAAAAQcMAEZpcmV3b3JrRmFkZQAAAAABDwBGaXJld29ya0ZsaWNrZXIAAQ0ARmlyZXdvcmtUcmFpbAABDABGaXJld29ya1R5cGUAAAMLAGN1c3RvbUNvbG9yJi6w/wA=" + }, + { + "id" : "minecraft:firework_star", + "damage" : 4, + "nbt_b64" : "CgAACg0ARmlyZXdvcmtzSXRlbQcNAEZpcmV3b3JrQ29sb3IBAAAABAcMAEZpcmV3b3JrRmFkZQAAAAABDwBGaXJld29ya0ZsaWNrZXIAAQ0ARmlyZXdvcmtUcmFpbAABDABGaXJld29ya1R5cGUAAAMLAGN1c3RvbUNvbG9yqkQ8/wA=" + }, + { + "id" : "minecraft:firework_star", + "damage" : 5, + "nbt_b64" : "CgAACg0ARmlyZXdvcmtzSXRlbQcNAEZpcmV3b3JrQ29sb3IBAAAABQcMAEZpcmV3b3JrRmFkZQAAAAABDwBGaXJld29ya0ZsaWNrZXIAAQ0ARmlyZXdvcmtUcmFpbAABDABGaXJld29ya1R5cGUAAAMLAGN1c3RvbUNvbG9yuDKJ/wA=" + }, + { + "id" : "minecraft:firework_star", + "damage" : 13, + "nbt_b64" : "CgAACg0ARmlyZXdvcmtzSXRlbQcNAEZpcmV3b3JrQ29sb3IBAAAADQcMAEZpcmV3b3JrRmFkZQAAAAABDwBGaXJld29ya0ZsaWNrZXIAAQ0ARmlyZXdvcmtUcmFpbAABDABGaXJld29ya1R5cGUAAAMLAGN1c3RvbUNvbG9yvU7H/wA=" + }, + { + "id" : "minecraft:firework_star", + "damage" : 9, + "nbt_b64" : "CgAACg0ARmlyZXdvcmtzSXRlbQcNAEZpcmV3b3JrQ29sb3IBAAAACQcMAEZpcmV3b3JrRmFkZQAAAAABDwBGaXJld29ya0ZsaWNrZXIAAQ0ARmlyZXdvcmtUcmFpbAABDABGaXJld29ya1R5cGUAAAMLAGN1c3RvbUNvbG9yqovz/wA=" + }, + { + "id" : "minecraft:firework_star", + "damage" : 3, + "nbt_b64" : "CgAACg0ARmlyZXdvcmtzSXRlbQcNAEZpcmV3b3JrQ29sb3IBAAAAAwcMAEZpcmV3b3JrRmFkZQAAAAABDwBGaXJld29ya0ZsaWNrZXIAAQ0ARmlyZXdvcmtUcmFpbAABDABGaXJld29ya1R5cGUAAAMLAGN1c3RvbUNvbG9yMlSD/wA=" + }, + { + "id" : "minecraft:firework_star", + "damage" : 11, + "nbt_b64" : "CgAACg0ARmlyZXdvcmtzSXRlbQcNAEZpcmV3b3JrQ29sb3IBAAAACwcMAEZpcmV3b3JrRmFkZQAAAAABDwBGaXJld29ya0ZsaWNrZXIAAQ0ARmlyZXdvcmtUcmFpbAABDABGaXJld29ya1R5cGUAAAMLAGN1c3RvbUNvbG9yPdj+/wA=" + }, + { + "id" : "minecraft:firework_star", + "damage" : 10, + "nbt_b64" : "CgAACg0ARmlyZXdvcmtzSXRlbQcNAEZpcmV3b3JrQ29sb3IBAAAACgcMAEZpcmV3b3JrRmFkZQAAAAABDwBGaXJld29ya0ZsaWNrZXIAAQ0ARmlyZXdvcmtUcmFpbAABDABGaXJld29ya1R5cGUAAAMLAGN1c3RvbUNvbG9yH8eA/wA=" + }, + { + "id" : "minecraft:firework_star", + "damage" : 2, + "nbt_b64" : "CgAACg0ARmlyZXdvcmtzSXRlbQcNAEZpcmV3b3JrQ29sb3IBAAAAAgcMAEZpcmV3b3JrRmFkZQAAAAABDwBGaXJld29ya0ZsaWNrZXIAAQ0ARmlyZXdvcmtUcmFpbAABDABGaXJld29ya1R5cGUAAAMLAGN1c3RvbUNvbG9yFnxe/wA=" + }, + { + "id" : "minecraft:firework_star", + "damage" : 6, + "nbt_b64" : "CgAACg0ARmlyZXdvcmtzSXRlbQcNAEZpcmV3b3JrQ29sb3IBAAAABgcMAEZpcmV3b3JrRmFkZQAAAAABDwBGaXJld29ya0ZsaWNrZXIAAQ0ARmlyZXdvcmtUcmFpbAABDABGaXJld29ya1R5cGUAAAMLAGN1c3RvbUNvbG9ynJwW/wA=" + }, + { + "id" : "minecraft:chain" + }, + { + "id" : "minecraft:target", + "blockRuntimeId" : 10228 + }, + { + "id" : "minecraft:lodestone_compass" + }, + { + "id" : "minecraft:wither_spawn_egg" + }, + { + "id" : "minecraft:ender_dragon_spawn_egg" + } + ] +} \ No newline at end of file diff --git a/core/src/main/resources/bedrock/runtime_item_states.1_19_70.json b/core/src/main/resources/bedrock/runtime_item_states.1_19_70.json new file mode 100644 index 000000000..500fcc4f6 --- /dev/null +++ b/core/src/main/resources/bedrock/runtime_item_states.1_19_70.json @@ -0,0 +1,4786 @@ +[ + { + "name" : "minecraft:acacia_boat", + "id" : 380 + }, + { + "name" : "minecraft:acacia_button", + "id" : -140 + }, + { + "name" : "minecraft:acacia_chest_boat", + "id" : 648 + }, + { + "name" : "minecraft:acacia_door", + "id" : 562 + }, + { + "name" : "minecraft:acacia_fence_gate", + "id" : 187 + }, + { + "name" : "minecraft:acacia_hanging_sign", + "id" : -504 + }, + { + "name" : "minecraft:acacia_pressure_plate", + "id" : -150 + }, + { + "name" : "minecraft:acacia_sign", + "id" : 585 + }, + { + "name" : "minecraft:acacia_stairs", + "id" : 163 + }, + { + "name" : "minecraft:acacia_standing_sign", + "id" : -190 + }, + { + "name" : "minecraft:acacia_trapdoor", + "id" : -145 + }, + { + "name" : "minecraft:acacia_wall_sign", + "id" : -191 + }, + { + "name" : "minecraft:activator_rail", + "id" : 126 + }, + { + "name" : "minecraft:agent_spawn_egg", + "id" : 488 + }, + { + "name" : "minecraft:air", + "id" : -158 + }, + { + "name" : "minecraft:allay_spawn_egg", + "id" : 637 + }, + { + "name" : "minecraft:allow", + "id" : 210 + }, + { + "name" : "minecraft:amethyst_block", + "id" : -327 + }, + { + "name" : "minecraft:amethyst_cluster", + "id" : -329 + }, + { + "name" : "minecraft:amethyst_shard", + "id" : 630 + }, + { + "name" : "minecraft:ancient_debris", + "id" : -271 + }, + { + "name" : "minecraft:andesite_stairs", + "id" : -171 + }, + { + "name" : "minecraft:anvil", + "id" : 145 + }, + { + "name" : "minecraft:apple", + "id" : 257 + }, + { + "name" : "minecraft:archer_pottery_shard", + "id" : 659 + }, + { + "name" : "minecraft:armor_stand", + "id" : 558 + }, + { + "name" : "minecraft:arms_up_pottery_shard", + "id" : 660 + }, + { + "name" : "minecraft:arrow", + "id" : 302 + }, + { + "name" : "minecraft:axolotl_bucket", + "id" : 370 + }, + { + "name" : "minecraft:axolotl_spawn_egg", + "id" : 502 + }, + { + "name" : "minecraft:azalea", + "id" : -337 + }, + { + "name" : "minecraft:azalea_leaves", + "id" : -324 + }, + { + "name" : "minecraft:azalea_leaves_flowered", + "id" : -325 + }, + { + "name" : "minecraft:baked_potato", + "id" : 281 + }, + { + "name" : "minecraft:balloon", + "id" : 604 + }, + { + "name" : "minecraft:bamboo", + "id" : -163 + }, + { + "name" : "minecraft:bamboo_block", + "id" : -527 + }, + { + "name" : "minecraft:bamboo_button", + "id" : -511 + }, + { + "name" : "minecraft:bamboo_chest_raft", + "id" : 657 + }, + { + "name" : "minecraft:bamboo_door", + "id" : -517 + }, + { + "name" : "minecraft:bamboo_double_slab", + "id" : -521 + }, + { + "name" : "minecraft:bamboo_fence", + "id" : -515 + }, + { + "name" : "minecraft:bamboo_fence_gate", + "id" : -516 + }, + { + "name" : "minecraft:bamboo_hanging_sign", + "id" : -522 + }, + { + "name" : "minecraft:bamboo_mosaic", + "id" : -509 + }, + { + "name" : "minecraft:bamboo_mosaic_double_slab", + "id" : -525 + }, + { + "name" : "minecraft:bamboo_mosaic_slab", + "id" : -524 + }, + { + "name" : "minecraft:bamboo_mosaic_stairs", + "id" : -523 + }, + { + "name" : "minecraft:bamboo_planks", + "id" : -510 + }, + { + "name" : "minecraft:bamboo_pressure_plate", + "id" : -514 + }, + { + "name" : "minecraft:bamboo_raft", + "id" : 656 + }, + { + "name" : "minecraft:bamboo_sapling", + "id" : -164 + }, + { + "name" : "minecraft:bamboo_sign", + "id" : 655 + }, + { + "name" : "minecraft:bamboo_slab", + "id" : -513 + }, + { + "name" : "minecraft:bamboo_stairs", + "id" : -512 + }, + { + "name" : "minecraft:bamboo_standing_sign", + "id" : -518 + }, + { + "name" : "minecraft:bamboo_trapdoor", + "id" : -520 + }, + { + "name" : "minecraft:bamboo_wall_sign", + "id" : -519 + }, + { + "name" : "minecraft:banner", + "id" : 573 + }, + { + "name" : "minecraft:banner_pattern", + "id" : 667 + }, + { + "name" : "minecraft:barrel", + "id" : -203 + }, + { + "name" : "minecraft:barrier", + "id" : -161 + }, + { + "name" : "minecraft:basalt", + "id" : -234 + }, + { + "name" : "minecraft:bat_spawn_egg", + "id" : 454 + }, + { + "name" : "minecraft:beacon", + "id" : 138 + }, + { + "name" : "minecraft:bed", + "id" : 419 + }, + { + "name" : "minecraft:bedrock", + "id" : 7 + }, + { + "name" : "minecraft:bee_nest", + "id" : -218 + }, + { + "name" : "minecraft:bee_spawn_egg", + "id" : 495 + }, + { + "name" : "minecraft:beef", + "id" : 273 + }, + { + "name" : "minecraft:beehive", + "id" : -219 + }, + { + "name" : "minecraft:beetroot", + "id" : 285 + }, + { + "name" : "minecraft:beetroot_seeds", + "id" : 295 + }, + { + "name" : "minecraft:beetroot_soup", + "id" : 286 + }, + { + "name" : "minecraft:bell", + "id" : -206 + }, + { + "name" : "minecraft:big_dripleaf", + "id" : -323 + }, + { + "name" : "minecraft:birch_boat", + "id" : 377 + }, + { + "name" : "minecraft:birch_button", + "id" : -141 + }, + { + "name" : "minecraft:birch_chest_boat", + "id" : 645 + }, + { + "name" : "minecraft:birch_door", + "id" : 560 + }, + { + "name" : "minecraft:birch_fence_gate", + "id" : 184 + }, + { + "name" : "minecraft:birch_hanging_sign", + "id" : -502 + }, + { + "name" : "minecraft:birch_pressure_plate", + "id" : -151 + }, + { + "name" : "minecraft:birch_sign", + "id" : 583 + }, + { + "name" : "minecraft:birch_stairs", + "id" : 135 + }, + { + "name" : "minecraft:birch_standing_sign", + "id" : -186 + }, + { + "name" : "minecraft:birch_trapdoor", + "id" : -146 + }, + { + "name" : "minecraft:birch_wall_sign", + "id" : -187 + }, + { + "name" : "minecraft:black_candle", + "id" : -428 + }, + { + "name" : "minecraft:black_candle_cake", + "id" : -445 + }, + { + "name" : "minecraft:black_dye", + "id" : 396 + }, + { + "name" : "minecraft:black_glazed_terracotta", + "id" : 235 + }, + { + "name" : "minecraft:black_wool", + "id" : -554 + }, + { + "name" : "minecraft:blackstone", + "id" : -273 + }, + { + "name" : "minecraft:blackstone_double_slab", + "id" : -283 + }, + { + "name" : "minecraft:blackstone_slab", + "id" : -282 + }, + { + "name" : "minecraft:blackstone_stairs", + "id" : -276 + }, + { + "name" : "minecraft:blackstone_wall", + "id" : -277 + }, + { + "name" : "minecraft:blast_furnace", + "id" : -196 + }, + { + "name" : "minecraft:blaze_powder", + "id" : 430 + }, + { + "name" : "minecraft:blaze_rod", + "id" : 424 + }, + { + "name" : "minecraft:blaze_spawn_egg", + "id" : 457 + }, + { + "name" : "minecraft:bleach", + "id" : 602 + }, + { + "name" : "minecraft:blue_candle", + "id" : -424 + }, + { + "name" : "minecraft:blue_candle_cake", + "id" : -441 + }, + { + "name" : "minecraft:blue_dye", + "id" : 400 + }, + { + "name" : "minecraft:blue_glazed_terracotta", + "id" : 231 + }, + { + "name" : "minecraft:blue_ice", + "id" : -11 + }, + { + "name" : "minecraft:blue_wool", + "id" : -563 + }, + { + "name" : "minecraft:boat", + "id" : 665 + }, + { + "name" : "minecraft:bone", + "id" : 416 + }, + { + "name" : "minecraft:bone_block", + "id" : 216 + }, + { + "name" : "minecraft:bone_meal", + "id" : 412 + }, + { + "name" : "minecraft:book", + "id" : 388 + }, + { + "name" : "minecraft:bookshelf", + "id" : 47 + }, + { + "name" : "minecraft:border_block", + "id" : 212 + }, + { + "name" : "minecraft:bordure_indented_banner_pattern", + "id" : 592 + }, + { + "name" : "minecraft:bow", + "id" : 301 + }, + { + "name" : "minecraft:bowl", + "id" : 322 + }, + { + "name" : "minecraft:bread", + "id" : 261 + }, + { + "name" : "minecraft:brewing_stand", + "id" : 432 + }, + { + "name" : "minecraft:brick", + "id" : 384 + }, + { + "name" : "minecraft:brick_block", + "id" : 45 + }, + { + "name" : "minecraft:brick_stairs", + "id" : 108 + }, + { + "name" : "minecraft:brown_candle", + "id" : -425 + }, + { + "name" : "minecraft:brown_candle_cake", + "id" : -442 + }, + { + "name" : "minecraft:brown_dye", + "id" : 399 + }, + { + "name" : "minecraft:brown_glazed_terracotta", + "id" : 232 + }, + { + "name" : "minecraft:brown_mushroom", + "id" : 39 + }, + { + "name" : "minecraft:brown_mushroom_block", + "id" : 99 + }, + { + "name" : "minecraft:brown_wool", + "id" : -555 + }, + { + "name" : "minecraft:brush", + "id" : 663 + }, + { + "name" : "minecraft:bubble_column", + "id" : -160 + }, + { + "name" : "minecraft:bucket", + "id" : 361 + }, + { + "name" : "minecraft:budding_amethyst", + "id" : -328 + }, + { + "name" : "minecraft:cactus", + "id" : 81 + }, + { + "name" : "minecraft:cake", + "id" : 418 + }, + { + "name" : "minecraft:calcite", + "id" : -326 + }, + { + "name" : "minecraft:camel_spawn_egg", + "id" : 658 + }, + { + "name" : "minecraft:camera", + "id" : 599 + }, + { + "name" : "minecraft:campfire", + "id" : 595 + }, + { + "name" : "minecraft:candle", + "id" : -412 + }, + { + "name" : "minecraft:candle_cake", + "id" : -429 + }, + { + "name" : "minecraft:carpet", + "id" : 171 + }, + { + "name" : "minecraft:carrot", + "id" : 279 + }, + { + "name" : "minecraft:carrot_on_a_stick", + "id" : 523 + }, + { + "name" : "minecraft:carrots", + "id" : 141 + }, + { + "name" : "minecraft:cartography_table", + "id" : -200 + }, + { + "name" : "minecraft:carved_pumpkin", + "id" : -155 + }, + { + "name" : "minecraft:cat_spawn_egg", + "id" : 489 + }, + { + "name" : "minecraft:cauldron", + "id" : 433 + }, + { + "name" : "minecraft:cave_spider_spawn_egg", + "id" : 458 + }, + { + "name" : "minecraft:cave_vines", + "id" : -322 + }, + { + "name" : "minecraft:cave_vines_body_with_berries", + "id" : -375 + }, + { + "name" : "minecraft:cave_vines_head_with_berries", + "id" : -376 + }, + { + "name" : "minecraft:chain", + "id" : 625 + }, + { + "name" : "minecraft:chain_command_block", + "id" : 189 + }, + { + "name" : "minecraft:chainmail_boots", + "id" : 343 + }, + { + "name" : "minecraft:chainmail_chestplate", + "id" : 341 + }, + { + "name" : "minecraft:chainmail_helmet", + "id" : 340 + }, + { + "name" : "minecraft:chainmail_leggings", + "id" : 342 + }, + { + "name" : "minecraft:charcoal", + "id" : 304 + }, + { + "name" : "minecraft:chemical_heat", + "id" : 192 + }, + { + "name" : "minecraft:chemistry_table", + "id" : 238 + }, + { + "name" : "minecraft:chest", + "id" : 54 + }, + { + "name" : "minecraft:chest_boat", + "id" : 651 + }, + { + "name" : "minecraft:chest_minecart", + "id" : 390 + }, + { + "name" : "minecraft:chicken", + "id" : 275 + }, + { + "name" : "minecraft:chicken_spawn_egg", + "id" : 436 + }, + { + "name" : "minecraft:chiseled_bookshelf", + "id" : -526 + }, + { + "name" : "minecraft:chiseled_deepslate", + "id" : -395 + }, + { + "name" : "minecraft:chiseled_nether_bricks", + "id" : -302 + }, + { + "name" : "minecraft:chiseled_polished_blackstone", + "id" : -279 + }, + { + "name" : "minecraft:chorus_flower", + "id" : 200 + }, + { + "name" : "minecraft:chorus_fruit", + "id" : 564 + }, + { + "name" : "minecraft:chorus_plant", + "id" : 240 + }, + { + "name" : "minecraft:clay", + "id" : 82 + }, + { + "name" : "minecraft:clay_ball", + "id" : 385 + }, + { + "name" : "minecraft:client_request_placeholder_block", + "id" : -465 + }, + { + "name" : "minecraft:clock", + "id" : 394 + }, + { + "name" : "minecraft:coal", + "id" : 303 + }, + { + "name" : "minecraft:coal_block", + "id" : 173 + }, + { + "name" : "minecraft:coal_ore", + "id" : 16 + }, + { + "name" : "minecraft:cobbled_deepslate", + "id" : -379 + }, + { + "name" : "minecraft:cobbled_deepslate_double_slab", + "id" : -396 + }, + { + "name" : "minecraft:cobbled_deepslate_slab", + "id" : -380 + }, + { + "name" : "minecraft:cobbled_deepslate_stairs", + "id" : -381 + }, + { + "name" : "minecraft:cobbled_deepslate_wall", + "id" : -382 + }, + { + "name" : "minecraft:cobblestone", + "id" : 4 + }, + { + "name" : "minecraft:cobblestone_wall", + "id" : 139 + }, + { + "name" : "minecraft:cocoa", + "id" : 127 + }, + { + "name" : "minecraft:cocoa_beans", + "id" : 413 + }, + { + "name" : "minecraft:cod", + "id" : 264 + }, + { + "name" : "minecraft:cod_bucket", + "id" : 365 + }, + { + "name" : "minecraft:cod_spawn_egg", + "id" : 481 + }, + { + "name" : "minecraft:colored_torch_bp", + "id" : 204 + }, + { + "name" : "minecraft:colored_torch_rg", + "id" : 202 + }, + { + "name" : "minecraft:command_block", + "id" : 137 + }, + { + "name" : "minecraft:command_block_minecart", + "id" : 569 + }, + { + "name" : "minecraft:comparator", + "id" : 528 + }, + { + "name" : "minecraft:compass", + "id" : 392 + }, + { + "name" : "minecraft:composter", + "id" : -213 + }, + { + "name" : "minecraft:compound", + "id" : 600 + }, + { + "name" : "minecraft:concrete", + "id" : 236 + }, + { + "name" : "minecraft:concrete_powder", + "id" : 237 + }, + { + "name" : "minecraft:conduit", + "id" : -157 + }, + { + "name" : "minecraft:cooked_beef", + "id" : 274 + }, + { + "name" : "minecraft:cooked_chicken", + "id" : 276 + }, + { + "name" : "minecraft:cooked_cod", + "id" : 268 + }, + { + "name" : "minecraft:cooked_mutton", + "id" : 557 + }, + { + "name" : "minecraft:cooked_porkchop", + "id" : 263 + }, + { + "name" : "minecraft:cooked_rabbit", + "id" : 289 + }, + { + "name" : "minecraft:cooked_salmon", + "id" : 269 + }, + { + "name" : "minecraft:cookie", + "id" : 271 + }, + { + "name" : "minecraft:copper_block", + "id" : -340 + }, + { + "name" : "minecraft:copper_ingot", + "id" : 510 + }, + { + "name" : "minecraft:copper_ore", + "id" : -311 + }, + { + "name" : "minecraft:coral", + "id" : -131 + }, + { + "name" : "minecraft:coral_block", + "id" : -132 + }, + { + "name" : "minecraft:coral_fan", + "id" : -133 + }, + { + "name" : "minecraft:coral_fan_dead", + "id" : -134 + }, + { + "name" : "minecraft:coral_fan_hang", + "id" : -135 + }, + { + "name" : "minecraft:coral_fan_hang2", + "id" : -136 + }, + { + "name" : "minecraft:coral_fan_hang3", + "id" : -137 + }, + { + "name" : "minecraft:cow_spawn_egg", + "id" : 437 + }, + { + "name" : "minecraft:cracked_deepslate_bricks", + "id" : -410 + }, + { + "name" : "minecraft:cracked_deepslate_tiles", + "id" : -409 + }, + { + "name" : "minecraft:cracked_nether_bricks", + "id" : -303 + }, + { + "name" : "minecraft:cracked_polished_blackstone_bricks", + "id" : -280 + }, + { + "name" : "minecraft:crafting_table", + "id" : 58 + }, + { + "name" : "minecraft:creeper_banner_pattern", + "id" : 588 + }, + { + "name" : "minecraft:creeper_spawn_egg", + "id" : 442 + }, + { + "name" : "minecraft:crimson_button", + "id" : -260 + }, + { + "name" : "minecraft:crimson_door", + "id" : 622 + }, + { + "name" : "minecraft:crimson_double_slab", + "id" : -266 + }, + { + "name" : "minecraft:crimson_fence", + "id" : -256 + }, + { + "name" : "minecraft:crimson_fence_gate", + "id" : -258 + }, + { + "name" : "minecraft:crimson_fungus", + "id" : -228 + }, + { + "name" : "minecraft:crimson_hanging_sign", + "id" : -506 + }, + { + "name" : "minecraft:crimson_hyphae", + "id" : -299 + }, + { + "name" : "minecraft:crimson_nylium", + "id" : -232 + }, + { + "name" : "minecraft:crimson_planks", + "id" : -242 + }, + { + "name" : "minecraft:crimson_pressure_plate", + "id" : -262 + }, + { + "name" : "minecraft:crimson_roots", + "id" : -223 + }, + { + "name" : "minecraft:crimson_sign", + "id" : 620 + }, + { + "name" : "minecraft:crimson_slab", + "id" : -264 + }, + { + "name" : "minecraft:crimson_stairs", + "id" : -254 + }, + { + "name" : "minecraft:crimson_standing_sign", + "id" : -250 + }, + { + "name" : "minecraft:crimson_stem", + "id" : -225 + }, + { + "name" : "minecraft:crimson_trapdoor", + "id" : -246 + }, + { + "name" : "minecraft:crimson_wall_sign", + "id" : -252 + }, + { + "name" : "minecraft:crossbow", + "id" : 581 + }, + { + "name" : "minecraft:crying_obsidian", + "id" : -289 + }, + { + "name" : "minecraft:cut_copper", + "id" : -347 + }, + { + "name" : "minecraft:cut_copper_slab", + "id" : -361 + }, + { + "name" : "minecraft:cut_copper_stairs", + "id" : -354 + }, + { + "name" : "minecraft:cyan_candle", + "id" : -422 + }, + { + "name" : "minecraft:cyan_candle_cake", + "id" : -439 + }, + { + "name" : "minecraft:cyan_dye", + "id" : 402 + }, + { + "name" : "minecraft:cyan_glazed_terracotta", + "id" : 229 + }, + { + "name" : "minecraft:cyan_wool", + "id" : -561 + }, + { + "name" : "minecraft:dark_oak_boat", + "id" : 381 + }, + { + "name" : "minecraft:dark_oak_button", + "id" : -142 + }, + { + "name" : "minecraft:dark_oak_chest_boat", + "id" : 649 + }, + { + "name" : "minecraft:dark_oak_door", + "id" : 563 + }, + { + "name" : "minecraft:dark_oak_fence_gate", + "id" : 186 + }, + { + "name" : "minecraft:dark_oak_hanging_sign", + "id" : -505 + }, + { + "name" : "minecraft:dark_oak_pressure_plate", + "id" : -152 + }, + { + "name" : "minecraft:dark_oak_sign", + "id" : 586 + }, + { + "name" : "minecraft:dark_oak_stairs", + "id" : 164 + }, + { + "name" : "minecraft:dark_oak_trapdoor", + "id" : -147 + }, + { + "name" : "minecraft:dark_prismarine_stairs", + "id" : -3 + }, + { + "name" : "minecraft:darkoak_standing_sign", + "id" : -192 + }, + { + "name" : "minecraft:darkoak_wall_sign", + "id" : -193 + }, + { + "name" : "minecraft:daylight_detector", + "id" : 151 + }, + { + "name" : "minecraft:daylight_detector_inverted", + "id" : 178 + }, + { + "name" : "minecraft:deadbush", + "id" : 32 + }, + { + "name" : "minecraft:decorated_pot", + "id" : -551 + }, + { + "name" : "minecraft:deepslate", + "id" : -378 + }, + { + "name" : "minecraft:deepslate_brick_double_slab", + "id" : -399 + }, + { + "name" : "minecraft:deepslate_brick_slab", + "id" : -392 + }, + { + "name" : "minecraft:deepslate_brick_stairs", + "id" : -393 + }, + { + "name" : "minecraft:deepslate_brick_wall", + "id" : -394 + }, + { + "name" : "minecraft:deepslate_bricks", + "id" : -391 + }, + { + "name" : "minecraft:deepslate_coal_ore", + "id" : -406 + }, + { + "name" : "minecraft:deepslate_copper_ore", + "id" : -408 + }, + { + "name" : "minecraft:deepslate_diamond_ore", + "id" : -405 + }, + { + "name" : "minecraft:deepslate_emerald_ore", + "id" : -407 + }, + { + "name" : "minecraft:deepslate_gold_ore", + "id" : -402 + }, + { + "name" : "minecraft:deepslate_iron_ore", + "id" : -401 + }, + { + "name" : "minecraft:deepslate_lapis_ore", + "id" : -400 + }, + { + "name" : "minecraft:deepslate_redstone_ore", + "id" : -403 + }, + { + "name" : "minecraft:deepslate_tile_double_slab", + "id" : -398 + }, + { + "name" : "minecraft:deepslate_tile_slab", + "id" : -388 + }, + { + "name" : "minecraft:deepslate_tile_stairs", + "id" : -389 + }, + { + "name" : "minecraft:deepslate_tile_wall", + "id" : -390 + }, + { + "name" : "minecraft:deepslate_tiles", + "id" : -387 + }, + { + "name" : "minecraft:deny", + "id" : 211 + }, + { + "name" : "minecraft:detector_rail", + "id" : 28 + }, + { + "name" : "minecraft:diamond", + "id" : 305 + }, + { + "name" : "minecraft:diamond_axe", + "id" : 320 + }, + { + "name" : "minecraft:diamond_block", + "id" : 57 + }, + { + "name" : "minecraft:diamond_boots", + "id" : 351 + }, + { + "name" : "minecraft:diamond_chestplate", + "id" : 349 + }, + { + "name" : "minecraft:diamond_helmet", + "id" : 348 + }, + { + "name" : "minecraft:diamond_hoe", + "id" : 333 + }, + { + "name" : "minecraft:diamond_horse_armor", + "id" : 539 + }, + { + "name" : "minecraft:diamond_leggings", + "id" : 350 + }, + { + "name" : "minecraft:diamond_ore", + "id" : 56 + }, + { + "name" : "minecraft:diamond_pickaxe", + "id" : 319 + }, + { + "name" : "minecraft:diamond_shovel", + "id" : 318 + }, + { + "name" : "minecraft:diamond_sword", + "id" : 317 + }, + { + "name" : "minecraft:diorite_stairs", + "id" : -170 + }, + { + "name" : "minecraft:dirt", + "id" : 3 + }, + { + "name" : "minecraft:dirt_with_roots", + "id" : -318 + }, + { + "name" : "minecraft:disc_fragment_5", + "id" : 643 + }, + { + "name" : "minecraft:dispenser", + "id" : 23 + }, + { + "name" : "minecraft:dolphin_spawn_egg", + "id" : 485 + }, + { + "name" : "minecraft:donkey_spawn_egg", + "id" : 466 + }, + { + "name" : "minecraft:double_cut_copper_slab", + "id" : -368 + }, + { + "name" : "minecraft:double_plant", + "id" : 175 + }, + { + "name" : "minecraft:double_stone_block_slab", + "id" : 43 + }, + { + "name" : "minecraft:double_stone_block_slab2", + "id" : 181 + }, + { + "name" : "minecraft:double_stone_block_slab3", + "id" : -167 + }, + { + "name" : "minecraft:double_stone_block_slab4", + "id" : -168 + }, + { + "name" : "minecraft:double_wooden_slab", + "id" : 157 + }, + { + "name" : "minecraft:dragon_breath", + "id" : 566 + }, + { + "name" : "minecraft:dragon_egg", + "id" : 122 + }, + { + "name" : "minecraft:dried_kelp", + "id" : 270 + }, + { + "name" : "minecraft:dried_kelp_block", + "id" : -139 + }, + { + "name" : "minecraft:dripstone_block", + "id" : -317 + }, + { + "name" : "minecraft:dropper", + "id" : 125 + }, + { + "name" : "minecraft:drowned_spawn_egg", + "id" : 484 + }, + { + "name" : "minecraft:dye", + "id" : 666 + }, + { + "name" : "minecraft:echo_shard", + "id" : 653 + }, + { + "name" : "minecraft:egg", + "id" : 391 + }, + { + "name" : "minecraft:elder_guardian_spawn_egg", + "id" : 472 + }, + { + "name" : "minecraft:element_0", + "id" : 36 + }, + { + "name" : "minecraft:element_1", + "id" : -12 + }, + { + "name" : "minecraft:element_10", + "id" : -21 + }, + { + "name" : "minecraft:element_100", + "id" : -111 + }, + { + "name" : "minecraft:element_101", + "id" : -112 + }, + { + "name" : "minecraft:element_102", + "id" : -113 + }, + { + "name" : "minecraft:element_103", + "id" : -114 + }, + { + "name" : "minecraft:element_104", + "id" : -115 + }, + { + "name" : "minecraft:element_105", + "id" : -116 + }, + { + "name" : "minecraft:element_106", + "id" : -117 + }, + { + "name" : "minecraft:element_107", + "id" : -118 + }, + { + "name" : "minecraft:element_108", + "id" : -119 + }, + { + "name" : "minecraft:element_109", + "id" : -120 + }, + { + "name" : "minecraft:element_11", + "id" : -22 + }, + { + "name" : "minecraft:element_110", + "id" : -121 + }, + { + "name" : "minecraft:element_111", + "id" : -122 + }, + { + "name" : "minecraft:element_112", + "id" : -123 + }, + { + "name" : "minecraft:element_113", + "id" : -124 + }, + { + "name" : "minecraft:element_114", + "id" : -125 + }, + { + "name" : "minecraft:element_115", + "id" : -126 + }, + { + "name" : "minecraft:element_116", + "id" : -127 + }, + { + "name" : "minecraft:element_117", + "id" : -128 + }, + { + "name" : "minecraft:element_118", + "id" : -129 + }, + { + "name" : "minecraft:element_12", + "id" : -23 + }, + { + "name" : "minecraft:element_13", + "id" : -24 + }, + { + "name" : "minecraft:element_14", + "id" : -25 + }, + { + "name" : "minecraft:element_15", + "id" : -26 + }, + { + "name" : "minecraft:element_16", + "id" : -27 + }, + { + "name" : "minecraft:element_17", + "id" : -28 + }, + { + "name" : "minecraft:element_18", + "id" : -29 + }, + { + "name" : "minecraft:element_19", + "id" : -30 + }, + { + "name" : "minecraft:element_2", + "id" : -13 + }, + { + "name" : "minecraft:element_20", + "id" : -31 + }, + { + "name" : "minecraft:element_21", + "id" : -32 + }, + { + "name" : "minecraft:element_22", + "id" : -33 + }, + { + "name" : "minecraft:element_23", + "id" : -34 + }, + { + "name" : "minecraft:element_24", + "id" : -35 + }, + { + "name" : "minecraft:element_25", + "id" : -36 + }, + { + "name" : "minecraft:element_26", + "id" : -37 + }, + { + "name" : "minecraft:element_27", + "id" : -38 + }, + { + "name" : "minecraft:element_28", + "id" : -39 + }, + { + "name" : "minecraft:element_29", + "id" : -40 + }, + { + "name" : "minecraft:element_3", + "id" : -14 + }, + { + "name" : "minecraft:element_30", + "id" : -41 + }, + { + "name" : "minecraft:element_31", + "id" : -42 + }, + { + "name" : "minecraft:element_32", + "id" : -43 + }, + { + "name" : "minecraft:element_33", + "id" : -44 + }, + { + "name" : "minecraft:element_34", + "id" : -45 + }, + { + "name" : "minecraft:element_35", + "id" : -46 + }, + { + "name" : "minecraft:element_36", + "id" : -47 + }, + { + "name" : "minecraft:element_37", + "id" : -48 + }, + { + "name" : "minecraft:element_38", + "id" : -49 + }, + { + "name" : "minecraft:element_39", + "id" : -50 + }, + { + "name" : "minecraft:element_4", + "id" : -15 + }, + { + "name" : "minecraft:element_40", + "id" : -51 + }, + { + "name" : "minecraft:element_41", + "id" : -52 + }, + { + "name" : "minecraft:element_42", + "id" : -53 + }, + { + "name" : "minecraft:element_43", + "id" : -54 + }, + { + "name" : "minecraft:element_44", + "id" : -55 + }, + { + "name" : "minecraft:element_45", + "id" : -56 + }, + { + "name" : "minecraft:element_46", + "id" : -57 + }, + { + "name" : "minecraft:element_47", + "id" : -58 + }, + { + "name" : "minecraft:element_48", + "id" : -59 + }, + { + "name" : "minecraft:element_49", + "id" : -60 + }, + { + "name" : "minecraft:element_5", + "id" : -16 + }, + { + "name" : "minecraft:element_50", + "id" : -61 + }, + { + "name" : "minecraft:element_51", + "id" : -62 + }, + { + "name" : "minecraft:element_52", + "id" : -63 + }, + { + "name" : "minecraft:element_53", + "id" : -64 + }, + { + "name" : "minecraft:element_54", + "id" : -65 + }, + { + "name" : "minecraft:element_55", + "id" : -66 + }, + { + "name" : "minecraft:element_56", + "id" : -67 + }, + { + "name" : "minecraft:element_57", + "id" : -68 + }, + { + "name" : "minecraft:element_58", + "id" : -69 + }, + { + "name" : "minecraft:element_59", + "id" : -70 + }, + { + "name" : "minecraft:element_6", + "id" : -17 + }, + { + "name" : "minecraft:element_60", + "id" : -71 + }, + { + "name" : "minecraft:element_61", + "id" : -72 + }, + { + "name" : "minecraft:element_62", + "id" : -73 + }, + { + "name" : "minecraft:element_63", + "id" : -74 + }, + { + "name" : "minecraft:element_64", + "id" : -75 + }, + { + "name" : "minecraft:element_65", + "id" : -76 + }, + { + "name" : "minecraft:element_66", + "id" : -77 + }, + { + "name" : "minecraft:element_67", + "id" : -78 + }, + { + "name" : "minecraft:element_68", + "id" : -79 + }, + { + "name" : "minecraft:element_69", + "id" : -80 + }, + { + "name" : "minecraft:element_7", + "id" : -18 + }, + { + "name" : "minecraft:element_70", + "id" : -81 + }, + { + "name" : "minecraft:element_71", + "id" : -82 + }, + { + "name" : "minecraft:element_72", + "id" : -83 + }, + { + "name" : "minecraft:element_73", + "id" : -84 + }, + { + "name" : "minecraft:element_74", + "id" : -85 + }, + { + "name" : "minecraft:element_75", + "id" : -86 + }, + { + "name" : "minecraft:element_76", + "id" : -87 + }, + { + "name" : "minecraft:element_77", + "id" : -88 + }, + { + "name" : "minecraft:element_78", + "id" : -89 + }, + { + "name" : "minecraft:element_79", + "id" : -90 + }, + { + "name" : "minecraft:element_8", + "id" : -19 + }, + { + "name" : "minecraft:element_80", + "id" : -91 + }, + { + "name" : "minecraft:element_81", + "id" : -92 + }, + { + "name" : "minecraft:element_82", + "id" : -93 + }, + { + "name" : "minecraft:element_83", + "id" : -94 + }, + { + "name" : "minecraft:element_84", + "id" : -95 + }, + { + "name" : "minecraft:element_85", + "id" : -96 + }, + { + "name" : "minecraft:element_86", + "id" : -97 + }, + { + "name" : "minecraft:element_87", + "id" : -98 + }, + { + "name" : "minecraft:element_88", + "id" : -99 + }, + { + "name" : "minecraft:element_89", + "id" : -100 + }, + { + "name" : "minecraft:element_9", + "id" : -20 + }, + { + "name" : "minecraft:element_90", + "id" : -101 + }, + { + "name" : "minecraft:element_91", + "id" : -102 + }, + { + "name" : "minecraft:element_92", + "id" : -103 + }, + { + "name" : "minecraft:element_93", + "id" : -104 + }, + { + "name" : "minecraft:element_94", + "id" : -105 + }, + { + "name" : "minecraft:element_95", + "id" : -106 + }, + { + "name" : "minecraft:element_96", + "id" : -107 + }, + { + "name" : "minecraft:element_97", + "id" : -108 + }, + { + "name" : "minecraft:element_98", + "id" : -109 + }, + { + "name" : "minecraft:element_99", + "id" : -110 + }, + { + "name" : "minecraft:elytra", + "id" : 570 + }, + { + "name" : "minecraft:emerald", + "id" : 518 + }, + { + "name" : "minecraft:emerald_block", + "id" : 133 + }, + { + "name" : "minecraft:emerald_ore", + "id" : 129 + }, + { + "name" : "minecraft:empty_map", + "id" : 521 + }, + { + "name" : "minecraft:enchanted_book", + "id" : 527 + }, + { + "name" : "minecraft:enchanted_golden_apple", + "id" : 259 + }, + { + "name" : "minecraft:enchanting_table", + "id" : 116 + }, + { + "name" : "minecraft:end_brick_stairs", + "id" : -178 + }, + { + "name" : "minecraft:end_bricks", + "id" : 206 + }, + { + "name" : "minecraft:end_crystal", + "id" : 669 + }, + { + "name" : "minecraft:end_gateway", + "id" : 209 + }, + { + "name" : "minecraft:end_portal", + "id" : 119 + }, + { + "name" : "minecraft:end_portal_frame", + "id" : 120 + }, + { + "name" : "minecraft:end_rod", + "id" : 208 + }, + { + "name" : "minecraft:end_stone", + "id" : 121 + }, + { + "name" : "minecraft:ender_chest", + "id" : 130 + }, + { + "name" : "minecraft:ender_dragon_spawn_egg", + "id" : 507 + }, + { + "name" : "minecraft:ender_eye", + "id" : 434 + }, + { + "name" : "minecraft:ender_pearl", + "id" : 423 + }, + { + "name" : "minecraft:enderman_spawn_egg", + "id" : 443 + }, + { + "name" : "minecraft:endermite_spawn_egg", + "id" : 461 + }, + { + "name" : "minecraft:evoker_spawn_egg", + "id" : 476 + }, + { + "name" : "minecraft:experience_bottle", + "id" : 514 + }, + { + "name" : "minecraft:exposed_copper", + "id" : -341 + }, + { + "name" : "minecraft:exposed_cut_copper", + "id" : -348 + }, + { + "name" : "minecraft:exposed_cut_copper_slab", + "id" : -362 + }, + { + "name" : "minecraft:exposed_cut_copper_stairs", + "id" : -355 + }, + { + "name" : "minecraft:exposed_double_cut_copper_slab", + "id" : -369 + }, + { + "name" : "minecraft:farmland", + "id" : 60 + }, + { + "name" : "minecraft:feather", + "id" : 328 + }, + { + "name" : "minecraft:fence", + "id" : 85 + }, + { + "name" : "minecraft:fence_gate", + "id" : 107 + }, + { + "name" : "minecraft:fermented_spider_eye", + "id" : 429 + }, + { + "name" : "minecraft:field_masoned_banner_pattern", + "id" : 591 + }, + { + "name" : "minecraft:filled_map", + "id" : 421 + }, + { + "name" : "minecraft:fire", + "id" : 51 + }, + { + "name" : "minecraft:fire_charge", + "id" : 515 + }, + { + "name" : "minecraft:firework_rocket", + "id" : 525 + }, + { + "name" : "minecraft:firework_star", + "id" : 526 + }, + { + "name" : "minecraft:fishing_rod", + "id" : 393 + }, + { + "name" : "minecraft:fletching_table", + "id" : -201 + }, + { + "name" : "minecraft:flint", + "id" : 357 + }, + { + "name" : "minecraft:flint_and_steel", + "id" : 300 + }, + { + "name" : "minecraft:flower_banner_pattern", + "id" : 587 + }, + { + "name" : "minecraft:flower_pot", + "id" : 520 + }, + { + "name" : "minecraft:flowering_azalea", + "id" : -338 + }, + { + "name" : "minecraft:flowing_lava", + "id" : 10 + }, + { + "name" : "minecraft:flowing_water", + "id" : 8 + }, + { + "name" : "minecraft:fox_spawn_egg", + "id" : 491 + }, + { + "name" : "minecraft:frame", + "id" : 519 + }, + { + "name" : "minecraft:frog_spawn", + "id" : -468 + }, + { + "name" : "minecraft:frog_spawn_egg", + "id" : 634 + }, + { + "name" : "minecraft:frosted_ice", + "id" : 207 + }, + { + "name" : "minecraft:furnace", + "id" : 61 + }, + { + "name" : "minecraft:ghast_spawn_egg", + "id" : 455 + }, + { + "name" : "minecraft:ghast_tear", + "id" : 425 + }, + { + "name" : "minecraft:gilded_blackstone", + "id" : -281 + }, + { + "name" : "minecraft:glass", + "id" : 20 + }, + { + "name" : "minecraft:glass_bottle", + "id" : 428 + }, + { + "name" : "minecraft:glass_pane", + "id" : 102 + }, + { + "name" : "minecraft:glistering_melon_slice", + "id" : 435 + }, + { + "name" : "minecraft:globe_banner_pattern", + "id" : 594 + }, + { + "name" : "minecraft:glow_berries", + "id" : 670 + }, + { + "name" : "minecraft:glow_frame", + "id" : 629 + }, + { + "name" : "minecraft:glow_ink_sac", + "id" : 509 + }, + { + "name" : "minecraft:glow_lichen", + "id" : -411 + }, + { + "name" : "minecraft:glow_squid_spawn_egg", + "id" : 504 + }, + { + "name" : "minecraft:glow_stick", + "id" : 607 + }, + { + "name" : "minecraft:glowingobsidian", + "id" : 246 + }, + { + "name" : "minecraft:glowstone", + "id" : 89 + }, + { + "name" : "minecraft:glowstone_dust", + "id" : 395 + }, + { + "name" : "minecraft:goat_horn", + "id" : 633 + }, + { + "name" : "minecraft:goat_spawn_egg", + "id" : 503 + }, + { + "name" : "minecraft:gold_block", + "id" : 41 + }, + { + "name" : "minecraft:gold_ingot", + "id" : 307 + }, + { + "name" : "minecraft:gold_nugget", + "id" : 426 + }, + { + "name" : "minecraft:gold_ore", + "id" : 14 + }, + { + "name" : "minecraft:golden_apple", + "id" : 258 + }, + { + "name" : "minecraft:golden_axe", + "id" : 326 + }, + { + "name" : "minecraft:golden_boots", + "id" : 355 + }, + { + "name" : "minecraft:golden_carrot", + "id" : 283 + }, + { + "name" : "minecraft:golden_chestplate", + "id" : 353 + }, + { + "name" : "minecraft:golden_helmet", + "id" : 352 + }, + { + "name" : "minecraft:golden_hoe", + "id" : 334 + }, + { + "name" : "minecraft:golden_horse_armor", + "id" : 538 + }, + { + "name" : "minecraft:golden_leggings", + "id" : 354 + }, + { + "name" : "minecraft:golden_pickaxe", + "id" : 325 + }, + { + "name" : "minecraft:golden_rail", + "id" : 27 + }, + { + "name" : "minecraft:golden_shovel", + "id" : 324 + }, + { + "name" : "minecraft:golden_sword", + "id" : 323 + }, + { + "name" : "minecraft:granite_stairs", + "id" : -169 + }, + { + "name" : "minecraft:grass", + "id" : 2 + }, + { + "name" : "minecraft:grass_path", + "id" : 198 + }, + { + "name" : "minecraft:gravel", + "id" : 13 + }, + { + "name" : "minecraft:gray_candle", + "id" : -420 + }, + { + "name" : "minecraft:gray_candle_cake", + "id" : -437 + }, + { + "name" : "minecraft:gray_dye", + "id" : 404 + }, + { + "name" : "minecraft:gray_glazed_terracotta", + "id" : 227 + }, + { + "name" : "minecraft:gray_wool", + "id" : -553 + }, + { + "name" : "minecraft:green_candle", + "id" : -426 + }, + { + "name" : "minecraft:green_candle_cake", + "id" : -443 + }, + { + "name" : "minecraft:green_dye", + "id" : 398 + }, + { + "name" : "minecraft:green_glazed_terracotta", + "id" : 233 + }, + { + "name" : "minecraft:green_wool", + "id" : -560 + }, + { + "name" : "minecraft:grindstone", + "id" : -195 + }, + { + "name" : "minecraft:guardian_spawn_egg", + "id" : 462 + }, + { + "name" : "minecraft:gunpowder", + "id" : 329 + }, + { + "name" : "minecraft:hanging_roots", + "id" : -319 + }, + { + "name" : "minecraft:hard_glass", + "id" : 253 + }, + { + "name" : "minecraft:hard_glass_pane", + "id" : 190 + }, + { + "name" : "minecraft:hard_stained_glass", + "id" : 254 + }, + { + "name" : "minecraft:hard_stained_glass_pane", + "id" : 191 + }, + { + "name" : "minecraft:hardened_clay", + "id" : 172 + }, + { + "name" : "minecraft:hay_block", + "id" : 170 + }, + { + "name" : "minecraft:heart_of_the_sea", + "id" : 577 + }, + { + "name" : "minecraft:heavy_weighted_pressure_plate", + "id" : 148 + }, + { + "name" : "minecraft:hoglin_spawn_egg", + "id" : 497 + }, + { + "name" : "minecraft:honey_block", + "id" : -220 + }, + { + "name" : "minecraft:honey_bottle", + "id" : 598 + }, + { + "name" : "minecraft:honeycomb", + "id" : 597 + }, + { + "name" : "minecraft:honeycomb_block", + "id" : -221 + }, + { + "name" : "minecraft:hopper", + "id" : 533 + }, + { + "name" : "minecraft:hopper_minecart", + "id" : 532 + }, + { + "name" : "minecraft:horse_spawn_egg", + "id" : 459 + }, + { + "name" : "minecraft:husk_spawn_egg", + "id" : 464 + }, + { + "name" : "minecraft:ice", + "id" : 79 + }, + { + "name" : "minecraft:ice_bomb", + "id" : 601 + }, + { + "name" : "minecraft:infested_deepslate", + "id" : -454 + }, + { + "name" : "minecraft:info_update", + "id" : 248 + }, + { + "name" : "minecraft:info_update2", + "id" : 249 + }, + { + "name" : "minecraft:ink_sac", + "id" : 414 + }, + { + "name" : "minecraft:invisible_bedrock", + "id" : 95 + }, + { + "name" : "minecraft:iron_axe", + "id" : 299 + }, + { + "name" : "minecraft:iron_bars", + "id" : 101 + }, + { + "name" : "minecraft:iron_block", + "id" : 42 + }, + { + "name" : "minecraft:iron_boots", + "id" : 347 + }, + { + "name" : "minecraft:iron_chestplate", + "id" : 345 + }, + { + "name" : "minecraft:iron_door", + "id" : 373 + }, + { + "name" : "minecraft:iron_golem_spawn_egg", + "id" : 505 + }, + { + "name" : "minecraft:iron_helmet", + "id" : 344 + }, + { + "name" : "minecraft:iron_hoe", + "id" : 332 + }, + { + "name" : "minecraft:iron_horse_armor", + "id" : 537 + }, + { + "name" : "minecraft:iron_ingot", + "id" : 306 + }, + { + "name" : "minecraft:iron_leggings", + "id" : 346 + }, + { + "name" : "minecraft:iron_nugget", + "id" : 575 + }, + { + "name" : "minecraft:iron_ore", + "id" : 15 + }, + { + "name" : "minecraft:iron_pickaxe", + "id" : 298 + }, + { + "name" : "minecraft:iron_shovel", + "id" : 297 + }, + { + "name" : "minecraft:iron_sword", + "id" : 308 + }, + { + "name" : "minecraft:iron_trapdoor", + "id" : 167 + }, + { + "name" : "minecraft:item.acacia_door", + "id" : 196 + }, + { + "name" : "minecraft:item.bed", + "id" : 26 + }, + { + "name" : "minecraft:item.beetroot", + "id" : 244 + }, + { + "name" : "minecraft:item.birch_door", + "id" : 194 + }, + { + "name" : "minecraft:item.brewing_stand", + "id" : 117 + }, + { + "name" : "minecraft:item.cake", + "id" : 92 + }, + { + "name" : "minecraft:item.camera", + "id" : 242 + }, + { + "name" : "minecraft:item.campfire", + "id" : -209 + }, + { + "name" : "minecraft:item.cauldron", + "id" : 118 + }, + { + "name" : "minecraft:item.chain", + "id" : -286 + }, + { + "name" : "minecraft:item.crimson_door", + "id" : -244 + }, + { + "name" : "minecraft:item.dark_oak_door", + "id" : 197 + }, + { + "name" : "minecraft:item.flower_pot", + "id" : 140 + }, + { + "name" : "minecraft:item.frame", + "id" : 199 + }, + { + "name" : "minecraft:item.glow_frame", + "id" : -339 + }, + { + "name" : "minecraft:item.hopper", + "id" : 154 + }, + { + "name" : "minecraft:item.iron_door", + "id" : 71 + }, + { + "name" : "minecraft:item.jungle_door", + "id" : 195 + }, + { + "name" : "minecraft:item.kelp", + "id" : -138 + }, + { + "name" : "minecraft:item.mangrove_door", + "id" : -493 + }, + { + "name" : "minecraft:item.nether_sprouts", + "id" : -238 + }, + { + "name" : "minecraft:item.nether_wart", + "id" : 115 + }, + { + "name" : "minecraft:item.reeds", + "id" : 83 + }, + { + "name" : "minecraft:item.skull", + "id" : 144 + }, + { + "name" : "minecraft:item.soul_campfire", + "id" : -290 + }, + { + "name" : "minecraft:item.spruce_door", + "id" : 193 + }, + { + "name" : "minecraft:item.warped_door", + "id" : -245 + }, + { + "name" : "minecraft:item.wheat", + "id" : 59 + }, + { + "name" : "minecraft:item.wooden_door", + "id" : 64 + }, + { + "name" : "minecraft:jigsaw", + "id" : -211 + }, + { + "name" : "minecraft:jukebox", + "id" : 84 + }, + { + "name" : "minecraft:jungle_boat", + "id" : 378 + }, + { + "name" : "minecraft:jungle_button", + "id" : -143 + }, + { + "name" : "minecraft:jungle_chest_boat", + "id" : 646 + }, + { + "name" : "minecraft:jungle_door", + "id" : 561 + }, + { + "name" : "minecraft:jungle_fence_gate", + "id" : 185 + }, + { + "name" : "minecraft:jungle_hanging_sign", + "id" : -503 + }, + { + "name" : "minecraft:jungle_pressure_plate", + "id" : -153 + }, + { + "name" : "minecraft:jungle_sign", + "id" : 584 + }, + { + "name" : "minecraft:jungle_stairs", + "id" : 136 + }, + { + "name" : "minecraft:jungle_standing_sign", + "id" : -188 + }, + { + "name" : "minecraft:jungle_trapdoor", + "id" : -148 + }, + { + "name" : "minecraft:jungle_wall_sign", + "id" : -189 + }, + { + "name" : "minecraft:kelp", + "id" : 383 + }, + { + "name" : "minecraft:ladder", + "id" : 65 + }, + { + "name" : "minecraft:lantern", + "id" : -208 + }, + { + "name" : "minecraft:lapis_block", + "id" : 22 + }, + { + "name" : "minecraft:lapis_lazuli", + "id" : 415 + }, + { + "name" : "minecraft:lapis_ore", + "id" : 21 + }, + { + "name" : "minecraft:large_amethyst_bud", + "id" : -330 + }, + { + "name" : "minecraft:lava", + "id" : 11 + }, + { + "name" : "minecraft:lava_bucket", + "id" : 364 + }, + { + "name" : "minecraft:lava_cauldron", + "id" : -210 + }, + { + "name" : "minecraft:lead", + "id" : 553 + }, + { + "name" : "minecraft:leather", + "id" : 382 + }, + { + "name" : "minecraft:leather_boots", + "id" : 339 + }, + { + "name" : "minecraft:leather_chestplate", + "id" : 337 + }, + { + "name" : "minecraft:leather_helmet", + "id" : 336 + }, + { + "name" : "minecraft:leather_horse_armor", + "id" : 536 + }, + { + "name" : "minecraft:leather_leggings", + "id" : 338 + }, + { + "name" : "minecraft:leaves", + "id" : 18 + }, + { + "name" : "minecraft:leaves2", + "id" : 161 + }, + { + "name" : "minecraft:lectern", + "id" : -194 + }, + { + "name" : "minecraft:lever", + "id" : 69 + }, + { + "name" : "minecraft:light_block", + "id" : -215 + }, + { + "name" : "minecraft:light_blue_candle", + "id" : -416 + }, + { + "name" : "minecraft:light_blue_candle_cake", + "id" : -433 + }, + { + "name" : "minecraft:light_blue_dye", + "id" : 408 + }, + { + "name" : "minecraft:light_blue_glazed_terracotta", + "id" : 223 + }, + { + "name" : "minecraft:light_blue_wool", + "id" : -562 + }, + { + "name" : "minecraft:light_gray_candle", + "id" : -421 + }, + { + "name" : "minecraft:light_gray_candle_cake", + "id" : -438 + }, + { + "name" : "minecraft:light_gray_dye", + "id" : 403 + }, + { + "name" : "minecraft:light_gray_wool", + "id" : -552 + }, + { + "name" : "minecraft:light_weighted_pressure_plate", + "id" : 147 + }, + { + "name" : "minecraft:lightning_rod", + "id" : -312 + }, + { + "name" : "minecraft:lime_candle", + "id" : -418 + }, + { + "name" : "minecraft:lime_candle_cake", + "id" : -435 + }, + { + "name" : "minecraft:lime_dye", + "id" : 406 + }, + { + "name" : "minecraft:lime_glazed_terracotta", + "id" : 225 + }, + { + "name" : "minecraft:lime_wool", + "id" : -559 + }, + { + "name" : "minecraft:lingering_potion", + "id" : 568 + }, + { + "name" : "minecraft:lit_blast_furnace", + "id" : -214 + }, + { + "name" : "minecraft:lit_deepslate_redstone_ore", + "id" : -404 + }, + { + "name" : "minecraft:lit_furnace", + "id" : 62 + }, + { + "name" : "minecraft:lit_pumpkin", + "id" : 91 + }, + { + "name" : "minecraft:lit_redstone_lamp", + "id" : 124 + }, + { + "name" : "minecraft:lit_redstone_ore", + "id" : 74 + }, + { + "name" : "minecraft:lit_smoker", + "id" : -199 + }, + { + "name" : "minecraft:llama_spawn_egg", + "id" : 474 + }, + { + "name" : "minecraft:lodestone", + "id" : -222 + }, + { + "name" : "minecraft:lodestone_compass", + "id" : 608 + }, + { + "name" : "minecraft:log", + "id" : 17 + }, + { + "name" : "minecraft:log2", + "id" : 162 + }, + { + "name" : "minecraft:loom", + "id" : -204 + }, + { + "name" : "minecraft:magenta_candle", + "id" : -415 + }, + { + "name" : "minecraft:magenta_candle_cake", + "id" : -432 + }, + { + "name" : "minecraft:magenta_dye", + "id" : 409 + }, + { + "name" : "minecraft:magenta_glazed_terracotta", + "id" : 222 + }, + { + "name" : "minecraft:magenta_wool", + "id" : -565 + }, + { + "name" : "minecraft:magma", + "id" : 213 + }, + { + "name" : "minecraft:magma_cream", + "id" : 431 + }, + { + "name" : "minecraft:magma_cube_spawn_egg", + "id" : 456 + }, + { + "name" : "minecraft:mangrove_boat", + "id" : 641 + }, + { + "name" : "minecraft:mangrove_button", + "id" : -487 + }, + { + "name" : "minecraft:mangrove_chest_boat", + "id" : 650 + }, + { + "name" : "minecraft:mangrove_door", + "id" : 639 + }, + { + "name" : "minecraft:mangrove_double_slab", + "id" : -499 + }, + { + "name" : "minecraft:mangrove_fence", + "id" : -491 + }, + { + "name" : "minecraft:mangrove_fence_gate", + "id" : -492 + }, + { + "name" : "minecraft:mangrove_hanging_sign", + "id" : -508 + }, + { + "name" : "minecraft:mangrove_leaves", + "id" : -472 + }, + { + "name" : "minecraft:mangrove_log", + "id" : -484 + }, + { + "name" : "minecraft:mangrove_planks", + "id" : -486 + }, + { + "name" : "minecraft:mangrove_pressure_plate", + "id" : -490 + }, + { + "name" : "minecraft:mangrove_propagule", + "id" : -474 + }, + { + "name" : "minecraft:mangrove_roots", + "id" : -482 + }, + { + "name" : "minecraft:mangrove_sign", + "id" : 640 + }, + { + "name" : "minecraft:mangrove_slab", + "id" : -489 + }, + { + "name" : "minecraft:mangrove_stairs", + "id" : -488 + }, + { + "name" : "minecraft:mangrove_standing_sign", + "id" : -494 + }, + { + "name" : "minecraft:mangrove_trapdoor", + "id" : -496 + }, + { + "name" : "minecraft:mangrove_wall_sign", + "id" : -495 + }, + { + "name" : "minecraft:mangrove_wood", + "id" : -497 + }, + { + "name" : "minecraft:medicine", + "id" : 605 + }, + { + "name" : "minecraft:medium_amethyst_bud", + "id" : -331 + }, + { + "name" : "minecraft:melon_block", + "id" : 103 + }, + { + "name" : "minecraft:melon_seeds", + "id" : 293 + }, + { + "name" : "minecraft:melon_slice", + "id" : 272 + }, + { + "name" : "minecraft:melon_stem", + "id" : 105 + }, + { + "name" : "minecraft:milk_bucket", + "id" : 362 + }, + { + "name" : "minecraft:minecart", + "id" : 371 + }, + { + "name" : "minecraft:mob_spawner", + "id" : 52 + }, + { + "name" : "minecraft:mojang_banner_pattern", + "id" : 590 + }, + { + "name" : "minecraft:monster_egg", + "id" : 97 + }, + { + "name" : "minecraft:mooshroom_spawn_egg", + "id" : 441 + }, + { + "name" : "minecraft:moss_block", + "id" : -320 + }, + { + "name" : "minecraft:moss_carpet", + "id" : -335 + }, + { + "name" : "minecraft:mossy_cobblestone", + "id" : 48 + }, + { + "name" : "minecraft:mossy_cobblestone_stairs", + "id" : -179 + }, + { + "name" : "minecraft:mossy_stone_brick_stairs", + "id" : -175 + }, + { + "name" : "minecraft:moving_block", + "id" : 250 + }, + { + "name" : "minecraft:mud", + "id" : -473 + }, + { + "name" : "minecraft:mud_brick_double_slab", + "id" : -479 + }, + { + "name" : "minecraft:mud_brick_slab", + "id" : -478 + }, + { + "name" : "minecraft:mud_brick_stairs", + "id" : -480 + }, + { + "name" : "minecraft:mud_brick_wall", + "id" : -481 + }, + { + "name" : "minecraft:mud_bricks", + "id" : -475 + }, + { + "name" : "minecraft:muddy_mangrove_roots", + "id" : -483 + }, + { + "name" : "minecraft:mule_spawn_egg", + "id" : 467 + }, + { + "name" : "minecraft:mushroom_stew", + "id" : 260 + }, + { + "name" : "minecraft:music_disc_11", + "id" : 550 + }, + { + "name" : "minecraft:music_disc_13", + "id" : 540 + }, + { + "name" : "minecraft:music_disc_5", + "id" : 642 + }, + { + "name" : "minecraft:music_disc_blocks", + "id" : 542 + }, + { + "name" : "minecraft:music_disc_cat", + "id" : 541 + }, + { + "name" : "minecraft:music_disc_chirp", + "id" : 543 + }, + { + "name" : "minecraft:music_disc_far", + "id" : 544 + }, + { + "name" : "minecraft:music_disc_mall", + "id" : 545 + }, + { + "name" : "minecraft:music_disc_mellohi", + "id" : 546 + }, + { + "name" : "minecraft:music_disc_otherside", + "id" : 632 + }, + { + "name" : "minecraft:music_disc_pigstep", + "id" : 626 + }, + { + "name" : "minecraft:music_disc_stal", + "id" : 547 + }, + { + "name" : "minecraft:music_disc_strad", + "id" : 548 + }, + { + "name" : "minecraft:music_disc_wait", + "id" : 551 + }, + { + "name" : "minecraft:music_disc_ward", + "id" : 549 + }, + { + "name" : "minecraft:mutton", + "id" : 556 + }, + { + "name" : "minecraft:mycelium", + "id" : 110 + }, + { + "name" : "minecraft:name_tag", + "id" : 554 + }, + { + "name" : "minecraft:nautilus_shell", + "id" : 576 + }, + { + "name" : "minecraft:nether_brick", + "id" : 112 + }, + { + "name" : "minecraft:nether_brick_fence", + "id" : 113 + }, + { + "name" : "minecraft:nether_brick_stairs", + "id" : 114 + }, + { + "name" : "minecraft:nether_gold_ore", + "id" : -288 + }, + { + "name" : "minecraft:nether_sprouts", + "id" : 627 + }, + { + "name" : "minecraft:nether_star", + "id" : 524 + }, + { + "name" : "minecraft:nether_wart", + "id" : 294 + }, + { + "name" : "minecraft:nether_wart_block", + "id" : 214 + }, + { + "name" : "minecraft:netherbrick", + "id" : 529 + }, + { + "name" : "minecraft:netherite_axe", + "id" : 613 + }, + { + "name" : "minecraft:netherite_block", + "id" : -270 + }, + { + "name" : "minecraft:netherite_boots", + "id" : 618 + }, + { + "name" : "minecraft:netherite_chestplate", + "id" : 616 + }, + { + "name" : "minecraft:netherite_helmet", + "id" : 615 + }, + { + "name" : "minecraft:netherite_hoe", + "id" : 614 + }, + { + "name" : "minecraft:netherite_ingot", + "id" : 609 + }, + { + "name" : "minecraft:netherite_leggings", + "id" : 617 + }, + { + "name" : "minecraft:netherite_pickaxe", + "id" : 612 + }, + { + "name" : "minecraft:netherite_scrap", + "id" : 619 + }, + { + "name" : "minecraft:netherite_shovel", + "id" : 611 + }, + { + "name" : "minecraft:netherite_sword", + "id" : 610 + }, + { + "name" : "minecraft:netherrack", + "id" : 87 + }, + { + "name" : "minecraft:netherreactor", + "id" : 247 + }, + { + "name" : "minecraft:normal_stone_stairs", + "id" : -180 + }, + { + "name" : "minecraft:noteblock", + "id" : 25 + }, + { + "name" : "minecraft:npc_spawn_egg", + "id" : 471 + }, + { + "name" : "minecraft:oak_boat", + "id" : 376 + }, + { + "name" : "minecraft:oak_chest_boat", + "id" : 644 + }, + { + "name" : "minecraft:oak_hanging_sign", + "id" : -500 + }, + { + "name" : "minecraft:oak_sign", + "id" : 359 + }, + { + "name" : "minecraft:oak_stairs", + "id" : 53 + }, + { + "name" : "minecraft:observer", + "id" : 251 + }, + { + "name" : "minecraft:obsidian", + "id" : 49 + }, + { + "name" : "minecraft:ocelot_spawn_egg", + "id" : 452 + }, + { + "name" : "minecraft:ochre_froglight", + "id" : -471 + }, + { + "name" : "minecraft:orange_candle", + "id" : -414 + }, + { + "name" : "minecraft:orange_candle_cake", + "id" : -431 + }, + { + "name" : "minecraft:orange_dye", + "id" : 410 + }, + { + "name" : "minecraft:orange_glazed_terracotta", + "id" : 221 + }, + { + "name" : "minecraft:orange_wool", + "id" : -557 + }, + { + "name" : "minecraft:oxidized_copper", + "id" : -343 + }, + { + "name" : "minecraft:oxidized_cut_copper", + "id" : -350 + }, + { + "name" : "minecraft:oxidized_cut_copper_slab", + "id" : -364 + }, + { + "name" : "minecraft:oxidized_cut_copper_stairs", + "id" : -357 + }, + { + "name" : "minecraft:oxidized_double_cut_copper_slab", + "id" : -371 + }, + { + "name" : "minecraft:packed_ice", + "id" : 174 + }, + { + "name" : "minecraft:packed_mud", + "id" : -477 + }, + { + "name" : "minecraft:painting", + "id" : 358 + }, + { + "name" : "minecraft:panda_spawn_egg", + "id" : 490 + }, + { + "name" : "minecraft:paper", + "id" : 387 + }, + { + "name" : "minecraft:parrot_spawn_egg", + "id" : 479 + }, + { + "name" : "minecraft:pearlescent_froglight", + "id" : -469 + }, + { + "name" : "minecraft:phantom_membrane", + "id" : 580 + }, + { + "name" : "minecraft:phantom_spawn_egg", + "id" : 487 + }, + { + "name" : "minecraft:pig_spawn_egg", + "id" : 438 + }, + { + "name" : "minecraft:piglin_banner_pattern", + "id" : 593 + }, + { + "name" : "minecraft:piglin_brute_spawn_egg", + "id" : 500 + }, + { + "name" : "minecraft:piglin_spawn_egg", + "id" : 498 + }, + { + "name" : "minecraft:pillager_spawn_egg", + "id" : 492 + }, + { + "name" : "minecraft:pink_candle", + "id" : -419 + }, + { + "name" : "minecraft:pink_candle_cake", + "id" : -436 + }, + { + "name" : "minecraft:pink_dye", + "id" : 405 + }, + { + "name" : "minecraft:pink_glazed_terracotta", + "id" : 226 + }, + { + "name" : "minecraft:pink_wool", + "id" : -566 + }, + { + "name" : "minecraft:piston", + "id" : 33 + }, + { + "name" : "minecraft:piston_arm_collision", + "id" : 34 + }, + { + "name" : "minecraft:planks", + "id" : 5 + }, + { + "name" : "minecraft:podzol", + "id" : 243 + }, + { + "name" : "minecraft:pointed_dripstone", + "id" : -308 + }, + { + "name" : "minecraft:poisonous_potato", + "id" : 282 + }, + { + "name" : "minecraft:polar_bear_spawn_egg", + "id" : 473 + }, + { + "name" : "minecraft:polished_andesite_stairs", + "id" : -174 + }, + { + "name" : "minecraft:polished_basalt", + "id" : -235 + }, + { + "name" : "minecraft:polished_blackstone", + "id" : -291 + }, + { + "name" : "minecraft:polished_blackstone_brick_double_slab", + "id" : -285 + }, + { + "name" : "minecraft:polished_blackstone_brick_slab", + "id" : -284 + }, + { + "name" : "minecraft:polished_blackstone_brick_stairs", + "id" : -275 + }, + { + "name" : "minecraft:polished_blackstone_brick_wall", + "id" : -278 + }, + { + "name" : "minecraft:polished_blackstone_bricks", + "id" : -274 + }, + { + "name" : "minecraft:polished_blackstone_button", + "id" : -296 + }, + { + "name" : "minecraft:polished_blackstone_double_slab", + "id" : -294 + }, + { + "name" : "minecraft:polished_blackstone_pressure_plate", + "id" : -295 + }, + { + "name" : "minecraft:polished_blackstone_slab", + "id" : -293 + }, + { + "name" : "minecraft:polished_blackstone_stairs", + "id" : -292 + }, + { + "name" : "minecraft:polished_blackstone_wall", + "id" : -297 + }, + { + "name" : "minecraft:polished_deepslate", + "id" : -383 + }, + { + "name" : "minecraft:polished_deepslate_double_slab", + "id" : -397 + }, + { + "name" : "minecraft:polished_deepslate_slab", + "id" : -384 + }, + { + "name" : "minecraft:polished_deepslate_stairs", + "id" : -385 + }, + { + "name" : "minecraft:polished_deepslate_wall", + "id" : -386 + }, + { + "name" : "minecraft:polished_diorite_stairs", + "id" : -173 + }, + { + "name" : "minecraft:polished_granite_stairs", + "id" : -172 + }, + { + "name" : "minecraft:popped_chorus_fruit", + "id" : 565 + }, + { + "name" : "minecraft:porkchop", + "id" : 262 + }, + { + "name" : "minecraft:portal", + "id" : 90 + }, + { + "name" : "minecraft:potato", + "id" : 280 + }, + { + "name" : "minecraft:potatoes", + "id" : 142 + }, + { + "name" : "minecraft:potion", + "id" : 427 + }, + { + "name" : "minecraft:powder_snow", + "id" : -306 + }, + { + "name" : "minecraft:powder_snow_bucket", + "id" : 369 + }, + { + "name" : "minecraft:powered_comparator", + "id" : 150 + }, + { + "name" : "minecraft:powered_repeater", + "id" : 94 + }, + { + "name" : "minecraft:prismarine", + "id" : 168 + }, + { + "name" : "minecraft:prismarine_bricks_stairs", + "id" : -4 + }, + { + "name" : "minecraft:prismarine_crystals", + "id" : 555 + }, + { + "name" : "minecraft:prismarine_shard", + "id" : 571 + }, + { + "name" : "minecraft:prismarine_stairs", + "id" : -2 + }, + { + "name" : "minecraft:prize_pottery_shard", + "id" : 661 + }, + { + "name" : "minecraft:pufferfish", + "id" : 267 + }, + { + "name" : "minecraft:pufferfish_bucket", + "id" : 368 + }, + { + "name" : "minecraft:pufferfish_spawn_egg", + "id" : 482 + }, + { + "name" : "minecraft:pumpkin", + "id" : 86 + }, + { + "name" : "minecraft:pumpkin_pie", + "id" : 284 + }, + { + "name" : "minecraft:pumpkin_seeds", + "id" : 292 + }, + { + "name" : "minecraft:pumpkin_stem", + "id" : 104 + }, + { + "name" : "minecraft:purple_candle", + "id" : -423 + }, + { + "name" : "minecraft:purple_candle_cake", + "id" : -440 + }, + { + "name" : "minecraft:purple_dye", + "id" : 401 + }, + { + "name" : "minecraft:purple_glazed_terracotta", + "id" : 219 + }, + { + "name" : "minecraft:purple_wool", + "id" : -564 + }, + { + "name" : "minecraft:purpur_block", + "id" : 201 + }, + { + "name" : "minecraft:purpur_stairs", + "id" : 203 + }, + { + "name" : "minecraft:quartz", + "id" : 530 + }, + { + "name" : "minecraft:quartz_block", + "id" : 155 + }, + { + "name" : "minecraft:quartz_bricks", + "id" : -304 + }, + { + "name" : "minecraft:quartz_ore", + "id" : 153 + }, + { + "name" : "minecraft:quartz_stairs", + "id" : 156 + }, + { + "name" : "minecraft:rabbit", + "id" : 288 + }, + { + "name" : "minecraft:rabbit_foot", + "id" : 534 + }, + { + "name" : "minecraft:rabbit_hide", + "id" : 535 + }, + { + "name" : "minecraft:rabbit_spawn_egg", + "id" : 460 + }, + { + "name" : "minecraft:rabbit_stew", + "id" : 290 + }, + { + "name" : "minecraft:rail", + "id" : 66 + }, + { + "name" : "minecraft:rapid_fertilizer", + "id" : 603 + }, + { + "name" : "minecraft:ravager_spawn_egg", + "id" : 494 + }, + { + "name" : "minecraft:raw_copper", + "id" : 513 + }, + { + "name" : "minecraft:raw_copper_block", + "id" : -452 + }, + { + "name" : "minecraft:raw_gold", + "id" : 512 + }, + { + "name" : "minecraft:raw_gold_block", + "id" : -453 + }, + { + "name" : "minecraft:raw_iron", + "id" : 511 + }, + { + "name" : "minecraft:raw_iron_block", + "id" : -451 + }, + { + "name" : "minecraft:recovery_compass", + "id" : 652 + }, + { + "name" : "minecraft:red_candle", + "id" : -427 + }, + { + "name" : "minecraft:red_candle_cake", + "id" : -444 + }, + { + "name" : "minecraft:red_dye", + "id" : 397 + }, + { + "name" : "minecraft:red_flower", + "id" : 38 + }, + { + "name" : "minecraft:red_glazed_terracotta", + "id" : 234 + }, + { + "name" : "minecraft:red_mushroom", + "id" : 40 + }, + { + "name" : "minecraft:red_mushroom_block", + "id" : 100 + }, + { + "name" : "minecraft:red_nether_brick", + "id" : 215 + }, + { + "name" : "minecraft:red_nether_brick_stairs", + "id" : -184 + }, + { + "name" : "minecraft:red_sandstone", + "id" : 179 + }, + { + "name" : "minecraft:red_sandstone_stairs", + "id" : 180 + }, + { + "name" : "minecraft:red_wool", + "id" : -556 + }, + { + "name" : "minecraft:redstone", + "id" : 374 + }, + { + "name" : "minecraft:redstone_block", + "id" : 152 + }, + { + "name" : "minecraft:redstone_lamp", + "id" : 123 + }, + { + "name" : "minecraft:redstone_ore", + "id" : 73 + }, + { + "name" : "minecraft:redstone_torch", + "id" : 76 + }, + { + "name" : "minecraft:redstone_wire", + "id" : 55 + }, + { + "name" : "minecraft:reinforced_deepslate", + "id" : -466 + }, + { + "name" : "minecraft:repeater", + "id" : 420 + }, + { + "name" : "minecraft:repeating_command_block", + "id" : 188 + }, + { + "name" : "minecraft:reserved6", + "id" : 255 + }, + { + "name" : "minecraft:respawn_anchor", + "id" : -272 + }, + { + "name" : "minecraft:rotten_flesh", + "id" : 277 + }, + { + "name" : "minecraft:saddle", + "id" : 372 + }, + { + "name" : "minecraft:salmon", + "id" : 265 + }, + { + "name" : "minecraft:salmon_bucket", + "id" : 366 + }, + { + "name" : "minecraft:salmon_spawn_egg", + "id" : 483 + }, + { + "name" : "minecraft:sand", + "id" : 12 + }, + { + "name" : "minecraft:sandstone", + "id" : 24 + }, + { + "name" : "minecraft:sandstone_stairs", + "id" : 128 + }, + { + "name" : "minecraft:sapling", + "id" : 6 + }, + { + "name" : "minecraft:scaffolding", + "id" : -165 + }, + { + "name" : "minecraft:sculk", + "id" : -458 + }, + { + "name" : "minecraft:sculk_catalyst", + "id" : -460 + }, + { + "name" : "minecraft:sculk_sensor", + "id" : -307 + }, + { + "name" : "minecraft:sculk_shrieker", + "id" : -461 + }, + { + "name" : "minecraft:sculk_vein", + "id" : -459 + }, + { + "name" : "minecraft:scute", + "id" : 578 + }, + { + "name" : "minecraft:sea_lantern", + "id" : 169 + }, + { + "name" : "minecraft:sea_pickle", + "id" : -156 + }, + { + "name" : "minecraft:seagrass", + "id" : -130 + }, + { + "name" : "minecraft:shears", + "id" : 422 + }, + { + "name" : "minecraft:sheep_spawn_egg", + "id" : 439 + }, + { + "name" : "minecraft:shield", + "id" : 356 + }, + { + "name" : "minecraft:shroomlight", + "id" : -230 + }, + { + "name" : "minecraft:shulker_box", + "id" : 218 + }, + { + "name" : "minecraft:shulker_shell", + "id" : 572 + }, + { + "name" : "minecraft:shulker_spawn_egg", + "id" : 470 + }, + { + "name" : "minecraft:silver_glazed_terracotta", + "id" : 228 + }, + { + "name" : "minecraft:silverfish_spawn_egg", + "id" : 444 + }, + { + "name" : "minecraft:skeleton_horse_spawn_egg", + "id" : 468 + }, + { + "name" : "minecraft:skeleton_spawn_egg", + "id" : 445 + }, + { + "name" : "minecraft:skull", + "id" : 522 + }, + { + "name" : "minecraft:skull_banner_pattern", + "id" : 589 + }, + { + "name" : "minecraft:skull_pottery_shard", + "id" : 662 + }, + { + "name" : "minecraft:slime", + "id" : 165 + }, + { + "name" : "minecraft:slime_ball", + "id" : 389 + }, + { + "name" : "minecraft:slime_spawn_egg", + "id" : 446 + }, + { + "name" : "minecraft:small_amethyst_bud", + "id" : -332 + }, + { + "name" : "minecraft:small_dripleaf_block", + "id" : -336 + }, + { + "name" : "minecraft:smithing_table", + "id" : -202 + }, + { + "name" : "minecraft:smoker", + "id" : -198 + }, + { + "name" : "minecraft:smooth_basalt", + "id" : -377 + }, + { + "name" : "minecraft:smooth_quartz_stairs", + "id" : -185 + }, + { + "name" : "minecraft:smooth_red_sandstone_stairs", + "id" : -176 + }, + { + "name" : "minecraft:smooth_sandstone_stairs", + "id" : -177 + }, + { + "name" : "minecraft:smooth_stone", + "id" : -183 + }, + { + "name" : "minecraft:sniffer_spawn_egg", + "id" : 501 + }, + { + "name" : "minecraft:snow", + "id" : 80 + }, + { + "name" : "minecraft:snow_golem_spawn_egg", + "id" : 506 + }, + { + "name" : "minecraft:snow_layer", + "id" : 78 + }, + { + "name" : "minecraft:snowball", + "id" : 375 + }, + { + "name" : "minecraft:soul_campfire", + "id" : 628 + }, + { + "name" : "minecraft:soul_fire", + "id" : -237 + }, + { + "name" : "minecraft:soul_lantern", + "id" : -269 + }, + { + "name" : "minecraft:soul_sand", + "id" : 88 + }, + { + "name" : "minecraft:soul_soil", + "id" : -236 + }, + { + "name" : "minecraft:soul_torch", + "id" : -268 + }, + { + "name" : "minecraft:sparkler", + "id" : 606 + }, + { + "name" : "minecraft:spawn_egg", + "id" : 668 + }, + { + "name" : "minecraft:spider_eye", + "id" : 278 + }, + { + "name" : "minecraft:spider_spawn_egg", + "id" : 447 + }, + { + "name" : "minecraft:splash_potion", + "id" : 567 + }, + { + "name" : "minecraft:sponge", + "id" : 19 + }, + { + "name" : "minecraft:spore_blossom", + "id" : -321 + }, + { + "name" : "minecraft:spruce_boat", + "id" : 379 + }, + { + "name" : "minecraft:spruce_button", + "id" : -144 + }, + { + "name" : "minecraft:spruce_chest_boat", + "id" : 647 + }, + { + "name" : "minecraft:spruce_door", + "id" : 559 + }, + { + "name" : "minecraft:spruce_fence_gate", + "id" : 183 + }, + { + "name" : "minecraft:spruce_hanging_sign", + "id" : -501 + }, + { + "name" : "minecraft:spruce_pressure_plate", + "id" : -154 + }, + { + "name" : "minecraft:spruce_sign", + "id" : 582 + }, + { + "name" : "minecraft:spruce_stairs", + "id" : 134 + }, + { + "name" : "minecraft:spruce_standing_sign", + "id" : -181 + }, + { + "name" : "minecraft:spruce_trapdoor", + "id" : -149 + }, + { + "name" : "minecraft:spruce_wall_sign", + "id" : -182 + }, + { + "name" : "minecraft:spyglass", + "id" : 631 + }, + { + "name" : "minecraft:squid_spawn_egg", + "id" : 451 + }, + { + "name" : "minecraft:stained_glass", + "id" : 241 + }, + { + "name" : "minecraft:stained_glass_pane", + "id" : 160 + }, + { + "name" : "minecraft:stained_hardened_clay", + "id" : 159 + }, + { + "name" : "minecraft:standing_banner", + "id" : 176 + }, + { + "name" : "minecraft:standing_sign", + "id" : 63 + }, + { + "name" : "minecraft:stick", + "id" : 321 + }, + { + "name" : "minecraft:sticky_piston", + "id" : 29 + }, + { + "name" : "minecraft:sticky_piston_arm_collision", + "id" : -217 + }, + { + "name" : "minecraft:stone", + "id" : 1 + }, + { + "name" : "minecraft:stone_axe", + "id" : 316 + }, + { + "name" : "minecraft:stone_block_slab", + "id" : 44 + }, + { + "name" : "minecraft:stone_block_slab2", + "id" : 182 + }, + { + "name" : "minecraft:stone_block_slab3", + "id" : -162 + }, + { + "name" : "minecraft:stone_block_slab4", + "id" : -166 + }, + { + "name" : "minecraft:stone_brick_stairs", + "id" : 109 + }, + { + "name" : "minecraft:stone_button", + "id" : 77 + }, + { + "name" : "minecraft:stone_hoe", + "id" : 331 + }, + { + "name" : "minecraft:stone_pickaxe", + "id" : 315 + }, + { + "name" : "minecraft:stone_pressure_plate", + "id" : 70 + }, + { + "name" : "minecraft:stone_shovel", + "id" : 314 + }, + { + "name" : "minecraft:stone_stairs", + "id" : 67 + }, + { + "name" : "minecraft:stone_sword", + "id" : 313 + }, + { + "name" : "minecraft:stonebrick", + "id" : 98 + }, + { + "name" : "minecraft:stonecutter", + "id" : 245 + }, + { + "name" : "minecraft:stonecutter_block", + "id" : -197 + }, + { + "name" : "minecraft:stray_spawn_egg", + "id" : 463 + }, + { + "name" : "minecraft:strider_spawn_egg", + "id" : 496 + }, + { + "name" : "minecraft:string", + "id" : 327 + }, + { + "name" : "minecraft:stripped_acacia_log", + "id" : -8 + }, + { + "name" : "minecraft:stripped_bamboo_block", + "id" : -528 + }, + { + "name" : "minecraft:stripped_birch_log", + "id" : -6 + }, + { + "name" : "minecraft:stripped_crimson_hyphae", + "id" : -300 + }, + { + "name" : "minecraft:stripped_crimson_stem", + "id" : -240 + }, + { + "name" : "minecraft:stripped_dark_oak_log", + "id" : -9 + }, + { + "name" : "minecraft:stripped_jungle_log", + "id" : -7 + }, + { + "name" : "minecraft:stripped_mangrove_log", + "id" : -485 + }, + { + "name" : "minecraft:stripped_mangrove_wood", + "id" : -498 + }, + { + "name" : "minecraft:stripped_oak_log", + "id" : -10 + }, + { + "name" : "minecraft:stripped_spruce_log", + "id" : -5 + }, + { + "name" : "minecraft:stripped_warped_hyphae", + "id" : -301 + }, + { + "name" : "minecraft:stripped_warped_stem", + "id" : -241 + }, + { + "name" : "minecraft:structure_block", + "id" : 252 + }, + { + "name" : "minecraft:structure_void", + "id" : 217 + }, + { + "name" : "minecraft:sugar", + "id" : 417 + }, + { + "name" : "minecraft:sugar_cane", + "id" : 386 + }, + { + "name" : "minecraft:suspicious_sand", + "id" : -529 + }, + { + "name" : "minecraft:suspicious_stew", + "id" : 596 + }, + { + "name" : "minecraft:sweet_berries", + "id" : 287 + }, + { + "name" : "minecraft:sweet_berry_bush", + "id" : -207 + }, + { + "name" : "minecraft:tadpole_bucket", + "id" : 636 + }, + { + "name" : "minecraft:tadpole_spawn_egg", + "id" : 635 + }, + { + "name" : "minecraft:tallgrass", + "id" : 31 + }, + { + "name" : "minecraft:target", + "id" : -239 + }, + { + "name" : "minecraft:tinted_glass", + "id" : -334 + }, + { + "name" : "minecraft:tnt", + "id" : 46 + }, + { + "name" : "minecraft:tnt_minecart", + "id" : 531 + }, + { + "name" : "minecraft:torch", + "id" : 50 + }, + { + "name" : "minecraft:torchflower", + "id" : -568 + }, + { + "name" : "minecraft:torchflower_crop", + "id" : -567 + }, + { + "name" : "minecraft:torchflower_seeds", + "id" : 296 + }, + { + "name" : "minecraft:totem_of_undying", + "id" : 574 + }, + { + "name" : "minecraft:trader_llama_spawn_egg", + "id" : 654 + }, + { + "name" : "minecraft:trapdoor", + "id" : 96 + }, + { + "name" : "minecraft:trapped_chest", + "id" : 146 + }, + { + "name" : "minecraft:trident", + "id" : 552 + }, + { + "name" : "minecraft:trip_wire", + "id" : 132 + }, + { + "name" : "minecraft:tripwire_hook", + "id" : 131 + }, + { + "name" : "minecraft:tropical_fish", + "id" : 266 + }, + { + "name" : "minecraft:tropical_fish_bucket", + "id" : 367 + }, + { + "name" : "minecraft:tropical_fish_spawn_egg", + "id" : 480 + }, + { + "name" : "minecraft:tuff", + "id" : -333 + }, + { + "name" : "minecraft:turtle_egg", + "id" : -159 + }, + { + "name" : "minecraft:turtle_helmet", + "id" : 579 + }, + { + "name" : "minecraft:turtle_spawn_egg", + "id" : 486 + }, + { + "name" : "minecraft:twisting_vines", + "id" : -287 + }, + { + "name" : "minecraft:underwater_torch", + "id" : 239 + }, + { + "name" : "minecraft:undyed_shulker_box", + "id" : 205 + }, + { + "name" : "minecraft:unknown", + "id" : -305 + }, + { + "name" : "minecraft:unlit_redstone_torch", + "id" : 75 + }, + { + "name" : "minecraft:unpowered_comparator", + "id" : 149 + }, + { + "name" : "minecraft:unpowered_repeater", + "id" : 93 + }, + { + "name" : "minecraft:verdant_froglight", + "id" : -470 + }, + { + "name" : "minecraft:vex_spawn_egg", + "id" : 477 + }, + { + "name" : "minecraft:villager_spawn_egg", + "id" : 450 + }, + { + "name" : "minecraft:vindicator_spawn_egg", + "id" : 475 + }, + { + "name" : "minecraft:vine", + "id" : 106 + }, + { + "name" : "minecraft:wall_banner", + "id" : 177 + }, + { + "name" : "minecraft:wall_sign", + "id" : 68 + }, + { + "name" : "minecraft:wandering_trader_spawn_egg", + "id" : 493 + }, + { + "name" : "minecraft:warden_spawn_egg", + "id" : 638 + }, + { + "name" : "minecraft:warped_button", + "id" : -261 + }, + { + "name" : "minecraft:warped_door", + "id" : 623 + }, + { + "name" : "minecraft:warped_double_slab", + "id" : -267 + }, + { + "name" : "minecraft:warped_fence", + "id" : -257 + }, + { + "name" : "minecraft:warped_fence_gate", + "id" : -259 + }, + { + "name" : "minecraft:warped_fungus", + "id" : -229 + }, + { + "name" : "minecraft:warped_fungus_on_a_stick", + "id" : 624 + }, + { + "name" : "minecraft:warped_hanging_sign", + "id" : -507 + }, + { + "name" : "minecraft:warped_hyphae", + "id" : -298 + }, + { + "name" : "minecraft:warped_nylium", + "id" : -233 + }, + { + "name" : "minecraft:warped_planks", + "id" : -243 + }, + { + "name" : "minecraft:warped_pressure_plate", + "id" : -263 + }, + { + "name" : "minecraft:warped_roots", + "id" : -224 + }, + { + "name" : "minecraft:warped_sign", + "id" : 621 + }, + { + "name" : "minecraft:warped_slab", + "id" : -265 + }, + { + "name" : "minecraft:warped_stairs", + "id" : -255 + }, + { + "name" : "minecraft:warped_standing_sign", + "id" : -251 + }, + { + "name" : "minecraft:warped_stem", + "id" : -226 + }, + { + "name" : "minecraft:warped_trapdoor", + "id" : -247 + }, + { + "name" : "minecraft:warped_wall_sign", + "id" : -253 + }, + { + "name" : "minecraft:warped_wart_block", + "id" : -227 + }, + { + "name" : "minecraft:water", + "id" : 9 + }, + { + "name" : "minecraft:water_bucket", + "id" : 363 + }, + { + "name" : "minecraft:waterlily", + "id" : 111 + }, + { + "name" : "minecraft:waxed_copper", + "id" : -344 + }, + { + "name" : "minecraft:waxed_cut_copper", + "id" : -351 + }, + { + "name" : "minecraft:waxed_cut_copper_slab", + "id" : -365 + }, + { + "name" : "minecraft:waxed_cut_copper_stairs", + "id" : -358 + }, + { + "name" : "minecraft:waxed_double_cut_copper_slab", + "id" : -372 + }, + { + "name" : "minecraft:waxed_exposed_copper", + "id" : -345 + }, + { + "name" : "minecraft:waxed_exposed_cut_copper", + "id" : -352 + }, + { + "name" : "minecraft:waxed_exposed_cut_copper_slab", + "id" : -366 + }, + { + "name" : "minecraft:waxed_exposed_cut_copper_stairs", + "id" : -359 + }, + { + "name" : "minecraft:waxed_exposed_double_cut_copper_slab", + "id" : -373 + }, + { + "name" : "minecraft:waxed_oxidized_copper", + "id" : -446 + }, + { + "name" : "minecraft:waxed_oxidized_cut_copper", + "id" : -447 + }, + { + "name" : "minecraft:waxed_oxidized_cut_copper_slab", + "id" : -449 + }, + { + "name" : "minecraft:waxed_oxidized_cut_copper_stairs", + "id" : -448 + }, + { + "name" : "minecraft:waxed_oxidized_double_cut_copper_slab", + "id" : -450 + }, + { + "name" : "minecraft:waxed_weathered_copper", + "id" : -346 + }, + { + "name" : "minecraft:waxed_weathered_cut_copper", + "id" : -353 + }, + { + "name" : "minecraft:waxed_weathered_cut_copper_slab", + "id" : -367 + }, + { + "name" : "minecraft:waxed_weathered_cut_copper_stairs", + "id" : -360 + }, + { + "name" : "minecraft:waxed_weathered_double_cut_copper_slab", + "id" : -374 + }, + { + "name" : "minecraft:weathered_copper", + "id" : -342 + }, + { + "name" : "minecraft:weathered_cut_copper", + "id" : -349 + }, + { + "name" : "minecraft:weathered_cut_copper_slab", + "id" : -363 + }, + { + "name" : "minecraft:weathered_cut_copper_stairs", + "id" : -356 + }, + { + "name" : "minecraft:weathered_double_cut_copper_slab", + "id" : -370 + }, + { + "name" : "minecraft:web", + "id" : 30 + }, + { + "name" : "minecraft:weeping_vines", + "id" : -231 + }, + { + "name" : "minecraft:wheat", + "id" : 335 + }, + { + "name" : "minecraft:wheat_seeds", + "id" : 291 + }, + { + "name" : "minecraft:white_candle", + "id" : -413 + }, + { + "name" : "minecraft:white_candle_cake", + "id" : -430 + }, + { + "name" : "minecraft:white_dye", + "id" : 411 + }, + { + "name" : "minecraft:white_glazed_terracotta", + "id" : 220 + }, + { + "name" : "minecraft:white_wool", + "id" : 35 + }, + { + "name" : "minecraft:witch_spawn_egg", + "id" : 453 + }, + { + "name" : "minecraft:wither_rose", + "id" : -216 + }, + { + "name" : "minecraft:wither_skeleton_spawn_egg", + "id" : 465 + }, + { + "name" : "minecraft:wither_spawn_egg", + "id" : 508 + }, + { + "name" : "minecraft:wolf_spawn_egg", + "id" : 440 + }, + { + "name" : "minecraft:wood", + "id" : -212 + }, + { + "name" : "minecraft:wooden_axe", + "id" : 312 + }, + { + "name" : "minecraft:wooden_button", + "id" : 143 + }, + { + "name" : "minecraft:wooden_door", + "id" : 360 + }, + { + "name" : "minecraft:wooden_hoe", + "id" : 330 + }, + { + "name" : "minecraft:wooden_pickaxe", + "id" : 311 + }, + { + "name" : "minecraft:wooden_pressure_plate", + "id" : 72 + }, + { + "name" : "minecraft:wooden_shovel", + "id" : 310 + }, + { + "name" : "minecraft:wooden_slab", + "id" : 158 + }, + { + "name" : "minecraft:wooden_sword", + "id" : 309 + }, + { + "name" : "minecraft:wool", + "id" : 664 + }, + { + "name" : "minecraft:writable_book", + "id" : 516 + }, + { + "name" : "minecraft:written_book", + "id" : 517 + }, + { + "name" : "minecraft:yellow_candle", + "id" : -417 + }, + { + "name" : "minecraft:yellow_candle_cake", + "id" : -434 + }, + { + "name" : "minecraft:yellow_dye", + "id" : 407 + }, + { + "name" : "minecraft:yellow_flower", + "id" : 37 + }, + { + "name" : "minecraft:yellow_glazed_terracotta", + "id" : 224 + }, + { + "name" : "minecraft:yellow_wool", + "id" : -558 + }, + { + "name" : "minecraft:zoglin_spawn_egg", + "id" : 499 + }, + { + "name" : "minecraft:zombie_horse_spawn_egg", + "id" : 469 + }, + { + "name" : "minecraft:zombie_pigman_spawn_egg", + "id" : 449 + }, + { + "name" : "minecraft:zombie_spawn_egg", + "id" : 448 + }, + { + "name" : "minecraft:zombie_villager_spawn_egg", + "id" : 478 + } +] \ No newline at end of file diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index d83aaa8f4..3fef94519 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -11,7 +11,7 @@ websocket = "1.5.1" protocol = "2.9.17-20230217.002312-1" raknet = "1.6.28-20220125.214016-6" mcauthlib = "d9d773e" -mcprotocollib = "1.19.4-SNAPSHOT" +mcprotocollib = "1.19.4-20230314.173921-2" adventure = "4.12.0-20220629.025215-9" adventure-platform = "4.1.2" junit = "5.9.2" From d3e7d993965a6af52e792dac27f8d22afa1de339 Mon Sep 17 00:00:00 2001 From: Camotoy <20743703+Camotoy@users.noreply.github.com> Date: Tue, 14 Mar 2023 15:53:16 -0400 Subject: [PATCH 28/59] Remove debug print --- .../protocol/bedrock/BedrockInventoryTransactionTranslator.java | 1 - 1 file changed, 1 deletion(-) 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 9e348e704..4d21a36eb 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 @@ -509,7 +509,6 @@ public class BedrockInventoryTransactionTranslator extends PacketTranslator Date: Tue, 14 Mar 2023 21:16:38 +0100 Subject: [PATCH 29/59] Removed another debug line --- .../protocol/bedrock/BedrockInventoryTransactionTranslator.java | 1 - 1 file changed, 1 deletion(-) 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 4d21a36eb..893af01cb 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 @@ -485,7 +485,6 @@ public class BedrockInventoryTransactionTranslator extends PacketTranslator Date: Wed, 15 Mar 2023 11:03:29 -0400 Subject: [PATCH 30/59] Fix smithing tables not opening --- .../geyser/translator/inventory/InventoryTranslator.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/main/java/org/geysermc/geyser/translator/inventory/InventoryTranslator.java b/core/src/main/java/org/geysermc/geyser/translator/inventory/InventoryTranslator.java index e6cc010f5..8fe6597c6 100644 --- a/core/src/main/java/org/geysermc/geyser/translator/inventory/InventoryTranslator.java +++ b/core/src/main/java/org/geysermc/geyser/translator/inventory/InventoryTranslator.java @@ -88,7 +88,7 @@ public abstract class InventoryTranslator { put(ContainerType.LOOM, new LoomInventoryTranslator()); put(ContainerType.MERCHANT, new MerchantInventoryTranslator()); put(ContainerType.SHULKER_BOX, new ShulkerInventoryTranslator()); - put(ContainerType.SMITHING, new SmithingInventoryTranslator()); + put(ContainerType.LEGACY_SMITHING, new SmithingInventoryTranslator()); put(ContainerType.STONECUTTER, new StonecutterInventoryTranslator()); /* Lectern */ From d7c68659122fd27a68081e70e1142550e115f5c2 Mon Sep 17 00:00:00 2001 From: Camotoy <20743703+Camotoy@users.noreply.github.com> Date: Wed, 15 Mar 2023 11:36:00 -0400 Subject: [PATCH 31/59] Fix #3607 --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 3fef94519..ddd3c38bf 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -11,7 +11,7 @@ websocket = "1.5.1" protocol = "2.9.17-20230217.002312-1" raknet = "1.6.28-20220125.214016-6" mcauthlib = "d9d773e" -mcprotocollib = "1.19.4-20230314.173921-2" +mcprotocollib = "1.19.4-20230315.153350-3" adventure = "4.12.0-20220629.025215-9" adventure-platform = "4.1.2" junit = "5.9.2" From acb25b0c99ad5cf638dad3e168116c00d9930a9e Mon Sep 17 00:00:00 2001 From: Camotoy <20743703+Camotoy@users.noreply.github.com> Date: Fri, 17 Mar 2023 13:41:01 -0400 Subject: [PATCH 32/59] Remove legacy 1.19.2x Bedrock code --- .../geysermc/geyser/network/GameProtocol.java | 4 -- .../BedrockAdventureSettingsTranslator.java | 42 ------------------- .../BedrockRequestAbilityTranslator.java | 38 +++++++---------- .../java/JavaKeepAliveTranslator.java | 5 +++ .../protocol/java/JavaLoginTranslator.java | 7 ---- 5 files changed, 19 insertions(+), 77 deletions(-) delete mode 100644 core/src/main/java/org/geysermc/geyser/translator/protocol/bedrock/BedrockAdventureSettingsTranslator.java diff --git a/core/src/main/java/org/geysermc/geyser/network/GameProtocol.java b/core/src/main/java/org/geysermc/geyser/network/GameProtocol.java index 16f4abd04..083bc5b58 100644 --- a/core/src/main/java/org/geysermc/geyser/network/GameProtocol.java +++ b/core/src/main/java/org/geysermc/geyser/network/GameProtocol.java @@ -96,10 +96,6 @@ public final class GameProtocol { /* Bedrock convenience methods to gatekeep features and easily remove the check on version removal */ - public static boolean supports1_19_30(GeyserSession session) { - return session.getUpstream().getProtocolVersion() >= Bedrock_v554.V554_CODEC.getProtocolVersion(); - } - public static boolean supports1_19_50(GeyserSession session) { return session.getUpstream().getProtocolVersion() >= Bedrock_v560.V560_CODEC.getProtocolVersion(); } diff --git a/core/src/main/java/org/geysermc/geyser/translator/protocol/bedrock/BedrockAdventureSettingsTranslator.java b/core/src/main/java/org/geysermc/geyser/translator/protocol/bedrock/BedrockAdventureSettingsTranslator.java deleted file mode 100644 index aabc39e12..000000000 --- a/core/src/main/java/org/geysermc/geyser/translator/protocol/bedrock/BedrockAdventureSettingsTranslator.java +++ /dev/null @@ -1,42 +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.protocol.bedrock; - -import com.nukkitx.protocol.bedrock.data.AdventureSetting; -import com.nukkitx.protocol.bedrock.packet.AdventureSettingsPacket; -import org.geysermc.geyser.session.GeyserSession; -import org.geysermc.geyser.translator.protocol.PacketTranslator; -import org.geysermc.geyser.translator.protocol.Translator; - -@Translator(packet = AdventureSettingsPacket.class) -public class BedrockAdventureSettingsTranslator extends PacketTranslator { - - @Override - public void translate(GeyserSession session, AdventureSettingsPacket packet) { - boolean isFlying = packet.getSettings().contains(AdventureSetting.FLYING); - BedrockRequestAbilityTranslator.handle(session, isFlying); - } -} diff --git a/core/src/main/java/org/geysermc/geyser/translator/protocol/bedrock/BedrockRequestAbilityTranslator.java b/core/src/main/java/org/geysermc/geyser/translator/protocol/bedrock/BedrockRequestAbilityTranslator.java index fe8150d40..bf268fd0e 100644 --- a/core/src/main/java/org/geysermc/geyser/translator/protocol/bedrock/BedrockRequestAbilityTranslator.java +++ b/core/src/main/java/org/geysermc/geyser/translator/protocol/bedrock/BedrockRequestAbilityTranslator.java @@ -30,7 +30,6 @@ import com.github.steveice10.mc.protocol.packet.ingame.serverbound.player.Server import com.nukkitx.protocol.bedrock.data.Ability; import com.nukkitx.protocol.bedrock.data.entity.EntityFlag; import com.nukkitx.protocol.bedrock.packet.RequestAbilityPacket; -import org.geysermc.geyser.network.GameProtocol; import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.translator.protocol.PacketTranslator; import org.geysermc.geyser.translator.protocol.Translator; @@ -43,31 +42,22 @@ public class BedrockRequestAbilityTranslator extends PacketTranslator Date: Fri, 17 Mar 2023 13:41:13 -0400 Subject: [PATCH 33/59] Fix player nametags always appearing --- .../org/geysermc/geyser/scoreboard/Team.java | 17 ++++++++--------- gradle/libs.versions.toml | 2 +- 2 files changed, 9 insertions(+), 10 deletions(-) diff --git a/core/src/main/java/org/geysermc/geyser/scoreboard/Team.java b/core/src/main/java/org/geysermc/geyser/scoreboard/Team.java index b2e9043b5..e985ca803 100644 --- a/core/src/main/java/org/geysermc/geyser/scoreboard/Team.java +++ b/core/src/main/java/org/geysermc/geyser/scoreboard/Team.java @@ -33,9 +33,9 @@ import lombok.Getter; import lombok.Setter; import lombok.experimental.Accessors; +import javax.annotation.Nonnull; import javax.annotation.Nullable; import java.util.HashSet; -import java.util.Objects; import java.util.Set; @Getter @@ -46,7 +46,7 @@ public final class Team { @Getter(AccessLevel.PACKAGE) private final Set entities; - @Setter @Nullable private NameTagVisibility nameTagVisibility; + @Nonnull private NameTagVisibility nameTagVisibility = NameTagVisibility.ALWAYS; @Setter private TeamColor color; private final TeamData currentData; @@ -189,11 +189,6 @@ public final class Team { } public boolean isVisibleFor(String entity) { - if (nameTagVisibility == null) { - // Null - normal behavior - return true; - } - return switch (nameTagVisibility) { case HIDE_FOR_OTHER_TEAMS -> { // Player must be in a team in order for HIDE_FOR_OTHER_TEAMS to be triggered @@ -206,8 +201,12 @@ public final class Team { }; } - public NameTagVisibility getNameTagVisibility() { - return Objects.requireNonNullElse(this.nameTagVisibility, NameTagVisibility.ALWAYS); + public Team setNameTagVisibility(@Nullable NameTagVisibility nameTagVisibility) { + if (nameTagVisibility != null) { + // Null check like this (and this.nameTagVisibility defaults to ALWAYS) as of Java 1.19.4 + this.nameTagVisibility = nameTagVisibility; + } + return this; } @Override diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index ddd3c38bf..7e8ddb2ad 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -11,7 +11,7 @@ websocket = "1.5.1" protocol = "2.9.17-20230217.002312-1" raknet = "1.6.28-20220125.214016-6" mcauthlib = "d9d773e" -mcprotocollib = "1.19.4-20230315.153350-3" +mcprotocollib = "1.19.4-20230317.173631-4" adventure = "4.12.0-20220629.025215-9" adventure-platform = "4.1.2" junit = "5.9.2" From 9609686eb34960937fe514d06d467ffd7fc57bcc Mon Sep 17 00:00:00 2001 From: Camotoy <20743703+Camotoy@users.noreply.github.com> Date: Fri, 17 Mar 2023 19:07:31 -0400 Subject: [PATCH 34/59] Version out potion registry The ID of (for example) redstone dust has shifted, meaning that our hack of re-using IDs no longer works. Fixes #3620 --- .../geysermc/geyser/registry/Registries.java | 4 +- .../loader/PotionMixRegistryLoader.java | 101 ++++++++++-------- .../geyser/session/GeyserSession.java | 2 +- .../java/JavaUpdateRecipesTranslator.java | 2 +- 4 files changed, 59 insertions(+), 50 deletions(-) diff --git a/core/src/main/java/org/geysermc/geyser/registry/Registries.java b/core/src/main/java/org/geysermc/geyser/registry/Registries.java index 866cbd291..b4afd7d2f 100644 --- a/core/src/main/java/org/geysermc/geyser/registry/Registries.java +++ b/core/src/main/java/org/geysermc/geyser/registry/Registries.java @@ -135,7 +135,7 @@ public final class Registries { /** * A registry holding all the potion mixes. */ - public static final SimpleRegistry> POTION_MIXES; + public static final VersionedRegistry> POTION_MIXES; /** * A registry holding all the @@ -178,7 +178,7 @@ public final class Registries { RecipeRegistryPopulator.populate(); // Create registries that require other registries to load first - POTION_MIXES = SimpleRegistry.create(PotionMixRegistryLoader::new); + POTION_MIXES = VersionedRegistry.create(PotionMixRegistryLoader::new); ENCHANTMENTS = SimpleMappedRegistry.create("mappings/enchantments.json", EnchantmentRegistryLoader::new); // Remove unneeded client generation data from NbtMapBuilder diff --git a/core/src/main/java/org/geysermc/geyser/registry/loader/PotionMixRegistryLoader.java b/core/src/main/java/org/geysermc/geyser/registry/loader/PotionMixRegistryLoader.java index 8d40edac3..694aeba9c 100644 --- a/core/src/main/java/org/geysermc/geyser/registry/loader/PotionMixRegistryLoader.java +++ b/core/src/main/java/org/geysermc/geyser/registry/loader/PotionMixRegistryLoader.java @@ -26,17 +26,18 @@ package org.geysermc.geyser.registry.loader; import com.nukkitx.protocol.bedrock.data.inventory.PotionMixData; +import it.unimi.dsi.fastutil.ints.Int2ObjectMap; +import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; import org.geysermc.geyser.inventory.item.Potion; -import org.geysermc.geyser.network.GameProtocol; import org.geysermc.geyser.registry.Registries; import org.geysermc.geyser.registry.type.ItemMapping; +import org.geysermc.geyser.registry.type.ItemMappings; import java.util.ArrayList; import java.util.HashSet; import java.util.List; import java.util.Set; -//TODO this needs to be versioned, but the runtime item states between 1.17 and 1.17.10 are identical except for new blocks so this works for both /** * Generates a collection of {@link PotionMixData} that enables the * Bedrock client to place brewing items into the brewing stand. @@ -46,64 +47,72 @@ import java.util.Set; * (Ex: Bedrock cannot normally place glass bottles or fully upgraded * potions into the brewing stand, but Java can.) */ -public class PotionMixRegistryLoader implements RegistryLoader> { +public class PotionMixRegistryLoader implements RegistryLoader>> { @Override - public Set load(Object input) { - List ingredients = new ArrayList<>(); - ingredients.add(getNonNull("minecraft:nether_wart")); - ingredients.add(getNonNull("minecraft:redstone")); - ingredients.add(getNonNull("minecraft:glowstone_dust")); - ingredients.add(getNonNull("minecraft:fermented_spider_eye")); - ingredients.add(getNonNull("minecraft:gunpowder")); - ingredients.add(getNonNull("minecraft:dragon_breath")); - ingredients.add(getNonNull("minecraft:sugar")); - ingredients.add(getNonNull("minecraft:rabbit_foot")); - ingredients.add(getNonNull("minecraft:glistering_melon_slice")); - ingredients.add(getNonNull("minecraft:spider_eye")); - ingredients.add(getNonNull("minecraft:pufferfish")); - ingredients.add(getNonNull("minecraft:magma_cream")); - ingredients.add(getNonNull("minecraft:golden_carrot")); - ingredients.add(getNonNull("minecraft:blaze_powder")); - ingredients.add(getNonNull("minecraft:ghast_tear")); - ingredients.add(getNonNull("minecraft:turtle_helmet")); - ingredients.add(getNonNull("minecraft:phantom_membrane")); + public Int2ObjectMap> load(Object input) { + var allPotionMixes = new Int2ObjectOpenHashMap>(Registries.ITEMS.get().size()); + for (var entry : Registries.ITEMS.get().int2ObjectEntrySet()) { + ItemMappings mappings = entry.getValue(); + List ingredients = new ArrayList<>(); + ingredients.add(getNonNull(mappings, "minecraft:nether_wart")); + ingredients.add(getNonNull(mappings, "minecraft:redstone")); + ingredients.add(getNonNull(mappings, "minecraft:glowstone_dust")); + ingredients.add(getNonNull(mappings, "minecraft:fermented_spider_eye")); + ingredients.add(getNonNull(mappings, "minecraft:gunpowder")); + ingredients.add(getNonNull(mappings, "minecraft:dragon_breath")); + ingredients.add(getNonNull(mappings, "minecraft:sugar")); + ingredients.add(getNonNull(mappings, "minecraft:rabbit_foot")); + ingredients.add(getNonNull(mappings, "minecraft:glistering_melon_slice")); + ingredients.add(getNonNull(mappings, "minecraft:spider_eye")); + ingredients.add(getNonNull(mappings, "minecraft:pufferfish")); + ingredients.add(getNonNull(mappings, "minecraft:magma_cream")); + ingredients.add(getNonNull(mappings, "minecraft:golden_carrot")); + ingredients.add(getNonNull(mappings, "minecraft:blaze_powder")); + ingredients.add(getNonNull(mappings, "minecraft:ghast_tear")); + ingredients.add(getNonNull(mappings, "minecraft:turtle_helmet")); + ingredients.add(getNonNull(mappings, "minecraft:phantom_membrane")); - List inputs = new ArrayList<>(); - inputs.add(getNonNull("minecraft:potion")); - inputs.add(getNonNull("minecraft:splash_potion")); - inputs.add(getNonNull("minecraft:lingering_potion")); + List inputs = List.of( + getNonNull(mappings, "minecraft:potion"), + getNonNull(mappings, "minecraft:splash_potion"), + getNonNull(mappings, "minecraft:lingering_potion") + ); - ItemMapping glassBottle = getNonNull("minecraft:glass_bottle"); + ItemMapping glassBottle = getNonNull(mappings, "minecraft:glass_bottle"); - Set potionMixes = new HashSet<>(); + Set potionMixes = new HashSet<>(); - // Add all types of potions as inputs - ItemMapping fillerIngredient = ingredients.get(0); - for (ItemMapping entryInput : inputs) { - for (Potion potion : Potion.values()) { + // Add all types of potions as inputs + ItemMapping fillerIngredient = ingredients.get(0); + for (ItemMapping entryInput : inputs) { + for (Potion potion : Potion.VALUES) { + potionMixes.add(new PotionMixData( + entryInput.getBedrockId(), potion.getBedrockId(), + fillerIngredient.getBedrockId(), fillerIngredient.getBedrockData(), + glassBottle.getBedrockId(), glassBottle.getBedrockData()) + ); + } + } + + // Add all brewing ingredients + // Also adds glass bottle as input + for (ItemMapping ingredient : ingredients) { potionMixes.add(new PotionMixData( - entryInput.getBedrockId(), potion.getBedrockId(), - fillerIngredient.getBedrockId(), fillerIngredient.getBedrockData(), + glassBottle.getBedrockId(), glassBottle.getBedrockData(), + ingredient.getBedrockId(), ingredient.getBedrockData(), glassBottle.getBedrockId(), glassBottle.getBedrockData()) ); } - } - // Add all brewing ingredients - // Also adds glass bottle as input - for (ItemMapping ingredient : ingredients) { - potionMixes.add(new PotionMixData( - glassBottle.getBedrockId(), glassBottle.getBedrockData(), - ingredient.getBedrockId(), ingredient.getBedrockData(), - glassBottle.getBedrockId(), glassBottle.getBedrockData()) - ); + allPotionMixes.put(entry.getIntKey(), potionMixes); } - return potionMixes; + allPotionMixes.trim(); + return allPotionMixes; } - private static ItemMapping getNonNull(String javaIdentifier) { - ItemMapping itemMapping = Registries.ITEMS.forVersion(GameProtocol.DEFAULT_BEDROCK_CODEC.getProtocolVersion()).getMapping(javaIdentifier); + private static ItemMapping getNonNull(ItemMappings mappings, String javaIdentifier) { + ItemMapping itemMapping = mappings.getMapping(javaIdentifier); if (itemMapping == null) throw new NullPointerException("No item entry exists for java identifier: " + javaIdentifier); diff --git a/core/src/main/java/org/geysermc/geyser/session/GeyserSession.java b/core/src/main/java/org/geysermc/geyser/session/GeyserSession.java index 9b7d334fc..fa0db15f2 100644 --- a/core/src/main/java/org/geysermc/geyser/session/GeyserSession.java +++ b/core/src/main/java/org/geysermc/geyser/session/GeyserSession.java @@ -640,7 +640,7 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource { // Potion mixes are registered by default, as they are needed to be able to put ingredients into the brewing stand. CraftingDataPacket craftingDataPacket = new CraftingDataPacket(); craftingDataPacket.setCleanRecipes(true); - craftingDataPacket.getPotionMixData().addAll(Registries.POTION_MIXES.get()); + craftingDataPacket.getPotionMixData().addAll(Registries.POTION_MIXES.forVersion(this.upstream.getProtocolVersion())); upstream.sendPacket(craftingDataPacket); PlayStatusPacket playStatusPacket = new PlayStatusPacket(); diff --git a/core/src/main/java/org/geysermc/geyser/translator/protocol/java/JavaUpdateRecipesTranslator.java b/core/src/main/java/org/geysermc/geyser/translator/protocol/java/JavaUpdateRecipesTranslator.java index 9923c0a16..e665d5424 100644 --- a/core/src/main/java/org/geysermc/geyser/translator/protocol/java/JavaUpdateRecipesTranslator.java +++ b/core/src/main/java/org/geysermc/geyser/translator/protocol/java/JavaUpdateRecipesTranslator.java @@ -167,7 +167,7 @@ public class JavaUpdateRecipesTranslator extends PacketTranslator stonecutterRecipeMap = new Int2ObjectOpenHashMap<>(); for (Int2ObjectMap.Entry> data : unsortedStonecutterData.int2ObjectEntrySet()) { From a330c9a5db69d12a2df3ac351ac69c8ae6367a3d Mon Sep 17 00:00:00 2001 From: Camotoy <20743703+Camotoy@users.noreply.github.com> Date: Sat, 18 Mar 2023 17:40:51 -0400 Subject: [PATCH 35/59] Fix lecterns Huge thanks to Dylan from PocketMine for the idea here. Fixes #3138 --- .../main/java/org/geysermc/geyser/session/GeyserSession.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/core/src/main/java/org/geysermc/geyser/session/GeyserSession.java b/core/src/main/java/org/geysermc/geyser/session/GeyserSession.java index fa0db15f2..d76cc4b97 100644 --- a/core/src/main/java/org/geysermc/geyser/session/GeyserSession.java +++ b/core/src/main/java/org/geysermc/geyser/session/GeyserSession.java @@ -1688,6 +1688,8 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource { abilities.add(Ability.MINE); // Needed so you can drop items abilities.add(Ability.DOORS_AND_SWITCHES); + // Required for lecterns to work (likely started around 1.19.10; confirmed on 1.19.70) + abilities.add(Ability.OPEN_CONTAINERS); if (gameMode == GameMode.CREATIVE) { // Needed so the client doesn't attempt to take away items abilities.add(Ability.INSTABUILD); From 23294484ded4e7b7994bc36d4f00444f0d8093ae Mon Sep 17 00:00:00 2001 From: rtm516 Date: Sun, 19 Mar 2023 15:08:51 +0000 Subject: [PATCH 36/59] Update downloads api publishing steps --- .github/workflows/build.yml | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 2d1c84670..0c88bf213 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -106,12 +106,16 @@ jobs: # Save the private key to a file echo "$DOWNLOADS_PRIVATE_KEY" > id_ecdsa chmod 600 id_ecdsa + # Set the project + project=geyser # Get the version from gradle.properties version=$(cat gradle.properties | grep -o "version=[0-9\\.]*" | cut -d"=" -f2) # Copy over artifacts - scp -B -o StrictHostKeyChecking=no -i id_ecdsa bootstrap/**/build/libs/Geyser-*.jar $DOWNLOADS_USERNAME@$DOWNLOADS_SERVER_IP:~/files/ + rsync -P -e "ssh -o StrictHostKeyChecking=no -i id_ecdsa" bootstrap/**/build/libs/Geyser-*.jar $DOWNLOADS_USERNAME@$DOWNLOADS_SERVER_IP:~/uploads/$project/$GITHUB_RUN_NUMBER/ # Run the build script - ssh -o StrictHostKeyChecking=no -i id_ecdsa $DOWNLOADS_USERNAME@$DOWNLOADS_SERVER_IP ./handleBuild.sh geyser $version $GITHUB_RUN_NUMBER $GITHUB_SHA + # Push the metadata + echo "{\"project\": \"$project\", \"version\": \"$version\", \"id\": $GITHUB_RUN_NUMBER, \"commit\": \"$GITHUB_SHA\"}" > metadata.json + rsync -P -e "ssh -o StrictHostKeyChecking=no -i id_ecdsa" metadata.json $DOWNLOADS_USERNAME@$DOWNLOADS_SERVER_IP:~/uploads/$project/$GITHUB_RUN_NUMBER/ - name: Notify Discord if: ${{ (success() || failure()) && github.repository == 'GeyserMC/Geyser' }} From b56f4016886a69c11a705b038ea0464e09d9b9a5 Mon Sep 17 00:00:00 2001 From: Camotoy <20743703+Camotoy@users.noreply.github.com> Date: Sun, 19 Mar 2023 11:24:45 -0400 Subject: [PATCH 37/59] Update MCProtocolLib to fix #3624 --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 7e8ddb2ad..a656f6b74 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -11,7 +11,7 @@ websocket = "1.5.1" protocol = "2.9.17-20230217.002312-1" raknet = "1.6.28-20220125.214016-6" mcauthlib = "d9d773e" -mcprotocollib = "1.19.4-20230317.173631-4" +mcprotocollib = "1.19.4-20230319.152208-5" adventure = "4.12.0-20220629.025215-9" adventure-platform = "4.1.2" junit = "5.9.2" From 021ffe2d946e270c170ebbd34c014fd6e80ffc87 Mon Sep 17 00:00:00 2001 From: David Choo <4722249+davchoo@users.noreply.github.com> Date: Sun, 19 Mar 2023 11:30:56 -0400 Subject: [PATCH 38/59] Update ringing bell block event logic (#3625) Check for BellValue instead of a GenericBlockValue Removes now unnecessary JAVA_BELL_ID from BlockStateValues --- .../geyser/level/block/BlockStateValues.java | 1 - .../registry/populator/BlockRegistryPopulator.java | 10 +--------- .../java/level/JavaBlockEventTranslator.java | 13 +++++++------ 3 files changed, 8 insertions(+), 16 deletions(-) diff --git a/core/src/main/java/org/geysermc/geyser/level/block/BlockStateValues.java b/core/src/main/java/org/geysermc/geyser/level/block/BlockStateValues.java index 58cbce77f..c6fc60303 100644 --- a/core/src/main/java/org/geysermc/geyser/level/block/BlockStateValues.java +++ b/core/src/main/java/org/geysermc/geyser/level/block/BlockStateValues.java @@ -68,7 +68,6 @@ public final class BlockStateValues { public static final int JAVA_AIR_ID = 0; - public static int JAVA_BELL_ID; public static int JAVA_COBWEB_ID; public static int JAVA_FURNACE_ID; public static int JAVA_FURNACE_LIT_ID; diff --git a/core/src/main/java/org/geysermc/geyser/registry/populator/BlockRegistryPopulator.java b/core/src/main/java/org/geysermc/geyser/registry/populator/BlockRegistryPopulator.java index b931750e1..ad1066491 100644 --- a/core/src/main/java/org/geysermc/geyser/registry/populator/BlockRegistryPopulator.java +++ b/core/src/main/java/org/geysermc/geyser/registry/populator/BlockRegistryPopulator.java @@ -223,7 +223,6 @@ public final class BlockRegistryPopulator { Deque cleanIdentifiers = new ArrayDeque<>(); int javaRuntimeId = -1; - int bellBlockId = -1; int cobwebBlockId = -1; int furnaceRuntimeId = -1; int furnaceLitRuntimeId = -1; @@ -300,10 +299,7 @@ public final class BlockRegistryPopulator { // It's possible to only have this store differences in names, but the key set of all Java names is used in sending command suggestions BlockRegistries.JAVA_TO_BEDROCK_IDENTIFIERS.register(cleanJavaIdentifier.intern(), bedrockIdentifier.intern()); - if (javaId.startsWith("minecraft:bell[")) { - bellBlockId = uniqueJavaId; - - } else if (javaId.contains("cobweb")) { + if (javaId.contains("cobweb")) { cobwebBlockId = uniqueJavaId; } else if (javaId.startsWith("minecraft:furnace[facing=north")) { @@ -324,10 +320,6 @@ public final class BlockRegistryPopulator { slimeBlockRuntimeId = javaRuntimeId; } } - if (bellBlockId == -1) { - throw new AssertionError("Unable to find bell in palette"); - } - BlockStateValues.JAVA_BELL_ID = bellBlockId; if (cobwebBlockId == -1) { throw new AssertionError("Unable to find cobwebs in palette"); 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 149e8356e..aa7c9e6e7 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 @@ -103,7 +103,7 @@ public class JavaBlockEventTranslator extends PacketTranslator 0; // north - case 4 -> 1; // east - case 5 -> 3;// west - default -> bellValue.getValue(); // south (2) is identical + int bedrockRingDirection = switch (bellValue.getDirection()) { + case SOUTH -> 0; + case WEST -> 1; + case NORTH -> 2; + case EAST -> 3; + default -> throw new IllegalStateException("Unexpected BellValue Direction: " + bellValue.getDirection()); }; builder.putInt("Direction", bedrockRingDirection); builder.putByte("Ringing", (byte) 1); From 775725c617acabdb8afc90b6134a2f0e128a4fcc Mon Sep 17 00:00:00 2001 From: rtm516 Date: Sun, 19 Mar 2023 17:09:18 +0000 Subject: [PATCH 39/59] Create the build directory when pushing to the api --- .github/workflows/build.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 0c88bf213..668c9ca72 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -110,6 +110,8 @@ jobs: project=geyser # Get the version from gradle.properties version=$(cat gradle.properties | grep -o "version=[0-9\\.]*" | cut -d"=" -f2) + # Create the build folder + ssh -o StrictHostKeyChecking=no -i id_ecdsa $DOWNLOADS_USERNAME@$DOWNLOADS_SERVER_IP mkdir -p "~/uploads/$project/$GITHUB_RUN_NUMBER/" # Copy over artifacts rsync -P -e "ssh -o StrictHostKeyChecking=no -i id_ecdsa" bootstrap/**/build/libs/Geyser-*.jar $DOWNLOADS_USERNAME@$DOWNLOADS_SERVER_IP:~/uploads/$project/$GITHUB_RUN_NUMBER/ # Run the build script From b664bb961e925d955a74ddf75ef36fe3ee16cec9 Mon Sep 17 00:00:00 2001 From: Camotoy <20743703+Camotoy@users.noreply.github.com> Date: Sun, 19 Mar 2023 14:08:48 -0400 Subject: [PATCH 40/59] Indicate 1.19.71 support --- README.md | 2 +- .../main/java/org/geysermc/geyser/network/GameProtocol.java | 6 ++++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index cfc99a8ba..013e87239 100644 --- a/README.md +++ b/README.md @@ -17,7 +17,7 @@ The ultimate goal of this project is to allow Minecraft: Bedrock Edition users t Special thanks to the DragonProxy project for being a trailblazer in protocol translation and for all the team members who have joined us here! -### Currently supporting Minecraft Bedrock 1.19.30 - 1.19.70 and Minecraft Java 1.19.4. +### Currently supporting Minecraft Bedrock 1.19.30 - 1.19.71 and Minecraft Java 1.19.4. ## Setting Up Take a look [here](https://wiki.geysermc.org/geyser/setup/) for how to set up Geyser. diff --git a/core/src/main/java/org/geysermc/geyser/network/GameProtocol.java b/core/src/main/java/org/geysermc/geyser/network/GameProtocol.java index 083bc5b58..8ce4fd196 100644 --- a/core/src/main/java/org/geysermc/geyser/network/GameProtocol.java +++ b/core/src/main/java/org/geysermc/geyser/network/GameProtocol.java @@ -49,7 +49,7 @@ public final class GameProtocol { */ public static final BedrockPacketCodec DEFAULT_BEDROCK_CODEC = Bedrock_v567patch.BEDROCK_V567PATCH.toBuilder() .protocolVersion(575) - .minecraftVersion("1.19.70") + .minecraftVersion("1.19.71") .build(); /** * A list of all supported Bedrock versions that can join Geyser @@ -77,7 +77,9 @@ public final class GameProtocol { .protocolVersion(568) .minecraftVersion("1.19.62") .build()); - SUPPORTED_BEDROCK_CODECS.add(DEFAULT_BEDROCK_CODEC); + SUPPORTED_BEDROCK_CODECS.add(DEFAULT_BEDROCK_CODEC.toBuilder() + .minecraftVersion("1.19.70/1.19.71") + .build()); } /** From b695dc075d5db6c6de3f5703a0dbfea317cd34af Mon Sep 17 00:00:00 2001 From: Camotoy <20743703+Camotoy@users.noreply.github.com> Date: Sun, 19 Mar 2023 14:09:01 -0400 Subject: [PATCH 41/59] Fix command redirection detection --- .../translator/protocol/java/JavaCommandsTranslator.java | 7 ++++--- gradle/libs.versions.toml | 2 +- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/core/src/main/java/org/geysermc/geyser/translator/protocol/java/JavaCommandsTranslator.java b/core/src/main/java/org/geysermc/geyser/translator/protocol/java/JavaCommandsTranslator.java index 24a52600a..5b009b81c 100644 --- a/core/src/main/java/org/geysermc/geyser/translator/protocol/java/JavaCommandsTranslator.java +++ b/core/src/main/java/org/geysermc/geyser/translator/protocol/java/JavaCommandsTranslator.java @@ -199,9 +199,10 @@ public class JavaCommandsTranslator extends PacketTranslator= 1) { diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index a656f6b74..4f7433034 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -11,7 +11,7 @@ websocket = "1.5.1" protocol = "2.9.17-20230217.002312-1" raknet = "1.6.28-20220125.214016-6" mcauthlib = "d9d773e" -mcprotocollib = "1.19.4-20230319.152208-5" +mcprotocollib = "1.19.4-20230319.175814-6" adventure = "4.12.0-20220629.025215-9" adventure-platform = "4.1.2" junit = "5.9.2" From 4a20bbad88f968bd9055b081df95c67924dead19 Mon Sep 17 00:00:00 2001 From: Redned Date: Sun, 19 Mar 2023 15:24:52 -0500 Subject: [PATCH 42/59] Update README with new download link --- README.md | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/README.md b/README.md index 013e87239..77e2671f9 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,6 @@ Geyser -[![forthebadge made-with-java](https://forthebadge.com/images/badges/made-with-java.svg)](https://java.com/) - [![License: MIT](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE) -[![Build Status](https://ci.opencollab.dev/job/Geyser/job/master/badge/icon)](https://ci.opencollab.dev/job/GeyserMC/job/Geyser/job/master/) [![Discord](https://img.shields.io/discord/613163671870242838.svg?color=%237289da&label=discord)](https://discord.gg/geysermc) [![Crowdin](https://badges.crowdin.net/geyser/localized.svg)](https://translate.geysermc.org/) @@ -27,7 +24,7 @@ Take a look [here](https://wiki.geysermc.org/geyser/setup/) for how to set up Ge ## Links: - Website: https://geysermc.org - Docs: https://wiki.geysermc.org/geyser/ -- Download: https://ci.geysermc.org +- Download: https://geysermc.org/download - Discord: https://discord.gg/geysermc - Donate: https://opencollective.com/geysermc - Test Server: `test.geysermc.org` port `25565` for Java and `19132` for Bedrock From 05829eeed8daadd8e779ad50f3d89879f196f81a Mon Sep 17 00:00:00 2001 From: Camotoy <20743703+Camotoy@users.noreply.github.com> Date: Mon, 20 Mar 2023 13:04:53 -0400 Subject: [PATCH 43/59] Upload Fabric platform to Modrinth --- .github/workflows/publish.yml | 16 ++++++++++++++++ bootstrap/fabric/build.gradle.kts | 19 +++++++++++++++++++ 2 files changed, 35 insertions(+) create mode 100644 .github/workflows/publish.yml diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml new file mode 100644 index 000000000..2b5bea7df --- /dev/null +++ b/.github/workflows/publish.yml @@ -0,0 +1,16 @@ +name: publish + +jobs: + build: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - uses: gradle/wrapper-validation-action@v1 + - uses: actions/setup-java@v3 + with: + distribution: 'temurin' + java-version: 17 + - name: build and publish + env: + MODRINTH_TOKEN: ${{ secrets.MODRINTH_TOKEN }} + run: ./gradlew fabric:modrinth \ No newline at end of file diff --git a/bootstrap/fabric/build.gradle.kts b/bootstrap/fabric/build.gradle.kts index 743b75a26..890f5d656 100644 --- a/bootstrap/fabric/build.gradle.kts +++ b/bootstrap/fabric/build.gradle.kts @@ -1,5 +1,6 @@ plugins { id("fabric-loom") version "1.0-SNAPSHOT" + id("com.modrinth.minotaur") version "2.+" } java { @@ -74,4 +75,22 @@ tasks { archiveClassifier.set("") archiveVersion.set("") } +} + +modrinth { + projectId.set("wKkoqHrH") + versionNumber.set(project.version as String + "-" + System.getenv("GITHUB_RUN_NUMBER")) + versionType.set("beta") + changelog.set("A changelog can be found at https://github.com/GeyserMC/Geyser/commits") + + syncBodyFrom.set(rootProject.file("README.md").readText()) + + uploadFile.set(tasks.getByPath("remapJar")) + gameVersions.addAll("1.19", "1.19.1", "1.19.2", "1.19.3", "1.19.4") + + loaders.add("fabric") + + dependencies { + required.project("fabric-api") + } } \ No newline at end of file From 66005edcc6c9015b75325fba8978cd182c587bab Mon Sep 17 00:00:00 2001 From: Camotoy <20743703+Camotoy@users.noreply.github.com> Date: Mon, 20 Mar 2023 13:08:07 -0400 Subject: [PATCH 44/59] Actually run it --- .github/workflows/publish.yml | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 2b5bea7df..04712681d 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -1,4 +1,17 @@ -name: publish +name: Publish +on: + workflow_dispatch: + push: + paths-ignore: + - '.github/ISSUE_TEMPLATE/*.yml' + - '.github/actions/pullrequest.yml' + - '.idea/copyright/*.xml' + - '.gitignore' + - 'CONTRIBUTING.md' + - 'LICENSE' + - 'Jenkinsfile ' + - 'README.md' + - 'licenseheader.txt' jobs: build: @@ -10,7 +23,7 @@ jobs: with: distribution: 'temurin' java-version: 17 - - name: build and publish + - name: Publish to Modrinth env: MODRINTH_TOKEN: ${{ secrets.MODRINTH_TOKEN }} run: ./gradlew fabric:modrinth \ No newline at end of file From 775e1e8921bd08b2d7f68ce67935b7d63685f3c5 Mon Sep 17 00:00:00 2001 From: Camotoy <20743703+Camotoy@users.noreply.github.com> Date: Mon, 20 Mar 2023 13:32:02 -0400 Subject: [PATCH 45/59] Modrinth: submodules exist too --- .github/workflows/publish.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 04712681d..2b002b0de 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -18,6 +18,8 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 + with: + submodules: recursive - uses: gradle/wrapper-validation-action@v1 - uses: actions/setup-java@v3 with: From 7ef005006b76875fcc7bca3c670b69bd544a4112 Mon Sep 17 00:00:00 2001 From: Camotoy <20743703+Camotoy@users.noreply.github.com> Date: Tue, 21 Mar 2023 15:07:51 -0400 Subject: [PATCH 46/59] Safety null check for dismount check code --- .../player/BedrockInteractTranslator.java | 30 ++++++++++--------- 1 file changed, 16 insertions(+), 14 deletions(-) diff --git a/core/src/main/java/org/geysermc/geyser/translator/protocol/bedrock/entity/player/BedrockInteractTranslator.java b/core/src/main/java/org/geysermc/geyser/translator/protocol/bedrock/entity/player/BedrockInteractTranslator.java index 22a895465..93d0c4a83 100644 --- a/core/src/main/java/org/geysermc/geyser/translator/protocol/bedrock/entity/player/BedrockInteractTranslator.java +++ b/core/src/main/java/org/geysermc/geyser/translator/protocol/bedrock/entity/player/BedrockInteractTranslator.java @@ -79,21 +79,23 @@ public class BedrockInteractTranslator extends PacketTranslator session.sendDownstreamPacket(sneakPacket); Entity currentVehicle = session.getPlayerEntity().getVehicle(); - session.setMountVehicleScheduledFuture(session.scheduleInEventLoop(() -> { - if (session.getPlayerEntity().getVehicle() == null) { - return; - } + if (currentVehicle != null) { + session.setMountVehicleScheduledFuture(session.scheduleInEventLoop(() -> { + if (session.getPlayerEntity().getVehicle() == null) { + return; + } - long vehicleBedrockId = currentVehicle.getGeyserId(); - if (session.getPlayerEntity().getVehicle().getGeyserId() == vehicleBedrockId) { - // The Bedrock client, as of 1.19.51, dismounts on its end. The server may not agree with this. - // If the server doesn't agree with our dismount (sends a packet saying we dismounted), - // then remount the player. - SetEntityLinkPacket linkPacket = new SetEntityLinkPacket(); - linkPacket.setEntityLink(new EntityLinkData(vehicleBedrockId, session.getPlayerEntity().getGeyserId(), EntityLinkData.Type.PASSENGER, true, false)); - session.sendUpstreamPacket(linkPacket); - } - }, 1, TimeUnit.SECONDS)); + long vehicleBedrockId = currentVehicle.getGeyserId(); + if (session.getPlayerEntity().getVehicle().getGeyserId() == vehicleBedrockId) { + // The Bedrock client, as of 1.19.51, dismounts on its end. The server may not agree with this. + // If the server doesn't agree with our dismount (sends a packet saying we dismounted), + // then remount the player. + SetEntityLinkPacket linkPacket = new SetEntityLinkPacket(); + linkPacket.setEntityLink(new EntityLinkData(vehicleBedrockId, session.getPlayerEntity().getGeyserId(), EntityLinkData.Type.PASSENGER, true, false)); + session.sendUpstreamPacket(linkPacket); + } + }, 1, TimeUnit.SECONDS)); + } break; case MOUSEOVER: // Handle the buttons for mobile - "Mount", etc; and the suggestions for console - "ZL: Mount", etc From 96260cc35879b7446b62303d3a4edc82a8577a3c Mon Sep 17 00:00:00 2001 From: Kas-tle <26531652+Kas-tle@users.noreply.github.com> Date: Thu, 23 Mar 2023 21:57:40 -0700 Subject: [PATCH 47/59] Bring Rain/Thunder Behavior Inline With Java (#3637) Closes #3611 Closes #2588 Closes #2499 Signed-off-by: Joshua Castle <26531652+Kas-tle@users.noreply.github.com> --- .../java/level/JavaGameEventTranslator.java | 67 ++++++++++--------- 1 file changed, 35 insertions(+), 32 deletions(-) diff --git a/core/src/main/java/org/geysermc/geyser/translator/protocol/java/level/JavaGameEventTranslator.java b/core/src/main/java/org/geysermc/geyser/translator/protocol/java/level/JavaGameEventTranslator.java index 05e14c41b..7019838c1 100644 --- a/core/src/main/java/org/geysermc/geyser/translator/protocol/java/level/JavaGameEventTranslator.java +++ b/core/src/main/java/org/geysermc/geyser/translator/protocol/java/level/JavaGameEventTranslator.java @@ -47,21 +47,23 @@ import org.geysermc.geyser.translator.protocol.Translator; @Translator(packet = ClientboundGameEventPacket.class) public class JavaGameEventTranslator extends PacketTranslator { + // Strength of rainstorms and thunderstorms is a 0-1 float on Java, while on Bedrock it is a 0-65535 int + private static final int MAX_STORM_STRENGTH = 65535; @Override public void translate(GeyserSession session, ClientboundGameEventPacket packet) { PlayerEntity entity = session.getPlayerEntity(); switch (packet.getNotification()) { + // Yes, START_RAIN and STOP_RAIN are swapped in terms of what they cause the client to do. + // This is how the Mojang mappings name them, so we go with it + // It seems Mojang's intent was that START_RAIN would set the rain strength to 0 so that it can then be incremeneted on a gradient by the server + // The inverse is true for STOP_RAIN + // This is indeed the behavior of the vanilla server + // However, it seems most server software (at least Spigot and Paper) did not go along with this + // As a result many developers use these packets for the opposite of what their names implies + // Behavior last verified with Java 1.19.4 and Bedrock 1.19.71 case START_RAIN: - LevelEventPacket startRainPacket = new LevelEventPacket(); - startRainPacket.setType(LevelEventType.START_RAINING); - startRainPacket.setData(Integer.MAX_VALUE); - startRainPacket.setPosition(Vector3f.ZERO); - session.sendUpstreamPacket(startRainPacket); - session.setRaining(true); - break; - case STOP_RAIN: LevelEventPacket stopRainPacket = new LevelEventPacket(); stopRainPacket.setType(LevelEventType.STOP_RAINING); stopRainPacket.setData(0); @@ -69,34 +71,35 @@ public class JavaGameEventTranslator extends PacketTranslator 0f; - // Java sends the rain level. Bedrock doesn't care, so we don't care if it's already raining. - if (isCurrentlyRaining != session.isRaining()) { - LevelEventPacket changeRainPacket = new LevelEventPacket(); - changeRainPacket.setType(isCurrentlyRaining ? LevelEventType.START_RAINING : LevelEventType.STOP_RAINING); - changeRainPacket.setData(Integer.MAX_VALUE); // Dunno what this does; used to be implemented with ThreadLocalRandom - changeRainPacket.setPosition(Vector3f.ZERO); - session.sendUpstreamPacket(changeRainPacket); - session.setRaining(isCurrentlyRaining); - } + float rainStrength = ((RainStrengthValue) packet.getValue()).getStrength(); + boolean isCurrentlyRaining = rainStrength > 0f; + LevelEventPacket changeRainPacket = new LevelEventPacket(); + changeRainPacket.setType(isCurrentlyRaining ? LevelEventType.START_RAINING : LevelEventType.STOP_RAINING); + // This is the rain strength on LevelEventType.START_RAINING, but can be any value on LevelEventType.STOP_RAINING + changeRainPacket.setData((int) (rainStrength * MAX_STORM_STRENGTH)); + changeRainPacket.setPosition(Vector3f.ZERO); + session.sendUpstreamPacket(changeRainPacket); + session.setRaining(isCurrentlyRaining); break; case THUNDER_STRENGTH: // See above, same process - ThunderStrengthValue thunderValue = (ThunderStrengthValue) packet.getValue(); - boolean isCurrentlyThundering = thunderValue.getStrength() > 0f; - if (isCurrentlyThundering != session.isThunder()) { - LevelEventPacket changeThunderPacket = new LevelEventPacket(); - changeThunderPacket.setType(isCurrentlyThundering ? LevelEventType.START_THUNDERSTORM : LevelEventType.STOP_THUNDERSTORM); - changeThunderPacket.setData(Integer.MAX_VALUE); - changeThunderPacket.setPosition(Vector3f.ZERO); - session.sendUpstreamPacket(changeThunderPacket); - session.setThunder(isCurrentlyThundering); - } + float thunderStrength = ((ThunderStrengthValue) packet.getValue()).getStrength(); + boolean isCurrentlyThundering = thunderStrength > 0f; + LevelEventPacket changeThunderPacket = new LevelEventPacket(); + changeThunderPacket.setType(isCurrentlyThundering ? LevelEventType.START_THUNDERSTORM : LevelEventType.STOP_THUNDERSTORM); + changeThunderPacket.setData((int) (thunderStrength * MAX_STORM_STRENGTH)); + changeThunderPacket.setPosition(Vector3f.ZERO); + session.sendUpstreamPacket(changeThunderPacket); + session.setThunder(isCurrentlyThundering); break; case CHANGE_GAMEMODE: GameMode gameMode = (GameMode) packet.getValue(); From 7474d2c74565823842dbc251f75736bdbd4119ef Mon Sep 17 00:00:00 2001 From: onebeastchris Date: Sun, 26 Mar 2023 03:14:04 +0200 Subject: [PATCH 48/59] show sweeping edge enchantment (#3615) --- .../item/nbt/EnchantmentTranslator.java | 61 +++++++++++++------ 1 file changed, 43 insertions(+), 18 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 204981965..5a61b483d 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 @@ -25,11 +25,14 @@ package org.geysermc.geyser.translator.inventory.item.nbt; +import com.github.steveice10.mc.protocol.data.game.Identifier; import com.github.steveice10.opennbt.tag.builtin.*; import org.geysermc.geyser.GeyserImpl; import org.geysermc.geyser.inventory.item.Enchantment; import org.geysermc.geyser.registry.type.ItemMapping; import org.geysermc.geyser.session.GeyserSession; +import org.geysermc.geyser.text.ChatColor; +import org.geysermc.geyser.text.MinecraftLocale; import org.geysermc.geyser.translator.inventory.item.ItemRemapper; import org.geysermc.geyser.translator.inventory.item.NbtItemStackTranslator; @@ -43,28 +46,27 @@ public class EnchantmentTranslator extends NbtItemStackTranslator { @Override public void translateToBedrock(GeyserSession session, CompoundTag itemTag, ItemMapping mapping) { List newTags = new ArrayList<>(); - Tag enchantmentTag = itemTag.get("Enchantments"); + Tag enchantmentTag = itemTag.remove("Enchantments"); if (enchantmentTag instanceof ListTag listTag) { for (Tag tag : listTag.getValue()) { if (!(tag instanceof CompoundTag)) continue; - - CompoundTag bedrockTag = remapEnchantment((CompoundTag) tag); - newTags.add(bedrockTag); - } - itemTag.remove("Enchantments"); - } - enchantmentTag = itemTag.get("StoredEnchantments"); - if (enchantmentTag instanceof ListTag listTag) { - for (Tag tag : listTag.getValue()) { - if (!(tag instanceof CompoundTag)) continue; - - CompoundTag bedrockTag = remapEnchantment((CompoundTag) tag); + CompoundTag bedrockTag = remapEnchantment(session, (CompoundTag) tag, itemTag); + if (bedrockTag != null) { + newTags.add(bedrockTag); + } + } + } + + // TODO consolidate this into EnchantedBookTranslator + enchantmentTag = itemTag.remove("StoredEnchantments"); + if (enchantmentTag instanceof ListTag listTag) { + for (Tag tag : listTag.getValue()) { + if (!(tag instanceof CompoundTag)) continue; + CompoundTag bedrockTag = remapEnchantment(session, (CompoundTag) tag, itemTag); if (bedrockTag != null) { - bedrockTag.put(new ShortTag("GeyserStoredEnchantment", (short) 0)); newTags.add(bedrockTag); } } - itemTag.remove("StoredEnchantments"); } if (!newTags.isEmpty()) { @@ -99,7 +101,6 @@ public class EnchantmentTranslator extends NbtItemStackTranslator { javaValue.put("lvl", new IntTag("lvl", levelTag != null ? levelTag.getValue() : 1)); javaTag.setValue(javaValue); - if (geyserStoredEnchantmentTag != null) { tagValue.remove("GeyserStoredEnchantment"); storedEnchantments.add(javaTag); @@ -120,13 +121,20 @@ public class EnchantmentTranslator extends NbtItemStackTranslator { } - private CompoundTag remapEnchantment(CompoundTag tag) { + private CompoundTag remapEnchantment(GeyserSession session, CompoundTag tag, CompoundTag rootTag) { Tag javaEnchId = tag.get("id"); if (!(javaEnchId instanceof StringTag)) return null; Enchantment enchantment = Enchantment.getByJavaIdentifier(((StringTag) javaEnchId).getValue()); if (enchantment == null) { + if (Identifier.formalize((String) javaEnchId.getValue()).equals("minecraft:sweeping")) { + Tag javaEnchLvl = tag.get("lvl"); + int sweepingLvl = javaEnchLvl != null && javaEnchLvl.getValue() instanceof Number lvl ? lvl.intValue() : 0; + + addSweeping(session, rootTag, sweepingLvl); + return null; + } GeyserImpl.getInstance().getLogger().debug("Unknown Java enchantment while NBT item translating: " + javaEnchId.getValue()); return null; } @@ -140,4 +148,21 @@ public class EnchantmentTranslator extends NbtItemStackTranslator { return bedrockTag; } -} + private void addSweeping(GeyserSession session, CompoundTag itemTag, int level) { + CompoundTag displayTag = itemTag.get("display"); + if (displayTag == null) { + displayTag = new CompoundTag("display"); + itemTag.put(displayTag); + } + ListTag loreTag = displayTag.get("Lore"); + if (loreTag == null) { + loreTag = new ListTag("Lore"); + displayTag.put(loreTag); + } + + String sweepingTranslation = MinecraftLocale.getLocaleString("enchantment.minecraft.sweeping", session.locale()); + String lvlTranslation = MinecraftLocale.getLocaleString("enchantment.level." + level, session.locale()); + + loreTag.add(new StringTag("", ChatColor.RESET + ChatColor.GRAY + sweepingTranslation + " " + lvlTranslation)); + } +} \ No newline at end of file From 323394d2b50d6df5bab1b483a337f045e908300d Mon Sep 17 00:00:00 2001 From: Camotoy <20743703+Camotoy@users.noreply.github.com> Date: Wed, 22 Mar 2023 11:10:04 -0400 Subject: [PATCH 49/59] Clean up jar file classes a bit --- bootstrap/fabric/build.gradle.kts | 16 ++++++++++++++++ bootstrap/spigot/build.gradle.kts | 1 + .../geyser.shadow-conventions.gradle.kts | 5 +++++ .../geyser/session/GeyserSession.java | 19 ++++++++++++++++--- 4 files changed, 38 insertions(+), 3 deletions(-) diff --git a/bootstrap/fabric/build.gradle.kts b/bootstrap/fabric/build.gradle.kts index 890f5d656..af9c4547d 100644 --- a/bootstrap/fabric/build.gradle.kts +++ b/bootstrap/fabric/build.gradle.kts @@ -66,6 +66,22 @@ tasks { relocate("org.yaml", "org.geysermc.relocate.yaml") // https://github.com/CardboardPowered/cardboard/issues/139 relocate("com.fasterxml.jackson", "org.geysermc.relocate.jackson") relocate("net.kyori", "org.geysermc.relocate.kyori") + + dependencies { + // Exclude everything EXCEPT KQueue and some DNS stuff required for HAProxyc + exclude(dependency("io.netty:netty-transport-classes-epoll:.*")) + exclude(dependency("io.netty:netty-transport-native-epoll:.*")) + exclude(dependency("io.netty:netty-transport-native-unix-common:.*")) + exclude(dependency("io.netty:netty-transport-native-kqueue:.*")) + exclude(dependency("io.netty:netty-handler:.*")) + exclude(dependency("io.netty:netty-common:.*")) + exclude(dependency("io.netty:netty-buffer:.*")) + exclude(dependency("io.netty:netty-resolver:.*")) + exclude(dependency("io.netty:netty-transport:.*")) + exclude(dependency("io.netty:netty-codec:.*")) + exclude(dependency("io.netty:netty-resolver-dns:.*")) + exclude(dependency("io.netty:netty-resolver-dns-native-macos:.*")) + } } remapJar { diff --git a/bootstrap/spigot/build.gradle.kts b/bootstrap/spigot/build.gradle.kts index b5ef4e69e..da4e5af33 100644 --- a/bootstrap/spigot/build.gradle.kts +++ b/bootstrap/spigot/build.gradle.kts @@ -44,6 +44,7 @@ tasks.withType { // We cannot shade Netty, or else native libraries will not load // Needed because older Spigot builds do not provide the haproxy module + exclude(dependency("io.netty:netty-transport-classes-epoll:.*")) exclude(dependency("io.netty:netty-transport-native-epoll:.*")) exclude(dependency("io.netty:netty-transport-native-unix-common:.*")) exclude(dependency("io.netty:netty-transport-native-kqueue:.*")) diff --git a/build-logic/src/main/kotlin/geyser.shadow-conventions.gradle.kts b/build-logic/src/main/kotlin/geyser.shadow-conventions.gradle.kts index 395beb104..dde85c33a 100644 --- a/build-logic/src/main/kotlin/geyser.shadow-conventions.gradle.kts +++ b/build-logic/src/main/kotlin/geyser.shadow-conventions.gradle.kts @@ -24,6 +24,11 @@ tasks { exclude(dependency(string)) } } + + sJar.dependencies { + exclude(dependency("org.checkerframework:checker-qual:.*")) + exclude(dependency("org.jetbrains:annotations:.*")) + } } } named("build") { diff --git a/core/src/main/java/org/geysermc/geyser/session/GeyserSession.java b/core/src/main/java/org/geysermc/geyser/session/GeyserSession.java index d76cc4b97..e245d0f56 100644 --- a/core/src/main/java/org/geysermc/geyser/session/GeyserSession.java +++ b/core/src/main/java/org/geysermc/geyser/session/GeyserSession.java @@ -732,7 +732,11 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource { return; } - connectDownstream(); + try { + connectDownstream(); + } catch (Throwable t) { + t.printStackTrace(); + } }); } @@ -776,7 +780,11 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource { return; } - connectDownstream(); + try { + connectDownstream(); + } catch (Throwable t) { + t.printStackTrace(); + } }); } @@ -850,7 +858,12 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource { selectedProfile, service.getAccessToken() ); - connectDownstream(); + try { + connectDownstream(); + } catch (Throwable t) { + t.printStackTrace(); + return false; + } // Save our refresh token for later use geyser.saveRefreshToken(bedrockUsername(), service.getRefreshToken()); From a7c04d53237a1d3d6ebea8063638d4f776452585 Mon Sep 17 00:00:00 2001 From: Camotoy <20743703+Camotoy@users.noreply.github.com> Date: Sun, 26 Mar 2023 13:20:39 -0400 Subject: [PATCH 50/59] Fix #3619 --- .../geyser/registry/populator/ItemRegistryPopulator.java | 1 + 1 file changed, 1 insertion(+) 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 9b4ae99a2..427ab2454 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 @@ -286,6 +286,7 @@ public class ItemRegistryPopulator { Set javaOnlyItems = new ObjectOpenHashSet<>(); Collections.addAll(javaOnlyItems, "minecraft:spectral_arrow", "minecraft:debug_stick", "minecraft:knowledge_book", "minecraft:tipped_arrow", "minecraft:bundle"); + javaOnlyItems.add("minecraft:decorated_pot"); // TODO 1.19.80 resolve probs? if (!customItemsAllowed) { javaOnlyItems.add("minecraft:furnace_minecart"); } From 2a8d8b6cdfcfbcd633262bf380fab3ca58597e86 Mon Sep 17 00:00:00 2001 From: Kas-tle <26531652+Kas-tle@users.noreply.github.com> Date: Tue, 28 Mar 2023 08:18:21 -0700 Subject: [PATCH 51/59] Set Shulker Color by Default (#3648) --- .../geyser/entity/type/living/monster/ShulkerEntity.java | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/core/src/main/java/org/geysermc/geyser/entity/type/living/monster/ShulkerEntity.java b/core/src/main/java/org/geysermc/geyser/entity/type/living/monster/ShulkerEntity.java index ff1ba9ac3..e484dfc59 100644 --- a/core/src/main/java/org/geysermc/geyser/entity/type/living/monster/ShulkerEntity.java +++ b/core/src/main/java/org/geysermc/geyser/entity/type/living/monster/ShulkerEntity.java @@ -43,6 +43,15 @@ public class ShulkerEntity extends GolemEntity { super(session, entityId, geyserId, uuid, definition, position, motion, yaw, pitch, headYaw); // Indicate that invisibility should be fixed through the resource pack setFlag(EntityFlag.BRIBED, true); + + } + + @Override + protected void initializeMetadata() { + super.initializeMetadata(); + // As of 1.19.4, it seems Java no longer sends the shulker color if it's the default color on initial spawn + // We still need the special case for 16 color in setShulkerColor though as it will send it for an entity metadata update + dirtyMetadata.put(EntityData.VARIANT, 16); } public void setAttachedFace(EntityMetadata entityMetadata) { From 45e043c6e9d229b57979b33d006a06d4e247f327 Mon Sep 17 00:00:00 2001 From: Tydium <67938521+Tydium@users.noreply.github.com> Date: Tue, 28 Mar 2023 14:35:22 -0400 Subject: [PATCH 52/59] Fix Custom Model Data not working on Potions. (#3616) --- .../inventory/item/PotionTranslator.java | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) 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 3e814a098..68985ae5e 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 @@ -48,15 +48,23 @@ public class PotionTranslator extends ItemTranslator { if (itemStack.getNbt() == null) return super.translateToBedrock(itemStack, mapping, mappings); Tag potionTag = itemStack.getNbt().get("Potion"); if (potionTag instanceof StringTag) { - Potion potion = Potion.getByJavaIdentifier(((StringTag) potionTag).getValue()); - if (potion != null) { + int customItemId = CustomItemTranslator.getCustomItem(itemStack.getNbt(), mapping); + if (customItemId == -1) { + Potion potion = Potion.getByJavaIdentifier(((StringTag) potionTag).getValue()); + if (potion != null) { + return ItemData.builder() + .id(mapping.getBedrockId()) + .damage(potion.getBedrockId()) + .count(itemStack.getAmount()) + .tag(translateNbtToBedrock(itemStack.getNbt())); + } + GeyserImpl.getInstance().getLogger().debug("Unknown Java potion: " + potionTag.getValue()); + } else { return ItemData.builder() - .id(mapping.getBedrockId()) - .damage(potion.getBedrockId()) + .id(customItemId) .count(itemStack.getAmount()) .tag(translateNbtToBedrock(itemStack.getNbt())); } - GeyserImpl.getInstance().getLogger().debug("Unknown Java potion: " + potionTag.getValue()); } return super.translateToBedrock(itemStack, mapping, mappings); } From 95236b37e05b53ce1cdd7f279672c62f7c0bc9a6 Mon Sep 17 00:00:00 2001 From: Camotoy <20743703+Camotoy@users.noreply.github.com> Date: Tue, 28 Mar 2023 18:48:48 -0400 Subject: [PATCH 53/59] Add support for basic display entities --- .../geyser/entity/EntityDefinitions.java | 23 ++++++++ .../geyser/entity/type/TextDisplayEntity.java | 55 +++++++++++++++++++ 2 files changed, 78 insertions(+) create mode 100644 core/src/main/java/org/geysermc/geyser/entity/type/TextDisplayEntity.java diff --git a/core/src/main/java/org/geysermc/geyser/entity/EntityDefinitions.java b/core/src/main/java/org/geysermc/geyser/entity/EntityDefinitions.java index 41a88f64f..8656be098 100644 --- a/core/src/main/java/org/geysermc/geyser/entity/EntityDefinitions.java +++ b/core/src/main/java/org/geysermc/geyser/entity/EntityDefinitions.java @@ -148,6 +148,7 @@ public final class EntityDefinitions { public static final EntityDefinition STRAY; public static final EntityDefinition STRIDER; public static final EntityDefinition TADPOLE; + public static final EntityDefinition TEXT_DISPLAY; public static final EntityDefinition TNT; public static final EntityDefinition TNT_MINECART; public static final EntityDefinition TRADER_LLAMA; @@ -295,6 +296,28 @@ public final class EntityDefinitions { .addTranslator(MetadataType.INT, TNTEntity::setFuseLength) .build(); + EntityDefinition displayBase = EntityDefinition.inherited(entityBase.factory(), entityBase) + .addTranslator(null) // Interpolation start ticks + .addTranslator(null) // Interpolation duration ID + .addTranslator(null) // Translation + .addTranslator(null) // Scale + .addTranslator(null) // Left rotation + .addTranslator(null) // Right rotation + .addTranslator(null) // Billboard render constraints + .addTranslator(null) // Brightness override + .addTranslator(null) // View range + .addTranslator(null) // Shadow radius + .addTranslator(null) // Shadow strength + .addTranslator(null) // Width + .addTranslator(null) // Height + .addTranslator(null) // Glow color override + .build(); + TEXT_DISPLAY = EntityDefinition.inherited(TextDisplayEntity::new, displayBase) + .type(EntityType.TEXT_DISPLAY) + .identifier("minecraft:armor_stand") + .addTranslator(MetadataType.CHAT, TextDisplayEntity::setText) + .build(); + EntityDefinition fireballBase = EntityDefinition.inherited(FireballEntity::new, entityBase) .addTranslator(null) // Item .build(); diff --git a/core/src/main/java/org/geysermc/geyser/entity/type/TextDisplayEntity.java b/core/src/main/java/org/geysermc/geyser/entity/type/TextDisplayEntity.java new file mode 100644 index 000000000..fecca8ac0 --- /dev/null +++ b/core/src/main/java/org/geysermc/geyser/entity/type/TextDisplayEntity.java @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2019-2023 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.entity.type; + +import com.github.steveice10.mc.protocol.data.game.entity.metadata.EntityMetadata; +import com.nukkitx.math.vector.Vector3f; +import com.nukkitx.protocol.bedrock.data.entity.EntityData; +import net.kyori.adventure.text.Component; +import org.geysermc.geyser.entity.EntityDefinition; +import org.geysermc.geyser.session.GeyserSession; +import org.geysermc.geyser.translator.text.MessageTranslator; + +import java.util.UUID; + +// Note: 1.19.4 requires that the billboard is set to something in order to show, on Java Edition +public class TextDisplayEntity extends Entity { + public TextDisplayEntity(GeyserSession session, int entityId, long geyserId, UUID uuid, EntityDefinition definition, Vector3f position, Vector3f motion, float yaw, float pitch, float headYaw) { + super(session, entityId, geyserId, uuid, definition, position, motion, yaw, pitch, headYaw); + } + + @Override + protected void initializeMetadata() { + super.initializeMetadata(); + // Remove armor stand body + this.dirtyMetadata.put(EntityData.SCALE, 0f); + this.dirtyMetadata.put(EntityData.NAMETAG_ALWAYS_SHOW, (byte) 1); + } + + public void setText(EntityMetadata entityMetadata) { + this.dirtyMetadata.put(EntityData.NAMETAG, MessageTranslator.convertMessage(entityMetadata.getValue())); + } +} From ed99655ed9f1b666aa37f48c57cf6e1ed54370dc Mon Sep 17 00:00:00 2001 From: Kas-tle <26531652+Kas-tle@users.noreply.github.com> Date: Thu, 30 Mar 2023 08:02:12 -0700 Subject: [PATCH 54/59] Do Modrinth Publish in build.yml (#3644) * Migrate modrinth publish and edit caching Signed-off-by: Joshua Castle <26531652+Kas-tle@users.noreply.github.com> * Address review from tim --------- Signed-off-by: Joshua Castle <26531652+Kas-tle@users.noreply.github.com> --- .github/workflows/build.yml | 29 ++++++++++++++++------------- .github/workflows/publish.yml | 31 ------------------------------- 2 files changed, 16 insertions(+), 44 deletions(-) delete mode 100644 .github/workflows/publish.yml diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 668c9ca72..ca8b63cc4 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -23,25 +23,19 @@ jobs: with: submodules: recursive + - name: Validate Gradle Wrapper + uses: gradle/wrapper-validation-action@v1 + - uses: actions/setup-java@v3 with: java-version: 17 distribution: temurin - - name: Cache Gradle Packages - uses: actions/cache@v3 - with: - path: | - ~/.m2 - ~/.gradle/caches - ~/.gradle/wrapper - key: ${{ github.ref_name }}-gradle-${{ hashFiles('*.gradle.kts', 'gradle.properties', 'gradlew', 'gradle/*', 'gradle/**/*', 'build-logic/*', 'build-logic/**/**/**/*', '**/*.gradle.kts', '**/**/*.gradle.kts') }} - restore-keys: ${{ github.ref_name }}-gradle- - - name: Build uses: gradle/gradle-build-action@v2 with: - arguments: build --no-daemon + arguments: build + gradle-home-cache-cleanup: true - name: Archive artifacts (Geyser Fabric) uses: actions/upload-artifact@v3 @@ -87,7 +81,7 @@ jobs: if-no-files-found: error - name: Publish to Maven Repository - if: ${{ job.status == 'success' && github.repository == 'GeyserMC/Geyser' && github.ref_name == 'master' }} + if: ${{ success() && github.repository == 'GeyserMC/Geyser' && github.ref_name == 'master' }} uses: gradle/gradle-build-action@v2 env: ORG_GRADLE_PROJECT_geysermcUsername: ${{ vars.DEPLOY_USER }} @@ -96,7 +90,7 @@ jobs: arguments: publish - name: Publish to Downloads API - if: ${{ github.ref_name == 'master' && job.status == 'success' && github.repository == 'GeyserMC/Geyser' }} + if: ${{ success() && github.repository == 'GeyserMC/Geyser' && github.ref_name == 'master' }} shell: bash env: DOWNLOADS_USERNAME: ${{ vars.DOWNLOADS_USERNAME }} @@ -119,6 +113,15 @@ jobs: echo "{\"project\": \"$project\", \"version\": \"$version\", \"id\": $GITHUB_RUN_NUMBER, \"commit\": \"$GITHUB_SHA\"}" > metadata.json rsync -P -e "ssh -o StrictHostKeyChecking=no -i id_ecdsa" metadata.json $DOWNLOADS_USERNAME@$DOWNLOADS_SERVER_IP:~/uploads/$project/$GITHUB_RUN_NUMBER/ + - name: Publish to Modrinth + uses: gradle/gradle-build-action@v2 + if: ${{ success() && github.repository == 'GeyserMC/Geyser' && github.ref_name == 'master' }} + env: + MODRINTH_TOKEN: ${{ secrets.MODRINTH_TOKEN }} + with: + arguments: fabric:modrinth + gradle-home-cache-cleanup: true + - name: Notify Discord if: ${{ (success() || failure()) && github.repository == 'GeyserMC/Geyser' }} uses: Tim203/actions-git-discord-webhook@main diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml deleted file mode 100644 index 2b002b0de..000000000 --- a/.github/workflows/publish.yml +++ /dev/null @@ -1,31 +0,0 @@ -name: Publish -on: - workflow_dispatch: - push: - paths-ignore: - - '.github/ISSUE_TEMPLATE/*.yml' - - '.github/actions/pullrequest.yml' - - '.idea/copyright/*.xml' - - '.gitignore' - - 'CONTRIBUTING.md' - - 'LICENSE' - - 'Jenkinsfile ' - - 'README.md' - - 'licenseheader.txt' - -jobs: - build: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v3 - with: - submodules: recursive - - uses: gradle/wrapper-validation-action@v1 - - uses: actions/setup-java@v3 - with: - distribution: 'temurin' - java-version: 17 - - name: Publish to Modrinth - env: - MODRINTH_TOKEN: ${{ secrets.MODRINTH_TOKEN }} - run: ./gradlew fabric:modrinth \ No newline at end of file From cd80ee893cb8c2c5e3482971395434a2b59831f7 Mon Sep 17 00:00:00 2001 From: Camotoy <20743703+Camotoy@users.noreply.github.com> Date: Thu, 30 Mar 2023 15:27:00 -0400 Subject: [PATCH 55/59] Potentially fix Modrinth token caching --- bootstrap/fabric/build.gradle.kts | 1 + 1 file changed, 1 insertion(+) diff --git a/bootstrap/fabric/build.gradle.kts b/bootstrap/fabric/build.gradle.kts index af9c4547d..35270df80 100644 --- a/bootstrap/fabric/build.gradle.kts +++ b/bootstrap/fabric/build.gradle.kts @@ -94,6 +94,7 @@ tasks { } modrinth { + token.set(System.getenv("MODRINTH_TOKEN")) // Even though this is the default value, apparently this prevents GitHub Actions caching the token? projectId.set("wKkoqHrH") versionNumber.set(project.version as String + "-" + System.getenv("GITHUB_RUN_NUMBER")) versionType.set("beta") From e2535108e6801ecf4f2a3fe276ced7e4d021d8b3 Mon Sep 17 00:00:00 2001 From: Camotoy <20743703+Camotoy@users.noreply.github.com> Date: Thu, 30 Mar 2023 15:44:55 -0400 Subject: [PATCH 56/59] Folia support and preparations for future changes --- .../world/GeyserFabricWorldManager.java | 139 +++++++----- bootstrap/spigot/build.gradle.kts | 7 +- .../spigot/GeyserPaperPingPassthrough.java | 4 +- .../spigot/GeyserSpigotPingPassthrough.java | 2 +- .../platform/spigot/ReflectedNames.java | 8 +- .../spigot/world/GeyserPistonListener.java | 4 +- .../manager/GeyserSpigotWorldManager.java | 180 +++++++-------- .../spigot/src/main/resources/plugin.yml | 1 + core/build.gradle.kts | 4 + .../java/org/geysermc/geyser/GeyserImpl.java | 16 ++ .../geyser/entity/type/FishingHookEntity.java | 3 +- .../geyser/entity/type/ItemEntity.java | 11 +- .../entity/type/living/SquidEntity.java | 13 +- .../AbstractGeyserboundPacketHandler.java | 88 ++++++++ .../erosion/GeyserErosionPacketSender.java | 60 +++++ .../GeyserboundHandshakePacketHandler.java | 72 ++++++ .../erosion/GeyserboundPacketHandlerImpl.java | 209 ++++++++++++++++++ .../erosion/UnixSocketClientListener.java | 70 ++++++ .../geyser/level/GeyserWorldManager.java | 92 +++++++- .../geysermc/geyser/level/WorldManager.java | 39 +++- .../level/block/BlockPositionIterator.java | 80 ------- .../level/physics/CollisionManager.java | 11 +- .../geysermc/geyser/pack/ResourcePack.java | 20 +- .../geyser/session/GeyserSession.java | 15 +- .../inventory/LecternInventoryTranslator.java | 23 +- .../inventory/item/nbt/BannerTranslator.java | 13 +- .../level/block/entity/PistonBlockEntity.java | 2 +- .../BedrockBlockPickRequestTranslator.java | 32 ++- .../java/JavaCustomPayloadTranslator.java | 129 ++++++----- .../protocol/java/JavaLoginTranslator.java | 10 + .../java/level/JavaBlockEventTranslator.java | 9 +- .../java/level/JavaBlockUpdateTranslator.java | 2 +- .../level/JavaForgetLevelChunkTranslator.java | 2 +- .../JavaLevelChunkWithLightTranslator.java | 22 +- .../util/collection/LecternHasBookMap.java | 17 +- gradle/libs.versions.toml | 12 +- 36 files changed, 999 insertions(+), 422 deletions(-) create mode 100644 core/src/main/java/org/geysermc/geyser/erosion/AbstractGeyserboundPacketHandler.java create mode 100644 core/src/main/java/org/geysermc/geyser/erosion/GeyserErosionPacketSender.java create mode 100644 core/src/main/java/org/geysermc/geyser/erosion/GeyserboundHandshakePacketHandler.java create mode 100644 core/src/main/java/org/geysermc/geyser/erosion/GeyserboundPacketHandlerImpl.java create mode 100644 core/src/main/java/org/geysermc/geyser/erosion/UnixSocketClientListener.java delete mode 100644 core/src/main/java/org/geysermc/geyser/level/block/BlockPositionIterator.java diff --git a/bootstrap/fabric/src/main/java/org/geysermc/geyser/platform/fabric/world/GeyserFabricWorldManager.java b/bootstrap/fabric/src/main/java/org/geysermc/geyser/platform/fabric/world/GeyserFabricWorldManager.java index b003a76ba..dc81315d2 100644 --- a/bootstrap/fabric/src/main/java/org/geysermc/geyser/platform/fabric/world/GeyserFabricWorldManager.java +++ b/bootstrap/fabric/src/main/java/org/geysermc/geyser/platform/fabric/world/GeyserFabricWorldManager.java @@ -25,6 +25,7 @@ package org.geysermc.geyser.platform.fabric.world; +import com.github.steveice10.mc.protocol.data.game.level.block.BlockEntityInfo; import com.nukkitx.math.vector.Vector3i; import com.nukkitx.nbt.NbtMap; import com.nukkitx.nbt.NbtMapBuilder; @@ -40,16 +41,16 @@ import net.minecraft.world.item.WrittenBookItem; import net.minecraft.world.level.block.entity.BannerBlockEntity; import net.minecraft.world.level.block.entity.BlockEntity; import net.minecraft.world.level.block.entity.LecternBlockEntity; +import net.minecraft.world.level.chunk.LevelChunk; +import org.geysermc.erosion.util.LecternUtils; import org.geysermc.geyser.level.GeyserWorldManager; import org.geysermc.geyser.session.GeyserSession; -import org.geysermc.geyser.translator.inventory.LecternInventoryTranslator; import org.geysermc.geyser.util.BlockEntityUtils; import javax.annotation.Nonnull; import java.util.ArrayList; import java.util.List; import java.util.concurrent.CompletableFuture; -import java.util.concurrent.TimeUnit; public class GeyserFabricWorldManager extends GeyserWorldManager { private final MinecraftServer server; @@ -59,69 +60,91 @@ public class GeyserFabricWorldManager extends GeyserWorldManager { } @Override - public boolean shouldExpectLecternHandled() { + public boolean shouldExpectLecternHandled(GeyserSession session) { return true; } @Override - public NbtMap getLecternDataAt(GeyserSession session, int x, int y, int z, boolean isChunkLoad) { - Runnable lecternGet = () -> { - // Mostly a reimplementation of Spigot lectern support + public void sendLecternData(GeyserSession session, int x, int z, List blockEntityInfos) { + server.execute(() -> { ServerPlayer player = getPlayer(session); - if (player != null) { - BlockEntity blockEntity = player.level.getBlockEntity(new BlockPos(x, y, z)); - if (!(blockEntity instanceof LecternBlockEntity lectern)) { - return; - } - - if (!lectern.hasBook()) { - if (!isChunkLoad) { - BlockEntityUtils.updateBlockEntity(session, LecternInventoryTranslator.getBaseLecternTag(x, y, z, 0).build(), Vector3i.from(x, y, z)); - } - return; - } - - ItemStack book = lectern.getBook(); - int pageCount = WrittenBookItem.getPageCount(book); - boolean hasBookPages = pageCount > 0; - NbtMapBuilder lecternTag = LecternInventoryTranslator.getBaseLecternTag(x, y, z, hasBookPages ? pageCount : 1); - lecternTag.putInt("page", lectern.getPage() / 2); - NbtMapBuilder bookTag = NbtMap.builder() - .putByte("Count", (byte) book.getCount()) - .putShort("Damage", (short) 0) - .putString("Name", "minecraft:writable_book"); - List pages = new ArrayList<>(hasBookPages ? pageCount : 1); - if (hasBookPages && WritableBookItem.makeSureTagIsValid(book.getTag())) { - ListTag listTag = book.getTag().getList("pages", 8); - - for (int i = 0; i < listTag.size(); i++) { - String page = listTag.getString(i); - NbtMapBuilder pageBuilder = NbtMap.builder() - .putString("photoname", "") - .putString("text", page); - pages.add(pageBuilder.build()); - } - } else { - // Empty page - NbtMapBuilder pageBuilder = NbtMap.builder() - .putString("photoname", "") - .putString("text", ""); - pages.add(pageBuilder.build()); - } - - bookTag.putCompound("tag", NbtMap.builder().putList("pages", NbtType.COMPOUND, pages).build()); - lecternTag.putCompound("book", bookTag.build()); - NbtMap blockEntityTag = lecternTag.build(); - BlockEntityUtils.updateBlockEntity(session, blockEntityTag, Vector3i.from(x, y, z)); + if (player == null) { + return; } - }; - if (isChunkLoad) { - // Hacky hacks to allow lectern loading to be delayed - session.scheduleInEventLoop(() -> server.execute(lecternGet), 1, TimeUnit.SECONDS); - } else { - server.execute(lecternGet); + + LevelChunk chunk = player.getLevel().getChunk(x, z); + final int chunkBlockX = x << 4; + final int chunkBlockZ = z << 4; + for (int i = 0; i < blockEntityInfos.size(); i++) { + BlockEntityInfo blockEntityInfo = blockEntityInfos.get(i); + BlockEntity blockEntity = chunk.getBlockEntity(new BlockPos(chunkBlockX + blockEntityInfo.getX(), + blockEntityInfo.getY(), chunkBlockZ + blockEntityInfo.getZ())); + sendLecternData(session, blockEntity, true); + } + }); + } + + @Override + public void sendLecternData(GeyserSession session, int x, int y, int z) { + server.execute(() -> { + ServerPlayer player = getPlayer(session); + if (player == null) { + return; + } + + BlockEntity blockEntity = player.level.getBlockEntity(new BlockPos(x, y, z)); + sendLecternData(session, blockEntity, false); + }); + } + + private void sendLecternData(GeyserSession session, BlockEntity blockEntity, boolean isChunkLoad) { + if (!(blockEntity instanceof LecternBlockEntity lectern)) { + return; } - return LecternInventoryTranslator.getBaseLecternTag(x, y, z, 0).build(); + + int x = blockEntity.getBlockPos().getX(); + int y = blockEntity.getBlockPos().getY(); + int z = blockEntity.getBlockPos().getZ(); + + if (!lectern.hasBook()) { + if (!isChunkLoad) { + BlockEntityUtils.updateBlockEntity(session, LecternUtils.getBaseLecternTag(x, y, z, 0).build(), Vector3i.from(x, y, z)); + } + return; + } + + ItemStack book = lectern.getBook(); + int pageCount = WrittenBookItem.getPageCount(book); + boolean hasBookPages = pageCount > 0; + NbtMapBuilder lecternTag = LecternUtils.getBaseLecternTag(x, y, z, hasBookPages ? pageCount : 1); + lecternTag.putInt("page", lectern.getPage() / 2); + NbtMapBuilder bookTag = NbtMap.builder() + .putByte("Count", (byte) book.getCount()) + .putShort("Damage", (short) 0) + .putString("Name", "minecraft:writable_book"); + List pages = new ArrayList<>(hasBookPages ? pageCount : 1); + if (hasBookPages && WritableBookItem.makeSureTagIsValid(book.getTag())) { + ListTag listTag = book.getTag().getList("pages", 8); + + for (int i = 0; i < listTag.size(); i++) { + String page = listTag.getString(i); + NbtMapBuilder pageBuilder = NbtMap.builder() + .putString("photoname", "") + .putString("text", page); + pages.add(pageBuilder.build()); + } + } else { + // Empty page + NbtMapBuilder pageBuilder = NbtMap.builder() + .putString("photoname", "") + .putString("text", ""); + pages.add(pageBuilder.build()); + } + + bookTag.putCompound("tag", NbtMap.builder().putList("pages", NbtType.COMPOUND, pages).build()); + lecternTag.putCompound("book", bookTag.build()); + NbtMap blockEntityTag = lecternTag.build(); + BlockEntityUtils.updateBlockEntity(session, blockEntityTag, Vector3i.from(x, y, z)); } @Override diff --git a/bootstrap/spigot/build.gradle.kts b/bootstrap/spigot/build.gradle.kts index da4e5af33..aa7958732 100644 --- a/bootstrap/spigot/build.gradle.kts +++ b/bootstrap/spigot/build.gradle.kts @@ -1,5 +1,8 @@ dependencies { api(projects.core) + api(libs.erosion.bukkit.common) { + isTransitive = false + } implementation(libs.adapters.spigot) @@ -7,8 +10,8 @@ dependencies { implementation(libs.adventure.text.serializer.bungeecord) - // Both paper-api and paper-mojangapi only provide Java 17 versions for 1.19 - compileOnly(libs.paper.api) { + // Both folia-api and paper-mojangapi only provide Java 17 versions for 1.19 + compileOnly(libs.folia.api) { attributes { attribute(TargetJvmVersion.TARGET_JVM_VERSION_ATTRIBUTE, 17) } diff --git a/bootstrap/spigot/src/main/java/org/geysermc/geyser/platform/spigot/GeyserPaperPingPassthrough.java b/bootstrap/spigot/src/main/java/org/geysermc/geyser/platform/spigot/GeyserPaperPingPassthrough.java index 36dd81d44..bb0f30e70 100644 --- a/bootstrap/spigot/src/main/java/org/geysermc/geyser/platform/spigot/GeyserPaperPingPassthrough.java +++ b/bootstrap/spigot/src/main/java/org/geysermc/geyser/platform/spigot/GeyserPaperPingPassthrough.java @@ -59,13 +59,13 @@ public final class GeyserPaperPingPassthrough implements IGeyserPingPassthrough // runtime because we still have to shade in our own Adventure class. For now. PaperServerListPingEvent event; if (OLD_CONSTRUCTOR != null) { - // Approximately pre-1.19 + // 1.19, removed in 1.19.4 event = OLD_CONSTRUCTOR.newInstance(new GeyserStatusClient(inetSocketAddress), Bukkit.getMotd(), Bukkit.getOnlinePlayers().size(), Bukkit.getMaxPlayers(), Bukkit.getVersion(), GameProtocol.getJavaProtocolVersion(), null); } else { event = new PaperServerListPingEvent(new GeyserStatusClient(inetSocketAddress), - Bukkit.getMotd(), Bukkit.shouldSendChatPreviews(), Bukkit.getOnlinePlayers().size(), + Bukkit.getMotd(), Bukkit.getOnlinePlayers().size(), Bukkit.getMaxPlayers(), Bukkit.getVersion(), GameProtocol.getJavaProtocolVersion(), null); } Bukkit.getPluginManager().callEvent(event); diff --git a/bootstrap/spigot/src/main/java/org/geysermc/geyser/platform/spigot/GeyserSpigotPingPassthrough.java b/bootstrap/spigot/src/main/java/org/geysermc/geyser/platform/spigot/GeyserSpigotPingPassthrough.java index 634d1f8a8..1e6a0ad6c 100644 --- a/bootstrap/spigot/src/main/java/org/geysermc/geyser/platform/spigot/GeyserSpigotPingPassthrough.java +++ b/bootstrap/spigot/src/main/java/org/geysermc/geyser/platform/spigot/GeyserSpigotPingPassthrough.java @@ -66,7 +66,7 @@ public class GeyserSpigotPingPassthrough implements IGeyserPingPassthrough { private static class GeyserPingEvent extends ServerListPingEvent { public GeyserPingEvent(InetAddress address, String motd, int numPlayers, int maxPlayers) { - super(address, motd, Bukkit.shouldSendChatPreviews(), numPlayers, maxPlayers); + super("", address, motd, numPlayers, maxPlayers); } @Override diff --git a/bootstrap/spigot/src/main/java/org/geysermc/geyser/platform/spigot/ReflectedNames.java b/bootstrap/spigot/src/main/java/org/geysermc/geyser/platform/spigot/ReflectedNames.java index 3185f2d30..67e31fea2 100644 --- a/bootstrap/spigot/src/main/java/org/geysermc/geyser/platform/spigot/ReflectedNames.java +++ b/bootstrap/spigot/src/main/java/org/geysermc/geyser/platform/spigot/ReflectedNames.java @@ -60,16 +60,16 @@ public final class ReflectedNames { } static Constructor getOldPaperPingConstructor() { - if (getConstructor(PaperServerListPingEvent.class, StatusClient.class, String.class, boolean.class, int.class, + if (getConstructor(PaperServerListPingEvent.class, StatusClient.class, String.class, int.class, int.class, String.class, int.class, CachedServerIcon.class) != null) { - // @NotNull StatusClient client, @NotNull String motd, boolean shouldSendChatPreviews, int numPlayers, int maxPlayers, + // @NotNull StatusClient client, @NotNull String motd, int numPlayers, int maxPlayers, // @NotNull String version, int protocolVersion, @Nullable CachedServerIcon favicon // New constructor is present return null; } - // @NotNull StatusClient client, @NotNull String motd, int numPlayers, int maxPlayers, + // @NotNull StatusClient client, @NotNull String motd, boolean shouldSendChatPreviews, int numPlayers, int maxPlayers, // @NotNull String version, int protocolVersion, @Nullable CachedServerIcon favicon - return getConstructor(PaperServerListPingEvent.class, StatusClient.class, String.class, int.class, int.class, + return getConstructor(PaperServerListPingEvent.class, StatusClient.class, String.class, boolean.class, int.class, int.class, String.class, int.class, CachedServerIcon.class); } diff --git a/bootstrap/spigot/src/main/java/org/geysermc/geyser/platform/spigot/world/GeyserPistonListener.java b/bootstrap/spigot/src/main/java/org/geysermc/geyser/platform/spigot/world/GeyserPistonListener.java index 5eb99e10c..d7f34c4b3 100644 --- a/bootstrap/spigot/src/main/java/org/geysermc/geyser/platform/spigot/world/GeyserPistonListener.java +++ b/bootstrap/spigot/src/main/java/org/geysermc/geyser/platform/spigot/world/GeyserPistonListener.java @@ -27,8 +27,8 @@ package org.geysermc.geyser.platform.spigot.world; import com.github.steveice10.mc.protocol.data.game.level.block.value.PistonValueType; import com.nukkitx.math.vector.Vector3i; +import it.unimi.dsi.fastutil.objects.Object2IntArrayMap; import it.unimi.dsi.fastutil.objects.Object2IntMap; -import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap; import org.bukkit.Bukkit; import org.bukkit.Location; import org.bukkit.World; @@ -85,7 +85,7 @@ public class GeyserPistonListener implements Listener { PistonValueType type = isExtend ? PistonValueType.PUSHING : PistonValueType.PULLING; boolean sticky = event.isSticky(); - Object2IntMap attachedBlocks = new Object2IntOpenHashMap<>(); + Object2IntMap attachedBlocks = new Object2IntArrayMap<>(); boolean blocksFilled = false; for (Map.Entry entry : geyser.getSessionManager().getSessions().entrySet()) { diff --git a/bootstrap/spigot/src/main/java/org/geysermc/geyser/platform/spigot/world/manager/GeyserSpigotWorldManager.java b/bootstrap/spigot/src/main/java/org/geysermc/geyser/platform/spigot/world/manager/GeyserSpigotWorldManager.java index cca982cbb..481953747 100644 --- a/bootstrap/spigot/src/main/java/org/geysermc/geyser/platform/spigot/world/manager/GeyserSpigotWorldManager.java +++ b/bootstrap/spigot/src/main/java/org/geysermc/geyser/platform/spigot/world/manager/GeyserSpigotWorldManager.java @@ -25,33 +25,28 @@ package org.geysermc.geyser.platform.spigot.world.manager; +import com.github.steveice10.mc.protocol.data.game.level.block.BlockEntityInfo; 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.math.vector.Vector3i; import com.nukkitx.nbt.NbtMap; -import com.nukkitx.nbt.NbtMapBuilder; -import com.nukkitx.nbt.NbtType; import org.bukkit.Bukkit; +import org.bukkit.Chunk; import org.bukkit.World; -import org.bukkit.block.*; -import org.bukkit.block.banner.Pattern; +import org.bukkit.block.Block; import org.bukkit.entity.Player; -import org.bukkit.inventory.ItemStack; -import org.bukkit.inventory.meta.BookMeta; import org.bukkit.plugin.Plugin; +import org.geysermc.erosion.bukkit.BukkitLecterns; +import org.geysermc.erosion.bukkit.BukkitUtils; +import org.geysermc.erosion.bukkit.PickBlockUtils; +import org.geysermc.erosion.bukkit.SchedulerUtils; import org.geysermc.geyser.level.GameRule; import org.geysermc.geyser.level.WorldManager; import org.geysermc.geyser.level.block.BlockStateValues; import org.geysermc.geyser.registry.BlockRegistries; import org.geysermc.geyser.session.GeyserSession; -import org.geysermc.geyser.translator.inventory.LecternInventoryTranslator; -import org.geysermc.geyser.translator.inventory.item.nbt.BannerTranslator; import org.geysermc.geyser.util.BlockEntityUtils; import org.jetbrains.annotations.Nullable; import javax.annotation.Nonnull; -import java.util.ArrayList; import java.util.List; import java.util.concurrent.CompletableFuture; @@ -60,9 +55,11 @@ import java.util.concurrent.CompletableFuture; */ public class GeyserSpigotWorldManager extends WorldManager { private final Plugin plugin; + private final BukkitLecterns lecterns; public GeyserSpigotWorldManager(Plugin plugin) { this.plugin = plugin; + this.lecterns = new BukkitLecterns(plugin); } @Override @@ -81,6 +78,12 @@ public class GeyserSpigotWorldManager extends WorldManager { } public int getBlockNetworkId(Block block) { + if (SchedulerUtils.FOLIA && !Bukkit.isOwnedByCurrentRegion(block)) { + // Terrible behavior, but this is basically what's always been happening behind the scenes anyway. + CompletableFuture blockData = new CompletableFuture<>(); + Bukkit.getRegionScheduler().execute(this.plugin, block.getLocation(), () -> blockData.complete(block.getBlockData().getAsString())); + return BlockRegistries.JAVA_IDENTIFIERS.getOrDefault(blockData.join(), BlockStateValues.JAVA_AIR_ID); + } return BlockRegistries.JAVA_IDENTIFIERS.getOrDefault(block.getBlockData().getAsString(), BlockStateValues.JAVA_AIR_ID); } @@ -90,71 +93,64 @@ public class GeyserSpigotWorldManager extends WorldManager { } @Override - public NbtMap getLecternDataAt(GeyserSession session, int x, int y, int z, boolean isChunkLoad) { - // Run as a task to prevent async issues - Runnable lecternInfoGet = () -> { - Player bukkitPlayer; - if ((bukkitPlayer = Bukkit.getPlayer(session.getPlayerEntity().getUsername())) == null) { - return; - } - - Block block = bukkitPlayer.getWorld().getBlockAt(x, y, z); - if (!(block.getState() instanceof Lectern lectern)) { - session.getGeyser().getLogger().error("Lectern expected at: " + Vector3i.from(x, y, z).toString() + " but was not! " + block.toString()); - return; - } - - ItemStack itemStack = lectern.getInventory().getItem(0); - if (itemStack == null || !(itemStack.getItemMeta() instanceof BookMeta bookMeta)) { - if (!isChunkLoad) { - // We need to update the lectern since it's not going to be updated otherwise - BlockEntityUtils.updateBlockEntity(session, LecternInventoryTranslator.getBaseLecternTag(x, y, z, 0).build(), Vector3i.from(x, y, z)); - } - // We don't care; return - return; - } - - // On the count: allow the book to show/open even there are no pages. We know there is a book here, after all, and this matches Java behavior - boolean hasBookPages = bookMeta.getPageCount() > 0; - NbtMapBuilder lecternTag = LecternInventoryTranslator.getBaseLecternTag(x, y, z, hasBookPages ? bookMeta.getPageCount() : 1); - lecternTag.putInt("page", lectern.getPage() / 2); - NbtMapBuilder bookTag = NbtMap.builder() - .putByte("Count", (byte) itemStack.getAmount()) - .putShort("Damage", (short) 0) - .putString("Name", "minecraft:writable_book"); - List pages = new ArrayList<>(bookMeta.getPageCount()); - if (hasBookPages) { - for (String page : bookMeta.getPages()) { - NbtMapBuilder pageBuilder = NbtMap.builder() - .putString("photoname", "") - .putString("text", page); - pages.add(pageBuilder.build()); - } - } else { - // Empty page - NbtMapBuilder pageBuilder = NbtMap.builder() - .putString("photoname", "") - .putString("text", ""); - pages.add(pageBuilder.build()); - } - - bookTag.putCompound("tag", NbtMap.builder().putList("pages", NbtType.COMPOUND, pages).build()); - lecternTag.putCompound("book", bookTag.build()); - NbtMap blockEntityTag = lecternTag.build(); - BlockEntityUtils.updateBlockEntity(session, blockEntityTag, Vector3i.from(x, y, z)); - }; - - if (isChunkLoad) { - // Delay to ensure the chunk is sent first, and then the lectern data - Bukkit.getScheduler().runTaskLater(this.plugin, lecternInfoGet, 5); - } else { - Bukkit.getScheduler().runTask(this.plugin, lecternInfoGet); + public void sendLecternData(GeyserSession session, int x, int y, int z) { + Player bukkitPlayer; + if ((bukkitPlayer = Bukkit.getPlayer(session.getPlayerEntity().getUsername())) == null) { + return; + } + + Block block = bukkitPlayer.getWorld().getBlockAt(x, y, z); + // Run as a task to prevent async issues + SchedulerUtils.runTask(this.plugin, () -> sendLecternData(session, block, false), block); + } + + public void sendLecternData(GeyserSession session, int x, int z, List blockEntityInfos) { + Player bukkitPlayer; + if ((bukkitPlayer = Bukkit.getPlayer(session.getPlayerEntity().getUsername())) == null) { + return; + } + if (SchedulerUtils.FOLIA) { + Chunk chunk = getChunk(bukkitPlayer.getWorld(), x, z); + if (chunk == null) { + return; + } + Bukkit.getRegionScheduler().execute(this.plugin, bukkitPlayer.getWorld(), x, z, () -> + sendLecternData(session, chunk, blockEntityInfos)); + } else { + Bukkit.getScheduler().runTask(this.plugin, () -> { + Chunk chunk = getChunk(bukkitPlayer.getWorld(), x, z); + if (chunk == null) { + return; + } + sendLecternData(session, chunk, blockEntityInfos); + }); + } + } + + private Chunk getChunk(World world, int x, int z) { + if (!world.isChunkLoaded(x, z)) { + return null; + } + return world.getChunkAt(x, z); + } + + private void sendLecternData(GeyserSession session, Chunk chunk, List blockEntityInfos) { + for (int i = 0; i < blockEntityInfos.size(); i++) { + BlockEntityInfo info = blockEntityInfos.get(i); + Block block = chunk.getBlock(info.getX(), info.getY(), info.getZ()); + sendLecternData(session, block, true); + } + } + + private void sendLecternData(GeyserSession session, Block block, boolean isChunkLoad) { + NbtMap blockEntityTag = this.lecterns.getLecternData(block, isChunkLoad); + if (blockEntityTag != null) { + BlockEntityUtils.updateBlockEntity(session, blockEntityTag, BukkitUtils.getVector(block.getLocation())); } - return LecternInventoryTranslator.getBaseLecternTag(x, y, z, 0).build(); // Will be updated later } @Override - public boolean shouldExpectLecternHandled() { + public boolean shouldExpectLecternHandled(GeyserSession session) { return true; } @@ -184,42 +180,18 @@ public class GeyserSpigotWorldManager extends WorldManager { @Override public CompletableFuture<@Nullable CompoundTag> getPickItemNbt(GeyserSession session, int x, int y, int z, boolean addNbtData) { CompletableFuture<@Nullable CompoundTag> future = new CompletableFuture<>(); + Player bukkitPlayer; + if ((bukkitPlayer = Bukkit.getPlayer(session.getPlayerEntity().getUuid())) == null) { + future.complete(null); + return future; + } + Block block = bukkitPlayer.getWorld().getBlockAt(x, y, z); // Paper 1.19.3 complains about async access otherwise. // java.lang.IllegalStateException: Tile is null, asynchronous access? - Bukkit.getScheduler().runTask(this.plugin, () -> { - Player bukkitPlayer; - if ((bukkitPlayer = Bukkit.getPlayer(session.getPlayerEntity().getUuid())) == null) { - future.complete(null); - return; - } - - Block block = bukkitPlayer.getWorld().getBlockAt(x, y, z); - BlockState state = block.getState(); - if (state instanceof Banner banner) { - ListTag list = new ListTag("Patterns"); - for (int i = 0; i < banner.numberOfPatterns(); i++) { - Pattern pattern = banner.getPattern(i); - list.add(BannerTranslator.getJavaPatternTag(pattern.getPattern().getIdentifier(), pattern.getColor().ordinal())); - } - - CompoundTag root = addToBlockEntityTag(list); - - future.complete(root); - return; - } - future.complete(null); - }); + SchedulerUtils.runTask(this.plugin, () -> future.complete(PickBlockUtils.pickBlock(block)), block); return future; } - private CompoundTag addToBlockEntityTag(Tag tag) { - CompoundTag compoundTag = new CompoundTag(""); - CompoundTag blockEntityTag = new CompoundTag("BlockEntityTag"); - blockEntityTag.put(tag); - compoundTag.put(blockEntityTag); - return compoundTag; - } - /** * This should be set to true if we are post-1.13 but before the latest version, and we should convert the old block state id * to the current one. diff --git a/bootstrap/spigot/src/main/resources/plugin.yml b/bootstrap/spigot/src/main/resources/plugin.yml index d8bc264a9..6e81ccdb6 100644 --- a/bootstrap/spigot/src/main/resources/plugin.yml +++ b/bootstrap/spigot/src/main/resources/plugin.yml @@ -5,6 +5,7 @@ website: ${url} version: ${version} softdepend: ["ViaVersion", "floodgate"] api-version: 1.13 +folia-supported: true commands: geyser: description: The main command for Geyser. diff --git a/core/build.gradle.kts b/core/build.gradle.kts index 9ba399674..39fa0bb43 100644 --- a/core/build.gradle.kts +++ b/core/build.gradle.kts @@ -49,6 +49,10 @@ dependencies { // Adventure text serialization api(libs.bundles.adventure) + api(libs.erosion.common) { + isTransitive = false + } + // Test testImplementation(libs.junit) diff --git a/core/src/main/java/org/geysermc/geyser/GeyserImpl.java b/core/src/main/java/org/geysermc/geyser/GeyserImpl.java index c1b21e943..fbad1ab02 100644 --- a/core/src/main/java/org/geysermc/geyser/GeyserImpl.java +++ b/core/src/main/java/org/geysermc/geyser/GeyserImpl.java @@ -50,6 +50,7 @@ import org.geysermc.api.Geyser; import org.geysermc.common.PlatformType; import org.geysermc.cumulus.form.Form; import org.geysermc.cumulus.form.util.FormBuilder; +import org.geysermc.erosion.packet.Packets; import org.geysermc.floodgate.crypto.AesCipher; import org.geysermc.floodgate.crypto.AesKeyProducer; import org.geysermc.floodgate.crypto.Base64Topping; @@ -67,6 +68,7 @@ import org.geysermc.geyser.api.network.RemoteServer; import org.geysermc.geyser.command.GeyserCommandManager; import org.geysermc.geyser.configuration.GeyserConfiguration; import org.geysermc.geyser.entity.EntityDefinitions; +import org.geysermc.geyser.erosion.UnixSocketClientListener; import org.geysermc.geyser.event.GeyserEventBus; import org.geysermc.geyser.extension.GeyserExtensionManager; import org.geysermc.geyser.level.WorldManager; @@ -140,6 +142,8 @@ public class GeyserImpl implements GeyserApi { private FloodgateSkinUploader skinUploader; private NewsHandler newsHandler; + private UnixSocketClientListener erosionUnixListener; + private volatile boolean shuttingDown = false; private ScheduledExecutorService scheduledThread; @@ -293,6 +297,14 @@ public class GeyserImpl implements GeyserApi { this.newsHandler = new NewsHandler(BRANCH, this.buildNumber()); + Packets.initGeyser(); + + if (Epoll.isAvailable()) { + this.erosionUnixListener = new UnixSocketClientListener(); + } else { + logger.debug("Epoll is not available; Erosion's Unix socket handling will not work."); + } + CooldownUtils.setDefaultShowCooldown(config.getShowCooldown()); DimensionUtils.changeBedrockNetherId(config.isAboveBedrockNetherBuilding()); // Apply End dimension ID workaround to Nether @@ -570,6 +582,10 @@ public class GeyserImpl implements GeyserApi { newsHandler.shutdown(); this.commandManager().getCommands().clear(); + if (this.erosionUnixListener != null) { + this.erosionUnixListener.close(); + } + ResourcePack.PACKS.clear(); this.eventBus.fire(new GeyserShutdownEvent(this.extensionManager, this.eventBus)); diff --git a/core/src/main/java/org/geysermc/geyser/entity/type/FishingHookEntity.java b/core/src/main/java/org/geysermc/geyser/entity/type/FishingHookEntity.java index 65662bbe4..deaca789a 100644 --- a/core/src/main/java/org/geysermc/geyser/entity/type/FishingHookEntity.java +++ b/core/src/main/java/org/geysermc/geyser/entity/type/FishingHookEntity.java @@ -30,12 +30,11 @@ import com.nukkitx.math.vector.Vector3f; import com.nukkitx.protocol.bedrock.data.entity.EntityData; import com.nukkitx.protocol.bedrock.packet.PlaySoundPacket; import lombok.Getter; +import org.geysermc.erosion.util.BlockPositionIterator; import org.geysermc.geyser.entity.EntityDefinitions; import org.geysermc.geyser.entity.type.player.PlayerEntity; -import org.geysermc.geyser.level.block.BlockPositionIterator; import org.geysermc.geyser.level.block.BlockStateValues; import org.geysermc.geyser.level.physics.BoundingBox; -import org.geysermc.geyser.registry.BlockRegistries; import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.translator.collision.BlockCollision; import org.geysermc.geyser.util.BlockUtils; diff --git a/core/src/main/java/org/geysermc/geyser/entity/type/ItemEntity.java b/core/src/main/java/org/geysermc/geyser/entity/type/ItemEntity.java index 89db9b0c8..e804099d8 100644 --- a/core/src/main/java/org/geysermc/geyser/entity/type/ItemEntity.java +++ b/core/src/main/java/org/geysermc/geyser/entity/type/ItemEntity.java @@ -40,11 +40,12 @@ import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.translator.inventory.item.ItemTranslator; import java.util.UUID; +import java.util.concurrent.CompletableFuture; public class ItemEntity extends ThrowableEntity { protected ItemData item; - private int waterLevel = -1; + private CompletableFuture waterLevel = CompletableFuture.completedFuture(-1); public ItemEntity(GeyserSession session, int entityId, long geyserId, UUID uuid, EntityDefinition definition, Vector3f position, Vector3f motion, float yaw, float pitch, float headYaw) { super(session, entityId, geyserId, uuid, definition, position, motion, yaw, pitch, headYaw); @@ -111,15 +112,15 @@ public class ItemEntity extends ThrowableEntity { @Override protected void moveAbsoluteImmediate(Vector3f position, float yaw, float pitch, float headYaw, boolean isOnGround, boolean teleported) { float offset = definition.offset(); - if (waterLevel == 0) { // Item is in a full block of water + if (waterLevel.join() == 0) { // Item is in a full block of water // Move the item entity down so it doesn't float above the water offset = -definition.offset(); } super.moveAbsoluteImmediate(position.add(0, offset, 0), 0, 0, 0, isOnGround, teleported); this.position = position; - int block = session.getGeyser().getWorldManager().getBlockAt(session, position.toInt()); - waterLevel = BlockStateValues.getWaterLevel(block); + waterLevel = session.getGeyser().getWorldManager().getBlockAtAsync(session, position.getFloorX(), position.getFloorY(), position.getFloorZ()) + .thenApply(BlockStateValues::getWaterLevel); } @Override @@ -144,6 +145,6 @@ public class ItemEntity extends ThrowableEntity { @Override protected boolean isInWater() { - return waterLevel != -1; + return waterLevel.join() != -1; } } diff --git a/core/src/main/java/org/geysermc/geyser/entity/type/living/SquidEntity.java b/core/src/main/java/org/geysermc/geyser/entity/type/living/SquidEntity.java index 6b235a8e5..453d70bf6 100644 --- a/core/src/main/java/org/geysermc/geyser/entity/type/living/SquidEntity.java +++ b/core/src/main/java/org/geysermc/geyser/entity/type/living/SquidEntity.java @@ -34,12 +34,13 @@ import org.geysermc.geyser.level.block.BlockStateValues; import org.geysermc.geyser.session.GeyserSession; import java.util.UUID; +import java.util.concurrent.CompletableFuture; public class SquidEntity extends WaterEntity implements Tickable { private float targetPitch; private float targetYaw; - private boolean inWater; + private CompletableFuture inWater = CompletableFuture.completedFuture(Boolean.FALSE); public SquidEntity(GeyserSession session, int entityId, long geyserId, UUID uuid, EntityDefinition definition, Vector3f position, Vector3f motion, float yaw, float pitch, float headYaw) { super(session, entityId, geyserId, uuid, definition, position, motion, yaw, pitch, headYaw); @@ -50,7 +51,7 @@ public class SquidEntity extends WaterEntity implements Tickable { boolean pitchChanged; boolean yawChanged; float oldPitch = pitch; - if (inWater) { + if (inWater.join()) { float oldYaw = yaw; pitch += (targetPitch - pitch) * 0.1f; yaw += (targetYaw - yaw) * 0.1f; @@ -93,7 +94,7 @@ public class SquidEntity extends WaterEntity implements Tickable { @Override public void setYaw(float yaw) { // Let the Java server control yaw when the squid is out of water - if (!inWater) { + if (!inWater.join()) { this.yaw = yaw; } } @@ -127,10 +128,10 @@ public class SquidEntity extends WaterEntity implements Tickable { private void checkInWater() { if (getFlag(EntityFlag.RIDING)) { - inWater = false; + inWater = CompletableFuture.completedFuture(false); } else { - int block = session.getGeyser().getWorldManager().getBlockAt(session, position.toInt()); - inWater = BlockStateValues.getWaterLevel(block) != -1; + inWater = session.getGeyser().getWorldManager().getBlockAtAsync(session, position.toInt()) + .thenApply(block -> BlockStateValues.getWaterLevel(block) != -1); } } } diff --git a/core/src/main/java/org/geysermc/geyser/erosion/AbstractGeyserboundPacketHandler.java b/core/src/main/java/org/geysermc/geyser/erosion/AbstractGeyserboundPacketHandler.java new file mode 100644 index 000000000..eabed8f7b --- /dev/null +++ b/core/src/main/java/org/geysermc/geyser/erosion/AbstractGeyserboundPacketHandler.java @@ -0,0 +1,88 @@ +/* + * Copyright (c) 2019-2023 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.erosion; + +import org.geysermc.erosion.packet.geyserbound.*; +import org.geysermc.geyser.session.GeyserSession; +import org.jetbrains.annotations.Nullable; + +public abstract class AbstractGeyserboundPacketHandler implements GeyserboundPacketHandler { + protected final GeyserSession session; + + public AbstractGeyserboundPacketHandler(GeyserSession session) { + this.session = session; + } + + @Override + public void handleBatchBlockId(GeyserboundBatchBlockIdPacket packet) { + illegalPacket(packet); + } + + @Override + public void handleBlockEntity(GeyserboundBlockEntityPacket packet) { + illegalPacket(packet); + } + + @Override + public void handleBlockId(GeyserboundBlockIdPacket packet) { + illegalPacket(packet); + } + + @Override + public void handleBlockLookupFail(GeyserboundBlockLookupFailPacket packet) { + illegalPacket(packet); + } + + @Override + public void handleBlockPlace(GeyserboundBlockPlacePacket packet) { + illegalPacket(packet); + } + + @Override + public void handlePistonEvent(GeyserboundPistonEventPacket packet) { + illegalPacket(packet); + } + + @Override + public void handlePickBlock(GeyserboundPickBlockPacket packet) { + illegalPacket(packet); + } + + /** + * Is this handler actually listening to any packets? + */ + public abstract boolean isActive(); + + @Nullable + public abstract GeyserboundPacketHandlerImpl getAsActive(); + + public void close() { + } + + protected final void illegalPacket(GeyserboundPacket packet) { + session.getGeyser().getLogger().warning("Illegal packet sent from backend server! " + packet); + } +} diff --git a/core/src/main/java/org/geysermc/geyser/erosion/GeyserErosionPacketSender.java b/core/src/main/java/org/geysermc/geyser/erosion/GeyserErosionPacketSender.java new file mode 100644 index 000000000..610917adc --- /dev/null +++ b/core/src/main/java/org/geysermc/geyser/erosion/GeyserErosionPacketSender.java @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2019-2023 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.erosion; + +import io.netty.buffer.ByteBuf; +import io.netty.buffer.Unpooled; +import io.netty.channel.Channel; +import org.geysermc.erosion.Constants; +import org.geysermc.erosion.packet.ErosionPacketSender; +import org.geysermc.erosion.packet.Packets; +import org.geysermc.erosion.packet.backendbound.BackendboundPacket; +import org.geysermc.geyser.session.GeyserSession; +import org.geysermc.geyser.util.PluginMessageUtils; + +import java.io.IOException; + +public record GeyserErosionPacketSender(GeyserSession session) implements ErosionPacketSender { + + @Override + public void sendPacket(BackendboundPacket packet) { + ByteBuf buf = Unpooled.buffer(); + try { + Packets.encode(buf, packet); + byte[] bytes = new byte[buf.readableBytes()]; + buf.readBytes(bytes); + PluginMessageUtils.sendMessage(session, Constants.PLUGIN_MESSAGE, bytes); + } catch (IOException e) { + e.printStackTrace(); + } finally { + buf.release(); + } + } + + @Override + public void setChannel(Channel channel) { + } +} diff --git a/core/src/main/java/org/geysermc/geyser/erosion/GeyserboundHandshakePacketHandler.java b/core/src/main/java/org/geysermc/geyser/erosion/GeyserboundHandshakePacketHandler.java new file mode 100644 index 000000000..196595383 --- /dev/null +++ b/core/src/main/java/org/geysermc/geyser/erosion/GeyserboundHandshakePacketHandler.java @@ -0,0 +1,72 @@ +/* + * Copyright (c) 2019-2023 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.erosion; + +import io.netty.channel.Channel; +import org.geysermc.erosion.netty.NettyPacketSender; +import org.geysermc.erosion.packet.ErosionPacketHandler; +import org.geysermc.erosion.packet.geyserbound.GeyserboundHandshakePacket; +import org.geysermc.geyser.session.GeyserSession; +import org.jetbrains.annotations.Nullable; + +public final class GeyserboundHandshakePacketHandler extends AbstractGeyserboundPacketHandler { + + public GeyserboundHandshakePacketHandler(GeyserSession session) { + super(session); + } + + @Override + public void handleHandshake(GeyserboundHandshakePacket packet) { + boolean useTcp = packet.getTransportType().getSocketAddress() == null; + GeyserboundPacketHandlerImpl handler = new GeyserboundPacketHandlerImpl(session, useTcp ? new GeyserErosionPacketSender(session) : new NettyPacketSender<>()); + session.setErosionHandler(handler); + if (!useTcp) { + if (session.getGeyser().getErosionUnixListener() == null) { + session.disconnect("Erosion configurations using Unix socket handling are not supported on this hardware!"); + return; + } + session.getGeyser().getErosionUnixListener().createClient(handler, packet.getTransportType().getSocketAddress()); + } else { + handler.onConnect(); + } + session.ensureInEventLoop(() -> session.getChunkCache().clear()); + } + + @Override + public boolean isActive() { + return false; + } + + @Override + public @Nullable GeyserboundPacketHandlerImpl getAsActive() { + return null; + } + + @Override + public ErosionPacketHandler setChannel(Channel channel) { + return null; + } +} diff --git a/core/src/main/java/org/geysermc/geyser/erosion/GeyserboundPacketHandlerImpl.java b/core/src/main/java/org/geysermc/geyser/erosion/GeyserboundPacketHandlerImpl.java new file mode 100644 index 000000000..af3098edb --- /dev/null +++ b/core/src/main/java/org/geysermc/geyser/erosion/GeyserboundPacketHandlerImpl.java @@ -0,0 +1,209 @@ +/* + * Copyright (c) 2019-2023 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.erosion; + +import com.github.steveice10.mc.protocol.data.game.level.block.value.PistonValueType; +import com.github.steveice10.opennbt.tag.builtin.CompoundTag; +import com.nukkitx.math.vector.Vector3i; +import com.nukkitx.nbt.NbtMap; +import com.nukkitx.protocol.bedrock.data.SoundEvent; +import com.nukkitx.protocol.bedrock.packet.LevelSoundEventPacket; +import io.netty.channel.Channel; +import it.unimi.dsi.fastutil.ints.Int2ObjectMap; +import it.unimi.dsi.fastutil.ints.Int2ObjectMaps; +import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; +import it.unimi.dsi.fastutil.ints.IntArrays; +import it.unimi.dsi.fastutil.objects.Object2IntArrayMap; +import it.unimi.dsi.fastutil.objects.Object2IntMap; +import lombok.Getter; +import lombok.Setter; +import org.geysermc.erosion.packet.ErosionPacketHandler; +import org.geysermc.erosion.packet.ErosionPacketSender; +import org.geysermc.erosion.packet.backendbound.BackendboundInitializePacket; +import org.geysermc.erosion.packet.backendbound.BackendboundPacket; +import org.geysermc.erosion.packet.geyserbound.*; +import org.geysermc.geyser.level.block.BlockStateValues; +import org.geysermc.geyser.level.physics.Direction; +import org.geysermc.geyser.network.GameProtocol; +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.util.BlockEntityUtils; + +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.atomic.AtomicInteger; + +public final class GeyserboundPacketHandlerImpl extends AbstractGeyserboundPacketHandler { + private final ErosionPacketSender packetSender; + @Setter + private CompletableFuture pendingLookup = null; + @Getter + private final Int2ObjectMap> asyncPendingLookups = Int2ObjectMaps.synchronize(new Int2ObjectOpenHashMap<>(4)); + @Setter + private CompletableFuture pendingBatchLookup = null; + @Setter + private CompletableFuture pickBlockLookup = null; + + private final AtomicInteger nextTransactionId = new AtomicInteger(1); + + public GeyserboundPacketHandlerImpl(GeyserSession session, ErosionPacketSender packetSender) { + super(session); + this.packetSender = packetSender; + } + + @Override + public void handleBatchBlockId(GeyserboundBatchBlockIdPacket packet) { + if (this.pendingBatchLookup != null) { + this.pendingBatchLookup.complete(packet.getBlocks()); + } else { + session.getGeyser().getLogger().warning("Batch block ID packet received with no future to complete."); + } + } + + @Override + public void handleBlockEntity(GeyserboundBlockEntityPacket packet) { + NbtMap nbt = packet.getNbt(); + BlockEntityUtils.updateBlockEntity(session, nbt, Vector3i.from(nbt.getInt("x"), nbt.getInt("y"), nbt.getInt("z"))); + } + + @Override + public void handleBlockId(GeyserboundBlockIdPacket packet) { + if (packet.getTransactionId() == 0) { + if (this.pendingLookup != null) { + this.pendingLookup.complete(packet.getBlockId()); + return; + } + } + CompletableFuture future = this.asyncPendingLookups.remove(packet.getTransactionId()); + if (future != null) { + future.complete(packet.getBlockId()); + return; + } + session.getGeyser().getLogger().warning("Block ID packet received with no future to complete."); + } + + @Override + public void handleBlockLookupFail(GeyserboundBlockLookupFailPacket packet) { + if (packet.getTransactionId() == 0) { + if (this.pendingBatchLookup != null) { + this.pendingBatchLookup.complete(null); + return; + } + } + int transactionId = packet.getTransactionId() - 1; + if (transactionId == 0) { + if (this.pendingLookup != null) { + this.pendingLookup.complete(0); + } + } + CompletableFuture future = this.asyncPendingLookups.remove(transactionId); + if (future != null) { + future.complete(BlockStateValues.JAVA_AIR_ID); + } + } + + @Override + public void handleBlockPlace(GeyserboundBlockPlacePacket packet) { + LevelSoundEventPacket placeBlockSoundPacket = new LevelSoundEventPacket(); + placeBlockSoundPacket.setSound(SoundEvent.PLACE); + placeBlockSoundPacket.setPosition(packet.getPos().toFloat()); + placeBlockSoundPacket.setBabySound(false); + placeBlockSoundPacket.setExtraData(session.getBlockMappings().getBedrockBlockId(packet.getBlockId())); + placeBlockSoundPacket.setIdentifier(":"); + session.sendUpstreamPacket(placeBlockSoundPacket); + session.setLastBlockPlacePosition(null); + session.setLastBlockPlacedId(null); + } + + @Override + public void handlePickBlock(GeyserboundPickBlockPacket packet) { + if (this.pickBlockLookup != null) { + this.pickBlockLookup.complete(packet.getTag()); + } + } + + @Override + public void handlePistonEvent(GeyserboundPistonEventPacket packet) { + Direction orientation = BlockStateValues.getPistonOrientation(packet.getBlockId()); + Vector3i position = packet.getPos(); + boolean isExtend = packet.isExtend(); + + var stream = packet.getAttachedBlocks() + .object2IntEntrySet() + .stream() + .filter(entry -> BlockStateValues.canPistonMoveBlock(entry.getIntValue(), isExtend)); + Object2IntMap attachedBlocks = new Object2IntArrayMap<>(); + stream.forEach(entry -> attachedBlocks.put(entry.getKey(), entry.getIntValue())); + + session.executeInEventLoop(() -> { + PistonCache pistonCache = session.getPistonCache(); + PistonBlockEntity blockEntity = pistonCache.getPistons().computeIfAbsent(position, pos -> + new PistonBlockEntity(session, position, orientation, packet.isSticky(), !isExtend)); + blockEntity.setAction(isExtend ? PistonValueType.PUSHING : PistonValueType.PULLING, attachedBlocks); + }); + } + + @Override + public void handleHandshake(GeyserboundHandshakePacket packet) { + this.close(); + var handler = new GeyserboundHandshakePacketHandler(this.session); + session.setErosionHandler(handler); + handler.handleHandshake(packet); + } + + @Override + public boolean isActive() { + return true; + } + + @Override + public GeyserboundPacketHandlerImpl getAsActive() { + return this; + } + + @Override + public void onConnect() { + sendPacket(new BackendboundInitializePacket(session.getPlayerEntity().getUuid(), GameProtocol.getJavaProtocolVersion())); + } + + public void sendPacket(BackendboundPacket packet) { + this.packetSender.sendPacket(packet); + } + + public void close() { + this.packetSender.close(); + } + + public int getNextTransactionId() { + return nextTransactionId.getAndIncrement(); + } + + @Override + public ErosionPacketHandler setChannel(Channel channel) { + this.packetSender.setChannel(channel); + return this; + } +} diff --git a/core/src/main/java/org/geysermc/geyser/erosion/UnixSocketClientListener.java b/core/src/main/java/org/geysermc/geyser/erosion/UnixSocketClientListener.java new file mode 100644 index 000000000..a236099df --- /dev/null +++ b/core/src/main/java/org/geysermc/geyser/erosion/UnixSocketClientListener.java @@ -0,0 +1,70 @@ +/* + * Copyright (c) 2019-2023 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.erosion; + +import io.netty.bootstrap.Bootstrap; +import io.netty.channel.Channel; +import io.netty.channel.ChannelInitializer; +import io.netty.channel.EventLoopGroup; +import io.netty.channel.epoll.EpollDomainSocketChannel; +import io.netty.channel.epoll.EpollEventLoopGroup; +import org.geysermc.erosion.netty.impl.AbstractUnixSocketListener; +import org.geysermc.erosion.packet.geyserbound.GeyserboundPacketHandler; + +import java.net.SocketAddress; + +public final class UnixSocketClientListener extends AbstractUnixSocketListener { + private EventLoopGroup eventLoopGroup; + + public void initializeEventLoopGroup() { + if (this.eventLoopGroup == null) { + this.eventLoopGroup = new EpollEventLoopGroup(); + } + } + + public void createClient(GeyserboundPacketHandler handler, SocketAddress address) { + initializeEventLoopGroup(); + (new Bootstrap() + .channel(EpollDomainSocketChannel.class) + .handler(new ChannelInitializer() { + @Override + protected void initChannel(Channel ch) { + initPipeline(ch, handler); + } + }) + .group(this.eventLoopGroup.next()) + .connect(address)) + .syncUninterruptibly() + .channel(); + } + + @Override + public void close() { + if (this.eventLoopGroup != null) { + this.eventLoopGroup.shutdownGracefully(); + } + } +} diff --git a/core/src/main/java/org/geysermc/geyser/level/GeyserWorldManager.java b/core/src/main/java/org/geysermc/geyser/level/GeyserWorldManager.java index 5df97e8d7..43ba452a0 100644 --- a/core/src/main/java/org/geysermc/geyser/level/GeyserWorldManager.java +++ b/core/src/main/java/org/geysermc/geyser/level/GeyserWorldManager.java @@ -25,19 +25,63 @@ package org.geysermc.geyser.level; +import com.github.steveice10.mc.protocol.data.game.level.block.BlockEntityInfo; +import com.github.steveice10.opennbt.tag.builtin.CompoundTag; +import com.nukkitx.math.vector.Vector3i; import com.nukkitx.nbt.NbtMap; import com.nukkitx.nbt.NbtMapBuilder; import it.unimi.dsi.fastutil.objects.Object2ObjectMap; import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap; +import it.unimi.dsi.fastutil.objects.ObjectArrayList; +import org.geysermc.erosion.packet.backendbound.*; +import org.geysermc.erosion.util.BlockPositionIterator; +import org.geysermc.erosion.util.LecternUtils; import org.geysermc.geyser.session.GeyserSession; -import org.geysermc.geyser.translator.inventory.LecternInventoryTranslator; +import org.geysermc.geyser.util.BlockEntityUtils; +import org.jetbrains.annotations.Nullable; + +import javax.annotation.Nonnull; +import java.util.List; +import java.util.concurrent.CompletableFuture; public class GeyserWorldManager extends WorldManager { private final Object2ObjectMap gameruleCache = new Object2ObjectOpenHashMap<>(); @Override public int getBlockAt(GeyserSession session, int x, int y, int z) { - return session.getChunkCache().getBlockAt(x, y, z); + var erosionHandler = session.getErosionHandler().getAsActive(); + if (erosionHandler == null) { + return session.getChunkCache().getBlockAt(x, y, z); + } + CompletableFuture future = new CompletableFuture<>(); // Boxes + erosionHandler.setPendingLookup(future); + erosionHandler.sendPacket(new BackendboundBlockRequestPacket(0, Vector3i.from(x, y, z))); + return future.join(); + } + + @Override + public CompletableFuture getBlockAtAsync(GeyserSession session, int x, int y, int z) { + var erosionHandler = session.getErosionHandler().getAsActive(); + if (erosionHandler == null) { + return super.getBlockAtAsync(session, x, y, z); + } + CompletableFuture future = new CompletableFuture<>(); // Boxes + int transactionId = erosionHandler.getNextTransactionId(); + erosionHandler.getAsyncPendingLookups().put(transactionId, future); + erosionHandler.sendPacket(new BackendboundBlockRequestPacket(transactionId, Vector3i.from(x, y, z))); + return future; + } + + @Override + public int[] getBlocksAt(GeyserSession session, BlockPositionIterator iter) { + var erosionHandler = session.getErosionHandler().getAsActive(); + if (erosionHandler == null) { + return super.getBlocksAt(session, iter); + } + CompletableFuture future = new CompletableFuture<>(); + erosionHandler.setPendingBatchLookup(future); + erosionHandler.sendPacket(new BackendboundBatchBlockRequestPacket(iter)); + return future.join(); } @Override @@ -47,10 +91,31 @@ public class GeyserWorldManager extends WorldManager { } @Override - public NbtMap getLecternDataAt(GeyserSession session, int x, int y, int z, boolean isChunkLoad) { + public void sendLecternData(GeyserSession session, int x, int z, List blockEntityInfos) { + var erosionHandler = session.getErosionHandler().getAsActive(); + if (erosionHandler == null) { + // No-op - don't send any additional information other than what the chunk has already sent + return; + } + List vectors = new ObjectArrayList<>(blockEntityInfos.size()); + for (int i = 0; i < blockEntityInfos.size(); i++) { + BlockEntityInfo info = blockEntityInfos.get(i); + vectors.add(Vector3i.from(info.getX(), info.getY(), info.getZ())); + } + erosionHandler.sendPacket(new BackendboundBatchBlockEntityPacket(x, z, vectors)); + } + + @Override + public void sendLecternData(GeyserSession session, int x, int y, int z) { + var erosionHandler = session.getErosionHandler().getAsActive(); + if (erosionHandler != null) { + erosionHandler.sendPacket(new BackendboundBlockEntityPacket(Vector3i.from(x, y, z))); + return; + } + // Without direct server access, we can't get lectern information on-the-fly. // I should have set this up so it's only called when there is a book in the block state. - Camotoy - NbtMapBuilder lecternTag = LecternInventoryTranslator.getBaseLecternTag(x, y, z, 1); + NbtMapBuilder lecternTag = LecternUtils.getBaseLecternTag(x, y, z, 1); lecternTag.putCompound("book", NbtMap.builder() .putByte("Count", (byte) 1) .putShort("Damage", (short) 0) @@ -61,12 +126,12 @@ public class GeyserWorldManager extends WorldManager { .build()) .build()); lecternTag.putInt("page", -1); // I'm surprisingly glad this exists - it forces Bedrock to stop reading immediately. Usually. - return lecternTag.build(); + BlockEntityUtils.updateBlockEntity(session, lecternTag.build(), Vector3i.from(x, y, z)); } @Override - public boolean shouldExpectLecternHandled() { - return false; + public boolean shouldExpectLecternHandled(GeyserSession session) { + return session.getErosionHandler().isActive(); } @Override @@ -99,4 +164,17 @@ public class GeyserWorldManager extends WorldManager { public boolean hasPermission(GeyserSession session, String permission) { return false; } + + @Nonnull + @Override + public CompletableFuture<@Nullable CompoundTag> getPickItemNbt(GeyserSession session, int x, int y, int z, boolean addNbtData) { + var erosionHandler = session.getErosionHandler().getAsActive(); + if (erosionHandler == null) { + return super.getPickItemNbt(session, x, y, z, addNbtData); + } + CompletableFuture future = new CompletableFuture<>(); + erosionHandler.setPickBlockLookup(future); + erosionHandler.sendPacket(new BackendboundPickBlockPacket(Vector3i.from(x, y, z))); + return future; + } } diff --git a/core/src/main/java/org/geysermc/geyser/level/WorldManager.java b/core/src/main/java/org/geysermc/geyser/level/WorldManager.java index 1909915db..84a03022f 100644 --- a/core/src/main/java/org/geysermc/geyser/level/WorldManager.java +++ b/core/src/main/java/org/geysermc/geyser/level/WorldManager.java @@ -26,14 +26,16 @@ package org.geysermc.geyser.level; import com.github.steveice10.mc.protocol.data.game.entity.player.GameMode; +import com.github.steveice10.mc.protocol.data.game.level.block.BlockEntityInfo; import com.github.steveice10.mc.protocol.data.game.setting.Difficulty; import com.github.steveice10.opennbt.tag.builtin.CompoundTag; import com.nukkitx.math.vector.Vector3i; -import com.nukkitx.nbt.NbtMap; +import org.geysermc.erosion.util.BlockPositionIterator; import org.geysermc.geyser.session.GeyserSession; import org.jetbrains.annotations.Nullable; import javax.annotation.Nonnull; +import java.util.List; import java.util.Locale; import java.util.concurrent.CompletableFuture; @@ -68,6 +70,23 @@ public abstract class WorldManager { */ public abstract int getBlockAt(GeyserSession session, int x, int y, int z); + public final CompletableFuture getBlockAtAsync(GeyserSession session, Vector3i vector) { + return this.getBlockAtAsync(session, vector.getX(), vector.getY(), vector.getZ()); + } + + public CompletableFuture getBlockAtAsync(GeyserSession session, int x, int y, int z) { + return CompletableFuture.completedFuture(this.getBlockAt(session, x, y, z)); + } + + public int[] getBlocksAt(GeyserSession session, BlockPositionIterator iter) { + int[] blocks = new int[iter.getMaxIterations()]; + for (; iter.hasNext(); iter.next()) { + int networkId = this.getBlockAt(session, iter.getX(), iter.getY(), iter.getZ()); + blocks[iter.getIteration()] = networkId; + } + return blocks; + } + /** * Checks whether or not this world manager requires a separate chunk cache/has access to more block data than the chunk cache. *

@@ -89,20 +108,28 @@ public abstract class WorldManager { * We solve this problem by querying all loaded lecterns, where possible, and sending their information in a block entity * tag. * + * Note that the lectern data may be sent asynchronously. + * * @param session the session of the player * @param x the x coordinate of the lectern * @param y the y coordinate of the lectern * @param z the z coordinate of the lectern - * @param isChunkLoad if this is called during a chunk load or not. Changes behavior in certain instances. - * @return the Bedrock lectern block entity tag. This may not be the exact block entity tag - for example, Spigot's - * block handled must be done on the server thread, so we send the tag manually there. */ - public abstract NbtMap getLecternDataAt(GeyserSession session, int x, int y, int z, boolean isChunkLoad); + public abstract void sendLecternData(GeyserSession session, int x, int y, int z); + + /** + * {@link #sendLecternData(GeyserSession, int, int, int)} but batched for chunks. + * + * @param x chunk x + * @param z chunk z + * @param blockEntityInfos a list of coordinates (chunk local) to grab lecterns from. + */ + public abstract void sendLecternData(GeyserSession session, int x, int z, List blockEntityInfos); /** * @return whether we should expect lectern data to update, or if we have to fall back on a workaround. */ - public abstract boolean shouldExpectLecternHandled(); + public abstract boolean shouldExpectLecternHandled(GeyserSession session); /** * Updates a gamerule value on the Java server diff --git a/core/src/main/java/org/geysermc/geyser/level/block/BlockPositionIterator.java b/core/src/main/java/org/geysermc/geyser/level/block/BlockPositionIterator.java deleted file mode 100644 index d22150ccf..000000000 --- a/core/src/main/java/org/geysermc/geyser/level/block/BlockPositionIterator.java +++ /dev/null @@ -1,80 +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.block; - -import com.nukkitx.network.util.Preconditions; - -public class BlockPositionIterator { - private final int minX; - private final int minY; - private final int minZ; - - private final int sizeX; - private final int sizeZ; - - private int i = 0; - private final int maxI; - - public BlockPositionIterator(int minX, int minY, int minZ, int maxX, int maxY, int maxZ) { - Preconditions.checkArgument(maxX >= minX, "maxX is not greater than or equal to minX"); - Preconditions.checkArgument(maxY >= minY, "maxY is not greater than or equal to minY"); - Preconditions.checkArgument(maxZ >= minZ, "maxZ is not greater than or equal to minZ"); - - this.minX = minX; - this.minY = minY; - this.minZ = minZ; - - this.sizeX = maxX - minX + 1; - int sizeY = maxY - minY + 1; - this.sizeZ = maxZ - minZ + 1; - this.maxI = sizeX * sizeY * sizeZ; - } - - public boolean hasNext() { - return i < maxI; - } - - public void next() { - // Iterate in zxy order - i++; - } - - public void reset() { - i = 0; - } - - public int getX() { - return ((i / sizeZ) % sizeX) + minX; - } - - public int getY() { - return (i / sizeZ / sizeX) + minY; - } - - public int getZ() { - return (i % sizeZ) + minZ; - } -} \ No newline at end of file diff --git a/core/src/main/java/org/geysermc/geyser/level/physics/CollisionManager.java b/core/src/main/java/org/geysermc/geyser/level/physics/CollisionManager.java index 2a830cd70..666e1191d 100644 --- a/core/src/main/java/org/geysermc/geyser/level/physics/CollisionManager.java +++ b/core/src/main/java/org/geysermc/geyser/level/physics/CollisionManager.java @@ -33,10 +33,10 @@ import com.nukkitx.protocol.bedrock.data.entity.EntityFlag; import com.nukkitx.protocol.bedrock.packet.MovePlayerPacket; import lombok.Getter; import lombok.Setter; +import org.geysermc.erosion.util.BlockPositionIterator; import org.geysermc.geyser.entity.EntityDefinitions; import org.geysermc.geyser.entity.type.Entity; import org.geysermc.geyser.entity.type.player.PlayerEntity; -import org.geysermc.geyser.level.block.BlockPositionIterator; import org.geysermc.geyser.level.block.BlockStateValues; import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.session.cache.PistonCache; @@ -215,7 +215,7 @@ public class CollisionManager { int minCollisionZ = (int) Math.floor(position.getZ() - ((box.getSizeZ() / 2) + COLLISION_TOLERANCE + pistonExpand)); int maxCollisionZ = (int) Math.floor(position.getZ() + (box.getSizeZ() / 2) + COLLISION_TOLERANCE + pistonExpand); - return new BlockPositionIterator(minCollisionX, minCollisionY, minCollisionZ, maxCollisionX, maxCollisionY, maxCollisionZ); + return BlockPositionIterator.fromMinMax(minCollisionX, minCollisionY, minCollisionZ, maxCollisionX, maxCollisionY, maxCollisionZ); } public BlockPositionIterator playerCollidableBlocksIterator() { @@ -235,8 +235,9 @@ public class CollisionManager { // Used when correction code needs to be run before the main correction BlockPositionIterator iter = session.getCollisionManager().playerCollidableBlocksIterator(); - for (; iter.hasNext(); iter.next()) { - BlockCollision blockCollision = BlockUtils.getCollisionAt(session, iter.getX(), iter.getY(), iter.getZ()); + int[] blocks = session.getGeyser().getWorldManager().getBlocksAt(session, iter); + for (iter.reset(); iter.hasNext(); iter.next()) { + BlockCollision blockCollision = BlockUtils.getCollision(blocks[iter.getIteration()]); if (blockCollision != null) { blockCollision.beforeCorrectPosition(iter.getX(), iter.getY(), iter.getZ(), playerBoundingBox); } @@ -244,7 +245,7 @@ public class CollisionManager { // Main correction code for (iter.reset(); iter.hasNext(); iter.next()) { - BlockCollision blockCollision = BlockUtils.getCollisionAt(session, iter.getX(), iter.getY(), iter.getZ()); + BlockCollision blockCollision = BlockUtils.getCollision(blocks[iter.getIteration()]); if (blockCollision != null) { if (!blockCollision.correctPosition(session, iter.getX(), iter.getY(), iter.getZ(), playerBoundingBox)) { return false; diff --git a/core/src/main/java/org/geysermc/geyser/pack/ResourcePack.java b/core/src/main/java/org/geysermc/geyser/pack/ResourcePack.java index 6df1a0c0e..07d56194a 100644 --- a/core/src/main/java/org/geysermc/geyser/pack/ResourcePack.java +++ b/core/src/main/java/org/geysermc/geyser/pack/ResourcePack.java @@ -102,13 +102,17 @@ public class ResourcePack { pack.sha256 = FileUtils.calculateSHA256(file); - Stream stream = null; - try { - ZipFile zip = new ZipFile(file); - - stream = zip.stream(); + try (ZipFile zip = new ZipFile(file); + Stream stream = zip.stream()) { stream.forEach((x) -> { - if (x.getName().contains("manifest.json")) { + String name = x.getName(); + if (name.length() >= 80) { + GeyserImpl.getInstance().getLogger().warning("The resource pack " + file.getName() + + " has a file in it that meets or exceeds 80 characters in its path (" + name + + ", " + name.length() + " characters long). This will cause problems on some Bedrock platforms." + + " Please rename it to be shorter, or reduce the amount of folders needed to get to the file."); + } + if (name.contains("manifest.json")) { try { ResourcePackManifest manifest = FileUtils.loadJson(zip.getInputStream(x), ResourcePackManifest.class); // Sometimes a pack_manifest file is present and not in a valid format, @@ -133,10 +137,6 @@ public class ResourcePack { } catch (Exception e) { GeyserImpl.getInstance().getLogger().error(GeyserLocale.getLocaleStringLog("geyser.resource_pack.broken", file.getName())); e.printStackTrace(); - } finally { - if (stream != null) { - stream.close(); - } } } } diff --git a/core/src/main/java/org/geysermc/geyser/session/GeyserSession.java b/core/src/main/java/org/geysermc/geyser/session/GeyserSession.java index e245d0f56..2d36e2813 100644 --- a/core/src/main/java/org/geysermc/geyser/session/GeyserSession.java +++ b/core/src/main/java/org/geysermc/geyser/session/GeyserSession.java @@ -112,6 +112,8 @@ import org.geysermc.geyser.entity.type.Entity; import org.geysermc.geyser.entity.type.ItemFrameEntity; import org.geysermc.geyser.entity.type.Tickable; import org.geysermc.geyser.entity.type.player.SessionPlayerEntity; +import org.geysermc.geyser.erosion.AbstractGeyserboundPacketHandler; +import org.geysermc.geyser.erosion.GeyserboundHandshakePacketHandler; import org.geysermc.geyser.inventory.Inventory; import org.geysermc.geyser.inventory.PlayerInventory; import org.geysermc.geyser.inventory.recipe.GeyserRecipe; @@ -136,6 +138,7 @@ import org.geysermc.geyser.translator.text.MessageTranslator; import org.geysermc.geyser.util.ChunkUtils; import org.geysermc.geyser.util.DimensionUtils; import org.geysermc.geyser.util.LoginEncryptionUtils; +import org.jetbrains.annotations.NotNull; import java.net.ConnectException; import java.net.InetSocketAddress; @@ -168,6 +171,10 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource { @Setter private JsonNode certChainData; + @NotNull + @Setter + private AbstractGeyserboundPacketHandler erosionHandler; + @Accessors(fluent = true) @Setter private RemoteServer remoteServer; @@ -255,7 +262,7 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource { /** * Stores a list of all lectern locations and their block entity tags. - * See {@link WorldManager#getLecternDataAt(GeyserSession, int, int, int, boolean)} + * See {@link WorldManager#sendLecternData(GeyserSession, int, int, int)} * for more information. */ private final Set lecternCache; @@ -551,6 +558,8 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource { this.upstream = new UpstreamSession(bedrockServerSession); this.eventLoop = eventLoop; + this.erosionHandler = new GeyserboundHandshakePacketHandler(this); + this.advancementsCache = new AdvancementsCache(this); this.bookEditCache = new BookEditCache(this); this.chunkCache = new ChunkCache(this); @@ -579,7 +588,7 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource { this.spawned = false; this.loggedIn = false; - if (geyser.getWorldManager().shouldExpectLecternHandled()) { + if (geyser.getWorldManager().shouldExpectLecternHandled(this)) { // Unneeded on these platforms this.lecternCache = null; } else { @@ -1088,6 +1097,8 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource { tickThread.cancel(false); } + erosionHandler.close(); + closed = true; } diff --git a/core/src/main/java/org/geysermc/geyser/translator/inventory/LecternInventoryTranslator.java b/core/src/main/java/org/geysermc/geyser/translator/inventory/LecternInventoryTranslator.java index 59fe81751..117f4e851 100644 --- a/core/src/main/java/org/geysermc/geyser/translator/inventory/LecternInventoryTranslator.java +++ b/core/src/main/java/org/geysermc/geyser/translator/inventory/LecternInventoryTranslator.java @@ -35,6 +35,7 @@ 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.erosion.util.LecternUtils; import org.geysermc.geyser.inventory.GeyserItemStack; import org.geysermc.geyser.inventory.Inventory; import org.geysermc.geyser.inventory.LecternContainer; @@ -110,13 +111,13 @@ public class LecternInventoryTranslator extends BaseInventoryTranslator { Vector3i position = session.getLastInteractionBlockPosition(); // If shouldExpectLecternHandled returns true, this is already handled for us // shouldRefresh means that we should boot out the client on our side because their lectern GUI isn't updated yet - boolean shouldRefresh = !session.getGeyser().getWorldManager().shouldExpectLecternHandled() && !session.getLecternCache().contains(position); + boolean shouldRefresh = !session.getGeyser().getWorldManager().shouldExpectLecternHandled(session) && !session.getLecternCache().contains(position); NbtMap blockEntityTag; if (tag != null) { int pagesSize = ((ListTag) tag.get("pages")).size(); ItemData itemData = book.getItemData(session); - NbtMapBuilder lecternTag = getBaseLecternTag(position.getX(), position.getY(), position.getZ(), pagesSize); + NbtMapBuilder lecternTag = LecternUtils.getBaseLecternTag(position.getX(), position.getY(), position.getZ(), pagesSize); lecternTag.putCompound("book", NbtMap.builder() .putByte("Count", (byte) itemData.getCount()) .putShort("Damage", (short) 0) @@ -127,7 +128,7 @@ public class LecternInventoryTranslator extends BaseInventoryTranslator { blockEntityTag = lecternTag.build(); } else { // There is *a* book here, but... no NBT. - NbtMapBuilder lecternTag = getBaseLecternTag(position.getX(), position.getY(), position.getZ(), 1); + NbtMapBuilder lecternTag = LecternUtils.getBaseLecternTag(position.getX(), position.getY(), position.getZ(), 1); NbtMapBuilder bookTag = NbtMap.builder() .putByte("Count", (byte) 1) .putShort("Damage", (short) 0) @@ -162,20 +163,4 @@ public class LecternInventoryTranslator extends BaseInventoryTranslator { public Inventory createInventory(String name, int windowId, ContainerType containerType, PlayerInventory playerInventory) { return new LecternContainer(name, windowId, this.size, containerType, playerInventory); } - - public static NbtMapBuilder getBaseLecternTag(int x, int y, int z, int totalPages) { - NbtMapBuilder builder = NbtMap.builder() - .putInt("x", x) - .putInt("y", y) - .putInt("z", z) - .putString("id", "Lectern"); - if (totalPages != 0) { - builder.putByte("hasBook", (byte) 1); - builder.putInt("totalPages", totalPages); - } else { - // Not usually needed, but helps with kicking out Bedrock players from reading the UI - builder.putByte("hasBook", (byte) 0); - } - return builder; - } } 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 a69a2cfe9..c0af434c5 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 @@ -40,6 +40,8 @@ import javax.annotation.Nonnull; import java.util.*; import java.util.stream.Collectors; +import static org.geysermc.erosion.util.BannerUtils.getJavaPatternTag; + @ItemRemapper public class BannerTranslator extends NbtItemStackTranslator { /** @@ -66,15 +68,6 @@ public class BannerTranslator extends NbtItemStackTranslator { OMINOUS_BANNER_PATTERN.add(getJavaPatternTag("bo", 15)); } - public static CompoundTag getJavaPatternTag(String pattern, int color) { - StringTag patternType = new StringTag("Pattern", pattern); - IntTag colorTag = new IntTag("Color", color); - CompoundTag tag = new CompoundTag(""); - tag.put(patternType); - tag.put(colorTag); - return tag; - } - public BannerTranslator() { appliedItems = Arrays.stream(Registries.ITEMS.forVersion(GameProtocol.DEFAULT_BEDROCK_CODEC.getProtocolVersion()).getItems()) .filter(entry -> entry.getJavaIdentifier().endsWith("banner")) @@ -117,7 +110,7 @@ public class BannerTranslator extends NbtItemStackTranslator { * @return The Java edition format pattern nbt */ public static CompoundTag getJavaBannerPattern(NbtMap pattern) { - return BannerTranslator.getJavaPatternTag(pattern.getString("Pattern"), 15 - pattern.getInt("Color")); + return getJavaPatternTag(pattern.getString("Pattern"), 15 - pattern.getInt("Color")); } /** diff --git a/core/src/main/java/org/geysermc/geyser/translator/level/block/entity/PistonBlockEntity.java b/core/src/main/java/org/geysermc/geyser/translator/level/block/entity/PistonBlockEntity.java index c52689014..0974af0e4 100644 --- a/core/src/main/java/org/geysermc/geyser/translator/level/block/entity/PistonBlockEntity.java +++ b/core/src/main/java/org/geysermc/geyser/translator/level/block/entity/PistonBlockEntity.java @@ -224,7 +224,7 @@ public class PistonBlockEntity { int blockId = session.getGeyser().getWorldManager().getBlockAt(session, blockInFront); if (BlockStateValues.isPistonHead(blockId)) { ChunkUtils.updateBlock(session, BlockStateValues.JAVA_AIR_ID, blockInFront); - } else if (session.getGeyser().getPlatformType() == PlatformType.SPIGOT && blockId == BlockStateValues.JAVA_AIR_ID) { + } else if ((session.getGeyser().getPlatformType() == PlatformType.SPIGOT || session.getErosionHandler().isActive()) && blockId == BlockStateValues.JAVA_AIR_ID) { // Spigot removes the piston head from the cache, but we need to send the block update ourselves ChunkUtils.updateBlock(session, BlockStateValues.JAVA_AIR_ID, blockInFront); } diff --git a/core/src/main/java/org/geysermc/geyser/translator/protocol/bedrock/BedrockBlockPickRequestTranslator.java b/core/src/main/java/org/geysermc/geyser/translator/protocol/bedrock/BedrockBlockPickRequestTranslator.java index 90316a8bd..5090de11f 100644 --- a/core/src/main/java/org/geysermc/geyser/translator/protocol/bedrock/BedrockBlockPickRequestTranslator.java +++ b/core/src/main/java/org/geysermc/geyser/translator/protocol/bedrock/BedrockBlockPickRequestTranslator.java @@ -71,30 +71,28 @@ public class BedrockBlockPickRequestTranslator extends PacketTranslator { + .whenComplete((tag, ex) -> session.ensureInEventLoop(() -> { if (tag == null) { pickItem(session, blockMapping); return; } - session.ensureInEventLoop(() -> { - if (addNbtData) { - ListTag lore = new ListTag("Lore"); - lore.add(new StringTag("", "\"(+NBT)\"")); - CompoundTag display = tag.get("display"); - if (display == null) { - display = new CompoundTag("display"); - tag.put(display); - } - display.put(lore); + if (addNbtData) { + ListTag lore = new ListTag("Lore"); + lore.add(new StringTag("", "\"(+NBT)\"")); + CompoundTag display = tag.get("display"); + if (display == null) { + display = new CompoundTag("display"); + tag.put(display); } - // I don't really like this... I'd rather get an ID from the block mapping I think - ItemMapping mapping = session.getItemMappings().getMapping(blockMapping.getPickItem()); + display.put(lore); + } + // I don't really like this... I'd rather get an ID from the block mapping I think + ItemMapping mapping = session.getItemMappings().getMapping(blockMapping.getPickItem()); - ItemStack itemStack = new ItemStack(mapping.getJavaId(), 1, tag); - InventoryUtils.findOrCreateItem(session, itemStack); - }); - }); + ItemStack itemStack = new ItemStack(mapping.getJavaId(), 1, tag); + InventoryUtils.findOrCreateItem(session, itemStack); + })); return; } diff --git a/core/src/main/java/org/geysermc/geyser/translator/protocol/java/JavaCustomPayloadTranslator.java b/core/src/main/java/org/geysermc/geyser/translator/protocol/java/JavaCustomPayloadTranslator.java index aaedfa443..142ed6e88 100644 --- a/core/src/main/java/org/geysermc/geyser/translator/protocol/java/JavaCustomPayloadTranslator.java +++ b/core/src/main/java/org/geysermc/geyser/translator/protocol/java/JavaCustomPayloadTranslator.java @@ -35,10 +35,13 @@ import io.netty.buffer.Unpooled; import org.geysermc.cumulus.Forms; import org.geysermc.cumulus.form.Form; import org.geysermc.cumulus.form.util.FormType; +import org.geysermc.erosion.Constants; +import org.geysermc.erosion.packet.ErosionPacket; +import org.geysermc.erosion.packet.Packets; +import org.geysermc.erosion.packet.geyserbound.GeyserboundPacket; import org.geysermc.floodgate.pluginmessage.PluginMessageChannels; import org.geysermc.geyser.GeyserImpl; import org.geysermc.geyser.GeyserLogger; -import org.geysermc.geyser.api.network.AuthType; import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.translator.protocol.PacketTranslator; import org.geysermc.geyser.translator.protocol.Translator; @@ -51,82 +54,96 @@ public class JavaCustomPayloadTranslator extends PacketTranslator erosionPacket = Packets.decode(buf); + ((GeyserboundPacket) erosionPacket).handle(session.getErosionHandler()); return; } - String channel = packet.getChannel(); - if (channel.equals(PluginMessageChannels.FORM)) { - byte[] data = packet.getData(); + session.ensureInEventLoop(() -> { + byte[] data = packet.getData(); - // receive: first byte is form type, second and third are the id, remaining is the form data - // respond: first and second byte id, remaining is form response data + // receive: first byte is form type, second and third are the id, remaining is the form data + // respond: first and second byte id, remaining is form response data - FormType type = FormType.fromOrdinal(data[0]); - if (type == null) { - throw new NullPointerException("Got type " + data[0] + " which isn't a valid form type!"); - } - - String dataString = new String(data, 3, data.length - 3, Charsets.UTF_8); - - Form form = Forms.fromJson(dataString, type, (ignored, response) -> { - byte[] finalData; - if (response == null) { - // Response data can be null as of 1.19.20 (same behaviour as empty response data) - // Only need to send the form id - finalData = new byte[]{data[1], data[2]}; - } else { - byte[] raw = response.getBytes(StandardCharsets.UTF_8); - finalData = new byte[raw.length + 2]; - - finalData[0] = data[1]; - finalData[1] = data[2]; - System.arraycopy(raw, 0, finalData, 2, raw.length); + FormType type = FormType.fromOrdinal(data[0]); + if (type == null) { + throw new NullPointerException("Got type " + data[0] + " which isn't a valid form type!"); } - session.sendDownstreamPacket(new ServerboundCustomPayloadPacket(channel, finalData)); + String dataString = new String(data, 3, data.length - 3, Charsets.UTF_8); + + Form form = Forms.fromJson(dataString, type, (ignored, response) -> { + byte[] finalData; + if (response == null) { + // Response data can be null as of 1.19.20 (same behaviour as empty response data) + // Only need to send the form id + finalData = new byte[]{data[1], data[2]}; + } else { + byte[] raw = response.getBytes(StandardCharsets.UTF_8); + finalData = new byte[raw.length + 2]; + + finalData[0] = data[1]; + finalData[1] = data[2]; + System.arraycopy(raw, 0, finalData, 2, raw.length); + } + + session.sendDownstreamPacket(new ServerboundCustomPayloadPacket(channel, finalData)); + }); + session.sendForm(form); }); - session.sendForm(form); } else if (channel.equals(PluginMessageChannels.TRANSFER)) { - byte[] data = packet.getData(); + session.ensureInEventLoop(() -> { + byte[] data = packet.getData(); - // port (4 bytes), address (remaining data) - if (data.length < 5) { - throw new NullPointerException("Transfer data should be at least 5 bytes long"); - } + // port (4 bytes), address (remaining data) + if (data.length < 5) { + throw new NullPointerException("Transfer data should be at least 5 bytes long"); + } - int port = data[0] << 24 | (data[1] & 0xFF) << 16 | (data[2] & 0xFF) << 8 | data[3] & 0xFF; - String address = new String(data, 4, data.length - 4); + int port = data[0] << 24 | (data[1] & 0xFF) << 16 | (data[2] & 0xFF) << 8 | data[3] & 0xFF; + String address = new String(data, 4, data.length - 4); - if (logger.isDebug()) { - logger.info("Transferring client to: " + address + ":" + port); - } + if (logger.isDebug()) { + logger.info("Transferring client to: " + address + ":" + port); + } - TransferPacket transferPacket = new TransferPacket(); - transferPacket.setAddress(address); - transferPacket.setPort(port); - session.sendUpstreamPacket(transferPacket); + TransferPacket transferPacket = new TransferPacket(); + transferPacket.setAddress(address); + transferPacket.setPort(port); + session.sendUpstreamPacket(transferPacket); + }); } else if (channel.equals(PluginMessageChannels.PACKET)) { - logger.debug("A packet has been sent using the Floodgate api"); - byte[] data = packet.getData(); + session.ensureInEventLoop(() -> { + logger.debug("A packet has been sent using the Floodgate api"); + byte[] data = packet.getData(); - // packet id, packet data - if (data.length < 2) { - throw new IllegalStateException("Packet data should be at least 2 bytes long"); - } + // packet id, packet data + if (data.length < 2) { + throw new IllegalStateException("Packet data should be at least 2 bytes long"); + } - int packetId = data[0] & 0xFF; - ByteBuf packetData = Unpooled.wrappedBuffer(data, 1, data.length - 1); + int packetId = data[0] & 0xFF; + ByteBuf packetData = Unpooled.wrappedBuffer(data, 1, data.length - 1); - var toSend = new UnknownPacket(); - toSend.setPacketId(packetId); - toSend.setPayload(packetData); + var toSend = new UnknownPacket(); + toSend.setPacketId(packetId); + toSend.setPayload(packetData); - session.sendUpstreamPacket(toSend); + session.sendUpstreamPacket(toSend); + }); } } + + @Override + public boolean shouldExecuteInEventLoop() { + // For Erosion packets + return false; + } } diff --git a/core/src/main/java/org/geysermc/geyser/translator/protocol/java/JavaLoginTranslator.java b/core/src/main/java/org/geysermc/geyser/translator/protocol/java/JavaLoginTranslator.java index 9afa337ef..7839a8102 100644 --- a/core/src/main/java/org/geysermc/geyser/translator/protocol/java/JavaLoginTranslator.java +++ b/core/src/main/java/org/geysermc/geyser/translator/protocol/java/JavaLoginTranslator.java @@ -36,6 +36,7 @@ import it.unimi.dsi.fastutil.ints.Int2ObjectMap; import org.geysermc.floodgate.pluginmessage.PluginMessageChannels; import org.geysermc.geyser.api.network.AuthType; import org.geysermc.geyser.entity.type.player.SessionPlayerEntity; +import org.geysermc.geyser.erosion.GeyserboundHandshakePacketHandler; import org.geysermc.geyser.level.JavaDimension; import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.text.TextDecoration; @@ -57,6 +58,11 @@ public class JavaLoginTranslator extends PacketTranslator dimensions = session.getDimensions(); dimensions.clear(); @@ -129,6 +135,10 @@ public class JavaLoginTranslator extends PacketTranslator { + blockEventPacket.setEventData(BlockStateValues.getNoteblockPitch(blockState)); + session.sendUpstreamPacket(blockEventPacket); + }); } else if (packet.getValue() instanceof PistonValue pistonValue) { PistonValueType action = (PistonValueType) packet.getType(); Direction direction = Direction.fromPistonValue(pistonValue.getDirection()); PistonCache pistonCache = session.getPistonCache(); - if (session.getGeyser().getPlatformType() == PlatformType.SPIGOT) { + if (session.getGeyser().getPlatformType() == PlatformType.SPIGOT || session.getErosionHandler().isActive()) { // Mostly handled in the GeyserPistonEvents class // Retracting sticky pistons is an exception, since the event is not called on Spigot from 1.13.2 - 1.17.1 // See https://github.com/PaperMC/Paper/blob/6fa1983e9ce177a4a412d5b950fd978620174777/patches/server/0304-Fire-BlockPistonRetractEvent-for-all-empty-pistons.patch diff --git a/core/src/main/java/org/geysermc/geyser/translator/protocol/java/level/JavaBlockUpdateTranslator.java b/core/src/main/java/org/geysermc/geyser/translator/protocol/java/level/JavaBlockUpdateTranslator.java index cd965e128..aab921aa8 100644 --- a/core/src/main/java/org/geysermc/geyser/translator/protocol/java/level/JavaBlockUpdateTranslator.java +++ b/core/src/main/java/org/geysermc/geyser/translator/protocol/java/level/JavaBlockUpdateTranslator.java @@ -43,7 +43,7 @@ public class JavaBlockUpdateTranslator extends PacketTranslator iterator = session.getLecternCache().iterator(); while (iterator.hasNext()) { diff --git a/core/src/main/java/org/geysermc/geyser/translator/protocol/java/level/JavaLevelChunkWithLightTranslator.java b/core/src/main/java/org/geysermc/geyser/translator/protocol/java/level/JavaLevelChunkWithLightTranslator.java index 081251447..4d6464643 100644 --- a/core/src/main/java/org/geysermc/geyser/translator/protocol/java/level/JavaLevelChunkWithLightTranslator.java +++ b/core/src/main/java/org/geysermc/geyser/translator/protocol/java/level/JavaLevelChunkWithLightTranslator.java @@ -38,6 +38,7 @@ import com.github.steveice10.opennbt.tag.builtin.CompoundTag; import com.nukkitx.math.vector.Vector3i; import com.nukkitx.nbt.NBTOutputStream; import com.nukkitx.nbt.NbtMap; +import com.nukkitx.nbt.NbtMapBuilder; import com.nukkitx.nbt.NbtUtils; import com.nukkitx.protocol.bedrock.packet.LevelChunkPacket; import io.netty.buffer.ByteBuf; @@ -48,6 +49,7 @@ import it.unimi.dsi.fastutil.ints.IntArrayList; import it.unimi.dsi.fastutil.ints.IntList; import it.unimi.dsi.fastutil.ints.IntLists; import it.unimi.dsi.fastutil.objects.ObjectArrayList; +import org.geysermc.erosion.util.LecternUtils; import org.geysermc.geyser.entity.type.ItemFrameEntity; import org.geysermc.geyser.level.BedrockDimension; import org.geysermc.geyser.level.block.BlockStateValues; @@ -94,6 +96,7 @@ public class JavaLevelChunkWithLightTranslator extends PacketTranslator bedrockBlockEntities = new ObjectArrayList<>(blockEntities.length); + final List lecterns = new ObjectArrayList<>(); BitSet waterloggedPaletteIds = new BitSet(); BitSet bedrockOnlyBlockEntityIds = new BitSet(); @@ -240,7 +243,9 @@ public class JavaLevelChunkWithLightTranslator extends PacketTranslator entry : session.getItemFrameCache().entrySet()) { Vector3i position = entry.getKey(); if ((position.getX() >> 4) == packet.getX() && (position.getZ() >> 4) == packet.getZ()) { diff --git a/core/src/main/java/org/geysermc/geyser/util/collection/LecternHasBookMap.java b/core/src/main/java/org/geysermc/geyser/util/collection/LecternHasBookMap.java index 913ea44d5..91cd56f8e 100644 --- a/core/src/main/java/org/geysermc/geyser/util/collection/LecternHasBookMap.java +++ b/core/src/main/java/org/geysermc/geyser/util/collection/LecternHasBookMap.java @@ -27,9 +27,9 @@ package org.geysermc.geyser.util.collection; import com.nukkitx.math.vector.Vector3i; import com.nukkitx.nbt.NbtMap; +import org.geysermc.erosion.util.LecternUtils; import org.geysermc.geyser.level.WorldManager; import org.geysermc.geyser.session.GeyserSession; -import org.geysermc.geyser.translator.inventory.LecternInventoryTranslator; import org.geysermc.geyser.util.BlockEntityUtils; /** @@ -47,27 +47,24 @@ public class LecternHasBookMap extends FixedInt2BooleanMap { int offset = blockState - this.start; if (offset < 0 || offset >= this.value.length) { // Block state is out of bounds of this map - lectern has been destroyed, if it existed - if (!worldManager.shouldExpectLecternHandled()) { + if (!worldManager.shouldExpectLecternHandled(session)) { session.getLecternCache().remove(position); } return; } boolean newLecternHasBook; - if (worldManager.shouldExpectLecternHandled()) { - // As of right now, no tag can be added asynchronously - worldManager.getLecternDataAt(session, position.getX(), position.getY(), position.getZ(), false); + if (worldManager.shouldExpectLecternHandled(session)) { + worldManager.sendLecternData(session, position.getX(), position.getY(), position.getZ()); } else if ((newLecternHasBook = this.value[offset]) != this.get(worldManager.getBlockAt(session, position))) { - // If the lectern block was updated, or it previously had a book - NbtMap newLecternTag; // newLecternHasBook = the new lectern block state's "has book" toggle. if (newLecternHasBook) { - newLecternTag = worldManager.getLecternDataAt(session, position.getX(), position.getY(), position.getZ(), false); + worldManager.sendLecternData(session, position.getX(), position.getY(), position.getZ()); } else { session.getLecternCache().remove(position); - newLecternTag = LecternInventoryTranslator.getBaseLecternTag(position.getX(), position.getY(), position.getZ(), 0).build(); + NbtMap newLecternTag = LecternUtils.getBaseLecternTag(position.getX(), position.getY(), position.getZ(), 0).build(); + BlockEntityUtils.updateBlockEntity(session, newLecternTag, position); } - BlockEntityUtils.updateBlockEntity(session, newLecternTag, position); } } } diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 4f7433034..dfa459f01 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,6 +1,7 @@ [versions] base-api = "1.0.0-SNAPSHOT" cumulus = "1.1.1" +erosion = "1.0-20230330.170602-3" events = "1.0-SNAPSHOT" jackson = "2.14.0" fastutil = "8.5.2" @@ -19,9 +20,9 @@ checkerframework = "3.19.0" log4j = "2.17.1" jline = "3.21.0" terminalconsoleappender = "1.2.0" -paper = "1.19-R0.1-SNAPSHOT" +folia = "1.19.4-R0.1-SNAPSHOT" viaversion = "4.0.0" -adapters = "1.6-SNAPSHOT" +adapters = "1.7-SNAPSHOT" commodore = "2.2" bungeecord = "a7c6ede" velocity = "3.0.0" @@ -35,6 +36,9 @@ base-api = { group = "org.geysermc.api", name = "base-api", version.ref = "base- cumulus = { group = "org.geysermc.cumulus", name = "cumulus", version.ref = "cumulus" } events = { group = "org.geysermc.event", name = "events", version.ref = "events" } +erosion-bukkit-common = { group = "org.geysermc.erosion", name = "bukkit-common", version.ref = "erosion" } +erosion-common = { group = "org.geysermc.erosion", name = "common", version.ref = "erosion" } + jackson-annotations = { group = "com.fasterxml.jackson.core", name = "jackson-annotations", version.ref = "jackson" } jackson-core = { group = "com.fasterxml.jackson.core", name = "jackson-databind", version.ref = "jackson" } jackson-dataformat-yaml = { group = "com.fasterxml.jackson.dataformat", name = "jackson-dataformat-yaml", version.ref = "jackson" } @@ -66,8 +70,8 @@ jline-terminal = { group = "org.jline", name = "jline-terminal", version.ref = " jline-terminal-jna = { group = "org.jline", name = "jline-terminal-jna", version.ref = "jline" } jline-reader = { group = "org.jline", name = "jline-reader", version.ref = "jline" } -paper-api = { group = "io.papermc.paper", name = "paper-api", version.ref = "paper" } -paper-mojangapi = { group = "io.papermc.paper", name = "paper-mojangapi", version.ref = "paper" } +folia-api = { group = "dev.folia", name = "folia-api", version.ref = "folia" } +paper-mojangapi = { group = "io.papermc.paper", name = "paper-mojangapi", version.ref = "folia" } # check these on https://modmuss50.me/fabric.html fabric-minecraft = { group = "com.mojang", name = "minecraft", version.ref = "fabric-minecraft" } From a004fe9a3143df2d4b3d7ba9ba0c5f510fac0f9d Mon Sep 17 00:00:00 2001 From: Camotoy <20743703+Camotoy@users.noreply.github.com> Date: Sat, 1 Apr 2023 01:32:06 -0400 Subject: [PATCH 57/59] Update Adapters to fix #3654 --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index dfa459f01..71b243430 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -22,7 +22,7 @@ jline = "3.21.0" terminalconsoleappender = "1.2.0" folia = "1.19.4-R0.1-SNAPSHOT" viaversion = "4.0.0" -adapters = "1.7-SNAPSHOT" +adapters = "1.8-SNAPSHOT" commodore = "2.2" bungeecord = "a7c6ede" velocity = "3.0.0" From 5871ca3f22a2a45f0be5247ffce563427818079a Mon Sep 17 00:00:00 2001 From: Camotoy <20743703+Camotoy@users.noreply.github.com> Date: Sat, 1 Apr 2023 15:25:37 -0400 Subject: [PATCH 58/59] Fix up boat types after 1.19.4 Fixes #3649 --- .../java/org/geysermc/geyser/entity/type/BoatEntity.java | 6 +++++- .../bedrock/BedrockEntityPickRequestTranslator.java | 6 ++++-- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/core/src/main/java/org/geysermc/geyser/entity/type/BoatEntity.java b/core/src/main/java/org/geysermc/geyser/entity/type/BoatEntity.java index 9fd96f46b..07c7e7643 100644 --- a/core/src/main/java/org/geysermc/geyser/entity/type/BoatEntity.java +++ b/core/src/main/java/org/geysermc/geyser/entity/type/BoatEntity.java @@ -124,7 +124,11 @@ public class BoatEntity extends Entity { public void setVariant(IntEntityMetadata entityMetadata) { variant = entityMetadata.getPrimitiveValue(); - dirtyMetadata.put(EntityData.VARIANT, variant); + dirtyMetadata.put(EntityData.VARIANT, switch (variant) { + case 6, 7 -> variant - 1; // Dark oak and mangrove + case 5, 8 -> 0; // TODO temp until 1.20. Cherry and bamboo + default -> variant; + }); } public void setPaddlingLeft(BooleanEntityMetadata entityMetadata) { diff --git a/core/src/main/java/org/geysermc/geyser/translator/protocol/bedrock/BedrockEntityPickRequestTranslator.java b/core/src/main/java/org/geysermc/geyser/translator/protocol/bedrock/BedrockEntityPickRequestTranslator.java index 482d153bb..b20bc9ad9 100644 --- a/core/src/main/java/org/geysermc/geyser/translator/protocol/bedrock/BedrockEntityPickRequestTranslator.java +++ b/core/src/main/java/org/geysermc/geyser/translator/protocol/bedrock/BedrockEntityPickRequestTranslator.java @@ -62,8 +62,10 @@ public class BedrockEntityPickRequestTranslator extends PacketTranslator "birch"; case 3 -> "jungle"; case 4 -> "acacia"; - case 5 -> "dark_oak"; - case 6 -> "mangrove"; + //case 5 -> "cherry"; TODO + case 6 -> "dark_oak"; + case 7 -> "mangrove"; + //case 8 -> "bamboo"; default -> "oak"; }; itemName = typeOfBoat + "_" + entity.getDefinition().entityType().name().toLowerCase(Locale.ROOT); From 0f99abc3a44a7beaef221f174456bbb7ef7c6227 Mon Sep 17 00:00:00 2001 From: Camotoy <20743703+Camotoy@users.noreply.github.com> Date: Sun, 2 Apr 2023 16:42:44 -0400 Subject: [PATCH 59/59] Fix #3549 --- .../java/JavaUpdateRecipesTranslator.java | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/core/src/main/java/org/geysermc/geyser/translator/protocol/java/JavaUpdateRecipesTranslator.java b/core/src/main/java/org/geysermc/geyser/translator/protocol/java/JavaUpdateRecipesTranslator.java index e665d5424..2fe25bcb1 100644 --- a/core/src/main/java/org/geysermc/geyser/translator/protocol/java/JavaUpdateRecipesTranslator.java +++ b/core/src/main/java/org/geysermc/geyser/translator/protocol/java/JavaUpdateRecipesTranslator.java @@ -70,7 +70,7 @@ public class JavaUpdateRecipesTranslator extends PacketTranslator CARTOGRAPHY_RECIPES = Arrays.asList( + private static final List CARTOGRAPHY_RECIPES = List.of( CraftingData.fromMulti(UUID.fromString("8b36268c-1829-483c-a0f1-993b7156a8f2"), ++LAST_RECIPE_NET_ID), // Map extending CraftingData.fromMulti(UUID.fromString("442d85ed-8272-4543-a6f1-418f90ded05d"), ++LAST_RECIPE_NET_ID), // Map cloning CraftingData.fromMulti(UUID.fromString("98c84b38-1085-46bd-b1ce-dd38c159e6cc"), ++LAST_RECIPE_NET_ID), // Map upgrading @@ -99,6 +99,10 @@ public class JavaUpdateRecipesTranslator extends PacketTranslator, IntSet> squashedOptions = new HashMap<>(); for (int i = 0; i < ingredients.length; i++) { if (ingredients[i].getOptions().length == 0) { squashedOptions.computeIfAbsent(Collections.singleton(ItemDescriptorWithCount.EMPTY), k -> new IntOpenHashSet()).add(i); continue; } + empty = false; Ingredient ingredient = ingredients[i]; Map> groupedByIds = Arrays.stream(ingredient.getOptions()) .map(item -> ItemDescriptorWithCount.fromItem(ItemTranslator.translateToBedrock(session, item))) @@ -249,6 +258,11 @@ public class JavaUpdateRecipesTranslator extends PacketTranslator new IntOpenHashSet()).add(i); } + if (empty) { + // Crashes Bedrock 1.19.70 otherwise + // Fixes https://github.com/GeyserMC/Geyser/issues/3549 + return null; + } int totalCombinations = 1; for (Set optionSet : squashedOptions.keySet()) { totalCombinations *= optionSet.size();