diff --git a/README.md b/README.md index a28ba8eb0..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.0 - 1.19.40 and Minecraft Java 1.19.1/1.19.2. +### 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/api/geyser/build.gradle.kts b/api/geyser/build.gradle.kts deleted file mode 100644 index 27d21dfb9..000000000 --- a/api/geyser/build.gradle.kts +++ /dev/null @@ -1,14 +0,0 @@ -plugins { - id("geyser.api-conventions") -} - -dependencies { - compileOnly(libs.baseApi) -} - -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/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/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/api/geyser/src/main/java/org/geysermc/geyser/api/network/AuthType.java b/bootstrap/bungeecord/src/main/java/org/geysermc/geyser/platform/bungeecord/GeyserBungeeCompressionDisabler.java similarity index 52% rename from api/geyser/src/main/java/org/geysermc/geyser/api/network/AuthType.java rename to bootstrap/bungeecord/src/main/java/org/geysermc/geyser/platform/bungeecord/GeyserBungeeCompressionDisabler.java index 3176f3384..084e1d2dc 100644 --- a/api/geyser/src/main/java/org/geysermc/geyser/api/network/AuthType.java +++ b/bootstrap/bungeecord/src/main/java/org/geysermc/geyser/platform/bungeecord/GeyserBungeeCompressionDisabler.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019-2022 GeyserMC. http://geysermc.org + * 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 @@ -23,39 +23,30 @@ * @link https://github.com/GeyserMC/Geyser */ -package org.geysermc.geyser.api.network; +package org.geysermc.geyser.platform.bungeecord; -import java.util.Locale; +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; -/** - * 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; +public class GeyserBungeeCompressionDisabler extends ChannelOutboundHandlerAdapter { - 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; + @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); } - return ONLINE; } -} \ No newline at end of file +} 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 cef430bd6..e10b3ce6f 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) @@ -163,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/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 a98c45d68..6e4907c07 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 @@ -81,7 +81,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(" / \\"); 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/GeyserSpigotCompressionDisabler.java b/bootstrap/spigot/src/main/java/org/geysermc/geyser/platform/spigot/GeyserSpigotCompressionDisabler.java new file mode 100644 index 000000000..9b112f62f --- /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.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.geyser.GeyserImpl; + +/** + * 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 { + 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) { + GeyserImpl.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 77dfaf4d4..5f78c5d16 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 @@ -117,10 +117,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()); + } + if (GeyserImpl.getInstance().getConfig().getRemote().authType() == AuthType.FLOODGATE) { // we have to add the packet blocker in the data handler, otherwise ProtocolSupport breaks ch.pipeline().addBefore( 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 9c6c5b25c..0f6e7a771 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 @@ -187,6 +187,9 @@ public class GeyserSpigotPlugin extends JavaPlugin implements GeyserBootstrap { geyserLogger.severe("WHY DO YOU HAVE FLOODGATE INSTALLED!!!!!!! REMOVE IT!!!!"); } + 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() { @@ -198,9 +201,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 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/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..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,33 +25,40 @@ 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; 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; 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 */ -public class GeyserSpigotWorldManager extends GeyserWorldManager { +public class GeyserSpigotWorldManager extends WorldManager { private final Plugin plugin; public GeyserSpigotWorldManager(Plugin plugin) { @@ -151,12 +158,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 +172,7 @@ public class GeyserSpigotWorldManager extends GeyserWorldManager { if (!value.isEmpty()) { return Integer.parseInt(value); } - return (int) gameRule.getDefaultValue(); + return gameRule.getDefaultIntValue(); } @Override @@ -173,6 +180,46 @@ public class GeyserSpigotWorldManager extends GeyserWorldManager { 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/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..e787e7355 --- /dev/null +++ b/bootstrap/velocity/src/main/java/org/geysermc/geyser/platform/velocity/GeyserVelocityCompressionDisabler.java @@ -0,0 +1,99 @@ +/* + * 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.geyser.platform.velocity; + +import io.netty.channel.ChannelDuplexHandler; +import io.netty.channel.ChannelHandlerContext; +import io.netty.channel.ChannelPromise; +import org.geysermc.geyser.GeyserImpl; + +import java.lang.reflect.Method; + +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; + } catch (Exception e) { + GeyserImpl.getInstance().getLogger().error("Could not initialize compression disabler!", e); + } + + ENABLED = enabled; + COMPRESSION_PACKET_CLASS = compressionPacketClass; + LOGIN_SUCCESS_PACKET_CLASS = loginSuccessPacketClass; + COMPRESSION_ENABLED_EVENT = compressionEnabledEvent; + 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(); + if (!COMPRESSION_PACKET_CLASS.isAssignableFrom(msgClass)) { + if (LOGIN_SUCCESS_PACKET_CLASS.isAssignableFrom(msgClass)) { + // We're past the point that compression can be enabled + + 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 + } +} 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/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/build.gradle.kts b/build.gradle.kts index 626f34224..4ad2ee911 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -5,7 +5,7 @@ plugins { } allprojects { - group = "org.geysermc" + group = "org.geysermc.geyser" version = "3.0.0-SNAPSHOT" description = "Allows for players from Minecraft: Bedrock Edition to join Minecraft: Java Edition servers." @@ -30,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") FIXME - 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 814329909..74922934b 100644 --- a/core/build.gradle.kts +++ b/core/build.gradle.kts @@ -8,7 +8,7 @@ plugins { dependencies { api("org.geysermc.floodgate", "core", "2.2.0-SNAPSHOT") - api(projects.geyserApi) + api(libs.geyser.api) // Jackson JSON and YAML serialization api(libs.bundles.jackson) 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 a588581cd..5f94030c0 100644 --- a/core/src/main/java/org/geysermc/geyser/GeyserImpl.java +++ b/core/src/main/java/org/geysermc/geyser/GeyserImpl.java @@ -76,6 +76,7 @@ import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.session.PendingMicrosoftAuthentication; import org.geysermc.geyser.session.SessionManager; import org.geysermc.geyser.skin.BedrockSkinUploader; +import org.geysermc.geyser.skin.ProvidedSkins; import org.geysermc.geyser.skin.SkinProvider; import org.geysermc.geyser.text.GeyserLocale; import org.geysermc.geyser.text.MinecraftLocale; @@ -91,6 +92,7 @@ import java.net.InetSocketAddress; import java.net.UnknownHostException; 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; @@ -199,7 +201,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(); @@ -227,7 +245,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); } 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 2288a8a3d..1c8c7f65f 100644 --- a/core/src/main/java/org/geysermc/geyser/configuration/GeyserConfiguration.java +++ b/core/src/main/java/org/geysermc/geyser/configuration/GeyserConfiguration.java @@ -113,6 +113,8 @@ public interface GeyserConfiguration { boolean isNotifyOnNewBedrockUpdate(); + String getUnusableSpaceBlock(); + IMetricsInfo getMetrics(); int getPendingAuthenticationTimeout(); @@ -189,6 +191,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 af1d952fe..6f627962c 100644 --- a/core/src/main/java/org/geysermc/geyser/configuration/GeyserJacksonConfiguration.java +++ b/core/src/main/java/org/geysermc/geyser/configuration/GeyserJacksonConfiguration.java @@ -166,6 +166,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") @@ -344,6 +347,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/java/org/geysermc/geyser/entity/EntityDefinitions.java b/core/src/main/java/org/geysermc/geyser/entity/EntityDefinitions.java index a552d0875..b97e23847 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; @@ -859,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) 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); 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..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 @@ -355,6 +355,7 @@ public class Entity { 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); 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..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 @@ -40,6 +40,13 @@ public class AgeableEntity extends CreatureEntity { super(session, entityId, geyserId, uuid, definition, position, motion, yaw, pitch, headYaw); } + @Override + protected void initializeMetadata() { + super.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(); 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/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); 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..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 @@ -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 = session.getBlockMappings().getBedrockBlockId(entityMetadata.getValue().getAsInt()); + } else { + bedrockBlockId = session.getBlockMappings().getBedrockAirId(); + } + + dirtyMetadata.put(EntityData.CARRIED_BLOCK, bedrockBlockId); } /** 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..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 @@ -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. @@ -101,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; } @@ -120,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); @@ -316,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); @@ -340,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); } } @@ -417,4 +401,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..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,9 +49,14 @@ 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); - setPlayerList(false); } @Override @@ -103,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/hybrid/IntegratedHybridProvider.java b/core/src/main/java/org/geysermc/geyser/hybrid/IntegratedHybridProvider.java index b31bfd944..6b4dff7a5 100644 --- a/core/src/main/java/org/geysermc/geyser/hybrid/IntegratedHybridProvider.java +++ b/core/src/main/java/org/geysermc/geyser/hybrid/IntegratedHybridProvider.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019-2022 GeyserMC. http://geysermc.org + * 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 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/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; 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..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; @@ -39,6 +38,7 @@ 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; @@ -63,14 +63,14 @@ 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)); } } @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 +83,16 @@ public class BlockInventoryHolder extends InventoryHolder { inventory.setHolderPosition(session.getLastInteractionBlockPosition()); ((Container) inventory).setUsingRealBlock(true, javaBlockString[0]); setCustomName(session, session.getLastInteractionBlockPosition(), inventory, javaBlockId); - return; + + return true; } } - // Otherwise, time to conjure up a fake block! - Vector3i position = session.getPlayerEntity().getPosition().toInt(); - position = position.add(Vector3i.UP); + Vector3i position = InventoryUtils.findAvailableWorldSpace(session); + if (position == null) { + return false; + } + UpdateBlockPacket blockPacket = new UpdateBlockPacket(); blockPacket.setDataLayer(0); blockPacket.setBlockPosition(position); @@ -99,6 +102,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/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..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,9 +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.Nonnull; +import java.util.Locale; +import java.util.concurrent.CompletableFuture; /** * Class that manages or retrieves various information @@ -105,7 +111,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 +122,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 +139,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 +149,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 @@ -149,4 +161,22 @@ 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; + } + + /** + * 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/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()) { 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..6b46f8056 100644 --- a/core/src/main/java/org/geysermc/geyser/network/GameProtocol.java +++ b/core/src/main/java/org/geysermc/geyser/network/GameProtocol.java @@ -28,12 +28,11 @@ 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; 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 +47,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,12 +60,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()); SUPPORTED_BEDROCK_CODECS.add(Bedrock_v544.V544_CODEC); SUPPORTED_BEDROCK_CODECS.add(Bedrock_v545.V545_CODEC.toBuilder() .minecraftVersion("1.19.21/1.19.22") @@ -74,7 +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(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()); } /** @@ -93,14 +91,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. * @@ -116,7 +114,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()); } /** 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); + } } 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()); 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/CustomItemRegistryPopulator.java b/core/src/main/java/org/geysermc/geyser/registry/populator/CustomItemRegistryPopulator.java index 94e04e972..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 @@ -124,6 +124,33 @@ public class CustomItemRegistryPopulator { computeArmorProperties(mapping.getArmorType(), mapping.getProtectionValue(), componentBuilder); } + if (mapping.getFirstBlockRuntimeId() != null) { + 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()); @@ -260,6 +287,48 @@ 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 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/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/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 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/java/org/geysermc/geyser/scoreboard/Scoreboard.java b/core/src/main/java/org/geysermc/geyser/scoreboard/Scoreboard.java index a6e80a375..f97693a62 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; @@ -37,6 +38,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.*; @@ -55,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; @@ -132,6 +141,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; } @@ -328,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) { @@ -343,9 +351,19 @@ 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()); } } + @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. @@ -368,7 +386,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; } @@ -384,7 +403,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; } 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 f9e6ede3e..79a5110f8 100644 --- a/core/src/main/java/org/geysermc/geyser/session/GeyserSession.java +++ b/core/src/main/java/org/geysermc/geyser/session/GeyserSession.java @@ -62,18 +62,18 @@ 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; 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; 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; @@ -120,7 +120,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; @@ -296,6 +295,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; @@ -452,9 +456,8 @@ 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. */ - @Getter(AccessLevel.NONE) private int armAnimationTicks = -1; /** @@ -534,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) { @@ -627,6 +636,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); @@ -1063,6 +1078,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. */ @@ -1133,7 +1159,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) @@ -1172,7 +1198,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(); } @@ -1304,6 +1330,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. */ @@ -1357,18 +1393,21 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource { * 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) { - renderDistance = GenericMath.ceil(++renderDistance * MathUtils.SQRT_OF_TWO); //square to circle + // +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(); @@ -1420,7 +1459,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); @@ -1622,76 +1661,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) { @@ -1701,20 +1704,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() { @@ -1894,4 +1898,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/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/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/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/skin/FakeHeadProvider.java b/core/src/main/java/org/geysermc/geyser/skin/FakeHeadProvider.java index 6794af498..b312f9811 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; @@ -68,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( @@ -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/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 730d46908..48233fe3d 100644 --- a/core/src/main/java/org/geysermc/geyser/skin/SkinManager.java +++ b/core/src/main/java/org/geysermc/geyser/skin/SkinManager.java @@ -32,9 +32,10 @@ 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; +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; @@ -53,13 +54,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.getUuid()); + if (skin == null) { + skin = fallbackSkinData.skin(); + geometry = fallbackSkinData.geometry(); + } + if (cape == null) { + cape = fallbackSkinData.cape(); + } } return buildEntryManually( @@ -67,10 +85,8 @@ public class SkinManager { playerEntity.getUuid(), playerEntity.getUsername(), playerEntity.getGeyserId(), - skin.getTextureUrl(), - skin.getSkinData(), - cape.getCapeId(), - cape.getCapeData(), + skin, + cape, geometry ); } @@ -79,14 +95,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 +128,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.geometryName(), + ImageData.of(skin.getSkinData()), Collections.emptyList(), + ImageData.of(cape.capeData()), geometry.geometryData(), + "", true, false, false, cape.capeId(), skinId); + } + public static void requestAndHandleSkinAndCape(PlayerEntity entity, GeyserSession session, Consumer skinAndCapeConsumer) { SkinProvider.requestSkinData(entity).whenCompleteAsync((skinData, throwable) -> { @@ -128,34 +179,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) { @@ -186,7 +210,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); @@ -231,30 +255,29 @@ public class SkinManager { * @param entity entity to build the GameProfileData from * @return The built GameProfileData */ - public static GameProfileData from(PlayerEntity entity) { - try { - String texturesProperty = entity.getTexturesProperty(); + public static @Nullable GameProfileData from(PlayerEntity entity) { + String texturesProperty = entity.getTexturesProperty(); + if (texturesProperty == null) { + // Likely offline mode + return null; + } - if (texturesProperty == null) { - // Likely offline mode - return loadBedrockOrOfflineSkin(entity); - } - GameProfileData data = loadFromJson(texturesProperty); - if (data != null) { - return data; + try { + return loadFromJson(texturesProperty); + } 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 { - return loadBedrockOrOfflineSkin(entity); + GeyserImpl.getInstance().getLogger().debug("Something went wrong while processing skin for " + entity.getUsername() + " with Value: " + 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; } - 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"); @@ -267,38 +290,25 @@ 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://"); - } - - 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(); + 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 43cf30b47..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,22 +26,22 @@ 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 +57,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 +86,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 +159,103 @@ 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(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. + 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(uuid); + 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.getUuid())); + } 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 +286,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 +305,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 +313,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 +323,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 +351,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 +375,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 +383,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 +406,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 +424,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 +445,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 +454,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 +468,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) { @@ -539,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(); } @@ -589,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); @@ -604,7 +664,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 +743,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 +762,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 +782,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 +792,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..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 { @@ -42,34 +43,43 @@ 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 ); } - 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/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/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/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/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); 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..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 @@ -40,6 +40,7 @@ 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.util.InventoryUtils; 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,16 @@ public class DoubleChestInventoryTranslator extends ChestInventoryTranslator { dataPacket.setData(tag.build()); dataPacket.setBlockPosition(session.getLastInteractionBlockPosition()); session.sendUpstreamPacket(dataPacket); - return; + + return true; } } - Vector3i position = session.getPlayerEntity().getPosition().toInt().add(Vector3i.UP); + Vector3i position = InventoryUtils.findAvailableWorldSpace(session); + if (position == null) { + return false; + } + Vector3i pairPosition = position.add(Vector3i.UNIT_X); int bedrockBlockId = session.getBlockMappings().getBedrockBlockId(defaultJavaBlockState); @@ -125,6 +131,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/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/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()))); 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 1163a0ae9..ff3d9fe71 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 @@ -41,6 +41,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()) 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/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)); } }); } 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); + } } } 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..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,15 +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(() -> { - session.sendDownstreamPacket(new ServerboundSwingPacket(Hand.MAIN_HAND)); - session.activateArmAnimationTicking(); + 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/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 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/BedrockCommandRequestTranslator.java b/core/src/main/java/org/geysermc/geyser/translator/protocol/bedrock/BedrockCommandRequestTranslator.java index ee42c674a..d5cbd0216 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 @@ -28,6 +28,7 @@ package org.geysermc.geyser.translator.protocol.bedrock; import com.nukkitx.protocol.bedrock.packet.CommandRequestPacket; 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! @@ -62,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/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/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(); 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 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(); @@ -198,7 +207,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 +220,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 ITEM_ENCHANTMENT -> Enchantment.JavaEnchantment.ALL_JAVA_IDENTIFIERS; - case ENTITY_SUMMON -> Registries.JAVA_ENTITY_IDENTIFIERS.get().keySet().toArray(new String[0]); + case BLOCK_STATE -> context.getBlockStates(); + case ITEM_STACK -> context.session.getItemMappings().getItemNames(); 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; - } - } + 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(); + case TEAM -> context.getTeams(); // Note: as of Java 1.19.3, objectives are currently parsed from the server + 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 +267,67 @@ public class JavaCommandsTranslator extends PacketTranslator 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/JavaDisguisedChatTranslator.java similarity index 62% rename from core/src/main/java/org/geysermc/geyser/translator/protocol/java/level/JavaCustomSoundTranslator.java rename to core/src/main/java/org/geysermc/geyser/translator/protocol/java/JavaDisguisedChatTranslator.java index 00894bd8b..2ad45fe52 100644 --- 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/JavaDisguisedChatTranslator.java @@ -23,27 +23,19 @@ * @link https://github.com/GeyserMC/Geyser */ -package org.geysermc.geyser.translator.protocol.java.level; +package org.geysermc.geyser.translator.protocol.java; -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 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.util.SoundUtils; +import org.geysermc.geyser.translator.text.MessageTranslator; -@Translator(packet = ClientboundCustomSoundPacket.class) -public class JavaCustomSoundTranslator extends PacketTranslator { +@Translator(packet = ClientboundDisguisedChatPacket.class) +public class JavaDisguisedChatTranslator 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); + 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/JavaLoginTranslator.java b/core/src/main/java/org/geysermc/geyser/translator/protocol/java/JavaLoginTranslator.java index 905d2683d..b7a05f771 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 @@ -86,6 +86,7 @@ public class JavaLoginTranslator 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 ? packet.getMessageDecorated() : 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); + Component message = packet.getUnsignedContent() == null ? Component.text(packet.getContent()) : packet.getUnsignedContent(); + MessageTranslator.handleChatPacket(session, message, packet.getChatType(), packet.getTargetName(), packet.getName()); } } 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 ed429b87d..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 @@ -36,6 +36,7 @@ import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.translator.inventory.InventoryTranslator; import org.geysermc.geyser.translator.protocol.PacketTranslator; import org.geysermc.geyser.translator.protocol.Translator; +import org.geysermc.geyser.util.ChunkUtils; import org.geysermc.geyser.util.DimensionUtils; @Translator(packet = ClientboundRespawnPacket.class) @@ -82,16 +83,15 @@ public class JavaRespawnTranslator extends PacketTranslator { -/** - * 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); + 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/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 { + @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); + UUID removeId; + 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. + removeId = entity.getTabListUuid(); + } else { + removeId = id; + } + translate.getEntries().add(new PlayerListPacket.Entry(removeId)); + } + + 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..2784b1cc4 --- /dev/null +++ b/core/src/main/java/org/geysermc/geyser/translator/protocol/java/entity/player/JavaPlayerInfoUpdateTranslator.java @@ -0,0 +1,124 @@ +/* + * 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; + +import java.util.ArrayList; +import java.util.List; +import java.util.Set; + +@Translator(packet = ClientboundPlayerInfoUpdatePacket.class) +public class JavaPlayerInfoUpdateTranslator extends PacketTranslator { + @Override + public void translate(GeyserSession session, ClientboundPlayerInfoUpdatePacket packet) { + Set actions = packet.getActions(); + + 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()); + + GameProfile.Property textures = profile.getProperty("textures"); + String texturesProperty = textures == null ? null : textures.getValue(); + + 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 + ); + + session.getEntityCache().addPlayerEntity(playerEntity); + } + playerEntity.setUsername(profile.getName()); + playerEntity.setTexturesProperty(texturesProperty); + + 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 (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); + } + } + } +} 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..5f182805e 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; @@ -85,7 +84,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()); @@ -97,7 +96,7 @@ 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 diff --git a/core/src/main/java/org/geysermc/geyser/translator/protocol/java/entity/spawn/JavaAddEntityTranslator.java b/core/src/main/java/org/geysermc/geyser/translator/protocol/java/entity/spawn/JavaAddEntityTranslator.java index 49cca79d2..1a6d98575 100644 --- a/core/src/main/java/org/geysermc/geyser/translator/protocol/java/entity/spawn/JavaAddEntityTranslator.java +++ b/core/src/main/java/org/geysermc/geyser/translator/protocol/java/entity/spawn/JavaAddEntityTranslator.java @@ -36,7 +36,6 @@ import org.geysermc.geyser.entity.type.*; import org.geysermc.geyser.entity.type.player.PlayerEntity; import org.geysermc.geyser.registry.Registries; import org.geysermc.geyser.session.GeyserSession; -import org.geysermc.geyser.text.GeyserLocale; import org.geysermc.geyser.translator.protocol.PacketTranslator; import org.geysermc.geyser.translator.protocol.Translator; @@ -53,7 +52,7 @@ public class JavaAddEntityTranslator 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/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 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 * 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/java/org/geysermc/geyser/util/ChunkUtils.java b/core/src/main/java/org/geysermc/geyser/util/ChunkUtils.java index 679d7a658..501087f78 100644 --- a/core/src/main/java/org/geysermc/geyser/util/ChunkUtils.java +++ b/core/src/main/java/org/geysermc/geyser/util/ChunkUtils.java @@ -222,7 +222,8 @@ public class ChunkUtils { * This must be done after the player has switched dimensions so we know what their dimension is */ public static void loadDimension(GeyserSession session) { - JavaDimension dimension = session.getDimensionType(); + JavaDimension dimension = session.getDimensions().get(session.getDimension()); + session.setDimensionType(dimension); int minY = dimension.minY(); int maxY = dimension.maxY(); @@ -233,13 +234,7 @@ public class ChunkUtils { throw new RuntimeException("Maximum Y must be a multiple of 16!"); } - BedrockDimension bedrockDimension = switch (session.getDimension()) { - case DimensionUtils.THE_END -> 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..6b7296c12 100644 --- a/core/src/main/java/org/geysermc/geyser/util/DimensionUtils.java +++ b/core/src/main/java/org/geysermc/geyser/util/DimensionUtils.java @@ -27,11 +27,16 @@ package org.geysermc.geyser.util; import com.github.steveice10.mc.protocol.data.game.entity.Effect; import com.nukkitx.math.vector.Vector3f; +import com.nukkitx.math.vector.Vector3i; +import com.nukkitx.protocol.bedrock.data.PlayerActionType; import com.nukkitx.protocol.bedrock.packet.ChangeDimensionPacket; import com.nukkitx.protocol.bedrock.packet.ChunkRadiusUpdatedPacket; import com.nukkitx.protocol.bedrock.packet.MobEffectPacket; +import com.nukkitx.protocol.bedrock.packet.PlayerActionPacket; import com.nukkitx.protocol.bedrock.packet.StopSoundPacket; import org.geysermc.geyser.entity.type.Entity; +import org.geysermc.geyser.level.BedrockDimension; +import org.geysermc.geyser.network.GameProtocol; import org.geysermc.geyser.session.GeyserSession; import java.util.Set; @@ -70,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 } @@ -93,9 +97,10 @@ public class DimensionUtils { changeDimensionPacket.setRespawn(true); changeDimensionPacket.setPosition(pos); session.sendUpstreamPacket(changeDimensionPacket); + session.setDimension(javaDimension); - session.setDimensionType(session.getDimensions().get(javaDimension)); - ChunkUtils.loadDimension(session); + setBedrockDimension(session, javaDimension); + player.setPosition(pos); session.setSpawned(false); session.setLastChunkPosition(null); @@ -117,6 +122,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); @@ -133,6 +151,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 * @@ -171,7 +207,9 @@ public class DimensionUtils { // Prevents rare instances of Bedrock locking up return javaToBedrock(newDimension) == 2 ? OVERWORLD : NETHER; } - return currentDimension.equals(OVERWORLD) ? NETHER : OVERWORLD; + // Check current Bedrock dimension and not just the Java dimension. + // Fixes rare instances like https://github.com/GeyserMC/Geyser/issues/3161 + return javaToBedrock(currentDimension) == 0 ? NETHER : OVERWORLD; } public static boolean isCustomBedrockNetherId() { 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..1f31d917b 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; @@ -38,6 +39,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; @@ -46,6 +48,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; @@ -85,8 +88,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 +105,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); } } @@ -136,6 +137,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); @@ -150,18 +173,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. */ @@ -186,11 +197,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/java/org/geysermc/geyser/util/SettingsUtils.java b/core/src/main/java/org/geysermc/geyser/util/SettingsUtils.java index 483a4372f..5957fb9d9 100644 --- a/core/src/main/java/org/geysermc/geyser/util/SettingsUtils.java +++ b/core/src/main/java/org/geysermc/geyser/util/SettingsUtils.java @@ -100,11 +100,7 @@ public class SettingsUtils { .translator(MinecraftLocale::getLocaleString); // we need translate gamerules next WorldManager worldManager = GeyserImpl.getInstance().getWorldManager(); - for (GameRule gamerule : GameRule.values()) { - if (gamerule.equals(GameRule.UNKNOWN)) { - continue; - } - + for (GameRule gamerule : GameRule.VALUES) { // Add the relevant form item based on the gamerule type if (Boolean.class.equals(gamerule.getType())) { builder.toggle("gamerule." + gamerule.getJavaID(), worldManager.getGameRuleBool(session, gamerule)); @@ -146,10 +142,6 @@ public class SettingsUtils { if (showGamerules) { for (GameRule gamerule : GameRule.VALUES) { - if (gamerule.equals(GameRule.UNKNOWN)) { - continue; - } - if (Boolean.class.equals(gamerule.getType())) { boolean value = response.next(); if (value != session.getGeyser().getWorldManager().getGameRuleBool(session, gamerule)) { 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 15348939c..fd694f53e 100644 --- a/core/src/main/java/org/geysermc/geyser/util/SoundUtils.java +++ b/core/src/main/java/org/geysermc/geyser/util/SoundUtils.java @@ -26,7 +26,6 @@ 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.CustomSound; import com.github.steveice10.mc.protocol.data.game.level.sound.Sound; import com.nukkitx.math.vector.Vector3f; import com.nukkitx.protocol.bedrock.data.LevelEventType; @@ -63,34 +62,38 @@ public final class SoundUtils { /** * Translates a Java Custom or Builtin Sound to its Bedrock equivalent * - * @param sound the sound to translate + * @param javaIdentifier the sound to translate * @return a Bedrock sound */ - public static String translatePlaySound(Sound sound) { - String packetSound; - if (sound instanceof BuiltinSound builtinSound) { - packetSound = builtinSound.getName(); - } else if (sound instanceof CustomSound customSound) { - packetSound = customSound.getName(); - } else { - GeyserImpl.getInstance().getLogger().debug("Unknown sound, we were unable to map this. " + sound); - return ""; - } + public static String translatePlaySound(String javaIdentifier) { + javaIdentifier = trim(javaIdentifier); - // Drop the Minecraft namespace if applicable - if (packetSound.startsWith("minecraft:")) { - packetSound = packetSound.substring("minecraft:".length()); - } - - SoundMapping soundMapping = Registries.SOUNDS.get(packetSound); + SoundMapping soundMapping = Registries.SOUNDS.get(javaIdentifier); if (soundMapping == null || soundMapping.getPlaysound() == null) { // no mapping - GeyserImpl.getInstance().getLogger().debug("[PlaySound] Defaulting to sound server gave us for " + sound); - return packetSound; + GeyserImpl.getInstance().getLogger().debug("[PlaySound] Defaulting to sound server gave us for " + javaIdentifier); + return javaIdentifier; } 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 * @@ -99,23 +102,25 @@ public final class SoundUtils { * @param position the position * @param pitch the pitch */ - public static void playBuiltinSound(GeyserSession session, BuiltinSound javaSound, Vector3f position, float volume, float pitch) { - String packetSound = javaSound.getName(); + public static void playSound(GeyserSession session, Sound javaSound, Vector3f position, float volume, float pitch) { + String packetSound; + if (!(javaSound instanceof BuiltinSound)) { + // Identifier needs trimmed probably. + packetSound = trim(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; } diff --git a/core/src/main/resources/bedrock/biome_definitions.dat b/core/src/main/resources/bedrock/biome_definitions.dat index 0520c61e2..47b19ab44 100644 Binary files a/core/src/main/resources/bedrock/biome_definitions.dat and b/core/src/main/resources/bedrock/biome_definitions.dat differ diff --git a/core/src/main/resources/bedrock/block_palette.1_19_0.nbt b/core/src/main/resources/bedrock/block_palette.1_19_0.nbt deleted file mode 100644 index 8c095ad02..000000000 Binary files a/core/src/main/resources/bedrock/block_palette.1_19_0.nbt and /dev/null differ 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 000000000..8b53566c5 Binary files /dev/null and b/core/src/main/resources/bedrock/block_palette.1_19_50.nbt differ diff --git a/core/src/main/resources/bedrock/creative_items.1_19_0.json b/core/src/main/resources/bedrock/creative_items.1_19_0.json deleted file mode 100644 index 3a356a802..000000000 --- a/core/src/main/resources/bedrock/creative_items.1_19_0.json +++ /dev/null @@ -1,5437 +0,0 @@ -{ - "items" : [ - { - "id" : "minecraft:planks", - "blockRuntimeId" : 6071 - }, - { - "id" : "minecraft:planks", - "blockRuntimeId" : 6072 - }, - { - "id" : "minecraft:planks", - "blockRuntimeId" : 6073 - }, - { - "id" : "minecraft:planks", - "blockRuntimeId" : 6074 - }, - { - "id" : "minecraft:planks", - "blockRuntimeId" : 6075 - }, - { - "id" : "minecraft:planks", - "blockRuntimeId" : 6076 - }, - { - "id" : "minecraft:mangrove_planks", - "blockRuntimeId" : 947 - }, - { - "id" : "minecraft:crimson_planks", - "blockRuntimeId" : 4850 - }, - { - "id" : "minecraft:warped_planks", - "blockRuntimeId" : 920 - }, - { - "id" : "minecraft:cobblestone_wall", - "blockRuntimeId" : 1182 - }, - { - "id" : "minecraft:cobblestone_wall", - "blockRuntimeId" : 1183 - }, - { - "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" : 1194 - }, - { - "id" : "minecraft:cobblestone_wall", - "blockRuntimeId" : 1189 - }, - { - "id" : "minecraft:cobblestone_wall", - "blockRuntimeId" : 1190 - }, - { - "id" : "minecraft:cobblestone_wall", - "blockRuntimeId" : 1188 - }, - { - "id" : "minecraft:cobblestone_wall", - "blockRuntimeId" : 1191 - }, - { - "id" : "minecraft:cobblestone_wall", - "blockRuntimeId" : 1195 - }, - { - "id" : "minecraft:cobblestone_wall", - "blockRuntimeId" : 1192 - }, - { - "id" : "minecraft:cobblestone_wall", - "blockRuntimeId" : 1193 - }, - { - "id" : "minecraft:blackstone_wall", - "blockRuntimeId" : 3930 - }, - { - "id" : "minecraft:polished_blackstone_wall", - "blockRuntimeId" : 6724 - }, - { - "id" : "minecraft:polished_blackstone_brick_wall", - "blockRuntimeId" : 971 - }, - { - "id" : "minecraft:cobbled_deepslate_wall", - "blockRuntimeId" : 8082 - }, - { - "id" : "minecraft:deepslate_tile_wall", - "blockRuntimeId" : 5071 - }, - { - "id" : "minecraft:polished_deepslate_wall", - "blockRuntimeId" : 7817 - }, - { - "id" : "minecraft:deepslate_brick_wall", - "blockRuntimeId" : 429 - }, - { - "id" : "minecraft:mud_brick_wall", - "blockRuntimeId" : 730 - }, - { - "id" : "minecraft:fence", - "blockRuntimeId" : 7364 - }, - { - "id" : "minecraft:fence", - "blockRuntimeId" : 7365 - }, - { - "id" : "minecraft:fence", - "blockRuntimeId" : 7366 - }, - { - "id" : "minecraft:fence", - "blockRuntimeId" : 7367 - }, - { - "id" : "minecraft:fence", - "blockRuntimeId" : 7368 - }, - { - "id" : "minecraft:fence", - "blockRuntimeId" : 7369 - }, - { - "id" : "minecraft:mangrove_fence", - "blockRuntimeId" : 6633 - }, - { - "id" : "minecraft:nether_brick_fence", - "blockRuntimeId" : 4290 - }, - { - "id" : "minecraft:crimson_fence", - "blockRuntimeId" : 7996 - }, - { - "id" : "minecraft:warped_fence", - "blockRuntimeId" : 5853 - }, - { - "id" : "minecraft:fence_gate", - "blockRuntimeId" : 76 - }, - { - "id" : "minecraft:spruce_fence_gate", - "blockRuntimeId" : 6584 - }, - { - "id" : "minecraft:birch_fence_gate", - "blockRuntimeId" : 3777 - }, - { - "id" : "minecraft:jungle_fence_gate", - "blockRuntimeId" : 5365 - }, - { - "id" : "minecraft:acacia_fence_gate", - "blockRuntimeId" : 7586 - }, - { - "id" : "minecraft:dark_oak_fence_gate", - "blockRuntimeId" : 4173 - }, - { - "id" : "minecraft:mangrove_fence_gate", - "blockRuntimeId" : 4625 - }, - { - "id" : "minecraft:crimson_fence_gate", - "blockRuntimeId" : 4661 - }, - { - "id" : "minecraft:warped_fence_gate", - "blockRuntimeId" : 5399 - }, - { - "id" : "minecraft:normal_stone_stairs", - "blockRuntimeId" : 633 - }, - { - "id" : "minecraft:stone_stairs", - "blockRuntimeId" : 3708 - }, - { - "id" : "minecraft:mossy_cobblestone_stairs", - "blockRuntimeId" : 4092 - }, - { - "id" : "minecraft:oak_stairs", - "blockRuntimeId" : 273 - }, - { - "id" : "minecraft:spruce_stairs", - "blockRuntimeId" : 128 - }, - { - "id" : "minecraft:birch_stairs", - "blockRuntimeId" : 7003 - }, - { - "id" : "minecraft:jungle_stairs", - "blockRuntimeId" : 6967 - }, - { - "id" : "minecraft:acacia_stairs", - "blockRuntimeId" : 6200 - }, - { - "id" : "minecraft:dark_oak_stairs", - "blockRuntimeId" : 5063 - }, - { - "id" : "minecraft:mangrove_stairs", - "blockRuntimeId" : 4595 - }, - { - "id" : "minecraft:stone_brick_stairs", - "blockRuntimeId" : 931 - }, - { - "id" : "minecraft:mossy_stone_brick_stairs", - "blockRuntimeId" : 5883 - }, - { - "id" : "minecraft:sandstone_stairs", - "blockRuntimeId" : 3587 - }, - { - "id" : "minecraft:smooth_sandstone_stairs", - "blockRuntimeId" : 3627 - }, - { - "id" : "minecraft:red_sandstone_stairs", - "blockRuntimeId" : 5350 - }, - { - "id" : "minecraft:smooth_red_sandstone_stairs", - "blockRuntimeId" : 5546 - }, - { - "id" : "minecraft:granite_stairs", - "blockRuntimeId" : 3537 - }, - { - "id" : "minecraft:polished_granite_stairs", - "blockRuntimeId" : 4150 - }, - { - "id" : "minecraft:diorite_stairs", - "blockRuntimeId" : 4391 - }, - { - "id" : "minecraft:polished_diorite_stairs", - "blockRuntimeId" : 6714 - }, - { - "id" : "minecraft:andesite_stairs", - "blockRuntimeId" : 5308 - }, - { - "id" : "minecraft:polished_andesite_stairs", - "blockRuntimeId" : 7028 - }, - { - "id" : "minecraft:brick_stairs", - "blockRuntimeId" : 6530 - }, - { - "id" : "minecraft:nether_brick_stairs", - "blockRuntimeId" : 106 - }, - { - "id" : "minecraft:red_nether_brick_stairs", - "blockRuntimeId" : 6602 - }, - { - "id" : "minecraft:end_brick_stairs", - "blockRuntimeId" : 6382 - }, - { - "id" : "minecraft:quartz_stairs", - "blockRuntimeId" : 4767 - }, - { - "id" : "minecraft:smooth_quartz_stairs", - "blockRuntimeId" : 7700 - }, - { - "id" : "minecraft:purpur_stairs", - "blockRuntimeId" : 7755 - }, - { - "id" : "minecraft:prismarine_stairs", - "blockRuntimeId" : 7263 - }, - { - "id" : "minecraft:dark_prismarine_stairs", - "blockRuntimeId" : 7430 - }, - { - "id" : "minecraft:prismarine_bricks_stairs", - "blockRuntimeId" : 206 - }, - { - "id" : "minecraft:crimson_stairs", - "blockRuntimeId" : 6280 - }, - { - "id" : "minecraft:warped_stairs", - "blockRuntimeId" : 3718 - }, - { - "id" : "minecraft:blackstone_stairs", - "blockRuntimeId" : 7019 - }, - { - "id" : "minecraft:polished_blackstone_stairs", - "blockRuntimeId" : 4297 - }, - { - "id" : "minecraft:polished_blackstone_brick_stairs", - "blockRuntimeId" : 4477 - }, - { - "id" : "minecraft:cut_copper_stairs", - "blockRuntimeId" : 4604 - }, - { - "id" : "minecraft:exposed_cut_copper_stairs", - "blockRuntimeId" : 4587 - }, - { - "id" : "minecraft:weathered_cut_copper_stairs", - "blockRuntimeId" : 4305 - }, - { - "id" : "minecraft:oxidized_cut_copper_stairs", - "blockRuntimeId" : 351 - }, - { - "id" : "minecraft:waxed_cut_copper_stairs", - "blockRuntimeId" : 393 - }, - { - "id" : "minecraft:waxed_exposed_cut_copper_stairs", - "blockRuntimeId" : 3902 - }, - { - "id" : "minecraft:waxed_weathered_cut_copper_stairs", - "blockRuntimeId" : 6167 - }, - { - "id" : "minecraft:waxed_oxidized_cut_copper_stairs", - "blockRuntimeId" : 5840 - }, - { - "id" : "minecraft:cobbled_deepslate_stairs", - "blockRuntimeId" : 147 - }, - { - "id" : "minecraft:deepslate_tile_stairs", - "blockRuntimeId" : 4653 - }, - { - "id" : "minecraft:polished_deepslate_stairs", - "blockRuntimeId" : 294 - }, - { - "id" : "minecraft:deepslate_brick_stairs", - "blockRuntimeId" : 7422 - }, - { - "id" : "minecraft:mud_brick_stairs", - "blockRuntimeId" : 5522 - }, - { - "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" : 6552 - }, - { - "id" : "minecraft:birch_trapdoor", - "blockRuntimeId" : 6650 - }, - { - "id" : "minecraft:jungle_trapdoor", - "blockRuntimeId" : 5381 - }, - { - "id" : "minecraft:acacia_trapdoor", - "blockRuntimeId" : 5589 - }, - { - "id" : "minecraft:dark_oak_trapdoor", - "blockRuntimeId" : 7502 - }, - { - "id" : "minecraft:mangrove_trapdoor", - "blockRuntimeId" : 4485 - }, - { - "id" : "minecraft:iron_trapdoor", - "blockRuntimeId" : 321 - }, - { - "id" : "minecraft:crimson_trapdoor", - "blockRuntimeId" : 4333 - }, - { - "id" : "minecraft:warped_trapdoor", - "blockRuntimeId" : 4733 - }, - { - "id" : "minecraft:iron_bars", - "blockRuntimeId" : 4801 - }, - { - "id" : "minecraft:glass", - "blockRuntimeId" : 6164 - }, - { - "id" : "minecraft:stained_glass", - "blockRuntimeId" : 1133 - }, - { - "id" : "minecraft:stained_glass", - "blockRuntimeId" : 1141 - }, - { - "id" : "minecraft:stained_glass", - "blockRuntimeId" : 1140 - }, - { - "id" : "minecraft:stained_glass", - "blockRuntimeId" : 1148 - }, - { - "id" : "minecraft:stained_glass", - "blockRuntimeId" : 1145 - }, - { - "id" : "minecraft:stained_glass", - "blockRuntimeId" : 1147 - }, - { - "id" : "minecraft:stained_glass", - "blockRuntimeId" : 1134 - }, - { - "id" : "minecraft:stained_glass", - "blockRuntimeId" : 1137 - }, - { - "id" : "minecraft:stained_glass", - "blockRuntimeId" : 1138 - }, - { - "id" : "minecraft:stained_glass", - "blockRuntimeId" : 1146 - }, - { - "id" : "minecraft:stained_glass", - "blockRuntimeId" : 1142 - }, - { - "id" : "minecraft:stained_glass", - "blockRuntimeId" : 1136 - }, - { - "id" : "minecraft:stained_glass", - "blockRuntimeId" : 1144 - }, - { - "id" : "minecraft:stained_glass", - "blockRuntimeId" : 1143 - }, - { - "id" : "minecraft:stained_glass", - "blockRuntimeId" : 1135 - }, - { - "id" : "minecraft:stained_glass", - "blockRuntimeId" : 1139 - }, - { - "id" : "minecraft:tinted_glass", - "blockRuntimeId" : 5975 - }, - { - "id" : "minecraft:glass_pane", - "blockRuntimeId" : 5233 - }, - { - "id" : "minecraft:stained_glass_pane", - "blockRuntimeId" : 4852 - }, - { - "id" : "minecraft:stained_glass_pane", - "blockRuntimeId" : 4860 - }, - { - "id" : "minecraft:stained_glass_pane", - "blockRuntimeId" : 4859 - }, - { - "id" : "minecraft:stained_glass_pane", - "blockRuntimeId" : 4867 - }, - { - "id" : "minecraft:stained_glass_pane", - "blockRuntimeId" : 4864 - }, - { - "id" : "minecraft:stained_glass_pane", - "blockRuntimeId" : 4866 - }, - { - "id" : "minecraft:stained_glass_pane", - "blockRuntimeId" : 4853 - }, - { - "id" : "minecraft:stained_glass_pane", - "blockRuntimeId" : 4856 - }, - { - "id" : "minecraft:stained_glass_pane", - "blockRuntimeId" : 4857 - }, - { - "id" : "minecraft:stained_glass_pane", - "blockRuntimeId" : 4865 - }, - { - "id" : "minecraft:stained_glass_pane", - "blockRuntimeId" : 4861 - }, - { - "id" : "minecraft:stained_glass_pane", - "blockRuntimeId" : 4855 - }, - { - "id" : "minecraft:stained_glass_pane", - "blockRuntimeId" : 4863 - }, - { - "id" : "minecraft:stained_glass_pane", - "blockRuntimeId" : 4862 - }, - { - "id" : "minecraft:stained_glass_pane", - "blockRuntimeId" : 4854 - }, - { - "id" : "minecraft:stained_glass_pane", - "blockRuntimeId" : 4858 - }, - { - "id" : "minecraft:ladder", - "blockRuntimeId" : 8262 - }, - { - "id" : "minecraft:scaffolding", - "blockRuntimeId" : 3571 - }, - { - "id" : "minecraft:stone_block_slab", - "blockRuntimeId" : 4270 - }, - { - "id" : "minecraft:stone_block_slab4", - "blockRuntimeId" : 5822 - }, - { - "id" : "minecraft:stone_block_slab", - "blockRuntimeId" : 4273 - }, - { - "id" : "minecraft:stone_block_slab2", - "blockRuntimeId" : 5793 - }, - { - "id" : "minecraft:wooden_slab", - "blockRuntimeId" : 5270 - }, - { - "id" : "minecraft:wooden_slab", - "blockRuntimeId" : 5271 - }, - { - "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:mangrove_slab", - "blockRuntimeId" : 1149 - }, - { - "id" : "minecraft:stone_block_slab", - "blockRuntimeId" : 4275 - }, - { - "id" : "minecraft:stone_block_slab4", - "blockRuntimeId" : 5820 - }, - { - "id" : "minecraft:stone_block_slab", - "blockRuntimeId" : 4271 - }, - { - "id" : "minecraft:stone_block_slab4", - "blockRuntimeId" : 5823 - }, - { - "id" : "minecraft:stone_block_slab2", - "blockRuntimeId" : 5794 - }, - { - "id" : "minecraft:stone_block_slab2", - "blockRuntimeId" : 5788 - }, - { - "id" : "minecraft:stone_block_slab4", - "blockRuntimeId" : 5824 - }, - { - "id" : "minecraft:stone_block_slab3", - "blockRuntimeId" : 5805 - }, - { - "id" : "minecraft:stone_block_slab3", - "blockRuntimeId" : 5810 - }, - { - "id" : "minecraft:stone_block_slab3", - "blockRuntimeId" : 5811 - }, - { - "id" : "minecraft:stone_block_slab3", - "blockRuntimeId" : 5808 - }, - { - "id" : "minecraft:stone_block_slab3", - "blockRuntimeId" : 5809 - }, - { - "id" : "minecraft:stone_block_slab3", - "blockRuntimeId" : 5807 - }, - { - "id" : "minecraft:stone_block_slab3", - "blockRuntimeId" : 5806 - }, - { - "id" : "minecraft:stone_block_slab", - "blockRuntimeId" : 4274 - }, - { - "id" : "minecraft:stone_block_slab", - "blockRuntimeId" : 4277 - }, - { - "id" : "minecraft:stone_block_slab2", - "blockRuntimeId" : 5795 - }, - { - "id" : "minecraft:stone_block_slab3", - "blockRuntimeId" : 5804 - }, - { - "id" : "minecraft:stone_block_slab", - "blockRuntimeId" : 4276 - }, - { - "id" : "minecraft:stone_block_slab4", - "blockRuntimeId" : 5821 - }, - { - "id" : "minecraft:stone_block_slab2", - "blockRuntimeId" : 5789 - }, - { - "id" : "minecraft:stone_block_slab2", - "blockRuntimeId" : 5790 - }, - { - "id" : "minecraft:stone_block_slab2", - "blockRuntimeId" : 5791 - }, - { - "id" : "minecraft:stone_block_slab2", - "blockRuntimeId" : 5792 - }, - { - "id" : "minecraft:crimson_slab", - "blockRuntimeId" : 5900 - }, - { - "id" : "minecraft:warped_slab", - "blockRuntimeId" : 6484 - }, - { - "id" : "minecraft:blackstone_slab", - "blockRuntimeId" : 910 - }, - { - "id" : "minecraft:polished_blackstone_slab", - "blockRuntimeId" : 6018 - }, - { - "id" : "minecraft:polished_blackstone_brick_slab", - "blockRuntimeId" : 4192 - }, - { - "id" : "minecraft:cut_copper_slab", - "blockRuntimeId" : 5235 - }, - { - "id" : "minecraft:exposed_cut_copper_slab", - "blockRuntimeId" : 6600 - }, - { - "id" : "minecraft:weathered_cut_copper_slab", - "blockRuntimeId" : 6053 - }, - { - "id" : "minecraft:oxidized_cut_copper_slab", - "blockRuntimeId" : 5282 - }, - { - "id" : "minecraft:waxed_cut_copper_slab", - "blockRuntimeId" : 7815 - }, - { - "id" : "minecraft:waxed_exposed_cut_copper_slab", - "blockRuntimeId" : 249 - }, - { - "id" : "minecraft:waxed_weathered_cut_copper_slab", - "blockRuntimeId" : 6545 - }, - { - "id" : "minecraft:waxed_oxidized_cut_copper_slab", - "blockRuntimeId" : 708 - }, - { - "id" : "minecraft:cobbled_deepslate_slab", - "blockRuntimeId" : 7310 - }, - { - "id" : "minecraft:polished_deepslate_slab", - "blockRuntimeId" : 288 - }, - { - "id" : "minecraft:deepslate_tile_slab", - "blockRuntimeId" : 4291 - }, - { - "id" : "minecraft:deepslate_brick_slab", - "blockRuntimeId" : 3716 - }, - { - "id" : "minecraft:mud_brick_slab", - "blockRuntimeId" : 3910 - }, - { - "id" : "minecraft:brick_block", - "blockRuntimeId" : 4765 - }, - { - "id" : "minecraft:chiseled_nether_bricks", - "blockRuntimeId" : 7249 - }, - { - "id" : "minecraft:cracked_nether_bricks", - "blockRuntimeId" : 4552 - }, - { - "id" : "minecraft:quartz_bricks", - "blockRuntimeId" : 6351 - }, - { - "id" : "minecraft:stonebrick", - "blockRuntimeId" : 6547 - }, - { - "id" : "minecraft:stonebrick", - "blockRuntimeId" : 6548 - }, - { - "id" : "minecraft:stonebrick", - "blockRuntimeId" : 6549 - }, - { - "id" : "minecraft:stonebrick", - "blockRuntimeId" : 6550 - }, - { - "id" : "minecraft:end_bricks", - "blockRuntimeId" : 281 - }, - { - "id" : "minecraft:prismarine", - "blockRuntimeId" : 6087 - }, - { - "id" : "minecraft:polished_blackstone_bricks", - "blockRuntimeId" : 4680 - }, - { - "id" : "minecraft:cracked_polished_blackstone_bricks", - "blockRuntimeId" : 7214 - }, - { - "id" : "minecraft:gilded_blackstone", - "blockRuntimeId" : 4586 - }, - { - "id" : "minecraft:chiseled_polished_blackstone", - "blockRuntimeId" : 5062 - }, - { - "id" : "minecraft:deepslate_tiles", - "blockRuntimeId" : 4581 - }, - { - "id" : "minecraft:cracked_deepslate_tiles", - "blockRuntimeId" : 4160 - }, - { - "id" : "minecraft:deepslate_bricks", - "blockRuntimeId" : 5464 - }, - { - "id" : "minecraft:cracked_deepslate_bricks", - "blockRuntimeId" : 5364 - }, - { - "id" : "minecraft:chiseled_deepslate", - "blockRuntimeId" : 5234 - }, - { - "id" : "minecraft:cobblestone", - "blockRuntimeId" : 3615 - }, - { - "id" : "minecraft:mossy_cobblestone", - "blockRuntimeId" : 252 - }, - { - "id" : "minecraft:cobbled_deepslate", - "blockRuntimeId" : 6670 - }, - { - "id" : "minecraft:smooth_stone", - "blockRuntimeId" : 4582 - }, - { - "id" : "minecraft:sandstone", - "blockRuntimeId" : 3653 - }, - { - "id" : "minecraft:sandstone", - "blockRuntimeId" : 3654 - }, - { - "id" : "minecraft:sandstone", - "blockRuntimeId" : 3655 - }, - { - "id" : "minecraft:sandstone", - "blockRuntimeId" : 3656 - }, - { - "id" : "minecraft:red_sandstone", - "blockRuntimeId" : 6580 - }, - { - "id" : "minecraft:red_sandstone", - "blockRuntimeId" : 6581 - }, - { - "id" : "minecraft:red_sandstone", - "blockRuntimeId" : 6582 - }, - { - "id" : "minecraft:red_sandstone", - "blockRuntimeId" : 6583 - }, - { - "id" : "minecraft:coal_block", - "blockRuntimeId" : 5398 - }, - { - "id" : "minecraft:dried_kelp_block", - "blockRuntimeId" : 7979 - }, - { - "id" : "minecraft:gold_block", - "blockRuntimeId" : 291 - }, - { - "id" : "minecraft:iron_block", - "blockRuntimeId" : 8261 - }, - { - "id" : "minecraft:copper_block", - "blockRuntimeId" : 4651 - }, - { - "id" : "minecraft:exposed_copper", - "blockRuntimeId" : 593 - }, - { - "id" : "minecraft:weathered_copper", - "blockRuntimeId" : 8246 - }, - { - "id" : "minecraft:oxidized_copper", - "blockRuntimeId" : 3553 - }, - { - "id" : "minecraft:waxed_copper", - "blockRuntimeId" : 7734 - }, - { - "id" : "minecraft:waxed_exposed_copper", - "blockRuntimeId" : 694 - }, - { - "id" : "minecraft:waxed_weathered_copper", - "blockRuntimeId" : 707 - }, - { - "id" : "minecraft:waxed_oxidized_copper", - "blockRuntimeId" : 7542 - }, - { - "id" : "minecraft:cut_copper", - "blockRuntimeId" : 4689 - }, - { - "id" : "minecraft:exposed_cut_copper", - "blockRuntimeId" : 6166 - }, - { - "id" : "minecraft:weathered_cut_copper", - "blockRuntimeId" : 7197 - }, - { - "id" : "minecraft:oxidized_cut_copper", - "blockRuntimeId" : 5478 - }, - { - "id" : "minecraft:waxed_cut_copper", - "blockRuntimeId" : 7293 - }, - { - "id" : "minecraft:waxed_exposed_cut_copper", - "blockRuntimeId" : 3809 - }, - { - "id" : "minecraft:waxed_weathered_cut_copper", - "blockRuntimeId" : 4851 - }, - { - "id" : "minecraft:waxed_oxidized_cut_copper", - "blockRuntimeId" : 214 - }, - { - "id" : "minecraft:emerald_block", - "blockRuntimeId" : 1159 - }, - { - "id" : "minecraft:diamond_block", - "blockRuntimeId" : 272 - }, - { - "id" : "minecraft:lapis_block", - "blockRuntimeId" : 4286 - }, - { - "id" : "minecraft:raw_iron_block", - "blockRuntimeId" : 8260 - }, - { - "id" : "minecraft:raw_copper_block", - "blockRuntimeId" : 5269 - }, - { - "id" : "minecraft:raw_gold_block", - "blockRuntimeId" : 361 - }, - { - "id" : "minecraft:quartz_block", - "blockRuntimeId" : 3696 - }, - { - "id" : "minecraft:quartz_block", - "blockRuntimeId" : 3698 - }, - { - "id" : "minecraft:quartz_block", - "blockRuntimeId" : 3697 - }, - { - "id" : "minecraft:quartz_block", - "blockRuntimeId" : 3699 - }, - { - "id" : "minecraft:prismarine", - "blockRuntimeId" : 6085 - }, - { - "id" : "minecraft:prismarine", - "blockRuntimeId" : 6086 - }, - { - "id" : "minecraft:slime", - "blockRuntimeId" : 4233 - }, - { - "id" : "minecraft:honey_block", - "blockRuntimeId" : 892 - }, - { - "id" : "minecraft:honeycomb_block", - "blockRuntimeId" : 4476 - }, - { - "id" : "minecraft:hay_block", - "blockRuntimeId" : 695 - }, - { - "id" : "minecraft:bone_block", - "blockRuntimeId" : 4234 - }, - { - "id" : "minecraft:nether_brick", - "blockRuntimeId" : 7272 - }, - { - "id" : "minecraft:red_nether_brick", - "blockRuntimeId" : 146 - }, - { - "id" : "minecraft:netherite_block", - "blockRuntimeId" : 3775 - }, - { - "id" : "minecraft:lodestone", - "blockRuntimeId" : 8259 - }, - { - "id" : "minecraft:wool", - "blockRuntimeId" : 3458 - }, - { - "id" : "minecraft:wool", - "blockRuntimeId" : 3466 - }, - { - "id" : "minecraft:wool", - "blockRuntimeId" : 3465 - }, - { - "id" : "minecraft:wool", - "blockRuntimeId" : 3473 - }, - { - "id" : "minecraft:wool", - "blockRuntimeId" : 3470 - }, - { - "id" : "minecraft:wool", - "blockRuntimeId" : 3472 - }, - { - "id" : "minecraft:wool", - "blockRuntimeId" : 3459 - }, - { - "id" : "minecraft:wool", - "blockRuntimeId" : 3462 - }, - { - "id" : "minecraft:wool", - "blockRuntimeId" : 3463 - }, - { - "id" : "minecraft:wool", - "blockRuntimeId" : 3471 - }, - { - "id" : "minecraft:wool", - "blockRuntimeId" : 3467 - }, - { - "id" : "minecraft:wool", - "blockRuntimeId" : 3461 - }, - { - "id" : "minecraft:wool", - "blockRuntimeId" : 3469 - }, - { - "id" : "minecraft:wool", - "blockRuntimeId" : 3468 - }, - { - "id" : "minecraft:wool", - "blockRuntimeId" : 3460 - }, - { - "id" : "minecraft:wool", - "blockRuntimeId" : 3464 - }, - { - "id" : "minecraft:carpet", - "blockRuntimeId" : 949 - }, - { - "id" : "minecraft:carpet", - "blockRuntimeId" : 957 - }, - { - "id" : "minecraft:carpet", - "blockRuntimeId" : 956 - }, - { - "id" : "minecraft:carpet", - "blockRuntimeId" : 964 - }, - { - "id" : "minecraft:carpet", - "blockRuntimeId" : 961 - }, - { - "id" : "minecraft:carpet", - "blockRuntimeId" : 963 - }, - { - "id" : "minecraft:carpet", - "blockRuntimeId" : 950 - }, - { - "id" : "minecraft:carpet", - "blockRuntimeId" : 953 - }, - { - "id" : "minecraft:carpet", - "blockRuntimeId" : 954 - }, - { - "id" : "minecraft:carpet", - "blockRuntimeId" : 962 - }, - { - "id" : "minecraft:carpet", - "blockRuntimeId" : 958 - }, - { - "id" : "minecraft:carpet", - "blockRuntimeId" : 952 - }, - { - "id" : "minecraft:carpet", - "blockRuntimeId" : 960 - }, - { - "id" : "minecraft:carpet", - "blockRuntimeId" : 959 - }, - { - "id" : "minecraft:carpet", - "blockRuntimeId" : 951 - }, - { - "id" : "minecraft:carpet", - "blockRuntimeId" : 955 - }, - { - "id" : "minecraft:concrete_powder", - "blockRuntimeId" : 6264 - }, - { - "id" : "minecraft:concrete_powder", - "blockRuntimeId" : 6272 - }, - { - "id" : "minecraft:concrete_powder", - "blockRuntimeId" : 6271 - }, - { - "id" : "minecraft:concrete_powder", - "blockRuntimeId" : 6279 - }, - { - "id" : "minecraft:concrete_powder", - "blockRuntimeId" : 6276 - }, - { - "id" : "minecraft:concrete_powder", - "blockRuntimeId" : 6278 - }, - { - "id" : "minecraft:concrete_powder", - "blockRuntimeId" : 6265 - }, - { - "id" : "minecraft:concrete_powder", - "blockRuntimeId" : 6268 - }, - { - "id" : "minecraft:concrete_powder", - "blockRuntimeId" : 6269 - }, - { - "id" : "minecraft:concrete_powder", - "blockRuntimeId" : 6277 - }, - { - "id" : "minecraft:concrete_powder", - "blockRuntimeId" : 6273 - }, - { - "id" : "minecraft:concrete_powder", - "blockRuntimeId" : 6267 - }, - { - "id" : "minecraft:concrete_powder", - "blockRuntimeId" : 6275 - }, - { - "id" : "minecraft:concrete_powder", - "blockRuntimeId" : 6274 - }, - { - "id" : "minecraft:concrete_powder", - "blockRuntimeId" : 6266 - }, - { - "id" : "minecraft:concrete_powder", - "blockRuntimeId" : 6270 - }, - { - "id" : "minecraft:concrete", - "blockRuntimeId" : 660 - }, - { - "id" : "minecraft:concrete", - "blockRuntimeId" : 668 - }, - { - "id" : "minecraft:concrete", - "blockRuntimeId" : 667 - }, - { - "id" : "minecraft:concrete", - "blockRuntimeId" : 675 - }, - { - "id" : "minecraft:concrete", - "blockRuntimeId" : 672 - }, - { - "id" : "minecraft:concrete", - "blockRuntimeId" : 674 - }, - { - "id" : "minecraft:concrete", - "blockRuntimeId" : 661 - }, - { - "id" : "minecraft:concrete", - "blockRuntimeId" : 664 - }, - { - "id" : "minecraft:concrete", - "blockRuntimeId" : 665 - }, - { - "id" : "minecraft:concrete", - "blockRuntimeId" : 673 - }, - { - "id" : "minecraft:concrete", - "blockRuntimeId" : 669 - }, - { - "id" : "minecraft:concrete", - "blockRuntimeId" : 663 - }, - { - "id" : "minecraft:concrete", - "blockRuntimeId" : 671 - }, - { - "id" : "minecraft:concrete", - "blockRuntimeId" : 670 - }, - { - "id" : "minecraft:concrete", - "blockRuntimeId" : 662 - }, - { - "id" : "minecraft:concrete", - "blockRuntimeId" : 666 - }, - { - "id" : "minecraft:clay", - "blockRuntimeId" : 7124 - }, - { - "id" : "minecraft:hardened_clay", - "blockRuntimeId" : 641 - }, - { - "id" : "minecraft:stained_hardened_clay", - "blockRuntimeId" : 6176 - }, - { - "id" : "minecraft:stained_hardened_clay", - "blockRuntimeId" : 6184 - }, - { - "id" : "minecraft:stained_hardened_clay", - "blockRuntimeId" : 6183 - }, - { - "id" : "minecraft:stained_hardened_clay", - "blockRuntimeId" : 6191 - }, - { - "id" : "minecraft:stained_hardened_clay", - "blockRuntimeId" : 6188 - }, - { - "id" : "minecraft:stained_hardened_clay", - "blockRuntimeId" : 6190 - }, - { - "id" : "minecraft:stained_hardened_clay", - "blockRuntimeId" : 6177 - }, - { - "id" : "minecraft:stained_hardened_clay", - "blockRuntimeId" : 6180 - }, - { - "id" : "minecraft:stained_hardened_clay", - "blockRuntimeId" : 6181 - }, - { - "id" : "minecraft:stained_hardened_clay", - "blockRuntimeId" : 6189 - }, - { - "id" : "minecraft:stained_hardened_clay", - "blockRuntimeId" : 6185 - }, - { - "id" : "minecraft:stained_hardened_clay", - "blockRuntimeId" : 6179 - }, - { - "id" : "minecraft:stained_hardened_clay", - "blockRuntimeId" : 6187 - }, - { - "id" : "minecraft:stained_hardened_clay", - "blockRuntimeId" : 6186 - }, - { - "id" : "minecraft:stained_hardened_clay", - "blockRuntimeId" : 6178 - }, - { - "id" : "minecraft:stained_hardened_clay", - "blockRuntimeId" : 6182 - }, - { - "id" : "minecraft:white_glazed_terracotta", - "blockRuntimeId" : 5573 - }, - { - "id" : "minecraft:silver_glazed_terracotta", - "blockRuntimeId" : 3531 - }, - { - "id" : "minecraft:gray_glazed_terracotta", - "blockRuntimeId" : 8253 - }, - { - "id" : "minecraft:black_glazed_terracotta", - "blockRuntimeId" : 5834 - }, - { - "id" : "minecraft:brown_glazed_terracotta", - "blockRuntimeId" : 3547 - }, - { - "id" : "minecraft:red_glazed_terracotta", - "blockRuntimeId" : 4167 - }, - { - "id" : "minecraft:orange_glazed_terracotta", - "blockRuntimeId" : 1151 - }, - { - "id" : "minecraft:yellow_glazed_terracotta", - "blockRuntimeId" : 913 - }, - { - "id" : "minecraft:lime_glazed_terracotta", - "blockRuntimeId" : 223 - }, - { - "id" : "minecraft:green_glazed_terracotta", - "blockRuntimeId" : 6610 - }, - { - "id" : "minecraft:cyan_glazed_terracotta", - "blockRuntimeId" : 5358 - }, - { - "id" : "minecraft:light_blue_glazed_terracotta", - "blockRuntimeId" : 5471 - }, - { - "id" : "minecraft:blue_glazed_terracotta", - "blockRuntimeId" : 5465 - }, - { - "id" : "minecraft:purple_glazed_terracotta", - "blockRuntimeId" : 7011 - }, - { - "id" : "minecraft:magenta_glazed_terracotta", - "blockRuntimeId" : 965 - }, - { - "id" : "minecraft:pink_glazed_terracotta", - "blockRuntimeId" : 6539 - }, - { - "id" : "minecraft:purpur_block", - "blockRuntimeId" : 7714 - }, - { - "id" : "minecraft:purpur_block", - "blockRuntimeId" : 7716 - }, - { - "id" : "minecraft:packed_mud", - "blockRuntimeId" : 283 - }, - { - "id" : "minecraft:mud_bricks", - "blockRuntimeId" : 6889 - }, - { - "id" : "minecraft:nether_wart_block", - "blockRuntimeId" : 4293 - }, - { - "id" : "minecraft:warped_wart_block", - "blockRuntimeId" : 5905 - }, - { - "id" : "minecraft:shroomlight", - "blockRuntimeId" : 5061 - }, - { - "id" : "minecraft:crimson_nylium", - "blockRuntimeId" : 4189 - }, - { - "id" : "minecraft:warped_nylium", - "blockRuntimeId" : 6349 - }, - { - "id" : "minecraft:basalt", - "blockRuntimeId" : 4349 - }, - { - "id" : "minecraft:polished_basalt", - "blockRuntimeId" : 24 - }, - { - "id" : "minecraft:smooth_basalt", - "blockRuntimeId" : 1157 - }, - { - "id" : "minecraft:soul_soil", - "blockRuntimeId" : 5830 - }, - { - "id" : "minecraft:dirt", - "blockRuntimeId" : 5751 - }, - { - "id" : "minecraft:dirt", - "blockRuntimeId" : 5752 - }, - { - "id" : "minecraft:farmland", - "blockRuntimeId" : 3912 - }, - { - "id" : "minecraft:grass", - "blockRuntimeId" : 6975 - }, - { - "id" : "minecraft:grass_path", - "blockRuntimeId" : 8081 - }, - { - "id" : "minecraft:podzol", - "blockRuntimeId" : 4650 - }, - { - "id" : "minecraft:mycelium", - "blockRuntimeId" : 3683 - }, - { - "id" : "minecraft:mud", - "blockRuntimeId" : 6684 - }, - { - "id" : "minecraft:stone", - "blockRuntimeId" : 653 - }, - { - "id" : "minecraft:iron_ore", - "blockRuntimeId" : 4690 - }, - { - "id" : "minecraft:gold_ore", - "blockRuntimeId" : 912 - }, - { - "id" : "minecraft:diamond_ore", - "blockRuntimeId" : 4361 - }, - { - "id" : "minecraft:lapis_ore", - "blockRuntimeId" : 7699 - }, - { - "id" : "minecraft:redstone_ore", - "blockRuntimeId" : 4289 - }, - { - "id" : "minecraft:coal_ore", - "blockRuntimeId" : 4287 - }, - { - "id" : "minecraft:copper_ore", - "blockRuntimeId" : 3554 - }, - { - "id" : "minecraft:emerald_ore", - "blockRuntimeId" : 7347 - }, - { - "id" : "minecraft:quartz_ore", - "blockRuntimeId" : 4501 - }, - { - "id" : "minecraft:nether_gold_ore", - "blockRuntimeId" : 27 - }, - { - "id" : "minecraft:ancient_debris", - "blockRuntimeId" : 6107 - }, - { - "id" : "minecraft:deepslate_iron_ore", - "blockRuntimeId" : 7273 - }, - { - "id" : "minecraft:deepslate_gold_ore", - "blockRuntimeId" : 6106 - }, - { - "id" : "minecraft:deepslate_diamond_ore", - "blockRuntimeId" : 8038 - }, - { - "id" : "minecraft:deepslate_lapis_ore", - "blockRuntimeId" : 7262 - }, - { - "id" : "minecraft:deepslate_redstone_ore", - "blockRuntimeId" : 6616 - }, - { - "id" : "minecraft:deepslate_emerald_ore", - "blockRuntimeId" : 6350 - }, - { - "id" : "minecraft:deepslate_coal_ore", - "blockRuntimeId" : 7196 - }, - { - "id" : "minecraft:deepslate_copper_ore", - "blockRuntimeId" : 105 - }, - { - "id" : "minecraft:gravel", - "blockRuntimeId" : 8287 - }, - { - "id" : "minecraft:stone", - "blockRuntimeId" : 654 - }, - { - "id" : "minecraft:stone", - "blockRuntimeId" : 656 - }, - { - "id" : "minecraft:stone", - "blockRuntimeId" : 658 - }, - { - "id" : "minecraft:blackstone", - "blockRuntimeId" : 7585 - }, - { - "id" : "minecraft:deepslate", - "blockRuntimeId" : 253 - }, - { - "id" : "minecraft:stone", - "blockRuntimeId" : 655 - }, - { - "id" : "minecraft:stone", - "blockRuntimeId" : 657 - }, - { - "id" : "minecraft:stone", - "blockRuntimeId" : 659 - }, - { - "id" : "minecraft:polished_blackstone", - "blockRuntimeId" : 3682 - }, - { - "id" : "minecraft:polished_deepslate", - "blockRuntimeId" : 7754 - }, - { - "id" : "minecraft:sand", - "blockRuntimeId" : 4195 - }, - { - "id" : "minecraft:sand", - "blockRuntimeId" : 4196 - }, - { - "id" : "minecraft:cactus", - "blockRuntimeId" : 6986 - }, - { - "id" : "minecraft:log", - "blockRuntimeId" : 6672 - }, - { - "id" : "minecraft:stripped_oak_log", - "blockRuntimeId" : 7543 - }, - { - "id" : "minecraft:log", - "blockRuntimeId" : 6673 - }, - { - "id" : "minecraft:stripped_spruce_log", - "blockRuntimeId" : 6288 - }, - { - "id" : "minecraft:log", - "blockRuntimeId" : 6674 - }, - { - "id" : "minecraft:stripped_birch_log", - "blockRuntimeId" : 5972 - }, - { - "id" : "minecraft:log", - "blockRuntimeId" : 6675 - }, - { - "id" : "minecraft:stripped_jungle_log", - "blockRuntimeId" : 642 - }, - { - "id" : "minecraft:log2", - "blockRuntimeId" : 3830 - }, - { - "id" : "minecraft:stripped_acacia_log", - "blockRuntimeId" : 5848 - }, - { - "id" : "minecraft:log2", - "blockRuntimeId" : 3831 - }, - { - "id" : "minecraft:stripped_dark_oak_log", - "blockRuntimeId" : 216 - }, - { - "id" : "minecraft:mangrove_log", - "blockRuntimeId" : 348 - }, - { - "id" : "minecraft:stripped_mangrove_log", - "blockRuntimeId" : 8284 - }, - { - "id" : "minecraft:crimson_stem", - "blockRuntimeId" : 5897 - }, - { - "id" : "minecraft:stripped_crimson_stem", - "blockRuntimeId" : 6948 - }, - { - "id" : "minecraft:warped_stem", - "blockRuntimeId" : 6486 - }, - { - "id" : "minecraft:stripped_warped_stem", - "blockRuntimeId" : 7400 - }, - { - "id" : "minecraft:wood", - "blockRuntimeId" : 3474 - }, - { - "id" : "minecraft:wood", - "blockRuntimeId" : 3480 - }, - { - "id" : "minecraft:wood", - "blockRuntimeId" : 3475 - }, - { - "id" : "minecraft:wood", - "blockRuntimeId" : 3481 - }, - { - "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:mangrove_wood", - "blockRuntimeId" : 4161 - }, - { - "id" : "minecraft:stripped_mangrove_wood", - "blockRuntimeId" : 4229 - }, - { - "id" : "minecraft:crimson_hyphae", - "blockRuntimeId" : 4294 - }, - { - "id" : "minecraft:stripped_crimson_hyphae", - "blockRuntimeId" : 6499 - }, - { - "id" : "minecraft:warped_hyphae", - "blockRuntimeId" : 5902 - }, - { - "id" : "minecraft:stripped_warped_hyphae", - "blockRuntimeId" : 5579 - }, - { - "id" : "minecraft:leaves", - "blockRuntimeId" : 6090 - }, - { - "id" : "minecraft:leaves", - "blockRuntimeId" : 6091 - }, - { - "id" : "minecraft:leaves", - "blockRuntimeId" : 6092 - }, - { - "id" : "minecraft:leaves", - "blockRuntimeId" : 6093 - }, - { - "id" : "minecraft:leaves2", - "blockRuntimeId" : 4353 - }, - { - "id" : "minecraft:leaves2", - "blockRuntimeId" : 4354 - }, - { - "id" : "minecraft:mangrove_leaves", - "blockRuntimeId" : 6666 - }, - { - "id" : "minecraft:azalea_leaves", - "blockRuntimeId" : 7710 - }, - { - "id" : "minecraft:azalea_leaves_flowered", - "blockRuntimeId" : 6339 - }, - { - "id" : "minecraft:sapling", - "blockRuntimeId" : 712 - }, - { - "id" : "minecraft:sapling", - "blockRuntimeId" : 713 - }, - { - "id" : "minecraft:sapling", - "blockRuntimeId" : 714 - }, - { - "id" : "minecraft:sapling", - "blockRuntimeId" : 715 - }, - { - "id" : "minecraft:sapling", - "blockRuntimeId" : 716 - }, - { - "id" : "minecraft:sapling", - "blockRuntimeId" : 717 - }, - { - "id" : "minecraft:mangrove_propagule", - "blockRuntimeId" : 6976 - }, - { - "id" : "minecraft:bee_nest", - "blockRuntimeId" : 5754 - }, - { - "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" : 392 - }, - { - "id" : "minecraft:melon_slice" - }, - { - "id" : "minecraft:glistering_melon_slice" - }, - { - "id" : "minecraft:sweet_berries" - }, - { - "id" : "minecraft:glow_berries" - }, - { - "id" : "minecraft:pumpkin", - "blockRuntimeId" : 4577 - }, - { - "id" : "minecraft:carved_pumpkin", - "blockRuntimeId" : 7378 - }, - { - "id" : "minecraft:lit_pumpkin", - "blockRuntimeId" : 6685 - }, - { - "id" : "minecraft:honeycomb" - }, - { - "id" : "minecraft:tallgrass", - "blockRuntimeId" : 929 - }, - { - "id" : "minecraft:double_plant", - "blockRuntimeId" : 5455 - }, - { - "id" : "minecraft:tallgrass", - "blockRuntimeId" : 928 - }, - { - "id" : "minecraft:double_plant", - "blockRuntimeId" : 5454 - }, - { - "id" : "minecraft:nether_sprouts" - }, - { - "id" : "minecraft:coral", - "blockRuntimeId" : 6492 - }, - { - "id" : "minecraft:coral", - "blockRuntimeId" : 6490 - }, - { - "id" : "minecraft:coral", - "blockRuntimeId" : 6491 - }, - { - "id" : "minecraft:coral", - "blockRuntimeId" : 6489 - }, - { - "id" : "minecraft:coral", - "blockRuntimeId" : 6493 - }, - { - "id" : "minecraft:coral", - "blockRuntimeId" : 6497 - }, - { - "id" : "minecraft:coral", - "blockRuntimeId" : 6495 - }, - { - "id" : "minecraft:coral", - "blockRuntimeId" : 6496 - }, - { - "id" : "minecraft:coral", - "blockRuntimeId" : 6494 - }, - { - "id" : "minecraft:coral", - "blockRuntimeId" : 6498 - }, - { - "id" : "minecraft:coral_fan", - "blockRuntimeId" : 4616 - }, - { - "id" : "minecraft:coral_fan", - "blockRuntimeId" : 4614 - }, - { - "id" : "minecraft:coral_fan", - "blockRuntimeId" : 4615 - }, - { - "id" : "minecraft:coral_fan", - "blockRuntimeId" : 4613 - }, - { - "id" : "minecraft:coral_fan", - "blockRuntimeId" : 4617 - }, - { - "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" : 7573 - }, - { - "id" : "minecraft:warped_roots", - "blockRuntimeId" : 4362 - }, - { - "id" : "minecraft:yellow_flower", - "blockRuntimeId" : 302 - }, - { - "id" : "minecraft:red_flower", - "blockRuntimeId" : 3616 - }, - { - "id" : "minecraft:red_flower", - "blockRuntimeId" : 3617 - }, - { - "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:double_plant", - "blockRuntimeId" : 5452 - }, - { - "id" : "minecraft:double_plant", - "blockRuntimeId" : 5453 - }, - { - "id" : "minecraft:double_plant", - "blockRuntimeId" : 5456 - }, - { - "id" : "minecraft:double_plant", - "blockRuntimeId" : 5457 - }, - { - "id" : "minecraft:wither_rose", - "blockRuntimeId" : 6165 - }, - { - "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" : 894 - }, - { - "id" : "minecraft:weeping_vines", - "blockRuntimeId" : 5479 - }, - { - "id" : "minecraft:twisting_vines", - "blockRuntimeId" : 5691 - }, - { - "id" : "minecraft:waterlily", - "blockRuntimeId" : 1158 - }, - { - "id" : "minecraft:deadbush", - "blockRuntimeId" : 4677 - }, - { - "id" : "minecraft:bamboo", - "blockRuntimeId" : 3684 - }, - { - "id" : "minecraft:snow", - "blockRuntimeId" : 4194 - }, - { - "id" : "minecraft:ice", - "blockRuntimeId" : 6689 - }, - { - "id" : "minecraft:packed_ice", - "blockRuntimeId" : 282 - }, - { - "id" : "minecraft:blue_ice", - "blockRuntimeId" : 7027 - }, - { - "id" : "minecraft:snow_layer", - "blockRuntimeId" : 155 - }, - { - "id" : "minecraft:pointed_dripstone", - "blockRuntimeId" : 7416 - }, - { - "id" : "minecraft:dripstone_block", - "blockRuntimeId" : 893 - }, - { - "id" : "minecraft:moss_carpet", - "blockRuntimeId" : 286 - }, - { - "id" : "minecraft:moss_block", - "blockRuntimeId" : 6538 - }, - { - "id" : "minecraft:dirt_with_roots", - "blockRuntimeId" : 5397 - }, - { - "id" : "minecraft:hanging_roots", - "blockRuntimeId" : 205 - }, - { - "id" : "minecraft:mangrove_roots", - "blockRuntimeId" : 6175 - }, - { - "id" : "minecraft:muddy_mangrove_roots", - "blockRuntimeId" : 345 - }, - { - "id" : "minecraft:big_dripleaf", - "blockRuntimeId" : 5980 - }, - { - "id" : "minecraft:small_dripleaf_block", - "blockRuntimeId" : 4320 - }, - { - "id" : "minecraft:spore_blossom", - "blockRuntimeId" : 7312 - }, - { - "id" : "minecraft:azalea", - "blockRuntimeId" : 6888 - }, - { - "id" : "minecraft:flowering_azalea", - "blockRuntimeId" : 5477 - }, - { - "id" : "minecraft:glow_lichen", - "blockRuntimeId" : 5684 - }, - { - "id" : "minecraft:amethyst_block", - "blockRuntimeId" : 290 - }, - { - "id" : "minecraft:budding_amethyst", - "blockRuntimeId" : 7002 - }, - { - "id" : "minecraft:amethyst_cluster", - "blockRuntimeId" : 7810 - }, - { - "id" : "minecraft:large_amethyst_bud", - "blockRuntimeId" : 4728 - }, - { - "id" : "minecraft:medium_amethyst_bud", - "blockRuntimeId" : 4376 - }, - { - "id" : "minecraft:small_amethyst_bud", - "blockRuntimeId" : 304 - }, - { - "id" : "minecraft:tuff", - "blockRuntimeId" : 347 - }, - { - "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" : 3546 - }, - { - "id" : "minecraft:red_mushroom", - "blockRuntimeId" : 4585 - }, - { - "id" : "minecraft:crimson_fungus", - "blockRuntimeId" : 7753 - }, - { - "id" : "minecraft:warped_fungus", - "blockRuntimeId" : 287 - }, - { - "id" : "minecraft:brown_mushroom_block", - "blockRuntimeId" : 7362 - }, - { - "id" : "minecraft:red_mushroom_block", - "blockRuntimeId" : 3611 - }, - { - "id" : "minecraft:brown_mushroom_block", - "blockRuntimeId" : 7363 - }, - { - "id" : "minecraft:brown_mushroom_block", - "blockRuntimeId" : 7348 - }, - { - "id" : "minecraft:egg" - }, - { - "id" : "minecraft:sugar_cane" - }, - { - "id" : "minecraft:sugar" - }, - { - "id" : "minecraft:rotten_flesh" - }, - { - "id" : "minecraft:bone" - }, - { - "id" : "minecraft:web", - "blockRuntimeId" : 6713 - }, - { - "id" : "minecraft:spider_eye" - }, - { - "id" : "minecraft:mob_spawner", - "blockRuntimeId" : 401 - }, - { - "id" : "minecraft:monster_egg", - "blockRuntimeId" : 4144 - }, - { - "id" : "minecraft:monster_egg", - "blockRuntimeId" : 4145 - }, - { - "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:infested_deepslate", - "blockRuntimeId" : 4641 - }, - { - "id" : "minecraft:dragon_egg", - "blockRuntimeId" : 7271 - }, - { - "id" : "minecraft:turtle_egg", - "blockRuntimeId" : 7997 - }, - { - "id" : "minecraft:frog_spawn", - "blockRuntimeId" : 4399 - }, - { - "id" : "minecraft:pearlescent_froglight", - "blockRuntimeId" : 6435 - }, - { - "id" : "minecraft:verdant_froglight", - "blockRuntimeId" : 6481 - }, - { - "id" : "minecraft:ochre_froglight", - "blockRuntimeId" : 3510 - }, - { - "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: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" : 428 - }, - { - "id" : "minecraft:crying_obsidian", - "blockRuntimeId" : 6722 - }, - { - "id" : "minecraft:bedrock", - "blockRuntimeId" : 7017 - }, - { - "id" : "minecraft:soul_sand", - "blockRuntimeId" : 5831 - }, - { - "id" : "minecraft:netherrack", - "blockRuntimeId" : 7037 - }, - { - "id" : "minecraft:magma", - "blockRuntimeId" : 8009 - }, - { - "id" : "minecraft:nether_wart" - }, - { - "id" : "minecraft:end_stone", - "blockRuntimeId" : 3836 - }, - { - "id" : "minecraft:chorus_flower", - "blockRuntimeId" : 4530 - }, - { - "id" : "minecraft:chorus_plant", - "blockRuntimeId" : 5505 - }, - { - "id" : "minecraft:chorus_fruit" - }, - { - "id" : "minecraft:popped_chorus_fruit" - }, - { - "id" : "minecraft:sponge", - "blockRuntimeId" : 629 - }, - { - "id" : "minecraft:sponge", - "blockRuntimeId" : 630 - }, - { - "id" : "minecraft:coral_block", - "blockRuntimeId" : 5237 - }, - { - "id" : "minecraft:coral_block", - "blockRuntimeId" : 5238 - }, - { - "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:sculk", - "blockRuntimeId" : 7036 - }, - { - "id" : "minecraft:sculk_vein", - "blockRuntimeId" : 7132 - }, - { - "id" : "minecraft:sculk_catalyst", - "blockRuntimeId" : 3613 - }, - { - "id" : "minecraft:sculk_shrieker", - "blockRuntimeId" : 219 - }, - { - "id" : "minecraft:sculk_sensor", - "blockRuntimeId" : 4389 - }, - { - "id" : "minecraft:reinforced_deepslate", - "blockRuntimeId" : 5832 - }, - { - "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" : 724 - }, - { - "id" : "minecraft:soul_torch", - "blockRuntimeId" : 4644 - }, - { - "id" : "minecraft:sea_pickle", - "blockRuntimeId" : 5855 - }, - { - "id" : "minecraft:lantern", - "blockRuntimeId" : 7074 - }, - { - "id" : "minecraft:soul_lantern", - "blockRuntimeId" : 5749 - }, - { - "id" : "minecraft:candle", - "blockRuntimeId" : 7403 - }, - { - "id" : "minecraft:white_candle", - "blockRuntimeId" : 5300 - }, - { - "id" : "minecraft:orange_candle", - "blockRuntimeId" : 362 - }, - { - "id" : "minecraft:magenta_candle", - "blockRuntimeId" : 418 - }, - { - "id" : "minecraft:light_blue_candle", - "blockRuntimeId" : 4569 - }, - { - "id" : "minecraft:yellow_candle", - "blockRuntimeId" : 6192 - }, - { - "id" : "minecraft:lime_candle", - "blockRuntimeId" : 6368 - }, - { - "id" : "minecraft:pink_candle", - "blockRuntimeId" : 7370 - }, - { - "id" : "minecraft:gray_candle", - "blockRuntimeId" : 939 - }, - { - "id" : "minecraft:light_gray_candle", - "blockRuntimeId" : 6224 - }, - { - "id" : "minecraft:cyan_candle", - "blockRuntimeId" : 7726 - }, - { - "id" : "minecraft:purple_candle", - "blockRuntimeId" : 7038 - }, - { - "id" : "minecraft:blue_candle" - }, - { - "id" : "minecraft:brown_candle", - "blockRuntimeId" : 5875 - }, - { - "id" : "minecraft:green_candle", - "blockRuntimeId" : 686 - }, - { - "id" : "minecraft:red_candle", - "blockRuntimeId" : 4681 - }, - { - "id" : "minecraft:black_candle", - "blockRuntimeId" : 171 - }, - { - "id" : "minecraft:crafting_table", - "blockRuntimeId" : 5854 - }, - { - "id" : "minecraft:cartography_table", - "blockRuntimeId" : 8288 - }, - { - "id" : "minecraft:fletching_table", - "blockRuntimeId" : 5833 - }, - { - "id" : "minecraft:smithing_table", - "blockRuntimeId" : 3726 - }, - { - "id" : "minecraft:beehive", - "blockRuntimeId" : 6108 - }, - { - "id" : "minecraft:campfire" - }, - { - "id" : "minecraft:soul_campfire" - }, - { - "id" : "minecraft:furnace", - "blockRuntimeId" : 7802 - }, - { - "id" : "minecraft:blast_furnace", - "blockRuntimeId" : 7567 - }, - { - "id" : "minecraft:smoker", - "blockRuntimeId" : 647 - }, - { - "id" : "minecraft:respawn_anchor", - "blockRuntimeId" : 681 - }, - { - "id" : "minecraft:brewing_stand" - }, - { - "id" : "minecraft:anvil", - "blockRuntimeId" : 6634 - }, - { - "id" : "minecraft:anvil", - "blockRuntimeId" : 6638 - }, - { - "id" : "minecraft:anvil", - "blockRuntimeId" : 6642 - }, - { - "id" : "minecraft:grindstone", - "blockRuntimeId" : 8039 - }, - { - "id" : "minecraft:enchanting_table", - "blockRuntimeId" : 6723 - }, - { - "id" : "minecraft:bookshelf", - "blockRuntimeId" : 6671 - }, - { - "id" : "minecraft:lectern", - "blockRuntimeId" : 6940 - }, - { - "id" : "minecraft:cauldron" - }, - { - "id" : "minecraft:composter", - "blockRuntimeId" : 5415 - }, - { - "id" : "minecraft:chest", - "blockRuntimeId" : 7115 - }, - { - "id" : "minecraft:trapped_chest", - "blockRuntimeId" : 5583 - }, - { - "id" : "minecraft:ender_chest", - "blockRuntimeId" : 4369 - }, - { - "id" : "minecraft:barrel", - "blockRuntimeId" : 4518 - }, - { - "id" : "minecraft:undyed_shulker_box", - "blockRuntimeId" : 3681 - }, - { - "id" : "minecraft:shulker_box", - "blockRuntimeId" : 5316 - }, - { - "id" : "minecraft:shulker_box", - "blockRuntimeId" : 5324 - }, - { - "id" : "minecraft:shulker_box", - "blockRuntimeId" : 5323 - }, - { - "id" : "minecraft:shulker_box", - "blockRuntimeId" : 5331 - }, - { - "id" : "minecraft:shulker_box", - "blockRuntimeId" : 5328 - }, - { - "id" : "minecraft:shulker_box", - "blockRuntimeId" : 5330 - }, - { - "id" : "minecraft:shulker_box", - "blockRuntimeId" : 5317 - }, - { - "id" : "minecraft:shulker_box", - "blockRuntimeId" : 5320 - }, - { - "id" : "minecraft:shulker_box", - "blockRuntimeId" : 5321 - }, - { - "id" : "minecraft:shulker_box", - "blockRuntimeId" : 5329 - }, - { - "id" : "minecraft:shulker_box", - "blockRuntimeId" : 5325 - }, - { - "id" : "minecraft:shulker_box", - "blockRuntimeId" : 5319 - }, - { - "id" : "minecraft:shulker_box", - "blockRuntimeId" : 5327 - }, - { - "id" : "minecraft:shulker_box", - "blockRuntimeId" : 5326 - }, - { - "id" : "minecraft:shulker_box", - "blockRuntimeId" : 5318 - }, - { - "id" : "minecraft:shulker_box", - "blockRuntimeId" : 5322 - }, - { - "id" : "minecraft:armor_stand" - }, - { - "id" : "minecraft:noteblock", - "blockRuntimeId" : 346 - }, - { - "id" : "minecraft:jukebox", - "blockRuntimeId" : 4874 - }, - { - "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" : 3885 - }, - { - "id" : "minecraft:redstone_lamp", - "blockRuntimeId" : 251 - }, - { - "id" : "minecraft:sea_lantern", - "blockRuntimeId" : 7546 - }, - { - "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" : 6908 - }, - { - "id" : "minecraft:conduit", - "blockRuntimeId" : 4232 - }, - { - "id" : "minecraft:stonecutter_block", - "blockRuntimeId" : 7574 - }, - { - "id" : "minecraft:end_portal_frame", - "blockRuntimeId" : 6077 - }, - { - "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" : 5891 - }, - { - "id" : "minecraft:lightning_rod", - "blockRuntimeId" : 1176 - }, - { - "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" : 3920 - }, - { - "id" : "minecraft:golden_rail", - "blockRuntimeId" : 5332 - }, - { - "id" : "minecraft:detector_rail", - "blockRuntimeId" : 4132 - }, - { - "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" : 3776 - }, - { - "id" : "minecraft:redstone_torch", - "blockRuntimeId" : 3525 - }, - { - "id" : "minecraft:lever", - "blockRuntimeId" : 6514 - }, - { - "id" : "minecraft:wooden_button", - "blockRuntimeId" : 6391 - }, - { - "id" : "minecraft:spruce_button", - "blockRuntimeId" : 4321 - }, - { - "id" : "minecraft:birch_button", - "blockRuntimeId" : 7766 - }, - { - "id" : "minecraft:jungle_button", - "blockRuntimeId" : 116 - }, - { - "id" : "minecraft:acacia_button", - "blockRuntimeId" : 7231 - }, - { - "id" : "minecraft:dark_oak_button", - "blockRuntimeId" : 93 - }, - { - "id" : "minecraft:mangrove_button", - "blockRuntimeId" : 7062 - }, - { - "id" : "minecraft:stone_button", - "blockRuntimeId" : 596 - }, - { - "id" : "minecraft:crimson_button", - "blockRuntimeId" : 4432 - }, - { - "id" : "minecraft:warped_button", - "blockRuntimeId" : 7250 - }, - { - "id" : "minecraft:polished_blackstone_button", - "blockRuntimeId" : 7790 - }, - { - "id" : "minecraft:tripwire_hook", - "blockRuntimeId" : 5914 - }, - { - "id" : "minecraft:wooden_pressure_plate", - "blockRuntimeId" : 8063 - }, - { - "id" : "minecraft:spruce_pressure_plate", - "blockRuntimeId" : 3759 - }, - { - "id" : "minecraft:birch_pressure_plate", - "blockRuntimeId" : 3555 - }, - { - "id" : "minecraft:jungle_pressure_plate", - "blockRuntimeId" : 3635 - }, - { - "id" : "minecraft:acacia_pressure_plate", - "blockRuntimeId" : 5247 - }, - { - "id" : "minecraft:dark_oak_pressure_plate", - "blockRuntimeId" : 5956 - }, - { - "id" : "minecraft:mangrove_pressure_plate", - "blockRuntimeId" : 3869 - }, - { - "id" : "minecraft:crimson_pressure_plate", - "blockRuntimeId" : 8268 - }, - { - "id" : "minecraft:warped_pressure_plate", - "blockRuntimeId" : 256 - }, - { - "id" : "minecraft:stone_pressure_plate", - "blockRuntimeId" : 3886 - }, - { - "id" : "minecraft:light_weighted_pressure_plate", - "blockRuntimeId" : 3665 - }, - { - "id" : "minecraft:heavy_weighted_pressure_plate", - "blockRuntimeId" : 1160 - }, - { - "id" : "minecraft:polished_blackstone_pressure_plate", - "blockRuntimeId" : 6232 - }, - { - "id" : "minecraft:observer", - "blockRuntimeId" : 3513 - }, - { - "id" : "minecraft:daylight_detector", - "blockRuntimeId" : 4197 - }, - { - "id" : "minecraft:repeater" - }, - { - "id" : "minecraft:comparator" - }, - { - "id" : "minecraft:hopper" - }, - { - "id" : "minecraft:dropper", - "blockRuntimeId" : 7385 - }, - { - "id" : "minecraft:dispenser", - "blockRuntimeId" : 8013 - }, - { - "id" : "minecraft:piston", - "blockRuntimeId" : 922 - }, - { - "id" : "minecraft:sticky_piston", - "blockRuntimeId" : 4364 - }, - { - "id" : "minecraft:tnt", - "blockRuntimeId" : 6707 - }, - { - "id" : "minecraft:name_tag" - }, - { - "id" : "minecraft:loom", - "blockRuntimeId" : 3826 - }, - { - "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" : 6390 - }, - { - "id" : "minecraft:lodestone_compass" - } - ] -} \ No newline at end of file diff --git a/core/src/main/resources/bedrock/creative_items.1_19_10.json b/core/src/main/resources/bedrock/creative_items.1_19_50.json similarity index 82% rename from core/src/main/resources/bedrock/creative_items.1_19_10.json rename to core/src/main/resources/bedrock/creative_items.1_19_50.json index 50e09d830..4ed5f8194 100644 --- a/core/src/main/resources/bedrock/creative_items.1_19_10.json +++ b/core/src/main/resources/bedrock/creative_items.1_19_50.json @@ -2,167 +2,179 @@ "items" : [ { "id" : "minecraft:planks", - "blockRuntimeId" : 6071 + "blockRuntimeId" : 9805 }, { "id" : "minecraft:planks", - "blockRuntimeId" : 6072 + "blockRuntimeId" : 9806 }, { "id" : "minecraft:planks", - "blockRuntimeId" : 6073 + "blockRuntimeId" : 9807 }, { "id" : "minecraft:planks", - "blockRuntimeId" : 6074 + "blockRuntimeId" : 9808 }, { "id" : "minecraft:planks", - "blockRuntimeId" : 6075 + "blockRuntimeId" : 9809 }, { "id" : "minecraft:planks", - "blockRuntimeId" : 6076 + "blockRuntimeId" : 9810 }, { "id" : "minecraft:mangrove_planks", - "blockRuntimeId" : 947 + "blockRuntimeId" : 1570 + }, + { + "id" : "minecraft:bamboo_planks", + "blockRuntimeId" : 8202 + }, + { + "id" : "minecraft:bamboo_mosaic", + "blockRuntimeId" : 12438 }, { "id" : "minecraft:crimson_planks", - "blockRuntimeId" : 4850 + "blockRuntimeId" : 7399 }, { "id" : "minecraft:warped_planks", - "blockRuntimeId" : 920 + "blockRuntimeId" : 1543 }, { "id" : "minecraft:cobblestone_wall", - "blockRuntimeId" : 1182 + "blockRuntimeId" : 1805 }, { "id" : "minecraft:cobblestone_wall", - "blockRuntimeId" : 1183 + "blockRuntimeId" : 1806 }, { "id" : "minecraft:cobblestone_wall", - "blockRuntimeId" : 1184 + "blockRuntimeId" : 1807 }, { "id" : "minecraft:cobblestone_wall", - "blockRuntimeId" : 1185 + "blockRuntimeId" : 1808 }, { "id" : "minecraft:cobblestone_wall", - "blockRuntimeId" : 1186 + "blockRuntimeId" : 1809 }, { "id" : "minecraft:cobblestone_wall", - "blockRuntimeId" : 1187 + "blockRuntimeId" : 1810 }, { "id" : "minecraft:cobblestone_wall", - "blockRuntimeId" : 1194 + "blockRuntimeId" : 1817 }, { "id" : "minecraft:cobblestone_wall", - "blockRuntimeId" : 1189 + "blockRuntimeId" : 1812 }, { "id" : "minecraft:cobblestone_wall", - "blockRuntimeId" : 1190 + "blockRuntimeId" : 1813 }, { "id" : "minecraft:cobblestone_wall", - "blockRuntimeId" : 1188 + "blockRuntimeId" : 1811 }, { "id" : "minecraft:cobblestone_wall", - "blockRuntimeId" : 1191 + "blockRuntimeId" : 1814 }, { "id" : "minecraft:cobblestone_wall", - "blockRuntimeId" : 1195 + "blockRuntimeId" : 1818 }, { "id" : "minecraft:cobblestone_wall", - "blockRuntimeId" : 1192 + "blockRuntimeId" : 1815 }, { "id" : "minecraft:cobblestone_wall", - "blockRuntimeId" : 1193 + "blockRuntimeId" : 1816 }, { "id" : "minecraft:blackstone_wall", - "blockRuntimeId" : 3930 + "blockRuntimeId" : 5707 }, { "id" : "minecraft:polished_blackstone_wall", - "blockRuntimeId" : 6724 + "blockRuntimeId" : 10496 }, { "id" : "minecraft:polished_blackstone_brick_wall", - "blockRuntimeId" : 971 + "blockRuntimeId" : 1594 }, { "id" : "minecraft:cobbled_deepslate_wall", - "blockRuntimeId" : 8082 + "blockRuntimeId" : 12260 }, { "id" : "minecraft:deepslate_tile_wall", - "blockRuntimeId" : 5071 + "blockRuntimeId" : 7636 }, { "id" : "minecraft:polished_deepslate_wall", - "blockRuntimeId" : 7817 + "blockRuntimeId" : 11995 }, { "id" : "minecraft:deepslate_brick_wall", - "blockRuntimeId" : 429 + "blockRuntimeId" : 659 }, { "id" : "minecraft:mud_brick_wall", - "blockRuntimeId" : 730 + "blockRuntimeId" : 1353 }, { "id" : "minecraft:fence", - "blockRuntimeId" : 7364 + "blockRuntimeId" : 11542 }, { "id" : "minecraft:fence", - "blockRuntimeId" : 7365 + "blockRuntimeId" : 11543 }, { "id" : "minecraft:fence", - "blockRuntimeId" : 7366 + "blockRuntimeId" : 11544 }, { "id" : "minecraft:fence", - "blockRuntimeId" : 7367 + "blockRuntimeId" : 11545 }, { "id" : "minecraft:fence", - "blockRuntimeId" : 7368 + "blockRuntimeId" : 11546 }, { "id" : "minecraft:fence", - "blockRuntimeId" : 7369 + "blockRuntimeId" : 11547 }, { "id" : "minecraft:mangrove_fence", - "blockRuntimeId" : 6633 + "blockRuntimeId" : 10405 + }, + { + "id" : "minecraft:bamboo_fence", + "blockRuntimeId" : 863 }, { "id" : "minecraft:nether_brick_fence", - "blockRuntimeId" : 4290 + "blockRuntimeId" : 6071 }, { "id" : "minecraft:crimson_fence", - "blockRuntimeId" : 7996 + "blockRuntimeId" : 12174 }, { "id" : "minecraft:warped_fence", - "blockRuntimeId" : 5853 + "blockRuntimeId" : 8819 }, { "id" : "minecraft:fence_gate", @@ -170,47 +182,51 @@ }, { "id" : "minecraft:spruce_fence_gate", - "blockRuntimeId" : 6584 + "blockRuntimeId" : 10356 }, { "id" : "minecraft:birch_fence_gate", - "blockRuntimeId" : 3777 + "blockRuntimeId" : 5170 }, { "id" : "minecraft:jungle_fence_gate", - "blockRuntimeId" : 5365 + "blockRuntimeId" : 7946 }, { "id" : "minecraft:acacia_fence_gate", - "blockRuntimeId" : 7586 + "blockRuntimeId" : 11764 }, { "id" : "minecraft:dark_oak_fence_gate", - "blockRuntimeId" : 4173 + "blockRuntimeId" : 5950 }, { "id" : "minecraft:mangrove_fence_gate", - "blockRuntimeId" : 4625 + "blockRuntimeId" : 6406 + }, + { + "id" : "minecraft:bamboo_fence_gate", + "blockRuntimeId" : 7611 }, { "id" : "minecraft:crimson_fence_gate", - "blockRuntimeId" : 4661 + "blockRuntimeId" : 6826 }, { "id" : "minecraft:warped_fence_gate", - "blockRuntimeId" : 5399 + "blockRuntimeId" : 7980 }, { "id" : "minecraft:normal_stone_stairs", - "blockRuntimeId" : 633 + "blockRuntimeId" : 864 }, { "id" : "minecraft:stone_stairs", - "blockRuntimeId" : 3708 + "blockRuntimeId" : 5101 }, { "id" : "minecraft:mossy_cobblestone_stairs", - "blockRuntimeId" : 4092 + "blockRuntimeId" : 5869 }, { "id" : "minecraft:oak_stairs", @@ -222,75 +238,83 @@ }, { "id" : "minecraft:birch_stairs", - "blockRuntimeId" : 7003 + "blockRuntimeId" : 10781 }, { "id" : "minecraft:jungle_stairs", - "blockRuntimeId" : 6967 + "blockRuntimeId" : 10745 }, { "id" : "minecraft:acacia_stairs", - "blockRuntimeId" : 6200 + "blockRuntimeId" : 9950 }, { "id" : "minecraft:dark_oak_stairs", - "blockRuntimeId" : 5063 + "blockRuntimeId" : 7628 }, { "id" : "minecraft:mangrove_stairs", - "blockRuntimeId" : 4595 + "blockRuntimeId" : 6376 + }, + { + "id" : "minecraft:bamboo_stairs", + "blockRuntimeId" : 1339 + }, + { + "id" : "minecraft:bamboo_mosaic_stairs", + "blockRuntimeId" : 9958 }, { "id" : "minecraft:stone_brick_stairs", - "blockRuntimeId" : 931 + "blockRuntimeId" : 1554 }, { "id" : "minecraft:mossy_stone_brick_stairs", - "blockRuntimeId" : 5883 + "blockRuntimeId" : 9233 }, { "id" : "minecraft:sandstone_stairs", - "blockRuntimeId" : 3587 + "blockRuntimeId" : 4980 }, { "id" : "minecraft:smooth_sandstone_stairs", - "blockRuntimeId" : 3627 + "blockRuntimeId" : 5020 }, { "id" : "minecraft:red_sandstone_stairs", - "blockRuntimeId" : 5350 + "blockRuntimeId" : 7931 }, { "id" : "minecraft:smooth_red_sandstone_stairs", - "blockRuntimeId" : 5546 + "blockRuntimeId" : 8127 }, { "id" : "minecraft:granite_stairs", - "blockRuntimeId" : 3537 + "blockRuntimeId" : 4546 }, { "id" : "minecraft:polished_granite_stairs", - "blockRuntimeId" : 4150 + "blockRuntimeId" : 5927 }, { "id" : "minecraft:diorite_stairs", - "blockRuntimeId" : 4391 + "blockRuntimeId" : 6172 }, { "id" : "minecraft:polished_diorite_stairs", - "blockRuntimeId" : 6714 + "blockRuntimeId" : 10486 }, { "id" : "minecraft:andesite_stairs", - "blockRuntimeId" : 5308 + "blockRuntimeId" : 7889 }, { "id" : "minecraft:polished_andesite_stairs", - "blockRuntimeId" : 7028 + "blockRuntimeId" : 10806 }, { "id" : "minecraft:brick_stairs", - "blockRuntimeId" : 6530 + "blockRuntimeId" : 10302 }, { "id" : "minecraft:nether_brick_stairs", @@ -298,31 +322,31 @@ }, { "id" : "minecraft:red_nether_brick_stairs", - "blockRuntimeId" : 6602 + "blockRuntimeId" : 10374 }, { "id" : "minecraft:end_brick_stairs", - "blockRuntimeId" : 6382 + "blockRuntimeId" : 10140 }, { "id" : "minecraft:quartz_stairs", - "blockRuntimeId" : 4767 + "blockRuntimeId" : 6932 }, { "id" : "minecraft:smooth_quartz_stairs", - "blockRuntimeId" : 7700 + "blockRuntimeId" : 11878 }, { "id" : "minecraft:purpur_stairs", - "blockRuntimeId" : 7755 + "blockRuntimeId" : 11933 }, { "id" : "minecraft:prismarine_stairs", - "blockRuntimeId" : 7263 + "blockRuntimeId" : 11441 }, { "id" : "minecraft:dark_prismarine_stairs", - "blockRuntimeId" : 7430 + "blockRuntimeId" : 11608 }, { "id" : "minecraft:prismarine_bricks_stairs", @@ -330,55 +354,55 @@ }, { "id" : "minecraft:crimson_stairs", - "blockRuntimeId" : 6280 + "blockRuntimeId" : 10038 }, { "id" : "minecraft:warped_stairs", - "blockRuntimeId" : 3718 + "blockRuntimeId" : 5111 }, { "id" : "minecraft:blackstone_stairs", - "blockRuntimeId" : 7019 + "blockRuntimeId" : 10797 }, { "id" : "minecraft:polished_blackstone_stairs", - "blockRuntimeId" : 4297 + "blockRuntimeId" : 6078 }, { "id" : "minecraft:polished_blackstone_brick_stairs", - "blockRuntimeId" : 4477 + "blockRuntimeId" : 6258 }, { "id" : "minecraft:cut_copper_stairs", - "blockRuntimeId" : 4604 + "blockRuntimeId" : 6385 }, { "id" : "minecraft:exposed_cut_copper_stairs", - "blockRuntimeId" : 4587 + "blockRuntimeId" : 6368 }, { "id" : "minecraft:weathered_cut_copper_stairs", - "blockRuntimeId" : 4305 + "blockRuntimeId" : 6086 }, { "id" : "minecraft:oxidized_cut_copper_stairs", - "blockRuntimeId" : 351 + "blockRuntimeId" : 581 }, { "id" : "minecraft:waxed_cut_copper_stairs", - "blockRuntimeId" : 393 + "blockRuntimeId" : 623 }, { "id" : "minecraft:waxed_exposed_cut_copper_stairs", - "blockRuntimeId" : 3902 + "blockRuntimeId" : 5679 }, { "id" : "minecraft:waxed_weathered_cut_copper_stairs", - "blockRuntimeId" : 6167 + "blockRuntimeId" : 9917 }, { "id" : "minecraft:waxed_oxidized_cut_copper_stairs", - "blockRuntimeId" : 5840 + "blockRuntimeId" : 8806 }, { "id" : "minecraft:cobbled_deepslate_stairs", @@ -386,19 +410,19 @@ }, { "id" : "minecraft:deepslate_tile_stairs", - "blockRuntimeId" : 4653 + "blockRuntimeId" : 6818 }, { "id" : "minecraft:polished_deepslate_stairs", - "blockRuntimeId" : 294 + "blockRuntimeId" : 522 }, { "id" : "minecraft:deepslate_brick_stairs", - "blockRuntimeId" : 7422 + "blockRuntimeId" : 11600 }, { "id" : "minecraft:mud_brick_stairs", - "blockRuntimeId" : 5522 + "blockRuntimeId" : 8103 }, { "id" : "minecraft:wooden_door" @@ -421,6 +445,9 @@ { "id" : "minecraft:mangrove_door" }, + { + "id" : "minecraft:bamboo_door" + }, { "id" : "minecraft:iron_door" }, @@ -436,371 +463,383 @@ }, { "id" : "minecraft:spruce_trapdoor", - "blockRuntimeId" : 6552 + "blockRuntimeId" : 10324 }, { "id" : "minecraft:birch_trapdoor", - "blockRuntimeId" : 6650 + "blockRuntimeId" : 10422 }, { "id" : "minecraft:jungle_trapdoor", - "blockRuntimeId" : 5381 + "blockRuntimeId" : 7962 }, { "id" : "minecraft:acacia_trapdoor", - "blockRuntimeId" : 5589 + "blockRuntimeId" : 8170 }, { "id" : "minecraft:dark_oak_trapdoor", - "blockRuntimeId" : 7502 + "blockRuntimeId" : 11680 }, { "id" : "minecraft:mangrove_trapdoor", - "blockRuntimeId" : 4485 + "blockRuntimeId" : 6266 + }, + { + "id" : "minecraft:bamboo_trapdoor", + "blockRuntimeId" : 7828 }, { "id" : "minecraft:iron_trapdoor", - "blockRuntimeId" : 321 + "blockRuntimeId" : 549 }, { "id" : "minecraft:crimson_trapdoor", - "blockRuntimeId" : 4333 + "blockRuntimeId" : 6114 }, { "id" : "minecraft:warped_trapdoor", - "blockRuntimeId" : 4733 + "blockRuntimeId" : 6898 }, { "id" : "minecraft:iron_bars", - "blockRuntimeId" : 4801 + "blockRuntimeId" : 6966 }, { "id" : "minecraft:glass", - "blockRuntimeId" : 6164 + "blockRuntimeId" : 9914 }, { "id" : "minecraft:stained_glass", - "blockRuntimeId" : 1133 + "blockRuntimeId" : 1756 }, { "id" : "minecraft:stained_glass", - "blockRuntimeId" : 1141 + "blockRuntimeId" : 1764 }, { "id" : "minecraft:stained_glass", - "blockRuntimeId" : 1140 + "blockRuntimeId" : 1763 }, { "id" : "minecraft:stained_glass", - "blockRuntimeId" : 1148 + "blockRuntimeId" : 1771 }, { "id" : "minecraft:stained_glass", - "blockRuntimeId" : 1145 + "blockRuntimeId" : 1768 }, { "id" : "minecraft:stained_glass", - "blockRuntimeId" : 1147 + "blockRuntimeId" : 1770 }, { "id" : "minecraft:stained_glass", - "blockRuntimeId" : 1134 + "blockRuntimeId" : 1757 }, { "id" : "minecraft:stained_glass", - "blockRuntimeId" : 1137 + "blockRuntimeId" : 1760 }, { "id" : "minecraft:stained_glass", - "blockRuntimeId" : 1138 + "blockRuntimeId" : 1761 }, { "id" : "minecraft:stained_glass", - "blockRuntimeId" : 1146 + "blockRuntimeId" : 1769 }, { "id" : "minecraft:stained_glass", - "blockRuntimeId" : 1142 + "blockRuntimeId" : 1765 }, { "id" : "minecraft:stained_glass", - "blockRuntimeId" : 1136 + "blockRuntimeId" : 1759 }, { "id" : "minecraft:stained_glass", - "blockRuntimeId" : 1144 + "blockRuntimeId" : 1767 }, { "id" : "minecraft:stained_glass", - "blockRuntimeId" : 1143 + "blockRuntimeId" : 1766 }, { "id" : "minecraft:stained_glass", - "blockRuntimeId" : 1135 + "blockRuntimeId" : 1758 }, { "id" : "minecraft:stained_glass", - "blockRuntimeId" : 1139 + "blockRuntimeId" : 1762 }, { "id" : "minecraft:tinted_glass", - "blockRuntimeId" : 5975 + "blockRuntimeId" : 9325 }, { "id" : "minecraft:glass_pane", - "blockRuntimeId" : 5233 + "blockRuntimeId" : 7798 }, { "id" : "minecraft:stained_glass_pane", - "blockRuntimeId" : 4852 + "blockRuntimeId" : 7401 }, { "id" : "minecraft:stained_glass_pane", - "blockRuntimeId" : 4860 + "blockRuntimeId" : 7409 }, { "id" : "minecraft:stained_glass_pane", - "blockRuntimeId" : 4859 + "blockRuntimeId" : 7408 }, { "id" : "minecraft:stained_glass_pane", - "blockRuntimeId" : 4867 + "blockRuntimeId" : 7416 }, { "id" : "minecraft:stained_glass_pane", - "blockRuntimeId" : 4864 + "blockRuntimeId" : 7413 }, { "id" : "minecraft:stained_glass_pane", - "blockRuntimeId" : 4866 + "blockRuntimeId" : 7415 }, { "id" : "minecraft:stained_glass_pane", - "blockRuntimeId" : 4853 + "blockRuntimeId" : 7402 }, { "id" : "minecraft:stained_glass_pane", - "blockRuntimeId" : 4856 + "blockRuntimeId" : 7405 }, { "id" : "minecraft:stained_glass_pane", - "blockRuntimeId" : 4857 + "blockRuntimeId" : 7406 }, { "id" : "minecraft:stained_glass_pane", - "blockRuntimeId" : 4865 + "blockRuntimeId" : 7414 }, { "id" : "minecraft:stained_glass_pane", - "blockRuntimeId" : 4861 + "blockRuntimeId" : 7410 }, { "id" : "minecraft:stained_glass_pane", - "blockRuntimeId" : 4855 + "blockRuntimeId" : 7404 }, { "id" : "minecraft:stained_glass_pane", - "blockRuntimeId" : 4863 + "blockRuntimeId" : 7412 }, { "id" : "minecraft:stained_glass_pane", - "blockRuntimeId" : 4862 + "blockRuntimeId" : 7411 }, { "id" : "minecraft:stained_glass_pane", - "blockRuntimeId" : 4854 + "blockRuntimeId" : 7403 }, { "id" : "minecraft:stained_glass_pane", - "blockRuntimeId" : 4858 + "blockRuntimeId" : 7407 }, { "id" : "minecraft:ladder", - "blockRuntimeId" : 8262 + "blockRuntimeId" : 12441 }, { "id" : "minecraft:scaffolding", - "blockRuntimeId" : 3571 + "blockRuntimeId" : 4964 }, { "id" : "minecraft:stone_block_slab", - "blockRuntimeId" : 4270 + "blockRuntimeId" : 6049 }, { "id" : "minecraft:stone_block_slab4", - "blockRuntimeId" : 5822 + "blockRuntimeId" : 8404 }, { "id" : "minecraft:stone_block_slab", - "blockRuntimeId" : 4273 + "blockRuntimeId" : 6052 }, { "id" : "minecraft:stone_block_slab2", - "blockRuntimeId" : 5793 + "blockRuntimeId" : 8375 }, { "id" : "minecraft:wooden_slab", - "blockRuntimeId" : 5270 + "blockRuntimeId" : 7851 }, { "id" : "minecraft:wooden_slab", - "blockRuntimeId" : 5271 + "blockRuntimeId" : 7852 }, { "id" : "minecraft:wooden_slab", - "blockRuntimeId" : 5272 + "blockRuntimeId" : 7853 }, { "id" : "minecraft:wooden_slab", - "blockRuntimeId" : 5273 + "blockRuntimeId" : 7854 }, { "id" : "minecraft:wooden_slab", - "blockRuntimeId" : 5274 + "blockRuntimeId" : 7855 }, { "id" : "minecraft:wooden_slab", - "blockRuntimeId" : 5275 + "blockRuntimeId" : 7856 }, { "id" : "minecraft:mangrove_slab", - "blockRuntimeId" : 1149 + "blockRuntimeId" : 1772 + }, + { + "id" : "minecraft:bamboo_slab", + "blockRuntimeId" : 10300 + }, + { + "id" : "minecraft:bamboo_mosaic_slab", + "blockRuntimeId" : 4081 }, { "id" : "minecraft:stone_block_slab", - "blockRuntimeId" : 4275 + "blockRuntimeId" : 6054 }, { "id" : "minecraft:stone_block_slab4", - "blockRuntimeId" : 5820 + "blockRuntimeId" : 8402 }, { "id" : "minecraft:stone_block_slab", - "blockRuntimeId" : 4271 + "blockRuntimeId" : 6050 }, { "id" : "minecraft:stone_block_slab4", - "blockRuntimeId" : 5823 + "blockRuntimeId" : 8405 }, { "id" : "minecraft:stone_block_slab2", - "blockRuntimeId" : 5794 + "blockRuntimeId" : 8376 }, { "id" : "minecraft:stone_block_slab2", - "blockRuntimeId" : 5788 + "blockRuntimeId" : 8370 }, { "id" : "minecraft:stone_block_slab4", - "blockRuntimeId" : 5824 + "blockRuntimeId" : 8406 }, { "id" : "minecraft:stone_block_slab3", - "blockRuntimeId" : 5805 + "blockRuntimeId" : 8387 }, { "id" : "minecraft:stone_block_slab3", - "blockRuntimeId" : 5810 + "blockRuntimeId" : 8392 }, { "id" : "minecraft:stone_block_slab3", - "blockRuntimeId" : 5811 + "blockRuntimeId" : 8393 }, { "id" : "minecraft:stone_block_slab3", - "blockRuntimeId" : 5808 + "blockRuntimeId" : 8390 }, { "id" : "minecraft:stone_block_slab3", - "blockRuntimeId" : 5809 + "blockRuntimeId" : 8391 }, { "id" : "minecraft:stone_block_slab3", - "blockRuntimeId" : 5807 + "blockRuntimeId" : 8389 }, { "id" : "minecraft:stone_block_slab3", - "blockRuntimeId" : 5806 + "blockRuntimeId" : 8388 }, { "id" : "minecraft:stone_block_slab", - "blockRuntimeId" : 4274 - }, - { - "id" : "minecraft:stone_block_slab", - "blockRuntimeId" : 4277 - }, - { - "id" : "minecraft:stone_block_slab2", - "blockRuntimeId" : 5795 - }, - { - "id" : "minecraft:stone_block_slab3", - "blockRuntimeId" : 5804 - }, - { - "id" : "minecraft:stone_block_slab", - "blockRuntimeId" : 4276 - }, - { - "id" : "minecraft:stone_block_slab4", - "blockRuntimeId" : 5821 - }, - { - "id" : "minecraft:stone_block_slab2", - "blockRuntimeId" : 5789 - }, - { - "id" : "minecraft:stone_block_slab2", - "blockRuntimeId" : 5790 - }, - { - "id" : "minecraft:stone_block_slab2", - "blockRuntimeId" : 5791 - }, - { - "id" : "minecraft:stone_block_slab2", - "blockRuntimeId" : 5792 - }, - { - "id" : "minecraft:crimson_slab", - "blockRuntimeId" : 5900 - }, - { - "id" : "minecraft:warped_slab", - "blockRuntimeId" : 6484 - }, - { - "id" : "minecraft:blackstone_slab", - "blockRuntimeId" : 910 - }, - { - "id" : "minecraft:polished_blackstone_slab", - "blockRuntimeId" : 6018 - }, - { - "id" : "minecraft:polished_blackstone_brick_slab", - "blockRuntimeId" : 4192 - }, - { - "id" : "minecraft:cut_copper_slab", - "blockRuntimeId" : 5235 - }, - { - "id" : "minecraft:exposed_cut_copper_slab", - "blockRuntimeId" : 6600 - }, - { - "id" : "minecraft:weathered_cut_copper_slab", "blockRuntimeId" : 6053 }, + { + "id" : "minecraft:stone_block_slab", + "blockRuntimeId" : 6056 + }, + { + "id" : "minecraft:stone_block_slab2", + "blockRuntimeId" : 8377 + }, + { + "id" : "minecraft:stone_block_slab3", + "blockRuntimeId" : 8386 + }, + { + "id" : "minecraft:stone_block_slab", + "blockRuntimeId" : 6055 + }, + { + "id" : "minecraft:stone_block_slab4", + "blockRuntimeId" : 8403 + }, + { + "id" : "minecraft:stone_block_slab2", + "blockRuntimeId" : 8371 + }, + { + "id" : "minecraft:stone_block_slab2", + "blockRuntimeId" : 8372 + }, + { + "id" : "minecraft:stone_block_slab2", + "blockRuntimeId" : 8373 + }, + { + "id" : "minecraft:stone_block_slab2", + "blockRuntimeId" : 8374 + }, + { + "id" : "minecraft:crimson_slab", + "blockRuntimeId" : 9250 + }, + { + "id" : "minecraft:warped_slab", + "blockRuntimeId" : 10254 + }, + { + "id" : "minecraft:blackstone_slab", + "blockRuntimeId" : 1533 + }, + { + "id" : "minecraft:polished_blackstone_slab", + "blockRuntimeId" : 9752 + }, + { + "id" : "minecraft:polished_blackstone_brick_slab", + "blockRuntimeId" : 5971 + }, + { + "id" : "minecraft:cut_copper_slab", + "blockRuntimeId" : 7800 + }, + { + "id" : "minecraft:exposed_cut_copper_slab", + "blockRuntimeId" : 10372 + }, + { + "id" : "minecraft:weathered_cut_copper_slab", + "blockRuntimeId" : 9787 + }, { "id" : "minecraft:oxidized_cut_copper_slab", - "blockRuntimeId" : 5282 + "blockRuntimeId" : 7863 }, { "id" : "minecraft:waxed_cut_copper_slab", - "blockRuntimeId" : 7815 + "blockRuntimeId" : 11993 }, { "id" : "minecraft:waxed_exposed_cut_copper_slab", @@ -808,15 +847,15 @@ }, { "id" : "minecraft:waxed_weathered_cut_copper_slab", - "blockRuntimeId" : 6545 + "blockRuntimeId" : 10317 }, { "id" : "minecraft:waxed_oxidized_cut_copper_slab", - "blockRuntimeId" : 708 + "blockRuntimeId" : 1323 }, { "id" : "minecraft:cobbled_deepslate_slab", - "blockRuntimeId" : 7310 + "blockRuntimeId" : 11488 }, { "id" : "minecraft:polished_deepslate_slab", @@ -824,47 +863,47 @@ }, { "id" : "minecraft:deepslate_tile_slab", - "blockRuntimeId" : 4291 + "blockRuntimeId" : 6072 }, { "id" : "minecraft:deepslate_brick_slab", - "blockRuntimeId" : 3716 + "blockRuntimeId" : 5109 }, { "id" : "minecraft:mud_brick_slab", - "blockRuntimeId" : 3910 + "blockRuntimeId" : 5687 }, { "id" : "minecraft:brick_block", - "blockRuntimeId" : 4765 + "blockRuntimeId" : 6930 }, { "id" : "minecraft:chiseled_nether_bricks", - "blockRuntimeId" : 7249 + "blockRuntimeId" : 11427 }, { "id" : "minecraft:cracked_nether_bricks", - "blockRuntimeId" : 4552 + "blockRuntimeId" : 6333 }, { "id" : "minecraft:quartz_bricks", - "blockRuntimeId" : 6351 + "blockRuntimeId" : 10109 }, { "id" : "minecraft:stonebrick", - "blockRuntimeId" : 6547 + "blockRuntimeId" : 10319 }, { "id" : "minecraft:stonebrick", - "blockRuntimeId" : 6548 + "blockRuntimeId" : 10320 }, { "id" : "minecraft:stonebrick", - "blockRuntimeId" : 6549 + "blockRuntimeId" : 10321 }, { "id" : "minecraft:stonebrick", - "blockRuntimeId" : 6550 + "blockRuntimeId" : 10322 }, { "id" : "minecraft:end_bricks", @@ -872,47 +911,47 @@ }, { "id" : "minecraft:prismarine", - "blockRuntimeId" : 6087 + "blockRuntimeId" : 9837 }, { "id" : "minecraft:polished_blackstone_bricks", - "blockRuntimeId" : 4680 + "blockRuntimeId" : 6845 }, { "id" : "minecraft:cracked_polished_blackstone_bricks", - "blockRuntimeId" : 7214 + "blockRuntimeId" : 11376 }, { "id" : "minecraft:gilded_blackstone", - "blockRuntimeId" : 4586 + "blockRuntimeId" : 6367 }, { "id" : "minecraft:chiseled_polished_blackstone", - "blockRuntimeId" : 5062 + "blockRuntimeId" : 7627 }, { "id" : "minecraft:deepslate_tiles", - "blockRuntimeId" : 4581 + "blockRuntimeId" : 6362 }, { "id" : "minecraft:cracked_deepslate_tiles", - "blockRuntimeId" : 4160 + "blockRuntimeId" : 5937 }, { "id" : "minecraft:deepslate_bricks", - "blockRuntimeId" : 5464 + "blockRuntimeId" : 8045 }, { "id" : "minecraft:cracked_deepslate_bricks", - "blockRuntimeId" : 5364 + "blockRuntimeId" : 7945 }, { "id" : "minecraft:chiseled_deepslate", - "blockRuntimeId" : 5234 + "blockRuntimeId" : 7799 }, { "id" : "minecraft:cobblestone", - "blockRuntimeId" : 3615 + "blockRuntimeId" : 5008 }, { "id" : "minecraft:mossy_cobblestone", @@ -920,119 +959,119 @@ }, { "id" : "minecraft:cobbled_deepslate", - "blockRuntimeId" : 6670 + "blockRuntimeId" : 10442 }, { "id" : "minecraft:smooth_stone", - "blockRuntimeId" : 4582 + "blockRuntimeId" : 6363 }, { "id" : "minecraft:sandstone", - "blockRuntimeId" : 3653 + "blockRuntimeId" : 5046 }, { "id" : "minecraft:sandstone", - "blockRuntimeId" : 3654 + "blockRuntimeId" : 5047 }, { "id" : "minecraft:sandstone", - "blockRuntimeId" : 3655 + "blockRuntimeId" : 5048 }, { "id" : "minecraft:sandstone", - "blockRuntimeId" : 3656 + "blockRuntimeId" : 5049 }, { "id" : "minecraft:red_sandstone", - "blockRuntimeId" : 6580 + "blockRuntimeId" : 10352 }, { "id" : "minecraft:red_sandstone", - "blockRuntimeId" : 6581 + "blockRuntimeId" : 10353 }, { "id" : "minecraft:red_sandstone", - "blockRuntimeId" : 6582 + "blockRuntimeId" : 10354 }, { "id" : "minecraft:red_sandstone", - "blockRuntimeId" : 6583 + "blockRuntimeId" : 10355 }, { "id" : "minecraft:coal_block", - "blockRuntimeId" : 5398 - }, - { - "id" : "minecraft:dried_kelp_block", "blockRuntimeId" : 7979 }, + { + "id" : "minecraft:dried_kelp_block", + "blockRuntimeId" : 12157 + }, { "id" : "minecraft:gold_block", - "blockRuntimeId" : 291 + "blockRuntimeId" : 323 }, { "id" : "minecraft:iron_block", - "blockRuntimeId" : 8261 + "blockRuntimeId" : 12440 }, { "id" : "minecraft:copper_block", - "blockRuntimeId" : 4651 + "blockRuntimeId" : 6816 }, { "id" : "minecraft:exposed_copper", - "blockRuntimeId" : 593 + "blockRuntimeId" : 823 }, { "id" : "minecraft:weathered_copper", - "blockRuntimeId" : 8246 + "blockRuntimeId" : 12424 }, { "id" : "minecraft:oxidized_copper", - "blockRuntimeId" : 3553 + "blockRuntimeId" : 4946 }, { "id" : "minecraft:waxed_copper", - "blockRuntimeId" : 7734 + "blockRuntimeId" : 11912 }, { "id" : "minecraft:waxed_exposed_copper", - "blockRuntimeId" : 694 + "blockRuntimeId" : 1309 }, { "id" : "minecraft:waxed_weathered_copper", - "blockRuntimeId" : 707 + "blockRuntimeId" : 1322 }, { "id" : "minecraft:waxed_oxidized_copper", - "blockRuntimeId" : 7542 + "blockRuntimeId" : 11720 }, { "id" : "minecraft:cut_copper", - "blockRuntimeId" : 4689 + "blockRuntimeId" : 6854 }, { "id" : "minecraft:exposed_cut_copper", - "blockRuntimeId" : 6166 + "blockRuntimeId" : 9916 }, { "id" : "minecraft:weathered_cut_copper", - "blockRuntimeId" : 7197 + "blockRuntimeId" : 11359 }, { "id" : "minecraft:oxidized_cut_copper", - "blockRuntimeId" : 5478 + "blockRuntimeId" : 8059 }, { "id" : "minecraft:waxed_cut_copper", - "blockRuntimeId" : 7293 + "blockRuntimeId" : 11471 }, { "id" : "minecraft:waxed_exposed_cut_copper", - "blockRuntimeId" : 3809 + "blockRuntimeId" : 5202 }, { "id" : "minecraft:waxed_weathered_cut_copper", - "blockRuntimeId" : 4851 + "blockRuntimeId" : 7400 }, { "id" : "minecraft:waxed_oxidized_cut_copper", @@ -1040,7 +1079,7 @@ }, { "id" : "minecraft:emerald_block", - "blockRuntimeId" : 1159 + "blockRuntimeId" : 1782 }, { "id" : "minecraft:diamond_block", @@ -1048,67 +1087,67 @@ }, { "id" : "minecraft:lapis_block", - "blockRuntimeId" : 4286 + "blockRuntimeId" : 6065 }, { "id" : "minecraft:raw_iron_block", - "blockRuntimeId" : 8260 + "blockRuntimeId" : 12439 }, { "id" : "minecraft:raw_copper_block", - "blockRuntimeId" : 5269 + "blockRuntimeId" : 7850 }, { "id" : "minecraft:raw_gold_block", - "blockRuntimeId" : 361 + "blockRuntimeId" : 591 }, { "id" : "minecraft:quartz_block", - "blockRuntimeId" : 3696 + "blockRuntimeId" : 5089 }, { "id" : "minecraft:quartz_block", - "blockRuntimeId" : 3698 + "blockRuntimeId" : 5091 }, { "id" : "minecraft:quartz_block", - "blockRuntimeId" : 3697 + "blockRuntimeId" : 5090 }, { "id" : "minecraft:quartz_block", - "blockRuntimeId" : 3699 + "blockRuntimeId" : 5092 }, { "id" : "minecraft:prismarine", - "blockRuntimeId" : 6085 + "blockRuntimeId" : 9835 }, { "id" : "minecraft:prismarine", - "blockRuntimeId" : 6086 + "blockRuntimeId" : 9836 }, { "id" : "minecraft:slime", - "blockRuntimeId" : 4233 + "blockRuntimeId" : 6012 }, { "id" : "minecraft:honey_block", - "blockRuntimeId" : 892 + "blockRuntimeId" : 1515 }, { "id" : "minecraft:honeycomb_block", - "blockRuntimeId" : 4476 + "blockRuntimeId" : 6257 }, { "id" : "minecraft:hay_block", - "blockRuntimeId" : 695 + "blockRuntimeId" : 1310 }, { "id" : "minecraft:bone_block", - "blockRuntimeId" : 4234 + "blockRuntimeId" : 6013 }, { "id" : "minecraft:nether_brick", - "blockRuntimeId" : 7272 + "blockRuntimeId" : 11450 }, { "id" : "minecraft:red_nether_brick", @@ -1116,371 +1155,371 @@ }, { "id" : "minecraft:netherite_block", - "blockRuntimeId" : 3775 + "blockRuntimeId" : 5168 }, { "id" : "minecraft:lodestone", - "blockRuntimeId" : 8259 + "blockRuntimeId" : 12437 }, { "id" : "minecraft:wool", - "blockRuntimeId" : 3458 + "blockRuntimeId" : 4083 }, { "id" : "minecraft:wool", - "blockRuntimeId" : 3466 + "blockRuntimeId" : 4091 }, { "id" : "minecraft:wool", - "blockRuntimeId" : 3465 + "blockRuntimeId" : 4090 }, { "id" : "minecraft:wool", - "blockRuntimeId" : 3473 + "blockRuntimeId" : 4098 }, { "id" : "minecraft:wool", - "blockRuntimeId" : 3470 + "blockRuntimeId" : 4095 }, { "id" : "minecraft:wool", - "blockRuntimeId" : 3472 + "blockRuntimeId" : 4097 }, { "id" : "minecraft:wool", - "blockRuntimeId" : 3459 + "blockRuntimeId" : 4084 }, { "id" : "minecraft:wool", - "blockRuntimeId" : 3462 + "blockRuntimeId" : 4087 }, { "id" : "minecraft:wool", - "blockRuntimeId" : 3463 + "blockRuntimeId" : 4088 }, { "id" : "minecraft:wool", - "blockRuntimeId" : 3471 + "blockRuntimeId" : 4096 }, { "id" : "minecraft:wool", - "blockRuntimeId" : 3467 + "blockRuntimeId" : 4092 }, { "id" : "minecraft:wool", - "blockRuntimeId" : 3461 + "blockRuntimeId" : 4086 }, { "id" : "minecraft:wool", - "blockRuntimeId" : 3469 + "blockRuntimeId" : 4094 }, { "id" : "minecraft:wool", - "blockRuntimeId" : 3468 + "blockRuntimeId" : 4093 }, { "id" : "minecraft:wool", - "blockRuntimeId" : 3460 + "blockRuntimeId" : 4085 }, { "id" : "minecraft:wool", - "blockRuntimeId" : 3464 + "blockRuntimeId" : 4089 }, { "id" : "minecraft:carpet", - "blockRuntimeId" : 949 + "blockRuntimeId" : 1572 }, { "id" : "minecraft:carpet", - "blockRuntimeId" : 957 + "blockRuntimeId" : 1580 }, { "id" : "minecraft:carpet", - "blockRuntimeId" : 956 + "blockRuntimeId" : 1579 }, { "id" : "minecraft:carpet", - "blockRuntimeId" : 964 + "blockRuntimeId" : 1587 }, { "id" : "minecraft:carpet", - "blockRuntimeId" : 961 + "blockRuntimeId" : 1584 }, { "id" : "minecraft:carpet", - "blockRuntimeId" : 963 + "blockRuntimeId" : 1586 }, { "id" : "minecraft:carpet", - "blockRuntimeId" : 950 + "blockRuntimeId" : 1573 }, { "id" : "minecraft:carpet", - "blockRuntimeId" : 953 + "blockRuntimeId" : 1576 }, { "id" : "minecraft:carpet", - "blockRuntimeId" : 954 + "blockRuntimeId" : 1577 }, { "id" : "minecraft:carpet", - "blockRuntimeId" : 962 + "blockRuntimeId" : 1585 }, { "id" : "minecraft:carpet", - "blockRuntimeId" : 958 + "blockRuntimeId" : 1581 }, { "id" : "minecraft:carpet", - "blockRuntimeId" : 952 + "blockRuntimeId" : 1575 }, { "id" : "minecraft:carpet", - "blockRuntimeId" : 960 + "blockRuntimeId" : 1583 }, { "id" : "minecraft:carpet", - "blockRuntimeId" : 959 + "blockRuntimeId" : 1582 }, { "id" : "minecraft:carpet", - "blockRuntimeId" : 951 + "blockRuntimeId" : 1574 }, { "id" : "minecraft:carpet", - "blockRuntimeId" : 955 + "blockRuntimeId" : 1578 }, { "id" : "minecraft:concrete_powder", - "blockRuntimeId" : 6264 + "blockRuntimeId" : 10022 }, { "id" : "minecraft:concrete_powder", - "blockRuntimeId" : 6272 + "blockRuntimeId" : 10030 }, { "id" : "minecraft:concrete_powder", - "blockRuntimeId" : 6271 + "blockRuntimeId" : 10029 }, { "id" : "minecraft:concrete_powder", - "blockRuntimeId" : 6279 + "blockRuntimeId" : 10037 }, { "id" : "minecraft:concrete_powder", - "blockRuntimeId" : 6276 + "blockRuntimeId" : 10034 }, { "id" : "minecraft:concrete_powder", - "blockRuntimeId" : 6278 + "blockRuntimeId" : 10036 }, { "id" : "minecraft:concrete_powder", - "blockRuntimeId" : 6265 + "blockRuntimeId" : 10023 }, { "id" : "minecraft:concrete_powder", - "blockRuntimeId" : 6268 + "blockRuntimeId" : 10026 }, { "id" : "minecraft:concrete_powder", - "blockRuntimeId" : 6269 + "blockRuntimeId" : 10027 }, { "id" : "minecraft:concrete_powder", - "blockRuntimeId" : 6277 + "blockRuntimeId" : 10035 }, { "id" : "minecraft:concrete_powder", - "blockRuntimeId" : 6273 + "blockRuntimeId" : 10031 }, { "id" : "minecraft:concrete_powder", - "blockRuntimeId" : 6267 + "blockRuntimeId" : 10025 }, { "id" : "minecraft:concrete_powder", - "blockRuntimeId" : 6275 + "blockRuntimeId" : 10033 }, { "id" : "minecraft:concrete_powder", - "blockRuntimeId" : 6274 + "blockRuntimeId" : 10032 }, { "id" : "minecraft:concrete_powder", - "blockRuntimeId" : 6266 + "blockRuntimeId" : 10024 }, { "id" : "minecraft:concrete_powder", - "blockRuntimeId" : 6270 + "blockRuntimeId" : 10028 }, { "id" : "minecraft:concrete", - "blockRuntimeId" : 660 + "blockRuntimeId" : 1275 }, { "id" : "minecraft:concrete", - "blockRuntimeId" : 668 + "blockRuntimeId" : 1283 }, { "id" : "minecraft:concrete", - "blockRuntimeId" : 667 + "blockRuntimeId" : 1282 }, { "id" : "minecraft:concrete", - "blockRuntimeId" : 675 + "blockRuntimeId" : 1290 }, { "id" : "minecraft:concrete", - "blockRuntimeId" : 672 + "blockRuntimeId" : 1287 }, { "id" : "minecraft:concrete", - "blockRuntimeId" : 674 + "blockRuntimeId" : 1289 }, { "id" : "minecraft:concrete", - "blockRuntimeId" : 661 + "blockRuntimeId" : 1276 }, { "id" : "minecraft:concrete", - "blockRuntimeId" : 664 + "blockRuntimeId" : 1279 }, { "id" : "minecraft:concrete", - "blockRuntimeId" : 665 + "blockRuntimeId" : 1280 }, { "id" : "minecraft:concrete", - "blockRuntimeId" : 673 + "blockRuntimeId" : 1288 }, { "id" : "minecraft:concrete", - "blockRuntimeId" : 669 + "blockRuntimeId" : 1284 }, { "id" : "minecraft:concrete", - "blockRuntimeId" : 663 + "blockRuntimeId" : 1278 }, { "id" : "minecraft:concrete", - "blockRuntimeId" : 671 + "blockRuntimeId" : 1286 }, { "id" : "minecraft:concrete", - "blockRuntimeId" : 670 + "blockRuntimeId" : 1285 }, { "id" : "minecraft:concrete", - "blockRuntimeId" : 662 + "blockRuntimeId" : 1277 }, { "id" : "minecraft:concrete", - "blockRuntimeId" : 666 + "blockRuntimeId" : 1281 }, { "id" : "minecraft:clay", - "blockRuntimeId" : 7124 + "blockRuntimeId" : 10902 }, { "id" : "minecraft:hardened_clay", - "blockRuntimeId" : 641 + "blockRuntimeId" : 872 }, { "id" : "minecraft:stained_hardened_clay", - "blockRuntimeId" : 6176 + "blockRuntimeId" : 9926 }, { "id" : "minecraft:stained_hardened_clay", - "blockRuntimeId" : 6184 + "blockRuntimeId" : 9934 }, { "id" : "minecraft:stained_hardened_clay", - "blockRuntimeId" : 6183 + "blockRuntimeId" : 9933 }, { "id" : "minecraft:stained_hardened_clay", - "blockRuntimeId" : 6191 + "blockRuntimeId" : 9941 }, { "id" : "minecraft:stained_hardened_clay", - "blockRuntimeId" : 6188 + "blockRuntimeId" : 9938 }, { "id" : "minecraft:stained_hardened_clay", - "blockRuntimeId" : 6190 + "blockRuntimeId" : 9940 }, { "id" : "minecraft:stained_hardened_clay", - "blockRuntimeId" : 6177 + "blockRuntimeId" : 9927 }, { "id" : "minecraft:stained_hardened_clay", - "blockRuntimeId" : 6180 + "blockRuntimeId" : 9930 }, { "id" : "minecraft:stained_hardened_clay", - "blockRuntimeId" : 6181 + "blockRuntimeId" : 9931 }, { "id" : "minecraft:stained_hardened_clay", - "blockRuntimeId" : 6189 + "blockRuntimeId" : 9939 }, { "id" : "minecraft:stained_hardened_clay", - "blockRuntimeId" : 6185 + "blockRuntimeId" : 9935 }, { "id" : "minecraft:stained_hardened_clay", - "blockRuntimeId" : 6179 + "blockRuntimeId" : 9929 }, { "id" : "minecraft:stained_hardened_clay", - "blockRuntimeId" : 6187 + "blockRuntimeId" : 9937 }, { "id" : "minecraft:stained_hardened_clay", - "blockRuntimeId" : 6186 + "blockRuntimeId" : 9936 }, { "id" : "minecraft:stained_hardened_clay", - "blockRuntimeId" : 6178 + "blockRuntimeId" : 9928 }, { "id" : "minecraft:stained_hardened_clay", - "blockRuntimeId" : 6182 + "blockRuntimeId" : 9932 }, { "id" : "minecraft:white_glazed_terracotta", - "blockRuntimeId" : 5573 + "blockRuntimeId" : 8154 }, { "id" : "minecraft:silver_glazed_terracotta", - "blockRuntimeId" : 3531 + "blockRuntimeId" : 4540 }, { "id" : "minecraft:gray_glazed_terracotta", - "blockRuntimeId" : 8253 + "blockRuntimeId" : 12431 }, { "id" : "minecraft:black_glazed_terracotta", - "blockRuntimeId" : 5834 + "blockRuntimeId" : 8800 }, { "id" : "minecraft:brown_glazed_terracotta", - "blockRuntimeId" : 3547 + "blockRuntimeId" : 4940 }, { "id" : "minecraft:red_glazed_terracotta", - "blockRuntimeId" : 4167 + "blockRuntimeId" : 5944 }, { "id" : "minecraft:orange_glazed_terracotta", - "blockRuntimeId" : 1151 + "blockRuntimeId" : 1774 }, { "id" : "minecraft:yellow_glazed_terracotta", - "blockRuntimeId" : 913 + "blockRuntimeId" : 1536 }, { "id" : "minecraft:lime_glazed_terracotta", @@ -1488,39 +1527,39 @@ }, { "id" : "minecraft:green_glazed_terracotta", - "blockRuntimeId" : 6610 + "blockRuntimeId" : 10382 }, { "id" : "minecraft:cyan_glazed_terracotta", - "blockRuntimeId" : 5358 + "blockRuntimeId" : 7939 }, { "id" : "minecraft:light_blue_glazed_terracotta", - "blockRuntimeId" : 5471 + "blockRuntimeId" : 8052 }, { "id" : "minecraft:blue_glazed_terracotta", - "blockRuntimeId" : 5465 + "blockRuntimeId" : 8046 }, { "id" : "minecraft:purple_glazed_terracotta", - "blockRuntimeId" : 7011 + "blockRuntimeId" : 10789 }, { "id" : "minecraft:magenta_glazed_terracotta", - "blockRuntimeId" : 965 + "blockRuntimeId" : 1588 }, { "id" : "minecraft:pink_glazed_terracotta", - "blockRuntimeId" : 6539 + "blockRuntimeId" : 10311 }, { "id" : "minecraft:purpur_block", - "blockRuntimeId" : 7714 + "blockRuntimeId" : 11892 }, { "id" : "minecraft:purpur_block", - "blockRuntimeId" : 7716 + "blockRuntimeId" : 11894 }, { "id" : "minecraft:packed_mud", @@ -1528,31 +1567,31 @@ }, { "id" : "minecraft:mud_bricks", - "blockRuntimeId" : 6889 + "blockRuntimeId" : 10661 }, { "id" : "minecraft:nether_wart_block", - "blockRuntimeId" : 4293 + "blockRuntimeId" : 6074 }, { "id" : "minecraft:warped_wart_block", - "blockRuntimeId" : 5905 + "blockRuntimeId" : 9255 }, { "id" : "minecraft:shroomlight", - "blockRuntimeId" : 5061 + "blockRuntimeId" : 7610 }, { "id" : "minecraft:crimson_nylium", - "blockRuntimeId" : 4189 + "blockRuntimeId" : 5968 }, { "id" : "minecraft:warped_nylium", - "blockRuntimeId" : 6349 + "blockRuntimeId" : 10107 }, { "id" : "minecraft:basalt", - "blockRuntimeId" : 4349 + "blockRuntimeId" : 6130 }, { "id" : "minecraft:polished_basalt", @@ -1560,83 +1599,83 @@ }, { "id" : "minecraft:smooth_basalt", - "blockRuntimeId" : 1157 + "blockRuntimeId" : 1780 }, { "id" : "minecraft:soul_soil", - "blockRuntimeId" : 5830 + "blockRuntimeId" : 8412 }, { "id" : "minecraft:dirt", - "blockRuntimeId" : 5751 + "blockRuntimeId" : 8333 }, { "id" : "minecraft:dirt", - "blockRuntimeId" : 5752 + "blockRuntimeId" : 8334 }, { "id" : "minecraft:farmland", - "blockRuntimeId" : 3912 + "blockRuntimeId" : 5689 }, { "id" : "minecraft:grass", - "blockRuntimeId" : 6975 + "blockRuntimeId" : 10753 }, { "id" : "minecraft:grass_path", - "blockRuntimeId" : 8081 + "blockRuntimeId" : 12259 }, { "id" : "minecraft:podzol", - "blockRuntimeId" : 4650 + "blockRuntimeId" : 6815 }, { "id" : "minecraft:mycelium", - "blockRuntimeId" : 3683 + "blockRuntimeId" : 5076 }, { "id" : "minecraft:mud", - "blockRuntimeId" : 6684 + "blockRuntimeId" : 10456 }, { "id" : "minecraft:stone", - "blockRuntimeId" : 653 + "blockRuntimeId" : 1268 }, { "id" : "minecraft:iron_ore", - "blockRuntimeId" : 4690 + "blockRuntimeId" : 6855 }, { "id" : "minecraft:gold_ore", - "blockRuntimeId" : 912 + "blockRuntimeId" : 1535 }, { "id" : "minecraft:diamond_ore", - "blockRuntimeId" : 4361 + "blockRuntimeId" : 6142 }, { "id" : "minecraft:lapis_ore", - "blockRuntimeId" : 7699 + "blockRuntimeId" : 11877 }, { "id" : "minecraft:redstone_ore", - "blockRuntimeId" : 4289 + "blockRuntimeId" : 6068 }, { "id" : "minecraft:coal_ore", - "blockRuntimeId" : 4287 + "blockRuntimeId" : 6066 }, { "id" : "minecraft:copper_ore", - "blockRuntimeId" : 3554 + "blockRuntimeId" : 4947 }, { "id" : "minecraft:emerald_ore", - "blockRuntimeId" : 7347 + "blockRuntimeId" : 11525 }, { "id" : "minecraft:quartz_ore", - "blockRuntimeId" : 4501 + "blockRuntimeId" : 6282 }, { "id" : "minecraft:nether_gold_ore", @@ -1644,35 +1683,35 @@ }, { "id" : "minecraft:ancient_debris", - "blockRuntimeId" : 6107 + "blockRuntimeId" : 9857 }, { "id" : "minecraft:deepslate_iron_ore", - "blockRuntimeId" : 7273 + "blockRuntimeId" : 11451 }, { "id" : "minecraft:deepslate_gold_ore", - "blockRuntimeId" : 6106 + "blockRuntimeId" : 9856 }, { "id" : "minecraft:deepslate_diamond_ore", - "blockRuntimeId" : 8038 + "blockRuntimeId" : 12216 }, { "id" : "minecraft:deepslate_lapis_ore", - "blockRuntimeId" : 7262 + "blockRuntimeId" : 11440 }, { "id" : "minecraft:deepslate_redstone_ore", - "blockRuntimeId" : 6616 + "blockRuntimeId" : 10388 }, { "id" : "minecraft:deepslate_emerald_ore", - "blockRuntimeId" : 6350 + "blockRuntimeId" : 10108 }, { "id" : "minecraft:deepslate_coal_ore", - "blockRuntimeId" : 7196 + "blockRuntimeId" : 11358 }, { "id" : "minecraft:deepslate_copper_ore", @@ -1680,23 +1719,23 @@ }, { "id" : "minecraft:gravel", - "blockRuntimeId" : 8287 + "blockRuntimeId" : 12466 }, { "id" : "minecraft:stone", - "blockRuntimeId" : 654 + "blockRuntimeId" : 1269 }, { "id" : "minecraft:stone", - "blockRuntimeId" : 656 + "blockRuntimeId" : 1271 }, { "id" : "minecraft:stone", - "blockRuntimeId" : 658 + "blockRuntimeId" : 1273 }, { "id" : "minecraft:blackstone", - "blockRuntimeId" : 7585 + "blockRuntimeId" : 11763 }, { "id" : "minecraft:deepslate", @@ -1704,79 +1743,79 @@ }, { "id" : "minecraft:stone", - "blockRuntimeId" : 655 + "blockRuntimeId" : 1270 }, { "id" : "minecraft:stone", - "blockRuntimeId" : 657 + "blockRuntimeId" : 1272 }, { "id" : "minecraft:stone", - "blockRuntimeId" : 659 + "blockRuntimeId" : 1274 }, { "id" : "minecraft:polished_blackstone", - "blockRuntimeId" : 3682 + "blockRuntimeId" : 5075 }, { "id" : "minecraft:polished_deepslate", - "blockRuntimeId" : 7754 + "blockRuntimeId" : 11932 }, { "id" : "minecraft:sand", - "blockRuntimeId" : 4195 + "blockRuntimeId" : 5974 }, { "id" : "minecraft:sand", - "blockRuntimeId" : 4196 + "blockRuntimeId" : 5975 }, { "id" : "minecraft:cactus", - "blockRuntimeId" : 6986 + "blockRuntimeId" : 10764 }, { "id" : "minecraft:log", - "blockRuntimeId" : 6672 + "blockRuntimeId" : 10444 }, { "id" : "minecraft:stripped_oak_log", - "blockRuntimeId" : 7543 + "blockRuntimeId" : 11721 }, { "id" : "minecraft:log", - "blockRuntimeId" : 6673 + "blockRuntimeId" : 10445 }, { "id" : "minecraft:stripped_spruce_log", - "blockRuntimeId" : 6288 + "blockRuntimeId" : 10046 }, { "id" : "minecraft:log", - "blockRuntimeId" : 6674 + "blockRuntimeId" : 10446 }, { "id" : "minecraft:stripped_birch_log", - "blockRuntimeId" : 5972 + "blockRuntimeId" : 9322 }, { "id" : "minecraft:log", - "blockRuntimeId" : 6675 + "blockRuntimeId" : 10447 }, { "id" : "minecraft:stripped_jungle_log", - "blockRuntimeId" : 642 + "blockRuntimeId" : 1257 }, { "id" : "minecraft:log2", - "blockRuntimeId" : 3830 + "blockRuntimeId" : 5607 }, { "id" : "minecraft:stripped_acacia_log", - "blockRuntimeId" : 5848 + "blockRuntimeId" : 8814 }, { "id" : "minecraft:log2", - "blockRuntimeId" : 3831 + "blockRuntimeId" : 5608 }, { "id" : "minecraft:stripped_dark_oak_log", @@ -1784,167 +1823,167 @@ }, { "id" : "minecraft:mangrove_log", - "blockRuntimeId" : 348 + "blockRuntimeId" : 578 }, { "id" : "minecraft:stripped_mangrove_log", - "blockRuntimeId" : 8284 + "blockRuntimeId" : 12463 }, { "id" : "minecraft:crimson_stem", - "blockRuntimeId" : 5897 + "blockRuntimeId" : 9247 }, { "id" : "minecraft:stripped_crimson_stem", - "blockRuntimeId" : 6948 + "blockRuntimeId" : 10726 }, { "id" : "minecraft:warped_stem", - "blockRuntimeId" : 6486 + "blockRuntimeId" : 10256 }, { "id" : "minecraft:stripped_warped_stem", - "blockRuntimeId" : 7400 + "blockRuntimeId" : 11578 }, { "id" : "minecraft:wood", - "blockRuntimeId" : 3474 + "blockRuntimeId" : 4099 }, { "id" : "minecraft:wood", - "blockRuntimeId" : 3480 + "blockRuntimeId" : 4105 }, { "id" : "minecraft:wood", - "blockRuntimeId" : 3475 + "blockRuntimeId" : 4100 }, { "id" : "minecraft:wood", - "blockRuntimeId" : 3481 + "blockRuntimeId" : 4106 }, { "id" : "minecraft:wood", - "blockRuntimeId" : 3476 + "blockRuntimeId" : 4101 }, { "id" : "minecraft:wood", - "blockRuntimeId" : 3482 + "blockRuntimeId" : 4107 }, { "id" : "minecraft:wood", - "blockRuntimeId" : 3477 + "blockRuntimeId" : 4102 }, { "id" : "minecraft:wood", - "blockRuntimeId" : 3483 + "blockRuntimeId" : 4108 }, { "id" : "minecraft:wood", - "blockRuntimeId" : 3478 + "blockRuntimeId" : 4103 }, { "id" : "minecraft:wood", - "blockRuntimeId" : 3484 + "blockRuntimeId" : 4109 }, { "id" : "minecraft:wood", - "blockRuntimeId" : 3479 + "blockRuntimeId" : 4104 }, { "id" : "minecraft:wood", - "blockRuntimeId" : 3485 + "blockRuntimeId" : 4110 }, { "id" : "minecraft:mangrove_wood", - "blockRuntimeId" : 4161 + "blockRuntimeId" : 5938 }, { "id" : "minecraft:stripped_mangrove_wood", - "blockRuntimeId" : 4229 + "blockRuntimeId" : 6008 }, { "id" : "minecraft:crimson_hyphae", - "blockRuntimeId" : 4294 + "blockRuntimeId" : 6075 }, { "id" : "minecraft:stripped_crimson_hyphae", - "blockRuntimeId" : 6499 + "blockRuntimeId" : 10269 }, { "id" : "minecraft:warped_hyphae", - "blockRuntimeId" : 5902 + "blockRuntimeId" : 9252 }, { "id" : "minecraft:stripped_warped_hyphae", - "blockRuntimeId" : 5579 + "blockRuntimeId" : 8160 }, { "id" : "minecraft:leaves", - "blockRuntimeId" : 6090 + "blockRuntimeId" : 9840 }, { "id" : "minecraft:leaves", - "blockRuntimeId" : 6091 + "blockRuntimeId" : 9841 }, { "id" : "minecraft:leaves", - "blockRuntimeId" : 6092 + "blockRuntimeId" : 9842 }, { "id" : "minecraft:leaves", - "blockRuntimeId" : 6093 + "blockRuntimeId" : 9843 }, { "id" : "minecraft:leaves2", - "blockRuntimeId" : 4353 + "blockRuntimeId" : 6134 }, { "id" : "minecraft:leaves2", - "blockRuntimeId" : 4354 + "blockRuntimeId" : 6135 }, { "id" : "minecraft:mangrove_leaves", - "blockRuntimeId" : 6666 + "blockRuntimeId" : 10438 }, { "id" : "minecraft:azalea_leaves", - "blockRuntimeId" : 7710 + "blockRuntimeId" : 11888 }, { "id" : "minecraft:azalea_leaves_flowered", - "blockRuntimeId" : 6339 + "blockRuntimeId" : 10097 }, { "id" : "minecraft:sapling", - "blockRuntimeId" : 712 + "blockRuntimeId" : 1327 }, { "id" : "minecraft:sapling", - "blockRuntimeId" : 713 + "blockRuntimeId" : 1328 }, { "id" : "minecraft:sapling", - "blockRuntimeId" : 714 + "blockRuntimeId" : 1329 }, { "id" : "minecraft:sapling", - "blockRuntimeId" : 715 + "blockRuntimeId" : 1330 }, { "id" : "minecraft:sapling", - "blockRuntimeId" : 716 + "blockRuntimeId" : 1331 }, { "id" : "minecraft:sapling", - "blockRuntimeId" : 717 + "blockRuntimeId" : 1332 }, { "id" : "minecraft:mangrove_propagule", - "blockRuntimeId" : 6976 + "blockRuntimeId" : 10754 }, { "id" : "minecraft:bee_nest", - "blockRuntimeId" : 5754 + "blockRuntimeId" : 8336 }, { "id" : "minecraft:wheat_seeds" @@ -1987,7 +2026,7 @@ }, { "id" : "minecraft:melon_block", - "blockRuntimeId" : 392 + "blockRuntimeId" : 622 }, { "id" : "minecraft:melon_slice" @@ -2003,97 +2042,97 @@ }, { "id" : "minecraft:pumpkin", - "blockRuntimeId" : 4577 + "blockRuntimeId" : 6358 }, { "id" : "minecraft:carved_pumpkin", - "blockRuntimeId" : 7378 + "blockRuntimeId" : 11556 }, { "id" : "minecraft:lit_pumpkin", - "blockRuntimeId" : 6685 + "blockRuntimeId" : 10457 }, { "id" : "minecraft:honeycomb" }, { "id" : "minecraft:tallgrass", - "blockRuntimeId" : 929 + "blockRuntimeId" : 1552 }, { "id" : "minecraft:double_plant", - "blockRuntimeId" : 5455 + "blockRuntimeId" : 8036 }, { "id" : "minecraft:tallgrass", - "blockRuntimeId" : 928 + "blockRuntimeId" : 1551 }, { "id" : "minecraft:double_plant", - "blockRuntimeId" : 5454 + "blockRuntimeId" : 8035 }, { "id" : "minecraft:nether_sprouts" }, { "id" : "minecraft:coral", - "blockRuntimeId" : 6492 + "blockRuntimeId" : 10262 }, { "id" : "minecraft:coral", - "blockRuntimeId" : 6490 + "blockRuntimeId" : 10260 }, { "id" : "minecraft:coral", - "blockRuntimeId" : 6491 + "blockRuntimeId" : 10261 }, { "id" : "minecraft:coral", - "blockRuntimeId" : 6489 + "blockRuntimeId" : 10259 }, { "id" : "minecraft:coral", - "blockRuntimeId" : 6493 + "blockRuntimeId" : 10263 }, { "id" : "minecraft:coral", - "blockRuntimeId" : 6497 + "blockRuntimeId" : 10267 }, { "id" : "minecraft:coral", - "blockRuntimeId" : 6495 + "blockRuntimeId" : 10265 }, { "id" : "minecraft:coral", - "blockRuntimeId" : 6496 + "blockRuntimeId" : 10266 }, { "id" : "minecraft:coral", - "blockRuntimeId" : 6494 + "blockRuntimeId" : 10264 }, { "id" : "minecraft:coral", - "blockRuntimeId" : 6498 + "blockRuntimeId" : 10268 }, { "id" : "minecraft:coral_fan", - "blockRuntimeId" : 4616 + "blockRuntimeId" : 6397 }, { "id" : "minecraft:coral_fan", - "blockRuntimeId" : 4614 + "blockRuntimeId" : 6395 }, { "id" : "minecraft:coral_fan", - "blockRuntimeId" : 4615 + "blockRuntimeId" : 6396 }, { "id" : "minecraft:coral_fan", - "blockRuntimeId" : 4613 + "blockRuntimeId" : 6394 }, { "id" : "minecraft:coral_fan", - "blockRuntimeId" : 4617 + "blockRuntimeId" : 6398 }, { "id" : "minecraft:coral_fan_dead", @@ -2115,88 +2154,81 @@ "id" : "minecraft:coral_fan_dead", "blockRuntimeId" : 70 }, - { - "id" : "minecraft:kelp" - }, - { - "id" : "minecraft:seagrass", - "blockRuntimeId" : 246 - }, { "id" : "minecraft:crimson_roots", - "blockRuntimeId" : 7573 + "blockRuntimeId" : 11751 }, { "id" : "minecraft:warped_roots", - "blockRuntimeId" : 4362 + "blockRuntimeId" : 6143 }, { "id" : "minecraft:yellow_flower", - "blockRuntimeId" : 302 + "blockRuntimeId" : 530 }, { "id" : "minecraft:red_flower", - "blockRuntimeId" : 3616 + "blockRuntimeId" : 5009 }, { "id" : "minecraft:red_flower", - "blockRuntimeId" : 3617 + "blockRuntimeId" : 5010 }, { "id" : "minecraft:red_flower", - "blockRuntimeId" : 3618 + "blockRuntimeId" : 5011 }, { "id" : "minecraft:red_flower", - "blockRuntimeId" : 3619 + "blockRuntimeId" : 5012 }, { "id" : "minecraft:red_flower", - "blockRuntimeId" : 3620 + "blockRuntimeId" : 5013 }, { "id" : "minecraft:red_flower", - "blockRuntimeId" : 3621 + "blockRuntimeId" : 5014 }, { "id" : "minecraft:red_flower", - "blockRuntimeId" : 3622 + "blockRuntimeId" : 5015 }, { "id" : "minecraft:red_flower", - "blockRuntimeId" : 3623 + "blockRuntimeId" : 5016 }, { "id" : "minecraft:red_flower", - "blockRuntimeId" : 3624 + "blockRuntimeId" : 5017 }, { "id" : "minecraft:red_flower", - "blockRuntimeId" : 3625 + "blockRuntimeId" : 5018 }, { "id" : "minecraft:red_flower", - "blockRuntimeId" : 3626 + "blockRuntimeId" : 5019 }, { "id" : "minecraft:double_plant", - "blockRuntimeId" : 5452 + "blockRuntimeId" : 8033 }, { "id" : "minecraft:double_plant", - "blockRuntimeId" : 5453 + "blockRuntimeId" : 8034 }, { "id" : "minecraft:double_plant", - "blockRuntimeId" : 5456 + "blockRuntimeId" : 8037 }, { "id" : "minecraft:double_plant", - "blockRuntimeId" : 5457 + "blockRuntimeId" : 8038 }, { "id" : "minecraft:wither_rose", - "blockRuntimeId" : 6165 + "blockRuntimeId" : 9915 }, { "id" : "minecraft:white_dye" @@ -2263,35 +2295,42 @@ }, { "id" : "minecraft:vine", - "blockRuntimeId" : 894 + "blockRuntimeId" : 1517 }, { "id" : "minecraft:weeping_vines", - "blockRuntimeId" : 5479 + "blockRuntimeId" : 8060 }, { "id" : "minecraft:twisting_vines", - "blockRuntimeId" : 5691 + "blockRuntimeId" : 8273 }, { "id" : "minecraft:waterlily", - "blockRuntimeId" : 1158 + "blockRuntimeId" : 1781 + }, + { + "id" : "minecraft:seagrass", + "blockRuntimeId" : 246 + }, + { + "id" : "minecraft:kelp" }, { "id" : "minecraft:deadbush", - "blockRuntimeId" : 4677 + "blockRuntimeId" : 6842 }, { "id" : "minecraft:bamboo", - "blockRuntimeId" : 3684 + "blockRuntimeId" : 5077 }, { "id" : "minecraft:snow", - "blockRuntimeId" : 4194 + "blockRuntimeId" : 5973 }, { "id" : "minecraft:ice", - "blockRuntimeId" : 6689 + "blockRuntimeId" : 10461 }, { "id" : "minecraft:packed_ice", @@ -2299,7 +2338,7 @@ }, { "id" : "minecraft:blue_ice", - "blockRuntimeId" : 7027 + "blockRuntimeId" : 10805 }, { "id" : "minecraft:snow_layer", @@ -2307,11 +2346,11 @@ }, { "id" : "minecraft:pointed_dripstone", - "blockRuntimeId" : 7416 + "blockRuntimeId" : 11594 }, { "id" : "minecraft:dripstone_block", - "blockRuntimeId" : 893 + "blockRuntimeId" : 1516 }, { "id" : "minecraft:moss_carpet", @@ -2319,11 +2358,11 @@ }, { "id" : "minecraft:moss_block", - "blockRuntimeId" : 6538 + "blockRuntimeId" : 10310 }, { "id" : "minecraft:dirt_with_roots", - "blockRuntimeId" : 5397 + "blockRuntimeId" : 7978 }, { "id" : "minecraft:hanging_roots", @@ -2331,63 +2370,63 @@ }, { "id" : "minecraft:mangrove_roots", - "blockRuntimeId" : 6175 + "blockRuntimeId" : 9925 }, { "id" : "minecraft:muddy_mangrove_roots", - "blockRuntimeId" : 345 + "blockRuntimeId" : 573 }, { "id" : "minecraft:big_dripleaf", - "blockRuntimeId" : 5980 + "blockRuntimeId" : 9330 }, { "id" : "minecraft:small_dripleaf_block", - "blockRuntimeId" : 4320 + "blockRuntimeId" : 6101 }, { "id" : "minecraft:spore_blossom", - "blockRuntimeId" : 7312 + "blockRuntimeId" : 11490 }, { "id" : "minecraft:azalea", - "blockRuntimeId" : 6888 + "blockRuntimeId" : 10660 }, { "id" : "minecraft:flowering_azalea", - "blockRuntimeId" : 5477 + "blockRuntimeId" : 8058 }, { "id" : "minecraft:glow_lichen", - "blockRuntimeId" : 5684 + "blockRuntimeId" : 8266 }, { "id" : "minecraft:amethyst_block", - "blockRuntimeId" : 290 + "blockRuntimeId" : 322 }, { "id" : "minecraft:budding_amethyst", - "blockRuntimeId" : 7002 + "blockRuntimeId" : 10780 }, { "id" : "minecraft:amethyst_cluster", - "blockRuntimeId" : 7810 + "blockRuntimeId" : 11988 }, { "id" : "minecraft:large_amethyst_bud", - "blockRuntimeId" : 4728 + "blockRuntimeId" : 6893 }, { "id" : "minecraft:medium_amethyst_bud", - "blockRuntimeId" : 4376 + "blockRuntimeId" : 6157 }, { "id" : "minecraft:small_amethyst_bud", - "blockRuntimeId" : 304 + "blockRuntimeId" : 532 }, { "id" : "minecraft:tuff", - "blockRuntimeId" : 347 + "blockRuntimeId" : 577 }, { "id" : "minecraft:calcite", @@ -2422,15 +2461,15 @@ }, { "id" : "minecraft:brown_mushroom", - "blockRuntimeId" : 3546 + "blockRuntimeId" : 4939 }, { "id" : "minecraft:red_mushroom", - "blockRuntimeId" : 4585 + "blockRuntimeId" : 6366 }, { "id" : "minecraft:crimson_fungus", - "blockRuntimeId" : 7753 + "blockRuntimeId" : 11931 }, { "id" : "minecraft:warped_fungus", @@ -2438,19 +2477,19 @@ }, { "id" : "minecraft:brown_mushroom_block", - "blockRuntimeId" : 7362 + "blockRuntimeId" : 11540 }, { "id" : "minecraft:red_mushroom_block", - "blockRuntimeId" : 3611 + "blockRuntimeId" : 5004 }, { "id" : "minecraft:brown_mushroom_block", - "blockRuntimeId" : 7363 + "blockRuntimeId" : 11541 }, { "id" : "minecraft:brown_mushroom_block", - "blockRuntimeId" : 7348 + "blockRuntimeId" : 11526 }, { "id" : "minecraft:egg" @@ -2469,66 +2508,66 @@ }, { "id" : "minecraft:web", - "blockRuntimeId" : 6713 + "blockRuntimeId" : 10485 }, { "id" : "minecraft:spider_eye" }, { "id" : "minecraft:mob_spawner", - "blockRuntimeId" : 401 + "blockRuntimeId" : 631 }, { "id" : "minecraft:monster_egg", - "blockRuntimeId" : 4144 + "blockRuntimeId" : 5921 }, { "id" : "minecraft:monster_egg", - "blockRuntimeId" : 4145 + "blockRuntimeId" : 5922 }, { "id" : "minecraft:monster_egg", - "blockRuntimeId" : 4146 + "blockRuntimeId" : 5923 }, { "id" : "minecraft:monster_egg", - "blockRuntimeId" : 4147 + "blockRuntimeId" : 5924 }, { "id" : "minecraft:monster_egg", - "blockRuntimeId" : 4148 + "blockRuntimeId" : 5925 }, { "id" : "minecraft:monster_egg", - "blockRuntimeId" : 4149 + "blockRuntimeId" : 5926 }, { "id" : "minecraft:infested_deepslate", - "blockRuntimeId" : 4641 + "blockRuntimeId" : 6806 }, { "id" : "minecraft:dragon_egg", - "blockRuntimeId" : 7271 + "blockRuntimeId" : 11449 }, { "id" : "minecraft:turtle_egg", - "blockRuntimeId" : 7997 + "blockRuntimeId" : 12175 }, { "id" : "minecraft:frog_spawn", - "blockRuntimeId" : 4399 + "blockRuntimeId" : 6180 }, { "id" : "minecraft:pearlescent_froglight", - "blockRuntimeId" : 6435 + "blockRuntimeId" : 10193 }, { "id" : "minecraft:verdant_froglight", - "blockRuntimeId" : 6481 + "blockRuntimeId" : 10251 }, { "id" : "minecraft:ochre_froglight", - "blockRuntimeId" : 3510 + "blockRuntimeId" : 4519 }, { "id" : "minecraft:chicken_spawn_egg" @@ -2707,6 +2746,9 @@ { "id" : "minecraft:trader_llama_spawn_egg" }, + { + "id" : "minecraft:camel_spawn_egg" + }, { "id" : "minecraft:ghast_spawn_egg" }, @@ -2745,42 +2787,42 @@ }, { "id" : "minecraft:obsidian", - "blockRuntimeId" : 428 + "blockRuntimeId" : 658 }, { "id" : "minecraft:crying_obsidian", - "blockRuntimeId" : 6722 + "blockRuntimeId" : 10494 }, { "id" : "minecraft:bedrock", - "blockRuntimeId" : 7017 + "blockRuntimeId" : 10795 }, { "id" : "minecraft:soul_sand", - "blockRuntimeId" : 5831 + "blockRuntimeId" : 8413 }, { "id" : "minecraft:netherrack", - "blockRuntimeId" : 7037 + "blockRuntimeId" : 10815 }, { "id" : "minecraft:magma", - "blockRuntimeId" : 8009 + "blockRuntimeId" : 12187 }, { "id" : "minecraft:nether_wart" }, { "id" : "minecraft:end_stone", - "blockRuntimeId" : 3836 + "blockRuntimeId" : 5613 }, { "id" : "minecraft:chorus_flower", - "blockRuntimeId" : 4530 + "blockRuntimeId" : 6311 }, { "id" : "minecraft:chorus_plant", - "blockRuntimeId" : 5505 + "blockRuntimeId" : 8086 }, { "id" : "minecraft:chorus_fruit" @@ -2790,63 +2832,63 @@ }, { "id" : "minecraft:sponge", - "blockRuntimeId" : 629 + "blockRuntimeId" : 859 }, { "id" : "minecraft:sponge", - "blockRuntimeId" : 630 + "blockRuntimeId" : 860 }, { "id" : "minecraft:coral_block", - "blockRuntimeId" : 5237 + "blockRuntimeId" : 7802 }, { "id" : "minecraft:coral_block", - "blockRuntimeId" : 5238 + "blockRuntimeId" : 7803 }, { "id" : "minecraft:coral_block", - "blockRuntimeId" : 5239 + "blockRuntimeId" : 7804 }, { "id" : "minecraft:coral_block", - "blockRuntimeId" : 5240 + "blockRuntimeId" : 7805 }, { "id" : "minecraft:coral_block", - "blockRuntimeId" : 5241 + "blockRuntimeId" : 7806 }, { "id" : "minecraft:coral_block", - "blockRuntimeId" : 5242 + "blockRuntimeId" : 7807 }, { "id" : "minecraft:coral_block", - "blockRuntimeId" : 5243 + "blockRuntimeId" : 7808 }, { "id" : "minecraft:coral_block", - "blockRuntimeId" : 5244 + "blockRuntimeId" : 7809 }, { "id" : "minecraft:coral_block", - "blockRuntimeId" : 5245 + "blockRuntimeId" : 7810 }, { "id" : "minecraft:coral_block", - "blockRuntimeId" : 5246 + "blockRuntimeId" : 7811 }, { "id" : "minecraft:sculk", - "blockRuntimeId" : 7036 + "blockRuntimeId" : 10814 }, { "id" : "minecraft:sculk_vein", - "blockRuntimeId" : 7132 + "blockRuntimeId" : 11294 }, { "id" : "minecraft:sculk_catalyst", - "blockRuntimeId" : 3613 + "blockRuntimeId" : 5006 }, { "id" : "minecraft:sculk_shrieker", @@ -2854,11 +2896,11 @@ }, { "id" : "minecraft:sculk_sensor", - "blockRuntimeId" : 4389 + "blockRuntimeId" : 6170 }, { "id" : "minecraft:reinforced_deepslate", - "blockRuntimeId" : 5832 + "blockRuntimeId" : 8798 }, { "id" : "minecraft:leather_helmet" @@ -3919,86 +3961,86 @@ }, { "id" : "minecraft:torch", - "blockRuntimeId" : 724 + "blockRuntimeId" : 1347 }, { "id" : "minecraft:soul_torch", - "blockRuntimeId" : 4644 + "blockRuntimeId" : 6809 }, { "id" : "minecraft:sea_pickle", - "blockRuntimeId" : 5855 + "blockRuntimeId" : 8821 }, { "id" : "minecraft:lantern", - "blockRuntimeId" : 7074 + "blockRuntimeId" : 10852 }, { "id" : "minecraft:soul_lantern", - "blockRuntimeId" : 5749 + "blockRuntimeId" : 8331 }, { "id" : "minecraft:candle", - "blockRuntimeId" : 7403 + "blockRuntimeId" : 11581 }, { "id" : "minecraft:white_candle", - "blockRuntimeId" : 5300 + "blockRuntimeId" : 7881 }, { "id" : "minecraft:orange_candle", - "blockRuntimeId" : 362 + "blockRuntimeId" : 592 }, { "id" : "minecraft:magenta_candle", - "blockRuntimeId" : 418 + "blockRuntimeId" : 648 }, { "id" : "minecraft:light_blue_candle", - "blockRuntimeId" : 4569 + "blockRuntimeId" : 6350 }, { "id" : "minecraft:yellow_candle", - "blockRuntimeId" : 6192 + "blockRuntimeId" : 9942 }, { "id" : "minecraft:lime_candle", - "blockRuntimeId" : 6368 + "blockRuntimeId" : 10126 }, { "id" : "minecraft:pink_candle", - "blockRuntimeId" : 7370 + "blockRuntimeId" : 11548 }, { "id" : "minecraft:gray_candle", - "blockRuntimeId" : 939 + "blockRuntimeId" : 1562 }, { "id" : "minecraft:light_gray_candle", - "blockRuntimeId" : 6224 + "blockRuntimeId" : 9982 }, { "id" : "minecraft:cyan_candle", - "blockRuntimeId" : 7726 + "blockRuntimeId" : 11904 }, { "id" : "minecraft:purple_candle", - "blockRuntimeId" : 7038 + "blockRuntimeId" : 10816 }, { "id" : "minecraft:blue_candle" }, { "id" : "minecraft:brown_candle", - "blockRuntimeId" : 5875 + "blockRuntimeId" : 9225 }, { "id" : "minecraft:green_candle", - "blockRuntimeId" : 686 + "blockRuntimeId" : 1301 }, { "id" : "minecraft:red_candle", - "blockRuntimeId" : 4681 + "blockRuntimeId" : 6846 }, { "id" : "minecraft:black_candle", @@ -4006,23 +4048,23 @@ }, { "id" : "minecraft:crafting_table", - "blockRuntimeId" : 5854 + "blockRuntimeId" : 8820 }, { "id" : "minecraft:cartography_table", - "blockRuntimeId" : 8288 + "blockRuntimeId" : 12467 }, { "id" : "minecraft:fletching_table", - "blockRuntimeId" : 5833 + "blockRuntimeId" : 8799 }, { "id" : "minecraft:smithing_table", - "blockRuntimeId" : 3726 + "blockRuntimeId" : 5119 }, { "id" : "minecraft:beehive", - "blockRuntimeId" : 6108 + "blockRuntimeId" : 9858 }, { "id" : "minecraft:campfire" @@ -4032,152 +4074,156 @@ }, { "id" : "minecraft:furnace", - "blockRuntimeId" : 7802 + "blockRuntimeId" : 11980 }, { "id" : "minecraft:blast_furnace", - "blockRuntimeId" : 7567 + "blockRuntimeId" : 11745 }, { "id" : "minecraft:smoker", - "blockRuntimeId" : 647 + "blockRuntimeId" : 1262 }, { "id" : "minecraft:respawn_anchor", - "blockRuntimeId" : 681 + "blockRuntimeId" : 1296 }, { "id" : "minecraft:brewing_stand" }, { "id" : "minecraft:anvil", - "blockRuntimeId" : 6634 + "blockRuntimeId" : 10406 }, { "id" : "minecraft:anvil", - "blockRuntimeId" : 6638 + "blockRuntimeId" : 10410 }, { "id" : "minecraft:anvil", - "blockRuntimeId" : 6642 + "blockRuntimeId" : 10414 }, { "id" : "minecraft:grindstone", - "blockRuntimeId" : 8039 + "blockRuntimeId" : 12217 }, { "id" : "minecraft:enchanting_table", - "blockRuntimeId" : 6723 + "blockRuntimeId" : 10495 }, { "id" : "minecraft:bookshelf", - "blockRuntimeId" : 6671 + "blockRuntimeId" : 10443 + }, + { + "id" : "minecraft:chiseled_bookshelf", + "blockRuntimeId" : 326 }, { "id" : "minecraft:lectern", - "blockRuntimeId" : 6940 + "blockRuntimeId" : 10718 }, { "id" : "minecraft:cauldron" }, { "id" : "minecraft:composter", - "blockRuntimeId" : 5415 + "blockRuntimeId" : 7996 }, { "id" : "minecraft:chest", - "blockRuntimeId" : 7115 + "blockRuntimeId" : 10893 }, { "id" : "minecraft:trapped_chest", - "blockRuntimeId" : 5583 + "blockRuntimeId" : 8164 }, { "id" : "minecraft:ender_chest", - "blockRuntimeId" : 4369 + "blockRuntimeId" : 6150 }, { "id" : "minecraft:barrel", - "blockRuntimeId" : 4518 + "blockRuntimeId" : 6299 }, { "id" : "minecraft:undyed_shulker_box", - "blockRuntimeId" : 3681 + "blockRuntimeId" : 5074 }, { "id" : "minecraft:shulker_box", - "blockRuntimeId" : 5316 + "blockRuntimeId" : 7897 }, { "id" : "minecraft:shulker_box", - "blockRuntimeId" : 5324 + "blockRuntimeId" : 7905 }, { "id" : "minecraft:shulker_box", - "blockRuntimeId" : 5323 + "blockRuntimeId" : 7904 }, { "id" : "minecraft:shulker_box", - "blockRuntimeId" : 5331 + "blockRuntimeId" : 7912 }, { "id" : "minecraft:shulker_box", - "blockRuntimeId" : 5328 + "blockRuntimeId" : 7909 }, { "id" : "minecraft:shulker_box", - "blockRuntimeId" : 5330 + "blockRuntimeId" : 7911 }, { "id" : "minecraft:shulker_box", - "blockRuntimeId" : 5317 + "blockRuntimeId" : 7898 }, { "id" : "minecraft:shulker_box", - "blockRuntimeId" : 5320 + "blockRuntimeId" : 7901 }, { "id" : "minecraft:shulker_box", - "blockRuntimeId" : 5321 + "blockRuntimeId" : 7902 }, { "id" : "minecraft:shulker_box", - "blockRuntimeId" : 5329 + "blockRuntimeId" : 7910 }, { "id" : "minecraft:shulker_box", - "blockRuntimeId" : 5325 + "blockRuntimeId" : 7906 }, { "id" : "minecraft:shulker_box", - "blockRuntimeId" : 5319 + "blockRuntimeId" : 7900 }, { "id" : "minecraft:shulker_box", - "blockRuntimeId" : 5327 + "blockRuntimeId" : 7908 }, { "id" : "minecraft:shulker_box", - "blockRuntimeId" : 5326 + "blockRuntimeId" : 7907 }, { "id" : "minecraft:shulker_box", - "blockRuntimeId" : 5318 + "blockRuntimeId" : 7899 }, { "id" : "minecraft:shulker_box", - "blockRuntimeId" : 5322 + "blockRuntimeId" : 7903 }, { "id" : "minecraft:armor_stand" }, { "id" : "minecraft:noteblock", - "blockRuntimeId" : 346 + "blockRuntimeId" : 576 }, { "id" : "minecraft:jukebox", - "blockRuntimeId" : 4874 + "blockRuntimeId" : 7423 }, { "id" : "minecraft:music_disc_13" @@ -4232,7 +4278,7 @@ }, { "id" : "minecraft:glowstone", - "blockRuntimeId" : 3885 + "blockRuntimeId" : 5662 }, { "id" : "minecraft:redstone_lamp", @@ -4240,7 +4286,7 @@ }, { "id" : "minecraft:sea_lantern", - "blockRuntimeId" : 7546 + "blockRuntimeId" : 11724 }, { "id" : "minecraft:oak_sign" @@ -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" }, @@ -4349,19 +4428,19 @@ }, { "id" : "minecraft:bell", - "blockRuntimeId" : 6908 + "blockRuntimeId" : 10686 }, { "id" : "minecraft:conduit", - "blockRuntimeId" : 4232 + "blockRuntimeId" : 6011 }, { "id" : "minecraft:stonecutter_block", - "blockRuntimeId" : 7574 + "blockRuntimeId" : 11752 }, { "id" : "minecraft:end_portal_frame", - "blockRuntimeId" : 6077 + "blockRuntimeId" : 9811 }, { "id" : "minecraft:coal" @@ -4500,11 +4579,11 @@ }, { "id" : "minecraft:end_rod", - "blockRuntimeId" : 5891 + "blockRuntimeId" : 9241 }, { "id" : "minecraft:lightning_rod", - "blockRuntimeId" : 1176 + "blockRuntimeId" : 1799 }, { "id" : "minecraft:end_crystal" @@ -4979,6 +5058,9 @@ { "id" : "minecraft:mangrove_boat" }, + { + "id" : "minecraft:bamboo_raft" + }, { "id" : "minecraft:oak_chest_boat" }, @@ -5000,21 +5082,24 @@ { "id" : "minecraft:mangrove_chest_boat" }, + { + "id" : "minecraft:bamboo_chest_raft" + }, { "id" : "minecraft:rail", - "blockRuntimeId" : 3920 + "blockRuntimeId" : 5697 }, { "id" : "minecraft:golden_rail", - "blockRuntimeId" : 5332 + "blockRuntimeId" : 7913 }, { "id" : "minecraft:detector_rail", - "blockRuntimeId" : 4132 + "blockRuntimeId" : 5909 }, { "id" : "minecraft:activator_rail", - "blockRuntimeId" : 309 + "blockRuntimeId" : 537 }, { "id" : "minecraft:minecart" @@ -5033,27 +5118,27 @@ }, { "id" : "minecraft:redstone_block", - "blockRuntimeId" : 3776 + "blockRuntimeId" : 5169 }, { "id" : "minecraft:redstone_torch", - "blockRuntimeId" : 3525 + "blockRuntimeId" : 4534 }, { "id" : "minecraft:lever", - "blockRuntimeId" : 6514 + "blockRuntimeId" : 10284 }, { "id" : "minecraft:wooden_button", - "blockRuntimeId" : 6391 + "blockRuntimeId" : 10149 }, { "id" : "minecraft:spruce_button", - "blockRuntimeId" : 4321 + "blockRuntimeId" : 6102 }, { "id" : "minecraft:birch_button", - "blockRuntimeId" : 7766 + "blockRuntimeId" : 11944 }, { "id" : "minecraft:jungle_button", @@ -5061,7 +5146,7 @@ }, { "id" : "minecraft:acacia_button", - "blockRuntimeId" : 7231 + "blockRuntimeId" : 11409 }, { "id" : "minecraft:dark_oak_button", @@ -5069,59 +5154,67 @@ }, { "id" : "minecraft:mangrove_button", - "blockRuntimeId" : 7062 + "blockRuntimeId" : 10840 + }, + { + "id" : "minecraft:bamboo_button", + "blockRuntimeId" : 10238 }, { "id" : "minecraft:stone_button", - "blockRuntimeId" : 596 + "blockRuntimeId" : 826 }, { "id" : "minecraft:crimson_button", - "blockRuntimeId" : 4432 + "blockRuntimeId" : 6213 }, { "id" : "minecraft:warped_button", - "blockRuntimeId" : 7250 + "blockRuntimeId" : 11428 }, { "id" : "minecraft:polished_blackstone_button", - "blockRuntimeId" : 7790 + "blockRuntimeId" : 11968 }, { "id" : "minecraft:tripwire_hook", - "blockRuntimeId" : 5914 + "blockRuntimeId" : 9264 }, { "id" : "minecraft:wooden_pressure_plate", - "blockRuntimeId" : 8063 + "blockRuntimeId" : 12241 }, { "id" : "minecraft:spruce_pressure_plate", - "blockRuntimeId" : 3759 + "blockRuntimeId" : 5152 }, { "id" : "minecraft:birch_pressure_plate", - "blockRuntimeId" : 3555 + "blockRuntimeId" : 4948 }, { "id" : "minecraft:jungle_pressure_plate", - "blockRuntimeId" : 3635 + "blockRuntimeId" : 5028 }, { "id" : "minecraft:acacia_pressure_plate", - "blockRuntimeId" : 5247 + "blockRuntimeId" : 7812 }, { "id" : "minecraft:dark_oak_pressure_plate", - "blockRuntimeId" : 5956 + "blockRuntimeId" : 9306 }, { "id" : "minecraft:mangrove_pressure_plate", - "blockRuntimeId" : 3869 + "blockRuntimeId" : 5646 + }, + { + "id" : "minecraft:bamboo_pressure_plate", + "blockRuntimeId" : 9819 }, { "id" : "minecraft:crimson_pressure_plate", - "blockRuntimeId" : 8268 + "blockRuntimeId" : 12447 }, { "id" : "minecraft:warped_pressure_plate", @@ -5129,27 +5222,27 @@ }, { "id" : "minecraft:stone_pressure_plate", - "blockRuntimeId" : 3886 + "blockRuntimeId" : 5663 }, { "id" : "minecraft:light_weighted_pressure_plate", - "blockRuntimeId" : 3665 + "blockRuntimeId" : 5058 }, { "id" : "minecraft:heavy_weighted_pressure_plate", - "blockRuntimeId" : 1160 + "blockRuntimeId" : 1783 }, { "id" : "minecraft:polished_blackstone_pressure_plate", - "blockRuntimeId" : 6232 + "blockRuntimeId" : 9990 }, { "id" : "minecraft:observer", - "blockRuntimeId" : 3513 + "blockRuntimeId" : 4522 }, { "id" : "minecraft:daylight_detector", - "blockRuntimeId" : 4197 + "blockRuntimeId" : 5976 }, { "id" : "minecraft:repeater" @@ -5162,30 +5255,30 @@ }, { "id" : "minecraft:dropper", - "blockRuntimeId" : 7385 + "blockRuntimeId" : 11563 }, { "id" : "minecraft:dispenser", - "blockRuntimeId" : 8013 + "blockRuntimeId" : 12191 }, { "id" : "minecraft:piston", - "blockRuntimeId" : 922 + "blockRuntimeId" : 1545 }, { "id" : "minecraft:sticky_piston", - "blockRuntimeId" : 4364 + "blockRuntimeId" : 6145 }, { "id" : "minecraft:tnt", - "blockRuntimeId" : 6707 + "blockRuntimeId" : 10479 }, { "id" : "minecraft:name_tag" }, { "id" : "minecraft:loom", - "blockRuntimeId" : 3826 + "blockRuntimeId" : 5603 }, { "id" : "minecraft:banner" @@ -5431,7 +5524,7 @@ }, { "id" : "minecraft:target", - "blockRuntimeId" : 6390 + "blockRuntimeId" : 10148 }, { "id" : "minecraft:lodestone_compass" diff --git a/core/src/main/resources/bedrock/entity_identifiers.dat b/core/src/main/resources/bedrock/entity_identifiers.dat index 297d30537..8f0c9ce8e 100644 Binary files a/core/src/main/resources/bedrock/entity_identifiers.dat and b/core/src/main/resources/bedrock/entity_identifiers.dat differ diff --git a/core/src/main/resources/bedrock/runtime_item_states.1_19_0.json b/core/src/main/resources/bedrock/runtime_item_states.1_19_0.json deleted file mode 100644 index b1ffe6353..000000000 --- a/core/src/main/resources/bedrock/runtime_item_states.1_19_0.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" : 643 - }, - { - "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" : 640 - }, - { - "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" : 646 - }, - { - "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" : 644 - }, - { - "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" : 638 - }, - { - "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" : 648 - }, - { - "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:firefly_spawn_egg", - "id" : 633 - }, - { - "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" : 641 - }, - { - "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" : 636 - }, - { - "name" : "minecraft:mangrove_button", - "id" : -487 - }, - { - "name" : "minecraft:mangrove_chest_boat", - "id" : 645 - }, - { - "name" : "minecraft:mangrove_door", - "id" : 634 - }, - { - "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" : 635 - }, - { - "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" : 637 - }, - { - "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" : 639 - }, - { - "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" : 647 - }, - { - "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" : 642 - }, - { - "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: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 diff --git a/core/src/main/resources/bedrock/runtime_item_states.1_19_10.json b/core/src/main/resources/bedrock/runtime_item_states.1_19_50.json similarity index 97% rename from core/src/main/resources/bedrock/runtime_item_states.1_19_10.json rename to core/src/main/resources/bedrock/runtime_item_states.1_19_50.json index 00be1af06..d8a12c794 100644 --- a/core/src/main/resources/bedrock/runtime_item_states.1_19_10.json +++ b/core/src/main/resources/bedrock/runtime_item_states.1_19_50.json @@ -9,7 +9,7 @@ }, { "name" : "minecraft:acacia_chest_boat", - "id" : 642 + "id" : 645 }, { "name" : "minecraft:acacia_door", @@ -19,6 +19,10 @@ "name" : "minecraft:acacia_fence_gate", "id" : 187 }, + { + "name" : "minecraft:acacia_hanging_sign", + "id" : -504 + }, { "name" : "minecraft:acacia_pressure_plate", "id" : -150 @@ -131,17 +135,97 @@ "name" : "minecraft:bamboo", "id" : -163 }, + { + "name" : "minecraft:bamboo_button", + "id" : -511 + }, + { + "name" : "minecraft:bamboo_chest_raft", + "id" : 648 + }, + { + "name" : "minecraft:bamboo_door", + "id" : -517 + }, + { + "name" : "minecraft:bamboo_double_slab", + "id" : -521 + }, + { + "name" : "minecraft:bamboo_fence", + "id" : -515 + }, + { + "name" : "minecraft:bamboo_fence_gate", + "id" : -516 + }, + { + "name" : "minecraft:bamboo_hanging_sign", + "id" : -522 + }, + { + "name" : "minecraft:bamboo_mosaic", + "id" : -509 + }, + { + "name" : "minecraft:bamboo_mosaic_double_slab", + "id" : -525 + }, + { + "name" : "minecraft:bamboo_mosaic_slab", + "id" : -524 + }, + { + "name" : "minecraft:bamboo_mosaic_stairs", + "id" : -523 + }, + { + "name" : "minecraft:bamboo_planks", + "id" : -510 + }, + { + "name" : "minecraft:bamboo_pressure_plate", + "id" : -514 + }, + { + "name" : "minecraft:bamboo_raft", + "id" : 638 + }, { "name" : "minecraft:bamboo_sapling", "id" : -164 }, + { + "name" : "minecraft:bamboo_sign", + "id" : 637 + }, + { + "name" : "minecraft:bamboo_slab", + "id" : -513 + }, + { + "name" : "minecraft:bamboo_stairs", + "id" : -512 + }, + { + "name" : "minecraft:bamboo_standing_sign", + "id" : -518 + }, + { + "name" : "minecraft:bamboo_trapdoor", + "id" : -520 + }, + { + "name" : "minecraft:bamboo_wall_sign", + "id" : -519 + }, { "name" : "minecraft:banner", "id" : 567 }, { "name" : "minecraft:banner_pattern", - "id" : 651 + "id" : 655 }, { "name" : "minecraft:barrel", @@ -217,7 +301,7 @@ }, { "name" : "minecraft:birch_chest_boat", - "id" : 639 + "id" : 642 }, { "name" : "minecraft:birch_door", @@ -227,6 +311,10 @@ "name" : "minecraft:birch_fence_gate", "id" : 184 }, + { + "name" : "minecraft:birch_hanging_sign", + "id" : -502 + }, { "name" : "minecraft:birch_pressure_plate", "id" : -151 @@ -329,7 +417,7 @@ }, { "name" : "minecraft:boat", - "id" : 649 + "id" : 653 }, { "name" : "minecraft:bone", @@ -435,6 +523,10 @@ "name" : "minecraft:calcite", "id" : -326 }, + { + "name" : "minecraft:camel_spawn_egg", + "id" : 633 + }, { "name" : "minecraft:camera", "id" : 593 @@ -541,7 +633,7 @@ }, { "name" : "minecraft:chest_boat", - "id" : 645 + "id" : 649 }, { "name" : "minecraft:chest_minecart", @@ -555,6 +647,10 @@ "name" : "minecraft:chicken_spawn_egg", "id" : 435 }, + { + "name" : "minecraft:chiseled_bookshelf", + "id" : -526 + }, { "name" : "minecraft:chiseled_deepslate", "id" : -395 @@ -827,6 +923,10 @@ "name" : "minecraft:crimson_fungus", "id" : -228 }, + { + "name" : "minecraft:crimson_hanging_sign", + "id" : -506 + }, { "name" : "minecraft:crimson_hyphae", "id" : -299 @@ -921,7 +1021,7 @@ }, { "name" : "minecraft:dark_oak_chest_boat", - "id" : 643 + "id" : 646 }, { "name" : "minecraft:dark_oak_door", @@ -931,6 +1031,10 @@ "name" : "minecraft:dark_oak_fence_gate", "id" : 186 }, + { + "name" : "minecraft:dark_oak_hanging_sign", + "id" : -505 + }, { "name" : "minecraft:dark_oak_pressure_plate", "id" : -152 @@ -1121,7 +1225,7 @@ }, { "name" : "minecraft:disc_fragment_5", - "id" : 637 + "id" : 640 }, { "name" : "minecraft:dispenser", @@ -1193,11 +1297,11 @@ }, { "name" : "minecraft:dye", - "id" : 650 + "id" : 654 }, { "name" : "minecraft:echo_shard", - "id" : 647 + "id" : 651 }, { "name" : "minecraft:egg", @@ -1725,7 +1829,7 @@ }, { "name" : "minecraft:end_crystal", - "id" : 653 + "id" : 657 }, { "name" : "minecraft:end_gateway", @@ -1933,7 +2037,7 @@ }, { "name" : "minecraft:glow_berries", - "id" : 654 + "id" : 658 }, { "name" : "minecraft:glow_frame", @@ -2405,7 +2509,7 @@ }, { "name" : "minecraft:jungle_chest_boat", - "id" : 640 + "id" : 643 }, { "name" : "minecraft:jungle_door", @@ -2415,6 +2519,10 @@ "name" : "minecraft:jungle_fence_gate", "id" : 185 }, + { + "name" : "minecraft:jungle_hanging_sign", + "id" : -503 + }, { "name" : "minecraft:jungle_pressure_plate", "id" : -153 @@ -2665,7 +2773,7 @@ }, { "name" : "minecraft:mangrove_boat", - "id" : 635 + "id" : 636 }, { "name" : "minecraft:mangrove_button", @@ -2673,11 +2781,11 @@ }, { "name" : "minecraft:mangrove_chest_boat", - "id" : 644 + "id" : 647 }, { "name" : "minecraft:mangrove_door", - "id" : 633 + "id" : 634 }, { "name" : "minecraft:mangrove_double_slab", @@ -2691,6 +2799,10 @@ "name" : "minecraft:mangrove_fence_gate", "id" : -492 }, + { + "name" : "minecraft:mangrove_hanging_sign", + "id" : -508 + }, { "name" : "minecraft:mangrove_leaves", "id" : -472 @@ -2717,7 +2829,7 @@ }, { "name" : "minecraft:mangrove_sign", - "id" : 634 + "id" : 635 }, { "name" : "minecraft:mangrove_slab", @@ -2861,7 +2973,7 @@ }, { "name" : "minecraft:music_disc_5", - "id" : 636 + "id" : 639 }, { "name" : "minecraft:music_disc_blocks", @@ -3037,7 +3149,11 @@ }, { "name" : "minecraft:oak_chest_boat", - "id" : 638 + "id" : 641 + }, + { + "name" : "minecraft:oak_hanging_sign", + "id" : -500 }, { "name" : "minecraft:oak_sign", @@ -3473,7 +3589,7 @@ }, { "name" : "minecraft:recovery_compass", - "id" : 646 + "id" : 650 }, { "name" : "minecraft:red_candle", @@ -3781,7 +3897,7 @@ }, { "name" : "minecraft:spawn_egg", - "id" : 652 + "id" : 656 }, { "name" : "minecraft:spider_eye", @@ -3813,7 +3929,7 @@ }, { "name" : "minecraft:spruce_chest_boat", - "id" : 641 + "id" : 644 }, { "name" : "minecraft:spruce_door", @@ -3823,6 +3939,10 @@ "name" : "minecraft:spruce_fence_gate", "id" : 183 }, + { + "name" : "minecraft:spruce_hanging_sign", + "id" : -501 + }, { "name" : "minecraft:spruce_pressure_plate", "id" : -154 @@ -4081,7 +4201,7 @@ }, { "name" : "minecraft:trader_llama_spawn_egg", - "id" : 648 + "id" : 652 }, { "name" : "minecraft:trapdoor", @@ -4223,6 +4343,10 @@ "name" : "minecraft:warped_fungus_on_a_stick", "id" : 618 }, + { + "name" : "minecraft:warped_hanging_sign", + "id" : -507 + }, { "name" : "minecraft:warped_hyphae", "id" : -298 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 ffd8e0719..000000000 Binary files a/core/src/main/resources/bedrock/skin/skin_alex.png and /dev/null differ diff --git a/core/src/main/resources/bedrock/skin/skin_steve.png b/core/src/main/resources/bedrock/skin/skin_steve.png deleted file mode 100644 index 056f108f2..000000000 Binary files a/core/src/main/resources/bedrock/skin/skin_steve.png and /dev/null differ diff --git a/core/src/main/resources/config.yml b/core/src/main/resources/config.yml index d23c3acdd..f25fb8be0 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. @@ -111,14 +111,17 @@ 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 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. @@ -183,6 +186,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/. @@ -209,4 +216,9 @@ enable-proxy-connections: false # 1400 is the default. mtu: 1400 +# 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 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 diff --git a/core/src/main/resources/mappings b/core/src/main/resources/mappings index 10baa9a45..677c5b087 160000 --- a/core/src/main/resources/mappings +++ b/core/src/main/resources/mappings @@ -1 +1 @@ -Subproject commit 10baa9a45de074afa643e8477bd5a4e72ecfa563 +Subproject commit 677c5b0872d2f0c99ad834c0ca49a0ae3b45fde3 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")); diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 4030d96e9..741013f1f 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,28 +1,29 @@ [versions] -geyser-base-api = "3.0.0-SNAPSHOT" -jackson = "2.13.4" +base-api = "1.0.0-SNAPSHOT" +geyser-api = "1.0.1-SNAPSHOT" +cumulus = "1.1.1" +events = "1.0-SNAPSHOT" +jackson = "2.14.0" fastutil = "8.5.2" 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-20221025.193624-1" +protocol = "2.9.15-20230106.005737-3" raknet = "1.6.28-20220125.214016-6" mcauthlib = "d9d773e" -mcprotocollib = "9f78bd5" -packetlib = "3.0" +mcprotocollib = "1.19.3-20230107.194116-10" +packetlib = "3.0.1" adventure = "4.12.0-20220629.025215-9" adventure-platform = "4.1.2" junit = "4.13.1" checkerframework = "3.19.0" -cumulus = "1.1.1" -events = "1.0-SNAPSHOT" -log4j = "2.17.1" +log4j = "2.17.1" jline = "3.21.0" terminalconsoleappender = "1.2.0" paper = "1.19-R0.1-SNAPSHOT" viaversion = "4.0.0" -adapters = "1.5-SNAPSHOT" +adapters = "1.6-SNAPSHOT" commodore = "2.2" bungeecord = "a7c6ede" velocity = "3.0.0" @@ -32,7 +33,10 @@ fabric-loader = "0.14.8" fabric-api = "0.58.5+1.19.1" [libraries] -baseApi = { group = "org.geysermc", name = "api", version.ref = "geyser-base-api" } +base-api = { group = "org.geysermc.api", name = "base-api", version.ref = "base-api" } +geyser-api = { group = "org.geysermc.api", name = "geyser-api", version.ref = "geyser-api" } +cumulus = { group = "org.geysermc.cumulus", name = "cumulus", version.ref = "cumulus" } +events = { group = "org.geysermc.event", name = "events", version.ref = "events" } jackson-annotations = { group = "com.fasterxml.jackson.core", name = "jackson-annotations", version.ref = "jackson" } jackson-core = { group = "com.fasterxml.jackson.core", name = "jackson-databind", version.ref = "jackson" } @@ -77,15 +81,13 @@ adapters-spigot = { group = "org.geysermc.geyser.adapters", name = "spigot-all", bungeecord-proxy = { group = "com.github.SpigotMC.BungeeCord", name = "bungeecord-proxy", version.ref = "bungeecord" } checker-qual = { group = "org.checkerframework", name = "checker-qual", version.ref = "checkerframework" } commodore = { group = "me.lucko", name = "commodore", version.ref = "commodore" } -cumulus = { group = "org.geysermc.cumulus", name = "cumulus", version.ref = "cumulus" } -events = { group = "org.geysermc.event", name = "events", version.ref = "events" } guava = { group = "com.google.guava", name = "guava", version.ref = "guava" } gson = { group = "com.google.code.gson", name = "gson", version.ref = "gson" } junit = { group = "junit", name = "junit", version.ref = "junit" } mcauthlib = { group = "com.github.GeyserMC", name = "MCAuthLib", version.ref = "mcauthlib" } -mcprotocollib = { group = "com.github.GeyserMC", name = "MCProtocolLib", version.ref = "mcprotocollib" } +mcprotocollib = { group = "com.github.steveice10", name = "mcprotocollib", version.ref = "mcprotocollib" } packetlib = { group = "com.github.steveice10", name = "packetlib", version.ref = "packetlib" } -protocol = { group = "com.nukkitx.protocol", name = "bedrock-v557", version.ref = "protocol" } +protocol = { group = "com.nukkitx.protocol", name = "bedrock-v560", version.ref = "protocol" } raknet = { group = "com.nukkitx.network", name = "raknet", version.ref = "raknet" } sponge-api = { group = "org.spongepowered", name = "spongeapi", version.ref = "sponge" } terminalconsoleappender = { group = "net.minecrell", name = "terminalconsoleappender", version.ref = "terminalconsoleappender" } @@ -94,8 +96,8 @@ viaversion = { group = "com.viaversion", name = "viaversion", version.ref = "via websocket = { group = "org.java-websocket", name = "Java-WebSocket", version.ref = "websocket" } [bundles] -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 +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"] diff --git a/settings.gradle.kts b/settings.gradle.kts index 1bf19467d..67ddc97d9 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -61,7 +61,6 @@ pluginManagement { rootProject.name = "geyser-parent" include(":ap") -include(":geyser-api") include(":bungeecord") include(":fabric") include(":spigot") @@ -72,7 +71,6 @@ include(":common") include(":core") // Specify project dirs -project(":geyser-api").projectDir = file("api/geyser") project(":bungeecord").projectDir = file("bootstrap/bungeecord") project(":fabric").projectDir = file("bootstrap/fabric") project(":spigot").projectDir = file("bootstrap/spigot")