From 37d6fd8abfbc315c66e555b82343ba9e2b1db31a Mon Sep 17 00:00:00 2001 From: Camotoy <20743703+Camotoy@users.noreply.github.com> Date: Fri, 17 Sep 2021 09:39:49 -0400 Subject: [PATCH 01/81] Add an option to disable compression for Geyser clients --- .../GeyserBungeeCompressionDisabler.java | 52 ++++++++ .../bungeecord/GeyserBungeeInjector.java | 5 + .../GeyserSpigotCompressionDisabler.java | 114 ++++++++++++++++++ .../platform/spigot/GeyserSpigotInjector.java | 6 +- .../GeyserVelocityCompressionDisabler.java | 85 +++++++++++++ .../velocity/GeyserVelocityInjector.java | 17 ++- .../configuration/GeyserConfiguration.java | 2 + .../GeyserJacksonConfiguration.java | 3 + core/src/main/resources/config.yml | 5 + 9 files changed, 287 insertions(+), 2 deletions(-) create mode 100644 bootstrap/bungeecord/src/main/java/org/geysermc/geyser/platform/bungeecord/GeyserBungeeCompressionDisabler.java create mode 100644 bootstrap/spigot/src/main/java/org/geysermc/geyser/platform/spigot/GeyserSpigotCompressionDisabler.java create mode 100644 bootstrap/velocity/src/main/java/org/geysermc/geyser/platform/velocity/GeyserVelocityCompressionDisabler.java diff --git a/bootstrap/bungeecord/src/main/java/org/geysermc/geyser/platform/bungeecord/GeyserBungeeCompressionDisabler.java b/bootstrap/bungeecord/src/main/java/org/geysermc/geyser/platform/bungeecord/GeyserBungeeCompressionDisabler.java new file mode 100644 index 000000000..cf5e3cc5b --- /dev/null +++ b/bootstrap/bungeecord/src/main/java/org/geysermc/geyser/platform/bungeecord/GeyserBungeeCompressionDisabler.java @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2019-2021 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.platform.bungeecord; + +import io.netty.channel.ChannelHandlerContext; +import io.netty.channel.ChannelOutboundHandlerAdapter; +import io.netty.channel.ChannelPromise; +import net.md_5.bungee.protocol.packet.LoginSuccess; +import net.md_5.bungee.protocol.packet.SetCompression; + +public class GeyserBungeeCompressionDisabler extends ChannelOutboundHandlerAdapter { + + @Override + public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception { + if (!(msg instanceof SetCompression)) { + if (msg instanceof LoginSuccess) { + // We're past the point that compression can be enabled + if (ctx.pipeline().get("compress") != null) { + ctx.pipeline().remove("compress"); + } + if (ctx.pipeline().get("decompress") != null) { + ctx.pipeline().remove("decompress"); + } + ctx.pipeline().remove(this); + } + super.write(ctx, msg, promise); + } + } +} diff --git a/bootstrap/bungeecord/src/main/java/org/geysermc/geyser/platform/bungeecord/GeyserBungeeInjector.java b/bootstrap/bungeecord/src/main/java/org/geysermc/geyser/platform/bungeecord/GeyserBungeeInjector.java index 2b1fa10c0..2354e9753 100644 --- a/bootstrap/bungeecord/src/main/java/org/geysermc/geyser/platform/bungeecord/GeyserBungeeInjector.java +++ b/bootstrap/bungeecord/src/main/java/org/geysermc/geyser/platform/bungeecord/GeyserBungeeInjector.java @@ -140,6 +140,11 @@ public class GeyserBungeeInjector extends GeyserInjector implements Listener { channelInitializer = PipelineUtils.SERVER_CHILD; } initChannel.invoke(channelInitializer, ch); + + if (bootstrap.getGeyserConfig().isDisableCompression()) { + ch.pipeline().addAfter(PipelineUtils.PACKET_ENCODER, "geyser-compression-disabler", + new GeyserBungeeCompressionDisabler()); + } } }) .childAttr(listener, listenerInfo) diff --git a/bootstrap/spigot/src/main/java/org/geysermc/geyser/platform/spigot/GeyserSpigotCompressionDisabler.java b/bootstrap/spigot/src/main/java/org/geysermc/geyser/platform/spigot/GeyserSpigotCompressionDisabler.java new file mode 100644 index 000000000..2ee948496 --- /dev/null +++ b/bootstrap/spigot/src/main/java/org/geysermc/geyser/platform/spigot/GeyserSpigotCompressionDisabler.java @@ -0,0 +1,114 @@ +/* + * Copyright (c) 2019-2021 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.platform.spigot; + +import io.netty.channel.ChannelHandlerContext; +import io.netty.channel.ChannelOutboundHandlerAdapter; +import io.netty.channel.ChannelPromise; +import org.bukkit.Bukkit; +import org.geysermc.connector.GeyserConnector; + +/** + * Disables the compression packet (and the compression handlers from being added to the pipeline) for Geyser clients + * that won't be receiving the data over the network. + * + * As of 1.8 - 1.17.1, compression is enabled in the Netty pipeline by adding a listener after a packet is written. + * If we simply "cancel" or don't forward the packet, then the listener is never called. + */ +public class GeyserSpigotCompressionDisabler extends ChannelOutboundHandlerAdapter { + public static final boolean ENABLED; + + private static final Class COMPRESSION_PACKET_CLASS; + private static final Class LOGIN_SUCCESS_PACKET_CLASS; + private static final boolean PROTOCOL_SUPPORT_INSTALLED; + + static { + PROTOCOL_SUPPORT_INSTALLED = Bukkit.getPluginManager().getPlugin("ProtocolSupport") != null; + + Class compressionPacketClass = null; + Class loginSuccessPacketClass = null; + boolean enabled = false; + try { + compressionPacketClass = findCompressionPacket(); + loginSuccessPacketClass = findLoginSuccessPacket(); + enabled = true; + } catch (Exception e) { + GeyserConnector.getInstance().getLogger().error("Could not initialize compression disabler!", e); + } + COMPRESSION_PACKET_CLASS = compressionPacketClass; + LOGIN_SUCCESS_PACKET_CLASS = loginSuccessPacketClass; + ENABLED = enabled; + } + + public GeyserSpigotCompressionDisabler() { + if (!ENABLED) { + throw new RuntimeException("Geyser compression disabler cannot be initialized in its current state!"); + } + } + + @Override + public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception { + Class msgClass = msg.getClass(); + // Don't let any compression packet get through + if (!COMPRESSION_PACKET_CLASS.isAssignableFrom(msgClass)) { + if (LOGIN_SUCCESS_PACKET_CLASS.isAssignableFrom(msgClass)) { + if (PROTOCOL_SUPPORT_INSTALLED) { + // ProtocolSupport must send the compression packet, so let's remove what it did before it does damage + if (ctx.pipeline().get("compress") != null) { + ctx.pipeline().remove("compress"); + } + if (ctx.pipeline().get("decompress") != null) { + ctx.pipeline().remove("decompress"); + } + } + // We're past the point that a compression packet can be sent, so we can safely yeet ourselves away + ctx.channel().pipeline().remove(this); + } + super.write(ctx, msg, promise); + } else if (PROTOCOL_SUPPORT_INSTALLED) { + // We must indicate it "succeeded" or ProtocolSupport will time us out + promise.setSuccess(); + } + } + + private static Class findCompressionPacket() throws ClassNotFoundException { + try { + return Class.forName("net.minecraft.network.protocol.login.PacketLoginOutSetCompression"); + } catch (ClassNotFoundException e) { + String prefix = Bukkit.getServer().getClass().getPackage().getName().replace("org.bukkit.craftbukkit", "net.minecraft.server"); + return Class.forName(prefix + ".PacketLoginOutSetCompression"); + } + } + + private static Class findLoginSuccessPacket() throws ClassNotFoundException { + try { + return Class.forName("net.minecraft.network.protocol.login.PacketLoginOutSuccess"); + } catch (ClassNotFoundException e) { + String prefix = Bukkit.getServer().getClass().getPackage().getName().replace("org.bukkit.craftbukkit", "net.minecraft.server"); + return Class.forName(prefix + ".PacketLoginOutSuccess"); + } + } +} diff --git a/bootstrap/spigot/src/main/java/org/geysermc/geyser/platform/spigot/GeyserSpigotInjector.java b/bootstrap/spigot/src/main/java/org/geysermc/geyser/platform/spigot/GeyserSpigotInjector.java index 0fd8d849b..998d0a245 100644 --- a/bootstrap/spigot/src/main/java/org/geysermc/geyser/platform/spigot/GeyserSpigotInjector.java +++ b/bootstrap/spigot/src/main/java/org/geysermc/geyser/platform/spigot/GeyserSpigotInjector.java @@ -115,10 +115,14 @@ public class GeyserSpigotInjector extends GeyserInjector { ChannelFuture channelFuture = (new ServerBootstrap() .channel(LocalServerChannelWrapper.class) - .childHandler(new ChannelInitializer() { + .childHandler(new ChannelInitializer<>() { @Override protected void initChannel(Channel ch) throws Exception { initChannel.invoke(childHandler, ch); + + if (bootstrap.getGeyserConfig().isDisableCompression() && GeyserSpigotCompressionDisabler.ENABLED) { + ch.pipeline().addAfter("encoder", "geyser-compression-disabler", new GeyserSpigotCompressionDisabler()); + } } }) // Set to MAX_PRIORITY as MultithreadEventLoopGroup#newDefaultThreadFactory which DefaultEventLoopGroup implements does by default diff --git a/bootstrap/velocity/src/main/java/org/geysermc/geyser/platform/velocity/GeyserVelocityCompressionDisabler.java b/bootstrap/velocity/src/main/java/org/geysermc/geyser/platform/velocity/GeyserVelocityCompressionDisabler.java new file mode 100644 index 000000000..4d8b7799c --- /dev/null +++ b/bootstrap/velocity/src/main/java/org/geysermc/geyser/platform/velocity/GeyserVelocityCompressionDisabler.java @@ -0,0 +1,85 @@ +/* + * Copyright (c) 2019-2021 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.platform.velocity; + +import io.netty.channel.ChannelHandlerContext; +import io.netty.channel.ChannelOutboundHandlerAdapter; +import io.netty.channel.ChannelPromise; +import org.geysermc.connector.GeyserConnector; + +import java.lang.reflect.Method; + +public class GeyserVelocityCompressionDisabler extends ChannelOutboundHandlerAdapter { + public static final boolean ENABLED; + private static final Class COMPRESSION_PACKET_CLASS; + private static final Class LOGIN_SUCCESS_PACKET_CLASS; + private static final Method SET_COMPRESSION_METHOD; + + static { + boolean enabled = false; + Class compressionPacketClass = null; + Class loginSuccessPacketClass = null; + Method setCompressionMethod = null; + + try { + compressionPacketClass = Class.forName("com.velocitypowered.proxy.protocol.packet.SetCompression"); + loginSuccessPacketClass = Class.forName("com.velocitypowered.proxy.protocol.packet.ServerLoginSuccess"); + setCompressionMethod = Class.forName("com.velocitypowered.proxy.connection.MinecraftConnection") + .getMethod("setCompressionThreshold", int.class); + enabled = true; + } catch (Exception e) { + GeyserConnector.getInstance().getLogger().error("Could not initialize compression disabler!", e); + } + + ENABLED = enabled; + COMPRESSION_PACKET_CLASS = compressionPacketClass; + LOGIN_SUCCESS_PACKET_CLASS = loginSuccessPacketClass; + SET_COMPRESSION_METHOD = setCompressionMethod; + } + + public GeyserVelocityCompressionDisabler() { + if (!ENABLED) { + throw new RuntimeException("Geyser compression disabler cannot be initialized in its current state!"); + } + } + + @Override + public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception { + Class msgClass = msg.getClass(); + // instanceOf doesn't work. Unsure why. + if (COMPRESSION_PACKET_CLASS != msgClass) { + if (LOGIN_SUCCESS_PACKET_CLASS == msgClass) { + // We're past the point that compression can be enabled + // Invoke the method as it calls a Netty event and handles removing cleaner than we could + Object minecraftConnection = ctx.pipeline().get("handler"); + SET_COMPRESSION_METHOD.invoke(minecraftConnection, -1); + + ctx.pipeline().remove(this); + } + super.write(ctx, msg, promise); + } + } +} diff --git a/bootstrap/velocity/src/main/java/org/geysermc/geyser/platform/velocity/GeyserVelocityInjector.java b/bootstrap/velocity/src/main/java/org/geysermc/geyser/platform/velocity/GeyserVelocityInjector.java index e1a1030e3..4ffb286b8 100644 --- a/bootstrap/velocity/src/main/java/org/geysermc/geyser/platform/velocity/GeyserVelocityInjector.java +++ b/bootstrap/velocity/src/main/java/org/geysermc/geyser/platform/velocity/GeyserVelocityInjector.java @@ -34,6 +34,7 @@ import org.geysermc.geyser.network.netty.GeyserInjector; import org.geysermc.geyser.network.netty.LocalServerChannelWrapper; import java.lang.reflect.Field; +import java.lang.reflect.Method; import java.util.function.Supplier; public class GeyserVelocityInjector extends GeyserInjector { @@ -67,9 +68,23 @@ public class GeyserVelocityInjector extends GeyserInjector { workerGroupField.setAccessible(true); EventLoopGroup workerGroup = (EventLoopGroup) workerGroupField.get(connectionManager); + // This method is what initializes the connection in Java Edition, after Netty is all set. + Method initChannel = ChannelInitializer.class.getDeclaredMethod("initChannel", Channel.class); + initChannel.setAccessible(true); + ChannelFuture channelFuture = (new ServerBootstrap() .channel(LocalServerChannelWrapper.class) - .childHandler(channelInitializer) + .childHandler(new ChannelInitializer<>() { + @Override + protected void initChannel(Channel ch) throws Exception { + initChannel.invoke(channelInitializer, ch); + + if (bootstrap.getGeyserConfig().isDisableCompression() && GeyserVelocityCompressionDisabler.ENABLED) { + ch.pipeline().addAfter("minecraft-encoder", "geyser-compression-disabler", + new GeyserVelocityCompressionDisabler()); + } + } + }) .group(bossGroup, workerGroup) // Cannot be DefaultEventLoopGroup .childOption(ChannelOption.WRITE_BUFFER_WATER_MARK, serverWriteMark) // Required or else rare network freezes can occur .localAddress(LocalAddress.ANY)) 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 1f188cf40..10b21601a 100644 --- a/core/src/main/java/org/geysermc/geyser/configuration/GeyserConfiguration.java +++ b/core/src/main/java/org/geysermc/geyser/configuration/GeyserConfiguration.java @@ -182,6 +182,8 @@ public interface GeyserConfiguration { boolean isUseDirectConnection(); + boolean isDisableCompression(); + int getConfigVersion(); static void checkGeyserConfiguration(GeyserConfiguration geyserConfig, GeyserLogger geyserLogger) { 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 30a947e53..3a6b5fff6 100644 --- a/core/src/main/java/org/geysermc/geyser/configuration/GeyserJacksonConfiguration.java +++ b/core/src/main/java/org/geysermc/geyser/configuration/GeyserJacksonConfiguration.java @@ -275,6 +275,9 @@ public abstract class GeyserJacksonConfiguration implements GeyserConfiguration @JsonProperty("use-direct-connection") private boolean useDirectConnection = true; + @JsonProperty("disable-compression") + private boolean isDisableCompression = true; + @JsonProperty("config-version") private int configVersion = 0; diff --git a/core/src/main/resources/config.yml b/core/src/main/resources/config.yml index d1a956187..76d65a2f0 100644 --- a/core/src/main/resources/config.yml +++ b/core/src/main/resources/config.yml @@ -207,4 +207,9 @@ mtu: 1400 # If disabled on plugin versions, expect performance decrease and latency increase use-direct-connection: true +# Whether Geyser should attempt to disable compression for Bedrock players. This should be a benefit as there is no need to compress data +# when Java packets aren't being handled over the network. +# This requires use-direct-connection to be true. +disable-compression: true + config-version: 4 From 2af10ad8bd772a361e42da9aa912110ba40292ce Mon Sep 17 00:00:00 2001 From: Camotoy <20743703+Camotoy@users.noreply.github.com> Date: Sat, 18 Jun 2022 16:45:50 -0400 Subject: [PATCH 02/81] Update for 1.19 --- .../bungeecord/GeyserBungeeCompressionDisabler.java | 2 +- .../platform/bungeecord/GeyserBungeeInjector.java | 2 +- .../spigot/GeyserSpigotCompressionDisabler.java | 8 ++++---- .../velocity/GeyserVelocityCompressionDisabler.java | 13 ++++++------- 4 files changed, 12 insertions(+), 13 deletions(-) diff --git a/bootstrap/bungeecord/src/main/java/org/geysermc/geyser/platform/bungeecord/GeyserBungeeCompressionDisabler.java b/bootstrap/bungeecord/src/main/java/org/geysermc/geyser/platform/bungeecord/GeyserBungeeCompressionDisabler.java index cf5e3cc5b..084e1d2dc 100644 --- a/bootstrap/bungeecord/src/main/java/org/geysermc/geyser/platform/bungeecord/GeyserBungeeCompressionDisabler.java +++ b/bootstrap/bungeecord/src/main/java/org/geysermc/geyser/platform/bungeecord/GeyserBungeeCompressionDisabler.java @@ -23,7 +23,7 @@ * @link https://github.com/GeyserMC/Geyser */ -package org.geysermc.platform.bungeecord; +package org.geysermc.geyser.platform.bungeecord; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelOutboundHandlerAdapter; diff --git a/bootstrap/bungeecord/src/main/java/org/geysermc/geyser/platform/bungeecord/GeyserBungeeInjector.java b/bootstrap/bungeecord/src/main/java/org/geysermc/geyser/platform/bungeecord/GeyserBungeeInjector.java index 2354e9753..04edbadde 100644 --- a/bootstrap/bungeecord/src/main/java/org/geysermc/geyser/platform/bungeecord/GeyserBungeeInjector.java +++ b/bootstrap/bungeecord/src/main/java/org/geysermc/geyser/platform/bungeecord/GeyserBungeeInjector.java @@ -168,7 +168,7 @@ public class GeyserBungeeInjector extends GeyserInjector implements Listener { // If native compression is enabled, then this line is tripped up if a heap buffer is sent over in such a situation // as a new direct buffer is not created with that patch (HeapByteBufs throw an UnsupportedOperationException here): // https://github.com/SpigotMC/BungeeCord/blob/a283aaf724d4c9a815540cd32f3aafaa72df9e05/native/src/main/java/net/md_5/bungee/jni/zlib/NativeZlib.java#L43 - // This issue could be mitigated down the line by preventing Bungee from setting compression + // If disable compression is enabled, this can probably be disabled now, but BungeeCord (not Waterfall) complains LocalSession.createDirectByteBufAllocator(); } diff --git a/bootstrap/spigot/src/main/java/org/geysermc/geyser/platform/spigot/GeyserSpigotCompressionDisabler.java b/bootstrap/spigot/src/main/java/org/geysermc/geyser/platform/spigot/GeyserSpigotCompressionDisabler.java index 2ee948496..9b112f62f 100644 --- a/bootstrap/spigot/src/main/java/org/geysermc/geyser/platform/spigot/GeyserSpigotCompressionDisabler.java +++ b/bootstrap/spigot/src/main/java/org/geysermc/geyser/platform/spigot/GeyserSpigotCompressionDisabler.java @@ -23,13 +23,13 @@ * @link https://github.com/GeyserMC/Geyser */ -package org.geysermc.platform.spigot; +package org.geysermc.geyser.platform.spigot; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelOutboundHandlerAdapter; import io.netty.channel.ChannelPromise; import org.bukkit.Bukkit; -import org.geysermc.connector.GeyserConnector; +import org.geysermc.geyser.GeyserImpl; /** * Disables the compression packet (and the compression handlers from being added to the pipeline) for Geyser clients @@ -39,7 +39,7 @@ import org.geysermc.connector.GeyserConnector; * If we simply "cancel" or don't forward the packet, then the listener is never called. */ public class GeyserSpigotCompressionDisabler extends ChannelOutboundHandlerAdapter { - public static final boolean ENABLED; + static final boolean ENABLED; private static final Class COMPRESSION_PACKET_CLASS; private static final Class LOGIN_SUCCESS_PACKET_CLASS; @@ -56,7 +56,7 @@ public class GeyserSpigotCompressionDisabler extends ChannelOutboundHandlerAdapt loginSuccessPacketClass = findLoginSuccessPacket(); enabled = true; } catch (Exception e) { - GeyserConnector.getInstance().getLogger().error("Could not initialize compression disabler!", e); + GeyserImpl.getInstance().getLogger().error("Could not initialize compression disabler!", e); } COMPRESSION_PACKET_CLASS = compressionPacketClass; LOGIN_SUCCESS_PACKET_CLASS = loginSuccessPacketClass; diff --git a/bootstrap/velocity/src/main/java/org/geysermc/geyser/platform/velocity/GeyserVelocityCompressionDisabler.java b/bootstrap/velocity/src/main/java/org/geysermc/geyser/platform/velocity/GeyserVelocityCompressionDisabler.java index 4d8b7799c..760b68c31 100644 --- a/bootstrap/velocity/src/main/java/org/geysermc/geyser/platform/velocity/GeyserVelocityCompressionDisabler.java +++ b/bootstrap/velocity/src/main/java/org/geysermc/geyser/platform/velocity/GeyserVelocityCompressionDisabler.java @@ -23,17 +23,17 @@ * @link https://github.com/GeyserMC/Geyser */ -package org.geysermc.platform.velocity; +package org.geysermc.geyser.platform.velocity; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelOutboundHandlerAdapter; import io.netty.channel.ChannelPromise; -import org.geysermc.connector.GeyserConnector; +import org.geysermc.geyser.GeyserImpl; import java.lang.reflect.Method; public class GeyserVelocityCompressionDisabler extends ChannelOutboundHandlerAdapter { - public static final boolean ENABLED; + static final boolean ENABLED; private static final Class COMPRESSION_PACKET_CLASS; private static final Class LOGIN_SUCCESS_PACKET_CLASS; private static final Method SET_COMPRESSION_METHOD; @@ -51,7 +51,7 @@ public class GeyserVelocityCompressionDisabler extends ChannelOutboundHandlerAda .getMethod("setCompressionThreshold", int.class); enabled = true; } catch (Exception e) { - GeyserConnector.getInstance().getLogger().error("Could not initialize compression disabler!", e); + GeyserImpl.getInstance().getLogger().error("Could not initialize compression disabler!", e); } ENABLED = enabled; @@ -69,9 +69,8 @@ public class GeyserVelocityCompressionDisabler extends ChannelOutboundHandlerAda @Override public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception { Class msgClass = msg.getClass(); - // instanceOf doesn't work. Unsure why. - if (COMPRESSION_PACKET_CLASS != msgClass) { - if (LOGIN_SUCCESS_PACKET_CLASS == msgClass) { + if (!COMPRESSION_PACKET_CLASS.isAssignableFrom(msgClass)) { + if (LOGIN_SUCCESS_PACKET_CLASS.isAssignableFrom(msgClass)) { // We're past the point that compression can be enabled // Invoke the method as it calls a Netty event and handles removing cleaner than we could Object minecraftConnection = ctx.pipeline().get("handler"); From b1d832ddedf724165f9a2c6c4afad9f020c33550 Mon Sep 17 00:00:00 2001 From: Konicai <71294714+Konicai@users.noreply.github.com> Date: Wed, 9 Nov 2022 11:12:12 -0500 Subject: [PATCH 03/81] Replace ; with : in motd/submotd (#3389) --- .../network/ConnectorServerEventHandler.java | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/core/src/main/java/org/geysermc/geyser/network/ConnectorServerEventHandler.java b/core/src/main/java/org/geysermc/geyser/network/ConnectorServerEventHandler.java index c9a3201c1..49fe6c42d 100644 --- a/core/src/main/java/org/geysermc/geyser/network/ConnectorServerEventHandler.java +++ b/core/src/main/java/org/geysermc/geyser/network/ConnectorServerEventHandler.java @@ -125,13 +125,9 @@ public class ConnectorServerEventHandler implements BedrockServerEventHandler { pong.setSubMotd(config.getBedrock().secondaryMotd()); } - if (config.isPassthroughPlayerCounts() && pingInfo != null) { - pong.setPlayerCount(pingInfo.getPlayers().getOnline()); - pong.setMaximumPlayerCount(pingInfo.getPlayers().getMax()); - } else { - pong.setPlayerCount(geyser.getSessionManager().getSessions().size()); - pong.setMaximumPlayerCount(config.getMaxPlayers()); - } + // https://github.com/GeyserMC/Geyser/issues/3388 + pong.setMotd(pong.getMotd().replace(';', ':')); + pong.setSubMotd(pong.getSubMotd().replace(';', ':')); // Fallbacks to prevent errors and allow Bedrock to see the server if (pong.getMotd() == null || pong.getMotd().isBlank()) { @@ -160,6 +156,14 @@ public class ConnectorServerEventHandler implements BedrockServerEventHandler { } } + if (config.isPassthroughPlayerCounts() && pingInfo != null) { + pong.setPlayerCount(pingInfo.getPlayers().getOnline()); + pong.setMaximumPlayerCount(pingInfo.getPlayers().getMax()); + } else { + pong.setPlayerCount(geyser.getSessionManager().getSessions().size()); + pong.setMaximumPlayerCount(config.getMaxPlayers()); + } + //Bedrock will not even attempt a connection if the client thinks the server is full //so we have to fake it not being full if (pong.getPlayerCount() >= pong.getMaximumPlayerCount()) { From 7d84928627e94aab7b91d1aabc757371f48c8339 Mon Sep 17 00:00:00 2001 From: Camotoy <20743703+Camotoy@users.noreply.github.com> Date: Fri, 11 Nov 2022 11:10:08 -0500 Subject: [PATCH 04/81] (Should) remove unneeded messages about incorrect chunk heights --- .../translator/protocol/java/JavaLoginTranslator.java | 8 ++------ .../protocol/java/JavaRespawnTranslator.java | 3 +++ .../java/org/geysermc/geyser/util/ChunkUtils.java | 11 +++-------- .../java/org/geysermc/geyser/util/DimensionUtils.java | 8 ++++++-- 4 files changed, 14 insertions(+), 16 deletions(-) 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 d48d78439..ea0de7851 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 @@ -99,16 +99,10 @@ public class JavaLoginTranslator extends PacketTranslator BedrockDimension.THE_END; - case DimensionUtils.NETHER -> DimensionUtils.isCustomBedrockNetherId() ? BedrockDimension.THE_END : BedrockDimension.THE_NETHER; - default -> BedrockDimension.OVERWORLD; - }; - session.getChunkCache().setBedrockDimension(bedrockDimension); - + BedrockDimension bedrockDimension = session.getChunkCache().getBedrockDimension(); // Yell in the console if the world height is too height in the current scenario // The constraints change depending on if the player is in the overworld or not, and if experimental height is enabled // (Ignore this for the Nether. We can't change that at the moment without the workaround. :/ ) diff --git a/core/src/main/java/org/geysermc/geyser/util/DimensionUtils.java b/core/src/main/java/org/geysermc/geyser/util/DimensionUtils.java index 59f5ed55d..5989b4e9d 100644 --- a/core/src/main/java/org/geysermc/geyser/util/DimensionUtils.java +++ b/core/src/main/java/org/geysermc/geyser/util/DimensionUtils.java @@ -32,6 +32,7 @@ import com.nukkitx.protocol.bedrock.packet.ChunkRadiusUpdatedPacket; import com.nukkitx.protocol.bedrock.packet.MobEffectPacket; import com.nukkitx.protocol.bedrock.packet.StopSoundPacket; import org.geysermc.geyser.entity.type.Entity; +import org.geysermc.geyser.level.BedrockDimension; import org.geysermc.geyser.session.GeyserSession; import java.util.Set; @@ -94,8 +95,11 @@ public class DimensionUtils { changeDimensionPacket.setPosition(pos); session.sendUpstreamPacket(changeDimensionPacket); session.setDimension(javaDimension); - session.setDimensionType(session.getDimensions().get(javaDimension)); - ChunkUtils.loadDimension(session); + session.getChunkCache().setBedrockDimension(switch (javaDimension) { + case DimensionUtils.THE_END -> BedrockDimension.THE_END; + case DimensionUtils.NETHER -> DimensionUtils.isCustomBedrockNetherId() ? BedrockDimension.THE_END : BedrockDimension.THE_NETHER; + default -> BedrockDimension.OVERWORLD; + }); player.setPosition(pos); session.setSpawned(false); session.setLastChunkPosition(null); From 886d7e5b4b44e84a7cf91139ac9305b2180c8c88 Mon Sep 17 00:00:00 2001 From: Camotoy <20743703+Camotoy@users.noreply.github.com> Date: Sat, 12 Nov 2022 10:28:53 -0500 Subject: [PATCH 05/81] Fix crashes when joining a server in the Nether --- .../geyser/session/GeyserSession.java | 2 +- .../protocol/java/JavaLoginTranslator.java | 1 + .../geysermc/geyser/util/DimensionUtils.java | 24 +++++++++++++++---- 3 files changed, 21 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 67aedec15..5fd01f541 100644 --- a/core/src/main/java/org/geysermc/geyser/session/GeyserSession.java +++ b/core/src/main/java/org/geysermc/geyser/session/GeyserSession.java @@ -1420,7 +1420,7 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource { startGamePacket.setRotation(Vector2f.from(1, 1)); startGamePacket.setSeed(-1L); - startGamePacket.setDimensionId(DimensionUtils.javaToBedrock(dimension)); + startGamePacket.setDimensionId(DimensionUtils.javaToBedrock(chunkCache.getBedrockDimension())); startGamePacket.setGeneratorId(1); startGamePacket.setLevelGameType(GameType.SURVIVAL); startGamePacket.setDifficulty(1); 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 ea0de7851..a0d418324 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 @@ -99,6 +99,7 @@ public class JavaLoginTranslator extends PacketTranslator BedrockDimension.THE_END; - case DimensionUtils.NETHER -> DimensionUtils.isCustomBedrockNetherId() ? BedrockDimension.THE_END : BedrockDimension.THE_NETHER; - default -> BedrockDimension.OVERWORLD; - }); + setBedrockDimension(session, javaDimension); player.setPosition(pos); session.setSpawned(false); session.setLastChunkPosition(null); @@ -137,6 +133,24 @@ public class DimensionUtils { } } + public static void setBedrockDimension(GeyserSession session, String javaDimension) { + session.getChunkCache().setBedrockDimension(switch (javaDimension) { + case DimensionUtils.THE_END -> BedrockDimension.THE_END; + case DimensionUtils.NETHER -> DimensionUtils.isCustomBedrockNetherId() ? BedrockDimension.THE_END : BedrockDimension.THE_NETHER; + default -> BedrockDimension.OVERWORLD; + }); + } + + public static int javaToBedrock(BedrockDimension dimension) { + if (dimension == BedrockDimension.THE_NETHER) { + return BEDROCK_NETHER_ID; + } else if (dimension == BedrockDimension.THE_END) { + return 2; + } else { + return 0; + } + } + /** * Map the Java edition dimension IDs to Bedrock edition * From 5ddb0ad90a7098ed5822e630b51717f6ebb9592d Mon Sep 17 00:00:00 2001 From: Camotoy <20743703+Camotoy@users.noreply.github.com> Date: Mon, 14 Nov 2022 15:12:46 -0500 Subject: [PATCH 06/81] Allow virtual inventories to be opened when player at world height commit c53bb38a47d1a48f0b5a72059e81c4354c2b8e90 Author: Camotoy <20743703+Camotoy@users.noreply.github.com> Date: Mon Nov 14 15:12:29 2022 -0500 Final touch commit f9ff9553eda7c80620a8e6f63e14f01adb39ac8b Merge: b57109ddf 886d7e5b4 Author: Camotoy <20743703+Camotoy@users.noreply.github.com> Date: Mon Nov 14 14:54:28 2022 -0500 Merge branch 'master' of https://github.com/GeyserMC/Geyser into pull/3281 commit b57109ddf7dabe77ca97f36de48d7266eaec08a1 Author: Kevin Ludwig Date: Mon Sep 12 12:23:36 2022 +0200 Revert use entities for single chest inventories commit fda66e83b90984505bdc3d87cf41e986dd10424b Author: Kevin Ludwig Date: Sat Sep 10 11:49:40 2022 +0200 Use entities for single chest inventories, check if a block for server-side opened inventories can be placed either above or below, otherwise, close the inventory (same logic as with inventory translator found) --- .../holder/BlockInventoryHolder.java | 28 +++++++++++++++---- .../inventory/holder/InventoryHolder.java | 2 +- .../AbstractBlockInventoryTranslator.java | 4 +-- .../inventory/InventoryTranslator.java | 2 +- .../inventory/LecternInventoryTranslator.java | 3 +- .../MerchantInventoryTranslator.java | 4 ++- .../inventory/PlayerInventoryTranslator.java | 3 +- .../chest/DoubleChestInventoryTranslator.java | 27 ++++++++++++++---- .../chest/SingleChestInventoryTranslator.java | 4 +-- .../AbstractHorseInventoryTranslator.java | 3 +- .../geysermc/geyser/util/InventoryUtils.java | 4 +-- 11 files changed, 61 insertions(+), 23 deletions(-) diff --git a/core/src/main/java/org/geysermc/geyser/inventory/holder/BlockInventoryHolder.java b/core/src/main/java/org/geysermc/geyser/inventory/holder/BlockInventoryHolder.java index 379eb2566..eb15fa477 100644 --- a/core/src/main/java/org/geysermc/geyser/inventory/holder/BlockInventoryHolder.java +++ b/core/src/main/java/org/geysermc/geyser/inventory/holder/BlockInventoryHolder.java @@ -35,6 +35,7 @@ import com.nukkitx.protocol.bedrock.packet.ContainerOpenPacket; import com.nukkitx.protocol.bedrock.packet.UpdateBlockPacket; import org.geysermc.geyser.inventory.Container; import org.geysermc.geyser.inventory.Inventory; +import org.geysermc.geyser.level.BedrockDimension; import org.geysermc.geyser.registry.BlockRegistries; import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.translator.inventory.InventoryTranslator; @@ -49,6 +50,8 @@ import java.util.Set; * This class will attempt to use a real block first, if possible. */ public class BlockInventoryHolder extends InventoryHolder { + private static final int FAKE_BLOCK_DISTANCE = 1; + /** * The default Java block ID to translate as a fake block */ @@ -70,7 +73,7 @@ public class BlockInventoryHolder extends InventoryHolder { } @Override - public void prepareInventory(InventoryTranslator translator, GeyserSession session, Inventory inventory) { + public boolean prepareInventory(InventoryTranslator translator, GeyserSession session, Inventory inventory) { // Check to see if there is an existing block we can use that the player just selected. // First, verify that the player's position has not changed, so we don't try to select a block wildly out of range. // (This could be a virtual inventory that the player is opening) @@ -83,13 +86,26 @@ public class BlockInventoryHolder extends InventoryHolder { inventory.setHolderPosition(session.getLastInteractionBlockPosition()); ((Container) inventory).setUsingRealBlock(true, javaBlockString[0]); setCustomName(session, session.getLastInteractionBlockPosition(), inventory, javaBlockId); - return; + + return true; + } + } + + // Check if a fake block can be placed, either above the player or beneath. + BedrockDimension dimension = session.getChunkCache().getBedrockDimension(); + int minY = dimension.minY(), maxY = minY + dimension.height(); + Vector3i flatPlayerPosition = session.getPlayerEntity().getPosition().toInt(); + Vector3i position = flatPlayerPosition.add(Vector3i.UP); + if (position.getY() < minY) { + return false; + } + if (position.getY() >= maxY) { + position = flatPlayerPosition.sub(0, 4, 0); + if (position.getY() >= maxY) { + return false; } } - // Otherwise, time to conjure up a fake block! - Vector3i position = session.getPlayerEntity().getPosition().toInt(); - position = position.add(Vector3i.UP); UpdateBlockPacket blockPacket = new UpdateBlockPacket(); blockPacket.setDataLayer(0); blockPacket.setBlockPosition(position); @@ -99,6 +115,8 @@ public class BlockInventoryHolder extends InventoryHolder { inventory.setHolderPosition(position); setCustomName(session, position, inventory, defaultJavaBlockState); + + return true; } /** diff --git a/core/src/main/java/org/geysermc/geyser/inventory/holder/InventoryHolder.java b/core/src/main/java/org/geysermc/geyser/inventory/holder/InventoryHolder.java index fe54e1dc0..986df53db 100644 --- a/core/src/main/java/org/geysermc/geyser/inventory/holder/InventoryHolder.java +++ b/core/src/main/java/org/geysermc/geyser/inventory/holder/InventoryHolder.java @@ -30,7 +30,7 @@ import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.translator.inventory.InventoryTranslator; public abstract class InventoryHolder { - public abstract void prepareInventory(InventoryTranslator translator, GeyserSession session, Inventory inventory); + public abstract boolean prepareInventory(InventoryTranslator translator, GeyserSession session, Inventory inventory); public abstract void openInventory(InventoryTranslator translator, GeyserSession session, Inventory inventory); public abstract void closeInventory(InventoryTranslator translator, GeyserSession session, Inventory inventory); } diff --git a/core/src/main/java/org/geysermc/geyser/translator/inventory/AbstractBlockInventoryTranslator.java b/core/src/main/java/org/geysermc/geyser/translator/inventory/AbstractBlockInventoryTranslator.java index c1fabcf0f..a24178161 100644 --- a/core/src/main/java/org/geysermc/geyser/translator/inventory/AbstractBlockInventoryTranslator.java +++ b/core/src/main/java/org/geysermc/geyser/translator/inventory/AbstractBlockInventoryTranslator.java @@ -65,8 +65,8 @@ public abstract class AbstractBlockInventoryTranslator extends BaseInventoryTran } @Override - public void prepareInventory(GeyserSession session, Inventory inventory) { - holder.prepareInventory(this, session, inventory); + public boolean prepareInventory(GeyserSession session, Inventory inventory) { + return holder.prepareInventory(this, session, inventory); } @Override 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 8c7ee1c80..e6cc010f5 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 @@ -101,7 +101,7 @@ public abstract class InventoryTranslator { public final int size; - public abstract void prepareInventory(GeyserSession session, Inventory inventory); + public abstract boolean prepareInventory(GeyserSession session, Inventory inventory); public abstract void openInventory(GeyserSession session, Inventory inventory); public abstract void closeInventory(GeyserSession session, Inventory inventory); public abstract void updateProperty(GeyserSession session, Inventory inventory, int key, int value); 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 7b2f861f5..59fe81751 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 @@ -55,7 +55,8 @@ public class LecternInventoryTranslator extends BaseInventoryTranslator { } @Override - public void prepareInventory(GeyserSession session, Inventory inventory) { + public boolean prepareInventory(GeyserSession session, Inventory inventory) { + return true; } @Override diff --git a/core/src/main/java/org/geysermc/geyser/translator/inventory/MerchantInventoryTranslator.java b/core/src/main/java/org/geysermc/geyser/translator/inventory/MerchantInventoryTranslator.java index 5e9c99ae9..857b96e55 100644 --- a/core/src/main/java/org/geysermc/geyser/translator/inventory/MerchantInventoryTranslator.java +++ b/core/src/main/java/org/geysermc/geyser/translator/inventory/MerchantInventoryTranslator.java @@ -94,7 +94,7 @@ public class MerchantInventoryTranslator extends BaseInventoryTranslator { } @Override - public void prepareInventory(GeyserSession session, Inventory inventory) { + public boolean prepareInventory(GeyserSession session, Inventory inventory) { MerchantContainer merchantInventory = (MerchantContainer) inventory; if (merchantInventory.getVillager() == null) { long geyserId = session.getEntityCache().getNextEntityId().incrementAndGet(); @@ -117,6 +117,8 @@ public class MerchantInventoryTranslator extends BaseInventoryTranslator { merchantInventory.setVillager(villager); } + + return true; } @Override diff --git a/core/src/main/java/org/geysermc/geyser/translator/inventory/PlayerInventoryTranslator.java b/core/src/main/java/org/geysermc/geyser/translator/inventory/PlayerInventoryTranslator.java index ee7d6a7c6..8432b0253 100644 --- a/core/src/main/java/org/geysermc/geyser/translator/inventory/PlayerInventoryTranslator.java +++ b/core/src/main/java/org/geysermc/geyser/translator/inventory/PlayerInventoryTranslator.java @@ -514,7 +514,8 @@ public class PlayerInventoryTranslator extends InventoryTranslator { } @Override - public void prepareInventory(GeyserSession session, Inventory inventory) { + public boolean prepareInventory(GeyserSession session, Inventory inventory) { + return true; } @Override diff --git a/core/src/main/java/org/geysermc/geyser/translator/inventory/chest/DoubleChestInventoryTranslator.java b/core/src/main/java/org/geysermc/geyser/translator/inventory/chest/DoubleChestInventoryTranslator.java index 0dd8553fd..5bf96fd39 100644 --- a/core/src/main/java/org/geysermc/geyser/translator/inventory/chest/DoubleChestInventoryTranslator.java +++ b/core/src/main/java/org/geysermc/geyser/translator/inventory/chest/DoubleChestInventoryTranslator.java @@ -35,11 +35,12 @@ import com.nukkitx.protocol.bedrock.packet.ContainerOpenPacket; import com.nukkitx.protocol.bedrock.packet.UpdateBlockPacket; import org.geysermc.geyser.inventory.Container; import org.geysermc.geyser.inventory.Inventory; +import org.geysermc.geyser.level.BedrockDimension; +import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.level.block.BlockStateValues; import org.geysermc.geyser.level.block.DoubleChestValue; -import org.geysermc.geyser.registry.BlockRegistries; -import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.translator.level.block.entity.DoubleChestBlockEntityTranslator; +import org.geysermc.geyser.registry.BlockRegistries; public class DoubleChestInventoryTranslator extends ChestInventoryTranslator { private final int defaultJavaBlockState; @@ -50,7 +51,7 @@ public class DoubleChestInventoryTranslator extends ChestInventoryTranslator { } @Override - public void prepareInventory(GeyserSession session, Inventory inventory) { + public boolean prepareInventory(GeyserSession session, Inventory inventory) { // See BlockInventoryHolder - same concept there except we're also dealing with a specific block state if (session.getLastInteractionPlayerPosition().equals(session.getPlayerEntity().getPosition())) { int javaBlockId = session.getGeyser().getWorldManager().getBlockAt(session, session.getLastInteractionBlockPosition()); @@ -76,11 +77,25 @@ public class DoubleChestInventoryTranslator extends ChestInventoryTranslator { dataPacket.setData(tag.build()); dataPacket.setBlockPosition(session.getLastInteractionBlockPosition()); session.sendUpstreamPacket(dataPacket); - return; + + return true; + } + } + + // Check if a fake block can be placed, either above the player or beneath. + BedrockDimension dimension = session.getChunkCache().getBedrockDimension(); + int minY = dimension.minY(), maxY = minY + dimension.height(); + Vector3i position = session.getPlayerEntity().getPosition().toInt().add(0, 5, 0); + if (position.getY() < minY) { + return false; + } + if (position.getY() >= maxY) { + position = session.getPlayerEntity().getPosition().toInt().sub(0, 5, 0); + if (position.getY() >= maxY) { + return false; } } - Vector3i position = session.getPlayerEntity().getPosition().toInt().add(Vector3i.UP); Vector3i pairPosition = position.add(Vector3i.UNIT_X); int bedrockBlockId = session.getBlockMappings().getBedrockBlockId(defaultJavaBlockState); @@ -125,6 +140,8 @@ public class DoubleChestInventoryTranslator extends ChestInventoryTranslator { session.sendUpstreamPacket(dataPacket); inventory.setHolderPosition(position); + + return true; } @Override diff --git a/core/src/main/java/org/geysermc/geyser/translator/inventory/chest/SingleChestInventoryTranslator.java b/core/src/main/java/org/geysermc/geyser/translator/inventory/chest/SingleChestInventoryTranslator.java index 41e7bfb9f..ae914ed8c 100644 --- a/core/src/main/java/org/geysermc/geyser/translator/inventory/chest/SingleChestInventoryTranslator.java +++ b/core/src/main/java/org/geysermc/geyser/translator/inventory/chest/SingleChestInventoryTranslator.java @@ -52,8 +52,8 @@ public class SingleChestInventoryTranslator extends ChestInventoryTranslator { } @Override - public void prepareInventory(GeyserSession session, Inventory inventory) { - holder.prepareInventory(this, session, inventory); + public boolean prepareInventory(GeyserSession session, Inventory inventory) { + return holder.prepareInventory(this, session, inventory); } @Override diff --git a/core/src/main/java/org/geysermc/geyser/translator/inventory/horse/AbstractHorseInventoryTranslator.java b/core/src/main/java/org/geysermc/geyser/translator/inventory/horse/AbstractHorseInventoryTranslator.java index 0ad6ba137..538133e0e 100644 --- a/core/src/main/java/org/geysermc/geyser/translator/inventory/horse/AbstractHorseInventoryTranslator.java +++ b/core/src/main/java/org/geysermc/geyser/translator/inventory/horse/AbstractHorseInventoryTranslator.java @@ -40,7 +40,8 @@ public abstract class AbstractHorseInventoryTranslator extends BaseInventoryTran } @Override - public void prepareInventory(GeyserSession session, Inventory inventory) { + public boolean prepareInventory(GeyserSession session, Inventory inventory) { + return true; } @Override 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 56da67bec..ce88bc69c 100644 --- a/core/src/main/java/org/geysermc/geyser/util/InventoryUtils.java +++ b/core/src/main/java/org/geysermc/geyser/util/InventoryUtils.java @@ -85,8 +85,7 @@ public class InventoryUtils { public static void displayInventory(GeyserSession session, Inventory inventory) { InventoryTranslator translator = session.getInventoryTranslator(); - if (translator != null) { - translator.prepareInventory(session, inventory); + if (translator != null && translator.prepareInventory(session, inventory)) { if (translator instanceof DoubleChestInventoryTranslator && !((Container) inventory).isUsingRealBlock()) { session.scheduleInEventLoop(() -> { Inventory openInv = session.getOpenInventory(); @@ -103,7 +102,6 @@ public class InventoryUtils { translator.updateInventory(session, inventory); } } else { - // Precaution - as of 1.16 every inventory should be translated so this shouldn't happen session.setOpenInventory(null); } } From 3338f5c707bd82dc54b55895276ef90801233e9f Mon Sep 17 00:00:00 2001 From: Camotoy <20743703+Camotoy@users.noreply.github.com> Date: Tue, 15 Nov 2022 11:50:58 -0500 Subject: [PATCH 07/81] Remove duplicate inventory logic --- .../holder/BlockInventoryHolder.java | 21 +++-------- .../chest/DoubleChestInventoryTranslator.java | 19 +++------- .../geysermc/geyser/util/InventoryUtils.java | 36 ++++++++++++------- 3 files changed, 33 insertions(+), 43 deletions(-) diff --git a/core/src/main/java/org/geysermc/geyser/inventory/holder/BlockInventoryHolder.java b/core/src/main/java/org/geysermc/geyser/inventory/holder/BlockInventoryHolder.java index eb15fa477..3e0892be4 100644 --- a/core/src/main/java/org/geysermc/geyser/inventory/holder/BlockInventoryHolder.java +++ b/core/src/main/java/org/geysermc/geyser/inventory/holder/BlockInventoryHolder.java @@ -25,7 +25,6 @@ package org.geysermc.geyser.inventory.holder; -import com.google.common.collect.ImmutableSet; import com.nukkitx.math.vector.Vector3i; import com.nukkitx.nbt.NbtMap; import com.nukkitx.protocol.bedrock.data.inventory.ContainerType; @@ -35,11 +34,11 @@ import com.nukkitx.protocol.bedrock.packet.ContainerOpenPacket; import com.nukkitx.protocol.bedrock.packet.UpdateBlockPacket; import org.geysermc.geyser.inventory.Container; import org.geysermc.geyser.inventory.Inventory; -import org.geysermc.geyser.level.BedrockDimension; import org.geysermc.geyser.registry.BlockRegistries; import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.translator.inventory.InventoryTranslator; import org.geysermc.geyser.util.BlockUtils; +import org.geysermc.geyser.util.InventoryUtils; import java.util.Collections; import java.util.HashSet; @@ -50,8 +49,6 @@ import java.util.Set; * This class will attempt to use a real block first, if possible. */ public class BlockInventoryHolder extends InventoryHolder { - private static final int FAKE_BLOCK_DISTANCE = 1; - /** * The default Java block ID to translate as a fake block */ @@ -66,7 +63,7 @@ public class BlockInventoryHolder extends InventoryHolder { Set validBlocksTemp = new HashSet<>(validBlocks.length + 1); Collections.addAll(validBlocksTemp, validBlocks); validBlocksTemp.add(BlockUtils.getCleanIdentifier(javaBlockIdentifier)); - this.validBlocks = ImmutableSet.copyOf(validBlocksTemp); + this.validBlocks = Set.copyOf(validBlocksTemp); } else { this.validBlocks = Collections.singleton(BlockUtils.getCleanIdentifier(javaBlockIdentifier)); } @@ -91,20 +88,10 @@ public class BlockInventoryHolder extends InventoryHolder { } } - // Check if a fake block can be placed, either above the player or beneath. - BedrockDimension dimension = session.getChunkCache().getBedrockDimension(); - int minY = dimension.minY(), maxY = minY + dimension.height(); - Vector3i flatPlayerPosition = session.getPlayerEntity().getPosition().toInt(); - Vector3i position = flatPlayerPosition.add(Vector3i.UP); - if (position.getY() < minY) { + Vector3i position = InventoryUtils.findAvailableWorldSpace(session); + if (position == null) { return false; } - if (position.getY() >= maxY) { - position = flatPlayerPosition.sub(0, 4, 0); - if (position.getY() >= maxY) { - return false; - } - } UpdateBlockPacket blockPacket = new UpdateBlockPacket(); blockPacket.setDataLayer(0); diff --git a/core/src/main/java/org/geysermc/geyser/translator/inventory/chest/DoubleChestInventoryTranslator.java b/core/src/main/java/org/geysermc/geyser/translator/inventory/chest/DoubleChestInventoryTranslator.java index 5bf96fd39..fa20e6dbb 100644 --- a/core/src/main/java/org/geysermc/geyser/translator/inventory/chest/DoubleChestInventoryTranslator.java +++ b/core/src/main/java/org/geysermc/geyser/translator/inventory/chest/DoubleChestInventoryTranslator.java @@ -35,12 +35,12 @@ import com.nukkitx.protocol.bedrock.packet.ContainerOpenPacket; import com.nukkitx.protocol.bedrock.packet.UpdateBlockPacket; import org.geysermc.geyser.inventory.Container; import org.geysermc.geyser.inventory.Inventory; -import org.geysermc.geyser.level.BedrockDimension; -import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.level.block.BlockStateValues; import org.geysermc.geyser.level.block.DoubleChestValue; -import org.geysermc.geyser.translator.level.block.entity.DoubleChestBlockEntityTranslator; import org.geysermc.geyser.registry.BlockRegistries; +import org.geysermc.geyser.session.GeyserSession; +import org.geysermc.geyser.translator.level.block.entity.DoubleChestBlockEntityTranslator; +import org.geysermc.geyser.util.InventoryUtils; public class DoubleChestInventoryTranslator extends ChestInventoryTranslator { private final int defaultJavaBlockState; @@ -82,19 +82,10 @@ public class DoubleChestInventoryTranslator extends ChestInventoryTranslator { } } - // Check if a fake block can be placed, either above the player or beneath. - BedrockDimension dimension = session.getChunkCache().getBedrockDimension(); - int minY = dimension.minY(), maxY = minY + dimension.height(); - Vector3i position = session.getPlayerEntity().getPosition().toInt().add(0, 5, 0); - if (position.getY() < minY) { + Vector3i position = InventoryUtils.findAvailableWorldSpace(session); + if (position == null) { return false; } - if (position.getY() >= maxY) { - position = session.getPlayerEntity().getPosition().toInt().sub(0, 5, 0); - if (position.getY() >= maxY) { - return false; - } - } Vector3i pairPosition = position.add(Vector3i.UNIT_X); int bedrockBlockId = session.getBlockMappings().getBedrockBlockId(defaultJavaBlockState); 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 ce88bc69c..cb426963c 100644 --- a/core/src/main/java/org/geysermc/geyser/util/InventoryUtils.java +++ b/core/src/main/java/org/geysermc/geyser/util/InventoryUtils.java @@ -31,6 +31,7 @@ import com.github.steveice10.mc.protocol.data.game.recipe.Ingredient; import com.github.steveice10.mc.protocol.packet.ingame.serverbound.inventory.ServerboundPickItemPacket; import com.github.steveice10.mc.protocol.packet.ingame.serverbound.inventory.ServerboundSetCreativeModeSlotPacket; 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 com.nukkitx.nbt.NbtType; @@ -46,6 +47,7 @@ import org.geysermc.geyser.inventory.click.Click; import org.geysermc.geyser.inventory.recipe.GeyserRecipe; import org.geysermc.geyser.inventory.recipe.GeyserShapedRecipe; import org.geysermc.geyser.inventory.recipe.GeyserShapelessRecipe; +import org.geysermc.geyser.level.BedrockDimension; import org.geysermc.geyser.registry.Registries; import org.geysermc.geyser.registry.type.ItemMapping; import org.geysermc.geyser.session.GeyserSession; @@ -134,6 +136,28 @@ public class InventoryUtils { } } + /** + * Finds a usable block space in the world to place a fake inventory block, and returns the position. + */ + @Nullable + public static Vector3i findAvailableWorldSpace(GeyserSession session) { + // Check if a fake block can be placed, either above the player or beneath. + BedrockDimension dimension = session.getChunkCache().getBedrockDimension(); + int minY = dimension.minY(), maxY = minY + dimension.height(); + Vector3i flatPlayerPosition = session.getPlayerEntity().getPosition().toInt(); + Vector3i position = flatPlayerPosition.add(Vector3i.UP); + if (position.getY() < minY) { + return null; + } + if (position.getY() >= maxY) { + position = flatPlayerPosition.sub(0, 4, 0); + if (position.getY() >= maxY) { + return null; + } + } + return position; + } + public static void updateCursor(GeyserSession session) { InventorySlotPacket cursorPacket = new InventorySlotPacket(); cursorPacket.setContainerId(ContainerId.UI); @@ -148,18 +172,6 @@ public class InventoryUtils { return item1.getJavaId() == item2.getJavaId() && Objects.equals(item1.getNbt(), item2.getNbt()); } - public static boolean canStack(ItemStack item1, ItemStack item2) { - if (item1 == null || item2 == null) - return false; - return item1.getId() == item2.getId() && Objects.equals(item1.getNbt(), item2.getNbt()); - } - - public static boolean canStack(ItemData item1, ItemData item2) { - if (item1 == null || item2 == null) - return false; - return item1.equals(item2, false, true, true); - } - /** * Checks to see if an item stack represents air or has no count. */ From 37931e49968ca774fcc4cc21fb88db14072ae430 Mon Sep 17 00:00:00 2001 From: Kevin Ludwig <32491319+valaphee@users.noreply.github.com> Date: Fri, 18 Nov 2022 18:36:18 +0100 Subject: [PATCH 08/81] Fix potion recipes not working on pre-1.12 servers (#3408) --- .../java/org/geysermc/geyser/session/GeyserSession.java | 6 ++++++ 1 file changed, 6 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 5fd01f541..51648f8a2 100644 --- a/core/src/main/java/org/geysermc/geyser/session/GeyserSession.java +++ b/core/src/main/java/org/geysermc/geyser/session/GeyserSession.java @@ -629,6 +629,12 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource { creativePacket.setContents(this.itemMappings.getCreativeItems()); upstream.sendPacket(creativePacket); + // 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()); + upstream.sendPacket(craftingDataPacket); + PlayStatusPacket playStatusPacket = new PlayStatusPacket(); playStatusPacket.setStatus(PlayStatusPacket.Status.PLAYER_SPAWN); upstream.sendPacket(playStatusPacket); From 7171ade0bd2da19fc70e3c36005371c50048ab2a Mon Sep 17 00:00:00 2001 From: Kas-tle <26531652+Kas-tle@users.noreply.github.com> Date: Fri, 18 Nov 2022 11:04:22 -0800 Subject: [PATCH 09/81] Prevent double placement for custom block items (#3399) --- .../populator/CustomItemRegistryPopulator.java | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/core/src/main/java/org/geysermc/geyser/registry/populator/CustomItemRegistryPopulator.java b/core/src/main/java/org/geysermc/geyser/registry/populator/CustomItemRegistryPopulator.java index 94e04e972..0e40f9c43 100644 --- a/core/src/main/java/org/geysermc/geyser/registry/populator/CustomItemRegistryPopulator.java +++ b/core/src/main/java/org/geysermc/geyser/registry/populator/CustomItemRegistryPopulator.java @@ -124,6 +124,10 @@ public class CustomItemRegistryPopulator { computeArmorProperties(mapping.getArmorType(), mapping.getProtectionValue(), componentBuilder); } + if (mapping.getFirstBlockRuntimeId() != null) { + computeBlockItemProperties(mapping.getBedrockIdentifier(), componentBuilder); + } + computeRenderOffsets(false, customItemData, componentBuilder); componentBuilder.putCompound("item_properties", itemProperties.build()); @@ -260,6 +264,15 @@ public class CustomItemRegistryPopulator { } } + private static void computeBlockItemProperties(String blockItem, NbtMapBuilder componentBuilder) { + // carved pumpkin should be able to be worn and for that we would need to add wearable and armor with protection 0 here + // however this would have the side effect of preventing carved pumpkins from working as an attachable on the RP side outside the head slot + // it also causes the item to glitch when right clicked to "equip" so this should only be added here later if these issues can be overcome + + // all block items registered should be given this component to prevent double placement + componentBuilder.putCompound("minecraft:block_placer", NbtMap.builder().putString("block", blockItem).build()); + } + private static void computeRenderOffsets(boolean isHat, CustomItemData customItemData, NbtMapBuilder componentBuilder) { if (isHat) { componentBuilder.remove("minecraft:render_offsets"); From 759db72536e74eddd9b864a7ed6a6d6b7dda8e7b Mon Sep 17 00:00:00 2001 From: Konicai <71294714+Konicai@users.noreply.github.com> Date: Tue, 22 Nov 2022 17:13:59 -0500 Subject: [PATCH 10/81] Compiling for 1.19.3 --- .../type/living/monster/EndermanEntity.java | 15 ++- .../geyser/session/GeyserSession.java | 6 +- .../geysermc/geyser/text/ChatTypeEntry.java | 2 +- .../java/JavaPlayerChatTranslator.java | 2 +- .../JavaPlayerInfoRemoveTranslator.java | 65 +++++++++ .../player/JavaPlayerInfoTranslator.java | 125 ------------------ .../JavaPlayerInfoUpdateTranslator.java | 113 ++++++++++++++++ .../java/level/JavaExplodeTranslator.java | 7 +- gradle/libs.versions.toml | 4 +- 9 files changed, 201 insertions(+), 138 deletions(-) create mode 100644 core/src/main/java/org/geysermc/geyser/translator/protocol/java/entity/player/JavaPlayerInfoRemoveTranslator.java delete mode 100644 core/src/main/java/org/geysermc/geyser/translator/protocol/java/entity/player/JavaPlayerInfoTranslator.java create mode 100644 core/src/main/java/org/geysermc/geyser/translator/protocol/java/entity/player/JavaPlayerInfoUpdateTranslator.java 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 03492d518..593cd9457 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 @@ -25,8 +25,9 @@ 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; @@ -35,6 +36,7 @@ import com.nukkitx.protocol.bedrock.packet.LevelSoundEvent2Packet; import org.geysermc.geyser.entity.EntityDefinition; import org.geysermc.geyser.session.GeyserSession; +import java.util.OptionalInt; import java.util.UUID; public class EndermanEntity extends MonsterEntity { @@ -43,8 +45,15 @@ public class EndermanEntity extends MonsterEntity { super(session, entityId, geyserId, uuid, definition, position, motion, yaw, pitch, headYaw); } - public void setCarriedBlock(IntEntityMetadata entityMetadata) { - dirtyMetadata.put(EntityData.CARRIED_BLOCK, session.getBlockMappings().getBedrockBlockId(entityMetadata.getPrimitiveValue())); + public void setCarriedBlock(EntityMetadata entityMetadata) { + int bedrockBlockId; + if (entityMetadata.getValue().isPresent()) { + bedrockBlockId = entityMetadata.getValue().getAsInt(); + } else { + bedrockBlockId = session.getBlockMappings().getBedrockAirId(); + } + + dirtyMetadata.put(EntityData.CARRIED_BLOCK, bedrockBlockId); } /** 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 51648f8a2..f4d7a174a 100644 --- a/core/src/main/java/org/geysermc/geyser/session/GeyserSession.java +++ b/core/src/main/java/org/geysermc/geyser/session/GeyserSession.java @@ -1360,18 +1360,20 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource { return clientData.getLanguageCode(); } + // TODO: 1.19.3 int offest and ack'd messages BitSet??? + /** * Sends a chat message to the Java server. */ public void sendChat(String message) { - sendDownstreamPacket(new ServerboundChatPacket(message, Instant.now().toEpochMilli(), 0L, ByteArrays.EMPTY_ARRAY, false, Collections.emptyList(), null)); + sendDownstreamPacket(new ServerboundChatPacket(message, Instant.now().toEpochMilli(), 0L, null, 0, new BitSet())); } /** * Sends a command to the Java server. */ public void sendCommand(String command) { - sendDownstreamPacket(new ServerboundChatCommandPacket(command, Instant.now().toEpochMilli(), 0L, Collections.emptyList(), false, Collections.emptyList(), null)); + sendDownstreamPacket(new ServerboundChatCommandPacket(command, Instant.now().toEpochMilli(), 0L, Collections.emptyList(), 0, new BitSet())); } public void setServerRenderDistance(int renderDistance) { diff --git a/core/src/main/java/org/geysermc/geyser/text/ChatTypeEntry.java b/core/src/main/java/org/geysermc/geyser/text/ChatTypeEntry.java index c45de8f9f..af965ba8a 100644 --- a/core/src/main/java/org/geysermc/geyser/text/ChatTypeEntry.java +++ b/core/src/main/java/org/geysermc/geyser/text/ChatTypeEntry.java @@ -25,7 +25,7 @@ package org.geysermc.geyser.text; -import com.github.steveice10.mc.protocol.data.game.BuiltinChatType; +import com.github.steveice10.mc.protocol.data.game.chat.BuiltinChatType; import com.nukkitx.protocol.bedrock.packet.TextPacket; import it.unimi.dsi.fastutil.ints.Int2ObjectMap; diff --git a/core/src/main/java/org/geysermc/geyser/translator/protocol/java/JavaPlayerChatTranslator.java b/core/src/main/java/org/geysermc/geyser/translator/protocol/java/JavaPlayerChatTranslator.java index 143fa16a9..4c2d51cb8 100644 --- a/core/src/main/java/org/geysermc/geyser/translator/protocol/java/JavaPlayerChatTranslator.java +++ b/core/src/main/java/org/geysermc/geyser/translator/protocol/java/JavaPlayerChatTranslator.java @@ -51,7 +51,7 @@ public class JavaPlayerChatTranslator extends PacketTranslator { + @Override + public void translate(GeyserSession session, ClientboundPlayerInfoRemovePacket packet) { + PlayerListPacket translate = new PlayerListPacket(); + translate.setAction(PlayerListPacket.Action.REMOVE); + + for (UUID id : packet.getProfileIds()) { + // As the player entity is no longer present, we can remove the entry + PlayerEntity entity = session.getEntityCache().removePlayerEntity(id); + if (entity != null) { + // Just remove the entity's player list status + // Don't despawn the entity - the Java server will also take care of that. + entity.setPlayerList(false); + } + if (entity == session.getPlayerEntity()) { + // If removing ourself we use our AuthData UUID + translate.getEntries().add(new PlayerListPacket.Entry(session.getAuthData().uuid())); + } else { + translate.getEntries().add(new PlayerListPacket.Entry(id)); + } + } + + if (!translate.getEntries().isEmpty()) { + session.sendUpstreamPacket(translate); + } + } +} diff --git a/core/src/main/java/org/geysermc/geyser/translator/protocol/java/entity/player/JavaPlayerInfoTranslator.java b/core/src/main/java/org/geysermc/geyser/translator/protocol/java/entity/player/JavaPlayerInfoTranslator.java deleted file mode 100644 index 1cefb9731..000000000 --- a/core/src/main/java/org/geysermc/geyser/translator/protocol/java/entity/player/JavaPlayerInfoTranslator.java +++ /dev/null @@ -1,125 +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.java.entity.player; - -import com.github.steveice10.mc.auth.data.GameProfile; -import com.github.steveice10.mc.protocol.data.game.PlayerListEntry; -import com.github.steveice10.mc.protocol.data.game.PlayerListEntryAction; -import com.github.steveice10.mc.protocol.packet.ingame.clientbound.ClientboundPlayerInfoPacket; -import com.nukkitx.math.vector.Vector3f; -import com.nukkitx.protocol.bedrock.packet.PlayerListPacket; -import org.geysermc.geyser.GeyserImpl; -import org.geysermc.geyser.entity.type.player.PlayerEntity; -import org.geysermc.geyser.session.GeyserSession; -import org.geysermc.geyser.skin.SkinManager; -import org.geysermc.geyser.translator.protocol.PacketTranslator; -import org.geysermc.geyser.translator.protocol.Translator; - -@Translator(packet = ClientboundPlayerInfoPacket.class) -public class JavaPlayerInfoTranslator extends PacketTranslator { - @Override - public void translate(GeyserSession session, ClientboundPlayerInfoPacket packet) { - if (packet.getAction() != PlayerListEntryAction.ADD_PLAYER && packet.getAction() != PlayerListEntryAction.REMOVE_PLAYER) - return; - - PlayerListPacket translate = new PlayerListPacket(); - translate.setAction(packet.getAction() == PlayerListEntryAction.ADD_PLAYER ? PlayerListPacket.Action.ADD : PlayerListPacket.Action.REMOVE); - - for (PlayerListEntry entry : packet.getEntries()) { - switch (packet.getAction()) { - case ADD_PLAYER -> { - GameProfile profile = entry.getProfile(); - PlayerEntity playerEntity; - boolean self = profile.getId().equals(session.getPlayerEntity().getUuid()); - - if (self) { - // Entity is ourself - playerEntity = session.getPlayerEntity(); - } else { - playerEntity = session.getEntityCache().getPlayerEntity(profile.getId()); - } - - GameProfile.Property textures = profile.getProperty("textures"); - String texturesProperty = textures == null ? null : textures.getValue(); - - if (playerEntity == null) { - // It's a new player - playerEntity = new PlayerEntity( - session, - -1, - session.getEntityCache().getNextEntityId().incrementAndGet(), - profile.getId(), - Vector3f.ZERO, - Vector3f.ZERO, - 0, 0, 0, - profile.getName(), - texturesProperty - ); - - session.getEntityCache().addPlayerEntity(playerEntity); - } else { - playerEntity.setUsername(profile.getName()); - playerEntity.setTexturesProperty(texturesProperty); - } - - playerEntity.setPlayerList(true); - - // We'll send our own PlayerListEntry in requestAndHandleSkinAndCape - // But we need to send other player's entries so they show up in the player list - // without processing their skin information - that'll be processed when they spawn in - if (self) { - SkinManager.requestAndHandleSkinAndCape(playerEntity, session, skinAndCape -> - GeyserImpl.getInstance().getLogger().debug("Loaded Local Bedrock Java Skin Data for " + session.getClientData().getUsername())); - } else { - playerEntity.setValid(true); - PlayerListPacket.Entry playerListEntry = SkinManager.buildCachedEntry(session, playerEntity); - - translate.getEntries().add(playerListEntry); - } - } - case REMOVE_PLAYER -> { - // As the player entity is no longer present, we can remove the entry - PlayerEntity entity = session.getEntityCache().removePlayerEntity(entry.getProfile().getId()); - if (entity != null) { - // Just remove the entity's player list status - // Don't despawn the entity - the Java server will also take care of that. - entity.setPlayerList(false); - } - if (entity == session.getPlayerEntity()) { - // If removing ourself we use our AuthData UUID - translate.getEntries().add(new PlayerListPacket.Entry(session.getAuthData().uuid())); - } else { - translate.getEntries().add(new PlayerListPacket.Entry(entry.getProfile().getId())); - } - } - } - } - - if (!translate.getEntries().isEmpty()) { - session.sendUpstreamPacket(translate); - } - } -} diff --git a/core/src/main/java/org/geysermc/geyser/translator/protocol/java/entity/player/JavaPlayerInfoUpdateTranslator.java b/core/src/main/java/org/geysermc/geyser/translator/protocol/java/entity/player/JavaPlayerInfoUpdateTranslator.java new file mode 100644 index 000000000..24989c701 --- /dev/null +++ b/core/src/main/java/org/geysermc/geyser/translator/protocol/java/entity/player/JavaPlayerInfoUpdateTranslator.java @@ -0,0 +1,113 @@ +/* + * 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.java.entity.player; + +import com.github.steveice10.mc.auth.data.GameProfile; +import com.github.steveice10.mc.protocol.data.game.PlayerListEntry; +import com.github.steveice10.mc.protocol.data.game.PlayerListEntryAction; +import com.github.steveice10.mc.protocol.packet.ingame.clientbound.ClientboundPlayerInfoUpdatePacket; +import com.nukkitx.math.vector.Vector3f; +import com.nukkitx.protocol.bedrock.packet.PlayerListPacket; +import org.geysermc.geyser.GeyserImpl; +import org.geysermc.geyser.entity.type.player.PlayerEntity; +import org.geysermc.geyser.session.GeyserSession; +import org.geysermc.geyser.skin.SkinManager; +import org.geysermc.geyser.translator.protocol.PacketTranslator; +import org.geysermc.geyser.translator.protocol.Translator; + +@Translator(packet = ClientboundPlayerInfoUpdatePacket.class) +public class JavaPlayerInfoUpdateTranslator extends PacketTranslator { + @Override + public void translate(GeyserSession session, ClientboundPlayerInfoUpdatePacket packet) { + if (!packet.getActions().contains(PlayerListEntryAction.ADD_PLAYER)) { + return; + } + + PlayerListPacket translate = new PlayerListPacket(); + translate.setAction(PlayerListPacket.Action.ADD); + + for (PlayerListEntry entry : packet.getEntries()) { + for (PlayerListEntryAction action : packet.getActions()) { + if (action != PlayerListEntryAction.ADD_PLAYER) { + continue; + } + + GameProfile profile = entry.getProfile(); + PlayerEntity playerEntity; + boolean self = profile.getId().equals(session.getPlayerEntity().getUuid()); + + if (self) { + // Entity is ourself + playerEntity = session.getPlayerEntity(); + } else { + playerEntity = session.getEntityCache().getPlayerEntity(profile.getId()); + } + + GameProfile.Property textures = profile.getProperty("textures"); + String texturesProperty = textures == null ? null : textures.getValue(); + + if (playerEntity == null) { + // It's a new player + playerEntity = new PlayerEntity( + session, + -1, + session.getEntityCache().getNextEntityId().incrementAndGet(), + profile.getId(), + Vector3f.ZERO, + Vector3f.ZERO, + 0, 0, 0, + profile.getName(), + texturesProperty + ); + + session.getEntityCache().addPlayerEntity(playerEntity); + } else { + playerEntity.setUsername(profile.getName()); + playerEntity.setTexturesProperty(texturesProperty); + } + + playerEntity.setPlayerList(true); + + // We'll send our own PlayerListEntry in requestAndHandleSkinAndCape + // But we need to send other player's entries so they show up in the player list + // without processing their skin information - that'll be processed when they spawn in + if (self) { + SkinManager.requestAndHandleSkinAndCape(playerEntity, session, skinAndCape -> + GeyserImpl.getInstance().getLogger().debug("Loaded Local Bedrock Java Skin Data for " + session.getClientData().getUsername())); + } else { + playerEntity.setValid(true); + PlayerListPacket.Entry playerListEntry = SkinManager.buildCachedEntry(session, playerEntity); + + translate.getEntries().add(playerListEntry); + } + } + } + + if (!translate.getEntries().isEmpty()) { + session.sendUpstreamPacket(translate); + } + } +} diff --git a/core/src/main/java/org/geysermc/geyser/translator/protocol/java/level/JavaExplodeTranslator.java b/core/src/main/java/org/geysermc/geyser/translator/protocol/java/level/JavaExplodeTranslator.java index 51c508e57..0e90831ff 100644 --- a/core/src/main/java/org/geysermc/geyser/translator/protocol/java/level/JavaExplodeTranslator.java +++ b/core/src/main/java/org/geysermc/geyser/translator/protocol/java/level/JavaExplodeTranslator.java @@ -30,7 +30,6 @@ import com.nukkitx.math.vector.Vector3f; import com.nukkitx.math.vector.Vector3i; import com.nukkitx.nbt.NbtMap; import com.nukkitx.nbt.NbtMapBuilder; -import com.nukkitx.protocol.bedrock.data.LevelEventType; import com.nukkitx.protocol.bedrock.data.SoundEvent; import com.nukkitx.protocol.bedrock.packet.LevelEventGenericPacket; import com.nukkitx.protocol.bedrock.packet.LevelSoundEventPacket; @@ -49,9 +48,9 @@ public class JavaExplodeTranslator extends PacketTranslator Date: Tue, 22 Nov 2022 18:58:34 -0500 Subject: [PATCH 11/81] Recipe fix thing --- .../geyser/registry/populator/RecipeRegistryPopulator.java | 2 -- 1 file changed, 2 deletions(-) diff --git a/core/src/main/java/org/geysermc/geyser/registry/populator/RecipeRegistryPopulator.java b/core/src/main/java/org/geysermc/geyser/registry/populator/RecipeRegistryPopulator.java index 920ada5fb..6b6bfe9fe 100644 --- a/core/src/main/java/org/geysermc/geyser/registry/populator/RecipeRegistryPopulator.java +++ b/core/src/main/java/org/geysermc/geyser/registry/populator/RecipeRegistryPopulator.java @@ -82,8 +82,6 @@ public class RecipeRegistryPopulator { Collections.singletonList(CraftingData.fromMulti(UUID.fromString("d392b075-4ba1-40ae-8789-af868d56f6ce"), ++LAST_RECIPE_NET_ID))); craftingData.put(RecipeType.CRAFTING_SPECIAL_MAPCLONING, Collections.singletonList(CraftingData.fromMulti(UUID.fromString("85939755-ba10-4d9d-a4cc-efb7a8e943c4"), ++LAST_RECIPE_NET_ID))); - craftingData.put(RecipeType.CRAFTING_SPECIAL_BANNERADDPATTERN, - Collections.singletonList(CraftingData.fromMulti(UUID.fromString("b5c5d105-75a2-4076-af2b-923ea2bf4bf0"), ++LAST_RECIPE_NET_ID))); // https://github.com/pmmp/PocketMine-MP/blob/stable/src/pocketmine/inventory/MultiRecipe.php From b35667d187bbfaff3841b7f18ec1ed085de74a0c Mon Sep 17 00:00:00 2001 From: Konicai <71294714+Konicai@users.noreply.github.com> Date: Tue, 22 Nov 2022 19:02:01 -0500 Subject: [PATCH 12/81] Fix mistake on Enderman carried block updater --- .../geyser/entity/type/living/monster/EndermanEntity.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 593cd9457..04b46997d 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 @@ -48,7 +48,7 @@ public class EndermanEntity extends MonsterEntity { public void setCarriedBlock(EntityMetadata entityMetadata) { int bedrockBlockId; if (entityMetadata.getValue().isPresent()) { - bedrockBlockId = entityMetadata.getValue().getAsInt(); + bedrockBlockId = session.getBlockMappings().getBedrockBlockId(entityMetadata.getValue().getAsInt()); } else { bedrockBlockId = session.getBlockMappings().getBedrockAirId(); } From 70a8272bc24149395caae1dcc64a433b65728f3f Mon Sep 17 00:00:00 2001 From: Comstepr <32700815+Comstepr@users.noreply.github.com> Date: Thu, 24 Nov 2022 09:10:04 +0800 Subject: [PATCH 13/81] Bump jackson-databind to 2.14.0 (#3406) --- build-logic/build.gradle.kts | 4 ++-- gradle/libs.versions.toml | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/build-logic/build.gradle.kts b/build-logic/build.gradle.kts index c992a3ca9..e21806660 100644 --- a/build-logic/build.gradle.kts +++ b/build-logic/build.gradle.kts @@ -16,11 +16,11 @@ dependencies { // 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.13.3") + implementation("com.fasterxml.jackson.core:jackson-databind:2.14.0") } tasks.withType { kotlinOptions { jvmTarget = "16" } -} \ No newline at end of file +} diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 3724836d9..b3f3df9c5 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,5 +1,5 @@ [versions] -jackson = "2.13.4" +jackson = "2.14.0" fastutil = "8.5.2" netty = "4.1.80.Final" guava = "29.0-jre" @@ -95,4 +95,4 @@ jackson = [ "jackson-annotations", "jackson-core", "jackson-dataformat-yaml" ] fastutil = [ "fastutil-int-int-maps", "fastutil-int-long-maps", "fastutil-int-byte-maps", "fastutil-int-boolean-maps", "fastutil-object-int-maps", "fastutil-object-object-maps" ] adventure = [ "adventure-text-serializer-gson", "adventure-text-serializer-legacy", "adventure-text-serializer-plain" ] log4j = [ "log4j-api", "log4j-core", "log4j-slf4j18-impl" ] -jline = [ "jline-terminal", "jline-terminal-jna", "jline-reader" ] \ No newline at end of file +jline = [ "jline-terminal", "jline-terminal-jna", "jline-reader" ] From 1a1837619c9b10f203efd5712f3ba51b607a5849 Mon Sep 17 00:00:00 2001 From: onebeastchris Date: Thu, 24 Nov 2022 03:33:55 +0100 Subject: [PATCH 14/81] Option to specify the "unusable inventory space" item (#3402) Adds an "unusable-space-block" setting in the config.yml to specify an item to indicate unavailable spaces in a bedrock inventory. If the item is invalid, a barrier block is used & an error gets printed --- .../geyser/configuration/GeyserConfiguration.java | 2 ++ .../configuration/GeyserJacksonConfiguration.java | 3 +++ .../org/geysermc/geyser/util/InventoryUtils.java | 14 +++++++++++++- core/src/main/resources/config.yml | 4 ++++ 4 files changed, 22 insertions(+), 1 deletion(-) 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 109ad3211..8a366baae 100644 --- a/core/src/main/java/org/geysermc/geyser/configuration/GeyserConfiguration.java +++ b/core/src/main/java/org/geysermc/geyser/configuration/GeyserConfiguration.java @@ -111,6 +111,8 @@ public interface GeyserConfiguration { boolean isNotifyOnNewBedrockUpdate(); + String getUnusableSpaceBlock(); + IMetricsInfo getMetrics(); int getPendingAuthenticationTimeout(); 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 73e208963..229895c3c 100644 --- a/core/src/main/java/org/geysermc/geyser/configuration/GeyserJacksonConfiguration.java +++ b/core/src/main/java/org/geysermc/geyser/configuration/GeyserJacksonConfiguration.java @@ -154,6 +154,9 @@ public abstract class GeyserJacksonConfiguration implements GeyserConfiguration @JsonProperty("notify-on-new-bedrock-update") private boolean notifyOnNewBedrockUpdate = true; + @JsonProperty("unusable-space-block") + private String unusableSpaceBlock = "minecraft:barrier"; + private MetricsInfo metrics = new MetricsInfo(); @JsonProperty("pending-authentication-timeout") 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 ce88bc69c..4b001901d 100644 --- a/core/src/main/java/org/geysermc/geyser/util/InventoryUtils.java +++ b/core/src/main/java/org/geysermc/geyser/util/InventoryUtils.java @@ -38,6 +38,7 @@ import com.nukkitx.protocol.bedrock.data.inventory.ContainerId; import com.nukkitx.protocol.bedrock.data.inventory.ItemData; import com.nukkitx.protocol.bedrock.packet.InventorySlotPacket; import com.nukkitx.protocol.bedrock.packet.PlayerHotbarPacket; +import org.geysermc.geyser.GeyserImpl; import org.geysermc.geyser.inventory.Container; import org.geysermc.geyser.inventory.GeyserItemStack; import org.geysermc.geyser.inventory.Inventory; @@ -184,11 +185,22 @@ public class InventoryUtils { root.put("display", display.build()); return protocolVersion -> ItemData.builder() - .id(Registries.ITEMS.forVersion(protocolVersion).getStoredItems().barrier().getBedrockId()) + .id(getUnusableSpaceBlockID(protocolVersion)) .count(1) .tag(root.build()).build(); } + private static int getUnusableSpaceBlockID(int protocolVersion) { + String unusableSpaceBlock = GeyserImpl.getInstance().getConfig().getUnusableSpaceBlock(); + ItemMapping unusableSpaceBlockID = Registries.ITEMS.forVersion(protocolVersion).getMapping(unusableSpaceBlock); + if (unusableSpaceBlockID != null) { + return unusableSpaceBlockID.getBedrockId(); + } else { + GeyserImpl.getInstance().getLogger().error("Invalid value" + unusableSpaceBlock + ". Resorting to barrier block."); + return Registries.ITEMS.forVersion(protocolVersion).getStoredItems().barrier().getBedrockId(); + } + } + /** * See {@link #findOrCreateItem(GeyserSession, String)}. This is for finding a specified {@link ItemStack}. * diff --git a/core/src/main/resources/config.yml b/core/src/main/resources/config.yml index d5b9c755f..502441560 100644 --- a/core/src/main/resources/config.yml +++ b/core/src/main/resources/config.yml @@ -183,6 +183,10 @@ log-player-ip-addresses: true # auto-update. notify-on-new-bedrock-update: true +# Which item to use to mark unavailable slots in a Bedrock player inventory. Examples of this are the 2x2 crafting grid while in creative, +# or custom inventory menus with sizes different from the usual 3x9. A barrier block is the default item. +unusable-space-block: minecraft:barrier + # bStats is a stat tracker that is entirely anonymous and tracks only basic information # about Geyser, such as how many people are online, how many servers are using Geyser, # what OS is being used, etc. You can learn more about bStats here: https://bstats.org/. From f505f132162f6cac0ac6aa451f9c5b123829f822 Mon Sep 17 00:00:00 2001 From: Kevin Ludwig <32491319+valaphee@users.noreply.github.com> Date: Thu, 24 Nov 2022 21:19:55 +0100 Subject: [PATCH 15/81] Fix issues with sending multiple Bedrock resource packs (#3416) --- .../geyser/network/UpstreamPacketHandler.java | 52 ++++++++++++------- 1 file changed, 33 insertions(+), 19 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 c2a91fd75..227f0ed5a 100644 --- a/core/src/main/java/org/geysermc/geyser/network/UpstreamPacketHandler.java +++ b/core/src/main/java/org/geysermc/geyser/network/UpstreamPacketHandler.java @@ -46,9 +46,13 @@ import org.geysermc.geyser.util.MathUtils; import java.io.FileInputStream; import java.io.InputStream; +import java.util.ArrayDeque; +import java.util.Deque; public class UpstreamPacketHandler extends LoggingPacketHandler { + private Deque packsToSent = new ArrayDeque<>(); + public UpstreamPacketHandler(GeyserImpl geyser, GeyserSession session) { super(geyser, session); } @@ -161,24 +165,8 @@ public class UpstreamPacketHandler extends LoggingPacketHandler { break; case SEND_PACKS: - for(String id : packet.getPackIds()) { - ResourcePackDataInfoPacket data = new ResourcePackDataInfoPacket(); - String[] packID = id.split("_"); - ResourcePack pack = ResourcePack.PACKS.get(packID[0]); - ResourcePackManifest.Header header = pack.getManifest().getHeader(); - - data.setPackId(header.getUuid()); - int chunkCount = (int) Math.ceil((int) pack.getFile().length() / (double) ResourcePack.CHUNK_SIZE); - data.setChunkCount(chunkCount); - data.setCompressedPackSize(pack.getFile().length()); - data.setMaxChunkSize(ResourcePack.CHUNK_SIZE); - data.setHash(pack.getSha256()); - data.setPackVersion(packID[1]); - data.setPremium(false); - data.setType(ResourcePackType.RESOURCE); - - session.sendUpstreamPacket(data); - } + packsToSent.addAll(packet.getPackIds()); + sendPackDataInfo(packsToSent.pop()); break; case HAVE_ALL_PACKS: @@ -271,7 +259,8 @@ public class UpstreamPacketHandler extends LoggingPacketHandler { data.setPackId(packet.getPackId()); int offset = packet.getChunkIndex() * ResourcePack.CHUNK_SIZE; - byte[] packData = new byte[(int) MathUtils.constrain(pack.getFile().length() - offset, 0, ResourcePack.CHUNK_SIZE)]; + long remainingSize = pack.getFile().length() - offset; + byte[] packData = new byte[(int) MathUtils.constrain(remainingSize, 0, ResourcePack.CHUNK_SIZE)]; try (InputStream inputStream = new FileInputStream(pack.getFile())) { inputStream.skip(offset); @@ -283,6 +272,31 @@ public class UpstreamPacketHandler extends LoggingPacketHandler { data.setData(packData); session.sendUpstreamPacket(data); + + // Check if it is the last chunk and send next pack in queue when available. + if (remainingSize <= ResourcePack.CHUNK_SIZE && !packsToSent.isEmpty()) { + sendPackDataInfo(packsToSent.pop()); + } + return true; } + + private void sendPackDataInfo(String id) { + ResourcePackDataInfoPacket data = new ResourcePackDataInfoPacket(); + String[] packID = id.split("_"); + ResourcePack pack = ResourcePack.PACKS.get(packID[0]); + ResourcePackManifest.Header header = pack.getManifest().getHeader(); + + data.setPackId(header.getUuid()); + int chunkCount = (int) Math.ceil((int) pack.getFile().length() / (double) ResourcePack.CHUNK_SIZE); + data.setChunkCount(chunkCount); + data.setCompressedPackSize(pack.getFile().length()); + data.setMaxChunkSize(ResourcePack.CHUNK_SIZE); + data.setHash(pack.getSha256()); + data.setPackVersion(packID[1]); + data.setPremium(false); + data.setType(ResourcePackType.RESOURCE); + + session.sendUpstreamPacket(data); + } } From 09cce58746fee8a7c9b1889bed099aeb89389eb1 Mon Sep 17 00:00:00 2001 From: Konicai <71294714+Konicai@users.noreply.github.com> Date: Thu, 24 Nov 2022 18:35:00 -0500 Subject: [PATCH 16/81] use my mappings --- core/src/main/resources/mappings | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/main/resources/mappings b/core/src/main/resources/mappings index 10baa9a45..72f927083 160000 --- a/core/src/main/resources/mappings +++ b/core/src/main/resources/mappings @@ -1 +1 @@ -Subproject commit 10baa9a45de074afa643e8477bd5a4e72ecfa563 +Subproject commit 72f927083d8e08f1f1c736d7d12095c7eee3bc68 From 7dc2ca35d615a43f4c6e37fd5bd3e01a6b969c63 Mon Sep 17 00:00:00 2001 From: Kevin Ludwig <32491319+valaphee@users.noreply.github.com> Date: Mon, 28 Nov 2022 18:46:07 +0100 Subject: [PATCH 17/81] Fully strip formatting from chat and commands (#3417) --- .../geyser/inventory/AnvilContainer.java | 2 +- .../BedrockCommandRequestTranslator.java | 11 +++++----- .../bedrock/BedrockTextTranslator.java | 16 +------------- .../translator/text/MessageTranslator.java | 22 +++++++++++++++++++ .../chat/MessageTranslatorTest.java | 1 + 5 files changed, 30 insertions(+), 22 deletions(-) diff --git a/core/src/main/java/org/geysermc/geyser/inventory/AnvilContainer.java b/core/src/main/java/org/geysermc/geyser/inventory/AnvilContainer.java index 141f2b6f2..471aff8b2 100644 --- a/core/src/main/java/org/geysermc/geyser/inventory/AnvilContainer.java +++ b/core/src/main/java/org/geysermc/geyser/inventory/AnvilContainer.java @@ -76,7 +76,7 @@ public class AnvilContainer extends Container { String originalName = ItemUtils.getCustomName(getInput().getNbt()); String plainOriginalName = MessageTranslator.convertToPlainText(originalName, session.locale()); - String plainNewName = MessageTranslator.convertToPlainText(rename, session.locale()); + String plainNewName = MessageTranslator.convertToPlainText(rename); if (!plainOriginalName.equals(plainNewName)) { // Strip out formatting since Java Edition does not allow it correctRename = plainNewName; diff --git a/core/src/main/java/org/geysermc/geyser/translator/protocol/bedrock/BedrockCommandRequestTranslator.java b/core/src/main/java/org/geysermc/geyser/translator/protocol/bedrock/BedrockCommandRequestTranslator.java index 3301f7b9f..24fc8396f 100644 --- a/core/src/main/java/org/geysermc/geyser/translator/protocol/bedrock/BedrockCommandRequestTranslator.java +++ b/core/src/main/java/org/geysermc/geyser/translator/protocol/bedrock/BedrockCommandRequestTranslator.java @@ -29,6 +29,7 @@ import com.nukkitx.protocol.bedrock.packet.CommandRequestPacket; import org.geysermc.common.PlatformType; import org.geysermc.geyser.GeyserImpl; import org.geysermc.geyser.session.GeyserSession; +import org.geysermc.geyser.text.ChatColor; import org.geysermc.geyser.translator.protocol.PacketTranslator; import org.geysermc.geyser.translator.protocol.Translator; import org.geysermc.geyser.translator.text.MessageTranslator; @@ -38,16 +39,14 @@ public class BedrockCommandRequestTranslator extends PacketTranslator { @Override public void translate(GeyserSession session, TextPacket packet) { - String message = packet.getMessage(); - - // The order here is important - strip out illegal characters first, then check if it's blank - // (in case the message is blank after removing) - if (message.indexOf(ChatColor.ESCAPE) != -1) { - // Filter out all escape characters - Java doesn't let you type these - StringBuilder builder = new StringBuilder(); - for (int i = 0; i < message.length(); i++) { - char c = message.charAt(i); - if (c != ChatColor.ESCAPE) { - builder.append(c); - } - } - message = builder.toString(); - } + String message = MessageTranslator.convertToPlainText(packet.getMessage()); if (message.isBlank()) { // Java Edition (as of 1.17.1) just doesn't pass on these messages, so... we won't either! diff --git a/core/src/main/java/org/geysermc/geyser/translator/text/MessageTranslator.java b/core/src/main/java/org/geysermc/geyser/translator/text/MessageTranslator.java index 10b1bbc5a..1b267823a 100644 --- a/core/src/main/java/org/geysermc/geyser/translator/text/MessageTranslator.java +++ b/core/src/main/java/org/geysermc/geyser/translator/text/MessageTranslator.java @@ -201,6 +201,28 @@ public class MessageTranslator { return GSON_SERIALIZER.serialize(component); } + + /** + * Convert legacy format message to plain text + * + * @param message Message to convert + * @return The plain text of the message + */ + public static String convertToPlainText(String message) { + char[] input = message.toCharArray(); + char[] output = new char[input.length]; + int outputSize = 0; + for (int i = 0, inputLength = input.length; i < inputLength; i++) { + char c = input[i]; + if (c == ChatColor.ESCAPE) { + i++; + } else { + output[outputSize++] = c; + } + } + return new String(output, 0, outputSize); + } + /** * Convert JSON and legacy format message to plain text * 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 6a280ea57..e83c6f73d 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 @@ -85,6 +85,7 @@ public class MessageTranslatorTest { @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")); From 8f968230485f90e3eaa5109344bfd8ac0529b101 Mon Sep 17 00:00:00 2001 From: RednedEpic Date: Mon, 28 Nov 2022 20:53:17 -0600 Subject: [PATCH 18/81] Add support for Bedrock 1.19.50 (560) --- README.md | 2 +- .../geysermc/geyser/entity/type/Entity.java | 14 +- .../type/player/SessionPlayerEntity.java | 4 +- .../geysermc/geyser/network/GameProtocol.java | 15 +- .../populator/BlockRegistryPopulator.java | 11 +- .../populator/ItemRegistryPopulator.java | 5 +- .../geyser/session/GeyserSession.java | 185 +- ...BedrockInventoryTransactionTranslator.java | 26 +- .../JavaPlayerCombatKillTranslator.java | 2 +- .../geysermc/geyser/util/DimensionUtils.java | 18 + .../bedrock/block_palette.1_19_0.nbt | Bin 46005 -> 0 bytes .../bedrock/block_palette.1_19_50.nbt | Bin 0 -> 74726 bytes .../bedrock/creative_items.1_19_0.json | 5437 ----------------- ...19_10.json => creative_items.1_19_50.json} | 1482 ++--- .../bedrock/runtime_item_states.1_19_0.json | 4530 -------------- ....json => runtime_item_states.1_19_50.json} | 168 +- gradle/libs.versions.toml | 4 +- 17 files changed, 1058 insertions(+), 10845 deletions(-) delete mode 100644 core/src/main/resources/bedrock/block_palette.1_19_0.nbt create mode 100644 core/src/main/resources/bedrock/block_palette.1_19_50.nbt delete mode 100644 core/src/main/resources/bedrock/creative_items.1_19_0.json rename core/src/main/resources/bedrock/{creative_items.1_19_10.json => creative_items.1_19_50.json} (83%) delete mode 100644 core/src/main/resources/bedrock/runtime_item_states.1_19_0.json rename core/src/main/resources/bedrock/{runtime_item_states.1_19_10.json => runtime_item_states.1_19_50.json} (97%) diff --git a/README.md b/README.md index a28ba8eb0..f5051ed04 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.0 - 1.19.40 and Minecraft Java 1.19.1/1.19.2. +### Currently supporting Minecraft Bedrock 1.19.20 - 1.19.50 and Minecraft Java 1.19.1/1.19.2. ## 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/entity/type/Entity.java b/core/src/main/java/org/geysermc/geyser/entity/type/Entity.java index c4046bcf3..dbbdba05a 100644 --- a/core/src/main/java/org/geysermc/geyser/entity/type/Entity.java +++ b/core/src/main/java/org/geysermc/geyser/entity/type/Entity.java @@ -44,6 +44,8 @@ import lombok.Setter; import net.kyori.adventure.text.Component; import org.geysermc.geyser.entity.EntityDefinition; import org.geysermc.geyser.entity.GeyserDirtyMetadata; +import org.geysermc.geyser.entity.type.player.SessionPlayerEntity; +import org.geysermc.geyser.network.GameProtocol; import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.translator.text.MessageTranslator; import org.geysermc.geyser.util.EntityUtils; @@ -353,10 +355,14 @@ public class Entity { public void setFlags(ByteEntityMetadata entityMetadata) { byte xd = entityMetadata.getPrimitiveValue(); setFlag(EntityFlag.ON_FIRE, ((xd & 0x01) == 0x01) && !getFlag(EntityFlag.FIRE_IMMUNE)); // Otherwise immune entities sometimes flicker onfire - setFlag(EntityFlag.SNEAKING, (xd & 0x02) == 0x02); - setFlag(EntityFlag.SPRINTING, (xd & 0x08) == 0x08); - // Swimming is ignored here and instead we rely on the pose - setFlag(EntityFlag.GLIDING, (xd & 0x80) == 0x80); + // As of 1.19.50, the client does not want the sprinting, sneaking or gliding set on itself + if (!GameProtocol.supports1_19_50(session) || !(this instanceof SessionPlayerEntity sessionPlayer) || sessionPlayer.getSession() != session) { + setFlag(EntityFlag.SNEAKING, (xd & 0x02) == 0x02); + setFlag(EntityFlag.SPRINTING, (xd & 0x08) == 0x08); + + // Swimming is ignored here and instead we rely on the pose + setFlag(EntityFlag.GLIDING, (xd & 0x80) == 0x80); + } setInvisible((xd & 0x20) == 0x20); } diff --git a/core/src/main/java/org/geysermc/geyser/entity/type/player/SessionPlayerEntity.java b/core/src/main/java/org/geysermc/geyser/entity/type/player/SessionPlayerEntity.java index 74b95b73c..be1eca2c3 100644 --- a/core/src/main/java/org/geysermc/geyser/entity/type/player/SessionPlayerEntity.java +++ b/core/src/main/java/org/geysermc/geyser/entity/type/player/SessionPlayerEntity.java @@ -116,7 +116,9 @@ public class SessionPlayerEntity extends PlayerEntity { @Override public void setFlags(ByteEntityMetadata entityMetadata) { super.setFlags(entityMetadata); - session.setSwimmingInWater((entityMetadata.getPrimitiveValue() & 0x10) == 0x10 && getFlag(EntityFlag.SPRINTING)); + + byte flags = entityMetadata.getPrimitiveValue(); + session.setSwimmingInWater((flags & 0x10) == 0x10 && (flags & 0x08) == 0x08); refreshSpeed = true; } 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 7bba6bb89..d10111fee 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.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; import org.geysermc.geyser.session.GeyserSession; import java.util.ArrayList; @@ -48,7 +49,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_v557.V557_CODEC; + public static final BedrockPacketCodec DEFAULT_BEDROCK_CODEC = Bedrock_v560.V560_CODEC; /** * A list of all supported Bedrock versions that can join Geyser */ @@ -61,9 +62,6 @@ public final class GameProtocol { private static final PacketCodec DEFAULT_JAVA_CODEC = MinecraftCodec.CODEC; static { - SUPPORTED_BEDROCK_CODECS.add(Bedrock_v527.V527_CODEC.toBuilder() - .minecraftVersion("1.19.0/1.19.2") - .build()); SUPPORTED_BEDROCK_CODECS.add(Bedrock_v534.V534_CODEC.toBuilder() .minecraftVersion("1.19.10/1.19.11") .build()); @@ -74,6 +72,7 @@ public final class GameProtocol { SUPPORTED_BEDROCK_CODECS.add(Bedrock_v554.V554_CODEC.toBuilder() .minecraftVersion("1.19.30/1.19.31") .build()); + SUPPORTED_BEDROCK_CODECS.add(Bedrock_v557.V557_CODEC); SUPPORTED_BEDROCK_CODECS.add(DEFAULT_BEDROCK_CODEC); } @@ -93,14 +92,14 @@ public final class GameProtocol { /* Bedrock convenience methods to gatekeep features and easily remove the check on version removal */ - public static boolean supports1_19_10(GeyserSession session) { - return session.getUpstream().getProtocolVersion() >= Bedrock_v534.V534_CODEC.getProtocolVersion(); - } - 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(); + } + /** * Gets the {@link PacketCodec} for Minecraft: Java Edition. * 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 afc79082a..cbab03990 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 @@ -31,6 +31,7 @@ import com.google.common.collect.ImmutableMap; import com.nukkitx.nbt.*; import com.nukkitx.protocol.bedrock.v527.Bedrock_v527; import com.nukkitx.protocol.bedrock.v544.Bedrock_v544; +import com.nukkitx.protocol.bedrock.v560.Bedrock_v560; import it.unimi.dsi.fastutil.ints.IntOpenHashSet; import it.unimi.dsi.fastutil.ints.IntSet; import it.unimi.dsi.fastutil.objects.Object2IntMap; @@ -73,13 +74,9 @@ public final class BlockRegistryPopulator { private static void registerBedrockBlocks() { BiFunction emptyMapper = (bedrockIdentifier, statesBuilder) -> null; ImmutableMap, BiFunction> blockMappers = ImmutableMap., BiFunction>builder() - .put(ObjectIntPair.of("1_19_0", Bedrock_v527.V527_CODEC.getProtocolVersion()), (bedrockIdentifier, statesBuilder) -> { - if (bedrockIdentifier.equals("minecraft:muddy_mangrove_roots")) { - statesBuilder.remove("pillar_axis"); - } - return null; - }) - .put(ObjectIntPair.of("1_19_20", Bedrock_v544.V544_CODEC.getProtocolVersion()), emptyMapper).build(); + .put(ObjectIntPair.of("1_19_20", Bedrock_v544.V544_CODEC.getProtocolVersion()), emptyMapper) + .put(ObjectIntPair.of("1_19_50", Bedrock_v560.V560_CODEC.getProtocolVersion()), emptyMapper) + .build(); for (Map.Entry, BiFunction> palette : blockMappers.entrySet()) { NbtList blocksTag; 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 f928361cc..4b218aa7d 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 @@ -37,6 +37,7 @@ import com.nukkitx.protocol.bedrock.data.SoundEvent; import com.nukkitx.protocol.bedrock.data.inventory.ComponentItemData; import com.nukkitx.protocol.bedrock.data.inventory.ItemData; import com.nukkitx.protocol.bedrock.packet.StartGamePacket; +import com.nukkitx.protocol.bedrock.v560.Bedrock_v560; import it.unimi.dsi.fastutil.ints.*; import com.nukkitx.protocol.bedrock.v527.Bedrock_v527; import com.nukkitx.protocol.bedrock.v534.Bedrock_v534; @@ -76,10 +77,8 @@ public class ItemRegistryPopulator { public static void populate() { Map paletteVersions = new Object2ObjectOpenHashMap<>(); - paletteVersions.put("1_19_0", new PaletteVersion(Bedrock_v527.V527_CODEC.getProtocolVersion(), - Collections.singletonMap("minecraft:trader_llama_spawn_egg", "minecraft:llama_spawn_egg"))); - paletteVersions.put("1_19_10", new PaletteVersion(Bedrock_v534.V534_CODEC.getProtocolVersion(), Collections.emptyMap())); 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())); GeyserBootstrap bootstrap = GeyserImpl.getInstance().getBootstrap(); 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 51648f8a2..0d4eee1dd 100644 --- a/core/src/main/java/org/geysermc/geyser/session/GeyserSession.java +++ b/core/src/main/java/org/geysermc/geyser/session/GeyserSession.java @@ -58,19 +58,55 @@ import com.github.steveice10.mc.protocol.packet.ingame.serverbound.player.Server import com.github.steveice10.mc.protocol.packet.login.serverbound.ServerboundCustomQueryPacket; import com.github.steveice10.packetlib.BuiltinFlags; import com.github.steveice10.packetlib.Session; -import com.github.steveice10.packetlib.event.session.*; +import com.github.steveice10.packetlib.event.session.ConnectedEvent; +import com.github.steveice10.packetlib.event.session.DisconnectedEvent; +import com.github.steveice10.packetlib.event.session.PacketErrorEvent; +import com.github.steveice10.packetlib.event.session.PacketSendingEvent; +import com.github.steveice10.packetlib.event.session.SessionAdapter; import com.github.steveice10.packetlib.packet.Packet; import com.github.steveice10.packetlib.tcp.TcpClientSession; import com.github.steveice10.packetlib.tcp.TcpSession; import com.nukkitx.math.GenericMath; -import com.nukkitx.math.vector.*; +import com.nukkitx.math.vector.Vector2f; +import com.nukkitx.math.vector.Vector2i; +import com.nukkitx.math.vector.Vector3d; +import com.nukkitx.math.vector.Vector3f; +import com.nukkitx.math.vector.Vector3i; import com.nukkitx.nbt.NbtMap; import com.nukkitx.protocol.bedrock.BedrockPacket; import com.nukkitx.protocol.bedrock.BedrockServerSession; -import com.nukkitx.protocol.bedrock.data.*; +import com.nukkitx.protocol.bedrock.data.Ability; +import com.nukkitx.protocol.bedrock.data.AbilityLayer; +import com.nukkitx.protocol.bedrock.data.AttributeData; +import com.nukkitx.protocol.bedrock.data.AuthoritativeMovementMode; +import com.nukkitx.protocol.bedrock.data.ChatRestrictionLevel; +import com.nukkitx.protocol.bedrock.data.GamePublishSetting; +import com.nukkitx.protocol.bedrock.data.GameRuleData; +import com.nukkitx.protocol.bedrock.data.GameType; +import com.nukkitx.protocol.bedrock.data.PlayerPermission; +import com.nukkitx.protocol.bedrock.data.SoundEvent; +import com.nukkitx.protocol.bedrock.data.SyncedPlayerMovementSettings; import com.nukkitx.protocol.bedrock.data.command.CommandPermission; import com.nukkitx.protocol.bedrock.data.entity.EntityFlag; -import com.nukkitx.protocol.bedrock.packet.*; +import com.nukkitx.protocol.bedrock.packet.AvailableEntityIdentifiersPacket; +import com.nukkitx.protocol.bedrock.packet.BiomeDefinitionListPacket; +import com.nukkitx.protocol.bedrock.packet.ChunkRadiusUpdatedPacket; +import com.nukkitx.protocol.bedrock.packet.ClientboundMapItemDataPacket; +import com.nukkitx.protocol.bedrock.packet.CraftingDataPacket; +import com.nukkitx.protocol.bedrock.packet.CreativeContentPacket; +import com.nukkitx.protocol.bedrock.packet.EmoteListPacket; +import com.nukkitx.protocol.bedrock.packet.GameRulesChangedPacket; +import com.nukkitx.protocol.bedrock.packet.ItemComponentPacket; +import com.nukkitx.protocol.bedrock.packet.LevelSoundEvent2Packet; +import com.nukkitx.protocol.bedrock.packet.PlayStatusPacket; +import com.nukkitx.protocol.bedrock.packet.PlayerFogPacket; +import com.nukkitx.protocol.bedrock.packet.SetTimePacket; +import com.nukkitx.protocol.bedrock.packet.StartGamePacket; +import com.nukkitx.protocol.bedrock.packet.TextPacket; +import com.nukkitx.protocol.bedrock.packet.TransferPacket; +import com.nukkitx.protocol.bedrock.packet.UpdateAbilitiesPacket; +import com.nukkitx.protocol.bedrock.packet.UpdateAdventureSettingsPacket; +import com.nukkitx.protocol.bedrock.packet.UpdateAttributesPacket; import io.netty.channel.Channel; import io.netty.channel.EventLoop; import it.unimi.dsi.fastutil.bytes.ByteArrays; @@ -127,7 +163,20 @@ import org.geysermc.geyser.registry.type.ItemMapping; import org.geysermc.geyser.registry.type.ItemMappings; import org.geysermc.geyser.session.auth.AuthData; import org.geysermc.geyser.session.auth.BedrockClientData; -import org.geysermc.geyser.session.cache.*; +import org.geysermc.geyser.session.cache.AdvancementsCache; +import org.geysermc.geyser.session.cache.BookEditCache; +import org.geysermc.geyser.session.cache.ChunkCache; +import org.geysermc.geyser.session.cache.EntityCache; +import org.geysermc.geyser.session.cache.EntityEffectCache; +import org.geysermc.geyser.session.cache.FormCache; +import org.geysermc.geyser.session.cache.LodestoneCache; +import org.geysermc.geyser.session.cache.PistonCache; +import org.geysermc.geyser.session.cache.PreferencesCache; +import org.geysermc.geyser.session.cache.SkullCache; +import org.geysermc.geyser.session.cache.TagCache; +import org.geysermc.geyser.session.cache.TeleportCache; +import org.geysermc.geyser.session.cache.WorldBorder; +import org.geysermc.geyser.session.cache.WorldCache; import org.geysermc.geyser.skin.FloodgateSkinUploader; import org.geysermc.geyser.text.GeyserLocale; import org.geysermc.geyser.text.MinecraftLocale; @@ -143,7 +192,14 @@ import java.net.ConnectException; import java.net.InetSocketAddress; import java.nio.charset.StandardCharsets; import java.time.Instant; -import java.util.*; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.UUID; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ScheduledFuture; import java.util.concurrent.TimeUnit; @@ -1228,7 +1284,11 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource { this.pose = Pose.SNEAKING; playerEntity.setBoundingBoxHeight(1.5f); } - playerEntity.setFlag(EntityFlag.SNEAKING, sneaking); + + // As of 1.19.50, the client does not want sneaking set on itself + if (!GameProtocol.supports1_19_50(this)) { + playerEntity.setFlag(EntityFlag.SNEAKING, sneaking); + } } public void setSwimming(boolean swimming) { @@ -1628,76 +1688,40 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource { boolean spectator = gameMode == GameMode.SPECTATOR; boolean worldImmutable = gameMode == GameMode.ADVENTURE || spectator; - if (GameProtocol.supports1_19_10(this)) { - UpdateAdventureSettingsPacket adventureSettingsPacket = new UpdateAdventureSettingsPacket(); - adventureSettingsPacket.setNoMvP(false); - adventureSettingsPacket.setNoPvM(false); - adventureSettingsPacket.setImmutableWorld(worldImmutable); - adventureSettingsPacket.setShowNameTags(false); - adventureSettingsPacket.setAutoJump(true); - sendUpstreamPacket(adventureSettingsPacket); + UpdateAdventureSettingsPacket adventureSettingsPacket = new UpdateAdventureSettingsPacket(); + adventureSettingsPacket.setNoMvP(false); + adventureSettingsPacket.setNoPvM(false); + adventureSettingsPacket.setImmutableWorld(worldImmutable); + adventureSettingsPacket.setShowNameTags(false); + adventureSettingsPacket.setAutoJump(true); + sendUpstreamPacket(adventureSettingsPacket); - UpdateAbilitiesPacket updateAbilitiesPacket = new UpdateAbilitiesPacket(); - updateAbilitiesPacket.setUniqueEntityId(bedrockId); - updateAbilitiesPacket.setCommandPermission(commandPermission); - updateAbilitiesPacket.setPlayerPermission(playerPermission); + UpdateAbilitiesPacket updateAbilitiesPacket = new UpdateAbilitiesPacket(); + updateAbilitiesPacket.setUniqueEntityId(bedrockId); + updateAbilitiesPacket.setCommandPermission(commandPermission); + updateAbilitiesPacket.setPlayerPermission(playerPermission); - AbilityLayer abilityLayer = new AbilityLayer(); - Set abilities = abilityLayer.getAbilityValues(); - if (canFly || spectator) { - abilities.add(Ability.MAY_FLY); - } - - // Default stuff we have to fill in - abilities.add(Ability.BUILD); - abilities.add(Ability.MINE); - // Needed so you can drop items - abilities.add(Ability.DOORS_AND_SWITCHES); - if (gameMode == GameMode.CREATIVE) { - // Needed so the client doesn't attempt to take away items - abilities.add(Ability.INSTABUILD); - } - - if (commandPermission == CommandPermission.OPERATOR) { - // Fixes a bug? since 1.19.11 where the player can change their gamemode in Bedrock settings and - // a packet is not sent to the server. - // https://github.com/GeyserMC/Geyser/issues/3191 - abilities.add(Ability.OPERATOR_COMMANDS); - } - - if (flying || spectator) { - if (spectator && !flying) { - // We're "flying locked" in this gamemode - flying = true; - ServerboundPlayerAbilitiesPacket abilitiesPacket = new ServerboundPlayerAbilitiesPacket(true); - sendDownstreamPacket(abilitiesPacket); - } - abilities.add(Ability.FLYING); - } - - if (spectator) { - abilities.add(Ability.NO_CLIP); - } - - abilityLayer.setLayerType(AbilityLayer.Type.BASE); - abilityLayer.setFlySpeed(flySpeed); - // https://github.com/GeyserMC/Geyser/issues/3139 as of 1.19.10 - abilityLayer.setWalkSpeed(walkSpeed == 0f ? 0.01f : walkSpeed); - Collections.addAll(abilityLayer.getAbilitiesSet(), USED_ABILITIES); - - updateAbilitiesPacket.getAbilityLayers().add(abilityLayer); - sendUpstreamPacket(updateAbilitiesPacket); - return; + AbilityLayer abilityLayer = new AbilityLayer(); + Set abilities = abilityLayer.getAbilityValues(); + if (canFly || spectator) { + abilities.add(Ability.MAY_FLY); } - AdventureSettingsPacket adventureSettingsPacket = new AdventureSettingsPacket(); - adventureSettingsPacket.setUniqueEntityId(bedrockId); - adventureSettingsPacket.setCommandPermission(commandPermission); - adventureSettingsPacket.setPlayerPermission(playerPermission); + // Default stuff we have to fill in + abilities.add(Ability.BUILD); + abilities.add(Ability.MINE); + // Needed so you can drop items + abilities.add(Ability.DOORS_AND_SWITCHES); + if (gameMode == GameMode.CREATIVE) { + // Needed so the client doesn't attempt to take away items + abilities.add(Ability.INSTABUILD); + } - Set flags = adventureSettingsPacket.getSettings(); - if (canFly || spectator) { - flags.add(AdventureSetting.MAY_FLY); + if (commandPermission == CommandPermission.OPERATOR) { + // Fixes a bug? since 1.19.11 where the player can change their gamemode in Bedrock settings and + // a packet is not sent to the server. + // https://github.com/GeyserMC/Geyser/issues/3191 + abilities.add(Ability.OPERATOR_COMMANDS); } if (flying || spectator) { @@ -1707,20 +1731,21 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource { ServerboundPlayerAbilitiesPacket abilitiesPacket = new ServerboundPlayerAbilitiesPacket(true); sendDownstreamPacket(abilitiesPacket); } - flags.add(AdventureSetting.FLYING); - } - - if (worldImmutable) { - flags.add(AdventureSetting.WORLD_IMMUTABLE); + abilities.add(Ability.FLYING); } if (spectator) { - flags.add(AdventureSetting.NO_CLIP); + abilities.add(Ability.NO_CLIP); } - flags.add(AdventureSetting.AUTO_JUMP); + abilityLayer.setLayerType(AbilityLayer.Type.BASE); + abilityLayer.setFlySpeed(flySpeed); + // https://github.com/GeyserMC/Geyser/issues/3139 as of 1.19.10 + abilityLayer.setWalkSpeed(walkSpeed == 0f ? 0.01f : walkSpeed); + Collections.addAll(abilityLayer.getAbilitiesSet(), USED_ABILITIES); - sendUpstreamPacket(adventureSettingsPacket); + updateAbilitiesPacket.getAbilityLayers().add(abilityLayer); + sendUpstreamPacket(updateAbilitiesPacket); } private int getRenderDistance() { 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 436f26cb9..6992dada4 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,21 @@ 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.*; +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.nukkitx.math.vector.Vector3d; import com.nukkitx.math.vector.Vector3f; import com.nukkitx.math.vector.Vector3i; import com.nukkitx.protocol.bedrock.data.LevelEventType; -import com.nukkitx.protocol.bedrock.data.inventory.*; +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; @@ -54,7 +63,6 @@ import org.geysermc.geyser.inventory.Inventory; import org.geysermc.geyser.inventory.PlayerInventory; import org.geysermc.geyser.inventory.click.Click; import org.geysermc.geyser.level.block.BlockStateValues; -import org.geysermc.geyser.network.GameProtocol; import org.geysermc.geyser.registry.BlockRegistries; import org.geysermc.geyser.registry.type.ItemMapping; import org.geysermc.geyser.registry.type.ItemMappings; @@ -63,7 +71,11 @@ import org.geysermc.geyser.translator.inventory.InventoryTranslator; import org.geysermc.geyser.translator.inventory.item.ItemTranslator; import org.geysermc.geyser.translator.protocol.PacketTranslator; import org.geysermc.geyser.translator.protocol.Translator; -import org.geysermc.geyser.util.*; +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; @@ -464,10 +476,8 @@ public class BedrockInventoryTransactionTranslator extends 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 diff --git a/core/src/main/resources/bedrock/block_palette.1_19_50.nbt b/core/src/main/resources/bedrock/block_palette.1_19_50.nbt new file mode 100644 index 0000000000000000000000000000000000000000..8b53566c5c9b8439b71c1fd4de7052cfd6e86590 GIT binary patch literal 74726 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 Date: Mon, 28 Nov 2022 21:00:24 -0600 Subject: [PATCH 19/81] Actually drop 1.19.10 support & remove redundant check --- core/src/main/java/org/geysermc/geyser/entity/type/Entity.java | 2 +- .../main/java/org/geysermc/geyser/network/GameProtocol.java | 3 --- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/core/src/main/java/org/geysermc/geyser/entity/type/Entity.java b/core/src/main/java/org/geysermc/geyser/entity/type/Entity.java index dbbdba05a..fff15a494 100644 --- a/core/src/main/java/org/geysermc/geyser/entity/type/Entity.java +++ b/core/src/main/java/org/geysermc/geyser/entity/type/Entity.java @@ -356,7 +356,7 @@ public class Entity { byte xd = entityMetadata.getPrimitiveValue(); setFlag(EntityFlag.ON_FIRE, ((xd & 0x01) == 0x01) && !getFlag(EntityFlag.FIRE_IMMUNE)); // Otherwise immune entities sometimes flicker onfire // As of 1.19.50, the client does not want the sprinting, sneaking or gliding set on itself - if (!GameProtocol.supports1_19_50(session) || !(this instanceof SessionPlayerEntity sessionPlayer) || sessionPlayer.getSession() != session) { + if (!GameProtocol.supports1_19_50(session) || !(this instanceof SessionPlayerEntity)) { setFlag(EntityFlag.SNEAKING, (xd & 0x02) == 0x02); setFlag(EntityFlag.SPRINTING, (xd & 0x08) == 0x08); 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 d10111fee..7aa64f1f7 100644 --- a/core/src/main/java/org/geysermc/geyser/network/GameProtocol.java +++ b/core/src/main/java/org/geysermc/geyser/network/GameProtocol.java @@ -62,9 +62,6 @@ public final class GameProtocol { private static final PacketCodec DEFAULT_JAVA_CODEC = MinecraftCodec.CODEC; static { - SUPPORTED_BEDROCK_CODECS.add(Bedrock_v534.V534_CODEC.toBuilder() - .minecraftVersion("1.19.10/1.19.11") - .build()); SUPPORTED_BEDROCK_CODECS.add(Bedrock_v544.V544_CODEC); SUPPORTED_BEDROCK_CODECS.add(Bedrock_v545.V545_CODEC.toBuilder() .minecraftVersion("1.19.21/1.19.22") From 49d3254ea925ac550e33f30dd785bbbbe56eaacd Mon Sep 17 00:00:00 2001 From: RednedEpic Date: Mon, 28 Nov 2022 21:29:55 -0600 Subject: [PATCH 20/81] Use new Protocol version --- 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 a784c0853..01b7179f3 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -5,7 +5,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.14-20221129.013131-3" +protocol = "2.9.15-20221129.032348-1" raknet = "1.6.28-20220125.214016-6" mcauthlib = "d9d773e" mcprotocollib = "9f78bd5" From c7e79299b698e9d4e89f508ee70667921d47792b Mon Sep 17 00:00:00 2001 From: Konicai <71294714+Konicai@users.noreply.github.com> Date: Tue, 29 Nov 2022 16:34:09 -0500 Subject: [PATCH 21/81] Improve 1.19.50 flags (#3422) --- .../java/org/geysermc/geyser/entity/type/Entity.java | 11 ++++------- .../entity/type/player/SessionPlayerEntity.java | 4 +--- .../org/geysermc/geyser/session/GeyserSession.java | 6 +----- gradle/libs.versions.toml | 2 +- 4 files changed, 7 insertions(+), 16 deletions(-) diff --git a/core/src/main/java/org/geysermc/geyser/entity/type/Entity.java b/core/src/main/java/org/geysermc/geyser/entity/type/Entity.java index fff15a494..663dd3c33 100644 --- a/core/src/main/java/org/geysermc/geyser/entity/type/Entity.java +++ b/core/src/main/java/org/geysermc/geyser/entity/type/Entity.java @@ -355,14 +355,11 @@ public class Entity { public void setFlags(ByteEntityMetadata entityMetadata) { byte xd = entityMetadata.getPrimitiveValue(); setFlag(EntityFlag.ON_FIRE, ((xd & 0x01) == 0x01) && !getFlag(EntityFlag.FIRE_IMMUNE)); // Otherwise immune entities sometimes flicker onfire - // As of 1.19.50, the client does not want the sprinting, sneaking or gliding set on itself - if (!GameProtocol.supports1_19_50(session) || !(this instanceof SessionPlayerEntity)) { - setFlag(EntityFlag.SNEAKING, (xd & 0x02) == 0x02); - setFlag(EntityFlag.SPRINTING, (xd & 0x08) == 0x08); + setFlag(EntityFlag.SNEAKING, (xd & 0x02) == 0x02); + setFlag(EntityFlag.SPRINTING, (xd & 0x08) == 0x08); - // Swimming is ignored here and instead we rely on the pose - setFlag(EntityFlag.GLIDING, (xd & 0x80) == 0x80); - } + // Swimming is ignored here and instead we rely on the pose + setFlag(EntityFlag.GLIDING, (xd & 0x80) == 0x80); setInvisible((xd & 0x20) == 0x20); } diff --git a/core/src/main/java/org/geysermc/geyser/entity/type/player/SessionPlayerEntity.java b/core/src/main/java/org/geysermc/geyser/entity/type/player/SessionPlayerEntity.java index be1eca2c3..74b95b73c 100644 --- a/core/src/main/java/org/geysermc/geyser/entity/type/player/SessionPlayerEntity.java +++ b/core/src/main/java/org/geysermc/geyser/entity/type/player/SessionPlayerEntity.java @@ -116,9 +116,7 @@ public class SessionPlayerEntity extends PlayerEntity { @Override public void setFlags(ByteEntityMetadata entityMetadata) { super.setFlags(entityMetadata); - - byte flags = entityMetadata.getPrimitiveValue(); - session.setSwimmingInWater((flags & 0x10) == 0x10 && (flags & 0x08) == 0x08); + session.setSwimmingInWater((entityMetadata.getPrimitiveValue() & 0x10) == 0x10 && getFlag(EntityFlag.SPRINTING)); refreshSpeed = true; } 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 0d4eee1dd..17a609fb7 100644 --- a/core/src/main/java/org/geysermc/geyser/session/GeyserSession.java +++ b/core/src/main/java/org/geysermc/geyser/session/GeyserSession.java @@ -1284,11 +1284,7 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource { this.pose = Pose.SNEAKING; playerEntity.setBoundingBoxHeight(1.5f); } - - // As of 1.19.50, the client does not want sneaking set on itself - if (!GameProtocol.supports1_19_50(this)) { - playerEntity.setFlag(EntityFlag.SNEAKING, sneaking); - } + playerEntity.setFlag(EntityFlag.SNEAKING, sneaking); } public void setSwimming(boolean swimming) { diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 01b7179f3..5218c3e8a 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -5,7 +5,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.15-20221129.032348-1" +protocol = "2.9.15-20221129.204554-2" raknet = "1.6.28-20220125.214016-6" mcauthlib = "d9d773e" mcprotocollib = "9f78bd5" From c6e417a6af869cb49ca92503d6163cdd439df2ef Mon Sep 17 00:00:00 2001 From: Camotoy <20743703+Camotoy@users.noreply.github.com> Date: Tue, 29 Nov 2022 23:59:01 -0500 Subject: [PATCH 22/81] Possibly fix #3421 --- .../geysermc/geyser/network/GameProtocol.java | 2 -- .../geysermc/geyser/util/DimensionUtils.java | 25 ++++++++++--------- 2 files changed, 13 insertions(+), 14 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 7aa64f1f7..e85dc689d 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.v527.Bedrock_v527; -import com.nukkitx.protocol.bedrock.v534.Bedrock_v534; import com.nukkitx.protocol.bedrock.v544.Bedrock_v544; import com.nukkitx.protocol.bedrock.v545.Bedrock_v545; import com.nukkitx.protocol.bedrock.v554.Bedrock_v554; diff --git a/core/src/main/java/org/geysermc/geyser/util/DimensionUtils.java b/core/src/main/java/org/geysermc/geyser/util/DimensionUtils.java index edcbeefa7..887f42a8e 100644 --- a/core/src/main/java/org/geysermc/geyser/util/DimensionUtils.java +++ b/core/src/main/java/org/geysermc/geyser/util/DimensionUtils.java @@ -123,6 +123,19 @@ public class DimensionUtils { stopSoundPacket.setSoundName(""); session.sendUpstreamPacket(stopSoundPacket); + // Kind of silly but Bedrock 1.19.50 requires an acknowledgement after the + // initial chunks are sent, prior to the client acknowledgement + if (GameProtocol.supports1_19_50(session)) { + // Note: send this before chunks are sent. Fixed https://github.com/GeyserMC/Geyser/issues/3421 + PlayerActionPacket ackPacket = new PlayerActionPacket(); + ackPacket.setRuntimeEntityId(player.getGeyserId()); + ackPacket.setAction(PlayerActionType.DIMENSION_CHANGE_SUCCESS); + ackPacket.setBlockPosition(Vector3i.ZERO); + ackPacket.setResultPosition(Vector3i.ZERO); + ackPacket.setFace(0); + session.sendUpstreamPacket(ackPacket); + } + // TODO - fix this hack of a fix by sending the final dimension switching logic after sections have been sent. // The client wants sections sent to it before it can successfully respawn. ChunkUtils.sendEmptyChunks(session, player.getPosition().toInt(), 3, true); @@ -137,18 +150,6 @@ public class DimensionUtils { session.removeFog("minecraft:fog_hell"); } } - - // Kind of silly but Bedrock 1.19.50 requires an acknowledgement after the - // initial chunks are sent, prior to the client acknowledgement - if (GameProtocol.supports1_19_50(session)) { - PlayerActionPacket ackPacket = new PlayerActionPacket(); - ackPacket.setRuntimeEntityId(player.getGeyserId()); - ackPacket.setAction(PlayerActionType.DIMENSION_CHANGE_SUCCESS); - ackPacket.setBlockPosition(Vector3i.ZERO); - ackPacket.setResultPosition(Vector3i.ZERO); - ackPacket.setFace(0); - session.sendUpstreamPacket(ackPacket); - } } public static void setBedrockDimension(GeyserSession session, String javaDimension) { From 8c70ac48d509ee10963d6e8510555a2e662edbe9 Mon Sep 17 00:00:00 2001 From: Camotoy <20743703+Camotoy@users.noreply.github.com> Date: Wed, 30 Nov 2022 12:09:21 -0500 Subject: [PATCH 23/81] Fix maps in 1.19.50 Fixes #3427 --- .../protocol/java/level/JavaMapItemDataTranslator.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/core/src/main/java/org/geysermc/geyser/translator/protocol/java/level/JavaMapItemDataTranslator.java b/core/src/main/java/org/geysermc/geyser/translator/protocol/java/level/JavaMapItemDataTranslator.java index 4685cf115..3a2d1a050 100644 --- a/core/src/main/java/org/geysermc/geyser/translator/protocol/java/level/JavaMapItemDataTranslator.java +++ b/core/src/main/java/org/geysermc/geyser/translator/protocol/java/level/JavaMapItemDataTranslator.java @@ -51,6 +51,8 @@ public class JavaMapItemDataTranslator extends PacketTranslator Date: Wed, 30 Nov 2022 16:05:35 -0500 Subject: [PATCH 24/81] Fix anvil usage in 1.19.50 --- .../translator/inventory/AnvilInventoryTranslator.java | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/core/src/main/java/org/geysermc/geyser/translator/inventory/AnvilInventoryTranslator.java b/core/src/main/java/org/geysermc/geyser/translator/inventory/AnvilInventoryTranslator.java index 956fdeae0..52e542b7b 100644 --- a/core/src/main/java/org/geysermc/geyser/translator/inventory/AnvilInventoryTranslator.java +++ b/core/src/main/java/org/geysermc/geyser/translator/inventory/AnvilInventoryTranslator.java @@ -59,10 +59,12 @@ public class AnvilInventoryTranslator extends AbstractBlockInventoryTranslator { CraftRecipeOptionalStackRequestActionData data = (CraftRecipeOptionalStackRequestActionData) request.getActions()[0]; AnvilContainer container = (AnvilContainer) inventory; - // Required as of 1.18.30 - FilterTextPackets no longer appear to be sent - String name = request.getFilterStrings()[data.getFilteredStringIndex()]; - if (!Objects.equals(name, container.getNewName())) { - container.checkForRename(session, name); + if (request.getFilterStrings().length != 0) { + // Required as of 1.18.30 - FilterTextPackets no longer appear to be sent + String name = request.getFilterStrings()[data.getFilteredStringIndex()]; + if (!Objects.equals(name, container.getNewName())) { // TODO is this still necessary after pre-1.19.50 support is dropped? + container.checkForRename(session, name); + } } return super.translateRequest(session, inventory, request); From 1616b7740c252e147aa0ee96d68073d9fb34d438 Mon Sep 17 00:00:00 2001 From: Konicai <71294714+Konicai@users.noreply.github.com> Date: Thu, 1 Dec 2022 22:05:12 -0500 Subject: [PATCH 25/81] Bump mappings --- core/src/main/resources/mappings | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/main/resources/mappings b/core/src/main/resources/mappings index 72f927083..19094e360 160000 --- a/core/src/main/resources/mappings +++ b/core/src/main/resources/mappings @@ -1 +1 @@ -Subproject commit 72f927083d8e08f1f1c736d7d12095c7eee3bc68 +Subproject commit 19094e36080d78212534f5f0d073fb5d3f68a89f From 58eede37c0ef2dac2b2c1c1a8671993dced9e6c5 Mon Sep 17 00:00:00 2001 From: Konicai <71294714+Konicai@users.noreply.github.com> Date: Fri, 2 Dec 2022 00:28:24 -0500 Subject: [PATCH 26/81] Drop anything below 1.19.50 --- .../geysermc/geyser/network/GameProtocol.java | 18 - .../populator/BlockRegistryPopulator.java | 3 - .../populator/ItemRegistryPopulator.java | 1 - .../BedrockRequestAbilityTranslator.java | 5 - .../geysermc/geyser/util/DimensionUtils.java | 18 +- .../bedrock/block_palette.1_19_20.nbt | Bin 55132 -> 0 bytes .../bedrock/creative_items.1_19_20.json | 5440 ----------------- .../bedrock/runtime_item_states.1_19_20.json | 4530 -------------- 8 files changed, 8 insertions(+), 10007 deletions(-) delete mode 100644 core/src/main/resources/bedrock/block_palette.1_19_20.nbt delete mode 100644 core/src/main/resources/bedrock/creative_items.1_19_20.json delete mode 100644 core/src/main/resources/bedrock/runtime_item_states.1_19_20.json 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 e85dc689d..56159cfa8 100644 --- a/core/src/main/java/org/geysermc/geyser/network/GameProtocol.java +++ b/core/src/main/java/org/geysermc/geyser/network/GameProtocol.java @@ -60,14 +60,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()); - SUPPORTED_BEDROCK_CODECS.add(Bedrock_v557.V557_CODEC); SUPPORTED_BEDROCK_CODECS.add(DEFAULT_BEDROCK_CODEC); } @@ -85,16 +77,6 @@ public final class GameProtocol { return null; } - /* 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(); - } - /** * Gets the {@link PacketCodec} for Minecraft: Java Edition. * 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 cbab03990..72116f548 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 @@ -29,8 +29,6 @@ import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.node.ArrayNode; import com.google.common.collect.ImmutableMap; import com.nukkitx.nbt.*; -import com.nukkitx.protocol.bedrock.v527.Bedrock_v527; -import com.nukkitx.protocol.bedrock.v544.Bedrock_v544; import com.nukkitx.protocol.bedrock.v560.Bedrock_v560; import it.unimi.dsi.fastutil.ints.IntOpenHashSet; import it.unimi.dsi.fastutil.ints.IntSet; @@ -74,7 +72,6 @@ public final class BlockRegistryPopulator { private static void registerBedrockBlocks() { BiFunction emptyMapper = (bedrockIdentifier, statesBuilder) -> null; ImmutableMap, BiFunction> blockMappers = ImmutableMap., BiFunction>builder() - .put(ObjectIntPair.of("1_19_20", Bedrock_v544.V544_CODEC.getProtocolVersion()), emptyMapper) .put(ObjectIntPair.of("1_19_50", Bedrock_v560.V560_CODEC.getProtocolVersion()), emptyMapper) .build(); 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 4b218aa7d..645cf17ba 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 @@ -77,7 +77,6 @@ public class ItemRegistryPopulator { public static void populate() { Map paletteVersions = new Object2ObjectOpenHashMap<>(); - 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())); GeyserBootstrap bootstrap = GeyserImpl.getInstance().getBootstrap(); 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..44953dfda 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 @@ -43,11 +43,6 @@ public class BedrockRequestAbilityTranslator extends PacketTranslatorX5GfPypkF`Ueo`Ex z6MA5>AAUHlK8&TNvPMf(M7-+U=(*9h0sOha(VJr`#u{G;M_z^8YT+BI;e?ZGj?n+9 zMa9)D3y4N*Cx+aL-YLkN>B9B=SZW#!{MI<5eEM(MPvy_5pKE{N0)M5|`C_H3&s2LL zW6_my`n4m1=p(luox9oHBPho^@CUQlC2yE~$TFz>>mV)bLh`5Ko#wWJq5Ns-?ZS^2 zOP;=Urxo9jZ_NEH{`I|Y&|tf0@RV1itn>8kj^NRSH*vxRQ@$Rwnf%2WwojR|Zp~MR zcR2t0z5H{e^1`*@plc!9*_ie48rNuFOIw(^)Qbfn3XClTzgQ4H?J{@SD~Kd(;U<}7rb6>D09)h-MxhI+H4!@rfqNg z6ff(i&rZWTL2O&+n$3THu@i-Lwuyi9pJ_klbFaB^<;jd%z=x*S3!M$TLyuY>XN!N6 z`$^6Gyz{yO6d){r>3*(Y%MoumWusV`KKrAw0fZeHK(a~Eu_nwx8m=>FZ9+}s9HaCl+Q z318?8MQRU5;`UCmx6kw+^3l#d>JpjM7wd|W$&h?ZmSz3etk3Q`$!zxRui@WsH}%+D z-^7}^F28ai`pjlY`BS9kdM`?Jp8sJ)!yHmgs)+R(hT$X#jb%z-1_=(IC4EB!oIgJ+ zRhjs)O!+0L+-&fgyREjvXQ?vb6S0DHR@9|zLBsw1;q^Ly$&z~|Dl)HvJ`lgK1o3IF zm1vIn)=9iejGS}ix!a3B8v}mj_X&7`$C8gVgsFWQLI;&6UNJnuD=_7r8T6CQ@Y@>l z`T2bKOrxw-WvK(J{PF>OE4_0Iy>sT%lvJHSJJ&;1hI(D0>(SI_-YLH221_9ZHTzE2 z>|5f(-dGqo1a`Bq%(Ea#ELlC6?u>rzK0w}(9=NG8ebaDrWh+Rr?5FTxhtc}v2fMGL znA$-%pU%{d>z{vybvBRq?^M(VcsR|NwfQe93M{P?><>8(wDOb@xh-Cp7C5jBqc`04 zTT=5*UzMa+5cL_GP%*n|nLGn?lU%b3b$7^+EGvHNMgRPEM&5c6t;zOgMD&za(`k>0 z5?8}il{rPH?eQC*Ydpr!iVOzS3@j-KGEzSHu-kDc*6B4}drAMvW_4XAFW}=?fnr#N zt<Nfim*R*`_fKxQW3oUUeWwZJI}Pw z`|2xuKNZ1Gq*uN115457H`zDmnJ@gFQZ>Bua*O%1TvzM-TUWX=B=+OJqV518dWD5g zIp$a1m3M72xbT?re|l+lRkFOg><7KiYV`-{u3EL=m!+9LQ^%Kej{n;J9w=W)8$S9F zaqo4D)d`D_A=cYxMmIbEPOohJZCF{oRv&o(mfJ>ln$;^M+6S%J?11jWU&J$Ascd(f zO>K$}zrD+1%`{}t^z+QIzhmKy9bIpdS7@n6uh=tx){`Qt~S&Zvw$!V8gMC2 zeqMcTwc7XZcuDo2AX4M?ciZ&uq7x-~&7NnzKSg3Q8^w1TIzHHtX%(w^c37M@3w%7u zgSMo%nJUzlisN2Ps}>Jt+5|f>Y1$iv1YtD`O(3hRfLG(!+mSEsC%GVc)1mQe zanX|o;?KnD3Y%5a_qt;=|Bf5QPcE9l5BsP6el|yH4*nzYpkeyyfb!iat~qT6KK#ki z_I=^qIY;=f_GaO6XN*p97Vtf#2 zwp#UB=S?p`y0-JBO#|}&p7YJ`3yshAyDJ=ZQS!5SnLK_j?Lg^--l{-1cj?3r8*@J! z$<7a+{-sh*FXtL7(%ABT(QYfGjX}W*)ymilnw8*_UF_4@&fmL%|Gjy%k*$EIE}J;z zrt~4hJl^zi<%IoR$MY8E$@je8(mbavrYO03sGiB`RH1<0p}$j)F4=#J%{kcTqMPsk z44qo4L`bow4?W$I9lSX|WA(jQ%Awi+sQh$;(#Egu7e`cKY?eForE4>dYc-`$CAN|U zLk()*EHdF~s*aaP*!2?vEldsOtv1^M%-`*BFS}hmmcQeygT6-((cG-oEdzix7 zAN=Sc4mDqDaKGf|+eRwuZ2u91>)QSS0zNKJPZdqWNJ zt@()xb?#rNGsjf^ezklO=bGuE58mjP6!1p-z30d8Yg6IfJVyqVzB|>wtXXQiur-t# zwDK-9$FUzNlPq<2+rHKf;rGlg`<&uX2GY@J)zqjiu(EVHu8dJX_uM;{2{9L>6nN)@JOV#tu2@`q zceFK^qF?5SQ)HJDlu)hhboEc?OpGf?J@K0Lc^4pS;H6QWM4Y{khw+J>!8}T z%RaMRgY~h0XHuo>Zmv8(%V0nqFoSy;XO|l-${&}p&_6QrY);Hq?e|NwV+y_FCGhah zz_->>28}Cu3uAv$tL>l6pRhEgtvx&~DY|iHF1q~A?bXR&kBf(8-kW6~4OsINa}W5E z9j_-!TI$^I5j-^+xgBv#HL3fM^5G=RkG_i%qTn~ocd#)f1X^W!k`Vd>7R^p+a849L z`*oaDO@Pkwl?b|NjObie(i3$5MG2XtoO>9XQ_23-;*4WfI7#35uZ9OO%rAkK3hSC z8rQ6x-jIqrIPYHWU#?^O)%z>Zd&mi9EA*%M*SB*I;*(8_`{(0V#kzwAKB;*wMAOBM zo`I**DgMlDGO?!IWN)1Mjg?WJH%mPm<6^;={?T@|v};~7yLR2YdaF*?pl0LkF_iiA z(&W$98>hE_4cQ2d;vbkfmCJK!uLW5ZU9xVuuxK##q}gTW)+LI{*uV!|zx$Nn>ypGb zUpDXE&=*mb3TV~cnWP-C)&AXEt$2_@O8nUI^(fLitme6hA{o~8sHK>iBOuZ$p z0d?ACO}3mg7}yBG(n1ian^WOZ=1g z;{TNBN2&hW-1+c1cwKS^Q?`J(CI7i-{cicuw)}?=5w>iX&J+avI?|TEI_1HgjXcI< zuTZck!QYl&ardP6O21X1m5q3}-?OiVOFxH#Z0cuj3~qPT+uXKX_c@2=aF`Jgy2`b{5NRf*gFB|6W)456{Uqu zB<0EPt51aftg);+KH!hMh&4?qkzvaf8*$&tLYnXDjFlZYNzz-?60*#j(yL#mCFe zlKT4&Go<5?$W~G|x?_@b;-Pj-xJHCV9x<^^BTwD@=8*fR%aSRDcW>Plt6|CYANh1@ zIbiP1miTXX<3e*oIh(NRDyOO&bItM=@1iwN|1^}azu2+HPdk{I#r>NVb+_bc=JStU zZGViaW(XTu&OLBmKhYksQl%rbkF}F-;x}dpm3z^)56~_#4T;|3)EgC#Ez1VD z=35%@ORRbGva4{iz$@o#QhwPUTrcoyds0KF=9QX|v$N>Hzd{^ebI^ouSi>ee2We?*);T zbVl8wC5Az{F>!T@whJCawP`{Rw_?-TzagO#q9(;ITE%5bkH+)ZMkkbIRBtce+A>5t zG>4h3Z0RRV=p!zwHC%S!xTi>E?fia<*pm-JnU`Ls;vQRrm{i@No0tuk9q>)&I6K&+ zZbO>`(B|A9XtN#Kl-S*D$SYv->M*>aU;4&$UNCQLr@_f8`2AstdVYl+W?_by(J#jja><R zWQu`J|AA6n*0B}1vf9o!u4DcmUi>N;xMDV+lJ52Q6QZ(nI(26|oc-?)UZ32LO6yXH zJ`AqV+lGO+=Zxg0O?L9r2d+C&BNM8@#xawQ&;A-FYSpYh8dF-^*`F5Ru`nq!Z+uZ% zK5S$#dFlOVx-0Hc^8>>>Y@3~OO^>?1{*5!d(^Y%pLfv*=LBrR@_qG`;aAJm_^4qhm ziJVoI!GbEmTN@%W8p_^J9PlWLQ}s?oY-!Hd#R2P#kItSINZE<~tqTLTk5>jeOvwk_ z`ma4c{9fk@*y^>-o!7w-d=_PUm^#t@C92c>*Nt61mcLZ-PRx;CdJhgE5_72jxnRVJ+`O#ZH?zXfb*P? zlF+FC`6Lg?nM6vj>y)b__g7j%2Vidd%u5@(YxUiJZ_dfOcYBn%^K`JX%lFK+pXMyF zioamkb|{EHNpXI2Xf3v^=;YGJ>ltTwh`Hc?uws|d?>x30_-)NEgWHulA%#Fem%)h4 zDPQNhT79T8R@V3nztR0T|F?3lkm(TxX=$Vk?U5p_3L| z##MkZ=wxEi9V}6GOf0`7DGo!AeC3Ou4|;_gXf(0yP)s7X49Fdk=Hg&U+t9r&$NUdyL_7x)34 z`4A?Gx%9km^85*njt|AnDqkcKs>(i3ogzd^#&j2a4!vC%Q*a)m}7nS?b+Al z{;KQu{N_(ycKKM`EcHcF@-n!HLj;PldlB?WMv1e`rMlocPjcs2#Tvla^V_C`CtYj3 z(QTc(sbh2-T)gKu0o@ZXIL&|UGBY@f%HTuN!KDG~$9*6j0SA+7N=&T8`V#vZ7YOxz zO{AlXzdGA)4?uL2I;h7n?|nh~l$4&Y-%eZbpY2P_YcrV^>nfR_`eWximdQJdDWO<@ z-(grS=RFIv$WB(dNO)K5xcW=_Q*&U~=*@t+K3lyHV;?q5%7wP#6l&drVYPy1 zZ6xwd2vfq}-gaAU$y|ETt&-tMZ{jki-z~^gGNh}= zmA>q@>9WoKLye6#Sk`6-PCPNb-VDr-j2^|07k2&ZhQ@X6*-cd zEbM2(wA~mqeA6H`(xltDvW&Lj#$$RuwW}2|Q(=DP(pkTBm*I}}%;!x({$v4LHC5xd z(u|D!*Sv;?xGy*I#0pj@@3;nk(>_32g*(M8X1Z906g2*N$oPWimrm-jniZ2l_L1L{ z^Nbz=aX;zIq@M{=hdTXng}+?}t}m~(zx=k^wQQ5AIXbd2IB8&1VieE&mM`O1s(>;N zqmL!S^3u_*;jO^s`Ef4xV-CR)hxBXzJh0FxM6IOp@+dF=i6#nr!}Qi;_(_iLhVa%;_MV-ob?wN;Sb zR<%?=LRqP64tUdAG64HDkX#wF?1YW^>-OUNCVFu&ESLG+r^kA{T&J=f2VaeMYH%MB z_><`375;gAkQVgv?QHe2GS#cUmCc0F#>L!QT^>ov&0D4uMTRPfNI4Tit9ZA5m)TZq zfOF*n*`9`*T`I|3Lj)DiJv=hT%|LWrjb*ZAXutCPrEH2=wK6}0*N^Pv225#1;M{`}FBM=QjJ^Ti#NY=Qwks(#I8PFxEHMWoq|1Pto%JcEx_!$D3)0 z%DZ(GopMh9IPLOp#pv&ubqkC#ft!76bZzaKKZCz4RVhh~-8EF2iC!qFS?Q8#|C8d* ziM!PJc=Bq=;lU&hv$Tc48@4N<$=l@c-Z^W+w)Uv*k0LWklk5`3F0Qd#t0SEmJC(QR z_16-<+4Aake7&5J6!?Sge4=V*5-sfLWr?%x5Agzrcph1mCp9!iT7MXi_`4bRH-_=T z-(y&%1?6VfugFm5t+(?J_pj#eKcz>(J5(QRrD{Kv41+2Bsae0Iu|mTtItB{|%JOVZ2ehMmJ9SN)|-5G&wmWZY(?0pvbtJRQtPdKIzHRn zy&`J;>B_RnAJlWjug|5nqbbrM?O2*AGRZs6e*_`nD>NIDn;Tw}GqQCOpO2NV-1n8( zfFX^v<1?zriPo^9g-86N7{@y7jk;(D)N}=jYz8G8Kl(why0WY2Ta@OEE<_Vgs zi$lXp$v24bV^JADURjXp1<_Lh5vB$P;uUf9pdCqqz};&=WufOFgcM#a5Kp$oX))Wf<)z0nofg( z;8gB)@sv^|OGtgfnHdo5$J=Tg9LyzNyXH;P266w&h$KYLbg7Ogafp>LGBCtP!JO zln#xhyr2b##<@8}WqzW(3%E&W;Nt$T12^~o9Jn_V&uhn}U1QJFq{#B8Etrma|I%9s^8{Q+Tz?C$sl&GW)NdvZL=2>5FIL6ge+4e? zaR%g;U_I@0fm|YVs}lT&JBFy)Z$-e{uKNk{z~Q7#UOeK2e>)+2@FYr#JMU$_{HLiY z6W+92uNA*-FF44U!r`!of-|VE=_u!7~1K~@fIyS`Ccs6LWb{|n#;gulJUYqtcC5(flY?& z_lg3~nmY1-c4-&iJmVTzXuR+#*5Y%?O3l+lK!2I>>tkx1lkXjEUMZ-wE!)l{bgFNR zJa)tx2J!w34tw|1%<*@+05Tycd2c%@y2p51b{YGOA6*TbEak-@7JnTNE)C z)NJMUPxo7>Q2uqZjn~9>y^(ua*|7m*R9GbYdAZz)!Js3NUui15k2P)evI~R3#;iPD zUclqrF;?Co1Axv#>Nsq+zgikLVJC#>#5TUcv1>L~hEZ1RjUU~zJBNSP_iR-KzoTOi z_*8CJBvf_jy~9*CZ*@k>-523e*2h3&Eevg~x%~~xXV#5}eWCg3U$Z(N<6F#kbAgOr zJXe=Hsw$%#{7X{icmF6>z~k=Ly|y-6X^>==|;$jxo_X3Q_XSzfB)rU3~g0yyoa!u?x#E zP4wn|%jCmSS0%X^fctW-0X}RxS^VNT=S}+Oa%0pdktao^xOHuckNPwo{(Pfihj6yA z^t>doeaJWHS&iW%S%G!G)MJA|V`baSFV|V3mSk2+EA^FNe_t;e@;$fZyLy45FwNT3pJt<}Pa7j;`qSOuq z(cSbh-*`D;8NSd^JMb>BfIHGSEj6W-K|gbNwocqYocz2lsJrm@v)oH=K3G0nLDwXH zSR^m&#N_>AgQ%9HKXt@_efIdH<6VBt*^f-EuRuhXC1bVCZ}=gE2~IwE6l#tMP>^sAuck&X54EZXd?xwPh|cQqBlyYn*6%tJNs-eAx|i|zO7;4JHg zC*Nvrx2<}n&GH?3k#OWkn%`TO?zw%ewddbhYmbagc#mj)d++t_3~$Ou!TN61dAUdR zmObfeJ?`o8Z%%(T5Ofl{l|>ybo_Tv+`fRzfcUrwc_IdvZ>-Kd2Zk1`A*rR<6qd5z2 zvkmZg=BvGI{@pgz4!z}g>lq<$D)ZR0<&iJa@@>jK>Gh6}k|#%@V=!C|*N2iXSv&jt zE}Zur)V^)NQlC(dPxE^f9e-;-Ypq64@|(2jG~O2psa|P~rd-Lx;4sx`O>F)3q1sE< zj<=!1D5vCV!Oi%+ip=$?&N%y^_U)^01Gl~*SNKIr{*4 z`J%A>|JU91A8wbO^{{pl@+Lo4Z!bIYGu>~*rS8D-mmdAz>vj6hiM~t9ziMLi*SgbZ zg#4({&?CdsdbO5^y{CU%Bx>dem9)U-$y`e;i(umUVojkbAfHh^}F~CuPxJ0pnQm$v*ea?jsHy+&gK?7#I-iXAh4Ck4;CJv|A&)RVL+6 zhu=T0I18QmXz(Gv+g|aNL#`rR;8oVXv2u3xT}1xM@6`L|p*mACpmoYOFaGRs6uves$ze2_esJU3dtFp@2O zuBCoG{q9I)?wvO!UPEqj*al1R#;m|iFCy3SS@f5oi+t3Hzu?4s`w6&?lD@LAT|3AO zm1+4mnRHPjXTt>@FJAIQ)7`d}SNFbV zcDA8s&k0xuWe8c9t@Lg!U*0(XKcRbg?#i!;@Qbg9di>zc?|;0#@)?5et#^mz z+PcDyMfElCe|dCG*2#I~(NB`Xjdr}~IuC34D43=NS+}0>G^AwR8iIg39PL$k*A*E* z&2~%9Bs%bO!jk6V6a6#&K|C(Q8M+w9qf7c3JXwmP4qFb*YCCb6qrc-EKK<`@Acx^I zQwxl@J~P&_?v7a(8}|CDv*D1Y=>d#;m@Col{C#D^R={ox zJs&rwma&+Xu_(ImdZ3*eyHUoK7HR7OHp6mgLqehZ?utQ*WO4B9+${@fB{4jYWR_ zS-$--(a7S#wstmrtIhu@pU#oy&ylu)y@f2FYvfQ1>n9hy#}bcdP^Kfdc}VAVrbOf! zsaE%=g^4kJM{{m#q=e(7(OwA)ix*3(*=t+WzG`nJq%L0cc+OtiruNlzE8!FPW$4at z>G>^-EeDZ!nl*>w&SsS9&Yx=bttj;Utjejgh^W=WI`^{zrVrbRAG;mFN6Y&-A<0K^ z_DB9u=DPjUaSyJij+guGT&BCH|>}9*f*_^$NF8#G;e5dh4#U){guY9 zS@ehNm~KE(=}EGLbG$OklrZ52WW3qj9}ECj3_LZ8!Y^uN6bXK9saiRqbzV z$qNjbrkZ%hgskv;ycDRUFKnVeUfr)A{gJd^Bl_cbXg%k%miCIElbya7ZBuPK>B8Fke0wK4=eNHO<|;UTRV%~SN6@S zB`t#>-Nk4-9-ZA9aMi(A(2SS+pqCN4)KU47S<^3 zU?NU51Vb=of*|(LM@UcMq=-ESW2L9eq=`L@RB8GPGaVta^iC0a$>cS4QT-M_(!1Po zEe-zox(8WFsw$Aamxo1FLgYEl#lCOl~6jt zgn$=>fL9lRrkBhLJKJJQX@wiN)V{z_?>OK*4lUK`R& zaTdk8ow|ybaBSgDW#6{EzF8|VfzL0heyuZearr&pij?!Hz=?GFF3amV??`uV#NvhU z9&r!5LwAbL^e4CbgMgac2-pKQ3iend1%iM`1r&&h1?R{Ei6AGHK$r<{=5)Xc!Fvvb za5~_Q!h7g^MQH`{hzyVkv_e@#2JE0HjVgu6VB!{~m5L)W4xvS1L9<+q++bMHG30mZ zC$(E#gt6J zQwPr#&7H=v&zgWcNe?A~9r1EY^wm<`J{V;jr#^?N!AL0aa)8mf0_z4j5GnN}Y)#{1 zElVKnQ7A}hwRWU$xjAWmB+@ao1mrnGD+mhR2g!e47$m=At2&qr$Ev^#!R^hE549}% z2XBEO?+*~fUlligdq6_r*dL-e#*aIdexE48i|va+xN%v9 zxWZv^GQdPk%RMZI&XA|JD2Z=Xb&+}PJgIjWm83DtuDLe8X% zdM_o7mNN+rgdZH;hTTumO(vq-pn_ly*KIqyKU+h{ z&R`UAA#~FvE1<-}4ARJFHL?x*%t=XkvsS$eSiH6lJp##(hr!dCHKPdz^u^jJr*Nwr zGFKQGbEY$IHdZ(vmvaDtWamJ@3nK{K2ZMbHD9HX)7@ca< za76F=Z#yrI;V&`z2OS>BC~&<3LHpiczwMwYaV92j9GVh2{Sr#hlqmFs8GtE4Fjb(> z15=_EuHOPpiL0Vurh_SA-@={7?r#0DSypN?AV0Ph&&t<_$^G&oJYHUqPUuI~93MxP zgu$B-W;SmmgOqdUyEEe=*=mAx+CS*$`4Y0IM&K*!Xe0y9zBm8+3vGpmNCv1fL`}Ur zbM>GGr9y%fzaI<*(U{qr_33yi#vE?^CWJ+bNGps*GT2|u#~DNpoFN^!L|K)j#P2_z zgMe?>h||fz(>dkRVTK2ZM$k*3f|np}(nl5UFPN22Aei9gSo2NrXy+*e@<@a~JROE^ zA#~H1+oAHpZ7RrT4`iEb=oZcx9*9y3R}vr{`)f;zib3nv}Q+iPh=e2zU%S|`|u|SMA+AWJ$UQD9%7nXAfRVo z3j+N7;2b@S_oW@Ks1OaALZltANTMNKn6v{z*$$b^Yo(+ZstnJi4!G^#x! zlbO$&R(chY$&R)r1o?0`%A%Yi(8;Z9>L>O4{Dj}-&T92WWAUe)2PBXzNvquHEdGID z;Hc^-v2cSd=p`BVQAvaOvu+77IJ~A24&k`LoqqTrk$_W2&Ft5t*b$BTl0g>q5%OoP zu2OL|a$qj;A>c&`;Pu3#Bc&*0F3pc$Vgj!ug@W|gZ1)dnaUp^9&kZdB>Ce~-f+F|9 zM7b=y1cI&VApMV5f%LcH0CR#%nSaam$=dapaq{b%v_d!=A!MB^{v;YTW3c^pDQ7L6jk7m z$NUR$Dl8Uc9Fip^1&3s-OT;1Bkpvu)aWf2ucpz<5r@ImUQS%f64? zU)wH@qPz>u(mKzhLs@IFjb4>_fOZ0S={6!BJp&5ll!*Yb`(PrcgDD@;fIf`V!Gwos zaFD?1aD|&_z)0a7+{k!bE}r*VvHBK|4Lkm&EAzejS+bsOzSuyda+PU0_e)6 z00B{mcQ)Ct+~imgJB=O2QQAD>}-)jKKz-SF(=Gjzms}7kTlf(iecvz%vUv zgNyJY2!`f^c0tznLP@y?QcJI`#H8I>K9 z$N&iSldr3X$MX>l!!rbkJ>j`WiG~qTLPW!`=fXrolr{?Bsldx1oArSs&tz?k$c$z! zz^UcdraqCakA+dy)i_9oG+yzB?HGEG2-6bkf5r)e(j*wX!6`7XB=F{yYlY#0fX1$m57U~OQ>Qf5NQ_~q>ANB~jJ^+T8tU*?@*#m?m4^kb#)DIwd&;-CHW|05A zxTmuKAw|>xgv3Gv2uVZ-9GXn}27)iaB#}QRT*qB?hoBD%K_nvG?p+8;w*{6rtW`Ed z@pQntAxL-$n?CMTPzMO6a&3+Ww4d1V=#_8uHlNGQOyxAVN81HKKRn_N_k_I%sK~sx z;hZ*@2M~nPn0e1PcXakMz(diynCiq!um`ob5$wVK5$qu*(g^~!?RNlfLdqtIzdS&_ zqxM_USYgU|1wzf*ZN@7qo@V4CboOMjI)>Z;%bujF8Ellsp%!w;f!v!TP!=G@|+E#~%Q>~(E%0zpj6Fou?=p&Luc)4;|NW;D#= z31XU;9uUMdY!e81B84JwLmh_8hzu5MeSo9fjS(5qQlO6U^E-g?2lL?#C-n|AA2LO# zccA%@CkId!m=89Qm=$O~I8rbx(0nj$!>mB_A!-V<5@0lsDeiIMzk&C^21-9sE^;PR zCk1ebT>ZXi5?9`=UT;1Y&m%r1f#m(M35q`^lwiP+u8DFAGa{3*8imGvAB1_Svk*vl zy9xjopbyj#0JkF0Z3xgp0DU0Cjr$<_;OT(FL-gUwanRw95@F_OlvqU78?!HxR`#4>~^$Tgxie-QtZxrLhx@pT_S$VL`(mz0>U)*m}7o?MuhtGqrBmn{785J1-Tb1XFu}V|6O+9-H^^XDuEz@R*hH2{NDX2$?fVoC=<=@BVdChiWR z0tzIsLQqO`7fLyQ27zcP2sxej0?q+Z2-cXqRfs~kq4QQD3K0v-TZJfuIZ=NKq7Z&? z{V9k-oG0o{K@?&Gt~Uiyh`ye9GH>o|tdRuvj?n!a&?xLba;GyPZvh4nheSDrVlv2H z8l{GP{Kw(>v-Z6bIPj80!Aq(V0AqXmApzQI5b)K8wiFsfgn4FHNjkk9+DG8hz2dt(_e|idO{VUszO2uZdAuz z;tbGhgfzWmav1dR$60d617;WcgFX+t21nf`HN3@SJHc~T`TMZYba*TeHE9LVq)U{D zJrVIpVoz9;3b6-e8i>ez$a=s;jlT|NqQ>=+(?kt!E?*F!AVBqYkzm!Ot}5$OcO zgAa`&#lrNct!FjFOeUzLzJl-aMg)^PauTgom}zNy3js9z!(6HC^^EH#sp{tP*&oGp2LSO{4AgmtOoSAK=8;95ZtYft%$wqSoE(t7VK3=B5tob(gPX+pL;>6_o048JlzS|6yh}(@1x?Oy+v+>!Y}FvqZ}P@HiC?Z zee#lp2NskP0G0A1UU?G=iUW<>jyz+c#-zasFrBq#Ul((VHF?jrDd4;n)zTr`r^sr+eCsn9 z_@uy>_Fi#>TkRFcsei?>{9kc&-7Ahf7JdAE%^SIy z*>c?lH2<}@0PP>5B4yO z`Udt$?rj1AxH)u)_UqJ}Um z7$D_sfL@&^fq_mC4kb{9FNv)?-Z-ytX8@4hu@^!5KCv$lM)F2fq5 zbPSO#3nZkfq&cPUq!apYLKs!ukqlbS1Rxq|UQCbQ&+>5<&sx)-ZwT@|Fcb(y&WHi< zl^HpHKb&8Fp0DhoehyHJ#1X`68$hwrFrvD_fcykNqytVUDJ{DjIb`niism%8Ss;Yvs}q#R2$=Etu8gK%3wZ_p1;{wj4Wn zIxC$knEV#Cgl>i`8&qBxrVMoPqAPuKq$fqQu`irWd;1YcQH~UNK@GtGlt+3=fUowG z%qL()v-Tk_rVX5u$Rolj@ZxqLP$f9FNmY{1YJOsi4J=8D26#w&7IXwZ;RS}*)1hSm z4>8d|@KipKpv*}CBu%&O!mC#RB-tAQBz0EfBY;=*-T+=5VE{WZYZy~WktUwn95@8d z9G=F=M1+~*04r@qM@mw|yZ~@qffbBHGF#Pcz(yc znh%b$TnMk!X%jJLn!?Nl%u+GHkch_2NjbY>=HzG5m^qbMaSUMCESNbx`@!t~jdtNn z6l_+ZHf4=;TARZ45EX--5y^%mqbSa)E5gKX#$Zueo(duzXz?_v5+a@P8#>9%1t?WI z=p^%NI3%12on&H;Ljo~A>6$SPDVT)DE!?$eBB$99!niaks6H2A&g^md1gf#8(Ck-n zrNi?6+-V2V8ZZO;MiF3F;1a{UH1dGWAj}QCwcY}Zr(C-OaMys$P^ksRGejDNNDPD0 zpq!R74#^D8kr+=%X0W0HV~NQcW-3JwWr5~n-IfhtGYtMtdt9oi*bB@zM)MhI1r{6- zkfeIOziGZx9|l1>W1uQA0`sm{k8f9xdJd!~*sfC{4jj2?;cq~X3TpzvL#(0%bK$mW z;Kra359})Uk+jMQ*~5^);7l=gJC%?mZgq`26__zFQ=Ya{2T;+PJgD-yUQ_b+jb_e6 zJ~VOE6c?F-dK1DRcm~)Js^<`yjQZz+qQs_($UNw-fygAp5^!qAHxqGcyg&|AJ6uP` zsd4e+r~z^n9Y8V)0aRHOvjM5_3MkA5B)DliU^bvpD1uQ|A>FY7O<9Fx_%s4=^nl{_ zhelc55FVtATocsoh#u@D05SilS+Uh(Cdx6fg@MxeWSZ;I~zh{D5vGfWHYGx$tfvB%KGU zlr|7up}?gmum?74*R!hv0RyeaAmH_I1)KvirlRP9WbxgM>_*F;ghac^N%g9QFhB-dHVzMxE+ zYq0)ZQrKJLBqt&naO-zu;6!j{PrCHt!1(nNvq1~!%bvX4%bPPvZ7B+O`< zJpwq&GKt_M5`%ynYHOP#G61Wjm1_ZSZ=W?SPX{=A2Z7a~2T;{|U6NTAP6g1)By%cG z1>{4LNjXl1OSUfQS}jiHSW+Es#>=8xAl>FEDE$<@2|wY)+*y?b7c5?6_LT&ZOMad^ zogVE68fzmc{t-A5`H@B*@E3%W@@H|qt~k6xI|_jWAOT((0HmrgGzvi?ukFjil%qil z0c_G4g~LOr*@_=DdREXnbx?W0iPC(G>0>^=v?BNiF74Ffv8y3f2-$iHy2=fro4p$)S4xok91M_^} z!BFrMAp>s*K{Pq+%~}JPbps6?1d78HPDhXqGAWxe^L!kd1(?1EF>=D#K8fS+sNc1W zm`%V-NCN6a1P0Jgkz#@Y9l+vZFGDN%JpOvV!cjKgaw`CjeQHgp?`i zu&W`}0TwL;sCBuY@FMWp!YTk}VlDufiN_f@bhWa5z>H%8ELwp-31B9@1aK%;V-y6V z04aBa3@PVp@H9Z7U?!&YkRcTYH)(+ksR?w!IAlmQgdvn5L#j9pbh>yT$mE7`k%MLQ zFI+$!l_T!@ZL_1gCo&m|0qS{2@&me(5FT>tBDw)Cy4>3ga1nghzSD^Udw6Bt1$&58 zL0+8JV-R@sa0LLW-44-)-Rlq(>|Te6#O`$nTJBzlAmr|K2z2gVhk)g-Zjc5jvrd=# zSbzY&7cJ8AbA2vGnv>(JxykUMG04D4*+P8Qz>v*|GakJ)6%oos`$4v*p9${h-JZ{^O!zm+>~VBDdVJ1KiBchI?eD|f;Gw%Dwz`mFf;W9Ec(d7E*T z6Oq!t(Tk-_n)h;2fFCiC)9=zt%<1>*HO>Jof;R`O+$gw2S$n7vkIvIHi$l+7SjM8o zL}GY|-6*j1CLQ1eTDdwRoqY$LWatN+y?y8;>@DEyf%QA3{rPwZC{(uQ;}IN5;)NCY zco8GuT#)neNO59*_php0)cOBv;fC^WFN@c((cNVk_UZM*QbqtKRjLmGpN($UeA5Cj z>1YC2&2;t}2_Pc&kHDcrFaen4M*_CpsjwOlWZ48k$c=Ff1&d;K(A-oNLKj%M(uRn) zTYF(0O(trWLuQO;q_G<`1Xs+tQ^CR&m^pV_?nTS!_0ny$(y#;oord=x^9a%j;9unj z2>ckx4-k@{$`25c|B)ZSSw&9`K*%8g6q?lq)*8UG8(OmjV6NF4QC2(JPw7tW5{Jh+ zd&GhJ@*Z&@Uj9cMlKv3~;y>c>_#bh20*C|dNm%BZz4}vtgmU$O)RveFL4 zMtcQ;kiSct=wjzjq`p8(b@0hsy)z*MjxL;f(3V0%wO zhzfgc7f?+CSR@V9Rsd6%Rw5n4>Hwf(1xq=4p+e9*DWL#4)CG8gK5(e%CDZI6+SE$a zFNcug9Te6La;Pmjz>+dPpwP;;O<(-`{%Md0T1gPG0PxU8Z~ZxK;7$Xl%}HYo03Z3; zG2q29rb9@G6b!)AsR}6YfEDbKr}+!)q2dg=b@!z}U`FH%ILF)zBQh&X&Lp&$?L*R~ zHV8HI!!NZ#nNWd0d7-;Y_Q)W~4*@4TD_M4zTDSui0gnLFL&XS84=#-nKrQG4K~Rtn&0VQZ=mN}% zfD>wS1SY*5{8BlD75mWH!+OFJ#xO?_4Nq)( z?KSq&vwcMq@DyX~NS{1-RmJkqk!yAZl>>ySe4^Bt-D+lTeaOvG^UvF3)eV09N|~P$ z_nmBx*}T~S{)?-jk>~LI4e@!jGW-4K8GrLjk3N%Z`-GE#Wh*J3v(^84gxcY4yf}!c zQuwzTSSQW{7LGnQA@6pWs|@;s3`18hIo4_COXtnmT`Q}2z<*?<-f0j1itl<=w$$l6 zw7hoD?4s&Pf1RU(tLDk~*VG>LOz1&>$9K+L$0q3h=%L2MX%(4YvVK~$uhae< zD2%k3r?j2J*FuPWJ5McJTbExwdwbXK0I!*=a*oBE2xzX3xxjq)5Db1LWoG`;&hxjI zt;+t(W7X&F)BsQ^g2W@$;;cyXo_e)(h$Ltk%o% z(~gmQ$~0De%jSgw~zrgV;EfH#dvFM^!wkQg{0LP<%iBCF{y% z;)mNlV#g5sO-fXzt+soT*FDA-T=9Ov$qlKof4ihpzMm0Dn6jtQBKF4(qAfFV4)skTlw{qac?+wDkHlWTGrOHuVWWmv-bxIxou zxM?7bN)0tp8E`p_k4(-|crElkx2j*t{^g;G-K0{>x0V2&49g z@s9i6P7)8R=Due0Jk&mqguFY?n}G^?r5aT%_8rGM=d`ZS@b zPzsQ#@~@pkpJp@_T9FqD$n`ZIN=XXpwz-@$ais9apV? z0+m8cxs)=cRq!TsJe*bqTt%8scFP*it8&tmI3&U3pw4HaC z2B^-MaiZ3vZeCQXVt)sC0OVxl3tIuLzr9`C4(5q8)4VBt-C3~(YD+?=qSS8&cf%IO z1@G1UJ080=)tqM(HRD8F*h9XIHFPiSfGu8k`w1x4 z9P>^%Z8Fq#OuR7Od`=bV|2lT~Ej)m_BO>2jH{OF|643{k+gm)kPns&yB*T7Wir>G^r1GNPFe6u<{xyXY>c}A;1BJIe??p#z8 z*C5UoX_TrUZ6F}Ff`J|)Jgqox)iI8hXI1YEt{yf%f4H_}TIgsXL$X}68KZWv7cogg zI6{e#*HGg%u0dcsw0LuU=9O^jsT4o0rH*f%DuVdhc@XOatrc5gTuVKySld$CX*cdK z{!QSLsbiIBg6pKYru}Ld1?Mgkx6d0mZSe^>ZIK!VoVK8w0Zv=QC;Qk@>&9($W9G`} zCHlw(!`ViPOliZ*kZ_K2lkx|$2<4&DKo))b0aV!P1wgsECYfa_&ROmOf^|sa^;G47 zx4^NJ+dDd<_S+mTRHZ*`CVe^}qRl$@cl=Ku_Zg_V5n3M76y1;JSK3)8^^W*LoBX0T zC=2sW+1X!PB-iF!Cq^Kam!e$SZuatX3P*+~(%)V$E?ThTT?*8J!*zMv{SJ1oKWVPp zl6>c1JT8e)-ptRpXdp%3m>zsK&8n2)L~7~?w3>!?aN>xxm2k#px42Z$#^d|do=(FN zxRO6q3ripL4ZRugMXmoDm5Md2>-EPD=gkKQ!3oion=LCl{GoIq? zB6im)J$iBP+wgp0K7M-fzoEnFe}(M7%e%uzFzZbOygr|czVR$t=zWuK$}<8?3CL6l z`6So*4H6e`HX4{C(*_sYE)O1+JSr&i;N0ApaypIMdot7cl`C5Q=#}a>yMyU(MQNA_ z&=B3odA&*v}y=(YAW+}Qy5+n{pMJ>Drrz^<@NIx zuk(A#)h7hGUjA$<%Ss8+wh<-GUYTRu1UmT`1%<-cSeM=z7|4r)vPRRohTj^g8Q)M>I)i9t9>zCIp3lJ&l z@g2Zy`Y!_0sz-Jkrhh8`gGssy8Ba(`dqRG)+e~}VY&dnDxLI(sy*?KY$^Ido4{BEC~* zio%Ki`g-{Y1r{SoPLEJ># z)F^4jkDMfVPA&nu5;p^Y9pNDrb*%Jqi5djd0s%V#ptC37sc|JM>wDmgmC_bO7$_tS z%or%H>AfsQxE&v6I6cJ!yj^KvDJn^fIpSsXDO33h;bEkAXY~&`y?dLIlI~^od3zHm zF&kLSY#x6y$xkJP1-t%oC+O^qsc_3WpXK?xvx|Ob zJO6DT?5_1C)DX^%LezajKG>&8ptfG>t?|xtyS||PVfuK?GxaAmYI6l56a?>`^ZPEX zgLX-3PQz;Y2(%2kcu3y6An!jcy3W*gI3$gObc8@4$vcEy*9HiX0)ex=(A};{_Yy6= z@lPvImU`pNn4i}6VFdO+74RF1Yv+r){|Kmf4cRZnawNb zwiUHq1|`rAYlGjIbDaq0>FI*gV`GJ;{cc|i_H{Nc28-A)QEs5N!SJLy~IkqhWI zt20rRjHeJ+a-VPy)WTCQ;tY#=$ZWU!CG5b%{D^QobxC{9QbW82 zYLFnG5R2QI3gu02QyS#n^>av#(~-X0ea?C>)$Sk*M!Zp9^e&caN$wph$4(WBukp5| zTd(Z|SIc8xjck8Ioo7x$gCIlcOdMORcG~=E)~Dm^eRzGPeX>Bnb%UeAtvYH1+xE$V+QpNn2*bgd^7CN*w>cB6$UcF!>~n_Xpxxg7 zfAfT)OPlTzwOQ}Id|-Z9o$u2mpu%~{*x6#2-y;-EmtZI!+8Dqqgizl_o*Oc_eCx+6 z++(jppLw;Fm3y>5CT%Gc5$+6k*GEyr1*kJh95PB?RzJsi1N;#b`7&QCRY5EkLXY-G zBy93@%tTm;o2@=avjFkNoUn*Yx5>1kh4tK^DBw-Xdo&9OeRVh?YiSG??kP{xM^PpT zTCA!#`4%3@cQOxDL5lIm0&#W7L<7w(8Doa2Ps3vX%}oOo4Rgx`aR7HlxlKs%IAu|2 zX7W{RMs-w_$6E6+WiikXUCpIFc_A2wV^TJCe0*@Wn^;w`JOZr-P=(0r<5^jUV*hN> zk{XD`5p-K1NGhI!ETAO*2waXJO$Ak(;eeD&9iIT2?KVzb4aAr+UL8^z%XhM<=l~-q zga@?SPzZE8+;YYF3{)eKX4Gi zZ@?5dGz$p8I2@5QBm#480Xz}%xQ7cx1t)TFV_+?3|IF(TlX-8b5@FyD;tqbP`&Odc z#btmPNPwB6EC;$_YPj^5f#igY4a7jwVh_-z0OL6T+zyC=1h@!#z)r3D9k=}SR-4d-a za%KI&S+Ve{Nj>dtnQ>BB`m`u1GtRxDG{s9i41Yoo)0opPy^Zm{k~W+@<{=9;;N~H# zD`2EiDFX*lY%f3ubACSn0*7*2$KB&w;wVBNbLj*E9H&~pe(i&G2KM%SyU!g$vvnvV z*XYG2EhPYs0_B0)0_#9XTOSih5Bjy;Jy4zBsCxq9tbsHW1GtLz&P&fHoMGz|4K%?e zOG3h&MpResyY};z*X`2=jx*ta+K}JPCKPk`AlyWrH+ri@bZ1J7zbgH>HQ}yNG~W|eJZZcMwR}5c^No`M2qFo!0h6x> z@{BIQ^va@_(SNnEMAiwIn7$ikv2rqb$8BD(bZN3XDf!BHTo9G<{J0PNyh&N^D2RmB z#`(dRteobAHCU~`QNh%`Gl87KeaS4U%Jsxa<-SbzcI(lhQD6RNnK~h`lzI6WWk08I zRsRIecEIy@rfs>%kFBwr@1NQl4^Cmxo1S?%8}g2=IjW=>b3=gmk^cv588{>=vYy+X+P`zsRbV%Sl$F3En`Tj#AcYRq2MxNq9 zQd0s(u8+s?D=(G>qB8bzuP`?E62u}uh79}~9HLVAYN{@@(e!+O&Ut{_P!P#jUL0!% zZxRv~0O>HD-K=@svUf0fB{4;mnvIg0eHYY1E2{#L4#zirh+OMz`TgiTyHPGj{T(%Ux4WJ85#&&JQ<5eM#6_|6{FCJFMBN+LK<|y>}A@yT9>WD zBpbKvW1qEMX?Q*zdSVc-HWUb8K-j9YiSMsJELE)hNvPno$&B(*3>BlYkn)u_W;8!+ zoyk^Be=vLVDKf4uI$4Nmih34hX-H&c$YEwxSjU_9 z^pWg8k3{=gd}QD1G{TalH`4UHqjr`;^}NS~V9W$)hYSd0%><@hPj{9|u~6_2+Z@zF zbMKa|X`ATRBBpTte)v$7p6ma1U+T7X2$s^>ZWqEsK!8O} zwj{ym$4T8H-aLx(dAq_?$3xdvF1OoRHIo|CCmA@32mJy|T~S@i;N-dS1=HpiTHpH+ z3WD-ZqLmdNc&nKVFV zPS-LUj7E2bCb;|blGWgiBYBGt?RTUNm*l54!*TaT6;r$w*z_)Du^yYZDO^{bB@OW8 zu%}`Pg{_sSeQW2$T&^4XZ%;uPZzDh1i1JT-y);dcb>FfTXrn@ICdnS=h{+q;)Hucq z*6ucnG14!7d14y9O_oy?v67dgAJ3$(FkizddY%S-Fvn`rAsh23NMJMnbEsY?u+*w2=tUQy^*|l<=S@UkbMj?x{}l@;f-hVq{v+ zBU8VvS*pTCoq%gQ)A4y?qZQa;s747f9wj4VrQeGK58QrYie#F1lY0$y<+`8t*+084 zmQxetSm^?ok!!{Q<8FdHka?m;N8?|*&#r{&GxrdAQws@DCXWZOm4#}r-z^_9qwy8Z zk;iAda14!cKvYD!f}#5f8qpoEYKWz7F#haT|1XA9J<3AIJC^2 zRIRqn5tP#CtU>QJE<-?Mo-R@SSJ-yOR)5AMpY%x8PfnhTl>nv@-NWyxp#_7ef)4vN z<5kH9k@gY`^uRYhgZXHBX*1e_eB*2>D=>PnwyIuc+7@Jy6r<+g)qYK7KNZGLXZOy% z%(DRY`w{88yTF+N$+pcBu9n~ZoKzfShVIu=5dNE27Mnz?-6*Tw2!U-9Yh79=;yr+8 zd=FN|1h-*dAG2bA{?4PD!mW$q`Pbr#+8z9F$lmq6=85+m_&3tQ3m4txmiPIZPU}w| z*2^X7F>Ou^!1{K|1X$_n$QRS2LYyw2$v`t+?;RQYUQXdOZFX_Bz5RBvIGP#cGdaJ( zP-kk7J9q3kKELgHYjHHmR5g8dvzCr?B)!!jaW=fVY%;&8zY(ZWw>i_h7B6#YQpY@P z$Q1P3u=@RuTJgi=l_pD(`Gcj|JJPqS@@-4wcP_7NfgOAAWGKjYJ5xeTcIQqpcOBDO z&Q_>AdUusoc=y^GUq~D=`Nzu5?=Azd@0V64TszOa>Zhx#(P9lyf^RabL=+y!bSK!i zFMTp1T_!)Z@zdb>&l~gxqN=RGP7?)4RiK3es3EE z<{o$46|BySEd0P4vz8Pt+9n)WIPRkZuN1~X&U%z-dfjdz!&(ls^= z9OLabB{*%Nl?$WP2kWNG@Aq{ASbJaR|tn4o#x zY$t#tnm(mOh#HpHX$0(ziN=R>%z!r99w$&*K4f;|X!b!vUwRz3v_@Q@E=)hsPY#&kZs&`N5 z;CAqEf2gvh;`*w04k#En+z%>=nmi3nNh7k@kW>rvwU7iVSKY5#GXV9ifcjIm1E)`a zLEfqpM8i=h%4ot2#SgG~MiwroU21dZE)y?*cs1!+D)VUUCG0C=IM^V$E6is;Ni^-I z5~bP9&-tE?&kJVfEu!s$E zc%28tD5nD~CGf@cTlcmfPK^WuIgfs7~%QjG-j=H6TUU z)7UVH|Ja?KXZFh8(xZ0EGSZ@Us~VzNw&}OR#s6Vc>0i-9v#il?rHKDi4`d9`EL&hI zj(575Z+mJ_VIw@po-EvVB#%f|f>+WM*SEyq@0x>&FTN*Te4ktkPOlGJc)YeYUrGl< zuMJzM1#2{oJQ)iwbu`!{!w{?*wk5X6ne-!!NwsAlPB860J|X`CMx*ciP6XH>AwH5YbJ8?%%7&-;3P>>B+ZW!Laj$v?uTTY&;mTg!uFMuu1=0srr9SoCu1;$8XiJ$*gh6B5^3r@- zvE}uF3*fHeV1i-a^VdPG-}i^WfcME}U2WlXfnH_T2ee147JJk|_2kj^ zh0wEc@l$kX?wH_1f#+r+*NPe%G1ByY`~>I)o?0n_!&0;4#ZtuE9Zd#~5g9`ha*Aid zo;*m!$>*km<6_^i_5Mpk!Lo;a!UMHKoR{Rox<~kE9r_$|@ZK~bmCJdD=e_3n>b*Jg z1{t@~*^%cN?ft~DhSOC`hUc3XM1(5M0igbJIzuT^W>EEw!lR(7K7rL^ek`EF=2cd{ zn+op)jvEcnl~<}<7i642@D4uC`Ly9Krot+B*n(s}o;y~xmOk9SVcn)HnsGd-`Yv_S zSB|3?3jB(jVuUl}>{kYOX0@(p#+$RnKalCRlD%9`U2L~`JiHnY_H(nHe|bCe)iiLv zI!FmY7j!%LvA^%(`b}Ge8lend?-Ms!L8!A)T3DcTR!u&{)g+@%8UsgSfI>fO9*%0= zlFF>Rq1ZMWn}_XT#k%c!=XK;T-H3fTz-R1os(9`>jCeKT&$z?r=%MtNH@e;t3I-BJ z?G!I*-VP0|9(&f{w5PlT_;lo;bAu=QCstz%=yQ|v@2&2JapN>l5C(BHi`+HCs8$=w z`r~V0H6qZd&8F45JascOts4(N&b2c_vk;}o0T!+=H|2imF4j;cbRIcN?Qf4imnh)YSqrUqWZNH}MW@gC{CO{t zlGjj<&ZnKnc0w4wif8YZy%f*hf9)|`yQ%wNNp`F3=y}>nrkmknhST^X?=EFxT_o?} zm;<5F2r8_oMxPe_o>~=F?|07$d88LYO_dh@9;;iYDR zg6rJ%R1#vPnA1eOVFbNM#r{CtG3)tHevHS}O3W+aO?veYX+?ll-(UmH^qc&CEbH!2 zxB9vEm7{sZF7X@=O#<1@f@aIPxjRXO#Eb=YqWvDJxKQsn(PN| z9E(Y-3R|chsMQo&t^*7>^fUX>3i8F;2luP<4R#Dj7xyU2gk5B<*Y1R;T!k5_{?Pa!Qp<%5Io9S5QM zgyG07J>D`43gbNAR&R&bH1CZ~$bH5+vHuF*lsxC4&K0c%Xr%TMJw$+niIh_Jm#OCX4_Z9x9Hm!A4`|I7;nT?WR43SSXFP2M=Kro%MoLrJ>vC{752Xvyi?d80vRtR7_<3_ z@X_w%aP#?z(9v#E-4u(D*SDaLLpmIEJ^tLHsM$VkI*RER`;b}_mCf;1%#*_}ejwzX zG%>)0=0?M${di?9)vgeYORPyg^f|U7UJq(GK@?b99REP`z66i?#QhK~?4Rj2M8GT6 zZUBPOgJ8)Zm^iI3R{s}x+VIL5=5ESuC`b%J=3eIRto|W&BZmKQ9_y zd>=dpF!~_V2Qoim0pkZUyLJS%t#dc=4bAJ93Ja?1M=m3(>XyzW#}tpgd)0F=N0RlAN{$duGA!Ap+IwCvlE0IV=jbsDHz3sePK`X5;m zpmZ20t!`e=2f+W%*3PeJ7nRNC`Lr)%lUz_xv0wlH^_He@pI|4U#Q&T|*h*Jbo5zz2a7U*=yDh!LX4{J66cwE8(7D zf~uqpZd2(a=ze6W*jh+V=MEGnyHiieEw*h7<@Oz-PNX~R+Cma0JXO?@+$Vp|!k+{P zRBMG1L;#*+GZz>H)&~Bh>&^?(#N_ZN%neB}DS$zh;OEm8dCteW2Wt0p z99Hd3t_+l~3}i6=VmgdBU+i2crVg=}C0z;~q68kI6oXuqPp(sz955`H>g8r#HOlbt@8LHkRI))&)hy!lu{g`?rpLt@IiX4+$xhKbYG-&X-0 z9C^QP$=fgEvsKDID49{tzHDvn8pYHV?pY3EtzV&}sg6Cq=GtN{O-s6Llds>_--rB1 zO(^HB>8^TJj&n_|k!7xs&@%x?!pEaJcwdk1!Sk}mcwF3d>V)gZE`e%;$7OIqpxyDJ zw8odXNUOC+<3A5e6Ob&>3q>iV*(7*t%{MXf>^}%p3oI_DNqu4_y@JM=Qzc&XkR;Q{ z3GgxrvGkFo!}tIX;zciCfgo3HApA18Ff{jgSyqDvDarwhZCJEHNT|gER0U0Vp(>{o zj}&hgXg0xSYVr4pR2Oq9<(eLnc-f0M*e3k}QHj%$nH3CF0Si;oArjcod|5{87SIn? zhge0v9|USZhh@m#e=V0~Qs$TCYqmcIDu@WxORVkW$rG#OvC3vNDADW#!Rc!eu#dC3 zz~w}yDZxKW`FPOEl6{pa*A;Z;mlK$_KP9P13>In$$J)(PCCF880EVXk==P$t!WX0{ zo3+Ow(Wc02Q5J+kRY7I`*g|cX@3{gT`be_L=_G*mfn98p!)w!TSaGn zd4WryQ7R;Sya%AO@_c@|8Vl*w!9}6HpwVUZ&rTb5{+aeTM4C?jBKHoA;gGl_FuGO_ z=wxQb=xm5@>_3wbW%svHylGHRjZvVD7s@h9w@C00|2E2>`*#E=2_T2-lYUdEg*FY5 zCey>oKld0iFOgPh%|IQz z=`vL=R{q2wCa-&CXZ z=pHVa1!J-Gw=qBNyk#+0=OJlNvpG#ADk|3^TKjz@**ufU3*L^@-vQh?$N<~LtB@0E{T9ob@k6uHgAt9-{!(b_AZ-F%Utk@ zpZaX*HC00$aT|0&!n$(gkk$dMe+7buI};&55<%4Psg6jx;FS-kfF#g>5?E>IA88UT z&wasJZ8Bbs5-I; zC`zd~98rie@XdS~#%}~vpitF|Z>3@{lCKkV-GZ9=rMpch)ze)-VofK2QWPN8RE}lv zSFEY3gFFr-)-9Z(17H@bUj)A%7YzK zikkCHOZ5L}1?00(HM2cvQ{Q3OC+hkds|;HKT3njxf3?d_0o^V@yKEe!+XZNs0ge9E zE?d+L2Wgk7vyIY#w99lol}UdDThLK%+8&TK-<3$IW9HX54>^i&2TD%CFakdYz^7y}CN4m2?< z;Pz;m>4~s%J}#s-up>pK)&c_)KNT072#hvuySEHzw2yFEik6BRFm-{+Zn00$wFA%7 zFIcaZ)9btW1#!QQ@ohfA2KbGf`4?B~FreUZ(o#Tgfa*eX^AhPm<-S zinFcSUXtZYq3A61*NBh1LsKTj60)rjMS~fXzK6x7b~cU%g^i?6{*x>=j`oF&j1BzS zGbzh0lNR-hM5@i1xF2STa4&gmScJ73#H^gi;OIY8lpmlK4~TOeo%V`2Fzj$cnNb(F za&5pZ_{ykr$VVKM|63>*G3$3>4q%`_#wZ&wtMz~(cj!-)a;-eu2w^tpp98VnjB z!qUPYgvdeWW<+g;s_sG+h=lW}^){oF?1dyoQ#eHBzA z$!S=S^&zP=X@NJI*58U+W?sL!I(V-T-^LwJF{AFJ_5sHu^?})aOmj(n?E9ygcHN)h z=#%1bgR&o*3)50_Anas6Tz+|HU^@LmSA0sNMw6`ngvzw`SSQJ$<{D(IPItthN>=0- z&7w!&uV8V6D#K8scWM7&R?8UK2MdY;^+bKLlSP>mqVxJc|1u+zijvfej(yf?Ns zUrs52_lr}W69$Y3t|=@2ZDF%KxX#|jBmzl%cAD}eCH_5hieVJ4Hny#s=I$|WaeIsu zeh1jX9kPQs3a+N;YB%f-H?h4ch-XWL$>y$>3fn_68tmmm^u7qDECk^efh z9`z~NGK-8q-9U!X-tu8ohJM5CWHE%YQETd=>fDgk-+0{;=0~5jK=bS2m}l3_n+%w9 zcxC4MuGe^&!I_Ed_g#l&5 zviZ!SPY~ztaG37zmATxn?ox=<%lP(w(FBcx@qS7AAn=_Q@)NrrJgOxI9TC{9OqYY9 zBfg;GvVr5k%b~;*r0m)H2kR<6D6CL%TsvwVzsWA<{=5P8pI>-TcJ3NQNq7!2HH1ODZH`wvjNIhd}c%Q|`;r}qP zwDfJT1f8l9x2WQ=QAJ^GSxx~N0G7SNgyO1!u~HSQGDJ@i@7A5)aAtA-wjTZ2GQv-x zGd3tbJx1g_USvWvPw+mC`3Ub>LM-BfjfSK9{QZingo!JP5*r_ zwlKWOvLsG|t?n}~h{)*|ms2wm&9pYn6R}z($=u)&68AfZlA{OCHx1Uv@c!RQ^5cJT zPb}GAUkU&NxswCg>y+Wa-V!T!MvbWdz*%$b+KE%o?)5c7o9 zcWku!b!eG&0*?Y;_)O944y#Y3%&ZiE1ufUMY+h{n^XkU%th_vF`39yZmF}^p``2E! zoqZg-${l1BZa2IY0&EnRg*q;6Cl(nWM5DCd;5$opJlam!$iPpP9uo>?PnxIZheCSLhmWM_@dMZ5>cWOr2J~OGsN$0&e|kY2~C6o9ltC>`IWpVPK`i` zl?;gSDF85%(jC06Xhi^*R~8+T8boD-+&;K;kGIf;!UCzeqvjTMycME`ptC+BXd zC*B|y3GHOPI&ZQ5lg-306EvC5c;gb8XEj;5naSLH7V>Uq($7--%p4fORxY%kH{W-5 zeWC1}(DL;$%OBNQteUsPd>mMV)Tna^T}{&&-*aiu=$Y8_$kA}U&X-jrXvgIP+h%DE zgZLljgWlyVlJ&CtXZ2Z&8Cu#h+1QH{ON_t!{K;%6%_BmmR|0x1C?@(=)I0)VC|0MIB1)B=FIPyi5^ zE&$R7Kw_cLo65|T3SuP|4OC8Uw%xa`C{%B=%27Ge`Tb{+(>8LyQwq}Td}&=v+|l?8 zj+V3R$69(+VX02nveeR9hlF=7XN4s25(+qNzBaMfMa2w)4@6)Mg9n6S4eu7+rSVZR zQ=dptG9!b`^D$tyKxPnR%0VU;WL$KS$%}7fY!8_;B?g%$JTAN<)NgwiG33$|_i%}> z$dhLxyT~OaAfCY3ZB|9tcl5wK!d1NjQb(_%mDUQK$s$dPw2Bz&D2$)AYIHf>ugfL9 z_NUsiZSXzE#qpbIG57T7%_QLKRpA+_ATbV9)Aw(o+(aYbt|>SeCmXI8e{Nj1S_%DD zbur7dJgo9+&l`YvjBh7{`v1zk7bylR9p>oUMEk$|xNCn{_j^mV8JoHK5-^Py_l_=6 z=tpNhp$v2t%@@Z-ihojSN*U<%=IP>c1^FHla+StNzjXT3GO9QO&Q{}{XyeR6ez0st zQ9={zUxi~aK`LLrn}q0P$3q0FM2gK;zmrxaM66@~IjbDIIT3< zdp`3Nq1o(sEvFTvn?bT6J_`8HHzu^wA@BLx;>BjW-$_>xA=cUcDrAZdQo;UyGzPQ^ z9_T`rRG0^ry)Hp$lJHLYE&N*@T21D%6AtCI+M=-P+H;+J1LoyT?f{Iikye8!&xa?) zujq?W(UYC#lwZ*&ax&|6jTtJcyM(-ahONr7?}IT??bb0gIbVZMDfP#JgI~tYRoef!5G$nmn^HwJUievKY4HjhMcGG^mp0n%DS9{Ec_-#7!>BW7)CQtSCl4lPy zlHts|f+I2c@A)7mh6W|j*dg)R^e<9FPGY-k$*W}{7Auzq;4sd6A?JD69()Yjp4(T} zo3PYghwRm^sz0#u)J?%3K+OrTW#Y?WpCXU|doB2{4hWDV( z4$7u&fY&?zxz3#vSkMh<4wk-zfG9gRN@KsCF~oUOTHy2NXfR8%&zZe9KIy`T5lZA| z?-Ba;c|z$ccrRThAK0JOI&Q!;fJ_s}w17+-$aH{A7s&L0OdrS$aD#ajur3Mm+uIgq znx4hZM?Ad752)XEA`|)~DV80FD_HH<9ijFe-=0)rL*w+_Jd(o)AFnB6ep?8sVJ0qb zFtC|BS&0l$^JC$0?W7W8ue#tCzhcOk9CLxJ`k!0g=|5gbP%fGz7VcAYcGV?oEW|?ig~tw zODZLh*PIl-E{S<|Z%axwkeA|fz_JXc37K_aSzlKDmw;sjOcOTi!qdJihtB~n8O*cc zTT*O6ycFL8R0!bflrT+#m}h0I3-!08MgRzMz%oCki68Uq6Ocg)SQf`LakMVv=*_y_ z`N@D|DEq0$GvsQgK$5+Ic^7shI=!18|Kdc!Uf}mq?onuG4bvQ&m!W7e-TOwwI!@bm zN1>Xu&Sayvwgv7s9869F^bbj=Z}xJ1s%oE52bp||gsYjPv16&DDk4+4n+pa2kv3IzHAfEqxc zcmUM#6#&VDKq3H0a0v5;jAUM`OAZ3ItYAtq>9Pc2t!4e(5upi^dhLnMFE~VA@GP@- zRdCZ43YIrTt+@73B98Gv&KMD!sY$Lc%*ecZTcDP4(;8nTW%0p(|2DAEjkj3*mCv5xrv1W}6Y-SmX4U0Px*LL;g)9e+poZ+43N77(vhbeZd zXrj-sCTwXB4#~S@>UP88?g8wMe-OBsNG)`>W5U>3IJ&6IinF&zD3XKqOW1AXbr*^5 znx1hd)|@v_q?;l`mvm_#U8U*IY-G+P$1Hzmd0qi>~&*j8uWMoJ2tG1`jqu+@PcaZsr&6j~C zJ-#YyHgB5nZU$r1C&fBVw6O8YREj%=G(OTjsz*_8H0TKQ!_vA6bG4`14Fi8zv4gx= zY`Too7~3hNEipI>(^l`A*5jK)UX z-Fgy9HWtpAz_j<4V|W~n*U9~jFC>qT#|t$q%Z|nqNg4$hY$^ZGzL2!O-(BSVKi_=u zPRYNB_*Kw9{f;5tzo^RYbP(S0Thz}8V|O!$oGUhjer zx76Wygy3~ylZoGQd9}oXtGUPt`F9Hn6F=T473_A#n42_5ycJ|vI-MSCx@+!OXE1V? z6LNA(u2pWqMX4ApFPZ_wBKRZY{2UT?mb$>5s8(=cX(p#&P*&bIGEN*CgHy zuA7$q3cXwL6^)tThw3&f@lGY=a%j~3ZU(Rw;IMYnY%g?XkS{YBebi^7UxZqzX1(xI z0veFm&Q0P>BjnGar|>URU{A?Uw7lQO1H!+c{U#d-{B#ktU6you+rPy{r(umHbr)Xh z)C0Wy|DEyXlCi8Cf|l@S43Aigec(he-5*;jEZcI{PQbbaYvb`MV>}d-fhG$Z%@OaYzndf8j;eu$f44^h8SO#aBiZ6GDeSa)2s)6VKQW< zphp%!gx>!$6k3ZyIC!@<`^)Vk@V&~<6+*$zle<#SJIv>^PQ?g5wVto}UeA{A%K4#Y z-l_R?E_^>jkE4eB*w4MnK%$NQ%bMC$?8A9sxc;Lsg6dc~D)4Hy+kC zPO#l16Y;ch2U{Z-kY7d6W-ZFjlQ*BiYGW~(3s7?VjZv|-=IZ$DC;DmgckmB?E2|<} zA*YedTQ+-h3265@950Y}QFLs>|-{W=>r&uk$`tYF|u z2hWU$XPT*d&hO)`ZY>Hp>Nln~obYY*MqT64oa;8!lSTXK^?~j1v%t&xj|B zto9ZgX`N|J)somB=*z{@$o_I1hG>m=}&B9>x$;|s7@Pg&Sdt zq|RlD()6^?*uOJ`fT?Nq@@B#)b!K?X)_B9y8?Hpe?D$IC^@jo% z5%Ue?&;Sk>BH%FKBD(2^MnZG42mp73gRkcu5%q<(D`5yK={KFeuh_*HEf3-SFEju) z27;M`U@Rb5IsikN1YovC0L+{ofJvbPForMymes#%iADjXQ6slf-U9pvKUOcZ#cuOx zC~0qB5cp)_&nAo7G?E_&_oG&X20U~h{u_GeJ`Bh}fXw@E=$|mtWIH>xS-?MGMgWc% zGvIguP8Jj3*aA)$BjD%*&L#ukDAE;h3R=Q2g!k5Ke0w}&T(ET8@xWOjf90gZe!ptg z&wG)IYenDlC)oBN_!L9U?atd$vvzKuD-!vo%Sa3*o*o< zt-WPj9nI1&3JD=dfB?ZIxVt+EZh_$L?(S~EA-F?uhv4q+F2Qx-?tW(F+3!C4+*&lRXx=;v!>UouI|Z=X<6SG=ULzMPa7c?Pyf9hor{9Wv;A;pj4xg+2A^>U zh)P`e1@T|{P)FM_MUgmm`2mQ|+0Vo7OjnX3B3hSCK$NN%DFXr1^Qwu<&T!^=_?I>8 z>GA_AH66G0eqOkAR=p!PDtzPwX64P#Vy=ki>J0(!mGZluHm@d?xwz!}W{I2J1J*G(Jut8-5NGdBph{2=;(6C_!lV)as2!hFQOrzF(g$ua{jneu(yi>+` z6oRxHnH_2*W#ckU|CZ(FbG$jC2GA zh|r4zB9pXzE`r6Iyeji@HjRc}cAdL?)tdq2?5|&rTt5Oa@ALd9U$xJ#&o66nf?Db| zyIPE(uOYb`2hcaQA-TT1rbMPX{K;b23-Dwy&^NYVNCkji;{fQ91b}G4kSGAP{}jVc zx<(a$Ywre>`Z5M-DE31c_fo~4*8_}ir;S$`{5k&&sE*gZtwd&3=l+SSl~{`!-)&yu zM8w(BX7)aY*u0SGFi%UdGR2fLR15R>@@tRW062rr=IMq z-^{mvw8e1!nsF3`DDIaOe8Yi?t;p6H$PKcMEnd>cj(l_MqvfLL`Pz-|s*7;iXVxF{ zHHdjoTRC3wMKXkc`4&ELrSZPLDT$tWbVf1|G1CHOy=Os=) z!;BK*Ws5esh+lyiz9j6AX8DnD1&tSVMU3wPJAY=zCXemdXC3p9*QSSMz2(C&;99q5 zbU@4uv)a|$)8NBc8ROhP(#Og<{Ta_2u%8KfJzKYA!?}B04IDTY17$JvQkZ3Mk3M!kqu*}MSCA6KbK+m1VyzMSYWypW z;(I;Ccdbu_E22EC{<0oIP-eHt$UsTxm(%C-86A=Hf0vMb7d?CAr?Gqg)`3fZg=Hda zK7Iao39QP)I{zOH5tvvl5&FiMK8&?%KK(g9;m>oMZzeSFoNncpFEkNR#Yr8!l-Nt=nqrT)Z4|dIie+O z|9F7H{U1au3=rrUqo3LoE~Ps@kCB|b6V3n_BALV+m*@hZp#l8%foW16ErTfS?R`KO z<~ZvA_yk(`vxVUbZiJ^kd1=U7>uL71)wTcqgLUL`n*N82gScj$;TlhB5e)vCF0RYs z(ONOt5x(4}CXj@#wbE}C`s9~Yw4iZqMq;_mn{c}!vs2yC4sIMvdID{x%j{bxm+J)f zWK`MoHc>h)w29MAMaX*Yo{~4^2dKp;x1)CF#RI|d4HZdY%J0I_KpujMj6(^H5{}ba z!%KyUapW{Bm(T6E;UjXX;o_Gy>@B;2XeGIB-tsf?x|tJ;UhM79&G+60i2hgT-&Sgx z+=8f5-fF$;(3N7Ko!8J4TN|)dR2wqTq*iVbO*oucvrw6Jv=2s~$}v~e<90Jkfy7S| z5s0IGSK1P}ji5fQx!>dTDG1GfAyRFnxRc%c=g}`~yw>N0RR0hxLrKLDEZ2IR>=!w$ zM$+I;p7)C*?8HIOo>kWOJ&JvqC(S9zs#mPuhU~juS+}rUybthQ1zecuAM5?j#1Wwh9Deviz@)DJ^7!?;anu9kq(E%y`i)Gd z$7MfTMnm=C2Em#%b#487Hi&~GuFw?`k<_f*{{X}02_SkBGcE6D=?M&j7$v@!8QtCY zN=7X`q-R=R&a-}C8-s7}(NMkd6Ex7H;}9VO`8iVsH?H$S>d~p_L?po{=n;ebgqMOF zbzIp22qqGM!U3pv2Y|4+0O&Uus?!h6jHFG6!r*Vc>U%s%emJPgWXg>Uwbj3H`M*Ls zUaVI!D@?G3B^{Uf=5VR*k!-bY4iThB#~&B!x9HZLsd)9S+vLw>C2!M*c2(+6@8ARJ z7oDm29D3XIH*|-u@!*lvOHS`fg6LA@qZJ5{Os`oN8J*FF4AmHf^lwtzGF;W zZnuq6{b>L7m1)-3vVY2qbrx@f6^;Vkq58<5p3kxUbiD0}giKKCKjVt{$kCN*5c&u` zI$E1$vn91(M=zt*3bc7*L;v=9 zOK$k!+j;w7mvuKpWF(Do!cE^pPM4On^_xW0KV#k$Yd(&~mCk5HV{Ye^y8 ziN{oB<&(RTN11!CjcBo~@E<{QXv3=VCT&mqs7`l+w|M7r`$*AUNyO@oIddbBCqZR% zpCoOcuHRI&92elMUHVRc01em8IOyZBr|N9 z&XwZ(He}QPVI0d=myuWi>m&&|>x1)1@b`RPBRkU}W|Vl-D6Ej7n*MMh!v=j3>5S+? zpn71C**0Fc7|OlC`)xUowriB@SXtt#EnJRZ`0!);P*hXhTG3M>{9m>>>gsv$3CEH5 z^ZsM(U{q5Ke^fey>UH+k3b%#A-VNhR&9ign)560zgXj#X=Bed=-8zc9ez#HAr>iPn zkte7^R;8)v4A-Pj=Nm;zLZ{;@zW-bSR>>z#RHHQIFDrHgi_rQkvlOi4#gMzw6)DWG z{_av-mJsJpY@ApYe%l5%!og`D%0FyGZ)I$ek;9+aFIO*b8%&IkJf27D#VhvrKU%zE z)Ol8exs^Wn7 zB*N<9klY1A5t|D=jpQIZ3OJ7Ga!NDZ^KDAm!*mk}PP$akPWMDeS)OTWbmtl@_wKtX*MXK6+>-@<4K_r_eD^q4AKCAn$@LTrZ z@LR4Kv;Sm%u8M!rJxhcpU(w=7aY`Mp21Uwq`Iq}j?&wGKivGn2xkO082>D6eZ>g$O#;<&I3c~URaoT8q zaf5WQkkprJTEtHB61!eO(Jfk33P@;}Y~Bv!cacOP&!7_07Z3f7wiGJRtyD_ot4n=n z9uQczeua)!$D_C}N|xj_%5)fc%GWtNY^7#wdLDQxlyPdTmFf6uFYY_HQ^8kWb@srV zIo!Xd|ApA+5#9vK#@e?LjzbQ3TMH@mV+uX)`JArxRZDl;=FYG{dKjp&qe00*QoPVy z3GZQUBevpM%cFv50QxzE0Ox8uobiwFw46icOv!0-{m#gBdxwPOQ(*@W=T<0Hkp$&T z0p8b^{?I}~V(ZKA`Yi<}1K&*fq=Zir(AWGU5T|cRembqi>Zp0h=u4!YejPDtYpxk( z|2oH4E{9dF_-U>8#QdeI>abLKH3NsAp2)rfH)LdEI1zY>lKAtZHOR7YS5t> z)pFJSB2y;|Z5%@*q}N|L#iqyqHX$ZteA4St>3m?VO_ArK-Wo0H>A2an-Lu4H+j@-( z|9+`Hboc(IscGnyZDkyN>^>8(&0+XKiY#^V4%wd8%57_?rg%YGSF9HAW*Z-^sy_!7 zGNh_--f1@ujgX|frsrq=ym=sWDZxlX5uKa~$(RLHyAJgRdVg&8cQsr*u{dE%+Q`V6 z#mxaRn=oQQ0ouq9m~)#h2hD=4U%hUQt-bf=U!Tr3N19{`;+@2b>@0SQOrm6Viph()W3(1Yudj!OAvHV%Kn+0sxZt{DSLuO6* zwFaO4HDf0CdSb^1i31_eW?a>@bFaM^1 z^Z+H)>7_&20khN1f7ioX00m?(jD^*$33fd;LaID6DjvUcvd%1Ch zBZuE6*rcP=Unb|JFSkZYggWxn5XYK@IyzmGU^qDFQ6b;&niifl>_`ZA^a{BzXXg>E z>t*v=?aLwCWE|wRF<0>&A)oTO_0BszqcyXfKHF?N(9MtFg)!%4o2`aSZvZbmi*XVy z^{AA#lIfXo^HT5U|Al?>nxJV*bMOERTWzObjSUBWnTO)23WkWm?UVSjYPA!J4;jR&iYB9(%J0P8Lq=_02kqc-)(H z1z1hr266@=N&-6Q&5YrXc@Q!m;%H~T`DWn&F{;$p&Jqmo-ML(HYB3X@)!v-*29zkW zIqUT%XjlY|0i7|s@K&l*wkVKB^N#9$=Ih~bruWVXu}l*kDv zTD3@iM#;Gnc?(A!(}uFf?|r$T4Bz|S{X8Q09@cS0a6wH%Q9(q-4WmFn!+jQqghgw= zs^WS!x#qF|lE`+@#A-*~V z9GD5V{0Iq!rt^>D+k-vL5U#drXRefPW5Zs}Wy*eij}HS;#tja4Ww&cXeB3AOtg*p~ z4maZpg2VpF!-1@%WQc0Ee)1{|3D>^Y|CMut$tcIvZ8X-1dzDyWbeXA9i`td?GVS)m zN89bWogTHst?Ee5=LwBTDm0N>QIC%5a*u+$%&2yHJ3>7?GL&!j6Xk^u5Y&5p@8Dl9 zUfO9i(J4(9lL%I59)*s6@9doNx~=4lh&dD&CBmJIi%1)#3=ueIH6U`Jro0ys`sIc* zsunCueJG-ep{MXOvqQlB&MW)NyT_28LLGkDUw%(e=u|zosPCv2WJp8+_`PCTqDNu6 zQ`1vx|EfDRlOvO(yW=S3%)#v9FQYr5V<%U~n<#hfH=;TY*{;PT8M>h1w60Qs|Eygq3d}cLT#~3zS>3 z%UHkBmV3f_6|2(Bt#~7A^|5oZkiT7|eVdX98F53o07L010Rb?OEQ8;kA32?m&@VP>_7Iq@7l-YQb%oRy>3&q9&9m`~>h0Je zA(;bJQa`+4@oDa01o=pNw)qn5^i~>|r6&*Vy3cKd_jBjI;Ny!9;wUe?+&h=Zv!7HQ z_6(^CkQvpB9B>Xc<9-T|wu}><;MxvMorKV^x!=y8R*#(DnT1*#xlb1iHRJ!D#o-T@ zqQ+sU?+MovX2g}r&AB>k3%DI_w&K`pim1Dj%xxKc+a}Xq{^@;c|87f|UFmXa0lM?! zW`(q8qN-T(e1nuS3qsC4V~VwtD{G);0JXl}JhX4SNgM>?t9TCI*h{Zg2LSR+~#ZyL~lQl#RNuxc-^k zD!~juM`Amfc1J4(H5BHBqt^X($%+DuFuEj)1pFUBU z@2i)O7G2xl!~g>Fhx;RWR*h^0&Odr9_m-Y$wt7aHzDbuQHqLo4gNy`Hckd6KuyM&&T+Vwvz3`9uG+T9u0{jg<=k%Dt&!PWrGmM6 zj@_-k2q9adsvOS+5KxrX!!<*>gmJdBT`$y4bFA(jU;q`_Ga9;{3!u}qHD(+ zlm@_0+-qGNT!~bo|1y*Nf1By?UuIVQcV?>m_hu5!ttyL$q#CBAsnKuYX5eUBk=20x zK&i)DzAR;I7rG00ZV~8XMJC|6f1E&T3+`ieaX6_}5m~|9aI)mmKTb%f$xBIF$LF84w8zfJ&6f$s(G_93;0 zf9|8!Z9o8Uf_)KiFYKr5W__xZj{Yw*!~f@IesFW`XN(Psn=NlWFuNUZ@TLV4H#KQL z9Y%%~%_@}W97?p0KY6aU-8Jc-yz~~{|Lebt@egk!0Wy=_5yt!~kx9kC_=l3}wYwwj zF`nZ};9bpeoceObj_hnSqY((1x#U{>msjjgTwHQfEsWye>~t2t5*>5HZt|1siu9+) zX8a`s1&uaykdNFbH4k*H?1CH(u%r67?S?v|#c?Ve%4*LDUhT9=O-*AEH{1 zcU#7%$Y^Sf<%5x`bj_norQ-U(>^8rzY<_pOEyTAi)Uu`UhPohe$%P>ib_^5T>CJNM z$#T2@KAkL8@(^U*zN*_w53+uptWVllGU&C9)5|NymYx^7)y5{pNIgx7BOu=N=e#85 zOJY_B7<*$o8*O36Ary@dHiNvtfd?GVOZw)diZ%BBzqT_F{9>9}VI)qbLuQ*!OjtD= zv$E1=|w1s{9$PY!2TE04Cvc$G))RB@Wy5()kxIhGI(zAkqqR}|# zQS0k>yUdI0);FD3uE~H74Cv~-l=1h;i|Xt}8O_Us&ud!4?BtF&ll_sBw{%gmVDXNn zPr%h#x~!c5#fkuhD`6aZ`$t!P@_;GYfSBD}3nr0DJjs&THmmo{RsT71fNy9vCyr;9YAwM{bRB!#d)nFlEdjW?~&;xS-ozgN!HB5 zxo5RcK;ZnykTA=It>))X3gOUmX+eH-u;QsQ*GPAY3F<{cz^R8b;)DjVj$8A8m)mNwI47EKf2Il z4pJDUO7`%?phZ9GH>uRg6bXCx$WCG*9cc|oQ~R%DZwX6(0m#JlTE^;cYaG`yekHkR znEf%*N!x2<-%nNhy?P`?uKciAX4AYNt^NFVoHvkB=lD$uwR_)Q=D7uZiEiiDAgH(g zh_KM_@K8R$aFK8n_I?5N1j#VOuz2`I=Wd{elOSa3kYMtSxF%-HIUKa%IxWQ&6s)++ zN2D_Pr+Nrkki_-dmuEzwzC(k?uw2j zAjIUMrYaFyG~C&$xa@t7wnL8gV|C9x-P>_avi&j)Y}|QaP9EGn0qiuLHD5tyY+MdI zeKw58*%(9g_nuQF^G`d2uUt>nQu0aVSZ`CZuIFj}oio{^br=>w%zpjlWOeLYYi+iJ z-#}@iGGC7%_9s!wpqFrXu`S$#_ZrfPc+~z zID%8Z2bgwLd+K2vHd$lZ^}D@#j*(m6-FHWe2R7bXHC*2GeIKFU&)~kT8*A81jEbFj!r%W6PmSDjvn$Hx3e@O!HjV7jyn4=f}I2>nQU>yy)b z{_NPF?)>s5Y8eg4q&rB3+EKXtv&E8gqf(B`yy@(uBa=H!!Do znv@=kxeK8#miE?y&=&D`U=L>pI?66YspZuNt7CUOwe|D2DNiwjND6lsrG7>Zo+W%t+~4U0_8n}UG@X=9 zR*02Vr)>|zp5$q68<|@)ovh9$lKK11BMlGOd8WpeM)<-?1U>b#HT3pi~%A(k%FMQ!5oI z^bODzHuy~Pe|##His4s?$%9XzsEckIiZ<)bxS#Pvc$q-sdSxOKrULH^Q$=|Vjq&u+ z;4)28k_ty-LGb5jb&_%^MT#}!Lx!sRhsQh9s_kI5>TSYnH@2Fwo@+4& z*++P`im@I)+7jkUoty(HnuQE5RQL>HJ-39u%K0v;TGAKg&+eVZ_H0;1W_MHYoHP33iSM}Y!{$f5wIsV~D+LYJ62AW0iu6TvW5*Cn%9Nqqp~U*kPP? z3OCi~ycA^4*}?DS*nK~KdWo8^9E{d&0>z*-38N#Gjoh&yNbJM-Lj*$KRQ_f$ z!lKEaqfi^ai z%uU{ZFyS;@B+d*#9enUHq6j*R0c7P$=YZdj4}CWxU%7poDWgfkd*p+%UXC-VUUet$!@mYwe?iW29l}M)a;5qVkWfOW(35op3Zfbq zZRa!SPsk~|Oa_B;LsqM@!-k(PLlXIK+lLdL7{=<}a$j5xx@}BrFB+W+o`lAsxx{^~ z);LzXm>0Ch&wI8z$Yc+RYZyxsa<L@4+jjCOSv9xq7k?cNzJ4n_On<5ew*Ujh^i z7gflwUP!-4X(AI6rzWnLT)?KRkd1RKFD38Gr=Lc2sW{LLs8K(@c?PhYUqLCd}mPNAQ#$!MT z>BS6Q2zhkDq8LvI539(g_;wKuiVTqqZ?p?aXL@T7O6O<`!b0bz+1bLMZe@=g7SEt+ z?a*-R{4cJR*-ETSl|SGkr%t_9_M-?za6DaUtIbzYuMvxPL|GLbH54? z*Us0dm2l(nwf@b{hpC)EtCk0g5eG56%D}e+9Vc0 zl0B~+TjXmu!9MXW!Pv?Bh^Qze3>!q1{U%TlO}3IrT=^mOm4;H_%R8aocZEKMCJ<;4 z`aUZ^86O{8pOY$+OCQ^x`FKybOdiMan{^18D>4s&^knCJRJG(!@m9=z)QQDZCAD04nUkep2ou!fR8$7Py6M3F$V^5P-C*MI(5!^Kv`snf8%FQKt;eIoc+d+nF3%s0cMxH$Y(u7f{MVUF9r@KqbV1F7yxKc3V@Vi0ch30 zhqM=)CBF0HuZNwbSC47sjIIMDSzn%KLaLty{6g<8u7V1`WW0Pt8<_r_{l@h{P=P?k zI~Zjg%~RAWCw;P`H635w`w6y2&ntRhm(t51zT4pic@rZ1@etM7rLt+s zGWSLA#jD|QjmqiA`tURfW@U^=TI<(#wR-9VQ>9dEq&tIE*V(}b`z4Fc1|z3DEv00B z6j}R}H+}XY_y~RW{$Sz>CeC1D3nu1ZVhASMV4?~p@?i1}Ohm!tE0}QOBS1Snk5KwS z&Yo%ibdkF68{WnDxKK{cdk_cmZOzKZcUNp-+h$;$MvE~NJvv(>5`$nqX{oXgart(k zrYPr}y_30ujt~5J%GjNkV)Ow$^kiYWJ>+u1^HQ3|>u#hqso~K4rI<0pwja}N;H*Oa zo>T9BrRUs9*^7IV^~JxVxl>SXFM38kFd93h67P2X%s#Xn z4v=Y?tA5DjZ$J56_z}$?*#Kc70$AVi@&V&*Imd>c%Up^DJDd6H&d}t6W$~%bROW5# zF0j8WlRlo&OXvL;GBPL+dQ{Xme3|>rL{nUR&-O)p$79|+R^+Mtjs#q02Q$8Vg{t!J zvvbI#(!I?Yh#gu!7EERJSd?M`SVR!BT<3zx93}>6{iD5*gaUqvTZL5*zF9j-h|HMvSKAw z=!MT3U3yKt4FOq8ev+7tqzwVK>i%K7Vvd(pHF;F4Xm3AXjY(Ei%D6vo40!5*KHV+s zpZ4t>`fzW1d+p}*r+FR@c_u|1O2f0jZ2Ete|M(fV3x4x*nf1#?KkXtxF#X7@?Jzsc zrv3fek~C}?;d!EEc>K7Pm29oI0AJ!O?7hYd`8NEeH~DeqPe3-k27qNkO8Sm#v$HYO ztT*^sFMlVWwpBlQM*G9&(*@S!%VJJF6-;NSUF7WYx$RyW-mIr?a!6D%RaqUs*o#-A zm2f?b%bS5F_Ntu9%~Le%&5LD2Rd&ggND|S77u#SkceC4Jl8G8?dc*>VHuXqOFrs&` zZ5gMqOB-vpqczkn#Ad*VtE-S&xDW*qeUw;Obb~gOf&p8zhsL~QLc0%0-pz5{*YeaW zLNz(L^2Rw31P`a#DtO}k`$9AJJ&Pv&-T{>&63$o*a_c z1i%aejJhJYv!RVHNKj-n@f^9f7w%jdoDIKEw=VNq`Vxgzr}BIE*<*b zW}cvYGReIpKaJK+NnyxZiU6b_-v|ysEg=%3TCfL5{T}@Y00IMXlS&<_AwX8L9}yvH zOM%z`R0TDV^{gHSKo?-B8w@c45KctENHR)NH3@^Uq)s`pFQ@1m`*J8y6H3ydxK25} zFK5(N+R`F?{kwiSGtz1A?W6sni|ILjtDNeNuz+&0>U?Zs^Fhn^7=`zogHKk2Pb~&s z0k5uOs&WsRX`eT|?SRtYed()x%u@RYuX+P7Ehhf1R{?M#;5D%BmBRz1rZqY9%^G!% zu`HW=q455!;9Bb{FG{PCv$}}8==@Q{=G&24{e94))xq^_#MkcGuPANCz+9)Qg<^Ey z39HmF6`QH5P z3lD)l-0{XIkSWQ`s;>A`OP=EF1zzplVazLyyG?=j>BsxpWJ%8FghiF&+SaZ=e7f(C zKpyv_ft+LxbGOe8s?us$swc1y*3X2mWtdH5K4m&|$8bM9zka#D^5|5cNMeC@E$@_! z_^Jy>7SXBT(J2JSWQhG3Z`N*#ZjqPYOyq&><<Y;C*ovX$fwksd)8h2lW&UMrNp#q}n!f52E3l?Y zh^_JP1hUq^MA)7atd?MXb# z_h?QYcSlTI2Ayb5oe5wvy+at}(>ZMOgDNE_GET!8*ge9WWc>NA)H&lh(_B)D43Tzu z0az~#cka`(+YJ0uNGpJTbY=ZUgFIsk{MMVro12#!mUYPNpgn27{E@-`nkA4|Q4x~((EEQZz^NqmE z$}-v|scI&zxa4!TQC1NIWIa|&y>M_H=>{_oufvcZMmcHw$!x*I>n zI_@iqa7B@t8M$gJv*G4M`sK=t@F;rsn_~|B4~ohq#T%c{X|KfHeqGcHDqGkY5{ZYs zA<>K%o7#jB=NBh275B-_B#WtG`7$*dLP87w)8QMD`0!hjXPW5T^tU9MsbW)fkTKQ>kZsS zfNR?UYcL7+*Z%2aI$-^Ku?LyI*RU11o2DSdPkW=ckLms>+D~}X9#|XGx1IadUq+Gh zTK#yp&S^i548f!G;~j)df-YnSv7rjY84cFukZ<`6OS7Zog*E%ib5PXt-Jd;J&c|^3 zS(bE(jlZk)-r)N@tW7zIKHUW~ComdUb9wrFgTeb?0PHC;1nen7<|7&Pc$V_9iJ-mT z(*dZtdTe2Aa;C@R_;KG(o_Tq3Ba_CNdaGkBD(5P_Ms5CSrFp)Xs^kd`TKh@iWXF3< z<5o%pbd^Fr?9?LN=%sVAx9m!d<&+iOcGP!EW|kTiNiYXz;-TN2NgBP-cfh=|bI z&v}QkRU8)KhKv;XjEofJf=ngJXDT&rLdRMT1=9K=&VvGx8hBy(A+gux&-czt$BwDV z27hy#qjK!D=fsHkMQOmlg)>yVl*13%@jSel{{B`*zDOpUr(vDe(gpQ0NAdS1UB)XD z8+WEwZq26@bF2!rm?)KKmHFHxJl&<=re)J6jUx_+<;zaBbk=8Aq(}Cu)*$CW+_4H% zLP&=RQ9_J`1B4DcL=HkjK|q;+ycrDwF4+8?&zns($X`AUN)QwTTL*Mwz;c$3Z0hf_wZi~zSLzJ#62@oXSS#u%3YNPrX=m9uo;mc38v zmN#P=$%E8=5>Y)C5EN6L4IhG%+ z_ZFoK3-6ZKM)UKf(aZv9GP$ea=x7{U!R$EhJ+-+N84G9YWnnm}wUq56*X_lkW%v4(1CN}ug%PmG2K z1IP+yhj~hx|JX65_wJ{Q8&Ycy|II5<>q@Wy=M_}O0fx|dCGOaO{ zCctttV_D3Cl@v>2Jm~~8JGItu`(;ay#^fnb%>&j>&(fV8bJpN~x~L(Isbn26yA94( zumq;~(`$QZ|A1!33Yiibc2d%m=)qY7b)02hV`@_g=5}Vg3KXKNQ`<&B_QCA9nDJ^~ zVvcx(bV-42WNQSysQhBu>fbB|3viMGkj1bMp5h0x7yvgM|IK2k8a4f!#bA*$eE`m4 z0Nmj8BhZ$J?i-L7@z+cetACrRu>3DGmH#_4i~oBw74uu8Vi3uPYdFgI`Z!V9%hRMx zz$cJ5bmQSn9^1Sc%dLtp2l}W+4|wh$C#Vel?W61;i9d3*(j`&V3BUhw0*$6*Ek|uN zMOxR?zHOBPa19M}1+BM+{<-vGfqDJ0sj%OqPkE^H#nr38nHr#}y1z~+CH>n+k@(7< zf9@l2#t7I6b1`)qvGUvn0!2*e`2S_*Dck?pOw6luXnpiVno(Bg@3ZECUMn<+4^o~W z{d&u%<#z?)DR6_S++;HILulk*W=hr`U$map`5CFG)>uoA-&Le zgxGnBWTN)6M6afZ$tH*&_K;Eckz@CeWA~Be_mJiHk*D{Nr}vSW33}Z0gTX70>#1>o z72iVwt9LJ1NE(cf@-iY$eD-|-oQ}hjD;f0~Mx?E*`!ky8W%Z}=Qc=94&z`mu$9LGT zUC(8zCQmrG{G+(2n@+Wk-TMdjqKm8iL-HM1OnFocgGDOE%hn9G9i{3$*jEuxIk^Nd zz0*NoFtx-W0w@W(p5=>&4*Zrs1}pgTeaKe$hyo`oA@rj=oDr{BqOthyEOTwc&oWcX z#?)ZE3X%ES)KQArLh%l&gTpllgWql4JITxD-zf*9oNCJYeT<`(HFm1C8gILevc)>m zfE{Q|bhKE0i?aMyX8BDk(do*h7$^63DLW2(0X=V~qmCjEx=xsKyh=~F65Dn486!_X zRoSBXyZnc>_17|UwibiecbRR;`-E;``BrK`umElJm>`%ERZ5a^Xgi6o-dYYpGz%o9 z#T{KT?cJ!WI6q2gCKXk#IB2cTb3ey>VaIA zSHH#-G|;YM{RgiyR{p{Y;E8&V#Dis&AE84#1$=Y#`dCZfnxk$2nJW2r~ zqI585AD2=0fqh+UMc=;BggG1F0S;W?c$x?2Kg3=7=@?|d`$ad>!ia#X|K>kfiPF

N!K?VWkIdC++V5CX^Dc;QTi}}9T=lerttP4d#b zb@t-=s{f05Iog>R1Xnsx(kBjuLM1(5y> z0`qrk9IcJ3+X^bauWCFyv*Q|9W3$obUsIWO6GmRVc&~n)wYRmd-o(d};}KuUSvr_< zo?aev3X z@?O();jB+SQW0T>*lBV|#ERayqI6_69C>_wlrJ2B*~T_zyVA)LGk_H(QOpU&q^aPc zgIaaCmZOZhK#J7-|J%$y!aAdFvA{53{F3{;m8ORSjLa;Yd0HZz1AiZcz$hmDM`Awa zn{-Jc@GLf5N>k!DrZJRc<1Di;v;f9x^n#gwRmlZFpeT_!p?pUzZBF&mka*oEPSx^3 z%4?=6**e=DBe_)h1LL%53-I%rtiTCBzA~3~2^a{$*?Ucsrhl{dEK;Yra)wkk6D;f` zTGJY90D-s%PIKUCK4_XQuZ!y-86EpCGadf7nMeO+rrdvH=H-8HrUaJBX=-j)FB5`v zQIKt9Qv^8%>R&&^`QnSxu&Fp5}1Li)>l8YTv9@r0ZJG;P5AP^?cZ9k;c1oTm@6?}pJ zIzgZEZyyB>sSSb4mB%**{(%8@LZt)k{ASHq_V=ML1%3ZzX666fOlaPgjik=}JWRvd z?+W4I@6-2dtjAl;)4`RdKUK2&n$d0piAM?yg}^}O7o^ZzRG4s>o2^<>lpu37+`n>F zuqyr-85+24$wO9Q)v)eIJ?^yZpk^agqM~c~^5}C*Mt3)pwabX8U4Nd__3uP7Z->8O zKwPFCId^Pi0`xyHR#^k9M&`)F7Hs@kBQ5%K94Lx^#YN~G4y%nvc$ur@K>|$EG zAKNdmu)?UQPP2YS zU=&B@Ra9zNLe2ROO+n^5G1=p$??b{bYTsXUWu8g(qoWFqbDVo8GMsqN@FsYKVTl z8lK9|?b-#)Zwz9VH%lp}R{E-1_D8c(moRQtfWn;P2kME5d;EbqxtsOQ1YUdN?gTlp vX!0f2XX*s~rfjqL4L=}#m+!XCdF;?V!=^KE4f37S^XE`*=^ZZ(bcp{2Ge*tJ diff --git a/core/src/main/resources/bedrock/creative_items.1_19_20.json b/core/src/main/resources/bedrock/creative_items.1_19_20.json deleted file mode 100644 index 98d9e007a..000000000 --- a/core/src/main/resources/bedrock/creative_items.1_19_20.json +++ /dev/null @@ -1,5440 +0,0 @@ -{ - "items" : [ - { - "id" : "minecraft:planks", - "blockRuntimeId" : 6073 - }, - { - "id" : "minecraft:planks", - "blockRuntimeId" : 6074 - }, - { - "id" : "minecraft:planks", - "blockRuntimeId" : 6075 - }, - { - "id" : "minecraft:planks", - "blockRuntimeId" : 6076 - }, - { - "id" : "minecraft:planks", - "blockRuntimeId" : 6077 - }, - { - "id" : "minecraft:planks", - "blockRuntimeId" : 6078 - }, - { - "id" : "minecraft:mangrove_planks", - "blockRuntimeId" : 949 - }, - { - "id" : "minecraft:crimson_planks", - "blockRuntimeId" : 4852 - }, - { - "id" : "minecraft:warped_planks", - "blockRuntimeId" : 922 - }, - { - "id" : "minecraft:cobblestone_wall", - "blockRuntimeId" : 1184 - }, - { - "id" : "minecraft:cobblestone_wall", - "blockRuntimeId" : 1185 - }, - { - "id" : "minecraft:cobblestone_wall", - "blockRuntimeId" : 1186 - }, - { - "id" : "minecraft:cobblestone_wall", - "blockRuntimeId" : 1187 - }, - { - "id" : "minecraft:cobblestone_wall", - "blockRuntimeId" : 1188 - }, - { - "id" : "minecraft:cobblestone_wall", - "blockRuntimeId" : 1189 - }, - { - "id" : "minecraft:cobblestone_wall", - "blockRuntimeId" : 1196 - }, - { - "id" : "minecraft:cobblestone_wall", - "blockRuntimeId" : 1191 - }, - { - "id" : "minecraft:cobblestone_wall", - "blockRuntimeId" : 1192 - }, - { - "id" : "minecraft:cobblestone_wall", - "blockRuntimeId" : 1190 - }, - { - "id" : "minecraft:cobblestone_wall", - "blockRuntimeId" : 1193 - }, - { - "id" : "minecraft:cobblestone_wall", - "blockRuntimeId" : 1197 - }, - { - "id" : "minecraft:cobblestone_wall", - "blockRuntimeId" : 1194 - }, - { - "id" : "minecraft:cobblestone_wall", - "blockRuntimeId" : 1195 - }, - { - "id" : "minecraft:blackstone_wall", - "blockRuntimeId" : 3932 - }, - { - "id" : "minecraft:polished_blackstone_wall", - "blockRuntimeId" : 6726 - }, - { - "id" : "minecraft:polished_blackstone_brick_wall", - "blockRuntimeId" : 973 - }, - { - "id" : "minecraft:cobbled_deepslate_wall", - "blockRuntimeId" : 8084 - }, - { - "id" : "minecraft:deepslate_tile_wall", - "blockRuntimeId" : 5073 - }, - { - "id" : "minecraft:polished_deepslate_wall", - "blockRuntimeId" : 7819 - }, - { - "id" : "minecraft:deepslate_brick_wall", - "blockRuntimeId" : 431 - }, - { - "id" : "minecraft:mud_brick_wall", - "blockRuntimeId" : 732 - }, - { - "id" : "minecraft:fence", - "blockRuntimeId" : 7366 - }, - { - "id" : "minecraft:fence", - "blockRuntimeId" : 7367 - }, - { - "id" : "minecraft:fence", - "blockRuntimeId" : 7368 - }, - { - "id" : "minecraft:fence", - "blockRuntimeId" : 7369 - }, - { - "id" : "minecraft:fence", - "blockRuntimeId" : 7370 - }, - { - "id" : "minecraft:fence", - "blockRuntimeId" : 7371 - }, - { - "id" : "minecraft:mangrove_fence", - "blockRuntimeId" : 6635 - }, - { - "id" : "minecraft:nether_brick_fence", - "blockRuntimeId" : 4292 - }, - { - "id" : "minecraft:crimson_fence", - "blockRuntimeId" : 7998 - }, - { - "id" : "minecraft:warped_fence", - "blockRuntimeId" : 5855 - }, - { - "id" : "minecraft:fence_gate", - "blockRuntimeId" : 76 - }, - { - "id" : "minecraft:spruce_fence_gate", - "blockRuntimeId" : 6586 - }, - { - "id" : "minecraft:birch_fence_gate", - "blockRuntimeId" : 3779 - }, - { - "id" : "minecraft:jungle_fence_gate", - "blockRuntimeId" : 5367 - }, - { - "id" : "minecraft:acacia_fence_gate", - "blockRuntimeId" : 7588 - }, - { - "id" : "minecraft:dark_oak_fence_gate", - "blockRuntimeId" : 4175 - }, - { - "id" : "minecraft:mangrove_fence_gate", - "blockRuntimeId" : 4627 - }, - { - "id" : "minecraft:crimson_fence_gate", - "blockRuntimeId" : 4663 - }, - { - "id" : "minecraft:warped_fence_gate", - "blockRuntimeId" : 5401 - }, - { - "id" : "minecraft:normal_stone_stairs", - "blockRuntimeId" : 635 - }, - { - "id" : "minecraft:stone_stairs", - "blockRuntimeId" : 3710 - }, - { - "id" : "minecraft:mossy_cobblestone_stairs", - "blockRuntimeId" : 4094 - }, - { - "id" : "minecraft:oak_stairs", - "blockRuntimeId" : 273 - }, - { - "id" : "minecraft:spruce_stairs", - "blockRuntimeId" : 128 - }, - { - "id" : "minecraft:birch_stairs", - "blockRuntimeId" : 7005 - }, - { - "id" : "minecraft:jungle_stairs", - "blockRuntimeId" : 6969 - }, - { - "id" : "minecraft:acacia_stairs", - "blockRuntimeId" : 6202 - }, - { - "id" : "minecraft:dark_oak_stairs", - "blockRuntimeId" : 5065 - }, - { - "id" : "minecraft:mangrove_stairs", - "blockRuntimeId" : 4597 - }, - { - "id" : "minecraft:stone_brick_stairs", - "blockRuntimeId" : 933 - }, - { - "id" : "minecraft:mossy_stone_brick_stairs", - "blockRuntimeId" : 5885 - }, - { - "id" : "minecraft:sandstone_stairs", - "blockRuntimeId" : 3589 - }, - { - "id" : "minecraft:smooth_sandstone_stairs", - "blockRuntimeId" : 3629 - }, - { - "id" : "minecraft:red_sandstone_stairs", - "blockRuntimeId" : 5352 - }, - { - "id" : "minecraft:smooth_red_sandstone_stairs", - "blockRuntimeId" : 5548 - }, - { - "id" : "minecraft:granite_stairs", - "blockRuntimeId" : 3539 - }, - { - "id" : "minecraft:polished_granite_stairs", - "blockRuntimeId" : 4152 - }, - { - "id" : "minecraft:diorite_stairs", - "blockRuntimeId" : 4393 - }, - { - "id" : "minecraft:polished_diorite_stairs", - "blockRuntimeId" : 6716 - }, - { - "id" : "minecraft:andesite_stairs", - "blockRuntimeId" : 5310 - }, - { - "id" : "minecraft:polished_andesite_stairs", - "blockRuntimeId" : 7030 - }, - { - "id" : "minecraft:brick_stairs", - "blockRuntimeId" : 6532 - }, - { - "id" : "minecraft:nether_brick_stairs", - "blockRuntimeId" : 106 - }, - { - "id" : "minecraft:red_nether_brick_stairs", - "blockRuntimeId" : 6604 - }, - { - "id" : "minecraft:end_brick_stairs", - "blockRuntimeId" : 6384 - }, - { - "id" : "minecraft:quartz_stairs", - "blockRuntimeId" : 4769 - }, - { - "id" : "minecraft:smooth_quartz_stairs", - "blockRuntimeId" : 7702 - }, - { - "id" : "minecraft:purpur_stairs", - "blockRuntimeId" : 7757 - }, - { - "id" : "minecraft:prismarine_stairs", - "blockRuntimeId" : 7265 - }, - { - "id" : "minecraft:dark_prismarine_stairs", - "blockRuntimeId" : 7432 - }, - { - "id" : "minecraft:prismarine_bricks_stairs", - "blockRuntimeId" : 206 - }, - { - "id" : "minecraft:crimson_stairs", - "blockRuntimeId" : 6282 - }, - { - "id" : "minecraft:warped_stairs", - "blockRuntimeId" : 3720 - }, - { - "id" : "minecraft:blackstone_stairs", - "blockRuntimeId" : 7021 - }, - { - "id" : "minecraft:polished_blackstone_stairs", - "blockRuntimeId" : 4299 - }, - { - "id" : "minecraft:polished_blackstone_brick_stairs", - "blockRuntimeId" : 4479 - }, - { - "id" : "minecraft:cut_copper_stairs", - "blockRuntimeId" : 4606 - }, - { - "id" : "minecraft:exposed_cut_copper_stairs", - "blockRuntimeId" : 4589 - }, - { - "id" : "minecraft:weathered_cut_copper_stairs", - "blockRuntimeId" : 4307 - }, - { - "id" : "minecraft:oxidized_cut_copper_stairs", - "blockRuntimeId" : 353 - }, - { - "id" : "minecraft:waxed_cut_copper_stairs", - "blockRuntimeId" : 395 - }, - { - "id" : "minecraft:waxed_exposed_cut_copper_stairs", - "blockRuntimeId" : 3904 - }, - { - "id" : "minecraft:waxed_weathered_cut_copper_stairs", - "blockRuntimeId" : 6169 - }, - { - "id" : "minecraft:waxed_oxidized_cut_copper_stairs", - "blockRuntimeId" : 5842 - }, - { - "id" : "minecraft:cobbled_deepslate_stairs", - "blockRuntimeId" : 147 - }, - { - "id" : "minecraft:deepslate_tile_stairs", - "blockRuntimeId" : 4655 - }, - { - "id" : "minecraft:polished_deepslate_stairs", - "blockRuntimeId" : 294 - }, - { - "id" : "minecraft:deepslate_brick_stairs", - "blockRuntimeId" : 7424 - }, - { - "id" : "minecraft:mud_brick_stairs", - "blockRuntimeId" : 5524 - }, - { - "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" : 229 - }, - { - "id" : "minecraft:spruce_trapdoor", - "blockRuntimeId" : 6554 - }, - { - "id" : "minecraft:birch_trapdoor", - "blockRuntimeId" : 6652 - }, - { - "id" : "minecraft:jungle_trapdoor", - "blockRuntimeId" : 5383 - }, - { - "id" : "minecraft:acacia_trapdoor", - "blockRuntimeId" : 5591 - }, - { - "id" : "minecraft:dark_oak_trapdoor", - "blockRuntimeId" : 7504 - }, - { - "id" : "minecraft:mangrove_trapdoor", - "blockRuntimeId" : 4487 - }, - { - "id" : "minecraft:iron_trapdoor", - "blockRuntimeId" : 321 - }, - { - "id" : "minecraft:crimson_trapdoor", - "blockRuntimeId" : 4335 - }, - { - "id" : "minecraft:warped_trapdoor", - "blockRuntimeId" : 4735 - }, - { - "id" : "minecraft:iron_bars", - "blockRuntimeId" : 4803 - }, - { - "id" : "minecraft:glass", - "blockRuntimeId" : 6166 - }, - { - "id" : "minecraft:stained_glass", - "blockRuntimeId" : 1135 - }, - { - "id" : "minecraft:stained_glass", - "blockRuntimeId" : 1143 - }, - { - "id" : "minecraft:stained_glass", - "blockRuntimeId" : 1142 - }, - { - "id" : "minecraft:stained_glass", - "blockRuntimeId" : 1150 - }, - { - "id" : "minecraft:stained_glass", - "blockRuntimeId" : 1147 - }, - { - "id" : "minecraft:stained_glass", - "blockRuntimeId" : 1149 - }, - { - "id" : "minecraft:stained_glass", - "blockRuntimeId" : 1136 - }, - { - "id" : "minecraft:stained_glass", - "blockRuntimeId" : 1139 - }, - { - "id" : "minecraft:stained_glass", - "blockRuntimeId" : 1140 - }, - { - "id" : "minecraft:stained_glass", - "blockRuntimeId" : 1148 - }, - { - "id" : "minecraft:stained_glass", - "blockRuntimeId" : 1144 - }, - { - "id" : "minecraft:stained_glass", - "blockRuntimeId" : 1138 - }, - { - "id" : "minecraft:stained_glass", - "blockRuntimeId" : 1146 - }, - { - "id" : "minecraft:stained_glass", - "blockRuntimeId" : 1145 - }, - { - "id" : "minecraft:stained_glass", - "blockRuntimeId" : 1137 - }, - { - "id" : "minecraft:stained_glass", - "blockRuntimeId" : 1141 - }, - { - "id" : "minecraft:tinted_glass", - "blockRuntimeId" : 5977 - }, - { - "id" : "minecraft:glass_pane", - "blockRuntimeId" : 5235 - }, - { - "id" : "minecraft:stained_glass_pane", - "blockRuntimeId" : 4854 - }, - { - "id" : "minecraft:stained_glass_pane", - "blockRuntimeId" : 4862 - }, - { - "id" : "minecraft:stained_glass_pane", - "blockRuntimeId" : 4861 - }, - { - "id" : "minecraft:stained_glass_pane", - "blockRuntimeId" : 4869 - }, - { - "id" : "minecraft:stained_glass_pane", - "blockRuntimeId" : 4866 - }, - { - "id" : "minecraft:stained_glass_pane", - "blockRuntimeId" : 4868 - }, - { - "id" : "minecraft:stained_glass_pane", - "blockRuntimeId" : 4855 - }, - { - "id" : "minecraft:stained_glass_pane", - "blockRuntimeId" : 4858 - }, - { - "id" : "minecraft:stained_glass_pane", - "blockRuntimeId" : 4859 - }, - { - "id" : "minecraft:stained_glass_pane", - "blockRuntimeId" : 4867 - }, - { - "id" : "minecraft:stained_glass_pane", - "blockRuntimeId" : 4863 - }, - { - "id" : "minecraft:stained_glass_pane", - "blockRuntimeId" : 4857 - }, - { - "id" : "minecraft:stained_glass_pane", - "blockRuntimeId" : 4865 - }, - { - "id" : "minecraft:stained_glass_pane", - "blockRuntimeId" : 4864 - }, - { - "id" : "minecraft:stained_glass_pane", - "blockRuntimeId" : 4856 - }, - { - "id" : "minecraft:stained_glass_pane", - "blockRuntimeId" : 4860 - }, - { - "id" : "minecraft:ladder", - "blockRuntimeId" : 8264 - }, - { - "id" : "minecraft:scaffolding", - "blockRuntimeId" : 3573 - }, - { - "id" : "minecraft:stone_block_slab", - "blockRuntimeId" : 4272 - }, - { - "id" : "minecraft:stone_block_slab4", - "blockRuntimeId" : 5824 - }, - { - "id" : "minecraft:stone_block_slab", - "blockRuntimeId" : 4275 - }, - { - "id" : "minecraft:stone_block_slab2", - "blockRuntimeId" : 5795 - }, - { - "id" : "minecraft:wooden_slab", - "blockRuntimeId" : 5272 - }, - { - "id" : "minecraft:wooden_slab", - "blockRuntimeId" : 5273 - }, - { - "id" : "minecraft:wooden_slab", - "blockRuntimeId" : 5274 - }, - { - "id" : "minecraft:wooden_slab", - "blockRuntimeId" : 5275 - }, - { - "id" : "minecraft:wooden_slab", - "blockRuntimeId" : 5276 - }, - { - "id" : "minecraft:wooden_slab", - "blockRuntimeId" : 5277 - }, - { - "id" : "minecraft:mangrove_slab", - "blockRuntimeId" : 1151 - }, - { - "id" : "minecraft:stone_block_slab", - "blockRuntimeId" : 4277 - }, - { - "id" : "minecraft:stone_block_slab4", - "blockRuntimeId" : 5822 - }, - { - "id" : "minecraft:stone_block_slab", - "blockRuntimeId" : 4273 - }, - { - "id" : "minecraft:stone_block_slab4", - "blockRuntimeId" : 5825 - }, - { - "id" : "minecraft:stone_block_slab2", - "blockRuntimeId" : 5796 - }, - { - "id" : "minecraft:stone_block_slab2", - "blockRuntimeId" : 5790 - }, - { - "id" : "minecraft:stone_block_slab4", - "blockRuntimeId" : 5826 - }, - { - "id" : "minecraft:stone_block_slab3", - "blockRuntimeId" : 5807 - }, - { - "id" : "minecraft:stone_block_slab3", - "blockRuntimeId" : 5812 - }, - { - "id" : "minecraft:stone_block_slab3", - "blockRuntimeId" : 5813 - }, - { - "id" : "minecraft:stone_block_slab3", - "blockRuntimeId" : 5810 - }, - { - "id" : "minecraft:stone_block_slab3", - "blockRuntimeId" : 5811 - }, - { - "id" : "minecraft:stone_block_slab3", - "blockRuntimeId" : 5809 - }, - { - "id" : "minecraft:stone_block_slab3", - "blockRuntimeId" : 5808 - }, - { - "id" : "minecraft:stone_block_slab", - "blockRuntimeId" : 4276 - }, - { - "id" : "minecraft:stone_block_slab", - "blockRuntimeId" : 4279 - }, - { - "id" : "minecraft:stone_block_slab2", - "blockRuntimeId" : 5797 - }, - { - "id" : "minecraft:stone_block_slab3", - "blockRuntimeId" : 5806 - }, - { - "id" : "minecraft:stone_block_slab", - "blockRuntimeId" : 4278 - }, - { - "id" : "minecraft:stone_block_slab4", - "blockRuntimeId" : 5823 - }, - { - "id" : "minecraft:stone_block_slab2", - "blockRuntimeId" : 5791 - }, - { - "id" : "minecraft:stone_block_slab2", - "blockRuntimeId" : 5792 - }, - { - "id" : "minecraft:stone_block_slab2", - "blockRuntimeId" : 5793 - }, - { - "id" : "minecraft:stone_block_slab2", - "blockRuntimeId" : 5794 - }, - { - "id" : "minecraft:crimson_slab", - "blockRuntimeId" : 5902 - }, - { - "id" : "minecraft:warped_slab", - "blockRuntimeId" : 6486 - }, - { - "id" : "minecraft:blackstone_slab", - "blockRuntimeId" : 912 - }, - { - "id" : "minecraft:polished_blackstone_slab", - "blockRuntimeId" : 6020 - }, - { - "id" : "minecraft:polished_blackstone_brick_slab", - "blockRuntimeId" : 4194 - }, - { - "id" : "minecraft:cut_copper_slab", - "blockRuntimeId" : 5237 - }, - { - "id" : "minecraft:exposed_cut_copper_slab", - "blockRuntimeId" : 6602 - }, - { - "id" : "minecraft:weathered_cut_copper_slab", - "blockRuntimeId" : 6055 - }, - { - "id" : "minecraft:oxidized_cut_copper_slab", - "blockRuntimeId" : 5284 - }, - { - "id" : "minecraft:waxed_cut_copper_slab", - "blockRuntimeId" : 7817 - }, - { - "id" : "minecraft:waxed_exposed_cut_copper_slab", - "blockRuntimeId" : 249 - }, - { - "id" : "minecraft:waxed_weathered_cut_copper_slab", - "blockRuntimeId" : 6547 - }, - { - "id" : "minecraft:waxed_oxidized_cut_copper_slab", - "blockRuntimeId" : 710 - }, - { - "id" : "minecraft:cobbled_deepslate_slab", - "blockRuntimeId" : 7312 - }, - { - "id" : "minecraft:polished_deepslate_slab", - "blockRuntimeId" : 288 - }, - { - "id" : "minecraft:deepslate_tile_slab", - "blockRuntimeId" : 4293 - }, - { - "id" : "minecraft:deepslate_brick_slab", - "blockRuntimeId" : 3718 - }, - { - "id" : "minecraft:mud_brick_slab", - "blockRuntimeId" : 3912 - }, - { - "id" : "minecraft:brick_block", - "blockRuntimeId" : 4767 - }, - { - "id" : "minecraft:chiseled_nether_bricks", - "blockRuntimeId" : 7251 - }, - { - "id" : "minecraft:cracked_nether_bricks", - "blockRuntimeId" : 4554 - }, - { - "id" : "minecraft:quartz_bricks", - "blockRuntimeId" : 6353 - }, - { - "id" : "minecraft:stonebrick", - "blockRuntimeId" : 6549 - }, - { - "id" : "minecraft:stonebrick", - "blockRuntimeId" : 6550 - }, - { - "id" : "minecraft:stonebrick", - "blockRuntimeId" : 6551 - }, - { - "id" : "minecraft:stonebrick", - "blockRuntimeId" : 6552 - }, - { - "id" : "minecraft:end_bricks", - "blockRuntimeId" : 281 - }, - { - "id" : "minecraft:prismarine", - "blockRuntimeId" : 6089 - }, - { - "id" : "minecraft:polished_blackstone_bricks", - "blockRuntimeId" : 4682 - }, - { - "id" : "minecraft:cracked_polished_blackstone_bricks", - "blockRuntimeId" : 7216 - }, - { - "id" : "minecraft:gilded_blackstone", - "blockRuntimeId" : 4588 - }, - { - "id" : "minecraft:chiseled_polished_blackstone", - "blockRuntimeId" : 5064 - }, - { - "id" : "minecraft:deepslate_tiles", - "blockRuntimeId" : 4583 - }, - { - "id" : "minecraft:cracked_deepslate_tiles", - "blockRuntimeId" : 4162 - }, - { - "id" : "minecraft:deepslate_bricks", - "blockRuntimeId" : 5466 - }, - { - "id" : "minecraft:cracked_deepslate_bricks", - "blockRuntimeId" : 5366 - }, - { - "id" : "minecraft:chiseled_deepslate", - "blockRuntimeId" : 5236 - }, - { - "id" : "minecraft:cobblestone", - "blockRuntimeId" : 3617 - }, - { - "id" : "minecraft:mossy_cobblestone", - "blockRuntimeId" : 252 - }, - { - "id" : "minecraft:cobbled_deepslate", - "blockRuntimeId" : 6672 - }, - { - "id" : "minecraft:smooth_stone", - "blockRuntimeId" : 4584 - }, - { - "id" : "minecraft:sandstone", - "blockRuntimeId" : 3655 - }, - { - "id" : "minecraft:sandstone", - "blockRuntimeId" : 3656 - }, - { - "id" : "minecraft:sandstone", - "blockRuntimeId" : 3657 - }, - { - "id" : "minecraft:sandstone", - "blockRuntimeId" : 3658 - }, - { - "id" : "minecraft:red_sandstone", - "blockRuntimeId" : 6582 - }, - { - "id" : "minecraft:red_sandstone", - "blockRuntimeId" : 6583 - }, - { - "id" : "minecraft:red_sandstone", - "blockRuntimeId" : 6584 - }, - { - "id" : "minecraft:red_sandstone", - "blockRuntimeId" : 6585 - }, - { - "id" : "minecraft:coal_block", - "blockRuntimeId" : 5400 - }, - { - "id" : "minecraft:dried_kelp_block", - "blockRuntimeId" : 7981 - }, - { - "id" : "minecraft:gold_block", - "blockRuntimeId" : 291 - }, - { - "id" : "minecraft:iron_block", - "blockRuntimeId" : 8263 - }, - { - "id" : "minecraft:copper_block", - "blockRuntimeId" : 4653 - }, - { - "id" : "minecraft:exposed_copper", - "blockRuntimeId" : 595 - }, - { - "id" : "minecraft:weathered_copper", - "blockRuntimeId" : 8248 - }, - { - "id" : "minecraft:oxidized_copper", - "blockRuntimeId" : 3555 - }, - { - "id" : "minecraft:waxed_copper", - "blockRuntimeId" : 7736 - }, - { - "id" : "minecraft:waxed_exposed_copper", - "blockRuntimeId" : 696 - }, - { - "id" : "minecraft:waxed_weathered_copper", - "blockRuntimeId" : 709 - }, - { - "id" : "minecraft:waxed_oxidized_copper", - "blockRuntimeId" : 7544 - }, - { - "id" : "minecraft:cut_copper", - "blockRuntimeId" : 4691 - }, - { - "id" : "minecraft:exposed_cut_copper", - "blockRuntimeId" : 6168 - }, - { - "id" : "minecraft:weathered_cut_copper", - "blockRuntimeId" : 7199 - }, - { - "id" : "minecraft:oxidized_cut_copper", - "blockRuntimeId" : 5480 - }, - { - "id" : "minecraft:waxed_cut_copper", - "blockRuntimeId" : 7295 - }, - { - "id" : "minecraft:waxed_exposed_cut_copper", - "blockRuntimeId" : 3811 - }, - { - "id" : "minecraft:waxed_weathered_cut_copper", - "blockRuntimeId" : 4853 - }, - { - "id" : "minecraft:waxed_oxidized_cut_copper", - "blockRuntimeId" : 214 - }, - { - "id" : "minecraft:emerald_block", - "blockRuntimeId" : 1161 - }, - { - "id" : "minecraft:diamond_block", - "blockRuntimeId" : 272 - }, - { - "id" : "minecraft:lapis_block", - "blockRuntimeId" : 4288 - }, - { - "id" : "minecraft:raw_iron_block", - "blockRuntimeId" : 8262 - }, - { - "id" : "minecraft:raw_copper_block", - "blockRuntimeId" : 5271 - }, - { - "id" : "minecraft:raw_gold_block", - "blockRuntimeId" : 363 - }, - { - "id" : "minecraft:quartz_block", - "blockRuntimeId" : 3698 - }, - { - "id" : "minecraft:quartz_block", - "blockRuntimeId" : 3700 - }, - { - "id" : "minecraft:quartz_block", - "blockRuntimeId" : 3699 - }, - { - "id" : "minecraft:quartz_block", - "blockRuntimeId" : 3701 - }, - { - "id" : "minecraft:prismarine", - "blockRuntimeId" : 6087 - }, - { - "id" : "minecraft:prismarine", - "blockRuntimeId" : 6088 - }, - { - "id" : "minecraft:slime", - "blockRuntimeId" : 4235 - }, - { - "id" : "minecraft:honey_block", - "blockRuntimeId" : 894 - }, - { - "id" : "minecraft:honeycomb_block", - "blockRuntimeId" : 4478 - }, - { - "id" : "minecraft:hay_block", - "blockRuntimeId" : 697 - }, - { - "id" : "minecraft:bone_block", - "blockRuntimeId" : 4236 - }, - { - "id" : "minecraft:nether_brick", - "blockRuntimeId" : 7274 - }, - { - "id" : "minecraft:red_nether_brick", - "blockRuntimeId" : 146 - }, - { - "id" : "minecraft:netherite_block", - "blockRuntimeId" : 3777 - }, - { - "id" : "minecraft:lodestone", - "blockRuntimeId" : 8261 - }, - { - "id" : "minecraft:wool", - "blockRuntimeId" : 3460 - }, - { - "id" : "minecraft:wool", - "blockRuntimeId" : 3468 - }, - { - "id" : "minecraft:wool", - "blockRuntimeId" : 3467 - }, - { - "id" : "minecraft:wool", - "blockRuntimeId" : 3475 - }, - { - "id" : "minecraft:wool", - "blockRuntimeId" : 3472 - }, - { - "id" : "minecraft:wool", - "blockRuntimeId" : 3474 - }, - { - "id" : "minecraft:wool", - "blockRuntimeId" : 3461 - }, - { - "id" : "minecraft:wool", - "blockRuntimeId" : 3464 - }, - { - "id" : "minecraft:wool", - "blockRuntimeId" : 3465 - }, - { - "id" : "minecraft:wool", - "blockRuntimeId" : 3473 - }, - { - "id" : "minecraft:wool", - "blockRuntimeId" : 3469 - }, - { - "id" : "minecraft:wool", - "blockRuntimeId" : 3463 - }, - { - "id" : "minecraft:wool", - "blockRuntimeId" : 3471 - }, - { - "id" : "minecraft:wool", - "blockRuntimeId" : 3470 - }, - { - "id" : "minecraft:wool", - "blockRuntimeId" : 3462 - }, - { - "id" : "minecraft:wool", - "blockRuntimeId" : 3466 - }, - { - "id" : "minecraft:carpet", - "blockRuntimeId" : 951 - }, - { - "id" : "minecraft:carpet", - "blockRuntimeId" : 959 - }, - { - "id" : "minecraft:carpet", - "blockRuntimeId" : 958 - }, - { - "id" : "minecraft:carpet", - "blockRuntimeId" : 966 - }, - { - "id" : "minecraft:carpet", - "blockRuntimeId" : 963 - }, - { - "id" : "minecraft:carpet", - "blockRuntimeId" : 965 - }, - { - "id" : "minecraft:carpet", - "blockRuntimeId" : 952 - }, - { - "id" : "minecraft:carpet", - "blockRuntimeId" : 955 - }, - { - "id" : "minecraft:carpet", - "blockRuntimeId" : 956 - }, - { - "id" : "minecraft:carpet", - "blockRuntimeId" : 964 - }, - { - "id" : "minecraft:carpet", - "blockRuntimeId" : 960 - }, - { - "id" : "minecraft:carpet", - "blockRuntimeId" : 954 - }, - { - "id" : "minecraft:carpet", - "blockRuntimeId" : 962 - }, - { - "id" : "minecraft:carpet", - "blockRuntimeId" : 961 - }, - { - "id" : "minecraft:carpet", - "blockRuntimeId" : 953 - }, - { - "id" : "minecraft:carpet", - "blockRuntimeId" : 957 - }, - { - "id" : "minecraft:concrete_powder", - "blockRuntimeId" : 6266 - }, - { - "id" : "minecraft:concrete_powder", - "blockRuntimeId" : 6274 - }, - { - "id" : "minecraft:concrete_powder", - "blockRuntimeId" : 6273 - }, - { - "id" : "minecraft:concrete_powder", - "blockRuntimeId" : 6281 - }, - { - "id" : "minecraft:concrete_powder", - "blockRuntimeId" : 6278 - }, - { - "id" : "minecraft:concrete_powder", - "blockRuntimeId" : 6280 - }, - { - "id" : "minecraft:concrete_powder", - "blockRuntimeId" : 6267 - }, - { - "id" : "minecraft:concrete_powder", - "blockRuntimeId" : 6270 - }, - { - "id" : "minecraft:concrete_powder", - "blockRuntimeId" : 6271 - }, - { - "id" : "minecraft:concrete_powder", - "blockRuntimeId" : 6279 - }, - { - "id" : "minecraft:concrete_powder", - "blockRuntimeId" : 6275 - }, - { - "id" : "minecraft:concrete_powder", - "blockRuntimeId" : 6269 - }, - { - "id" : "minecraft:concrete_powder", - "blockRuntimeId" : 6277 - }, - { - "id" : "minecraft:concrete_powder", - "blockRuntimeId" : 6276 - }, - { - "id" : "minecraft:concrete_powder", - "blockRuntimeId" : 6268 - }, - { - "id" : "minecraft:concrete_powder", - "blockRuntimeId" : 6272 - }, - { - "id" : "minecraft:concrete", - "blockRuntimeId" : 662 - }, - { - "id" : "minecraft:concrete", - "blockRuntimeId" : 670 - }, - { - "id" : "minecraft:concrete", - "blockRuntimeId" : 669 - }, - { - "id" : "minecraft:concrete", - "blockRuntimeId" : 677 - }, - { - "id" : "minecraft:concrete", - "blockRuntimeId" : 674 - }, - { - "id" : "minecraft:concrete", - "blockRuntimeId" : 676 - }, - { - "id" : "minecraft:concrete", - "blockRuntimeId" : 663 - }, - { - "id" : "minecraft:concrete", - "blockRuntimeId" : 666 - }, - { - "id" : "minecraft:concrete", - "blockRuntimeId" : 667 - }, - { - "id" : "minecraft:concrete", - "blockRuntimeId" : 675 - }, - { - "id" : "minecraft:concrete", - "blockRuntimeId" : 671 - }, - { - "id" : "minecraft:concrete", - "blockRuntimeId" : 665 - }, - { - "id" : "minecraft:concrete", - "blockRuntimeId" : 673 - }, - { - "id" : "minecraft:concrete", - "blockRuntimeId" : 672 - }, - { - "id" : "minecraft:concrete", - "blockRuntimeId" : 664 - }, - { - "id" : "minecraft:concrete", - "blockRuntimeId" : 668 - }, - { - "id" : "minecraft:clay", - "blockRuntimeId" : 7126 - }, - { - "id" : "minecraft:hardened_clay", - "blockRuntimeId" : 643 - }, - { - "id" : "minecraft:stained_hardened_clay", - "blockRuntimeId" : 6178 - }, - { - "id" : "minecraft:stained_hardened_clay", - "blockRuntimeId" : 6186 - }, - { - "id" : "minecraft:stained_hardened_clay", - "blockRuntimeId" : 6185 - }, - { - "id" : "minecraft:stained_hardened_clay", - "blockRuntimeId" : 6193 - }, - { - "id" : "minecraft:stained_hardened_clay", - "blockRuntimeId" : 6190 - }, - { - "id" : "minecraft:stained_hardened_clay", - "blockRuntimeId" : 6192 - }, - { - "id" : "minecraft:stained_hardened_clay", - "blockRuntimeId" : 6179 - }, - { - "id" : "minecraft:stained_hardened_clay", - "blockRuntimeId" : 6182 - }, - { - "id" : "minecraft:stained_hardened_clay", - "blockRuntimeId" : 6183 - }, - { - "id" : "minecraft:stained_hardened_clay", - "blockRuntimeId" : 6191 - }, - { - "id" : "minecraft:stained_hardened_clay", - "blockRuntimeId" : 6187 - }, - { - "id" : "minecraft:stained_hardened_clay", - "blockRuntimeId" : 6181 - }, - { - "id" : "minecraft:stained_hardened_clay", - "blockRuntimeId" : 6189 - }, - { - "id" : "minecraft:stained_hardened_clay", - "blockRuntimeId" : 6188 - }, - { - "id" : "minecraft:stained_hardened_clay", - "blockRuntimeId" : 6180 - }, - { - "id" : "minecraft:stained_hardened_clay", - "blockRuntimeId" : 6184 - }, - { - "id" : "minecraft:white_glazed_terracotta", - "blockRuntimeId" : 5575 - }, - { - "id" : "minecraft:silver_glazed_terracotta", - "blockRuntimeId" : 3533 - }, - { - "id" : "minecraft:gray_glazed_terracotta", - "blockRuntimeId" : 8255 - }, - { - "id" : "minecraft:black_glazed_terracotta", - "blockRuntimeId" : 5836 - }, - { - "id" : "minecraft:brown_glazed_terracotta", - "blockRuntimeId" : 3549 - }, - { - "id" : "minecraft:red_glazed_terracotta", - "blockRuntimeId" : 4169 - }, - { - "id" : "minecraft:orange_glazed_terracotta", - "blockRuntimeId" : 1153 - }, - { - "id" : "minecraft:yellow_glazed_terracotta", - "blockRuntimeId" : 915 - }, - { - "id" : "minecraft:lime_glazed_terracotta", - "blockRuntimeId" : 223 - }, - { - "id" : "minecraft:green_glazed_terracotta", - "blockRuntimeId" : 6612 - }, - { - "id" : "minecraft:cyan_glazed_terracotta", - "blockRuntimeId" : 5360 - }, - { - "id" : "minecraft:light_blue_glazed_terracotta", - "blockRuntimeId" : 5473 - }, - { - "id" : "minecraft:blue_glazed_terracotta", - "blockRuntimeId" : 5467 - }, - { - "id" : "minecraft:purple_glazed_terracotta", - "blockRuntimeId" : 7013 - }, - { - "id" : "minecraft:magenta_glazed_terracotta", - "blockRuntimeId" : 967 - }, - { - "id" : "minecraft:pink_glazed_terracotta", - "blockRuntimeId" : 6541 - }, - { - "id" : "minecraft:purpur_block", - "blockRuntimeId" : 7716 - }, - { - "id" : "minecraft:purpur_block", - "blockRuntimeId" : 7718 - }, - { - "id" : "minecraft:packed_mud", - "blockRuntimeId" : 283 - }, - { - "id" : "minecraft:mud_bricks", - "blockRuntimeId" : 6891 - }, - { - "id" : "minecraft:nether_wart_block", - "blockRuntimeId" : 4295 - }, - { - "id" : "minecraft:warped_wart_block", - "blockRuntimeId" : 5907 - }, - { - "id" : "minecraft:shroomlight", - "blockRuntimeId" : 5063 - }, - { - "id" : "minecraft:crimson_nylium", - "blockRuntimeId" : 4191 - }, - { - "id" : "minecraft:warped_nylium", - "blockRuntimeId" : 6351 - }, - { - "id" : "minecraft:basalt", - "blockRuntimeId" : 4351 - }, - { - "id" : "minecraft:polished_basalt", - "blockRuntimeId" : 24 - }, - { - "id" : "minecraft:smooth_basalt", - "blockRuntimeId" : 1159 - }, - { - "id" : "minecraft:soul_soil", - "blockRuntimeId" : 5832 - }, - { - "id" : "minecraft:dirt", - "blockRuntimeId" : 5753 - }, - { - "id" : "minecraft:dirt", - "blockRuntimeId" : 5754 - }, - { - "id" : "minecraft:farmland", - "blockRuntimeId" : 3914 - }, - { - "id" : "minecraft:grass", - "blockRuntimeId" : 6977 - }, - { - "id" : "minecraft:grass_path", - "blockRuntimeId" : 8083 - }, - { - "id" : "minecraft:podzol", - "blockRuntimeId" : 4652 - }, - { - "id" : "minecraft:mycelium", - "blockRuntimeId" : 3685 - }, - { - "id" : "minecraft:mud", - "blockRuntimeId" : 6686 - }, - { - "id" : "minecraft:stone", - "blockRuntimeId" : 655 - }, - { - "id" : "minecraft:iron_ore", - "blockRuntimeId" : 4692 - }, - { - "id" : "minecraft:gold_ore", - "blockRuntimeId" : 914 - }, - { - "id" : "minecraft:diamond_ore", - "blockRuntimeId" : 4363 - }, - { - "id" : "minecraft:lapis_ore", - "blockRuntimeId" : 7701 - }, - { - "id" : "minecraft:redstone_ore", - "blockRuntimeId" : 4291 - }, - { - "id" : "minecraft:coal_ore", - "blockRuntimeId" : 4289 - }, - { - "id" : "minecraft:copper_ore", - "blockRuntimeId" : 3556 - }, - { - "id" : "minecraft:emerald_ore", - "blockRuntimeId" : 7349 - }, - { - "id" : "minecraft:quartz_ore", - "blockRuntimeId" : 4503 - }, - { - "id" : "minecraft:nether_gold_ore", - "blockRuntimeId" : 27 - }, - { - "id" : "minecraft:ancient_debris", - "blockRuntimeId" : 6109 - }, - { - "id" : "minecraft:deepslate_iron_ore", - "blockRuntimeId" : 7275 - }, - { - "id" : "minecraft:deepslate_gold_ore", - "blockRuntimeId" : 6108 - }, - { - "id" : "minecraft:deepslate_diamond_ore", - "blockRuntimeId" : 8040 - }, - { - "id" : "minecraft:deepslate_lapis_ore", - "blockRuntimeId" : 7264 - }, - { - "id" : "minecraft:deepslate_redstone_ore", - "blockRuntimeId" : 6618 - }, - { - "id" : "minecraft:deepslate_emerald_ore", - "blockRuntimeId" : 6352 - }, - { - "id" : "minecraft:deepslate_coal_ore", - "blockRuntimeId" : 7198 - }, - { - "id" : "minecraft:deepslate_copper_ore", - "blockRuntimeId" : 105 - }, - { - "id" : "minecraft:gravel", - "blockRuntimeId" : 8289 - }, - { - "id" : "minecraft:stone", - "blockRuntimeId" : 656 - }, - { - "id" : "minecraft:stone", - "blockRuntimeId" : 658 - }, - { - "id" : "minecraft:stone", - "blockRuntimeId" : 660 - }, - { - "id" : "minecraft:blackstone", - "blockRuntimeId" : 7587 - }, - { - "id" : "minecraft:deepslate", - "blockRuntimeId" : 253 - }, - { - "id" : "minecraft:stone", - "blockRuntimeId" : 657 - }, - { - "id" : "minecraft:stone", - "blockRuntimeId" : 659 - }, - { - "id" : "minecraft:stone", - "blockRuntimeId" : 661 - }, - { - "id" : "minecraft:polished_blackstone", - "blockRuntimeId" : 3684 - }, - { - "id" : "minecraft:polished_deepslate", - "blockRuntimeId" : 7756 - }, - { - "id" : "minecraft:sand", - "blockRuntimeId" : 4197 - }, - { - "id" : "minecraft:sand", - "blockRuntimeId" : 4198 - }, - { - "id" : "minecraft:cactus", - "blockRuntimeId" : 6988 - }, - { - "id" : "minecraft:log", - "blockRuntimeId" : 6674 - }, - { - "id" : "minecraft:stripped_oak_log", - "blockRuntimeId" : 7545 - }, - { - "id" : "minecraft:log", - "blockRuntimeId" : 6675 - }, - { - "id" : "minecraft:stripped_spruce_log", - "blockRuntimeId" : 6290 - }, - { - "id" : "minecraft:log", - "blockRuntimeId" : 6676 - }, - { - "id" : "minecraft:stripped_birch_log", - "blockRuntimeId" : 5974 - }, - { - "id" : "minecraft:log", - "blockRuntimeId" : 6677 - }, - { - "id" : "minecraft:stripped_jungle_log", - "blockRuntimeId" : 644 - }, - { - "id" : "minecraft:log2", - "blockRuntimeId" : 3832 - }, - { - "id" : "minecraft:stripped_acacia_log", - "blockRuntimeId" : 5850 - }, - { - "id" : "minecraft:log2", - "blockRuntimeId" : 3833 - }, - { - "id" : "minecraft:stripped_dark_oak_log", - "blockRuntimeId" : 216 - }, - { - "id" : "minecraft:mangrove_log", - "blockRuntimeId" : 350 - }, - { - "id" : "minecraft:stripped_mangrove_log", - "blockRuntimeId" : 8286 - }, - { - "id" : "minecraft:crimson_stem", - "blockRuntimeId" : 5899 - }, - { - "id" : "minecraft:stripped_crimson_stem", - "blockRuntimeId" : 6950 - }, - { - "id" : "minecraft:warped_stem", - "blockRuntimeId" : 6488 - }, - { - "id" : "minecraft:stripped_warped_stem", - "blockRuntimeId" : 7402 - }, - { - "id" : "minecraft:wood", - "blockRuntimeId" : 3476 - }, - { - "id" : "minecraft:wood", - "blockRuntimeId" : 3482 - }, - { - "id" : "minecraft:wood", - "blockRuntimeId" : 3477 - }, - { - "id" : "minecraft:wood", - "blockRuntimeId" : 3483 - }, - { - "id" : "minecraft:wood", - "blockRuntimeId" : 3478 - }, - { - "id" : "minecraft:wood", - "blockRuntimeId" : 3484 - }, - { - "id" : "minecraft:wood", - "blockRuntimeId" : 3479 - }, - { - "id" : "minecraft:wood", - "blockRuntimeId" : 3485 - }, - { - "id" : "minecraft:wood", - "blockRuntimeId" : 3480 - }, - { - "id" : "minecraft:wood", - "blockRuntimeId" : 3486 - }, - { - "id" : "minecraft:wood", - "blockRuntimeId" : 3481 - }, - { - "id" : "minecraft:wood", - "blockRuntimeId" : 3487 - }, - { - "id" : "minecraft:mangrove_wood", - "blockRuntimeId" : 4163 - }, - { - "id" : "minecraft:stripped_mangrove_wood", - "blockRuntimeId" : 4231 - }, - { - "id" : "minecraft:crimson_hyphae", - "blockRuntimeId" : 4296 - }, - { - "id" : "minecraft:stripped_crimson_hyphae", - "blockRuntimeId" : 6501 - }, - { - "id" : "minecraft:warped_hyphae", - "blockRuntimeId" : 5904 - }, - { - "id" : "minecraft:stripped_warped_hyphae", - "blockRuntimeId" : 5581 - }, - { - "id" : "minecraft:leaves", - "blockRuntimeId" : 6092 - }, - { - "id" : "minecraft:leaves", - "blockRuntimeId" : 6093 - }, - { - "id" : "minecraft:leaves", - "blockRuntimeId" : 6094 - }, - { - "id" : "minecraft:leaves", - "blockRuntimeId" : 6095 - }, - { - "id" : "minecraft:leaves2", - "blockRuntimeId" : 4355 - }, - { - "id" : "minecraft:leaves2", - "blockRuntimeId" : 4356 - }, - { - "id" : "minecraft:mangrove_leaves", - "blockRuntimeId" : 6668 - }, - { - "id" : "minecraft:azalea_leaves", - "blockRuntimeId" : 7712 - }, - { - "id" : "minecraft:azalea_leaves_flowered", - "blockRuntimeId" : 6341 - }, - { - "id" : "minecraft:sapling", - "blockRuntimeId" : 714 - }, - { - "id" : "minecraft:sapling", - "blockRuntimeId" : 715 - }, - { - "id" : "minecraft:sapling", - "blockRuntimeId" : 716 - }, - { - "id" : "minecraft:sapling", - "blockRuntimeId" : 717 - }, - { - "id" : "minecraft:sapling", - "blockRuntimeId" : 718 - }, - { - "id" : "minecraft:sapling", - "blockRuntimeId" : 719 - }, - { - "id" : "minecraft:mangrove_propagule", - "blockRuntimeId" : 6978 - }, - { - "id" : "minecraft:bee_nest", - "blockRuntimeId" : 5756 - }, - { - "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" : 394 - }, - { - "id" : "minecraft:melon_slice" - }, - { - "id" : "minecraft:glistering_melon_slice" - }, - { - "id" : "minecraft:sweet_berries" - }, - { - "id" : "minecraft:glow_berries" - }, - { - "id" : "minecraft:pumpkin", - "blockRuntimeId" : 4579 - }, - { - "id" : "minecraft:carved_pumpkin", - "blockRuntimeId" : 7380 - }, - { - "id" : "minecraft:lit_pumpkin", - "blockRuntimeId" : 6687 - }, - { - "id" : "minecraft:honeycomb" - }, - { - "id" : "minecraft:tallgrass", - "blockRuntimeId" : 931 - }, - { - "id" : "minecraft:double_plant", - "blockRuntimeId" : 5457 - }, - { - "id" : "minecraft:tallgrass", - "blockRuntimeId" : 930 - }, - { - "id" : "minecraft:double_plant", - "blockRuntimeId" : 5456 - }, - { - "id" : "minecraft:nether_sprouts" - }, - { - "id" : "minecraft:coral", - "blockRuntimeId" : 6494 - }, - { - "id" : "minecraft:coral", - "blockRuntimeId" : 6492 - }, - { - "id" : "minecraft:coral", - "blockRuntimeId" : 6493 - }, - { - "id" : "minecraft:coral", - "blockRuntimeId" : 6491 - }, - { - "id" : "minecraft:coral", - "blockRuntimeId" : 6495 - }, - { - "id" : "minecraft:coral", - "blockRuntimeId" : 6499 - }, - { - "id" : "minecraft:coral", - "blockRuntimeId" : 6497 - }, - { - "id" : "minecraft:coral", - "blockRuntimeId" : 6498 - }, - { - "id" : "minecraft:coral", - "blockRuntimeId" : 6496 - }, - { - "id" : "minecraft:coral", - "blockRuntimeId" : 6500 - }, - { - "id" : "minecraft:coral_fan", - "blockRuntimeId" : 4618 - }, - { - "id" : "minecraft:coral_fan", - "blockRuntimeId" : 4616 - }, - { - "id" : "minecraft:coral_fan", - "blockRuntimeId" : 4617 - }, - { - "id" : "minecraft:coral_fan", - "blockRuntimeId" : 4615 - }, - { - "id" : "minecraft:coral_fan", - "blockRuntimeId" : 4619 - }, - { - "id" : "minecraft:coral_fan_dead", - "blockRuntimeId" : 69 - }, - { - "id" : "minecraft:coral_fan_dead", - "blockRuntimeId" : 67 - }, - { - "id" : "minecraft:coral_fan_dead", - "blockRuntimeId" : 68 - }, - { - "id" : "minecraft:coral_fan_dead", - "blockRuntimeId" : 66 - }, - { - "id" : "minecraft:coral_fan_dead", - "blockRuntimeId" : 70 - }, - { - "id" : "minecraft:kelp" - }, - { - "id" : "minecraft:seagrass", - "blockRuntimeId" : 246 - }, - { - "id" : "minecraft:crimson_roots", - "blockRuntimeId" : 7575 - }, - { - "id" : "minecraft:warped_roots", - "blockRuntimeId" : 4364 - }, - { - "id" : "minecraft:yellow_flower", - "blockRuntimeId" : 302 - }, - { - "id" : "minecraft:red_flower", - "blockRuntimeId" : 3618 - }, - { - "id" : "minecraft:red_flower", - "blockRuntimeId" : 3619 - }, - { - "id" : "minecraft:red_flower", - "blockRuntimeId" : 3620 - }, - { - "id" : "minecraft:red_flower", - "blockRuntimeId" : 3621 - }, - { - "id" : "minecraft:red_flower", - "blockRuntimeId" : 3622 - }, - { - "id" : "minecraft:red_flower", - "blockRuntimeId" : 3623 - }, - { - "id" : "minecraft:red_flower", - "blockRuntimeId" : 3624 - }, - { - "id" : "minecraft:red_flower", - "blockRuntimeId" : 3625 - }, - { - "id" : "minecraft:red_flower", - "blockRuntimeId" : 3626 - }, - { - "id" : "minecraft:red_flower", - "blockRuntimeId" : 3627 - }, - { - "id" : "minecraft:red_flower", - "blockRuntimeId" : 3628 - }, - { - "id" : "minecraft:double_plant", - "blockRuntimeId" : 5454 - }, - { - "id" : "minecraft:double_plant", - "blockRuntimeId" : 5455 - }, - { - "id" : "minecraft:double_plant", - "blockRuntimeId" : 5458 - }, - { - "id" : "minecraft:double_plant", - "blockRuntimeId" : 5459 - }, - { - "id" : "minecraft:wither_rose", - "blockRuntimeId" : 6167 - }, - { - "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" : 896 - }, - { - "id" : "minecraft:weeping_vines", - "blockRuntimeId" : 5481 - }, - { - "id" : "minecraft:twisting_vines", - "blockRuntimeId" : 5693 - }, - { - "id" : "minecraft:waterlily", - "blockRuntimeId" : 1160 - }, - { - "id" : "minecraft:deadbush", - "blockRuntimeId" : 4679 - }, - { - "id" : "minecraft:bamboo", - "blockRuntimeId" : 3686 - }, - { - "id" : "minecraft:snow", - "blockRuntimeId" : 4196 - }, - { - "id" : "minecraft:ice", - "blockRuntimeId" : 6691 - }, - { - "id" : "minecraft:packed_ice", - "blockRuntimeId" : 282 - }, - { - "id" : "minecraft:blue_ice", - "blockRuntimeId" : 7029 - }, - { - "id" : "minecraft:snow_layer", - "blockRuntimeId" : 155 - }, - { - "id" : "minecraft:pointed_dripstone", - "blockRuntimeId" : 7418 - }, - { - "id" : "minecraft:dripstone_block", - "blockRuntimeId" : 895 - }, - { - "id" : "minecraft:moss_carpet", - "blockRuntimeId" : 286 - }, - { - "id" : "minecraft:moss_block", - "blockRuntimeId" : 6540 - }, - { - "id" : "minecraft:dirt_with_roots", - "blockRuntimeId" : 5399 - }, - { - "id" : "minecraft:hanging_roots", - "blockRuntimeId" : 205 - }, - { - "id" : "minecraft:mangrove_roots", - "blockRuntimeId" : 6177 - }, - { - "id" : "minecraft:muddy_mangrove_roots", - "blockRuntimeId" : 345 - }, - { - "id" : "minecraft:big_dripleaf", - "blockRuntimeId" : 5982 - }, - { - "id" : "minecraft:small_dripleaf_block", - "blockRuntimeId" : 4322 - }, - { - "id" : "minecraft:spore_blossom", - "blockRuntimeId" : 7314 - }, - { - "id" : "minecraft:azalea", - "blockRuntimeId" : 6890 - }, - { - "id" : "minecraft:flowering_azalea", - "blockRuntimeId" : 5479 - }, - { - "id" : "minecraft:glow_lichen", - "blockRuntimeId" : 5686 - }, - { - "id" : "minecraft:amethyst_block", - "blockRuntimeId" : 290 - }, - { - "id" : "minecraft:budding_amethyst", - "blockRuntimeId" : 7004 - }, - { - "id" : "minecraft:amethyst_cluster", - "blockRuntimeId" : 7812 - }, - { - "id" : "minecraft:large_amethyst_bud", - "blockRuntimeId" : 4730 - }, - { - "id" : "minecraft:medium_amethyst_bud", - "blockRuntimeId" : 4378 - }, - { - "id" : "minecraft:small_amethyst_bud", - "blockRuntimeId" : 304 - }, - { - "id" : "minecraft:tuff", - "blockRuntimeId" : 349 - }, - { - "id" : "minecraft:calcite", - "blockRuntimeId" : 215 - }, - { - "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" : 3548 - }, - { - "id" : "minecraft:red_mushroom", - "blockRuntimeId" : 4587 - }, - { - "id" : "minecraft:crimson_fungus", - "blockRuntimeId" : 7755 - }, - { - "id" : "minecraft:warped_fungus", - "blockRuntimeId" : 287 - }, - { - "id" : "minecraft:brown_mushroom_block", - "blockRuntimeId" : 7364 - }, - { - "id" : "minecraft:red_mushroom_block", - "blockRuntimeId" : 3613 - }, - { - "id" : "minecraft:brown_mushroom_block", - "blockRuntimeId" : 7365 - }, - { - "id" : "minecraft:brown_mushroom_block", - "blockRuntimeId" : 7350 - }, - { - "id" : "minecraft:egg" - }, - { - "id" : "minecraft:sugar_cane" - }, - { - "id" : "minecraft:sugar" - }, - { - "id" : "minecraft:rotten_flesh" - }, - { - "id" : "minecraft:bone" - }, - { - "id" : "minecraft:web", - "blockRuntimeId" : 6715 - }, - { - "id" : "minecraft:spider_eye" - }, - { - "id" : "minecraft:mob_spawner", - "blockRuntimeId" : 403 - }, - { - "id" : "minecraft:monster_egg", - "blockRuntimeId" : 4146 - }, - { - "id" : "minecraft:monster_egg", - "blockRuntimeId" : 4147 - }, - { - "id" : "minecraft:monster_egg", - "blockRuntimeId" : 4148 - }, - { - "id" : "minecraft:monster_egg", - "blockRuntimeId" : 4149 - }, - { - "id" : "minecraft:monster_egg", - "blockRuntimeId" : 4150 - }, - { - "id" : "minecraft:monster_egg", - "blockRuntimeId" : 4151 - }, - { - "id" : "minecraft:infested_deepslate", - "blockRuntimeId" : 4643 - }, - { - "id" : "minecraft:dragon_egg", - "blockRuntimeId" : 7273 - }, - { - "id" : "minecraft:turtle_egg", - "blockRuntimeId" : 7999 - }, - { - "id" : "minecraft:frog_spawn", - "blockRuntimeId" : 4401 - }, - { - "id" : "minecraft:pearlescent_froglight", - "blockRuntimeId" : 6437 - }, - { - "id" : "minecraft:verdant_froglight", - "blockRuntimeId" : 6483 - }, - { - "id" : "minecraft:ochre_froglight", - "blockRuntimeId" : 3512 - }, - { - "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:obsidian", - "blockRuntimeId" : 430 - }, - { - "id" : "minecraft:crying_obsidian", - "blockRuntimeId" : 6724 - }, - { - "id" : "minecraft:bedrock", - "blockRuntimeId" : 7019 - }, - { - "id" : "minecraft:soul_sand", - "blockRuntimeId" : 5833 - }, - { - "id" : "minecraft:netherrack", - "blockRuntimeId" : 7039 - }, - { - "id" : "minecraft:magma", - "blockRuntimeId" : 8011 - }, - { - "id" : "minecraft:nether_wart" - }, - { - "id" : "minecraft:end_stone", - "blockRuntimeId" : 3838 - }, - { - "id" : "minecraft:chorus_flower", - "blockRuntimeId" : 4532 - }, - { - "id" : "minecraft:chorus_plant", - "blockRuntimeId" : 5507 - }, - { - "id" : "minecraft:chorus_fruit" - }, - { - "id" : "minecraft:popped_chorus_fruit" - }, - { - "id" : "minecraft:sponge", - "blockRuntimeId" : 631 - }, - { - "id" : "minecraft:sponge", - "blockRuntimeId" : 632 - }, - { - "id" : "minecraft:coral_block", - "blockRuntimeId" : 5239 - }, - { - "id" : "minecraft:coral_block", - "blockRuntimeId" : 5240 - }, - { - "id" : "minecraft:coral_block", - "blockRuntimeId" : 5241 - }, - { - "id" : "minecraft:coral_block", - "blockRuntimeId" : 5242 - }, - { - "id" : "minecraft:coral_block", - "blockRuntimeId" : 5243 - }, - { - "id" : "minecraft:coral_block", - "blockRuntimeId" : 5244 - }, - { - "id" : "minecraft:coral_block", - "blockRuntimeId" : 5245 - }, - { - "id" : "minecraft:coral_block", - "blockRuntimeId" : 5246 - }, - { - "id" : "minecraft:coral_block", - "blockRuntimeId" : 5247 - }, - { - "id" : "minecraft:coral_block", - "blockRuntimeId" : 5248 - }, - { - "id" : "minecraft:sculk", - "blockRuntimeId" : 7038 - }, - { - "id" : "minecraft:sculk_vein", - "blockRuntimeId" : 7134 - }, - { - "id" : "minecraft:sculk_catalyst", - "blockRuntimeId" : 3615 - }, - { - "id" : "minecraft:sculk_shrieker", - "blockRuntimeId" : 219 - }, - { - "id" : "minecraft:sculk_sensor", - "blockRuntimeId" : 4391 - }, - { - "id" : "minecraft:reinforced_deepslate", - "blockRuntimeId" : 5834 - }, - { - "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" : 726 - }, - { - "id" : "minecraft:soul_torch", - "blockRuntimeId" : 4646 - }, - { - "id" : "minecraft:sea_pickle", - "blockRuntimeId" : 5857 - }, - { - "id" : "minecraft:lantern", - "blockRuntimeId" : 7076 - }, - { - "id" : "minecraft:soul_lantern", - "blockRuntimeId" : 5751 - }, - { - "id" : "minecraft:candle", - "blockRuntimeId" : 7405 - }, - { - "id" : "minecraft:white_candle", - "blockRuntimeId" : 5302 - }, - { - "id" : "minecraft:orange_candle", - "blockRuntimeId" : 364 - }, - { - "id" : "minecraft:magenta_candle", - "blockRuntimeId" : 420 - }, - { - "id" : "minecraft:light_blue_candle", - "blockRuntimeId" : 4571 - }, - { - "id" : "minecraft:yellow_candle", - "blockRuntimeId" : 6194 - }, - { - "id" : "minecraft:lime_candle", - "blockRuntimeId" : 6370 - }, - { - "id" : "minecraft:pink_candle", - "blockRuntimeId" : 7372 - }, - { - "id" : "minecraft:gray_candle", - "blockRuntimeId" : 941 - }, - { - "id" : "minecraft:light_gray_candle", - "blockRuntimeId" : 6226 - }, - { - "id" : "minecraft:cyan_candle", - "blockRuntimeId" : 7728 - }, - { - "id" : "minecraft:purple_candle", - "blockRuntimeId" : 7040 - }, - { - "id" : "minecraft:blue_candle" - }, - { - "id" : "minecraft:brown_candle", - "blockRuntimeId" : 5877 - }, - { - "id" : "minecraft:green_candle", - "blockRuntimeId" : 688 - }, - { - "id" : "minecraft:red_candle", - "blockRuntimeId" : 4683 - }, - { - "id" : "minecraft:black_candle", - "blockRuntimeId" : 171 - }, - { - "id" : "minecraft:crafting_table", - "blockRuntimeId" : 5856 - }, - { - "id" : "minecraft:cartography_table", - "blockRuntimeId" : 8290 - }, - { - "id" : "minecraft:fletching_table", - "blockRuntimeId" : 5835 - }, - { - "id" : "minecraft:smithing_table", - "blockRuntimeId" : 3728 - }, - { - "id" : "minecraft:beehive", - "blockRuntimeId" : 6110 - }, - { - "id" : "minecraft:campfire" - }, - { - "id" : "minecraft:soul_campfire" - }, - { - "id" : "minecraft:furnace", - "blockRuntimeId" : 7804 - }, - { - "id" : "minecraft:blast_furnace", - "blockRuntimeId" : 7569 - }, - { - "id" : "minecraft:smoker", - "blockRuntimeId" : 649 - }, - { - "id" : "minecraft:respawn_anchor", - "blockRuntimeId" : 683 - }, - { - "id" : "minecraft:brewing_stand" - }, - { - "id" : "minecraft:anvil", - "blockRuntimeId" : 6636 - }, - { - "id" : "minecraft:anvil", - "blockRuntimeId" : 6640 - }, - { - "id" : "minecraft:anvil", - "blockRuntimeId" : 6644 - }, - { - "id" : "minecraft:grindstone", - "blockRuntimeId" : 8041 - }, - { - "id" : "minecraft:enchanting_table", - "blockRuntimeId" : 6725 - }, - { - "id" : "minecraft:bookshelf", - "blockRuntimeId" : 6673 - }, - { - "id" : "minecraft:lectern", - "blockRuntimeId" : 6942 - }, - { - "id" : "minecraft:cauldron" - }, - { - "id" : "minecraft:composter", - "blockRuntimeId" : 5417 - }, - { - "id" : "minecraft:chest", - "blockRuntimeId" : 7117 - }, - { - "id" : "minecraft:trapped_chest", - "blockRuntimeId" : 5585 - }, - { - "id" : "minecraft:ender_chest", - "blockRuntimeId" : 4371 - }, - { - "id" : "minecraft:barrel", - "blockRuntimeId" : 4520 - }, - { - "id" : "minecraft:undyed_shulker_box", - "blockRuntimeId" : 3683 - }, - { - "id" : "minecraft:shulker_box", - "blockRuntimeId" : 5318 - }, - { - "id" : "minecraft:shulker_box", - "blockRuntimeId" : 5326 - }, - { - "id" : "minecraft:shulker_box", - "blockRuntimeId" : 5325 - }, - { - "id" : "minecraft:shulker_box", - "blockRuntimeId" : 5333 - }, - { - "id" : "minecraft:shulker_box", - "blockRuntimeId" : 5330 - }, - { - "id" : "minecraft:shulker_box", - "blockRuntimeId" : 5332 - }, - { - "id" : "minecraft:shulker_box", - "blockRuntimeId" : 5319 - }, - { - "id" : "minecraft:shulker_box", - "blockRuntimeId" : 5322 - }, - { - "id" : "minecraft:shulker_box", - "blockRuntimeId" : 5323 - }, - { - "id" : "minecraft:shulker_box", - "blockRuntimeId" : 5331 - }, - { - "id" : "minecraft:shulker_box", - "blockRuntimeId" : 5327 - }, - { - "id" : "minecraft:shulker_box", - "blockRuntimeId" : 5321 - }, - { - "id" : "minecraft:shulker_box", - "blockRuntimeId" : 5329 - }, - { - "id" : "minecraft:shulker_box", - "blockRuntimeId" : 5328 - }, - { - "id" : "minecraft:shulker_box", - "blockRuntimeId" : 5320 - }, - { - "id" : "minecraft:shulker_box", - "blockRuntimeId" : 5324 - }, - { - "id" : "minecraft:armor_stand" - }, - { - "id" : "minecraft:noteblock", - "blockRuntimeId" : 348 - }, - { - "id" : "minecraft:jukebox", - "blockRuntimeId" : 4876 - }, - { - "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" : 3887 - }, - { - "id" : "minecraft:redstone_lamp", - "blockRuntimeId" : 251 - }, - { - "id" : "minecraft:sea_lantern", - "blockRuntimeId" : 7548 - }, - { - "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" : 145 - }, - { - "id" : "minecraft:bell", - "blockRuntimeId" : 6910 - }, - { - "id" : "minecraft:conduit", - "blockRuntimeId" : 4234 - }, - { - "id" : "minecraft:stonecutter_block", - "blockRuntimeId" : 7576 - }, - { - "id" : "minecraft:end_portal_frame", - "blockRuntimeId" : 6079 - }, - { - "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" : 5893 - }, - { - "id" : "minecraft:lightning_rod", - "blockRuntimeId" : 1178 - }, - { - "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" : 3922 - }, - { - "id" : "minecraft:golden_rail", - "blockRuntimeId" : 5334 - }, - { - "id" : "minecraft:detector_rail", - "blockRuntimeId" : 4134 - }, - { - "id" : "minecraft:activator_rail", - "blockRuntimeId" : 309 - }, - { - "id" : "minecraft:minecart" - }, - { - "id" : "minecraft:chest_minecart" - }, - { - "id" : "minecraft:hopper_minecart" - }, - { - "id" : "minecraft:tnt_minecart" - }, - { - "id" : "minecraft:redstone" - }, - { - "id" : "minecraft:redstone_block", - "blockRuntimeId" : 3778 - }, - { - "id" : "minecraft:redstone_torch", - "blockRuntimeId" : 3527 - }, - { - "id" : "minecraft:lever", - "blockRuntimeId" : 6516 - }, - { - "id" : "minecraft:wooden_button", - "blockRuntimeId" : 6393 - }, - { - "id" : "minecraft:spruce_button", - "blockRuntimeId" : 4323 - }, - { - "id" : "minecraft:birch_button", - "blockRuntimeId" : 7768 - }, - { - "id" : "minecraft:jungle_button", - "blockRuntimeId" : 116 - }, - { - "id" : "minecraft:acacia_button", - "blockRuntimeId" : 7233 - }, - { - "id" : "minecraft:dark_oak_button", - "blockRuntimeId" : 93 - }, - { - "id" : "minecraft:mangrove_button", - "blockRuntimeId" : 7064 - }, - { - "id" : "minecraft:stone_button", - "blockRuntimeId" : 598 - }, - { - "id" : "minecraft:crimson_button", - "blockRuntimeId" : 4434 - }, - { - "id" : "minecraft:warped_button", - "blockRuntimeId" : 7252 - }, - { - "id" : "minecraft:polished_blackstone_button", - "blockRuntimeId" : 7792 - }, - { - "id" : "minecraft:tripwire_hook", - "blockRuntimeId" : 5916 - }, - { - "id" : "minecraft:wooden_pressure_plate", - "blockRuntimeId" : 8065 - }, - { - "id" : "minecraft:spruce_pressure_plate", - "blockRuntimeId" : 3761 - }, - { - "id" : "minecraft:birch_pressure_plate", - "blockRuntimeId" : 3557 - }, - { - "id" : "minecraft:jungle_pressure_plate", - "blockRuntimeId" : 3637 - }, - { - "id" : "minecraft:acacia_pressure_plate", - "blockRuntimeId" : 5249 - }, - { - "id" : "minecraft:dark_oak_pressure_plate", - "blockRuntimeId" : 5958 - }, - { - "id" : "minecraft:mangrove_pressure_plate", - "blockRuntimeId" : 3871 - }, - { - "id" : "minecraft:crimson_pressure_plate", - "blockRuntimeId" : 8270 - }, - { - "id" : "minecraft:warped_pressure_plate", - "blockRuntimeId" : 256 - }, - { - "id" : "minecraft:stone_pressure_plate", - "blockRuntimeId" : 3888 - }, - { - "id" : "minecraft:light_weighted_pressure_plate", - "blockRuntimeId" : 3667 - }, - { - "id" : "minecraft:heavy_weighted_pressure_plate", - "blockRuntimeId" : 1162 - }, - { - "id" : "minecraft:polished_blackstone_pressure_plate", - "blockRuntimeId" : 6234 - }, - { - "id" : "minecraft:observer", - "blockRuntimeId" : 3515 - }, - { - "id" : "minecraft:daylight_detector", - "blockRuntimeId" : 4199 - }, - { - "id" : "minecraft:repeater" - }, - { - "id" : "minecraft:comparator" - }, - { - "id" : "minecraft:hopper" - }, - { - "id" : "minecraft:dropper", - "blockRuntimeId" : 7387 - }, - { - "id" : "minecraft:dispenser", - "blockRuntimeId" : 8015 - }, - { - "id" : "minecraft:piston", - "blockRuntimeId" : 924 - }, - { - "id" : "minecraft:sticky_piston", - "blockRuntimeId" : 4366 - }, - { - "id" : "minecraft:tnt", - "blockRuntimeId" : 6709 - }, - { - "id" : "minecraft:name_tag" - }, - { - "id" : "minecraft:loom", - "blockRuntimeId" : 3828 - }, - { - "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" : 6392 - }, - { - "id" : "minecraft:lodestone_compass" - } - ] -} \ No newline at end of file diff --git a/core/src/main/resources/bedrock/runtime_item_states.1_19_20.json b/core/src/main/resources/bedrock/runtime_item_states.1_19_20.json deleted file mode 100644 index 00be1af06..000000000 --- a/core/src/main/resources/bedrock/runtime_item_states.1_19_20.json +++ /dev/null @@ -1,4530 +0,0 @@ -[ - { - "name" : "minecraft:acacia_boat", - "id" : 379 - }, - { - "name" : "minecraft:acacia_button", - "id" : -140 - }, - { - "name" : "minecraft:acacia_chest_boat", - "id" : 642 - }, - { - "name" : "minecraft:acacia_door", - "id" : 556 - }, - { - "name" : "minecraft:acacia_fence_gate", - "id" : 187 - }, - { - "name" : "minecraft:acacia_pressure_plate", - "id" : -150 - }, - { - "name" : "minecraft:acacia_sign", - "id" : 579 - }, - { - "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" : 487 - }, - { - "name" : "minecraft:air", - "id" : -158 - }, - { - "name" : "minecraft:allay_spawn_egg", - "id" : 631 - }, - { - "name" : "minecraft:allow", - "id" : 210 - }, - { - "name" : "minecraft:amethyst_block", - "id" : -327 - }, - { - "name" : "minecraft:amethyst_cluster", - "id" : -329 - }, - { - "name" : "minecraft:amethyst_shard", - "id" : 624 - }, - { - "name" : "minecraft:ancient_debris", - "id" : -271 - }, - { - "name" : "minecraft:andesite_stairs", - "id" : -171 - }, - { - "name" : "minecraft:anvil", - "id" : 145 - }, - { - "name" : "minecraft:apple", - "id" : 257 - }, - { - "name" : "minecraft:armor_stand", - "id" : 552 - }, - { - "name" : "minecraft:arrow", - "id" : 301 - }, - { - "name" : "minecraft:axolotl_bucket", - "id" : 369 - }, - { - "name" : "minecraft:axolotl_spawn_egg", - "id" : 500 - }, - { - "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" : 598 - }, - { - "name" : "minecraft:bamboo", - "id" : -163 - }, - { - "name" : "minecraft:bamboo_sapling", - "id" : -164 - }, - { - "name" : "minecraft:banner", - "id" : 567 - }, - { - "name" : "minecraft:banner_pattern", - "id" : 651 - }, - { - "name" : "minecraft:barrel", - "id" : -203 - }, - { - "name" : "minecraft:barrier", - "id" : -161 - }, - { - "name" : "minecraft:basalt", - "id" : -234 - }, - { - "name" : "minecraft:bat_spawn_egg", - "id" : 453 - }, - { - "name" : "minecraft:beacon", - "id" : 138 - }, - { - "name" : "minecraft:bed", - "id" : 418 - }, - { - "name" : "minecraft:bedrock", - "id" : 7 - }, - { - "name" : "minecraft:bee_nest", - "id" : -218 - }, - { - "name" : "minecraft:bee_spawn_egg", - "id" : 494 - }, - { - "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" : 376 - }, - { - "name" : "minecraft:birch_button", - "id" : -141 - }, - { - "name" : "minecraft:birch_chest_boat", - "id" : 639 - }, - { - "name" : "minecraft:birch_door", - "id" : 554 - }, - { - "name" : "minecraft:birch_fence_gate", - "id" : 184 - }, - { - "name" : "minecraft:birch_pressure_plate", - "id" : -151 - }, - { - "name" : "minecraft:birch_sign", - "id" : 577 - }, - { - "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" : 395 - }, - { - "name" : "minecraft:black_glazed_terracotta", - "id" : 235 - }, - { - "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" : 429 - }, - { - "name" : "minecraft:blaze_rod", - "id" : 423 - }, - { - "name" : "minecraft:blaze_spawn_egg", - "id" : 456 - }, - { - "name" : "minecraft:bleach", - "id" : 596 - }, - { - "name" : "minecraft:blue_candle", - "id" : -424 - }, - { - "name" : "minecraft:blue_candle_cake", - "id" : -441 - }, - { - "name" : "minecraft:blue_dye", - "id" : 399 - }, - { - "name" : "minecraft:blue_glazed_terracotta", - "id" : 231 - }, - { - "name" : "minecraft:blue_ice", - "id" : -11 - }, - { - "name" : "minecraft:boat", - "id" : 649 - }, - { - "name" : "minecraft:bone", - "id" : 415 - }, - { - "name" : "minecraft:bone_block", - "id" : 216 - }, - { - "name" : "minecraft:bone_meal", - "id" : 411 - }, - { - "name" : "minecraft:book", - "id" : 387 - }, - { - "name" : "minecraft:bookshelf", - "id" : 47 - }, - { - "name" : "minecraft:border_block", - "id" : 212 - }, - { - "name" : "minecraft:bordure_indented_banner_pattern", - "id" : 586 - }, - { - "name" : "minecraft:bow", - "id" : 300 - }, - { - "name" : "minecraft:bowl", - "id" : 321 - }, - { - "name" : "minecraft:bread", - "id" : 261 - }, - { - "name" : "minecraft:brewing_stand", - "id" : 431 - }, - { - "name" : "minecraft:brick", - "id" : 383 - }, - { - "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" : 398 - }, - { - "name" : "minecraft:brown_glazed_terracotta", - "id" : 232 - }, - { - "name" : "minecraft:brown_mushroom", - "id" : 39 - }, - { - "name" : "minecraft:brown_mushroom_block", - "id" : 99 - }, - { - "name" : "minecraft:bubble_column", - "id" : -160 - }, - { - "name" : "minecraft:bucket", - "id" : 360 - }, - { - "name" : "minecraft:budding_amethyst", - "id" : -328 - }, - { - "name" : "minecraft:cactus", - "id" : 81 - }, - { - "name" : "minecraft:cake", - "id" : 417 - }, - { - "name" : "minecraft:calcite", - "id" : -326 - }, - { - "name" : "minecraft:camera", - "id" : 593 - }, - { - "name" : "minecraft:campfire", - "id" : 589 - }, - { - "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" : 517 - }, - { - "name" : "minecraft:carrots", - "id" : 141 - }, - { - "name" : "minecraft:cartography_table", - "id" : -200 - }, - { - "name" : "minecraft:carved_pumpkin", - "id" : -155 - }, - { - "name" : "minecraft:cat_spawn_egg", - "id" : 488 - }, - { - "name" : "minecraft:cauldron", - "id" : 432 - }, - { - "name" : "minecraft:cave_spider_spawn_egg", - "id" : 457 - }, - { - "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" : 619 - }, - { - "name" : "minecraft:chain_command_block", - "id" : 189 - }, - { - "name" : "minecraft:chainmail_boots", - "id" : 342 - }, - { - "name" : "minecraft:chainmail_chestplate", - "id" : 340 - }, - { - "name" : "minecraft:chainmail_helmet", - "id" : 339 - }, - { - "name" : "minecraft:chainmail_leggings", - "id" : 341 - }, - { - "name" : "minecraft:charcoal", - "id" : 303 - }, - { - "name" : "minecraft:chemical_heat", - "id" : 192 - }, - { - "name" : "minecraft:chemistry_table", - "id" : 238 - }, - { - "name" : "minecraft:chest", - "id" : 54 - }, - { - "name" : "minecraft:chest_boat", - "id" : 645 - }, - { - "name" : "minecraft:chest_minecart", - "id" : 389 - }, - { - "name" : "minecraft:chicken", - "id" : 275 - }, - { - "name" : "minecraft:chicken_spawn_egg", - "id" : 435 - }, - { - "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" : 558 - }, - { - "name" : "minecraft:chorus_plant", - "id" : 240 - }, - { - "name" : "minecraft:clay", - "id" : 82 - }, - { - "name" : "minecraft:clay_ball", - "id" : 384 - }, - { - "name" : "minecraft:client_request_placeholder_block", - "id" : -465 - }, - { - "name" : "minecraft:clock", - "id" : 393 - }, - { - "name" : "minecraft:coal", - "id" : 302 - }, - { - "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" : 412 - }, - { - "name" : "minecraft:cod", - "id" : 264 - }, - { - "name" : "minecraft:cod_bucket", - "id" : 364 - }, - { - "name" : "minecraft:cod_spawn_egg", - "id" : 480 - }, - { - "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" : 563 - }, - { - "name" : "minecraft:comparator", - "id" : 522 - }, - { - "name" : "minecraft:compass", - "id" : 391 - }, - { - "name" : "minecraft:composter", - "id" : -213 - }, - { - "name" : "minecraft:compound", - "id" : 594 - }, - { - "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" : 551 - }, - { - "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" : 504 - }, - { - "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" : 436 - }, - { - "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" : 582 - }, - { - "name" : "minecraft:creeper_spawn_egg", - "id" : 441 - }, - { - "name" : "minecraft:crimson_button", - "id" : -260 - }, - { - "name" : "minecraft:crimson_door", - "id" : 616 - }, - { - "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_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" : 614 - }, - { - "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" : 575 - }, - { - "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" : 401 - }, - { - "name" : "minecraft:cyan_glazed_terracotta", - "id" : 229 - }, - { - "name" : "minecraft:dark_oak_boat", - "id" : 380 - }, - { - "name" : "minecraft:dark_oak_button", - "id" : -142 - }, - { - "name" : "minecraft:dark_oak_chest_boat", - "id" : 643 - }, - { - "name" : "minecraft:dark_oak_door", - "id" : 557 - }, - { - "name" : "minecraft:dark_oak_fence_gate", - "id" : 186 - }, - { - "name" : "minecraft:dark_oak_pressure_plate", - "id" : -152 - }, - { - "name" : "minecraft:dark_oak_sign", - "id" : 580 - }, - { - "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: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" : 304 - }, - { - "name" : "minecraft:diamond_axe", - "id" : 319 - }, - { - "name" : "minecraft:diamond_block", - "id" : 57 - }, - { - "name" : "minecraft:diamond_boots", - "id" : 350 - }, - { - "name" : "minecraft:diamond_chestplate", - "id" : 348 - }, - { - "name" : "minecraft:diamond_helmet", - "id" : 347 - }, - { - "name" : "minecraft:diamond_hoe", - "id" : 332 - }, - { - "name" : "minecraft:diamond_horse_armor", - "id" : 533 - }, - { - "name" : "minecraft:diamond_leggings", - "id" : 349 - }, - { - "name" : "minecraft:diamond_ore", - "id" : 56 - }, - { - "name" : "minecraft:diamond_pickaxe", - "id" : 318 - }, - { - "name" : "minecraft:diamond_shovel", - "id" : 317 - }, - { - "name" : "minecraft:diamond_sword", - "id" : 316 - }, - { - "name" : "minecraft:diorite_stairs", - "id" : -170 - }, - { - "name" : "minecraft:dirt", - "id" : 3 - }, - { - "name" : "minecraft:dirt_with_roots", - "id" : -318 - }, - { - "name" : "minecraft:disc_fragment_5", - "id" : 637 - }, - { - "name" : "minecraft:dispenser", - "id" : 23 - }, - { - "name" : "minecraft:dolphin_spawn_egg", - "id" : 484 - }, - { - "name" : "minecraft:donkey_spawn_egg", - "id" : 465 - }, - { - "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" : 560 - }, - { - "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" : 483 - }, - { - "name" : "minecraft:dye", - "id" : 650 - }, - { - "name" : "minecraft:echo_shard", - "id" : 647 - }, - { - "name" : "minecraft:egg", - "id" : 390 - }, - { - "name" : "minecraft:elder_guardian_spawn_egg", - "id" : 471 - }, - { - "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" : 564 - }, - { - "name" : "minecraft:emerald", - "id" : 512 - }, - { - "name" : "minecraft:emerald_block", - "id" : 133 - }, - { - "name" : "minecraft:emerald_ore", - "id" : 129 - }, - { - "name" : "minecraft:empty_map", - "id" : 515 - }, - { - "name" : "minecraft:enchanted_book", - "id" : 521 - }, - { - "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" : 653 - }, - { - "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_eye", - "id" : 433 - }, - { - "name" : "minecraft:ender_pearl", - "id" : 422 - }, - { - "name" : "minecraft:enderman_spawn_egg", - "id" : 442 - }, - { - "name" : "minecraft:endermite_spawn_egg", - "id" : 460 - }, - { - "name" : "minecraft:evoker_spawn_egg", - "id" : 475 - }, - { - "name" : "minecraft:experience_bottle", - "id" : 508 - }, - { - "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" : 327 - }, - { - "name" : "minecraft:fence", - "id" : 85 - }, - { - "name" : "minecraft:fence_gate", - "id" : 107 - }, - { - "name" : "minecraft:fermented_spider_eye", - "id" : 428 - }, - { - "name" : "minecraft:field_masoned_banner_pattern", - "id" : 585 - }, - { - "name" : "minecraft:filled_map", - "id" : 420 - }, - { - "name" : "minecraft:fire", - "id" : 51 - }, - { - "name" : "minecraft:fire_charge", - "id" : 509 - }, - { - "name" : "minecraft:firework_rocket", - "id" : 519 - }, - { - "name" : "minecraft:firework_star", - "id" : 520 - }, - { - "name" : "minecraft:fishing_rod", - "id" : 392 - }, - { - "name" : "minecraft:fletching_table", - "id" : -201 - }, - { - "name" : "minecraft:flint", - "id" : 356 - }, - { - "name" : "minecraft:flint_and_steel", - "id" : 299 - }, - { - "name" : "minecraft:flower_banner_pattern", - "id" : 581 - }, - { - "name" : "minecraft:flower_pot", - "id" : 514 - }, - { - "name" : "minecraft:flowering_azalea", - "id" : -338 - }, - { - "name" : "minecraft:flowing_lava", - "id" : 10 - }, - { - "name" : "minecraft:flowing_water", - "id" : 8 - }, - { - "name" : "minecraft:fox_spawn_egg", - "id" : 490 - }, - { - "name" : "minecraft:frame", - "id" : 513 - }, - { - "name" : "minecraft:frog_spawn", - "id" : -468 - }, - { - "name" : "minecraft:frog_spawn_egg", - "id" : 628 - }, - { - "name" : "minecraft:frosted_ice", - "id" : 207 - }, - { - "name" : "minecraft:furnace", - "id" : 61 - }, - { - "name" : "minecraft:ghast_spawn_egg", - "id" : 454 - }, - { - "name" : "minecraft:ghast_tear", - "id" : 424 - }, - { - "name" : "minecraft:gilded_blackstone", - "id" : -281 - }, - { - "name" : "minecraft:glass", - "id" : 20 - }, - { - "name" : "minecraft:glass_bottle", - "id" : 427 - }, - { - "name" : "minecraft:glass_pane", - "id" : 102 - }, - { - "name" : "minecraft:glistering_melon_slice", - "id" : 434 - }, - { - "name" : "minecraft:globe_banner_pattern", - "id" : 588 - }, - { - "name" : "minecraft:glow_berries", - "id" : 654 - }, - { - "name" : "minecraft:glow_frame", - "id" : 623 - }, - { - "name" : "minecraft:glow_ink_sac", - "id" : 503 - }, - { - "name" : "minecraft:glow_lichen", - "id" : -411 - }, - { - "name" : "minecraft:glow_squid_spawn_egg", - "id" : 502 - }, - { - "name" : "minecraft:glow_stick", - "id" : 601 - }, - { - "name" : "minecraft:glowingobsidian", - "id" : 246 - }, - { - "name" : "minecraft:glowstone", - "id" : 89 - }, - { - "name" : "minecraft:glowstone_dust", - "id" : 394 - }, - { - "name" : "minecraft:goat_horn", - "id" : 627 - }, - { - "name" : "minecraft:goat_spawn_egg", - "id" : 501 - }, - { - "name" : "minecraft:gold_block", - "id" : 41 - }, - { - "name" : "minecraft:gold_ingot", - "id" : 306 - }, - { - "name" : "minecraft:gold_nugget", - "id" : 425 - }, - { - "name" : "minecraft:gold_ore", - "id" : 14 - }, - { - "name" : "minecraft:golden_apple", - "id" : 258 - }, - { - "name" : "minecraft:golden_axe", - "id" : 325 - }, - { - "name" : "minecraft:golden_boots", - "id" : 354 - }, - { - "name" : "minecraft:golden_carrot", - "id" : 283 - }, - { - "name" : "minecraft:golden_chestplate", - "id" : 352 - }, - { - "name" : "minecraft:golden_helmet", - "id" : 351 - }, - { - "name" : "minecraft:golden_hoe", - "id" : 333 - }, - { - "name" : "minecraft:golden_horse_armor", - "id" : 532 - }, - { - "name" : "minecraft:golden_leggings", - "id" : 353 - }, - { - "name" : "minecraft:golden_pickaxe", - "id" : 324 - }, - { - "name" : "minecraft:golden_rail", - "id" : 27 - }, - { - "name" : "minecraft:golden_shovel", - "id" : 323 - }, - { - "name" : "minecraft:golden_sword", - "id" : 322 - }, - { - "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" : 403 - }, - { - "name" : "minecraft:gray_glazed_terracotta", - "id" : 227 - }, - { - "name" : "minecraft:green_candle", - "id" : -426 - }, - { - "name" : "minecraft:green_candle_cake", - "id" : -443 - }, - { - "name" : "minecraft:green_dye", - "id" : 397 - }, - { - "name" : "minecraft:green_glazed_terracotta", - "id" : 233 - }, - { - "name" : "minecraft:grindstone", - "id" : -195 - }, - { - "name" : "minecraft:guardian_spawn_egg", - "id" : 461 - }, - { - "name" : "minecraft:gunpowder", - "id" : 328 - }, - { - "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" : 571 - }, - { - "name" : "minecraft:heavy_weighted_pressure_plate", - "id" : 148 - }, - { - "name" : "minecraft:hoglin_spawn_egg", - "id" : 496 - }, - { - "name" : "minecraft:honey_block", - "id" : -220 - }, - { - "name" : "minecraft:honey_bottle", - "id" : 592 - }, - { - "name" : "minecraft:honeycomb", - "id" : 591 - }, - { - "name" : "minecraft:honeycomb_block", - "id" : -221 - }, - { - "name" : "minecraft:hopper", - "id" : 527 - }, - { - "name" : "minecraft:hopper_minecart", - "id" : 526 - }, - { - "name" : "minecraft:horse_spawn_egg", - "id" : 458 - }, - { - "name" : "minecraft:husk_spawn_egg", - "id" : 463 - }, - { - "name" : "minecraft:ice", - "id" : 79 - }, - { - "name" : "minecraft:ice_bomb", - "id" : 595 - }, - { - "name" : "minecraft:infested_deepslate", - "id" : -454 - }, - { - "name" : "minecraft:info_update", - "id" : 248 - }, - { - "name" : "minecraft:info_update2", - "id" : 249 - }, - { - "name" : "minecraft:ink_sac", - "id" : 413 - }, - { - "name" : "minecraft:invisible_bedrock", - "id" : 95 - }, - { - "name" : "minecraft:iron_axe", - "id" : 298 - }, - { - "name" : "minecraft:iron_bars", - "id" : 101 - }, - { - "name" : "minecraft:iron_block", - "id" : 42 - }, - { - "name" : "minecraft:iron_boots", - "id" : 346 - }, - { - "name" : "minecraft:iron_chestplate", - "id" : 344 - }, - { - "name" : "minecraft:iron_door", - "id" : 372 - }, - { - "name" : "minecraft:iron_helmet", - "id" : 343 - }, - { - "name" : "minecraft:iron_hoe", - "id" : 331 - }, - { - "name" : "minecraft:iron_horse_armor", - "id" : 531 - }, - { - "name" : "minecraft:iron_ingot", - "id" : 305 - }, - { - "name" : "minecraft:iron_leggings", - "id" : 345 - }, - { - "name" : "minecraft:iron_nugget", - "id" : 569 - }, - { - "name" : "minecraft:iron_ore", - "id" : 15 - }, - { - "name" : "minecraft:iron_pickaxe", - "id" : 297 - }, - { - "name" : "minecraft:iron_shovel", - "id" : 296 - }, - { - "name" : "minecraft:iron_sword", - "id" : 307 - }, - { - "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" : 377 - }, - { - "name" : "minecraft:jungle_button", - "id" : -143 - }, - { - "name" : "minecraft:jungle_chest_boat", - "id" : 640 - }, - { - "name" : "minecraft:jungle_door", - "id" : 555 - }, - { - "name" : "minecraft:jungle_fence_gate", - "id" : 185 - }, - { - "name" : "minecraft:jungle_pressure_plate", - "id" : -153 - }, - { - "name" : "minecraft:jungle_sign", - "id" : 578 - }, - { - "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" : 382 - }, - { - "name" : "minecraft:ladder", - "id" : 65 - }, - { - "name" : "minecraft:lantern", - "id" : -208 - }, - { - "name" : "minecraft:lapis_block", - "id" : 22 - }, - { - "name" : "minecraft:lapis_lazuli", - "id" : 414 - }, - { - "name" : "minecraft:lapis_ore", - "id" : 21 - }, - { - "name" : "minecraft:large_amethyst_bud", - "id" : -330 - }, - { - "name" : "minecraft:lava", - "id" : 11 - }, - { - "name" : "minecraft:lava_bucket", - "id" : 363 - }, - { - "name" : "minecraft:lava_cauldron", - "id" : -210 - }, - { - "name" : "minecraft:lead", - "id" : 547 - }, - { - "name" : "minecraft:leather", - "id" : 381 - }, - { - "name" : "minecraft:leather_boots", - "id" : 338 - }, - { - "name" : "minecraft:leather_chestplate", - "id" : 336 - }, - { - "name" : "minecraft:leather_helmet", - "id" : 335 - }, - { - "name" : "minecraft:leather_horse_armor", - "id" : 530 - }, - { - "name" : "minecraft:leather_leggings", - "id" : 337 - }, - { - "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" : 407 - }, - { - "name" : "minecraft:light_blue_glazed_terracotta", - "id" : 223 - }, - { - "name" : "minecraft:light_gray_candle", - "id" : -421 - }, - { - "name" : "minecraft:light_gray_candle_cake", - "id" : -438 - }, - { - "name" : "minecraft:light_gray_dye", - "id" : 402 - }, - { - "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" : 405 - }, - { - "name" : "minecraft:lime_glazed_terracotta", - "id" : 225 - }, - { - "name" : "minecraft:lingering_potion", - "id" : 562 - }, - { - "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" : 473 - }, - { - "name" : "minecraft:lodestone", - "id" : -222 - }, - { - "name" : "minecraft:lodestone_compass", - "id" : 602 - }, - { - "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" : 408 - }, - { - "name" : "minecraft:magenta_glazed_terracotta", - "id" : 222 - }, - { - "name" : "minecraft:magma", - "id" : 213 - }, - { - "name" : "minecraft:magma_cream", - "id" : 430 - }, - { - "name" : "minecraft:magma_cube_spawn_egg", - "id" : 455 - }, - { - "name" : "minecraft:mangrove_boat", - "id" : 635 - }, - { - "name" : "minecraft:mangrove_button", - "id" : -487 - }, - { - "name" : "minecraft:mangrove_chest_boat", - "id" : 644 - }, - { - "name" : "minecraft:mangrove_door", - "id" : 633 - }, - { - "name" : "minecraft:mangrove_double_slab", - "id" : -499 - }, - { - "name" : "minecraft:mangrove_fence", - "id" : -491 - }, - { - "name" : "minecraft:mangrove_fence_gate", - "id" : -492 - }, - { - "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" : 634 - }, - { - "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" : 599 - }, - { - "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" : 361 - }, - { - "name" : "minecraft:minecart", - "id" : 370 - }, - { - "name" : "minecraft:mob_spawner", - "id" : 52 - }, - { - "name" : "minecraft:mojang_banner_pattern", - "id" : 584 - }, - { - "name" : "minecraft:monster_egg", - "id" : 97 - }, - { - "name" : "minecraft:mooshroom_spawn_egg", - "id" : 440 - }, - { - "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" : 466 - }, - { - "name" : "minecraft:mushroom_stew", - "id" : 260 - }, - { - "name" : "minecraft:music_disc_11", - "id" : 544 - }, - { - "name" : "minecraft:music_disc_13", - "id" : 534 - }, - { - "name" : "minecraft:music_disc_5", - "id" : 636 - }, - { - "name" : "minecraft:music_disc_blocks", - "id" : 536 - }, - { - "name" : "minecraft:music_disc_cat", - "id" : 535 - }, - { - "name" : "minecraft:music_disc_chirp", - "id" : 537 - }, - { - "name" : "minecraft:music_disc_far", - "id" : 538 - }, - { - "name" : "minecraft:music_disc_mall", - "id" : 539 - }, - { - "name" : "minecraft:music_disc_mellohi", - "id" : 540 - }, - { - "name" : "minecraft:music_disc_otherside", - "id" : 626 - }, - { - "name" : "minecraft:music_disc_pigstep", - "id" : 620 - }, - { - "name" : "minecraft:music_disc_stal", - "id" : 541 - }, - { - "name" : "minecraft:music_disc_strad", - "id" : 542 - }, - { - "name" : "minecraft:music_disc_wait", - "id" : 545 - }, - { - "name" : "minecraft:music_disc_ward", - "id" : 543 - }, - { - "name" : "minecraft:mutton", - "id" : 550 - }, - { - "name" : "minecraft:mycelium", - "id" : 110 - }, - { - "name" : "minecraft:name_tag", - "id" : 548 - }, - { - "name" : "minecraft:nautilus_shell", - "id" : 570 - }, - { - "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" : 621 - }, - { - "name" : "minecraft:nether_star", - "id" : 518 - }, - { - "name" : "minecraft:nether_wart", - "id" : 294 - }, - { - "name" : "minecraft:nether_wart_block", - "id" : 214 - }, - { - "name" : "minecraft:netherbrick", - "id" : 523 - }, - { - "name" : "minecraft:netherite_axe", - "id" : 607 - }, - { - "name" : "minecraft:netherite_block", - "id" : -270 - }, - { - "name" : "minecraft:netherite_boots", - "id" : 612 - }, - { - "name" : "minecraft:netherite_chestplate", - "id" : 610 - }, - { - "name" : "minecraft:netherite_helmet", - "id" : 609 - }, - { - "name" : "minecraft:netherite_hoe", - "id" : 608 - }, - { - "name" : "minecraft:netherite_ingot", - "id" : 603 - }, - { - "name" : "minecraft:netherite_leggings", - "id" : 611 - }, - { - "name" : "minecraft:netherite_pickaxe", - "id" : 606 - }, - { - "name" : "minecraft:netherite_scrap", - "id" : 613 - }, - { - "name" : "minecraft:netherite_shovel", - "id" : 605 - }, - { - "name" : "minecraft:netherite_sword", - "id" : 604 - }, - { - "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" : 470 - }, - { - "name" : "minecraft:oak_boat", - "id" : 375 - }, - { - "name" : "minecraft:oak_chest_boat", - "id" : 638 - }, - { - "name" : "minecraft:oak_sign", - "id" : 358 - }, - { - "name" : "minecraft:oak_stairs", - "id" : 53 - }, - { - "name" : "minecraft:observer", - "id" : 251 - }, - { - "name" : "minecraft:obsidian", - "id" : 49 - }, - { - "name" : "minecraft:ocelot_spawn_egg", - "id" : 451 - }, - { - "name" : "minecraft:ochre_froglight", - "id" : -471 - }, - { - "name" : "minecraft:orange_candle", - "id" : -414 - }, - { - "name" : "minecraft:orange_candle_cake", - "id" : -431 - }, - { - "name" : "minecraft:orange_dye", - "id" : 409 - }, - { - "name" : "minecraft:orange_glazed_terracotta", - "id" : 221 - }, - { - "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" : 357 - }, - { - "name" : "minecraft:panda_spawn_egg", - "id" : 489 - }, - { - "name" : "minecraft:paper", - "id" : 386 - }, - { - "name" : "minecraft:parrot_spawn_egg", - "id" : 478 - }, - { - "name" : "minecraft:pearlescent_froglight", - "id" : -469 - }, - { - "name" : "minecraft:phantom_membrane", - "id" : 574 - }, - { - "name" : "minecraft:phantom_spawn_egg", - "id" : 486 - }, - { - "name" : "minecraft:pig_spawn_egg", - "id" : 437 - }, - { - "name" : "minecraft:piglin_banner_pattern", - "id" : 587 - }, - { - "name" : "minecraft:piglin_brute_spawn_egg", - "id" : 499 - }, - { - "name" : "minecraft:piglin_spawn_egg", - "id" : 497 - }, - { - "name" : "minecraft:pillager_spawn_egg", - "id" : 491 - }, - { - "name" : "minecraft:pink_candle", - "id" : -419 - }, - { - "name" : "minecraft:pink_candle_cake", - "id" : -436 - }, - { - "name" : "minecraft:pink_dye", - "id" : 404 - }, - { - "name" : "minecraft:pink_glazed_terracotta", - "id" : 226 - }, - { - "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" : 472 - }, - { - "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" : 559 - }, - { - "name" : "minecraft:porkchop", - "id" : 262 - }, - { - "name" : "minecraft:portal", - "id" : 90 - }, - { - "name" : "minecraft:potato", - "id" : 280 - }, - { - "name" : "minecraft:potatoes", - "id" : 142 - }, - { - "name" : "minecraft:potion", - "id" : 426 - }, - { - "name" : "minecraft:powder_snow", - "id" : -306 - }, - { - "name" : "minecraft:powder_snow_bucket", - "id" : 368 - }, - { - "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" : 549 - }, - { - "name" : "minecraft:prismarine_shard", - "id" : 565 - }, - { - "name" : "minecraft:prismarine_stairs", - "id" : -2 - }, - { - "name" : "minecraft:pufferfish", - "id" : 267 - }, - { - "name" : "minecraft:pufferfish_bucket", - "id" : 367 - }, - { - "name" : "minecraft:pufferfish_spawn_egg", - "id" : 481 - }, - { - "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" : 400 - }, - { - "name" : "minecraft:purple_glazed_terracotta", - "id" : 219 - }, - { - "name" : "minecraft:purpur_block", - "id" : 201 - }, - { - "name" : "minecraft:purpur_stairs", - "id" : 203 - }, - { - "name" : "minecraft:quartz", - "id" : 524 - }, - { - "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" : 528 - }, - { - "name" : "minecraft:rabbit_hide", - "id" : 529 - }, - { - "name" : "minecraft:rabbit_spawn_egg", - "id" : 459 - }, - { - "name" : "minecraft:rabbit_stew", - "id" : 290 - }, - { - "name" : "minecraft:rail", - "id" : 66 - }, - { - "name" : "minecraft:rapid_fertilizer", - "id" : 597 - }, - { - "name" : "minecraft:ravager_spawn_egg", - "id" : 493 - }, - { - "name" : "minecraft:raw_copper", - "id" : 507 - }, - { - "name" : "minecraft:raw_copper_block", - "id" : -452 - }, - { - "name" : "minecraft:raw_gold", - "id" : 506 - }, - { - "name" : "minecraft:raw_gold_block", - "id" : -453 - }, - { - "name" : "minecraft:raw_iron", - "id" : 505 - }, - { - "name" : "minecraft:raw_iron_block", - "id" : -451 - }, - { - "name" : "minecraft:recovery_compass", - "id" : 646 - }, - { - "name" : "minecraft:red_candle", - "id" : -427 - }, - { - "name" : "minecraft:red_candle_cake", - "id" : -444 - }, - { - "name" : "minecraft:red_dye", - "id" : 396 - }, - { - "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:redstone", - "id" : 373 - }, - { - "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" : 419 - }, - { - "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" : 371 - }, - { - "name" : "minecraft:salmon", - "id" : 265 - }, - { - "name" : "minecraft:salmon_bucket", - "id" : 365 - }, - { - "name" : "minecraft:salmon_spawn_egg", - "id" : 482 - }, - { - "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" : 572 - }, - { - "name" : "minecraft:sea_lantern", - "id" : 169 - }, - { - "name" : "minecraft:sea_pickle", - "id" : -156 - }, - { - "name" : "minecraft:seagrass", - "id" : -130 - }, - { - "name" : "minecraft:shears", - "id" : 421 - }, - { - "name" : "minecraft:sheep_spawn_egg", - "id" : 438 - }, - { - "name" : "minecraft:shield", - "id" : 355 - }, - { - "name" : "minecraft:shroomlight", - "id" : -230 - }, - { - "name" : "minecraft:shulker_box", - "id" : 218 - }, - { - "name" : "minecraft:shulker_shell", - "id" : 566 - }, - { - "name" : "minecraft:shulker_spawn_egg", - "id" : 469 - }, - { - "name" : "minecraft:silver_glazed_terracotta", - "id" : 228 - }, - { - "name" : "minecraft:silverfish_spawn_egg", - "id" : 443 - }, - { - "name" : "minecraft:skeleton_horse_spawn_egg", - "id" : 467 - }, - { - "name" : "minecraft:skeleton_spawn_egg", - "id" : 444 - }, - { - "name" : "minecraft:skull", - "id" : 516 - }, - { - "name" : "minecraft:skull_banner_pattern", - "id" : 583 - }, - { - "name" : "minecraft:slime", - "id" : 165 - }, - { - "name" : "minecraft:slime_ball", - "id" : 388 - }, - { - "name" : "minecraft:slime_spawn_egg", - "id" : 445 - }, - { - "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:snow", - "id" : 80 - }, - { - "name" : "minecraft:snow_layer", - "id" : 78 - }, - { - "name" : "minecraft:snowball", - "id" : 374 - }, - { - "name" : "minecraft:soul_campfire", - "id" : 622 - }, - { - "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" : 600 - }, - { - "name" : "minecraft:spawn_egg", - "id" : 652 - }, - { - "name" : "minecraft:spider_eye", - "id" : 278 - }, - { - "name" : "minecraft:spider_spawn_egg", - "id" : 446 - }, - { - "name" : "minecraft:splash_potion", - "id" : 561 - }, - { - "name" : "minecraft:sponge", - "id" : 19 - }, - { - "name" : "minecraft:spore_blossom", - "id" : -321 - }, - { - "name" : "minecraft:spruce_boat", - "id" : 378 - }, - { - "name" : "minecraft:spruce_button", - "id" : -144 - }, - { - "name" : "minecraft:spruce_chest_boat", - "id" : 641 - }, - { - "name" : "minecraft:spruce_door", - "id" : 553 - }, - { - "name" : "minecraft:spruce_fence_gate", - "id" : 183 - }, - { - "name" : "minecraft:spruce_pressure_plate", - "id" : -154 - }, - { - "name" : "minecraft:spruce_sign", - "id" : 576 - }, - { - "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" : 625 - }, - { - "name" : "minecraft:squid_spawn_egg", - "id" : 450 - }, - { - "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" : 320 - }, - { - "name" : "minecraft:sticky_piston", - "id" : 29 - }, - { - "name" : "minecraft:sticky_piston_arm_collision", - "id" : -217 - }, - { - "name" : "minecraft:stone", - "id" : 1 - }, - { - "name" : "minecraft:stone_axe", - "id" : 315 - }, - { - "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" : 330 - }, - { - "name" : "minecraft:stone_pickaxe", - "id" : 314 - }, - { - "name" : "minecraft:stone_pressure_plate", - "id" : 70 - }, - { - "name" : "minecraft:stone_shovel", - "id" : 313 - }, - { - "name" : "minecraft:stone_stairs", - "id" : 67 - }, - { - "name" : "minecraft:stone_sword", - "id" : 312 - }, - { - "name" : "minecraft:stonebrick", - "id" : 98 - }, - { - "name" : "minecraft:stonecutter", - "id" : 245 - }, - { - "name" : "minecraft:stonecutter_block", - "id" : -197 - }, - { - "name" : "minecraft:stray_spawn_egg", - "id" : 462 - }, - { - "name" : "minecraft:strider_spawn_egg", - "id" : 495 - }, - { - "name" : "minecraft:string", - "id" : 326 - }, - { - "name" : "minecraft:stripped_acacia_log", - "id" : -8 - }, - { - "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" : 416 - }, - { - "name" : "minecraft:sugar_cane", - "id" : 385 - }, - { - "name" : "minecraft:suspicious_stew", - "id" : 590 - }, - { - "name" : "minecraft:sweet_berries", - "id" : 287 - }, - { - "name" : "minecraft:sweet_berry_bush", - "id" : -207 - }, - { - "name" : "minecraft:tadpole_bucket", - "id" : 630 - }, - { - "name" : "minecraft:tadpole_spawn_egg", - "id" : 629 - }, - { - "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" : 525 - }, - { - "name" : "minecraft:torch", - "id" : 50 - }, - { - "name" : "minecraft:totem_of_undying", - "id" : 568 - }, - { - "name" : "minecraft:trader_llama_spawn_egg", - "id" : 648 - }, - { - "name" : "minecraft:trapdoor", - "id" : 96 - }, - { - "name" : "minecraft:trapped_chest", - "id" : 146 - }, - { - "name" : "minecraft:trident", - "id" : 546 - }, - { - "name" : "minecraft:trip_wire", - "id" : 132 - }, - { - "name" : "minecraft:tripwire_hook", - "id" : 131 - }, - { - "name" : "minecraft:tropical_fish", - "id" : 266 - }, - { - "name" : "minecraft:tropical_fish_bucket", - "id" : 366 - }, - { - "name" : "minecraft:tropical_fish_spawn_egg", - "id" : 479 - }, - { - "name" : "minecraft:tuff", - "id" : -333 - }, - { - "name" : "minecraft:turtle_egg", - "id" : -159 - }, - { - "name" : "minecraft:turtle_helmet", - "id" : 573 - }, - { - "name" : "minecraft:turtle_spawn_egg", - "id" : 485 - }, - { - "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" : 476 - }, - { - "name" : "minecraft:villager_spawn_egg", - "id" : 449 - }, - { - "name" : "minecraft:vindicator_spawn_egg", - "id" : 474 - }, - { - "name" : "minecraft:vine", - "id" : 106 - }, - { - "name" : "minecraft:wall_banner", - "id" : 177 - }, - { - "name" : "minecraft:wall_sign", - "id" : 68 - }, - { - "name" : "minecraft:wandering_trader_spawn_egg", - "id" : 492 - }, - { - "name" : "minecraft:warden_spawn_egg", - "id" : 632 - }, - { - "name" : "minecraft:warped_button", - "id" : -261 - }, - { - "name" : "minecraft:warped_door", - "id" : 617 - }, - { - "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" : 618 - }, - { - "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" : 615 - }, - { - "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" : 362 - }, - { - "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" : 334 - }, - { - "name" : "minecraft:wheat_seeds", - "id" : 291 - }, - { - "name" : "minecraft:white_candle", - "id" : -413 - }, - { - "name" : "minecraft:white_candle_cake", - "id" : -430 - }, - { - "name" : "minecraft:white_dye", - "id" : 410 - }, - { - "name" : "minecraft:white_glazed_terracotta", - "id" : 220 - }, - { - "name" : "minecraft:witch_spawn_egg", - "id" : 452 - }, - { - "name" : "minecraft:wither_rose", - "id" : -216 - }, - { - "name" : "minecraft:wither_skeleton_spawn_egg", - "id" : 464 - }, - { - "name" : "minecraft:wolf_spawn_egg", - "id" : 439 - }, - { - "name" : "minecraft:wood", - "id" : -212 - }, - { - "name" : "minecraft:wooden_axe", - "id" : 311 - }, - { - "name" : "minecraft:wooden_button", - "id" : 143 - }, - { - "name" : "minecraft:wooden_door", - "id" : 359 - }, - { - "name" : "minecraft:wooden_hoe", - "id" : 329 - }, - { - "name" : "minecraft:wooden_pickaxe", - "id" : 310 - }, - { - "name" : "minecraft:wooden_pressure_plate", - "id" : 72 - }, - { - "name" : "minecraft:wooden_shovel", - "id" : 309 - }, - { - "name" : "minecraft:wooden_slab", - "id" : 158 - }, - { - "name" : "minecraft:wooden_sword", - "id" : 308 - }, - { - "name" : "minecraft:wool", - "id" : 35 - }, - { - "name" : "minecraft:writable_book", - "id" : 510 - }, - { - "name" : "minecraft:written_book", - "id" : 511 - }, - { - "name" : "minecraft:yellow_candle", - "id" : -417 - }, - { - "name" : "minecraft:yellow_candle_cake", - "id" : -434 - }, - { - "name" : "minecraft:yellow_dye", - "id" : 406 - }, - { - "name" : "minecraft:yellow_flower", - "id" : 37 - }, - { - "name" : "minecraft:yellow_glazed_terracotta", - "id" : 224 - }, - { - "name" : "minecraft:zoglin_spawn_egg", - "id" : 498 - }, - { - "name" : "minecraft:zombie_horse_spawn_egg", - "id" : 468 - }, - { - "name" : "minecraft:zombie_pigman_spawn_egg", - "id" : 448 - }, - { - "name" : "minecraft:zombie_spawn_egg", - "id" : 447 - }, - { - "name" : "minecraft:zombie_villager_spawn_egg", - "id" : 477 - } -] \ No newline at end of file From b09caed0f1e19e85a78043fd79348f87385e228e Mon Sep 17 00:00:00 2001 From: Konicai <71294714+Konicai@users.noreply.github.com> Date: Fri, 2 Dec 2022 00:29:20 -0500 Subject: [PATCH 27/81] Fix BitSet import --- .../src/main/java/org/geysermc/geyser/session/GeyserSession.java | 1 + 1 file changed, 1 insertion(+) 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 8da5b1273..8f89e81f1 100644 --- a/core/src/main/java/org/geysermc/geyser/session/GeyserSession.java +++ b/core/src/main/java/org/geysermc/geyser/session/GeyserSession.java @@ -194,6 +194,7 @@ import java.nio.charset.StandardCharsets; import java.time.Instant; import java.util.ArrayList; import java.util.Arrays; +import java.util.BitSet; import java.util.Collections; import java.util.HashSet; import java.util.List; From 309f9737bb70dc474c09b81578a2346cd772a9d7 Mon Sep 17 00:00:00 2001 From: Konicai <71294714+Konicai@users.noreply.github.com> Date: Fri, 2 Dec 2022 01:43:09 -0500 Subject: [PATCH 28/81] Update palettes/mappings --- .../resources/bedrock/biome_definitions.dat | Bin 42385 -> 2605093 bytes .../bedrock/creative_items.1_19_50.json | 93 ++++++++++++++++++ .../resources/bedrock/entity_identifiers.dat | Bin 7762 -> 7823 bytes core/src/main/resources/mappings | 2 +- 4 files changed, 94 insertions(+), 1 deletion(-) diff --git a/core/src/main/resources/bedrock/biome_definitions.dat b/core/src/main/resources/bedrock/biome_definitions.dat index 0520c61e28fdd87c24bfdfc25fa30f3f52b70eee..47b19ab44fbfbf531f82c14cbceaad74d87afe47 100644 GIT binary patch literal 2605093 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%0Dr|j|qX{c2 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

_$5>vWeDnm=+-ais~iGb2nk|k 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 Smn@;KMDP@7D?Fubz4d<}Lt$J1 diff --git a/core/src/main/resources/bedrock/creative_items.1_19_50.json b/core/src/main/resources/bedrock/creative_items.1_19_50.json index 243826c9e..4ed5f8194 100644 --- a/core/src/main/resources/bedrock/creative_items.1_19_50.json +++ b/core/src/main/resources/bedrock/creative_items.1_19_50.json @@ -28,6 +28,14 @@ "id" : "minecraft:mangrove_planks", "blockRuntimeId" : 1570 }, + { + "id" : "minecraft:bamboo_planks", + "blockRuntimeId" : 8202 + }, + { + "id" : "minecraft:bamboo_mosaic", + "blockRuntimeId" : 12438 + }, { "id" : "minecraft:crimson_planks", "blockRuntimeId" : 7399 @@ -152,6 +160,10 @@ "id" : "minecraft:mangrove_fence", "blockRuntimeId" : 10405 }, + { + "id" : "minecraft:bamboo_fence", + "blockRuntimeId" : 863 + }, { "id" : "minecraft:nether_brick_fence", "blockRuntimeId" : 6071 @@ -192,6 +204,10 @@ "id" : "minecraft:mangrove_fence_gate", "blockRuntimeId" : 6406 }, + { + "id" : "minecraft:bamboo_fence_gate", + "blockRuntimeId" : 7611 + }, { "id" : "minecraft:crimson_fence_gate", "blockRuntimeId" : 6826 @@ -240,6 +256,14 @@ "id" : "minecraft:mangrove_stairs", "blockRuntimeId" : 6376 }, + { + "id" : "minecraft:bamboo_stairs", + "blockRuntimeId" : 1339 + }, + { + "id" : "minecraft:bamboo_mosaic_stairs", + "blockRuntimeId" : 9958 + }, { "id" : "minecraft:stone_brick_stairs", "blockRuntimeId" : 1554 @@ -421,6 +445,9 @@ { "id" : "minecraft:mangrove_door" }, + { + "id" : "minecraft:bamboo_door" + }, { "id" : "minecraft:iron_door" }, @@ -458,6 +485,10 @@ "id" : "minecraft:mangrove_trapdoor", "blockRuntimeId" : 6266 }, + { + "id" : "minecraft:bamboo_trapdoor", + "blockRuntimeId" : 7828 + }, { "id" : "minecraft:iron_trapdoor", "blockRuntimeId" : 549 @@ -666,6 +697,14 @@ "id" : "minecraft:mangrove_slab", "blockRuntimeId" : 1772 }, + { + "id" : "minecraft:bamboo_slab", + "blockRuntimeId" : 10300 + }, + { + "id" : "minecraft:bamboo_mosaic_slab", + "blockRuntimeId" : 4081 + }, { "id" : "minecraft:stone_block_slab", "blockRuntimeId" : 6054 @@ -2707,6 +2746,9 @@ { "id" : "minecraft:trader_llama_spawn_egg" }, + { + "id" : "minecraft:camel_spawn_egg" + }, { "id" : "minecraft:ghast_spawn_egg" }, @@ -4073,6 +4115,10 @@ "id" : "minecraft:bookshelf", "blockRuntimeId" : 10443 }, + { + "id" : "minecraft:chiseled_bookshelf", + "blockRuntimeId" : 326 + }, { "id" : "minecraft:lectern", "blockRuntimeId" : 10718 @@ -4263,12 +4309,45 @@ { "id" : "minecraft:mangrove_sign" }, + { + "id" : "minecraft:bamboo_sign" + }, { "id" : "minecraft:crimson_sign" }, { "id" : "minecraft:warped_sign" }, + { + "id" : "minecraft:oak_hanging_sign" + }, + { + "id" : "minecraft:spruce_hanging_sign" + }, + { + "id" : "minecraft:birch_hanging_sign" + }, + { + "id" : "minecraft:jungle_hanging_sign" + }, + { + "id" : "minecraft:acacia_hanging_sign" + }, + { + "id" : "minecraft:dark_oak_hanging_sign" + }, + { + "id" : "minecraft:crimson_hanging_sign" + }, + { + "id" : "minecraft:warped_hanging_sign" + }, + { + "id" : "minecraft:mangrove_hanging_sign" + }, + { + "id" : "minecraft:bamboo_hanging_sign" + }, { "id" : "minecraft:painting" }, @@ -4979,6 +5058,9 @@ { "id" : "minecraft:mangrove_boat" }, + { + "id" : "minecraft:bamboo_raft" + }, { "id" : "minecraft:oak_chest_boat" }, @@ -5000,6 +5082,9 @@ { "id" : "minecraft:mangrove_chest_boat" }, + { + "id" : "minecraft:bamboo_chest_raft" + }, { "id" : "minecraft:rail", "blockRuntimeId" : 5697 @@ -5071,6 +5156,10 @@ "id" : "minecraft:mangrove_button", "blockRuntimeId" : 10840 }, + { + "id" : "minecraft:bamboo_button", + "blockRuntimeId" : 10238 + }, { "id" : "minecraft:stone_button", "blockRuntimeId" : 826 @@ -5119,6 +5208,10 @@ "id" : "minecraft:mangrove_pressure_plate", "blockRuntimeId" : 5646 }, + { + "id" : "minecraft:bamboo_pressure_plate", + "blockRuntimeId" : 9819 + }, { "id" : "minecraft:crimson_pressure_plate", "blockRuntimeId" : 12447 diff --git a/core/src/main/resources/bedrock/entity_identifiers.dat b/core/src/main/resources/bedrock/entity_identifiers.dat index 297d30537148f5cb80ecd63810b352f3e4c7ab03..8f0c9ce8ef8f468bf6b1f2b530a131b33f6023c2 100644 GIT binary patch delta 58 zcmV-A0LA~(JdZsM3IGWPX=H3^b94&w0h2)i7?aEk3$ZZX9g&a|lfVND3}az!Wo!cj Qa%p6g0+XQ(6O#@eL@PfMnE(I) delta 34 qcmeCTy=247#lXpynUa%PT*CE%ak3+$#N;#F+#3z<$xil`a{&OlNecr2 diff --git a/core/src/main/resources/mappings b/core/src/main/resources/mappings index 19094e360..5bd26dd73 160000 --- a/core/src/main/resources/mappings +++ b/core/src/main/resources/mappings @@ -1 +1 @@ -Subproject commit 19094e36080d78212534f5f0d073fb5d3f68a89f +Subproject commit 5bd26dd735bd89dd50e5c55a0d022f7c70916300 From cc3037d6c53400b4f1a33e7bc5e4be239e6f1331 Mon Sep 17 00:00:00 2001 From: Camotoy <20743703+Camotoy@users.noreply.github.com> Date: Fri, 2 Dec 2022 14:11:56 -0500 Subject: [PATCH 29/81] Update to 1.19.3-rc1; various changes and fixes --- .../manager/GeyserSpigotWorldManager.java | 10 +- .../org/geysermc/geyser/level/GameRule.java | 117 +++++++----------- .../geyser/level/GeyserWorldManager.java | 25 +--- .../geysermc/geyser/level/WorldManager.java | 16 ++- .../protocol/java/JavaCommandsTranslator.java | 33 +++-- .../entity/JavaSoundEntityTranslator.java | 2 +- .../JavaPlayerInfoRemoveTranslator.java | 4 +- .../JavaPlayerInfoUpdateTranslator.java | 66 +++++----- .../java/level/JavaCustomSoundTranslator.java | 49 -------- .../java/level/JavaSoundTranslator.java | 2 +- .../geysermc/geyser/util/SettingsUtils.java | 10 +- .../org/geysermc/geyser/util/SoundUtils.java | 28 ++--- 12 files changed, 134 insertions(+), 228 deletions(-) delete mode 100644 core/src/main/java/org/geysermc/geyser/translator/protocol/java/level/JavaCustomSoundTranslator.java 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 7bb8f1666..52f29dcfe 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 @@ -38,7 +38,7 @@ import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.meta.BookMeta; import org.bukkit.plugin.Plugin; import org.geysermc.geyser.level.GameRule; -import org.geysermc.geyser.level.GeyserWorldManager; +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; @@ -51,7 +51,7 @@ import java.util.List; /** * The base world manager to use when there is no supported NMS revision */ -public class GeyserSpigotWorldManager extends GeyserWorldManager { +public class GeyserSpigotWorldManager extends WorldManager { private final Plugin plugin; public GeyserSpigotWorldManager(Plugin plugin) { @@ -151,12 +151,12 @@ public class GeyserSpigotWorldManager extends GeyserWorldManager { return true; } - public Boolean getGameRuleBool(GeyserSession session, GameRule gameRule) { + public boolean getGameRuleBool(GeyserSession session, GameRule gameRule) { String value = Bukkit.getPlayer(session.getPlayerEntity().getUsername()).getWorld().getGameRuleValue(gameRule.getJavaID()); if (!value.isEmpty()) { return Boolean.parseBoolean(value); } - return (Boolean) gameRule.getDefaultValue(); + return gameRule.getDefaultBooleanValue(); } @Override @@ -165,7 +165,7 @@ public class GeyserSpigotWorldManager extends GeyserWorldManager { if (!value.isEmpty()) { return Integer.parseInt(value); } - return (int) gameRule.getDefaultValue(); + return gameRule.getDefaultIntValue(); } @Override diff --git a/core/src/main/java/org/geysermc/geyser/level/GameRule.java b/core/src/main/java/org/geysermc/geyser/level/GameRule.java index be647cff6..015f9c50c 100644 --- a/core/src/main/java/org/geysermc/geyser/level/GameRule.java +++ b/core/src/main/java/org/geysermc/geyser/level/GameRule.java @@ -32,43 +32,41 @@ import lombok.Getter; * It is used to construct the list for the settings menu */ public enum GameRule { - ANNOUNCEADVANCEMENTS("announceAdvancements", Boolean.class, true), // JE only - COMMANDBLOCKOUTPUT("commandBlockOutput", Boolean.class, true), - DISABLEELYTRAMOVEMENTCHECK("disableElytraMovementCheck", Boolean.class, false), // JE only - DISABLERAIDS("disableRaids", Boolean.class, false), // JE only - DODAYLIGHTCYCLE("doDaylightCycle", Boolean.class, true), - DOENTITYDROPS("doEntityDrops", Boolean.class, true), - DOFIRETICK("doFireTick", Boolean.class, true), - DOIMMEDIATERESPAWN("doImmediateRespawn", Boolean.class, false), - DOINSOMNIA("doInsomnia", Boolean.class, true), - DOLIMITEDCRAFTING("doLimitedCrafting", Boolean.class, false), // JE only - DOMOBLOOT("doMobLoot", Boolean.class, true), - DOMOBSPAWNING("doMobSpawning", Boolean.class, true), - DOPATROLSPAWNING("doPatrolSpawning", Boolean.class, true), // JE only - DOTILEDROPS("doTileDrops", Boolean.class, true), - DOTRADERSPAWNING("doTraderSpawning", Boolean.class, true), // JE only - DOWEATHERCYCLE("doWeatherCycle", Boolean.class, true), - DROWNINGDAMAGE("drowningDamage", Boolean.class, true), - FALLDAMAGE("fallDamage", Boolean.class, true), - FIREDAMAGE("fireDamage", Boolean.class, true), - FREEZEDAMAGE("freezeDamage", Boolean.class, true), - FORGIVEDEADPLAYERS("forgiveDeadPlayers", Boolean.class, true), // JE only - KEEPINVENTORY("keepInventory", Boolean.class, false), - LOGADMINCOMMANDS("logAdminCommands", Boolean.class, true), // JE only - MAXCOMMANDCHAINLENGTH("maxCommandChainLength", Integer.class, 65536), - MAXENTITYCRAMMING("maxEntityCramming", Integer.class, 24), // JE only - MOBGRIEFING("mobGriefing", Boolean.class, true), - NATURALREGENERATION("naturalRegeneration", Boolean.class, true), - PLAYERSSLEEPINGPERCENTAGE("playersSleepingPercentage", Integer.class, 100), // JE only - RANDOMTICKSPEED("randomTickSpeed", Integer.class, 3), - REDUCEDDEBUGINFO("reducedDebugInfo", Boolean.class, false), // JE only - SENDCOMMANDFEEDBACK("sendCommandFeedback", Boolean.class, true), - SHOWDEATHMESSAGES("showDeathMessages", Boolean.class, true), - SPAWNRADIUS("spawnRadius", Integer.class, 10), - SPECTATORSGENERATECHUNKS("spectatorsGenerateChunks", Boolean.class, true), // JE only - UNIVERSALANGER("universalAnger", Boolean.class, false), // JE only - - UNKNOWN("unknown", Object.class); + ANNOUNCEADVANCEMENTS("announceAdvancements", true), // JE only + COMMANDBLOCKOUTPUT("commandBlockOutput", true), + DISABLEELYTRAMOVEMENTCHECK("disableElytraMovementCheck", false), // JE only + DISABLERAIDS("disableRaids", false), // JE only + DODAYLIGHTCYCLE("doDaylightCycle", true), + DOENTITYDROPS("doEntityDrops", true), + DOFIRETICK("doFireTick", true), + DOIMMEDIATERESPAWN("doImmediateRespawn", false), + DOINSOMNIA("doInsomnia", true), + DOLIMITEDCRAFTING("doLimitedCrafting", false), // JE only + DOMOBLOOT("doMobLoot", true), + DOMOBSPAWNING("doMobSpawning", true), + DOPATROLSPAWNING("doPatrolSpawning", true), // JE only + DOTILEDROPS("doTileDrops", true), + DOTRADERSPAWNING("doTraderSpawning", true), // JE only + DOWEATHERCYCLE("doWeatherCycle", true), + DROWNINGDAMAGE("drowningDamage", true), + FALLDAMAGE("fallDamage", true), + FIREDAMAGE("fireDamage", true), + FREEZEDAMAGE("freezeDamage", true), + FORGIVEDEADPLAYERS("forgiveDeadPlayers", true), // JE only + KEEPINVENTORY("keepInventory", false), + LOGADMINCOMMANDS("logAdminCommands", true), // JE only + MAXCOMMANDCHAINLENGTH("maxCommandChainLength", 65536), + MAXENTITYCRAMMING("maxEntityCramming", 24), // JE only + MOBGRIEFING("mobGriefing", true), + NATURALREGENERATION("naturalRegeneration", true), + PLAYERSSLEEPINGPERCENTAGE("playersSleepingPercentage", 100), // JE only + RANDOMTICKSPEED("randomTickSpeed", 3), + REDUCEDDEBUGINFO("reducedDebugInfo", false), // JE only + SENDCOMMANDFEEDBACK("sendCommandFeedback", true), + SHOWDEATHMESSAGES("showDeathMessages", true), + SPAWNRADIUS("spawnRadius", 10), + SPECTATORSGENERATECHUNKS("spectatorsGenerateChunks", true), // JE only + UNIVERSALANGER("universalAnger", false); // JE only public static final GameRule[] VALUES = values(); @@ -78,48 +76,25 @@ public enum GameRule { @Getter private final Class type; - @Getter - private final Object defaultValue; + private final int defaultValue; - GameRule(String javaID, Class type) { - this(javaID, type, null); + GameRule(String javaID, boolean defaultValue) { + this.javaID = javaID; + this.type = Boolean.class; + this.defaultValue = defaultValue ? 1 : 0; } - GameRule(String javaID, Class type, Object defaultValue) { + GameRule(String javaID, int defaultValue) { this.javaID = javaID; - this.type = type; + this.type = Integer.class; this.defaultValue = defaultValue; } - /** - * Convert a string to an object of the correct type for the current gamerule - * - * @param value The string value to convert - * @return The converted and formatted value - */ - public Object convertValue(String value) { - if (type.equals(Boolean.class)) { - return Boolean.parseBoolean(value); - } else if (type.equals(Integer.class)) { - return Integer.parseInt(value); - } - - return null; + public boolean getDefaultBooleanValue() { + return defaultValue != 0; } - /** - * Fetch a game rule by the given Java ID - * - * @param id The ID of the gamerule - * @return A {@link GameRule} object representing the requested ID or {@link GameRule#UNKNOWN} - */ - public static GameRule fromJavaID(String id) { - for (GameRule gamerule : VALUES) { - if (gamerule.javaID.equals(id)) { - return gamerule; - } - } - - return UNKNOWN; + public int getDefaultIntValue() { + return defaultValue; } } 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 100917793..f19060c65 100644 --- a/core/src/main/java/org/geysermc/geyser/level/GeyserWorldManager.java +++ b/core/src/main/java/org/geysermc/geyser/level/GeyserWorldManager.java @@ -25,8 +25,6 @@ package org.geysermc.geyser.level; -import com.github.steveice10.mc.protocol.data.game.entity.player.GameMode; -import com.github.steveice10.mc.protocol.data.game.setting.Difficulty; import com.nukkitx.nbt.NbtMap; import com.nukkitx.nbt.NbtMapBuilder; import it.unimi.dsi.fastutil.objects.Object2ObjectMap; @@ -36,11 +34,8 @@ import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.session.cache.ChunkCache; import org.geysermc.geyser.translator.inventory.LecternInventoryTranslator; -import java.util.Locale; - public class GeyserWorldManager extends WorldManager { - - private static final Object2ObjectMap gameruleCache = new Object2ObjectOpenHashMap<>(); + private final Object2ObjectMap gameruleCache = new Object2ObjectOpenHashMap<>(); @Override public int getBlockAt(GeyserSession session, int x, int y, int z) { @@ -82,18 +77,18 @@ public class GeyserWorldManager extends WorldManager { @Override public void setGameRule(GeyserSession session, String name, Object value) { - session.sendCommand("gamerule " + name + " " + value); + super.setGameRule(session, name, value); gameruleCache.put(name, String.valueOf(value)); } @Override - public Boolean getGameRuleBool(GeyserSession session, GameRule gameRule) { + public boolean getGameRuleBool(GeyserSession session, GameRule gameRule) { String value = gameruleCache.get(gameRule.getJavaID()); if (value != null) { return Boolean.parseBoolean(value); } - return gameRule.getDefaultValue() != null ? (Boolean) gameRule.getDefaultValue() : false; + return gameRule.getDefaultBooleanValue(); } @Override @@ -103,17 +98,7 @@ public class GeyserWorldManager extends WorldManager { return Integer.parseInt(value); } - return gameRule.getDefaultValue() != null ? (int) gameRule.getDefaultValue() : 0; - } - - @Override - public void setPlayerGameMode(GeyserSession session, GameMode gameMode) { - session.sendCommand("gamemode " + gameMode.name().toLowerCase(Locale.ROOT)); - } - - @Override - public void setDifficulty(GeyserSession session, Difficulty difficulty) { - session.sendCommand("difficulty " + difficulty.name().toLowerCase(Locale.ROOT)); + return gameRule.getDefaultIntValue(); } @Override 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 69f5d5beb..b3a727d26 100644 --- a/core/src/main/java/org/geysermc/geyser/level/WorldManager.java +++ b/core/src/main/java/org/geysermc/geyser/level/WorldManager.java @@ -31,6 +31,8 @@ import com.nukkitx.math.vector.Vector3i; import com.nukkitx.nbt.NbtMap; import org.geysermc.geyser.session.GeyserSession; +import java.util.Locale; + /** * Class that manages or retrieves various information * from the world. Everything in this class should be @@ -105,7 +107,9 @@ public abstract class WorldManager { * @param name The gamerule to change * @param value The new value for the gamerule */ - public abstract void setGameRule(GeyserSession session, String name, Object value); + public void setGameRule(GeyserSession session, String name, Object value) { + session.sendCommand("gamerule " + name + " " + value); + } /** * Gets a gamerule value as a boolean @@ -114,7 +118,7 @@ public abstract class WorldManager { * @param gameRule The gamerule to fetch the value of * @return The boolean representation of the value */ - public abstract Boolean getGameRuleBool(GeyserSession session, GameRule gameRule); + public abstract boolean getGameRuleBool(GeyserSession session, GameRule gameRule); /** * Get a gamerule value as an integer @@ -131,7 +135,9 @@ public abstract class WorldManager { * @param session The session of the player to change the game mode of * @param gameMode The game mode to change the player to */ - public abstract void setPlayerGameMode(GeyserSession session, GameMode gameMode); + public void setPlayerGameMode(GeyserSession session, GameMode gameMode) { + session.sendCommand("gamemode " + gameMode.name().toLowerCase(Locale.ROOT)); + } /** * Change the difficulty of the Java server @@ -139,7 +145,9 @@ public abstract class WorldManager { * @param session The session of the user that requested the change * @param difficulty The difficulty to change to */ - public abstract void setDifficulty(GeyserSession session, Difficulty difficulty); + public void setDifficulty(GeyserSession session, Difficulty difficulty) { + session.sendCommand("difficulty " + difficulty.name().toLowerCase(Locale.ROOT)); + } /** * Checks if the given session's player has a permission 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 8f4e46454..14ff1a51a 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 @@ -234,18 +234,18 @@ public class JavaCommandsTranslator extends PacketTranslator CommandParam.OPERATOR; // ">=", "==", etc case BLOCK_STATE -> BlockRegistries.JAVA_TO_BEDROCK_IDENTIFIERS.get().keySet().toArray(new String[0]); case ITEM_STACK -> session.getItemMappings().getItemNames(); - case ITEM_ENCHANTMENT -> Enchantment.JavaEnchantment.ALL_JAVA_IDENTIFIERS; - case ENTITY_SUMMON -> Registries.JAVA_ENTITY_IDENTIFIERS.get().keySet().toArray(new String[0]); case COLOR -> VALID_COLORS; case SCOREBOARD_SLOT -> VALID_SCOREBOARD_SLOTS; - case MOB_EFFECT -> ALL_EFFECT_IDENTIFIERS; case RESOURCE, RESOURCE_OR_TAG -> { String resource = ((ResourceProperties) node.getProperties()).getRegistryKey(); - if (resource.equals("minecraft:attribute")) { - yield ATTRIBUTES; - } else { - yield CommandParam.STRING; - } + yield switch (resource) { + // minecraft:worldgen/biome is also valid but we currently don't cache biome IDs + case "minecraft:attribute" -> ATTRIBUTES; + case "minecraft:enchantment" -> Enchantment.JavaEnchantment.ALL_JAVA_IDENTIFIERS; + case "minecraft:entity_type" -> Registries.JAVA_ENTITY_IDENTIFIERS.get().keySet().toArray(new String[0]); + case "minecraft:mob_effect" -> ALL_EFFECT_IDENTIFIERS; + default -> CommandParam.STRING; + }; } default -> CommandParam.STRING; }; @@ -325,7 +325,7 @@ public class JavaCommandsTranslator extends PacketTranslator diff --git a/core/src/main/java/org/geysermc/geyser/translator/protocol/java/entity/JavaSoundEntityTranslator.java b/core/src/main/java/org/geysermc/geyser/translator/protocol/java/entity/JavaSoundEntityTranslator.java index 06f141aa6..68f310db4 100644 --- a/core/src/main/java/org/geysermc/geyser/translator/protocol/java/entity/JavaSoundEntityTranslator.java +++ b/core/src/main/java/org/geysermc/geyser/translator/protocol/java/entity/JavaSoundEntityTranslator.java @@ -40,6 +40,6 @@ public class JavaSoundEntityTranslator extends PacketTranslator + // We'll send our own PlayerListEntry in requestAndHandleSkinAndCape + // But we need to send other player's entries so they show up in the player list + // without processing their skin information - that'll be processed when they spawn in + if (self) { + SkinManager.requestAndHandleSkinAndCape(playerEntity, session, skinAndCape -> GeyserImpl.getInstance().getLogger().debug("Loaded Local Bedrock Java Skin Data for " + session.getClientData().getUsername())); - } else { - playerEntity.setValid(true); - PlayerListPacket.Entry playerListEntry = SkinManager.buildCachedEntry(session, playerEntity); + } else { + playerEntity.setValid(true); + PlayerListPacket.Entry playerListEntry = SkinManager.buildCachedEntry(session, playerEntity); - translate.getEntries().add(playerListEntry); - } + translate.getEntries().add(playerListEntry); } } diff --git a/core/src/main/java/org/geysermc/geyser/translator/protocol/java/level/JavaCustomSoundTranslator.java b/core/src/main/java/org/geysermc/geyser/translator/protocol/java/level/JavaCustomSoundTranslator.java deleted file mode 100644 index 00894bd8b..000000000 --- a/core/src/main/java/org/geysermc/geyser/translator/protocol/java/level/JavaCustomSoundTranslator.java +++ /dev/null @@ -1,49 +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.java.level; - -import com.github.steveice10.mc.protocol.packet.ingame.clientbound.level.ClientboundCustomSoundPacket; -import com.nukkitx.math.vector.Vector3f; -import com.nukkitx.protocol.bedrock.packet.PlaySoundPacket; -import org.geysermc.geyser.session.GeyserSession; -import org.geysermc.geyser.translator.protocol.PacketTranslator; -import org.geysermc.geyser.translator.protocol.Translator; -import org.geysermc.geyser.util.SoundUtils; - -@Translator(packet = ClientboundCustomSoundPacket.class) -public class JavaCustomSoundTranslator extends PacketTranslator { - - @Override - public void translate(GeyserSession session, ClientboundCustomSoundPacket packet) { - PlaySoundPacket playSoundPacket = new PlaySoundPacket(); - playSoundPacket.setSound(SoundUtils.translatePlaySound(packet.getSound())); - playSoundPacket.setPosition(Vector3f.from(packet.getX(), packet.getY(), packet.getZ())); - playSoundPacket.setVolume(packet.getVolume()); - playSoundPacket.setPitch(packet.getPitch()); - - session.sendUpstreamPacket(playSoundPacket); - } -} diff --git a/core/src/main/java/org/geysermc/geyser/translator/protocol/java/level/JavaSoundTranslator.java b/core/src/main/java/org/geysermc/geyser/translator/protocol/java/level/JavaSoundTranslator.java index 5b83ff551..8bd1d94d4 100644 --- a/core/src/main/java/org/geysermc/geyser/translator/protocol/java/level/JavaSoundTranslator.java +++ b/core/src/main/java/org/geysermc/geyser/translator/protocol/java/level/JavaSoundTranslator.java @@ -38,6 +38,6 @@ public class JavaSoundTranslator extends PacketTranslator Date: Sat, 3 Dec 2022 17:20:33 -0500 Subject: [PATCH 30/81] Revert "Drop anything below 1.19.50" This reverts commit 58eede37 --- .../geysermc/geyser/network/GameProtocol.java | 18 + .../populator/BlockRegistryPopulator.java | 3 + .../populator/ItemRegistryPopulator.java | 1 + .../BedrockRequestAbilityTranslator.java | 5 + .../geysermc/geyser/util/DimensionUtils.java | 18 +- .../bedrock/block_palette.1_19_20.nbt | Bin 0 -> 55132 bytes .../bedrock/creative_items.1_19_20.json | 5440 +++++++++++++++++ .../bedrock/runtime_item_states.1_19_20.json | 4530 ++++++++++++++ 8 files changed, 10007 insertions(+), 8 deletions(-) create mode 100644 core/src/main/resources/bedrock/block_palette.1_19_20.nbt create mode 100644 core/src/main/resources/bedrock/creative_items.1_19_20.json create mode 100644 core/src/main/resources/bedrock/runtime_item_states.1_19_20.json 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 56159cfa8..e85dc689d 100644 --- a/core/src/main/java/org/geysermc/geyser/network/GameProtocol.java +++ b/core/src/main/java/org/geysermc/geyser/network/GameProtocol.java @@ -60,6 +60,14 @@ 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()); + SUPPORTED_BEDROCK_CODECS.add(Bedrock_v557.V557_CODEC); SUPPORTED_BEDROCK_CODECS.add(DEFAULT_BEDROCK_CODEC); } @@ -77,6 +85,16 @@ public final class GameProtocol { return null; } + /* 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(); + } + /** * Gets the {@link PacketCodec} for Minecraft: Java Edition. * 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 72116f548..cbab03990 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 @@ -29,6 +29,8 @@ import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.node.ArrayNode; import com.google.common.collect.ImmutableMap; import com.nukkitx.nbt.*; +import com.nukkitx.protocol.bedrock.v527.Bedrock_v527; +import com.nukkitx.protocol.bedrock.v544.Bedrock_v544; import com.nukkitx.protocol.bedrock.v560.Bedrock_v560; import it.unimi.dsi.fastutil.ints.IntOpenHashSet; import it.unimi.dsi.fastutil.ints.IntSet; @@ -72,6 +74,7 @@ public final class BlockRegistryPopulator { private static void registerBedrockBlocks() { BiFunction emptyMapper = (bedrockIdentifier, statesBuilder) -> null; ImmutableMap, BiFunction> blockMappers = ImmutableMap., BiFunction>builder() + .put(ObjectIntPair.of("1_19_20", Bedrock_v544.V544_CODEC.getProtocolVersion()), emptyMapper) .put(ObjectIntPair.of("1_19_50", Bedrock_v560.V560_CODEC.getProtocolVersion()), emptyMapper) .build(); 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 645cf17ba..4b218aa7d 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 @@ -77,6 +77,7 @@ public class ItemRegistryPopulator { public static void populate() { Map paletteVersions = new Object2ObjectOpenHashMap<>(); + 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())); GeyserBootstrap bootstrap = GeyserImpl.getInstance().getBootstrap(); 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 44953dfda..fe8150d40 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 @@ -43,6 +43,11 @@ public class BedrockRequestAbilityTranslator extends PacketTranslatorX5GfPypkF`Ueo`Ex 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

N!K?VWkIdC++V5CX^Dc;QTi}}9T=lerttP4d#b zb@t-=s{f05Iog>R1Xnsx(kBjuLM1(5y> z0`qrk9IcJ3+X^bauWCFyv*Q|9W3$obUsIWO6GmRVc&~n)wYRmd-o(d};}KuUSvr_< zo?aev3X z@?O();jB+SQW0T>*lBV|#ERayqI6_69C>_wlrJ2B*~T_zyVA)LGk_H(QOpU&q^aPc zgIaaCmZOZhK#J7-|J%$y!aAdFvA{53{F3{;m8ORSjLa;Yd0HZz1AiZcz$hmDM`Awa zn{-Jc@GLf5N>k!DrZJRc<1Di;v;f9x^n#gwRmlZFpeT_!p?pUzZBF&mka*oEPSx^3 z%4?=6**e=DBe_)h1LL%53-I%rtiTCBzA~3~2^a{$*?Ucsrhl{dEK;Yra)wkk6D;f` zTGJY90D-s%PIKUCK4_XQuZ!y-86EpCGadf7nMeO+rrdvH=H-8HrUaJBX=-j)FB5`v zQIKt9Qv^8%>R&&^`QnSxu&Fp5}1Li)>l8YTv9@r0ZJG;P5AP^?cZ9k;c1oTm@6?}pJ zIzgZEZyyB>sSSb4mB%**{(%8@LZt)k{ASHq_V=ML1%3ZzX666fOlaPgjik=}JWRvd z?+W4I@6-2dtjAl;)4`RdKUK2&n$d0piAM?yg}^}O7o^ZzRG4s>o2^<>lpu37+`n>F zuqyr-85+24$wO9Q)v)eIJ?^yZpk^agqM~c~^5}C*Mt3)pwabX8U4Nd__3uP7Z->8O zKwPFCId^Pi0`xyHR#^k9M&`)F7Hs@kBQ5%K94Lx^#YN~G4y%nvc$ur@K>|$EG zAKNdmu)?UQPP2YS zU=&B@Ra9zNLe2ROO+n^5G1=p$??b{bYTsXUWu8g(qoWFqbDVo8GMsqN@FsYKVTl z8lK9|?b-#)Zwz9VH%lp}R{E-1_D8c(moRQtfWn;P2kME5d;EbqxtsOQ1YUdN?gTlp vX!0f2XX*s~rfjqL4L=}#m+!XCdF;?V!=^KE4f37S^XE`*=^ZZ(bcp{2Ge*tJ literal 0 HcmV?d00001 diff --git a/core/src/main/resources/bedrock/creative_items.1_19_20.json b/core/src/main/resources/bedrock/creative_items.1_19_20.json new file mode 100644 index 000000000..98d9e007a --- /dev/null +++ b/core/src/main/resources/bedrock/creative_items.1_19_20.json @@ -0,0 +1,5440 @@ +{ + "items" : [ + { + "id" : "minecraft:planks", + "blockRuntimeId" : 6073 + }, + { + "id" : "minecraft:planks", + "blockRuntimeId" : 6074 + }, + { + "id" : "minecraft:planks", + "blockRuntimeId" : 6075 + }, + { + "id" : "minecraft:planks", + "blockRuntimeId" : 6076 + }, + { + "id" : "minecraft:planks", + "blockRuntimeId" : 6077 + }, + { + "id" : "minecraft:planks", + "blockRuntimeId" : 6078 + }, + { + "id" : "minecraft:mangrove_planks", + "blockRuntimeId" : 949 + }, + { + "id" : "minecraft:crimson_planks", + "blockRuntimeId" : 4852 + }, + { + "id" : "minecraft:warped_planks", + "blockRuntimeId" : 922 + }, + { + "id" : "minecraft:cobblestone_wall", + "blockRuntimeId" : 1184 + }, + { + "id" : "minecraft:cobblestone_wall", + "blockRuntimeId" : 1185 + }, + { + "id" : "minecraft:cobblestone_wall", + "blockRuntimeId" : 1186 + }, + { + "id" : "minecraft:cobblestone_wall", + "blockRuntimeId" : 1187 + }, + { + "id" : "minecraft:cobblestone_wall", + "blockRuntimeId" : 1188 + }, + { + "id" : "minecraft:cobblestone_wall", + "blockRuntimeId" : 1189 + }, + { + "id" : "minecraft:cobblestone_wall", + "blockRuntimeId" : 1196 + }, + { + "id" : "minecraft:cobblestone_wall", + "blockRuntimeId" : 1191 + }, + { + "id" : "minecraft:cobblestone_wall", + "blockRuntimeId" : 1192 + }, + { + "id" : "minecraft:cobblestone_wall", + "blockRuntimeId" : 1190 + }, + { + "id" : "minecraft:cobblestone_wall", + "blockRuntimeId" : 1193 + }, + { + "id" : "minecraft:cobblestone_wall", + "blockRuntimeId" : 1197 + }, + { + "id" : "minecraft:cobblestone_wall", + "blockRuntimeId" : 1194 + }, + { + "id" : "minecraft:cobblestone_wall", + "blockRuntimeId" : 1195 + }, + { + "id" : "minecraft:blackstone_wall", + "blockRuntimeId" : 3932 + }, + { + "id" : "minecraft:polished_blackstone_wall", + "blockRuntimeId" : 6726 + }, + { + "id" : "minecraft:polished_blackstone_brick_wall", + "blockRuntimeId" : 973 + }, + { + "id" : "minecraft:cobbled_deepslate_wall", + "blockRuntimeId" : 8084 + }, + { + "id" : "minecraft:deepslate_tile_wall", + "blockRuntimeId" : 5073 + }, + { + "id" : "minecraft:polished_deepslate_wall", + "blockRuntimeId" : 7819 + }, + { + "id" : "minecraft:deepslate_brick_wall", + "blockRuntimeId" : 431 + }, + { + "id" : "minecraft:mud_brick_wall", + "blockRuntimeId" : 732 + }, + { + "id" : "minecraft:fence", + "blockRuntimeId" : 7366 + }, + { + "id" : "minecraft:fence", + "blockRuntimeId" : 7367 + }, + { + "id" : "minecraft:fence", + "blockRuntimeId" : 7368 + }, + { + "id" : "minecraft:fence", + "blockRuntimeId" : 7369 + }, + { + "id" : "minecraft:fence", + "blockRuntimeId" : 7370 + }, + { + "id" : "minecraft:fence", + "blockRuntimeId" : 7371 + }, + { + "id" : "minecraft:mangrove_fence", + "blockRuntimeId" : 6635 + }, + { + "id" : "minecraft:nether_brick_fence", + "blockRuntimeId" : 4292 + }, + { + "id" : "minecraft:crimson_fence", + "blockRuntimeId" : 7998 + }, + { + "id" : "minecraft:warped_fence", + "blockRuntimeId" : 5855 + }, + { + "id" : "minecraft:fence_gate", + "blockRuntimeId" : 76 + }, + { + "id" : "minecraft:spruce_fence_gate", + "blockRuntimeId" : 6586 + }, + { + "id" : "minecraft:birch_fence_gate", + "blockRuntimeId" : 3779 + }, + { + "id" : "minecraft:jungle_fence_gate", + "blockRuntimeId" : 5367 + }, + { + "id" : "minecraft:acacia_fence_gate", + "blockRuntimeId" : 7588 + }, + { + "id" : "minecraft:dark_oak_fence_gate", + "blockRuntimeId" : 4175 + }, + { + "id" : "minecraft:mangrove_fence_gate", + "blockRuntimeId" : 4627 + }, + { + "id" : "minecraft:crimson_fence_gate", + "blockRuntimeId" : 4663 + }, + { + "id" : "minecraft:warped_fence_gate", + "blockRuntimeId" : 5401 + }, + { + "id" : "minecraft:normal_stone_stairs", + "blockRuntimeId" : 635 + }, + { + "id" : "minecraft:stone_stairs", + "blockRuntimeId" : 3710 + }, + { + "id" : "minecraft:mossy_cobblestone_stairs", + "blockRuntimeId" : 4094 + }, + { + "id" : "minecraft:oak_stairs", + "blockRuntimeId" : 273 + }, + { + "id" : "minecraft:spruce_stairs", + "blockRuntimeId" : 128 + }, + { + "id" : "minecraft:birch_stairs", + "blockRuntimeId" : 7005 + }, + { + "id" : "minecraft:jungle_stairs", + "blockRuntimeId" : 6969 + }, + { + "id" : "minecraft:acacia_stairs", + "blockRuntimeId" : 6202 + }, + { + "id" : "minecraft:dark_oak_stairs", + "blockRuntimeId" : 5065 + }, + { + "id" : "minecraft:mangrove_stairs", + "blockRuntimeId" : 4597 + }, + { + "id" : "minecraft:stone_brick_stairs", + "blockRuntimeId" : 933 + }, + { + "id" : "minecraft:mossy_stone_brick_stairs", + "blockRuntimeId" : 5885 + }, + { + "id" : "minecraft:sandstone_stairs", + "blockRuntimeId" : 3589 + }, + { + "id" : "minecraft:smooth_sandstone_stairs", + "blockRuntimeId" : 3629 + }, + { + "id" : "minecraft:red_sandstone_stairs", + "blockRuntimeId" : 5352 + }, + { + "id" : "minecraft:smooth_red_sandstone_stairs", + "blockRuntimeId" : 5548 + }, + { + "id" : "minecraft:granite_stairs", + "blockRuntimeId" : 3539 + }, + { + "id" : "minecraft:polished_granite_stairs", + "blockRuntimeId" : 4152 + }, + { + "id" : "minecraft:diorite_stairs", + "blockRuntimeId" : 4393 + }, + { + "id" : "minecraft:polished_diorite_stairs", + "blockRuntimeId" : 6716 + }, + { + "id" : "minecraft:andesite_stairs", + "blockRuntimeId" : 5310 + }, + { + "id" : "minecraft:polished_andesite_stairs", + "blockRuntimeId" : 7030 + }, + { + "id" : "minecraft:brick_stairs", + "blockRuntimeId" : 6532 + }, + { + "id" : "minecraft:nether_brick_stairs", + "blockRuntimeId" : 106 + }, + { + "id" : "minecraft:red_nether_brick_stairs", + "blockRuntimeId" : 6604 + }, + { + "id" : "minecraft:end_brick_stairs", + "blockRuntimeId" : 6384 + }, + { + "id" : "minecraft:quartz_stairs", + "blockRuntimeId" : 4769 + }, + { + "id" : "minecraft:smooth_quartz_stairs", + "blockRuntimeId" : 7702 + }, + { + "id" : "minecraft:purpur_stairs", + "blockRuntimeId" : 7757 + }, + { + "id" : "minecraft:prismarine_stairs", + "blockRuntimeId" : 7265 + }, + { + "id" : "minecraft:dark_prismarine_stairs", + "blockRuntimeId" : 7432 + }, + { + "id" : "minecraft:prismarine_bricks_stairs", + "blockRuntimeId" : 206 + }, + { + "id" : "minecraft:crimson_stairs", + "blockRuntimeId" : 6282 + }, + { + "id" : "minecraft:warped_stairs", + "blockRuntimeId" : 3720 + }, + { + "id" : "minecraft:blackstone_stairs", + "blockRuntimeId" : 7021 + }, + { + "id" : "minecraft:polished_blackstone_stairs", + "blockRuntimeId" : 4299 + }, + { + "id" : "minecraft:polished_blackstone_brick_stairs", + "blockRuntimeId" : 4479 + }, + { + "id" : "minecraft:cut_copper_stairs", + "blockRuntimeId" : 4606 + }, + { + "id" : "minecraft:exposed_cut_copper_stairs", + "blockRuntimeId" : 4589 + }, + { + "id" : "minecraft:weathered_cut_copper_stairs", + "blockRuntimeId" : 4307 + }, + { + "id" : "minecraft:oxidized_cut_copper_stairs", + "blockRuntimeId" : 353 + }, + { + "id" : "minecraft:waxed_cut_copper_stairs", + "blockRuntimeId" : 395 + }, + { + "id" : "minecraft:waxed_exposed_cut_copper_stairs", + "blockRuntimeId" : 3904 + }, + { + "id" : "minecraft:waxed_weathered_cut_copper_stairs", + "blockRuntimeId" : 6169 + }, + { + "id" : "minecraft:waxed_oxidized_cut_copper_stairs", + "blockRuntimeId" : 5842 + }, + { + "id" : "minecraft:cobbled_deepslate_stairs", + "blockRuntimeId" : 147 + }, + { + "id" : "minecraft:deepslate_tile_stairs", + "blockRuntimeId" : 4655 + }, + { + "id" : "minecraft:polished_deepslate_stairs", + "blockRuntimeId" : 294 + }, + { + "id" : "minecraft:deepslate_brick_stairs", + "blockRuntimeId" : 7424 + }, + { + "id" : "minecraft:mud_brick_stairs", + "blockRuntimeId" : 5524 + }, + { + "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" : 229 + }, + { + "id" : "minecraft:spruce_trapdoor", + "blockRuntimeId" : 6554 + }, + { + "id" : "minecraft:birch_trapdoor", + "blockRuntimeId" : 6652 + }, + { + "id" : "minecraft:jungle_trapdoor", + "blockRuntimeId" : 5383 + }, + { + "id" : "minecraft:acacia_trapdoor", + "blockRuntimeId" : 5591 + }, + { + "id" : "minecraft:dark_oak_trapdoor", + "blockRuntimeId" : 7504 + }, + { + "id" : "minecraft:mangrove_trapdoor", + "blockRuntimeId" : 4487 + }, + { + "id" : "minecraft:iron_trapdoor", + "blockRuntimeId" : 321 + }, + { + "id" : "minecraft:crimson_trapdoor", + "blockRuntimeId" : 4335 + }, + { + "id" : "minecraft:warped_trapdoor", + "blockRuntimeId" : 4735 + }, + { + "id" : "minecraft:iron_bars", + "blockRuntimeId" : 4803 + }, + { + "id" : "minecraft:glass", + "blockRuntimeId" : 6166 + }, + { + "id" : "minecraft:stained_glass", + "blockRuntimeId" : 1135 + }, + { + "id" : "minecraft:stained_glass", + "blockRuntimeId" : 1143 + }, + { + "id" : "minecraft:stained_glass", + "blockRuntimeId" : 1142 + }, + { + "id" : "minecraft:stained_glass", + "blockRuntimeId" : 1150 + }, + { + "id" : "minecraft:stained_glass", + "blockRuntimeId" : 1147 + }, + { + "id" : "minecraft:stained_glass", + "blockRuntimeId" : 1149 + }, + { + "id" : "minecraft:stained_glass", + "blockRuntimeId" : 1136 + }, + { + "id" : "minecraft:stained_glass", + "blockRuntimeId" : 1139 + }, + { + "id" : "minecraft:stained_glass", + "blockRuntimeId" : 1140 + }, + { + "id" : "minecraft:stained_glass", + "blockRuntimeId" : 1148 + }, + { + "id" : "minecraft:stained_glass", + "blockRuntimeId" : 1144 + }, + { + "id" : "minecraft:stained_glass", + "blockRuntimeId" : 1138 + }, + { + "id" : "minecraft:stained_glass", + "blockRuntimeId" : 1146 + }, + { + "id" : "minecraft:stained_glass", + "blockRuntimeId" : 1145 + }, + { + "id" : "minecraft:stained_glass", + "blockRuntimeId" : 1137 + }, + { + "id" : "minecraft:stained_glass", + "blockRuntimeId" : 1141 + }, + { + "id" : "minecraft:tinted_glass", + "blockRuntimeId" : 5977 + }, + { + "id" : "minecraft:glass_pane", + "blockRuntimeId" : 5235 + }, + { + "id" : "minecraft:stained_glass_pane", + "blockRuntimeId" : 4854 + }, + { + "id" : "minecraft:stained_glass_pane", + "blockRuntimeId" : 4862 + }, + { + "id" : "minecraft:stained_glass_pane", + "blockRuntimeId" : 4861 + }, + { + "id" : "minecraft:stained_glass_pane", + "blockRuntimeId" : 4869 + }, + { + "id" : "minecraft:stained_glass_pane", + "blockRuntimeId" : 4866 + }, + { + "id" : "minecraft:stained_glass_pane", + "blockRuntimeId" : 4868 + }, + { + "id" : "minecraft:stained_glass_pane", + "blockRuntimeId" : 4855 + }, + { + "id" : "minecraft:stained_glass_pane", + "blockRuntimeId" : 4858 + }, + { + "id" : "minecraft:stained_glass_pane", + "blockRuntimeId" : 4859 + }, + { + "id" : "minecraft:stained_glass_pane", + "blockRuntimeId" : 4867 + }, + { + "id" : "minecraft:stained_glass_pane", + "blockRuntimeId" : 4863 + }, + { + "id" : "minecraft:stained_glass_pane", + "blockRuntimeId" : 4857 + }, + { + "id" : "minecraft:stained_glass_pane", + "blockRuntimeId" : 4865 + }, + { + "id" : "minecraft:stained_glass_pane", + "blockRuntimeId" : 4864 + }, + { + "id" : "minecraft:stained_glass_pane", + "blockRuntimeId" : 4856 + }, + { + "id" : "minecraft:stained_glass_pane", + "blockRuntimeId" : 4860 + }, + { + "id" : "minecraft:ladder", + "blockRuntimeId" : 8264 + }, + { + "id" : "minecraft:scaffolding", + "blockRuntimeId" : 3573 + }, + { + "id" : "minecraft:stone_block_slab", + "blockRuntimeId" : 4272 + }, + { + "id" : "minecraft:stone_block_slab4", + "blockRuntimeId" : 5824 + }, + { + "id" : "minecraft:stone_block_slab", + "blockRuntimeId" : 4275 + }, + { + "id" : "minecraft:stone_block_slab2", + "blockRuntimeId" : 5795 + }, + { + "id" : "minecraft:wooden_slab", + "blockRuntimeId" : 5272 + }, + { + "id" : "minecraft:wooden_slab", + "blockRuntimeId" : 5273 + }, + { + "id" : "minecraft:wooden_slab", + "blockRuntimeId" : 5274 + }, + { + "id" : "minecraft:wooden_slab", + "blockRuntimeId" : 5275 + }, + { + "id" : "minecraft:wooden_slab", + "blockRuntimeId" : 5276 + }, + { + "id" : "minecraft:wooden_slab", + "blockRuntimeId" : 5277 + }, + { + "id" : "minecraft:mangrove_slab", + "blockRuntimeId" : 1151 + }, + { + "id" : "minecraft:stone_block_slab", + "blockRuntimeId" : 4277 + }, + { + "id" : "minecraft:stone_block_slab4", + "blockRuntimeId" : 5822 + }, + { + "id" : "minecraft:stone_block_slab", + "blockRuntimeId" : 4273 + }, + { + "id" : "minecraft:stone_block_slab4", + "blockRuntimeId" : 5825 + }, + { + "id" : "minecraft:stone_block_slab2", + "blockRuntimeId" : 5796 + }, + { + "id" : "minecraft:stone_block_slab2", + "blockRuntimeId" : 5790 + }, + { + "id" : "minecraft:stone_block_slab4", + "blockRuntimeId" : 5826 + }, + { + "id" : "minecraft:stone_block_slab3", + "blockRuntimeId" : 5807 + }, + { + "id" : "minecraft:stone_block_slab3", + "blockRuntimeId" : 5812 + }, + { + "id" : "minecraft:stone_block_slab3", + "blockRuntimeId" : 5813 + }, + { + "id" : "minecraft:stone_block_slab3", + "blockRuntimeId" : 5810 + }, + { + "id" : "minecraft:stone_block_slab3", + "blockRuntimeId" : 5811 + }, + { + "id" : "minecraft:stone_block_slab3", + "blockRuntimeId" : 5809 + }, + { + "id" : "minecraft:stone_block_slab3", + "blockRuntimeId" : 5808 + }, + { + "id" : "minecraft:stone_block_slab", + "blockRuntimeId" : 4276 + }, + { + "id" : "minecraft:stone_block_slab", + "blockRuntimeId" : 4279 + }, + { + "id" : "minecraft:stone_block_slab2", + "blockRuntimeId" : 5797 + }, + { + "id" : "minecraft:stone_block_slab3", + "blockRuntimeId" : 5806 + }, + { + "id" : "minecraft:stone_block_slab", + "blockRuntimeId" : 4278 + }, + { + "id" : "minecraft:stone_block_slab4", + "blockRuntimeId" : 5823 + }, + { + "id" : "minecraft:stone_block_slab2", + "blockRuntimeId" : 5791 + }, + { + "id" : "minecraft:stone_block_slab2", + "blockRuntimeId" : 5792 + }, + { + "id" : "minecraft:stone_block_slab2", + "blockRuntimeId" : 5793 + }, + { + "id" : "minecraft:stone_block_slab2", + "blockRuntimeId" : 5794 + }, + { + "id" : "minecraft:crimson_slab", + "blockRuntimeId" : 5902 + }, + { + "id" : "minecraft:warped_slab", + "blockRuntimeId" : 6486 + }, + { + "id" : "minecraft:blackstone_slab", + "blockRuntimeId" : 912 + }, + { + "id" : "minecraft:polished_blackstone_slab", + "blockRuntimeId" : 6020 + }, + { + "id" : "minecraft:polished_blackstone_brick_slab", + "blockRuntimeId" : 4194 + }, + { + "id" : "minecraft:cut_copper_slab", + "blockRuntimeId" : 5237 + }, + { + "id" : "minecraft:exposed_cut_copper_slab", + "blockRuntimeId" : 6602 + }, + { + "id" : "minecraft:weathered_cut_copper_slab", + "blockRuntimeId" : 6055 + }, + { + "id" : "minecraft:oxidized_cut_copper_slab", + "blockRuntimeId" : 5284 + }, + { + "id" : "minecraft:waxed_cut_copper_slab", + "blockRuntimeId" : 7817 + }, + { + "id" : "minecraft:waxed_exposed_cut_copper_slab", + "blockRuntimeId" : 249 + }, + { + "id" : "minecraft:waxed_weathered_cut_copper_slab", + "blockRuntimeId" : 6547 + }, + { + "id" : "minecraft:waxed_oxidized_cut_copper_slab", + "blockRuntimeId" : 710 + }, + { + "id" : "minecraft:cobbled_deepslate_slab", + "blockRuntimeId" : 7312 + }, + { + "id" : "minecraft:polished_deepslate_slab", + "blockRuntimeId" : 288 + }, + { + "id" : "minecraft:deepslate_tile_slab", + "blockRuntimeId" : 4293 + }, + { + "id" : "minecraft:deepslate_brick_slab", + "blockRuntimeId" : 3718 + }, + { + "id" : "minecraft:mud_brick_slab", + "blockRuntimeId" : 3912 + }, + { + "id" : "minecraft:brick_block", + "blockRuntimeId" : 4767 + }, + { + "id" : "minecraft:chiseled_nether_bricks", + "blockRuntimeId" : 7251 + }, + { + "id" : "minecraft:cracked_nether_bricks", + "blockRuntimeId" : 4554 + }, + { + "id" : "minecraft:quartz_bricks", + "blockRuntimeId" : 6353 + }, + { + "id" : "minecraft:stonebrick", + "blockRuntimeId" : 6549 + }, + { + "id" : "minecraft:stonebrick", + "blockRuntimeId" : 6550 + }, + { + "id" : "minecraft:stonebrick", + "blockRuntimeId" : 6551 + }, + { + "id" : "minecraft:stonebrick", + "blockRuntimeId" : 6552 + }, + { + "id" : "minecraft:end_bricks", + "blockRuntimeId" : 281 + }, + { + "id" : "minecraft:prismarine", + "blockRuntimeId" : 6089 + }, + { + "id" : "minecraft:polished_blackstone_bricks", + "blockRuntimeId" : 4682 + }, + { + "id" : "minecraft:cracked_polished_blackstone_bricks", + "blockRuntimeId" : 7216 + }, + { + "id" : "minecraft:gilded_blackstone", + "blockRuntimeId" : 4588 + }, + { + "id" : "minecraft:chiseled_polished_blackstone", + "blockRuntimeId" : 5064 + }, + { + "id" : "minecraft:deepslate_tiles", + "blockRuntimeId" : 4583 + }, + { + "id" : "minecraft:cracked_deepslate_tiles", + "blockRuntimeId" : 4162 + }, + { + "id" : "minecraft:deepslate_bricks", + "blockRuntimeId" : 5466 + }, + { + "id" : "minecraft:cracked_deepslate_bricks", + "blockRuntimeId" : 5366 + }, + { + "id" : "minecraft:chiseled_deepslate", + "blockRuntimeId" : 5236 + }, + { + "id" : "minecraft:cobblestone", + "blockRuntimeId" : 3617 + }, + { + "id" : "minecraft:mossy_cobblestone", + "blockRuntimeId" : 252 + }, + { + "id" : "minecraft:cobbled_deepslate", + "blockRuntimeId" : 6672 + }, + { + "id" : "minecraft:smooth_stone", + "blockRuntimeId" : 4584 + }, + { + "id" : "minecraft:sandstone", + "blockRuntimeId" : 3655 + }, + { + "id" : "minecraft:sandstone", + "blockRuntimeId" : 3656 + }, + { + "id" : "minecraft:sandstone", + "blockRuntimeId" : 3657 + }, + { + "id" : "minecraft:sandstone", + "blockRuntimeId" : 3658 + }, + { + "id" : "minecraft:red_sandstone", + "blockRuntimeId" : 6582 + }, + { + "id" : "minecraft:red_sandstone", + "blockRuntimeId" : 6583 + }, + { + "id" : "minecraft:red_sandstone", + "blockRuntimeId" : 6584 + }, + { + "id" : "minecraft:red_sandstone", + "blockRuntimeId" : 6585 + }, + { + "id" : "minecraft:coal_block", + "blockRuntimeId" : 5400 + }, + { + "id" : "minecraft:dried_kelp_block", + "blockRuntimeId" : 7981 + }, + { + "id" : "minecraft:gold_block", + "blockRuntimeId" : 291 + }, + { + "id" : "minecraft:iron_block", + "blockRuntimeId" : 8263 + }, + { + "id" : "minecraft:copper_block", + "blockRuntimeId" : 4653 + }, + { + "id" : "minecraft:exposed_copper", + "blockRuntimeId" : 595 + }, + { + "id" : "minecraft:weathered_copper", + "blockRuntimeId" : 8248 + }, + { + "id" : "minecraft:oxidized_copper", + "blockRuntimeId" : 3555 + }, + { + "id" : "minecraft:waxed_copper", + "blockRuntimeId" : 7736 + }, + { + "id" : "minecraft:waxed_exposed_copper", + "blockRuntimeId" : 696 + }, + { + "id" : "minecraft:waxed_weathered_copper", + "blockRuntimeId" : 709 + }, + { + "id" : "minecraft:waxed_oxidized_copper", + "blockRuntimeId" : 7544 + }, + { + "id" : "minecraft:cut_copper", + "blockRuntimeId" : 4691 + }, + { + "id" : "minecraft:exposed_cut_copper", + "blockRuntimeId" : 6168 + }, + { + "id" : "minecraft:weathered_cut_copper", + "blockRuntimeId" : 7199 + }, + { + "id" : "minecraft:oxidized_cut_copper", + "blockRuntimeId" : 5480 + }, + { + "id" : "minecraft:waxed_cut_copper", + "blockRuntimeId" : 7295 + }, + { + "id" : "minecraft:waxed_exposed_cut_copper", + "blockRuntimeId" : 3811 + }, + { + "id" : "minecraft:waxed_weathered_cut_copper", + "blockRuntimeId" : 4853 + }, + { + "id" : "minecraft:waxed_oxidized_cut_copper", + "blockRuntimeId" : 214 + }, + { + "id" : "minecraft:emerald_block", + "blockRuntimeId" : 1161 + }, + { + "id" : "minecraft:diamond_block", + "blockRuntimeId" : 272 + }, + { + "id" : "minecraft:lapis_block", + "blockRuntimeId" : 4288 + }, + { + "id" : "minecraft:raw_iron_block", + "blockRuntimeId" : 8262 + }, + { + "id" : "minecraft:raw_copper_block", + "blockRuntimeId" : 5271 + }, + { + "id" : "minecraft:raw_gold_block", + "blockRuntimeId" : 363 + }, + { + "id" : "minecraft:quartz_block", + "blockRuntimeId" : 3698 + }, + { + "id" : "minecraft:quartz_block", + "blockRuntimeId" : 3700 + }, + { + "id" : "minecraft:quartz_block", + "blockRuntimeId" : 3699 + }, + { + "id" : "minecraft:quartz_block", + "blockRuntimeId" : 3701 + }, + { + "id" : "minecraft:prismarine", + "blockRuntimeId" : 6087 + }, + { + "id" : "minecraft:prismarine", + "blockRuntimeId" : 6088 + }, + { + "id" : "minecraft:slime", + "blockRuntimeId" : 4235 + }, + { + "id" : "minecraft:honey_block", + "blockRuntimeId" : 894 + }, + { + "id" : "minecraft:honeycomb_block", + "blockRuntimeId" : 4478 + }, + { + "id" : "minecraft:hay_block", + "blockRuntimeId" : 697 + }, + { + "id" : "minecraft:bone_block", + "blockRuntimeId" : 4236 + }, + { + "id" : "minecraft:nether_brick", + "blockRuntimeId" : 7274 + }, + { + "id" : "minecraft:red_nether_brick", + "blockRuntimeId" : 146 + }, + { + "id" : "minecraft:netherite_block", + "blockRuntimeId" : 3777 + }, + { + "id" : "minecraft:lodestone", + "blockRuntimeId" : 8261 + }, + { + "id" : "minecraft:wool", + "blockRuntimeId" : 3460 + }, + { + "id" : "minecraft:wool", + "blockRuntimeId" : 3468 + }, + { + "id" : "minecraft:wool", + "blockRuntimeId" : 3467 + }, + { + "id" : "minecraft:wool", + "blockRuntimeId" : 3475 + }, + { + "id" : "minecraft:wool", + "blockRuntimeId" : 3472 + }, + { + "id" : "minecraft:wool", + "blockRuntimeId" : 3474 + }, + { + "id" : "minecraft:wool", + "blockRuntimeId" : 3461 + }, + { + "id" : "minecraft:wool", + "blockRuntimeId" : 3464 + }, + { + "id" : "minecraft:wool", + "blockRuntimeId" : 3465 + }, + { + "id" : "minecraft:wool", + "blockRuntimeId" : 3473 + }, + { + "id" : "minecraft:wool", + "blockRuntimeId" : 3469 + }, + { + "id" : "minecraft:wool", + "blockRuntimeId" : 3463 + }, + { + "id" : "minecraft:wool", + "blockRuntimeId" : 3471 + }, + { + "id" : "minecraft:wool", + "blockRuntimeId" : 3470 + }, + { + "id" : "minecraft:wool", + "blockRuntimeId" : 3462 + }, + { + "id" : "minecraft:wool", + "blockRuntimeId" : 3466 + }, + { + "id" : "minecraft:carpet", + "blockRuntimeId" : 951 + }, + { + "id" : "minecraft:carpet", + "blockRuntimeId" : 959 + }, + { + "id" : "minecraft:carpet", + "blockRuntimeId" : 958 + }, + { + "id" : "minecraft:carpet", + "blockRuntimeId" : 966 + }, + { + "id" : "minecraft:carpet", + "blockRuntimeId" : 963 + }, + { + "id" : "minecraft:carpet", + "blockRuntimeId" : 965 + }, + { + "id" : "minecraft:carpet", + "blockRuntimeId" : 952 + }, + { + "id" : "minecraft:carpet", + "blockRuntimeId" : 955 + }, + { + "id" : "minecraft:carpet", + "blockRuntimeId" : 956 + }, + { + "id" : "minecraft:carpet", + "blockRuntimeId" : 964 + }, + { + "id" : "minecraft:carpet", + "blockRuntimeId" : 960 + }, + { + "id" : "minecraft:carpet", + "blockRuntimeId" : 954 + }, + { + "id" : "minecraft:carpet", + "blockRuntimeId" : 962 + }, + { + "id" : "minecraft:carpet", + "blockRuntimeId" : 961 + }, + { + "id" : "minecraft:carpet", + "blockRuntimeId" : 953 + }, + { + "id" : "minecraft:carpet", + "blockRuntimeId" : 957 + }, + { + "id" : "minecraft:concrete_powder", + "blockRuntimeId" : 6266 + }, + { + "id" : "minecraft:concrete_powder", + "blockRuntimeId" : 6274 + }, + { + "id" : "minecraft:concrete_powder", + "blockRuntimeId" : 6273 + }, + { + "id" : "minecraft:concrete_powder", + "blockRuntimeId" : 6281 + }, + { + "id" : "minecraft:concrete_powder", + "blockRuntimeId" : 6278 + }, + { + "id" : "minecraft:concrete_powder", + "blockRuntimeId" : 6280 + }, + { + "id" : "minecraft:concrete_powder", + "blockRuntimeId" : 6267 + }, + { + "id" : "minecraft:concrete_powder", + "blockRuntimeId" : 6270 + }, + { + "id" : "minecraft:concrete_powder", + "blockRuntimeId" : 6271 + }, + { + "id" : "minecraft:concrete_powder", + "blockRuntimeId" : 6279 + }, + { + "id" : "minecraft:concrete_powder", + "blockRuntimeId" : 6275 + }, + { + "id" : "minecraft:concrete_powder", + "blockRuntimeId" : 6269 + }, + { + "id" : "minecraft:concrete_powder", + "blockRuntimeId" : 6277 + }, + { + "id" : "minecraft:concrete_powder", + "blockRuntimeId" : 6276 + }, + { + "id" : "minecraft:concrete_powder", + "blockRuntimeId" : 6268 + }, + { + "id" : "minecraft:concrete_powder", + "blockRuntimeId" : 6272 + }, + { + "id" : "minecraft:concrete", + "blockRuntimeId" : 662 + }, + { + "id" : "minecraft:concrete", + "blockRuntimeId" : 670 + }, + { + "id" : "minecraft:concrete", + "blockRuntimeId" : 669 + }, + { + "id" : "minecraft:concrete", + "blockRuntimeId" : 677 + }, + { + "id" : "minecraft:concrete", + "blockRuntimeId" : 674 + }, + { + "id" : "minecraft:concrete", + "blockRuntimeId" : 676 + }, + { + "id" : "minecraft:concrete", + "blockRuntimeId" : 663 + }, + { + "id" : "minecraft:concrete", + "blockRuntimeId" : 666 + }, + { + "id" : "minecraft:concrete", + "blockRuntimeId" : 667 + }, + { + "id" : "minecraft:concrete", + "blockRuntimeId" : 675 + }, + { + "id" : "minecraft:concrete", + "blockRuntimeId" : 671 + }, + { + "id" : "minecraft:concrete", + "blockRuntimeId" : 665 + }, + { + "id" : "minecraft:concrete", + "blockRuntimeId" : 673 + }, + { + "id" : "minecraft:concrete", + "blockRuntimeId" : 672 + }, + { + "id" : "minecraft:concrete", + "blockRuntimeId" : 664 + }, + { + "id" : "minecraft:concrete", + "blockRuntimeId" : 668 + }, + { + "id" : "minecraft:clay", + "blockRuntimeId" : 7126 + }, + { + "id" : "minecraft:hardened_clay", + "blockRuntimeId" : 643 + }, + { + "id" : "minecraft:stained_hardened_clay", + "blockRuntimeId" : 6178 + }, + { + "id" : "minecraft:stained_hardened_clay", + "blockRuntimeId" : 6186 + }, + { + "id" : "minecraft:stained_hardened_clay", + "blockRuntimeId" : 6185 + }, + { + "id" : "minecraft:stained_hardened_clay", + "blockRuntimeId" : 6193 + }, + { + "id" : "minecraft:stained_hardened_clay", + "blockRuntimeId" : 6190 + }, + { + "id" : "minecraft:stained_hardened_clay", + "blockRuntimeId" : 6192 + }, + { + "id" : "minecraft:stained_hardened_clay", + "blockRuntimeId" : 6179 + }, + { + "id" : "minecraft:stained_hardened_clay", + "blockRuntimeId" : 6182 + }, + { + "id" : "minecraft:stained_hardened_clay", + "blockRuntimeId" : 6183 + }, + { + "id" : "minecraft:stained_hardened_clay", + "blockRuntimeId" : 6191 + }, + { + "id" : "minecraft:stained_hardened_clay", + "blockRuntimeId" : 6187 + }, + { + "id" : "minecraft:stained_hardened_clay", + "blockRuntimeId" : 6181 + }, + { + "id" : "minecraft:stained_hardened_clay", + "blockRuntimeId" : 6189 + }, + { + "id" : "minecraft:stained_hardened_clay", + "blockRuntimeId" : 6188 + }, + { + "id" : "minecraft:stained_hardened_clay", + "blockRuntimeId" : 6180 + }, + { + "id" : "minecraft:stained_hardened_clay", + "blockRuntimeId" : 6184 + }, + { + "id" : "minecraft:white_glazed_terracotta", + "blockRuntimeId" : 5575 + }, + { + "id" : "minecraft:silver_glazed_terracotta", + "blockRuntimeId" : 3533 + }, + { + "id" : "minecraft:gray_glazed_terracotta", + "blockRuntimeId" : 8255 + }, + { + "id" : "minecraft:black_glazed_terracotta", + "blockRuntimeId" : 5836 + }, + { + "id" : "minecraft:brown_glazed_terracotta", + "blockRuntimeId" : 3549 + }, + { + "id" : "minecraft:red_glazed_terracotta", + "blockRuntimeId" : 4169 + }, + { + "id" : "minecraft:orange_glazed_terracotta", + "blockRuntimeId" : 1153 + }, + { + "id" : "minecraft:yellow_glazed_terracotta", + "blockRuntimeId" : 915 + }, + { + "id" : "minecraft:lime_glazed_terracotta", + "blockRuntimeId" : 223 + }, + { + "id" : "minecraft:green_glazed_terracotta", + "blockRuntimeId" : 6612 + }, + { + "id" : "minecraft:cyan_glazed_terracotta", + "blockRuntimeId" : 5360 + }, + { + "id" : "minecraft:light_blue_glazed_terracotta", + "blockRuntimeId" : 5473 + }, + { + "id" : "minecraft:blue_glazed_terracotta", + "blockRuntimeId" : 5467 + }, + { + "id" : "minecraft:purple_glazed_terracotta", + "blockRuntimeId" : 7013 + }, + { + "id" : "minecraft:magenta_glazed_terracotta", + "blockRuntimeId" : 967 + }, + { + "id" : "minecraft:pink_glazed_terracotta", + "blockRuntimeId" : 6541 + }, + { + "id" : "minecraft:purpur_block", + "blockRuntimeId" : 7716 + }, + { + "id" : "minecraft:purpur_block", + "blockRuntimeId" : 7718 + }, + { + "id" : "minecraft:packed_mud", + "blockRuntimeId" : 283 + }, + { + "id" : "minecraft:mud_bricks", + "blockRuntimeId" : 6891 + }, + { + "id" : "minecraft:nether_wart_block", + "blockRuntimeId" : 4295 + }, + { + "id" : "minecraft:warped_wart_block", + "blockRuntimeId" : 5907 + }, + { + "id" : "minecraft:shroomlight", + "blockRuntimeId" : 5063 + }, + { + "id" : "minecraft:crimson_nylium", + "blockRuntimeId" : 4191 + }, + { + "id" : "minecraft:warped_nylium", + "blockRuntimeId" : 6351 + }, + { + "id" : "minecraft:basalt", + "blockRuntimeId" : 4351 + }, + { + "id" : "minecraft:polished_basalt", + "blockRuntimeId" : 24 + }, + { + "id" : "minecraft:smooth_basalt", + "blockRuntimeId" : 1159 + }, + { + "id" : "minecraft:soul_soil", + "blockRuntimeId" : 5832 + }, + { + "id" : "minecraft:dirt", + "blockRuntimeId" : 5753 + }, + { + "id" : "minecraft:dirt", + "blockRuntimeId" : 5754 + }, + { + "id" : "minecraft:farmland", + "blockRuntimeId" : 3914 + }, + { + "id" : "minecraft:grass", + "blockRuntimeId" : 6977 + }, + { + "id" : "minecraft:grass_path", + "blockRuntimeId" : 8083 + }, + { + "id" : "minecraft:podzol", + "blockRuntimeId" : 4652 + }, + { + "id" : "minecraft:mycelium", + "blockRuntimeId" : 3685 + }, + { + "id" : "minecraft:mud", + "blockRuntimeId" : 6686 + }, + { + "id" : "minecraft:stone", + "blockRuntimeId" : 655 + }, + { + "id" : "minecraft:iron_ore", + "blockRuntimeId" : 4692 + }, + { + "id" : "minecraft:gold_ore", + "blockRuntimeId" : 914 + }, + { + "id" : "minecraft:diamond_ore", + "blockRuntimeId" : 4363 + }, + { + "id" : "minecraft:lapis_ore", + "blockRuntimeId" : 7701 + }, + { + "id" : "minecraft:redstone_ore", + "blockRuntimeId" : 4291 + }, + { + "id" : "minecraft:coal_ore", + "blockRuntimeId" : 4289 + }, + { + "id" : "minecraft:copper_ore", + "blockRuntimeId" : 3556 + }, + { + "id" : "minecraft:emerald_ore", + "blockRuntimeId" : 7349 + }, + { + "id" : "minecraft:quartz_ore", + "blockRuntimeId" : 4503 + }, + { + "id" : "minecraft:nether_gold_ore", + "blockRuntimeId" : 27 + }, + { + "id" : "minecraft:ancient_debris", + "blockRuntimeId" : 6109 + }, + { + "id" : "minecraft:deepslate_iron_ore", + "blockRuntimeId" : 7275 + }, + { + "id" : "minecraft:deepslate_gold_ore", + "blockRuntimeId" : 6108 + }, + { + "id" : "minecraft:deepslate_diamond_ore", + "blockRuntimeId" : 8040 + }, + { + "id" : "minecraft:deepslate_lapis_ore", + "blockRuntimeId" : 7264 + }, + { + "id" : "minecraft:deepslate_redstone_ore", + "blockRuntimeId" : 6618 + }, + { + "id" : "minecraft:deepslate_emerald_ore", + "blockRuntimeId" : 6352 + }, + { + "id" : "minecraft:deepslate_coal_ore", + "blockRuntimeId" : 7198 + }, + { + "id" : "minecraft:deepslate_copper_ore", + "blockRuntimeId" : 105 + }, + { + "id" : "minecraft:gravel", + "blockRuntimeId" : 8289 + }, + { + "id" : "minecraft:stone", + "blockRuntimeId" : 656 + }, + { + "id" : "minecraft:stone", + "blockRuntimeId" : 658 + }, + { + "id" : "minecraft:stone", + "blockRuntimeId" : 660 + }, + { + "id" : "minecraft:blackstone", + "blockRuntimeId" : 7587 + }, + { + "id" : "minecraft:deepslate", + "blockRuntimeId" : 253 + }, + { + "id" : "minecraft:stone", + "blockRuntimeId" : 657 + }, + { + "id" : "minecraft:stone", + "blockRuntimeId" : 659 + }, + { + "id" : "minecraft:stone", + "blockRuntimeId" : 661 + }, + { + "id" : "minecraft:polished_blackstone", + "blockRuntimeId" : 3684 + }, + { + "id" : "minecraft:polished_deepslate", + "blockRuntimeId" : 7756 + }, + { + "id" : "minecraft:sand", + "blockRuntimeId" : 4197 + }, + { + "id" : "minecraft:sand", + "blockRuntimeId" : 4198 + }, + { + "id" : "minecraft:cactus", + "blockRuntimeId" : 6988 + }, + { + "id" : "minecraft:log", + "blockRuntimeId" : 6674 + }, + { + "id" : "minecraft:stripped_oak_log", + "blockRuntimeId" : 7545 + }, + { + "id" : "minecraft:log", + "blockRuntimeId" : 6675 + }, + { + "id" : "minecraft:stripped_spruce_log", + "blockRuntimeId" : 6290 + }, + { + "id" : "minecraft:log", + "blockRuntimeId" : 6676 + }, + { + "id" : "minecraft:stripped_birch_log", + "blockRuntimeId" : 5974 + }, + { + "id" : "minecraft:log", + "blockRuntimeId" : 6677 + }, + { + "id" : "minecraft:stripped_jungle_log", + "blockRuntimeId" : 644 + }, + { + "id" : "minecraft:log2", + "blockRuntimeId" : 3832 + }, + { + "id" : "minecraft:stripped_acacia_log", + "blockRuntimeId" : 5850 + }, + { + "id" : "minecraft:log2", + "blockRuntimeId" : 3833 + }, + { + "id" : "minecraft:stripped_dark_oak_log", + "blockRuntimeId" : 216 + }, + { + "id" : "minecraft:mangrove_log", + "blockRuntimeId" : 350 + }, + { + "id" : "minecraft:stripped_mangrove_log", + "blockRuntimeId" : 8286 + }, + { + "id" : "minecraft:crimson_stem", + "blockRuntimeId" : 5899 + }, + { + "id" : "minecraft:stripped_crimson_stem", + "blockRuntimeId" : 6950 + }, + { + "id" : "minecraft:warped_stem", + "blockRuntimeId" : 6488 + }, + { + "id" : "minecraft:stripped_warped_stem", + "blockRuntimeId" : 7402 + }, + { + "id" : "minecraft:wood", + "blockRuntimeId" : 3476 + }, + { + "id" : "minecraft:wood", + "blockRuntimeId" : 3482 + }, + { + "id" : "minecraft:wood", + "blockRuntimeId" : 3477 + }, + { + "id" : "minecraft:wood", + "blockRuntimeId" : 3483 + }, + { + "id" : "minecraft:wood", + "blockRuntimeId" : 3478 + }, + { + "id" : "minecraft:wood", + "blockRuntimeId" : 3484 + }, + { + "id" : "minecraft:wood", + "blockRuntimeId" : 3479 + }, + { + "id" : "minecraft:wood", + "blockRuntimeId" : 3485 + }, + { + "id" : "minecraft:wood", + "blockRuntimeId" : 3480 + }, + { + "id" : "minecraft:wood", + "blockRuntimeId" : 3486 + }, + { + "id" : "minecraft:wood", + "blockRuntimeId" : 3481 + }, + { + "id" : "minecraft:wood", + "blockRuntimeId" : 3487 + }, + { + "id" : "minecraft:mangrove_wood", + "blockRuntimeId" : 4163 + }, + { + "id" : "minecraft:stripped_mangrove_wood", + "blockRuntimeId" : 4231 + }, + { + "id" : "minecraft:crimson_hyphae", + "blockRuntimeId" : 4296 + }, + { + "id" : "minecraft:stripped_crimson_hyphae", + "blockRuntimeId" : 6501 + }, + { + "id" : "minecraft:warped_hyphae", + "blockRuntimeId" : 5904 + }, + { + "id" : "minecraft:stripped_warped_hyphae", + "blockRuntimeId" : 5581 + }, + { + "id" : "minecraft:leaves", + "blockRuntimeId" : 6092 + }, + { + "id" : "minecraft:leaves", + "blockRuntimeId" : 6093 + }, + { + "id" : "minecraft:leaves", + "blockRuntimeId" : 6094 + }, + { + "id" : "minecraft:leaves", + "blockRuntimeId" : 6095 + }, + { + "id" : "minecraft:leaves2", + "blockRuntimeId" : 4355 + }, + { + "id" : "minecraft:leaves2", + "blockRuntimeId" : 4356 + }, + { + "id" : "minecraft:mangrove_leaves", + "blockRuntimeId" : 6668 + }, + { + "id" : "minecraft:azalea_leaves", + "blockRuntimeId" : 7712 + }, + { + "id" : "minecraft:azalea_leaves_flowered", + "blockRuntimeId" : 6341 + }, + { + "id" : "minecraft:sapling", + "blockRuntimeId" : 714 + }, + { + "id" : "minecraft:sapling", + "blockRuntimeId" : 715 + }, + { + "id" : "minecraft:sapling", + "blockRuntimeId" : 716 + }, + { + "id" : "minecraft:sapling", + "blockRuntimeId" : 717 + }, + { + "id" : "minecraft:sapling", + "blockRuntimeId" : 718 + }, + { + "id" : "minecraft:sapling", + "blockRuntimeId" : 719 + }, + { + "id" : "minecraft:mangrove_propagule", + "blockRuntimeId" : 6978 + }, + { + "id" : "minecraft:bee_nest", + "blockRuntimeId" : 5756 + }, + { + "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" : 394 + }, + { + "id" : "minecraft:melon_slice" + }, + { + "id" : "minecraft:glistering_melon_slice" + }, + { + "id" : "minecraft:sweet_berries" + }, + { + "id" : "minecraft:glow_berries" + }, + { + "id" : "minecraft:pumpkin", + "blockRuntimeId" : 4579 + }, + { + "id" : "minecraft:carved_pumpkin", + "blockRuntimeId" : 7380 + }, + { + "id" : "minecraft:lit_pumpkin", + "blockRuntimeId" : 6687 + }, + { + "id" : "minecraft:honeycomb" + }, + { + "id" : "minecraft:tallgrass", + "blockRuntimeId" : 931 + }, + { + "id" : "minecraft:double_plant", + "blockRuntimeId" : 5457 + }, + { + "id" : "minecraft:tallgrass", + "blockRuntimeId" : 930 + }, + { + "id" : "minecraft:double_plant", + "blockRuntimeId" : 5456 + }, + { + "id" : "minecraft:nether_sprouts" + }, + { + "id" : "minecraft:coral", + "blockRuntimeId" : 6494 + }, + { + "id" : "minecraft:coral", + "blockRuntimeId" : 6492 + }, + { + "id" : "minecraft:coral", + "blockRuntimeId" : 6493 + }, + { + "id" : "minecraft:coral", + "blockRuntimeId" : 6491 + }, + { + "id" : "minecraft:coral", + "blockRuntimeId" : 6495 + }, + { + "id" : "minecraft:coral", + "blockRuntimeId" : 6499 + }, + { + "id" : "minecraft:coral", + "blockRuntimeId" : 6497 + }, + { + "id" : "minecraft:coral", + "blockRuntimeId" : 6498 + }, + { + "id" : "minecraft:coral", + "blockRuntimeId" : 6496 + }, + { + "id" : "minecraft:coral", + "blockRuntimeId" : 6500 + }, + { + "id" : "minecraft:coral_fan", + "blockRuntimeId" : 4618 + }, + { + "id" : "minecraft:coral_fan", + "blockRuntimeId" : 4616 + }, + { + "id" : "minecraft:coral_fan", + "blockRuntimeId" : 4617 + }, + { + "id" : "minecraft:coral_fan", + "blockRuntimeId" : 4615 + }, + { + "id" : "minecraft:coral_fan", + "blockRuntimeId" : 4619 + }, + { + "id" : "minecraft:coral_fan_dead", + "blockRuntimeId" : 69 + }, + { + "id" : "minecraft:coral_fan_dead", + "blockRuntimeId" : 67 + }, + { + "id" : "minecraft:coral_fan_dead", + "blockRuntimeId" : 68 + }, + { + "id" : "minecraft:coral_fan_dead", + "blockRuntimeId" : 66 + }, + { + "id" : "minecraft:coral_fan_dead", + "blockRuntimeId" : 70 + }, + { + "id" : "minecraft:kelp" + }, + { + "id" : "minecraft:seagrass", + "blockRuntimeId" : 246 + }, + { + "id" : "minecraft:crimson_roots", + "blockRuntimeId" : 7575 + }, + { + "id" : "minecraft:warped_roots", + "blockRuntimeId" : 4364 + }, + { + "id" : "minecraft:yellow_flower", + "blockRuntimeId" : 302 + }, + { + "id" : "minecraft:red_flower", + "blockRuntimeId" : 3618 + }, + { + "id" : "minecraft:red_flower", + "blockRuntimeId" : 3619 + }, + { + "id" : "minecraft:red_flower", + "blockRuntimeId" : 3620 + }, + { + "id" : "minecraft:red_flower", + "blockRuntimeId" : 3621 + }, + { + "id" : "minecraft:red_flower", + "blockRuntimeId" : 3622 + }, + { + "id" : "minecraft:red_flower", + "blockRuntimeId" : 3623 + }, + { + "id" : "minecraft:red_flower", + "blockRuntimeId" : 3624 + }, + { + "id" : "minecraft:red_flower", + "blockRuntimeId" : 3625 + }, + { + "id" : "minecraft:red_flower", + "blockRuntimeId" : 3626 + }, + { + "id" : "minecraft:red_flower", + "blockRuntimeId" : 3627 + }, + { + "id" : "minecraft:red_flower", + "blockRuntimeId" : 3628 + }, + { + "id" : "minecraft:double_plant", + "blockRuntimeId" : 5454 + }, + { + "id" : "minecraft:double_plant", + "blockRuntimeId" : 5455 + }, + { + "id" : "minecraft:double_plant", + "blockRuntimeId" : 5458 + }, + { + "id" : "minecraft:double_plant", + "blockRuntimeId" : 5459 + }, + { + "id" : "minecraft:wither_rose", + "blockRuntimeId" : 6167 + }, + { + "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" : 896 + }, + { + "id" : "minecraft:weeping_vines", + "blockRuntimeId" : 5481 + }, + { + "id" : "minecraft:twisting_vines", + "blockRuntimeId" : 5693 + }, + { + "id" : "minecraft:waterlily", + "blockRuntimeId" : 1160 + }, + { + "id" : "minecraft:deadbush", + "blockRuntimeId" : 4679 + }, + { + "id" : "minecraft:bamboo", + "blockRuntimeId" : 3686 + }, + { + "id" : "minecraft:snow", + "blockRuntimeId" : 4196 + }, + { + "id" : "minecraft:ice", + "blockRuntimeId" : 6691 + }, + { + "id" : "minecraft:packed_ice", + "blockRuntimeId" : 282 + }, + { + "id" : "minecraft:blue_ice", + "blockRuntimeId" : 7029 + }, + { + "id" : "minecraft:snow_layer", + "blockRuntimeId" : 155 + }, + { + "id" : "minecraft:pointed_dripstone", + "blockRuntimeId" : 7418 + }, + { + "id" : "minecraft:dripstone_block", + "blockRuntimeId" : 895 + }, + { + "id" : "minecraft:moss_carpet", + "blockRuntimeId" : 286 + }, + { + "id" : "minecraft:moss_block", + "blockRuntimeId" : 6540 + }, + { + "id" : "minecraft:dirt_with_roots", + "blockRuntimeId" : 5399 + }, + { + "id" : "minecraft:hanging_roots", + "blockRuntimeId" : 205 + }, + { + "id" : "minecraft:mangrove_roots", + "blockRuntimeId" : 6177 + }, + { + "id" : "minecraft:muddy_mangrove_roots", + "blockRuntimeId" : 345 + }, + { + "id" : "minecraft:big_dripleaf", + "blockRuntimeId" : 5982 + }, + { + "id" : "minecraft:small_dripleaf_block", + "blockRuntimeId" : 4322 + }, + { + "id" : "minecraft:spore_blossom", + "blockRuntimeId" : 7314 + }, + { + "id" : "minecraft:azalea", + "blockRuntimeId" : 6890 + }, + { + "id" : "minecraft:flowering_azalea", + "blockRuntimeId" : 5479 + }, + { + "id" : "minecraft:glow_lichen", + "blockRuntimeId" : 5686 + }, + { + "id" : "minecraft:amethyst_block", + "blockRuntimeId" : 290 + }, + { + "id" : "minecraft:budding_amethyst", + "blockRuntimeId" : 7004 + }, + { + "id" : "minecraft:amethyst_cluster", + "blockRuntimeId" : 7812 + }, + { + "id" : "minecraft:large_amethyst_bud", + "blockRuntimeId" : 4730 + }, + { + "id" : "minecraft:medium_amethyst_bud", + "blockRuntimeId" : 4378 + }, + { + "id" : "minecraft:small_amethyst_bud", + "blockRuntimeId" : 304 + }, + { + "id" : "minecraft:tuff", + "blockRuntimeId" : 349 + }, + { + "id" : "minecraft:calcite", + "blockRuntimeId" : 215 + }, + { + "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" : 3548 + }, + { + "id" : "minecraft:red_mushroom", + "blockRuntimeId" : 4587 + }, + { + "id" : "minecraft:crimson_fungus", + "blockRuntimeId" : 7755 + }, + { + "id" : "minecraft:warped_fungus", + "blockRuntimeId" : 287 + }, + { + "id" : "minecraft:brown_mushroom_block", + "blockRuntimeId" : 7364 + }, + { + "id" : "minecraft:red_mushroom_block", + "blockRuntimeId" : 3613 + }, + { + "id" : "minecraft:brown_mushroom_block", + "blockRuntimeId" : 7365 + }, + { + "id" : "minecraft:brown_mushroom_block", + "blockRuntimeId" : 7350 + }, + { + "id" : "minecraft:egg" + }, + { + "id" : "minecraft:sugar_cane" + }, + { + "id" : "minecraft:sugar" + }, + { + "id" : "minecraft:rotten_flesh" + }, + { + "id" : "minecraft:bone" + }, + { + "id" : "minecraft:web", + "blockRuntimeId" : 6715 + }, + { + "id" : "minecraft:spider_eye" + }, + { + "id" : "minecraft:mob_spawner", + "blockRuntimeId" : 403 + }, + { + "id" : "minecraft:monster_egg", + "blockRuntimeId" : 4146 + }, + { + "id" : "minecraft:monster_egg", + "blockRuntimeId" : 4147 + }, + { + "id" : "minecraft:monster_egg", + "blockRuntimeId" : 4148 + }, + { + "id" : "minecraft:monster_egg", + "blockRuntimeId" : 4149 + }, + { + "id" : "minecraft:monster_egg", + "blockRuntimeId" : 4150 + }, + { + "id" : "minecraft:monster_egg", + "blockRuntimeId" : 4151 + }, + { + "id" : "minecraft:infested_deepslate", + "blockRuntimeId" : 4643 + }, + { + "id" : "minecraft:dragon_egg", + "blockRuntimeId" : 7273 + }, + { + "id" : "minecraft:turtle_egg", + "blockRuntimeId" : 7999 + }, + { + "id" : "minecraft:frog_spawn", + "blockRuntimeId" : 4401 + }, + { + "id" : "minecraft:pearlescent_froglight", + "blockRuntimeId" : 6437 + }, + { + "id" : "minecraft:verdant_froglight", + "blockRuntimeId" : 6483 + }, + { + "id" : "minecraft:ochre_froglight", + "blockRuntimeId" : 3512 + }, + { + "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:obsidian", + "blockRuntimeId" : 430 + }, + { + "id" : "minecraft:crying_obsidian", + "blockRuntimeId" : 6724 + }, + { + "id" : "minecraft:bedrock", + "blockRuntimeId" : 7019 + }, + { + "id" : "minecraft:soul_sand", + "blockRuntimeId" : 5833 + }, + { + "id" : "minecraft:netherrack", + "blockRuntimeId" : 7039 + }, + { + "id" : "minecraft:magma", + "blockRuntimeId" : 8011 + }, + { + "id" : "minecraft:nether_wart" + }, + { + "id" : "minecraft:end_stone", + "blockRuntimeId" : 3838 + }, + { + "id" : "minecraft:chorus_flower", + "blockRuntimeId" : 4532 + }, + { + "id" : "minecraft:chorus_plant", + "blockRuntimeId" : 5507 + }, + { + "id" : "minecraft:chorus_fruit" + }, + { + "id" : "minecraft:popped_chorus_fruit" + }, + { + "id" : "minecraft:sponge", + "blockRuntimeId" : 631 + }, + { + "id" : "minecraft:sponge", + "blockRuntimeId" : 632 + }, + { + "id" : "minecraft:coral_block", + "blockRuntimeId" : 5239 + }, + { + "id" : "minecraft:coral_block", + "blockRuntimeId" : 5240 + }, + { + "id" : "minecraft:coral_block", + "blockRuntimeId" : 5241 + }, + { + "id" : "minecraft:coral_block", + "blockRuntimeId" : 5242 + }, + { + "id" : "minecraft:coral_block", + "blockRuntimeId" : 5243 + }, + { + "id" : "minecraft:coral_block", + "blockRuntimeId" : 5244 + }, + { + "id" : "minecraft:coral_block", + "blockRuntimeId" : 5245 + }, + { + "id" : "minecraft:coral_block", + "blockRuntimeId" : 5246 + }, + { + "id" : "minecraft:coral_block", + "blockRuntimeId" : 5247 + }, + { + "id" : "minecraft:coral_block", + "blockRuntimeId" : 5248 + }, + { + "id" : "minecraft:sculk", + "blockRuntimeId" : 7038 + }, + { + "id" : "minecraft:sculk_vein", + "blockRuntimeId" : 7134 + }, + { + "id" : "minecraft:sculk_catalyst", + "blockRuntimeId" : 3615 + }, + { + "id" : "minecraft:sculk_shrieker", + "blockRuntimeId" : 219 + }, + { + "id" : "minecraft:sculk_sensor", + "blockRuntimeId" : 4391 + }, + { + "id" : "minecraft:reinforced_deepslate", + "blockRuntimeId" : 5834 + }, + { + "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" : 726 + }, + { + "id" : "minecraft:soul_torch", + "blockRuntimeId" : 4646 + }, + { + "id" : "minecraft:sea_pickle", + "blockRuntimeId" : 5857 + }, + { + "id" : "minecraft:lantern", + "blockRuntimeId" : 7076 + }, + { + "id" : "minecraft:soul_lantern", + "blockRuntimeId" : 5751 + }, + { + "id" : "minecraft:candle", + "blockRuntimeId" : 7405 + }, + { + "id" : "minecraft:white_candle", + "blockRuntimeId" : 5302 + }, + { + "id" : "minecraft:orange_candle", + "blockRuntimeId" : 364 + }, + { + "id" : "minecraft:magenta_candle", + "blockRuntimeId" : 420 + }, + { + "id" : "minecraft:light_blue_candle", + "blockRuntimeId" : 4571 + }, + { + "id" : "minecraft:yellow_candle", + "blockRuntimeId" : 6194 + }, + { + "id" : "minecraft:lime_candle", + "blockRuntimeId" : 6370 + }, + { + "id" : "minecraft:pink_candle", + "blockRuntimeId" : 7372 + }, + { + "id" : "minecraft:gray_candle", + "blockRuntimeId" : 941 + }, + { + "id" : "minecraft:light_gray_candle", + "blockRuntimeId" : 6226 + }, + { + "id" : "minecraft:cyan_candle", + "blockRuntimeId" : 7728 + }, + { + "id" : "minecraft:purple_candle", + "blockRuntimeId" : 7040 + }, + { + "id" : "minecraft:blue_candle" + }, + { + "id" : "minecraft:brown_candle", + "blockRuntimeId" : 5877 + }, + { + "id" : "minecraft:green_candle", + "blockRuntimeId" : 688 + }, + { + "id" : "minecraft:red_candle", + "blockRuntimeId" : 4683 + }, + { + "id" : "minecraft:black_candle", + "blockRuntimeId" : 171 + }, + { + "id" : "minecraft:crafting_table", + "blockRuntimeId" : 5856 + }, + { + "id" : "minecraft:cartography_table", + "blockRuntimeId" : 8290 + }, + { + "id" : "minecraft:fletching_table", + "blockRuntimeId" : 5835 + }, + { + "id" : "minecraft:smithing_table", + "blockRuntimeId" : 3728 + }, + { + "id" : "minecraft:beehive", + "blockRuntimeId" : 6110 + }, + { + "id" : "minecraft:campfire" + }, + { + "id" : "minecraft:soul_campfire" + }, + { + "id" : "minecraft:furnace", + "blockRuntimeId" : 7804 + }, + { + "id" : "minecraft:blast_furnace", + "blockRuntimeId" : 7569 + }, + { + "id" : "minecraft:smoker", + "blockRuntimeId" : 649 + }, + { + "id" : "minecraft:respawn_anchor", + "blockRuntimeId" : 683 + }, + { + "id" : "minecraft:brewing_stand" + }, + { + "id" : "minecraft:anvil", + "blockRuntimeId" : 6636 + }, + { + "id" : "minecraft:anvil", + "blockRuntimeId" : 6640 + }, + { + "id" : "minecraft:anvil", + "blockRuntimeId" : 6644 + }, + { + "id" : "minecraft:grindstone", + "blockRuntimeId" : 8041 + }, + { + "id" : "minecraft:enchanting_table", + "blockRuntimeId" : 6725 + }, + { + "id" : "minecraft:bookshelf", + "blockRuntimeId" : 6673 + }, + { + "id" : "minecraft:lectern", + "blockRuntimeId" : 6942 + }, + { + "id" : "minecraft:cauldron" + }, + { + "id" : "minecraft:composter", + "blockRuntimeId" : 5417 + }, + { + "id" : "minecraft:chest", + "blockRuntimeId" : 7117 + }, + { + "id" : "minecraft:trapped_chest", + "blockRuntimeId" : 5585 + }, + { + "id" : "minecraft:ender_chest", + "blockRuntimeId" : 4371 + }, + { + "id" : "minecraft:barrel", + "blockRuntimeId" : 4520 + }, + { + "id" : "minecraft:undyed_shulker_box", + "blockRuntimeId" : 3683 + }, + { + "id" : "minecraft:shulker_box", + "blockRuntimeId" : 5318 + }, + { + "id" : "minecraft:shulker_box", + "blockRuntimeId" : 5326 + }, + { + "id" : "minecraft:shulker_box", + "blockRuntimeId" : 5325 + }, + { + "id" : "minecraft:shulker_box", + "blockRuntimeId" : 5333 + }, + { + "id" : "minecraft:shulker_box", + "blockRuntimeId" : 5330 + }, + { + "id" : "minecraft:shulker_box", + "blockRuntimeId" : 5332 + }, + { + "id" : "minecraft:shulker_box", + "blockRuntimeId" : 5319 + }, + { + "id" : "minecraft:shulker_box", + "blockRuntimeId" : 5322 + }, + { + "id" : "minecraft:shulker_box", + "blockRuntimeId" : 5323 + }, + { + "id" : "minecraft:shulker_box", + "blockRuntimeId" : 5331 + }, + { + "id" : "minecraft:shulker_box", + "blockRuntimeId" : 5327 + }, + { + "id" : "minecraft:shulker_box", + "blockRuntimeId" : 5321 + }, + { + "id" : "minecraft:shulker_box", + "blockRuntimeId" : 5329 + }, + { + "id" : "minecraft:shulker_box", + "blockRuntimeId" : 5328 + }, + { + "id" : "minecraft:shulker_box", + "blockRuntimeId" : 5320 + }, + { + "id" : "minecraft:shulker_box", + "blockRuntimeId" : 5324 + }, + { + "id" : "minecraft:armor_stand" + }, + { + "id" : "minecraft:noteblock", + "blockRuntimeId" : 348 + }, + { + "id" : "minecraft:jukebox", + "blockRuntimeId" : 4876 + }, + { + "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" : 3887 + }, + { + "id" : "minecraft:redstone_lamp", + "blockRuntimeId" : 251 + }, + { + "id" : "minecraft:sea_lantern", + "blockRuntimeId" : 7548 + }, + { + "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" : 145 + }, + { + "id" : "minecraft:bell", + "blockRuntimeId" : 6910 + }, + { + "id" : "minecraft:conduit", + "blockRuntimeId" : 4234 + }, + { + "id" : "minecraft:stonecutter_block", + "blockRuntimeId" : 7576 + }, + { + "id" : "minecraft:end_portal_frame", + "blockRuntimeId" : 6079 + }, + { + "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" : 5893 + }, + { + "id" : "minecraft:lightning_rod", + "blockRuntimeId" : 1178 + }, + { + "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" : 3922 + }, + { + "id" : "minecraft:golden_rail", + "blockRuntimeId" : 5334 + }, + { + "id" : "minecraft:detector_rail", + "blockRuntimeId" : 4134 + }, + { + "id" : "minecraft:activator_rail", + "blockRuntimeId" : 309 + }, + { + "id" : "minecraft:minecart" + }, + { + "id" : "minecraft:chest_minecart" + }, + { + "id" : "minecraft:hopper_minecart" + }, + { + "id" : "minecraft:tnt_minecart" + }, + { + "id" : "minecraft:redstone" + }, + { + "id" : "minecraft:redstone_block", + "blockRuntimeId" : 3778 + }, + { + "id" : "minecraft:redstone_torch", + "blockRuntimeId" : 3527 + }, + { + "id" : "minecraft:lever", + "blockRuntimeId" : 6516 + }, + { + "id" : "minecraft:wooden_button", + "blockRuntimeId" : 6393 + }, + { + "id" : "minecraft:spruce_button", + "blockRuntimeId" : 4323 + }, + { + "id" : "minecraft:birch_button", + "blockRuntimeId" : 7768 + }, + { + "id" : "minecraft:jungle_button", + "blockRuntimeId" : 116 + }, + { + "id" : "minecraft:acacia_button", + "blockRuntimeId" : 7233 + }, + { + "id" : "minecraft:dark_oak_button", + "blockRuntimeId" : 93 + }, + { + "id" : "minecraft:mangrove_button", + "blockRuntimeId" : 7064 + }, + { + "id" : "minecraft:stone_button", + "blockRuntimeId" : 598 + }, + { + "id" : "minecraft:crimson_button", + "blockRuntimeId" : 4434 + }, + { + "id" : "minecraft:warped_button", + "blockRuntimeId" : 7252 + }, + { + "id" : "minecraft:polished_blackstone_button", + "blockRuntimeId" : 7792 + }, + { + "id" : "minecraft:tripwire_hook", + "blockRuntimeId" : 5916 + }, + { + "id" : "minecraft:wooden_pressure_plate", + "blockRuntimeId" : 8065 + }, + { + "id" : "minecraft:spruce_pressure_plate", + "blockRuntimeId" : 3761 + }, + { + "id" : "minecraft:birch_pressure_plate", + "blockRuntimeId" : 3557 + }, + { + "id" : "minecraft:jungle_pressure_plate", + "blockRuntimeId" : 3637 + }, + { + "id" : "minecraft:acacia_pressure_plate", + "blockRuntimeId" : 5249 + }, + { + "id" : "minecraft:dark_oak_pressure_plate", + "blockRuntimeId" : 5958 + }, + { + "id" : "minecraft:mangrove_pressure_plate", + "blockRuntimeId" : 3871 + }, + { + "id" : "minecraft:crimson_pressure_plate", + "blockRuntimeId" : 8270 + }, + { + "id" : "minecraft:warped_pressure_plate", + "blockRuntimeId" : 256 + }, + { + "id" : "minecraft:stone_pressure_plate", + "blockRuntimeId" : 3888 + }, + { + "id" : "minecraft:light_weighted_pressure_plate", + "blockRuntimeId" : 3667 + }, + { + "id" : "minecraft:heavy_weighted_pressure_plate", + "blockRuntimeId" : 1162 + }, + { + "id" : "minecraft:polished_blackstone_pressure_plate", + "blockRuntimeId" : 6234 + }, + { + "id" : "minecraft:observer", + "blockRuntimeId" : 3515 + }, + { + "id" : "minecraft:daylight_detector", + "blockRuntimeId" : 4199 + }, + { + "id" : "minecraft:repeater" + }, + { + "id" : "minecraft:comparator" + }, + { + "id" : "minecraft:hopper" + }, + { + "id" : "minecraft:dropper", + "blockRuntimeId" : 7387 + }, + { + "id" : "minecraft:dispenser", + "blockRuntimeId" : 8015 + }, + { + "id" : "minecraft:piston", + "blockRuntimeId" : 924 + }, + { + "id" : "minecraft:sticky_piston", + "blockRuntimeId" : 4366 + }, + { + "id" : "minecraft:tnt", + "blockRuntimeId" : 6709 + }, + { + "id" : "minecraft:name_tag" + }, + { + "id" : "minecraft:loom", + "blockRuntimeId" : 3828 + }, + { + "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" : 6392 + }, + { + "id" : "minecraft:lodestone_compass" + } + ] +} \ No newline at end of file diff --git a/core/src/main/resources/bedrock/runtime_item_states.1_19_20.json b/core/src/main/resources/bedrock/runtime_item_states.1_19_20.json new file mode 100644 index 000000000..00be1af06 --- /dev/null +++ b/core/src/main/resources/bedrock/runtime_item_states.1_19_20.json @@ -0,0 +1,4530 @@ +[ + { + "name" : "minecraft:acacia_boat", + "id" : 379 + }, + { + "name" : "minecraft:acacia_button", + "id" : -140 + }, + { + "name" : "minecraft:acacia_chest_boat", + "id" : 642 + }, + { + "name" : "minecraft:acacia_door", + "id" : 556 + }, + { + "name" : "minecraft:acacia_fence_gate", + "id" : 187 + }, + { + "name" : "minecraft:acacia_pressure_plate", + "id" : -150 + }, + { + "name" : "minecraft:acacia_sign", + "id" : 579 + }, + { + "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" : 487 + }, + { + "name" : "minecraft:air", + "id" : -158 + }, + { + "name" : "minecraft:allay_spawn_egg", + "id" : 631 + }, + { + "name" : "minecraft:allow", + "id" : 210 + }, + { + "name" : "minecraft:amethyst_block", + "id" : -327 + }, + { + "name" : "minecraft:amethyst_cluster", + "id" : -329 + }, + { + "name" : "minecraft:amethyst_shard", + "id" : 624 + }, + { + "name" : "minecraft:ancient_debris", + "id" : -271 + }, + { + "name" : "minecraft:andesite_stairs", + "id" : -171 + }, + { + "name" : "minecraft:anvil", + "id" : 145 + }, + { + "name" : "minecraft:apple", + "id" : 257 + }, + { + "name" : "minecraft:armor_stand", + "id" : 552 + }, + { + "name" : "minecraft:arrow", + "id" : 301 + }, + { + "name" : "minecraft:axolotl_bucket", + "id" : 369 + }, + { + "name" : "minecraft:axolotl_spawn_egg", + "id" : 500 + }, + { + "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" : 598 + }, + { + "name" : "minecraft:bamboo", + "id" : -163 + }, + { + "name" : "minecraft:bamboo_sapling", + "id" : -164 + }, + { + "name" : "minecraft:banner", + "id" : 567 + }, + { + "name" : "minecraft:banner_pattern", + "id" : 651 + }, + { + "name" : "minecraft:barrel", + "id" : -203 + }, + { + "name" : "minecraft:barrier", + "id" : -161 + }, + { + "name" : "minecraft:basalt", + "id" : -234 + }, + { + "name" : "minecraft:bat_spawn_egg", + "id" : 453 + }, + { + "name" : "minecraft:beacon", + "id" : 138 + }, + { + "name" : "minecraft:bed", + "id" : 418 + }, + { + "name" : "minecraft:bedrock", + "id" : 7 + }, + { + "name" : "minecraft:bee_nest", + "id" : -218 + }, + { + "name" : "minecraft:bee_spawn_egg", + "id" : 494 + }, + { + "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" : 376 + }, + { + "name" : "minecraft:birch_button", + "id" : -141 + }, + { + "name" : "minecraft:birch_chest_boat", + "id" : 639 + }, + { + "name" : "minecraft:birch_door", + "id" : 554 + }, + { + "name" : "minecraft:birch_fence_gate", + "id" : 184 + }, + { + "name" : "minecraft:birch_pressure_plate", + "id" : -151 + }, + { + "name" : "minecraft:birch_sign", + "id" : 577 + }, + { + "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" : 395 + }, + { + "name" : "minecraft:black_glazed_terracotta", + "id" : 235 + }, + { + "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" : 429 + }, + { + "name" : "minecraft:blaze_rod", + "id" : 423 + }, + { + "name" : "minecraft:blaze_spawn_egg", + "id" : 456 + }, + { + "name" : "minecraft:bleach", + "id" : 596 + }, + { + "name" : "minecraft:blue_candle", + "id" : -424 + }, + { + "name" : "minecraft:blue_candle_cake", + "id" : -441 + }, + { + "name" : "minecraft:blue_dye", + "id" : 399 + }, + { + "name" : "minecraft:blue_glazed_terracotta", + "id" : 231 + }, + { + "name" : "minecraft:blue_ice", + "id" : -11 + }, + { + "name" : "minecraft:boat", + "id" : 649 + }, + { + "name" : "minecraft:bone", + "id" : 415 + }, + { + "name" : "minecraft:bone_block", + "id" : 216 + }, + { + "name" : "minecraft:bone_meal", + "id" : 411 + }, + { + "name" : "minecraft:book", + "id" : 387 + }, + { + "name" : "minecraft:bookshelf", + "id" : 47 + }, + { + "name" : "minecraft:border_block", + "id" : 212 + }, + { + "name" : "minecraft:bordure_indented_banner_pattern", + "id" : 586 + }, + { + "name" : "minecraft:bow", + "id" : 300 + }, + { + "name" : "minecraft:bowl", + "id" : 321 + }, + { + "name" : "minecraft:bread", + "id" : 261 + }, + { + "name" : "minecraft:brewing_stand", + "id" : 431 + }, + { + "name" : "minecraft:brick", + "id" : 383 + }, + { + "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" : 398 + }, + { + "name" : "minecraft:brown_glazed_terracotta", + "id" : 232 + }, + { + "name" : "minecraft:brown_mushroom", + "id" : 39 + }, + { + "name" : "minecraft:brown_mushroom_block", + "id" : 99 + }, + { + "name" : "minecraft:bubble_column", + "id" : -160 + }, + { + "name" : "minecraft:bucket", + "id" : 360 + }, + { + "name" : "minecraft:budding_amethyst", + "id" : -328 + }, + { + "name" : "minecraft:cactus", + "id" : 81 + }, + { + "name" : "minecraft:cake", + "id" : 417 + }, + { + "name" : "minecraft:calcite", + "id" : -326 + }, + { + "name" : "minecraft:camera", + "id" : 593 + }, + { + "name" : "minecraft:campfire", + "id" : 589 + }, + { + "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" : 517 + }, + { + "name" : "minecraft:carrots", + "id" : 141 + }, + { + "name" : "minecraft:cartography_table", + "id" : -200 + }, + { + "name" : "minecraft:carved_pumpkin", + "id" : -155 + }, + { + "name" : "minecraft:cat_spawn_egg", + "id" : 488 + }, + { + "name" : "minecraft:cauldron", + "id" : 432 + }, + { + "name" : "minecraft:cave_spider_spawn_egg", + "id" : 457 + }, + { + "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" : 619 + }, + { + "name" : "minecraft:chain_command_block", + "id" : 189 + }, + { + "name" : "minecraft:chainmail_boots", + "id" : 342 + }, + { + "name" : "minecraft:chainmail_chestplate", + "id" : 340 + }, + { + "name" : "minecraft:chainmail_helmet", + "id" : 339 + }, + { + "name" : "minecraft:chainmail_leggings", + "id" : 341 + }, + { + "name" : "minecraft:charcoal", + "id" : 303 + }, + { + "name" : "minecraft:chemical_heat", + "id" : 192 + }, + { + "name" : "minecraft:chemistry_table", + "id" : 238 + }, + { + "name" : "minecraft:chest", + "id" : 54 + }, + { + "name" : "minecraft:chest_boat", + "id" : 645 + }, + { + "name" : "minecraft:chest_minecart", + "id" : 389 + }, + { + "name" : "minecraft:chicken", + "id" : 275 + }, + { + "name" : "minecraft:chicken_spawn_egg", + "id" : 435 + }, + { + "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" : 558 + }, + { + "name" : "minecraft:chorus_plant", + "id" : 240 + }, + { + "name" : "minecraft:clay", + "id" : 82 + }, + { + "name" : "minecraft:clay_ball", + "id" : 384 + }, + { + "name" : "minecraft:client_request_placeholder_block", + "id" : -465 + }, + { + "name" : "minecraft:clock", + "id" : 393 + }, + { + "name" : "minecraft:coal", + "id" : 302 + }, + { + "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" : 412 + }, + { + "name" : "minecraft:cod", + "id" : 264 + }, + { + "name" : "minecraft:cod_bucket", + "id" : 364 + }, + { + "name" : "minecraft:cod_spawn_egg", + "id" : 480 + }, + { + "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" : 563 + }, + { + "name" : "minecraft:comparator", + "id" : 522 + }, + { + "name" : "minecraft:compass", + "id" : 391 + }, + { + "name" : "minecraft:composter", + "id" : -213 + }, + { + "name" : "minecraft:compound", + "id" : 594 + }, + { + "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" : 551 + }, + { + "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" : 504 + }, + { + "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" : 436 + }, + { + "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" : 582 + }, + { + "name" : "minecraft:creeper_spawn_egg", + "id" : 441 + }, + { + "name" : "minecraft:crimson_button", + "id" : -260 + }, + { + "name" : "minecraft:crimson_door", + "id" : 616 + }, + { + "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_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" : 614 + }, + { + "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" : 575 + }, + { + "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" : 401 + }, + { + "name" : "minecraft:cyan_glazed_terracotta", + "id" : 229 + }, + { + "name" : "minecraft:dark_oak_boat", + "id" : 380 + }, + { + "name" : "minecraft:dark_oak_button", + "id" : -142 + }, + { + "name" : "minecraft:dark_oak_chest_boat", + "id" : 643 + }, + { + "name" : "minecraft:dark_oak_door", + "id" : 557 + }, + { + "name" : "minecraft:dark_oak_fence_gate", + "id" : 186 + }, + { + "name" : "minecraft:dark_oak_pressure_plate", + "id" : -152 + }, + { + "name" : "minecraft:dark_oak_sign", + "id" : 580 + }, + { + "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: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" : 304 + }, + { + "name" : "minecraft:diamond_axe", + "id" : 319 + }, + { + "name" : "minecraft:diamond_block", + "id" : 57 + }, + { + "name" : "minecraft:diamond_boots", + "id" : 350 + }, + { + "name" : "minecraft:diamond_chestplate", + "id" : 348 + }, + { + "name" : "minecraft:diamond_helmet", + "id" : 347 + }, + { + "name" : "minecraft:diamond_hoe", + "id" : 332 + }, + { + "name" : "minecraft:diamond_horse_armor", + "id" : 533 + }, + { + "name" : "minecraft:diamond_leggings", + "id" : 349 + }, + { + "name" : "minecraft:diamond_ore", + "id" : 56 + }, + { + "name" : "minecraft:diamond_pickaxe", + "id" : 318 + }, + { + "name" : "minecraft:diamond_shovel", + "id" : 317 + }, + { + "name" : "minecraft:diamond_sword", + "id" : 316 + }, + { + "name" : "minecraft:diorite_stairs", + "id" : -170 + }, + { + "name" : "minecraft:dirt", + "id" : 3 + }, + { + "name" : "minecraft:dirt_with_roots", + "id" : -318 + }, + { + "name" : "minecraft:disc_fragment_5", + "id" : 637 + }, + { + "name" : "minecraft:dispenser", + "id" : 23 + }, + { + "name" : "minecraft:dolphin_spawn_egg", + "id" : 484 + }, + { + "name" : "minecraft:donkey_spawn_egg", + "id" : 465 + }, + { + "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" : 560 + }, + { + "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" : 483 + }, + { + "name" : "minecraft:dye", + "id" : 650 + }, + { + "name" : "minecraft:echo_shard", + "id" : 647 + }, + { + "name" : "minecraft:egg", + "id" : 390 + }, + { + "name" : "minecraft:elder_guardian_spawn_egg", + "id" : 471 + }, + { + "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" : 564 + }, + { + "name" : "minecraft:emerald", + "id" : 512 + }, + { + "name" : "minecraft:emerald_block", + "id" : 133 + }, + { + "name" : "minecraft:emerald_ore", + "id" : 129 + }, + { + "name" : "minecraft:empty_map", + "id" : 515 + }, + { + "name" : "minecraft:enchanted_book", + "id" : 521 + }, + { + "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" : 653 + }, + { + "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_eye", + "id" : 433 + }, + { + "name" : "minecraft:ender_pearl", + "id" : 422 + }, + { + "name" : "minecraft:enderman_spawn_egg", + "id" : 442 + }, + { + "name" : "minecraft:endermite_spawn_egg", + "id" : 460 + }, + { + "name" : "minecraft:evoker_spawn_egg", + "id" : 475 + }, + { + "name" : "minecraft:experience_bottle", + "id" : 508 + }, + { + "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" : 327 + }, + { + "name" : "minecraft:fence", + "id" : 85 + }, + { + "name" : "minecraft:fence_gate", + "id" : 107 + }, + { + "name" : "minecraft:fermented_spider_eye", + "id" : 428 + }, + { + "name" : "minecraft:field_masoned_banner_pattern", + "id" : 585 + }, + { + "name" : "minecraft:filled_map", + "id" : 420 + }, + { + "name" : "minecraft:fire", + "id" : 51 + }, + { + "name" : "minecraft:fire_charge", + "id" : 509 + }, + { + "name" : "minecraft:firework_rocket", + "id" : 519 + }, + { + "name" : "minecraft:firework_star", + "id" : 520 + }, + { + "name" : "minecraft:fishing_rod", + "id" : 392 + }, + { + "name" : "minecraft:fletching_table", + "id" : -201 + }, + { + "name" : "minecraft:flint", + "id" : 356 + }, + { + "name" : "minecraft:flint_and_steel", + "id" : 299 + }, + { + "name" : "minecraft:flower_banner_pattern", + "id" : 581 + }, + { + "name" : "minecraft:flower_pot", + "id" : 514 + }, + { + "name" : "minecraft:flowering_azalea", + "id" : -338 + }, + { + "name" : "minecraft:flowing_lava", + "id" : 10 + }, + { + "name" : "minecraft:flowing_water", + "id" : 8 + }, + { + "name" : "minecraft:fox_spawn_egg", + "id" : 490 + }, + { + "name" : "minecraft:frame", + "id" : 513 + }, + { + "name" : "minecraft:frog_spawn", + "id" : -468 + }, + { + "name" : "minecraft:frog_spawn_egg", + "id" : 628 + }, + { + "name" : "minecraft:frosted_ice", + "id" : 207 + }, + { + "name" : "minecraft:furnace", + "id" : 61 + }, + { + "name" : "minecraft:ghast_spawn_egg", + "id" : 454 + }, + { + "name" : "minecraft:ghast_tear", + "id" : 424 + }, + { + "name" : "minecraft:gilded_blackstone", + "id" : -281 + }, + { + "name" : "minecraft:glass", + "id" : 20 + }, + { + "name" : "minecraft:glass_bottle", + "id" : 427 + }, + { + "name" : "minecraft:glass_pane", + "id" : 102 + }, + { + "name" : "minecraft:glistering_melon_slice", + "id" : 434 + }, + { + "name" : "minecraft:globe_banner_pattern", + "id" : 588 + }, + { + "name" : "minecraft:glow_berries", + "id" : 654 + }, + { + "name" : "minecraft:glow_frame", + "id" : 623 + }, + { + "name" : "minecraft:glow_ink_sac", + "id" : 503 + }, + { + "name" : "minecraft:glow_lichen", + "id" : -411 + }, + { + "name" : "minecraft:glow_squid_spawn_egg", + "id" : 502 + }, + { + "name" : "minecraft:glow_stick", + "id" : 601 + }, + { + "name" : "minecraft:glowingobsidian", + "id" : 246 + }, + { + "name" : "minecraft:glowstone", + "id" : 89 + }, + { + "name" : "minecraft:glowstone_dust", + "id" : 394 + }, + { + "name" : "minecraft:goat_horn", + "id" : 627 + }, + { + "name" : "minecraft:goat_spawn_egg", + "id" : 501 + }, + { + "name" : "minecraft:gold_block", + "id" : 41 + }, + { + "name" : "minecraft:gold_ingot", + "id" : 306 + }, + { + "name" : "minecraft:gold_nugget", + "id" : 425 + }, + { + "name" : "minecraft:gold_ore", + "id" : 14 + }, + { + "name" : "minecraft:golden_apple", + "id" : 258 + }, + { + "name" : "minecraft:golden_axe", + "id" : 325 + }, + { + "name" : "minecraft:golden_boots", + "id" : 354 + }, + { + "name" : "minecraft:golden_carrot", + "id" : 283 + }, + { + "name" : "minecraft:golden_chestplate", + "id" : 352 + }, + { + "name" : "minecraft:golden_helmet", + "id" : 351 + }, + { + "name" : "minecraft:golden_hoe", + "id" : 333 + }, + { + "name" : "minecraft:golden_horse_armor", + "id" : 532 + }, + { + "name" : "minecraft:golden_leggings", + "id" : 353 + }, + { + "name" : "minecraft:golden_pickaxe", + "id" : 324 + }, + { + "name" : "minecraft:golden_rail", + "id" : 27 + }, + { + "name" : "minecraft:golden_shovel", + "id" : 323 + }, + { + "name" : "minecraft:golden_sword", + "id" : 322 + }, + { + "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" : 403 + }, + { + "name" : "minecraft:gray_glazed_terracotta", + "id" : 227 + }, + { + "name" : "minecraft:green_candle", + "id" : -426 + }, + { + "name" : "minecraft:green_candle_cake", + "id" : -443 + }, + { + "name" : "minecraft:green_dye", + "id" : 397 + }, + { + "name" : "minecraft:green_glazed_terracotta", + "id" : 233 + }, + { + "name" : "minecraft:grindstone", + "id" : -195 + }, + { + "name" : "minecraft:guardian_spawn_egg", + "id" : 461 + }, + { + "name" : "minecraft:gunpowder", + "id" : 328 + }, + { + "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" : 571 + }, + { + "name" : "minecraft:heavy_weighted_pressure_plate", + "id" : 148 + }, + { + "name" : "minecraft:hoglin_spawn_egg", + "id" : 496 + }, + { + "name" : "minecraft:honey_block", + "id" : -220 + }, + { + "name" : "minecraft:honey_bottle", + "id" : 592 + }, + { + "name" : "minecraft:honeycomb", + "id" : 591 + }, + { + "name" : "minecraft:honeycomb_block", + "id" : -221 + }, + { + "name" : "minecraft:hopper", + "id" : 527 + }, + { + "name" : "minecraft:hopper_minecart", + "id" : 526 + }, + { + "name" : "minecraft:horse_spawn_egg", + "id" : 458 + }, + { + "name" : "minecraft:husk_spawn_egg", + "id" : 463 + }, + { + "name" : "minecraft:ice", + "id" : 79 + }, + { + "name" : "minecraft:ice_bomb", + "id" : 595 + }, + { + "name" : "minecraft:infested_deepslate", + "id" : -454 + }, + { + "name" : "minecraft:info_update", + "id" : 248 + }, + { + "name" : "minecraft:info_update2", + "id" : 249 + }, + { + "name" : "minecraft:ink_sac", + "id" : 413 + }, + { + "name" : "minecraft:invisible_bedrock", + "id" : 95 + }, + { + "name" : "minecraft:iron_axe", + "id" : 298 + }, + { + "name" : "minecraft:iron_bars", + "id" : 101 + }, + { + "name" : "minecraft:iron_block", + "id" : 42 + }, + { + "name" : "minecraft:iron_boots", + "id" : 346 + }, + { + "name" : "minecraft:iron_chestplate", + "id" : 344 + }, + { + "name" : "minecraft:iron_door", + "id" : 372 + }, + { + "name" : "minecraft:iron_helmet", + "id" : 343 + }, + { + "name" : "minecraft:iron_hoe", + "id" : 331 + }, + { + "name" : "minecraft:iron_horse_armor", + "id" : 531 + }, + { + "name" : "minecraft:iron_ingot", + "id" : 305 + }, + { + "name" : "minecraft:iron_leggings", + "id" : 345 + }, + { + "name" : "minecraft:iron_nugget", + "id" : 569 + }, + { + "name" : "minecraft:iron_ore", + "id" : 15 + }, + { + "name" : "minecraft:iron_pickaxe", + "id" : 297 + }, + { + "name" : "minecraft:iron_shovel", + "id" : 296 + }, + { + "name" : "minecraft:iron_sword", + "id" : 307 + }, + { + "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" : 377 + }, + { + "name" : "minecraft:jungle_button", + "id" : -143 + }, + { + "name" : "minecraft:jungle_chest_boat", + "id" : 640 + }, + { + "name" : "minecraft:jungle_door", + "id" : 555 + }, + { + "name" : "minecraft:jungle_fence_gate", + "id" : 185 + }, + { + "name" : "minecraft:jungle_pressure_plate", + "id" : -153 + }, + { + "name" : "minecraft:jungle_sign", + "id" : 578 + }, + { + "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" : 382 + }, + { + "name" : "minecraft:ladder", + "id" : 65 + }, + { + "name" : "minecraft:lantern", + "id" : -208 + }, + { + "name" : "minecraft:lapis_block", + "id" : 22 + }, + { + "name" : "minecraft:lapis_lazuli", + "id" : 414 + }, + { + "name" : "minecraft:lapis_ore", + "id" : 21 + }, + { + "name" : "minecraft:large_amethyst_bud", + "id" : -330 + }, + { + "name" : "minecraft:lava", + "id" : 11 + }, + { + "name" : "minecraft:lava_bucket", + "id" : 363 + }, + { + "name" : "minecraft:lava_cauldron", + "id" : -210 + }, + { + "name" : "minecraft:lead", + "id" : 547 + }, + { + "name" : "minecraft:leather", + "id" : 381 + }, + { + "name" : "minecraft:leather_boots", + "id" : 338 + }, + { + "name" : "minecraft:leather_chestplate", + "id" : 336 + }, + { + "name" : "minecraft:leather_helmet", + "id" : 335 + }, + { + "name" : "minecraft:leather_horse_armor", + "id" : 530 + }, + { + "name" : "minecraft:leather_leggings", + "id" : 337 + }, + { + "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" : 407 + }, + { + "name" : "minecraft:light_blue_glazed_terracotta", + "id" : 223 + }, + { + "name" : "minecraft:light_gray_candle", + "id" : -421 + }, + { + "name" : "minecraft:light_gray_candle_cake", + "id" : -438 + }, + { + "name" : "minecraft:light_gray_dye", + "id" : 402 + }, + { + "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" : 405 + }, + { + "name" : "minecraft:lime_glazed_terracotta", + "id" : 225 + }, + { + "name" : "minecraft:lingering_potion", + "id" : 562 + }, + { + "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" : 473 + }, + { + "name" : "minecraft:lodestone", + "id" : -222 + }, + { + "name" : "minecraft:lodestone_compass", + "id" : 602 + }, + { + "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" : 408 + }, + { + "name" : "minecraft:magenta_glazed_terracotta", + "id" : 222 + }, + { + "name" : "minecraft:magma", + "id" : 213 + }, + { + "name" : "minecraft:magma_cream", + "id" : 430 + }, + { + "name" : "minecraft:magma_cube_spawn_egg", + "id" : 455 + }, + { + "name" : "minecraft:mangrove_boat", + "id" : 635 + }, + { + "name" : "minecraft:mangrove_button", + "id" : -487 + }, + { + "name" : "minecraft:mangrove_chest_boat", + "id" : 644 + }, + { + "name" : "minecraft:mangrove_door", + "id" : 633 + }, + { + "name" : "minecraft:mangrove_double_slab", + "id" : -499 + }, + { + "name" : "minecraft:mangrove_fence", + "id" : -491 + }, + { + "name" : "minecraft:mangrove_fence_gate", + "id" : -492 + }, + { + "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" : 634 + }, + { + "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" : 599 + }, + { + "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" : 361 + }, + { + "name" : "minecraft:minecart", + "id" : 370 + }, + { + "name" : "minecraft:mob_spawner", + "id" : 52 + }, + { + "name" : "minecraft:mojang_banner_pattern", + "id" : 584 + }, + { + "name" : "minecraft:monster_egg", + "id" : 97 + }, + { + "name" : "minecraft:mooshroom_spawn_egg", + "id" : 440 + }, + { + "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" : 466 + }, + { + "name" : "minecraft:mushroom_stew", + "id" : 260 + }, + { + "name" : "minecraft:music_disc_11", + "id" : 544 + }, + { + "name" : "minecraft:music_disc_13", + "id" : 534 + }, + { + "name" : "minecraft:music_disc_5", + "id" : 636 + }, + { + "name" : "minecraft:music_disc_blocks", + "id" : 536 + }, + { + "name" : "minecraft:music_disc_cat", + "id" : 535 + }, + { + "name" : "minecraft:music_disc_chirp", + "id" : 537 + }, + { + "name" : "minecraft:music_disc_far", + "id" : 538 + }, + { + "name" : "minecraft:music_disc_mall", + "id" : 539 + }, + { + "name" : "minecraft:music_disc_mellohi", + "id" : 540 + }, + { + "name" : "minecraft:music_disc_otherside", + "id" : 626 + }, + { + "name" : "minecraft:music_disc_pigstep", + "id" : 620 + }, + { + "name" : "minecraft:music_disc_stal", + "id" : 541 + }, + { + "name" : "minecraft:music_disc_strad", + "id" : 542 + }, + { + "name" : "minecraft:music_disc_wait", + "id" : 545 + }, + { + "name" : "minecraft:music_disc_ward", + "id" : 543 + }, + { + "name" : "minecraft:mutton", + "id" : 550 + }, + { + "name" : "minecraft:mycelium", + "id" : 110 + }, + { + "name" : "minecraft:name_tag", + "id" : 548 + }, + { + "name" : "minecraft:nautilus_shell", + "id" : 570 + }, + { + "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" : 621 + }, + { + "name" : "minecraft:nether_star", + "id" : 518 + }, + { + "name" : "minecraft:nether_wart", + "id" : 294 + }, + { + "name" : "minecraft:nether_wart_block", + "id" : 214 + }, + { + "name" : "minecraft:netherbrick", + "id" : 523 + }, + { + "name" : "minecraft:netherite_axe", + "id" : 607 + }, + { + "name" : "minecraft:netherite_block", + "id" : -270 + }, + { + "name" : "minecraft:netherite_boots", + "id" : 612 + }, + { + "name" : "minecraft:netherite_chestplate", + "id" : 610 + }, + { + "name" : "minecraft:netherite_helmet", + "id" : 609 + }, + { + "name" : "minecraft:netherite_hoe", + "id" : 608 + }, + { + "name" : "minecraft:netherite_ingot", + "id" : 603 + }, + { + "name" : "minecraft:netherite_leggings", + "id" : 611 + }, + { + "name" : "minecraft:netherite_pickaxe", + "id" : 606 + }, + { + "name" : "minecraft:netherite_scrap", + "id" : 613 + }, + { + "name" : "minecraft:netherite_shovel", + "id" : 605 + }, + { + "name" : "minecraft:netherite_sword", + "id" : 604 + }, + { + "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" : 470 + }, + { + "name" : "minecraft:oak_boat", + "id" : 375 + }, + { + "name" : "minecraft:oak_chest_boat", + "id" : 638 + }, + { + "name" : "minecraft:oak_sign", + "id" : 358 + }, + { + "name" : "minecraft:oak_stairs", + "id" : 53 + }, + { + "name" : "minecraft:observer", + "id" : 251 + }, + { + "name" : "minecraft:obsidian", + "id" : 49 + }, + { + "name" : "minecraft:ocelot_spawn_egg", + "id" : 451 + }, + { + "name" : "minecraft:ochre_froglight", + "id" : -471 + }, + { + "name" : "minecraft:orange_candle", + "id" : -414 + }, + { + "name" : "minecraft:orange_candle_cake", + "id" : -431 + }, + { + "name" : "minecraft:orange_dye", + "id" : 409 + }, + { + "name" : "minecraft:orange_glazed_terracotta", + "id" : 221 + }, + { + "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" : 357 + }, + { + "name" : "minecraft:panda_spawn_egg", + "id" : 489 + }, + { + "name" : "minecraft:paper", + "id" : 386 + }, + { + "name" : "minecraft:parrot_spawn_egg", + "id" : 478 + }, + { + "name" : "minecraft:pearlescent_froglight", + "id" : -469 + }, + { + "name" : "minecraft:phantom_membrane", + "id" : 574 + }, + { + "name" : "minecraft:phantom_spawn_egg", + "id" : 486 + }, + { + "name" : "minecraft:pig_spawn_egg", + "id" : 437 + }, + { + "name" : "minecraft:piglin_banner_pattern", + "id" : 587 + }, + { + "name" : "minecraft:piglin_brute_spawn_egg", + "id" : 499 + }, + { + "name" : "minecraft:piglin_spawn_egg", + "id" : 497 + }, + { + "name" : "minecraft:pillager_spawn_egg", + "id" : 491 + }, + { + "name" : "minecraft:pink_candle", + "id" : -419 + }, + { + "name" : "minecraft:pink_candle_cake", + "id" : -436 + }, + { + "name" : "minecraft:pink_dye", + "id" : 404 + }, + { + "name" : "minecraft:pink_glazed_terracotta", + "id" : 226 + }, + { + "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" : 472 + }, + { + "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" : 559 + }, + { + "name" : "minecraft:porkchop", + "id" : 262 + }, + { + "name" : "minecraft:portal", + "id" : 90 + }, + { + "name" : "minecraft:potato", + "id" : 280 + }, + { + "name" : "minecraft:potatoes", + "id" : 142 + }, + { + "name" : "minecraft:potion", + "id" : 426 + }, + { + "name" : "minecraft:powder_snow", + "id" : -306 + }, + { + "name" : "minecraft:powder_snow_bucket", + "id" : 368 + }, + { + "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" : 549 + }, + { + "name" : "minecraft:prismarine_shard", + "id" : 565 + }, + { + "name" : "minecraft:prismarine_stairs", + "id" : -2 + }, + { + "name" : "minecraft:pufferfish", + "id" : 267 + }, + { + "name" : "minecraft:pufferfish_bucket", + "id" : 367 + }, + { + "name" : "minecraft:pufferfish_spawn_egg", + "id" : 481 + }, + { + "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" : 400 + }, + { + "name" : "minecraft:purple_glazed_terracotta", + "id" : 219 + }, + { + "name" : "minecraft:purpur_block", + "id" : 201 + }, + { + "name" : "minecraft:purpur_stairs", + "id" : 203 + }, + { + "name" : "minecraft:quartz", + "id" : 524 + }, + { + "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" : 528 + }, + { + "name" : "minecraft:rabbit_hide", + "id" : 529 + }, + { + "name" : "minecraft:rabbit_spawn_egg", + "id" : 459 + }, + { + "name" : "minecraft:rabbit_stew", + "id" : 290 + }, + { + "name" : "minecraft:rail", + "id" : 66 + }, + { + "name" : "minecraft:rapid_fertilizer", + "id" : 597 + }, + { + "name" : "minecraft:ravager_spawn_egg", + "id" : 493 + }, + { + "name" : "minecraft:raw_copper", + "id" : 507 + }, + { + "name" : "minecraft:raw_copper_block", + "id" : -452 + }, + { + "name" : "minecraft:raw_gold", + "id" : 506 + }, + { + "name" : "minecraft:raw_gold_block", + "id" : -453 + }, + { + "name" : "minecraft:raw_iron", + "id" : 505 + }, + { + "name" : "minecraft:raw_iron_block", + "id" : -451 + }, + { + "name" : "minecraft:recovery_compass", + "id" : 646 + }, + { + "name" : "minecraft:red_candle", + "id" : -427 + }, + { + "name" : "minecraft:red_candle_cake", + "id" : -444 + }, + { + "name" : "minecraft:red_dye", + "id" : 396 + }, + { + "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:redstone", + "id" : 373 + }, + { + "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" : 419 + }, + { + "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" : 371 + }, + { + "name" : "minecraft:salmon", + "id" : 265 + }, + { + "name" : "minecraft:salmon_bucket", + "id" : 365 + }, + { + "name" : "minecraft:salmon_spawn_egg", + "id" : 482 + }, + { + "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" : 572 + }, + { + "name" : "minecraft:sea_lantern", + "id" : 169 + }, + { + "name" : "minecraft:sea_pickle", + "id" : -156 + }, + { + "name" : "minecraft:seagrass", + "id" : -130 + }, + { + "name" : "minecraft:shears", + "id" : 421 + }, + { + "name" : "minecraft:sheep_spawn_egg", + "id" : 438 + }, + { + "name" : "minecraft:shield", + "id" : 355 + }, + { + "name" : "minecraft:shroomlight", + "id" : -230 + }, + { + "name" : "minecraft:shulker_box", + "id" : 218 + }, + { + "name" : "minecraft:shulker_shell", + "id" : 566 + }, + { + "name" : "minecraft:shulker_spawn_egg", + "id" : 469 + }, + { + "name" : "minecraft:silver_glazed_terracotta", + "id" : 228 + }, + { + "name" : "minecraft:silverfish_spawn_egg", + "id" : 443 + }, + { + "name" : "minecraft:skeleton_horse_spawn_egg", + "id" : 467 + }, + { + "name" : "minecraft:skeleton_spawn_egg", + "id" : 444 + }, + { + "name" : "minecraft:skull", + "id" : 516 + }, + { + "name" : "minecraft:skull_banner_pattern", + "id" : 583 + }, + { + "name" : "minecraft:slime", + "id" : 165 + }, + { + "name" : "minecraft:slime_ball", + "id" : 388 + }, + { + "name" : "minecraft:slime_spawn_egg", + "id" : 445 + }, + { + "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:snow", + "id" : 80 + }, + { + "name" : "minecraft:snow_layer", + "id" : 78 + }, + { + "name" : "minecraft:snowball", + "id" : 374 + }, + { + "name" : "minecraft:soul_campfire", + "id" : 622 + }, + { + "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" : 600 + }, + { + "name" : "minecraft:spawn_egg", + "id" : 652 + }, + { + "name" : "minecraft:spider_eye", + "id" : 278 + }, + { + "name" : "minecraft:spider_spawn_egg", + "id" : 446 + }, + { + "name" : "minecraft:splash_potion", + "id" : 561 + }, + { + "name" : "minecraft:sponge", + "id" : 19 + }, + { + "name" : "minecraft:spore_blossom", + "id" : -321 + }, + { + "name" : "minecraft:spruce_boat", + "id" : 378 + }, + { + "name" : "minecraft:spruce_button", + "id" : -144 + }, + { + "name" : "minecraft:spruce_chest_boat", + "id" : 641 + }, + { + "name" : "minecraft:spruce_door", + "id" : 553 + }, + { + "name" : "minecraft:spruce_fence_gate", + "id" : 183 + }, + { + "name" : "minecraft:spruce_pressure_plate", + "id" : -154 + }, + { + "name" : "minecraft:spruce_sign", + "id" : 576 + }, + { + "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" : 625 + }, + { + "name" : "minecraft:squid_spawn_egg", + "id" : 450 + }, + { + "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" : 320 + }, + { + "name" : "minecraft:sticky_piston", + "id" : 29 + }, + { + "name" : "minecraft:sticky_piston_arm_collision", + "id" : -217 + }, + { + "name" : "minecraft:stone", + "id" : 1 + }, + { + "name" : "minecraft:stone_axe", + "id" : 315 + }, + { + "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" : 330 + }, + { + "name" : "minecraft:stone_pickaxe", + "id" : 314 + }, + { + "name" : "minecraft:stone_pressure_plate", + "id" : 70 + }, + { + "name" : "minecraft:stone_shovel", + "id" : 313 + }, + { + "name" : "minecraft:stone_stairs", + "id" : 67 + }, + { + "name" : "minecraft:stone_sword", + "id" : 312 + }, + { + "name" : "minecraft:stonebrick", + "id" : 98 + }, + { + "name" : "minecraft:stonecutter", + "id" : 245 + }, + { + "name" : "minecraft:stonecutter_block", + "id" : -197 + }, + { + "name" : "minecraft:stray_spawn_egg", + "id" : 462 + }, + { + "name" : "minecraft:strider_spawn_egg", + "id" : 495 + }, + { + "name" : "minecraft:string", + "id" : 326 + }, + { + "name" : "minecraft:stripped_acacia_log", + "id" : -8 + }, + { + "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" : 416 + }, + { + "name" : "minecraft:sugar_cane", + "id" : 385 + }, + { + "name" : "minecraft:suspicious_stew", + "id" : 590 + }, + { + "name" : "minecraft:sweet_berries", + "id" : 287 + }, + { + "name" : "minecraft:sweet_berry_bush", + "id" : -207 + }, + { + "name" : "minecraft:tadpole_bucket", + "id" : 630 + }, + { + "name" : "minecraft:tadpole_spawn_egg", + "id" : 629 + }, + { + "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" : 525 + }, + { + "name" : "minecraft:torch", + "id" : 50 + }, + { + "name" : "minecraft:totem_of_undying", + "id" : 568 + }, + { + "name" : "minecraft:trader_llama_spawn_egg", + "id" : 648 + }, + { + "name" : "minecraft:trapdoor", + "id" : 96 + }, + { + "name" : "minecraft:trapped_chest", + "id" : 146 + }, + { + "name" : "minecraft:trident", + "id" : 546 + }, + { + "name" : "minecraft:trip_wire", + "id" : 132 + }, + { + "name" : "minecraft:tripwire_hook", + "id" : 131 + }, + { + "name" : "minecraft:tropical_fish", + "id" : 266 + }, + { + "name" : "minecraft:tropical_fish_bucket", + "id" : 366 + }, + { + "name" : "minecraft:tropical_fish_spawn_egg", + "id" : 479 + }, + { + "name" : "minecraft:tuff", + "id" : -333 + }, + { + "name" : "minecraft:turtle_egg", + "id" : -159 + }, + { + "name" : "minecraft:turtle_helmet", + "id" : 573 + }, + { + "name" : "minecraft:turtle_spawn_egg", + "id" : 485 + }, + { + "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" : 476 + }, + { + "name" : "minecraft:villager_spawn_egg", + "id" : 449 + }, + { + "name" : "minecraft:vindicator_spawn_egg", + "id" : 474 + }, + { + "name" : "minecraft:vine", + "id" : 106 + }, + { + "name" : "minecraft:wall_banner", + "id" : 177 + }, + { + "name" : "minecraft:wall_sign", + "id" : 68 + }, + { + "name" : "minecraft:wandering_trader_spawn_egg", + "id" : 492 + }, + { + "name" : "minecraft:warden_spawn_egg", + "id" : 632 + }, + { + "name" : "minecraft:warped_button", + "id" : -261 + }, + { + "name" : "minecraft:warped_door", + "id" : 617 + }, + { + "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" : 618 + }, + { + "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" : 615 + }, + { + "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" : 362 + }, + { + "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" : 334 + }, + { + "name" : "minecraft:wheat_seeds", + "id" : 291 + }, + { + "name" : "minecraft:white_candle", + "id" : -413 + }, + { + "name" : "minecraft:white_candle_cake", + "id" : -430 + }, + { + "name" : "minecraft:white_dye", + "id" : 410 + }, + { + "name" : "minecraft:white_glazed_terracotta", + "id" : 220 + }, + { + "name" : "minecraft:witch_spawn_egg", + "id" : 452 + }, + { + "name" : "minecraft:wither_rose", + "id" : -216 + }, + { + "name" : "minecraft:wither_skeleton_spawn_egg", + "id" : 464 + }, + { + "name" : "minecraft:wolf_spawn_egg", + "id" : 439 + }, + { + "name" : "minecraft:wood", + "id" : -212 + }, + { + "name" : "minecraft:wooden_axe", + "id" : 311 + }, + { + "name" : "minecraft:wooden_button", + "id" : 143 + }, + { + "name" : "minecraft:wooden_door", + "id" : 359 + }, + { + "name" : "minecraft:wooden_hoe", + "id" : 329 + }, + { + "name" : "minecraft:wooden_pickaxe", + "id" : 310 + }, + { + "name" : "minecraft:wooden_pressure_plate", + "id" : 72 + }, + { + "name" : "minecraft:wooden_shovel", + "id" : 309 + }, + { + "name" : "minecraft:wooden_slab", + "id" : 158 + }, + { + "name" : "minecraft:wooden_sword", + "id" : 308 + }, + { + "name" : "minecraft:wool", + "id" : 35 + }, + { + "name" : "minecraft:writable_book", + "id" : 510 + }, + { + "name" : "minecraft:written_book", + "id" : 511 + }, + { + "name" : "minecraft:yellow_candle", + "id" : -417 + }, + { + "name" : "minecraft:yellow_candle_cake", + "id" : -434 + }, + { + "name" : "minecraft:yellow_dye", + "id" : 406 + }, + { + "name" : "minecraft:yellow_flower", + "id" : 37 + }, + { + "name" : "minecraft:yellow_glazed_terracotta", + "id" : 224 + }, + { + "name" : "minecraft:zoglin_spawn_egg", + "id" : 498 + }, + { + "name" : "minecraft:zombie_horse_spawn_egg", + "id" : 468 + }, + { + "name" : "minecraft:zombie_pigman_spawn_egg", + "id" : 448 + }, + { + "name" : "minecraft:zombie_spawn_egg", + "id" : 447 + }, + { + "name" : "minecraft:zombie_villager_spawn_egg", + "id" : 477 + } +] \ No newline at end of file From 656c8b1a5ebeb990025f9f1bc207b519b85ced19 Mon Sep 17 00:00:00 2001 From: Konicai <71294714+Konicai@users.noreply.github.com> Date: Sat, 3 Dec 2022 23:59:27 -0500 Subject: [PATCH 31/81] Update mappings --- core/src/main/resources/mappings | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/main/resources/mappings b/core/src/main/resources/mappings index 5bd26dd73..810a4e817 160000 --- a/core/src/main/resources/mappings +++ b/core/src/main/resources/mappings @@ -1 +1 @@ -Subproject commit 5bd26dd735bd89dd50e5c55a0d022f7c70916300 +Subproject commit 810a4e8174fd9d5b81c5e7d2f3c2f6164565eb9c From 91a2e79bd1aab3e970c4f53ebb57a275372588e1 Mon Sep 17 00:00:00 2001 From: Konicai <71294714+Konicai@users.noreply.github.com> Date: Sun, 4 Dec 2022 00:30:16 -0500 Subject: [PATCH 32/81] Actually update mappings --- core/src/main/resources/mappings | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/main/resources/mappings b/core/src/main/resources/mappings index 810a4e817..e8703ccb1 160000 --- a/core/src/main/resources/mappings +++ b/core/src/main/resources/mappings @@ -1 +1 @@ -Subproject commit 810a4e8174fd9d5b81c5e7d2f3c2f6164565eb9c +Subproject commit e8703ccb187f98cd845357395d7b4ecfafbcd864 From f9a52ffc96b6ab1cd10425836bd5c266a0e62b3d Mon Sep 17 00:00:00 2001 From: Camotoy <20743703+Camotoy@users.noreply.github.com> Date: Sun, 4 Dec 2022 00:56:43 -0500 Subject: [PATCH 33/81] Add support for SetContentPacket containerId 0 --- .../JavaContainerSetContentTranslator.java | 23 +++++++++++-------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/core/src/main/java/org/geysermc/geyser/translator/protocol/java/inventory/JavaContainerSetContentTranslator.java b/core/src/main/java/org/geysermc/geyser/translator/protocol/java/inventory/JavaContainerSetContentTranslator.java index 619825338..cfe1c404e 100644 --- a/core/src/main/java/org/geysermc/geyser/translator/protocol/java/inventory/JavaContainerSetContentTranslator.java +++ b/core/src/main/java/org/geysermc/geyser/translator/protocol/java/inventory/JavaContainerSetContentTranslator.java @@ -31,6 +31,7 @@ import org.geysermc.geyser.inventory.GeyserItemStack; import org.geysermc.geyser.inventory.Inventory; import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.translator.inventory.InventoryTranslator; +import org.geysermc.geyser.translator.inventory.PlayerInventoryTranslator; import org.geysermc.geyser.translator.protocol.PacketTranslator; import org.geysermc.geyser.translator.protocol.Translator; import org.geysermc.geyser.util.InventoryUtils; @@ -46,7 +47,7 @@ public class JavaContainerSetContentTranslator extends PacketTranslator inventorySize) { + if (i >= inventorySize) { GeyserImpl geyser = session.getGeyser(); geyser.getLogger().warning("ClientboundContainerSetContentPacket sent to " + session.bedrockUsername() + " that exceeds inventory size!"); @@ -54,10 +55,7 @@ public class JavaContainerSetContentTranslator extends PacketTranslator 0 || stateId != inventory.getStateId()); @@ -80,4 +75,14 @@ public class JavaContainerSetContentTranslator extends PacketTranslator Date: Sun, 4 Dec 2022 13:34:51 -0500 Subject: [PATCH 34/81] Fix rare dimension switch inconsistencies Fixes #3161 --- .../java/org/geysermc/geyser/session/GeyserSession.java | 1 - .../translator/protocol/java/JavaRespawnTranslator.java | 7 ++----- .../main/java/org/geysermc/geyser/util/DimensionUtils.java | 4 +++- 3 files changed, 5 insertions(+), 7 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 17a609fb7..056fa52ee 100644 --- a/core/src/main/java/org/geysermc/geyser/session/GeyserSession.java +++ b/core/src/main/java/org/geysermc/geyser/session/GeyserSession.java @@ -155,7 +155,6 @@ import org.geysermc.geyser.inventory.recipe.GeyserStonecutterData; import org.geysermc.geyser.level.JavaDimension; import org.geysermc.geyser.level.WorldManager; import org.geysermc.geyser.level.physics.CollisionManager; -import org.geysermc.geyser.network.GameProtocol; import org.geysermc.geyser.network.netty.LocalSession; import org.geysermc.geyser.registry.Registries; import org.geysermc.geyser.registry.type.BlockMappings; diff --git a/core/src/main/java/org/geysermc/geyser/translator/protocol/java/JavaRespawnTranslator.java b/core/src/main/java/org/geysermc/geyser/translator/protocol/java/JavaRespawnTranslator.java index 0f02256d0..45db499f1 100644 --- a/core/src/main/java/org/geysermc/geyser/translator/protocol/java/JavaRespawnTranslator.java +++ b/core/src/main/java/org/geysermc/geyser/translator/protocol/java/JavaRespawnTranslator.java @@ -83,11 +83,8 @@ public class JavaRespawnTranslator extends PacketTranslator Date: Sun, 4 Dec 2022 18:12:07 -0500 Subject: [PATCH 35/81] A start on camels and hanging signs --- .../geyser/entity/EntityDefinitions.java | 8 +++ .../type/living/animal/horse/CamelEntity.java | 70 +++++++++++++++++++ .../entity/SignBlockEntityTranslator.java | 3 +- .../BedrockBlockEntityDataTranslator.java | 4 ++ 4 files changed, 84 insertions(+), 1 deletion(-) create mode 100644 core/src/main/java/org/geysermc/geyser/entity/type/living/animal/horse/CamelEntity.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 a552d0875..cad69a117 100644 --- a/core/src/main/java/org/geysermc/geyser/entity/EntityDefinitions.java +++ b/core/src/main/java/org/geysermc/geyser/entity/EntityDefinitions.java @@ -60,6 +60,7 @@ public final class EntityDefinitions { public static final EntityDefinition BEE; public static final EntityDefinition BLAZE; public static final EntityDefinition BOAT; + public static final EntityDefinition CAMEL; public static final EntityDefinition CAT; public static final EntityDefinition CAVE_SPIDER; public static final EntityDefinition CHEST_MINECART; @@ -894,6 +895,13 @@ public final class EntityDefinitions { .type(EntityType.TRADER_LLAMA) .identifier("minecraft:llama") .build(); + CAMEL = EntityDefinition.inherited(CamelEntity::new, abstractHorseEntityBase) + .type(EntityType.CAMEL) + .identifier("minecraft:llama") // todo 1.20 + .height(2.375f).width(1.7f) + .addTranslator(MetadataType.BOOLEAN, CamelEntity::setDashing) + .addTranslator(null) // Last pose change tick + .build(); } EntityDefinition tameableEntityBase = EntityDefinition.inherited(TameableEntity::new, ageableEntityBase) diff --git a/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/horse/CamelEntity.java b/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/horse/CamelEntity.java new file mode 100644 index 000000000..408e2ec21 --- /dev/null +++ b/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/horse/CamelEntity.java @@ -0,0 +1,70 @@ +/* + * 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.entity.type.living.animal.horse; + +import com.github.steveice10.mc.protocol.data.game.entity.metadata.Pose; +import com.github.steveice10.mc.protocol.data.game.entity.metadata.type.BooleanEntityMetadata; +import com.nukkitx.math.vector.Vector3f; +import com.nukkitx.protocol.bedrock.data.entity.EntityData; +import org.geysermc.geyser.entity.EntityDefinition; +import org.geysermc.geyser.registry.type.ItemMapping; +import org.geysermc.geyser.session.GeyserSession; + +import java.util.UUID; + +public class CamelEntity extends AbstractHorseEntity { + + private static final float SITTING_HEIGHT_DIFFERENCE = 1.43F; + + public CamelEntity(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(); + this.dirtyMetadata.put(EntityData.VARIANT, 2); // Closest llama colour to camel + } + + @Override + public boolean canEat(String javaIdentifierStripped, ItemMapping mapping) { + return "cactus".equals(javaIdentifierStripped); + } + + @Override + protected void setDimensions(Pose pose) { + if (pose == Pose.SITTING) { + setBoundingBoxWidth(definition.height() - SITTING_HEIGHT_DIFFERENCE); + setBoundingBoxWidth(definition.width()); + } else { + super.setDimensions(pose); + } + } + + public void setDashing(BooleanEntityMetadata entityMetadata) { + + } +} diff --git a/core/src/main/java/org/geysermc/geyser/translator/level/block/entity/SignBlockEntityTranslator.java b/core/src/main/java/org/geysermc/geyser/translator/level/block/entity/SignBlockEntityTranslator.java index bd3f96836..1b4fd6a10 100644 --- a/core/src/main/java/org/geysermc/geyser/translator/level/block/entity/SignBlockEntityTranslator.java +++ b/core/src/main/java/org/geysermc/geyser/translator/level/block/entity/SignBlockEntityTranslator.java @@ -32,7 +32,7 @@ import com.nukkitx.nbt.NbtMapBuilder; import org.geysermc.geyser.translator.text.MessageTranslator; import org.geysermc.geyser.util.SignUtils; -@BlockEntity(type = BlockEntityType.SIGN) +@BlockEntity(type = {BlockEntityType.SIGN, BlockEntityType.HANGING_SIGN}) public class SignBlockEntityTranslator extends BlockEntityTranslator { /** * Maps a color stored in a sign's Color tag to its ARGB value. @@ -88,6 +88,7 @@ public class SignBlockEntityTranslator extends BlockEntityTranslator { signWidth += SignUtils.getCharacterWidth(c); } + // todo 1.20: update for hanging signs (smaller width). Currently OK because bedrock sees hanging signs as normal signs if (signWidth <= SignUtils.BEDROCK_CHARACTER_WIDTH_MAX) { finalSignLine.append(c); } else { diff --git a/core/src/main/java/org/geysermc/geyser/translator/protocol/bedrock/BedrockBlockEntityDataTranslator.java b/core/src/main/java/org/geysermc/geyser/translator/protocol/bedrock/BedrockBlockEntityDataTranslator.java index 67f0d0d59..d70759ffb 100644 --- a/core/src/main/java/org/geysermc/geyser/translator/protocol/bedrock/BedrockBlockEntityDataTranslator.java +++ b/core/src/main/java/org/geysermc/geyser/translator/protocol/bedrock/BedrockBlockEntityDataTranslator.java @@ -57,6 +57,10 @@ public class BedrockBlockEntityDataTranslator extends PacketTranslator SignUtils.JAVA_CHARACTER_WIDTH_MAX) { // We need to apply some more logic if we went over the character width max From bd5428a2e63cc877473634280876da63b12b1734 Mon Sep 17 00:00:00 2001 From: Konicai <71294714+Konicai@users.noreply.github.com> Date: Mon, 5 Dec 2022 14:20:34 -0500 Subject: [PATCH 36/81] Alphabetize the camel --- .../geysermc/geyser/entity/EntityDefinitions.java | 14 +++++++------- 1 file changed, 7 insertions(+), 7 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 cad69a117..b97e23847 100644 --- a/core/src/main/java/org/geysermc/geyser/entity/EntityDefinitions.java +++ b/core/src/main/java/org/geysermc/geyser/entity/EntityDefinitions.java @@ -860,6 +860,13 @@ public final class EntityDefinitions { .addTranslator(MetadataType.BYTE, AbstractHorseEntity::setHorseFlags) .addTranslator(null) // UUID of owner .build(); + CAMEL = EntityDefinition.inherited(CamelEntity::new, abstractHorseEntityBase) + .type(EntityType.CAMEL) + .identifier("minecraft:llama") // todo 1.20 + .height(2.375f).width(1.7f) + .addTranslator(MetadataType.BOOLEAN, CamelEntity::setDashing) + .addTranslator(null) // Last pose change tick + .build(); HORSE = EntityDefinition.inherited(HorseEntity::new, abstractHorseEntityBase) .type(EntityType.HORSE) .height(1.6f).width(1.3965f) @@ -895,13 +902,6 @@ public final class EntityDefinitions { .type(EntityType.TRADER_LLAMA) .identifier("minecraft:llama") .build(); - CAMEL = EntityDefinition.inherited(CamelEntity::new, abstractHorseEntityBase) - .type(EntityType.CAMEL) - .identifier("minecraft:llama") // todo 1.20 - .height(2.375f).width(1.7f) - .addTranslator(MetadataType.BOOLEAN, CamelEntity::setDashing) - .addTranslator(null) // Last pose change tick - .build(); } EntityDefinition tameableEntityBase = EntityDefinition.inherited(TameableEntity::new, ageableEntityBase) From f76aa71b5b01d2d0e8f74abf80464dc4a954d98e Mon Sep 17 00:00:00 2001 From: Camotoy <20743703+Camotoy@users.noreply.github.com> Date: Mon, 5 Dec 2022 20:06:40 -0500 Subject: [PATCH 37/81] Point to stable MCProtocolLib version --- 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 62da6ca89..0a0bccf88 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -8,7 +8,7 @@ websocket = "1.5.1" protocol = "2.9.15-20221129.204554-2" raknet = "1.6.28-20220125.214016-6" mcauthlib = "d9d773e" -mcprotocollib = "1.19.3-SNAPSHOT" +mcprotocollib = "1.19.3-20221206.010348-1" packetlib = "3.0" adventure = "4.12.0-20220629.025215-9" adventure-platform = "4.1.2" From 0c79732d025ec62738f42b7d67b5ee80f9e11637 Mon Sep 17 00:00:00 2001 From: Camotoy <20743703+Camotoy@users.noreply.github.com> Date: Tue, 6 Dec 2022 17:06:10 -0500 Subject: [PATCH 38/81] Add changes from 1.19.3-rc3 --- 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 0a0bccf88..b18c2db63 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -8,7 +8,7 @@ websocket = "1.5.1" protocol = "2.9.15-20221129.204554-2" raknet = "1.6.28-20220125.214016-6" mcauthlib = "d9d773e" -mcprotocollib = "1.19.3-20221206.010348-1" +mcprotocollib = "1.19.3-20221206.215111-2" packetlib = "3.0" adventure = "4.12.0-20220629.025215-9" adventure-platform = "4.1.2" From 32829622e719049fd07eff66379ec70c078dbd12 Mon Sep 17 00:00:00 2001 From: Camotoy <20743703+Camotoy@users.noreply.github.com> Date: Wed, 7 Dec 2022 11:07:40 -0500 Subject: [PATCH 39/81] Update README --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index f5051ed04..60cbd94f7 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.50 and Minecraft Java 1.19.1/1.19.2. +### Currently supporting Minecraft Bedrock 1.19.20 - 1.19.50 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 95ebdb8ebf58ac2a484304b124804373cb9632c1 Mon Sep 17 00:00:00 2001 From: Camotoy <20743703+Camotoy@users.noreply.github.com> Date: Wed, 7 Dec 2022 11:59:20 -0500 Subject: [PATCH 40/81] Update BungeeCord version check --- .../geysermc/geyser/platform/bungeecord/GeyserBungeePlugin.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bootstrap/bungeecord/src/main/java/org/geysermc/geyser/platform/bungeecord/GeyserBungeePlugin.java b/bootstrap/bungeecord/src/main/java/org/geysermc/geyser/platform/bungeecord/GeyserBungeePlugin.java index 1c460f4de..dc7602163 100644 --- a/bootstrap/bungeecord/src/main/java/org/geysermc/geyser/platform/bungeecord/GeyserBungeePlugin.java +++ b/bootstrap/bungeecord/src/main/java/org/geysermc/geyser/platform/bungeecord/GeyserBungeePlugin.java @@ -76,7 +76,7 @@ public class GeyserBungeePlugin extends Plugin implements GeyserBootstrap { // Copied from ViaVersion. // https://github.com/ViaVersion/ViaVersion/blob/b8072aad86695cc8ec6f5e4103e43baf3abf6cc5/bungee/src/main/java/us/myles/ViaVersion/BungeePlugin.java#L43 try { - ProtocolConstants.class.getField("MINECRAFT_1_19_1"); + ProtocolConstants.class.getField("MINECRAFT_1_19_3"); } catch (NoSuchFieldException e) { getLogger().warning(" / \\"); getLogger().warning(" / \\"); From e7544c0bb44eee07b9da628a99756abeeed5d6db Mon Sep 17 00:00:00 2001 From: Camotoy <20743703+Camotoy@users.noreply.github.com> Date: Wed, 7 Dec 2022 20:09:48 -0500 Subject: [PATCH 41/81] Fix some chat not appearing for Bedrock users --- .../geyser/session/GeyserSession.java | 72 ++----------------- .../java/JavaDisguisedChatTranslator.java | 41 +++++++++++ .../java/JavaPlayerChatTranslator.java | 41 +---------- .../translator/text/MessageTranslator.java | 45 +++++++++++- 4 files changed, 91 insertions(+), 108 deletions(-) create mode 100644 core/src/main/java/org/geysermc/geyser/translator/protocol/java/JavaDisguisedChatTranslator.java 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 7a407f6f9..2e3ee4dde 100644 --- a/core/src/main/java/org/geysermc/geyser/session/GeyserSession.java +++ b/core/src/main/java/org/geysermc/geyser/session/GeyserSession.java @@ -58,58 +58,21 @@ import com.github.steveice10.mc.protocol.packet.ingame.serverbound.player.Server import com.github.steveice10.mc.protocol.packet.login.serverbound.ServerboundCustomQueryPacket; import com.github.steveice10.packetlib.BuiltinFlags; import com.github.steveice10.packetlib.Session; -import com.github.steveice10.packetlib.event.session.ConnectedEvent; -import com.github.steveice10.packetlib.event.session.DisconnectedEvent; -import com.github.steveice10.packetlib.event.session.PacketErrorEvent; -import com.github.steveice10.packetlib.event.session.PacketSendingEvent; -import com.github.steveice10.packetlib.event.session.SessionAdapter; +import com.github.steveice10.packetlib.event.session.*; import com.github.steveice10.packetlib.packet.Packet; import com.github.steveice10.packetlib.tcp.TcpClientSession; import com.github.steveice10.packetlib.tcp.TcpSession; import com.nukkitx.math.GenericMath; -import com.nukkitx.math.vector.Vector2f; -import com.nukkitx.math.vector.Vector2i; -import com.nukkitx.math.vector.Vector3d; -import com.nukkitx.math.vector.Vector3f; -import com.nukkitx.math.vector.Vector3i; +import com.nukkitx.math.vector.*; import com.nukkitx.nbt.NbtMap; import com.nukkitx.protocol.bedrock.BedrockPacket; import com.nukkitx.protocol.bedrock.BedrockServerSession; -import com.nukkitx.protocol.bedrock.data.Ability; -import com.nukkitx.protocol.bedrock.data.AbilityLayer; -import com.nukkitx.protocol.bedrock.data.AttributeData; -import com.nukkitx.protocol.bedrock.data.AuthoritativeMovementMode; -import com.nukkitx.protocol.bedrock.data.ChatRestrictionLevel; -import com.nukkitx.protocol.bedrock.data.GamePublishSetting; -import com.nukkitx.protocol.bedrock.data.GameRuleData; -import com.nukkitx.protocol.bedrock.data.GameType; -import com.nukkitx.protocol.bedrock.data.PlayerPermission; -import com.nukkitx.protocol.bedrock.data.SoundEvent; -import com.nukkitx.protocol.bedrock.data.SyncedPlayerMovementSettings; +import com.nukkitx.protocol.bedrock.data.*; import com.nukkitx.protocol.bedrock.data.command.CommandPermission; import com.nukkitx.protocol.bedrock.data.entity.EntityFlag; -import com.nukkitx.protocol.bedrock.packet.AvailableEntityIdentifiersPacket; -import com.nukkitx.protocol.bedrock.packet.BiomeDefinitionListPacket; -import com.nukkitx.protocol.bedrock.packet.ChunkRadiusUpdatedPacket; -import com.nukkitx.protocol.bedrock.packet.ClientboundMapItemDataPacket; -import com.nukkitx.protocol.bedrock.packet.CraftingDataPacket; -import com.nukkitx.protocol.bedrock.packet.CreativeContentPacket; -import com.nukkitx.protocol.bedrock.packet.EmoteListPacket; -import com.nukkitx.protocol.bedrock.packet.GameRulesChangedPacket; -import com.nukkitx.protocol.bedrock.packet.ItemComponentPacket; -import com.nukkitx.protocol.bedrock.packet.LevelSoundEvent2Packet; -import com.nukkitx.protocol.bedrock.packet.PlayStatusPacket; -import com.nukkitx.protocol.bedrock.packet.PlayerFogPacket; -import com.nukkitx.protocol.bedrock.packet.SetTimePacket; -import com.nukkitx.protocol.bedrock.packet.StartGamePacket; -import com.nukkitx.protocol.bedrock.packet.TextPacket; -import com.nukkitx.protocol.bedrock.packet.TransferPacket; -import com.nukkitx.protocol.bedrock.packet.UpdateAbilitiesPacket; -import com.nukkitx.protocol.bedrock.packet.UpdateAdventureSettingsPacket; -import com.nukkitx.protocol.bedrock.packet.UpdateAttributesPacket; +import com.nukkitx.protocol.bedrock.packet.*; import io.netty.channel.Channel; import io.netty.channel.EventLoop; -import it.unimi.dsi.fastutil.bytes.ByteArrays; import it.unimi.dsi.fastutil.ints.Int2IntMap; import it.unimi.dsi.fastutil.ints.Int2IntOpenHashMap; import it.unimi.dsi.fastutil.ints.Int2ObjectMap; @@ -162,20 +125,7 @@ import org.geysermc.geyser.registry.type.ItemMapping; import org.geysermc.geyser.registry.type.ItemMappings; import org.geysermc.geyser.session.auth.AuthData; import org.geysermc.geyser.session.auth.BedrockClientData; -import org.geysermc.geyser.session.cache.AdvancementsCache; -import org.geysermc.geyser.session.cache.BookEditCache; -import org.geysermc.geyser.session.cache.ChunkCache; -import org.geysermc.geyser.session.cache.EntityCache; -import org.geysermc.geyser.session.cache.EntityEffectCache; -import org.geysermc.geyser.session.cache.FormCache; -import org.geysermc.geyser.session.cache.LodestoneCache; -import org.geysermc.geyser.session.cache.PistonCache; -import org.geysermc.geyser.session.cache.PreferencesCache; -import org.geysermc.geyser.session.cache.SkullCache; -import org.geysermc.geyser.session.cache.TagCache; -import org.geysermc.geyser.session.cache.TeleportCache; -import org.geysermc.geyser.session.cache.WorldBorder; -import org.geysermc.geyser.session.cache.WorldCache; +import org.geysermc.geyser.session.cache.*; import org.geysermc.geyser.skin.FloodgateSkinUploader; import org.geysermc.geyser.text.GeyserLocale; import org.geysermc.geyser.text.MinecraftLocale; @@ -191,15 +141,7 @@ import java.net.ConnectException; import java.net.InetSocketAddress; import java.nio.charset.StandardCharsets; import java.time.Instant; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.BitSet; -import java.util.Collections; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.UUID; +import java.util.*; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ScheduledFuture; import java.util.concurrent.TimeUnit; @@ -1416,8 +1358,6 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource { return clientData.getLanguageCode(); } - // TODO: 1.19.3 int offest and ack'd messages BitSet??? - /** * Sends a chat message to the Java server. */ diff --git a/core/src/main/java/org/geysermc/geyser/translator/protocol/java/JavaDisguisedChatTranslator.java b/core/src/main/java/org/geysermc/geyser/translator/protocol/java/JavaDisguisedChatTranslator.java new file mode 100644 index 000000000..2ad45fe52 --- /dev/null +++ b/core/src/main/java/org/geysermc/geyser/translator/protocol/java/JavaDisguisedChatTranslator.java @@ -0,0 +1,41 @@ +/* + * 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.java; + +import com.github.steveice10.mc.protocol.packet.ingame.clientbound.ClientboundDisguisedChatPacket; +import org.geysermc.geyser.session.GeyserSession; +import org.geysermc.geyser.translator.protocol.PacketTranslator; +import org.geysermc.geyser.translator.protocol.Translator; +import org.geysermc.geyser.translator.text.MessageTranslator; + +@Translator(packet = ClientboundDisguisedChatPacket.class) +public class JavaDisguisedChatTranslator extends PacketTranslator { + + @Override + public void translate(GeyserSession session, ClientboundDisguisedChatPacket packet) { + MessageTranslator.handleChatPacket(session, packet.getMessage(), packet.getChatType(), packet.getTargetName(), packet.getName()); + } +} diff --git a/core/src/main/java/org/geysermc/geyser/translator/protocol/java/JavaPlayerChatTranslator.java b/core/src/main/java/org/geysermc/geyser/translator/protocol/java/JavaPlayerChatTranslator.java index 4c2d51cb8..e06182b8d 100644 --- a/core/src/main/java/org/geysermc/geyser/translator/protocol/java/JavaPlayerChatTranslator.java +++ b/core/src/main/java/org/geysermc/geyser/translator/protocol/java/JavaPlayerChatTranslator.java @@ -26,57 +26,18 @@ package org.geysermc.geyser.translator.protocol.java; import com.github.steveice10.mc.protocol.packet.ingame.clientbound.ClientboundPlayerChatPacket; -import com.nukkitx.protocol.bedrock.packet.TextPacket; import net.kyori.adventure.text.Component; -import net.kyori.adventure.text.TranslatableComponent; import org.geysermc.geyser.session.GeyserSession; -import org.geysermc.geyser.text.TextDecoration; import org.geysermc.geyser.translator.protocol.PacketTranslator; import org.geysermc.geyser.translator.protocol.Translator; import org.geysermc.geyser.translator.text.MessageTranslator; -import java.util.ArrayList; -import java.util.List; -import java.util.Set; - @Translator(packet = ClientboundPlayerChatPacket.class) public class JavaPlayerChatTranslator extends PacketTranslator { @Override public void translate(GeyserSession session, ClientboundPlayerChatPacket packet) { - TextPacket textPacket = new TextPacket(); - textPacket.setPlatformChatId(""); - textPacket.setSourceName(""); - textPacket.setXuid(session.getAuthData().xuid()); - textPacket.setType(TextPacket.Type.CHAT); - - textPacket.setNeedsTranslation(false); Component message = packet.getUnsignedContent() == null ? Component.text(packet.getContent()) : packet.getUnsignedContent(); - - TextDecoration decoration = session.getChatTypes().get(packet.getChatType()); - if (decoration != null) { - // As of 1.19 - do this to apply all the styling for signed messages - // Though, Bedrock cannot care about the signed stuff. - TranslatableComponent.Builder withDecoration = Component.translatable() - .key(decoration.translationKey()) - .style(decoration.style()); - Set parameters = decoration.parameters(); - List args = new ArrayList<>(3); - if (parameters.contains(TextDecoration.Parameter.TARGET)) { - args.add(packet.getTargetName()); - } - if (parameters.contains(TextDecoration.Parameter.SENDER)) { - args.add(packet.getName()); - } - if (parameters.contains(TextDecoration.Parameter.CONTENT)) { - args.add(message); - } - withDecoration.args(args); - textPacket.setMessage(MessageTranslator.convertMessage(withDecoration.build(), session.locale())); - } else { - textPacket.setMessage(MessageTranslator.convertMessage(message, session.locale())); - } - - session.sendUpstreamPacket(textPacket); + MessageTranslator.handleChatPacket(session, message, packet.getChatType(), packet.getTargetName(), packet.getName()); } } diff --git a/core/src/main/java/org/geysermc/geyser/translator/text/MessageTranslator.java b/core/src/main/java/org/geysermc/geyser/translator/text/MessageTranslator.java index 1b267823a..3d5f5fe0f 100644 --- a/core/src/main/java/org/geysermc/geyser/translator/text/MessageTranslator.java +++ b/core/src/main/java/org/geysermc/geyser/translator/text/MessageTranslator.java @@ -27,7 +27,9 @@ package org.geysermc.geyser.translator.text; import com.github.steveice10.mc.protocol.data.DefaultComponentSerializer; import com.github.steveice10.mc.protocol.data.game.scoreboard.TeamColor; +import com.nukkitx.protocol.bedrock.packet.TextPacket; import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.TranslatableComponent; import net.kyori.adventure.text.renderer.TranslatableComponentRenderer; import net.kyori.adventure.text.serializer.gson.GsonComponentSerializer; import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer; @@ -36,8 +38,7 @@ import org.geysermc.geyser.GeyserImpl; import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.text.*; -import java.util.EnumMap; -import java.util.Map; +import java.util.*; public class MessageTranslator { // These are used for handling the translations of the messages @@ -250,6 +251,46 @@ public class MessageTranslator { return PlainTextComponentSerializer.plainText().serialize(messageComponent); } + public static void handleChatPacket(GeyserSession session, Component message, int chatType, Component targetName, Component sender) { + TextPacket textPacket = new TextPacket(); + textPacket.setPlatformChatId(""); + textPacket.setSourceName(""); + textPacket.setXuid(session.getAuthData().xuid()); + textPacket.setType(TextPacket.Type.CHAT); + + textPacket.setNeedsTranslation(false); + + TextDecoration decoration = session.getChatTypes().get(chatType); + if (decoration != null) { + // As of 1.19 - do this to apply all the styling for signed messages + // Though, Bedrock cannot care about the signed stuff. + TranslatableComponent.Builder withDecoration = Component.translatable() + .key(decoration.translationKey()) + .style(decoration.style()); + Set parameters = decoration.parameters(); + List args = new ArrayList<>(3); + if (parameters.contains(TextDecoration.Parameter.TARGET)) { + args.add(targetName); + } + if (parameters.contains(TextDecoration.Parameter.SENDER)) { + args.add(sender); + } + if (parameters.contains(TextDecoration.Parameter.CONTENT)) { + args.add(message); + } + withDecoration.args(args); + textPacket.setMessage(MessageTranslator.convertMessage(withDecoration.build(), session.locale())); + } else { + session.getGeyser().getLogger().debug("Likely illegal chat type detection found."); + if (session.getGeyser().getConfig().isDebugMode()) { + Thread.dumpStack(); + } + textPacket.setMessage(MessageTranslator.convertMessage(message, session.locale())); + } + + session.sendUpstreamPacket(textPacket); + } + /** * Convert a team color to a chat color * From 57e34372d8e408835a34d3c0ba7d2ab18d693400 Mon Sep 17 00:00:00 2001 From: Camotoy <20743703+Camotoy@users.noreply.github.com> Date: Wed, 7 Dec 2022 22:54:43 -0500 Subject: [PATCH 42/81] Update MCProtocolLib to potentially fix respawn issues --- 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 b18c2db63..d44f8e24a 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -8,7 +8,7 @@ websocket = "1.5.1" protocol = "2.9.15-20221129.204554-2" raknet = "1.6.28-20220125.214016-6" mcauthlib = "d9d773e" -mcprotocollib = "1.19.3-20221206.215111-2" +mcprotocollib = "1.19.3-20221208.034912-3" packetlib = "3.0" adventure = "4.12.0-20220629.025215-9" adventure-platform = "4.1.2" From 247edc6665ce954220288fbcfcddfa9b5a6b88b6 Mon Sep 17 00:00:00 2001 From: Konicai <71294714+Konicai@users.noreply.github.com> Date: Thu, 8 Dec 2022 21:31:45 -0500 Subject: [PATCH 43/81] Don't say that 1.19.2 is supported (#3443) --- .../src/main/java/org/geysermc/geyser/network/GameProtocol.java | 2 +- 1 file changed, 1 insertion(+), 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 e85dc689d..2e080c4dd 100644 --- a/core/src/main/java/org/geysermc/geyser/network/GameProtocol.java +++ b/core/src/main/java/org/geysermc/geyser/network/GameProtocol.java @@ -110,7 +110,7 @@ public final class GameProtocol { * @return the supported Minecraft: Java Edition version names */ public static List getJavaVersions() { - return List.of(DEFAULT_JAVA_CODEC.getMinecraftVersion(), "1.19.2"); + return List.of(DEFAULT_JAVA_CODEC.getMinecraftVersion()); } /** From 6876a90c3b7337f9d340a2c588e1f04be4d53925 Mon Sep 17 00:00:00 2001 From: Camotoy <20743703+Camotoy@users.noreply.github.com> Date: Fri, 9 Dec 2022 13:39:24 -0500 Subject: [PATCH 44/81] Lower size of BiomeDefinitionsPacket --- .../main/java/org/geysermc/geyser/registry/Registries.java | 7 +++++-- 1 file changed, 5 insertions(+), 2 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 2c1c51baf..866cbd291 100644 --- a/core/src/main/java/org/geysermc/geyser/registry/Registries.java +++ b/core/src/main/java/org/geysermc/geyser/registry/Registries.java @@ -181,12 +181,15 @@ public final class Registries { POTION_MIXES = SimpleRegistry.create(PotionMixRegistryLoader::new); ENCHANTMENTS = SimpleMappedRegistry.create("mappings/enchantments.json", EnchantmentRegistryLoader::new); - // TEMPORARY FIX TO MAKE OLD BIOMES NBT WORK WITH 1.19.30 + // Remove unneeded client generation data from NbtMapBuilder NbtMapBuilder biomesNbt = NbtMap.builder(); for (Map.Entry entry : BIOMES_NBT.get().entrySet()) { String key = entry.getKey(); NbtMapBuilder value = ((NbtMap) entry.getValue()).toBuilder(); - value.put("name_hash", key); + value.remove("minecraft:consolidated_features"); + value.remove("minecraft:multinoise_generation_rules"); + value.remove("minecraft:surface_material_adjustments"); + value.remove( "minecraft:surface_parameters"); biomesNbt.put(key, value.build()); } BIOMES_NBT.set(biomesNbt.build()); From 2d63f09e1674f78868118f1b1c16451710448082 Mon Sep 17 00:00:00 2001 From: Konicai <71294714+Konicai@users.noreply.github.com> Date: Sun, 11 Dec 2022 00:01:36 -0500 Subject: [PATCH 45/81] Check if spawner contains entity type (#3450) --- .../entity/SpawnerBlockEntityTranslator.java | 21 +++++++++++-------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/core/src/main/java/org/geysermc/geyser/translator/level/block/entity/SpawnerBlockEntityTranslator.java b/core/src/main/java/org/geysermc/geyser/translator/level/block/entity/SpawnerBlockEntityTranslator.java index 3d11d5ced..2a4711e26 100644 --- a/core/src/main/java/org/geysermc/geyser/translator/level/block/entity/SpawnerBlockEntityTranslator.java +++ b/core/src/main/java/org/geysermc/geyser/translator/level/block/entity/SpawnerBlockEntityTranslator.java @@ -27,6 +27,7 @@ package org.geysermc.geyser.translator.level.block.entity; import com.github.steveice10.mc.protocol.data.game.level.block.BlockEntityType; import com.github.steveice10.opennbt.tag.builtin.CompoundTag; +import com.github.steveice10.opennbt.tag.builtin.StringTag; import com.github.steveice10.opennbt.tag.builtin.Tag; import com.nukkitx.nbt.NbtMapBuilder; import org.geysermc.geyser.entity.EntityDefinition; @@ -68,16 +69,18 @@ public class SpawnerBlockEntityTranslator extends BlockEntityTranslator { CompoundTag spawnData = tag.get("SpawnData"); if (spawnData != null) { - String entityID = (String) ((CompoundTag) spawnData.get("entity")) - .get("id") - .getValue(); - builder.put("EntityIdentifier", entityID); + StringTag idTag = ((CompoundTag) spawnData.get("entity")).get("id"); + if (idTag != null) { + // As of 1.19.3, spawners can be empty + String entityId = idTag.getValue(); + builder.put("EntityIdentifier", entityId); - EntityDefinition definition = Registries.JAVA_ENTITY_IDENTIFIERS.get(entityID); - if (definition != null) { - builder.put("DisplayEntityWidth", definition.width()); - builder.put("DisplayEntityHeight", definition.height()); - builder.put("DisplayEntityScale", 1.0f); + EntityDefinition definition = Registries.JAVA_ENTITY_IDENTIFIERS.get(entityId); + if (definition != null) { + builder.put("DisplayEntityWidth", definition.width()); + builder.put("DisplayEntityHeight", definition.height()); + builder.put("DisplayEntityScale", 1.0f); + } } } From b27b1c86bdbd6da510ca714a6b85d88c2f7c1704 Mon Sep 17 00:00:00 2001 From: Kas-tle <26531652+Kas-tle@users.noreply.github.com> Date: Sun, 11 Dec 2022 10:15:49 -0800 Subject: [PATCH 46/81] Makes bows, crossbows, tridents, projectiles, and lighters registered as custom items function properly (#3420) Co-authored-by: Camotoy <20743703+Camotoy@users.noreply.github.com> --- .../CustomItemRegistryPopulator.java | 56 +++++++++++++++++++ .../registry/type/GeyserMappingItem.java | 2 + core/src/main/resources/mappings | 2 +- 3 files changed, 59 insertions(+), 1 deletion(-) diff --git a/core/src/main/java/org/geysermc/geyser/registry/populator/CustomItemRegistryPopulator.java b/core/src/main/java/org/geysermc/geyser/registry/populator/CustomItemRegistryPopulator.java index 0e40f9c43..e32030db6 100644 --- a/core/src/main/java/org/geysermc/geyser/registry/populator/CustomItemRegistryPopulator.java +++ b/core/src/main/java/org/geysermc/geyser/registry/populator/CustomItemRegistryPopulator.java @@ -128,6 +128,29 @@ public class CustomItemRegistryPopulator { computeBlockItemProperties(mapping.getBedrockIdentifier(), componentBuilder); } + if (mapping.isEdible()) { + computeConsumableProperties(itemProperties, componentBuilder, 1, false); + } + + if (mapping.isEntityPlacer()) { + computeEntityPlacerProperties(componentBuilder); + } + + switch (mapping.getBedrockIdentifier()) { + case "minecraft:fire_charge", "minecraft:flint_and_steel" -> { + computeBlockItemProperties("minecraft:fire", componentBuilder); + } + case "minecraft:bow", "minecraft:crossbow", "minecraft:trident" -> { + computeChargeableProperties(itemProperties, componentBuilder); + } + case "minecraft:honey_bottle", "minecraft:milk_bucket", "minecraft:potion" -> { + computeConsumableProperties(itemProperties, componentBuilder, 2, true); + } + case "minecraft:experience_bottle", "minecraft:egg", "minecraft:ender_pearl", "minecraft:ender_eye", "minecraft:lingering_potion", "minecraft:snowball", "minecraft:splash_potion" -> { + computeThrowableProperties(componentBuilder); + } + } + computeRenderOffsets(false, customItemData, componentBuilder); componentBuilder.putCompound("item_properties", itemProperties.build()); @@ -273,6 +296,39 @@ public class CustomItemRegistryPopulator { componentBuilder.putCompound("minecraft:block_placer", NbtMap.builder().putString("block", blockItem).build()); } + private static void computeChargeableProperties(NbtMapBuilder itemProperties, NbtMapBuilder componentBuilder) { + // setting high use_duration prevents the consume animation from playing + itemProperties.putInt("use_duration", Integer.MAX_VALUE); + // display item as tool (mainly for crossbow and bow) + itemProperties.putBoolean("hand_equipped", true); + // ensure client moves at slow speed while charging (note: this was calculated by hand as the movement modifer value does not seem to scale linearly) + componentBuilder.putCompound("minecraft:chargeable", NbtMap.builder().putFloat("movement_modifier", 0.35F).build()); + } + + private static void computeConsumableProperties(NbtMapBuilder itemProperties, NbtMapBuilder componentBuilder, int useAnimation, boolean canAlwaysEat) { + // this is the duration of the use animation in ticks; note that in behavior packs this is set as a float in seconds, but over the network it is an int in ticks + itemProperties.putInt("use_duration", 32); + // this dictates that the item will use the eat or drink animation (in the first person) and play eat or drink sounds + // note that in behavior packs this is set as the string "eat" or "drink", but over the network it as an int, with these values being 1 and 2 respectively + itemProperties.putInt("use_animation", useAnimation); + // this component is required to allow the eat animation to play + componentBuilder.putCompound("minecraft:food", NbtMap.builder().putBoolean("can_always_eat", canAlwaysEat).build()); + } + + private static void computeEntityPlacerProperties(NbtMapBuilder componentBuilder) { + // all items registered that place entities should be given this component to prevent double placement + // it is okay that the entity here does not match the actual one since we control what entity actually spawns + componentBuilder.putCompound("minecraft:entity_placer", NbtMap.builder().putString("entity", "minecraft:minecart").build()); + } + + private static void computeThrowableProperties(NbtMapBuilder componentBuilder) { + // allows item to be thrown when holding down right click (individual presses are required w/o this component) + componentBuilder.putCompound("minecraft:throwable", NbtMap.builder().putBoolean("do_swing_animation", true).build()); + // this must be set to something for the swing animation to play + // it is okay that the projectile here does not match the actual one since we control what entity actually spawns + componentBuilder.putCompound("minecraft:projectile", NbtMap.builder().putString("projectile_entity", "minecraft:snowball").build()); + } + private static void computeRenderOffsets(boolean isHat, CustomItemData customItemData, NbtMapBuilder componentBuilder) { if (isHat) { componentBuilder.remove("minecraft:render_offsets"); diff --git a/core/src/main/java/org/geysermc/geyser/registry/type/GeyserMappingItem.java b/core/src/main/java/org/geysermc/geyser/registry/type/GeyserMappingItem.java index 6c65f1c34..480d1095d 100644 --- a/core/src/main/java/org/geysermc/geyser/registry/type/GeyserMappingItem.java +++ b/core/src/main/java/org/geysermc/geyser/registry/type/GeyserMappingItem.java @@ -48,4 +48,6 @@ public class GeyserMappingItem { @JsonProperty("repair_materials") List repairMaterials; @JsonProperty("has_suspicious_stew_effect") boolean hasSuspiciousStewEffect = false; @JsonProperty("dye_color") int dyeColor = -1; + @JsonProperty("is_edible") boolean edible = false; + @JsonProperty("is_entity_placer") boolean entityPlacer = false; } diff --git a/core/src/main/resources/mappings b/core/src/main/resources/mappings index e8703ccb1..f9d62b3f7 160000 --- a/core/src/main/resources/mappings +++ b/core/src/main/resources/mappings @@ -1 +1 @@ -Subproject commit e8703ccb187f98cd845357395d7b4ecfafbcd864 +Subproject commit f9d62b3f73db270bd4e0c833b7728b30d29e1369 From 7c26036906f870d739846b8d49f3990c435adc67 Mon Sep 17 00:00:00 2001 From: Camotoy <20743703+Camotoy@users.noreply.github.com> Date: Tue, 13 Dec 2022 13:53:28 -0500 Subject: [PATCH 47/81] Update adapters to support 1.19.3 and add biome command completions --- .../GeyserSpigotNativeWorldManager.java | 9 ++ .../geysermc/geyser/level/WorldManager.java | 9 ++ .../geyser/session/GeyserSession.java | 5 + .../protocol/java/JavaCommandsTranslator.java | 94 ++++++++++++++----- .../protocol/java/JavaLoginTranslator.java | 1 + gradle/libs.versions.toml | 2 +- 6 files changed, 98 insertions(+), 22 deletions(-) diff --git a/bootstrap/spigot/src/main/java/org/geysermc/geyser/platform/spigot/world/manager/GeyserSpigotNativeWorldManager.java b/bootstrap/spigot/src/main/java/org/geysermc/geyser/platform/spigot/world/manager/GeyserSpigotNativeWorldManager.java index bf9085979..6b5d1ea1e 100644 --- a/bootstrap/spigot/src/main/java/org/geysermc/geyser/platform/spigot/world/manager/GeyserSpigotNativeWorldManager.java +++ b/bootstrap/spigot/src/main/java/org/geysermc/geyser/platform/spigot/world/manager/GeyserSpigotNativeWorldManager.java @@ -32,6 +32,7 @@ import org.geysermc.geyser.adapters.spigot.SpigotAdapters; import org.geysermc.geyser.adapters.spigot.SpigotWorldAdapter; import org.geysermc.geyser.level.block.BlockStateValues; import org.geysermc.geyser.session.GeyserSession; +import org.jetbrains.annotations.Nullable; public class GeyserSpigotNativeWorldManager extends GeyserSpigotWorldManager { protected final SpigotWorldAdapter adapter; @@ -49,4 +50,12 @@ public class GeyserSpigotNativeWorldManager extends GeyserSpigotWorldManager { } return adapter.getBlockAt(player.getWorld(), x, y, z); } + + @Nullable + @Override + public String[] getBiomeIdentifiers(boolean withTags) { + // Biome identifiers will basically always be the same for one server, since you have to re-send the + // ClientboundLoginPacket to change the registry. Therefore, don't bother caching for each player. + return adapter.getBiomeSuggestions(withTags); + } } 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 b3a727d26..e10981f4b 100644 --- a/core/src/main/java/org/geysermc/geyser/level/WorldManager.java +++ b/core/src/main/java/org/geysermc/geyser/level/WorldManager.java @@ -31,6 +31,7 @@ import com.nukkitx.math.vector.Vector3i; import com.nukkitx.nbt.NbtMap; import org.geysermc.geyser.session.GeyserSession; +import javax.annotation.Nullable; import java.util.Locale; /** @@ -157,4 +158,12 @@ public abstract class WorldManager { * @return True if the player has the requested permission, false if not */ public abstract boolean hasPermission(GeyserSession session, String permission); + + /** + * Returns a list of biome identifiers available on the server. + */ + @Nullable + public String[] getBiomeIdentifiers(boolean withTags) { + return null; + } } 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 2e3ee4dde..8f9bc394a 100644 --- a/core/src/main/java/org/geysermc/geyser/session/GeyserSession.java +++ b/core/src/main/java/org/geysermc/geyser/session/GeyserSession.java @@ -296,6 +296,11 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource { */ @Setter private String worldName = null; + /** + * As of Java 1.19.3, the client only uses these for commands. + */ + @Setter + private String[] levels; private boolean sneaking; 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 14ff1a51a..11311b63c 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 @@ -44,6 +44,7 @@ import it.unimi.dsi.fastutil.objects.Object2ObjectOpenCustomHashMap; import lombok.Getter; import lombok.ToString; import net.kyori.adventure.text.format.NamedTextColor; +import org.checkerframework.checker.nullness.qual.MonotonicNonNull; import org.geysermc.geyser.GeyserImpl; import org.geysermc.geyser.api.event.downstream.ServerDefineCommandsEvent; import org.geysermc.geyser.command.GeyserCommandManager; @@ -198,7 +199,7 @@ public class JavaCommandsTranslator extends PacketTranslator= 1) { // Create the root param node and build all the children ParamInfo rootParam = new ParamInfo(commandNode, null); - rootParam.buildChildren(session, allNodes); + rootParam.buildChildren(new CommandBuilderContext(session), allNodes); List treeData = rootParam.getTree(); @@ -211,11 +212,11 @@ public class JavaCommandsTranslator extends PacketTranslator CommandParam.FILE_PATH; case BOOL -> ENUM_BOOLEAN; case OPERATION -> CommandParam.OPERATOR; // ">=", "==", etc - case BLOCK_STATE -> BlockRegistries.JAVA_TO_BEDROCK_IDENTIFIERS.get().keySet().toArray(new String[0]); - case ITEM_STACK -> session.getItemMappings().getItemNames(); + case BLOCK_STATE -> context.getBlockStates(); + case ITEM_STACK -> context.session.getItemMappings().getItemNames(); case COLOR -> VALID_COLORS; case SCOREBOARD_SLOT -> VALID_SCOREBOARD_SLOTS; - case RESOURCE, RESOURCE_OR_TAG -> { - String resource = ((ResourceProperties) node.getProperties()).getRegistryKey(); - yield switch (resource) { - // minecraft:worldgen/biome is also valid but we currently don't cache biome IDs - case "minecraft:attribute" -> ATTRIBUTES; - case "minecraft:enchantment" -> Enchantment.JavaEnchantment.ALL_JAVA_IDENTIFIERS; - case "minecraft:entity_type" -> Registries.JAVA_ENTITY_IDENTIFIERS.get().keySet().toArray(new String[0]); - case "minecraft:mob_effect" -> ALL_EFFECT_IDENTIFIERS; - default -> CommandParam.STRING; - }; - } + case RESOURCE -> handleResource(context, ((ResourceProperties) node.getProperties()).getRegistryKey(), false); + case RESOURCE_OR_TAG -> handleResource(context, ((ResourceProperties) node.getProperties()).getRegistryKey(), true); + case DIMENSION -> context.session.getLevels(); + default -> CommandParam.STRING; + }; + } + + private static Object handleResource(CommandBuilderContext context, String resource, boolean tags) { + return switch (resource) { + case "minecraft:attribute" -> ATTRIBUTES; + case "minecraft:enchantment" -> Enchantment.JavaEnchantment.ALL_JAVA_IDENTIFIERS; + case "minecraft:entity_type" -> context.getEntityTypes(); + case "minecraft:mob_effect" -> ALL_EFFECT_IDENTIFIERS; + case "minecraft:worldgen/biome" -> tags ? context.getBiomesWithTags() : context.getBiomes(); default -> CommandParam.STRING; }; } @@ -254,7 +258,55 @@ public class JavaCommandsTranslator extends PacketTranslator Date: Tue, 13 Dec 2022 13:54:40 -0500 Subject: [PATCH 48/81] Indicate 1.19.51 support --- README.md | 2 +- .../java/org/geysermc/geyser/network/GameProtocol.java | 8 ++++++-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 60cbd94f7..dc6e21b1a 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.50 and Minecraft Java 1.19.3. +### Currently supporting Minecraft Bedrock 1.19.20 - 1.19.51 and Minecraft Java 1.19.3. ## 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 2e080c4dd..6b46f8056 100644 --- a/core/src/main/java/org/geysermc/geyser/network/GameProtocol.java +++ b/core/src/main/java/org/geysermc/geyser/network/GameProtocol.java @@ -67,8 +67,12 @@ public final class GameProtocol { SUPPORTED_BEDROCK_CODECS.add(Bedrock_v554.V554_CODEC.toBuilder() .minecraftVersion("1.19.30/1.19.31") .build()); - SUPPORTED_BEDROCK_CODECS.add(Bedrock_v557.V557_CODEC); - SUPPORTED_BEDROCK_CODECS.add(DEFAULT_BEDROCK_CODEC); + 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() + .minecraftVersion("1.19.50/1.19.51") + .build()); } /** From f7375ed7dc9596a66b4413fc765c249e197ae889 Mon Sep 17 00:00:00 2001 From: Camotoy <20743703+Camotoy@users.noreply.github.com> Date: Tue, 13 Dec 2022 14:04:17 -0500 Subject: [PATCH 49/81] Fix chests not animating on open Fixes #3454 --- 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 286430220..55a25a99f 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -8,7 +8,7 @@ websocket = "1.5.1" protocol = "2.9.15-20221129.204554-2" raknet = "1.6.28-20220125.214016-6" mcauthlib = "d9d773e" -mcprotocollib = "1.19.3-20221208.034912-3" +mcprotocollib = "1.19.3-20221213.190132-4" packetlib = "3.0" adventure = "4.12.0-20220629.025215-9" adventure-platform = "4.1.2" From 298d59405a77eb9ecceb94bc9b698ac3fc0db944 Mon Sep 17 00:00:00 2001 From: Valaphee Date: Thu, 15 Dec 2022 11:56:52 +0100 Subject: [PATCH 50/81] Fix wrong render distance calculation --- .../main/java/org/geysermc/geyser/session/GeyserSession.java | 1 - .../java/entity/player/JavaPlayerPositionTranslator.java | 2 +- .../main/java/org/geysermc/geyser/util/DimensionUtils.java | 5 ++--- 3 files changed, 3 insertions(+), 5 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 8f9bc394a..7d911e22c 100644 --- a/core/src/main/java/org/geysermc/geyser/session/GeyserSession.java +++ b/core/src/main/java/org/geysermc/geyser/session/GeyserSession.java @@ -1378,7 +1378,6 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource { } public void setServerRenderDistance(int renderDistance) { - renderDistance = GenericMath.ceil(++renderDistance * MathUtils.SQRT_OF_TWO); //square to circle this.serverRenderDistance = renderDistance; ChunkRadiusUpdatedPacket chunkRadiusUpdatedPacket = new ChunkRadiusUpdatedPacket(); 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 2d2d7279f..0652792ef 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 @@ -85,7 +85,7 @@ public class JavaPlayerPositionTranslator extends PacketTranslator 47 && !session.isEmulatePost1_13Logic()) { + if (session.getServerRenderDistance() > 32 && !session.isEmulatePost1_13Logic()) { // See DimensionUtils for an explanation ChunkRadiusUpdatedPacket chunkRadiusUpdatedPacket = new ChunkRadiusUpdatedPacket(); chunkRadiusUpdatedPacket.setRadius(session.getServerRenderDistance()); diff --git a/core/src/main/java/org/geysermc/geyser/util/DimensionUtils.java b/core/src/main/java/org/geysermc/geyser/util/DimensionUtils.java index 5963da703..6b7296c12 100644 --- a/core/src/main/java/org/geysermc/geyser/util/DimensionUtils.java +++ b/core/src/main/java/org/geysermc/geyser/util/DimensionUtils.java @@ -75,18 +75,17 @@ public class DimensionUtils { session.getPistonCache().clear(); session.getSkullCache().clear(); - if (session.getServerRenderDistance() > 47 && !session.isEmulatePost1_13Logic()) { + if (session.getServerRenderDistance() > 32 && !session.isEmulatePost1_13Logic()) { // The server-sided view distance wasn't a thing until Minecraft Java 1.14 // So ViaVersion compensates by sending a "view distance" of 64 // That's fine, except when the actual view distance sent from the server is five chunks // The client locks up when switching dimensions, expecting more chunks than it's getting // To solve this, we cap at 32 unless we know that the render distance actually exceeds 32 - // 47 is the Bedrock equivalent of 32 // Also, as of 1.19: PS4 crashes with a ChunkRadiusUpdatedPacket too large session.getGeyser().getLogger().debug("Applying dimension switching workaround for Bedrock render distance of " + session.getServerRenderDistance()); ChunkRadiusUpdatedPacket chunkRadiusUpdatedPacket = new ChunkRadiusUpdatedPacket(); - chunkRadiusUpdatedPacket.setRadius(47); + chunkRadiusUpdatedPacket.setRadius(32); session.sendUpstreamPacket(chunkRadiusUpdatedPacket); // Will be re-adjusted on spawn } From 97bedd39e223ccc155884b9d71b78fab7b240f17 Mon Sep 17 00:00:00 2001 From: Camotoy <20743703+Camotoy@users.noreply.github.com> Date: Fri, 16 Dec 2022 14:17:12 -0500 Subject: [PATCH 51/81] Update MCProtocolLib to fix some commands packet decoding --- 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 55a25a99f..c463130b1 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -8,7 +8,7 @@ websocket = "1.5.1" protocol = "2.9.15-20221129.204554-2" raknet = "1.6.28-20220125.214016-6" mcauthlib = "d9d773e" -mcprotocollib = "1.19.3-20221213.190132-4" +mcprotocollib = "1.19.3-20221215.055419-5" packetlib = "3.0" adventure = "4.12.0-20220629.025215-9" adventure-platform = "4.1.2" From 486e2fca1e7afa4123c3fc51bfe455c17210de81 Mon Sep 17 00:00:00 2001 From: Camotoy <20743703+Camotoy@users.noreply.github.com> Date: Sat, 17 Dec 2022 12:38:49 -0500 Subject: [PATCH 52/81] Should clean up some crafting transactions a bit --- .../geyser/inventory/click/ClickPlan.java | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/core/src/main/java/org/geysermc/geyser/inventory/click/ClickPlan.java b/core/src/main/java/org/geysermc/geyser/inventory/click/ClickPlan.java index da72f9f99..bfe5a7d9d 100644 --- a/core/src/main/java/org/geysermc/geyser/inventory/click/ClickPlan.java +++ b/core/src/main/java/org/geysermc/geyser/inventory/click/ClickPlan.java @@ -30,10 +30,7 @@ import com.github.steveice10.mc.protocol.data.game.inventory.ContainerActionType import com.github.steveice10.mc.protocol.data.game.inventory.ContainerType; import com.github.steveice10.mc.protocol.data.game.inventory.MoveToHotbarAction; import com.github.steveice10.mc.protocol.packet.ingame.serverbound.inventory.ServerboundContainerClickPacket; -import it.unimi.dsi.fastutil.ints.Int2ObjectMap; -import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; -import it.unimi.dsi.fastutil.ints.IntOpenHashSet; -import it.unimi.dsi.fastutil.ints.IntSet; +import it.unimi.dsi.fastutil.ints.*; import org.geysermc.geyser.inventory.GeyserItemStack; import org.geysermc.geyser.inventory.Inventory; import org.geysermc.geyser.inventory.SlotType; @@ -124,12 +121,14 @@ public final class ClickPlan { } ItemStack clickedItemStack; - if (!planIter.hasNext() && refresh) { - clickedItemStack = InventoryUtils.REFRESH_ITEM; + if (emulatePost1_16Logic) { + // The action must be simulated first as Java expects the new contents of the cursor (as of 1.18.1) + clickedItemStack = simulatedCursor.getItemStack(); } else { - if (emulatePost1_16Logic) { - // The action must be simulated first as Java expects the new contents of the cursor (as of 1.18.1) - clickedItemStack = simulatedCursor.getItemStack(); + if (!planIter.hasNext() && refresh) { + // Doesn't have the intended effect with state IDs since this won't cause a complete window refresh + // (It will eventually once state IDs desync, but this causes more problems than not) + clickedItemStack = InventoryUtils.REFRESH_ITEM; } else { if (action.click.actionType == ContainerActionType.DROP_ITEM || action.slot == Click.OUTSIDE_SLOT) { clickedItemStack = null; From 47fd148b7e56fe1724f526eea18e31c0cd6874e8 Mon Sep 17 00:00:00 2001 From: Camotoy <20743703+Camotoy@users.noreply.github.com> Date: Sat, 17 Dec 2022 14:52:20 -0500 Subject: [PATCH 53/81] Refactor player add code for 1.19.3 Players are now not always added into the tab list. --- .../entity/type/player/PlayerEntity.java | 8 +- .../type/player/SessionPlayerEntity.java | 5 + .../entity/type/player/SkullPlayerEntity.java | 1 - .../geyser/session/cache/EntityCache.java | 3 +- .../geyser/skin/FakeHeadProvider.java | 49 +------- .../org/geysermc/geyser/skin/SkinManager.java | 85 +++++++------ .../JavaPlayerInfoRemoveTranslator.java | 10 +- .../JavaPlayerInfoUpdateTranslator.java | 119 ++++++++++-------- 8 files changed, 136 insertions(+), 144 deletions(-) diff --git a/core/src/main/java/org/geysermc/geyser/entity/type/player/PlayerEntity.java b/core/src/main/java/org/geysermc/geyser/entity/type/player/PlayerEntity.java index 8e600b1a8..3e3a298bd 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 @@ -77,7 +77,6 @@ public class PlayerEntity extends LivingEntity { } private String username; - private boolean playerList = true; // Player is in the player list /** * The textures property from the GameProfile. @@ -417,4 +416,11 @@ public class PlayerEntity extends LivingEntity { session.sendUpstreamPacket(packet); } } + + /** + * @return the UUID that should be used when dealing with Bedrock's tab list. + */ + public UUID getTabListUuid() { + return getUuid(); + } } diff --git a/core/src/main/java/org/geysermc/geyser/entity/type/player/SessionPlayerEntity.java b/core/src/main/java/org/geysermc/geyser/entity/type/player/SessionPlayerEntity.java index 74b95b73c..99517b208 100644 --- a/core/src/main/java/org/geysermc/geyser/entity/type/player/SessionPlayerEntity.java +++ b/core/src/main/java/org/geysermc/geyser/entity/type/player/SessionPlayerEntity.java @@ -250,4 +250,9 @@ public class SessionPlayerEntity extends PlayerEntity { dirtyMetadata.put(EntityData.PLAYER_HAS_DIED, (byte) 0); } } + + @Override + public UUID getTabListUuid() { + return session.getAuthData().uuid(); + } } diff --git a/core/src/main/java/org/geysermc/geyser/entity/type/player/SkullPlayerEntity.java b/core/src/main/java/org/geysermc/geyser/entity/type/player/SkullPlayerEntity.java index 176d171de..c2af2e36b 100644 --- a/core/src/main/java/org/geysermc/geyser/entity/type/player/SkullPlayerEntity.java +++ b/core/src/main/java/org/geysermc/geyser/entity/type/player/SkullPlayerEntity.java @@ -48,7 +48,6 @@ public class SkullPlayerEntity extends PlayerEntity { public SkullPlayerEntity(GeyserSession session, long geyserId) { super(session, 0, geyserId, UUID.randomUUID(), Vector3f.ZERO, Vector3f.ZERO, 0, 0, 0, "", null); - setPlayerList(false); } @Override diff --git a/core/src/main/java/org/geysermc/geyser/session/cache/EntityCache.java b/core/src/main/java/org/geysermc/geyser/session/cache/EntityCache.java index 012606615..9dc89215a 100644 --- a/core/src/main/java/org/geysermc/geyser/session/cache/EntityCache.java +++ b/core/src/main/java/org/geysermc/geyser/session/cache/EntityCache.java @@ -123,7 +123,8 @@ public class EntityCache { } public void addPlayerEntity(PlayerEntity entity) { - playerEntities.put(entity.getUuid(), entity); + // putIfAbsent matches the behavior of playerInfoMap in Java as of 1.19.3 + playerEntities.putIfAbsent(entity.getUuid(), entity); } public PlayerEntity getPlayerEntity(UUID uuid) { diff --git a/core/src/main/java/org/geysermc/geyser/skin/FakeHeadProvider.java b/core/src/main/java/org/geysermc/geyser/skin/FakeHeadProvider.java index 6794af498..464f53d48 100644 --- a/core/src/main/java/org/geysermc/geyser/skin/FakeHeadProvider.java +++ b/core/src/main/java/org/geysermc/geyser/skin/FakeHeadProvider.java @@ -29,10 +29,6 @@ import com.github.steveice10.opennbt.tag.builtin.CompoundTag; import com.google.common.cache.CacheBuilder; import com.google.common.cache.CacheLoader; import com.google.common.cache.LoadingCache; -import com.nukkitx.protocol.bedrock.data.skin.ImageData; -import com.nukkitx.protocol.bedrock.data.skin.SerializedSkin; -import com.nukkitx.protocol.bedrock.packet.PlayerListPacket; -import com.nukkitx.protocol.bedrock.packet.PlayerSkinPacket; import lombok.AllArgsConstructor; import lombok.Getter; import lombok.Setter; @@ -45,7 +41,6 @@ import org.geysermc.geyser.text.GeyserLocale; import javax.annotation.Nonnull; import java.awt.*; import java.awt.image.BufferedImage; -import java.util.Collections; import java.util.Objects; import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; @@ -111,7 +106,7 @@ public class FakeHeadProvider { try { SkinProvider.SkinData mergedSkinData = MERGED_SKINS_LOADING_CACHE.get(new FakeHeadEntry(texturesProperty, fakeHeadSkinUrl, entity)); - sendSkinPacket(session, entity, mergedSkinData); + SkinManager.sendSkinPacket(session, entity, mergedSkinData); } catch (ExecutionException e) { GeyserImpl.getInstance().getLogger().error("Couldn't merge skin of " + entity.getUsername() + " with head skin url " + fakeHeadSkinUrl, e); } @@ -133,50 +128,10 @@ public class FakeHeadProvider { return; } - sendSkinPacket(session, entity, skinData); + SkinManager.sendSkinPacket(session, entity, skinData); }); } - private static void sendSkinPacket(GeyserSession session, PlayerEntity entity, SkinProvider.SkinData skinData) { - SkinProvider.Skin skin = skinData.skin(); - SkinProvider.Cape cape = skinData.cape(); - SkinProvider.SkinGeometry geometry = skinData.geometry(); - - if (entity.getUuid().equals(session.getPlayerEntity().getUuid())) { - PlayerListPacket.Entry updatedEntry = SkinManager.buildEntryManually( - session, - entity.getUuid(), - entity.getUsername(), - entity.getGeyserId(), - skin.getTextureUrl(), - skin.getSkinData(), - cape.getCapeId(), - cape.getCapeData(), - geometry - ); - - PlayerListPacket playerAddPacket = new PlayerListPacket(); - playerAddPacket.setAction(PlayerListPacket.Action.ADD); - playerAddPacket.getEntries().add(updatedEntry); - session.sendUpstreamPacket(playerAddPacket); - } else { - PlayerSkinPacket packet = new PlayerSkinPacket(); - packet.setUuid(entity.getUuid()); - packet.setOldSkinName(""); - packet.setNewSkinName(skin.getTextureUrl()); - packet.setSkin(getSkin(skin.getTextureUrl(), skin, cape, geometry)); - packet.setTrustedSkin(true); - session.sendUpstreamPacket(packet); - } - } - - private static SerializedSkin getSkin(String skinId, SkinProvider.Skin skin, SkinProvider.Cape cape, SkinProvider.SkinGeometry geometry) { - return SerializedSkin.of(skinId, "", geometry.getGeometryName(), - ImageData.of(skin.getSkinData()), Collections.emptyList(), - ImageData.of(cape.getCapeData()), geometry.getGeometryData(), - "", true, false, false, cape.getCapeId(), skinId); - } - @AllArgsConstructor @Getter @Setter diff --git a/core/src/main/java/org/geysermc/geyser/skin/SkinManager.java b/core/src/main/java/org/geysermc/geyser/skin/SkinManager.java index 730d46908..85a1539bc 100644 --- a/core/src/main/java/org/geysermc/geyser/skin/SkinManager.java +++ b/core/src/main/java/org/geysermc/geyser/skin/SkinManager.java @@ -32,6 +32,7 @@ import com.github.steveice10.opennbt.tag.builtin.StringTag; import com.nukkitx.protocol.bedrock.data.skin.ImageData; import com.nukkitx.protocol.bedrock.data.skin.SerializedSkin; import com.nukkitx.protocol.bedrock.packet.PlayerListPacket; +import com.nukkitx.protocol.bedrock.packet.PlayerSkinPacket; import org.geysermc.geyser.GeyserImpl; import org.geysermc.geyser.api.network.AuthType; import org.geysermc.geyser.entity.type.player.PlayerEntity; @@ -67,10 +68,8 @@ public class SkinManager { playerEntity.getUuid(), playerEntity.getUsername(), playerEntity.getGeyserId(), - skin.getTextureUrl(), - skin.getSkinData(), - cape.getCapeId(), - cape.getCapeData(), + skin, + cape, geometry ); } @@ -79,14 +78,10 @@ public class SkinManager { * With all the information needed, build a Bedrock player entry with translated skin information. */ public static PlayerListPacket.Entry buildEntryManually(GeyserSession session, UUID uuid, String username, long geyserId, - String skinId, byte[] skinData, - String capeId, byte[] capeData, + SkinProvider.Skin skin, + SkinProvider.Cape cape, SkinProvider.SkinGeometry geometry) { - SerializedSkin serializedSkin = SerializedSkin.of( - skinId, "", geometry.getGeometryName(), ImageData.of(skinData), Collections.emptyList(), - ImageData.of(capeData), geometry.getGeometryData(), "", true, false, - !capeId.equals(SkinProvider.EMPTY_CAPE.getCapeId()), capeId, skinId - ); + SerializedSkin serializedSkin = getSkin(skin.getTextureUrl(), skin, cape, geometry); // This attempts to find the XUID of the player so profile images show up for Xbox accounts String xuid = ""; @@ -116,6 +111,45 @@ public class SkinManager { return entry; } + public static void sendSkinPacket(GeyserSession session, PlayerEntity entity, SkinProvider.SkinData skinData) { + SkinProvider.Skin skin = skinData.skin(); + SkinProvider.Cape cape = skinData.cape(); + SkinProvider.SkinGeometry geometry = skinData.geometry(); + + if (entity.getUuid().equals(session.getPlayerEntity().getUuid())) { + // TODO is this special behavior needed? + PlayerListPacket.Entry updatedEntry = buildEntryManually( + session, + entity.getUuid(), + entity.getUsername(), + entity.getGeyserId(), + skin, + cape, + geometry + ); + + PlayerListPacket playerAddPacket = new PlayerListPacket(); + playerAddPacket.setAction(PlayerListPacket.Action.ADD); + playerAddPacket.getEntries().add(updatedEntry); + session.sendUpstreamPacket(playerAddPacket); + } else { + PlayerSkinPacket packet = new PlayerSkinPacket(); + packet.setUuid(entity.getUuid()); + packet.setOldSkinName(""); + packet.setNewSkinName(skin.getTextureUrl()); + packet.setSkin(getSkin(skin.getTextureUrl(), skin, cape, geometry)); + packet.setTrustedSkin(true); + session.sendUpstreamPacket(packet); + } + } + + private static SerializedSkin getSkin(String skinId, SkinProvider.Skin skin, SkinProvider.Cape cape, SkinProvider.SkinGeometry geometry) { + return SerializedSkin.of(skinId, "", geometry.getGeometryName(), + ImageData.of(skin.getSkinData()), Collections.emptyList(), + ImageData.of(cape.getCapeData()), geometry.getGeometryData(), + "", true, false, false, cape.getCapeId(), skinId); + } + public static void requestAndHandleSkinAndCape(PlayerEntity entity, GeyserSession session, Consumer skinAndCapeConsumer) { SkinProvider.requestSkinData(entity).whenCompleteAsync((skinData, throwable) -> { @@ -128,34 +162,7 @@ public class SkinManager { } if (skinData.geometry() != null) { - SkinProvider.Skin skin = skinData.skin(); - SkinProvider.Cape cape = skinData.cape(); - SkinProvider.SkinGeometry geometry = skinData.geometry(); - - PlayerListPacket.Entry updatedEntry = buildEntryManually( - session, - entity.getUuid(), - entity.getUsername(), - entity.getGeyserId(), - skin.getTextureUrl(), - skin.getSkinData(), - cape.getCapeId(), - cape.getCapeData(), - geometry - ); - - - PlayerListPacket playerAddPacket = new PlayerListPacket(); - playerAddPacket.setAction(PlayerListPacket.Action.ADD); - playerAddPacket.getEntries().add(updatedEntry); - session.sendUpstreamPacket(playerAddPacket); - - if (!entity.isPlayerList()) { - PlayerListPacket playerRemovePacket = new PlayerListPacket(); - playerRemovePacket.setAction(PlayerListPacket.Action.REMOVE); - playerRemovePacket.getEntries().add(updatedEntry); - session.sendUpstreamPacket(playerRemovePacket); - } + sendSkinPacket(session, entity, skinData); } if (skinAndCapeConsumer != null) { diff --git a/core/src/main/java/org/geysermc/geyser/translator/protocol/java/entity/player/JavaPlayerInfoRemoveTranslator.java b/core/src/main/java/org/geysermc/geyser/translator/protocol/java/entity/player/JavaPlayerInfoRemoveTranslator.java index 4e9f0ca42..1aa9e33d9 100644 --- a/core/src/main/java/org/geysermc/geyser/translator/protocol/java/entity/player/JavaPlayerInfoRemoveTranslator.java +++ b/core/src/main/java/org/geysermc/geyser/translator/protocol/java/entity/player/JavaPlayerInfoRemoveTranslator.java @@ -45,17 +45,15 @@ public class JavaPlayerInfoRemoveTranslator extends PacketTranslator { @Override public void translate(GeyserSession session, ClientboundPlayerInfoUpdatePacket packet) { - if (!packet.getActions().contains(PlayerListEntryAction.ADD_PLAYER)) { - return; - } + Set actions = packet.getActions(); - PlayerListPacket translate = new PlayerListPacket(); - translate.setAction(PlayerListPacket.Action.ADD); + if (actions.contains(PlayerListEntryAction.ADD_PLAYER)) { + for (PlayerListEntry entry : packet.getEntries()) { + GameProfile profile = entry.getProfile(); + PlayerEntity playerEntity; + boolean self = profile.getId().equals(session.getPlayerEntity().getUuid()); - for (PlayerListEntry entry : packet.getEntries()) { - GameProfile profile = entry.getProfile(); - PlayerEntity playerEntity; - boolean self = profile.getId().equals(session.getPlayerEntity().getUuid()); + GameProfile.Property textures = profile.getProperty("textures"); + String texturesProperty = textures == null ? null : textures.getValue(); - if (self) { - // Entity is ourself - playerEntity = session.getPlayerEntity(); - } else { - playerEntity = session.getEntityCache().getPlayerEntity(profile.getId()); - } + if (self) { + // Entity is ourself + playerEntity = session.getPlayerEntity(); + } else { + // It's a new player + playerEntity = new PlayerEntity( + session, + -1, + session.getEntityCache().getNextEntityId().incrementAndGet(), + profile.getId(), + Vector3f.ZERO, + Vector3f.ZERO, + 0, 0, 0, + profile.getName(), + texturesProperty + ); - GameProfile.Property textures = profile.getProperty("textures"); - String texturesProperty = textures == null ? null : textures.getValue(); - - if (playerEntity == null) { - // It's a new player - playerEntity = new PlayerEntity( - session, - -1, - session.getEntityCache().getNextEntityId().incrementAndGet(), - profile.getId(), - Vector3f.ZERO, - Vector3f.ZERO, - 0, 0, 0, - profile.getName(), - texturesProperty - ); - - session.getEntityCache().addPlayerEntity(playerEntity); - } else { + session.getEntityCache().addPlayerEntity(playerEntity); + } playerEntity.setUsername(profile.getName()); playerEntity.setTexturesProperty(texturesProperty); - } - playerEntity.setPlayerList(true); - // We'll send our own PlayerListEntry in requestAndHandleSkinAndCape - // But we need to send other player's entries so they show up in the player list - // without processing their skin information - that'll be processed when they spawn in - if (self) { - SkinManager.requestAndHandleSkinAndCape(playerEntity, session, skinAndCape -> - GeyserImpl.getInstance().getLogger().debug("Loaded Local Bedrock Java Skin Data for " + session.getClientData().getUsername())); - } else { - playerEntity.setValid(true); - PlayerListPacket.Entry playerListEntry = SkinManager.buildCachedEntry(session, playerEntity); - - translate.getEntries().add(playerListEntry); + // We'll send our own PlayerListEntry in requestAndHandleSkinAndCape + // But we need to send other player's entries so they show up in the player list + // without processing their skin information - that'll be processed when they spawn in + if (self) { + SkinManager.requestAndHandleSkinAndCape(playerEntity, session, skinAndCape -> + GeyserImpl.getInstance().getLogger().debug("Loaded Local Bedrock Java Skin Data for " + session.getClientData().getUsername())); + } else { + playerEntity.setValid(true); + } } } - if (!translate.getEntries().isEmpty()) { - session.sendUpstreamPacket(translate); + if (actions.contains(PlayerListEntryAction.UPDATE_LISTED)) { + List toAdd = new ArrayList<>(); + List toRemove = new ArrayList<>(); + + for (PlayerListEntry entry : packet.getEntries()) { + PlayerEntity entity = session.getEntityCache().getPlayerEntity(entry.getProfileId()); + if (entity == null) { + session.getGeyser().getLogger().debug("Ignoring player info update for " + entry.getProfileId()); + continue; + } + + if (entry.isListed()) { + PlayerListPacket.Entry playerListEntry = SkinManager.buildCachedEntry(session, entity); + toAdd.add(playerListEntry); + } else { + toRemove.add(new PlayerListPacket.Entry(entity.getTabListUuid())); + } + } + + if (!toAdd.isEmpty()) { + PlayerListPacket tabListPacket = new PlayerListPacket(); + tabListPacket.setAction(PlayerListPacket.Action.ADD); + tabListPacket.getEntries().addAll(toAdd); + session.sendUpstreamPacket(tabListPacket); + } + if (!toRemove.isEmpty()) { + PlayerListPacket tabListPacket = new PlayerListPacket(); + tabListPacket.setAction(PlayerListPacket.Action.REMOVE); + tabListPacket.getEntries().addAll(toRemove); + session.sendUpstreamPacket(tabListPacket); + } } } } From ad4424d2b656ba6f4acf153e9a9f4f759cf55c7b Mon Sep 17 00:00:00 2001 From: Camotoy <20743703+Camotoy@users.noreply.github.com> Date: Sat, 17 Dec 2022 21:05:41 -0500 Subject: [PATCH 54/81] Use server data to pick block on banners --- .../world/GeyserFabricWorldManager.java | 125 +++++++++++++++++- .../manager/GeyserSpigotWorldManager.java | 51 ++++++- .../geysermc/geyser/level/WorldManager.java | 15 ++- .../geyser/session/GeyserSession.java | 11 ++ .../inventory/item/nbt/BannerTranslator.java | 24 ++-- .../BedrockBlockPickRequestTranslator.java | 43 +++++- .../BedrockEntityPickRequestTranslator.java | 6 +- 7 files changed, 254 insertions(+), 21 deletions(-) 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 eb4f61c67..b003a76ba 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 @@ -31,12 +31,13 @@ import com.nukkitx.nbt.NbtMapBuilder; import com.nukkitx.nbt.NbtType; import me.lucko.fabric.api.permissions.v0.Permissions; import net.minecraft.core.BlockPos; -import net.minecraft.nbt.ListTag; +import net.minecraft.nbt.*; import net.minecraft.server.MinecraftServer; import net.minecraft.server.level.ServerPlayer; import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.WritableBookItem; 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 org.geysermc.geyser.level.GeyserWorldManager; @@ -44,8 +45,10 @@ 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 { @@ -127,7 +130,127 @@ public class GeyserFabricWorldManager extends GeyserWorldManager { return Permissions.check(player, permission); } + @Nonnull + @Override + public CompletableFuture getPickItemNbt(GeyserSession session, int x, int y, int z, boolean addNbtData) { + CompletableFuture future = new CompletableFuture<>(); + server.execute(() -> { + ServerPlayer player = getPlayer(session); + if (player == null) { + future.complete(null); + return; + } + + BlockPos pos = new BlockPos(x, y, z); + // Don't create a new block entity if invalid + BlockEntity blockEntity = player.level.getChunkAt(pos).getBlockEntity(pos); + if (blockEntity instanceof BannerBlockEntity banner) { + // Potentially exposes other NBT data? But we need to get the NBT data for the banner patterns *and* + // the banner might have a custom name, both of which a Java client knows and caches + ItemStack itemStack = banner.getItem(); + var tag = OpenNbtTagVisitor.convert("", itemStack.getOrCreateTag()); + + future.complete(tag); + return; + } + future.complete(null); + }); + return future; + } + private ServerPlayer getPlayer(GeyserSession session) { return server.getPlayerList().getPlayer(session.getPlayerEntity().getUuid()); } + + // Future considerations: option to clone; would affect arrays + private static class OpenNbtTagVisitor implements TagVisitor { + private String currentKey; + private final com.github.steveice10.opennbt.tag.builtin.CompoundTag root; + private com.github.steveice10.opennbt.tag.builtin.Tag currentTag; + + OpenNbtTagVisitor(String key) { + root = new com.github.steveice10.opennbt.tag.builtin.CompoundTag(key); + } + + @Override + public void visitString(StringTag stringTag) { + currentTag = new com.github.steveice10.opennbt.tag.builtin.StringTag(currentKey, stringTag.getAsString()); + } + + @Override + public void visitByte(ByteTag byteTag) { + currentTag = new com.github.steveice10.opennbt.tag.builtin.ByteTag(currentKey, byteTag.getAsByte()); + } + + @Override + public void visitShort(ShortTag shortTag) { + currentTag = new com.github.steveice10.opennbt.tag.builtin.ShortTag(currentKey, shortTag.getAsShort()); + } + + @Override + public void visitInt(IntTag intTag) { + currentTag = new com.github.steveice10.opennbt.tag.builtin.IntTag(currentKey, intTag.getAsInt()); + } + + @Override + public void visitLong(LongTag longTag) { + currentTag = new com.github.steveice10.opennbt.tag.builtin.LongTag(currentKey, longTag.getAsLong()); + } + + @Override + public void visitFloat(FloatTag floatTag) { + currentTag = new com.github.steveice10.opennbt.tag.builtin.FloatTag(currentKey, floatTag.getAsFloat()); + } + + @Override + public void visitDouble(DoubleTag doubleTag) { + currentTag = new com.github.steveice10.opennbt.tag.builtin.DoubleTag(currentKey, doubleTag.getAsDouble()); + } + + @Override + public void visitByteArray(ByteArrayTag byteArrayTag) { + currentTag = new com.github.steveice10.opennbt.tag.builtin.ByteArrayTag(currentKey, byteArrayTag.getAsByteArray()); + } + + @Override + public void visitIntArray(IntArrayTag intArrayTag) { + currentTag = new com.github.steveice10.opennbt.tag.builtin.IntArrayTag(currentKey, intArrayTag.getAsIntArray()); + } + + @Override + public void visitLongArray(LongArrayTag longArrayTag) { + currentTag = new com.github.steveice10.opennbt.tag.builtin.LongArrayTag(currentKey, longArrayTag.getAsLongArray()); + } + + @Override + public void visitList(ListTag listTag) { + var newList = new com.github.steveice10.opennbt.tag.builtin.ListTag(currentKey); + for (Tag tag : listTag) { + currentKey = ""; + tag.accept(this); + newList.add(currentTag); + } + currentTag = newList; + } + + @Override + public void visitCompound(CompoundTag compoundTag) { + currentTag = convert(currentKey, compoundTag); + } + + private static com.github.steveice10.opennbt.tag.builtin.CompoundTag convert(String name, CompoundTag compoundTag) { + OpenNbtTagVisitor visitor = new OpenNbtTagVisitor(name); + for (String key : compoundTag.getAllKeys()) { + visitor.currentKey = key; + Tag tag = compoundTag.get(key); + tag.accept(visitor); + visitor.root.put(visitor.currentTag); + } + return visitor.root; + } + + @Override + public void visitEnd(EndTag endTag) { + } + } } 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 52f29dcfe..cca982cbb 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,14 +25,17 @@ package org.geysermc.geyser.platform.spigot.world.manager; +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.World; -import org.bukkit.block.Block; -import org.bukkit.block.Lectern; +import org.bukkit.block.*; +import org.bukkit.block.banner.Pattern; import org.bukkit.entity.Player; import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.meta.BookMeta; @@ -43,10 +46,14 @@ 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; /** * The base world manager to use when there is no supported NMS revision @@ -173,6 +180,46 @@ public class GeyserSpigotWorldManager extends WorldManager { return Bukkit.getPlayer(session.getPlayerEntity().getUsername()).hasPermission(permission); } + @Nonnull + @Override + public CompletableFuture<@Nullable CompoundTag> getPickItemNbt(GeyserSession session, int x, int y, int z, boolean addNbtData) { + CompletableFuture<@Nullable CompoundTag> future = new CompletableFuture<>(); + // 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); + }); + 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/core/src/main/java/org/geysermc/geyser/level/WorldManager.java b/core/src/main/java/org/geysermc/geyser/level/WorldManager.java index e10981f4b..1909915db 100644 --- a/core/src/main/java/org/geysermc/geyser/level/WorldManager.java +++ b/core/src/main/java/org/geysermc/geyser/level/WorldManager.java @@ -27,12 +27,15 @@ package org.geysermc.geyser.level; import com.github.steveice10.mc.protocol.data.game.entity.player.GameMode; 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.geyser.session.GeyserSession; +import org.jetbrains.annotations.Nullable; -import javax.annotation.Nullable; +import javax.annotation.Nonnull; import java.util.Locale; +import java.util.concurrent.CompletableFuture; /** * Class that manages or retrieves various information @@ -166,4 +169,14 @@ public abstract class WorldManager { public String[] getBiomeIdentifiers(boolean withTags) { return null; } + + /** + * Used for pick block, so we don't need to cache more data than necessary. + * + * @return expected NBT for this item. + */ + @Nonnull + public CompletableFuture<@Nullable CompoundTag> getPickItemNbt(GeyserSession session, int x, int y, int z, boolean addNbtData) { + return CompletableFuture.completedFuture(null); + } } 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 8f9bc394a..743cc83db 100644 --- a/core/src/main/java/org/geysermc/geyser/session/GeyserSession.java +++ b/core/src/main/java/org/geysermc/geyser/session/GeyserSession.java @@ -1073,6 +1073,17 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource { closed = true; } + /** + * Moves task to the session event loop if already not in it. Otherwise, the task is automatically ran. + */ + public void ensureInEventLoop(Runnable runnable) { + if (eventLoop.inEventLoop()) { + runnable.run(); + return; + } + executeInEventLoop(runnable); + } + /** * Executes a task and prints a stack trace if an error occurs. */ 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 95dd07f22..a69a2cfe9 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 @@ -56,17 +56,17 @@ public class BannerTranslator extends NbtItemStackTranslator { static { OMINOUS_BANNER_PATTERN = new ListTag("Patterns"); // Construct what an ominous banner is supposed to look like - OMINOUS_BANNER_PATTERN.add(getPatternTag("mr", 9)); - OMINOUS_BANNER_PATTERN.add(getPatternTag("bs", 8)); - OMINOUS_BANNER_PATTERN.add(getPatternTag("cs", 7)); - OMINOUS_BANNER_PATTERN.add(getPatternTag("bo", 8)); - OMINOUS_BANNER_PATTERN.add(getPatternTag("ms", 15)); - OMINOUS_BANNER_PATTERN.add(getPatternTag("hh", 8)); - OMINOUS_BANNER_PATTERN.add(getPatternTag("mc", 8)); - OMINOUS_BANNER_PATTERN.add(getPatternTag("bo", 15)); + OMINOUS_BANNER_PATTERN.add(getJavaPatternTag("mr", 9)); + OMINOUS_BANNER_PATTERN.add(getJavaPatternTag("bs", 8)); + OMINOUS_BANNER_PATTERN.add(getJavaPatternTag("cs", 7)); + OMINOUS_BANNER_PATTERN.add(getJavaPatternTag("bo", 8)); + OMINOUS_BANNER_PATTERN.add(getJavaPatternTag("ms", 15)); + OMINOUS_BANNER_PATTERN.add(getJavaPatternTag("hh", 8)); + OMINOUS_BANNER_PATTERN.add(getJavaPatternTag("mc", 8)); + OMINOUS_BANNER_PATTERN.add(getJavaPatternTag("bo", 15)); } - private static CompoundTag getPatternTag(String pattern, int color) { + public static CompoundTag getJavaPatternTag(String pattern, int color) { StringTag patternType = new StringTag("Pattern", pattern); IntTag colorTag = new IntTag("Color", color); CompoundTag tag = new CompoundTag(""); @@ -117,11 +117,7 @@ public class BannerTranslator extends NbtItemStackTranslator { * @return The Java edition format pattern nbt */ public static CompoundTag getJavaBannerPattern(NbtMap pattern) { - Map tags = new HashMap<>(); - tags.put("Color", new IntTag("Color", 15 - pattern.getInt("Color"))); - tags.put("Pattern", new StringTag("Pattern", pattern.getString("Pattern"))); - - return new CompoundTag("", tags); + return BannerTranslator.getJavaPatternTag(pattern.getString("Pattern"), 15 - pattern.getInt("Color")); } /** 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 8d7cbe22b..90316a8bd 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 @@ -25,12 +25,18 @@ package org.geysermc.geyser.translator.protocol.bedrock; +import com.github.steveice10.mc.protocol.data.game.entity.metadata.ItemStack; +import com.github.steveice10.opennbt.tag.builtin.CompoundTag; +import com.github.steveice10.opennbt.tag.builtin.ListTag; +import com.github.steveice10.opennbt.tag.builtin.StringTag; import com.nukkitx.math.vector.Vector3i; import com.nukkitx.protocol.bedrock.packet.BlockPickRequestPacket; import org.geysermc.geyser.entity.EntityDefinitions; import org.geysermc.geyser.entity.type.ItemFrameEntity; import org.geysermc.geyser.level.block.BlockStateValues; import org.geysermc.geyser.registry.BlockRegistries; +import org.geysermc.geyser.registry.type.BlockMapping; +import org.geysermc.geyser.registry.type.ItemMapping; import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.translator.protocol.PacketTranslator; import org.geysermc.geyser.translator.protocol.Translator; @@ -61,6 +67,41 @@ public class BedrockBlockPickRequestTranslator extends PacketTranslator { + 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); + } + // 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); + }); + }); + return; + } + + pickItem(session, blockMapping); + } + + private void pickItem(GeyserSession session, BlockMapping blockToPick) { + InventoryUtils.findOrCreateItem(session, blockToPick.getPickItem()); } } 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 a9ef65fb5..482d153bb 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 @@ -25,7 +25,6 @@ package org.geysermc.geyser.translator.protocol.bedrock; -import com.github.steveice10.mc.protocol.data.game.entity.player.GameMode; import com.nukkitx.protocol.bedrock.packet.EntityPickRequestPacket; import org.geysermc.geyser.entity.type.BoatEntity; import org.geysermc.geyser.entity.type.Entity; @@ -45,7 +44,10 @@ public class BedrockEntityPickRequestTranslator extends PacketTranslator Date: Sun, 18 Dec 2022 15:49:52 +0100 Subject: [PATCH 55/81] Bump MCProtocolLib and PacketLib --- gradle/libs.versions.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index c463130b1..9b595671e 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -8,8 +8,8 @@ websocket = "1.5.1" protocol = "2.9.15-20221129.204554-2" raknet = "1.6.28-20220125.214016-6" mcauthlib = "d9d773e" -mcprotocollib = "1.19.3-20221215.055419-5" -packetlib = "3.0" +mcprotocollib = "1.19.3-20221218.141127-8" +packetlib = "3.0.1" adventure = "4.12.0-20220629.025215-9" adventure-platform = "4.1.2" junit = "4.13.1" From 09f17f59ae91de2ee91c7658d8553c4ed53bb19e Mon Sep 17 00:00:00 2001 From: Camotoy <20743703+Camotoy@users.noreply.github.com> Date: Sun, 18 Dec 2022 13:18:06 -0500 Subject: [PATCH 56/81] Support new default Java skins from 1.19.3 --- .../java/org/geysermc/geyser/Constants.java | 1 - .../java/org/geysermc/geyser/GeyserImpl.java | 20 +- .../geyser/skin/FakeHeadProvider.java | 2 +- .../geysermc/geyser/skin/ProvidedSkin.java | 63 ---- .../geysermc/geyser/skin/ProvidedSkins.java | 128 +++++++ .../org/geysermc/geyser/skin/SkinManager.java | 71 ++-- .../geysermc/geyser/skin/SkinProvider.java | 279 +++++++++------ .../geyser/skin/SkullSkinManager.java | 6 +- .../geysermc/geyser/text/MinecraftLocale.java | 279 ++------------- .../JavaPlayerInfoUpdateTranslator.java | 4 - .../org/geysermc/geyser/util/AssetUtils.java | 329 ++++++++++++++++++ .../main/resources/bedrock/skin/skin_alex.png | Bin 2105 -> 0 bytes .../resources/bedrock/skin/skin_steve.png | Bin 2162 -> 0 bytes core/src/main/resources/config.yml | 2 +- 14 files changed, 704 insertions(+), 480 deletions(-) delete mode 100644 core/src/main/java/org/geysermc/geyser/skin/ProvidedSkin.java create mode 100644 core/src/main/java/org/geysermc/geyser/skin/ProvidedSkins.java create mode 100644 core/src/main/java/org/geysermc/geyser/util/AssetUtils.java delete mode 100644 core/src/main/resources/bedrock/skin/skin_alex.png delete mode 100644 core/src/main/resources/bedrock/skin/skin_steve.png diff --git a/core/src/main/java/org/geysermc/geyser/Constants.java b/core/src/main/java/org/geysermc/geyser/Constants.java index 6a53c37de..46a1cb2ec 100644 --- a/core/src/main/java/org/geysermc/geyser/Constants.java +++ b/core/src/main/java/org/geysermc/geyser/Constants.java @@ -30,7 +30,6 @@ import java.net.URISyntaxException; public final class Constants { public static final URI GLOBAL_API_WS_URI; - public static final String NTP_SERVER = "time.cloudflare.com"; public static final String NEWS_OVERVIEW_URL = "https://api.geysermc.org/v2/news/"; public static final String NEWS_PROJECT_NAME = "geyser"; diff --git a/core/src/main/java/org/geysermc/geyser/GeyserImpl.java b/core/src/main/java/org/geysermc/geyser/GeyserImpl.java index a10e54f90..91e8e4c52 100644 --- a/core/src/main/java/org/geysermc/geyser/GeyserImpl.java +++ b/core/src/main/java/org/geysermc/geyser/GeyserImpl.java @@ -79,6 +79,7 @@ import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.session.PendingMicrosoftAuthentication; import org.geysermc.geyser.session.SessionManager; import org.geysermc.geyser.skin.FloodgateSkinUploader; +import org.geysermc.geyser.skin.ProvidedSkins; import org.geysermc.geyser.skin.SkinProvider; import org.geysermc.geyser.text.GeyserLocale; import org.geysermc.geyser.text.MinecraftLocale; @@ -95,6 +96,7 @@ import java.net.UnknownHostException; import java.security.Key; import java.text.DecimalFormat; import java.util.*; +import java.util.concurrent.CompletableFuture; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; @@ -195,7 +197,23 @@ public class GeyserImpl implements GeyserApi { EntityDefinitions.init(); ItemTranslator.init(); MessageTranslator.init(); - MinecraftLocale.init(); + + // Download the latest asset list and cache it + AssetUtils.generateAssetCache().whenComplete((aVoid, ex) -> { + if (ex != null) { + return; + } + MinecraftLocale.ensureEN_US(); + String locale = GeyserLocale.getDefaultLocale(); + if (!"en_us".equals(locale)) { + // English will be loaded after assets are downloaded, if necessary + MinecraftLocale.downloadAndLoadLocale(locale); + } + + ProvidedSkins.init(); + + CompletableFuture.runAsync(AssetUtils::downloadAndRunClientJarTasks); + }); startInstance(); diff --git a/core/src/main/java/org/geysermc/geyser/skin/FakeHeadProvider.java b/core/src/main/java/org/geysermc/geyser/skin/FakeHeadProvider.java index 464f53d48..b312f9811 100644 --- a/core/src/main/java/org/geysermc/geyser/skin/FakeHeadProvider.java +++ b/core/src/main/java/org/geysermc/geyser/skin/FakeHeadProvider.java @@ -63,7 +63,7 @@ public class FakeHeadProvider { SkinProvider.Skin skin = skinData.skin(); SkinProvider.Cape cape = skinData.cape(); - SkinProvider.SkinGeometry geometry = skinData.geometry().getGeometryName().equals("{\"geometry\" :{\"default\" :\"geometry.humanoid.customSlim\"}}") + SkinProvider.SkinGeometry geometry = skinData.geometry().geometryName().equals("{\"geometry\" :{\"default\" :\"geometry.humanoid.customSlim\"}}") ? SkinProvider.WEARING_CUSTOM_SKULL_SLIM : SkinProvider.WEARING_CUSTOM_SKULL; SkinProvider.Skin headSkin = SkinProvider.getOrDefault( diff --git a/core/src/main/java/org/geysermc/geyser/skin/ProvidedSkin.java b/core/src/main/java/org/geysermc/geyser/skin/ProvidedSkin.java deleted file mode 100644 index bb638556d..000000000 --- a/core/src/main/java/org/geysermc/geyser/skin/ProvidedSkin.java +++ /dev/null @@ -1,63 +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.skin; - -import lombok.Getter; -import org.geysermc.geyser.GeyserImpl; - -import javax.imageio.ImageIO; -import java.awt.image.BufferedImage; -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.io.InputStream; - -public class ProvidedSkin { - @Getter private byte[] skin; - - public ProvidedSkin(String internalUrl) { - try { - BufferedImage image; - try (InputStream stream = GeyserImpl.getInstance().getBootstrap().getResource(internalUrl)) { - image = ImageIO.read(stream); - } - - ByteArrayOutputStream outputStream = new ByteArrayOutputStream(image.getWidth() * 4 + image.getHeight() * 4); - for (int y = 0; y < image.getHeight(); y++) { - for (int x = 0; x < image.getWidth(); x++) { - int rgba = image.getRGB(x, y); - outputStream.write((rgba >> 16) & 0xFF); // Red - outputStream.write((rgba >> 8) & 0xFF); // Green - outputStream.write(rgba & 0xFF); // Blue - outputStream.write((rgba >> 24) & 0xFF); // Alpha - } - } - image.flush(); - skin = outputStream.toByteArray(); - } catch (IOException e) { - e.printStackTrace(); - } - } -} diff --git a/core/src/main/java/org/geysermc/geyser/skin/ProvidedSkins.java b/core/src/main/java/org/geysermc/geyser/skin/ProvidedSkins.java new file mode 100644 index 000000000..999df0929 --- /dev/null +++ b/core/src/main/java/org/geysermc/geyser/skin/ProvidedSkins.java @@ -0,0 +1,128 @@ +/* + * 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.skin; + +import org.geysermc.geyser.GeyserImpl; +import org.geysermc.geyser.util.AssetUtils; + +import javax.imageio.ImageIO; +import java.awt.image.BufferedImage; +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.nio.file.Path; +import java.util.Objects; +import java.util.UUID; + +public final class ProvidedSkins { + private static final ProvidedSkin[] PROVIDED_SKINS = { + new ProvidedSkin("textures/entity/player/slim/alex.png", true), + new ProvidedSkin("textures/entity/player/slim/ari.png", true), + new ProvidedSkin("textures/entity/player/slim/efe.png", true), + new ProvidedSkin("textures/entity/player/slim/kai.png", true), + new ProvidedSkin("textures/entity/player/slim/makena.png", true), + new ProvidedSkin("textures/entity/player/slim/noor.png", true), + new ProvidedSkin("textures/entity/player/slim/steve.png", true), + new ProvidedSkin("textures/entity/player/slim/sunny.png", true), + new ProvidedSkin("textures/entity/player/slim/zuri.png", true), + new ProvidedSkin("textures/entity/player/wide/alex.png", false), + new ProvidedSkin("textures/entity/player/wide/ari.png", false), + new ProvidedSkin("textures/entity/player/wide/efe.png", false), + new ProvidedSkin("textures/entity/player/wide/kai.png", false), + new ProvidedSkin("textures/entity/player/wide/makena.png", false), + new ProvidedSkin("textures/entity/player/wide/noor.png", false), + new ProvidedSkin("textures/entity/player/wide/steve.png", false), + new ProvidedSkin("textures/entity/player/wide/sunny.png", false), + new ProvidedSkin("textures/entity/player/wide/zuri.png", false) + }; + + public static ProvidedSkin getDefaultPlayerSkin(UUID uuid) { + return PROVIDED_SKINS[Math.floorMod(uuid.hashCode(), PROVIDED_SKINS.length)]; + } + + private ProvidedSkins() { + } + + public static final class ProvidedSkin { + private SkinProvider.Skin data; + private final boolean slim; + + ProvidedSkin(String asset, boolean slim) { + this.slim = slim; + + Path folder = GeyserImpl.getInstance().getBootstrap().getConfigFolder() + .resolve("cache") + .resolve("default_player_skins") + .resolve(slim ? "slim" : "wide"); + String assetName = asset.substring(asset.lastIndexOf('/') + 1); + + File location = folder.resolve(assetName).toFile(); + AssetUtils.addTask(!location.exists(), new AssetUtils.ClientJarTask("assets/minecraft/" + asset, + (stream) -> AssetUtils.saveFile(location, stream), + () -> { + try { + // TODO lazy initialize? + BufferedImage image; + try (InputStream stream = new FileInputStream(location)) { + image = ImageIO.read(stream); + } + + byte[] byteData = SkinProvider.bufferedImageToImageData(image); + image.flush(); + + String identifier = "geysermc:" + assetName + "_" + (slim ? "slim" : "wide"); + this.data = new SkinProvider.Skin(-1, identifier, byteData); + } catch (IOException e) { + e.printStackTrace(); + } + })); + } + + public SkinProvider.Skin getData() { + // Fall back to the default skin if we can't load our skins, or it's not loaded yet. + return Objects.requireNonNullElse(data, SkinProvider.EMPTY_SKIN); + } + + public boolean isSlim() { + return slim; + } + } + + public static void init() { + // no-op + } + + static { + Path folder = GeyserImpl.getInstance().getBootstrap().getConfigFolder() + .resolve("cache") + .resolve("default_player_skins"); + folder.toFile().mkdirs(); + // Two directories since there are two skins for each model: one slim, one wide + folder.resolve("slim").toFile().mkdir(); + folder.resolve("wide").toFile().mkdir(); + } +} diff --git a/core/src/main/java/org/geysermc/geyser/skin/SkinManager.java b/core/src/main/java/org/geysermc/geyser/skin/SkinManager.java index 85a1539bc..800b71c96 100644 --- a/core/src/main/java/org/geysermc/geyser/skin/SkinManager.java +++ b/core/src/main/java/org/geysermc/geyser/skin/SkinManager.java @@ -34,7 +34,6 @@ import com.nukkitx.protocol.bedrock.data.skin.SerializedSkin; import com.nukkitx.protocol.bedrock.packet.PlayerListPacket; import com.nukkitx.protocol.bedrock.packet.PlayerSkinPacket; import org.geysermc.geyser.GeyserImpl; -import org.geysermc.geyser.api.network.AuthType; import org.geysermc.geyser.entity.type.player.PlayerEntity; import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.session.auth.BedrockClientData; @@ -54,13 +53,30 @@ public class SkinManager { * Builds a Bedrock player list entry from our existing, cached Bedrock skin information */ public static PlayerListPacket.Entry buildCachedEntry(GeyserSession session, PlayerEntity playerEntity) { + // First: see if we have the cached skin texture ID. GameProfileData data = GameProfileData.from(playerEntity); - SkinProvider.Cape cape = SkinProvider.getCachedCape(data.capeUrl()); - SkinProvider.SkinGeometry geometry = SkinProvider.SkinGeometry.getLegacy(data.isAlex()); + SkinProvider.Skin skin = null; + SkinProvider.Cape cape = null; + SkinProvider.SkinGeometry geometry = SkinProvider.SkinGeometry.WIDE; + if (data != null) { + // GameProfileData is not null = server provided us with textures data to work with. + skin = SkinProvider.getCachedSkin(data.skinUrl()); + cape = SkinProvider.getCachedCape(data.capeUrl()); + geometry = data.isAlex() ? SkinProvider.SkinGeometry.SLIM : SkinProvider.SkinGeometry.WIDE; + } - SkinProvider.Skin skin = SkinProvider.getCachedSkin(data.skinUrl()); - if (skin == null) { - skin = SkinProvider.EMPTY_SKIN; + if (skin == null || cape == null) { + // The server either didn't have a texture to send, or we didn't have the texture ID cached. + // Let's see if this player is a Bedrock player, and if so, let's pull their skin. + // Otherwise, grab the default player skin + SkinProvider.SkinData fallbackSkinData = SkinProvider.determineFallbackSkinData(playerEntity); + if (skin == null) { + skin = fallbackSkinData.skin(); + geometry = fallbackSkinData.geometry(); + } + if (cape == null) { + cape = fallbackSkinData.cape(); + } } return buildEntryManually( @@ -144,10 +160,10 @@ public class SkinManager { } private static SerializedSkin getSkin(String skinId, SkinProvider.Skin skin, SkinProvider.Cape cape, SkinProvider.SkinGeometry geometry) { - return SerializedSkin.of(skinId, "", geometry.getGeometryName(), + return SerializedSkin.of(skinId, "", geometry.geometryName(), ImageData.of(skin.getSkinData()), Collections.emptyList(), - ImageData.of(cape.getCapeData()), geometry.getGeometryData(), - "", true, false, false, cape.getCapeId(), skinId); + ImageData.of(cape.capeData()), geometry.geometryData(), + "", true, false, false, cape.capeId(), skinId); } public static void requestAndHandleSkinAndCape(PlayerEntity entity, GeyserSession session, @@ -193,7 +209,7 @@ public class SkinManager { } if (!clientData.getCapeId().equals("")) { - SkinProvider.storeBedrockCape(playerEntity.getUuid(), capeBytes); + SkinProvider.storeBedrockCape(clientData.getCapeId(), capeBytes); } } catch (Exception e) { throw new AssertionError("Failed to cache skin for bedrock user (" + playerEntity.getUsername() + "): ", e); @@ -238,26 +254,21 @@ public class SkinManager { * @param entity entity to build the GameProfileData from * @return The built GameProfileData */ - public static GameProfileData from(PlayerEntity entity) { + public static @Nullable GameProfileData from(PlayerEntity entity) { try { String texturesProperty = entity.getTexturesProperty(); if (texturesProperty == null) { // Likely offline mode - return loadBedrockOrOfflineSkin(entity); - } - GameProfileData data = loadFromJson(texturesProperty); - if (data != null) { - return data; - } else { - return loadBedrockOrOfflineSkin(entity); + return null; } + return loadFromJson(texturesProperty); } catch (IOException exception) { GeyserImpl.getInstance().getLogger().debug("Something went wrong while processing skin for " + entity.getUsername()); if (GeyserImpl.getInstance().getConfig().isDebugMode()) { exception.printStackTrace(); } - return loadBedrockOrOfflineSkin(entity); + return null; } } @@ -286,27 +297,5 @@ public class SkinManager { return new GameProfileData(skinUrl, capeUrl, isAlex); } - - /** - * @return default skin with default cape when texture data is invalid, or the Bedrock player's skin if this - * is a Bedrock player. - */ - private static GameProfileData loadBedrockOrOfflineSkin(PlayerEntity entity) { - // Fallback to the offline mode of working it out - UUID uuid = entity.getUuid(); - boolean isAlex = (Math.abs(uuid.hashCode() % 2) == 1); - - String skinUrl = isAlex ? SkinProvider.EMPTY_SKIN_ALEX.getTextureUrl() : SkinProvider.EMPTY_SKIN.getTextureUrl(); - String capeUrl = SkinProvider.EMPTY_CAPE.getTextureUrl(); - if (("steve".equals(skinUrl) || "alex".equals(skinUrl)) && GeyserImpl.getInstance().getConfig().getRemote().authType() != AuthType.ONLINE) { - GeyserSession session = GeyserImpl.getInstance().connectionByUuid(uuid); - - if (session != null) { - skinUrl = session.getClientData().getSkinId(); - capeUrl = session.getClientData().getCapeId(); - } - } - return new GameProfileData(skinUrl, capeUrl, isAlex); - } } } diff --git a/core/src/main/java/org/geysermc/geyser/skin/SkinProvider.java b/core/src/main/java/org/geysermc/geyser/skin/SkinProvider.java index 43cf30b47..61f24ac1e 100644 --- a/core/src/main/java/org/geysermc/geyser/skin/SkinProvider.java +++ b/core/src/main/java/org/geysermc/geyser/skin/SkinProvider.java @@ -26,22 +26,25 @@ package org.geysermc.geyser.skin; import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.ObjectMapper; import com.github.steveice10.opennbt.tag.builtin.CompoundTag; import com.github.steveice10.opennbt.tag.builtin.IntArrayTag; import com.github.steveice10.opennbt.tag.builtin.Tag; import com.google.common.cache.Cache; import com.google.common.cache.CacheBuilder; +import it.unimi.dsi.fastutil.bytes.ByteArrays; import lombok.AllArgsConstructor; import lombok.Getter; import lombok.NoArgsConstructor; import org.geysermc.geyser.GeyserImpl; +import org.geysermc.geyser.api.network.AuthType; import org.geysermc.geyser.entity.type.player.PlayerEntity; import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.text.GeyserLocale; import org.geysermc.geyser.util.FileUtils; import org.geysermc.geyser.util.WebUtils; +import javax.annotation.Nonnull; +import javax.annotation.Nullable; import javax.imageio.ImageIO; import java.awt.*; import java.awt.image.BufferedImage; @@ -57,28 +60,28 @@ import java.util.concurrent.*; import java.util.function.Predicate; public class SkinProvider { - public static final boolean ALLOW_THIRD_PARTY_CAPES = GeyserImpl.getInstance().getConfig().isAllowThirdPartyCapes(); + private static final boolean ALLOW_THIRD_PARTY_CAPES = GeyserImpl.getInstance().getConfig().isAllowThirdPartyCapes(); static final ExecutorService EXECUTOR_SERVICE = Executors.newFixedThreadPool(ALLOW_THIRD_PARTY_CAPES ? 21 : 14); - public static final byte[] STEVE_SKIN = new ProvidedSkin("bedrock/skin/skin_steve.png").getSkin(); - public static final Skin EMPTY_SKIN = new Skin(-1, "steve", STEVE_SKIN); - public static final byte[] ALEX_SKIN = new ProvidedSkin("bedrock/skin/skin_alex.png").getSkin(); - public static final Skin EMPTY_SKIN_ALEX = new Skin(-1, "alex", ALEX_SKIN); - private static final Map permanentSkins = new HashMap<>() {{ - put("steve", EMPTY_SKIN); - put("alex", EMPTY_SKIN_ALEX); - }}; - private static final Cache cachedSkins = CacheBuilder.newBuilder() + static final Skin EMPTY_SKIN; + static final Cape EMPTY_CAPE = new Cape("", "no-cape", ByteArrays.EMPTY_ARRAY, -1, true); + + private static final Cache CACHED_JAVA_CAPES = CacheBuilder.newBuilder() + .expireAfterAccess(1, TimeUnit.HOURS) + .build(); + private static final Cache CACHED_JAVA_SKINS = CacheBuilder.newBuilder() .expireAfterAccess(1, TimeUnit.HOURS) .build(); - private static final Map> requestedSkins = new ConcurrentHashMap<>(); - - public static final Cape EMPTY_CAPE = new Cape("", "no-cape", new byte[0], -1, true); - private static final Cache cachedCapes = CacheBuilder.newBuilder() + private static final Cache CACHED_BEDROCK_CAPES = CacheBuilder.newBuilder() .expireAfterAccess(1, TimeUnit.HOURS) .build(); + private static final Cache CACHED_BEDROCK_SKINS = CacheBuilder.newBuilder() + .expireAfterAccess(1, TimeUnit.HOURS) + .build(); + private static final Map> requestedCapes = new ConcurrentHashMap<>(); + private static final Map> requestedSkins = new ConcurrentHashMap<>(); private static final Map cachedGeometry = new ConcurrentHashMap<>(); @@ -86,18 +89,36 @@ public class SkinProvider { * Citizens NPCs use UUID version 2, while legitimate Minecraft players use version 4, and * offline mode players use version 3. */ - public static final Predicate IS_NPC = uuid -> uuid.version() == 2; + private static final Predicate IS_NPC = uuid -> uuid.version() == 2; - public static final boolean ALLOW_THIRD_PARTY_EARS = GeyserImpl.getInstance().getConfig().isAllowThirdPartyEars(); - public static final String EARS_GEOMETRY; - public static final String EARS_GEOMETRY_SLIM; - public static final SkinGeometry SKULL_GEOMETRY; - public static final SkinGeometry WEARING_CUSTOM_SKULL; - public static final SkinGeometry WEARING_CUSTOM_SKULL_SLIM; - - public static final ObjectMapper OBJECT_MAPPER = new ObjectMapper(); + private static final boolean ALLOW_THIRD_PARTY_EARS = GeyserImpl.getInstance().getConfig().isAllowThirdPartyEars(); + private static final String EARS_GEOMETRY; + private static final String EARS_GEOMETRY_SLIM; + static final SkinGeometry SKULL_GEOMETRY; + static final SkinGeometry WEARING_CUSTOM_SKULL; + static final SkinGeometry WEARING_CUSTOM_SKULL_SLIM; static { + // Generate the empty texture to use as an emergency fallback + final int pink = -524040; + final int black = -16777216; + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(64 * 4 + 64 * 4); + for (int y = 0; y < 64; y++) { + for (int x = 0; x < 64; x++) { + int rgba; + if (y > 32) { + rgba = x >= 32 ? pink : black; + } else { + rgba = x >= 32 ? black : pink; + } + outputStream.write((rgba >> 16) & 0xFF); // Red + outputStream.write((rgba >> 8) & 0xFF); // Green + outputStream.write(rgba & 0xFF); // Blue + outputStream.write((rgba >> 24) & 0xFF); // Alpha + } + } + EMPTY_SKIN = new Skin(-1, "geysermc:empty", outputStream.toByteArray()); + /* Load in the normal ears geometry */ EARS_GEOMETRY = new String(FileUtils.readAllBytes("bedrock/skin/geometry.humanoid.ears.json"), StandardCharsets.UTF_8); @@ -141,48 +162,104 @@ public class SkinProvider { } } - public static boolean hasCapeCached(String capeUrl) { - return cachedCapes.getIfPresent(capeUrl) != null; + /** + * Search our cached database for an already existing, translated skin of this Java URL. + */ + static Skin getCachedSkin(String skinUrl) { + return CACHED_JAVA_SKINS.getIfPresent(skinUrl); } - public static Skin getCachedSkin(String skinUrl) { - return permanentSkins.getOrDefault(skinUrl, cachedSkins.getIfPresent(skinUrl)); + /** + * If skin data fails to apply, or there is no skin data to apply, determine what skin we should give as a fallback. + */ + static SkinData determineFallbackSkinData(PlayerEntity entity) { + Skin skin = null; + Cape cape = null; + SkinGeometry geometry = SkinGeometry.WIDE; + + if (GeyserImpl.getInstance().getConfig().getRemote().authType() != AuthType.ONLINE) { + // Let's see if this player is a Bedrock player, and if so, let's pull their skin. + UUID uuid = entity.getUuid(); + GeyserSession session = GeyserImpl.getInstance().connectionByUuid(uuid); + if (session != null) { + String skinId = session.getClientData().getSkinId(); + skin = CACHED_BEDROCK_SKINS.getIfPresent(skinId); + String capeId = session.getClientData().getCapeId(); + cape = CACHED_BEDROCK_CAPES.getIfPresent(capeId); + geometry = cachedGeometry.getOrDefault(uuid, geometry); + } + } + + if (skin == null) { + // We don't have a skin for the player right now. Fall back to a default. + ProvidedSkins.ProvidedSkin providedSkin = ProvidedSkins.getDefaultPlayerSkin(entity.getUuid()); + skin = providedSkin.getData(); + geometry = providedSkin.isSlim() ? SkinProvider.SkinGeometry.SLIM : SkinProvider.SkinGeometry.WIDE; + } + + if (cape == null) { + cape = EMPTY_CAPE; + } + + return new SkinData(skin, cape, geometry); } - public static Cape getCachedCape(String capeUrl) { - Cape cape = capeUrl != null ? cachedCapes.getIfPresent(capeUrl) : EMPTY_CAPE; - return cape != null ? cape : EMPTY_CAPE; + /** + * Used as a fallback if an official Java cape doesn't exist for this user. + */ + @Nonnull + private static Cape getCachedBedrockCape(UUID uuid) { + GeyserSession session = GeyserImpl.getInstance().connectionByUuid(uuid); + if (session != null) { + String capeId = session.getClientData().getCapeId(); + Cape bedrockCape = CACHED_BEDROCK_CAPES.getIfPresent(capeId); + if (bedrockCape != null) { + return bedrockCape; + } + } + return EMPTY_CAPE; } - public static CompletableFuture requestSkinData(PlayerEntity entity) { + @Nullable + static Cape getCachedCape(String capeUrl) { + if (capeUrl == null) { + return null; + } + return CACHED_JAVA_CAPES.getIfPresent(capeUrl); + } + + static CompletableFuture requestSkinData(PlayerEntity entity) { SkinManager.GameProfileData data = SkinManager.GameProfileData.from(entity); + if (data == null) { + // This player likely does not have a textures property + return CompletableFuture.completedFuture(determineFallbackSkinData(entity)); + } return requestSkinAndCape(entity.getUuid(), data.skinUrl(), data.capeUrl()) .thenApplyAsync(skinAndCape -> { try { - Skin skin = skinAndCape.getSkin(); - Cape cape = skinAndCape.getCape(); - SkinGeometry geometry = SkinGeometry.getLegacy(data.isAlex()); + Skin skin = skinAndCape.skin(); + Cape cape = skinAndCape.cape(); + SkinGeometry geometry = data.isAlex() ? SkinGeometry.SLIM : SkinGeometry.WIDE; - if (cape.isFailed()) { - cape = getOrDefault(requestBedrockCape(entity.getUuid()), - EMPTY_CAPE, 3); + // Whether we should see if this player has a Bedrock skin we should check for on failure of + // any skin property + boolean checkForBedrock = entity.getUuid().version() != 4; + + if (cape.failed() && checkForBedrock) { + cape = getCachedBedrockCape(entity.getUuid()); } - if (cape.isFailed() && ALLOW_THIRD_PARTY_CAPES) { + if (cape.failed() && ALLOW_THIRD_PARTY_CAPES) { cape = getOrDefault(requestUnofficialCape( cape, entity.getUuid(), entity.getUsername(), false ), EMPTY_CAPE, CapeProvider.VALUES.length * 3); } - geometry = getOrDefault(requestBedrockGeometry( - geometry, entity.getUuid() - ), geometry, 3); - boolean isDeadmau5 = "deadmau5".equals(entity.getUsername()); // Not a bedrock player check for ears - if (geometry.isFailed() && (ALLOW_THIRD_PARTY_EARS || isDeadmau5)) { + if (geometry.failed() && (ALLOW_THIRD_PARTY_EARS || isDeadmau5)) { boolean isEars; // Its deadmau5, gotta support his skin :) @@ -213,26 +290,17 @@ public class SkinProvider { GeyserImpl.getInstance().getLogger().error(GeyserLocale.getLocaleStringLog("geyser.skin.fail", entity.getUuid()), e); } - return new SkinData(skinAndCape.getSkin(), skinAndCape.getCape(), null); + return new SkinData(skinAndCape.skin(), skinAndCape.cape(), null); }); } - public static CompletableFuture requestSkinAndCape(UUID playerId, String skinUrl, String capeUrl) { + private static CompletableFuture requestSkinAndCape(UUID playerId, String skinUrl, String capeUrl) { return CompletableFuture.supplyAsync(() -> { long time = System.currentTimeMillis(); - String newSkinUrl = skinUrl; - - if ("steve".equals(skinUrl) || "alex".equals(skinUrl)) { - GeyserSession session = GeyserImpl.getInstance().connectionByUuid(playerId); - - if (session != null) { - newSkinUrl = session.getClientData().getSkinId(); - } - } CapeProvider provider = capeUrl != null ? CapeProvider.MINECRAFT : null; SkinAndCape skinAndCape = new SkinAndCape( - getOrDefault(requestSkin(playerId, newSkinUrl, false), EMPTY_SKIN, 5), + getOrDefault(requestSkin(playerId, skinUrl, false), EMPTY_SKIN, 5), getOrDefault(requestCape(capeUrl, provider, false), EMPTY_CAPE, 5) ); @@ -241,7 +309,7 @@ public class SkinProvider { }, EXECUTOR_SERVICE); } - public static CompletableFuture requestSkin(UUID playerId, String textureUrl, boolean newThread) { + static CompletableFuture requestSkin(UUID playerId, String textureUrl, boolean newThread) { if (textureUrl == null || textureUrl.isEmpty()) return CompletableFuture.completedFuture(EMPTY_SKIN); CompletableFuture requestedSkin = requestedSkins.get(textureUrl); if (requestedSkin != null) { @@ -249,7 +317,7 @@ public class SkinProvider { return requestedSkin; } - Skin cachedSkin = getCachedSkin(textureUrl); + Skin cachedSkin = CACHED_JAVA_SKINS.getIfPresent(textureUrl); if (cachedSkin != null) { return CompletableFuture.completedFuture(cachedSkin); } @@ -259,23 +327,26 @@ public class SkinProvider { future = CompletableFuture.supplyAsync(() -> supplySkin(playerId, textureUrl), EXECUTOR_SERVICE) .whenCompleteAsync((skin, throwable) -> { skin.updated = true; - cachedSkins.put(textureUrl, skin); + CACHED_JAVA_SKINS.put(textureUrl, skin); requestedSkins.remove(textureUrl); }); requestedSkins.put(textureUrl, future); } else { Skin skin = supplySkin(playerId, textureUrl); future = CompletableFuture.completedFuture(skin); - cachedSkins.put(textureUrl, skin); + CACHED_JAVA_SKINS.put(textureUrl, skin); } return future; } - public static CompletableFuture requestCape(String capeUrl, CapeProvider provider, boolean newThread) { + private static CompletableFuture requestCape(String capeUrl, CapeProvider provider, boolean newThread) { if (capeUrl == null || capeUrl.isEmpty()) return CompletableFuture.completedFuture(EMPTY_CAPE); - if (requestedCapes.containsKey(capeUrl)) return requestedCapes.get(capeUrl); // already requested + CompletableFuture requestedCape = requestedCapes.get(capeUrl); + if (requestedCape != null) { + return requestedCape; + } - Cape cachedCape = cachedCapes.getIfPresent(capeUrl); + Cape cachedCape = CACHED_JAVA_CAPES.getIfPresent(capeUrl); if (cachedCape != null) { return CompletableFuture.completedFuture(cachedCape); } @@ -284,21 +355,21 @@ public class SkinProvider { if (newThread) { future = CompletableFuture.supplyAsync(() -> supplyCape(capeUrl, provider), EXECUTOR_SERVICE) .whenCompleteAsync((cape, throwable) -> { - cachedCapes.put(capeUrl, cape); + CACHED_JAVA_CAPES.put(capeUrl, cape); requestedCapes.remove(capeUrl); }); requestedCapes.put(capeUrl, future); } else { Cape cape = supplyCape(capeUrl, provider); // blocking future = CompletableFuture.completedFuture(cape); - cachedCapes.put(capeUrl, cape); + CACHED_JAVA_CAPES.put(capeUrl, cape); } return future; } - public static CompletableFuture requestUnofficialCape(Cape officialCape, UUID playerId, + private static CompletableFuture requestUnofficialCape(Cape officialCape, UUID playerId, String username, boolean newThread) { - if (officialCape.isFailed() && ALLOW_THIRD_PARTY_CAPES) { + if (officialCape.failed() && ALLOW_THIRD_PARTY_CAPES) { for (CapeProvider provider : CapeProvider.VALUES) { if (provider.type != CapeUrlType.USERNAME && IS_NPC.test(playerId)) { continue; @@ -308,7 +379,7 @@ public class SkinProvider { requestCape(provider.getUrlFor(playerId, username), provider, newThread), EMPTY_CAPE, 4 ); - if (!cape1.isFailed()) { + if (!cape1.failed()) { return CompletableFuture.completedFuture(cape1); } } @@ -316,7 +387,7 @@ public class SkinProvider { return CompletableFuture.completedFuture(officialCape); } - public static CompletableFuture requestEars(String earsUrl, boolean newThread, Skin skin) { + private static CompletableFuture requestEars(String earsUrl, boolean newThread, Skin skin) { if (earsUrl == null || earsUrl.isEmpty()) return CompletableFuture.completedFuture(skin); CompletableFuture future; @@ -339,7 +410,7 @@ public class SkinProvider { * @param newThread Should we start in a new thread * @return The updated skin with ears */ - public static CompletableFuture requestUnofficialEars(Skin officialSkin, UUID playerId, String username, boolean newThread) { + private static CompletableFuture requestUnofficialEars(Skin officialSkin, UUID playerId, String username, boolean newThread) { for (EarsProvider provider : EarsProvider.VALUES) { if (provider.type != CapeUrlType.USERNAME && IS_NPC.test(playerId)) { continue; @@ -357,30 +428,17 @@ public class SkinProvider { return CompletableFuture.completedFuture(officialSkin); } - public static CompletableFuture requestBedrockCape(UUID playerID) { - Cape bedrockCape = cachedCapes.getIfPresent(playerID.toString() + ".Bedrock"); - if (bedrockCape == null) { - bedrockCape = EMPTY_CAPE; - } - return CompletableFuture.completedFuture(bedrockCape); + static void storeBedrockSkin(UUID playerID, String skinId, byte[] skinData) { + Skin skin = new Skin(playerID, skinId, skinData, System.currentTimeMillis(), true, false); + CACHED_BEDROCK_SKINS.put(skin.getTextureUrl(), skin); } - public static CompletableFuture requestBedrockGeometry(SkinGeometry currentGeometry, UUID playerID) { - SkinGeometry bedrockGeometry = cachedGeometry.getOrDefault(playerID, currentGeometry); - return CompletableFuture.completedFuture(bedrockGeometry); + static void storeBedrockCape(String capeId, byte[] capeData) { + Cape cape = new Cape(capeId, capeId, capeData, System.currentTimeMillis(), false); + CACHED_BEDROCK_CAPES.put(capeId, cape); } - public static void storeBedrockSkin(UUID playerID, String skinID, byte[] skinData) { - Skin skin = new Skin(playerID, skinID, skinData, System.currentTimeMillis(), true, false); - cachedSkins.put(skin.getTextureUrl(), skin); - } - - public static void storeBedrockCape(UUID playerID, byte[] capeData) { - Cape cape = new Cape(playerID.toString() + ".Bedrock", playerID.toString(), capeData, System.currentTimeMillis(), false); - cachedCapes.put(playerID.toString() + ".Bedrock", cape); - } - - public static void storeBedrockGeometry(UUID playerID, byte[] geometryName, byte[] geometryData) { + static void storeBedrockGeometry(UUID playerID, byte[] geometryName, byte[] geometryData) { SkinGeometry geometry = new SkinGeometry(new String(geometryName), new String(geometryData), false); cachedGeometry.put(playerID, geometry); } @@ -391,7 +449,7 @@ public class SkinProvider { * @param skin The skin to cache */ public static void storeEarSkin(Skin skin) { - cachedSkins.put(skin.getTextureUrl(), skin); + CACHED_JAVA_SKINS.put(skin.getTextureUrl(), skin); } /** @@ -400,7 +458,7 @@ public class SkinProvider { * @param playerID The UUID to cache it against * @param isSlim If the player is using an slim base */ - public static void storeEarGeometry(UUID playerID, boolean isSlim) { + private static void storeEarGeometry(UUID playerID, boolean isSlim) { cachedGeometry.put(playerID, SkinGeometry.getEars(isSlim)); } @@ -414,7 +472,7 @@ public class SkinProvider { } private static Cape supplyCape(String capeUrl, CapeProvider provider) { - byte[] cape = EMPTY_CAPE.getCapeData(); + byte[] cape = EMPTY_CAPE.capeData(); try { cape = requestImage(capeUrl, provider); } catch (Exception ignored) { @@ -604,7 +662,7 @@ public class SkinProvider { } private static BufferedImage readFiveZigCape(String url) throws IOException { - JsonNode element = OBJECT_MAPPER.readTree(WebUtils.getBody(url)); + JsonNode element = GeyserImpl.JSON_MAPPER.readTree(WebUtils.getBody(url)); if (element != null && element.isObject()) { JsonNode capeElement = element.get("d"); if (capeElement == null || capeElement.isNull()) return null; @@ -683,13 +741,12 @@ public class SkinProvider { return defaultValue; } - @AllArgsConstructor - @Getter - public static class SkinAndCape { - private final Skin skin; - private final Cape cape; + public record SkinAndCape(Skin skin, Cape cape) { } + /** + * Represents a full package of skin, cape, and geometry. + */ public record SkinData(Skin skin, Cape cape, SkinGeometry geometry) { } @@ -703,29 +760,19 @@ public class SkinProvider { private boolean updated; private boolean ears; - private Skin(long requestedOn, String textureUrl, byte[] skinData) { + Skin(long requestedOn, String textureUrl, byte[] skinData) { this.requestedOn = requestedOn; this.textureUrl = textureUrl; this.skinData = skinData; } } - @AllArgsConstructor - @Getter - public static class Cape { - private final String textureUrl; - private final String capeId; - private final byte[] capeData; - private final long requestedOn; - private final boolean failed; + public record Cape(String textureUrl, String capeId, byte[] capeData, long requestedOn, boolean failed) { } - @AllArgsConstructor - @Getter - public static class SkinGeometry { - private final String geometryName; - private final String geometryData; - private final boolean failed; + public record SkinGeometry(String geometryName, String geometryData, boolean failed) { + public static SkinGeometry WIDE = getLegacy(false); + public static SkinGeometry SLIM = getLegacy(true); /** * Generate generic geometry @@ -733,7 +780,7 @@ public class SkinProvider { * @param isSlim Should it be the alex model * @return The generic geometry object */ - public static SkinGeometry getLegacy(boolean isSlim) { + private static SkinGeometry getLegacy(boolean isSlim) { return new SkinProvider.SkinGeometry("{\"geometry\" :{\"default\" :\"geometry.humanoid.custom" + (isSlim ? "Slim" : "") + "\"}}", "", true); } @@ -743,7 +790,7 @@ public class SkinProvider { * @param isSlim Should it be the alex model * @return The generated geometry for the ears model */ - public static SkinGeometry getEars(boolean isSlim) { + private static SkinGeometry getEars(boolean isSlim) { return new SkinProvider.SkinGeometry("{\"geometry\" :{\"default\" :\"geometry.humanoid.ears" + (isSlim ? "Slim" : "") + "\"}}", (isSlim ? EARS_GEOMETRY_SLIM : EARS_GEOMETRY), false); } } diff --git a/core/src/main/java/org/geysermc/geyser/skin/SkullSkinManager.java b/core/src/main/java/org/geysermc/geyser/skin/SkullSkinManager.java index 58054e9c5..2759b1408 100644 --- a/core/src/main/java/org/geysermc/geyser/skin/SkullSkinManager.java +++ b/core/src/main/java/org/geysermc/geyser/skin/SkullSkinManager.java @@ -42,9 +42,9 @@ public class SkullSkinManager extends SkinManager { // Prevents https://cdn.discordapp.com/attachments/613194828359925800/779458146191147008/unknown.png skinId = skinId + "_skull"; return SerializedSkin.of( - skinId, "", SkinProvider.SKULL_GEOMETRY.getGeometryName(), ImageData.of(skinData), Collections.emptyList(), - ImageData.of(SkinProvider.EMPTY_CAPE.getCapeData()), SkinProvider.SKULL_GEOMETRY.getGeometryData(), - "", true, false, false, SkinProvider.EMPTY_CAPE.getCapeId(), skinId + skinId, "", SkinProvider.SKULL_GEOMETRY.geometryName(), ImageData.of(skinData), Collections.emptyList(), + ImageData.of(SkinProvider.EMPTY_CAPE.capeData()), SkinProvider.SKULL_GEOMETRY.geometryData(), + "", true, false, false, SkinProvider.EMPTY_CAPE.capeId(), skinId ); } diff --git a/core/src/main/java/org/geysermc/geyser/text/MinecraftLocale.java b/core/src/main/java/org/geysermc/geyser/text/MinecraftLocale.java index 94ad5eead..9b0edd82f 100644 --- a/core/src/main/java/org/geysermc/geyser/text/MinecraftLocale.java +++ b/core/src/main/java/org/geysermc/geyser/text/MinecraftLocale.java @@ -25,91 +25,45 @@ package org.geysermc.geyser.text; -import com.fasterxml.jackson.annotation.JsonIgnoreProperties; -import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.databind.JsonNode; -import lombok.Getter; import org.geysermc.geyser.GeyserImpl; -import org.geysermc.geyser.network.GameProtocol; +import org.geysermc.geyser.util.AssetUtils; import org.geysermc.geyser.util.FileUtils; import org.geysermc.geyser.util.WebUtils; import java.io.*; import java.nio.file.Files; -import java.nio.file.Path; -import java.util.*; -import java.util.concurrent.CompletableFuture; -import java.util.zip.ZipFile; +import java.util.HashMap; +import java.util.Iterator; +import java.util.Locale; +import java.util.Map; public class MinecraftLocale { public static final Map> LOCALE_MAPPINGS = new HashMap<>(); - private static final Map ASSET_MAP = new HashMap<>(); - - private static VersionDownload clientJarInfo; - static { // Create the locales folder File localesFolder = GeyserImpl.getInstance().getBootstrap().getConfigFolder().resolve("locales").toFile(); //noinspection ResultOfMethodCallIgnored localesFolder.mkdir(); - // Download the latest asset list and cache it - generateAssetCache().whenComplete((aVoid, ex) -> downloadAndLoadLocale(GeyserLocale.getDefaultLocale())); + // FIXME TEMPORARY + try { + Files.delete(localesFolder.toPath().resolve("en_us.hash")); + } catch (IOException ignored) { + } } - /** - * Fetch the latest versions asset cache from Mojang so we can grab the locale files later - */ - private static CompletableFuture generateAssetCache() { - return CompletableFuture.supplyAsync(() -> { - try { - // Get the version manifest from Mojang - VersionManifest versionManifest = GeyserImpl.JSON_MAPPER.readValue(WebUtils.getBody("https://launchermeta.mojang.com/mc/game/version_manifest.json"), VersionManifest.class); - - // Get the url for the latest version of the games manifest - String latestInfoURL = ""; - for (Version version : versionManifest.getVersions()) { - if (version.getId().equals(GameProtocol.getJavaCodec().getMinecraftVersion())) { - latestInfoURL = version.getUrl(); - break; + public static void ensureEN_US() { + File localeFile = getFile("en_us"); + AssetUtils.addTask(!localeFile.exists(), new AssetUtils.ClientJarTask("assets/minecraft/lang/en_us.json", + (stream) -> AssetUtils.saveFile(localeFile, stream), + () -> { + if ("en_us".equals(GeyserLocale.getDefaultLocale())) { + loadLocale("en_us"); } - } - - // Make sure we definitely got a version - if (latestInfoURL.isEmpty()) { - throw new Exception(GeyserLocale.getLocaleStringLog("geyser.locale.fail.latest_version")); - } - - // Get the individual version manifest - VersionInfo versionInfo = GeyserImpl.JSON_MAPPER.readValue(WebUtils.getBody(latestInfoURL), VersionInfo.class); - - // Get the client jar for use when downloading the en_us locale - GeyserImpl.getInstance().getLogger().debug(GeyserImpl.JSON_MAPPER.writeValueAsString(versionInfo.getDownloads())); - clientJarInfo = versionInfo.getDownloads().get("client"); - GeyserImpl.getInstance().getLogger().debug(GeyserImpl.JSON_MAPPER.writeValueAsString(clientJarInfo)); - - // Get the assets list - JsonNode assets = GeyserImpl.JSON_MAPPER.readTree(WebUtils.getBody(versionInfo.getAssetIndex().getUrl())).get("objects"); - - // Put each asset into an array for use later - Iterator> assetIterator = assets.fields(); - while (assetIterator.hasNext()) { - Map.Entry entry = assetIterator.next(); - if (!entry.getKey().startsWith("minecraft/lang/")) { - // No need to cache non-language assets as we don't use them - continue; - } - - Asset asset = GeyserImpl.JSON_MAPPER.treeToValue(entry.getValue(), Asset.class); - ASSET_MAP.put(entry.getKey(), asset); - } - } catch (Exception e) { - GeyserImpl.getInstance().getLogger().error(GeyserLocale.getLocaleStringLog("geyser.locale.fail.asset_cache", (!e.getMessage().isEmpty() ? e.getMessage() : e.getStackTrace()))); - } - return null; - }); + })); } /** @@ -125,7 +79,7 @@ public class MinecraftLocale { } // Check the locale isn't already loaded - if (!ASSET_MAP.containsKey("minecraft/lang/" + locale + ".json") && !locale.equals("en_us")) { + if (!AssetUtils.isAssetKnown("minecraft/lang/" + locale + ".json") && !locale.equals("en_us")) { if (loadLocale(locale)) { GeyserImpl.getInstance().getLogger().debug("Loaded locale locally while not being in asset map: " + locale); } else { @@ -148,33 +102,15 @@ public class MinecraftLocale { * @param locale Locale to download */ private static void downloadLocale(String locale) { - File localeFile = GeyserImpl.getInstance().getBootstrap().getConfigFolder().resolve("locales/" + locale + ".json").toFile(); + if (locale.equals("en_us")) { + return; + } + File localeFile = getFile(locale); // Check if we have already downloaded the locale file if (localeFile.exists()) { - String curHash = ""; - String targetHash; - - if (locale.equals("en_us")) { - try { - File hashFile = GeyserImpl.getInstance().getBootstrap().getConfigFolder().resolve("locales/en_us.hash").toFile(); - if (hashFile.exists()) { - try (BufferedReader br = new BufferedReader(new FileReader(hashFile))) { - curHash = br.readLine().trim(); - } - } - } catch (IOException ignored) { } - - if (clientJarInfo == null) { - // Likely failed to download - GeyserImpl.getInstance().getLogger().debug("Skipping en_US hash check as client jar is null."); - return; - } - targetHash = clientJarInfo.getSha1(); - } else { - curHash = byteArrayToHexString(FileUtils.calculateSHA1(localeFile)); - targetHash = ASSET_MAP.get("minecraft/lang/" + locale + ".json").getHash(); - } + String curHash = byteArrayToHexString(FileUtils.calculateSHA1(localeFile)); + String targetHash = AssetUtils.getAsset("minecraft/lang/" + locale + ".json").getHash(); if (!curHash.equals(targetHash)) { GeyserImpl.getInstance().getLogger().debug("Locale out of date; re-downloading: " + locale); @@ -184,22 +120,19 @@ public class MinecraftLocale { } } - // Create the en_us locale - if (locale.equals("en_us")) { - downloadEN_US(localeFile); - - return; - } - try { // Get the hash and download the locale - String hash = ASSET_MAP.get("minecraft/lang/" + locale + ".json").getHash(); + String hash = AssetUtils.getAsset("minecraft/lang/" + locale + ".json").getHash(); WebUtils.downloadFile("https://resources.download.minecraft.net/" + hash.substring(0, 2) + "/" + hash, localeFile.toString()); } catch (Exception e) { GeyserImpl.getInstance().getLogger().error("Unable to download locale file hash", e); } } + private static File getFile(String locale) { + return GeyserImpl.getInstance().getBootstrap().getConfigFolder().resolve("locales/" + locale + ".json").toFile(); + } + /** * Loads a locale already downloaded, if the file doesn't exist it just logs a warning * @@ -254,51 +187,6 @@ public class MinecraftLocale { } } - /** - * Download then en_us locale by downloading the server jar and extracting it from there. - * - * @param localeFile File to save the locale to - */ - private static void downloadEN_US(File localeFile) { - try { - // Let the user know we are downloading the JAR - GeyserImpl.getInstance().getLogger().info(GeyserLocale.getLocaleStringLog("geyser.locale.download.en_us")); - GeyserImpl.getInstance().getLogger().debug("Download URL: " + clientJarInfo.getUrl()); - - // Download the smallest JAR (client or server) - Path tmpFilePath = GeyserImpl.getInstance().getBootstrap().getConfigFolder().resolve("tmp_locale.jar"); - WebUtils.downloadFile(clientJarInfo.getUrl(), tmpFilePath.toString()); - - // Load in the JAR as a zip and extract the file - try (ZipFile localeJar = new ZipFile(tmpFilePath.toString())) { - try (InputStream fileStream = localeJar.getInputStream(localeJar.getEntry("assets/minecraft/lang/en_us.json"))) { - try (FileOutputStream outStream = new FileOutputStream(localeFile)) { - - // Write the file to the locale dir - byte[] buf = new byte[fileStream.available()]; - int length; - while ((length = fileStream.read(buf)) != -1) { - outStream.write(buf, 0, length); - } - - // Flush all changes to disk and cleanup - outStream.flush(); - } - } - } - - // Store the latest jar hash - FileUtils.writeFile(GeyserImpl.getInstance().getBootstrap().getConfigFolder().resolve("locales/en_us.hash").toString(), clientJarInfo.getSha1().toCharArray()); - - // Delete the nolonger needed client/server jar - Files.delete(tmpFilePath); - - GeyserImpl.getInstance().getLogger().info(GeyserLocale.getLocaleStringLog("geyser.locale.download.en_us.done")); - } catch (Exception e) { - GeyserImpl.getInstance().getLogger().error(GeyserLocale.getLocaleStringLog("geyser.locale.fail.en_us"), e); - } - } - /** * Translate the given language string into the given locale, or falls back to the default locale * @@ -333,111 +221,4 @@ public class MinecraftLocale { } return result.toString(); } - - public static void init() { - // no-op - } - - @JsonIgnoreProperties(ignoreUnknown = true) - @Getter - static class VersionManifest { - @JsonProperty("latest") - private LatestVersion latestVersion; - - @JsonProperty("versions") - private List versions; - } - - @JsonIgnoreProperties(ignoreUnknown = true) - @Getter - static class LatestVersion { - @JsonProperty("release") - private String release; - - @JsonProperty("snapshot") - private String snapshot; - } - - @JsonIgnoreProperties(ignoreUnknown = true) - @Getter - static class Version { - @JsonProperty("id") - private String id; - - @JsonProperty("type") - private String type; - - @JsonProperty("url") - private String url; - - @JsonProperty("time") - private String time; - - @JsonProperty("releaseTime") - private String releaseTime; - } - - @JsonIgnoreProperties(ignoreUnknown = true) - @Getter - static class VersionInfo { - @JsonProperty("id") - private String id; - - @JsonProperty("type") - private String type; - - @JsonProperty("time") - private String time; - - @JsonProperty("releaseTime") - private String releaseTime; - - @JsonProperty("assetIndex") - private AssetIndex assetIndex; - - @JsonProperty("downloads") - private Map downloads; - } - - @JsonIgnoreProperties(ignoreUnknown = true) - @Getter - static class VersionDownload { - @JsonProperty("sha1") - private String sha1; - - @JsonProperty("size") - private int size; - - @JsonProperty("url") - private String url; - } - - @JsonIgnoreProperties(ignoreUnknown = true) - @Getter - static class AssetIndex { - @JsonProperty("id") - private String id; - - @JsonProperty("sha1") - private String sha1; - - @JsonProperty("size") - private int size; - - @JsonProperty("totalSize") - private int totalSize; - - @JsonProperty("url") - private String url; - } - - @JsonIgnoreProperties(ignoreUnknown = true) - @Getter - static class Asset { - @JsonProperty("hash") - private String hash; - - @JsonProperty("size") - private int size; - } } \ No newline at end of file diff --git a/core/src/main/java/org/geysermc/geyser/translator/protocol/java/entity/player/JavaPlayerInfoUpdateTranslator.java b/core/src/main/java/org/geysermc/geyser/translator/protocol/java/entity/player/JavaPlayerInfoUpdateTranslator.java index 4b7b3cdf8..2784b1cc4 100644 --- a/core/src/main/java/org/geysermc/geyser/translator/protocol/java/entity/player/JavaPlayerInfoUpdateTranslator.java +++ b/core/src/main/java/org/geysermc/geyser/translator/protocol/java/entity/player/JavaPlayerInfoUpdateTranslator.java @@ -79,10 +79,6 @@ public class JavaPlayerInfoUpdateTranslator extends PacketTranslator GeyserImpl.getInstance().getLogger().debug("Loaded Local Bedrock Java Skin Data for " + session.getClientData().getUsername())); diff --git a/core/src/main/java/org/geysermc/geyser/util/AssetUtils.java b/core/src/main/java/org/geysermc/geyser/util/AssetUtils.java new file mode 100644 index 000000000..299e63e0e --- /dev/null +++ b/core/src/main/java/org/geysermc/geyser/util/AssetUtils.java @@ -0,0 +1,329 @@ +/* + * 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.util; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.databind.JsonNode; +import lombok.Getter; +import org.geysermc.geyser.GeyserImpl; +import org.geysermc.geyser.network.GameProtocol; +import org.geysermc.geyser.text.GeyserLocale; + +import java.io.*; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.*; +import java.util.concurrent.CompletableFuture; +import java.util.zip.ZipFile; + +/** + * Implementation note: try to design processes to fail softly if the client jar can't be downloaded, + * either if Mojang is down or internet access to Mojang is spotty. + */ +public final class AssetUtils { + private static final String CLIENT_JAR_HASH_FILE = "client_jar.hash"; + + private static final Map ASSET_MAP = new HashMap<>(); + + private static VersionDownload CLIENT_JAR_INFO; + + private static final Queue CLIENT_JAR_TASKS = new ArrayDeque<>(); + /** + * Download the client jar even if the hash is correct + */ + private static boolean FORCE_DOWNLOAD_JAR = false; + + public static Asset getAsset(String name) { + return ASSET_MAP.get(name); + } + + public static boolean isAssetKnown(String name) { + return ASSET_MAP.containsKey(name); + } + + /** + * Add task to be ran after the client jar is downloaded or found to be cached. + * + * @param required if set to true, the client jar will always be downloaded, even if a pre-existing hash is matched. + * This means an asset or texture is missing. + */ + public static void addTask(boolean required, ClientJarTask task) { + CLIENT_JAR_TASKS.add(task); + FORCE_DOWNLOAD_JAR |= required; + } + + /** + * Fetch the latest versions asset cache from Mojang so we can grab the locale files later + */ + public static CompletableFuture generateAssetCache() { + return CompletableFuture.supplyAsync(() -> { + try { + // Get the version manifest from Mojang + VersionManifest versionManifest = GeyserImpl.JSON_MAPPER.readValue( + WebUtils.getBody("https://launchermeta.mojang.com/mc/game/version_manifest.json"), VersionManifest.class); + + // Get the url for the latest version of the games manifest + String latestInfoURL = ""; + for (Version version : versionManifest.getVersions()) { + if (version.getId().equals(GameProtocol.getJavaCodec().getMinecraftVersion())) { + latestInfoURL = version.getUrl(); + break; + } + } + + // Make sure we definitely got a version + if (latestInfoURL.isEmpty()) { + throw new Exception(GeyserLocale.getLocaleStringLog("geyser.locale.fail.latest_version")); + } + + // Get the individual version manifest + VersionInfo versionInfo = GeyserImpl.JSON_MAPPER.readValue(WebUtils.getBody(latestInfoURL), VersionInfo.class); + + // Get the client jar for use when downloading the en_us locale + GeyserImpl.getInstance().getLogger().debug(GeyserImpl.JSON_MAPPER.writeValueAsString(versionInfo.getDownloads())); + CLIENT_JAR_INFO = versionInfo.getDownloads().get("client"); + GeyserImpl.getInstance().getLogger().debug(GeyserImpl.JSON_MAPPER.writeValueAsString(CLIENT_JAR_INFO)); + + // Get the assets list + JsonNode assets = GeyserImpl.JSON_MAPPER.readTree(WebUtils.getBody(versionInfo.getAssetIndex().getUrl())).get("objects"); + + // Put each asset into an array for use later + Iterator> assetIterator = assets.fields(); + while (assetIterator.hasNext()) { + Map.Entry entry = assetIterator.next(); + if (!entry.getKey().startsWith("minecraft/lang/")) { + // No need to cache non-language assets as we don't use them + continue; + } + + Asset asset = GeyserImpl.JSON_MAPPER.treeToValue(entry.getValue(), Asset.class); + ASSET_MAP.put(entry.getKey(), asset); + } + + } catch (Exception e) { + GeyserImpl.getInstance().getLogger().error(GeyserLocale.getLocaleStringLog("geyser.locale.fail.asset_cache", (!e.getMessage().isEmpty() ? e.getMessage() : e.getStackTrace()))); + } + return null; + }); + } + + public static void downloadAndRunClientJarTasks() { + if (CLIENT_JAR_INFO == null) { + // Likely failed to download + GeyserImpl.getInstance().getLogger().debug("Skipping en_US hash check as client jar is null."); + return; + } + + if (!FORCE_DOWNLOAD_JAR) { // Don't bother checking the hash if we need to download new files anyway. + String curHash = null; + try { + File hashFile = GeyserImpl.getInstance().getBootstrap().getConfigFolder().resolve("cache").resolve(CLIENT_JAR_HASH_FILE).toFile(); + if (hashFile.exists()) { + try (BufferedReader br = new BufferedReader(new FileReader(hashFile))) { + curHash = br.readLine().trim(); + } + } + } catch (IOException ignored) { } + String targetHash = CLIENT_JAR_INFO.getSha1(); + if (targetHash.equals(curHash)) { + // Just run all tasks - no new download required + ClientJarTask task; + while ((task = CLIENT_JAR_TASKS.poll()) != null) { + task.whenDone.run(); + } + return; + } + } + + try { + // Let the user know we are downloading the JAR + GeyserImpl.getInstance().getLogger().info(GeyserLocale.getLocaleStringLog("geyser.locale.download.en_us")); + GeyserImpl.getInstance().getLogger().debug("Download URL: " + CLIENT_JAR_INFO.getUrl()); + + Path tmpFilePath = GeyserImpl.getInstance().getBootstrap().getConfigFolder().resolve("tmp_locale.jar"); + WebUtils.downloadFile(CLIENT_JAR_INFO.getUrl(), tmpFilePath.toString()); + + // Load in the JAR as a zip and extract the files + try (ZipFile localeJar = new ZipFile(tmpFilePath.toString())) { + ClientJarTask task; + while ((task = CLIENT_JAR_TASKS.poll()) != null) { + try (InputStream fileStream = localeJar.getInputStream(localeJar.getEntry(task.asset))) { + task.ifNewDownload.accept(fileStream); + task.whenDone.run(); + } + } + } + + // Store the latest jar hash + Path cache = GeyserImpl.getInstance().getBootstrap().getConfigFolder().resolve("cache"); + Files.createDirectories(cache); + FileUtils.writeFile(cache.resolve(CLIENT_JAR_HASH_FILE).toString(), CLIENT_JAR_INFO.getSha1().toCharArray()); + + // Delete the nolonger needed client/server jar + Files.delete(tmpFilePath); + + GeyserImpl.getInstance().getLogger().info(GeyserLocale.getLocaleStringLog("geyser.locale.download.en_us.done")); + } catch (Exception e) { + GeyserImpl.getInstance().getLogger().error(GeyserLocale.getLocaleStringLog("geyser.locale.fail.en_us"), e); + } + } + + public static void saveFile(File location, InputStream fileStream) throws IOException { + try (FileOutputStream outStream = new FileOutputStream(location)) { + + // Write the file to the locale dir + byte[] buf = new byte[fileStream.available()]; + int length; + while ((length = fileStream.read(buf)) != -1) { + outStream.write(buf, 0, length); + } + + // Flush all changes to disk and cleanup + outStream.flush(); + } + } + + /** + * A process that requires we download the client jar. + * Designed to accommodate Geyser updates that require more assets from the jar. + */ + public record ClientJarTask(String asset, InputStreamConsumer ifNewDownload, Runnable whenDone) { + } + + @FunctionalInterface + public interface InputStreamConsumer { + void accept(InputStream stream) throws IOException; + } + + /* Classes that map to JSON files served by Mojang */ + + @JsonIgnoreProperties(ignoreUnknown = true) + @Getter + static class VersionManifest { + @JsonProperty("latest") + private LatestVersion latestVersion; + + @JsonProperty("versions") + private List versions; + } + + @JsonIgnoreProperties(ignoreUnknown = true) + @Getter + static class LatestVersion { + @JsonProperty("release") + private String release; + + @JsonProperty("snapshot") + private String snapshot; + } + + @JsonIgnoreProperties(ignoreUnknown = true) + @Getter + static class Version { + @JsonProperty("id") + private String id; + + @JsonProperty("type") + private String type; + + @JsonProperty("url") + private String url; + + @JsonProperty("time") + private String time; + + @JsonProperty("releaseTime") + private String releaseTime; + } + + @JsonIgnoreProperties(ignoreUnknown = true) + @Getter + static class VersionInfo { + @JsonProperty("id") + private String id; + + @JsonProperty("type") + private String type; + + @JsonProperty("time") + private String time; + + @JsonProperty("releaseTime") + private String releaseTime; + + @JsonProperty("assetIndex") + private AssetIndex assetIndex; + + @JsonProperty("downloads") + private Map downloads; + } + + @JsonIgnoreProperties(ignoreUnknown = true) + @Getter + static class VersionDownload { + @JsonProperty("sha1") + private String sha1; + + @JsonProperty("size") + private int size; + + @JsonProperty("url") + private String url; + } + + @JsonIgnoreProperties(ignoreUnknown = true) + @Getter + static class AssetIndex { + @JsonProperty("id") + private String id; + + @JsonProperty("sha1") + private String sha1; + + @JsonProperty("size") + private int size; + + @JsonProperty("totalSize") + private int totalSize; + + @JsonProperty("url") + private String url; + } + + @JsonIgnoreProperties(ignoreUnknown = true) + @Getter + public static class Asset { + @JsonProperty("hash") + private String hash; + + @JsonProperty("size") + private int size; + } + + private AssetUtils() { + } +} diff --git a/core/src/main/resources/bedrock/skin/skin_alex.png b/core/src/main/resources/bedrock/skin/skin_alex.png deleted file mode 100644 index ffd8e0719a1a4fac1f58895ed3a30130d3aa4b46..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2105 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 diff --git a/core/src/main/resources/config.yml b/core/src/main/resources/config.yml index 502441560..ac5e76cd1 100644 --- a/core/src/main/resources/config.yml +++ b/core/src/main/resources/config.yml @@ -111,7 +111,7 @@ debug-mode: false # Allow third party capes to be visible. Currently allowing: # OptiFine capes, LabyMod capes, 5Zig capes and MinecraftCapes -allow-third-party-capes: true +allow-third-party-capes: false # Allow third party deadmau5 ears to be visible. Currently allowing: # MinecraftCapes From 3b5984117d7cbd897ed9be8a7d3d9cb3c3f2686a Mon Sep 17 00:00:00 2001 From: Camotoy <20743703+Camotoy@users.noreply.github.com> Date: Sun, 18 Dec 2022 22:34:16 -0500 Subject: [PATCH 57/81] Update aux value for polished granite Fixes #3462 --- core/src/main/resources/mappings | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/main/resources/mappings b/core/src/main/resources/mappings index f9d62b3f7..677c5b087 160000 --- a/core/src/main/resources/mappings +++ b/core/src/main/resources/mappings @@ -1 +1 @@ -Subproject commit f9d62b3f73db270bd4e0c833b7728b30d29e1369 +Subproject commit 677c5b0872d2f0c99ad834c0ca49a0ae3b45fde3 From 98069cff83c923d808b5fe8b54f0a2458a9a37e7 Mon Sep 17 00:00:00 2001 From: Camotoy <20743703+Camotoy@users.noreply.github.com> Date: Wed, 21 Dec 2022 00:35:03 -0500 Subject: [PATCH 58/81] Fix certain sounds not correctly playing Fixes #3463 --- .../org/geysermc/geyser/util/SoundUtils.java | 41 +++++++++++++------ 1 file changed, 29 insertions(+), 12 deletions(-) diff --git a/core/src/main/java/org/geysermc/geyser/util/SoundUtils.java b/core/src/main/java/org/geysermc/geyser/util/SoundUtils.java index b80a9afe7..ccfd658ce 100644 --- a/core/src/main/java/org/geysermc/geyser/util/SoundUtils.java +++ b/core/src/main/java/org/geysermc/geyser/util/SoundUtils.java @@ -25,6 +25,7 @@ package org.geysermc.geyser.util; +import com.github.steveice10.mc.protocol.data.game.level.sound.BuiltinSound; import com.github.steveice10.mc.protocol.data.game.level.sound.Sound; import com.nukkitx.math.vector.Vector3f; import com.nukkitx.protocol.bedrock.data.LevelEventType; @@ -65,10 +66,7 @@ public final class SoundUtils { * @return a Bedrock sound */ public static String translatePlaySound(String javaIdentifier) { - // Drop the Minecraft namespace if applicable - if (javaIdentifier.startsWith("minecraft:")) { - javaIdentifier = javaIdentifier.substring("minecraft:".length()); - } + javaIdentifier = trim(javaIdentifier); SoundMapping soundMapping = Registries.SOUNDS.get(javaIdentifier); if (soundMapping == null || soundMapping.getPlaysound() == null) { @@ -79,6 +77,23 @@ public final class SoundUtils { return soundMapping.getPlaysound(); } + private static String trim(String identifier) { + // Drop the Minecraft namespace if applicable + if (identifier.startsWith("minecraft:")) { + return identifier.substring("minecraft:".length()); + } + return identifier; + } + + private static void playSound(GeyserSession session, String bedrockName, Vector3f position, float volume, float pitch) { + PlaySoundPacket playSoundPacket = new PlaySoundPacket(); + playSoundPacket.setSound(bedrockName); + playSoundPacket.setPosition(position); + playSoundPacket.setVolume(volume); + playSoundPacket.setPitch(pitch); + session.sendUpstreamPacket(playSoundPacket); + } + /** * Translates and plays a Java Builtin Sound for a Bedrock client * @@ -88,22 +103,24 @@ public final class SoundUtils { * @param pitch the pitch */ public static void playSound(GeyserSession session, Sound javaSound, Vector3f position, float volume, float pitch) { - String packetSound = javaSound.getName(); + String packetSound; + if (!(javaSound instanceof BuiltinSound)) { + // Identifier needs trimmed probably. + packetSound = translatePlaySound(javaSound.getName()); + } else { + packetSound = javaSound.getName(); + } SoundMapping soundMapping = Registries.SOUNDS.get(packetSound); if (soundMapping == null) { - session.getGeyser().getLogger().debug("[Builtin] Sound mapping for " + packetSound + " not found"); + session.getGeyser().getLogger().debug("[Builtin] Sound mapping for " + packetSound + " not found; assuming custom."); + playSound(session, packetSound, position, volume, pitch); return; } if (soundMapping.getPlaysound() != null) { // We always prefer the PlaySound mapping because we can control volume and pitch - PlaySoundPacket playSoundPacket = new PlaySoundPacket(); - playSoundPacket.setSound(soundMapping.getPlaysound()); - playSoundPacket.setPosition(position); - playSoundPacket.setVolume(volume); - playSoundPacket.setPitch(pitch); - session.sendUpstreamPacket(playSoundPacket); + playSound(session, soundMapping.getPlaysound(), position, volume, pitch); return; } From fcd5fe1341ee1346d1576deac929e4483886c990 Mon Sep 17 00:00:00 2001 From: Camotoy <20743703+Camotoy@users.noreply.github.com> Date: Wed, 21 Dec 2022 00:37:48 -0500 Subject: [PATCH 59/81] Wrong method call on previous commit --- core/src/main/java/org/geysermc/geyser/util/SoundUtils.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/main/java/org/geysermc/geyser/util/SoundUtils.java b/core/src/main/java/org/geysermc/geyser/util/SoundUtils.java index ccfd658ce..fd694f53e 100644 --- a/core/src/main/java/org/geysermc/geyser/util/SoundUtils.java +++ b/core/src/main/java/org/geysermc/geyser/util/SoundUtils.java @@ -106,7 +106,7 @@ public final class SoundUtils { String packetSound; if (!(javaSound instanceof BuiltinSound)) { // Identifier needs trimmed probably. - packetSound = translatePlaySound(javaSound.getName()); + packetSound = trim(javaSound.getName()); } else { packetSound = javaSound.getName(); } From 03390b99e8240dc96a12414a016812b5e34de4dd Mon Sep 17 00:00:00 2001 From: Camotoy <20743703+Camotoy@users.noreply.github.com> Date: Wed, 21 Dec 2022 21:18:49 -0500 Subject: [PATCH 60/81] Fix black cats not appearing correctly --- .../entity/type/living/animal/tameable/CatEntity.java | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/tameable/CatEntity.java b/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/tameable/CatEntity.java index f700f6951..cca62f543 100644 --- a/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/tameable/CatEntity.java +++ b/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/tameable/CatEntity.java @@ -50,6 +50,13 @@ public class CatEntity extends TameableEntity { super(session, entityId, geyserId, uuid, definition, position, motion, yaw, pitch, headYaw); } + @Override + protected void initializeMetadata() { + super.initializeMetadata(); + // Default value (minecraft:black). + dirtyMetadata.put(EntityData.VARIANT, 1); + } + @Override public void updateRotation(float yaw, float pitch, boolean isOnGround) { moveRelative(0, 0, 0, yaw, pitch, yaw, isOnGround); From c48cb2a4a8779194e1a17bb6a556978ff4cea621 Mon Sep 17 00:00:00 2001 From: Camotoy <20743703+Camotoy@users.noreply.github.com> Date: Thu, 22 Dec 2022 13:19:46 -0500 Subject: [PATCH 61/81] Remount players in their vehicle if they're not supposed to leave --- .../geysermc/geyser/entity/type/Entity.java | 2 -- .../geyser/session/GeyserSession.java | 8 +++++-- .../player/BedrockInteractTranslator.java | 21 +++++++++++++++++++ .../player/JavaPlayerPositionTranslator.java | 7 +++++++ 4 files changed, 34 insertions(+), 4 deletions(-) diff --git a/core/src/main/java/org/geysermc/geyser/entity/type/Entity.java b/core/src/main/java/org/geysermc/geyser/entity/type/Entity.java index 663dd3c33..ed009bf8a 100644 --- a/core/src/main/java/org/geysermc/geyser/entity/type/Entity.java +++ b/core/src/main/java/org/geysermc/geyser/entity/type/Entity.java @@ -44,8 +44,6 @@ import lombok.Setter; import net.kyori.adventure.text.Component; import org.geysermc.geyser.entity.EntityDefinition; import org.geysermc.geyser.entity.GeyserDirtyMetadata; -import org.geysermc.geyser.entity.type.player.SessionPlayerEntity; -import org.geysermc.geyser.network.GameProtocol; import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.translator.text.MessageTranslator; import org.geysermc.geyser.util.EntityUtils; 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 d91c277a6..72697a85c 100644 --- a/core/src/main/java/org/geysermc/geyser/session/GeyserSession.java +++ b/core/src/main/java/org/geysermc/geyser/session/GeyserSession.java @@ -62,7 +62,6 @@ import com.github.steveice10.packetlib.event.session.*; import com.github.steveice10.packetlib.packet.Packet; import com.github.steveice10.packetlib.tcp.TcpClientSession; import com.github.steveice10.packetlib.tcp.TcpSession; -import com.nukkitx.math.GenericMath; import com.nukkitx.math.vector.*; import com.nukkitx.nbt.NbtMap; import com.nukkitx.protocol.bedrock.BedrockPacket; @@ -135,7 +134,6 @@ 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.geysermc.geyser.util.MathUtils; import java.net.ConnectException; import java.net.InetSocketAddress; @@ -539,6 +537,12 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource { @Setter private ScheduledFuture lookBackScheduledFuture = null; + /** + * Used to return players back to their vehicles if the server doesn't want them unmounting. + */ + @Setter + private ScheduledFuture mountVehicleScheduledFuture = null; + private MinecraftProtocol protocol; public GeyserSession(GeyserImpl geyser, BedrockServerSession bedrockServerSession, EventLoop eventLoop) { 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 fed8f5ce6..22a895465 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 @@ -32,15 +32,19 @@ import com.github.steveice10.mc.protocol.data.game.entity.type.EntityType; import com.github.steveice10.mc.protocol.packet.ingame.serverbound.player.ServerboundInteractPacket; import com.github.steveice10.mc.protocol.packet.ingame.serverbound.player.ServerboundPlayerCommandPacket; import com.nukkitx.protocol.bedrock.data.entity.EntityData; +import com.nukkitx.protocol.bedrock.data.entity.EntityLinkData; import com.nukkitx.protocol.bedrock.data.inventory.ContainerType; import com.nukkitx.protocol.bedrock.packet.ContainerOpenPacket; import com.nukkitx.protocol.bedrock.packet.InteractPacket; +import com.nukkitx.protocol.bedrock.packet.SetEntityLinkPacket; import org.geysermc.geyser.entity.type.Entity; import org.geysermc.geyser.entity.type.living.animal.horse.AbstractHorseEntity; import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.translator.protocol.PacketTranslator; import org.geysermc.geyser.translator.protocol.Translator; +import java.util.concurrent.TimeUnit; + @Translator(packet = InteractPacket.class) public class BedrockInteractTranslator extends PacketTranslator { @@ -73,6 +77,23 @@ public class BedrockInteractTranslator extends PacketTranslator case LEAVE_VEHICLE: ServerboundPlayerCommandPacket sneakPacket = new ServerboundPlayerCommandPacket(entity.getEntityId(), PlayerState.START_SNEAKING); session.sendDownstreamPacket(sneakPacket); + + Entity currentVehicle = session.getPlayerEntity().getVehicle(); + 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)); break; case MOUSEOVER: // Handle the buttons for mobile - "Mount", etc; and the suggestions for console - "ZL: Mount", etc 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 0652792ef..520443ae7 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 @@ -114,6 +114,13 @@ 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 From 6485200c1ffd47a69bd041290cb75668f4269394 Mon Sep 17 00:00:00 2001 From: David Choo <4722249+davchoo@users.noreply.github.com> Date: Fri, 23 Dec 2022 19:26:37 -0500 Subject: [PATCH 62/81] Fix visual glitch with blocks attached to extending pistons in 1.19.50 (#3475) --- .../level/block/entity/PistonBlockEntity.java | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) 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 28e30d6be..c52689014 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 @@ -42,6 +42,7 @@ import org.geysermc.geyser.level.physics.Axis; import org.geysermc.geyser.level.physics.BoundingBox; import org.geysermc.geyser.level.physics.CollisionManager; import org.geysermc.geyser.level.physics.Direction; +import org.geysermc.geyser.network.GameProtocol; import org.geysermc.geyser.registry.Registries; import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.session.cache.PistonCache; @@ -621,8 +622,10 @@ public class PistonBlockEntity { Vector3i movement = getMovement(); attachedBlocks.forEach((blockPos, javaId) -> { blockPos = blockPos.add(movement); - // Send a final block entity packet to detach blocks - BlockEntityUtils.updateBlockEntity(session, buildMovingBlockTag(blockPos, javaId, Direction.DOWN.getUnitVector()), blockPos); + if (!GameProtocol.supports1_19_50(session)) { + // Send a final block entity packet to detach blocks for clients older than 1.19.50 + BlockEntityUtils.updateBlockEntity(session, buildMovingBlockTag(blockPos, javaId, Direction.DOWN.getUnitVector()), blockPos); + } // Don't place blocks that collide with the player if (!SOLID_BOUNDING_BOX.checkIntersection(blockPos.toDouble(), session.getCollisionManager().getPlayerBoundingBox())) { ChunkUtils.updateBlock(session, javaId, blockPos); @@ -739,8 +742,8 @@ public class PistonBlockEntity { .putFloat("LastProgress", lastProgress) .putByte("NewState", getState()) .putByte("State", getState()) - .putByte("Sticky", (byte) (sticky ? 1 : 0)) - .putByte("isMovable", (byte) 0) + .putBoolean("Sticky", sticky) + .putBoolean("isMovable", false) .putInt("x", position.getX()) .putInt("y", position.getY()) .putInt("z", position.getZ()); @@ -762,8 +765,8 @@ public class PistonBlockEntity { .putFloat("LastProgress", extended ? 1.0f : 0.0f) .putByte("NewState", (byte) (extended ? 2 : 0)) .putByte("State", (byte) (extended ? 2 : 0)) - .putByte("Sticky", (byte) (sticky ? 1 : 0)) - .putByte("isMovable", (byte) 0) + .putBoolean("Sticky", sticky) + .putBoolean("isMovable", false) .putInt("x", position.getX()) .putInt("y", position.getY()) .putInt("z", position.getZ()); @@ -783,8 +786,9 @@ public class PistonBlockEntity { NbtMap movingBlock = session.getBlockMappings().getBedrockBlockStates().get(session.getBlockMappings().getBedrockBlockId(javaId)); NbtMapBuilder builder = NbtMap.builder() .putString("id", "MovingBlock") + .putBoolean("expanding", action == PistonValueType.PUSHING) .putCompound("movingBlock", movingBlock) - .putByte("isMovable", (byte) 1) + .putBoolean("isMovable", true) .putInt("pistonPosX", pistonPosition.getX()) .putInt("pistonPosY", pistonPosition.getY()) .putInt("pistonPosZ", pistonPosition.getZ()) From 566b2635c0f5556a9077d8d9a63dc6481c44a7d7 Mon Sep 17 00:00:00 2001 From: Camotoy <20743703+Camotoy@users.noreply.github.com> Date: Fri, 30 Dec 2022 16:24:16 -0500 Subject: [PATCH 63/81] Explain why Bedrock players can't chat with enforce-secure-profile --- .../java/org/geysermc/geyser/GeyserImpl.java | 7 ++- .../geyser/session/cache/WorldCache.java | 13 ++++++ .../bedrock/BedrockTextTranslator.java | 12 ++++- .../java/JavaServerDataTranslator.java | 44 +++++++++++++++++++ .../player/JavaPlayerPositionTranslator.java | 3 +- .../entity/spawn/JavaAddEntityTranslator.java | 3 +- core/src/main/resources/languages | 2 +- 7 files changed, 76 insertions(+), 8 deletions(-) create mode 100644 core/src/main/java/org/geysermc/geyser/translator/protocol/java/JavaServerDataTranslator.java diff --git a/core/src/main/java/org/geysermc/geyser/GeyserImpl.java b/core/src/main/java/org/geysermc/geyser/GeyserImpl.java index a10e54f90..982ce6fef 100644 --- a/core/src/main/java/org/geysermc/geyser/GeyserImpl.java +++ b/core/src/main/java/org/geysermc/geyser/GeyserImpl.java @@ -223,7 +223,10 @@ public class GeyserImpl implements GeyserApi { logger.info(message); if (platformType == PlatformType.STANDALONE) { - logger.warning(GeyserLocale.getLocaleStringLog("geyser.core.movement_warn")); + if (config.getRemote().authType() != AuthType.FLOODGATE) { + // If the auth-type is Floodgate, then this Geyser instance is probably owned by the Java server + logger.warning(GeyserLocale.getLocaleStringLog("geyser.core.movement_warn")); + } } else if (config.getRemote().authType() == AuthType.FLOODGATE) { VersionCheckUtils.checkForOutdatedFloodgate(logger); } @@ -329,7 +332,7 @@ public class GeyserImpl implements GeyserApi { Key key = new AesKeyProducer().produceFrom(config.getFloodgateKeyPath()); cipher = new AesCipher(new Base64Topping()); cipher.init(key); - logger.debug(GeyserLocale.getLocaleStringLog("geyser.auth.floodgate.loaded_key")); + logger.debug("Loaded Floodgate key!"); // Note: this is positioned after the bind so the skin uploader doesn't try to run if Geyser fails // to load successfully. Spigot complains about class loader if the plugin is disabled. skinUploader = new FloodgateSkinUploader(this).start(); diff --git a/core/src/main/java/org/geysermc/geyser/session/cache/WorldCache.java b/core/src/main/java/org/geysermc/geyser/session/cache/WorldCache.java index b3d0518b3..3eaabb399 100644 --- a/core/src/main/java/org/geysermc/geyser/session/cache/WorldCache.java +++ b/core/src/main/java/org/geysermc/geyser/session/cache/WorldCache.java @@ -33,6 +33,8 @@ import it.unimi.dsi.fastutil.objects.Object2IntMaps; import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap; import lombok.Getter; import lombok.Setter; +import org.checkerframework.checker.nullness.qual.NonNull; +import org.geysermc.geyser.api.util.TriState; import org.geysermc.geyser.scoreboard.Scoreboard; import org.geysermc.geyser.scoreboard.ScoreboardUpdater.ScoreboardSession; import org.geysermc.geyser.session.GeyserSession; @@ -61,6 +63,17 @@ public final class WorldCache { private int currentSequence; private final Object2IntMap unverifiedPredictions = new Object2IntOpenHashMap<>(1); + /** + *

    + *
  • NOT_SET = not yet triggered
  • + *
  • FALSE = enforce-secure-profile is true but player hasn't chatted yet
  • + *
  • TRUE = enforce-secure-profile is enabled, and player has chatted and they have seen our message.
  • + *
+ */ + @Getter + @Setter + private @NonNull TriState chatWarningSent = TriState.NOT_SET; + public WorldCache(GeyserSession session) { this.session = session; this.scoreboard = new Scoreboard(session); diff --git a/core/src/main/java/org/geysermc/geyser/translator/protocol/bedrock/BedrockTextTranslator.java b/core/src/main/java/org/geysermc/geyser/translator/protocol/bedrock/BedrockTextTranslator.java index 00eb95455..0dedc9b1b 100644 --- a/core/src/main/java/org/geysermc/geyser/translator/protocol/bedrock/BedrockTextTranslator.java +++ b/core/src/main/java/org/geysermc/geyser/translator/protocol/bedrock/BedrockTextTranslator.java @@ -26,8 +26,9 @@ package org.geysermc.geyser.translator.protocol.bedrock; import com.nukkitx.protocol.bedrock.packet.TextPacket; +import org.geysermc.geyser.api.util.TriState; import org.geysermc.geyser.session.GeyserSession; -import org.geysermc.geyser.text.ChatColor; +import org.geysermc.geyser.text.GeyserLocale; import org.geysermc.geyser.translator.protocol.PacketTranslator; import org.geysermc.geyser.translator.protocol.Translator; import org.geysermc.geyser.translator.text.MessageTranslator; @@ -48,6 +49,15 @@ public class BedrockTextTranslator extends PacketTranslator { return; } + if (session.getWorldCache().getChatWarningSent() == TriState.FALSE) { + if (Boolean.parseBoolean(System.getProperty("Geyser.PrintSecureChatInformation", "true"))) { + session.sendMessage(GeyserLocale.getPlayerLocaleString("geyser.chat.secure_info_1", session.locale())); + session.sendMessage(GeyserLocale.getPlayerLocaleString("geyser.chat.secure_info_2", session.locale(), "https://geysermc.link/secure-chat")); + } + // Never send this message again for this session. + session.getWorldCache().setChatWarningSent(TriState.TRUE); + } + session.sendChat(message); } } diff --git a/core/src/main/java/org/geysermc/geyser/translator/protocol/java/JavaServerDataTranslator.java b/core/src/main/java/org/geysermc/geyser/translator/protocol/java/JavaServerDataTranslator.java new file mode 100644 index 000000000..9484b99d5 --- /dev/null +++ b/core/src/main/java/org/geysermc/geyser/translator/protocol/java/JavaServerDataTranslator.java @@ -0,0 +1,44 @@ +/* + * 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.java; + +import com.github.steveice10.mc.protocol.packet.ingame.clientbound.ClientboundServerDataPacket; +import org.geysermc.geyser.api.util.TriState; +import org.geysermc.geyser.session.GeyserSession; +import org.geysermc.geyser.translator.protocol.PacketTranslator; +import org.geysermc.geyser.translator.protocol.Translator; + +@Translator(packet = ClientboundServerDataPacket.class) +public class JavaServerDataTranslator extends PacketTranslator { + + @Override + public void translate(GeyserSession session, ClientboundServerDataPacket packet) { + // We only want to warn about chat maybe not working once + if (packet.isEnforcesSecureChat() && session.getWorldCache().getChatWarningSent() == TriState.NOT_SET) { + session.getWorldCache().setChatWarningSent(TriState.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 2d2d7279f..a55e49f70 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 @@ -40,7 +40,6 @@ 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.text.GeyserLocale; import org.geysermc.geyser.translator.protocol.PacketTranslator; import org.geysermc.geyser.translator.protocol.Translator; import org.geysermc.geyser.util.ChunkUtils; @@ -97,7 +96,7 @@ public class JavaPlayerPositionTranslator extends PacketTranslator definition = Registries.ENTITY_DEFINITIONS.get(packet.getType()); if (definition == null) { - session.getGeyser().getLogger().warning(GeyserLocale.getLocaleStringLog("geyser.entity.type_null", packet.getType())); + session.getGeyser().getLogger().warning("Could not find an entity definition with type " + packet.getType()); return; } diff --git a/core/src/main/resources/languages b/core/src/main/resources/languages index a9cf5999a..f6685c4cc 160000 --- a/core/src/main/resources/languages +++ b/core/src/main/resources/languages @@ -1 +1 @@ -Subproject commit a9cf5999af605902b18dd5c77d3562481f8d7f3d +Subproject commit f6685c4ccc6e77b07402d45cb41213559004b7d6 From 3437fc98d8db1ae1172295c21e5c97f210d9e803 Mon Sep 17 00:00:00 2001 From: Camotoy <20743703+Camotoy@users.noreply.github.com> Date: Sat, 31 Dec 2022 19:34:43 -0500 Subject: [PATCH 64/81] Fix ageable entity scales not applying by default Fixes #3486 --- .../geysermc/geyser/entity/type/living/AgeableEntity.java | 6 ++++++ .../geyser/entity/type/living/animal/RabbitEntity.java | 6 ------ 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/core/src/main/java/org/geysermc/geyser/entity/type/living/AgeableEntity.java b/core/src/main/java/org/geysermc/geyser/entity/type/living/AgeableEntity.java index 2d1de8ed2..5e604fa2b 100644 --- a/core/src/main/java/org/geysermc/geyser/entity/type/living/AgeableEntity.java +++ b/core/src/main/java/org/geysermc/geyser/entity/type/living/AgeableEntity.java @@ -40,6 +40,12 @@ public class AgeableEntity extends CreatureEntity { super(session, entityId, geyserId, uuid, definition, position, motion, yaw, pitch, headYaw); } + @Override + protected void initializeMetadata() { + // Required as of 1.19.3 Java + dirtyMetadata.put(EntityData.SCALE, getAdultSize()); + } + public void setBaby(BooleanEntityMetadata entityMetadata) { boolean isBaby = entityMetadata.getPrimitiveValue(); dirtyMetadata.put(EntityData.SCALE, isBaby ? getBabySize() : getAdultSize()); diff --git a/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/RabbitEntity.java b/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/RabbitEntity.java index c49c9beb3..afa2530d3 100644 --- a/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/RabbitEntity.java +++ b/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/RabbitEntity.java @@ -25,7 +25,6 @@ package org.geysermc.geyser.entity.type.living.animal; -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.entity.EntityData; @@ -42,11 +41,6 @@ public class RabbitEntity extends AnimalEntity { super(session, entityId, geyserId, uuid, definition, position, motion, yaw, pitch, headYaw); } - @Override - public void setBaby(BooleanEntityMetadata entityMetadata) { - super.setBaby(entityMetadata); - } - public void setRabbitVariant(IntEntityMetadata entityMetadata) { int variant = entityMetadata.getPrimitiveValue(); From 1b4be6135a1cc0fbaa587d5b37c01490b15b9149 Mon Sep 17 00:00:00 2001 From: Camotoy <20743703+Camotoy@users.noreply.github.com> Date: Sat, 31 Dec 2022 19:44:30 -0500 Subject: [PATCH 65/81] Let's call super too --- .../org/geysermc/geyser/entity/type/living/AgeableEntity.java | 1 + 1 file changed, 1 insertion(+) diff --git a/core/src/main/java/org/geysermc/geyser/entity/type/living/AgeableEntity.java b/core/src/main/java/org/geysermc/geyser/entity/type/living/AgeableEntity.java index 5e604fa2b..82e43b44d 100644 --- a/core/src/main/java/org/geysermc/geyser/entity/type/living/AgeableEntity.java +++ b/core/src/main/java/org/geysermc/geyser/entity/type/living/AgeableEntity.java @@ -42,6 +42,7 @@ public class AgeableEntity extends CreatureEntity { @Override protected void initializeMetadata() { + super.initializeMetadata(); // Required as of 1.19.3 Java dirtyMetadata.put(EntityData.SCALE, getAdultSize()); } From b93b35b4328a776e031af015a5a1e6f8725cd0cc Mon Sep 17 00:00:00 2001 From: Camotoy <20743703+Camotoy@users.noreply.github.com> Date: Sun, 1 Jan 2023 14:35:16 -0500 Subject: [PATCH 66/81] Should probably call super on this too --- .../geysermc/geyser/entity/type/CommandBlockMinecartEntity.java | 1 + 1 file changed, 1 insertion(+) diff --git a/core/src/main/java/org/geysermc/geyser/entity/type/CommandBlockMinecartEntity.java b/core/src/main/java/org/geysermc/geyser/entity/type/CommandBlockMinecartEntity.java index 251eb98a0..d610c9261 100644 --- a/core/src/main/java/org/geysermc/geyser/entity/type/CommandBlockMinecartEntity.java +++ b/core/src/main/java/org/geysermc/geyser/entity/type/CommandBlockMinecartEntity.java @@ -46,6 +46,7 @@ public class CommandBlockMinecartEntity extends DefaultBlockMinecartEntity { @Override protected void initializeMetadata() { + super.initializeMetadata(); // Required, or else the GUI will not open dirtyMetadata.put(EntityData.CONTAINER_TYPE, (byte) 16); dirtyMetadata.put(EntityData.CONTAINER_BASE_SIZE, 1); From 402b3403bab09b9dcdae40dfb080fd033360e0f4 Mon Sep 17 00:00:00 2001 From: Camotoy <20743703+Camotoy@users.noreply.github.com> Date: Mon, 2 Jan 2023 12:12:22 -0500 Subject: [PATCH 67/81] Velocity: Fix disabling compression when there is no compression to disable --- .../GeyserVelocityCompressionDisabler.java | 25 +++++++++++++++---- 1 file changed, 20 insertions(+), 5 deletions(-) diff --git a/bootstrap/velocity/src/main/java/org/geysermc/geyser/platform/velocity/GeyserVelocityCompressionDisabler.java b/bootstrap/velocity/src/main/java/org/geysermc/geyser/platform/velocity/GeyserVelocityCompressionDisabler.java index 760b68c31..e787e7355 100644 --- a/bootstrap/velocity/src/main/java/org/geysermc/geyser/platform/velocity/GeyserVelocityCompressionDisabler.java +++ b/bootstrap/velocity/src/main/java/org/geysermc/geyser/platform/velocity/GeyserVelocityCompressionDisabler.java @@ -25,28 +25,32 @@ package org.geysermc.geyser.platform.velocity; +import io.netty.channel.ChannelDuplexHandler; import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.ChannelOutboundHandlerAdapter; import io.netty.channel.ChannelPromise; import org.geysermc.geyser.GeyserImpl; import java.lang.reflect.Method; -public class GeyserVelocityCompressionDisabler extends ChannelOutboundHandlerAdapter { +public class GeyserVelocityCompressionDisabler extends ChannelDuplexHandler { static final boolean ENABLED; private static final Class COMPRESSION_PACKET_CLASS; private static final Class LOGIN_SUCCESS_PACKET_CLASS; + private static final Object COMPRESSION_ENABLED_EVENT; private static final Method SET_COMPRESSION_METHOD; static { boolean enabled = false; Class compressionPacketClass = null; Class loginSuccessPacketClass = null; + Object compressionEnabledEvent = null; Method setCompressionMethod = null; try { compressionPacketClass = Class.forName("com.velocitypowered.proxy.protocol.packet.SetCompression"); loginSuccessPacketClass = Class.forName("com.velocitypowered.proxy.protocol.packet.ServerLoginSuccess"); + compressionEnabledEvent = Class.forName("com.velocitypowered.proxy.protocol.VelocityConnectionEvent") + .getDeclaredField("COMPRESSION_ENABLED").get(null); setCompressionMethod = Class.forName("com.velocitypowered.proxy.connection.MinecraftConnection") .getMethod("setCompressionThreshold", int.class); enabled = true; @@ -57,6 +61,7 @@ public class GeyserVelocityCompressionDisabler extends ChannelOutboundHandlerAda ENABLED = enabled; COMPRESSION_PACKET_CLASS = compressionPacketClass; LOGIN_SUCCESS_PACKET_CLASS = loginSuccessPacketClass; + COMPRESSION_ENABLED_EVENT = compressionEnabledEvent; SET_COMPRESSION_METHOD = setCompressionMethod; } @@ -72,13 +77,23 @@ public class GeyserVelocityCompressionDisabler extends ChannelOutboundHandlerAda if (!COMPRESSION_PACKET_CLASS.isAssignableFrom(msgClass)) { if (LOGIN_SUCCESS_PACKET_CLASS.isAssignableFrom(msgClass)) { // We're past the point that compression can be enabled - // Invoke the method as it calls a Netty event and handles removing cleaner than we could - Object minecraftConnection = ctx.pipeline().get("handler"); - SET_COMPRESSION_METHOD.invoke(minecraftConnection, -1); ctx.pipeline().remove(this); } super.write(ctx, msg, promise); } } + + @Override + public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception { + if (evt != COMPRESSION_ENABLED_EVENT) { + super.userEventTriggered(ctx, evt); + return; + } + + // Invoke the method as it calls a Netty event and handles removing cleaner than we could + Object minecraftConnection = ctx.pipeline().get("handler"); + SET_COMPRESSION_METHOD.invoke(minecraftConnection, -1); + // Do not call super and let the new compression enabled event continue firing + } } From 00df4c26ba13fc8ddf0c92500b414061b592dae4 Mon Sep 17 00:00:00 2001 From: Camotoy <20743703+Camotoy@users.noreply.github.com> Date: Tue, 3 Jan 2023 15:51:58 -0500 Subject: [PATCH 68/81] Don't send more than one ServerboundSwingPacket per tick Should address #2875 --- .../geyser/session/GeyserSession.java | 1 - .../bedrock/BedrockAnimateTranslator.java | 21 ++++++++++++++++--- 2 files changed, 18 insertions(+), 4 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 72697a85c..a95706307 100644 --- a/core/src/main/java/org/geysermc/geyser/session/GeyserSession.java +++ b/core/src/main/java/org/geysermc/geyser/session/GeyserSession.java @@ -457,7 +457,6 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource { * Counts how many ticks have occurred since an arm animation started. * -1 means there is no active arm swing. */ - @Getter(AccessLevel.NONE) private int armAnimationTicks = -1; /** diff --git a/core/src/main/java/org/geysermc/geyser/translator/protocol/bedrock/BedrockAnimateTranslator.java b/core/src/main/java/org/geysermc/geyser/translator/protocol/bedrock/BedrockAnimateTranslator.java index 670e55785..4905b5647 100644 --- a/core/src/main/java/org/geysermc/geyser/translator/protocol/bedrock/BedrockAnimateTranslator.java +++ b/core/src/main/java/org/geysermc/geyser/translator/protocol/bedrock/BedrockAnimateTranslator.java @@ -49,11 +49,26 @@ public class BedrockAnimateTranslator extends PacketTranslator { case SWING_ARM -> // Delay so entity damage can be processed first session.scheduleInEventLoop(() -> { + if (session.getArmAnimationTicks() != 0) { + // So, generally, a Java player can only do one *thing* at a time. + // If a player right-clicks, for example, then there's probably only one action associated with + // that right-click that will send a swing. + // The only exception I can think of to this, *maybe*, is a player dropping items + // Bedrock is a little funkier than this - it can send several arm animation packets in the + // same tick, notably with high levels of haste applied. + // Packet limiters do not like this and can crash the player. + // If arm animation ticks is 0, then we just sent an arm swing packet this tick. + // See https://github.com/GeyserMC/Geyser/issues/2875 + // This behavior was last touched on with ViaVersion 4.5.1 (with its packet limiter), Java 1.16.5, + // and Bedrock 1.19.51. + // Note for the future: we should probably largely ignore this packet and instead replicate + // all actions on our end, and send swings where needed. session.sendDownstreamPacket(new ServerboundSwingPacket(Hand.MAIN_HAND)); session.activateArmAnimationTicking(); - }, - 25, - TimeUnit.MILLISECONDS + } + }, + 25, + TimeUnit.MILLISECONDS ); // These two might need to be flipped, but my recommendation is getting moving working first case ROW_LEFT -> { From d1afb81a3b4b8f0dc4d8c33db5276233827e8cb1 Mon Sep 17 00:00:00 2001 From: Camotoy <20743703+Camotoy@users.noreply.github.com> Date: Tue, 3 Jan 2023 16:10:15 -0500 Subject: [PATCH 69/81] Config: elaborate on show-cooldown --- core/src/main/resources/config.yml | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/core/src/main/resources/config.yml b/core/src/main/resources/config.yml index f237e32ab..eb5b7e73c 100644 --- a/core/src/main/resources/config.yml +++ b/core/src/main/resources/config.yml @@ -4,13 +4,13 @@ # A bridge between Minecraft: Bedrock Edition and Minecraft: Java Edition. # # GitHub: https://github.com/GeyserMC/Geyser -# Discord: https://discord.geysermc.org/ +# Discord: https://discord.gg/geysermc # -------------------------------- bedrock: # The IP address that will listen for connections. - # There is no reason to change this unless you want to limit what IPs can connect to your server. - address: 0.0.0.0 + # Generally, you should only uncomment and change this if you want to limit what IPs can connect to your server. + #address: 0.0.0.0 # The port that will listen for connections port: 19132 # Some hosting services change your Java port everytime you start the server and require the same port to be used for Bedrock. @@ -117,8 +117,11 @@ allow-third-party-capes: false # MinecraftCapes allow-third-party-ears: false -# Allow a fake cooldown indicator to be sent. Bedrock players do not see a cooldown as they still use 1.8 combat -# Can be title, actionbar or false +# Allow a fake cooldown indicator to be sent. Bedrock players otherwise do not see a cooldown as they still use 1.8 combat. +# Please note: if the cooldown is enabled, some users may see a black box during the cooldown sequence, like below: +# https://cdn.discordapp.com/attachments/613170125696270357/957075682230419466/Screenshot_from_2022-03-25_20-35-08.png +# This can be disabled by going into Bedrock settings under the accessibility tab and setting "Text Background Opacity" to 0 +# This setting can be set to "title", "actionbar" or "false" show-cooldown: title # Controls if coordinates are shown to players. From b5e1ddc3c86bd560e3ebbb8814c7d074c1bd6a69 Mon Sep 17 00:00:00 2001 From: Camotoy <20743703+Camotoy@users.noreply.github.com> Date: Tue, 3 Jan 2023 19:28:43 -0500 Subject: [PATCH 70/81] Guess when the client is clicking air on mobile Should address #2113 --- .../geyser/session/GeyserSession.java | 16 +++++-- .../bedrock/BedrockAnimateTranslator.java | 44 ++++++++++--------- .../BedrockLevelSoundEventTranslator.java | 20 +++++++++ 3 files changed, 56 insertions(+), 24 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 a95706307..9ca124393 100644 --- a/core/src/main/java/org/geysermc/geyser/session/GeyserSession.java +++ b/core/src/main/java/org/geysermc/geyser/session/GeyserSession.java @@ -455,7 +455,7 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource { /** * Counts how many ticks have occurred since an arm animation started. - * -1 means there is no active arm swing. + * -1 means there is no active arm swing; -2 means an arm swing will start in a tick. */ private int armAnimationTicks = -1; @@ -1157,7 +1157,7 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource { entity.tick(); } - if (armAnimationTicks != -1) { + if (armAnimationTicks >= 0) { // As of 1.18.2 Java Edition, it appears that the swing time is dynamically updated depending on the // player's effect status, but the animation can cut short if the duration suddenly decreases // (from suddenly no longer having mining fatigue, for example) @@ -1196,7 +1196,7 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource { public void startSneaking() { // Toggle the shield, if there is no ongoing arm animation // This matches Bedrock Edition behavior as of 1.18.12 - if (armAnimationTicks == -1) { + if (armAnimationTicks < 0) { attemptToBlock(); } @@ -1328,6 +1328,16 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource { } } + /** + * For https://github.com/GeyserMC/Geyser/issues/2113 and combating arm ticking activating being delayed in + * BedrockAnimateTranslator. + */ + public void armSwingPending() { + if (armAnimationTicks == -1) { + armAnimationTicks = -2; + } + } + /** * Indicates to the client to stop blocking and tells the Java server the same. */ diff --git a/core/src/main/java/org/geysermc/geyser/translator/protocol/bedrock/BedrockAnimateTranslator.java b/core/src/main/java/org/geysermc/geyser/translator/protocol/bedrock/BedrockAnimateTranslator.java index 4905b5647..6122f573b 100644 --- a/core/src/main/java/org/geysermc/geyser/translator/protocol/bedrock/BedrockAnimateTranslator.java +++ b/core/src/main/java/org/geysermc/geyser/translator/protocol/bedrock/BedrockAnimateTranslator.java @@ -46,30 +46,32 @@ public class BedrockAnimateTranslator extends PacketTranslator { } switch (packet.getAction()) { - case SWING_ARM -> + case SWING_ARM -> { + session.armSwingPending(); // Delay so entity damage can be processed first session.scheduleInEventLoop(() -> { - if (session.getArmAnimationTicks() != 0) { - // So, generally, a Java player can only do one *thing* at a time. - // If a player right-clicks, for example, then there's probably only one action associated with - // that right-click that will send a swing. - // The only exception I can think of to this, *maybe*, is a player dropping items - // Bedrock is a little funkier than this - it can send several arm animation packets in the - // same tick, notably with high levels of haste applied. - // Packet limiters do not like this and can crash the player. - // If arm animation ticks is 0, then we just sent an arm swing packet this tick. - // See https://github.com/GeyserMC/Geyser/issues/2875 - // This behavior was last touched on with ViaVersion 4.5.1 (with its packet limiter), Java 1.16.5, - // and Bedrock 1.19.51. - // Note for the future: we should probably largely ignore this packet and instead replicate - // all actions on our end, and send swings where needed. - session.sendDownstreamPacket(new ServerboundSwingPacket(Hand.MAIN_HAND)); - session.activateArmAnimationTicking(); - } - }, - 25, - TimeUnit.MILLISECONDS + if (session.getArmAnimationTicks() != 0) { + // So, generally, a Java player can only do one *thing* at a time. + // If a player right-clicks, for example, then there's probably only one action associated with + // that right-click that will send a swing. + // The only exception I can think of to this, *maybe*, is a player dropping items + // Bedrock is a little funkier than this - it can send several arm animation packets in the + // same tick, notably with high levels of haste applied. + // Packet limiters do not like this and can crash the player. + // If arm animation ticks is 0, then we just sent an arm swing packet this tick. + // See https://github.com/GeyserMC/Geyser/issues/2875 + // This behavior was last touched on with ViaVersion 4.5.1 (with its packet limiter), Java 1.16.5, + // and Bedrock 1.19.51. + // Note for the future: we should probably largely ignore this packet and instead replicate + // all actions on our end, and send swings where needed. + session.sendDownstreamPacket(new ServerboundSwingPacket(Hand.MAIN_HAND)); + session.activateArmAnimationTicking(); + } + }, + 25, + TimeUnit.MILLISECONDS ); + } // These two might need to be flipped, but my recommendation is getting moving working first case ROW_LEFT -> { // Packet value is a float of how long one has been rowing, so we convert that into a boolean diff --git a/core/src/main/java/org/geysermc/geyser/translator/protocol/bedrock/world/BedrockLevelSoundEventTranslator.java b/core/src/main/java/org/geysermc/geyser/translator/protocol/bedrock/world/BedrockLevelSoundEventTranslator.java index df8cd07c1..87c03479d 100644 --- a/core/src/main/java/org/geysermc/geyser/translator/protocol/bedrock/world/BedrockLevelSoundEventTranslator.java +++ b/core/src/main/java/org/geysermc/geyser/translator/protocol/bedrock/world/BedrockLevelSoundEventTranslator.java @@ -25,7 +25,10 @@ package org.geysermc.geyser.translator.protocol.bedrock.world; +import com.github.steveice10.mc.protocol.data.game.entity.player.Hand; +import com.github.steveice10.mc.protocol.packet.ingame.serverbound.player.ServerboundSwingPacket; import com.nukkitx.protocol.bedrock.data.SoundEvent; +import com.nukkitx.protocol.bedrock.packet.AnimatePacket; import com.nukkitx.protocol.bedrock.packet.LevelSoundEventPacket; import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.translator.protocol.PacketTranslator; @@ -46,5 +49,22 @@ public class BedrockLevelSoundEventTranslator extends PacketTranslator Date: Wed, 4 Jan 2023 16:05:12 -0500 Subject: [PATCH 71/81] Fix some instances of chat not appearing --- 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 9b595671e..f7f488f17 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -8,7 +8,7 @@ websocket = "1.5.1" protocol = "2.9.15-20221129.204554-2" raknet = "1.6.28-20220125.214016-6" mcauthlib = "d9d773e" -mcprotocollib = "1.19.3-20221218.141127-8" +mcprotocollib = "1.19.3-20230104.210231-9" packetlib = "3.0.1" adventure = "4.12.0-20220629.025215-9" adventure-platform = "4.1.2" From 3ac931e11b243a8803f5243478e3e2ef9eb295ad Mon Sep 17 00:00:00 2001 From: Tim203 Date: Sat, 7 Jan 2023 14:29:33 +0100 Subject: [PATCH 72/81] Moved the APIs to a dedicated repo They can now be found at https://github.com/GeyserMC/api --- api/base/build.gradle.kts | 7 - .../main/java/org/geysermc/api/Geyser.java | 95 -------- .../java/org/geysermc/api/GeyserApiBase.java | 130 ----------- .../geysermc/api/connection/Connection.java | 121 ---------- .../geysermc/api/util/BedrockPlatform.java | 73 ------ .../java/org/geysermc/api/util/InputMode.java | 49 ---- .../java/org/geysermc/api/util/UiProfile.java | 45 ---- api/geyser/build.gradle.kts | 14 -- .../org/geysermc/geyser/api/GeyserApi.java | 119 ---------- .../geysermc/geyser/api/command/Command.java | 215 ------------------ .../geyser/api/command/CommandExecutor.java | 45 ---- .../geyser/api/command/CommandSource.java | 81 ------- .../api/connection/GeyserConnection.java | 35 --- .../geysermc/geyser/api/event/EventBus.java | 43 ---- .../geyser/api/event/EventRegistrar.java | 47 ---- .../geyser/api/event/EventSubscriber.java | 40 ---- .../geyser/api/event/ExtensionEventBus.java | 41 ---- .../api/event/ExtensionEventSubscriber.java | 32 --- .../api/event/connection/ConnectionEvent.java | 51 ----- .../downstream/ServerDefineCommandsEvent.java | 85 ------- .../lifecycle/GeyserDefineCommandsEvent.java | 57 ----- .../GeyserDefineCustomItemsEvent.java | 76 ------- .../GeyserLoadResourcePacksEvent.java | 40 ---- .../lifecycle/GeyserPostInitializeEvent.java | 41 ---- .../lifecycle/GeyserPreInitializeEvent.java | 41 ---- .../event/lifecycle/GeyserShutdownEvent.java | 38 ---- .../geyser/api/extension/Extension.java | 139 ----------- .../api/extension/ExtensionDescription.java | 106 --------- .../geyser/api/extension/ExtensionLoader.java | 105 --------- .../geyser/api/extension/ExtensionLogger.java | 94 -------- .../api/extension/ExtensionManager.java | 90 -------- .../InvalidDescriptionException.java | 43 ---- .../exception/InvalidExtensionException.java | 43 ---- .../api/item/custom/CustomItemData.java | 109 --------- .../api/item/custom/CustomItemOptions.java | 83 ------- .../api/item/custom/CustomRenderOffsets.java | 51 ----- .../item/custom/NonVanillaCustomItemData.java | 188 --------------- .../geysermc/geyser/api/network/AuthType.java | 61 ----- .../geyser/api/network/BedrockListener.java | 77 ------- .../geyser/api/network/RemoteServer.java | 70 ------ .../geysermc/geyser/api/util/TriState.java | 83 ------- build.gradle.kts | 18 +- core/build.gradle.kts | 2 +- .../protocol/java/JavaCommandsTranslator.java | 19 +- gradle/libs.versions.toml | 23 +- settings.gradle.kts | 4 - 46 files changed, 33 insertions(+), 3036 deletions(-) delete mode 100644 api/base/build.gradle.kts delete mode 100644 api/base/src/main/java/org/geysermc/api/Geyser.java delete mode 100644 api/base/src/main/java/org/geysermc/api/GeyserApiBase.java delete mode 100644 api/base/src/main/java/org/geysermc/api/connection/Connection.java delete mode 100644 api/base/src/main/java/org/geysermc/api/util/BedrockPlatform.java delete mode 100644 api/base/src/main/java/org/geysermc/api/util/InputMode.java delete mode 100644 api/base/src/main/java/org/geysermc/api/util/UiProfile.java delete mode 100644 api/geyser/build.gradle.kts delete mode 100644 api/geyser/src/main/java/org/geysermc/geyser/api/GeyserApi.java delete mode 100644 api/geyser/src/main/java/org/geysermc/geyser/api/command/Command.java delete mode 100644 api/geyser/src/main/java/org/geysermc/geyser/api/command/CommandExecutor.java delete mode 100644 api/geyser/src/main/java/org/geysermc/geyser/api/command/CommandSource.java delete mode 100644 api/geyser/src/main/java/org/geysermc/geyser/api/connection/GeyserConnection.java delete mode 100644 api/geyser/src/main/java/org/geysermc/geyser/api/event/EventBus.java delete mode 100644 api/geyser/src/main/java/org/geysermc/geyser/api/event/EventRegistrar.java delete mode 100644 api/geyser/src/main/java/org/geysermc/geyser/api/event/EventSubscriber.java delete mode 100644 api/geyser/src/main/java/org/geysermc/geyser/api/event/ExtensionEventBus.java delete mode 100644 api/geyser/src/main/java/org/geysermc/geyser/api/event/ExtensionEventSubscriber.java delete mode 100644 api/geyser/src/main/java/org/geysermc/geyser/api/event/connection/ConnectionEvent.java delete mode 100644 api/geyser/src/main/java/org/geysermc/geyser/api/event/downstream/ServerDefineCommandsEvent.java delete mode 100644 api/geyser/src/main/java/org/geysermc/geyser/api/event/lifecycle/GeyserDefineCommandsEvent.java delete mode 100644 api/geyser/src/main/java/org/geysermc/geyser/api/event/lifecycle/GeyserDefineCustomItemsEvent.java delete mode 100644 api/geyser/src/main/java/org/geysermc/geyser/api/event/lifecycle/GeyserLoadResourcePacksEvent.java delete mode 100644 api/geyser/src/main/java/org/geysermc/geyser/api/event/lifecycle/GeyserPostInitializeEvent.java delete mode 100644 api/geyser/src/main/java/org/geysermc/geyser/api/event/lifecycle/GeyserPreInitializeEvent.java delete mode 100644 api/geyser/src/main/java/org/geysermc/geyser/api/event/lifecycle/GeyserShutdownEvent.java delete mode 100644 api/geyser/src/main/java/org/geysermc/geyser/api/extension/Extension.java delete mode 100644 api/geyser/src/main/java/org/geysermc/geyser/api/extension/ExtensionDescription.java delete mode 100644 api/geyser/src/main/java/org/geysermc/geyser/api/extension/ExtensionLoader.java delete mode 100644 api/geyser/src/main/java/org/geysermc/geyser/api/extension/ExtensionLogger.java delete mode 100644 api/geyser/src/main/java/org/geysermc/geyser/api/extension/ExtensionManager.java delete mode 100644 api/geyser/src/main/java/org/geysermc/geyser/api/extension/exception/InvalidDescriptionException.java delete mode 100644 api/geyser/src/main/java/org/geysermc/geyser/api/extension/exception/InvalidExtensionException.java delete mode 100644 api/geyser/src/main/java/org/geysermc/geyser/api/item/custom/CustomItemData.java delete mode 100644 api/geyser/src/main/java/org/geysermc/geyser/api/item/custom/CustomItemOptions.java delete mode 100644 api/geyser/src/main/java/org/geysermc/geyser/api/item/custom/CustomRenderOffsets.java delete mode 100644 api/geyser/src/main/java/org/geysermc/geyser/api/item/custom/NonVanillaCustomItemData.java delete mode 100644 api/geyser/src/main/java/org/geysermc/geyser/api/network/AuthType.java delete mode 100644 api/geyser/src/main/java/org/geysermc/geyser/api/network/BedrockListener.java delete mode 100644 api/geyser/src/main/java/org/geysermc/geyser/api/network/RemoteServer.java delete mode 100644 api/geyser/src/main/java/org/geysermc/geyser/api/util/TriState.java diff --git a/api/base/build.gradle.kts b/api/base/build.gradle.kts deleted file mode 100644 index 6b6fb8f46..000000000 --- a/api/base/build.gradle.kts +++ /dev/null @@ -1,7 +0,0 @@ -dependencies { - api(libs.cumulus) - api(libs.events) { - exclude(group = "com.google.guava", module = "guava") - exclude(group = "org.lanternpowered", module = "lmbda") - } -} \ No newline at end of file diff --git a/api/base/src/main/java/org/geysermc/api/Geyser.java b/api/base/src/main/java/org/geysermc/api/Geyser.java deleted file mode 100644 index 7543d1661..000000000 --- a/api/base/src/main/java/org/geysermc/api/Geyser.java +++ /dev/null @@ -1,95 +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.api; - -import org.checkerframework.checker.nullness.qual.NonNull; - -/** - * General API class for Geyser. - */ -@NonNull -public class Geyser { - private static GeyserApiBase api; - - /** - * Returns the base api. - * - * @return the base api - */ - @NonNull - public static GeyserApiBase api() { - if (api == null) { - throw new RuntimeException("Api has not been registered yet!"); - } - - return api; - } - - /** - * Returns the api of the given type. - * - * @param apiClass the api class - * @param the type - * @return the api of the given type - */ - @SuppressWarnings("unchecked") - public static T api(@NonNull Class apiClass) { - if (apiClass.isInstance(api)) { - return (T) api; - } - - if (api == null) { - throw new RuntimeException("Api has not been registered yet!"); - } else { - throw new RuntimeException("Api was not an instance of " + apiClass + "! Was " + api.getClass().getCanonicalName()); - } - } - - /** - * Registers the given api type. The api cannot be - * registered if {@link #isRegistered()} is true as - * an api has already been specified. - * - * @param api the api - */ - public static void set(@NonNull GeyserApiBase api) { - if (Geyser.api != null) { - throw new RuntimeException("Cannot redefine already registered api!"); - } - - Geyser.api = api; - } - - /** - * Gets if the api has been registered and - * is ready for usage. - * - * @return if the api has been registered - */ - public static boolean isRegistered() { - return api != null; - } -} diff --git a/api/base/src/main/java/org/geysermc/api/GeyserApiBase.java b/api/base/src/main/java/org/geysermc/api/GeyserApiBase.java deleted file mode 100644 index a845e37fd..000000000 --- a/api/base/src/main/java/org/geysermc/api/GeyserApiBase.java +++ /dev/null @@ -1,130 +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.api; - -import org.checkerframework.checker.nullness.qual.MonotonicNonNull; -import org.checkerframework.checker.nullness.qual.NonNull; -import org.checkerframework.checker.nullness.qual.Nullable; -import org.checkerframework.common.value.qual.IntRange; -import org.geysermc.api.connection.Connection; -import org.geysermc.cumulus.form.Form; -import org.geysermc.cumulus.form.util.FormBuilder; - -import java.util.List; -import java.util.UUID; - -/** - * The base API class. - */ -public interface GeyserApiBase { - /** - * Gets the connection from the given UUID, if applicable. The player must be logged in to the Java server - * for this to return a non-null value. - * - * @param uuid the UUID of the connection - * @return the connection from the given UUID, if applicable - */ - @Nullable - Connection connectionByUuid(@NonNull UUID uuid); - - /** - * Gets the connection from the given XUID, if applicable. This method only works for online connections. - * - * @param xuid the XUID of the session - * @return the connection from the given UUID, if applicable - */ - @Nullable - Connection connectionByXuid(@NonNull String xuid); - - /** - * Method to determine if the given online player is a Bedrock player. - * - * @param uuid the uuid of the online player - * @return true if the given online player is a Bedrock player - */ - boolean isBedrockPlayer(@NonNull UUID uuid); - - /** - * Sends a form to the given connection and opens it. - * - * @param uuid the uuid of the connection to open it on - * @param form the form to send - * @return whether the form was successfully sent - */ - boolean sendForm(@NonNull UUID uuid, @NonNull Form form); - - /** - * Sends a form to the given connection and opens it. - * - * @param uuid the uuid of the connection to open it on - * @param formBuilder the formBuilder to send - * @return whether the form was successfully sent - */ - boolean sendForm(@NonNull UUID uuid, @NonNull FormBuilder formBuilder); - - /** - * Transfer the given connection to a server. A Bedrock player can successfully transfer to the same server they are - * currently playing on. - * - * @param uuid the uuid of the connection - * @param address the address of the server - * @param port the port of the server - * @return true if the transfer was a success - */ - boolean transfer(@NonNull UUID uuid, @NonNull String address, @IntRange(from = 0, to = 65535) int port); - - - /** - * Returns all the online connections. - */ - @NonNull - List onlineConnections(); - - /** - * Returns the amount of online connections. - */ - int onlineConnectionsCount(); - - /** - * Returns the prefix used by Floodgate. Will be null when the auth-type isn't Floodgate. - */ - @MonotonicNonNull - String usernamePrefix(); - - /** - * Returns the major API version. Bumped whenever a significant breaking change or feature addition is added. - */ - default int majorApiVersion() { - return 1; - } - - /** - * Returns the minor API version. May be bumped for new API additions. - */ - default int minorApiVersion() { - return 0; - } -} diff --git a/api/base/src/main/java/org/geysermc/api/connection/Connection.java b/api/base/src/main/java/org/geysermc/api/connection/Connection.java deleted file mode 100644 index 1cd7a9d13..000000000 --- a/api/base/src/main/java/org/geysermc/api/connection/Connection.java +++ /dev/null @@ -1,121 +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.api.connection; - -import org.checkerframework.checker.nullness.qual.MonotonicNonNull; -import org.checkerframework.checker.nullness.qual.NonNull; -import org.checkerframework.common.value.qual.IntRange; -import org.geysermc.api.util.BedrockPlatform; -import org.geysermc.api.util.InputMode; -import org.geysermc.api.util.UiProfile; -import org.geysermc.cumulus.form.Form; -import org.geysermc.cumulus.form.util.FormBuilder; - -import java.util.UUID; - -/** - * Represents a player connection. - */ -public interface Connection { - /** - * Returns the bedrock name of the connection. - */ - @NonNull String bedrockUsername(); - - /** - * Returns the java name of the connection. - */ - @MonotonicNonNull - String javaUsername(); - - /** - * Returns the UUID of the connection. - */ - @MonotonicNonNull - UUID javaUuid(); - - /** - * Returns the XUID of the connection. - */ - @NonNull String xuid(); - - /** - * Returns the version of the Bedrock client. - */ - @NonNull String version(); - - /** - * Returns the platform that the connection is playing on. - */ - @NonNull BedrockPlatform platform(); - - /** - * Returns the language code of the connection. - */ - @NonNull String languageCode(); - - /** - * Returns the User Interface Profile of the connection. - */ - @NonNull UiProfile uiProfile(); - - /** - * Returns the Input Mode of the Bedrock client. - */ - @NonNull InputMode inputMode(); - - /** - * Returns whether the connection is linked. - * This will always return false when the auth-type isn't Floodgate. - */ - boolean isLinked(); - - /** - * Sends a form to the connection and opens it. - * - * @param form the form to send - * @return whether the form was successfully sent - */ - boolean sendForm(@NonNull Form form); - - /** - * Sends a form to the connection and opens it. - * - * @param formBuilder the formBuilder to send - * @return whether the form was successfully sent - */ - boolean sendForm(@NonNull FormBuilder formBuilder); - - /** - * Transfer the connection to a server. A Bedrock player can successfully transfer to the same server they are - * currently playing on. - * - * @param address the address of the server - * @param port the port of the server - * @return true if the transfer was a success - */ - boolean transfer(@NonNull String address, @IntRange(from = 0, to = 65535) int port); -} diff --git a/api/base/src/main/java/org/geysermc/api/util/BedrockPlatform.java b/api/base/src/main/java/org/geysermc/api/util/BedrockPlatform.java deleted file mode 100644 index 15d0da027..000000000 --- a/api/base/src/main/java/org/geysermc/api/util/BedrockPlatform.java +++ /dev/null @@ -1,73 +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.api.util; - -import org.checkerframework.checker.nullness.qual.NonNull; - -public enum BedrockPlatform { - UNKNOWN("Unknown"), - GOOGLE("Android"), - IOS("iOS"), - OSX("macOS"), - AMAZON("Amazon"), - GEARVR("Gear VR"), - HOLOLENS("Hololens"), - UWP("Windows"), - WIN32("Windows x86"), - DEDICATED("Dedicated"), - TVOS("Apple TV"), - PS4("PS4"), - NX("Switch"), - XBOX("Xbox One"), - WINDOWS_PHONE("Windows Phone"); - - private static final BedrockPlatform[] VALUES = values(); - - private final String displayName; - - BedrockPlatform(String displayName) { - this.displayName = displayName; - } - - /** - * Get the BedrockPlatform from the identifier. - * - * @param id the BedrockPlatform identifier - * @return The BedrockPlatform or {@link #UNKNOWN} if the platform wasn't found - */ - @NonNull - public static BedrockPlatform fromId(int id) { - return id < VALUES.length ? VALUES[id] : VALUES[0]; - } - - /** - * @return friendly display name of platform. - */ - @Override - public String toString() { - return displayName; - } -} diff --git a/api/base/src/main/java/org/geysermc/api/util/InputMode.java b/api/base/src/main/java/org/geysermc/api/util/InputMode.java deleted file mode 100644 index 70346ffa5..000000000 --- a/api/base/src/main/java/org/geysermc/api/util/InputMode.java +++ /dev/null @@ -1,49 +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.api.util; - -import org.checkerframework.checker.nullness.qual.NonNull; - -public enum InputMode { - UNKNOWN, - KEYBOARD_MOUSE, - TOUCH, - CONTROLLER, - VR; - - private static final InputMode[] VALUES = values(); - - /** - * Get the InputMode from the identifier. - * - * @param id the InputMode identifier - * @return The InputMode or {@link #UNKNOWN} if the mode wasn't found - */ - @NonNull - public static InputMode fromId(int id) { - return VALUES.length > id ? VALUES[id] : VALUES[0]; - } -} \ No newline at end of file diff --git a/api/base/src/main/java/org/geysermc/api/util/UiProfile.java b/api/base/src/main/java/org/geysermc/api/util/UiProfile.java deleted file mode 100644 index cddb97260..000000000 --- a/api/base/src/main/java/org/geysermc/api/util/UiProfile.java +++ /dev/null @@ -1,45 +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.api.util; - -import org.checkerframework.checker.nullness.qual.NonNull; - -public enum UiProfile { - CLASSIC, POCKET; - - private static final UiProfile[] VALUES = values(); - - /** - * Get the UiProfile from the identifier. - * - * @param id the UiProfile identifier - * @return The UiProfile or {@link #CLASSIC} if the profile wasn't found - */ - @NonNull - public static UiProfile fromId(int id) { - return VALUES.length > id ? VALUES[id] : VALUES[0]; - } -} diff --git a/api/geyser/build.gradle.kts b/api/geyser/build.gradle.kts deleted file mode 100644 index dcde85337..000000000 --- a/api/geyser/build.gradle.kts +++ /dev/null @@ -1,14 +0,0 @@ -plugins { - id("geyser.api-conventions") -} - -dependencies { - api(projects.api) -} - -publishing { - publications.named("mavenJava") { - groupId = rootProject.group as String + ".geyser" - artifactId = "api" - } -} \ No newline at end of file diff --git a/api/geyser/src/main/java/org/geysermc/geyser/api/GeyserApi.java b/api/geyser/src/main/java/org/geysermc/geyser/api/GeyserApi.java deleted file mode 100644 index f86206d36..000000000 --- a/api/geyser/src/main/java/org/geysermc/geyser/api/GeyserApi.java +++ /dev/null @@ -1,119 +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.api; - -import org.checkerframework.checker.nullness.qual.NonNull; -import org.checkerframework.checker.nullness.qual.Nullable; -import org.geysermc.api.Geyser; -import org.geysermc.api.GeyserApiBase; -import org.geysermc.geyser.api.connection.GeyserConnection; -import org.geysermc.geyser.api.event.EventBus; -import org.geysermc.geyser.api.event.EventRegistrar; -import org.geysermc.geyser.api.extension.ExtensionManager; -import org.geysermc.geyser.api.network.BedrockListener; -import org.geysermc.geyser.api.network.RemoteServer; - -import java.util.List; -import java.util.UUID; - -/** - * Represents the API used in Geyser. - */ -public interface GeyserApi extends GeyserApiBase { - /** - * {@inheritDoc} - */ - @Override - @Nullable GeyserConnection connectionByUuid(@NonNull UUID uuid); - - /** - * {@inheritDoc} - */ - @Override - @Nullable GeyserConnection connectionByXuid(@NonNull String xuid); - - /** - * {@inheritDoc} - */ - @NonNull - List onlineConnections(); - - /** - * Gets the {@link ExtensionManager}. - * - * @return the extension manager - */ - @NonNull - ExtensionManager extensionManager(); - - /** - * Provides an implementation for the specified API type. - * - * @param apiClass the builder class - * @param the implementation type - * @param the API type - * @return the builder instance - */ - @NonNull - R provider(@NonNull Class apiClass, @Nullable Object... args); - - /** - * Gets the {@link EventBus} for handling - * Geyser events. - * - * @return the event bus - */ - @NonNull - EventBus eventBus(); - - /** - * Gets the default {@link RemoteServer} configured - * within the config file that is used by default. - * - * @return the default remote server used within Geyser - */ - @NonNull - RemoteServer defaultRemoteServer(); - - /** - * Gets the {@link BedrockListener} used for listening - * for Minecraft: Bedrock Edition client connections. - * - * @return the listener used for Bedrock client connectins - */ - @NonNull - BedrockListener bedrockListener(); - - /** - * Gets the current {@link GeyserApiBase} instance. - * - * @return the current geyser api instance - */ - @NonNull - static GeyserApi api() { - return Geyser.api(GeyserApi.class); - } -} diff --git a/api/geyser/src/main/java/org/geysermc/geyser/api/command/Command.java b/api/geyser/src/main/java/org/geysermc/geyser/api/command/Command.java deleted file mode 100644 index 2f1f2b24d..000000000 --- a/api/geyser/src/main/java/org/geysermc/geyser/api/command/Command.java +++ /dev/null @@ -1,215 +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.api.command; - -import org.checkerframework.checker.nullness.qual.NonNull; -import org.geysermc.geyser.api.GeyserApi; -import org.geysermc.geyser.api.connection.GeyserConnection; -import org.geysermc.geyser.api.extension.Extension; - -import java.util.Collections; -import java.util.List; - -/** - * Represents a command. - */ -public interface Command { - - /** - * Gets the command name. - * - * @return the command name - */ - @NonNull - String name(); - - /** - * Gets the command description. - * - * @return the command description - */ - @NonNull - String description(); - - /** - * Gets the permission node associated with - * this command. - * - * @return the permission node for this command - */ - @NonNull - String permission(); - - /** - * Gets the aliases for this command. - * - * @return the aliases for this command - */ - @NonNull - List aliases(); - - /** - * Gets if this command is designed to be used only by server operators. - * - * @return if this command is designated to be used only by server operators. - */ - boolean isSuggestedOpOnly(); - - /** - * Gets if this command is executable on console. - * - * @return if this command is executable on console - */ - boolean isExecutableOnConsole(); - - /** - * Gets the subcommands associated with this - * command. Mainly used within the Geyser Standalone - * GUI to know what subcommands are supported. - * - * @return the subcommands associated with this command - */ - @NonNull - default List subCommands() { - return Collections.emptyList(); - } - - /** - * Used to send a deny message to Java players if this command can only be used by Bedrock players. - * - * @return true if this command can only be used by Bedrock players. - */ - default boolean isBedrockOnly() { - return false; - } - - /** - * Creates a new {@link Command.Builder} used to construct commands. - * - * @param extension the extension - * @param the source type - * @return a new command builder used to construct commands - */ - static Command.Builder builder(@NonNull Extension extension) { - return GeyserApi.api().provider(Builder.class, extension); - } - - interface Builder { - - /** - * Defines the source type to use for this command. - *

- * Command source types can be anything that extend - * {@link CommandSource}, such as {@link GeyserConnection}. - * This will guarantee that the source used in the executor - * is an instance of this source. - * - * @param sourceType the source type - * @return the builder - */ - Builder source(@NonNull Class sourceType); - - /** - * Sets the command name. - * - * @param name the command name - * @return the builder - */ - Builder name(@NonNull String name); - - /** - * Sets the command description. - * - * @param description the command description - * @return the builder - */ - Builder description(@NonNull String description); - - /** - * Sets the permission node. - * - * @param permission the permission node - * @return the builder - */ - Builder permission(@NonNull String permission); - - /** - * Sets the aliases. - * - * @param aliases the aliases - * @return the builder - */ - Builder aliases(@NonNull List aliases); - - /** - * Sets if this command is designed to be used only by server operators. - * - * @param suggestedOpOnly if this command is designed to be used only by server operators - * @return the builder - */ - Builder suggestedOpOnly(boolean suggestedOpOnly); - - /** - * Sets if this command is executable on console. - * - * @param executableOnConsole if this command is executable on console - * @return the builder - */ - Builder executableOnConsole(boolean executableOnConsole); - - /** - * Sets the subcommands. - * - * @param subCommands the subcommands - * @return the builder - */ - Builder subCommands(@NonNull List subCommands); - - /** - * Sets if this command is bedrock only. - * - * @param bedrockOnly if this command is bedrock only - * @return the builder - */ - Builder bedrockOnly(boolean bedrockOnly); - - /** - * Sets the {@link CommandExecutor} for this command. - * - * @param executor the command executor - * @return the builder - */ - Builder executor(@NonNull CommandExecutor executor); - - /** - * Builds the command. - * - * @return the command - */ - @NonNull - Command build(); - } -} diff --git a/api/geyser/src/main/java/org/geysermc/geyser/api/command/CommandExecutor.java b/api/geyser/src/main/java/org/geysermc/geyser/api/command/CommandExecutor.java deleted file mode 100644 index 12a54ee90..000000000 --- a/api/geyser/src/main/java/org/geysermc/geyser/api/command/CommandExecutor.java +++ /dev/null @@ -1,45 +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.api.command; - -import org.checkerframework.checker.nullness.qual.NonNull; - -/** - * Handles executing a command. - * - * @param the command source - */ -public interface CommandExecutor { - /** - * Executes the given {@link Command} with the given - * {@link CommandSource}. - * - * @param source the command source - * @param command the command - * @param args the arguments - */ - void execute(@NonNull T source, @NonNull Command command, @NonNull String[] args); -} diff --git a/api/geyser/src/main/java/org/geysermc/geyser/api/command/CommandSource.java b/api/geyser/src/main/java/org/geysermc/geyser/api/command/CommandSource.java deleted file mode 100644 index 45276e2c4..000000000 --- a/api/geyser/src/main/java/org/geysermc/geyser/api/command/CommandSource.java +++ /dev/null @@ -1,81 +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.api.command; - -import org.checkerframework.checker.nullness.qual.NonNull; - -/** - * Represents an instance capable of sending commands. - */ -public interface CommandSource { - - /** - * The name of the command source. - * - * @return the name of the command source - */ - String name(); - - /** - * Sends the given message to the command source - * - * @param message the message to send - */ - void sendMessage(@NonNull String message); - - /** - * Sends the given messages to the command source - * - * @param messages the messages to send - */ - default void sendMessage(String[] messages) { - for (String message : messages) { - sendMessage(message); - } - } - - /** - * If this source is the console. - * - * @return true if this source is the console - */ - boolean isConsole(); - - /** - * Returns the locale of the command source. - * - * @return the locale of the command source. - */ - String locale(); - - /** - * Checks if this command source has the given permission - * - * @param permission The permission node to check - * @return true if this command source has a permission - */ - boolean hasPermission(String permission); -} diff --git a/api/geyser/src/main/java/org/geysermc/geyser/api/connection/GeyserConnection.java b/api/geyser/src/main/java/org/geysermc/geyser/api/connection/GeyserConnection.java deleted file mode 100644 index 13fd60407..000000000 --- a/api/geyser/src/main/java/org/geysermc/geyser/api/connection/GeyserConnection.java +++ /dev/null @@ -1,35 +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.api.connection; - -import org.geysermc.api.connection.Connection; -import org.geysermc.geyser.api.command.CommandSource; - -/** - * Represents a player connection used in Geyser. - */ -public interface GeyserConnection extends Connection, CommandSource { -} diff --git a/api/geyser/src/main/java/org/geysermc/geyser/api/event/EventBus.java b/api/geyser/src/main/java/org/geysermc/geyser/api/event/EventBus.java deleted file mode 100644 index 801bfa45f..000000000 --- a/api/geyser/src/main/java/org/geysermc/geyser/api/event/EventBus.java +++ /dev/null @@ -1,43 +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.api.event; - -import org.checkerframework.checker.nullness.qual.NonNull; -import org.geysermc.event.Event; -import org.geysermc.event.bus.OwnedEventBus; -import org.geysermc.geyser.api.extension.Extension; - -import java.util.Set; - -/** - * Represents a bus capable of subscribing - * or "listening" to events and firing them. - */ -public interface EventBus extends OwnedEventBus> { - @Override - @NonNull - Set> subscribers(@NonNull Class eventClass); -} diff --git a/api/geyser/src/main/java/org/geysermc/geyser/api/event/EventRegistrar.java b/api/geyser/src/main/java/org/geysermc/geyser/api/event/EventRegistrar.java deleted file mode 100644 index 064dd55f6..000000000 --- a/api/geyser/src/main/java/org/geysermc/geyser/api/event/EventRegistrar.java +++ /dev/null @@ -1,47 +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.api.event; - -import org.checkerframework.checker.nullness.qual.NonNull; -import org.geysermc.geyser.api.GeyserApi; - -/** - * Represents an owner for an event that allows it - * to be registered through an {@link EventBus}. - */ -public interface EventRegistrar { - - /** - * Creates an {@link EventRegistrar} instance. - * - * @param object the object to wrap around - * @return an event registrar instance - */ - @NonNull - static EventRegistrar of(@NonNull Object object) { - return GeyserApi.api().provider(EventRegistrar.class, object); - } -} diff --git a/api/geyser/src/main/java/org/geysermc/geyser/api/event/EventSubscriber.java b/api/geyser/src/main/java/org/geysermc/geyser/api/event/EventSubscriber.java deleted file mode 100644 index 7f91d09a3..000000000 --- a/api/geyser/src/main/java/org/geysermc/geyser/api/event/EventSubscriber.java +++ /dev/null @@ -1,40 +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.api.event; - -import org.geysermc.event.Event; -import org.geysermc.event.subscribe.OwnedSubscriber; -import org.geysermc.geyser.api.extension.Extension; - -/** - * Represents a subscribed listener to a {@link Event}. Wraps around - * the event and is capable of unsubscribing from the event or give - * information about it. - * - * @param the class of the event - */ -public interface EventSubscriber extends OwnedSubscriber { -} diff --git a/api/geyser/src/main/java/org/geysermc/geyser/api/event/ExtensionEventBus.java b/api/geyser/src/main/java/org/geysermc/geyser/api/event/ExtensionEventBus.java deleted file mode 100644 index a58d35891..000000000 --- a/api/geyser/src/main/java/org/geysermc/geyser/api/event/ExtensionEventBus.java +++ /dev/null @@ -1,41 +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.api.event; - -import org.checkerframework.checker.nullness.qual.NonNull; -import org.geysermc.event.Event; -import org.geysermc.geyser.api.extension.Extension; - -import java.util.Set; - -/** - * An {@link EventBus} with additional methods that implicitly - * set the extension instance. - */ -public interface ExtensionEventBus extends org.geysermc.event.bus.EventBus> { - @Override - @NonNull Set> subscribers(@NonNull Class eventClass); -} diff --git a/api/geyser/src/main/java/org/geysermc/geyser/api/event/ExtensionEventSubscriber.java b/api/geyser/src/main/java/org/geysermc/geyser/api/event/ExtensionEventSubscriber.java deleted file mode 100644 index 9c5fffa2f..000000000 --- a/api/geyser/src/main/java/org/geysermc/geyser/api/event/ExtensionEventSubscriber.java +++ /dev/null @@ -1,32 +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.api.event; - -import org.geysermc.event.Event; -import org.geysermc.event.subscribe.Subscriber; - -public interface ExtensionEventSubscriber extends Subscriber { -} diff --git a/api/geyser/src/main/java/org/geysermc/geyser/api/event/connection/ConnectionEvent.java b/api/geyser/src/main/java/org/geysermc/geyser/api/event/connection/ConnectionEvent.java deleted file mode 100644 index 158f14d53..000000000 --- a/api/geyser/src/main/java/org/geysermc/geyser/api/event/connection/ConnectionEvent.java +++ /dev/null @@ -1,51 +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.api.event.connection; - -import org.checkerframework.checker.nullness.qual.NonNull; -import org.geysermc.event.Event; -import org.geysermc.geyser.api.connection.GeyserConnection; - -/** - * An event that contains a {@link GeyserConnection}. - */ -public abstract class ConnectionEvent implements Event { - private final GeyserConnection connection; - - public ConnectionEvent(@NonNull GeyserConnection connection) { - this.connection = connection; - } - - /** - * Gets the {@link GeyserConnection}. - * - * @return the connection - */ - @NonNull - public GeyserConnection connection() { - return this.connection; - } -} diff --git a/api/geyser/src/main/java/org/geysermc/geyser/api/event/downstream/ServerDefineCommandsEvent.java b/api/geyser/src/main/java/org/geysermc/geyser/api/event/downstream/ServerDefineCommandsEvent.java deleted file mode 100644 index e46492b36..000000000 --- a/api/geyser/src/main/java/org/geysermc/geyser/api/event/downstream/ServerDefineCommandsEvent.java +++ /dev/null @@ -1,85 +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.api.event.downstream; - -import org.checkerframework.checker.nullness.qual.NonNull; -import org.geysermc.event.Cancellable; -import org.geysermc.geyser.api.connection.GeyserConnection; -import org.geysermc.geyser.api.event.connection.ConnectionEvent; - -import java.util.Set; - -/** - * Called when the Java server defines the commands available on the server. - *
- * This event is mapped to the existence of Brigadier on the server. - */ -public class ServerDefineCommandsEvent extends ConnectionEvent implements Cancellable { - private final Set commands; - private boolean cancelled; - - public ServerDefineCommandsEvent(@NonNull GeyserConnection connection, @NonNull Set commands) { - super(connection); - this.commands = commands; - } - - /** - * A collection of commands sent from the server. Any element in this collection can be removed, but no element can - * be added. - * - * @return a collection of the commands sent over - */ - @NonNull - public Set commands() { - return this.commands; - } - - @Override - public boolean isCancelled() { - return this.cancelled; - } - - @Override - public void setCancelled(boolean cancelled) { - this.cancelled = cancelled; - } - - public interface CommandInfo { - /** - * Gets the name of the command. - * - * @return the name of the command - */ - String name(); - - /** - * Gets the description of the command. - * - * @return the description of the command - */ - String description(); - } -} diff --git a/api/geyser/src/main/java/org/geysermc/geyser/api/event/lifecycle/GeyserDefineCommandsEvent.java b/api/geyser/src/main/java/org/geysermc/geyser/api/event/lifecycle/GeyserDefineCommandsEvent.java deleted file mode 100644 index 77d5efa65..000000000 --- a/api/geyser/src/main/java/org/geysermc/geyser/api/event/lifecycle/GeyserDefineCommandsEvent.java +++ /dev/null @@ -1,57 +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.api.event.lifecycle; - -import org.checkerframework.checker.nullness.qual.NonNull; -import org.geysermc.event.Event; -import org.geysermc.geyser.api.command.Command; - -import java.util.Map; - -/** - * Called when commands are defined within Geyser. - * - * This event allows you to register new commands using the {@link #register(Command)} - * method and retrieve the default commands defined. - */ -public interface GeyserDefineCommandsEvent extends Event { - - /** - * Registers the given {@link Command} into the Geyser - * command manager. - * - * @param command the command to register - */ - void register(@NonNull Command command); - - /** - * Gets all the registered built-in {@link Command}s. - * - * @return all the registered built-in commands - */ - @NonNull - Map commands(); -} diff --git a/api/geyser/src/main/java/org/geysermc/geyser/api/event/lifecycle/GeyserDefineCustomItemsEvent.java b/api/geyser/src/main/java/org/geysermc/geyser/api/event/lifecycle/GeyserDefineCustomItemsEvent.java deleted file mode 100644 index 0957b8551..000000000 --- a/api/geyser/src/main/java/org/geysermc/geyser/api/event/lifecycle/GeyserDefineCustomItemsEvent.java +++ /dev/null @@ -1,76 +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.api.event.lifecycle; - -import org.checkerframework.checker.nullness.qual.NonNull; -import org.geysermc.event.Event; -import org.geysermc.geyser.api.item.custom.CustomItemData; -import org.geysermc.geyser.api.item.custom.NonVanillaCustomItemData; - -import java.util.Collection; -import java.util.List; -import java.util.Map; - -/** - * Called on Geyser's startup when looking for custom items. Custom items must be registered through this event. - * - * This event will not be called if the "add non-Bedrock items" setting is disabled in the Geyser config. - */ -public interface GeyserDefineCustomItemsEvent extends Event { - /** - * Gets a multimap of all the already registered custom items indexed by the item's extended java item's identifier. - * - * @return a multimap of all the already registered custom items - */ - @NonNull - Map> getExistingCustomItems(); - - /** - * Gets the list of the already registered non-vanilla custom items. - * - * @return the list of the already registered non-vanilla custom items - */ - @NonNull - List getExistingNonVanillaCustomItems(); - - /** - * Registers a custom item with a base Java item. This is used to register items with custom textures and properties - * based on NBT data. - * - * @param identifier the base (java) item - * @param customItemData the custom item data to register - * @return if the item was registered - */ - boolean register(@NonNull String identifier, @NonNull CustomItemData customItemData); - - /** - * Registers a custom item with no base item. This is used for mods. - * - * @param customItemData the custom item data to register - * @return if the item was registered - */ - boolean register(@NonNull NonVanillaCustomItemData customItemData); -} \ No newline at end of file diff --git a/api/geyser/src/main/java/org/geysermc/geyser/api/event/lifecycle/GeyserLoadResourcePacksEvent.java b/api/geyser/src/main/java/org/geysermc/geyser/api/event/lifecycle/GeyserLoadResourcePacksEvent.java deleted file mode 100644 index e9b283ecb..000000000 --- a/api/geyser/src/main/java/org/geysermc/geyser/api/event/lifecycle/GeyserLoadResourcePacksEvent.java +++ /dev/null @@ -1,40 +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.api.event.lifecycle; - -import org.checkerframework.checker.nullness.qual.NonNull; -import org.geysermc.event.Event; - -import java.nio.file.Path; -import java.util.List; - -/** - * Called when resource packs are loaded within Geyser. - * - * @param resourcePacks a mutable list of the currently listed resource packs - */ -public record GeyserLoadResourcePacksEvent(@NonNull List resourcePacks) implements Event { -} diff --git a/api/geyser/src/main/java/org/geysermc/geyser/api/event/lifecycle/GeyserPostInitializeEvent.java b/api/geyser/src/main/java/org/geysermc/geyser/api/event/lifecycle/GeyserPostInitializeEvent.java deleted file mode 100644 index 8d145f615..000000000 --- a/api/geyser/src/main/java/org/geysermc/geyser/api/event/lifecycle/GeyserPostInitializeEvent.java +++ /dev/null @@ -1,41 +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.api.event.lifecycle; - -import org.checkerframework.checker.nullness.qual.NonNull; -import org.geysermc.event.Event; -import org.geysermc.geyser.api.event.EventBus; -import org.geysermc.geyser.api.event.EventRegistrar; -import org.geysermc.geyser.api.extension.ExtensionManager; - -/** - * Called when Geyser has completed initializing. - * - * @param extensionManager the extension manager - * @param eventBus the event bus - */ -public record GeyserPostInitializeEvent(@NonNull ExtensionManager extensionManager, @NonNull EventBus eventBus) implements Event { -} diff --git a/api/geyser/src/main/java/org/geysermc/geyser/api/event/lifecycle/GeyserPreInitializeEvent.java b/api/geyser/src/main/java/org/geysermc/geyser/api/event/lifecycle/GeyserPreInitializeEvent.java deleted file mode 100644 index 8be89dafd..000000000 --- a/api/geyser/src/main/java/org/geysermc/geyser/api/event/lifecycle/GeyserPreInitializeEvent.java +++ /dev/null @@ -1,41 +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.api.event.lifecycle; - -import org.checkerframework.checker.nullness.qual.NonNull; -import org.geysermc.event.Event; -import org.geysermc.geyser.api.event.EventBus; -import org.geysermc.geyser.api.event.EventRegistrar; -import org.geysermc.geyser.api.extension.ExtensionManager; - -/** - * Called when Geyser is starting to initialize. - * - * @param extensionManager the extension manager - * @param eventBus the event bus - */ -public record GeyserPreInitializeEvent(@NonNull ExtensionManager extensionManager, @NonNull EventBus eventBus) implements Event { -} diff --git a/api/geyser/src/main/java/org/geysermc/geyser/api/event/lifecycle/GeyserShutdownEvent.java b/api/geyser/src/main/java/org/geysermc/geyser/api/event/lifecycle/GeyserShutdownEvent.java deleted file mode 100644 index 7793ef997..000000000 --- a/api/geyser/src/main/java/org/geysermc/geyser/api/event/lifecycle/GeyserShutdownEvent.java +++ /dev/null @@ -1,38 +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.api.event.lifecycle; - -import org.checkerframework.checker.nullness.qual.NonNull; -import org.geysermc.event.Event; -import org.geysermc.geyser.api.event.EventBus; -import org.geysermc.geyser.api.event.EventRegistrar; -import org.geysermc.geyser.api.extension.ExtensionManager; - -/** - * Called when Geyser is shutting down. - */ -public record GeyserShutdownEvent(@NonNull ExtensionManager extensionManager, @NonNull EventBus eventBus) implements Event { -} diff --git a/api/geyser/src/main/java/org/geysermc/geyser/api/extension/Extension.java b/api/geyser/src/main/java/org/geysermc/geyser/api/extension/Extension.java deleted file mode 100644 index 33fc159de..000000000 --- a/api/geyser/src/main/java/org/geysermc/geyser/api/extension/Extension.java +++ /dev/null @@ -1,139 +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.api.extension; - -import org.checkerframework.checker.nullness.qual.NonNull; -import org.geysermc.api.GeyserApiBase; -import org.geysermc.geyser.api.GeyserApi; -import org.geysermc.geyser.api.event.EventRegistrar; -import org.geysermc.geyser.api.event.ExtensionEventBus; - -import java.nio.file.Path; -import java.util.Objects; - -/** - * Represents an extension within Geyser. - */ -public interface Extension extends EventRegistrar { - - /** - * Gets if the extension is enabled - * - * @return true if the extension is enabled - */ - default boolean isEnabled() { - return this.extensionLoader().isEnabled(this); - } - - /** - * Enables or disables the extension - * - * @param enabled if the extension should be enabled - */ - default void setEnabled(boolean enabled) { - this.extensionLoader().setEnabled(this, enabled); - } - - /** - * Gets the extension's data folder - * - * @return the extension's data folder - */ - @NonNull - default Path dataFolder() { - return this.extensionLoader().dataFolder(this); - } - - /** - * Gets the {@link ExtensionEventBus}. - * - * @return the extension event bus - */ - @NonNull - default ExtensionEventBus eventBus() { - return this.extensionLoader().eventBus(this); - } - - /** - * Gets the {@link ExtensionManager}. - * - * @return the extension manager - */ - @NonNull - default ExtensionManager extensionManager() { - return this.geyserApi().extensionManager(); - } - - /** - * Gets the extension's name - * - * @return the extension's name - */ - @NonNull - default String name() { - return this.description().name(); - } - - /** - * Gets this extension's {@link ExtensionDescription}. - * - * @return the extension's description - */ - @NonNull - default ExtensionDescription description() { - return this.extensionLoader().description(this); - } - - /** - * Gets the extension's logger - * - * @return the extension's logger - */ - @NonNull - default ExtensionLogger logger() { - return this.extensionLoader().logger(this); - } - - /** - * Gets the {@link ExtensionLoader}. - * - * @return the extension loader - */ - @NonNull - default ExtensionLoader extensionLoader() { - return Objects.requireNonNull(this.extensionManager().extensionLoader()); - } - - /** - * Gets the {@link GeyserApiBase} instance - * - * @return the geyser api instance - */ - @NonNull - default GeyserApi geyserApi() { - return GeyserApi.api(); - } -} diff --git a/api/geyser/src/main/java/org/geysermc/geyser/api/extension/ExtensionDescription.java b/api/geyser/src/main/java/org/geysermc/geyser/api/extension/ExtensionDescription.java deleted file mode 100644 index 2df3ee815..000000000 --- a/api/geyser/src/main/java/org/geysermc/geyser/api/extension/ExtensionDescription.java +++ /dev/null @@ -1,106 +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.api.extension; - -import org.checkerframework.checker.nullness.qual.NonNull; - -import java.util.List; - -/** - * Represents the description of an {@link Extension}. - */ -public interface ExtensionDescription { - - /** - * Gets the extension's id. - * - * @return the extension's id - */ - @NonNull - String id(); - - /** - * Gets the extension's name. - * - * @return the extension's name - */ - @NonNull - String name(); - - /** - * Gets the extension's main class. - * - * @return the extension's main class - */ - @NonNull - String main(); - - /** - * Gets the extension's major api version - * - * @return the extension's major api version - */ - int majorApiVersion(); - - /** - * Gets the extension's minor api version - * - * @return the extension's minor api version - */ - int minorApiVersion(); - - /** - * Gets the extension's patch api version - * - * @return the extension's patch api version - */ - int patchApiVersion(); - - /** - * Gets the extension's api version. - * - * @return the extension's api version - */ - default String apiVersion() { - return majorApiVersion() + "." + minorApiVersion() + "." + patchApiVersion(); - } - - /** - * Gets the extension's description. - * - * @return the extension's description - */ - @NonNull - String version(); - - /** - * Gets the extension's authors. - * - * @return the extension's authors - */ - @NonNull - List authors(); -} diff --git a/api/geyser/src/main/java/org/geysermc/geyser/api/extension/ExtensionLoader.java b/api/geyser/src/main/java/org/geysermc/geyser/api/extension/ExtensionLoader.java deleted file mode 100644 index 30414d500..000000000 --- a/api/geyser/src/main/java/org/geysermc/geyser/api/extension/ExtensionLoader.java +++ /dev/null @@ -1,105 +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.api.extension; - -import org.checkerframework.checker.nullness.qual.NonNull; -import org.geysermc.geyser.api.event.ExtensionEventBus; - -import java.nio.file.Path; - -/** - * The extension loader is responsible for loading, unloading, enabling and disabling extensions - */ -public abstract class ExtensionLoader { - /** - * Gets if the given {@link Extension} is enabled. - * - * @param extension the extension - * @return if the extension is enabled - */ - protected abstract boolean isEnabled(@NonNull Extension extension); - - /** - * Sets if the given {@link Extension} is enabled. - * - * @param extension the extension to enable - * @param enabled if the extension should be enabled - */ - protected abstract void setEnabled(@NonNull Extension extension, boolean enabled); - - /** - * Gets the given {@link Extension}'s data folder. - * - * @param extension the extension - * @return the data folder of the given extension - */ - @NonNull - protected abstract Path dataFolder(@NonNull Extension extension); - - /** - * Gets the given {@link Extension}'s {@link ExtensionDescription}. - * - * @param extension the extension - * @return the description of the given extension - */ - @NonNull - protected abstract ExtensionDescription description(@NonNull Extension extension); - - /** - * Gets the given {@link Extension}'s {@link ExtensionEventBus}. - * - * @param extension the extension - * @return the extension's event bus - */ - @NonNull - protected abstract ExtensionEventBus eventBus(@NonNull Extension extension); - - /** - * Gets the {@link ExtensionLogger} for the given {@link Extension}. - * - * @param extension the extension - * @return the extension logger for the given extension - */ - @NonNull - protected abstract ExtensionLogger logger(@NonNull Extension extension); - - /** - * Loads all extensions. - * - * @param extensionManager the extension manager - */ - protected abstract void loadAllExtensions(@NonNull ExtensionManager extensionManager); - - /** - * Registers the given {@link Extension} with the given {@link ExtensionManager}. - * - * @param extension the extension - * @param extensionManager the extension manager - */ - protected void register(@NonNull Extension extension, @NonNull ExtensionManager extensionManager) { - extensionManager.register(extension); - } -} \ No newline at end of file diff --git a/api/geyser/src/main/java/org/geysermc/geyser/api/extension/ExtensionLogger.java b/api/geyser/src/main/java/org/geysermc/geyser/api/extension/ExtensionLogger.java deleted file mode 100644 index 17e108455..000000000 --- a/api/geyser/src/main/java/org/geysermc/geyser/api/extension/ExtensionLogger.java +++ /dev/null @@ -1,94 +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.api.extension; - -/** - * This is the Geyser extension logger - */ -public interface ExtensionLogger { - /** - * Get the logger prefix - * - * @return the logger prefix - */ - String prefix(); - - /** - * Logs a severe message to console - * - * @param message the message to log - */ - void severe(String message); - - /** - * Logs a severe message and an exception to console - * - * @param message the message to log - * @param error the error to throw - */ - void severe(String message, Throwable error); - - /** - * Logs an error message to console - * - * @param message the message to log - */ - void error(String message); - - /** - * Logs an error message and an exception to console - * - * @param message the message to log - * @param error the error to throw - */ - void error(String message, Throwable error); - - /** - * Logs a warning message to console - * - * @param message the message to log - */ - void warning(String message); - - /** - * Logs an info message to console - * - * @param message the message to log - */ - void info(String message); - - /** - * Logs a debug message to console - * - * @param message the message to log - */ - void debug(String message); - - /** - * If debug is enabled for this logger - */ - boolean isDebug(); -} diff --git a/api/geyser/src/main/java/org/geysermc/geyser/api/extension/ExtensionManager.java b/api/geyser/src/main/java/org/geysermc/geyser/api/extension/ExtensionManager.java deleted file mode 100644 index a9d0d7376..000000000 --- a/api/geyser/src/main/java/org/geysermc/geyser/api/extension/ExtensionManager.java +++ /dev/null @@ -1,90 +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.api.extension; - -import org.checkerframework.checker.nullness.qual.NonNull; -import org.checkerframework.checker.nullness.qual.Nullable; - -import java.util.Collection; - -/** - * Manages Geyser {@link Extension}s - */ -public abstract class ExtensionManager { - - /** - * Gets an extension with the given name. - * - * @param name the name of the extension - * @return an extension with the given name - */ - @Nullable - public abstract Extension extension(@NonNull String name); - - /** - * Enables the given {@link Extension}. - * - * @param extension the extension to enable - */ - public abstract void enable(@NonNull Extension extension); - - /** - * Disables the given {@link Extension}. - * - * @param extension the extension to disable - */ - public abstract void disable(@NonNull Extension extension); - - /** - * Gets all the {@link Extension}s currently loaded. - * - * @return all the extensions currently loaded - */ - @NonNull - public abstract Collection extensions(); - - /** - * Gets the {@link ExtensionLoader}. - * - * @return the extension loader - */ - @Nullable - public abstract ExtensionLoader extensionLoader(); - - /** - * Registers an {@link Extension} with the given {@link ExtensionLoader}. - * - * @param extension the extension - */ - public abstract void register(@NonNull Extension extension); - - /** - * Loads all extensions from the given {@link ExtensionLoader}. - */ - protected final void loadAllExtensions(@NonNull ExtensionLoader extensionLoader) { - extensionLoader.loadAllExtensions(this); - } -} diff --git a/api/geyser/src/main/java/org/geysermc/geyser/api/extension/exception/InvalidDescriptionException.java b/api/geyser/src/main/java/org/geysermc/geyser/api/extension/exception/InvalidDescriptionException.java deleted file mode 100644 index 1fe88e9e9..000000000 --- a/api/geyser/src/main/java/org/geysermc/geyser/api/extension/exception/InvalidDescriptionException.java +++ /dev/null @@ -1,43 +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.api.extension.exception; - -/** - * Thrown when an extension's description is invalid. - */ -public class InvalidDescriptionException extends Exception { - public InvalidDescriptionException(Throwable cause) { - super(cause); - } - - public InvalidDescriptionException(String message) { - super(message); - } - - public InvalidDescriptionException(String message, Throwable cause) { - super(message, cause); - } -} diff --git a/api/geyser/src/main/java/org/geysermc/geyser/api/extension/exception/InvalidExtensionException.java b/api/geyser/src/main/java/org/geysermc/geyser/api/extension/exception/InvalidExtensionException.java deleted file mode 100644 index 7fb6b6922..000000000 --- a/api/geyser/src/main/java/org/geysermc/geyser/api/extension/exception/InvalidExtensionException.java +++ /dev/null @@ -1,43 +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.api.extension.exception; - -/** - * Thrown when an extension is invalid. - */ -public class InvalidExtensionException extends Exception { - public InvalidExtensionException(Throwable cause) { - super(cause); - } - - public InvalidExtensionException(String message) { - super(message); - } - - public InvalidExtensionException(String message, Throwable cause) { - super(message, cause); - } -} diff --git a/api/geyser/src/main/java/org/geysermc/geyser/api/item/custom/CustomItemData.java b/api/geyser/src/main/java/org/geysermc/geyser/api/item/custom/CustomItemData.java deleted file mode 100644 index 17763fb77..000000000 --- a/api/geyser/src/main/java/org/geysermc/geyser/api/item/custom/CustomItemData.java +++ /dev/null @@ -1,109 +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.api.item.custom; - -import org.checkerframework.checker.nullness.qual.NonNull; -import org.checkerframework.checker.nullness.qual.Nullable; -import org.geysermc.geyser.api.GeyserApi; - -/** - * This is used to store data for a custom item. - */ -public interface CustomItemData { - /** - * Gets the item's name. - * - * @return the item's name - */ - @NonNull String name(); - - /** - * Gets the custom item options of the item. - * - * @return the custom item options of the item. - */ - CustomItemOptions customItemOptions(); - - /** - * Gets the item's display name. By default, this is the item's name. - * - * @return the item's display name - */ - @NonNull String displayName(); - - /** - * Gets the item's icon. By default, this is the item's name. - * - * @return the item's icon - */ - @NonNull String icon(); - - /** - * Gets if the item is allowed to be put into the offhand. - * - * @return true if the item is allowed to be used in the offhand, false otherwise - */ - boolean allowOffhand(); - - /** - * Gets the item's texture size. This is to resize the item if the texture is not 16x16. - * - * @return the item's texture size - */ - int textureSize(); - - /** - * Gets the item's render offsets. If it is null, the item will be rendered normally, with no offsets. - * - * @return the item's render offsets - */ - @Nullable CustomRenderOffsets renderOffsets(); - - static CustomItemData.Builder builder() { - return GeyserApi.api().provider(CustomItemData.Builder.class); - } - - interface Builder { - /** - * Will also set the display name and icon to the provided parameter, if it is currently not set. - */ - Builder name(@NonNull String name); - - Builder customItemOptions(@NonNull CustomItemOptions customItemOptions); - - Builder displayName(@NonNull String displayName); - - Builder icon(@NonNull String icon); - - Builder allowOffhand(boolean allowOffhand); - - Builder textureSize(int textureSize); - - Builder renderOffsets(@Nullable CustomRenderOffsets renderOffsets); - - CustomItemData build(); - } -} diff --git a/api/geyser/src/main/java/org/geysermc/geyser/api/item/custom/CustomItemOptions.java b/api/geyser/src/main/java/org/geysermc/geyser/api/item/custom/CustomItemOptions.java deleted file mode 100644 index ec26a6e37..000000000 --- a/api/geyser/src/main/java/org/geysermc/geyser/api/item/custom/CustomItemOptions.java +++ /dev/null @@ -1,83 +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.api.item.custom; - -import org.checkerframework.checker.nullness.qual.NonNull; -import org.geysermc.geyser.api.GeyserApi; -import org.geysermc.geyser.api.util.TriState; - -import java.util.OptionalInt; - -/** - * This class represents the different ways you can register custom items - */ -public interface CustomItemOptions { - /** - * Gets if the item should be unbreakable. - * - * @return if the item should be unbreakable - */ - @NonNull TriState unbreakable(); - - /** - * Gets the item's custom model data predicate. - * - * @return the item's custom model data - */ - @NonNull OptionalInt customModelData(); - - /** - * Gets the item's damage predicate. - * - * @return the item's damage predicate - */ - @NonNull OptionalInt damagePredicate(); - - /** - * Checks if the item has at least one option set - * - * @return true if the item at least one options set - */ - default boolean hasCustomItemOptions() { - return this.unbreakable() != TriState.NOT_SET || - this.customModelData().isPresent() || - this.damagePredicate().isPresent(); - } - - static CustomItemOptions.Builder builder() { - return GeyserApi.api().provider(CustomItemOptions.Builder.class); - } - - interface Builder { - Builder unbreakable(boolean unbreakable); - - Builder customModelData(int customModelData); - - Builder damagePredicate(int damagePredicate); - - CustomItemOptions build(); - } -} diff --git a/api/geyser/src/main/java/org/geysermc/geyser/api/item/custom/CustomRenderOffsets.java b/api/geyser/src/main/java/org/geysermc/geyser/api/item/custom/CustomRenderOffsets.java deleted file mode 100644 index f81da0ae2..000000000 --- a/api/geyser/src/main/java/org/geysermc/geyser/api/item/custom/CustomRenderOffsets.java +++ /dev/null @@ -1,51 +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.api.item.custom; - -import org.checkerframework.checker.nullness.qual.Nullable; - -/** - * This class is used to store the render offsets of custom items. - */ -public record CustomRenderOffsets(@Nullable Hand mainHand, @Nullable Hand offhand) { - /** - * The hand that is used for the offset. - */ - public record Hand(@Nullable Offset firstPerson, @Nullable Offset thirdPerson) { - } - - /** - * The offset of the item. - */ - public record Offset(@Nullable OffsetXYZ position, @Nullable OffsetXYZ rotation, @Nullable OffsetXYZ scale) { - } - - /** - * X, Y and Z positions for the offset. - */ - public record OffsetXYZ(float x, float y, float z) { - } -} diff --git a/api/geyser/src/main/java/org/geysermc/geyser/api/item/custom/NonVanillaCustomItemData.java b/api/geyser/src/main/java/org/geysermc/geyser/api/item/custom/NonVanillaCustomItemData.java deleted file mode 100644 index d2cef637a..000000000 --- a/api/geyser/src/main/java/org/geysermc/geyser/api/item/custom/NonVanillaCustomItemData.java +++ /dev/null @@ -1,188 +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.api.item.custom; - -import org.checkerframework.checker.index.qual.NonNegative; -import org.checkerframework.checker.nullness.qual.NonNull; -import org.checkerframework.checker.nullness.qual.Nullable; -import org.geysermc.geyser.api.GeyserApi; - -import java.util.OptionalInt; -import java.util.Set; - -/** - * Represents a completely custom item that is not based on an existing vanilla Minecraft item. - */ -public interface NonVanillaCustomItemData extends CustomItemData { - /** - * Gets the java identifier for this item. - * - * @return The java identifier for this item. - */ - @NonNull String identifier(); - - /** - * Gets the java item id of the item. - * - * @return the java item id of the item - */ - @NonNegative int javaId(); - - /** - * Gets the stack size of the item. - * - * @return the stack size of the item - */ - @NonNegative int stackSize(); - - /** - * Gets the max damage of the item. - * - * @return the max damage of the item - */ - int maxDamage(); - - /** - * Gets the tool type of the item. - * - * @return the tool type of the item - */ - @Nullable String toolType(); - - /** - * Gets the tool tier of the item. - * - * @return the tool tier of the item - */ - @Nullable String toolTier(); - - /** - * Gets the armor type of the item. - * - * @return the armor type of the item - */ - @Nullable String armorType(); - - /** - * Gets the armor protection value of the item. - * - * @return the armor protection value of the item - */ - int protectionValue(); - - /** - * Gets the item's translation string. - * - * @return the item's translation string - */ - @Nullable String translationString(); - - /** - * Gets the repair materials of the item. - * - * @return the repair materials of the item - */ - @Nullable Set repairMaterials(); - - /** - * Gets the item's creative category, or tab id. - * - * @return the item's creative category - */ - @NonNull OptionalInt creativeCategory(); - - /** - * Gets the item's creative group. - * - * @return the item's creative group - */ - @Nullable String creativeGroup(); - - /** - * Gets if the item is a hat. This is used to determine if the item should be rendered on the player's head, and - * normally allow the player to equip it. This is not meant for armor. - * - * @return if the item is a hat - */ - boolean isHat(); - - /** - * Gets if the item is a tool. This is used to set the render type of the item, if the item is handheld. - * - * @return if the item is a tool - */ - boolean isTool(); - - static NonVanillaCustomItemData.Builder builder() { - return GeyserApi.api().provider(NonVanillaCustomItemData.Builder.class); - } - - interface Builder extends CustomItemData.Builder { - Builder name(@NonNull String name); - - Builder identifier(@NonNull String identifier); - - Builder javaId(@NonNegative int javaId); - - Builder stackSize(@NonNegative int stackSize); - - Builder maxDamage(int maxDamage); - - Builder toolType(@Nullable String toolType); - - Builder toolTier(@Nullable String toolTier); - - Builder armorType(@Nullable String armorType); - - Builder protectionValue(int protectionValue); - - Builder translationString(@Nullable String translationString); - - Builder repairMaterials(@Nullable Set repairMaterials); - - Builder creativeCategory(int creativeCategory); - - Builder creativeGroup(@Nullable String creativeGroup); - - Builder hat(boolean isHat); - - Builder tool(boolean isTool); - - @Override - Builder displayName(@NonNull String displayName); - - @Override - Builder allowOffhand(boolean allowOffhand); - - @Override - Builder textureSize(int textureSize); - - @Override - Builder renderOffsets(@Nullable CustomRenderOffsets renderOffsets); - - NonVanillaCustomItemData build(); - } -} diff --git a/api/geyser/src/main/java/org/geysermc/geyser/api/network/AuthType.java b/api/geyser/src/main/java/org/geysermc/geyser/api/network/AuthType.java deleted file mode 100644 index 3176f3384..000000000 --- a/api/geyser/src/main/java/org/geysermc/geyser/api/network/AuthType.java +++ /dev/null @@ -1,61 +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.api.network; - -import java.util.Locale; - -/** - * The authentication types that a Java server can be on connection. - */ -public enum AuthType { - OFFLINE, - ONLINE, - /** - * The internal name for connecting to an online mode server without needing a Java account. The presence of this - * authentication type does not necessarily mean the Floodgate plugin is installed; it only means that this - * authentication type will be attempted. - */ - FLOODGATE; - - private static final AuthType[] VALUES = values(); - - /** - * Convert the AuthType string (from config) to the enum, ONLINE on fail - * - * @param name AuthType string - * - * @return The converted AuthType - */ - public static AuthType getByName(String name) { - String upperCase = name.toUpperCase(Locale.ROOT); - for (AuthType type : VALUES) { - if (type.name().equals(upperCase)) { - return type; - } - } - return ONLINE; - } -} \ No newline at end of file diff --git a/api/geyser/src/main/java/org/geysermc/geyser/api/network/BedrockListener.java b/api/geyser/src/main/java/org/geysermc/geyser/api/network/BedrockListener.java deleted file mode 100644 index 61fe286aa..000000000 --- a/api/geyser/src/main/java/org/geysermc/geyser/api/network/BedrockListener.java +++ /dev/null @@ -1,77 +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.api.network; - -import org.checkerframework.checker.nullness.qual.NonNull; - -/** - * The listener that handles connections from Minecraft: - * Bedrock Edition. - */ -public interface BedrockListener { - - /** - * Gets the address used for listening for Bedrock - * connections from. - * - * @return the listening address - */ - @NonNull - String address(); - - /** - * Gets the port used for listening for Bedrock - * connections from. - * - * @return the listening port - */ - int port(); - - /** - * Gets the primary MOTD shown to Bedrock players if a ping passthrough setting is not enabled. - *

- * This is the first line that will be displayed. - * - * @return the primary MOTD shown to Bedrock players. - */ - String primaryMotd(); - - /** - * Gets the secondary MOTD shown to Bedrock players if a ping passthrough setting is not enabled. - *

- * This is the second line that will be displayed. - * - * @return the secondary MOTD shown to Bedrock players. - */ - String secondaryMotd(); - - /** - * Gets the server name that is sent to Bedrock clients. - * - * @return the server sent to Bedrock clients - */ - String serverName(); -} diff --git a/api/geyser/src/main/java/org/geysermc/geyser/api/network/RemoteServer.java b/api/geyser/src/main/java/org/geysermc/geyser/api/network/RemoteServer.java deleted file mode 100644 index 8ac5d8a03..000000000 --- a/api/geyser/src/main/java/org/geysermc/geyser/api/network/RemoteServer.java +++ /dev/null @@ -1,70 +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.api.network; - -import org.checkerframework.checker.nullness.qual.NonNull; - -/** - * Represents the Java server that Geyser is connecting to. - */ -public interface RemoteServer { - - /** - * Gets the IP address of the remote server. - * - * @return the IP address of the remote server - */ - String address(); - - /** - * Gets the port of the remote server. - * - * @return the port of the remote server - */ - int port(); - - /** - * Gets the protocol version of the remote server. - * - * @return the protocol version of the remote server - */ - int protocolVersion(); - - /** - * Gets the Minecraft version of the remote server. - * - * @return the Minecraft version of the remote server - */ - String minecraftVersion(); - - /** - * Gets the {@link AuthType} required by the remote server. - * - * @return the auth type required by the remote server - */ - @NonNull - AuthType authType(); -} diff --git a/api/geyser/src/main/java/org/geysermc/geyser/api/util/TriState.java b/api/geyser/src/main/java/org/geysermc/geyser/api/util/TriState.java deleted file mode 100644 index 457a38e32..000000000 --- a/api/geyser/src/main/java/org/geysermc/geyser/api/util/TriState.java +++ /dev/null @@ -1,83 +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.api.util; - -import org.checkerframework.checker.nullness.qual.NonNull; -import org.checkerframework.checker.nullness.qual.Nullable; - -/** - * This is a way to represent a boolean, but with a non set value added. - * This class was inspired by adventure's version https://github.com/KyoriPowered/adventure/blob/main/4/api/src/main/java/net/kyori/adventure/util/TriState.java - */ -public enum TriState { - /** - * Describes a value that is not set, null, or not present. - */ - NOT_SET, - - /** - * Describes a true value. - */ - TRUE, - - /** - * Describes a false value. - */ - FALSE; - - /** - * Converts the TriState to a boolean. - * - * @return the boolean value of the TriState - */ - public @Nullable Boolean toBoolean() { - return switch (this) { - case TRUE -> true; - case FALSE -> false; - default -> null; - }; - } - - /** - * Creates a TriState from a boolean. - * - * @param value the Boolean value - * @return the created TriState - */ - public static @NonNull TriState fromBoolean(@Nullable Boolean value) { - return value == null ? NOT_SET : fromBoolean(value.booleanValue()); - } - - /** - * Creates a TriState from a primitive boolean. - * - * @param value the boolean value - * @return the created TriState - */ - public @NonNull static TriState fromBoolean(boolean value) { - return value ? TRUE : FALSE; - } -} diff --git a/build.gradle.kts b/build.gradle.kts index 06c2e987b..4304811ff 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -5,7 +5,7 @@ plugins { } allprojects { - group = "org.geysermc" + group = "org.geysermc.geyser" version = "2.1.0-SNAPSHOT" description = "Allows for players from Minecraft: Bedrock Edition to join Minecraft: Java Edition servers." @@ -23,8 +23,6 @@ val platforms = setOf( projects.velocity ).map { it.dependencyProject } -val api: Project = projects.api.dependencyProject - subprojects { apply { plugin("java-library") @@ -32,16 +30,8 @@ subprojects { plugin("geyser.build-logic") } - val relativePath = projectDir.relativeTo(rootProject.projectDir).path - - if (relativePath.contains("api")) { - plugins.apply("geyser.api-conventions") - } else { - group = rootProject.group as String + ".geyser" - when (this) { - in platforms -> plugins.apply("geyser.platform-conventions") - api -> plugins.apply("geyser.publish-conventions") - else -> plugins.apply("geyser.base-conventions") - } + when (this) { + in platforms -> plugins.apply("geyser.platform-conventions") + else -> plugins.apply("geyser.base-conventions") } } \ No newline at end of file diff --git a/core/build.gradle.kts b/core/build.gradle.kts index 994325ea0..c9a929bcc 100644 --- a/core/build.gradle.kts +++ b/core/build.gradle.kts @@ -7,8 +7,8 @@ plugins { } dependencies { - api(projects.geyserApi) api(projects.common) + api(libs.geyser.api) // Jackson JSON and YAML serialization api(libs.bundles.jackson) 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 11311b63c..8fd425df4 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 @@ -46,7 +46,7 @@ import lombok.ToString; import net.kyori.adventure.text.format.NamedTextColor; import org.checkerframework.checker.nullness.qual.MonotonicNonNull; import org.geysermc.geyser.GeyserImpl; -import org.geysermc.geyser.api.event.downstream.ServerDefineCommandsEvent; +import org.geysermc.geyser.api.event.java.ServerDefineCommandsEvent; import org.geysermc.geyser.command.GeyserCommandManager; import org.geysermc.geyser.inventory.item.Enchantment; import org.geysermc.geyser.registry.BlockRegistries; @@ -150,12 +150,20 @@ public class JavaCommandsTranslator extends PacketTranslator new HashSet<>()).add(node.getName().toLowerCase()); } - ServerDefineCommandsEvent event = new ServerDefineCommandsEvent(session, commands.keySet()); - session.getGeyser().eventBus().fire(event); + var eventBus = session.getGeyser().eventBus(); + + var event = new ServerDefineCommandsEvent(session, commands.keySet()); + eventBus.fire(event); if (event.isCancelled()) { return; } + var oldEvent = new org.geysermc.geyser.api.event.downstream.ServerDefineCommandsEvent(session, commands.keySet()); + eventBus.fire(oldEvent); + if (oldEvent.isCancelled()) { + return; + } + // The command flags, not sure what these do apart from break things List flags = Collections.emptyList(); @@ -258,7 +266,10 @@ public class JavaCommandsTranslator extends PacketTranslator Date: Sat, 7 Jan 2023 14:46:22 -0500 Subject: [PATCH 73/81] Should fix some instances of chat not showing --- 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 f7f488f17..a54513df9 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -8,7 +8,7 @@ websocket = "1.5.1" protocol = "2.9.15-20221129.204554-2" raknet = "1.6.28-20220125.214016-6" mcauthlib = "d9d773e" -mcprotocollib = "1.19.3-20230104.210231-9" +mcprotocollib = "1.19.3-20230107.194116-10" packetlib = "3.0.1" adventure = "4.12.0-20220629.025215-9" adventure-platform = "4.1.2" From 0b80c589584129ba2280463f4c65fcd84c6d77db Mon Sep 17 00:00:00 2001 From: Camotoy <20743703+Camotoy@users.noreply.github.com> Date: Mon, 16 Jan 2023 19:16:01 -0500 Subject: [PATCH 74/81] Handle invalid items in shulker box --- .../inventory/item/nbt/ShulkerBoxItemTranslator.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/core/src/main/java/org/geysermc/geyser/translator/inventory/item/nbt/ShulkerBoxItemTranslator.java b/core/src/main/java/org/geysermc/geyser/translator/inventory/item/nbt/ShulkerBoxItemTranslator.java index f95c54e18..29d97dc27 100644 --- a/core/src/main/java/org/geysermc/geyser/translator/inventory/item/nbt/ShulkerBoxItemTranslator.java +++ b/core/src/main/java/org/geysermc/geyser/translator/inventory/item/nbt/ShulkerBoxItemTranslator.java @@ -52,6 +52,11 @@ public class ShulkerBoxItemTranslator extends NbtItemStackTranslator { ItemMapping boxMapping = session.getItemMappings().getMapping(Identifier.formalize(((StringTag) itemData.get("id")).getValue())); + if (boxMapping == null) { + // If invalid ID + continue; + } + boxItemTag.put(new StringTag("Name", boxMapping.getBedrockIdentifier())); boxItemTag.put(new ShortTag("Damage", (short) boxMapping.getBedrockData())); boxItemTag.put(new ByteTag("Count", MathUtils.getNbtByte(itemData.get("Count").getValue()))); From b8040a1d9849d9f4ba62ff9e5a9a2447820dd438 Mon Sep 17 00:00:00 2001 From: Kas-tle <26531652+Kas-tle@users.noreply.github.com> Date: Fri, 20 Jan 2023 07:02:20 -0800 Subject: [PATCH 75/81] Fix some instances of chunks not appearing (#3498) --- .../main/java/org/geysermc/geyser/session/GeyserSession.java | 4 ++++ 1 file changed, 4 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 9ca124393..b629fa4d4 100644 --- a/core/src/main/java/org/geysermc/geyser/session/GeyserSession.java +++ b/core/src/main/java/org/geysermc/geyser/session/GeyserSession.java @@ -1402,6 +1402,10 @@ 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(); From 48d78720a170ba7630f26d415f1905400fe36d25 Mon Sep 17 00:00:00 2001 From: David Choo <4722249+davchoo@users.noreply.github.com> Date: Sun, 22 Jan 2023 13:23:16 -0500 Subject: [PATCH 76/81] Apply fallback skins to custom skulls with invalid or empty texture values (#3515) --- .../entity/type/player/SkullPlayerEntity.java | 14 +++- .../geyser/session/cache/SkullCache.java | 4 +- .../org/geysermc/geyser/skin/SkinManager.java | 40 ++++++---- .../geysermc/geyser/skin/SkinProvider.java | 78 ++++++++++--------- .../geyser/skin/SkullSkinManager.java | 52 ++++++++----- .../entity/SkullBlockEntityTranslator.java | 65 +++++++++++----- 6 files changed, 159 insertions(+), 94 deletions(-) diff --git a/core/src/main/java/org/geysermc/geyser/entity/type/player/SkullPlayerEntity.java b/core/src/main/java/org/geysermc/geyser/entity/type/player/SkullPlayerEntity.java index c2af2e36b..369436b21 100644 --- a/core/src/main/java/org/geysermc/geyser/entity/type/player/SkullPlayerEntity.java +++ b/core/src/main/java/org/geysermc/geyser/entity/type/player/SkullPlayerEntity.java @@ -26,17 +26,20 @@ package org.geysermc.geyser.entity.type.player; import com.nukkitx.math.vector.Vector3f; +import com.nukkitx.math.vector.Vector3i; import com.nukkitx.protocol.bedrock.data.GameType; import com.nukkitx.protocol.bedrock.data.PlayerPermission; import com.nukkitx.protocol.bedrock.data.command.CommandPermission; import com.nukkitx.protocol.bedrock.data.entity.EntityData; import com.nukkitx.protocol.bedrock.data.entity.EntityFlag; import com.nukkitx.protocol.bedrock.packet.AddPlayerPacket; +import lombok.Getter; import org.geysermc.geyser.level.block.BlockStateValues; import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.session.cache.SkullCache; import org.geysermc.geyser.skin.SkullSkinManager; +import java.util.Objects; import java.util.UUID; import java.util.concurrent.TimeUnit; @@ -46,6 +49,12 @@ import java.util.concurrent.TimeUnit; */ public class SkullPlayerEntity extends PlayerEntity { + @Getter + private UUID skullUUID; + + @Getter + private Vector3i skullPosition; + public SkullPlayerEntity(GeyserSession session, long geyserId) { super(session, 0, geyserId, UUID.randomUUID(), Vector3f.ZERO, Vector3f.ZERO, 0, 0, 0, "", null); } @@ -102,11 +111,14 @@ public class SkullPlayerEntity extends PlayerEntity { } public void updateSkull(SkullCache.Skull skull) { - if (!skull.getTexturesProperty().equals(getTexturesProperty())) { + skullPosition = skull.getPosition(); + + if (!Objects.equals(skull.getTexturesProperty(), getTexturesProperty()) || !Objects.equals(skullUUID, skull.getUuid())) { // Make skull invisible as we change skins setFlag(EntityFlag.INVISIBLE, true); updateBedrockMetadata(); + skullUUID = skull.getUuid(); setTexturesProperty(skull.getTexturesProperty()); SkullSkinManager.requestAndHandleSkin(this, session, (skin -> session.scheduleInEventLoop(() -> { diff --git a/core/src/main/java/org/geysermc/geyser/session/cache/SkullCache.java b/core/src/main/java/org/geysermc/geyser/session/cache/SkullCache.java index f26e1cce3..6b25e84b4 100644 --- a/core/src/main/java/org/geysermc/geyser/session/cache/SkullCache.java +++ b/core/src/main/java/org/geysermc/geyser/session/cache/SkullCache.java @@ -71,8 +71,9 @@ public class SkullCache { this.skullRenderDistanceSquared = distance * distance; } - public void putSkull(Vector3i position, String texturesProperty, int blockState) { + public void putSkull(Vector3i position, UUID uuid, String texturesProperty, int blockState) { Skull skull = skulls.computeIfAbsent(position, Skull::new); + skull.uuid = uuid; skull.texturesProperty = texturesProperty; skull.blockState = blockState; @@ -201,6 +202,7 @@ public class SkullCache { @RequiredArgsConstructor @Data public static class Skull { + private UUID uuid; private String texturesProperty; private int blockState; private SkullPlayerEntity entity; diff --git a/core/src/main/java/org/geysermc/geyser/skin/SkinManager.java b/core/src/main/java/org/geysermc/geyser/skin/SkinManager.java index 800b71c96..48233fe3d 100644 --- a/core/src/main/java/org/geysermc/geyser/skin/SkinManager.java +++ b/core/src/main/java/org/geysermc/geyser/skin/SkinManager.java @@ -35,6 +35,7 @@ import com.nukkitx.protocol.bedrock.packet.PlayerListPacket; import com.nukkitx.protocol.bedrock.packet.PlayerSkinPacket; import org.geysermc.geyser.GeyserImpl; import org.geysermc.geyser.entity.type.player.PlayerEntity; +import org.geysermc.geyser.entity.type.player.SkullPlayerEntity; import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.session.auth.BedrockClientData; import org.geysermc.geyser.text.GeyserLocale; @@ -69,7 +70,7 @@ public class SkinManager { // The server either didn't have a texture to send, or we didn't have the texture ID cached. // Let's see if this player is a Bedrock player, and if so, let's pull their skin. // Otherwise, grab the default player skin - SkinProvider.SkinData fallbackSkinData = SkinProvider.determineFallbackSkinData(playerEntity); + SkinProvider.SkinData fallbackSkinData = SkinProvider.determineFallbackSkinData(playerEntity.getUuid()); if (skin == null) { skin = fallbackSkinData.skin(); geometry = fallbackSkinData.geometry(); @@ -255,24 +256,28 @@ public class SkinManager { * @return The built GameProfileData */ public static @Nullable GameProfileData from(PlayerEntity entity) { - try { - String texturesProperty = entity.getTexturesProperty(); + String texturesProperty = entity.getTexturesProperty(); + if (texturesProperty == null) { + // Likely offline mode + return null; + } - if (texturesProperty == null) { - // Likely offline mode - return null; - } + try { return loadFromJson(texturesProperty); - } catch (IOException exception) { - GeyserImpl.getInstance().getLogger().debug("Something went wrong while processing skin for " + entity.getUsername()); + } catch (Exception exception) { + if (entity instanceof SkullPlayerEntity skullEntity) { + GeyserImpl.getInstance().getLogger().debug("Something went wrong while processing skin for skull at " + skullEntity.getSkullPosition() + " with Value: " + texturesProperty); + } else { + GeyserImpl.getInstance().getLogger().debug("Something went wrong while processing skin for " + entity.getUsername() + " with Value: " + texturesProperty); + } if (GeyserImpl.getInstance().getConfig().isDebugMode()) { exception.printStackTrace(); } - return null; } + return null; } - private static GameProfileData loadFromJson(String encodedJson) throws IOException { + private static GameProfileData loadFromJson(String encodedJson) throws IOException, IllegalArgumentException { JsonNode skinObject = GeyserImpl.JSON_MAPPER.readTree(new String(Base64.getDecoder().decode(encodedJson), StandardCharsets.UTF_8)); JsonNode textures = skinObject.get("textures"); @@ -285,14 +290,23 @@ public class SkinManager { return null; } - String skinUrl = skinTexture.get("url").asText().replace("http://", "https://"); + String skinUrl; + JsonNode skinUrlNode = skinTexture.get("url"); + if (skinUrlNode != null && skinUrlNode.isTextual()) { + skinUrl = skinUrlNode.asText().replace("http://", "https://"); + } else { + return null; + } boolean isAlex = skinTexture.has("metadata"); String capeUrl = null; JsonNode capeTexture = textures.get("CAPE"); if (capeTexture != null) { - capeUrl = capeTexture.get("url").asText().replace("http://", "https://"); + JsonNode capeUrlNode = capeTexture.get("url"); + if (capeUrlNode != null && capeUrlNode.isTextual()) { + capeUrl = capeUrlNode.asText().replace("http://", "https://"); + } } return new GameProfileData(skinUrl, capeUrl, isAlex); diff --git a/core/src/main/java/org/geysermc/geyser/skin/SkinProvider.java b/core/src/main/java/org/geysermc/geyser/skin/SkinProvider.java index 61f24ac1e..38ff92e8f 100644 --- a/core/src/main/java/org/geysermc/geyser/skin/SkinProvider.java +++ b/core/src/main/java/org/geysermc/geyser/skin/SkinProvider.java @@ -26,9 +26,6 @@ package org.geysermc.geyser.skin; import com.fasterxml.jackson.databind.JsonNode; -import com.github.steveice10.opennbt.tag.builtin.CompoundTag; -import com.github.steveice10.opennbt.tag.builtin.IntArrayTag; -import com.github.steveice10.opennbt.tag.builtin.Tag; import com.google.common.cache.Cache; import com.google.common.cache.CacheBuilder; import it.unimi.dsi.fastutil.bytes.ByteArrays; @@ -172,14 +169,13 @@ public class SkinProvider { /** * If skin data fails to apply, or there is no skin data to apply, determine what skin we should give as a fallback. */ - static SkinData determineFallbackSkinData(PlayerEntity entity) { + static SkinData determineFallbackSkinData(UUID uuid) { Skin skin = null; Cape cape = null; SkinGeometry geometry = SkinGeometry.WIDE; if (GeyserImpl.getInstance().getConfig().getRemote().authType() != AuthType.ONLINE) { // Let's see if this player is a Bedrock player, and if so, let's pull their skin. - UUID uuid = entity.getUuid(); GeyserSession session = GeyserImpl.getInstance().connectionByUuid(uuid); if (session != null) { String skinId = session.getClientData().getSkinId(); @@ -192,7 +188,7 @@ public class SkinProvider { if (skin == null) { // We don't have a skin for the player right now. Fall back to a default. - ProvidedSkins.ProvidedSkin providedSkin = ProvidedSkins.getDefaultPlayerSkin(entity.getUuid()); + ProvidedSkins.ProvidedSkin providedSkin = ProvidedSkins.getDefaultPlayerSkin(uuid); skin = providedSkin.getData(); geometry = providedSkin.isSlim() ? SkinProvider.SkinGeometry.SLIM : SkinProvider.SkinGeometry.WIDE; } @@ -232,7 +228,7 @@ public class SkinProvider { SkinManager.GameProfileData data = SkinManager.GameProfileData.from(entity); if (data == null) { // This player likely does not have a textures property - return CompletableFuture.completedFuture(determineFallbackSkinData(entity)); + return CompletableFuture.completedFuture(determineFallbackSkinData(entity.getUuid())); } return requestSkinAndCape(entity.getUuid(), data.skinUrl(), data.capeUrl()) @@ -597,48 +593,23 @@ public class SkinProvider { } /** - * If a skull has a username but no textures, request them. + * Request textures from a player's UUID * - * @param skullOwner the CompoundTag of the skull with no textures + * @param uuid the player's UUID without any hyphens * @return a completable GameProfile with textures included */ - public static CompletableFuture requestTexturesFromUsername(CompoundTag skullOwner) { + public static CompletableFuture requestTexturesFromUUID(String uuid) { return CompletableFuture.supplyAsync(() -> { - Tag uuidTag = skullOwner.get("Id"); - String uuidToString = ""; - JsonNode node; - boolean retrieveUuidFromInternet = !(uuidTag instanceof IntArrayTag); // also covers null check - - if (!retrieveUuidFromInternet) { - int[] uuidAsArray = ((IntArrayTag) uuidTag).getValue(); - // thank u viaversion - UUID uuid = new UUID((long) uuidAsArray[0] << 32 | ((long) uuidAsArray[1] & 0xFFFFFFFFL), - (long) uuidAsArray[2] << 32 | ((long) uuidAsArray[3] & 0xFFFFFFFFL)); - retrieveUuidFromInternet = uuid.version() != 4; - uuidToString = uuid.toString().replace("-", ""); - } - try { - if (retrieveUuidFromInternet) { - // Offline skin, or no present UUID - node = WebUtils.getJson("https://api.mojang.com/users/profiles/minecraft/" + skullOwner.get("Name").getValue()); - JsonNode id = node.get("id"); - if (id == null) { - GeyserImpl.getInstance().getLogger().debug("No UUID found in Mojang response for " + skullOwner.get("Name").getValue()); - return null; - } - uuidToString = id.asText(); - } - - // Get textures from UUID - node = WebUtils.getJson("https://sessionserver.mojang.com/session/minecraft/profile/" + uuidToString); + JsonNode node = WebUtils.getJson("https://sessionserver.mojang.com/session/minecraft/profile/" + uuid); JsonNode properties = node.get("properties"); if (properties == null) { - GeyserImpl.getInstance().getLogger().debug("No properties found in Mojang response for " + uuidToString); + GeyserImpl.getInstance().getLogger().debug("No properties found in Mojang response for " + uuid); return null; } return node.get("properties").get(0).get("value").asText(); } catch (Exception e) { + GeyserImpl.getInstance().getLogger().debug("Unable to request textures for " + uuid); if (GeyserImpl.getInstance().getConfig().isDebugMode()) { e.printStackTrace(); } @@ -647,6 +618,37 @@ public class SkinProvider { }, EXECUTOR_SERVICE); } + /** + * Request textures from a player's username + * + * @param username the player's username + * @return a completable GameProfile with textures included + */ + public static CompletableFuture requestTexturesFromUsername(String username) { + return CompletableFuture.supplyAsync(() -> { + try { + // Offline skin, or no present UUID + JsonNode node = WebUtils.getJson("https://api.mojang.com/users/profiles/minecraft/" + username); + JsonNode id = node.get("id"); + if (id == null) { + GeyserImpl.getInstance().getLogger().debug("No UUID found in Mojang response for " + username); + return null; + } + return id.asText(); + } catch (Exception e) { + if (GeyserImpl.getInstance().getConfig().isDebugMode()) { + e.printStackTrace(); + } + return null; + } + }, EXECUTOR_SERVICE).thenCompose(uuid -> { + if (uuid == null) { + return CompletableFuture.completedFuture(null); + } + return requestTexturesFromUUID(uuid); + }); + } + private static BufferedImage downloadImage(String imageUrl, CapeProvider provider) throws IOException { if (provider == CapeProvider.FIVEZIG) return readFiveZigCape(imageUrl); diff --git a/core/src/main/java/org/geysermc/geyser/skin/SkullSkinManager.java b/core/src/main/java/org/geysermc/geyser/skin/SkullSkinManager.java index 2759b1408..7f1605561 100644 --- a/core/src/main/java/org/geysermc/geyser/skin/SkullSkinManager.java +++ b/core/src/main/java/org/geysermc/geyser/skin/SkullSkinManager.java @@ -29,11 +29,12 @@ import com.nukkitx.protocol.bedrock.data.skin.ImageData; import com.nukkitx.protocol.bedrock.data.skin.SerializedSkin; import com.nukkitx.protocol.bedrock.packet.PlayerSkinPacket; import org.geysermc.geyser.GeyserImpl; -import org.geysermc.geyser.entity.type.player.PlayerEntity; +import org.geysermc.geyser.entity.type.player.SkullPlayerEntity; import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.text.GeyserLocale; import java.util.Collections; +import java.util.function.BiConsumer; import java.util.function.Consumer; public class SkullSkinManager extends SkinManager { @@ -48,28 +49,37 @@ public class SkullSkinManager extends SkinManager { ); } - public static void requestAndHandleSkin(PlayerEntity entity, GeyserSession session, + public static void requestAndHandleSkin(SkullPlayerEntity entity, GeyserSession session, Consumer skinConsumer) { + BiConsumer applySkin = (skin, throwable) -> { + try { + PlayerSkinPacket packet = new PlayerSkinPacket(); + packet.setUuid(entity.getUuid()); + packet.setOldSkinName(""); + packet.setNewSkinName(skin.getTextureUrl()); + packet.setSkin(buildSkullEntryManually(skin.getTextureUrl(), skin.getSkinData())); + packet.setTrustedSkin(true); + session.sendUpstreamPacket(packet); + } catch (Exception e) { + GeyserImpl.getInstance().getLogger().error(GeyserLocale.getLocaleStringLog("geyser.skin.fail", entity.getUuid()), e); + } + + if (skinConsumer != null) { + skinConsumer.accept(skin); + } + }; + GameProfileData data = GameProfileData.from(entity); - - SkinProvider.requestSkin(entity.getUuid(), data.skinUrl(), true) - .whenCompleteAsync((skin, throwable) -> { - try { - PlayerSkinPacket packet = new PlayerSkinPacket(); - packet.setUuid(entity.getUuid()); - packet.setOldSkinName(""); - packet.setNewSkinName(skin.getTextureUrl()); - packet.setSkin(buildSkullEntryManually(skin.getTextureUrl(), skin.getSkinData())); - packet.setTrustedSkin(true); - session.sendUpstreamPacket(packet); - } catch (Exception e) { - GeyserImpl.getInstance().getLogger().error(GeyserLocale.getLocaleStringLog("geyser.skin.fail", entity.getUuid()), e); - } - - if (skinConsumer != null) { - skinConsumer.accept(skin); - } - }); + if (data == null) { + GeyserImpl.getInstance().getLogger().debug("Using fallback skin for skull at " + entity.getSkullPosition() + + " with texture value: " + entity.getTexturesProperty() + " and UUID: " + entity.getSkullUUID()); + // No texture available, fallback using the UUID + SkinProvider.SkinData fallback = SkinProvider.determineFallbackSkinData(entity.getSkullUUID()); + applySkin.accept(fallback.skin(), null); + } else { + SkinProvider.requestSkin(entity.getUuid(), data.skinUrl(), true) + .whenCompleteAsync(applySkin); + } } } diff --git a/core/src/main/java/org/geysermc/geyser/translator/level/block/entity/SkullBlockEntityTranslator.java b/core/src/main/java/org/geysermc/geyser/translator/level/block/entity/SkullBlockEntityTranslator.java index 94e2d4767..2130206e1 100644 --- a/core/src/main/java/org/geysermc/geyser/translator/level/block/entity/SkullBlockEntityTranslator.java +++ b/core/src/main/java/org/geysermc/geyser/translator/level/block/entity/SkullBlockEntityTranslator.java @@ -27,6 +27,7 @@ package org.geysermc.geyser.translator.level.block.entity; import com.github.steveice10.mc.protocol.data.game.level.block.BlockEntityType; import com.github.steveice10.opennbt.tag.builtin.CompoundTag; +import com.github.steveice10.opennbt.tag.builtin.IntArrayTag; import com.github.steveice10.opennbt.tag.builtin.ListTag; import com.github.steveice10.opennbt.tag.builtin.StringTag; import com.nukkitx.math.vector.Vector3i; @@ -35,7 +36,10 @@ import org.geysermc.geyser.level.block.BlockStateValues; import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.skin.SkinProvider; +import java.nio.charset.StandardCharsets; import java.util.LinkedHashMap; +import java.util.Locale; +import java.util.UUID; import java.util.concurrent.CompletableFuture; @BlockEntity(type = BlockEntityType.SKULL) @@ -53,33 +57,54 @@ public class SkullBlockEntityTranslator extends BlockEntityTranslator implements builder.put("SkullType", skullVariant); } - private static CompletableFuture getTextures(CompoundTag tag) { - CompoundTag owner = tag.get("SkullOwner"); - if (owner != null) { - CompoundTag properties = owner.get("Properties"); - if (properties == null) { - return SkinProvider.requestTexturesFromUsername(owner); - } - - ListTag textures = properties.get("textures"); - LinkedHashMap tag1 = (LinkedHashMap) textures.get(0).getValue(); - StringTag texture = (StringTag) tag1.get("Value"); - return CompletableFuture.completedFuture(texture.getValue()); + private static UUID getUUID(CompoundTag owner) { + if (owner.get("Id") instanceof IntArrayTag uuidTag && uuidTag.length() == 4) { + int[] uuidAsArray = uuidTag.getValue(); + // thank u viaversion + return new UUID((long) uuidAsArray[0] << 32 | ((long) uuidAsArray[1] & 0xFFFFFFFFL), + (long) uuidAsArray[2] << 32 | ((long) uuidAsArray[3] & 0xFFFFFFFFL)); } - return CompletableFuture.completedFuture(null); + // Convert username to an offline UUID + String username = null; + if (owner.get("Name") instanceof StringTag nameTag) { + username = nameTag.getValue().toLowerCase(Locale.ROOT); + } + return UUID.nameUUIDFromBytes(("OfflinePlayer:" + username).getBytes(StandardCharsets.UTF_8)); + } + + private static CompletableFuture getTextures(CompoundTag owner, UUID uuid) { + CompoundTag properties = owner.get("Properties"); + if (properties == null) { + if (uuid != null && uuid.version() == 4) { + String uuidString = uuid.toString().replace("-", ""); + return SkinProvider.requestTexturesFromUUID(uuidString); + } else if (owner.get("Name") instanceof StringTag nameTag) { + // Fall back to username if UUID was missing or was an offline mode UUID + return SkinProvider.requestTexturesFromUsername(nameTag.getValue()); + } + return CompletableFuture.completedFuture(null); + } + + ListTag textures = properties.get("textures"); + LinkedHashMap tag1 = (LinkedHashMap) textures.get(0).getValue(); + StringTag texture = (StringTag) tag1.get("Value"); + return CompletableFuture.completedFuture(texture.getValue()); } public static void translateSkull(GeyserSession session, CompoundTag tag, int posX, int posY, int posZ, int blockState) { Vector3i blockPosition = Vector3i.from(posX, posY, posZ); - getTextures(tag).whenComplete((texturesProperty, throwable) -> { - if (texturesProperty == null) { - session.getGeyser().getLogger().debug("Custom skull with invalid SkullOwner tag: " + blockPosition + " " + tag); - return; - } + CompoundTag owner = tag.get("SkullOwner"); + if (owner == null) { + session.getSkullCache().removeSkull(blockPosition); + return; + } + + UUID uuid = getUUID(owner); + getTextures(owner, uuid).whenComplete((texturesProperty, throwable) -> { if (session.getEventLoop().inEventLoop()) { - session.getSkullCache().putSkull(blockPosition, texturesProperty, blockState); + session.getSkullCache().putSkull(blockPosition, uuid, texturesProperty, blockState); } else { - session.executeInEventLoop(() -> session.getSkullCache().putSkull(blockPosition, texturesProperty, blockState)); + session.executeInEventLoop(() -> session.getSkullCache().putSkull(blockPosition, uuid, texturesProperty, blockState)); } }); } From af5d03f5dd69f06255627f57127caecb54b48d35 Mon Sep 17 00:00:00 2001 From: Camotoy <20743703+Camotoy@users.noreply.github.com> Date: Wed, 25 Jan 2023 11:05:04 -0500 Subject: [PATCH 77/81] Show teams in command suggestions --- .../geysermc/geyser/scoreboard/Scoreboard.java | 12 ++++++++++++ .../geysermc/geyser/session/GeyserSession.java | 17 +++++++++++++++++ .../protocol/java/JavaCommandsTranslator.java | 15 ++++++++++++++- gradle/libs.versions.toml | 2 +- 4 files changed, 44 insertions(+), 2 deletions(-) diff --git a/core/src/main/java/org/geysermc/geyser/scoreboard/Scoreboard.java b/core/src/main/java/org/geysermc/geyser/scoreboard/Scoreboard.java index a6e80a375..f26d5846d 100644 --- a/core/src/main/java/org/geysermc/geyser/scoreboard/Scoreboard.java +++ b/core/src/main/java/org/geysermc/geyser/scoreboard/Scoreboard.java @@ -37,6 +37,7 @@ import org.geysermc.geyser.entity.type.Entity; import org.geysermc.geyser.entity.type.player.PlayerEntity; import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.text.GeyserLocale; +import org.jetbrains.annotations.Contract; import javax.annotation.Nullable; import java.util.*; @@ -132,6 +133,10 @@ public final class Scoreboard { team = new Team(this, teamName); team.addEntities(players); teams.put(teamName, team); + + // Update command parameters - is safe to send even if the command enum doesn't exist on the client (as of 1.19.51) + session.addCommandEnum("Geyser_Teams", team.getId()); + return team; } @@ -343,9 +348,16 @@ public final class Scoreboard { // We need to use the direct entities list here, so #refreshSessionPlayerDisplays also updates accordingly // With the player's lack of a team in visibility checks updateEntityNames(remove, remove.getEntities(), true); + + session.removeCommandEnum("Geyser_Teams", remove.getId()); } } + @Contract("-> new") + public String[] getTeamNames() { + return teams.keySet().toArray(new String[0]); + } + /** * Updates the display names of all entities in a given team. * @param teamChange the players have either joined or left the team. Used for optimizations when just the display name updated. 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 b629fa4d4..33655beda 100644 --- a/core/src/main/java/org/geysermc/geyser/session/GeyserSession.java +++ b/core/src/main/java/org/geysermc/geyser/session/GeyserSession.java @@ -67,7 +67,9 @@ import com.nukkitx.nbt.NbtMap; import com.nukkitx.protocol.bedrock.BedrockPacket; import com.nukkitx.protocol.bedrock.BedrockServerSession; import com.nukkitx.protocol.bedrock.data.*; +import com.nukkitx.protocol.bedrock.data.command.CommandEnumData; import com.nukkitx.protocol.bedrock.data.command.CommandPermission; +import com.nukkitx.protocol.bedrock.data.command.SoftEnumUpdateType; import com.nukkitx.protocol.bedrock.data.entity.EntityFlag; import com.nukkitx.protocol.bedrock.packet.*; import io.netty.channel.Channel; @@ -1895,4 +1897,19 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource { sendUpstreamPacket(transferPacket); return true; } + + public void addCommandEnum(String name, String... enums) { + softEnumPacket(name, SoftEnumUpdateType.ADD, enums); + } + + public void removeCommandEnum(String name, String... enums) { + softEnumPacket(name, SoftEnumUpdateType.REMOVE, enums); + } + + private void softEnumPacket(String name, SoftEnumUpdateType type, String... enums) { + UpdateSoftEnumPacket packet = new UpdateSoftEnumPacket(); + packet.setType(type); + packet.setSoftEnum(new CommandEnumData(name, enums, true)); + sendUpstreamPacket(packet); + } } 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 11311b63c..bd0fab85f 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 @@ -240,6 +240,7 @@ public class JavaCommandsTranslator extends PacketTranslator handleResource(context, ((ResourceProperties) node.getProperties()).getRegistryKey(), false); case RESOURCE_OR_TAG -> handleResource(context, ((ResourceProperties) node.getProperties()).getRegistryKey(), true); case DIMENSION -> context.session.getLevels(); + case TEAM -> context.getTeams(); // Note: as of Java 1.19.3, objectives are currently parsed from the server default -> CommandParam.STRING; }; } @@ -271,6 +272,7 @@ public class JavaCommandsTranslator extends PacketTranslator Date: Sun, 29 Jan 2023 22:15:26 -0500 Subject: [PATCH 78/81] Fix /geyser reload on Spigot Fixes #3478 --- .../geysermc/geyser/platform/spigot/GeyserSpigotPlugin.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/bootstrap/spigot/src/main/java/org/geysermc/geyser/platform/spigot/GeyserSpigotPlugin.java b/bootstrap/spigot/src/main/java/org/geysermc/geyser/platform/spigot/GeyserSpigotPlugin.java index 5f0061382..1be2eb32a 100644 --- a/bootstrap/spigot/src/main/java/org/geysermc/geyser/platform/spigot/GeyserSpigotPlugin.java +++ b/bootstrap/spigot/src/main/java/org/geysermc/geyser/platform/spigot/GeyserSpigotPlugin.java @@ -195,6 +195,9 @@ public class GeyserSpigotPlugin extends JavaPlugin implements GeyserBootstrap { geyserConfig.loadFloodgate(this); + this.geyserCommandManager = new GeyserSpigotCommandManager(geyser); + this.geyserCommandManager.init(); + if (!INITIALIZED) { // Needs to be an anonymous inner class otherwise Bukkit complains about missing classes Bukkit.getPluginManager().registerEvents(new Listener() { @@ -206,9 +209,6 @@ public class GeyserSpigotPlugin extends JavaPlugin implements GeyserBootstrap { } }, this); - this.geyserCommandManager = new GeyserSpigotCommandManager(geyser); - this.geyserCommandManager.init(); - // Because Bukkit locks its command map upon startup, we need to // add our plugin commands in onEnable, but populating the executor // can happen at any time From c909b2b1a52ee617961b56cd8677f2f5fa1a1969 Mon Sep 17 00:00:00 2001 From: Camotoy <20743703+Camotoy@users.noreply.github.com> Date: Tue, 31 Jan 2023 13:01:20 -0500 Subject: [PATCH 79/81] Fix #3521 --- .../player/BedrockMovePlayerTranslator.java | 55 +++++++++++-------- 1 file changed, 32 insertions(+), 23 deletions(-) diff --git a/core/src/main/java/org/geysermc/geyser/translator/protocol/bedrock/entity/player/BedrockMovePlayerTranslator.java b/core/src/main/java/org/geysermc/geyser/translator/protocol/bedrock/entity/player/BedrockMovePlayerTranslator.java index 6078b7ebd..c6f42c48c 100644 --- a/core/src/main/java/org/geysermc/geyser/translator/protocol/bedrock/entity/player/BedrockMovePlayerTranslator.java +++ b/core/src/main/java/org/geysermc/geyser/translator/protocol/bedrock/entity/player/BedrockMovePlayerTranslator.java @@ -92,11 +92,29 @@ public class BedrockMovePlayerTranslator extends PacketTranslator= packet.getPosition().getY()) { + int floorY = position.getFloorY(); + // The void floor is offset about 40 blocks below the bottom of the world + BedrockDimension bedrockDimension = session.getChunkCache().getBedrockDimension(); + int voidFloorLocation = bedrockDimension.minY() - 40; + teleportThroughVoidFloor = floorY <= (voidFloorLocation + 2) && floorY >= voidFloorLocation; + if (teleportThroughVoidFloor) { + // https://github.com/GeyserMC/Geyser/issues/3521 - no void floor in Java so we cannot be on the ground. + onGround = false; + } + } else { + teleportThroughVoidFloor = false; + } + Packet movePacket; if (rotationChanged) { // Send rotation updates as well movePacket = new ServerboundMovePlayerPosRotPacket( - packet.isOnGround(), + onGround, position.getX(), position.getY(), position.getZ(), yaw, pitch ); @@ -105,35 +123,26 @@ public class BedrockMovePlayerTranslator extends PacketTranslator= packet.getPosition().getY(); - entity.setPositionManual(packet.getPosition()); - entity.setOnGround(packet.isOnGround()); + entity.setOnGround(onGround); // Send final movement changes session.sendDownstreamPacket(movePacket); - if (notMovingUp) { - int floorY = position.getFloorY(); - // The void floor is offset about 40 blocks below the bottom of the world - BedrockDimension bedrockDimension = session.getChunkCache().getBedrockDimension(); - int voidFloorLocation = bedrockDimension.minY() - 40; - if (floorY <= (voidFloorLocation + 2) && floorY >= voidFloorLocation) { - // Work around there being a floor at the bottom of the world and teleport the player below it - // Moving from below to above the void floor works fine - entity.setPosition(entity.getPosition().sub(0, 4f, 0)); - MovePlayerPacket movePlayerPacket = new MovePlayerPacket(); - movePlayerPacket.setRuntimeEntityId(entity.getGeyserId()); - movePlayerPacket.setPosition(entity.getPosition()); - movePlayerPacket.setRotation(entity.getBedrockRotation()); - movePlayerPacket.setMode(MovePlayerPacket.Mode.TELEPORT); - movePlayerPacket.setTeleportationCause(MovePlayerPacket.TeleportationCause.BEHAVIOR); - session.sendUpstreamPacket(movePlayerPacket); - } + if (teleportThroughVoidFloor) { + // Work around there being a floor at the bottom of the world and teleport the player below it + // Moving from below to above the void floor works fine + entity.setPosition(entity.getPosition().sub(0, 4f, 0)); + MovePlayerPacket movePlayerPacket = new MovePlayerPacket(); + movePlayerPacket.setRuntimeEntityId(entity.getGeyserId()); + movePlayerPacket.setPosition(entity.getPosition()); + movePlayerPacket.setRotation(entity.getBedrockRotation()); + movePlayerPacket.setMode(MovePlayerPacket.Mode.TELEPORT); + movePlayerPacket.setTeleportationCause(MovePlayerPacket.TeleportationCause.BEHAVIOR); + session.sendUpstreamPacket(movePlayerPacket); } session.getSkullCache().updateVisibleSkulls(); From 0388785ea7d37cbdd5f4af7e02363c040d2026e9 Mon Sep 17 00:00:00 2001 From: Camotoy <20743703+Camotoy@users.noreply.github.com> Date: Thu, 2 Feb 2023 15:25:05 -0500 Subject: [PATCH 80/81] Fix some instances of team-applied nametags not working Fixes #3531 --- .../entity/type/player/PlayerEntity.java | 35 ++++++------------- .../geyser/scoreboard/Scoreboard.java | 21 ++++++----- .../org/geysermc/geyser/scoreboard/Team.java | 2 ++ 3 files changed, 25 insertions(+), 33 deletions(-) diff --git a/core/src/main/java/org/geysermc/geyser/entity/type/player/PlayerEntity.java b/core/src/main/java/org/geysermc/geyser/entity/type/player/PlayerEntity.java index 3e3a298bd..3501eb296 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 @@ -100,6 +100,7 @@ public class PlayerEntity extends LivingEntity { super(session, entityId, geyserId, uuid, EntityDefinitions.PLAYER, position, motion, yaw, pitch, headYaw); this.username = username; + this.nametag = username; this.texturesProperty = texturesProperty; } @@ -119,7 +120,7 @@ public class PlayerEntity extends LivingEntity { } // The name can't be updated later (the entity metadata for it is ignored), so we need to check for this now - updateDisplayName(null, false); + updateDisplayName(session.getWorldCache().getScoreboard().getTeamFor(username)); AddPlayerPacket addPlayerPacket = new AddPlayerPacket(); addPlayerPacket.setUuid(uuid); @@ -315,19 +316,10 @@ public class PlayerEntity extends LivingEntity { } //todo this will become common entity logic once UUID support is implemented for them - /** - * @param useGivenTeam even if there is no team, update the username in the entity metadata anyway, and don't look for a team - */ - public void updateDisplayName(@Nullable Team team, boolean useGivenTeam) { - if (team == null && !useGivenTeam) { - // Only search for the team if we are not supposed to use the given team - // If the given team is null, this is intentional that we are being removed from the team - team = session.getWorldCache().getScoreboard().getTeamFor(username); - } - + public void updateDisplayName(@Nullable Team team) { boolean needsUpdate; - String newDisplayName = this.username; if (team != null) { + String newDisplayName; if (team.isVisibleFor(session.getPlayerEntity().getUsername())) { TeamColor color = team.getColor(); String chatColor = MessageTranslator.toChatColor(color); @@ -339,23 +331,16 @@ public class PlayerEntity extends LivingEntity { // The name is not visible to the session player; clear name newDisplayName = ""; } - needsUpdate = useGivenTeam && !newDisplayName.equals(nametag); - nametag = newDisplayName; - dirtyMetadata.put(EntityData.NAMETAG, newDisplayName); - } else if (useGivenTeam) { - // The name has reset, if it was previously something else - needsUpdate = !newDisplayName.equals(nametag); - dirtyMetadata.put(EntityData.NAMETAG, this.username); + needsUpdate = !newDisplayName.equals(this.nametag); + this.nametag = newDisplayName; } else { - needsUpdate = false; + // The name has reset, if it was previously something else + needsUpdate = !this.nametag.equals(this.username); + this.nametag = this.username; } if (needsUpdate) { - // Update the metadata as it won't be updated later - SetEntityDataPacket packet = new SetEntityDataPacket(); - packet.getMetadata().put(EntityData.NAMETAG, newDisplayName); - packet.setRuntimeEntityId(geyserId); - session.sendUpstreamPacket(packet); + dirtyMetadata.put(EntityData.NAMETAG, this.nametag); } } diff --git a/core/src/main/java/org/geysermc/geyser/scoreboard/Scoreboard.java b/core/src/main/java/org/geysermc/geyser/scoreboard/Scoreboard.java index f26d5846d..56e1e67f8 100644 --- a/core/src/main/java/org/geysermc/geyser/scoreboard/Scoreboard.java +++ b/core/src/main/java/org/geysermc/geyser/scoreboard/Scoreboard.java @@ -30,6 +30,7 @@ import com.nukkitx.protocol.bedrock.data.ScoreInfo; import com.nukkitx.protocol.bedrock.packet.RemoveObjectivePacket; import com.nukkitx.protocol.bedrock.packet.SetDisplayObjectivePacket; import com.nukkitx.protocol.bedrock.packet.SetScorePacket; +import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap; import lombok.Getter; import org.geysermc.geyser.GeyserImpl; import org.geysermc.geyser.GeyserLogger; @@ -56,6 +57,13 @@ public final class Scoreboard { @Getter private final Map objectiveSlots = new EnumMap<>(ScoreboardPosition.class); private final Map teams = new ConcurrentHashMap<>(); // updated on multiple threads + /** + * Required to preserve vanilla behavior, which also uses a map. + * Otherwise, for example, if TAB has a team for a player and vanilla has a team, "race conditions" that do not + * match vanilla could occur. + */ + @Getter + private final Map playerToTeam = new Object2ObjectOpenHashMap<>(); private int lastAddScoreCount = 0; private int lastRemoveScoreCount = 0; @@ -333,12 +341,7 @@ public final class Scoreboard { } public Team getTeamFor(String entity) { - for (Team team : teams.values()) { - if (team.hasEntity(entity)) { - return team; - } - } - return null; + return playerToTeam.get(entity); } public void removeTeam(String teamName) { @@ -380,7 +383,8 @@ public final class Scoreboard { for (Entity entity : session.getEntityCache().getEntities().values()) { // This more complex logic is for the future to iterate over all entities, not just players if (entity instanceof PlayerEntity player && names.remove(player.getUsername())) { - player.updateDisplayName(team, true); + player.updateDisplayName(team); + player.updateBedrockMetadata(); if (names.isEmpty()) { break; } @@ -396,7 +400,8 @@ public final class Scoreboard { for (Entity entity : session.getEntityCache().getEntities().values()) { if (entity instanceof PlayerEntity player) { Team playerTeam = session.getWorldCache().getScoreboard().getTeamFor(player.getUsername()); - player.updateDisplayName(playerTeam, true); + player.updateDisplayName(playerTeam); + player.updateBedrockMetadata(); } } } 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 d7840627f..34db4a048 100644 --- a/core/src/main/java/org/geysermc/geyser/scoreboard/Team.java +++ b/core/src/main/java/org/geysermc/geyser/scoreboard/Team.java @@ -65,6 +65,7 @@ public final class Team { if (entities.add(name)) { added.add(name); } + scoreboard.getPlayerToTeam().put(name, this); } if (added.isEmpty()) { @@ -93,6 +94,7 @@ public final class Team { if (entities.remove(name)) { removed.add(name); } + scoreboard.getPlayerToTeam().remove(name, this); } return removed; } From 25c2d30881ec2a0c7abf39be950db503303c2b03 Mon Sep 17 00:00:00 2001 From: Camotoy <20743703+Camotoy@users.noreply.github.com> Date: Thu, 2 Feb 2023 16:31:26 -0500 Subject: [PATCH 81/81] Remove players from player team map on team remove --- .../main/java/org/geysermc/geyser/scoreboard/Scoreboard.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/core/src/main/java/org/geysermc/geyser/scoreboard/Scoreboard.java b/core/src/main/java/org/geysermc/geyser/scoreboard/Scoreboard.java index 56e1e67f8..f97693a62 100644 --- a/core/src/main/java/org/geysermc/geyser/scoreboard/Scoreboard.java +++ b/core/src/main/java/org/geysermc/geyser/scoreboard/Scoreboard.java @@ -351,6 +351,9 @@ public final class Scoreboard { // We need to use the direct entities list here, so #refreshSessionPlayerDisplays also updates accordingly // With the player's lack of a team in visibility checks updateEntityNames(remove, remove.getEntities(), true); + for (String name : remove.getEntities()) { + playerToTeam.remove(name, remove); + } session.removeCommandEnum("Geyser_Teams", remove.getId()); }