diff --git a/README.md b/README.md index dec1e9dd1..d0bcb1fdc 100644 --- a/README.md +++ b/README.md @@ -15,7 +15,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! ## Supported Versions -Geyser is currently supporting Minecraft Bedrock 1.20.80 - 1.21.30 and Minecraft Java 1.21/1.21.1. For more information, please see [here](https://geysermc.org/wiki/geyser/supported-versions/). +Geyser is currently supporting Minecraft Bedrock 1.20.80 - 1.21.44 and Minecraft Java 1.21.2/1.21.3. For more information, please see [here](https://geysermc.org/wiki/geyser/supported-versions/). ## Setting Up Take a look [here](https://geysermc.org/wiki/geyser/setup/) for how to set up Geyser. diff --git a/api/src/main/java/org/geysermc/geyser/api/connection/GeyserConnection.java b/api/src/main/java/org/geysermc/geyser/api/connection/GeyserConnection.java index 0a580f975..ede4c1bc4 100644 --- a/api/src/main/java/org/geysermc/geyser/api/connection/GeyserConnection.java +++ b/api/src/main/java/org/geysermc/geyser/api/connection/GeyserConnection.java @@ -70,6 +70,11 @@ public interface GeyserConnection extends Connection, CommandSource { */ void closeForm(); + /** + * Gets the Bedrock protocol version of the player. + */ + int protocolVersion(); + /** * @param javaId the Java entity ID to look up. * @return a {@link GeyserEntity} if present in this connection's entity tracker. diff --git a/bootstrap/mod/fabric/src/main/java/org/geysermc/geyser/platform/fabric/GeyserFabricBootstrap.java b/bootstrap/mod/fabric/src/main/java/org/geysermc/geyser/platform/fabric/GeyserFabricBootstrap.java index 149246d59..96a60322b 100644 --- a/bootstrap/mod/fabric/src/main/java/org/geysermc/geyser/platform/fabric/GeyserFabricBootstrap.java +++ b/bootstrap/mod/fabric/src/main/java/org/geysermc/geyser/platform/fabric/GeyserFabricBootstrap.java @@ -32,7 +32,7 @@ import net.fabricmc.fabric.api.event.lifecycle.v1.ServerLifecycleEvents; import net.fabricmc.fabric.api.networking.v1.ServerPlayConnectionEvents; import net.fabricmc.loader.api.FabricLoader; import net.minecraft.commands.CommandSourceStack; -import net.minecraft.world.entity.player.Player; +import net.minecraft.server.level.ServerPlayer; import org.geysermc.geyser.GeyserImpl; import org.geysermc.geyser.command.CommandRegistry; import org.geysermc.geyser.command.CommandSourceConverter; @@ -80,7 +80,7 @@ public class GeyserFabricBootstrap extends GeyserModBootstrap implements ModInit var sourceConverter = CommandSourceConverter.layered( CommandSourceStack.class, id -> getServer().getPlayerList().getPlayer(id), - Player::createCommandSourceStack, + ServerPlayer::createCommandSourceStack, () -> getServer().createCommandSourceStack(), // NPE if method reference is used, since server is not available yet ModCommandSource::new ); diff --git a/bootstrap/mod/fabric/src/main/resources/fabric.mod.json b/bootstrap/mod/fabric/src/main/resources/fabric.mod.json index 262f9833a..a801eb207 100644 --- a/bootstrap/mod/fabric/src/main/resources/fabric.mod.json +++ b/bootstrap/mod/fabric/src/main/resources/fabric.mod.json @@ -23,8 +23,8 @@ "geyser.mixins.json" ], "depends": { - "fabricloader": ">=0.15.11", + "fabricloader": ">=0.16.7", "fabric": "*", - "minecraft": ">=1.21" + "minecraft": ">=1.21.2" } } diff --git a/bootstrap/mod/neoforge/build.gradle.kts b/bootstrap/mod/neoforge/build.gradle.kts index 4ab005b4f..23f16d64e 100644 --- a/bootstrap/mod/neoforge/build.gradle.kts +++ b/bootstrap/mod/neoforge/build.gradle.kts @@ -13,6 +13,9 @@ architectury { provided("org.cloudburstmc.math", "api") provided("com.google.errorprone", "error_prone_annotations") +// Jackson shipped by Minecraft is too old, so we shade & relocate our newer version +relocate("com.fasterxml.jackson") + val includeTransitive: Configuration = configurations.getByName("includeTransitive") dependencies { @@ -31,6 +34,12 @@ dependencies { } shadow(projects.core) { isTransitive = false } + // Minecraft (1.21.2+) includes jackson. But an old version! + shadow(libs.jackson.core) { isTransitive = false } + shadow(libs.jackson.databind) { isTransitive = false } + shadow(libs.jackson.dataformat.yaml) { isTransitive = false } + shadow(libs.jackson.annotations) { isTransitive = false } + // Let's shade in our own api shadow(projects.api) { isTransitive = false } @@ -56,6 +65,11 @@ tasks { remapModrinthJar { archiveBaseName.set("geyser-neoforge") } + + shadowJar { + // Without this, jackson's service files are not relocated + mergeServiceFiles() + } } modrinth { diff --git a/bootstrap/mod/neoforge/src/main/java/org/geysermc/geyser/platform/neoforge/GeyserNeoForgeBootstrap.java b/bootstrap/mod/neoforge/src/main/java/org/geysermc/geyser/platform/neoforge/GeyserNeoForgeBootstrap.java index ad56eda39..aa731befc 100644 --- a/bootstrap/mod/neoforge/src/main/java/org/geysermc/geyser/platform/neoforge/GeyserNeoForgeBootstrap.java +++ b/bootstrap/mod/neoforge/src/main/java/org/geysermc/geyser/platform/neoforge/GeyserNeoForgeBootstrap.java @@ -26,7 +26,7 @@ package org.geysermc.geyser.platform.neoforge; import net.minecraft.commands.CommandSourceStack; -import net.minecraft.world.entity.player.Player; +import net.minecraft.server.level.ServerPlayer; import net.neoforged.bus.api.EventPriority; import net.neoforged.fml.ModContainer; import net.neoforged.fml.common.Mod; @@ -72,7 +72,7 @@ public class GeyserNeoForgeBootstrap extends GeyserModBootstrap { var sourceConverter = CommandSourceConverter.layered( CommandSourceStack.class, id -> getServer().getPlayerList().getPlayer(id), - Player::createCommandSourceStack, + ServerPlayer::createCommandSourceStack, () -> getServer().createCommandSourceStack(), ModCommandSource::new ); @@ -104,7 +104,9 @@ public class GeyserNeoForgeBootstrap extends GeyserModBootstrap { } private void onPlayerJoin(PlayerEvent.PlayerLoggedInEvent event) { - GeyserModUpdateListener.onPlayReady(event.getEntity()); + if (event.getEntity() instanceof ServerPlayer player) { + GeyserModUpdateListener.onPlayReady(player); + } } @Override diff --git a/bootstrap/mod/src/main/java/org/geysermc/geyser/platform/mod/GeyserModCompressionDisabler.java b/bootstrap/mod/src/main/java/org/geysermc/geyser/platform/mod/GeyserModCompressionDisabler.java index 631a21510..89ca53544 100644 --- a/bootstrap/mod/src/main/java/org/geysermc/geyser/platform/mod/GeyserModCompressionDisabler.java +++ b/bootstrap/mod/src/main/java/org/geysermc/geyser/platform/mod/GeyserModCompressionDisabler.java @@ -28,8 +28,8 @@ package org.geysermc.geyser.platform.mod; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelOutboundHandlerAdapter; import io.netty.channel.ChannelPromise; -import net.minecraft.network.protocol.login.ClientboundGameProfilePacket; import net.minecraft.network.protocol.login.ClientboundLoginCompressionPacket; +import net.minecraft.network.protocol.login.ClientboundLoginFinishedPacket; /** * Disables the compression packet (and the compression handlers from being added to the pipeline) for Geyser clients @@ -45,7 +45,7 @@ public class GeyserModCompressionDisabler extends ChannelOutboundHandlerAdapter Class msgClass = msg.getClass(); // Don't let any compression packet get through if (!ClientboundLoginCompressionPacket.class.isAssignableFrom(msgClass)) { - if (ClientboundGameProfilePacket.class.isAssignableFrom(msgClass)) { + if (ClientboundLoginFinishedPacket.class.isAssignableFrom(msgClass)) { // We're past the point that a compression packet can be sent, so we can safely yeet ourselves away ctx.channel().pipeline().remove(this); diff --git a/bootstrap/mod/src/main/java/org/geysermc/geyser/platform/mod/GeyserModUpdateListener.java b/bootstrap/mod/src/main/java/org/geysermc/geyser/platform/mod/GeyserModUpdateListener.java index 6a724155f..ec34766dc 100644 --- a/bootstrap/mod/src/main/java/org/geysermc/geyser/platform/mod/GeyserModUpdateListener.java +++ b/bootstrap/mod/src/main/java/org/geysermc/geyser/platform/mod/GeyserModUpdateListener.java @@ -25,13 +25,13 @@ package org.geysermc.geyser.platform.mod; -import net.minecraft.world.entity.player.Player; +import net.minecraft.server.level.ServerPlayer; import org.geysermc.geyser.Permissions; import org.geysermc.geyser.platform.mod.command.ModCommandSource; import org.geysermc.geyser.util.VersionCheckUtils; public final class GeyserModUpdateListener { - public static void onPlayReady(Player player) { + public static void onPlayReady(ServerPlayer player) { // Should be creating this in the supplier, but we need it for the permission check. // Not a big deal currently because ModCommandSource doesn't load locale, so don't need to try to wait for it. ModCommandSource source = new ModCommandSource(player.createCommandSourceStack()); diff --git a/bootstrap/mod/src/main/java/org/geysermc/geyser/platform/mod/world/GeyserModWorldManager.java b/bootstrap/mod/src/main/java/org/geysermc/geyser/platform/mod/world/GeyserModWorldManager.java index 89452eba3..f85b6e079 100644 --- a/bootstrap/mod/src/main/java/org/geysermc/geyser/platform/mod/world/GeyserModWorldManager.java +++ b/bootstrap/mod/src/main/java/org/geysermc/geyser/platform/mod/world/GeyserModWorldManager.java @@ -33,8 +33,10 @@ import net.minecraft.core.component.DataComponents; import net.minecraft.core.registries.BuiltInRegistries; import net.minecraft.network.chat.Component; import net.minecraft.server.MinecraftServer; +import net.minecraft.server.level.ServerChunkCache; import net.minecraft.server.level.ServerPlayer; import net.minecraft.world.item.ItemStack; +import net.minecraft.world.level.ChunkPos; import net.minecraft.world.level.Level; import net.minecraft.world.level.block.Block; import net.minecraft.world.level.block.entity.BannerBlockEntity; @@ -43,7 +45,6 @@ import net.minecraft.world.level.block.entity.BlockEntity; import net.minecraft.world.level.block.entity.DecoratedPotBlockEntity; import net.minecraft.world.level.chunk.ChunkAccess; import net.minecraft.world.level.chunk.LevelChunkSection; -import net.minecraft.world.level.chunk.status.ChunkStatus; import org.checkerframework.checker.nullness.qual.NonNull; import org.cloudburstmc.math.vector.Vector3i; import org.geysermc.geyser.level.GeyserWorldManager; @@ -84,16 +85,17 @@ public class GeyserModWorldManager extends GeyserWorldManager { } Level level = player.level(); - if (y < level.getMinBuildHeight()) { + if (y < level.getMinY()) { return 0; } - ChunkAccess chunk = level.getChunkSource().getChunk(x >> 4, z >> 4, ChunkStatus.FULL, false); + // Only loads active chunks, and doesn't delegate to main thread + ChunkAccess chunk = ((ServerChunkCache) level.getChunkSource()).chunkMap.getChunkToSend(ChunkPos.asLong(x >> 4, z >> 4)); if (chunk == null) { return 0; } - int worldOffset = level.getMinBuildHeight() >> 4; + int worldOffset = level.getMinY() >> 4; int chunkOffset = (y >> 4) - worldOffset; if (chunkOffset < chunk.getSections().length) { LevelChunkSection section = chunk.getSections()[chunkOffset]; diff --git a/bootstrap/standalone/src/main/java/org/geysermc/geyser/platform/standalone/GeyserStandaloneLogger.java b/bootstrap/standalone/src/main/java/org/geysermc/geyser/platform/standalone/GeyserStandaloneLogger.java index 4bcf11d5e..5636c0ebf 100644 --- a/bootstrap/standalone/src/main/java/org/geysermc/geyser/platform/standalone/GeyserStandaloneLogger.java +++ b/bootstrap/standalone/src/main/java/org/geysermc/geyser/platform/standalone/GeyserStandaloneLogger.java @@ -34,10 +34,24 @@ import org.geysermc.geyser.GeyserImpl; import org.geysermc.geyser.GeyserLogger; import org.geysermc.geyser.command.GeyserCommandSource; import org.geysermc.geyser.text.ChatColor; +import org.jline.reader.Candidate; +import org.jline.reader.LineReader; +import org.jline.reader.LineReaderBuilder; @Slf4j public class GeyserStandaloneLogger extends SimpleTerminalConsole implements GeyserLogger, GeyserCommandSource { + @Override + protected LineReader buildReader(LineReaderBuilder builder) { + builder.completer((reader, line, candidates) -> { + var suggestions = GeyserImpl.getInstance().commandRegistry().suggestionsFor(this, line.line()); + for (var suggestion : suggestions.list()) { + candidates.add(new Candidate(suggestion.suggestion())); + } + }); + return super.buildReader(builder); + } + @Override protected boolean isRunning() { return !GeyserImpl.getInstance().isShuttingDown(); diff --git a/bootstrap/standalone/src/main/resources/log4j2.xml b/bootstrap/standalone/src/main/resources/log4j2.xml index 54f6f9528..bf361a851 100644 --- a/bootstrap/standalone/src/main/resources/log4j2.xml +++ b/bootstrap/standalone/src/main/resources/log4j2.xml @@ -20,6 +20,9 @@ + + + - \ No newline at end of file + diff --git a/bootstrap/velocity/src/main/java/org/geysermc/geyser/platform/velocity/GeyserVelocityPingPassthrough.java b/bootstrap/velocity/src/main/java/org/geysermc/geyser/platform/velocity/GeyserVelocityPingPassthrough.java index b2258d3a3..f20076597 100644 --- a/bootstrap/velocity/src/main/java/org/geysermc/geyser/platform/velocity/GeyserVelocityPingPassthrough.java +++ b/bootstrap/velocity/src/main/java/org/geysermc/geyser/platform/velocity/GeyserVelocityPingPassthrough.java @@ -31,8 +31,10 @@ import com.velocitypowered.api.network.ProtocolVersion; import com.velocitypowered.api.proxy.InboundConnection; import com.velocitypowered.api.proxy.ProxyServer; import com.velocitypowered.api.proxy.server.ServerPing; +import com.velocitypowered.api.proxy.server.ServerPing.Version; import lombok.AllArgsConstructor; import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer; +import org.geysermc.geyser.network.GameProtocol; import org.geysermc.geyser.ping.GeyserPingInfo; import org.geysermc.geyser.ping.IGeyserPingPassthrough; @@ -51,7 +53,9 @@ public class GeyserVelocityPingPassthrough implements IGeyserPingPassthrough { try { event = server.getEventManager().fire(new ProxyPingEvent(new GeyserInboundConnection(inetSocketAddress), ServerPing.builder() .description(server.getConfiguration().getMotd()).onlinePlayers(server.getPlayerCount()) - .maximumPlayers(server.getConfiguration().getShowMaxPlayers()).build())).get(); + .maximumPlayers(server.getConfiguration().getShowMaxPlayers()) + .version(new Version(GameProtocol.getJavaProtocolVersion(), GameProtocol.getJavaMinecraftVersion())) + .build())).get(); } catch (ExecutionException | InterruptedException e) { throw new RuntimeException(e); } diff --git a/build-logic/src/main/kotlin/geyser.modrinth-uploading-conventions.gradle.kts b/build-logic/src/main/kotlin/geyser.modrinth-uploading-conventions.gradle.kts index d2e207fa4..59f85d182 100644 --- a/build-logic/src/main/kotlin/geyser.modrinth-uploading-conventions.gradle.kts +++ b/build-logic/src/main/kotlin/geyser.modrinth-uploading-conventions.gradle.kts @@ -7,12 +7,13 @@ tasks.modrinth.get().dependsOn(tasks.modrinthSyncBody) modrinth { token.set(System.getenv("MODRINTH_TOKEN") ?: "") // Even though this is the default value, apparently this prevents GitHub Actions caching the token? + debugMode.set(System.getenv("MODRINTH_TOKEN") == null) projectId.set("geyser") versionName.set(versionName(project)) versionNumber.set(projectVersion(project)) versionType.set("beta") changelog.set(System.getenv("CHANGELOG") ?: "") - gameVersions.addAll("1.21", libs.minecraft.get().version as String) + gameVersions.addAll("1.21.2", libs.minecraft.get().version as String) failSilently.set(true) syncBodyFrom.set(rootProject.file("README.md").readText()) diff --git a/core/build.gradle.kts b/core/build.gradle.kts index aa4ae3fef..5bb9be1c0 100644 --- a/core/build.gradle.kts +++ b/core/build.gradle.kts @@ -70,6 +70,7 @@ dependencies { // Test testImplementation(libs.junit) testImplementation(libs.gson.runtime) // Record support + testImplementation(libs.mockito) // Annotation Processors compileOnly(projects.ap) diff --git a/core/src/main/java/org/geysermc/geyser/GeyserImpl.java b/core/src/main/java/org/geysermc/geyser/GeyserImpl.java index e9a9b56f9..caaef479d 100644 --- a/core/src/main/java/org/geysermc/geyser/GeyserImpl.java +++ b/core/src/main/java/org/geysermc/geyser/GeyserImpl.java @@ -79,6 +79,7 @@ import org.geysermc.geyser.erosion.UnixSocketClientListener; import org.geysermc.geyser.event.GeyserEventBus; import org.geysermc.geyser.extension.GeyserExtensionManager; import org.geysermc.geyser.impl.MinecraftVersionImpl; +import org.geysermc.geyser.level.BedrockDimension; import org.geysermc.geyser.level.WorldManager; import org.geysermc.geyser.network.GameProtocol; import org.geysermc.geyser.network.netty.GeyserServer; @@ -97,7 +98,6 @@ import org.geysermc.geyser.text.GeyserLocale; import org.geysermc.geyser.text.MinecraftLocale; import org.geysermc.geyser.translator.text.MessageTranslator; import org.geysermc.geyser.util.AssetUtils; -import org.geysermc.geyser.util.DimensionUtils; import org.geysermc.geyser.util.JsonUtils; import org.geysermc.geyser.util.MinecraftAuthLogger; import org.geysermc.geyser.util.NewsHandler; @@ -229,9 +229,14 @@ public class GeyserImpl implements GeyserApi, EventRegistrar { } logger.info("******************************************"); - /* Initialize registries */ - Registries.init(); - BlockRegistries.init(); + /* + First load the registries and then populate them. + Both the block registries and the common registries depend on each other, + so maintaining this order is crucial for Geyser to load. + */ + Registries.load(); + BlockRegistries.populate(); + Registries.populate(); RegistryCache.init(); @@ -424,7 +429,7 @@ public class GeyserImpl implements GeyserApi, EventRegistrar { logger.debug("Epoll is not available; Erosion's Unix socket handling will not work."); } - DimensionUtils.changeBedrockNetherId(config.aboveBedrockNetherBuilding()); // Apply End dimension ID workaround to Nether + BedrockDimension.changeBedrockNetherId(config.aboveBedrockNetherBuilding()); // Apply End dimension ID workaround to Nether Integer bedrockThreadCount = Integer.getInteger("Geyser.BedrockNetworkThreads"); if (bedrockThreadCount == null) { @@ -747,7 +752,9 @@ public class GeyserImpl implements GeyserApi, EventRegistrar { runIfNonNull(newsHandler, NewsHandler::shutdown); runIfNonNull(erosionUnixListener, UnixSocketClientListener::close); - Registries.RESOURCE_PACKS.get().clear(); + if (Registries.RESOURCE_PACKS.loaded()) { + Registries.RESOURCE_PACKS.get().clear(); + } this.setEnabled(false); } diff --git a/core/src/main/java/org/geysermc/geyser/command/CommandRegistry.java b/core/src/main/java/org/geysermc/geyser/command/CommandRegistry.java index 1618932c2..18a422165 100644 --- a/core/src/main/java/org/geysermc/geyser/command/CommandRegistry.java +++ b/core/src/main/java/org/geysermc/geyser/command/CommandRegistry.java @@ -27,6 +27,13 @@ package org.geysermc.geyser.command; import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap; import org.checkerframework.checker.nullness.qual.NonNull; +import org.cloudburstmc.protocol.bedrock.data.command.CommandData; +import org.cloudburstmc.protocol.bedrock.data.command.CommandEnumConstraint; +import org.cloudburstmc.protocol.bedrock.data.command.CommandEnumData; +import org.cloudburstmc.protocol.bedrock.data.command.CommandOverloadData; +import org.cloudburstmc.protocol.bedrock.data.command.CommandParam; +import org.cloudburstmc.protocol.bedrock.data.command.CommandParamData; +import org.cloudburstmc.protocol.bedrock.data.command.CommandPermission; import org.geysermc.geyser.GeyserImpl; import org.geysermc.geyser.api.command.Command; import org.geysermc.geyser.api.event.EventRegistrar; @@ -51,14 +58,29 @@ import org.geysermc.geyser.command.defaults.StopCommand; import org.geysermc.geyser.command.defaults.VersionCommand; import org.geysermc.geyser.event.type.GeyserDefineCommandsEventImpl; import org.geysermc.geyser.extension.command.GeyserExtensionCommand; +import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.text.GeyserLocale; import org.incendo.cloud.Command.Builder; import org.incendo.cloud.CommandManager; import org.incendo.cloud.execution.ExecutionCoordinator; +import org.incendo.cloud.internal.CommandNode; +import org.incendo.cloud.parser.standard.EnumParser; +import org.incendo.cloud.parser.standard.IntegerParser; +import org.incendo.cloud.parser.standard.LiteralParser; +import org.incendo.cloud.parser.standard.StringArrayParser; +import org.incendo.cloud.suggestion.Suggestion; +import org.incendo.cloud.suggestion.Suggestions; +import java.util.ArrayList; import java.util.Collection; +import java.util.Collections; +import java.util.EnumSet; import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Locale; import java.util.Map; +import java.util.Set; import static org.geysermc.geyser.command.GeyserCommand.DEFAULT_ROOT_COMMAND; @@ -299,4 +321,90 @@ public class CommandRegistry implements EventRegistrar { public void runCommand(@NonNull GeyserCommandSource source, @NonNull String command) { cloud.commandExecutor().executeCommand(source, command); } + + public Suggestions suggestionsFor(GeyserCommandSource source, String input) { + return cloud.suggestionFactory().suggestImmediately(source, input); + } + + public void export(GeyserSession session, List bedrockCommands, Set knownAliases) { + cloud.commandTree().rootNodes().forEach(commandTree -> { + var command = commandTree.command(); + // Command null happens if you register an extension command with custom Cloud parameters... + if (command == null || session.hasPermission(command.commandPermission().permissionString())) { + var rootComponent = commandTree.component(); + String name = rootComponent.name(); + if (!knownAliases.add(name)) { + // If the server already defined the command, let's not crash. + return; + } + + LinkedHashMap> values = new LinkedHashMap<>(); + for (String s : rootComponent.aliases()) { + values.put(s, EnumSet.of(CommandEnumConstraint.ALLOW_ALIASES)); + } + CommandEnumData aliases = new CommandEnumData(name + "Aliases", values, false); + + List data = new ArrayList<>(); + for (var node : commandTree.children()) { + List> params = createParamData(session, node); + params.forEach(param -> data.add(new CommandOverloadData(false, param.toArray(CommandParamData[]::new)))); + } + + CommandData bedrockCommand = new CommandData(name, rootComponent.description().textDescription(), + Set.of(CommandData.Flag.NOT_CHEAT), CommandPermission.ANY, aliases, + Collections.emptyList(), data.toArray(new CommandOverloadData[0])); + bedrockCommands.add(bedrockCommand); + } + }); + } + + private List> createParamData(GeyserSession session, CommandNode node) { + var command = node.command(); + if (command != null && !session.hasPermission(command.commandPermission().permissionString())) { + // Triggers with subcommands like Geyser dump, stop, etc. + return Collections.emptyList(); + } + + CommandParamData data = new CommandParamData(); + var component = node.component(); + data.setName(component.name()); + data.setOptional(component.optional()); + var suggestionProvider = component.suggestionProvider(); + if (suggestionProvider instanceof LiteralParser parser) { + Map> values = new LinkedHashMap<>(); + for (String alias : parser.aliases()) { + values.put(alias, Set.of()); + } + + data.setEnumData(new CommandEnumData(component.name(), values, false)); + } else if (suggestionProvider instanceof IntegerParser) { + data.setType(CommandParam.INT); + } else if (suggestionProvider instanceof EnumParser parser) { + LinkedHashMap> map = new LinkedHashMap<>(); + for (Enum e : parser.acceptedValues()) { + map.put(e.name().toLowerCase(Locale.ROOT), Set.of()); + } + + data.setEnumData(new CommandEnumData(component.name().toLowerCase(Locale.ROOT), map, false)); + } else if (component.parser() instanceof StringArrayParser) { + data.setType(CommandParam.TEXT); + } else { + data.setType(CommandParam.STRING); + } + + var children = node.children(); + if (children.isEmpty()) { + List list = new ArrayList<>(); // Must be mutable; parents will be added to list. + list.add(data); + return Collections.singletonList(list); // Safe to do; will be consumed in an addAll call. + } + List> collectiveData = new ArrayList<>(); + // If a node has multiple children, this will need to be represented + // by creating a new list/branch for each and cloning this node down each line. + for (var child : children) { + collectiveData.addAll(createParamData(session, child)); + } + collectiveData.forEach(list -> list.add(0, data)); + return collectiveData; + } } diff --git a/core/src/main/java/org/geysermc/geyser/entity/EntityDefinition.java b/core/src/main/java/org/geysermc/geyser/entity/EntityDefinition.java index 85458abb6..c4dae5411 100644 --- a/core/src/main/java/org/geysermc/geyser/entity/EntityDefinition.java +++ b/core/src/main/java/org/geysermc/geyser/entity/EntityDefinition.java @@ -25,10 +25,10 @@ package org.geysermc.geyser.entity; -import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.EntityMetadata; -import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.MetadataType; -import org.geysermc.mcprotocollib.protocol.data.game.entity.type.EntityType; import it.unimi.dsi.fastutil.objects.ObjectArrayList; +import java.util.List; +import java.util.Locale; +import java.util.function.BiConsumer; import lombok.Setter; import lombok.experimental.Accessors; import org.geysermc.geyser.GeyserImpl; @@ -37,10 +37,9 @@ import org.geysermc.geyser.entity.properties.GeyserEntityProperties; import org.geysermc.geyser.entity.type.Entity; import org.geysermc.geyser.registry.Registries; import org.geysermc.geyser.translator.entity.EntityMetadataTranslator; - -import java.util.List; -import java.util.Locale; -import java.util.function.BiConsumer; +import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.EntityMetadata; +import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.MetadataType; +import org.geysermc.mcprotocollib.protocol.data.game.entity.type.EntityType; /** * Represents data for an entity. This includes properties such as height and width, as well as the list of entity 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 5932ecf41..47b97c934 100644 --- a/core/src/main/java/org/geysermc/geyser/entity/EntityDefinitions.java +++ b/core/src/main/java/org/geysermc/geyser/entity/EntityDefinitions.java @@ -25,59 +25,163 @@ package org.geysermc.geyser.entity; -import org.geysermc.geyser.entity.type.AbstractWindChargeEntity; -import org.geysermc.geyser.entity.factory.EntityFactory; -import org.geysermc.geyser.entity.type.living.monster.raid.RavagerEntity; -import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.MetadataType; -import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.type.BooleanEntityMetadata; -import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.type.FloatEntityMetadata; -import org.geysermc.mcprotocollib.protocol.data.game.entity.type.EntityType; import org.cloudburstmc.protocol.bedrock.data.entity.EntityDataTypes; import org.cloudburstmc.protocol.bedrock.data.entity.EntityFlag; +import org.geysermc.geyser.entity.factory.EntityFactory; import org.geysermc.geyser.entity.properties.GeyserEntityProperties; -import org.geysermc.geyser.entity.type.*; -import org.geysermc.geyser.entity.type.living.*; -import org.geysermc.geyser.entity.type.living.animal.*; -import org.geysermc.geyser.entity.type.living.animal.horse.*; +import org.geysermc.geyser.entity.type.AbstractArrowEntity; +import org.geysermc.geyser.entity.type.AbstractWindChargeEntity; +import org.geysermc.geyser.entity.type.AreaEffectCloudEntity; +import org.geysermc.geyser.entity.type.ArrowEntity; +import org.geysermc.geyser.entity.type.BoatEntity; +import org.geysermc.geyser.entity.type.ChestBoatEntity; +import org.geysermc.geyser.entity.type.CommandBlockMinecartEntity; +import org.geysermc.geyser.entity.type.DisplayBaseEntity; +import org.geysermc.geyser.entity.type.EnderCrystalEntity; +import org.geysermc.geyser.entity.type.Entity; +import org.geysermc.geyser.entity.type.EvokerFangsEntity; +import org.geysermc.geyser.entity.type.ExpOrbEntity; +import org.geysermc.geyser.entity.type.FallingBlockEntity; +import org.geysermc.geyser.entity.type.FireballEntity; +import org.geysermc.geyser.entity.type.FireworkEntity; +import org.geysermc.geyser.entity.type.FishingHookEntity; +import org.geysermc.geyser.entity.type.FurnaceMinecartEntity; +import org.geysermc.geyser.entity.type.InteractionEntity; +import org.geysermc.geyser.entity.type.ItemEntity; +import org.geysermc.geyser.entity.type.ItemFrameEntity; +import org.geysermc.geyser.entity.type.LeashKnotEntity; +import org.geysermc.geyser.entity.type.LightningEntity; +import org.geysermc.geyser.entity.type.LivingEntity; +import org.geysermc.geyser.entity.type.MinecartEntity; +import org.geysermc.geyser.entity.type.PaintingEntity; +import org.geysermc.geyser.entity.type.SpawnerMinecartEntity; +import org.geysermc.geyser.entity.type.TNTEntity; +import org.geysermc.geyser.entity.type.TextDisplayEntity; +import org.geysermc.geyser.entity.type.ThrowableEntity; +import org.geysermc.geyser.entity.type.ThrowableItemEntity; +import org.geysermc.geyser.entity.type.ThrownPotionEntity; +import org.geysermc.geyser.entity.type.TridentEntity; +import org.geysermc.geyser.entity.type.WitherSkullEntity; +import org.geysermc.geyser.entity.type.living.AbstractFishEntity; +import org.geysermc.geyser.entity.type.living.AgeableEntity; +import org.geysermc.geyser.entity.type.living.AllayEntity; +import org.geysermc.geyser.entity.type.living.ArmorStandEntity; +import org.geysermc.geyser.entity.type.living.BatEntity; +import org.geysermc.geyser.entity.type.living.DolphinEntity; +import org.geysermc.geyser.entity.type.living.GlowSquidEntity; +import org.geysermc.geyser.entity.type.living.IronGolemEntity; +import org.geysermc.geyser.entity.type.living.MagmaCubeEntity; +import org.geysermc.geyser.entity.type.living.MobEntity; +import org.geysermc.geyser.entity.type.living.SlimeEntity; +import org.geysermc.geyser.entity.type.living.SnowGolemEntity; +import org.geysermc.geyser.entity.type.living.SquidEntity; +import org.geysermc.geyser.entity.type.living.TadpoleEntity; +import org.geysermc.geyser.entity.type.living.animal.ArmadilloEntity; +import org.geysermc.geyser.entity.type.living.animal.AxolotlEntity; +import org.geysermc.geyser.entity.type.living.animal.BeeEntity; +import org.geysermc.geyser.entity.type.living.animal.ChickenEntity; +import org.geysermc.geyser.entity.type.living.animal.CowEntity; +import org.geysermc.geyser.entity.type.living.animal.FoxEntity; +import org.geysermc.geyser.entity.type.living.animal.FrogEntity; +import org.geysermc.geyser.entity.type.living.animal.GoatEntity; +import org.geysermc.geyser.entity.type.living.animal.HoglinEntity; +import org.geysermc.geyser.entity.type.living.animal.MooshroomEntity; +import org.geysermc.geyser.entity.type.living.animal.OcelotEntity; +import org.geysermc.geyser.entity.type.living.animal.PandaEntity; +import org.geysermc.geyser.entity.type.living.animal.PigEntity; +import org.geysermc.geyser.entity.type.living.animal.PolarBearEntity; +import org.geysermc.geyser.entity.type.living.animal.PufferFishEntity; +import org.geysermc.geyser.entity.type.living.animal.RabbitEntity; +import org.geysermc.geyser.entity.type.living.animal.SheepEntity; +import org.geysermc.geyser.entity.type.living.animal.SnifferEntity; +import org.geysermc.geyser.entity.type.living.animal.StriderEntity; +import org.geysermc.geyser.entity.type.living.animal.TropicalFishEntity; +import org.geysermc.geyser.entity.type.living.animal.TurtleEntity; +import org.geysermc.geyser.entity.type.living.animal.horse.AbstractHorseEntity; +import org.geysermc.geyser.entity.type.living.animal.horse.CamelEntity; +import org.geysermc.geyser.entity.type.living.animal.horse.ChestedHorseEntity; +import org.geysermc.geyser.entity.type.living.animal.horse.HorseEntity; +import org.geysermc.geyser.entity.type.living.animal.horse.LlamaEntity; +import org.geysermc.geyser.entity.type.living.animal.horse.SkeletonHorseEntity; +import org.geysermc.geyser.entity.type.living.animal.horse.TraderLlamaEntity; +import org.geysermc.geyser.entity.type.living.animal.horse.ZombieHorseEntity; import org.geysermc.geyser.entity.type.living.animal.tameable.CatEntity; import org.geysermc.geyser.entity.type.living.animal.tameable.ParrotEntity; import org.geysermc.geyser.entity.type.living.animal.tameable.TameableEntity; import org.geysermc.geyser.entity.type.living.animal.tameable.WolfEntity; import org.geysermc.geyser.entity.type.living.merchant.AbstractMerchantEntity; import org.geysermc.geyser.entity.type.living.merchant.VillagerEntity; -import org.geysermc.geyser.entity.type.living.monster.*; +import org.geysermc.geyser.entity.type.living.monster.AbstractSkeletonEntity; +import org.geysermc.geyser.entity.type.living.monster.BasePiglinEntity; +import org.geysermc.geyser.entity.type.living.monster.BlazeEntity; +import org.geysermc.geyser.entity.type.living.monster.BoggedEntity; +import org.geysermc.geyser.entity.type.living.monster.BreezeEntity; +import org.geysermc.geyser.entity.type.living.monster.CreeperEntity; +import org.geysermc.geyser.entity.type.living.monster.ElderGuardianEntity; +import org.geysermc.geyser.entity.type.living.monster.EnderDragonEntity; +import org.geysermc.geyser.entity.type.living.monster.EnderDragonPartEntity; +import org.geysermc.geyser.entity.type.living.monster.EndermanEntity; +import org.geysermc.geyser.entity.type.living.monster.GhastEntity; +import org.geysermc.geyser.entity.type.living.monster.GiantEntity; +import org.geysermc.geyser.entity.type.living.monster.GuardianEntity; +import org.geysermc.geyser.entity.type.living.monster.MonsterEntity; +import org.geysermc.geyser.entity.type.living.monster.PhantomEntity; +import org.geysermc.geyser.entity.type.living.monster.PiglinEntity; +import org.geysermc.geyser.entity.type.living.monster.ShulkerEntity; +import org.geysermc.geyser.entity.type.living.monster.SkeletonEntity; +import org.geysermc.geyser.entity.type.living.monster.SpiderEntity; +import org.geysermc.geyser.entity.type.living.monster.VexEntity; +import org.geysermc.geyser.entity.type.living.monster.WardenEntity; +import org.geysermc.geyser.entity.type.living.monster.WitherEntity; +import org.geysermc.geyser.entity.type.living.monster.ZoglinEntity; +import org.geysermc.geyser.entity.type.living.monster.ZombieEntity; +import org.geysermc.geyser.entity.type.living.monster.ZombieVillagerEntity; +import org.geysermc.geyser.entity.type.living.monster.ZombifiedPiglinEntity; import org.geysermc.geyser.entity.type.living.monster.raid.PillagerEntity; import org.geysermc.geyser.entity.type.living.monster.raid.RaidParticipantEntity; +import org.geysermc.geyser.entity.type.living.monster.raid.RavagerEntity; import org.geysermc.geyser.entity.type.living.monster.raid.SpellcasterIllagerEntity; import org.geysermc.geyser.entity.type.living.monster.raid.VindicatorEntity; import org.geysermc.geyser.entity.type.player.PlayerEntity; import org.geysermc.geyser.registry.Registries; import org.geysermc.geyser.translator.text.MessageTranslator; +import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.MetadataType; +import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.type.BooleanEntityMetadata; +import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.type.FloatEntityMetadata; +import org.geysermc.mcprotocollib.protocol.data.game.entity.type.EntityType; public final class EntityDefinitions { + public static final EntityDefinition ACACIA_BOAT; + public static final EntityDefinition ACACIA_CHEST_BOAT; public static final EntityDefinition ALLAY; public static final EntityDefinition AREA_EFFECT_CLOUD; public static final EntityDefinition ARMADILLO; public static final EntityDefinition ARMOR_STAND; public static final EntityDefinition ARROW; public static final EntityDefinition AXOLOTL; + public static final EntityDefinition BAMBOO_RAFT; + public static final EntityDefinition BAMBOO_CHEST_RAFT; public static final EntityDefinition BAT; public static final EntityDefinition BEE; + public static final EntityDefinition BIRCH_BOAT; + public static final EntityDefinition BIRCH_CHEST_BOAT; public static final EntityDefinition BLAZE; - public static final EntityDefinition BOAT; public static final EntityDefinition BOGGED; public static final EntityDefinition BREEZE; public static final EntityDefinition BREEZE_WIND_CHARGE; public static final EntityDefinition CAMEL; public static final EntityDefinition CAT; public static final EntityDefinition CAVE_SPIDER; + public static final EntityDefinition CHERRY_BOAT; + public static final EntityDefinition CHERRY_CHEST_BOAT; public static final EntityDefinition CHEST_MINECART; public static final EntityDefinition CHICKEN; - public static final EntityDefinition CHEST_BOAT; public static final EntityDefinition COD; public static final EntityDefinition COMMAND_BLOCK_MINECART; public static final EntityDefinition COW; public static final EntityDefinition CREEPER; + public static final EntityDefinition DARK_OAK_BOAT; + public static final EntityDefinition DARK_OAK_CHEST_BOAT; public static final EntityDefinition DOLPHIN; public static final EntityDefinition DONKEY; public static final EntityDefinition DRAGON_FIREBALL; @@ -116,14 +220,20 @@ public final class EntityDefinitions { public static final EntityDefinition IRON_GOLEM; public static final EntityDefinition ITEM; public static final EntityDefinition ITEM_FRAME; + public static final EntityDefinition JUNGLE_BOAT; + public static final EntityDefinition JUNGLE_CHEST_BOAT; public static final EntityDefinition LEASH_KNOT; public static final EntityDefinition LIGHTNING_BOLT; public static final EntityDefinition LLAMA; public static final EntityDefinition LLAMA_SPIT; public static final EntityDefinition MAGMA_CUBE; + public static final EntityDefinition MANGROVE_BOAT; + public static final EntityDefinition MANGROVE_CHEST_BOAT; public static final EntityDefinition MINECART; public static final EntityDefinition MOOSHROOM; public static final EntityDefinition MULE; + public static final EntityDefinition OAK_BOAT; + public static final EntityDefinition OAK_CHEST_BOAT; public static final EntityDefinition OCELOT; public static final EntityDefinition PAINTING; public static final EntityDefinition PANDA; @@ -154,6 +264,8 @@ public final class EntityDefinitions { public static final EntityDefinition SPAWNER_MINECART; // Not present on Bedrock public static final EntityDefinition SPECTRAL_ARROW; public static final EntityDefinition SPIDER; + public static final EntityDefinition SPRUCE_BOAT; + public static final EntityDefinition SPRUCE_CHEST_BOAT; public static final EntityDefinition SQUID; public static final EntityDefinition STRAY; public static final EntityDefinition STRIDER; @@ -212,23 +324,6 @@ public final class EntityDefinitions { .addTranslator(null) // Waiting .addTranslator(MetadataType.PARTICLE, AreaEffectCloudEntity::setParticle) .build(); - BOAT = EntityDefinition.inherited(BoatEntity::new, entityBase) - .type(EntityType.BOAT) - .height(0.6f).width(1.6f) - .offset(0.35f) - .addTranslator(MetadataType.INT, (boatEntity, entityMetadata) -> boatEntity.getDirtyMetadata().put(EntityDataTypes.HURT_TICKS, entityMetadata.getValue())) // Time since last hit - .addTranslator(MetadataType.INT, (boatEntity, entityMetadata) -> boatEntity.getDirtyMetadata().put(EntityDataTypes.HURT_DIRECTION, entityMetadata.getValue())) // Rocking direction - .addTranslator(MetadataType.FLOAT, (boatEntity, entityMetadata) -> - // 'Health' in Bedrock, damage taken in Java - it makes motion in Bedrock - boatEntity.getDirtyMetadata().put(EntityDataTypes.STRUCTURAL_INTEGRITY, 40 - ((int) ((FloatEntityMetadata) entityMetadata).getPrimitiveValue()))) - .addTranslator(MetadataType.INT, BoatEntity::setVariant) - .addTranslator(MetadataType.BOOLEAN, BoatEntity::setPaddlingLeft) - .addTranslator(MetadataType.BOOLEAN, BoatEntity::setPaddlingRight) - .addTranslator(MetadataType.INT, (boatEntity, entityMetadata) -> boatEntity.getDirtyMetadata().put(EntityDataTypes.BOAT_BUBBLE_TIME, entityMetadata.getValue())) // May not actually do anything - .build(); - CHEST_BOAT = EntityDefinition.inherited(ChestBoatEntity::new, BOAT) - .type(EntityType.CHEST_BOAT) - .build(); DRAGON_FIREBALL = EntityDefinition.inherited(FireballEntity::new, entityBase) .type(EntityType.DRAGON_FIREBALL) .heightAndWidth(1.0f) @@ -397,6 +492,7 @@ public final class EntityDefinitions { EntityDefinition abstractArrowBase = EntityDefinition.inherited(AbstractArrowEntity::new, entityBase) .addTranslator(MetadataType.BYTE, AbstractArrowEntity::setArrowFlags) .addTranslator(null) // "Piercing level" + .addTranslator(null) // If the arrow is in the ground .build(); ARROW = EntityDefinition.inherited(ArrowEntity::new, abstractArrowBase) .type(EntityType.ARROW) @@ -471,6 +567,45 @@ public final class EntityDefinitions { .build(false); } + // Boats + { + EntityDefinition boatBase = EntityDefinition.inherited(null, entityBase) + .height(0.6f).width(1.6f) + .offset(0.35f) + .addTranslator(MetadataType.INT, (boatEntity, entityMetadata) -> boatEntity.getDirtyMetadata().put(EntityDataTypes.HURT_TICKS, entityMetadata.getValue())) // Time since last hit + .addTranslator(MetadataType.INT, (boatEntity, entityMetadata) -> boatEntity.getDirtyMetadata().put(EntityDataTypes.HURT_DIRECTION, entityMetadata.getValue())) // Rocking direction + .addTranslator(MetadataType.FLOAT, (boatEntity, entityMetadata) -> + // 'Health' in Bedrock, damage taken in Java - it makes motion in Bedrock + boatEntity.getDirtyMetadata().put(EntityDataTypes.STRUCTURAL_INTEGRITY, 40 - ((int) ((FloatEntityMetadata) entityMetadata).getPrimitiveValue()))) + .addTranslator(MetadataType.BOOLEAN, BoatEntity::setPaddlingLeft) + .addTranslator(MetadataType.BOOLEAN, BoatEntity::setPaddlingRight) + .addTranslator(MetadataType.INT, (boatEntity, entityMetadata) -> boatEntity.getDirtyMetadata().put(EntityDataTypes.BOAT_BUBBLE_TIME, entityMetadata.getValue())) // May not actually do anything + .build(); + + ACACIA_BOAT = buildBoat(boatBase, EntityType.ACACIA_BOAT, BoatEntity.BoatVariant.ACACIA); + BAMBOO_RAFT = buildBoat(boatBase, EntityType.BAMBOO_RAFT, BoatEntity.BoatVariant.BAMBOO); + BIRCH_BOAT = buildBoat(boatBase, EntityType.BIRCH_BOAT, BoatEntity.BoatVariant.BIRCH); + CHERRY_BOAT = buildBoat(boatBase, EntityType.CHERRY_BOAT, BoatEntity.BoatVariant.CHERRY); + DARK_OAK_BOAT = buildBoat(boatBase, EntityType.DARK_OAK_BOAT, BoatEntity.BoatVariant.DARK_OAK); + JUNGLE_BOAT = buildBoat(boatBase, EntityType.JUNGLE_BOAT, BoatEntity.BoatVariant.JUNGLE); + MANGROVE_BOAT = buildBoat(boatBase, EntityType.MANGROVE_BOAT, BoatEntity.BoatVariant.MANGROVE); + OAK_BOAT = buildBoat(boatBase, EntityType.OAK_BOAT, BoatEntity.BoatVariant.OAK); + SPRUCE_BOAT = buildBoat(boatBase, EntityType.SPRUCE_BOAT, BoatEntity.BoatVariant.SPRUCE); + + EntityDefinition chestBoatBase = EntityDefinition.inherited(null, boatBase) + .build(); + + ACACIA_CHEST_BOAT = buildChestBoat(chestBoatBase, EntityType.ACACIA_CHEST_BOAT, BoatEntity.BoatVariant.ACACIA); + BAMBOO_CHEST_RAFT = buildChestBoat(chestBoatBase, EntityType.BAMBOO_CHEST_RAFT, BoatEntity.BoatVariant.BAMBOO); + BIRCH_CHEST_BOAT = buildChestBoat(chestBoatBase, EntityType.BIRCH_CHEST_BOAT, BoatEntity.BoatVariant.BIRCH); + CHERRY_CHEST_BOAT = buildChestBoat(chestBoatBase, EntityType.CHERRY_CHEST_BOAT, BoatEntity.BoatVariant.CHERRY); + DARK_OAK_CHEST_BOAT = buildChestBoat(chestBoatBase, EntityType.DARK_OAK_CHEST_BOAT, BoatEntity.BoatVariant.DARK_OAK); + JUNGLE_CHEST_BOAT = buildChestBoat(chestBoatBase, EntityType.JUNGLE_CHEST_BOAT, BoatEntity.BoatVariant.JUNGLE); + MANGROVE_CHEST_BOAT = buildChestBoat(chestBoatBase, EntityType.MANGROVE_CHEST_BOAT, BoatEntity.BoatVariant.MANGROVE); + OAK_CHEST_BOAT = buildChestBoat(chestBoatBase, EntityType.OAK_CHEST_BOAT, BoatEntity.BoatVariant.OAK); + SPRUCE_CHEST_BOAT = buildChestBoat(chestBoatBase, EntityType.SPRUCE_CHEST_BOAT, BoatEntity.BoatVariant.SPRUCE); + } + EntityDefinition livingEntityBase = EntityDefinition.inherited(LivingEntity::new, entityBase) .addTranslator(MetadataType.BYTE, LivingEntity::setLivingEntityFlags) .addTranslator(MetadataType.FLOAT, LivingEntity::setHealth) @@ -544,14 +679,6 @@ public final class EntityDefinitions { .addTranslator(MetadataType.BOOLEAN, (entity, entityMetadata) -> entity.setFlag(EntityFlag.POWERED, ((BooleanEntityMetadata) entityMetadata).getPrimitiveValue())) .addTranslator(MetadataType.BOOLEAN, CreeperEntity::setIgnited) .build(); - DOLPHIN = EntityDefinition.inherited(DolphinEntity::new, mobEntityBase) - .type(EntityType.DOLPHIN) - .height(0.6f).width(0.9f) - //TODO check - .addTranslator(null) // treasure position - .addTranslator(null) // "got fish" - .addTranslator(null) // "moistness level" - .build(); ENDERMAN = EntityDefinition.inherited(EndermanEntity::new, mobEntityBase) .type(EntityType.ENDERMAN) .height(2.9f).width(0.6f) @@ -621,10 +748,6 @@ public final class EntityDefinitions { .type(EntityType.CAVE_SPIDER) .height(0.5f).width(0.7f) .build(); - SQUID = EntityDefinition.inherited(SquidEntity::new, mobEntityBase) - .type(EntityType.SQUID) - .heightAndWidth(0.8f) - .build(); STRAY = EntityDefinition.inherited(AbstractSkeletonEntity::new, mobEntityBase) .type(EntityType.STRAY) .height(1.8f).width(0.6f) @@ -723,6 +846,7 @@ public final class EntityDefinitions { SALMON = EntityDefinition.inherited(abstractFishEntityBase.factory(), abstractFishEntityBase) .type(EntityType.SALMON) .height(0.5f).width(0.7f) + .addTranslator(null) // Scale/variant - TODO .build(); TADPOLE = EntityDefinition.inherited(TadpoleEntity::new, abstractFishEntityBase) .type(EntityType.TADPOLE) @@ -750,11 +874,6 @@ public final class EntityDefinitions { .height(1.95f).width(0.6f) .build(); - GLOW_SQUID = EntityDefinition.inherited(GlowSquidEntity::new, SQUID) - .type(EntityType.GLOW_SQUID) - .addTranslator(null) // Set dark ticks remaining, possible TODO - .build(); - EntityDefinition raidParticipantEntityBase = EntityDefinition.inherited(RaidParticipantEntity::new, mobEntityBase) .addTranslator(null) // Celebrating //TODO .build(); @@ -946,6 +1065,26 @@ public final class EntityDefinitions { .build(); } + // Water creatures (AgeableWaterCreature) + { + DOLPHIN = EntityDefinition.inherited(DolphinEntity::new, ageableEntityBase) + .type(EntityType.DOLPHIN) + .height(0.6f).width(0.9f) + //TODO check + .addTranslator(null) // treasure position + .addTranslator(null) // "got fish" + .addTranslator(null) // "moistness level" + .build(); + SQUID = EntityDefinition.inherited(SquidEntity::new, ageableEntityBase) + .type(EntityType.SQUID) + .heightAndWidth(0.8f) + .build(); + GLOW_SQUID = EntityDefinition.inherited(GlowSquidEntity::new, SQUID) + .type(EntityType.GLOW_SQUID) + .addTranslator(null) // Set dark ticks remaining, possible TODO + .build(); + } + // Horses { EntityDefinition abstractHorseEntityBase = EntityDefinition.inherited(AbstractHorseEntity::new, ageableEntityBase) @@ -1028,6 +1167,22 @@ public final class EntityDefinitions { Registries.JAVA_ENTITY_IDENTIFIERS.get().put("minecraft:marker", null); // We don't need an entity definition for this as it is never sent over the network } + private static EntityDefinition buildBoat(EntityDefinition base, EntityType entityType, BoatEntity.BoatVariant variant) { + return EntityDefinition.inherited((session, javaId, bedrockId, uuid, definition, position, motion, yaw, pitch, headYaw) -> + new BoatEntity(session, javaId, bedrockId, uuid, definition, position, motion, yaw, variant), base) + .type(entityType) + .identifier("minecraft:boat") + .build(); + } + + private static EntityDefinition buildChestBoat(EntityDefinition base, EntityType entityType, BoatEntity.BoatVariant variant) { + return EntityDefinition.inherited((session, javaId, bedrockId, uuid, definition, position, motion, yaw, pitch, headYaw) -> + new ChestBoatEntity(session, javaId, bedrockId, uuid, definition, position, motion, yaw, variant), base) + .type(entityType) + .identifier("minecraft:chest_boat") + .build(); + } + public static void init() { // no-op } diff --git a/core/src/main/java/org/geysermc/geyser/entity/GeyserDirtyMetadata.java b/core/src/main/java/org/geysermc/geyser/entity/GeyserDirtyMetadata.java index bc567ab91..1ee84f4b5 100644 --- a/core/src/main/java/org/geysermc/geyser/entity/GeyserDirtyMetadata.java +++ b/core/src/main/java/org/geysermc/geyser/entity/GeyserDirtyMetadata.java @@ -32,7 +32,7 @@ import org.cloudburstmc.protocol.bedrock.data.entity.EntityDataType; import java.util.Map; /** - * A write-only wrapper for temporarily storing entity metadata that will be sent to Bedrock. + * A wrapper for temporarily storing entity metadata that will be sent to Bedrock. */ public final class GeyserDirtyMetadata { private final Map, Object> metadata = new Object2ObjectLinkedOpenHashMap<>(); @@ -53,6 +53,14 @@ public final class GeyserDirtyMetadata { return !metadata.isEmpty(); } + /** + * Intended for testing purposes only + */ + public T get(EntityDataType entityData) { + //noinspection unchecked + return (T) metadata.get(entityData); + } + @Override public String toString() { return metadata.toString(); diff --git a/core/src/main/java/org/geysermc/geyser/entity/type/BoatEntity.java b/core/src/main/java/org/geysermc/geyser/entity/type/BoatEntity.java index 47ae6777a..86accea17 100644 --- a/core/src/main/java/org/geysermc/geyser/entity/type/BoatEntity.java +++ b/core/src/main/java/org/geysermc/geyser/entity/type/BoatEntity.java @@ -32,12 +32,14 @@ import org.cloudburstmc.protocol.bedrock.packet.AnimatePacket; import org.cloudburstmc.protocol.bedrock.packet.MoveEntityAbsolutePacket; import org.geysermc.geyser.entity.EntityDefinition; import org.geysermc.geyser.entity.EntityDefinitions; +import org.geysermc.geyser.item.Items; +import org.geysermc.geyser.item.type.Item; import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.util.InteractionResult; import org.geysermc.geyser.util.InteractiveTag; import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.type.BooleanEntityMetadata; -import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.type.IntEntityMetadata; import org.geysermc.mcprotocollib.protocol.data.game.entity.player.Hand; +import org.geysermc.mcprotocollib.protocol.packet.ingame.serverbound.level.ServerboundPaddleBoatPacket; import java.util.UUID; @@ -63,16 +65,19 @@ public class BoatEntity extends Entity implements Leashable, Tickable { * Saved for using the "pick" functionality on a boat. */ @Getter - private int variant; + protected final BoatVariant variant; private long leashHolderBedrockId = -1; // Looks too fast and too choppy with 0.1f, which is how I believe the Microsoftian client handles it private final float ROWING_SPEED = 0.1f; - public BoatEntity(GeyserSession session, int entityId, long geyserId, UUID uuid, EntityDefinition definition, Vector3f position, Vector3f motion, float yaw, float pitch, float headYaw) { + public BoatEntity(GeyserSession session, int entityId, long geyserId, UUID uuid, EntityDefinition definition, Vector3f position, Vector3f motion, float yaw, BoatVariant variant) { // Initial rotation is incorrect super(session, entityId, geyserId, uuid, definition, position.add(0d, definition.offset(), 0d), motion, yaw + 90, 0, yaw + 90); + this.variant = variant; + + dirtyMetadata.put(EntityDataTypes.VARIANT, variant.ordinal()); // Required to be able to move on land 1.16.200+ or apply gravity not in the water 1.16.100+ dirtyMetadata.put(EntityDataTypes.IS_BUOYANT, true); @@ -124,15 +129,6 @@ public class BoatEntity extends Entity implements Leashable, Tickable { moveRelative(0, 0, 0, yaw + 90, 0, 0, isOnGround); } - public void setVariant(IntEntityMetadata entityMetadata) { - variant = entityMetadata.getPrimitiveValue(); - dirtyMetadata.put(EntityDataTypes.VARIANT, switch (variant) { - case 6, 7, 8 -> variant - 1; // dark_oak, mangrove, bamboo - case 5 -> 8; // cherry - default -> variant; - }); - } - public void setPaddlingLeft(BooleanEntityMetadata entityMetadata) { isPaddlingLeft = entityMetadata.getPrimitiveValue(); if (!isPaddlingLeft) { @@ -187,6 +183,12 @@ public class BoatEntity extends Entity implements Leashable, Tickable { @Override public void tick() { // Java sends simply "true" and "false" (is_paddling_left), Bedrock keeps sending packets as you're rowing + if (session.getPlayerEntity().getVehicle() == this) { + // For packet timing accuracy, we'll send the packets here, as that's what Java Edition 1.21.3 does. + ServerboundPaddleBoatPacket steerPacket = new ServerboundPaddleBoatPacket(session.isSteeringLeft(), session.isSteeringRight()); + session.sendDownstreamGamePacket(steerPacket); + return; + } doTick = !doTick; // Run every 100 ms if (!doTick || passengers.isEmpty()) { return; @@ -212,6 +214,10 @@ public class BoatEntity extends Entity implements Leashable, Tickable { return leashHolderBedrockId; } + public Item getPickItem() { + return variant.pickItem; + } + private void sendAnimationPacket(GeyserSession session, Entity rower, AnimatePacket.Action action, float rowTime) { AnimatePacket packet = new AnimatePacket(); packet.setRuntimeEntityId(rower.getGeyserId()); @@ -219,4 +225,27 @@ public class BoatEntity extends Entity implements Leashable, Tickable { packet.setRowingTime(rowTime); session.sendUpstreamPacket(packet); } + + /** + * Ordered by Bedrock ordinal + */ + public enum BoatVariant { + OAK(Items.OAK_BOAT, Items.OAK_CHEST_BOAT), + SPRUCE(Items.SPRUCE_BOAT, Items.SPRUCE_CHEST_BOAT), + BIRCH(Items.BIRCH_BOAT, Items.BIRCH_CHEST_BOAT), + JUNGLE(Items.JUNGLE_BOAT, Items.JUNGLE_CHEST_BOAT), + ACACIA(Items.ACACIA_BOAT, Items.ACACIA_CHEST_BOAT), + DARK_OAK(Items.DARK_OAK_BOAT, Items.DARK_OAK_CHEST_BOAT), + MANGROVE(Items.MANGROVE_BOAT, Items.MANGROVE_CHEST_BOAT), + BAMBOO(Items.BAMBOO_RAFT, Items.BAMBOO_CHEST_RAFT), + CHERRY(Items.CHERRY_BOAT, Items.CHERRY_CHEST_BOAT); + + private final Item pickItem; + final Item chestPickItem; + + BoatVariant(Item pickItem, Item chestPickItem) { + this.pickItem = pickItem; + this.chestPickItem = chestPickItem; + } + } } diff --git a/core/src/main/java/org/geysermc/geyser/entity/type/ChestBoatEntity.java b/core/src/main/java/org/geysermc/geyser/entity/type/ChestBoatEntity.java index 479b4d80d..967da41df 100644 --- a/core/src/main/java/org/geysermc/geyser/entity/type/ChestBoatEntity.java +++ b/core/src/main/java/org/geysermc/geyser/entity/type/ChestBoatEntity.java @@ -27,6 +27,7 @@ package org.geysermc.geyser.entity.type; import org.cloudburstmc.math.vector.Vector3f; import org.geysermc.geyser.entity.EntityDefinition; +import org.geysermc.geyser.item.type.Item; import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.util.InteractionResult; import org.geysermc.geyser.util.InteractiveTag; @@ -35,8 +36,8 @@ import org.geysermc.mcprotocollib.protocol.data.game.entity.player.Hand; import java.util.UUID; public class ChestBoatEntity extends BoatEntity { - public ChestBoatEntity(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); + public ChestBoatEntity(GeyserSession session, int entityId, long geyserId, UUID uuid, EntityDefinition definition, Vector3f position, Vector3f motion, float yaw, BoatVariant variant) { + super(session, entityId, geyserId, uuid, definition, position, motion, yaw, variant); } @Override @@ -48,4 +49,9 @@ public class ChestBoatEntity extends BoatEntity { public InteractionResult interact(Hand hand) { return passengers.isEmpty() && !session.isSneaking() ? super.interact(hand) : InteractionResult.SUCCESS; } + + @Override + public Item getPickItem() { + return this.variant.chestPickItem; + } } 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 6024cde8f..73c8d298d 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 @@ -35,12 +35,18 @@ import org.cloudburstmc.math.vector.Vector3f; import org.cloudburstmc.protocol.bedrock.data.entity.EntityDataTypes; import org.cloudburstmc.protocol.bedrock.data.entity.EntityEventType; import org.cloudburstmc.protocol.bedrock.data.entity.EntityFlag; -import org.cloudburstmc.protocol.bedrock.packet.*; +import org.cloudburstmc.protocol.bedrock.packet.AddEntityPacket; +import org.cloudburstmc.protocol.bedrock.packet.EntityEventPacket; +import org.cloudburstmc.protocol.bedrock.packet.MoveEntityAbsolutePacket; +import org.cloudburstmc.protocol.bedrock.packet.MoveEntityDeltaPacket; +import org.cloudburstmc.protocol.bedrock.packet.RemoveEntityPacket; +import org.cloudburstmc.protocol.bedrock.packet.SetEntityDataPacket; import org.geysermc.geyser.api.entity.type.GeyserEntity; import org.geysermc.geyser.entity.EntityDefinition; import org.geysermc.geyser.entity.GeyserDirtyMetadata; import org.geysermc.geyser.entity.properties.GeyserEntityPropertyManager; import org.geysermc.geyser.item.Items; +import org.geysermc.geyser.scoreboard.Team; import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.translator.text.MessageTranslator; import org.geysermc.geyser.util.EntityUtils; @@ -55,12 +61,16 @@ import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.type.IntEnt import org.geysermc.mcprotocollib.protocol.data.game.entity.player.Hand; import org.geysermc.mcprotocollib.protocol.data.game.entity.type.EntityType; -import java.util.*; +import java.util.Collections; +import java.util.EnumSet; +import java.util.List; +import java.util.Objects; +import java.util.Optional; +import java.util.UUID; @Getter @Setter public class Entity implements GeyserEntity { - private static final boolean PRINT_ENTITY_SPAWN_DEBUG = Boolean.parseBoolean(System.getProperty("Geyser.PrintEntitySpawnDebug", "false")); protected final GeyserSession session; @@ -68,12 +78,19 @@ public class Entity implements GeyserEntity { protected int entityId; protected final long geyserId; protected UUID uuid; + /** + * Do not call this setter directly! + * This will bypass the scoreboard and setting the metadata + */ + @Setter(AccessLevel.NONE) + protected String nametag = ""; protected Vector3f position; protected Vector3f motion; /** * x = Yaw, y = Pitch, z = HeadYaw + * Java: Y = Yaw, X = Pitch */ protected float yaw; protected float pitch; @@ -97,7 +114,7 @@ public class Entity implements GeyserEntity { @Setter(AccessLevel.NONE) private float boundingBoxWidth; @Setter(AccessLevel.NONE) - protected String nametag = ""; + private String displayName; @Setter(AccessLevel.NONE) protected boolean silent = false; /* Metadata end */ @@ -126,11 +143,12 @@ public class Entity implements GeyserEntity { public Entity(GeyserSession session, int entityId, long geyserId, UUID uuid, EntityDefinition definition, Vector3f position, Vector3f motion, float yaw, float pitch, float headYaw) { this.session = session; + this.definition = definition; + this.displayName = standardDisplayName(); this.entityId = entityId; this.geyserId = geyserId; this.uuid = uuid; - this.definition = definition; this.motion = motion; this.yaw = yaw; this.pitch = pitch; @@ -341,7 +359,7 @@ public class Entity implements GeyserEntity { * Sends the Bedrock metadata to the client */ public void updateBedrockMetadata() { - if (!valid) { + if (!isValid()) { return; } @@ -410,17 +428,84 @@ public class Entity implements GeyserEntity { return 300; } + public String teamIdentifier() { + // experience orbs are the only known entities that do not send an uuid (even though they do have one), + // but to be safe in the future it's done in the entity class itself instead of the entity specific one. + // All entities without an uuid cannot show up in the scoreboard! + return uuid != null ? uuid.toString() : null; + } + public void setDisplayName(EntityMetadata, ?> entityMetadata) { + // displayName is shown when always display name is enabled. Either with or without team. + // That's why there are both a displayName and a nametag variable. + // Displayname is ignored for players, and is always their username. Optional name = entityMetadata.getValue(); if (name.isPresent()) { - nametag = MessageTranslator.convertMessage(name.get(), session.locale()); - dirtyMetadata.put(EntityDataTypes.NAME, nametag); - } else if (!nametag.isEmpty()) { - // Clear nametag - dirtyMetadata.put(EntityDataTypes.NAME, ""); + String displayName = MessageTranslator.convertMessage(name.get(), session.locale()); + this.displayName = displayName; + setNametag(displayName, true); + return; } + + // if no displayName is set, use entity name (ENDER_DRAGON -> Ender Dragon) + // maybe we can/should use a translatable here instead? + this.displayName = standardDisplayName(); + setNametag(null, true); } + protected String standardDisplayName() { + return EntityUtils.translatedEntityName(definition.entityType(), session); + } + + protected void setNametag(@Nullable String nametag, boolean fromDisplayName) { + // ensure that the team format is used when nametag changes + if (nametag != null && fromDisplayName) { + var team = session.getWorldCache().getScoreboard().getTeamFor(teamIdentifier()); + if (team != null) { + updateNametag(team); + return; + } + } + + if (nametag == null) { + nametag = ""; + } + boolean changed = !Objects.equals(this.nametag, nametag); + this.nametag = nametag; + // we only update metadata if the value has changed + if (!changed) { + return; + } + + dirtyMetadata.put(EntityDataTypes.NAME, nametag); + // if nametag (player with team) is hidden for player, so should the score (belowname) + scoreVisibility(!nametag.isEmpty()); + } + + public void updateNametag(@Nullable Team team) { + // allow LivingEntity+ to have a different visibility check + updateNametag(team, true); + } + + protected void updateNametag(@Nullable Team team, boolean visible) { + if (team != null) { + String newNametag; + // (team) visibility is LivingEntity+, team displayName is Entity+ + if (visible) { + newNametag = team.displayName(getDisplayName()); + } else { + // The name is not visible to the session player; clear name + newNametag = ""; + } + setNametag(newNametag, false); + return; + } + // The name has reset, if it was previously something else + setNametag(null, false); + } + + protected void scoreVisibility(boolean show) {} + public void setDisplayNameVisible(BooleanEntityMetadata entityMetadata) { dirtyMetadata.put(EntityDataTypes.NAMETAG_ALWAYS_SHOW, (byte) (entityMetadata.getPrimitiveValue() ? 1 : 0)); } @@ -616,9 +701,4 @@ public class Entity implements GeyserEntity { packet.setData(data); session.sendUpstreamPacket(packet); } - - @SuppressWarnings("unchecked") - public @Nullable I as(Class entityClass) { - return entityClass.isInstance(this) ? (I) this : null; - } } diff --git a/core/src/main/java/org/geysermc/geyser/entity/type/LivingEntity.java b/core/src/main/java/org/geysermc/geyser/entity/type/LivingEntity.java index 266189e63..33064edfe 100644 --- a/core/src/main/java/org/geysermc/geyser/entity/type/LivingEntity.java +++ b/core/src/main/java/org/geysermc/geyser/entity/type/LivingEntity.java @@ -45,6 +45,7 @@ import org.geysermc.geyser.entity.vehicle.ClientVehicle; import org.geysermc.geyser.inventory.GeyserItemStack; import org.geysermc.geyser.item.Items; import org.geysermc.geyser.registry.type.ItemMapping; +import org.geysermc.geyser.scoreboard.Team; import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.translator.item.ItemTranslator; import org.geysermc.geyser.util.AttributeUtils; @@ -65,12 +66,15 @@ import org.geysermc.mcprotocollib.protocol.data.game.level.particle.EntityEffect import org.geysermc.mcprotocollib.protocol.data.game.level.particle.Particle; import org.geysermc.mcprotocollib.protocol.data.game.level.particle.ParticleType; -import java.util.*; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Optional; +import java.util.UUID; @Getter @Setter public class LivingEntity extends Entity { - protected ItemData helmet = ItemData.AIR; protected ItemData chestplate = ItemData.AIR; protected ItemData leggings = ItemData.AIR; @@ -150,6 +154,16 @@ public class LivingEntity extends Entity { dirtyMetadata.put(EntityDataTypes.STRUCTURAL_INTEGRITY, 1); } + @Override + public void updateNametag(@Nullable Team team) { + // if name not visible, don't mark it as visible + updateNametag(team, team == null || team.isVisibleFor(session.getPlayerEntity().getUsername())); + } + + public void hideNametag() { + setNametag("", false); + } + public void setLivingEntityFlags(ByteEntityMetadata entityMetadata) { byte xd = entityMetadata.getPrimitiveValue(); @@ -432,35 +446,35 @@ public class LivingEntity extends Entity { protected void updateAttribute(Attribute javaAttribute, List newAttributes) { if (javaAttribute.getType() instanceof AttributeType.Builtin type) { switch (type) { - case GENERIC_MAX_HEALTH -> { + case MAX_HEALTH -> { // Since 1.18.0, setting the max health to 0 or below causes the entity to die on Bedrock but not on Java // See https://github.com/GeyserMC/Geyser/issues/2971 this.maxHealth = Math.max((float) AttributeUtils.calculateValue(javaAttribute), 1f); newAttributes.add(createHealthAttribute()); } - case GENERIC_MOVEMENT_SPEED -> { + case MOVEMENT_SPEED -> { AttributeData attributeData = calculateAttribute(javaAttribute, GeyserAttributeType.MOVEMENT_SPEED); newAttributes.add(attributeData); if (this instanceof ClientVehicle clientVehicle) { clientVehicle.getVehicleComponent().setMoveSpeed(attributeData.getValue()); } } - case GENERIC_STEP_HEIGHT -> { + case STEP_HEIGHT -> { if (this instanceof ClientVehicle clientVehicle) { clientVehicle.getVehicleComponent().setStepHeight((float) AttributeUtils.calculateValue(javaAttribute)); } } - case GENERIC_GRAVITY -> { + case GRAVITY -> { if (this instanceof ClientVehicle clientVehicle) { clientVehicle.getVehicleComponent().setGravity(AttributeUtils.calculateValue(javaAttribute)); } } - case GENERIC_ATTACK_DAMAGE -> newAttributes.add(calculateAttribute(javaAttribute, GeyserAttributeType.ATTACK_DAMAGE)); - case GENERIC_FLYING_SPEED -> newAttributes.add(calculateAttribute(javaAttribute, GeyserAttributeType.FLYING_SPEED)); - case GENERIC_FOLLOW_RANGE -> newAttributes.add(calculateAttribute(javaAttribute, GeyserAttributeType.FOLLOW_RANGE)); - case GENERIC_KNOCKBACK_RESISTANCE -> newAttributes.add(calculateAttribute(javaAttribute, GeyserAttributeType.KNOCKBACK_RESISTANCE)); - case GENERIC_JUMP_STRENGTH -> newAttributes.add(calculateAttribute(javaAttribute, GeyserAttributeType.HORSE_JUMP_STRENGTH)); - case GENERIC_SCALE -> { + case ATTACK_DAMAGE -> newAttributes.add(calculateAttribute(javaAttribute, GeyserAttributeType.ATTACK_DAMAGE)); + case FLYING_SPEED -> newAttributes.add(calculateAttribute(javaAttribute, GeyserAttributeType.FLYING_SPEED)); + case FOLLOW_RANGE -> newAttributes.add(calculateAttribute(javaAttribute, GeyserAttributeType.FOLLOW_RANGE)); + case KNOCKBACK_RESISTANCE -> newAttributes.add(calculateAttribute(javaAttribute, GeyserAttributeType.KNOCKBACK_RESISTANCE)); + case JUMP_STRENGTH -> newAttributes.add(calculateAttribute(javaAttribute, GeyserAttributeType.HORSE_JUMP_STRENGTH)); + case SCALE -> { // Attribute on Java, entity data on Bedrock setAttributeScale((float) AttributeUtils.calculateValue(javaAttribute)); updateBedrockMetadata(); diff --git a/core/src/main/java/org/geysermc/geyser/entity/type/living/AgeableWaterEntity.java b/core/src/main/java/org/geysermc/geyser/entity/type/living/AgeableWaterEntity.java new file mode 100644 index 000000000..843215674 --- /dev/null +++ b/core/src/main/java/org/geysermc/geyser/entity/type/living/AgeableWaterEntity.java @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2024 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; + +import org.cloudburstmc.math.vector.Vector3f; +import org.geysermc.geyser.entity.EntityDefinition; +import org.geysermc.geyser.session.GeyserSession; + +import java.util.UUID; + +public abstract class AgeableWaterEntity extends AgeableEntity { + public AgeableWaterEntity(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 + public boolean canBeLeashed() { + return false; + } +} diff --git a/core/src/main/java/org/geysermc/geyser/entity/type/living/ArmorStandEntity.java b/core/src/main/java/org/geysermc/geyser/entity/type/living/ArmorStandEntity.java index d057f09c7..e1c82345f 100644 --- a/core/src/main/java/org/geysermc/geyser/entity/type/living/ArmorStandEntity.java +++ b/core/src/main/java/org/geysermc/geyser/entity/type/living/ArmorStandEntity.java @@ -27,6 +27,7 @@ package org.geysermc.geyser.entity.type.living; import lombok.Getter; import net.kyori.adventure.text.Component; +import org.checkerframework.checker.nullness.qual.Nullable; import org.cloudburstmc.math.vector.Vector3f; import org.cloudburstmc.protocol.bedrock.data.entity.EntityDataType; import org.cloudburstmc.protocol.bedrock.data.entity.EntityDataTypes; @@ -36,6 +37,7 @@ import org.geysermc.geyser.entity.EntityDefinition; import org.geysermc.geyser.entity.EntityDefinitions; import org.geysermc.geyser.entity.type.LivingEntity; import org.geysermc.geyser.item.Items; +import org.geysermc.geyser.scoreboard.Team; import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.util.InteractionResult; import org.geysermc.geyser.util.MathUtils; @@ -123,6 +125,12 @@ public class ArmorStandEntity extends LivingEntity { this.position = position; } + @Override + public void updateNametag(@Nullable Team team) { + // unlike all other LivingEntities, armor stands are not affected by team nametag visibility + super.updateNametag(team, true); + } + @Override public void setDisplayName(EntityMetadata, ?> entityMetadata) { super.setDisplayName(entityMetadata); diff --git a/core/src/main/java/org/geysermc/geyser/entity/type/living/DolphinEntity.java b/core/src/main/java/org/geysermc/geyser/entity/type/living/DolphinEntity.java index a0ea79d67..8c404be97 100644 --- a/core/src/main/java/org/geysermc/geyser/entity/type/living/DolphinEntity.java +++ b/core/src/main/java/org/geysermc/geyser/entity/type/living/DolphinEntity.java @@ -37,7 +37,7 @@ import org.geysermc.mcprotocollib.protocol.data.game.entity.player.Hand; import java.util.UUID; -public class DolphinEntity extends WaterEntity { +public class DolphinEntity extends AgeableWaterEntity { public DolphinEntity(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); } diff --git a/core/src/main/java/org/geysermc/geyser/entity/type/living/SquidEntity.java b/core/src/main/java/org/geysermc/geyser/entity/type/living/SquidEntity.java index 6285bd9a4..ac3456829 100644 --- a/core/src/main/java/org/geysermc/geyser/entity/type/living/SquidEntity.java +++ b/core/src/main/java/org/geysermc/geyser/entity/type/living/SquidEntity.java @@ -36,7 +36,7 @@ import org.geysermc.geyser.session.GeyserSession; import java.util.UUID; import java.util.concurrent.CompletableFuture; -public class SquidEntity extends WaterEntity implements Tickable { +public class SquidEntity extends AgeableWaterEntity implements Tickable { private float targetPitch; private float targetYaw; diff --git a/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/AnimalEntity.java b/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/AnimalEntity.java index 2e627b461..57cbdc783 100644 --- a/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/AnimalEntity.java +++ b/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/AnimalEntity.java @@ -33,8 +33,9 @@ import org.cloudburstmc.protocol.bedrock.data.entity.EntityFlag; import org.geysermc.geyser.entity.EntityDefinition; import org.geysermc.geyser.entity.type.living.AgeableEntity; import org.geysermc.geyser.inventory.GeyserItemStack; +import org.geysermc.geyser.item.type.Item; import org.geysermc.geyser.session.GeyserSession; -import org.geysermc.geyser.session.cache.tags.ItemTag; +import org.geysermc.geyser.session.cache.tags.Tag; import org.geysermc.geyser.util.InteractionResult; import org.geysermc.geyser.util.InteractiveTag; import org.geysermc.mcprotocollib.protocol.data.game.entity.player.Hand; @@ -48,7 +49,7 @@ public abstract class AnimalEntity extends AgeableEntity { } protected final boolean canEat(GeyserItemStack itemStack) { - ItemTag tag = getFoodTag(); + Tag tag = getFoodTag(); if (tag == null) { return false; } @@ -58,7 +59,7 @@ public abstract class AnimalEntity extends AgeableEntity { /** * @return the tag associated with this animal for eating food. Null for nothing or different behavior. */ - protected abstract @Nullable ItemTag getFoodTag(); + protected abstract @Nullable Tag getFoodTag(); @NonNull @Override diff --git a/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/ArmadilloEntity.java b/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/ArmadilloEntity.java index 968520bb6..2b443f5e4 100644 --- a/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/ArmadilloEntity.java +++ b/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/ArmadilloEntity.java @@ -28,8 +28,10 @@ package org.geysermc.geyser.entity.type.living.animal; import org.checkerframework.checker.nullness.qual.Nullable; import org.cloudburstmc.math.vector.Vector3f; import org.geysermc.geyser.entity.EntityDefinition; +import org.geysermc.geyser.item.type.Item; import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.session.cache.tags.ItemTag; +import org.geysermc.geyser.session.cache.tags.Tag; import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.ArmadilloState; import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.type.ObjectEntityMetadata; @@ -75,7 +77,7 @@ public class ArmadilloEntity extends AnimalEntity { @Override @Nullable - protected ItemTag getFoodTag() { + protected Tag getFoodTag() { return ItemTag.ARMADILLO_FOOD; } } diff --git a/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/AxolotlEntity.java b/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/AxolotlEntity.java index a0ab56ead..0a87f59bc 100644 --- a/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/AxolotlEntity.java +++ b/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/AxolotlEntity.java @@ -32,8 +32,10 @@ import org.cloudburstmc.protocol.bedrock.data.entity.EntityDataTypes; import org.cloudburstmc.protocol.bedrock.data.entity.EntityFlag; import org.geysermc.geyser.entity.EntityDefinition; import org.geysermc.geyser.inventory.GeyserItemStack; +import org.geysermc.geyser.item.type.Item; import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.session.cache.tags.ItemTag; +import org.geysermc.geyser.session.cache.tags.Tag; import org.geysermc.geyser.util.EntityUtils; import org.geysermc.geyser.util.InteractionResult; import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.type.BooleanEntityMetadata; @@ -62,7 +64,7 @@ public class AxolotlEntity extends AnimalEntity { @Override @Nullable - protected ItemTag getFoodTag() { + protected Tag getFoodTag() { return ItemTag.AXOLOTL_FOOD; } diff --git a/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/BeeEntity.java b/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/BeeEntity.java index 4fcf0e178..5f8956b6a 100644 --- a/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/BeeEntity.java +++ b/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/BeeEntity.java @@ -32,8 +32,10 @@ import org.cloudburstmc.protocol.bedrock.data.entity.EntityEventType; import org.cloudburstmc.protocol.bedrock.data.entity.EntityFlag; import org.cloudburstmc.protocol.bedrock.packet.EntityEventPacket; import org.geysermc.geyser.entity.EntityDefinition; +import org.geysermc.geyser.item.type.Item; import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.session.cache.tags.ItemTag; +import org.geysermc.geyser.session.cache.tags.Tag; import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.type.ByteEntityMetadata; import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.type.IntEntityMetadata; @@ -69,7 +71,7 @@ public class BeeEntity extends AnimalEntity { @Override @Nullable - protected ItemTag getFoodTag() { + protected Tag getFoodTag() { return ItemTag.BEE_FOOD; } } diff --git a/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/ChickenEntity.java b/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/ChickenEntity.java index 075a49923..0c8e437c8 100644 --- a/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/ChickenEntity.java +++ b/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/ChickenEntity.java @@ -28,8 +28,10 @@ package org.geysermc.geyser.entity.type.living.animal; import org.checkerframework.checker.nullness.qual.Nullable; import org.cloudburstmc.math.vector.Vector3f; import org.geysermc.geyser.entity.EntityDefinition; +import org.geysermc.geyser.item.type.Item; import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.session.cache.tags.ItemTag; +import org.geysermc.geyser.session.cache.tags.Tag; import java.util.UUID; @@ -41,7 +43,7 @@ public class ChickenEntity extends AnimalEntity { @Override @Nullable - protected ItemTag getFoodTag() { + protected Tag getFoodTag() { return ItemTag.CHICKEN_FOOD; } } diff --git a/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/CowEntity.java b/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/CowEntity.java index 64e7de193..66210068b 100644 --- a/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/CowEntity.java +++ b/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/CowEntity.java @@ -33,8 +33,10 @@ import org.cloudburstmc.protocol.bedrock.data.entity.EntityFlag; import org.geysermc.geyser.entity.EntityDefinition; import org.geysermc.geyser.inventory.GeyserItemStack; import org.geysermc.geyser.item.Items; +import org.geysermc.geyser.item.type.Item; import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.session.cache.tags.ItemTag; +import org.geysermc.geyser.session.cache.tags.Tag; import org.geysermc.geyser.util.InteractionResult; import org.geysermc.geyser.util.InteractiveTag; import org.geysermc.mcprotocollib.protocol.data.game.entity.player.Hand; @@ -69,7 +71,7 @@ public class CowEntity extends AnimalEntity { @Override @Nullable - protected ItemTag getFoodTag() { + protected Tag getFoodTag() { return ItemTag.COW_FOOD; } } diff --git a/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/FoxEntity.java b/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/FoxEntity.java index e20031baa..b140b7956 100644 --- a/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/FoxEntity.java +++ b/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/FoxEntity.java @@ -30,8 +30,10 @@ import org.cloudburstmc.math.vector.Vector3f; import org.cloudburstmc.protocol.bedrock.data.entity.EntityDataTypes; import org.cloudburstmc.protocol.bedrock.data.entity.EntityFlag; import org.geysermc.geyser.entity.EntityDefinition; +import org.geysermc.geyser.item.type.Item; import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.session.cache.tags.ItemTag; +import org.geysermc.geyser.session.cache.tags.Tag; import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.type.ByteEntityMetadata; import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.type.IntEntityMetadata; @@ -57,7 +59,7 @@ public class FoxEntity extends AnimalEntity { @Override @Nullable - protected ItemTag getFoodTag() { + protected Tag getFoodTag() { return ItemTag.FOX_FOOD; } } diff --git a/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/FrogEntity.java b/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/FrogEntity.java index 120bfcdd4..a0b909b75 100644 --- a/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/FrogEntity.java +++ b/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/FrogEntity.java @@ -31,8 +31,10 @@ import org.cloudburstmc.protocol.bedrock.data.entity.EntityDataTypes; import org.cloudburstmc.protocol.bedrock.data.entity.EntityFlag; import org.geysermc.geyser.entity.EntityDefinition; import org.geysermc.geyser.entity.type.Entity; +import org.geysermc.geyser.item.type.Item; import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.session.cache.tags.ItemTag; +import org.geysermc.geyser.session.cache.tags.Tag; import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.Pose; import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.type.IntEntityMetadata; import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.type.ObjectEntityMetadata; @@ -77,7 +79,7 @@ public class FrogEntity extends AnimalEntity { @Override @Nullable - protected ItemTag getFoodTag() { + protected Tag getFoodTag() { return ItemTag.FROG_FOOD; } } diff --git a/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/GoatEntity.java b/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/GoatEntity.java index 4e919b81c..b5e4ad117 100644 --- a/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/GoatEntity.java +++ b/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/GoatEntity.java @@ -34,8 +34,10 @@ import org.cloudburstmc.protocol.bedrock.data.entity.EntityFlag; import org.geysermc.geyser.entity.EntityDefinition; import org.geysermc.geyser.inventory.GeyserItemStack; import org.geysermc.geyser.item.Items; +import org.geysermc.geyser.item.type.Item; import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.session.cache.tags.ItemTag; +import org.geysermc.geyser.session.cache.tags.Tag; import org.geysermc.geyser.util.InteractionResult; import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.Pose; import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.type.BooleanEntityMetadata; @@ -99,7 +101,7 @@ public class GoatEntity extends AnimalEntity { @Override @Nullable - protected ItemTag getFoodTag() { + protected Tag getFoodTag() { return ItemTag.GOAT_FOOD; } } diff --git a/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/HoglinEntity.java b/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/HoglinEntity.java index cc23fc607..b506f1425 100644 --- a/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/HoglinEntity.java +++ b/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/HoglinEntity.java @@ -30,8 +30,10 @@ import org.cloudburstmc.math.vector.Vector3f; import org.cloudburstmc.protocol.bedrock.data.entity.EntityDataTypes; import org.cloudburstmc.protocol.bedrock.data.entity.EntityFlag; import org.geysermc.geyser.entity.EntityDefinition; +import org.geysermc.geyser.item.type.Item; import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.session.cache.tags.ItemTag; +import org.geysermc.geyser.session.cache.tags.Tag; import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.type.BooleanEntityMetadata; import java.util.UUID; @@ -58,7 +60,7 @@ public class HoglinEntity extends AnimalEntity { @Override @Nullable - protected ItemTag getFoodTag() { + protected Tag getFoodTag() { return ItemTag.HOGLIN_FOOD; } diff --git a/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/OcelotEntity.java b/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/OcelotEntity.java index 9d6d33227..c45092b02 100644 --- a/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/OcelotEntity.java +++ b/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/OcelotEntity.java @@ -31,8 +31,10 @@ import org.cloudburstmc.math.vector.Vector3f; import org.cloudburstmc.protocol.bedrock.data.entity.EntityFlag; import org.geysermc.geyser.entity.EntityDefinition; import org.geysermc.geyser.inventory.GeyserItemStack; +import org.geysermc.geyser.item.type.Item; import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.session.cache.tags.ItemTag; +import org.geysermc.geyser.session.cache.tags.Tag; import org.geysermc.geyser.util.InteractionResult; import org.geysermc.geyser.util.InteractiveTag; import org.geysermc.mcprotocollib.protocol.data.game.entity.player.Hand; @@ -47,7 +49,7 @@ public class OcelotEntity extends AnimalEntity { @Override @Nullable - protected ItemTag getFoodTag() { + protected Tag getFoodTag() { return ItemTag.OCELOT_FOOD; } diff --git a/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/PandaEntity.java b/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/PandaEntity.java index aaa7c2d7e..022e58bc0 100644 --- a/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/PandaEntity.java +++ b/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/PandaEntity.java @@ -34,8 +34,10 @@ import org.cloudburstmc.protocol.bedrock.data.entity.EntityFlag; import org.cloudburstmc.protocol.bedrock.packet.EntityEventPacket; import org.geysermc.geyser.entity.EntityDefinition; import org.geysermc.geyser.inventory.GeyserItemStack; +import org.geysermc.geyser.item.type.Item; import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.session.cache.tags.ItemTag; +import org.geysermc.geyser.session.cache.tags.Tag; import org.geysermc.geyser.util.InteractionResult; import org.geysermc.geyser.util.InteractiveTag; import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.type.ByteEntityMetadata; @@ -90,7 +92,7 @@ public class PandaEntity extends AnimalEntity { @Override @Nullable - protected ItemTag getFoodTag() { + protected Tag getFoodTag() { return ItemTag.PANDA_FOOD; } diff --git a/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/PigEntity.java b/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/PigEntity.java index 2ec23d673..b8ba2c94f 100644 --- a/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/PigEntity.java +++ b/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/PigEntity.java @@ -38,9 +38,11 @@ import org.geysermc.geyser.entity.vehicle.BoostableVehicleComponent; import org.geysermc.geyser.entity.vehicle.ClientVehicle; import org.geysermc.geyser.entity.vehicle.VehicleComponent; import org.geysermc.geyser.inventory.GeyserItemStack; +import org.geysermc.geyser.item.type.Item; import org.geysermc.geyser.item.Items; import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.session.cache.tags.ItemTag; +import org.geysermc.geyser.session.cache.tags.Tag; import org.geysermc.geyser.util.EntityUtils; import org.geysermc.geyser.util.InteractionResult; import org.geysermc.geyser.util.InteractiveTag; @@ -58,7 +60,7 @@ public class PigEntity extends AnimalEntity implements Tickable, ClientVehicle { @Override @Nullable - protected ItemTag getFoodTag() { + protected Tag getFoodTag() { return ItemTag.PIG_FOOD; } diff --git a/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/PolarBearEntity.java b/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/PolarBearEntity.java index 0e83615f7..900878dbd 100644 --- a/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/PolarBearEntity.java +++ b/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/PolarBearEntity.java @@ -28,8 +28,9 @@ package org.geysermc.geyser.entity.type.living.animal; import org.checkerframework.checker.nullness.qual.Nullable; import org.cloudburstmc.math.vector.Vector3f; import org.geysermc.geyser.entity.EntityDefinition; +import org.geysermc.geyser.item.type.Item; import org.geysermc.geyser.session.GeyserSession; -import org.geysermc.geyser.session.cache.tags.ItemTag; +import org.geysermc.geyser.session.cache.tags.Tag; import java.util.UUID; @@ -41,7 +42,7 @@ public class PolarBearEntity extends AnimalEntity { @Override @Nullable - protected ItemTag getFoodTag() { + protected Tag getFoodTag() { return null; } } 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 0a108be73..dca6fe5ff 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,18 +25,23 @@ package org.geysermc.geyser.entity.type.living.animal; +import net.kyori.adventure.key.Key; import org.checkerframework.checker.nullness.qual.Nullable; import org.cloudburstmc.math.vector.Vector3f; import org.cloudburstmc.protocol.bedrock.data.entity.EntityDataTypes; import org.cloudburstmc.protocol.bedrock.data.entity.EntityFlag; import org.geysermc.geyser.entity.EntityDefinition; +import org.geysermc.geyser.item.type.Item; import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.session.cache.tags.ItemTag; +import org.geysermc.geyser.session.cache.tags.Tag; +import org.geysermc.geyser.util.EntityUtils; import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.type.IntEntityMetadata; import java.util.UUID; public class RabbitEntity extends AnimalEntity { + private boolean isKillerBunny; public RabbitEntity(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); @@ -46,7 +51,7 @@ public class RabbitEntity extends AnimalEntity { int variant = entityMetadata.getPrimitiveValue(); // Change the killer bunny to display as white since it only exists on Java Edition - boolean isKillerBunny = variant == 99; + isKillerBunny = variant == 99; if (isKillerBunny) { variant = 1; } @@ -56,6 +61,14 @@ public class RabbitEntity extends AnimalEntity { dirtyMetadata.put(EntityDataTypes.VARIANT, variant); } + @Override + protected String standardDisplayName() { + if (isKillerBunny) { + return EntityUtils.translatedEntityName(Key.key("killer_bunny"), session); + } + return super.standardDisplayName(); + } + @Override protected float getAdultSize() { return 0.55f; @@ -68,7 +81,7 @@ public class RabbitEntity extends AnimalEntity { @Override @Nullable - protected ItemTag getFoodTag() { + protected Tag getFoodTag() { return ItemTag.RABBIT_FOOD; } -} \ No newline at end of file +} diff --git a/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/SheepEntity.java b/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/SheepEntity.java index 155ddf00c..e26b0be61 100644 --- a/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/SheepEntity.java +++ b/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/SheepEntity.java @@ -34,8 +34,10 @@ import org.geysermc.geyser.entity.EntityDefinition; import org.geysermc.geyser.inventory.GeyserItemStack; import org.geysermc.geyser.item.Items; import org.geysermc.geyser.item.type.DyeItem; +import org.geysermc.geyser.item.type.Item; import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.session.cache.tags.ItemTag; +import org.geysermc.geyser.session.cache.tags.Tag; import org.geysermc.geyser.util.InteractionResult; import org.geysermc.geyser.util.InteractiveTag; import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.type.ByteEntityMetadata; @@ -59,7 +61,7 @@ public class SheepEntity extends AnimalEntity { @Override @Nullable - protected ItemTag getFoodTag() { + protected Tag getFoodTag() { return ItemTag.SHEEP_FOOD; } @@ -103,4 +105,4 @@ public class SheepEntity extends AnimalEntity { private boolean canDye(GeyserItemStack item) { return item.asItem() instanceof DyeItem dyeItem && dyeItem.dyeColor() != this.color && !getFlag(EntityFlag.SHEARED); } -} \ No newline at end of file +} diff --git a/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/SnifferEntity.java b/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/SnifferEntity.java index 11fee5bbf..203a48f19 100644 --- a/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/SnifferEntity.java +++ b/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/SnifferEntity.java @@ -35,8 +35,10 @@ import org.cloudburstmc.protocol.bedrock.packet.LevelSoundEventPacket; import org.geysermc.geyser.entity.EntityDefinition; import org.geysermc.geyser.entity.EntityDefinitions; import org.geysermc.geyser.entity.type.Tickable; +import org.geysermc.geyser.item.type.Item; import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.session.cache.tags.ItemTag; +import org.geysermc.geyser.session.cache.tags.Tag; import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.Pose; import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.SnifferState; import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.type.ObjectEntityMetadata; @@ -73,7 +75,7 @@ public class SnifferEntity extends AnimalEntity implements Tickable { @Override @Nullable - protected ItemTag getFoodTag() { + protected Tag getFoodTag() { return ItemTag.SNIFFER_FOOD; } diff --git a/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/StriderEntity.java b/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/StriderEntity.java index e06af2786..62318e255 100644 --- a/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/StriderEntity.java +++ b/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/StriderEntity.java @@ -39,9 +39,11 @@ import org.geysermc.geyser.entity.vehicle.BoostableVehicleComponent; import org.geysermc.geyser.entity.vehicle.ClientVehicle; import org.geysermc.geyser.entity.vehicle.VehicleComponent; import org.geysermc.geyser.inventory.GeyserItemStack; +import org.geysermc.geyser.item.type.Item; import org.geysermc.geyser.item.Items; import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.session.cache.tags.ItemTag; +import org.geysermc.geyser.session.cache.tags.Tag; import org.geysermc.geyser.util.EntityUtils; import org.geysermc.geyser.util.InteractionResult; import org.geysermc.geyser.util.InteractiveTag; @@ -105,7 +107,7 @@ public class StriderEntity extends AnimalEntity implements Tickable, ClientVehic @Override @Nullable - protected ItemTag getFoodTag() { + protected Tag getFoodTag() { return ItemTag.STRIDER_FOOD; } diff --git a/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/TurtleEntity.java b/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/TurtleEntity.java index 16901a844..1f700f12b 100644 --- a/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/TurtleEntity.java +++ b/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/TurtleEntity.java @@ -29,8 +29,10 @@ import org.checkerframework.checker.nullness.qual.Nullable; import org.cloudburstmc.math.vector.Vector3f; import org.cloudburstmc.protocol.bedrock.data.entity.EntityFlag; import org.geysermc.geyser.entity.EntityDefinition; +import org.geysermc.geyser.item.type.Item; import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.session.cache.tags.ItemTag; +import org.geysermc.geyser.session.cache.tags.Tag; import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.type.BooleanEntityMetadata; import java.util.UUID; @@ -51,10 +53,21 @@ public class TurtleEntity extends AnimalEntity { @Override @Nullable - protected ItemTag getFoodTag() { + protected Tag getFoodTag() { return ItemTag.TURTLE_FOOD; } + @Override + protected float getAdultSize() { + return super.getAdultSize() * 0.7f; + } + + @Override + protected float getBabySize() { + // 0.3f is Java scale, plus Bedrock difference + return 0.3f * 0.5f; + } + @Override public boolean canBeLeashed() { return false; diff --git a/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/horse/AbstractHorseEntity.java b/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/horse/AbstractHorseEntity.java index ddc212053..100a29299 100644 --- a/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/horse/AbstractHorseEntity.java +++ b/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/horse/AbstractHorseEntity.java @@ -39,8 +39,10 @@ import org.geysermc.geyser.entity.attribute.GeyserAttributeType; import org.geysermc.geyser.entity.type.living.animal.AnimalEntity; import org.geysermc.geyser.inventory.GeyserItemStack; import org.geysermc.geyser.item.Items; +import org.geysermc.geyser.item.type.Item; import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.session.cache.tags.ItemTag; +import org.geysermc.geyser.session.cache.tags.Tag; import org.geysermc.geyser.util.InteractionResult; import org.geysermc.geyser.util.InteractiveTag; import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.type.ByteEntityMetadata; @@ -119,7 +121,7 @@ public class AbstractHorseEntity extends AnimalEntity { @Override @Nullable - protected ItemTag getFoodTag() { + protected Tag getFoodTag() { return ItemTag.HORSE_FOOD; } 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 index 3c0bf1a70..ca39bd1e6 100644 --- 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 @@ -35,12 +35,14 @@ import org.cloudburstmc.protocol.bedrock.data.entity.EntityFlag; import org.cloudburstmc.protocol.bedrock.data.inventory.ContainerType; import org.cloudburstmc.protocol.bedrock.packet.EntityEventPacket; import org.geysermc.geyser.entity.EntityDefinition; +import org.geysermc.geyser.item.type.Item; import org.geysermc.geyser.entity.attribute.GeyserAttributeType; import org.geysermc.geyser.entity.vehicle.CamelVehicleComponent; import org.geysermc.geyser.entity.vehicle.ClientVehicle; import org.geysermc.geyser.entity.vehicle.VehicleComponent; import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.session.cache.tags.ItemTag; +import org.geysermc.geyser.session.cache.tags.Tag; import org.geysermc.mcprotocollib.protocol.data.game.entity.attribute.Attribute; import org.geysermc.mcprotocollib.protocol.data.game.entity.attribute.AttributeType; import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.Pose; @@ -100,7 +102,7 @@ public class CamelEntity extends AbstractHorseEntity implements ClientVehicle { } @Override - protected @Nullable ItemTag getFoodTag() { + protected @Nullable Tag getFoodTag() { return ItemTag.CAMEL_FOOD; } @@ -141,7 +143,7 @@ public class CamelEntity extends AbstractHorseEntity implements ClientVehicle { @Override protected AttributeData calculateAttribute(Attribute javaAttribute, GeyserAttributeType type) { AttributeData attributeData = super.calculateAttribute(javaAttribute, type); - if (javaAttribute.getType() == AttributeType.Builtin.GENERIC_JUMP_STRENGTH) { + if (javaAttribute.getType() == AttributeType.Builtin.JUMP_STRENGTH) { vehicleComponent.setHorseJumpStrength(attributeData.getValue()); } return attributeData; diff --git a/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/horse/LlamaEntity.java b/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/horse/LlamaEntity.java index 76939ceb9..d27a1fff3 100644 --- a/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/horse/LlamaEntity.java +++ b/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/horse/LlamaEntity.java @@ -30,8 +30,10 @@ import org.checkerframework.checker.nullness.qual.Nullable; import org.cloudburstmc.math.vector.Vector3f; import org.cloudburstmc.protocol.bedrock.data.entity.EntityDataTypes; import org.geysermc.geyser.entity.EntityDefinition; +import org.geysermc.geyser.item.type.Item; import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.session.cache.tags.ItemTag; +import org.geysermc.geyser.session.cache.tags.Tag; import org.geysermc.geyser.util.MathUtils; import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.type.IntEntityMetadata; @@ -56,7 +58,7 @@ public class LlamaEntity extends ChestedHorseEntity { } @Override - protected @Nullable ItemTag getFoodTag() { + protected @Nullable Tag getFoodTag() { return ItemTag.LLAMA_FOOD; } } 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 bf1555e9d..fb53c18ed 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 @@ -32,8 +32,10 @@ import org.cloudburstmc.protocol.bedrock.data.entity.EntityDataTypes; import org.cloudburstmc.protocol.bedrock.data.entity.EntityFlag; import org.geysermc.geyser.entity.EntityDefinition; import org.geysermc.geyser.inventory.GeyserItemStack; +import org.geysermc.geyser.item.type.Item; import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.session.cache.tags.ItemTag; +import org.geysermc.geyser.session.cache.tags.Tag; import org.geysermc.geyser.util.InteractionResult; import org.geysermc.geyser.util.InteractiveTag; import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.type.BooleanEntityMetadata; @@ -109,7 +111,7 @@ public class CatEntity extends TameableEntity { } @Override - protected @Nullable ItemTag getFoodTag() { + protected @Nullable Tag getFoodTag() { return ItemTag.CAT_FOOD; } diff --git a/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/tameable/ParrotEntity.java b/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/tameable/ParrotEntity.java index 69b19b1b9..95e9c901b 100644 --- a/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/tameable/ParrotEntity.java +++ b/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/tameable/ParrotEntity.java @@ -34,6 +34,7 @@ import org.geysermc.geyser.inventory.GeyserItemStack; import org.geysermc.geyser.item.type.Item; import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.session.cache.tags.ItemTag; +import org.geysermc.geyser.session.cache.tags.Tag; import org.geysermc.geyser.util.InteractionResult; import org.geysermc.geyser.util.InteractiveTag; import org.geysermc.mcprotocollib.protocol.data.game.entity.player.Hand; @@ -47,7 +48,7 @@ public class ParrotEntity extends TameableEntity { @Override @Nullable - protected ItemTag getFoodTag() { + protected Tag getFoodTag() { return null; } diff --git a/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/tameable/WolfEntity.java b/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/tameable/WolfEntity.java index e7fde2be8..f0b554ef9 100644 --- a/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/tameable/WolfEntity.java +++ b/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/tameable/WolfEntity.java @@ -36,8 +36,10 @@ import org.geysermc.geyser.inventory.GeyserItemStack; import org.geysermc.geyser.item.Items; import org.geysermc.geyser.item.enchantment.EnchantmentComponent; import org.geysermc.geyser.item.type.DyeItem; +import org.geysermc.geyser.item.type.Item; import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.session.cache.tags.ItemTag; +import org.geysermc.geyser.session.cache.tags.Tag; import org.geysermc.geyser.util.InteractionResult; import org.geysermc.geyser.util.InteractiveTag; import org.geysermc.geyser.util.ItemUtils; @@ -116,7 +118,7 @@ public class WolfEntity extends TameableEntity { @Override @Nullable - protected ItemTag getFoodTag() { + protected Tag getFoodTag() { return ItemTag.WOLF_FOOD; } 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 b326f2e04..7e747e33d 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 @@ -32,7 +32,6 @@ import org.checkerframework.checker.nullness.qual.Nullable; import org.cloudburstmc.math.vector.Vector3f; import org.cloudburstmc.math.vector.Vector3i; import org.cloudburstmc.nbt.NbtMap; -import org.cloudburstmc.nbt.NbtMapBuilder; import org.cloudburstmc.protocol.bedrock.data.Ability; import org.cloudburstmc.protocol.bedrock.data.AbilityLayer; import org.cloudburstmc.protocol.bedrock.data.GameType; @@ -44,7 +43,6 @@ import org.cloudburstmc.protocol.bedrock.data.entity.EntityLinkData; import org.cloudburstmc.protocol.bedrock.data.inventory.ItemData; import org.cloudburstmc.protocol.bedrock.packet.AddPlayerPacket; import org.cloudburstmc.protocol.bedrock.packet.MovePlayerPacket; -import org.cloudburstmc.protocol.bedrock.packet.SetEntityDataPacket; import org.cloudburstmc.protocol.bedrock.packet.SetEntityLinkPacket; import org.cloudburstmc.protocol.bedrock.packet.UpdateAttributesPacket; import org.geysermc.geyser.api.entity.type.player.GeyserPlayerEntity; @@ -53,29 +51,17 @@ import org.geysermc.geyser.entity.attribute.GeyserAttributeType; import org.geysermc.geyser.entity.type.Entity; import org.geysermc.geyser.entity.type.LivingEntity; import org.geysermc.geyser.entity.type.living.animal.tameable.ParrotEntity; -import org.geysermc.geyser.scoreboard.Objective; -import org.geysermc.geyser.scoreboard.Score; -import org.geysermc.geyser.scoreboard.Team; -import org.geysermc.geyser.scoreboard.UpdateType; import org.geysermc.geyser.session.GeyserSession; -import org.geysermc.geyser.text.ChatColor; -import org.geysermc.geyser.translator.text.MessageTranslator; import org.geysermc.geyser.util.ChunkUtils; -import org.geysermc.mcprotocollib.protocol.codec.NbtComponentSerializer; -import org.geysermc.mcprotocollib.protocol.data.game.chat.numbers.BlankFormat; -import org.geysermc.mcprotocollib.protocol.data.game.chat.numbers.FixedFormat; -import org.geysermc.mcprotocollib.protocol.data.game.chat.numbers.NumberFormat; -import org.geysermc.mcprotocollib.protocol.data.game.chat.numbers.StyledFormat; import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.EntityMetadata; import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.Pose; import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.type.BooleanEntityMetadata; import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.type.ByteEntityMetadata; import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.type.FloatEntityMetadata; -import org.geysermc.mcprotocollib.protocol.data.game.scoreboard.ScoreboardPosition; -import org.geysermc.mcprotocollib.protocol.data.game.scoreboard.TeamColor; import java.util.Collections; import java.util.List; +import java.util.Objects; import java.util.Optional; import java.util.UUID; import java.util.concurrent.TimeUnit; @@ -96,6 +82,9 @@ public class PlayerEntity extends LivingEntity implements GeyserPlayerEntity { private String username; + private String cachedScore = ""; + private boolean scoreVisible = true; + /** * The textures property from the GameProfile. */ @@ -132,17 +121,6 @@ public class PlayerEntity extends LivingEntity implements GeyserPlayerEntity { @Override public void spawnEntity() { - // Check to see if the player should have a belowname counterpart added - Objective objective = session.getWorldCache().getScoreboard().getObjectiveSlots().get(ScoreboardPosition.BELOW_NAME); - if (objective != null) { - setBelowNameText(objective); - } - - // Update in case this entity has been despawned, then respawned - this.nametag = this.username; - // The name can't be updated later (the entity metadata for it is ignored), so we need to check for this now - updateDisplayName(session.getWorldCache().getScoreboard().getTeamFor(username)); - AddPlayerPacket addPlayerPacket = new AddPlayerPacket(); addPlayerPacket.setUuid(uuid); addPlayerPacket.setUsername(username); @@ -177,6 +155,7 @@ public class PlayerEntity extends LivingEntity implements GeyserPlayerEntity { // Since we re-use player entities: Clear flags, held item, etc this.resetMetadata(); + this.nametag = username; this.hand = ItemData.AIR; this.offhand = ItemData.AIR; this.boots = ItemData.AIR; @@ -200,11 +179,7 @@ public class PlayerEntity extends LivingEntity implements GeyserPlayerEntity { if (session.getEntityCache().getPlayerEntity(uuid) == null) return; - if (session.getEntityCache().getEntityByGeyserId(geyserId) == null) { - session.getEntityCache().spawnEntity(this); - } else { - spawnEntity(); - } + session.getEntityCache().spawnEntity(this); } @Override @@ -276,10 +251,6 @@ public class PlayerEntity extends LivingEntity implements GeyserPlayerEntity { } } - public void updateRotation(float yaw, float pitch, float headYaw, boolean isOnGround) { - moveRelative(0, 0, 0, yaw, pitch, headYaw, isOnGround); - } - @Override public void setPosition(Vector3f position) { if (this.bedPosition != null) { @@ -386,38 +357,30 @@ public class PlayerEntity extends LivingEntity implements GeyserPlayerEntity { } } + @Override + public String getDisplayName() { + return username; + } + @Override public void setDisplayName(EntityMetadata, ?> entityMetadata) { // Doesn't do anything for players } - //todo this will become common entity logic once UUID support is implemented for them - public void updateDisplayName(@Nullable Team team) { - boolean needsUpdate; - if (team != null) { - String newDisplayName; - if (team.isVisibleFor(session.getPlayerEntity().getUsername())) { - TeamColor color = team.getColor(); - String chatColor = MessageTranslator.toChatColor(color); - // We have to emulate what modern Java text already does for us and add the color to each section - String prefix = team.getCurrentData().getPrefix(); - String suffix = team.getCurrentData().getSuffix(); - newDisplayName = chatColor + prefix + chatColor + this.username + chatColor + suffix; - } else { - // The name is not visible to the session player; clear name - newDisplayName = ""; - } - needsUpdate = !newDisplayName.equals(this.nametag); - this.nametag = newDisplayName; - } else { - // The name has reset, if it was previously something else - needsUpdate = !this.nametag.equals(this.username); - this.nametag = this.username; - } + @Override + public String teamIdentifier() { + return username; + } - if (needsUpdate) { - dirtyMetadata.put(EntityDataTypes.NAME, this.nametag); + @Override + protected void setNametag(@Nullable String nametag, boolean fromDisplayName) { + // when fromDisplayName, LivingEntity will call scoreboard code. After that + // setNametag is called again with fromDisplayName on false + if (nametag == null && !fromDisplayName) { + // nametag = null means reset, so reset it back to username + nametag = username; } + super.setNametag(nametag, fromDisplayName); } @Override @@ -425,6 +388,33 @@ public class PlayerEntity extends LivingEntity implements GeyserPlayerEntity { // Doesn't do anything for players } + public void setBelowNameText(String text) { + if (text == null) { + text = ""; + } + + boolean changed = !Objects.equals(cachedScore, text); + cachedScore = text; + if (isScoreVisible() && changed) { + dirtyMetadata.put(EntityDataTypes.SCORE, text); + } + } + + @Override + protected void scoreVisibility(boolean show) { + boolean visibilityChanged = scoreVisible != show; + scoreVisible = show; + if (!visibilityChanged) { + return; + } + // if the player has no cachedScore, we never have to change the score. + // hide = set to "" (does nothing), show = change from "" (does nothing) + if (cachedScore.isEmpty()) { + return; + } + dirtyMetadata.put(EntityDataTypes.SCORE, show ? cachedScore : ""); + } + @Override protected void setDimensions(Pose pose) { float height; @@ -451,64 +441,6 @@ public class PlayerEntity extends LivingEntity implements GeyserPlayerEntity { setBoundingBoxHeight(height); } - public void setBelowNameText(Objective objective) { - if (objective != null && objective.getUpdateType() != UpdateType.REMOVE) { - Score score = objective.getScores().get(username); - String numberString; - NumberFormat numberFormat; - int amount; - if (score != null) { - amount = score.getScore(); - numberFormat = score.getNumberFormat(); - if (numberFormat == null) { - numberFormat = objective.getNumberFormat(); - } - } else { - amount = 0; - numberFormat = objective.getNumberFormat(); - } - - if (numberFormat instanceof BlankFormat) { - numberString = ""; - } else if (numberFormat instanceof FixedFormat fixedFormat) { - numberString = MessageTranslator.convertMessage(fixedFormat.getValue()); - } else if (numberFormat instanceof StyledFormat styledFormat) { - NbtMapBuilder styledAmount = styledFormat.getStyle().toBuilder(); - styledAmount.putString("text", String.valueOf(amount)); - - numberString = MessageTranslator.convertJsonMessage( - NbtComponentSerializer.tagComponentToJson(styledAmount.build()).toString(), session.locale()); - } else { - numberString = String.valueOf(amount); - } - - String displayString = numberString + " " + ChatColor.RESET + objective.getDisplayName(); - - if (valid) { - // Already spawned - we still need to run the rest of this code because the spawn packet will be - // providing the information - SetEntityDataPacket packet = new SetEntityDataPacket(); - packet.setRuntimeEntityId(geyserId); - packet.getMetadata().put(EntityDataTypes.SCORE, displayString); - session.sendUpstreamPacket(packet); - } else { - // Not spawned yet, store score value in dirtyMetadata to be picked up by #spawnEntity - dirtyMetadata.put(EntityDataTypes.SCORE, displayString); - } - } else { - if (valid) { - SetEntityDataPacket packet = new SetEntityDataPacket(); - packet.setRuntimeEntityId(geyserId); - packet.getMetadata().put(EntityDataTypes.SCORE, ""); - session.sendUpstreamPacket(packet); - } else { - // Not spawned yet, store score value in dirtyMetadata to be picked up by #spawnEntity - dirtyMetadata.put(EntityDataTypes.SCORE, ""); - } - } - - } - /** * @return the UUID that should be used when dealing with Bedrock's tab list. */ 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 f427b001a..9d5bc011c 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 @@ -38,8 +38,8 @@ import org.cloudburstmc.protocol.bedrock.packet.MovePlayerPacket; import org.cloudburstmc.protocol.bedrock.packet.UpdateAttributesPacket; import org.geysermc.geyser.entity.attribute.GeyserAttributeType; import org.geysermc.geyser.item.Items; -import org.geysermc.geyser.network.GameProtocol; import org.geysermc.geyser.level.BedrockDimension; +import org.geysermc.geyser.network.GameProtocol; import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.util.AttributeUtils; import org.geysermc.geyser.util.DimensionUtils; @@ -143,9 +143,32 @@ public class SessionPlayerEntity extends PlayerEntity { this.position = position.add(0, definition.offset(), 0); } + /** + * Special method used only when updating the session player's rotation. + * For some reason, Mode#NORMAL ignored rotation. Yay. + * @param yaw the new yaw + * @param pitch the new pitch + * @param headYaw the head yaw + */ + public void updateOwnRotation(float yaw, float pitch, float headYaw) { + setYaw(yaw); + setPitch(pitch); + setHeadYaw(headYaw); + + MovePlayerPacket movePlayerPacket = new MovePlayerPacket(); + movePlayerPacket.setRuntimeEntityId(geyserId); + movePlayerPacket.setPosition(position); + movePlayerPacket.setRotation(getBedrockRotation()); + movePlayerPacket.setOnGround(isOnGround()); + movePlayerPacket.setMode(MovePlayerPacket.Mode.TELEPORT); + movePlayerPacket.setTeleportationCause(MovePlayerPacket.TeleportationCause.BEHAVIOR); + + session.sendUpstreamPacket(movePlayerPacket); + } + /** * Set the player's position without applying an offset or moving the bounding box - * This is used in BedrockMovePlayerTranslator which receives the player's position + * This is used in BedrockMovePlayer which receives the player's position * with the offset pre-applied * * @param position the new position of the Bedrock player @@ -247,9 +270,9 @@ public class SessionPlayerEntity extends PlayerEntity { @Override protected void updateAttribute(Attribute javaAttribute, List newAttributes) { - if (javaAttribute.getType() == AttributeType.Builtin.GENERIC_ATTACK_SPEED) { + if (javaAttribute.getType() == AttributeType.Builtin.ATTACK_SPEED) { session.setAttackSpeed(AttributeUtils.calculateValue(javaAttribute)); - } else if (javaAttribute.getType() == AttributeType.Builtin.PLAYER_BLOCK_INTERACTION_RANGE) { + } else if (javaAttribute.getType() == AttributeType.Builtin.BLOCK_INTERACTION_RANGE) { this.blockInteractionRange = AttributeUtils.calculateValue(javaAttribute); } else { super.updateAttribute(javaAttribute, newAttributes); diff --git a/core/src/main/java/org/geysermc/geyser/entity/vehicle/VehicleComponent.java b/core/src/main/java/org/geysermc/geyser/entity/vehicle/VehicleComponent.java index db703a3cb..91f54162b 100644 --- a/core/src/main/java/org/geysermc/geyser/entity/vehicle/VehicleComponent.java +++ b/core/src/main/java/org/geysermc/geyser/entity/vehicle/VehicleComponent.java @@ -76,8 +76,8 @@ public class VehicleComponent { public VehicleComponent(T vehicle, float stepHeight) { this.vehicle = vehicle; this.stepHeight = stepHeight; - this.moveSpeed = (float) AttributeType.Builtin.GENERIC_MOVEMENT_SPEED.getDef(); - this.gravity = AttributeType.Builtin.GENERIC_GRAVITY.getDef(); + this.moveSpeed = (float) AttributeType.Builtin.MOVEMENT_SPEED.getDef(); + this.gravity = AttributeType.Builtin.GRAVITY.getDef(); double width = vehicle.getBoundingBoxWidth(); double height = vehicle.getBoundingBoxHeight(); @@ -758,7 +758,6 @@ public class VehicleComponent { ServerboundMoveVehiclePacket moveVehiclePacket = new ServerboundMoveVehiclePacket(javaPos.getX(), javaPos.getY(), javaPos.getZ(), rotation.getX(), rotation.getY()); vehicle.getSession().sendDownstreamPacket(moveVehiclePacket); - vehicle.getSession().setLastVehicleMoveTimestamp(System.currentTimeMillis()); } protected double getGravity() { diff --git a/core/src/main/java/org/geysermc/geyser/extension/command/GeyserExtensionCommand.java b/core/src/main/java/org/geysermc/geyser/extension/command/GeyserExtensionCommand.java index 0b22a8b8e..0dc49f5c1 100644 --- a/core/src/main/java/org/geysermc/geyser/extension/command/GeyserExtensionCommand.java +++ b/core/src/main/java/org/geysermc/geyser/extension/command/GeyserExtensionCommand.java @@ -38,6 +38,7 @@ import org.geysermc.geyser.command.GeyserCommandSource; import org.geysermc.geyser.session.GeyserSession; import org.incendo.cloud.CommandManager; import org.incendo.cloud.context.CommandContext; +import org.incendo.cloud.description.CommandDescription; import java.util.ArrayList; import java.util.List; @@ -193,11 +194,17 @@ public abstract class GeyserExtensionCommand extends GeyserCommand { .handler(this::execute)); } + @Override + protected org.incendo.cloud.Command.Builder.Applicable meta() { + // We don't want to localize the extension command description + return builder -> builder.commandDescription(CommandDescription.commandDescription(description)); + } + @SuppressWarnings("unchecked") @Override public void execute(CommandContext context) { GeyserCommandSource source = context.sender(); - String[] args = context.getOrDefault("args", "").split(" "); + String[] args = context.getOrDefault("args", " ").split(" "); if (sourceType.isInstance(source)) { executor.execute((T) source, this, args); diff --git a/core/src/main/java/org/geysermc/geyser/impl/camera/CameraDefinitions.java b/core/src/main/java/org/geysermc/geyser/impl/camera/CameraDefinitions.java index 1cf6a794e..ee20c14a2 100644 --- a/core/src/main/java/org/geysermc/geyser/impl/camera/CameraDefinitions.java +++ b/core/src/main/java/org/geysermc/geyser/impl/camera/CameraDefinitions.java @@ -43,13 +43,13 @@ public class CameraDefinitions { static { CAMERA_PRESETS = List.of( - new CameraPreset(CameraPerspective.FIRST_PERSON.id(), "", null, null, null, null, null, null, OptionalBoolean.empty(), null, OptionalBoolean.empty(), null), - new CameraPreset(CameraPerspective.FREE.id(), "", null, null, null, null, null, null, OptionalBoolean.empty(), null, OptionalBoolean.empty(), null), - new CameraPreset(CameraPerspective.THIRD_PERSON.id(), "", null, null, null, null, null, null, OptionalBoolean.empty(), null, OptionalBoolean.empty(), null), - new CameraPreset(CameraPerspective.THIRD_PERSON_FRONT.id(), "", null, null, null, null, null, null, OptionalBoolean.empty(), null, OptionalBoolean.empty(), null), - new CameraPreset("geyser:free_audio", "minecraft:free", null, null, null, null, null, CameraAudioListener.PLAYER, OptionalBoolean.empty(), null, OptionalBoolean.of(false), null), - new CameraPreset("geyser:free_effects", "minecraft:free", null, null, null, null, null, CameraAudioListener.CAMERA, OptionalBoolean.empty(), null, OptionalBoolean.of(true), null), - new CameraPreset("geyser:free_audio_effects", "minecraft:free", null, null, null, null, null, CameraAudioListener.PLAYER, OptionalBoolean.empty(), null, OptionalBoolean.of(true), null)); + new CameraPreset(CameraPerspective.FIRST_PERSON.id(), "", null, null, null, null, null, null, OptionalBoolean.empty(), null, OptionalBoolean.empty(), null, null, null, OptionalBoolean.empty(), OptionalBoolean.empty()), + new CameraPreset(CameraPerspective.FREE.id(), "", null, null, null, null, null, null, OptionalBoolean.empty(), null, OptionalBoolean.empty(), null, null, null, OptionalBoolean.empty(), OptionalBoolean.empty()), + new CameraPreset(CameraPerspective.THIRD_PERSON.id(), "", null, null, null, null, null, null, OptionalBoolean.empty(), null, OptionalBoolean.empty(), null, null, null, OptionalBoolean.empty(), OptionalBoolean.empty()), + new CameraPreset(CameraPerspective.THIRD_PERSON_FRONT.id(), "", null, null, null, null, null, null, OptionalBoolean.empty(), null, OptionalBoolean.empty(), null, null, null, OptionalBoolean.empty(), OptionalBoolean.empty()), + new CameraPreset("geyser:free_audio", "minecraft:free", null, null, null, null, null, CameraAudioListener.PLAYER, OptionalBoolean.empty(), null, OptionalBoolean.of(false), null, null, null, OptionalBoolean.empty(), OptionalBoolean.empty()), + new CameraPreset("geyser:free_effects", "minecraft:free", null, null, null, null, null, CameraAudioListener.CAMERA, OptionalBoolean.empty(), null, OptionalBoolean.of(true), null, null, null, OptionalBoolean.empty(), OptionalBoolean.empty()), + new CameraPreset("geyser:free_audio_effects", "minecraft:free", null, null, null, null, null, CameraAudioListener.PLAYER, OptionalBoolean.empty(), null, OptionalBoolean.of(true), null, null, null, OptionalBoolean.empty(), OptionalBoolean.empty())); SimpleDefinitionRegistry.Builder builder = SimpleDefinitionRegistry.builder(); for (int i = 0; i < CAMERA_PRESETS.size(); i++) { diff --git a/core/src/main/java/org/geysermc/geyser/inventory/GeyserItemStack.java b/core/src/main/java/org/geysermc/geyser/inventory/GeyserItemStack.java index 744ad70b6..256de7799 100644 --- a/core/src/main/java/org/geysermc/geyser/inventory/GeyserItemStack.java +++ b/core/src/main/java/org/geysermc/geyser/inventory/GeyserItemStack.java @@ -29,6 +29,7 @@ import lombok.*; import org.checkerframework.checker.nullness.qual.NonNull; import org.checkerframework.checker.nullness.qual.Nullable; import org.cloudburstmc.protocol.bedrock.data.inventory.ItemData; +import org.geysermc.geyser.GeyserImpl; import org.geysermc.geyser.item.Items; import org.geysermc.geyser.item.type.Item; import org.geysermc.geyser.registry.Registries; @@ -38,6 +39,10 @@ import org.geysermc.geyser.translator.item.ItemTranslator; import org.geysermc.mcprotocollib.protocol.data.game.item.ItemStack; import org.geysermc.mcprotocollib.protocol.data.game.item.component.DataComponentType; import org.geysermc.mcprotocollib.protocol.data.game.item.component.DataComponents; +import org.geysermc.mcprotocollib.protocol.data.game.recipe.display.slot.EmptySlotDisplay; +import org.geysermc.mcprotocollib.protocol.data.game.recipe.display.slot.ItemSlotDisplay; +import org.geysermc.mcprotocollib.protocol.data.game.recipe.display.slot.ItemStackSlotDisplay; +import org.geysermc.mcprotocollib.protocol.data.game.recipe.display.slot.SlotDisplay; import java.util.HashMap; @@ -77,6 +82,20 @@ public class GeyserItemStack { return itemStack == null ? EMPTY : new GeyserItemStack(itemStack.getId(), itemStack.getAmount(), itemStack.getDataComponents()); } + public static @NonNull GeyserItemStack from(@NonNull SlotDisplay slotDisplay) { + if (slotDisplay instanceof EmptySlotDisplay) { + return GeyserItemStack.EMPTY; + } + if (slotDisplay instanceof ItemSlotDisplay itemSlotDisplay) { + return GeyserItemStack.of(itemSlotDisplay.item(), 1); + } + if (slotDisplay instanceof ItemStackSlotDisplay itemStackSlotDisplay) { + return GeyserItemStack.from(itemStackSlotDisplay.itemStack()); + } + GeyserImpl.getInstance().getLogger().warning("Unsure how to convert to ItemStack: " + slotDisplay); + return GeyserItemStack.EMPTY; + } + public int getJavaId() { return isEmpty() ? 0 : javaId; } @@ -163,7 +182,17 @@ public class GeyserItemStack { return session.getItemMappings().getMapping(this.javaId); } + public SlotDisplay asSlotDisplay() { + if (isEmpty()) { + return EmptySlotDisplay.INSTANCE; + } + return new ItemStackSlotDisplay(this.getItemStack()); + } + public Item asItem() { + if (isEmpty()) { + return Items.AIR; + } if (item == null) { return (item = Registries.JAVA_ITEMS.get().get(javaId)); } diff --git a/core/src/main/java/org/geysermc/geyser/inventory/item/GeyserInstrument.java b/core/src/main/java/org/geysermc/geyser/inventory/item/GeyserInstrument.java new file mode 100644 index 000000000..9983a8e90 --- /dev/null +++ b/core/src/main/java/org/geysermc/geyser/inventory/item/GeyserInstrument.java @@ -0,0 +1,166 @@ +/* + * Copyright (c) 2024 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.inventory.item; + +import net.kyori.adventure.key.Key; +import org.checkerframework.checker.nullness.qual.Nullable; +import org.cloudburstmc.nbt.NbtMap; +import org.geysermc.geyser.session.GeyserSession; +import org.geysermc.geyser.session.cache.registry.JavaRegistry; +import org.geysermc.geyser.session.cache.registry.RegistryEntryContext; +import org.geysermc.geyser.translator.text.MessageTranslator; +import org.geysermc.geyser.util.MinecraftKey; +import org.geysermc.geyser.util.SoundUtils; +import org.geysermc.mcprotocollib.protocol.data.game.Holder; +import org.geysermc.mcprotocollib.protocol.data.game.item.component.Instrument; +import org.geysermc.mcprotocollib.protocol.data.game.level.sound.BuiltinSound; + +import java.util.Locale; + +public interface GeyserInstrument { + + static GeyserInstrument read(RegistryEntryContext context) { + NbtMap data = context.data(); + String soundEvent = SoundUtils.readSoundEvent(data, "instrument " + context.id()); + float range = data.getFloat("range"); + String description = MessageTranslator.deserializeDescriptionForTooltip(context.session(), data); + BedrockInstrument bedrockInstrument = BedrockInstrument.getByJavaIdentifier(context.id()); + return new GeyserInstrument.Impl(soundEvent, range, description, bedrockInstrument); + } + + String soundEvent(); + + float range(); + + /** + * In Bedrock format + */ + String description(); + + BedrockInstrument bedrockInstrument(); + + /** + * @return the ID of the Bedrock counterpart for this instrument. If there is none ({@link #bedrockInstrument()} is null), then -1 is returned. + */ + default int bedrockId() { + BedrockInstrument bedrockInstrument = bedrockInstrument(); + if (bedrockInstrument != null) { + return bedrockInstrument.ordinal(); + } + return -1; + } + + /** + * @return the ID of the Java counterpart for the given Bedrock ID. If an invalid Bedrock ID was given, or there is no counterpart, -1 is returned. + */ + static int bedrockIdToJava(GeyserSession session, int id) { + JavaRegistry instruments = session.getRegistryCache().instruments(); + BedrockInstrument bedrockInstrument = BedrockInstrument.getByBedrockId(id); + if (bedrockInstrument != null) { + for (int i = 0; i < instruments.values().size(); i++) { + GeyserInstrument instrument = instruments.byId(i); + if (instrument.bedrockInstrument() == bedrockInstrument) { + return i; + } + } + } + return -1; + } + + static GeyserInstrument fromHolder(GeyserSession session, Holder holder) { + if (holder.isId()) { + return session.getRegistryCache().instruments().byId(holder.id()); + } + Instrument custom = holder.custom(); + return new Wrapper(custom, session.locale()); + } + + record Wrapper(Instrument instrument, String locale) implements GeyserInstrument { + @Override + public String soundEvent() { + return instrument.getSoundEvent().getName(); + } + + @Override + public float range() { + return instrument.getRange(); + } + + @Override + public String description() { + return MessageTranslator.convertMessageForTooltip(instrument.getDescription(), locale); + } + + @Override + public BedrockInstrument bedrockInstrument() { + if (instrument.getSoundEvent() instanceof BuiltinSound) { + return BedrockInstrument.getByJavaIdentifier(MinecraftKey.key(instrument.getSoundEvent().getName())); + } + // Probably custom + return null; + } + } + + record Impl(String soundEvent, float range, String description, @Nullable BedrockInstrument bedrockInstrument) implements GeyserInstrument { + } + + /** + * Each vanilla instrument on Bedrock, ordered in their network IDs. + */ + enum BedrockInstrument { + PONDER, + SING, + SEEK, + FEEL, + ADMIRE, + CALL, + YEARN, + DREAM; + + private static final BedrockInstrument[] VALUES = values(); + private final Key javaIdentifier; + + BedrockInstrument() { + this.javaIdentifier = MinecraftKey.key(this.name().toLowerCase(Locale.ENGLISH) + "_goat_horn"); + } + + public static @Nullable BedrockInstrument getByJavaIdentifier(Key javaIdentifier) { + for (BedrockInstrument instrument : VALUES) { + if (instrument.javaIdentifier.equals(javaIdentifier)) { + return instrument; + } + } + return null; + } + + public static @Nullable BedrockInstrument getByBedrockId(int bedrockId) { + if (bedrockId >= 0 && bedrockId < VALUES.length) { + return VALUES[bedrockId]; + } + return null; + } + } +} diff --git a/core/src/main/java/org/geysermc/geyser/inventory/item/Potion.java b/core/src/main/java/org/geysermc/geyser/inventory/item/Potion.java index 129c365a9..21de6394e 100644 --- a/core/src/main/java/org/geysermc/geyser/inventory/item/Potion.java +++ b/core/src/main/java/org/geysermc/geyser/inventory/item/Potion.java @@ -99,7 +99,7 @@ public enum Potion { } public PotionContents toComponent() { - return new PotionContents(this.ordinal(), -1, Collections.emptyList()); + return new PotionContents(this.ordinal(), -1, Collections.emptyList(), null); } public static Potion getByJavaIdentifier(String javaIdentifier) { diff --git a/core/src/main/java/org/geysermc/geyser/inventory/recipe/GeyserRecipe.java b/core/src/main/java/org/geysermc/geyser/inventory/recipe/GeyserRecipe.java index 8b7fa9522..7d25c5803 100644 --- a/core/src/main/java/org/geysermc/geyser/inventory/recipe/GeyserRecipe.java +++ b/core/src/main/java/org/geysermc/geyser/inventory/recipe/GeyserRecipe.java @@ -25,11 +25,10 @@ package org.geysermc.geyser.inventory.recipe; -import org.checkerframework.checker.nullness.qual.Nullable; -import org.geysermc.mcprotocollib.protocol.data.game.item.ItemStack; +import org.geysermc.mcprotocollib.protocol.data.game.recipe.display.slot.SlotDisplay; /** - * A more compact version of {@link org.geysermc.mcprotocollib.protocol.data.game.recipe.Recipe}. + * A more compact version of {@link org.geysermc.mcprotocollib.protocol.data.game.recipe.display.RecipeDisplay}. */ public interface GeyserRecipe { /** @@ -37,6 +36,5 @@ public interface GeyserRecipe { */ boolean isShaped(); - @Nullable - ItemStack result(); + SlotDisplay result(); } diff --git a/core/src/main/java/org/geysermc/geyser/inventory/recipe/GeyserShapedRecipe.java b/core/src/main/java/org/geysermc/geyser/inventory/recipe/GeyserShapedRecipe.java index ac9fa3ab4..7fc1d52aa 100644 --- a/core/src/main/java/org/geysermc/geyser/inventory/recipe/GeyserShapedRecipe.java +++ b/core/src/main/java/org/geysermc/geyser/inventory/recipe/GeyserShapedRecipe.java @@ -25,15 +25,15 @@ package org.geysermc.geyser.inventory.recipe; -import org.checkerframework.checker.nullness.qual.Nullable; -import org.geysermc.mcprotocollib.protocol.data.game.item.ItemStack; -import org.geysermc.mcprotocollib.protocol.data.game.recipe.Ingredient; -import org.geysermc.mcprotocollib.protocol.data.game.recipe.data.ShapedRecipeData; +import org.geysermc.mcprotocollib.protocol.data.game.recipe.display.ShapedCraftingRecipeDisplay; +import org.geysermc.mcprotocollib.protocol.data.game.recipe.display.slot.SlotDisplay; -public record GeyserShapedRecipe(int width, int height, Ingredient[] ingredients, @Nullable ItemStack result) implements GeyserRecipe { +import java.util.List; - public GeyserShapedRecipe(ShapedRecipeData data) { - this(data.getWidth(), data.getHeight(), data.getIngredients(), data.getResult()); +public record GeyserShapedRecipe(int width, int height, List ingredients, SlotDisplay result) implements GeyserRecipe { + + public GeyserShapedRecipe(ShapedCraftingRecipeDisplay data) { + this(data.width(), data.height(), data.ingredients(), data.result()); } @Override diff --git a/core/src/main/java/org/geysermc/geyser/inventory/recipe/GeyserShapelessRecipe.java b/core/src/main/java/org/geysermc/geyser/inventory/recipe/GeyserShapelessRecipe.java index 388831d4c..ed513a804 100644 --- a/core/src/main/java/org/geysermc/geyser/inventory/recipe/GeyserShapelessRecipe.java +++ b/core/src/main/java/org/geysermc/geyser/inventory/recipe/GeyserShapelessRecipe.java @@ -25,15 +25,15 @@ package org.geysermc.geyser.inventory.recipe; -import org.geysermc.mcprotocollib.protocol.data.game.item.ItemStack; -import org.geysermc.mcprotocollib.protocol.data.game.recipe.Ingredient; -import org.geysermc.mcprotocollib.protocol.data.game.recipe.data.ShapelessRecipeData; -import org.checkerframework.checker.nullness.qual.Nullable; +import org.geysermc.mcprotocollib.protocol.data.game.recipe.display.ShapelessCraftingRecipeDisplay; +import org.geysermc.mcprotocollib.protocol.data.game.recipe.display.slot.SlotDisplay; -public record GeyserShapelessRecipe(Ingredient[] ingredients, @Nullable ItemStack result) implements GeyserRecipe { +import java.util.List; - public GeyserShapelessRecipe(ShapelessRecipeData data) { - this(data.getIngredients(), data.getResult()); +public record GeyserShapelessRecipe(List ingredients, SlotDisplay result) implements GeyserRecipe { + + public GeyserShapelessRecipe(ShapelessCraftingRecipeDisplay data) { + this(data.ingredients(), data.result()); } @Override diff --git a/core/src/main/java/org/geysermc/geyser/inventory/recipe/GeyserSmithingRecipe.java b/core/src/main/java/org/geysermc/geyser/inventory/recipe/GeyserSmithingRecipe.java new file mode 100644 index 000000000..7e4131a4c --- /dev/null +++ b/core/src/main/java/org/geysermc/geyser/inventory/recipe/GeyserSmithingRecipe.java @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2024 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.inventory.recipe; + +import org.geysermc.mcprotocollib.protocol.data.game.recipe.display.SmithingRecipeDisplay; +import org.geysermc.mcprotocollib.protocol.data.game.recipe.display.slot.SlotDisplay; + +public record GeyserSmithingRecipe(SlotDisplay template, + SlotDisplay base, + SlotDisplay addition, + SlotDisplay result) implements GeyserRecipe { + public GeyserSmithingRecipe(SmithingRecipeDisplay display) { + this(display.template(), display.base(), display.addition(), display.result()); + } + + @Override + public boolean isShaped() { + return false; + } +} diff --git a/core/src/main/java/org/geysermc/geyser/inventory/updater/AnvilInventoryUpdater.java b/core/src/main/java/org/geysermc/geyser/inventory/updater/AnvilInventoryUpdater.java index 2e0c75708..0ffb74082 100644 --- a/core/src/main/java/org/geysermc/geyser/inventory/updater/AnvilInventoryUpdater.java +++ b/core/src/main/java/org/geysermc/geyser/inventory/updater/AnvilInventoryUpdater.java @@ -27,13 +27,10 @@ package org.geysermc.geyser.inventory.updater; import it.unimi.dsi.fastutil.objects.Object2IntMap; import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap; -import java.util.stream.IntStream; import net.kyori.adventure.text.Component; import org.cloudburstmc.nbt.NbtMap; import org.cloudburstmc.nbt.NbtMapBuilder; import org.cloudburstmc.protocol.bedrock.data.inventory.ContainerId; -import org.cloudburstmc.protocol.bedrock.data.inventory.ContainerSlotType; -import org.cloudburstmc.protocol.bedrock.data.inventory.FullContainerName; import org.cloudburstmc.protocol.bedrock.data.inventory.ItemData; import org.cloudburstmc.protocol.bedrock.packet.InventorySlotPacket; import org.geysermc.geyser.GeyserImpl; @@ -41,22 +38,21 @@ import org.geysermc.geyser.inventory.AnvilContainer; import org.geysermc.geyser.inventory.GeyserItemStack; import org.geysermc.geyser.inventory.Inventory; import org.geysermc.geyser.inventory.item.BedrockEnchantment; -import org.geysermc.geyser.item.enchantment.Enchantment; import org.geysermc.geyser.item.Items; +import org.geysermc.geyser.item.enchantment.Enchantment; import org.geysermc.geyser.session.GeyserSession; -import org.geysermc.geyser.session.cache.tags.EnchantmentTag; -import org.geysermc.geyser.session.cache.tags.ItemTag; import org.geysermc.geyser.translator.inventory.InventoryTranslator; import org.geysermc.geyser.translator.text.MessageTranslator; import org.geysermc.geyser.util.ItemUtils; import org.geysermc.mcprotocollib.protocol.data.game.entity.player.GameMode; import org.geysermc.mcprotocollib.protocol.data.game.item.component.DataComponentType; -import org.geysermc.mcprotocollib.protocol.data.game.item.component.HolderSet; import org.geysermc.mcprotocollib.protocol.data.game.item.component.ItemEnchantments; import org.geysermc.mcprotocollib.protocol.packet.ingame.serverbound.inventory.ServerboundRenameItemPacket; +import java.util.List; import java.util.Map; import java.util.Objects; +import java.util.stream.IntStream; public class AnvilInventoryUpdater extends InventoryUpdater { public static final AnvilInventoryUpdater INSTANCE = new AnvilInventoryUpdater(); @@ -80,7 +76,6 @@ public class AnvilInventoryUpdater extends InventoryUpdater { slotPacket.setContainerId(ContainerId.UI); slotPacket.setSlot(bedrockSlot); slotPacket.setItem(inventory.getItem(i).getItemData(session)); - slotPacket.setContainerNameData(new FullContainerName(ContainerSlotType.ANVIL_INPUT, null)); session.sendUpstreamPacket(slotPacket); } } @@ -101,7 +96,6 @@ public class AnvilInventoryUpdater extends InventoryUpdater { slotPacket.setContainerId(ContainerId.UI); slotPacket.setSlot(translator.javaSlotToBedrock(javaSlot)); slotPacket.setItem(inventory.getItem(javaSlot).getItemData(session)); - slotPacket.setContainerNameData(new FullContainerName(ContainerSlotType.ANVIL_INPUT, null)); session.sendUpstreamPacket(slotPacket); } else if (lastTargetSlot != javaSlot) { // Update the previous target slot to remove repair cost changes @@ -109,7 +103,6 @@ public class AnvilInventoryUpdater extends InventoryUpdater { slotPacket.setContainerId(ContainerId.UI); slotPacket.setSlot(translator.javaSlotToBedrock(lastTargetSlot)); slotPacket.setItem(inventory.getItem(lastTargetSlot).getItemData(session)); - slotPacket.setContainerNameData(new FullContainerName(ContainerSlotType.ANVIL_INPUT, null)); session.sendUpstreamPacket(slotPacket); } @@ -173,7 +166,6 @@ public class AnvilInventoryUpdater extends InventoryUpdater { slotPacket.setContainerId(ContainerId.UI); slotPacket.setSlot(translator.javaSlotToBedrock(slot)); slotPacket.setItem(itemData); - slotPacket.setContainerNameData(new FullContainerName(ContainerSlotType.ANVIL_INPUT, null)); session.sendUpstreamPacket(slotPacket); } @@ -320,14 +312,10 @@ public class AnvilInventoryUpdater extends InventoryUpdater { for (Object2IntMap.Entry entry : getEnchantments(session, material).object2IntEntrySet()) { Enchantment enchantment = entry.getKey(); - HolderSet supportedItems = enchantment.supportedItems(); - int[] supportedItemIds = supportedItems.resolve(tagId -> session.getTagCache().get(ItemTag.ALL_ITEM_TAGS.get(tagId))); - boolean canApply = isEnchantedBook(input) || IntStream.of(supportedItemIds).anyMatch(id -> id == input.getJavaId()); + boolean canApply = isEnchantedBook(input) || session.getTagCache().is(enchantment.supportedItems(), input.asItem()); - HolderSet exclusiveSet = enchantment.exclusiveSet(); - int[] incompatibleEnchantments = exclusiveSet.resolve(tagId -> session.getTagCache().get(EnchantmentTag.ALL_ENCHANTMENT_TAGS.get(tagId))); - for (int i : incompatibleEnchantments) { - Enchantment incompatible = session.getRegistryCache().enchantments().byId(i); + List incompatibleEnchantments = enchantment.exclusiveSet().resolve(session); + for (Enchantment incompatible : incompatibleEnchantments) { if (combinedEnchantments.containsKey(incompatible)) { canApply = false; if (!bedrock) { diff --git a/core/src/main/java/org/geysermc/geyser/inventory/updater/ChestInventoryUpdater.java b/core/src/main/java/org/geysermc/geyser/inventory/updater/ChestInventoryUpdater.java index 9f3d00c57..a67f594ab 100644 --- a/core/src/main/java/org/geysermc/geyser/inventory/updater/ChestInventoryUpdater.java +++ b/core/src/main/java/org/geysermc/geyser/inventory/updater/ChestInventoryUpdater.java @@ -25,12 +25,10 @@ package org.geysermc.geyser.inventory.updater; -import org.cloudburstmc.protocol.bedrock.data.inventory.ContainerSlotType; -import org.cloudburstmc.protocol.bedrock.data.inventory.FullContainerName; +import lombok.AllArgsConstructor; import org.cloudburstmc.protocol.bedrock.data.inventory.ItemData; import org.cloudburstmc.protocol.bedrock.packet.InventoryContentPacket; import org.cloudburstmc.protocol.bedrock.packet.InventorySlotPacket; -import lombok.AllArgsConstructor; import org.geysermc.geyser.inventory.Inventory; import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.text.GeyserLocale; @@ -63,7 +61,6 @@ public class ChestInventoryUpdater extends InventoryUpdater { InventoryContentPacket contentPacket = new InventoryContentPacket(); contentPacket.setContainerId(inventory.getBedrockId()); contentPacket.setContents(bedrockItems); - contentPacket.setContainerNameData(new FullContainerName(ContainerSlotType.ANVIL_INPUT, null)); session.sendUpstreamPacket(contentPacket); } @@ -76,7 +73,6 @@ public class ChestInventoryUpdater extends InventoryUpdater { slotPacket.setContainerId(inventory.getBedrockId()); slotPacket.setSlot(translator.javaSlotToBedrock(javaSlot)); slotPacket.setItem(inventory.getItem(javaSlot).getItemData(session)); - slotPacket.setContainerNameData(new FullContainerName(ContainerSlotType.ANVIL_INPUT, null)); session.sendUpstreamPacket(slotPacket); return true; } diff --git a/core/src/main/java/org/geysermc/geyser/inventory/updater/ContainerInventoryUpdater.java b/core/src/main/java/org/geysermc/geyser/inventory/updater/ContainerInventoryUpdater.java index 3d372c083..c9f313f2a 100644 --- a/core/src/main/java/org/geysermc/geyser/inventory/updater/ContainerInventoryUpdater.java +++ b/core/src/main/java/org/geysermc/geyser/inventory/updater/ContainerInventoryUpdater.java @@ -25,8 +25,6 @@ package org.geysermc.geyser.inventory.updater; -import org.cloudburstmc.protocol.bedrock.data.inventory.ContainerSlotType; -import org.cloudburstmc.protocol.bedrock.data.inventory.FullContainerName; import org.cloudburstmc.protocol.bedrock.data.inventory.ItemData; import org.cloudburstmc.protocol.bedrock.packet.InventoryContentPacket; import org.cloudburstmc.protocol.bedrock.packet.InventorySlotPacket; @@ -51,7 +49,6 @@ public class ContainerInventoryUpdater extends InventoryUpdater { InventoryContentPacket contentPacket = new InventoryContentPacket(); contentPacket.setContainerId(inventory.getBedrockId()); contentPacket.setContents(Arrays.asList(bedrockItems)); - contentPacket.setContainerNameData(new FullContainerName(ContainerSlotType.ANVIL_INPUT, null)); session.sendUpstreamPacket(contentPacket); } @@ -64,7 +61,6 @@ public class ContainerInventoryUpdater extends InventoryUpdater { slotPacket.setContainerId(inventory.getBedrockId()); slotPacket.setSlot(translator.javaSlotToBedrock(javaSlot)); slotPacket.setItem(inventory.getItem(javaSlot).getItemData(session)); - slotPacket.setContainerNameData(new FullContainerName(ContainerSlotType.ANVIL_INPUT, null)); session.sendUpstreamPacket(slotPacket); return true; } diff --git a/core/src/main/java/org/geysermc/geyser/inventory/updater/CrafterInventoryUpdater.java b/core/src/main/java/org/geysermc/geyser/inventory/updater/CrafterInventoryUpdater.java index 315b84c6d..4474d420c 100644 --- a/core/src/main/java/org/geysermc/geyser/inventory/updater/CrafterInventoryUpdater.java +++ b/core/src/main/java/org/geysermc/geyser/inventory/updater/CrafterInventoryUpdater.java @@ -26,8 +26,6 @@ package org.geysermc.geyser.inventory.updater; import org.cloudburstmc.protocol.bedrock.data.inventory.ContainerId; -import org.cloudburstmc.protocol.bedrock.data.inventory.ContainerSlotType; -import org.cloudburstmc.protocol.bedrock.data.inventory.FullContainerName; import org.cloudburstmc.protocol.bedrock.data.inventory.ItemData; import org.cloudburstmc.protocol.bedrock.packet.InventoryContentPacket; import org.cloudburstmc.protocol.bedrock.packet.InventorySlotPacket; @@ -58,7 +56,6 @@ public class CrafterInventoryUpdater extends InventoryUpdater { contentPacket = new InventoryContentPacket(); contentPacket.setContainerId(inventory.getBedrockId()); contentPacket.setContents(Arrays.asList(bedrockItems)); - contentPacket.setContainerNameData(new FullContainerName(ContainerSlotType.ANVIL_INPUT, null)); session.sendUpstreamPacket(contentPacket); // inventory and hotbar @@ -70,7 +67,6 @@ public class CrafterInventoryUpdater extends InventoryUpdater { contentPacket = new InventoryContentPacket(); contentPacket.setContainerId(ContainerId.INVENTORY); contentPacket.setContents(Arrays.asList(bedrockItems)); - contentPacket.setContainerNameData(new FullContainerName(ContainerSlotType.ANVIL_INPUT, null)); session.sendUpstreamPacket(contentPacket); // Crafter result - it doesn't come after the grid, as explained elsewhere. @@ -92,7 +88,6 @@ public class CrafterInventoryUpdater extends InventoryUpdater { packet.setContainerId(containerId); packet.setSlot(translator.javaSlotToBedrock(javaSlot)); packet.setItem(inventory.getItem(javaSlot).getItemData(session)); - packet.setContainerNameData(new FullContainerName(ContainerSlotType.ANVIL_INPUT, null)); session.sendUpstreamPacket(packet); return true; } diff --git a/core/src/main/java/org/geysermc/geyser/inventory/updater/HorseInventoryUpdater.java b/core/src/main/java/org/geysermc/geyser/inventory/updater/HorseInventoryUpdater.java index 1a46fc02a..7441e66d0 100644 --- a/core/src/main/java/org/geysermc/geyser/inventory/updater/HorseInventoryUpdater.java +++ b/core/src/main/java/org/geysermc/geyser/inventory/updater/HorseInventoryUpdater.java @@ -25,8 +25,6 @@ package org.geysermc.geyser.inventory.updater; -import org.cloudburstmc.protocol.bedrock.data.inventory.ContainerSlotType; -import org.cloudburstmc.protocol.bedrock.data.inventory.FullContainerName; import org.cloudburstmc.protocol.bedrock.data.inventory.ItemData; import org.cloudburstmc.protocol.bedrock.packet.InventoryContentPacket; import org.cloudburstmc.protocol.bedrock.packet.InventorySlotPacket; @@ -51,7 +49,6 @@ public class HorseInventoryUpdater extends InventoryUpdater { InventoryContentPacket contentPacket = new InventoryContentPacket(); contentPacket.setContainerId(inventory.getBedrockId()); contentPacket.setContents(Arrays.asList(bedrockItems)); - contentPacket.setContainerNameData(new FullContainerName(ContainerSlotType.ANVIL_INPUT, null)); session.sendUpstreamPacket(contentPacket); } @@ -64,7 +61,6 @@ public class HorseInventoryUpdater extends InventoryUpdater { slotPacket.setContainerId(4); // Horse GUI? slotPacket.setSlot(translator.javaSlotToBedrock(javaSlot)); slotPacket.setItem(inventory.getItem(javaSlot).getItemData(session)); - slotPacket.setContainerNameData(new FullContainerName(ContainerSlotType.ANVIL_INPUT, null)); session.sendUpstreamPacket(slotPacket); return true; } diff --git a/core/src/main/java/org/geysermc/geyser/inventory/updater/InventoryUpdater.java b/core/src/main/java/org/geysermc/geyser/inventory/updater/InventoryUpdater.java index b7ef4720f..68ee334ba 100644 --- a/core/src/main/java/org/geysermc/geyser/inventory/updater/InventoryUpdater.java +++ b/core/src/main/java/org/geysermc/geyser/inventory/updater/InventoryUpdater.java @@ -26,8 +26,6 @@ package org.geysermc.geyser.inventory.updater; import org.cloudburstmc.protocol.bedrock.data.inventory.ContainerId; -import org.cloudburstmc.protocol.bedrock.data.inventory.ContainerSlotType; -import org.cloudburstmc.protocol.bedrock.data.inventory.FullContainerName; import org.cloudburstmc.protocol.bedrock.data.inventory.ItemData; import org.cloudburstmc.protocol.bedrock.packet.InventoryContentPacket; import org.cloudburstmc.protocol.bedrock.packet.InventorySlotPacket; @@ -47,7 +45,6 @@ public class InventoryUpdater { InventoryContentPacket contentPacket = new InventoryContentPacket(); contentPacket.setContainerId(ContainerId.INVENTORY); contentPacket.setContents(Arrays.asList(bedrockItems)); - contentPacket.setContainerNameData(new FullContainerName(ContainerSlotType.ANVIL_INPUT, null)); session.sendUpstreamPacket(contentPacket); } @@ -57,7 +54,6 @@ public class InventoryUpdater { slotPacket.setContainerId(ContainerId.INVENTORY); slotPacket.setSlot(translator.javaSlotToBedrock(javaSlot)); slotPacket.setItem(inventory.getItem(javaSlot).getItemData(session)); - slotPacket.setContainerNameData(new FullContainerName(ContainerSlotType.ANVIL_INPUT, null)); session.sendUpstreamPacket(slotPacket); return true; } diff --git a/core/src/main/java/org/geysermc/geyser/inventory/updater/UIInventoryUpdater.java b/core/src/main/java/org/geysermc/geyser/inventory/updater/UIInventoryUpdater.java index f4f40d6ce..a23385b53 100644 --- a/core/src/main/java/org/geysermc/geyser/inventory/updater/UIInventoryUpdater.java +++ b/core/src/main/java/org/geysermc/geyser/inventory/updater/UIInventoryUpdater.java @@ -26,8 +26,6 @@ package org.geysermc.geyser.inventory.updater; import org.cloudburstmc.protocol.bedrock.data.inventory.ContainerId; -import org.cloudburstmc.protocol.bedrock.data.inventory.ContainerSlotType; -import org.cloudburstmc.protocol.bedrock.data.inventory.FullContainerName; import org.cloudburstmc.protocol.bedrock.packet.InventorySlotPacket; import org.geysermc.geyser.inventory.Inventory; import org.geysermc.geyser.session.GeyserSession; @@ -48,7 +46,6 @@ public class UIInventoryUpdater extends InventoryUpdater { slotPacket.setContainerId(ContainerId.UI); slotPacket.setSlot(bedrockSlot); slotPacket.setItem(inventory.getItem(i).getItemData(session)); - slotPacket.setContainerNameData(new FullContainerName(ContainerSlotType.ANVIL_INPUT, null)); session.sendUpstreamPacket(slotPacket); } } @@ -62,7 +59,6 @@ public class UIInventoryUpdater extends InventoryUpdater { slotPacket.setContainerId(ContainerId.UI); slotPacket.setSlot(translator.javaSlotToBedrock(javaSlot)); slotPacket.setItem(inventory.getItem(javaSlot).getItemData(session)); - slotPacket.setContainerNameData(new FullContainerName(ContainerSlotType.ANVIL_INPUT, null)); session.sendUpstreamPacket(slotPacket); return true; } diff --git a/core/src/main/java/org/geysermc/geyser/item/Items.java b/core/src/main/java/org/geysermc/geyser/item/Items.java index 462e98d19..7af5aadce 100644 --- a/core/src/main/java/org/geysermc/geyser/item/Items.java +++ b/core/src/main/java/org/geysermc/geyser/item/Items.java @@ -25,9 +25,41 @@ package org.geysermc.geyser.item; -import org.geysermc.geyser.item.components.Rarity; import org.geysermc.geyser.item.components.ToolTier; -import org.geysermc.geyser.item.type.*; +import org.geysermc.geyser.item.type.ArmorItem; +import org.geysermc.geyser.item.type.ArrowItem; +import org.geysermc.geyser.item.type.AxolotlBucketItem; +import org.geysermc.geyser.item.type.BannerItem; +import org.geysermc.geyser.item.type.BlockItem; +import org.geysermc.geyser.item.type.BoatItem; +import org.geysermc.geyser.item.type.CompassItem; +import org.geysermc.geyser.item.type.CrossbowItem; +import org.geysermc.geyser.item.type.DecoratedPotItem; +import org.geysermc.geyser.item.type.DyeItem; +import org.geysermc.geyser.item.type.DyeableArmorItem; +import org.geysermc.geyser.item.type.ElytraItem; +import org.geysermc.geyser.item.type.EnchantedBookItem; +import org.geysermc.geyser.item.type.FilledMapItem; +import org.geysermc.geyser.item.type.FireworkRocketItem; +import org.geysermc.geyser.item.type.FireworkStarItem; +import org.geysermc.geyser.item.type.FishingRodItem; +import org.geysermc.geyser.item.type.GoatHornItem; +import org.geysermc.geyser.item.type.Item; +import org.geysermc.geyser.item.type.LightItem; +import org.geysermc.geyser.item.type.MaceItem; +import org.geysermc.geyser.item.type.MapItem; +import org.geysermc.geyser.item.type.OminousBottleItem; +import org.geysermc.geyser.item.type.PlayerHeadItem; +import org.geysermc.geyser.item.type.PotionItem; +import org.geysermc.geyser.item.type.ShieldItem; +import org.geysermc.geyser.item.type.ShulkerBoxItem; +import org.geysermc.geyser.item.type.SpawnEggItem; +import org.geysermc.geyser.item.type.TieredItem; +import org.geysermc.geyser.item.type.TippedArrowItem; +import org.geysermc.geyser.item.type.TropicalFishBucketItem; +import org.geysermc.geyser.item.type.WolfArmorItem; +import org.geysermc.geyser.item.type.WritableBookItem; +import org.geysermc.geyser.item.type.WrittenBookItem; import org.geysermc.geyser.level.block.Blocks; import org.geysermc.geyser.registry.Registries; @@ -81,6 +113,7 @@ public final class Items { public static final Item ACACIA_PLANKS = register(new BlockItem(builder(), Blocks.ACACIA_PLANKS)); public static final Item CHERRY_PLANKS = register(new BlockItem(builder(), Blocks.CHERRY_PLANKS)); public static final Item DARK_OAK_PLANKS = register(new BlockItem(builder(), Blocks.DARK_OAK_PLANKS)); + public static final Item PALE_OAK_PLANKS = register(new BlockItem(builder(), Blocks.PALE_OAK_PLANKS)); public static final Item MANGROVE_PLANKS = register(new BlockItem(builder(), Blocks.MANGROVE_PLANKS)); public static final Item BAMBOO_PLANKS = register(new BlockItem(builder(), Blocks.BAMBOO_PLANKS)); public static final Item CRIMSON_PLANKS = register(new BlockItem(builder(), Blocks.CRIMSON_PLANKS)); @@ -93,6 +126,7 @@ public final class Items { public static final Item ACACIA_SAPLING = register(new BlockItem(builder(), Blocks.ACACIA_SAPLING)); public static final Item CHERRY_SAPLING = register(new BlockItem(builder(), Blocks.CHERRY_SAPLING)); public static final Item DARK_OAK_SAPLING = register(new BlockItem(builder(), Blocks.DARK_OAK_SAPLING)); + public static final Item PALE_OAK_SAPLING = register(new BlockItem(builder(), Blocks.PALE_OAK_SAPLING)); public static final Item MANGROVE_PROPAGULE = register(new BlockItem(builder(), Blocks.MANGROVE_PROPAGULE)); public static final Item BEDROCK = register(new BlockItem(builder(), Blocks.BEDROCK)); public static final Item SAND = register(new BlockItem(builder(), Blocks.SAND)); @@ -123,7 +157,7 @@ public final class Items { public static final Item RAW_IRON_BLOCK = register(new BlockItem(builder(), Blocks.RAW_IRON_BLOCK)); public static final Item RAW_COPPER_BLOCK = register(new BlockItem(builder(), Blocks.RAW_COPPER_BLOCK)); public static final Item RAW_GOLD_BLOCK = register(new BlockItem(builder(), Blocks.RAW_GOLD_BLOCK)); - public static final Item HEAVY_CORE = register(new BlockItem(builder().rarity(Rarity.EPIC), Blocks.HEAVY_CORE)); + public static final Item HEAVY_CORE = register(new BlockItem(builder(), Blocks.HEAVY_CORE)); public static final Item AMETHYST_BLOCK = register(new BlockItem(builder(), Blocks.AMETHYST_BLOCK)); public static final Item BUDDING_AMETHYST = register(new BlockItem(builder(), Blocks.BUDDING_AMETHYST)); public static final Item IRON_BLOCK = register(new BlockItem(builder(), Blocks.IRON_BLOCK)); @@ -176,6 +210,7 @@ public final class Items { public static final Item JUNGLE_LOG = register(new BlockItem(builder(), Blocks.JUNGLE_LOG)); public static final Item ACACIA_LOG = register(new BlockItem(builder(), Blocks.ACACIA_LOG)); public static final Item CHERRY_LOG = register(new BlockItem(builder(), Blocks.CHERRY_LOG)); + public static final Item PALE_OAK_LOG = register(new BlockItem(builder(), Blocks.PALE_OAK_LOG)); public static final Item DARK_OAK_LOG = register(new BlockItem(builder(), Blocks.DARK_OAK_LOG)); public static final Item MANGROVE_LOG = register(new BlockItem(builder(), Blocks.MANGROVE_LOG)); public static final Item MANGROVE_ROOTS = register(new BlockItem(builder(), Blocks.MANGROVE_ROOTS)); @@ -190,6 +225,7 @@ public final class Items { public static final Item STRIPPED_ACACIA_LOG = register(new BlockItem(builder(), Blocks.STRIPPED_ACACIA_LOG)); public static final Item STRIPPED_CHERRY_LOG = register(new BlockItem(builder(), Blocks.STRIPPED_CHERRY_LOG)); public static final Item STRIPPED_DARK_OAK_LOG = register(new BlockItem(builder(), Blocks.STRIPPED_DARK_OAK_LOG)); + public static final Item STRIPPED_PALE_OAK_LOG = register(new BlockItem(builder(), Blocks.STRIPPED_PALE_OAK_LOG)); public static final Item STRIPPED_MANGROVE_LOG = register(new BlockItem(builder(), Blocks.STRIPPED_MANGROVE_LOG)); public static final Item STRIPPED_CRIMSON_STEM = register(new BlockItem(builder(), Blocks.STRIPPED_CRIMSON_STEM)); public static final Item STRIPPED_WARPED_STEM = register(new BlockItem(builder(), Blocks.STRIPPED_WARPED_STEM)); @@ -200,6 +236,7 @@ public final class Items { public static final Item STRIPPED_ACACIA_WOOD = register(new BlockItem(builder(), Blocks.STRIPPED_ACACIA_WOOD)); public static final Item STRIPPED_CHERRY_WOOD = register(new BlockItem(builder(), Blocks.STRIPPED_CHERRY_WOOD)); public static final Item STRIPPED_DARK_OAK_WOOD = register(new BlockItem(builder(), Blocks.STRIPPED_DARK_OAK_WOOD)); + public static final Item STRIPPED_PALE_OAK_WOOD = register(new BlockItem(builder(), Blocks.STRIPPED_PALE_OAK_WOOD)); public static final Item STRIPPED_MANGROVE_WOOD = register(new BlockItem(builder(), Blocks.STRIPPED_MANGROVE_WOOD)); public static final Item STRIPPED_CRIMSON_HYPHAE = register(new BlockItem(builder(), Blocks.STRIPPED_CRIMSON_HYPHAE)); public static final Item STRIPPED_WARPED_HYPHAE = register(new BlockItem(builder(), Blocks.STRIPPED_WARPED_HYPHAE)); @@ -210,6 +247,7 @@ public final class Items { public static final Item JUNGLE_WOOD = register(new BlockItem(builder(), Blocks.JUNGLE_WOOD)); public static final Item ACACIA_WOOD = register(new BlockItem(builder(), Blocks.ACACIA_WOOD)); public static final Item CHERRY_WOOD = register(new BlockItem(builder(), Blocks.CHERRY_WOOD)); + public static final Item PALE_OAK_WOOD = register(new BlockItem(builder(), Blocks.PALE_OAK_WOOD)); public static final Item DARK_OAK_WOOD = register(new BlockItem(builder(), Blocks.DARK_OAK_WOOD)); public static final Item MANGROVE_WOOD = register(new BlockItem(builder(), Blocks.MANGROVE_WOOD)); public static final Item CRIMSON_HYPHAE = register(new BlockItem(builder(), Blocks.CRIMSON_HYPHAE)); @@ -221,6 +259,7 @@ public final class Items { public static final Item ACACIA_LEAVES = register(new BlockItem(builder(), Blocks.ACACIA_LEAVES)); public static final Item CHERRY_LEAVES = register(new BlockItem(builder(), Blocks.CHERRY_LEAVES)); public static final Item DARK_OAK_LEAVES = register(new BlockItem(builder(), Blocks.DARK_OAK_LEAVES)); + public static final Item PALE_OAK_LEAVES = register(new BlockItem(builder(), Blocks.PALE_OAK_LEAVES)); public static final Item MANGROVE_LEAVES = register(new BlockItem(builder(), Blocks.MANGROVE_LEAVES)); public static final Item AZALEA_LEAVES = register(new BlockItem(builder(), Blocks.AZALEA_LEAVES)); public static final Item FLOWERING_AZALEA_LEAVES = register(new BlockItem(builder(), Blocks.FLOWERING_AZALEA_LEAVES)); @@ -283,9 +322,12 @@ public final class Items { public static final Item TWISTING_VINES = register(new BlockItem(builder(), Blocks.TWISTING_VINES)); public static final Item SUGAR_CANE = register(new BlockItem(builder(), Blocks.SUGAR_CANE)); public static final Item KELP = register(new BlockItem(builder(), Blocks.KELP)); - public static final Item MOSS_CARPET = register(new BlockItem(builder(), Blocks.MOSS_CARPET)); public static final Item PINK_PETALS = register(new BlockItem(builder(), Blocks.PINK_PETALS)); + public static final Item MOSS_CARPET = register(new BlockItem(builder(), Blocks.MOSS_CARPET)); public static final Item MOSS_BLOCK = register(new BlockItem(builder(), Blocks.MOSS_BLOCK)); + public static final Item PALE_MOSS_CARPET = register(new BlockItem(builder(), Blocks.PALE_MOSS_CARPET)); + public static final Item PALE_HANGING_MOSS = register(new BlockItem(builder(), Blocks.PALE_HANGING_MOSS)); + public static final Item PALE_MOSS_BLOCK = register(new BlockItem(builder(), Blocks.PALE_MOSS_BLOCK)); public static final Item HANGING_ROOTS = register(new BlockItem(builder(), Blocks.HANGING_ROOTS)); public static final Item BIG_DRIPLEAF = register(new BlockItem(builder(), Blocks.BIG_DRIPLEAF, Blocks.BIG_DRIPLEAF_STEM)); public static final Item SMALL_DRIPLEAF = register(new BlockItem(builder(), Blocks.SMALL_DRIPLEAF)); @@ -297,6 +339,7 @@ public final class Items { public static final Item ACACIA_SLAB = register(new BlockItem(builder(), Blocks.ACACIA_SLAB)); public static final Item CHERRY_SLAB = register(new BlockItem(builder(), Blocks.CHERRY_SLAB)); public static final Item DARK_OAK_SLAB = register(new BlockItem(builder(), Blocks.DARK_OAK_SLAB)); + public static final Item PALE_OAK_SLAB = register(new BlockItem(builder(), Blocks.PALE_OAK_SLAB)); public static final Item MANGROVE_SLAB = register(new BlockItem(builder(), Blocks.MANGROVE_SLAB)); public static final Item BAMBOO_SLAB = register(new BlockItem(builder(), Blocks.BAMBOO_SLAB)); public static final Item BAMBOO_MOSAIC_SLAB = register(new BlockItem(builder(), Blocks.BAMBOO_MOSAIC_SLAB)); @@ -337,6 +380,7 @@ public final class Items { public static final Item PURPUR_PILLAR = register(new BlockItem(builder(), Blocks.PURPUR_PILLAR)); public static final Item PURPUR_STAIRS = register(new BlockItem(builder(), Blocks.PURPUR_STAIRS)); public static final Item SPAWNER = register(new BlockItem(builder(), Blocks.SPAWNER)); + public static final Item CREAKING_HEART = register(new BlockItem(builder(), Blocks.CREAKING_HEART)); public static final Item CHEST = register(new BlockItem(builder(), Blocks.CHEST)); public static final Item CRAFTING_TABLE = register(new BlockItem(builder(), Blocks.CRAFTING_TABLE)); public static final Item FARMLAND = register(new BlockItem(builder(), Blocks.FARMLAND)); @@ -356,6 +400,7 @@ public final class Items { public static final Item ACACIA_FENCE = register(new BlockItem(builder(), Blocks.ACACIA_FENCE)); public static final Item CHERRY_FENCE = register(new BlockItem(builder(), Blocks.CHERRY_FENCE)); public static final Item DARK_OAK_FENCE = register(new BlockItem(builder(), Blocks.DARK_OAK_FENCE)); + public static final Item PALE_OAK_FENCE = register(new BlockItem(builder(), Blocks.PALE_OAK_FENCE)); public static final Item MANGROVE_FENCE = register(new BlockItem(builder(), Blocks.MANGROVE_FENCE)); public static final Item BAMBOO_FENCE = register(new BlockItem(builder(), Blocks.BAMBOO_FENCE)); public static final Item CRIMSON_FENCE = register(new BlockItem(builder(), Blocks.CRIMSON_FENCE)); @@ -417,7 +462,7 @@ public final class Items { public static final Item END_PORTAL_FRAME = register(new BlockItem(builder(), Blocks.END_PORTAL_FRAME)); public static final Item END_STONE = register(new BlockItem(builder(), Blocks.END_STONE)); public static final Item END_STONE_BRICKS = register(new BlockItem(builder(), Blocks.END_STONE_BRICKS)); - public static final Item DRAGON_EGG = register(new BlockItem(builder().rarity(Rarity.EPIC), Blocks.DRAGON_EGG)); + public static final Item DRAGON_EGG = register(new BlockItem(builder(), Blocks.DRAGON_EGG)); public static final Item SANDSTONE_STAIRS = register(new BlockItem(builder(), Blocks.SANDSTONE_STAIRS)); public static final Item ENDER_CHEST = register(new BlockItem(builder(), Blocks.ENDER_CHEST)); public static final Item EMERALD_BLOCK = register(new BlockItem(builder(), Blocks.EMERALD_BLOCK)); @@ -428,13 +473,14 @@ public final class Items { public static final Item ACACIA_STAIRS = register(new BlockItem(builder(), Blocks.ACACIA_STAIRS)); public static final Item CHERRY_STAIRS = register(new BlockItem(builder(), Blocks.CHERRY_STAIRS)); public static final Item DARK_OAK_STAIRS = register(new BlockItem(builder(), Blocks.DARK_OAK_STAIRS)); + public static final Item PALE_OAK_STAIRS = register(new BlockItem(builder(), Blocks.PALE_OAK_STAIRS)); public static final Item MANGROVE_STAIRS = register(new BlockItem(builder(), Blocks.MANGROVE_STAIRS)); public static final Item BAMBOO_STAIRS = register(new BlockItem(builder(), Blocks.BAMBOO_STAIRS)); public static final Item BAMBOO_MOSAIC_STAIRS = register(new BlockItem(builder(), Blocks.BAMBOO_MOSAIC_STAIRS)); public static final Item CRIMSON_STAIRS = register(new BlockItem(builder(), Blocks.CRIMSON_STAIRS)); public static final Item WARPED_STAIRS = register(new BlockItem(builder(), Blocks.WARPED_STAIRS)); - public static final Item COMMAND_BLOCK = register(new BlockItem(builder().rarity(Rarity.EPIC), Blocks.COMMAND_BLOCK)); - public static final Item BEACON = register(new BlockItem(builder().rarity(Rarity.RARE), Blocks.BEACON)); + public static final Item COMMAND_BLOCK = register(new BlockItem(builder(), Blocks.COMMAND_BLOCK)); + public static final Item BEACON = register(new BlockItem(builder(), Blocks.BEACON)); public static final Item COBBLESTONE_WALL = register(new BlockItem(builder(), Blocks.COBBLESTONE_WALL)); public static final Item MOSSY_COBBLESTONE_WALL = register(new BlockItem(builder(), Blocks.MOSSY_COBBLESTONE_WALL)); public static final Item BRICK_WALL = register(new BlockItem(builder(), Blocks.BRICK_WALL)); @@ -481,8 +527,8 @@ public final class Items { public static final Item GREEN_TERRACOTTA = register(new BlockItem(builder(), Blocks.GREEN_TERRACOTTA)); public static final Item RED_TERRACOTTA = register(new BlockItem(builder(), Blocks.RED_TERRACOTTA)); public static final Item BLACK_TERRACOTTA = register(new BlockItem(builder(), Blocks.BLACK_TERRACOTTA)); - public static final Item BARRIER = register(new BlockItem(builder().rarity(Rarity.EPIC), Blocks.BARRIER)); - public static final Item LIGHT = register(new BlockItem(builder().rarity(Rarity.EPIC), Blocks.LIGHT)); + public static final Item BARRIER = register(new BlockItem(builder(), Blocks.BARRIER)); + public static final Item LIGHT = register(new LightItem(builder(), Blocks.LIGHT)); public static final Item HAY_BLOCK = register(new BlockItem(builder(), Blocks.HAY_BLOCK)); public static final Item WHITE_CARPET = register(new BlockItem(builder(), Blocks.WHITE_CARPET)); public static final Item ORANGE_CARPET = register(new BlockItem(builder(), Blocks.ORANGE_CARPET)); @@ -552,14 +598,14 @@ public final class Items { public static final Item CHISELED_RED_SANDSTONE = register(new BlockItem(builder(), Blocks.CHISELED_RED_SANDSTONE)); public static final Item CUT_RED_SANDSTONE = register(new BlockItem(builder(), Blocks.CUT_RED_SANDSTONE)); public static final Item RED_SANDSTONE_STAIRS = register(new BlockItem(builder(), Blocks.RED_SANDSTONE_STAIRS)); - public static final Item REPEATING_COMMAND_BLOCK = register(new BlockItem(builder().rarity(Rarity.EPIC), Blocks.REPEATING_COMMAND_BLOCK)); - public static final Item CHAIN_COMMAND_BLOCK = register(new BlockItem(builder().rarity(Rarity.EPIC), Blocks.CHAIN_COMMAND_BLOCK)); + public static final Item REPEATING_COMMAND_BLOCK = register(new BlockItem(builder(), Blocks.REPEATING_COMMAND_BLOCK)); + public static final Item CHAIN_COMMAND_BLOCK = register(new BlockItem(builder(), Blocks.CHAIN_COMMAND_BLOCK)); public static final Item MAGMA_BLOCK = register(new BlockItem(builder(), Blocks.MAGMA_BLOCK)); public static final Item NETHER_WART_BLOCK = register(new BlockItem(builder(), Blocks.NETHER_WART_BLOCK)); public static final Item WARPED_WART_BLOCK = register(new BlockItem(builder(), Blocks.WARPED_WART_BLOCK)); public static final Item RED_NETHER_BRICKS = register(new BlockItem(builder(), Blocks.RED_NETHER_BRICKS)); public static final Item BONE_BLOCK = register(new BlockItem(builder(), Blocks.BONE_BLOCK)); - public static final Item STRUCTURE_VOID = register(new BlockItem(builder().rarity(Rarity.EPIC), Blocks.STRUCTURE_VOID)); + public static final Item STRUCTURE_VOID = register(new BlockItem(builder(), Blocks.STRUCTURE_VOID)); public static final Item SHULKER_BOX = register(new ShulkerBoxItem(builder().stackSize(1), Blocks.SHULKER_BOX)); public static final Item WHITE_SHULKER_BOX = register(new ShulkerBoxItem(builder().stackSize(1), Blocks.WHITE_SHULKER_BOX)); public static final Item ORANGE_SHULKER_BOX = register(new ShulkerBoxItem(builder().stackSize(1), Blocks.ORANGE_SHULKER_BOX)); @@ -658,7 +704,7 @@ public final class Items { public static final Item DEAD_FIRE_CORAL_FAN = register(new BlockItem(builder(), Blocks.DEAD_FIRE_CORAL_FAN, Blocks.DEAD_FIRE_CORAL_WALL_FAN)); public static final Item DEAD_HORN_CORAL_FAN = register(new BlockItem(builder(), Blocks.DEAD_HORN_CORAL_FAN, Blocks.DEAD_HORN_CORAL_WALL_FAN)); public static final Item BLUE_ICE = register(new BlockItem(builder(), Blocks.BLUE_ICE)); - public static final Item CONDUIT = register(new BlockItem(builder().rarity(Rarity.RARE), Blocks.CONDUIT)); + public static final Item CONDUIT = register(new BlockItem(builder(), Blocks.CONDUIT)); public static final Item POLISHED_GRANITE_STAIRS = register(new BlockItem(builder(), Blocks.POLISHED_GRANITE_STAIRS)); public static final Item SMOOTH_RED_SANDSTONE_STAIRS = register(new BlockItem(builder(), Blocks.SMOOTH_RED_SANDSTONE_STAIRS)); public static final Item MOSSY_STONE_BRICK_STAIRS = register(new BlockItem(builder(), Blocks.MOSSY_STONE_BRICK_STAIRS)); @@ -729,6 +775,7 @@ public final class Items { public static final Item ACACIA_BUTTON = register(new BlockItem(builder(), Blocks.ACACIA_BUTTON)); public static final Item CHERRY_BUTTON = register(new BlockItem(builder(), Blocks.CHERRY_BUTTON)); public static final Item DARK_OAK_BUTTON = register(new BlockItem(builder(), Blocks.DARK_OAK_BUTTON)); + public static final Item PALE_OAK_BUTTON = register(new BlockItem(builder(), Blocks.PALE_OAK_BUTTON)); public static final Item MANGROVE_BUTTON = register(new BlockItem(builder(), Blocks.MANGROVE_BUTTON)); public static final Item BAMBOO_BUTTON = register(new BlockItem(builder(), Blocks.BAMBOO_BUTTON)); public static final Item CRIMSON_BUTTON = register(new BlockItem(builder(), Blocks.CRIMSON_BUTTON)); @@ -744,6 +791,7 @@ public final class Items { public static final Item ACACIA_PRESSURE_PLATE = register(new BlockItem(builder(), Blocks.ACACIA_PRESSURE_PLATE)); public static final Item CHERRY_PRESSURE_PLATE = register(new BlockItem(builder(), Blocks.CHERRY_PRESSURE_PLATE)); public static final Item DARK_OAK_PRESSURE_PLATE = register(new BlockItem(builder(), Blocks.DARK_OAK_PRESSURE_PLATE)); + public static final Item PALE_OAK_PRESSURE_PLATE = register(new BlockItem(builder(), Blocks.PALE_OAK_PRESSURE_PLATE)); public static final Item MANGROVE_PRESSURE_PLATE = register(new BlockItem(builder(), Blocks.MANGROVE_PRESSURE_PLATE)); public static final Item BAMBOO_PRESSURE_PLATE = register(new BlockItem(builder(), Blocks.BAMBOO_PRESSURE_PLATE)); public static final Item CRIMSON_PRESSURE_PLATE = register(new BlockItem(builder(), Blocks.CRIMSON_PRESSURE_PLATE)); @@ -756,6 +804,7 @@ public final class Items { public static final Item ACACIA_DOOR = register(new BlockItem(builder(), Blocks.ACACIA_DOOR)); public static final Item CHERRY_DOOR = register(new BlockItem(builder(), Blocks.CHERRY_DOOR)); public static final Item DARK_OAK_DOOR = register(new BlockItem(builder(), Blocks.DARK_OAK_DOOR)); + public static final Item PALE_OAK_DOOR = register(new BlockItem(builder(), Blocks.PALE_OAK_DOOR)); public static final Item MANGROVE_DOOR = register(new BlockItem(builder(), Blocks.MANGROVE_DOOR)); public static final Item BAMBOO_DOOR = register(new BlockItem(builder(), Blocks.BAMBOO_DOOR)); public static final Item CRIMSON_DOOR = register(new BlockItem(builder(), Blocks.CRIMSON_DOOR)); @@ -776,6 +825,7 @@ public final class Items { public static final Item ACACIA_TRAPDOOR = register(new BlockItem(builder(), Blocks.ACACIA_TRAPDOOR)); public static final Item CHERRY_TRAPDOOR = register(new BlockItem(builder(), Blocks.CHERRY_TRAPDOOR)); public static final Item DARK_OAK_TRAPDOOR = register(new BlockItem(builder(), Blocks.DARK_OAK_TRAPDOOR)); + public static final Item PALE_OAK_TRAPDOOR = register(new BlockItem(builder(), Blocks.PALE_OAK_TRAPDOOR)); public static final Item MANGROVE_TRAPDOOR = register(new BlockItem(builder(), Blocks.MANGROVE_TRAPDOOR)); public static final Item BAMBOO_TRAPDOOR = register(new BlockItem(builder(), Blocks.BAMBOO_TRAPDOOR)); public static final Item CRIMSON_TRAPDOOR = register(new BlockItem(builder(), Blocks.CRIMSON_TRAPDOOR)); @@ -795,6 +845,7 @@ public final class Items { public static final Item ACACIA_FENCE_GATE = register(new BlockItem(builder(), Blocks.ACACIA_FENCE_GATE)); public static final Item CHERRY_FENCE_GATE = register(new BlockItem(builder(), Blocks.CHERRY_FENCE_GATE)); public static final Item DARK_OAK_FENCE_GATE = register(new BlockItem(builder(), Blocks.DARK_OAK_FENCE_GATE)); + public static final Item PALE_OAK_FENCE_GATE = register(new BlockItem(builder(), Blocks.PALE_OAK_FENCE_GATE)); public static final Item MANGROVE_FENCE_GATE = register(new BlockItem(builder(), Blocks.MANGROVE_FENCE_GATE)); public static final Item BAMBOO_FENCE_GATE = register(new BlockItem(builder(), Blocks.BAMBOO_FENCE_GATE)); public static final Item CRIMSON_FENCE_GATE = register(new BlockItem(builder(), Blocks.CRIMSON_FENCE_GATE)); @@ -811,7 +862,8 @@ public final class Items { public static final Item HOPPER_MINECART = register(new Item("hopper_minecart", builder().stackSize(1))); public static final Item CARROT_ON_A_STICK = register(new Item("carrot_on_a_stick", builder().stackSize(1).maxDamage(25))); public static final Item WARPED_FUNGUS_ON_A_STICK = register(new Item("warped_fungus_on_a_stick", builder().stackSize(1).maxDamage(100))); - public static final Item ELYTRA = register(new ElytraItem("elytra", builder().stackSize(1).maxDamage(432).rarity(Rarity.UNCOMMON))); + public static final Item PHANTOM_MEMBRANE = register(new Item("phantom_membrane", builder())); + public static final Item ELYTRA = register(new ElytraItem("elytra", builder().stackSize(1).maxDamage(432))); public static final Item OAK_BOAT = register(new BoatItem("oak_boat", builder().stackSize(1))); public static final Item OAK_CHEST_BOAT = register(new BoatItem("oak_chest_boat", builder().stackSize(1))); public static final Item SPRUCE_BOAT = register(new BoatItem("spruce_boat", builder().stackSize(1))); @@ -826,12 +878,14 @@ public final class Items { public static final Item CHERRY_CHEST_BOAT = register(new BoatItem("cherry_chest_boat", builder().stackSize(1))); public static final Item DARK_OAK_BOAT = register(new BoatItem("dark_oak_boat", builder().stackSize(1))); public static final Item DARK_OAK_CHEST_BOAT = register(new BoatItem("dark_oak_chest_boat", builder().stackSize(1))); + public static final Item PALE_OAK_BOAT = register(new BoatItem("pale_oak_boat", builder().stackSize(1))); + public static final Item PALE_OAK_CHEST_BOAT = register(new BoatItem("pale_oak_chest_boat", builder().stackSize(1))); public static final Item MANGROVE_BOAT = register(new BoatItem("mangrove_boat", builder().stackSize(1))); public static final Item MANGROVE_CHEST_BOAT = register(new BoatItem("mangrove_chest_boat", builder().stackSize(1))); public static final Item BAMBOO_RAFT = register(new BoatItem("bamboo_raft", builder().stackSize(1))); public static final Item BAMBOO_CHEST_RAFT = register(new BoatItem("bamboo_chest_raft", builder().stackSize(1))); - public static final Item STRUCTURE_BLOCK = register(new BlockItem(builder().rarity(Rarity.EPIC), Blocks.STRUCTURE_BLOCK)); - public static final Item JIGSAW = register(new BlockItem(builder().rarity(Rarity.EPIC), Blocks.JIGSAW)); + public static final Item STRUCTURE_BLOCK = register(new BlockItem(builder(), Blocks.STRUCTURE_BLOCK)); + public static final Item JIGSAW = register(new BlockItem(builder(), Blocks.JIGSAW)); public static final Item TURTLE_HELMET = register(new ArmorItem("turtle_helmet", ArmorMaterial.TURTLE, builder().stackSize(1).maxDamage(275))); public static final Item TURTLE_SCUTE = register(new Item("turtle_scute", builder())); public static final Item ARMADILLO_SCUTE = register(new Item("armadillo_scute", builder())); @@ -922,8 +976,8 @@ public final class Items { public static final Item PORKCHOP = register(new Item("porkchop", builder())); public static final Item COOKED_PORKCHOP = register(new Item("cooked_porkchop", builder())); public static final Item PAINTING = register(new Item("painting", builder())); - public static final Item GOLDEN_APPLE = register(new Item("golden_apple", builder().rarity(Rarity.RARE))); - public static final Item ENCHANTED_GOLDEN_APPLE = register(new Item("enchanted_golden_apple", builder().rarity(Rarity.EPIC))); + public static final Item GOLDEN_APPLE = register(new Item("golden_apple", builder())); + public static final Item ENCHANTED_GOLDEN_APPLE = register(new Item("enchanted_golden_apple", builder())); public static final Item OAK_SIGN = register(new BlockItem(builder().stackSize(16), Blocks.OAK_SIGN, Blocks.OAK_WALL_SIGN)); public static final Item SPRUCE_SIGN = register(new BlockItem(builder().stackSize(16), Blocks.SPRUCE_SIGN, Blocks.SPRUCE_WALL_SIGN)); public static final Item BIRCH_SIGN = register(new BlockItem(builder().stackSize(16), Blocks.BIRCH_SIGN, Blocks.BIRCH_WALL_SIGN)); @@ -931,6 +985,7 @@ public final class Items { public static final Item ACACIA_SIGN = register(new BlockItem(builder().stackSize(16), Blocks.ACACIA_SIGN, Blocks.ACACIA_WALL_SIGN)); public static final Item CHERRY_SIGN = register(new BlockItem(builder().stackSize(16), Blocks.CHERRY_SIGN, Blocks.CHERRY_WALL_SIGN)); public static final Item DARK_OAK_SIGN = register(new BlockItem(builder().stackSize(16), Blocks.DARK_OAK_SIGN, Blocks.DARK_OAK_WALL_SIGN)); + public static final Item PALE_OAK_SIGN = register(new BlockItem(builder().stackSize(16), Blocks.PALE_OAK_SIGN, Blocks.PALE_OAK_WALL_SIGN)); public static final Item MANGROVE_SIGN = register(new BlockItem(builder().stackSize(16), Blocks.MANGROVE_SIGN, Blocks.MANGROVE_WALL_SIGN)); public static final Item BAMBOO_SIGN = register(new BlockItem(builder().stackSize(16), Blocks.BAMBOO_SIGN, Blocks.BAMBOO_WALL_SIGN)); public static final Item CRIMSON_SIGN = register(new BlockItem(builder().stackSize(16), Blocks.CRIMSON_SIGN, Blocks.CRIMSON_WALL_SIGN)); @@ -942,6 +997,7 @@ public final class Items { public static final Item ACACIA_HANGING_SIGN = register(new BlockItem(builder().stackSize(16), Blocks.ACACIA_HANGING_SIGN, Blocks.ACACIA_WALL_HANGING_SIGN)); public static final Item CHERRY_HANGING_SIGN = register(new BlockItem(builder().stackSize(16), Blocks.CHERRY_HANGING_SIGN, Blocks.CHERRY_WALL_HANGING_SIGN)); public static final Item DARK_OAK_HANGING_SIGN = register(new BlockItem(builder().stackSize(16), Blocks.DARK_OAK_HANGING_SIGN, Blocks.DARK_OAK_WALL_HANGING_SIGN)); + public static final Item PALE_OAK_HANGING_SIGN = register(new BlockItem(builder().stackSize(16), Blocks.PALE_OAK_HANGING_SIGN, Blocks.PALE_OAK_WALL_HANGING_SIGN)); public static final Item MANGROVE_HANGING_SIGN = register(new BlockItem(builder().stackSize(16), Blocks.MANGROVE_HANGING_SIGN, Blocks.MANGROVE_WALL_HANGING_SIGN)); public static final Item BAMBOO_HANGING_SIGN = register(new BlockItem(builder().stackSize(16), Blocks.BAMBOO_HANGING_SIGN, Blocks.BAMBOO_WALL_HANGING_SIGN)); public static final Item CRIMSON_HANGING_SIGN = register(new BlockItem(builder().stackSize(16), Blocks.CRIMSON_HANGING_SIGN, Blocks.CRIMSON_WALL_HANGING_SIGN)); @@ -969,6 +1025,22 @@ public final class Items { public static final Item COMPASS = register(new CompassItem("compass", builder())); public static final Item RECOVERY_COMPASS = register(new Item("recovery_compass", builder())); public static final Item BUNDLE = register(new Item("bundle", builder().stackSize(1))); + public static final Item WHITE_BUNDLE = register(new Item("white_bundle", builder().stackSize(1))); + public static final Item ORANGE_BUNDLE = register(new Item("orange_bundle", builder().stackSize(1))); + public static final Item MAGENTA_BUNDLE = register(new Item("magenta_bundle", builder().stackSize(1))); + public static final Item LIGHT_BLUE_BUNDLE = register(new Item("light_blue_bundle", builder().stackSize(1))); + public static final Item YELLOW_BUNDLE = register(new Item("yellow_bundle", builder().stackSize(1))); + public static final Item LIME_BUNDLE = register(new Item("lime_bundle", builder().stackSize(1))); + public static final Item PINK_BUNDLE = register(new Item("pink_bundle", builder().stackSize(1))); + public static final Item GRAY_BUNDLE = register(new Item("gray_bundle", builder().stackSize(1))); + public static final Item LIGHT_GRAY_BUNDLE = register(new Item("light_gray_bundle", builder().stackSize(1))); + public static final Item CYAN_BUNDLE = register(new Item("cyan_bundle", builder().stackSize(1))); + public static final Item PURPLE_BUNDLE = register(new Item("purple_bundle", builder().stackSize(1))); + public static final Item BLUE_BUNDLE = register(new Item("blue_bundle", builder().stackSize(1))); + public static final Item BROWN_BUNDLE = register(new Item("brown_bundle", builder().stackSize(1))); + public static final Item GREEN_BUNDLE = register(new Item("green_bundle", builder().stackSize(1))); + public static final Item RED_BUNDLE = register(new Item("red_bundle", builder().stackSize(1))); + public static final Item BLACK_BUNDLE = register(new Item("black_bundle", builder().stackSize(1))); public static final Item FISHING_ROD = register(new FishingRodItem("fishing_rod", builder().stackSize(1).maxDamage(64))); public static final Item CLOCK = register(new Item("clock", builder())); public static final Item SPYGLASS = register(new Item("spyglass", builder().stackSize(1))); @@ -1036,14 +1108,14 @@ public final class Items { public static final Item GHAST_TEAR = register(new Item("ghast_tear", builder())); public static final Item GOLD_NUGGET = register(new Item("gold_nugget", builder())); public static final Item NETHER_WART = register(new BlockItem(builder(), Blocks.NETHER_WART)); - public static final Item POTION = register(new PotionItem("potion", builder().stackSize(1))); public static final Item GLASS_BOTTLE = register(new Item("glass_bottle", builder())); + public static final Item POTION = register(new PotionItem("potion", builder().stackSize(1))); public static final Item SPIDER_EYE = register(new Item("spider_eye", builder())); public static final Item FERMENTED_SPIDER_EYE = register(new Item("fermented_spider_eye", builder())); public static final Item BLAZE_POWDER = register(new Item("blaze_powder", builder())); public static final Item MAGMA_CREAM = register(new Item("magma_cream", builder())); public static final Item BREWING_STAND = register(new BlockItem(builder(), Blocks.BREWING_STAND)); - public static final Item CAULDRON = register(new BlockItem(builder(), Blocks.CAULDRON, Blocks.WATER_CAULDRON, Blocks.POWDER_SNOW_CAULDRON, Blocks.LAVA_CAULDRON)); + public static final Item CAULDRON = register(new BlockItem(builder(), Blocks.CAULDRON, Blocks.LAVA_CAULDRON, Blocks.WATER_CAULDRON, Blocks.POWDER_SNOW_CAULDRON)); public static final Item ENDER_EYE = register(new Item("ender_eye", builder())); public static final Item GLISTERING_MELON_SLICE = register(new Item("glistering_melon_slice", builder())); public static final Item ARMADILLO_SPAWN_EGG = register(new SpawnEggItem("armadillo_spawn_egg", builder())); @@ -1122,16 +1194,18 @@ public final class Items { public static final Item WITHER_SKELETON_SPAWN_EGG = register(new SpawnEggItem("wither_skeleton_spawn_egg", builder())); public static final Item WOLF_SPAWN_EGG = register(new SpawnEggItem("wolf_spawn_egg", builder())); public static final Item ZOGLIN_SPAWN_EGG = register(new SpawnEggItem("zoglin_spawn_egg", builder())); + public static final Item CREAKING_SPAWN_EGG = register(new SpawnEggItem("creaking_spawn_egg", builder())); public static final Item ZOMBIE_SPAWN_EGG = register(new SpawnEggItem("zombie_spawn_egg", builder())); public static final Item ZOMBIE_HORSE_SPAWN_EGG = register(new SpawnEggItem("zombie_horse_spawn_egg", builder())); public static final Item ZOMBIE_VILLAGER_SPAWN_EGG = register(new SpawnEggItem("zombie_villager_spawn_egg", builder())); public static final Item ZOMBIFIED_PIGLIN_SPAWN_EGG = register(new SpawnEggItem("zombified_piglin_spawn_egg", builder())); - public static final Item EXPERIENCE_BOTTLE = register(new Item("experience_bottle", builder().rarity(Rarity.UNCOMMON))); + public static final Item EXPERIENCE_BOTTLE = register(new Item("experience_bottle", builder())); public static final Item FIRE_CHARGE = register(new Item("fire_charge", builder())); public static final Item WIND_CHARGE = register(new Item("wind_charge", builder())); public static final Item WRITABLE_BOOK = register(new WritableBookItem("writable_book", builder().stackSize(1))); public static final Item WRITTEN_BOOK = register(new WrittenBookItem("written_book", builder().stackSize(16))); - public static final Item MACE = register(new MaceItem("mace", builder().stackSize(1).maxDamage(500).rarity(Rarity.EPIC))); + public static final Item BREEZE_ROD = register(new Item("breeze_rod", builder())); + public static final Item MACE = register(new MaceItem("mace", builder().stackSize(1).maxDamage(500))); public static final Item ITEM_FRAME = register(new Item("item_frame", builder())); public static final Item GLOW_ITEM_FRAME = register(new Item("glow_item_frame", builder())); public static final Item FLOWER_POT = register(new BlockItem(builder(), Blocks.FLOWER_POT)); @@ -1141,18 +1215,18 @@ public final class Items { public static final Item POISONOUS_POTATO = register(new Item("poisonous_potato", builder())); public static final Item MAP = register(new MapItem("map", builder())); public static final Item GOLDEN_CARROT = register(new Item("golden_carrot", builder())); - public static final Item SKELETON_SKULL = register(new BlockItem(builder().rarity(Rarity.UNCOMMON), Blocks.SKELETON_SKULL, Blocks.SKELETON_WALL_SKULL)); - public static final Item WITHER_SKELETON_SKULL = register(new BlockItem(builder().rarity(Rarity.UNCOMMON), Blocks.WITHER_SKELETON_SKULL, Blocks.WITHER_SKELETON_WALL_SKULL)); - public static final Item PLAYER_HEAD = register(new PlayerHeadItem(builder().rarity(Rarity.UNCOMMON), Blocks.PLAYER_HEAD, Blocks.PLAYER_WALL_HEAD)); - public static final Item ZOMBIE_HEAD = register(new BlockItem(builder().rarity(Rarity.UNCOMMON), Blocks.ZOMBIE_HEAD, Blocks.ZOMBIE_WALL_HEAD)); - public static final Item CREEPER_HEAD = register(new BlockItem(builder().rarity(Rarity.UNCOMMON), Blocks.CREEPER_HEAD, Blocks.CREEPER_WALL_HEAD)); - public static final Item DRAGON_HEAD = register(new BlockItem(builder().rarity(Rarity.UNCOMMON), Blocks.DRAGON_HEAD, Blocks.DRAGON_WALL_HEAD)); - public static final Item PIGLIN_HEAD = register(new BlockItem(builder().rarity(Rarity.UNCOMMON), Blocks.PIGLIN_HEAD, Blocks.PIGLIN_WALL_HEAD)); - public static final Item NETHER_STAR = register(new Item("nether_star", builder().rarity(Rarity.UNCOMMON))); + public static final Item SKELETON_SKULL = register(new BlockItem(builder(), Blocks.SKELETON_SKULL, Blocks.SKELETON_WALL_SKULL)); + public static final Item WITHER_SKELETON_SKULL = register(new BlockItem(builder(), Blocks.WITHER_SKELETON_SKULL, Blocks.WITHER_SKELETON_WALL_SKULL)); + public static final Item PLAYER_HEAD = register(new PlayerHeadItem(builder(), Blocks.PLAYER_HEAD, Blocks.PLAYER_WALL_HEAD)); + public static final Item ZOMBIE_HEAD = register(new BlockItem(builder(), Blocks.ZOMBIE_HEAD, Blocks.ZOMBIE_WALL_HEAD)); + public static final Item CREEPER_HEAD = register(new BlockItem(builder(), Blocks.CREEPER_HEAD, Blocks.CREEPER_WALL_HEAD)); + public static final Item DRAGON_HEAD = register(new BlockItem(builder(), Blocks.DRAGON_HEAD, Blocks.DRAGON_WALL_HEAD)); + public static final Item PIGLIN_HEAD = register(new BlockItem(builder(), Blocks.PIGLIN_HEAD, Blocks.PIGLIN_WALL_HEAD)); + public static final Item NETHER_STAR = register(new Item("nether_star", builder())); public static final Item PUMPKIN_PIE = register(new Item("pumpkin_pie", builder())); public static final Item FIREWORK_ROCKET = register(new FireworkRocketItem("firework_rocket", builder())); public static final Item FIREWORK_STAR = register(new FireworkStarItem("firework_star", builder())); - public static final Item ENCHANTED_BOOK = register(new EnchantedBookItem("enchanted_book", builder().stackSize(1).rarity(Rarity.UNCOMMON))); + public static final Item ENCHANTED_BOOK = register(new EnchantedBookItem("enchanted_book", builder().stackSize(1))); public static final Item NETHER_BRICK = register(new Item("nether_brick", builder())); public static final Item PRISMARINE_SHARD = register(new Item("prismarine_shard", builder())); public static final Item PRISMARINE_CRYSTALS = register(new Item("prismarine_crystals", builder())); @@ -1162,13 +1236,13 @@ public final class Items { public static final Item RABBIT_FOOT = register(new Item("rabbit_foot", builder())); public static final Item RABBIT_HIDE = register(new Item("rabbit_hide", builder())); public static final Item ARMOR_STAND = register(new Item("armor_stand", builder().stackSize(16))); - public static final Item IRON_HORSE_ARMOR = register(new ArmorItem("iron_horse_armor", ArmorMaterial.IRON, builder().stackSize(1))); - public static final Item GOLDEN_HORSE_ARMOR = register(new ArmorItem("golden_horse_armor", ArmorMaterial.GOLD, builder().stackSize(1))); - public static final Item DIAMOND_HORSE_ARMOR = register(new ArmorItem("diamond_horse_armor", ArmorMaterial.DIAMOND, builder().stackSize(1))); + public static final Item IRON_HORSE_ARMOR = register(new Item("iron_horse_armor", builder().stackSize(1))); + public static final Item GOLDEN_HORSE_ARMOR = register(new Item("golden_horse_armor", builder().stackSize(1))); + public static final Item DIAMOND_HORSE_ARMOR = register(new Item("diamond_horse_armor", builder().stackSize(1))); public static final Item LEATHER_HORSE_ARMOR = register(new DyeableArmorItem("leather_horse_armor", ArmorMaterial.LEATHER, builder().stackSize(1))); public static final Item LEAD = register(new Item("lead", builder())); public static final Item NAME_TAG = register(new Item("name_tag", builder())); - public static final Item COMMAND_BLOCK_MINECART = register(new Item("command_block_minecart", builder().stackSize(1).rarity(Rarity.EPIC))); + public static final Item COMMAND_BLOCK_MINECART = register(new Item("command_block_minecart", builder().stackSize(1))); public static final Item MUTTON = register(new Item("mutton", builder())); public static final Item COOKED_MUTTON = register(new Item("cooked_mutton", builder())); public static final Item WHITE_BANNER = register(new BannerItem(builder().stackSize(16), Blocks.WHITE_BANNER, Blocks.WHITE_WALL_BANNER)); @@ -1187,7 +1261,7 @@ public final class Items { public static final Item GREEN_BANNER = register(new BannerItem(builder().stackSize(16), Blocks.GREEN_BANNER, Blocks.GREEN_WALL_BANNER)); public static final Item RED_BANNER = register(new BannerItem(builder().stackSize(16), Blocks.RED_BANNER, Blocks.RED_WALL_BANNER)); public static final Item BLACK_BANNER = register(new BannerItem(builder().stackSize(16), Blocks.BLACK_BANNER, Blocks.BLACK_WALL_BANNER)); - public static final Item END_CRYSTAL = register(new Item("end_crystal", builder().rarity(Rarity.RARE))); + public static final Item END_CRYSTAL = register(new Item("end_crystal", builder())); public static final Item CHORUS_FRUIT = register(new Item("chorus_fruit", builder())); public static final Item POPPED_CHORUS_FRUIT = register(new Item("popped_chorus_fruit", builder())); public static final Item TORCHFLOWER_SEEDS = register(new BlockItem("torchflower_seeds", builder(), Blocks.TORCHFLOWER_CROP)); @@ -1195,52 +1269,53 @@ public final class Items { public static final Item BEETROOT = register(new Item("beetroot", builder())); public static final Item BEETROOT_SEEDS = register(new BlockItem("beetroot_seeds", builder(), Blocks.BEETROOTS)); public static final Item BEETROOT_SOUP = register(new Item("beetroot_soup", builder().stackSize(1))); - public static final Item DRAGON_BREATH = register(new Item("dragon_breath", builder().rarity(Rarity.UNCOMMON))); + public static final Item DRAGON_BREATH = register(new Item("dragon_breath", builder())); public static final Item SPLASH_POTION = register(new PotionItem("splash_potion", builder().stackSize(1))); public static final Item SPECTRAL_ARROW = register(new Item("spectral_arrow", builder())); public static final Item TIPPED_ARROW = register(new TippedArrowItem("tipped_arrow", builder())); public static final Item LINGERING_POTION = register(new PotionItem("lingering_potion", builder().stackSize(1))); public static final Item SHIELD = register(new ShieldItem("shield", builder().stackSize(1).maxDamage(336))); - public static final Item TOTEM_OF_UNDYING = register(new Item("totem_of_undying", builder().stackSize(1).rarity(Rarity.UNCOMMON))); + public static final Item TOTEM_OF_UNDYING = register(new Item("totem_of_undying", builder().stackSize(1))); public static final Item SHULKER_SHELL = register(new Item("shulker_shell", builder())); public static final Item IRON_NUGGET = register(new Item("iron_nugget", builder())); - public static final Item KNOWLEDGE_BOOK = register(new Item("knowledge_book", builder().stackSize(1).rarity(Rarity.EPIC))); - public static final Item DEBUG_STICK = register(new Item("debug_stick", builder().stackSize(1).rarity(Rarity.EPIC).glint(true))); - public static final Item MUSIC_DISC_13 = register(new Item("music_disc_13", builder().stackSize(1).rarity(Rarity.RARE))); - public static final Item MUSIC_DISC_CAT = register(new Item("music_disc_cat", builder().stackSize(1).rarity(Rarity.RARE))); - public static final Item MUSIC_DISC_BLOCKS = register(new Item("music_disc_blocks", builder().stackSize(1).rarity(Rarity.RARE))); - public static final Item MUSIC_DISC_CHIRP = register(new Item("music_disc_chirp", builder().stackSize(1).rarity(Rarity.RARE))); - public static final Item MUSIC_DISC_CREATOR = register(new Item("music_disc_creator", builder().stackSize(1).rarity(Rarity.RARE))); - public static final Item MUSIC_DISC_CREATOR_MUSIC_BOX = register(new Item("music_disc_creator_music_box", builder().stackSize(1).rarity(Rarity.RARE))); - public static final Item MUSIC_DISC_FAR = register(new Item("music_disc_far", builder().stackSize(1).rarity(Rarity.RARE))); - public static final Item MUSIC_DISC_MALL = register(new Item("music_disc_mall", builder().stackSize(1).rarity(Rarity.RARE))); - public static final Item MUSIC_DISC_MELLOHI = register(new Item("music_disc_mellohi", builder().stackSize(1).rarity(Rarity.RARE))); - public static final Item MUSIC_DISC_STAL = register(new Item("music_disc_stal", builder().stackSize(1).rarity(Rarity.RARE))); - public static final Item MUSIC_DISC_STRAD = register(new Item("music_disc_strad", builder().stackSize(1).rarity(Rarity.RARE))); - public static final Item MUSIC_DISC_WARD = register(new Item("music_disc_ward", builder().stackSize(1).rarity(Rarity.RARE))); - public static final Item MUSIC_DISC_11 = register(new Item("music_disc_11", builder().stackSize(1).rarity(Rarity.RARE))); - public static final Item MUSIC_DISC_WAIT = register(new Item("music_disc_wait", builder().stackSize(1).rarity(Rarity.RARE))); - public static final Item MUSIC_DISC_OTHERSIDE = register(new Item("music_disc_otherside", builder().stackSize(1).rarity(Rarity.RARE))); - public static final Item MUSIC_DISC_RELIC = register(new Item("music_disc_relic", builder().stackSize(1).rarity(Rarity.RARE))); - public static final Item MUSIC_DISC_5 = register(new Item("music_disc_5", builder().stackSize(1).rarity(Rarity.RARE))); - public static final Item MUSIC_DISC_PIGSTEP = register(new Item("music_disc_pigstep", builder().stackSize(1).rarity(Rarity.RARE))); - public static final Item MUSIC_DISC_PRECIPICE = register(new Item("music_disc_precipice", builder().stackSize(1).rarity(Rarity.RARE))); + public static final Item KNOWLEDGE_BOOK = register(new Item("knowledge_book", builder().stackSize(1))); + public static final Item DEBUG_STICK = register(new Item("debug_stick", builder().stackSize(1))); + public static final Item MUSIC_DISC_13 = register(new Item("music_disc_13", builder().stackSize(1))); + public static final Item MUSIC_DISC_CAT = register(new Item("music_disc_cat", builder().stackSize(1))); + public static final Item MUSIC_DISC_BLOCKS = register(new Item("music_disc_blocks", builder().stackSize(1))); + public static final Item MUSIC_DISC_CHIRP = register(new Item("music_disc_chirp", builder().stackSize(1))); + public static final Item MUSIC_DISC_CREATOR = register(new Item("music_disc_creator", builder().stackSize(1))); + public static final Item MUSIC_DISC_CREATOR_MUSIC_BOX = register(new Item("music_disc_creator_music_box", builder().stackSize(1))); + public static final Item MUSIC_DISC_FAR = register(new Item("music_disc_far", builder().stackSize(1))); + public static final Item MUSIC_DISC_MALL = register(new Item("music_disc_mall", builder().stackSize(1))); + public static final Item MUSIC_DISC_MELLOHI = register(new Item("music_disc_mellohi", builder().stackSize(1))); + public static final Item MUSIC_DISC_STAL = register(new Item("music_disc_stal", builder().stackSize(1))); + public static final Item MUSIC_DISC_STRAD = register(new Item("music_disc_strad", builder().stackSize(1))); + public static final Item MUSIC_DISC_WARD = register(new Item("music_disc_ward", builder().stackSize(1))); + public static final Item MUSIC_DISC_11 = register(new Item("music_disc_11", builder().stackSize(1))); + public static final Item MUSIC_DISC_WAIT = register(new Item("music_disc_wait", builder().stackSize(1))); + public static final Item MUSIC_DISC_OTHERSIDE = register(new Item("music_disc_otherside", builder().stackSize(1))); + public static final Item MUSIC_DISC_RELIC = register(new Item("music_disc_relic", builder().stackSize(1))); + public static final Item MUSIC_DISC_5 = register(new Item("music_disc_5", builder().stackSize(1))); + public static final Item MUSIC_DISC_PIGSTEP = register(new Item("music_disc_pigstep", builder().stackSize(1))); + public static final Item MUSIC_DISC_PRECIPICE = register(new Item("music_disc_precipice", builder().stackSize(1))); public static final Item DISC_FRAGMENT_5 = register(new Item("disc_fragment_5", builder())); - public static final Item TRIDENT = register(new Item("trident", builder().stackSize(1).maxDamage(250).attackDamage(9.0).rarity(Rarity.EPIC))); - public static final Item PHANTOM_MEMBRANE = register(new Item("phantom_membrane", builder())); + public static final Item TRIDENT = register(new Item("trident", builder().stackSize(1).maxDamage(250).attackDamage(9.0))); public static final Item NAUTILUS_SHELL = register(new Item("nautilus_shell", builder())); - public static final Item HEART_OF_THE_SEA = register(new Item("heart_of_the_sea", builder().rarity(Rarity.UNCOMMON))); + public static final Item HEART_OF_THE_SEA = register(new Item("heart_of_the_sea", builder())); public static final Item CROSSBOW = register(new CrossbowItem("crossbow", builder().stackSize(1).maxDamage(465))); public static final Item SUSPICIOUS_STEW = register(new Item("suspicious_stew", builder().stackSize(1))); public static final Item LOOM = register(new BlockItem(builder(), Blocks.LOOM)); public static final Item FLOWER_BANNER_PATTERN = register(new Item("flower_banner_pattern", builder().stackSize(1))); - public static final Item CREEPER_BANNER_PATTERN = register(new Item("creeper_banner_pattern", builder().stackSize(1).rarity(Rarity.UNCOMMON))); - public static final Item SKULL_BANNER_PATTERN = register(new Item("skull_banner_pattern", builder().stackSize(1).rarity(Rarity.UNCOMMON))); - public static final Item MOJANG_BANNER_PATTERN = register(new Item("mojang_banner_pattern", builder().stackSize(1).rarity(Rarity.EPIC))); + public static final Item CREEPER_BANNER_PATTERN = register(new Item("creeper_banner_pattern", builder().stackSize(1))); + public static final Item SKULL_BANNER_PATTERN = register(new Item("skull_banner_pattern", builder().stackSize(1))); + public static final Item MOJANG_BANNER_PATTERN = register(new Item("mojang_banner_pattern", builder().stackSize(1))); public static final Item GLOBE_BANNER_PATTERN = register(new Item("globe_banner_pattern", builder().stackSize(1))); - public static final Item PIGLIN_BANNER_PATTERN = register(new Item("piglin_banner_pattern", builder().stackSize(1).rarity(Rarity.UNCOMMON))); - public static final Item FLOW_BANNER_PATTERN = register(new Item("flow_banner_pattern", builder().stackSize(1).rarity(Rarity.RARE))); - public static final Item GUSTER_BANNER_PATTERN = register(new Item("guster_banner_pattern", builder().stackSize(1).rarity(Rarity.RARE))); + public static final Item PIGLIN_BANNER_PATTERN = register(new Item("piglin_banner_pattern", builder().stackSize(1))); + public static final Item FLOW_BANNER_PATTERN = register(new Item("flow_banner_pattern", builder().stackSize(1))); + public static final Item GUSTER_BANNER_PATTERN = register(new Item("guster_banner_pattern", builder().stackSize(1))); + public static final Item FIELD_MASONED_BANNER_PATTERN = register(new Item("field_masoned_banner_pattern", builder().stackSize(1))); + public static final Item BORDURE_INDENTED_BANNER_PATTERN = register(new Item("bordure_indented_banner_pattern", builder().stackSize(1))); public static final Item GOAT_HORN = register(new GoatHornItem("goat_horn", builder().stackSize(1))); public static final Item COMPOSTER = register(new BlockItem(builder(), Blocks.COMPOSTER)); public static final Item BARREL = register(new BlockItem(builder(), Blocks.BARREL)); @@ -1370,7 +1445,6 @@ public final class Items { public static final Item OMINOUS_TRIAL_KEY = register(new Item("ominous_trial_key", builder())); public static final Item VAULT = register(new BlockItem(builder(), Blocks.VAULT)); public static final Item OMINOUS_BOTTLE = register(new OminousBottleItem("ominous_bottle", builder())); - public static final Item BREEZE_ROD = register(new Item("breeze_rod", builder())); public static final int AIR_ID = AIR.javaId(); diff --git a/core/src/main/java/org/geysermc/geyser/item/enchantment/Enchantment.java b/core/src/main/java/org/geysermc/geyser/item/enchantment/Enchantment.java index 301f69a5f..e0b4f6e0f 100644 --- a/core/src/main/java/org/geysermc/geyser/item/enchantment/Enchantment.java +++ b/core/src/main/java/org/geysermc/geyser/item/enchantment/Enchantment.java @@ -25,52 +25,47 @@ package org.geysermc.geyser.item.enchantment; -import it.unimi.dsi.fastutil.ints.IntArrays; -import net.kyori.adventure.key.Key; import org.checkerframework.checker.nullness.qual.Nullable; import org.cloudburstmc.nbt.NbtMap; import org.geysermc.geyser.inventory.item.BedrockEnchantment; import org.geysermc.geyser.item.Items; +import org.geysermc.geyser.item.type.Item; import org.geysermc.geyser.registry.Registries; +import org.geysermc.geyser.session.cache.registry.JavaRegistries; import org.geysermc.geyser.session.cache.registry.RegistryEntryContext; +import org.geysermc.geyser.session.cache.tags.GeyserHolderSet; import org.geysermc.geyser.translator.text.MessageTranslator; -import org.geysermc.geyser.util.MinecraftKey; -import org.geysermc.mcprotocollib.protocol.data.game.item.component.HolderSet; import java.util.HashSet; -import java.util.List; import java.util.Map; import java.util.Set; -import java.util.function.ToIntFunction; /** - * @param description only populated if {@link #bedrockEnchantment()} is not null. + * @param description only populated if {@link #bedrockEnchantment()} is null. * @param anvilCost also as a rarity multiplier */ public record Enchantment(String identifier, Set effects, - HolderSet supportedItems, + GeyserHolderSet supportedItems, int maxLevel, String description, int anvilCost, - HolderSet exclusiveSet, + GeyserHolderSet exclusiveSet, @Nullable BedrockEnchantment bedrockEnchantment) { public static Enchantment read(RegistryEntryContext context) { NbtMap data = context.data(); Set effects = readEnchantmentComponents(data.getCompound("effects")); - HolderSet supportedItems = readHolderSet(data.get("supported_items"), itemId -> Registries.JAVA_ITEM_IDENTIFIERS.getOrDefault(itemId.asString(), Items.AIR).javaId()); + GeyserHolderSet supportedItems = GeyserHolderSet.readHolderSet(context.session(), JavaRegistries.ITEM, data.get("supported_items"), itemId -> Registries.JAVA_ITEM_IDENTIFIERS.getOrDefault(itemId.asString(), Items.AIR).javaId()); int maxLevel = data.getInt("max_level"); int anvilCost = data.getInt("anvil_cost"); - HolderSet exclusiveSet = readHolderSet(data.getOrDefault("exclusive_set", null), context::getNetworkId); + GeyserHolderSet exclusiveSet = GeyserHolderSet.readHolderSet(context.session(), JavaRegistries.ENCHANTMENT, data.get("exclusive_set"), context::getNetworkId); BedrockEnchantment bedrockEnchantment = BedrockEnchantment.getByJavaIdentifier(context.id().asString()); - // TODO - description is a component. So if a hardcoded literal string is given, this will display normally on Java, - // but Geyser will attempt to lookup the literal string as translation - and will fail, displaying an empty string as enchantment name. String description = bedrockEnchantment == null ? MessageTranslator.deserializeDescription(context.session(), data) : null; return new Enchantment(context.id().asString(), effects, supportedItems, maxLevel, @@ -86,24 +81,4 @@ public record Enchantment(String identifier, } return Set.copyOf(components); // Also ensures any empty sets are consolidated } - - // TODO holder set util? - private static HolderSet readHolderSet(@Nullable Object holderSet, ToIntFunction keyIdMapping) { - if (holderSet == null) { - return new HolderSet(IntArrays.EMPTY_ARRAY); - } - - if (holderSet instanceof String stringTag) { - // Tag - if (stringTag.startsWith("#")) { - return new HolderSet(MinecraftKey.key(stringTag.substring(1))); // Remove '#' at beginning that indicates tag - } else { - return new HolderSet(new int[]{keyIdMapping.applyAsInt(MinecraftKey.key(stringTag))}); - } - } else if (holderSet instanceof List list) { - // Assume the list is a list of strings - return new HolderSet(list.stream().map(o -> (String) o).map(Key::key).mapToInt(keyIdMapping).toArray()); - } - throw new IllegalArgumentException("Holder set must either be a tag, a string ID or a list of string IDs"); - } } diff --git a/core/src/main/java/org/geysermc/geyser/item/type/ArrowItem.java b/core/src/main/java/org/geysermc/geyser/item/type/ArrowItem.java index 4e4f1830e..b2d3737d8 100644 --- a/core/src/main/java/org/geysermc/geyser/item/type/ArrowItem.java +++ b/core/src/main/java/org/geysermc/geyser/item/type/ArrowItem.java @@ -32,6 +32,7 @@ import org.geysermc.geyser.inventory.item.Potion; import org.geysermc.geyser.item.Items; import org.geysermc.geyser.registry.type.ItemMapping; import org.geysermc.geyser.registry.type.ItemMappings; +import org.geysermc.geyser.session.GeyserSession; import org.geysermc.mcprotocollib.protocol.data.game.item.component.DataComponentType; import org.geysermc.mcprotocollib.protocol.data.game.item.component.PotionContents; @@ -41,9 +42,9 @@ public class ArrowItem extends Item { } @Override - public @NonNull GeyserItemStack translateToJava(@NonNull ItemData itemData, @NonNull ItemMapping mapping, @NonNull ItemMappings mappings) { + public @NonNull GeyserItemStack translateToJava(GeyserSession session, @NonNull ItemData itemData, @NonNull ItemMapping mapping, @NonNull ItemMappings mappings) { Potion potion = Potion.getByTippedArrowDamage(itemData.getDamage()); - GeyserItemStack itemStack = super.translateToJava(itemData, mapping, mappings); + GeyserItemStack itemStack = super.translateToJava(session, itemData, mapping, mappings); if (potion != null) { itemStack = Items.TIPPED_ARROW.newItemStack(itemStack.getAmount(), itemStack.getComponents()); PotionContents contents = potion.toComponent(); diff --git a/core/src/main/java/org/geysermc/geyser/item/type/CompassItem.java b/core/src/main/java/org/geysermc/geyser/item/type/CompassItem.java index 712e75a23..1c0ec0d5f 100644 --- a/core/src/main/java/org/geysermc/geyser/item/type/CompassItem.java +++ b/core/src/main/java/org/geysermc/geyser/item/type/CompassItem.java @@ -43,11 +43,11 @@ public class CompassItem extends Item { } @Override - public ItemData.Builder translateToBedrock(int count, DataComponents components, ItemMapping mapping, ItemMappings mappings) { + public ItemData.Builder translateToBedrock(GeyserSession session, int count, DataComponents components, ItemMapping mapping, ItemMappings mappings) { if (isLodestoneCompass(components)) { - return super.translateToBedrock(count, components, mappings.getLodestoneCompass(), mappings); + return super.translateToBedrock(session, count, components, mappings.getLodestoneCompass(), mappings); } - return super.translateToBedrock(count, components, mapping, mappings); + return super.translateToBedrock(session, count, components, mapping, mappings); } @Override @@ -78,12 +78,12 @@ public class CompassItem extends Item { } @Override - public @NonNull GeyserItemStack translateToJava(@NonNull ItemData itemData, @NonNull ItemMapping mapping, @NonNull ItemMappings mappings) { + public @NonNull GeyserItemStack translateToJava(GeyserSession session, @NonNull ItemData itemData, @NonNull ItemMapping mapping, @NonNull ItemMappings mappings) { if (mapping.getBedrockIdentifier().equals("minecraft:lodestone_compass")) { // Revert the entry back to the compass mapping = mappings.getStoredItems().compass(); } - return super.translateToJava(itemData, mapping, mappings); + return super.translateToJava(session, itemData, mapping, mappings); } } diff --git a/core/src/main/java/org/geysermc/geyser/item/type/FilledMapItem.java b/core/src/main/java/org/geysermc/geyser/item/type/FilledMapItem.java index e571a796a..07a0ad133 100644 --- a/core/src/main/java/org/geysermc/geyser/item/type/FilledMapItem.java +++ b/core/src/main/java/org/geysermc/geyser/item/type/FilledMapItem.java @@ -28,6 +28,7 @@ package org.geysermc.geyser.item.type; import org.cloudburstmc.protocol.bedrock.data.inventory.ItemData; import org.geysermc.geyser.registry.type.ItemMapping; import org.geysermc.geyser.registry.type.ItemMappings; +import org.geysermc.geyser.session.GeyserSession; import org.geysermc.mcprotocollib.protocol.data.game.item.component.DataComponentType; import org.geysermc.mcprotocollib.protocol.data.game.item.component.DataComponents; @@ -37,8 +38,8 @@ public class FilledMapItem extends MapItem { } @Override - public ItemData.Builder translateToBedrock(int count, DataComponents components, ItemMapping mapping, ItemMappings mappings) { - ItemData.Builder builder = super.translateToBedrock(count, components, mapping, mappings); + public ItemData.Builder translateToBedrock(GeyserSession session, int count, DataComponents components, ItemMapping mapping, ItemMappings mappings) { + ItemData.Builder builder = super.translateToBedrock(session, count, components, mapping, mappings); if (components == null) { // This is a fallback for maps with no nbt (Change added back in June 2020; is it needed in 2023?) //return builder.tag(NbtMap.builder().putInt("map", 0).build()); TODO if this is *still* broken, let's move it to translateComponentsToBedrock diff --git a/core/src/main/java/org/geysermc/geyser/item/type/GoatHornItem.java b/core/src/main/java/org/geysermc/geyser/item/type/GoatHornItem.java index d0e85ec52..9af07a40e 100644 --- a/core/src/main/java/org/geysermc/geyser/item/type/GoatHornItem.java +++ b/core/src/main/java/org/geysermc/geyser/item/type/GoatHornItem.java @@ -28,8 +28,11 @@ package org.geysermc.geyser.item.type; import org.checkerframework.checker.nullness.qual.NonNull; import org.cloudburstmc.protocol.bedrock.data.inventory.ItemData; import org.geysermc.geyser.inventory.GeyserItemStack; +import org.geysermc.geyser.inventory.item.GeyserInstrument; import org.geysermc.geyser.registry.type.ItemMapping; import org.geysermc.geyser.registry.type.ItemMappings; +import org.geysermc.geyser.session.GeyserSession; +import org.geysermc.geyser.translator.item.BedrockItemBuilder; import org.geysermc.mcprotocollib.protocol.data.game.Holder; import org.geysermc.mcprotocollib.protocol.data.game.item.component.DataComponentType; import org.geysermc.mcprotocollib.protocol.data.game.item.component.DataComponents; @@ -41,24 +44,45 @@ public class GoatHornItem extends Item { } @Override - public ItemData.Builder translateToBedrock(int count, DataComponents components, ItemMapping mapping, ItemMappings mappings) { - ItemData.Builder builder = super.translateToBedrock(count, components, mapping, mappings); + public ItemData.Builder translateToBedrock(GeyserSession session, int count, DataComponents components, ItemMapping mapping, ItemMappings mappings) { + ItemData.Builder builder = super.translateToBedrock(session, count, components, mapping, mappings); if (components == null) { return builder; } - Holder instrument = components.get(DataComponentType.INSTRUMENT); - if (instrument != null && instrument.isId()) { - builder.damage(instrument.id()); + + Holder holder = components.get(DataComponentType.INSTRUMENT); + if (holder != null) { + GeyserInstrument instrument = GeyserInstrument.fromHolder(session, holder); + int bedrockId = instrument.bedrockId(); + if (bedrockId >= 0) { + builder.damage(bedrockId); + } } + return builder; } @Override - public @NonNull GeyserItemStack translateToJava(@NonNull ItemData itemData, @NonNull ItemMapping mapping, @NonNull ItemMappings mappings) { - GeyserItemStack itemStack = super.translateToJava(itemData, mapping, mappings); + public void translateComponentsToBedrock(@NonNull GeyserSession session, @NonNull DataComponents components, @NonNull BedrockItemBuilder builder) { + super.translateComponentsToBedrock(session, components, builder); + + Holder holder = components.get(DataComponentType.INSTRUMENT); + if (holder != null && components.get(DataComponentType.HIDE_TOOLTIP) == null + && components.get(DataComponentType.HIDE_ADDITIONAL_TOOLTIP) == null) { + GeyserInstrument instrument = GeyserInstrument.fromHolder(session, holder); + if (instrument.bedrockInstrument() == null) { + builder.getOrCreateLore().add(instrument.description()); + } + } + } + + @Override + public @NonNull GeyserItemStack translateToJava(GeyserSession session, @NonNull ItemData itemData, @NonNull ItemMapping mapping, @NonNull ItemMappings mappings) { + GeyserItemStack itemStack = super.translateToJava(session, itemData, mapping, mappings); int damage = itemData.getDamage(); - itemStack.getOrCreateComponents().put(DataComponentType.INSTRUMENT, Holder.ofId(damage)); + // This could cause an issue since -1 is returned for non-vanilla goat horns + itemStack.getOrCreateComponents().put(DataComponentType.INSTRUMENT, Holder.ofId(GeyserInstrument.bedrockIdToJava(session, damage))); return itemStack; } diff --git a/core/src/main/java/org/geysermc/geyser/item/type/Item.java b/core/src/main/java/org/geysermc/geyser/item/type/Item.java index a8a477025..249936e5a 100644 --- a/core/src/main/java/org/geysermc/geyser/item/type/Item.java +++ b/core/src/main/java/org/geysermc/geyser/item/type/Item.java @@ -115,7 +115,7 @@ public class Item { /* Translation methods to Bedrock and back */ - public ItemData.Builder translateToBedrock(int count, DataComponents components, ItemMapping mapping, ItemMappings mappings) { + public ItemData.Builder translateToBedrock(GeyserSession session, int count, DataComponents components, ItemMapping mapping, ItemMappings mappings) { if (this == Items.AIR || count <= 0) { // Return, essentially, air return ItemData.builder(); @@ -130,7 +130,7 @@ public class Item { return builder; } - public @NonNull GeyserItemStack translateToJava(@NonNull ItemData itemData, @NonNull ItemMapping mapping, @NonNull ItemMappings mappings) { + public @NonNull GeyserItemStack translateToJava(GeyserSession session, @NonNull ItemData itemData, @NonNull ItemMapping mapping, @NonNull ItemMappings mappings) { return GeyserItemStack.of(javaId, itemData.getCount()); } diff --git a/core/src/main/java/org/geysermc/geyser/item/type/LightItem.java b/core/src/main/java/org/geysermc/geyser/item/type/LightItem.java new file mode 100644 index 000000000..fa10b08b1 --- /dev/null +++ b/core/src/main/java/org/geysermc/geyser/item/type/LightItem.java @@ -0,0 +1,79 @@ +/* + * Copyright (c) 2024 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.item.type; + +import org.cloudburstmc.protocol.bedrock.data.definitions.ItemDefinition; +import org.cloudburstmc.protocol.bedrock.data.inventory.ItemData; +import org.geysermc.geyser.level.block.property.Properties; +import org.geysermc.geyser.level.block.type.Block; +import org.geysermc.geyser.registry.type.ItemMapping; +import org.geysermc.geyser.registry.type.ItemMappings; +import org.geysermc.geyser.session.GeyserSession; +import org.geysermc.mcprotocollib.protocol.data.game.item.component.BlockStateProperties; +import org.geysermc.mcprotocollib.protocol.data.game.item.component.DataComponentType; +import org.geysermc.mcprotocollib.protocol.data.game.item.component.DataComponents; + +public class LightItem extends BlockItem { + + public LightItem(Builder builder, Block block, Block... otherBlocks) { + super(builder, block, otherBlocks); + } + + @Override + public ItemData.Builder translateToBedrock(GeyserSession session, int count, DataComponents components, ItemMapping mapping, ItemMappings mappings) { + ItemMapping lightLevelMapping = getLightLevelMapping(components, mappings); + if (lightLevelMapping != null) { + return super.translateToBedrock(session, count, components, lightLevelMapping, mappings); + } + return super.translateToBedrock(session, count, components, mapping, mappings); + } + + @Override + public ItemMapping toBedrockDefinition(DataComponents components, ItemMappings mappings) { + ItemMapping lightLevelMapping = getLightLevelMapping(components, mappings); + if (lightLevelMapping != null) { + return lightLevelMapping; + } + return super.toBedrockDefinition(components, mappings); + } + + + private static ItemMapping getLightLevelMapping(DataComponents components, ItemMappings mappings) { + String lightLevel = "15"; + if (components != null) { + BlockStateProperties blockStateProperties = components.get(DataComponentType.BLOCK_STATE); + + if (blockStateProperties != null) { + lightLevel = blockStateProperties.getProperties().get(Properties.LEVEL.name()); + } + } + ItemDefinition definition = mappings.getDefinition("minecraft:light_block_" + lightLevel); + if (definition != null) { + return mappings.getLightBlocks().get(definition.getRuntimeId()); + } + return null; + } +} diff --git a/core/src/main/java/org/geysermc/geyser/item/type/OminousBottleItem.java b/core/src/main/java/org/geysermc/geyser/item/type/OminousBottleItem.java index 815f71419..92a8d726d 100644 --- a/core/src/main/java/org/geysermc/geyser/item/type/OminousBottleItem.java +++ b/core/src/main/java/org/geysermc/geyser/item/type/OminousBottleItem.java @@ -31,6 +31,7 @@ import org.cloudburstmc.protocol.bedrock.data.inventory.ItemData; import org.geysermc.geyser.inventory.GeyserItemStack; import org.geysermc.geyser.registry.type.ItemMapping; import org.geysermc.geyser.registry.type.ItemMappings; +import org.geysermc.geyser.session.GeyserSession; import org.geysermc.mcprotocollib.protocol.data.game.item.component.DataComponentType; import org.geysermc.mcprotocollib.protocol.data.game.item.component.DataComponents; @@ -40,8 +41,8 @@ public class OminousBottleItem extends Item { } @Override - public ItemData.Builder translateToBedrock(int count, @Nullable DataComponents components, ItemMapping mapping, ItemMappings mappings) { - var builder = super.translateToBedrock(count, components, mapping, mappings); + public ItemData.Builder translateToBedrock(GeyserSession session, int count, @Nullable DataComponents components, ItemMapping mapping, ItemMappings mappings) { + var builder = super.translateToBedrock(session, count, components, mapping, mappings); if (components == null) { // Level 1 ominous bottle is null components - Java 1.21. return builder; @@ -54,9 +55,9 @@ public class OminousBottleItem extends Item { } @Override - public @NonNull GeyserItemStack translateToJava(@NonNull ItemData itemData, @NonNull ItemMapping mapping, @NonNull ItemMappings mappings) { + public @NonNull GeyserItemStack translateToJava(GeyserSession session, @NonNull ItemData itemData, @NonNull ItemMapping mapping, @NonNull ItemMappings mappings) { // This item can be pulled from the creative inventory with amplifiers. - GeyserItemStack itemStack = super.translateToJava(itemData, mapping, mappings); + GeyserItemStack itemStack = super.translateToJava(session, itemData, mapping, mappings); int damage = itemData.getDamage(); if (damage == 0) { return itemStack; diff --git a/core/src/main/java/org/geysermc/geyser/item/type/PotionItem.java b/core/src/main/java/org/geysermc/geyser/item/type/PotionItem.java index f8fe2b4ee..89e60b325 100644 --- a/core/src/main/java/org/geysermc/geyser/item/type/PotionItem.java +++ b/core/src/main/java/org/geysermc/geyser/item/type/PotionItem.java @@ -33,6 +33,7 @@ import org.geysermc.geyser.inventory.GeyserItemStack; import org.geysermc.geyser.inventory.item.Potion; import org.geysermc.geyser.registry.type.ItemMapping; import org.geysermc.geyser.registry.type.ItemMappings; +import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.translator.item.CustomItemTranslator; import org.geysermc.mcprotocollib.protocol.data.game.item.component.DataComponentType; import org.geysermc.mcprotocollib.protocol.data.game.item.component.DataComponents; @@ -44,8 +45,8 @@ public class PotionItem extends Item { } @Override - public ItemData.Builder translateToBedrock(int count, DataComponents components, ItemMapping mapping, ItemMappings mappings) { - if (components == null) return super.translateToBedrock(count, components, mapping, mappings); + public ItemData.Builder translateToBedrock(GeyserSession session, int count, DataComponents components, ItemMapping mapping, ItemMappings mappings) { + if (components == null) return super.translateToBedrock(session, count, components, mapping, mappings); PotionContents potionContents = components.get(DataComponentType.POTION_CONTENTS); if (potionContents != null) { ItemDefinition customItemDefinition = CustomItemTranslator.getCustomItem(components, mapping); @@ -64,13 +65,13 @@ public class PotionItem extends Item { .count(count); } } - return super.translateToBedrock(count, components, mapping, mappings); + return super.translateToBedrock(session, count, components, mapping, mappings); } @Override - public @NonNull GeyserItemStack translateToJava(@NonNull ItemData itemData, @NonNull ItemMapping mapping, @NonNull ItemMappings mappings) { + public @NonNull GeyserItemStack translateToJava(GeyserSession session, @NonNull ItemData itemData, @NonNull ItemMapping mapping, @NonNull ItemMappings mappings) { Potion potion = Potion.getByBedrockId(itemData.getDamage()); - GeyserItemStack itemStack = super.translateToJava(itemData, mapping, mappings); + GeyserItemStack itemStack = super.translateToJava(session, itemData, mapping, mappings); if (potion != null) { itemStack.getOrCreateComponents().put(DataComponentType.POTION_CONTENTS, potion.toComponent()); } diff --git a/core/src/main/java/org/geysermc/geyser/item/type/TippedArrowItem.java b/core/src/main/java/org/geysermc/geyser/item/type/TippedArrowItem.java index d9e58eaf9..09e4ee21f 100644 --- a/core/src/main/java/org/geysermc/geyser/item/type/TippedArrowItem.java +++ b/core/src/main/java/org/geysermc/geyser/item/type/TippedArrowItem.java @@ -30,6 +30,7 @@ import org.geysermc.geyser.GeyserImpl; import org.geysermc.geyser.inventory.item.Potion; import org.geysermc.geyser.registry.type.ItemMapping; import org.geysermc.geyser.registry.type.ItemMappings; +import org.geysermc.geyser.session.GeyserSession; import org.geysermc.mcprotocollib.protocol.data.game.item.component.DataComponentType; import org.geysermc.mcprotocollib.protocol.data.game.item.component.DataComponents; import org.geysermc.mcprotocollib.protocol.data.game.item.component.PotionContents; @@ -40,7 +41,7 @@ public class TippedArrowItem extends ArrowItem { } @Override - public ItemData.Builder translateToBedrock(int count, DataComponents components, ItemMapping mapping, ItemMappings mappings) { + public ItemData.Builder translateToBedrock(GeyserSession session, int count, DataComponents components, ItemMapping mapping, ItemMappings mappings) { if (components != null) { PotionContents potionContents = components.get(DataComponentType.POTION_CONTENTS); if (potionContents != null) { @@ -54,6 +55,6 @@ public class TippedArrowItem extends ArrowItem { GeyserImpl.getInstance().getLogger().debug("Unknown Java potion (tipped arrow): " + potionContents.getPotionId()); } } - return super.translateToBedrock(count, components, mapping, mappings); + return super.translateToBedrock(session, count, components, mapping, mappings); } } diff --git a/core/src/main/java/org/geysermc/geyser/level/BedrockDimension.java b/core/src/main/java/org/geysermc/geyser/level/BedrockDimension.java index 250c0f7a4..d62a17232 100644 --- a/core/src/main/java/org/geysermc/geyser/level/BedrockDimension.java +++ b/core/src/main/java/org/geysermc/geyser/level/BedrockDimension.java @@ -25,17 +25,84 @@ package org.geysermc.geyser.level; +import lombok.ToString; + /** * A data structure to represent what Bedrock believes are the height requirements for a specific dimension. * As of 1.18.30, biome count is representative of the height of the world, and out-of-bounds chunks can crash * the client. - * - * @param minY The minimum height Bedrock Edition will accept. - * @param height The maximum chunk height Bedrock Edition will accept, from the lowest point to the highest. - * @param doUpperHeightWarn whether to warn in the console if the Java dimension height exceeds Bedrock's. */ -public record BedrockDimension(int minY, int height, boolean doUpperHeightWarn) { - public static final BedrockDimension OVERWORLD = new BedrockDimension(-64, 384, true); - public static final BedrockDimension THE_NETHER = new BedrockDimension(0, 128, false); - public static final BedrockDimension THE_END = new BedrockDimension(0, 256, true); +@ToString +public class BedrockDimension { + + public static final int OVERWORLD_ID = 0; + public static final int DEFAULT_NETHER_ID = 1; + public static final int END_ID = 2; + + // Changes if the above-bedrock Nether building workaround is applied + public static int BEDROCK_NETHER_ID = DEFAULT_NETHER_ID; + + public static final BedrockDimension OVERWORLD = new BedrockDimension(-64, 384, true, OVERWORLD_ID); + public static final BedrockDimension THE_NETHER = new BedrockDimension(0, 128, false, -1) { + @Override + public int bedrockId() { + return BEDROCK_NETHER_ID; + } + }; + public static final BedrockDimension THE_END = new BedrockDimension(0, 256, true, END_ID); + public static final String NETHER_IDENTIFIER = "minecraft:the_nether"; + + private final int minY; + private final int height; + private final boolean doUpperHeightWarn; + private final int bedrockId; + + /** + * @param minY The minimum height Bedrock Edition will accept. + * @param height The maximum chunk height Bedrock Edition will accept, from the lowest point to the highest. + * @param doUpperHeightWarn whether to warn in the console if the Java dimension height exceeds Bedrock's. + * @param bedrockId the Bedrock dimension ID of this dimension. + */ + public BedrockDimension(int minY, int height, boolean doUpperHeightWarn, int bedrockId) { + this.minY = minY; + this.height = height; + this.doUpperHeightWarn = doUpperHeightWarn; + this.bedrockId = bedrockId; + } + + /** + * The Nether dimension in Bedrock does not permit building above Y128 - the Bedrock above the dimension. + * This workaround sets the Nether as the End dimension to ignore this limit. + * + * @param isAboveNetherBedrockBuilding true if we should apply The End workaround + */ + public static void changeBedrockNetherId(boolean isAboveNetherBedrockBuilding) { + // Change dimension ID to the End to allow for building above Bedrock + BEDROCK_NETHER_ID = isAboveNetherBedrockBuilding ? END_ID : DEFAULT_NETHER_ID; + } + + public static boolean isCustomBedrockNetherId() { + return BEDROCK_NETHER_ID == END_ID; + } + + public int maxY() { + return minY + height; + } + + public int minY() { + return minY; + } + + public int height() { + return height; + } + + public boolean doUpperHeightWarn() { + return doUpperHeightWarn; + } + + public int bedrockId() { + return bedrockId; + } + } diff --git a/core/src/main/java/org/geysermc/geyser/level/JavaDimension.java b/core/src/main/java/org/geysermc/geyser/level/JavaDimension.java index 50589851b..c4592517c 100644 --- a/core/src/main/java/org/geysermc/geyser/level/JavaDimension.java +++ b/core/src/main/java/org/geysermc/geyser/level/JavaDimension.java @@ -63,12 +63,19 @@ public record JavaDimension(int minY, int maxY, boolean piglinSafe, boolean ultr if ("minecraft".equals(id.namespace())) { String identifier = id.asString(); bedrockId = DimensionUtils.javaToBedrock(identifier); - isNetherLike = DimensionUtils.NETHER_IDENTIFIER.equals(identifier); + isNetherLike = BedrockDimension.NETHER_IDENTIFIER.equals(identifier); } else { // Effects should give is a clue on how this (custom) dimension is supposed to look like String effects = dimension.getString("effects"); bedrockId = DimensionUtils.javaToBedrock(effects); - isNetherLike = DimensionUtils.NETHER_IDENTIFIER.equals(effects); + isNetherLike = BedrockDimension.NETHER_IDENTIFIER.equals(effects); + } + + if (minY % 16 != 0) { + throw new RuntimeException("Minimum Y must be a multiple of 16!"); + } + if (maxY % 16 != 0) { + throw new RuntimeException("Maximum Y must be a multiple of 16!"); } return new JavaDimension(minY, maxY, piglinSafe, ultrawarm, coordinateScale, bedrockId, isNetherLike); diff --git a/core/src/main/java/org/geysermc/geyser/level/JukeboxSong.java b/core/src/main/java/org/geysermc/geyser/level/JukeboxSong.java index 86d66e209..1bed4099a 100644 --- a/core/src/main/java/org/geysermc/geyser/level/JukeboxSong.java +++ b/core/src/main/java/org/geysermc/geyser/level/JukeboxSong.java @@ -26,24 +26,15 @@ package org.geysermc.geyser.level; import org.cloudburstmc.nbt.NbtMap; -import org.geysermc.geyser.GeyserImpl; import org.geysermc.geyser.session.cache.registry.RegistryEntryContext; import org.geysermc.geyser.translator.text.MessageTranslator; +import org.geysermc.geyser.util.SoundUtils; public record JukeboxSong(String soundEvent, String description) { public static JukeboxSong read(RegistryEntryContext context) { NbtMap data = context.data(); - Object soundEventObject = data.get("sound_event"); - String soundEvent; - if (soundEventObject instanceof NbtMap map) { - soundEvent = map.getString("sound_id"); - } else if (soundEventObject instanceof String string) { - soundEvent = string; - } else { - soundEvent = ""; - GeyserImpl.getInstance().getLogger().debug("Sound event for " + context.id() + " was of an unexpected type! Expected string or NBT map, got " + soundEventObject); - } + String soundEvent = SoundUtils.readSoundEvent(data, "jukebox song " + context.id()); String description = MessageTranslator.deserializeDescription(context.session(), data); return new JukeboxSong(soundEvent, description); } diff --git a/core/src/main/java/org/geysermc/geyser/level/block/Blocks.java b/core/src/main/java/org/geysermc/geyser/level/block/Blocks.java index 735c1f6c4..5db38c559 100644 --- a/core/src/main/java/org/geysermc/geyser/level/block/Blocks.java +++ b/core/src/main/java/org/geysermc/geyser/level/block/Blocks.java @@ -49,11 +49,11 @@ public final class Blocks { public static final Block ANDESITE = register(new Block("andesite", builder().requiresCorrectToolForDrops().destroyTime(1.5f))); public static final Block POLISHED_ANDESITE = register(new Block("polished_andesite", builder().requiresCorrectToolForDrops().destroyTime(1.5f))); public static final Block GRASS_BLOCK = register(new Block("grass_block", builder().destroyTime(0.6f) - .booleanState(SNOWY))); + .booleanState(SNOWY))); public static final Block DIRT = register(new Block("dirt", builder().destroyTime(0.5f))); public static final Block COARSE_DIRT = register(new Block("coarse_dirt", builder().destroyTime(0.5f))); public static final Block PODZOL = register(new Block("podzol", builder().destroyTime(0.5f) - .booleanState(SNOWY))); + .booleanState(SNOWY))); public static final Block COBBLESTONE = register(new Block("cobblestone", builder().requiresCorrectToolForDrops().destroyTime(2.0f))); public static final Block OAK_PLANKS = register(new Block("oak_planks", builder().destroyTime(2.0f))); public static final Block SPRUCE_PLANKS = register(new Block("spruce_planks", builder().destroyTime(2.0f))); @@ -62,40 +62,45 @@ public final class Blocks { public static final Block ACACIA_PLANKS = register(new Block("acacia_planks", builder().destroyTime(2.0f))); public static final Block CHERRY_PLANKS = register(new Block("cherry_planks", builder().destroyTime(2.0f))); public static final Block DARK_OAK_PLANKS = register(new Block("dark_oak_planks", builder().destroyTime(2.0f))); + public static final Block PALE_OAK_WOOD = register(new Block("pale_oak_wood", builder().destroyTime(2.0f) + .enumState(AXIS, Axis.VALUES))); + public static final Block PALE_OAK_PLANKS = register(new Block("pale_oak_planks", builder().destroyTime(2.0f))); public static final Block MANGROVE_PLANKS = register(new Block("mangrove_planks", builder().destroyTime(2.0f))); public static final Block BAMBOO_PLANKS = register(new Block("bamboo_planks", builder().destroyTime(2.0f))); public static final Block BAMBOO_MOSAIC = register(new Block("bamboo_mosaic", builder().destroyTime(2.0f))); public static final Block OAK_SAPLING = register(new Block("oak_sapling", builder().pushReaction(PistonBehavior.DESTROY) - .intState(STAGE))); + .intState(STAGE))); public static final Block SPRUCE_SAPLING = register(new Block("spruce_sapling", builder().pushReaction(PistonBehavior.DESTROY) - .intState(STAGE))); + .intState(STAGE))); public static final Block BIRCH_SAPLING = register(new Block("birch_sapling", builder().pushReaction(PistonBehavior.DESTROY) - .intState(STAGE))); + .intState(STAGE))); public static final Block JUNGLE_SAPLING = register(new Block("jungle_sapling", builder().pushReaction(PistonBehavior.DESTROY) - .intState(STAGE))); + .intState(STAGE))); public static final Block ACACIA_SAPLING = register(new Block("acacia_sapling", builder().pushReaction(PistonBehavior.DESTROY) - .intState(STAGE))); + .intState(STAGE))); public static final Block CHERRY_SAPLING = register(new Block("cherry_sapling", builder().pushReaction(PistonBehavior.DESTROY) - .intState(STAGE))); + .intState(STAGE))); public static final Block DARK_OAK_SAPLING = register(new Block("dark_oak_sapling", builder().pushReaction(PistonBehavior.DESTROY) - .intState(STAGE))); + .intState(STAGE))); + public static final Block PALE_OAK_SAPLING = register(new Block("pale_oak_sapling", builder().pushReaction(PistonBehavior.DESTROY) + .intState(STAGE))); public static final Block MANGROVE_PROPAGULE = register(new Block("mangrove_propagule", builder().pushReaction(PistonBehavior.DESTROY) - .intState(AGE_4) - .booleanState(HANGING) - .intState(STAGE) - .booleanState(WATERLOGGED))); + .intState(AGE_4) + .booleanState(HANGING) + .intState(STAGE) + .booleanState(WATERLOGGED))); public static final Block BEDROCK = register(new Block("bedrock", builder().destroyTime(-1.0f))); public static final Block WATER = register(new WaterBlock("water", builder().destroyTime(100.0f).pushReaction(PistonBehavior.DESTROY) - .intState(LEVEL))); + .intState(LEVEL))); public static final Block LAVA = register(new Block("lava", builder().destroyTime(100.0f).pushReaction(PistonBehavior.DESTROY) - .intState(LEVEL))); + .intState(LEVEL))); public static final Block SAND = register(new Block("sand", builder().destroyTime(0.5f))); public static final Block SUSPICIOUS_SAND = register(new Block("suspicious_sand", builder().setBlockEntity(BlockEntityType.BRUSHABLE_BLOCK).destroyTime(0.25f).pushReaction(PistonBehavior.DESTROY) - .intState(DUSTED))); + .intState(DUSTED))); public static final Block RED_SAND = register(new Block("red_sand", builder().destroyTime(0.5f))); public static final Block GRAVEL = register(new Block("gravel", builder().destroyTime(0.6f))); public static final Block SUSPICIOUS_GRAVEL = register(new Block("suspicious_gravel", builder().setBlockEntity(BlockEntityType.BRUSHABLE_BLOCK).destroyTime(0.25f).pushReaction(PistonBehavior.DESTROY) - .intState(DUSTED))); + .intState(DUSTED))); public static final Block GOLD_ORE = register(new Block("gold_ore", builder().requiresCorrectToolForDrops().destroyTime(3.0f))); public static final Block DEEPSLATE_GOLD_ORE = register(new Block("deepslate_gold_ore", builder().requiresCorrectToolForDrops().destroyTime(4.5f))); public static final Block IRON_ORE = register(new Block("iron_ore", builder().requiresCorrectToolForDrops().destroyTime(3.0f))); @@ -104,117 +109,127 @@ public final class Blocks { public static final Block DEEPSLATE_COAL_ORE = register(new Block("deepslate_coal_ore", builder().requiresCorrectToolForDrops().destroyTime(4.5f))); public static final Block NETHER_GOLD_ORE = register(new Block("nether_gold_ore", builder().requiresCorrectToolForDrops().destroyTime(3.0f))); public static final Block OAK_LOG = register(new Block("oak_log", builder().destroyTime(2.0f) - .enumState(AXIS, Axis.VALUES))); + .enumState(AXIS, Axis.VALUES))); public static final Block SPRUCE_LOG = register(new Block("spruce_log", builder().destroyTime(2.0f) - .enumState(AXIS, Axis.VALUES))); + .enumState(AXIS, Axis.VALUES))); public static final Block BIRCH_LOG = register(new Block("birch_log", builder().destroyTime(2.0f) - .enumState(AXIS, Axis.VALUES))); + .enumState(AXIS, Axis.VALUES))); public static final Block JUNGLE_LOG = register(new Block("jungle_log", builder().destroyTime(2.0f) - .enumState(AXIS, Axis.VALUES))); + .enumState(AXIS, Axis.VALUES))); public static final Block ACACIA_LOG = register(new Block("acacia_log", builder().destroyTime(2.0f) - .enumState(AXIS, Axis.VALUES))); + .enumState(AXIS, Axis.VALUES))); public static final Block CHERRY_LOG = register(new Block("cherry_log", builder().destroyTime(2.0f) - .enumState(AXIS, Axis.VALUES))); + .enumState(AXIS, Axis.VALUES))); public static final Block DARK_OAK_LOG = register(new Block("dark_oak_log", builder().destroyTime(2.0f) - .enumState(AXIS, Axis.VALUES))); + .enumState(AXIS, Axis.VALUES))); + public static final Block PALE_OAK_LOG = register(new Block("pale_oak_log", builder().destroyTime(2.0f) + .enumState(AXIS, Axis.VALUES))); public static final Block MANGROVE_LOG = register(new Block("mangrove_log", builder().destroyTime(2.0f) - .enumState(AXIS, Axis.VALUES))); + .enumState(AXIS, Axis.VALUES))); public static final Block MANGROVE_ROOTS = register(new Block("mangrove_roots", builder().destroyTime(0.7f) - .booleanState(WATERLOGGED))); + .booleanState(WATERLOGGED))); public static final Block MUDDY_MANGROVE_ROOTS = register(new Block("muddy_mangrove_roots", builder().destroyTime(0.7f) - .enumState(AXIS, Axis.VALUES))); + .enumState(AXIS, Axis.VALUES))); public static final Block BAMBOO_BLOCK = register(new Block("bamboo_block", builder().destroyTime(2.0f) - .enumState(AXIS, Axis.VALUES))); + .enumState(AXIS, Axis.VALUES))); public static final Block STRIPPED_SPRUCE_LOG = register(new Block("stripped_spruce_log", builder().destroyTime(2.0f) - .enumState(AXIS, Axis.VALUES))); + .enumState(AXIS, Axis.VALUES))); public static final Block STRIPPED_BIRCH_LOG = register(new Block("stripped_birch_log", builder().destroyTime(2.0f) - .enumState(AXIS, Axis.VALUES))); + .enumState(AXIS, Axis.VALUES))); public static final Block STRIPPED_JUNGLE_LOG = register(new Block("stripped_jungle_log", builder().destroyTime(2.0f) - .enumState(AXIS, Axis.VALUES))); + .enumState(AXIS, Axis.VALUES))); public static final Block STRIPPED_ACACIA_LOG = register(new Block("stripped_acacia_log", builder().destroyTime(2.0f) - .enumState(AXIS, Axis.VALUES))); + .enumState(AXIS, Axis.VALUES))); public static final Block STRIPPED_CHERRY_LOG = register(new Block("stripped_cherry_log", builder().destroyTime(2.0f) - .enumState(AXIS, Axis.VALUES))); + .enumState(AXIS, Axis.VALUES))); public static final Block STRIPPED_DARK_OAK_LOG = register(new Block("stripped_dark_oak_log", builder().destroyTime(2.0f) - .enumState(AXIS, Axis.VALUES))); + .enumState(AXIS, Axis.VALUES))); + public static final Block STRIPPED_PALE_OAK_LOG = register(new Block("stripped_pale_oak_log", builder().destroyTime(2.0f) + .enumState(AXIS, Axis.VALUES))); public static final Block STRIPPED_OAK_LOG = register(new Block("stripped_oak_log", builder().destroyTime(2.0f) - .enumState(AXIS, Axis.VALUES))); + .enumState(AXIS, Axis.VALUES))); public static final Block STRIPPED_MANGROVE_LOG = register(new Block("stripped_mangrove_log", builder().destroyTime(2.0f) - .enumState(AXIS, Axis.VALUES))); + .enumState(AXIS, Axis.VALUES))); public static final Block STRIPPED_BAMBOO_BLOCK = register(new Block("stripped_bamboo_block", builder().destroyTime(2.0f) - .enumState(AXIS, Axis.VALUES))); + .enumState(AXIS, Axis.VALUES))); public static final Block OAK_WOOD = register(new Block("oak_wood", builder().destroyTime(2.0f) - .enumState(AXIS, Axis.VALUES))); + .enumState(AXIS, Axis.VALUES))); public static final Block SPRUCE_WOOD = register(new Block("spruce_wood", builder().destroyTime(2.0f) - .enumState(AXIS, Axis.VALUES))); + .enumState(AXIS, Axis.VALUES))); public static final Block BIRCH_WOOD = register(new Block("birch_wood", builder().destroyTime(2.0f) - .enumState(AXIS, Axis.VALUES))); + .enumState(AXIS, Axis.VALUES))); public static final Block JUNGLE_WOOD = register(new Block("jungle_wood", builder().destroyTime(2.0f) - .enumState(AXIS, Axis.VALUES))); + .enumState(AXIS, Axis.VALUES))); public static final Block ACACIA_WOOD = register(new Block("acacia_wood", builder().destroyTime(2.0f) - .enumState(AXIS, Axis.VALUES))); + .enumState(AXIS, Axis.VALUES))); public static final Block CHERRY_WOOD = register(new Block("cherry_wood", builder().destroyTime(2.0f) - .enumState(AXIS, Axis.VALUES))); + .enumState(AXIS, Axis.VALUES))); public static final Block DARK_OAK_WOOD = register(new Block("dark_oak_wood", builder().destroyTime(2.0f) - .enumState(AXIS, Axis.VALUES))); + .enumState(AXIS, Axis.VALUES))); public static final Block MANGROVE_WOOD = register(new Block("mangrove_wood", builder().destroyTime(2.0f) - .enumState(AXIS, Axis.VALUES))); + .enumState(AXIS, Axis.VALUES))); public static final Block STRIPPED_OAK_WOOD = register(new Block("stripped_oak_wood", builder().destroyTime(2.0f) - .enumState(AXIS, Axis.VALUES))); + .enumState(AXIS, Axis.VALUES))); public static final Block STRIPPED_SPRUCE_WOOD = register(new Block("stripped_spruce_wood", builder().destroyTime(2.0f) - .enumState(AXIS, Axis.VALUES))); + .enumState(AXIS, Axis.VALUES))); public static final Block STRIPPED_BIRCH_WOOD = register(new Block("stripped_birch_wood", builder().destroyTime(2.0f) - .enumState(AXIS, Axis.VALUES))); + .enumState(AXIS, Axis.VALUES))); public static final Block STRIPPED_JUNGLE_WOOD = register(new Block("stripped_jungle_wood", builder().destroyTime(2.0f) - .enumState(AXIS, Axis.VALUES))); + .enumState(AXIS, Axis.VALUES))); public static final Block STRIPPED_ACACIA_WOOD = register(new Block("stripped_acacia_wood", builder().destroyTime(2.0f) - .enumState(AXIS, Axis.VALUES))); + .enumState(AXIS, Axis.VALUES))); public static final Block STRIPPED_CHERRY_WOOD = register(new Block("stripped_cherry_wood", builder().destroyTime(2.0f) - .enumState(AXIS, Axis.VALUES))); + .enumState(AXIS, Axis.VALUES))); public static final Block STRIPPED_DARK_OAK_WOOD = register(new Block("stripped_dark_oak_wood", builder().destroyTime(2.0f) - .enumState(AXIS, Axis.VALUES))); + .enumState(AXIS, Axis.VALUES))); + public static final Block STRIPPED_PALE_OAK_WOOD = register(new Block("stripped_pale_oak_wood", builder().destroyTime(2.0f) + .enumState(AXIS, Axis.VALUES))); public static final Block STRIPPED_MANGROVE_WOOD = register(new Block("stripped_mangrove_wood", builder().destroyTime(2.0f) - .enumState(AXIS, Axis.VALUES))); + .enumState(AXIS, Axis.VALUES))); public static final Block OAK_LEAVES = register(new Block("oak_leaves", builder().destroyTime(0.2f).pushReaction(PistonBehavior.DESTROY) - .intState(DISTANCE) - .booleanState(PERSISTENT) - .booleanState(WATERLOGGED))); + .intState(DISTANCE) + .booleanState(PERSISTENT) + .booleanState(WATERLOGGED))); public static final Block SPRUCE_LEAVES = register(new Block("spruce_leaves", builder().destroyTime(0.2f).pushReaction(PistonBehavior.DESTROY) - .intState(DISTANCE) - .booleanState(PERSISTENT) - .booleanState(WATERLOGGED))); + .intState(DISTANCE) + .booleanState(PERSISTENT) + .booleanState(WATERLOGGED))); public static final Block BIRCH_LEAVES = register(new Block("birch_leaves", builder().destroyTime(0.2f).pushReaction(PistonBehavior.DESTROY) - .intState(DISTANCE) - .booleanState(PERSISTENT) - .booleanState(WATERLOGGED))); + .intState(DISTANCE) + .booleanState(PERSISTENT) + .booleanState(WATERLOGGED))); public static final Block JUNGLE_LEAVES = register(new Block("jungle_leaves", builder().destroyTime(0.2f).pushReaction(PistonBehavior.DESTROY) - .intState(DISTANCE) - .booleanState(PERSISTENT) - .booleanState(WATERLOGGED))); + .intState(DISTANCE) + .booleanState(PERSISTENT) + .booleanState(WATERLOGGED))); public static final Block ACACIA_LEAVES = register(new Block("acacia_leaves", builder().destroyTime(0.2f).pushReaction(PistonBehavior.DESTROY) - .intState(DISTANCE) - .booleanState(PERSISTENT) - .booleanState(WATERLOGGED))); + .intState(DISTANCE) + .booleanState(PERSISTENT) + .booleanState(WATERLOGGED))); public static final Block CHERRY_LEAVES = register(new Block("cherry_leaves", builder().destroyTime(0.2f).pushReaction(PistonBehavior.DESTROY) - .intState(DISTANCE) - .booleanState(PERSISTENT) - .booleanState(WATERLOGGED))); + .intState(DISTANCE) + .booleanState(PERSISTENT) + .booleanState(WATERLOGGED))); public static final Block DARK_OAK_LEAVES = register(new Block("dark_oak_leaves", builder().destroyTime(0.2f).pushReaction(PistonBehavior.DESTROY) - .intState(DISTANCE) - .booleanState(PERSISTENT) - .booleanState(WATERLOGGED))); + .intState(DISTANCE) + .booleanState(PERSISTENT) + .booleanState(WATERLOGGED))); + public static final Block PALE_OAK_LEAVES = register(new Block("pale_oak_leaves", builder().destroyTime(0.2f).pushReaction(PistonBehavior.DESTROY) + .intState(DISTANCE) + .booleanState(PERSISTENT) + .booleanState(WATERLOGGED))); public static final Block MANGROVE_LEAVES = register(new Block("mangrove_leaves", builder().destroyTime(0.2f).pushReaction(PistonBehavior.DESTROY) - .intState(DISTANCE) - .booleanState(PERSISTENT) - .booleanState(WATERLOGGED))); + .intState(DISTANCE) + .booleanState(PERSISTENT) + .booleanState(WATERLOGGED))); public static final Block AZALEA_LEAVES = register(new Block("azalea_leaves", builder().destroyTime(0.2f).pushReaction(PistonBehavior.DESTROY) - .intState(DISTANCE) - .booleanState(PERSISTENT) - .booleanState(WATERLOGGED))); + .intState(DISTANCE) + .booleanState(PERSISTENT) + .booleanState(WATERLOGGED))); public static final Block FLOWERING_AZALEA_LEAVES = register(new Block("flowering_azalea_leaves", builder().destroyTime(0.2f).pushReaction(PistonBehavior.DESTROY) - .intState(DISTANCE) - .booleanState(PERSISTENT) - .booleanState(WATERLOGGED))); + .intState(DISTANCE) + .booleanState(PERSISTENT) + .booleanState(WATERLOGGED))); public static final Block SPONGE = register(new Block("sponge", builder().destroyTime(0.6f))); public static final Block WET_SPONGE = register(new Block("wet_sponge", builder().destroyTime(0.6f))); public static final Block GLASS = register(new Block("glass", builder().destroyTime(0.3f))); @@ -222,104 +237,104 @@ public final class Blocks { public static final Block DEEPSLATE_LAPIS_ORE = register(new Block("deepslate_lapis_ore", builder().requiresCorrectToolForDrops().destroyTime(4.5f))); public static final Block LAPIS_BLOCK = register(new Block("lapis_block", builder().requiresCorrectToolForDrops().destroyTime(3.0f))); public static final Block DISPENSER = register(new Block("dispenser", builder().setBlockEntity(BlockEntityType.DISPENSER).requiresCorrectToolForDrops().destroyTime(3.5f) - .enumState(FACING, Direction.NORTH, Direction.EAST, Direction.SOUTH, Direction.WEST, Direction.UP, Direction.DOWN) - .booleanState(TRIGGERED))); + .enumState(FACING, Direction.NORTH, Direction.EAST, Direction.SOUTH, Direction.WEST, Direction.UP, Direction.DOWN) + .booleanState(TRIGGERED))); public static final Block SANDSTONE = register(new Block("sandstone", builder().requiresCorrectToolForDrops().destroyTime(0.8f))); public static final Block CHISELED_SANDSTONE = register(new Block("chiseled_sandstone", builder().requiresCorrectToolForDrops().destroyTime(0.8f))); public static final Block CUT_SANDSTONE = register(new Block("cut_sandstone", builder().requiresCorrectToolForDrops().destroyTime(0.8f))); public static final Block NOTE_BLOCK = register(new Block("note_block", builder().destroyTime(0.8f) - .enumState(NOTEBLOCK_INSTRUMENT) - .intState(NOTE) - .booleanState(POWERED))); + .enumState(NOTEBLOCK_INSTRUMENT) + .intState(NOTE) + .booleanState(POWERED))); public static final Block WHITE_BED = register(new BedBlock("white_bed", 0, builder().setBlockEntity(BlockEntityType.BED).destroyTime(0.2f).pushReaction(PistonBehavior.DESTROY) - .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) - .booleanState(OCCUPIED) - .enumState(BED_PART))); + .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) + .booleanState(OCCUPIED) + .enumState(BED_PART))); public static final Block ORANGE_BED = register(new BedBlock("orange_bed", 1, builder().setBlockEntity(BlockEntityType.BED).destroyTime(0.2f).pushReaction(PistonBehavior.DESTROY) - .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) - .booleanState(OCCUPIED) - .enumState(BED_PART))); + .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) + .booleanState(OCCUPIED) + .enumState(BED_PART))); public static final Block MAGENTA_BED = register(new BedBlock("magenta_bed", 2, builder().setBlockEntity(BlockEntityType.BED).destroyTime(0.2f).pushReaction(PistonBehavior.DESTROY) - .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) - .booleanState(OCCUPIED) - .enumState(BED_PART))); + .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) + .booleanState(OCCUPIED) + .enumState(BED_PART))); public static final Block LIGHT_BLUE_BED = register(new BedBlock("light_blue_bed", 3, builder().setBlockEntity(BlockEntityType.BED).destroyTime(0.2f).pushReaction(PistonBehavior.DESTROY) - .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) - .booleanState(OCCUPIED) - .enumState(BED_PART))); + .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) + .booleanState(OCCUPIED) + .enumState(BED_PART))); public static final Block YELLOW_BED = register(new BedBlock("yellow_bed", 4, builder().setBlockEntity(BlockEntityType.BED).destroyTime(0.2f).pushReaction(PistonBehavior.DESTROY) - .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) - .booleanState(OCCUPIED) - .enumState(BED_PART))); + .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) + .booleanState(OCCUPIED) + .enumState(BED_PART))); public static final Block LIME_BED = register(new BedBlock("lime_bed", 5, builder().setBlockEntity(BlockEntityType.BED).destroyTime(0.2f).pushReaction(PistonBehavior.DESTROY) - .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) - .booleanState(OCCUPIED) - .enumState(BED_PART))); + .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) + .booleanState(OCCUPIED) + .enumState(BED_PART))); public static final Block PINK_BED = register(new BedBlock("pink_bed", 6, builder().setBlockEntity(BlockEntityType.BED).destroyTime(0.2f).pushReaction(PistonBehavior.DESTROY) - .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) - .booleanState(OCCUPIED) - .enumState(BED_PART))); + .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) + .booleanState(OCCUPIED) + .enumState(BED_PART))); public static final Block GRAY_BED = register(new BedBlock("gray_bed", 7, builder().setBlockEntity(BlockEntityType.BED).destroyTime(0.2f).pushReaction(PistonBehavior.DESTROY) - .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) - .booleanState(OCCUPIED) - .enumState(BED_PART))); + .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) + .booleanState(OCCUPIED) + .enumState(BED_PART))); public static final Block LIGHT_GRAY_BED = register(new BedBlock("light_gray_bed", 8, builder().setBlockEntity(BlockEntityType.BED).destroyTime(0.2f).pushReaction(PistonBehavior.DESTROY) - .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) - .booleanState(OCCUPIED) - .enumState(BED_PART))); + .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) + .booleanState(OCCUPIED) + .enumState(BED_PART))); public static final Block CYAN_BED = register(new BedBlock("cyan_bed", 9, builder().setBlockEntity(BlockEntityType.BED).destroyTime(0.2f).pushReaction(PistonBehavior.DESTROY) - .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) - .booleanState(OCCUPIED) - .enumState(BED_PART))); + .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) + .booleanState(OCCUPIED) + .enumState(BED_PART))); public static final Block PURPLE_BED = register(new BedBlock("purple_bed", 10, builder().setBlockEntity(BlockEntityType.BED).destroyTime(0.2f).pushReaction(PistonBehavior.DESTROY) - .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) - .booleanState(OCCUPIED) - .enumState(BED_PART))); + .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) + .booleanState(OCCUPIED) + .enumState(BED_PART))); public static final Block BLUE_BED = register(new BedBlock("blue_bed", 11, builder().setBlockEntity(BlockEntityType.BED).destroyTime(0.2f).pushReaction(PistonBehavior.DESTROY) - .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) - .booleanState(OCCUPIED) - .enumState(BED_PART))); + .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) + .booleanState(OCCUPIED) + .enumState(BED_PART))); public static final Block BROWN_BED = register(new BedBlock("brown_bed", 12, builder().setBlockEntity(BlockEntityType.BED).destroyTime(0.2f).pushReaction(PistonBehavior.DESTROY) - .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) - .booleanState(OCCUPIED) - .enumState(BED_PART))); + .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) + .booleanState(OCCUPIED) + .enumState(BED_PART))); public static final Block GREEN_BED = register(new BedBlock("green_bed", 13, builder().setBlockEntity(BlockEntityType.BED).destroyTime(0.2f).pushReaction(PistonBehavior.DESTROY) - .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) - .booleanState(OCCUPIED) - .enumState(BED_PART))); + .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) + .booleanState(OCCUPIED) + .enumState(BED_PART))); public static final Block RED_BED = register(new BedBlock("red_bed", 14, builder().setBlockEntity(BlockEntityType.BED).destroyTime(0.2f).pushReaction(PistonBehavior.DESTROY) - .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) - .booleanState(OCCUPIED) - .enumState(BED_PART))); + .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) + .booleanState(OCCUPIED) + .enumState(BED_PART))); public static final Block BLACK_BED = register(new BedBlock("black_bed", 15, builder().setBlockEntity(BlockEntityType.BED).destroyTime(0.2f).pushReaction(PistonBehavior.DESTROY) - .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) - .booleanState(OCCUPIED) - .enumState(BED_PART))); + .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) + .booleanState(OCCUPIED) + .enumState(BED_PART))); public static final Block POWERED_RAIL = register(new Block("powered_rail", builder().destroyTime(0.7f) - .booleanState(POWERED) - .enumState(RAIL_SHAPE_STRAIGHT) - .booleanState(WATERLOGGED))); + .booleanState(POWERED) + .enumState(RAIL_SHAPE_STRAIGHT) + .booleanState(WATERLOGGED))); public static final Block DETECTOR_RAIL = register(new Block("detector_rail", builder().destroyTime(0.7f) - .booleanState(POWERED) - .enumState(RAIL_SHAPE_STRAIGHT) - .booleanState(WATERLOGGED))); + .booleanState(POWERED) + .enumState(RAIL_SHAPE_STRAIGHT) + .booleanState(WATERLOGGED))); public static final Block STICKY_PISTON = register(new PistonBlock("sticky_piston", builder().destroyTime(1.5f).pushReaction(PistonBehavior.BLOCK) - .booleanState(EXTENDED) - .enumState(FACING, Direction.NORTH, Direction.EAST, Direction.SOUTH, Direction.WEST, Direction.UP, Direction.DOWN))); + .booleanState(EXTENDED) + .enumState(FACING, Direction.NORTH, Direction.EAST, Direction.SOUTH, Direction.WEST, Direction.UP, Direction.DOWN))); public static final Block COBWEB = register(new Block("cobweb", builder().requiresCorrectToolForDrops().destroyTime(4.0f).pushReaction(PistonBehavior.DESTROY))); public static final Block SHORT_GRASS = register(new Block("short_grass", builder().pushReaction(PistonBehavior.DESTROY))); public static final Block FERN = register(new Block("fern", builder().pushReaction(PistonBehavior.DESTROY))); public static final Block DEAD_BUSH = register(new Block("dead_bush", builder().pushReaction(PistonBehavior.DESTROY))); public static final Block SEAGRASS = register(new Block("seagrass", builder().pushReaction(PistonBehavior.DESTROY))); public static final Block TALL_SEAGRASS = register(new Block("tall_seagrass", builder().pushReaction(PistonBehavior.DESTROY).pickItem(() -> Items.SEAGRASS) - .enumState(DOUBLE_BLOCK_HALF))); + .enumState(DOUBLE_BLOCK_HALF))); public static final Block PISTON = register(new PistonBlock("piston", builder().destroyTime(1.5f).pushReaction(PistonBehavior.BLOCK) - .booleanState(EXTENDED) - .enumState(FACING, Direction.NORTH, Direction.EAST, Direction.SOUTH, Direction.WEST, Direction.UP, Direction.DOWN))); + .booleanState(EXTENDED) + .enumState(FACING, Direction.NORTH, Direction.EAST, Direction.SOUTH, Direction.WEST, Direction.UP, Direction.DOWN))); public static final Block PISTON_HEAD = register(new PistonHeadBlock("piston_head", builder().destroyTime(1.5f).pushReaction(PistonBehavior.BLOCK) - .enumState(FACING, Direction.NORTH, Direction.EAST, Direction.SOUTH, Direction.WEST, Direction.UP, Direction.DOWN) - .booleanState(SHORT) - .enumState(PISTON_TYPE))); + .enumState(FACING, Direction.NORTH, Direction.EAST, Direction.SOUTH, Direction.WEST, Direction.UP, Direction.DOWN) + .booleanState(SHORT) + .enumState(PISTON_TYPE))); public static final Block WHITE_WOOL = register(new Block("white_wool", builder().destroyTime(0.8f))); public static final Block ORANGE_WOOL = register(new Block("orange_wool", builder().destroyTime(0.8f))); public static final Block MAGENTA_WOOL = register(new Block("magenta_wool", builder().destroyTime(0.8f))); @@ -337,8 +352,8 @@ public final class Blocks { public static final Block RED_WOOL = register(new Block("red_wool", builder().destroyTime(0.8f))); public static final Block BLACK_WOOL = register(new Block("black_wool", builder().destroyTime(0.8f))); public static final Block MOVING_PISTON = register(new MovingPistonBlock("moving_piston", builder().setBlockEntity(BlockEntityType.PISTON).destroyTime(-1.0f).pushReaction(PistonBehavior.BLOCK) - .enumState(FACING, Direction.NORTH, Direction.EAST, Direction.SOUTH, Direction.WEST, Direction.UP, Direction.DOWN) - .enumState(PISTON_TYPE))); + .enumState(FACING, Direction.NORTH, Direction.EAST, Direction.SOUTH, Direction.WEST, Direction.UP, Direction.DOWN) + .enumState(PISTON_TYPE))); public static final Block DANDELION = register(new Block("dandelion", builder().pushReaction(PistonBehavior.DESTROY))); public static final Block TORCHFLOWER = register(new Block("torchflower", builder().pushReaction(PistonBehavior.DESTROY))); public static final Block POPPY = register(new Block("poppy", builder().pushReaction(PistonBehavior.DESTROY))); @@ -359,288 +374,306 @@ public final class Blocks { public static final Block IRON_BLOCK = register(new Block("iron_block", builder().requiresCorrectToolForDrops().destroyTime(5.0f))); public static final Block BRICKS = register(new Block("bricks", builder().requiresCorrectToolForDrops().destroyTime(2.0f))); public static final Block TNT = register(new Block("tnt", builder() - .booleanState(UNSTABLE))); + .booleanState(UNSTABLE))); public static final Block BOOKSHELF = register(new Block("bookshelf", builder().destroyTime(1.5f))); public static final Block CHISELED_BOOKSHELF = register(new Block("chiseled_bookshelf", builder().setBlockEntity(BlockEntityType.CHISELED_BOOKSHELF).destroyTime(1.5f) - .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) - .booleanState(CHISELED_BOOKSHELF_SLOT_0_OCCUPIED) - .booleanState(CHISELED_BOOKSHELF_SLOT_1_OCCUPIED) - .booleanState(CHISELED_BOOKSHELF_SLOT_2_OCCUPIED) - .booleanState(CHISELED_BOOKSHELF_SLOT_3_OCCUPIED) - .booleanState(CHISELED_BOOKSHELF_SLOT_4_OCCUPIED) - .booleanState(CHISELED_BOOKSHELF_SLOT_5_OCCUPIED))); + .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) + .booleanState(CHISELED_BOOKSHELF_SLOT_0_OCCUPIED) + .booleanState(CHISELED_BOOKSHELF_SLOT_1_OCCUPIED) + .booleanState(CHISELED_BOOKSHELF_SLOT_2_OCCUPIED) + .booleanState(CHISELED_BOOKSHELF_SLOT_3_OCCUPIED) + .booleanState(CHISELED_BOOKSHELF_SLOT_4_OCCUPIED) + .booleanState(CHISELED_BOOKSHELF_SLOT_5_OCCUPIED))); public static final Block MOSSY_COBBLESTONE = register(new Block("mossy_cobblestone", builder().requiresCorrectToolForDrops().destroyTime(2.0f))); public static final Block OBSIDIAN = register(new Block("obsidian", builder().requiresCorrectToolForDrops().destroyTime(50.0f))); public static final Block TORCH = register(new Block("torch", builder().pushReaction(PistonBehavior.DESTROY))); public static final Block WALL_TORCH = register(new Block("wall_torch", builder().pushReaction(PistonBehavior.DESTROY) - .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST))); + .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST))); public static final Block FIRE = register(new Block("fire", builder().pushReaction(PistonBehavior.DESTROY) - .intState(AGE_15) - .booleanState(EAST) - .booleanState(NORTH) - .booleanState(SOUTH) - .booleanState(UP) - .booleanState(WEST))); + .intState(AGE_15) + .booleanState(EAST) + .booleanState(NORTH) + .booleanState(SOUTH) + .booleanState(UP) + .booleanState(WEST))); public static final Block SOUL_FIRE = register(new Block("soul_fire", builder().pushReaction(PistonBehavior.DESTROY))); public static final Block SPAWNER = register(new SpawnerBlock("spawner", builder().setBlockEntity(BlockEntityType.MOB_SPAWNER).requiresCorrectToolForDrops().destroyTime(5.0f))); + public static final Block CREAKING_HEART = register(new Block("creaking_heart", builder().setBlockEntity(BlockEntityType.CREAKING_HEART).destroyTime(5.0f) + .enumState(AXIS, Axis.VALUES) + .enumState(CREAKING))); public static final Block OAK_STAIRS = register(new Block("oak_stairs", builder().destroyTime(2.0f) - .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) - .enumState(HALF) - .enumState(STAIRS_SHAPE) - .booleanState(WATERLOGGED))); + .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) + .enumState(HALF) + .enumState(STAIRS_SHAPE) + .booleanState(WATERLOGGED))); public static final Block CHEST = register(new ChestBlock("chest", builder().setBlockEntity(BlockEntityType.CHEST).destroyTime(2.5f) - .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) - .enumState(CHEST_TYPE, ChestType.VALUES) - .booleanState(WATERLOGGED))); + .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) + .enumState(CHEST_TYPE, ChestType.VALUES) + .booleanState(WATERLOGGED))); public static final Block REDSTONE_WIRE = register(new Block("redstone_wire", builder().pushReaction(PistonBehavior.DESTROY) - .enumState(EAST_REDSTONE) - .enumState(NORTH_REDSTONE) - .intState(POWER) - .enumState(SOUTH_REDSTONE) - .enumState(WEST_REDSTONE))); + .enumState(EAST_REDSTONE) + .enumState(NORTH_REDSTONE) + .intState(POWER) + .enumState(SOUTH_REDSTONE) + .enumState(WEST_REDSTONE))); public static final Block DIAMOND_ORE = register(new Block("diamond_ore", builder().requiresCorrectToolForDrops().destroyTime(3.0f))); public static final Block DEEPSLATE_DIAMOND_ORE = register(new Block("deepslate_diamond_ore", builder().requiresCorrectToolForDrops().destroyTime(4.5f))); public static final Block DIAMOND_BLOCK = register(new Block("diamond_block", builder().requiresCorrectToolForDrops().destroyTime(5.0f))); public static final Block CRAFTING_TABLE = register(new Block("crafting_table", builder().destroyTime(2.5f))); public static final Block WHEAT = register(new Block("wheat", builder().pushReaction(PistonBehavior.DESTROY) - .intState(AGE_7))); + .intState(AGE_7))); public static final Block FARMLAND = register(new Block("farmland", builder().destroyTime(0.6f) - .intState(MOISTURE))); + .intState(MOISTURE))); public static final Block FURNACE = register(new FurnaceBlock("furnace", builder().setBlockEntity(BlockEntityType.FURNACE).requiresCorrectToolForDrops().destroyTime(3.5f) - .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) - .booleanState(LIT))); + .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) + .booleanState(LIT))); public static final Block OAK_SIGN = register(new Block("oak_sign", builder().setBlockEntity(BlockEntityType.SIGN).destroyTime(1.0f) - .intState(ROTATION_16) - .booleanState(WATERLOGGED))); + .intState(ROTATION_16) + .booleanState(WATERLOGGED))); public static final Block SPRUCE_SIGN = register(new Block("spruce_sign", builder().setBlockEntity(BlockEntityType.SIGN).destroyTime(1.0f) - .intState(ROTATION_16) - .booleanState(WATERLOGGED))); + .intState(ROTATION_16) + .booleanState(WATERLOGGED))); public static final Block BIRCH_SIGN = register(new Block("birch_sign", builder().setBlockEntity(BlockEntityType.SIGN).destroyTime(1.0f) - .intState(ROTATION_16) - .booleanState(WATERLOGGED))); + .intState(ROTATION_16) + .booleanState(WATERLOGGED))); public static final Block ACACIA_SIGN = register(new Block("acacia_sign", builder().setBlockEntity(BlockEntityType.SIGN).destroyTime(1.0f) - .intState(ROTATION_16) - .booleanState(WATERLOGGED))); + .intState(ROTATION_16) + .booleanState(WATERLOGGED))); public static final Block CHERRY_SIGN = register(new Block("cherry_sign", builder().setBlockEntity(BlockEntityType.SIGN).destroyTime(1.0f) - .intState(ROTATION_16) - .booleanState(WATERLOGGED))); + .intState(ROTATION_16) + .booleanState(WATERLOGGED))); public static final Block JUNGLE_SIGN = register(new Block("jungle_sign", builder().setBlockEntity(BlockEntityType.SIGN).destroyTime(1.0f) - .intState(ROTATION_16) - .booleanState(WATERLOGGED))); + .intState(ROTATION_16) + .booleanState(WATERLOGGED))); public static final Block DARK_OAK_SIGN = register(new Block("dark_oak_sign", builder().setBlockEntity(BlockEntityType.SIGN).destroyTime(1.0f) - .intState(ROTATION_16) - .booleanState(WATERLOGGED))); + .intState(ROTATION_16) + .booleanState(WATERLOGGED))); + public static final Block PALE_OAK_SIGN = register(new Block("pale_oak_sign", builder().setBlockEntity(BlockEntityType.SIGN).destroyTime(1.0f) + .intState(ROTATION_16) + .booleanState(WATERLOGGED))); public static final Block MANGROVE_SIGN = register(new Block("mangrove_sign", builder().setBlockEntity(BlockEntityType.SIGN).destroyTime(1.0f) - .intState(ROTATION_16) - .booleanState(WATERLOGGED))); + .intState(ROTATION_16) + .booleanState(WATERLOGGED))); public static final Block BAMBOO_SIGN = register(new Block("bamboo_sign", builder().setBlockEntity(BlockEntityType.SIGN).destroyTime(1.0f) - .intState(ROTATION_16) - .booleanState(WATERLOGGED))); + .intState(ROTATION_16) + .booleanState(WATERLOGGED))); public static final Block OAK_DOOR = register(new DoorBlock("oak_door", builder().destroyTime(3.0f).pushReaction(PistonBehavior.DESTROY) - .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) - .enumState(DOUBLE_BLOCK_HALF) - .enumState(DOOR_HINGE) - .booleanState(OPEN) - .booleanState(POWERED))); + .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) + .enumState(DOUBLE_BLOCK_HALF) + .enumState(DOOR_HINGE) + .booleanState(OPEN) + .booleanState(POWERED))); public static final Block LADDER = register(new Block("ladder", builder().destroyTime(0.4f).pushReaction(PistonBehavior.DESTROY) - .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) - .booleanState(WATERLOGGED))); + .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) + .booleanState(WATERLOGGED))); public static final Block RAIL = register(new Block("rail", builder().destroyTime(0.7f) - .enumState(RAIL_SHAPE) - .booleanState(WATERLOGGED))); + .enumState(RAIL_SHAPE) + .booleanState(WATERLOGGED))); public static final Block COBBLESTONE_STAIRS = register(new Block("cobblestone_stairs", builder().requiresCorrectToolForDrops().destroyTime(2.0f) - .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) - .enumState(HALF) - .enumState(STAIRS_SHAPE) - .booleanState(WATERLOGGED))); + .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) + .enumState(HALF) + .enumState(STAIRS_SHAPE) + .booleanState(WATERLOGGED))); public static final Block OAK_WALL_SIGN = register(new Block("oak_wall_sign", builder().setBlockEntity(BlockEntityType.SIGN).destroyTime(1.0f) - .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) - .booleanState(WATERLOGGED))); + .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) + .booleanState(WATERLOGGED))); public static final Block SPRUCE_WALL_SIGN = register(new Block("spruce_wall_sign", builder().setBlockEntity(BlockEntityType.SIGN).destroyTime(1.0f) - .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) - .booleanState(WATERLOGGED))); + .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) + .booleanState(WATERLOGGED))); public static final Block BIRCH_WALL_SIGN = register(new Block("birch_wall_sign", builder().setBlockEntity(BlockEntityType.SIGN).destroyTime(1.0f) - .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) - .booleanState(WATERLOGGED))); + .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) + .booleanState(WATERLOGGED))); public static final Block ACACIA_WALL_SIGN = register(new Block("acacia_wall_sign", builder().setBlockEntity(BlockEntityType.SIGN).destroyTime(1.0f) - .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) - .booleanState(WATERLOGGED))); + .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) + .booleanState(WATERLOGGED))); public static final Block CHERRY_WALL_SIGN = register(new Block("cherry_wall_sign", builder().setBlockEntity(BlockEntityType.SIGN).destroyTime(1.0f) - .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) - .booleanState(WATERLOGGED))); + .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) + .booleanState(WATERLOGGED))); public static final Block JUNGLE_WALL_SIGN = register(new Block("jungle_wall_sign", builder().setBlockEntity(BlockEntityType.SIGN).destroyTime(1.0f) - .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) - .booleanState(WATERLOGGED))); + .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) + .booleanState(WATERLOGGED))); public static final Block DARK_OAK_WALL_SIGN = register(new Block("dark_oak_wall_sign", builder().setBlockEntity(BlockEntityType.SIGN).destroyTime(1.0f) - .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) - .booleanState(WATERLOGGED))); + .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) + .booleanState(WATERLOGGED))); + public static final Block PALE_OAK_WALL_SIGN = register(new Block("pale_oak_wall_sign", builder().setBlockEntity(BlockEntityType.SIGN).destroyTime(1.0f) + .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) + .booleanState(WATERLOGGED))); public static final Block MANGROVE_WALL_SIGN = register(new Block("mangrove_wall_sign", builder().setBlockEntity(BlockEntityType.SIGN).destroyTime(1.0f) - .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) - .booleanState(WATERLOGGED))); + .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) + .booleanState(WATERLOGGED))); public static final Block BAMBOO_WALL_SIGN = register(new Block("bamboo_wall_sign", builder().setBlockEntity(BlockEntityType.SIGN).destroyTime(1.0f) - .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) - .booleanState(WATERLOGGED))); + .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) + .booleanState(WATERLOGGED))); public static final Block OAK_HANGING_SIGN = register(new Block("oak_hanging_sign", builder().setBlockEntity(BlockEntityType.HANGING_SIGN).destroyTime(1.0f) - .booleanState(ATTACHED) - .intState(ROTATION_16) - .booleanState(WATERLOGGED))); + .booleanState(ATTACHED) + .intState(ROTATION_16) + .booleanState(WATERLOGGED))); public static final Block SPRUCE_HANGING_SIGN = register(new Block("spruce_hanging_sign", builder().setBlockEntity(BlockEntityType.HANGING_SIGN).destroyTime(1.0f) - .booleanState(ATTACHED) - .intState(ROTATION_16) - .booleanState(WATERLOGGED))); + .booleanState(ATTACHED) + .intState(ROTATION_16) + .booleanState(WATERLOGGED))); public static final Block BIRCH_HANGING_SIGN = register(new Block("birch_hanging_sign", builder().setBlockEntity(BlockEntityType.HANGING_SIGN).destroyTime(1.0f) - .booleanState(ATTACHED) - .intState(ROTATION_16) - .booleanState(WATERLOGGED))); + .booleanState(ATTACHED) + .intState(ROTATION_16) + .booleanState(WATERLOGGED))); public static final Block ACACIA_HANGING_SIGN = register(new Block("acacia_hanging_sign", builder().setBlockEntity(BlockEntityType.HANGING_SIGN).destroyTime(1.0f) - .booleanState(ATTACHED) - .intState(ROTATION_16) - .booleanState(WATERLOGGED))); + .booleanState(ATTACHED) + .intState(ROTATION_16) + .booleanState(WATERLOGGED))); public static final Block CHERRY_HANGING_SIGN = register(new Block("cherry_hanging_sign", builder().setBlockEntity(BlockEntityType.HANGING_SIGN).destroyTime(1.0f) - .booleanState(ATTACHED) - .intState(ROTATION_16) - .booleanState(WATERLOGGED))); + .booleanState(ATTACHED) + .intState(ROTATION_16) + .booleanState(WATERLOGGED))); public static final Block JUNGLE_HANGING_SIGN = register(new Block("jungle_hanging_sign", builder().setBlockEntity(BlockEntityType.HANGING_SIGN).destroyTime(1.0f) - .booleanState(ATTACHED) - .intState(ROTATION_16) - .booleanState(WATERLOGGED))); + .booleanState(ATTACHED) + .intState(ROTATION_16) + .booleanState(WATERLOGGED))); public static final Block DARK_OAK_HANGING_SIGN = register(new Block("dark_oak_hanging_sign", builder().setBlockEntity(BlockEntityType.HANGING_SIGN).destroyTime(1.0f) - .booleanState(ATTACHED) - .intState(ROTATION_16) - .booleanState(WATERLOGGED))); + .booleanState(ATTACHED) + .intState(ROTATION_16) + .booleanState(WATERLOGGED))); + public static final Block PALE_OAK_HANGING_SIGN = register(new Block("pale_oak_hanging_sign", builder().setBlockEntity(BlockEntityType.HANGING_SIGN).destroyTime(1.0f) + .booleanState(ATTACHED) + .intState(ROTATION_16) + .booleanState(WATERLOGGED))); public static final Block CRIMSON_HANGING_SIGN = register(new Block("crimson_hanging_sign", builder().setBlockEntity(BlockEntityType.HANGING_SIGN).destroyTime(1.0f) - .booleanState(ATTACHED) - .intState(ROTATION_16) - .booleanState(WATERLOGGED))); + .booleanState(ATTACHED) + .intState(ROTATION_16) + .booleanState(WATERLOGGED))); public static final Block WARPED_HANGING_SIGN = register(new Block("warped_hanging_sign", builder().setBlockEntity(BlockEntityType.HANGING_SIGN).destroyTime(1.0f) - .booleanState(ATTACHED) - .intState(ROTATION_16) - .booleanState(WATERLOGGED))); + .booleanState(ATTACHED) + .intState(ROTATION_16) + .booleanState(WATERLOGGED))); public static final Block MANGROVE_HANGING_SIGN = register(new Block("mangrove_hanging_sign", builder().setBlockEntity(BlockEntityType.HANGING_SIGN).destroyTime(1.0f) - .booleanState(ATTACHED) - .intState(ROTATION_16) - .booleanState(WATERLOGGED))); + .booleanState(ATTACHED) + .intState(ROTATION_16) + .booleanState(WATERLOGGED))); public static final Block BAMBOO_HANGING_SIGN = register(new Block("bamboo_hanging_sign", builder().setBlockEntity(BlockEntityType.HANGING_SIGN).destroyTime(1.0f) - .booleanState(ATTACHED) - .intState(ROTATION_16) - .booleanState(WATERLOGGED))); + .booleanState(ATTACHED) + .intState(ROTATION_16) + .booleanState(WATERLOGGED))); public static final Block OAK_WALL_HANGING_SIGN = register(new Block("oak_wall_hanging_sign", builder().setBlockEntity(BlockEntityType.HANGING_SIGN).destroyTime(1.0f) - .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) - .booleanState(WATERLOGGED))); + .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) + .booleanState(WATERLOGGED))); public static final Block SPRUCE_WALL_HANGING_SIGN = register(new Block("spruce_wall_hanging_sign", builder().setBlockEntity(BlockEntityType.HANGING_SIGN).destroyTime(1.0f) - .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) - .booleanState(WATERLOGGED))); + .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) + .booleanState(WATERLOGGED))); public static final Block BIRCH_WALL_HANGING_SIGN = register(new Block("birch_wall_hanging_sign", builder().setBlockEntity(BlockEntityType.HANGING_SIGN).destroyTime(1.0f) - .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) - .booleanState(WATERLOGGED))); + .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) + .booleanState(WATERLOGGED))); public static final Block ACACIA_WALL_HANGING_SIGN = register(new Block("acacia_wall_hanging_sign", builder().setBlockEntity(BlockEntityType.HANGING_SIGN).destroyTime(1.0f) - .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) - .booleanState(WATERLOGGED))); + .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) + .booleanState(WATERLOGGED))); public static final Block CHERRY_WALL_HANGING_SIGN = register(new Block("cherry_wall_hanging_sign", builder().setBlockEntity(BlockEntityType.HANGING_SIGN).destroyTime(1.0f) - .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) - .booleanState(WATERLOGGED))); + .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) + .booleanState(WATERLOGGED))); public static final Block JUNGLE_WALL_HANGING_SIGN = register(new Block("jungle_wall_hanging_sign", builder().setBlockEntity(BlockEntityType.HANGING_SIGN).destroyTime(1.0f) - .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) - .booleanState(WATERLOGGED))); + .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) + .booleanState(WATERLOGGED))); public static final Block DARK_OAK_WALL_HANGING_SIGN = register(new Block("dark_oak_wall_hanging_sign", builder().setBlockEntity(BlockEntityType.HANGING_SIGN).destroyTime(1.0f) - .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) - .booleanState(WATERLOGGED))); + .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) + .booleanState(WATERLOGGED))); + public static final Block PALE_OAK_WALL_HANGING_SIGN = register(new Block("pale_oak_wall_hanging_sign", builder().setBlockEntity(BlockEntityType.HANGING_SIGN).destroyTime(1.0f) + .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) + .booleanState(WATERLOGGED))); public static final Block MANGROVE_WALL_HANGING_SIGN = register(new Block("mangrove_wall_hanging_sign", builder().setBlockEntity(BlockEntityType.HANGING_SIGN).destroyTime(1.0f) - .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) - .booleanState(WATERLOGGED))); + .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) + .booleanState(WATERLOGGED))); public static final Block CRIMSON_WALL_HANGING_SIGN = register(new Block("crimson_wall_hanging_sign", builder().setBlockEntity(BlockEntityType.HANGING_SIGN).destroyTime(1.0f) - .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) - .booleanState(WATERLOGGED))); + .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) + .booleanState(WATERLOGGED))); public static final Block WARPED_WALL_HANGING_SIGN = register(new Block("warped_wall_hanging_sign", builder().setBlockEntity(BlockEntityType.HANGING_SIGN).destroyTime(1.0f) - .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) - .booleanState(WATERLOGGED))); + .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) + .booleanState(WATERLOGGED))); public static final Block BAMBOO_WALL_HANGING_SIGN = register(new Block("bamboo_wall_hanging_sign", builder().setBlockEntity(BlockEntityType.HANGING_SIGN).destroyTime(1.0f) - .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) - .booleanState(WATERLOGGED))); + .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) + .booleanState(WATERLOGGED))); public static final Block LEVER = register(new Block("lever", builder().destroyTime(0.5f).pushReaction(PistonBehavior.DESTROY) - .enumState(ATTACH_FACE) - .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) - .booleanState(POWERED))); + .enumState(ATTACH_FACE) + .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) + .booleanState(POWERED))); public static final Block STONE_PRESSURE_PLATE = register(new Block("stone_pressure_plate", builder().requiresCorrectToolForDrops().destroyTime(0.5f).pushReaction(PistonBehavior.DESTROY) - .booleanState(POWERED))); + .booleanState(POWERED))); public static final Block IRON_DOOR = register(new DoorBlock("iron_door", builder().requiresCorrectToolForDrops().destroyTime(5.0f).pushReaction(PistonBehavior.DESTROY) - .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) - .enumState(DOUBLE_BLOCK_HALF) - .enumState(DOOR_HINGE) - .booleanState(OPEN) - .booleanState(POWERED))); + .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) + .enumState(DOUBLE_BLOCK_HALF) + .enumState(DOOR_HINGE) + .booleanState(OPEN) + .booleanState(POWERED))); public static final Block OAK_PRESSURE_PLATE = register(new Block("oak_pressure_plate", builder().destroyTime(0.5f).pushReaction(PistonBehavior.DESTROY) - .booleanState(POWERED))); + .booleanState(POWERED))); public static final Block SPRUCE_PRESSURE_PLATE = register(new Block("spruce_pressure_plate", builder().destroyTime(0.5f).pushReaction(PistonBehavior.DESTROY) - .booleanState(POWERED))); + .booleanState(POWERED))); public static final Block BIRCH_PRESSURE_PLATE = register(new Block("birch_pressure_plate", builder().destroyTime(0.5f).pushReaction(PistonBehavior.DESTROY) - .booleanState(POWERED))); + .booleanState(POWERED))); public static final Block JUNGLE_PRESSURE_PLATE = register(new Block("jungle_pressure_plate", builder().destroyTime(0.5f).pushReaction(PistonBehavior.DESTROY) - .booleanState(POWERED))); + .booleanState(POWERED))); public static final Block ACACIA_PRESSURE_PLATE = register(new Block("acacia_pressure_plate", builder().destroyTime(0.5f).pushReaction(PistonBehavior.DESTROY) - .booleanState(POWERED))); + .booleanState(POWERED))); public static final Block CHERRY_PRESSURE_PLATE = register(new Block("cherry_pressure_plate", builder().destroyTime(0.5f).pushReaction(PistonBehavior.DESTROY) - .booleanState(POWERED))); + .booleanState(POWERED))); public static final Block DARK_OAK_PRESSURE_PLATE = register(new Block("dark_oak_pressure_plate", builder().destroyTime(0.5f).pushReaction(PistonBehavior.DESTROY) - .booleanState(POWERED))); + .booleanState(POWERED))); + public static final Block PALE_OAK_PRESSURE_PLATE = register(new Block("pale_oak_pressure_plate", builder().destroyTime(0.5f).pushReaction(PistonBehavior.DESTROY) + .booleanState(POWERED))); public static final Block MANGROVE_PRESSURE_PLATE = register(new Block("mangrove_pressure_plate", builder().destroyTime(0.5f).pushReaction(PistonBehavior.DESTROY) - .booleanState(POWERED))); + .booleanState(POWERED))); public static final Block BAMBOO_PRESSURE_PLATE = register(new Block("bamboo_pressure_plate", builder().destroyTime(0.5f).pushReaction(PistonBehavior.DESTROY) - .booleanState(POWERED))); + .booleanState(POWERED))); public static final Block REDSTONE_ORE = register(new Block("redstone_ore", builder().requiresCorrectToolForDrops().destroyTime(3.0f) - .booleanState(LIT))); + .booleanState(LIT))); public static final Block DEEPSLATE_REDSTONE_ORE = register(new Block("deepslate_redstone_ore", builder().requiresCorrectToolForDrops().destroyTime(4.5f) - .booleanState(LIT))); + .booleanState(LIT))); public static final Block REDSTONE_TORCH = register(new Block("redstone_torch", builder().pushReaction(PistonBehavior.DESTROY) - .booleanState(LIT))); + .booleanState(LIT))); public static final Block REDSTONE_WALL_TORCH = register(new Block("redstone_wall_torch", builder().pushReaction(PistonBehavior.DESTROY) - .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) - .booleanState(LIT))); + .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) + .booleanState(LIT))); public static final Block STONE_BUTTON = register(new Block("stone_button", builder().destroyTime(0.5f).pushReaction(PistonBehavior.DESTROY) - .enumState(ATTACH_FACE) - .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) - .booleanState(POWERED))); + .enumState(ATTACH_FACE) + .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) + .booleanState(POWERED))); public static final Block SNOW = register(new Block("snow", builder().requiresCorrectToolForDrops().destroyTime(0.1f).pushReaction(PistonBehavior.DESTROY) - .intState(LAYERS))); + .intState(LAYERS))); public static final Block ICE = register(new Block("ice", builder().destroyTime(0.5f))); public static final Block SNOW_BLOCK = register(new Block("snow_block", builder().requiresCorrectToolForDrops().destroyTime(0.2f))); public static final Block CACTUS = register(new Block("cactus", builder().destroyTime(0.4f).pushReaction(PistonBehavior.DESTROY) - .intState(AGE_15))); + .intState(AGE_15))); public static final Block CLAY = register(new Block("clay", builder().destroyTime(0.6f))); public static final Block SUGAR_CANE = register(new Block("sugar_cane", builder().pushReaction(PistonBehavior.DESTROY) - .intState(AGE_15))); + .intState(AGE_15))); public static final Block JUKEBOX = register(new Block("jukebox", builder().setBlockEntity(BlockEntityType.JUKEBOX).destroyTime(2.0f) - .booleanState(HAS_RECORD))); + .booleanState(HAS_RECORD))); public static final Block OAK_FENCE = register(new Block("oak_fence", builder().destroyTime(2.0f) - .booleanState(EAST) - .booleanState(NORTH) - .booleanState(SOUTH) - .booleanState(WATERLOGGED) - .booleanState(WEST))); + .booleanState(EAST) + .booleanState(NORTH) + .booleanState(SOUTH) + .booleanState(WATERLOGGED) + .booleanState(WEST))); public static final Block NETHERRACK = register(new Block("netherrack", builder().requiresCorrectToolForDrops().destroyTime(0.4f))); public static final Block SOUL_SAND = register(new Block("soul_sand", builder().destroyTime(0.5f))); public static final Block SOUL_SOIL = register(new Block("soul_soil", builder().destroyTime(0.5f))); public static final Block BASALT = register(new Block("basalt", builder().requiresCorrectToolForDrops().destroyTime(1.25f) - .enumState(AXIS, Axis.VALUES))); + .enumState(AXIS, Axis.VALUES))); public static final Block POLISHED_BASALT = register(new Block("polished_basalt", builder().requiresCorrectToolForDrops().destroyTime(1.25f) - .enumState(AXIS, Axis.VALUES))); + .enumState(AXIS, Axis.VALUES))); public static final Block SOUL_TORCH = register(new Block("soul_torch", builder().pushReaction(PistonBehavior.DESTROY))); public static final Block SOUL_WALL_TORCH = register(new Block("soul_wall_torch", builder().pushReaction(PistonBehavior.DESTROY) - .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST))); + .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST))); public static final Block GLOWSTONE = register(new Block("glowstone", builder().destroyTime(0.3f))); public static final Block NETHER_PORTAL = register(new Block("nether_portal", builder().destroyTime(-1.0f).pushReaction(PistonBehavior.BLOCK) - .enumState(HORIZONTAL_AXIS, Axis.X, Axis.Z))); + .enumState(HORIZONTAL_AXIS, Axis.X, Axis.Z))); public static final Block CARVED_PUMPKIN = register(new Block("carved_pumpkin", builder().destroyTime(1.0f).pushReaction(PistonBehavior.DESTROY) - .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST))); + .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST))); public static final Block JACK_O_LANTERN = register(new Block("jack_o_lantern", builder().destroyTime(1.0f).pushReaction(PistonBehavior.DESTROY) - .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST))); + .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST))); public static final Block CAKE = register(new Block("cake", builder().destroyTime(0.5f).pushReaction(PistonBehavior.DESTROY) - .intState(BITES))); + .intState(BITES))); public static final Block REPEATER = register(new Block("repeater", builder().pushReaction(PistonBehavior.DESTROY) - .intState(DELAY) - .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) - .booleanState(LOCKED) - .booleanState(POWERED))); + .intState(DELAY) + .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) + .booleanState(LOCKED) + .booleanState(POWERED))); public static final Block WHITE_STAINED_GLASS = register(new Block("white_stained_glass", builder().destroyTime(0.3f))); public static final Block ORANGE_STAINED_GLASS = register(new Block("orange_stained_glass", builder().destroyTime(0.3f))); public static final Block MAGENTA_STAINED_GLASS = register(new Block("magenta_stained_glass", builder().destroyTime(0.3f))); @@ -658,59 +691,65 @@ public final class Blocks { public static final Block RED_STAINED_GLASS = register(new Block("red_stained_glass", builder().destroyTime(0.3f))); public static final Block BLACK_STAINED_GLASS = register(new Block("black_stained_glass", builder().destroyTime(0.3f))); public static final Block OAK_TRAPDOOR = register(new TrapDoorBlock("oak_trapdoor", builder().destroyTime(3.0f) - .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) - .enumState(HALF) - .booleanState(OPEN) - .booleanState(POWERED) - .booleanState(WATERLOGGED))); + .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) + .enumState(HALF) + .booleanState(OPEN) + .booleanState(POWERED) + .booleanState(WATERLOGGED))); public static final Block SPRUCE_TRAPDOOR = register(new TrapDoorBlock("spruce_trapdoor", builder().destroyTime(3.0f) - .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) - .enumState(HALF) - .booleanState(OPEN) - .booleanState(POWERED) - .booleanState(WATERLOGGED))); + .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) + .enumState(HALF) + .booleanState(OPEN) + .booleanState(POWERED) + .booleanState(WATERLOGGED))); public static final Block BIRCH_TRAPDOOR = register(new TrapDoorBlock("birch_trapdoor", builder().destroyTime(3.0f) - .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) - .enumState(HALF) - .booleanState(OPEN) - .booleanState(POWERED) - .booleanState(WATERLOGGED))); + .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) + .enumState(HALF) + .booleanState(OPEN) + .booleanState(POWERED) + .booleanState(WATERLOGGED))); public static final Block JUNGLE_TRAPDOOR = register(new TrapDoorBlock("jungle_trapdoor", builder().destroyTime(3.0f) - .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) - .enumState(HALF) - .booleanState(OPEN) - .booleanState(POWERED) - .booleanState(WATERLOGGED))); + .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) + .enumState(HALF) + .booleanState(OPEN) + .booleanState(POWERED) + .booleanState(WATERLOGGED))); public static final Block ACACIA_TRAPDOOR = register(new TrapDoorBlock("acacia_trapdoor", builder().destroyTime(3.0f) - .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) - .enumState(HALF) - .booleanState(OPEN) - .booleanState(POWERED) - .booleanState(WATERLOGGED))); + .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) + .enumState(HALF) + .booleanState(OPEN) + .booleanState(POWERED) + .booleanState(WATERLOGGED))); public static final Block CHERRY_TRAPDOOR = register(new TrapDoorBlock("cherry_trapdoor", builder().destroyTime(3.0f) - .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) - .enumState(HALF) - .booleanState(OPEN) - .booleanState(POWERED) - .booleanState(WATERLOGGED))); + .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) + .enumState(HALF) + .booleanState(OPEN) + .booleanState(POWERED) + .booleanState(WATERLOGGED))); public static final Block DARK_OAK_TRAPDOOR = register(new TrapDoorBlock("dark_oak_trapdoor", builder().destroyTime(3.0f) - .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) - .enumState(HALF) - .booleanState(OPEN) - .booleanState(POWERED) - .booleanState(WATERLOGGED))); + .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) + .enumState(HALF) + .booleanState(OPEN) + .booleanState(POWERED) + .booleanState(WATERLOGGED))); + public static final Block PALE_OAK_TRAPDOOR = register(new TrapDoorBlock("pale_oak_trapdoor", builder().destroyTime(3.0f) + .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) + .enumState(HALF) + .booleanState(OPEN) + .booleanState(POWERED) + .booleanState(WATERLOGGED))); public static final Block MANGROVE_TRAPDOOR = register(new TrapDoorBlock("mangrove_trapdoor", builder().destroyTime(3.0f) - .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) - .enumState(HALF) - .booleanState(OPEN) - .booleanState(POWERED) - .booleanState(WATERLOGGED))); + .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) + .enumState(HALF) + .booleanState(OPEN) + .booleanState(POWERED) + .booleanState(WATERLOGGED))); public static final Block BAMBOO_TRAPDOOR = register(new TrapDoorBlock("bamboo_trapdoor", builder().destroyTime(3.0f) - .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) - .enumState(HALF) - .booleanState(OPEN) - .booleanState(POWERED) - .booleanState(WATERLOGGED))); + .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) + .enumState(HALF) + .booleanState(OPEN) + .booleanState(POWERED) + .booleanState(WATERLOGGED))); public static final Block STONE_BRICKS = register(new Block("stone_bricks", builder().requiresCorrectToolForDrops().destroyTime(1.5f))); public static final Block MOSSY_STONE_BRICKS = register(new Block("mossy_stone_bricks", builder().requiresCorrectToolForDrops().destroyTime(1.5f))); public static final Block CRACKED_STONE_BRICKS = register(new Block("cracked_stone_bricks", builder().requiresCorrectToolForDrops().destroyTime(1.5f))); @@ -724,180 +763,180 @@ public final class Blocks { public static final Block INFESTED_CRACKED_STONE_BRICKS = register(new Block("infested_cracked_stone_bricks", builder().destroyTime(0.75f))); public static final Block INFESTED_CHISELED_STONE_BRICKS = register(new Block("infested_chiseled_stone_bricks", builder().destroyTime(0.75f))); public static final Block BROWN_MUSHROOM_BLOCK = register(new Block("brown_mushroom_block", builder().destroyTime(0.2f) - .booleanState(DOWN) - .booleanState(EAST) - .booleanState(NORTH) - .booleanState(SOUTH) - .booleanState(UP) - .booleanState(WEST))); + .booleanState(DOWN) + .booleanState(EAST) + .booleanState(NORTH) + .booleanState(SOUTH) + .booleanState(UP) + .booleanState(WEST))); public static final Block RED_MUSHROOM_BLOCK = register(new Block("red_mushroom_block", builder().destroyTime(0.2f) - .booleanState(DOWN) - .booleanState(EAST) - .booleanState(NORTH) - .booleanState(SOUTH) - .booleanState(UP) - .booleanState(WEST))); + .booleanState(DOWN) + .booleanState(EAST) + .booleanState(NORTH) + .booleanState(SOUTH) + .booleanState(UP) + .booleanState(WEST))); public static final Block MUSHROOM_STEM = register(new Block("mushroom_stem", builder().destroyTime(0.2f) - .booleanState(DOWN) - .booleanState(EAST) - .booleanState(NORTH) - .booleanState(SOUTH) - .booleanState(UP) - .booleanState(WEST))); + .booleanState(DOWN) + .booleanState(EAST) + .booleanState(NORTH) + .booleanState(SOUTH) + .booleanState(UP) + .booleanState(WEST))); public static final Block IRON_BARS = register(new Block("iron_bars", builder().requiresCorrectToolForDrops().destroyTime(5.0f) - .booleanState(EAST) - .booleanState(NORTH) - .booleanState(SOUTH) - .booleanState(WATERLOGGED) - .booleanState(WEST))); + .booleanState(EAST) + .booleanState(NORTH) + .booleanState(SOUTH) + .booleanState(WATERLOGGED) + .booleanState(WEST))); public static final Block CHAIN = register(new Block("chain", builder().requiresCorrectToolForDrops().destroyTime(5.0f) - .enumState(AXIS, Axis.VALUES) - .booleanState(WATERLOGGED))); + .enumState(AXIS, Axis.VALUES) + .booleanState(WATERLOGGED))); public static final Block GLASS_PANE = register(new Block("glass_pane", builder().destroyTime(0.3f) - .booleanState(EAST) - .booleanState(NORTH) - .booleanState(SOUTH) - .booleanState(WATERLOGGED) - .booleanState(WEST))); + .booleanState(EAST) + .booleanState(NORTH) + .booleanState(SOUTH) + .booleanState(WATERLOGGED) + .booleanState(WEST))); public static final Block PUMPKIN = register(new Block("pumpkin", builder().destroyTime(1.0f).pushReaction(PistonBehavior.DESTROY))); public static final Block MELON = register(new Block("melon", builder().destroyTime(1.0f).pushReaction(PistonBehavior.DESTROY))); public static final Block ATTACHED_PUMPKIN_STEM = register(new Block("attached_pumpkin_stem", builder().pushReaction(PistonBehavior.DESTROY).pickItem(() -> Items.PUMPKIN_SEEDS) - .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST))); + .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST))); public static final Block ATTACHED_MELON_STEM = register(new Block("attached_melon_stem", builder().pushReaction(PistonBehavior.DESTROY).pickItem(() -> Items.MELON_SEEDS) - .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST))); + .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST))); public static final Block PUMPKIN_STEM = register(new Block("pumpkin_stem", builder().pushReaction(PistonBehavior.DESTROY) - .intState(AGE_7))); + .intState(AGE_7))); public static final Block MELON_STEM = register(new Block("melon_stem", builder().pushReaction(PistonBehavior.DESTROY) - .intState(AGE_7))); + .intState(AGE_7))); public static final Block VINE = register(new Block("vine", builder().destroyTime(0.2f).pushReaction(PistonBehavior.DESTROY) - .booleanState(EAST) - .booleanState(NORTH) - .booleanState(SOUTH) - .booleanState(UP) - .booleanState(WEST))); + .booleanState(EAST) + .booleanState(NORTH) + .booleanState(SOUTH) + .booleanState(UP) + .booleanState(WEST))); public static final Block GLOW_LICHEN = register(new Block("glow_lichen", builder().destroyTime(0.2f).pushReaction(PistonBehavior.DESTROY) - .booleanState(DOWN) - .booleanState(EAST) - .booleanState(NORTH) - .booleanState(SOUTH) - .booleanState(UP) - .booleanState(WATERLOGGED) - .booleanState(WEST))); + .booleanState(DOWN) + .booleanState(EAST) + .booleanState(NORTH) + .booleanState(SOUTH) + .booleanState(UP) + .booleanState(WATERLOGGED) + .booleanState(WEST))); public static final Block OAK_FENCE_GATE = register(new Block("oak_fence_gate", builder().destroyTime(2.0f) - .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) - .booleanState(IN_WALL) - .booleanState(OPEN) - .booleanState(POWERED))); + .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) + .booleanState(IN_WALL) + .booleanState(OPEN) + .booleanState(POWERED))); public static final Block BRICK_STAIRS = register(new Block("brick_stairs", builder().requiresCorrectToolForDrops().destroyTime(2.0f) - .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) - .enumState(HALF) - .enumState(STAIRS_SHAPE) - .booleanState(WATERLOGGED))); + .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) + .enumState(HALF) + .enumState(STAIRS_SHAPE) + .booleanState(WATERLOGGED))); public static final Block STONE_BRICK_STAIRS = register(new Block("stone_brick_stairs", builder().requiresCorrectToolForDrops().destroyTime(1.5f) - .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) - .enumState(HALF) - .enumState(STAIRS_SHAPE) - .booleanState(WATERLOGGED))); + .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) + .enumState(HALF) + .enumState(STAIRS_SHAPE) + .booleanState(WATERLOGGED))); public static final Block MUD_BRICK_STAIRS = register(new Block("mud_brick_stairs", builder().requiresCorrectToolForDrops().destroyTime(1.5f) - .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) - .enumState(HALF) - .enumState(STAIRS_SHAPE) - .booleanState(WATERLOGGED))); + .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) + .enumState(HALF) + .enumState(STAIRS_SHAPE) + .booleanState(WATERLOGGED))); public static final Block MYCELIUM = register(new Block("mycelium", builder().destroyTime(0.6f) - .booleanState(SNOWY))); + .booleanState(SNOWY))); public static final Block LILY_PAD = register(new Block("lily_pad", builder().pushReaction(PistonBehavior.DESTROY))); public static final Block NETHER_BRICKS = register(new Block("nether_bricks", builder().requiresCorrectToolForDrops().destroyTime(2.0f))); public static final Block NETHER_BRICK_FENCE = register(new Block("nether_brick_fence", builder().requiresCorrectToolForDrops().destroyTime(2.0f) - .booleanState(EAST) - .booleanState(NORTH) - .booleanState(SOUTH) - .booleanState(WATERLOGGED) - .booleanState(WEST))); + .booleanState(EAST) + .booleanState(NORTH) + .booleanState(SOUTH) + .booleanState(WATERLOGGED) + .booleanState(WEST))); public static final Block NETHER_BRICK_STAIRS = register(new Block("nether_brick_stairs", builder().requiresCorrectToolForDrops().destroyTime(2.0f) - .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) - .enumState(HALF) - .enumState(STAIRS_SHAPE) - .booleanState(WATERLOGGED))); + .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) + .enumState(HALF) + .enumState(STAIRS_SHAPE) + .booleanState(WATERLOGGED))); public static final Block NETHER_WART = register(new Block("nether_wart", builder().pushReaction(PistonBehavior.DESTROY) - .intState(AGE_3))); + .intState(AGE_3))); public static final Block ENCHANTING_TABLE = register(new Block("enchanting_table", builder().setBlockEntity(BlockEntityType.ENCHANTING_TABLE).requiresCorrectToolForDrops().destroyTime(5.0f))); public static final Block BREWING_STAND = register(new Block("brewing_stand", builder().setBlockEntity(BlockEntityType.BREWING_STAND).requiresCorrectToolForDrops().destroyTime(0.5f) - .booleanState(HAS_BOTTLE_0) - .booleanState(HAS_BOTTLE_1) - .booleanState(HAS_BOTTLE_2))); + .booleanState(HAS_BOTTLE_0) + .booleanState(HAS_BOTTLE_1) + .booleanState(HAS_BOTTLE_2))); public static final Block CAULDRON = register(new CauldronBlock("cauldron", builder().requiresCorrectToolForDrops().destroyTime(2.0f))); public static final Block WATER_CAULDRON = register(new CauldronBlock("water_cauldron", builder().requiresCorrectToolForDrops().destroyTime(2.0f) - .intState(LEVEL_CAULDRON))); + .intState(LEVEL_CAULDRON))); public static final Block LAVA_CAULDRON = register(new CauldronBlock("lava_cauldron", builder().requiresCorrectToolForDrops().destroyTime(2.0f))); public static final Block POWDER_SNOW_CAULDRON = register(new CauldronBlock("powder_snow_cauldron", builder().requiresCorrectToolForDrops().destroyTime(2.0f) - .intState(LEVEL_CAULDRON))); + .intState(LEVEL_CAULDRON))); public static final Block END_PORTAL = register(new Block("end_portal", builder().setBlockEntity(BlockEntityType.END_PORTAL).destroyTime(-1.0f).pushReaction(PistonBehavior.BLOCK))); public static final Block END_PORTAL_FRAME = register(new Block("end_portal_frame", builder().destroyTime(-1.0f) - .booleanState(EYE) - .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST))); + .booleanState(EYE) + .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST))); public static final Block END_STONE = register(new Block("end_stone", builder().requiresCorrectToolForDrops().destroyTime(3.0f))); public static final Block DRAGON_EGG = register(new Block("dragon_egg", builder().destroyTime(3.0f).pushReaction(PistonBehavior.DESTROY))); public static final Block REDSTONE_LAMP = register(new Block("redstone_lamp", builder().destroyTime(0.3f) - .booleanState(LIT))); + .booleanState(LIT))); public static final Block COCOA = register(new Block("cocoa", builder().destroyTime(0.2f).pushReaction(PistonBehavior.DESTROY) - .intState(AGE_2) - .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST))); + .intState(AGE_2) + .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST))); public static final Block SANDSTONE_STAIRS = register(new Block("sandstone_stairs", builder().requiresCorrectToolForDrops().destroyTime(0.8f) - .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) - .enumState(HALF) - .enumState(STAIRS_SHAPE) - .booleanState(WATERLOGGED))); + .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) + .enumState(HALF) + .enumState(STAIRS_SHAPE) + .booleanState(WATERLOGGED))); public static final Block EMERALD_ORE = register(new Block("emerald_ore", builder().requiresCorrectToolForDrops().destroyTime(3.0f))); public static final Block DEEPSLATE_EMERALD_ORE = register(new Block("deepslate_emerald_ore", builder().requiresCorrectToolForDrops().destroyTime(4.5f))); public static final Block ENDER_CHEST = register(new Block("ender_chest", builder().setBlockEntity(BlockEntityType.ENDER_CHEST).requiresCorrectToolForDrops().destroyTime(22.5f) - .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) - .booleanState(WATERLOGGED))); + .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) + .booleanState(WATERLOGGED))); public static final Block TRIPWIRE_HOOK = register(new Block("tripwire_hook", builder().pushReaction(PistonBehavior.DESTROY) - .booleanState(ATTACHED) - .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) - .booleanState(POWERED))); + .booleanState(ATTACHED) + .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) + .booleanState(POWERED))); public static final Block TRIPWIRE = register(new Block("tripwire", builder().pushReaction(PistonBehavior.DESTROY) - .booleanState(ATTACHED) - .booleanState(DISARMED) - .booleanState(EAST) - .booleanState(NORTH) - .booleanState(POWERED) - .booleanState(SOUTH) - .booleanState(WEST))); + .booleanState(ATTACHED) + .booleanState(DISARMED) + .booleanState(EAST) + .booleanState(NORTH) + .booleanState(POWERED) + .booleanState(SOUTH) + .booleanState(WEST))); public static final Block EMERALD_BLOCK = register(new Block("emerald_block", builder().requiresCorrectToolForDrops().destroyTime(5.0f))); public static final Block SPRUCE_STAIRS = register(new Block("spruce_stairs", builder().destroyTime(2.0f) - .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) - .enumState(HALF) - .enumState(STAIRS_SHAPE) - .booleanState(WATERLOGGED))); + .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) + .enumState(HALF) + .enumState(STAIRS_SHAPE) + .booleanState(WATERLOGGED))); public static final Block BIRCH_STAIRS = register(new Block("birch_stairs", builder().destroyTime(2.0f) - .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) - .enumState(HALF) - .enumState(STAIRS_SHAPE) - .booleanState(WATERLOGGED))); + .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) + .enumState(HALF) + .enumState(STAIRS_SHAPE) + .booleanState(WATERLOGGED))); public static final Block JUNGLE_STAIRS = register(new Block("jungle_stairs", builder().destroyTime(2.0f) - .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) - .enumState(HALF) - .enumState(STAIRS_SHAPE) - .booleanState(WATERLOGGED))); + .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) + .enumState(HALF) + .enumState(STAIRS_SHAPE) + .booleanState(WATERLOGGED))); public static final Block COMMAND_BLOCK = register(new Block("command_block", builder().setBlockEntity(BlockEntityType.COMMAND_BLOCK).requiresCorrectToolForDrops().destroyTime(-1.0f) - .booleanState(CONDITIONAL) - .enumState(FACING, Direction.NORTH, Direction.EAST, Direction.SOUTH, Direction.WEST, Direction.UP, Direction.DOWN))); + .booleanState(CONDITIONAL) + .enumState(FACING, Direction.NORTH, Direction.EAST, Direction.SOUTH, Direction.WEST, Direction.UP, Direction.DOWN))); public static final Block BEACON = register(new Block("beacon", builder().setBlockEntity(BlockEntityType.BEACON).destroyTime(3.0f))); public static final Block COBBLESTONE_WALL = register(new Block("cobblestone_wall", builder().requiresCorrectToolForDrops().destroyTime(2.0f) - .enumState(EAST_WALL) - .enumState(NORTH_WALL) - .enumState(SOUTH_WALL) - .booleanState(UP) - .booleanState(WATERLOGGED) - .enumState(WEST_WALL))); + .enumState(EAST_WALL) + .enumState(NORTH_WALL) + .enumState(SOUTH_WALL) + .booleanState(UP) + .booleanState(WATERLOGGED) + .enumState(WEST_WALL))); public static final Block MOSSY_COBBLESTONE_WALL = register(new Block("mossy_cobblestone_wall", builder().requiresCorrectToolForDrops().destroyTime(2.0f) - .enumState(EAST_WALL) - .enumState(NORTH_WALL) - .enumState(SOUTH_WALL) - .booleanState(UP) - .booleanState(WATERLOGGED) - .enumState(WEST_WALL))); + .enumState(EAST_WALL) + .enumState(NORTH_WALL) + .enumState(SOUTH_WALL) + .booleanState(UP) + .booleanState(WATERLOGGED) + .enumState(WEST_WALL))); public static final Block FLOWER_POT = register(new FlowerPotBlock("flower_pot", AIR, builder().pushReaction(PistonBehavior.DESTROY))); public static final Block POTTED_TORCHFLOWER = register(new FlowerPotBlock("potted_torchflower", TORCHFLOWER, builder().pushReaction(PistonBehavior.DESTROY))); public static final Block POTTED_OAK_SAPLING = register(new FlowerPotBlock("potted_oak_sapling", OAK_SAPLING, builder().pushReaction(PistonBehavior.DESTROY))); @@ -907,6 +946,7 @@ public final class Blocks { public static final Block POTTED_ACACIA_SAPLING = register(new FlowerPotBlock("potted_acacia_sapling", ACACIA_SAPLING, builder().pushReaction(PistonBehavior.DESTROY))); public static final Block POTTED_CHERRY_SAPLING = register(new FlowerPotBlock("potted_cherry_sapling", CHERRY_SAPLING, builder().pushReaction(PistonBehavior.DESTROY))); public static final Block POTTED_DARK_OAK_SAPLING = register(new FlowerPotBlock("potted_dark_oak_sapling", DARK_OAK_SAPLING, builder().pushReaction(PistonBehavior.DESTROY))); + public static final Block POTTED_PALE_OAK_SAPLING = register(new FlowerPotBlock("potted_pale_oak_sapling", PALE_OAK_SAPLING, builder().pushReaction(PistonBehavior.DESTROY))); public static final Block POTTED_MANGROVE_PROPAGULE = register(new FlowerPotBlock("potted_mangrove_propagule", MANGROVE_PROPAGULE, builder().pushReaction(PistonBehavior.DESTROY))); public static final Block POTTED_FERN = register(new FlowerPotBlock("potted_fern", FERN, builder().pushReaction(PistonBehavior.DESTROY))); public static final Block POTTED_DANDELION = register(new FlowerPotBlock("potted_dandelion", DANDELION, builder().pushReaction(PistonBehavior.DESTROY))); @@ -927,129 +967,133 @@ public final class Blocks { public static final Block POTTED_DEAD_BUSH = register(new FlowerPotBlock("potted_dead_bush", DEAD_BUSH, builder().pushReaction(PistonBehavior.DESTROY))); public static final Block POTTED_CACTUS = register(new FlowerPotBlock("potted_cactus", CACTUS, builder().pushReaction(PistonBehavior.DESTROY))); public static final Block CARROTS = register(new Block("carrots", builder().pushReaction(PistonBehavior.DESTROY) - .intState(AGE_7))); + .intState(AGE_7))); public static final Block POTATOES = register(new Block("potatoes", builder().pushReaction(PistonBehavior.DESTROY) - .intState(AGE_7))); + .intState(AGE_7))); public static final Block OAK_BUTTON = register(new Block("oak_button", builder().destroyTime(0.5f).pushReaction(PistonBehavior.DESTROY) - .enumState(ATTACH_FACE) - .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) - .booleanState(POWERED))); + .enumState(ATTACH_FACE) + .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) + .booleanState(POWERED))); public static final Block SPRUCE_BUTTON = register(new Block("spruce_button", builder().destroyTime(0.5f).pushReaction(PistonBehavior.DESTROY) - .enumState(ATTACH_FACE) - .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) - .booleanState(POWERED))); + .enumState(ATTACH_FACE) + .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) + .booleanState(POWERED))); public static final Block BIRCH_BUTTON = register(new Block("birch_button", builder().destroyTime(0.5f).pushReaction(PistonBehavior.DESTROY) - .enumState(ATTACH_FACE) - .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) - .booleanState(POWERED))); + .enumState(ATTACH_FACE) + .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) + .booleanState(POWERED))); public static final Block JUNGLE_BUTTON = register(new Block("jungle_button", builder().destroyTime(0.5f).pushReaction(PistonBehavior.DESTROY) - .enumState(ATTACH_FACE) - .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) - .booleanState(POWERED))); + .enumState(ATTACH_FACE) + .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) + .booleanState(POWERED))); public static final Block ACACIA_BUTTON = register(new Block("acacia_button", builder().destroyTime(0.5f).pushReaction(PistonBehavior.DESTROY) - .enumState(ATTACH_FACE) - .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) - .booleanState(POWERED))); + .enumState(ATTACH_FACE) + .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) + .booleanState(POWERED))); public static final Block CHERRY_BUTTON = register(new Block("cherry_button", builder().destroyTime(0.5f).pushReaction(PistonBehavior.DESTROY) - .enumState(ATTACH_FACE) - .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) - .booleanState(POWERED))); + .enumState(ATTACH_FACE) + .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) + .booleanState(POWERED))); public static final Block DARK_OAK_BUTTON = register(new Block("dark_oak_button", builder().destroyTime(0.5f).pushReaction(PistonBehavior.DESTROY) - .enumState(ATTACH_FACE) - .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) - .booleanState(POWERED))); + .enumState(ATTACH_FACE) + .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) + .booleanState(POWERED))); + public static final Block PALE_OAK_BUTTON = register(new Block("pale_oak_button", builder().destroyTime(0.5f).pushReaction(PistonBehavior.DESTROY) + .enumState(ATTACH_FACE) + .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) + .booleanState(POWERED))); public static final Block MANGROVE_BUTTON = register(new Block("mangrove_button", builder().destroyTime(0.5f).pushReaction(PistonBehavior.DESTROY) - .enumState(ATTACH_FACE) - .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) - .booleanState(POWERED))); + .enumState(ATTACH_FACE) + .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) + .booleanState(POWERED))); public static final Block BAMBOO_BUTTON = register(new Block("bamboo_button", builder().destroyTime(0.5f).pushReaction(PistonBehavior.DESTROY) - .enumState(ATTACH_FACE) - .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) - .booleanState(POWERED))); + .enumState(ATTACH_FACE) + .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) + .booleanState(POWERED))); public static final Block SKELETON_SKULL = register(new SkullBlock("skeleton_skull", SkullBlock.Type.SKELETON, builder().setBlockEntity(BlockEntityType.SKULL).destroyTime(1.0f).pushReaction(PistonBehavior.DESTROY) - .booleanState(POWERED) - .intState(ROTATION_16))); + .booleanState(POWERED) + .intState(ROTATION_16))); public static final Block SKELETON_WALL_SKULL = register(new WallSkullBlock("skeleton_wall_skull", SkullBlock.Type.SKELETON, builder().setBlockEntity(BlockEntityType.SKULL).destroyTime(1.0f).pushReaction(PistonBehavior.DESTROY) - .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) - .booleanState(POWERED))); + .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) + .booleanState(POWERED))); public static final Block WITHER_SKELETON_SKULL = register(new SkullBlock("wither_skeleton_skull", SkullBlock.Type.WITHER_SKELETON, builder().setBlockEntity(BlockEntityType.SKULL).destroyTime(1.0f).pushReaction(PistonBehavior.DESTROY) - .booleanState(POWERED) - .intState(ROTATION_16))); + .booleanState(POWERED) + .intState(ROTATION_16))); public static final Block WITHER_SKELETON_WALL_SKULL = register(new WallSkullBlock("wither_skeleton_wall_skull", SkullBlock.Type.WITHER_SKELETON, builder().setBlockEntity(BlockEntityType.SKULL).destroyTime(1.0f).pushReaction(PistonBehavior.DESTROY) - .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) - .booleanState(POWERED))); + .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) + .booleanState(POWERED))); public static final Block ZOMBIE_HEAD = register(new SkullBlock("zombie_head", SkullBlock.Type.ZOMBIE, builder().setBlockEntity(BlockEntityType.SKULL).destroyTime(1.0f).pushReaction(PistonBehavior.DESTROY) - .booleanState(POWERED) - .intState(ROTATION_16))); + .booleanState(POWERED) + .intState(ROTATION_16))); public static final Block ZOMBIE_WALL_HEAD = register(new WallSkullBlock("zombie_wall_head", SkullBlock.Type.ZOMBIE, builder().setBlockEntity(BlockEntityType.SKULL).destroyTime(1.0f).pushReaction(PistonBehavior.DESTROY) - .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) - .booleanState(POWERED))); + .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) + .booleanState(POWERED))); public static final Block PLAYER_HEAD = register(new SkullBlock("player_head", SkullBlock.Type.PLAYER, builder().setBlockEntity(BlockEntityType.SKULL).destroyTime(1.0f).pushReaction(PistonBehavior.DESTROY) - .booleanState(POWERED) - .intState(ROTATION_16))); + .booleanState(POWERED) + .intState(ROTATION_16))); public static final Block PLAYER_WALL_HEAD = register(new WallSkullBlock("player_wall_head", SkullBlock.Type.PLAYER, builder().setBlockEntity(BlockEntityType.SKULL).destroyTime(1.0f).pushReaction(PistonBehavior.DESTROY) - .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) - .booleanState(POWERED))); + .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) + .booleanState(POWERED))); public static final Block CREEPER_HEAD = register(new SkullBlock("creeper_head", SkullBlock.Type.CREEPER, builder().setBlockEntity(BlockEntityType.SKULL).destroyTime(1.0f).pushReaction(PistonBehavior.DESTROY) - .booleanState(POWERED) - .intState(ROTATION_16))); + .booleanState(POWERED) + .intState(ROTATION_16))); public static final Block CREEPER_WALL_HEAD = register(new WallSkullBlock("creeper_wall_head", SkullBlock.Type.CREEPER, builder().setBlockEntity(BlockEntityType.SKULL).destroyTime(1.0f).pushReaction(PistonBehavior.DESTROY) - .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) - .booleanState(POWERED))); + .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) + .booleanState(POWERED))); public static final Block DRAGON_HEAD = register(new SkullBlock("dragon_head", SkullBlock.Type.DRAGON, builder().setBlockEntity(BlockEntityType.SKULL).destroyTime(1.0f).pushReaction(PistonBehavior.DESTROY) - .booleanState(POWERED) - .intState(ROTATION_16))); + .booleanState(POWERED) + .intState(ROTATION_16))); public static final Block DRAGON_WALL_HEAD = register(new WallSkullBlock("dragon_wall_head", SkullBlock.Type.DRAGON, builder().setBlockEntity(BlockEntityType.SKULL).destroyTime(1.0f).pushReaction(PistonBehavior.DESTROY) - .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) - .booleanState(POWERED))); + .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) + .booleanState(POWERED))); public static final Block PIGLIN_HEAD = register(new SkullBlock("piglin_head", SkullBlock.Type.PIGLIN, builder().setBlockEntity(BlockEntityType.SKULL).destroyTime(1.0f).pushReaction(PistonBehavior.DESTROY) - .booleanState(POWERED) - .intState(ROTATION_16))); + .booleanState(POWERED) + .intState(ROTATION_16))); public static final Block PIGLIN_WALL_HEAD = register(new WallSkullBlock("piglin_wall_head", SkullBlock.Type.PIGLIN, builder().setBlockEntity(BlockEntityType.SKULL).destroyTime(1.0f).pushReaction(PistonBehavior.DESTROY) - .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) - .booleanState(POWERED))); + .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) + .booleanState(POWERED))); public static final Block ANVIL = register(new Block("anvil", builder().requiresCorrectToolForDrops().destroyTime(5.0f).pushReaction(PistonBehavior.BLOCK) - .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST))); + .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST))); public static final Block CHIPPED_ANVIL = register(new Block("chipped_anvil", builder().requiresCorrectToolForDrops().destroyTime(5.0f).pushReaction(PistonBehavior.BLOCK) - .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST))); + .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST))); public static final Block DAMAGED_ANVIL = register(new Block("damaged_anvil", builder().requiresCorrectToolForDrops().destroyTime(5.0f).pushReaction(PistonBehavior.BLOCK) - .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST))); + .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST))); public static final Block TRAPPED_CHEST = register(new ChestBlock("trapped_chest", builder().setBlockEntity(BlockEntityType.TRAPPED_CHEST).destroyTime(2.5f) - .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) - .enumState(CHEST_TYPE, ChestType.VALUES) - .booleanState(WATERLOGGED))); + .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) + .enumState(CHEST_TYPE, ChestType.VALUES) + .booleanState(WATERLOGGED))); public static final Block LIGHT_WEIGHTED_PRESSURE_PLATE = register(new Block("light_weighted_pressure_plate", builder().requiresCorrectToolForDrops().destroyTime(0.5f).pushReaction(PistonBehavior.DESTROY) - .intState(POWER))); + .intState(POWER))); public static final Block HEAVY_WEIGHTED_PRESSURE_PLATE = register(new Block("heavy_weighted_pressure_plate", builder().requiresCorrectToolForDrops().destroyTime(0.5f).pushReaction(PistonBehavior.DESTROY) - .intState(POWER))); + .intState(POWER))); public static final Block COMPARATOR = register(new Block("comparator", builder().setBlockEntity(BlockEntityType.COMPARATOR).pushReaction(PistonBehavior.DESTROY) - .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) - .enumState(MODE_COMPARATOR) - .booleanState(POWERED))); + .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) + .enumState(MODE_COMPARATOR) + .booleanState(POWERED))); public static final Block DAYLIGHT_DETECTOR = register(new Block("daylight_detector", builder().setBlockEntity(BlockEntityType.DAYLIGHT_DETECTOR).destroyTime(0.2f) - .booleanState(INVERTED) - .intState(POWER))); + .booleanState(INVERTED) + .intState(POWER))); public static final Block REDSTONE_BLOCK = register(new Block("redstone_block", builder().requiresCorrectToolForDrops().destroyTime(5.0f))); public static final Block NETHER_QUARTZ_ORE = register(new Block("nether_quartz_ore", builder().requiresCorrectToolForDrops().destroyTime(3.0f))); public static final Block HOPPER = register(new Block("hopper", builder().setBlockEntity(BlockEntityType.HOPPER).requiresCorrectToolForDrops().destroyTime(3.0f) - .booleanState(ENABLED) - .enumState(FACING_HOPPER, Direction.DOWN, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST))); + .booleanState(ENABLED) + .enumState(FACING_HOPPER, Direction.DOWN, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST))); public static final Block QUARTZ_BLOCK = register(new Block("quartz_block", builder().requiresCorrectToolForDrops().destroyTime(0.8f))); public static final Block CHISELED_QUARTZ_BLOCK = register(new Block("chiseled_quartz_block", builder().requiresCorrectToolForDrops().destroyTime(0.8f))); public static final Block QUARTZ_PILLAR = register(new Block("quartz_pillar", builder().requiresCorrectToolForDrops().destroyTime(0.8f) - .enumState(AXIS, Axis.VALUES))); + .enumState(AXIS, Axis.VALUES))); public static final Block QUARTZ_STAIRS = register(new Block("quartz_stairs", builder().requiresCorrectToolForDrops().destroyTime(0.8f) - .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) - .enumState(HALF) - .enumState(STAIRS_SHAPE) - .booleanState(WATERLOGGED))); + .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) + .enumState(HALF) + .enumState(STAIRS_SHAPE) + .booleanState(WATERLOGGED))); public static final Block ACTIVATOR_RAIL = register(new Block("activator_rail", builder().destroyTime(0.7f) - .booleanState(POWERED) - .enumState(RAIL_SHAPE_STRAIGHT) - .booleanState(WATERLOGGED))); + .booleanState(POWERED) + .enumState(RAIL_SHAPE_STRAIGHT) + .booleanState(WATERLOGGED))); public static final Block DROPPER = register(new Block("dropper", builder().setBlockEntity(BlockEntityType.DROPPER).requiresCorrectToolForDrops().destroyTime(3.5f) - .enumState(FACING, Direction.NORTH, Direction.EAST, Direction.SOUTH, Direction.WEST, Direction.UP, Direction.DOWN) - .booleanState(TRIGGERED))); + .enumState(FACING, Direction.NORTH, Direction.EAST, Direction.SOUTH, Direction.WEST, Direction.UP, Direction.DOWN) + .booleanState(TRIGGERED))); public static final Block WHITE_TERRACOTTA = register(new Block("white_terracotta", builder().requiresCorrectToolForDrops().destroyTime(1.25f))); public static final Block ORANGE_TERRACOTTA = register(new Block("orange_terracotta", builder().requiresCorrectToolForDrops().destroyTime(1.25f))); public static final Block MAGENTA_TERRACOTTA = register(new Block("magenta_terracotta", builder().requiresCorrectToolForDrops().destroyTime(1.25f))); @@ -1067,173 +1111,178 @@ public final class Blocks { public static final Block RED_TERRACOTTA = register(new Block("red_terracotta", builder().requiresCorrectToolForDrops().destroyTime(1.25f))); public static final Block BLACK_TERRACOTTA = register(new Block("black_terracotta", builder().requiresCorrectToolForDrops().destroyTime(1.25f))); public static final Block WHITE_STAINED_GLASS_PANE = register(new Block("white_stained_glass_pane", builder().destroyTime(0.3f) - .booleanState(EAST) - .booleanState(NORTH) - .booleanState(SOUTH) - .booleanState(WATERLOGGED) - .booleanState(WEST))); + .booleanState(EAST) + .booleanState(NORTH) + .booleanState(SOUTH) + .booleanState(WATERLOGGED) + .booleanState(WEST))); public static final Block ORANGE_STAINED_GLASS_PANE = register(new Block("orange_stained_glass_pane", builder().destroyTime(0.3f) - .booleanState(EAST) - .booleanState(NORTH) - .booleanState(SOUTH) - .booleanState(WATERLOGGED) - .booleanState(WEST))); + .booleanState(EAST) + .booleanState(NORTH) + .booleanState(SOUTH) + .booleanState(WATERLOGGED) + .booleanState(WEST))); public static final Block MAGENTA_STAINED_GLASS_PANE = register(new Block("magenta_stained_glass_pane", builder().destroyTime(0.3f) - .booleanState(EAST) - .booleanState(NORTH) - .booleanState(SOUTH) - .booleanState(WATERLOGGED) - .booleanState(WEST))); + .booleanState(EAST) + .booleanState(NORTH) + .booleanState(SOUTH) + .booleanState(WATERLOGGED) + .booleanState(WEST))); public static final Block LIGHT_BLUE_STAINED_GLASS_PANE = register(new Block("light_blue_stained_glass_pane", builder().destroyTime(0.3f) - .booleanState(EAST) - .booleanState(NORTH) - .booleanState(SOUTH) - .booleanState(WATERLOGGED) - .booleanState(WEST))); + .booleanState(EAST) + .booleanState(NORTH) + .booleanState(SOUTH) + .booleanState(WATERLOGGED) + .booleanState(WEST))); public static final Block YELLOW_STAINED_GLASS_PANE = register(new Block("yellow_stained_glass_pane", builder().destroyTime(0.3f) - .booleanState(EAST) - .booleanState(NORTH) - .booleanState(SOUTH) - .booleanState(WATERLOGGED) - .booleanState(WEST))); + .booleanState(EAST) + .booleanState(NORTH) + .booleanState(SOUTH) + .booleanState(WATERLOGGED) + .booleanState(WEST))); public static final Block LIME_STAINED_GLASS_PANE = register(new Block("lime_stained_glass_pane", builder().destroyTime(0.3f) - .booleanState(EAST) - .booleanState(NORTH) - .booleanState(SOUTH) - .booleanState(WATERLOGGED) - .booleanState(WEST))); + .booleanState(EAST) + .booleanState(NORTH) + .booleanState(SOUTH) + .booleanState(WATERLOGGED) + .booleanState(WEST))); public static final Block PINK_STAINED_GLASS_PANE = register(new Block("pink_stained_glass_pane", builder().destroyTime(0.3f) - .booleanState(EAST) - .booleanState(NORTH) - .booleanState(SOUTH) - .booleanState(WATERLOGGED) - .booleanState(WEST))); + .booleanState(EAST) + .booleanState(NORTH) + .booleanState(SOUTH) + .booleanState(WATERLOGGED) + .booleanState(WEST))); public static final Block GRAY_STAINED_GLASS_PANE = register(new Block("gray_stained_glass_pane", builder().destroyTime(0.3f) - .booleanState(EAST) - .booleanState(NORTH) - .booleanState(SOUTH) - .booleanState(WATERLOGGED) - .booleanState(WEST))); + .booleanState(EAST) + .booleanState(NORTH) + .booleanState(SOUTH) + .booleanState(WATERLOGGED) + .booleanState(WEST))); public static final Block LIGHT_GRAY_STAINED_GLASS_PANE = register(new Block("light_gray_stained_glass_pane", builder().destroyTime(0.3f) - .booleanState(EAST) - .booleanState(NORTH) - .booleanState(SOUTH) - .booleanState(WATERLOGGED) - .booleanState(WEST))); + .booleanState(EAST) + .booleanState(NORTH) + .booleanState(SOUTH) + .booleanState(WATERLOGGED) + .booleanState(WEST))); public static final Block CYAN_STAINED_GLASS_PANE = register(new Block("cyan_stained_glass_pane", builder().destroyTime(0.3f) - .booleanState(EAST) - .booleanState(NORTH) - .booleanState(SOUTH) - .booleanState(WATERLOGGED) - .booleanState(WEST))); + .booleanState(EAST) + .booleanState(NORTH) + .booleanState(SOUTH) + .booleanState(WATERLOGGED) + .booleanState(WEST))); public static final Block PURPLE_STAINED_GLASS_PANE = register(new Block("purple_stained_glass_pane", builder().destroyTime(0.3f) - .booleanState(EAST) - .booleanState(NORTH) - .booleanState(SOUTH) - .booleanState(WATERLOGGED) - .booleanState(WEST))); + .booleanState(EAST) + .booleanState(NORTH) + .booleanState(SOUTH) + .booleanState(WATERLOGGED) + .booleanState(WEST))); public static final Block BLUE_STAINED_GLASS_PANE = register(new Block("blue_stained_glass_pane", builder().destroyTime(0.3f) - .booleanState(EAST) - .booleanState(NORTH) - .booleanState(SOUTH) - .booleanState(WATERLOGGED) - .booleanState(WEST))); + .booleanState(EAST) + .booleanState(NORTH) + .booleanState(SOUTH) + .booleanState(WATERLOGGED) + .booleanState(WEST))); public static final Block BROWN_STAINED_GLASS_PANE = register(new Block("brown_stained_glass_pane", builder().destroyTime(0.3f) - .booleanState(EAST) - .booleanState(NORTH) - .booleanState(SOUTH) - .booleanState(WATERLOGGED) - .booleanState(WEST))); + .booleanState(EAST) + .booleanState(NORTH) + .booleanState(SOUTH) + .booleanState(WATERLOGGED) + .booleanState(WEST))); public static final Block GREEN_STAINED_GLASS_PANE = register(new Block("green_stained_glass_pane", builder().destroyTime(0.3f) - .booleanState(EAST) - .booleanState(NORTH) - .booleanState(SOUTH) - .booleanState(WATERLOGGED) - .booleanState(WEST))); + .booleanState(EAST) + .booleanState(NORTH) + .booleanState(SOUTH) + .booleanState(WATERLOGGED) + .booleanState(WEST))); public static final Block RED_STAINED_GLASS_PANE = register(new Block("red_stained_glass_pane", builder().destroyTime(0.3f) - .booleanState(EAST) - .booleanState(NORTH) - .booleanState(SOUTH) - .booleanState(WATERLOGGED) - .booleanState(WEST))); + .booleanState(EAST) + .booleanState(NORTH) + .booleanState(SOUTH) + .booleanState(WATERLOGGED) + .booleanState(WEST))); public static final Block BLACK_STAINED_GLASS_PANE = register(new Block("black_stained_glass_pane", builder().destroyTime(0.3f) - .booleanState(EAST) - .booleanState(NORTH) - .booleanState(SOUTH) - .booleanState(WATERLOGGED) - .booleanState(WEST))); + .booleanState(EAST) + .booleanState(NORTH) + .booleanState(SOUTH) + .booleanState(WATERLOGGED) + .booleanState(WEST))); public static final Block ACACIA_STAIRS = register(new Block("acacia_stairs", builder().destroyTime(2.0f) - .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) - .enumState(HALF) - .enumState(STAIRS_SHAPE) - .booleanState(WATERLOGGED))); + .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) + .enumState(HALF) + .enumState(STAIRS_SHAPE) + .booleanState(WATERLOGGED))); public static final Block CHERRY_STAIRS = register(new Block("cherry_stairs", builder().destroyTime(2.0f) - .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) - .enumState(HALF) - .enumState(STAIRS_SHAPE) - .booleanState(WATERLOGGED))); + .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) + .enumState(HALF) + .enumState(STAIRS_SHAPE) + .booleanState(WATERLOGGED))); public static final Block DARK_OAK_STAIRS = register(new Block("dark_oak_stairs", builder().destroyTime(2.0f) - .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) - .enumState(HALF) - .enumState(STAIRS_SHAPE) - .booleanState(WATERLOGGED))); + .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) + .enumState(HALF) + .enumState(STAIRS_SHAPE) + .booleanState(WATERLOGGED))); + public static final Block PALE_OAK_STAIRS = register(new Block("pale_oak_stairs", builder().destroyTime(2.0f) + .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) + .enumState(HALF) + .enumState(STAIRS_SHAPE) + .booleanState(WATERLOGGED))); public static final Block MANGROVE_STAIRS = register(new Block("mangrove_stairs", builder().destroyTime(2.0f) - .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) - .enumState(HALF) - .enumState(STAIRS_SHAPE) - .booleanState(WATERLOGGED))); + .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) + .enumState(HALF) + .enumState(STAIRS_SHAPE) + .booleanState(WATERLOGGED))); public static final Block BAMBOO_STAIRS = register(new Block("bamboo_stairs", builder().destroyTime(2.0f) - .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) - .enumState(HALF) - .enumState(STAIRS_SHAPE) - .booleanState(WATERLOGGED))); + .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) + .enumState(HALF) + .enumState(STAIRS_SHAPE) + .booleanState(WATERLOGGED))); public static final Block BAMBOO_MOSAIC_STAIRS = register(new Block("bamboo_mosaic_stairs", builder().destroyTime(2.0f) - .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) - .enumState(HALF) - .enumState(STAIRS_SHAPE) - .booleanState(WATERLOGGED))); + .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) + .enumState(HALF) + .enumState(STAIRS_SHAPE) + .booleanState(WATERLOGGED))); public static final Block SLIME_BLOCK = register(new Block("slime_block", builder())); public static final Block BARRIER = register(new Block("barrier", builder().destroyTime(-1.0f).pushReaction(PistonBehavior.BLOCK) - .booleanState(WATERLOGGED))); + .booleanState(WATERLOGGED))); public static final Block LIGHT = register(new Block("light", builder().destroyTime(-1.0f) - .intState(LEVEL) - .booleanState(WATERLOGGED))); + .intState(LEVEL) + .booleanState(WATERLOGGED))); public static final Block IRON_TRAPDOOR = register(new TrapDoorBlock("iron_trapdoor", builder().requiresCorrectToolForDrops().destroyTime(5.0f) - .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) - .enumState(HALF) - .booleanState(OPEN) - .booleanState(POWERED) - .booleanState(WATERLOGGED))); + .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) + .enumState(HALF) + .booleanState(OPEN) + .booleanState(POWERED) + .booleanState(WATERLOGGED))); public static final Block PRISMARINE = register(new Block("prismarine", builder().requiresCorrectToolForDrops().destroyTime(1.5f))); public static final Block PRISMARINE_BRICKS = register(new Block("prismarine_bricks", builder().requiresCorrectToolForDrops().destroyTime(1.5f))); public static final Block DARK_PRISMARINE = register(new Block("dark_prismarine", builder().requiresCorrectToolForDrops().destroyTime(1.5f))); public static final Block PRISMARINE_STAIRS = register(new Block("prismarine_stairs", builder().requiresCorrectToolForDrops().destroyTime(1.5f) - .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) - .enumState(HALF) - .enumState(STAIRS_SHAPE) - .booleanState(WATERLOGGED))); + .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) + .enumState(HALF) + .enumState(STAIRS_SHAPE) + .booleanState(WATERLOGGED))); public static final Block PRISMARINE_BRICK_STAIRS = register(new Block("prismarine_brick_stairs", builder().requiresCorrectToolForDrops().destroyTime(1.5f) - .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) - .enumState(HALF) - .enumState(STAIRS_SHAPE) - .booleanState(WATERLOGGED))); + .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) + .enumState(HALF) + .enumState(STAIRS_SHAPE) + .booleanState(WATERLOGGED))); public static final Block DARK_PRISMARINE_STAIRS = register(new Block("dark_prismarine_stairs", builder().requiresCorrectToolForDrops().destroyTime(1.5f) - .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) - .enumState(HALF) - .enumState(STAIRS_SHAPE) - .booleanState(WATERLOGGED))); + .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) + .enumState(HALF) + .enumState(STAIRS_SHAPE) + .booleanState(WATERLOGGED))); public static final Block PRISMARINE_SLAB = register(new Block("prismarine_slab", builder().requiresCorrectToolForDrops().destroyTime(1.5f) - .enumState(SLAB_TYPE) - .booleanState(WATERLOGGED))); + .enumState(SLAB_TYPE) + .booleanState(WATERLOGGED))); public static final Block PRISMARINE_BRICK_SLAB = register(new Block("prismarine_brick_slab", builder().requiresCorrectToolForDrops().destroyTime(1.5f) - .enumState(SLAB_TYPE) - .booleanState(WATERLOGGED))); + .enumState(SLAB_TYPE) + .booleanState(WATERLOGGED))); public static final Block DARK_PRISMARINE_SLAB = register(new Block("dark_prismarine_slab", builder().requiresCorrectToolForDrops().destroyTime(1.5f) - .enumState(SLAB_TYPE) - .booleanState(WATERLOGGED))); + .enumState(SLAB_TYPE) + .booleanState(WATERLOGGED))); public static final Block SEA_LANTERN = register(new Block("sea_lantern", builder().destroyTime(0.3f))); public static final Block HAY_BLOCK = register(new Block("hay_block", builder().destroyTime(0.5f) - .enumState(AXIS, Axis.VALUES))); + .enumState(AXIS, Axis.VALUES))); public static final Block WHITE_CARPET = register(new Block("white_carpet", builder().destroyTime(0.1f))); public static final Block ORANGE_CARPET = register(new Block("orange_carpet", builder().destroyTime(0.1f))); public static final Block MAGENTA_CARPET = register(new Block("magenta_carpet", builder().destroyTime(0.1f))); @@ -1254,415 +1303,435 @@ public final class Blocks { public static final Block COAL_BLOCK = register(new Block("coal_block", builder().requiresCorrectToolForDrops().destroyTime(5.0f))); public static final Block PACKED_ICE = register(new Block("packed_ice", builder().destroyTime(0.5f))); public static final Block SUNFLOWER = register(new Block("sunflower", builder().pushReaction(PistonBehavior.DESTROY) - .enumState(DOUBLE_BLOCK_HALF))); + .enumState(DOUBLE_BLOCK_HALF))); public static final Block LILAC = register(new Block("lilac", builder().pushReaction(PistonBehavior.DESTROY) - .enumState(DOUBLE_BLOCK_HALF))); + .enumState(DOUBLE_BLOCK_HALF))); public static final Block ROSE_BUSH = register(new Block("rose_bush", builder().pushReaction(PistonBehavior.DESTROY) - .enumState(DOUBLE_BLOCK_HALF))); + .enumState(DOUBLE_BLOCK_HALF))); public static final Block PEONY = register(new Block("peony", builder().pushReaction(PistonBehavior.DESTROY) - .enumState(DOUBLE_BLOCK_HALF))); + .enumState(DOUBLE_BLOCK_HALF))); public static final Block TALL_GRASS = register(new Block("tall_grass", builder().pushReaction(PistonBehavior.DESTROY) - .enumState(DOUBLE_BLOCK_HALF))); + .enumState(DOUBLE_BLOCK_HALF))); public static final Block LARGE_FERN = register(new Block("large_fern", builder().pushReaction(PistonBehavior.DESTROY) - .enumState(DOUBLE_BLOCK_HALF))); + .enumState(DOUBLE_BLOCK_HALF))); public static final Block WHITE_BANNER = register(new BannerBlock("white_banner", 0, builder().setBlockEntity(BlockEntityType.BANNER).destroyTime(1.0f) - .intState(ROTATION_16))); + .intState(ROTATION_16))); public static final Block ORANGE_BANNER = register(new BannerBlock("orange_banner", 1, builder().setBlockEntity(BlockEntityType.BANNER).destroyTime(1.0f) - .intState(ROTATION_16))); + .intState(ROTATION_16))); public static final Block MAGENTA_BANNER = register(new BannerBlock("magenta_banner", 2, builder().setBlockEntity(BlockEntityType.BANNER).destroyTime(1.0f) - .intState(ROTATION_16))); + .intState(ROTATION_16))); public static final Block LIGHT_BLUE_BANNER = register(new BannerBlock("light_blue_banner", 3, builder().setBlockEntity(BlockEntityType.BANNER).destroyTime(1.0f) - .intState(ROTATION_16))); + .intState(ROTATION_16))); public static final Block YELLOW_BANNER = register(new BannerBlock("yellow_banner", 4, builder().setBlockEntity(BlockEntityType.BANNER).destroyTime(1.0f) - .intState(ROTATION_16))); + .intState(ROTATION_16))); public static final Block LIME_BANNER = register(new BannerBlock("lime_banner", 5, builder().setBlockEntity(BlockEntityType.BANNER).destroyTime(1.0f) - .intState(ROTATION_16))); + .intState(ROTATION_16))); public static final Block PINK_BANNER = register(new BannerBlock("pink_banner", 6, builder().setBlockEntity(BlockEntityType.BANNER).destroyTime(1.0f) - .intState(ROTATION_16))); + .intState(ROTATION_16))); public static final Block GRAY_BANNER = register(new BannerBlock("gray_banner", 7, builder().setBlockEntity(BlockEntityType.BANNER).destroyTime(1.0f) - .intState(ROTATION_16))); + .intState(ROTATION_16))); public static final Block LIGHT_GRAY_BANNER = register(new BannerBlock("light_gray_banner", 8, builder().setBlockEntity(BlockEntityType.BANNER).destroyTime(1.0f) - .intState(ROTATION_16))); + .intState(ROTATION_16))); public static final Block CYAN_BANNER = register(new BannerBlock("cyan_banner", 9, builder().setBlockEntity(BlockEntityType.BANNER).destroyTime(1.0f) - .intState(ROTATION_16))); + .intState(ROTATION_16))); public static final Block PURPLE_BANNER = register(new BannerBlock("purple_banner", 10, builder().setBlockEntity(BlockEntityType.BANNER).destroyTime(1.0f) - .intState(ROTATION_16))); + .intState(ROTATION_16))); public static final Block BLUE_BANNER = register(new BannerBlock("blue_banner", 11, builder().setBlockEntity(BlockEntityType.BANNER).destroyTime(1.0f) - .intState(ROTATION_16))); + .intState(ROTATION_16))); public static final Block BROWN_BANNER = register(new BannerBlock("brown_banner", 12, builder().setBlockEntity(BlockEntityType.BANNER).destroyTime(1.0f) - .intState(ROTATION_16))); + .intState(ROTATION_16))); public static final Block GREEN_BANNER = register(new BannerBlock("green_banner", 13, builder().setBlockEntity(BlockEntityType.BANNER).destroyTime(1.0f) - .intState(ROTATION_16))); + .intState(ROTATION_16))); public static final Block RED_BANNER = register(new BannerBlock("red_banner", 14, builder().setBlockEntity(BlockEntityType.BANNER).destroyTime(1.0f) - .intState(ROTATION_16))); + .intState(ROTATION_16))); public static final Block BLACK_BANNER = register(new BannerBlock("black_banner", 15, builder().setBlockEntity(BlockEntityType.BANNER).destroyTime(1.0f) - .intState(ROTATION_16))); + .intState(ROTATION_16))); public static final Block WHITE_WALL_BANNER = register(new BannerBlock("white_wall_banner", 0, builder().setBlockEntity(BlockEntityType.BANNER).destroyTime(1.0f) - .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST))); + .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST))); public static final Block ORANGE_WALL_BANNER = register(new BannerBlock("orange_wall_banner", 1, builder().setBlockEntity(BlockEntityType.BANNER).destroyTime(1.0f) - .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST))); + .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST))); public static final Block MAGENTA_WALL_BANNER = register(new BannerBlock("magenta_wall_banner", 2, builder().setBlockEntity(BlockEntityType.BANNER).destroyTime(1.0f) - .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST))); + .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST))); public static final Block LIGHT_BLUE_WALL_BANNER = register(new BannerBlock("light_blue_wall_banner", 3, builder().setBlockEntity(BlockEntityType.BANNER).destroyTime(1.0f) - .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST))); + .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST))); public static final Block YELLOW_WALL_BANNER = register(new BannerBlock("yellow_wall_banner", 4, builder().setBlockEntity(BlockEntityType.BANNER).destroyTime(1.0f) - .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST))); + .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST))); public static final Block LIME_WALL_BANNER = register(new BannerBlock("lime_wall_banner", 5, builder().setBlockEntity(BlockEntityType.BANNER).destroyTime(1.0f) - .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST))); + .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST))); public static final Block PINK_WALL_BANNER = register(new BannerBlock("pink_wall_banner", 6, builder().setBlockEntity(BlockEntityType.BANNER).destroyTime(1.0f) - .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST))); + .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST))); public static final Block GRAY_WALL_BANNER = register(new BannerBlock("gray_wall_banner", 7, builder().setBlockEntity(BlockEntityType.BANNER).destroyTime(1.0f) - .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST))); + .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST))); public static final Block LIGHT_GRAY_WALL_BANNER = register(new BannerBlock("light_gray_wall_banner", 8, builder().setBlockEntity(BlockEntityType.BANNER).destroyTime(1.0f) - .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST))); + .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST))); public static final Block CYAN_WALL_BANNER = register(new BannerBlock("cyan_wall_banner", 9, builder().setBlockEntity(BlockEntityType.BANNER).destroyTime(1.0f) - .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST))); + .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST))); public static final Block PURPLE_WALL_BANNER = register(new BannerBlock("purple_wall_banner", 10, builder().setBlockEntity(BlockEntityType.BANNER).destroyTime(1.0f) - .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST))); + .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST))); public static final Block BLUE_WALL_BANNER = register(new BannerBlock("blue_wall_banner", 11, builder().setBlockEntity(BlockEntityType.BANNER).destroyTime(1.0f) - .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST))); + .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST))); public static final Block BROWN_WALL_BANNER = register(new BannerBlock("brown_wall_banner", 12, builder().setBlockEntity(BlockEntityType.BANNER).destroyTime(1.0f) - .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST))); + .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST))); public static final Block GREEN_WALL_BANNER = register(new BannerBlock("green_wall_banner", 13, builder().setBlockEntity(BlockEntityType.BANNER).destroyTime(1.0f) - .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST))); + .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST))); public static final Block RED_WALL_BANNER = register(new BannerBlock("red_wall_banner", 14, builder().setBlockEntity(BlockEntityType.BANNER).destroyTime(1.0f) - .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST))); + .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST))); public static final Block BLACK_WALL_BANNER = register(new BannerBlock("black_wall_banner", 15, builder().setBlockEntity(BlockEntityType.BANNER).destroyTime(1.0f) - .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST))); + .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST))); public static final Block RED_SANDSTONE = register(new Block("red_sandstone", builder().requiresCorrectToolForDrops().destroyTime(0.8f))); public static final Block CHISELED_RED_SANDSTONE = register(new Block("chiseled_red_sandstone", builder().requiresCorrectToolForDrops().destroyTime(0.8f))); public static final Block CUT_RED_SANDSTONE = register(new Block("cut_red_sandstone", builder().requiresCorrectToolForDrops().destroyTime(0.8f))); public static final Block RED_SANDSTONE_STAIRS = register(new Block("red_sandstone_stairs", builder().requiresCorrectToolForDrops().destroyTime(0.8f) - .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) - .enumState(HALF) - .enumState(STAIRS_SHAPE) - .booleanState(WATERLOGGED))); + .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) + .enumState(HALF) + .enumState(STAIRS_SHAPE) + .booleanState(WATERLOGGED))); public static final Block OAK_SLAB = register(new Block("oak_slab", builder().destroyTime(2.0f) - .enumState(SLAB_TYPE) - .booleanState(WATERLOGGED))); + .enumState(SLAB_TYPE) + .booleanState(WATERLOGGED))); public static final Block SPRUCE_SLAB = register(new Block("spruce_slab", builder().destroyTime(2.0f) - .enumState(SLAB_TYPE) - .booleanState(WATERLOGGED))); + .enumState(SLAB_TYPE) + .booleanState(WATERLOGGED))); public static final Block BIRCH_SLAB = register(new Block("birch_slab", builder().destroyTime(2.0f) - .enumState(SLAB_TYPE) - .booleanState(WATERLOGGED))); + .enumState(SLAB_TYPE) + .booleanState(WATERLOGGED))); public static final Block JUNGLE_SLAB = register(new Block("jungle_slab", builder().destroyTime(2.0f) - .enumState(SLAB_TYPE) - .booleanState(WATERLOGGED))); + .enumState(SLAB_TYPE) + .booleanState(WATERLOGGED))); public static final Block ACACIA_SLAB = register(new Block("acacia_slab", builder().destroyTime(2.0f) - .enumState(SLAB_TYPE) - .booleanState(WATERLOGGED))); + .enumState(SLAB_TYPE) + .booleanState(WATERLOGGED))); public static final Block CHERRY_SLAB = register(new Block("cherry_slab", builder().destroyTime(2.0f) - .enumState(SLAB_TYPE) - .booleanState(WATERLOGGED))); + .enumState(SLAB_TYPE) + .booleanState(WATERLOGGED))); public static final Block DARK_OAK_SLAB = register(new Block("dark_oak_slab", builder().destroyTime(2.0f) - .enumState(SLAB_TYPE) - .booleanState(WATERLOGGED))); + .enumState(SLAB_TYPE) + .booleanState(WATERLOGGED))); + public static final Block PALE_OAK_SLAB = register(new Block("pale_oak_slab", builder().destroyTime(2.0f) + .enumState(SLAB_TYPE) + .booleanState(WATERLOGGED))); public static final Block MANGROVE_SLAB = register(new Block("mangrove_slab", builder().destroyTime(2.0f) - .enumState(SLAB_TYPE) - .booleanState(WATERLOGGED))); + .enumState(SLAB_TYPE) + .booleanState(WATERLOGGED))); public static final Block BAMBOO_SLAB = register(new Block("bamboo_slab", builder().destroyTime(2.0f) - .enumState(SLAB_TYPE) - .booleanState(WATERLOGGED))); + .enumState(SLAB_TYPE) + .booleanState(WATERLOGGED))); public static final Block BAMBOO_MOSAIC_SLAB = register(new Block("bamboo_mosaic_slab", builder().destroyTime(2.0f) - .enumState(SLAB_TYPE) - .booleanState(WATERLOGGED))); + .enumState(SLAB_TYPE) + .booleanState(WATERLOGGED))); public static final Block STONE_SLAB = register(new Block("stone_slab", builder().requiresCorrectToolForDrops().destroyTime(2.0f) - .enumState(SLAB_TYPE) - .booleanState(WATERLOGGED))); + .enumState(SLAB_TYPE) + .booleanState(WATERLOGGED))); public static final Block SMOOTH_STONE_SLAB = register(new Block("smooth_stone_slab", builder().requiresCorrectToolForDrops().destroyTime(2.0f) - .enumState(SLAB_TYPE) - .booleanState(WATERLOGGED))); + .enumState(SLAB_TYPE) + .booleanState(WATERLOGGED))); public static final Block SANDSTONE_SLAB = register(new Block("sandstone_slab", builder().requiresCorrectToolForDrops().destroyTime(2.0f) - .enumState(SLAB_TYPE) - .booleanState(WATERLOGGED))); + .enumState(SLAB_TYPE) + .booleanState(WATERLOGGED))); public static final Block CUT_SANDSTONE_SLAB = register(new Block("cut_sandstone_slab", builder().requiresCorrectToolForDrops().destroyTime(2.0f) - .enumState(SLAB_TYPE) - .booleanState(WATERLOGGED))); + .enumState(SLAB_TYPE) + .booleanState(WATERLOGGED))); public static final Block PETRIFIED_OAK_SLAB = register(new Block("petrified_oak_slab", builder().requiresCorrectToolForDrops().destroyTime(2.0f) - .enumState(SLAB_TYPE) - .booleanState(WATERLOGGED))); + .enumState(SLAB_TYPE) + .booleanState(WATERLOGGED))); public static final Block COBBLESTONE_SLAB = register(new Block("cobblestone_slab", builder().requiresCorrectToolForDrops().destroyTime(2.0f) - .enumState(SLAB_TYPE) - .booleanState(WATERLOGGED))); + .enumState(SLAB_TYPE) + .booleanState(WATERLOGGED))); public static final Block BRICK_SLAB = register(new Block("brick_slab", builder().requiresCorrectToolForDrops().destroyTime(2.0f) - .enumState(SLAB_TYPE) - .booleanState(WATERLOGGED))); + .enumState(SLAB_TYPE) + .booleanState(WATERLOGGED))); public static final Block STONE_BRICK_SLAB = register(new Block("stone_brick_slab", builder().requiresCorrectToolForDrops().destroyTime(2.0f) - .enumState(SLAB_TYPE) - .booleanState(WATERLOGGED))); + .enumState(SLAB_TYPE) + .booleanState(WATERLOGGED))); public static final Block MUD_BRICK_SLAB = register(new Block("mud_brick_slab", builder().requiresCorrectToolForDrops().destroyTime(1.5f) - .enumState(SLAB_TYPE) - .booleanState(WATERLOGGED))); + .enumState(SLAB_TYPE) + .booleanState(WATERLOGGED))); public static final Block NETHER_BRICK_SLAB = register(new Block("nether_brick_slab", builder().requiresCorrectToolForDrops().destroyTime(2.0f) - .enumState(SLAB_TYPE) - .booleanState(WATERLOGGED))); + .enumState(SLAB_TYPE) + .booleanState(WATERLOGGED))); public static final Block QUARTZ_SLAB = register(new Block("quartz_slab", builder().requiresCorrectToolForDrops().destroyTime(2.0f) - .enumState(SLAB_TYPE) - .booleanState(WATERLOGGED))); + .enumState(SLAB_TYPE) + .booleanState(WATERLOGGED))); public static final Block RED_SANDSTONE_SLAB = register(new Block("red_sandstone_slab", builder().requiresCorrectToolForDrops().destroyTime(2.0f) - .enumState(SLAB_TYPE) - .booleanState(WATERLOGGED))); + .enumState(SLAB_TYPE) + .booleanState(WATERLOGGED))); public static final Block CUT_RED_SANDSTONE_SLAB = register(new Block("cut_red_sandstone_slab", builder().requiresCorrectToolForDrops().destroyTime(2.0f) - .enumState(SLAB_TYPE) - .booleanState(WATERLOGGED))); + .enumState(SLAB_TYPE) + .booleanState(WATERLOGGED))); public static final Block PURPUR_SLAB = register(new Block("purpur_slab", builder().requiresCorrectToolForDrops().destroyTime(2.0f) - .enumState(SLAB_TYPE) - .booleanState(WATERLOGGED))); + .enumState(SLAB_TYPE) + .booleanState(WATERLOGGED))); public static final Block SMOOTH_STONE = register(new Block("smooth_stone", builder().requiresCorrectToolForDrops().destroyTime(2.0f))); public static final Block SMOOTH_SANDSTONE = register(new Block("smooth_sandstone", builder().requiresCorrectToolForDrops().destroyTime(2.0f))); public static final Block SMOOTH_QUARTZ = register(new Block("smooth_quartz", builder().requiresCorrectToolForDrops().destroyTime(2.0f))); public static final Block SMOOTH_RED_SANDSTONE = register(new Block("smooth_red_sandstone", builder().requiresCorrectToolForDrops().destroyTime(2.0f))); public static final Block SPRUCE_FENCE_GATE = register(new Block("spruce_fence_gate", builder().destroyTime(2.0f) - .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) - .booleanState(IN_WALL) - .booleanState(OPEN) - .booleanState(POWERED))); + .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) + .booleanState(IN_WALL) + .booleanState(OPEN) + .booleanState(POWERED))); public static final Block BIRCH_FENCE_GATE = register(new Block("birch_fence_gate", builder().destroyTime(2.0f) - .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) - .booleanState(IN_WALL) - .booleanState(OPEN) - .booleanState(POWERED))); + .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) + .booleanState(IN_WALL) + .booleanState(OPEN) + .booleanState(POWERED))); public static final Block JUNGLE_FENCE_GATE = register(new Block("jungle_fence_gate", builder().destroyTime(2.0f) - .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) - .booleanState(IN_WALL) - .booleanState(OPEN) - .booleanState(POWERED))); + .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) + .booleanState(IN_WALL) + .booleanState(OPEN) + .booleanState(POWERED))); public static final Block ACACIA_FENCE_GATE = register(new Block("acacia_fence_gate", builder().destroyTime(2.0f) - .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) - .booleanState(IN_WALL) - .booleanState(OPEN) - .booleanState(POWERED))); + .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) + .booleanState(IN_WALL) + .booleanState(OPEN) + .booleanState(POWERED))); public static final Block CHERRY_FENCE_GATE = register(new Block("cherry_fence_gate", builder().destroyTime(2.0f) - .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) - .booleanState(IN_WALL) - .booleanState(OPEN) - .booleanState(POWERED))); + .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) + .booleanState(IN_WALL) + .booleanState(OPEN) + .booleanState(POWERED))); public static final Block DARK_OAK_FENCE_GATE = register(new Block("dark_oak_fence_gate", builder().destroyTime(2.0f) - .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) - .booleanState(IN_WALL) - .booleanState(OPEN) - .booleanState(POWERED))); + .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) + .booleanState(IN_WALL) + .booleanState(OPEN) + .booleanState(POWERED))); + public static final Block PALE_OAK_FENCE_GATE = register(new Block("pale_oak_fence_gate", builder().destroyTime(2.0f) + .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) + .booleanState(IN_WALL) + .booleanState(OPEN) + .booleanState(POWERED))); public static final Block MANGROVE_FENCE_GATE = register(new Block("mangrove_fence_gate", builder().destroyTime(2.0f) - .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) - .booleanState(IN_WALL) - .booleanState(OPEN) - .booleanState(POWERED))); + .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) + .booleanState(IN_WALL) + .booleanState(OPEN) + .booleanState(POWERED))); public static final Block BAMBOO_FENCE_GATE = register(new Block("bamboo_fence_gate", builder().destroyTime(2.0f) - .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) - .booleanState(IN_WALL) - .booleanState(OPEN) - .booleanState(POWERED))); + .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) + .booleanState(IN_WALL) + .booleanState(OPEN) + .booleanState(POWERED))); public static final Block SPRUCE_FENCE = register(new Block("spruce_fence", builder().destroyTime(2.0f) - .booleanState(EAST) - .booleanState(NORTH) - .booleanState(SOUTH) - .booleanState(WATERLOGGED) - .booleanState(WEST))); + .booleanState(EAST) + .booleanState(NORTH) + .booleanState(SOUTH) + .booleanState(WATERLOGGED) + .booleanState(WEST))); public static final Block BIRCH_FENCE = register(new Block("birch_fence", builder().destroyTime(2.0f) - .booleanState(EAST) - .booleanState(NORTH) - .booleanState(SOUTH) - .booleanState(WATERLOGGED) - .booleanState(WEST))); + .booleanState(EAST) + .booleanState(NORTH) + .booleanState(SOUTH) + .booleanState(WATERLOGGED) + .booleanState(WEST))); public static final Block JUNGLE_FENCE = register(new Block("jungle_fence", builder().destroyTime(2.0f) - .booleanState(EAST) - .booleanState(NORTH) - .booleanState(SOUTH) - .booleanState(WATERLOGGED) - .booleanState(WEST))); + .booleanState(EAST) + .booleanState(NORTH) + .booleanState(SOUTH) + .booleanState(WATERLOGGED) + .booleanState(WEST))); public static final Block ACACIA_FENCE = register(new Block("acacia_fence", builder().destroyTime(2.0f) - .booleanState(EAST) - .booleanState(NORTH) - .booleanState(SOUTH) - .booleanState(WATERLOGGED) - .booleanState(WEST))); + .booleanState(EAST) + .booleanState(NORTH) + .booleanState(SOUTH) + .booleanState(WATERLOGGED) + .booleanState(WEST))); public static final Block CHERRY_FENCE = register(new Block("cherry_fence", builder().destroyTime(2.0f) - .booleanState(EAST) - .booleanState(NORTH) - .booleanState(SOUTH) - .booleanState(WATERLOGGED) - .booleanState(WEST))); + .booleanState(EAST) + .booleanState(NORTH) + .booleanState(SOUTH) + .booleanState(WATERLOGGED) + .booleanState(WEST))); public static final Block DARK_OAK_FENCE = register(new Block("dark_oak_fence", builder().destroyTime(2.0f) - .booleanState(EAST) - .booleanState(NORTH) - .booleanState(SOUTH) - .booleanState(WATERLOGGED) - .booleanState(WEST))); + .booleanState(EAST) + .booleanState(NORTH) + .booleanState(SOUTH) + .booleanState(WATERLOGGED) + .booleanState(WEST))); + public static final Block PALE_OAK_FENCE = register(new Block("pale_oak_fence", builder().destroyTime(2.0f) + .booleanState(EAST) + .booleanState(NORTH) + .booleanState(SOUTH) + .booleanState(WATERLOGGED) + .booleanState(WEST))); public static final Block MANGROVE_FENCE = register(new Block("mangrove_fence", builder().destroyTime(2.0f) - .booleanState(EAST) - .booleanState(NORTH) - .booleanState(SOUTH) - .booleanState(WATERLOGGED) - .booleanState(WEST))); + .booleanState(EAST) + .booleanState(NORTH) + .booleanState(SOUTH) + .booleanState(WATERLOGGED) + .booleanState(WEST))); public static final Block BAMBOO_FENCE = register(new Block("bamboo_fence", builder().destroyTime(2.0f) - .booleanState(EAST) - .booleanState(NORTH) - .booleanState(SOUTH) - .booleanState(WATERLOGGED) - .booleanState(WEST))); + .booleanState(EAST) + .booleanState(NORTH) + .booleanState(SOUTH) + .booleanState(WATERLOGGED) + .booleanState(WEST))); public static final Block SPRUCE_DOOR = register(new DoorBlock("spruce_door", builder().destroyTime(3.0f).pushReaction(PistonBehavior.DESTROY) - .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) - .enumState(DOUBLE_BLOCK_HALF) - .enumState(DOOR_HINGE) - .booleanState(OPEN) - .booleanState(POWERED))); + .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) + .enumState(DOUBLE_BLOCK_HALF) + .enumState(DOOR_HINGE) + .booleanState(OPEN) + .booleanState(POWERED))); public static final Block BIRCH_DOOR = register(new DoorBlock("birch_door", builder().destroyTime(3.0f).pushReaction(PistonBehavior.DESTROY) - .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) - .enumState(DOUBLE_BLOCK_HALF) - .enumState(DOOR_HINGE) - .booleanState(OPEN) - .booleanState(POWERED))); + .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) + .enumState(DOUBLE_BLOCK_HALF) + .enumState(DOOR_HINGE) + .booleanState(OPEN) + .booleanState(POWERED))); public static final Block JUNGLE_DOOR = register(new DoorBlock("jungle_door", builder().destroyTime(3.0f).pushReaction(PistonBehavior.DESTROY) - .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) - .enumState(DOUBLE_BLOCK_HALF) - .enumState(DOOR_HINGE) - .booleanState(OPEN) - .booleanState(POWERED))); + .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) + .enumState(DOUBLE_BLOCK_HALF) + .enumState(DOOR_HINGE) + .booleanState(OPEN) + .booleanState(POWERED))); public static final Block ACACIA_DOOR = register(new DoorBlock("acacia_door", builder().destroyTime(3.0f).pushReaction(PistonBehavior.DESTROY) - .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) - .enumState(DOUBLE_BLOCK_HALF) - .enumState(DOOR_HINGE) - .booleanState(OPEN) - .booleanState(POWERED))); + .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) + .enumState(DOUBLE_BLOCK_HALF) + .enumState(DOOR_HINGE) + .booleanState(OPEN) + .booleanState(POWERED))); public static final Block CHERRY_DOOR = register(new DoorBlock("cherry_door", builder().destroyTime(3.0f).pushReaction(PistonBehavior.DESTROY) - .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) - .enumState(DOUBLE_BLOCK_HALF) - .enumState(DOOR_HINGE) - .booleanState(OPEN) - .booleanState(POWERED))); + .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) + .enumState(DOUBLE_BLOCK_HALF) + .enumState(DOOR_HINGE) + .booleanState(OPEN) + .booleanState(POWERED))); public static final Block DARK_OAK_DOOR = register(new DoorBlock("dark_oak_door", builder().destroyTime(3.0f).pushReaction(PistonBehavior.DESTROY) - .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) - .enumState(DOUBLE_BLOCK_HALF) - .enumState(DOOR_HINGE) - .booleanState(OPEN) - .booleanState(POWERED))); + .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) + .enumState(DOUBLE_BLOCK_HALF) + .enumState(DOOR_HINGE) + .booleanState(OPEN) + .booleanState(POWERED))); + public static final Block PALE_OAK_DOOR = register(new DoorBlock("pale_oak_door", builder().destroyTime(3.0f).pushReaction(PistonBehavior.DESTROY) + .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) + .enumState(DOUBLE_BLOCK_HALF) + .enumState(DOOR_HINGE) + .booleanState(OPEN) + .booleanState(POWERED))); public static final Block MANGROVE_DOOR = register(new DoorBlock("mangrove_door", builder().destroyTime(3.0f).pushReaction(PistonBehavior.DESTROY) - .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) - .enumState(DOUBLE_BLOCK_HALF) - .enumState(DOOR_HINGE) - .booleanState(OPEN) - .booleanState(POWERED))); + .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) + .enumState(DOUBLE_BLOCK_HALF) + .enumState(DOOR_HINGE) + .booleanState(OPEN) + .booleanState(POWERED))); public static final Block BAMBOO_DOOR = register(new DoorBlock("bamboo_door", builder().destroyTime(3.0f).pushReaction(PistonBehavior.DESTROY) - .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) - .enumState(DOUBLE_BLOCK_HALF) - .enumState(DOOR_HINGE) - .booleanState(OPEN) - .booleanState(POWERED))); + .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) + .enumState(DOUBLE_BLOCK_HALF) + .enumState(DOOR_HINGE) + .booleanState(OPEN) + .booleanState(POWERED))); public static final Block END_ROD = register(new Block("end_rod", builder() - .enumState(FACING, Direction.NORTH, Direction.EAST, Direction.SOUTH, Direction.WEST, Direction.UP, Direction.DOWN))); + .enumState(FACING, Direction.NORTH, Direction.EAST, Direction.SOUTH, Direction.WEST, Direction.UP, Direction.DOWN))); public static final Block CHORUS_PLANT = register(new Block("chorus_plant", builder().destroyTime(0.4f).pushReaction(PistonBehavior.DESTROY) - .booleanState(DOWN) - .booleanState(EAST) - .booleanState(NORTH) - .booleanState(SOUTH) - .booleanState(UP) - .booleanState(WEST))); + .booleanState(DOWN) + .booleanState(EAST) + .booleanState(NORTH) + .booleanState(SOUTH) + .booleanState(UP) + .booleanState(WEST))); public static final Block CHORUS_FLOWER = register(new Block("chorus_flower", builder().destroyTime(0.4f).pushReaction(PistonBehavior.DESTROY) - .intState(AGE_5))); + .intState(AGE_5))); public static final Block PURPUR_BLOCK = register(new Block("purpur_block", builder().requiresCorrectToolForDrops().destroyTime(1.5f))); public static final Block PURPUR_PILLAR = register(new Block("purpur_pillar", builder().requiresCorrectToolForDrops().destroyTime(1.5f) - .enumState(AXIS, Axis.VALUES))); + .enumState(AXIS, Axis.VALUES))); public static final Block PURPUR_STAIRS = register(new Block("purpur_stairs", builder().requiresCorrectToolForDrops().destroyTime(1.5f) - .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) - .enumState(HALF) - .enumState(STAIRS_SHAPE) - .booleanState(WATERLOGGED))); + .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) + .enumState(HALF) + .enumState(STAIRS_SHAPE) + .booleanState(WATERLOGGED))); public static final Block END_STONE_BRICKS = register(new Block("end_stone_bricks", builder().requiresCorrectToolForDrops().destroyTime(3.0f))); public static final Block TORCHFLOWER_CROP = register(new Block("torchflower_crop", builder().pushReaction(PistonBehavior.DESTROY) - .intState(AGE_1))); + .intState(AGE_1))); public static final Block PITCHER_CROP = register(new Block("pitcher_crop", builder().pushReaction(PistonBehavior.DESTROY) - .intState(AGE_4) - .enumState(DOUBLE_BLOCK_HALF))); + .intState(AGE_4) + .enumState(DOUBLE_BLOCK_HALF))); public static final Block PITCHER_PLANT = register(new Block("pitcher_plant", builder().pushReaction(PistonBehavior.DESTROY) - .enumState(DOUBLE_BLOCK_HALF))); + .enumState(DOUBLE_BLOCK_HALF))); public static final Block BEETROOTS = register(new Block("beetroots", builder().pushReaction(PistonBehavior.DESTROY) - .intState(AGE_3))); + .intState(AGE_3))); public static final Block DIRT_PATH = register(new Block("dirt_path", builder().destroyTime(0.65f))); public static final Block END_GATEWAY = register(new Block("end_gateway", builder().setBlockEntity(BlockEntityType.END_GATEWAY).destroyTime(-1.0f).pushReaction(PistonBehavior.BLOCK))); public static final Block REPEATING_COMMAND_BLOCK = register(new Block("repeating_command_block", builder().setBlockEntity(BlockEntityType.COMMAND_BLOCK).requiresCorrectToolForDrops().destroyTime(-1.0f) - .booleanState(CONDITIONAL) - .enumState(FACING, Direction.NORTH, Direction.EAST, Direction.SOUTH, Direction.WEST, Direction.UP, Direction.DOWN))); + .booleanState(CONDITIONAL) + .enumState(FACING, Direction.NORTH, Direction.EAST, Direction.SOUTH, Direction.WEST, Direction.UP, Direction.DOWN))); public static final Block CHAIN_COMMAND_BLOCK = register(new Block("chain_command_block", builder().setBlockEntity(BlockEntityType.COMMAND_BLOCK).requiresCorrectToolForDrops().destroyTime(-1.0f) - .booleanState(CONDITIONAL) - .enumState(FACING, Direction.NORTH, Direction.EAST, Direction.SOUTH, Direction.WEST, Direction.UP, Direction.DOWN))); + .booleanState(CONDITIONAL) + .enumState(FACING, Direction.NORTH, Direction.EAST, Direction.SOUTH, Direction.WEST, Direction.UP, Direction.DOWN))); public static final Block FROSTED_ICE = register(new Block("frosted_ice", builder().destroyTime(0.5f) - .intState(AGE_3))); + .intState(AGE_3))); public static final Block MAGMA_BLOCK = register(new Block("magma_block", builder().requiresCorrectToolForDrops().destroyTime(0.5f))); public static final Block NETHER_WART_BLOCK = register(new Block("nether_wart_block", builder().destroyTime(1.0f))); public static final Block RED_NETHER_BRICKS = register(new Block("red_nether_bricks", builder().requiresCorrectToolForDrops().destroyTime(2.0f))); public static final Block BONE_BLOCK = register(new Block("bone_block", builder().requiresCorrectToolForDrops().destroyTime(2.0f) - .enumState(AXIS, Axis.VALUES))); + .enumState(AXIS, Axis.VALUES))); public static final Block STRUCTURE_VOID = register(new Block("structure_void", builder().pushReaction(PistonBehavior.DESTROY))); public static final Block OBSERVER = register(new Block("observer", builder().requiresCorrectToolForDrops().destroyTime(3.0f) - .enumState(FACING, Direction.NORTH, Direction.EAST, Direction.SOUTH, Direction.WEST, Direction.UP, Direction.DOWN) - .booleanState(POWERED))); + .enumState(FACING, Direction.NORTH, Direction.EAST, Direction.SOUTH, Direction.WEST, Direction.UP, Direction.DOWN) + .booleanState(POWERED))); public static final Block SHULKER_BOX = register(new Block("shulker_box", builder().setBlockEntity(BlockEntityType.SHULKER_BOX).destroyTime(2.0f).pushReaction(PistonBehavior.DESTROY) - .enumState(FACING, Direction.NORTH, Direction.EAST, Direction.SOUTH, Direction.WEST, Direction.UP, Direction.DOWN))); + .enumState(FACING, Direction.NORTH, Direction.EAST, Direction.SOUTH, Direction.WEST, Direction.UP, Direction.DOWN))); public static final Block WHITE_SHULKER_BOX = register(new Block("white_shulker_box", builder().setBlockEntity(BlockEntityType.SHULKER_BOX).destroyTime(2.0f).pushReaction(PistonBehavior.DESTROY) - .enumState(FACING, Direction.NORTH, Direction.EAST, Direction.SOUTH, Direction.WEST, Direction.UP, Direction.DOWN))); + .enumState(FACING, Direction.NORTH, Direction.EAST, Direction.SOUTH, Direction.WEST, Direction.UP, Direction.DOWN))); public static final Block ORANGE_SHULKER_BOX = register(new Block("orange_shulker_box", builder().setBlockEntity(BlockEntityType.SHULKER_BOX).destroyTime(2.0f).pushReaction(PistonBehavior.DESTROY) - .enumState(FACING, Direction.NORTH, Direction.EAST, Direction.SOUTH, Direction.WEST, Direction.UP, Direction.DOWN))); + .enumState(FACING, Direction.NORTH, Direction.EAST, Direction.SOUTH, Direction.WEST, Direction.UP, Direction.DOWN))); public static final Block MAGENTA_SHULKER_BOX = register(new Block("magenta_shulker_box", builder().setBlockEntity(BlockEntityType.SHULKER_BOX).destroyTime(2.0f).pushReaction(PistonBehavior.DESTROY) - .enumState(FACING, Direction.NORTH, Direction.EAST, Direction.SOUTH, Direction.WEST, Direction.UP, Direction.DOWN))); + .enumState(FACING, Direction.NORTH, Direction.EAST, Direction.SOUTH, Direction.WEST, Direction.UP, Direction.DOWN))); public static final Block LIGHT_BLUE_SHULKER_BOX = register(new Block("light_blue_shulker_box", builder().setBlockEntity(BlockEntityType.SHULKER_BOX).destroyTime(2.0f).pushReaction(PistonBehavior.DESTROY) - .enumState(FACING, Direction.NORTH, Direction.EAST, Direction.SOUTH, Direction.WEST, Direction.UP, Direction.DOWN))); + .enumState(FACING, Direction.NORTH, Direction.EAST, Direction.SOUTH, Direction.WEST, Direction.UP, Direction.DOWN))); public static final Block YELLOW_SHULKER_BOX = register(new Block("yellow_shulker_box", builder().setBlockEntity(BlockEntityType.SHULKER_BOX).destroyTime(2.0f).pushReaction(PistonBehavior.DESTROY) - .enumState(FACING, Direction.NORTH, Direction.EAST, Direction.SOUTH, Direction.WEST, Direction.UP, Direction.DOWN))); + .enumState(FACING, Direction.NORTH, Direction.EAST, Direction.SOUTH, Direction.WEST, Direction.UP, Direction.DOWN))); public static final Block LIME_SHULKER_BOX = register(new Block("lime_shulker_box", builder().setBlockEntity(BlockEntityType.SHULKER_BOX).destroyTime(2.0f).pushReaction(PistonBehavior.DESTROY) - .enumState(FACING, Direction.NORTH, Direction.EAST, Direction.SOUTH, Direction.WEST, Direction.UP, Direction.DOWN))); + .enumState(FACING, Direction.NORTH, Direction.EAST, Direction.SOUTH, Direction.WEST, Direction.UP, Direction.DOWN))); public static final Block PINK_SHULKER_BOX = register(new Block("pink_shulker_box", builder().setBlockEntity(BlockEntityType.SHULKER_BOX).destroyTime(2.0f).pushReaction(PistonBehavior.DESTROY) - .enumState(FACING, Direction.NORTH, Direction.EAST, Direction.SOUTH, Direction.WEST, Direction.UP, Direction.DOWN))); + .enumState(FACING, Direction.NORTH, Direction.EAST, Direction.SOUTH, Direction.WEST, Direction.UP, Direction.DOWN))); public static final Block GRAY_SHULKER_BOX = register(new Block("gray_shulker_box", builder().setBlockEntity(BlockEntityType.SHULKER_BOX).destroyTime(2.0f).pushReaction(PistonBehavior.DESTROY) - .enumState(FACING, Direction.NORTH, Direction.EAST, Direction.SOUTH, Direction.WEST, Direction.UP, Direction.DOWN))); + .enumState(FACING, Direction.NORTH, Direction.EAST, Direction.SOUTH, Direction.WEST, Direction.UP, Direction.DOWN))); public static final Block LIGHT_GRAY_SHULKER_BOX = register(new Block("light_gray_shulker_box", builder().setBlockEntity(BlockEntityType.SHULKER_BOX).destroyTime(2.0f).pushReaction(PistonBehavior.DESTROY) - .enumState(FACING, Direction.NORTH, Direction.EAST, Direction.SOUTH, Direction.WEST, Direction.UP, Direction.DOWN))); + .enumState(FACING, Direction.NORTH, Direction.EAST, Direction.SOUTH, Direction.WEST, Direction.UP, Direction.DOWN))); public static final Block CYAN_SHULKER_BOX = register(new Block("cyan_shulker_box", builder().setBlockEntity(BlockEntityType.SHULKER_BOX).destroyTime(2.0f).pushReaction(PistonBehavior.DESTROY) - .enumState(FACING, Direction.NORTH, Direction.EAST, Direction.SOUTH, Direction.WEST, Direction.UP, Direction.DOWN))); + .enumState(FACING, Direction.NORTH, Direction.EAST, Direction.SOUTH, Direction.WEST, Direction.UP, Direction.DOWN))); public static final Block PURPLE_SHULKER_BOX = register(new Block("purple_shulker_box", builder().setBlockEntity(BlockEntityType.SHULKER_BOX).destroyTime(2.0f).pushReaction(PistonBehavior.DESTROY) - .enumState(FACING, Direction.NORTH, Direction.EAST, Direction.SOUTH, Direction.WEST, Direction.UP, Direction.DOWN))); + .enumState(FACING, Direction.NORTH, Direction.EAST, Direction.SOUTH, Direction.WEST, Direction.UP, Direction.DOWN))); public static final Block BLUE_SHULKER_BOX = register(new Block("blue_shulker_box", builder().setBlockEntity(BlockEntityType.SHULKER_BOX).destroyTime(2.0f).pushReaction(PistonBehavior.DESTROY) - .enumState(FACING, Direction.NORTH, Direction.EAST, Direction.SOUTH, Direction.WEST, Direction.UP, Direction.DOWN))); + .enumState(FACING, Direction.NORTH, Direction.EAST, Direction.SOUTH, Direction.WEST, Direction.UP, Direction.DOWN))); public static final Block BROWN_SHULKER_BOX = register(new Block("brown_shulker_box", builder().setBlockEntity(BlockEntityType.SHULKER_BOX).destroyTime(2.0f).pushReaction(PistonBehavior.DESTROY) - .enumState(FACING, Direction.NORTH, Direction.EAST, Direction.SOUTH, Direction.WEST, Direction.UP, Direction.DOWN))); + .enumState(FACING, Direction.NORTH, Direction.EAST, Direction.SOUTH, Direction.WEST, Direction.UP, Direction.DOWN))); public static final Block GREEN_SHULKER_BOX = register(new Block("green_shulker_box", builder().setBlockEntity(BlockEntityType.SHULKER_BOX).destroyTime(2.0f).pushReaction(PistonBehavior.DESTROY) - .enumState(FACING, Direction.NORTH, Direction.EAST, Direction.SOUTH, Direction.WEST, Direction.UP, Direction.DOWN))); + .enumState(FACING, Direction.NORTH, Direction.EAST, Direction.SOUTH, Direction.WEST, Direction.UP, Direction.DOWN))); public static final Block RED_SHULKER_BOX = register(new Block("red_shulker_box", builder().setBlockEntity(BlockEntityType.SHULKER_BOX).destroyTime(2.0f).pushReaction(PistonBehavior.DESTROY) - .enumState(FACING, Direction.NORTH, Direction.EAST, Direction.SOUTH, Direction.WEST, Direction.UP, Direction.DOWN))); + .enumState(FACING, Direction.NORTH, Direction.EAST, Direction.SOUTH, Direction.WEST, Direction.UP, Direction.DOWN))); public static final Block BLACK_SHULKER_BOX = register(new Block("black_shulker_box", builder().setBlockEntity(BlockEntityType.SHULKER_BOX).destroyTime(2.0f).pushReaction(PistonBehavior.DESTROY) - .enumState(FACING, Direction.NORTH, Direction.EAST, Direction.SOUTH, Direction.WEST, Direction.UP, Direction.DOWN))); + .enumState(FACING, Direction.NORTH, Direction.EAST, Direction.SOUTH, Direction.WEST, Direction.UP, Direction.DOWN))); public static final Block WHITE_GLAZED_TERRACOTTA = register(new Block("white_glazed_terracotta", builder().requiresCorrectToolForDrops().destroyTime(1.4f).pushReaction(PistonBehavior.PUSH_ONLY) - .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST))); + .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST))); public static final Block ORANGE_GLAZED_TERRACOTTA = register(new Block("orange_glazed_terracotta", builder().requiresCorrectToolForDrops().destroyTime(1.4f).pushReaction(PistonBehavior.PUSH_ONLY) - .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST))); + .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST))); public static final Block MAGENTA_GLAZED_TERRACOTTA = register(new Block("magenta_glazed_terracotta", builder().requiresCorrectToolForDrops().destroyTime(1.4f).pushReaction(PistonBehavior.PUSH_ONLY) - .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST))); + .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST))); public static final Block LIGHT_BLUE_GLAZED_TERRACOTTA = register(new Block("light_blue_glazed_terracotta", builder().requiresCorrectToolForDrops().destroyTime(1.4f).pushReaction(PistonBehavior.PUSH_ONLY) - .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST))); + .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST))); public static final Block YELLOW_GLAZED_TERRACOTTA = register(new Block("yellow_glazed_terracotta", builder().requiresCorrectToolForDrops().destroyTime(1.4f).pushReaction(PistonBehavior.PUSH_ONLY) - .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST))); + .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST))); public static final Block LIME_GLAZED_TERRACOTTA = register(new Block("lime_glazed_terracotta", builder().requiresCorrectToolForDrops().destroyTime(1.4f).pushReaction(PistonBehavior.PUSH_ONLY) - .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST))); + .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST))); public static final Block PINK_GLAZED_TERRACOTTA = register(new Block("pink_glazed_terracotta", builder().requiresCorrectToolForDrops().destroyTime(1.4f).pushReaction(PistonBehavior.PUSH_ONLY) - .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST))); + .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST))); public static final Block GRAY_GLAZED_TERRACOTTA = register(new Block("gray_glazed_terracotta", builder().requiresCorrectToolForDrops().destroyTime(1.4f).pushReaction(PistonBehavior.PUSH_ONLY) - .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST))); + .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST))); public static final Block LIGHT_GRAY_GLAZED_TERRACOTTA = register(new Block("light_gray_glazed_terracotta", builder().requiresCorrectToolForDrops().destroyTime(1.4f).pushReaction(PistonBehavior.PUSH_ONLY) - .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST))); + .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST))); public static final Block CYAN_GLAZED_TERRACOTTA = register(new Block("cyan_glazed_terracotta", builder().requiresCorrectToolForDrops().destroyTime(1.4f).pushReaction(PistonBehavior.PUSH_ONLY) - .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST))); + .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST))); public static final Block PURPLE_GLAZED_TERRACOTTA = register(new Block("purple_glazed_terracotta", builder().requiresCorrectToolForDrops().destroyTime(1.4f).pushReaction(PistonBehavior.PUSH_ONLY) - .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST))); + .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST))); public static final Block BLUE_GLAZED_TERRACOTTA = register(new Block("blue_glazed_terracotta", builder().requiresCorrectToolForDrops().destroyTime(1.4f).pushReaction(PistonBehavior.PUSH_ONLY) - .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST))); + .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST))); public static final Block BROWN_GLAZED_TERRACOTTA = register(new Block("brown_glazed_terracotta", builder().requiresCorrectToolForDrops().destroyTime(1.4f).pushReaction(PistonBehavior.PUSH_ONLY) - .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST))); + .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST))); public static final Block GREEN_GLAZED_TERRACOTTA = register(new Block("green_glazed_terracotta", builder().requiresCorrectToolForDrops().destroyTime(1.4f).pushReaction(PistonBehavior.PUSH_ONLY) - .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST))); + .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST))); public static final Block RED_GLAZED_TERRACOTTA = register(new Block("red_glazed_terracotta", builder().requiresCorrectToolForDrops().destroyTime(1.4f).pushReaction(PistonBehavior.PUSH_ONLY) - .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST))); + .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST))); public static final Block BLACK_GLAZED_TERRACOTTA = register(new Block("black_glazed_terracotta", builder().requiresCorrectToolForDrops().destroyTime(1.4f).pushReaction(PistonBehavior.PUSH_ONLY) - .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST))); + .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST))); public static final Block WHITE_CONCRETE = register(new Block("white_concrete", builder().requiresCorrectToolForDrops().destroyTime(1.8f))); public static final Block ORANGE_CONCRETE = register(new Block("orange_concrete", builder().requiresCorrectToolForDrops().destroyTime(1.8f))); public static final Block MAGENTA_CONCRETE = register(new Block("magenta_concrete", builder().requiresCorrectToolForDrops().destroyTime(1.8f))); @@ -1696,14 +1765,14 @@ public final class Blocks { public static final Block RED_CONCRETE_POWDER = register(new Block("red_concrete_powder", builder().destroyTime(0.5f))); public static final Block BLACK_CONCRETE_POWDER = register(new Block("black_concrete_powder", builder().destroyTime(0.5f))); public static final Block KELP = register(new Block("kelp", builder().pushReaction(PistonBehavior.DESTROY) - .intState(AGE_25))); + .intState(AGE_25))); public static final Block KELP_PLANT = register(new Block("kelp_plant", builder().pushReaction(PistonBehavior.DESTROY).pickItem(() -> Items.KELP))); public static final Block DRIED_KELP_BLOCK = register(new Block("dried_kelp_block", builder().destroyTime(0.5f))); public static final Block TURTLE_EGG = register(new Block("turtle_egg", builder().destroyTime(0.5f).pushReaction(PistonBehavior.DESTROY) - .intState(EGGS) - .intState(HATCH))); + .intState(EGGS) + .intState(HATCH))); public static final Block SNIFFER_EGG = register(new Block("sniffer_egg", builder().destroyTime(0.5f) - .intState(HATCH))); + .intState(HATCH))); public static final Block DEAD_TUBE_CORAL_BLOCK = register(new Block("dead_tube_coral_block", builder().requiresCorrectToolForDrops().destroyTime(1.5f))); public static final Block DEAD_BRAIN_CORAL_BLOCK = register(new Block("dead_brain_coral_block", builder().requiresCorrectToolForDrops().destroyTime(1.5f))); public static final Block DEAD_BUBBLE_CORAL_BLOCK = register(new Block("dead_bubble_coral_block", builder().requiresCorrectToolForDrops().destroyTime(1.5f))); @@ -1715,480 +1784,480 @@ public final class Blocks { public static final Block FIRE_CORAL_BLOCK = register(new Block("fire_coral_block", builder().requiresCorrectToolForDrops().destroyTime(1.5f))); public static final Block HORN_CORAL_BLOCK = register(new Block("horn_coral_block", builder().requiresCorrectToolForDrops().destroyTime(1.5f))); public static final Block DEAD_TUBE_CORAL = register(new Block("dead_tube_coral", builder().requiresCorrectToolForDrops() - .booleanState(WATERLOGGED))); + .booleanState(WATERLOGGED))); public static final Block DEAD_BRAIN_CORAL = register(new Block("dead_brain_coral", builder().requiresCorrectToolForDrops() - .booleanState(WATERLOGGED))); + .booleanState(WATERLOGGED))); public static final Block DEAD_BUBBLE_CORAL = register(new Block("dead_bubble_coral", builder().requiresCorrectToolForDrops() - .booleanState(WATERLOGGED))); + .booleanState(WATERLOGGED))); public static final Block DEAD_FIRE_CORAL = register(new Block("dead_fire_coral", builder().requiresCorrectToolForDrops() - .booleanState(WATERLOGGED))); + .booleanState(WATERLOGGED))); public static final Block DEAD_HORN_CORAL = register(new Block("dead_horn_coral", builder().requiresCorrectToolForDrops() - .booleanState(WATERLOGGED))); + .booleanState(WATERLOGGED))); public static final Block TUBE_CORAL = register(new Block("tube_coral", builder().pushReaction(PistonBehavior.DESTROY) - .booleanState(WATERLOGGED))); + .booleanState(WATERLOGGED))); public static final Block BRAIN_CORAL = register(new Block("brain_coral", builder().pushReaction(PistonBehavior.DESTROY) - .booleanState(WATERLOGGED))); + .booleanState(WATERLOGGED))); public static final Block BUBBLE_CORAL = register(new Block("bubble_coral", builder().pushReaction(PistonBehavior.DESTROY) - .booleanState(WATERLOGGED))); + .booleanState(WATERLOGGED))); public static final Block FIRE_CORAL = register(new Block("fire_coral", builder().pushReaction(PistonBehavior.DESTROY) - .booleanState(WATERLOGGED))); + .booleanState(WATERLOGGED))); public static final Block HORN_CORAL = register(new Block("horn_coral", builder().pushReaction(PistonBehavior.DESTROY) - .booleanState(WATERLOGGED))); + .booleanState(WATERLOGGED))); public static final Block DEAD_TUBE_CORAL_FAN = register(new Block("dead_tube_coral_fan", builder().requiresCorrectToolForDrops() - .booleanState(WATERLOGGED))); + .booleanState(WATERLOGGED))); public static final Block DEAD_BRAIN_CORAL_FAN = register(new Block("dead_brain_coral_fan", builder().requiresCorrectToolForDrops() - .booleanState(WATERLOGGED))); + .booleanState(WATERLOGGED))); public static final Block DEAD_BUBBLE_CORAL_FAN = register(new Block("dead_bubble_coral_fan", builder().requiresCorrectToolForDrops() - .booleanState(WATERLOGGED))); + .booleanState(WATERLOGGED))); public static final Block DEAD_FIRE_CORAL_FAN = register(new Block("dead_fire_coral_fan", builder().requiresCorrectToolForDrops() - .booleanState(WATERLOGGED))); + .booleanState(WATERLOGGED))); public static final Block DEAD_HORN_CORAL_FAN = register(new Block("dead_horn_coral_fan", builder().requiresCorrectToolForDrops() - .booleanState(WATERLOGGED))); + .booleanState(WATERLOGGED))); public static final Block TUBE_CORAL_FAN = register(new Block("tube_coral_fan", builder().pushReaction(PistonBehavior.DESTROY) - .booleanState(WATERLOGGED))); + .booleanState(WATERLOGGED))); public static final Block BRAIN_CORAL_FAN = register(new Block("brain_coral_fan", builder().pushReaction(PistonBehavior.DESTROY) - .booleanState(WATERLOGGED))); + .booleanState(WATERLOGGED))); public static final Block BUBBLE_CORAL_FAN = register(new Block("bubble_coral_fan", builder().pushReaction(PistonBehavior.DESTROY) - .booleanState(WATERLOGGED))); + .booleanState(WATERLOGGED))); public static final Block FIRE_CORAL_FAN = register(new Block("fire_coral_fan", builder().pushReaction(PistonBehavior.DESTROY) - .booleanState(WATERLOGGED))); + .booleanState(WATERLOGGED))); public static final Block HORN_CORAL_FAN = register(new Block("horn_coral_fan", builder().pushReaction(PistonBehavior.DESTROY) - .booleanState(WATERLOGGED))); + .booleanState(WATERLOGGED))); public static final Block DEAD_TUBE_CORAL_WALL_FAN = register(new Block("dead_tube_coral_wall_fan", builder().requiresCorrectToolForDrops() - .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) - .booleanState(WATERLOGGED))); + .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) + .booleanState(WATERLOGGED))); public static final Block DEAD_BRAIN_CORAL_WALL_FAN = register(new Block("dead_brain_coral_wall_fan", builder().requiresCorrectToolForDrops() - .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) - .booleanState(WATERLOGGED))); + .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) + .booleanState(WATERLOGGED))); public static final Block DEAD_BUBBLE_CORAL_WALL_FAN = register(new Block("dead_bubble_coral_wall_fan", builder().requiresCorrectToolForDrops() - .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) - .booleanState(WATERLOGGED))); + .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) + .booleanState(WATERLOGGED))); public static final Block DEAD_FIRE_CORAL_WALL_FAN = register(new Block("dead_fire_coral_wall_fan", builder().requiresCorrectToolForDrops() - .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) - .booleanState(WATERLOGGED))); + .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) + .booleanState(WATERLOGGED))); public static final Block DEAD_HORN_CORAL_WALL_FAN = register(new Block("dead_horn_coral_wall_fan", builder().requiresCorrectToolForDrops() - .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) - .booleanState(WATERLOGGED))); + .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) + .booleanState(WATERLOGGED))); public static final Block TUBE_CORAL_WALL_FAN = register(new Block("tube_coral_wall_fan", builder().pushReaction(PistonBehavior.DESTROY) - .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) - .booleanState(WATERLOGGED))); + .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) + .booleanState(WATERLOGGED))); public static final Block BRAIN_CORAL_WALL_FAN = register(new Block("brain_coral_wall_fan", builder().pushReaction(PistonBehavior.DESTROY) - .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) - .booleanState(WATERLOGGED))); + .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) + .booleanState(WATERLOGGED))); public static final Block BUBBLE_CORAL_WALL_FAN = register(new Block("bubble_coral_wall_fan", builder().pushReaction(PistonBehavior.DESTROY) - .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) - .booleanState(WATERLOGGED))); + .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) + .booleanState(WATERLOGGED))); public static final Block FIRE_CORAL_WALL_FAN = register(new Block("fire_coral_wall_fan", builder().pushReaction(PistonBehavior.DESTROY) - .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) - .booleanState(WATERLOGGED))); + .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) + .booleanState(WATERLOGGED))); public static final Block HORN_CORAL_WALL_FAN = register(new Block("horn_coral_wall_fan", builder().pushReaction(PistonBehavior.DESTROY) - .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) - .booleanState(WATERLOGGED))); + .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) + .booleanState(WATERLOGGED))); public static final Block SEA_PICKLE = register(new Block("sea_pickle", builder().pushReaction(PistonBehavior.DESTROY) - .intState(PICKLES) - .booleanState(WATERLOGGED))); + .intState(PICKLES) + .booleanState(WATERLOGGED))); public static final Block BLUE_ICE = register(new Block("blue_ice", builder().destroyTime(2.8f))); public static final Block CONDUIT = register(new Block("conduit", builder().setBlockEntity(BlockEntityType.CONDUIT).destroyTime(3.0f) - .booleanState(WATERLOGGED))); + .booleanState(WATERLOGGED))); public static final Block BAMBOO_SAPLING = register(new Block("bamboo_sapling", builder().destroyTime(1.0f).pushReaction(PistonBehavior.DESTROY).pickItem(() -> Items.BAMBOO))); public static final Block BAMBOO = register(new Block("bamboo", builder().destroyTime(1.0f).pushReaction(PistonBehavior.DESTROY) - .intState(AGE_1) - .enumState(BAMBOO_LEAVES) - .intState(STAGE))); + .intState(AGE_1) + .enumState(BAMBOO_LEAVES) + .intState(STAGE))); public static final Block POTTED_BAMBOO = register(new FlowerPotBlock("potted_bamboo", BAMBOO, builder().pushReaction(PistonBehavior.DESTROY))); public static final Block VOID_AIR = register(new Block("void_air", builder())); public static final Block CAVE_AIR = register(new Block("cave_air", builder())); public static final Block BUBBLE_COLUMN = register(new Block("bubble_column", builder().pushReaction(PistonBehavior.DESTROY) - .booleanState(DRAG))); + .booleanState(DRAG))); public static final Block POLISHED_GRANITE_STAIRS = register(new Block("polished_granite_stairs", builder().requiresCorrectToolForDrops().destroyTime(1.5f) - .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) - .enumState(HALF) - .enumState(STAIRS_SHAPE) - .booleanState(WATERLOGGED))); + .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) + .enumState(HALF) + .enumState(STAIRS_SHAPE) + .booleanState(WATERLOGGED))); public static final Block SMOOTH_RED_SANDSTONE_STAIRS = register(new Block("smooth_red_sandstone_stairs", builder().requiresCorrectToolForDrops().destroyTime(2.0f) - .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) - .enumState(HALF) - .enumState(STAIRS_SHAPE) - .booleanState(WATERLOGGED))); + .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) + .enumState(HALF) + .enumState(STAIRS_SHAPE) + .booleanState(WATERLOGGED))); public static final Block MOSSY_STONE_BRICK_STAIRS = register(new Block("mossy_stone_brick_stairs", builder().requiresCorrectToolForDrops().destroyTime(1.5f) - .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) - .enumState(HALF) - .enumState(STAIRS_SHAPE) - .booleanState(WATERLOGGED))); + .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) + .enumState(HALF) + .enumState(STAIRS_SHAPE) + .booleanState(WATERLOGGED))); public static final Block POLISHED_DIORITE_STAIRS = register(new Block("polished_diorite_stairs", builder().requiresCorrectToolForDrops().destroyTime(1.5f) - .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) - .enumState(HALF) - .enumState(STAIRS_SHAPE) - .booleanState(WATERLOGGED))); + .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) + .enumState(HALF) + .enumState(STAIRS_SHAPE) + .booleanState(WATERLOGGED))); public static final Block MOSSY_COBBLESTONE_STAIRS = register(new Block("mossy_cobblestone_stairs", builder().requiresCorrectToolForDrops().destroyTime(2.0f) - .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) - .enumState(HALF) - .enumState(STAIRS_SHAPE) - .booleanState(WATERLOGGED))); + .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) + .enumState(HALF) + .enumState(STAIRS_SHAPE) + .booleanState(WATERLOGGED))); public static final Block END_STONE_BRICK_STAIRS = register(new Block("end_stone_brick_stairs", builder().requiresCorrectToolForDrops().destroyTime(3.0f) - .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) - .enumState(HALF) - .enumState(STAIRS_SHAPE) - .booleanState(WATERLOGGED))); + .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) + .enumState(HALF) + .enumState(STAIRS_SHAPE) + .booleanState(WATERLOGGED))); public static final Block STONE_STAIRS = register(new Block("stone_stairs", builder().requiresCorrectToolForDrops().destroyTime(1.5f) - .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) - .enumState(HALF) - .enumState(STAIRS_SHAPE) - .booleanState(WATERLOGGED))); + .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) + .enumState(HALF) + .enumState(STAIRS_SHAPE) + .booleanState(WATERLOGGED))); public static final Block SMOOTH_SANDSTONE_STAIRS = register(new Block("smooth_sandstone_stairs", builder().requiresCorrectToolForDrops().destroyTime(2.0f) - .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) - .enumState(HALF) - .enumState(STAIRS_SHAPE) - .booleanState(WATERLOGGED))); + .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) + .enumState(HALF) + .enumState(STAIRS_SHAPE) + .booleanState(WATERLOGGED))); public static final Block SMOOTH_QUARTZ_STAIRS = register(new Block("smooth_quartz_stairs", builder().requiresCorrectToolForDrops().destroyTime(2.0f) - .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) - .enumState(HALF) - .enumState(STAIRS_SHAPE) - .booleanState(WATERLOGGED))); + .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) + .enumState(HALF) + .enumState(STAIRS_SHAPE) + .booleanState(WATERLOGGED))); public static final Block GRANITE_STAIRS = register(new Block("granite_stairs", builder().requiresCorrectToolForDrops().destroyTime(1.5f) - .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) - .enumState(HALF) - .enumState(STAIRS_SHAPE) - .booleanState(WATERLOGGED))); + .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) + .enumState(HALF) + .enumState(STAIRS_SHAPE) + .booleanState(WATERLOGGED))); public static final Block ANDESITE_STAIRS = register(new Block("andesite_stairs", builder().requiresCorrectToolForDrops().destroyTime(1.5f) - .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) - .enumState(HALF) - .enumState(STAIRS_SHAPE) - .booleanState(WATERLOGGED))); + .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) + .enumState(HALF) + .enumState(STAIRS_SHAPE) + .booleanState(WATERLOGGED))); public static final Block RED_NETHER_BRICK_STAIRS = register(new Block("red_nether_brick_stairs", builder().requiresCorrectToolForDrops().destroyTime(2.0f) - .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) - .enumState(HALF) - .enumState(STAIRS_SHAPE) - .booleanState(WATERLOGGED))); + .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) + .enumState(HALF) + .enumState(STAIRS_SHAPE) + .booleanState(WATERLOGGED))); public static final Block POLISHED_ANDESITE_STAIRS = register(new Block("polished_andesite_stairs", builder().requiresCorrectToolForDrops().destroyTime(1.5f) - .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) - .enumState(HALF) - .enumState(STAIRS_SHAPE) - .booleanState(WATERLOGGED))); + .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) + .enumState(HALF) + .enumState(STAIRS_SHAPE) + .booleanState(WATERLOGGED))); public static final Block DIORITE_STAIRS = register(new Block("diorite_stairs", builder().requiresCorrectToolForDrops().destroyTime(1.5f) - .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) - .enumState(HALF) - .enumState(STAIRS_SHAPE) - .booleanState(WATERLOGGED))); + .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) + .enumState(HALF) + .enumState(STAIRS_SHAPE) + .booleanState(WATERLOGGED))); public static final Block POLISHED_GRANITE_SLAB = register(new Block("polished_granite_slab", builder().requiresCorrectToolForDrops().destroyTime(1.5f) - .enumState(SLAB_TYPE) - .booleanState(WATERLOGGED))); + .enumState(SLAB_TYPE) + .booleanState(WATERLOGGED))); public static final Block SMOOTH_RED_SANDSTONE_SLAB = register(new Block("smooth_red_sandstone_slab", builder().requiresCorrectToolForDrops().destroyTime(2.0f) - .enumState(SLAB_TYPE) - .booleanState(WATERLOGGED))); + .enumState(SLAB_TYPE) + .booleanState(WATERLOGGED))); public static final Block MOSSY_STONE_BRICK_SLAB = register(new Block("mossy_stone_brick_slab", builder().requiresCorrectToolForDrops().destroyTime(1.5f) - .enumState(SLAB_TYPE) - .booleanState(WATERLOGGED))); + .enumState(SLAB_TYPE) + .booleanState(WATERLOGGED))); public static final Block POLISHED_DIORITE_SLAB = register(new Block("polished_diorite_slab", builder().requiresCorrectToolForDrops().destroyTime(1.5f) - .enumState(SLAB_TYPE) - .booleanState(WATERLOGGED))); + .enumState(SLAB_TYPE) + .booleanState(WATERLOGGED))); public static final Block MOSSY_COBBLESTONE_SLAB = register(new Block("mossy_cobblestone_slab", builder().requiresCorrectToolForDrops().destroyTime(2.0f) - .enumState(SLAB_TYPE) - .booleanState(WATERLOGGED))); + .enumState(SLAB_TYPE) + .booleanState(WATERLOGGED))); public static final Block END_STONE_BRICK_SLAB = register(new Block("end_stone_brick_slab", builder().requiresCorrectToolForDrops().destroyTime(3.0f) - .enumState(SLAB_TYPE) - .booleanState(WATERLOGGED))); + .enumState(SLAB_TYPE) + .booleanState(WATERLOGGED))); public static final Block SMOOTH_SANDSTONE_SLAB = register(new Block("smooth_sandstone_slab", builder().requiresCorrectToolForDrops().destroyTime(2.0f) - .enumState(SLAB_TYPE) - .booleanState(WATERLOGGED))); + .enumState(SLAB_TYPE) + .booleanState(WATERLOGGED))); public static final Block SMOOTH_QUARTZ_SLAB = register(new Block("smooth_quartz_slab", builder().requiresCorrectToolForDrops().destroyTime(2.0f) - .enumState(SLAB_TYPE) - .booleanState(WATERLOGGED))); + .enumState(SLAB_TYPE) + .booleanState(WATERLOGGED))); public static final Block GRANITE_SLAB = register(new Block("granite_slab", builder().requiresCorrectToolForDrops().destroyTime(1.5f) - .enumState(SLAB_TYPE) - .booleanState(WATERLOGGED))); + .enumState(SLAB_TYPE) + .booleanState(WATERLOGGED))); public static final Block ANDESITE_SLAB = register(new Block("andesite_slab", builder().requiresCorrectToolForDrops().destroyTime(1.5f) - .enumState(SLAB_TYPE) - .booleanState(WATERLOGGED))); + .enumState(SLAB_TYPE) + .booleanState(WATERLOGGED))); public static final Block RED_NETHER_BRICK_SLAB = register(new Block("red_nether_brick_slab", builder().requiresCorrectToolForDrops().destroyTime(2.0f) - .enumState(SLAB_TYPE) - .booleanState(WATERLOGGED))); + .enumState(SLAB_TYPE) + .booleanState(WATERLOGGED))); public static final Block POLISHED_ANDESITE_SLAB = register(new Block("polished_andesite_slab", builder().requiresCorrectToolForDrops().destroyTime(1.5f) - .enumState(SLAB_TYPE) - .booleanState(WATERLOGGED))); + .enumState(SLAB_TYPE) + .booleanState(WATERLOGGED))); public static final Block DIORITE_SLAB = register(new Block("diorite_slab", builder().requiresCorrectToolForDrops().destroyTime(1.5f) - .enumState(SLAB_TYPE) - .booleanState(WATERLOGGED))); + .enumState(SLAB_TYPE) + .booleanState(WATERLOGGED))); public static final Block BRICK_WALL = register(new Block("brick_wall", builder().requiresCorrectToolForDrops().destroyTime(2.0f) - .enumState(EAST_WALL) - .enumState(NORTH_WALL) - .enumState(SOUTH_WALL) - .booleanState(UP) - .booleanState(WATERLOGGED) - .enumState(WEST_WALL))); + .enumState(EAST_WALL) + .enumState(NORTH_WALL) + .enumState(SOUTH_WALL) + .booleanState(UP) + .booleanState(WATERLOGGED) + .enumState(WEST_WALL))); public static final Block PRISMARINE_WALL = register(new Block("prismarine_wall", builder().requiresCorrectToolForDrops().destroyTime(1.5f) - .enumState(EAST_WALL) - .enumState(NORTH_WALL) - .enumState(SOUTH_WALL) - .booleanState(UP) - .booleanState(WATERLOGGED) - .enumState(WEST_WALL))); + .enumState(EAST_WALL) + .enumState(NORTH_WALL) + .enumState(SOUTH_WALL) + .booleanState(UP) + .booleanState(WATERLOGGED) + .enumState(WEST_WALL))); public static final Block RED_SANDSTONE_WALL = register(new Block("red_sandstone_wall", builder().requiresCorrectToolForDrops().destroyTime(0.8f) - .enumState(EAST_WALL) - .enumState(NORTH_WALL) - .enumState(SOUTH_WALL) - .booleanState(UP) - .booleanState(WATERLOGGED) - .enumState(WEST_WALL))); + .enumState(EAST_WALL) + .enumState(NORTH_WALL) + .enumState(SOUTH_WALL) + .booleanState(UP) + .booleanState(WATERLOGGED) + .enumState(WEST_WALL))); public static final Block MOSSY_STONE_BRICK_WALL = register(new Block("mossy_stone_brick_wall", builder().requiresCorrectToolForDrops().destroyTime(1.5f) - .enumState(EAST_WALL) - .enumState(NORTH_WALL) - .enumState(SOUTH_WALL) - .booleanState(UP) - .booleanState(WATERLOGGED) - .enumState(WEST_WALL))); + .enumState(EAST_WALL) + .enumState(NORTH_WALL) + .enumState(SOUTH_WALL) + .booleanState(UP) + .booleanState(WATERLOGGED) + .enumState(WEST_WALL))); public static final Block GRANITE_WALL = register(new Block("granite_wall", builder().requiresCorrectToolForDrops().destroyTime(1.5f) - .enumState(EAST_WALL) - .enumState(NORTH_WALL) - .enumState(SOUTH_WALL) - .booleanState(UP) - .booleanState(WATERLOGGED) - .enumState(WEST_WALL))); + .enumState(EAST_WALL) + .enumState(NORTH_WALL) + .enumState(SOUTH_WALL) + .booleanState(UP) + .booleanState(WATERLOGGED) + .enumState(WEST_WALL))); public static final Block STONE_BRICK_WALL = register(new Block("stone_brick_wall", builder().requiresCorrectToolForDrops().destroyTime(1.5f) - .enumState(EAST_WALL) - .enumState(NORTH_WALL) - .enumState(SOUTH_WALL) - .booleanState(UP) - .booleanState(WATERLOGGED) - .enumState(WEST_WALL))); + .enumState(EAST_WALL) + .enumState(NORTH_WALL) + .enumState(SOUTH_WALL) + .booleanState(UP) + .booleanState(WATERLOGGED) + .enumState(WEST_WALL))); public static final Block MUD_BRICK_WALL = register(new Block("mud_brick_wall", builder().requiresCorrectToolForDrops().destroyTime(1.5f) - .enumState(EAST_WALL) - .enumState(NORTH_WALL) - .enumState(SOUTH_WALL) - .booleanState(UP) - .booleanState(WATERLOGGED) - .enumState(WEST_WALL))); + .enumState(EAST_WALL) + .enumState(NORTH_WALL) + .enumState(SOUTH_WALL) + .booleanState(UP) + .booleanState(WATERLOGGED) + .enumState(WEST_WALL))); public static final Block NETHER_BRICK_WALL = register(new Block("nether_brick_wall", builder().requiresCorrectToolForDrops().destroyTime(2.0f) - .enumState(EAST_WALL) - .enumState(NORTH_WALL) - .enumState(SOUTH_WALL) - .booleanState(UP) - .booleanState(WATERLOGGED) - .enumState(WEST_WALL))); + .enumState(EAST_WALL) + .enumState(NORTH_WALL) + .enumState(SOUTH_WALL) + .booleanState(UP) + .booleanState(WATERLOGGED) + .enumState(WEST_WALL))); public static final Block ANDESITE_WALL = register(new Block("andesite_wall", builder().requiresCorrectToolForDrops().destroyTime(1.5f) - .enumState(EAST_WALL) - .enumState(NORTH_WALL) - .enumState(SOUTH_WALL) - .booleanState(UP) - .booleanState(WATERLOGGED) - .enumState(WEST_WALL))); + .enumState(EAST_WALL) + .enumState(NORTH_WALL) + .enumState(SOUTH_WALL) + .booleanState(UP) + .booleanState(WATERLOGGED) + .enumState(WEST_WALL))); public static final Block RED_NETHER_BRICK_WALL = register(new Block("red_nether_brick_wall", builder().requiresCorrectToolForDrops().destroyTime(2.0f) - .enumState(EAST_WALL) - .enumState(NORTH_WALL) - .enumState(SOUTH_WALL) - .booleanState(UP) - .booleanState(WATERLOGGED) - .enumState(WEST_WALL))); + .enumState(EAST_WALL) + .enumState(NORTH_WALL) + .enumState(SOUTH_WALL) + .booleanState(UP) + .booleanState(WATERLOGGED) + .enumState(WEST_WALL))); public static final Block SANDSTONE_WALL = register(new Block("sandstone_wall", builder().requiresCorrectToolForDrops().destroyTime(0.8f) - .enumState(EAST_WALL) - .enumState(NORTH_WALL) - .enumState(SOUTH_WALL) - .booleanState(UP) - .booleanState(WATERLOGGED) - .enumState(WEST_WALL))); + .enumState(EAST_WALL) + .enumState(NORTH_WALL) + .enumState(SOUTH_WALL) + .booleanState(UP) + .booleanState(WATERLOGGED) + .enumState(WEST_WALL))); public static final Block END_STONE_BRICK_WALL = register(new Block("end_stone_brick_wall", builder().requiresCorrectToolForDrops().destroyTime(3.0f) - .enumState(EAST_WALL) - .enumState(NORTH_WALL) - .enumState(SOUTH_WALL) - .booleanState(UP) - .booleanState(WATERLOGGED) - .enumState(WEST_WALL))); + .enumState(EAST_WALL) + .enumState(NORTH_WALL) + .enumState(SOUTH_WALL) + .booleanState(UP) + .booleanState(WATERLOGGED) + .enumState(WEST_WALL))); public static final Block DIORITE_WALL = register(new Block("diorite_wall", builder().requiresCorrectToolForDrops().destroyTime(1.5f) - .enumState(EAST_WALL) - .enumState(NORTH_WALL) - .enumState(SOUTH_WALL) - .booleanState(UP) - .booleanState(WATERLOGGED) - .enumState(WEST_WALL))); + .enumState(EAST_WALL) + .enumState(NORTH_WALL) + .enumState(SOUTH_WALL) + .booleanState(UP) + .booleanState(WATERLOGGED) + .enumState(WEST_WALL))); public static final Block SCAFFOLDING = register(new Block("scaffolding", builder().pushReaction(PistonBehavior.DESTROY) - .booleanState(BOTTOM) - .intState(STABILITY_DISTANCE) - .booleanState(WATERLOGGED))); + .booleanState(BOTTOM) + .intState(STABILITY_DISTANCE) + .booleanState(WATERLOGGED))); public static final Block LOOM = register(new Block("loom", builder().destroyTime(2.5f) - .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST))); + .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST))); public static final Block BARREL = register(new Block("barrel", builder().setBlockEntity(BlockEntityType.BARREL).destroyTime(2.5f) - .enumState(FACING, Direction.NORTH, Direction.EAST, Direction.SOUTH, Direction.WEST, Direction.UP, Direction.DOWN) - .booleanState(OPEN))); + .enumState(FACING, Direction.NORTH, Direction.EAST, Direction.SOUTH, Direction.WEST, Direction.UP, Direction.DOWN) + .booleanState(OPEN))); public static final Block SMOKER = register(new Block("smoker", builder().setBlockEntity(BlockEntityType.SMOKER).requiresCorrectToolForDrops().destroyTime(3.5f) - .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) - .booleanState(LIT))); + .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) + .booleanState(LIT))); public static final Block BLAST_FURNACE = register(new Block("blast_furnace", builder().setBlockEntity(BlockEntityType.BLAST_FURNACE).requiresCorrectToolForDrops().destroyTime(3.5f) - .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) - .booleanState(LIT))); + .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) + .booleanState(LIT))); public static final Block CARTOGRAPHY_TABLE = register(new Block("cartography_table", builder().destroyTime(2.5f))); public static final Block FLETCHING_TABLE = register(new Block("fletching_table", builder().destroyTime(2.5f))); public static final Block GRINDSTONE = register(new Block("grindstone", builder().requiresCorrectToolForDrops().destroyTime(2.0f).pushReaction(PistonBehavior.BLOCK) - .enumState(ATTACH_FACE) - .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST))); + .enumState(ATTACH_FACE) + .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST))); public static final Block LECTERN = register(new LecternBlock("lectern", builder().setBlockEntity(BlockEntityType.LECTERN).destroyTime(2.5f) - .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) - .booleanState(HAS_BOOK) - .booleanState(POWERED))); + .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) + .booleanState(HAS_BOOK) + .booleanState(POWERED))); public static final Block SMITHING_TABLE = register(new Block("smithing_table", builder().destroyTime(2.5f))); public static final Block STONECUTTER = register(new Block("stonecutter", builder().requiresCorrectToolForDrops().destroyTime(3.5f) - .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST))); + .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST))); public static final Block BELL = register(new Block("bell", builder().setBlockEntity(BlockEntityType.BELL).requiresCorrectToolForDrops().destroyTime(5.0f).pushReaction(PistonBehavior.DESTROY) - .enumState(BELL_ATTACHMENT) - .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) - .booleanState(POWERED))); + .enumState(BELL_ATTACHMENT) + .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) + .booleanState(POWERED))); public static final Block LANTERN = register(new Block("lantern", builder().requiresCorrectToolForDrops().destroyTime(3.5f).pushReaction(PistonBehavior.DESTROY) - .booleanState(HANGING) - .booleanState(WATERLOGGED))); + .booleanState(HANGING) + .booleanState(WATERLOGGED))); public static final Block SOUL_LANTERN = register(new Block("soul_lantern", builder().requiresCorrectToolForDrops().destroyTime(3.5f).pushReaction(PistonBehavior.DESTROY) - .booleanState(HANGING) - .booleanState(WATERLOGGED))); + .booleanState(HANGING) + .booleanState(WATERLOGGED))); public static final Block CAMPFIRE = register(new Block("campfire", builder().setBlockEntity(BlockEntityType.CAMPFIRE).destroyTime(2.0f) - .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) - .booleanState(LIT) - .booleanState(SIGNAL_FIRE) - .booleanState(WATERLOGGED))); + .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) + .booleanState(LIT) + .booleanState(SIGNAL_FIRE) + .booleanState(WATERLOGGED))); public static final Block SOUL_CAMPFIRE = register(new Block("soul_campfire", builder().setBlockEntity(BlockEntityType.CAMPFIRE).destroyTime(2.0f) - .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) - .booleanState(LIT) - .booleanState(SIGNAL_FIRE) - .booleanState(WATERLOGGED))); + .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) + .booleanState(LIT) + .booleanState(SIGNAL_FIRE) + .booleanState(WATERLOGGED))); public static final Block SWEET_BERRY_BUSH = register(new Block("sweet_berry_bush", builder().pushReaction(PistonBehavior.DESTROY) - .intState(AGE_3))); + .intState(AGE_3))); public static final Block WARPED_STEM = register(new Block("warped_stem", builder().destroyTime(2.0f) - .enumState(AXIS, Axis.VALUES))); + .enumState(AXIS, Axis.VALUES))); public static final Block STRIPPED_WARPED_STEM = register(new Block("stripped_warped_stem", builder().destroyTime(2.0f) - .enumState(AXIS, Axis.VALUES))); + .enumState(AXIS, Axis.VALUES))); public static final Block WARPED_HYPHAE = register(new Block("warped_hyphae", builder().destroyTime(2.0f) - .enumState(AXIS, Axis.VALUES))); + .enumState(AXIS, Axis.VALUES))); public static final Block STRIPPED_WARPED_HYPHAE = register(new Block("stripped_warped_hyphae", builder().destroyTime(2.0f) - .enumState(AXIS, Axis.VALUES))); + .enumState(AXIS, Axis.VALUES))); public static final Block WARPED_NYLIUM = register(new Block("warped_nylium", builder().requiresCorrectToolForDrops().destroyTime(0.4f))); public static final Block WARPED_FUNGUS = register(new Block("warped_fungus", builder().pushReaction(PistonBehavior.DESTROY))); public static final Block WARPED_WART_BLOCK = register(new Block("warped_wart_block", builder().destroyTime(1.0f))); public static final Block WARPED_ROOTS = register(new Block("warped_roots", builder().pushReaction(PistonBehavior.DESTROY))); public static final Block NETHER_SPROUTS = register(new Block("nether_sprouts", builder().pushReaction(PistonBehavior.DESTROY))); public static final Block CRIMSON_STEM = register(new Block("crimson_stem", builder().destroyTime(2.0f) - .enumState(AXIS, Axis.VALUES))); + .enumState(AXIS, Axis.VALUES))); public static final Block STRIPPED_CRIMSON_STEM = register(new Block("stripped_crimson_stem", builder().destroyTime(2.0f) - .enumState(AXIS, Axis.VALUES))); + .enumState(AXIS, Axis.VALUES))); public static final Block CRIMSON_HYPHAE = register(new Block("crimson_hyphae", builder().destroyTime(2.0f) - .enumState(AXIS, Axis.VALUES))); + .enumState(AXIS, Axis.VALUES))); public static final Block STRIPPED_CRIMSON_HYPHAE = register(new Block("stripped_crimson_hyphae", builder().destroyTime(2.0f) - .enumState(AXIS, Axis.VALUES))); + .enumState(AXIS, Axis.VALUES))); public static final Block CRIMSON_NYLIUM = register(new Block("crimson_nylium", builder().requiresCorrectToolForDrops().destroyTime(0.4f))); public static final Block CRIMSON_FUNGUS = register(new Block("crimson_fungus", builder().pushReaction(PistonBehavior.DESTROY))); public static final Block SHROOMLIGHT = register(new Block("shroomlight", builder().destroyTime(1.0f))); public static final Block WEEPING_VINES = register(new Block("weeping_vines", builder().pushReaction(PistonBehavior.DESTROY) - .intState(AGE_25))); + .intState(AGE_25))); public static final Block WEEPING_VINES_PLANT = register(new Block("weeping_vines_plant", builder().pushReaction(PistonBehavior.DESTROY).pickItem(() -> Items.WEEPING_VINES))); public static final Block TWISTING_VINES = register(new Block("twisting_vines", builder().pushReaction(PistonBehavior.DESTROY) - .intState(AGE_25))); + .intState(AGE_25))); public static final Block TWISTING_VINES_PLANT = register(new Block("twisting_vines_plant", builder().pushReaction(PistonBehavior.DESTROY).pickItem(() -> Items.TWISTING_VINES))); public static final Block CRIMSON_ROOTS = register(new Block("crimson_roots", builder().pushReaction(PistonBehavior.DESTROY))); public static final Block CRIMSON_PLANKS = register(new Block("crimson_planks", builder().destroyTime(2.0f))); public static final Block WARPED_PLANKS = register(new Block("warped_planks", builder().destroyTime(2.0f))); public static final Block CRIMSON_SLAB = register(new Block("crimson_slab", builder().destroyTime(2.0f) - .enumState(SLAB_TYPE) - .booleanState(WATERLOGGED))); + .enumState(SLAB_TYPE) + .booleanState(WATERLOGGED))); public static final Block WARPED_SLAB = register(new Block("warped_slab", builder().destroyTime(2.0f) - .enumState(SLAB_TYPE) - .booleanState(WATERLOGGED))); + .enumState(SLAB_TYPE) + .booleanState(WATERLOGGED))); public static final Block CRIMSON_PRESSURE_PLATE = register(new Block("crimson_pressure_plate", builder().destroyTime(0.5f).pushReaction(PistonBehavior.DESTROY) - .booleanState(POWERED))); + .booleanState(POWERED))); public static final Block WARPED_PRESSURE_PLATE = register(new Block("warped_pressure_plate", builder().destroyTime(0.5f).pushReaction(PistonBehavior.DESTROY) - .booleanState(POWERED))); + .booleanState(POWERED))); public static final Block CRIMSON_FENCE = register(new Block("crimson_fence", builder().destroyTime(2.0f) - .booleanState(EAST) - .booleanState(NORTH) - .booleanState(SOUTH) - .booleanState(WATERLOGGED) - .booleanState(WEST))); + .booleanState(EAST) + .booleanState(NORTH) + .booleanState(SOUTH) + .booleanState(WATERLOGGED) + .booleanState(WEST))); public static final Block WARPED_FENCE = register(new Block("warped_fence", builder().destroyTime(2.0f) - .booleanState(EAST) - .booleanState(NORTH) - .booleanState(SOUTH) - .booleanState(WATERLOGGED) - .booleanState(WEST))); + .booleanState(EAST) + .booleanState(NORTH) + .booleanState(SOUTH) + .booleanState(WATERLOGGED) + .booleanState(WEST))); public static final Block CRIMSON_TRAPDOOR = register(new TrapDoorBlock("crimson_trapdoor", builder().destroyTime(3.0f) - .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) - .enumState(HALF) - .booleanState(OPEN) - .booleanState(POWERED) - .booleanState(WATERLOGGED))); + .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) + .enumState(HALF) + .booleanState(OPEN) + .booleanState(POWERED) + .booleanState(WATERLOGGED))); public static final Block WARPED_TRAPDOOR = register(new TrapDoorBlock("warped_trapdoor", builder().destroyTime(3.0f) - .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) - .enumState(HALF) - .booleanState(OPEN) - .booleanState(POWERED) - .booleanState(WATERLOGGED))); + .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) + .enumState(HALF) + .booleanState(OPEN) + .booleanState(POWERED) + .booleanState(WATERLOGGED))); public static final Block CRIMSON_FENCE_GATE = register(new Block("crimson_fence_gate", builder().destroyTime(2.0f) - .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) - .booleanState(IN_WALL) - .booleanState(OPEN) - .booleanState(POWERED))); + .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) + .booleanState(IN_WALL) + .booleanState(OPEN) + .booleanState(POWERED))); public static final Block WARPED_FENCE_GATE = register(new Block("warped_fence_gate", builder().destroyTime(2.0f) - .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) - .booleanState(IN_WALL) - .booleanState(OPEN) - .booleanState(POWERED))); + .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) + .booleanState(IN_WALL) + .booleanState(OPEN) + .booleanState(POWERED))); public static final Block CRIMSON_STAIRS = register(new Block("crimson_stairs", builder().destroyTime(2.0f) - .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) - .enumState(HALF) - .enumState(STAIRS_SHAPE) - .booleanState(WATERLOGGED))); + .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) + .enumState(HALF) + .enumState(STAIRS_SHAPE) + .booleanState(WATERLOGGED))); public static final Block WARPED_STAIRS = register(new Block("warped_stairs", builder().destroyTime(2.0f) - .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) - .enumState(HALF) - .enumState(STAIRS_SHAPE) - .booleanState(WATERLOGGED))); + .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) + .enumState(HALF) + .enumState(STAIRS_SHAPE) + .booleanState(WATERLOGGED))); public static final Block CRIMSON_BUTTON = register(new Block("crimson_button", builder().destroyTime(0.5f).pushReaction(PistonBehavior.DESTROY) - .enumState(ATTACH_FACE) - .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) - .booleanState(POWERED))); + .enumState(ATTACH_FACE) + .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) + .booleanState(POWERED))); public static final Block WARPED_BUTTON = register(new Block("warped_button", builder().destroyTime(0.5f).pushReaction(PistonBehavior.DESTROY) - .enumState(ATTACH_FACE) - .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) - .booleanState(POWERED))); + .enumState(ATTACH_FACE) + .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) + .booleanState(POWERED))); public static final Block CRIMSON_DOOR = register(new DoorBlock("crimson_door", builder().destroyTime(3.0f).pushReaction(PistonBehavior.DESTROY) - .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) - .enumState(DOUBLE_BLOCK_HALF) - .enumState(DOOR_HINGE) - .booleanState(OPEN) - .booleanState(POWERED))); + .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) + .enumState(DOUBLE_BLOCK_HALF) + .enumState(DOOR_HINGE) + .booleanState(OPEN) + .booleanState(POWERED))); public static final Block WARPED_DOOR = register(new DoorBlock("warped_door", builder().destroyTime(3.0f).pushReaction(PistonBehavior.DESTROY) - .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) - .enumState(DOUBLE_BLOCK_HALF) - .enumState(DOOR_HINGE) - .booleanState(OPEN) - .booleanState(POWERED))); + .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) + .enumState(DOUBLE_BLOCK_HALF) + .enumState(DOOR_HINGE) + .booleanState(OPEN) + .booleanState(POWERED))); public static final Block CRIMSON_SIGN = register(new Block("crimson_sign", builder().setBlockEntity(BlockEntityType.SIGN).destroyTime(1.0f) - .intState(ROTATION_16) - .booleanState(WATERLOGGED))); + .intState(ROTATION_16) + .booleanState(WATERLOGGED))); public static final Block WARPED_SIGN = register(new Block("warped_sign", builder().setBlockEntity(BlockEntityType.SIGN).destroyTime(1.0f) - .intState(ROTATION_16) - .booleanState(WATERLOGGED))); + .intState(ROTATION_16) + .booleanState(WATERLOGGED))); public static final Block CRIMSON_WALL_SIGN = register(new Block("crimson_wall_sign", builder().setBlockEntity(BlockEntityType.SIGN).destroyTime(1.0f) - .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) - .booleanState(WATERLOGGED))); + .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) + .booleanState(WATERLOGGED))); public static final Block WARPED_WALL_SIGN = register(new Block("warped_wall_sign", builder().setBlockEntity(BlockEntityType.SIGN).destroyTime(1.0f) - .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) - .booleanState(WATERLOGGED))); + .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) + .booleanState(WATERLOGGED))); public static final Block STRUCTURE_BLOCK = register(new Block("structure_block", builder().setBlockEntity(BlockEntityType.STRUCTURE_BLOCK).requiresCorrectToolForDrops().destroyTime(-1.0f) - .enumState(STRUCTUREBLOCK_MODE))); + .enumState(STRUCTUREBLOCK_MODE))); public static final Block JIGSAW = register(new Block("jigsaw", builder().setBlockEntity(BlockEntityType.JIGSAW).requiresCorrectToolForDrops().destroyTime(-1.0f) - .enumState(ORIENTATION, FrontAndTop.VALUES))); + .enumState(ORIENTATION, FrontAndTop.VALUES))); public static final Block COMPOSTER = register(new Block("composter", builder().destroyTime(0.6f) - .intState(LEVEL_COMPOSTER))); + .intState(LEVEL_COMPOSTER))); public static final Block TARGET = register(new Block("target", builder().destroyTime(0.5f) - .intState(POWER))); + .intState(POWER))); public static final Block BEE_NEST = register(new Block("bee_nest", builder().setBlockEntity(BlockEntityType.BEEHIVE).destroyTime(0.3f) - .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) - .intState(LEVEL_HONEY))); + .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) + .intState(LEVEL_HONEY))); public static final Block BEEHIVE = register(new Block("beehive", builder().setBlockEntity(BlockEntityType.BEEHIVE).destroyTime(0.6f) - .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) - .intState(LEVEL_HONEY))); + .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) + .intState(LEVEL_HONEY))); public static final Block HONEY_BLOCK = register(new HoneyBlock("honey_block", builder())); public static final Block HONEYCOMB_BLOCK = register(new Block("honeycomb_block", builder().destroyTime(0.6f))); public static final Block NETHERITE_BLOCK = register(new Block("netherite_block", builder().requiresCorrectToolForDrops().destroyTime(50.0f))); public static final Block ANCIENT_DEBRIS = register(new Block("ancient_debris", builder().requiresCorrectToolForDrops().destroyTime(30.0f))); public static final Block CRYING_OBSIDIAN = register(new Block("crying_obsidian", builder().requiresCorrectToolForDrops().destroyTime(50.0f))); public static final Block RESPAWN_ANCHOR = register(new Block("respawn_anchor", builder().requiresCorrectToolForDrops().destroyTime(50.0f) - .intState(RESPAWN_ANCHOR_CHARGES))); + .intState(RESPAWN_ANCHOR_CHARGES))); public static final Block POTTED_CRIMSON_FUNGUS = register(new FlowerPotBlock("potted_crimson_fungus", CRIMSON_FUNGUS, builder().pushReaction(PistonBehavior.DESTROY))); public static final Block POTTED_WARPED_FUNGUS = register(new FlowerPotBlock("potted_warped_fungus", WARPED_FUNGUS, builder().pushReaction(PistonBehavior.DESTROY))); public static final Block POTTED_CRIMSON_ROOTS = register(new FlowerPotBlock("potted_crimson_roots", CRIMSON_ROOTS, builder().pushReaction(PistonBehavior.DESTROY))); @@ -2196,257 +2265,257 @@ public final class Blocks { public static final Block LODESTONE = register(new Block("lodestone", builder().requiresCorrectToolForDrops().destroyTime(3.5f).pushReaction(PistonBehavior.BLOCK))); public static final Block BLACKSTONE = register(new Block("blackstone", builder().requiresCorrectToolForDrops().destroyTime(1.5f))); public static final Block BLACKSTONE_STAIRS = register(new Block("blackstone_stairs", builder().requiresCorrectToolForDrops().destroyTime(1.5f) - .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) - .enumState(HALF) - .enumState(STAIRS_SHAPE) - .booleanState(WATERLOGGED))); + .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) + .enumState(HALF) + .enumState(STAIRS_SHAPE) + .booleanState(WATERLOGGED))); public static final Block BLACKSTONE_WALL = register(new Block("blackstone_wall", builder().requiresCorrectToolForDrops().destroyTime(1.5f) - .enumState(EAST_WALL) - .enumState(NORTH_WALL) - .enumState(SOUTH_WALL) - .booleanState(UP) - .booleanState(WATERLOGGED) - .enumState(WEST_WALL))); + .enumState(EAST_WALL) + .enumState(NORTH_WALL) + .enumState(SOUTH_WALL) + .booleanState(UP) + .booleanState(WATERLOGGED) + .enumState(WEST_WALL))); public static final Block BLACKSTONE_SLAB = register(new Block("blackstone_slab", builder().requiresCorrectToolForDrops().destroyTime(2.0f) - .enumState(SLAB_TYPE) - .booleanState(WATERLOGGED))); + .enumState(SLAB_TYPE) + .booleanState(WATERLOGGED))); public static final Block POLISHED_BLACKSTONE = register(new Block("polished_blackstone", builder().requiresCorrectToolForDrops().destroyTime(2.0f))); public static final Block POLISHED_BLACKSTONE_BRICKS = register(new Block("polished_blackstone_bricks", builder().requiresCorrectToolForDrops().destroyTime(1.5f))); public static final Block CRACKED_POLISHED_BLACKSTONE_BRICKS = register(new Block("cracked_polished_blackstone_bricks", builder().requiresCorrectToolForDrops().destroyTime(1.5f))); public static final Block CHISELED_POLISHED_BLACKSTONE = register(new Block("chiseled_polished_blackstone", builder().requiresCorrectToolForDrops().destroyTime(1.5f))); public static final Block POLISHED_BLACKSTONE_BRICK_SLAB = register(new Block("polished_blackstone_brick_slab", builder().requiresCorrectToolForDrops().destroyTime(2.0f) - .enumState(SLAB_TYPE) - .booleanState(WATERLOGGED))); + .enumState(SLAB_TYPE) + .booleanState(WATERLOGGED))); public static final Block POLISHED_BLACKSTONE_BRICK_STAIRS = register(new Block("polished_blackstone_brick_stairs", builder().requiresCorrectToolForDrops().destroyTime(1.5f) - .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) - .enumState(HALF) - .enumState(STAIRS_SHAPE) - .booleanState(WATERLOGGED))); + .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) + .enumState(HALF) + .enumState(STAIRS_SHAPE) + .booleanState(WATERLOGGED))); public static final Block POLISHED_BLACKSTONE_BRICK_WALL = register(new Block("polished_blackstone_brick_wall", builder().requiresCorrectToolForDrops().destroyTime(1.5f) - .enumState(EAST_WALL) - .enumState(NORTH_WALL) - .enumState(SOUTH_WALL) - .booleanState(UP) - .booleanState(WATERLOGGED) - .enumState(WEST_WALL))); + .enumState(EAST_WALL) + .enumState(NORTH_WALL) + .enumState(SOUTH_WALL) + .booleanState(UP) + .booleanState(WATERLOGGED) + .enumState(WEST_WALL))); public static final Block GILDED_BLACKSTONE = register(new Block("gilded_blackstone", builder().requiresCorrectToolForDrops().destroyTime(1.5f))); public static final Block POLISHED_BLACKSTONE_STAIRS = register(new Block("polished_blackstone_stairs", builder().requiresCorrectToolForDrops().destroyTime(2.0f) - .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) - .enumState(HALF) - .enumState(STAIRS_SHAPE) - .booleanState(WATERLOGGED))); + .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) + .enumState(HALF) + .enumState(STAIRS_SHAPE) + .booleanState(WATERLOGGED))); public static final Block POLISHED_BLACKSTONE_SLAB = register(new Block("polished_blackstone_slab", builder().requiresCorrectToolForDrops().destroyTime(2.0f) - .enumState(SLAB_TYPE) - .booleanState(WATERLOGGED))); + .enumState(SLAB_TYPE) + .booleanState(WATERLOGGED))); public static final Block POLISHED_BLACKSTONE_PRESSURE_PLATE = register(new Block("polished_blackstone_pressure_plate", builder().requiresCorrectToolForDrops().destroyTime(0.5f).pushReaction(PistonBehavior.DESTROY) - .booleanState(POWERED))); + .booleanState(POWERED))); public static final Block POLISHED_BLACKSTONE_BUTTON = register(new Block("polished_blackstone_button", builder().destroyTime(0.5f).pushReaction(PistonBehavior.DESTROY) - .enumState(ATTACH_FACE) - .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) - .booleanState(POWERED))); + .enumState(ATTACH_FACE) + .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) + .booleanState(POWERED))); public static final Block POLISHED_BLACKSTONE_WALL = register(new Block("polished_blackstone_wall", builder().requiresCorrectToolForDrops().destroyTime(2.0f) - .enumState(EAST_WALL) - .enumState(NORTH_WALL) - .enumState(SOUTH_WALL) - .booleanState(UP) - .booleanState(WATERLOGGED) - .enumState(WEST_WALL))); + .enumState(EAST_WALL) + .enumState(NORTH_WALL) + .enumState(SOUTH_WALL) + .booleanState(UP) + .booleanState(WATERLOGGED) + .enumState(WEST_WALL))); public static final Block CHISELED_NETHER_BRICKS = register(new Block("chiseled_nether_bricks", builder().requiresCorrectToolForDrops().destroyTime(2.0f))); public static final Block CRACKED_NETHER_BRICKS = register(new Block("cracked_nether_bricks", builder().requiresCorrectToolForDrops().destroyTime(2.0f))); public static final Block QUARTZ_BRICKS = register(new Block("quartz_bricks", builder().requiresCorrectToolForDrops().destroyTime(0.8f))); public static final Block CANDLE = register(new Block("candle", builder().destroyTime(0.1f).pushReaction(PistonBehavior.DESTROY) - .intState(CANDLES) - .booleanState(LIT) - .booleanState(WATERLOGGED))); + .intState(CANDLES) + .booleanState(LIT) + .booleanState(WATERLOGGED))); public static final Block WHITE_CANDLE = register(new Block("white_candle", builder().destroyTime(0.1f).pushReaction(PistonBehavior.DESTROY) - .intState(CANDLES) - .booleanState(LIT) - .booleanState(WATERLOGGED))); + .intState(CANDLES) + .booleanState(LIT) + .booleanState(WATERLOGGED))); public static final Block ORANGE_CANDLE = register(new Block("orange_candle", builder().destroyTime(0.1f).pushReaction(PistonBehavior.DESTROY) - .intState(CANDLES) - .booleanState(LIT) - .booleanState(WATERLOGGED))); + .intState(CANDLES) + .booleanState(LIT) + .booleanState(WATERLOGGED))); public static final Block MAGENTA_CANDLE = register(new Block("magenta_candle", builder().destroyTime(0.1f).pushReaction(PistonBehavior.DESTROY) - .intState(CANDLES) - .booleanState(LIT) - .booleanState(WATERLOGGED))); + .intState(CANDLES) + .booleanState(LIT) + .booleanState(WATERLOGGED))); public static final Block LIGHT_BLUE_CANDLE = register(new Block("light_blue_candle", builder().destroyTime(0.1f).pushReaction(PistonBehavior.DESTROY) - .intState(CANDLES) - .booleanState(LIT) - .booleanState(WATERLOGGED))); + .intState(CANDLES) + .booleanState(LIT) + .booleanState(WATERLOGGED))); public static final Block YELLOW_CANDLE = register(new Block("yellow_candle", builder().destroyTime(0.1f).pushReaction(PistonBehavior.DESTROY) - .intState(CANDLES) - .booleanState(LIT) - .booleanState(WATERLOGGED))); + .intState(CANDLES) + .booleanState(LIT) + .booleanState(WATERLOGGED))); public static final Block LIME_CANDLE = register(new Block("lime_candle", builder().destroyTime(0.1f).pushReaction(PistonBehavior.DESTROY) - .intState(CANDLES) - .booleanState(LIT) - .booleanState(WATERLOGGED))); + .intState(CANDLES) + .booleanState(LIT) + .booleanState(WATERLOGGED))); public static final Block PINK_CANDLE = register(new Block("pink_candle", builder().destroyTime(0.1f).pushReaction(PistonBehavior.DESTROY) - .intState(CANDLES) - .booleanState(LIT) - .booleanState(WATERLOGGED))); + .intState(CANDLES) + .booleanState(LIT) + .booleanState(WATERLOGGED))); public static final Block GRAY_CANDLE = register(new Block("gray_candle", builder().destroyTime(0.1f).pushReaction(PistonBehavior.DESTROY) - .intState(CANDLES) - .booleanState(LIT) - .booleanState(WATERLOGGED))); + .intState(CANDLES) + .booleanState(LIT) + .booleanState(WATERLOGGED))); public static final Block LIGHT_GRAY_CANDLE = register(new Block("light_gray_candle", builder().destroyTime(0.1f).pushReaction(PistonBehavior.DESTROY) - .intState(CANDLES) - .booleanState(LIT) - .booleanState(WATERLOGGED))); + .intState(CANDLES) + .booleanState(LIT) + .booleanState(WATERLOGGED))); public static final Block CYAN_CANDLE = register(new Block("cyan_candle", builder().destroyTime(0.1f).pushReaction(PistonBehavior.DESTROY) - .intState(CANDLES) - .booleanState(LIT) - .booleanState(WATERLOGGED))); + .intState(CANDLES) + .booleanState(LIT) + .booleanState(WATERLOGGED))); public static final Block PURPLE_CANDLE = register(new Block("purple_candle", builder().destroyTime(0.1f).pushReaction(PistonBehavior.DESTROY) - .intState(CANDLES) - .booleanState(LIT) - .booleanState(WATERLOGGED))); + .intState(CANDLES) + .booleanState(LIT) + .booleanState(WATERLOGGED))); public static final Block BLUE_CANDLE = register(new Block("blue_candle", builder().destroyTime(0.1f).pushReaction(PistonBehavior.DESTROY) - .intState(CANDLES) - .booleanState(LIT) - .booleanState(WATERLOGGED))); + .intState(CANDLES) + .booleanState(LIT) + .booleanState(WATERLOGGED))); public static final Block BROWN_CANDLE = register(new Block("brown_candle", builder().destroyTime(0.1f).pushReaction(PistonBehavior.DESTROY) - .intState(CANDLES) - .booleanState(LIT) - .booleanState(WATERLOGGED))); + .intState(CANDLES) + .booleanState(LIT) + .booleanState(WATERLOGGED))); public static final Block GREEN_CANDLE = register(new Block("green_candle", builder().destroyTime(0.1f).pushReaction(PistonBehavior.DESTROY) - .intState(CANDLES) - .booleanState(LIT) - .booleanState(WATERLOGGED))); + .intState(CANDLES) + .booleanState(LIT) + .booleanState(WATERLOGGED))); public static final Block RED_CANDLE = register(new Block("red_candle", builder().destroyTime(0.1f).pushReaction(PistonBehavior.DESTROY) - .intState(CANDLES) - .booleanState(LIT) - .booleanState(WATERLOGGED))); + .intState(CANDLES) + .booleanState(LIT) + .booleanState(WATERLOGGED))); public static final Block BLACK_CANDLE = register(new Block("black_candle", builder().destroyTime(0.1f).pushReaction(PistonBehavior.DESTROY) - .intState(CANDLES) - .booleanState(LIT) - .booleanState(WATERLOGGED))); + .intState(CANDLES) + .booleanState(LIT) + .booleanState(WATERLOGGED))); public static final Block CANDLE_CAKE = register(new Block("candle_cake", builder().destroyTime(0.5f).pushReaction(PistonBehavior.DESTROY).pickItem(() -> Items.CAKE) - .booleanState(LIT))); + .booleanState(LIT))); public static final Block WHITE_CANDLE_CAKE = register(new Block("white_candle_cake", builder().destroyTime(0.5f).pushReaction(PistonBehavior.DESTROY).pickItem(() -> Items.CAKE) - .booleanState(LIT))); + .booleanState(LIT))); public static final Block ORANGE_CANDLE_CAKE = register(new Block("orange_candle_cake", builder().destroyTime(0.5f).pushReaction(PistonBehavior.DESTROY).pickItem(() -> Items.CAKE) - .booleanState(LIT))); + .booleanState(LIT))); public static final Block MAGENTA_CANDLE_CAKE = register(new Block("magenta_candle_cake", builder().destroyTime(0.5f).pushReaction(PistonBehavior.DESTROY).pickItem(() -> Items.CAKE) - .booleanState(LIT))); + .booleanState(LIT))); public static final Block LIGHT_BLUE_CANDLE_CAKE = register(new Block("light_blue_candle_cake", builder().destroyTime(0.5f).pushReaction(PistonBehavior.DESTROY).pickItem(() -> Items.CAKE) - .booleanState(LIT))); + .booleanState(LIT))); public static final Block YELLOW_CANDLE_CAKE = register(new Block("yellow_candle_cake", builder().destroyTime(0.5f).pushReaction(PistonBehavior.DESTROY).pickItem(() -> Items.CAKE) - .booleanState(LIT))); + .booleanState(LIT))); public static final Block LIME_CANDLE_CAKE = register(new Block("lime_candle_cake", builder().destroyTime(0.5f).pushReaction(PistonBehavior.DESTROY).pickItem(() -> Items.CAKE) - .booleanState(LIT))); + .booleanState(LIT))); public static final Block PINK_CANDLE_CAKE = register(new Block("pink_candle_cake", builder().destroyTime(0.5f).pushReaction(PistonBehavior.DESTROY).pickItem(() -> Items.CAKE) - .booleanState(LIT))); + .booleanState(LIT))); public static final Block GRAY_CANDLE_CAKE = register(new Block("gray_candle_cake", builder().destroyTime(0.5f).pushReaction(PistonBehavior.DESTROY).pickItem(() -> Items.CAKE) - .booleanState(LIT))); + .booleanState(LIT))); public static final Block LIGHT_GRAY_CANDLE_CAKE = register(new Block("light_gray_candle_cake", builder().destroyTime(0.5f).pushReaction(PistonBehavior.DESTROY).pickItem(() -> Items.CAKE) - .booleanState(LIT))); + .booleanState(LIT))); public static final Block CYAN_CANDLE_CAKE = register(new Block("cyan_candle_cake", builder().destroyTime(0.5f).pushReaction(PistonBehavior.DESTROY).pickItem(() -> Items.CAKE) - .booleanState(LIT))); + .booleanState(LIT))); public static final Block PURPLE_CANDLE_CAKE = register(new Block("purple_candle_cake", builder().destroyTime(0.5f).pushReaction(PistonBehavior.DESTROY).pickItem(() -> Items.CAKE) - .booleanState(LIT))); + .booleanState(LIT))); public static final Block BLUE_CANDLE_CAKE = register(new Block("blue_candle_cake", builder().destroyTime(0.5f).pushReaction(PistonBehavior.DESTROY).pickItem(() -> Items.CAKE) - .booleanState(LIT))); + .booleanState(LIT))); public static final Block BROWN_CANDLE_CAKE = register(new Block("brown_candle_cake", builder().destroyTime(0.5f).pushReaction(PistonBehavior.DESTROY).pickItem(() -> Items.CAKE) - .booleanState(LIT))); + .booleanState(LIT))); public static final Block GREEN_CANDLE_CAKE = register(new Block("green_candle_cake", builder().destroyTime(0.5f).pushReaction(PistonBehavior.DESTROY).pickItem(() -> Items.CAKE) - .booleanState(LIT))); + .booleanState(LIT))); public static final Block RED_CANDLE_CAKE = register(new Block("red_candle_cake", builder().destroyTime(0.5f).pushReaction(PistonBehavior.DESTROY).pickItem(() -> Items.CAKE) - .booleanState(LIT))); + .booleanState(LIT))); public static final Block BLACK_CANDLE_CAKE = register(new Block("black_candle_cake", builder().destroyTime(0.5f).pushReaction(PistonBehavior.DESTROY).pickItem(() -> Items.CAKE) - .booleanState(LIT))); + .booleanState(LIT))); public static final Block AMETHYST_BLOCK = register(new Block("amethyst_block", builder().requiresCorrectToolForDrops().destroyTime(1.5f))); public static final Block BUDDING_AMETHYST = register(new Block("budding_amethyst", builder().requiresCorrectToolForDrops().destroyTime(1.5f).pushReaction(PistonBehavior.DESTROY))); public static final Block AMETHYST_CLUSTER = register(new Block("amethyst_cluster", builder().destroyTime(1.5f).pushReaction(PistonBehavior.DESTROY) - .enumState(FACING, Direction.NORTH, Direction.EAST, Direction.SOUTH, Direction.WEST, Direction.UP, Direction.DOWN) - .booleanState(WATERLOGGED))); + .enumState(FACING, Direction.NORTH, Direction.EAST, Direction.SOUTH, Direction.WEST, Direction.UP, Direction.DOWN) + .booleanState(WATERLOGGED))); public static final Block LARGE_AMETHYST_BUD = register(new Block("large_amethyst_bud", builder().destroyTime(1.5f).pushReaction(PistonBehavior.DESTROY) - .enumState(FACING, Direction.NORTH, Direction.EAST, Direction.SOUTH, Direction.WEST, Direction.UP, Direction.DOWN) - .booleanState(WATERLOGGED))); + .enumState(FACING, Direction.NORTH, Direction.EAST, Direction.SOUTH, Direction.WEST, Direction.UP, Direction.DOWN) + .booleanState(WATERLOGGED))); public static final Block MEDIUM_AMETHYST_BUD = register(new Block("medium_amethyst_bud", builder().destroyTime(1.5f).pushReaction(PistonBehavior.DESTROY) - .enumState(FACING, Direction.NORTH, Direction.EAST, Direction.SOUTH, Direction.WEST, Direction.UP, Direction.DOWN) - .booleanState(WATERLOGGED))); + .enumState(FACING, Direction.NORTH, Direction.EAST, Direction.SOUTH, Direction.WEST, Direction.UP, Direction.DOWN) + .booleanState(WATERLOGGED))); public static final Block SMALL_AMETHYST_BUD = register(new Block("small_amethyst_bud", builder().destroyTime(1.5f).pushReaction(PistonBehavior.DESTROY) - .enumState(FACING, Direction.NORTH, Direction.EAST, Direction.SOUTH, Direction.WEST, Direction.UP, Direction.DOWN) - .booleanState(WATERLOGGED))); + .enumState(FACING, Direction.NORTH, Direction.EAST, Direction.SOUTH, Direction.WEST, Direction.UP, Direction.DOWN) + .booleanState(WATERLOGGED))); public static final Block TUFF = register(new Block("tuff", builder().requiresCorrectToolForDrops().destroyTime(1.5f))); public static final Block TUFF_SLAB = register(new Block("tuff_slab", builder().requiresCorrectToolForDrops().destroyTime(1.5f) - .enumState(SLAB_TYPE) - .booleanState(WATERLOGGED))); + .enumState(SLAB_TYPE) + .booleanState(WATERLOGGED))); public static final Block TUFF_STAIRS = register(new Block("tuff_stairs", builder().requiresCorrectToolForDrops().destroyTime(1.5f) - .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) - .enumState(HALF) - .enumState(STAIRS_SHAPE) - .booleanState(WATERLOGGED))); + .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) + .enumState(HALF) + .enumState(STAIRS_SHAPE) + .booleanState(WATERLOGGED))); public static final Block TUFF_WALL = register(new Block("tuff_wall", builder().requiresCorrectToolForDrops().destroyTime(1.5f) - .enumState(EAST_WALL) - .enumState(NORTH_WALL) - .enumState(SOUTH_WALL) - .booleanState(UP) - .booleanState(WATERLOGGED) - .enumState(WEST_WALL))); + .enumState(EAST_WALL) + .enumState(NORTH_WALL) + .enumState(SOUTH_WALL) + .booleanState(UP) + .booleanState(WATERLOGGED) + .enumState(WEST_WALL))); public static final Block POLISHED_TUFF = register(new Block("polished_tuff", builder().requiresCorrectToolForDrops().destroyTime(1.5f))); public static final Block POLISHED_TUFF_SLAB = register(new Block("polished_tuff_slab", builder().requiresCorrectToolForDrops().destroyTime(1.5f) - .enumState(SLAB_TYPE) - .booleanState(WATERLOGGED))); + .enumState(SLAB_TYPE) + .booleanState(WATERLOGGED))); public static final Block POLISHED_TUFF_STAIRS = register(new Block("polished_tuff_stairs", builder().requiresCorrectToolForDrops().destroyTime(1.5f) - .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) - .enumState(HALF) - .enumState(STAIRS_SHAPE) - .booleanState(WATERLOGGED))); + .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) + .enumState(HALF) + .enumState(STAIRS_SHAPE) + .booleanState(WATERLOGGED))); public static final Block POLISHED_TUFF_WALL = register(new Block("polished_tuff_wall", builder().requiresCorrectToolForDrops().destroyTime(1.5f) - .enumState(EAST_WALL) - .enumState(NORTH_WALL) - .enumState(SOUTH_WALL) - .booleanState(UP) - .booleanState(WATERLOGGED) - .enumState(WEST_WALL))); + .enumState(EAST_WALL) + .enumState(NORTH_WALL) + .enumState(SOUTH_WALL) + .booleanState(UP) + .booleanState(WATERLOGGED) + .enumState(WEST_WALL))); public static final Block CHISELED_TUFF = register(new Block("chiseled_tuff", builder().requiresCorrectToolForDrops().destroyTime(1.5f))); public static final Block TUFF_BRICKS = register(new Block("tuff_bricks", builder().requiresCorrectToolForDrops().destroyTime(1.5f))); public static final Block TUFF_BRICK_SLAB = register(new Block("tuff_brick_slab", builder().requiresCorrectToolForDrops().destroyTime(1.5f) - .enumState(SLAB_TYPE) - .booleanState(WATERLOGGED))); + .enumState(SLAB_TYPE) + .booleanState(WATERLOGGED))); public static final Block TUFF_BRICK_STAIRS = register(new Block("tuff_brick_stairs", builder().requiresCorrectToolForDrops().destroyTime(1.5f) - .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) - .enumState(HALF) - .enumState(STAIRS_SHAPE) - .booleanState(WATERLOGGED))); + .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) + .enumState(HALF) + .enumState(STAIRS_SHAPE) + .booleanState(WATERLOGGED))); public static final Block TUFF_BRICK_WALL = register(new Block("tuff_brick_wall", builder().requiresCorrectToolForDrops().destroyTime(1.5f) - .enumState(EAST_WALL) - .enumState(NORTH_WALL) - .enumState(SOUTH_WALL) - .booleanState(UP) - .booleanState(WATERLOGGED) - .enumState(WEST_WALL))); + .enumState(EAST_WALL) + .enumState(NORTH_WALL) + .enumState(SOUTH_WALL) + .booleanState(UP) + .booleanState(WATERLOGGED) + .enumState(WEST_WALL))); public static final Block CHISELED_TUFF_BRICKS = register(new Block("chiseled_tuff_bricks", builder().requiresCorrectToolForDrops().destroyTime(1.5f))); public static final Block CALCITE = register(new Block("calcite", builder().requiresCorrectToolForDrops().destroyTime(0.75f))); public static final Block TINTED_GLASS = register(new Block("tinted_glass", builder().destroyTime(0.3f))); public static final Block POWDER_SNOW = register(new Block("powder_snow", builder().destroyTime(0.25f))); public static final Block SCULK_SENSOR = register(new Block("sculk_sensor", builder().setBlockEntity(BlockEntityType.SCULK_SENSOR).destroyTime(1.5f) - .intState(POWER) - .enumState(SCULK_SENSOR_PHASE) - .booleanState(WATERLOGGED))); + .intState(POWER) + .enumState(SCULK_SENSOR_PHASE) + .booleanState(WATERLOGGED))); public static final Block CALIBRATED_SCULK_SENSOR = register(new Block("calibrated_sculk_sensor", builder().setBlockEntity(BlockEntityType.CALIBRATED_SCULK_SENSOR).destroyTime(1.5f) - .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) - .intState(POWER) - .enumState(SCULK_SENSOR_PHASE) - .booleanState(WATERLOGGED))); + .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) + .intState(POWER) + .enumState(SCULK_SENSOR_PHASE) + .booleanState(WATERLOGGED))); public static final Block SCULK = register(new Block("sculk", builder().destroyTime(0.2f))); public static final Block SCULK_VEIN = register(new Block("sculk_vein", builder().destroyTime(0.2f).pushReaction(PistonBehavior.DESTROY) - .booleanState(DOWN) - .booleanState(EAST) - .booleanState(NORTH) - .booleanState(SOUTH) - .booleanState(UP) - .booleanState(WATERLOGGED) - .booleanState(WEST))); + .booleanState(DOWN) + .booleanState(EAST) + .booleanState(NORTH) + .booleanState(SOUTH) + .booleanState(UP) + .booleanState(WATERLOGGED) + .booleanState(WEST))); public static final Block SCULK_CATALYST = register(new Block("sculk_catalyst", builder().setBlockEntity(BlockEntityType.SCULK_CATALYST).destroyTime(3.0f) - .booleanState(BLOOM))); + .booleanState(BLOOM))); public static final Block SCULK_SHRIEKER = register(new Block("sculk_shrieker", builder().setBlockEntity(BlockEntityType.SCULK_SHRIEKER).destroyTime(3.0f) - .booleanState(CAN_SUMMON) - .booleanState(SHRIEKING) - .booleanState(WATERLOGGED))); + .booleanState(CAN_SUMMON) + .booleanState(SHRIEKING) + .booleanState(WATERLOGGED))); public static final Block COPPER_BLOCK = register(new Block("copper_block", builder().requiresCorrectToolForDrops().destroyTime(3.0f))); public static final Block EXPOSED_COPPER = register(new Block("exposed_copper", builder().requiresCorrectToolForDrops().destroyTime(3.0f))); public static final Block WEATHERED_COPPER = register(new Block("weathered_copper", builder().requiresCorrectToolForDrops().destroyTime(3.0f))); @@ -2466,37 +2535,37 @@ public final class Blocks { public static final Block WAXED_EXPOSED_CHISELED_COPPER = register(new Block("waxed_exposed_chiseled_copper", builder().requiresCorrectToolForDrops().destroyTime(3.0f))); public static final Block WAXED_CHISELED_COPPER = register(new Block("waxed_chiseled_copper", builder().requiresCorrectToolForDrops().destroyTime(3.0f))); public static final Block OXIDIZED_CUT_COPPER_STAIRS = register(new Block("oxidized_cut_copper_stairs", builder().requiresCorrectToolForDrops().destroyTime(3.0f) - .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) - .enumState(HALF) - .enumState(STAIRS_SHAPE) - .booleanState(WATERLOGGED))); + .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) + .enumState(HALF) + .enumState(STAIRS_SHAPE) + .booleanState(WATERLOGGED))); public static final Block WEATHERED_CUT_COPPER_STAIRS = register(new Block("weathered_cut_copper_stairs", builder().requiresCorrectToolForDrops().destroyTime(3.0f) - .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) - .enumState(HALF) - .enumState(STAIRS_SHAPE) - .booleanState(WATERLOGGED))); + .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) + .enumState(HALF) + .enumState(STAIRS_SHAPE) + .booleanState(WATERLOGGED))); public static final Block EXPOSED_CUT_COPPER_STAIRS = register(new Block("exposed_cut_copper_stairs", builder().requiresCorrectToolForDrops().destroyTime(3.0f) - .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) - .enumState(HALF) - .enumState(STAIRS_SHAPE) - .booleanState(WATERLOGGED))); + .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) + .enumState(HALF) + .enumState(STAIRS_SHAPE) + .booleanState(WATERLOGGED))); public static final Block CUT_COPPER_STAIRS = register(new Block("cut_copper_stairs", builder().requiresCorrectToolForDrops().destroyTime(3.0f) - .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) - .enumState(HALF) - .enumState(STAIRS_SHAPE) - .booleanState(WATERLOGGED))); + .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) + .enumState(HALF) + .enumState(STAIRS_SHAPE) + .booleanState(WATERLOGGED))); public static final Block OXIDIZED_CUT_COPPER_SLAB = register(new Block("oxidized_cut_copper_slab", builder().requiresCorrectToolForDrops().destroyTime(3.0f) - .enumState(SLAB_TYPE) - .booleanState(WATERLOGGED))); + .enumState(SLAB_TYPE) + .booleanState(WATERLOGGED))); public static final Block WEATHERED_CUT_COPPER_SLAB = register(new Block("weathered_cut_copper_slab", builder().requiresCorrectToolForDrops().destroyTime(3.0f) - .enumState(SLAB_TYPE) - .booleanState(WATERLOGGED))); + .enumState(SLAB_TYPE) + .booleanState(WATERLOGGED))); public static final Block EXPOSED_CUT_COPPER_SLAB = register(new Block("exposed_cut_copper_slab", builder().requiresCorrectToolForDrops().destroyTime(3.0f) - .enumState(SLAB_TYPE) - .booleanState(WATERLOGGED))); + .enumState(SLAB_TYPE) + .booleanState(WATERLOGGED))); public static final Block CUT_COPPER_SLAB = register(new Block("cut_copper_slab", builder().requiresCorrectToolForDrops().destroyTime(3.0f) - .enumState(SLAB_TYPE) - .booleanState(WATERLOGGED))); + .enumState(SLAB_TYPE) + .booleanState(WATERLOGGED))); public static final Block WAXED_COPPER_BLOCK = register(new Block("waxed_copper_block", builder().requiresCorrectToolForDrops().destroyTime(3.0f))); public static final Block WAXED_WEATHERED_COPPER = register(new Block("waxed_weathered_copper", builder().requiresCorrectToolForDrops().destroyTime(3.0f))); public static final Block WAXED_EXPOSED_COPPER = register(new Block("waxed_exposed_copper", builder().requiresCorrectToolForDrops().destroyTime(3.0f))); @@ -2506,281 +2575,281 @@ public final class Blocks { public static final Block WAXED_EXPOSED_CUT_COPPER = register(new Block("waxed_exposed_cut_copper", builder().requiresCorrectToolForDrops().destroyTime(3.0f))); public static final Block WAXED_CUT_COPPER = register(new Block("waxed_cut_copper", builder().requiresCorrectToolForDrops().destroyTime(3.0f))); public static final Block WAXED_OXIDIZED_CUT_COPPER_STAIRS = register(new Block("waxed_oxidized_cut_copper_stairs", builder().requiresCorrectToolForDrops().destroyTime(3.0f) - .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) - .enumState(HALF) - .enumState(STAIRS_SHAPE) - .booleanState(WATERLOGGED))); + .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) + .enumState(HALF) + .enumState(STAIRS_SHAPE) + .booleanState(WATERLOGGED))); public static final Block WAXED_WEATHERED_CUT_COPPER_STAIRS = register(new Block("waxed_weathered_cut_copper_stairs", builder().requiresCorrectToolForDrops().destroyTime(3.0f) - .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) - .enumState(HALF) - .enumState(STAIRS_SHAPE) - .booleanState(WATERLOGGED))); + .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) + .enumState(HALF) + .enumState(STAIRS_SHAPE) + .booleanState(WATERLOGGED))); public static final Block WAXED_EXPOSED_CUT_COPPER_STAIRS = register(new Block("waxed_exposed_cut_copper_stairs", builder().requiresCorrectToolForDrops().destroyTime(3.0f) - .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) - .enumState(HALF) - .enumState(STAIRS_SHAPE) - .booleanState(WATERLOGGED))); + .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) + .enumState(HALF) + .enumState(STAIRS_SHAPE) + .booleanState(WATERLOGGED))); public static final Block WAXED_CUT_COPPER_STAIRS = register(new Block("waxed_cut_copper_stairs", builder().requiresCorrectToolForDrops().destroyTime(3.0f) - .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) - .enumState(HALF) - .enumState(STAIRS_SHAPE) - .booleanState(WATERLOGGED))); + .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) + .enumState(HALF) + .enumState(STAIRS_SHAPE) + .booleanState(WATERLOGGED))); public static final Block WAXED_OXIDIZED_CUT_COPPER_SLAB = register(new Block("waxed_oxidized_cut_copper_slab", builder().requiresCorrectToolForDrops().destroyTime(3.0f) - .enumState(SLAB_TYPE) - .booleanState(WATERLOGGED))); + .enumState(SLAB_TYPE) + .booleanState(WATERLOGGED))); public static final Block WAXED_WEATHERED_CUT_COPPER_SLAB = register(new Block("waxed_weathered_cut_copper_slab", builder().requiresCorrectToolForDrops().destroyTime(3.0f) - .enumState(SLAB_TYPE) - .booleanState(WATERLOGGED))); + .enumState(SLAB_TYPE) + .booleanState(WATERLOGGED))); public static final Block WAXED_EXPOSED_CUT_COPPER_SLAB = register(new Block("waxed_exposed_cut_copper_slab", builder().requiresCorrectToolForDrops().destroyTime(3.0f) - .enumState(SLAB_TYPE) - .booleanState(WATERLOGGED))); + .enumState(SLAB_TYPE) + .booleanState(WATERLOGGED))); public static final Block WAXED_CUT_COPPER_SLAB = register(new Block("waxed_cut_copper_slab", builder().requiresCorrectToolForDrops().destroyTime(3.0f) - .enumState(SLAB_TYPE) - .booleanState(WATERLOGGED))); + .enumState(SLAB_TYPE) + .booleanState(WATERLOGGED))); public static final Block COPPER_DOOR = register(new DoorBlock("copper_door", builder().requiresCorrectToolForDrops().destroyTime(3.0f).pushReaction(PistonBehavior.DESTROY) - .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) - .enumState(DOUBLE_BLOCK_HALF) - .enumState(DOOR_HINGE) - .booleanState(OPEN) - .booleanState(POWERED))); + .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) + .enumState(DOUBLE_BLOCK_HALF) + .enumState(DOOR_HINGE) + .booleanState(OPEN) + .booleanState(POWERED))); public static final Block EXPOSED_COPPER_DOOR = register(new DoorBlock("exposed_copper_door", builder().requiresCorrectToolForDrops().destroyTime(3.0f).pushReaction(PistonBehavior.DESTROY) - .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) - .enumState(DOUBLE_BLOCK_HALF) - .enumState(DOOR_HINGE) - .booleanState(OPEN) - .booleanState(POWERED))); + .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) + .enumState(DOUBLE_BLOCK_HALF) + .enumState(DOOR_HINGE) + .booleanState(OPEN) + .booleanState(POWERED))); public static final Block OXIDIZED_COPPER_DOOR = register(new DoorBlock("oxidized_copper_door", builder().requiresCorrectToolForDrops().destroyTime(3.0f).pushReaction(PistonBehavior.DESTROY) - .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) - .enumState(DOUBLE_BLOCK_HALF) - .enumState(DOOR_HINGE) - .booleanState(OPEN) - .booleanState(POWERED))); + .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) + .enumState(DOUBLE_BLOCK_HALF) + .enumState(DOOR_HINGE) + .booleanState(OPEN) + .booleanState(POWERED))); public static final Block WEATHERED_COPPER_DOOR = register(new DoorBlock("weathered_copper_door", builder().requiresCorrectToolForDrops().destroyTime(3.0f).pushReaction(PistonBehavior.DESTROY) - .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) - .enumState(DOUBLE_BLOCK_HALF) - .enumState(DOOR_HINGE) - .booleanState(OPEN) - .booleanState(POWERED))); + .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) + .enumState(DOUBLE_BLOCK_HALF) + .enumState(DOOR_HINGE) + .booleanState(OPEN) + .booleanState(POWERED))); public static final Block WAXED_COPPER_DOOR = register(new DoorBlock("waxed_copper_door", builder().requiresCorrectToolForDrops().destroyTime(3.0f).pushReaction(PistonBehavior.DESTROY) - .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) - .enumState(DOUBLE_BLOCK_HALF) - .enumState(DOOR_HINGE) - .booleanState(OPEN) - .booleanState(POWERED))); + .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) + .enumState(DOUBLE_BLOCK_HALF) + .enumState(DOOR_HINGE) + .booleanState(OPEN) + .booleanState(POWERED))); public static final Block WAXED_EXPOSED_COPPER_DOOR = register(new DoorBlock("waxed_exposed_copper_door", builder().requiresCorrectToolForDrops().destroyTime(3.0f).pushReaction(PistonBehavior.DESTROY) - .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) - .enumState(DOUBLE_BLOCK_HALF) - .enumState(DOOR_HINGE) - .booleanState(OPEN) - .booleanState(POWERED))); + .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) + .enumState(DOUBLE_BLOCK_HALF) + .enumState(DOOR_HINGE) + .booleanState(OPEN) + .booleanState(POWERED))); public static final Block WAXED_OXIDIZED_COPPER_DOOR = register(new DoorBlock("waxed_oxidized_copper_door", builder().requiresCorrectToolForDrops().destroyTime(3.0f).pushReaction(PistonBehavior.DESTROY) - .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) - .enumState(DOUBLE_BLOCK_HALF) - .enumState(DOOR_HINGE) - .booleanState(OPEN) - .booleanState(POWERED))); + .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) + .enumState(DOUBLE_BLOCK_HALF) + .enumState(DOOR_HINGE) + .booleanState(OPEN) + .booleanState(POWERED))); public static final Block WAXED_WEATHERED_COPPER_DOOR = register(new DoorBlock("waxed_weathered_copper_door", builder().requiresCorrectToolForDrops().destroyTime(3.0f).pushReaction(PistonBehavior.DESTROY) - .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) - .enumState(DOUBLE_BLOCK_HALF) - .enumState(DOOR_HINGE) - .booleanState(OPEN) - .booleanState(POWERED))); + .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) + .enumState(DOUBLE_BLOCK_HALF) + .enumState(DOOR_HINGE) + .booleanState(OPEN) + .booleanState(POWERED))); public static final Block COPPER_TRAPDOOR = register(new TrapDoorBlock("copper_trapdoor", builder().requiresCorrectToolForDrops().destroyTime(3.0f) - .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) - .enumState(HALF) - .booleanState(OPEN) - .booleanState(POWERED) - .booleanState(WATERLOGGED))); + .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) + .enumState(HALF) + .booleanState(OPEN) + .booleanState(POWERED) + .booleanState(WATERLOGGED))); public static final Block EXPOSED_COPPER_TRAPDOOR = register(new TrapDoorBlock("exposed_copper_trapdoor", builder().requiresCorrectToolForDrops().destroyTime(3.0f) - .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) - .enumState(HALF) - .booleanState(OPEN) - .booleanState(POWERED) - .booleanState(WATERLOGGED))); + .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) + .enumState(HALF) + .booleanState(OPEN) + .booleanState(POWERED) + .booleanState(WATERLOGGED))); public static final Block OXIDIZED_COPPER_TRAPDOOR = register(new TrapDoorBlock("oxidized_copper_trapdoor", builder().requiresCorrectToolForDrops().destroyTime(3.0f) - .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) - .enumState(HALF) - .booleanState(OPEN) - .booleanState(POWERED) - .booleanState(WATERLOGGED))); + .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) + .enumState(HALF) + .booleanState(OPEN) + .booleanState(POWERED) + .booleanState(WATERLOGGED))); public static final Block WEATHERED_COPPER_TRAPDOOR = register(new TrapDoorBlock("weathered_copper_trapdoor", builder().requiresCorrectToolForDrops().destroyTime(3.0f) - .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) - .enumState(HALF) - .booleanState(OPEN) - .booleanState(POWERED) - .booleanState(WATERLOGGED))); + .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) + .enumState(HALF) + .booleanState(OPEN) + .booleanState(POWERED) + .booleanState(WATERLOGGED))); public static final Block WAXED_COPPER_TRAPDOOR = register(new TrapDoorBlock("waxed_copper_trapdoor", builder().requiresCorrectToolForDrops().destroyTime(3.0f) - .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) - .enumState(HALF) - .booleanState(OPEN) - .booleanState(POWERED) - .booleanState(WATERLOGGED))); + .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) + .enumState(HALF) + .booleanState(OPEN) + .booleanState(POWERED) + .booleanState(WATERLOGGED))); public static final Block WAXED_EXPOSED_COPPER_TRAPDOOR = register(new TrapDoorBlock("waxed_exposed_copper_trapdoor", builder().requiresCorrectToolForDrops().destroyTime(3.0f) - .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) - .enumState(HALF) - .booleanState(OPEN) - .booleanState(POWERED) - .booleanState(WATERLOGGED))); + .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) + .enumState(HALF) + .booleanState(OPEN) + .booleanState(POWERED) + .booleanState(WATERLOGGED))); public static final Block WAXED_OXIDIZED_COPPER_TRAPDOOR = register(new TrapDoorBlock("waxed_oxidized_copper_trapdoor", builder().requiresCorrectToolForDrops().destroyTime(3.0f) - .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) - .enumState(HALF) - .booleanState(OPEN) - .booleanState(POWERED) - .booleanState(WATERLOGGED))); + .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) + .enumState(HALF) + .booleanState(OPEN) + .booleanState(POWERED) + .booleanState(WATERLOGGED))); public static final Block WAXED_WEATHERED_COPPER_TRAPDOOR = register(new TrapDoorBlock("waxed_weathered_copper_trapdoor", builder().requiresCorrectToolForDrops().destroyTime(3.0f) - .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) - .enumState(HALF) - .booleanState(OPEN) - .booleanState(POWERED) - .booleanState(WATERLOGGED))); + .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) + .enumState(HALF) + .booleanState(OPEN) + .booleanState(POWERED) + .booleanState(WATERLOGGED))); public static final Block COPPER_GRATE = register(new Block("copper_grate", builder().requiresCorrectToolForDrops().destroyTime(3.0f) - .booleanState(WATERLOGGED))); + .booleanState(WATERLOGGED))); public static final Block EXPOSED_COPPER_GRATE = register(new Block("exposed_copper_grate", builder().requiresCorrectToolForDrops().destroyTime(3.0f) - .booleanState(WATERLOGGED))); + .booleanState(WATERLOGGED))); public static final Block WEATHERED_COPPER_GRATE = register(new Block("weathered_copper_grate", builder().requiresCorrectToolForDrops().destroyTime(3.0f) - .booleanState(WATERLOGGED))); + .booleanState(WATERLOGGED))); public static final Block OXIDIZED_COPPER_GRATE = register(new Block("oxidized_copper_grate", builder().requiresCorrectToolForDrops().destroyTime(3.0f) - .booleanState(WATERLOGGED))); + .booleanState(WATERLOGGED))); public static final Block WAXED_COPPER_GRATE = register(new Block("waxed_copper_grate", builder().requiresCorrectToolForDrops().destroyTime(3.0f) - .booleanState(WATERLOGGED))); + .booleanState(WATERLOGGED))); public static final Block WAXED_EXPOSED_COPPER_GRATE = register(new Block("waxed_exposed_copper_grate", builder().requiresCorrectToolForDrops().destroyTime(3.0f) - .booleanState(WATERLOGGED))); + .booleanState(WATERLOGGED))); public static final Block WAXED_WEATHERED_COPPER_GRATE = register(new Block("waxed_weathered_copper_grate", builder().requiresCorrectToolForDrops().destroyTime(3.0f) - .booleanState(WATERLOGGED))); + .booleanState(WATERLOGGED))); public static final Block WAXED_OXIDIZED_COPPER_GRATE = register(new Block("waxed_oxidized_copper_grate", builder().requiresCorrectToolForDrops().destroyTime(3.0f) - .booleanState(WATERLOGGED))); + .booleanState(WATERLOGGED))); public static final Block COPPER_BULB = register(new Block("copper_bulb", builder().requiresCorrectToolForDrops().destroyTime(3.0f) - .booleanState(LIT) - .booleanState(POWERED))); + .booleanState(LIT) + .booleanState(POWERED))); public static final Block EXPOSED_COPPER_BULB = register(new Block("exposed_copper_bulb", builder().requiresCorrectToolForDrops().destroyTime(3.0f) - .booleanState(LIT) - .booleanState(POWERED))); + .booleanState(LIT) + .booleanState(POWERED))); public static final Block WEATHERED_COPPER_BULB = register(new Block("weathered_copper_bulb", builder().requiresCorrectToolForDrops().destroyTime(3.0f) - .booleanState(LIT) - .booleanState(POWERED))); + .booleanState(LIT) + .booleanState(POWERED))); public static final Block OXIDIZED_COPPER_BULB = register(new Block("oxidized_copper_bulb", builder().requiresCorrectToolForDrops().destroyTime(3.0f) - .booleanState(LIT) - .booleanState(POWERED))); + .booleanState(LIT) + .booleanState(POWERED))); public static final Block WAXED_COPPER_BULB = register(new Block("waxed_copper_bulb", builder().requiresCorrectToolForDrops().destroyTime(3.0f) - .booleanState(LIT) - .booleanState(POWERED))); + .booleanState(LIT) + .booleanState(POWERED))); public static final Block WAXED_EXPOSED_COPPER_BULB = register(new Block("waxed_exposed_copper_bulb", builder().requiresCorrectToolForDrops().destroyTime(3.0f) - .booleanState(LIT) - .booleanState(POWERED))); + .booleanState(LIT) + .booleanState(POWERED))); public static final Block WAXED_WEATHERED_COPPER_BULB = register(new Block("waxed_weathered_copper_bulb", builder().requiresCorrectToolForDrops().destroyTime(3.0f) - .booleanState(LIT) - .booleanState(POWERED))); + .booleanState(LIT) + .booleanState(POWERED))); public static final Block WAXED_OXIDIZED_COPPER_BULB = register(new Block("waxed_oxidized_copper_bulb", builder().requiresCorrectToolForDrops().destroyTime(3.0f) - .booleanState(LIT) - .booleanState(POWERED))); + .booleanState(LIT) + .booleanState(POWERED))); public static final Block LIGHTNING_ROD = register(new Block("lightning_rod", builder().requiresCorrectToolForDrops().destroyTime(3.0f) - .enumState(FACING, Direction.NORTH, Direction.EAST, Direction.SOUTH, Direction.WEST, Direction.UP, Direction.DOWN) - .booleanState(POWERED) - .booleanState(WATERLOGGED))); + .enumState(FACING, Direction.NORTH, Direction.EAST, Direction.SOUTH, Direction.WEST, Direction.UP, Direction.DOWN) + .booleanState(POWERED) + .booleanState(WATERLOGGED))); public static final Block POINTED_DRIPSTONE = register(new Block("pointed_dripstone", builder().destroyTime(1.5f).pushReaction(PistonBehavior.DESTROY) - .enumState(DRIPSTONE_THICKNESS) - .enumState(VERTICAL_DIRECTION, Direction.UP, Direction.DOWN) - .booleanState(WATERLOGGED))); + .enumState(DRIPSTONE_THICKNESS) + .enumState(VERTICAL_DIRECTION, Direction.UP, Direction.DOWN) + .booleanState(WATERLOGGED))); public static final Block DRIPSTONE_BLOCK = register(new Block("dripstone_block", builder().requiresCorrectToolForDrops().destroyTime(1.5f))); public static final Block CAVE_VINES = register(new Block("cave_vines", builder().pushReaction(PistonBehavior.DESTROY) - .intState(AGE_25) - .booleanState(BERRIES))); + .intState(AGE_25) + .booleanState(BERRIES))); public static final Block CAVE_VINES_PLANT = register(new Block("cave_vines_plant", builder().pushReaction(PistonBehavior.DESTROY).pickItem(() -> Items.GLOW_BERRIES) - .booleanState(BERRIES))); + .booleanState(BERRIES))); public static final Block SPORE_BLOSSOM = register(new Block("spore_blossom", builder().pushReaction(PistonBehavior.DESTROY))); public static final Block AZALEA = register(new Block("azalea", builder().pushReaction(PistonBehavior.DESTROY))); public static final Block FLOWERING_AZALEA = register(new Block("flowering_azalea", builder().pushReaction(PistonBehavior.DESTROY))); public static final Block MOSS_CARPET = register(new Block("moss_carpet", builder().destroyTime(0.1f).pushReaction(PistonBehavior.DESTROY))); public static final Block PINK_PETALS = register(new Block("pink_petals", builder().pushReaction(PistonBehavior.DESTROY) - .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) - .intState(FLOWER_AMOUNT))); + .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) + .intState(FLOWER_AMOUNT))); public static final Block MOSS_BLOCK = register(new Block("moss_block", builder().destroyTime(0.1f).pushReaction(PistonBehavior.DESTROY))); public static final Block BIG_DRIPLEAF = register(new Block("big_dripleaf", builder().destroyTime(0.1f).pushReaction(PistonBehavior.DESTROY) - .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) - .enumState(TILT) - .booleanState(WATERLOGGED))); + .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) + .enumState(TILT) + .booleanState(WATERLOGGED))); public static final Block BIG_DRIPLEAF_STEM = register(new Block("big_dripleaf_stem", builder().destroyTime(0.1f).pushReaction(PistonBehavior.DESTROY) - .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) - .booleanState(WATERLOGGED))); + .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) + .booleanState(WATERLOGGED))); public static final Block SMALL_DRIPLEAF = register(new Block("small_dripleaf", builder().pushReaction(PistonBehavior.DESTROY) - .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) - .enumState(DOUBLE_BLOCK_HALF) - .booleanState(WATERLOGGED))); + .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) + .enumState(DOUBLE_BLOCK_HALF) + .booleanState(WATERLOGGED))); public static final Block HANGING_ROOTS = register(new Block("hanging_roots", builder().pushReaction(PistonBehavior.DESTROY) - .booleanState(WATERLOGGED))); + .booleanState(WATERLOGGED))); public static final Block ROOTED_DIRT = register(new Block("rooted_dirt", builder().destroyTime(0.5f))); public static final Block MUD = register(new Block("mud", builder().destroyTime(0.5f))); public static final Block DEEPSLATE = register(new Block("deepslate", builder().requiresCorrectToolForDrops().destroyTime(3.0f) - .enumState(AXIS, Axis.VALUES))); + .enumState(AXIS, Axis.VALUES))); public static final Block COBBLED_DEEPSLATE = register(new Block("cobbled_deepslate", builder().requiresCorrectToolForDrops().destroyTime(3.5f))); public static final Block COBBLED_DEEPSLATE_STAIRS = register(new Block("cobbled_deepslate_stairs", builder().requiresCorrectToolForDrops().destroyTime(3.5f) - .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) - .enumState(HALF) - .enumState(STAIRS_SHAPE) - .booleanState(WATERLOGGED))); + .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) + .enumState(HALF) + .enumState(STAIRS_SHAPE) + .booleanState(WATERLOGGED))); public static final Block COBBLED_DEEPSLATE_SLAB = register(new Block("cobbled_deepslate_slab", builder().requiresCorrectToolForDrops().destroyTime(3.5f) - .enumState(SLAB_TYPE) - .booleanState(WATERLOGGED))); + .enumState(SLAB_TYPE) + .booleanState(WATERLOGGED))); public static final Block COBBLED_DEEPSLATE_WALL = register(new Block("cobbled_deepslate_wall", builder().requiresCorrectToolForDrops().destroyTime(3.5f) - .enumState(EAST_WALL) - .enumState(NORTH_WALL) - .enumState(SOUTH_WALL) - .booleanState(UP) - .booleanState(WATERLOGGED) - .enumState(WEST_WALL))); + .enumState(EAST_WALL) + .enumState(NORTH_WALL) + .enumState(SOUTH_WALL) + .booleanState(UP) + .booleanState(WATERLOGGED) + .enumState(WEST_WALL))); public static final Block POLISHED_DEEPSLATE = register(new Block("polished_deepslate", builder().requiresCorrectToolForDrops().destroyTime(3.5f))); public static final Block POLISHED_DEEPSLATE_STAIRS = register(new Block("polished_deepslate_stairs", builder().requiresCorrectToolForDrops().destroyTime(3.5f) - .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) - .enumState(HALF) - .enumState(STAIRS_SHAPE) - .booleanState(WATERLOGGED))); + .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) + .enumState(HALF) + .enumState(STAIRS_SHAPE) + .booleanState(WATERLOGGED))); public static final Block POLISHED_DEEPSLATE_SLAB = register(new Block("polished_deepslate_slab", builder().requiresCorrectToolForDrops().destroyTime(3.5f) - .enumState(SLAB_TYPE) - .booleanState(WATERLOGGED))); + .enumState(SLAB_TYPE) + .booleanState(WATERLOGGED))); public static final Block POLISHED_DEEPSLATE_WALL = register(new Block("polished_deepslate_wall", builder().requiresCorrectToolForDrops().destroyTime(3.5f) - .enumState(EAST_WALL) - .enumState(NORTH_WALL) - .enumState(SOUTH_WALL) - .booleanState(UP) - .booleanState(WATERLOGGED) - .enumState(WEST_WALL))); + .enumState(EAST_WALL) + .enumState(NORTH_WALL) + .enumState(SOUTH_WALL) + .booleanState(UP) + .booleanState(WATERLOGGED) + .enumState(WEST_WALL))); public static final Block DEEPSLATE_TILES = register(new Block("deepslate_tiles", builder().requiresCorrectToolForDrops().destroyTime(3.5f))); public static final Block DEEPSLATE_TILE_STAIRS = register(new Block("deepslate_tile_stairs", builder().requiresCorrectToolForDrops().destroyTime(3.5f) - .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) - .enumState(HALF) - .enumState(STAIRS_SHAPE) - .booleanState(WATERLOGGED))); + .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) + .enumState(HALF) + .enumState(STAIRS_SHAPE) + .booleanState(WATERLOGGED))); public static final Block DEEPSLATE_TILE_SLAB = register(new Block("deepslate_tile_slab", builder().requiresCorrectToolForDrops().destroyTime(3.5f) - .enumState(SLAB_TYPE) - .booleanState(WATERLOGGED))); + .enumState(SLAB_TYPE) + .booleanState(WATERLOGGED))); public static final Block DEEPSLATE_TILE_WALL = register(new Block("deepslate_tile_wall", builder().requiresCorrectToolForDrops().destroyTime(3.5f) - .enumState(EAST_WALL) - .enumState(NORTH_WALL) - .enumState(SOUTH_WALL) - .booleanState(UP) - .booleanState(WATERLOGGED) - .enumState(WEST_WALL))); + .enumState(EAST_WALL) + .enumState(NORTH_WALL) + .enumState(SOUTH_WALL) + .booleanState(UP) + .booleanState(WATERLOGGED) + .enumState(WEST_WALL))); public static final Block DEEPSLATE_BRICKS = register(new Block("deepslate_bricks", builder().requiresCorrectToolForDrops().destroyTime(3.5f))); public static final Block DEEPSLATE_BRICK_STAIRS = register(new Block("deepslate_brick_stairs", builder().requiresCorrectToolForDrops().destroyTime(3.5f) - .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) - .enumState(HALF) - .enumState(STAIRS_SHAPE) - .booleanState(WATERLOGGED))); + .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) + .enumState(HALF) + .enumState(STAIRS_SHAPE) + .booleanState(WATERLOGGED))); public static final Block DEEPSLATE_BRICK_SLAB = register(new Block("deepslate_brick_slab", builder().requiresCorrectToolForDrops().destroyTime(3.5f) - .enumState(SLAB_TYPE) - .booleanState(WATERLOGGED))); + .enumState(SLAB_TYPE) + .booleanState(WATERLOGGED))); public static final Block DEEPSLATE_BRICK_WALL = register(new Block("deepslate_brick_wall", builder().requiresCorrectToolForDrops().destroyTime(3.5f) - .enumState(EAST_WALL) - .enumState(NORTH_WALL) - .enumState(SOUTH_WALL) - .booleanState(UP) - .booleanState(WATERLOGGED) - .enumState(WEST_WALL))); + .enumState(EAST_WALL) + .enumState(NORTH_WALL) + .enumState(SOUTH_WALL) + .booleanState(UP) + .booleanState(WATERLOGGED) + .enumState(WEST_WALL))); public static final Block CHISELED_DEEPSLATE = register(new Block("chiseled_deepslate", builder().requiresCorrectToolForDrops().destroyTime(3.5f))); public static final Block CRACKED_DEEPSLATE_BRICKS = register(new Block("cracked_deepslate_bricks", builder().requiresCorrectToolForDrops().destroyTime(3.5f))); public static final Block CRACKED_DEEPSLATE_TILES = register(new Block("cracked_deepslate_tiles", builder().requiresCorrectToolForDrops().destroyTime(3.5f))); public static final Block INFESTED_DEEPSLATE = register(new Block("infested_deepslate", builder().destroyTime(1.5f) - .enumState(AXIS, Axis.VALUES))); + .enumState(AXIS, Axis.VALUES))); public static final Block SMOOTH_BASALT = register(new Block("smooth_basalt", builder().requiresCorrectToolForDrops().destroyTime(1.25f))); public static final Block RAW_IRON_BLOCK = register(new Block("raw_iron_block", builder().requiresCorrectToolForDrops().destroyTime(5.0f))); public static final Block RAW_COPPER_BLOCK = register(new Block("raw_copper_block", builder().requiresCorrectToolForDrops().destroyTime(5.0f))); @@ -2788,30 +2857,39 @@ public final class Blocks { public static final Block POTTED_AZALEA_BUSH = register(new FlowerPotBlock("potted_azalea_bush", AZALEA, builder().pushReaction(PistonBehavior.DESTROY))); public static final Block POTTED_FLOWERING_AZALEA_BUSH = register(new FlowerPotBlock("potted_flowering_azalea_bush", FLOWERING_AZALEA, builder().pushReaction(PistonBehavior.DESTROY))); public static final Block OCHRE_FROGLIGHT = register(new Block("ochre_froglight", builder().destroyTime(0.3f) - .enumState(AXIS, Axis.VALUES))); + .enumState(AXIS, Axis.VALUES))); public static final Block VERDANT_FROGLIGHT = register(new Block("verdant_froglight", builder().destroyTime(0.3f) - .enumState(AXIS, Axis.VALUES))); + .enumState(AXIS, Axis.VALUES))); public static final Block PEARLESCENT_FROGLIGHT = register(new Block("pearlescent_froglight", builder().destroyTime(0.3f) - .enumState(AXIS, Axis.VALUES))); + .enumState(AXIS, Axis.VALUES))); public static final Block FROGSPAWN = register(new Block("frogspawn", builder().pushReaction(PistonBehavior.DESTROY))); public static final Block REINFORCED_DEEPSLATE = register(new Block("reinforced_deepslate", builder().destroyTime(55.0f))); public static final Block DECORATED_POT = register(new Block("decorated_pot", builder().setBlockEntity(BlockEntityType.DECORATED_POT).pushReaction(PistonBehavior.DESTROY) - .booleanState(CRACKED) - .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) - .booleanState(WATERLOGGED))); + .booleanState(CRACKED) + .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) + .booleanState(WATERLOGGED))); public static final Block CRAFTER = register(new Block("crafter", builder().setBlockEntity(BlockEntityType.CRAFTER).destroyTime(1.5f) - .booleanState(CRAFTING) - .enumState(ORIENTATION, FrontAndTop.VALUES) - .booleanState(TRIGGERED))); + .booleanState(CRAFTING) + .enumState(ORIENTATION, FrontAndTop.VALUES) + .booleanState(TRIGGERED))); public static final Block TRIAL_SPAWNER = register(new Block("trial_spawner", builder().setBlockEntity(BlockEntityType.TRIAL_SPAWNER).destroyTime(50.0f) - .booleanState(OMINOUS) - .enumState(TRIAL_SPAWNER_STATE))); + .booleanState(OMINOUS) + .enumState(TRIAL_SPAWNER_STATE))); public static final Block VAULT = register(new Block("vault", builder().setBlockEntity(BlockEntityType.VAULT).destroyTime(50.0f) - .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) - .booleanState(OMINOUS) - .enumState(VAULT_STATE))); + .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) + .booleanState(OMINOUS) + .enumState(VAULT_STATE))); public static final Block HEAVY_CORE = register(new Block("heavy_core", builder().destroyTime(10.0f) - .booleanState(WATERLOGGED))); + .booleanState(WATERLOGGED))); + public static final Block PALE_MOSS_BLOCK = register(new Block("pale_moss_block", builder().destroyTime(0.1f).pushReaction(PistonBehavior.DESTROY))); + public static final Block PALE_MOSS_CARPET = register(new Block("pale_moss_carpet", builder().destroyTime(0.1f).pushReaction(PistonBehavior.DESTROY) + .booleanState(BOTTOM) + .enumState(EAST_WALL) + .enumState(NORTH_WALL) + .enumState(SOUTH_WALL) + .enumState(WEST_WALL))); + public static final Block PALE_HANGING_MOSS = register(new Block("pale_hanging_moss", builder().destroyTime(0.1f).pushReaction(PistonBehavior.DESTROY) + .booleanState(TIP))); private static T register(T block) { block.setJavaId(BlockRegistries.JAVA_BLOCKS.get().size()); diff --git a/core/src/main/java/org/geysermc/geyser/level/block/property/Properties.java b/core/src/main/java/org/geysermc/geyser/level/block/property/Properties.java index 7efa2ef80..3e5f1b510 100644 --- a/core/src/main/java/org/geysermc/geyser/level/block/property/Properties.java +++ b/core/src/main/java/org/geysermc/geyser/level/block/property/Properties.java @@ -47,6 +47,7 @@ public final class Properties { public static final BooleanProperty INVERTED = BooleanProperty.create("inverted"); public static final BooleanProperty IN_WALL = BooleanProperty.create("in_wall"); public static final BooleanProperty LIT = BooleanProperty.create("lit"); + public static final BooleanProperty TIP = BooleanProperty.create("tip"); public static final BooleanProperty LOCKED = BooleanProperty.create("locked"); public static final BooleanProperty OCCUPIED = BooleanProperty.create("occupied"); public static final BooleanProperty OPEN = BooleanProperty.create("open"); @@ -142,5 +143,6 @@ public final class Properties { public static final BooleanProperty CRAFTING = BooleanProperty.create("crafting"); public static final BasicEnumProperty TRIAL_SPAWNER_STATE = BasicEnumProperty.create("trial_spawner_state", "inactive", "waiting_for_players", "active", "waiting_for_reward_ejection", "ejecting_reward", "cooldown"); public static final BasicEnumProperty VAULT_STATE = BasicEnumProperty.create("vault_state", "inactive", "active", "unlocking", "ejecting"); + public static final BasicEnumProperty CREAKING = BasicEnumProperty.create("creaking", "disabled", "dormant", "active"); public static final BooleanProperty OMINOUS = BooleanProperty.create("ominous"); } diff --git a/core/src/main/java/org/geysermc/geyser/level/block/type/BlockState.java b/core/src/main/java/org/geysermc/geyser/level/block/type/BlockState.java index 2513e3ceb..32aa2fd3f 100644 --- a/core/src/main/java/org/geysermc/geyser/level/block/type/BlockState.java +++ b/core/src/main/java/org/geysermc/geyser/level/block/type/BlockState.java @@ -63,7 +63,7 @@ public final class BlockState { return null; } //noinspection unchecked - return (T) get(property); + return (T) value; } public > T getValue(Property property, T def) { diff --git a/core/src/main/java/org/geysermc/geyser/level/physics/CollisionManager.java b/core/src/main/java/org/geysermc/geyser/level/physics/CollisionManager.java index a0fb312b4..69f88e2a1 100644 --- a/core/src/main/java/org/geysermc/geyser/level/physics/CollisionManager.java +++ b/core/src/main/java/org/geysermc/geyser/level/physics/CollisionManager.java @@ -27,6 +27,7 @@ package org.geysermc.geyser.level.physics; import lombok.Getter; import lombok.Setter; +import net.kyori.adventure.util.TriState; import org.checkerframework.checker.nullness.qual.Nullable; import org.cloudburstmc.math.GenericMath; import org.cloudburstmc.math.vector.Vector3d; @@ -157,7 +158,7 @@ public class CollisionManager { * @param teleported whether the Bedrock player has teleported to a new position. If true, movement correction is skipped. * @return the position to send to the Java server, or null to cancel sending the packet */ - public @Nullable Vector3d adjustBedrockPosition(Vector3f bedrockPosition, boolean onGround, boolean teleported) { + public @Nullable CollisionResult adjustBedrockPosition(Vector3f bedrockPosition, boolean onGround, boolean teleported) { PistonCache pistonCache = session.getPistonCache(); // Bedrock clients tend to fall off of honey blocks, so we need to teleport them to the new position if (pistonCache.isPlayerAttachedToHoney()) { @@ -176,7 +177,7 @@ public class CollisionManager { playerBoundingBox.setMiddleY(position.getY() + playerBoundingBox.getSizeY() / 2); playerBoundingBox.setMiddleZ(position.getZ()); - return playerBoundingBox.getBottomCenter(); + return new CollisionResult(playerBoundingBox.getBottomCenter(), TriState.NOT_SET); } Vector3d startingPos = playerBoundingBox.getBottomCenter(); @@ -204,16 +205,16 @@ public class CollisionManager { PlayerEntity playerEntity = session.getPlayerEntity(); // Client will dismount if on a vehicle if (playerEntity.getVehicle() == null && pistonCache.getPlayerMotion().equals(Vector3f.ZERO) && !pistonCache.isPlayerSlimeCollision()) { - playerEntity.moveAbsolute(position.toFloat(), playerEntity.getYaw(), playerEntity.getPitch(), playerEntity.getHeadYaw(), newOnGround, true); + playerEntity.moveAbsolute(position.toFloat(), playerEntity.getYaw(), playerEntity.getPitch(), playerEntity.getHeadYaw(), onGround, true); } } - if (!onGround) { + if (!newOnGround) { // Trim the position to prevent rounding errors that make Java think we are clipping into a block position = Vector3d.from(position.getX(), Double.parseDouble(DECIMAL_FORMAT.format(position.getY())), position.getZ()); } - return position; + return new CollisionResult(position, TriState.byBoolean(onGround)); } // TODO: This makes the player look upwards for some reason, rotation values must be wrong @@ -415,44 +416,38 @@ public class CollisionManager { return BlockUtils.getCollision(blockId); } - /** - * @return true if the block located at the player's floor position plus 1 would intersect with the player, - * were they not sneaking - */ - public boolean mustPlayerSneakHere() { - return checkPose(EntityDefinitions.PLAYER.height()); - } - - /** - * @return true if the block located at the player's floor position plus 1 would intersect with the player, - * were they not crawling - */ - public boolean mustPlayerCrawlHere() { - return checkPose(PlayerEntity.SNEAKING_POSE_HEIGHT); - } - - /** - * @param height check and see if this height is invalid in the current player position - */ - private boolean checkPose(float height) { - Vector3i position = session.getPlayerEntity().getPosition().toInt(); - BlockCollision collision = BlockUtils.getCollisionAt(session, position); - if (collision != null) { - // Determine, if the player's bounding box *were* at full height, if it would intersect with the block - // at the current location. - double originalY = playerBoundingBox.getMiddleY(); - double originalHeight = playerBoundingBox.getSizeY(); - double standingY = originalY - (originalHeight / 2.0) + (height / 2.0); - - playerBoundingBox.setSizeY(EntityDefinitions.PLAYER.height()); - playerBoundingBox.setMiddleY(standingY); - boolean result = collision.checkIntersection(position, playerBoundingBox); - result |= session.getPistonCache().checkCollision(position, playerBoundingBox); - playerBoundingBox.setSizeY(originalHeight); - playerBoundingBox.setMiddleY(originalY); - return result; + public boolean isOnGround() { + // Temporary until pre-1.21.30 support is dropped. + Vector3d bottomCenter = playerBoundingBox.getBottomCenter(); + Vector3i groundPos = Vector3i.from(bottomCenter.getX(), bottomCenter.getY() - 1, bottomCenter.getZ()); + BlockCollision collision = BlockUtils.getCollisionAt(session, groundPos); + if (collision == null) { + return false; // Probably air. } - return false; + + // Hack to not check below the player + playerBoundingBox.setSizeY(playerBoundingBox.getSizeY() - 0.001); + playerBoundingBox.setMiddleY(playerBoundingBox.getMiddleY() + 0.002); + + boolean intersected = collision.checkIntersection(groundPos.getX(), groundPos.getY(), groundPos.getZ(), playerBoundingBox); + + playerBoundingBox.setSizeY(playerBoundingBox.getSizeY() + 0.001); + playerBoundingBox.setMiddleY(playerBoundingBox.getMiddleY() - 0.002); + + boolean result; + if (intersected) { + result = true; + } else { + // Hack to check slightly below the player + playerBoundingBox.setSizeY(playerBoundingBox.getSizeY() + 0.001); + playerBoundingBox.setMiddleY(playerBoundingBox.getMiddleY() - 0.002); + + result = collision.checkIntersection(groundPos.getX(), groundPos.getY(), groundPos.getZ(), playerBoundingBox); + + playerBoundingBox.setSizeY(playerBoundingBox.getSizeY() - 0.001); + playerBoundingBox.setMiddleY(playerBoundingBox.getMiddleY() + 0.002); + } + return result; } /** diff --git a/core/src/main/java/org/geysermc/geyser/util/Ordered.java b/core/src/main/java/org/geysermc/geyser/level/physics/CollisionResult.java similarity index 82% rename from core/src/main/java/org/geysermc/geyser/util/Ordered.java rename to core/src/main/java/org/geysermc/geyser/level/physics/CollisionResult.java index 08ff5df72..3c8271cd9 100644 --- a/core/src/main/java/org/geysermc/geyser/util/Ordered.java +++ b/core/src/main/java/org/geysermc/geyser/level/physics/CollisionResult.java @@ -23,11 +23,13 @@ * @link https://github.com/GeyserMC/Geyser */ -package org.geysermc.geyser.util; +package org.geysermc.geyser.level.physics; + +import net.kyori.adventure.util.TriState; +import org.cloudburstmc.math.vector.Vector3d; /** - * Represents anything that could be tracked like a enum, without also creating a name and enum-wide array. + * Holds the result of a collision check. */ -public interface Ordered { - int ordinal(); +public record CollisionResult(Vector3d correctedMovement, TriState onGround) { } diff --git a/core/src/main/java/org/geysermc/geyser/network/CodecProcessor.java b/core/src/main/java/org/geysermc/geyser/network/CodecProcessor.java index 741369c46..15aa8bc2c 100644 --- a/core/src/main/java/org/geysermc/geyser/network/CodecProcessor.java +++ b/core/src/main/java/org/geysermc/geyser/network/CodecProcessor.java @@ -31,11 +31,13 @@ import org.cloudburstmc.protocol.bedrock.codec.BedrockCodecHelper; import org.cloudburstmc.protocol.bedrock.codec.BedrockPacketSerializer; import org.cloudburstmc.protocol.bedrock.codec.v291.serializer.MobArmorEquipmentSerializer_v291; import org.cloudburstmc.protocol.bedrock.codec.v291.serializer.MobEquipmentSerializer_v291; +import org.cloudburstmc.protocol.bedrock.codec.v291.serializer.MoveEntityAbsoluteSerializer_v291; import org.cloudburstmc.protocol.bedrock.codec.v291.serializer.PlayerHotbarSerializer_v291; import org.cloudburstmc.protocol.bedrock.codec.v291.serializer.SetEntityLinkSerializer_v291; import org.cloudburstmc.protocol.bedrock.codec.v390.serializer.PlayerSkinSerializer_v390; import org.cloudburstmc.protocol.bedrock.codec.v407.serializer.InventoryContentSerializer_v407; import org.cloudburstmc.protocol.bedrock.codec.v407.serializer.InventorySlotSerializer_v407; +import org.cloudburstmc.protocol.bedrock.codec.v419.serializer.MovePlayerSerializer_v419; import org.cloudburstmc.protocol.bedrock.codec.v486.serializer.BossEventSerializer_v486; import org.cloudburstmc.protocol.bedrock.codec.v557.serializer.SetEntityDataSerializer_v557; import org.cloudburstmc.protocol.bedrock.codec.v662.serializer.SetEntityMotionSerializer_v662; @@ -44,6 +46,8 @@ import org.cloudburstmc.protocol.bedrock.codec.v712.serializer.InventorySlotSeri import org.cloudburstmc.protocol.bedrock.codec.v712.serializer.MobArmorEquipmentSerializer_v712; import org.cloudburstmc.protocol.bedrock.codec.v729.serializer.InventoryContentSerializer_v729; import org.cloudburstmc.protocol.bedrock.codec.v729.serializer.InventorySlotSerializer_v729; +import org.cloudburstmc.protocol.bedrock.codec.v748.serializer.InventoryContentSerializer_v748; +import org.cloudburstmc.protocol.bedrock.codec.v748.serializer.InventorySlotSerializer_v748; import org.cloudburstmc.protocol.bedrock.packet.AnvilDamagePacket; import org.cloudburstmc.protocol.bedrock.packet.BedrockPacket; import org.cloudburstmc.protocol.bedrock.packet.BossEventPacket; @@ -65,15 +69,18 @@ import org.cloudburstmc.protocol.bedrock.packet.MapCreateLockedCopyPacket; import org.cloudburstmc.protocol.bedrock.packet.MapInfoRequestPacket; import org.cloudburstmc.protocol.bedrock.packet.MobArmorEquipmentPacket; import org.cloudburstmc.protocol.bedrock.packet.MobEquipmentPacket; +import org.cloudburstmc.protocol.bedrock.packet.MoveEntityAbsolutePacket; +import org.cloudburstmc.protocol.bedrock.packet.MovePlayerPacket; import org.cloudburstmc.protocol.bedrock.packet.MultiplayerSettingsPacket; import org.cloudburstmc.protocol.bedrock.packet.NpcRequestPacket; import org.cloudburstmc.protocol.bedrock.packet.PhotoInfoRequestPacket; import org.cloudburstmc.protocol.bedrock.packet.PhotoTransferPacket; -import org.cloudburstmc.protocol.bedrock.packet.PlayerAuthInputPacket; import org.cloudburstmc.protocol.bedrock.packet.PlayerHotbarPacket; +import org.cloudburstmc.protocol.bedrock.packet.PlayerInputPacket; import org.cloudburstmc.protocol.bedrock.packet.PlayerSkinPacket; import org.cloudburstmc.protocol.bedrock.packet.PurchaseReceiptPacket; import org.cloudburstmc.protocol.bedrock.packet.RefreshEntitlementsPacket; +import org.cloudburstmc.protocol.bedrock.packet.RiderJumpPacket; import org.cloudburstmc.protocol.bedrock.packet.ScriptMessagePacket; import org.cloudburstmc.protocol.bedrock.packet.SetEntityDataPacket; import org.cloudburstmc.protocol.bedrock.packet.SetEntityLinkPacket; @@ -140,6 +147,13 @@ class CodecProcessor { } }; + private static final BedrockPacketSerializer INVENTORY_CONTENT_SERIALIZER_V748 = new InventoryContentSerializer_v748() { + @Override + public void deserialize(ByteBuf buffer, BedrockCodecHelper helper, InventoryContentPacket packet) { + throw new IllegalArgumentException("Client cannot send InventoryContentPacket in server-auth inventory environment!"); + } + }; + private static final BedrockPacketSerializer INVENTORY_CONTENT_SERIALIZER_V729 = new InventoryContentSerializer_v729() { @Override public void deserialize(ByteBuf buffer, BedrockCodecHelper helper, InventoryContentPacket packet) { @@ -174,6 +188,27 @@ class CodecProcessor { } }; + private static final BedrockPacketSerializer INVENTORY_SLOT_SERIALIZER_V748 = new InventorySlotSerializer_v748() { + @Override + public void deserialize(ByteBuf buffer, BedrockCodecHelper helper, InventorySlotPacket packet) { + throw new IllegalArgumentException("Client cannot send InventorySlotPacket in server-auth inventory environment!"); + } + }; + + private static final BedrockPacketSerializer MOVE_PLAYER_SERIALIZER = new MovePlayerSerializer_v419() { + @Override + public void deserialize(ByteBuf buffer, BedrockCodecHelper helper, MovePlayerPacket packet) { + throw new IllegalArgumentException("Client cannot send MovePlayerPacket in server-auth movement environment!"); + } + }; + + private static final BedrockPacketSerializer MOVE_ENTITY_SERIALIZER = new MoveEntityAbsoluteSerializer_v291() { + @Override + public void deserialize(ByteBuf buffer, BedrockCodecHelper helper, MoveEntityAbsolutePacket packet) { + throw new IllegalArgumentException("Client cannot send MoveEntityAbsolutePacket in server-auth movement environment!"); + } + }; + /** * Serializer that does nothing when trying to deserialize BossEventPacket since it is not used from the client. */ @@ -262,11 +297,14 @@ class CodecProcessor { @SuppressWarnings("unchecked") static BedrockCodec processCodec(BedrockCodec codec) { + boolean is748OrAbove = codec.getProtocolVersion() >= 748; boolean is729OrAbove = codec.getProtocolVersion() >= 729; boolean is712OrAbove = codec.getProtocolVersion() >= 712; BedrockPacketSerializer inventoryContentSerializer; - if (is729OrAbove) { + if (is748OrAbove) { + inventoryContentSerializer = INVENTORY_CONTENT_SERIALIZER_V748; + } else if (is729OrAbove) { inventoryContentSerializer = INVENTORY_CONTENT_SERIALIZER_V729; } else if (is712OrAbove) { inventoryContentSerializer = INVENTORY_CONTENT_SERIALIZER_V712; @@ -275,7 +313,9 @@ class CodecProcessor { } BedrockPacketSerializer inventorySlotSerializer; - if (is729OrAbove) { + if (is748OrAbove) { + inventorySlotSerializer = INVENTORY_SLOT_SERIALIZER_V748; + } else if (is729OrAbove) { inventorySlotSerializer = INVENTORY_SLOT_SERIALIZER_V729; } else if (is712OrAbove) { inventorySlotSerializer = INVENTORY_SLOT_SERIALIZER_V712; @@ -297,7 +337,6 @@ class CodecProcessor { .updateSerializer(ClientCheatAbilityPacket.class, ILLEGAL_SERIALIZER) .updateSerializer(CraftingEventPacket.class, ILLEGAL_SERIALIZER) // Illegal unusued serverbound packets that relate to unused features - .updateSerializer(PlayerAuthInputPacket.class, ILLEGAL_SERIALIZER) .updateSerializer(ClientCacheBlobStatusPacket.class, ILLEGAL_SERIALIZER) .updateSerializer(SubClientLoginPacket.class, ILLEGAL_SERIALIZER) .updateSerializer(SubChunkRequestPacket.class, ILLEGAL_SERIALIZER) @@ -313,6 +352,10 @@ class CodecProcessor { // Illegal when serverbound due to Geyser specific setup .updateSerializer(InventoryContentPacket.class, inventoryContentSerializer) .updateSerializer(InventorySlotPacket.class, inventorySlotSerializer) + .updateSerializer(MovePlayerPacket.class, MOVE_PLAYER_SERIALIZER) + .updateSerializer(MoveEntityAbsolutePacket.class, MOVE_ENTITY_SERIALIZER) + .updateSerializer(RiderJumpPacket.class, ILLEGAL_SERIALIZER) + .updateSerializer(PlayerInputPacket.class, ILLEGAL_SERIALIZER) // Ignored only when serverbound .updateSerializer(BossEventPacket.class, BOSS_EVENT_SERIALIZER) .updateSerializer(MobArmorEquipmentPacket.class, is712OrAbove ? MOB_ARMOR_EQUIPMENT_SERIALIZER_V712 : MOB_ARMOR_EQUIPMENT_SERIALIZER_V291) 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 d3abf934f..463cc54fd 100644 --- a/core/src/main/java/org/geysermc/geyser/network/GameProtocol.java +++ b/core/src/main/java/org/geysermc/geyser/network/GameProtocol.java @@ -32,6 +32,7 @@ import org.cloudburstmc.protocol.bedrock.codec.v685.Bedrock_v685; import org.cloudburstmc.protocol.bedrock.codec.v686.Bedrock_v686; import org.cloudburstmc.protocol.bedrock.codec.v712.Bedrock_v712; import org.cloudburstmc.protocol.bedrock.codec.v729.Bedrock_v729; +import org.cloudburstmc.protocol.bedrock.codec.v748.Bedrock_v748; import org.cloudburstmc.protocol.bedrock.netty.codec.packet.BedrockPacketCodec; import org.geysermc.geyser.session.GeyserSession; import org.geysermc.mcprotocollib.protocol.codec.MinecraftCodec; @@ -50,8 +51,8 @@ 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 BedrockCodec DEFAULT_BEDROCK_CODEC = CodecProcessor.processCodec(Bedrock_v729.CODEC.toBuilder() - .minecraftVersion("1.21.30") + public static final BedrockCodec DEFAULT_BEDROCK_CODEC = CodecProcessor.processCodec(Bedrock_v748.CODEC.toBuilder() + .minecraftVersion("1.21.44") .build()); /** @@ -78,7 +79,12 @@ public final class GameProtocol { SUPPORTED_BEDROCK_CODECS.add(CodecProcessor.processCodec(Bedrock_v712.CODEC.toBuilder() .minecraftVersion("1.21.20 - 1.21.23") .build())); - SUPPORTED_BEDROCK_CODECS.add(DEFAULT_BEDROCK_CODEC); + SUPPORTED_BEDROCK_CODECS.add(CodecProcessor.processCodec(Bedrock_v729.CODEC.toBuilder() + .minecraftVersion("1.21.30/1.21.31") + .build())); + SUPPORTED_BEDROCK_CODECS.add(DEFAULT_BEDROCK_CODEC.toBuilder() + .minecraftVersion("1.21.40 - 1.21.44") + .build()); } /** @@ -105,6 +111,14 @@ public final class GameProtocol { return session.getUpstream().getProtocolVersion() < Bedrock_v686.CODEC.getProtocolVersion(); } + public static boolean isPre1_21_30(GeyserSession session) { + return session.getUpstream().getProtocolVersion() < Bedrock_v729.CODEC.getProtocolVersion(); + } + + public static boolean isPre1_21_40(GeyserSession session) { + return session.getUpstream().getProtocolVersion() < Bedrock_v748.CODEC.getProtocolVersion(); + } + /** * Gets the {@link PacketCodec} for Minecraft: Java Edition. * 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 e39c67810..8a4e7c4d9 100644 --- a/core/src/main/java/org/geysermc/geyser/network/UpstreamPacketHandler.java +++ b/core/src/main/java/org/geysermc/geyser/network/UpstreamPacketHandler.java @@ -26,6 +26,7 @@ package org.geysermc.geyser.network; import io.netty.buffer.Unpooled; +import org.cloudburstmc.math.vector.Vector2f; import org.cloudburstmc.protocol.bedrock.BedrockDisconnectReasons; import org.cloudburstmc.protocol.bedrock.codec.BedrockCodec; import org.cloudburstmc.protocol.bedrock.codec.compat.BedrockCompat; @@ -38,9 +39,9 @@ import org.cloudburstmc.protocol.bedrock.netty.codec.compression.ZlibCompression import org.cloudburstmc.protocol.bedrock.packet.BedrockPacket; import org.cloudburstmc.protocol.bedrock.packet.LoginPacket; import org.cloudburstmc.protocol.bedrock.packet.ModalFormResponsePacket; -import org.cloudburstmc.protocol.bedrock.packet.MovePlayerPacket; import org.cloudburstmc.protocol.bedrock.packet.NetworkSettingsPacket; import org.cloudburstmc.protocol.bedrock.packet.PlayStatusPacket; +import org.cloudburstmc.protocol.bedrock.packet.PlayerAuthInputPacket; import org.cloudburstmc.protocol.bedrock.packet.RequestNetworkSettingsPacket; import org.cloudburstmc.protocol.bedrock.packet.ResourcePackChunkDataPacket; import org.cloudburstmc.protocol.bedrock.packet.ResourcePackChunkRequestPacket; @@ -209,7 +210,7 @@ public class UpstreamPacketHandler extends LoggingPacketHandler { ResourcePackManifest.Header header = pack.manifest().header(); resourcePacksInfo.getResourcePackInfos().add(new ResourcePacksInfoPacket.Entry( header.uuid().toString(), header.version().toString(), codec.size(), pack.contentKey(), - "", header.uuid().toString(), false, false, false)); + "", header.uuid().toString(), false, false, false, "")); } resourcePacksInfo.setForcedToAccept(GeyserImpl.getInstance().config().forceResourcePacks()); session.sendUpstreamPacket(resourcePacksInfo); @@ -290,8 +291,9 @@ public class UpstreamPacketHandler extends LoggingPacketHandler { } @Override - public PacketSignal handle(MovePlayerPacket packet) { - if (session.isLoggingIn()) { + public PacketSignal handle(PlayerAuthInputPacket packet) { + // This doesn't catch rotation, but for a niche case I don't exactly want to cache rotation... + if (session.isLoggingIn() && !packet.getMotion().equals(Vector2f.ZERO)) { SetTitlePacket titlePacket = new SetTitlePacket(); titlePacket.setType(SetTitlePacket.Type.ACTIONBAR); titlePacket.setText(GeyserLocale.getPlayerLocaleString("geyser.auth.login.wait", session.locale())); diff --git a/core/src/main/java/org/geysermc/geyser/network/netty/LocalSession.java b/core/src/main/java/org/geysermc/geyser/network/netty/LocalSession.java index 958e88288..739c1c25e 100644 --- a/core/src/main/java/org/geysermc/geyser/network/netty/LocalSession.java +++ b/core/src/main/java/org/geysermc/geyser/network/netty/LocalSession.java @@ -27,15 +27,30 @@ package org.geysermc.geyser.network.netty; import io.netty.bootstrap.Bootstrap; import io.netty.buffer.ByteBufAllocator; -import io.netty.channel.*; +import io.netty.channel.ChannelHandlerContext; +import io.netty.channel.ChannelInboundHandlerAdapter; +import io.netty.channel.ChannelInitializer; +import io.netty.channel.ChannelOption; +import io.netty.channel.ChannelPipeline; +import io.netty.channel.DefaultEventLoopGroup; import io.netty.channel.unix.PreferredDirectByteBufAllocator; -import io.netty.handler.codec.haproxy.*; +import io.netty.handler.codec.haproxy.HAProxyCommand; +import io.netty.handler.codec.haproxy.HAProxyMessage; +import io.netty.handler.codec.haproxy.HAProxyMessageEncoder; +import io.netty.handler.codec.haproxy.HAProxyProtocolVersion; +import io.netty.handler.codec.haproxy.HAProxyProxiedProtocol; +import io.netty.handler.timeout.ReadTimeoutHandler; +import io.netty.handler.timeout.WriteTimeoutHandler; import io.netty.util.concurrent.DefaultThreadFactory; import org.checkerframework.checker.nullness.qual.NonNull; import org.geysermc.mcprotocollib.network.BuiltinFlags; import org.geysermc.mcprotocollib.network.codec.PacketCodecHelper; import org.geysermc.mcprotocollib.network.packet.PacketProtocol; +import org.geysermc.mcprotocollib.network.tcp.FlushHandler; +import org.geysermc.mcprotocollib.network.tcp.TcpFlowControlHandler; import org.geysermc.mcprotocollib.network.tcp.TcpPacketCodec; +import org.geysermc.mcprotocollib.network.tcp.TcpPacketCompression; +import org.geysermc.mcprotocollib.network.tcp.TcpPacketEncryptor; import org.geysermc.mcprotocollib.network.tcp.TcpPacketSizer; import org.geysermc.mcprotocollib.network.tcp.TcpSession; import org.geysermc.mcprotocollib.protocol.codec.MinecraftCodecHelper; @@ -43,6 +58,7 @@ import org.geysermc.mcprotocollib.protocol.codec.MinecraftCodecHelper; import java.net.Inet4Address; import java.net.InetSocketAddress; import java.net.SocketAddress; +import java.util.concurrent.CompletableFuture; import java.util.concurrent.TimeUnit; /** @@ -72,44 +88,53 @@ public final class LocalSession extends TcpSession { if (DEFAULT_EVENT_LOOP_GROUP == null) { DEFAULT_EVENT_LOOP_GROUP = new DefaultEventLoopGroup(new DefaultThreadFactory(this.getClass(), true)); Runtime.getRuntime().addShutdownHook(new Thread( - () -> DEFAULT_EVENT_LOOP_GROUP.shutdownGracefully(100, 500, TimeUnit.MILLISECONDS))); + () -> DEFAULT_EVENT_LOOP_GROUP.shutdownGracefully(100, 500, TimeUnit.MILLISECONDS))); } - try { - final Bootstrap bootstrap = new Bootstrap(); - bootstrap.channel(LocalChannelWithRemoteAddress.class); - bootstrap.handler(new ChannelInitializer() { - @Override - public void initChannel(@NonNull LocalChannelWithRemoteAddress channel) { - channel.spoofedRemoteAddress(new InetSocketAddress(clientIp, 0)); - PacketProtocol protocol = getPacketProtocol(); - protocol.newClientSession(LocalSession.this, transferring); + final Bootstrap bootstrap = new Bootstrap(); + bootstrap.channel(LocalChannelWithRemoteAddress.class); + bootstrap.handler(new ChannelInitializer() { + @Override + public void initChannel(@NonNull LocalChannelWithRemoteAddress channel) { + channel.spoofedRemoteAddress(new InetSocketAddress(clientIp, 0)); + PacketProtocol protocol = getPacketProtocol(); + protocol.newClientSession(LocalSession.this, transferring); - refreshReadTimeoutHandler(channel); - refreshWriteTimeoutHandler(channel); + ChannelPipeline pipeline = channel.pipeline(); - ChannelPipeline pipeline = channel.pipeline(); - pipeline.addLast("sizer", new TcpPacketSizer(LocalSession.this, protocol.getPacketHeader().getLengthSize())); - pipeline.addLast("codec", new TcpPacketCodec(LocalSession.this, true)); - pipeline.addLast("manager", LocalSession.this); + addHAProxySupport(pipeline); - addHAProxySupport(pipeline); - } - }).group(DEFAULT_EVENT_LOOP_GROUP).option(ChannelOption.CONNECT_TIMEOUT_MILLIS, getConnectTimeout() * 1000); + pipeline.addLast("read-timeout", new ReadTimeoutHandler(getFlag(BuiltinFlags.READ_TIMEOUT, 30))); + pipeline.addLast("write-timeout", new WriteTimeoutHandler(getFlag(BuiltinFlags.WRITE_TIMEOUT, 0))); - if (PREFERRED_DIRECT_BYTE_BUF_ALLOCATOR != null) { - bootstrap.option(ChannelOption.ALLOCATOR, PREFERRED_DIRECT_BYTE_BUF_ALLOCATOR); + pipeline.addLast("encryption", new TcpPacketEncryptor()); + pipeline.addLast("sizer", new TcpPacketSizer(protocol.getPacketHeader(), getCodecHelper())); + pipeline.addLast("compression", new TcpPacketCompression(getCodecHelper())); + + pipeline.addLast("flow-control", new TcpFlowControlHandler()); + pipeline.addLast("codec", new TcpPacketCodec(LocalSession.this, true)); + pipeline.addLast("flush-handler", new FlushHandler()); + pipeline.addLast("manager", LocalSession.this); + } + }).group(DEFAULT_EVENT_LOOP_GROUP).option(ChannelOption.CONNECT_TIMEOUT_MILLIS, getFlag(BuiltinFlags.CLIENT_CONNECT_TIMEOUT, 30) * 1000); + + if (PREFERRED_DIRECT_BYTE_BUF_ALLOCATOR != null) { + bootstrap.option(ChannelOption.ALLOCATOR, PREFERRED_DIRECT_BYTE_BUF_ALLOCATOR); + } + + bootstrap.remoteAddress(targetAddress); + + CompletableFuture handleFuture = new CompletableFuture<>(); + bootstrap.connect().addListener((futureListener) -> { + if (!futureListener.isSuccess()) { + exceptionCaught(null, futureListener.cause()); } - bootstrap.remoteAddress(targetAddress); + handleFuture.complete(null); + }); - bootstrap.connect().addListener((future) -> { - if (!future.isSuccess()) { - exceptionCaught(null, future.cause()); - } - }); - } catch (Throwable t) { - exceptionCaught(null, t); + if (wait) { + handleFuture.join(); } } @@ -121,7 +146,7 @@ public final class LocalSession extends TcpSession { // TODO duplicate code private void addHAProxySupport(ChannelPipeline pipeline) { InetSocketAddress clientAddress = getFlag(BuiltinFlags.CLIENT_PROXIED_ADDRESS); - if (getFlag(BuiltinFlags.ENABLE_CLIENT_PROXY_PROTOCOL, false) && clientAddress != null) { + if (clientAddress != null) { pipeline.addFirst("proxy-protocol-packet-sender", new ChannelInboundHandlerAdapter() { @Override public void channelActive(@NonNull ChannelHandlerContext ctx) throws Exception { @@ -133,9 +158,9 @@ public final class LocalSession extends TcpSession { remoteAddress = new InetSocketAddress(host, port); } ctx.channel().writeAndFlush(new HAProxyMessage( - HAProxyProtocolVersion.V2, HAProxyCommand.PROXY, proxiedProtocol, - clientAddress.getAddress().getHostAddress(), remoteAddress.getAddress().getHostAddress(), - clientAddress.getPort(), remoteAddress.getPort() + HAProxyProtocolVersion.V2, HAProxyCommand.PROXY, proxiedProtocol, + clientAddress.getAddress().getHostAddress(), remoteAddress.getAddress().getHostAddress(), + clientAddress.getPort(), remoteAddress.getPort() )); ctx.pipeline().remove(this); ctx.pipeline().remove("proxy-protocol-encoder"); @@ -144,7 +169,7 @@ public final class LocalSession extends TcpSession { }); pipeline.addFirst("proxy-protocol-encoder", HAProxyMessageEncoder.INSTANCE); } - } + } /** * Should only be called when direct ByteBufs should be preferred. At this moment, this should only be called on BungeeCord. diff --git a/core/src/main/java/org/geysermc/geyser/registry/AbstractMappedDeferredRegistry.java b/core/src/main/java/org/geysermc/geyser/registry/AbstractMappedDeferredRegistry.java new file mode 100644 index 000000000..2506bb873 --- /dev/null +++ b/core/src/main/java/org/geysermc/geyser/registry/AbstractMappedDeferredRegistry.java @@ -0,0 +1,103 @@ +/* + * Copyright (c) 2024 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.registry; + +import org.checkerframework.checker.nullness.qual.Nullable; +import org.geysermc.geyser.registry.loader.RegistryLoader; + +import java.util.Map; +import java.util.Optional; +import java.util.function.Function; +import java.util.function.Supplier; + +public abstract class AbstractMappedDeferredRegistry, R extends AbstractMappedRegistry> extends DeferredRegistry { + protected AbstractMappedDeferredRegistry(Function, R> registryLoader, RegistryLoader deferredLoader) { + super(registryLoader, deferredLoader); + } + + protected AbstractMappedDeferredRegistry(Function, R> registryLoader, Supplier> deferredLoader) { + super(registryLoader, deferredLoader); + } + + protected AbstractMappedDeferredRegistry(I input, RegistryInitializer registryInitializer, RegistryLoader deferredLoader) { + super(input, registryInitializer, deferredLoader); + } + + protected AbstractMappedDeferredRegistry(I input, RegistryInitializer registryInitializer, Supplier> deferredLoader) { + super(input, registryInitializer, deferredLoader); + } + + /** + * Returns the value registered by the given key. + * + * @param key the key + * @return the value registered by the given key. + */ + public @Nullable V get(K key) { + return get().get(key); + } + + /** + * Returns and maps the value by the given key if present. + * + * @param key the key + * @param mapper the mapper + * @param the type + * @return the mapped value from the given key if present + */ + public Optional map(K key, Function mapper) { + V value = this.get(key); + if (value == null) { + return Optional.empty(); + } else { + return Optional.ofNullable(mapper.apply(value)); + } + } + + /** + * Returns the value registered by the given key or the default value + * specified if null. + * + * @param key the key + * @param defaultValue the default value + * @return the value registered by the given key or the default value + * specified if null. + */ + public V getOrDefault(K key, V defaultValue) { + return get().getOrDefault(key, defaultValue); + } + + /** + * Registers a new value into this registry with the given key. + * + * @param key the key + * @param value the value + * @return a new value into this registry with the given key. + */ + public V register(K key, V value) { + return get().put(key, value); + } +} diff --git a/core/src/main/java/org/geysermc/geyser/registry/BlockRegistries.java b/core/src/main/java/org/geysermc/geyser/registry/BlockRegistries.java index 2f15094ef..521d67542 100644 --- a/core/src/main/java/org/geysermc/geyser/registry/BlockRegistries.java +++ b/core/src/main/java/org/geysermc/geyser/registry/BlockRegistries.java @@ -69,7 +69,7 @@ public class BlockRegistries { /** * A mapped registry containing which holds block IDs to its {@link BlockCollision}. */ - public static final ListRegistry COLLISIONS; + public static final ListDeferredRegistry COLLISIONS = ListDeferredRegistry.create(Pair.of("org.geysermc.geyser.translator.collision.CollisionRemapper", "mappings/collisions.nbt"), CollisionRegistryLoader::new); /** * A registry which stores Java IDs to {@link Block}, containing miscellaneous information about @@ -130,22 +130,17 @@ public class BlockRegistries { */ public static final SimpleMappedRegistry CUSTOM_SKULLS = SimpleMappedRegistry.create(RegistryLoaders.empty(Object2ObjectOpenHashMap::new)); - static { + public static void populate() { Blocks.VAULT.javaId(); // FIXME CustomSkullRegistryPopulator.populate(); BlockRegistryPopulator.populate(BlockRegistryPopulator.Stage.PRE_INIT); CustomBlockRegistryPopulator.populate(CustomBlockRegistryPopulator.Stage.DEFINITION); CustomBlockRegistryPopulator.populate(CustomBlockRegistryPopulator.Stage.NON_VANILLA_REGISTRATION); BlockRegistryPopulator.populate(BlockRegistryPopulator.Stage.INIT_JAVA); - COLLISIONS = ListRegistry.create(Pair.of("org.geysermc.geyser.translator.collision.CollisionRemapper", "mappings/collisions.nbt"), CollisionRegistryLoader::new); + COLLISIONS.load(); CustomBlockRegistryPopulator.populate(CustomBlockRegistryPopulator.Stage.VANILLA_REGISTRATION); CustomBlockRegistryPopulator.populate(CustomBlockRegistryPopulator.Stage.CUSTOM_REGISTRATION); BlockRegistryPopulator.populate(BlockRegistryPopulator.Stage.INIT_BEDROCK); BlockRegistryPopulator.populate(BlockRegistryPopulator.Stage.POST_INIT); } - - public static void init() { - // no-op - } - -} \ No newline at end of file +} diff --git a/core/src/main/java/org/geysermc/geyser/registry/DeferredRegistry.java b/core/src/main/java/org/geysermc/geyser/registry/DeferredRegistry.java index 6a2f2ae37..f4273edea 100644 --- a/core/src/main/java/org/geysermc/geyser/registry/DeferredRegistry.java +++ b/core/src/main/java/org/geysermc/geyser/registry/DeferredRegistry.java @@ -43,32 +43,36 @@ import java.util.function.Supplier; * * @param the value being held by the registry */ -public final class DeferredRegistry implements IRegistry { - private final Registry backingRegistry; +class DeferredRegistry> implements IRegistry { + private final R backingRegistry; private final Supplier loader; private boolean loaded; - private DeferredRegistry(Function, Registry> registryLoader, RegistryLoader deferredLoader) { + protected DeferredRegistry(Function, R> registryLoader, RegistryLoader deferredLoader) { this.backingRegistry = registryLoader.apply(RegistryLoaders.uninitialized()); this.loader = () -> deferredLoader.load(null); } - private DeferredRegistry(Function, Registry> registryLoader, Supplier> deferredLoader) { + protected DeferredRegistry(Function, R> registryLoader, Supplier> deferredLoader) { this.backingRegistry = registryLoader.apply(RegistryLoaders.uninitialized()); this.loader = () -> deferredLoader.get().load(null); } - private DeferredRegistry(I input, RegistryInitializer registryInitializer, RegistryLoader deferredLoader) { + protected DeferredRegistry(I input, RegistryInitializer registryInitializer, RegistryLoader deferredLoader) { this.backingRegistry = registryInitializer.initialize(input, RegistryLoaders.uninitialized()); this.loader = () -> deferredLoader.load(input); } - private DeferredRegistry(I input, RegistryInitializer registryInitializer, Supplier> deferredLoader) { + protected DeferredRegistry(I input, RegistryInitializer registryInitializer, Supplier> deferredLoader) { this.backingRegistry = registryInitializer.initialize(input, RegistryLoaders.uninitialized()); this.loader = () -> deferredLoader.get().load(input); } + protected R backingRegistry() { + return this.backingRegistry; + } + /** * Gets the underlying value held by this registry. * @@ -113,55 +117,10 @@ public final class DeferredRegistry implements IRegistry { } /** - * Creates a new deferred registry. - * - * @param registryLoader the registry loader - * @param deferredLoader the deferred loader - * @param the input type - * @param the registry type - * @return the new deferred registry + * Whether this registry was loaded. */ - public static DeferredRegistry create(Function, Registry> registryLoader, RegistryLoader deferredLoader) { - return new DeferredRegistry<>(registryLoader, deferredLoader); - } - - /** - * Creates a new deferred registry. - * - * @param registryLoader the registry loader - * @param deferredLoader the deferred loader - * @param the input type - * @param the registry type - * @return the new deferred registry - */ - public static DeferredRegistry create(Function, Registry> registryLoader, Supplier> deferredLoader) { - return new DeferredRegistry<>(registryLoader, deferredLoader); - } - - /** - * Creates a new deferred registry. - * - * @param registryInitializer the registry initializer - * @param deferredLoader the deferred loader - * @param the input type - * @param the registry type - * @return the new deferred registry - */ - public static DeferredRegistry create(I input, RegistryInitializer registryInitializer, RegistryLoader deferredLoader) { - return new DeferredRegistry<>(input, registryInitializer, deferredLoader); - } - - /** - * Creates a new deferred registry. - * - * @param registryInitializer the registry initializer - * @param deferredLoader the deferred loader - * @param the input type - * @param the registry type - * @return the new deferred registry - */ - public static DeferredRegistry create(I input, RegistryInitializer registryInitializer, Supplier> deferredLoader) { - return new DeferredRegistry<>(input, registryInitializer, deferredLoader); + public boolean loaded() { + return this.loaded; } /** @@ -169,7 +128,7 @@ public final class DeferredRegistry implements IRegistry { * * @param the registry type */ - interface RegistryInitializer { + public interface RegistryInitializer> { /** * Initializes the registry. @@ -179,6 +138,6 @@ public final class DeferredRegistry implements IRegistry { * @param the input type * @return the initialized registry */ - Registry initialize(I input, RegistryLoader registryLoader); + R initialize(I input, RegistryLoader registryLoader); } } diff --git a/core/src/main/java/org/geysermc/geyser/registry/ListDeferredRegistry.java b/core/src/main/java/org/geysermc/geyser/registry/ListDeferredRegistry.java new file mode 100644 index 000000000..91dc74eb7 --- /dev/null +++ b/core/src/main/java/org/geysermc/geyser/registry/ListDeferredRegistry.java @@ -0,0 +1,175 @@ +/* + * Copyright (c) 2024 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.registry; + +import org.checkerframework.checker.nullness.qual.Nullable; +import org.geysermc.geyser.registry.loader.RegistryLoader; + +import java.util.List; +import java.util.function.Function; +import java.util.function.Supplier; + +public class ListDeferredRegistry extends DeferredRegistry, ListRegistry> { + protected ListDeferredRegistry(Function>, ListRegistry> registryLoader, RegistryLoader> deferredLoader) { + super(registryLoader, deferredLoader); + } + + protected ListDeferredRegistry(Function>, ListRegistry> registryLoader, Supplier>> deferredLoader) { + super(registryLoader, deferredLoader); + } + + protected ListDeferredRegistry(I input, RegistryInitializer, ListRegistry> registryInitializer, RegistryLoader> deferredLoader) { + super(input, registryInitializer, deferredLoader); + } + + protected ListDeferredRegistry(I input, RegistryInitializer, ListRegistry> registryInitializer, Supplier>> deferredLoader) { + super(input, registryInitializer, deferredLoader); + } + /** + * Returns the value registered by the given index. + * + * @param index the index + * @return the value registered by the given index. + */ + @Nullable + public V get(int index) { + return backingRegistry().get(index); + } + + /** + * Returns the value registered by the given index or the default value + * specified if null. + * + * @param index the index + * @param defaultValue the default value + * @return the value registered by the given key or the default value + * specified if null. + */ + public V getOrDefault(int index, V defaultValue) { + return backingRegistry().getOrDefault(index, defaultValue); + } + + /** + * Registers a new value into this registry with the given index. + * + * @param index the index + * @param value the value + * @return a new value into this registry with the given index. + */ + public V register(int index, V value) { + return backingRegistry().register(index, value); + } + + /** + * Registers a new value into this registry with the given index, even if this value would normally be outside + * the range of a list. + * + * @param index the index + * @param value the value + * @param defaultValue the default value to fill empty spaces in the registry with. + * @return a new value into this registry with the given index. + */ + public V registerWithAnyIndex(int index, V value, V defaultValue) { + return backingRegistry().registerWithAnyIndex(index, value, defaultValue); + } + + /** + * Mark this registry as unsuitable for new additions. The backing list will then be optimized for storage. + */ + public void freeze() { + backingRegistry().freeze(); + } + + /** + * Creates a new deferred registry. + * + * @param registryLoader the registry loader + * @param deferredLoader the deferred loader + * @param the input type + * @return the new deferred registry + */ + public static ListDeferredRegistry create(Function>, ListRegistry> registryLoader, RegistryLoader> deferredLoader) { + return new ListDeferredRegistry<>(registryLoader, deferredLoader); + } + + /** + * Creates a new deferred registry. + * + * @param registryLoader the registry loader + * @param deferredLoader the deferred loader + * @param the input type + * @return the new deferred registry + */ + public static ListDeferredRegistry create(Function>, ListRegistry> registryLoader, Supplier>> deferredLoader) { + return new ListDeferredRegistry<>(registryLoader, deferredLoader); + } + + /** + * Creates a new deferred registry. + * + * @param registryInitializer the registry initializer + * @param deferredLoader the deferred loader + * @param the input type + * @return the new deferred registry + */ + public static ListDeferredRegistry create(I input, RegistryInitializer, ListRegistry> registryInitializer, RegistryLoader> deferredLoader) { + return new ListDeferredRegistry<>(input, registryInitializer, deferredLoader); + } + + /** + * Creates a new deferred registry. + * + * @param registryInitializer the registry initializer + * @param deferredLoader the deferred loader + * @param the input type + * @return the new deferred registry + */ + public static ListDeferredRegistry create(I input, RegistryInitializer, ListRegistry> registryInitializer, Supplier>> deferredLoader) { + return new ListDeferredRegistry<>(input, registryInitializer, deferredLoader); + } + + /** + * Creates a new deferred registry. + * + * @param deferredLoader the deferred loader + * @param the input type + * @return the new deferred registry + */ + public static ListDeferredRegistry create(I input, RegistryLoader> deferredLoader) { + return create(input, ListRegistry::create, deferredLoader); + } + + /** + * Creates a new deferred registry. + * + * @param deferredLoader the deferred loader + * @param the input type + * @return the new deferred registry + */ + public static ListDeferredRegistry create(I input, Supplier>> deferredLoader) { + return create(input, ListRegistry::create, deferredLoader); + } +} diff --git a/core/src/main/java/org/geysermc/geyser/registry/ListRegistry.java b/core/src/main/java/org/geysermc/geyser/registry/ListRegistry.java index 2070d67ae..d1d3d7af0 100644 --- a/core/src/main/java/org/geysermc/geyser/registry/ListRegistry.java +++ b/core/src/main/java/org/geysermc/geyser/registry/ListRegistry.java @@ -140,6 +140,18 @@ public class ListRegistry extends Registry> { return new ListRegistry<>(null, registryLoader); } + /** + * Creates a new integer mapped registry with the given {@link RegistryLoader} and input. + * + * @param registryLoader the registry loader + * @param the input + * @param the type value + * @return a new registry with the given RegistryLoader + */ + public static ListRegistry create(I input, RegistryLoader> registryLoader) { + return new ListRegistry<>(input, registryLoader); + } + /** * Creates a new integer mapped registry with the given {@link RegistryLoader} and input. * 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 30d3c0763..b0ae0d6aa 100644 --- a/core/src/main/java/org/geysermc/geyser/registry/Registries.java +++ b/core/src/main/java/org/geysermc/geyser/registry/Registries.java @@ -27,6 +27,7 @@ package org.geysermc.geyser.registry; import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; import it.unimi.dsi.fastutil.objects.Object2IntMap; +import it.unimi.dsi.fastutil.objects.Object2ObjectMap; import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap; import org.cloudburstmc.nbt.NbtMap; import org.cloudburstmc.nbt.NbtMapBuilder; @@ -37,10 +38,18 @@ import org.geysermc.geyser.api.pack.ResourcePack; import org.geysermc.geyser.entity.EntityDefinition; import org.geysermc.geyser.inventory.recipe.GeyserRecipe; import org.geysermc.geyser.item.type.Item; -import org.geysermc.geyser.registry.loader.*; +import org.geysermc.geyser.registry.loader.BiomeIdentifierRegistryLoader; +import org.geysermc.geyser.registry.loader.BlockEntityRegistryLoader; +import org.geysermc.geyser.registry.loader.ParticleTypesRegistryLoader; +import org.geysermc.geyser.registry.loader.PotionMixRegistryLoader; +import org.geysermc.geyser.registry.loader.ProviderRegistryLoader; +import org.geysermc.geyser.registry.loader.RegistryLoaders; +import org.geysermc.geyser.registry.loader.SoundEventsRegistryLoader; +import org.geysermc.geyser.registry.loader.SoundRegistryLoader; +import org.geysermc.geyser.registry.loader.SoundTranslatorRegistryLoader; import org.geysermc.geyser.registry.populator.ItemRegistryPopulator; import org.geysermc.geyser.registry.populator.PacketRegistryPopulator; -import org.geysermc.geyser.registry.loader.RecipeRegistryLoader; +import org.geysermc.geyser.registry.populator.TagRegistryPopulator; import org.geysermc.geyser.registry.provider.ProviderSupplier; import org.geysermc.geyser.registry.type.ItemMappings; import org.geysermc.geyser.registry.type.ParticleMapping; @@ -54,14 +63,20 @@ import org.geysermc.mcprotocollib.protocol.data.game.entity.type.EntityType; import org.geysermc.mcprotocollib.protocol.data.game.level.block.BlockEntityType; import org.geysermc.mcprotocollib.protocol.data.game.level.event.LevelEvent; import org.geysermc.mcprotocollib.protocol.data.game.level.particle.ParticleType; -import org.geysermc.mcprotocollib.protocol.data.game.recipe.RecipeType; -import java.util.*; +import java.util.ArrayList; +import java.util.EnumMap; +import java.util.HashSet; +import java.util.IdentityHashMap; +import java.util.Map; +import java.util.Set; /** * Holds all the common registries in Geyser. */ public final class Registries { + private static boolean loaded = false; + /** * A registry holding all the providers. * This has to be initialized first to allow extensions to access providers during other registry events. @@ -69,9 +84,9 @@ public final class Registries { public static final SimpleMappedRegistry, ProviderSupplier> PROVIDERS = SimpleMappedRegistry.create(new IdentityHashMap<>(), ProviderRegistryLoader::new); /** - * A registry holding a CompoundTag of the known entity identifiers. + * A registry holding a NbtMap of the known entity identifiers. */ - public static final SimpleRegistry BEDROCK_ENTITY_IDENTIFIERS = SimpleRegistry.create("bedrock/entity_identifiers.dat", RegistryLoaders.NBT); + public static final SimpleDeferredRegistry BEDROCK_ENTITY_IDENTIFIERS = SimpleDeferredRegistry.create("bedrock/entity_identifiers.dat", RegistryLoaders.NBT); /** * A registry containing all the Bedrock packet translators. @@ -79,19 +94,19 @@ public final class Registries { public static final PacketTranslatorRegistry BEDROCK_PACKET_TRANSLATORS = PacketTranslatorRegistry.create(); /** - * A registry holding a CompoundTag of all the known biomes. + * A registry holding a NbtMap of all the known biomes. */ - public static final SimpleRegistry BIOMES_NBT = SimpleRegistry.create("bedrock/biome_definitions.dat", RegistryLoaders.NBT); + public static final SimpleDeferredRegistry BIOMES_NBT = SimpleDeferredRegistry.create("bedrock/biome_definitions.dat", RegistryLoaders.NBT); /** * A mapped registry which stores Java biome identifiers and their Bedrock biome identifier. */ - public static final SimpleRegistry> BIOME_IDENTIFIERS = SimpleRegistry.create("mappings/biomes.json", BiomeIdentifierRegistryLoader::new); + public static final SimpleDeferredRegistry> BIOME_IDENTIFIERS = SimpleDeferredRegistry.create("mappings/biomes.json", BiomeIdentifierRegistryLoader::new); /** * A mapped registry which stores a block entity identifier to its {@link BlockEntityTranslator}. */ - public static final SimpleMappedRegistry BLOCK_ENTITIES = SimpleMappedRegistry.create("org.geysermc.geyser.translator.level.block.entity.BlockEntity", BlockEntityRegistryLoader::new); + public static final SimpleMappedDeferredRegistry BLOCK_ENTITIES = SimpleMappedDeferredRegistry.create("org.geysermc.geyser.translator.level.block.entity.BlockEntity", BlockEntityRegistryLoader::new); /** * A map containing all entity types and their respective Geyser definitions @@ -118,6 +133,9 @@ public final class Registries { */ public static final ListRegistry JAVA_ITEMS = ListRegistry.create(RegistryLoaders.empty(ArrayList::new)); + /** + * A registry containing item identifiers. + */ public static final SimpleMappedRegistry JAVA_ITEM_IDENTIFIERS = SimpleMappedRegistry.create(RegistryLoaders.empty(Object2ObjectOpenHashMap::new)); /** @@ -130,48 +148,71 @@ public final class Registries { * A mapped registry holding the {@link ParticleType} to a corresponding {@link ParticleMapping}, containing various pieces of * data primarily for how Bedrock should handle the particle. */ - public static final SimpleMappedRegistry PARTICLES = SimpleMappedRegistry.create("mappings/particles.json", ParticleTypesRegistryLoader::new); + public static final SimpleMappedDeferredRegistry PARTICLES = SimpleMappedDeferredRegistry.create("mappings/particles.json", ParticleTypesRegistryLoader::new); /** * A registry holding all the potion mixes. */ - public static final VersionedRegistry> POTION_MIXES; + public static final VersionedDeferredRegistry> POTION_MIXES = VersionedDeferredRegistry.create(VersionedRegistry::create, PotionMixRegistryLoader::new); /** * A versioned registry holding all the recipes, with the net ID being the key, and {@link GeyserRecipe} as the value. */ - public static final SimpleMappedRegistry> RECIPES = SimpleMappedRegistry.create("mappings/recipes.nbt", RecipeRegistryLoader::new); + //public static final SimpleMappedDeferredRegistry> RECIPES = SimpleMappedDeferredRegistry.create("mappings/recipes.nbt", RecipeRegistryLoader::new); /** * A mapped registry holding {@link ResourcePack}'s with the pack uuid as keys. */ - public static final DeferredRegistry> RESOURCE_PACKS = DeferredRegistry.create(GeyserImpl.getInstance().packDirectory(), SimpleMappedRegistry::create, RegistryLoaders.RESOURCE_PACKS); + public static final SimpleMappedDeferredRegistry RESOURCE_PACKS = SimpleMappedDeferredRegistry.create(GeyserImpl.getInstance().packDirectory(), RegistryLoaders.RESOURCE_PACKS); + + /** + * A versioned registry holding most Bedrock tags, with the Java item list (sorted) being the key, and the tag name as the value. + */ + public static final VersionedRegistry> TAGS = VersionedRegistry.create(RegistryLoaders.empty(Int2ObjectOpenHashMap::new)); /** * A mapped registry holding sound identifiers to their corresponding {@link SoundMapping}. */ - public static final SimpleMappedRegistry SOUNDS = SimpleMappedRegistry.create("mappings/sounds.json", SoundRegistryLoader::new); + public static final SimpleMappedDeferredRegistry SOUNDS = SimpleMappedDeferredRegistry.create("mappings/sounds.json", SoundRegistryLoader::new); /** * A mapped registry holding {@link LevelEvent}s to their corresponding {@link LevelEventTranslator}. */ - public static final SimpleMappedRegistry SOUND_LEVEL_EVENTS = SimpleMappedRegistry.create("mappings/effects.json", SoundEventsRegistryLoader::new); + public static final SimpleMappedDeferredRegistry SOUND_LEVEL_EVENTS = SimpleMappedDeferredRegistry.create("mappings/effects.json", SoundEventsRegistryLoader::new); /** * A mapped registry holding {@link SoundTranslator}s to their corresponding {@link SoundInteractionTranslator}. */ - public static final SimpleMappedRegistry> SOUND_TRANSLATORS = SimpleMappedRegistry.create("org.geysermc.geyser.translator.sound.SoundTranslator", SoundTranslatorRegistryLoader::new); + public static final SimpleMappedDeferredRegistry> SOUND_TRANSLATORS = SimpleMappedDeferredRegistry.create("org.geysermc.geyser.translator.sound.SoundTranslator", SoundTranslatorRegistryLoader::new); - public static void init() { - // no-op + public static void load() { + if (loaded) return; + loaded = true; + + // the following registries are registries that are more complicated than initializing as an empty collection. + // They generally have in common that they either depend on loading a resource file directly or indirectly + // (by using the Items or Blocks class, which loads all the blocks) + + BEDROCK_ENTITY_IDENTIFIERS.load(); + BIOMES_NBT.load(); + BIOME_IDENTIFIERS.load(); + BLOCK_ENTITIES.load(); + PARTICLES.load(); + // load potion mixes later + //RECIPES.load(); + RESOURCE_PACKS.load(); + SOUNDS.load(); + SOUND_LEVEL_EVENTS.load(); + SOUND_TRANSLATORS.load(); } - static { + public static void populate() { PacketRegistryPopulator.populate(); ItemRegistryPopulator.populate(); + TagRegistryPopulator.populate(); - // Create registries that require other registries to load first - POTION_MIXES = VersionedRegistry.create(PotionMixRegistryLoader::new); + // potion mixes depend on other registries + POTION_MIXES.load(); // Remove unneeded client generation data from NbtMapBuilder NbtMapBuilder biomesNbt = NbtMap.builder(); diff --git a/core/src/main/java/org/geysermc/geyser/registry/Registry.java b/core/src/main/java/org/geysermc/geyser/registry/Registry.java index 8836502b3..8a82af053 100644 --- a/core/src/main/java/org/geysermc/geyser/registry/Registry.java +++ b/core/src/main/java/org/geysermc/geyser/registry/Registry.java @@ -25,9 +25,8 @@ package org.geysermc.geyser.registry; -import org.geysermc.geyser.registry.loader.RegistryLoader; - import java.util.function.Consumer; +import org.geysermc.geyser.registry.loader.RegistryLoader; /** * A wrapper around a value which is loaded based on the output from the provided @@ -111,4 +110,4 @@ public abstract class Registry implements IRegistry { public void register(Consumer consumer) { consumer.accept(this.mappings); } -} \ No newline at end of file +} diff --git a/core/src/main/java/org/geysermc/geyser/registry/SimpleDeferredRegistry.java b/core/src/main/java/org/geysermc/geyser/registry/SimpleDeferredRegistry.java new file mode 100644 index 000000000..cdbd985be --- /dev/null +++ b/core/src/main/java/org/geysermc/geyser/registry/SimpleDeferredRegistry.java @@ -0,0 +1,119 @@ +/* + * Copyright (c) 2024 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.registry; + +import org.geysermc.geyser.registry.loader.RegistryLoader; + +import java.util.function.Function; +import java.util.function.Supplier; + +public class SimpleDeferredRegistry extends DeferredRegistry> { + protected SimpleDeferredRegistry(Function, SimpleRegistry> registryLoader, RegistryLoader deferredLoader) { + super(registryLoader, deferredLoader); + } + + protected SimpleDeferredRegistry(Function, SimpleRegistry> registryLoader, Supplier> deferredLoader) { + super(registryLoader, deferredLoader); + } + + protected SimpleDeferredRegistry(I input, RegistryInitializer> registryInitializer, RegistryLoader deferredLoader) { + super(input, registryInitializer, deferredLoader); + } + + protected SimpleDeferredRegistry(I input, RegistryInitializer> registryInitializer, Supplier> deferredLoader) { + super(input, registryInitializer, deferredLoader); + } + + /** + * Creates a new deferred registry. + * + * @param registryLoader the registry loader + * @param deferredLoader the deferred loader + * @param the input type + * @return the new deferred registry + */ + public static SimpleDeferredRegistry create(Function, SimpleRegistry> registryLoader, RegistryLoader deferredLoader) { + return new SimpleDeferredRegistry<>(registryLoader, deferredLoader); + } + + /** + * Creates a new deferred registry. + * + * @param registryLoader the registry loader + * @param deferredLoader the deferred loader + * @param the input type + * @return the new deferred registry + */ + public static SimpleDeferredRegistry create(Function, SimpleRegistry> registryLoader, Supplier> deferredLoader) { + return new SimpleDeferredRegistry<>(registryLoader, deferredLoader); + } + + /** + * Creates a new deferred registry. + * + * @param registryInitializer the registry initializer + * @param deferredLoader the deferred loader + * @param the input type + * @return the new deferred registry + */ + public static SimpleDeferredRegistry create(I input, RegistryInitializer> registryInitializer, RegistryLoader deferredLoader) { + return new SimpleDeferredRegistry<>(input, registryInitializer, deferredLoader); + } + + /** + * Creates a new deferred registry. + * + * @param registryInitializer the registry initializer + * @param deferredLoader the deferred loader + * @param the input type + * @return the new deferred registry + */ + public static SimpleDeferredRegistry create(I input, RegistryInitializer> registryInitializer, Supplier> deferredLoader) { + return new SimpleDeferredRegistry<>(input, registryInitializer, deferredLoader); + } + + /** + * Creates a new deferred registry. + * + * @param deferredLoader the deferred loader + * @param the input type + * @return the new deferred registry + */ + public static SimpleDeferredRegistry create(I input, RegistryLoader deferredLoader) { + return create(input, SimpleRegistry::create, deferredLoader); + } + + /** + * Creates a new deferred registry. + * + * @param deferredLoader the deferred loader + * @param the input type + * @return the new deferred registry + */ + public static SimpleDeferredRegistry create(I input, Supplier> deferredLoader) { + return create(input, SimpleRegistry::create, deferredLoader); + } +} diff --git a/core/src/main/java/org/geysermc/geyser/registry/SimpleMappedDeferredRegistry.java b/core/src/main/java/org/geysermc/geyser/registry/SimpleMappedDeferredRegistry.java new file mode 100644 index 000000000..6a837c9e0 --- /dev/null +++ b/core/src/main/java/org/geysermc/geyser/registry/SimpleMappedDeferredRegistry.java @@ -0,0 +1,120 @@ +/* + * Copyright (c) 2024 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.registry; + +import org.geysermc.geyser.registry.loader.RegistryLoader; + +import java.util.Map; +import java.util.function.Function; +import java.util.function.Supplier; + +public class SimpleMappedDeferredRegistry extends AbstractMappedDeferredRegistry, SimpleMappedRegistry> { + protected SimpleMappedDeferredRegistry(Function>, SimpleMappedRegistry> registryLoader, RegistryLoader> deferredLoader) { + super(registryLoader, deferredLoader); + } + + protected SimpleMappedDeferredRegistry(Function>, SimpleMappedRegistry> registryLoader, Supplier>> deferredLoader) { + super(registryLoader, deferredLoader); + } + + protected SimpleMappedDeferredRegistry(I input, RegistryInitializer, SimpleMappedRegistry> registryInitializer, RegistryLoader> deferredLoader) { + super(input, registryInitializer, deferredLoader); + } + + protected SimpleMappedDeferredRegistry(I input, RegistryInitializer, SimpleMappedRegistry> registryInitializer, Supplier>> deferredLoader) { + super(input, registryInitializer, deferredLoader); + } + + /** + * Creates a new deferred registry. + * + * @param registryLoader the registry loader + * @param deferredLoader the deferred loader + * @param the input type + * @return the new deferred registry + */ + public static SimpleMappedDeferredRegistry create(Function>, SimpleMappedRegistry> registryLoader, RegistryLoader> deferredLoader) { + return new SimpleMappedDeferredRegistry<>(registryLoader, deferredLoader); + } + + /** + * Creates a new deferred registry. + * + * @param registryLoader the registry loader + * @param deferredLoader the deferred loader + * @param the input type + * @return the new deferred registry + */ + public static SimpleMappedDeferredRegistry create(Function>, SimpleMappedRegistry> registryLoader, Supplier>> deferredLoader) { + return new SimpleMappedDeferredRegistry<>(registryLoader, deferredLoader); + } + + /** + * Creates a new deferred registry. + * + * @param registryInitializer the registry initializer + * @param deferredLoader the deferred loader + * @param the input type + * @return the new deferred registry + */ + public static SimpleMappedDeferredRegistry create(I input, DeferredRegistry.RegistryInitializer, SimpleMappedRegistry> registryInitializer, RegistryLoader> deferredLoader) { + return new SimpleMappedDeferredRegistry<>(input, registryInitializer, deferredLoader); + } + + /** + * Creates a new deferred registry. + * + * @param registryInitializer the registry initializer + * @param deferredLoader the deferred loader + * @param the input type + * @return the new deferred registry + */ + public static SimpleMappedDeferredRegistry create(I input, DeferredRegistry.RegistryInitializer, SimpleMappedRegistry> registryInitializer, Supplier>> deferredLoader) { + return new SimpleMappedDeferredRegistry<>(input, registryInitializer, deferredLoader); + } + + /** + * Creates a new deferred registry. + * + * @param deferredLoader the deferred loader + * @param the input type + * @return the new deferred registry + */ + public static SimpleMappedDeferredRegistry create(I input, RegistryLoader> deferredLoader) { + return create(input, SimpleMappedRegistry::create, deferredLoader); + } + + /** + * Creates a new deferred registry. + * + * @param deferredLoader the deferred loader + * @param the input type + * @return the new deferred registry + */ + public static SimpleMappedDeferredRegistry create(I input, Supplier>> deferredLoader) { + return create(input, SimpleMappedRegistry::create, deferredLoader); + } +} diff --git a/core/src/main/java/org/geysermc/geyser/registry/VersionedDeferredRegistry.java b/core/src/main/java/org/geysermc/geyser/registry/VersionedDeferredRegistry.java new file mode 100644 index 000000000..6b2f9906b --- /dev/null +++ b/core/src/main/java/org/geysermc/geyser/registry/VersionedDeferredRegistry.java @@ -0,0 +1,114 @@ +/* + * Copyright (c) 2024 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.registry; + +import it.unimi.dsi.fastutil.ints.Int2ObjectMap; +import org.checkerframework.checker.nullness.qual.NonNull; +import org.geysermc.geyser.registry.loader.RegistryLoader; + +import java.util.function.Function; +import java.util.function.Supplier; + +public class VersionedDeferredRegistry extends AbstractMappedDeferredRegistry, VersionedRegistry> { + protected VersionedDeferredRegistry(Function>, VersionedRegistry> registryLoader, RegistryLoader> deferredLoader) { + super(registryLoader, deferredLoader); + } + + protected VersionedDeferredRegistry(Function>, VersionedRegistry> registryLoader, Supplier>> deferredLoader) { + super(registryLoader, deferredLoader); + } + + protected VersionedDeferredRegistry(I input, RegistryInitializer, VersionedRegistry> registryInitializer, RegistryLoader> deferredLoader) { + super(input, registryInitializer, deferredLoader); + } + + protected VersionedDeferredRegistry(I input, RegistryInitializer, VersionedRegistry> registryInitializer, Supplier>> deferredLoader) { + super(input, registryInitializer, deferredLoader); + } + + /** + * Gets the closest value for the specified version. Only + * returns versions higher up than the specified if one + * does not exist for the given one. Useful in the event + * that you want to get a resource which is guaranteed for + * older versions, but not on newer ones. + * + * @param version the version + * @return the closest value for the specified version + * @throws IllegalArgumentException if no values exist at or above the given version + */ + @NonNull + public V forVersion(int version) { + return backingRegistry().forVersion(version); + } + /** + * Creates a new deferred registry. + * + * @param registryLoader the registry loader + * @param deferredLoader the deferred loader + * @param the input type + * @return the new deferred registry + */ + public static VersionedDeferredRegistry create(Function>, VersionedRegistry> registryLoader, RegistryLoader> deferredLoader) { + return new VersionedDeferredRegistry<>(registryLoader, deferredLoader); + } + + /** + * Creates a new deferred registry. + * + * @param registryLoader the registry loader + * @param deferredLoader the deferred loader + * @param the input type + * @return the new deferred registry + */ + public static VersionedDeferredRegistry create(Function>, VersionedRegistry> registryLoader, Supplier>> deferredLoader) { + return new VersionedDeferredRegistry<>(registryLoader, deferredLoader); + } + + /** + * Creates a new deferred registry. + * + * @param registryInitializer the registry initializer + * @param deferredLoader the deferred loader + * @param the input type + * @return the new deferred registry + */ + public static VersionedDeferredRegistry create(I input, RegistryInitializer, VersionedRegistry> registryInitializer, RegistryLoader> deferredLoader) { + return new VersionedDeferredRegistry<>(input, registryInitializer, deferredLoader); + } + + /** + * Creates a new deferred registry. + * + * @param registryInitializer the registry initializer + * @param deferredLoader the deferred loader + * @param the input type + * @return the new deferred registry + */ + public static VersionedDeferredRegistry create(I input, RegistryInitializer, VersionedRegistry> registryInitializer, Supplier>> deferredLoader) { + return new VersionedDeferredRegistry<>(input, registryInitializer, deferredLoader); + } +} diff --git a/core/src/main/java/org/geysermc/geyser/registry/loader/PotionMixRegistryLoader.java b/core/src/main/java/org/geysermc/geyser/registry/loader/PotionMixRegistryLoader.java index eae4e2bea..613df61aa 100644 --- a/core/src/main/java/org/geysermc/geyser/registry/loader/PotionMixRegistryLoader.java +++ b/core/src/main/java/org/geysermc/geyser/registry/loader/PotionMixRegistryLoader.java @@ -125,4 +125,4 @@ public class PotionMixRegistryLoader implements RegistryLoader>> { +public abstract class RecipeRegistryLoader implements RegistryLoader>> { - @Override - public Map> load(String input) { - Map> deserializedRecipes = new Object2ObjectOpenHashMap<>(); +// @Override +// public Map> load(String input) { +// if (true) { +// return Collections.emptyMap(); +// } +// Map> deserializedRecipes = new Object2ObjectOpenHashMap<>(); +// +// List recipes; +// try (InputStream stream = GeyserImpl.getInstance().getBootstrap().getResourceOrThrow("mappings/recipes.nbt")) { +// try (NBTInputStream nbtStream = new NBTInputStream(new DataInputStream(stream))) { +// recipes = ((NbtMap) nbtStream.readTag()).getList("recipes", NbtType.COMPOUND); +// } +// } catch (Exception e) { +// throw new AssertionError(GeyserLocale.getLocaleStringLog("geyser.toolbox.fail.runtime_java"), e); +// } +// +// MinecraftCodecHelper helper = MinecraftCodec.CODEC.getHelperFactory().get(); +// for (NbtMap recipeCollection : recipes) { +// var pair = getRecipes(recipeCollection, helper); +// deserializedRecipes.put(pair.key(), pair.value()); +// } +// return deserializedRecipes; +// } - List recipes; - try (InputStream stream = GeyserImpl.getInstance().getBootstrap().getResourceOrThrow("mappings/recipes.nbt")) { - try (NBTInputStream nbtStream = new NBTInputStream(new DataInputStream(stream))) { - recipes = ((NbtMap) nbtStream.readTag()).getList("recipes", NbtType.COMPOUND); - } - } catch (Exception e) { - throw new AssertionError(GeyserLocale.getLocaleStringLog("geyser.toolbox.fail.runtime_java"), e); - } - - MinecraftCodecHelper helper = MinecraftCodec.CODEC.getHelperFactory().get(); - for (NbtMap recipeCollection : recipes) { - var pair = getRecipes(recipeCollection, helper); - deserializedRecipes.put(pair.key(), pair.value()); - } - return deserializedRecipes; - } - - private static Pair> getRecipes(NbtMap recipes, MinecraftCodecHelper helper) { - List typedRecipes = recipes.getList("recipes", NbtType.COMPOUND); - RecipeType recipeType = RecipeType.from(recipes.getInt("recipe_type", -1)); - if (recipeType == RecipeType.CRAFTING_SPECIAL_TIPPEDARROW) { - return Pair.of(recipeType, getShapedRecipes(typedRecipes, helper)); - } else { - return Pair.of(recipeType, getShapelessRecipes(typedRecipes, helper)); - } - } +// private static Pair> getRecipes(NbtMap recipes, MinecraftCodecHelper helper) { +// List typedRecipes = recipes.getList("recipes", NbtType.COMPOUND); +// RecipeType recipeType = RecipeType.from(recipes.getInt("recipe_type", -1)); +// if (recipeType == RecipeType.CRAFTING_SPECIAL_TIPPEDARROW) { +// return Pair.of(recipeType, getShapedRecipes(typedRecipes, helper)); +// } else { +// return Pair.of(recipeType, getShapelessRecipes(typedRecipes, helper)); +// } +// } private static List getShapelessRecipes(List recipes, MinecraftCodecHelper helper) { List deserializedRecipes = new ObjectArrayList<>(recipes.size()); @@ -96,9 +88,9 @@ public final class RecipeRegistryLoader implements RegistryLoader rawInputs = recipe.getList("inputs", NbtType.COMPOUND); Ingredient[] javaInputs = new Ingredient[rawInputs.size()]; for (int i = 0; i < rawInputs.size(); i++) { - javaInputs[i] = new Ingredient(new ItemStack[] {toItemStack(rawInputs.get(i), helper)}); + //javaInputs[i] = new Ingredient(new ItemStack[] {toItemStack(rawInputs.get(i), helper)}); } - deserializedRecipes.add(new GeyserShapelessRecipe(javaInputs, output)); + //deserializedRecipes.add(new GeyserShapelessRecipe(javaInputs, output)); } return deserializedRecipes; } @@ -121,10 +113,10 @@ public final class RecipeRegistryLoader implements RegistryLoader tag) + .put(ObjectIntPair.of("1_21_40", Bedrock_v748.CODEC.getProtocolVersion()), tag -> { + final String name = tag.getString("name"); + if (name.endsWith("_wood") && tag.getCompound("states").containsKey("stripped_bit")) { + NbtMapBuilder builder = tag.getCompound("states").toBuilder(); + builder.remove("stripped_bit"); + NbtMap states = builder.build(); + return tag.toBuilder().putCompound("states", states).build(); + } + return tag; + }) .build(); // We can keep this strong as nothing should be garbage collected @@ -186,11 +198,10 @@ public final class BlockRegistryPopulator { GeyserBedrockBlock[] bedrockRuntimeMap = new GeyserBedrockBlock[blockStates.size()]; for (int i = 0; i < blockStates.size(); i++) { NbtMap tag = blockStates.get(i); - if (blockStateOrderedMap.containsKey(tag)) { + GeyserBedrockBlock block = new GeyserBedrockBlock(i, tag); + if (blockStateOrderedMap.put(tag, block) != null) { throw new AssertionError("Duplicate block states in Bedrock palette: " + tag); } - GeyserBedrockBlock block = new GeyserBedrockBlock(i, tag); - blockStateOrderedMap.put(tag, block); bedrockRuntimeMap[i] = block; } @@ -264,6 +275,15 @@ public final class BlockRegistryPopulator { NbtMap originalBedrockTag = buildBedrockState(blockState, entry); NbtMap bedrockTag = stateMapper.remap(originalBedrockTag); + // FIXME TEMPORARY + if (blockState.block() instanceof SkullBlock && palette.valueInt() >= Bedrock_v748.CODEC.getProtocolVersion()) { + // The flattening must be a very interesting process. + String skullName = blockState.block().javaIdentifier().asString().replace("_wall", ""); + bedrockTag = bedrockTag.toBuilder() + .putString("name", skullName) + .build(); + } + GeyserBedrockBlock vanillaBedrockDefinition = blockStateOrderedMap.get(bedrockTag); GeyserBedrockBlock bedrockDefinition; diff --git a/core/src/main/java/org/geysermc/geyser/registry/populator/Conversion685_671.java b/core/src/main/java/org/geysermc/geyser/registry/populator/Conversion685_671.java index 0c7f540bf..c72ea64b2 100644 --- a/core/src/main/java/org/geysermc/geyser/registry/populator/Conversion685_671.java +++ b/core/src/main/java/org/geysermc/geyser/registry/populator/Conversion685_671.java @@ -46,8 +46,7 @@ public class Conversion685_671 { static GeyserMappingItem remapItem(Item item, GeyserMappingItem mapping) { mapping = Conversion712_685.remapItem(item, mapping); - - String identifer = mapping.getBedrockIdentifier(); + String identifier = mapping.getBedrockIdentifier(); if (NEW_MUSIC_DISCS.contains(item)) { return mapping.withBedrockIdentifier("minecraft:music_disc_otherside"); @@ -59,12 +58,12 @@ public class Conversion685_671 { return mapping.withBedrockIdentifier("minecraft:glass_bottle"); } - if (!NEW_BLOCKS.contains(identifer)) { + if (!NEW_BLOCKS.contains(identifier)) { return mapping; } - if (NEW_CORAL_BLOCKS.contains(identifer)) { - switch (identifer) { + if (NEW_CORAL_BLOCKS.contains(identifier)) { + switch (identifier) { case "minecraft:tube_coral_block" -> { return mapping.withBedrockIdentifier("minecraft:coral_block").withBedrockData(0); } case "minecraft:brain_coral_block" -> { return mapping.withBedrockIdentifier("minecraft:coral_block").withBedrockData(1); } case "minecraft:bubble_coral_block" -> { return mapping.withBedrockIdentifier("minecraft:coral_block").withBedrockData(2); } @@ -78,8 +77,8 @@ public class Conversion685_671 { } } - if (NEW_DOUBLE_PLANTS.contains(identifer)) { - switch (identifer) { + if (NEW_DOUBLE_PLANTS.contains(identifier)) { + switch (identifier) { case "minecraft:sunflower" -> { return mapping.withBedrockIdentifier("minecraft:double_plant").withBedrockData(0); } case "minecraft:lilac" -> { return mapping.withBedrockIdentifier("minecraft:double_plant").withBedrockData(1); } case "minecraft:tall_grass" -> { return mapping.withBedrockIdentifier("minecraft:double_plant").withBedrockData(2); } @@ -89,8 +88,8 @@ public class Conversion685_671 { } } - if (NEW_STONE_BLOCK_SLABS.contains(identifer)) { - switch (identifer) { + if (NEW_STONE_BLOCK_SLABS.contains(identifier)) { + switch (identifier) { case "minecraft:smooth_stone_slab" -> { return mapping.withBedrockIdentifier("minecraft:stone_block_slab").withBedrockData(0); } case "minecraft:sandstone_slab" -> { return mapping.withBedrockIdentifier("minecraft:stone_block_slab").withBedrockData(1); } case "minecraft:petrified_oak_slab" -> { return mapping.withBedrockIdentifier("minecraft:stone_block_slab").withBedrockData(2); } @@ -102,8 +101,8 @@ public class Conversion685_671 { } } - if (NEW_TALLGRASSES.contains(identifer)) { - switch (identifer) { + if (NEW_TALLGRASSES.contains(identifier)) { + switch (identifier) { case "minecraft:short_grass" -> { return mapping.withBedrockIdentifier("minecraft:tallgrass").withBedrockData(1); } case "minecraft:fern" -> { return mapping.withBedrockIdentifier("minecraft:tallgrass").withBedrockData(2); } } diff --git a/core/src/main/java/org/geysermc/geyser/registry/populator/Conversion712_685.java b/core/src/main/java/org/geysermc/geyser/registry/populator/Conversion712_685.java index db715e015..45963cb90 100644 --- a/core/src/main/java/org/geysermc/geyser/registry/populator/Conversion712_685.java +++ b/core/src/main/java/org/geysermc/geyser/registry/populator/Conversion712_685.java @@ -33,35 +33,34 @@ public class Conversion712_685 { static GeyserMappingItem remapItem(Item item, GeyserMappingItem mapping) { mapping = Conversion729_712.remapItem(item, mapping); + String identifier = mapping.getBedrockIdentifier(); - String identifer = mapping.getBedrockIdentifier(); - - if (!NEW_BLOCKS.contains(identifer)) { + if (!NEW_BLOCKS.contains(identifier)) { return mapping; } - if (identifer.equals("minecraft:coarse_dirt")) { + if (identifier.equals("minecraft:coarse_dirt")) { return mapping.withBedrockIdentifier("minecraft:dirt").withBedrockData(1); } - if (identifer.equals("minecraft:dandelion")) { + if (identifier.equals("minecraft:dandelion")) { return mapping.withBedrockIdentifier("minecraft:yellow_flower").withBedrockData(0); } - if (identifer.equals("minecraft:red_sand")) { + if (identifier.equals("minecraft:red_sand")) { return mapping.withBedrockIdentifier("minecraft:sand").withBedrockData(1); } - if (NEW_PRISMARINE_BLOCKS.contains(identifer)) { - switch (identifer) { + if (NEW_PRISMARINE_BLOCKS.contains(identifier)) { + switch (identifier) { case "minecraft:prismarine" -> { return mapping.withBedrockIdentifier("minecraft:prismarine").withBedrockData(0); } case "minecraft:dark_prismarine" -> { return mapping.withBedrockIdentifier("minecraft:prismarine").withBedrockData(1); } case "minecraft:prismarine_bricks" -> { return mapping.withBedrockIdentifier("minecraft:prismarine").withBedrockData(2); } } } - if (NEW_SANDSTONE_BLOCKS.contains(identifer)) { - switch (identifer) { + if (NEW_SANDSTONE_BLOCKS.contains(identifier)) { + switch (identifier) { case "minecraft:sandstone" -> { return mapping.withBedrockIdentifier("minecraft:sandstone").withBedrockData(0); } case "minecraft:chiseled_sandstone" -> { return mapping.withBedrockIdentifier("minecraft:sandstone").withBedrockData(1); } case "minecraft:cut_sandstone" -> { return mapping.withBedrockIdentifier("minecraft:sandstone").withBedrockData(2); } @@ -69,8 +68,8 @@ public class Conversion712_685 { } } - if (NEW_RED_SANDSTONE_BLOCKS.contains(identifer)) { - switch (identifer) { + if (NEW_RED_SANDSTONE_BLOCKS.contains(identifier)) { + switch (identifier) { case "minecraft:red_sandstone" -> { return mapping.withBedrockIdentifier("minecraft:red_sandstone").withBedrockData(0); } case "minecraft:chiseled_red_sandstone" -> { return mapping.withBedrockIdentifier("minecraft:red_sandstone").withBedrockData(1); } case "minecraft:cut_red_sandstone" -> { return mapping.withBedrockIdentifier("minecraft:red_sandstone").withBedrockData(2); } @@ -78,8 +77,8 @@ public class Conversion712_685 { } } - if (NEW_QUARTZ_BLOCKS.contains(identifer)) { - switch (identifer) { + if (NEW_QUARTZ_BLOCKS.contains(identifier)) { + switch (identifier) { case "minecraft:quartz_block" -> { return mapping.withBedrockIdentifier("minecraft:quartz_block").withBedrockData(0); } case "minecraft:chiseled_quartz_block" -> { return mapping.withBedrockIdentifier("minecraft:quartz_block").withBedrockData(1); } case "minecraft:quartz_pillar" -> { return mapping.withBedrockIdentifier("minecraft:quartz_block").withBedrockData(2); } @@ -87,8 +86,8 @@ public class Conversion712_685 { } } - if (NEW_STONE_BLOCK_SLABS_2.contains(identifer)) { - switch (identifer) { + if (NEW_STONE_BLOCK_SLABS_2.contains(identifier)) { + switch (identifier) { case "minecraft:red_sandstone_slab" -> { return mapping.withBedrockIdentifier("minecraft:stone_block_slab2").withBedrockData(0); } case "minecraft:purpur_slab" -> { return mapping.withBedrockIdentifier("minecraft:stone_block_slab2").withBedrockData(1); } case "minecraft:prismarine_slab" -> { return mapping.withBedrockIdentifier("minecraft:stone_block_slab2").withBedrockData(2); } @@ -100,8 +99,8 @@ public class Conversion712_685 { } } - if (NEW_STONE_BLOCK_SLABS_3.contains(identifer)) { - switch (identifer) { + if (NEW_STONE_BLOCK_SLABS_3.contains(identifier)) { + switch (identifier) { case "minecraft:end_stone_brick_slab" -> { return mapping.withBedrockIdentifier("minecraft:stone_block_slab3").withBedrockData(0); } case "minecraft:smooth_red_sandstone_slab" -> { return mapping.withBedrockIdentifier("minecraft:stone_block_slab3").withBedrockData(1); } case "minecraft:polished_andesite_slab" -> { return mapping.withBedrockIdentifier("minecraft:stone_block_slab3").withBedrockData(2); } @@ -113,8 +112,8 @@ public class Conversion712_685 { } } - if (NEW_STONE_BLOCK_SLABS_4.contains(identifer)) { - switch (identifer) { + if (NEW_STONE_BLOCK_SLABS_4.contains(identifier)) { + switch (identifier) { case "minecraft:mossy_stone_brick_slab" -> { return mapping.withBedrockIdentifier("minecraft:stone_block_slab4").withBedrockData(0); } case "minecraft:smooth_quartz_slab" -> { return mapping.withBedrockIdentifier("minecraft:stone_block_slab4").withBedrockData(1); } case "minecraft:normal_stone_slab" -> { return mapping.withBedrockIdentifier("minecraft:stone_block_slab4").withBedrockData(2); } @@ -123,8 +122,8 @@ public class Conversion712_685 { } } - if (NEW_MONSTER_EGGS.contains(identifer)) { - switch (identifer) { + if (NEW_MONSTER_EGGS.contains(identifier)) { + switch (identifier) { case "minecraft:infested_stone" -> { return mapping.withBedrockIdentifier("minecraft:monster_egg").withBedrockData(0); } case "minecraft:infested_cobblestone" -> { return mapping.withBedrockIdentifier("minecraft:monster_egg").withBedrockData(1); } case "minecraft:infested_stone_bricks" -> { return mapping.withBedrockIdentifier("minecraft:monster_egg").withBedrockData(2); } @@ -134,8 +133,8 @@ public class Conversion712_685 { } } - if (NEW_STONEBRICK_BLOCKS.contains(identifer)) { - switch (identifer) { + if (NEW_STONEBRICK_BLOCKS.contains(identifier)) { + switch (identifier) { case "minecraft:stone_bricks" -> { return mapping.withBedrockIdentifier("minecraft:stonebrick").withBedrockData(0); } case "minecraft:mossy_stone_bricks" -> { return mapping.withBedrockIdentifier("minecraft:stonebrick").withBedrockData(1); } case "minecraft:cracked_stone_bricks" -> { return mapping.withBedrockIdentifier("minecraft:stonebrick").withBedrockData(2); } @@ -143,8 +142,8 @@ public class Conversion712_685 { } } - if (NEW_ANVILS.contains(identifer)) { - switch (identifer) { + if (NEW_ANVILS.contains(identifier)) { + switch (identifier) { case "minecraft:anvil" -> { return mapping.withBedrockIdentifier("minecraft:anvil").withBedrockData(0); } case "minecraft:chipped_anvil" -> { return mapping.withBedrockIdentifier("minecraft:anvil").withBedrockData(4); } case "minecraft:damaged_anvil" -> { return mapping.withBedrockIdentifier("minecraft:anvil").withBedrockData(8); } diff --git a/core/src/main/java/org/geysermc/geyser/registry/populator/Conversion729_712.java b/core/src/main/java/org/geysermc/geyser/registry/populator/Conversion729_712.java index 5d4ebdc47..fbc2233bc 100644 --- a/core/src/main/java/org/geysermc/geyser/registry/populator/Conversion729_712.java +++ b/core/src/main/java/org/geysermc/geyser/registry/populator/Conversion729_712.java @@ -16,6 +16,7 @@ public class Conversion729_712 { private static final List NEW_BLOCKS = Stream.of(NEW_PURPUR_BLOCKS, NEW_WALL_BLOCKS, NEW_SPONGE_BLOCKS, NEW_TNT_BLOCKS, STRUCTURE_VOID).flatMap(List::stream).toList(); static GeyserMappingItem remapItem(Item item, GeyserMappingItem mapping) { + mapping = Conversion748_729.remapItem(item, mapping); String identifier = mapping.getBedrockIdentifier(); if (!NEW_BLOCKS.contains(identifier)) { diff --git a/core/src/main/java/org/geysermc/geyser/registry/populator/Conversion748_729.java b/core/src/main/java/org/geysermc/geyser/registry/populator/Conversion748_729.java new file mode 100644 index 000000000..7a2d1a0cb --- /dev/null +++ b/core/src/main/java/org/geysermc/geyser/registry/populator/Conversion748_729.java @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2024 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.registry.populator; + +import org.geysermc.geyser.item.type.Item; +import org.geysermc.geyser.registry.type.GeyserMappingItem; + +import java.util.Map; + +public class Conversion748_729 { + + private static final Map NEW_PLAYER_HEADS = Map.of("minecraft:skeleton_skull", 0, "minecraft:wither_skeleton_skull", 1, "minecraft:zombie_head", 2, "minecraft:player_head", 3, "minecraft:creeper_head", 4, "minecraft:dragon_head", 5, "minecraft:piglin_head", 6); + + static GeyserMappingItem remapItem(Item item, GeyserMappingItem mapping) { + String identifier = mapping.getBedrockIdentifier(); + + if (NEW_PLAYER_HEADS.containsKey(identifier)) { + return mapping.withBedrockIdentifier("minecraft:skull") + .withBedrockData(NEW_PLAYER_HEADS.get(identifier)); + } + + return mapping; + } + +} 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 a136defd8..f43fd214d 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 @@ -49,6 +49,7 @@ import org.cloudburstmc.protocol.bedrock.codec.v671.Bedrock_v671; import org.cloudburstmc.protocol.bedrock.codec.v685.Bedrock_v685; import org.cloudburstmc.protocol.bedrock.codec.v712.Bedrock_v712; import org.cloudburstmc.protocol.bedrock.codec.v729.Bedrock_v729; +import org.cloudburstmc.protocol.bedrock.codec.v748.Bedrock_v748; import org.cloudburstmc.protocol.bedrock.data.definitions.BlockDefinition; import org.cloudburstmc.protocol.bedrock.data.definitions.ItemDefinition; import org.cloudburstmc.protocol.bedrock.data.definitions.SimpleItemDefinition; @@ -69,6 +70,7 @@ import org.geysermc.geyser.item.Items; import org.geysermc.geyser.item.components.Rarity; import org.geysermc.geyser.item.type.BlockItem; import org.geysermc.geyser.item.type.Item; +import org.geysermc.geyser.level.block.property.Properties; import org.geysermc.geyser.registry.BlockRegistries; import org.geysermc.geyser.registry.Registries; import org.geysermc.geyser.registry.type.BlockMappings; @@ -85,6 +87,7 @@ import java.lang.reflect.Type; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; +import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Objects; @@ -96,7 +99,7 @@ import java.util.concurrent.atomic.AtomicInteger; */ public class ItemRegistryPopulator { - record PaletteVersion(String version, int protocolVersion, Map javaOnlyItems, Remapper remapper) { + record PaletteVersion(String version, int protocolVersion, Map javaOnlyItems, Remapper remapper) { public PaletteVersion(String version, int protocolVersion) { this(version, protocolVersion, Collections.emptyMap(), (item, mapping) -> mapping); @@ -110,11 +113,18 @@ public class ItemRegistryPopulator { } public static void populate() { + List bundles = List.of(Items.BUNDLE, Items.BLACK_BUNDLE, Items.BLUE_BUNDLE, Items.BROWN_BUNDLE, Items.CYAN_BUNDLE, Items.GRAY_BUNDLE, + Items.GREEN_BUNDLE, Items.LIGHT_BLUE_BUNDLE, Items.LIGHT_GRAY_BUNDLE, Items.LIME_BUNDLE, Items.MAGENTA_BUNDLE, Items.ORANGE_BUNDLE, Items.RED_BUNDLE, + Items.PINK_BUNDLE, Items.PURPLE_BUNDLE, Items.WHITE_BUNDLE, Items.YELLOW_BUNDLE); + Map pre1_21_2Items = new HashMap<>(); + bundles.forEach(bundle -> pre1_21_2Items.put(bundle, Items.SHULKER_SHELL)); + List paletteVersions = new ArrayList<>(3); - paletteVersions.add(new PaletteVersion("1_20_80", Bedrock_v671.CODEC.getProtocolVersion(), Collections.emptyMap(), Conversion685_671::remapItem)); - paletteVersions.add(new PaletteVersion("1_21_0", Bedrock_v685.CODEC.getProtocolVersion(), Collections.emptyMap(), Conversion712_685::remapItem)); - paletteVersions.add(new PaletteVersion("1_21_20", Bedrock_v712.CODEC.getProtocolVersion(), Collections.emptyMap(), Conversion729_712::remapItem)); - paletteVersions.add(new PaletteVersion("1_21_30", Bedrock_v729.CODEC.getProtocolVersion())); + paletteVersions.add(new PaletteVersion("1_20_80", Bedrock_v671.CODEC.getProtocolVersion(), pre1_21_2Items, Conversion685_671::remapItem)); + paletteVersions.add(new PaletteVersion("1_21_0", Bedrock_v685.CODEC.getProtocolVersion(), pre1_21_2Items, Conversion712_685::remapItem)); + paletteVersions.add(new PaletteVersion("1_21_20", Bedrock_v712.CODEC.getProtocolVersion(), pre1_21_2Items, Conversion729_712::remapItem)); + paletteVersions.add(new PaletteVersion("1_21_30", Bedrock_v729.CODEC.getProtocolVersion(), pre1_21_2Items, Conversion748_729::remapItem)); + paletteVersions.add(new PaletteVersion("1_21_40", Bedrock_v748.CODEC.getProtocolVersion())); GeyserBootstrap bootstrap = GeyserImpl.getInstance().getBootstrap(); @@ -227,7 +237,7 @@ public class ItemRegistryPopulator { Set javaOnlyItems = new ObjectOpenHashSet<>(); Collections.addAll(javaOnlyItems, Items.SPECTRAL_ARROW, Items.DEBUG_STICK, - Items.KNOWLEDGE_BOOK, Items.TIPPED_ARROW, Items.BUNDLE); + Items.KNOWLEDGE_BOOK, Items.TIPPED_ARROW); if (!customItemsAllowed) { javaOnlyItems.add(Items.FURNACE_MINECART); } @@ -243,9 +253,9 @@ public class ItemRegistryPopulator { throw new RuntimeException("Extra item in mappings? " + entry.getKey()); } GeyserMappingItem mappingItem; - String replacementItem = palette.javaOnlyItems().get(javaItem); + Item replacementItem = palette.javaOnlyItems().get(javaItem); if (replacementItem != null) { - mappingItem = items.get(replacementItem); // java only item, a java id fallback has been provided + mappingItem = items.get(replacementItem.javaIdentifier()); // java only item, a java id fallback has been provided } else { // check if any mapping changes need to be made on this version mappingItem = palette.remapper().remap(javaItem, entry.getValue()); @@ -501,6 +511,26 @@ public class ItemRegistryPopulator { javaItemToMapping.put(javaItem, mapping); } + // Add the light block level since it doesn't exist on java but we need it for item conversion + Int2ObjectMap lightBlocks = new Int2ObjectOpenHashMap<>(); + + for (int i = 0; i <= Properties.LEVEL.high(); i++) { + ItemDefinition lightBlock = definitions.get("minecraft:light_block_" + i); + if (lightBlock == null) { + break; + } + + ItemMapping lightBlockEntry = ItemMapping.builder() + .javaItem(Items.LIGHT) + .bedrockIdentifier("minecraft:light_block_" + i) + .bedrockDefinition(lightBlock) + .bedrockData(0) + .bedrockBlockDefinition(null) + .customItemOptions(Collections.emptyList()) + .build(); + lightBlocks.put(lightBlock.getRuntimeId(), lightBlockEntry); + } + ItemDefinition lodestoneCompass = definitions.get("minecraft:lodestone_compass"); if (lodestoneCompass == null) { throw new RuntimeException("Lodestone compass not found in item palette!"); @@ -634,6 +664,7 @@ public class ItemRegistryPopulator { .javaOnlyItems(javaOnlyItems) .buckets(buckets) .componentItemData(componentItemData) + .lightBlocks(lightBlocks) .lodestoneCompass(lodestoneEntry) .customIdMappings(customIdMappings) .customBlockItemDefinitions(customBlockItemDefinitions) diff --git a/core/src/main/java/org/geysermc/geyser/registry/populator/TagRegistryPopulator.java b/core/src/main/java/org/geysermc/geyser/registry/populator/TagRegistryPopulator.java new file mode 100644 index 000000000..24cc270c9 --- /dev/null +++ b/core/src/main/java/org/geysermc/geyser/registry/populator/TagRegistryPopulator.java @@ -0,0 +1,136 @@ +/* + * Copyright (c) 2024 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.registry.populator; + +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import com.google.gson.reflect.TypeToken; +import it.unimi.dsi.fastutil.Hash; +import it.unimi.dsi.fastutil.ints.IntArrayList; +import it.unimi.dsi.fastutil.ints.IntList; +import it.unimi.dsi.fastutil.objects.Object2ObjectOpenCustomHashMap; +import it.unimi.dsi.fastutil.objects.ObjectIntPair; +import org.cloudburstmc.protocol.bedrock.codec.v671.Bedrock_v671; +import org.cloudburstmc.protocol.bedrock.codec.v685.Bedrock_v685; +import org.cloudburstmc.protocol.bedrock.codec.v712.Bedrock_v712; +import org.cloudburstmc.protocol.bedrock.codec.v729.Bedrock_v729; +import org.cloudburstmc.protocol.bedrock.codec.v748.Bedrock_v748; +import org.geysermc.geyser.GeyserBootstrap; +import org.geysermc.geyser.GeyserImpl; +import org.geysermc.geyser.item.type.Item; +import org.geysermc.geyser.registry.Registries; +import org.geysermc.geyser.registry.type.ItemMapping; +import org.geysermc.geyser.registry.type.ItemMappings; + +import java.io.InputStream; +import java.io.InputStreamReader; +import java.lang.reflect.Type; +import java.util.Arrays; +import java.util.List; +import java.util.Map; + +public final class TagRegistryPopulator { + private static final Gson GSON = new GsonBuilder().create(); // temporary + + public static void populate() { + Hash.Strategy hashStrategy = new Hash.Strategy<>() { + // Necessary so arrays can actually be compared + @Override + public int hashCode(int[] o) { + return Arrays.hashCode(o); + } + + @Override + public boolean equals(int[] a, int[] b) { + return Arrays.equals(a, b); + } + }; + + List> paletteVersions = List.of( + ObjectIntPair.of("1_20_80", Bedrock_v671.CODEC.getProtocolVersion()), + ObjectIntPair.of("1_21_0", Bedrock_v685.CODEC.getProtocolVersion()), + ObjectIntPair.of("1_21_20", Bedrock_v712.CODEC.getProtocolVersion()), + ObjectIntPair.of("1_21_30", Bedrock_v729.CODEC.getProtocolVersion()), + ObjectIntPair.of("1_21_40", Bedrock_v748.CODEC.getProtocolVersion()) + ); + Type type = new TypeToken>>() {}.getType(); + + GeyserBootstrap bootstrap = GeyserImpl.getInstance().getBootstrap(); + + for (var palette : paletteVersions) { + ItemMappings mappings = Registries.ITEMS.forVersion(palette.rightInt()); + + Map> bedrockTags; + try (InputStream stream = bootstrap.getResourceOrThrow(String.format("bedrock/item_tags.%s.json", palette.left()))) { + bedrockTags = GSON.fromJson(new InputStreamReader(stream), type); + } catch (Exception e) { + throw new AssertionError("Unable to load Bedrock runtime item IDs", e); + } + + var javaItemsToBedrockTag = new Object2ObjectOpenCustomHashMap(hashStrategy); + + for (var entry : bedrockTags.entrySet()) { + List value = entry.getValue(); + if (value.isEmpty() || value.size() == 1) { + // For our usecase, we don't need this. Empty values are worthless; one value can just be a reference + // to the item itself, instead of the tag. + continue; + } + + // In some cases, the int list will need to be minimized + IntList javaNetworkIds = new IntArrayList(value.size()); + for (int i = 0; i < value.size(); i++) { + String bedrockIdentifier = value.get(i); + Item javaItem = Registries.JAVA_ITEM_IDENTIFIERS.get(bedrockIdentifier); + if (javaItem == null) { + // Time to search the long way around. + for (ItemMapping mapping : mappings.getItems()) { + if (mapping.getBedrockIdentifier().equals(bedrockIdentifier)) { + javaItem = mapping.getJavaItem(); + break; + } + } + } + if (javaItem == null) { + // Triggers for Bedrock-only spawn eggs. We don't care. + continue; + } + + javaNetworkIds.add(javaItem.javaId()); + } + + int[] javaNetworkIdArray = javaNetworkIds.toIntArray(); + // Sort IDs so equality checks just have to match if each is equal and not necessarily an order difference. + Arrays.sort(javaNetworkIdArray); + + javaItemsToBedrockTag.put(javaNetworkIdArray, entry.getKey()); + } + + javaItemsToBedrockTag.trim(); + Registries.TAGS.register(palette.rightInt(), javaItemsToBedrockTag); + } + } +} diff --git a/core/src/main/java/org/geysermc/geyser/registry/type/ItemMappings.java b/core/src/main/java/org/geysermc/geyser/registry/type/ItemMappings.java index 189474238..8aae05fdc 100644 --- a/core/src/main/java/org/geysermc/geyser/registry/type/ItemMappings.java +++ b/core/src/main/java/org/geysermc/geyser/registry/type/ItemMappings.java @@ -59,6 +59,7 @@ public class ItemMappings implements DefinitionRegistry { * A unique exception as this is an item in Bedrock, but not in Java. */ ItemMapping lodestoneCompass; + Int2ObjectMap lightBlocks; ItemData[] creativeItems; Int2ObjectMap itemDefinitions; @@ -136,6 +137,11 @@ public class ItemMappings implements DefinitionRegistry { return lodestoneCompass; } + ItemMapping lightBlock = lightBlocks.get(definition.getRuntimeId()); + if (lightBlock != null) { + return lightBlock; + } + boolean isBlock = data.getBlockDefinition() != null; boolean hasDamage = data.getDamage() != 0; diff --git a/core/src/main/java/org/geysermc/geyser/scoreboard/Objective.java b/core/src/main/java/org/geysermc/geyser/scoreboard/Objective.java index 6c1389ef5..f3b7f20d2 100644 --- a/core/src/main/java/org/geysermc/geyser/scoreboard/Objective.java +++ b/core/src/main/java/org/geysermc/geyser/scoreboard/Objective.java @@ -25,185 +25,100 @@ package org.geysermc.geyser.scoreboard; -import lombok.Getter; -import lombok.Setter; -import net.kyori.adventure.text.Component; -import org.checkerframework.checker.nullness.qual.Nullable; -import org.geysermc.mcprotocollib.protocol.data.game.chat.numbers.NumberFormat; -import org.geysermc.mcprotocollib.protocol.data.game.scoreboard.ScoreboardPosition; -import org.geysermc.mcprotocollib.protocol.data.game.scoreboard.TeamColor; - +import java.util.ArrayList; +import java.util.List; import java.util.Map; import java.util.Objects; import java.util.concurrent.ConcurrentHashMap; +import lombok.Getter; +import net.kyori.adventure.text.Component; +import org.geysermc.geyser.scoreboard.display.slot.DisplaySlot; +import org.geysermc.geyser.translator.text.MessageTranslator; +import org.geysermc.mcprotocollib.protocol.data.game.chat.numbers.NumberFormat; +import org.geysermc.mcprotocollib.protocol.data.game.scoreboard.ScoreType; @Getter public final class Objective { private final Scoreboard scoreboard; - private final long id; - private boolean active = true; + private final List activeSlots = new ArrayList<>(); - @Setter - private UpdateType updateType = UpdateType.ADD; + private final String objectiveName; + private final Map scores = new ConcurrentHashMap<>(); - private String objectiveName; - private ScoreboardPosition displaySlot; - private String displaySlotName; - private String displayName = "unknown"; + private String displayName; private NumberFormat numberFormat; - private int type = 0; // 0 = integer, 1 = heart + private ScoreType type; - private Map scores = new ConcurrentHashMap<>(); - - private Objective(Scoreboard scoreboard) { - this.id = scoreboard.getNextId().getAndIncrement(); - this.scoreboard = scoreboard; - } - - /** - * /!\ This method is made for temporary objectives until the real objective is received - * - * @param scoreboard the scoreboard - * @param objectiveName the name of the objective - */ public Objective(Scoreboard scoreboard, String objectiveName) { - this(scoreboard); + this.scoreboard = scoreboard; this.objectiveName = objectiveName; - this.active = false; - } - - public Objective(Scoreboard scoreboard, String objectiveName, ScoreboardPosition displaySlot, String displayName, int type) { - this(scoreboard); - this.objectiveName = objectiveName; - this.displaySlot = displaySlot; - this.displaySlotName = translateDisplaySlot(displaySlot); - this.displayName = displayName; - this.type = type; - } - - private static String translateDisplaySlot(ScoreboardPosition displaySlot) { - return switch (displaySlot) { - case BELOW_NAME -> "belowname"; - case PLAYER_LIST -> "list"; - default -> "sidebar"; - }; } public void registerScore(String id, int score, Component displayName, NumberFormat numberFormat) { - if (!scores.containsKey(id)) { - long scoreId = scoreboard.getNextId().getAndIncrement(); - Score scoreObject = new Score(scoreId, id) - .setScore(score) - .setTeam(scoreboard.getTeamFor(id)) - .setDisplayName(displayName) - .setNumberFormat(numberFormat) - .setUpdateType(UpdateType.ADD); - scores.put(id, scoreObject); + if (scores.containsKey(id)) { + return; + } + var reference = new ScoreReference(scoreboard, id, score, displayName, numberFormat); + scores.put(id, reference); + + for (var slot : activeSlots) { + slot.addScore(reference); } } public void setScore(String id, int score, Component displayName, NumberFormat numberFormat) { - Score stored = scores.get(id); + ScoreReference stored = scores.get(id); if (stored != null) { - stored.setScore(score) - .setDisplayName(displayName) - .setNumberFormat(numberFormat) - .setUpdateType(UpdateType.UPDATE); + stored.updateProperties(scoreboard, score, displayName, numberFormat); return; } registerScore(id, score, displayName, numberFormat); } public void removeScore(String id) { - Score stored = scores.get(id); + ScoreReference stored = scores.remove(id); if (stored != null) { - stored.setUpdateType(UpdateType.REMOVE); + stored.markDeleted(); } } - /** - * Used internally to remove a score from the score map - */ - public void removeScore0(String id) { - scores.remove(id); - } + public void updateProperties(Component displayNameComponent, ScoreType type, NumberFormat format) { + String displayName = MessageTranslator.convertMessageRaw(displayNameComponent, scoreboard.session().locale()); + boolean changed = !Objects.equals(this.displayName, displayName) || this.type != type; - public Objective setDisplayName(String displayName) { this.displayName = displayName; - if (updateType == UpdateType.NOTHING) { - updateType = UpdateType.UPDATE; - } - return this; - } + this.type = type; - public Objective setNumberFormat(NumberFormat numberFormat) { - if (Objects.equals(this.numberFormat, numberFormat)) { - return this; - } - - this.numberFormat = numberFormat; - if (updateType == UpdateType.NOTHING) { - updateType = UpdateType.UPDATE; - } - - // Update the number format for scores that are following this objective's number format - for (Score score : scores.values()) { - if (score.getNumberFormat() == null) { - score.setUpdateType(UpdateType.UPDATE); + if (!Objects.equals(this.numberFormat, format)) { + this.numberFormat = format; + // update the number format for scores that are following this objective's number format, + // but only if the objective itself doesn't need to be updated. + // When the objective itself has to update all scores are updated anyway + if (!changed) { + for (ScoreReference score : scores.values()) { + if (score.numberFormat() == null) { + score.markChanged(); + } + } } } - return this; - } - - public Objective setType(int type) { - this.type = type; - if (updateType == UpdateType.NOTHING) { - updateType = UpdateType.UPDATE; - } - return this; - } - - public void setActive(ScoreboardPosition displaySlot) { - if (!active) { - active = true; - this.displaySlot = displaySlot; - displaySlotName = translateDisplaySlot(displaySlot); + if (changed) { + for (DisplaySlot slot : activeSlots) { + slot.markNeedsUpdate(); + } } } - /** - * The objective will be removed on the next update - */ - public void pendingRemove() { - updateType = UpdateType.REMOVE; + public boolean hasDisplaySlot() { + return !activeSlots.isEmpty(); } - public @Nullable TeamColor getTeamColor() { - return switch (displaySlot) { - case SIDEBAR_TEAM_RED -> TeamColor.RED; - case SIDEBAR_TEAM_AQUA -> TeamColor.AQUA; - case SIDEBAR_TEAM_BLUE -> TeamColor.BLUE; - case SIDEBAR_TEAM_GOLD -> TeamColor.GOLD; - case SIDEBAR_TEAM_GRAY -> TeamColor.GRAY; - case SIDEBAR_TEAM_BLACK -> TeamColor.BLACK; - case SIDEBAR_TEAM_GREEN -> TeamColor.GREEN; - case SIDEBAR_TEAM_WHITE -> TeamColor.WHITE; - case SIDEBAR_TEAM_YELLOW -> TeamColor.YELLOW; - case SIDEBAR_TEAM_DARK_RED -> TeamColor.DARK_RED; - case SIDEBAR_TEAM_DARK_AQUA -> TeamColor.DARK_AQUA; - case SIDEBAR_TEAM_DARK_BLUE -> TeamColor.DARK_BLUE; - case SIDEBAR_TEAM_DARK_GRAY -> TeamColor.DARK_GRAY; - case SIDEBAR_TEAM_DARK_GREEN -> TeamColor.DARK_GREEN; - case SIDEBAR_TEAM_DARK_PURPLE -> TeamColor.DARK_PURPLE; - case SIDEBAR_TEAM_LIGHT_PURPLE -> TeamColor.LIGHT_PURPLE; - default -> null; - }; + public void addDisplaySlot(DisplaySlot slot) { + activeSlots.add(slot); } - public void removed() { - active = false; - updateType = UpdateType.REMOVE; - scores = null; + public void removeDisplaySlot(DisplaySlot slot) { + activeSlots.remove(slot); } } diff --git a/core/src/main/java/org/geysermc/geyser/scoreboard/Score.java b/core/src/main/java/org/geysermc/geyser/scoreboard/Score.java deleted file mode 100644 index 9a26b7f77..000000000 --- a/core/src/main/java/org/geysermc/geyser/scoreboard/Score.java +++ /dev/null @@ -1,199 +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.scoreboard; - -import org.geysermc.mcprotocollib.protocol.data.game.chat.numbers.FixedFormat; -import org.geysermc.mcprotocollib.protocol.data.game.chat.numbers.NumberFormat; -import net.kyori.adventure.text.Component; -import org.cloudburstmc.protocol.bedrock.data.ScoreInfo; -import lombok.Getter; -import lombok.experimental.Accessors; -import org.geysermc.geyser.text.ChatColor; -import org.geysermc.geyser.translator.text.MessageTranslator; - -import java.util.Objects; - -@Getter -@Accessors(chain = true) -public final class Score { - private final long id; - private final String name; - private ScoreInfo cachedInfo; - - /** - * Changes that have been made since the last cached data. - */ - private final Score.ScoreData currentData; - /** - * The data that is currently displayed to the Bedrock client. - */ - private Score.ScoreData cachedData; - - public Score(long id, String name) { - this.id = id; - this.name = name; - this.currentData = new ScoreData(); - } - - public String getDisplayName() { - String displayName = cachedData.displayName; - if (displayName != null) { - return displayName; - } - Team team = cachedData.team; - if (team != null) { - return team.getDisplayName(name); - } - return name; - } - - public int getScore() { - return currentData.getScore(); - } - - public Score setScore(int score) { - currentData.score = score; - return this; - } - - public Team getTeam() { - return currentData.team; - } - - public Score setTeam(Team team) { - if (currentData.team != null && team != null) { - if (!currentData.team.equals(team)) { - currentData.team = team; - setUpdateType(UpdateType.UPDATE); - } - return this; - } - // simplified from (this.team != null && team == null) || (this.team == null && team != null) - if (currentData.team != null || team != null) { - currentData.team = team; - setUpdateType(UpdateType.UPDATE); - } - return this; - } - - public Score setDisplayName(Component displayName) { - if (currentData.displayName != null && displayName != null) { - String convertedDisplayName = MessageTranslator.convertMessage(displayName); - if (!currentData.displayName.equals(convertedDisplayName)) { - currentData.displayName = convertedDisplayName; - setUpdateType(UpdateType.UPDATE); - } - return this; - } - // simplified from (this.displayName != null && displayName == null) || (this.displayName == null && displayName != null) - if (currentData.displayName != null || displayName != null) { - currentData.displayName = MessageTranslator.convertMessage(displayName); - setUpdateType(UpdateType.UPDATE); - } - return this; - } - - public NumberFormat getNumberFormat() { - return currentData.numberFormat; - } - - public Score setNumberFormat(NumberFormat numberFormat) { - if (!Objects.equals(currentData.numberFormat, numberFormat)) { - currentData.numberFormat = numberFormat; - setUpdateType(UpdateType.UPDATE); - } - return this; - } - - public UpdateType getUpdateType() { - return currentData.updateType; - } - - public Score setUpdateType(UpdateType updateType) { - if (updateType != UpdateType.NOTHING) { - currentData.changed = true; - } - currentData.updateType = updateType; - return this; - } - - public boolean shouldUpdate() { - return cachedData == null || currentData.changed || - (currentData.team != null && currentData.team.shouldUpdate()); - } - - public void update(Objective objective) { - if (cachedData == null) { - cachedData = new ScoreData(); - cachedData.updateType = UpdateType.ADD; - if (currentData.updateType == UpdateType.REMOVE) { - cachedData.updateType = UpdateType.REMOVE; - } - } else { - cachedData.updateType = currentData.updateType; - } - - currentData.changed = false; - cachedData.team = currentData.team; - cachedData.score = currentData.score; - cachedData.displayName = currentData.displayName; - cachedData.numberFormat = currentData.numberFormat; - - String name = this.name; - if (cachedData.displayName != null) { - name = cachedData.displayName; - } else if (cachedData.team != null) { - cachedData.team.prepareUpdate(); - name = cachedData.team.getDisplayName(name); - } - - NumberFormat numberFormat = cachedData.numberFormat; - if (numberFormat == null) { - numberFormat = objective.getNumberFormat(); - } - if (numberFormat instanceof FixedFormat fixedFormat) { - name += " " + ChatColor.RESET + MessageTranslator.convertMessage(fixedFormat.getValue()); - } - - cachedInfo = new ScoreInfo(id, objective.getObjectiveName(), cachedData.score, name); - } - - @Getter - public static final class ScoreData { - private UpdateType updateType; - private boolean changed; - - private Team team; - private int score; - - private String displayName; - private NumberFormat numberFormat; - - private ScoreData() { - updateType = UpdateType.ADD; - } - } -} diff --git a/core/src/main/java/org/geysermc/geyser/scoreboard/ScoreReference.java b/core/src/main/java/org/geysermc/geyser/scoreboard/ScoreReference.java new file mode 100644 index 000000000..c26a59899 --- /dev/null +++ b/core/src/main/java/org/geysermc/geyser/scoreboard/ScoreReference.java @@ -0,0 +1,132 @@ +/* + * 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.scoreboard; + +import java.util.Objects; +import net.kyori.adventure.text.Component; +import org.geysermc.geyser.translator.text.MessageTranslator; +import org.geysermc.mcprotocollib.protocol.data.game.chat.numbers.NumberFormat; + +public final class ScoreReference { + public static final long LAST_UPDATE_DEFAULT = -1; + private static final long LAST_UPDATE_REMOVE = -2; + + private final String name; + private final boolean hidden; + + private String displayName; + private int score; + private NumberFormat numberFormat; + + private long lastUpdate; + + public ScoreReference( + Scoreboard scoreboard, String name, int score, Component displayName, NumberFormat format) { + this.name = name; + // hidden is a sidebar exclusive feature + this.hidden = name.startsWith("#"); + + updateProperties(scoreboard, score, displayName, format); + this.lastUpdate = LAST_UPDATE_DEFAULT; + } + + public String name() { + return name; + } + + public boolean hidden() { + return hidden; + } + + public String displayName() { + return displayName; + } + + public void displayName(Component displayName, Scoreboard scoreboard) { + if (this.displayName != null && displayName != null) { + String convertedDisplayName = MessageTranslator.convertMessage(displayName, scoreboard.session().locale()); + if (!this.displayName.equals(convertedDisplayName)) { + this.displayName = convertedDisplayName; + markChanged(); + } + return; + } + // simplified from (this.displayName != null && displayName == null) || (this.displayName == null && displayName != null) + if (this.displayName != null || displayName != null) { + this.displayName = MessageTranslator.convertMessage(displayName, scoreboard.session().locale()); + markChanged(); + } + } + + public int score() { + return score; + } + + private void score(int score) { + boolean changed = this.score != score; + this.score = score; + if (changed) { + markChanged(); + } + } + + public NumberFormat numberFormat() { + return numberFormat; + } + + private void numberFormat(NumberFormat numberFormat) { + if (Objects.equals(numberFormat(), numberFormat)) { + return; + } + this.numberFormat = numberFormat; + markChanged(); + } + + public void updateProperties(Scoreboard scoreboard, int score, Component displayName, NumberFormat numberFormat) { + score(score); + displayName(displayName, scoreboard); + numberFormat(numberFormat); + } + + public long lastUpdate() { + return lastUpdate; + } + + public boolean isRemoved() { + return lastUpdate == LAST_UPDATE_REMOVE; + } + + public void markChanged() { + if (lastUpdate == LAST_UPDATE_REMOVE) { + return; + } + lastUpdate = System.currentTimeMillis(); + } + + public void markDeleted() { + lastUpdate = -1; + } +} 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 acce86f4d..3d3bfb48d 100644 --- a/core/src/main/java/org/geysermc/geyser/scoreboard/Scoreboard.java +++ b/core/src/main/java/org/geysermc/geyser/scoreboard/Scoreboard.java @@ -25,43 +25,72 @@ package org.geysermc.geyser.scoreboard; +import static org.geysermc.geyser.scoreboard.UpdateType.REMOVE; + import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap; +import java.util.ArrayList; +import java.util.Collections; +import java.util.EnumMap; +import java.util.EnumSet; +import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicLong; +import java.util.function.Function; +import java.util.stream.Collectors; import lombok.Getter; +import net.kyori.adventure.text.Component; import org.checkerframework.checker.nullness.qual.Nullable; import org.cloudburstmc.protocol.bedrock.data.ScoreInfo; import org.cloudburstmc.protocol.bedrock.data.command.CommandEnumConstraint; -import org.cloudburstmc.protocol.bedrock.packet.RemoveObjectivePacket; -import org.cloudburstmc.protocol.bedrock.packet.SetDisplayObjectivePacket; import org.cloudburstmc.protocol.bedrock.packet.SetScorePacket; import org.geysermc.geyser.GeyserImpl; import org.geysermc.geyser.GeyserLogger; import org.geysermc.geyser.entity.type.Entity; import org.geysermc.geyser.entity.type.player.PlayerEntity; +import org.geysermc.geyser.scoreboard.display.slot.BelownameDisplaySlot; +import org.geysermc.geyser.scoreboard.display.slot.DisplaySlot; +import org.geysermc.geyser.scoreboard.display.slot.PlayerlistDisplaySlot; +import org.geysermc.geyser.scoreboard.display.slot.SidebarDisplaySlot; import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.text.GeyserLocale; +import org.geysermc.mcprotocollib.protocol.data.game.scoreboard.NameTagVisibility; import org.geysermc.mcprotocollib.protocol.data.game.scoreboard.ScoreboardPosition; +import org.geysermc.mcprotocollib.protocol.data.game.scoreboard.TeamColor; import org.jetbrains.annotations.Contract; -import java.util.*; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.atomic.AtomicLong; -import java.util.function.Function; -import java.util.stream.Collectors; - -import static org.geysermc.geyser.scoreboard.UpdateType.*; - +/** + * Here follows some information about how scoreboards work in Java Edition, that is related to the workings of this + * class: + *

+ * Objectives can be divided in two states: inactive and active. + * Inactive objectives is the default state for objectives that have been created using the SetObjective packet. + * Scores can be added, updated and removed, but as long as they're inactive they aren't shown to the player. + * An objective becomes active when a SetDisplayObjective packet is received, which contains the slot that + * the objective should be displayed at. + *

+ * While Bedrock can handle showing one objective on multiple slots at the same time, we have to help Bedrock a bit + * for example by limiting the amount of sidebar scores to the amount of lines that can be shown + * (otherwise Bedrock may lag) and only showing online players in the playerlist (otherwise it's too cluttered.) + * This fact is the biggest contributor for the class being structured like it is. + */ public final class Scoreboard { private static final boolean SHOW_SCOREBOARD_LOGS = Boolean.parseBoolean(System.getProperty("Geyser.ShowScoreboardLogs", "true")); private static final boolean ADD_TEAM_SUGGESTIONS = Boolean.parseBoolean(System.getProperty("Geyser.AddTeamSuggestions", "true")); private final GeyserSession session; private final GeyserLogger logger; - @Getter private final AtomicLong nextId = new AtomicLong(0); private final Map objectives = new ConcurrentHashMap<>(); @Getter - private final Map objectiveSlots = new EnumMap<>(ScoreboardPosition.class); + private final Map objectiveSlots = Collections.synchronizedMap(new EnumMap<>(ScoreboardPosition.class)); + private final List removedSlots = Collections.synchronizedList(new ArrayList<>()); + private final Map teams = new ConcurrentHashMap<>(); // updated on multiple threads /** * Required to preserve vanilla behavior, which also uses a map. @@ -71,6 +100,7 @@ public final class Scoreboard { @Getter private final Map playerToTeam = new Object2ObjectOpenHashMap<>(); + private final AtomicBoolean updateLockActive = new AtomicBoolean(false); private int lastAddScoreCount = 0; private int lastRemoveScoreCount = 0; @@ -80,24 +110,22 @@ public final class Scoreboard { } public void removeScoreboard() { - Iterator iterator = objectives.values().iterator(); - while (iterator.hasNext()) { - Objective objective = iterator.next(); - iterator.remove(); + var copy = new HashMap<>(objectiveSlots); + objectiveSlots.clear(); - deleteObjective(objective, false); + for (DisplaySlot slot : copy.values()) { + slot.remove(); } } public @Nullable Objective registerNewObjective(String objectiveId) { Objective objective = objectives.get(objectiveId); if (objective != null) { - // we have no other choice, or we have to make a new map? - // if the objective hasn't been deleted, we have to force it - if (objective.getUpdateType() != REMOVE) { - return null; + // matches vanilla behaviour + if (SHOW_SCOREBOARD_LOGS) { + logger.warning("An objective with the same name '" + objectiveId + "' already exists! Ignoring new objective!"); } - deleteObjective(objective, true); + return null; } objective = new Objective(this, objectiveId); @@ -105,273 +133,162 @@ public final class Scoreboard { return objective; } - public void displayObjective(String objectiveId, ScoreboardPosition displaySlot) { + public void displayObjective(String objectiveId, ScoreboardPosition slot) { + if (objectiveId.isEmpty()) { + // matches vanilla behaviour + var display = objectiveSlots.get(slot); + if (display != null) { + removedSlots.add(display); + objectiveSlots.remove(slot, display); + var objective = display.objective(); + objective.removeDisplaySlot(display); + } + return; + } + Objective objective = objectives.get(objectiveId); if (objective == null) { return; } - if (!objective.isActive()) { - objective.setActive(displaySlot); - // for reactivated objectives - objective.setUpdateType(ADD); + var display = objectiveSlots.get(slot); + if (display != null && display.objective() != objective) { + removedSlots.add(display); } - Objective storedObjective = objectiveSlots.get(displaySlot); - if (storedObjective != null && storedObjective != objective) { - storedObjective.pendingRemove(); - } - objectiveSlots.put(displaySlot, objective); - - if (displaySlot == ScoreboardPosition.BELOW_NAME) { - // Display the below name score option to all players - // Of note: unlike Bedrock, if there is an objective in the below name slot, everyone has a display - for (PlayerEntity entity : session.getEntityCache().getAllPlayerEntities()) { - if (!entity.isValid()) { - // Player hasn't spawned yet - don't bother, it'll be done then - continue; - } - - entity.setBelowNameText(objective); - } - } + display = switch (DisplaySlot.slotCategory(slot)) { + case SIDEBAR -> new SidebarDisplaySlot(session, objective, slot); + case BELOW_NAME -> new BelownameDisplaySlot(session, objective); + case PLAYER_LIST -> new PlayerlistDisplaySlot(session, objective); + default -> throw new IllegalStateException("Unexpected value: " + slot); + }; + objectiveSlots.put(slot, display); + objective.addDisplaySlot(display); } - public Team registerNewTeam(String teamName, String[] players) { + public void registerNewTeam( + String teamName, + String[] players, + Component name, + Component prefix, + Component suffix, + NameTagVisibility visibility, + TeamColor color + ) { Team team = teams.get(teamName); if (team != null) { if (SHOW_SCOREBOARD_LOGS) { - logger.info(GeyserLocale.getLocaleStringLog("geyser.network.translator.team.failed_overrides", teamName)); + logger.info("Ignoring team %s for %s. It overrides without removing old team.".formatted(teamName, session.javaUsername())); } - return team; + return; } - team = new Team(this, teamName); - team.addEntities(players); + team = new Team(this, teamName, players, name, prefix, suffix, visibility, color); 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) if (ADD_TEAM_SUGGESTIONS) { - session.addCommandEnum("Geyser_Teams", team.getId()); + session.addCommandEnum("Geyser_Teams", team.id()); } - return team; } public void onUpdate() { + // if an update is already running, let it finish + if (updateLockActive.getAndSet(true)) { + return; + } + List addScores = new ArrayList<>(lastAddScoreCount); List removeScores = new ArrayList<>(lastRemoveScoreCount); - List removedObjectives = new ArrayList<>(); Team playerTeam = getTeamFor(session.getPlayerEntity().getUsername()); - Objective correctSidebar = null; + DisplaySlot correctSidebarSlot = null; - for (Objective objective : objectives.values()) { - // objective has been deleted - if (objective.getUpdateType() == REMOVE) { - removedObjectives.add(objective); + for (DisplaySlot slot : objectiveSlots.values()) { + // slot has been removed + if (slot.updateType() == REMOVE) { continue; } - // there's nothing we can do with inactive objectives - // after checking if the objective has been deleted, - // except waiting for the objective to become activated (: - if (!objective.isActive()) { - continue; - } - - if (playerTeam != null && playerTeam.getColor() == objective.getTeamColor()) { - correctSidebar = objective; + if (playerTeam != null && playerTeam.color() == slot.teamColor()) { + correctSidebarSlot = slot; } } - if (correctSidebar == null) { - correctSidebar = objectiveSlots.get(ScoreboardPosition.SIDEBAR); + if (correctSidebarSlot == null) { + correctSidebarSlot = objectiveSlots.get(ScoreboardPosition.SIDEBAR); } - for (Objective objective : removedObjectives) { + var actualRemovedSlots = new ArrayList<>(removedSlots); + for (var slot : actualRemovedSlots) { // Deletion must be handled before the active objectives are handled - otherwise if a scoreboard display is changed before the current // scoreboard is removed, the client can crash - deleteObjective(objective, true); + slot.remove(); } + removedSlots.removeAll(actualRemovedSlots); - handleObjective(objectiveSlots.get(ScoreboardPosition.PLAYER_LIST), addScores, removeScores); - handleObjective(correctSidebar, addScores, removeScores); - handleObjective(objectiveSlots.get(ScoreboardPosition.BELOW_NAME), addScores, removeScores); - - Iterator teamIterator = teams.values().iterator(); - while (teamIterator.hasNext()) { - Team current = teamIterator.next(); - - switch (current.getCachedUpdateType()) { - case ADD, UPDATE -> current.markUpdated(); - case REMOVE -> teamIterator.remove(); - } - } + handleDisplaySlot(objectiveSlots.get(ScoreboardPosition.PLAYER_LIST), addScores, removeScores); + handleDisplaySlot(correctSidebarSlot, addScores, removeScores); + handleDisplaySlot(objectiveSlots.get(ScoreboardPosition.BELOW_NAME), addScores, removeScores); if (!removeScores.isEmpty()) { - SetScorePacket setScorePacket = new SetScorePacket(); - setScorePacket.setAction(SetScorePacket.Action.REMOVE); - setScorePacket.setInfos(removeScores); - session.sendUpstreamPacket(setScorePacket); + SetScorePacket packet = new SetScorePacket(); + packet.setAction(SetScorePacket.Action.REMOVE); + packet.setInfos(removeScores); + session.sendUpstreamPacket(packet); } if (!addScores.isEmpty()) { - SetScorePacket setScorePacket = new SetScorePacket(); - setScorePacket.setAction(SetScorePacket.Action.SET); - setScorePacket.setInfos(addScores); - session.sendUpstreamPacket(setScorePacket); + SetScorePacket packet = new SetScorePacket(); + packet.setAction(SetScorePacket.Action.SET); + packet.setInfos(addScores); + session.sendUpstreamPacket(packet); } lastAddScoreCount = addScores.size(); lastRemoveScoreCount = removeScores.size(); + updateLockActive.set(false); } - private void handleObjective(Objective objective, List addScores, List removeScores) { - if (objective == null || objective.getUpdateType() == REMOVE) { - return; + private void handleDisplaySlot(DisplaySlot slot, List addScores, List removeScores) { + if (slot != null) { + slot.render(addScores, removeScores); } - - // hearts can't hold teams, so we treat them differently - if (objective.getType() == 1) { - for (Score score : objective.getScores().values()) { - boolean update = score.shouldUpdate(); - - if (update) { - score.update(objective); - } - - if (score.getUpdateType() != REMOVE && update) { - addScores.add(score.getCachedInfo()); - } - if (score.getUpdateType() != ADD && update) { - removeScores.add(score.getCachedInfo()); - } - } - return; - } - - boolean objectiveAdd = objective.getUpdateType() == ADD; - boolean objectiveUpdate = objective.getUpdateType() == UPDATE; - - for (Score score : objective.getScores().values()) { - if (score.getUpdateType() == REMOVE) { - ScoreInfo cachedInfo = score.getCachedInfo(); - // cachedInfo can be null here when ScoreboardUpdater is being used and a score is added and - // removed before a single update cycle is performed - if (cachedInfo != null) { - removeScores.add(cachedInfo); - } - // score is pending to be removed, so we can remove it from the objective - objective.removeScore0(score.getName()); - break; - } - - Team team = score.getTeam(); - - boolean add = objectiveAdd || objectiveUpdate; - - if (team != null) { - if (team.getUpdateType() == REMOVE || !team.hasEntity(score.getName())) { - score.setTeam(null); - add = true; - } - } - - if (score.shouldUpdate()) { - score.update(objective); - add = true; - } - - if (add) { - addScores.add(score.getCachedInfo()); - } - - // we need this as long as MCPE-143063 hasn't been fixed. - // the checks after 'add' are there to prevent removing scores that - // are going to be removed anyway / don't need to be removed - if (add && score.getUpdateType() != ADD && !(objectiveUpdate || objectiveAdd)) { - removeScores.add(score.getCachedInfo()); - } - - score.setUpdateType(NOTHING); - } - - if (objectiveUpdate) { - RemoveObjectivePacket removeObjectivePacket = new RemoveObjectivePacket(); - removeObjectivePacket.setObjectiveId(objective.getObjectiveName()); - session.sendUpstreamPacket(removeObjectivePacket); - } - - if (objectiveAdd || objectiveUpdate) { - SetDisplayObjectivePacket displayObjectivePacket = new SetDisplayObjectivePacket(); - displayObjectivePacket.setObjectiveId(objective.getObjectiveName()); - displayObjectivePacket.setDisplayName(objective.getDisplayName()); - displayObjectivePacket.setCriteria("dummy"); - displayObjectivePacket.setDisplaySlot(objective.getDisplaySlotName()); - displayObjectivePacket.setSortOrder(1); // 0 = ascending, 1 = descending - session.sendUpstreamPacket(displayObjectivePacket); - } - - objective.setUpdateType(NOTHING); - } - - /** - * @param remove if we should remove the objective from the objectives map. - */ - public void deleteObjective(Objective objective, boolean remove) { - if (remove) { - objectives.remove(objective.getObjectiveName()); - } - objectiveSlots.remove(objective.getDisplaySlot(), objective); - - objective.removed(); - - RemoveObjectivePacket removeObjectivePacket = new RemoveObjectivePacket(); - removeObjectivePacket.setObjectiveId(objective.getObjectiveName()); - session.sendUpstreamPacket(removeObjectivePacket); } public Objective getObjective(String objectiveName) { return objectives.get(objectiveName); } - public Collection getObjectives() { - return objectives.values(); - } - - public void unregisterObjective(String objectiveName) { - Objective objective = getObjective(objectiveName); - if (objective != null) { - objective.pendingRemove(); + public void removeObjective(Objective objective) { + objectives.remove(objective.getObjectiveName()); + for (DisplaySlot slot : objective.getActiveSlots()) { + objectiveSlots.remove(slot.position(), slot); + removedSlots.add(slot); } } - public Objective getSlot(ScoreboardPosition slot) { - return objectiveSlots.get(slot); + public void resetPlayerScores(String playerNameOrEntityUuid) { + for (Objective objective : objectives.values()) { + objective.removeScore(playerNameOrEntityUuid); + } } public Team getTeam(String teamName) { return teams.get(teamName); } - public Team getTeamFor(String entity) { - return playerToTeam.get(entity); + public Team getTeamFor(String playerNameOrEntityUuid) { + return playerToTeam.get(playerNameOrEntityUuid); } public void removeTeam(String teamName) { Team remove = teams.remove(teamName); - if (remove != null) { - remove.setUpdateType(REMOVE); - // 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()) { - // 1.19.3 Mojmap Scoreboard#removePlayerTeam(PlayerTeam) - playerToTeam.remove(name); - } - - session.removeCommandEnum("Geyser_Teams", remove.getId()); + if (remove == null) { + return; } + remove.remove(); + session.removeCommandEnum("Geyser_Teams", remove.id()); } @Contract("-> new") @@ -381,48 +298,46 @@ public final class Scoreboard { (o1, o2) -> o1, LinkedHashMap::new)); } - /** - * 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. - */ - public void updateEntityNames(Team team, boolean teamChange) { - Set names = new HashSet<>(team.getEntities()); - updateEntityNames(team, names, teamChange); + public void playerRegistered(PlayerEntity player) { + for (DisplaySlot slot : objectiveSlots.values()) { + slot.playerRegistered(player); + } } - /** - * Updates the display name of a set of entities within a given team. The team may also be null if the set is being removed - * from a team. - */ - public void updateEntityNames(@Nullable Team team, Set names, boolean teamChange) { - if (names.remove(session.getPlayerEntity().getUsername()) && teamChange) { - // If the player's team changed, then other entities' teams may modify their visibility based on team status - refreshSessionPlayerDisplays(); + public void playerRemoved(PlayerEntity player) { + for (DisplaySlot slot : objectiveSlots.values()) { + slot.playerRemoved(player); } - if (!names.isEmpty()) { - 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); - player.updateBedrockMetadata(); - if (names.isEmpty()) { - break; - } - } + } + + public void entityRegistered(Entity entity) { + var team = getTeamFor(entity.teamIdentifier()); + if (team != null) { + team.onEntitySpawn(entity); + } + } + + public void entityRemoved(Entity entity) { + var team = getTeamFor(entity.teamIdentifier()); + if (team != null) { + team.onEntityRemove(entity); + } + } + + public void setTeamFor(Team team, Set entities) { + for (DisplaySlot slot : objectiveSlots.values()) { + // only sidebar slots use teams + if (slot instanceof SidebarDisplaySlot sidebar) { + sidebar.setTeamFor(team, entities); } } } - /** - * If the team's player was refreshed, then we need to go through every entity and check... - */ - private void refreshSessionPlayerDisplays() { - for (Entity entity : session.getEntityCache().getEntities().values()) { - if (entity instanceof PlayerEntity player) { - Team playerTeam = session.getWorldCache().getScoreboard().getTeamFor(player.getUsername()); - player.updateDisplayName(playerTeam); - player.updateBedrockMetadata(); - } - } + public long nextId() { + return nextId.getAndIncrement(); + } + + public GeyserSession session() { + return session; } } diff --git a/core/src/main/java/org/geysermc/geyser/scoreboard/ScoreboardUpdater.java b/core/src/main/java/org/geysermc/geyser/scoreboard/ScoreboardUpdater.java index 72c9def87..19c5e64f0 100644 --- a/core/src/main/java/org/geysermc/geyser/scoreboard/ScoreboardUpdater.java +++ b/core/src/main/java/org/geysermc/geyser/scoreboard/ScoreboardUpdater.java @@ -173,7 +173,6 @@ public final class ScoreboardUpdater extends Thread { @Getter public static final class ScoreboardSession { private final GeyserSession session; - @SuppressWarnings("WriteOnlyObject") private final AtomicInteger pendingPacketsPerSecond = new AtomicInteger(0); private int packetsPerSecond; private long lastUpdate; 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 cdf2e247e..507523539 100644 --- a/core/src/main/java/org/geysermc/geyser/scoreboard/Team.java +++ b/core/src/main/java/org/geysermc/geyser/scoreboard/Team.java @@ -25,79 +25,88 @@ package org.geysermc.geyser.scoreboard; -import org.geysermc.mcprotocollib.protocol.data.game.scoreboard.NameTagVisibility; -import org.geysermc.mcprotocollib.protocol.data.game.scoreboard.TeamColor; import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet; -import lombok.AccessLevel; -import lombok.Getter; -import lombok.Setter; -import lombok.experimental.Accessors; -import org.checkerframework.checker.nullness.qual.NonNull; -import org.checkerframework.checker.nullness.qual.Nullable; - import java.util.HashSet; import java.util.Set; +import net.kyori.adventure.text.Component; +import org.checkerframework.checker.nullness.qual.NonNull; +import org.geysermc.geyser.entity.type.Entity; +import org.geysermc.geyser.session.GeyserSession; +import org.geysermc.geyser.text.ChatColor; +import org.geysermc.geyser.translator.text.MessageTranslator; +import org.geysermc.mcprotocollib.protocol.data.game.scoreboard.NameTagVisibility; +import org.geysermc.mcprotocollib.protocol.data.game.scoreboard.TeamColor; -@Getter -@Accessors(chain = true) public final class Team { + public static final long LAST_UPDATE_DEFAULT = -1; + private static final long LAST_UPDATE_REMOVE = -2; + private final Scoreboard scoreboard; private final String id; - @Getter(AccessLevel.PACKAGE) private final Set entities; + private final Set managedEntities; @NonNull private NameTagVisibility nameTagVisibility = NameTagVisibility.ALWAYS; - @Setter private TeamColor color; + private TeamColor color; - private final TeamData currentData; - private TeamData cachedData; + private String name; + private String prefix; + private String suffix; + private long lastUpdate; - private boolean updating; - - public Team(Scoreboard scoreboard, String id) { + public Team( + Scoreboard scoreboard, + String id, + String[] players, + Component name, + Component prefix, + Component suffix, + NameTagVisibility visibility, + TeamColor color + ) { this.scoreboard = scoreboard; this.id = id; - currentData = new TeamData(); - entities = new ObjectOpenHashSet<>(); + this.entities = new ObjectOpenHashSet<>(); + this.managedEntities = new ObjectOpenHashSet<>(); + this.lastUpdate = LAST_UPDATE_DEFAULT; + + // doesn't call entity update + updateProperties(name, prefix, suffix, visibility, color); + // calls entity update + addEntities(players); + lastUpdate = LAST_UPDATE_DEFAULT; } - public Set addEntities(String... names) { + public void addEntities(String... names) { Set added = new HashSet<>(); for (String name : names) { - if (entities.add(name)) { - added.add(name); + // go to next score if score is already present + if (!entities.add(name)) { + continue; } + added.add(name); scoreboard.getPlayerToTeam().compute(name, (player, oldTeam) -> { if (oldTeam != null) { // Remove old team from this map, and from the set of players of the old team. // Java 1.19.3 Mojmap: Scoreboard#addPlayerToTeam calls #removePlayerFromTeam oldTeam.entities.remove(player); + // also remove the managed entity if there is one + removeManagedEntity(player); } return this; }); } if (added.isEmpty()) { - return added; + return; } - // we don't have to change the updateType, - // because the scores itself need updating, not the team - for (Objective objective : scoreboard.getObjectives()) { - for (String addedEntity : added) { - Score score = objective.getScores().get(addedEntity); - if (score != null) { - score.setTeam(this); - } - } - } - - return added; + // we don't have to change our updateType, + // because the scores themselves need updating, not the team + scoreboard.setTeamFor(this, added); + addAddedEntities(added); } - /** - * @return all removed entities from this team - */ - public Set removeEntities(String... names) { + public void removeEntities(String... names) { Set removed = new HashSet<>(); for (String name : names) { if (entities.remove(name)) { @@ -105,87 +114,22 @@ public final class Team { } scoreboard.getPlayerToTeam().remove(name, this); } - return removed; + removeRemovedEntities(removed); } public boolean hasEntity(String name) { return entities.contains(name); } - public Team setName(String name) { - currentData.name = name; - return this; - } - - public Team setPrefix(String prefix) { - // replace "null" to an empty string, - // we do this here to improve the performance of Score#getDisplayName - if (prefix.length() == 4 && "null".equals(prefix)) { - currentData.prefix = ""; - return this; + public String displayName(String score) { + String chatColor = ChatColor.chatColorFor(color); + // most sidebar plugins will use the reset color, because they don't want color + // skip the unneeded double reset color in that case + if (ChatColor.RESET.equals(chatColor)) { + chatColor = ""; } - currentData.prefix = prefix; - return this; - } - - public Team setSuffix(String suffix) { - // replace "null" to an empty string, - // we do this here to improve the performance of Score#getDisplayName - if (suffix.length() == 4 && "null".equals(suffix)) { - currentData.suffix = ""; - return this; - } - currentData.suffix = suffix; - return this; - } - - public String getDisplayName(String score) { - return cachedData != null ? - cachedData.getDisplayName(score) : - currentData.getDisplayName(score); - } - - public void markUpdated() { - updating = false; - } - - public boolean shouldUpdate() { - return updating || cachedData == null || currentData.changed; - } - - public void prepareUpdate() { - if (updating) { - return; - } - updating = true; - - if (cachedData == null) { - cachedData = new TeamData(); - cachedData.updateType = currentData.updateType != UpdateType.REMOVE ? UpdateType.ADD : UpdateType.REMOVE; - } else { - cachedData.updateType = currentData.updateType; - } - - currentData.changed = false; - cachedData.name = currentData.name; - cachedData.prefix = currentData.prefix; - cachedData.suffix = currentData.suffix; - } - - public UpdateType getUpdateType() { - return currentData.updateType; - } - - public UpdateType getCachedUpdateType() { - return cachedData != null ? cachedData.updateType : currentData.updateType; - } - - public Team setUpdateType(UpdateType updateType) { - if (updateType != UpdateType.NOTHING) { - currentData.changed = true; - } - currentData.updateType = updateType; - return this; + // also add reset because setting the color does not reset the formatting, unlike Java + return chatColor + prefix + ChatColor.RESET + chatColor + score + ChatColor.RESET + chatColor + suffix; } public boolean isVisibleFor(String entity) { @@ -201,34 +145,187 @@ public final class Team { }; } - public Team setNameTagVisibility(@Nullable NameTagVisibility nameTagVisibility) { - if (nameTagVisibility != null) { - // Null check like this (and this.nameTagVisibility defaults to ALWAYS) as of Java 1.19.4 - this.nameTagVisibility = nameTagVisibility; + public void updateProperties(Component name, Component prefix, Component suffix, NameTagVisibility visibility, TeamColor color) { + // this shouldn't happen but hey! + if (lastUpdate == LAST_UPDATE_REMOVE) { + return; } - return this; + + String oldName = this.name; + String oldPrefix = this.prefix; + String oldSuffix = this.suffix; + boolean oldVisible = isVisibleFor(playerName()); + var oldColor = this.color; + + this.name = MessageTranslator.convertMessageRaw(name, session().locale()); + this.prefix = MessageTranslator.convertMessageRaw(prefix, session().locale()); + this.suffix = MessageTranslator.convertMessageRaw(suffix, session().locale()); + // matches vanilla behaviour, the visibility is not reset (to ALWAYS) if it is null. + // instead the visibility is not altered + if (visibility != null) { + this.nameTagVisibility = visibility; + } + this.color = color; + + if (lastUpdate == LAST_UPDATE_DEFAULT) { + // addEntities is called after the initial updateProperties, so no need to do any entity updates here + if (this.color != TeamColor.RESET || !this.prefix.isEmpty() || !this.suffix.isEmpty()) { + markChanged(); + } + return; + } + + if (!this.name.equals(oldName) + || !this.prefix.equals(oldPrefix) + || !this.suffix.equals(oldSuffix) + || color != oldColor) { + markChanged(); + updateEntities(); + return; + } + + if (isVisibleFor(playerName()) != oldVisible) { + // if just the visibility changed, we only have to update the entities. + // We don't have to mark it as changed + updateEntities(); + } + } + + public boolean shouldRemove() { + return lastUpdate == LAST_UPDATE_REMOVE; + } + + public void markChanged() { + if (lastUpdate == LAST_UPDATE_REMOVE) { + return; + } + lastUpdate = System.currentTimeMillis(); + } + + public void remove() { + lastUpdate = LAST_UPDATE_REMOVE; + + for (String name : entities()) { + // 1.19.3 Mojmap Scoreboard#removePlayerTeam(PlayerTeam) + scoreboard.getPlayerToTeam().remove(name); + } + + if (entities().contains(playerName())) { + refreshAllEntities(); + return; + } + for (Entity entity : managedEntities) { + entity.updateNametag(null); + entity.updateBedrockMetadata(); + } + } + + private void updateEntities() { + for (Entity entity : managedEntities) { + entity.updateNametag(this); + entity.updateBedrockMetadata(); + } + } + + public void onEntitySpawn(Entity entity) { + // I've basically ported addAddedEntities + if (entities.contains(entity.teamIdentifier())) { + managedEntities.add(entity); + // onEntitySpawn includes all entities but players, so it cannot contain self + entity.updateNametag(this); + entity.updateBedrockMetadata(); + } + } + + public void onEntityRemove(Entity entity) { + // we don't have to update anything, since the player is removed. + managedEntities.remove(entity); + } + + private void addAddedEntities(Set names) { + // can't contain self if none are added + if (names.isEmpty()) { + return; + } + boolean containsSelf = names.contains(playerName()); + + for (Entity entity : session().getEntityCache().getEntities().values()) { + if (names.contains(entity.teamIdentifier())) { + managedEntities.add(entity); + if (!containsSelf) { + entity.updateNametag(this); + entity.updateBedrockMetadata(); + } + } + } + + if (containsSelf) { + refreshAllEntities(); + } + } + + private void removeRemovedEntities(Set names) { + boolean containsSelf = names.contains(playerName()); + + var iterator = managedEntities.iterator(); + while (iterator.hasNext()) { + var entity = iterator.next(); + if (names.contains(entity.teamIdentifier())) { + iterator.remove(); + if (!containsSelf) { + entity.updateNametag(null); + entity.updateBedrockMetadata(); + } + } + } + + if (containsSelf) { + refreshAllEntities(); + } + } + + /** + * Used internally to remove a managed entity without causing an update. + * This is fine because its only used when the entity is added to another team, + * which will fire the correct nametag updates etc. + */ + private void removeManagedEntity(String name) { + managedEntities.removeIf(entity -> name.equals(entity.teamIdentifier())); + } + + private void refreshAllEntities() { + for (Entity entity : session().getEntityCache().getEntities().values()) { + entity.updateNametag(scoreboard.getTeamFor(entity.teamIdentifier())); + entity.updateBedrockMetadata(); + } + } + + private GeyserSession session() { + return scoreboard.session(); + } + + private String playerName() { + return session().getPlayerEntity().getUsername(); + } + + public String id() { + return id; + } + + public TeamColor color() { + return color; + } + + public long lastUpdate() { + return lastUpdate; + } + + public Set entities() { + return entities; } @Override public int hashCode() { return id.hashCode(); } - - @Getter - public static final class TeamData { - private UpdateType updateType; - private boolean changed; - - private String name; - private String prefix; - private String suffix; - - private TeamData() { - updateType = UpdateType.ADD; - } - - public String getDisplayName(String score) { - return prefix + score + suffix; - } - } } diff --git a/core/src/main/java/org/geysermc/geyser/scoreboard/display/score/BelownameDisplayScore.java b/core/src/main/java/org/geysermc/geyser/scoreboard/display/score/BelownameDisplayScore.java new file mode 100644 index 000000000..8e101d66a --- /dev/null +++ b/core/src/main/java/org/geysermc/geyser/scoreboard/display/score/BelownameDisplayScore.java @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2024 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.scoreboard.display.score; + +import org.geysermc.geyser.entity.type.player.PlayerEntity; +import org.geysermc.geyser.scoreboard.Objective; +import org.geysermc.geyser.scoreboard.ScoreReference; +import org.geysermc.geyser.scoreboard.display.slot.DisplaySlot; + +public class BelownameDisplayScore extends DisplayScore { + private final PlayerEntity player; + + public BelownameDisplayScore(DisplaySlot slot, long scoreId, ScoreReference reference, PlayerEntity player) { + super(slot, scoreId, reference); + this.player = player; + } + + @Override + public void update(Objective objective) {} + + public PlayerEntity player() { + return player; + } + + @Override + public void markUpdated() { + super.markUpdated(); + } + + public ScoreReference reference() { + return reference; + } +} diff --git a/core/src/main/java/org/geysermc/geyser/scoreboard/display/score/DisplayScore.java b/core/src/main/java/org/geysermc/geyser/scoreboard/display/score/DisplayScore.java new file mode 100644 index 000000000..c6d70bb96 --- /dev/null +++ b/core/src/main/java/org/geysermc/geyser/scoreboard/display/score/DisplayScore.java @@ -0,0 +1,70 @@ +/* + * Copyright (c) 2024 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.scoreboard.display.score; + +import org.geysermc.geyser.scoreboard.Objective; +import org.geysermc.geyser.scoreboard.ScoreReference; +import org.geysermc.geyser.scoreboard.display.slot.DisplaySlot; + +public abstract class DisplayScore { + protected final DisplaySlot slot; + protected final long id; + protected final ScoreReference reference; + + protected long lastTeamUpdate; + protected long lastUpdate; + + public DisplayScore(DisplaySlot slot, long scoreId, ScoreReference reference) { + this.slot = slot; + this.id = scoreId; + this.reference = reference; + } + + public boolean shouldUpdate() { + return reference.lastUpdate() != lastUpdate; + } + + public abstract void update(Objective objective); + + public String name() { + return reference.name(); + } + + public int score() { + return reference.score(); + } + + public boolean referenceRemoved() { + return reference.isRemoved(); + } + + protected void markUpdated() { + // with the last update (also for team) we rather have an old lastUpdate + // (and have to update again the next cycle) than potentially losing information + // by fetching the lastUpdate after update was performed + this.lastUpdate = reference.lastUpdate(); + } +} diff --git a/core/src/main/java/org/geysermc/geyser/scoreboard/display/score/PlayerlistDisplayScore.java b/core/src/main/java/org/geysermc/geyser/scoreboard/display/score/PlayerlistDisplayScore.java new file mode 100644 index 000000000..c4d8d91be --- /dev/null +++ b/core/src/main/java/org/geysermc/geyser/scoreboard/display/score/PlayerlistDisplayScore.java @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2024 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.scoreboard.display.score; + +import org.cloudburstmc.protocol.bedrock.data.ScoreInfo; +import org.geysermc.geyser.scoreboard.Objective; +import org.geysermc.geyser.scoreboard.ScoreReference; +import org.geysermc.geyser.scoreboard.display.slot.DisplaySlot; + +public final class PlayerlistDisplayScore extends DisplayScore { + private final long playerId; + private ScoreInfo cachedInfo; + + public PlayerlistDisplayScore(DisplaySlot slot, long scoreId, ScoreReference reference, long playerId) { + super(slot, scoreId, reference); + this.playerId = playerId; + } + + @Override + public boolean shouldUpdate() { + // for player references the player's name is shown, + // so we only have to update when the score has changed + return cachedInfo == null || cachedInfo.getScore() != reference.score(); + } + + @Override + public void update(Objective objective) { + cachedInfo = new ScoreInfo(id, slot.objectiveId(), reference.score(), ScoreInfo.ScorerType.PLAYER, playerId); + } + + public ScoreInfo cachedInfo() { + return cachedInfo; + } + + public boolean exists() { + return cachedInfo != null; + } +} diff --git a/core/src/main/java/org/geysermc/geyser/scoreboard/display/score/SidebarDisplayScore.java b/core/src/main/java/org/geysermc/geyser/scoreboard/display/score/SidebarDisplayScore.java new file mode 100644 index 000000000..42c0dbbf7 --- /dev/null +++ b/core/src/main/java/org/geysermc/geyser/scoreboard/display/score/SidebarDisplayScore.java @@ -0,0 +1,139 @@ +/* + * Copyright (c) 2024 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.scoreboard.display.score; + +import java.util.Objects; +import org.cloudburstmc.protocol.bedrock.data.ScoreInfo; +import org.geysermc.geyser.scoreboard.Objective; +import org.geysermc.geyser.scoreboard.ScoreReference; +import org.geysermc.geyser.scoreboard.Team; +import org.geysermc.geyser.scoreboard.display.slot.DisplaySlot; +import org.geysermc.geyser.text.ChatColor; +import org.geysermc.geyser.translator.text.MessageTranslator; +import org.geysermc.mcprotocollib.protocol.data.game.chat.numbers.FixedFormat; +import org.geysermc.mcprotocollib.protocol.data.game.chat.numbers.NumberFormat; + +public final class SidebarDisplayScore extends DisplayScore { + private ScoreInfo cachedInfo; + private Team team; + private String order; + private boolean onlyScoreValueChanged; + + public SidebarDisplayScore(DisplaySlot slot, long scoreId, ScoreReference reference) { + super(slot, scoreId, reference); + team(slot.objective().getScoreboard().getTeamFor(reference.name())); + } + + @Override + public boolean shouldUpdate() { + return super.shouldUpdate() || shouldTeamUpdate(); + } + + private boolean shouldTeamUpdate() { + return team != null && team.lastUpdate() != lastTeamUpdate; + } + + @Override + public void update(Objective objective) { + markUpdated(); + + String finalName = reference.name(); + String displayName = reference.displayName(); + + if (displayName != null) { + finalName = displayName; + } else if (team != null) { + this.lastTeamUpdate = team.lastUpdate(); + finalName = team.displayName(reference.name()); + } + + NumberFormat numberFormat = reference.numberFormat(); + if (numberFormat == null) { + numberFormat = objective.getNumberFormat(); + } + if (numberFormat instanceof FixedFormat fixedFormat) { + finalName += " " + ChatColor.RESET + MessageTranslator.convertMessage(fixedFormat.getValue(), objective.getScoreboard().session().locale()); + } + + if (order != null) { + finalName = order + ChatColor.RESET + finalName; + } + + if (cachedInfo != null) { + onlyScoreValueChanged = finalName.equals(cachedInfo.getName()); + } + cachedInfo = new ScoreInfo(id, slot.objectiveId(), reference.score(), finalName); + } + + public String order() { + return order; + } + + public DisplayScore order(String order) { + if (Objects.equals(this.order, order)) { + return this; + } + this.order = order; + // this guarantees an update + requestUpdate(); + return this; + } + + public Team team() { + return team; + } + + public void team(Team team) { + if (this.team != null && team != null) { + if (!this.team.equals(team)) { + this.team = team; + requestUpdate(); + } + return; + } + // simplified from (this.team != null && team == null) || (this.team == null && team != null) + if (this.team != null || team != null) { + this.team = team; + requestUpdate(); + } + } + + private void requestUpdate() { + this.lastUpdate = 0; + } + + public ScoreInfo cachedInfo() { + return cachedInfo; + } + + public boolean exists() { + return cachedInfo != null; + } + + public boolean onlyScoreValueChanged() { + return onlyScoreValueChanged; + } +} diff --git a/core/src/main/java/org/geysermc/geyser/scoreboard/display/slot/BelownameDisplaySlot.java b/core/src/main/java/org/geysermc/geyser/scoreboard/display/slot/BelownameDisplaySlot.java new file mode 100644 index 000000000..42a1e8c3f --- /dev/null +++ b/core/src/main/java/org/geysermc/geyser/scoreboard/display/slot/BelownameDisplaySlot.java @@ -0,0 +1,182 @@ +/* + * Copyright (c) 2024 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.scoreboard.display.slot; + +import it.unimi.dsi.fastutil.longs.Long2ObjectMap; +import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap; +import java.util.List; +import org.cloudburstmc.nbt.NbtMapBuilder; +import org.cloudburstmc.protocol.bedrock.data.ScoreInfo; +import org.geysermc.geyser.entity.type.player.PlayerEntity; +import org.geysermc.geyser.scoreboard.Objective; +import org.geysermc.geyser.scoreboard.ScoreReference; +import org.geysermc.geyser.scoreboard.UpdateType; +import org.geysermc.geyser.scoreboard.display.score.BelownameDisplayScore; +import org.geysermc.geyser.session.GeyserSession; +import org.geysermc.geyser.text.ChatColor; +import org.geysermc.geyser.translator.text.MessageTranslator; +import org.geysermc.mcprotocollib.protocol.codec.NbtComponentSerializer; +import org.geysermc.mcprotocollib.protocol.data.game.chat.numbers.BlankFormat; +import org.geysermc.mcprotocollib.protocol.data.game.chat.numbers.FixedFormat; +import org.geysermc.mcprotocollib.protocol.data.game.chat.numbers.NumberFormat; +import org.geysermc.mcprotocollib.protocol.data.game.chat.numbers.StyledFormat; +import org.geysermc.mcprotocollib.protocol.data.game.scoreboard.ScoreboardPosition; + +public class BelownameDisplaySlot extends DisplaySlot { + private final Long2ObjectMap displayScores = new Long2ObjectOpenHashMap<>(); + + public BelownameDisplaySlot(GeyserSession session, Objective objective) { + super(session, objective, ScoreboardPosition.BELOW_NAME); + } + + @Override + protected void render0(List addScores, List removeScores) { + // how belowname works is that if the player itself has belowname as a display slot, + // every player entity will show a score below their name. + // when the objective is added, updated or removed we thus have to update the belowname for every player + // when an individual score is updated (score or number format) we have to update the individual player + + // remove is handled in #remove() + if (updateType == UpdateType.ADD) { + for (PlayerEntity player : session.getEntityCache().getAllPlayerEntities()) { + playerRegistered(player); + } + return; + } + if (updateType == UpdateType.UPDATE) { + for (PlayerEntity player : session.getEntityCache().getAllPlayerEntities()) { + setBelowNameText(player, scoreFor(player.getUsername())); + } + updateType = UpdateType.NOTHING; + return; + } + + for (var score : displayScores.values()) { + // we don't have to worry about a score not existing, because that's handled by both + // this method when an objective is added and addScore/playerRegistered. + // we only have to update them, if they have changed + // (or delete them, if the score no longer exists) + if (!score.shouldUpdate()) { + continue; + } + + if (score.referenceRemoved()) { + clearBelowNameText(score.player()); + continue; + } + + score.markUpdated(); + setBelowNameText(score.player(), score.reference()); + } + } + + @Override + public void remove() { + updateType = UpdateType.REMOVE; + for (PlayerEntity player : session.getEntityCache().getAllPlayerEntities()) { + clearBelowNameText(player); + } + } + + @Override + public void addScore(ScoreReference reference) { + addDisplayScore(reference); + } + + @Override + public void playerRegistered(PlayerEntity player) { + var reference = scoreFor(player.getUsername()); + setBelowNameText(player, reference); + // keep track of score when the player is active + if (reference != null) { + // we already set the text, so we only have to update once the score does + addDisplayScore(player, reference).markUpdated(); + } + } + + @Override + public void playerRemoved(PlayerEntity player) { + displayScores.remove(player.getGeyserId()); + } + + private void addDisplayScore(ScoreReference reference) { + var players = session.getEntityCache().getPlayersByName(reference.name()); + for (PlayerEntity player : players) { + addDisplayScore(player, reference); + } + } + + private BelownameDisplayScore addDisplayScore(PlayerEntity player, ScoreReference reference) { + var score = new BelownameDisplayScore(this, objective.getScoreboard().nextId(), reference, player); + displayScores.put(player.getGeyserId(), score); + return score; + } + + private void setBelowNameText(PlayerEntity player, ScoreReference reference) { + player.setBelowNameText(calculateBelowNameText(reference)); + player.updateBedrockMetadata(); + } + + private void clearBelowNameText(PlayerEntity player) { + player.setBelowNameText(null); + player.updateBedrockMetadata(); + } + + private String calculateBelowNameText(ScoreReference reference) { + String numberString; + NumberFormat numberFormat = null; + // even if the player doesn't have a score, as long as belowname is on the client Java behaviour is + // to show them with a score of 0 + int score = 0; + if (reference != null) { + score = reference.score(); + numberFormat = reference.numberFormat(); + } + if (numberFormat == null) { + numberFormat = objective.getNumberFormat(); + } + + if (numberFormat instanceof BlankFormat) { + numberString = ""; + } else if (numberFormat instanceof FixedFormat fixedFormat) { + numberString = MessageTranslator.convertMessage(fixedFormat.getValue(), session.locale()); + } else if (numberFormat instanceof StyledFormat styledFormat) { + NbtMapBuilder styledAmount = styledFormat.getStyle().toBuilder(); + styledAmount.putString("text", String.valueOf(score)); + + numberString = MessageTranslator.convertJsonMessage( + NbtComponentSerializer.tagComponentToJson(styledAmount.build()).toString(), session.locale()); + } else { + numberString = String.valueOf(score); + } + + return numberString + " " + ChatColor.RESET + objective.getDisplayName(); + } + + private ScoreReference scoreFor(String username) { + return objective.getScores().get(username); + } +} diff --git a/core/src/main/java/org/geysermc/geyser/scoreboard/display/slot/DisplaySlot.java b/core/src/main/java/org/geysermc/geyser/scoreboard/display/slot/DisplaySlot.java new file mode 100644 index 000000000..bac79e23e --- /dev/null +++ b/core/src/main/java/org/geysermc/geyser/scoreboard/display/slot/DisplaySlot.java @@ -0,0 +1,162 @@ +/* + * Copyright (c) 2024 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.scoreboard.display.slot; + +import java.util.List; +import org.checkerframework.checker.nullness.qual.Nullable; +import org.cloudburstmc.protocol.bedrock.data.ScoreInfo; +import org.cloudburstmc.protocol.bedrock.packet.RemoveObjectivePacket; +import org.cloudburstmc.protocol.bedrock.packet.SetDisplayObjectivePacket; +import org.geysermc.geyser.entity.type.player.PlayerEntity; +import org.geysermc.geyser.scoreboard.Objective; +import org.geysermc.geyser.scoreboard.ScoreReference; +import org.geysermc.geyser.scoreboard.UpdateType; +import org.geysermc.geyser.session.GeyserSession; +import org.geysermc.mcprotocollib.protocol.data.game.scoreboard.ScoreboardPosition; +import org.geysermc.mcprotocollib.protocol.data.game.scoreboard.TeamColor; + +public abstract class DisplaySlot { + protected final GeyserSession session; + protected final Objective objective; + /** + * Use this instead of objective name because one objective can be shared in multiple slots, + * but each slot has its own logic and might not contain all scores + */ + protected final String objectiveId; + protected final ScoreboardPosition slot; + protected final TeamColor teamColor; + protected final String positionName; + + protected UpdateType updateType = UpdateType.ADD; + + public DisplaySlot(GeyserSession session, Objective objective, ScoreboardPosition slot) { + this.session = session; + this.objective = objective; + this.objectiveId = String.valueOf(objective.getScoreboard().nextId()); + this.slot = slot; + this.teamColor = teamColor(slot); + this.positionName = positionName(slot); + } + + public final void render(List addScores, List removeScores) { + if (updateType == UpdateType.REMOVE) { + return; + } + render0(addScores, removeScores); + } + + protected abstract void render0(List addScores, List removeScores); + + public abstract void addScore(ScoreReference reference); + + public abstract void playerRegistered(PlayerEntity player); + public abstract void playerRemoved(PlayerEntity player); + + public void remove() { + updateType = UpdateType.REMOVE; + sendRemoveObjective(); + } + + public void markNeedsUpdate() { + if (updateType == UpdateType.NOTHING) { + updateType = UpdateType.UPDATE; + } + } + + protected void sendDisplayObjective() { + SetDisplayObjectivePacket packet = new SetDisplayObjectivePacket(); + packet.setObjectiveId(objectiveId()); + packet.setDisplayName(objective.getDisplayName()); + packet.setCriteria("dummy"); + packet.setDisplaySlot(positionName); + packet.setSortOrder(1); // 0 = ascending, 1 = descending + session.sendUpstreamPacket(packet); + } + + protected void sendRemoveObjective() { + RemoveObjectivePacket packet = new RemoveObjectivePacket(); + packet.setObjectiveId(objectiveId()); + session.sendUpstreamPacket(packet); + } + + public Objective objective() { + return objective; + } + + public String objectiveId() { + return objectiveId; + } + + public ScoreboardPosition position() { + return slot; + } + + public @Nullable TeamColor teamColor() { + return teamColor; + } + + public UpdateType updateType() { + return updateType; + } + + public static ScoreboardPosition slotCategory(ScoreboardPosition slot) { + return switch (slot) { + case BELOW_NAME -> ScoreboardPosition.BELOW_NAME; + case PLAYER_LIST -> ScoreboardPosition.PLAYER_LIST; + default -> ScoreboardPosition.SIDEBAR; + }; + } + + private static String positionName(ScoreboardPosition slot) { + return switch (slot) { + case BELOW_NAME -> "belowname"; + case PLAYER_LIST -> "list"; + default -> "sidebar"; + }; + } + + private static @Nullable TeamColor teamColor(ScoreboardPosition slot) { + return switch (slot) { + case SIDEBAR_TEAM_RED -> TeamColor.RED; + case SIDEBAR_TEAM_AQUA -> TeamColor.AQUA; + case SIDEBAR_TEAM_BLUE -> TeamColor.BLUE; + case SIDEBAR_TEAM_GOLD -> TeamColor.GOLD; + case SIDEBAR_TEAM_GRAY -> TeamColor.GRAY; + case SIDEBAR_TEAM_BLACK -> TeamColor.BLACK; + case SIDEBAR_TEAM_GREEN -> TeamColor.GREEN; + case SIDEBAR_TEAM_WHITE -> TeamColor.WHITE; + case SIDEBAR_TEAM_YELLOW -> TeamColor.YELLOW; + case SIDEBAR_TEAM_DARK_RED -> TeamColor.DARK_RED; + case SIDEBAR_TEAM_DARK_AQUA -> TeamColor.DARK_AQUA; + case SIDEBAR_TEAM_DARK_BLUE -> TeamColor.DARK_BLUE; + case SIDEBAR_TEAM_DARK_GRAY -> TeamColor.DARK_GRAY; + case SIDEBAR_TEAM_DARK_GREEN -> TeamColor.DARK_GREEN; + case SIDEBAR_TEAM_DARK_PURPLE -> TeamColor.DARK_PURPLE; + case SIDEBAR_TEAM_LIGHT_PURPLE -> TeamColor.LIGHT_PURPLE; + default -> null; + }; + } +} diff --git a/core/src/main/java/org/geysermc/geyser/scoreboard/display/slot/PlayerlistDisplaySlot.java b/core/src/main/java/org/geysermc/geyser/scoreboard/display/slot/PlayerlistDisplaySlot.java new file mode 100644 index 000000000..6fd83ab8d --- /dev/null +++ b/core/src/main/java/org/geysermc/geyser/scoreboard/display/slot/PlayerlistDisplaySlot.java @@ -0,0 +1,158 @@ +/* + * Copyright (c) 2024 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.scoreboard.display.slot; + +import it.unimi.dsi.fastutil.longs.Long2ObjectMap; +import it.unimi.dsi.fastutil.longs.Long2ObjectMaps; +import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import org.cloudburstmc.protocol.bedrock.data.ScoreInfo; +import org.geysermc.geyser.entity.type.player.PlayerEntity; +import org.geysermc.geyser.scoreboard.Objective; +import org.geysermc.geyser.scoreboard.ScoreReference; +import org.geysermc.geyser.scoreboard.UpdateType; +import org.geysermc.geyser.scoreboard.display.score.PlayerlistDisplayScore; +import org.geysermc.geyser.session.GeyserSession; +import org.geysermc.mcprotocollib.protocol.data.game.scoreboard.ScoreboardPosition; + +public class PlayerlistDisplaySlot extends DisplaySlot { + private final Long2ObjectMap displayScores = + Long2ObjectMaps.synchronize(new Long2ObjectOpenHashMap<>()); + private final List removedScores = Collections.synchronizedList(new ArrayList<>()); + + public PlayerlistDisplaySlot(GeyserSession session, Objective objective) { + super(session, objective, ScoreboardPosition.PLAYER_LIST); + registerExisting(); + } + + @Override + protected void render0(List addScores, List removeScores) { + boolean objectiveAdd = updateType == UpdateType.ADD; + boolean objectiveUpdate = updateType == UpdateType.UPDATE; + boolean objectiveNothing = updateType == UpdateType.NOTHING; + + // if 'add' the scores aren't present, if 'update' the objective is re-added so the scores don't have to be + // manually removed, if 'remove' the scores are removed anyway + if (objectiveNothing) { + var removedScoresCopy = new ArrayList<>(removedScores); + for (var removedScore : removedScoresCopy) { + //todo idk if this if-statement is needed + if (removedScore.cachedInfo() != null) { + removeScores.add(removedScore.cachedInfo()); + } + } + removedScores.removeAll(removedScoresCopy); + } else { + removedScores.clear(); + } + + for (var score : displayScores.values()) { + if (score.referenceRemoved()) { + ScoreInfo cachedInfo = score.cachedInfo(); + // cachedInfo can be null here when ScoreboardUpdater is being used and a score is added and + // removed before a single update cycle is performed + if (cachedInfo != null) { + removeScores.add(cachedInfo); + } + continue; + } + + //todo does an animated title exist on tab? + boolean add = objectiveAdd || objectiveUpdate; + boolean exists = score.exists(); + + if (score.shouldUpdate()) { + score.update(objective); + add = true; + } + + if (add) { + addScores.add(score.cachedInfo()); + } + + // we need this as long as MCPE-143063 hasn't been fixed. + // the checks after 'add' are there to prevent removing scores that + // are going to be removed anyway / don't need to be removed + if (add && exists && objectiveNothing) { + removeScores.add(score.cachedInfo()); + } + } + + if (objectiveUpdate) { + sendRemoveObjective(); + } + + if (objectiveAdd || objectiveUpdate) { + sendDisplayObjective(); + } + + updateType = UpdateType.NOTHING; + } + + @Override + public void addScore(ScoreReference reference) { + // while it breaks a lot of stuff in Java, scoreboard do work fine with multiple players having + // the same username + var players = session.getEntityCache().getPlayersByName(reference.name()); + var selfPlayer = session.getPlayerEntity(); + if (reference.name().equals(selfPlayer.getUsername())) { + players.add(selfPlayer); + } + + for (PlayerEntity player : players) { + var score = + new PlayerlistDisplayScore(this, objective.getScoreboard().nextId(), reference, player.getGeyserId()); + displayScores.put(player.getGeyserId(), score); + } + } + + private void registerExisting() { + playerRegistered(session.getPlayerEntity()); + session.getEntityCache().getAllPlayerEntities().forEach(this::playerRegistered); + } + + @Override + public void playerRegistered(PlayerEntity player) { + var reference = objective.getScores().get(player.getUsername()); + if (reference == null) { + return; + } + var score = + new PlayerlistDisplayScore(this, objective.getScoreboard().nextId(), reference, player.getGeyserId()); + displayScores.put(player.getGeyserId(), score); + } + + @Override + public void playerRemoved(PlayerEntity player) { + var score = displayScores.remove(player.getGeyserId()); + if (score == null) { + return; + } + removedScores.add(score); + } +} diff --git a/core/src/main/java/org/geysermc/geyser/scoreboard/display/slot/SidebarDisplaySlot.java b/core/src/main/java/org/geysermc/geyser/scoreboard/display/slot/SidebarDisplaySlot.java new file mode 100644 index 000000000..24cc81f78 --- /dev/null +++ b/core/src/main/java/org/geysermc/geyser/scoreboard/display/slot/SidebarDisplaySlot.java @@ -0,0 +1,189 @@ +/* + * Copyright (c) 2024 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.scoreboard.display.slot; + +import java.util.ArrayList; +import java.util.Comparator; +import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; +import org.cloudburstmc.protocol.bedrock.data.ScoreInfo; +import org.geysermc.geyser.entity.type.player.PlayerEntity; +import org.geysermc.geyser.scoreboard.Objective; +import org.geysermc.geyser.scoreboard.ScoreReference; +import org.geysermc.geyser.scoreboard.Team; +import org.geysermc.geyser.scoreboard.UpdateType; +import org.geysermc.geyser.scoreboard.display.score.SidebarDisplayScore; +import org.geysermc.geyser.session.GeyserSession; +import org.geysermc.geyser.text.ChatColor; +import org.geysermc.mcprotocollib.protocol.data.game.scoreboard.ScoreboardPosition; + +public final class SidebarDisplaySlot extends DisplaySlot { + private static final int SCORE_DISPLAY_LIMIT = 15; + private static final Comparator SCORE_DISPLAY_ORDER = + Comparator.comparing(ScoreReference::score) + .reversed() + .thenComparing(ScoreReference::name, String.CASE_INSENSITIVE_ORDER); + + private List displayScores = new ArrayList<>(SCORE_DISPLAY_LIMIT); + + public SidebarDisplaySlot(GeyserSession session, Objective objective, ScoreboardPosition position) { + super(session, objective, position); + } + + @Override + protected void render0(List addScores, List removeScores) { + // while one could argue that we may not have to do this fancy Java filter when there are fewer scores than the + // line limit, we would lose the correct order of the scores if we don't + var newDisplayScores = + objective.getScores().values().stream() + .filter(score -> !score.hidden()) + .sorted(SCORE_DISPLAY_ORDER) + .limit(SCORE_DISPLAY_LIMIT) + .map(reference -> { + // pretty much an ArrayList#remove + var iterator = this.displayScores.iterator(); + while (iterator.hasNext()) { + var score = iterator.next(); + if (score.name().equals(reference.name())) { + iterator.remove(); + return score; + } + } + + // new score, so it should be added + return new SidebarDisplayScore(this, objective.getScoreboard().nextId(), reference); + }).collect(Collectors.toList()); + + // in newDisplayScores we removed the items that were already present from displayScores, + // meaning that the items that remain are items that are no longer displayed + for (var score : this.displayScores) { + removeScores.add(score.cachedInfo()); + } + + // preserves the new order + this.displayScores = newDisplayScores; + + // fixes ordering issues with multiple entries with same score + if (!this.displayScores.isEmpty()) { + SidebarDisplayScore lastScore = null; + int count = 0; + for (var score : this.displayScores) { + if (lastScore == null) { + lastScore = score; + continue; + } + + if (score.score() == lastScore.score()) { + // something to keep in mind is that Bedrock doesn't support some legacy color codes and adds some + // codes as well, so if the line limit is every increased keep that in mind + if (count == 0) { + lastScore.order(ChatColor.styleOrder(count++)); + } + score.order(ChatColor.styleOrder(count++)); + } else { + if (count == 0) { + lastScore.order(null); + } + count = 0; + } + lastScore = score; + } + + if (count == 0 && lastScore != null) { + lastScore.order(null); + } + } + + boolean objectiveAdd = updateType == UpdateType.ADD; + boolean objectiveUpdate = updateType == UpdateType.UPDATE; + + for (var score : this.displayScores) { + Team team = score.team(); + boolean add = objectiveAdd || objectiveUpdate; + boolean exists = score.exists(); + + if (team != null) { + // entities are mostly removed from teams without notifying the scores. + if (team.shouldRemove() || !team.hasEntity(score.name())) { + score.team(null); + add = true; + } + } + + if (score.shouldUpdate()) { + score.update(objective); + add = true; + } + + if (add) { + addScores.add(score.cachedInfo()); + } + + // we need this as long as MCPE-143063 hasn't been fixed. + // the checks after 'add' are there to prevent removing scores that + // are going to be removed anyway / don't need to be removed + if (add && exists && !(objectiveUpdate || objectiveAdd) && !score.onlyScoreValueChanged()) { + removeScores.add(score.cachedInfo()); + } + } + + if (objectiveUpdate) { + sendRemoveObjective(); + } + + if (objectiveAdd || objectiveUpdate) { + sendDisplayObjective(); + } + + updateType = UpdateType.NOTHING; + } + + @Override + public void addScore(ScoreReference reference) { + // we handle them a bit different: we sort the scores, and we add them ourselves + } + + @Override + public void playerRegistered(PlayerEntity player) { + + } + + @Override + public void playerRemoved(PlayerEntity player) { + + } + + public void setTeamFor(Team team, Set entities) { + // we only have to worry about scores that are currently displayed, + // because the constructor of the display score fetches the team + for (var score : displayScores) { + if (entities.contains(score.name())) { + score.team(team); + } + } + } +} 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 ad1996028..cccd2aa05 100644 --- a/core/src/main/java/org/geysermc/geyser/session/GeyserSession.java +++ b/core/src/main/java/org/geysermc/geyser/session/GeyserSession.java @@ -27,6 +27,7 @@ package org.geysermc.geyser.session; import io.netty.channel.Channel; import io.netty.channel.EventLoop; +import it.unimi.dsi.fastutil.Pair; import it.unimi.dsi.fastutil.ints.Int2ObjectMap; import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; import it.unimi.dsi.fastutil.objects.Object2IntMap; @@ -72,16 +73,18 @@ import org.cloudburstmc.protocol.bedrock.data.SpawnBiomeType; import org.cloudburstmc.protocol.bedrock.data.command.CommandEnumData; import org.cloudburstmc.protocol.bedrock.data.command.CommandPermission; import org.cloudburstmc.protocol.bedrock.data.command.SoftEnumUpdateType; +import org.cloudburstmc.protocol.bedrock.data.definitions.DimensionDefinition; import org.cloudburstmc.protocol.bedrock.data.entity.EntityFlag; import org.cloudburstmc.protocol.bedrock.data.inventory.ItemData; +import org.cloudburstmc.protocol.bedrock.data.inventory.crafting.recipe.CraftingRecipeData; import org.cloudburstmc.protocol.bedrock.packet.AvailableEntityIdentifiersPacket; import org.cloudburstmc.protocol.bedrock.packet.BedrockPacket; import org.cloudburstmc.protocol.bedrock.packet.BiomeDefinitionListPacket; import org.cloudburstmc.protocol.bedrock.packet.CameraPresetsPacket; import org.cloudburstmc.protocol.bedrock.packet.ChunkRadiusUpdatedPacket; import org.cloudburstmc.protocol.bedrock.packet.ClientboundCloseFormPacket; -import org.cloudburstmc.protocol.bedrock.packet.CraftingDataPacket; import org.cloudburstmc.protocol.bedrock.packet.CreativeContentPacket; +import org.cloudburstmc.protocol.bedrock.packet.DimensionDataPacket; import org.cloudburstmc.protocol.bedrock.packet.EmoteListPacket; import org.cloudburstmc.protocol.bedrock.packet.GameRulesChangedPacket; import org.cloudburstmc.protocol.bedrock.packet.ItemComponentPacket; @@ -137,6 +140,7 @@ import org.geysermc.geyser.impl.camera.GeyserCameraData; import org.geysermc.geyser.inventory.Inventory; import org.geysermc.geyser.inventory.PlayerInventory; import org.geysermc.geyser.inventory.recipe.GeyserRecipe; +import org.geysermc.geyser.inventory.recipe.GeyserSmithingRecipe; import org.geysermc.geyser.inventory.recipe.GeyserStonecutterData; import org.geysermc.geyser.item.Items; import org.geysermc.geyser.item.type.BlockItem; @@ -156,6 +160,7 @@ import org.geysermc.geyser.session.cache.ChunkCache; import org.geysermc.geyser.session.cache.EntityCache; import org.geysermc.geyser.session.cache.EntityEffectCache; import org.geysermc.geyser.session.cache.FormCache; +import org.geysermc.geyser.session.cache.InputCache; import org.geysermc.geyser.session.cache.LodestoneCache; import org.geysermc.geyser.session.cache.PistonCache; import org.geysermc.geyser.session.cache.PreferencesCache; @@ -172,8 +177,8 @@ import org.geysermc.geyser.text.MinecraftLocale; import org.geysermc.geyser.translator.inventory.InventoryTranslator; import org.geysermc.geyser.translator.text.MessageTranslator; import org.geysermc.geyser.util.ChunkUtils; -import org.geysermc.geyser.util.DimensionUtils; import org.geysermc.geyser.util.EntityUtils; +import org.geysermc.geyser.util.InventoryUtils; import org.geysermc.geyser.util.JsonUtils; import org.geysermc.geyser.util.LoginEncryptionUtils; import org.geysermc.geyser.util.MinecraftAuthLogger; @@ -188,7 +193,6 @@ import org.geysermc.mcprotocollib.network.event.session.SessionAdapter; import org.geysermc.mcprotocollib.network.packet.Packet; import org.geysermc.mcprotocollib.network.tcp.TcpClientSession; import org.geysermc.mcprotocollib.network.tcp.TcpSession; -import org.geysermc.mcprotocollib.protocol.ClientListener; import org.geysermc.mcprotocollib.protocol.MinecraftConstants; import org.geysermc.mcprotocollib.protocol.MinecraftProtocol; import org.geysermc.mcprotocollib.protocol.data.ProtocolState; @@ -200,16 +204,15 @@ import org.geysermc.mcprotocollib.protocol.data.game.entity.player.Hand; import org.geysermc.mcprotocollib.protocol.data.game.entity.player.HandPreference; import org.geysermc.mcprotocollib.protocol.data.game.entity.player.PlayerAction; import org.geysermc.mcprotocollib.protocol.data.game.setting.ChatVisibility; +import org.geysermc.mcprotocollib.protocol.data.game.setting.ParticleStatus; import org.geysermc.mcprotocollib.protocol.data.game.setting.SkinPart; import org.geysermc.mcprotocollib.protocol.data.game.statistic.CustomStatistic; import org.geysermc.mcprotocollib.protocol.data.game.statistic.Statistic; import org.geysermc.mcprotocollib.protocol.packet.common.serverbound.ServerboundClientInformationPacket; -import org.geysermc.mcprotocollib.protocol.packet.configuration.clientbound.ClientboundFinishConfigurationPacket; -import org.geysermc.mcprotocollib.protocol.packet.configuration.serverbound.ServerboundFinishConfigurationPacket; import org.geysermc.mcprotocollib.protocol.packet.handshake.serverbound.ClientIntentionPacket; import org.geysermc.mcprotocollib.protocol.packet.ingame.serverbound.ServerboundChatCommandSignedPacket; import org.geysermc.mcprotocollib.protocol.packet.ingame.serverbound.ServerboundChatPacket; -import org.geysermc.mcprotocollib.protocol.packet.ingame.serverbound.player.ServerboundMovePlayerPosPacket; +import org.geysermc.mcprotocollib.protocol.packet.ingame.serverbound.ServerboundClientTickEndPacket; import org.geysermc.mcprotocollib.protocol.packet.ingame.serverbound.player.ServerboundPlayerAbilitiesPacket; import org.geysermc.mcprotocollib.protocol.packet.ingame.serverbound.player.ServerboundPlayerActionPacket; import org.geysermc.mcprotocollib.protocol.packet.ingame.serverbound.player.ServerboundUseItemPacket; @@ -249,7 +252,6 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource { private final EventLoop eventLoop; @Setter private AuthData authData; - @Setter private BedrockClientData clientData; /** * Used for Floodgate skin uploading @@ -273,6 +275,7 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource { private final EntityCache entityCache; private final EntityEffectCache effectCache; private final FormCache formCache; + private final InputCache inputCache; private final LodestoneCache lodestoneCache; private final PistonCache pistonCache; private final PreferencesCache preferencesCache; @@ -307,7 +310,7 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource { private final AtomicInteger itemNetId = new AtomicInteger(2); @Setter - private ScheduledFuture craftingGridFuture; + private ScheduledFuture containerOutputFuture; /** * Stores session collision @@ -384,6 +387,10 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource { @Setter private boolean sprinting; + /** + * The overworld dimension which Bedrock Edition uses. + */ + private BedrockDimension bedrockOverworldDimension = BedrockDimension.OVERWORLD; /** * The dimension of the player. * As all entities are in the same world, this can be safely applied to all other entities. @@ -397,7 +404,7 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource { * right before the StartGamePacket is sent. */ @Setter - private BedrockDimension bedrockDimension = BedrockDimension.OVERWORLD; + private BedrockDimension bedrockDimension = this.bedrockOverworldDimension; @Setter private int breakingBlock; @@ -434,21 +441,22 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource { private Entity mouseoverEntity; /** - * Stores all Java recipes by recipe identifier, and matches them to all possible Bedrock recipe identifiers. - * They are not 1:1, since Bedrock can have multiple recipes for the same Java recipe. + * Stores all Java recipes by ID, and matches them to all possible Bedrock recipe identifiers. */ - private final Map> javaToBedrockRecipeIds; + private final Int2ObjectMap> javaToBedrockRecipeIds; + private final Int2ObjectMap craftingRecipes; @Setter - private Int2ObjectMap craftingRecipes; + private Pair lastCreatedRecipe = null; // TODO try to prevent sending duplicate recipes private final AtomicInteger lastRecipeNetId; /** * Saves a list of all stonecutter recipes, for use in a stonecutter inventory. - * The key is the Java ID of the item; the values are all the possible outputs' Java IDs sorted by their string identifier + * The key is the Bedrock recipe net ID; the values are their respective output and button ID. */ @Setter private Int2ObjectMap stonecutterRecipes; + private final List smithingRecipes = new ArrayList<>(); /** * Whether to work around 1.13's different behavior in villager trading menus. @@ -517,18 +525,6 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource { @Setter private boolean placedBucket; - /** - * Used to send a movement packet every three seconds if the player hasn't moved. Prevents timeouts when AFK in certain instances. - */ - @Setter - private long lastMovementTimestamp = System.currentTimeMillis(); - - /** - * Used to send a ServerboundMoveVehiclePacket for every PlayerInputPacket after idling on a boat/horse for more than 100ms - */ - @Setter - private long lastVehicleMoveTimestamp = System.currentTimeMillis(); - /** * Counts how many ticks have occurred since an arm animation started. * -1 means there is no active arm swing; -2 means an arm swing will start in a tick. @@ -666,13 +662,14 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource { this.entityCache = new EntityCache(this); this.effectCache = new EntityEffectCache(); this.formCache = new FormCache(this); + this.inputCache = new InputCache(this); this.lodestoneCache = new LodestoneCache(); this.pistonCache = new PistonCache(this); this.preferencesCache = new PreferencesCache(this); this.registryCache = new RegistryCache(this); this.skullCache = new SkullCache(this); this.structureBlockCache = new StructureBlockCache(); - this.tagCache = new TagCache(); + this.tagCache = new TagCache(this); this.worldCache = new WorldCache(this); this.cameraData = new GeyserCameraData(this); this.entityData = new GeyserEntityData(this); @@ -687,8 +684,8 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource { this.playerInventory = new PlayerInventory(); this.openInventory = null; this.craftingRecipes = new Int2ObjectOpenHashMap<>(); - this.javaToBedrockRecipeIds = new Object2ObjectOpenHashMap<>(); - this.lastRecipeNetId = new AtomicInteger(1); + this.javaToBedrockRecipeIds = new Int2ObjectOpenHashMap<>(); + this.lastRecipeNetId = new AtomicInteger(InventoryUtils.LAST_RECIPE_NET_ID + 1); this.spawned = false; this.loggedIn = false; @@ -703,6 +700,31 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource { * Send all necessary packets to load Bedrock into the server */ public void connect() { + // Note: this.dimensionType may be null here if the player is connecting from online mode + int minY = BedrockDimension.OVERWORLD.minY(); + int maxY = BedrockDimension.OVERWORLD.maxY(); + for (JavaDimension javaDimension : this.registryCache.dimensions().values()) { + if (javaDimension.bedrockId() == BedrockDimension.OVERWORLD_ID) { + minY = Math.min(minY, javaDimension.minY()); + maxY = Math.max(maxY, javaDimension.maxY()); + } + } + minY = Math.max(minY, -512); + maxY = Math.min(maxY, 512); + + if (minY < BedrockDimension.OVERWORLD.minY() || maxY > BedrockDimension.OVERWORLD.maxY()) { + final boolean isInOverworld = this.bedrockDimension == this.bedrockOverworldDimension; + this.bedrockOverworldDimension = new BedrockDimension(minY, maxY - minY, true, BedrockDimension.OVERWORLD_ID); + if (isInOverworld) { + this.bedrockDimension = this.bedrockOverworldDimension; + } + geyser.getLogger().debug("Extending overworld dimension to " + minY + " - " + maxY); + + DimensionDataPacket dimensionDataPacket = new DimensionDataPacket(); + dimensionDataPacket.getDefinitions().add(new DimensionDefinition("minecraft:overworld", maxY, minY, 5 /* Void */)); + upstream.sendPacket(dimensionDataPacket); + } + startGame(); sentSpawnPacket = true; syncEntityProperties(); @@ -731,12 +753,6 @@ 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.forVersion(this.upstream.getProtocolVersion())); - upstream.sendPacket(craftingDataPacket); - PlayStatusPacket playStatusPacket = new PlayStatusPacket(); playStatusPacket.setStatus(PlayStatusPacket.Status.PLAYER_SPAWN); upstream.sendPacket(playStatusPacket); @@ -925,8 +941,6 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource { // Start ticking tickThread = eventLoop.scheduleAtFixedRate(this::tick, 50, 50, TimeUnit.MILLISECONDS); - this.protocol.setUseDefaultListeners(false); - TcpSession downstream; if (geyser.getBootstrap().getSocketAddress() != null) { // We're going to connect through the JVM and not through TCP @@ -952,7 +966,6 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource { this.downstream.getSession().setFlag(MinecraftConstants.FOLLOW_TRANSFERS, false); if (geyser.config().java().useProxyProtocol()) { - downstream.setFlag(BuiltinFlags.ENABLE_CLIENT_PROXY_PROTOCOL, true); downstream.setFlag(BuiltinFlags.CLIENT_PROXIED_ADDRESS, upstream.getAddress()); } if (geyser.config().forwardPlayerPing()) { @@ -962,22 +975,6 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource { // We'll handle this since we have the registry data on hand downstream.setFlag(MinecraftConstants.SEND_BLANK_KNOWN_PACKS_RESPONSE, false); - // This isn't a great solution, but... we want to make sure the finish configuration packet cannot be sent - // before the KnownPacks packet. - this.downstream.getSession().addListener(new ClientListener(ProtocolState.LOGIN, loginEvent.transferring()) { - @Override - public void packetReceived(Session session, Packet packet) { - if (protocol.getState() == ProtocolState.CONFIGURATION) { - if (packet instanceof ClientboundFinishConfigurationPacket) { - // Prevent - GeyserSession.this.ensureInEventLoop(() -> GeyserSession.this.sendDownstreamPacket(new ServerboundFinishConfigurationPacket())); - return; - } - } - super.packetReceived(session, packet); - } - }); - downstream.addListener(new SessionAdapter() { @Override public void packetSending(PacketSendingEvent event) { @@ -1072,6 +1069,11 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource { // Download and load the language for the player MinecraftLocale.downloadAndLoadLocale(locale); + +// if (sentSpawnPacket && !GameProtocol.isPre1_21_2(GeyserSession.this)) { +// // Possible form to close. +// upstream.sendPacket(new ClientboundCloseFormPacket()); +// } } @Override @@ -1250,18 +1252,6 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource { protected void tick() { try { pistonCache.tick(); - // Check to see if the player's position needs updating - a position update should be sent once every 3 seconds - if (spawned && (System.currentTimeMillis() - lastMovementTimestamp) > 3000) { - // Recalculate in case something else changed position - Vector3d position = collisionManager.adjustBedrockPosition(playerEntity.getPosition(), playerEntity.isOnGround(), false); - // A null return value cancels the packet - if (position != null) { - ServerboundMovePlayerPosPacket packet = new ServerboundMovePlayerPosPacket(playerEntity.isOnGround(), - position.getX(), position.getY(), position.getZ()); - sendDownstreamGamePacket(packet); - } - lastMovementTimestamp = System.currentTimeMillis(); - } if (worldBorder.isResizing()) { worldBorder.resize(); @@ -1319,6 +1309,12 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource { armAnimationTicks = -1; } } + + if (spawned) { + // Could move this to the PlayerAuthInput translator, in the event the player lags + // but this will work once we implement matching Java custom tick cycles + sendDownstreamGamePacket(ServerboundClientTickEndPacket.INSTANCE); + } } catch (Throwable throwable) { throwable.printStackTrace(); } @@ -1377,14 +1373,28 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource { } public void setSwimming(boolean swimming) { - if (swimming) { + if (!swimming && playerEntity.getFlag(EntityFlag.CRAWLING)) { + // Do not update bounding box. + playerEntity.setFlag(EntityFlag.SWIMMING, false); + playerEntity.updateBedrockMetadata(); + return; + } + toggleSwimmingPose(swimming, EntityFlag.SWIMMING); + } + + public void setCrawling(boolean crawling) { + toggleSwimmingPose(crawling, EntityFlag.CRAWLING); + } + + private void toggleSwimmingPose(boolean crawling, EntityFlag flag) { + if (crawling) { this.pose = Pose.SWIMMING; playerEntity.setBoundingBoxHeight(0.6f); } else { this.pose = Pose.STANDING; playerEntity.setBoundingBoxHeight(playerEntity.getDefinition().height()); } - playerEntity.setFlag(EntityFlag.SWIMMING, swimming); + playerEntity.setFlag(flag, crawling); playerEntity.updateBedrockMetadata(); } @@ -1404,6 +1414,12 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource { this.cameraData.handleGameModeChange(currentlySpectator, newGamemode); } + public void setClientData(BedrockClientData data) { + this.clientData = data; + this.inputCache.setInputMode( + org.cloudburstmc.protocol.bedrock.data.InputMode.values()[data.getCurrentInputMode().ordinal()]); + } + /** * Convenience method to reduce amount of duplicate code. Sends ServerboundUseItemPacket. */ @@ -1586,7 +1602,7 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource { startGamePacket.setRotation(Vector2f.from(1, 1)); startGamePacket.setSeed(-1L); - startGamePacket.setDimensionId(DimensionUtils.javaToBedrock(bedrockDimension)); + startGamePacket.setDimensionId(bedrockDimension.bedrockId()); startGamePacket.setGeneratorId(1); startGamePacket.setLevelGameType(GameType.SURVIVAL); startGamePacket.setDifficulty(1); @@ -1652,7 +1668,7 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource { startGamePacket.setChatRestrictionLevel(ChatRestrictionLevel.NONE); - startGamePacket.setAuthoritativeMovementMode(AuthoritativeMovementMode.CLIENT); + startGamePacket.setAuthoritativeMovementMode(AuthoritativeMovementMode.SERVER); startGamePacket.setRewindHistorySize(0); startGamePacket.setServerAuthoritativeBlockBreaking(false); @@ -1750,8 +1766,8 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource { return; } - if (protocol.getState() != intendedState) { - geyser.getLogger().debug("Tried to send " + packet.getClass().getSimpleName() + " packet while not in " + intendedState.name() + " state"); + if (protocol.getOutboundState() != intendedState) { + geyser.getLogger().debug("Tried to send " + packet.getClass().getSimpleName() + " packet while not in " + intendedState.name() + " outbound state"); return; } @@ -1785,7 +1801,7 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource { } private void sendDownstreamPacket0(Packet packet) { - ProtocolState state = protocol.getState(); + ProtocolState state = protocol.getOutboundState(); if (state == ProtocolState.GAME || state == ProtocolState.CONFIGURATION || packet.getClass() == ServerboundCustomQueryAnswerPacket.class) { downstream.sendPacket(packet); } else { @@ -1950,7 +1966,7 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource { public void sendJavaClientSettings() { ServerboundClientInformationPacket clientSettingsPacket = new ServerboundClientInformationPacket(locale(), getRenderDistance(), ChatVisibility.FULL, true, SKIN_PARTS, - HandPreference.RIGHT_HAND, false, true); + HandPreference.RIGHT_HAND, false, true, ParticleStatus.ALL); // TODO particle status sendDownstreamPacket(clientSettingsPacket); } @@ -2117,7 +2133,7 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource { @Override public @NonNull InputMode inputMode() { - return InputMode.values()[clientData.getCurrentInputMode().ordinal()]; //todo + return InputMode.values()[inputCache.getInputMode().ordinal()]; //todo } @Override @@ -2210,6 +2226,11 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource { return (int) Math.floor(rakSessionCodec.getPing()); } + @Override + public int protocolVersion() { + return upstream.getProtocolVersion(); + } + @Override public void closeForm() { if (!GameProtocol.isPre1_21_2(this)) { 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 3affa12cf..78d21e63b 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 @@ -31,15 +31,18 @@ import it.unimi.dsi.fastutil.longs.Long2ObjectMap; import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap; import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap; import it.unimi.dsi.fastutil.objects.ObjectArrayList; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.Map; +import java.util.UUID; +import java.util.concurrent.atomic.AtomicLong; import lombok.Getter; import org.geysermc.geyser.entity.type.Entity; import org.geysermc.geyser.entity.type.Tickable; import org.geysermc.geyser.entity.type.player.PlayerEntity; import org.geysermc.geyser.session.GeyserSession; -import java.util.*; -import java.util.concurrent.atomic.AtomicLong; - /** * Each session has its own EntityCache in the occasion that an entity packet is sent specifically * for that player (e.g. seeing vanished players from /vanish) @@ -66,6 +69,11 @@ public class EntityCache { public void spawnEntity(Entity entity) { if (cacheEntity(entity)) { + // start tracking newly spawned entities. Doing this before the actual entity spawn can result in combining + // the otherwise sent metadata packet (in the case of team visibility, which sets the NAME metadata to + // empty) with the entity spawn packet (which also includes metadata). Resulting in 1 less packet sent. + session.getWorldCache().getScoreboard().entityRegistered(entity); + entity.spawnEntity(); if (entity instanceof Tickable) { @@ -86,21 +94,24 @@ public class EntityCache { } public void removeEntity(Entity entity) { + if (entity == null) { + return; + } + if (entity instanceof PlayerEntity player) { session.getPlayerWithCustomHeads().remove(player.getUuid()); } - if (entity != null) { - if (entity.isValid()) { - entity.despawnEntity(); - } + if (entity.isValid()) { + entity.despawnEntity(); + } + entities.remove(entityIdTranslations.remove(entity.getEntityId())); - long geyserId = entityIdTranslations.remove(entity.getEntityId()); - entities.remove(geyserId); + // don't track the entity anymore, now that it's removed + session.getWorldCache().getScoreboard().entityRemoved(entity); - if (entity instanceof Tickable) { - tickableEntities.remove(entity); - } + if (entity instanceof Tickable) { + tickableEntities.remove(entity); } } @@ -126,15 +137,37 @@ public class EntityCache { public void addPlayerEntity(PlayerEntity entity) { // putIfAbsent matches the behavior of playerInfoMap in Java as of 1.19.3 - playerEntities.putIfAbsent(entity.getUuid(), entity); + boolean exists = playerEntities.putIfAbsent(entity.getUuid(), entity) != null; + if (exists) { + return; + } + + // notify scoreboard for new entity + var scoreboard = session.getWorldCache().getScoreboard(); + scoreboard.playerRegistered(entity); } public PlayerEntity getPlayerEntity(UUID uuid) { return playerEntities.get(uuid); } + public List getPlayersByName(String name) { + var list = new ArrayList(); + for (PlayerEntity player : playerEntities.values()) { + if (name.equals(player.getUsername())) { + list.add(player); + } + } + return list; + } + public PlayerEntity removePlayerEntity(UUID uuid) { - return playerEntities.remove(uuid); + var player = playerEntities.remove(uuid); + if (player != null) { + // notify scoreboard + session.getWorldCache().getScoreboard().playerRemoved(player); + } + return player; } public Collection getAllPlayerEntities() { diff --git a/core/src/main/java/org/geysermc/geyser/session/cache/InputCache.java b/core/src/main/java/org/geysermc/geyser/session/cache/InputCache.java new file mode 100644 index 000000000..f12c4d3c8 --- /dev/null +++ b/core/src/main/java/org/geysermc/geyser/session/cache/InputCache.java @@ -0,0 +1,112 @@ +/* + * Copyright (c) 2024 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.session.cache; + +import lombok.Getter; +import lombok.Setter; +import org.checkerframework.checker.nullness.qual.MonotonicNonNull; +import org.cloudburstmc.math.vector.Vector2f; +import org.cloudburstmc.protocol.bedrock.data.InputMode; +import org.cloudburstmc.protocol.bedrock.data.PlayerAuthInputData; +import org.cloudburstmc.protocol.bedrock.packet.PlayerAuthInputPacket; +import org.geysermc.geyser.session.GeyserSession; +import org.geysermc.mcprotocollib.protocol.packet.ingame.serverbound.level.ServerboundPlayerInputPacket; + +import java.util.Set; + +public final class InputCache { + private final GeyserSession session; + private ServerboundPlayerInputPacket inputPacket = new ServerboundPlayerInputPacket(false, false, false, false, false, false, false); + private boolean lastHorizontalCollision; + private int ticksSinceLastMovePacket; + @Getter @Setter + private int jumpingTicks; + @Getter @Setter + private float jumpScale; + @Getter @Setter + private @MonotonicNonNull InputMode inputMode; + + public InputCache(GeyserSession session) { + this.session = session; + } + + public void processInputs(PlayerAuthInputPacket packet) { + // Input is sent to the server before packet positions, as of 1.21.2 + Set bedrockInput = packet.getInputData(); + var oldInputPacket = this.inputPacket; + this.inputMode = packet.getInputMode(); + + boolean up, down, left, right; + if (this.inputMode == InputMode.MOUSE) { + up = bedrockInput.contains(PlayerAuthInputData.UP); + down = bedrockInput.contains(PlayerAuthInputData.DOWN); + left = bedrockInput.contains(PlayerAuthInputData.LEFT); + right = bedrockInput.contains(PlayerAuthInputData.RIGHT); + } else { + // The above flags don't fire TODO test console + Vector2f analogMovement = packet.getAnalogMoveVector(); + up = analogMovement.getY() > 0; + down = analogMovement.getY() < 0; + left = analogMovement.getX() > 0; + right = analogMovement.getX() < 0; + } + + // TODO when is UP_LEFT, etc. used? + this.inputPacket = this.inputPacket + .withForward(up) + .withBackward(down) + .withLeft(left) + .withRight(right) + .withJump(bedrockInput.contains(PlayerAuthInputData.JUMPING)) // Looks like this only triggers when the JUMP key input is being pressed. There's also JUMP_DOWN? + .withShift(bedrockInput.contains(PlayerAuthInputData.SNEAKING)) + .withSprint(bedrockInput.contains(PlayerAuthInputData.SPRINTING)); // SPRINTING will trigger even if the player isn't moving + + if (oldInputPacket != this.inputPacket) { // Simple equality check is fine since we're checking for an instance change. + session.sendDownstreamGamePacket(this.inputPacket); + } + } + + public boolean wasJumping() { + return this.inputPacket.isJump(); + } + + public void markPositionPacketSent() { + this.ticksSinceLastMovePacket = 0; + } + + public boolean shouldSendPositionReminder() { + // NOTE: if we implement spectating entities, DO NOT TICK THIS LOGIC THEN. + return ++this.ticksSinceLastMovePacket >= 20; + } + + public boolean lastHorizontalCollision() { + return lastHorizontalCollision; + } + + public void setLastHorizontalCollision(boolean lastHorizontalCollision) { + this.lastHorizontalCollision = lastHorizontalCollision; + } +} diff --git a/core/src/main/java/org/geysermc/geyser/session/cache/RegistryCache.java b/core/src/main/java/org/geysermc/geyser/session/cache/RegistryCache.java index 4a4167f15..ecd293bff 100644 --- a/core/src/main/java/org/geysermc/geyser/session/cache/RegistryCache.java +++ b/core/src/main/java/org/geysermc/geyser/session/cache/RegistryCache.java @@ -40,13 +40,16 @@ import org.cloudburstmc.protocol.bedrock.data.TrimPattern; import org.geysermc.geyser.GeyserImpl; import org.geysermc.geyser.entity.type.living.animal.tameable.WolfEntity; import org.geysermc.geyser.inventory.item.BannerPattern; +import org.geysermc.geyser.inventory.item.GeyserInstrument; import org.geysermc.geyser.inventory.recipe.TrimRecipe; import org.geysermc.geyser.item.enchantment.Enchantment; import org.geysermc.geyser.level.JavaDimension; import org.geysermc.geyser.level.JukeboxSong; import org.geysermc.geyser.level.PaintingType; import org.geysermc.geyser.session.GeyserSession; +import org.geysermc.geyser.session.cache.registry.JavaRegistries; import org.geysermc.geyser.session.cache.registry.JavaRegistry; +import org.geysermc.geyser.session.cache.registry.JavaRegistryKey; import org.geysermc.geyser.session.cache.registry.RegistryEntryContext; import org.geysermc.geyser.session.cache.registry.SimpleJavaRegistry; import org.geysermc.geyser.text.ChatDecoration; @@ -80,7 +83,8 @@ public final class RegistryCache { static { register("chat_type", cache -> cache.chatTypes, ChatDecoration::readChatType); register("dimension_type", cache -> cache.dimensions, JavaDimension::read); - register("enchantment", cache -> cache.enchantments, Enchantment::read); + register(JavaRegistries.ENCHANTMENT, cache -> cache.enchantments, Enchantment::read); + register("instrument", cache -> cache.instruments, GeyserInstrument::read); register("jukebox_song", cache -> cache.jukeboxSongs, JukeboxSong::read); register("painting_variant", cache -> cache.paintings, context -> PaintingType.getByName(context.id())); register("trim_material", cache -> cache.trimMaterials, TrimRecipe::readTrimMaterial); @@ -94,8 +98,7 @@ public final class RegistryCache { Map> defaults = new HashMap<>(); // Don't create a keySet - no need to create the cached object in HashMap if we don't use it again REGISTRIES.forEach((key, $) -> { - List rawValues = tag.getCompound(key.asString()) - .getList("value", NbtType.COMPOUND); + List rawValues = tag.getCompound(key.asString()).getList("value", NbtType.COMPOUND); Map values = new HashMap<>(); for (NbtMap value : rawValues) { Key name = MinecraftKey.key(value.getString("name")); @@ -128,6 +131,7 @@ public final class RegistryCache { private final JavaRegistry bannerPatterns = new SimpleJavaRegistry<>(); private final JavaRegistry wolfVariants = new SimpleJavaRegistry<>(); + private final JavaRegistry instruments = new SimpleJavaRegistry<>(); public RegistryCache(GeyserSession session) { this.session = session; @@ -152,8 +156,27 @@ public final class RegistryCache { * @param the class that represents these entries. */ private static void register(String registry, Function> localCacheFunction, Function reader) { - Key registryKey = MinecraftKey.key(registry); - REGISTRIES.put(registryKey, (registryCache, entries) -> { + register(MinecraftKey.key(registry), localCacheFunction, reader); + } + + /** + * @param registry the Java registry resource location. + * @param localCacheFunction which local field in RegistryCache are we caching entries for this registry? + * @param reader converts the RegistryEntry NBT into a class file + * @param the class that represents these entries. + */ + private static void register(JavaRegistryKey registry, Function> localCacheFunction, Function reader) { + register(registry.registryKey(), localCacheFunction, reader); + } + + /** + * @param registry the Java registry resource location. + * @param localCacheFunction which local field in RegistryCache are we caching entries for this registry? + * @param reader converts the RegistryEntry NBT into a class file + * @param the class that represents these entries. + */ + private static void register(Key registry, Function> localCacheFunction, Function reader) { + REGISTRIES.put(registry, (registryCache, entries) -> { Map localRegistry = null; JavaRegistry localCache = localCacheFunction.apply(registryCache); // Clear each local cache every time a new registry entry is given to us @@ -172,7 +195,7 @@ public final class RegistryCache { // If the data is null, that's the server telling us we need to use our default values. if (entry.getData() == null) { if (localRegistry == null) { // Lazy initialize - localRegistry = DEFAULTS.get(registryKey); + localRegistry = DEFAULTS.get(registry); } entry = new RegistryEntry(entry.getId(), localRegistry.get(entry.getId())); } diff --git a/core/src/main/java/org/geysermc/geyser/session/cache/TagCache.java b/core/src/main/java/org/geysermc/geyser/session/cache/TagCache.java index f4d69dcdb..2b0f257a3 100644 --- a/core/src/main/java/org/geysermc/geyser/session/cache/TagCache.java +++ b/core/src/main/java/org/geysermc/geyser/session/cache/TagCache.java @@ -26,116 +26,119 @@ package org.geysermc.geyser.session.cache; import it.unimi.dsi.fastutil.ints.IntArrays; +import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap; import net.kyori.adventure.key.Key; -import org.checkerframework.checker.nullness.qual.Nullable; -import org.geysermc.geyser.GeyserImpl; import org.geysermc.geyser.GeyserLogger; import org.geysermc.geyser.inventory.GeyserItemStack; import org.geysermc.geyser.item.type.Item; -import org.geysermc.geyser.level.block.type.Block; import org.geysermc.geyser.session.GeyserSession; -import org.geysermc.geyser.session.cache.tags.BlockTag; -import org.geysermc.geyser.session.cache.tags.EnchantmentTag; -import org.geysermc.geyser.session.cache.tags.ItemTag; +import org.geysermc.geyser.session.cache.registry.JavaRegistries; +import org.geysermc.geyser.session.cache.registry.JavaRegistryKey; +import org.geysermc.geyser.session.cache.tags.GeyserHolderSet; +import org.geysermc.geyser.session.cache.tags.Tag; import org.geysermc.geyser.util.MinecraftKey; -import org.geysermc.geyser.util.Ordered; import org.geysermc.mcprotocollib.protocol.packet.common.clientbound.ClientboundUpdateTagsPacket; import javax.annotation.ParametersAreNonnullByDefault; import java.util.Arrays; +import java.util.List; import java.util.Map; -import static org.geysermc.geyser.session.cache.tags.BlockTag.ALL_BLOCK_TAGS; -import static org.geysermc.geyser.session.cache.tags.EnchantmentTag.ALL_ENCHANTMENT_TAGS; -import static org.geysermc.geyser.session.cache.tags.ItemTag.ALL_ITEM_TAGS; - /** * Manages information sent from the {@link ClientboundUpdateTagsPacket}. If that packet is not sent, all lists here - * will remain empty, matching Java Edition behavior. + * will remain empty, matching Java Edition behavior. Looking up a tag that wasn't listed in that packet will return an empty array. + * Only tags from suitable registries in {@link JavaRegistries} are stored. Read {@link JavaRegistryKey} for more information. */ @ParametersAreNonnullByDefault public final class TagCache { - private final int[][] blocks = new int[ALL_BLOCK_TAGS.size()][]; - private final int[][] items = new int[ALL_ITEM_TAGS.size()][]; - private final int[][] enchantments = new int[ALL_ENCHANTMENT_TAGS.size()][]; + private final GeyserSession session; + private final Map, int[]> tags = new Object2ObjectOpenHashMap<>(); + + public TagCache(GeyserSession session) { + this.session = session; + } public void loadPacket(GeyserSession session, ClientboundUpdateTagsPacket packet) { - Map blockTags = packet.getTags().get(MinecraftKey.key("block")); - loadTags("Block", blockTags, ALL_BLOCK_TAGS, this.blocks); - - // Hack btw + Map> allTags = packet.getTags(); GeyserLogger logger = session.getGeyser().getLogger(); - int[] convertableToMud = blockTags.get(MinecraftKey.key("convertable_to_mud")); - boolean emulatePost1_18Logic = convertableToMud != null && convertableToMud.length != 0; - session.setEmulatePost1_18Logic(emulatePost1_18Logic); - if (logger.isDebug()) { - logger.debug("Emulating post 1.18 block predication logic for " + session.bedrockUsername() + "? " + emulatePost1_18Logic); - } - Map itemTags = packet.getTags().get(MinecraftKey.key("item")); - loadTags("Item", itemTags, ALL_ITEM_TAGS, this.items); + this.tags.clear(); - // Hack btw - boolean emulatePost1_13Logic = itemTags.get(MinecraftKey.key("signs")).length > 1; - session.setEmulatePost1_13Logic(emulatePost1_13Logic); - if (logger.isDebug()) { - logger.debug("Emulating post 1.13 villager logic for " + session.bedrockUsername() + "? " + emulatePost1_13Logic); - } - - Map enchantmentTags = packet.getTags().get(MinecraftKey.key("enchantment")); - loadTags("Enchantment", enchantmentTags, ALL_ENCHANTMENT_TAGS, this.enchantments); - } - - private void loadTags(String type, @Nullable Map packetTags, Map allTags, int[][] localValues) { - if (packetTags == null) { - Arrays.fill(localValues, IntArrays.EMPTY_ARRAY); - GeyserImpl.getInstance().getLogger().debug("Not loading " + type + " tags; they do not exist here."); - return; - } - allTags.forEach((location, tag) -> { - int[] values = packetTags.get(location); - if (values != null) { - if (values.length != 0) { - localValues[tag.ordinal()] = values; - } else { - localValues[tag.ordinal()] = IntArrays.EMPTY_ARRAY; - } - } else { - localValues[tag.ordinal()] = IntArrays.EMPTY_ARRAY; - GeyserImpl.getInstance().getLogger().debug(type + " tag not found from server: " + location); + for (Key registryKey : allTags.keySet()) { + JavaRegistryKey registry = JavaRegistries.fromKey(registryKey); + if (registry == null || !registry.shouldStoreTags()) { + logger.debug("Not loading tags for registry " + registryKey + " (registry not listed in JavaRegistries, or was not suitable to load tags)"); + continue; } - }); + + Map registryTags = allTags.get(registryKey); + + if (registry == JavaRegistries.BLOCK) { + // Hack btw + int[] convertableToMud = registryTags.get(MinecraftKey.key("convertable_to_mud")); + boolean emulatePost1_18Logic = convertableToMud != null && convertableToMud.length != 0; + session.setEmulatePost1_18Logic(emulatePost1_18Logic); + if (logger.isDebug()) { + logger.debug("Emulating post 1.18 block predication logic for " + session.bedrockUsername() + "? " + emulatePost1_18Logic); + } + } else if (registry == JavaRegistries.ITEM) { + // Hack btw + boolean emulatePost1_13Logic = registryTags.get(MinecraftKey.key("signs")).length > 1; + session.setEmulatePost1_13Logic(emulatePost1_13Logic); + if (logger.isDebug()) { + logger.debug("Emulating post 1.13 villager logic for " + session.bedrockUsername() + "? " + emulatePost1_13Logic); + } + } + + loadTags(registryTags, registry, registry == JavaRegistries.ITEM); + } } - /** - * @return true if the block tag is present and contains this block mapping's Java ID. - */ - public boolean is(BlockTag tag, Block block) { - int[] values = this.blocks[tag.ordinal()]; - return contains(values, block.javaId()); + private void loadTags(Map packetTags, JavaRegistryKey registry, boolean sort) { + for (Map.Entry tag : packetTags.entrySet()) { + int[] value = tag.getValue(); + if (sort) { + // Used in RecipeBookAddTranslator + Arrays.sort(value); + } + this.tags.put(new Tag<>(registry, tag.getKey()), value); + } + } + + public boolean is(Tag tag, T object) { + return contains(getRaw(tag), tag.registry().toNetworkId(session, object)); } /** * @return true if the item tag is present and contains this item stack's Java ID. */ - public boolean is(ItemTag tag, GeyserItemStack itemStack) { + public boolean is(Tag tag, GeyserItemStack itemStack) { return is(tag, itemStack.asItem()); } /** - * @return true if the item tag is present and contains this item's Java ID. + * @return true if the specified network ID is in the given holder set. */ - public boolean is(ItemTag tag, Item item) { - int[] values = this.items[tag.ordinal()]; - return contains(values, item.javaId()); + public boolean is(GeyserHolderSet holderSet, T object) { + return contains(holderSet.resolveRaw(this), holderSet.getRegistry().toNetworkId(session, object)); } - public int[] get(ItemTag itemTag) { - return this.items[itemTag.ordinal()]; + public List get(Tag tag) { + return mapRawArray(session, getRaw(tag), tag.registry()); } - public int[] get(EnchantmentTag enchantmentTag) { - return this.enchantments[enchantmentTag.ordinal()]; + /** + * @return the network IDs in the given tag. This can be an empty list. + */ + public int[] getRaw(Tag tag) { + return this.tags.getOrDefault(tag, IntArrays.EMPTY_ARRAY); + } + + /** + * Maps a raw array of network IDs to their respective objects. + */ + public static List mapRawArray(GeyserSession session, int[] array, JavaRegistryKey registry) { + return Arrays.stream(array).mapToObj(i -> registry.fromNetworkId(session, i)).toList(); } private static boolean contains(int[] array, int i) { diff --git a/core/src/main/java/org/geysermc/geyser/session/cache/TeleportCache.java b/core/src/main/java/org/geysermc/geyser/session/cache/TeleportCache.java index 80139a988..7a7a5f1e2 100644 --- a/core/src/main/java/org/geysermc/geyser/session/cache/TeleportCache.java +++ b/core/src/main/java/org/geysermc/geyser/session/cache/TeleportCache.java @@ -48,7 +48,7 @@ public class TeleportCache { /** * How many move packets the teleport can be unconfirmed for before it gets resent to the client */ - private static final int RESEND_THRESHOLD = 5; + private static final int RESEND_THRESHOLD = 20; // Make it one full second with auth input private final double x, y, z; private final float pitch, yaw; 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 86cb69314..609ac3f3b 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 @@ -31,14 +31,18 @@ import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap; import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap; import lombok.Getter; import lombok.Setter; +import net.kyori.adventure.key.Key; +import org.checkerframework.checker.nullness.qual.NonNull; import org.checkerframework.checker.nullness.qual.Nullable; import org.cloudburstmc.math.vector.Vector3i; import org.cloudburstmc.protocol.bedrock.packet.SetTitlePacket; -import org.geysermc.geyser.item.type.Item; +import org.geysermc.geyser.inventory.GeyserItemStack; import org.geysermc.geyser.scoreboard.Scoreboard; import org.geysermc.geyser.scoreboard.ScoreboardUpdater.ScoreboardSession; import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.util.ChunkUtils; +import org.geysermc.mcprotocollib.protocol.data.game.item.component.DataComponentType; +import org.geysermc.mcprotocollib.protocol.data.game.item.component.UseCooldown; import org.geysermc.mcprotocollib.protocol.data.game.setting.Difficulty; import java.util.Iterator; @@ -49,7 +53,7 @@ public final class WorldCache { @Getter private final ScoreboardSession scoreboardSession; @Getter - private Scoreboard scoreboard; + private @NonNull Scoreboard scoreboard; @Getter @Setter private Difficulty difficulty = Difficulty.EASY; @@ -71,7 +75,7 @@ public final class WorldCache { @Setter private boolean editingSignOnFront; - private final Object2IntMap activeCooldowns = new Object2IntOpenHashMap<>(2); + private final Object2IntMap activeCooldowns = new Object2IntOpenHashMap<>(2); public WorldCache(GeyserSession session) { this.session = session; @@ -80,11 +84,9 @@ public final class WorldCache { resetTitleTimes(false); } - public void removeScoreboard() { - if (scoreboard != null) { - scoreboard.removeScoreboard(); - scoreboard = new Scoreboard(session); - } + public void resetScoreboard() { + scoreboard.removeScoreboard(); + scoreboard = new Scoreboard(session); } public int increaseAndGetScoreboardPacketsPerSecond() { @@ -205,17 +207,24 @@ public final class WorldCache { return this.activeRecords.remove(pos); } - public void setCooldown(Item item, int ticks) { + public void setCooldown(Key cooldownGroup, int ticks) { if (ticks == 0) { // As of Java 1.21 - this.activeCooldowns.removeInt(item); + this.activeCooldowns.removeInt(cooldownGroup.asString()); return; } - this.activeCooldowns.put(item, session.getTicks() + ticks); + this.activeCooldowns.put(cooldownGroup.asString(), session.getTicks() + ticks); } - public boolean hasCooldown(Item item) { - return this.activeCooldowns.containsKey(item); + public boolean hasCooldown(GeyserItemStack item) { + UseCooldown cooldown = item.getComponent(DataComponentType.USE_COOLDOWN); + String cooldownGroup; + if (cooldown != null && cooldown.cooldownGroup() != null) { + cooldownGroup = cooldown.cooldownGroup().asString(); + } else { + cooldownGroup = item.asItem().javaIdentifier(); + } + return this.activeCooldowns.containsKey(cooldownGroup); } public void tick() { @@ -223,9 +232,9 @@ public final class WorldCache { // but we don't want the cooldown field to balloon in size from overuse. if (!this.activeCooldowns.isEmpty()) { int ticks = session.getTicks(); - Iterator> it = Object2IntMaps.fastIterator(this.activeCooldowns); + Iterator> it = Object2IntMaps.fastIterator(this.activeCooldowns); while (it.hasNext()) { - Object2IntMap.Entry entry = it.next(); + Object2IntMap.Entry entry = it.next(); if (entry.getIntValue() <= ticks) { it.remove(); } diff --git a/core/src/main/java/org/geysermc/geyser/session/cache/registry/JavaRegistries.java b/core/src/main/java/org/geysermc/geyser/session/cache/registry/JavaRegistries.java new file mode 100644 index 000000000..646b647d0 --- /dev/null +++ b/core/src/main/java/org/geysermc/geyser/session/cache/registry/JavaRegistries.java @@ -0,0 +1,87 @@ +/* + * Copyright (c) 2024 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.session.cache.registry; + +import net.kyori.adventure.key.Key; +import org.checkerframework.checker.nullness.qual.Nullable; +import org.geysermc.geyser.item.enchantment.Enchantment; +import org.geysermc.geyser.item.type.Item; +import org.geysermc.geyser.level.block.type.Block; +import org.geysermc.geyser.registry.BlockRegistries; +import org.geysermc.geyser.registry.ListRegistry; +import org.geysermc.geyser.registry.Registries; +import org.geysermc.geyser.session.cache.RegistryCache; +import org.geysermc.geyser.util.MinecraftKey; + +import java.util.ArrayList; +import java.util.List; + +/** + * Stores {@link JavaRegistryKey} for Java registries that are used for loading of data-driven objects, tags, or both. Read {@link JavaRegistryKey} for more information on how to use one. + */ +public class JavaRegistries { + private static final List> VALUES = new ArrayList<>(); + + public static final JavaRegistryKey BLOCK = create("block", BlockRegistries.JAVA_BLOCKS, Block::javaId); + public static final JavaRegistryKey ITEM = create("item", Registries.JAVA_ITEMS, Item::javaId); + public static final JavaRegistryKey ENCHANTMENT = create("enchantment", RegistryCache::enchantments); + + private static JavaRegistryKey create(String key, JavaRegistryKey.NetworkSerializer networkSerializer, JavaRegistryKey.NetworkDeserializer networkDeserializer) { + JavaRegistryKey registry = new JavaRegistryKey<>(MinecraftKey.key(key), networkSerializer, networkDeserializer); + VALUES.add(registry); + return registry; + } + + private static JavaRegistryKey create(String key, ListRegistry registry, RegistryNetworkMapper networkSerializer) { + return create(key, (session, object) -> networkSerializer.get(object), (session, id) -> registry.get(id)); + } + + private static JavaRegistryKey create(String key, RegistryGetter getter) { + return create(key, (session, object) -> getter.get(session.getRegistryCache()).byValue(object), (session, id) -> getter.get(session.getRegistryCache()).byId(id)); + } + + @Nullable + public static JavaRegistryKey fromKey(Key registryKey) { + for (JavaRegistryKey registry : VALUES) { + if (registry.registryKey().equals(registryKey)) { + return registry; + } + } + return null; + } + + @FunctionalInterface + interface RegistryGetter { + + JavaRegistry get(RegistryCache cache); + } + + @FunctionalInterface + interface RegistryNetworkMapper { + + int get(T object); + } +} diff --git a/core/src/main/java/org/geysermc/geyser/session/cache/registry/JavaRegistryKey.java b/core/src/main/java/org/geysermc/geyser/session/cache/registry/JavaRegistryKey.java new file mode 100644 index 000000000..369bea7a4 --- /dev/null +++ b/core/src/main/java/org/geysermc/geyser/session/cache/registry/JavaRegistryKey.java @@ -0,0 +1,86 @@ +/* + * Copyright (c) 2024 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.session.cache.registry; + +import net.kyori.adventure.key.Key; +import org.geysermc.geyser.session.GeyserSession; + +import javax.annotation.Nullable; + +/** + * Defines a Java registry, which can be hardcoded or data-driven. This class doesn't store registry contents itself, that is handled by {@link org.geysermc.geyser.session.cache.RegistryCache} in the case of + * data-driven registries and other classes in the case of hardcoded registries. + * + *

This class is used when, for a Java registry, data-driven objects or tags need to be loaded. Only one instance of this class should be created for each Java registry. Instances of this + * class are kept in {@link JavaRegistries}, which also has useful methods for creating instances of this class. When only using a registry to load data-driven objects, the network (de)serializer parameters + * can be null. For tag loading however, these are required, as {@link org.geysermc.geyser.session.cache.TagCache} relies on their functionality.

+ * + * @param registryKey the registry key, as it appears on Java. + * @param networkSerializer a method that converts an object in this registry to its network ID. + * @param networkDeserializer a method that converts a network ID to an object in this registry. + * @param the object type this registry holds. + */ +public record JavaRegistryKey(Key registryKey, @Nullable NetworkSerializer networkSerializer, @Nullable NetworkDeserializer networkDeserializer) { + + /** + * Converts an object in this registry to its network ID. This will fail if this registry doesn't have a network serializer. + */ + public int toNetworkId(GeyserSession session, T object) { + if (networkSerializer == null) { + throw new UnsupportedOperationException("Registry does not hava a network serializer"); + } + return networkSerializer.toNetworkId(session, object); + } + + /** + * Converts a network ID to an object in this registry. This will fail if this registry doesn't have a network deserializer. + */ + public T fromNetworkId(GeyserSession session, int networkId) { + if (networkDeserializer == null) { + throw new UnsupportedOperationException("Registry does not hava a network deserializer"); + } + return networkDeserializer.fromNetworkId(session, networkId); + } + + /** + * @return true if this registry has a network serializer and deserializer. + */ + public boolean shouldStoreTags() { + return networkSerializer != null && networkDeserializer != null; + } + + @FunctionalInterface + public interface NetworkSerializer { + + int toNetworkId(GeyserSession session, T object); + } + + @FunctionalInterface + public interface NetworkDeserializer { + + T fromNetworkId(GeyserSession session, int networkId); + } +} diff --git a/core/src/main/java/org/geysermc/geyser/session/cache/tags/BlockTag.java b/core/src/main/java/org/geysermc/geyser/session/cache/tags/BlockTag.java index 1dca7843a..6ad666780 100644 --- a/core/src/main/java/org/geysermc/geyser/session/cache/tags/BlockTag.java +++ b/core/src/main/java/org/geysermc/geyser/session/cache/tags/BlockTag.java @@ -25,214 +25,203 @@ package org.geysermc.geyser.session.cache.tags; -import net.kyori.adventure.key.Key; -import org.geysermc.geyser.util.Ordered; - -import java.util.HashMap; -import java.util.Map; +import org.geysermc.geyser.level.block.type.Block; +import org.geysermc.geyser.session.cache.registry.JavaRegistries; +import org.geysermc.geyser.util.MinecraftKey; +/** + * Lists vanilla block tags. + */ @SuppressWarnings("unused") -public final class BlockTag implements Ordered { - public static final Map ALL_BLOCK_TAGS = new HashMap<>(); +public final class BlockTag { + public static final Tag WOOL = create("wool"); + public static final Tag PLANKS = create("planks"); + public static final Tag STONE_BRICKS = create("stone_bricks"); + public static final Tag WOODEN_BUTTONS = create("wooden_buttons"); + public static final Tag STONE_BUTTONS = create("stone_buttons"); + public static final Tag BUTTONS = create("buttons"); + public static final Tag WOOL_CARPETS = create("wool_carpets"); + public static final Tag WOODEN_DOORS = create("wooden_doors"); + public static final Tag MOB_INTERACTABLE_DOORS = create("mob_interactable_doors"); + public static final Tag WOODEN_STAIRS = create("wooden_stairs"); + public static final Tag WOODEN_SLABS = create("wooden_slabs"); + public static final Tag WOODEN_FENCES = create("wooden_fences"); + public static final Tag PRESSURE_PLATES = create("pressure_plates"); + public static final Tag WOODEN_PRESSURE_PLATES = create("wooden_pressure_plates"); + public static final Tag STONE_PRESSURE_PLATES = create("stone_pressure_plates"); + public static final Tag WOODEN_TRAPDOORS = create("wooden_trapdoors"); + public static final Tag DOORS = create("doors"); + public static final Tag SAPLINGS = create("saplings"); + public static final Tag LOGS_THAT_BURN = create("logs_that_burn"); + public static final Tag OVERWORLD_NATURAL_LOGS = create("overworld_natural_logs"); + public static final Tag LOGS = create("logs"); + public static final Tag DARK_OAK_LOGS = create("dark_oak_logs"); + public static final Tag OAK_LOGS = create("oak_logs"); + public static final Tag BIRCH_LOGS = create("birch_logs"); + public static final Tag ACACIA_LOGS = create("acacia_logs"); + public static final Tag CHERRY_LOGS = create("cherry_logs"); + public static final Tag JUNGLE_LOGS = create("jungle_logs"); + public static final Tag SPRUCE_LOGS = create("spruce_logs"); + public static final Tag MANGROVE_LOGS = create("mangrove_logs"); + public static final Tag CRIMSON_STEMS = create("crimson_stems"); + public static final Tag WARPED_STEMS = create("warped_stems"); + public static final Tag BAMBOO_BLOCKS = create("bamboo_blocks"); + public static final Tag WART_BLOCKS = create("wart_blocks"); + public static final Tag BANNERS = create("banners"); + public static final Tag SAND = create("sand"); + public static final Tag SMELTS_TO_GLASS = create("smelts_to_glass"); + public static final Tag STAIRS = create("stairs"); + public static final Tag SLABS = create("slabs"); + public static final Tag WALLS = create("walls"); + public static final Tag ANVIL = create("anvil"); + public static final Tag RAILS = create("rails"); + public static final Tag LEAVES = create("leaves"); + public static final Tag TRAPDOORS = create("trapdoors"); + public static final Tag SMALL_FLOWERS = create("small_flowers"); + public static final Tag BEDS = create("beds"); + public static final Tag FENCES = create("fences"); + public static final Tag TALL_FLOWERS = create("tall_flowers"); + public static final Tag FLOWERS = create("flowers"); + public static final Tag PIGLIN_REPELLENTS = create("piglin_repellents"); + public static final Tag GOLD_ORES = create("gold_ores"); + public static final Tag IRON_ORES = create("iron_ores"); + public static final Tag DIAMOND_ORES = create("diamond_ores"); + public static final Tag REDSTONE_ORES = create("redstone_ores"); + public static final Tag LAPIS_ORES = create("lapis_ores"); + public static final Tag COAL_ORES = create("coal_ores"); + public static final Tag EMERALD_ORES = create("emerald_ores"); + public static final Tag COPPER_ORES = create("copper_ores"); + public static final Tag CANDLES = create("candles"); + public static final Tag DIRT = create("dirt"); + public static final Tag TERRACOTTA = create("terracotta"); + public static final Tag BADLANDS_TERRACOTTA = create("badlands_terracotta"); + public static final Tag CONCRETE_POWDER = create("concrete_powder"); + public static final Tag COMPLETES_FIND_TREE_TUTORIAL = create("completes_find_tree_tutorial"); + public static final Tag FLOWER_POTS = create("flower_pots"); + public static final Tag ENDERMAN_HOLDABLE = create("enderman_holdable"); + public static final Tag ICE = create("ice"); + public static final Tag VALID_SPAWN = create("valid_spawn"); + public static final Tag IMPERMEABLE = create("impermeable"); + public static final Tag UNDERWATER_BONEMEALS = create("underwater_bonemeals"); + public static final Tag CORAL_BLOCKS = create("coral_blocks"); + public static final Tag WALL_CORALS = create("wall_corals"); + public static final Tag CORAL_PLANTS = create("coral_plants"); + public static final Tag CORALS = create("corals"); + public static final Tag BAMBOO_PLANTABLE_ON = create("bamboo_plantable_on"); + public static final Tag STANDING_SIGNS = create("standing_signs"); + public static final Tag WALL_SIGNS = create("wall_signs"); + public static final Tag SIGNS = create("signs"); + public static final Tag CEILING_HANGING_SIGNS = create("ceiling_hanging_signs"); + public static final Tag WALL_HANGING_SIGNS = create("wall_hanging_signs"); + public static final Tag ALL_HANGING_SIGNS = create("all_hanging_signs"); + public static final Tag ALL_SIGNS = create("all_signs"); + public static final Tag DRAGON_IMMUNE = create("dragon_immune"); + public static final Tag DRAGON_TRANSPARENT = create("dragon_transparent"); + public static final Tag WITHER_IMMUNE = create("wither_immune"); + public static final Tag WITHER_SUMMON_BASE_BLOCKS = create("wither_summon_base_blocks"); + public static final Tag BEEHIVES = create("beehives"); + public static final Tag CROPS = create("crops"); + public static final Tag BEE_GROWABLES = create("bee_growables"); + public static final Tag PORTALS = create("portals"); + public static final Tag FIRE = create("fire"); + public static final Tag NYLIUM = create("nylium"); + public static final Tag BEACON_BASE_BLOCKS = create("beacon_base_blocks"); + public static final Tag SOUL_SPEED_BLOCKS = create("soul_speed_blocks"); + public static final Tag WALL_POST_OVERRIDE = create("wall_post_override"); + public static final Tag CLIMBABLE = create("climbable"); + public static final Tag FALL_DAMAGE_RESETTING = create("fall_damage_resetting"); + public static final Tag SHULKER_BOXES = create("shulker_boxes"); + public static final Tag HOGLIN_REPELLENTS = create("hoglin_repellents"); + public static final Tag SOUL_FIRE_BASE_BLOCKS = create("soul_fire_base_blocks"); + public static final Tag STRIDER_WARM_BLOCKS = create("strider_warm_blocks"); + public static final Tag CAMPFIRES = create("campfires"); + public static final Tag GUARDED_BY_PIGLINS = create("guarded_by_piglins"); + public static final Tag PREVENT_MOB_SPAWNING_INSIDE = create("prevent_mob_spawning_inside"); + public static final Tag FENCE_GATES = create("fence_gates"); + public static final Tag UNSTABLE_BOTTOM_CENTER = create("unstable_bottom_center"); + public static final Tag MUSHROOM_GROW_BLOCK = create("mushroom_grow_block"); + public static final Tag INFINIBURN_OVERWORLD = create("infiniburn_overworld"); + public static final Tag INFINIBURN_NETHER = create("infiniburn_nether"); + public static final Tag INFINIBURN_END = create("infiniburn_end"); + public static final Tag BASE_STONE_OVERWORLD = create("base_stone_overworld"); + public static final Tag STONE_ORE_REPLACEABLES = create("stone_ore_replaceables"); + public static final Tag DEEPSLATE_ORE_REPLACEABLES = create("deepslate_ore_replaceables"); + public static final Tag BASE_STONE_NETHER = create("base_stone_nether"); + public static final Tag OVERWORLD_CARVER_REPLACEABLES = create("overworld_carver_replaceables"); + public static final Tag NETHER_CARVER_REPLACEABLES = create("nether_carver_replaceables"); + public static final Tag CANDLE_CAKES = create("candle_cakes"); + public static final Tag CAULDRONS = create("cauldrons"); + public static final Tag CRYSTAL_SOUND_BLOCKS = create("crystal_sound_blocks"); + public static final Tag INSIDE_STEP_SOUND_BLOCKS = create("inside_step_sound_blocks"); + public static final Tag COMBINATION_STEP_SOUND_BLOCKS = create("combination_step_sound_blocks"); + public static final Tag CAMEL_SAND_STEP_SOUND_BLOCKS = create("camel_sand_step_sound_blocks"); + public static final Tag OCCLUDES_VIBRATION_SIGNALS = create("occludes_vibration_signals"); + public static final Tag DAMPENS_VIBRATIONS = create("dampens_vibrations"); + public static final Tag DRIPSTONE_REPLACEABLE_BLOCKS = create("dripstone_replaceable_blocks"); + public static final Tag CAVE_VINES = create("cave_vines"); + public static final Tag MOSS_REPLACEABLE = create("moss_replaceable"); + public static final Tag LUSH_GROUND_REPLACEABLE = create("lush_ground_replaceable"); + public static final Tag AZALEA_ROOT_REPLACEABLE = create("azalea_root_replaceable"); + public static final Tag SMALL_DRIPLEAF_PLACEABLE = create("small_dripleaf_placeable"); + public static final Tag BIG_DRIPLEAF_PLACEABLE = create("big_dripleaf_placeable"); + public static final Tag SNOW = create("snow"); + public static final Tag MINEABLE_AXE = create("mineable/axe"); + public static final Tag MINEABLE_HOE = create("mineable/hoe"); + public static final Tag MINEABLE_PICKAXE = create("mineable/pickaxe"); + public static final Tag MINEABLE_SHOVEL = create("mineable/shovel"); + public static final Tag SWORD_EFFICIENT = create("sword_efficient"); + public static final Tag NEEDS_DIAMOND_TOOL = create("needs_diamond_tool"); + public static final Tag NEEDS_IRON_TOOL = create("needs_iron_tool"); + public static final Tag NEEDS_STONE_TOOL = create("needs_stone_tool"); + public static final Tag INCORRECT_FOR_NETHERITE_TOOL = create("incorrect_for_netherite_tool"); + public static final Tag INCORRECT_FOR_DIAMOND_TOOL = create("incorrect_for_diamond_tool"); + public static final Tag INCORRECT_FOR_IRON_TOOL = create("incorrect_for_iron_tool"); + public static final Tag INCORRECT_FOR_STONE_TOOL = create("incorrect_for_stone_tool"); + public static final Tag INCORRECT_FOR_GOLD_TOOL = create("incorrect_for_gold_tool"); + public static final Tag INCORRECT_FOR_WOODEN_TOOL = create("incorrect_for_wooden_tool"); + public static final Tag FEATURES_CANNOT_REPLACE = create("features_cannot_replace"); + public static final Tag LAVA_POOL_STONE_CANNOT_REPLACE = create("lava_pool_stone_cannot_replace"); + public static final Tag GEODE_INVALID_BLOCKS = create("geode_invalid_blocks"); + public static final Tag FROG_PREFER_JUMP_TO = create("frog_prefer_jump_to"); + public static final Tag SCULK_REPLACEABLE = create("sculk_replaceable"); + public static final Tag SCULK_REPLACEABLE_WORLD_GEN = create("sculk_replaceable_world_gen"); + public static final Tag ANCIENT_CITY_REPLACEABLE = create("ancient_city_replaceable"); + public static final Tag VIBRATION_RESONATORS = create("vibration_resonators"); + public static final Tag ANIMALS_SPAWNABLE_ON = create("animals_spawnable_on"); + public static final Tag ARMADILLO_SPAWNABLE_ON = create("armadillo_spawnable_on"); + public static final Tag AXOLOTLS_SPAWNABLE_ON = create("axolotls_spawnable_on"); + public static final Tag GOATS_SPAWNABLE_ON = create("goats_spawnable_on"); + public static final Tag MOOSHROOMS_SPAWNABLE_ON = create("mooshrooms_spawnable_on"); + public static final Tag PARROTS_SPAWNABLE_ON = create("parrots_spawnable_on"); + public static final Tag POLAR_BEARS_SPAWNABLE_ON_ALTERNATE = create("polar_bears_spawnable_on_alternate"); + public static final Tag RABBITS_SPAWNABLE_ON = create("rabbits_spawnable_on"); + public static final Tag FOXES_SPAWNABLE_ON = create("foxes_spawnable_on"); + public static final Tag WOLVES_SPAWNABLE_ON = create("wolves_spawnable_on"); + public static final Tag FROGS_SPAWNABLE_ON = create("frogs_spawnable_on"); + public static final Tag AZALEA_GROWS_ON = create("azalea_grows_on"); + public static final Tag CONVERTABLE_TO_MUD = create("convertable_to_mud"); + public static final Tag MANGROVE_LOGS_CAN_GROW_THROUGH = create("mangrove_logs_can_grow_through"); + public static final Tag MANGROVE_ROOTS_CAN_GROW_THROUGH = create("mangrove_roots_can_grow_through"); + public static final Tag DEAD_BUSH_MAY_PLACE_ON = create("dead_bush_may_place_on"); + public static final Tag SNAPS_GOAT_HORN = create("snaps_goat_horn"); + public static final Tag REPLACEABLE_BY_TREES = create("replaceable_by_trees"); + public static final Tag SNOW_LAYER_CANNOT_SURVIVE_ON = create("snow_layer_cannot_survive_on"); + public static final Tag SNOW_LAYER_CAN_SURVIVE_ON = create("snow_layer_can_survive_on"); + public static final Tag INVALID_SPAWN_INSIDE = create("invalid_spawn_inside"); + public static final Tag SNIFFER_DIGGABLE_BLOCK = create("sniffer_diggable_block"); + public static final Tag SNIFFER_EGG_HATCH_BOOST = create("sniffer_egg_hatch_boost"); + public static final Tag TRAIL_RUINS_REPLACEABLE = create("trail_ruins_replaceable"); + public static final Tag REPLACEABLE = create("replaceable"); + public static final Tag ENCHANTMENT_POWER_PROVIDER = create("enchantment_power_provider"); + public static final Tag ENCHANTMENT_POWER_TRANSMITTER = create("enchantment_power_transmitter"); + public static final Tag MAINTAINS_FARMLAND = create("maintains_farmland"); + public static final Tag BLOCKS_WIND_CHARGE_EXPLOSIONS = create("blocks_wind_charge_explosions"); + public static final Tag DOES_NOT_BLOCK_HOPPERS = create("does_not_block_hoppers"); + public static final Tag AIR = create("air"); - public static final BlockTag WOOL = new BlockTag("wool"); - public static final BlockTag PLANKS = new BlockTag("planks"); - public static final BlockTag STONE_BRICKS = new BlockTag("stone_bricks"); - public static final BlockTag WOODEN_BUTTONS = new BlockTag("wooden_buttons"); - public static final BlockTag STONE_BUTTONS = new BlockTag("stone_buttons"); - public static final BlockTag BUTTONS = new BlockTag("buttons"); - public static final BlockTag WOOL_CARPETS = new BlockTag("wool_carpets"); - public static final BlockTag WOODEN_DOORS = new BlockTag("wooden_doors"); - public static final BlockTag MOB_INTERACTABLE_DOORS = new BlockTag("mob_interactable_doors"); - public static final BlockTag WOODEN_STAIRS = new BlockTag("wooden_stairs"); - public static final BlockTag WOODEN_SLABS = new BlockTag("wooden_slabs"); - public static final BlockTag WOODEN_FENCES = new BlockTag("wooden_fences"); - public static final BlockTag PRESSURE_PLATES = new BlockTag("pressure_plates"); - public static final BlockTag WOODEN_PRESSURE_PLATES = new BlockTag("wooden_pressure_plates"); - public static final BlockTag STONE_PRESSURE_PLATES = new BlockTag("stone_pressure_plates"); - public static final BlockTag WOODEN_TRAPDOORS = new BlockTag("wooden_trapdoors"); - public static final BlockTag DOORS = new BlockTag("doors"); - public static final BlockTag SAPLINGS = new BlockTag("saplings"); - public static final BlockTag LOGS_THAT_BURN = new BlockTag("logs_that_burn"); - public static final BlockTag OVERWORLD_NATURAL_LOGS = new BlockTag("overworld_natural_logs"); - public static final BlockTag LOGS = new BlockTag("logs"); - public static final BlockTag DARK_OAK_LOGS = new BlockTag("dark_oak_logs"); - public static final BlockTag OAK_LOGS = new BlockTag("oak_logs"); - public static final BlockTag BIRCH_LOGS = new BlockTag("birch_logs"); - public static final BlockTag ACACIA_LOGS = new BlockTag("acacia_logs"); - public static final BlockTag CHERRY_LOGS = new BlockTag("cherry_logs"); - public static final BlockTag JUNGLE_LOGS = new BlockTag("jungle_logs"); - public static final BlockTag SPRUCE_LOGS = new BlockTag("spruce_logs"); - public static final BlockTag MANGROVE_LOGS = new BlockTag("mangrove_logs"); - public static final BlockTag CRIMSON_STEMS = new BlockTag("crimson_stems"); - public static final BlockTag WARPED_STEMS = new BlockTag("warped_stems"); - public static final BlockTag BAMBOO_BLOCKS = new BlockTag("bamboo_blocks"); - public static final BlockTag WART_BLOCKS = new BlockTag("wart_blocks"); - public static final BlockTag BANNERS = new BlockTag("banners"); - public static final BlockTag SAND = new BlockTag("sand"); - public static final BlockTag SMELTS_TO_GLASS = new BlockTag("smelts_to_glass"); - public static final BlockTag STAIRS = new BlockTag("stairs"); - public static final BlockTag SLABS = new BlockTag("slabs"); - public static final BlockTag WALLS = new BlockTag("walls"); - public static final BlockTag ANVIL = new BlockTag("anvil"); - public static final BlockTag RAILS = new BlockTag("rails"); - public static final BlockTag LEAVES = new BlockTag("leaves"); - public static final BlockTag TRAPDOORS = new BlockTag("trapdoors"); - public static final BlockTag SMALL_FLOWERS = new BlockTag("small_flowers"); - public static final BlockTag BEDS = new BlockTag("beds"); - public static final BlockTag FENCES = new BlockTag("fences"); - public static final BlockTag TALL_FLOWERS = new BlockTag("tall_flowers"); - public static final BlockTag FLOWERS = new BlockTag("flowers"); - public static final BlockTag PIGLIN_REPELLENTS = new BlockTag("piglin_repellents"); - public static final BlockTag GOLD_ORES = new BlockTag("gold_ores"); - public static final BlockTag IRON_ORES = new BlockTag("iron_ores"); - public static final BlockTag DIAMOND_ORES = new BlockTag("diamond_ores"); - public static final BlockTag REDSTONE_ORES = new BlockTag("redstone_ores"); - public static final BlockTag LAPIS_ORES = new BlockTag("lapis_ores"); - public static final BlockTag COAL_ORES = new BlockTag("coal_ores"); - public static final BlockTag EMERALD_ORES = new BlockTag("emerald_ores"); - public static final BlockTag COPPER_ORES = new BlockTag("copper_ores"); - public static final BlockTag CANDLES = new BlockTag("candles"); - public static final BlockTag DIRT = new BlockTag("dirt"); - public static final BlockTag TERRACOTTA = new BlockTag("terracotta"); - public static final BlockTag BADLANDS_TERRACOTTA = new BlockTag("badlands_terracotta"); - public static final BlockTag CONCRETE_POWDER = new BlockTag("concrete_powder"); - public static final BlockTag COMPLETES_FIND_TREE_TUTORIAL = new BlockTag("completes_find_tree_tutorial"); - public static final BlockTag FLOWER_POTS = new BlockTag("flower_pots"); - public static final BlockTag ENDERMAN_HOLDABLE = new BlockTag("enderman_holdable"); - public static final BlockTag ICE = new BlockTag("ice"); - public static final BlockTag VALID_SPAWN = new BlockTag("valid_spawn"); - public static final BlockTag IMPERMEABLE = new BlockTag("impermeable"); - public static final BlockTag UNDERWATER_BONEMEALS = new BlockTag("underwater_bonemeals"); - public static final BlockTag CORAL_BLOCKS = new BlockTag("coral_blocks"); - public static final BlockTag WALL_CORALS = new BlockTag("wall_corals"); - public static final BlockTag CORAL_PLANTS = new BlockTag("coral_plants"); - public static final BlockTag CORALS = new BlockTag("corals"); - public static final BlockTag BAMBOO_PLANTABLE_ON = new BlockTag("bamboo_plantable_on"); - public static final BlockTag STANDING_SIGNS = new BlockTag("standing_signs"); - public static final BlockTag WALL_SIGNS = new BlockTag("wall_signs"); - public static final BlockTag SIGNS = new BlockTag("signs"); - public static final BlockTag CEILING_HANGING_SIGNS = new BlockTag("ceiling_hanging_signs"); - public static final BlockTag WALL_HANGING_SIGNS = new BlockTag("wall_hanging_signs"); - public static final BlockTag ALL_HANGING_SIGNS = new BlockTag("all_hanging_signs"); - public static final BlockTag ALL_SIGNS = new BlockTag("all_signs"); - public static final BlockTag DRAGON_IMMUNE = new BlockTag("dragon_immune"); - public static final BlockTag DRAGON_TRANSPARENT = new BlockTag("dragon_transparent"); - public static final BlockTag WITHER_IMMUNE = new BlockTag("wither_immune"); - public static final BlockTag WITHER_SUMMON_BASE_BLOCKS = new BlockTag("wither_summon_base_blocks"); - public static final BlockTag BEEHIVES = new BlockTag("beehives"); - public static final BlockTag CROPS = new BlockTag("crops"); - public static final BlockTag BEE_GROWABLES = new BlockTag("bee_growables"); - public static final BlockTag PORTALS = new BlockTag("portals"); - public static final BlockTag FIRE = new BlockTag("fire"); - public static final BlockTag NYLIUM = new BlockTag("nylium"); - public static final BlockTag BEACON_BASE_BLOCKS = new BlockTag("beacon_base_blocks"); - public static final BlockTag SOUL_SPEED_BLOCKS = new BlockTag("soul_speed_blocks"); - public static final BlockTag WALL_POST_OVERRIDE = new BlockTag("wall_post_override"); - public static final BlockTag CLIMBABLE = new BlockTag("climbable"); - public static final BlockTag FALL_DAMAGE_RESETTING = new BlockTag("fall_damage_resetting"); - public static final BlockTag SHULKER_BOXES = new BlockTag("shulker_boxes"); - public static final BlockTag HOGLIN_REPELLENTS = new BlockTag("hoglin_repellents"); - public static final BlockTag SOUL_FIRE_BASE_BLOCKS = new BlockTag("soul_fire_base_blocks"); - public static final BlockTag STRIDER_WARM_BLOCKS = new BlockTag("strider_warm_blocks"); - public static final BlockTag CAMPFIRES = new BlockTag("campfires"); - public static final BlockTag GUARDED_BY_PIGLINS = new BlockTag("guarded_by_piglins"); - public static final BlockTag PREVENT_MOB_SPAWNING_INSIDE = new BlockTag("prevent_mob_spawning_inside"); - public static final BlockTag FENCE_GATES = new BlockTag("fence_gates"); - public static final BlockTag UNSTABLE_BOTTOM_CENTER = new BlockTag("unstable_bottom_center"); - public static final BlockTag MUSHROOM_GROW_BLOCK = new BlockTag("mushroom_grow_block"); - public static final BlockTag INFINIBURN_OVERWORLD = new BlockTag("infiniburn_overworld"); - public static final BlockTag INFINIBURN_NETHER = new BlockTag("infiniburn_nether"); - public static final BlockTag INFINIBURN_END = new BlockTag("infiniburn_end"); - public static final BlockTag BASE_STONE_OVERWORLD = new BlockTag("base_stone_overworld"); - public static final BlockTag STONE_ORE_REPLACEABLES = new BlockTag("stone_ore_replaceables"); - public static final BlockTag DEEPSLATE_ORE_REPLACEABLES = new BlockTag("deepslate_ore_replaceables"); - public static final BlockTag BASE_STONE_NETHER = new BlockTag("base_stone_nether"); - public static final BlockTag OVERWORLD_CARVER_REPLACEABLES = new BlockTag("overworld_carver_replaceables"); - public static final BlockTag NETHER_CARVER_REPLACEABLES = new BlockTag("nether_carver_replaceables"); - public static final BlockTag CANDLE_CAKES = new BlockTag("candle_cakes"); - public static final BlockTag CAULDRONS = new BlockTag("cauldrons"); - public static final BlockTag CRYSTAL_SOUND_BLOCKS = new BlockTag("crystal_sound_blocks"); - public static final BlockTag INSIDE_STEP_SOUND_BLOCKS = new BlockTag("inside_step_sound_blocks"); - public static final BlockTag COMBINATION_STEP_SOUND_BLOCKS = new BlockTag("combination_step_sound_blocks"); - public static final BlockTag CAMEL_SAND_STEP_SOUND_BLOCKS = new BlockTag("camel_sand_step_sound_blocks"); - public static final BlockTag OCCLUDES_VIBRATION_SIGNALS = new BlockTag("occludes_vibration_signals"); - public static final BlockTag DAMPENS_VIBRATIONS = new BlockTag("dampens_vibrations"); - public static final BlockTag DRIPSTONE_REPLACEABLE_BLOCKS = new BlockTag("dripstone_replaceable_blocks"); - public static final BlockTag CAVE_VINES = new BlockTag("cave_vines"); - public static final BlockTag MOSS_REPLACEABLE = new BlockTag("moss_replaceable"); - public static final BlockTag LUSH_GROUND_REPLACEABLE = new BlockTag("lush_ground_replaceable"); - public static final BlockTag AZALEA_ROOT_REPLACEABLE = new BlockTag("azalea_root_replaceable"); - public static final BlockTag SMALL_DRIPLEAF_PLACEABLE = new BlockTag("small_dripleaf_placeable"); - public static final BlockTag BIG_DRIPLEAF_PLACEABLE = new BlockTag("big_dripleaf_placeable"); - public static final BlockTag SNOW = new BlockTag("snow"); - public static final BlockTag MINEABLE_AXE = new BlockTag("mineable/axe"); - public static final BlockTag MINEABLE_HOE = new BlockTag("mineable/hoe"); - public static final BlockTag MINEABLE_PICKAXE = new BlockTag("mineable/pickaxe"); - public static final BlockTag MINEABLE_SHOVEL = new BlockTag("mineable/shovel"); - public static final BlockTag SWORD_EFFICIENT = new BlockTag("sword_efficient"); - public static final BlockTag NEEDS_DIAMOND_TOOL = new BlockTag("needs_diamond_tool"); - public static final BlockTag NEEDS_IRON_TOOL = new BlockTag("needs_iron_tool"); - public static final BlockTag NEEDS_STONE_TOOL = new BlockTag("needs_stone_tool"); - public static final BlockTag INCORRECT_FOR_NETHERITE_TOOL = new BlockTag("incorrect_for_netherite_tool"); - public static final BlockTag INCORRECT_FOR_DIAMOND_TOOL = new BlockTag("incorrect_for_diamond_tool"); - public static final BlockTag INCORRECT_FOR_IRON_TOOL = new BlockTag("incorrect_for_iron_tool"); - public static final BlockTag INCORRECT_FOR_STONE_TOOL = new BlockTag("incorrect_for_stone_tool"); - public static final BlockTag INCORRECT_FOR_GOLD_TOOL = new BlockTag("incorrect_for_gold_tool"); - public static final BlockTag INCORRECT_FOR_WOODEN_TOOL = new BlockTag("incorrect_for_wooden_tool"); - public static final BlockTag FEATURES_CANNOT_REPLACE = new BlockTag("features_cannot_replace"); - public static final BlockTag LAVA_POOL_STONE_CANNOT_REPLACE = new BlockTag("lava_pool_stone_cannot_replace"); - public static final BlockTag GEODE_INVALID_BLOCKS = new BlockTag("geode_invalid_blocks"); - public static final BlockTag FROG_PREFER_JUMP_TO = new BlockTag("frog_prefer_jump_to"); - public static final BlockTag SCULK_REPLACEABLE = new BlockTag("sculk_replaceable"); - public static final BlockTag SCULK_REPLACEABLE_WORLD_GEN = new BlockTag("sculk_replaceable_world_gen"); - public static final BlockTag ANCIENT_CITY_REPLACEABLE = new BlockTag("ancient_city_replaceable"); - public static final BlockTag VIBRATION_RESONATORS = new BlockTag("vibration_resonators"); - public static final BlockTag ANIMALS_SPAWNABLE_ON = new BlockTag("animals_spawnable_on"); - public static final BlockTag ARMADILLO_SPAWNABLE_ON = new BlockTag("armadillo_spawnable_on"); - public static final BlockTag AXOLOTLS_SPAWNABLE_ON = new BlockTag("axolotls_spawnable_on"); - public static final BlockTag GOATS_SPAWNABLE_ON = new BlockTag("goats_spawnable_on"); - public static final BlockTag MOOSHROOMS_SPAWNABLE_ON = new BlockTag("mooshrooms_spawnable_on"); - public static final BlockTag PARROTS_SPAWNABLE_ON = new BlockTag("parrots_spawnable_on"); - public static final BlockTag POLAR_BEARS_SPAWNABLE_ON_ALTERNATE = new BlockTag("polar_bears_spawnable_on_alternate"); - public static final BlockTag RABBITS_SPAWNABLE_ON = new BlockTag("rabbits_spawnable_on"); - public static final BlockTag FOXES_SPAWNABLE_ON = new BlockTag("foxes_spawnable_on"); - public static final BlockTag WOLVES_SPAWNABLE_ON = new BlockTag("wolves_spawnable_on"); - public static final BlockTag FROGS_SPAWNABLE_ON = new BlockTag("frogs_spawnable_on"); - public static final BlockTag AZALEA_GROWS_ON = new BlockTag("azalea_grows_on"); - public static final BlockTag CONVERTABLE_TO_MUD = new BlockTag("convertable_to_mud"); - public static final BlockTag MANGROVE_LOGS_CAN_GROW_THROUGH = new BlockTag("mangrove_logs_can_grow_through"); - public static final BlockTag MANGROVE_ROOTS_CAN_GROW_THROUGH = new BlockTag("mangrove_roots_can_grow_through"); - public static final BlockTag DEAD_BUSH_MAY_PLACE_ON = new BlockTag("dead_bush_may_place_on"); - public static final BlockTag SNAPS_GOAT_HORN = new BlockTag("snaps_goat_horn"); - public static final BlockTag REPLACEABLE_BY_TREES = new BlockTag("replaceable_by_trees"); - public static final BlockTag SNOW_LAYER_CANNOT_SURVIVE_ON = new BlockTag("snow_layer_cannot_survive_on"); - public static final BlockTag SNOW_LAYER_CAN_SURVIVE_ON = new BlockTag("snow_layer_can_survive_on"); - public static final BlockTag INVALID_SPAWN_INSIDE = new BlockTag("invalid_spawn_inside"); - public static final BlockTag SNIFFER_DIGGABLE_BLOCK = new BlockTag("sniffer_diggable_block"); - public static final BlockTag SNIFFER_EGG_HATCH_BOOST = new BlockTag("sniffer_egg_hatch_boost"); - public static final BlockTag TRAIL_RUINS_REPLACEABLE = new BlockTag("trail_ruins_replaceable"); - public static final BlockTag REPLACEABLE = new BlockTag("replaceable"); - public static final BlockTag ENCHANTMENT_POWER_PROVIDER = new BlockTag("enchantment_power_provider"); - public static final BlockTag ENCHANTMENT_POWER_TRANSMITTER = new BlockTag("enchantment_power_transmitter"); - public static final BlockTag MAINTAINS_FARMLAND = new BlockTag("maintains_farmland"); - public static final BlockTag BLOCKS_WIND_CHARGE_EXPLOSIONS = new BlockTag("blocks_wind_charge_explosions"); - public static final BlockTag DOES_NOT_BLOCK_HOPPERS = new BlockTag("does_not_block_hoppers"); - public static final BlockTag AIR = new BlockTag("air"); + private BlockTag() {} - private final int id; - - private BlockTag(String identifier) { - this.id = ALL_BLOCK_TAGS.size(); - register(identifier, this); - } - - @Override - public int ordinal() { - return id; - } - - private static void register(String name, BlockTag tag) { - ALL_BLOCK_TAGS.put(Key.key(name), tag); + private static Tag create(String name) { + return new Tag<>(JavaRegistries.BLOCK, MinecraftKey.key(name)); } } diff --git a/core/src/main/java/org/geysermc/geyser/session/cache/tags/EnchantmentTag.java b/core/src/main/java/org/geysermc/geyser/session/cache/tags/EnchantmentTag.java index 0af690abd..1ef875642 100644 --- a/core/src/main/java/org/geysermc/geyser/session/cache/tags/EnchantmentTag.java +++ b/core/src/main/java/org/geysermc/geyser/session/cache/tags/EnchantmentTag.java @@ -25,67 +25,55 @@ package org.geysermc.geyser.session.cache.tags; -import net.kyori.adventure.key.Key; +import org.geysermc.geyser.item.enchantment.Enchantment; +import org.geysermc.geyser.session.cache.registry.JavaRegistries; import org.geysermc.geyser.util.MinecraftKey; -import org.geysermc.geyser.util.Ordered; - -import java.util.HashMap; -import java.util.Map; +/** + * Lists vanilla enchantment tags. + */ @SuppressWarnings("unused") -public final class EnchantmentTag implements Ordered { - public static final Map ALL_ENCHANTMENT_TAGS = new HashMap<>(); +public final class EnchantmentTag { + public static final Tag TOOLTIP_ORDER = create("tooltip_order"); + public static final Tag EXCLUSIVE_SET_ARMOR = create("exclusive_set/armor"); + public static final Tag EXCLUSIVE_SET_BOOTS = create("exclusive_set/boots"); + public static final Tag EXCLUSIVE_SET_BOW = create("exclusive_set/bow"); + public static final Tag EXCLUSIVE_SET_CROSSBOW = create("exclusive_set/crossbow"); + public static final Tag EXCLUSIVE_SET_DAMAGE = create("exclusive_set/damage"); + public static final Tag EXCLUSIVE_SET_MINING = create("exclusive_set/mining"); + public static final Tag EXCLUSIVE_SET_RIPTIDE = create("exclusive_set/riptide"); + public static final Tag TRADEABLE = create("tradeable"); + public static final Tag DOUBLE_TRADE_PRICE = create("double_trade_price"); + public static final Tag IN_ENCHANTING_TABLE = create("in_enchanting_table"); + public static final Tag ON_MOB_SPAWN_EQUIPMENT = create("on_mob_spawn_equipment"); + public static final Tag ON_TRADED_EQUIPMENT = create("on_traded_equipment"); + public static final Tag ON_RANDOM_LOOT = create("on_random_loot"); + public static final Tag CURSE = create("curse"); + public static final Tag SMELTS_LOOT = create("smelts_loot"); + public static final Tag PREVENTS_BEE_SPAWNS_WHEN_MINING = create("prevents_bee_spawns_when_mining"); + public static final Tag PREVENTS_DECORATED_POT_SHATTERING = create("prevents_decorated_pot_shattering"); + public static final Tag PREVENTS_ICE_MELTING = create("prevents_ice_melting"); + public static final Tag PREVENTS_INFESTED_SPAWNS = create("prevents_infested_spawns"); + public static final Tag TREASURE = create("treasure"); + public static final Tag NON_TREASURE = create("non_treasure"); + public static final Tag TRADES_DESERT_COMMON = create("trades/desert_common"); + public static final Tag TRADES_JUNGLE_COMMON = create("trades/jungle_common"); + public static final Tag TRADES_PLAINS_COMMON = create("trades/plains_common"); + public static final Tag TRADES_SAVANNA_COMMON = create("trades/savanna_common"); + public static final Tag TRADES_SNOW_COMMON = create("trades/snow_common"); + public static final Tag TRADES_SWAMP_COMMON = create("trades/swamp_common"); + public static final Tag TRADES_TAIGA_COMMON = create("trades/taiga_common"); + public static final Tag TRADES_DESERT_SPECIAL = create("trades/desert_special"); + public static final Tag TRADES_JUNGLE_SPECIAL = create("trades/jungle_special"); + public static final Tag TRADES_PLAINS_SPECIAL = create("trades/plains_special"); + public static final Tag TRADES_SAVANNA_SPECIAL = create("trades/savanna_special"); + public static final Tag TRADES_SNOW_SPECIAL = create("trades/snow_special"); + public static final Tag TRADES_SWAMP_SPECIAL = create("trades/swamp_special"); + public static final Tag TRADES_TAIGA_SPECIAL = create("trades/taiga_special"); - public static final EnchantmentTag TOOLTIP_ORDER = new EnchantmentTag("tooltip_order"); - public static final EnchantmentTag EXCLUSIVE_SET_ARMOR = new EnchantmentTag("exclusive_set/armor"); - public static final EnchantmentTag EXCLUSIVE_SET_BOOTS = new EnchantmentTag("exclusive_set/boots"); - public static final EnchantmentTag EXCLUSIVE_SET_BOW = new EnchantmentTag("exclusive_set/bow"); - public static final EnchantmentTag EXCLUSIVE_SET_CROSSBOW = new EnchantmentTag("exclusive_set/crossbow"); - public static final EnchantmentTag EXCLUSIVE_SET_DAMAGE = new EnchantmentTag("exclusive_set/damage"); - public static final EnchantmentTag EXCLUSIVE_SET_MINING = new EnchantmentTag("exclusive_set/mining"); - public static final EnchantmentTag EXCLUSIVE_SET_RIPTIDE = new EnchantmentTag("exclusive_set/riptide"); - public static final EnchantmentTag TRADEABLE = new EnchantmentTag("tradeable"); - public static final EnchantmentTag DOUBLE_TRADE_PRICE = new EnchantmentTag("double_trade_price"); - public static final EnchantmentTag IN_ENCHANTING_TABLE = new EnchantmentTag("in_enchanting_table"); - public static final EnchantmentTag ON_MOB_SPAWN_EQUIPMENT = new EnchantmentTag("on_mob_spawn_equipment"); - public static final EnchantmentTag ON_TRADED_EQUIPMENT = new EnchantmentTag("on_traded_equipment"); - public static final EnchantmentTag ON_RANDOM_LOOT = new EnchantmentTag("on_random_loot"); - public static final EnchantmentTag CURSE = new EnchantmentTag("curse"); - public static final EnchantmentTag SMELTS_LOOT = new EnchantmentTag("smelts_loot"); - public static final EnchantmentTag PREVENTS_BEE_SPAWNS_WHEN_MINING = new EnchantmentTag("prevents_bee_spawns_when_mining"); - public static final EnchantmentTag PREVENTS_DECORATED_POT_SHATTERING = new EnchantmentTag("prevents_decorated_pot_shattering"); - public static final EnchantmentTag PREVENTS_ICE_MELTING = new EnchantmentTag("prevents_ice_melting"); - public static final EnchantmentTag PREVENTS_INFESTED_SPAWNS = new EnchantmentTag("prevents_infested_spawns"); - public static final EnchantmentTag TREASURE = new EnchantmentTag("treasure"); - public static final EnchantmentTag NON_TREASURE = new EnchantmentTag("non_treasure"); - public static final EnchantmentTag TRADES_DESERT_COMMON = new EnchantmentTag("trades/desert_common"); - public static final EnchantmentTag TRADES_JUNGLE_COMMON = new EnchantmentTag("trades/jungle_common"); - public static final EnchantmentTag TRADES_PLAINS_COMMON = new EnchantmentTag("trades/plains_common"); - public static final EnchantmentTag TRADES_SAVANNA_COMMON = new EnchantmentTag("trades/savanna_common"); - public static final EnchantmentTag TRADES_SNOW_COMMON = new EnchantmentTag("trades/snow_common"); - public static final EnchantmentTag TRADES_SWAMP_COMMON = new EnchantmentTag("trades/swamp_common"); - public static final EnchantmentTag TRADES_TAIGA_COMMON = new EnchantmentTag("trades/taiga_common"); - public static final EnchantmentTag TRADES_DESERT_SPECIAL = new EnchantmentTag("trades/desert_special"); - public static final EnchantmentTag TRADES_JUNGLE_SPECIAL = new EnchantmentTag("trades/jungle_special"); - public static final EnchantmentTag TRADES_PLAINS_SPECIAL = new EnchantmentTag("trades/plains_special"); - public static final EnchantmentTag TRADES_SAVANNA_SPECIAL = new EnchantmentTag("trades/savanna_special"); - public static final EnchantmentTag TRADES_SNOW_SPECIAL = new EnchantmentTag("trades/snow_special"); - public static final EnchantmentTag TRADES_SWAMP_SPECIAL = new EnchantmentTag("trades/swamp_special"); - public static final EnchantmentTag TRADES_TAIGA_SPECIAL = new EnchantmentTag("trades/taiga_special"); + private EnchantmentTag() {} - private final int id; - - private EnchantmentTag(String identifier) { - this.id = ALL_ENCHANTMENT_TAGS.size(); - register(identifier, this); - } - - @Override - public int ordinal() { - return id; - } - - private static void register(String name, EnchantmentTag tag) { - ALL_ENCHANTMENT_TAGS.put(MinecraftKey.key(name), tag); + private static Tag create(String name) { + return new Tag<>(JavaRegistries.ENCHANTMENT, MinecraftKey.key(name)); } } diff --git a/core/src/main/java/org/geysermc/geyser/session/cache/tags/GeyserHolderSet.java b/core/src/main/java/org/geysermc/geyser/session/cache/tags/GeyserHolderSet.java new file mode 100644 index 000000000..3c6e02e53 --- /dev/null +++ b/core/src/main/java/org/geysermc/geyser/session/cache/tags/GeyserHolderSet.java @@ -0,0 +1,118 @@ +/* + * Copyright (c) 2024 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.session.cache.tags; + +import java.util.List; +import java.util.Objects; +import java.util.function.ToIntFunction; + +import it.unimi.dsi.fastutil.ints.IntArrays; +import lombok.Data; +import net.kyori.adventure.key.Key; +import org.checkerframework.checker.nullness.qual.NonNull; +import org.checkerframework.checker.nullness.qual.Nullable; +import org.geysermc.geyser.session.GeyserSession; +import org.geysermc.geyser.session.cache.TagCache; +import org.geysermc.geyser.session.cache.registry.JavaRegistryKey; + +/** + * Similar to vanilla Minecraft's HolderSets, stores either a tag or a list of IDs (this list can also be represented as a single ID in vanilla HolderSets). + * + *

Because HolderSets utilise tags, when loading a HolderSet, Geyser must store tags for the registry the HolderSet is for (see {@link JavaRegistryKey}).

+ * + *

Use the {@link GeyserHolderSet#readHolderSet} method to easily read a HolderSet from NBT sent by a server. To turn the HolderSet into a list of network IDs, use the {@link GeyserHolderSet#resolveRaw} method. + * To turn the HolderSet into a list of objects, use the {@link GeyserHolderSet#resolve} method.

+ */ +@Data +public final class GeyserHolderSet { + + private final JavaRegistryKey registry; + private final @Nullable Tag tag; + private final int @Nullable [] holders; + + public GeyserHolderSet(JavaRegistryKey registry, int @NonNull [] holders) { + this.registry = registry; + this.tag = null; + this.holders = holders; + } + + public GeyserHolderSet(JavaRegistryKey registry, @NonNull Tag tagId) { + this.registry = registry; + this.tag = tagId; + this.holders = null; + } + + /** + * Resolves the HolderSet, and automatically maps the network IDs to their respective object types. If the HolderSet is a list of IDs, this will be returned. If it is a tag, the tag will be resolved from the tag cache. + * + * @return the HolderSet turned into a list of objects. + */ + public List resolve(GeyserSession session) { + return TagCache.mapRawArray(session, resolveRaw(session.getTagCache()), registry); + } + + /** + * Resolves the HolderSet. If the HolderSet is a list of IDs, this will be returned. If it is a tag, the tag will be resolved from the tag cache. + * + * @return the HolderSet turned into a list of objects. + */ + public int[] resolveRaw(TagCache tagCache) { + if (holders != null) { + return holders; + } + + return tagCache.getRaw(Objects.requireNonNull(tag, "HolderSet must have a tag if it doesn't have a list of IDs")); + } + + /** + * Reads a HolderSet from an object from NBT. + * + * @param session session, only used for logging purposes. + * @param registry the registry the HolderSet contains IDs from. + * @param holderSet the HolderSet as an object from NBT. + * @param keyIdMapping a function that maps resource location IDs in the HolderSet's registry to their network IDs. + */ + public static GeyserHolderSet readHolderSet(GeyserSession session, JavaRegistryKey registry, @Nullable Object holderSet, ToIntFunction keyIdMapping) { + if (holderSet == null) { + return new GeyserHolderSet<>(registry, IntArrays.EMPTY_ARRAY); + } + + if (holderSet instanceof String stringTag) { + if (stringTag.startsWith("#")) { + // Tag + return new GeyserHolderSet<>(registry, new Tag<>(registry, Key.key(stringTag.substring(1)))); // Remove '#' at beginning that indicates tag + } else if (stringTag.isEmpty()) { + return new GeyserHolderSet<>(registry, IntArrays.EMPTY_ARRAY); + } + return new GeyserHolderSet<>(registry, new int[]{keyIdMapping.applyAsInt(Key.key(stringTag))}); + } else if (holderSet instanceof List list) { + // Assume the list is a list of strings + return new GeyserHolderSet<>(registry, list.stream().map(o -> (String) o).map(Key::key).mapToInt(keyIdMapping).toArray()); + } + session.getGeyser().getLogger().warning("Failed parsing HolderSet for registry + " + registry + "! Expected either a tag, a string ID or a list of string IDs, found " + holderSet); + return new GeyserHolderSet<>(registry, IntArrays.EMPTY_ARRAY); + } +} diff --git a/core/src/main/java/org/geysermc/geyser/session/cache/tags/ItemTag.java b/core/src/main/java/org/geysermc/geyser/session/cache/tags/ItemTag.java index a2e861dd6..4f42f146a 100644 --- a/core/src/main/java/org/geysermc/geyser/session/cache/tags/ItemTag.java +++ b/core/src/main/java/org/geysermc/geyser/session/cache/tags/ItemTag.java @@ -25,178 +25,166 @@ package org.geysermc.geyser.session.cache.tags; -import net.kyori.adventure.key.Key; +import org.geysermc.geyser.item.type.Item; +import org.geysermc.geyser.session.cache.registry.JavaRegistries; import org.geysermc.geyser.util.MinecraftKey; -import org.geysermc.geyser.util.Ordered; - -import java.util.HashMap; -import java.util.Map; +/** + * Lists vanilla item tags. + */ @SuppressWarnings("unused") -public final class ItemTag implements Ordered { - public static final Map ALL_ITEM_TAGS = new HashMap<>(); +public final class ItemTag { + public static final Tag WOOL = create("wool"); + public static final Tag PLANKS = create("planks"); + public static final Tag STONE_BRICKS = create("stone_bricks"); + public static final Tag WOODEN_BUTTONS = create("wooden_buttons"); + public static final Tag STONE_BUTTONS = create("stone_buttons"); + public static final Tag BUTTONS = create("buttons"); + public static final Tag WOOL_CARPETS = create("wool_carpets"); + public static final Tag WOODEN_DOORS = create("wooden_doors"); + public static final Tag WOODEN_STAIRS = create("wooden_stairs"); + public static final Tag WOODEN_SLABS = create("wooden_slabs"); + public static final Tag WOODEN_FENCES = create("wooden_fences"); + public static final Tag FENCE_GATES = create("fence_gates"); + public static final Tag WOODEN_PRESSURE_PLATES = create("wooden_pressure_plates"); + public static final Tag WOODEN_TRAPDOORS = create("wooden_trapdoors"); + public static final Tag DOORS = create("doors"); + public static final Tag SAPLINGS = create("saplings"); + public static final Tag LOGS_THAT_BURN = create("logs_that_burn"); + public static final Tag LOGS = create("logs"); + public static final Tag DARK_OAK_LOGS = create("dark_oak_logs"); + public static final Tag OAK_LOGS = create("oak_logs"); + public static final Tag BIRCH_LOGS = create("birch_logs"); + public static final Tag ACACIA_LOGS = create("acacia_logs"); + public static final Tag CHERRY_LOGS = create("cherry_logs"); + public static final Tag JUNGLE_LOGS = create("jungle_logs"); + public static final Tag SPRUCE_LOGS = create("spruce_logs"); + public static final Tag MANGROVE_LOGS = create("mangrove_logs"); + public static final Tag CRIMSON_STEMS = create("crimson_stems"); + public static final Tag WARPED_STEMS = create("warped_stems"); + public static final Tag BAMBOO_BLOCKS = create("bamboo_blocks"); + public static final Tag WART_BLOCKS = create("wart_blocks"); + public static final Tag BANNERS = create("banners"); + public static final Tag SAND = create("sand"); + public static final Tag SMELTS_TO_GLASS = create("smelts_to_glass"); + public static final Tag STAIRS = create("stairs"); + public static final Tag SLABS = create("slabs"); + public static final Tag WALLS = create("walls"); + public static final Tag ANVIL = create("anvil"); + public static final Tag RAILS = create("rails"); + public static final Tag LEAVES = create("leaves"); + public static final Tag TRAPDOORS = create("trapdoors"); + public static final Tag SMALL_FLOWERS = create("small_flowers"); + public static final Tag BEDS = create("beds"); + public static final Tag FENCES = create("fences"); + public static final Tag TALL_FLOWERS = create("tall_flowers"); + public static final Tag FLOWERS = create("flowers"); + public static final Tag PIGLIN_REPELLENTS = create("piglin_repellents"); + public static final Tag PIGLIN_LOVED = create("piglin_loved"); + public static final Tag IGNORED_BY_PIGLIN_BABIES = create("ignored_by_piglin_babies"); + public static final Tag MEAT = create("meat"); + public static final Tag SNIFFER_FOOD = create("sniffer_food"); + public static final Tag PIGLIN_FOOD = create("piglin_food"); + public static final Tag FOX_FOOD = create("fox_food"); + public static final Tag COW_FOOD = create("cow_food"); + public static final Tag GOAT_FOOD = create("goat_food"); + public static final Tag SHEEP_FOOD = create("sheep_food"); + public static final Tag WOLF_FOOD = create("wolf_food"); + public static final Tag CAT_FOOD = create("cat_food"); + public static final Tag HORSE_FOOD = create("horse_food"); + public static final Tag HORSE_TEMPT_ITEMS = create("horse_tempt_items"); + public static final Tag CAMEL_FOOD = create("camel_food"); + public static final Tag ARMADILLO_FOOD = create("armadillo_food"); + public static final Tag BEE_FOOD = create("bee_food"); + public static final Tag CHICKEN_FOOD = create("chicken_food"); + public static final Tag FROG_FOOD = create("frog_food"); + public static final Tag HOGLIN_FOOD = create("hoglin_food"); + public static final Tag LLAMA_FOOD = create("llama_food"); + public static final Tag LLAMA_TEMPT_ITEMS = create("llama_tempt_items"); + public static final Tag OCELOT_FOOD = create("ocelot_food"); + public static final Tag PANDA_FOOD = create("panda_food"); + public static final Tag PIG_FOOD = create("pig_food"); + public static final Tag RABBIT_FOOD = create("rabbit_food"); + public static final Tag STRIDER_FOOD = create("strider_food"); + public static final Tag STRIDER_TEMPT_ITEMS = create("strider_tempt_items"); + public static final Tag TURTLE_FOOD = create("turtle_food"); + public static final Tag PARROT_FOOD = create("parrot_food"); + public static final Tag PARROT_POISONOUS_FOOD = create("parrot_poisonous_food"); + public static final Tag AXOLOTL_FOOD = create("axolotl_food"); + public static final Tag GOLD_ORES = create("gold_ores"); + public static final Tag IRON_ORES = create("iron_ores"); + public static final Tag DIAMOND_ORES = create("diamond_ores"); + public static final Tag REDSTONE_ORES = create("redstone_ores"); + public static final Tag LAPIS_ORES = create("lapis_ores"); + public static final Tag COAL_ORES = create("coal_ores"); + public static final Tag EMERALD_ORES = create("emerald_ores"); + public static final Tag COPPER_ORES = create("copper_ores"); + public static final Tag NON_FLAMMABLE_WOOD = create("non_flammable_wood"); + public static final Tag SOUL_FIRE_BASE_BLOCKS = create("soul_fire_base_blocks"); + public static final Tag CANDLES = create("candles"); + public static final Tag DIRT = create("dirt"); + public static final Tag TERRACOTTA = create("terracotta"); + public static final Tag COMPLETES_FIND_TREE_TUTORIAL = create("completes_find_tree_tutorial"); + public static final Tag BOATS = create("boats"); + public static final Tag CHEST_BOATS = create("chest_boats"); + public static final Tag FISHES = create("fishes"); + public static final Tag SIGNS = create("signs"); + public static final Tag CREEPER_DROP_MUSIC_DISCS = create("creeper_drop_music_discs"); + public static final Tag COALS = create("coals"); + public static final Tag ARROWS = create("arrows"); + public static final Tag LECTERN_BOOKS = create("lectern_books"); + public static final Tag BOOKSHELF_BOOKS = create("bookshelf_books"); + public static final Tag BEACON_PAYMENT_ITEMS = create("beacon_payment_items"); + public static final Tag STONE_TOOL_MATERIALS = create("stone_tool_materials"); + public static final Tag STONE_CRAFTING_MATERIALS = create("stone_crafting_materials"); + public static final Tag FREEZE_IMMUNE_WEARABLES = create("freeze_immune_wearables"); + public static final Tag DAMPENS_VIBRATIONS = create("dampens_vibrations"); + public static final Tag CLUSTER_MAX_HARVESTABLES = create("cluster_max_harvestables"); + public static final Tag COMPASSES = create("compasses"); + public static final Tag HANGING_SIGNS = create("hanging_signs"); + public static final Tag CREEPER_IGNITERS = create("creeper_igniters"); + public static final Tag NOTEBLOCK_TOP_INSTRUMENTS = create("noteblock_top_instruments"); + public static final Tag FOOT_ARMOR = create("foot_armor"); + public static final Tag LEG_ARMOR = create("leg_armor"); + public static final Tag CHEST_ARMOR = create("chest_armor"); + public static final Tag HEAD_ARMOR = create("head_armor"); + public static final Tag SKULLS = create("skulls"); + public static final Tag TRIMMABLE_ARMOR = create("trimmable_armor"); + public static final Tag TRIM_MATERIALS = create("trim_materials"); + public static final Tag TRIM_TEMPLATES = create("trim_templates"); + public static final Tag DECORATED_POT_SHERDS = create("decorated_pot_sherds"); + public static final Tag DECORATED_POT_INGREDIENTS = create("decorated_pot_ingredients"); + public static final Tag SWORDS = create("swords"); + public static final Tag AXES = create("axes"); + public static final Tag HOES = create("hoes"); + public static final Tag PICKAXES = create("pickaxes"); + public static final Tag SHOVELS = create("shovels"); + public static final Tag BREAKS_DECORATED_POTS = create("breaks_decorated_pots"); + public static final Tag VILLAGER_PLANTABLE_SEEDS = create("villager_plantable_seeds"); + public static final Tag DYEABLE = create("dyeable"); + public static final Tag ENCHANTABLE_FOOT_ARMOR = create("enchantable/foot_armor"); + public static final Tag ENCHANTABLE_LEG_ARMOR = create("enchantable/leg_armor"); + public static final Tag ENCHANTABLE_CHEST_ARMOR = create("enchantable/chest_armor"); + public static final Tag ENCHANTABLE_HEAD_ARMOR = create("enchantable/head_armor"); + public static final Tag ENCHANTABLE_ARMOR = create("enchantable/armor"); + public static final Tag ENCHANTABLE_SWORD = create("enchantable/sword"); + public static final Tag ENCHANTABLE_FIRE_ASPECT = create("enchantable/fire_aspect"); + public static final Tag ENCHANTABLE_SHARP_WEAPON = create("enchantable/sharp_weapon"); + public static final Tag ENCHANTABLE_WEAPON = create("enchantable/weapon"); + public static final Tag ENCHANTABLE_MINING = create("enchantable/mining"); + public static final Tag ENCHANTABLE_MINING_LOOT = create("enchantable/mining_loot"); + public static final Tag ENCHANTABLE_FISHING = create("enchantable/fishing"); + public static final Tag ENCHANTABLE_TRIDENT = create("enchantable/trident"); + public static final Tag ENCHANTABLE_DURABILITY = create("enchantable/durability"); + public static final Tag ENCHANTABLE_BOW = create("enchantable/bow"); + public static final Tag ENCHANTABLE_EQUIPPABLE = create("enchantable/equippable"); + public static final Tag ENCHANTABLE_CROSSBOW = create("enchantable/crossbow"); + public static final Tag ENCHANTABLE_VANISHING = create("enchantable/vanishing"); + public static final Tag ENCHANTABLE_MACE = create("enchantable/mace"); - public static final ItemTag WOOL = new ItemTag("wool"); - public static final ItemTag PLANKS = new ItemTag("planks"); - public static final ItemTag STONE_BRICKS = new ItemTag("stone_bricks"); - public static final ItemTag WOODEN_BUTTONS = new ItemTag("wooden_buttons"); - public static final ItemTag STONE_BUTTONS = new ItemTag("stone_buttons"); - public static final ItemTag BUTTONS = new ItemTag("buttons"); - public static final ItemTag WOOL_CARPETS = new ItemTag("wool_carpets"); - public static final ItemTag WOODEN_DOORS = new ItemTag("wooden_doors"); - public static final ItemTag WOODEN_STAIRS = new ItemTag("wooden_stairs"); - public static final ItemTag WOODEN_SLABS = new ItemTag("wooden_slabs"); - public static final ItemTag WOODEN_FENCES = new ItemTag("wooden_fences"); - public static final ItemTag FENCE_GATES = new ItemTag("fence_gates"); - public static final ItemTag WOODEN_PRESSURE_PLATES = new ItemTag("wooden_pressure_plates"); - public static final ItemTag WOODEN_TRAPDOORS = new ItemTag("wooden_trapdoors"); - public static final ItemTag DOORS = new ItemTag("doors"); - public static final ItemTag SAPLINGS = new ItemTag("saplings"); - public static final ItemTag LOGS_THAT_BURN = new ItemTag("logs_that_burn"); - public static final ItemTag LOGS = new ItemTag("logs"); - public static final ItemTag DARK_OAK_LOGS = new ItemTag("dark_oak_logs"); - public static final ItemTag OAK_LOGS = new ItemTag("oak_logs"); - public static final ItemTag BIRCH_LOGS = new ItemTag("birch_logs"); - public static final ItemTag ACACIA_LOGS = new ItemTag("acacia_logs"); - public static final ItemTag CHERRY_LOGS = new ItemTag("cherry_logs"); - public static final ItemTag JUNGLE_LOGS = new ItemTag("jungle_logs"); - public static final ItemTag SPRUCE_LOGS = new ItemTag("spruce_logs"); - public static final ItemTag MANGROVE_LOGS = new ItemTag("mangrove_logs"); - public static final ItemTag CRIMSON_STEMS = new ItemTag("crimson_stems"); - public static final ItemTag WARPED_STEMS = new ItemTag("warped_stems"); - public static final ItemTag BAMBOO_BLOCKS = new ItemTag("bamboo_blocks"); - public static final ItemTag WART_BLOCKS = new ItemTag("wart_blocks"); - public static final ItemTag BANNERS = new ItemTag("banners"); - public static final ItemTag SAND = new ItemTag("sand"); - public static final ItemTag SMELTS_TO_GLASS = new ItemTag("smelts_to_glass"); - public static final ItemTag STAIRS = new ItemTag("stairs"); - public static final ItemTag SLABS = new ItemTag("slabs"); - public static final ItemTag WALLS = new ItemTag("walls"); - public static final ItemTag ANVIL = new ItemTag("anvil"); - public static final ItemTag RAILS = new ItemTag("rails"); - public static final ItemTag LEAVES = new ItemTag("leaves"); - public static final ItemTag TRAPDOORS = new ItemTag("trapdoors"); - public static final ItemTag SMALL_FLOWERS = new ItemTag("small_flowers"); - public static final ItemTag BEDS = new ItemTag("beds"); - public static final ItemTag FENCES = new ItemTag("fences"); - public static final ItemTag TALL_FLOWERS = new ItemTag("tall_flowers"); - public static final ItemTag FLOWERS = new ItemTag("flowers"); - public static final ItemTag PIGLIN_REPELLENTS = new ItemTag("piglin_repellents"); - public static final ItemTag PIGLIN_LOVED = new ItemTag("piglin_loved"); - public static final ItemTag IGNORED_BY_PIGLIN_BABIES = new ItemTag("ignored_by_piglin_babies"); - public static final ItemTag MEAT = new ItemTag("meat"); - public static final ItemTag SNIFFER_FOOD = new ItemTag("sniffer_food"); - public static final ItemTag PIGLIN_FOOD = new ItemTag("piglin_food"); - public static final ItemTag FOX_FOOD = new ItemTag("fox_food"); - public static final ItemTag COW_FOOD = new ItemTag("cow_food"); - public static final ItemTag GOAT_FOOD = new ItemTag("goat_food"); - public static final ItemTag SHEEP_FOOD = new ItemTag("sheep_food"); - public static final ItemTag WOLF_FOOD = new ItemTag("wolf_food"); - public static final ItemTag CAT_FOOD = new ItemTag("cat_food"); - public static final ItemTag HORSE_FOOD = new ItemTag("horse_food"); - public static final ItemTag HORSE_TEMPT_ITEMS = new ItemTag("horse_tempt_items"); - public static final ItemTag CAMEL_FOOD = new ItemTag("camel_food"); - public static final ItemTag ARMADILLO_FOOD = new ItemTag("armadillo_food"); - public static final ItemTag BEE_FOOD = new ItemTag("bee_food"); - public static final ItemTag CHICKEN_FOOD = new ItemTag("chicken_food"); - public static final ItemTag FROG_FOOD = new ItemTag("frog_food"); - public static final ItemTag HOGLIN_FOOD = new ItemTag("hoglin_food"); - public static final ItemTag LLAMA_FOOD = new ItemTag("llama_food"); - public static final ItemTag LLAMA_TEMPT_ITEMS = new ItemTag("llama_tempt_items"); - public static final ItemTag OCELOT_FOOD = new ItemTag("ocelot_food"); - public static final ItemTag PANDA_FOOD = new ItemTag("panda_food"); - public static final ItemTag PIG_FOOD = new ItemTag("pig_food"); - public static final ItemTag RABBIT_FOOD = new ItemTag("rabbit_food"); - public static final ItemTag STRIDER_FOOD = new ItemTag("strider_food"); - public static final ItemTag STRIDER_TEMPT_ITEMS = new ItemTag("strider_tempt_items"); - public static final ItemTag TURTLE_FOOD = new ItemTag("turtle_food"); - public static final ItemTag PARROT_FOOD = new ItemTag("parrot_food"); - public static final ItemTag PARROT_POISONOUS_FOOD = new ItemTag("parrot_poisonous_food"); - public static final ItemTag AXOLOTL_FOOD = new ItemTag("axolotl_food"); - public static final ItemTag GOLD_ORES = new ItemTag("gold_ores"); - public static final ItemTag IRON_ORES = new ItemTag("iron_ores"); - public static final ItemTag DIAMOND_ORES = new ItemTag("diamond_ores"); - public static final ItemTag REDSTONE_ORES = new ItemTag("redstone_ores"); - public static final ItemTag LAPIS_ORES = new ItemTag("lapis_ores"); - public static final ItemTag COAL_ORES = new ItemTag("coal_ores"); - public static final ItemTag EMERALD_ORES = new ItemTag("emerald_ores"); - public static final ItemTag COPPER_ORES = new ItemTag("copper_ores"); - public static final ItemTag NON_FLAMMABLE_WOOD = new ItemTag("non_flammable_wood"); - public static final ItemTag SOUL_FIRE_BASE_BLOCKS = new ItemTag("soul_fire_base_blocks"); - public static final ItemTag CANDLES = new ItemTag("candles"); - public static final ItemTag DIRT = new ItemTag("dirt"); - public static final ItemTag TERRACOTTA = new ItemTag("terracotta"); - public static final ItemTag COMPLETES_FIND_TREE_TUTORIAL = new ItemTag("completes_find_tree_tutorial"); - public static final ItemTag BOATS = new ItemTag("boats"); - public static final ItemTag CHEST_BOATS = new ItemTag("chest_boats"); - public static final ItemTag FISHES = new ItemTag("fishes"); - public static final ItemTag SIGNS = new ItemTag("signs"); - public static final ItemTag CREEPER_DROP_MUSIC_DISCS = new ItemTag("creeper_drop_music_discs"); - public static final ItemTag COALS = new ItemTag("coals"); - public static final ItemTag ARROWS = new ItemTag("arrows"); - public static final ItemTag LECTERN_BOOKS = new ItemTag("lectern_books"); - public static final ItemTag BOOKSHELF_BOOKS = new ItemTag("bookshelf_books"); - public static final ItemTag BEACON_PAYMENT_ITEMS = new ItemTag("beacon_payment_items"); - public static final ItemTag STONE_TOOL_MATERIALS = new ItemTag("stone_tool_materials"); - public static final ItemTag STONE_CRAFTING_MATERIALS = new ItemTag("stone_crafting_materials"); - public static final ItemTag FREEZE_IMMUNE_WEARABLES = new ItemTag("freeze_immune_wearables"); - public static final ItemTag DAMPENS_VIBRATIONS = new ItemTag("dampens_vibrations"); - public static final ItemTag CLUSTER_MAX_HARVESTABLES = new ItemTag("cluster_max_harvestables"); - public static final ItemTag COMPASSES = new ItemTag("compasses"); - public static final ItemTag HANGING_SIGNS = new ItemTag("hanging_signs"); - public static final ItemTag CREEPER_IGNITERS = new ItemTag("creeper_igniters"); - public static final ItemTag NOTEBLOCK_TOP_INSTRUMENTS = new ItemTag("noteblock_top_instruments"); - public static final ItemTag FOOT_ARMOR = new ItemTag("foot_armor"); - public static final ItemTag LEG_ARMOR = new ItemTag("leg_armor"); - public static final ItemTag CHEST_ARMOR = new ItemTag("chest_armor"); - public static final ItemTag HEAD_ARMOR = new ItemTag("head_armor"); - public static final ItemTag SKULLS = new ItemTag("skulls"); - public static final ItemTag TRIMMABLE_ARMOR = new ItemTag("trimmable_armor"); - public static final ItemTag TRIM_MATERIALS = new ItemTag("trim_materials"); - public static final ItemTag TRIM_TEMPLATES = new ItemTag("trim_templates"); - public static final ItemTag DECORATED_POT_SHERDS = new ItemTag("decorated_pot_sherds"); - public static final ItemTag DECORATED_POT_INGREDIENTS = new ItemTag("decorated_pot_ingredients"); - public static final ItemTag SWORDS = new ItemTag("swords"); - public static final ItemTag AXES = new ItemTag("axes"); - public static final ItemTag HOES = new ItemTag("hoes"); - public static final ItemTag PICKAXES = new ItemTag("pickaxes"); - public static final ItemTag SHOVELS = new ItemTag("shovels"); - public static final ItemTag BREAKS_DECORATED_POTS = new ItemTag("breaks_decorated_pots"); - public static final ItemTag VILLAGER_PLANTABLE_SEEDS = new ItemTag("villager_plantable_seeds"); - public static final ItemTag DYEABLE = new ItemTag("dyeable"); - public static final ItemTag ENCHANTABLE_FOOT_ARMOR = new ItemTag("enchantable/foot_armor"); - public static final ItemTag ENCHANTABLE_LEG_ARMOR = new ItemTag("enchantable/leg_armor"); - public static final ItemTag ENCHANTABLE_CHEST_ARMOR = new ItemTag("enchantable/chest_armor"); - public static final ItemTag ENCHANTABLE_HEAD_ARMOR = new ItemTag("enchantable/head_armor"); - public static final ItemTag ENCHANTABLE_ARMOR = new ItemTag("enchantable/armor"); - public static final ItemTag ENCHANTABLE_SWORD = new ItemTag("enchantable/sword"); - public static final ItemTag ENCHANTABLE_FIRE_ASPECT = new ItemTag("enchantable/fire_aspect"); - public static final ItemTag ENCHANTABLE_SHARP_WEAPON = new ItemTag("enchantable/sharp_weapon"); - public static final ItemTag ENCHANTABLE_WEAPON = new ItemTag("enchantable/weapon"); - public static final ItemTag ENCHANTABLE_MINING = new ItemTag("enchantable/mining"); - public static final ItemTag ENCHANTABLE_MINING_LOOT = new ItemTag("enchantable/mining_loot"); - public static final ItemTag ENCHANTABLE_FISHING = new ItemTag("enchantable/fishing"); - public static final ItemTag ENCHANTABLE_TRIDENT = new ItemTag("enchantable/trident"); - public static final ItemTag ENCHANTABLE_DURABILITY = new ItemTag("enchantable/durability"); - public static final ItemTag ENCHANTABLE_BOW = new ItemTag("enchantable/bow"); - public static final ItemTag ENCHANTABLE_EQUIPPABLE = new ItemTag("enchantable/equippable"); - public static final ItemTag ENCHANTABLE_CROSSBOW = new ItemTag("enchantable/crossbow"); - public static final ItemTag ENCHANTABLE_VANISHING = new ItemTag("enchantable/vanishing"); - public static final ItemTag ENCHANTABLE_MACE = new ItemTag("enchantable/mace"); + private ItemTag() {} - private final int id; - - private ItemTag(String identifier) { - this.id = ALL_ITEM_TAGS.size(); - register(identifier, this); - } - - @Override - public int ordinal() { - return id; - } - - private static void register(String name, ItemTag tag) { - ALL_ITEM_TAGS.put(MinecraftKey.key(name), tag); + private static Tag create(String name) { + return new Tag<>(JavaRegistries.ITEM, MinecraftKey.key(name)); } } diff --git a/core/src/main/java/org/geysermc/geyser/session/cache/tags/Tag.java b/core/src/main/java/org/geysermc/geyser/session/cache/tags/Tag.java new file mode 100644 index 000000000..276e30b9f --- /dev/null +++ b/core/src/main/java/org/geysermc/geyser/session/cache/tags/Tag.java @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2024 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.session.cache.tags; + +import net.kyori.adventure.key.Key; +import org.geysermc.geyser.session.cache.registry.JavaRegistryKey; + +/** + * A tag in any of the registries that tags are loaded for by Geyser. + */ +public record Tag(JavaRegistryKey registry, Key tag) { +} diff --git a/core/src/main/java/org/geysermc/geyser/text/ChatColor.java b/core/src/main/java/org/geysermc/geyser/text/ChatColor.java index 49178f033..22e553678 100644 --- a/core/src/main/java/org/geysermc/geyser/text/ChatColor.java +++ b/core/src/main/java/org/geysermc/geyser/text/ChatColor.java @@ -25,6 +25,8 @@ package org.geysermc.geyser.text; +import org.geysermc.mcprotocollib.protocol.data.game.scoreboard.TeamColor; + public class ChatColor { public static final String ANSI_RESET = (char) 0x1b + "[0m"; @@ -84,4 +86,58 @@ public class ChatColor { string = string.replace(WHITE, (char) 0x1b + "[37;1m"); return string; } -} \ No newline at end of file + + public static String styleOrder(int index) { + // https://bugs.mojang.com/browse/MCPE-41729 + // strikethrough and underlined do not exist on Bedrock + return switch (index) { + case 0 -> BLACK; + case 1 -> DARK_BLUE; + case 2 -> DARK_GREEN; + case 3 -> DARK_AQUA; + case 4 -> DARK_RED; + case 5 -> DARK_PURPLE; + case 6 -> GOLD; + case 7 -> GRAY; + case 8 -> DARK_GRAY; + case 9 -> BLUE; + case 10 -> GREEN; + case 11 -> AQUA; + case 12 -> RED; + case 13 -> LIGHT_PURPLE; + case 14 -> YELLOW; + case 15 -> WHITE; + case 16 -> OBFUSCATED; + case 17 -> BOLD; + default -> ITALIC; + }; + } + + public static String chatColorFor(TeamColor teamColor) { + // https://bugs.mojang.com/browse/MCPE-41729 + // strikethrough and underlined do not exist on Bedrock + return switch (teamColor) { + case BLACK -> BLACK; + case DARK_BLUE -> DARK_BLUE; + case DARK_GREEN -> DARK_GREEN; + case DARK_AQUA -> DARK_AQUA; + case DARK_RED -> DARK_RED; + case DARK_PURPLE -> DARK_PURPLE; + case GOLD -> GOLD; + case GRAY -> GRAY; + case DARK_GRAY -> DARK_GRAY; + case BLUE -> BLUE; + case GREEN -> GREEN; + case AQUA -> AQUA; + case RED -> RED; + case LIGHT_PURPLE -> LIGHT_PURPLE; + case YELLOW -> YELLOW; + case WHITE -> WHITE; + case OBFUSCATED -> OBFUSCATED; + case BOLD -> BOLD; + case STRIKETHROUGH, UNDERLINED -> ""; + case ITALIC -> ITALIC; + default -> RESET; + }; + } +} 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 cee48d096..4dd9a50f8 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 @@ -25,21 +25,40 @@ package org.geysermc.geyser.translator.inventory; -import it.unimi.dsi.fastutil.ints.*; +import it.unimi.dsi.fastutil.ints.Int2IntMap; +import it.unimi.dsi.fastutil.ints.Int2IntOpenHashMap; +import it.unimi.dsi.fastutil.ints.Int2ObjectMap; +import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; +import it.unimi.dsi.fastutil.ints.IntIterator; +import it.unimi.dsi.fastutil.ints.IntLinkedOpenHashSet; +import it.unimi.dsi.fastutil.ints.IntOpenHashSet; +import it.unimi.dsi.fastutil.ints.IntSet; +import it.unimi.dsi.fastutil.ints.IntSortedSet; import lombok.AllArgsConstructor; import org.checkerframework.checker.nullness.qual.Nullable; import org.cloudburstmc.protocol.bedrock.data.inventory.ContainerSlotType; import org.cloudburstmc.protocol.bedrock.data.inventory.FullContainerName; import org.cloudburstmc.protocol.bedrock.data.inventory.itemstack.request.ItemStackRequest; import org.cloudburstmc.protocol.bedrock.data.inventory.itemstack.request.ItemStackRequestSlotData; -import org.cloudburstmc.protocol.bedrock.data.inventory.itemstack.request.action.*; +import org.cloudburstmc.protocol.bedrock.data.inventory.itemstack.request.action.AutoCraftRecipeAction; +import org.cloudburstmc.protocol.bedrock.data.inventory.itemstack.request.action.ConsumeAction; +import org.cloudburstmc.protocol.bedrock.data.inventory.itemstack.request.action.CraftResultsDeprecatedAction; +import org.cloudburstmc.protocol.bedrock.data.inventory.itemstack.request.action.DropAction; +import org.cloudburstmc.protocol.bedrock.data.inventory.itemstack.request.action.ItemStackRequestAction; +import org.cloudburstmc.protocol.bedrock.data.inventory.itemstack.request.action.SwapAction; +import org.cloudburstmc.protocol.bedrock.data.inventory.itemstack.request.action.TransferItemStackRequestAction; import org.cloudburstmc.protocol.bedrock.data.inventory.itemstack.response.ItemStackResponse; import org.cloudburstmc.protocol.bedrock.data.inventory.itemstack.response.ItemStackResponseContainer; import org.cloudburstmc.protocol.bedrock.data.inventory.itemstack.response.ItemStackResponseSlot; import org.cloudburstmc.protocol.bedrock.data.inventory.itemstack.response.ItemStackResponseStatus; import org.cloudburstmc.protocol.bedrock.packet.ItemStackResponsePacket; import org.geysermc.geyser.GeyserImpl; -import org.geysermc.geyser.inventory.*; +import org.geysermc.geyser.inventory.BedrockContainerSlot; +import org.geysermc.geyser.inventory.CartographyContainer; +import org.geysermc.geyser.inventory.GeyserItemStack; +import org.geysermc.geyser.inventory.Inventory; +import org.geysermc.geyser.inventory.PlayerInventory; +import org.geysermc.geyser.inventory.SlotType; import org.geysermc.geyser.inventory.click.Click; import org.geysermc.geyser.inventory.click.ClickPlan; import org.geysermc.geyser.inventory.recipe.GeyserRecipe; @@ -56,12 +75,17 @@ import org.geysermc.geyser.translator.inventory.furnace.SmokerInventoryTranslato import org.geysermc.geyser.util.InventoryUtils; import org.geysermc.geyser.util.ItemUtils; import org.geysermc.mcprotocollib.protocol.data.game.inventory.ContainerType; -import org.geysermc.mcprotocollib.protocol.data.game.item.ItemStack; import org.geysermc.mcprotocollib.protocol.data.game.item.component.DataComponentType; import org.geysermc.mcprotocollib.protocol.data.game.item.component.DataComponents; -import org.geysermc.mcprotocollib.protocol.data.game.recipe.Ingredient; +import org.geysermc.mcprotocollib.protocol.data.game.recipe.display.slot.EmptySlotDisplay; +import org.geysermc.mcprotocollib.protocol.data.game.recipe.display.slot.SlotDisplay; -import java.util.*; +import java.util.ArrayList; +import java.util.Collections; +import java.util.EnumMap; +import java.util.HashMap; +import java.util.List; +import java.util.Map; @AllArgsConstructor public abstract class InventoryTranslator { @@ -642,8 +666,8 @@ public abstract class InventoryTranslator { } int gridDimensions = gridSize == 4 ? 2 : 3; - Ingredient[] ingredients = new Ingredient[0]; - ItemStack output = null; + List ingredients = Collections.emptyList(); + SlotDisplay output = null; int recipeWidth = 0; int ingRemaining = 0; int ingredientIndex = -1; @@ -697,7 +721,7 @@ public abstract class InventoryTranslator { ingredients = shapelessRecipe.ingredients(); recipeWidth = gridDimensions; output = shapelessRecipe.result(); - if (ingredients.length > gridSize) { + if (ingredients.size() > gridSize) { return rejectRequest(request); } } @@ -728,8 +752,8 @@ public abstract class InventoryTranslator { craftState = CraftState.INGREDIENTS; if (ingRemaining == 0) { - while (++ingredientIndex < ingredients.length) { - if (ingredients[ingredientIndex].getOptions().length != 0) { + while (++ingredientIndex < ingredients.size()) { + if (!(ingredients.get(ingredientIndex) instanceof EmptySlotDisplay)) { // TODO I guess can technically other options be empty? ingRemaining = timesCrafted; break; } diff --git a/core/src/main/java/org/geysermc/geyser/translator/inventory/OldSmithingTableTranslator.java b/core/src/main/java/org/geysermc/geyser/translator/inventory/OldSmithingTableTranslator.java index 685d51fc0..38bb6ddcd 100644 --- a/core/src/main/java/org/geysermc/geyser/translator/inventory/OldSmithingTableTranslator.java +++ b/core/src/main/java/org/geysermc/geyser/translator/inventory/OldSmithingTableTranslator.java @@ -28,11 +28,14 @@ package org.geysermc.geyser.translator.inventory; import org.cloudburstmc.protocol.bedrock.data.inventory.ContainerId; import org.cloudburstmc.protocol.bedrock.data.inventory.ContainerSlotType; import org.cloudburstmc.protocol.bedrock.data.inventory.ContainerType; -import org.cloudburstmc.protocol.bedrock.data.inventory.FullContainerName; import org.cloudburstmc.protocol.bedrock.data.inventory.ItemData; import org.cloudburstmc.protocol.bedrock.data.inventory.itemstack.request.ItemStackRequest; import org.cloudburstmc.protocol.bedrock.data.inventory.itemstack.request.ItemStackRequestSlotData; -import org.cloudburstmc.protocol.bedrock.data.inventory.itemstack.request.action.*; +import org.cloudburstmc.protocol.bedrock.data.inventory.itemstack.request.action.DropAction; +import org.cloudburstmc.protocol.bedrock.data.inventory.itemstack.request.action.ItemStackRequestAction; +import org.cloudburstmc.protocol.bedrock.data.inventory.itemstack.request.action.PlaceAction; +import org.cloudburstmc.protocol.bedrock.data.inventory.itemstack.request.action.SwapAction; +import org.cloudburstmc.protocol.bedrock.data.inventory.itemstack.request.action.TakeAction; import org.cloudburstmc.protocol.bedrock.data.inventory.itemstack.response.ItemStackResponse; import org.cloudburstmc.protocol.bedrock.packet.InventorySlotPacket; import org.geysermc.geyser.inventory.BedrockContainerSlot; @@ -140,7 +143,6 @@ public class OldSmithingTableTranslator extends AbstractBlockInventoryTranslator slotPacket.setContainerId(ContainerId.UI); slotPacket.setSlot(53); slotPacket.setItem(UPGRADE_TEMPLATE.apply(session.getUpstream().getProtocolVersion())); - slotPacket.setContainerNameData(new FullContainerName(ContainerSlotType.ANVIL_INPUT, null)); session.sendUpstreamPacket(slotPacket); } } 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 a276e4750..8fd365d7f 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 @@ -30,17 +30,25 @@ import it.unimi.dsi.fastutil.ints.IntOpenHashSet; import it.unimi.dsi.fastutil.ints.IntSet; import org.cloudburstmc.protocol.bedrock.data.inventory.ContainerId; import org.cloudburstmc.protocol.bedrock.data.inventory.ContainerSlotType; -import org.cloudburstmc.protocol.bedrock.data.inventory.FullContainerName; import org.cloudburstmc.protocol.bedrock.data.inventory.ItemData; import org.cloudburstmc.protocol.bedrock.data.inventory.itemstack.request.ItemStackRequest; import org.cloudburstmc.protocol.bedrock.data.inventory.itemstack.request.ItemStackRequestSlotData; -import org.cloudburstmc.protocol.bedrock.data.inventory.itemstack.request.action.*; +import org.cloudburstmc.protocol.bedrock.data.inventory.itemstack.request.action.CraftCreativeAction; +import org.cloudburstmc.protocol.bedrock.data.inventory.itemstack.request.action.DestroyAction; +import org.cloudburstmc.protocol.bedrock.data.inventory.itemstack.request.action.DropAction; +import org.cloudburstmc.protocol.bedrock.data.inventory.itemstack.request.action.ItemStackRequestAction; +import org.cloudburstmc.protocol.bedrock.data.inventory.itemstack.request.action.SwapAction; +import org.cloudburstmc.protocol.bedrock.data.inventory.itemstack.request.action.TransferItemStackRequestAction; import org.cloudburstmc.protocol.bedrock.data.inventory.itemstack.response.ItemStackResponse; import org.cloudburstmc.protocol.bedrock.packet.ContainerClosePacket; import org.cloudburstmc.protocol.bedrock.packet.ContainerOpenPacket; import org.cloudburstmc.protocol.bedrock.packet.InventoryContentPacket; import org.cloudburstmc.protocol.bedrock.packet.InventorySlotPacket; -import org.geysermc.geyser.inventory.*; +import org.geysermc.geyser.inventory.BedrockContainerSlot; +import org.geysermc.geyser.inventory.GeyserItemStack; +import org.geysermc.geyser.inventory.Inventory; +import org.geysermc.geyser.inventory.PlayerInventory; +import org.geysermc.geyser.inventory.SlotType; import org.geysermc.geyser.item.Items; import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.skin.FakeHeadProvider; @@ -84,7 +92,6 @@ public class PlayerInventoryTranslator extends InventoryTranslator { contents[i - 36] = inventory.getItem(i).getItemData(session); } inventoryContentPacket.setContents(Arrays.asList(contents)); - inventoryContentPacket.setContainerNameData(new FullContainerName(ContainerSlotType.ANVIL_INPUT, null)); session.sendUpstreamPacket(inventoryContentPacket); // Armor @@ -101,14 +108,12 @@ public class PlayerInventoryTranslator extends InventoryTranslator { } } armorContentPacket.setContents(Arrays.asList(contents)); - armorContentPacket.setContainerNameData(new FullContainerName(ContainerSlotType.ANVIL_INPUT, null)); session.sendUpstreamPacket(armorContentPacket); // Offhand InventoryContentPacket offhandPacket = new InventoryContentPacket(); offhandPacket.setContainerId(ContainerId.OFFHAND); offhandPacket.setContents(Collections.singletonList(inventory.getItem(45).getItemData(session))); - offhandPacket.setContainerNameData(new FullContainerName(ContainerSlotType.ANVIL_INPUT, null)); session.sendUpstreamPacket(offhandPacket); } @@ -130,7 +135,6 @@ public class PlayerInventoryTranslator extends InventoryTranslator { slotPacket.setItem(inventory.getItem(i).getItemData(session)); } - slotPacket.setContainerNameData(new FullContainerName(ContainerSlotType.ANVIL_INPUT, null)); session.sendUpstreamPacket(slotPacket); } } @@ -167,13 +171,11 @@ public class PlayerInventoryTranslator extends InventoryTranslator { slotPacket.setSlot(slot + 27); } slotPacket.setItem(bedrockItem); - slotPacket.setContainerNameData(new FullContainerName(ContainerSlotType.ANVIL_INPUT, null)); session.sendUpstreamPacket(slotPacket); } else if (slot == 45) { InventoryContentPacket offhandPacket = new InventoryContentPacket(); offhandPacket.setContainerId(ContainerId.OFFHAND); offhandPacket.setContents(Collections.singletonList(bedrockItem)); - offhandPacket.setContainerNameData(new FullContainerName(ContainerSlotType.ANVIL_INPUT, null)); session.sendUpstreamPacket(offhandPacket); } } diff --git a/core/src/main/java/org/geysermc/geyser/translator/inventory/SmithingInventoryTranslator.java b/core/src/main/java/org/geysermc/geyser/translator/inventory/SmithingInventoryTranslator.java index c68347fd3..dbe24230a 100644 --- a/core/src/main/java/org/geysermc/geyser/translator/inventory/SmithingInventoryTranslator.java +++ b/core/src/main/java/org/geysermc/geyser/translator/inventory/SmithingInventoryTranslator.java @@ -33,6 +33,11 @@ import org.geysermc.geyser.inventory.updater.UIInventoryUpdater; import org.geysermc.geyser.level.block.Blocks; public class SmithingInventoryTranslator extends AbstractBlockInventoryTranslator { + public static final int TEMPLATE = 0; + public static final int INPUT = 1; + public static final int MATERIAL = 2; + public static final int OUTPUT = 3; + public SmithingInventoryTranslator() { super(4, Blocks.SMITHING_TABLE, ContainerType.SMITHING_TABLE, UIInventoryUpdater.INSTANCE); } @@ -40,10 +45,10 @@ public class SmithingInventoryTranslator extends AbstractBlockInventoryTranslato @Override public int bedrockSlotToJava(ItemStackRequestSlotData slotInfoData) { return switch (slotInfoData.getContainer()) { - case SMITHING_TABLE_TEMPLATE -> 0; - case SMITHING_TABLE_INPUT -> 1; - case SMITHING_TABLE_MATERIAL -> 2; - case SMITHING_TABLE_RESULT, CREATED_OUTPUT -> 3; + case SMITHING_TABLE_TEMPLATE -> TEMPLATE; + case SMITHING_TABLE_INPUT -> INPUT; + case SMITHING_TABLE_MATERIAL -> MATERIAL; + case SMITHING_TABLE_RESULT, CREATED_OUTPUT -> OUTPUT; default -> super.bedrockSlotToJava(slotInfoData); }; } @@ -51,10 +56,10 @@ public class SmithingInventoryTranslator extends AbstractBlockInventoryTranslato @Override public BedrockContainerSlot javaSlotToBedrockContainer(int slot) { return switch (slot) { - case 0 -> new BedrockContainerSlot(ContainerSlotType.SMITHING_TABLE_TEMPLATE, 53); - case 1 -> new BedrockContainerSlot(ContainerSlotType.SMITHING_TABLE_INPUT, 51); - case 2 -> new BedrockContainerSlot(ContainerSlotType.SMITHING_TABLE_MATERIAL, 52); - case 3 -> new BedrockContainerSlot(ContainerSlotType.SMITHING_TABLE_RESULT, 50); + case TEMPLATE -> new BedrockContainerSlot(ContainerSlotType.SMITHING_TABLE_TEMPLATE, 53); + case INPUT -> new BedrockContainerSlot(ContainerSlotType.SMITHING_TABLE_INPUT, 51); + case MATERIAL -> new BedrockContainerSlot(ContainerSlotType.SMITHING_TABLE_MATERIAL, 52); + case OUTPUT -> new BedrockContainerSlot(ContainerSlotType.SMITHING_TABLE_RESULT, 50); default -> super.javaSlotToBedrockContainer(slot); }; } @@ -62,10 +67,10 @@ public class SmithingInventoryTranslator extends AbstractBlockInventoryTranslato @Override public int javaSlotToBedrock(int slot) { return switch (slot) { - case 0 -> 53; - case 1 -> 51; - case 2 -> 52; - case 3 -> 50; + case TEMPLATE -> 53; + case INPUT -> 51; + case MATERIAL -> 52; + case OUTPUT -> 50; default -> super.javaSlotToBedrock(slot); }; } diff --git a/core/src/main/java/org/geysermc/geyser/translator/inventory/horse/ChestedHorseInventoryTranslator.java b/core/src/main/java/org/geysermc/geyser/translator/inventory/horse/ChestedHorseInventoryTranslator.java index ba3b7285e..f1a5723c8 100644 --- a/core/src/main/java/org/geysermc/geyser/translator/inventory/horse/ChestedHorseInventoryTranslator.java +++ b/core/src/main/java/org/geysermc/geyser/translator/inventory/horse/ChestedHorseInventoryTranslator.java @@ -27,7 +27,6 @@ package org.geysermc.geyser.translator.inventory.horse; import org.cloudburstmc.protocol.bedrock.data.inventory.ContainerId; import org.cloudburstmc.protocol.bedrock.data.inventory.ContainerSlotType; -import org.cloudburstmc.protocol.bedrock.data.inventory.FullContainerName; import org.cloudburstmc.protocol.bedrock.data.inventory.ItemData; import org.cloudburstmc.protocol.bedrock.data.inventory.itemstack.request.ItemStackRequestSlotData; import org.cloudburstmc.protocol.bedrock.packet.InventoryContentPacket; @@ -95,7 +94,6 @@ public abstract class ChestedHorseInventoryTranslator extends AbstractHorseInven InventoryContentPacket contentPacket = new InventoryContentPacket(); contentPacket.setContainerId(ContainerId.INVENTORY); contentPacket.setContents(Arrays.asList(bedrockItems)); - contentPacket.setContainerNameData(new FullContainerName(ContainerSlotType.ANVIL_INPUT, null)); session.sendUpstreamPacket(contentPacket); ItemData[] horseItems = new ItemData[chestSize + 1]; @@ -109,7 +107,6 @@ public abstract class ChestedHorseInventoryTranslator extends AbstractHorseInven InventoryContentPacket horseContentsPacket = new InventoryContentPacket(); horseContentsPacket.setContainerId(inventory.getBedrockId()); horseContentsPacket.setContents(Arrays.asList(horseItems)); - horseContentsPacket.setContainerNameData(new FullContainerName(ContainerSlotType.ANVIL_INPUT, null)); session.sendUpstreamPacket(horseContentsPacket); } } diff --git a/core/src/main/java/org/geysermc/geyser/translator/item/ItemTranslator.java b/core/src/main/java/org/geysermc/geyser/translator/item/ItemTranslator.java index 163eef20b..3cfd00233 100644 --- a/core/src/main/java/org/geysermc/geyser/translator/item/ItemTranslator.java +++ b/core/src/main/java/org/geysermc/geyser/translator/item/ItemTranslator.java @@ -108,7 +108,7 @@ public final class ItemTranslator { ItemMapping bedrockItem = session.getItemMappings().getMapping(data); Item javaItem = bedrockItem.getJavaItem(); - GeyserItemStack itemStack = javaItem.translateToJava(data, bedrockItem, session.getItemMappings()); + GeyserItemStack itemStack = javaItem.translateToJava(session, data, bedrockItem, session.getItemMappings()); NbtMap nbt = data.getTag(); if (nbt != null && !nbt.isEmpty()) { @@ -198,7 +198,7 @@ public final class ItemTranslator { nbtMapBuilder.putIfAbsent("ench", NbtList.EMPTY); } - ItemData.Builder builder = javaItem.translateToBedrock(count, components, bedrockItem, session.getItemMappings()); + ItemData.Builder builder = javaItem.translateToBedrock(session, count, components, bedrockItem, session.getItemMappings()); // Finalize the Bedrock NBT builder.tag(nbtBuilder.build()); if (bedrockItem.isBlock()) { 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 db3f85946..61d58b722 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 @@ -36,6 +36,7 @@ import org.geysermc.geyser.GeyserImpl; import org.geysermc.geyser.level.block.property.Properties; import org.geysermc.geyser.level.block.type.BlockState; import org.geysermc.geyser.level.block.type.SkullBlock; +import org.geysermc.geyser.network.GameProtocol; import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.session.cache.SkullCache; import org.geysermc.geyser.skin.SkinProvider; @@ -57,7 +58,9 @@ public class SkullBlockEntityTranslator extends BlockEntityTranslator implements // Could be a wall skull block otherwise, which has rotation in its Bedrock state bedrockNbt.putFloat("Rotation", rotation * 22.5f); } - bedrockNbt.putByte("SkullType", (byte) (blockState.block() instanceof SkullBlock skull ? skull.skullType().bedrockId() : 0)); + if (GameProtocol.isPre1_21_40(session)) { + bedrockNbt.putByte("SkullType", (byte) (blockState.block() instanceof SkullBlock skull ? skull.skullType().bedrockId() : 0)); + } if (blockState.getValue(Properties.POWERED)) { bedrockNbt.putBoolean("MouthMoving", true); } 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 79e013246..5f3b9662f 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 @@ -25,13 +25,12 @@ package org.geysermc.geyser.translator.protocol.bedrock; -import org.geysermc.mcprotocollib.protocol.data.game.entity.player.Hand; -import org.geysermc.mcprotocollib.protocol.packet.ingame.serverbound.level.ServerboundPaddleBoatPacket; -import org.geysermc.mcprotocollib.protocol.packet.ingame.serverbound.player.ServerboundSwingPacket; import org.cloudburstmc.protocol.bedrock.packet.AnimatePacket; import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.translator.protocol.PacketTranslator; import org.geysermc.geyser.translator.protocol.Translator; +import org.geysermc.mcprotocollib.protocol.data.game.entity.player.Hand; +import org.geysermc.mcprotocollib.protocol.packet.ingame.serverbound.player.ServerboundSwingPacket; import java.util.concurrent.TimeUnit; @@ -45,45 +44,31 @@ public class BedrockAnimateTranslator extends PacketTranslator { return; } - switch (packet.getAction()) { - case SWING_ARM -> { - session.armSwingPending(); - // Delay so entity damage can be processed first - session.scheduleInEventLoop(() -> { - if (session.getArmAnimationTicks() != 0) { - // So, generally, a Java player can only do one *thing* at a time. - // If a player right-clicks, for example, then there's probably only one action associated with - // that right-click that will send a swing. - // The only exception I can think of to this, *maybe*, is a player dropping items - // Bedrock is a little funkier than this - it can send several arm animation packets in the - // same tick, notably with high levels of haste applied. - // Packet limiters do not like this and can crash the player. - // If arm animation ticks is 0, then we just sent an arm swing packet this tick. - // See https://github.com/GeyserMC/Geyser/issues/2875 - // This behavior was last touched on with ViaVersion 4.5.1 (with its packet limiter), Java 1.16.5, - // and Bedrock 1.19.51. - // Note for the future: we should probably largely ignore this packet and instead replicate - // all actions on our end, and send swings where needed. - session.sendDownstreamGamePacket(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 - session.setSteeringLeft(packet.getRowingTime() > 0.0); - ServerboundPaddleBoatPacket steerLeftPacket = new ServerboundPaddleBoatPacket(session.isSteeringLeft(), session.isSteeringRight()); - session.sendDownstreamGamePacket(steerLeftPacket); - } - case ROW_RIGHT -> { - session.setSteeringRight(packet.getRowingTime() > 0.0); - ServerboundPaddleBoatPacket steerRightPacket = new ServerboundPaddleBoatPacket(session.isSteeringLeft(), session.isSteeringRight()); - session.sendDownstreamGamePacket(steerRightPacket); - } + if (packet.getAction() == AnimatePacket.Action.SWING_ARM) { + session.armSwingPending(); + // Delay so entity damage can be processed first + session.scheduleInEventLoop(() -> { + if (session.getArmAnimationTicks() != 0) { + // So, generally, a Java player can only do one *thing* at a time. + // If a player right-clicks, for example, then there's probably only one action associated with + // that right-click that will send a swing. + // The only exception I can think of to this, *maybe*, is a player dropping items + // Bedrock is a little funkier than this - it can send several arm animation packets in the + // same tick, notably with high levels of haste applied. + // Packet limiters do not like this and can crash the player. + // If arm animation ticks is 0, then we just sent an arm swing packet this tick. + // See https://github.com/GeyserMC/Geyser/issues/2875 + // This behavior was last touched on with ViaVersion 4.5.1 (with its packet limiter), Java 1.16.5, + // and Bedrock 1.19.51. + // Note for the future: we should probably largely ignore this packet and instead replicate + // all actions on our end, and send swings where needed. + session.sendDownstreamGamePacket(new ServerboundSwingPacket(Hand.MAIN_HAND)); + session.activateArmAnimationTicking(); + } + }, + 25, + TimeUnit.MILLISECONDS + ); } } } diff --git a/core/src/main/java/org/geysermc/geyser/translator/protocol/bedrock/BedrockEntityPickRequestTranslator.java b/core/src/main/java/org/geysermc/geyser/translator/protocol/bedrock/BedrockEntityPickRequestTranslator.java index e85456c33..acb8573fb 100644 --- a/core/src/main/java/org/geysermc/geyser/translator/protocol/bedrock/BedrockEntityPickRequestTranslator.java +++ b/core/src/main/java/org/geysermc/geyser/translator/protocol/bedrock/BedrockEntityPickRequestTranslator.java @@ -51,29 +51,14 @@ public class BedrockEntityPickRequestTranslator extends PacketTranslator { - // Include type of boat in the name - int variant = ((BoatEntity) entity).getVariant(); - String typeOfBoat = switch (variant) { - case 1 -> "spruce"; - case 2 -> "birch"; - case 3 -> "jungle"; - case 4 -> "acacia"; - case 5 -> "cherry"; - case 6 -> "dark_oak"; - case 7 -> "mangrove"; - case 8 -> "bamboo"; - default -> "oak"; - }; - itemName = typeOfBoat + "_" + entity.getDefinition().entityType().name().toLowerCase(Locale.ROOT); - // Bamboo boat is a raft - if (variant == 8) { - itemName = itemName.replace("boat", "raft"); - } - } case LEASH_KNOT -> itemName = "lead"; case CHEST_MINECART, COMMAND_BLOCK_MINECART, FURNACE_MINECART, HOPPER_MINECART, TNT_MINECART -> // The Bedrock identifier matches the item name which moves MINECART to the end of the name diff --git a/core/src/main/java/org/geysermc/geyser/translator/protocol/bedrock/BedrockInventoryTransactionTranslator.java b/core/src/main/java/org/geysermc/geyser/translator/protocol/bedrock/BedrockInventoryTransactionTranslator.java index 62be015ce..04257db6b 100644 --- a/core/src/main/java/org/geysermc/geyser/translator/protocol/bedrock/BedrockInventoryTransactionTranslator.java +++ b/core/src/main/java/org/geysermc/geyser/translator/protocol/bedrock/BedrockInventoryTransactionTranslator.java @@ -30,7 +30,6 @@ import it.unimi.dsi.fastutil.ints.Int2ObjectMaps; import org.cloudburstmc.math.vector.Vector3d; import org.cloudburstmc.math.vector.Vector3f; import org.cloudburstmc.math.vector.Vector3i; -import org.cloudburstmc.protocol.bedrock.data.LevelEvent; import org.cloudburstmc.protocol.bedrock.data.SoundEvent; import org.cloudburstmc.protocol.bedrock.data.definitions.BlockDefinition; import org.cloudburstmc.protocol.bedrock.data.definitions.ItemDefinition; @@ -42,8 +41,8 @@ import org.cloudburstmc.protocol.bedrock.data.inventory.transaction.InventoryTra import org.cloudburstmc.protocol.bedrock.data.inventory.transaction.LegacySetItemSlotData; import org.cloudburstmc.protocol.bedrock.packet.ContainerOpenPacket; import org.cloudburstmc.protocol.bedrock.packet.InventoryTransactionPacket; -import org.cloudburstmc.protocol.bedrock.packet.LevelEventPacket; import org.cloudburstmc.protocol.bedrock.packet.LevelSoundEventPacket; +import org.cloudburstmc.protocol.bedrock.packet.PlaySoundPacket; import org.cloudburstmc.protocol.bedrock.packet.UpdateBlockPacket; import org.geysermc.geyser.entity.EntityDefinitions; import org.geysermc.geyser.entity.type.Entity; @@ -53,6 +52,7 @@ import org.geysermc.geyser.inventory.GeyserItemStack; import org.geysermc.geyser.inventory.Inventory; import org.geysermc.geyser.inventory.PlayerInventory; import org.geysermc.geyser.inventory.click.Click; +import org.geysermc.geyser.inventory.item.GeyserInstrument; import org.geysermc.geyser.item.Items; import org.geysermc.geyser.item.type.BlockItem; import org.geysermc.geyser.item.type.BoatItem; @@ -77,6 +77,7 @@ import org.geysermc.geyser.util.CooldownUtils; import org.geysermc.geyser.util.EntityUtils; import org.geysermc.geyser.util.InteractionResult; import org.geysermc.geyser.util.InventoryUtils; +import org.geysermc.geyser.util.SoundUtils; import org.geysermc.mcprotocollib.protocol.data.game.Holder; import org.geysermc.mcprotocollib.protocol.data.game.entity.object.Direction; import org.geysermc.mcprotocollib.protocol.data.game.entity.player.GameMode; @@ -187,7 +188,7 @@ public class BedrockInventoryTransactionTranslator extends PacketTranslator false; }; if (isGodBridging) { - restoreCorrectBlock(session, blockPos, packet); + restoreCorrectBlock(session, blockPos); return; } } @@ -207,7 +208,7 @@ public class BedrockInventoryTransactionTranslator extends PacketTranslator instrument = session.getPlayerInventory() + if (!session.getWorldCache().hasCooldown(session.getPlayerInventory().getItemInHand())) { + Holder holder = session.getPlayerInventory() .getItemInHand() .getComponent(DataComponentType.INSTRUMENT); - if (instrument != null && instrument.isId()) { - // BDS uses a LevelSoundEvent2Packet, but that doesn't work here... (as of 1.21.20) - LevelSoundEventPacket soundPacket = new LevelSoundEventPacket(); - soundPacket.setSound(SoundEvent.valueOf("GOAT_CALL_" + instrument.id())); - soundPacket.setPosition(session.getPlayerEntity().getPosition()); - soundPacket.setIdentifier("minecraft:player"); - soundPacket.setExtraData(-1); - session.sendUpstreamPacket(soundPacket); + if (holder != null) { + GeyserInstrument instrument = GeyserInstrument.fromHolder(session, holder); + if (instrument.bedrockInstrument() != null) { + // BDS uses a LevelSoundEvent2Packet, but that doesn't work here... (as of 1.21.20) + LevelSoundEventPacket soundPacket = new LevelSoundEventPacket(); + soundPacket.setSound(SoundEvent.valueOf("GOAT_CALL_" + instrument.bedrockInstrument().ordinal())); + soundPacket.setPosition(session.getPlayerEntity().getPosition()); + soundPacket.setIdentifier("minecraft:player"); + soundPacket.setExtraData(-1); + session.sendUpstreamPacket(soundPacket); + } else { + PlaySoundPacket playSoundPacket = new PlaySoundPacket(); + playSoundPacket.setPosition(session.getPlayerEntity().position()); + playSoundPacket.setSound(SoundUtils.translatePlaySound(instrument.soundEvent())); + playSoundPacket.setPitch(1.0F); + playSoundPacket.setVolume(instrument.range() / 16.0F); + session.sendUpstreamPacket(playSoundPacket); + } } } } @@ -423,53 +435,6 @@ public class BedrockInventoryTransactionTranslator extends PacketTranslator { - int blockState = session.getGameMode() == GameMode.CREATIVE ? - session.getGeyser().getWorldManager().getBlockAt(session, packet.getBlockPosition()) : session.getBreakingBlock(); - - session.setLastBlockPlaced(null); - session.setLastBlockPlacePosition(null); - - // Same deal with vanilla block placing as above. - if (!session.getWorldBorder().isInsideBorderBoundaries()) { - restoreCorrectBlock(session, packet.getBlockPosition(), packet); - return; - } - - Vector3f playerPosition = session.getPlayerEntity().getPosition(); - playerPosition = playerPosition.down(EntityDefinitions.PLAYER.offset() - session.getEyeHeight()); - - if (!canInteractWithBlock(session, playerPosition, packet.getBlockPosition())) { - restoreCorrectBlock(session, packet.getBlockPosition(), packet); - return; - } - - int sequence = session.getWorldCache().nextPredictionSequence(); - session.getWorldCache().markPositionInSequence(packet.getBlockPosition()); - // -1 means we don't know what block they're breaking - if (blockState == -1) { - blockState = Block.JAVA_AIR_ID; - } - - LevelEventPacket blockBreakPacket = new LevelEventPacket(); - blockBreakPacket.setType(LevelEvent.PARTICLE_DESTROY_BLOCK); - blockBreakPacket.setPosition(packet.getBlockPosition().toFloat()); - blockBreakPacket.setData(session.getBlockMappings().getBedrockBlockId(blockState)); - session.sendUpstreamPacket(blockBreakPacket); - session.setBreakingBlock(-1); - - Entity itemFrameEntity = ItemFrameEntity.getItemFrameEntity(session, packet.getBlockPosition()); - if (itemFrameEntity != null) { - ServerboundInteractPacket attackPacket = new ServerboundInteractPacket(itemFrameEntity.getEntityId(), - InteractAction.ATTACK, session.isSneaking()); - session.sendDownstreamGamePacket(attackPacket); - break; - } - - PlayerAction action = session.getGameMode() == GameMode.CREATIVE ? PlayerAction.START_DIGGING : PlayerAction.FINISH_DIGGING; - ServerboundPlayerActionPacket breakPacket = new ServerboundPlayerActionPacket(action, packet.getBlockPosition(), Direction.VALUES[packet.getBlockFace()], sequence); - session.sendDownstreamGamePacket(breakPacket); - } } break; case ITEM_RELEASE: @@ -549,7 +514,7 @@ public class BedrockInventoryTransactionTranslator extends PacketTranslator { - - @Override - public void translate(GeyserSession session, MoveEntityAbsolutePacket packet) { - session.setLastVehicleMoveTimestamp(System.currentTimeMillis()); - - Entity ridingEntity = session.getPlayerEntity().getVehicle(); - if (ridingEntity != null && session.getWorldBorder().isPassingIntoBorderBoundaries(packet.getPosition(), false)) { - Vector3f position = Vector3f.from(ridingEntity.getPosition().getX(), packet.getPosition().getY(), - ridingEntity.getPosition().getZ()); - if (ridingEntity instanceof BoatEntity) { - // Undo the changes usually applied to the boat - ridingEntity.as(BoatEntity.class) - .moveAbsoluteWithoutAdjustments(position, ridingEntity.getYaw(), - ridingEntity.isOnGround(), true); - } else { - // This doesn't work if teleported is false - ridingEntity.moveAbsolute(position, - ridingEntity.getYaw(), ridingEntity.getPitch(), ridingEntity.getHeadYaw(), - ridingEntity.isOnGround(), true); - } - return; - } - - float y = packet.getPosition().getY(); - if (ridingEntity instanceof BoatEntity && !ridingEntity.isOnGround()) { - // Remove the offset to prevents boats from looking like they're floating in water - y -= EntityDefinitions.BOAT.offset(); - } - ServerboundMoveVehiclePacket ServerboundMoveVehiclePacket = new ServerboundMoveVehiclePacket( - packet.getPosition().getX(), y, packet.getPosition().getZ(), - packet.getRotation().getY() - 90, packet.getRotation().getX() - ); - session.sendDownstreamGamePacket(ServerboundMoveVehiclePacket); - } -} diff --git a/core/src/main/java/org/geysermc/geyser/translator/protocol/bedrock/BedrockPlayerInputTranslator.java b/core/src/main/java/org/geysermc/geyser/translator/protocol/bedrock/BedrockPlayerInputTranslator.java deleted file mode 100644 index 1498c2184..000000000 --- a/core/src/main/java/org/geysermc/geyser/translator/protocol/bedrock/BedrockPlayerInputTranslator.java +++ /dev/null @@ -1,92 +0,0 @@ -/* - * Copyright (c) 2019-2022 GeyserMC. http://geysermc.org - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - * - * @author GeyserMC - * @link https://github.com/GeyserMC/Geyser - */ - -package org.geysermc.geyser.translator.protocol.bedrock; - -import org.geysermc.mcprotocollib.protocol.packet.ingame.serverbound.level.ServerboundMoveVehiclePacket; -import org.geysermc.mcprotocollib.protocol.packet.ingame.serverbound.level.ServerboundPlayerInputPacket; -import org.cloudburstmc.math.vector.Vector3f; -import org.cloudburstmc.protocol.bedrock.packet.PlayerInputPacket; -import org.geysermc.geyser.entity.EntityDefinitions; -import org.geysermc.geyser.entity.type.BoatEntity; -import org.geysermc.geyser.entity.type.Entity; -import org.geysermc.geyser.entity.type.living.animal.horse.AbstractHorseEntity; -import org.geysermc.geyser.entity.type.living.animal.horse.LlamaEntity; -import org.geysermc.geyser.session.GeyserSession; -import org.geysermc.geyser.translator.protocol.PacketTranslator; -import org.geysermc.geyser.translator.protocol.Translator; - -/** - * Sent by the client for minecarts and boats. - */ -@Translator(packet = PlayerInputPacket.class) -public class BedrockPlayerInputTranslator extends PacketTranslator { - - @Override - public void translate(GeyserSession session, PlayerInputPacket packet) { - ServerboundPlayerInputPacket playerInputPacket = new ServerboundPlayerInputPacket( - packet.getInputMotion().getX(), packet.getInputMotion().getY(), packet.isJumping(), packet.isSneaking() - ); - - session.sendDownstreamGamePacket(playerInputPacket); - - session.getPlayerEntity().setVehicleInput(packet.getInputMotion()); - - // Bedrock only sends movement vehicle packets while moving - // This allows horses to take damage while standing on magma - Entity vehicle = session.getPlayerEntity().getVehicle(); - boolean sendMovement = false; - if (vehicle instanceof AbstractHorseEntity && !(vehicle instanceof LlamaEntity)) { - sendMovement = vehicle.isOnGround(); - } else if (vehicle instanceof BoatEntity) { - if (vehicle.getPassengers().size() == 1) { - // The player is the only rider - sendMovement = true; - } else { - // Check if the player is the front rider - if (session.getPlayerEntity().isRidingInFront()) { - sendMovement = true; - } - } - } - if (sendMovement) { - long timeSinceVehicleMove = System.currentTimeMillis() - session.getLastVehicleMoveTimestamp(); - if (timeSinceVehicleMove >= 100) { - Vector3f vehiclePosition = vehicle.getPosition(); - - if (vehicle instanceof BoatEntity && !vehicle.isOnGround()) { - // Remove some Y position to prevents boats flying up - vehiclePosition = vehiclePosition.down(EntityDefinitions.BOAT.offset()); - } - - ServerboundMoveVehiclePacket moveVehiclePacket = new ServerboundMoveVehiclePacket( - vehiclePosition.getX(), vehiclePosition.getY(), vehiclePosition.getZ(), - vehicle.getYaw() - 90, vehicle.getPitch() - ); - session.sendDownstreamGamePacket(moveVehiclePacket); - } - } - } -} diff --git a/core/src/main/java/org/geysermc/geyser/translator/protocol/bedrock/entity/player/BedrockActionTranslator.java b/core/src/main/java/org/geysermc/geyser/translator/protocol/bedrock/entity/player/BedrockActionTranslator.java deleted file mode 100644 index cd1300a13..000000000 --- a/core/src/main/java/org/geysermc/geyser/translator/protocol/bedrock/entity/player/BedrockActionTranslator.java +++ /dev/null @@ -1,387 +0,0 @@ -/* - * Copyright (c) 2019-2022 GeyserMC. http://geysermc.org - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - * - * @author GeyserMC - * @link https://github.com/GeyserMC/Geyser - */ - -package org.geysermc.geyser.translator.protocol.bedrock.entity.player; - -import org.cloudburstmc.math.vector.Vector3f; -import org.cloudburstmc.math.vector.Vector3i; -import org.cloudburstmc.protocol.bedrock.data.LevelEvent; -import org.cloudburstmc.protocol.bedrock.data.PlayerActionType; -import org.cloudburstmc.protocol.bedrock.data.definitions.ItemDefinition; -import org.cloudburstmc.protocol.bedrock.data.entity.EntityEventType; -import org.cloudburstmc.protocol.bedrock.data.entity.EntityFlag; -import org.cloudburstmc.protocol.bedrock.packet.AnimatePacket; -import org.cloudburstmc.protocol.bedrock.packet.EntityEventPacket; -import org.cloudburstmc.protocol.bedrock.packet.LevelEventPacket; -import org.cloudburstmc.protocol.bedrock.packet.PlayStatusPacket; -import org.cloudburstmc.protocol.bedrock.packet.PlayerActionPacket; -import org.cloudburstmc.protocol.bedrock.packet.UpdateAttributesPacket; -import org.geysermc.geyser.api.block.custom.CustomBlockState; -import org.geysermc.geyser.entity.type.Entity; -import org.geysermc.geyser.entity.type.ItemFrameEntity; -import org.geysermc.geyser.entity.type.player.SessionPlayerEntity; -import org.geysermc.geyser.inventory.GeyserItemStack; -import org.geysermc.geyser.level.block.Blocks; -import org.geysermc.geyser.level.block.property.Properties; -import org.geysermc.geyser.level.block.type.Block; -import org.geysermc.geyser.level.block.type.BlockState; -import org.geysermc.geyser.registry.BlockRegistries; -import org.geysermc.geyser.registry.type.ItemMapping; -import org.geysermc.geyser.session.GeyserSession; -import org.geysermc.geyser.session.cache.SkullCache; -import org.geysermc.geyser.translator.item.CustomItemTranslator; -import org.geysermc.geyser.translator.protocol.PacketTranslator; -import org.geysermc.geyser.translator.protocol.Translator; -import org.geysermc.geyser.util.BlockUtils; -import org.geysermc.geyser.util.CooldownUtils; -import org.geysermc.mcprotocollib.protocol.data.game.entity.object.Direction; -import org.geysermc.mcprotocollib.protocol.data.game.entity.player.GameMode; -import org.geysermc.mcprotocollib.protocol.data.game.entity.player.Hand; -import org.geysermc.mcprotocollib.protocol.data.game.entity.player.InteractAction; -import org.geysermc.mcprotocollib.protocol.data.game.entity.player.PlayerAction; -import org.geysermc.mcprotocollib.protocol.data.game.entity.player.PlayerState; -import org.geysermc.mcprotocollib.protocol.packet.ingame.serverbound.player.ServerboundInteractPacket; -import org.geysermc.mcprotocollib.protocol.packet.ingame.serverbound.player.ServerboundPlayerAbilitiesPacket; -import org.geysermc.mcprotocollib.protocol.packet.ingame.serverbound.player.ServerboundPlayerActionPacket; -import org.geysermc.mcprotocollib.protocol.packet.ingame.serverbound.player.ServerboundPlayerCommandPacket; -import org.geysermc.mcprotocollib.protocol.packet.ingame.serverbound.player.ServerboundSwingPacket; -import org.geysermc.mcprotocollib.protocol.packet.ingame.serverbound.player.ServerboundUseItemOnPacket; - -@Translator(packet = PlayerActionPacket.class) -public class BedrockActionTranslator extends PacketTranslator { - - @Override - public void translate(GeyserSession session, PlayerActionPacket packet) { - SessionPlayerEntity entity = session.getPlayerEntity(); - - // Send book update before any player action - if (packet.getAction() != PlayerActionType.RESPAWN) { - session.getBookEditCache().checkForSend(); - } - - Vector3i vector = packet.getBlockPosition(); - - switch (packet.getAction()) { - case RESPAWN -> { - // Respawn process is finished and the server and client are both OK with respawning. - EntityEventPacket eventPacket = new EntityEventPacket(); - eventPacket.setRuntimeEntityId(entity.getGeyserId()); - eventPacket.setType(EntityEventType.RESPAWN); - eventPacket.setData(0); - session.sendUpstreamPacket(eventPacket); - // Resend attributes or else in rare cases the user can think they're not dead when they are, upon joining the server - UpdateAttributesPacket attributesPacket = new UpdateAttributesPacket(); - attributesPacket.setRuntimeEntityId(entity.getGeyserId()); - attributesPacket.getAttributes().addAll(entity.getAttributes().values()); - session.sendUpstreamPacket(attributesPacket); - - // Bounding box must be sent after a player dies and respawns since 1.19.40 - entity.updateBoundingBox(); - - // Needed here since 1.19.81 for dimension switching - session.getEntityCache().updateBossBars(); - } - case START_SWIMMING -> { - if (!entity.getFlag(EntityFlag.SWIMMING)) { - ServerboundPlayerCommandPacket startSwimPacket = new ServerboundPlayerCommandPacket(entity.getEntityId(), PlayerState.START_SPRINTING); - session.sendDownstreamGamePacket(startSwimPacket); - - session.setSwimming(true); - } - } - case STOP_SWIMMING -> { - // Prevent packet spam when Bedrock players are crawling near the edge of a block - if (!session.getCollisionManager().mustPlayerCrawlHere()) { - ServerboundPlayerCommandPacket stopSwimPacket = new ServerboundPlayerCommandPacket(entity.getEntityId(), PlayerState.STOP_SPRINTING); - session.sendDownstreamGamePacket(stopSwimPacket); - - session.setSwimming(false); - } - } - case START_GLIDE -> { - // Otherwise gliding will not work in creative - ServerboundPlayerAbilitiesPacket playerAbilitiesPacket = new ServerboundPlayerAbilitiesPacket(false); - session.sendDownstreamGamePacket(playerAbilitiesPacket); - sendPlayerGlideToggle(session, entity); - } - case STOP_GLIDE -> sendPlayerGlideToggle(session, entity); - case START_SNEAK -> { - ServerboundPlayerCommandPacket startSneakPacket = new ServerboundPlayerCommandPacket(entity.getEntityId(), PlayerState.START_SNEAKING); - session.sendDownstreamGamePacket(startSneakPacket); - - session.startSneaking(); - } - case STOP_SNEAK -> { - ServerboundPlayerCommandPacket stopSneakPacket = new ServerboundPlayerCommandPacket(entity.getEntityId(), PlayerState.STOP_SNEAKING); - session.sendDownstreamGamePacket(stopSneakPacket); - - session.stopSneaking(); - } - case START_SPRINT -> { - if (!entity.getFlag(EntityFlag.SWIMMING)) { - ServerboundPlayerCommandPacket startSprintPacket = new ServerboundPlayerCommandPacket(entity.getEntityId(), PlayerState.START_SPRINTING); - session.sendDownstreamGamePacket(startSprintPacket); - session.setSprinting(true); - } - } - case STOP_SPRINT -> { - if (!entity.getFlag(EntityFlag.SWIMMING)) { - ServerboundPlayerCommandPacket stopSprintPacket = new ServerboundPlayerCommandPacket(entity.getEntityId(), PlayerState.STOP_SPRINTING); - session.sendDownstreamGamePacket(stopSprintPacket); - } - session.setSprinting(false); - } - case DROP_ITEM -> { - ServerboundPlayerActionPacket dropItemPacket = new ServerboundPlayerActionPacket(PlayerAction.DROP_ITEM, - vector, Direction.VALUES[packet.getFace()], 0); - session.sendDownstreamGamePacket(dropItemPacket); - } - case STOP_SLEEP -> { - ServerboundPlayerCommandPacket stopSleepingPacket = new ServerboundPlayerCommandPacket(entity.getEntityId(), PlayerState.LEAVE_BED); - session.sendDownstreamGamePacket(stopSleepingPacket); - } - case START_BREAK -> { - // Ignore START_BREAK when the player is CREATIVE to avoid Spigot receiving 2 packets it interpets as block breaking. https://github.com/GeyserMC/Geyser/issues/4021 - if (session.getGameMode() == GameMode.CREATIVE) { - break; - } - - // Start the block breaking animation - int blockState = session.getGeyser().getWorldManager().getBlockAt(session, vector); - LevelEventPacket startBreak = new LevelEventPacket(); - startBreak.setType(LevelEvent.BLOCK_START_BREAK); - startBreak.setPosition(vector.toFloat()); - double breakTime = BlockUtils.getSessionBreakTime(session, BlockState.of(blockState).block()) * 20; - - // If the block is custom or the breaking item is custom, we must keep track of break time ourselves - GeyserItemStack item = session.getPlayerInventory().getItemInHand(); - ItemMapping mapping = item.getMapping(session); - ItemDefinition customItem = mapping.isTool() ? CustomItemTranslator.getCustomItem(item.getComponents(), mapping) : null; - CustomBlockState blockStateOverride = BlockRegistries.CUSTOM_BLOCK_STATE_OVERRIDES.get(blockState); - SkullCache.Skull skull = session.getSkullCache().getSkulls().get(vector); - - session.setBlockBreakStartTime(0); - if (blockStateOverride != null || customItem != null || (skull != null && skull.getBlockDefinition() != null)) { - session.setBlockBreakStartTime(System.currentTimeMillis()); - } - startBreak.setData((int) (65535 / breakTime)); - session.setBreakingBlock(blockState); - session.sendUpstreamPacket(startBreak); - - // Account for fire - the client likes to hit the block behind. - Vector3i fireBlockPos = BlockUtils.getBlockPosition(vector, packet.getFace()); - Block block = session.getGeyser().getWorldManager().blockAt(session, fireBlockPos).block(); - Direction direction = Direction.VALUES[packet.getFace()]; - if (block == Blocks.FIRE || block == Blocks.SOUL_FIRE) { - ServerboundPlayerActionPacket startBreakingPacket = new ServerboundPlayerActionPacket(PlayerAction.START_DIGGING, fireBlockPos, - direction, session.getWorldCache().nextPredictionSequence()); - session.sendDownstreamGamePacket(startBreakingPacket); - } - - ServerboundPlayerActionPacket startBreakingPacket = new ServerboundPlayerActionPacket(PlayerAction.START_DIGGING, - vector, direction, session.getWorldCache().nextPredictionSequence()); - session.sendDownstreamGamePacket(startBreakingPacket); - - spawnBlockBreakParticles(session, direction, vector, BlockState.of(blockState)); - } - case CONTINUE_BREAK -> { - if (session.getGameMode() == GameMode.CREATIVE) { - break; - } - int breakingBlock = session.getBreakingBlock(); - if (breakingBlock == -1) { - breakingBlock = Block.JAVA_AIR_ID; - } - - Vector3f vectorFloat = vector.toFloat(); - - BlockState breakingBlockState = BlockState.of(breakingBlock); - Direction direction = Direction.VALUES[packet.getFace()]; - spawnBlockBreakParticles(session, direction, vector, breakingBlockState); - - double breakTime = BlockUtils.getSessionBreakTime(session, breakingBlockState.block()) * 20; - // If the block is custom, we must keep track of when it should break ourselves - long blockBreakStartTime = session.getBlockBreakStartTime(); - if (blockBreakStartTime != 0) { - long timeSinceStart = System.currentTimeMillis() - blockBreakStartTime; - // We need to add a slight delay to the break time, otherwise the client breaks blocks too fast - if (timeSinceStart >= (breakTime += 2) * 50) { - // Play break sound and particle - LevelEventPacket effectPacket = new LevelEventPacket(); - effectPacket.setPosition(vectorFloat); - effectPacket.setType(LevelEvent.PARTICLE_DESTROY_BLOCK); - effectPacket.setData(session.getBlockMappings().getBedrockBlockId(breakingBlock)); - session.sendUpstreamPacket(effectPacket); - - // Break the block - ServerboundPlayerActionPacket finishBreakingPacket = new ServerboundPlayerActionPacket(PlayerAction.FINISH_DIGGING, - vector, direction, session.getWorldCache().nextPredictionSequence()); - session.sendDownstreamGamePacket(finishBreakingPacket); - session.setBlockBreakStartTime(0); - break; - } - } - // Update the break time in the event that player conditions changed (jumping, effects applied) - LevelEventPacket updateBreak = new LevelEventPacket(); - updateBreak.setType(LevelEvent.BLOCK_UPDATE_BREAK); - updateBreak.setPosition(vectorFloat); - updateBreak.setData((int) (65535 / breakTime)); - session.sendUpstreamPacket(updateBreak); - } - case ABORT_BREAK -> { - if (session.getGameMode() != GameMode.CREATIVE) { - // As of 1.16.210: item frame items are taken out here. - // Survival also sends START_BREAK, but by attaching our process here adventure mode also works - Entity itemFrameEntity = ItemFrameEntity.getItemFrameEntity(session, vector); - if (itemFrameEntity != null) { - ServerboundInteractPacket interactPacket = new ServerboundInteractPacket(itemFrameEntity.getEntityId(), - InteractAction.ATTACK, Hand.MAIN_HAND, session.isSneaking()); - session.sendDownstreamGamePacket(interactPacket); - break; - } - } - - ServerboundPlayerActionPacket abortBreakingPacket = new ServerboundPlayerActionPacket(PlayerAction.CANCEL_DIGGING, vector, Direction.DOWN, 0); - session.sendDownstreamGamePacket(abortBreakingPacket); - LevelEventPacket stopBreak = new LevelEventPacket(); - stopBreak.setType(LevelEvent.BLOCK_STOP_BREAK); - stopBreak.setPosition(vector.toFloat()); - stopBreak.setData(0); - session.setBreakingBlock(-1); - session.sendUpstreamPacket(stopBreak); - } - // Handled in BedrockInventoryTransactionTranslator - case STOP_BREAK -> { - } - case DIMENSION_CHANGE_SUCCESS -> { - //sometimes the client doesn't feel like loading - PlayStatusPacket spawnPacket = new PlayStatusPacket(); - spawnPacket.setStatus(PlayStatusPacket.Status.PLAYER_SPAWN); - session.sendUpstreamPacket(spawnPacket); - - UpdateAttributesPacket attributesPacket = new UpdateAttributesPacket(); - attributesPacket.setRuntimeEntityId(entity.getGeyserId()); - attributesPacket.getAttributes().addAll(entity.getAttributes().values()); - session.sendUpstreamPacket(attributesPacket); - } - case JUMP -> entity.setOnGround(false); // Increase block break time while jumping - case MISSED_SWING -> { - // Java edition sends a cooldown when hitting air. - // Normally handled by BedrockLevelSoundEventTranslator, but there is no sound on Java for this. - CooldownUtils.sendCooldown(session); - - // TODO Re-evaluate after pre-1.20.10 is no longer supported? - if (session.getArmAnimationTicks() == -1) { - session.sendDownstreamGamePacket(new ServerboundSwingPacket(Hand.MAIN_HAND)); - session.activateArmAnimationTicking(); - - // Send packet to Bedrock so it knows - AnimatePacket animatePacket = new AnimatePacket(); - animatePacket.setRuntimeEntityId(session.getPlayerEntity().getGeyserId()); - animatePacket.setAction(AnimatePacket.Action.SWING_ARM); - session.sendUpstreamPacket(animatePacket); - } - } - case START_FLYING -> { // Since 1.20.30 - if (session.isCanFly()) { - if (session.getGameMode() == GameMode.SPECTATOR) { - // should already be flying - session.sendAdventureSettings(); - break; - } - - if (session.getPlayerEntity().getFlag(EntityFlag.SWIMMING) && session.getCollisionManager().isPlayerInWater()) { - // As of 1.18.1, Java Edition cannot fly while in water, but it can fly while crawling - // If this isn't present, swimming on a 1.13.2 server and then attempting to fly will put you into a flying/swimming state that is invalid on JE - session.sendAdventureSettings(); - break; - } - - session.setFlying(true); - session.sendDownstreamGamePacket(new ServerboundPlayerAbilitiesPacket(true)); - } else { - // update whether we can fly - session.sendAdventureSettings(); - // stop flying - PlayerActionPacket stopFlyingPacket = new PlayerActionPacket(); - stopFlyingPacket.setRuntimeEntityId(session.getPlayerEntity().getGeyserId()); - stopFlyingPacket.setAction(PlayerActionType.STOP_FLYING); - stopFlyingPacket.setBlockPosition(Vector3i.ZERO); - stopFlyingPacket.setResultPosition(Vector3i.ZERO); - stopFlyingPacket.setFace(0); - session.sendUpstreamPacket(stopFlyingPacket); - } - } - case STOP_FLYING -> { - session.setFlying(false); - session.sendDownstreamGamePacket(new ServerboundPlayerAbilitiesPacket(false)); - } - case DIMENSION_CHANGE_REQUEST_OR_CREATIVE_DESTROY_BLOCK -> { // Used by client to get book from lecterns and items from item frame in creative mode since 1.20.70 - BlockState state = session.getGeyser().getWorldManager().blockAt(session, vector); - - if (state.getValue(Properties.HAS_BOOK, false)) { - session.setDroppingLecternBook(true); - - ServerboundUseItemOnPacket blockPacket = new ServerboundUseItemOnPacket( - vector, - Direction.DOWN, - Hand.MAIN_HAND, - 0, 0, 0, - false, - session.getWorldCache().nextPredictionSequence()); - session.sendDownstreamGamePacket(blockPacket); - break; - } - - Entity itemFrame = ItemFrameEntity.getItemFrameEntity(session, packet.getBlockPosition()); - if (itemFrame != null) { - ServerboundInteractPacket interactPacket = new ServerboundInteractPacket(itemFrame.getEntityId(), - InteractAction.ATTACK, Hand.MAIN_HAND, session.isSneaking()); - session.sendDownstreamGamePacket(interactPacket); - } - } - } - } - - private void spawnBlockBreakParticles(GeyserSession session, Direction direction, Vector3i position, BlockState blockState) { - LevelEventPacket levelEventPacket = new LevelEventPacket(); - switch (direction) { - case UP -> levelEventPacket.setType(LevelEvent.PARTICLE_BREAK_BLOCK_UP); - case DOWN -> levelEventPacket.setType(LevelEvent.PARTICLE_BREAK_BLOCK_DOWN); - case NORTH -> levelEventPacket.setType(LevelEvent.PARTICLE_BREAK_BLOCK_NORTH); - case EAST -> levelEventPacket.setType(LevelEvent.PARTICLE_BREAK_BLOCK_EAST); - case SOUTH -> levelEventPacket.setType(LevelEvent.PARTICLE_BREAK_BLOCK_SOUTH); - case WEST -> levelEventPacket.setType(LevelEvent.PARTICLE_BREAK_BLOCK_WEST); - } - levelEventPacket.setPosition(position.toFloat()); - levelEventPacket.setData(session.getBlockMappings().getBedrockBlock(blockState).getRuntimeId()); - session.sendUpstreamPacket(levelEventPacket); - } - - private void sendPlayerGlideToggle(GeyserSession session, Entity entity) { - ServerboundPlayerCommandPacket glidePacket = new ServerboundPlayerCommandPacket(entity.getEntityId(), PlayerState.START_ELYTRA_FLYING); - session.sendDownstreamGamePacket(glidePacket); - } -} 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 20c1b055d..2df77ad16 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 @@ -25,16 +25,11 @@ package org.geysermc.geyser.translator.protocol.bedrock.entity.player; -import org.geysermc.mcprotocollib.protocol.data.game.entity.player.Hand; -import org.geysermc.mcprotocollib.protocol.data.game.entity.player.InteractAction; -import org.geysermc.mcprotocollib.protocol.data.game.entity.player.PlayerState; -import org.geysermc.mcprotocollib.protocol.data.game.entity.type.EntityType; -import org.geysermc.mcprotocollib.protocol.packet.ingame.serverbound.player.ServerboundInteractPacket; -import org.geysermc.mcprotocollib.protocol.packet.ingame.serverbound.player.ServerboundPlayerCommandPacket; import org.cloudburstmc.protocol.bedrock.data.entity.EntityDataTypes; import org.cloudburstmc.protocol.bedrock.data.entity.EntityLinkData; import org.cloudburstmc.protocol.bedrock.packet.InteractPacket; import org.cloudburstmc.protocol.bedrock.packet.SetEntityLinkPacket; +import org.geysermc.geyser.entity.type.ChestBoatEntity; import org.geysermc.geyser.entity.type.Entity; import org.geysermc.geyser.entity.type.living.animal.horse.AbstractHorseEntity; import org.geysermc.geyser.item.Items; @@ -42,6 +37,11 @@ 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.InventoryUtils; +import org.geysermc.mcprotocollib.protocol.data.game.entity.player.Hand; +import org.geysermc.mcprotocollib.protocol.data.game.entity.player.InteractAction; +import org.geysermc.mcprotocollib.protocol.data.game.entity.player.PlayerState; +import org.geysermc.mcprotocollib.protocol.packet.ingame.serverbound.player.ServerboundInteractPacket; +import org.geysermc.mcprotocollib.protocol.packet.ingame.serverbound.player.ServerboundPlayerCommandPacket; import java.util.concurrent.TimeUnit; @@ -119,7 +119,7 @@ public class BedrockInteractTranslator extends PacketTranslator case OPEN_INVENTORY: if (session.getOpenInventory() == null) { Entity ridingEntity = session.getPlayerEntity().getVehicle(); - if (ridingEntity instanceof AbstractHorseEntity || (ridingEntity != null && ridingEntity.getDefinition().entityType() == EntityType.CHEST_BOAT)) { + if (ridingEntity instanceof AbstractHorseEntity || ridingEntity instanceof ChestBoatEntity) { // This mob has an inventory of its own that we should open instead. ServerboundPlayerCommandPacket openVehicleWindowPacket = new ServerboundPlayerCommandPacket(session.getPlayerEntity().getEntityId(), PlayerState.OPEN_VEHICLE_INVENTORY); session.sendDownstreamGamePacket(openVehicleWindowPacket); diff --git a/core/src/main/java/org/geysermc/geyser/translator/protocol/bedrock/entity/player/BedrockPlayerActionTranslator.java b/core/src/main/java/org/geysermc/geyser/translator/protocol/bedrock/entity/player/BedrockPlayerActionTranslator.java new file mode 100644 index 000000000..797505e99 --- /dev/null +++ b/core/src/main/java/org/geysermc/geyser/translator/protocol/bedrock/entity/player/BedrockPlayerActionTranslator.java @@ -0,0 +1,121 @@ +/* + * Copyright (c) 2024 GeyserMC. http://geysermc.org + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * @author GeyserMC + * @link https://github.com/GeyserMC/Geyser + */ + +package org.geysermc.geyser.translator.protocol.bedrock.entity.player; + +import org.cloudburstmc.math.vector.Vector3i; +import org.cloudburstmc.protocol.bedrock.data.entity.EntityEventType; +import org.cloudburstmc.protocol.bedrock.packet.EntityEventPacket; +import org.cloudburstmc.protocol.bedrock.packet.PlayStatusPacket; +import org.cloudburstmc.protocol.bedrock.packet.PlayerActionPacket; +import org.cloudburstmc.protocol.bedrock.packet.UpdateAttributesPacket; +import org.geysermc.geyser.entity.type.Entity; +import org.geysermc.geyser.entity.type.ItemFrameEntity; +import org.geysermc.geyser.entity.type.player.SessionPlayerEntity; +import org.geysermc.geyser.level.block.property.Properties; +import org.geysermc.geyser.level.block.type.BlockState; +import org.geysermc.geyser.session.GeyserSession; +import org.geysermc.geyser.translator.protocol.PacketTranslator; +import org.geysermc.geyser.translator.protocol.Translator; +import org.geysermc.mcprotocollib.protocol.data.game.entity.object.Direction; +import org.geysermc.mcprotocollib.protocol.data.game.entity.player.Hand; +import org.geysermc.mcprotocollib.protocol.data.game.entity.player.InteractAction; +import org.geysermc.mcprotocollib.protocol.data.game.entity.player.PlayerState; +import org.geysermc.mcprotocollib.protocol.packet.ingame.serverbound.player.ServerboundInteractPacket; +import org.geysermc.mcprotocollib.protocol.packet.ingame.serverbound.player.ServerboundPlayerCommandPacket; +import org.geysermc.mcprotocollib.protocol.packet.ingame.serverbound.player.ServerboundUseItemOnPacket; + +@Translator(packet = PlayerActionPacket.class) +public class BedrockPlayerActionTranslator extends PacketTranslator { + + @Override + public void translate(GeyserSession session, PlayerActionPacket packet) { + // This packet was used more before server auth movement was needed, but it's still used for a couple things... + switch (packet.getAction()) { + case RESPAWN -> { + SessionPlayerEntity entity = session.getPlayerEntity(); + // Respawn process is finished and the server and client are both OK with respawning. + EntityEventPacket eventPacket = new EntityEventPacket(); + eventPacket.setRuntimeEntityId(entity.getGeyserId()); + eventPacket.setType(EntityEventType.RESPAWN); + eventPacket.setData(0); + session.sendUpstreamPacket(eventPacket); + // Resend attributes or else in rare cases the user can think they're not dead when they are, upon joining the server + UpdateAttributesPacket attributesPacket = new UpdateAttributesPacket(); + attributesPacket.setRuntimeEntityId(entity.getGeyserId()); + attributesPacket.getAttributes().addAll(entity.getAttributes().values()); + session.sendUpstreamPacket(attributesPacket); + + // Bounding box must be sent after a player dies and respawns since 1.19.40 + entity.updateBoundingBox(); + + // Needed here since 1.19.81 for dimension switching + session.getEntityCache().updateBossBars(); + } + case STOP_SLEEP -> { + ServerboundPlayerCommandPacket stopSleepingPacket = new ServerboundPlayerCommandPacket(session.getPlayerEntity().getEntityId(), PlayerState.LEAVE_BED); + session.sendDownstreamGamePacket(stopSleepingPacket); + } + case DIMENSION_CHANGE_REQUEST_OR_CREATIVE_DESTROY_BLOCK -> { // Used by client to get book from lecterns and items from item frame in creative mode since 1.20.70 + Vector3i vector = packet.getBlockPosition(); + BlockState state = session.getGeyser().getWorldManager().blockAt(session, vector); + + if (state.getValue(Properties.HAS_BOOK, false)) { + session.setDroppingLecternBook(true); + + ServerboundUseItemOnPacket blockPacket = new ServerboundUseItemOnPacket( + vector, + Direction.DOWN, + Hand.MAIN_HAND, + 0, 0, 0, + false, + false, + session.getWorldCache().nextPredictionSequence()); + session.sendDownstreamGamePacket(blockPacket); + break; + } + + Entity itemFrame = ItemFrameEntity.getItemFrameEntity(session, vector); + if (itemFrame != null) { + ServerboundInteractPacket interactPacket = new ServerboundInteractPacket(itemFrame.getEntityId(), + InteractAction.ATTACK, Hand.MAIN_HAND, session.isSneaking()); + session.sendDownstreamGamePacket(interactPacket); + } + } + case DIMENSION_CHANGE_SUCCESS -> { + SessionPlayerEntity entity = session.getPlayerEntity(); + // Sometimes the client doesn't feel like loading + PlayStatusPacket spawnPacket = new PlayStatusPacket(); + spawnPacket.setStatus(PlayStatusPacket.Status.PLAYER_SPAWN); + session.sendUpstreamPacket(spawnPacket); + + UpdateAttributesPacket attributesPacket = new UpdateAttributesPacket(); + attributesPacket.setRuntimeEntityId(entity.getGeyserId()); + attributesPacket.getAttributes().addAll(entity.getAttributes().values()); + session.sendUpstreamPacket(attributesPacket); + } + } + } +} diff --git a/core/src/main/java/org/geysermc/geyser/translator/protocol/bedrock/entity/player/input/BedrockBlockActions.java b/core/src/main/java/org/geysermc/geyser/translator/protocol/bedrock/entity/player/input/BedrockBlockActions.java new file mode 100644 index 000000000..061a04b77 --- /dev/null +++ b/core/src/main/java/org/geysermc/geyser/translator/protocol/bedrock/entity/player/input/BedrockBlockActions.java @@ -0,0 +1,210 @@ +/* + * Copyright (c) 2019-2024 GeyserMC. http://geysermc.org + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * @author GeyserMC + * @link https://github.com/GeyserMC/Geyser + */ + +package org.geysermc.geyser.translator.protocol.bedrock.entity.player.input; + +import org.cloudburstmc.math.vector.Vector3f; +import org.cloudburstmc.math.vector.Vector3i; +import org.cloudburstmc.protocol.bedrock.data.LevelEvent; +import org.cloudburstmc.protocol.bedrock.data.PlayerActionType; +import org.cloudburstmc.protocol.bedrock.data.PlayerBlockActionData; +import org.cloudburstmc.protocol.bedrock.data.definitions.ItemDefinition; +import org.cloudburstmc.protocol.bedrock.packet.LevelEventPacket; +import org.geysermc.geyser.api.block.custom.CustomBlockState; +import org.geysermc.geyser.entity.type.Entity; +import org.geysermc.geyser.entity.type.ItemFrameEntity; +import org.geysermc.geyser.inventory.GeyserItemStack; +import org.geysermc.geyser.level.block.Blocks; +import org.geysermc.geyser.level.block.type.Block; +import org.geysermc.geyser.level.block.type.BlockState; +import org.geysermc.geyser.registry.BlockRegistries; +import org.geysermc.geyser.registry.type.ItemMapping; +import org.geysermc.geyser.session.GeyserSession; +import org.geysermc.geyser.session.cache.SkullCache; +import org.geysermc.geyser.translator.item.CustomItemTranslator; +import org.geysermc.geyser.util.BlockUtils; +import org.geysermc.mcprotocollib.protocol.data.game.entity.object.Direction; +import org.geysermc.mcprotocollib.protocol.data.game.entity.player.GameMode; +import org.geysermc.mcprotocollib.protocol.data.game.entity.player.Hand; +import org.geysermc.mcprotocollib.protocol.data.game.entity.player.InteractAction; +import org.geysermc.mcprotocollib.protocol.data.game.entity.player.PlayerAction; +import org.geysermc.mcprotocollib.protocol.packet.ingame.serverbound.player.ServerboundInteractPacket; +import org.geysermc.mcprotocollib.protocol.packet.ingame.serverbound.player.ServerboundPlayerActionPacket; + +import java.util.List; + +final class BedrockBlockActions { + + static void translate(GeyserSession session, List playerActions) { + // Send book update before any player action + session.getBookEditCache().checkForSend(); + + for (PlayerBlockActionData blockActionData : playerActions) { + handle(session, blockActionData); + } + } + + private static void handle(GeyserSession session, PlayerBlockActionData blockActionData) { + PlayerActionType action = blockActionData.getAction(); + Vector3i vector = blockActionData.getBlockPosition(); + int blockFace = blockActionData.getFace(); + + switch (action) { + case DROP_ITEM -> { + ServerboundPlayerActionPacket dropItemPacket = new ServerboundPlayerActionPacket(PlayerAction.DROP_ITEM, + vector, Direction.VALUES[blockFace], 0); + session.sendDownstreamGamePacket(dropItemPacket); + } + case START_BREAK -> { + // Ignore START_BREAK when the player is CREATIVE to avoid Spigot receiving 2 packets it interpets as block breaking. https://github.com/GeyserMC/Geyser/issues/4021 + if (session.getGameMode() == GameMode.CREATIVE) { + break; + } + + // Start the block breaking animation + int blockState = session.getGeyser().getWorldManager().getBlockAt(session, vector); + LevelEventPacket startBreak = new LevelEventPacket(); + startBreak.setType(LevelEvent.BLOCK_START_BREAK); + startBreak.setPosition(vector.toFloat()); + double breakTime = BlockUtils.getSessionBreakTime(session, BlockState.of(blockState).block()) * 20; + + // If the block is custom or the breaking item is custom, we must keep track of break time ourselves + GeyserItemStack item = session.getPlayerInventory().getItemInHand(); + ItemMapping mapping = item.getMapping(session); + ItemDefinition customItem = mapping.isTool() ? CustomItemTranslator.getCustomItem(item.getComponents(), mapping) : null; + CustomBlockState blockStateOverride = BlockRegistries.CUSTOM_BLOCK_STATE_OVERRIDES.get(blockState); + SkullCache.Skull skull = session.getSkullCache().getSkulls().get(vector); + + session.setBlockBreakStartTime(0); + if (blockStateOverride != null || customItem != null || (skull != null && skull.getBlockDefinition() != null)) { + session.setBlockBreakStartTime(System.currentTimeMillis()); + } + startBreak.setData((int) (65535 / breakTime)); + session.setBreakingBlock(blockState); + session.sendUpstreamPacket(startBreak); + + // Account for fire - the client likes to hit the block behind. + Vector3i fireBlockPos = BlockUtils.getBlockPosition(vector, blockFace); + Block block = session.getGeyser().getWorldManager().blockAt(session, fireBlockPos).block(); + Direction direction = Direction.VALUES[blockFace]; + if (block == Blocks.FIRE || block == Blocks.SOUL_FIRE) { + ServerboundPlayerActionPacket startBreakingPacket = new ServerboundPlayerActionPacket(PlayerAction.START_DIGGING, fireBlockPos, + direction, session.getWorldCache().nextPredictionSequence()); + session.sendDownstreamGamePacket(startBreakingPacket); + } + + ServerboundPlayerActionPacket startBreakingPacket = new ServerboundPlayerActionPacket(PlayerAction.START_DIGGING, + vector, direction, session.getWorldCache().nextPredictionSequence()); + session.sendDownstreamGamePacket(startBreakingPacket); + + spawnBlockBreakParticles(session, direction, vector, BlockState.of(blockState)); + } + case CONTINUE_BREAK -> { + if (session.getGameMode() == GameMode.CREATIVE) { + break; + } + int breakingBlock = session.getBreakingBlock(); + if (breakingBlock == -1) { + breakingBlock = Block.JAVA_AIR_ID; + } + + Vector3f vectorFloat = vector.toFloat(); + + BlockState breakingBlockState = BlockState.of(breakingBlock); + Direction direction = Direction.VALUES[blockFace]; + spawnBlockBreakParticles(session, direction, vector, breakingBlockState); + + double breakTime = BlockUtils.getSessionBreakTime(session, breakingBlockState.block()) * 20; + // If the block is custom, we must keep track of when it should break ourselves + long blockBreakStartTime = session.getBlockBreakStartTime(); + if (blockBreakStartTime != 0) { + long timeSinceStart = System.currentTimeMillis() - blockBreakStartTime; + // We need to add a slight delay to the break time, otherwise the client breaks blocks too fast + if (timeSinceStart >= (breakTime += 2) * 50) { + // Play break sound and particle + LevelEventPacket effectPacket = new LevelEventPacket(); + effectPacket.setPosition(vectorFloat); + effectPacket.setType(LevelEvent.PARTICLE_DESTROY_BLOCK); + effectPacket.setData(session.getBlockMappings().getBedrockBlockId(breakingBlock)); + session.sendUpstreamPacket(effectPacket); + + // Break the block + ServerboundPlayerActionPacket finishBreakingPacket = new ServerboundPlayerActionPacket(PlayerAction.FINISH_DIGGING, + vector, direction, session.getWorldCache().nextPredictionSequence()); + session.sendDownstreamGamePacket(finishBreakingPacket); + session.setBlockBreakStartTime(0); + break; + } + } + // Update the break time in the event that player conditions changed (jumping, effects applied) + LevelEventPacket updateBreak = new LevelEventPacket(); + updateBreak.setType(LevelEvent.BLOCK_UPDATE_BREAK); + updateBreak.setPosition(vectorFloat); + updateBreak.setData((int) (65535 / breakTime)); + session.sendUpstreamPacket(updateBreak); + } + case ABORT_BREAK -> { + if (session.getGameMode() != GameMode.CREATIVE) { + // As of 1.16.210: item frame items are taken out here. + // Survival also sends START_BREAK, but by attaching our process here adventure mode also works + Entity itemFrameEntity = ItemFrameEntity.getItemFrameEntity(session, vector); + if (itemFrameEntity != null) { + ServerboundInteractPacket interactPacket = new ServerboundInteractPacket(itemFrameEntity.getEntityId(), + InteractAction.ATTACK, Hand.MAIN_HAND, session.isSneaking()); + session.sendDownstreamGamePacket(interactPacket); + break; + } + } + + ServerboundPlayerActionPacket abortBreakingPacket = new ServerboundPlayerActionPacket(PlayerAction.CANCEL_DIGGING, vector, Direction.DOWN, 0); + session.sendDownstreamGamePacket(abortBreakingPacket); + LevelEventPacket stopBreak = new LevelEventPacket(); + stopBreak.setType(LevelEvent.BLOCK_STOP_BREAK); + stopBreak.setPosition(vector.toFloat()); + stopBreak.setData(0); + session.setBreakingBlock(-1); + session.sendUpstreamPacket(stopBreak); + } + // Handled in BedrockInventoryTransactionTranslator + case STOP_BREAK -> { + } + } + } + + private static void spawnBlockBreakParticles(GeyserSession session, Direction direction, Vector3i position, BlockState blockState) { + LevelEventPacket levelEventPacket = new LevelEventPacket(); + switch (direction) { + case UP -> levelEventPacket.setType(LevelEvent.PARTICLE_BREAK_BLOCK_UP); + case DOWN -> levelEventPacket.setType(LevelEvent.PARTICLE_BREAK_BLOCK_DOWN); + case NORTH -> levelEventPacket.setType(LevelEvent.PARTICLE_BREAK_BLOCK_NORTH); + case EAST -> levelEventPacket.setType(LevelEvent.PARTICLE_BREAK_BLOCK_EAST); + case SOUTH -> levelEventPacket.setType(LevelEvent.PARTICLE_BREAK_BLOCK_SOUTH); + case WEST -> levelEventPacket.setType(LevelEvent.PARTICLE_BREAK_BLOCK_WEST); + } + levelEventPacket.setPosition(position.toFloat()); + levelEventPacket.setData(session.getBlockMappings().getBedrockBlock(blockState).getRuntimeId()); + session.sendUpstreamPacket(levelEventPacket); + } +} 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/input/BedrockMovePlayer.java similarity index 68% rename from core/src/main/java/org/geysermc/geyser/translator/protocol/bedrock/entity/player/BedrockMovePlayerTranslator.java rename to core/src/main/java/org/geysermc/geyser/translator/protocol/bedrock/entity/player/input/BedrockMovePlayer.java index ee80cac16..6abb3899a 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/input/BedrockMovePlayer.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019-2022 GeyserMC. http://geysermc.org + * Copyright (c) 2019-2024 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,42 +23,47 @@ * @link https://github.com/GeyserMC/Geyser */ -package org.geysermc.geyser.translator.protocol.bedrock.entity.player; +package org.geysermc.geyser.translator.protocol.bedrock.entity.player.input; +import net.kyori.adventure.util.TriState; import org.cloudburstmc.math.vector.Vector3d; import org.cloudburstmc.math.vector.Vector3f; -import org.cloudburstmc.protocol.bedrock.packet.MovePlayerPacket; +import org.cloudburstmc.protocol.bedrock.data.PlayerAuthInputData; +import org.cloudburstmc.protocol.bedrock.packet.PlayerAuthInputPacket; import org.geysermc.geyser.entity.EntityDefinitions; import org.geysermc.geyser.entity.type.player.SessionPlayerEntity; -import org.geysermc.geyser.entity.vehicle.ClientVehicle; +import org.geysermc.geyser.level.physics.CollisionResult; +import org.geysermc.geyser.network.GameProtocol; 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.mcprotocollib.network.packet.Packet; import org.geysermc.mcprotocollib.protocol.packet.ingame.serverbound.player.ServerboundMovePlayerPosPacket; import org.geysermc.mcprotocollib.protocol.packet.ingame.serverbound.player.ServerboundMovePlayerPosRotPacket; import org.geysermc.mcprotocollib.protocol.packet.ingame.serverbound.player.ServerboundMovePlayerRotPacket; +import org.geysermc.mcprotocollib.protocol.packet.ingame.serverbound.player.ServerboundMovePlayerStatusOnlyPacket; -@Translator(packet = MovePlayerPacket.class) -public class BedrockMovePlayerTranslator extends PacketTranslator { +/** + * Holds processing input coming in from the {@link PlayerAuthInputPacket} packet. + */ +final class BedrockMovePlayer { - @Override - public void translate(GeyserSession session, MovePlayerPacket packet) { + static void translate(GeyserSession session, PlayerAuthInputPacket packet) { SessionPlayerEntity entity = session.getPlayerEntity(); if (!session.isSpawned()) return; - session.setLastMovementTimestamp(System.currentTimeMillis()); - - // Send book update before the player moves - session.getBookEditCache().checkForSend(); - // Ignore movement packets until Bedrock's position matches the teleported position if (session.getUnconfirmedTeleport() != null) { session.confirmTeleport(packet.getPosition().toDouble().sub(0, EntityDefinitions.PLAYER.offset(), 0)); return; } + boolean actualPositionChanged = !entity.getPosition().equals(packet.getPosition()); + + if (actualPositionChanged) { + // Send book update before the player moves + session.getBookEditCache().checkForSend(); + } + if (entity.getBedPosition() != null) { // https://github.com/GeyserMC/Geyser/issues/5001 // Bedrock 1.21.22 started sending a MovePlayerPacket as soon as it got into a bed. @@ -70,8 +75,11 @@ public class BedrockMovePlayerTranslator extends PacketTranslator session.getCollisionManager().isOnGround()); boolean isBelowVoid = entity.isVoidPositionDesynched(); boolean teleportThroughVoidFloor, mustResyncPosition; @@ -138,6 +152,7 @@ public class BedrockMovePlayerTranslator extends PacketTranslator { + + @Override + public void translate(GeyserSession session, PlayerAuthInputPacket packet) { + SessionPlayerEntity entity = session.getPlayerEntity(); + + boolean wasJumping = session.getInputCache().wasJumping(); + session.getInputCache().processInputs(packet); + + BedrockMovePlayer.translate(session, packet); + + processVehicleInput(session, packet, wasJumping); + + Set inputData = packet.getInputData(); + for (PlayerAuthInputData input : inputData) { + switch (input) { + case PERFORM_ITEM_INTERACTION -> processItemUseTransaction(session, packet.getItemUseTransaction()); + case PERFORM_BLOCK_ACTIONS -> BedrockBlockActions.translate(session, packet.getPlayerActions()); + case START_SNEAKING -> { + ServerboundPlayerCommandPacket startSneakPacket = new ServerboundPlayerCommandPacket(entity.getEntityId(), PlayerState.START_SNEAKING); + session.sendDownstreamGamePacket(startSneakPacket); + + session.startSneaking(); + } + case STOP_SNEAKING -> { + ServerboundPlayerCommandPacket stopSneakPacket = new ServerboundPlayerCommandPacket(entity.getEntityId(), PlayerState.STOP_SNEAKING); + session.sendDownstreamGamePacket(stopSneakPacket); + + session.stopSneaking(); + } + case START_SPRINTING -> { + if (!entity.getFlag(EntityFlag.SWIMMING)) { + ServerboundPlayerCommandPacket startSprintPacket = new ServerboundPlayerCommandPacket(entity.getEntityId(), PlayerState.START_SPRINTING); + session.sendDownstreamGamePacket(startSprintPacket); + session.setSprinting(true); + } + } + case STOP_SPRINTING -> { + if (!entity.getFlag(EntityFlag.SWIMMING)) { + ServerboundPlayerCommandPacket stopSprintPacket = new ServerboundPlayerCommandPacket(entity.getEntityId(), PlayerState.STOP_SPRINTING); + session.sendDownstreamGamePacket(stopSprintPacket); + } + session.setSprinting(false); + } + case START_SWIMMING -> session.setSwimming(true); + case STOP_SWIMMING -> session.setSwimming(false); + case START_CRAWLING -> session.setCrawling(true); + case STOP_CRAWLING -> session.setCrawling(false); + case START_FLYING -> { // Since 1.20.30 + if (session.isCanFly()) { + if (session.getGameMode() == GameMode.SPECTATOR) { + // should already be flying + session.sendAdventureSettings(); + break; + } + + if (session.getPlayerEntity().getFlag(EntityFlag.SWIMMING) && session.getCollisionManager().isPlayerInWater()) { + // As of 1.18.1, Java Edition cannot fly while in water, but it can fly while crawling + // If this isn't present, swimming on a 1.13.2 server and then attempting to fly will put you into a flying/swimming state that is invalid on JE + session.sendAdventureSettings(); + break; + } + + session.setFlying(true); + session.sendDownstreamGamePacket(new ServerboundPlayerAbilitiesPacket(true)); + } else { + // update whether we can fly + session.sendAdventureSettings(); + // stop flying + PlayerActionPacket stopFlyingPacket = new PlayerActionPacket(); + stopFlyingPacket.setRuntimeEntityId(session.getPlayerEntity().getGeyserId()); + stopFlyingPacket.setAction(PlayerActionType.STOP_FLYING); + stopFlyingPacket.setBlockPosition(Vector3i.ZERO); + stopFlyingPacket.setResultPosition(Vector3i.ZERO); + stopFlyingPacket.setFace(0); + session.sendUpstreamPacket(stopFlyingPacket); + } + } + case STOP_FLYING -> { + session.setFlying(false); + session.sendDownstreamGamePacket(new ServerboundPlayerAbilitiesPacket(false)); + } + case START_GLIDING -> { + // Otherwise gliding will not work in creative + ServerboundPlayerAbilitiesPacket playerAbilitiesPacket = new ServerboundPlayerAbilitiesPacket(false); + session.sendDownstreamGamePacket(playerAbilitiesPacket); + sendPlayerGlideToggle(session, entity); + } + case STOP_GLIDING -> sendPlayerGlideToggle(session, entity); + case MISSED_SWING -> CooldownUtils.sendCooldown(session); // Java edition sends a cooldown when hitting air. + } + } + if (entity.getVehicle() instanceof BoatEntity) { + boolean up = inputData.contains(PlayerAuthInputData.UP); + // Yes. These are flipped. It's always been an issue with Geyser. That's what it's like working with this codebase. + // Hi random stranger. I am six days into updating for 1.21.3. How's it going? + session.setSteeringLeft(up || inputData.contains(PlayerAuthInputData.PADDLE_RIGHT)); + session.setSteeringRight(up || inputData.contains(PlayerAuthInputData.PADDLE_LEFT)); + } + } + + private static void sendPlayerGlideToggle(GeyserSession session, Entity entity) { + ServerboundPlayerCommandPacket glidePacket = new ServerboundPlayerCommandPacket(entity.getEntityId(), PlayerState.START_ELYTRA_FLYING); + session.sendDownstreamGamePacket(glidePacket); + } + + private static void processItemUseTransaction(GeyserSession session, ItemUseTransaction transaction) { + if (transaction.getActionType() == 2) { + int blockState = session.getGameMode() == GameMode.CREATIVE ? + session.getGeyser().getWorldManager().getBlockAt(session, transaction.getBlockPosition()) : session.getBreakingBlock(); + + session.setLastBlockPlaced(null); + session.setLastBlockPlacePosition(null); + + // Same deal with vanilla block placing as above. + if (!session.getWorldBorder().isInsideBorderBoundaries()) { + BedrockInventoryTransactionTranslator.restoreCorrectBlock(session, transaction.getBlockPosition()); + return; + } + + Vector3f playerPosition = session.getPlayerEntity().getPosition(); + playerPosition = playerPosition.down(EntityDefinitions.PLAYER.offset() - session.getEyeHeight()); + + if (!BedrockInventoryTransactionTranslator.canInteractWithBlock(session, playerPosition, transaction.getBlockPosition())) { + BedrockInventoryTransactionTranslator.restoreCorrectBlock(session, transaction.getBlockPosition()); + return; + } + + int sequence = session.getWorldCache().nextPredictionSequence(); + session.getWorldCache().markPositionInSequence(transaction.getBlockPosition()); + // -1 means we don't know what block they're breaking + if (blockState == -1) { + blockState = Block.JAVA_AIR_ID; + } + + LevelEventPacket blockBreakPacket = new LevelEventPacket(); + blockBreakPacket.setType(LevelEvent.PARTICLE_DESTROY_BLOCK); + blockBreakPacket.setPosition(transaction.getBlockPosition().toFloat()); + blockBreakPacket.setData(session.getBlockMappings().getBedrockBlockId(blockState)); + session.sendUpstreamPacket(blockBreakPacket); + session.setBreakingBlock(-1); + + Entity itemFrameEntity = ItemFrameEntity.getItemFrameEntity(session, transaction.getBlockPosition()); + if (itemFrameEntity != null) { + ServerboundInteractPacket attackPacket = new ServerboundInteractPacket(itemFrameEntity.getEntityId(), + InteractAction.ATTACK, session.isSneaking()); + session.sendDownstreamGamePacket(attackPacket); + return; + } + + PlayerAction action = session.getGameMode() == GameMode.CREATIVE ? PlayerAction.START_DIGGING : PlayerAction.FINISH_DIGGING; + ServerboundPlayerActionPacket breakPacket = new ServerboundPlayerActionPacket(action, transaction.getBlockPosition(), Direction.VALUES[transaction.getBlockFace()], sequence); + session.sendDownstreamGamePacket(breakPacket); + } else { + session.getGeyser().getLogger().error("Unhandled item use transaction type!"); + if (session.getGeyser().getLogger().isDebug()) { + session.getGeyser().getLogger().debug(transaction); + } + } + } + + private static void processVehicleInput(GeyserSession session, PlayerAuthInputPacket packet, boolean wasJumping) { + Entity vehicle = session.getPlayerEntity().getVehicle(); + if (vehicle == null) { + return; + } + if (vehicle instanceof ClientVehicle) { + session.getPlayerEntity().setVehicleInput(packet.getMotion()); + } + + boolean sendMovement = false; + if (vehicle instanceof AbstractHorseEntity && !(vehicle instanceof LlamaEntity)) { + sendMovement = !(vehicle instanceof ClientVehicle); + } else if (vehicle instanceof BoatEntity) { + if (vehicle.getPassengers().size() == 1) { + // The player is the only rider + sendMovement = true; + } else { + // Check if the player is the front rider + if (session.getPlayerEntity().isRidingInFront()) { + sendMovement = true; + } + } + } + + if (vehicle instanceof AbstractHorseEntity) { + // Behavior verified as of Java Edition 1.21.3 + int currentJumpingTicks = session.getInputCache().getJumpingTicks(); + if (currentJumpingTicks < 0) { + session.getInputCache().setJumpingTicks(++currentJumpingTicks); + if (currentJumpingTicks == 0) { + session.getInputCache().setJumpScale(0); + } + } + + boolean holdingJump = packet.getInputData().contains(PlayerAuthInputData.JUMPING); + if (wasJumping && !holdingJump) { + // Jump released + // Yes, I'm fairly certain that entity ID is correct. + int finalVehicleJumpStrength = GenericMath.floor(session.getInputCache().getJumpScale() * 100f); + session.sendDownstreamGamePacket(new ServerboundPlayerCommandPacket(session.getPlayerEntity().getEntityId(), + PlayerState.START_HORSE_JUMP, finalVehicleJumpStrength)); + session.getInputCache().setJumpingTicks(-10); + session.getPlayerEntity().setVehicleJumpStrength(finalVehicleJumpStrength); + } else if (!wasJumping && holdingJump) { + session.getInputCache().setJumpingTicks(0); + session.getInputCache().setJumpScale(0); + } else if (holdingJump) { + session.getInputCache().setJumpingTicks(++currentJumpingTicks); + if (currentJumpingTicks < 10) { + session.getInputCache().setJumpScale(session.getInputCache().getJumpingTicks() * 0.1F); + } else { + session.getInputCache().setJumpScale(0.8f + 2.0f / (currentJumpingTicks - 9) * 0.1f); + } + } + } else { + session.getInputCache().setJumpScale(0); + } + + if (sendMovement) { + Vector3f vehiclePosition = packet.getPosition(); + Vector2f vehicleRotation = packet.getVehicleRotation(); + if (vehicleRotation == null) { + return; // If the client just got in or out of a vehicle for example. + } + + if (session.getWorldBorder().isPassingIntoBorderBoundaries(vehiclePosition, false)) { + Vector3f position = vehicle.getPosition(); + if (vehicle instanceof BoatEntity boat) { + // Undo the changes usually applied to the boat + boat.moveAbsoluteWithoutAdjustments(position, vehicle.getYaw(), vehicle.isOnGround(), true); + } else { + // This doesn't work if teleported is false + vehicle.moveAbsolute(position, + vehicle.getYaw(), vehicle.getPitch(), vehicle.getHeadYaw(), + vehicle.isOnGround(), true); + } + return; + } + + if (vehicle instanceof BoatEntity && !vehicle.isOnGround()) { + // Remove some Y position to prevents boats flying up + vehiclePosition = vehiclePosition.down(vehicle.getDefinition().offset()); + } + + vehicle.setPosition(vehiclePosition); + ServerboundMoveVehiclePacket moveVehiclePacket = new ServerboundMoveVehiclePacket( + vehiclePosition.getX(), vehiclePosition.getY(), vehiclePosition.getZ(), + vehicleRotation.getY() - 90, vehiclePosition.getX() // TODO I wonder if this is related to the horse spinning bugs... + ); + session.sendDownstreamGamePacket(moveVehiclePacket); + } + } +} 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 7e791ab2d..62b1b8b14 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 @@ -89,6 +89,7 @@ public class BedrockLevelSoundEventTranslator extends PacketTranslator { + /** + * Required to use the specified cartography table recipes + */ + private static final List CARTOGRAPHY_RECIPES = List.of( + MultiRecipeData.of(UUID.fromString("8b36268c-1829-483c-a0f1-993b7156a8f2"), ++LAST_RECIPE_NET_ID), // Map extending + MultiRecipeData.of(UUID.fromString("442d85ed-8272-4543-a6f1-418f90ded05d"), ++LAST_RECIPE_NET_ID), // Map cloning + MultiRecipeData.of(UUID.fromString("98c84b38-1085-46bd-b1ce-dd38c159e6cc"), ++LAST_RECIPE_NET_ID), // Map upgrading + MultiRecipeData.of(UUID.fromString("602234e4-cac1-4353-8bb7-b1ebff70024b"), ++LAST_RECIPE_NET_ID) // Map locking + ); + + @Override + public void translate(GeyserSession session, ClientboundFinishConfigurationPacket packet) { + // Clear the player list, as on Java the player list is cleared after transitioning from config to play phase + PlayerListPacket playerListPacket = new PlayerListPacket(); + playerListPacket.setAction(PlayerListPacket.Action.REMOVE); + for (PlayerEntity otherEntity : session.getEntityCache().getAllPlayerEntities()) { + playerListPacket.getEntries().add(new PlayerListPacket.Entry(otherEntity.getTabListUuid())); + } + session.sendUpstreamPacket(playerListPacket); + session.getEntityCache().removeAllPlayerEntities(); + + // Potion mixes are registered by default, as they are needed to be able to put ingredients into the brewing stand. + // (Also add it here so recipes get cleared on configuration - 1.21.3) + CraftingDataPacket craftingDataPacket = new CraftingDataPacket(); + craftingDataPacket.setCleanRecipes(true); + craftingDataPacket.getCraftingData().addAll(CARTOGRAPHY_RECIPES); + craftingDataPacket.getPotionMixData().addAll(Registries.POTION_MIXES.forVersion(session.getUpstream().getProtocolVersion())); + if (session.isSentSpawnPacket()) { + session.getUpstream().sendPacket(craftingDataPacket); + // TODO proper fix to check if we've been online - in online mode (with auth screen), + // recipes are not yet known + if (session.getStonecutterRecipes() != null) { + session.getCraftingRecipes().clear(); + session.getJavaToBedrockRecipeIds().clear(); + session.getSmithingRecipes().clear(); + session.getStonecutterRecipes().clear(); + } + } else { + session.getUpstream().queuePostStartGamePacket(craftingDataPacket); + } + + // while ClientboundLoginPacket holds the level, it doesn't hold the scoreboard. + // The ClientboundStartConfigurationPacket indirectly removes the old scoreboard, + // and this packet indirectly creates the new one. + // This makes this packet a good place to reset the scoreboard. + session.getWorldCache().resetScoreboard(); + } +} diff --git a/core/src/main/java/org/geysermc/geyser/translator/protocol/java/JavaGameProfileTranslator.java b/core/src/main/java/org/geysermc/geyser/translator/protocol/java/JavaLoginFinishedTranslator.java similarity index 91% rename from core/src/main/java/org/geysermc/geyser/translator/protocol/java/JavaGameProfileTranslator.java rename to core/src/main/java/org/geysermc/geyser/translator/protocol/java/JavaLoginFinishedTranslator.java index 47d1cff08..cebf71efd 100644 --- a/core/src/main/java/org/geysermc/geyser/translator/protocol/java/JavaGameProfileTranslator.java +++ b/core/src/main/java/org/geysermc/geyser/translator/protocol/java/JavaLoginFinishedTranslator.java @@ -25,7 +25,6 @@ package org.geysermc.geyser.translator.protocol.java; -import org.geysermc.mcprotocollib.auth.GameProfile; import net.kyori.adventure.key.Key; import org.geysermc.geyser.api.network.AuthType; import org.geysermc.geyser.entity.type.player.PlayerEntity; @@ -34,17 +33,18 @@ import org.geysermc.geyser.skin.SkinManager; import org.geysermc.geyser.translator.protocol.PacketTranslator; import org.geysermc.geyser.translator.protocol.Translator; import org.geysermc.geyser.util.PluginMessageUtils; +import org.geysermc.mcprotocollib.auth.GameProfile; import org.geysermc.mcprotocollib.protocol.packet.common.serverbound.ServerboundCustomPayloadPacket; -import org.geysermc.mcprotocollib.protocol.packet.login.clientbound.ClientboundGameProfilePacket; +import org.geysermc.mcprotocollib.protocol.packet.login.clientbound.ClientboundLoginFinishedPacket; /** - * ClientboundGameProfilePacket triggers protocol change LOGIN -> CONFIGURATION + * Triggers protocol change LOGIN -> CONFIGURATION */ -@Translator(packet = ClientboundGameProfilePacket.class) -public class JavaGameProfileTranslator extends PacketTranslator { +@Translator(packet = ClientboundLoginFinishedPacket.class) +public class JavaLoginFinishedTranslator extends PacketTranslator { @Override - public void translate(GeyserSession session, ClientboundGameProfilePacket packet) { + public void translate(GeyserSession session, ClientboundLoginFinishedPacket packet) { PlayerEntity playerEntity = session.getPlayerEntity(); AuthType remoteAuthType = session.remoteServer().authType(); 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 6470a5f0a..93a7d9a14 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 @@ -33,6 +33,7 @@ import org.geysermc.erosion.Constants; import org.geysermc.floodgate.pluginmessage.PluginMessageChannels; import org.geysermc.geyser.api.network.AuthType; import org.geysermc.geyser.entity.type.player.SessionPlayerEntity; +import org.geysermc.geyser.level.BedrockDimension; import org.geysermc.geyser.level.JavaDimension; import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.translator.protocol.PacketTranslator; @@ -62,7 +63,7 @@ public class JavaLoginTranslator extends PacketTranslator { + + @Override + public void translate(GeyserSession session, ClientboundRecipeBookAddPacket packet) { + int netId = session.getLastRecipeNetId().get(); + Int2ObjectMap> javaToBedrockRecipeIds = session.getJavaToBedrockRecipeIds(); + Int2ObjectMap geyserRecipes = session.getCraftingRecipes(); + CraftingDataPacket craftingDataPacket = new CraftingDataPacket(); + + UnlockedRecipesPacket recipesPacket = new UnlockedRecipesPacket(); + recipesPacket.setAction(packet.isReplace() ? UnlockedRecipesPacket.ActionType.INITIALLY_UNLOCKED : UnlockedRecipesPacket.ActionType.NEWLY_UNLOCKED); + + for (ClientboundRecipeBookAddPacket.Entry entry : packet.getEntries()) { + RecipeDisplayEntry contents = entry.contents(); + RecipeDisplay display = contents.display(); + + switch (display.getType()) { + case CRAFTING_SHAPED -> { + ShapedCraftingRecipeDisplay shapedRecipe = (ShapedCraftingRecipeDisplay) display; + var bedrockRecipes = combinations(session, display, shapedRecipe.ingredients()); + if (bedrockRecipes == null) { + continue; + } + List bedrockRecipeIds = new ArrayList<>(); + ItemData output = bedrockRecipes.right(); + List> left = bedrockRecipes.left(); + GeyserRecipe geyserRecipe = new GeyserShapedRecipe(shapedRecipe); + for (int i = 0; i < left.size(); i++) { + List inputs = left.get(i); + String recipeId = contents.id() + "_" + i; + int recipeNetworkId = netId++; + craftingDataPacket.getCraftingData().add(ShapedRecipeData.shaped(recipeId, + shapedRecipe.width(), shapedRecipe.height(), inputs, + Collections.singletonList(output), UUID.randomUUID(), "crafting_table", 0, recipeNetworkId, false, RecipeUnlockingRequirement.INVALID)); + recipesPacket.getUnlockedRecipes().add(recipeId); + bedrockRecipeIds.add(recipeId); + geyserRecipes.put(recipeNetworkId, geyserRecipe); + } + javaToBedrockRecipeIds.put(contents.id(), List.copyOf(bedrockRecipeIds)); + } + case CRAFTING_SHAPELESS -> { + ShapelessCraftingRecipeDisplay shapelessRecipe = (ShapelessCraftingRecipeDisplay) display; + var bedrockRecipes = combinations(session, display, shapelessRecipe.ingredients()); + if (bedrockRecipes == null) { + continue; + } + List bedrockRecipeIds = new ArrayList<>(); + ItemData output = bedrockRecipes.right(); + List> left = bedrockRecipes.left(); + GeyserRecipe geyserRecipe = new GeyserShapelessRecipe(shapelessRecipe); + for (int i = 0; i < left.size(); i++) { + List inputs = left.get(i); + String recipeId = contents.id() + "_" + i; + int recipeNetworkId = netId++; + craftingDataPacket.getCraftingData().add(ShapelessRecipeData.shapeless(recipeId, + inputs, Collections.singletonList(output), UUID.randomUUID(), "crafting_table", 0, recipeNetworkId, RecipeUnlockingRequirement.INVALID)); + recipesPacket.getUnlockedRecipes().add(recipeId); + bedrockRecipeIds.add(recipeId); + geyserRecipes.put(recipeNetworkId, geyserRecipe); + } + javaToBedrockRecipeIds.put(contents.id(), List.copyOf(bedrockRecipeIds)); + } + case SMITHING -> { + if (display.result() instanceof SmithingTrimDemoSlotDisplay) { + // Skip these - Bedrock already knows about them from the TrimDataPacket + continue; + } + SmithingRecipeDisplay smithingRecipe = (SmithingRecipeDisplay) display; + Pair output = translateToOutput(session, smithingRecipe.result()); + if (output == null) { + continue; + } + + List bases = translateToInput(session, smithingRecipe.base()); + List templates = translateToInput(session, smithingRecipe.template()); + List additions = translateToInput(session, smithingRecipe.addition()); + + if (bases == null || templates == null || additions == null) { + continue; + } + + int i = 0; + List bedrockRecipeIds = new ArrayList<>(); + for (ItemDescriptorWithCount template : templates) { + for (ItemDescriptorWithCount base : bases) { + for (ItemDescriptorWithCount addition : additions) { + String id = contents.id() + "_" + i++; + // Note: vanilla inputs use aux value of Short.MAX_VALUE + craftingDataPacket.getCraftingData().add(SmithingTransformRecipeData.of(id, + template, base, addition, output.right(), "smithing_table", netId++)); + + recipesPacket.getUnlockedRecipes().add(id); + bedrockRecipeIds.add(id); + } + } + } + javaToBedrockRecipeIds.put(contents.id(), bedrockRecipeIds); + session.getSmithingRecipes().add(new GeyserSmithingRecipe(smithingRecipe)); + } + } + } + + session.sendUpstreamPacket(craftingDataPacket); + session.sendUpstreamPacket(recipesPacket); + session.getLastRecipeNetId().set(netId); + + // Multi-version can mean different Bedrock item IDs + TAG_TO_ITEM_DESCRIPTOR_CACHE.remove(); + } + + // Arrays are usually an issue in maps, but because it's referencing the tag array that is unchanged, it actually works out for us. + private static final ThreadLocal>> TAG_TO_ITEM_DESCRIPTOR_CACHE = ThreadLocal.withInitial(Object2ObjectOpenHashMap::new); + + private List translateToInput(GeyserSession session, SlotDisplay slotDisplay) { + if (slotDisplay instanceof EmptySlotDisplay) { + return Collections.singletonList(ItemDescriptorWithCount.EMPTY); + } + if (slotDisplay instanceof CompositeSlotDisplay composite) { + if (composite.contents().size() == 1) { + return translateToInput(session, composite.contents().get(0)); + } + + // Try and see if the contents match a tag. + // ViaVersion maps pre-1.21.2 ingredient lists to CompositeSlotDisplays. + int[] items = new int[composite.contents().size()]; + List contents = composite.contents(); + for (int i = 0; i < contents.size(); i++) { + SlotDisplay subDisplay = contents.get(i); + int id; + if (subDisplay instanceof ItemSlotDisplay item) { + id = item.item(); + } else if (!(subDisplay instanceof ItemStackSlotDisplay itemStackSlotDisplay)) { + id = -1; + } else if (itemStackSlotDisplay.itemStack().getAmount() == 1 + && itemStackSlotDisplay.itemStack().getDataComponents() == null) { + id = itemStackSlotDisplay.itemStack().getId(); + } else { + id = -1; + } + if (id == -1) { + // We couldn't guarantee a "normal" item from this stack. + return fallbackCompositeMapping(session, composite); + } + items[i] = id; + } + // For searching in the tag map. + Arrays.sort(items); + + List tagDescriptor = lookupBedrockTag(session, items); + if (tagDescriptor != null) { + return tagDescriptor; + } + + return fallbackCompositeMapping(session, composite); + } + if (slotDisplay instanceof WithRemainderSlotDisplay remainder) { + // Don't need to worry about what will stay in the crafting table after crafting for the purposes of sending recipes to Bedrock + return translateToInput(session, remainder.input()); + } + if (slotDisplay instanceof ItemSlotDisplay itemSlot) { + return Collections.singletonList(fromItem(session, itemSlot.item())); + } + if (slotDisplay instanceof ItemStackSlotDisplay itemStackSlot) { + ItemData item = ItemTranslator.translateToBedrock(session, itemStackSlot.itemStack()); + return Collections.singletonList(ItemDescriptorWithCount.fromItem(item)); + } + if (slotDisplay instanceof TagSlotDisplay tagSlot) { + Key tag = tagSlot.tag(); + int[] items = session.getTagCache().getRaw(new Tag<>(JavaRegistries.ITEM, tag)); // I don't like this... + if (items == null || items.length == 0) { + return Collections.singletonList(ItemDescriptorWithCount.EMPTY); + } else if (items.length == 1) { + return Collections.singletonList(fromItem(session, items[0])); + } else { + // Cache is implemented as, presumably, an item tag will be used multiple times in succession + // (E.G. a chest with planks tags) + return TAG_TO_ITEM_DESCRIPTOR_CACHE.get().computeIfAbsent(items, key -> { + List tagDescriptor = lookupBedrockTag(session, key); + if (tagDescriptor != null) { + return tagDescriptor; + } + + // In the future, we can probably search through and use subsets of tags as well. + // I.E. if a Bedrock tag contains [stone stone_brick] and the Java tag uses [stone stone_brick bricks] + // we can still use that Bedrock tag alongside plain item descriptors for "bricks". + + Set itemDescriptors = new HashSet<>(); + for (int item : key) { + itemDescriptors.add(fromItem(session, item)); + } + return List.copyOf(itemDescriptors); // This, or a list from the start with contains -> add? + }); + } + } + session.getGeyser().getLogger().warning("Unimplemented slot display type for input: " + slotDisplay); + return null; + } + + private Pair translateToOutput(GeyserSession session, SlotDisplay slotDisplay) { + if (slotDisplay instanceof EmptySlotDisplay) { + return null; + } + if (slotDisplay instanceof ItemSlotDisplay itemSlot) { + int item = itemSlot.item(); + return Pair.of(Registries.JAVA_ITEMS.get(item), ItemTranslator.translateToBedrock(session, new ItemStack(item))); + } + if (slotDisplay instanceof ItemStackSlotDisplay itemStackSlot) { + ItemStack stack = itemStackSlot.itemStack(); + return Pair.of(Registries.JAVA_ITEMS.get(stack.getId()), ItemTranslator.translateToBedrock(session, stack)); + } + session.getGeyser().getLogger().warning("Unimplemented slot display type for output: " + slotDisplay); + return null; + } + + private ItemDescriptorWithCount fromItem(GeyserSession session, int item) { + if (item == Items.AIR_ID) { + return ItemDescriptorWithCount.EMPTY; + } + ItemMapping mapping = session.getItemMappings().getMapping(item); + return new ItemDescriptorWithCount(new DefaultDescriptor(mapping.getBedrockDefinition(), mapping.getBedrockData()), 1); // Need to check count + } + + /** + * Checks to see if this list of items matches with one of this Bedrock version's tags. + */ + @Nullable + private List lookupBedrockTag(GeyserSession session, int[] items) { + var bedrockTags = Registries.TAGS.forVersion(session.getUpstream().getProtocolVersion()); + String bedrockTag = bedrockTags.get(items); + if (bedrockTag != null) { + return Collections.singletonList( + new ItemDescriptorWithCount(new ItemTagDescriptor(bedrockTag), 1) + ); + } else { + return null; + } + } + + /** + * Converts CompositeSlotDisplay contents to a list of basic ItemDescriptorWithCounts. + */ + private List fallbackCompositeMapping(GeyserSession session, CompositeSlotDisplay composite) { + return composite.contents().stream() + .map(subDisplay -> translateToInput(session, subDisplay)) + .filter(Objects::nonNull) + .flatMap(List::stream) + .toList(); + } + + private Pair>, ItemData> combinations(GeyserSession session, RecipeDisplay display, List ingredients) { + Pair pair = translateToOutput(session, display.result()); + if (pair == null || !pair.right().isValid()) { + // Likely modded item Bedrock will complain about + // Implementation note: ItemData#isValid() may return true for air because count might be > 0 and the air definition may not be ItemDefinition.AIR + return null; + } + + ItemData output = pair.right(); + if (!(pair.left() instanceof BedrockRequiresTagItem)) { + // Strip NBT - tools won't appear in the recipe book otherwise + output = output.toBuilder().tag(null).build(); + } + + boolean empty = true; + boolean complexInputs = false; + List> inputs = new ArrayList<>(ingredients.size()); + for (SlotDisplay input : ingredients) { + List translated = translateToInput(session, input); + if (translated == null) { + continue; + } + inputs.add(translated); + if (translated.size() != 1 || translated.get(0) != ItemDescriptorWithCount.EMPTY) { + empty = false; + } + complexInputs |= translated.size() > 1; + } + if (empty) { + // Crashes Bedrock 1.19.70 otherwise + // Fixes https://github.com/GeyserMC/Geyser/issues/3549 + return null; + } + + if (complexInputs) { + long size = 1; + // See how big a cartesian product will get without creating one (Guava throws an error; not really ideal) + for (List list : inputs) { + size *= list.size(); + if (size > 500) { + // Too much. No. + complexInputs = false; + break; + } + } + if (complexInputs) { + return Pair.of(Lists.cartesianProduct(inputs), output); + } + } + // TODO: + return Pair.of( + Collections.singletonList(inputs.stream().map(descriptors -> descriptors.get(0)).toList()), + output + ); + } +} diff --git a/core/src/main/java/org/geysermc/geyser/translator/protocol/java/JavaClientboundRecipesTranslator.java b/core/src/main/java/org/geysermc/geyser/translator/protocol/java/JavaRecipeBookRemoveTranslator.java similarity index 59% rename from core/src/main/java/org/geysermc/geyser/translator/protocol/java/JavaClientboundRecipesTranslator.java rename to core/src/main/java/org/geysermc/geyser/translator/protocol/java/JavaRecipeBookRemoveTranslator.java index 9eb69183d..27faecef7 100644 --- a/core/src/main/java/org/geysermc/geyser/translator/protocol/java/JavaClientboundRecipesTranslator.java +++ b/core/src/main/java/org/geysermc/geyser/translator/protocol/java/JavaRecipeBookRemoveTranslator.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019-2023 GeyserMC. http://geysermc.org + * Copyright (c) 2024 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 @@ -25,51 +25,34 @@ package org.geysermc.geyser.translator.protocol.java; -import org.geysermc.mcprotocollib.protocol.packet.ingame.clientbound.ClientboundRecipePacket; import org.cloudburstmc.protocol.bedrock.packet.UnlockedRecipesPacket; import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.translator.protocol.PacketTranslator; import org.geysermc.geyser.translator.protocol.Translator; +import org.geysermc.mcprotocollib.protocol.packet.ingame.clientbound.ClientboundRecipeBookRemovePacket; import java.util.ArrayList; import java.util.List; -@Translator(packet = ClientboundRecipePacket.class) -public class JavaClientboundRecipesTranslator extends PacketTranslator { +@Translator(packet = ClientboundRecipeBookRemovePacket.class) +public class JavaRecipeBookRemoveTranslator extends PacketTranslator { @Override - public void translate(GeyserSession session, ClientboundRecipePacket packet) { - UnlockedRecipesPacket recipesPacket = new UnlockedRecipesPacket(); - switch (packet.getAction()) { - case INIT -> { - recipesPacket.setAction(UnlockedRecipesPacket.ActionType.INITIALLY_UNLOCKED); - recipesPacket.getUnlockedRecipes().addAll(getBedrockRecipes(session, packet.getAlreadyKnownRecipes())); - } - case ADD -> { - List recipes = getBedrockRecipes(session, packet.getRecipes()); - if (recipes.isEmpty()) { - // Sending an empty list here packet will crash the client as of 1.20.60 - return; - } - recipesPacket.setAction(UnlockedRecipesPacket.ActionType.NEWLY_UNLOCKED); - recipesPacket.getUnlockedRecipes().addAll(recipes); - } - case REMOVE -> { - List recipes = getBedrockRecipes(session, packet.getRecipes()); - if (recipes.isEmpty()) { - // Sending an empty list here will crash the client as of 1.20.60 - return; - } - recipesPacket.setAction(UnlockedRecipesPacket.ActionType.REMOVE_UNLOCKED); - recipesPacket.getUnlockedRecipes().addAll(recipes); - } + public void translate(GeyserSession session, ClientboundRecipeBookRemovePacket packet) { + List recipes = getBedrockRecipes(session, packet.getRecipes()); + if (recipes.isEmpty()) { + // Sending an empty list here will crash the client as of 1.20.60 + return; } + UnlockedRecipesPacket recipesPacket = new UnlockedRecipesPacket(); + recipesPacket.setAction(UnlockedRecipesPacket.ActionType.REMOVE_UNLOCKED); + recipesPacket.getUnlockedRecipes().addAll(recipes); session.sendUpstreamPacket(recipesPacket); } - private List getBedrockRecipes(GeyserSession session, String[] javaRecipeIdentifiers) { + private List getBedrockRecipes(GeyserSession session, int[] javaRecipeIds) { List recipes = new ArrayList<>(); - for (String javaIdentifier : javaRecipeIdentifiers) { + for (int javaIdentifier : javaRecipeIds) { List bedrockRecipes = session.getJavaToBedrockRecipeIds().get(javaIdentifier); // Some recipes are not (un)lockable on Bedrock edition, like furnace or stonecutter recipes. // So we don't store/send these. @@ -79,4 +62,4 @@ public class JavaClientboundRecipesTranslator extends PacketTranslator { // todo: dump from client? - private static final Set KNOWN_PACK_IDS = Set.of("core", "update_1_21", "bundle", "trade_rebalance"); + private static final Set KNOWN_PACK_IDS = Set.of("core", "winter_drop", "trade_rebalance", "redstone_experiments", "minecart_improvements"); @Override public void translate(GeyserSession session, ClientboundSelectKnownPacks packet) { @@ -59,4 +59,10 @@ public class JavaSelectKnownPacksTranslator extends PacketTranslator { - /** - * Required to use the specified cartography table recipes - */ - private static final List CARTOGRAPHY_RECIPES = List.of( - MultiRecipeData.of(UUID.fromString("8b36268c-1829-483c-a0f1-993b7156a8f2"), ++LAST_RECIPE_NET_ID), // Map extending - MultiRecipeData.of(UUID.fromString("442d85ed-8272-4543-a6f1-418f90ded05d"), ++LAST_RECIPE_NET_ID), // Map cloning - MultiRecipeData.of(UUID.fromString("98c84b38-1085-46bd-b1ce-dd38c159e6cc"), ++LAST_RECIPE_NET_ID), // Map upgrading - MultiRecipeData.of(UUID.fromString("602234e4-cac1-4353-8bb7-b1ebff70024b"), ++LAST_RECIPE_NET_ID) // Map locking - ); private static final List NETHERITE_UPGRADES = List.of( "minecraft:netherite_sword", @@ -112,182 +83,26 @@ public class JavaUpdateRecipesTranslator extends PacketTranslator RECIPE_TAGS = Map.of( - "minecraft:wood", "minecraft:logs", - "minecraft:wooden_slab", "minecraft:wooden_slabs", - "minecraft:planks", "minecraft:planks"); + private static final Key SMITHING_BASE = MinecraftKey.key("smithing_base"); + private static final Key SMITHING_TEMPLATE = MinecraftKey.key("smithing_template"); + private static final Key SMITHING_ADDITION = MinecraftKey.key("smithing_addition"); @Override public void translate(GeyserSession session, ClientboundUpdateRecipesPacket packet) { - boolean sendTrimRecipes = false; - Map> recipeIDs = session.getJavaToBedrockRecipeIds(); - recipeIDs.clear(); - Int2ObjectMap recipeMap = new Int2ObjectOpenHashMap<>(); - Int2ObjectMap> unsortedStonecutterData = new Int2ObjectOpenHashMap<>(); + int netId = session.getLastRecipeNetId().get(); CraftingDataPacket craftingDataPacket = new CraftingDataPacket(); - craftingDataPacket.setCleanRecipes(true); - RecipeContext context = new RecipeContext(session, craftingDataPacket, recipeMap); - - for (Recipe recipe : packet.getRecipes()) { - switch (recipe.getType()) { - case CRAFTING_SHAPELESS -> { - ShapelessRecipeData shapelessRecipeData = (ShapelessRecipeData) recipe.getData(); - List bedrockRecipeIDs = context.translateShapelessRecipe(new GeyserShapelessRecipe(shapelessRecipeData)); - if (bedrockRecipeIDs != null) { - context.addRecipeIdentifier(session, recipe.getIdentifier().asString(), bedrockRecipeIDs); - } - } - case CRAFTING_SHAPED -> { - ShapedRecipeData shapedRecipeData = (ShapedRecipeData) recipe.getData(); - List bedrockRecipeIDs = context.translateShapedRecipe(new GeyserShapedRecipe(shapedRecipeData)); - if (bedrockRecipeIDs != null) { - context.addRecipeIdentifier(session, recipe.getIdentifier().asString(), bedrockRecipeIDs); - } - } - case STONECUTTING -> { - StoneCuttingRecipeData stoneCuttingData = (StoneCuttingRecipeData) recipe.getData(); - if (stoneCuttingData.getIngredient().getOptions().length == 0) { - if (GeyserImpl.getInstance().config().debugMode()) { - GeyserImpl.getInstance().getLogger().debug("Received broken stone cutter recipe: " + stoneCuttingData + " " + - recipe.getIdentifier() + " " + Registries.JAVA_ITEMS.get().get(stoneCuttingData.getResult().getId()).javaIdentifier()); - } - continue; - } - ItemStack ingredient = stoneCuttingData.getIngredient().getOptions()[0]; - List data = unsortedStonecutterData.get(ingredient.getId()); - if (data == null) { - data = new ArrayList<>(); - unsortedStonecutterData.put(ingredient.getId(), data); - } - // Save for processing after all recipes have been received - data.add(stoneCuttingData); - } - case SMITHING_TRANSFORM -> { - SmithingTransformRecipeData data = (SmithingTransformRecipeData) recipe.getData(); - ItemData output = ItemTranslator.translateToBedrock(session, data.getResult()); - - for (ItemStack template : data.getTemplate().getOptions()) { - ItemDescriptorWithCount bedrockTemplate = ItemDescriptorWithCount.fromItem(ItemTranslator.translateToBedrock(session, template)); - - for (ItemStack base : data.getBase().getOptions()) { - ItemDescriptorWithCount bedrockBase = ItemDescriptorWithCount.fromItem(ItemTranslator.translateToBedrock(session, base)); - - for (ItemStack addition : data.getAddition().getOptions()) { - ItemDescriptorWithCount bedrockAddition = ItemDescriptorWithCount.fromItem(ItemTranslator.translateToBedrock(session, addition)); - - String id = recipe.getIdentifier().asString(); - // Note: vanilla inputs use aux value of Short.MAX_VALUE - craftingDataPacket.getCraftingData().add(org.cloudburstmc.protocol.bedrock.data.inventory.crafting.recipe.SmithingTransformRecipeData.of(id, - bedrockTemplate, bedrockBase, bedrockAddition, output, "smithing_table", context.getAndIncrementNetId())); - - recipeIDs.put(id, new ArrayList<>(Collections.singletonList(id))); - } - } - } - } - case SMITHING_TRIM -> { - sendTrimRecipes = true; - // ignored currently - see below - } - case CRAFTING_DECORATED_POT -> { - // Paper 1.20 seems to send only one recipe, which seems to be hardcoded to include all recipes. - // We can send the equivalent Bedrock MultiRecipe! :) - craftingDataPacket.getCraftingData().add(MultiRecipeData.of(UUID.fromString("685a742a-c42e-4a4e-88ea-5eb83fc98e5b"), context.getAndIncrementNetId())); - } - case CRAFTING_SPECIAL_BOOKCLONING -> { - craftingDataPacket.getCraftingData().add(MultiRecipeData.of(UUID.fromString("d1ca6b84-338e-4f2f-9c6b-76cc8b4bd98d"), context.getAndIncrementNetId())); - } - case CRAFTING_SPECIAL_REPAIRITEM -> { - craftingDataPacket.getCraftingData().add(MultiRecipeData.of(UUID.fromString("00000000-0000-0000-0000-000000000001"), context.getAndIncrementNetId())); - } - case CRAFTING_SPECIAL_MAPEXTENDING -> { - craftingDataPacket.getCraftingData().add(MultiRecipeData.of(UUID.fromString("d392b075-4ba1-40ae-8789-af868d56f6ce"), context.getAndIncrementNetId())); - } - case CRAFTING_SPECIAL_MAPCLONING -> { - craftingDataPacket.getCraftingData().add(MultiRecipeData.of(UUID.fromString("85939755-ba10-4d9d-a4cc-efb7a8e943c4"), context.getAndIncrementNetId())); - } - case CRAFTING_SPECIAL_FIREWORK_ROCKET -> { - craftingDataPacket.getCraftingData().add(MultiRecipeData.of(UUID.fromString("00000000-0000-0000-0000-000000000002"), context.getAndIncrementNetId())); - } - default -> { - List recipes = Registries.RECIPES.get(recipe.getType()); - if (recipes != null) { - List bedrockRecipeIds = new ArrayList<>(); - if (recipe.getType() == RecipeType.CRAFTING_SPECIAL_TIPPEDARROW) { - // Only shaped recipe at this moment - for (GeyserRecipe builtInRecipe : recipes) { - var recipeIds = context.translateShapedRecipe((GeyserShapedRecipe) builtInRecipe); - if (recipeIds != null) { - bedrockRecipeIds.addAll(recipeIds); - } - } - } else if (recipe.getType() == RecipeType.CRAFTING_SPECIAL_SHULKERBOXCOLORING) { - for (GeyserRecipe builtInRecipe : recipes) { - var recipeIds = context.translateShulkerBoxRecipe((GeyserShapelessRecipe) builtInRecipe); - if (recipeIds != null) { - bedrockRecipeIds.addAll(recipeIds); - } - } - } else { - for (GeyserRecipe builtInRecipe : recipes) { - var recipeIds = context.translateShapelessRecipe((GeyserShapelessRecipe) builtInRecipe); - if (recipeIds != null) { - bedrockRecipeIds.addAll(recipeIds); - } - } - } - context.addSpecialRecipesIdentifiers(recipe, bedrockRecipeIds); - } - } - } - } - craftingDataPacket.getCraftingData().addAll(CARTOGRAPHY_RECIPES); - craftingDataPacket.getPotionMixData().addAll(Registries.POTION_MIXES.forVersion(session.getUpstream().getProtocolVersion())); - - Int2ObjectMap stonecutterRecipeMap = new Int2ObjectOpenHashMap<>(); - for (Int2ObjectMap.Entry> data : unsortedStonecutterData.int2ObjectEntrySet()) { - // Sort the list by each output item's Java identifier - this is how it's sorted on Java, and therefore - // We can get the correct order for button pressing - data.getValue().sort(Comparator.comparing((stoneCuttingRecipeData -> - Registries.JAVA_ITEMS.get().get(stoneCuttingRecipeData.getResult().getId()) - // See RecipeManager#getRecipesFor as of 1.21 - .translationKey()))); - - // Now that it's sorted, let's translate these recipes - int buttonId = 0; - for (StoneCuttingRecipeData stoneCuttingData : data.getValue()) { - // As of 1.16.4, all stonecutter recipes have one ingredient option - ItemStack ingredient = stoneCuttingData.getIngredient().getOptions()[0]; - ItemData input = ItemTranslator.translateToBedrock(session, ingredient); - ItemDescriptorWithCount descriptor = ItemDescriptorWithCount.fromItem(input); - ItemStack javaOutput = stoneCuttingData.getResult(); - ItemData output = ItemTranslator.translateToBedrock(session, javaOutput); - if (!input.isValid() || !output.isValid()) { - // Probably modded items - continue; - } - UUID uuid = UUID.randomUUID(); - // We need to register stonecutting recipes, so they show up on Bedrock - craftingDataPacket.getCraftingData().add(org.cloudburstmc.protocol.bedrock.data.inventory.crafting.recipe.ShapelessRecipeData.shapeless(uuid.toString(), - Collections.singletonList(descriptor), Collections.singletonList(output), uuid, "stonecutter", 0, context.netId, RecipeUnlockingRequirement.INVALID)); - - // Save the recipe list for reference when crafting - // Add the net ID as the key and the button required + output for the value - stonecutterRecipeMap.put(context.getAndIncrementNetId(), new GeyserStonecutterData(buttonId++, javaOutput)); - - // Currently, stone cutter recipes are not locked/unlocked on Bedrock; so no need to cache their identifiers. - } - } - - session.getLastRecipeNetId().set(context.netId); // No increment - - // Only send smithing trim recipes if Java/ViaVersion sends them. - if (sendTrimRecipes) { + boolean oldSmithingTable; + int[] smithingBase = packet.getItemSets().get(SMITHING_BASE); + int[] smithingTemplate = packet.getItemSets().get(SMITHING_TEMPLATE); + int[] smithingAddition = packet.getItemSets().get(SMITHING_ADDITION); + if (smithingBase == null || smithingTemplate == null || smithingAddition == null) { + // We're probably on a version before the smithing table got expanded functionality. + oldSmithingTable = true; + addSmithingTransformRecipes(session, craftingDataPacket.getCraftingData()); + netId = session.getLastRecipeNetId().get(); // Was updated in the above method. + } else { + oldSmithingTable = false; // BDS sends armor trim templates and materials before the CraftingDataPacket TrimDataPacket trimDataPacket = new TrimDataPacket(); trimDataPacket.getPatterns().addAll(session.getRegistryCache().trimPatterns().values()); @@ -297,114 +112,80 @@ public class JavaUpdateRecipesTranslator extends PacketTranslator, IntSet> squashedOptions = new HashMap<>(); - for (int i = 0; i < ingredients.length; i++) { - if (ingredients[i].getOptions().length == 0) { - squashedOptions.computeIfAbsent(Collections.singleton(ItemDescriptorWithCount.EMPTY), k -> new IntOpenHashSet()).add(i); + Int2ObjectMap> unsortedStonecutterData = new Int2ObjectOpenHashMap<>(); + + List stonecutterRecipes = packet.getStonecutterRecipes(); + for (SelectableRecipe recipe : stonecutterRecipes) { + // Hardcoding the heck out of this until we see different examples of how this works. + HolderSet ingredient = recipe.input().getValues(); + if (ingredient.getHolders() == null || ingredient.getHolders().length != 1) { + session.getGeyser().getLogger().debug("Ignoring stonecutter recipe for weird input: " + recipe); continue; } - empty = false; - Ingredient ingredient = ingredients[i]; - Map> groupedByIds = Arrays.stream(ingredient.getOptions()) - .map(item -> ItemDescriptorWithCount.fromItem(ItemTranslator.translateToBedrock(session, item))) - .collect(Collectors.groupingBy(item -> item == ItemDescriptorWithCount.EMPTY ? new GroupedItem(ItemDefinition.AIR, 0) : new GroupedItem(((DefaultDescriptor) item.getDescriptor()).getItemId(), item.getCount()))); - Set optionSet = new HashSet<>(groupedByIds.size()); - for (Map.Entry> entry : groupedByIds.entrySet()) { - if (entry.getValue().size() > 1) { - GroupedItem groupedItem = entry.getKey(); + if (!(recipe.recipe() instanceof ItemStackSlotDisplay)) { + session.getGeyser().getLogger().debug("Ignoring stonecutter recipe for weird output: " + recipe); + continue; + } + unsortedStonecutterData.computeIfAbsent(ingredient.getHolders()[0], $ -> new ArrayList<>()).add(recipe); + } - String recipeTag = RECIPE_TAGS.get(groupedItem.id.getIdentifier()); - if (recipeTag != null && ingredients.length > 1) { - optionSet.add(new ItemDescriptorWithCount(new ItemTagDescriptor(recipeTag), groupedItem.count)); - continue; - } + Int2ObjectMap stonecutterRecipeMap = new Int2ObjectOpenHashMap<>(); + for (Int2ObjectMap.Entry> data : unsortedStonecutterData.int2ObjectEntrySet()) { + // Sort the list by each output item's Java identifier - this is how it's sorted on Java, and therefore + // We can get the correct order for button pressing + data.getValue().sort(Comparator.comparing((stoneCuttingRecipeData -> + Registries.JAVA_ITEMS.get().get(((ItemStackSlotDisplay) stoneCuttingRecipeData.recipe()).itemStack().getId()) + // See RecipeManager#getRecipesFor as of 1.21 + .translationKey()))); - int idCount = 0; - //not optimal - for (ItemMapping mapping : session.getItemMappings().getItems()) { - if (mapping.getBedrockDefinition() == groupedItem.id) { - idCount++; - } - } - if (entry.getValue().size() < idCount) { - optionSet.addAll(entry.getValue()); - } else { - optionSet.add(groupedItem.id == ItemDefinition.AIR ? ItemDescriptorWithCount.EMPTY : new ItemDescriptorWithCount(new DefaultDescriptor(groupedItem.id, Short.MAX_VALUE), groupedItem.count)); - } - } else { - ItemDescriptorWithCount item = entry.getValue().get(0); - optionSet.add(item); + // Now that it's sorted, let's translate these recipes + int buttonId = 0; + for (SelectableRecipe recipe : data.getValue()) { + // As of 1.16.4, all stonecutter recipes have one ingredient option + HolderSet ingredient = recipe.input().getValues(); + int javaInput = ingredient.getHolders()[0]; + ItemMapping mapping = session.getItemMappings().getMapping(javaInput); + if (mapping.getJavaItem() == Items.AIR) { + // Modded ? + continue; } - } - squashedOptions.computeIfAbsent(optionSet, k -> new IntOpenHashSet()).add(i); - } - if (empty) { - // Crashes Bedrock 1.19.70 otherwise - // Fixes https://github.com/GeyserMC/Geyser/issues/3549 - return null; - } - int totalCombinations = 1; - for (Set optionSet : squashedOptions.keySet()) { - totalCombinations *= optionSet.size(); - } - if (totalCombinations > 500) { - ItemDescriptorWithCount[] translatedItems = new ItemDescriptorWithCount[ingredients.length]; - for (int i = 0; i < ingredients.length; i++) { - if (ingredients[i].getOptions().length > 0) { - translatedItems[i] = ItemDescriptorWithCount.fromItem(ItemTranslator.translateToBedrock(session, ingredients[i].getOptions()[0])); - } else { - translatedItems[i] = ItemDescriptorWithCount.EMPTY; + ItemDescriptorWithCount descriptor = new ItemDescriptorWithCount(new DefaultDescriptor(mapping.getBedrockDefinition(), mapping.getBedrockData()), 1); + ItemStack javaOutput = ((ItemStackSlotDisplay) recipe.recipe()).itemStack(); + ItemData output = ItemTranslator.translateToBedrock(session, javaOutput); + if (!output.isValid()) { + // Probably modded items + continue; } + int recipeNetId = netId++; + UUID uuid = UUID.randomUUID(); + // We need to register stonecutting recipes, so they show up on Bedrock + // (Implementation note: recipe ID creates the order which stonecutting recipes are shown in stonecutter) + craftingDataPacket.getCraftingData().add(ShapelessRecipeData.shapeless("stonecutter_" + javaInput + "_" + buttonId, + Collections.singletonList(descriptor), Collections.singletonList(output), uuid, "stonecutter", 0, recipeNetId, RecipeUnlockingRequirement.INVALID)); + + // Save the recipe list for reference when crafting + // Add the net ID as the key and the button required + output for the value + stonecutterRecipeMap.put(recipeNetId, new GeyserStonecutterData(buttonId++, javaOutput)); + + // Currently, stone cutter recipes are not locked/unlocked on Bedrock; so no need to cache their identifiers. } - return new ItemDescriptorWithCount[][]{translatedItems}; } - List> sortedSets = new ArrayList<>(squashedOptions.keySet()); - sortedSets.sort(Comparator.comparing(Set::size, Comparator.reverseOrder())); - ItemDescriptorWithCount[][] combinations = new ItemDescriptorWithCount[totalCombinations][ingredients.length]; - int x = 1; - for (Set set : sortedSets) { - IntSet slotSet = squashedOptions.get(set); - int i = 0; - for (ItemDescriptorWithCount item : set) { - for (int j = 0; j < totalCombinations / set.size(); j++) { - final int comboIndex = (i * x) + (j % x) + ((j / x) * set.size() * x); - for (IntIterator it = slotSet.iterator(); it.hasNext(); ) { - combinations[comboIndex][it.nextInt()] = item; - } - } - i++; - } - x *= set.size(); - } - return combinations; + + session.sendUpstreamPacket(craftingDataPacket); + session.setStonecutterRecipes(stonecutterRecipeMap); + session.getLastRecipeNetId().set(netId); } - - private List getSmithingTransformRecipes(GeyserSession session) { - List recipes = new ArrayList<>(); + + private void addSmithingTransformRecipes(GeyserSession session, List recipes) { ItemMapping template = session.getItemMappings().getStoredItems().upgradeTemplate(); for (String identifier : NETHERITE_UPGRADES) { - recipes.add(org.cloudburstmc.protocol.bedrock.data.inventory.crafting.recipe.SmithingTransformRecipeData.of(identifier + "_smithing", + recipes.add(SmithingTransformRecipeData.of(identifier + "_smithing", getDescriptorFromId(session, template.getBedrockIdentifier()), getDescriptorFromId(session, identifier.replace("netherite", "diamond")), getDescriptorFromId(session, "minecraft:netherite_ingot"), @@ -412,7 +193,6 @@ public class JavaUpdateRecipesTranslator extends PacketTranslator recipeMap; - // Get the last known network ID (first used for some pregenerated recipes) and increment from there. - private int netId = InventoryUtils.LAST_RECIPE_NET_ID + 1; - - private RecipeContext(GeyserSession session, CraftingDataPacket packet, Int2ObjectMap recipeMap) { - this.session = session; - this.packet = packet; - this.recipeMap = recipeMap; - } - - List translateShulkerBoxRecipe(GeyserShapelessRecipe recipe) { - ItemStack result = recipe.result(); - ItemData output = ItemTranslator.translateToBedrock(session, result); - if (!output.isValid()) { - // Likely modded item that Bedrock will complain about if it persists - return null; - } - - Item javaItem = Registries.JAVA_ITEMS.get(result.getId()); - if (!(javaItem instanceof BedrockRequiresTagItem)) { - // Strip NBT - tools won't appear in the recipe book otherwise - output = output.toBuilder().tag(null).build(); - } - ItemDescriptorWithCount[][] inputCombinations = combinations(session, recipe.ingredients()); - if (inputCombinations == null) { - return null; - } - - List bedrockRecipeIDs = new ArrayList<>(); - for (ItemDescriptorWithCount[] inputs : inputCombinations) { - UUID uuid = UUID.randomUUID(); - bedrockRecipeIDs.add(uuid.toString()); - packet.getCraftingData().add(org.cloudburstmc.protocol.bedrock.data.inventory.crafting.recipe.ShapelessRecipeData.shulkerBox(uuid.toString(), - Arrays.asList(inputs), Collections.singletonList(output), uuid, "crafting_table", 0, netId)); - recipeMap.put(netId++, recipe); - } - return bedrockRecipeIDs; - } - - List translateShapelessRecipe(GeyserShapelessRecipe recipe) { - ItemStack result = recipe.result(); - ItemData output = ItemTranslator.translateToBedrock(session, result); - if (!output.isValid()) { - // Likely modded item that Bedrock will complain about if it persists - return null; - } - - Item javaItem = Registries.JAVA_ITEMS.get(result.getId()); - if (!(javaItem instanceof BedrockRequiresTagItem)) { - // Strip NBT - tools won't appear in the recipe book otherwise - output = output.toBuilder().tag(null).build(); - } - ItemDescriptorWithCount[][] inputCombinations = combinations(session, recipe.ingredients()); - if (inputCombinations == null) { - return null; - } - - List bedrockRecipeIDs = new ArrayList<>(); - for (ItemDescriptorWithCount[] inputs : inputCombinations) { - UUID uuid = UUID.randomUUID(); - bedrockRecipeIDs.add(uuid.toString()); - packet.getCraftingData().add(org.cloudburstmc.protocol.bedrock.data.inventory.crafting.recipe.ShapelessRecipeData.shapeless(uuid.toString(), - Arrays.asList(inputs), Collections.singletonList(output), uuid, "crafting_table", 0, netId, RecipeUnlockingRequirement.INVALID)); - recipeMap.put(netId++, recipe); - } - return bedrockRecipeIDs; - } - - List translateShapedRecipe(GeyserShapedRecipe recipe) { - ItemStack result = recipe.result(); - ItemData output = ItemTranslator.translateToBedrock(session, result); - if (!output.isValid()) { - // Likely modded item that Bedrock will complain about if it persists - return null; - } - - Item javaItem = Registries.JAVA_ITEMS.get(result.getId()); - if (!(javaItem instanceof BedrockRequiresTagItem)) { - // Strip NBT - tools won't appear in the recipe book otherwise - output = output.toBuilder().tag(null).build(); - } - ItemDescriptorWithCount[][] inputCombinations = combinations(session, recipe.ingredients()); - if (inputCombinations == null) { - return null; - } - - List bedrockRecipeIDs = new ArrayList<>(); - for (ItemDescriptorWithCount[] inputs : inputCombinations) { - UUID uuid = UUID.randomUUID(); - bedrockRecipeIDs.add(uuid.toString()); - packet.getCraftingData().add(org.cloudburstmc.protocol.bedrock.data.inventory.crafting.recipe.ShapedRecipeData.shaped(uuid.toString(), - recipe.width(), recipe.height(), Arrays.asList(inputs), - Collections.singletonList(output), uuid, "crafting_table", 0, netId, false, RecipeUnlockingRequirement.INVALID)); - recipeMap.put(netId++, recipe); - } - return bedrockRecipeIDs; - } - - void addSpecialRecipesIdentifiers(Recipe recipe, List identifiers) { - String javaRecipeID = switch (recipe.getType()) { - case CRAFTING_SPECIAL_SHULKERBOXCOLORING -> - // BDS (un)locks the dyeing with the shulker box recipe, Java never - we want BDS behavior for ease of use - "minecraft:shulker_box"; - case CRAFTING_SPECIAL_TIPPEDARROW -> - // similar as above - "minecraft:arrow"; - default -> recipe.getIdentifier().asString(); - }; - - addRecipeIdentifier(session, javaRecipeID, identifiers); - } - - void addRecipeIdentifier(GeyserSession session, String javaIdentifier, List bedrockIdentifiers) { - session.getJavaToBedrockRecipeIds().computeIfAbsent(javaIdentifier, k -> new ArrayList<>()).addAll(bedrockIdentifiers); - } - - int getAndIncrementNetId() { - return this.netId++; - } - } } diff --git a/core/src/main/java/org/geysermc/geyser/translator/protocol/java/entity/JavaEntityEventTranslator.java b/core/src/main/java/org/geysermc/geyser/translator/protocol/java/entity/JavaEntityEventTranslator.java index 3195a6536..6c2e02cd3 100644 --- a/core/src/main/java/org/geysermc/geyser/translator/protocol/java/entity/JavaEntityEventTranslator.java +++ b/core/src/main/java/org/geysermc/geyser/translator/protocol/java/entity/JavaEntityEventTranslator.java @@ -30,8 +30,6 @@ import org.cloudburstmc.protocol.bedrock.data.SoundEvent; import org.cloudburstmc.protocol.bedrock.data.entity.EntityDataTypes; import org.cloudburstmc.protocol.bedrock.data.entity.EntityEventType; import org.cloudburstmc.protocol.bedrock.data.inventory.ContainerId; -import org.cloudburstmc.protocol.bedrock.data.inventory.ContainerSlotType; -import org.cloudburstmc.protocol.bedrock.data.inventory.FullContainerName; import org.cloudburstmc.protocol.bedrock.packet.EntityEventPacket; import org.cloudburstmc.protocol.bedrock.packet.InventoryContentPacket; import org.cloudburstmc.protocol.bedrock.packet.LevelEventPacket; @@ -169,7 +167,6 @@ public class JavaEntityEventTranslator extends PacketTranslator { -@Translator(packet = RiderJumpPacket.class) -public class BedrockRiderJumpTranslator extends PacketTranslator { @Override - public void translate(GeyserSession session, RiderJumpPacket packet) { - session.getPlayerEntity().setVehicleJumpStrength(packet.getJumpStrength()); + public void translate(GeyserSession session, ClientboundEntityPositionSyncPacket packet) { + Entity entity = session.getEntityCache().getEntityByJavaId(packet.getId()); + if (entity == null) return; - Entity vehicle = session.getPlayerEntity().getVehicle(); - if (vehicle instanceof AbstractHorseEntity) { - ServerboundPlayerCommandPacket playerCommandPacket = new ServerboundPlayerCommandPacket(vehicle.getEntityId(), PlayerState.START_HORSE_JUMP, packet.getJumpStrength()); - session.sendDownstreamGamePacket(playerCommandPacket); + Vector3d pos = packet.getPosition(); + + if (entity instanceof ClientVehicle clientVehicle) { + clientVehicle.getVehicleComponent().moveAbsolute(pos.getX(), pos.getY(), pos.getZ()); } + + entity.teleport(pos.toFloat(), packet.getYRot(), packet.getXRot(), packet.isOnGround()); } } diff --git a/core/src/main/java/org/geysermc/geyser/translator/protocol/java/entity/JavaTeleportEntityTranslator.java b/core/src/main/java/org/geysermc/geyser/translator/protocol/java/entity/JavaTeleportEntityTranslator.java index 47c61eb8e..eda195a8f 100644 --- a/core/src/main/java/org/geysermc/geyser/translator/protocol/java/entity/JavaTeleportEntityTranslator.java +++ b/core/src/main/java/org/geysermc/geyser/translator/protocol/java/entity/JavaTeleportEntityTranslator.java @@ -25,26 +25,16 @@ package org.geysermc.geyser.translator.protocol.java.entity; -import org.geysermc.mcprotocollib.protocol.packet.ingame.clientbound.entity.ClientboundTeleportEntityPacket; -import org.cloudburstmc.math.vector.Vector3f; -import org.geysermc.geyser.entity.type.Entity; -import org.geysermc.geyser.entity.vehicle.ClientVehicle; import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.translator.protocol.PacketTranslator; import org.geysermc.geyser.translator.protocol.Translator; +import org.geysermc.mcprotocollib.protocol.packet.ingame.clientbound.entity.ClientboundTeleportEntityPacket; @Translator(packet = ClientboundTeleportEntityPacket.class) public class JavaTeleportEntityTranslator extends PacketTranslator { @Override public void translate(GeyserSession session, ClientboundTeleportEntityPacket packet) { - Entity entity = session.getEntityCache().getEntityByJavaId(packet.getEntityId()); - if (entity == null) return; - - if (entity instanceof ClientVehicle clientVehicle) { - clientVehicle.getVehicleComponent().moveAbsolute(packet.getX(), packet.getY(), packet.getZ()); - } - - entity.teleport(Vector3f.from(packet.getX(), packet.getY(), packet.getZ()), packet.getYaw(), packet.getPitch(), packet.isOnGround()); + session.getGeyser().getLogger().info(packet.toString()); } } diff --git a/core/src/main/java/org/geysermc/geyser/translator/protocol/java/entity/player/JavaCookieRequestTranslator.java b/core/src/main/java/org/geysermc/geyser/translator/protocol/java/entity/player/JavaCookieRequestTranslator.java index 33bfa7be8..a44faed29 100644 --- a/core/src/main/java/org/geysermc/geyser/translator/protocol/java/entity/player/JavaCookieRequestTranslator.java +++ b/core/src/main/java/org/geysermc/geyser/translator/protocol/java/entity/player/JavaCookieRequestTranslator.java @@ -38,7 +38,7 @@ public class JavaCookieRequestTranslator extends PacketTranslator GeyserImpl.getInstance().getLogger().debug("Loaded Local Bedrock Java Skin Data for " + session.getClientData().getUsername())); - } else { - playerEntity.setValid(true); } } } diff --git a/core/src/main/java/org/geysermc/geyser/translator/protocol/java/entity/player/JavaPlayerLookAtTranslator.java b/core/src/main/java/org/geysermc/geyser/translator/protocol/java/entity/player/JavaPlayerLookAtTranslator.java index b1413542b..22786e918 100644 --- a/core/src/main/java/org/geysermc/geyser/translator/protocol/java/entity/player/JavaPlayerLookAtTranslator.java +++ b/core/src/main/java/org/geysermc/geyser/translator/protocol/java/entity/player/JavaPlayerLookAtTranslator.java @@ -49,7 +49,7 @@ public class JavaPlayerLookAtTranslator extends PacketTranslator { @@ -50,14 +51,15 @@ public class JavaPlayerPositionTranslator extends PacketTranslator 32 && !session.isEmulatePost1_13Logic()) { // See DimensionUtils for an explanation @@ -92,23 +100,23 @@ public class JavaPlayerPositionTranslator extends PacketTranslator { + + @Override + public void translate(GeyserSession session, ClientboundPlayerRotationPacket packet) { + session.getPlayerEntity().updateOwnRotation(packet.getYRot(), packet.getXRot(), packet.getYRot()); + } +} diff --git a/core/src/main/java/org/geysermc/geyser/translator/protocol/java/entity/player/JavaSetCarriedItemTranslator.java b/core/src/main/java/org/geysermc/geyser/translator/protocol/java/entity/player/JavaSetHeldSlotTranslator.java similarity index 86% rename from core/src/main/java/org/geysermc/geyser/translator/protocol/java/entity/player/JavaSetCarriedItemTranslator.java rename to core/src/main/java/org/geysermc/geyser/translator/protocol/java/entity/player/JavaSetHeldSlotTranslator.java index e590b5658..a02aa61ee 100644 --- a/core/src/main/java/org/geysermc/geyser/translator/protocol/java/entity/player/JavaSetCarriedItemTranslator.java +++ b/core/src/main/java/org/geysermc/geyser/translator/protocol/java/entity/player/JavaSetHeldSlotTranslator.java @@ -25,17 +25,17 @@ package org.geysermc.geyser.translator.protocol.java.entity.player; -import org.geysermc.mcprotocollib.protocol.packet.ingame.clientbound.entity.player.ClientboundSetCarriedItemPacket; import org.cloudburstmc.protocol.bedrock.packet.PlayerHotbarPacket; import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.translator.protocol.PacketTranslator; import org.geysermc.geyser.translator.protocol.Translator; +import org.geysermc.mcprotocollib.protocol.packet.ingame.clientbound.entity.player.ClientboundSetHeldSlotPacket; -@Translator(packet = ClientboundSetCarriedItemPacket.class) -public class JavaSetCarriedItemTranslator extends PacketTranslator { +@Translator(packet = ClientboundSetHeldSlotPacket.class) +public class JavaSetHeldSlotTranslator extends PacketTranslator { @Override - public void translate(GeyserSession session, ClientboundSetCarriedItemPacket packet) { + public void translate(GeyserSession session, ClientboundSetHeldSlotPacket packet) { PlayerHotbarPacket hotbarPacket = new PlayerHotbarPacket(); hotbarPacket.setContainerId(0); hotbarPacket.setSelectedHotbarSlot(packet.getSlot()); 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 572d233d0..ed1951243 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 @@ -25,17 +25,14 @@ package org.geysermc.geyser.translator.protocol.java.entity.spawn; -import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.Pose; -import org.geysermc.mcprotocollib.protocol.data.game.entity.object.Direction; -import org.geysermc.mcprotocollib.protocol.data.game.entity.object.FallingBlockData; -import org.geysermc.mcprotocollib.protocol.data.game.entity.object.ProjectileData; -import org.geysermc.mcprotocollib.protocol.data.game.entity.object.WardenData; -import org.geysermc.mcprotocollib.protocol.data.game.entity.type.EntityType; -import org.geysermc.mcprotocollib.protocol.packet.ingame.clientbound.entity.spawn.ClientboundAddEntityPacket; import org.cloudburstmc.math.vector.Vector3f; import org.geysermc.geyser.GeyserImpl; import org.geysermc.geyser.entity.EntityDefinition; -import org.geysermc.geyser.entity.type.*; +import org.geysermc.geyser.entity.type.Entity; +import org.geysermc.geyser.entity.type.FallingBlockEntity; +import org.geysermc.geyser.entity.type.FishingHookEntity; +import org.geysermc.geyser.entity.type.ItemFrameEntity; +import org.geysermc.geyser.entity.type.PaintingEntity; import org.geysermc.geyser.entity.type.player.PlayerEntity; import org.geysermc.geyser.registry.Registries; import org.geysermc.geyser.session.GeyserSession; @@ -43,6 +40,14 @@ import org.geysermc.geyser.skin.SkinManager; 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.EnvironmentUtils; +import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.Pose; +import org.geysermc.mcprotocollib.protocol.data.game.entity.object.Direction; +import org.geysermc.mcprotocollib.protocol.data.game.entity.object.FallingBlockData; +import org.geysermc.mcprotocollib.protocol.data.game.entity.object.ProjectileData; +import org.geysermc.mcprotocollib.protocol.data.game.entity.object.WardenData; +import org.geysermc.mcprotocollib.protocol.data.game.entity.type.EntityType; +import org.geysermc.mcprotocollib.protocol.packet.ingame.clientbound.entity.spawn.ClientboundAddEntityPacket; @Translator(packet = ClientboundAddEntityPacket.class) public class JavaAddEntityTranslator extends PacketTranslator { @@ -83,10 +88,13 @@ public class JavaAddEntityTranslator extends PacketTranslator { + session.setContainerOutputFuture(session.scheduleInEventLoop(() -> { int offset = gridSize == 4 ? 28 : 32; int gridDimensions = gridSize == 4 ? 2 : 3; int firstRow = -1, height = -1; @@ -169,27 +173,25 @@ public class JavaContainerSetSlotTranslator extends PacketTranslator javaIngredients = new ArrayList<>(height * width); int index = 0; for (int row = firstRow; row < height + firstRow; row++) { for (int col = firstCol; col < width + firstCol; col++) { GeyserItemStack geyserItemStack = inventory.getItem(col + (row * gridDimensions) + 1); ingredients[index] = geyserItemStack.getItemData(session); - ItemStack[] itemStacks = new ItemStack[] {geyserItemStack.isEmpty() ? null : geyserItemStack.getItemStack(1)}; - javaIngredients[index] = new Ingredient(itemStacks); + javaIngredients.add(geyserItemStack.asSlotDisplay()); InventorySlotPacket slotPacket = new InventorySlotPacket(); slotPacket.setContainerId(ContainerId.UI); slotPacket.setSlot(col + (row * gridDimensions) + offset); slotPacket.setItem(ItemData.AIR); - slotPacket.setContainerNameData(new FullContainerName(ContainerSlotType.ANVIL_INPUT, null)); session.sendUpstreamPacket(slotPacket); index++; } } // Cache this recipe so we know the client has received it - session.getCraftingRecipes().put(newRecipeId, new GeyserShapedRecipe(width, height, javaIngredients, item)); + session.getCraftingRecipes().put(newRecipeId, new GeyserShapedRecipe(width, height, javaIngredients, new ItemStackSlotDisplay(item))); CraftingDataPacket craftPacket = new CraftingDataPacket(); craftPacket.getCraftingData().add(ShapedRecipeData.shaped( @@ -215,11 +217,78 @@ public class JavaContainerSetSlotTranslator extends PacketTranslator { + GeyserItemStack template = inventory.getItem(SmithingInventoryTranslator.TEMPLATE); + if (template.asItem() != Items.NETHERITE_UPGRADE_SMITHING_TEMPLATE) { + // Technically we should probably also do this for custom items, but last I checked Bedrock doesn't even support that. + return; + } + + GeyserItemStack input = inventory.getItem(SmithingInventoryTranslator.INPUT); + GeyserItemStack material = inventory.getItem(SmithingInventoryTranslator.MATERIAL); + GeyserItemStack geyserOutput = GeyserItemStack.from(output); + + for (GeyserSmithingRecipe recipe : session.getSmithingRecipes()) { + if (InventoryUtils.acceptsAsInput(session, recipe.result(), geyserOutput) + && InventoryUtils.acceptsAsInput(session, recipe.base(), input) + && InventoryUtils.acceptsAsInput(session, recipe.addition(), material) + && InventoryUtils.acceptsAsInput(session, recipe.template(), template)) { + // The client already recognizes this item. + return; + } + } + + session.getSmithingRecipes().add(new GeyserSmithingRecipe( + template.asSlotDisplay(), + input.asSlotDisplay(), + material.asSlotDisplay(), + new ItemStackSlotDisplay(output) + )); + + UUID uuid = UUID.randomUUID(); + + CraftingDataPacket craftPacket = new CraftingDataPacket(); + craftPacket.getCraftingData().add(SmithingTransformRecipeData.of( + uuid.toString(), + ItemDescriptorWithCount.fromItem(ItemTranslator.translateToBedrock(session, template.getItemStack())), + ItemDescriptorWithCount.fromItem(ItemTranslator.translateToBedrock(session, input.getItemStack())), + ItemDescriptorWithCount.fromItem(ItemTranslator.translateToBedrock(session, material.getItemStack())), + ItemTranslator.translateToBedrock(session, output), + "smithing_table", + session.getLastRecipeNetId().incrementAndGet() + )); + craftPacket.setCleanRecipes(false); + session.sendUpstreamPacket(craftPacket); + + // Just set one of the slots to air, then right back to its proper item. + InventorySlotPacket slotPacket = new InventorySlotPacket(); + slotPacket.setContainerId(ContainerId.UI); + slotPacket.setSlot(session.getInventoryTranslator().javaSlotToBedrock(SmithingInventoryTranslator.MATERIAL)); + slotPacket.setItem(ItemData.AIR); + session.sendUpstreamPacket(slotPacket); + + session.getInventoryTranslator().updateSlot(session, inventory, SmithingInventoryTranslator.MATERIAL); + }, 150, TimeUnit.MILLISECONDS)); + } } diff --git a/core/src/main/java/org/geysermc/geyser/translator/protocol/java/inventory/JavaOpenScreenTranslator.java b/core/src/main/java/org/geysermc/geyser/translator/protocol/java/inventory/JavaOpenScreenTranslator.java index edb13f806..bc4e22b5a 100644 --- a/core/src/main/java/org/geysermc/geyser/translator/protocol/java/inventory/JavaOpenScreenTranslator.java +++ b/core/src/main/java/org/geysermc/geyser/translator/protocol/java/inventory/JavaOpenScreenTranslator.java @@ -52,8 +52,9 @@ public class JavaOpenScreenTranslator extends PacketTranslator { +@Translator(packet = ClientboundSetCursorItemPacket.class) +public class JavaSetCursorItemTranslator extends PacketTranslator { @Override - public void translate(GeyserSession session, ClientboundFinishConfigurationPacket packet) { - // Clear the player list, as on Java the player list is cleared after transitioning from config to play phase - PlayerListPacket playerListPacket = new PlayerListPacket(); - playerListPacket.setAction(PlayerListPacket.Action.REMOVE); - for (PlayerEntity otherEntity : session.getEntityCache().getAllPlayerEntities()) { - playerListPacket.getEntries().add(new PlayerListPacket.Entry(otherEntity.getTabListUuid())); - } - session.sendUpstreamPacket(playerListPacket); - session.getEntityCache().removeAllPlayerEntities(); + public void translate(GeyserSession session, ClientboundSetCursorItemPacket packet) { + GeyserItemStack newItem = GeyserItemStack.from(packet.getContents()); + session.getPlayerInventory().setCursor(newItem, session); + InventoryUtils.updateCursor(session); } } diff --git a/core/src/main/java/org/geysermc/geyser/translator/protocol/java/inventory/JavaSetPlayerInventoryTranslator.java b/core/src/main/java/org/geysermc/geyser/translator/protocol/java/inventory/JavaSetPlayerInventoryTranslator.java new file mode 100644 index 000000000..58b30bfc8 --- /dev/null +++ b/core/src/main/java/org/geysermc/geyser/translator/protocol/java/inventory/JavaSetPlayerInventoryTranslator.java @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2024 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.inventory; + +import org.geysermc.geyser.GeyserLogger; +import org.geysermc.geyser.inventory.GeyserItemStack; +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.mcprotocollib.protocol.packet.ingame.clientbound.inventory.ClientboundSetPlayerInventoryPacket; + +@Translator(packet = ClientboundSetPlayerInventoryPacket.class) +public class JavaSetPlayerInventoryTranslator extends PacketTranslator { + + @Override + public void translate(GeyserSession session, ClientboundSetPlayerInventoryPacket packet) { + int slot = packet.getSlot(); + if (slot >= 0 && slot <= 8) { + // As of 1.21.3 - can be replicated in vanilla server survival by picking an item in-world in your inventory not in your hotbar. + slot = session.getPlayerInventory().getOffsetForHotbar(slot); + } + + if (slot >= session.getPlayerInventory().getSize()) { + GeyserLogger logger = session.getGeyser().getLogger(); + logger.warning("ClientboundSetPlayerInventoryPacket sent to " + session.bedrockUsername() + + " that exceeds inventory size!"); + if (logger.isDebug()) { + logger.debug(packet.toString()); + logger.debug(session.getPlayerInventory().toString()); + } + return; + } + + session.getPlayerInventory().setItem(slot, GeyserItemStack.from(packet.getContents()), session); + InventoryTranslator.PLAYER_INVENTORY_TRANSLATOR.updateSlot(session, session.getPlayerInventory(), slot); + } +} diff --git a/core/src/main/java/org/geysermc/geyser/translator/protocol/java/level/JavaCooldownTranslator.java b/core/src/main/java/org/geysermc/geyser/translator/protocol/java/level/JavaCooldownTranslator.java index 636671651..2b14f015f 100644 --- a/core/src/main/java/org/geysermc/geyser/translator/protocol/java/level/JavaCooldownTranslator.java +++ b/core/src/main/java/org/geysermc/geyser/translator/protocol/java/level/JavaCooldownTranslator.java @@ -25,6 +25,7 @@ package org.geysermc.geyser.translator.protocol.java.level; +import net.kyori.adventure.key.Key; import org.geysermc.mcprotocollib.protocol.packet.ingame.clientbound.ClientboundCooldownPacket; import org.cloudburstmc.protocol.bedrock.packet.PlayerStartItemCooldownPacket; import org.geysermc.geyser.item.Items; @@ -39,7 +40,11 @@ public class JavaCooldownTranslator extends PacketTranslator entry : session.getItemFrameCache().entrySet()) { diff --git a/core/src/main/java/org/geysermc/geyser/translator/protocol/java/level/JavaLevelParticlesTranslator.java b/core/src/main/java/org/geysermc/geyser/translator/protocol/java/level/JavaLevelParticlesTranslator.java index 65f84a595..336fedc65 100644 --- a/core/src/main/java/org/geysermc/geyser/translator/protocol/java/level/JavaLevelParticlesTranslator.java +++ b/core/src/main/java/org/geysermc/geyser/translator/protocol/java/level/JavaLevelParticlesTranslator.java @@ -132,10 +132,7 @@ public class JavaLevelParticlesTranslator extends PacketTranslator { //TODO DustParticleData data = (DustParticleData) particle.getData(); - int r = (int) (data.getRed() * 255); - int g = (int) (data.getGreen() * 255); - int b = (int) (data.getBlue() * 255); - int rgbData = ((0xff) << 24) | ((r & 0xff) << 16) | ((g & 0xff) << 8) | (b & 0xff); + int rgbData = data.getColor(); return (position) -> { LevelEventPacket packet = new LevelEventPacket(); packet.setType(ParticleType.FALLING_DUST); diff --git a/core/src/main/java/org/geysermc/geyser/translator/protocol/java/level/JavaSetTimeTranslator.java b/core/src/main/java/org/geysermc/geyser/translator/protocol/java/level/JavaSetTimeTranslator.java index 43ef0870a..b7a92dbd4 100644 --- a/core/src/main/java/org/geysermc/geyser/translator/protocol/java/level/JavaSetTimeTranslator.java +++ b/core/src/main/java/org/geysermc/geyser/translator/protocol/java/level/JavaSetTimeTranslator.java @@ -36,11 +36,9 @@ public class JavaSetTimeTranslator extends PacketTranslator= 0) { - // Client thinks there is no daylight cycle but there is - session.setDaylightCycle(true); - } else if (session.isDaylightCycle() && time < 0) { - // Client thinks there is daylight cycle but there isn't - session.setDaylightCycle(false); + + // We need to send a gamerule if this changed + if (session.isDaylightCycle() != packet.isTickDayTime()) { + session.setDaylightCycle(packet.isTickDayTime()); } } } diff --git a/core/src/main/java/org/geysermc/geyser/translator/protocol/java/scoreboard/JavaResetScorePacket.java b/core/src/main/java/org/geysermc/geyser/translator/protocol/java/scoreboard/JavaResetScorePacket.java index e8d307c90..72dcd4062 100644 --- a/core/src/main/java/org/geysermc/geyser/translator/protocol/java/scoreboard/JavaResetScorePacket.java +++ b/core/src/main/java/org/geysermc/geyser/translator/protocol/java/scoreboard/JavaResetScorePacket.java @@ -25,6 +25,8 @@ package org.geysermc.geyser.translator.protocol.java.scoreboard; +import org.geysermc.geyser.GeyserImpl; +import org.geysermc.geyser.GeyserLogger; import org.geysermc.geyser.scoreboard.Objective; import org.geysermc.geyser.scoreboard.Scoreboard; import org.geysermc.geyser.scoreboard.ScoreboardUpdater; @@ -32,11 +34,13 @@ import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.session.cache.WorldCache; import org.geysermc.geyser.translator.protocol.PacketTranslator; import org.geysermc.geyser.translator.protocol.Translator; -import org.geysermc.mcprotocollib.protocol.data.game.scoreboard.ScoreboardPosition; import org.geysermc.mcprotocollib.protocol.packet.ingame.clientbound.scoreboard.ClientboundResetScorePacket; @Translator(packet = ClientboundResetScorePacket.class) public class JavaResetScorePacket extends PacketTranslator { + private static final boolean SHOW_SCOREBOARD_LOGS = Boolean.parseBoolean(System.getProperty("Geyser.ShowScoreboardLogs", "true")); + + private final GeyserLogger logger = GeyserImpl.getInstance().getLogger(); @Override public void translate(GeyserSession session, ClientboundResetScorePacket packet) { @@ -44,28 +48,20 @@ public class JavaResetScorePacket extends PacketTranslator { diff --git a/core/src/main/java/org/geysermc/geyser/translator/protocol/java/scoreboard/JavaSetObjectiveTranslator.java b/core/src/main/java/org/geysermc/geyser/translator/protocol/java/scoreboard/JavaSetObjectiveTranslator.java index 85d93c0b5..0a7c6131f 100644 --- a/core/src/main/java/org/geysermc/geyser/translator/protocol/java/scoreboard/JavaSetObjectiveTranslator.java +++ b/core/src/main/java/org/geysermc/geyser/translator/protocol/java/scoreboard/JavaSetObjectiveTranslator.java @@ -25,72 +25,45 @@ package org.geysermc.geyser.translator.protocol.java.scoreboard; -import org.geysermc.mcprotocollib.protocol.data.game.scoreboard.ObjectiveAction; -import org.geysermc.mcprotocollib.protocol.data.game.scoreboard.ScoreboardPosition; -import org.geysermc.mcprotocollib.protocol.packet.ingame.clientbound.scoreboard.ClientboundSetObjectivePacket; -import org.geysermc.geyser.GeyserImpl; -import org.geysermc.geyser.GeyserLogger; -import org.geysermc.geyser.entity.type.player.PlayerEntity; import org.geysermc.geyser.scoreboard.Objective; import org.geysermc.geyser.scoreboard.Scoreboard; import org.geysermc.geyser.scoreboard.ScoreboardUpdater; -import org.geysermc.geyser.scoreboard.UpdateType; import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.session.cache.WorldCache; import org.geysermc.geyser.translator.protocol.PacketTranslator; import org.geysermc.geyser.translator.protocol.Translator; -import org.geysermc.geyser.translator.text.MessageTranslator; +import org.geysermc.mcprotocollib.protocol.data.game.scoreboard.ObjectiveAction; +import org.geysermc.mcprotocollib.protocol.packet.ingame.clientbound.scoreboard.ClientboundSetObjectivePacket; @Translator(packet = ClientboundSetObjectivePacket.class) public class JavaSetObjectiveTranslator extends PacketTranslator { - private final GeyserLogger logger = GeyserImpl.getInstance().getLogger(); - @Override public void translate(GeyserSession session, ClientboundSetObjectivePacket packet) { WorldCache worldCache = session.getWorldCache(); Scoreboard scoreboard = worldCache.getScoreboard(); int pps = worldCache.increaseAndGetScoreboardPacketsPerSecond(); - Objective objective = scoreboard.getObjective(packet.getName()); - if (objective != null && objective.getUpdateType() != UpdateType.REMOVE && packet.getAction() == ObjectiveAction.ADD) { - // matches vanilla behaviour - logger.warning("An objective with the same name '" + packet.getName() + "' already exists! Ignoring packet"); + Objective objective; + if (packet.getAction() == ObjectiveAction.ADD) { + objective = scoreboard.registerNewObjective(packet.getName()); + } else { + objective = scoreboard.getObjective(packet.getName()); + } + + // matches vanilla + if (objective == null) { return; } - if ((objective == null || objective.getUpdateType() == UpdateType.REMOVE) && packet.getAction() != ObjectiveAction.REMOVE) { - objective = scoreboard.registerNewObjective(packet.getName()); - } - switch (packet.getAction()) { - case ADD, UPDATE -> { - objective.setDisplayName(MessageTranslator.convertMessage(packet.getDisplayName())) - .setNumberFormat(packet.getNumberFormat()) - .setType(packet.getType().ordinal()); - if (objective == scoreboard.getObjectiveSlots().get(ScoreboardPosition.BELOW_NAME)) { - // Update the score tag of all players - for (PlayerEntity entity : session.getEntityCache().getAllPlayerEntities()) { - if (entity.isValid()) { - entity.setBelowNameText(objective); - } - } - } - } - case REMOVE -> { - scoreboard.unregisterObjective(packet.getName()); - if (objective != null && objective == scoreboard.getObjectiveSlots().get(ScoreboardPosition.BELOW_NAME)) { - // Clear the score tag from all players - for (PlayerEntity entity : session.getEntityCache().getAllPlayerEntities()) { - // Other places we check for the entity being valid, - // but we must set the below name text as null for all players - // or else PlayerEntity#spawnEntity will find a null objective and not touch EntityData#SCORE_TAG - entity.setBelowNameText(null); - } - } - } + case ADD, UPDATE -> + objective.updateProperties(packet.getDisplayName(), packet.getType(), packet.getNumberFormat()); + case REMOVE -> scoreboard.removeObjective(objective); } - if (objective == null || !objective.isActive()) { + // Scoreboard#removeObjective doesn't touch the display slot(s) that were attached to it. + // So Objective#hasDisplaySlot will be true as long as it's currently present on the Bedrock client + if (!objective.hasDisplaySlot()) { return; } diff --git a/core/src/main/java/org/geysermc/geyser/translator/protocol/java/scoreboard/JavaSetPlayerTeamTranslator.java b/core/src/main/java/org/geysermc/geyser/translator/protocol/java/scoreboard/JavaSetPlayerTeamTranslator.java index 999edcc8c..3a1ee6373 100644 --- a/core/src/main/java/org/geysermc/geyser/translator/protocol/java/scoreboard/JavaSetPlayerTeamTranslator.java +++ b/core/src/main/java/org/geysermc/geyser/translator/protocol/java/scoreboard/JavaSetPlayerTeamTranslator.java @@ -25,23 +25,17 @@ package org.geysermc.geyser.translator.protocol.java.scoreboard; -import org.geysermc.mcprotocollib.protocol.data.game.scoreboard.NameTagVisibility; -import org.geysermc.mcprotocollib.protocol.data.game.scoreboard.TeamAction; -import org.geysermc.mcprotocollib.protocol.data.game.scoreboard.TeamColor; -import org.geysermc.mcprotocollib.protocol.packet.ingame.clientbound.scoreboard.ClientboundSetPlayerTeamPacket; +import java.util.Arrays; import org.geysermc.geyser.GeyserImpl; import org.geysermc.geyser.GeyserLogger; import org.geysermc.geyser.scoreboard.Scoreboard; import org.geysermc.geyser.scoreboard.ScoreboardUpdater; import org.geysermc.geyser.scoreboard.Team; -import org.geysermc.geyser.scoreboard.UpdateType; import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.translator.protocol.PacketTranslator; import org.geysermc.geyser.translator.protocol.Translator; -import org.geysermc.geyser.translator.text.MessageTranslator; - -import java.util.Arrays; -import java.util.Set; +import org.geysermc.mcprotocollib.protocol.data.game.scoreboard.TeamAction; +import org.geysermc.mcprotocollib.protocol.packet.ingame.clientbound.scoreboard.ClientboundSetPlayerTeamPacket; @Translator(packet = ClientboundSetPlayerTeamPacket.class) public class JavaSetPlayerTeamTranslator extends PacketTranslator { @@ -60,83 +54,45 @@ public class JavaSetPlayerTeamTranslator extends PacketTranslator { - team = scoreboard.registerNewTeam(packet.getTeamName(), packet.getPlayers()) - .setName(MessageTranslator.convertMessage(packet.getDisplayName())) - .setColor(packet.getColor()) - .setNameTagVisibility(packet.getNameTagVisibility()) - .setPrefix(MessageTranslator.convertMessage(packet.getPrefix(), session.locale())) - .setSuffix(MessageTranslator.convertMessage(packet.getSuffix(), session.locale())); - if (packet.getPlayers().length != 0) { - if ((team.getNameTagVisibility() != NameTagVisibility.ALWAYS && !team.isVisibleFor(session.getPlayerEntity().getUsername())) - || team.getColor() != TeamColor.RESET - || !team.getCurrentData().getPrefix().isEmpty() - || !team.getCurrentData().getSuffix().isEmpty()) { - // Something is here that would modify entity names - scoreboard.updateEntityNames(team, true); - } + if (packet.getAction() == TeamAction.CREATE) { + scoreboard.registerNewTeam( + packet.getTeamName(), + packet.getPlayers(), + packet.getDisplayName(), + packet.getPrefix(), + packet.getSuffix(), + packet.getNameTagVisibility(), + packet.getColor() + ); + } else { + Team team = scoreboard.getTeam(packet.getTeamName()); + if (team == null) { + if (logger.isDebug()) { + logger.debug("Error while translating Team Packet " + packet.getAction() + + "! Scoreboard Team " + packet.getTeamName() + " is not registered." + ); } + return; } - case UPDATE -> { - if (team == null) { - if (logger.isDebug()) { - logger.debug("Error while translating Team Packet " + packet.getAction() - + "! Scoreboard Team " + packet.getTeamName() + " is not registered." - ); - } - return; - } - TeamColor oldColor = team.getColor(); - NameTagVisibility oldVisibility = team.getNameTagVisibility(); - String oldPrefix = team.getCurrentData().getPrefix(); - String oldSuffix = team.getCurrentData().getSuffix(); - - team.setName(MessageTranslator.convertMessage(packet.getDisplayName())) - .setColor(packet.getColor()) - .setNameTagVisibility(packet.getNameTagVisibility()) - .setPrefix(MessageTranslator.convertMessage(packet.getPrefix(), session.locale())) - .setSuffix(MessageTranslator.convertMessage(packet.getSuffix(), session.locale())) - .setUpdateType(UpdateType.UPDATE); - - if (oldVisibility != team.getNameTagVisibility() - || oldColor != team.getColor() - || !oldPrefix.equals(team.getCurrentData().getPrefix()) - || !oldSuffix.equals(team.getCurrentData().getSuffix())) { - // Update entities attached to this team as something about their nameplates have changed - scoreboard.updateEntityNames(team, false); + switch (packet.getAction()) { + case UPDATE -> { + team.updateProperties( + packet.getDisplayName(), + packet.getPrefix(), + packet.getSuffix(), + packet.getNameTagVisibility(), + packet.getColor() + ); } + case ADD_PLAYER -> team.addEntities(packet.getPlayers()); + case REMOVE_PLAYER -> team.removeEntities(packet.getPlayers()); + case REMOVE -> scoreboard.removeTeam(packet.getTeamName()); } - case ADD_PLAYER -> { - if (team == null) { - if (logger.isDebug()) { - logger.debug("Error while translating Team Packet " + packet.getAction() - + "! Scoreboard Team " + packet.getTeamName() + " is not registered." - ); - } - return; - } - Set added = team.addEntities(packet.getPlayers()); - scoreboard.updateEntityNames(team, added, true); - } - case REMOVE_PLAYER -> { - if (team == null) { - if (logger.isDebug()) { - logger.debug("Error while translating Team Packet " + packet.getAction() - + "! Scoreboard Team " + packet.getTeamName() + " is not registered." - ); - } - return; - } - Set removed = team.removeEntities(packet.getPlayers()); - scoreboard.updateEntityNames(null, removed, true); - } - case REMOVE -> scoreboard.removeTeam(packet.getTeamName()); } + // ScoreboardUpdater will handle it for us if the packets per second // (for score and team packets) is higher than the first threshold if (pps < ScoreboardUpdater.FIRST_SCORE_PACKETS_PER_SECOND_THRESHOLD) { diff --git a/core/src/main/java/org/geysermc/geyser/translator/protocol/java/scoreboard/JavaSetScoreTranslator.java b/core/src/main/java/org/geysermc/geyser/translator/protocol/java/scoreboard/JavaSetScoreTranslator.java index d1645b496..c7159041b 100644 --- a/core/src/main/java/org/geysermc/geyser/translator/protocol/java/scoreboard/JavaSetScoreTranslator.java +++ b/core/src/main/java/org/geysermc/geyser/translator/protocol/java/scoreboard/JavaSetScoreTranslator.java @@ -25,30 +25,22 @@ package org.geysermc.geyser.translator.protocol.java.scoreboard; -import org.geysermc.mcprotocollib.protocol.data.game.scoreboard.ScoreboardPosition; -import org.geysermc.mcprotocollib.protocol.packet.ingame.clientbound.scoreboard.ClientboundSetScorePacket; -import org.checkerframework.checker.nullness.qual.Nullable; import org.geysermc.geyser.GeyserImpl; import org.geysermc.geyser.GeyserLogger; -import org.geysermc.geyser.entity.type.player.PlayerEntity; import org.geysermc.geyser.scoreboard.Objective; import org.geysermc.geyser.scoreboard.Scoreboard; import org.geysermc.geyser.scoreboard.ScoreboardUpdater; import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.session.cache.WorldCache; -import org.geysermc.geyser.text.GeyserLocale; import org.geysermc.geyser.translator.protocol.PacketTranslator; import org.geysermc.geyser.translator.protocol.Translator; +import org.geysermc.mcprotocollib.protocol.packet.ingame.clientbound.scoreboard.ClientboundSetScorePacket; @Translator(packet = ClientboundSetScorePacket.class) public class JavaSetScoreTranslator extends PacketTranslator { private static final boolean SHOW_SCOREBOARD_LOGS = Boolean.parseBoolean(System.getProperty("Geyser.ShowScoreboardLogs", "true")); - private final GeyserLogger logger; - - public JavaSetScoreTranslator() { - logger = GeyserImpl.getInstance().getLogger(); - } + private final GeyserLogger logger = GeyserImpl.getInstance().getLogger(); @Override public void translate(GeyserSession session, ClientboundSetScorePacket packet) { @@ -59,20 +51,13 @@ public class JavaSetScoreTranslator extends PacketTranslator TEAM_COLORS = new EnumMap<>(TeamColor.class); - // Legacy formatting character private static final String BASE = "\u00a7"; @@ -81,31 +74,6 @@ public class MessageTranslator { private static final String RESET = BASE + "r"; static { - TEAM_COLORS.put(TeamColor.RESET, RESET); - - TEAM_COLORS.put(TeamColor.BLACK, BASE + "0"); - TEAM_COLORS.put(TeamColor.DARK_BLUE, BASE + "1"); - TEAM_COLORS.put(TeamColor.DARK_GREEN, BASE + "2"); - TEAM_COLORS.put(TeamColor.DARK_AQUA, BASE + "3"); - TEAM_COLORS.put(TeamColor.DARK_RED, BASE + "4"); - TEAM_COLORS.put(TeamColor.DARK_PURPLE, BASE + "5"); - TEAM_COLORS.put(TeamColor.GOLD, BASE + "6"); - TEAM_COLORS.put(TeamColor.GRAY, BASE + "7"); - TEAM_COLORS.put(TeamColor.DARK_GRAY, BASE + "8"); - TEAM_COLORS.put(TeamColor.BLUE, BASE + "9"); - TEAM_COLORS.put(TeamColor.GREEN, BASE + "a"); - TEAM_COLORS.put(TeamColor.AQUA, BASE + "b"); - TEAM_COLORS.put(TeamColor.RED, BASE + "c"); - TEAM_COLORS.put(TeamColor.LIGHT_PURPLE, BASE + "d"); - TEAM_COLORS.put(TeamColor.YELLOW, BASE + "e"); - TEAM_COLORS.put(TeamColor.WHITE, BASE + "f"); - - // Formats, not colors - TEAM_COLORS.put(TeamColor.OBFUSCATED, BASE + "k"); - TEAM_COLORS.put(TeamColor.BOLD, BASE + "l"); - TEAM_COLORS.put(TeamColor.STRIKETHROUGH, BASE + "m"); - TEAM_COLORS.put(TeamColor.ITALIC, BASE + "o"); - // Temporary fix for https://github.com/KyoriPowered/adventure/issues/447 - TODO resolve properly GsonComponentSerializer source = DefaultComponentSerializer.get() .toBuilder() @@ -157,13 +125,43 @@ public class MessageTranslator { } /** - * Convert a Java message to the legacy format ready for bedrock + * Convert a Java message to the legacy format ready for bedrock. Unlike + * {@link #convertMessageRaw(Component, String)} this adds a leading color reset. In Bedrock + * some places have build-in colors. * * @param message Java message * @param locale Locale to use for translation strings * @return Parsed and formatted message for bedrock */ public static String convertMessage(Component message, String locale) { + return convertMessage(message, locale, true); + } + + /** + * Convert a Java message to the legacy format ready for bedrock, for use in item tooltips + * (a gray color is applied). + * + * @param message Java message + * @param locale Locale to use for translation strings + * @return Parsed and formatted message for bedrock, in gray color + */ + public static String convertMessageForTooltip(Component message, String locale) { + return RESET + ChatColor.GRAY + convertMessageRaw(message, locale); + } + + /** + * Convert a Java message to the legacy format ready for bedrock. Unlike {@link #convertMessage(Component, String)} + * this version does not add a leading color reset. In Bedrock some places have build-in colors. + * + * @param message Java message + * @param locale Locale to use for translation strings + * @return Parsed and formatted message for bedrock + */ + public static String convertMessageRaw(Component message, String locale) { + return convertMessage(message, locale, false); + } + + private static String convertMessage(Component message, String locale, boolean addLeadingResetFormat) { try { // Translate any components that require it message = RENDERER.render(message, locale); @@ -172,7 +170,7 @@ public class MessageTranslator { StringBuilder finalLegacy = new StringBuilder(); char[] legacyChars = legacy.toCharArray(); - boolean lastFormatReset = false; + boolean lastFormatReset = !addLeadingResetFormat; for (int i = 0; i < legacyChars.length; i++) { char legacyChar = legacyChars[i]; if (legacyChar != ChatColor.ESCAPE || i >= legacyChars.length - 1) { @@ -185,7 +183,7 @@ public class MessageTranslator { char next = legacyChars[++i]; if (BEDROCK_COLORS.indexOf(next) != -1) { - // Append this color code, as well as a necessary reset code + // Unlike Java Edition, the ChatFormatting is not reset when a ChatColor is added if (!lastFormatReset) { finalLegacy.append(RESET); } @@ -378,16 +376,6 @@ public class MessageTranslator { session.sendUpstreamPacket(textPacket); } - /** - * Convert a team color to a chat color - * - * @param teamColor Color or format to convert - * @return The chat color character - */ - public static String toChatColor(TeamColor teamColor) { - return TEAM_COLORS.getOrDefault(teamColor, ""); - } - /** * Checks if the given message is over 256 characters (Java edition server chat limit) and sends a message to the user if it is * @@ -446,6 +434,15 @@ public class MessageTranslator { return convertMessage(session, parsed); } + /** + * Deserialize an NbtMap with a description text component (usually provided from a registry) into a Bedrock-formatted string. + */ + public static String deserializeDescriptionForTooltip(GeyserSession session, NbtMap tag) { + Object description = tag.get("description"); + Component parsed = componentFromNbtTag(description); + return convertMessageForTooltip(parsed, session.locale()); + } + public static Component componentFromNbtTag(Object nbtTag) { return componentFromNbtTag(nbtTag, Style.empty()); } diff --git a/core/src/main/java/org/geysermc/geyser/util/BlockUtils.java b/core/src/main/java/org/geysermc/geyser/util/BlockUtils.java index e73fe1a34..6367b2d14 100644 --- a/core/src/main/java/org/geysermc/geyser/util/BlockUtils.java +++ b/core/src/main/java/org/geysermc/geyser/util/BlockUtils.java @@ -223,10 +223,6 @@ public final class BlockUtils { return getCollision(session.getGeyser().getWorldManager().getBlockAt(session, blockPos)); } - public static BlockCollision getCollisionAt(GeyserSession session, int x, int y, int z) { - return getCollision(session.getGeyser().getWorldManager().getBlockAt(session, x, y, z)); - } - private BlockUtils() { } } 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 288b425ba..96471a2ce 100644 --- a/core/src/main/java/org/geysermc/geyser/util/ChunkUtils.java +++ b/core/src/main/java/org/geysermc/geyser/util/ChunkUtils.java @@ -167,7 +167,7 @@ public class ChunkUtils { byteBuf.readBytes(payload); LevelChunkPacket data = new LevelChunkPacket(); - data.setDimension(DimensionUtils.javaToBedrock(session.getBedrockDimension())); + data.setDimension(session.getBedrockDimension().bedrockId()); data.setChunkX(chunkX); data.setChunkZ(chunkZ); data.setSubChunksLength(0); @@ -207,13 +207,6 @@ public class ChunkUtils { int minY = dimension.minY(); int maxY = dimension.maxY(); - if (minY % 16 != 0) { - throw new RuntimeException("Minimum Y must be a multiple of 16!"); - } - if (maxY % 16 != 0) { - throw new RuntimeException("Maximum Y must be a multiple of 16!"); - } - BedrockDimension bedrockDimension = session.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 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 8dc94a165..b4fd6b924 100644 --- a/core/src/main/java/org/geysermc/geyser/util/DimensionUtils.java +++ b/core/src/main/java/org/geysermc/geyser/util/DimensionUtils.java @@ -44,17 +44,8 @@ import java.util.Set; public class DimensionUtils { - // Changes if the above-bedrock Nether building workaround is applied - private static int BEDROCK_NETHER_ID = 1; - public static final String BEDROCK_FOG_HELL = "minecraft:fog_hell"; - public static final String NETHER_IDENTIFIER = "minecraft:the_nether"; - - private static final int BEDROCK_OVERWORLD_ID = 0; - private static final int BEDROCK_DEFAULT_NETHER_ID = 1; - private static final int BEDROCK_END_ID = 2; - public static void switchDimension(GeyserSession session, JavaDimension javaDimension) { switchDimension(session, javaDimension, javaDimension.bedrockId()); } @@ -95,7 +86,7 @@ public class DimensionUtils { // If the bedrock nether height workaround is enabled, meaning the client is told it's in the end dimension, // we check if the player is entering the nether and apply the nether fog to fake the fact that the client // thinks they are in the end dimension. - if (isCustomBedrockNetherId()) { + if (BedrockDimension.isCustomBedrockNetherId()) { if (javaDimension.isNetherLike()) { session.camera().sendFog(BEDROCK_FOG_HELL); } else if (previousDimension != null && previousDimension.isNetherLike()) { @@ -168,22 +159,12 @@ public class DimensionUtils { public static void setBedrockDimension(GeyserSession session, int bedrockDimension) { session.setBedrockDimension(switch (bedrockDimension) { - case BEDROCK_END_ID -> BedrockDimension.THE_END; - case BEDROCK_DEFAULT_NETHER_ID -> BedrockDimension.THE_NETHER; // JavaDimension *should* be set to BEDROCK_END_ID if the Nether workaround is enabled. - default -> BedrockDimension.OVERWORLD; + case BedrockDimension.END_ID -> BedrockDimension.THE_END; + case BedrockDimension.DEFAULT_NETHER_ID -> BedrockDimension.THE_NETHER; // JavaDimension *should* be set to BEDROCK_END_ID if the Nether workaround is enabled. + default -> session.getBedrockOverworldDimension(); }); } - public static int javaToBedrock(BedrockDimension dimension) { - if (dimension == BedrockDimension.THE_NETHER) { - return BEDROCK_NETHER_ID; - } else if (dimension == BedrockDimension.THE_END) { - return BEDROCK_END_ID; - } else { - return BEDROCK_OVERWORLD_ID; - } - } - /** * Map the Java edition dimension IDs to Bedrock edition * @@ -192,9 +173,9 @@ public class DimensionUtils { */ public static int javaToBedrock(String javaDimension) { return switch (javaDimension) { - case NETHER_IDENTIFIER -> BEDROCK_NETHER_ID; - case "minecraft:the_end" -> 2; - default -> 0; + case BedrockDimension.NETHER_IDENTIFIER -> BedrockDimension.BEDROCK_NETHER_ID; + case "minecraft:the_end" -> BedrockDimension.END_ID; + default -> BedrockDimension.OVERWORLD_ID; }; } @@ -204,22 +185,11 @@ public class DimensionUtils { public static int javaToBedrock(GeyserSession session) { JavaDimension dimension = session.getDimensionType(); if (dimension == null) { - return BEDROCK_OVERWORLD_ID; + return BedrockDimension.OVERWORLD_ID; } return dimension.bedrockId(); } - /** - * The Nether dimension in Bedrock does not permit building above Y128 - the Bedrock above the dimension. - * This workaround sets the Nether as the End dimension to ignore this limit. - * - * @param isAboveNetherBedrockBuilding true if we should apply The End workaround - */ - public static void changeBedrockNetherId(boolean isAboveNetherBedrockBuilding) { - // Change dimension ID to the End to allow for building above Bedrock - BEDROCK_NETHER_ID = isAboveNetherBedrockBuilding ? BEDROCK_END_ID : BEDROCK_DEFAULT_NETHER_ID; - } - /** * Gets the fake, temporary dimension we send clients to so we aren't switching to the same dimension without an additional * dimension switch. @@ -229,16 +199,13 @@ public class DimensionUtils { * @return the Bedrock fake dimension to transfer to */ public static int getTemporaryDimension(int currentBedrockDimension, int newBedrockDimension) { - if (isCustomBedrockNetherId()) { + if (BedrockDimension.isCustomBedrockNetherId()) { // Prevents rare instances of Bedrock locking up - return newBedrockDimension == BEDROCK_END_ID ? BEDROCK_OVERWORLD_ID : BEDROCK_END_ID; + return newBedrockDimension == BedrockDimension.END_ID ? BedrockDimension.OVERWORLD_ID : BedrockDimension.END_ID; } // Check current Bedrock dimension and not just the Java dimension. // Fixes rare instances like https://github.com/GeyserMC/Geyser/issues/3161 - return currentBedrockDimension == BEDROCK_OVERWORLD_ID ? BEDROCK_DEFAULT_NETHER_ID : BEDROCK_OVERWORLD_ID; + return currentBedrockDimension == BedrockDimension.OVERWORLD_ID ? BedrockDimension.DEFAULT_NETHER_ID : BedrockDimension.OVERWORLD_ID; } - public static boolean isCustomBedrockNetherId() { - return BEDROCK_NETHER_ID == BEDROCK_END_ID; - } } diff --git a/core/src/main/java/org/geysermc/geyser/util/EntityUtils.java b/core/src/main/java/org/geysermc/geyser/util/EntityUtils.java index 53aefde1e..034975b15 100644 --- a/core/src/main/java/org/geysermc/geyser/util/EntityUtils.java +++ b/core/src/main/java/org/geysermc/geyser/util/EntityUtils.java @@ -25,12 +25,16 @@ package org.geysermc.geyser.util; +import net.kyori.adventure.key.Key; +import org.checkerframework.checker.nullness.qual.NonNull; +import org.checkerframework.checker.nullness.qual.Nullable; import org.cloudburstmc.math.vector.Vector3f; import org.cloudburstmc.protocol.bedrock.data.GameType; import org.cloudburstmc.protocol.bedrock.data.entity.EntityDataTypes; import org.cloudburstmc.protocol.bedrock.data.entity.EntityFlag; import org.geysermc.geyser.entity.EntityDefinitions; import org.geysermc.geyser.entity.type.BoatEntity; +import org.geysermc.geyser.entity.type.ChestBoatEntity; import org.geysermc.geyser.entity.type.Entity; import org.geysermc.geyser.entity.type.TextDisplayEntity; import org.geysermc.geyser.entity.type.living.ArmorStandEntity; @@ -38,6 +42,8 @@ import org.geysermc.geyser.entity.type.living.animal.AnimalEntity; import org.geysermc.geyser.entity.type.living.animal.horse.CamelEntity; import org.geysermc.geyser.inventory.GeyserItemStack; import org.geysermc.geyser.item.Items; +import org.geysermc.geyser.session.GeyserSession; +import org.geysermc.geyser.text.MinecraftLocale; import org.geysermc.mcprotocollib.protocol.data.game.entity.Effect; import org.geysermc.mcprotocollib.protocol.data.game.entity.player.GameMode; import org.geysermc.mcprotocollib.protocol.data.game.entity.player.Hand; @@ -89,6 +95,10 @@ public final class EntityUtils { } private static float getMountedHeightOffset(Entity mount) { + if (mount instanceof BoatEntity boat && boat.getVariant() != BoatEntity.BoatVariant.BAMBOO) { + return -0.1f; + } + float height = mount.getBoundingBoxHeight(); float mountedHeightOffset = height * 0.75f; switch (mount.getDefinition().entityType()) { @@ -101,10 +111,7 @@ public final class EntityUtils { case TRADER_LLAMA, LLAMA -> mountedHeightOffset = height * 0.6f; case MINECART, HOPPER_MINECART, TNT_MINECART, CHEST_MINECART, FURNACE_MINECART, SPAWNER_MINECART, COMMAND_BLOCK_MINECART -> mountedHeightOffset = 0; - case BOAT, CHEST_BOAT -> { - boolean isBamboo = ((BoatEntity) mount).getVariant() == 8; - mountedHeightOffset = isBamboo ? 0.25f : -0.1f; - } + case BAMBOO_RAFT, BAMBOO_CHEST_RAFT -> mountedHeightOffset = 0.25f; case HOGLIN, ZOGLIN -> { boolean isBaby = mount.getFlag(EntityFlag.BABY); mountedHeightOffset = height - (isBaby ? 0.2f : 0.15f); @@ -170,15 +177,6 @@ public final class EntityUtils { float yOffset = mountedHeightOffset + heightOffset; float zOffset = 0; switch (mount.getDefinition().entityType()) { - case BOAT -> { - // Without the X offset, more than one entity on a boat is stacked on top of each other - if (moreThanOneEntity) { - xOffset = rider ? 0.2f : -0.6f; - if (passenger instanceof AnimalEntity) { - xOffset += 0.2f; - } - } - } case CAMEL -> { zOffset = 0.5f; if (moreThanOneEntity) { @@ -197,7 +195,6 @@ public final class EntityUtils { } } } - case CHEST_BOAT -> xOffset = 0.15F; case CHICKEN -> zOffset = -0.1f; case TRADER_LLAMA, LLAMA -> zOffset = -0.3f; case TEXT_DISPLAY -> { @@ -213,6 +210,17 @@ public final class EntityUtils { } } } + if (mount instanceof ChestBoatEntity) { + xOffset = 0.15F; + } else if (mount instanceof BoatEntity) { + // Without the X offset, more than one entity on a boat is stacked on top of each other + if (moreThanOneEntity) { + xOffset = rider ? 0.2f : -0.6f; + if (passenger instanceof AnimalEntity) { + xOffset += 0.2f; + } + } + } /* * Bedrock Differences * Zoglin & Hoglin seem to be taller in Bedrock edition @@ -227,13 +235,19 @@ public final class EntityUtils { } switch (mount.getDefinition().entityType()) { case MINECART, HOPPER_MINECART, TNT_MINECART, CHEST_MINECART, FURNACE_MINECART, SPAWNER_MINECART, - COMMAND_BLOCK_MINECART, BOAT, CHEST_BOAT -> yOffset -= mount.getDefinition().height() * 0.5f; + COMMAND_BLOCK_MINECART -> yOffset -= mount.getDefinition().height() * 0.5f; } switch (passenger.getDefinition().entityType()) { case MINECART, HOPPER_MINECART, TNT_MINECART, CHEST_MINECART, FURNACE_MINECART, SPAWNER_MINECART, - COMMAND_BLOCK_MINECART, BOAT, CHEST_BOAT -> yOffset += passenger.getDefinition().height() * 0.5f; + COMMAND_BLOCK_MINECART -> yOffset += passenger.getDefinition().height() * 0.5f; case FALLING_BLOCK -> yOffset += 0.5f; } + if (mount instanceof BoatEntity) { + yOffset -= mount.getDefinition().height() * 0.5f; + } + if (passenger instanceof BoatEntity) { + yOffset += passenger.getDefinition().height() * 0.5f; + } if (mount instanceof ArmorStandEntity armorStand) { yOffset -= armorStand.getYOffset(); } @@ -290,6 +304,32 @@ public final class EntityUtils { }; } + private static String translatedEntityName(@NonNull String namespace, @NonNull String name, @NonNull GeyserSession session) { + // MinecraftLocale would otherwise invoke getBootstrap (which doesn't exist) and create some folders, + // so use the default fallback value as used in Minecraft Java + if (EnvironmentUtils.IS_UNIT_TESTING) { + return "entity." + namespace + "." + name; + } + return MinecraftLocale.getLocaleString("entity." + namespace + "." + name, session.locale()); + } + + public static String translatedEntityName(@NonNull Key type, @NonNull GeyserSession session) { + return translatedEntityName(type.namespace(), type.value(), session); + } + + public static String translatedEntityName(@Nullable EntityType type, @NonNull GeyserSession session) { + if (type == EntityType.PLAYER) { + return "Player"; // the player's name is always shown instead + } + // default fallback value as used in Minecraft Java + if (type == null) { + return "entity.unregistered_sadface"; + } + // this works at least with all 1.20.5 entities, except the killer bunny since that's not an entity type. + String typeName = type.name().toLowerCase(Locale.ROOT); + return translatedEntityName("minecraft", typeName, session); + } + private EntityUtils() { } } diff --git a/core/src/main/java/org/geysermc/geyser/util/EnvironmentUtils.java b/core/src/main/java/org/geysermc/geyser/util/EnvironmentUtils.java new file mode 100644 index 000000000..35a1a1e8f --- /dev/null +++ b/core/src/main/java/org/geysermc/geyser/util/EnvironmentUtils.java @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2024 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; + +public final class EnvironmentUtils { + public static final boolean IS_UNIT_TESTING = isUnitTesting(); + + private EnvironmentUtils() {} + + private static boolean isUnitTesting() { + for (StackTraceElement element : Thread.currentThread().getStackTrace()) { + if (element.getClassName().startsWith("org.junit.")) { + return true; + } + } + return false; + } +} 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 408c892f6..045b43984 100644 --- a/core/src/main/java/org/geysermc/geyser/util/InventoryUtils.java +++ b/core/src/main/java/org/geysermc/geyser/util/InventoryUtils.java @@ -32,13 +32,15 @@ import org.cloudburstmc.nbt.NbtMapBuilder; import org.cloudburstmc.nbt.NbtType; import org.cloudburstmc.protocol.bedrock.data.definitions.ItemDefinition; import org.cloudburstmc.protocol.bedrock.data.inventory.ContainerId; -import org.cloudburstmc.protocol.bedrock.data.inventory.ContainerSlotType; -import org.cloudburstmc.protocol.bedrock.data.inventory.FullContainerName; import org.cloudburstmc.protocol.bedrock.data.inventory.ItemData; import org.cloudburstmc.protocol.bedrock.packet.InventorySlotPacket; import org.cloudburstmc.protocol.bedrock.packet.PlayerHotbarPacket; import org.geysermc.geyser.GeyserImpl; -import org.geysermc.geyser.inventory.*; +import org.geysermc.geyser.inventory.Container; +import org.geysermc.geyser.inventory.GeyserItemStack; +import org.geysermc.geyser.inventory.Inventory; +import org.geysermc.geyser.inventory.LecternContainer; +import org.geysermc.geyser.inventory.PlayerInventory; import org.geysermc.geyser.inventory.click.Click; import org.geysermc.geyser.inventory.recipe.GeyserRecipe; import org.geysermc.geyser.inventory.recipe.GeyserShapedRecipe; @@ -47,9 +49,10 @@ import org.geysermc.geyser.item.Items; import org.geysermc.geyser.item.type.Item; import org.geysermc.geyser.level.BedrockDimension; import org.geysermc.geyser.registry.Registries; -import org.geysermc.geyser.registry.type.ItemMapping; import org.geysermc.geyser.registry.type.ItemMappings; import org.geysermc.geyser.session.GeyserSession; +import org.geysermc.geyser.session.cache.registry.JavaRegistries; +import org.geysermc.geyser.session.cache.tags.Tag; import org.geysermc.geyser.text.ChatColor; import org.geysermc.geyser.text.GeyserLocale; import org.geysermc.geyser.translator.inventory.InventoryTranslator; @@ -58,15 +61,22 @@ import org.geysermc.geyser.translator.inventory.chest.DoubleChestInventoryTransl import org.geysermc.mcprotocollib.protocol.data.game.entity.player.GameMode; import org.geysermc.mcprotocollib.protocol.data.game.item.ItemStack; import org.geysermc.mcprotocollib.protocol.data.game.item.component.DataComponents; -import org.geysermc.mcprotocollib.protocol.data.game.recipe.Ingredient; +import org.geysermc.mcprotocollib.protocol.data.game.recipe.display.slot.CompositeSlotDisplay; +import org.geysermc.mcprotocollib.protocol.data.game.recipe.display.slot.EmptySlotDisplay; +import org.geysermc.mcprotocollib.protocol.data.game.recipe.display.slot.ItemSlotDisplay; +import org.geysermc.mcprotocollib.protocol.data.game.recipe.display.slot.ItemStackSlotDisplay; +import org.geysermc.mcprotocollib.protocol.data.game.recipe.display.slot.SlotDisplay; +import org.geysermc.mcprotocollib.protocol.data.game.recipe.display.slot.TagSlotDisplay; +import org.geysermc.mcprotocollib.protocol.data.game.recipe.display.slot.WithRemainderSlotDisplay; import org.geysermc.mcprotocollib.protocol.packet.ingame.serverbound.inventory.ServerboundContainerClosePacket; import org.geysermc.mcprotocollib.protocol.packet.ingame.serverbound.inventory.ServerboundPickItemPacket; import org.geysermc.mcprotocollib.protocol.packet.ingame.serverbound.inventory.ServerboundSetCreativeModeSlotPacket; import org.jetbrains.annotations.Contract; -import java.util.Arrays; +import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; +import java.util.List; import java.util.Objects; import java.util.concurrent.TimeUnit; import java.util.function.IntFunction; @@ -182,7 +192,6 @@ public class InventoryUtils { cursorPacket.setContainerId(ContainerId.UI); cursorPacket.setSlot(0); cursorPacket.setItem(session.getPlayerInventory().getCursor().getItemData(session)); - cursorPacket.setContainerNameData(new FullContainerName(ContainerSlotType.ANVIL_INPUT, null)); session.sendUpstreamPacket(cursorPacket); } @@ -304,14 +313,7 @@ public class InventoryUtils { // If we still have not found the item, and we're in creative, ask for the item from the server. if (session.getGameMode() == GameMode.CREATIVE) { - int slot = findEmptyHotbarSlot(inventory); - - ServerboundSetCreativeModeSlotPacket actionPacket = new ServerboundSetCreativeModeSlotPacket((short) slot, - itemStack); - if ((slot - 36) != inventory.getHeldItemSlot()) { - setHotbarItem(session, slot); - } - session.sendDownstreamGamePacket(actionPacket); + setPickedItem(session, inventory, GeyserItemStack.from(itemStack)); } } @@ -371,24 +373,15 @@ public class InventoryUtils { return; } - // If we still have not found the item, and we're in creative, ask for the item from the server. + // If we still have not found the item, and we're in creative, set the item ourselves. if (session.getGameMode() == GameMode.CREATIVE) { - int slot = findEmptyHotbarSlot(inventory); - - ItemMapping mapping = session.getItemMappings().getMapping(item); - ServerboundSetCreativeModeSlotPacket actionPacket = new ServerboundSetCreativeModeSlotPacket((short)slot, - new ItemStack(mapping.getJavaItem().javaId())); - if ((slot - 36) != inventory.getHeldItemSlot()) { - setHotbarItem(session, slot); - } - session.sendDownstreamGamePacket(actionPacket); + GeyserItemStack itemStack = item.newItemStack(1, null); + setPickedItem(session, inventory, itemStack); } } - /** - * @return the first empty slot found in this inventory, or else the player's currently held slot. - */ - private static int findEmptyHotbarSlot(PlayerInventory inventory) { + private static void setPickedItem(GeyserSession session, PlayerInventory inventory, GeyserItemStack itemStack) { + // Try to find an empty hotbar slot. int slot = inventory.getHeldItemSlot() + 36; if (!inventory.getItemInHand().isEmpty()) { // Otherwise we should just use the current slot for (int i = 36; i < 45; i++) { @@ -398,7 +391,32 @@ public class InventoryUtils { } } } - return slot; + GeyserItemStack existingItem = inventory.getItem(slot); + if (!existingItem.isEmpty()) { + // Try to move the item to another slot. + for (int i = 9; i < 36; i++) { + if (inventory.getItem(i).isEmpty()) { + inventory.setItem(i, existingItem, session); + InventoryTranslator.PLAYER_INVENTORY_TRANSLATOR.updateSlot(session, inventory, i); + + ServerboundSetCreativeModeSlotPacket actionPacket = new ServerboundSetCreativeModeSlotPacket((short) i, + existingItem.getItemStack()); + session.sendDownstreamGamePacket(actionPacket); + break; + } + } + } + + // As of 1.21.3 - the client does this on its own end and the server doesn't send a slot response back. + inventory.setItem(slot, itemStack, session); + InventoryTranslator.PLAYER_INVENTORY_TRANSLATOR.updateSlot(session, inventory, slot); + + ServerboundSetCreativeModeSlotPacket actionPacket = new ServerboundSetCreativeModeSlotPacket((short) slot, + itemStack.getItemStack()); + if ((slot - 36) != inventory.getHeldItemSlot()) { + setHotbarItem(session, slot); + } + session.sendDownstreamGamePacket(actionPacket); } /** @@ -432,6 +450,38 @@ public class InventoryUtils { }; } + /** + * Returns if the provided item stack would be accepted by the slot display. + */ + public static boolean acceptsAsInput(GeyserSession session, SlotDisplay slotDisplay, GeyserItemStack itemStack) { + if (slotDisplay instanceof EmptySlotDisplay) { + return itemStack.isEmpty(); + } + if (slotDisplay instanceof CompositeSlotDisplay compositeSlotDisplay) { + if (compositeSlotDisplay.contents().size() == 1) { + return acceptsAsInput(session, compositeSlotDisplay.contents().get(0), itemStack); + } + return compositeSlotDisplay.contents().stream().anyMatch(aSlotDisplay -> acceptsAsInput(session, aSlotDisplay, itemStack)); + } + if (slotDisplay instanceof WithRemainderSlotDisplay remainderSlotDisplay) { + return acceptsAsInput(session, remainderSlotDisplay.input(), itemStack); + } + if (slotDisplay instanceof ItemSlotDisplay itemSlotDisplay) { + return itemStack.getJavaId() == itemSlotDisplay.item(); + } + if (slotDisplay instanceof ItemStackSlotDisplay itemStackSlotDisplay) { + ItemStack other = itemStackSlotDisplay.itemStack(); + // Amount check might be flimsy? + return itemStack.getJavaId() == other.getId() && itemStack.getAmount() >= other.getAmount() + && Objects.equals(itemStack.getComponents(), other.getDataComponents()); + } + if (slotDisplay instanceof TagSlotDisplay tagSlotDisplay) { + return session.getTagCache().is(new Tag<>(JavaRegistries.ITEM, tagSlotDisplay.tag()), itemStack.asItem()); + } + session.getGeyser().getLogger().warning("Unknown slot display type: " + slotDisplay); + return false; + } + /** * Test all known recipes to find a valid match * @@ -453,59 +503,55 @@ public class InventoryUtils { for (GeyserRecipe recipe : session.getCraftingRecipes().values()) { if (recipe.isShaped()) { GeyserShapedRecipe shapedRecipe = (GeyserShapedRecipe) recipe; - if (output != null && !shapedRecipe.result().equals(output)) { + if (output != null && !acceptsAsInput(session, shapedRecipe.result(), GeyserItemStack.from(output))) { continue; } - Ingredient[] ingredients = shapedRecipe.ingredients(); - if (shapedRecipe.width() != width || shapedRecipe.height() != height || width * height != ingredients.length) { + List ingredients = shapedRecipe.ingredients(); + if (shapedRecipe.width() != width || shapedRecipe.height() != height || width * height != ingredients.size()) { continue; } - if (!testShapedRecipe(ingredients, inventoryGetter, gridDimensions, firstRow, height, firstCol, width)) { - Ingredient[] mirroredIngredients = new Ingredient[ingredients.length]; + if (!testShapedRecipe(session, ingredients, inventoryGetter, gridDimensions, firstRow, height, firstCol, width)) { + List mirroredIngredients = new ArrayList<>(ingredients.size()); for (int row = 0; row < height; row++) { for (int col = 0; col < width; col++) { - mirroredIngredients[col + (row * width)] = ingredients[(width - 1 - col) + (row * width)]; + int index = col + (row * width); + while (mirroredIngredients.size() <= index) { + mirroredIngredients.add(null); + } + mirroredIngredients.set(index, ingredients.get((width - 1 - col) + (row * width))); } } - if (Arrays.equals(ingredients, mirroredIngredients) || - !testShapedRecipe(mirroredIngredients, inventoryGetter, gridDimensions, firstRow, height, firstCol, width)) { + if (ingredients.equals(mirroredIngredients) || + !testShapedRecipe(session, mirroredIngredients, inventoryGetter, gridDimensions, firstRow, height, firstCol, width)) { continue; } } } else { GeyserShapelessRecipe data = (GeyserShapelessRecipe) recipe; - if (output != null && !data.result().equals(output)) { + if (output != null && !acceptsAsInput(session, data.result(), GeyserItemStack.from(output))) { continue; } - if (nonAirCount != data.ingredients().length) { + if (nonAirCount != data.ingredients().size()) { // There is an amount of items on the crafting table that is not the same as the ingredient count so this is invalid continue; } - for (int i = 0; i < data.ingredients().length; i++) { - Ingredient ingredient = data.ingredients()[i]; - for (ItemStack itemStack : ingredient.getOptions()) { - boolean inventoryHasItem = false; - // Iterate only over the crafting table to find this item - crafting: - for (int row = firstRow; row < height + firstRow; row++) { - for (int col = firstCol; col < width + firstCol; col++) { - GeyserItemStack geyserItemStack = inventoryGetter.apply(col + (row * gridDimensions) + 1); - if (geyserItemStack.isEmpty()) { - inventoryHasItem = itemStack == null || itemStack.getId() == 0; - if (inventoryHasItem) { - break crafting; - } - } else if (itemStack.equals(geyserItemStack.getItemStack(1))) { - inventoryHasItem = true; - break crafting; - } + for (int i = 0; i < data.ingredients().size(); i++) { + SlotDisplay slotDisplay = data.ingredients().get(i); + boolean inventoryHasItem = false; + // Iterate only over the crafting table to find this item + for (int row = firstRow; row < height + firstRow; row++) { + for (int col = firstCol; col < width + firstCol; col++) { + GeyserItemStack geyserItemStack = inventoryGetter.apply(col + (row * gridDimensions) + 1); + if (acceptsAsInput(session, slotDisplay, geyserItemStack)) { + inventoryHasItem = true; + break; } } - if (!inventoryHasItem) { - continue recipes; - } + } + if (!inventoryHasItem) { + continue recipes; } } } @@ -515,28 +561,15 @@ public class InventoryUtils { } @SuppressWarnings("BooleanMethodIsAlwaysInverted") - private static boolean testShapedRecipe(final Ingredient[] ingredients, final IntFunction inventoryGetter, + private static boolean testShapedRecipe(final GeyserSession session, final List ingredients, final IntFunction inventoryGetter, final int gridDimensions, final int firstRow, final int height, final int firstCol, final int width) { int ingredientIndex = 0; for (int row = firstRow; row < height + firstRow; row++) { for (int col = firstCol; col < width + firstCol; col++) { GeyserItemStack geyserItemStack = inventoryGetter.apply(col + (row * gridDimensions) + 1); - Ingredient ingredient = ingredients[ingredientIndex++]; - if (ingredient.getOptions().length == 0) { - if (!geyserItemStack.isEmpty()) { - return false; - } - } else { - boolean inventoryHasItem = false; - for (ItemStack item : ingredient.getOptions()) { - if (Objects.equals(geyserItemStack.getItemStack(1), item)) { - inventoryHasItem = true; - break; - } - } - if (!inventoryHasItem) { - return false; - } + SlotDisplay slotDisplay = ingredients.get(ingredientIndex++); + if (!acceptsAsInput(session, slotDisplay, geyserItemStack)) { + return false; } } } 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 693ce136a..d1c1c77e3 100644 --- a/core/src/main/java/org/geysermc/geyser/util/SoundUtils.java +++ b/core/src/main/java/org/geysermc/geyser/util/SoundUtils.java @@ -27,6 +27,7 @@ package org.geysermc.geyser.util; import org.checkerframework.checker.nullness.qual.Nullable; import org.cloudburstmc.math.vector.Vector3f; +import org.cloudburstmc.nbt.NbtMap; import org.cloudburstmc.protocol.bedrock.data.LevelEvent; import org.cloudburstmc.protocol.bedrock.data.SoundEvent; import org.cloudburstmc.protocol.bedrock.packet.LevelEventPacket; @@ -162,6 +163,20 @@ public final class SoundUtils { session.sendUpstreamPacket(soundPacket); } + public static String readSoundEvent(NbtMap data, String context) { + Object soundEventObject = data.get("sound_event"); + String soundEvent; + if (soundEventObject instanceof NbtMap map) { + soundEvent = map.getString("sound_id"); + } else if (soundEventObject instanceof String string) { + soundEvent = string; + } else { + soundEvent = ""; + GeyserImpl.getInstance().getLogger().debug("Sound event for " + context + " was of an unexpected type! Expected string or NBT map, got " + soundEventObject); + } + return soundEvent; + } + private SoundUtils() { } } diff --git a/core/src/main/resources/bedrock/biome_definitions.dat b/core/src/main/resources/bedrock/biome_definitions.dat index 3ae94a5c8..2bd58d0a3 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_21_40.nbt b/core/src/main/resources/bedrock/block_palette.1_21_40.nbt new file mode 100644 index 000000000..f7d3ace9a Binary files /dev/null and b/core/src/main/resources/bedrock/block_palette.1_21_40.nbt differ diff --git a/core/src/main/resources/bedrock/creative_items.1_21_40.json b/core/src/main/resources/bedrock/creative_items.1_21_40.json new file mode 100644 index 000000000..e4a15d9c6 --- /dev/null +++ b/core/src/main/resources/bedrock/creative_items.1_21_40.json @@ -0,0 +1,6262 @@ +{ + "items": [ + { + "id": "minecraft:oak_planks", + "block_state_b64": "CgAAAwgAYmxvY2tfaWQFAAAACAQAbmFtZRQAbWluZWNyYWZ0Om9ha19wbGFua3MECQBuYW1lX2hhc2ilMDLR92rQ4wMKAG5ldHdvcmtfaWS2GotyCgYAc3RhdGVzAAMHAHZlcnNpb24BKBUBAA==" + }, + { + "id": "minecraft:spruce_planks", + "block_state_b64": "CgAAAwgAYmxvY2tfaWTiAwAACAQAbmFtZRcAbWluZWNyYWZ0OnNwcnVjZV9wbGFua3MECQBuYW1lX2hhc2iumBkmFGFE8gMKAG5ldHdvcmtfaWSo8TFgCgYAc3RhdGVzAAMHAHZlcnNpb24BKBUBAA==" + }, + { + "id": "minecraft:birch_planks", + "block_state_b64": "CgAAAwgAYmxvY2tfaWTjAwAACAQAbmFtZRYAbWluZWNyYWZ0OmJpcmNoX3BsYW5rcwQJAG5hbWVfaGFzaLrrAKJqV2WFAwoAbmV0d29ya19pZL+e3ZAKBgBzdGF0ZXMAAwcAdmVyc2lvbgEoFQEA" + }, + { + "id": "minecraft:jungle_planks", + "block_state_b64": "CgAAAwgAYmxvY2tfaWTkAwAACAQAbmFtZRcAbWluZWNyYWZ0Omp1bmdsZV9wbGFua3MECQBuYW1lX2hhc2iBM3k4T3FAugMKAG5ldHdvcmtfaWSXUmBCCgYAc3RhdGVzAAMHAHZlcnNpb24BKBUBAA==" + }, + { + "id": "minecraft:acacia_planks", + "block_state_b64": "CgAAAwgAYmxvY2tfaWTlAwAACAQAbmFtZRcAbWluZWNyYWZ0OmFjYWNpYV9wbGFua3MECQBuYW1lX2hhc2g60edJxO5/aAMKAG5ldHdvcmtfaWTUXozECgYAc3RhdGVzAAMHAHZlcnNpb24BKBUBAA==" + }, + { + "id": "minecraft:dark_oak_planks", + "block_state_b64": "CgAAAwgAYmxvY2tfaWTmAwAACAQAbmFtZRkAbWluZWNyYWZ0OmRhcmtfb2FrX3BsYW5rcwQJAG5hbWVfaGFzaAr64wkQ9cA7AwoAbmV0d29ya19pZFbMeR0KBgBzdGF0ZXMAAwcAdmVyc2lvbgEoFQEA" + }, + { + "id": "minecraft:mangrove_planks", + "block_state_b64": "CgAAAwgAYmxvY2tfaWTlAgAACAQAbmFtZRkAbWluZWNyYWZ0Om1hbmdyb3ZlX3BsYW5rcwQJAG5hbWVfaGFzaPvLtcEA0F8xAwoAbmV0d29ya19pZEvnlCYKBgBzdGF0ZXMAAwcAdmVyc2lvbgEoFQEA" + }, + { + "id": "minecraft:cherry_planks", + "block_state_b64": "CgAAAwgAYmxvY2tfaWQYAwAACAQAbmFtZRcAbWluZWNyYWZ0OmNoZXJyeV9wbGFua3MECQBuYW1lX2hhc2hNIvVh/lVW7gMKAG5ldHdvcmtfaWQTXpRoCgYAc3RhdGVzAAMHAHZlcnNpb24BKBUBAA==" + }, + { + "id": "minecraft:bamboo_planks", + "block_state_b64": "CgAAAwgAYmxvY2tfaWT9AgAACAQAbmFtZRcAbWluZWNyYWZ0OmJhbWJvb19wbGFua3MECQBuYW1lX2hhc2gYnjNz7SCCjgMKAG5ldHdvcmtfaWTi8ySSCgYAc3RhdGVzAAMHAHZlcnNpb24BKBUBAA==" + }, + { + "id": "minecraft:bamboo_mosaic", + "block_state_b64": "CgAAAwgAYmxvY2tfaWT8AgAACAQAbmFtZRcAbWluZWNyYWZ0OmJhbWJvb19tb3NhaWMECQBuYW1lX2hhc2izSEgiMKOp/AMKAG5ldHdvcmtfaWQZ/p8xCgYAc3RhdGVzAAMHAHZlcnNpb24BKBUBAA==" + }, + { + "id": "minecraft:crimson_planks", + "block_state_b64": "CgAAAwgAYmxvY2tfaWTxAQAACAQAbmFtZRgAbWluZWNyYWZ0OmNyaW1zb25fcGxhbmtzBAkAbmFtZV9oYXNoJc5IKqNXJnwDCgBuZXR3b3JrX2lkwtJDdQoGAHN0YXRlcwADBwB2ZXJzaW9uASgVAQA=" + }, + { + "id": "minecraft:warped_planks", + "block_state_b64": "CgAAAwgAYmxvY2tfaWTyAQAACAQAbmFtZRcAbWluZWNyYWZ0OndhcnBlZF9wbGFua3MECQBuYW1lX2hhc2g3yGXEWhe6LgMKAG5ldHdvcmtfaWStTABvCgYAc3RhdGVzAAMHAHZlcnNpb24BKBUBAA==" + }, + { + "id": "minecraft:cobblestone_wall", + "block_state_b64": "CgAAAwgAYmxvY2tfaWSLAAAACAQAbmFtZRoAbWluZWNyYWZ0OmNvYmJsZXN0b25lX3dhbGwECQBuYW1lX2hhc2hZu/xE7lYtNgMKAG5ldHdvcmtfaWSLY2XwCgYAc3RhdGVzCBkAd2FsbF9jb25uZWN0aW9uX3R5cGVfZWFzdAQAbm9uZQgaAHdhbGxfY29ubmVjdGlvbl90eXBlX25vcnRoBABub25lCBoAd2FsbF9jb25uZWN0aW9uX3R5cGVfc291dGgEAG5vbmUIGQB3YWxsX2Nvbm5lY3Rpb25fdHlwZV93ZXN0BABub25lAQ0Ad2FsbF9wb3N0X2JpdAAAAwcAdmVyc2lvbgEoFQEA" + }, + { + "id": "minecraft:mossy_cobblestone_wall", + "block_state_b64": "CgAAAwgAYmxvY2tfaWTKBAAACAQAbmFtZSAAbWluZWNyYWZ0Om1vc3N5X2NvYmJsZXN0b25lX3dhbGwECQBuYW1lX2hhc2gHUQMwwFQeNQMKAG5ldHdvcmtfaWRzPhG8CgYAc3RhdGVzCBkAd2FsbF9jb25uZWN0aW9uX3R5cGVfZWFzdAQAbm9uZQgaAHdhbGxfY29ubmVjdGlvbl90eXBlX25vcnRoBABub25lCBoAd2FsbF9jb25uZWN0aW9uX3R5cGVfc291dGgEAG5vbmUIGQB3YWxsX2Nvbm5lY3Rpb25fdHlwZV93ZXN0BABub25lAQ0Ad2FsbF9wb3N0X2JpdAAAAwcAdmVyc2lvbgEoFQEA" + }, + { + "id": "minecraft:granite_wall", + "block_state_b64": "CgAAAwgAYmxvY2tfaWTLBAAACAQAbmFtZRYAbWluZWNyYWZ0OmdyYW5pdGVfd2FsbAQJAG5hbWVfaGFzaE1GmM5AU0qUAwoAbmV0d29ya19pZE/UoPUKBgBzdGF0ZXMIGQB3YWxsX2Nvbm5lY3Rpb25fdHlwZV9lYXN0BABub25lCBoAd2FsbF9jb25uZWN0aW9uX3R5cGVfbm9ydGgEAG5vbmUIGgB3YWxsX2Nvbm5lY3Rpb25fdHlwZV9zb3V0aAQAbm9uZQgZAHdhbGxfY29ubmVjdGlvbl90eXBlX3dlc3QEAG5vbmUBDQB3YWxsX3Bvc3RfYml0AAADBwB2ZXJzaW9uASgVAQA=" + }, + { + "id": "minecraft:diorite_wall", + "block_state_b64": "CgAAAwgAYmxvY2tfaWTMBAAACAQAbmFtZRYAbWluZWNyYWZ0OmRpb3JpdGVfd2FsbAQJAG5hbWVfaGFzaF27l0QvdM8xAwoAbmV0d29ya19pZJe7jOwKBgBzdGF0ZXMIGQB3YWxsX2Nvbm5lY3Rpb25fdHlwZV9lYXN0BABub25lCBoAd2FsbF9jb25uZWN0aW9uX3R5cGVfbm9ydGgEAG5vbmUIGgB3YWxsX2Nvbm5lY3Rpb25fdHlwZV9zb3V0aAQAbm9uZQgZAHdhbGxfY29ubmVjdGlvbl90eXBlX3dlc3QEAG5vbmUBDQB3YWxsX3Bvc3RfYml0AAADBwB2ZXJzaW9uASgVAQA=" + }, + { + "id": "minecraft:andesite_wall", + "block_state_b64": "CgAAAwgAYmxvY2tfaWTNBAAACAQAbmFtZRcAbWluZWNyYWZ0OmFuZGVzaXRlX3dhbGwECQBuYW1lX2hhc2gAL1Vay0kZjQMKAG5ldHdvcmtfaWRJGxdvCgYAc3RhdGVzCBkAd2FsbF9jb25uZWN0aW9uX3R5cGVfZWFzdAQAbm9uZQgaAHdhbGxfY29ubmVjdGlvbl90eXBlX25vcnRoBABub25lCBoAd2FsbF9jb25uZWN0aW9uX3R5cGVfc291dGgEAG5vbmUIGQB3YWxsX2Nvbm5lY3Rpb25fdHlwZV93ZXN0BABub25lAQ0Ad2FsbF9wb3N0X2JpdAAAAwcAdmVyc2lvbgEoFQEA" + }, + { + "id": "minecraft:sandstone_wall", + "block_state_b64": "CgAAAwgAYmxvY2tfaWTOBAAACAQAbmFtZRgAbWluZWNyYWZ0OnNhbmRzdG9uZV93YWxsBAkAbmFtZV9oYXNoYL2gu8a6HfgDCgBuZXR3b3JrX2lkHrhRjgoGAHN0YXRlcwgZAHdhbGxfY29ubmVjdGlvbl90eXBlX2Vhc3QEAG5vbmUIGgB3YWxsX2Nvbm5lY3Rpb25fdHlwZV9ub3J0aAQAbm9uZQgaAHdhbGxfY29ubmVjdGlvbl90eXBlX3NvdXRoBABub25lCBkAd2FsbF9jb25uZWN0aW9uX3R5cGVfd2VzdAQAbm9uZQENAHdhbGxfcG9zdF9iaXQAAAMHAHZlcnNpb24BKBUBAA==" + }, + { + "id": "minecraft:red_sandstone_wall", + "block_state_b64": "CgAAAwgAYmxvY2tfaWTVBAAACAQAbmFtZRwAbWluZWNyYWZ0OnJlZF9zYW5kc3RvbmVfd2FsbAQJAG5hbWVfaGFzaLAUUdOlo24MAwoAbmV0d29ya19pZI66BqAKBgBzdGF0ZXMIGQB3YWxsX2Nvbm5lY3Rpb25fdHlwZV9lYXN0BABub25lCBoAd2FsbF9jb25uZWN0aW9uX3R5cGVfbm9ydGgEAG5vbmUIGgB3YWxsX2Nvbm5lY3Rpb25fdHlwZV9zb3V0aAQAbm9uZQgZAHdhbGxfY29ubmVjdGlvbl90eXBlX3dlc3QEAG5vbmUBDQB3YWxsX3Bvc3RfYml0AAADBwB2ZXJzaW9uASgVAQA=" + }, + { + "id": "minecraft:stone_brick_wall", + "block_state_b64": "CgAAAwgAYmxvY2tfaWTQBAAACAQAbmFtZRoAbWluZWNyYWZ0OnN0b25lX2JyaWNrX3dhbGwECQBuYW1lX2hhc2hQegufuP6vjAMKAG5ldHdvcmtfaWS4AsOKCgYAc3RhdGVzCBkAd2FsbF9jb25uZWN0aW9uX3R5cGVfZWFzdAQAbm9uZQgaAHdhbGxfY29ubmVjdGlvbl90eXBlX25vcnRoBABub25lCBoAd2FsbF9jb25uZWN0aW9uX3R5cGVfc291dGgEAG5vbmUIGQB3YWxsX2Nvbm5lY3Rpb25fdHlwZV93ZXN0BABub25lAQ0Ad2FsbF9wb3N0X2JpdAAAAwcAdmVyc2lvbgEoFQEA" + }, + { + "id": "minecraft:mossy_stone_brick_wall", + "block_state_b64": "CgAAAwgAYmxvY2tfaWTRBAAACAQAbmFtZSAAbWluZWNyYWZ0Om1vc3N5X3N0b25lX2JyaWNrX3dhbGwECQBuYW1lX2hhc2i680zzUekp+wMKAG5ldHdvcmtfaWTQTaHPCgYAc3RhdGVzCBkAd2FsbF9jb25uZWN0aW9uX3R5cGVfZWFzdAQAbm9uZQgaAHdhbGxfY29ubmVjdGlvbl90eXBlX25vcnRoBABub25lCBoAd2FsbF9jb25uZWN0aW9uX3R5cGVfc291dGgEAG5vbmUIGQB3YWxsX2Nvbm5lY3Rpb25fdHlwZV93ZXN0BABub25lAQ0Ad2FsbF9wb3N0X2JpdAAAAwcAdmVyc2lvbgEoFQEA" + }, + { + "id": "minecraft:brick_wall", + "block_state_b64": "CgAAAwgAYmxvY2tfaWTPBAAACAQAbmFtZRQAbWluZWNyYWZ0OmJyaWNrX3dhbGwECQBuYW1lX2hhc2gGJFLNjfgSCAMKAG5ldHdvcmtfaWSc5iUZCgYAc3RhdGVzCBkAd2FsbF9jb25uZWN0aW9uX3R5cGVfZWFzdAQAbm9uZQgaAHdhbGxfY29ubmVjdGlvbl90eXBlX25vcnRoBABub25lCBoAd2FsbF9jb25uZWN0aW9uX3R5cGVfc291dGgEAG5vbmUIGQB3YWxsX2Nvbm5lY3Rpb25fdHlwZV93ZXN0BABub25lAQ0Ad2FsbF9wb3N0X2JpdAAAAwcAdmVyc2lvbgEoFQEA" + }, + { + "id": "minecraft:nether_brick_wall", + "block_state_b64": "CgAAAwgAYmxvY2tfaWTSBAAACAQAbmFtZRsAbWluZWNyYWZ0Om5ldGhlcl9icmlja193YWxsBAkAbmFtZV9oYXNoAxb5f2yQ5MYDCgBuZXR3b3JrX2lkAECPDAoGAHN0YXRlcwgZAHdhbGxfY29ubmVjdGlvbl90eXBlX2Vhc3QEAG5vbmUIGgB3YWxsX2Nvbm5lY3Rpb25fdHlwZV9ub3J0aAQAbm9uZQgaAHdhbGxfY29ubmVjdGlvbl90eXBlX3NvdXRoBABub25lCBkAd2FsbF9jb25uZWN0aW9uX3R5cGVfd2VzdAQAbm9uZQENAHdhbGxfcG9zdF9iaXQAAAMHAHZlcnNpb24BKBUBAA==" + }, + { + "id": "minecraft:red_nether_brick_wall", + "block_state_b64": "CgAAAwgAYmxvY2tfaWTWBAAACAQAbmFtZR8AbWluZWNyYWZ0OnJlZF9uZXRoZXJfYnJpY2tfd2FsbAQJAG5hbWVfaGFzaBOtwkokUt3cAwoAbmV0d29ya19pZJykmZUKBgBzdGF0ZXMIGQB3YWxsX2Nvbm5lY3Rpb25fdHlwZV9lYXN0BABub25lCBoAd2FsbF9jb25uZWN0aW9uX3R5cGVfbm9ydGgEAG5vbmUIGgB3YWxsX2Nvbm5lY3Rpb25fdHlwZV9zb3V0aAQAbm9uZQgZAHdhbGxfY29ubmVjdGlvbl90eXBlX3dlc3QEAG5vbmUBDQB3YWxsX3Bvc3RfYml0AAADBwB2ZXJzaW9uASgVAQA=" + }, + { + "id": "minecraft:end_stone_brick_wall", + "block_state_b64": "CgAAAwgAYmxvY2tfaWTTBAAACAQAbmFtZR4AbWluZWNyYWZ0OmVuZF9zdG9uZV9icmlja193YWxsBAkAbmFtZV9oYXNoOsr1L9kJIAMDCgBuZXR3b3JrX2lkjuYlYgoGAHN0YXRlcwgZAHdhbGxfY29ubmVjdGlvbl90eXBlX2Vhc3QEAG5vbmUIGgB3YWxsX2Nvbm5lY3Rpb25fdHlwZV9ub3J0aAQAbm9uZQgaAHdhbGxfY29ubmVjdGlvbl90eXBlX3NvdXRoBABub25lCBkAd2FsbF9jb25uZWN0aW9uX3R5cGVfd2VzdAQAbm9uZQENAHdhbGxfcG9zdF9iaXQAAAMHAHZlcnNpb24BKBUBAA==" + }, + { + "id": "minecraft:prismarine_wall", + "block_state_b64": "CgAAAwgAYmxvY2tfaWTUBAAACAQAbmFtZRkAbWluZWNyYWZ0OnByaXNtYXJpbmVfd2FsbAQJAG5hbWVfaGFzaDO5IGrYZu1/AwoAbmV0d29ya19pZB4nLYYKBgBzdGF0ZXMIGQB3YWxsX2Nvbm5lY3Rpb25fdHlwZV9lYXN0BABub25lCBoAd2FsbF9jb25uZWN0aW9uX3R5cGVfbm9ydGgEAG5vbmUIGgB3YWxsX2Nvbm5lY3Rpb25fdHlwZV9zb3V0aAQAbm9uZQgZAHdhbGxfY29ubmVjdGlvbl90eXBlX3dlc3QEAG5vbmUBDQB3YWxsX3Bvc3RfYml0AAADBwB2ZXJzaW9uASgVAQA=" + }, + { + "id": "minecraft:blackstone_wall", + "block_state_b64": "CgAAAwgAYmxvY2tfaWQUAgAACAQAbmFtZRkAbWluZWNyYWZ0OmJsYWNrc3RvbmVfd2FsbAQJAG5hbWVfaGFzaMP8XppUSU1RAwoAbmV0d29ya19pZMbeBBsKBgBzdGF0ZXMIGQB3YWxsX2Nvbm5lY3Rpb25fdHlwZV9lYXN0BABub25lCBoAd2FsbF9jb25uZWN0aW9uX3R5cGVfbm9ydGgEAG5vbmUIGgB3YWxsX2Nvbm5lY3Rpb25fdHlwZV9zb3V0aAQAbm9uZQgZAHdhbGxfY29ubmVjdGlvbl90eXBlX3dlc3QEAG5vbmUBDQB3YWxsX3Bvc3RfYml0AAADBwB2ZXJzaW9uASgVAQA=" + }, + { + "id": "minecraft:polished_blackstone_wall", + "block_state_b64": "CgAAAwgAYmxvY2tfaWQoAgAACAQAbmFtZSIAbWluZWNyYWZ0OnBvbGlzaGVkX2JsYWNrc3RvbmVfd2FsbAQJAG5hbWVfaGFzaP6SwV08YwzAAwoAbmV0d29ya19pZAJLsz8KBgBzdGF0ZXMIGQB3YWxsX2Nvbm5lY3Rpb25fdHlwZV9lYXN0BABub25lCBoAd2FsbF9jb25uZWN0aW9uX3R5cGVfbm9ydGgEAG5vbmUIGgB3YWxsX2Nvbm5lY3Rpb25fdHlwZV9zb3V0aAQAbm9uZQgZAHdhbGxfY29ubmVjdGlvbl90eXBlX3dlc3QEAG5vbmUBDQB3YWxsX3Bvc3RfYml0AAADBwB2ZXJzaW9uASgVAQA=" + }, + { + "id": "minecraft:polished_blackstone_brick_wall", + "block_state_b64": "CgAAAwgAYmxvY2tfaWQVAgAACAQAbmFtZSgAbWluZWNyYWZ0OnBvbGlzaGVkX2JsYWNrc3RvbmVfYnJpY2tfd2FsbAQJAG5hbWVfaGFzaBBIDZbHxiEzAwoAbmV0d29ya19pZEbLV8cKBgBzdGF0ZXMIGQB3YWxsX2Nvbm5lY3Rpb25fdHlwZV9lYXN0BABub25lCBoAd2FsbF9jb25uZWN0aW9uX3R5cGVfbm9ydGgEAG5vbmUIGgB3YWxsX2Nvbm5lY3Rpb25fdHlwZV9zb3V0aAQAbm9uZQgZAHdhbGxfY29ubmVjdGlvbl90eXBlX3dlc3QEAG5vbmUBDQB3YWxsX3Bvc3RfYml0AAADBwB2ZXJzaW9uASgVAQA=" + }, + { + "id": "minecraft:cobbled_deepslate_wall", + "block_state_b64": "CgAAAwgAYmxvY2tfaWR9AgAACAQAbmFtZSAAbWluZWNyYWZ0OmNvYmJsZWRfZGVlcHNsYXRlX3dhbGwECQBuYW1lX2hhc2iECY5oKxeT+gMKAG5ldHdvcmtfaWRCnPrFCgYAc3RhdGVzCBkAd2FsbF9jb25uZWN0aW9uX3R5cGVfZWFzdAQAbm9uZQgaAHdhbGxfY29ubmVjdGlvbl90eXBlX25vcnRoBABub25lCBoAd2FsbF9jb25uZWN0aW9uX3R5cGVfc291dGgEAG5vbmUIGQB3YWxsX2Nvbm5lY3Rpb25fdHlwZV93ZXN0BABub25lAQ0Ad2FsbF9wb3N0X2JpdAAAAwcAdmVyc2lvbgEoFQEA" + }, + { + "id": "minecraft:deepslate_tile_wall", + "block_state_b64": "CgAAAwgAYmxvY2tfaWSFAgAACAQAbmFtZR0AbWluZWNyYWZ0OmRlZXBzbGF0ZV90aWxlX3dhbGwECQBuYW1lX2hhc2jz7N+PeuEXgQMKAG5ldHdvcmtfaWTqw4s4CgYAc3RhdGVzCBkAd2FsbF9jb25uZWN0aW9uX3R5cGVfZWFzdAQAbm9uZQgaAHdhbGxfY29ubmVjdGlvbl90eXBlX25vcnRoBABub25lCBoAd2FsbF9jb25uZWN0aW9uX3R5cGVfc291dGgEAG5vbmUIGQB3YWxsX2Nvbm5lY3Rpb25fdHlwZV93ZXN0BABub25lAQ0Ad2FsbF9wb3N0X2JpdAAAAwcAdmVyc2lvbgEoFQEA" + }, + { + "id": "minecraft:polished_deepslate_wall", + "block_state_b64": "CgAAAwgAYmxvY2tfaWSBAgAACAQAbmFtZSEAbWluZWNyYWZ0OnBvbGlzaGVkX2RlZXBzbGF0ZV93YWxsBAkAbmFtZV9oYXNoHxjTdj9pevMDCgBuZXR3b3JrX2lkIvBYYwoGAHN0YXRlcwgZAHdhbGxfY29ubmVjdGlvbl90eXBlX2Vhc3QEAG5vbmUIGgB3YWxsX2Nvbm5lY3Rpb25fdHlwZV9ub3J0aAQAbm9uZQgaAHdhbGxfY29ubmVjdGlvbl90eXBlX3NvdXRoBABub25lCBkAd2FsbF9jb25uZWN0aW9uX3R5cGVfd2VzdAQAbm9uZQENAHdhbGxfcG9zdF9iaXQAAAMHAHZlcnNpb24BKBUBAA==" + }, + { + "id": "minecraft:deepslate_brick_wall", + "block_state_b64": "CgAAAwgAYmxvY2tfaWSJAgAACAQAbmFtZR4AbWluZWNyYWZ0OmRlZXBzbGF0ZV9icmlja193YWxsBAkAbmFtZV9oYXNoEs3EQrjroyEDCgBuZXR3b3JrX2lkwlrCGwoGAHN0YXRlcwgZAHdhbGxfY29ubmVjdGlvbl90eXBlX2Vhc3QEAG5vbmUIGgB3YWxsX2Nvbm5lY3Rpb25fdHlwZV9ub3J0aAQAbm9uZQgaAHdhbGxfY29ubmVjdGlvbl90eXBlX3NvdXRoBABub25lCBkAd2FsbF9jb25uZWN0aW9uX3R5cGVfd2VzdAQAbm9uZQENAHdhbGxfcG9zdF9iaXQAAAMHAHZlcnNpb24BKBUBAA==" + }, + { + "id": "minecraft:tuff_wall", + "block_state_b64": "CgAAAwgAYmxvY2tfaWTqAwAACAQAbmFtZRMAbWluZWNyYWZ0OnR1ZmZfd2FsbAQJAG5hbWVfaGFzaMyeeu1IRf03AwoAbmV0d29ya19pZDkIrosKBgBzdGF0ZXMIGQB3YWxsX2Nvbm5lY3Rpb25fdHlwZV9lYXN0BABub25lCBoAd2FsbF9jb25uZWN0aW9uX3R5cGVfbm9ydGgEAG5vbmUIGgB3YWxsX2Nvbm5lY3Rpb25fdHlwZV9zb3V0aAQAbm9uZQgZAHdhbGxfY29ubmVjdGlvbl90eXBlX3dlc3QEAG5vbmUBDQB3YWxsX3Bvc3RfYml0AAADBwB2ZXJzaW9uASgVAQA=" + }, + { + "id": "minecraft:tuff_brick_wall", + "block_state_b64": "CgAAAwgAYmxvY2tfaWT1AwAACAQAbmFtZRkAbWluZWNyYWZ0OnR1ZmZfYnJpY2tfd2FsbAQJAG5hbWVfaGFzaIL0IyNCOsonAwoAbmV0d29ya19pZJW4T5UKBgBzdGF0ZXMIGQB3YWxsX2Nvbm5lY3Rpb25fdHlwZV9lYXN0BABub25lCBoAd2FsbF9jb25uZWN0aW9uX3R5cGVfbm9ydGgEAG5vbmUIGgB3YWxsX2Nvbm5lY3Rpb25fdHlwZV9zb3V0aAQAbm9uZQgZAHdhbGxfY29ubmVjdGlvbl90eXBlX3dlc3QEAG5vbmUBDQB3YWxsX3Bvc3RfYml0AAADBwB2ZXJzaW9uASgVAQA=" + }, + { + "id": "minecraft:polished_tuff_wall", + "block_state_b64": "CgAAAwgAYmxvY2tfaWTvAwAACAQAbmFtZRwAbWluZWNyYWZ0OnBvbGlzaGVkX3R1ZmZfd2FsbAQJAG5hbWVfaGFzaJVZj6QYWXUrAwoAbmV0d29ya19pZLU7dooKBgBzdGF0ZXMIGQB3YWxsX2Nvbm5lY3Rpb25fdHlwZV9lYXN0BABub25lCBoAd2FsbF9jb25uZWN0aW9uX3R5cGVfbm9ydGgEAG5vbmUIGgB3YWxsX2Nvbm5lY3Rpb25fdHlwZV9zb3V0aAQAbm9uZQgZAHdhbGxfY29ubmVjdGlvbl90eXBlX3dlc3QEAG5vbmUBDQB3YWxsX3Bvc3RfYml0AAADBwB2ZXJzaW9uASgVAQA=" + }, + { + "id": "minecraft:mud_brick_wall", + "block_state_b64": "CgAAAwgAYmxvY2tfaWTgAgAACAQAbmFtZRgAbWluZWNyYWZ0Om11ZF9icmlja193YWxsBAkAbmFtZV9oYXNov9b98ATpUSwDCgBuZXR3b3JrX2lkH/1WZQoGAHN0YXRlcwgZAHdhbGxfY29ubmVjdGlvbl90eXBlX2Vhc3QEAG5vbmUIGgB3YWxsX2Nvbm5lY3Rpb25fdHlwZV9ub3J0aAQAbm9uZQgaAHdhbGxfY29ubmVjdGlvbl90eXBlX3NvdXRoBABub25lCBkAd2FsbF9jb25uZWN0aW9uX3R5cGVfd2VzdAQAbm9uZQENAHdhbGxfcG9zdF9iaXQAAAMHAHZlcnNpb24BKBUBAA==" + }, + { + "id": "minecraft:oak_fence", + "block_state_b64": "CgAAAwgAYmxvY2tfaWRVAAAACAQAbmFtZRMAbWluZWNyYWZ0Om9ha19mZW5jZQQJAG5hbWVfaGFzaGEmid7AaCWRAwoAbmV0d29ya19pZDvPEXcKBgBzdGF0ZXMAAwcAdmVyc2lvbgEoFQEA" + }, + { + "id": "minecraft:spruce_fence", + "block_state_b64": "CgAAAwgAYmxvY2tfaWRCAwAACAQAbmFtZRYAbWluZWNyYWZ0OnNwcnVjZV9mZW5jZQQJAG5hbWVfaGFzaPQCm+aX1ZQeAwoAbmV0d29ya19pZD1QUEoKBgBzdGF0ZXMAAwcAdmVyc2lvbgEoFQEA" + }, + { + "id": "minecraft:birch_fence", + "block_state_b64": "CgAAAwgAYmxvY2tfaWQ/AwAACAQAbmFtZRUAbWluZWNyYWZ0OmJpcmNoX2ZlbmNlBAkAbmFtZV9oYXNo6CJ2ATpANfgDCgBuZXR3b3JrX2lkmCUV2QoGAHN0YXRlcwADBwB2ZXJzaW9uASgVAQA=" + }, + { + "id": "minecraft:jungle_fence", + "block_state_b64": "CgAAAwgAYmxvY2tfaWRBAwAACAQAbmFtZRYAbWluZWNyYWZ0Omp1bmdsZV9mZW5jZQQJAG5hbWVfaGFzaOX4cD9uAmsdAwoAbmV0d29ya19pZHz1VxkKBgBzdGF0ZXMAAwcAdmVyc2lvbgEoFQEA" + }, + { + "id": "minecraft:acacia_fence", + "block_state_b64": "CgAAAwgAYmxvY2tfaWQ+AwAACAQAbmFtZRYAbWluZWNyYWZ0OmFjYWNpYV9mZW5jZQQJAG5hbWVfaGFzaGjn+RlKVDH6AwoAbmV0d29ya19pZNVGubwKBgBzdGF0ZXMAAwcAdmVyc2lvbgEoFQEA" + }, + { + "id": "minecraft:dark_oak_fence", + "block_state_b64": "CgAAAwgAYmxvY2tfaWRAAwAACAQAbmFtZRgAbWluZWNyYWZ0OmRhcmtfb2FrX2ZlbmNlBAkAbmFtZV9oYXNoGPj0gCgM0c0DCgBuZXR3b3JrX2lk2w+gEwoGAHN0YXRlcwADBwB2ZXJzaW9uASgVAQA=" + }, + { + "id": "minecraft:mangrove_fence", + "block_state_b64": "CgAAAwgAYmxvY2tfaWTqAgAACAQAbmFtZRgAbWluZWNyYWZ0Om1hbmdyb3ZlX2ZlbmNlBAkAbmFtZV9oYXNowwAd7tPu9bsDCgBuZXR3b3JrX2lkKEcd0goGAHN0YXRlcwADBwB2ZXJzaW9uASgVAQA=" + }, + { + "id": "minecraft:cherry_fence", + "block_state_b64": "CgAAAwgAYmxvY2tfaWQTAwAACAQAbmFtZRYAbWluZWNyYWZ0OmNoZXJyeV9mZW5jZQQJAG5hbWVfaGFzaFmtUfHfTxcxAwoAbmV0d29ya19pZPCBxAIKBgBzdGF0ZXMAAwcAdmVyc2lvbgEoFQEA" + }, + { + "id": "minecraft:bamboo_fence", + "block_state_b64": "CgAAAwgAYmxvY2tfaWQCAwAACAQAbmFtZRYAbWluZWNyYWZ0OmJhbWJvb19mZW5jZQQJAG5hbWVfaGFzaCKRbxfXsfkiAwoAbmV0d29ya19pZJNXKFcKBgBzdGF0ZXMAAwcAdmVyc2lvbgEoFQEA" + }, + { + "id": "minecraft:nether_brick_fence", + "block_state_b64": "CgAAAwgAYmxvY2tfaWRxAAAACAQAbmFtZRwAbWluZWNyYWZ0Om5ldGhlcl9icmlja19mZW5jZQQJAG5hbWVfaGFzaA6030ngawxcAwoAbmV0d29ya19pZLnjLF4KBgBzdGF0ZXMAAwcAdmVyc2lvbgEoFQEA" + }, + { + "id": "minecraft:crimson_fence", + "block_state_b64": "CgAAAwgAYmxvY2tfaWT/AQAACAQAbmFtZRcAbWluZWNyYWZ0OmNyaW1zb25fZmVuY2UECQBuYW1lX2hhc2jhUhKv1HGj9AMKAG5ldHdvcmtfaWR3OH3OCgYAc3RhdGVzAAMHAHZlcnNpb24BKBUBAA==" + }, + { + "id": "minecraft:warped_fence", + "block_state_b64": "CgAAAwgAYmxvY2tfaWQAAgAACAQAbmFtZRYAbWluZWNyYWZ0OndhcnBlZF9mZW5jZQQJAG5hbWVfaGFzaJfb3/YuKmOWAwoAbmV0d29ya19pZCpaGC8KBgBzdGF0ZXMAAwcAdmVyc2lvbgEoFQEA" + }, + { + "id": "minecraft:fence_gate", + "block_state_b64": "CgAAAwgAYmxvY2tfaWRrAAAACAQAbmFtZRQAbWluZWNyYWZ0OmZlbmNlX2dhdGUECQBuYW1lX2hhc2hTxpjEDmRzAwMKAG5ldHdvcmtfaWR+T9kTCgYAc3RhdGVzAwkAZGlyZWN0aW9uAAAAAAELAGluX3dhbGxfYml0AAEIAG9wZW5fYml0AAADBwB2ZXJzaW9uASgVAQA=" + }, + { + "id": "minecraft:spruce_fence_gate", + "block_state_b64": "CgAAAwgAYmxvY2tfaWS3AAAACAQAbmFtZRsAbWluZWNyYWZ0OnNwcnVjZV9mZW5jZV9nYXRlBAkAbmFtZV9oYXNoanTVB84HRbkDCgBuZXR3b3JrX2lkEnw5egoGAHN0YXRlcwMJAGRpcmVjdGlvbgAAAAABCwBpbl93YWxsX2JpdAABCABvcGVuX2JpdAAAAwcAdmVyc2lvbgEoFQEA" + }, + { + "id": "minecraft:birch_fence_gate", + "block_state_b64": "CgAAAwgAYmxvY2tfaWS4AAAACAQAbmFtZRoAbWluZWNyYWZ0OmJpcmNoX2ZlbmNlX2dhdGUECQBuYW1lX2hhc2jmfPklI8azSwMKAG5ldHdvcmtfaWQL77/BCgYAc3RhdGVzAwkAZGlyZWN0aW9uAAAAAAELAGluX3dhbGxfYml0AAEIAG9wZW5fYml0AAADBwB2ZXJzaW9uASgVAQA=" + }, + { + "id": "minecraft:jungle_fence_gate", + "block_state_b64": "CgAAAwgAYmxvY2tfaWS5AAAACAQAbmFtZRsAbWluZWNyYWZ0Omp1bmdsZV9mZW5jZV9nYXRlBAkAbmFtZV9oYXNobYVQkfBomIcDCgBuZXR3b3JrX2lkA1zgtgoGAHN0YXRlcwMJAGRpcmVjdGlvbgAAAAABCwBpbl93YWxsX2JpdAABCABvcGVuX2JpdAAAAwcAdmVyc2lvbgEoFQEA" + }, + { + "id": "minecraft:acacia_fence_gate", + "block_state_b64": "CgAAAwgAYmxvY2tfaWS7AAAACAQAbmFtZRsAbWluZWNyYWZ0OmFjYWNpYV9mZW5jZV9nYXRlBAkAbmFtZV9oYXNoZnrLUx/XSekDCgBuZXR3b3JrX2lkHg/kTgoGAHN0YXRlcwMJAGRpcmVjdGlvbgAAAAABCwBpbl93YWxsX2JpdAABCABvcGVuX2JpdAAAAwcAdmVyc2lvbgEoFQEA" + }, + { + "id": "minecraft:dark_oak_fence_gate", + "block_state_b64": "CgAAAwgAYmxvY2tfaWS6AAAACAQAbmFtZR0AbWluZWNyYWZ0OmRhcmtfb2FrX2ZlbmNlX2dhdGUECQBuYW1lX2hhc2j2PTvdJJHcVQMKAG5ldHdvcmtfaWTwjOCeCgYAc3RhdGVzAwkAZGlyZWN0aW9uAAAAAAELAGluX3dhbGxfYml0AAEIAG9wZW5fYml0AAADBwB2ZXJzaW9uASgVAQA=" + }, + { + "id": "minecraft:mangrove_fence_gate", + "block_state_b64": "CgAAAwgAYmxvY2tfaWTrAgAACAQAbmFtZR0AbWluZWNyYWZ0Om1hbmdyb3ZlX2ZlbmNlX2dhdGUECQBuYW1lX2hhc2i/kOhBKiI/dAMKAG5ldHdvcmtfaWSfweCSCgYAc3RhdGVzAwkAZGlyZWN0aW9uAAAAAAELAGluX3dhbGxfYml0AAEIAG9wZW5fYml0AAADBwB2ZXJzaW9uASgVAQA=" + }, + { + "id": "minecraft:cherry_fence_gate", + "block_state_b64": "CgAAAwgAYmxvY2tfaWQUAwAACAQAbmFtZRsAbWluZWNyYWZ0OmNoZXJyeV9mZW5jZV9nYXRlBAkAbmFtZV9oYXNoKWLgCk0z+PsDCgBuZXR3b3JrX2lk/9bTZQoGAHN0YXRlcwMJAGRpcmVjdGlvbgAAAAABCwBpbl93YWxsX2JpdAABCABvcGVuX2JpdAAAAwcAdmVyc2lvbgEoFQEA" + }, + { + "id": "minecraft:bamboo_fence_gate", + "block_state_b64": "CgAAAwgAYmxvY2tfaWQDAwAACAQAbmFtZRsAbWluZWNyYWZ0OmJhbWJvb19mZW5jZV9nYXRlBAkAbmFtZV9oYXNopH1JrUgwdIADCgBuZXR3b3JrX2lkzIpPywoGAHN0YXRlcwMJAGRpcmVjdGlvbgAAAAABCwBpbl93YWxsX2JpdAABCABvcGVuX2JpdAAAAwcAdmVyc2lvbgEoFQEA" + }, + { + "id": "minecraft:crimson_fence_gate", + "block_state_b64": "CgAAAwgAYmxvY2tfaWQBAgAACAQAbmFtZRwAbWluZWNyYWZ0OmNyaW1zb25fZmVuY2VfZ2F0ZQQJAG5hbWVfaGFzaHE3Gfd0Z2d2AwoAbmV0d29ya19pZDQzVbEKBgBzdGF0ZXMDCQBkaXJlY3Rpb24AAAAAAQsAaW5fd2FsbF9iaXQAAQgAb3Blbl9iaXQAAAMHAHZlcnNpb24BKBUBAA==" + }, + { + "id": "minecraft:warped_fence_gate", + "block_state_b64": "CgAAAwgAYmxvY2tfaWQCAgAACAQAbmFtZRsAbWluZWNyYWZ0OndhcnBlZF9mZW5jZV9nYXRlBAkAbmFtZV9oYXNoy0oIBjDIG4kDCgBuZXR3b3JrX2lkkf+/3QoGAHN0YXRlcwMJAGRpcmVjdGlvbgAAAAABCwBpbl93YWxsX2JpdAABCABvcGVuX2JpdAAAAwcAdmVyc2lvbgEoFQEA" + }, + { + "id": "minecraft:normal_stone_stairs", + "block_state_b64": "CgAAAwgAYmxvY2tfaWSzAQAACAQAbmFtZR0AbWluZWNyYWZ0Om5vcm1hbF9zdG9uZV9zdGFpcnMECQBuYW1lX2hhc2hAEktZZOkGIwMKAG5ldHdvcmtfaWQeH1ALCgYAc3RhdGVzAQ8AdXBzaWRlX2Rvd25fYml0AAMQAHdlaXJkb19kaXJlY3Rpb24AAAAAAAMHAHZlcnNpb24BKBUBAA==" + }, + { + "id": "minecraft:stone_stairs", + "block_state_b64": "CgAAAwgAYmxvY2tfaWRDAAAACAQAbmFtZRYAbWluZWNyYWZ0OnN0b25lX3N0YWlycwQJAG5hbWVfaGFzaNRjqVC5GRVDAwoAbmV0d29ya19pZDcCv+MKBgBzdGF0ZXMBDwB1cHNpZGVfZG93bl9iaXQAAxAAd2VpcmRvX2RpcmVjdGlvbgAAAAAAAwcAdmVyc2lvbgEoFQEA" + }, + { + "id": "minecraft:mossy_cobblestone_stairs", + "block_state_b64": "CgAAAwgAYmxvY2tfaWSyAQAACAQAbmFtZSIAbWluZWNyYWZ0Om1vc3N5X2NvYmJsZXN0b25lX3N0YWlycwQJAG5hbWVfaGFzaMVSTq5z9n1RAwoAbmV0d29ya19pZFIfrhkKBgBzdGF0ZXMBDwB1cHNpZGVfZG93bl9iaXQAAxAAd2VpcmRvX2RpcmVjdGlvbgAAAAAAAwcAdmVyc2lvbgEoFQEA" + }, + { + "id": "minecraft:oak_stairs", + "block_state_b64": "CgAAAwgAYmxvY2tfaWQ1AAAACAQAbmFtZRQAbWluZWNyYWZ0Om9ha19zdGFpcnMECQBuYW1lX2hhc2jk/HFzdXy0FQMKAG5ldHdvcmtfaWQJjyzBCgYAc3RhdGVzAQ8AdXBzaWRlX2Rvd25fYml0AAMQAHdlaXJkb19kaXJlY3Rpb24AAAAAAAMHAHZlcnNpb24BKBUBAA==" + }, + { + "id": "minecraft:spruce_stairs", + "block_state_b64": "CgAAAwgAYmxvY2tfaWSGAAAACAQAbmFtZRcAbWluZWNyYWZ0OnNwcnVjZV9zdGFpcnMECQBuYW1lX2hhc2iznygw7uBPBQMKAG5ldHdvcmtfaWTv+is3CgYAc3RhdGVzAQ8AdXBzaWRlX2Rvd25fYml0AAMQAHdlaXJkb19kaXJlY3Rpb24AAAAAAAMHAHZlcnNpb24BKBUBAA==" + }, + { + "id": "minecraft:birch_stairs", + "block_state_b64": "CgAAAwgAYmxvY2tfaWSHAAAACAQAbmFtZRYAbWluZWNyYWZ0OmJpcmNoX3N0YWlycwQJAG5hbWVfaGFzaPfhbL619a3GAwoAbmV0d29ya19pZFyPlHAKBgBzdGF0ZXMBDwB1cHNpZGVfZG93bl9iaXQAAxAAd2VpcmRvX2RpcmVjdGlvbgAAAAAAAwcAdmVyc2lvbgEoFQEA" + }, + { + "id": "minecraft:jungle_stairs", + "block_state_b64": "CgAAAwgAYmxvY2tfaWSIAAAACAQAbmFtZRcAbWluZWNyYWZ0Omp1bmdsZV9zdGFpcnMECQBuYW1lX2hhc2jodJsHUbOVxQMKAG5ldHdvcmtfaWR0z5d4CgYAc3RhdGVzAQ8AdXBzaWRlX2Rvd25fYml0AAMQAHdlaXJkb19kaXJlY3Rpb24AAAAAAAMHAHZlcnNpb24BKBUBAA==" + }, + { + "id": "minecraft:acacia_stairs", + "block_state_b64": "CgAAAwgAYmxvY2tfaWSjAAAACAQAbmFtZRcAbWluZWNyYWZ0OmFjYWNpYV9zdGFpcnMECQBuYW1lX2hhc2h3x1NmD43IqQMKAG5ldHdvcmtfaWS7Jwz6CgYAc3RhdGVzAQ8AdXBzaWRlX2Rvd25fYml0AAMQAHdlaXJkb19kaXJlY3Rpb24AAAAAAAMHAHZlcnNpb24BKBUBAA==" + }, + { + "id": "minecraft:dark_oak_stairs", + "block_state_b64": "CgAAAwgAYmxvY2tfaWSkAAAACAQAbmFtZRkAbWluZWNyYWZ0OmRhcmtfb2FrX3N0YWlycwQJAG5hbWVfaGFzaMfwkbYPbNmAAwoAbmV0d29ya19pZCmBYKAKBgBzdGF0ZXMBDwB1cHNpZGVfZG93bl9iaXQAAxAAd2VpcmRvX2RpcmVjdGlvbgAAAAAAAwcAdmVyc2lvbgEoFQEA" + }, + { + "id": "minecraft:mangrove_stairs", + "block_state_b64": "CgAAAwgAYmxvY2tfaWTnAgAACAQAbmFtZRkAbWluZWNyYWZ0Om1hbmdyb3ZlX3N0YWlycwQJAG5hbWVfaGFzaNpUDY+uGMpyAwoAbmV0d29ya19pZChzUAsKBgBzdGF0ZXMBDwB1cHNpZGVfZG93bl9iaXQAAxAAd2VpcmRvX2RpcmVjdGlvbgAAAAAAAwcAdmVyc2lvbgEoFQEA" + }, + { + "id": "minecraft:cherry_stairs", + "block_state_b64": "CgAAAwgAYmxvY2tfaWQcAwAACAQAbmFtZRcAbWluZWNyYWZ0OmNoZXJyeV9zdGFpcnMECQBuYW1lX2hhc2jMtr0v9JY4zwMKAG5ldHdvcmtfaWRQwq31CgYAc3RhdGVzAQ8AdXBzaWRlX2Rvd25fYml0AAMQAHdlaXJkb19kaXJlY3Rpb24AAAAAAAMHAHZlcnNpb24BKBUBAA==" + }, + { + "id": "minecraft:bamboo_stairs", + "block_state_b64": "CgAAAwgAYmxvY2tfaWT/AgAACAQAbmFtZRcAbWluZWNyYWZ0OmJhbWJvb19zdGFpcnMECQBuYW1lX2hhc2jFOzWL8PalKwMKAG5ldHdvcmtfaWTVPh42CgYAc3RhdGVzAQ8AdXBzaWRlX2Rvd25fYml0AAMQAHdlaXJkb19kaXJlY3Rpb24AAAAAAAMHAHZlcnNpb24BKBUBAA==" + }, + { + "id": "minecraft:bamboo_mosaic_stairs", + "block_state_b64": "CgAAAwgAYmxvY2tfaWQKAwAACAQAbmFtZR4AbWluZWNyYWZ0OmJhbWJvb19tb3NhaWNfc3RhaXJzBAkAbmFtZV9oYXNoNLPiveSHPaoDCgBuZXR3b3JrX2lk44PHjgoGAHN0YXRlcwEPAHVwc2lkZV9kb3duX2JpdAADEAB3ZWlyZG9fZGlyZWN0aW9uAAAAAAADBwB2ZXJzaW9uASgVAQA=" + }, + { + "id": "minecraft:stone_brick_stairs", + "block_state_b64": "CgAAAwgAYmxvY2tfaWRtAAAACAQAbmFtZRwAbWluZWNyYWZ0OnN0b25lX2JyaWNrX3N0YWlycwQJAG5hbWVfaGFzaN6tQViRo5cwAwoAbmV0d29ya19pZDMyMgIKBgBzdGF0ZXMBDwB1cHNpZGVfZG93bl9iaXQAAxAAd2VpcmRvX2RpcmVjdGlvbgAAAAAAAwcAdmVyc2lvbgEoFQEA" + }, + { + "id": "minecraft:mossy_stone_brick_stairs", + "block_state_b64": "CgAAAwgAYmxvY2tfaWSuAQAACAQAbmFtZSIAbWluZWNyYWZ0Om1vc3N5X3N0b25lX2JyaWNrX3N0YWlycwQJAG5hbWVfaGFzaIB/Zv5YBPuYAwoAbmV0d29ya19pZANTOsMKBgBzdGF0ZXMBDwB1cHNpZGVfZG93bl9iaXQAAxAAd2VpcmRvX2RpcmVjdGlvbgAAAAAAAwcAdmVyc2lvbgEoFQEA" + }, + { + "id": "minecraft:sandstone_stairs", + "block_state_b64": "CgAAAwgAYmxvY2tfaWSAAAAACAQAbmFtZRoAbWluZWNyYWZ0OnNhbmRzdG9uZV9zdGFpcnMECQBuYW1lX2hhc2hOyA0BoYUOPQMKAG5ldHdvcmtfaWSV/834CgYAc3RhdGVzAQ8AdXBzaWRlX2Rvd25fYml0AAMQAHdlaXJkb19kaXJlY3Rpb24AAAAAAAMHAHZlcnNpb24BKBUBAA==" + }, + { + "id": "minecraft:smooth_sandstone_stairs", + "block_state_b64": "CgAAAwgAYmxvY2tfaWSwAQAACAQAbmFtZSEAbWluZWNyYWZ0OnNtb290aF9zYW5kc3RvbmVfc3RhaXJzBAkAbmFtZV9oYXNoB+CuCd8Ruz8DCgBuZXR3b3JrX2lksR+m8QoGAHN0YXRlcwEPAHVwc2lkZV9kb3duX2JpdAADEAB3ZWlyZG9fZGlyZWN0aW9uAAAAAAADBwB2ZXJzaW9uASgVAQA=" + }, + { + "id": "minecraft:red_sandstone_stairs", + "block_state_b64": "CgAAAwgAYmxvY2tfaWS0AAAACAQAbmFtZR4AbWluZWNyYWZ0OnJlZF9zYW5kc3RvbmVfc3RhaXJzBAkAbmFtZV9oYXNoPs0LpHPL24YDCgBuZXR3b3JrX2lkLYVt3woGAHN0YXRlcwEPAHVwc2lkZV9kb3duX2JpdAADEAB3ZWlyZG9fZGlyZWN0aW9uAAAAAAADBwB2ZXJzaW9uASgVAQA=" + }, + { + "id": "minecraft:smooth_red_sandstone_stairs", + "block_state_b64": "CgAAAwgAYmxvY2tfaWSvAQAACAQAbmFtZSUAbWluZWNyYWZ0OnNtb290aF9yZWRfc2FuZHN0b25lX3N0YWlycwQJAG5hbWVfaGFzaBvjtQv5pf+MAwoAbmV0d29ya19pZMHNND8KBgBzdGF0ZXMBDwB1cHNpZGVfZG93bl9iaXQAAxAAd2VpcmRvX2RpcmVjdGlvbgAAAAAAAwcAdmVyc2lvbgEoFQEA" + }, + { + "id": "minecraft:granite_stairs", + "block_state_b64": "CgAAAwgAYmxvY2tfaWSoAQAACAQAbmFtZRgAbWluZWNyYWZ0OmdyYW5pdGVfc3RhaXJzBAkAbmFtZV9oYXNoGzpvtoqKQjgDCgBuZXR3b3JrX2lkPkcB1goGAHN0YXRlcwEPAHVwc2lkZV9kb3duX2JpdAADEAB3ZWlyZG9fZGlyZWN0aW9uAAAAAAADBwB2ZXJzaW9uASgVAQA=" + }, + { + "id": "minecraft:polished_granite_stairs", + "block_state_b64": "CgAAAwgAYmxvY2tfaWSrAQAACAQAbmFtZSEAbWluZWNyYWZ0OnBvbGlzaGVkX2dyYW5pdGVfc3RhaXJzBAkAbmFtZV9oYXNo3PvbSfEQklIDCgBuZXR3b3JrX2lkMmEm3AoGAHN0YXRlcwEPAHVwc2lkZV9kb3duX2JpdAADEAB3ZWlyZG9fZGlyZWN0aW9uAAAAAAADBwB2ZXJzaW9uASgVAQA=" + }, + { + "id": "minecraft:diorite_stairs", + "block_state_b64": "CgAAAwgAYmxvY2tfaWSpAQAACAQAbmFtZRgAbWluZWNyYWZ0OmRpb3JpdGVfc3RhaXJzBAkAbmFtZV9oYXNoi73T8VQuZmcDCgBuZXR3b3JrX2lk6i6nBQoGAHN0YXRlcwEPAHVwc2lkZV9kb3duX2JpdAADEAB3ZWlyZG9fZGlyZWN0aW9uAAAAAAADBwB2ZXJzaW9uASgVAQA=" + }, + { + "id": "minecraft:polished_diorite_stairs", + "block_state_b64": "CgAAAwgAYmxvY2tfaWSsAQAACAQAbmFtZSEAbWluZWNyYWZ0OnBvbGlzaGVkX2Rpb3JpdGVfc3RhaXJzBAkAbmFtZV9oYXNoFKRJd5Wk5L0DCgBuZXR3b3JrX2lkbt2ioAoGAHN0YXRlcwEPAHVwc2lkZV9kb3duX2JpdAADEAB3ZWlyZG9fZGlyZWN0aW9uAAAAAAADBwB2ZXJzaW9uASgVAQA=" + }, + { + "id": "minecraft:andesite_stairs", + "block_state_b64": "CgAAAwgAYmxvY2tfaWSqAQAACAQAbmFtZRkAbWluZWNyYWZ0OmFuZGVzaXRlX3N0YWlycwQJAG5hbWVfaGFzaO5w2FKBw76EAwoAbmV0d29ya19pZKhXEgUKBgBzdGF0ZXMBDwB1cHNpZGVfZG93bl9iaXQAAxAAd2VpcmRvX2RpcmVjdGlvbgAAAAAAAwcAdmVyc2lvbgEoFQEA" + }, + { + "id": "minecraft:polished_andesite_stairs", + "block_state_b64": "CgAAAwgAYmxvY2tfaWStAQAACAQAbmFtZSIAbWluZWNyYWZ0OnBvbGlzaGVkX2FuZGVzaXRlX3N0YWlycwQJAG5hbWVfaGFzaNcZZ/zmLInIAwoAbmV0d29ya19pZJTHrlEKBgBzdGF0ZXMBDwB1cHNpZGVfZG93bl9iaXQAAxAAd2VpcmRvX2RpcmVjdGlvbgAAAAAAAwcAdmVyc2lvbgEoFQEA" + }, + { + "id": "minecraft:brick_stairs", + "block_state_b64": "CgAAAwgAYmxvY2tfaWRsAAAACAQAbmFtZRYAbWluZWNyYWZ0OmJyaWNrX3N0YWlycwQJAG5hbWVfaGFzaMyt+cRDk5O2AwoAbmV0d29ya19pZNeMh58KBgBzdGF0ZXMBDwB1cHNpZGVfZG93bl9iaXQAAxAAd2VpcmRvX2RpcmVjdGlvbgAAAAAAAwcAdmVyc2lvbgEoFQEA" + }, + { + "id": "minecraft:nether_brick_stairs", + "block_state_b64": "CgAAAwgAYmxvY2tfaWRyAAAACAQAbmFtZR0AbWluZWNyYWZ0Om5ldGhlcl9icmlja19zdGFpcnMECQBuYW1lX2hhc2jRqIoOXgifBAMKAG5ldHdvcmtfaWQDiw5yCgYAc3RhdGVzAQ8AdXBzaWRlX2Rvd25fYml0AAMQAHdlaXJkb19kaXJlY3Rpb24AAAAAAAMHAHZlcnNpb24BKBUBAA==" + }, + { + "id": "minecraft:red_nether_brick_stairs", + "block_state_b64": "CgAAAwgAYmxvY2tfaWS3AQAACAQAbmFtZSEAbWluZWNyYWZ0OnJlZF9uZXRoZXJfYnJpY2tfc3RhaXJzBAkAbmFtZV9oYXNogQvosSbcj7kDCgBuZXR3b3JrX2lkx2IMtAoGAHN0YXRlcwEPAHVwc2lkZV9kb3duX2JpdAADEAB3ZWlyZG9fZGlyZWN0aW9uAAAAAAADBwB2ZXJzaW9uASgVAQA=" + }, + { + "id": "minecraft:end_brick_stairs", + "block_state_b64": "CgAAAwgAYmxvY2tfaWSxAQAACAQAbmFtZRoAbWluZWNyYWZ0OmVuZF9icmlja19zdGFpcnMECQBuYW1lX2hhc2hmlAk+QhsUsQMKAG5ldHdvcmtfaWTN7KFaCgYAc3RhdGVzAQ8AdXBzaWRlX2Rvd25fYml0AAMQAHdlaXJkb19kaXJlY3Rpb24AAAAAAAMHAHZlcnNpb24BKBUBAA==" + }, + { + "id": "minecraft:quartz_stairs", + "block_state_b64": "CgAAAwgAYmxvY2tfaWScAAAACAQAbmFtZRcAbWluZWNyYWZ0OnF1YXJ0el9zdGFpcnMECQBuYW1lX2hhc2hmvpvOqGi6egMKAG5ldHdvcmtfaWRmUTh7CgYAc3RhdGVzAQ8AdXBzaWRlX2Rvd25fYml0AAMQAHdlaXJkb19kaXJlY3Rpb24AAAAAAAMHAHZlcnNpb24BKBUBAA==" + }, + { + "id": "minecraft:smooth_quartz_stairs", + "block_state_b64": "CgAAAwgAYmxvY2tfaWS4AQAACAQAbmFtZR4AbWluZWNyYWZ0OnNtb290aF9xdWFydHpfc3RhaXJzBAkAbmFtZV9oYXNoNZZ9rX0qZOsDCgBuZXR3b3JrX2lkzsgQyQoGAHN0YXRlcwEPAHVwc2lkZV9kb3duX2JpdAADEAB3ZWlyZG9fZGlyZWN0aW9uAAAAAAADBwB2ZXJzaW9uASgVAQA=" + }, + { + "id": "minecraft:purpur_stairs", + "block_state_b64": "CgAAAwgAYmxvY2tfaWTLAAAACAQAbmFtZRcAbWluZWNyYWZ0OnB1cnB1cl9zdGFpcnMECQBuYW1lX2hhc2ifwDxeezXD7gMKAG5ldHdvcmtfaWTT+rxiCgYAc3RhdGVzAQ8AdXBzaWRlX2Rvd25fYml0AAMQAHdlaXJkb19kaXJlY3Rpb24AAAAAAAMHAHZlcnNpb24BKBUBAA==" + }, + { + "id": "minecraft:prismarine_stairs", + "block_state_b64": "CgAAAwgAYmxvY2tfaWQBAQAACAQAbmFtZRsAbWluZWNyYWZ0OnByaXNtYXJpbmVfc3RhaXJzBAkAbmFtZV9oYXNooTHSZ+IrYtcDCgBuZXR3b3JrX2lkxTJfeAoGAHN0YXRlcwEPAHVwc2lkZV9kb3duX2JpdAADEAB3ZWlyZG9fZGlyZWN0aW9uAAAAAAADBwB2ZXJzaW9uASgVAQA=" + }, + { + "id": "minecraft:dark_prismarine_stairs", + "block_state_b64": "CgAAAwgAYmxvY2tfaWQCAQAACAQAbmFtZSAAbWluZWNyYWZ0OmRhcmtfcHJpc21hcmluZV9zdGFpcnMECQBuYW1lX2hhc2hIciLmam4o4AMKAG5ldHdvcmtfaWTVu7TCCgYAc3RhdGVzAQ8AdXBzaWRlX2Rvd25fYml0AAMQAHdlaXJkb19kaXJlY3Rpb24AAAAAAAMHAHZlcnNpb24BKBUBAA==" + }, + { + "id": "minecraft:prismarine_bricks_stairs", + "block_state_b64": "CgAAAwgAYmxvY2tfaWQDAQAACAQAbmFtZSIAbWluZWNyYWZ0OnByaXNtYXJpbmVfYnJpY2tzX3N0YWlycwQJAG5hbWVfaGFzaNIjq1oBlZMMAwoAbmV0d29ya19pZGEFwLYKBgBzdGF0ZXMBDwB1cHNpZGVfZG93bl9iaXQAAxAAd2VpcmRvX2RpcmVjdGlvbgAAAAAAAwcAdmVyc2lvbgEoFQEA" + }, + { + "id": "minecraft:crimson_stairs", + "block_state_b64": "CgAAAwgAYmxvY2tfaWT9AQAACAQAbmFtZRgAbWluZWNyYWZ0OmNyaW1zb25fc3RhaXJzBAkAbmFtZV9oYXNoZJqIzCBpCq4DCgBuZXR3b3JrX2lktXE00AoGAHN0YXRlcwEPAHVwc2lkZV9kb3duX2JpdAADEAB3ZWlyZG9fZGlyZWN0aW9uAAAAAAADBwB2ZXJzaW9uASgVAQA=" + }, + { + "id": "minecraft:warped_stairs", + "block_state_b64": "CgAAAwgAYmxvY2tfaWT+AQAACAQAbmFtZRcAbWluZWNyYWZ0OndhcnBlZF9zdGFpcnMECQBuYW1lX2hhc2hOkY27jLD4RQMKAG5ldHdvcmtfaWQ+E5VrCgYAc3RhdGVzAQ8AdXBzaWRlX2Rvd25fYml0AAMQAHdlaXJkb19kaXJlY3Rpb24AAAAAAAMHAHZlcnNpb24BKBUBAA==" + }, + { + "id": "minecraft:blackstone_stairs", + "block_state_b64": "CgAAAwgAYmxvY2tfaWQTAgAACAQAbmFtZRsAbWluZWNyYWZ0OmJsYWNrc3RvbmVfc3RhaXJzBAkAbmFtZV9oYXNokdoUb76p9McDCgBuZXR3b3JrX2lk5fWI5goGAHN0YXRlcwEPAHVwc2lkZV9kb3duX2JpdAADEAB3ZWlyZG9fZGlyZWN0aW9uAAAAAAADBwB2ZXJzaW9uASgVAQA=" + }, + { + "id": "minecraft:polished_blackstone_stairs", + "block_state_b64": "CgAAAwgAYmxvY2tfaWQjAgAACAQAbmFtZSQAbWluZWNyYWZ0OnBvbGlzaGVkX2JsYWNrc3RvbmVfc3RhaXJzBAkAbmFtZV9oYXNolCFtFIE8MmADCgBuZXR3b3JrX2lkGTf7sgoGAHN0YXRlcwEPAHVwc2lkZV9kb3duX2JpdAADEAB3ZWlyZG9fZGlyZWN0aW9uAAAAAAADBwB2ZXJzaW9uASgVAQA=" + }, + { + "id": "minecraft:polished_blackstone_brick_stairs", + "block_state_b64": "CgAAAwgAYmxvY2tfaWQSAgAACAQAbmFtZSoAbWluZWNyYWZ0OnBvbGlzaGVkX2JsYWNrc3RvbmVfYnJpY2tfc3RhaXJzBAkAbmFtZV9oYXNonks6UlfpOmkDCgBuZXR3b3JrX2lkgYeOdAoGAHN0YXRlcwEPAHVwc2lkZV9kb3duX2JpdAADEAB3ZWlyZG9fZGlyZWN0aW9uAAAAAAADBwB2ZXJzaW9uASgVAQA=" + }, + { + "id": "minecraft:cobbled_deepslate_stairs", + "block_state_b64": "CgAAAwgAYmxvY2tfaWR8AgAACAQAbmFtZSIAbWluZWNyYWZ0OmNvYmJsZWRfZGVlcHNsYXRlX3N0YWlycwQJAG5hbWVfaGFzaPIfa+TpyJcIAwoAbmV0d29ya19pZJUvOYIKBgBzdGF0ZXMBDwB1cHNpZGVfZG93bl9iaXQAAxAAd2VpcmRvX2RpcmVjdGlvbgAAAAAAAwcAdmVyc2lvbgEoFQEA" + }, + { + "id": "minecraft:deepslate_tile_stairs", + "block_state_b64": "CgAAAwgAYmxvY2tfaWSEAgAACAQAbmFtZR8AbWluZWNyYWZ0OmRlZXBzbGF0ZV90aWxlX3N0YWlycwQJAG5hbWVfaGFzaGFRFzB72mN2AwoAbmV0d29ya19pZJEOgIsKBgBzdGF0ZXMBDwB1cHNpZGVfZG93bl9iaXQAAxAAd2VpcmRvX2RpcmVjdGlvbgAAAAAAAwcAdmVyc2lvbgEoFQEA" + }, + { + "id": "minecraft:polished_deepslate_stairs", + "block_state_b64": "CgAAAwgAYmxvY2tfaWSAAgAACAQAbmFtZSMAbWluZWNyYWZ0OnBvbGlzaGVkX2RlZXBzbGF0ZV9zdGFpcnMECQBuYW1lX2hhc2iNCYxVik9sGAMKAG5ldHdvcmtfaWSRVPnYCgYAc3RhdGVzAQ8AdXBzaWRlX2Rvd25fYml0AAMQAHdlaXJkb19kaXJlY3Rpb24AAAAAAAMHAHZlcnNpb24BKBUBAA==" + }, + { + "id": "minecraft:deepslate_brick_stairs", + "block_state_b64": "CgAAAwgAYmxvY2tfaWSIAgAACAQAbmFtZSAAbWluZWNyYWZ0OmRlZXBzbGF0ZV9icmlja19zdGFpcnMECQBuYW1lX2hhc2hIasOahEf83wMKAG5ldHdvcmtfaWQ1qEDCCgYAc3RhdGVzAQ8AdXBzaWRlX2Rvd25fYml0AAMQAHdlaXJkb19kaXJlY3Rpb24AAAAAAAMHAHZlcnNpb24BKBUBAA==" + }, + { + "id": "minecraft:tuff_stairs", + "block_state_b64": "CgAAAwgAYmxvY2tfaWTpAwAACAQAbmFtZRUAbWluZWNyYWZ0OnR1ZmZfc3RhaXJzBAkAbmFtZV9oYXNoKjyNUBjcfZsDCgBuZXR3b3JrX2lk+LsycgoGAHN0YXRlcwEPAHVwc2lkZV9kb3duX2JpdAADEAB3ZWlyZG9fZGlyZWN0aW9uAAAAAAADBwB2ZXJzaW9uASgVAQA=" + }, + { + "id": "minecraft:polished_tuff_stairs", + "block_state_b64": "CgAAAwgAYmxvY2tfaWTuAwAACAQAbmFtZR4AbWluZWNyYWZ0OnBvbGlzaGVkX3R1ZmZfc3RhaXJzBAkAbmFtZV9oYXNo8yuah8QI1dcDCgBuZXR3b3JrX2lkjLoU4AoGAHN0YXRlcwEPAHVwc2lkZV9kb3duX2JpdAADEAB3ZWlyZG9fZGlyZWN0aW9uAAAAAAADBwB2ZXJzaW9uASgVAQA=" + }, + { + "id": "minecraft:tuff_brick_stairs", + "block_state_b64": "CgAAAwgAYmxvY2tfaWT0AwAACAQAbmFtZRsAbWluZWNyYWZ0OnR1ZmZfYnJpY2tfc3RhaXJzBAkAbmFtZV9oYXNoWJpkAurUfKwDCgBuZXR3b3JrX2lkUMcjiwoGAHN0YXRlcwEPAHVwc2lkZV9kb3duX2JpdAADEAB3ZWlyZG9fZGlyZWN0aW9uAAAAAAADBwB2ZXJzaW9uASgVAQA=" + }, + { + "id": "minecraft:mud_brick_stairs", + "block_state_b64": "CgAAAwgAYmxvY2tfaWTfAgAACAQAbmFtZRoAbWluZWNyYWZ0Om11ZF9icmlja19zdGFpcnMECQBuYW1lX2hhc2gt3qxK1NWajAMKAG5ldHdvcmtfaWSm9N3MCgYAc3RhdGVzAQ8AdXBzaWRlX2Rvd25fYml0AAMQAHdlaXJkb19kaXJlY3Rpb24AAAAAAAMHAHZlcnNpb24BKBUBAA==" + }, + { + "id": "minecraft:cut_copper_stairs", + "block_state_b64": "CgAAAwgAYmxvY2tfaWRhAgAACAQAbmFtZRsAbWluZWNyYWZ0OmN1dF9jb3BwZXJfc3RhaXJzBAkAbmFtZV9oYXNoHfoAXYq5G3MDCgBuZXR3b3JrX2lkeetf7woGAHN0YXRlcwEPAHVwc2lkZV9kb3duX2JpdAADEAB3ZWlyZG9fZGlyZWN0aW9uAAAAAAADBwB2ZXJzaW9uASgVAQA=" + }, + { + "id": "minecraft:exposed_cut_copper_stairs", + "block_state_b64": "CgAAAwgAYmxvY2tfaWRiAgAACAQAbmFtZSMAbWluZWNyYWZ0OmV4cG9zZWRfY3V0X2NvcHBlcl9zdGFpcnMECQBuYW1lX2hhc2howneQGtZ9cgMKAG5ldHdvcmtfaWSg73zdCgYAc3RhdGVzAQ8AdXBzaWRlX2Rvd25fYml0AAMQAHdlaXJkb19kaXJlY3Rpb24AAAAAAAMHAHZlcnNpb24BKBUBAA==" + }, + { + "id": "minecraft:weathered_cut_copper_stairs", + "block_state_b64": "CgAAAwgAYmxvY2tfaWRjAgAACAQAbmFtZSUAbWluZWNyYWZ0OndlYXRoZXJlZF9jdXRfY29wcGVyX3N0YWlycwQJAG5hbWVfaGFzaP+R5loXxrVgAwoAbmV0d29ya19pZOnbRf4KBgBzdGF0ZXMBDwB1cHNpZGVfZG93bl9iaXQAAxAAd2VpcmRvX2RpcmVjdGlvbgAAAAAAAwcAdmVyc2lvbgEoFQEA" + }, + { + "id": "minecraft:oxidized_cut_copper_stairs", + "block_state_b64": "CgAAAwgAYmxvY2tfaWRkAgAACAQAbmFtZSQAbWluZWNyYWZ0Om94aWRpemVkX2N1dF9jb3BwZXJfc3RhaXJzBAkAbmFtZV9oYXNo6Jeoq5rsPxsDCgBuZXR3b3JrX2lkmRjDnQoGAHN0YXRlcwEPAHVwc2lkZV9kb3duX2JpdAADEAB3ZWlyZG9fZGlyZWN0aW9uAAAAAAADBwB2ZXJzaW9uASgVAQA=" + }, + { + "id": "minecraft:waxed_cut_copper_stairs", + "block_state_b64": "CgAAAwgAYmxvY2tfaWRlAgAACAQAbmFtZSEAbWluZWNyYWZ0OndheGVkX2N1dF9jb3BwZXJfc3RhaXJzBAkAbmFtZV9oYXNoh07CQj0/SR8DCgBuZXR3b3JrX2lkmYqoqAoGAHN0YXRlcwEPAHVwc2lkZV9kb3duX2JpdAADEAB3ZWlyZG9fZGlyZWN0aW9uAAAAAAADBwB2ZXJzaW9uASgVAQA=" + }, + { + "id": "minecraft:waxed_exposed_cut_copper_stairs", + "block_state_b64": "CgAAAwgAYmxvY2tfaWRmAgAACAQAbmFtZSkAbWluZWNyYWZ0OndheGVkX2V4cG9zZWRfY3V0X2NvcHBlcl9zdGFpcnMECQBuYW1lX2hhc2guVct1ilmxTwMKAG5ldHdvcmtfaWQgCPROCgYAc3RhdGVzAQ8AdXBzaWRlX2Rvd25fYml0AAMQAHdlaXJkb19kaXJlY3Rpb24AAAAAAAMHAHZlcnNpb24BKBUBAA==" + }, + { + "id": "minecraft:waxed_weathered_cut_copper_stairs", + "block_state_b64": "CgAAAwgAYmxvY2tfaWRnAgAACAQAbmFtZSsAbWluZWNyYWZ0OndheGVkX3dlYXRoZXJlZF9jdXRfY29wcGVyX3N0YWlycwQJAG5hbWVfaGFzaPXC8Sz/phCpAwoAbmV0d29ya19pZHlwHVsKBgBzdGF0ZXMBDwB1cHNpZGVfZG93bl9iaXQAAxAAd2VpcmRvX2RpcmVjdGlvbgAAAAAAAwcAdmVyc2lvbgEoFQEA" + }, + { + "id": "minecraft:waxed_oxidized_cut_copper_stairs", + "block_state_b64": "CgAAAwgAYmxvY2tfaWS/AgAACAQAbmFtZSoAbWluZWNyYWZ0OndheGVkX294aWRpemVkX2N1dF9jb3BwZXJfc3RhaXJzBAkAbmFtZV9oYXNoaqGdkuhxVZUDCgBuZXR3b3JrX2lkYQXzzgoGAHN0YXRlcwEPAHVwc2lkZV9kb3duX2JpdAADEAB3ZWlyZG9fZGlyZWN0aW9uAAAAAAADBwB2ZXJzaW9uASgVAQA=" + }, + { + "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:cherry_door" + }, + { + "id": "minecraft:bamboo_door" + }, + { + "id": "minecraft:iron_door" + }, + { + "id": "minecraft:crimson_door" + }, + { + "id": "minecraft:warped_door" + }, + { + "id": "minecraft:copper_door" + }, + { + "id": "minecraft:exposed_copper_door" + }, + { + "id": "minecraft:weathered_copper_door" + }, + { + "id": "minecraft:oxidized_copper_door" + }, + { + "id": "minecraft:waxed_copper_door" + }, + { + "id": "minecraft:waxed_exposed_copper_door" + }, + { + "id": "minecraft:waxed_weathered_copper_door" + }, + { + "id": "minecraft:waxed_oxidized_copper_door" + }, + { + "id": "minecraft:trapdoor", + "block_state_b64": "CgAAAwgAYmxvY2tfaWRgAAAACAQAbmFtZRIAbWluZWNyYWZ0OnRyYXBkb29yBAkAbmFtZV9oYXNotYiAJGtN0xADCgBuZXR3b3JrX2lkyTAWkAoGAHN0YXRlcwMJAGRpcmVjdGlvbgAAAAABCABvcGVuX2JpdAABDwB1cHNpZGVfZG93bl9iaXQAAAMHAHZlcnNpb24BKBUBAA==" + }, + { + "id": "minecraft:spruce_trapdoor", + "block_state_b64": "CgAAAwgAYmxvY2tfaWSUAQAACAQAbmFtZRkAbWluZWNyYWZ0OnNwcnVjZV90cmFwZG9vcgQJAG5hbWVfaGFzaOwlfbgBkUW4AwoAbmV0d29ya19pZPHy1K0KBgBzdGF0ZXMDCQBkaXJlY3Rpb24AAAAAAQgAb3Blbl9iaXQAAQ8AdXBzaWRlX2Rvd25fYml0AAADBwB2ZXJzaW9uASgVAQA=" + }, + { + "id": "minecraft:birch_trapdoor", + "block_state_b64": "CgAAAwgAYmxvY2tfaWSRAQAACAQAbmFtZRgAbWluZWNyYWZ0OmJpcmNoX3RyYXBkb29yBAkAbmFtZV9oYXNoSLtLweOLJ7wDCgBuZXR3b3JrX2lkeJWDfgoGAHN0YXRlcwMJAGRpcmVjdGlvbgAAAAABCABvcGVuX2JpdAABDwB1cHNpZGVfZG93bl9iaXQAAAMHAHZlcnNpb24BKBUBAA==" + }, + { + "id": "minecraft:jungle_trapdoor", + "block_state_b64": "CgAAAwgAYmxvY2tfaWSTAQAACAQAbmFtZRkAbWluZWNyYWZ0Omp1bmdsZV90cmFwZG9vcgQJAG5hbWVfaGFzaDP/TnM9wyCIAwoAbmV0d29ya19pZEy2fJoKBgBzdGF0ZXMDCQBkaXJlY3Rpb24AAAAAAQgAb3Blbl9iaXQAAQ8AdXBzaWRlX2Rvd25fYml0AAADBwB2ZXJzaW9uASgVAQA=" + }, + { + "id": "minecraft:acacia_trapdoor", + "block_state_b64": "CgAAAwgAYmxvY2tfaWSQAQAACAQAbmFtZRkAbWluZWNyYWZ0OmFjYWNpYV90cmFwZG9vcgQJAG5hbWVfaGFzaMj8xi3vmEKOAwoAbmV0d29ya19pZOHj8E8KBgBzdGF0ZXMDCQBkaXJlY3Rpb24AAAAAAQgAb3Blbl9iaXQAAQ8AdXBzaWRlX2Rvd25fYml0AAADBwB2ZXJzaW9uASgVAQA=" + }, + { + "id": "minecraft:dark_oak_trapdoor", + "block_state_b64": "CgAAAwgAYmxvY2tfaWSSAQAACAQAbmFtZRsAbWluZWNyYWZ0OmRhcmtfb2FrX3RyYXBkb29yBAkAbmFtZV9oYXNomB2GGJQ2aOMDCgBuZXR3b3JrX2lko5ZHTwoGAHN0YXRlcwMJAGRpcmVjdGlvbgAAAAABCABvcGVuX2JpdAABDwB1cHNpZGVfZG93bl9iaXQAAAMHAHZlcnNpb24BKBUBAA==" + }, + { + "id": "minecraft:mangrove_trapdoor", + "block_state_b64": "CgAAAwgAYmxvY2tfaWTvAgAACAQAbmFtZRsAbWluZWNyYWZ0Om1hbmdyb3ZlX3RyYXBkb29yBAkAbmFtZV9oYXNooV3kQsQUUmkDCgBuZXR3b3JrX2lkkF/mxAoGAHN0YXRlcwMJAGRpcmVjdGlvbgAAAAABCABvcGVuX2JpdAABDwB1cHNpZGVfZG93bl9iaXQAAAMHAHZlcnNpb24BKBUBAA==" + }, + { + "id": "minecraft:cherry_trapdoor", + "block_state_b64": "CgAAAwgAYmxvY2tfaWQeAwAACAQAbmFtZRkAbWluZWNyYWZ0OmNoZXJyeV90cmFwZG9vcgQJAG5hbWVfaGFzaH/PefpfdHgtAwoAbmV0d29ya19pZOA7eNgKBgBzdGF0ZXMDCQBkaXJlY3Rpb24AAAAAAQgAb3Blbl9iaXQAAQ8AdXBzaWRlX2Rvd25fYml0AAADBwB2ZXJzaW9uASgVAQA=" + }, + { + "id": "minecraft:bamboo_trapdoor", + "block_state_b64": "CgAAAwgAYmxvY2tfaWQHAwAACAQAbmFtZRkAbWluZWNyYWZ0OmJhbWJvb190cmFwZG9vcgQJAG5hbWVfaGFzaJrEOpsTwtKCAwoAbmV0d29ya19pZLvbPz8KBgBzdGF0ZXMDCQBkaXJlY3Rpb24AAAAAAQgAb3Blbl9iaXQAAQ8AdXBzaWRlX2Rvd25fYml0AAADBwB2ZXJzaW9uASgVAQA=" + }, + { + "id": "minecraft:iron_trapdoor", + "block_state_b64": "CgAAAwgAYmxvY2tfaWSnAAAACAQAbmFtZRcAbWluZWNyYWZ0Omlyb25fdHJhcGRvb3IECQBuYW1lX2hhc2gwA+IumsEiGQMKAG5ldHdvcmtfaWTvSVl/CgYAc3RhdGVzAwkAZGlyZWN0aW9uAAAAAAEIAG9wZW5fYml0AAEPAHVwc2lkZV9kb3duX2JpdAAAAwcAdmVyc2lvbgEoFQEA" + }, + { + "id": "minecraft:crimson_trapdoor", + "block_state_b64": "CgAAAwgAYmxvY2tfaWT1AQAACAQAbmFtZRoAbWluZWNyYWZ0OmNyaW1zb25fdHJhcGRvb3IECQBuYW1lX2hhc2jHXufTnwUkYgMKAG5ldHdvcmtfaWQLjMYVCgYAc3RhdGVzAwkAZGlyZWN0aW9uAAAAAAEIAG9wZW5fYml0AAEPAHVwc2lkZV9kb3duX2JpdAAAAwcAdmVyc2lvbgEoFQEA" + }, + { + "id": "minecraft:warped_trapdoor", + "block_state_b64": "CgAAAwgAYmxvY2tfaWT2AQAACAQAbmFtZRkAbWluZWNyYWZ0OndhcnBlZF90cmFwZG9vcgQJAG5hbWVfaGFzaA20wG/+vkd6AwoAbmV0d29ya19pZHKR/hYKBgBzdGF0ZXMDCQBkaXJlY3Rpb24AAAAAAQgAb3Blbl9iaXQAAQ8AdXBzaWRlX2Rvd25fYml0AAADBwB2ZXJzaW9uASgVAQA=" + }, + { + "id": "minecraft:copper_trapdoor", + "block_state_b64": "CgAAAwgAYmxvY2tfaWQXBAAACAQAbmFtZRkAbWluZWNyYWZ0OmNvcHBlcl90cmFwZG9vcgQJAG5hbWVfaGFzaO9fXio+svKVAwoAbmV0d29ya19pZMCoRjEKBgBzdGF0ZXMDCQBkaXJlY3Rpb24AAAAAAQgAb3Blbl9iaXQAAQ8AdXBzaWRlX2Rvd25fYml0AAADBwB2ZXJzaW9uASgVAQA=" + }, + { + "id": "minecraft:exposed_copper_trapdoor", + "block_state_b64": "CgAAAwgAYmxvY2tfaWQYBAAACAQAbmFtZSEAbWluZWNyYWZ0OmV4cG9zZWRfY29wcGVyX3RyYXBkb29yBAkAbmFtZV9oYXNoYhDFUysN7qUDCgBuZXR3b3JrX2lkMzwGJgoGAHN0YXRlcwMJAGRpcmVjdGlvbgAAAAABCABvcGVuX2JpdAABDwB1cHNpZGVfZG93bl9iaXQAAAMHAHZlcnNpb24BKBUBAA==" + }, + { + "id": "minecraft:weathered_copper_trapdoor", + "block_state_b64": "CgAAAwgAYmxvY2tfaWQZBAAACAQAbmFtZSMAbWluZWNyYWZ0OndlYXRoZXJlZF9jb3BwZXJfdHJhcGRvb3IECQBuYW1lX2hhc2hFnEC282a1tgMKAG5ldHdvcmtfaWTk70oiCgYAc3RhdGVzAwkAZGlyZWN0aW9uAAAAAAEIAG9wZW5fYml0AAEPAHVwc2lkZV9kb3duX2JpdAAAAwcAdmVyc2lvbgEoFQEA" + }, + { + "id": "minecraft:oxidized_copper_trapdoor", + "block_state_b64": "CgAAAwgAYmxvY2tfaWQaBAAACAQAbmFtZSIAbWluZWNyYWZ0Om94aWRpemVkX2NvcHBlcl90cmFwZG9vcgQJAG5hbWVfaGFzaOJpG/XFexVwAwoAbmV0d29ya19pZPhi0J4KBgBzdGF0ZXMDCQBkaXJlY3Rpb24AAAAAAQgAb3Blbl9iaXQAAQ8AdXBzaWRlX2Rvd25fYml0AAADBwB2ZXJzaW9uASgVAQA=" + }, + { + "id": "minecraft:waxed_copper_trapdoor", + "block_state_b64": "CgAAAwgAYmxvY2tfaWQbBAAACAQAbmFtZR8AbWluZWNyYWZ0OndheGVkX2NvcHBlcl90cmFwZG9vcgQJAG5hbWVfaGFzaO0JUKUHqNU6AwoAbmV0d29ya19pZJC3ZuMKBgBzdGF0ZXMDCQBkaXJlY3Rpb24AAAAAAQgAb3Blbl9iaXQAAQ8AdXBzaWRlX2Rvd25fYml0AAADBwB2ZXJzaW9uASgVAQA=" + }, + { + "id": "minecraft:waxed_exposed_copper_trapdoor", + "block_state_b64": "CgAAAwgAYmxvY2tfaWQcBAAACAQAbmFtZScAbWluZWNyYWZ0OndheGVkX2V4cG9zZWRfY29wcGVyX3RyYXBkb29yBAkAbmFtZV9oYXNoBHHxCpkUzpgDCgBuZXR3b3JrX2lkw2XBGQoGAHN0YXRlcwMJAGRpcmVjdGlvbgAAAAABCABvcGVuX2JpdAABDwB1cHNpZGVfZG93bl9iaXQAAAMHAHZlcnNpb24BKBUBAA==" + }, + { + "id": "minecraft:waxed_weathered_copper_trapdoor", + "block_state_b64": "CgAAAwgAYmxvY2tfaWQdBAAACAQAbmFtZSkAbWluZWNyYWZ0OndheGVkX3dlYXRoZXJlZF9jb3BwZXJfdHJhcGRvb3IECQBuYW1lX2hhc2gH9Fi3JCF4egMKAG5ldHdvcmtfaWRkGU6TCgYAc3RhdGVzAwkAZGlyZWN0aW9uAAAAAAEIAG9wZW5fYml0AAEPAHVwc2lkZV9kb3duX2JpdAAAAwcAdmVyc2lvbgEoFQEA" + }, + { + "id": "minecraft:waxed_oxidized_copper_trapdoor", + "block_state_b64": "CgAAAwgAYmxvY2tfaWQeBAAACAQAbmFtZSgAbWluZWNyYWZ0OndheGVkX294aWRpemVkX2NvcHBlcl90cmFwZG9vcgQJAG5hbWVfaGFzaNA/q9qAy6Z9AwoAbmV0d29ya19pZDgExS8KBgBzdGF0ZXMDCQBkaXJlY3Rpb24AAAAAAQgAb3Blbl9iaXQAAQ8AdXBzaWRlX2Rvd25fYml0AAADBwB2ZXJzaW9uASgVAQA=" + }, + { + "id": "minecraft:iron_bars", + "block_state_b64": "CgAAAwgAYmxvY2tfaWRlAAAACAQAbmFtZRMAbWluZWNyYWZ0Omlyb25fYmFycwQJAG5hbWVfaGFzaPuefWSNAe56AwoAbmV0d29ya19pZN2LB5IKBgBzdGF0ZXMAAwcAdmVyc2lvbgEoFQEA" + }, + { + "id": "minecraft:glass", + "block_state_b64": "CgAAAwgAYmxvY2tfaWQUAAAACAQAbmFtZQ8AbWluZWNyYWZ0OmdsYXNzBAkAbmFtZV9oYXNowGJByfWff6gDCgBuZXR3b3JrX2lk0hdLNwoGAHN0YXRlcwADBwB2ZXJzaW9uASgVAQA=" + }, + { + "id": "minecraft:white_stained_glass", + "block_state_b64": "CgAAAwgAYmxvY2tfaWTxAAAACAQAbmFtZR0AbWluZWNyYWZ0OndoaXRlX3N0YWluZWRfZ2xhc3MECQBuYW1lX2hhc2iHubqoMbu9fAMKAG5ldHdvcmtfaWRndBrUCgYAc3RhdGVzAAMHAHZlcnNpb24BKBUBAA==" + }, + { + "id": "minecraft:light_gray_stained_glass", + "block_state_b64": "CgAAAwgAYmxvY2tfaWSnAwAACAQAbmFtZSIAbWluZWNyYWZ0OmxpZ2h0X2dyYXlfc3RhaW5lZF9nbGFzcwQJAG5hbWVfaGFzaKKa+LrRsHQhAwoAbmV0d29ya19pZEv2giYKBgBzdGF0ZXMAAwcAdmVyc2lvbgEoFQEA" + }, + { + "id": "minecraft:gray_stained_glass", + "block_state_b64": "CgAAAwgAYmxvY2tfaWSmAwAACAQAbmFtZRwAbWluZWNyYWZ0OmdyYXlfc3RhaW5lZF9nbGFzcwQJAG5hbWVfaGFzaIETy7Y/HZREAwoAbmV0d29ya19pZDomVrUKBgBzdGF0ZXMAAwcAdmVyc2lvbgEoFQEA" + }, + { + "id": "minecraft:black_stained_glass", + "block_state_b64": "CgAAAwgAYmxvY2tfaWSuAwAACAQAbmFtZR0AbWluZWNyYWZ0OmJsYWNrX3N0YWluZWRfZ2xhc3MECQBuYW1lX2hhc2iV6BCwpfDMmwMKAG5ldHdvcmtfaWSV7doJCgYAc3RhdGVzAAMHAHZlcnNpb24BKBUBAA==" + }, + { + "id": "minecraft:brown_stained_glass", + "block_state_b64": "CgAAAwgAYmxvY2tfaWSrAwAACAQAbmFtZR0AbWluZWNyYWZ0OmJyb3duX3N0YWluZWRfZ2xhc3MECQBuYW1lX2hhc2igsEiq5np8JgMKAG5ldHdvcmtfaWRMzE/lCgYAc3RhdGVzAAMHAHZlcnNpb24BKBUBAA==" + }, + { + "id": "minecraft:red_stained_glass", + "block_state_b64": "CgAAAwgAYmxvY2tfaWStAwAACAQAbmFtZRsAbWluZWNyYWZ0OnJlZF9zdGFpbmVkX2dsYXNzBAkAbmFtZV9oYXNoCa2J12/lQoIDCgBuZXR3b3JrX2lk283lWAoGAHN0YXRlcwADBwB2ZXJzaW9uASgVAQA=" + }, + { + "id": "minecraft:orange_stained_glass", + "block_state_b64": "CgAAAwgAYmxvY2tfaWSgAwAACAQAbmFtZR4AbWluZWNyYWZ0Om9yYW5nZV9zdGFpbmVkX2dsYXNzBAkAbmFtZV9oYXNozgjAuvzhxGsDCgBuZXR3b3JrX2lkW5CkhQoGAHN0YXRlcwADBwB2ZXJzaW9uASgVAQA=" + }, + { + "id": "minecraft:yellow_stained_glass", + "block_state_b64": "CgAAAwgAYmxvY2tfaWSjAwAACAQAbmFtZR4AbWluZWNyYWZ0OnllbGxvd19zdGFpbmVkX2dsYXNzBAkAbmFtZV9oYXNo7EbHMd5WVugDCgBuZXR3b3JrX2lkkdDyXQoGAHN0YXRlcwADBwB2ZXJzaW9uASgVAQA=" + }, + { + "id": "minecraft:lime_stained_glass", + "block_state_b64": "CgAAAwgAYmxvY2tfaWSkAwAACAQAbmFtZRwAbWluZWNyYWZ0OmxpbWVfc3RhaW5lZF9nbGFzcwQJAG5hbWVfaGFzaBtZA1nZtwcFAwoAbmV0d29ya19pZDxX85UKBgBzdGF0ZXMAAwcAdmVyc2lvbgEoFQEA" + }, + { + "id": "minecraft:green_stained_glass", + "block_state_b64": "CgAAAwgAYmxvY2tfaWSsAwAACAQAbmFtZR0AbWluZWNyYWZ0OmdyZWVuX3N0YWluZWRfZ2xhc3MECQBuYW1lX2hhc2h91ptDgbehWwMKAG5ldHdvcmtfaWTlDhnECgYAc3RhdGVzAAMHAHZlcnNpb24BKBUBAA==" + }, + { + "id": "minecraft:cyan_stained_glass", + "block_state_b64": "CgAAAwgAYmxvY2tfaWSoAwAACAQAbmFtZRwAbWluZWNyYWZ0OmN5YW5fc3RhaW5lZF9nbGFzcwQJAG5hbWVfaGFzaBkIYQ8nQLqbAwoAbmV0d29ya19pZOL1lHsKBgBzdGF0ZXMAAwcAdmVyc2lvbgEoFQEA" + }, + { + "id": "minecraft:light_blue_stained_glass", + "block_state_b64": "CgAAAwgAYmxvY2tfaWSiAwAACAQAbmFtZSIAbWluZWNyYWZ0OmxpZ2h0X2JsdWVfc3RhaW5lZF9nbGFzcwQJAG5hbWVfaGFzaLt05n1G0fiSAwoAbmV0d29ya19pZNbwulIKBgBzdGF0ZXMAAwcAdmVyc2lvbgEoFQEA" + }, + { + "id": "minecraft:blue_stained_glass", + "block_state_b64": "CgAAAwgAYmxvY2tfaWSqAwAACAQAbmFtZRwAbWluZWNyYWZ0OmJsdWVfc3RhaW5lZF9nbGFzcwQJAG5hbWVfaGFzaPhLocSfzduRAwoAbmV0d29ya19pZENsjFwKBgBzdGF0ZXMAAwcAdmVyc2lvbgEoFQEA" + }, + { + "id": "minecraft:purple_stained_glass", + "block_state_b64": "CgAAAwgAYmxvY2tfaWSpAwAACAQAbmFtZR4AbWluZWNyYWZ0OnB1cnBsZV9zdGFpbmVkX2dsYXNzBAkAbmFtZV9oYXNoJk0DhRO0szUDCgBuZXR3b3JrX2lkD98ZxgoGAHN0YXRlcwADBwB2ZXJzaW9uASgVAQA=" + }, + { + "id": "minecraft:magenta_stained_glass", + "block_state_b64": "CgAAAwgAYmxvY2tfaWShAwAACAQAbmFtZR8AbWluZWNyYWZ0Om1hZ2VudGFfc3RhaW5lZF9nbGFzcwQJAG5hbWVfaGFzaFEDeFiJj3zSAwoAbmV0d29ya19pZG+iFRoKBgBzdGF0ZXMAAwcAdmVyc2lvbgEoFQEA" + }, + { + "id": "minecraft:pink_stained_glass", + "block_state_b64": "CgAAAwgAYmxvY2tfaWSlAwAACAQAbmFtZRwAbWluZWNyYWZ0OnBpbmtfc3RhaW5lZF9nbGFzcwQJAG5hbWVfaGFzaDijTX87ywxhAwoAbmV0d29ya19pZKdEricKBgBzdGF0ZXMAAwcAdmVyc2lvbgEoFQEA" + }, + { + "id": "minecraft:tinted_glass", + "block_state_b64": "CgAAAwgAYmxvY2tfaWRNAgAACAQAbmFtZRYAbWluZWNyYWZ0OnRpbnRlZF9nbGFzcwQJAG5hbWVfaGFzaAFZWSamk6KdAwoAbmV0d29ya19pZGSvWX8KBgBzdGF0ZXMAAwcAdmVyc2lvbgEoFQEA" + }, + { + "id": "minecraft:glass_pane", + "block_state_b64": "CgAAAwgAYmxvY2tfaWRmAAAACAQAbmFtZRQAbWluZWNyYWZ0OmdsYXNzX3BhbmUECQBuYW1lX2hhc2gRSBHwNMQ4gQMKAG5ldHdvcmtfaWRGwixuCgYAc3RhdGVzAAMHAHZlcnNpb24BKBUBAA==" + }, + { + "id": "minecraft:white_stained_glass_pane", + "block_state_b64": "CgAAAwgAYmxvY2tfaWSgAAAACAQAbmFtZSIAbWluZWNyYWZ0OndoaXRlX3N0YWluZWRfZ2xhc3NfcGFuZQQJAG5hbWVfaGFzaHgxQmgJVtRrAwoAbmV0d29ya19pZBEr/DYKBgBzdGF0ZXMAAwcAdmVyc2lvbgEoFQEA" + }, + { + "id": "minecraft:light_gray_stained_glass_pane", + "block_state_b64": "CgAAAwgAYmxvY2tfaWSJAwAACAQAbmFtZScAbWluZWNyYWZ0OmxpZ2h0X2dyYXlfc3RhaW5lZF9nbGFzc19wYW5lBAkAbmFtZV9oYXNon0aQw9lNkSEDCgBuZXR3b3JrX2lk9dp5VgoGAHN0YXRlcwADBwB2ZXJzaW9uASgVAQA=" + }, + { + "id": "minecraft:gray_stained_glass_pane", + "block_state_b64": "CgAAAwgAYmxvY2tfaWSIAwAACAQAbmFtZSEAbWluZWNyYWZ0OmdyYXlfc3RhaW5lZF9nbGFzc19wYW5lBAkAbmFtZV9oYXNors74IIw+2MMDCgBuZXR3b3JrX2lkmrGO5woGAHN0YXRlcwADBwB2ZXJzaW9uASgVAQA=" + }, + { + "id": "minecraft:black_stained_glass_pane", + "block_state_b64": "CgAAAwgAYmxvY2tfaWSQAwAACAQAbmFtZSIAbWluZWNyYWZ0OmJsYWNrX3N0YWluZWRfZ2xhc3NfcGFuZQQJAG5hbWVfaGFzaOK/5ZRRd+M1AwoAbmV0d29ya19pZDv++oQKBgBzdGF0ZXMAAwcAdmVyc2lvbgEoFQEA" + }, + { + "id": "minecraft:brown_stained_glass_pane", + "block_state_b64": "CgAAAwgAYmxvY2tfaWSNAwAACAQAbmFtZSIAbWluZWNyYWZ0OmJyb3duX3N0YWluZWRfZ2xhc3NfcGFuZQQJAG5hbWVfaGFzaLHeGJyRFTIWAwoAbmV0d29ya19pZMz9L0wKBgBzdGF0ZXMAAwcAdmVyc2lvbgEoFQEA" + }, + { + "id": "minecraft:red_stained_glass_pane", + "block_state_b64": "CgAAAwgAYmxvY2tfaWSPAwAACAQAbmFtZSAAbWluZWNyYWZ0OnJlZF9zdGFpbmVkX2dsYXNzX3BhbmUECQBuYW1lX2hhc2gGr4x6JheAywMKAG5ldHdvcmtfaWQBjCTmCgYAc3RhdGVzAAMHAHZlcnNpb24BKBUBAA==" + }, + { + "id": "minecraft:orange_stained_glass_pane", + "block_state_b64": "CgAAAwgAYmxvY2tfaWSCAwAACAQAbmFtZSMAbWluZWNyYWZ0Om9yYW5nZV9zdGFpbmVkX2dsYXNzX3BhbmUECQBuYW1lX2hhc2hbHxPD2gEbEAMKAG5ldHdvcmtfaWSt/7a5CgYAc3RhdGVzAAMHAHZlcnNpb24BKBUBAA==" + }, + { + "id": "minecraft:yellow_stained_glass_pane", + "block_state_b64": "CgAAAwgAYmxvY2tfaWSFAwAACAQAbmFtZSMAbWluZWNyYWZ0OnllbGxvd19zdGFpbmVkX2dsYXNzX3BhbmUECQBuYW1lX2hhc2g9tl4aOCyZBwMKAG5ldHdvcmtfaWTXRAS7CgYAc3RhdGVzAAMHAHZlcnNpb24BKBUBAA==" + }, + { + "id": "minecraft:lime_stained_glass_pane", + "block_state_b64": "CgAAAwgAYmxvY2tfaWSGAwAACAQAbmFtZSEAbWluZWNyYWZ0OmxpbWVfc3RhaW5lZF9nbGFzc19wYW5lBAkAbmFtZV9oYXNo3CtUyLwoGegDCgBuZXR3b3JrX2lkYJDnggoGAHN0YXRlcwADBwB2ZXJzaW9uASgVAQA=" + }, + { + "id": "minecraft:green_stained_glass_pane", + "block_state_b64": "CgAAAwgAYmxvY2tfaWSOAwAACAQAbmFtZSIAbWluZWNyYWZ0OmdyZWVuX3N0YWluZWRfZ2xhc3NfcGFuZQQJAG5hbWVfaGFzaJo6YP7IMy9SAwoAbmV0d29ya19pZHOnixoKBgBzdGF0ZXMAAwcAdmVyc2lvbgEoFQEA" + }, + { + "id": "minecraft:cyan_stained_glass_pane", + "block_state_b64": "CgAAAwgAYmxvY2tfaWSKAwAACAQAbmFtZSEAbWluZWNyYWZ0OmN5YW5fc3RhaW5lZF9nbGFzc19wYW5lBAkAbmFtZV9oYXNoti97c6QrbLQDCgBuZXR3b3JrX2lkUqFUeQoGAHN0YXRlcwADBwB2ZXJzaW9uASgVAQA=" + }, + { + "id": "minecraft:light_blue_stained_glass_pane", + "block_state_b64": "CgAAAwgAYmxvY2tfaWSEAwAACAQAbmFtZScAbWluZWNyYWZ0OmxpZ2h0X2JsdWVfc3RhaW5lZF9nbGFzc19wYW5lBAkAbmFtZV9oYXNovDg/gQle104DCgBuZXR3b3JrX2lkFuy4MQoGAHN0YXRlcwADBwB2ZXJzaW9uASgVAQA=" + }, + { + "id": "minecraft:blue_stained_glass_pane", + "block_state_b64": "CgAAAwgAYmxvY2tfaWSMAwAACAQAbmFtZSEAbWluZWNyYWZ0OmJsdWVfc3RhaW5lZF9nbGFzc19wYW5lBAkAbmFtZV9oYXNoGc57tiexbQMDCgBuZXR3b3JrX2lk1eBLUAoGAHN0YXRlcwADBwB2ZXJzaW9uASgVAQA=" + }, + { + "id": "minecraft:purple_stained_glass_pane", + "block_state_b64": "CgAAAwgAYmxvY2tfaWSLAwAACAQAbmFtZSMAbWluZWNyYWZ0OnB1cnBsZV9zdGFpbmVkX2dsYXNzX3BhbmUECQBuYW1lX2hhc2hDJHYdd0FdfQMKAG5ldHdvcmtfaWSNsdK5CgYAc3RhdGVzAAMHAHZlcnNpb24BKBUBAA==" + }, + { + "id": "minecraft:magenta_stained_glass_pane", + "block_state_b64": "CgAAAwgAYmxvY2tfaWSDAwAACAQAbmFtZSQAbWluZWNyYWZ0Om1hZ2VudGFfc3RhaW5lZF9nbGFzc19wYW5lBAkAbmFtZV9oYXNo3pcOw5bs5XoDCgBuZXR3b3JrX2lkVbOR7AoGAHN0YXRlcwADBwB2ZXJzaW9uASgVAQA=" + }, + { + "id": "minecraft:pink_stained_glass_pane", + "block_state_b64": "CgAAAwgAYmxvY2tfaWSHAwAACAQAbmFtZSEAbWluZWNyYWZ0OnBpbmtfc3RhaW5lZF9nbGFzc19wYW5lBAkAbmFtZV9oYXNoWRhSACMWgswDCgBuZXR3b3JrX2lkIR92xwoGAHN0YXRlcwADBwB2ZXJzaW9uASgVAQA=" + }, + { + "id": "minecraft:ladder", + "block_state_b64": "CgAAAwgAYmxvY2tfaWRBAAAACAQAbmFtZRAAbWluZWNyYWZ0OmxhZGRlcgQJAG5hbWVfaGFzaKBhqheJVOz+AwoAbmV0d29ya19pZCgvzlsKBgBzdGF0ZXMDEABmYWNpbmdfZGlyZWN0aW9uAAAAAAADBwB2ZXJzaW9uASgVAQA=" + }, + { + "id": "minecraft:scaffolding", + "block_state_b64": "CgAAAwgAYmxvY2tfaWSkAQAACAQAbmFtZRUAbWluZWNyYWZ0OnNjYWZmb2xkaW5nBAkAbmFtZV9oYXNoYrkevrqcljwDCgBuZXR3b3JrX2lkD13mlAoGAHN0YXRlcwMJAHN0YWJpbGl0eQAAAAABDwBzdGFiaWxpdHlfY2hlY2sAAAMHAHZlcnNpb24BKBUBAA==" + }, + { + "id": "minecraft:brick_block", + "block_state_b64": "CgAAAwgAYmxvY2tfaWQtAAAACAQAbmFtZRUAbWluZWNyYWZ0OmJyaWNrX2Jsb2NrBAkAbmFtZV9oYXNo5Qc2E005S3oDCgBuZXR3b3JrX2lkqeGWRgoGAHN0YXRlcwADBwB2ZXJzaW9uASgVAQA=" + }, + { + "id": "minecraft:smooth_stone_slab", + "block_state_b64": "CgAAAwgAYmxvY2tfaWQsAAAACAQAbmFtZRsAbWluZWNyYWZ0OnNtb290aF9zdG9uZV9zbGFiBAkAbmFtZV9oYXNon5I1yVw74uMDCgBuZXR3b3JrX2lkqvjcBQoGAHN0YXRlcwgXAG1pbmVjcmFmdDp2ZXJ0aWNhbF9oYWxmBgBib3R0b20AAwcAdmVyc2lvbgEoFQEA" + }, + { + "id": "minecraft:normal_stone_slab", + "block_state_b64": "CgAAAwgAYmxvY2tfaWSCBAAACAQAbmFtZRsAbWluZWNyYWZ0Om5vcm1hbF9zdG9uZV9zbGFiBAkAbmFtZV9oYXNoIvsjJLQdolcDCgBuZXR3b3JrX2lkC1zqRQoGAHN0YXRlcwgXAG1pbmVjcmFmdDp2ZXJ0aWNhbF9oYWxmBgBib3R0b20AAwcAdmVyc2lvbgEoFQEA" + }, + { + "id": "minecraft:cobblestone_slab", + "block_state_b64": "CgAAAwgAYmxvY2tfaWRoBAAACAQAbmFtZRoAbWluZWNyYWZ0OmNvYmJsZXN0b25lX3NsYWIECQBuYW1lX2hhc2h5CXtW7vlQVgMKAG5ldHdvcmtfaWRDGyj2CgYAc3RhdGVzCBcAbWluZWNyYWZ0OnZlcnRpY2FsX2hhbGYGAGJvdHRvbQADBwB2ZXJzaW9uASgVAQA=" + }, + { + "id": "minecraft:mossy_cobblestone_slab", + "block_state_b64": "CgAAAwgAYmxvY2tfaWR3BAAACAQAbmFtZSAAbWluZWNyYWZ0Om1vc3N5X2NvYmJsZXN0b25lX3NsYWIECQBuYW1lX2hhc2ijm1BCwB82VgMKAG5ldHdvcmtfaWR7ByMGCgYAc3RhdGVzCBcAbWluZWNyYWZ0OnZlcnRpY2FsX2hhbGYGAGJvdHRvbQADBwB2ZXJzaW9uASgVAQA=" + }, + { + "id": "minecraft:oak_slab", + "block_state_b64": "CgAAAwgAYmxvY2tfaWSeAAAACAQAbmFtZRIAbWluZWNyYWZ0Om9ha19zbGFiBAkAbmFtZV9oYXNoJp1Cp1M4jlwDCgBuZXR3b3JrX2lkZH6+owoGAHN0YXRlcwgXAG1pbmVjcmFmdDp2ZXJ0aWNhbF9oYWxmBgBib3R0b20AAwcAdmVyc2lvbgEoFQEA" + }, + { + "id": "minecraft:spruce_slab", + "block_state_b64": "CgAAAwgAYmxvY2tfaWQjBAAACAQAbmFtZRUAbWluZWNyYWZ0OnNwcnVjZV9zbGFiBAkAbmFtZV9oYXNodQi70jB238cDCgBuZXR3b3JrX2lkrriOYQoGAHN0YXRlcwgXAG1pbmVjcmFmdDp2ZXJ0aWNhbF9oYWxmBgBib3R0b20AAwcAdmVyc2lvbgEoFQEA" + }, + { + "id": "minecraft:birch_slab", + "block_state_b64": "CgAAAwgAYmxvY2tfaWQkBAAACAQAbmFtZRQAbWluZWNyYWZ0OmJpcmNoX3NsYWIECQBuYW1lX2hhc2gZPpfMxoOsTAMKAG5ldHdvcmtfaWThR9jyCgYAc3RhdGVzCBcAbWluZWNyYWZ0OnZlcnRpY2FsX2hhbGYGAGJvdHRvbQADBwB2ZXJzaW9uASgVAQA=" + }, + { + "id": "minecraft:jungle_slab", + "block_state_b64": "CgAAAwgAYmxvY2tfaWQlBAAACAQAbmFtZRUAbWluZWNyYWZ0Omp1bmdsZV9zbGFiBAkAbmFtZV9oYXNo6gLs79NXak4DCgBuZXR3b3JrX2lk5ZiKgwoGAHN0YXRlcwgXAG1pbmVjcmFmdDp2ZXJ0aWNhbF9oYWxmBgBib3R0b20AAwcAdmVyc2lvbgEoFQEA" + }, + { + "id": "minecraft:acacia_slab", + "block_state_b64": "CgAAAwgAYmxvY2tfaWQmBAAACAQAbmFtZRUAbWluZWNyYWZ0OmFjYWNpYV9zbGFiBAkAbmFtZV9oYXNomSdFmDnv4OUDCgBuZXR3b3JrX2lkHttaXAoGAHN0YXRlcwgXAG1pbmVjcmFmdDp2ZXJ0aWNhbF9oYWxmBgBib3R0b20AAwcAdmVyc2lvbgEoFQEA" + }, + { + "id": "minecraft:dark_oak_slab", + "block_state_b64": "CgAAAwgAYmxvY2tfaWQnBAAACAQAbmFtZRcAbWluZWNyYWZ0OmRhcmtfb2FrX3NsYWIECQBuYW1lX2hhc2hJjTohRFyhIQMKAG5ldHdvcmtfaWRMzDTyCgYAc3RhdGVzCBcAbWluZWNyYWZ0OnZlcnRpY2FsX2hhbGYGAGJvdHRvbQADBwB2ZXJzaW9uASgVAQA=" + }, + { + "id": "minecraft:mangrove_slab", + "block_state_b64": "CgAAAwgAYmxvY2tfaWToAgAACAQAbmFtZRcAbWluZWNyYWZ0Om1hbmdyb3ZlX3NsYWIECQBuYW1lX2hhc2jYCcmhJPeNMwMKAG5ldHdvcmtfaWQx6U1yCgYAc3RhdGVzCBcAbWluZWNyYWZ0OnZlcnRpY2FsX2hhbGYGAGJvdHRvbQADBwB2ZXJzaW9uASgVAQA=" + }, + { + "id": "minecraft:cherry_slab", + "block_state_b64": "CgAAAwgAYmxvY2tfaWQaAwAACAQAbmFtZRUAbWluZWNyYWZ0OmNoZXJyeV9zbGFiBAkAbmFtZV9oYXNoTt0MmVn/mqoDCgBuZXR3b3JrX2lk2VVsZQoGAHN0YXRlcwgXAG1pbmVjcmFmdDp2ZXJ0aWNhbF9oYWxmBgBib3R0b20AAwcAdmVyc2lvbgEoFQEA" + }, + { + "id": "minecraft:bamboo_slab", + "block_state_b64": "CgAAAwgAYmxvY2tfaWQAAwAACAQAbmFtZRUAbWluZWNyYWZ0OmJhbWJvb19zbGFiBAkAbmFtZV9oYXNoo1xuFqINeLYDCgBuZXR3b3JrX2lkVC+0twoGAHN0YXRlcwgXAG1pbmVjcmFmdDp2ZXJ0aWNhbF9oYWxmBgBib3R0b20AAwcAdmVyc2lvbgEoFQEA" + }, + { + "id": "minecraft:bamboo_mosaic_slab", + "block_state_b64": "CgAAAwgAYmxvY2tfaWQLAwAACAQAbmFtZRwAbWluZWNyYWZ0OmJhbWJvb19tb3NhaWNfc2xhYgQJAG5hbWVfaGFzaNbVRBZ/ChI3AwoAbmV0d29ya19pZOLZHFMKBgBzdGF0ZXMIFwBtaW5lY3JhZnQ6dmVydGljYWxfaGFsZgYAYm90dG9tAAMHAHZlcnNpb24BKBUBAA==" + }, + { + "id": "minecraft:stone_brick_slab", + "block_state_b64": "CgAAAwgAYmxvY2tfaWRqBAAACAQAbmFtZRoAbWluZWNyYWZ0OnN0b25lX2JyaWNrX3NsYWIECQBuYW1lX2hhc2js6EexuKuzrQMKAG5ldHdvcmtfaWRSsMxaCgYAc3RhdGVzCBcAbWluZWNyYWZ0OnZlcnRpY2FsX2hhbGYGAGJvdHRvbQADBwB2ZXJzaW9uASgVAQA=" + }, + { + "id": "minecraft:mossy_stone_brick_slab", + "block_state_b64": "CgAAAwgAYmxvY2tfaWSlAQAACAQAbmFtZSAAbWluZWNyYWZ0Om1vc3N5X3N0b25lX2JyaWNrX3NsYWIECQBuYW1lX2hhc2hiA4kFUl4tHAMKAG5ldHdvcmtfaWS6joSOCgYAc3RhdGVzCBcAbWluZWNyYWZ0OnZlcnRpY2FsX2hhbGYGAGJvdHRvbQADBwB2ZXJzaW9uASgVAQA=" + }, + { + "id": "minecraft:sandstone_slab", + "block_state_b64": "CgAAAwgAYmxvY2tfaWRnBAAACAQAbmFtZRgAbWluZWNyYWZ0OnNhbmRzdG9uZV9zbGFiBAkAbmFtZV9oYXNo/GMI0MZnrhsDCgBuZXR3b3JrX2lkFP8WmwoGAHN0YXRlcwgXAG1pbmVjcmFmdDp2ZXJ0aWNhbF9oYWxmBgBib3R0b20AAwcAdmVyc2lvbgEoFQEA" + }, + { + "id": "minecraft:cut_sandstone_slab", + "block_state_b64": "CgAAAwgAYmxvY2tfaWSDBAAACAQAbmFtZRwAbWluZWNyYWZ0OmN1dF9zYW5kc3RvbmVfc2xhYgQJAG5hbWVfaGFzaE+zxVQweViJAwoAbmV0d29ya19pZHsu74YKBgBzdGF0ZXMIFwBtaW5lY3JhZnQ6dmVydGljYWxfaGFsZgYAYm90dG9tAAMHAHZlcnNpb24BKBUBAA==" + }, + { + "id": "minecraft:smooth_sandstone_slab", + "block_state_b64": "CgAAAwgAYmxvY2tfaWR4BAAACAQAbmFtZR8AbWluZWNyYWZ0OnNtb290aF9zYW5kc3RvbmVfc2xhYgQJAG5hbWVfaGFzaIkmsO1gw3gnAwoAbmV0d29ya19pZFSiwP0KBgBzdGF0ZXMIFwBtaW5lY3JhZnQ6dmVydGljYWxfaGFsZgYAYm90dG9tAAMHAHZlcnNpb24BKBUBAA==" + }, + { + "id": "minecraft:red_sandstone_slab", + "block_state_b64": "CgAAAwgAYmxvY2tfaWS2AAAACAQAbmFtZRwAbWluZWNyYWZ0OnJlZF9zYW5kc3RvbmVfc2xhYgQJAG5hbWVfaGFzaEyDjeWlUHItAwoAbmV0d29ya19pZIT4rmwKBgBzdGF0ZXMIFwBtaW5lY3JhZnQ6dmVydGljYWxfaGFsZgYAYm90dG9tAAMHAHZlcnNpb24BKBUBAA==" + }, + { + "id": "minecraft:cut_red_sandstone_slab", + "block_state_b64": "CgAAAwgAYmxvY2tfaWSEBAAACAQAbmFtZSAAbWluZWNyYWZ0OmN1dF9yZWRfc2FuZHN0b25lX3NsYWIECQBuYW1lX2hhc2hTVRS++snU3wMKAG5ldHdvcmtfaWSvIAviCgYAc3RhdGVzCBcAbWluZWNyYWZ0OnZlcnRpY2FsX2hhbGYGAGJvdHRvbQADBwB2ZXJzaW9uASgVAQA=" + }, + { + "id": "minecraft:smooth_red_sandstone_slab", + "block_state_b64": "CgAAAwgAYmxvY2tfaWR6BAAACAQAbmFtZSMAbWluZWNyYWZ0OnNtb290aF9yZWRfc2FuZHN0b25lX3NsYWIECQBuYW1lX2hhc2i9iN2UK272tgMKAG5ldHdvcmtfaWRUZrwJCgYAc3RhdGVzCBcAbWluZWNyYWZ0OnZlcnRpY2FsX2hhbGYGAGJvdHRvbQADBwB2ZXJzaW9uASgVAQA=" + }, + { + "id": "minecraft:granite_slab", + "block_state_b64": "CgAAAwgAYmxvY2tfaWR/BAAACAQAbmFtZRYAbWluZWNyYWZ0OmdyYW5pdGVfc2xhYgQJAG5hbWVfaGFzaL0HprlAhhZwAwoAbmV0d29ya19pZIcIdc8KBgBzdGF0ZXMIFwBtaW5lY3JhZnQ6dmVydGljYWxfaGFsZgYAYm90dG9tAAMHAHZlcnNpb24BKBUBAA==" + }, + { + "id": "minecraft:polished_granite_slab", + "block_state_b64": "CgAAAwgAYmxvY2tfaWSABAAACAQAbmFtZR8AbWluZWNyYWZ0OnBvbGlzaGVkX2dyYW5pdGVfc2xhYgQJAG5hbWVfaGFzaP6bXk5w2dGrAwoAbmV0d29ya19pZCsRy1cKBgBzdGF0ZXMIFwBtaW5lY3JhZnQ6dmVydGljYWxfaGFsZgYAYm90dG9tAAMHAHZlcnNpb24BKBUBAA==" + }, + { + "id": "minecraft:diorite_slab", + "block_state_b64": "CgAAAwgAYmxvY2tfaWR9BAAACAQAbmFtZRYAbWluZWNyYWZ0OmRpb3JpdGVfc2xhYgQJAG5hbWVfaGFzaM3ppS8v55sNAwoAbmV0d29ya19pZB+Pv9oKBgBzdGF0ZXMIFwBtaW5lY3JhZnQ6dmVydGljYWxfaGFsZgYAYm90dG9tAAMHAHZlcnNpb24BKBUBAA==" + }, + { + "id": "minecraft:polished_diorite_slab", + "block_state_b64": "CgAAAwgAYmxvY2tfaWR+BAAACAQAbmFtZR8AbWluZWNyYWZ0OnBvbGlzaGVkX2Rpb3JpdGVfc2xhYgQJAG5hbWVfaGFzaLZlyJLkMPhyAwoAbmV0d29ya19pZFM0HYwKBgBzdGF0ZXMIFwBtaW5lY3JhZnQ6dmVydGljYWxfaGFsZgYAYm90dG9tAAMHAHZlcnNpb24BKBUBAA==" + }, + { + "id": "minecraft:andesite_slab", + "block_state_b64": "CgAAAwgAYmxvY2tfaWR8BAAACAQAbmFtZRcAbWluZWNyYWZ0OmFuZGVzaXRlX3NsYWIECQBuYW1lX2hhc2icIrtuy/aosAMKAG5ldHdvcmtfaWTtXTtYCgYAc3RhdGVzCBcAbWluZWNyYWZ0OnZlcnRpY2FsX2hhbGYGAGJvdHRvbQADBwB2ZXJzaW9uASgVAQA=" + }, + { + "id": "minecraft:polished_andesite_slab", + "block_state_b64": "CgAAAwgAYmxvY2tfaWR7BAAACAQAbmFtZSAAbWluZWNyYWZ0OnBvbGlzaGVkX2FuZGVzaXRlX3NsYWIECQBuYW1lX2hhc2j56zJOfCF+3wMKAG5ldHdvcmtfaWRBs69FCgYAc3RhdGVzCBcAbWluZWNyYWZ0OnZlcnRpY2FsX2hhbGYGAGJvdHRvbQADBwB2ZXJzaW9uASgVAQA=" + }, + { + "id": "minecraft:brick_slab", + "block_state_b64": "CgAAAwgAYmxvY2tfaWRpBAAACAQAbmFtZRQAbWluZWNyYWZ0OmJyaWNrX3NsYWIECQBuYW1lX2hhc2hO/Da4jU2v4wMKAG5ldHdvcmtfaWRG/qphCgYAc3RhdGVzCBcAbWluZWNyYWZ0OnZlcnRpY2FsX2hhbGYGAGJvdHRvbQADBwB2ZXJzaW9uASgVAQA=" + }, + { + "id": "minecraft:nether_brick_slab", + "block_state_b64": "CgAAAwgAYmxvY2tfaWRsBAAACAQAbmFtZRsAbWluZWNyYWZ0Om5ldGhlcl9icmlja19zbGFiBAkAbmFtZV9oYXNonymoa2zbbqMDCgBuZXR3b3JrX2lkquvR1QoGAHN0YXRlcwgXAG1pbmVjcmFmdDp2ZXJ0aWNhbF9oYWxmBgBib3R0b20AAwcAdmVyc2lvbgEoFQEA" + }, + { + "id": "minecraft:red_nether_brick_slab", + "block_state_b64": "CgAAAwgAYmxvY2tfaWR5BAAACAQAbmFtZR8AbWluZWNyYWZ0OnJlZF9uZXRoZXJfYnJpY2tfc2xhYgQJAG5hbWVfaGFzaG89ujUk3Y64AwoAbmV0d29ya19pZEZIunAKBgBzdGF0ZXMIFwBtaW5lY3JhZnQ6dmVydGljYWxfaGFsZgYAYm90dG9tAAMHAHZlcnNpb24BKBUBAA==" + }, + { + "id": "minecraft:end_stone_brick_slab", + "block_state_b64": "CgAAAwgAYmxvY2tfaWShAQAACAQAbmFtZR4AbWluZWNyYWZ0OmVuZF9zdG9uZV9icmlja19zbGFiBAkAbmFtZV9oYXNo4tkxQtl+IyQDCgBuZXR3b3JrX2lkhByH/woGAHN0YXRlcwgXAG1pbmVjcmFmdDp2ZXJ0aWNhbF9oYWxmBgBib3R0b20AAwcAdmVyc2lvbgEoFQEA" + }, + { + "id": "minecraft:quartz_slab", + "block_state_b64": "CgAAAwgAYmxvY2tfaWRrBAAACAQAbmFtZRUAbWluZWNyYWZ0OnF1YXJ0el9zbGFiBAkAbmFtZV9oYXNo9JMj3upfsbwDCgBuZXR3b3JrX2lkn2g2VAoGAHN0YXRlcwgXAG1pbmVjcmFmdDp2ZXJ0aWNhbF9oYWxmBgBib3R0b20AAwcAdmVyc2lvbgEoFQEA" + }, + { + "id": "minecraft:smooth_quartz_slab", + "block_state_b64": "CgAAAwgAYmxvY2tfaWSBBAAACAQAbmFtZRwAbWluZWNyYWZ0OnNtb290aF9xdWFydHpfc2xhYgQJAG5hbWVfaGFzaHOSJv8ve0nmAwoAbmV0d29ya19pZFMk/JsKBgBzdGF0ZXMIFwBtaW5lY3JhZnQ6dmVydGljYWxfaGFsZgYAYm90dG9tAAMHAHZlcnNpb24BKBUBAA==" + }, + { + "id": "minecraft:purpur_slab", + "block_state_b64": "CgAAAwgAYmxvY2tfaWRzBAAACAQAbmFtZRUAbWluZWNyYWZ0OnB1cnB1cl9zbGFiBAkAbmFtZV9oYXNo4XeWbKpx2ScDCgBuZXR3b3JrX2lkRkga5goGAHN0YXRlcwgXAG1pbmVjcmFmdDp2ZXJ0aWNhbF9oYWxmBgBib3R0b20AAwcAdmVyc2lvbgEoFQEA" + }, + { + "id": "minecraft:prismarine_slab", + "block_state_b64": "CgAAAwgAYmxvY2tfaWR0BAAACAQAbmFtZRkAbWluZWNyYWZ0OnByaXNtYXJpbmVfc2xhYgQJAG5hbWVfaGFzaI9x+1fY8QRfAwoAbmV0d29ya19pZBTUZhwKBgBzdGF0ZXMIFwBtaW5lY3JhZnQ6dmVydGljYWxfaGFsZgYAYm90dG9tAAMHAHZlcnNpb24BKBUBAA==" + }, + { + "id": "minecraft:dark_prismarine_slab", + "block_state_b64": "CgAAAwgAYmxvY2tfaWR1BAAACAQAbmFtZR4AbWluZWNyYWZ0OmRhcmtfcHJpc21hcmluZV9zbGFiBAkAbmFtZV9oYXNoSsZGDkEL5ZUDCgBuZXR3b3JrX2lkNLQ8VwoGAHN0YXRlcwgXAG1pbmVjcmFmdDp2ZXJ0aWNhbF9oYWxmBgBib3R0b20AAwcAdmVyc2lvbgEoFQEA" + }, + { + "id": "minecraft:prismarine_brick_slab", + "block_state_b64": "CgAAAwgAYmxvY2tfaWR2BAAACAQAbmFtZR8AbWluZWNyYWZ0OnByaXNtYXJpbmVfYnJpY2tfc2xhYgQJAG5hbWVfaGFzaB1FSbVi97xJAwoAbmV0d29ya19pZEBwwFMKBgBzdGF0ZXMIFwBtaW5lY3JhZnQ6dmVydGljYWxfaGFsZgYAYm90dG9tAAMHAHZlcnNpb24BKBUBAA==" + }, + { + "id": "minecraft:crimson_slab", + "block_state_b64": "CgAAAwgAYmxvY2tfaWQHAgAACAQAbmFtZRYAbWluZWNyYWZ0OmNyaW1zb25fc2xhYgQJAG5hbWVfaGFzaKZ+EfP0ZYOZAwoAbmV0d29ya19pZAxRUWAKBgBzdGF0ZXMIFwBtaW5lY3JhZnQ6dmVydGljYWxfaGFsZgYAYm90dG9tAAMHAHZlcnNpb24BKBUBAA==" + }, + { + "id": "minecraft:warped_slab", + "block_state_b64": "CgAAAwgAYmxvY2tfaWQIAgAACAQAbmFtZRUAbWluZWNyYWZ0OndhcnBlZF9zbGFiBAkAbmFtZV9oYXNo/AT0e/Z9W7UDCgBuZXR3b3JrX2lk1yq11AoGAHN0YXRlcwgXAG1pbmVjcmFmdDp2ZXJ0aWNhbF9oYWxmBgBib3R0b20AAwcAdmVyc2lvbgEoFQEA" + }, + { + "id": "minecraft:blackstone_slab", + "block_state_b64": "CgAAAwgAYmxvY2tfaWQZAgAACAQAbmFtZRkAbWluZWNyYWZ0OmJsYWNrc3RvbmVfc2xhYgQJAG5hbWVfaGFzaF/DD4ZUlNgtAwoAbmV0d29ya19pZGy1DjwKBgBzdGF0ZXMIFwBtaW5lY3JhZnQ6dmVydGljYWxfaGFsZgYAYm90dG9tAAMHAHZlcnNpb24BKBUBAA==" + }, + { + "id": "minecraft:polished_blackstone_slab", + "block_state_b64": "CgAAAwgAYmxvY2tfaWQkAgAACAQAbmFtZSIAbWluZWNyYWZ0OnBvbGlzaGVkX2JsYWNrc3RvbmVfc2xhYgQJAG5hbWVfaGFzaDYnuUs86EWfAwoAbmV0d29ya19pZJj2bXIKBgBzdGF0ZXMIFwBtaW5lY3JhZnQ6dmVydGljYWxfaGFsZgYAYm90dG9tAAMHAHZlcnNpb24BKBUBAA==" + }, + { + "id": "minecraft:polished_blackstone_brick_slab", + "block_state_b64": "CgAAAwgAYmxvY2tfaWQbAgAACAQAbmFtZSgAbWluZWNyYWZ0OnBvbGlzaGVkX2JsYWNrc3RvbmVfYnJpY2tfc2xhYgQJAG5hbWVfaGFzaKySLqvHc4xXAwoAbmV0d29ya19pZOyWX94KBgBzdGF0ZXMIFwBtaW5lY3JhZnQ6dmVydGljYWxfaGFsZgYAYm90dG9tAAMHAHZlcnNpb24BKBUBAA==" + }, + { + "id": "minecraft:cobbled_deepslate_slab", + "block_state_b64": "CgAAAwgAYmxvY2tfaWR7AgAACAQAbmFtZSAAbWluZWNyYWZ0OmNvYmJsZWRfZGVlcHNsYXRlX3NsYWIECQBuYW1lX2hhc2gwJIVWK1TM2QMKAG5ldHdvcmtfaWTYAoX5CgYAc3RhdGVzCBcAbWluZWNyYWZ0OnZlcnRpY2FsX2hhbGYGAGJvdHRvbQADBwB2ZXJzaW9uASgVAQA=" + }, + { + "id": "minecraft:polished_deepslate_slab", + "block_state_b64": "CgAAAwgAYmxvY2tfaWR/AgAACAQAbmFtZSEAbWluZWNyYWZ0OnBvbGlzaGVkX2RlZXBzbGF0ZV9zbGFiBAkAbmFtZV9oYXNoC/Adiz8k6RYDCgBuZXR3b3JrX2lkuFYMAAoGAHN0YXRlcwgXAG1pbmVjcmFmdDp2ZXJ0aWNhbF9oYWxmBgBib3R0b20AAwcAdmVyc2lvbgEoFQEA" + }, + { + "id": "minecraft:deepslate_tile_slab", + "block_state_b64": "CgAAAwgAYmxvY2tfaWSDAgAACAQAbmFtZR0AbWluZWNyYWZ0OmRlZXBzbGF0ZV90aWxlX3NsYWIECQBuYW1lX2hhc2hPydV6emzIXAMKAG5ldHdvcmtfaWQwlbFCCgYAc3RhdGVzCBcAbWluZWNyYWZ0OnZlcnRpY2FsX2hhbGYGAGJvdHRvbQADBwB2ZXJzaW9uASgVAQA=" + }, + { + "id": "minecraft:deepslate_brick_slab", + "block_state_b64": "CgAAAwgAYmxvY2tfaWSHAgAACAQAbmFtZR4AbWluZWNyYWZ0OmRlZXBzbGF0ZV9icmlja19zbGFiBAkAbmFtZV9oYXNoSv62V7iw10UDCgBuZXR3b3JrX2lkWMoragoGAHN0YXRlcwgXAG1pbmVjcmFmdDp2ZXJ0aWNhbF9oYWxmBgBib3R0b20AAwcAdmVyc2lvbgEoFQEA" + }, + { + "id": "minecraft:tuff_slab", + "block_state_b64": "CgAAAwgAYmxvY2tfaWTnAwAACAQAbmFtZRMAbWluZWNyYWZ0OnR1ZmZfc2xhYgQJAG5hbWVfaGFzaIhCGdlIsnMUAwoAbmV0d29ya19pZN1dUL4KBgBzdGF0ZXMIFwBtaW5lY3JhZnQ6dmVydGljYWxfaGFsZgYAYm90dG9tAAMHAHZlcnNpb24BKBUBAA==" + }, + { + "id": "minecraft:polished_tuff_slab", + "block_state_b64": "CgAAAwgAYmxvY2tfaWTsAwAACAQAbmFtZRwAbWluZWNyYWZ0OnBvbGlzaGVkX3R1ZmZfc2xhYgQJAG5hbWVfaGFzaLXdb48YvAsHAwoAbmV0d29ya19pZAnJ7W0KBgBzdGF0ZXMIFwBtaW5lY3JhZnQ6dmVydGljYWxfaGFsZgYAYm90dG9tAAMHAHZlcnNpb24BKBUBAA==" + }, + { + "id": "minecraft:tuff_brick_slab", + "block_state_b64": "CgAAAwgAYmxvY2tfaWTyAwAACAQAbmFtZRkAbWluZWNyYWZ0OnR1ZmZfYnJpY2tfc2xhYgQJAG5hbWVfaGFzaLqPMjVCv5dIAwoAbmV0d29ya19pZOmeRhcKBgBzdGF0ZXMIFwBtaW5lY3JhZnQ6dmVydGljYWxfaGFsZgYAYm90dG9tAAMHAHZlcnNpb24BKBUBAA==" + }, + { + "id": "minecraft:mud_brick_slab", + "block_state_b64": "CgAAAwgAYmxvY2tfaWTdAgAACAQAbmFtZRgAbWluZWNyYWZ0Om11ZF9icmlja19zbGFiBAkAbmFtZV9oYXNoq/tGBQWkv08DCgBuZXR3b3JrX2lkl4nnMwoGAHN0YXRlcwgXAG1pbmVjcmFmdDp2ZXJ0aWNhbF9oYWxmBgBib3R0b20AAwcAdmVyc2lvbgEoFQEA" + }, + { + "id": "minecraft:cut_copper_slab", + "block_state_b64": "CgAAAwgAYmxvY2tfaWRoAgAACAQAbmFtZRkAbWluZWNyYWZ0OmN1dF9jb3BwZXJfc2xhYgQJAG5hbWVfaGFzaDsNpb2qs4iBAwoAbmV0d29ya19pZOTm2nsKBgBzdGF0ZXMIFwBtaW5lY3JhZnQ6dmVydGljYWxfaGFsZgYAYm90dG9tAAMHAHZlcnNpb24BKBUBAA==" + }, + { + "id": "minecraft:exposed_cut_copper_slab", + "block_state_b64": "CgAAAwgAYmxvY2tfaWRpAgAACAQAbmFtZSEAbWluZWNyYWZ0OmV4cG9zZWRfY3V0X2NvcHBlcl9zbGFiBAkAbmFtZV9oYXNoahQ5OwIQb7kDCgBuZXR3b3JrX2lkrUlZLwoGAHN0YXRlcwgXAG1pbmVjcmFmdDp2ZXJ0aWNhbF9oYWxmBgBib3R0b20AAwcAdmVyc2lvbgEoFQEA" + }, + { + "id": "minecraft:weathered_cut_copper_slab", + "block_state_b64": "CgAAAwgAYmxvY2tfaWRqAgAACAQAbmFtZSMAbWluZWNyYWZ0OndlYXRoZXJlZF9jdXRfY29wcGVyX3NsYWIECQBuYW1lX2hhc2hBIuGIOVVXogMKAG5ldHdvcmtfaWQgnaDiCgYAc3RhdGVzCBcAbWluZWNyYWZ0OnZlcnRpY2FsX2hhbGYGAGJvdHRvbQADBwB2ZXJzaW9uASgVAQA=" + }, + { + "id": "minecraft:oxidized_cut_copper_slab", + "block_state_b64": "CgAAAwgAYmxvY2tfaWRrAgAACAQAbmFtZSIAbWluZWNyYWZ0Om94aWRpemVkX2N1dF9jb3BwZXJfc2xhYgQJAG5hbWVfaGFzaOptj9ycfpaDAwoAbmV0d29ya19pZMzFSRgKBgBzdGF0ZXMIFwBtaW5lY3JhZnQ6dmVydGljYWxfaGFsZgYAYm90dG9tAAMHAHZlcnNpb24BKBUBAA==" + }, + { + "id": "minecraft:waxed_cut_copper_slab", + "block_state_b64": "CgAAAwgAYmxvY2tfaWRsAgAACAQAbmFtZR8AbWluZWNyYWZ0OndheGVkX2N1dF9jb3BwZXJfc2xhYgQJAG5hbWVfaGFzaAlx6DZOCTHzAwoAbmV0d29ya19pZFRBvDAKBgBzdGF0ZXMIFwBtaW5lY3JhZnQ6dmVydGljYWxfaGFsZgYAYm90dG9tAAMHAHZlcnNpb24BKBUBAA==" + }, + { + "id": "minecraft:waxed_exposed_cut_copper_slab", + "block_state_b64": "CgAAAwgAYmxvY2tfaWRtAgAACAQAbmFtZScAbWluZWNyYWZ0OndheGVkX2V4cG9zZWRfY3V0X2NvcHBlcl9zbGFiBAkAbmFtZV9oYXNo3KqS5OnhtRIDCgBuZXR3b3JrX2lkHTGcTgoGAHN0YXRlcwgXAG1pbmVjcmFmdDp2ZXJ0aWNhbF9oYWxmBgBib3R0b20AAwcAdmVyc2lvbgEoFQEA" + }, + { + "id": "minecraft:waxed_weathered_cut_copper_slab", + "block_state_b64": "CgAAAwgAYmxvY2tfaWRuAgAACAQAbmFtZSkAbWluZWNyYWZ0OndheGVkX3dlYXRoZXJlZF9jdXRfY29wcGVyX3NsYWIECQBuYW1lX2hhc2gzZ1oX0HCFtwMKAG5ldHdvcmtfaWSgJR+XCgYAc3RhdGVzCBcAbWluZWNyYWZ0OnZlcnRpY2FsX2hhbGYGAGJvdHRvbQADBwB2ZXJzaW9uASgVAQA=" + }, + { + "id": "minecraft:waxed_oxidized_cut_copper_slab", + "block_state_b64": "CgAAAwgAYmxvY2tfaWTAAgAACAQAbmFtZSgAbWluZWNyYWZ0OndheGVkX294aWRpemVkX2N1dF9jb3BwZXJfc2xhYgQJAG5hbWVfaGFzaMjjTnLu1KcqAwoAbmV0d29ya19pZIxsnFYKBgBzdGF0ZXMIFwBtaW5lY3JhZnQ6dmVydGljYWxfaGFsZgYAYm90dG9tAAMHAHZlcnNpb24BKBUBAA==" + }, + { + "id": "minecraft:stone_bricks", + "block_state_b64": "CgAAAwgAYmxvY2tfaWRiAAAACAQAbmFtZRYAbWluZWNyYWZ0OnN0b25lX2JyaWNrcwQJAG5hbWVfaGFzaGAiQu8VWVJRAwoAbmV0d29ya19pZH2DjXUKBgBzdGF0ZXMAAwcAdmVyc2lvbgEoFQEA" + }, + { + "id": "minecraft:mossy_stone_bricks", + "block_state_b64": "CgAAAwgAYmxvY2tfaWRjBAAACAQAbmFtZRwAbWluZWNyYWZ0Om1vc3N5X3N0b25lX2JyaWNrcwQJAG5hbWVfaGFzaIZBO00MONRIAwoAbmV0d29ya19pZL2WDrAKBgBzdGF0ZXMAAwcAdmVyc2lvbgEoFQEA" + }, + { + "id": "minecraft:cracked_stone_bricks", + "block_state_b64": "CgAAAwgAYmxvY2tfaWRkBAAACAQAbmFtZR4AbWluZWNyYWZ0OmNyYWNrZWRfc3RvbmVfYnJpY2tzBAkAbmFtZV9oYXNocIkAp6riMz4DCgBuZXR3b3JrX2lkTWGeCwoGAHN0YXRlcwADBwB2ZXJzaW9uASgVAQA=" + }, + { + "id": "minecraft:chiseled_stone_bricks", + "block_state_b64": "CgAAAwgAYmxvY2tfaWRlBAAACAQAbmFtZR8AbWluZWNyYWZ0OmNoaXNlbGVkX3N0b25lX2JyaWNrcwQJAG5hbWVfaGFzaMB2FPLLADkEAwoAbmV0d29ya19pZOIPn0IKBgBzdGF0ZXMAAwcAdmVyc2lvbgEoFQEA" + }, + { + "id": "minecraft:smooth_stone", + "block_state_b64": "CgAAAwgAYmxvY2tfaWS2AQAACAQAbmFtZRYAbWluZWNyYWZ0OnNtb290aF9zdG9uZQQJAG5hbWVfaGFzaMwf87/JaTNvAwoAbmV0d29ya19pZLkZICEKBgBzdGF0ZXMAAwcAdmVyc2lvbgEoFQEA" + }, + { + "id": "minecraft:end_bricks", + "block_state_b64": "CgAAAwgAYmxvY2tfaWTOAAAACAQAbmFtZRQAbWluZWNyYWZ0OmVuZF9icmlja3MECQBuYW1lX2hhc2hIUFfxNLZaFgMKAG5ldHdvcmtfaWQ/vDihCgYAc3RhdGVzAAMHAHZlcnNpb24BKBUBAA==" + }, + { + "id": "minecraft:polished_blackstone_bricks", + "block_state_b64": "CgAAAwgAYmxvY2tfaWQRAgAACAQAbmFtZSQAbWluZWNyYWZ0OnBvbGlzaGVkX2JsYWNrc3RvbmVfYnJpY2tzBAkAbmFtZV9oYXNoIHgsgIdzKXcDCgBuZXR3b3JrX2lkUw9b3woGAHN0YXRlcwADBwB2ZXJzaW9uASgVAQA=" + }, + { + "id": "minecraft:cracked_polished_blackstone_bricks", + "block_state_b64": "CgAAAwgAYmxvY2tfaWQXAgAACAQAbmFtZSwAbWluZWNyYWZ0OmNyYWNrZWRfcG9saXNoZWRfYmxhY2tzdG9uZV9icmlja3MECQBuYW1lX2hhc2jQIO1GQDk80AMKAG5ldHdvcmtfaWQ3UlRYCgYAc3RhdGVzAAMHAHZlcnNpb24BKBUBAA==" + }, + { + "id": "minecraft:gilded_blackstone", + "block_state_b64": "CgAAAwgAYmxvY2tfaWQYAgAACAQAbmFtZRsAbWluZWNyYWZ0OmdpbGRlZF9ibGFja3N0b25lBAkAbmFtZV9oYXNoNoWt1ocG0HEDCgBuZXR3b3JrX2lktL8gUwoGAHN0YXRlcwADBwB2ZXJzaW9uASgVAQA=" + }, + { + "id": "minecraft:chiseled_polished_blackstone", + "block_state_b64": "CgAAAwgAYmxvY2tfaWQWAgAACAQAbmFtZSYAbWluZWNyYWZ0OmNoaXNlbGVkX3BvbGlzaGVkX2JsYWNrc3RvbmUECQBuYW1lX2hhc2gzFa+kEjCJgAMKAG5ldHdvcmtfaWR2NJX2CgYAc3RhdGVzAAMHAHZlcnNpb24BKBUBAA==" + }, + { + "id": "minecraft:deepslate_tiles", + "block_state_b64": "CgAAAwgAYmxvY2tfaWSCAgAACAQAbmFtZRkAbWluZWNyYWZ0OmRlZXBzbGF0ZV90aWxlcwQJAG5hbWVfaGFzaGcLLx3NXAFvAwoAbmV0d29ya19pZI/G/xYKBgBzdGF0ZXMAAwcAdmVyc2lvbgEoFQEA" + }, + { + "id": "minecraft:cracked_deepslate_tiles", + "block_state_b64": "CgAAAwgAYmxvY2tfaWSYAgAACAQAbmFtZSEAbWluZWNyYWZ0OmNyYWNrZWRfZGVlcHNsYXRlX3RpbGVzBAkAbmFtZV9oYXNo9zWgkFuMM1QDCgBuZXR3b3JrX2lkGwY6OgoGAHN0YXRlcwADBwB2ZXJzaW9uASgVAQA=" + }, + { + "id": "minecraft:deepslate_bricks", + "block_state_b64": "CgAAAwgAYmxvY2tfaWSGAgAACAQAbmFtZRoAbWluZWNyYWZ0OmRlZXBzbGF0ZV9icmlja3MECQBuYW1lX2hhc2gucvFmPdZxigMKAG5ldHdvcmtfaWSH4HDPCgYAc3RhdGVzAAMHAHZlcnNpb24BKBUBAA==" + }, + { + "id": "minecraft:tuff_bricks", + "block_state_b64": "CgAAAwgAYmxvY2tfaWTxAwAACAQAbmFtZRUAbWluZWNyYWZ0OnR1ZmZfYnJpY2tzBAkAbmFtZV9oYXNo/hbQ+mXSK7wDCgBuZXR3b3JrX2lk6gmIwQoGAHN0YXRlcwADBwB2ZXJzaW9uASgVAQA=" + }, + { + "id": "minecraft:cracked_deepslate_bricks", + "block_state_b64": "CgAAAwgAYmxvY2tfaWSZAgAACAQAbmFtZSIAbWluZWNyYWZ0OmNyYWNrZWRfZGVlcHNsYXRlX2JyaWNrcwQJAG5hbWVfaGFzaN40aqhh9WqHAwoAbmV0d29ya19pZO9GPBQKBgBzdGF0ZXMAAwcAdmVyc2lvbgEoFQEA" + }, + { + "id": "minecraft:chiseled_deepslate", + "block_state_b64": "CgAAAwgAYmxvY2tfaWSKAgAACAQAbmFtZRwAbWluZWNyYWZ0OmNoaXNlbGVkX2RlZXBzbGF0ZQQJAG5hbWVfaGFzaEU7/uRG8HSBAwoAbmV0d29ya19pZEqmI0EKBgBzdGF0ZXMAAwcAdmVyc2lvbgEoFQEA" + }, + { + "id": "minecraft:chiseled_tuff", + "block_state_b64": "CgAAAwgAYmxvY2tfaWTwAwAACAQAbmFtZRcAbWluZWNyYWZ0OmNoaXNlbGVkX3R1ZmYECQBuYW1lX2hhc2iVliOT8OTQ9AMKAG5ldHdvcmtfaWTLNKOiCgYAc3RhdGVzAAMHAHZlcnNpb24BKBUBAA==" + }, + { + "id": "minecraft:chiseled_tuff_bricks", + "block_state_b64": "CgAAAwgAYmxvY2tfaWT2AwAACAQAbmFtZR4AbWluZWNyYWZ0OmNoaXNlbGVkX3R1ZmZfYnJpY2tzBAkAbmFtZV9oYXNo3oQw6gmxYuADCgBuZXR3b3JrX2lkm3D8AgoGAHN0YXRlcwADBwB2ZXJzaW9uASgVAQA=" + }, + { + "id": "minecraft:cobblestone", + "block_state_b64": "CgAAAwgAYmxvY2tfaWQEAAAACAQAbmFtZRUAbWluZWNyYWZ0OmNvYmJsZXN0b25lBAkAbmFtZV9oYXNoPoK7mGlSUz4DCgBuZXR3b3JrX2lkLm7RZwoGAHN0YXRlcwADBwB2ZXJzaW9uASgVAQA=" + }, + { + "id": "minecraft:mossy_cobblestone", + "block_state_b64": "CgAAAwgAYmxvY2tfaWQwAAAACAQAbmFtZRsAbWluZWNyYWZ0Om1vc3N5X2NvYmJsZXN0b25lBAkAbmFtZV9oYXNoGJ67FCbkChMDCgBuZXR3b3JrX2lk/pYs1AoGAHN0YXRlcwADBwB2ZXJzaW9uASgVAQA=" + }, + { + "id": "minecraft:cobbled_deepslate", + "block_state_b64": "CgAAAwgAYmxvY2tfaWR6AgAACAQAbmFtZRsAbWluZWNyYWZ0OmNvYmJsZWRfZGVlcHNsYXRlBAkAbmFtZV9oYXNoLUz9Y/ywmLwDCgBuZXR3b3JrX2lkNwzZ+AoGAHN0YXRlcwADBwB2ZXJzaW9uASgVAQA=" + }, + { + "id": "minecraft:sandstone", + "block_state_b64": "CgAAAwgAYmxvY2tfaWQYAAAACAQAbmFtZRMAbWluZWNyYWZ0OnNhbmRzdG9uZQQJAG5hbWVfaGFzaFEmWsEHFI1AAwoAbmV0d29ya19pZPsXMaQKBgBzdGF0ZXMAAwcAdmVyc2lvbgEoFQEA" + }, + { + "id": "minecraft:chiseled_sandstone", + "block_state_b64": "CgAAAwgAYmxvY2tfaWSvBAAACAQAbmFtZRwAbWluZWNyYWZ0OmNoaXNlbGVkX3NhbmRzdG9uZQQJAG5hbWVfaGFzaPEkxMvZmemgAwoAbmV0d29ya19pZGI5NB4KBgBzdGF0ZXMAAwcAdmVyc2lvbgEoFQEA" + }, + { + "id": "minecraft:cut_sandstone", + "block_state_b64": "CgAAAwgAYmxvY2tfaWSwBAAACAQAbmFtZRcAbWluZWNyYWZ0OmN1dF9zYW5kc3RvbmUECQBuYW1lX2hhc2ichLQc71njnQMKAG5ldHdvcmtfaWSmBLkRCgYAc3RhdGVzAAMHAHZlcnNpb24BKBUBAA==" + }, + { + "id": "minecraft:smooth_sandstone", + "block_state_b64": "CgAAAwgAYmxvY2tfaWSxBAAACAQAbmFtZRoAbWluZWNyYWZ0OnNtb290aF9zYW5kc3RvbmUECQBuYW1lX2hhc2huR7XTwISyCAMKAG5ldHdvcmtfaWSzWj3UCgYAc3RhdGVzAAMHAHZlcnNpb24BKBUBAA==" + }, + { + "id": "minecraft:red_sandstone", + "block_state_b64": "CgAAAwgAYmxvY2tfaWSzAAAACAQAbmFtZRcAbWluZWNyYWZ0OnJlZF9zYW5kc3RvbmUECQBuYW1lX2hhc2jBO4Gv2v59uAMKAG5ldHdvcmtfaWRXRYxZCgYAc3RhdGVzAAMHAHZlcnNpb24BKBUBAA==" + }, + { + "id": "minecraft:chiseled_red_sandstone", + "block_state_b64": "CgAAAwgAYmxvY2tfaWS7BAAACAQAbmFtZSAAbWluZWNyYWZ0OmNoaXNlbGVkX3JlZF9zYW5kc3RvbmUECQBuYW1lX2hhc2gh5sX+ON054wMKAG5ldHdvcmtfaWT6Pw1PCgYAc3RhdGVzAAMHAHZlcnNpb24BKBUBAA==" + }, + { + "id": "minecraft:cut_red_sandstone", + "block_state_b64": "CgAAAwgAYmxvY2tfaWS8BAAACAQAbmFtZRsAbWluZWNyYWZ0OmN1dF9yZWRfc2FuZHN0b25lBAkAbmFtZV9oYXNoaOtka4NrQ1EDCgBuZXR3b3JrX2lk3r/JPAoGAHN0YXRlcwADBwB2ZXJzaW9uASgVAQA=" + }, + { + "id": "minecraft:smooth_red_sandstone", + "block_state_b64": "CgAAAwgAYmxvY2tfaWS9BAAACAQAbmFtZR4AbWluZWNyYWZ0OnNtb290aF9yZWRfc2FuZHN0b25lBAkAbmFtZV9oYXNoqsNl8x36ju4DCgBuZXR3b3JrX2lk7x5DTwoGAHN0YXRlcwADBwB2ZXJzaW9uASgVAQA=" + }, + { + "id": "minecraft:coal_block", + "block_state_b64": "CgAAAwgAYmxvY2tfaWStAAAACAQAbmFtZRQAbWluZWNyYWZ0OmNvYWxfYmxvY2sECQBuYW1lX2hhc2jH8QQP3t5PiAMKAG5ldHdvcmtfaWRo+sR+CgYAc3RhdGVzAAMHAHZlcnNpb24BKBUBAA==" + }, + { + "id": "minecraft:dried_kelp_block", + "block_state_b64": "CgAAAwgAYmxvY2tfaWSKAQAACAQAbmFtZRoAbWluZWNyYWZ0OmRyaWVkX2tlbHBfYmxvY2sECQBuYW1lX2hhc2iRoucexkrl8wMKAG5ldHdvcmtfaWQQCCrvCgYAc3RhdGVzAAMHAHZlcnNpb24BKBUBAA==" + }, + { + "id": "minecraft:copper_block", + "block_state_b64": "CgAAAwgAYmxvY2tfaWRTAgAACAQAbmFtZRYAbWluZWNyYWZ0OmNvcHBlcl9ibG9jawQJAG5hbWVfaGFzaDVxnehsGaZ1AwoAbmV0d29ya19pZIiUodwKBgBzdGF0ZXMAAwcAdmVyc2lvbgEoFQEA" + }, + { + "id": "minecraft:weathered_copper", + "block_state_b64": "CgAAAwgAYmxvY2tfaWRVAgAACAQAbmFtZRoAbWluZWNyYWZ0OndlYXRoZXJlZF9jb3BwZXIECQBuYW1lX2hhc2hJCQXbvobv+gMKAG5ldHdvcmtfaWQwM0lJCgYAc3RhdGVzAAMHAHZlcnNpb24BKBUBAA==" + }, + { + "id": "minecraft:exposed_copper", + "block_state_b64": "CgAAAwgAYmxvY2tfaWRUAgAACAQAbmFtZRgAbWluZWNyYWZ0OmV4cG9zZWRfY29wcGVyBAkAbmFtZV9oYXNoQH3Fukmu3CEDCgBuZXR3b3JrX2lk72jFIwoGAHN0YXRlcwADBwB2ZXJzaW9uASgVAQA=" + }, + { + "id": "minecraft:oxidized_copper", + "block_state_b64": "CgAAAwgAYmxvY2tfaWRWAgAACAQAbmFtZRkAbWluZWNyYWZ0Om94aWRpemVkX2NvcHBlcgQJAG5hbWVfaGFzaMDtJqR0G5Y7AwoAbmV0d29ya19pZGjN8bUKBgBzdGF0ZXMAAwcAdmVyc2lvbgEoFQEA" + }, + { + "id": "minecraft:waxed_copper", + "block_state_b64": "CgAAAwgAYmxvY2tfaWRXAgAACAQAbmFtZRYAbWluZWNyYWZ0OndheGVkX2NvcHBlcgQJAG5hbWVfaGFzaPF+FG6Eh5fsAwoAbmV0d29ya19pZIjtz/0KBgBzdGF0ZXMAAwcAdmVyc2lvbgEoFQEA" + }, + { + "id": "minecraft:waxed_exposed_copper", + "block_state_b64": "CgAAAwgAYmxvY2tfaWRYAgAACAQAbmFtZR4AbWluZWNyYWZ0OndheGVkX2V4cG9zZWRfY29wcGVyBAkAbmFtZV9oYXNoig8IOc+SCikDCgBuZXR3b3JrX2lklz8yWQoGAHN0YXRlcwADBwB2ZXJzaW9uASgVAQA=" + }, + { + "id": "minecraft:waxed_weathered_copper", + "block_state_b64": "CgAAAwgAYmxvY2tfaWRZAgAACAQAbmFtZSAAbWluZWNyYWZ0OndheGVkX3dlYXRoZXJlZF9jb3BwZXIECQBuYW1lX2hhc2gjtPq8MOdvKgMKAG5ldHdvcmtfaWSQ9Ln9CgYAc3RhdGVzAAMHAHZlcnNpb24BKBUBAA==" + }, + { + "id": "minecraft:waxed_oxidized_copper", + "block_state_b64": "CgAAAwgAYmxvY2tfaWS9AgAACAQAbmFtZR8AbWluZWNyYWZ0OndheGVkX294aWRpemVkX2NvcHBlcgQJAG5hbWVfaGFzaMaORhsO+LzjAwoAbmV0d29ya19pZJhGfLEKBgBzdGF0ZXMAAwcAdmVyc2lvbgEoFQEA" + }, + { + "id": "minecraft:copper_grate", + "block_state_b64": "CgAAAwgAYmxvY2tfaWT/AwAACAQAbmFtZRYAbWluZWNyYWZ0OmNvcHBlcl9ncmF0ZQQJAG5hbWVfaGFzaC/JEFOWnmEcAwoAbmV0d29ya19pZC6YiiMKBgBzdGF0ZXMAAwcAdmVyc2lvbgEoFQEA" + }, + { + "id": "minecraft:exposed_copper_grate", + "block_state_b64": "CgAAAwgAYmxvY2tfaWQABAAACAQAbmFtZR4AbWluZWNyYWZ0OmV4cG9zZWRfY29wcGVyX2dyYXRlBAkAbmFtZV9oYXNolFIBYLYU0IcDCgBuZXR3b3JrX2lk4UqptAoGAHN0YXRlcwADBwB2ZXJzaW9uASgVAQA=" + }, + { + "id": "minecraft:weathered_copper_grate", + "block_state_b64": "CgAAAwgAYmxvY2tfaWQBBAAACAQAbmFtZSAAbWluZWNyYWZ0OndlYXRoZXJlZF9jb3BwZXJfZ3JhdGUECQBuYW1lX2hhc2jB3o8enlv1RgMKAG5ldHdvcmtfaWRih2pOCgYAc3RhdGVzAAMHAHZlcnNpb24BKBUBAA==" + }, + { + "id": "minecraft:oxidized_copper_grate", + "block_state_b64": "CgAAAwgAYmxvY2tfaWQCBAAACAQAbmFtZR8AbWluZWNyYWZ0Om94aWRpemVkX2NvcHBlcl9ncmF0ZQQJAG5hbWVfaGFzaBRfNhyndve7AwoAbmV0d29ya19pZKY2cnEKBgBzdGF0ZXMAAwcAdmVyc2lvbgEoFQEA" + }, + { + "id": "minecraft:waxed_copper_grate", + "block_state_b64": "CgAAAwgAYmxvY2tfaWQDBAAACAQAbmFtZRwAbWluZWNyYWZ0OndheGVkX2NvcHBlcl9ncmF0ZQQJAG5hbWVfaGFzaDmC92M2RO+HAwoAbmV0d29ya19pZH4og2AKBgBzdGF0ZXMAAwcAdmVyc2lvbgEoFQEA" + }, + { + "id": "minecraft:waxed_exposed_copper_grate", + "block_state_b64": "CgAAAwgAYmxvY2tfaWQEBAAACAQAbmFtZSQAbWluZWNyYWZ0OndheGVkX2V4cG9zZWRfY29wcGVyX2dyYXRlBAkAbmFtZV9oYXNoWmd6B+hWwiEDCgBuZXR3b3JrX2lk8d4ZQwoGAHN0YXRlcwADBwB2ZXJzaW9uASgVAQA=" + }, + { + "id": "minecraft:waxed_weathered_copper_grate", + "block_state_b64": "CgAAAwgAYmxvY2tfaWQFBAAACAQAbmFtZSYAbWluZWNyYWZ0OndheGVkX3dlYXRoZXJlZF9jb3BwZXJfZ3JhdGUECQBuYW1lX2hhc2hXfilVFDAiYQMKAG5ldHdvcmtfaWQqTGC1CgYAc3RhdGVzAAMHAHZlcnNpb24BKBUBAA==" + }, + { + "id": "minecraft:waxed_oxidized_copper_grate", + "block_state_b64": "CgAAAwgAYmxvY2tfaWQGBAAACAQAbmFtZSUAbWluZWNyYWZ0OndheGVkX294aWRpemVkX2NvcHBlcl9ncmF0ZQQJAG5hbWVfaGFzaEbeMT605GP4AwoAbmV0d29ya19pZOZjpkkKBgBzdGF0ZXMAAwcAdmVyc2lvbgEoFQEA" + }, + { + "id": "minecraft:cut_copper", + "block_state_b64": "CgAAAwgAYmxvY2tfaWRaAgAACAQAbmFtZRQAbWluZWNyYWZ0OmN1dF9jb3BwZXIECQBuYW1lX2hhc2hAfN3NGax3eAMKAG5ldHdvcmtfaWTnFBtYCgYAc3RhdGVzAAMHAHZlcnNpb24BKBUBAA==" + }, + { + "id": "minecraft:exposed_cut_copper", + "block_state_b64": "CgAAAwgAYmxvY2tfaWRbAgAACAQAbmFtZRwAbWluZWNyYWZ0OmV4cG9zZWRfY3V0X2NvcHBlcgQJAG5hbWVfaGFzaA85G3yv/w6pAwoAbmV0d29ya19pZMQhr0QKBgBzdGF0ZXMAAwcAdmVyc2lvbgEoFQEA" + }, + { + "id": "minecraft:weathered_cut_copper", + "block_state_b64": "CgAAAwgAYmxvY2tfaWRcAgAACAQAbmFtZR4AbWluZWNyYWZ0OndlYXRoZXJlZF9jdXRfY29wcGVyBAkAbmFtZV9oYXNoVgRV0fBaz88DCgBuZXR3b3JrX2lk/0cYugoGAHN0YXRlcwADBwB2ZXJzaW9uASgVAQA=" + }, + { + "id": "minecraft:oxidized_cut_copper", + "block_state_b64": "CgAAAwgAYmxvY2tfaWRdAgAACAQAbmFtZR0AbWluZWNyYWZ0Om94aWRpemVkX2N1dF9jb3BwZXIECQBuYW1lX2hhc2iP8WmFWOkriwMKAG5ldHdvcmtfaWQPdce7CgYAc3RhdGVzAAMHAHZlcnNpb24BKBUBAA==" + }, + { + "id": "minecraft:waxed_cut_copper", + "block_state_b64": "CgAAAwgAYmxvY2tfaWReAgAACAQAbmFtZRoAbWluZWNyYWZ0OndheGVkX2N1dF9jb3BwZXIECQBuYW1lX2hhc2jumiwOZIqv2AMKAG5ldHdvcmtfaWQvuxx9CgYAc3RhdGVzAAMHAHZlcnNpb24BKBUBAA==" + }, + { + "id": "minecraft:waxed_exposed_cut_copper", + "block_state_b64": "CgAAAwgAYmxvY2tfaWRfAgAACAQAbmFtZSIAbWluZWNyYWZ0OndheGVkX2V4cG9zZWRfY3V0X2NvcHBlcgQJAG5hbWVfaGFzaPE/OfK6IoVMAwoAbmV0d29ya19pZHy5HkcKBgBzdGF0ZXMAAwcAdmVyc2lvbgEoFQEA" + }, + { + "id": "minecraft:waxed_weathered_cut_copper", + "block_state_b64": "CgAAAwgAYmxvY2tfaWRgAgAACAQAbmFtZSQAbWluZWNyYWZ0OndheGVkX3dlYXRoZXJlZF9jdXRfY29wcGVyBAkAbmFtZV9oYXNoCA1xDp11bnwDCgBuZXR3b3JrX2lkDyEDVQoGAHN0YXRlcwADBwB2ZXJzaW9uASgVAQA=" + }, + { + "id": "minecraft:waxed_oxidized_cut_copper", + "block_state_b64": "CgAAAwgAYmxvY2tfaWS+AgAACAQAbmFtZSMAbWluZWNyYWZ0OndheGVkX294aWRpemVkX2N1dF9jb3BwZXIECQBuYW1lX2hhc2i1pZAsZYHLDAMKAG5ldHdvcmtfaWQ/wSkCCgYAc3RhdGVzAAMHAHZlcnNpb24BKBUBAA==" + }, + { + "id": "minecraft:chiseled_copper", + "block_state_b64": "CgAAAwgAYmxvY2tfaWT3AwAACAQAbmFtZRkAbWluZWNyYWZ0OmNoaXNlbGVkX2NvcHBlcgQJAG5hbWVfaGFzaIsW5pmpJEuQAwoAbmV0d29ya19pZHetwrkKBgBzdGF0ZXMAAwcAdmVyc2lvbgEoFQEA" + }, + { + "id": "minecraft:exposed_chiseled_copper", + "block_state_b64": "CgAAAwgAYmxvY2tfaWT4AwAACAQAbmFtZSEAbWluZWNyYWZ0OmV4cG9zZWRfY2hpc2VsZWRfY29wcGVyBAkAbmFtZV9oYXNoOvrLJ0UowbgDCgBuZXR3b3JrX2lkZj7cPwoGAHN0YXRlcwADBwB2ZXJzaW9uASgVAQA=" + }, + { + "id": "minecraft:weathered_chiseled_copper", + "block_state_b64": "CgAAAwgAYmxvY2tfaWT5AwAACAQAbmFtZSMAbWluZWNyYWZ0OndlYXRoZXJlZF9jaGlzZWxlZF9jb3BwZXIECQBuYW1lX2hhc2hh+42XlsWvGAMKAG5ldHdvcmtfaWS7Cy59CgYAc3RhdGVzAAMHAHZlcnNpb24BKBUBAA==" + }, + { + "id": "minecraft:oxidized_chiseled_copper", + "block_state_b64": "CgAAAwgAYmxvY2tfaWT6AwAACAQAbmFtZSIAbWluZWNyYWZ0Om94aWRpemVkX2NoaXNlbGVkX2NvcHBlcgQJAG5hbWVfaGFzaLpTIsnfluiCAwoAbmV0d29ya19pZB9/jS8KBgBzdGF0ZXMAAwcAdmVyc2lvbgEoFQEA" + }, + { + "id": "minecraft:waxed_chiseled_copper", + "block_state_b64": "CgAAAwgAYmxvY2tfaWT7AwAACAQAbmFtZR8AbWluZWNyYWZ0OndheGVkX2NoaXNlbGVkX2NvcHBlcgQJAG5hbWVfaGFzaFnXvXY5OinzAwoAbmV0d29ya19pZAcKtHsKBgBzdGF0ZXMAAwcAdmVyc2lvbgEoFQEA" + }, + { + "id": "minecraft:waxed_exposed_chiseled_copper", + "block_state_b64": "CgAAAwgAYmxvY2tfaWT8AwAACAQAbmFtZScAbWluZWNyYWZ0OndheGVkX2V4cG9zZWRfY2hpc2VsZWRfY29wcGVyBAkAbmFtZV9oYXNoHJdq+Pph6hMDCgBuZXR3b3JrX2lkdge7IAoGAHN0YXRlcwADBwB2ZXJzaW9uASgVAQA=" + }, + { + "id": "minecraft:waxed_oxidized_chiseled_copper", + "block_state_b64": "CgAAAwgAYmxvY2tfaWT9AwAACAQAbmFtZSgAbWluZWNyYWZ0OndheGVkX294aWRpemVkX2NoaXNlbGVkX2NvcHBlcgQJAG5hbWVfaGFzaMj49OvlTpgCAwoAbmV0d29ya19pZN/r+roKBgBzdGF0ZXMAAwcAdmVyc2lvbgEoFQEA" + }, + { + "id": "minecraft:waxed_weathered_chiseled_copper", + "block_state_b64": "CgAAAwgAYmxvY2tfaWT+AwAACAQAbmFtZSkAbWluZWNyYWZ0OndheGVkX3dlYXRoZXJlZF9jaGlzZWxlZF9jb3BwZXIECQBuYW1lX2hhc2hzuO+Sg9LYQwMKAG5ldHdvcmtfaWQ7AN7iCgYAc3RhdGVzAAMHAHZlcnNpb24BKBUBAA==" + }, + { + "id": "minecraft:copper_bulb", + "block_state_b64": "CgAAAwgAYmxvY2tfaWQHBAAACAQAbmFtZRUAbWluZWNyYWZ0OmNvcHBlcl9idWxiBAkAbmFtZV9oYXNo41TimHOsMWcDCgBuZXR3b3JrX2lkJnZvAgoGAHN0YXRlcwEDAGxpdAABCwBwb3dlcmVkX2JpdAAAAwcAdmVyc2lvbgEoFQEA" + }, + { + "id": "minecraft:exposed_copper_bulb", + "block_state_b64": "CgAAAwgAYmxvY2tfaWQIBAAACAQAbmFtZR0AbWluZWNyYWZ0OmV4cG9zZWRfY29wcGVyX2J1bGIECQBuYW1lX2hhc2g++f1wYLLCrAMKAG5ldHdvcmtfaWRLdMmGCgYAc3RhdGVzAQMAbGl0AAELAHBvd2VyZWRfYml0AAADBwB2ZXJzaW9uASgVAQA=" + }, + { + "id": "minecraft:weathered_copper_bulb", + "block_state_b64": "CgAAAwgAYmxvY2tfaWQJBAAACAQAbmFtZR8AbWluZWNyYWZ0OndlYXRoZXJlZF9jb3BwZXJfYnVsYgQJAG5hbWVfaGFzaMEtsYfwRTXlAwoAbmV0d29ya19pZAp51LQKBgBzdGF0ZXMBAwBsaXQAAQsAcG93ZXJlZF9iaXQAAAMHAHZlcnNpb24BKBUBAA==" + }, + { + "id": "minecraft:oxidized_copper_bulb", + "block_state_b64": "CgAAAwgAYmxvY2tfaWQKBAAACAQAbmFtZR4AbWluZWNyYWZ0Om94aWRpemVkX2NvcHBlcl9idWxiBAkAbmFtZV9oYXNovnrBQZs8nDIDCgBuZXR3b3JrX2lkPsj0AAoGAHN0YXRlcwEDAGxpdAABCwBwb3dlcmVkX2JpdAAAAwcAdmVyc2lvbgEoFQEA" + }, + { + "id": "minecraft:waxed_copper_bulb", + "block_state_b64": "CgAAAwgAYmxvY2tfaWQLBAAACAQAbmFtZRsAbWluZWNyYWZ0OndheGVkX2NvcHBlcl9idWxiBAkAbmFtZV9oYXNoGTg6TYllMiIDCgBuZXR3b3JrX2lk9m0WhgoGAHN0YXRlcwEDAGxpdAABCwBwb3dlcmVkX2JpdAAAAwcAdmVyc2lvbgEoFQEA" + }, + { + "id": "minecraft:waxed_exposed_copper_bulb", + "block_state_b64": "CgAAAwgAYmxvY2tfaWQMBAAACAQAbmFtZSMAbWluZWNyYWZ0OndheGVkX2V4cG9zZWRfY29wcGVyX2J1bGIECQBuYW1lX2hhc2gI6xkPcvBDVwMKAG5ldHdvcmtfaWR7BRcACgYAc3RhdGVzAQMAbGl0AAELAHBvd2VyZWRfYml0AAADBwB2ZXJzaW9uASgVAQA=" + }, + { + "id": "minecraft:waxed_weathered_copper_bulb", + "block_state_b64": "CgAAAwgAYmxvY2tfaWQNBAAACAQAbmFtZSUAbWluZWNyYWZ0OndheGVkX3dlYXRoZXJlZF9jb3BwZXJfYnVsYgQJAG5hbWVfaGFzaMsUnmp3/VqVAwoAbmV0d29ya19pZEoworoKBgBzdGF0ZXMBAwBsaXQAAQsAcG93ZXJlZF9iaXQAAAMHAHZlcnNpb24BKBUBAA==" + }, + { + "id": "minecraft:waxed_oxidized_copper_bulb", + "block_state_b64": "CgAAAwgAYmxvY2tfaWQOBAAACAQAbmFtZSQAbWluZWNyYWZ0OndheGVkX294aWRpemVkX2NvcHBlcl9idWxiBAkAbmFtZV9oYXNoBFKxY3fjVq4DCgBuZXR3b3JrX2lkzrJ6aAoGAHN0YXRlcwEDAGxpdAABCwBwb3dlcmVkX2JpdAAAAwcAdmVyc2lvbgEoFQEA" + }, + { + "id": "minecraft:iron_block", + "block_state_b64": "CgAAAwgAYmxvY2tfaWQqAAAACAQAbmFtZRQAbWluZWNyYWZ0Omlyb25fYmxvY2sECQBuYW1lX2hhc2jYINmJQbvV/gMKAG5ldHdvcmtfaWRf7AbICgYAc3RhdGVzAAMHAHZlcnNpb24BKBUBAA==" + }, + { + "id": "minecraft:gold_block", + "block_state_b64": "CgAAAwgAYmxvY2tfaWQpAAAACAQAbmFtZRQAbWluZWNyYWZ0OmdvbGRfYmxvY2sECQBuYW1lX2hhc2iYLshvjtXzFwMKAG5ldHdvcmtfaWTDJGBcCgYAc3RhdGVzAAMHAHZlcnNpb24BKBUBAA==" + }, + { + "id": "minecraft:emerald_block", + "block_state_b64": "CgAAAwgAYmxvY2tfaWSFAAAACAQAbmFtZRcAbWluZWNyYWZ0OmVtZXJhbGRfYmxvY2sECQBuYW1lX2hhc2hK6QunqJznNAMKAG5ldHdvcmtfaWRk5+otCgYAc3RhdGVzAAMHAHZlcnNpb24BKBUBAA==" + }, + { + "id": "minecraft:diamond_block", + "block_state_b64": "CgAAAwgAYmxvY2tfaWQ5AAAACAQAbmFtZRcAbWluZWNyYWZ0OmRpYW1vbmRfYmxvY2sECQBuYW1lX2hhc2iGKrxuvkytFQMKAG5ldHdvcmtfaWQQeQZXCgYAc3RhdGVzAAMHAHZlcnNpb24BKBUBAA==" + }, + { + "id": "minecraft:lapis_block", + "block_state_b64": "CgAAAwgAYmxvY2tfaWQWAAAACAQAbmFtZRUAbWluZWNyYWZ0OmxhcGlzX2Jsb2NrBAkAbmFtZV9oYXNoDZ44xdb2zVoDCgBuZXR3b3JrX2lktVy0BAoGAHN0YXRlcwADBwB2ZXJzaW9uASgVAQA=" + }, + { + "id": "minecraft:raw_copper_block", + "block_state_b64": "CgAAAwgAYmxvY2tfaWTDAgAACAQAbmFtZRoAbWluZWNyYWZ0OnJhd19jb3BwZXJfYmxvY2sECQBuYW1lX2hhc2hw1KG0TNUGgwMKAG5ldHdvcmtfaWS1vGo/CgYAc3RhdGVzAAMHAHZlcnNpb24BKBUBAA==" + }, + { + "id": "minecraft:raw_iron_block", + "block_state_b64": "CgAAAwgAYmxvY2tfaWTCAgAACAQAbmFtZRgAbWluZWNyYWZ0OnJhd19pcm9uX2Jsb2NrBAkAbmFtZV9oYXNo9XyzNIQXxvwDCgBuZXR3b3JrX2lknms1QAoGAHN0YXRlcwADBwB2ZXJzaW9uASgVAQA=" + }, + { + "id": "minecraft:raw_gold_block", + "block_state_b64": "CgAAAwgAYmxvY2tfaWTEAgAACAQAbmFtZRgAbWluZWNyYWZ0OnJhd19nb2xkX2Jsb2NrBAkAbmFtZV9oYXNo6YuwuLwfOBwDCgBuZXR3b3JrX2lkLiQ5gQoGAHN0YXRlcwADBwB2ZXJzaW9uASgVAQA=" + }, + { + "id": "minecraft:quartz_block", + "block_state_b64": "CgAAAwgAYmxvY2tfaWSbAAAACAQAbmFtZRYAbWluZWNyYWZ0OnF1YXJ0el9ibG9jawQJAG5hbWVfaGFzaCfpbqyIIvZCAwoAbmV0d29ya19pZE2axGsKBgBzdGF0ZXMICwBwaWxsYXJfYXhpcwEAeQADBwB2ZXJzaW9uASgVAQA=" + }, + { + "id": "minecraft:quartz_bricks", + "block_state_b64": "CgAAAwgAYmxvY2tfaWQvAgAACAQAbmFtZRcAbWluZWNyYWZ0OnF1YXJ0el9icmlja3MECQBuYW1lX2hhc2jSZO590dd8sAMKAG5ldHdvcmtfaWSc5xCLCgYAc3RhdGVzAAMHAHZlcnNpb24BKBUBAA==" + }, + { + "id": "minecraft:quartz_pillar", + "block_state_b64": "CgAAAwgAYmxvY2tfaWS5BAAACAQAbmFtZRcAbWluZWNyYWZ0OnF1YXJ0el9waWxsYXIECQBuYW1lX2hhc2igp62HI+PuSwMKAG5ldHdvcmtfaWS9SGXLCgYAc3RhdGVzCAsAcGlsbGFyX2F4aXMBAHkAAwcAdmVyc2lvbgEoFQEA" + }, + { + "id": "minecraft:chiseled_quartz_block", + "block_state_b64": "CgAAAwgAYmxvY2tfaWS4BAAACAQAbmFtZR8AbWluZWNyYWZ0OmNoaXNlbGVkX3F1YXJ0el9ibG9jawQJAG5hbWVfaGFzaAftJM9mCAvaAwoAbmV0d29ya19pZFwy0s0KBgBzdGF0ZXMICwBwaWxsYXJfYXhpcwEAeQADBwB2ZXJzaW9uASgVAQA=" + }, + { + "id": "minecraft:smooth_quartz", + "block_state_b64": "CgAAAwgAYmxvY2tfaWS6BAAACAQAbmFtZRcAbWluZWNyYWZ0OnNtb290aF9xdWFydHoECQBuYW1lX2hhc2hIVzzgiItGagMKAG5ldHdvcmtfaWTVWgU2CgYAc3RhdGVzCAsAcGlsbGFyX2F4aXMBAHkAAwcAdmVyc2lvbgEoFQEA" + }, + { + "id": "minecraft:prismarine", + "block_state_b64": "CgAAAwgAYmxvY2tfaWSoAAAACAQAbmFtZRQAbWluZWNyYWZ0OnByaXNtYXJpbmUECQBuYW1lX2hhc2jcnQCHi9CspQMKAG5ldHdvcmtfaWQnuuW1CgYAc3RhdGVzAAMHAHZlcnNpb24BKBUBAA==" + }, + { + "id": "minecraft:prismarine_bricks", + "block_state_b64": "CgAAAwgAYmxvY2tfaWSzBAAACAQAbmFtZRsAbWluZWNyYWZ0OnByaXNtYXJpbmVfYnJpY2tzBAkAbmFtZV9oYXNozeGe3/7s5fcDCgBuZXR3b3JrX2lkj/iBnAoGAHN0YXRlcwADBwB2ZXJzaW9uASgVAQA=" + }, + { + "id": "minecraft:dark_prismarine", + "block_state_b64": "CgAAAwgAYmxvY2tfaWSyBAAACAQAbmFtZRkAbWluZWNyYWZ0OmRhcmtfcHJpc21hcmluZQQJAG5hbWVfaGFzaK+rhxsgkzplAwoAbmV0d29ya19pZIdA0I0KBgBzdGF0ZXMAAwcAdmVyc2lvbgEoFQEA" + }, + { + "id": "minecraft:slime", + "block_state_b64": "CgAAAwgAYmxvY2tfaWSlAAAACAQAbmFtZQ8AbWluZWNyYWZ0OnNsaW1lBAkAbmFtZV9oYXNoHJiEEJx+JlkDCgBuZXR3b3JrX2lkfgfVzAoGAHN0YXRlcwADBwB2ZXJzaW9uASgVAQA=" + }, + { + "id": "minecraft:honey_block", + "block_state_b64": "CgAAAwgAYmxvY2tfaWTbAQAACAQAbmFtZRUAbWluZWNyYWZ0OmhvbmV5X2Jsb2NrBAkAbmFtZV9oYXNo9zLYSUlelywDCgBuZXR3b3JrX2lko+dyWgoGAHN0YXRlcwADBwB2ZXJzaW9uASgVAQA=" + }, + { + "id": "minecraft:honeycomb_block", + "block_state_b64": "CgAAAwgAYmxvY2tfaWTcAQAACAQAbmFtZRkAbWluZWNyYWZ0OmhvbmV5Y29tYl9ibG9jawQJAG5hbWVfaGFzaASIPuOCYd1oAwoAbmV0d29ya19pZKys4n4KBgBzdGF0ZXMAAwcAdmVyc2lvbgEoFQEA" + }, + { + "id": "minecraft:hay_block", + "block_state_b64": "CgAAAwgAYmxvY2tfaWSqAAAACAQAbmFtZRMAbWluZWNyYWZ0OmhheV9ibG9jawQJAG5hbWVfaGFzaIB2VxKxX8EpAwoAbmV0d29ya19pZKuQSloKBgBzdGF0ZXMDCgBkZXByZWNhdGVkAAAAAAgLAHBpbGxhcl9heGlzAQB5AAMHAHZlcnNpb24BKBUBAA==" + }, + { + "id": "minecraft:bone_block", + "block_state_b64": "CgAAAwgAYmxvY2tfaWTYAAAACAQAbmFtZRQAbWluZWNyYWZ0OmJvbmVfYmxvY2sECQBuYW1lX2hhc2i4ZX576W9AWgMKAG5ldHdvcmtfaWTWGacQCgYAc3RhdGVzAwoAZGVwcmVjYXRlZAAAAAAICwBwaWxsYXJfYXhpcwEAeQADBwB2ZXJzaW9uASgVAQA=" + }, + { + "id": "minecraft:nether_brick", + "block_state_b64": "CgAAAwgAYmxvY2tfaWRwAAAACAQAbmFtZRYAbWluZWNyYWZ0Om5ldGhlcl9icmljawQJAG5hbWVfaGFzaMxcRiheU+nXAwoAbmV0d29ya19pZMkmzloKBgBzdGF0ZXMAAwcAdmVyc2lvbgEoFQEA" + }, + { + "id": "minecraft:red_nether_brick", + "block_state_b64": "CgAAAwgAYmxvY2tfaWTXAAAACAQAbmFtZRoAbWluZWNyYWZ0OnJlZF9uZXRoZXJfYnJpY2sECQBuYW1lX2hhc2j8pRO4LfoECAMKAG5ldHdvcmtfaWRpdF0YCgYAc3RhdGVzAAMHAHZlcnNpb24BKBUBAA==" + }, + { + "id": "minecraft:chiseled_nether_bricks", + "block_state_b64": "CgAAAwgAYmxvY2tfaWQtAgAACAQAbmFtZSAAbWluZWNyYWZ0OmNoaXNlbGVkX25ldGhlcl9icmlja3MECQBuYW1lX2hhc2g31SBPTcUK1QMKAG5ldHdvcmtfaWS8TJ+TCgYAc3RhdGVzAAMHAHZlcnNpb24BKBUBAA==" + }, + { + "id": "minecraft:cracked_nether_bricks", + "block_state_b64": "CgAAAwgAYmxvY2tfaWQuAgAACAQAbmFtZR8AbWluZWNyYWZ0OmNyYWNrZWRfbmV0aGVyX2JyaWNrcwQJAG5hbWVfaGFzaAdC6eKzXT5tAwoAbmV0d29ya19pZIUSejwKBgBzdGF0ZXMAAwcAdmVyc2lvbgEoFQEA" + }, + { + "id": "minecraft:netherite_block", + "block_state_b64": "CgAAAwgAYmxvY2tfaWQNAgAACAQAbmFtZRkAbWluZWNyYWZ0Om5ldGhlcml0ZV9ibG9jawQJAG5hbWVfaGFzaMghh6Zib/ZKAwoAbmV0d29ya19pZIz0mq0KBgBzdGF0ZXMAAwcAdmVyc2lvbgEoFQEA" + }, + { + "id": "minecraft:lodestone", + "block_state_b64": "CgAAAwgAYmxvY2tfaWTdAQAACAQAbmFtZRMAbWluZWNyYWZ0OmxvZGVzdG9uZQQJAG5hbWVfaGFzaJ2gmHOTlXv8AwoAbmV0d29ya19pZEfgB4wKBgBzdGF0ZXMAAwcAdmVyc2lvbgEoFQEA" + }, + { + "id": "minecraft:white_wool", + "block_state_b64": "CgAAAwgAYmxvY2tfaWQjAAAACAQAbmFtZRQAbWluZWNyYWZ0OndoaXRlX3dvb2wECQBuYW1lX2hhc2jRWB7vaIEDiQMKAG5ldHdvcmtfaWSO8paQCgYAc3RhdGVzAAMHAHZlcnNpb24BKBUBAA==" + }, + { + "id": "minecraft:light_gray_wool", + "block_state_b64": "CgAAAwgAYmxvY2tfaWQnAwAACAQAbmFtZRkAbWluZWNyYWZ0OmxpZ2h0X2dyYXlfd29vbAQJAG5hbWVfaGFzaOpdQ1a2v4b3AwoAbmV0d29ya19pZIqZCYEKBgBzdGF0ZXMAAwcAdmVyc2lvbgEoFQEA" + }, + { + "id": "minecraft:gray_wool", + "block_state_b64": "CgAAAwgAYmxvY2tfaWQoAwAACAQAbmFtZRMAbWluZWNyYWZ0OmdyYXlfd29vbAQJAG5hbWVfaGFzaLsc1Lp1xdIOAwoAbmV0d29ya19pZFUs+HgKBgBzdGF0ZXMAAwcAdmVyc2lvbgEoFQEA" + }, + { + "id": "minecraft:black_wool", + "block_state_b64": "CgAAAwgAYmxvY2tfaWQpAwAACAQAbmFtZRQAbWluZWNyYWZ0OmJsYWNrX3dvb2wECQBuYW1lX2hhc2hP2HC6o0X4HAMKAG5ldHdvcmtfaWRUbORcCgYAc3RhdGVzAAMHAHZlcnNpb24BKBUBAA==" + }, + { + "id": "minecraft:brown_wool", + "block_state_b64": "CgAAAwgAYmxvY2tfaWQqAwAACAQAbmFtZRQAbWluZWNyYWZ0OmJyb3duX3dvb2wECQBuYW1lX2hhc2ig5IW89PrREwMKAG5ldHdvcmtfaWRjT9j8CgYAc3RhdGVzAAMHAHZlcnNpb24BKBUBAA==" + }, + { + "id": "minecraft:red_wool", + "block_state_b64": "CgAAAwgAYmxvY2tfaWQrAwAACAQAbmFtZRIAbWluZWNyYWZ0OnJlZF93b29sBAkAbmFtZV9oYXNoY4TBDq+mFgUDCgBuZXR3b3JrX2lktn9lcAoGAHN0YXRlcwADBwB2ZXJzaW9uASgVAQA=" + }, + { + "id": "minecraft:orange_wool", + "block_state_b64": "CgAAAwgAYmxvY2tfaWQsAwAACAQAbmFtZRUAbWluZWNyYWZ0Om9yYW5nZV93b29sBAkAbmFtZV9oYXNoFstfrTZfSCgDCgBuZXR3b3JrX2lk+rqywwoGAHN0YXRlcwADBwB2ZXJzaW9uASgVAQA=" + }, + { + "id": "minecraft:yellow_wool", + "block_state_b64": "CgAAAwgAYmxvY2tfaWQtAwAACAQAbmFtZRUAbWluZWNyYWZ0OnllbGxvd193b29sBAkAbmFtZV9oYXNoTFyus2RHegcDCgBuZXR3b3JrX2lkkKBhXAoGAHN0YXRlcwADBwB2ZXJzaW9uASgVAQA=" + }, + { + "id": "minecraft:lime_wool", + "block_state_b64": "CgAAAwgAYmxvY2tfaWQuAwAACAQAbmFtZRMAbWluZWNyYWZ0OmxpbWVfd29vbAQJAG5hbWVfaGFzaNVnnzKiMxmeAwoAbmV0d29ya19pZG9b32kKBgBzdGF0ZXMAAwcAdmVyc2lvbgEoFQEA" + }, + { + "id": "minecraft:green_wool", + "block_state_b64": "CgAAAwgAYmxvY2tfaWQvAwAACAQAbmFtZRQAbWluZWNyYWZ0OmdyZWVuX3dvb2wECQBuYW1lX2hhc2i3mElRYHIcSQMKAG5ldHdvcmtfaWSssprwCgYAc3RhdGVzAAMHAHZlcnNpb24BKBUBAA==" + }, + { + "id": "minecraft:cyan_wool", + "block_state_b64": "CgAAAwgAYmxvY2tfaWQwAwAACAQAbmFtZRMAbWluZWNyYWZ0OmN5YW5fd29vbAQJAG5hbWVfaGFzaBNDfvHn8dqFAwoAbmV0d29ya19pZK0hAbgKBgBzdGF0ZXMAAwcAdmVyc2lvbgEoFQEA" + }, + { + "id": "minecraft:light_blue_wool", + "block_state_b64": "CgAAAwgAYmxvY2tfaWQxAwAACAQAbmFtZRkAbWluZWNyYWZ0OmxpZ2h0X2JsdWVfd29vbAQJAG5hbWVfaGFzaLWFAUfyxFPNAwoAbmV0d29ya19pZL2oEugKBgBzdGF0ZXMAAwcAdmVyc2lvbgEoFQEA" + }, + { + "id": "minecraft:blue_wool", + "block_state_b64": "CgAAAwgAYmxvY2tfaWQyAwAACAQAbmFtZRMAbWluZWNyYWZ0OmJsdWVfd29vbAQJAG5hbWVfaGFzaLjHyxxbTWCLAwoAbmV0d29ya19pZPaLdFQKBgBzdGF0ZXMAAwcAdmVyc2lvbgEoFQEA" + }, + { + "id": "minecraft:purple_wool", + "block_state_b64": "CgAAAwgAYmxvY2tfaWQzAwAACAQAbmFtZRUAbWluZWNyYWZ0OnB1cnBsZV93b29sBAkAbmFtZV9oYXNojvFtqzjAf/4DCgBuZXR3b3JrX2lklqASNQoGAHN0YXRlcwADBwB2ZXJzaW9uASgVAQA=" + }, + { + "id": "minecraft:magenta_wool", + "block_state_b64": "CgAAAwgAYmxvY2tfaWQ0AwAACAQAbmFtZRYAbWluZWNyYWZ0Om1hZ2VudGFfd29vbAQJAG5hbWVfaGFzaGuOHvf+Pd4yAwoAbmV0d29ya19pZI4UoDQKBgBzdGF0ZXMAAwcAdmVyc2lvbgEoFQEA" + }, + { + "id": "minecraft:pink_wool", + "block_state_b64": "CgAAAwgAYmxvY2tfaWQ1AwAACAQAbmFtZRMAbWluZWNyYWZ0OnBpbmtfd29vbAQJAG5hbWVfaGFzaPiVA2pFeoFLAwoAbmV0d29ya19pZOZRO6oKBgBzdGF0ZXMAAwcAdmVyc2lvbgEoFQEA" + }, + { + "id": "minecraft:white_carpet", + "block_state_b64": "CgAAAwgAYmxvY2tfaWSrAAAACAQAbmFtZRYAbWluZWNyYWZ0OndoaXRlX2NhcnBldAQJAG5hbWVfaGFzaNeMHTI1fWPXAwoAbmV0d29ya19pZEahDFcKBgBzdGF0ZXMAAwcAdmVyc2lvbgEoFQEA" + }, + { + "id": "minecraft:light_gray_carpet", + "block_state_b64": "CgAAAwgAYmxvY2tfaWRbAwAACAQAbmFtZRsAbWluZWNyYWZ0OmxpZ2h0X2dyYXlfY2FycGV0BAkAbmFtZV9oYXNoHPw6ArBAsP0DCgBuZXR3b3JrX2lkQoAeUAoGAHN0YXRlcwADBwB2ZXJzaW9uASgVAQA=" + }, + { + "id": "minecraft:gray_carpet", + "block_state_b64": "CgAAAwgAYmxvY2tfaWRaAwAACAQAbmFtZRUAbWluZWNyYWZ0OmdyYXlfY2FycGV0BAkAbmFtZV9oYXNoZVR0OI+1VRADCgBuZXR3b3JrX2lkETF4WwoGAHN0YXRlcwADBwB2ZXJzaW9uASgVAQA=" + }, + { + "id": "minecraft:black_carpet", + "block_state_b64": "CgAAAwgAYmxvY2tfaWRiAwAACAQAbmFtZRYAbWluZWNyYWZ0OmJsYWNrX2NhcnBldAQJAG5hbWVfaGFzaOk7LP9NptyhAwoAbmV0d29ya19pZFjmXtIKBgBzdGF0ZXMAAwcAdmVyc2lvbgEoFQEA" + }, + { + "id": "minecraft:brown_carpet", + "block_state_b64": "CgAAAwgAYmxvY2tfaWRfAwAACAQAbmFtZRYAbWluZWNyYWZ0OmJyb3duX2NhcnBldAQJAG5hbWVfaGFzaNaXFyOsAvIvAwoAbmV0d29ya19pZHPjFuoKBgBzdGF0ZXMAAwcAdmVyc2lvbgEoFQEA" + }, + { + "id": "minecraft:red_carpet", + "block_state_b64": "CgAAAwgAYmxvY2tfaWRhAwAACAQAbmFtZRQAbWluZWNyYWZ0OnJlZF9jYXJwZXQECQBuYW1lX2hhc2i9eSKBf6SO3wMKAG5ldHdvcmtfaWQuhI/KCgYAc3RhdGVzAAMHAHZlcnNpb24BKBUBAA==" + }, + { + "id": "minecraft:orange_carpet", + "block_state_b64": "CgAAAwgAYmxvY2tfaWRUAwAACAQAbmFtZRcAbWluZWNyYWZ0Om9yYW5nZV9jYXJwZXQECQBuYW1lX2hhc2hIUkO4HlAdygMKAG5ldHdvcmtfaWSyKV9OCgYAc3RhdGVzAAMHAHZlcnNpb24BKBUBAA==" + }, + { + "id": "minecraft:yellow_carpet", + "block_state_b64": "CgAAAwgAYmxvY2tfaWRXAwAACAQAbmFtZRcAbWluZWNyYWZ0OnllbGxvd19jYXJwZXQECQBuYW1lX2hhc2hSDKX3scCamwMKAG5ldHdvcmtfaWT8nq+ECgYAc3RhdGVzAAMHAHZlcnNpb24BKBUBAA==" + }, + { + "id": "minecraft:lime_carpet", + "block_state_b64": "CgAAAwgAYmxvY2tfaWRYAwAACAQAbmFtZRUAbWluZWNyYWZ0OmxpbWVfY2FycGV0BAkAbmFtZV9oYXNo+6KFOpzsib4DCgBuZXR3b3JrX2lkT+DS4woGAHN0YXRlcwADBwB2ZXJzaW9uASgVAQA=" + }, + { + "id": "minecraft:green_carpet", + "block_state_b64": "CgAAAwgAYmxvY2tfaWRgAwAACAQAbmFtZRYAbWluZWNyYWZ0OmdyZWVuX2NhcnBldAQJAG5hbWVfaGFzaCHPMP9ltqFJAwoAbmV0d29ya19pZBgwAvAKBgBzdGF0ZXMAAwcAdmVyc2lvbgEoFQEA" + }, + { + "id": "minecraft:cyan_carpet", + "block_state_b64": "CgAAAwgAYmxvY2tfaWRcAwAACAQAbmFtZRUAbWluZWNyYWZ0OmN5YW5fY2FycGV0BAkAbmFtZV9oYXNobXf62dQBJj8DCgBuZXR3b3JrX2lkKVppLgoGAHN0YXRlcwADBwB2ZXJzaW9uASgVAQA=" + }, + { + "id": "minecraft:light_blue_carpet", + "block_state_b64": "CgAAAwgAYmxvY2tfaWRWAwAACAQAbmFtZRsAbWluZWNyYWZ0OmxpZ2h0X2JsdWVfY2FycGV0BAkAbmFtZV9oYXNo20l4oktdZ3sDCgBuZXR3b3JrX2lkjdeMiwoGAHN0YXRlcwADBwB2ZXJzaW9uASgVAQA=" + }, + { + "id": "minecraft:blue_carpet", + "block_state_b64": "CgAAAwgAYmxvY2tfaWReAwAACAQAbmFtZRUAbWluZWNyYWZ0OmJsdWVfY2FycGV0BAkAbmFtZV9oYXNo3p3lsW0eQwsDCgBuZXR3b3JrX2lkAovdPQoGAHN0YXRlcwADBwB2ZXJzaW9uASgVAQA=" + }, + { + "id": "minecraft:purple_carpet", + "block_state_b64": "CgAAAwgAYmxvY2tfaWRdAwAACAQAbmFtZRcAbWluZWNyYWZ0OnB1cnBsZV9jYXJwZXQECQBuYW1lX2hhc2jwIA9pW/qp7QMKAG5ldHdvcmtfaWTqJqhjCgYAc3RhdGVzAAMHAHZlcnNpb24BKBUBAA==" + }, + { + "id": "minecraft:magenta_carpet", + "block_state_b64": "CgAAAwgAYmxvY2tfaWRVAwAACAQAbmFtZRgAbWluZWNyYWZ0Om1hZ2VudGFfY2FycGV0BAkAbmFtZV9oYXNoFXT36YNNZhMDCgBuZXR3b3JrX2lk+tqsGAoGAHN0YXRlcwADBwB2ZXJzaW9uASgVAQA=" + }, + { + "id": "minecraft:pink_carpet", + "block_state_b64": "CgAAAwgAYmxvY2tfaWRZAwAACAQAbmFtZRUAbWluZWNyYWZ0OnBpbmtfY2FycGV0BAkAbmFtZV9oYXNoHll72oqk+OoDCgBuZXR3b3JrX2lkrnBYDwoGAHN0YXRlcwADBwB2ZXJzaW9uASgVAQA=" + }, + { + "id": "minecraft:white_concrete_powder", + "block_state_b64": "CgAAAwgAYmxvY2tfaWTtAAAACAQAbmFtZR8AbWluZWNyYWZ0OndoaXRlX2NvbmNyZXRlX3Bvd2RlcgQJAG5hbWVfaGFzaFUk9iXVjwV8AwoAbmV0d29ya19pZJPZY8AKBgBzdGF0ZXMAAwcAdmVyc2lvbgEoFQEA" + }, + { + "id": "minecraft:light_gray_concrete_powder", + "block_state_b64": "CgAAAwgAYmxvY2tfaWTLAwAACAQAbmFtZSQAbWluZWNyYWZ0OmxpZ2h0X2dyYXlfY29uY3JldGVfcG93ZGVyBAkAbmFtZV9oYXNo7EUk30hmUtYDCgBuZXR3b3JrX2lkh8jVIwoGAHN0YXRlcwADBwB2ZXJzaW9uASgVAQA=" + }, + { + "id": "minecraft:gray_concrete_powder", + "block_state_b64": "CgAAAwgAYmxvY2tfaWTKAwAACAQAbmFtZR4AbWluZWNyYWZ0OmdyYXlfY29uY3JldGVfcG93ZGVyBAkAbmFtZV9oYXNoW77af6WihdwDCgBuZXR3b3JrX2lkSsqC1woGAHN0YXRlcwADBwB2ZXJzaW9uASgVAQA=" + }, + { + "id": "minecraft:black_concrete_powder", + "block_state_b64": "CgAAAwgAYmxvY2tfaWTSAwAACAQAbmFtZR8AbWluZWNyYWZ0OmJsYWNrX2NvbmNyZXRlX3Bvd2RlcgQJAG5hbWVfaGFzaAfWYp0xtgcfAwoAbmV0d29ya19pZMWTC8EKBgBzdGF0ZXMAAwcAdmVyc2lvbgEoFQEA" + }, + { + "id": "minecraft:brown_concrete_powder", + "block_state_b64": "CgAAAwgAYmxvY2tfaWTPAwAACAQAbmFtZR8AbWluZWNyYWZ0OmJyb3duX2NvbmNyZXRlX3Bvd2RlcgQJAG5hbWVfaGFzaB74EeiLO46XAwoAbmV0d29ya19pZEDHKqwKBgBzdGF0ZXMAAwcAdmVyc2lvbgEoFQEA" + }, + { + "id": "minecraft:red_concrete_powder", + "block_state_b64": "CgAAAwgAYmxvY2tfaWTRAwAACAQAbmFtZR0AbWluZWNyYWZ0OnJlZF9jb25jcmV0ZV9wb3dkZXIECQBuYW1lX2hhc2gjFut6Z/VH1gMKAG5ldHdvcmtfaWSvcmwYCgYAc3RhdGVzAAMHAHZlcnNpb24BKBUBAA==" + }, + { + "id": "minecraft:orange_concrete_powder", + "block_state_b64": "CgAAAwgAYmxvY2tfaWTEAwAACAQAbmFtZSAAbWluZWNyYWZ0Om9yYW5nZV9jb25jcmV0ZV9wb3dkZXIECQBuYW1lX2hhc2gADDj2IJiw+gMKAG5ldHdvcmtfaWTHph0FCgYAc3RhdGVzAAMHAHZlcnNpb24BKBUBAA==" + }, + { + "id": "minecraft:yellow_concrete_powder", + "block_state_b64": "CgAAAwgAYmxvY2tfaWTHAwAACAQAbmFtZSAAbWluZWNyYWZ0OnllbGxvd19jb25jcmV0ZV9wb3dkZXIECQBuYW1lX2hhc2iy6qKNn3ob5wMKAG5ldHdvcmtfaWQZAI39CgYAc3RhdGVzAAMHAHZlcnNpb24BKBUBAA==" + }, + { + "id": "minecraft:lime_concrete_powder", + "block_state_b64": "CgAAAwgAYmxvY2tfaWTIAwAACAQAbmFtZR4AbWluZWNyYWZ0OmxpbWVfY29uY3JldGVfcG93ZGVyBAkAbmFtZV9oYXNo4dYIPslbXPUDCgBuZXR3b3JrX2lk2O8X0AoGAHN0YXRlcwADBwB2ZXJzaW9uASgVAQA=" + }, + { + "id": "minecraft:green_concrete_powder", + "block_state_b64": "CgAAAwgAYmxvY2tfaWTQAwAACAQAbmFtZR8AbWluZWNyYWZ0OmdyZWVuX2NvbmNyZXRlX3Bvd2RlcgQJAG5hbWVfaGFzaM/c9x2aJh3HAwoAbmV0d29ya19pZA0VfBMKBgBzdGF0ZXMAAwcAdmVyc2lvbgEoFQEA" + }, + { + "id": "minecraft:cyan_concrete_powder", + "block_state_b64": "CgAAAwgAYmxvY2tfaWTMAwAACAQAbmFtZR4AbWluZWNyYWZ0OmN5YW5fY29uY3JldGVfcG93ZGVyBAkAbmFtZV9oYXNok+xKAe7XXjoDCgBuZXR3b3JrX2lkmkn6uwoGAHN0YXRlcwADBwB2ZXJzaW9uASgVAQA=" + }, + { + "id": "minecraft:light_blue_concrete_powder", + "block_state_b64": "CgAAAwgAYmxvY2tfaWTGAwAACAQAbmFtZSQAbWluZWNyYWZ0OmxpZ2h0X2JsdWVfY29uY3JldGVfcG93ZGVyBAkAbmFtZV9oYXNogScpIQceyAEDCgBuZXR3b3JrX2lkOmVSbgoGAHN0YXRlcwADBwB2ZXJzaW9uASgVAQA=" + }, + { + "id": "minecraft:blue_concrete_powder", + "block_state_b64": "CgAAAwgAYmxvY2tfaWTOAwAACAQAbmFtZR4AbWluZWNyYWZ0OmJsdWVfY29uY3JldGVfcG93ZGVyBAkAbmFtZV9oYXNoFp7mmeL86r0DCgBuZXR3b3JrX2lkS3b3RQoGAHN0YXRlcwADBwB2ZXJzaW9uASgVAQA=" + }, + { + "id": "minecraft:purple_concrete_powder", + "block_state_b64": "CgAAAwgAYmxvY2tfaWTNAwAACAQAbmFtZSAAbWluZWNyYWZ0OnB1cnBsZV9jb25jcmV0ZV9wb3dkZXIECQBuYW1lX2hhc2iYcVU04hoStwMKAG5ldHdvcmtfaWQXimEjCgYAc3RhdGVzAAMHAHZlcnNpb24BKBUBAA==" + }, + { + "id": "minecraft:magenta_concrete_powder", + "block_state_b64": "CgAAAwgAYmxvY2tfaWTFAwAACAQAbmFtZSEAbWluZWNyYWZ0Om1hZ2VudGFfY29uY3JldGVfcG93ZGVyBAkAbmFtZV9oYXNoy/70q6VPsWgDCgBuZXR3b3JrX2lkf9mxQwoGAHN0YXRlcwADBwB2ZXJzaW9uASgVAQA=" + }, + { + "id": "minecraft:pink_concrete_powder", + "block_state_b64": "CgAAAwgAYmxvY2tfaWTJAwAACAQAbmFtZR4AbWluZWNyYWZ0OnBpbmtfY29uY3JldGVfcG93ZGVyBAkAbmFtZV9oYXNoVikSAf8DwV0DCgBuZXR3b3JrX2lku2MivwoGAHN0YXRlcwADBwB2ZXJzaW9uASgVAQA=" + }, + { + "id": "minecraft:white_concrete", + "block_state_b64": "CgAAAwgAYmxvY2tfaWTsAAAACAQAbmFtZRgAbWluZWNyYWZ0OndoaXRlX2NvbmNyZXRlBAkAbmFtZV9oYXNo6zAp7lsLlvkDCgBuZXR3b3JrX2lk3MAYQAoGAHN0YXRlcwADBwB2ZXJzaW9uASgVAQA=" + }, + { + "id": "minecraft:light_gray_concrete", + "block_state_b64": "CgAAAwgAYmxvY2tfaWR6AwAACAQAbmFtZR0AbWluZWNyYWZ0OmxpZ2h0X2dyYXlfY29uY3JldGUECQBuYW1lX2hhc2hEtet5wuDIKAMKAG5ldHdvcmtfaWQISs02CgYAc3RhdGVzAAMHAHZlcnNpb24BKBUBAA==" + }, + { + "id": "minecraft:gray_concrete", + "block_state_b64": "CgAAAwgAYmxvY2tfaWR5AwAACAQAbmFtZRcAbWluZWNyYWZ0OmdyYXlfY29uY3JldGUECQBuYW1lX2hhc2j92INnb0a83AMKAG5ldHdvcmtfaWQj8RHwCgYAc3RhdGVzAAMHAHZlcnNpb24BKBUBAA==" + }, + { + "id": "minecraft:black_concrete", + "block_state_b64": "CgAAAwgAYmxvY2tfaWSBAwAACAQAbmFtZRgAbWluZWNyYWZ0OmJsYWNrX2NvbmNyZXRlBAkAbmFtZV9oYXNo2X7NDIQmZ70DCgBuZXR3b3JrX2lk2uiVDQoGAHN0YXRlcwADBwB2ZXJzaW9uASgVAQA=" + }, + { + "id": "minecraft:brown_concrete", + "block_state_b64": "CgAAAwgAYmxvY2tfaWR+AwAACAQAbmFtZRgAbWluZWNyYWZ0OmJyb3duX2NvbmNyZXRlBAkAbmFtZV9oYXNoeka02BwXf6oDCgBuZXR3b3JrX2lkYf+xDQoGAHN0YXRlcwADBwB2ZXJzaW9uASgVAQA=" + }, + { + "id": "minecraft:red_concrete", + "block_state_b64": "CgAAAwgAYmxvY2tfaWSAAwAACAQAbmFtZRYAbWluZWNyYWZ0OnJlZF9jb25jcmV0ZQQJAG5hbWVfaGFzaPWmNowLGubqAwoAbmV0d29ya19pZKwyx58KBgBzdGF0ZXMAAwcAdmVyc2lvbgEoFQEA" + }, + { + "id": "minecraft:orange_concrete", + "block_state_b64": "CgAAAwgAYmxvY2tfaWRzAwAACAQAbmFtZRkAbWluZWNyYWZ0Om9yYW5nZV9jb25jcmV0ZQQJAG5hbWVfaGFzaAgE8XmaAi6+AwoAbmV0d29ya19pZMDQNz8KBgBzdGF0ZXMAAwcAdmVyc2lvbgEoFQEA" + }, + { + "id": "minecraft:yellow_concrete", + "block_state_b64": "CgAAAwgAYmxvY2tfaWR2AwAACAQAbmFtZRkAbWluZWNyYWZ0OnllbGxvd19jb25jcmV0ZQQJAG5hbWVfaGFzaE6ONfJPBd0+AwoAbmV0d29ya19pZMarutwKBgBzdGF0ZXMAAwcAdmVyc2lvbgEoFQEA" + }, + { + "id": "minecraft:lime_concrete", + "block_state_b64": "CgAAAwgAYmxvY2tfaWR3AwAACAQAbmFtZRcAbWluZWNyYWZ0OmxpbWVfY29uY3JldGUECQBuYW1lX2hhc2gnd8JW6wmJcAMKAG5ldHdvcmtfaWTd47aoCgYAc3RhdGVzAAMHAHZlcnNpb24BKBUBAA==" + }, + { + "id": "minecraft:green_concrete", + "block_state_b64": "CgAAAwgAYmxvY2tfaWR/AwAACAQAbmFtZRgAbWluZWNyYWZ0OmdyZWVuX2NvbmNyZXRlBAkAbmFtZV9oYXNokbFxRKchQZkDCgBuZXR3b3JrX2lkmhZWUgoGAHN0YXRlcwADBwB2ZXJzaW9uASgVAQA=" + }, + { + "id": "minecraft:cyan_concrete", + "block_state_b64": "CgAAAwgAYmxvY2tfaWR7AwAACAQAbmFtZRcAbWluZWNyYWZ0OmN5YW5fY29uY3JldGUECQBuYW1lX2hhc2hFRrWJ33qj1wMKAG5ldHdvcmtfaWQbi5b8CgYAc3RhdGVzAAMHAHZlcnNpb24BKBUBAA==" + }, + { + "id": "minecraft:light_blue_concrete", + "block_state_b64": "CgAAAwgAYmxvY2tfaWR1AwAACAQAbmFtZR0AbWluZWNyYWZ0OmxpZ2h0X2JsdWVfY29uY3JldGUECQBuYW1lX2hhc2gHAe0kl0SE4AMKAG5ldHdvcmtfaWRL/GbSCgYAc3RhdGVzAAMHAHZlcnNpb24BKBUBAA==" + }, + { + "id": "minecraft:blue_concrete", + "block_state_b64": "CgAAAwgAYmxvY2tfaWR9AwAACAQAbmFtZRcAbWluZWNyYWZ0OmJsdWVfY29uY3JldGUECQBuYW1lX2hhc2hiay301nnj1wMKAG5ldHdvcmtfaWRMvFXNCgYAc3RhdGVzAAMHAHZlcnNpb24BKBUBAA==" + }, + { + "id": "minecraft:purple_concrete", + "block_state_b64": "CgAAAwgAYmxvY2tfaWR8AwAACAQAbmFtZRkAbWluZWNyYWZ0OnB1cnBsZV9jb25jcmV0ZQQJAG5hbWVfaGFzaHBHflsPIwdXAwoAbmV0d29ya19pZCyKA5gKBgBzdGF0ZXMAAwcAdmVyc2lvbgEoFQEA" + }, + { + "id": "minecraft:magenta_concrete", + "block_state_b64": "CgAAAwgAYmxvY2tfaWR0AwAACAQAbmFtZRoAbWluZWNyYWZ0Om1hZ2VudGFfY29uY3JldGUECQBuYW1lX2hhc2gN7LuB/OvdZAMKAG5ldHdvcmtfaWTc6ZOdCgYAc3RhdGVzAAMHAHZlcnNpb24BKBUBAA==" + }, + { + "id": "minecraft:pink_concrete", + "block_state_b64": "CgAAAwgAYmxvY2tfaWR4AwAACAQAbmFtZRcAbWluZWNyYWZ0OnBpbmtfY29uY3JldGUECQBuYW1lX2hhc2ii2G5F0u3SOAMKAG5ldHdvcmtfaWSszGgrCgYAc3RhdGVzAAMHAHZlcnNpb24BKBUBAA==" + }, + { + "id": "minecraft:hardened_clay", + "block_state_b64": "CgAAAwgAYmxvY2tfaWSsAAAACAQAbmFtZRcAbWluZWNyYWZ0OmhhcmRlbmVkX2NsYXkECQBuYW1lX2hhc2jrnRwCJ0krJAMKAG5ldHdvcmtfaWRBCOrrCgYAc3RhdGVzAAMHAHZlcnNpb24BKBUBAA==" + }, + { + "id": "minecraft:white_terracotta", + "block_state_b64": "CgAAAwgAYmxvY2tfaWSfAAAACAQAbmFtZRoAbWluZWNyYWZ0OndoaXRlX3RlcnJhY290dGEECQBuYW1lX2hhc2j3RSdgmnAIewMKAG5ldHdvcmtfaWSimKw+CgYAc3RhdGVzAAMHAHZlcnNpb24BKBUBAA==" + }, + { + "id": "minecraft:light_gray_terracotta", + "block_state_b64": "CgAAAwgAYmxvY2tfaWTaAwAACAQAbmFtZR8AbWluZWNyYWZ0OmxpZ2h0X2dyYXlfdGVycmFjb3R0YQQJAG5hbWVfaGFzaAz1Ri3wIxomAwoAbmV0d29ya19pZH5qgOcKBgBzdGF0ZXMAAwcAdmVyc2lvbgEoFQEA" + }, + { + "id": "minecraft:gray_terracotta", + "block_state_b64": "CgAAAwgAYmxvY2tfaWTZAwAACAQAbmFtZRkAbWluZWNyYWZ0OmdyYXlfdGVycmFjb3R0YQQJAG5hbWVfaGFzaAXdSLAaNZ9vAwoAbmV0d29ya19pZM1QDV0KBgBzdGF0ZXMAAwcAdmVyc2lvbgEoFQEA" + }, + { + "id": "minecraft:black_terracotta", + "block_state_b64": "CgAAAwgAYmxvY2tfaWThAwAACAQAbmFtZRoAbWluZWNyYWZ0OmJsYWNrX3RlcnJhY290dGEECQBuYW1lX2hhc2jxssdv5vlbpgMKAG5ldHdvcmtfaWRE3Ru/CgYAc3RhdGVzAAMHAHZlcnNpb24BKBUBAA==" + }, + { + "id": "minecraft:brown_terracotta", + "block_state_b64": "CgAAAwgAYmxvY2tfaWTeAwAACAQAbmFtZRoAbWluZWNyYWZ0OmJyb3duX3RlcnJhY290dGEECQBuYW1lX2hhc2gG4kPenmOF9gMKAG5ldHdvcmtfaWQ/i0iNCgYAc3RhdGVzAAMHAHZlcnNpb24BKBUBAA==" + }, + { + "id": "minecraft:red_terracotta", + "block_state_b64": "CgAAAwgAYmxvY2tfaWTgAwAACAQAbmFtZRgAbWluZWNyYWZ0OnJlZF90ZXJyYWNvdHRhBAkAbmFtZV9oYXNo7fX56HXFejEDCgBuZXR3b3JrX2lk8tTF8QoGAHN0YXRlcwADBwB2ZXJzaW9uASgVAQA=" + }, + { + "id": "minecraft:orange_terracotta", + "block_state_b64": "CgAAAwgAYmxvY2tfaWTTAwAACAQAbmFtZRsAbWluZWNyYWZ0Om9yYW5nZV90ZXJyYWNvdHRhBAkAbmFtZV9oYXNo0Hjmql3sruMDCgBuZXR3b3JrX2lklmqmkAoGAHN0YXRlcwADBwB2ZXJzaW9uASgVAQA=" + }, + { + "id": "minecraft:yellow_terracotta", + "block_state_b64": "CgAAAwgAYmxvY2tfaWTWAwAACAQAbmFtZRsAbWluZWNyYWZ0OnllbGxvd190ZXJyYWNvdHRhBAkAbmFtZV9oYXNoqkyKKrmA3VcDCgBuZXR3b3JrX2lkaM/orAoGAHN0YXRlcwADBwB2ZXJzaW9uASgVAQA=" + }, + { + "id": "minecraft:lime_terracotta", + "block_state_b64": "CgAAAwgAYmxvY2tfaWTXAwAACAQAbmFtZRkAbWluZWNyYWZ0OmxpbWVfdGVycmFjb3R0YQQJAG5hbWVfaGFzaANjADFOF9v7AwoAbmV0d29ya19pZJt0XsgKBgBzdGF0ZXMAAwcAdmVyc2lvbgEoFQEA" + }, + { + "id": "minecraft:green_terracotta", + "block_state_b64": "CgAAAwgAYmxvY2tfaWTfAwAACAQAbmFtZRoAbWluZWNyYWZ0OmdyZWVuX3RlcnJhY290dGEECQBuYW1lX2hhc2j5Ybq36yYwRQMKAG5ldHdvcmtfaWQ8kGdHCgYAc3RhdGVzAAMHAHZlcnNpb24BKBUBAA==" + }, + { + "id": "minecraft:cyan_terracotta", + "block_state_b64": "CgAAAwgAYmxvY2tfaWTbAwAACAQAbmFtZRkAbWluZWNyYWZ0OmN5YW5fdGVycmFjb3R0YQQJAG5hbWVfaGFzaN09COzMuHwAAwoAbmV0d29ya19pZIWPCzoKBgBzdGF0ZXMAAwcAdmVyc2lvbgEoFQEA" + }, + { + "id": "minecraft:light_blue_terracotta", + "block_state_b64": "CgAAAwgAYmxvY2tfaWTVAwAACAQAbmFtZR8AbWluZWNyYWZ0OmxpZ2h0X2JsdWVfdGVycmFjb3R0YQQJAG5hbWVfaGFzaOMytez7cOZiAwoAbmV0d29ya19pZFHK1UsKBgBzdGF0ZXMAAwcAdmVyc2lvbgEoFQEA" + }, + { + "id": "minecraft:blue_terracotta", + "block_state_b64": "CgAAAwgAYmxvY2tfaWTdAwAACAQAbmFtZRkAbWluZWNyYWZ0OmJsdWVfdGVycmFjb3R0YQQJAG5hbWVfaGFzaF6inyTK5RpAAwoAbmV0d29ya19pZF5mVZIKBgBzdGF0ZXMAAwcAdmVyc2lvbgEoFQEA" + }, + { + "id": "minecraft:purple_terracotta", + "block_state_b64": "CgAAAwgAYmxvY2tfaWTcAwAACAQAbmFtZRsAbWluZWNyYWZ0OnB1cnBsZV90ZXJyYWNvdHRhBAkAbmFtZV9oYXNoKF7YG61yTbEDCgBuZXR3b3JrX2lkhtRDlwoGAHN0YXRlcwADBwB2ZXJzaW9uASgVAQA=" + }, + { + "id": "minecraft:magenta_terracotta", + "block_state_b64": "CgAAAwgAYmxvY2tfaWTUAwAACAQAbmFtZRwAbWluZWNyYWZ0Om1hZ2VudGFfdGVycmFjb3R0YQQJAG5hbWVfaGFzaLWvtpAVtztyAwoAbmV0d29ya19pZN5SoakKBgBzdGF0ZXMAAwcAdmVyc2lvbgEoFQEA" + }, + { + "id": "minecraft:pink_terracotta", + "block_state_b64": "CgAAAwgAYmxvY2tfaWTYAwAACAQAbmFtZRkAbWluZWNyYWZ0OnBpbmtfdGVycmFjb3R0YQQJAG5hbWVfaGFzaJ7mzvyzSQZTAwoAbmV0d29ya19pZDJWe4YKBgBzdGF0ZXMAAwcAdmVyc2lvbgEoFQEA" + }, + { + "id": "minecraft:white_glazed_terracotta", + "block_state_b64": "CgAAAwgAYmxvY2tfaWTcAAAACAQAbmFtZSEAbWluZWNyYWZ0OndoaXRlX2dsYXplZF90ZXJyYWNvdHRhBAkAbmFtZV9oYXNoiVzCdoHAJo0DCgBuZXR3b3JrX2lkIlj9AAoGAHN0YXRlcwMQAGZhY2luZ19kaXJlY3Rpb24AAAAAAAMHAHZlcnNpb24BKBUBAA==" + }, + { + "id": "minecraft:silver_glazed_terracotta", + "block_state_b64": "CgAAAwgAYmxvY2tfaWTkAAAACAQAbmFtZSIAbWluZWNyYWZ0OnNpbHZlcl9nbGF6ZWRfdGVycmFjb3R0YQQJAG5hbWVfaGFzaAVsA0CnhzA4AwoAbmV0d29ya19pZPnxtJEKBgBzdGF0ZXMDEABmYWNpbmdfZGlyZWN0aW9uAAAAAAADBwB2ZXJzaW9uASgVAQA=" + }, + { + "id": "minecraft:gray_glazed_terracotta", + "block_state_b64": "CgAAAwgAYmxvY2tfaWTjAAAACAQAbmFtZSAAbWluZWNyYWZ0OmdyYXlfZ2xhemVkX3RlcnJhY290dGEECQBuYW1lX2hhc2jvLZt9u/lF/AMKAG5ldHdvcmtfaWQVU8eFCgYAc3RhdGVzAxAAZmFjaW5nX2RpcmVjdGlvbgAAAAAAAwcAdmVyc2lvbgEoFQEA" + }, + { + "id": "minecraft:black_glazed_terracotta", + "block_state_b64": "CgAAAwgAYmxvY2tfaWTrAAAACAQAbmFtZSEAbWluZWNyYWZ0OmJsYWNrX2dsYXplZF90ZXJyYWNvdHRhBAkAbmFtZV9oYXNoe8I4xAXbO5UDCgBuZXR3b3JrX2lk2Icb9AoGAHN0YXRlcwMQAGZhY2luZ19kaXJlY3Rpb24AAAAAAAMHAHZlcnNpb24BKBUBAA==" + }, + { + "id": "minecraft:brown_glazed_terracotta", + "block_state_b64": "CgAAAwgAYmxvY2tfaWToAAAACAQAbmFtZSEAbWluZWNyYWZ0OmJyb3duX2dsYXplZF90ZXJyYWNvdHRhBAkAbmFtZV9oYXNoSiNZOobbpjoDCgBuZXR3b3JrX2lkJy0jwgoGAHN0YXRlcwMQAGZhY2luZ19kaXJlY3Rpb24AAAAAAAMHAHZlcnNpb24BKBUBAA==" + }, + { + "id": "minecraft:red_glazed_terracotta", + "block_state_b64": "CgAAAwgAYmxvY2tfaWTqAAAACAQAbmFtZR8AbWluZWNyYWZ0OnJlZF9nbGF6ZWRfdGVycmFjb3R0YQQJAG5hbWVfaGFzaBdWFGLmCLFVAwoAbmV0d29ya19pZMYBJSEKBgBzdGF0ZXMDEABmYWNpbmdfZGlyZWN0aW9uAAAAAAADBwB2ZXJzaW9uASgVAQA=" + }, + { + "id": "minecraft:orange_glazed_terracotta", + "block_state_b64": "CgAAAwgAYmxvY2tfaWTdAAAACAQAbmFtZSIAbWluZWNyYWZ0Om9yYW5nZV9nbGF6ZWRfdGVycmFjb3R0YQQJAG5hbWVfaGFzaMyJMrnPr7szAwoAbmV0d29ya19pZN6+7TUKBgBzdGF0ZXMDEABmYWNpbmdfZGlyZWN0aW9uAAAAAAADBwB2ZXJzaW9uASgVAQA=" + }, + { + "id": "minecraft:yellow_glazed_terracotta", + "block_state_b64": "CgAAAwgAYmxvY2tfaWTgAAAACAQAbmFtZSIAbWluZWNyYWZ0OnllbGxvd19nbGF6ZWRfdGVycmFjb3R0YQQJAG5hbWVfaGFzaN6NaIhf6m0uAwoAbmV0d29ya19pZKRHXeoKBgBzdGF0ZXMDEABmYWNpbmdfZGlyZWN0aW9uAAAAAAADBwB2ZXJzaW9uASgVAQA=" + }, + { + "id": "minecraft:lime_glazed_terracotta", + "block_state_b64": "CgAAAwgAYmxvY2tfaWThAAAACAQAbmFtZSAAbWluZWNyYWZ0OmxpbWVfZ2xhemVkX3RlcnJhY290dGEECQBuYW1lX2hhc2iF3E68/rB2EAMKAG5ldHdvcmtfaWSP7qQWCgYAc3RhdGVzAxAAZmFjaW5nX2RpcmVjdGlvbgAAAAAAAwcAdmVyc2lvbgEoFQEA" + }, + { + "id": "minecraft:green_glazed_terracotta", + "block_state_b64": "CgAAAwgAYmxvY2tfaWTpAAAACAQAbmFtZSEAbWluZWNyYWZ0OmdyZWVuX2dsYXplZF90ZXJyYWNvdHRhBAkAbmFtZV9oYXNow5mo8aQDFboDCgBuZXR3b3JrX2lkoF11kgoGAHN0YXRlcwMQAGZhY2luZ19kaXJlY3Rpb24AAAAAAAMHAHZlcnNpb24BKBUBAA==" + }, + { + "id": "minecraft:cyan_glazed_terracotta", + "block_state_b64": "CgAAAwgAYmxvY2tfaWTlAAAACAQAbmFtZSAAbWluZWNyYWZ0OmN5YW5fZ2xhemVkX3RlcnJhY290dGEECQBuYW1lX2hhc2gnNB+cCFRJhwMKAG5ldHdvcmtfaWT9buMtCgYAc3RhdGVzAxAAZmFjaW5nX2RpcmVjdGlvbgAAAAAAAwcAdmVyc2lvbgEoFQEA" + }, + { + "id": "minecraft:light_blue_glazed_terracotta", + "block_state_b64": "CgAAAwgAYmxvY2tfaWTfAAAACAQAbmFtZSYAbWluZWNyYWZ0OmxpZ2h0X2JsdWVfZ2xhemVkX3RlcnJhY290dGEECQBuYW1lX2hhc2gladnCDBKCigMKAG5ldHdvcmtfaWS5CszFCgYAc3RhdGVzAxAAZmFjaW5nX2RpcmVjdGlvbgAAAAAAAwcAdmVyc2lvbgEoFQEA" + }, + { + "id": "minecraft:blue_glazed_terracotta", + "block_state_b64": "CgAAAwgAYmxvY2tfaWTnAAAACAQAbmFtZSAAbWluZWNyYWZ0OmJsdWVfZ2xhemVkX3RlcnJhY290dGEECQBuYW1lX2hhc2giOZK+2nB1igMKAG5ldHdvcmtfaWR+e22CCgYAc3RhdGVzAxAAZmFjaW5nX2RpcmVjdGlvbgAAAAAAAwcAdmVyc2lvbgEoFQEA" + }, + { + "id": "minecraft:purple_glazed_terracotta", + "block_state_b64": "CgAAAwgAYmxvY2tfaWTbAAAACAQAbmFtZSIAbWluZWNyYWZ0OnB1cnBsZV9nbGF6ZWRfdGVycmFjb3R0YQQJAG5hbWVfaGFzaIQU03txeAfHAwoAbmV0d29ya19pZLKbSE4KBgBzdGF0ZXMDEABmYWNpbmdfZGlyZWN0aW9uAAAAAAADBwB2ZXJzaW9uASgVAQA=" + }, + { + "id": "minecraft:magenta_glazed_terracotta", + "block_state_b64": "CgAAAwgAYmxvY2tfaWTeAAAACAQAbmFtZSMAbWluZWNyYWZ0Om1hZ2VudGFfZ2xhemVkX3RlcnJhY290dGEECQBuYW1lX2hhc2i/SNqDJbfjMgMKAG5ldHdvcmtfaWQKf9UvCgYAc3RhdGVzAxAAZmFjaW5nX2RpcmVjdGlvbgAAAAAAAwcAdmVyc2lvbgEoFQEA" + }, + { + "id": "minecraft:pink_glazed_terracotta", + "block_state_b64": "CgAAAwgAYmxvY2tfaWTiAAAACAQAbmFtZSAAbWluZWNyYWZ0OnBpbmtfZ2xhemVkX3RlcnJhY290dGEECQBuYW1lX2hhc2hik8DVt4g+twMKAG5ldHdvcmtfaWTKzav2CgYAc3RhdGVzAxAAZmFjaW5nX2RpcmVjdGlvbgAAAAAAAwcAdmVyc2lvbgEoFQEA" + }, + { + "id": "minecraft:purpur_block", + "block_state_b64": "CgAAAwgAYmxvY2tfaWTJAAAACAQAbmFtZRYAbWluZWNyYWZ0OnB1cnB1cl9ibG9jawQJAG5hbWVfaGFzaAgLwnUZGlzsAwoAbmV0d29ya19pZGCZ+4UKBgBzdGF0ZXMICwBwaWxsYXJfYXhpcwEAeQADBwB2ZXJzaW9uASgVAQA=" + }, + { + "id": "minecraft:purpur_pillar", + "block_state_b64": "CgAAAwgAYmxvY2tfaWS2BAAACAQAbmFtZRcAbWluZWNyYWZ0OnB1cnB1cl9waWxsYXIECQBuYW1lX2hhc2iFcSsdykO+jgMKAG5ldHdvcmtfaWQe0+geCgYAc3RhdGVzCAsAcGlsbGFyX2F4aXMBAHkAAwcAdmVyc2lvbgEoFQEA" + }, + { + "id": "minecraft:packed_mud", + "block_state_b64": "CgAAAwgAYmxvY2tfaWTcAgAACAQAbmFtZRQAbWluZWNyYWZ0OnBhY2tlZF9tdWQECQBuYW1lX2hhc2gHOMa121h4FgMKAG5ldHdvcmtfaWTUb6LyCgYAc3RhdGVzAAMHAHZlcnNpb24BKBUBAA==" + }, + { + "id": "minecraft:mud_bricks", + "block_state_b64": "CgAAAwgAYmxvY2tfaWTaAgAACAQAbmFtZRQAbWluZWNyYWZ0Om11ZF9icmlja3MECQBuYW1lX2hhc2iDL/SVl/PewQMKAG5ldHdvcmtfaWSkBjaDCgYAc3RhdGVzAAMHAHZlcnNpb24BKBUBAA==" + }, + { + "id": "minecraft:nether_wart_block", + "block_state_b64": "CgAAAwgAYmxvY2tfaWTWAAAACAQAbmFtZRsAbWluZWNyYWZ0Om5ldGhlcl93YXJ0X2Jsb2NrBAkAbmFtZV9oYXNo9XGS4GNnlV4DCgBuZXR3b3JrX2lkh3apIgoGAHN0YXRlcwADBwB2ZXJzaW9uASgVAQA=" + }, + { + "id": "minecraft:warped_wart_block", + "block_state_b64": "CgAAAwgAYmxvY2tfaWTiAQAACAQAbmFtZRsAbWluZWNyYWZ0OndhcnBlZF93YXJ0X2Jsb2NrBAkAbmFtZV9oYXNo9IqDS9yUPJoDCgBuZXR3b3JrX2lkMpKAbAoGAHN0YXRlcwADBwB2ZXJzaW9uASgVAQA=" + }, + { + "id": "minecraft:shroomlight", + "block_state_b64": "CgAAAwgAYmxvY2tfaWTlAQAACAQAbmFtZRUAbWluZWNyYWZ0OnNocm9vbWxpZ2h0BAkAbmFtZV9oYXNoZHCHcHX/HYADCgBuZXR3b3JrX2lkLG2JiwoGAHN0YXRlcwADBwB2ZXJzaW9uASgVAQA=" + }, + { + "id": "minecraft:crimson_nylium", + "block_state_b64": "CgAAAwgAYmxvY2tfaWTnAQAACAQAbmFtZRgAbWluZWNyYWZ0OmNyaW1zb25fbnlsaXVtBAkAbmFtZV9oYXNoOr6DJYW2bFYDCgBuZXR3b3JrX2lkuWpRDgoGAHN0YXRlcwADBwB2ZXJzaW9uASgVAQA=" + }, + { + "id": "minecraft:warped_nylium", + "block_state_b64": "CgAAAwgAYmxvY2tfaWToAQAACAQAbmFtZRcAbWluZWNyYWZ0OndhcnBlZF9ueWxpdW0ECQBuYW1lX2hhc2g0Zf89cfr3rwMKAG5ldHdvcmtfaWSu/kekCgYAc3RhdGVzAAMHAHZlcnNpb24BKBUBAA==" + }, + { + "id": "minecraft:netherrack", + "block_state_b64": "CgAAAwgAYmxvY2tfaWRXAAAACAQAbmFtZRQAbWluZWNyYWZ0Om5ldGhlcnJhY2sECQBuYW1lX2hhc2i/r5ZyRsvPyQMKAG5ldHdvcmtfaWTAiTOACgYAc3RhdGVzAAMHAHZlcnNpb24BKBUBAA==" + }, + { + "id": "minecraft:soul_soil", + "block_state_b64": "CgAAAwgAYmxvY2tfaWTrAQAACAQAbmFtZRMAbWluZWNyYWZ0OnNvdWxfc29pbAQJAG5hbWVfaGFzaC1/87ccutuTAwoAbmV0d29ya19pZKc63SMKBgBzdGF0ZXMAAwcAdmVyc2lvbgEoFQEA" + }, + { + "id": "minecraft:grass_block", + "block_state_b64": "CgAAAwgAYmxvY2tfaWQCAAAACAQAbmFtZRUAbWluZWNyYWZ0OmdyYXNzX2Jsb2NrBAkAbmFtZV9oYXNojPyGp3/CSZwDCgBuZXR3b3JrX2lktCgx3goGAHN0YXRlcwADBwB2ZXJzaW9uASgVAQA=" + }, + { + "id": "minecraft:podzol", + "block_state_b64": "CgAAAwgAYmxvY2tfaWTzAAAACAQAbmFtZRAAbWluZWNyYWZ0OnBvZHpvbAQJAG5hbWVfaGFzaBzqokRjH4Z1AwoAbmV0d29ya19pZPPS/GUKBgBzdGF0ZXMAAwcAdmVyc2lvbgEoFQEA" + }, + { + "id": "minecraft:mycelium", + "block_state_b64": "CgAAAwgAYmxvY2tfaWRuAAAACAQAbmFtZRIAbWluZWNyYWZ0Om15Y2VsaXVtBAkAbmFtZV9oYXNojTN09cKickIDCgBuZXR3b3JrX2lkLNPxXQoGAHN0YXRlcwADBwB2ZXJzaW9uASgVAQA=" + }, + { + "id": "minecraft:grass_path", + "block_state_b64": "CgAAAwgAYmxvY2tfaWTGAAAACAQAbmFtZRQAbWluZWNyYWZ0OmdyYXNzX3BhdGgECQBuYW1lX2hhc2i0/KZV8Qsy+gMKAG5ldHdvcmtfaWT7CcdzCgYAc3RhdGVzAAMHAHZlcnNpb24BKBUBAA==" + }, + { + "id": "minecraft:dirt", + "block_state_b64": "CgAAAwgAYmxvY2tfaWQDAAAACAQAbmFtZQ4AbWluZWNyYWZ0OmRpcnQECQBuYW1lX2hhc2hXp6jnXAe+kQMKAG5ldHdvcmtfaWSG706CCgYAc3RhdGVzAAMHAHZlcnNpb24BKBUBAA==" + }, + { + "id": "minecraft:coarse_dirt", + "block_state_b64": "CgAAAwgAYmxvY2tfaWTBBAAACAQAbmFtZRUAbWluZWNyYWZ0OmNvYXJzZV9kaXJ0BAkAbmFtZV9oYXNosd+cah7WSmoDCgBuZXR3b3JrX2lkgS5RcAoGAHN0YXRlcwADBwB2ZXJzaW9uASgVAQA=" + }, + { + "id": "minecraft:dirt_with_roots", + "block_state_b64": "CgAAAwgAYmxvY2tfaWQ9AgAACAQAbmFtZRkAbWluZWNyYWZ0OmRpcnRfd2l0aF9yb290cwQJAG5hbWVfaGFzaLCNDYPviDCIAwoAbmV0d29ya19pZNCkwzoKBgBzdGF0ZXMAAwcAdmVyc2lvbgEoFQEA" + }, + { + "id": "minecraft:farmland", + "block_state_b64": "CgAAAwgAYmxvY2tfaWQ8AAAACAQAbmFtZRIAbWluZWNyYWZ0OmZhcm1sYW5kBAkAbmFtZV9oYXNoxyQ5ag7LolADCgBuZXR3b3JrX2lkX618FQoGAHN0YXRlcwMSAG1vaXN0dXJpemVkX2Ftb3VudAAAAAAAAwcAdmVyc2lvbgEoFQEA" + }, + { + "id": "minecraft:mud", + "block_state_b64": "CgAAAwgAYmxvY2tfaWTYAgAACAQAbmFtZQ0AbWluZWNyYWZ0Om11ZAQJAG5hbWVfaGFzaPb/3P+uLy+9AwoAbmV0d29ya19pZPIUlUkKBgBzdGF0ZXMAAwcAdmVyc2lvbgEoFQEA" + }, + { + "id": "minecraft:clay", + "block_state_b64": "CgAAAwgAYmxvY2tfaWRSAAAACAQAbmFtZQ4AbWluZWNyYWZ0OmNsYXkECQBuYW1lX2hhc2j/S6sKXRcpzwMKAG5ldHdvcmtfaWRmsb8nCgYAc3RhdGVzAAMHAHZlcnNpb24BKBUBAA==" + }, + { + "id": "minecraft:iron_ore", + "block_state_b64": "CgAAAwgAYmxvY2tfaWQPAAAACAQAbmFtZRIAbWluZWNyYWZ0Omlyb25fb3JlBAkAbmFtZV9oYXNoS7BYtLnfx3gDCgBuZXR3b3JrX2lk3loneQoGAHN0YXRlcwADBwB2ZXJzaW9uASgVAQA=" + }, + { + "id": "minecraft:gold_ore", + "block_state_b64": "CgAAAwgAYmxvY2tfaWQOAAAACAQAbmFtZRIAbWluZWNyYWZ0OmdvbGRfb3JlBAkAbmFtZV9oYXNoC5Y+DUGXLC4DCgBuZXR3b3JrX2lkNhvMfwoGAHN0YXRlcwADBwB2ZXJzaW9uASgVAQA=" + }, + { + "id": "minecraft:diamond_ore", + "block_state_b64": "CgAAAwgAYmxvY2tfaWQ4AAAACAQAbmFtZRUAbWluZWNyYWZ0OmRpYW1vbmRfb3JlBAkAbmFtZV9oYXNokUOJ2wZZrGQDCgBuZXR3b3JrX2lk/dChVAoGAHN0YXRlcwADBwB2ZXJzaW9uASgVAQA=" + }, + { + "id": "minecraft:lapis_ore", + "block_state_b64": "CgAAAwgAYmxvY2tfaWQVAAAACAQAbmFtZRMAbWluZWNyYWZ0OmxhcGlzX29yZQQJAG5hbWVfaGFzaMrmrUrSzb7qAwoAbmV0d29ya19pZMg+qK4KBgBzdGF0ZXMAAwcAdmVyc2lvbgEoFQEA" + }, + { + "id": "minecraft:redstone_ore", + "block_state_b64": "CgAAAwgAYmxvY2tfaWRJAAAACAQAbmFtZRYAbWluZWNyYWZ0OnJlZHN0b25lX29yZQQJAG5hbWVfaGFzaFHVnp8Wc4JbAwoAbmV0d29ya19pZKDYvQoKBgBzdGF0ZXMAAwcAdmVyc2lvbgEoFQEA" + }, + { + "id": "minecraft:coal_ore", + "block_state_b64": "CgAAAwgAYmxvY2tfaWQQAAAACAQAbmFtZRIAbWluZWNyYWZ0OmNvYWxfb3JlBAkAbmFtZV9oYXNo1OjA+Iuy51oDCgBuZXR3b3JrX2lk+R/aKAoGAHN0YXRlcwADBwB2ZXJzaW9uASgVAQA=" + }, + { + "id": "minecraft:copper_ore", + "block_state_b64": "CgAAAwgAYmxvY2tfaWQ2AgAACAQAbmFtZRQAbWluZWNyYWZ0OmNvcHBlcl9vcmUECQBuYW1lX2hhc2iSZduSntOzOwMKAG5ldHdvcmtfaWQtIuCnCgYAc3RhdGVzAAMHAHZlcnNpb24BKBUBAA==" + }, + { + "id": "minecraft:emerald_ore", + "block_state_b64": "CgAAAwgAYmxvY2tfaWSBAAAACAQAbmFtZRUAbWluZWNyYWZ0OmVtZXJhbGRfb3JlBAkAbmFtZV9oYXNoJTovr+VgINsDCgBuZXR3b3JrX2lknbkqCgoGAHN0YXRlcwADBwB2ZXJzaW9uASgVAQA=" + }, + { + "id": "minecraft:quartz_ore", + "block_state_b64": "CgAAAwgAYmxvY2tfaWSZAAAACAQAbmFtZRQAbWluZWNyYWZ0OnF1YXJ0el9vcmUECQBuYW1lX2hhc2g0yNHLMK9TaQMKAG5ldHdvcmtfaWSzN7nzCgYAc3RhdGVzAAMHAHZlcnNpb24BKBUBAA==" + }, + { + "id": "minecraft:nether_gold_ore", + "block_state_b64": "CgAAAwgAYmxvY2tfaWQfAgAACAQAbmFtZRkAbWluZWNyYWZ0Om5ldGhlcl9nb2xkX29yZQQJAG5hbWVfaGFzaEJZ7segIBgBAwoAbmV0d29ya19pZNI9pDgKBgBzdGF0ZXMAAwcAdmVyc2lvbgEoFQEA" + }, + { + "id": "minecraft:ancient_debris", + "block_state_b64": "CgAAAwgAYmxvY2tfaWQOAgAACAQAbmFtZRgAbWluZWNyYWZ0OmFuY2llbnRfZGVicmlzBAkAbmFtZV9oYXNoNrbxMc9AwKcDCgBuZXR3b3JrX2lkrSNjEAoGAHN0YXRlcwADBwB2ZXJzaW9uASgVAQA=" + }, + { + "id": "minecraft:deepslate_iron_ore", + "block_state_b64": "CgAAAwgAYmxvY2tfaWSQAgAACAQAbmFtZRwAbWluZWNyYWZ0OmRlZXBzbGF0ZV9pcm9uX29yZQQJAG5hbWVfaGFzaB/fDL9pgvXXAwoAbmV0d29ya19pZFA0bz4KBgBzdGF0ZXMAAwcAdmVyc2lvbgEoFQEA" + }, + { + "id": "minecraft:deepslate_gold_ore", + "block_state_b64": "CgAAAwgAYmxvY2tfaWSRAgAACAQAbmFtZRwAbWluZWNyYWZ0OmRlZXBzbGF0ZV9nb2xkX29yZQQJAG5hbWVfaGFzaF9G7WYhKFinAwoAbmV0d29ya19pZHQTfBUKBgBzdGF0ZXMAAwcAdmVyc2lvbgEoFQEA" + }, + { + "id": "minecraft:deepslate_diamond_ore", + "block_state_b64": "CgAAAwgAYmxvY2tfaWSUAgAACAQAbmFtZR8AbWluZWNyYWZ0OmRlZXBzbGF0ZV9kaWFtb25kX29yZQQJAG5hbWVfaGFzaEUH5USh+iD3AwoAbmV0d29ya19pZHP6VzAKBgBzdGF0ZXMAAwcAdmVyc2lvbgEoFQEA" + }, + { + "id": "minecraft:deepslate_lapis_ore", + "block_state_b64": "CgAAAwgAYmxvY2tfaWSPAgAACAQAbmFtZR0AbWluZWNyYWZ0OmRlZXBzbGF0ZV9sYXBpc19vcmUECQBuYW1lX2hhc2j+yFxU/KZs1gMKAG5ldHdvcmtfaWRKINzICgYAc3RhdGVzAAMHAHZlcnNpb24BKBUBAA==" + }, + { + "id": "minecraft:deepslate_redstone_ore", + "block_state_b64": "CgAAAwgAYmxvY2tfaWSSAgAACAQAbmFtZSAAbWluZWNyYWZ0OmRlZXBzbGF0ZV9yZWRzdG9uZV9vcmUECQBuYW1lX2hhc2iVgM3wWWD6ugMKAG5ldHdvcmtfaWReBdYRCgYAc3RhdGVzAAMHAHZlcnNpb24BKBUBAA==" + }, + { + "id": "minecraft:deepslate_emerald_ore", + "block_state_b64": "CgAAAwgAYmxvY2tfaWSWAgAACAQAbmFtZR8AbWluZWNyYWZ0OmRlZXBzbGF0ZV9lbWVyYWxkX29yZQQJAG5hbWVfaGFzaNlfo5HTwS6wAwoAbmV0d29ya19pZNeie6sKBgBzdGF0ZXMAAwcAdmVyc2lvbgEoFQEA" + }, + { + "id": "minecraft:deepslate_coal_ore", + "block_state_b64": "CgAAAwgAYmxvY2tfaWSVAgAACAQAbmFtZRwAbWluZWNyYWZ0OmRlZXBzbGF0ZV9jb2FsX29yZQQJAG5hbWVfaGFzaIjikmcbRrPPAwoAbmV0d29ya19pZD9TiygKBgBzdGF0ZXMAAwcAdmVyc2lvbgEoFQEA" + }, + { + "id": "minecraft:deepslate_copper_ore", + "block_state_b64": "CgAAAwgAYmxvY2tfaWSXAgAACAQAbmFtZR4AbWluZWNyYWZ0OmRlZXBzbGF0ZV9jb3BwZXJfb3JlBAkAbmFtZV9oYXNottjV4Ev5LAQDCgBuZXR3b3JrX2lkP23rgQoGAHN0YXRlcwADBwB2ZXJzaW9uASgVAQA=" + }, + { + "id": "minecraft:stone", + "block_state_b64": "CgAAAwgAYmxvY2tfaWQBAAAACAQAbmFtZQ8AbWluZWNyYWZ0OnN0b25lBAkAbmFtZV9oYXNoE3mqhJxzJycDCgBuZXR3b3JrX2lkIQ4xgAoGAHN0YXRlcwADBwB2ZXJzaW9uASgVAQA=" + }, + { + "id": "minecraft:granite", + "block_state_b64": "CgAAAwgAYmxvY2tfaWRNAwAACAQAbmFtZREAbWluZWNyYWZ0OmdyYW5pdGUECQBuYW1lX2hhc2iq+Dur2pw4AwMKAG5ldHdvcmtfaWT2NMfJCgYAc3RhdGVzAAMHAHZlcnNpb24BKBUBAA==" + }, + { + "id": "minecraft:diorite", + "block_state_b64": "CgAAAwgAYmxvY2tfaWRPAwAACAQAbmFtZREAbWluZWNyYWZ0OmRpb3JpdGUECQBuYW1lX2hhc2iaFsq2iinZBQMKAG5ldHdvcmtfaWQqGE6XCgYAc3RhdGVzAAMHAHZlcnNpb24BKBUBAA==" + }, + { + "id": "minecraft:andesite", + "block_state_b64": "CgAAAwgAYmxvY2tfaWRRAwAACAQAbmFtZRIAbWluZWNyYWZ0OmFuZGVzaXRlBAkAbmFtZV9oYXNosaLIEnQQoSYDCgBuZXR3b3JrX2lkEApRZAoGAHN0YXRlcwADBwB2ZXJzaW9uASgVAQA=" + }, + { + "id": "minecraft:blackstone", + "block_state_b64": "CgAAAwgAYmxvY2tfaWQQAgAACAQAbmFtZRQAbWluZWNyYWZ0OmJsYWNrc3RvbmUECQBuYW1lX2hhc2iMFYziD80D6QMKAG5ldHdvcmtfaWSrUryHCgYAc3RhdGVzAAMHAHZlcnNpb24BKBUBAA==" + }, + { + "id": "minecraft:deepslate", + "block_state_b64": "CgAAAwgAYmxvY2tfaWR5AgAACAQAbmFtZRMAbWluZWNyYWZ0OmRlZXBzbGF0ZQQJAG5hbWVfaGFzaKX5pAblxz8TAwoAbmV0d29ya19pZOJoQjsKBgBzdGF0ZXMICwBwaWxsYXJfYXhpcwEAeQADBwB2ZXJzaW9uASgVAQA=" + }, + { + "id": "minecraft:tuff", + "block_state_b64": "CgAAAwgAYmxvY2tfaWRMAgAACAQAbmFtZQ4AbWluZWNyYWZ0OnR1ZmYECQBuYW1lX2hhc2h1Rwc1XYsBGwMKAG5ldHdvcmtfaWRwQGn0CgYAc3RhdGVzAAMHAHZlcnNpb24BKBUBAA==" + }, + { + "id": "minecraft:basalt", + "block_state_b64": "CgAAAwgAYmxvY2tfaWTpAQAACAQAbmFtZRAAbWluZWNyYWZ0OmJhc2FsdAQJAG5hbWVfaGFzaH+UQO2yWodiAwoAbmV0d29ya19pZBPNSV4KBgBzdGF0ZXMICwBwaWxsYXJfYXhpcwEAeQADBwB2ZXJzaW9uASgVAQA=" + }, + { + "id": "minecraft:polished_granite", + "block_state_b64": "CgAAAwgAYmxvY2tfaWROAwAACAQAbmFtZRoAbWluZWNyYWZ0OnBvbGlzaGVkX2dyYW5pdGUECQBuYW1lX2hhc2iLiEfys8pFIAMKAG5ldHdvcmtfaWTCxxcHCgYAc3RhdGVzAAMHAHZlcnNpb24BKBUBAA==" + }, + { + "id": "minecraft:polished_diorite", + "block_state_b64": "CgAAAwgAYmxvY2tfaWRQAwAACAQAbmFtZRoAbWluZWNyYWZ0OnBvbGlzaGVkX2Rpb3JpdGUECQBuYW1lX2hhc2hTxY4fKmNmlAMKAG5ldHdvcmtfaWTmtjdRCgYAc3RhdGVzAAMHAHZlcnNpb24BKBUBAA==" + }, + { + "id": "minecraft:polished_andesite", + "block_state_b64": "CgAAAwgAYmxvY2tfaWRSAwAACAQAbmFtZRsAbWluZWNyYWZ0OnBvbGlzaGVkX2FuZGVzaXRlBAkAbmFtZV9oYXNovl28uFk4HuQDCgBuZXR3b3JrX2lklFjuCwoGAHN0YXRlcwADBwB2ZXJzaW9uASgVAQA=" + }, + { + "id": "minecraft:polished_blackstone", + "block_state_b64": "CgAAAwgAYmxvY2tfaWQiAgAACAQAbmFtZR0AbWluZWNyYWZ0OnBvbGlzaGVkX2JsYWNrc3RvbmUECQBuYW1lX2hhc2jT9fHCl6vWQQMKAG5ldHdvcmtfaWR/Ho6oCgYAc3RhdGVzAAMHAHZlcnNpb24BKBUBAA==" + }, + { + "id": "minecraft:polished_deepslate", + "block_state_b64": "CgAAAwgAYmxvY2tfaWR+AgAACAQAbmFtZRwAbWluZWNyYWZ0OnBvbGlzaGVkX2RlZXBzbGF0ZQQJAG5hbWVfaGFzaHC1edoaWF3uAwoAbmV0d29ya19pZCPeQsEKBgBzdGF0ZXMAAwcAdmVyc2lvbgEoFQEA" + }, + { + "id": "minecraft:polished_tuff", + "block_state_b64": "CgAAAwgAYmxvY2tfaWTrAwAACAQAbmFtZRcAbWluZWNyYWZ0OnBvbGlzaGVkX3R1ZmYECQBuYW1lX2hhc2hyaLe/KEVZ0gMKAG5ldHdvcmtfaWTcX3NrCgYAc3RhdGVzAAMHAHZlcnNpb24BKBUBAA==" + }, + { + "id": "minecraft:polished_basalt", + "block_state_b64": "CgAAAwgAYmxvY2tfaWTqAQAACAQAbmFtZRkAbWluZWNyYWZ0OnBvbGlzaGVkX2Jhc2FsdAQJAG5hbWVfaGFzaMS+L0gMnRcBAwoAbmV0d29ya19pZF+/mHwKBgBzdGF0ZXMICwBwaWxsYXJfYXhpcwEAeQADBwB2ZXJzaW9uASgVAQA=" + }, + { + "id": "minecraft:smooth_basalt", + "block_state_b64": "CgAAAwgAYmxvY2tfaWR4AgAACAQAbmFtZRcAbWluZWNyYWZ0OnNtb290aF9iYXNhbHQECQBuYW1lX2hhc2jKPUdz89kuNAMKAG5ldHdvcmtfaWTkb/oVCgYAc3RhdGVzAAMHAHZlcnNpb24BKBUBAA==" + }, + { + "id": "minecraft:gravel", + "block_state_b64": "CgAAAwgAYmxvY2tfaWQNAAAACAQAbmFtZRAAbWluZWNyYWZ0OmdyYXZlbAQJAG5hbWVfaGFzaOFxz8XJd2r/AwoAbmV0d29ya19pZBpfI1sKBgBzdGF0ZXMAAwcAdmVyc2lvbgEoFQEA" + }, + { + "id": "minecraft:sand", + "block_state_b64": "CgAAAwgAYmxvY2tfaWQMAAAACAQAbmFtZQ4AbWluZWNyYWZ0OnNhbmQECQBuYW1lX2hhc2i6lthXXbAyWAMKAG5ldHdvcmtfaWRjeUMICgYAc3RhdGVzAAMHAHZlcnNpb24BKBUBAA==" + }, + { + "id": "minecraft:red_sand", + "block_state_b64": "CgAAAwgAYmxvY2tfaWS0BAAACAQAbmFtZRIAbWluZWNyYWZ0OnJlZF9zYW5kBAkAbmFtZV9oYXNoCiarI69JQCkDCgBuZXR3b3JrX2lkU8UD/AoGAHN0YXRlcwADBwB2ZXJzaW9uASgVAQA=" + }, + { + "id": "minecraft:cactus", + "block_state_b64": "CgAAAwgAYmxvY2tfaWRRAAAACAQAbmFtZRAAbWluZWNyYWZ0OmNhY3R1cwQJAG5hbWVfaGFzaCG9zL0N4wvGAwoAbmV0d29ya19pZDeCERAKBgBzdGF0ZXMDAwBhZ2UAAAAAAAMHAHZlcnNpb24BKBUBAA==" + }, + { + "id": "minecraft:oak_log", + "block_state_b64": "CgAAAwgAYmxvY2tfaWQRAAAACAQAbmFtZREAbWluZWNyYWZ0Om9ha19sb2cECQBuYW1lX2hhc2ho6TS+K7PZFQMKAG5ldHdvcmtfaWQjfjoxCgYAc3RhdGVzCAsAcGlsbGFyX2F4aXMBAHkAAwcAdmVyc2lvbgEoFQEA" + }, + { + "id": "minecraft:stripped_oak_log", + "block_state_b64": "CgAAAwgAYmxvY2tfaWQJAQAACAQAbmFtZRoAbWluZWNyYWZ0OnN0cmlwcGVkX29ha19sb2cECQBuYW1lX2hhc2h8dqh+OOHU4wMKAG5ldHdvcmtfaWSYKjdrCgYAc3RhdGVzCAsAcGlsbGFyX2F4aXMBAHkAAwcAdmVyc2lvbgEoFQEA" + }, + { + "id": "minecraft:spruce_log", + "block_state_b64": "CgAAAwgAYmxvY2tfaWQ4AwAACAQAbmFtZRQAbWluZWNyYWZ0OnNwcnVjZV9sb2cECQBuYW1lX2hhc2hZ03qaLoF3WgMKAG5ldHdvcmtfaWRlFD8eCgYAc3RhdGVzCAsAcGlsbGFyX2F4aXMBAHkAAwcAdmVyc2lvbgEoFQEA" + }, + { + "id": "minecraft:stripped_spruce_log", + "block_state_b64": "CgAAAwgAYmxvY2tfaWQEAQAACAQAbmFtZR0AbWluZWNyYWZ0OnN0cmlwcGVkX3NwcnVjZV9sb2cECQBuYW1lX2hhc2iNrhKjS5IyrgMKAG5ldHdvcmtfaWRQcEC3CgYAc3RhdGVzCAsAcGlsbGFyX2F4aXMBAHkAAwcAdmVyc2lvbgEoFQEA" + }, + { + "id": "minecraft:birch_log", + "block_state_b64": "CgAAAwgAYmxvY2tfaWQ5AwAACAQAbmFtZRMAbWluZWNyYWZ0OmJpcmNoX2xvZwQJAG5hbWVfaGFzaBUzT3NxsZAnAwoAbmV0d29ya19pZBKN3VQKBgBzdGF0ZXMICwBwaWxsYXJfYXhpcwEAeQADBwB2ZXJzaW9uASgVAQA=" + }, + { + "id": "minecraft:stripped_birch_log", + "block_state_b64": "CgAAAwgAYmxvY2tfaWQFAQAACAQAbmFtZRwAbWluZWNyYWZ0OnN0cmlwcGVkX2JpcmNoX2xvZwQJAG5hbWVfaGFzaCFKS4AeuSidAwoAbmV0d29ya19pZN0IONIKBgBzdGF0ZXMICwBwaWxsYXJfYXhpcwEAeQADBwB2ZXJzaW9uASgVAQA=" + }, + { + "id": "minecraft:jungle_log", + "block_state_b64": "CgAAAwgAYmxvY2tfaWQ6AwAACAQAbmFtZRQAbWluZWNyYWZ0Omp1bmdsZV9sb2cECQBuYW1lX2hhc2gkwW0KNulqDgMKAG5ldHdvcmtfaWQaziU/CgYAc3RhdGVzCAsAcGlsbGFyX2F4aXMBAHkAAwcAdmVyc2lvbgEoFQEA" + }, + { + "id": "minecraft:stripped_jungle_log", + "block_state_b64": "CgAAAwgAYmxvY2tfaWQGAQAACAQAbmFtZR0AbWluZWNyYWZ0OnN0cmlwcGVkX2p1bmdsZV9sb2cECQBuYW1lX2hhc2hAwMsgOk02JAMKAG5ldHdvcmtfaWQvls0eCgYAc3RhdGVzCAsAcGlsbGFyX2F4aXMBAHkAAwcAdmVyc2lvbgEoFQEA" + }, + { + "id": "minecraft:acacia_log", + "block_state_b64": "CgAAAwgAYmxvY2tfaWSiAAAACAQAbmFtZRQAbWluZWNyYWZ0OmFjYWNpYV9sb2cECQBuYW1lX2hhc2iV48VpYhjoYQMKAG5ldHdvcmtfaWRxEqe0CgYAc3RhdGVzCAsAcGlsbGFyX2F4aXMBAHkAAwcAdmVyc2lvbgEoFQEA" + }, + { + "id": "minecraft:stripped_acacia_log", + "block_state_b64": "CgAAAwgAYmxvY2tfaWQHAQAACAQAbmFtZR0AbWluZWNyYWZ0OnN0cmlwcGVkX2FjYWNpYV9sb2cECQBuYW1lX2hhc2hJb0lQqnEqlgMKAG5ldHdvcmtfaWRg3IdRCgYAc3RhdGVzCAsAcGlsbGFyX2F4aXMBAHkAAwcAdmVyc2lvbgEoFQEA" + }, + { + "id": "minecraft:dark_oak_log", + "block_state_b64": "CgAAAwgAYmxvY2tfaWQ7AwAACAQAbmFtZRYAbWluZWNyYWZ0OmRhcmtfb2FrX2xvZwQJAG5hbWVfaGFzaIWfVRd0XUo3AwoAbmV0d29ya19pZPMM7LYKBgBzdGF0ZXMICwBwaWxsYXJfYXhpcwEAeQADBwB2ZXJzaW9uASgVAQA=" + }, + { + "id": "minecraft:stripped_dark_oak_log", + "block_state_b64": "CgAAAwgAYmxvY2tfaWQIAQAACAQAbmFtZR8AbWluZWNyYWZ0OnN0cmlwcGVkX2Rhcmtfb2FrX2xvZwQJAG5hbWVfaGFzaPFTdxRdPwkOAwoAbmV0d29ya19pZDIzenIKBgBzdGF0ZXMICwBwaWxsYXJfYXhpcwEAeQADBwB2ZXJzaW9uASgVAQA=" + }, + { + "id": "minecraft:mangrove_log", + "block_state_b64": "CgAAAwgAYmxvY2tfaWTjAgAACAQAbmFtZRYAbWluZWNyYWZ0Om1hbmdyb3ZlX2xvZwQJAG5hbWVfaGFzaHZe6DzPZBobAwoAbmV0d29ya19pZG6DuYkKBgBzdGF0ZXMICwBwaWxsYXJfYXhpcwEAeQADBwB2ZXJzaW9uASgVAQA=" + }, + { + "id": "minecraft:stripped_mangrove_log", + "block_state_b64": "CgAAAwgAYmxvY2tfaWTkAgAACAQAbmFtZR8AbWluZWNyYWZ0OnN0cmlwcGVkX21hbmdyb3ZlX2xvZwQJAG5hbWVfaGFzaLqIBo4hwA//AwoAbmV0d29ya19pZPtRn7UKBgBzdGF0ZXMICwBwaWxsYXJfYXhpcwEAeQADBwB2ZXJzaW9uASgVAQA=" + }, + { + "id": "minecraft:cherry_log", + "block_state_b64": "CgAAAwgAYmxvY2tfaWQXAwAACAQAbmFtZRQAbWluZWNyYWZ0OmNoZXJyeV9sb2cECQBuYW1lX2hhc2hwFlaioppB1wMKAG5ldHdvcmtfaWS2sdXECgYAc3RhdGVzCAsAcGlsbGFyX2F4aXMBAHkAAwcAdmVyc2lvbgEoFQEA" + }, + { + "id": "minecraft:stripped_cherry_log", + "block_state_b64": "CgAAAwgAYmxvY2tfaWQWAwAACAQAbmFtZR0AbWluZWNyYWZ0OnN0cmlwcGVkX2NoZXJyeV9sb2cECQBuYW1lX2hhc2i85H6G+WhXaAMKAG5ldHdvcmtfaWRjzoglCgYAc3RhdGVzCAsAcGlsbGFyX2F4aXMBAHkAAwcAdmVyc2lvbgEoFQEA" + }, + { + "id": "minecraft:crimson_stem", + "block_state_b64": "CgAAAwgAYmxvY2tfaWTgAQAACAQAbmFtZRYAbWluZWNyYWZ0OmNyaW1zb25fc3RlbQQJAG5hbWVfaGFzaM0FzfL0UTKZAwoAbmV0d29ya19pZKvzID0KBgBzdGF0ZXMICwBwaWxsYXJfYXhpcwEAeQADBwB2ZXJzaW9uASgVAQA=" + }, + { + "id": "minecraft:stripped_crimson_stem", + "block_state_b64": "CgAAAwgAYmxvY2tfaWTvAQAACAQAbmFtZR8AbWluZWNyYWZ0OnN0cmlwcGVkX2NyaW1zb25fc3RlbQQJAG5hbWVfaGFzaDlA6nood57EAwoAbmV0d29ya19pZHrIqjIKBgBzdGF0ZXMICwBwaWxsYXJfYXhpcwEAeQADBwB2ZXJzaW9uASgVAQA=" + }, + { + "id": "minecraft:warped_stem", + "block_state_b64": "CgAAAwgAYmxvY2tfaWThAQAACAQAbmFtZRUAbWluZWNyYWZ0OndhcnBlZF9zdGVtBAkAbmFtZV9oYXNon7cKfPZxdrUDCgBuZXR3b3JrX2lkerWyMwoGAHN0YXRlcwgLAHBpbGxhcl9heGlzAQB5AAMHAHZlcnNpb24BKBUBAA==" + }, + { + "id": "minecraft:stripped_warped_stem", + "block_state_b64": "CgAAAwgAYmxvY2tfaWTwAQAACAQAbmFtZR4AbWluZWNyYWZ0OnN0cmlwcGVkX3dhcnBlZF9zdGVtBAkAbmFtZV9oYXNoEw+y0dDPSd8DCgBuZXR3b3JrX2lkIQ9vBAoGAHN0YXRlcwgLAHBpbGxhcl9heGlzAQB5AAMHAHZlcnNpb24BKBUBAA==" + }, + { + "id": "minecraft:oak_wood", + "block_state_b64": "CgAAAwgAYmxvY2tfaWTTAQAACAQAbmFtZRIAbWluZWNyYWZ0Om9ha193b29kBAkAbmFtZV9oYXNoqQIkuVPyJX0DCgBuZXR3b3JrX2lku2G1YAoGAHN0YXRlcwgLAHBpbGxhcl9heGlzAQB5AAMHAHZlcnNpb24BKBUBAA==" + }, + { + "id": "minecraft:stripped_oak_wood", + "block_state_b64": "CgAAAwgAYmxvY2tfaWQyBAAACAQAbmFtZRsAbWluZWNyYWZ0OnN0cmlwcGVkX29ha193b29kBAkAbmFtZV9oYXNovW6KCv+VZnsDCgBuZXR3b3JrX2lkkhWGegoGAHN0YXRlcwgLAHBpbGxhcl9heGlzAQB5AAMHAHZlcnNpb24BKBUBAA==" + }, + { + "id": "minecraft:spruce_wood", + "block_state_b64": "CgAAAwgAYmxvY2tfaWQtBAAACAQAbmFtZRUAbWluZWNyYWZ0OnNwcnVjZV93b29kBAkAbmFtZV9oYXNoTrIJ5TAQ+OgDCgBuZXR3b3JrX2lkaXLxCwoGAHN0YXRlcwgLAHBpbGxhcl9heGlzAQB5AAMHAHZlcnNpb24BKBUBAA==" + }, + { + "id": "minecraft:stripped_spruce_wood", + "block_state_b64": "CgAAAwgAYmxvY2tfaWQzBAAACAQAbmFtZR4AbWluZWNyYWZ0OnN0cmlwcGVkX3NwcnVjZV93b29kBAkAbmFtZV9oYXNoMnuUk4Xo6icDCgBuZXR3b3JrX2lkes2ydAoGAHN0YXRlcwgLAHBpbGxhcl9heGlzAQB5AAMHAHZlcnNpb24BKBUBAA==" + }, + { + "id": "minecraft:birch_wood", + "block_state_b64": "CgAAAwgAYmxvY2tfaWQuBAAACAQAbmFtZRQAbWluZWNyYWZ0OmJpcmNoX3dvb2QECQBuYW1lX2hhc2iqVjG4xt0cKQMKAG5ldHdvcmtfaWS06c5VCgYAc3RhdGVzCAsAcGlsbGFyX2F4aXMBAHkAAwcAdmVyc2lvbgEoFQEA" + }, + { + "id": "minecraft:stripped_birch_wood", + "block_state_b64": "CgAAAwgAYmxvY2tfaWQ0BAAACAQAbmFtZR0AbWluZWNyYWZ0OnN0cmlwcGVkX2JpcmNoX3dvb2QECQBuYW1lX2hhc2hm88R604TKbAMKAG5ldHdvcmtfaWRleEMJCgYAc3RhdGVzCAsAcGlsbGFyX2F4aXMBAHkAAwcAdmVyc2lvbgEoFQEA" + }, + { + "id": "minecraft:jungle_wood", + "block_state_b64": "CgAAAwgAYmxvY2tfaWQvBAAACAQAbmFtZRUAbWluZWNyYWZ0Omp1bmdsZV93b29kBAkAbmFtZV9oYXNo9bYW29ORWCoDCgBuZXR3b3JrX2lkyFyKLQoGAHN0YXRlcwgLAHBpbGxhcl9heGlzAQB5AAMHAHZlcnNpb24BKBUBAA==" + }, + { + "id": "minecraft:stripped_jungle_wood", + "block_state_b64": "CgAAAwgAYmxvY2tfaWQ1BAAACAQAbmFtZR4AbWluZWNyYWZ0OnN0cmlwcGVkX2p1bmdsZV93b29kBAkAbmFtZV9oYXNoUVs6KsZQRBoDCgBuZXR3b3JrX2lk92k8HQoGAHN0YXRlcwgLAHBpbGxhcl9heGlzAQB5AAMHAHZlcnNpb24BKBUBAA==" + }, + { + "id": "minecraft:acacia_wood", + "block_state_b64": "CgAAAwgAYmxvY2tfaWQwBAAACAQAbmFtZRUAbWluZWNyYWZ0OmFjYWNpYV93b29kBAkAbmFtZV9oYXNoKkDfgzlJUcIDCgBuZXR3b3JrX2lkuTWlcgoGAHN0YXRlcwgLAHBpbGxhcl9heGlzAQB5AAMHAHZlcnNpb24BKBUBAA==" + }, + { + "id": "minecraft:stripped_acacia_wood", + "block_state_b64": "CgAAAwgAYmxvY2tfaWQ2BAAACAQAbmFtZR4AbWluZWNyYWZ0OnN0cmlwcGVkX2FjYWNpYV93b29kBAkAbmFtZV9oYXNo/kOPN2bCJhUDCgBuZXR3b3JrX2lktl6LwQoGAHN0YXRlcwgLAHBpbGxhcl9heGlzAQB5AAMHAHZlcnNpb24BKBUBAA==" + }, + { + "id": "minecraft:dark_oak_wood", + "block_state_b64": "CgAAAwgAYmxvY2tfaWQxBAAACAQAbmFtZRcAbWluZWNyYWZ0OmRhcmtfb2FrX3dvb2QECQBuYW1lX2hhc2jaKv4ORLadAAMKAG5ldHdvcmtfaWSDrNQ8CgYAc3RhdGVzCAsAcGlsbGFyX2F4aXMBAHkAAwcAdmVyc2lvbgEoFQEA" + }, + { + "id": "minecraft:stripped_dark_oak_wood", + "block_state_b64": "CgAAAwgAYmxvY2tfaWQ3BAAACAQAbmFtZSAAbWluZWNyYWZ0OnN0cmlwcGVkX2Rhcmtfb2FrX3dvb2QECQBuYW1lX2hhc2h2jFDfKVFgfAMKAG5ldHdvcmtfaWTgZQ5VCgYAc3RhdGVzCAsAcGlsbGFyX2F4aXMBAHkAAwcAdmVyc2lvbgEoFQEA" + }, + { + "id": "minecraft:mangrove_wood", + "block_state_b64": "CgAAAwgAYmxvY2tfaWTwAgAACAQAbmFtZRcAbWluZWNyYWZ0Om1hbmdyb3ZlX3dvb2QECQBuYW1lX2hhc2iXVxG0JG2fVAMKAG5ldHdvcmtfaWSkqJ4cCgYAc3RhdGVzCAsAcGlsbGFyX2F4aXMBAHkAAwcAdmVyc2lvbgEoFQEA" + }, + { + "id": "minecraft:stripped_mangrove_wood", + "block_state_b64": "CgAAAwgAYmxvY2tfaWTxAgAACAQAbmFtZSAAbWluZWNyYWZ0OnN0cmlwcGVkX21hbmdyb3ZlX3dvb2QECQBuYW1lX2hhc2h7CkbaBF7/WAMKAG5ldHdvcmtfaWQLAX88CgYAc3RhdGVzCAsAcGlsbGFyX2F4aXMBAHkAAwcAdmVyc2lvbgEoFQEA" + }, + { + "id": "minecraft:cherry_wood", + "block_state_b64": "CgAAAwgAYmxvY2tfaWQhAwAACAQAbmFtZRUAbWluZWNyYWZ0OmNoZXJyeV93b29kBAkAbmFtZV9oYXNoAW8srlmpBM8DCgBuZXR3b3JrX2lkLPsAwgoGAHN0YXRlcwgLAHBpbGxhcl9heGlzAQB5AAMHAHZlcnNpb24BKBUBAA==" + }, + { + "id": "minecraft:stripped_cherry_wood", + "block_state_b64": "CgAAAwgAYmxvY2tfaWQgAwAACAQAbmFtZR4AbWluZWNyYWZ0OnN0cmlwcGVkX2NoZXJyeV93b29kBAkAbmFtZV9oYXNo/e7KXv+CB38DCgBuZXR3b3JrX2lkg5aVtQoGAHN0YXRlcwgLAHBpbGxhcl9heGlzAQB5AAMHAHZlcnNpb24BKBUBAA==" + }, + { + "id": "minecraft:crimson_hyphae", + "block_state_b64": "CgAAAwgAYmxvY2tfaWQqAgAACAQAbmFtZRgAbWluZWNyYWZ0OmNyaW1zb25faHlwaGFlBAkAbmFtZV9oYXNouRmKmfSqEWADCgBuZXR3b3JrX2lk+Tm5rQoGAHN0YXRlcwgLAHBpbGxhcl9heGlzAQB5AAMHAHZlcnNpb24BKBUBAA==" + }, + { + "id": "minecraft:stripped_crimson_hyphae", + "block_state_b64": "CgAAAwgAYmxvY2tfaWQrAgAACAQAbmFtZSEAbWluZWNyYWZ0OnN0cmlwcGVkX2NyaW1zb25faHlwaGFlBAkAbmFtZV9oYXNoFffwmABq4LUDCgBuZXR3b3JrX2lkZAlUbgoGAHN0YXRlcwgLAHBpbGxhcl9heGlzAQB5AAMHAHZlcnNpb24BKBUBAA==" + }, + { + "id": "minecraft:warped_hyphae", + "block_state_b64": "CgAAAwgAYmxvY2tfaWQpAgAACAQAbmFtZRcAbWluZWNyYWZ0OndhcnBlZF9oeXBoYWUECQBuYW1lX2hhc2hn8plQUr6pmQMKAG5ldHdvcmtfaWRU2AIBCgYAc3RhdGVzCAsAcGlsbGFyX2F4aXMBAHkAAwcAdmVyc2lvbgEoFQEA" + }, + { + "id": "minecraft:stripped_warped_hyphae", + "block_state_b64": "CgAAAwgAYmxvY2tfaWQsAgAACAQAbmFtZSAAbWluZWNyYWZ0OnN0cmlwcGVkX3dhcnBlZF9oeXBoYWUECQBuYW1lX2hhc2irKq+HYPSgjQMKAG5ldHdvcmtfaWSbrOPDCgYAc3RhdGVzCAsAcGlsbGFyX2F4aXMBAHkAAwcAdmVyc2lvbgEoFQEA" + }, + { + "id": "minecraft:bamboo_block", + "block_state_b64": "CgAAAwgAYmxvY2tfaWQOAwAACAQAbmFtZRYAbWluZWNyYWZ0OmJhbWJvb19ibG9jawQJAG5hbWVfaGFzaAbDeur6stIBAwoAbmV0d29ya19pZCJAwn0KBgBzdGF0ZXMICwBwaWxsYXJfYXhpcwEAeQADBwB2ZXJzaW9uASgVAQA=" + }, + { + "id": "minecraft:stripped_bamboo_block", + "block_state_b64": "CgAAAwgAYmxvY2tfaWQPAwAACAQAbmFtZR8AbWluZWNyYWZ0OnN0cmlwcGVkX2JhbWJvb19ibG9jawQJAG5hbWVfaGFzaJpwytpZOZM9AwoAbmV0d29ya19pZKuRbNEKBgBzdGF0ZXMICwBwaWxsYXJfYXhpcwEAeQADBwB2ZXJzaW9uASgVAQA=" + }, + { + "id": "minecraft:oak_leaves", + "block_state_b64": "CgAAAwgAYmxvY2tfaWQSAAAACAQAbmFtZRQAbWluZWNyYWZ0Om9ha19sZWF2ZXMECQBuYW1lX2hhc2h6O4xGqA2oKgMKAG5ldHdvcmtfaWT98c59CgYAc3RhdGVzAQ4AcGVyc2lzdGVudF9iaXQAAQoAdXBkYXRlX2JpdAAAAwcAdmVyc2lvbgEoFQEA" + }, + { + "id": "minecraft:spruce_leaves", + "block_state_b64": "CgAAAwgAYmxvY2tfaWQfBAAACAQAbmFtZRcAbWluZWNyYWZ0OnNwcnVjZV9sZWF2ZXMECQBuYW1lX2hhc2i9x1CtNAuqZwMKAG5ldHdvcmtfaWSzF7pTCgYAc3RhdGVzAQ4AcGVyc2lzdGVudF9iaXQAAQoAdXBkYXRlX2JpdAAAAwcAdmVyc2lvbgEoFQEA" + }, + { + "id": "minecraft:birch_leaves", + "block_state_b64": "CgAAAwgAYmxvY2tfaWQgBAAACAQAbmFtZRYAbWluZWNyYWZ0OmJpcmNoX2xlYXZlcwQJAG5hbWVfaGFzaBlAGHaoaLZSAwoAbmV0d29ya19pZOjtvWcKBgBzdGF0ZXMBDgBwZXJzaXN0ZW50X2JpdAABCgB1cGRhdGVfYml0AAADBwB2ZXJzaW9uASgVAQA=" + }, + { + "id": "minecraft:jungle_leaves", + "block_state_b64": "CgAAAwgAYmxvY2tfaWQhBAAACAQAbmFtZRcAbWluZWNyYWZ0Omp1bmdsZV9sZWF2ZXMECQBuYW1lX2hhc2iW1uAH07zGhgMKAG5ldHdvcmtfaWSA5KX0CgYAc3RhdGVzAQ4AcGVyc2lzdGVudF9iaXQAAQoAdXBkYXRlX2JpdAAAAwcAdmVyc2lvbgEoFQEA" + }, + { + "id": "minecraft:acacia_leaves", + "block_state_b64": "CgAAAwgAYmxvY2tfaWShAAAACAQAbmFtZRcAbWluZWNyYWZ0OmFjYWNpYV9sZWF2ZXMECQBuYW1lX2hhc2iZJf8dAgDRNQMKAG5ldHdvcmtfaWQ/G7VuCgYAc3RhdGVzAQ4AcGVyc2lzdGVudF9iaXQAAQoAdXBkYXRlX2JpdAAAAwcAdmVyc2lvbgEoFQEA" + }, + { + "id": "minecraft:dark_oak_leaves", + "block_state_b64": "CgAAAwgAYmxvY2tfaWQiBAAACAQAbmFtZRkAbWluZWNyYWZ0OmRhcmtfb2FrX2xlYXZlcwQJAG5hbWVfaGFzaCk7rDipWFSjAwoAbmV0d29ya19pZJ2AkbYKBgBzdGF0ZXMBDgBwZXJzaXN0ZW50X2JpdAABCgB1cGRhdGVfYml0AAADBwB2ZXJzaW9uASgVAQA=" + }, + { + "id": "minecraft:mangrove_leaves", + "block_state_b64": "CgAAAwgAYmxvY2tfaWTXAgAACAQAbmFtZRkAbWluZWNyYWZ0Om1hbmdyb3ZlX2xlYXZlcwQJAG5hbWVfaGFzaKyI/dWvhEG8AwoAbmV0d29ya19pZPQxCZ8KBgBzdGF0ZXMBDgBwZXJzaXN0ZW50X2JpdAABCgB1cGRhdGVfYml0AAADBwB2ZXJzaW9uASgVAQA=" + }, + { + "id": "minecraft:cherry_leaves", + "block_state_b64": "CgAAAwgAYmxvY2tfaWQjAwAACAQAbmFtZRcAbWluZWNyYWZ0OmNoZXJyeV9sZWF2ZXMECQBuYW1lX2hhc2giTs9ChhYBlQMKAG5ldHdvcmtfaWR8bPpwCgYAc3RhdGVzAQ4AcGVyc2lzdGVudF9iaXQAAQoAdXBkYXRlX2JpdAAAAwcAdmVyc2lvbgEoFQEA" + }, + { + "id": "minecraft:azalea_leaves", + "block_state_b64": "CgAAAwgAYmxvY2tfaWRDAgAACAQAbmFtZRcAbWluZWNyYWZ0OmF6YWxlYV9sZWF2ZXMECQBuYW1lX2hhc2iXFhD57wFS7AMKAG5ldHdvcmtfaWTNB/9ECgYAc3RhdGVzAQ4AcGVyc2lzdGVudF9iaXQAAQoAdXBkYXRlX2JpdAAAAwcAdmVyc2lvbgEoFQEA" + }, + { + "id": "minecraft:azalea_leaves_flowered", + "block_state_b64": "CgAAAwgAYmxvY2tfaWREAgAACAQAbmFtZSAAbWluZWNyYWZ0OmF6YWxlYV9sZWF2ZXNfZmxvd2VyZWQECQBuYW1lX2hhc2gs8jxlS/pMrwMKAG5ldHdvcmtfaWQ7W4PyCgYAc3RhdGVzAQ4AcGVyc2lzdGVudF9iaXQAAQoAdXBkYXRlX2JpdAAAAwcAdmVyc2lvbgEoFQEA" + }, + { + "id": "minecraft:oak_sapling", + "block_state_b64": "CgAAAwgAYmxvY2tfaWQGAAAACAQAbmFtZRUAbWluZWNyYWZ0Om9ha19zYXBsaW5nBAkAbmFtZV9oYXNoogXcT9QfjiUDCgBuZXR3b3JrX2lkG22C+AoGAHN0YXRlcwEHAGFnZV9iaXQAAAMHAHZlcnNpb24BKBUBAA==" + }, + { + "id": "minecraft:spruce_sapling", + "block_state_b64": "CgAAAwgAYmxvY2tfaWQ4BAAACAQAbmFtZRgAbWluZWNyYWZ0OnNwcnVjZV9zYXBsaW5nBAkAbmFtZV9oYXNoe8hz4uYP0FcDCgBuZXR3b3JrX2lkUQmhaQoGAHN0YXRlcwEHAGFnZV9iaXQAAAMHAHZlcnNpb24BKBUBAA==" + }, + { + "id": "minecraft:birch_sapling", + "block_state_b64": "CgAAAwgAYmxvY2tfaWQ5BAAACAQAbmFtZRcAbWluZWNyYWZ0OmJpcmNoX3NhcGxpbmcECQBuYW1lX2hhc2h348iJQ/tK4wMKAG5ldHdvcmtfaWQ2Uh53CgYAc3RhdGVzAQcAYWdlX2JpdAAAAwcAdmVyc2lvbgEoFQEA" + }, + { + "id": "minecraft:jungle_sapling", + "block_state_b64": "CgAAAwgAYmxvY2tfaWQ6BAAACAQAbmFtZRgAbWluZWNyYWZ0Omp1bmdsZV9zYXBsaW5nBAkAbmFtZV9oYXNo7tyTOdSrxaADCgBuZXR3b3JrX2lkXmBAdAoGAHN0YXRlcwEHAGFnZV9iaXQAAAMHAHZlcnNpb24BKBUBAA==" + }, + { + "id": "minecraft:acacia_sapling", + "block_state_b64": "CgAAAwgAYmxvY2tfaWQ7BAAACAQAbmFtZRgAbWluZWNyYWZ0OmFjYWNpYV9zYXBsaW5nBAkAbmFtZV9oYXNo99sg15uoX7ADCgBuZXR3b3JrX2lkPXX1KgoGAHN0YXRlcwEHAGFnZV9iaXQAAAMHAHZlcnNpb24BKBUBAA==" + }, + { + "id": "minecraft:dark_oak_sapling", + "block_state_b64": "CgAAAwgAYmxvY2tfaWQ8BAAACAQAbmFtZRoAbWluZWNyYWZ0OmRhcmtfb2FrX3NhcGxpbmcECQBuYW1lX2hhc2jnVzFplW7cHgMKAG5ldHdvcmtfaWTD4giHCgYAc3RhdGVzAQcAYWdlX2JpdAAAAwcAdmVyc2lvbgEoFQEA" + }, + { + "id": "minecraft:mangrove_propagule", + "block_state_b64": "CgAAAwgAYmxvY2tfaWTZAgAACAQAbmFtZRwAbWluZWNyYWZ0Om1hbmdyb3ZlX3Byb3BhZ3VsZQQJAG5hbWVfaGFzaJGeox6hkfLFAwoAbmV0d29ya19pZAIpvpYKBgBzdGF0ZXMBBwBoYW5naW5nAAMPAHByb3BhZ3VsZV9zdGFnZQAAAAAAAwcAdmVyc2lvbgEoFQEA" + }, + { + "id": "minecraft:cherry_sapling", + "block_state_b64": "CgAAAwgAYmxvY2tfaWQiAwAACAQAbmFtZRgAbWluZWNyYWZ0OmNoZXJyeV9zYXBsaW5nBAkAbmFtZV9oYXNoGrPpNMf1LtcDCgBuZXR3b3JrX2lkypakXQoGAHN0YXRlcwEHAGFnZV9iaXQAAAMHAHZlcnNpb24BKBUBAA==" + }, + { + "id": "minecraft:bee_nest", + "block_state_b64": "CgAAAwgAYmxvY2tfaWTZAQAACAQAbmFtZRIAbWluZWNyYWZ0OmJlZV9uZXN0BAkAbmFtZV9oYXNo2R2WBxUHEZIDCgBuZXR3b3JrX2lkiXWLEAoGAHN0YXRlcwMJAGRpcmVjdGlvbgAAAAADCwBob25leV9sZXZlbAAAAAAAAwcAdmVyc2lvbgEoFQEA" + }, + { + "id": "minecraft:wheat_seeds" + }, + { + "id": "minecraft:pumpkin_seeds" + }, + { + "id": "minecraft:melon_seeds" + }, + { + "id": "minecraft:beetroot_seeds" + }, + { + "id": "minecraft:torchflower_seeds" + }, + { + "id": "minecraft:pitcher_pod" + }, + { + "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", + "block_state_b64": "CgAAAwgAYmxvY2tfaWRnAAAACAQAbmFtZRUAbWluZWNyYWZ0Om1lbG9uX2Jsb2NrBAkAbmFtZV9oYXNoXxSm0iYpAx8DCgBuZXR3b3JrX2lkC9rqygoGAHN0YXRlcwADBwB2ZXJzaW9uASgVAQA=" + }, + { + "id": "minecraft:melon_slice" + }, + { + "id": "minecraft:glistering_melon_slice" + }, + { + "id": "minecraft:sweet_berries" + }, + { + "id": "minecraft:glow_berries" + }, + { + "id": "minecraft:pumpkin", + "block_state_b64": "CgAAAwgAYmxvY2tfaWRWAAAACAQAbmFtZREAbWluZWNyYWZ0OnB1bXBraW4ECQBuYW1lX2hhc2gc8A3jaSzWbgMKAG5ldHdvcmtfaWRFGA+xCgYAc3RhdGVzCBwAbWluZWNyYWZ0OmNhcmRpbmFsX2RpcmVjdGlvbgUAc291dGgAAwcAdmVyc2lvbgEoFQEA" + }, + { + "id": "minecraft:carved_pumpkin", + "block_state_b64": "CgAAAwgAYmxvY2tfaWSaAQAACAQAbmFtZRgAbWluZWNyYWZ0OmNhcnZlZF9wdW1wa2luBAkAbmFtZV9oYXNoPu1T0MJuG90DCgBuZXR3b3JrX2lkXNNn5QoGAHN0YXRlcwgcAG1pbmVjcmFmdDpjYXJkaW5hbF9kaXJlY3Rpb24FAHNvdXRoAAMHAHZlcnNpb24BKBUBAA==" + }, + { + "id": "minecraft:lit_pumpkin", + "block_state_b64": "CgAAAwgAYmxvY2tfaWRbAAAACAQAbmFtZRUAbWluZWNyYWZ0OmxpdF9wdW1wa2luBAkAbmFtZV9oYXNo7gWtEm2uPL0DCgBuZXR3b3JrX2lki8sU4AoGAHN0YXRlcwgcAG1pbmVjcmFmdDpjYXJkaW5hbF9kaXJlY3Rpb24FAHNvdXRoAAMHAHZlcnNpb24BKBUBAA==" + }, + { + "id": "minecraft:honeycomb" + }, + { + "id": "minecraft:fern", + "block_state_b64": "CgAAAwgAYmxvY2tfaWRPBAAACAQAbmFtZQ4AbWluZWNyYWZ0OmZlcm4ECQBuYW1lX2hhc2iHbj3yXFn4owMKAG5ldHdvcmtfaWQKC6u7CgYAc3RhdGVzAAMHAHZlcnNpb24BKBUBAA==" + }, + { + "id": "minecraft:large_fern", + "block_state_b64": "CgAAAwgAYmxvY2tfaWRgBAAACAQAbmFtZRQAbWluZWNyYWZ0OmxhcmdlX2Zlcm4ECQBuYW1lX2hhc2gnE9sd0LzHtQMKAG5ldHdvcmtfaWTS9hG4CgYAc3RhdGVzAQ8AdXBwZXJfYmxvY2tfYml0AAADBwB2ZXJzaW9uASgVAQA=" + }, + { + "id": "minecraft:short_grass", + "block_state_b64": "CgAAAwgAYmxvY2tfaWQfAAAACAQAbmFtZRUAbWluZWNyYWZ0OnNob3J0X2dyYXNzBAkAbmFtZV9oYXNobWQghLH0bLcDCgBuZXR3b3JrX2lkJWOOqAoGAHN0YXRlcwADBwB2ZXJzaW9uASgVAQA=" + }, + { + "id": "minecraft:tall_grass", + "block_state_b64": "CgAAAwgAYmxvY2tfaWRfBAAACAQAbmFtZRQAbWluZWNyYWZ0OnRhbGxfZ3Jhc3MECQBuYW1lX2hhc2ii5MyZJpv4sgMKAG5ldHdvcmtfaWRRfeH4CgYAc3RhdGVzAQ8AdXBwZXJfYmxvY2tfYml0AAADBwB2ZXJzaW9uASgVAQA=" + }, + { + "id": "minecraft:nether_sprouts" + }, + { + "id": "minecraft:fire_coral", + "block_state_b64": "CgAAAwgAYmxvY2tfaWRGAwAACAQAbmFtZRQAbWluZWNyYWZ0OmZpcmVfY29yYWwECQBuYW1lX2hhc2hOHyyECVQVJwMKAG5ldHdvcmtfaWS9vF0UCgYAc3RhdGVzAAMHAHZlcnNpb24BKBUBAA==" + }, + { + "id": "minecraft:brain_coral", + "block_state_b64": "CgAAAwgAYmxvY2tfaWREAwAACAQAbmFtZRUAbWluZWNyYWZ0OmJyYWluX2NvcmFsBAkAbmFtZV9oYXNoRiWlLCwA2ycDCgBuZXR3b3JrX2lkrjAuhgoGAHN0YXRlcwADBwB2ZXJzaW9uASgVAQA=" + }, + { + "id": "minecraft:bubble_coral", + "block_state_b64": "CgAAAwgAYmxvY2tfaWRFAwAACAQAbmFtZRYAbWluZWNyYWZ0OmJ1YmJsZV9jb3JhbAQJAG5hbWVfaGFzaJz6rWnl+v2qAwoAbmV0d29ya19pZImIWy0KBgBzdGF0ZXMAAwcAdmVyc2lvbgEoFQEA" + }, + { + "id": "minecraft:tube_coral", + "block_state_b64": "CgAAAwgAYmxvY2tfaWSCAQAACAQAbmFtZRQAbWluZWNyYWZ0OnR1YmVfY29yYWwECQBuYW1lX2hhc2iYa8oO/tgk7wMKAG5ldHdvcmtfaWRTfND5CgYAc3RhdGVzAAMHAHZlcnNpb24BKBUBAA==" + }, + { + "id": "minecraft:horn_coral", + "block_state_b64": "CgAAAwgAYmxvY2tfaWRHAwAACAQAbmFtZRQAbWluZWNyYWZ0Omhvcm5fY29yYWwECQBuYW1lX2hhc2iZnRHjZbnLPgMKAG5ldHdvcmtfaWR+GGp8CgYAc3RhdGVzAAMHAHZlcnNpb24BKBUBAA==" + }, + { + "id": "minecraft:dead_fire_coral", + "block_state_b64": "CgAAAwgAYmxvY2tfaWRLAwAACAQAbmFtZRkAbWluZWNyYWZ0OmRlYWRfZmlyZV9jb3JhbAQJAG5hbWVfaGFzaEPU6tFy/latAwoAbmV0d29ya19pZNMa7V4KBgBzdGF0ZXMAAwcAdmVyc2lvbgEoFQEA" + }, + { + "id": "minecraft:dead_brain_coral", + "block_state_b64": "CgAAAwgAYmxvY2tfaWRJAwAACAQAbmFtZRoAbWluZWNyYWZ0OmRlYWRfYnJhaW5fY29yYWwECQBuYW1lX2hhc2j5L6QJCISvzwMKAG5ldHdvcmtfaWQkKzeiCgYAc3RhdGVzAAMHAHZlcnNpb24BKBUBAA==" + }, + { + "id": "minecraft:dead_bubble_coral", + "block_state_b64": "CgAAAwgAYmxvY2tfaWRKAwAACAQAbmFtZRsAbWluZWNyYWZ0OmRlYWRfYnViYmxlX2NvcmFsBAkAbmFtZV9oYXNoSTOZ/8wpeNYDCgBuZXR3b3JrX2lka6w9DAoGAHN0YXRlcwADBwB2ZXJzaW9uASgVAQA=" + }, + { + "id": "minecraft:dead_tube_coral", + "block_state_b64": "CgAAAwgAYmxvY2tfaWRIAwAACAQAbmFtZRkAbWluZWNyYWZ0OmRlYWRfdHViZV9jb3JhbAQJAG5hbWVfaGFzaJGjNWhlaIJeAwoAbmV0d29ya19pZO3Z0ygKBgBzdGF0ZXMAAwcAdmVyc2lvbgEoFQEA" + }, + { + "id": "minecraft:dead_horn_coral", + "block_state_b64": "CgAAAwgAYmxvY2tfaWRMAwAACAQAbmFtZRkAbWluZWNyYWZ0OmRlYWRfaG9ybl9jb3JhbAQJAG5hbWVfaGFzaJBkz3qt+g2cAwoAbmV0d29ya19pZBAN+eYKBgBzdGF0ZXMAAwcAdmVyc2lvbgEoFQEA" + }, + { + "id": "minecraft:fire_coral_fan", + "block_state_b64": "CgAAAwgAYmxvY2tfaWRJBAAACAQAbmFtZRgAbWluZWNyYWZ0OmZpcmVfY29yYWxfZmFuBAkAbmFtZV9oYXNosOTxYYxsDLgDCgBuZXR3b3JrX2lkFKxbEgoGAHN0YXRlcwMTAGNvcmFsX2Zhbl9kaXJlY3Rpb24AAAAAAAMHAHZlcnNpb24BKBUBAA==" + }, + { + "id": "minecraft:brain_coral_fan", + "block_state_b64": "CgAAAwgAYmxvY2tfaWRHBAAACAQAbmFtZRkAbWluZWNyYWZ0OmJyYWluX2NvcmFsX2ZhbgQJAG5hbWVfaGFzaAi5uHizSNcqAwoAbmV0d29ya19pZFtLjNwKBgBzdGF0ZXMDEwBjb3JhbF9mYW5fZGlyZWN0aW9uAAAAAAADBwB2ZXJzaW9uASgVAQA=" + }, + { + "id": "minecraft:bubble_coral_fan", + "block_state_b64": "CgAAAwgAYmxvY2tfaWRIBAAACAQAbmFtZRoAbWluZWNyYWZ0OmJ1YmJsZV9jb3JhbF9mYW4ECQBuYW1lX2hhc2hy/rX2on17DgMKAG5ldHdvcmtfaWQof60VCgYAc3RhdGVzAxMAY29yYWxfZmFuX2RpcmVjdGlvbgAAAAAAAwcAdmVyc2lvbgEoFQEA" + }, + { + "id": "minecraft:tube_coral_fan", + "block_state_b64": "CgAAAwgAYmxvY2tfaWSEAQAACAQAbmFtZRgAbWluZWNyYWZ0OnR1YmVfY29yYWxfZmFuBAkAbmFtZV9oYXNo9pbJbo+PphIDCgBuZXR3b3JrX2lkenDTYgoGAHN0YXRlcwMTAGNvcmFsX2Zhbl9kaXJlY3Rpb24AAAAAAAMHAHZlcnNpb24BKBUBAA==" + }, + { + "id": "minecraft:horn_coral_fan", + "block_state_b64": "CgAAAwgAYmxvY2tfaWRKBAAACAQAbmFtZRgAbWluZWNyYWZ0Omhvcm5fY29yYWxfZmFuBAkAbmFtZV9oYXNoA+ri6NPDkbUDCgBuZXR3b3JrX2lkezoHNwoGAHN0YXRlcwMTAGNvcmFsX2Zhbl9kaXJlY3Rpb24AAAAAAAMHAHZlcnNpb24BKBUBAA==" + }, + { + "id": "minecraft:dead_fire_coral_fan", + "block_state_b64": "CgAAAwgAYmxvY2tfaWRNBAAACAQAbmFtZR0AbWluZWNyYWZ0OmRlYWRfZmlyZV9jb3JhbF9mYW4ECQBuYW1lX2hhc2hpQO02NDxPvwMKAG5ldHdvcmtfaWTaOJgLCgYAc3RhdGVzAxMAY29yYWxfZmFuX2RpcmVjdGlvbgAAAAAAAwcAdmVyc2lvbgEoFQEA" + }, + { + "id": "minecraft:dead_brain_coral_fan", + "block_state_b64": "CgAAAwgAYmxvY2tfaWRLBAAACAQAbmFtZR4AbWluZWNyYWZ0OmRlYWRfYnJhaW5fY29yYWxfZmFuBAkAbmFtZV9oYXNoI9/+Z4YqMhIDCgBuZXR3b3JrX2lkqYXxYgoGAHN0YXRlcwMTAGNvcmFsX2Zhbl9kaXJlY3Rpb24AAAAAAAMHAHZlcnNpb24BKBUBAA==" + }, + { + "id": "minecraft:dead_bubble_coral_fan", + "block_state_b64": "CgAAAwgAYmxvY2tfaWRMBAAACAQAbmFtZR8AbWluZWNyYWZ0OmRlYWRfYnViYmxlX2NvcmFsX2ZhbgQJAG5hbWVfaGFzaBNECtIM6VIOAwoAbmV0d29ya19pZLrNtBEKBgBzdGF0ZXMDEwBjb3JhbF9mYW5fZGlyZWN0aW9uAAAAAAADBwB2ZXJzaW9uASgVAQA=" + }, + { + "id": "minecraft:dead_tube_coral_fan", + "block_state_b64": "CgAAAwgAYmxvY2tfaWSFAQAACAQAbmFtZR0AbWluZWNyYWZ0OmRlYWRfdHViZV9jb3JhbF9mYW4ECQBuYW1lX2hhc2hbBBM9jFKWvQMKAG5ldHdvcmtfaWSkJKUWCgYAc3RhdGVzAxMAY29yYWxfZmFuX2RpcmVjdGlvbgAAAAAAAwcAdmVyc2lvbgEoFQEA" + }, + { + "id": "minecraft:dead_horn_coral_fan", + "block_state_b64": "CgAAAwgAYmxvY2tfaWROBAAACAQAbmFtZR0AbWluZWNyYWZ0OmRlYWRfaG9ybl9jb3JhbF9mYW4ECQBuYW1lX2hhc2hObElFrHfPygMKAG5ldHdvcmtfaWQ1ZxvmCgYAc3RhdGVzAxMAY29yYWxfZmFuX2RpcmVjdGlvbgAAAAAAAwcAdmVyc2lvbgEoFQEA" + }, + { + "id": "minecraft:crimson_roots", + "block_state_b64": "CgAAAwgAYmxvY2tfaWTeAQAACAQAbmFtZRcAbWluZWNyYWZ0OmNyaW1zb25fcm9vdHMECQBuYW1lX2hhc2j1fWgQLViv5QMKAG5ldHdvcmtfaWRLh5DXCgYAc3RhdGVzAAMHAHZlcnNpb24BKBUBAA==" + }, + { + "id": "minecraft:warped_roots", + "block_state_b64": "CgAAAwgAYmxvY2tfaWTfAQAACAQAbmFtZRYAbWluZWNyYWZ0OndhcnBlZF9yb290cwQJAG5hbWVfaGFzaBc3WvbJOLlkAwoAbmV0d29ya19pZNLgDnAKBgBzdGF0ZXMAAwcAdmVyc2lvbgEoFQEA" + }, + { + "id": "minecraft:dandelion", + "block_state_b64": "CgAAAwgAYmxvY2tfaWQlAAAACAQAbmFtZRMAbWluZWNyYWZ0OmRhbmRlbGlvbgQJAG5hbWVfaGFzaBJ3bEUi+Nn/AwoAbmV0d29ya19pZBjjC44KBgBzdGF0ZXMAAwcAdmVyc2lvbgEoFQEA" + }, + { + "id": "minecraft:poppy", + "block_state_b64": "CgAAAwgAYmxvY2tfaWQmAAAACAQAbmFtZQ8AbWluZWNyYWZ0OnBvcHB5BAkAbmFtZV9oYXNocMF8pITMbkcDCgBuZXR3b3JrX2lk8im6ywoGAHN0YXRlcwADBwB2ZXJzaW9uASgVAQA=" + }, + { + "id": "minecraft:blue_orchid", + "block_state_b64": "CgAAAwgAYmxvY2tfaWQ9BAAACAQAbmFtZRUAbWluZWNyYWZ0OmJsdWVfb3JjaGlkBAkAbmFtZV9oYXNoBjz2MsgB21EDCgBuZXR3b3JrX2lk/iLsSwoGAHN0YXRlcwADBwB2ZXJzaW9uASgVAQA=" + }, + { + "id": "minecraft:allium", + "block_state_b64": "CgAAAwgAYmxvY2tfaWQ+BAAACAQAbmFtZRAAbWluZWNyYWZ0OmFsbGl1bQQJAG5hbWVfaGFzaDCGQBHNDTkcAwoAbmV0d29ya19pZD9Dgr0KBgBzdGF0ZXMAAwcAdmVyc2lvbgEoFQEA" + }, + { + "id": "minecraft:azure_bluet", + "block_state_b64": "CgAAAwgAYmxvY2tfaWQ/BAAACAQAbmFtZRUAbWluZWNyYWZ0OmF6dXJlX2JsdWV0BAkAbmFtZV9oYXNo9N5egqMT2QcDCgBuZXR3b3JrX2lkwIgDnwoGAHN0YXRlcwADBwB2ZXJzaW9uASgVAQA=" + }, + { + "id": "minecraft:red_tulip", + "block_state_b64": "CgAAAwgAYmxvY2tfaWRABAAACAQAbmFtZRMAbWluZWNyYWZ0OnJlZF90dWxpcAQJAG5hbWVfaGFzaAjMi9Rd+6rhAwoAbmV0d29ya19pZAZCnt8KBgBzdGF0ZXMAAwcAdmVyc2lvbgEoFQEA" + }, + { + "id": "minecraft:orange_tulip", + "block_state_b64": "CgAAAwgAYmxvY2tfaWRBBAAACAQAbmFtZRYAbWluZWNyYWZ0Om9yYW5nZV90dWxpcAQJAG5hbWVfaGFzaP+NjxMBZ8vAAwoAbmV0d29ya19pZPYatsMKBgBzdGF0ZXMAAwcAdmVyc2lvbgEoFQEA" + }, + { + "id": "minecraft:white_tulip", + "block_state_b64": "CgAAAwgAYmxvY2tfaWRCBAAACAQAbmFtZRUAbWluZWNyYWZ0OndoaXRlX3R1bGlwBAkAbmFtZV9oYXNo5vbU4VRPh3ADCgBuZXR3b3JrX2lkok+4rQoGAHN0YXRlcwADBwB2ZXJzaW9uASgVAQA=" + }, + { + "id": "minecraft:pink_tulip", + "block_state_b64": "CgAAAwgAYmxvY2tfaWRDBAAACAQAbmFtZRQAbWluZWNyYWZ0OnBpbmtfdHVsaXAECQBuYW1lX2hhc2hxDHZa6OaNXAMKAG5ldHdvcmtfaWTiOT+VCgYAc3RhdGVzAAMHAHZlcnNpb24BKBUBAA==" + }, + { + "id": "minecraft:oxeye_daisy", + "block_state_b64": "CgAAAwgAYmxvY2tfaWREBAAACAQAbmFtZRUAbWluZWNyYWZ0Om94ZXllX2RhaXN5BAkAbmFtZV9oYXNoXwxsqNQTN9gDCgBuZXR3b3JrX2lkw7R7dwoGAHN0YXRlcwADBwB2ZXJzaW9uASgVAQA=" + }, + { + "id": "minecraft:cornflower", + "block_state_b64": "CgAAAwgAYmxvY2tfaWRFBAAACAQAbmFtZRQAbWluZWNyYWZ0OmNvcm5mbG93ZXIECQBuYW1lX2hhc2gnhyC3EeqHgAMKAG5ldHdvcmtfaWR4VrvACgYAc3RhdGVzAAMHAHZlcnNpb24BKBUBAA==" + }, + { + "id": "minecraft:lily_of_the_valley", + "block_state_b64": "CgAAAwgAYmxvY2tfaWRGBAAACAQAbmFtZRwAbWluZWNyYWZ0OmxpbHlfb2ZfdGhlX3ZhbGxleQQJAG5hbWVfaGFzaI64TJSf9mgQAwoAbmV0d29ya19pZFE9+nwKBgBzdGF0ZXMAAwcAdmVyc2lvbgEoFQEA" + }, + { + "id": "minecraft:sunflower", + "block_state_b64": "CgAAAwgAYmxvY2tfaWSvAAAACAQAbmFtZRMAbWluZWNyYWZ0OnN1bmZsb3dlcgQJAG5hbWVfaGFzaAMxYQLoqlZ0AwoAbmV0d29ya19pZA10iSoKBgBzdGF0ZXMBDwB1cHBlcl9ibG9ja19iaXQAAAMHAHZlcnNpb24BKBUBAA==" + }, + { + "id": "minecraft:lilac", + "block_state_b64": "CgAAAwgAYmxvY2tfaWReBAAACAQAbmFtZQ8AbWluZWNyYWZ0OmxpbGFjBAkAbmFtZV9oYXNoD3nrQJuo7NkDCgBuZXR3b3JrX2lk5W+uFAoGAHN0YXRlcwEPAHVwcGVyX2Jsb2NrX2JpdAAAAwcAdmVyc2lvbgEoFQEA" + }, + { + "id": "minecraft:rose_bush", + "block_state_b64": "CgAAAwgAYmxvY2tfaWRhBAAACAQAbmFtZRMAbWluZWNyYWZ0OnJvc2VfYnVzaAQJAG5hbWVfaGFzaLoiFk8LVpGKAwoAbmV0d29ya19pZMZPv48KBgBzdGF0ZXMBDwB1cHBlcl9ibG9ja19iaXQAAAMHAHZlcnNpb24BKBUBAA==" + }, + { + "id": "minecraft:peony", + "block_state_b64": "CgAAAwgAYmxvY2tfaWRiBAAACAQAbmFtZQ8AbWluZWNyYWZ0OnBlb255BAkAbmFtZV9oYXNoR4dYc4QquPADCgBuZXR3b3JrX2lkrTe7RwoGAHN0YXRlcwEPAHVwcGVyX2Jsb2NrX2JpdAAAAwcAdmVyc2lvbgEoFQEA" + }, + { + "id": "minecraft:pitcher_plant", + "block_state_b64": "CgAAAwgAYmxvY2tfaWRjAwAACAQAbmFtZRcAbWluZWNyYWZ0OnBpdGNoZXJfcGxhbnQECQBuYW1lX2hhc2hRJHzsbDH+SQMKAG5ldHdvcmtfaWRnY76VCgYAc3RhdGVzAQ8AdXBwZXJfYmxvY2tfYml0AAADBwB2ZXJzaW9uASgVAQA=" + }, + { + "id": "minecraft:pink_petals", + "block_state_b64": "CgAAAwgAYmxvY2tfaWQkAwAACAQAbmFtZRUAbWluZWNyYWZ0OnBpbmtfcGV0YWxzBAkAbmFtZV9oYXNo6DQwN9SwV3QDCgBuZXR3b3JrX2lkNWneGgoGAHN0YXRlcwMGAGdyb3d0aAAAAAAIHABtaW5lY3JhZnQ6Y2FyZGluYWxfZGlyZWN0aW9uBQBzb3V0aAADBwB2ZXJzaW9uASgVAQA=" + }, + { + "id": "minecraft:wither_rose", + "block_state_b64": "CgAAAwgAYmxvY2tfaWTXAQAACAQAbmFtZRUAbWluZWNyYWZ0OndpdGhlcl9yb3NlBAkAbmFtZV9oYXNoaSKxl3I516gDCgBuZXR3b3JrX2lkATXLPwoGAHN0YXRlcwADBwB2ZXJzaW9uASgVAQA=" + }, + { + "id": "minecraft:torchflower", + "block_state_b64": "CgAAAwgAYmxvY2tfaWQ3AwAACAQAbmFtZRUAbWluZWNyYWZ0OnRvcmNoZmxvd2VyBAkAbmFtZV9oYXNoL+mHtElwbqQDCgBuZXR3b3JrX2lkI34O+AoGAHN0YXRlcwADBwB2ZXJzaW9uASgVAQA=" + }, + { + "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", + "block_state_b64": "CgAAAwgAYmxvY2tfaWRqAAAACAQAbmFtZQ4AbWluZWNyYWZ0OnZpbmUECQBuYW1lX2hhc2j0Sj8/XeXOLAMKAG5ldHdvcmtfaWSUkDtbCgYAc3RhdGVzAxMAdmluZV9kaXJlY3Rpb25fYml0cwAAAAAAAwcAdmVyc2lvbgEoFQEA" + }, + { + "id": "minecraft:weeping_vines", + "block_state_b64": "CgAAAwgAYmxvY2tfaWTmAQAACAQAbmFtZRcAbWluZWNyYWZ0OndlZXBpbmdfdmluZXMECQBuYW1lX2hhc2jrLgLHkQygiwMKAG5ldHdvcmtfaWQ8NHSJCgYAc3RhdGVzAxEAd2VlcGluZ192aW5lc19hZ2UAAAAAAAMHAHZlcnNpb24BKBUBAA==" + }, + { + "id": "minecraft:twisting_vines", + "block_state_b64": "CgAAAwgAYmxvY2tfaWQeAgAACAQAbmFtZRgAbWluZWNyYWZ0OnR3aXN0aW5nX3ZpbmVzBAkAbmFtZV9oYXNoDYR5QgVUQJADCgBuZXR3b3JrX2lk5kYVIQoGAHN0YXRlcwMSAHR3aXN0aW5nX3ZpbmVzX2FnZQAAAAAAAwcAdmVyc2lvbgEoFQEA" + }, + { + "id": "minecraft:waterlily", + "block_state_b64": "CgAAAwgAYmxvY2tfaWRvAAAACAQAbmFtZRMAbWluZWNyYWZ0OndhdGVybGlseQQJAG5hbWVfaGFzaEHgC4c1SXg0AwoAbmV0d29ya19pZOOerp8KBgBzdGF0ZXMAAwcAdmVyc2lvbgEoFQEA" + }, + { + "id": "minecraft:seagrass", + "block_state_b64": "CgAAAwgAYmxvY2tfaWSBAQAACAQAbmFtZRIAbWluZWNyYWZ0OnNlYWdyYXNzBAkAbmFtZV9oYXNoHSBFtoHdWxIDCgBuZXR3b3JrX2lkd3lhEAoGAHN0YXRlcwgOAHNlYV9ncmFzc190eXBlBwBkZWZhdWx0AAMHAHZlcnNpb24BKBUBAA==" + }, + { + "id": "minecraft:kelp" + }, + { + "id": "minecraft:deadbush", + "block_state_b64": "CgAAAwgAYmxvY2tfaWQgAAAACAQAbmFtZRIAbWluZWNyYWZ0OmRlYWRidXNoBAkAbmFtZV9oYXNoPFODe4IScnYDCgBuZXR3b3JrX2lkVfnl+goGAHN0YXRlcwADBwB2ZXJzaW9uASgVAQA=" + }, + { + "id": "minecraft:bamboo", + "block_state_b64": "CgAAAwgAYmxvY2tfaWSiAQAACAQAbmFtZRAAbWluZWNyYWZ0OmJhbWJvbwQJAG5hbWVfaGFzaBgpGmyzhedCAwoAbmV0d29ya19pZIZv1nYKBgBzdGF0ZXMBBwBhZ2VfYml0AAgQAGJhbWJvb19sZWFmX3NpemUJAG5vX2xlYXZlcwgWAGJhbWJvb19zdGFsa190aGlja25lc3MEAHRoaW4AAwcAdmVyc2lvbgEoFQEA" + }, + { + "id": "minecraft:snow", + "block_state_b64": "CgAAAwgAYmxvY2tfaWRQAAAACAQAbmFtZQ4AbWluZWNyYWZ0OnNub3cECQBuYW1lX2hhc2gVHr5XXdETWAMKAG5ldHdvcmtfaWQ0zCeHCgYAc3RhdGVzAAMHAHZlcnNpb24BKBUBAA==" + }, + { + "id": "minecraft:ice", + "block_state_b64": "CgAAAwgAYmxvY2tfaWRPAAAACAQAbmFtZQ0AbWluZWNyYWZ0OmljZQQJAG5hbWVfaGFzaNF26f+uUT29AwoAbmV0d29ya19pZOUMaQYKBgBzdGF0ZXMAAwcAdmVyc2lvbgEoFQEA" + }, + { + "id": "minecraft:packed_ice", + "block_state_b64": "CgAAAwgAYmxvY2tfaWSuAAAACAQAbmFtZRQAbWluZWNyYWZ0OnBhY2tlZF9pY2UECQBuYW1lX2hhc2hk4bu123ZrFgMKAG5ldHdvcmtfaWTr/ooaCgYAc3RhdGVzAAMHAHZlcnNpb24BKBUBAA==" + }, + { + "id": "minecraft:blue_ice", + "block_state_b64": "CgAAAwgAYmxvY2tfaWQKAQAACAQAbmFtZRIAbWluZWNyYWZ0OmJsdWVfaWNlBAkAbmFtZV9oYXNo+EKxYgFhKcgDCgBuZXR3b3JrX2lkxfsA8goGAHN0YXRlcwADBwB2ZXJzaW9uASgVAQA=" + }, + { + "id": "minecraft:snow_layer", + "block_state_b64": "CgAAAwgAYmxvY2tfaWROAAAACAQAbmFtZRQAbWluZWNyYWZ0OnNub3dfbGF5ZXIECQBuYW1lX2hhc2hXka6atMYUCQMKAG5ldHdvcmtfaWRCrIPcCgYAc3RhdGVzAQsAY292ZXJlZF9iaXQAAwYAaGVpZ2h0AAAAAAADBwB2ZXJzaW9uASgVAQA=" + }, + { + "id": "minecraft:pointed_dripstone", + "block_state_b64": "CgAAAwgAYmxvY2tfaWQzAgAACAQAbmFtZRsAbWluZWNyYWZ0OnBvaW50ZWRfZHJpcHN0b25lBAkAbmFtZV9oYXNoJMISzmHQgt8DCgBuZXR3b3JrX2lkbWrtYgoGAHN0YXRlcwgTAGRyaXBzdG9uZV90aGlja25lc3MDAHRpcAEHAGhhbmdpbmcBAAMHAHZlcnNpb24BKBUBAA==" + }, + { + "id": "minecraft:dripstone_block", + "block_state_b64": "CgAAAwgAYmxvY2tfaWQ8AgAACAQAbmFtZRkAbWluZWNyYWZ0OmRyaXBzdG9uZV9ibG9jawQJAG5hbWVfaGFzaIIXnEqY77YsAwoAbmV0d29ya19pZMZi2kwKBgBzdGF0ZXMAAwcAdmVyc2lvbgEoFQEA" + }, + { + "id": "minecraft:moss_carpet", + "block_state_b64": "CgAAAwgAYmxvY2tfaWROAgAACAQAbmFtZRUAbWluZWNyYWZ0Om1vc3NfY2FycGV0BAkAbmFtZV9oYXNo/NEDxRPTshYDCgBuZXR3b3JrX2lkaGG3QwoGAHN0YXRlcwADBwB2ZXJzaW9uASgVAQA=" + }, + { + "id": "minecraft:moss_block", + "block_state_b64": "CgAAAwgAYmxvY2tfaWQ/AgAACAQAbmFtZRQAbWluZWNyYWZ0Om1vc3NfYmxvY2sECQBuYW1lX2hhc2iovcsPUYX2tgMKAG5ldHdvcmtfaWT3JSbfCgYAc3RhdGVzAAMHAHZlcnNpb24BKBUBAA==" + }, + { + "id": "minecraft:hanging_roots", + "block_state_b64": "CgAAAwgAYmxvY2tfaWQ+AgAACAQAbmFtZRcAbWluZWNyYWZ0Omhhbmdpbmdfcm9vdHMECQBuYW1lX2hhc2jaXn+Y5UZpDAMKAG5ldHdvcmtfaWRU4c2vCgYAc3RhdGVzAAMHAHZlcnNpb24BKBUBAA==" + }, + { + "id": "minecraft:mangrove_roots", + "block_state_b64": "CgAAAwgAYmxvY2tfaWThAgAACAQAbmFtZRgAbWluZWNyYWZ0Om1hbmdyb3ZlX3Jvb3RzBAkAbmFtZV9oYXNoa786PzQGZ6kDCgBuZXR3b3JrX2lklA0AHgoGAHN0YXRlcwADBwB2ZXJzaW9uASgVAQA=" + }, + { + "id": "minecraft:muddy_mangrove_roots", + "block_state_b64": "CgAAAwgAYmxvY2tfaWTiAgAACAQAbmFtZR4AbWluZWNyYWZ0Om11ZGR5X21hbmdyb3ZlX3Jvb3RzBAkAbmFtZV9oYXNo9YApdHpo1RkDCgBuZXR3b3JrX2lkH0Oc4woGAHN0YXRlcwgLAHBpbGxhcl9heGlzAQB5AAMHAHZlcnNpb24BKBUBAA==" + }, + { + "id": "minecraft:big_dripleaf", + "block_state_b64": "CgAAAwgAYmxvY2tfaWRCAgAACAQAbmFtZRYAbWluZWNyYWZ0OmJpZ19kcmlwbGVhZgQJAG5hbWVfaGFzaGBEhXjo6qSdAwoAbmV0d29ya19pZMETsb8KBgBzdGF0ZXMBEQBiaWdfZHJpcGxlYWZfaGVhZAEIEQBiaWdfZHJpcGxlYWZfdGlsdAQAbm9uZQgcAG1pbmVjcmFmdDpjYXJkaW5hbF9kaXJlY3Rpb24FAHNvdXRoAAMHAHZlcnNpb24BKBUBAA==" + }, + { + "id": "minecraft:small_dripleaf_block", + "block_state_b64": "CgAAAwgAYmxvY2tfaWRPAgAACAQAbmFtZR4AbWluZWNyYWZ0OnNtYWxsX2RyaXBsZWFmX2Jsb2NrBAkAbmFtZV9oYXNojxRAgXP9uWADCgBuZXR3b3JrX2lkozbVPwoGAHN0YXRlcwgcAG1pbmVjcmFmdDpjYXJkaW5hbF9kaXJlY3Rpb24EAGVhc3QBDwB1cHBlcl9ibG9ja19iaXQBAAMHAHZlcnNpb24BKBUBAA==" + }, + { + "id": "minecraft:spore_blossom", + "block_state_b64": "CgAAAwgAYmxvY2tfaWRAAgAACAQAbmFtZRcAbWluZWNyYWZ0OnNwb3JlX2Jsb3Nzb20ECQBuYW1lX2hhc2il3U72Gbco2gMKAG5ldHdvcmtfaWSbbbgcCgYAc3RhdGVzAAMHAHZlcnNpb24BKBUBAA==" + }, + { + "id": "minecraft:azalea", + "block_state_b64": "CgAAAwgAYmxvY2tfaWRQAgAACAQAbmFtZRAAbWluZWNyYWZ0OmF6YWxlYQQJAG5hbWVfaGFzaNyUl+BW9JrBAwoAbmV0d29ya19pZO/XZtQKBgBzdGF0ZXMAAwcAdmVyc2lvbgEoFQEA" + }, + { + "id": "minecraft:flowering_azalea", + "block_state_b64": "CgAAAwgAYmxvY2tfaWRRAgAACAQAbmFtZRoAbWluZWNyYWZ0OmZsb3dlcmluZ19hemFsZWEECQBuYW1lX2hhc2ie9r33wz8kiwMKAG5ldHdvcmtfaWQ3ij0VCgYAc3RhdGVzAAMHAHZlcnNpb24BKBUBAA==" + }, + { + "id": "minecraft:glow_lichen", + "block_state_b64": "CgAAAwgAYmxvY2tfaWSaAgAACAQAbmFtZRUAbWluZWNyYWZ0Omdsb3dfbGljaGVuBAkAbmFtZV9oYXNobyPUrIYlo44DCgBuZXR3b3JrX2lkCh8lSAoGAHN0YXRlcwMZAG11bHRpX2ZhY2VfZGlyZWN0aW9uX2JpdHM/AAAAAAMHAHZlcnNpb24BKBUBAA==" + }, + { + "id": "minecraft:amethyst_block", + "block_state_b64": "CgAAAwgAYmxvY2tfaWRGAgAACAQAbmFtZRgAbWluZWNyYWZ0OmFtZXRoeXN0X2Jsb2NrBAkAbmFtZV9oYXNob+JK1iiAthcDCgBuZXR3b3JrX2lk8HtpzgoGAHN0YXRlcwADBwB2ZXJzaW9uASgVAQA=" + }, + { + "id": "minecraft:budding_amethyst", + "block_state_b64": "CgAAAwgAYmxvY2tfaWRHAgAACAQAbmFtZRoAbWluZWNyYWZ0OmJ1ZGRpbmdfYW1ldGh5c3QECQBuYW1lX2hhc2gJvAwfI14fxgMKAG5ldHdvcmtfaWTQYqfACgYAc3RhdGVzAAMHAHZlcnNpb24BKBUBAA==" + }, + { + "id": "minecraft:amethyst_cluster", + "block_state_b64": "CgAAAwgAYmxvY2tfaWRIAgAACAQAbmFtZRoAbWluZWNyYWZ0OmFtZXRoeXN0X2NsdXN0ZXIECQBuYW1lX2hhc2jK82S88Jgm8wMKAG5ldHdvcmtfaWSCPMPGCgYAc3RhdGVzCBQAbWluZWNyYWZ0OmJsb2NrX2ZhY2UCAHVwAAMHAHZlcnNpb24BKBUBAA==" + }, + { + "id": "minecraft:large_amethyst_bud", + "block_state_b64": "CgAAAwgAYmxvY2tfaWRJAgAACAQAbmFtZRwAbWluZWNyYWZ0OmxhcmdlX2FtZXRoeXN0X2J1ZAQJAG5hbWVfaGFzaAHhdpWD+sd5AwoAbmV0d29ya19pZKkQxOcKBgBzdGF0ZXMIFABtaW5lY3JhZnQ6YmxvY2tfZmFjZQIAdXAAAwcAdmVyc2lvbgEoFQEA" + }, + { + "id": "minecraft:medium_amethyst_bud", + "block_state_b64": "CgAAAwgAYmxvY2tfaWRKAgAACAQAbmFtZR0AbWluZWNyYWZ0Om1lZGl1bV9hbWV0aHlzdF9idWQECQBuYW1lX2hhc2g5lBGtC0DzZQMKAG5ldHdvcmtfaWSYiP4gCgYAc3RhdGVzCBQAbWluZWNyYWZ0OmJsb2NrX2ZhY2UCAHVwAAMHAHZlcnNpb24BKBUBAA==" + }, + { + "id": "minecraft:small_amethyst_bud", + "block_state_b64": "CgAAAwgAYmxvY2tfaWRLAgAACAQAbmFtZRwAbWluZWNyYWZ0OnNtYWxsX2FtZXRoeXN0X2J1ZAQJAG5hbWVfaGFzaEnb4+q9PO4YAwoAbmV0d29ya19pZGWzxrQKBgBzdGF0ZXMIFABtaW5lY3JhZnQ6YmxvY2tfZmFjZQIAdXAAAwcAdmVyc2lvbgEoFQEA" + }, + { + "id": "minecraft:calcite", + "block_state_b64": "CgAAAwgAYmxvY2tfaWRFAgAACAQAbmFtZREAbWluZWNyYWZ0OmNhbGNpdGUECQBuYW1lX2hhc2ixKLu8ZIdzDQMKAG5ldHdvcmtfaWQlSbJDCgYAc3RhdGVzAAMHAHZlcnNpb24BKBUBAA==" + }, + { + "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", + "block_state_b64": "CgAAAwgAYmxvY2tfaWQnAAAACAQAbmFtZRgAbWluZWNyYWZ0OmJyb3duX211c2hyb29tBAkAbmFtZV9oYXNonYw/FO78WDoDCgBuZXR3b3JrX2lkLh1OXAoGAHN0YXRlcwADBwB2ZXJzaW9uASgVAQA=" + }, + { + "id": "minecraft:red_mushroom", + "block_state_b64": "CgAAAwgAYmxvY2tfaWQoAAAACAQAbmFtZRYAbWluZWNyYWZ0OnJlZF9tdXNocm9vbQQJAG5hbWVfaGFzaPpzJua7669xAwoAbmV0d29ya19pZCvWPYkKBgBzdGF0ZXMAAwcAdmVyc2lvbgEoFQEA" + }, + { + "id": "minecraft:crimson_fungus", + "block_state_b64": "CgAAAwgAYmxvY2tfaWTjAQAACAQAbmFtZRgAbWluZWNyYWZ0OmNyaW1zb25fZnVuZ3VzBAkAbmFtZV9oYXNolIcCUuFM2u0DCgBuZXR3b3JrX2lkD2NN0QoGAHN0YXRlcwADBwB2ZXJzaW9uASgVAQA=" + }, + { + "id": "minecraft:warped_fungus", + "block_state_b64": "CgAAAwgAYmxvY2tfaWTkAQAACAQAbmFtZRcAbWluZWNyYWZ0OndhcnBlZF9mdW5ndXMECQBuYW1lX2hhc2gq8bSnRVTAFgMKAG5ldHdvcmtfaWTkwS+rCgYAc3RhdGVzAAMHAHZlcnNpb24BKBUBAA==" + }, + { + "id": "minecraft:brown_mushroom_block", + "block_state_b64": "CgAAAwgAYmxvY2tfaWRjAAAACAQAbmFtZR4AbWluZWNyYWZ0OmJyb3duX211c2hyb29tX2Jsb2NrBAkAbmFtZV9oYXNoIyjnbI6xy9sDCgBuZXR3b3JrX2lkdOMhDAoGAHN0YXRlcwMSAGh1Z2VfbXVzaHJvb21fYml0cw4AAAAAAwcAdmVyc2lvbgEoFQEA" + }, + { + "id": "minecraft:red_mushroom_block", + "block_state_b64": "CgAAAwgAYmxvY2tfaWRkAAAACAQAbmFtZRwAbWluZWNyYWZ0OnJlZF9tdXNocm9vbV9ibG9jawQJAG5hbWVfaGFzaJTTyJbth9M9AwoAbmV0d29ya19pZM+AyboKBgBzdGF0ZXMDEgBodWdlX211c2hyb29tX2JpdHMOAAAAAAMHAHZlcnNpb24BKBUBAA==" + }, + { + "id": "minecraft:mushroom_stem", + "block_state_b64": "CgAAAwgAYmxvY2tfaWTvBAAACAQAbmFtZRcAbWluZWNyYWZ0Om11c2hyb29tX3N0ZW0ECQBuYW1lX2hhc2i2SozhK9NLpgMKAG5ldHdvcmtfaWTTVND+CgYAc3RhdGVzAxIAaHVnZV9tdXNocm9vbV9iaXRzDwAAAAADBwB2ZXJzaW9uASgVAQA=" + }, + { + "id": "minecraft:egg" + }, + { + "id": "minecraft:sugar_cane" + }, + { + "id": "minecraft:sugar" + }, + { + "id": "minecraft:rotten_flesh" + }, + { + "id": "minecraft:bone" + }, + { + "id": "minecraft:web", + "block_state_b64": "CgAAAwgAYmxvY2tfaWQeAAAACAQAbmFtZQ0AbWluZWNyYWZ0OndlYgQJAG5hbWVfaGFzaA4GKQCvG4i9AwoAbmV0d29ya19pZApt+jgKBgBzdGF0ZXMAAwcAdmVyc2lvbgEoFQEA" + }, + { + "id": "minecraft:spider_eye" + }, + { + "id": "minecraft:mob_spawner", + "block_state_b64": "CgAAAwgAYmxvY2tfaWQ0AAAACAQAbmFtZRUAbWluZWNyYWZ0Om1vYl9zcGF3bmVyBAkAbmFtZV9oYXNoNwGrCV/Fkh8DCgBuZXR3b3JrX2lkM1wTmgoGAHN0YXRlcwADBwB2ZXJzaW9uASgVAQA=" + }, + { + "id": "minecraft:trial_spawner", + "block_state_b64": "CgAAAwgAYmxvY2tfaWQ6AgAACAQAbmFtZRcAbWluZWNyYWZ0OnRyaWFsX3NwYXduZXIECQBuYW1lX2hhc2iNLRPB4ACz+QMKAG5ldHdvcmtfaWTWFYHGCgYAc3RhdGVzAQcAb21pbm91cwADEwB0cmlhbF9zcGF3bmVyX3N0YXRlAAAAAAADBwB2ZXJzaW9uASgVAQA=" + }, + { + "id": "minecraft:vault", + "block_state_b64": "CgAAAwgAYmxvY2tfaWQ5AgAACAQAbmFtZQ8AbWluZWNyYWZ0OnZhdWx0BAkAbmFtZV9oYXNoCAp9n3IAyqcDCgBuZXR3b3JrX2lk6/P+vwoGAHN0YXRlcwgcAG1pbmVjcmFmdDpjYXJkaW5hbF9kaXJlY3Rpb24FAHNvdXRoAQcAb21pbm91cwAICwB2YXVsdF9zdGF0ZQgAaW5hY3RpdmUAAwcAdmVyc2lvbgEoFQEA" + }, + { + "id": "minecraft:end_portal_frame", + "block_state_b64": "CgAAAwgAYmxvY2tfaWR4AAAACAQAbmFtZRoAbWluZWNyYWZ0OmVuZF9wb3J0YWxfZnJhbWUECQBuYW1lX2hhc2gqofyUIjGOpQMKAG5ldHdvcmtfaWRbGHf8CgYAc3RhdGVzARIAZW5kX3BvcnRhbF9leWVfYml0AAgcAG1pbmVjcmFmdDpjYXJkaW5hbF9kaXJlY3Rpb24FAHNvdXRoAAMHAHZlcnNpb24BKBUBAA==" + }, + { + "id": "minecraft:infested_stone", + "block_state_b64": "CgAAAwgAYmxvY2tfaWRhAAAACAQAbmFtZRgAbWluZWNyYWZ0OmluZmVzdGVkX3N0b25lBAkAbmFtZV9oYXNoxnRcHDu4zqQDCgBuZXR3b3JrX2lkpfcnsgoGAHN0YXRlcwADBwB2ZXJzaW9uASgVAQA=" + }, + { + "id": "minecraft:infested_cobblestone", + "block_state_b64": "CgAAAwgAYmxvY2tfaWRZBAAACAQAbmFtZR4AbWluZWNyYWZ0OmluZmVzdGVkX2NvYmJsZXN0b25lBAkAbmFtZV9oYXNoy+LVCKG2kVMDCgBuZXR3b3JrX2lkpn+icAoGAHN0YXRlcwADBwB2ZXJzaW9uASgVAQA=" + }, + { + "id": "minecraft:infested_stone_bricks", + "block_state_b64": "CgAAAwgAYmxvY2tfaWRaBAAACAQAbmFtZR8AbWluZWNyYWZ0OmluZmVzdGVkX3N0b25lX2JyaWNrcwQJAG5hbWVfaGFzaBMnals7a32CAwoAbmV0d29ya19pZNHi2UYKBgBzdGF0ZXMAAwcAdmVyc2lvbgEoFQEA" + }, + { + "id": "minecraft:infested_mossy_stone_bricks", + "block_state_b64": "CgAAAwgAYmxvY2tfaWRbBAAACAQAbmFtZSUAbWluZWNyYWZ0OmluZmVzdGVkX21vc3N5X3N0b25lX2JyaWNrcwQJAG5hbWVfaGFzaAmJk+HmVq0rAwoAbmV0d29ya19pZAVH8/sKBgBzdGF0ZXMAAwcAdmVyc2lvbgEoFQEA" + }, + { + "id": "minecraft:infested_cracked_stone_bricks", + "block_state_b64": "CgAAAwgAYmxvY2tfaWRcBAAACAQAbmFtZScAbWluZWNyYWZ0OmluZmVzdGVkX2NyYWNrZWRfc3RvbmVfYnJpY2tzBAkAbmFtZV9oYXNoMyc60XcfcyoDCgBuZXR3b3JrX2lkaW+kbQoGAHN0YXRlcwADBwB2ZXJzaW9uASgVAQA=" + }, + { + "id": "minecraft:infested_chiseled_stone_bricks", + "block_state_b64": "CgAAAwgAYmxvY2tfaWRdBAAACAQAbmFtZSgAbWluZWNyYWZ0OmluZmVzdGVkX2NoaXNlbGVkX3N0b25lX2JyaWNrcwQJAG5hbWVfaGFzaNUvNIIg9dZbAwoAbmV0d29ya19pZCajGicKBgBzdGF0ZXMAAwcAdmVyc2lvbgEoFQEA" + }, + { + "id": "minecraft:infested_deepslate", + "block_state_b64": "CgAAAwgAYmxvY2tfaWTFAgAACAQAbmFtZRwAbWluZWNyYWZ0OmluZmVzdGVkX2RlZXBzbGF0ZQQJAG5hbWVfaGFzaICF2VYccxF1AwoAbmV0d29ya19pZDa/624KBgBzdGF0ZXMICwBwaWxsYXJfYXhpcwEAeQADBwB2ZXJzaW9uASgVAQA=" + }, + { + "id": "minecraft:dragon_egg", + "block_state_b64": "CgAAAwgAYmxvY2tfaWR6AAAACAQAbmFtZRQAbWluZWNyYWZ0OmRyYWdvbl9lZ2cECQBuYW1lX2hhc2inMzXrV+/e1wMKAG5ldHdvcmtfaWTgO1yRCgYAc3RhdGVzAAMHAHZlcnNpb24BKBUBAA==" + }, + { + "id": "minecraft:turtle_egg", + "block_state_b64": "CgAAAwgAYmxvY2tfaWSeAQAACAQAbmFtZRQAbWluZWNyYWZ0OnR1cnRsZV9lZ2cECQBuYW1lX2hhc2iwSRcxOJIJ9gMKAG5ldHdvcmtfaWSIRNUhCgYAc3RhdGVzCA0AY3JhY2tlZF9zdGF0ZQkAbm9fY3JhY2tzCBAAdHVydGxlX2VnZ19jb3VudAcAb25lX2VnZwADBwB2ZXJzaW9uASgVAQA=" + }, + { + "id": "minecraft:sniffer_egg", + "block_state_b64": "CgAAAwgAYmxvY2tfaWRTAwAACAQAbmFtZRUAbWluZWNyYWZ0OnNuaWZmZXJfZWdnBAkAbmFtZV9oYXNoY1lozc8lPcYDCgBuZXR3b3JrX2lk7yb/2QoGAHN0YXRlcwgNAGNyYWNrZWRfc3RhdGUJAG5vX2NyYWNrcwADBwB2ZXJzaW9uASgVAQA=" + }, + { + "id": "minecraft:frog_spawn", + "block_state_b64": "CgAAAwgAYmxvY2tfaWTTAgAACAQAbmFtZRQAbWluZWNyYWZ0OmZyb2dfc3Bhd24ECQBuYW1lX2hhc2iWmd7idp3ZZwMKAG5ldHdvcmtfaWRFzJudCgYAc3RhdGVzAAMHAHZlcnNpb24BKBUBAA==" + }, + { + "id": "minecraft:pearlescent_froglight", + "block_state_b64": "CgAAAwgAYmxvY2tfaWTUAgAACAQAbmFtZR8AbWluZWNyYWZ0OnBlYXJsZXNjZW50X2Zyb2dsaWdodAQJAG5hbWVfaGFzaKkcFRyycYGyAwoAbmV0d29ya19pZJqYakAKBgBzdGF0ZXMICwBwaWxsYXJfYXhpcwEAeQADBwB2ZXJzaW9uASgVAQA=" + }, + { + "id": "minecraft:verdant_froglight", + "block_state_b64": "CgAAAwgAYmxvY2tfaWTVAgAACAQAbmFtZRsAbWluZWNyYWZ0OnZlcmRhbnRfZnJvZ2xpZ2h0BAkAbmFtZV9oYXNoA+eXuTBohrQDCgBuZXR3b3JrX2lkDIVnsQoGAHN0YXRlcwgLAHBpbGxhcl9heGlzAQB5AAMHAHZlcnNpb24BKBUBAA==" + }, + { + "id": "minecraft:ochre_froglight", + "block_state_b64": "CgAAAwgAYmxvY2tfaWTWAgAACAQAbmFtZRkAbWluZWNyYWZ0Om9jaHJlX2Zyb2dsaWdodAQJAG5hbWVfaGFzaMY59kjPe+c3AwoAbmV0d29ya19pZO2TD50KBgBzdGF0ZXMICwBwaWxsYXJfYXhpcwEAeQADBwB2ZXJzaW9uASgVAQA=" + }, + { + "id": "minecraft:chicken_spawn_egg" + }, + { + "id": "minecraft:bee_spawn_egg" + }, + { + "id": "minecraft:cow_spawn_egg" + }, + { + "id": "minecraft:pig_spawn_egg" + }, + { + "id": "minecraft:sheep_spawn_egg" + }, + { + "id": "minecraft:wolf_spawn_egg" + }, + { + "id": "minecraft:polar_bear_spawn_egg" + }, + { + "id": "minecraft:ocelot_spawn_egg" + }, + { + "id": "minecraft:cat_spawn_egg" + }, + { + "id": "minecraft:mooshroom_spawn_egg" + }, + { + "id": "minecraft:bat_spawn_egg" + }, + { + "id": "minecraft:parrot_spawn_egg" + }, + { + "id": "minecraft:rabbit_spawn_egg" + }, + { + "id": "minecraft:llama_spawn_egg" + }, + { + "id": "minecraft:horse_spawn_egg" + }, + { + "id": "minecraft:donkey_spawn_egg" + }, + { + "id": "minecraft:mule_spawn_egg" + }, + { + "id": "minecraft:skeleton_horse_spawn_egg" + }, + { + "id": "minecraft:zombie_horse_spawn_egg" + }, + { + "id": "minecraft:tropical_fish_spawn_egg" + }, + { + "id": "minecraft:cod_spawn_egg" + }, + { + "id": "minecraft:pufferfish_spawn_egg" + }, + { + "id": "minecraft:salmon_spawn_egg" + }, + { + "id": "minecraft:dolphin_spawn_egg" + }, + { + "id": "minecraft:turtle_spawn_egg" + }, + { + "id": "minecraft:panda_spawn_egg" + }, + { + "id": "minecraft:fox_spawn_egg" + }, + { + "id": "minecraft:creeper_spawn_egg" + }, + { + "id": "minecraft:enderman_spawn_egg" + }, + { + "id": "minecraft:silverfish_spawn_egg" + }, + { + "id": "minecraft:skeleton_spawn_egg" + }, + { + "id": "minecraft:wither_skeleton_spawn_egg" + }, + { + "id": "minecraft:stray_spawn_egg" + }, + { + "id": "minecraft:slime_spawn_egg" + }, + { + "id": "minecraft:spider_spawn_egg" + }, + { + "id": "minecraft:zombie_spawn_egg" + }, + { + "id": "minecraft:zombie_pigman_spawn_egg" + }, + { + "id": "minecraft:husk_spawn_egg" + }, + { + "id": "minecraft:drowned_spawn_egg" + }, + { + "id": "minecraft:squid_spawn_egg" + }, + { + "id": "minecraft:glow_squid_spawn_egg" + }, + { + "id": "minecraft:cave_spider_spawn_egg" + }, + { + "id": "minecraft:witch_spawn_egg" + }, + { + "id": "minecraft:guardian_spawn_egg" + }, + { + "id": "minecraft:elder_guardian_spawn_egg" + }, + { + "id": "minecraft:endermite_spawn_egg" + }, + { + "id": "minecraft:magma_cube_spawn_egg" + }, + { + "id": "minecraft:strider_spawn_egg" + }, + { + "id": "minecraft:hoglin_spawn_egg" + }, + { + "id": "minecraft:piglin_spawn_egg" + }, + { + "id": "minecraft:zoglin_spawn_egg" + }, + { + "id": "minecraft:piglin_brute_spawn_egg" + }, + { + "id": "minecraft:goat_spawn_egg" + }, + { + "id": "minecraft:axolotl_spawn_egg" + }, + { + "id": "minecraft:warden_spawn_egg" + }, + { + "id": "minecraft:allay_spawn_egg" + }, + { + "id": "minecraft:frog_spawn_egg" + }, + { + "id": "minecraft:tadpole_spawn_egg" + }, + { + "id": "minecraft:trader_llama_spawn_egg" + }, + { + "id": "minecraft:camel_spawn_egg" + }, + { + "id": "minecraft:ghast_spawn_egg" + }, + { + "id": "minecraft:blaze_spawn_egg" + }, + { + "id": "minecraft:shulker_spawn_egg" + }, + { + "id": "minecraft:vindicator_spawn_egg" + }, + { + "id": "minecraft:evoker_spawn_egg" + }, + { + "id": "minecraft:vex_spawn_egg" + }, + { + "id": "minecraft:villager_spawn_egg" + }, + { + "id": "minecraft:wandering_trader_spawn_egg" + }, + { + "id": "minecraft:zombie_villager_spawn_egg" + }, + { + "id": "minecraft:phantom_spawn_egg" + }, + { + "id": "minecraft:pillager_spawn_egg" + }, + { + "id": "minecraft:ravager_spawn_egg" + }, + { + "id": "minecraft:iron_golem_spawn_egg" + }, + { + "id": "minecraft:snow_golem_spawn_egg" + }, + { + "id": "minecraft:sniffer_spawn_egg" + }, + { + "id": "minecraft:breeze_spawn_egg" + }, + { + "id": "minecraft:armadillo_spawn_egg" + }, + { + "id": "minecraft:bogged_spawn_egg" + }, + { + "id": "minecraft:obsidian", + "block_state_b64": "CgAAAwgAYmxvY2tfaWQxAAAACAQAbmFtZRIAbWluZWNyYWZ0Om9ic2lkaWFuBAkAbmFtZV9oYXNoiz4qrb8QjyEDCgBuZXR3b3JrX2lkuqnPpQoGAHN0YXRlcwADBwB2ZXJzaW9uASgVAQA=" + }, + { + "id": "minecraft:crying_obsidian", + "block_state_b64": "CgAAAwgAYmxvY2tfaWQgAgAACAQAbmFtZRkAbWluZWNyYWZ0OmNyeWluZ19vYnNpZGlhbgQJAG5hbWVfaGFzaKT0JlA7Z1K+AwoAbmV0d29ya19pZCjbPV4KBgBzdGF0ZXMAAwcAdmVyc2lvbgEoFQEA" + }, + { + "id": "minecraft:bedrock", + "block_state_b64": "CgAAAwgAYmxvY2tfaWQHAAAACAQAbmFtZREAbWluZWNyYWZ0OmJlZHJvY2sECQBuYW1lX2hhc2hWfFrh4LVtxwMKAG5ldHdvcmtfaWT7fKz1CgYAc3RhdGVzAQ4AaW5maW5pYnVybl9iaXQAAAMHAHZlcnNpb24BKBUBAA==" + }, + { + "id": "minecraft:soul_sand", + "block_state_b64": "CgAAAwgAYmxvY2tfaWRYAAAACAQAbmFtZRMAbWluZWNyYWZ0OnNvdWxfc2FuZAQJAG5hbWVfaGFzaMaf+bccu+KTAwoAbmV0d29ya19pZBQSHrMKBgBzdGF0ZXMAAwcAdmVyc2lvbgEoFQEA" + }, + { + "id": "minecraft:magma", + "block_state_b64": "CgAAAwgAYmxvY2tfaWTVAAAACAQAbmFtZQ8AbWluZWNyYWZ0Om1hZ21hBAkAbmFtZV9oYXNoqyTjKaIsWfYDCgBuZXR3b3JrX2lkyfWAZgoGAHN0YXRlcwADBwB2ZXJzaW9uASgVAQA=" + }, + { + "id": "minecraft:nether_wart" + }, + { + "id": "minecraft:end_stone", + "block_state_b64": "CgAAAwgAYmxvY2tfaWR5AAAACAQAbmFtZRMAbWluZWNyYWZ0OmVuZF9zdG9uZQQJAG5hbWVfaGFzaH1J9jA39GJNAwoAbmV0d29ya19pZFeFQ7UKBgBzdGF0ZXMAAwcAdmVyc2lvbgEoFQEA" + }, + { + "id": "minecraft:chorus_flower", + "block_state_b64": "CgAAAwgAYmxvY2tfaWTIAAAACAQAbmFtZRcAbWluZWNyYWZ0OmNob3J1c19mbG93ZXIECQBuYW1lX2hhc2iMpSodli5uawMKAG5ldHdvcmtfaWRnd1ZWCgYAc3RhdGVzAwMAYWdlAAAAAAADBwB2ZXJzaW9uASgVAQA=" + }, + { + "id": "minecraft:chorus_plant", + "block_state_b64": "CgAAAwgAYmxvY2tfaWTwAAAACAQAbmFtZRYAbWluZWNyYWZ0OmNob3J1c19wbGFudAQJAG5hbWVfaGFzaJhSrmNGKwaMAwoAbmV0d29ya19pZA3uVqMKBgBzdGF0ZXMAAwcAdmVyc2lvbgEoFQEA" + }, + { + "id": "minecraft:chorus_fruit" + }, + { + "id": "minecraft:popped_chorus_fruit" + }, + { + "id": "minecraft:sponge", + "block_state_b64": "CgAAAwgAYmxvY2tfaWQTAAAACAQAbmFtZRAAbWluZWNyYWZ0OnNwb25nZQQJAG5hbWVfaGFzaLrd2ScYRDMiAwoAbmV0d29ya19pZNmQW/oKBgBzdGF0ZXMAAwcAdmVyc2lvbgEoFQEA" + }, + { + "id": "minecraft:wet_sponge", + "block_state_b64": "CgAAAwgAYmxvY2tfaWTXBAAACAQAbmFtZRQAbWluZWNyYWZ0OndldF9zcG9uZ2UECQBuYW1lX2hhc2htp6nKpSHcAgMKAG5ldHdvcmtfaWQaW+fCCgYAc3RhdGVzAAMHAHZlcnNpb24BKBUBAA==" + }, + { + "id": "minecraft:tube_coral_block", + "block_state_b64": "CgAAAwgAYmxvY2tfaWSDAQAACAQAbmFtZRoAbWluZWNyYWZ0OnR1YmVfY29yYWxfYmxvY2sECQBuYW1lX2hhc2iGkaiR7Eot4wMKAG5ldHdvcmtfaWQPNJ6sCgYAc3RhdGVzAAMHAHZlcnNpb24BKBUBAA==" + }, + { + "id": "minecraft:brain_coral_block", + "block_state_b64": "CgAAAwgAYmxvY2tfaWRQBAAACAQAbmFtZRsAbWluZWNyYWZ0OmJyYWluX2NvcmFsX2Jsb2NrBAkAbmFtZV9oYXNoeDNAK18yUo4DCgBuZXR3b3JrX2lkloN1vgoGAHN0YXRlcwADBwB2ZXJzaW9uASgVAQA=" + }, + { + "id": "minecraft:bubble_coral_block", + "block_state_b64": "CgAAAwgAYmxvY2tfaWRRBAAACAQAbmFtZRwAbWluZWNyYWZ0OmJ1YmJsZV9jb3JhbF9ibG9jawQJAG5hbWVfaGFzaAI2mwMlvcNbAwoAbmV0d29ya19pZBlkxKIKBgBzdGF0ZXMAAwcAdmVyc2lvbgEoFQEA" + }, + { + "id": "minecraft:fire_coral_block", + "block_state_b64": "CgAAAwgAYmxvY2tfaWRSBAAACAQAbmFtZRoAbWluZWNyYWZ0OmZpcmVfY29yYWxfYmxvY2sECQBuYW1lX2hhc2gg1gLeXLmKaAMKAG5ldHdvcmtfaWSp3W57CgYAc3RhdGVzAAMHAHZlcnNpb24BKBUBAA==" + }, + { + "id": "minecraft:horn_coral_block", + "block_state_b64": "CgAAAwgAYmxvY2tfaWRTBAAACAQAbmFtZRoAbWluZWNyYWZ0Omhvcm5fY29yYWxfYmxvY2sECQBuYW1lX2hhc2hnZSLRWUwGhAMKAG5ldHdvcmtfaWRSK6ccCgYAc3RhdGVzAAMHAHZlcnNpb24BKBUBAA==" + }, + { + "id": "minecraft:dead_tube_coral_block", + "block_state_b64": "CgAAAwgAYmxvY2tfaWRUBAAACAQAbmFtZR8AbWluZWNyYWZ0OmRlYWRfdHViZV9jb3JhbF9ibG9jawQJAG5hbWVfaGFzaB9+lY3hAkNNAwoAbmV0d29ya19pZF0hKKYKBgBzdGF0ZXMAAwcAdmVyc2lvbgEoFQEA" + }, + { + "id": "minecraft:dead_brain_coral_block", + "block_state_b64": "CgAAAwgAYmxvY2tfaWRVBAAACAQAbmFtZSAAbWluZWNyYWZ0OmRlYWRfYnJhaW5fY29yYWxfYmxvY2sECQBuYW1lX2hhc2iHyDn52AO8uwMKAG5ldHdvcmtfaWQw7yCaCgYAc3RhdGVzAAMHAHZlcnNpb24BKBUBAA==" + }, + { + "id": "minecraft:dead_bubble_coral_block", + "block_state_b64": "CgAAAwgAYmxvY2tfaWRWBAAACAQAbmFtZSEAbWluZWNyYWZ0OmRlYWRfYnViYmxlX2NvcmFsX2Jsb2NrBAkAbmFtZV9oYXNotwkk/ITrsjADCgBuZXR3b3JrX2lk56mXUgoGAHN0YXRlcwADBwB2ZXJzaW9uASgVAQA=" + }, + { + "id": "minecraft:dead_fire_coral_block", + "block_state_b64": "CgAAAwgAYmxvY2tfaWRXBAAACAQAbmFtZR8AbWluZWNyYWZ0OmRlYWRfZmlyZV9jb3JhbF9ibG9jawQJAG5hbWVfaGFzaG0qHxbIrBEyAwoAbmV0d29ya19pZFvnH88KBgBzdGF0ZXMAAwcAdmVyc2lvbgEoFQEA" + }, + { + "id": "minecraft:dead_horn_coral_block", + "block_state_b64": "CgAAAwgAYmxvY2tfaWRYBAAACAQAbmFtZR8AbWluZWNyYWZ0OmRlYWRfaG9ybl9jb3JhbF9ibG9jawQJAG5hbWVfaGFzaL7D8bu4Fm+0AwoAbmV0d29ya19pZEALRLoKBgBzdGF0ZXMAAwcAdmVyc2lvbgEoFQEA" + }, + { + "id": "minecraft:sculk", + "block_state_b64": "CgAAAwgAYmxvY2tfaWTJAgAACAQAbmFtZQ8AbWluZWNyYWZ0OnNjdWxrBAkAbmFtZV9oYXNo2Lq7T5yQF8kDCgBuZXR3b3JrX2lkyqUPPgoGAHN0YXRlcwADBwB2ZXJzaW9uASgVAQA=" + }, + { + "id": "minecraft:sculk_vein", + "block_state_b64": "CgAAAwgAYmxvY2tfaWTKAgAACAQAbmFtZRQAbWluZWNyYWZ0OnNjdWxrX3ZlaW4ECQBuYW1lX2hhc2gJUdhVooV4zwMKAG5ldHdvcmtfaWSUfn1XCgYAc3RhdGVzAxkAbXVsdGlfZmFjZV9kaXJlY3Rpb25fYml0cwAAAAAAAwcAdmVyc2lvbgEoFQEA" + }, + { + "id": "minecraft:sculk_catalyst", + "block_state_b64": "CgAAAwgAYmxvY2tfaWTLAgAACAQAbmFtZRgAbWluZWNyYWZ0OnNjdWxrX2NhdGFseXN0BAkAbmFtZV9oYXNo+gCpbrCHST4DCgBuZXR3b3JrX2lkMJ2n/woGAHN0YXRlcwEFAGJsb29tAAADBwB2ZXJzaW9uASgVAQA=" + }, + { + "id": "minecraft:sculk_shrieker", + "block_state_b64": "CgAAAwgAYmxvY2tfaWTMAgAACAQAbmFtZRgAbWluZWNyYWZ0OnNjdWxrX3Nocmlla2VyBAkAbmFtZV9oYXNo5OXtyObniQ4DCgBuZXR3b3JrX2lkxapoNAoGAHN0YXRlcwEGAGFjdGl2ZQABCgBjYW5fc3VtbW9uAAADBwB2ZXJzaW9uASgVAQA=" + }, + { + "id": "minecraft:sculk_sensor", + "block_state_b64": "CgAAAwgAYmxvY2tfaWQyAgAACAQAbmFtZRYAbWluZWNyYWZ0OnNjdWxrX3NlbnNvcgQJAG5hbWVfaGFzaCkmHreeTgNnAwoAbmV0d29ya19pZLj2WPcKBgBzdGF0ZXMDEgBzY3Vsa19zZW5zb3JfcGhhc2UAAAAAAAMHAHZlcnNpb24BKBUBAA==" + }, + { + "id": "minecraft:calibrated_sculk_sensor", + "block_state_b64": "CgAAAwgAYmxvY2tfaWRDAwAACAQAbmFtZSEAbWluZWNyYWZ0OmNhbGlicmF0ZWRfc2N1bGtfc2Vuc29yBAkAbmFtZV9oYXNoffAcXXN/iJUDCgBuZXR3b3JrX2lkwOx3QQoGAHN0YXRlcwgcAG1pbmVjcmFmdDpjYXJkaW5hbF9kaXJlY3Rpb24FAHNvdXRoAxIAc2N1bGtfc2Vuc29yX3BoYXNlAAAAAAADBwB2ZXJzaW9uASgVAQA=" + }, + { + "id": "minecraft:reinforced_deepslate", + "block_state_b64": "CgAAAwgAYmxvY2tfaWTRAgAACAQAbmFtZR4AbWluZWNyYWZ0OnJlaW5mb3JjZWRfZGVlcHNsYXRlBAkAbmFtZV9oYXNoldDmj91EapQDCgBuZXR3b3JrX2lkHIt+aQoGAHN0YXRlcwADBwB2ZXJzaW9uASgVAQA=" + }, + { + "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:mace" + }, + { + "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:arrow", + "damage": 44 + }, + { + "id": "minecraft:arrow", + "damage": 45 + }, + { + "id": "minecraft:arrow", + "damage": 46 + }, + { + "id": "minecraft:arrow", + "damage": 47 + }, + { + "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:wind_charge" + }, + { + "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:bundle" + }, + { + "id": "minecraft:white_bundle" + }, + { + "id": "minecraft:light_gray_bundle" + }, + { + "id": "minecraft:gray_bundle" + }, + { + "id": "minecraft:black_bundle" + }, + { + "id": "minecraft:brown_bundle" + }, + { + "id": "minecraft:red_bundle" + }, + { + "id": "minecraft:orange_bundle" + }, + { + "id": "minecraft:yellow_bundle" + }, + { + "id": "minecraft:lime_bundle" + }, + { + "id": "minecraft:green_bundle" + }, + { + "id": "minecraft:cyan_bundle" + }, + { + "id": "minecraft:light_blue_bundle" + }, + { + "id": "minecraft:blue_bundle" + }, + { + "id": "minecraft:purple_bundle" + }, + { + "id": "minecraft:magenta_bundle" + }, + { + "id": "minecraft:pink_bundle" + }, + { + "id": "minecraft:leather_horse_armor" + }, + { + "id": "minecraft:iron_horse_armor" + }, + { + "id": "minecraft:golden_horse_armor" + }, + { + "id": "minecraft:diamond_horse_armor" + }, + { + "id": "minecraft:wolf_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:potion", + "damage": 43 + }, + { + "id": "minecraft:potion", + "damage": 44 + }, + { + "id": "minecraft:potion", + "damage": 45 + }, + { + "id": "minecraft:potion", + "damage": 46 + }, + { + "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:splash_potion", + "damage": 43 + }, + { + "id": "minecraft:splash_potion", + "damage": 44 + }, + { + "id": "minecraft:splash_potion", + "damage": 45 + }, + { + "id": "minecraft:splash_potion", + "damage": 46 + }, + { + "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:lingering_potion", + "damage": 43 + }, + { + "id": "minecraft:lingering_potion", + "damage": 44 + }, + { + "id": "minecraft:lingering_potion", + "damage": 45 + }, + { + "id": "minecraft:lingering_potion", + "damage": 46 + }, + { + "id": "minecraft:ominous_bottle" + }, + { + "id": "minecraft:ominous_bottle", + "damage": 1 + }, + { + "id": "minecraft:ominous_bottle", + "damage": 2 + }, + { + "id": "minecraft:ominous_bottle", + "damage": 3 + }, + { + "id": "minecraft:ominous_bottle", + "damage": 4 + }, + { + "id": "minecraft:spyglass" + }, + { + "id": "minecraft:brush" + }, + { + "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", + "block_state_b64": "CgAAAwgAYmxvY2tfaWQyAAAACAQAbmFtZQ8AbWluZWNyYWZ0OnRvcmNoBAkAbmFtZV9oYXNoagn7rmDBzisDCgBuZXR3b3JrX2lk+BwwuQoGAHN0YXRlcwgWAHRvcmNoX2ZhY2luZ19kaXJlY3Rpb24HAHVua25vd24AAwcAdmVyc2lvbgEoFQEA" + }, + { + "id": "minecraft:soul_torch", + "block_state_b64": "CgAAAwgAYmxvY2tfaWQLAgAACAQAbmFtZRQAbWluZWNyYWZ0OnNvdWxfdG9yY2gECQBuYW1lX2hhc2huixOT04BRdQMKAG5ldHdvcmtfaWShbFILCgYAc3RhdGVzCBYAdG9yY2hfZmFjaW5nX2RpcmVjdGlvbgcAdW5rbm93bgADBwB2ZXJzaW9uASgVAQA=" + }, + { + "id": "minecraft:sea_pickle", + "block_state_b64": "CgAAAwgAYmxvY2tfaWSbAQAACAQAbmFtZRQAbWluZWNyYWZ0OnNlYV9waWNrbGUECQBuYW1lX2hhc2iONEfZJB+glgMKAG5ldHdvcmtfaWSINWQyCgYAc3RhdGVzAw0AY2x1c3Rlcl9jb3VudAAAAAABCABkZWFkX2JpdAAAAwcAdmVyc2lvbgEoFQEA" + }, + { + "id": "minecraft:lantern", + "block_state_b64": "CgAAAwgAYmxvY2tfaWTPAQAACAQAbmFtZREAbWluZWNyYWZ0OmxhbnRlcm4ECQBuYW1lX2hhc2hMw44VI2HWygMKAG5ldHdvcmtfaWRkjQvzCgYAc3RhdGVzAQcAaGFuZ2luZwAAAwcAdmVyc2lvbgEoFQEA" + }, + { + "id": "minecraft:soul_lantern", + "block_state_b64": "CgAAAwgAYmxvY2tfaWQMAgAACAQAbmFtZRYAbWluZWNyYWZ0OnNvdWxfbGFudGVybgQJAG5hbWVfaGFzaGjIpjxk9z+RAwoAbmV0d29ya19pZGfoP8cKBgBzdGF0ZXMBBwBoYW5naW5nAAADBwB2ZXJzaW9uASgVAQA=" + }, + { + "id": "minecraft:candle", + "block_state_b64": "CgAAAwgAYmxvY2tfaWSbAgAACAQAbmFtZRAAbWluZWNyYWZ0OmNhbmRsZQQJAG5hbWVfaGFzaHPd+MsNdWTfAwoAbmV0d29ya19pZHsBMA0KBgBzdGF0ZXMDBwBjYW5kbGVzAAAAAAEDAGxpdAAAAwcAdmVyc2lvbgEoFQEA" + }, + { + "id": "minecraft:white_candle", + "block_state_b64": "CgAAAwgAYmxvY2tfaWScAgAACAQAbmFtZRYAbWluZWNyYWZ0OndoaXRlX2NhbmRsZQQJAG5hbWVfaGFzaN1EG5Q1mHiEAwoAbmV0d29ya19pZKN1mmgKBgBzdGF0ZXMDBwBjYW5kbGVzAAAAAAEDAGxpdAAAAwcAdmVyc2lvbgEoFQEA" + }, + { + "id": "minecraft:orange_candle", + "block_state_b64": "CgAAAwgAYmxvY2tfaWSdAgAACAQAbmFtZRcAbWluZWNyYWZ0Om9yYW5nZV9jYW5kbGUECQBuYW1lX2hhc2jySEVWHgUIHQMKAG5ldHdvcmtfaWSfVz82CgYAc3RhdGVzAwcAY2FuZGxlcwAAAAABAwBsaXQAAAMHAHZlcnNpb24BKBUBAA==" + }, + { + "id": "minecraft:magenta_candle", + "block_state_b64": "CgAAAwgAYmxvY2tfaWSeAgAACAQAbmFtZRgAbWluZWNyYWZ0Om1hZ2VudGFfY2FuZGxlBAkAbmFtZV9oYXNoG0u6YIOoBSEDCgBuZXR3b3JrX2lk9xGNkQoGAHN0YXRlcwMHAGNhbmRsZXMAAAAAAQMAbGl0AAADBwB2ZXJzaW9uASgVAQA=" + }, + { + "id": "minecraft:light_blue_candle", + "block_state_b64": "CgAAAwgAYmxvY2tfaWSfAgAACAQAbmFtZRsAbWluZWNyYWZ0OmxpZ2h0X2JsdWVfY2FuZGxlBAkAbmFtZV9oYXNocXGeK0zgrG0DCgBuZXR3b3JrX2lk2m1y8goGAHN0YXRlcwMHAGNhbmRsZXMAAAAAAQMAbGl0AAADBwB2ZXJzaW9uASgVAQA=" + }, + { + "id": "minecraft:yellow_candle", + "block_state_b64": "CgAAAwgAYmxvY2tfaWSgAgAACAQAbmFtZRcAbWluZWNyYWZ0OnllbGxvd19jYW5kbGUECQBuYW1lX2hhc2i00dtusU3CqQMKAG5ldHdvcmtfaWR9LTmpCgYAc3RhdGVzAwcAY2FuZGxlcwAAAAABAwBsaXQAAAMHAHZlcnNpb24BKBUBAA==" + }, + { + "id": "minecraft:lime_candle", + "block_state_b64": "CgAAAwgAYmxvY2tfaWShAgAACAQAbmFtZRUAbWluZWNyYWZ0OmxpbWVfY2FuZGxlBAkAbmFtZV9oYXNokcmrw5xvz7ADCgBuZXR3b3JrX2lkIAUu6QoGAHN0YXRlcwMHAGNhbmRsZXMAAAAAAQMAbGl0AAADBwB2ZXJzaW9uASgVAQA=" + }, + { + "id": "minecraft:pink_candle", + "block_state_b64": "CgAAAwgAYmxvY2tfaWSiAgAACAQAbmFtZRUAbWluZWNyYWZ0OnBpbmtfY2FuZGxlBAkAbmFtZV9oYXNoQJdEY4sZ0dwDCgBuZXR3b3JrX2lk23Rn5AoGAHN0YXRlcwMHAGNhbmRsZXMAAAAAAQMAbGl0AAADBwB2ZXJzaW9uASgVAQA=" + }, + { + "id": "minecraft:gray_candle", + "block_state_b64": "CgAAAwgAYmxvY2tfaWSjAgAACAQAbmFtZRUAbWluZWNyYWZ0OmdyYXlfY2FuZGxlBAkAbmFtZV9oYXNoS5poSo9wBDEDCgBuZXR3b3JrX2lk3trRCAoGAHN0YXRlcwMHAGNhbmRsZXMAAAAAAQMAbGl0AAADBwB2ZXJzaW9uASgVAQA=" + }, + { + "id": "minecraft:light_gray_candle", + "block_state_b64": "CgAAAwgAYmxvY2tfaWSkAgAACAQAbmFtZRsAbWluZWNyYWZ0OmxpZ2h0X2dyYXlfY2FuZGxlBAkAbmFtZV9oYXNo9ruTZLBNMasDCgBuZXR3b3JrX2lkb6DOegoGAHN0YXRlcwMHAGNhbmRsZXMAAAAAAQMAbGl0AAADBwB2ZXJzaW9uASgVAQA=" + }, + { + "id": "minecraft:cyan_candle", + "block_state_b64": "CgAAAwgAYmxvY2tfaWSlAgAACAQAbmFtZRUAbWluZWNyYWZ0OmN5YW5fY2FuZGxlBAkAbmFtZV9oYXNoc/M8PNVcjOwDCgBuZXR3b3JrX2lkZoIQOQoGAHN0YXRlcwMHAGNhbmRsZXMAAAAAAQMAbGl0AAADBwB2ZXJzaW9uASgVAQA=" + }, + { + "id": "minecraft:purple_candle", + "block_state_b64": "CgAAAwgAYmxvY2tfaWSmAgAACAQAbmFtZRcAbWluZWNyYWZ0OnB1cnBsZV9jYW5kbGUECQBuYW1lX2hhc2jaI3xUW0/myQMKAG5ldHdvcmtfaWSnLI2BCgYAc3RhdGVzAwcAY2FuZGxlcwAAAAABAwBsaXQAAAMHAHZlcnNpb24BKBUBAA==" + }, + { + "id": "minecraft:blue_candle", + "block_state_b64": "CgAAAwgAYmxvY2tfaWSnAgAACAQAbmFtZRUAbWluZWNyYWZ0OmJsdWVfY2FuZGxlBAkAbmFtZV9oYXNoAASSPW6TgQADCgBuZXR3b3JrX2lkrxrjQAoGAHN0YXRlcwMHAGNhbmRsZXMAAAAAAQMAbGl0AAADBwB2ZXJzaW9uASgVAQA=" + }, + { + "id": "minecraft:brown_candle", + "block_state_b64": "CgAAAwgAYmxvY2tfaWSoAgAACAQAbmFtZRYAbWluZWNyYWZ0OmJyb3duX2NhbmRsZQQJAG5hbWVfaGFzaDia0l6s1+WYAwoAbmV0d29ya19pZKSkBXYKBgBzdGF0ZXMDBwBjYW5kbGVzAAAAAAEDAGxpdAAAAwcAdmVyc2lvbgEoFQEA" + }, + { + "id": "minecraft:green_candle", + "block_state_b64": "CgAAAwgAYmxvY2tfaWSpAgAACAQAbmFtZRYAbWluZWNyYWZ0OmdyZWVuX2NhbmRsZQQJAG5hbWVfaGFzaLeFPO1l+fIoAwoAbmV0d29ya19pZBkznDsKBgBzdGF0ZXMDBwBjYW5kbGVzAAAAAAEDAGxpdAAAAwcAdmVyc2lvbgEoFQEA" + }, + { + "id": "minecraft:red_candle", + "block_state_b64": "CgAAAwgAYmxvY2tfaWSqAgAACAQAbmFtZRQAbWluZWNyYWZ0OnJlZF9jYW5kbGUECQBuYW1lX2hhc2jjAQpGf59ZdwMKAG5ldHdvcmtfaWRbb88GCgYAc3RhdGVzAwcAY2FuZGxlcwAAAAABAwBsaXQAAAMHAHZlcnNpb24BKBUBAA==" + }, + { + "id": "minecraft:black_candle", + "block_state_b64": "CgAAAwgAYmxvY2tfaWSrAgAACAQAbmFtZRYAbWluZWNyYWZ0OmJsYWNrX2NhbmRsZQQJAG5hbWVfaGFzaB+wRDpOqREKAwoAbmV0d29ya19pZNnOnuEKBgBzdGF0ZXMDBwBjYW5kbGVzAAAAAAEDAGxpdAAAAwcAdmVyc2lvbgEoFQEA" + }, + { + "id": "minecraft:crafting_table", + "block_state_b64": "CgAAAwgAYmxvY2tfaWQ6AAAACAQAbmFtZRgAbWluZWNyYWZ0OmNyYWZ0aW5nX3RhYmxlBAkAbmFtZV9oYXNoe76VAmjvbpYDCgBuZXR3b3JrX2lkwCxwaAoGAHN0YXRlcwADBwB2ZXJzaW9uASgVAQA=" + }, + { + "id": "minecraft:cartography_table", + "block_state_b64": "CgAAAwgAYmxvY2tfaWTHAQAACAQAbmFtZRsAbWluZWNyYWZ0OmNhcnRvZ3JhcGh5X3RhYmxlBAkAbmFtZV9oYXNomaWiiD/znP8DCgBuZXR3b3JrX2lkI6FzMwoGAHN0YXRlcwADBwB2ZXJzaW9uASgVAQA=" + }, + { + "id": "minecraft:fletching_table", + "block_state_b64": "CgAAAwgAYmxvY2tfaWTIAQAACAQAbmFtZRkAbWluZWNyYWZ0OmZsZXRjaGluZ190YWJsZQQJAG5hbWVfaGFzaPFibh8unKyUAwoAbmV0d29ya19pZJ2mW0oKBgBzdGF0ZXMAAwcAdmVyc2lvbgEoFQEA" + }, + { + "id": "minecraft:smithing_table", + "block_state_b64": "CgAAAwgAYmxvY2tfaWTJAQAACAQAbmFtZRgAbWluZWNyYWZ0OnNtaXRoaW5nX3RhYmxlBAkAbmFtZV9oYXNo4tFES2xOXEYDCgBuZXR3b3JrX2lkXWMBzQoGAHN0YXRlcwADBwB2ZXJzaW9uASgVAQA=" + }, + { + "id": "minecraft:beehive", + "block_state_b64": "CgAAAwgAYmxvY2tfaWTaAQAACAQAbmFtZREAbWluZWNyYWZ0OmJlZWhpdmUECQBuYW1lX2hhc2hCcqn12UbNpwMKAG5ldHdvcmtfaWR/idcaCgYAc3RhdGVzAwkAZGlyZWN0aW9uAAAAAAMLAGhvbmV5X2xldmVsAAAAAAADBwB2ZXJzaW9uASgVAQA=" + }, + { + "id": "minecraft:suspicious_sand", + "block_state_b64": "CgAAAwgAYmxvY2tfaWQQAwAACAQAbmFtZRkAbWluZWNyYWZ0OnN1c3BpY2lvdXNfc2FuZAQJAG5hbWVfaGFzaL67QsuvLP00AwoAbmV0d29ya19pZKnkaIAKBgBzdGF0ZXMDEABicnVzaGVkX3Byb2dyZXNzAAAAAAEHAGhhbmdpbmcBAAMHAHZlcnNpb24BKBUBAA==" + }, + { + "id": "minecraft:suspicious_gravel", + "block_state_b64": "CgAAAwgAYmxvY2tfaWQ8AwAACAQAbmFtZRsAbWluZWNyYWZ0OnN1c3BpY2lvdXNfZ3JhdmVsBAkAbmFtZV9oYXNoJSVbGNk7C3oDCgBuZXR3b3JrX2lkvIEJAAoGAHN0YXRlcwMQAGJydXNoZWRfcHJvZ3Jlc3MAAAAAAQcAaGFuZ2luZwEAAwcAdmVyc2lvbgEoFQEA" + }, + { + "id": "minecraft:campfire" + }, + { + "id": "minecraft:soul_campfire" + }, + { + "id": "minecraft:furnace", + "block_state_b64": "CgAAAwgAYmxvY2tfaWQ9AAAACAQAbmFtZREAbWluZWNyYWZ0OmZ1cm5hY2UECQBuYW1lX2hhc2ioOQrludYY8wMKAG5ldHdvcmtfaWRZxnDOCgYAc3RhdGVzCBwAbWluZWNyYWZ0OmNhcmRpbmFsX2RpcmVjdGlvbgUAc291dGgAAwcAdmVyc2lvbgEoFQEA" + }, + { + "id": "minecraft:blast_furnace", + "block_state_b64": "CgAAAwgAYmxvY2tfaWTDAQAACAQAbmFtZRcAbWluZWNyYWZ0OmJsYXN0X2Z1cm5hY2UECQBuYW1lX2hhc2ivDbnjkpGm5QMKAG5ldHdvcmtfaWTcEbV/CgYAc3RhdGVzCBwAbWluZWNyYWZ0OmNhcmRpbmFsX2RpcmVjdGlvbgUAc291dGgAAwcAdmVyc2lvbgEoFQEA" + }, + { + "id": "minecraft:smoker", + "block_state_b64": "CgAAAwgAYmxvY2tfaWTFAQAACAQAbmFtZRAAbWluZWNyYWZ0OnNtb2tlcgQJAG5hbWVfaGFzaJd1rDMkRWomAwoAbmV0d29ya19pZGWswMwKBgBzdGF0ZXMIHABtaW5lY3JhZnQ6Y2FyZGluYWxfZGlyZWN0aW9uBQBzb3V0aAADBwB2ZXJzaW9uASgVAQA=" + }, + { + "id": "minecraft:respawn_anchor", + "block_state_b64": "CgAAAwgAYmxvY2tfaWQPAgAACAQAbmFtZRgAbWluZWNyYWZ0OnJlc3Bhd25fYW5jaG9yBAkAbmFtZV9oYXNoZOdcjW05qigDCgBuZXR3b3JrX2lkmhMcaQoGAHN0YXRlcwMVAHJlc3Bhd25fYW5jaG9yX2NoYXJnZQAAAAAAAwcAdmVyc2lvbgEoFQEA" + }, + { + "id": "minecraft:brewing_stand" + }, + { + "id": "minecraft:anvil", + "block_state_b64": "CgAAAwgAYmxvY2tfaWSRAAAACAQAbmFtZQ8AbWluZWNyYWZ0OmFudmlsBAkAbmFtZV9oYXNoNqB3fgcUCbwDCgBuZXR3b3JrX2lkqXzNjwoGAHN0YXRlcwgcAG1pbmVjcmFmdDpjYXJkaW5hbF9kaXJlY3Rpb24FAHNvdXRoAAMHAHZlcnNpb24BKBUBAA==" + }, + { + "id": "minecraft:chipped_anvil", + "block_state_b64": "CgAAAwgAYmxvY2tfaWS+BAAACAQAbmFtZRcAbWluZWNyYWZ0OmNoaXBwZWRfYW52aWwECQBuYW1lX2hhc2ge+QY3vlS/eQMKAG5ldHdvcmtfaWRJ15iUCgYAc3RhdGVzCBwAbWluZWNyYWZ0OmNhcmRpbmFsX2RpcmVjdGlvbgUAc291dGgAAwcAdmVyc2lvbgEoFQEA" + }, + { + "id": "minecraft:damaged_anvil", + "block_state_b64": "CgAAAwgAYmxvY2tfaWS/BAAACAQAbmFtZRcAbWluZWNyYWZ0OmRhbWFnZWRfYW52aWwECQBuYW1lX2hhc2imJ12Be2V8+AMKAG5ldHdvcmtfaWRh5SHkCgYAc3RhdGVzCBwAbWluZWNyYWZ0OmNhcmRpbmFsX2RpcmVjdGlvbgUAc291dGgAAwcAdmVyc2lvbgEoFQEA" + }, + { + "id": "minecraft:grindstone", + "block_state_b64": "CgAAAwgAYmxvY2tfaWTCAQAACAQAbmFtZRQAbWluZWNyYWZ0OmdyaW5kc3RvbmUECQBuYW1lX2hhc2id56zc0nk99wMKAG5ldHdvcmtfaWS4Es07CgYAc3RhdGVzCAoAYXR0YWNobWVudAgAc3RhbmRpbmcDCQBkaXJlY3Rpb24AAAAAAAMHAHZlcnNpb24BKBUBAA==" + }, + { + "id": "minecraft:enchanting_table", + "block_state_b64": "CgAAAwgAYmxvY2tfaWR0AAAACAQAbmFtZRoAbWluZWNyYWZ0OmVuY2hhbnRpbmdfdGFibGUECQBuYW1lX2hhc2jgIx24VLvMvwMKAG5ldHdvcmtfaWRliFFJCgYAc3RhdGVzAAMHAHZlcnNpb24BKBUBAA==" + }, + { + "id": "minecraft:bookshelf", + "block_state_b64": "CgAAAwgAYmxvY2tfaWQvAAAACAQAbmFtZRMAbWluZWNyYWZ0OmJvb2tzaGVsZgQJAG5hbWVfaGFzaDU04DrgJCS9AwoAbmV0d29ya19pZBcWwIwKBgBzdGF0ZXMAAwcAdmVyc2lvbgEoFQEA" + }, + { + "id": "minecraft:chiseled_bookshelf", + "block_state_b64": "CgAAAwgAYmxvY2tfaWQNAwAACAQAbmFtZRwAbWluZWNyYWZ0OmNoaXNlbGVkX2Jvb2tzaGVsZgQJAG5hbWVfaGFzaNXDBnsIsywYAwoAbmV0d29ya19pZIprt5IKBgBzdGF0ZXMDDABib29rc19zdG9yZWQAAAAAAwkAZGlyZWN0aW9uAAAAAAADBwB2ZXJzaW9uASgVAQA=" + }, + { + "id": "minecraft:lectern", + "block_state_b64": "CgAAAwgAYmxvY2tfaWTBAQAACAQAbmFtZREAbWluZWNyYWZ0OmxlY3Rlcm4ECQBuYW1lX2hhc2j5Z4Mmi/1QxAMKAG5ldHdvcmtfaWR4JfDHCgYAc3RhdGVzCBwAbWluZWNyYWZ0OmNhcmRpbmFsX2RpcmVjdGlvbgUAc291dGgBCwBwb3dlcmVkX2JpdAAAAwcAdmVyc2lvbgEoFQEA" + }, + { + "id": "minecraft:cauldron" + }, + { + "id": "minecraft:composter", + "block_state_b64": "CgAAAwgAYmxvY2tfaWTUAQAACAQAbmFtZRMAbWluZWNyYWZ0OmNvbXBvc3RlcgQJAG5hbWVfaGFzaPAADHptzeWJAwoAbmV0d29ya19pZHIL6i4KBgBzdGF0ZXMDFABjb21wb3N0ZXJfZmlsbF9sZXZlbAAAAAAAAwcAdmVyc2lvbgEoFQEA" + }, + { + "id": "minecraft:chest", + "block_state_b64": "CgAAAwgAYmxvY2tfaWQ2AAAACAQAbmFtZQ8AbWluZWNyYWZ0OmNoZXN0BAkAbmFtZV9oYXNog9ozMxlcA88DCgBuZXR3b3JrX2lkDkOFvAoGAHN0YXRlcwgcAG1pbmVjcmFmdDpjYXJkaW5hbF9kaXJlY3Rpb24FAG5vcnRoAAMHAHZlcnNpb24BKBUBAA==" + }, + { + "id": "minecraft:trapped_chest", + "block_state_b64": "CgAAAwgAYmxvY2tfaWSSAAAACAQAbmFtZRcAbWluZWNyYWZ0OnRyYXBwZWRfY2hlc3QECQBuYW1lX2hhc2g2qpF9stsEjgMKAG5ldHdvcmtfaWTjJWYxCgYAc3RhdGVzCBwAbWluZWNyYWZ0OmNhcmRpbmFsX2RpcmVjdGlvbgUAbm9ydGgAAwcAdmVyc2lvbgEoFQEA" + }, + { + "id": "minecraft:ender_chest", + "block_state_b64": "CgAAAwgAYmxvY2tfaWSCAAAACAQAbmFtZRUAbWluZWNyYWZ0OmVuZGVyX2NoZXN0BAkAbmFtZV9oYXNohEZzOFdg0WUDCgBuZXR3b3JrX2lkx4jiSQoGAHN0YXRlcwgcAG1pbmVjcmFmdDpjYXJkaW5hbF9kaXJlY3Rpb24FAG5vcnRoAAMHAHZlcnNpb24BKBUBAA==" + }, + { + "id": "minecraft:barrel", + "block_state_b64": "CgAAAwgAYmxvY2tfaWTKAQAACAQAbmFtZRAAbWluZWNyYWZ0OmJhcnJlbAQJAG5hbWVfaGFzaHDkRPGymiRqAwoAbmV0d29ya19pZPnxzgsKBgBzdGF0ZXMDEABmYWNpbmdfZGlyZWN0aW9uAAAAAAEIAG9wZW5fYml0AAADBwB2ZXJzaW9uASgVAQA=" + }, + { + "id": "minecraft:undyed_shulker_box", + "block_state_b64": "CgAAAwgAYmxvY2tfaWTNAAAACAQAbmFtZRwAbWluZWNyYWZ0OnVuZHllZF9zaHVsa2VyX2JveAQJAG5hbWVfaGFzaOC9mypm/MlBAwoAbmV0d29ya19pZJ8rxp0KBgBzdGF0ZXMAAwcAdmVyc2lvbgEoFQEA" + }, + { + "id": "minecraft:white_shulker_box", + "block_state_b64": "CgAAAwgAYmxvY2tfaWTaAAAACAQAbmFtZRsAbWluZWNyYWZ0OndoaXRlX3NodWxrZXJfYm94BAkAbmFtZV9oYXNosK79m1rPUBwDCgBuZXR3b3JrX2lkjrET6goGAHN0YXRlcwADBwB2ZXJzaW9uASgVAQA=" + }, + { + "id": "minecraft:light_gray_shulker_box", + "block_state_b64": "CgAAAwgAYmxvY2tfaWRrAwAACAQAbmFtZSAAbWluZWNyYWZ0OmxpZ2h0X2dyYXlfc2h1bGtlcl9ib3gECQBuYW1lX2hhc2iBe5zq7PxHmgMKAG5ldHdvcmtfaWSCVJv0CgYAc3RhdGVzAAMHAHZlcnNpb24BKBUBAA==" + }, + { + "id": "minecraft:gray_shulker_box", + "block_state_b64": "CgAAAwgAYmxvY2tfaWRqAwAACAQAbmFtZRoAbWluZWNyYWZ0OmdyYXlfc2h1bGtlcl9ib3gECQBuYW1lX2hhc2ga2s8ctjHUhgMKAG5ldHdvcmtfaWS3WMsWCgYAc3RhdGVzAAMHAHZlcnNpb24BKBUBAA==" + }, + { + "id": "minecraft:black_shulker_box", + "block_state_b64": "CgAAAwgAYmxvY2tfaWRyAwAACAQAbmFtZRsAbWluZWNyYWZ0OmJsYWNrX3NodWxrZXJfYm94BAkAbmFtZV9oYXNoPm03OZphrp8DCgBuZXR3b3JrX2lkXHztNAoGAHN0YXRlcwADBwB2ZXJzaW9uASgVAQA=" + }, + { + "id": "minecraft:brown_shulker_box", + "block_state_b64": "CgAAAwgAYmxvY2tfaWRvAwAACAQAbmFtZRsAbWluZWNyYWZ0OmJyb3duX3NodWxrZXJfYm94BAkAbmFtZV9oYXNoT3DD6qAL9cADCgBuZXR3b3JrX2lkaXxpYQoGAHN0YXRlcwADBwB2ZXJzaW9uASgVAQA=" + }, + { + "id": "minecraft:red_shulker_box", + "block_state_b64": "CgAAAwgAYmxvY2tfaWRxAwAACAQAbmFtZRkAbWluZWNyYWZ0OnJlZF9zaHVsa2VyX2JveAQJAG5hbWVfaGFzaMIlKSCzqSZoAwoAbmV0d29ya19pZNrf+icKBgBzdGF0ZXMAAwcAdmVyc2lvbgEoFQEA" + }, + { + "id": "minecraft:orange_shulker_box", + "block_state_b64": "CgAAAwgAYmxvY2tfaWRkAwAACAQAbmFtZRwAbWluZWNyYWZ0Om9yYW5nZV9zaHVsa2VyX2JveAQJAG5hbWVfaGFzaG2MAXU67wGrAwoAbmV0d29ya19pZGoO05gKBgBzdGF0ZXMAAwcAdmVyc2lvbgEoFQEA" + }, + { + "id": "minecraft:yellow_shulker_box", + "block_state_b64": "CgAAAwgAYmxvY2tfaWRnAwAACAQAbmFtZRwAbWluZWNyYWZ0OnllbGxvd19zaHVsa2VyX2JveAQJAG5hbWVfaGFzaIsLwQHYjcIEAwoAbmV0d29ya19pZBCBSiYKBgBzdGF0ZXMAAwcAdmVyc2lvbgEoFQEA" + }, + { + "id": "minecraft:lime_shulker_box", + "block_state_b64": "CgAAAwgAYmxvY2tfaWRoAwAACAQAbmFtZRoAbWluZWNyYWZ0OmxpbWVfc2h1bGtlcl9ib3gECQBuYW1lX2hhc2hUwBkg+faUGAMKAG5ldHdvcmtfaWRJeKqqCgYAc3RhdGVzAAMHAHZlcnNpb24BKBUBAA==" + }, + { + "id": "minecraft:green_shulker_box", + "block_state_b64": "CgAAAwgAYmxvY2tfaWRwAwAACAQAbmFtZRsAbWluZWNyYWZ0OmdyZWVuX3NodWxrZXJfYm94BAkAbmFtZV9oYXNoZgUeT3LupLUDCgBuZXR3b3JrX2lkzJiohQoGAHN0YXRlcwADBwB2ZXJzaW9uASgVAQA=" + }, + { + "id": "minecraft:cyan_shulker_box", + "block_state_b64": "CgAAAwgAYmxvY2tfaWRsAwAACAQAbmFtZRoAbWluZWNyYWZ0OmN5YW5fc2h1bGtlcl9ib3gECQBuYW1lX2hhc2gSfbjteXg5yAMKAG5ldHdvcmtfaWTHeliECgYAc3RhdGVzAAMHAHZlcnNpb24BKBUBAA==" + }, + { + "id": "minecraft:light_blue_shulker_box", + "block_state_b64": "CgAAAwgAYmxvY2tfaWRmAwAACAQAbmFtZSAAbWluZWNyYWZ0OmxpZ2h0X2JsdWVfc2h1bGtlcl9ib3gECQBuYW1lX2hhc2h0VFCX0qsRxQMKAG5ldHdvcmtfaWQXD8U0CgYAc3RhdGVzAAMHAHZlcnNpb24BKBUBAA==" + }, + { + "id": "minecraft:blue_shulker_box", + "block_state_b64": "CgAAAwgAYmxvY2tfaWRuAwAACAQAbmFtZRoAbWluZWNyYWZ0OmJsdWVfc2h1bGtlcl9ib3gECQBuYW1lX2hhc2hn9gS0XIe6rAMKAG5ldHdvcmtfaWTO4PJaCgYAc3RhdGVzAAMHAHZlcnNpb24BKBUBAA==" + }, + { + "id": "minecraft:purple_shulker_box", + "block_state_b64": "CgAAAwgAYmxvY2tfaWRtAwAACAQAbmFtZRwAbWluZWNyYWZ0OnB1cnBsZV9zaHVsa2VyX2JveAQJAG5hbWVfaGFzaEV/lkNPxRDdAwoAbmV0d29ya19pZFK25GAKBgBzdGF0ZXMAAwcAdmVyc2lvbgEoFQEA" + }, + { + "id": "minecraft:magenta_shulker_box", + "block_state_b64": "CgAAAwgAYmxvY2tfaWRlAwAACAQAbmFtZR0AbWluZWNyYWZ0Om1hZ2VudGFfc2h1bGtlcl9ib3gECQBuYW1lX2hhc2iqWM7IJHxcFgMKAG5ldHdvcmtfaWTyyudTCgYAc3RhdGVzAAMHAHZlcnNpb24BKBUBAA==" + }, + { + "id": "minecraft:pink_shulker_box", + "block_state_b64": "CgAAAwgAYmxvY2tfaWRpAwAACAQAbmFtZRoAbWluZWNyYWZ0OnBpbmtfc2h1bGtlcl9ib3gECQBuYW1lX2hhc2in1tkJ1GNcZgMKAG5ldHdvcmtfaWQOEGXjCgYAc3RhdGVzAAMHAHZlcnNpb24BKBUBAA==" + }, + { + "id": "minecraft:armor_stand" + }, + { + "id": "minecraft:noteblock", + "block_state_b64": "CgAAAwgAYmxvY2tfaWQZAAAACAQAbmFtZRMAbWluZWNyYWZ0Om5vdGVibG9jawQJAG5hbWVfaGFzaHPA8dBBH0UaAwoAbmV0d29ya19pZH1U5QkKBgBzdGF0ZXMAAwcAdmVyc2lvbgEoFQEA" + }, + { + "id": "minecraft:jukebox", + "block_state_b64": "CgAAAwgAYmxvY2tfaWRUAAAACAQAbmFtZREAbWluZWNyYWZ0Omp1a2Vib3gECQBuYW1lX2hhc2ieAIPExf/ZfgMKAG5ldHdvcmtfaWSmR7JfCgYAc3RhdGVzAAMHAHZlcnNpb24BKBUBAA==" + }, + { + "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:music_disc_relic" + }, + { + "id": "minecraft:music_disc_creator" + }, + { + "id": "minecraft:music_disc_creator_music_box" + }, + { + "id": "minecraft:music_disc_precipice" + }, + { + "id": "minecraft:disc_fragment_5" + }, + { + "id": "minecraft:glowstone_dust" + }, + { + "id": "minecraft:glowstone", + "block_state_b64": "CgAAAwgAYmxvY2tfaWRZAAAACAQAbmFtZRMAbWluZWNyYWZ0Omdsb3dzdG9uZQQJAG5hbWVfaGFzaFYqXNkefIlPAwoAbmV0d29ya19pZGT7WYYKBgBzdGF0ZXMAAwcAdmVyc2lvbgEoFQEA" + }, + { + "id": "minecraft:redstone_lamp", + "block_state_b64": "CgAAAwgAYmxvY2tfaWR7AAAACAQAbmFtZRcAbWluZWNyYWZ0OnJlZHN0b25lX2xhbXAECQBuYW1lX2hhc2hJ9V80caPvEgMKAG5ldHdvcmtfaWRvNPwnCgYAc3RhdGVzAAMHAHZlcnNpb24BKBUBAA==" + }, + { + "id": "minecraft:sea_lantern", + "block_state_b64": "CgAAAwgAYmxvY2tfaWSpAAAACAQAbmFtZRUAbWluZWNyYWZ0OnNlYV9sYW50ZXJuBAkAbmFtZV9oYXNoLPsv1TX9M+QDCgBuZXR3b3JrX2lk1PPVyAoGAHN0YXRlcwADBwB2ZXJzaW9uASgVAQA=" + }, + { + "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:cherry_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:mangrove_hanging_sign" + }, + { + "id": "minecraft:cherry_hanging_sign" + }, + { + "id": "minecraft:bamboo_hanging_sign" + }, + { + "id": "minecraft:crimson_hanging_sign" + }, + { + "id": "minecraft:warped_hanging_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:player_head", + "block_state_b64": "CgAAAwgAYmxvY2tfaWTGBAAACAQAbmFtZRUAbWluZWNyYWZ0OnBsYXllcl9oZWFkBAkAbmFtZV9oYXNonFwZb7CL8EYDCgBuZXR3b3JrX2lkZeAXqAoGAHN0YXRlcwMQAGZhY2luZ19kaXJlY3Rpb24AAAAAAAMHAHZlcnNpb24BKBUBAA==" + }, + { + "id": "minecraft:zombie_head", + "block_state_b64": "CgAAAwgAYmxvY2tfaWTFBAAACAQAbmFtZRUAbWluZWNyYWZ0OnpvbWJpZV9oZWFkBAkAbmFtZV9oYXNoixuENYuaGgEDCgBuZXR3b3JrX2lk0NsHDgoGAHN0YXRlcwMQAGZhY2luZ19kaXJlY3Rpb24AAAAAAAMHAHZlcnNpb24BKBUBAA==" + }, + { + "id": "minecraft:creeper_head", + "block_state_b64": "CgAAAwgAYmxvY2tfaWTHBAAACAQAbmFtZRYAbWluZWNyYWZ0OmNyZWVwZXJfaGVhZAQJAG5hbWVfaGFzaCvAGFMS/RqVAwoAbmV0d29ya19pZEfskXYKBgBzdGF0ZXMDEABmYWNpbmdfZGlyZWN0aW9uAAAAAAADBwB2ZXJzaW9uASgVAQA=" + }, + { + "id": "minecraft:dragon_head", + "block_state_b64": "CgAAAwgAYmxvY2tfaWTIBAAACAQAbmFtZRUAbWluZWNyYWZ0OmRyYWdvbl9oZWFkBAkAbmFtZV9oYXNozjh6bGRaa5UDCgBuZXR3b3JrX2lk/zjetgoGAHN0YXRlcwMQAGZhY2luZ19kaXJlY3Rpb24AAAAAAAMHAHZlcnNpb24BKBUBAA==" + }, + { + "id": "minecraft:skeleton_skull", + "block_state_b64": "CgAAAwgAYmxvY2tfaWSQAAAACAQAbmFtZRgAbWluZWNyYWZ0OnNrZWxldG9uX3NrdWxsBAkAbmFtZV9oYXNo3+kbzeMgg4kDCgBuZXR3b3JrX2lk/RqWbwoGAHN0YXRlcwMQAGZhY2luZ19kaXJlY3Rpb24AAAAAAAMHAHZlcnNpb24BKBUBAA==" + }, + { + "id": "minecraft:wither_skeleton_skull", + "block_state_b64": "CgAAAwgAYmxvY2tfaWTEBAAACAQAbmFtZR8AbWluZWNyYWZ0OndpdGhlcl9za2VsZXRvbl9za3VsbAQJAG5hbWVfaGFzaEcZrUyy9cfRAwoAbmV0d29ya19pZJZ2G/oKBgBzdGF0ZXMDEABmYWNpbmdfZGlyZWN0aW9uAAAAAAADBwB2ZXJzaW9uASgVAQA=" + }, + { + "id": "minecraft:piglin_head", + "block_state_b64": "CgAAAwgAYmxvY2tfaWTJBAAACAQAbmFtZRUAbWluZWNyYWZ0OnBpZ2xpbl9oZWFkBAkAbmFtZV9oYXNo+jUCKgb5DskDCgBuZXR3b3JrX2lkQ1ETVwoGAHN0YXRlcwMQAGZhY2luZ19kaXJlY3Rpb24AAAAAAAMHAHZlcnNpb24BKBUBAA==" + }, + { + "id": "minecraft:beacon", + "block_state_b64": "CgAAAwgAYmxvY2tfaWSKAAAACAQAbmFtZRAAbWluZWNyYWZ0OmJlYWNvbgQJAG5hbWVfaGFzaACwhhfSkdkHAwoAbmV0d29ya19pZF8jfiEKBgBzdGF0ZXMAAwcAdmVyc2lvbgEoFQEA" + }, + { + "id": "minecraft:bell", + "block_state_b64": "CgAAAwgAYmxvY2tfaWTNAQAACAQAbmFtZQ4AbWluZWNyYWZ0OmJlbGwECQBuYW1lX2hhc2iPqsgDXRcsxAMKAG5ldHdvcmtfaWT7zhOoCgYAc3RhdGVzCAoAYXR0YWNobWVudAgAc3RhbmRpbmcDCQBkaXJlY3Rpb24AAAAAAQoAdG9nZ2xlX2JpdAAAAwcAdmVyc2lvbgEoFQEA" + }, + { + "id": "minecraft:conduit", + "block_state_b64": "CgAAAwgAYmxvY2tfaWScAQAACAQAbmFtZREAbWluZWNyYWZ0OmNvbmR1aXQECQBuYW1lX2hhc2jqxKAxq2EaWQMKAG5ldHdvcmtfaWTWcBVnCgYAc3RhdGVzAAMHAHZlcnNpb24BKBUBAA==" + }, + { + "id": "minecraft:stonecutter_block", + "block_state_b64": "CgAAAwgAYmxvY2tfaWTEAQAACAQAbmFtZRsAbWluZWNyYWZ0OnN0b25lY3V0dGVyX2Jsb2NrBAkAbmFtZV9oYXNoQAXTbAM3MeYDCgBuZXR3b3JrX2lkWS4RjAoGAHN0YXRlcwgcAG1pbmVjcmFmdDpjYXJkaW5hbF9kaXJlY3Rpb24FAG5vcnRoAAMHAHZlcnNpb24BKBUBAA==" + }, + { + "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:turtle_scute" + }, + { + "id": "minecraft:armadillo_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:breeze_rod" + }, + { + "id": "minecraft:heavy_core", + "block_state_b64": "CgAAAwgAYmxvY2tfaWQ7AgAACAQAbmFtZRQAbWluZWNyYWZ0OmhlYXZ5X2NvcmUECQBuYW1lX2hhc2hhz/uNCtrC2QMKAG5ldHdvcmtfaWRaFu+8CgYAc3RhdGVzAAMHAHZlcnNpb24BKBUBAA==" + }, + { + "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", + "block_state_b64": "CgAAAwgAYmxvY2tfaWTQAAAACAQAbmFtZREAbWluZWNyYWZ0OmVuZF9yb2QECQBuYW1lX2hhc2jx/q5cEA0hmQMKAG5ldHdvcmtfaWQ2eM8kCgYAc3RhdGVzAxAAZmFjaW5nX2RpcmVjdGlvbgAAAAAAAwcAdmVyc2lvbgEoFQEA" + }, + { + "id": "minecraft:lightning_rod", + "block_state_b64": "CgAAAwgAYmxvY2tfaWQ3AgAACAQAbmFtZRcAbWluZWNyYWZ0OmxpZ2h0bmluZ19yb2QECQBuYW1lX2hhc2ioXQF1xvfHNQMKAG5ldHdvcmtfaWRLuHyACgYAc3RhdGVzAxAAZmFjaW5nX2RpcmVjdGlvbgAAAAAAAwcAdmVyc2lvbgEoFQEA" + }, + { + "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:enchanted_book", + "nbt_b64": "CgAACQQAZW5jaAoBAAAAAgIAaWQmAAIDAGx2bAEAAAA=" + }, + { + "id": "minecraft:enchanted_book", + "nbt_b64": "CgAACQQAZW5jaAoBAAAAAgIAaWQmAAIDAGx2bAIAAAA=" + }, + { + "id": "minecraft:enchanted_book", + "nbt_b64": "CgAACQQAZW5jaAoBAAAAAgIAaWQmAAIDAGx2bAMAAAA=" + }, + { + "id": "minecraft:enchanted_book", + "nbt_b64": "CgAACQQAZW5jaAoBAAAAAgIAaWQnAAIDAGx2bAEAAAA=" + }, + { + "id": "minecraft:enchanted_book", + "nbt_b64": "CgAACQQAZW5jaAoBAAAAAgIAaWQnAAIDAGx2bAIAAAA=" + }, + { + "id": "minecraft:enchanted_book", + "nbt_b64": "CgAACQQAZW5jaAoBAAAAAgIAaWQnAAIDAGx2bAMAAAA=" + }, + { + "id": "minecraft:enchanted_book", + "nbt_b64": "CgAACQQAZW5jaAoBAAAAAgIAaWQnAAIDAGx2bAQAAAA=" + }, + { + "id": "minecraft:enchanted_book", + "nbt_b64": "CgAACQQAZW5jaAoBAAAAAgIAaWQnAAIDAGx2bAUAAAA=" + }, + { + "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": "CgAACQQAZW5jaAoBAAAAAgIAaWQoAAIDAGx2bAQAAAA=" + }, + { + "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:cherry_boat" + }, + { + "id": "minecraft:bamboo_raft" + }, + { + "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:cherry_chest_boat" + }, + { + "id": "minecraft:bamboo_chest_raft" + }, + { + "id": "minecraft:rail", + "block_state_b64": "CgAAAwgAYmxvY2tfaWRCAAAACAQAbmFtZQ4AbWluZWNyYWZ0OnJhaWwECQBuYW1lX2hhc2hUzmhUXYJDUQMKAG5ldHdvcmtfaWR+Sp6YCgYAc3RhdGVzAw4AcmFpbF9kaXJlY3Rpb24AAAAAAAMHAHZlcnNpb24BKBUBAA==" + }, + { + "id": "minecraft:golden_rail", + "block_state_b64": "CgAAAwgAYmxvY2tfaWQbAAAACAQAbmFtZRUAbWluZWNyYWZ0OmdvbGRlbl9yYWlsBAkAbmFtZV9oYXNoOoV5MaKipoUDCgBuZXR3b3JrX2lkfAcxLwoGAHN0YXRlcwENAHJhaWxfZGF0YV9iaXQAAw4AcmFpbF9kaXJlY3Rpb24AAAAAAAMHAHZlcnNpb24BKBUBAA==" + }, + { + "id": "minecraft:detector_rail", + "block_state_b64": "CgAAAwgAYmxvY2tfaWQcAAAACAQAbmFtZRcAbWluZWNyYWZ0OmRldGVjdG9yX3JhaWwECQBuYW1lX2hhc2gVUk31qOysUQMKAG5ldHdvcmtfaWRVW/aICgYAc3RhdGVzAQ0AcmFpbF9kYXRhX2JpdAADDgByYWlsX2RpcmVjdGlvbgAAAAAAAwcAdmVyc2lvbgEoFQEA" + }, + { + "id": "minecraft:activator_rail", + "block_state_b64": "CgAAAwgAYmxvY2tfaWR+AAAACAQAbmFtZRgAbWluZWNyYWZ0OmFjdGl2YXRvcl9yYWlsBAkAbmFtZV9oYXNosIL91qriCRkDCgBuZXR3b3JrX2lkZfckmwoGAHN0YXRlcwENAHJhaWxfZGF0YV9iaXQAAw4AcmFpbF9kaXJlY3Rpb24AAAAAAAMHAHZlcnNpb24BKBUBAA==" + }, + { + "id": "minecraft:minecart" + }, + { + "id": "minecraft:chest_minecart" + }, + { + "id": "minecraft:hopper_minecart" + }, + { + "id": "minecraft:tnt_minecart" + }, + { + "id": "minecraft:redstone" + }, + { + "id": "minecraft:redstone_block", + "block_state_b64": "CgAAAwgAYmxvY2tfaWSYAAAACAQAbmFtZRgAbWluZWNyYWZ0OnJlZHN0b25lX2Jsb2NrBAkAbmFtZV9oYXNoRhULL0r8o0sDCgBuZXR3b3JrX2lklayOHgoGAHN0YXRlcwADBwB2ZXJzaW9uASgVAQA=" + }, + { + "id": "minecraft:redstone_torch", + "block_state_b64": "CgAAAwgAYmxvY2tfaWRMAAAACAQAbmFtZRgAbWluZWNyYWZ0OnJlZHN0b25lX3RvcmNoBAkAbmFtZV9oYXNoizFRjpYMIDgDCgBuZXR3b3JrX2lkuHz7yAoGAHN0YXRlcwgWAHRvcmNoX2ZhY2luZ19kaXJlY3Rpb24HAHVua25vd24AAwcAdmVyc2lvbgEoFQEA" + }, + { + "id": "minecraft:lever", + "block_state_b64": "CgAAAwgAYmxvY2tfaWRFAAAACAQAbmFtZQ8AbWluZWNyYWZ0OmxldmVyBAkAbmFtZV9oYXNoGMJeLJsUMLYDCgBuZXR3b3JrX2lkEF/GuAoGAHN0YXRlcwgPAGxldmVyX2RpcmVjdGlvbg4AZG93bl9lYXN0X3dlc3QBCABvcGVuX2JpdAAAAwcAdmVyc2lvbgEoFQEA" + }, + { + "id": "minecraft:wooden_button", + "block_state_b64": "CgAAAwgAYmxvY2tfaWSPAAAACAQAbmFtZRcAbWluZWNyYWZ0Ondvb2Rlbl9idXR0b24ECQBuYW1lX2hhc2hR7PgSTQt0sQMKAG5ldHdvcmtfaWSU07kYCgYAc3RhdGVzARIAYnV0dG9uX3ByZXNzZWRfYml0AAMQAGZhY2luZ19kaXJlY3Rpb24AAAAAAAMHAHZlcnNpb24BKBUBAA==" + }, + { + "id": "minecraft:spruce_button", + "block_state_b64": "CgAAAwgAYmxvY2tfaWSPAQAACAQAbmFtZRcAbWluZWNyYWZ0OnNwcnVjZV9idXR0b24ECQBuYW1lX2hhc2jBW9Z8aYE7YQMKAG5ldHdvcmtfaWTkUIGuCgYAc3RhdGVzARIAYnV0dG9uX3ByZXNzZWRfYml0AAMQAGZhY2luZ19kaXJlY3Rpb24AAAAAAAMHAHZlcnNpb24BKBUBAA==" + }, + { + "id": "minecraft:birch_button", + "block_state_b64": "CgAAAwgAYmxvY2tfaWSMAQAACAQAbmFtZRYAbWluZWNyYWZ0OmJpcmNoX2J1dHRvbgQJAG5hbWVfaGFzaJXYgGuSHbTwAwoAbmV0d29ya19pZGWp3yoKBgBzdGF0ZXMBEgBidXR0b25fcHJlc3NlZF9iaXQAAxAAZmFjaW5nX2RpcmVjdGlvbgAAAAAAAwcAdmVyc2lvbgEoFQEA" + }, + { + "id": "minecraft:jungle_button", + "block_state_b64": "CgAAAwgAYmxvY2tfaWSOAQAACAQAbmFtZRcAbWluZWNyYWZ0Omp1bmdsZV9idXR0b24ECQBuYW1lX2hhc2iCgNANcJs+BQMKAG5ldHdvcmtfaWT9fImWCgYAc3RhdGVzARIAYnV0dG9uX3ByZXNzZWRfYml0AAMQAGZhY2luZ19kaXJlY3Rpb24AAAAAAAMHAHZlcnNpb24BKBUBAA==" + }, + { + "id": "minecraft:acacia_button", + "block_state_b64": "CgAAAwgAYmxvY2tfaWSLAQAACAQAbmFtZRcAbWluZWNyYWZ0OmFjYWNpYV9idXR0b24ECQBuYW1lX2hhc2gVvmcT7LTO0wMKAG5ldHdvcmtfaWRQnxIJCgYAc3RhdGVzARIAYnV0dG9uX3ByZXNzZWRfYml0AAMQAGZhY2luZ19kaXJlY3Rpb24AAAAAAAMHAHZlcnNpb24BKBUBAA==" + }, + { + "id": "minecraft:dark_oak_button", + "block_state_b64": "CgAAAwgAYmxvY2tfaWSNAQAACAQAbmFtZRkAbWluZWNyYWZ0OmRhcmtfb2FrX2J1dHRvbgQJAG5hbWVfaGFzaIV10ZGGrCIEAwoAbmV0d29ya19pZN5vAmIKBgBzdGF0ZXMBEgBidXR0b25fcHJlc3NlZF9iaXQAAxAAZmFjaW5nX2RpcmVjdGlvbgAAAAAAAwcAdmVyc2lvbgEoFQEA" + }, + { + "id": "minecraft:mangrove_button", + "block_state_b64": "CgAAAwgAYmxvY2tfaWTmAgAACAQAbmFtZRkAbWluZWNyYWZ0Om1hbmdyb3ZlX2J1dHRvbgQJAG5hbWVfaGFzaNzeYYKLgOzJAwoAbmV0d29ya19pZAFEGQ0KBgBzdGF0ZXMBEgBidXR0b25fcHJlc3NlZF9iaXQAAxAAZmFjaW5nX2RpcmVjdGlvbgAAAAAAAwcAdmVyc2lvbgEoFQEA" + }, + { + "id": "minecraft:cherry_button", + "block_state_b64": "CgAAAwgAYmxvY2tfaWQRAwAACAQAbmFtZRcAbWluZWNyYWZ0OmNoZXJyeV9idXR0b24ECQBuYW1lX2hhc2j2/IHjeAbUcwMKAG5ldHdvcmtfaWRJ1irQCgYAc3RhdGVzARIAYnV0dG9uX3ByZXNzZWRfYml0AAMQAGZhY2luZ19kaXJlY3Rpb24AAAAAAAMHAHZlcnNpb24BKBUBAA==" + }, + { + "id": "minecraft:bamboo_button", + "block_state_b64": "CgAAAwgAYmxvY2tfaWT+AgAACAQAbmFtZRcAbWluZWNyYWZ0OmJhbWJvb19idXR0b24ECQBuYW1lX2hhc2j7AddMi+6nsgMKAG5ldHdvcmtfaWSa9w4/CgYAc3RhdGVzARIAYnV0dG9uX3ByZXNzZWRfYml0AAMQAGZhY2luZ19kaXJlY3Rpb24AAAAAAAMHAHZlcnNpb24BKBUBAA==" + }, + { + "id": "minecraft:stone_button", + "block_state_b64": "CgAAAwgAYmxvY2tfaWRNAAAACAQAbmFtZRYAbWluZWNyYWZ0OnN0b25lX2J1dHRvbgQJAG5hbWVfaGFzaM4ejMctmvohAwoAbmV0d29ya19pZMw+aC0KBgBzdGF0ZXMBEgBidXR0b25fcHJlc3NlZF9iaXQAAxAAZmFjaW5nX2RpcmVjdGlvbgAAAAAAAwcAdmVyc2lvbgEoFQEA" + }, + { + "id": "minecraft:crimson_button", + "block_state_b64": "CgAAAwgAYmxvY2tfaWQDAgAACAQAbmFtZRgAbWluZWNyYWZ0OmNyaW1zb25fYnV0dG9uBAkAbmFtZV9oYXNofnjYHaYIeWgDCgBuZXR3b3JrX2lk+n1vyQoGAHN0YXRlcwESAGJ1dHRvbl9wcmVzc2VkX2JpdAADEABmYWNpbmdfZGlyZWN0aW9uAAAAAAADBwB2ZXJzaW9uASgVAQA=" + }, + { + "id": "minecraft:warped_button", + "block_state_b64": "CgAAAwgAYmxvY2tfaWQEAgAACAQAbmFtZRcAbWluZWNyYWZ0OndhcnBlZF9idXR0b24ECQBuYW1lX2hhc2jwkV2EU6Cn1QMKAG5ldHdvcmtfaWTnHnk1CgYAc3RhdGVzARIAYnV0dG9uX3ByZXNzZWRfYml0AAMQAGZhY2luZ19kaXJlY3Rpb24AAAAAAAMHAHZlcnNpb24BKBUBAA==" + }, + { + "id": "minecraft:polished_blackstone_button", + "block_state_b64": "CgAAAwgAYmxvY2tfaWQnAgAACAQAbmFtZSQAbWluZWNyYWZ0OnBvbGlzaGVkX2JsYWNrc3RvbmVfYnV0dG9uBAkAbmFtZV9oYXNojmxzQKS0S/EDCgBuZXR3b3JrX2lkDtQ95woGAHN0YXRlcwESAGJ1dHRvbl9wcmVzc2VkX2JpdAADEABmYWNpbmdfZGlyZWN0aW9uAAAAAAADBwB2ZXJzaW9uASgVAQA=" + }, + { + "id": "minecraft:tripwire_hook", + "block_state_b64": "CgAAAwgAYmxvY2tfaWSDAAAACAQAbmFtZRcAbWluZWNyYWZ0OnRyaXB3aXJlX2hvb2sECQBuYW1lX2hhc2gQdp+oGZLNnAMKAG5ldHdvcmtfaWSy+1KJCgYAc3RhdGVzAQwAYXR0YWNoZWRfYml0AAMJAGRpcmVjdGlvbgAAAAABCwBwb3dlcmVkX2JpdAAAAwcAdmVyc2lvbgEoFQEA" + }, + { + "id": "minecraft:wooden_pressure_plate", + "block_state_b64": "CgAAAwgAYmxvY2tfaWRIAAAACAQAbmFtZR8AbWluZWNyYWZ0Ondvb2Rlbl9wcmVzc3VyZV9wbGF0ZQQJAG5hbWVfaGFzaGkGs5kCuA74AwoAbmV0d29ya19pZDRzPNwKBgBzdGF0ZXMDDwByZWRzdG9uZV9zaWduYWwAAAAAAAMHAHZlcnNpb24BKBUBAA==" + }, + { + "id": "minecraft:spruce_pressure_plate", + "block_state_b64": "CgAAAwgAYmxvY2tfaWSZAQAACAQAbmFtZR8AbWluZWNyYWZ0OnNwcnVjZV9wcmVzc3VyZV9wbGF0ZQQJAG5hbWVfaGFzaNmwuq549fJKAwoAbmV0d29ya19pZLQMCw0KBgBzdGF0ZXMDDwByZWRzdG9uZV9zaWduYWwAAAAAAAMHAHZlcnNpb24BKBUBAA==" + }, + { + "id": "minecraft:birch_pressure_plate", + "block_state_b64": "CgAAAwgAYmxvY2tfaWSWAQAACAQAbmFtZR4AbWluZWNyYWZ0OmJpcmNoX3ByZXNzdXJlX3BsYXRlBAkAbmFtZV9oYXNorQkT9kDdlTwDCgBuZXR3b3JrX2lkH0G97AoGAHN0YXRlcwMPAHJlZHN0b25lX3NpZ25hbAAAAAAAAwcAdmVyc2lvbgEoFQEA" + }, + { + "id": "minecraft:jungle_pressure_plate", + "block_state_b64": "CgAAAwgAYmxvY2tfaWSYAQAACAQAbmFtZR8AbWluZWNyYWZ0Omp1bmdsZV9wcmVzc3VyZV9wbGF0ZQQJAG5hbWVfaGFzaJ7DcteCkb8/AwoAbmV0d29ya19pZLdPBSAKBgBzdGF0ZXMDDwByZWRzdG9uZV9zaWduYWwAAAAAAAMHAHZlcnNpb24BKBUBAA==" + }, + { + "id": "minecraft:acacia_pressure_plate", + "block_state_b64": "CgAAAwgAYmxvY2tfaWSVAQAACAQAbmFtZR8AbWluZWNyYWZ0OmFjYWNpYV9wcmVzc3VyZV9wbGF0ZQQJAG5hbWVfaGFzaC2frZtfoYqCAwoAbmV0d29ya19pZIDdI18KBgBzdGF0ZXMDDwByZWRzdG9uZV9zaWduYWwAAAAAAAMHAHZlcnNpb24BKBUBAA==" + }, + { + "id": "minecraft:dark_oak_pressure_plate", + "block_state_b64": "CgAAAwgAYmxvY2tfaWSXAQAACAQAbmFtZSEAbWluZWNyYWZ0OmRhcmtfb2FrX3ByZXNzdXJlX3BsYXRlBAkAbmFtZV9oYXNoHUCJsTy52pwDCgBuZXR3b3JrX2lkKpi8rAoGAHN0YXRlcwMPAHJlZHN0b25lX3NpZ25hbAAAAAAAAwcAdmVyc2lvbgEoFQEA" + }, + { + "id": "minecraft:mangrove_pressure_plate", + "block_state_b64": "CgAAAwgAYmxvY2tfaWTpAgAACAQAbmFtZSEAbWluZWNyYWZ0Om1hbmdyb3ZlX3ByZXNzdXJlX3BsYXRlBAkAbmFtZV9oYXNoiDsTfJaX100DCgBuZXR3b3JrX2lkuwWDyQoGAHN0YXRlcwMPAHJlZHN0b25lX3NpZ25hbAAAAAAAAwcAdmVyc2lvbgEoFQEA" + }, + { + "id": "minecraft:cherry_pressure_plate", + "block_state_b64": "CgAAAwgAYmxvY2tfaWQZAwAACAQAbmFtZR8AbWluZWNyYWZ0OmNoZXJyeV9wcmVzc3VyZV9wbGF0ZQQJAG5hbWVfaGFzaALMqYEZDUQHAwoAbmV0d29ya19pZPNT+r0KBgBzdGF0ZXMDDwByZWRzdG9uZV9zaWduYWwAAAAAAAMHAHZlcnNpb24BKBUBAA==" + }, + { + "id": "minecraft:bamboo_pressure_plate", + "block_state_b64": "CgAAAwgAYmxvY2tfaWQBAwAACAQAbmFtZR8AbWluZWNyYWZ0OmJhbWJvb19wcmVzc3VyZV9wbGF0ZQQJAG5hbWVfaGFzaNvxJ7NIAaqlAwoAbmV0d29ya19pZIZ8XnYKBgBzdGF0ZXMDDwByZWRzdG9uZV9zaWduYWwAAAAAAAMHAHZlcnNpb24BKBUBAA==" + }, + { + "id": "minecraft:crimson_pressure_plate", + "block_state_b64": "CgAAAwgAYmxvY2tfaWQFAgAACAQAbmFtZSAAbWluZWNyYWZ0OmNyaW1zb25fcHJlc3N1cmVfcGxhdGUECQBuYW1lX2hhc2hqBDVDAd31/gMKAG5ldHdvcmtfaWRmV18LCgYAc3RhdGVzAw8AcmVkc3RvbmVfc2lnbmFsAAAAAAADBwB2ZXJzaW9uASgVAQA=" + }, + { + "id": "minecraft:warped_pressure_plate", + "block_state_b64": "CgAAAwgAYmxvY2tfaWQGAgAACAQAbmFtZR8AbWluZWNyYWZ0OndhcnBlZF9wcmVzc3VyZV9wbGF0ZQQJAG5hbWVfaGFzaBxFoQksWtYUAwoAbmV0d29ya19pZJVRoIcKBgBzdGF0ZXMDDwByZWRzdG9uZV9zaWduYWwAAAAAAAMHAHZlcnNpb24BKBUBAA==" + }, + { + "id": "minecraft:stone_pressure_plate", + "block_state_b64": "CgAAAwgAYmxvY2tfaWRGAAAACAQAbmFtZR4AbWluZWNyYWZ0OnN0b25lX3ByZXNzdXJlX3BsYXRlBAkAbmFtZV9oYXNounJuTBUTrU8DCgBuZXR3b3JrX2lkjDydwQoGAHN0YXRlcwMPAHJlZHN0b25lX3NpZ25hbAAAAAAAAwcAdmVyc2lvbgEoFQEA" + }, + { + "id": "minecraft:light_weighted_pressure_plate", + "block_state_b64": "CgAAAwgAYmxvY2tfaWSTAAAACAQAbmFtZScAbWluZWNyYWZ0OmxpZ2h0X3dlaWdodGVkX3ByZXNzdXJlX3BsYXRlBAkAbmFtZV9oYXNoOyOJkNxLtkEDCgBuZXR3b3JrX2lkrr2AjgoGAHN0YXRlcwMPAHJlZHN0b25lX3NpZ25hbAAAAAAAAwcAdmVyc2lvbgEoFQEA" + }, + { + "id": "minecraft:heavy_weighted_pressure_plate", + "block_state_b64": "CgAAAwgAYmxvY2tfaWSUAAAACAQAbmFtZScAbWluZWNyYWZ0OmhlYXZ5X3dlaWdodGVkX3ByZXNzdXJlX3BsYXRlBAkAbmFtZV9oYXNoltgDmDvTajUDCgBuZXR3b3JrX2lkFxVKuQoGAHN0YXRlcwMPAHJlZHN0b25lX3NpZ25hbAAAAAAAAwcAdmVyc2lvbgEoFQEA" + }, + { + "id": "minecraft:polished_blackstone_pressure_plate", + "block_state_b64": "CgAAAwgAYmxvY2tfaWQmAgAACAQAbmFtZSwAbWluZWNyYWZ0OnBvbGlzaGVkX2JsYWNrc3RvbmVfcHJlc3N1cmVfcGxhdGUECQBuYW1lX2hhc2h65Ci6/CeGqwMKAG5ldHdvcmtfaWTaSW5xCgYAc3RhdGVzAw8AcmVkc3RvbmVfc2lnbmFsAAAAAAADBwB2ZXJzaW9uASgVAQA=" + }, + { + "id": "minecraft:observer", + "block_state_b64": "CgAAAwgAYmxvY2tfaWT7AAAACAQAbmFtZRIAbWluZWNyYWZ0Om9ic2VydmVyBAkAbmFtZV9oYXNoYhlh1lpmHTgDCgBuZXR3b3JrX2lkQEh55goGAHN0YXRlcwgaAG1pbmVjcmFmdDpmYWNpbmdfZGlyZWN0aW9uBABkb3duAQsAcG93ZXJlZF9iaXQAAAMHAHZlcnNpb24BKBUBAA==" + }, + { + "id": "minecraft:daylight_detector", + "block_state_b64": "CgAAAwgAYmxvY2tfaWSXAAAACAQAbmFtZRsAbWluZWNyYWZ0OmRheWxpZ2h0X2RldGVjdG9yBAkAbmFtZV9oYXNoV0F0s7B7PVgDCgBuZXR3b3JrX2lkri5afQoGAHN0YXRlcwMPAHJlZHN0b25lX3NpZ25hbAAAAAAAAwcAdmVyc2lvbgEoFQEA" + }, + { + "id": "minecraft:repeater" + }, + { + "id": "minecraft:comparator" + }, + { + "id": "minecraft:hopper" + }, + { + "id": "minecraft:dropper", + "block_state_b64": "CgAAAwgAYmxvY2tfaWR9AAAACAQAbmFtZREAbWluZWNyYWZ0OmRyb3BwZXIECQBuYW1lX2hhc2joXP7XqU0l3QMKAG5ldHdvcmtfaWQfQN6zCgYAc3RhdGVzAxAAZmFjaW5nX2RpcmVjdGlvbgMAAAABDQB0cmlnZ2VyZWRfYml0AAADBwB2ZXJzaW9uASgVAQA=" + }, + { + "id": "minecraft:dispenser", + "block_state_b64": "CgAAAwgAYmxvY2tfaWQXAAAACAQAbmFtZRMAbWluZWNyYWZ0OmRpc3BlbnNlcgQJAG5hbWVfaGFzaP1RR+zAbYP2AwoAbmV0d29ya19pZGAayD0KBgBzdGF0ZXMDEABmYWNpbmdfZGlyZWN0aW9uAwAAAAENAHRyaWdnZXJlZF9iaXQAAAMHAHZlcnNpb24BKBUBAA==" + }, + { + "id": "minecraft:crafter", + "block_state_b64": "CgAAAwgAYmxvY2tfaWQ4AgAACAQAbmFtZREAbWluZWNyYWZ0OmNyYWZ0ZXIECQBuYW1lX2hhc2iLCT/rJmRN8QMKAG5ldHdvcmtfaWTPTbvrCgYAc3RhdGVzAQgAY3JhZnRpbmcACAsAb3JpZW50YXRpb24JAGRvd25fZWFzdAENAHRyaWdnZXJlZF9iaXQAAAMHAHZlcnNpb24BKBUBAA==" + }, + { + "id": "minecraft:piston", + "block_state_b64": "CgAAAwgAYmxvY2tfaWQhAAAACAQAbmFtZRAAbWluZWNyYWZ0OnBpc3RvbgQJAG5hbWVfaGFzaDs3AFh1fL0uAwoAbmV0d29ya19pZLD/5XQKBgBzdGF0ZXMDEABmYWNpbmdfZGlyZWN0aW9uAQAAAAADBwB2ZXJzaW9uASgVAQA=" + }, + { + "id": "minecraft:sticky_piston", + "block_state_b64": "CgAAAwgAYmxvY2tfaWQdAAAACAQAbmFtZRcAbWluZWNyYWZ0OnN0aWNreV9waXN0b24ECQBuYW1lX2hhc2hPFJFJSiJ0ZQMKAG5ldHdvcmtfaWT/MzCJCgYAc3RhdGVzAxAAZmFjaW5nX2RpcmVjdGlvbgEAAAAAAwcAdmVyc2lvbgEoFQEA" + }, + { + "id": "minecraft:tnt", + "block_state_b64": "CgAAAwgAYmxvY2tfaWQuAAAACAQAbmFtZQ0AbWluZWNyYWZ0OnRudAQJAG5hbWVfaGFzaEYOHwCvJH29AwoAbmV0d29ya19pZAXzHyUKBgBzdGF0ZXMBCwBleHBsb2RlX2JpdAAAAwcAdmVyc2lvbgEoFQEA" + }, + { + "id": "minecraft:name_tag" + }, + { + "id": "minecraft:loom", + "block_state_b64": "CgAAAwgAYmxvY2tfaWTLAQAACAQAbmFtZQ4AbWluZWNyYWZ0Omxvb20ECQBuYW1lX2hhc2i7DKjAXNq8TAMKAG5ldHdvcmtfaWR/49HXCgYAc3RhdGVzAwkAZGlyZWN0aW9uAAAAAAADBwB2ZXJzaW9uASgVAQA=" + }, + { + "id": "minecraft:banner", + "nbt_b64": "CgAAAwQAVHlwZQAAAAAA" + }, + { + "id": "minecraft:banner", + "damage": 8, + "nbt_b64": "CgAAAwQAVHlwZQAAAAAA" + }, + { + "id": "minecraft:banner", + "damage": 7, + "nbt_b64": "CgAAAwQAVHlwZQAAAAAA" + }, + { + "id": "minecraft:banner", + "damage": 15, + "nbt_b64": "CgAAAwQAVHlwZQAAAAAA" + }, + { + "id": "minecraft:banner", + "damage": 12, + "nbt_b64": "CgAAAwQAVHlwZQAAAAAA" + }, + { + "id": "minecraft:banner", + "damage": 14, + "nbt_b64": "CgAAAwQAVHlwZQAAAAAA" + }, + { + "id": "minecraft:banner", + "damage": 1, + "nbt_b64": "CgAAAwQAVHlwZQAAAAAA" + }, + { + "id": "minecraft:banner", + "damage": 4, + "nbt_b64": "CgAAAwQAVHlwZQAAAAAA" + }, + { + "id": "minecraft:banner", + "damage": 5, + "nbt_b64": "CgAAAwQAVHlwZQAAAAAA" + }, + { + "id": "minecraft:banner", + "damage": 13, + "nbt_b64": "CgAAAwQAVHlwZQAAAAAA" + }, + { + "id": "minecraft:banner", + "damage": 9, + "nbt_b64": "CgAAAwQAVHlwZQAAAAAA" + }, + { + "id": "minecraft:banner", + "damage": 3, + "nbt_b64": "CgAAAwQAVHlwZQAAAAAA" + }, + { + "id": "minecraft:banner", + "damage": 11, + "nbt_b64": "CgAAAwQAVHlwZQAAAAAA" + }, + { + "id": "minecraft:banner", + "damage": 10, + "nbt_b64": "CgAAAwQAVHlwZQAAAAAA" + }, + { + "id": "minecraft:banner", + "damage": 2, + "nbt_b64": "CgAAAwQAVHlwZQAAAAAA" + }, + { + "id": "minecraft:banner", + "damage": 6, + "nbt_b64": "CgAAAwQAVHlwZQAAAAAA" + }, + { + "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:flow_banner_pattern" + }, + { + "id": "minecraft:guster_banner_pattern" + }, + { + "id": "minecraft:angler_pottery_sherd" + }, + { + "id": "minecraft:archer_pottery_sherd" + }, + { + "id": "minecraft:arms_up_pottery_sherd" + }, + { + "id": "minecraft:blade_pottery_sherd" + }, + { + "id": "minecraft:brewer_pottery_sherd" + }, + { + "id": "minecraft:burn_pottery_sherd" + }, + { + "id": "minecraft:danger_pottery_sherd" + }, + { + "id": "minecraft:explorer_pottery_sherd" + }, + { + "id": "minecraft:flow_pottery_sherd" + }, + { + "id": "minecraft:friend_pottery_sherd" + }, + { + "id": "minecraft:guster_pottery_sherd" + }, + { + "id": "minecraft:heart_pottery_sherd" + }, + { + "id": "minecraft:heartbreak_pottery_sherd" + }, + { + "id": "minecraft:howl_pottery_sherd" + }, + { + "id": "minecraft:miner_pottery_sherd" + }, + { + "id": "minecraft:mourner_pottery_sherd" + }, + { + "id": "minecraft:plenty_pottery_sherd" + }, + { + "id": "minecraft:prize_pottery_sherd" + }, + { + "id": "minecraft:scrape_pottery_sherd" + }, + { + "id": "minecraft:sheaf_pottery_sherd" + }, + { + "id": "minecraft:shelter_pottery_sherd" + }, + { + "id": "minecraft:skull_pottery_sherd" + }, + { + "id": "minecraft:snort_pottery_sherd" + }, + { + "id": "minecraft:netherite_upgrade_smithing_template" + }, + { + "id": "minecraft:sentry_armor_trim_smithing_template" + }, + { + "id": "minecraft:vex_armor_trim_smithing_template" + }, + { + "id": "minecraft:wild_armor_trim_smithing_template" + }, + { + "id": "minecraft:coast_armor_trim_smithing_template" + }, + { + "id": "minecraft:dune_armor_trim_smithing_template" + }, + { + "id": "minecraft:wayfinder_armor_trim_smithing_template" + }, + { + "id": "minecraft:shaper_armor_trim_smithing_template" + }, + { + "id": "minecraft:raiser_armor_trim_smithing_template" + }, + { + "id": "minecraft:host_armor_trim_smithing_template" + }, + { + "id": "minecraft:ward_armor_trim_smithing_template" + }, + { + "id": "minecraft:silence_armor_trim_smithing_template" + }, + { + "id": "minecraft:tide_armor_trim_smithing_template" + }, + { + "id": "minecraft:snout_armor_trim_smithing_template" + }, + { + "id": "minecraft:rib_armor_trim_smithing_template" + }, + { + "id": "minecraft:eye_armor_trim_smithing_template" + }, + { + "id": "minecraft:spire_armor_trim_smithing_template" + }, + { + "id": "minecraft:flow_armor_trim_smithing_template" + }, + { + "id": "minecraft:bolt_armor_trim_smithing_template" + }, + { + "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", + "block_state_b64": "CgAAAwgAYmxvY2tfaWTuAQAACAQAbmFtZRAAbWluZWNyYWZ0OnRhcmdldAQJAG5hbWVfaGFzaJc66SVbYlaxAwoAbmV0d29ya19pZPBozs0KBgBzdGF0ZXMAAwcAdmVyc2lvbgEoFQEA" + }, + { + "id": "minecraft:decorated_pot", + "block_state_b64": "CgAAAwgAYmxvY2tfaWQmAwAACAQAbmFtZRcAbWluZWNyYWZ0OmRlY29yYXRlZF9wb3QECQBuYW1lX2hhc2jjQgckn8VTvwMKAG5ldHdvcmtfaWRwvkUUCgYAc3RhdGVzAwkAZGlyZWN0aW9uAAAAAAADBwB2ZXJzaW9uASgVAQA=" + }, + { + "id": "minecraft:trial_key" + }, + { + "id": "minecraft:ominous_trial_key" + }, + { + "id": "minecraft:lodestone_compass" + }, + { + "id": "minecraft:wither_spawn_egg" + }, + { + "id": "minecraft:ender_dragon_spawn_egg" + } + ] +} \ No newline at end of file diff --git a/core/src/main/resources/bedrock/item_tags.1_20_80.json b/core/src/main/resources/bedrock/item_tags.1_20_80.json new file mode 100644 index 000000000..4203c96c1 --- /dev/null +++ b/core/src/main/resources/bedrock/item_tags.1_20_80.json @@ -0,0 +1,784 @@ +{ + "minecraft:arrow": [ + "minecraft:arrow" + ], + "minecraft:banner": [ + "minecraft:banner" + ], + "minecraft:boat": [ + "minecraft:oak_boat", + "minecraft:birch_boat", + "minecraft:jungle_boat", + "minecraft:spruce_boat", + "minecraft:mangrove_boat", + "minecraft:acacia_boat", + "minecraft:oak_chest_boat", + "minecraft:dark_oak_boat", + "minecraft:birch_chest_boat", + "minecraft:jungle_chest_boat", + "minecraft:spruce_chest_boat", + "minecraft:acacia_chest_boat", + "minecraft:dark_oak_chest_boat", + "minecraft:mangrove_chest_boat", + "minecraft:cherry_boat", + "minecraft:cherry_chest_boat", + "minecraft:bamboo_raft", + "minecraft:bamboo_chest_raft" + ], + "minecraft:boats": [ + "minecraft:oak_boat", + "minecraft:birch_boat", + "minecraft:jungle_boat", + "minecraft:spruce_boat", + "minecraft:mangrove_boat", + "minecraft:acacia_boat", + "minecraft:oak_chest_boat", + "minecraft:dark_oak_boat", + "minecraft:birch_chest_boat", + "minecraft:jungle_chest_boat", + "minecraft:spruce_chest_boat", + "minecraft:acacia_chest_boat", + "minecraft:dark_oak_chest_boat", + "minecraft:mangrove_chest_boat", + "minecraft:cherry_boat", + "minecraft:cherry_chest_boat", + "minecraft:bamboo_raft", + "minecraft:bamboo_chest_raft" + ], + "minecraft:bookshelf_books": [ + "minecraft:book", + "minecraft:writable_book", + "minecraft:enchanted_book", + "minecraft:written_book" + ], + "minecraft:chainmail_tier": [ + "minecraft:chainmail_helmet", + "minecraft:chainmail_leggings", + "minecraft:chainmail_chestplate", + "minecraft:chainmail_boots" + ], + "minecraft:chest_boat": [], + "minecraft:coals": [ + "minecraft:coal", + "minecraft:charcoal" + ], + "minecraft:crimson_stems": [ + "minecraft:crimson_hyphae", + "minecraft:stripped_crimson_hyphae", + "minecraft:crimson_stem", + "minecraft:stripped_crimson_stem" + ], + "minecraft:decorated_pot_sherds": [ + "minecraft:blade_pottery_sherd", + "minecraft:brick", + "minecraft:angler_pottery_sherd", + "minecraft:burn_pottery_sherd", + "minecraft:archer_pottery_sherd", + "minecraft:arms_up_pottery_sherd", + "minecraft:heartbreak_pottery_sherd", + "minecraft:brewer_pottery_sherd", + "minecraft:danger_pottery_sherd", + "minecraft:explorer_pottery_sherd", + "minecraft:friend_pottery_sherd", + "minecraft:heart_pottery_sherd", + "minecraft:howl_pottery_sherd", + "minecraft:miner_pottery_sherd", + "minecraft:mourner_pottery_sherd", + "minecraft:plenty_pottery_sherd", + "minecraft:skull_pottery_sherd", + "minecraft:prize_pottery_sherd", + "minecraft:sheaf_pottery_sherd", + "minecraft:shelter_pottery_sherd", + "minecraft:snort_pottery_sherd" + ], + "minecraft:diamond_tier": [ + "minecraft:diamond_axe", + "minecraft:diamond_sword", + "minecraft:diamond_pickaxe", + "minecraft:diamond_shovel", + "minecraft:diamond_hoe", + "minecraft:diamond_helmet", + "minecraft:diamond_chestplate", + "minecraft:diamond_leggings", + "minecraft:diamond_boots" + ], + "minecraft:digger": [ + "minecraft:stone_pickaxe", + "minecraft:iron_pickaxe", + "minecraft:iron_shovel", + "minecraft:iron_axe", + "minecraft:wooden_shovel", + "minecraft:wooden_pickaxe", + "minecraft:stone_shovel", + "minecraft:wooden_axe", + "minecraft:stone_axe", + "minecraft:diamond_shovel", + "minecraft:diamond_pickaxe", + "minecraft:diamond_axe", + "minecraft:golden_shovel", + "minecraft:golden_pickaxe", + "minecraft:golden_axe", + "minecraft:wooden_hoe", + "minecraft:stone_hoe", + "minecraft:iron_hoe", + "minecraft:diamond_hoe", + "minecraft:golden_hoe", + "minecraft:netherite_shovel", + "minecraft:netherite_pickaxe", + "minecraft:netherite_axe", + "minecraft:netherite_hoe" + ], + "minecraft:door": [ + "minecraft:wooden_door", + "minecraft:iron_door", + "minecraft:spruce_door", + "minecraft:birch_door", + "minecraft:crimson_door", + "minecraft:jungle_door", + "minecraft:dark_oak_door", + "minecraft:acacia_door", + "minecraft:mangrove_door", + "minecraft:warped_door", + "minecraft:cherry_door", + "minecraft:bamboo_door" + ], + "minecraft:golden_tier": [ + "minecraft:golden_sword", + "minecraft:golden_hoe", + "minecraft:golden_shovel", + "minecraft:golden_chestplate", + "minecraft:golden_pickaxe", + "minecraft:golden_axe", + "minecraft:golden_leggings", + "minecraft:golden_helmet", + "minecraft:golden_boots" + ], + "minecraft:hanging_actor": [ + "minecraft:painting" + ], + "minecraft:hanging_sign": [ + "minecraft:cherry_hanging_sign", + "minecraft:oak_hanging_sign", + "minecraft:crimson_hanging_sign", + "minecraft:acacia_hanging_sign", + "minecraft:spruce_hanging_sign", + "minecraft:birch_hanging_sign", + "minecraft:jungle_hanging_sign", + "minecraft:dark_oak_hanging_sign", + "minecraft:warped_hanging_sign", + "minecraft:mangrove_hanging_sign", + "minecraft:bamboo_hanging_sign" + ], + "minecraft:horse_armor": [ + "minecraft:leather_horse_armor", + "minecraft:iron_horse_armor", + "minecraft:golden_horse_armor", + "minecraft:diamond_horse_armor" + ], + "minecraft:iron_tier": [ + "minecraft:iron_pickaxe", + "minecraft:iron_shovel", + "minecraft:iron_sword", + "minecraft:iron_axe", + "minecraft:iron_hoe", + "minecraft:iron_chestplate", + "minecraft:iron_helmet", + "minecraft:iron_leggings", + "minecraft:iron_boots" + ], + "minecraft:is_armor": [ + "minecraft:leather_boots", + "minecraft:leather_leggings", + "minecraft:leather_helmet", + "minecraft:leather_chestplate", + "minecraft:chainmail_leggings", + "minecraft:elytra", + "minecraft:chainmail_helmet", + "minecraft:chainmail_chestplate", + "minecraft:chainmail_boots", + "minecraft:iron_helmet", + "minecraft:iron_chestplate", + "minecraft:iron_leggings", + "minecraft:iron_boots", + "minecraft:diamond_helmet", + "minecraft:diamond_chestplate", + "minecraft:diamond_leggings", + "minecraft:diamond_boots", + "minecraft:golden_helmet", + "minecraft:golden_chestplate", + "minecraft:golden_leggings", + "minecraft:golden_boots", + "minecraft:turtle_helmet", + "minecraft:netherite_helmet", + "minecraft:netherite_chestplate", + "minecraft:netherite_leggings", + "minecraft:netherite_boots" + ], + "minecraft:is_axe": [ + "minecraft:iron_axe", + "minecraft:wooden_axe", + "minecraft:diamond_axe", + "minecraft:netherite_axe", + "minecraft:stone_axe", + "minecraft:golden_axe" + ], + "minecraft:is_cooked": [ + "minecraft:cooked_porkchop", + "minecraft:cooked_cod", + "minecraft:rabbit_stew", + "minecraft:cooked_salmon", + "minecraft:cooked_beef", + "minecraft:cooked_chicken", + "minecraft:cooked_rabbit", + "minecraft:cooked_mutton" + ], + "minecraft:is_fish": [ + "minecraft:cod", + "minecraft:salmon", + "minecraft:tropical_fish", + "minecraft:pufferfish", + "minecraft:cooked_salmon", + "minecraft:cooked_cod" + ], + "minecraft:is_food": [ + "minecraft:porkchop", + "minecraft:cooked_mutton", + "minecraft:apple", + "minecraft:golden_apple", + "minecraft:dried_kelp", + "minecraft:cooked_porkchop", + "minecraft:mushroom_stew", + "minecraft:enchanted_golden_apple", + "minecraft:bread", + "minecraft:cookie", + "minecraft:melon_slice", + "minecraft:beef", + "minecraft:cooked_beef", + "minecraft:chicken", + "minecraft:cooked_chicken", + "minecraft:rotten_flesh", + "minecraft:carrot", + "minecraft:potato", + "minecraft:baked_potato", + "minecraft:golden_carrot", + "minecraft:pumpkin_pie", + "minecraft:beetroot", + "minecraft:beetroot_soup", + "minecraft:sweet_berries", + "minecraft:rabbit", + "minecraft:cooked_rabbit", + "minecraft:rabbit_stew", + "minecraft:mutton" + ], + "minecraft:is_hoe": [ + "minecraft:wooden_hoe", + "minecraft:stone_hoe", + "minecraft:iron_hoe", + "minecraft:netherite_hoe", + "minecraft:diamond_hoe", + "minecraft:golden_hoe" + ], + "minecraft:is_meat": [ + "minecraft:rotten_flesh", + "minecraft:porkchop", + "minecraft:cooked_mutton", + "minecraft:cooked_porkchop", + "minecraft:cooked_chicken", + "minecraft:beef", + "minecraft:rabbit", + "minecraft:cooked_beef", + "minecraft:chicken", + "minecraft:cooked_rabbit", + "minecraft:rabbit_stew", + "minecraft:mutton" + ], + "minecraft:is_minecart": [ + "minecraft:minecart", + "minecraft:tnt_minecart", + "minecraft:chest_minecart", + "minecraft:hopper_minecart", + "minecraft:command_block_minecart" + ], + "minecraft:is_pickaxe": [ + "minecraft:iron_pickaxe", + "minecraft:stone_pickaxe", + "minecraft:netherite_pickaxe", + "minecraft:wooden_pickaxe", + "minecraft:diamond_pickaxe", + "minecraft:golden_pickaxe" + ], + "minecraft:is_shovel": [ + "minecraft:iron_shovel", + "minecraft:golden_shovel", + "minecraft:wooden_shovel", + "minecraft:stone_shovel", + "minecraft:diamond_shovel", + "minecraft:netherite_shovel" + ], + "minecraft:is_sword": [ + "minecraft:iron_sword", + "minecraft:wooden_sword", + "minecraft:stone_sword", + "minecraft:golden_sword", + "minecraft:diamond_sword", + "minecraft:netherite_sword" + ], + "minecraft:is_tool": [ + "minecraft:iron_pickaxe", + "minecraft:iron_shovel", + "minecraft:iron_sword", + "minecraft:stone_hoe", + "minecraft:iron_axe", + "minecraft:wooden_sword", + "minecraft:diamond_hoe", + "minecraft:wooden_shovel", + "minecraft:golden_sword", + "minecraft:wooden_pickaxe", + "minecraft:wooden_axe", + "minecraft:stone_sword", + "minecraft:stone_shovel", + "minecraft:stone_pickaxe", + "minecraft:stone_axe", + "minecraft:diamond_sword", + "minecraft:diamond_axe", + "minecraft:diamond_shovel", + "minecraft:diamond_pickaxe", + "minecraft:golden_shovel", + "minecraft:golden_pickaxe", + "minecraft:golden_axe", + "minecraft:wooden_hoe", + "minecraft:netherite_sword", + "minecraft:iron_hoe", + "minecraft:golden_hoe", + "minecraft:netherite_shovel", + "minecraft:netherite_pickaxe", + "minecraft:netherite_axe", + "minecraft:netherite_hoe" + ], + "minecraft:is_trident": [ + "minecraft:trident" + ], + "minecraft:leather_tier": [ + "minecraft:leather_helmet", + "minecraft:leather_leggings", + "minecraft:leather_boots", + "minecraft:leather_chestplate" + ], + "minecraft:lectern_books": [ + "minecraft:writable_book", + "minecraft:written_book" + ], + "minecraft:logs": [ + "minecraft:oak_log", + "minecraft:stripped_oak_wood", + "minecraft:spruce_log", + "minecraft:crimson_stem", + "minecraft:birch_log", + "minecraft:jungle_log", + "minecraft:dark_oak_wood", + "minecraft:oak_wood", + "minecraft:dark_oak_log", + "minecraft:acacia_log", + "minecraft:spruce_wood", + "minecraft:stripped_spruce_wood", + "minecraft:birch_wood", + "minecraft:stripped_birch_wood", + "minecraft:warped_hyphae", + "minecraft:jungle_wood", + "minecraft:stripped_jungle_wood", + "minecraft:stripped_warped_stem", + "minecraft:acacia_wood", + "minecraft:stripped_acacia_wood", + "minecraft:stripped_dark_oak_wood", + "minecraft:mangrove_log", + "minecraft:stripped_spruce_log", + "minecraft:stripped_dark_oak_log", + "minecraft:stripped_jungle_log", + "minecraft:stripped_crimson_stem", + "minecraft:mangrove_wood", + "minecraft:stripped_mangrove_wood", + "minecraft:crimson_hyphae", + "minecraft:stripped_cherry_log", + "minecraft:stripped_cherry_wood", + "minecraft:stripped_acacia_log", + "minecraft:stripped_warped_hyphae", + "minecraft:stripped_birch_log", + "minecraft:warped_stem", + "minecraft:stripped_crimson_hyphae", + "minecraft:cherry_wood", + "minecraft:stripped_oak_log", + "minecraft:cherry_log", + "minecraft:stripped_mangrove_log" + ], + "minecraft:logs_that_burn": [ + "minecraft:oak_log", + "minecraft:stripped_oak_wood", + "minecraft:spruce_log", + "minecraft:birch_log", + "minecraft:jungle_log", + "minecraft:dark_oak_wood", + "minecraft:oak_wood", + "minecraft:dark_oak_log", + "minecraft:acacia_log", + "minecraft:spruce_wood", + "minecraft:stripped_spruce_wood", + "minecraft:birch_wood", + "minecraft:stripped_birch_wood", + "minecraft:jungle_wood", + "minecraft:stripped_jungle_wood", + "minecraft:acacia_wood", + "minecraft:stripped_acacia_wood", + "minecraft:stripped_dark_oak_wood", + "minecraft:mangrove_log", + "minecraft:stripped_spruce_log", + "minecraft:stripped_dark_oak_log", + "minecraft:stripped_jungle_log", + "minecraft:mangrove_wood", + "minecraft:stripped_mangrove_wood", + "minecraft:stripped_cherry_log", + "minecraft:stripped_cherry_wood", + "minecraft:stripped_acacia_log", + "minecraft:stripped_birch_log", + "minecraft:cherry_wood", + "minecraft:stripped_oak_log", + "minecraft:cherry_log", + "minecraft:stripped_mangrove_log" + ], + "minecraft:mangrove_logs": [ + "minecraft:mangrove_log", + "minecraft:mangrove_wood", + "minecraft:stripped_mangrove_wood", + "minecraft:stripped_mangrove_log" + ], + "minecraft:music_disc": [ + "minecraft:music_disc_stal", + "minecraft:music_disc_13", + "minecraft:music_disc_cat", + "minecraft:music_disc_11", + "minecraft:music_disc_blocks", + "minecraft:music_disc_mellohi", + "minecraft:music_disc_chirp", + "minecraft:music_disc_ward", + "minecraft:music_disc_far", + "minecraft:music_disc_mall", + "minecraft:music_disc_strad", + "minecraft:music_disc_wait", + "minecraft:music_disc_otherside", + "minecraft:music_disc_pigstep", + "minecraft:music_disc_5", + "minecraft:music_disc_relic" + ], + "minecraft:netherite_tier": [ + "minecraft:netherite_chestplate", + "minecraft:netherite_axe", + "minecraft:netherite_sword", + "minecraft:netherite_hoe", + "minecraft:netherite_shovel", + "minecraft:netherite_pickaxe", + "minecraft:netherite_helmet", + "minecraft:netherite_leggings", + "minecraft:netherite_boots" + ], + "minecraft:piglin_loved": [], + "minecraft:piglin_repellents": [], + "minecraft:planks": [ + "minecraft:dark_oak_planks", + "minecraft:oak_planks", + "minecraft:warped_planks", + "minecraft:spruce_planks", + "minecraft:birch_planks", + "minecraft:jungle_planks", + "minecraft:mangrove_planks", + "minecraft:acacia_planks", + "minecraft:crimson_planks", + "minecraft:bamboo_planks", + "minecraft:cherry_planks" + ], + "minecraft:sand": [ + "minecraft:sand" + ], + "minecraft:sign": [ + "minecraft:dark_oak_sign", + "minecraft:oak_sign", + "minecraft:spruce_sign", + "minecraft:crimson_sign", + "minecraft:birch_sign", + "minecraft:jungle_sign", + "minecraft:warped_sign", + "minecraft:bamboo_sign", + "minecraft:acacia_sign", + "minecraft:mangrove_sign", + "minecraft:crimson_hanging_sign", + "minecraft:cherry_sign", + "minecraft:acacia_hanging_sign", + "minecraft:cherry_hanging_sign", + "minecraft:oak_hanging_sign", + "minecraft:spruce_hanging_sign", + "minecraft:birch_hanging_sign", + "minecraft:jungle_hanging_sign", + "minecraft:dark_oak_hanging_sign", + "minecraft:warped_hanging_sign", + "minecraft:mangrove_hanging_sign", + "minecraft:bamboo_hanging_sign" + ], + "minecraft:soul_fire_base_blocks": [ + "minecraft:soul_soil", + "minecraft:soul_sand" + ], + "minecraft:spawn_egg": [ + "minecraft:zombie_villager_spawn_egg", + "minecraft:ender_dragon_spawn_egg", + "minecraft:zombie_horse_spawn_egg", + "minecraft:creeper_spawn_egg", + "minecraft:chicken_spawn_egg", + "minecraft:skeleton_spawn_egg", + "minecraft:cow_spawn_egg", + "minecraft:pig_spawn_egg", + "minecraft:dolphin_spawn_egg", + "minecraft:sheep_spawn_egg", + "minecraft:slime_spawn_egg", + "minecraft:wolf_spawn_egg", + "minecraft:phantom_spawn_egg", + "minecraft:mooshroom_spawn_egg", + "minecraft:agent_spawn_egg", + "minecraft:enderman_spawn_egg", + "minecraft:silverfish_spawn_egg", + "minecraft:zoglin_spawn_egg", + "minecraft:fox_spawn_egg", + "minecraft:spider_spawn_egg", + "minecraft:zombie_spawn_egg", + "minecraft:bat_spawn_egg", + "minecraft:zombie_pigman_spawn_egg", + "minecraft:villager_spawn_egg", + "minecraft:vex_spawn_egg", + "minecraft:llama_spawn_egg", + "minecraft:squid_spawn_egg", + "minecraft:cat_spawn_egg", + "minecraft:ocelot_spawn_egg", + "minecraft:witch_spawn_egg", + "minecraft:ghast_spawn_egg", + "minecraft:allay_spawn_egg", + "minecraft:strider_spawn_egg", + "minecraft:magma_cube_spawn_egg", + "minecraft:blaze_spawn_egg", + "minecraft:cave_spider_spawn_egg", + "minecraft:guardian_spawn_egg", + "minecraft:horse_spawn_egg", + "minecraft:husk_spawn_egg", + "minecraft:rabbit_spawn_egg", + "minecraft:endermite_spawn_egg", + "minecraft:mule_spawn_egg", + "minecraft:stray_spawn_egg", + "minecraft:wither_skeleton_spawn_egg", + "minecraft:donkey_spawn_egg", + "minecraft:skeleton_horse_spawn_egg", + "minecraft:shulker_spawn_egg", + "minecraft:npc_spawn_egg", + "minecraft:elder_guardian_spawn_egg", + "minecraft:polar_bear_spawn_egg", + "minecraft:cod_spawn_egg", + "minecraft:vindicator_spawn_egg", + "minecraft:evoker_spawn_egg", + "minecraft:parrot_spawn_egg", + "minecraft:tropical_fish_spawn_egg", + "minecraft:pufferfish_spawn_egg", + "minecraft:salmon_spawn_egg", + "minecraft:drowned_spawn_egg", + "minecraft:turtle_spawn_egg", + "minecraft:piglin_spawn_egg", + "minecraft:panda_spawn_egg", + "minecraft:pillager_spawn_egg", + "minecraft:wandering_trader_spawn_egg", + "minecraft:ravager_spawn_egg", + "minecraft:bee_spawn_egg", + "minecraft:hoglin_spawn_egg", + "minecraft:piglin_brute_spawn_egg", + "minecraft:sniffer_spawn_egg", + "minecraft:axolotl_spawn_egg", + "minecraft:goat_spawn_egg", + "minecraft:glow_squid_spawn_egg", + "minecraft:iron_golem_spawn_egg", + "minecraft:snow_golem_spawn_egg", + "minecraft:wither_spawn_egg", + "minecraft:frog_spawn_egg", + "minecraft:tadpole_spawn_egg", + "minecraft:warden_spawn_egg", + "minecraft:trader_llama_spawn_egg", + "minecraft:camel_spawn_egg", + "minecraft:armadillo_spawn_egg", + "minecraft:spawn_egg" + ], + "minecraft:stone_bricks": [ + "minecraft:stonebrick" + ], + "minecraft:stone_crafting_materials": [ + "minecraft:cobblestone", + "minecraft:cobbled_deepslate", + "minecraft:blackstone" + ], + "minecraft:stone_tier": [ + "minecraft:stone_sword", + "minecraft:stone_shovel", + "minecraft:stone_pickaxe", + "minecraft:stone_axe", + "minecraft:stone_hoe" + ], + "minecraft:stone_tool_materials": [ + "minecraft:cobblestone", + "minecraft:cobbled_deepslate", + "minecraft:blackstone" + ], + "minecraft:transform_materials": [ + "minecraft:netherite_ingot" + ], + "minecraft:transform_templates": [ + "minecraft:netherite_upgrade_smithing_template" + ], + "minecraft:transformable_items": [ + "minecraft:diamond_axe", + "minecraft:diamond_sword", + "minecraft:diamond_pickaxe", + "minecraft:diamond_shovel", + "minecraft:diamond_hoe", + "minecraft:diamond_helmet", + "minecraft:diamond_chestplate", + "minecraft:diamond_leggings", + "minecraft:diamond_boots", + "minecraft:golden_boots" + ], + "minecraft:trim_materials": [ + "minecraft:diamond", + "minecraft:emerald", + "minecraft:iron_ingot", + "minecraft:quartz", + "minecraft:lapis_lazuli", + "minecraft:gold_ingot", + "minecraft:redstone", + "minecraft:copper_ingot", + "minecraft:netherite_ingot", + "minecraft:amethyst_shard" + ], + "minecraft:trim_templates": [ + "minecraft:tide_armor_trim_smithing_template", + "minecraft:sentry_armor_trim_smithing_template", + "minecraft:coast_armor_trim_smithing_template", + "minecraft:dune_armor_trim_smithing_template", + "minecraft:wild_armor_trim_smithing_template", + "minecraft:ward_armor_trim_smithing_template", + "minecraft:eye_armor_trim_smithing_template", + "minecraft:vex_armor_trim_smithing_template", + "minecraft:snout_armor_trim_smithing_template", + "minecraft:rib_armor_trim_smithing_template", + "minecraft:spire_armor_trim_smithing_template", + "minecraft:silence_armor_trim_smithing_template", + "minecraft:wayfinder_armor_trim_smithing_template", + "minecraft:raiser_armor_trim_smithing_template", + "minecraft:shaper_armor_trim_smithing_template", + "minecraft:host_armor_trim_smithing_template" + ], + "minecraft:trimmable_armors": [ + "minecraft:leather_boots", + "minecraft:leather_leggings", + "minecraft:leather_helmet", + "minecraft:leather_chestplate", + "minecraft:chainmail_leggings", + "minecraft:chainmail_helmet", + "minecraft:chainmail_chestplate", + "minecraft:chainmail_boots", + "minecraft:iron_helmet", + "minecraft:iron_chestplate", + "minecraft:iron_leggings", + "minecraft:iron_boots", + "minecraft:diamond_helmet", + "minecraft:diamond_chestplate", + "minecraft:diamond_leggings", + "minecraft:diamond_boots", + "minecraft:golden_helmet", + "minecraft:golden_chestplate", + "minecraft:golden_leggings", + "minecraft:golden_boots", + "minecraft:turtle_helmet", + "minecraft:netherite_helmet", + "minecraft:netherite_chestplate", + "minecraft:netherite_leggings", + "minecraft:netherite_boots" + ], + "minecraft:vibration_damper": [ + "minecraft:pink_wool", + "minecraft:lime_wool", + "minecraft:white_wool", + "minecraft:magenta_wool", + "minecraft:red_wool", + "minecraft:orange_wool", + "minecraft:yellow_carpet", + "minecraft:light_blue_wool", + "minecraft:yellow_wool", + "minecraft:gray_wool", + "minecraft:light_gray_wool", + "minecraft:cyan_wool", + "minecraft:purple_wool", + "minecraft:purple_carpet", + "minecraft:blue_wool", + "minecraft:brown_wool", + "minecraft:green_wool", + "minecraft:black_wool", + "minecraft:white_carpet", + "minecraft:orange_carpet", + "minecraft:magenta_carpet", + "minecraft:light_blue_carpet", + "minecraft:lime_carpet", + "minecraft:pink_carpet", + "minecraft:gray_carpet", + "minecraft:light_gray_carpet", + "minecraft:cyan_carpet", + "minecraft:blue_carpet", + "minecraft:brown_carpet", + "minecraft:green_carpet", + "minecraft:red_carpet", + "minecraft:black_carpet" + ], + "minecraft:warped_stems": [ + "minecraft:stripped_warped_hyphae", + "minecraft:warped_hyphae", + "minecraft:stripped_warped_stem", + "minecraft:warped_stem" + ], + "minecraft:wooden_slabs": [ + "minecraft:mangrove_slab", + "minecraft:birch_slab", + "minecraft:cherry_slab", + "minecraft:jungle_slab", + "minecraft:spruce_slab", + "minecraft:bamboo_slab", + "minecraft:oak_slab", + "minecraft:acacia_slab", + "minecraft:dark_oak_slab", + "minecraft:crimson_slab", + "minecraft:warped_slab" + ], + "minecraft:wooden_tier": [ + "minecraft:wooden_sword", + "minecraft:wooden_shovel", + "minecraft:wooden_pickaxe", + "minecraft:wooden_axe", + "minecraft:wooden_hoe" + ], + "minecraft:wool": [ + "minecraft:pink_wool", + "minecraft:lime_wool", + "minecraft:white_wool", + "minecraft:magenta_wool", + "minecraft:red_wool", + "minecraft:orange_wool", + "minecraft:light_blue_wool", + "minecraft:yellow_wool", + "minecraft:gray_wool", + "minecraft:light_gray_wool", + "minecraft:cyan_wool", + "minecraft:purple_wool", + "minecraft:blue_wool", + "minecraft:brown_wool", + "minecraft:green_wool", + "minecraft:black_wool" + ] +} \ No newline at end of file diff --git a/core/src/main/resources/bedrock/item_tags.1_21_0.json b/core/src/main/resources/bedrock/item_tags.1_21_0.json new file mode 100644 index 000000000..5fac99114 --- /dev/null +++ b/core/src/main/resources/bedrock/item_tags.1_21_0.json @@ -0,0 +1,802 @@ +{ + "minecraft:arrow": [ + "minecraft:arrow" + ], + "minecraft:banner": [ + "minecraft:banner" + ], + "minecraft:boat": [ + "minecraft:cherry_chest_boat", + "minecraft:oak_chest_boat", + "minecraft:mangrove_boat", + "minecraft:oak_boat", + "minecraft:birch_boat", + "minecraft:jungle_boat", + "minecraft:spruce_boat", + "minecraft:acacia_boat", + "minecraft:dark_oak_boat", + "minecraft:birch_chest_boat", + "minecraft:jungle_chest_boat", + "minecraft:spruce_chest_boat", + "minecraft:acacia_chest_boat", + "minecraft:dark_oak_chest_boat", + "minecraft:mangrove_chest_boat", + "minecraft:cherry_boat", + "minecraft:bamboo_raft", + "minecraft:bamboo_chest_raft" + ], + "minecraft:boats": [ + "minecraft:cherry_chest_boat", + "minecraft:oak_chest_boat", + "minecraft:mangrove_boat", + "minecraft:oak_boat", + "minecraft:birch_boat", + "minecraft:jungle_boat", + "minecraft:spruce_boat", + "minecraft:acacia_boat", + "minecraft:dark_oak_boat", + "minecraft:birch_chest_boat", + "minecraft:jungle_chest_boat", + "minecraft:spruce_chest_boat", + "minecraft:acacia_chest_boat", + "minecraft:dark_oak_chest_boat", + "minecraft:mangrove_chest_boat", + "minecraft:cherry_boat", + "minecraft:bamboo_raft", + "minecraft:bamboo_chest_raft" + ], + "minecraft:bookshelf_books": [ + "minecraft:written_book", + "minecraft:book", + "minecraft:writable_book", + "minecraft:enchanted_book" + ], + "minecraft:chainmail_tier": [ + "minecraft:chainmail_helmet", + "minecraft:chainmail_chestplate", + "minecraft:chainmail_leggings", + "minecraft:chainmail_boots" + ], + "minecraft:coals": [ + "minecraft:coal", + "minecraft:charcoal" + ], + "minecraft:crimson_stems": [ + "minecraft:stripped_crimson_stem", + "minecraft:crimson_hyphae", + "minecraft:crimson_stem", + "minecraft:stripped_crimson_hyphae" + ], + "minecraft:decorated_pot_sherds": [ + "minecraft:skull_pottery_sherd", + "minecraft:angler_pottery_sherd", + "minecraft:prize_pottery_sherd", + "minecraft:mourner_pottery_sherd", + "minecraft:arms_up_pottery_sherd", + "minecraft:burn_pottery_sherd", + "minecraft:snort_pottery_sherd", + "minecraft:brick", + "minecraft:heartbreak_pottery_sherd", + "minecraft:miner_pottery_sherd", + "minecraft:brewer_pottery_sherd", + "minecraft:plenty_pottery_sherd", + "minecraft:scrape_pottery_sherd", + "minecraft:howl_pottery_sherd", + "minecraft:explorer_pottery_sherd", + "minecraft:archer_pottery_sherd", + "minecraft:blade_pottery_sherd", + "minecraft:danger_pottery_sherd", + "minecraft:flow_pottery_sherd", + "minecraft:friend_pottery_sherd", + "minecraft:guster_pottery_sherd", + "minecraft:heart_pottery_sherd", + "minecraft:sheaf_pottery_sherd", + "minecraft:shelter_pottery_sherd" + ], + "minecraft:diamond_tier": [ + "minecraft:diamond_sword", + "minecraft:diamond_shovel", + "minecraft:diamond_pickaxe", + "minecraft:diamond_axe", + "minecraft:mace", + "minecraft:diamond_hoe", + "minecraft:diamond_helmet", + "minecraft:diamond_chestplate", + "minecraft:diamond_leggings", + "minecraft:diamond_boots" + ], + "minecraft:digger": [ + "minecraft:iron_pickaxe", + "minecraft:wooden_pickaxe", + "minecraft:wooden_shovel", + "minecraft:iron_axe", + "minecraft:iron_shovel", + "minecraft:stone_shovel", + "minecraft:wooden_axe", + "minecraft:stone_pickaxe", + "minecraft:stone_axe", + "minecraft:diamond_shovel", + "minecraft:diamond_pickaxe", + "minecraft:diamond_axe", + "minecraft:golden_shovel", + "minecraft:golden_pickaxe", + "minecraft:golden_axe", + "minecraft:wooden_hoe", + "minecraft:stone_hoe", + "minecraft:iron_hoe", + "minecraft:diamond_hoe", + "minecraft:golden_hoe", + "minecraft:netherite_pickaxe", + "minecraft:netherite_shovel", + "minecraft:netherite_axe", + "minecraft:netherite_hoe" + ], + "minecraft:door": [ + "minecraft:warped_door", + "minecraft:waxed_weathered_copper_door", + "minecraft:wooden_door", + "minecraft:iron_door", + "minecraft:mangrove_door", + "minecraft:exposed_copper_door", + "minecraft:bamboo_door", + "minecraft:weathered_copper_door", + "minecraft:jungle_door", + "minecraft:spruce_door", + "minecraft:birch_door", + "minecraft:acacia_door", + "minecraft:dark_oak_door", + "minecraft:crimson_door", + "minecraft:cherry_door", + "minecraft:copper_door", + "minecraft:oxidized_copper_door", + "minecraft:waxed_copper_door", + "minecraft:waxed_exposed_copper_door", + "minecraft:waxed_oxidized_copper_door" + ], + "minecraft:golden_tier": [ + "minecraft:golden_sword", + "minecraft:golden_shovel", + "minecraft:golden_pickaxe", + "minecraft:golden_axe", + "minecraft:golden_hoe", + "minecraft:golden_helmet", + "minecraft:golden_chestplate", + "minecraft:golden_leggings", + "minecraft:golden_boots" + ], + "minecraft:hanging_actor": [ + "minecraft:painting" + ], + "minecraft:hanging_sign": [ + "minecraft:mangrove_hanging_sign", + "minecraft:bamboo_hanging_sign", + "minecraft:spruce_hanging_sign", + "minecraft:birch_hanging_sign", + "minecraft:cherry_hanging_sign", + "minecraft:oak_hanging_sign", + "minecraft:jungle_hanging_sign", + "minecraft:acacia_hanging_sign", + "minecraft:dark_oak_hanging_sign", + "minecraft:crimson_hanging_sign", + "minecraft:warped_hanging_sign" + ], + "minecraft:horse_armor": [ + "minecraft:golden_horse_armor", + "minecraft:leather_horse_armor", + "minecraft:iron_horse_armor", + "minecraft:diamond_horse_armor" + ], + "minecraft:iron_tier": [ + "minecraft:iron_sword", + "minecraft:iron_pickaxe", + "minecraft:iron_axe", + "minecraft:iron_shovel", + "minecraft:iron_hoe", + "minecraft:iron_helmet", + "minecraft:iron_chestplate", + "minecraft:iron_leggings", + "minecraft:iron_boots" + ], + "minecraft:is_armor": [ + "minecraft:netherite_boots", + "minecraft:chainmail_helmet", + "minecraft:elytra", + "minecraft:leather_helmet", + "minecraft:leather_chestplate", + "minecraft:leather_leggings", + "minecraft:leather_boots", + "minecraft:chainmail_chestplate", + "minecraft:chainmail_leggings", + "minecraft:chainmail_boots", + "minecraft:iron_helmet", + "minecraft:iron_chestplate", + "minecraft:iron_leggings", + "minecraft:iron_boots", + "minecraft:diamond_helmet", + "minecraft:diamond_chestplate", + "minecraft:diamond_leggings", + "minecraft:diamond_boots", + "minecraft:golden_helmet", + "minecraft:golden_chestplate", + "minecraft:golden_leggings", + "minecraft:golden_boots", + "minecraft:turtle_helmet", + "minecraft:netherite_chestplate", + "minecraft:netherite_leggings", + "minecraft:netherite_helmet" + ], + "minecraft:is_axe": [ + "minecraft:iron_axe", + "minecraft:wooden_axe", + "minecraft:stone_axe", + "minecraft:diamond_axe", + "minecraft:golden_axe", + "minecraft:netherite_axe" + ], + "minecraft:is_cooked": [ + "minecraft:cooked_cod", + "minecraft:cooked_porkchop", + "minecraft:cooked_rabbit", + "minecraft:cooked_salmon", + "minecraft:cooked_beef", + "minecraft:cooked_chicken", + "minecraft:rabbit_stew", + "minecraft:cooked_mutton" + ], + "minecraft:is_fish": [ + "minecraft:cooked_cod", + "minecraft:cod", + "minecraft:salmon", + "minecraft:tropical_fish", + "minecraft:pufferfish", + "minecraft:cooked_salmon" + ], + "minecraft:is_food": [ + "minecraft:rabbit", + "minecraft:cooked_porkchop", + "minecraft:mushroom_stew", + "minecraft:beetroot", + "minecraft:apple", + "minecraft:carrot", + "minecraft:chicken", + "minecraft:potato", + "minecraft:enchanted_golden_apple", + "minecraft:sweet_berries", + "minecraft:golden_apple", + "minecraft:bread", + "minecraft:porkchop", + "minecraft:cookie", + "minecraft:cooked_rabbit", + "minecraft:beef", + "minecraft:dried_kelp", + "minecraft:beetroot_soup", + "minecraft:melon_slice", + "minecraft:cooked_beef", + "minecraft:rotten_flesh", + "minecraft:cooked_chicken", + "minecraft:baked_potato", + "minecraft:golden_carrot", + "minecraft:pumpkin_pie", + "minecraft:rabbit_stew", + "minecraft:cooked_mutton", + "minecraft:mutton" + ], + "minecraft:is_hoe": [ + "minecraft:wooden_hoe", + "minecraft:stone_hoe", + "minecraft:iron_hoe", + "minecraft:diamond_hoe", + "minecraft:golden_hoe", + "minecraft:netherite_hoe" + ], + "minecraft:is_meat": [ + "minecraft:rabbit", + "minecraft:cooked_porkchop", + "minecraft:chicken", + "minecraft:porkchop", + "minecraft:cooked_rabbit", + "minecraft:beef", + "minecraft:cooked_beef", + "minecraft:rotten_flesh", + "minecraft:cooked_chicken", + "minecraft:rabbit_stew", + "minecraft:cooked_mutton", + "minecraft:mutton" + ], + "minecraft:is_minecart": [ + "minecraft:command_block_minecart", + "minecraft:minecart", + "minecraft:chest_minecart", + "minecraft:tnt_minecart", + "minecraft:hopper_minecart" + ], + "minecraft:is_pickaxe": [ + "minecraft:iron_pickaxe", + "minecraft:wooden_pickaxe", + "minecraft:stone_pickaxe", + "minecraft:diamond_pickaxe", + "minecraft:golden_pickaxe", + "minecraft:netherite_pickaxe" + ], + "minecraft:is_shovel": [ + "minecraft:wooden_shovel", + "minecraft:iron_shovel", + "minecraft:stone_shovel", + "minecraft:diamond_shovel", + "minecraft:golden_shovel", + "minecraft:netherite_shovel" + ], + "minecraft:is_sword": [ + "minecraft:iron_sword", + "minecraft:stone_sword", + "minecraft:wooden_sword", + "minecraft:diamond_sword", + "minecraft:mace", + "minecraft:golden_sword", + "minecraft:netherite_sword" + ], + "minecraft:is_tool": [ + "minecraft:iron_sword", + "minecraft:iron_pickaxe", + "minecraft:wooden_pickaxe", + "minecraft:wooden_shovel", + "minecraft:stone_sword", + "minecraft:iron_axe", + "minecraft:iron_shovel", + "minecraft:stone_shovel", + "minecraft:wooden_sword", + "minecraft:wooden_axe", + "minecraft:stone_pickaxe", + "minecraft:stone_axe", + "minecraft:diamond_sword", + "minecraft:diamond_shovel", + "minecraft:diamond_pickaxe", + "minecraft:diamond_axe", + "minecraft:mace", + "minecraft:golden_sword", + "minecraft:golden_shovel", + "minecraft:golden_pickaxe", + "minecraft:golden_axe", + "minecraft:wooden_hoe", + "minecraft:stone_hoe", + "minecraft:iron_hoe", + "minecraft:diamond_hoe", + "minecraft:golden_hoe", + "minecraft:netherite_sword", + "minecraft:netherite_pickaxe", + "minecraft:netherite_shovel", + "minecraft:netherite_axe", + "minecraft:netherite_hoe" + ], + "minecraft:is_trident": [ + "minecraft:trident" + ], + "minecraft:leather_tier": [ + "minecraft:leather_helmet", + "minecraft:leather_chestplate", + "minecraft:leather_leggings", + "minecraft:leather_boots" + ], + "minecraft:lectern_books": [ + "minecraft:written_book", + "minecraft:writable_book" + ], + "minecraft:logs": [ + "minecraft:stripped_oak_log", + "minecraft:mangrove_wood", + "minecraft:spruce_wood", + "minecraft:stripped_cherry_wood", + "minecraft:stripped_crimson_stem", + "minecraft:stripped_spruce_wood", + "minecraft:spruce_log", + "minecraft:acacia_wood", + "minecraft:stripped_acacia_wood", + "minecraft:stripped_warped_hyphae", + "minecraft:acacia_log", + "minecraft:oak_log", + "minecraft:birch_log", + "minecraft:jungle_log", + "minecraft:dark_oak_log", + "minecraft:jungle_wood", + "minecraft:stripped_jungle_wood", + "minecraft:oak_wood", + "minecraft:stripped_oak_wood", + "minecraft:birch_wood", + "minecraft:stripped_birch_wood", + "minecraft:dark_oak_wood", + "minecraft:stripped_dark_oak_wood", + "minecraft:stripped_dark_oak_log", + "minecraft:mangrove_log", + "minecraft:stripped_jungle_log", + "minecraft:stripped_mangrove_wood", + "minecraft:crimson_hyphae", + "minecraft:stripped_cherry_log", + "minecraft:stripped_birch_log", + "minecraft:stripped_acacia_log", + "minecraft:crimson_stem", + "minecraft:warped_hyphae", + "minecraft:stripped_spruce_log", + "minecraft:warped_stem", + "minecraft:stripped_crimson_hyphae", + "minecraft:cherry_wood", + "minecraft:cherry_log", + "minecraft:stripped_warped_stem", + "minecraft:stripped_mangrove_log" + ], + "minecraft:logs_that_burn": [ + "minecraft:stripped_oak_log", + "minecraft:mangrove_wood", + "minecraft:spruce_wood", + "minecraft:stripped_cherry_wood", + "minecraft:stripped_spruce_wood", + "minecraft:spruce_log", + "minecraft:acacia_wood", + "minecraft:stripped_acacia_wood", + "minecraft:acacia_log", + "minecraft:oak_log", + "minecraft:birch_log", + "minecraft:jungle_log", + "minecraft:dark_oak_log", + "minecraft:jungle_wood", + "minecraft:stripped_jungle_wood", + "minecraft:oak_wood", + "minecraft:stripped_oak_wood", + "minecraft:birch_wood", + "minecraft:stripped_birch_wood", + "minecraft:dark_oak_wood", + "minecraft:stripped_dark_oak_wood", + "minecraft:stripped_dark_oak_log", + "minecraft:mangrove_log", + "minecraft:stripped_jungle_log", + "minecraft:stripped_mangrove_wood", + "minecraft:stripped_cherry_log", + "minecraft:stripped_birch_log", + "minecraft:stripped_acacia_log", + "minecraft:stripped_spruce_log", + "minecraft:cherry_wood", + "minecraft:cherry_log", + "minecraft:stripped_mangrove_log" + ], + "minecraft:mangrove_logs": [ + "minecraft:mangrove_wood", + "minecraft:mangrove_log", + "minecraft:stripped_mangrove_wood", + "minecraft:stripped_mangrove_log" + ], + "minecraft:music_disc": [ + "minecraft:music_disc_ward", + "minecraft:music_disc_strad", + "minecraft:music_disc_chirp", + "minecraft:music_disc_creator_music_box", + "minecraft:music_disc_mall", + "minecraft:music_disc_pigstep", + "minecraft:music_disc_wait", + "minecraft:music_disc_11", + "minecraft:music_disc_stal", + "minecraft:music_disc_13", + "minecraft:music_disc_cat", + "minecraft:music_disc_blocks", + "minecraft:music_disc_far", + "minecraft:music_disc_mellohi", + "minecraft:music_disc_otherside", + "minecraft:music_disc_5", + "minecraft:music_disc_relic", + "minecraft:music_disc_creator", + "minecraft:music_disc_precipice" + ], + "minecraft:netherite_tier": [ + "minecraft:netherite_boots", + "minecraft:netherite_sword", + "minecraft:netherite_chestplate", + "minecraft:netherite_pickaxe", + "minecraft:netherite_leggings", + "minecraft:netherite_shovel", + "minecraft:netherite_axe", + "minecraft:netherite_hoe", + "minecraft:netherite_helmet" + ], + "minecraft:planks": [ + "minecraft:spruce_planks", + "minecraft:oak_planks", + "minecraft:mangrove_planks", + "minecraft:birch_planks", + "minecraft:jungle_planks", + "minecraft:acacia_planks", + "minecraft:dark_oak_planks", + "minecraft:bamboo_planks", + "minecraft:warped_planks", + "minecraft:crimson_planks", + "minecraft:cherry_planks" + ], + "minecraft:sand": [ + "minecraft:sand" + ], + "minecraft:sign": [ + "minecraft:bamboo_sign", + "minecraft:mangrove_hanging_sign", + "minecraft:cherry_sign", + "minecraft:oak_sign", + "minecraft:bamboo_hanging_sign", + "minecraft:warped_sign", + "minecraft:spruce_sign", + "minecraft:spruce_hanging_sign", + "minecraft:acacia_sign", + "minecraft:birch_hanging_sign", + "minecraft:birch_sign", + "minecraft:jungle_sign", + "minecraft:dark_oak_sign", + "minecraft:mangrove_sign", + "minecraft:crimson_sign", + "minecraft:cherry_hanging_sign", + "minecraft:oak_hanging_sign", + "minecraft:jungle_hanging_sign", + "minecraft:acacia_hanging_sign", + "minecraft:dark_oak_hanging_sign", + "minecraft:crimson_hanging_sign", + "minecraft:warped_hanging_sign" + ], + "minecraft:soul_fire_base_blocks": [ + "minecraft:soul_sand", + "minecraft:soul_soil" + ], + "minecraft:spawn_egg": [ + "minecraft:enderman_spawn_egg", + "minecraft:ravager_spawn_egg", + "minecraft:strider_spawn_egg", + "minecraft:slime_spawn_egg", + "minecraft:glow_squid_spawn_egg", + "minecraft:pillager_spawn_egg", + "minecraft:blaze_spawn_egg", + "minecraft:witch_spawn_egg", + "minecraft:horse_spawn_egg", + "minecraft:polar_bear_spawn_egg", + "minecraft:zoglin_spawn_egg", + "minecraft:stray_spawn_egg", + "minecraft:zombie_pigman_spawn_egg", + "minecraft:snow_golem_spawn_egg", + "minecraft:panda_spawn_egg", + "minecraft:axolotl_spawn_egg", + "minecraft:agent_spawn_egg", + "minecraft:shulker_spawn_egg", + "minecraft:vindicator_spawn_egg", + "minecraft:evoker_spawn_egg", + "minecraft:parrot_spawn_egg", + "minecraft:wolf_spawn_egg", + "minecraft:elder_guardian_spawn_egg", + "minecraft:silverfish_spawn_egg", + "minecraft:tadpole_spawn_egg", + "minecraft:cow_spawn_egg", + "minecraft:bogged_spawn_egg", + "minecraft:ocelot_spawn_egg", + "minecraft:bee_spawn_egg", + "minecraft:piglin_brute_spawn_egg", + "minecraft:rabbit_spawn_egg", + "minecraft:camel_spawn_egg", + "minecraft:creeper_spawn_egg", + "minecraft:drowned_spawn_egg", + "minecraft:chicken_spawn_egg", + "minecraft:pig_spawn_egg", + "minecraft:sheep_spawn_egg", + "minecraft:mooshroom_spawn_egg", + "minecraft:skeleton_spawn_egg", + "minecraft:spider_spawn_egg", + "minecraft:zombie_spawn_egg", + "minecraft:villager_spawn_egg", + "minecraft:squid_spawn_egg", + "minecraft:bat_spawn_egg", + "minecraft:ghast_spawn_egg", + "minecraft:magma_cube_spawn_egg", + "minecraft:cave_spider_spawn_egg", + "minecraft:endermite_spawn_egg", + "minecraft:guardian_spawn_egg", + "minecraft:husk_spawn_egg", + "minecraft:piglin_spawn_egg", + "minecraft:wither_skeleton_spawn_egg", + "minecraft:donkey_spawn_egg", + "minecraft:mule_spawn_egg", + "minecraft:skeleton_horse_spawn_egg", + "minecraft:zombie_horse_spawn_egg", + "minecraft:npc_spawn_egg", + "minecraft:breeze_spawn_egg", + "minecraft:llama_spawn_egg", + "minecraft:vex_spawn_egg", + "minecraft:warden_spawn_egg", + "minecraft:zombie_villager_spawn_egg", + "minecraft:tropical_fish_spawn_egg", + "minecraft:cod_spawn_egg", + "minecraft:pufferfish_spawn_egg", + "minecraft:salmon_spawn_egg", + "minecraft:dolphin_spawn_egg", + "minecraft:turtle_spawn_egg", + "minecraft:phantom_spawn_egg", + "minecraft:cat_spawn_egg", + "minecraft:fox_spawn_egg", + "minecraft:wandering_trader_spawn_egg", + "minecraft:hoglin_spawn_egg", + "minecraft:sniffer_spawn_egg", + "minecraft:goat_spawn_egg", + "minecraft:iron_golem_spawn_egg", + "minecraft:ender_dragon_spawn_egg", + "minecraft:wither_spawn_egg", + "minecraft:frog_spawn_egg", + "minecraft:allay_spawn_egg", + "minecraft:trader_llama_spawn_egg", + "minecraft:armadillo_spawn_egg", + "minecraft:spawn_egg" + ], + "minecraft:stone_bricks": [ + "minecraft:stonebrick" + ], + "minecraft:stone_crafting_materials": [ + "minecraft:cobblestone", + "minecraft:cobbled_deepslate", + "minecraft:blackstone" + ], + "minecraft:stone_tier": [ + "minecraft:stone_sword", + "minecraft:stone_shovel", + "minecraft:stone_pickaxe", + "minecraft:stone_axe", + "minecraft:stone_hoe" + ], + "minecraft:stone_tool_materials": [ + "minecraft:cobblestone", + "minecraft:cobbled_deepslate", + "minecraft:blackstone" + ], + "minecraft:transform_materials": [ + "minecraft:netherite_ingot" + ], + "minecraft:transform_templates": [ + "minecraft:netherite_upgrade_smithing_template" + ], + "minecraft:transformable_items": [ + "minecraft:diamond_sword", + "minecraft:diamond_shovel", + "minecraft:diamond_pickaxe", + "minecraft:diamond_axe", + "minecraft:diamond_hoe", + "minecraft:diamond_helmet", + "minecraft:diamond_chestplate", + "minecraft:diamond_leggings", + "minecraft:diamond_boots", + "minecraft:golden_boots" + ], + "minecraft:trim_materials": [ + "minecraft:gold_ingot", + "minecraft:iron_ingot", + "minecraft:diamond", + "minecraft:redstone", + "minecraft:netherite_ingot", + "minecraft:lapis_lazuli", + "minecraft:quartz", + "minecraft:copper_ingot", + "minecraft:emerald", + "minecraft:amethyst_shard" + ], + "minecraft:trim_templates": [ + "minecraft:vex_armor_trim_smithing_template", + "minecraft:wild_armor_trim_smithing_template", + "minecraft:flow_armor_trim_smithing_template", + "minecraft:raiser_armor_trim_smithing_template", + "minecraft:silence_armor_trim_smithing_template", + "minecraft:coast_armor_trim_smithing_template", + "minecraft:snout_armor_trim_smithing_template", + "minecraft:dune_armor_trim_smithing_template", + "minecraft:rib_armor_trim_smithing_template", + "minecraft:host_armor_trim_smithing_template", + "minecraft:bolt_armor_trim_smithing_template", + "minecraft:shaper_armor_trim_smithing_template", + "minecraft:eye_armor_trim_smithing_template", + "minecraft:spire_armor_trim_smithing_template", + "minecraft:sentry_armor_trim_smithing_template", + "minecraft:ward_armor_trim_smithing_template", + "minecraft:tide_armor_trim_smithing_template", + "minecraft:wayfinder_armor_trim_smithing_template" + ], + "minecraft:trimmable_armors": [ + "minecraft:netherite_boots", + "minecraft:chainmail_helmet", + "minecraft:leather_helmet", + "minecraft:leather_chestplate", + "minecraft:leather_leggings", + "minecraft:leather_boots", + "minecraft:chainmail_chestplate", + "minecraft:chainmail_leggings", + "minecraft:chainmail_boots", + "minecraft:iron_helmet", + "minecraft:iron_chestplate", + "minecraft:iron_leggings", + "minecraft:iron_boots", + "minecraft:diamond_helmet", + "minecraft:diamond_chestplate", + "minecraft:diamond_leggings", + "minecraft:diamond_boots", + "minecraft:golden_helmet", + "minecraft:golden_chestplate", + "minecraft:golden_leggings", + "minecraft:golden_boots", + "minecraft:turtle_helmet", + "minecraft:netherite_chestplate", + "minecraft:netherite_leggings", + "minecraft:netherite_helmet" + ], + "minecraft:vibration_damper": [ + "minecraft:black_carpet", + "minecraft:blue_carpet", + "minecraft:pink_carpet", + "minecraft:lime_carpet", + "minecraft:green_wool", + "minecraft:cyan_carpet", + "minecraft:orange_wool", + "minecraft:white_carpet", + "minecraft:purple_carpet", + "minecraft:yellow_carpet", + "minecraft:light_gray_wool", + "minecraft:magenta_carpet", + "minecraft:purple_wool", + "minecraft:gray_wool", + "minecraft:red_carpet", + "minecraft:brown_carpet", + "minecraft:light_blue_wool", + "minecraft:white_wool", + "minecraft:magenta_wool", + "minecraft:yellow_wool", + "minecraft:lime_wool", + "minecraft:pink_wool", + "minecraft:cyan_wool", + "minecraft:blue_wool", + "minecraft:brown_wool", + "minecraft:red_wool", + "minecraft:black_wool", + "minecraft:orange_carpet", + "minecraft:light_blue_carpet", + "minecraft:gray_carpet", + "minecraft:light_gray_carpet", + "minecraft:green_carpet" + ], + "minecraft:warped_stems": [ + "minecraft:stripped_warped_hyphae", + "minecraft:warped_hyphae", + "minecraft:warped_stem", + "minecraft:stripped_warped_stem" + ], + "minecraft:wooden_slabs": [ + "minecraft:birch_slab", + "minecraft:oak_slab", + "minecraft:spruce_slab", + "minecraft:bamboo_slab", + "minecraft:warped_slab", + "minecraft:mangrove_slab", + "minecraft:cherry_slab", + "minecraft:jungle_slab", + "minecraft:acacia_slab", + "minecraft:dark_oak_slab", + "minecraft:crimson_slab" + ], + "minecraft:wooden_tier": [ + "minecraft:wooden_pickaxe", + "minecraft:wooden_shovel", + "minecraft:wooden_sword", + "minecraft:wooden_axe", + "minecraft:wooden_hoe" + ], + "minecraft:wool": [ + "minecraft:green_wool", + "minecraft:orange_wool", + "minecraft:light_gray_wool", + "minecraft:purple_wool", + "minecraft:gray_wool", + "minecraft:light_blue_wool", + "minecraft:white_wool", + "minecraft:magenta_wool", + "minecraft:yellow_wool", + "minecraft:lime_wool", + "minecraft:pink_wool", + "minecraft:cyan_wool", + "minecraft:blue_wool", + "minecraft:brown_wool", + "minecraft:red_wool", + "minecraft:black_wool" + ] +} \ No newline at end of file diff --git a/core/src/main/resources/bedrock/item_tags.1_21_20.json b/core/src/main/resources/bedrock/item_tags.1_21_20.json new file mode 100644 index 000000000..faad0ed0c --- /dev/null +++ b/core/src/main/resources/bedrock/item_tags.1_21_20.json @@ -0,0 +1,806 @@ +{ + "minecraft:arrow": [ + "minecraft:arrow" + ], + "minecraft:banner": [ + "minecraft:banner" + ], + "minecraft:boat": [ + "minecraft:cherry_chest_boat", + "minecraft:oak_chest_boat", + "minecraft:mangrove_boat", + "minecraft:oak_boat", + "minecraft:birch_boat", + "minecraft:jungle_boat", + "minecraft:spruce_boat", + "minecraft:acacia_boat", + "minecraft:dark_oak_boat", + "minecraft:birch_chest_boat", + "minecraft:jungle_chest_boat", + "minecraft:spruce_chest_boat", + "minecraft:acacia_chest_boat", + "minecraft:dark_oak_chest_boat", + "minecraft:mangrove_chest_boat", + "minecraft:cherry_boat", + "minecraft:bamboo_raft", + "minecraft:bamboo_chest_raft" + ], + "minecraft:boats": [ + "minecraft:cherry_chest_boat", + "minecraft:oak_chest_boat", + "minecraft:mangrove_boat", + "minecraft:oak_boat", + "minecraft:birch_boat", + "minecraft:jungle_boat", + "minecraft:spruce_boat", + "minecraft:acacia_boat", + "minecraft:dark_oak_boat", + "minecraft:birch_chest_boat", + "minecraft:jungle_chest_boat", + "minecraft:spruce_chest_boat", + "minecraft:acacia_chest_boat", + "minecraft:dark_oak_chest_boat", + "minecraft:mangrove_chest_boat", + "minecraft:cherry_boat", + "minecraft:bamboo_raft", + "minecraft:bamboo_chest_raft" + ], + "minecraft:bookshelf_books": [ + "minecraft:written_book", + "minecraft:book", + "minecraft:writable_book", + "minecraft:enchanted_book" + ], + "minecraft:chainmail_tier": [ + "minecraft:chainmail_helmet", + "minecraft:chainmail_chestplate", + "minecraft:chainmail_leggings", + "minecraft:chainmail_boots" + ], + "minecraft:coals": [ + "minecraft:coal", + "minecraft:charcoal" + ], + "minecraft:crimson_stems": [ + "minecraft:stripped_crimson_stem", + "minecraft:crimson_hyphae", + "minecraft:crimson_stem", + "minecraft:stripped_crimson_hyphae" + ], + "minecraft:decorated_pot_sherds": [ + "minecraft:angler_pottery_sherd", + "minecraft:skull_pottery_sherd", + "minecraft:prize_pottery_sherd", + "minecraft:mourner_pottery_sherd", + "minecraft:arms_up_pottery_sherd", + "minecraft:burn_pottery_sherd", + "minecraft:snort_pottery_sherd", + "minecraft:brick", + "minecraft:heartbreak_pottery_sherd", + "minecraft:miner_pottery_sherd", + "minecraft:brewer_pottery_sherd", + "minecraft:plenty_pottery_sherd", + "minecraft:scrape_pottery_sherd", + "minecraft:howl_pottery_sherd", + "minecraft:explorer_pottery_sherd", + "minecraft:archer_pottery_sherd", + "minecraft:blade_pottery_sherd", + "minecraft:danger_pottery_sherd", + "minecraft:flow_pottery_sherd", + "minecraft:friend_pottery_sherd", + "minecraft:guster_pottery_sherd", + "minecraft:heart_pottery_sherd", + "minecraft:sheaf_pottery_sherd", + "minecraft:shelter_pottery_sherd" + ], + "minecraft:diamond_tier": [ + "minecraft:diamond_sword", + "minecraft:diamond_shovel", + "minecraft:diamond_pickaxe", + "minecraft:diamond_axe", + "minecraft:mace", + "minecraft:diamond_hoe", + "minecraft:diamond_helmet", + "minecraft:diamond_chestplate", + "minecraft:diamond_leggings", + "minecraft:diamond_boots" + ], + "minecraft:digger": [ + "minecraft:iron_pickaxe", + "minecraft:wooden_pickaxe", + "minecraft:wooden_shovel", + "minecraft:iron_axe", + "minecraft:iron_shovel", + "minecraft:stone_shovel", + "minecraft:wooden_axe", + "minecraft:stone_pickaxe", + "minecraft:stone_axe", + "minecraft:diamond_shovel", + "minecraft:diamond_pickaxe", + "minecraft:diamond_axe", + "minecraft:golden_shovel", + "minecraft:golden_pickaxe", + "minecraft:golden_axe", + "minecraft:wooden_hoe", + "minecraft:stone_hoe", + "minecraft:iron_hoe", + "minecraft:diamond_hoe", + "minecraft:golden_hoe", + "minecraft:netherite_pickaxe", + "minecraft:netherite_shovel", + "minecraft:netherite_axe", + "minecraft:netherite_hoe" + ], + "minecraft:door": [ + "minecraft:warped_door", + "minecraft:waxed_weathered_copper_door", + "minecraft:wooden_door", + "minecraft:iron_door", + "minecraft:mangrove_door", + "minecraft:exposed_copper_door", + "minecraft:bamboo_door", + "minecraft:weathered_copper_door", + "minecraft:jungle_door", + "minecraft:spruce_door", + "minecraft:birch_door", + "minecraft:acacia_door", + "minecraft:dark_oak_door", + "minecraft:crimson_door", + "minecraft:cherry_door", + "minecraft:copper_door", + "minecraft:oxidized_copper_door", + "minecraft:waxed_copper_door", + "minecraft:waxed_exposed_copper_door", + "minecraft:waxed_oxidized_copper_door" + ], + "minecraft:golden_tier": [ + "minecraft:golden_sword", + "minecraft:golden_shovel", + "minecraft:golden_pickaxe", + "minecraft:golden_axe", + "minecraft:golden_hoe", + "minecraft:golden_helmet", + "minecraft:golden_chestplate", + "minecraft:golden_leggings", + "minecraft:golden_boots" + ], + "minecraft:hanging_actor": [ + "minecraft:painting" + ], + "minecraft:hanging_sign": [ + "minecraft:mangrove_hanging_sign", + "minecraft:bamboo_hanging_sign", + "minecraft:spruce_hanging_sign", + "minecraft:birch_hanging_sign", + "minecraft:cherry_hanging_sign", + "minecraft:oak_hanging_sign", + "minecraft:jungle_hanging_sign", + "minecraft:acacia_hanging_sign", + "minecraft:dark_oak_hanging_sign", + "minecraft:crimson_hanging_sign", + "minecraft:warped_hanging_sign" + ], + "minecraft:horse_armor": [ + "minecraft:golden_horse_armor", + "minecraft:leather_horse_armor", + "minecraft:iron_horse_armor", + "minecraft:diamond_horse_armor" + ], + "minecraft:iron_tier": [ + "minecraft:iron_sword", + "minecraft:iron_pickaxe", + "minecraft:iron_axe", + "minecraft:iron_shovel", + "minecraft:iron_hoe", + "minecraft:iron_helmet", + "minecraft:iron_chestplate", + "minecraft:iron_leggings", + "minecraft:iron_boots" + ], + "minecraft:is_armor": [ + "minecraft:netherite_boots", + "minecraft:chainmail_helmet", + "minecraft:elytra", + "minecraft:leather_helmet", + "minecraft:leather_chestplate", + "minecraft:leather_leggings", + "minecraft:leather_boots", + "minecraft:chainmail_chestplate", + "minecraft:chainmail_leggings", + "minecraft:chainmail_boots", + "minecraft:iron_helmet", + "minecraft:iron_chestplate", + "minecraft:iron_leggings", + "minecraft:iron_boots", + "minecraft:diamond_helmet", + "minecraft:diamond_chestplate", + "minecraft:diamond_leggings", + "minecraft:diamond_boots", + "minecraft:golden_helmet", + "minecraft:golden_chestplate", + "minecraft:golden_leggings", + "minecraft:golden_boots", + "minecraft:turtle_helmet", + "minecraft:netherite_chestplate", + "minecraft:netherite_leggings", + "minecraft:netherite_helmet" + ], + "minecraft:is_axe": [ + "minecraft:iron_axe", + "minecraft:wooden_axe", + "minecraft:stone_axe", + "minecraft:diamond_axe", + "minecraft:golden_axe", + "minecraft:netherite_axe" + ], + "minecraft:is_cooked": [ + "minecraft:cooked_cod", + "minecraft:cooked_porkchop", + "minecraft:cooked_rabbit", + "minecraft:cooked_salmon", + "minecraft:cooked_beef", + "minecraft:cooked_chicken", + "minecraft:rabbit_stew", + "minecraft:cooked_mutton" + ], + "minecraft:is_fish": [ + "minecraft:cooked_cod", + "minecraft:cod", + "minecraft:salmon", + "minecraft:tropical_fish", + "minecraft:pufferfish", + "minecraft:cooked_salmon" + ], + "minecraft:is_food": [ + "minecraft:beetroot", + "minecraft:apple", + "minecraft:carrot", + "minecraft:chicken", + "minecraft:potato", + "minecraft:enchanted_golden_apple", + "minecraft:sweet_berries", + "minecraft:golden_carrot", + "minecraft:golden_apple", + "minecraft:rabbit", + "minecraft:cooked_porkchop", + "minecraft:mushroom_stew", + "minecraft:bread", + "minecraft:porkchop", + "minecraft:cookie", + "minecraft:cooked_rabbit", + "minecraft:beef", + "minecraft:dried_kelp", + "minecraft:beetroot_soup", + "minecraft:melon_slice", + "minecraft:cooked_beef", + "minecraft:rotten_flesh", + "minecraft:cooked_chicken", + "minecraft:baked_potato", + "minecraft:pumpkin_pie", + "minecraft:rabbit_stew", + "minecraft:cooked_mutton", + "minecraft:mutton" + ], + "minecraft:is_hoe": [ + "minecraft:wooden_hoe", + "minecraft:stone_hoe", + "minecraft:iron_hoe", + "minecraft:diamond_hoe", + "minecraft:golden_hoe", + "minecraft:netherite_hoe" + ], + "minecraft:is_meat": [ + "minecraft:chicken", + "minecraft:rabbit", + "minecraft:cooked_porkchop", + "minecraft:porkchop", + "minecraft:cooked_rabbit", + "minecraft:beef", + "minecraft:cooked_beef", + "minecraft:rotten_flesh", + "minecraft:cooked_chicken", + "minecraft:rabbit_stew", + "minecraft:cooked_mutton", + "minecraft:mutton" + ], + "minecraft:is_minecart": [ + "minecraft:command_block_minecart", + "minecraft:minecart", + "minecraft:chest_minecart", + "minecraft:tnt_minecart", + "minecraft:hopper_minecart" + ], + "minecraft:is_pickaxe": [ + "minecraft:iron_pickaxe", + "minecraft:wooden_pickaxe", + "minecraft:stone_pickaxe", + "minecraft:diamond_pickaxe", + "minecraft:golden_pickaxe", + "minecraft:netherite_pickaxe" + ], + "minecraft:is_shovel": [ + "minecraft:wooden_shovel", + "minecraft:iron_shovel", + "minecraft:stone_shovel", + "minecraft:diamond_shovel", + "minecraft:golden_shovel", + "minecraft:netherite_shovel" + ], + "minecraft:is_sword": [ + "minecraft:iron_sword", + "minecraft:stone_sword", + "minecraft:wooden_sword", + "minecraft:diamond_sword", + "minecraft:mace", + "minecraft:golden_sword", + "minecraft:netherite_sword" + ], + "minecraft:is_tool": [ + "minecraft:iron_sword", + "minecraft:iron_pickaxe", + "minecraft:wooden_pickaxe", + "minecraft:wooden_shovel", + "minecraft:stone_sword", + "minecraft:iron_axe", + "minecraft:iron_shovel", + "minecraft:stone_shovel", + "minecraft:wooden_sword", + "minecraft:wooden_axe", + "minecraft:stone_pickaxe", + "minecraft:stone_axe", + "minecraft:diamond_sword", + "minecraft:diamond_shovel", + "minecraft:diamond_pickaxe", + "minecraft:diamond_axe", + "minecraft:mace", + "minecraft:golden_sword", + "minecraft:golden_shovel", + "minecraft:golden_pickaxe", + "minecraft:golden_axe", + "minecraft:wooden_hoe", + "minecraft:stone_hoe", + "minecraft:iron_hoe", + "minecraft:diamond_hoe", + "minecraft:golden_hoe", + "minecraft:netherite_sword", + "minecraft:netherite_pickaxe", + "minecraft:netherite_shovel", + "minecraft:netherite_axe", + "minecraft:netherite_hoe" + ], + "minecraft:is_trident": [ + "minecraft:trident" + ], + "minecraft:leather_tier": [ + "minecraft:leather_helmet", + "minecraft:leather_chestplate", + "minecraft:leather_leggings", + "minecraft:leather_boots" + ], + "minecraft:lectern_books": [ + "minecraft:written_book", + "minecraft:writable_book" + ], + "minecraft:logs": [ + "minecraft:mangrove_wood", + "minecraft:spruce_wood", + "minecraft:stripped_cherry_wood", + "minecraft:stripped_crimson_stem", + "minecraft:stripped_spruce_wood", + "minecraft:spruce_log", + "minecraft:acacia_wood", + "minecraft:stripped_acacia_wood", + "minecraft:stripped_warped_hyphae", + "minecraft:acacia_log", + "minecraft:stripped_warped_stem", + "minecraft:oak_log", + "minecraft:birch_log", + "minecraft:jungle_log", + "minecraft:dark_oak_log", + "minecraft:jungle_wood", + "minecraft:stripped_jungle_wood", + "minecraft:oak_wood", + "minecraft:birch_wood", + "minecraft:dark_oak_wood", + "minecraft:stripped_oak_wood", + "minecraft:stripped_birch_wood", + "minecraft:stripped_dark_oak_wood", + "minecraft:stripped_oak_log", + "minecraft:stripped_dark_oak_log", + "minecraft:mangrove_log", + "minecraft:stripped_jungle_log", + "minecraft:stripped_mangrove_wood", + "minecraft:crimson_hyphae", + "minecraft:stripped_cherry_log", + "minecraft:stripped_birch_log", + "minecraft:stripped_acacia_log", + "minecraft:crimson_stem", + "minecraft:warped_hyphae", + "minecraft:stripped_spruce_log", + "minecraft:warped_stem", + "minecraft:stripped_crimson_hyphae", + "minecraft:cherry_wood", + "minecraft:cherry_log", + "minecraft:stripped_mangrove_log" + ], + "minecraft:logs_that_burn": [ + "minecraft:mangrove_wood", + "minecraft:spruce_wood", + "minecraft:stripped_cherry_wood", + "minecraft:stripped_spruce_wood", + "minecraft:spruce_log", + "minecraft:acacia_wood", + "minecraft:stripped_acacia_wood", + "minecraft:acacia_log", + "minecraft:oak_log", + "minecraft:birch_log", + "minecraft:jungle_log", + "minecraft:dark_oak_log", + "minecraft:jungle_wood", + "minecraft:stripped_jungle_wood", + "minecraft:oak_wood", + "minecraft:birch_wood", + "minecraft:dark_oak_wood", + "minecraft:stripped_oak_wood", + "minecraft:stripped_birch_wood", + "minecraft:stripped_dark_oak_wood", + "minecraft:stripped_oak_log", + "minecraft:stripped_dark_oak_log", + "minecraft:mangrove_log", + "minecraft:stripped_jungle_log", + "minecraft:stripped_mangrove_wood", + "minecraft:stripped_cherry_log", + "minecraft:stripped_birch_log", + "minecraft:stripped_acacia_log", + "minecraft:stripped_spruce_log", + "minecraft:cherry_wood", + "minecraft:cherry_log", + "minecraft:stripped_mangrove_log" + ], + "minecraft:mangrove_logs": [ + "minecraft:mangrove_wood", + "minecraft:mangrove_log", + "minecraft:stripped_mangrove_wood", + "minecraft:stripped_mangrove_log" + ], + "minecraft:music_disc": [ + "minecraft:music_disc_ward", + "minecraft:music_disc_strad", + "minecraft:music_disc_chirp", + "minecraft:music_disc_creator_music_box", + "minecraft:music_disc_mall", + "minecraft:music_disc_pigstep", + "minecraft:music_disc_wait", + "minecraft:music_disc_11", + "minecraft:music_disc_stal", + "minecraft:music_disc_13", + "minecraft:music_disc_cat", + "minecraft:music_disc_blocks", + "minecraft:music_disc_far", + "minecraft:music_disc_mellohi", + "minecraft:music_disc_otherside", + "minecraft:music_disc_5", + "minecraft:music_disc_relic", + "minecraft:music_disc_creator", + "minecraft:music_disc_precipice" + ], + "minecraft:netherite_tier": [ + "minecraft:netherite_boots", + "minecraft:netherite_sword", + "minecraft:netherite_chestplate", + "minecraft:netherite_pickaxe", + "minecraft:netherite_leggings", + "minecraft:netherite_shovel", + "minecraft:netherite_axe", + "minecraft:netherite_hoe", + "minecraft:netherite_helmet" + ], + "minecraft:planks": [ + "minecraft:spruce_planks", + "minecraft:oak_planks", + "minecraft:mangrove_planks", + "minecraft:dark_oak_planks", + "minecraft:birch_planks", + "minecraft:jungle_planks", + "minecraft:acacia_planks", + "minecraft:bamboo_planks", + "minecraft:warped_planks", + "minecraft:crimson_planks", + "minecraft:cherry_planks" + ], + "minecraft:sand": [ + "minecraft:sand", + "minecraft:red_sand" + ], + "minecraft:sign": [ + "minecraft:bamboo_sign", + "minecraft:mangrove_hanging_sign", + "minecraft:cherry_sign", + "minecraft:oak_sign", + "minecraft:bamboo_hanging_sign", + "minecraft:warped_sign", + "minecraft:spruce_sign", + "minecraft:spruce_hanging_sign", + "minecraft:acacia_sign", + "minecraft:birch_hanging_sign", + "minecraft:birch_sign", + "minecraft:jungle_sign", + "minecraft:dark_oak_sign", + "minecraft:mangrove_sign", + "minecraft:crimson_sign", + "minecraft:cherry_hanging_sign", + "minecraft:oak_hanging_sign", + "minecraft:jungle_hanging_sign", + "minecraft:acacia_hanging_sign", + "minecraft:dark_oak_hanging_sign", + "minecraft:crimson_hanging_sign", + "minecraft:warped_hanging_sign" + ], + "minecraft:soul_fire_base_blocks": [ + "minecraft:soul_soil", + "minecraft:soul_sand" + ], + "minecraft:spawn_egg": [ + "minecraft:enderman_spawn_egg", + "minecraft:strider_spawn_egg", + "minecraft:ravager_spawn_egg", + "minecraft:slime_spawn_egg", + "minecraft:glow_squid_spawn_egg", + "minecraft:pillager_spawn_egg", + "minecraft:blaze_spawn_egg", + "minecraft:witch_spawn_egg", + "minecraft:horse_spawn_egg", + "minecraft:polar_bear_spawn_egg", + "minecraft:zoglin_spawn_egg", + "minecraft:stray_spawn_egg", + "minecraft:zombie_pigman_spawn_egg", + "minecraft:snow_golem_spawn_egg", + "minecraft:panda_spawn_egg", + "minecraft:axolotl_spawn_egg", + "minecraft:agent_spawn_egg", + "minecraft:shulker_spawn_egg", + "minecraft:vindicator_spawn_egg", + "minecraft:evoker_spawn_egg", + "minecraft:parrot_spawn_egg", + "minecraft:wolf_spawn_egg", + "minecraft:elder_guardian_spawn_egg", + "minecraft:silverfish_spawn_egg", + "minecraft:tadpole_spawn_egg", + "minecraft:cow_spawn_egg", + "minecraft:bogged_spawn_egg", + "minecraft:ocelot_spawn_egg", + "minecraft:bee_spawn_egg", + "minecraft:piglin_brute_spawn_egg", + "minecraft:rabbit_spawn_egg", + "minecraft:camel_spawn_egg", + "minecraft:creeper_spawn_egg", + "minecraft:drowned_spawn_egg", + "minecraft:chicken_spawn_egg", + "minecraft:pig_spawn_egg", + "minecraft:sheep_spawn_egg", + "minecraft:mooshroom_spawn_egg", + "minecraft:skeleton_spawn_egg", + "minecraft:spider_spawn_egg", + "minecraft:zombie_spawn_egg", + "minecraft:villager_spawn_egg", + "minecraft:squid_spawn_egg", + "minecraft:bat_spawn_egg", + "minecraft:ghast_spawn_egg", + "minecraft:magma_cube_spawn_egg", + "minecraft:cave_spider_spawn_egg", + "minecraft:endermite_spawn_egg", + "minecraft:guardian_spawn_egg", + "minecraft:husk_spawn_egg", + "minecraft:piglin_spawn_egg", + "minecraft:wither_skeleton_spawn_egg", + "minecraft:donkey_spawn_egg", + "minecraft:mule_spawn_egg", + "minecraft:skeleton_horse_spawn_egg", + "minecraft:zombie_horse_spawn_egg", + "minecraft:npc_spawn_egg", + "minecraft:breeze_spawn_egg", + "minecraft:llama_spawn_egg", + "minecraft:vex_spawn_egg", + "minecraft:warden_spawn_egg", + "minecraft:zombie_villager_spawn_egg", + "minecraft:tropical_fish_spawn_egg", + "minecraft:cod_spawn_egg", + "minecraft:pufferfish_spawn_egg", + "minecraft:salmon_spawn_egg", + "minecraft:dolphin_spawn_egg", + "minecraft:turtle_spawn_egg", + "minecraft:phantom_spawn_egg", + "minecraft:cat_spawn_egg", + "minecraft:fox_spawn_egg", + "minecraft:wandering_trader_spawn_egg", + "minecraft:hoglin_spawn_egg", + "minecraft:sniffer_spawn_egg", + "minecraft:goat_spawn_egg", + "minecraft:iron_golem_spawn_egg", + "minecraft:ender_dragon_spawn_egg", + "minecraft:wither_spawn_egg", + "minecraft:frog_spawn_egg", + "minecraft:allay_spawn_egg", + "minecraft:trader_llama_spawn_egg", + "minecraft:armadillo_spawn_egg", + "minecraft:spawn_egg" + ], + "minecraft:stone_bricks": [ + "minecraft:mossy_stone_bricks", + "minecraft:stone_bricks", + "minecraft:cracked_stone_bricks", + "minecraft:chiseled_stone_bricks" + ], + "minecraft:stone_crafting_materials": [ + "minecraft:cobblestone", + "minecraft:cobbled_deepslate", + "minecraft:blackstone" + ], + "minecraft:stone_tier": [ + "minecraft:stone_sword", + "minecraft:stone_shovel", + "minecraft:stone_pickaxe", + "minecraft:stone_axe", + "minecraft:stone_hoe" + ], + "minecraft:stone_tool_materials": [ + "minecraft:cobblestone", + "minecraft:cobbled_deepslate", + "minecraft:blackstone" + ], + "minecraft:transform_materials": [ + "minecraft:netherite_ingot" + ], + "minecraft:transform_templates": [ + "minecraft:netherite_upgrade_smithing_template" + ], + "minecraft:transformable_items": [ + "minecraft:diamond_sword", + "minecraft:diamond_shovel", + "minecraft:diamond_pickaxe", + "minecraft:diamond_axe", + "minecraft:diamond_hoe", + "minecraft:diamond_helmet", + "minecraft:diamond_chestplate", + "minecraft:diamond_leggings", + "minecraft:diamond_boots", + "minecraft:golden_boots" + ], + "minecraft:trim_materials": [ + "minecraft:gold_ingot", + "minecraft:iron_ingot", + "minecraft:diamond", + "minecraft:redstone", + "minecraft:netherite_ingot", + "minecraft:lapis_lazuli", + "minecraft:quartz", + "minecraft:copper_ingot", + "minecraft:emerald", + "minecraft:amethyst_shard" + ], + "minecraft:trim_templates": [ + "minecraft:vex_armor_trim_smithing_template", + "minecraft:wild_armor_trim_smithing_template", + "minecraft:flow_armor_trim_smithing_template", + "minecraft:raiser_armor_trim_smithing_template", + "minecraft:silence_armor_trim_smithing_template", + "minecraft:coast_armor_trim_smithing_template", + "minecraft:snout_armor_trim_smithing_template", + "minecraft:dune_armor_trim_smithing_template", + "minecraft:rib_armor_trim_smithing_template", + "minecraft:host_armor_trim_smithing_template", + "minecraft:bolt_armor_trim_smithing_template", + "minecraft:shaper_armor_trim_smithing_template", + "minecraft:eye_armor_trim_smithing_template", + "minecraft:spire_armor_trim_smithing_template", + "minecraft:sentry_armor_trim_smithing_template", + "minecraft:ward_armor_trim_smithing_template", + "minecraft:tide_armor_trim_smithing_template", + "minecraft:wayfinder_armor_trim_smithing_template" + ], + "minecraft:trimmable_armors": [ + "minecraft:netherite_boots", + "minecraft:chainmail_helmet", + "minecraft:leather_helmet", + "minecraft:leather_chestplate", + "minecraft:leather_leggings", + "minecraft:leather_boots", + "minecraft:chainmail_chestplate", + "minecraft:chainmail_leggings", + "minecraft:chainmail_boots", + "minecraft:iron_helmet", + "minecraft:iron_chestplate", + "minecraft:iron_leggings", + "minecraft:iron_boots", + "minecraft:diamond_helmet", + "minecraft:diamond_chestplate", + "minecraft:diamond_leggings", + "minecraft:diamond_boots", + "minecraft:golden_helmet", + "minecraft:golden_chestplate", + "minecraft:golden_leggings", + "minecraft:golden_boots", + "minecraft:turtle_helmet", + "minecraft:netherite_chestplate", + "minecraft:netherite_leggings", + "minecraft:netherite_helmet" + ], + "minecraft:vibration_damper": [ + "minecraft:black_carpet", + "minecraft:blue_carpet", + "minecraft:pink_carpet", + "minecraft:lime_carpet", + "minecraft:green_wool", + "minecraft:cyan_carpet", + "minecraft:orange_wool", + "minecraft:white_carpet", + "minecraft:purple_carpet", + "minecraft:yellow_carpet", + "minecraft:light_gray_wool", + "minecraft:magenta_carpet", + "minecraft:purple_wool", + "minecraft:gray_wool", + "minecraft:red_carpet", + "minecraft:brown_carpet", + "minecraft:light_blue_wool", + "minecraft:white_wool", + "minecraft:magenta_wool", + "minecraft:yellow_wool", + "minecraft:lime_wool", + "minecraft:pink_wool", + "minecraft:cyan_wool", + "minecraft:blue_wool", + "minecraft:brown_wool", + "minecraft:red_wool", + "minecraft:black_wool", + "minecraft:orange_carpet", + "minecraft:light_blue_carpet", + "minecraft:gray_carpet", + "minecraft:light_gray_carpet", + "minecraft:green_carpet" + ], + "minecraft:warped_stems": [ + "minecraft:stripped_warped_hyphae", + "minecraft:stripped_warped_stem", + "minecraft:warped_hyphae", + "minecraft:warped_stem" + ], + "minecraft:wooden_slabs": [ + "minecraft:birch_slab", + "minecraft:oak_slab", + "minecraft:spruce_slab", + "minecraft:bamboo_slab", + "minecraft:warped_slab", + "minecraft:mangrove_slab", + "minecraft:cherry_slab", + "minecraft:jungle_slab", + "minecraft:acacia_slab", + "minecraft:dark_oak_slab", + "minecraft:crimson_slab" + ], + "minecraft:wooden_tier": [ + "minecraft:wooden_pickaxe", + "minecraft:wooden_shovel", + "minecraft:wooden_sword", + "minecraft:wooden_axe", + "minecraft:wooden_hoe" + ], + "minecraft:wool": [ + "minecraft:green_wool", + "minecraft:orange_wool", + "minecraft:light_gray_wool", + "minecraft:purple_wool", + "minecraft:gray_wool", + "minecraft:light_blue_wool", + "minecraft:white_wool", + "minecraft:magenta_wool", + "minecraft:yellow_wool", + "minecraft:lime_wool", + "minecraft:pink_wool", + "minecraft:cyan_wool", + "minecraft:blue_wool", + "minecraft:brown_wool", + "minecraft:red_wool", + "minecraft:black_wool" + ] +} \ No newline at end of file diff --git a/core/src/main/resources/bedrock/item_tags.1_21_30.json b/core/src/main/resources/bedrock/item_tags.1_21_30.json new file mode 100644 index 000000000..cff3f2556 --- /dev/null +++ b/core/src/main/resources/bedrock/item_tags.1_21_30.json @@ -0,0 +1,806 @@ +{ + "minecraft:arrow": [ + "minecraft:arrow" + ], + "minecraft:banner": [ + "minecraft:banner" + ], + "minecraft:boat": [ + "minecraft:cherry_chest_boat", + "minecraft:oak_chest_boat", + "minecraft:mangrove_boat", + "minecraft:oak_boat", + "minecraft:birch_boat", + "minecraft:jungle_boat", + "minecraft:spruce_boat", + "minecraft:acacia_boat", + "minecraft:dark_oak_boat", + "minecraft:birch_chest_boat", + "minecraft:jungle_chest_boat", + "minecraft:spruce_chest_boat", + "minecraft:acacia_chest_boat", + "minecraft:dark_oak_chest_boat", + "minecraft:mangrove_chest_boat", + "minecraft:cherry_boat", + "minecraft:bamboo_raft", + "minecraft:bamboo_chest_raft" + ], + "minecraft:boats": [ + "minecraft:cherry_chest_boat", + "minecraft:oak_chest_boat", + "minecraft:mangrove_boat", + "minecraft:oak_boat", + "minecraft:birch_boat", + "minecraft:jungle_boat", + "minecraft:spruce_boat", + "minecraft:acacia_boat", + "minecraft:dark_oak_boat", + "minecraft:birch_chest_boat", + "minecraft:jungle_chest_boat", + "minecraft:spruce_chest_boat", + "minecraft:acacia_chest_boat", + "minecraft:dark_oak_chest_boat", + "minecraft:mangrove_chest_boat", + "minecraft:cherry_boat", + "minecraft:bamboo_raft", + "minecraft:bamboo_chest_raft" + ], + "minecraft:bookshelf_books": [ + "minecraft:written_book", + "minecraft:book", + "minecraft:writable_book", + "minecraft:enchanted_book" + ], + "minecraft:chainmail_tier": [ + "minecraft:chainmail_helmet", + "minecraft:chainmail_chestplate", + "minecraft:chainmail_leggings", + "minecraft:chainmail_boots" + ], + "minecraft:coals": [ + "minecraft:coal", + "minecraft:charcoal" + ], + "minecraft:crimson_stems": [ + "minecraft:stripped_crimson_stem", + "minecraft:crimson_hyphae", + "minecraft:crimson_stem", + "minecraft:stripped_crimson_hyphae" + ], + "minecraft:decorated_pot_sherds": [ + "minecraft:angler_pottery_sherd", + "minecraft:skull_pottery_sherd", + "minecraft:prize_pottery_sherd", + "minecraft:mourner_pottery_sherd", + "minecraft:arms_up_pottery_sherd", + "minecraft:burn_pottery_sherd", + "minecraft:snort_pottery_sherd", + "minecraft:brick", + "minecraft:heartbreak_pottery_sherd", + "minecraft:miner_pottery_sherd", + "minecraft:brewer_pottery_sherd", + "minecraft:plenty_pottery_sherd", + "minecraft:scrape_pottery_sherd", + "minecraft:howl_pottery_sherd", + "minecraft:explorer_pottery_sherd", + "minecraft:archer_pottery_sherd", + "minecraft:blade_pottery_sherd", + "minecraft:danger_pottery_sherd", + "minecraft:flow_pottery_sherd", + "minecraft:friend_pottery_sherd", + "minecraft:guster_pottery_sherd", + "minecraft:heart_pottery_sherd", + "minecraft:sheaf_pottery_sherd", + "minecraft:shelter_pottery_sherd" + ], + "minecraft:diamond_tier": [ + "minecraft:diamond_sword", + "minecraft:diamond_shovel", + "minecraft:diamond_pickaxe", + "minecraft:diamond_axe", + "minecraft:mace", + "minecraft:diamond_hoe", + "minecraft:diamond_helmet", + "minecraft:diamond_chestplate", + "minecraft:diamond_leggings", + "minecraft:diamond_boots" + ], + "minecraft:digger": [ + "minecraft:iron_pickaxe", + "minecraft:wooden_pickaxe", + "minecraft:wooden_shovel", + "minecraft:iron_axe", + "minecraft:iron_shovel", + "minecraft:stone_shovel", + "minecraft:wooden_axe", + "minecraft:stone_pickaxe", + "minecraft:stone_axe", + "minecraft:diamond_shovel", + "minecraft:diamond_pickaxe", + "minecraft:diamond_axe", + "minecraft:golden_shovel", + "minecraft:golden_pickaxe", + "minecraft:golden_axe", + "minecraft:wooden_hoe", + "minecraft:stone_hoe", + "minecraft:iron_hoe", + "minecraft:diamond_hoe", + "minecraft:golden_hoe", + "minecraft:netherite_pickaxe", + "minecraft:netherite_shovel", + "minecraft:netherite_axe", + "minecraft:netherite_hoe" + ], + "minecraft:door": [ + "minecraft:warped_door", + "minecraft:waxed_weathered_copper_door", + "minecraft:wooden_door", + "minecraft:iron_door", + "minecraft:mangrove_door", + "minecraft:exposed_copper_door", + "minecraft:bamboo_door", + "minecraft:weathered_copper_door", + "minecraft:jungle_door", + "minecraft:spruce_door", + "minecraft:birch_door", + "minecraft:acacia_door", + "minecraft:dark_oak_door", + "minecraft:crimson_door", + "minecraft:cherry_door", + "minecraft:copper_door", + "minecraft:oxidized_copper_door", + "minecraft:waxed_copper_door", + "minecraft:waxed_exposed_copper_door", + "minecraft:waxed_oxidized_copper_door" + ], + "minecraft:golden_tier": [ + "minecraft:golden_sword", + "minecraft:golden_shovel", + "minecraft:golden_pickaxe", + "minecraft:golden_axe", + "minecraft:golden_hoe", + "minecraft:golden_helmet", + "minecraft:golden_chestplate", + "minecraft:golden_leggings", + "minecraft:golden_boots" + ], + "minecraft:hanging_actor": [ + "minecraft:painting" + ], + "minecraft:hanging_sign": [ + "minecraft:mangrove_hanging_sign", + "minecraft:bamboo_hanging_sign", + "minecraft:spruce_hanging_sign", + "minecraft:birch_hanging_sign", + "minecraft:cherry_hanging_sign", + "minecraft:oak_hanging_sign", + "minecraft:jungle_hanging_sign", + "minecraft:acacia_hanging_sign", + "minecraft:dark_oak_hanging_sign", + "minecraft:crimson_hanging_sign", + "minecraft:warped_hanging_sign" + ], + "minecraft:horse_armor": [ + "minecraft:golden_horse_armor", + "minecraft:leather_horse_armor", + "minecraft:iron_horse_armor", + "minecraft:diamond_horse_armor" + ], + "minecraft:iron_tier": [ + "minecraft:iron_sword", + "minecraft:iron_pickaxe", + "minecraft:iron_axe", + "minecraft:iron_shovel", + "minecraft:iron_hoe", + "minecraft:iron_helmet", + "minecraft:iron_chestplate", + "minecraft:iron_leggings", + "minecraft:iron_boots" + ], + "minecraft:is_armor": [ + "minecraft:netherite_boots", + "minecraft:chainmail_helmet", + "minecraft:elytra", + "minecraft:leather_helmet", + "minecraft:leather_chestplate", + "minecraft:leather_leggings", + "minecraft:leather_boots", + "minecraft:chainmail_chestplate", + "minecraft:chainmail_leggings", + "minecraft:chainmail_boots", + "minecraft:iron_helmet", + "minecraft:iron_chestplate", + "minecraft:iron_leggings", + "minecraft:iron_boots", + "minecraft:diamond_helmet", + "minecraft:diamond_chestplate", + "minecraft:diamond_leggings", + "minecraft:diamond_boots", + "minecraft:golden_helmet", + "minecraft:golden_chestplate", + "minecraft:golden_leggings", + "minecraft:golden_boots", + "minecraft:turtle_helmet", + "minecraft:netherite_chestplate", + "minecraft:netherite_leggings", + "minecraft:netherite_helmet" + ], + "minecraft:is_axe": [ + "minecraft:iron_axe", + "minecraft:wooden_axe", + "minecraft:stone_axe", + "minecraft:diamond_axe", + "minecraft:golden_axe", + "minecraft:netherite_axe" + ], + "minecraft:is_cooked": [ + "minecraft:cooked_cod", + "minecraft:cooked_porkchop", + "minecraft:cooked_rabbit", + "minecraft:cooked_salmon", + "minecraft:cooked_beef", + "minecraft:cooked_chicken", + "minecraft:rabbit_stew", + "minecraft:cooked_mutton" + ], + "minecraft:is_fish": [ + "minecraft:cooked_cod", + "minecraft:cod", + "minecraft:salmon", + "minecraft:tropical_fish", + "minecraft:pufferfish", + "minecraft:cooked_salmon" + ], + "minecraft:is_food": [ + "minecraft:golden_carrot", + "minecraft:carrot", + "minecraft:chicken", + "minecraft:apple", + "minecraft:beetroot", + "minecraft:potato", + "minecraft:enchanted_golden_apple", + "minecraft:sweet_berries", + "minecraft:golden_apple", + "minecraft:rabbit", + "minecraft:cooked_porkchop", + "minecraft:mushroom_stew", + "minecraft:bread", + "minecraft:porkchop", + "minecraft:cookie", + "minecraft:cooked_rabbit", + "minecraft:beef", + "minecraft:dried_kelp", + "minecraft:beetroot_soup", + "minecraft:melon_slice", + "minecraft:cooked_beef", + "minecraft:rotten_flesh", + "minecraft:cooked_chicken", + "minecraft:baked_potato", + "minecraft:pumpkin_pie", + "minecraft:rabbit_stew", + "minecraft:cooked_mutton", + "minecraft:mutton" + ], + "minecraft:is_hoe": [ + "minecraft:wooden_hoe", + "minecraft:stone_hoe", + "minecraft:iron_hoe", + "minecraft:diamond_hoe", + "minecraft:golden_hoe", + "minecraft:netherite_hoe" + ], + "minecraft:is_meat": [ + "minecraft:chicken", + "minecraft:rabbit", + "minecraft:cooked_porkchop", + "minecraft:porkchop", + "minecraft:cooked_rabbit", + "minecraft:beef", + "minecraft:cooked_beef", + "minecraft:rotten_flesh", + "minecraft:cooked_chicken", + "minecraft:rabbit_stew", + "minecraft:cooked_mutton", + "minecraft:mutton" + ], + "minecraft:is_minecart": [ + "minecraft:command_block_minecart", + "minecraft:minecart", + "minecraft:chest_minecart", + "minecraft:tnt_minecart", + "minecraft:hopper_minecart" + ], + "minecraft:is_pickaxe": [ + "minecraft:iron_pickaxe", + "minecraft:wooden_pickaxe", + "minecraft:stone_pickaxe", + "minecraft:diamond_pickaxe", + "minecraft:golden_pickaxe", + "minecraft:netherite_pickaxe" + ], + "minecraft:is_shovel": [ + "minecraft:wooden_shovel", + "minecraft:iron_shovel", + "minecraft:stone_shovel", + "minecraft:diamond_shovel", + "minecraft:golden_shovel", + "minecraft:netherite_shovel" + ], + "minecraft:is_sword": [ + "minecraft:iron_sword", + "minecraft:stone_sword", + "minecraft:wooden_sword", + "minecraft:diamond_sword", + "minecraft:mace", + "minecraft:golden_sword", + "minecraft:netherite_sword" + ], + "minecraft:is_tool": [ + "minecraft:iron_sword", + "minecraft:iron_pickaxe", + "minecraft:wooden_pickaxe", + "minecraft:wooden_shovel", + "minecraft:stone_sword", + "minecraft:iron_axe", + "minecraft:iron_shovel", + "minecraft:stone_shovel", + "minecraft:wooden_sword", + "minecraft:wooden_axe", + "minecraft:stone_pickaxe", + "minecraft:stone_axe", + "minecraft:diamond_sword", + "minecraft:diamond_shovel", + "minecraft:diamond_pickaxe", + "minecraft:diamond_axe", + "minecraft:mace", + "minecraft:golden_sword", + "minecraft:golden_shovel", + "minecraft:golden_pickaxe", + "minecraft:golden_axe", + "minecraft:wooden_hoe", + "minecraft:stone_hoe", + "minecraft:iron_hoe", + "minecraft:diamond_hoe", + "minecraft:golden_hoe", + "minecraft:netherite_sword", + "minecraft:netherite_pickaxe", + "minecraft:netherite_shovel", + "minecraft:netherite_axe", + "minecraft:netherite_hoe" + ], + "minecraft:is_trident": [ + "minecraft:trident" + ], + "minecraft:leather_tier": [ + "minecraft:leather_helmet", + "minecraft:leather_chestplate", + "minecraft:leather_leggings", + "minecraft:leather_boots" + ], + "minecraft:lectern_books": [ + "minecraft:written_book", + "minecraft:writable_book" + ], + "minecraft:logs": [ + "minecraft:mangrove_wood", + "minecraft:spruce_wood", + "minecraft:stripped_cherry_wood", + "minecraft:stripped_crimson_stem", + "minecraft:stripped_spruce_wood", + "minecraft:spruce_log", + "minecraft:acacia_wood", + "minecraft:stripped_acacia_wood", + "minecraft:stripped_warped_hyphae", + "minecraft:acacia_log", + "minecraft:stripped_warped_stem", + "minecraft:oak_log", + "minecraft:birch_log", + "minecraft:jungle_log", + "minecraft:dark_oak_log", + "minecraft:jungle_wood", + "minecraft:stripped_jungle_wood", + "minecraft:oak_wood", + "minecraft:birch_wood", + "minecraft:dark_oak_wood", + "minecraft:stripped_oak_wood", + "minecraft:stripped_birch_wood", + "minecraft:stripped_dark_oak_wood", + "minecraft:stripped_oak_log", + "minecraft:stripped_dark_oak_log", + "minecraft:mangrove_log", + "minecraft:stripped_jungle_log", + "minecraft:stripped_mangrove_wood", + "minecraft:crimson_hyphae", + "minecraft:stripped_cherry_log", + "minecraft:stripped_birch_log", + "minecraft:stripped_acacia_log", + "minecraft:crimson_stem", + "minecraft:warped_hyphae", + "minecraft:stripped_spruce_log", + "minecraft:warped_stem", + "minecraft:stripped_crimson_hyphae", + "minecraft:cherry_wood", + "minecraft:cherry_log", + "minecraft:stripped_mangrove_log" + ], + "minecraft:logs_that_burn": [ + "minecraft:mangrove_wood", + "minecraft:spruce_wood", + "minecraft:stripped_cherry_wood", + "minecraft:stripped_spruce_wood", + "minecraft:spruce_log", + "minecraft:acacia_wood", + "minecraft:stripped_acacia_wood", + "minecraft:acacia_log", + "minecraft:oak_log", + "minecraft:birch_log", + "minecraft:jungle_log", + "minecraft:dark_oak_log", + "minecraft:jungle_wood", + "minecraft:stripped_jungle_wood", + "minecraft:oak_wood", + "minecraft:birch_wood", + "minecraft:dark_oak_wood", + "minecraft:stripped_oak_wood", + "minecraft:stripped_birch_wood", + "minecraft:stripped_dark_oak_wood", + "minecraft:stripped_oak_log", + "minecraft:stripped_dark_oak_log", + "minecraft:mangrove_log", + "minecraft:stripped_jungle_log", + "minecraft:stripped_mangrove_wood", + "minecraft:stripped_cherry_log", + "minecraft:stripped_birch_log", + "minecraft:stripped_acacia_log", + "minecraft:stripped_spruce_log", + "minecraft:cherry_wood", + "minecraft:cherry_log", + "minecraft:stripped_mangrove_log" + ], + "minecraft:mangrove_logs": [ + "minecraft:mangrove_wood", + "minecraft:mangrove_log", + "minecraft:stripped_mangrove_wood", + "minecraft:stripped_mangrove_log" + ], + "minecraft:music_disc": [ + "minecraft:music_disc_ward", + "minecraft:music_disc_strad", + "minecraft:music_disc_chirp", + "minecraft:music_disc_creator_music_box", + "minecraft:music_disc_mall", + "minecraft:music_disc_pigstep", + "minecraft:music_disc_wait", + "minecraft:music_disc_11", + "minecraft:music_disc_stal", + "minecraft:music_disc_13", + "minecraft:music_disc_cat", + "minecraft:music_disc_blocks", + "minecraft:music_disc_far", + "minecraft:music_disc_mellohi", + "minecraft:music_disc_otherside", + "minecraft:music_disc_5", + "minecraft:music_disc_relic", + "minecraft:music_disc_creator", + "minecraft:music_disc_precipice" + ], + "minecraft:netherite_tier": [ + "minecraft:netherite_boots", + "minecraft:netherite_sword", + "minecraft:netherite_chestplate", + "minecraft:netherite_pickaxe", + "minecraft:netherite_leggings", + "minecraft:netherite_shovel", + "minecraft:netherite_axe", + "minecraft:netherite_hoe", + "minecraft:netherite_helmet" + ], + "minecraft:planks": [ + "minecraft:spruce_planks", + "minecraft:oak_planks", + "minecraft:mangrove_planks", + "minecraft:dark_oak_planks", + "minecraft:birch_planks", + "minecraft:jungle_planks", + "minecraft:acacia_planks", + "minecraft:bamboo_planks", + "minecraft:warped_planks", + "minecraft:crimson_planks", + "minecraft:cherry_planks" + ], + "minecraft:sand": [ + "minecraft:sand", + "minecraft:red_sand" + ], + "minecraft:sign": [ + "minecraft:bamboo_sign", + "minecraft:mangrove_hanging_sign", + "minecraft:cherry_sign", + "minecraft:oak_sign", + "minecraft:bamboo_hanging_sign", + "minecraft:warped_sign", + "minecraft:spruce_sign", + "minecraft:spruce_hanging_sign", + "minecraft:acacia_sign", + "minecraft:birch_hanging_sign", + "minecraft:birch_sign", + "minecraft:jungle_sign", + "minecraft:dark_oak_sign", + "minecraft:mangrove_sign", + "minecraft:crimson_sign", + "minecraft:cherry_hanging_sign", + "minecraft:oak_hanging_sign", + "minecraft:jungle_hanging_sign", + "minecraft:acacia_hanging_sign", + "minecraft:dark_oak_hanging_sign", + "minecraft:crimson_hanging_sign", + "minecraft:warped_hanging_sign" + ], + "minecraft:soul_fire_base_blocks": [ + "minecraft:soul_soil", + "minecraft:soul_sand" + ], + "minecraft:spawn_egg": [ + "minecraft:enderman_spawn_egg", + "minecraft:strider_spawn_egg", + "minecraft:ravager_spawn_egg", + "minecraft:slime_spawn_egg", + "minecraft:glow_squid_spawn_egg", + "minecraft:pillager_spawn_egg", + "minecraft:blaze_spawn_egg", + "minecraft:witch_spawn_egg", + "minecraft:horse_spawn_egg", + "minecraft:polar_bear_spawn_egg", + "minecraft:zoglin_spawn_egg", + "minecraft:stray_spawn_egg", + "minecraft:zombie_pigman_spawn_egg", + "minecraft:snow_golem_spawn_egg", + "minecraft:panda_spawn_egg", + "minecraft:axolotl_spawn_egg", + "minecraft:agent_spawn_egg", + "minecraft:shulker_spawn_egg", + "minecraft:vindicator_spawn_egg", + "minecraft:evoker_spawn_egg", + "minecraft:parrot_spawn_egg", + "minecraft:wolf_spawn_egg", + "minecraft:sheep_spawn_egg", + "minecraft:elder_guardian_spawn_egg", + "minecraft:silverfish_spawn_egg", + "minecraft:tadpole_spawn_egg", + "minecraft:cow_spawn_egg", + "minecraft:bogged_spawn_egg", + "minecraft:ocelot_spawn_egg", + "minecraft:bee_spawn_egg", + "minecraft:piglin_brute_spawn_egg", + "minecraft:rabbit_spawn_egg", + "minecraft:camel_spawn_egg", + "minecraft:creeper_spawn_egg", + "minecraft:drowned_spawn_egg", + "minecraft:chicken_spawn_egg", + "minecraft:pig_spawn_egg", + "minecraft:mooshroom_spawn_egg", + "minecraft:skeleton_spawn_egg", + "minecraft:spider_spawn_egg", + "minecraft:zombie_spawn_egg", + "minecraft:villager_spawn_egg", + "minecraft:squid_spawn_egg", + "minecraft:bat_spawn_egg", + "minecraft:ghast_spawn_egg", + "minecraft:magma_cube_spawn_egg", + "minecraft:cave_spider_spawn_egg", + "minecraft:endermite_spawn_egg", + "minecraft:guardian_spawn_egg", + "minecraft:husk_spawn_egg", + "minecraft:piglin_spawn_egg", + "minecraft:wither_skeleton_spawn_egg", + "minecraft:donkey_spawn_egg", + "minecraft:mule_spawn_egg", + "minecraft:skeleton_horse_spawn_egg", + "minecraft:zombie_horse_spawn_egg", + "minecraft:npc_spawn_egg", + "minecraft:breeze_spawn_egg", + "minecraft:llama_spawn_egg", + "minecraft:vex_spawn_egg", + "minecraft:warden_spawn_egg", + "minecraft:zombie_villager_spawn_egg", + "minecraft:tropical_fish_spawn_egg", + "minecraft:cod_spawn_egg", + "minecraft:pufferfish_spawn_egg", + "minecraft:salmon_spawn_egg", + "minecraft:dolphin_spawn_egg", + "minecraft:turtle_spawn_egg", + "minecraft:phantom_spawn_egg", + "minecraft:cat_spawn_egg", + "minecraft:fox_spawn_egg", + "minecraft:wandering_trader_spawn_egg", + "minecraft:hoglin_spawn_egg", + "minecraft:sniffer_spawn_egg", + "minecraft:goat_spawn_egg", + "minecraft:iron_golem_spawn_egg", + "minecraft:ender_dragon_spawn_egg", + "minecraft:wither_spawn_egg", + "minecraft:frog_spawn_egg", + "minecraft:allay_spawn_egg", + "minecraft:trader_llama_spawn_egg", + "minecraft:armadillo_spawn_egg", + "minecraft:spawn_egg" + ], + "minecraft:stone_bricks": [ + "minecraft:mossy_stone_bricks", + "minecraft:stone_bricks", + "minecraft:cracked_stone_bricks", + "minecraft:chiseled_stone_bricks" + ], + "minecraft:stone_crafting_materials": [ + "minecraft:cobblestone", + "minecraft:cobbled_deepslate", + "minecraft:blackstone" + ], + "minecraft:stone_tier": [ + "minecraft:stone_sword", + "minecraft:stone_shovel", + "minecraft:stone_pickaxe", + "minecraft:stone_axe", + "minecraft:stone_hoe" + ], + "minecraft:stone_tool_materials": [ + "minecraft:cobblestone", + "minecraft:cobbled_deepslate", + "minecraft:blackstone" + ], + "minecraft:transform_materials": [ + "minecraft:netherite_ingot" + ], + "minecraft:transform_templates": [ + "minecraft:netherite_upgrade_smithing_template" + ], + "minecraft:transformable_items": [ + "minecraft:diamond_sword", + "minecraft:diamond_shovel", + "minecraft:diamond_pickaxe", + "minecraft:diamond_axe", + "minecraft:diamond_hoe", + "minecraft:diamond_helmet", + "minecraft:diamond_chestplate", + "minecraft:diamond_leggings", + "minecraft:diamond_boots", + "minecraft:golden_boots" + ], + "minecraft:trim_materials": [ + "minecraft:gold_ingot", + "minecraft:iron_ingot", + "minecraft:diamond", + "minecraft:redstone", + "minecraft:netherite_ingot", + "minecraft:lapis_lazuli", + "minecraft:quartz", + "minecraft:copper_ingot", + "minecraft:emerald", + "minecraft:amethyst_shard" + ], + "minecraft:trim_templates": [ + "minecraft:vex_armor_trim_smithing_template", + "minecraft:wild_armor_trim_smithing_template", + "minecraft:flow_armor_trim_smithing_template", + "minecraft:raiser_armor_trim_smithing_template", + "minecraft:silence_armor_trim_smithing_template", + "minecraft:coast_armor_trim_smithing_template", + "minecraft:snout_armor_trim_smithing_template", + "minecraft:dune_armor_trim_smithing_template", + "minecraft:rib_armor_trim_smithing_template", + "minecraft:host_armor_trim_smithing_template", + "minecraft:bolt_armor_trim_smithing_template", + "minecraft:shaper_armor_trim_smithing_template", + "minecraft:eye_armor_trim_smithing_template", + "minecraft:spire_armor_trim_smithing_template", + "minecraft:sentry_armor_trim_smithing_template", + "minecraft:ward_armor_trim_smithing_template", + "minecraft:tide_armor_trim_smithing_template", + "minecraft:wayfinder_armor_trim_smithing_template" + ], + "minecraft:trimmable_armors": [ + "minecraft:netherite_boots", + "minecraft:chainmail_helmet", + "minecraft:leather_helmet", + "minecraft:leather_chestplate", + "minecraft:leather_leggings", + "minecraft:leather_boots", + "minecraft:chainmail_chestplate", + "minecraft:chainmail_leggings", + "minecraft:chainmail_boots", + "minecraft:iron_helmet", + "minecraft:iron_chestplate", + "minecraft:iron_leggings", + "minecraft:iron_boots", + "minecraft:diamond_helmet", + "minecraft:diamond_chestplate", + "minecraft:diamond_leggings", + "minecraft:diamond_boots", + "minecraft:golden_helmet", + "minecraft:golden_chestplate", + "minecraft:golden_leggings", + "minecraft:golden_boots", + "minecraft:turtle_helmet", + "minecraft:netherite_chestplate", + "minecraft:netherite_leggings", + "minecraft:netherite_helmet" + ], + "minecraft:vibration_damper": [ + "minecraft:black_carpet", + "minecraft:blue_carpet", + "minecraft:pink_carpet", + "minecraft:lime_carpet", + "minecraft:green_wool", + "minecraft:cyan_carpet", + "minecraft:orange_wool", + "minecraft:white_carpet", + "minecraft:purple_carpet", + "minecraft:yellow_carpet", + "minecraft:light_gray_wool", + "minecraft:magenta_carpet", + "minecraft:purple_wool", + "minecraft:gray_wool", + "minecraft:red_carpet", + "minecraft:brown_carpet", + "minecraft:light_blue_wool", + "minecraft:white_wool", + "minecraft:magenta_wool", + "minecraft:yellow_wool", + "minecraft:lime_wool", + "minecraft:pink_wool", + "minecraft:cyan_wool", + "minecraft:blue_wool", + "minecraft:brown_wool", + "minecraft:red_wool", + "minecraft:black_wool", + "minecraft:orange_carpet", + "minecraft:light_blue_carpet", + "minecraft:gray_carpet", + "minecraft:light_gray_carpet", + "minecraft:green_carpet" + ], + "minecraft:warped_stems": [ + "minecraft:stripped_warped_hyphae", + "minecraft:stripped_warped_stem", + "minecraft:warped_hyphae", + "minecraft:warped_stem" + ], + "minecraft:wooden_slabs": [ + "minecraft:birch_slab", + "minecraft:oak_slab", + "minecraft:spruce_slab", + "minecraft:bamboo_slab", + "minecraft:warped_slab", + "minecraft:mangrove_slab", + "minecraft:cherry_slab", + "minecraft:jungle_slab", + "minecraft:acacia_slab", + "minecraft:dark_oak_slab", + "minecraft:crimson_slab" + ], + "minecraft:wooden_tier": [ + "minecraft:wooden_pickaxe", + "minecraft:wooden_shovel", + "minecraft:wooden_sword", + "minecraft:wooden_axe", + "minecraft:wooden_hoe" + ], + "minecraft:wool": [ + "minecraft:green_wool", + "minecraft:orange_wool", + "minecraft:light_gray_wool", + "minecraft:purple_wool", + "minecraft:gray_wool", + "minecraft:light_blue_wool", + "minecraft:white_wool", + "minecraft:magenta_wool", + "minecraft:yellow_wool", + "minecraft:lime_wool", + "minecraft:pink_wool", + "minecraft:cyan_wool", + "minecraft:blue_wool", + "minecraft:brown_wool", + "minecraft:red_wool", + "minecraft:black_wool" + ] +} \ No newline at end of file diff --git a/core/src/main/resources/bedrock/item_tags.1_21_40.json b/core/src/main/resources/bedrock/item_tags.1_21_40.json new file mode 100644 index 000000000..d9e63ed26 --- /dev/null +++ b/core/src/main/resources/bedrock/item_tags.1_21_40.json @@ -0,0 +1,806 @@ +{ + "minecraft:arrow": [ + "minecraft:arrow" + ], + "minecraft:banner": [ + "minecraft:banner" + ], + "minecraft:boat": [ + "minecraft:cherry_chest_boat", + "minecraft:oak_chest_boat", + "minecraft:mangrove_boat", + "minecraft:oak_boat", + "minecraft:birch_boat", + "minecraft:jungle_boat", + "minecraft:spruce_boat", + "minecraft:acacia_boat", + "minecraft:dark_oak_boat", + "minecraft:birch_chest_boat", + "minecraft:jungle_chest_boat", + "minecraft:spruce_chest_boat", + "minecraft:acacia_chest_boat", + "minecraft:dark_oak_chest_boat", + "minecraft:mangrove_chest_boat", + "minecraft:cherry_boat", + "minecraft:bamboo_raft", + "minecraft:bamboo_chest_raft" + ], + "minecraft:boats": [ + "minecraft:cherry_chest_boat", + "minecraft:oak_chest_boat", + "minecraft:mangrove_boat", + "minecraft:oak_boat", + "minecraft:birch_boat", + "minecraft:jungle_boat", + "minecraft:spruce_boat", + "minecraft:acacia_boat", + "minecraft:dark_oak_boat", + "minecraft:birch_chest_boat", + "minecraft:jungle_chest_boat", + "minecraft:spruce_chest_boat", + "minecraft:acacia_chest_boat", + "minecraft:dark_oak_chest_boat", + "minecraft:mangrove_chest_boat", + "minecraft:cherry_boat", + "minecraft:bamboo_raft", + "minecraft:bamboo_chest_raft" + ], + "minecraft:bookshelf_books": [ + "minecraft:written_book", + "minecraft:book", + "minecraft:writable_book", + "minecraft:enchanted_book" + ], + "minecraft:chainmail_tier": [ + "minecraft:chainmail_helmet", + "minecraft:chainmail_chestplate", + "minecraft:chainmail_leggings", + "minecraft:chainmail_boots" + ], + "minecraft:coals": [ + "minecraft:coal", + "minecraft:charcoal" + ], + "minecraft:crimson_stems": [ + "minecraft:stripped_crimson_stem", + "minecraft:crimson_hyphae", + "minecraft:crimson_stem", + "minecraft:stripped_crimson_hyphae" + ], + "minecraft:decorated_pot_sherds": [ + "minecraft:skull_pottery_sherd", + "minecraft:mourner_pottery_sherd", + "minecraft:angler_pottery_sherd", + "minecraft:prize_pottery_sherd", + "minecraft:arms_up_pottery_sherd", + "minecraft:burn_pottery_sherd", + "minecraft:snort_pottery_sherd", + "minecraft:brick", + "minecraft:heartbreak_pottery_sherd", + "minecraft:miner_pottery_sherd", + "minecraft:brewer_pottery_sherd", + "minecraft:plenty_pottery_sherd", + "minecraft:scrape_pottery_sherd", + "minecraft:howl_pottery_sherd", + "minecraft:explorer_pottery_sherd", + "minecraft:archer_pottery_sherd", + "minecraft:blade_pottery_sherd", + "minecraft:danger_pottery_sherd", + "minecraft:flow_pottery_sherd", + "minecraft:friend_pottery_sherd", + "minecraft:guster_pottery_sherd", + "minecraft:heart_pottery_sherd", + "minecraft:sheaf_pottery_sherd", + "minecraft:shelter_pottery_sherd" + ], + "minecraft:diamond_tier": [ + "minecraft:diamond_sword", + "minecraft:diamond_shovel", + "minecraft:diamond_pickaxe", + "minecraft:diamond_axe", + "minecraft:mace", + "minecraft:diamond_hoe", + "minecraft:diamond_helmet", + "minecraft:diamond_chestplate", + "minecraft:diamond_leggings", + "minecraft:diamond_boots" + ], + "minecraft:digger": [ + "minecraft:iron_shovel", + "minecraft:wooden_pickaxe", + "minecraft:iron_pickaxe", + "minecraft:iron_axe", + "minecraft:wooden_shovel", + "minecraft:wooden_axe", + "minecraft:stone_shovel", + "minecraft:stone_pickaxe", + "minecraft:stone_axe", + "minecraft:diamond_shovel", + "minecraft:diamond_pickaxe", + "minecraft:diamond_axe", + "minecraft:golden_shovel", + "minecraft:golden_pickaxe", + "minecraft:golden_axe", + "minecraft:wooden_hoe", + "minecraft:stone_hoe", + "minecraft:iron_hoe", + "minecraft:diamond_hoe", + "minecraft:golden_hoe", + "minecraft:netherite_pickaxe", + "minecraft:netherite_shovel", + "minecraft:netherite_axe", + "minecraft:netherite_hoe" + ], + "minecraft:door": [ + "minecraft:weathered_copper_door", + "minecraft:warped_door", + "minecraft:wooden_door", + "minecraft:jungle_door", + "minecraft:waxed_weathered_copper_door", + "minecraft:iron_door", + "minecraft:mangrove_door", + "minecraft:exposed_copper_door", + "minecraft:bamboo_door", + "minecraft:spruce_door", + "minecraft:birch_door", + "minecraft:acacia_door", + "minecraft:dark_oak_door", + "minecraft:crimson_door", + "minecraft:cherry_door", + "minecraft:copper_door", + "minecraft:oxidized_copper_door", + "minecraft:waxed_copper_door", + "minecraft:waxed_exposed_copper_door", + "minecraft:waxed_oxidized_copper_door" + ], + "minecraft:golden_tier": [ + "minecraft:golden_sword", + "minecraft:golden_shovel", + "minecraft:golden_pickaxe", + "minecraft:golden_axe", + "minecraft:golden_hoe", + "minecraft:golden_helmet", + "minecraft:golden_chestplate", + "minecraft:golden_leggings", + "minecraft:golden_boots" + ], + "minecraft:hanging_actor": [ + "minecraft:painting" + ], + "minecraft:hanging_sign": [ + "minecraft:mangrove_hanging_sign", + "minecraft:bamboo_hanging_sign", + "minecraft:spruce_hanging_sign", + "minecraft:birch_hanging_sign", + "minecraft:cherry_hanging_sign", + "minecraft:oak_hanging_sign", + "minecraft:jungle_hanging_sign", + "minecraft:acacia_hanging_sign", + "minecraft:dark_oak_hanging_sign", + "minecraft:crimson_hanging_sign", + "minecraft:warped_hanging_sign" + ], + "minecraft:horse_armor": [ + "minecraft:iron_horse_armor", + "minecraft:golden_horse_armor", + "minecraft:leather_horse_armor", + "minecraft:diamond_horse_armor" + ], + "minecraft:iron_tier": [ + "minecraft:iron_chestplate", + "minecraft:iron_shovel", + "minecraft:iron_sword", + "minecraft:iron_pickaxe", + "minecraft:iron_axe", + "minecraft:iron_hoe", + "minecraft:iron_helmet", + "minecraft:iron_leggings", + "minecraft:iron_boots" + ], + "minecraft:is_armor": [ + "minecraft:iron_chestplate", + "minecraft:netherite_boots", + "minecraft:chainmail_helmet", + "minecraft:elytra", + "minecraft:leather_helmet", + "minecraft:leather_chestplate", + "minecraft:leather_leggings", + "minecraft:leather_boots", + "minecraft:chainmail_chestplate", + "minecraft:chainmail_leggings", + "minecraft:chainmail_boots", + "minecraft:iron_helmet", + "minecraft:iron_leggings", + "minecraft:iron_boots", + "minecraft:diamond_helmet", + "minecraft:diamond_chestplate", + "minecraft:diamond_leggings", + "minecraft:diamond_boots", + "minecraft:golden_helmet", + "minecraft:golden_chestplate", + "minecraft:golden_leggings", + "minecraft:golden_boots", + "minecraft:turtle_helmet", + "minecraft:netherite_chestplate", + "minecraft:netherite_leggings", + "minecraft:netherite_helmet" + ], + "minecraft:is_axe": [ + "minecraft:iron_axe", + "minecraft:wooden_axe", + "minecraft:stone_axe", + "minecraft:diamond_axe", + "minecraft:golden_axe", + "minecraft:netherite_axe" + ], + "minecraft:is_cooked": [ + "minecraft:cooked_cod", + "minecraft:cooked_porkchop", + "minecraft:cooked_salmon", + "minecraft:cooked_rabbit", + "minecraft:cooked_beef", + "minecraft:cooked_chicken", + "minecraft:rabbit_stew", + "minecraft:cooked_mutton" + ], + "minecraft:is_fish": [ + "minecraft:cooked_cod", + "minecraft:cooked_salmon", + "minecraft:cod", + "minecraft:salmon", + "minecraft:tropical_fish", + "minecraft:pufferfish" + ], + "minecraft:is_food": [ + "minecraft:rabbit", + "minecraft:cooked_porkchop", + "minecraft:mushroom_stew", + "minecraft:potato", + "minecraft:enchanted_golden_apple", + "minecraft:golden_carrot", + "minecraft:carrot", + "minecraft:chicken", + "minecraft:beetroot", + "minecraft:sweet_berries", + "minecraft:apple", + "minecraft:golden_apple", + "minecraft:bread", + "minecraft:porkchop", + "minecraft:cookie", + "minecraft:cooked_rabbit", + "minecraft:beef", + "minecraft:dried_kelp", + "minecraft:beetroot_soup", + "minecraft:melon_slice", + "minecraft:cooked_beef", + "minecraft:rotten_flesh", + "minecraft:cooked_chicken", + "minecraft:baked_potato", + "minecraft:pumpkin_pie", + "minecraft:rabbit_stew", + "minecraft:cooked_mutton", + "minecraft:mutton" + ], + "minecraft:is_hoe": [ + "minecraft:wooden_hoe", + "minecraft:stone_hoe", + "minecraft:iron_hoe", + "minecraft:diamond_hoe", + "minecraft:golden_hoe", + "minecraft:netherite_hoe" + ], + "minecraft:is_meat": [ + "minecraft:rabbit", + "minecraft:cooked_porkchop", + "minecraft:chicken", + "minecraft:porkchop", + "minecraft:cooked_rabbit", + "minecraft:beef", + "minecraft:cooked_beef", + "minecraft:rotten_flesh", + "minecraft:cooked_chicken", + "minecraft:rabbit_stew", + "minecraft:cooked_mutton", + "minecraft:mutton" + ], + "minecraft:is_minecart": [ + "minecraft:command_block_minecart", + "minecraft:minecart", + "minecraft:chest_minecart", + "minecraft:tnt_minecart", + "minecraft:hopper_minecart" + ], + "minecraft:is_pickaxe": [ + "minecraft:wooden_pickaxe", + "minecraft:iron_pickaxe", + "minecraft:stone_pickaxe", + "minecraft:diamond_pickaxe", + "minecraft:golden_pickaxe", + "minecraft:netherite_pickaxe" + ], + "minecraft:is_shovel": [ + "minecraft:iron_shovel", + "minecraft:wooden_shovel", + "minecraft:stone_shovel", + "minecraft:diamond_shovel", + "minecraft:golden_shovel", + "minecraft:netherite_shovel" + ], + "minecraft:is_sword": [ + "minecraft:netherite_sword", + "minecraft:iron_sword", + "minecraft:wooden_sword", + "minecraft:stone_sword", + "minecraft:diamond_sword", + "minecraft:mace", + "minecraft:golden_sword" + ], + "minecraft:is_tool": [ + "minecraft:netherite_sword", + "minecraft:iron_shovel", + "minecraft:iron_sword", + "minecraft:wooden_pickaxe", + "minecraft:iron_pickaxe", + "minecraft:iron_axe", + "minecraft:wooden_sword", + "minecraft:wooden_shovel", + "minecraft:wooden_axe", + "minecraft:stone_sword", + "minecraft:stone_shovel", + "minecraft:stone_pickaxe", + "minecraft:stone_axe", + "minecraft:diamond_sword", + "minecraft:diamond_shovel", + "minecraft:diamond_pickaxe", + "minecraft:diamond_axe", + "minecraft:mace", + "minecraft:golden_sword", + "minecraft:golden_shovel", + "minecraft:golden_pickaxe", + "minecraft:golden_axe", + "minecraft:wooden_hoe", + "minecraft:stone_hoe", + "minecraft:iron_hoe", + "minecraft:diamond_hoe", + "minecraft:golden_hoe", + "minecraft:netherite_pickaxe", + "minecraft:netherite_shovel", + "minecraft:netherite_axe", + "minecraft:netherite_hoe" + ], + "minecraft:is_trident": [ + "minecraft:trident" + ], + "minecraft:leather_tier": [ + "minecraft:leather_helmet", + "minecraft:leather_chestplate", + "minecraft:leather_leggings", + "minecraft:leather_boots" + ], + "minecraft:lectern_books": [ + "minecraft:written_book", + "minecraft:writable_book" + ], + "minecraft:logs": [ + "minecraft:mangrove_wood", + "minecraft:spruce_wood", + "minecraft:stripped_cherry_wood", + "minecraft:stripped_crimson_stem", + "minecraft:stripped_spruce_wood", + "minecraft:spruce_log", + "minecraft:acacia_wood", + "minecraft:stripped_acacia_wood", + "minecraft:stripped_warped_hyphae", + "minecraft:acacia_log", + "minecraft:stripped_warped_stem", + "minecraft:oak_log", + "minecraft:birch_log", + "minecraft:jungle_log", + "minecraft:dark_oak_log", + "minecraft:jungle_wood", + "minecraft:stripped_jungle_wood", + "minecraft:oak_wood", + "minecraft:birch_wood", + "minecraft:dark_oak_wood", + "minecraft:stripped_oak_wood", + "minecraft:stripped_birch_wood", + "minecraft:stripped_dark_oak_wood", + "minecraft:stripped_oak_log", + "minecraft:stripped_dark_oak_log", + "minecraft:mangrove_log", + "minecraft:stripped_jungle_log", + "minecraft:stripped_mangrove_wood", + "minecraft:crimson_hyphae", + "minecraft:stripped_cherry_log", + "minecraft:stripped_birch_log", + "minecraft:stripped_acacia_log", + "minecraft:crimson_stem", + "minecraft:warped_hyphae", + "minecraft:stripped_spruce_log", + "minecraft:warped_stem", + "minecraft:stripped_crimson_hyphae", + "minecraft:cherry_wood", + "minecraft:cherry_log", + "minecraft:stripped_mangrove_log" + ], + "minecraft:logs_that_burn": [ + "minecraft:mangrove_wood", + "minecraft:spruce_wood", + "minecraft:stripped_cherry_wood", + "minecraft:stripped_spruce_wood", + "minecraft:spruce_log", + "minecraft:acacia_wood", + "minecraft:stripped_acacia_wood", + "minecraft:acacia_log", + "minecraft:oak_log", + "minecraft:birch_log", + "minecraft:jungle_log", + "minecraft:dark_oak_log", + "minecraft:jungle_wood", + "minecraft:stripped_jungle_wood", + "minecraft:oak_wood", + "minecraft:birch_wood", + "minecraft:dark_oak_wood", + "minecraft:stripped_oak_wood", + "minecraft:stripped_birch_wood", + "minecraft:stripped_dark_oak_wood", + "minecraft:stripped_oak_log", + "minecraft:stripped_dark_oak_log", + "minecraft:mangrove_log", + "minecraft:stripped_jungle_log", + "minecraft:stripped_mangrove_wood", + "minecraft:stripped_cherry_log", + "minecraft:stripped_birch_log", + "minecraft:stripped_acacia_log", + "minecraft:stripped_spruce_log", + "minecraft:cherry_wood", + "minecraft:cherry_log", + "minecraft:stripped_mangrove_log" + ], + "minecraft:mangrove_logs": [ + "minecraft:mangrove_wood", + "minecraft:mangrove_log", + "minecraft:stripped_mangrove_wood", + "minecraft:stripped_mangrove_log" + ], + "minecraft:music_disc": [ + "minecraft:music_disc_ward", + "minecraft:music_disc_stal", + "minecraft:music_disc_wait", + "minecraft:music_disc_cat", + "minecraft:music_disc_strad", + "minecraft:music_disc_chirp", + "minecraft:music_disc_mall", + "minecraft:music_disc_creator_music_box", + "minecraft:music_disc_pigstep", + "minecraft:music_disc_11", + "minecraft:music_disc_13", + "minecraft:music_disc_blocks", + "minecraft:music_disc_far", + "minecraft:music_disc_mellohi", + "minecraft:music_disc_otherside", + "minecraft:music_disc_5", + "minecraft:music_disc_relic", + "minecraft:music_disc_creator", + "minecraft:music_disc_precipice" + ], + "minecraft:netherite_tier": [ + "minecraft:netherite_sword", + "minecraft:netherite_boots", + "minecraft:netherite_chestplate", + "minecraft:netherite_pickaxe", + "minecraft:netherite_leggings", + "minecraft:netherite_shovel", + "minecraft:netherite_axe", + "minecraft:netherite_hoe", + "minecraft:netherite_helmet" + ], + "minecraft:planks": [ + "minecraft:spruce_planks", + "minecraft:oak_planks", + "minecraft:mangrove_planks", + "minecraft:dark_oak_planks", + "minecraft:birch_planks", + "minecraft:jungle_planks", + "minecraft:acacia_planks", + "minecraft:bamboo_planks", + "minecraft:warped_planks", + "minecraft:crimson_planks", + "minecraft:cherry_planks" + ], + "minecraft:sand": [ + "minecraft:sand", + "minecraft:red_sand" + ], + "minecraft:sign": [ + "minecraft:jungle_sign", + "minecraft:bamboo_sign", + "minecraft:mangrove_hanging_sign", + "minecraft:cherry_sign", + "minecraft:oak_sign", + "minecraft:bamboo_hanging_sign", + "minecraft:warped_sign", + "minecraft:spruce_sign", + "minecraft:spruce_hanging_sign", + "minecraft:acacia_sign", + "minecraft:birch_hanging_sign", + "minecraft:birch_sign", + "minecraft:dark_oak_sign", + "minecraft:mangrove_sign", + "minecraft:crimson_sign", + "minecraft:cherry_hanging_sign", + "minecraft:oak_hanging_sign", + "minecraft:jungle_hanging_sign", + "minecraft:acacia_hanging_sign", + "minecraft:dark_oak_hanging_sign", + "minecraft:crimson_hanging_sign", + "minecraft:warped_hanging_sign" + ], + "minecraft:soul_fire_base_blocks": [ + "minecraft:soul_soil", + "minecraft:soul_sand" + ], + "minecraft:spawn_egg": [ + "minecraft:enderman_spawn_egg", + "minecraft:endermite_spawn_egg", + "minecraft:tropical_fish_spawn_egg", + "minecraft:ravager_spawn_egg", + "minecraft:pufferfish_spawn_egg", + "minecraft:zoglin_spawn_egg", + "minecraft:strider_spawn_egg", + "minecraft:slime_spawn_egg", + "minecraft:glow_squid_spawn_egg", + "minecraft:blaze_spawn_egg", + "minecraft:witch_spawn_egg", + "minecraft:horse_spawn_egg", + "minecraft:polar_bear_spawn_egg", + "minecraft:pillager_spawn_egg", + "minecraft:stray_spawn_egg", + "minecraft:zombie_pigman_spawn_egg", + "minecraft:snow_golem_spawn_egg", + "minecraft:panda_spawn_egg", + "minecraft:axolotl_spawn_egg", + "minecraft:agent_spawn_egg", + "minecraft:shulker_spawn_egg", + "minecraft:vindicator_spawn_egg", + "minecraft:evoker_spawn_egg", + "minecraft:parrot_spawn_egg", + "minecraft:wolf_spawn_egg", + "minecraft:sheep_spawn_egg", + "minecraft:elder_guardian_spawn_egg", + "minecraft:silverfish_spawn_egg", + "minecraft:tadpole_spawn_egg", + "minecraft:cow_spawn_egg", + "minecraft:bogged_spawn_egg", + "minecraft:ocelot_spawn_egg", + "minecraft:bee_spawn_egg", + "minecraft:piglin_brute_spawn_egg", + "minecraft:rabbit_spawn_egg", + "minecraft:camel_spawn_egg", + "minecraft:creeper_spawn_egg", + "minecraft:drowned_spawn_egg", + "minecraft:chicken_spawn_egg", + "minecraft:pig_spawn_egg", + "minecraft:mooshroom_spawn_egg", + "minecraft:skeleton_spawn_egg", + "minecraft:spider_spawn_egg", + "minecraft:zombie_spawn_egg", + "minecraft:villager_spawn_egg", + "minecraft:squid_spawn_egg", + "minecraft:bat_spawn_egg", + "minecraft:ghast_spawn_egg", + "minecraft:magma_cube_spawn_egg", + "minecraft:cave_spider_spawn_egg", + "minecraft:guardian_spawn_egg", + "minecraft:husk_spawn_egg", + "minecraft:piglin_spawn_egg", + "minecraft:wither_skeleton_spawn_egg", + "minecraft:donkey_spawn_egg", + "minecraft:mule_spawn_egg", + "minecraft:skeleton_horse_spawn_egg", + "minecraft:zombie_horse_spawn_egg", + "minecraft:npc_spawn_egg", + "minecraft:breeze_spawn_egg", + "minecraft:llama_spawn_egg", + "minecraft:vex_spawn_egg", + "minecraft:warden_spawn_egg", + "minecraft:zombie_villager_spawn_egg", + "minecraft:cod_spawn_egg", + "minecraft:salmon_spawn_egg", + "minecraft:dolphin_spawn_egg", + "minecraft:turtle_spawn_egg", + "minecraft:phantom_spawn_egg", + "minecraft:cat_spawn_egg", + "minecraft:fox_spawn_egg", + "minecraft:wandering_trader_spawn_egg", + "minecraft:hoglin_spawn_egg", + "minecraft:sniffer_spawn_egg", + "minecraft:goat_spawn_egg", + "minecraft:iron_golem_spawn_egg", + "minecraft:ender_dragon_spawn_egg", + "minecraft:wither_spawn_egg", + "minecraft:frog_spawn_egg", + "minecraft:allay_spawn_egg", + "minecraft:trader_llama_spawn_egg", + "minecraft:armadillo_spawn_egg", + "minecraft:spawn_egg" + ], + "minecraft:stone_bricks": [ + "minecraft:mossy_stone_bricks", + "minecraft:stone_bricks", + "minecraft:cracked_stone_bricks", + "minecraft:chiseled_stone_bricks" + ], + "minecraft:stone_crafting_materials": [ + "minecraft:cobblestone", + "minecraft:cobbled_deepslate", + "minecraft:blackstone" + ], + "minecraft:stone_tier": [ + "minecraft:stone_sword", + "minecraft:stone_shovel", + "minecraft:stone_pickaxe", + "minecraft:stone_axe", + "minecraft:stone_hoe" + ], + "minecraft:stone_tool_materials": [ + "minecraft:cobblestone", + "minecraft:cobbled_deepslate", + "minecraft:blackstone" + ], + "minecraft:transform_materials": [ + "minecraft:netherite_ingot" + ], + "minecraft:transform_templates": [ + "minecraft:netherite_upgrade_smithing_template" + ], + "minecraft:transformable_items": [ + "minecraft:diamond_sword", + "minecraft:diamond_shovel", + "minecraft:diamond_pickaxe", + "minecraft:diamond_axe", + "minecraft:diamond_hoe", + "minecraft:diamond_helmet", + "minecraft:diamond_chestplate", + "minecraft:diamond_leggings", + "minecraft:diamond_boots", + "minecraft:golden_boots" + ], + "minecraft:trim_materials": [ + "minecraft:diamond", + "minecraft:iron_ingot", + "minecraft:gold_ingot", + "minecraft:redstone", + "minecraft:netherite_ingot", + "minecraft:lapis_lazuli", + "minecraft:quartz", + "minecraft:copper_ingot", + "minecraft:emerald", + "minecraft:amethyst_shard" + ], + "minecraft:trim_templates": [ + "minecraft:coast_armor_trim_smithing_template", + "minecraft:vex_armor_trim_smithing_template", + "minecraft:wild_armor_trim_smithing_template", + "minecraft:flow_armor_trim_smithing_template", + "minecraft:silence_armor_trim_smithing_template", + "minecraft:snout_armor_trim_smithing_template", + "minecraft:dune_armor_trim_smithing_template", + "minecraft:rib_armor_trim_smithing_template", + "minecraft:host_armor_trim_smithing_template", + "minecraft:eye_armor_trim_smithing_template", + "minecraft:spire_armor_trim_smithing_template", + "minecraft:sentry_armor_trim_smithing_template", + "minecraft:ward_armor_trim_smithing_template", + "minecraft:tide_armor_trim_smithing_template", + "minecraft:wayfinder_armor_trim_smithing_template", + "minecraft:raiser_armor_trim_smithing_template", + "minecraft:shaper_armor_trim_smithing_template", + "minecraft:bolt_armor_trim_smithing_template" + ], + "minecraft:trimmable_armors": [ + "minecraft:iron_chestplate", + "minecraft:netherite_boots", + "minecraft:chainmail_helmet", + "minecraft:leather_helmet", + "minecraft:leather_chestplate", + "minecraft:leather_leggings", + "minecraft:leather_boots", + "minecraft:chainmail_chestplate", + "minecraft:chainmail_leggings", + "minecraft:chainmail_boots", + "minecraft:iron_helmet", + "minecraft:iron_leggings", + "minecraft:iron_boots", + "minecraft:diamond_helmet", + "minecraft:diamond_chestplate", + "minecraft:diamond_leggings", + "minecraft:diamond_boots", + "minecraft:golden_helmet", + "minecraft:golden_chestplate", + "minecraft:golden_leggings", + "minecraft:golden_boots", + "minecraft:turtle_helmet", + "minecraft:netherite_chestplate", + "minecraft:netherite_leggings", + "minecraft:netherite_helmet" + ], + "minecraft:vibration_damper": [ + "minecraft:black_carpet", + "minecraft:pink_carpet", + "minecraft:lime_carpet", + "minecraft:blue_carpet", + "minecraft:green_wool", + "minecraft:cyan_carpet", + "minecraft:orange_wool", + "minecraft:white_carpet", + "minecraft:purple_carpet", + "minecraft:yellow_carpet", + "minecraft:light_gray_wool", + "minecraft:magenta_carpet", + "minecraft:purple_wool", + "minecraft:gray_wool", + "minecraft:red_carpet", + "minecraft:brown_carpet", + "minecraft:light_blue_wool", + "minecraft:white_wool", + "minecraft:magenta_wool", + "minecraft:yellow_wool", + "minecraft:lime_wool", + "minecraft:pink_wool", + "minecraft:cyan_wool", + "minecraft:blue_wool", + "minecraft:brown_wool", + "minecraft:red_wool", + "minecraft:black_wool", + "minecraft:orange_carpet", + "minecraft:light_blue_carpet", + "minecraft:gray_carpet", + "minecraft:light_gray_carpet", + "minecraft:green_carpet" + ], + "minecraft:warped_stems": [ + "minecraft:stripped_warped_hyphae", + "minecraft:stripped_warped_stem", + "minecraft:warped_hyphae", + "minecraft:warped_stem" + ], + "minecraft:wooden_slabs": [ + "minecraft:birch_slab", + "minecraft:oak_slab", + "minecraft:spruce_slab", + "minecraft:bamboo_slab", + "minecraft:warped_slab", + "minecraft:mangrove_slab", + "minecraft:cherry_slab", + "minecraft:jungle_slab", + "minecraft:acacia_slab", + "minecraft:dark_oak_slab", + "minecraft:crimson_slab" + ], + "minecraft:wooden_tier": [ + "minecraft:wooden_pickaxe", + "minecraft:wooden_sword", + "minecraft:wooden_shovel", + "minecraft:wooden_axe", + "minecraft:wooden_hoe" + ], + "minecraft:wool": [ + "minecraft:green_wool", + "minecraft:orange_wool", + "minecraft:light_gray_wool", + "minecraft:purple_wool", + "minecraft:gray_wool", + "minecraft:light_blue_wool", + "minecraft:white_wool", + "minecraft:magenta_wool", + "minecraft:yellow_wool", + "minecraft:lime_wool", + "minecraft:pink_wool", + "minecraft:cyan_wool", + "minecraft:blue_wool", + "minecraft:brown_wool", + "minecraft:red_wool", + "minecraft:black_wool" + ] +} diff --git a/core/src/main/resources/bedrock/runtime_item_states.1_21_40.json b/core/src/main/resources/bedrock/runtime_item_states.1_21_40.json new file mode 100644 index 000000000..9ad93dd09 --- /dev/null +++ b/core/src/main/resources/bedrock/runtime_item_states.1_21_40.json @@ -0,0 +1,6994 @@ +[ + { + "name": "minecraft:acacia_boat", + "id": 405 + }, + { + "name": "minecraft:acacia_button", + "id": -140 + }, + { + "name": "minecraft:acacia_chest_boat", + "id": 678 + }, + { + "name": "minecraft:acacia_door", + "id": 589 + }, + { + "name": "minecraft:acacia_double_slab", + "id": -812 + }, + { + "name": "minecraft:acacia_fence", + "id": -575 + }, + { + "name": "minecraft:acacia_fence_gate", + "id": 187 + }, + { + "name": "minecraft:acacia_hanging_sign", + "id": -504 + }, + { + "name": "minecraft:acacia_leaves", + "id": 161 + }, + { + "name": "minecraft:acacia_log", + "id": 162 + }, + { + "name": "minecraft:acacia_planks", + "id": -742 + }, + { + "name": "minecraft:acacia_pressure_plate", + "id": -150 + }, + { + "name": "minecraft:acacia_sapling", + "id": -828 + }, + { + "name": "minecraft:acacia_sign", + "id": 612 + }, + { + "name": "minecraft:acacia_slab", + "id": -807 + }, + { + "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:acacia_wood", + "id": -817 + }, + { + "name": "minecraft:activator_rail", + "id": 126 + }, + { + "name": "minecraft:agent_spawn_egg", + "id": 515 + }, + { + "name": "minecraft:air", + "id": -158 + }, + { + "name": "minecraft:allay_spawn_egg", + "id": 667 + }, + { + "name": "minecraft:allium", + "id": -831 + }, + { + "name": "minecraft:allow", + "id": 210 + }, + { + "name": "minecraft:amethyst_block", + "id": -327 + }, + { + "name": "minecraft:amethyst_cluster", + "id": -329 + }, + { + "name": "minecraft:amethyst_shard", + "id": 660 + }, + { + "name": "minecraft:ancient_debris", + "id": -271 + }, + { + "name": "minecraft:andesite", + "id": -594 + }, + { + "name": "minecraft:andesite_double_slab", + "id": -920 + }, + { + "name": "minecraft:andesite_slab", + "id": -893 + }, + { + "name": "minecraft:andesite_stairs", + "id": -171 + }, + { + "name": "minecraft:andesite_wall", + "id": -974 + }, + { + "name": "minecraft:angler_pottery_sherd", + "id": 692 + }, + { + "name": "minecraft:anvil", + "id": 145 + }, + { + "name": "minecraft:apple", + "id": 278 + }, + { + "name": "minecraft:archer_pottery_sherd", + "id": 693 + }, + { + "name": "minecraft:armadillo_scute", + "id": 739 + }, + { + "name": "minecraft:armadillo_spawn_egg", + "id": 738 + }, + { + "name": "minecraft:armor_stand", + "id": 585 + }, + { + "name": "minecraft:arms_up_pottery_sherd", + "id": 694 + }, + { + "name": "minecraft:arrow", + "id": 325 + }, + { + "name": "minecraft:axolotl_bucket", + "id": 394 + }, + { + "name": "minecraft:axolotl_spawn_egg", + "id": 530 + }, + { + "name": "minecraft:azalea", + "id": -337 + }, + { + "name": "minecraft:azalea_leaves", + "id": -324 + }, + { + "name": "minecraft:azalea_leaves_flowered", + "id": -325 + }, + { + "name": "minecraft:azure_bluet", + "id": -832 + }, + { + "name": "minecraft:baked_potato", + "id": 303 + }, + { + "name": "minecraft:balloon", + "id": 634 + }, + { + "name": "minecraft:bamboo", + "id": -163 + }, + { + "name": "minecraft:bamboo_block", + "id": -527 + }, + { + "name": "minecraft:bamboo_button", + "id": -511 + }, + { + "name": "minecraft:bamboo_chest_raft", + "id": 690 + }, + { + "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": 689 + }, + { + "name": "minecraft:bamboo_sapling", + "id": -164 + }, + { + "name": "minecraft:bamboo_sign", + "id": 688 + }, + { + "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": 600 + }, + { + "name": "minecraft:banner_pattern", + "id": 787 + }, + { + "name": "minecraft:barrel", + "id": -203 + }, + { + "name": "minecraft:barrier", + "id": -161 + }, + { + "name": "minecraft:basalt", + "id": -234 + }, + { + "name": "minecraft:bat_spawn_egg", + "id": 480 + }, + { + "name": "minecraft:beacon", + "id": 138 + }, + { + "name": "minecraft:bed", + "id": 444 + }, + { + "name": "minecraft:bedrock", + "id": 7 + }, + { + "name": "minecraft:bee_nest", + "id": -218 + }, + { + "name": "minecraft:bee_spawn_egg", + "id": 522 + }, + { + "name": "minecraft:beef", + "id": 295 + }, + { + "name": "minecraft:beehive", + "id": -219 + }, + { + "name": "minecraft:beetroot", + "id": 307 + }, + { + "name": "minecraft:beetroot_seeds", + "id": 317 + }, + { + "name": "minecraft:beetroot_soup", + "id": 308 + }, + { + "name": "minecraft:bell", + "id": -206 + }, + { + "name": "minecraft:big_dripleaf", + "id": -323 + }, + { + "name": "minecraft:birch_boat", + "id": 402 + }, + { + "name": "minecraft:birch_button", + "id": -141 + }, + { + "name": "minecraft:birch_chest_boat", + "id": 675 + }, + { + "name": "minecraft:birch_door", + "id": 587 + }, + { + "name": "minecraft:birch_double_slab", + "id": -810 + }, + { + "name": "minecraft:birch_fence", + "id": -576 + }, + { + "name": "minecraft:birch_fence_gate", + "id": 184 + }, + { + "name": "minecraft:birch_hanging_sign", + "id": -502 + }, + { + "name": "minecraft:birch_leaves", + "id": -801 + }, + { + "name": "minecraft:birch_log", + "id": -570 + }, + { + "name": "minecraft:birch_planks", + "id": -740 + }, + { + "name": "minecraft:birch_pressure_plate", + "id": -151 + }, + { + "name": "minecraft:birch_sapling", + "id": -826 + }, + { + "name": "minecraft:birch_sign", + "id": 610 + }, + { + "name": "minecraft:birch_slab", + "id": -805 + }, + { + "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:birch_wood", + "id": -815 + }, + { + "name": "minecraft:black_bundle", + "id": 257 + }, + { + "name": "minecraft:black_candle", + "id": -428 + }, + { + "name": "minecraft:black_candle_cake", + "id": -445 + }, + { + "name": "minecraft:black_carpet", + "id": -611 + }, + { + "name": "minecraft:black_concrete", + "id": -642 + }, + { + "name": "minecraft:black_concrete_powder", + "id": -723 + }, + { + "name": "minecraft:black_dye", + "id": 421 + }, + { + "name": "minecraft:black_glazed_terracotta", + "id": 235 + }, + { + "name": "minecraft:black_shulker_box", + "id": -627 + }, + { + "name": "minecraft:black_stained_glass", + "id": -687 + }, + { + "name": "minecraft:black_stained_glass_pane", + "id": -657 + }, + { + "name": "minecraft:black_terracotta", + "id": -738 + }, + { + "name": "minecraft:black_wool", + "id": -554 + }, + { + "name": "minecraft:blackstone", + "id": -273 + }, + { + "name": "minecraft:blackstone_double_slab", + "id": -283 + }, + { + "name": "minecraft:blackstone_slab", + "id": -282 + }, + { + "name": "minecraft:blackstone_stairs", + "id": -276 + }, + { + "name": "minecraft:blackstone_wall", + "id": -277 + }, + { + "name": "minecraft:blade_pottery_sherd", + "id": 695 + }, + { + "name": "minecraft:blast_furnace", + "id": -196 + }, + { + "name": "minecraft:blaze_powder", + "id": 456 + }, + { + "name": "minecraft:blaze_rod", + "id": 449 + }, + { + "name": "minecraft:blaze_spawn_egg", + "id": 483 + }, + { + "name": "minecraft:bleach", + "id": 632 + }, + { + "name": "minecraft:blue_bundle", + "id": 258 + }, + { + "name": "minecraft:blue_candle", + "id": -424 + }, + { + "name": "minecraft:blue_candle_cake", + "id": -441 + }, + { + "name": "minecraft:blue_carpet", + "id": -607 + }, + { + "name": "minecraft:blue_concrete", + "id": -638 + }, + { + "name": "minecraft:blue_concrete_powder", + "id": -719 + }, + { + "name": "minecraft:blue_dye", + "id": 425 + }, + { + "name": "minecraft:blue_glazed_terracotta", + "id": 231 + }, + { + "name": "minecraft:blue_ice", + "id": -11 + }, + { + "name": "minecraft:blue_orchid", + "id": -830 + }, + { + "name": "minecraft:blue_shulker_box", + "id": -623 + }, + { + "name": "minecraft:blue_stained_glass", + "id": -683 + }, + { + "name": "minecraft:blue_stained_glass_pane", + "id": -653 + }, + { + "name": "minecraft:blue_terracotta", + "id": -734 + }, + { + "name": "minecraft:blue_wool", + "id": -563 + }, + { + "name": "minecraft:boat", + "id": 785 + }, + { + "name": "minecraft:bogged_spawn_egg", + "id": 490 + }, + { + "name": "minecraft:bolt_armor_trim_smithing_template", + "id": 734 + }, + { + "name": "minecraft:bone", + "id": 441 + }, + { + "name": "minecraft:bone_block", + "id": 216 + }, + { + "name": "minecraft:bone_meal", + "id": 437 + }, + { + "name": "minecraft:book", + "id": 413 + }, + { + "name": "minecraft:bookshelf", + "id": 47 + }, + { + "name": "minecraft:border_block", + "id": 212 + }, + { + "name": "minecraft:bordure_indented_banner_pattern", + "id": 619 + }, + { + "name": "minecraft:bow", + "id": 324 + }, + { + "name": "minecraft:bowl", + "id": 346 + }, + { + "name": "minecraft:brain_coral", + "id": -581 + }, + { + "name": "minecraft:brain_coral_block", + "id": -849 + }, + { + "name": "minecraft:brain_coral_fan", + "id": -840 + }, + { + "name": "minecraft:brain_coral_wall_fan", + "id": -904 + }, + { + "name": "minecraft:bread", + "id": 283 + }, + { + "name": "minecraft:breeze_rod", + "id": 274 + }, + { + "name": "minecraft:breeze_spawn_egg", + "id": 529 + }, + { + "name": "minecraft:brewer_pottery_sherd", + "id": 696 + }, + { + "name": "minecraft:brewing_stand", + "id": 458 + }, + { + "name": "minecraft:brick", + "id": 409 + }, + { + "name": "minecraft:brick_block", + "id": 45 + }, + { + "name": "minecraft:brick_double_slab", + "id": -880 + }, + { + "name": "minecraft:brick_slab", + "id": -874 + }, + { + "name": "minecraft:brick_stairs", + "id": 108 + }, + { + "name": "minecraft:brick_wall", + "id": -976 + }, + { + "name": "minecraft:brown_bundle", + "id": 259 + }, + { + "name": "minecraft:brown_candle", + "id": -425 + }, + { + "name": "minecraft:brown_candle_cake", + "id": -442 + }, + { + "name": "minecraft:brown_carpet", + "id": -608 + }, + { + "name": "minecraft:brown_concrete", + "id": -639 + }, + { + "name": "minecraft:brown_concrete_powder", + "id": -720 + }, + { + "name": "minecraft:brown_dye", + "id": 424 + }, + { + "name": "minecraft:brown_glazed_terracotta", + "id": 232 + }, + { + "name": "minecraft:brown_mushroom", + "id": 39 + }, + { + "name": "minecraft:brown_mushroom_block", + "id": 99 + }, + { + "name": "minecraft:brown_shulker_box", + "id": -624 + }, + { + "name": "minecraft:brown_stained_glass", + "id": -684 + }, + { + "name": "minecraft:brown_stained_glass_pane", + "id": -654 + }, + { + "name": "minecraft:brown_terracotta", + "id": -735 + }, + { + "name": "minecraft:brown_wool", + "id": -555 + }, + { + "name": "minecraft:brush", + "id": 715 + }, + { + "name": "minecraft:bubble_column", + "id": -160 + }, + { + "name": "minecraft:bubble_coral", + "id": -582 + }, + { + "name": "minecraft:bubble_coral_block", + "id": -850 + }, + { + "name": "minecraft:bubble_coral_fan", + "id": -841 + }, + { + "name": "minecraft:bubble_coral_wall_fan", + "id": -136 + }, + { + "name": "minecraft:bucket", + "id": 385 + }, + { + "name": "minecraft:budding_amethyst", + "id": -328 + }, + { + "name": "minecraft:bundle", + "id": 260 + }, + { + "name": "minecraft:burn_pottery_sherd", + "id": 697 + }, + { + "name": "minecraft:cactus", + "id": 81 + }, + { + "name": "minecraft:cake", + "id": 443 + }, + { + "name": "minecraft:calcite", + "id": -326 + }, + { + "name": "minecraft:calibrated_sculk_sensor", + "id": -580 + }, + { + "name": "minecraft:camel_spawn_egg", + "id": 691 + }, + { + "name": "minecraft:camera", + "id": 629 + }, + { + "name": "minecraft:campfire", + "id": 624 + }, + { + "name": "minecraft:candle", + "id": -412 + }, + { + "name": "minecraft:candle_cake", + "id": -429 + }, + { + "name": "minecraft:carpet", + "id": 744 + }, + { + "name": "minecraft:carrot", + "id": 301 + }, + { + "name": "minecraft:carrot_on_a_stick", + "id": 550 + }, + { + "name": "minecraft:carrots", + "id": 141 + }, + { + "name": "minecraft:cartography_table", + "id": -200 + }, + { + "name": "minecraft:carved_pumpkin", + "id": -155 + }, + { + "name": "minecraft:cat_spawn_egg", + "id": 516 + }, + { + "name": "minecraft:cauldron", + "id": 459 + }, + { + "name": "minecraft:cave_spider_spawn_egg", + "id": 484 + }, + { + "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": 655 + }, + { + "name": "minecraft:chain_command_block", + "id": 189 + }, + { + "name": "minecraft:chainmail_boots", + "id": 367 + }, + { + "name": "minecraft:chainmail_chestplate", + "id": 365 + }, + { + "name": "minecraft:chainmail_helmet", + "id": 364 + }, + { + "name": "minecraft:chainmail_leggings", + "id": 366 + }, + { + "name": "minecraft:charcoal", + "id": 327 + }, + { + "name": "minecraft:chemical_heat", + "id": 192 + }, + { + "name": "minecraft:chemistry_table", + "id": 779 + }, + { + "name": "minecraft:cherry_boat", + "id": 685 + }, + { + "name": "minecraft:cherry_button", + "id": -530 + }, + { + "name": "minecraft:cherry_chest_boat", + "id": 686 + }, + { + "name": "minecraft:cherry_door", + "id": -531 + }, + { + "name": "minecraft:cherry_double_slab", + "id": -540 + }, + { + "name": "minecraft:cherry_fence", + "id": -532 + }, + { + "name": "minecraft:cherry_fence_gate", + "id": -533 + }, + { + "name": "minecraft:cherry_hanging_sign", + "id": -534 + }, + { + "name": "minecraft:cherry_leaves", + "id": -548 + }, + { + "name": "minecraft:cherry_log", + "id": -536 + }, + { + "name": "minecraft:cherry_planks", + "id": -537 + }, + { + "name": "minecraft:cherry_pressure_plate", + "id": -538 + }, + { + "name": "minecraft:cherry_sapling", + "id": -547 + }, + { + "name": "minecraft:cherry_sign", + "id": 687 + }, + { + "name": "minecraft:cherry_slab", + "id": -539 + }, + { + "name": "minecraft:cherry_stairs", + "id": -541 + }, + { + "name": "minecraft:cherry_standing_sign", + "id": -542 + }, + { + "name": "minecraft:cherry_trapdoor", + "id": -543 + }, + { + "name": "minecraft:cherry_wall_sign", + "id": -544 + }, + { + "name": "minecraft:cherry_wood", + "id": -546 + }, + { + "name": "minecraft:chest", + "id": 54 + }, + { + "name": "minecraft:chest_boat", + "id": 681 + }, + { + "name": "minecraft:chest_minecart", + "id": 415 + }, + { + "name": "minecraft:chicken", + "id": 297 + }, + { + "name": "minecraft:chicken_spawn_egg", + "id": 462 + }, + { + "name": "minecraft:chipped_anvil", + "id": -959 + }, + { + "name": "minecraft:chiseled_bookshelf", + "id": -526 + }, + { + "name": "minecraft:chiseled_copper", + "id": -760 + }, + { + "name": "minecraft:chiseled_deepslate", + "id": -395 + }, + { + "name": "minecraft:chiseled_nether_bricks", + "id": -302 + }, + { + "name": "minecraft:chiseled_polished_blackstone", + "id": -279 + }, + { + "name": "minecraft:chiseled_quartz_block", + "id": -953 + }, + { + "name": "minecraft:chiseled_red_sandstone", + "id": -956 + }, + { + "name": "minecraft:chiseled_sandstone", + "id": -944 + }, + { + "name": "minecraft:chiseled_stone_bricks", + "id": -870 + }, + { + "name": "minecraft:chiseled_tuff", + "id": -753 + }, + { + "name": "minecraft:chiseled_tuff_bricks", + "id": -759 + }, + { + "name": "minecraft:chorus_flower", + "id": 200 + }, + { + "name": "minecraft:chorus_fruit", + "id": 591 + }, + { + "name": "minecraft:chorus_plant", + "id": 240 + }, + { + "name": "minecraft:clay", + "id": 82 + }, + { + "name": "minecraft:clay_ball", + "id": 410 + }, + { + "name": "minecraft:client_request_placeholder_block", + "id": -465 + }, + { + "name": "minecraft:clock", + "id": 419 + }, + { + "name": "minecraft:coal", + "id": 326 + }, + { + "name": "minecraft:coal_block", + "id": 173 + }, + { + "name": "minecraft:coal_ore", + "id": 16 + }, + { + "name": "minecraft:coarse_dirt", + "id": -962 + }, + { + "name": "minecraft:coast_armor_trim_smithing_template", + "id": 719 + }, + { + "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_double_slab", + "id": -879 + }, + { + "name": "minecraft:cobblestone_slab", + "id": -873 + }, + { + "name": "minecraft:cobblestone_wall", + "id": 139 + }, + { + "name": "minecraft:cocoa", + "id": 127 + }, + { + "name": "minecraft:cocoa_beans", + "id": 438 + }, + { + "name": "minecraft:cod", + "id": 286 + }, + { + "name": "minecraft:cod_bucket", + "id": 389 + }, + { + "name": "minecraft:cod_spawn_egg", + "id": 508 + }, + { + "name": "minecraft:colored_torch_blue", + "id": 204 + }, + { + "name": "minecraft:colored_torch_bp", + "id": 783 + }, + { + "name": "minecraft:colored_torch_green", + "id": -963 + }, + { + "name": "minecraft:colored_torch_purple", + "id": -964 + }, + { + "name": "minecraft:colored_torch_red", + "id": 202 + }, + { + "name": "minecraft:colored_torch_rg", + "id": 782 + }, + { + "name": "minecraft:command_block", + "id": 137 + }, + { + "name": "minecraft:command_block_minecart", + "id": 596 + }, + { + "name": "minecraft:comparator", + "id": 555 + }, + { + "name": "minecraft:compass", + "id": 417 + }, + { + "name": "minecraft:composter", + "id": -213 + }, + { + "name": "minecraft:compound", + "id": 630 + }, + { + "name": "minecraft:compound_creator", + "id": 238 + }, + { + "name": "minecraft:concrete", + "id": 770 + }, + { + "name": "minecraft:concrete_powder", + "id": 771 + }, + { + "name": "minecraft:conduit", + "id": -157 + }, + { + "name": "minecraft:cooked_beef", + "id": 296 + }, + { + "name": "minecraft:cooked_chicken", + "id": 298 + }, + { + "name": "minecraft:cooked_cod", + "id": 290 + }, + { + "name": "minecraft:cooked_mutton", + "id": 584 + }, + { + "name": "minecraft:cooked_porkchop", + "id": 285 + }, + { + "name": "minecraft:cooked_rabbit", + "id": 311 + }, + { + "name": "minecraft:cooked_salmon", + "id": 291 + }, + { + "name": "minecraft:cookie", + "id": 293 + }, + { + "name": "minecraft:copper_block", + "id": -340 + }, + { + "name": "minecraft:copper_bulb", + "id": -776 + }, + { + "name": "minecraft:copper_door", + "id": -784 + }, + { + "name": "minecraft:copper_grate", + "id": -768 + }, + { + "name": "minecraft:copper_ingot", + "id": 538 + }, + { + "name": "minecraft:copper_ore", + "id": -311 + }, + { + "name": "minecraft:copper_trapdoor", + "id": -792 + }, + { + "name": "minecraft:coral", + "id": 766 + }, + { + "name": "minecraft:coral_block", + "id": 748 + }, + { + "name": "minecraft:coral_fan", + "id": 757 + }, + { + "name": "minecraft:coral_fan_dead", + "id": 758 + }, + { + "name": "minecraft:cornflower", + "id": -838 + }, + { + "name": "minecraft:cow_spawn_egg", + "id": 463 + }, + { + "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:cracked_stone_bricks", + "id": -869 + }, + { + "name": "minecraft:crafter", + "id": -313 + }, + { + "name": "minecraft:crafting_table", + "id": 58 + }, + { + "name": "minecraft:creeper_banner_pattern", + "id": 615 + }, + { + "name": "minecraft:creeper_head", + "id": -968 + }, + { + "name": "minecraft:creeper_spawn_egg", + "id": 468 + }, + { + "name": "minecraft:crimson_button", + "id": -260 + }, + { + "name": "minecraft:crimson_door", + "id": 652 + }, + { + "name": "minecraft:crimson_double_slab", + "id": -266 + }, + { + "name": "minecraft:crimson_fence", + "id": -256 + }, + { + "name": "minecraft:crimson_fence_gate", + "id": -258 + }, + { + "name": "minecraft:crimson_fungus", + "id": -228 + }, + { + "name": "minecraft:crimson_hanging_sign", + "id": -506 + }, + { + "name": "minecraft:crimson_hyphae", + "id": -299 + }, + { + "name": "minecraft:crimson_nylium", + "id": -232 + }, + { + "name": "minecraft:crimson_planks", + "id": -242 + }, + { + "name": "minecraft:crimson_pressure_plate", + "id": -262 + }, + { + "name": "minecraft:crimson_roots", + "id": -223 + }, + { + "name": "minecraft:crimson_sign", + "id": 650 + }, + { + "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": 608 + }, + { + "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:cut_red_sandstone", + "id": -957 + }, + { + "name": "minecraft:cut_red_sandstone_double_slab", + "id": -928 + }, + { + "name": "minecraft:cut_red_sandstone_slab", + "id": -901 + }, + { + "name": "minecraft:cut_sandstone", + "id": -945 + }, + { + "name": "minecraft:cut_sandstone_double_slab", + "id": -927 + }, + { + "name": "minecraft:cut_sandstone_slab", + "id": -900 + }, + { + "name": "minecraft:cyan_bundle", + "id": 261 + }, + { + "name": "minecraft:cyan_candle", + "id": -422 + }, + { + "name": "minecraft:cyan_candle_cake", + "id": -439 + }, + { + "name": "minecraft:cyan_carpet", + "id": -605 + }, + { + "name": "minecraft:cyan_concrete", + "id": -636 + }, + { + "name": "minecraft:cyan_concrete_powder", + "id": -717 + }, + { + "name": "minecraft:cyan_dye", + "id": 427 + }, + { + "name": "minecraft:cyan_glazed_terracotta", + "id": 229 + }, + { + "name": "minecraft:cyan_shulker_box", + "id": -621 + }, + { + "name": "minecraft:cyan_stained_glass", + "id": -681 + }, + { + "name": "minecraft:cyan_stained_glass_pane", + "id": -651 + }, + { + "name": "minecraft:cyan_terracotta", + "id": -732 + }, + { + "name": "minecraft:cyan_wool", + "id": -561 + }, + { + "name": "minecraft:damaged_anvil", + "id": -960 + }, + { + "name": "minecraft:dandelion", + "id": 37 + }, + { + "name": "minecraft:danger_pottery_sherd", + "id": 698 + }, + { + "name": "minecraft:dark_oak_boat", + "id": 406 + }, + { + "name": "minecraft:dark_oak_button", + "id": -142 + }, + { + "name": "minecraft:dark_oak_chest_boat", + "id": 679 + }, + { + "name": "minecraft:dark_oak_door", + "id": 590 + }, + { + "name": "minecraft:dark_oak_double_slab", + "id": -813 + }, + { + "name": "minecraft:dark_oak_fence", + "id": -577 + }, + { + "name": "minecraft:dark_oak_fence_gate", + "id": 186 + }, + { + "name": "minecraft:dark_oak_hanging_sign", + "id": -505 + }, + { + "name": "minecraft:dark_oak_leaves", + "id": -803 + }, + { + "name": "minecraft:dark_oak_log", + "id": -572 + }, + { + "name": "minecraft:dark_oak_planks", + "id": -743 + }, + { + "name": "minecraft:dark_oak_pressure_plate", + "id": -152 + }, + { + "name": "minecraft:dark_oak_sapling", + "id": -829 + }, + { + "name": "minecraft:dark_oak_sign", + "id": 613 + }, + { + "name": "minecraft:dark_oak_slab", + "id": -808 + }, + { + "name": "minecraft:dark_oak_stairs", + "id": 164 + }, + { + "name": "minecraft:dark_oak_trapdoor", + "id": -147 + }, + { + "name": "minecraft:dark_oak_wood", + "id": -818 + }, + { + "name": "minecraft:dark_prismarine", + "id": -947 + }, + { + "name": "minecraft:dark_prismarine_double_slab", + "id": -913 + }, + { + "name": "minecraft:dark_prismarine_slab", + "id": -886 + }, + { + "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:dead_brain_coral", + "id": -586 + }, + { + "name": "minecraft:dead_brain_coral_block", + "id": -854 + }, + { + "name": "minecraft:dead_brain_coral_fan", + "id": -844 + }, + { + "name": "minecraft:dead_brain_coral_wall_fan", + "id": -906 + }, + { + "name": "minecraft:dead_bubble_coral", + "id": -587 + }, + { + "name": "minecraft:dead_bubble_coral_block", + "id": -855 + }, + { + "name": "minecraft:dead_bubble_coral_fan", + "id": -845 + }, + { + "name": "minecraft:dead_bubble_coral_wall_fan", + "id": -908 + }, + { + "name": "minecraft:dead_fire_coral", + "id": -588 + }, + { + "name": "minecraft:dead_fire_coral_block", + "id": -856 + }, + { + "name": "minecraft:dead_fire_coral_fan", + "id": -846 + }, + { + "name": "minecraft:dead_fire_coral_wall_fan", + "id": -909 + }, + { + "name": "minecraft:dead_horn_coral", + "id": -589 + }, + { + "name": "minecraft:dead_horn_coral_block", + "id": -857 + }, + { + "name": "minecraft:dead_horn_coral_fan", + "id": -847 + }, + { + "name": "minecraft:dead_horn_coral_wall_fan", + "id": -910 + }, + { + "name": "minecraft:dead_tube_coral", + "id": -585 + }, + { + "name": "minecraft:dead_tube_coral_block", + "id": -853 + }, + { + "name": "minecraft:dead_tube_coral_fan", + "id": -134 + }, + { + "name": "minecraft:dead_tube_coral_wall_fan", + "id": -905 + }, + { + "name": "minecraft:deadbush", + "id": 32 + }, + { + "name": "minecraft:decorated_pot", + "id": -551 + }, + { + "name": "minecraft:deepslate", + "id": -378 + }, + { + "name": "minecraft:deepslate_brick_double_slab", + "id": -399 + }, + { + "name": "minecraft:deepslate_brick_slab", + "id": -392 + }, + { + "name": "minecraft:deepslate_brick_stairs", + "id": -393 + }, + { + "name": "minecraft:deepslate_brick_wall", + "id": -394 + }, + { + "name": "minecraft:deepslate_bricks", + "id": -391 + }, + { + "name": "minecraft:deepslate_coal_ore", + "id": -406 + }, + { + "name": "minecraft:deepslate_copper_ore", + "id": -408 + }, + { + "name": "minecraft:deepslate_diamond_ore", + "id": -405 + }, + { + "name": "minecraft:deepslate_emerald_ore", + "id": -407 + }, + { + "name": "minecraft:deepslate_gold_ore", + "id": -402 + }, + { + "name": "minecraft:deepslate_iron_ore", + "id": -401 + }, + { + "name": "minecraft:deepslate_lapis_ore", + "id": -400 + }, + { + "name": "minecraft:deepslate_redstone_ore", + "id": -403 + }, + { + "name": "minecraft:deepslate_tile_double_slab", + "id": -398 + }, + { + "name": "minecraft:deepslate_tile_slab", + "id": -388 + }, + { + "name": "minecraft:deepslate_tile_stairs", + "id": -389 + }, + { + "name": "minecraft:deepslate_tile_wall", + "id": -390 + }, + { + "name": "minecraft:deepslate_tiles", + "id": -387 + }, + { + "name": "minecraft:deny", + "id": 211 + }, + { + "name": "minecraft:deprecated_anvil", + "id": -961 + }, + { + "name": "minecraft:deprecated_purpur_block_1", + "id": -950 + }, + { + "name": "minecraft:deprecated_purpur_block_2", + "id": -952 + }, + { + "name": "minecraft:detector_rail", + "id": 28 + }, + { + "name": "minecraft:diamond", + "id": 328 + }, + { + "name": "minecraft:diamond_axe", + "id": 343 + }, + { + "name": "minecraft:diamond_block", + "id": 57 + }, + { + "name": "minecraft:diamond_boots", + "id": 375 + }, + { + "name": "minecraft:diamond_chestplate", + "id": 373 + }, + { + "name": "minecraft:diamond_helmet", + "id": 372 + }, + { + "name": "minecraft:diamond_hoe", + "id": 357 + }, + { + "name": "minecraft:diamond_horse_armor", + "id": 566 + }, + { + "name": "minecraft:diamond_leggings", + "id": 374 + }, + { + "name": "minecraft:diamond_ore", + "id": 56 + }, + { + "name": "minecraft:diamond_pickaxe", + "id": 342 + }, + { + "name": "minecraft:diamond_shovel", + "id": 341 + }, + { + "name": "minecraft:diamond_sword", + "id": 340 + }, + { + "name": "minecraft:diorite", + "id": -592 + }, + { + "name": "minecraft:diorite_double_slab", + "id": -921 + }, + { + "name": "minecraft:diorite_slab", + "id": -894 + }, + { + "name": "minecraft:diorite_stairs", + "id": -170 + }, + { + "name": "minecraft:diorite_wall", + "id": -973 + }, + { + "name": "minecraft:dirt", + "id": 3 + }, + { + "name": "minecraft:dirt_with_roots", + "id": -318 + }, + { + "name": "minecraft:disc_fragment_5", + "id": 673 + }, + { + "name": "minecraft:dispenser", + "id": 23 + }, + { + "name": "minecraft:dolphin_spawn_egg", + "id": 512 + }, + { + "name": "minecraft:donkey_spawn_egg", + "id": 493 + }, + { + "name": "minecraft:double_cut_copper_slab", + "id": -368 + }, + { + "name": "minecraft:double_plant", + "id": 764 + }, + { + "name": "minecraft:double_stone_block_slab", + "id": 753 + }, + { + "name": "minecraft:double_stone_block_slab2", + "id": 754 + }, + { + "name": "minecraft:double_stone_block_slab3", + "id": 755 + }, + { + "name": "minecraft:double_stone_block_slab4", + "id": 756 + }, + { + "name": "minecraft:dragon_breath", + "id": 593 + }, + { + "name": "minecraft:dragon_egg", + "id": 122 + }, + { + "name": "minecraft:dragon_head", + "id": -969 + }, + { + "name": "minecraft:dried_kelp", + "id": 292 + }, + { + "name": "minecraft:dried_kelp_block", + "id": -139 + }, + { + "name": "minecraft:dripstone_block", + "id": -317 + }, + { + "name": "minecraft:dropper", + "id": 125 + }, + { + "name": "minecraft:drowned_spawn_egg", + "id": 511 + }, + { + "name": "minecraft:dune_armor_trim_smithing_template", + "id": 718 + }, + { + "name": "minecraft:dye", + "id": 786 + }, + { + "name": "minecraft:echo_shard", + "id": 683 + }, + { + "name": "minecraft:egg", + "id": 416 + }, + { + "name": "minecraft:elder_guardian_spawn_egg", + "id": 499 + }, + { + "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:element_constructor", + "id": -987 + }, + { + "name": "minecraft:elytra", + "id": 597 + }, + { + "name": "minecraft:emerald", + "id": 546 + }, + { + "name": "minecraft:emerald_block", + "id": 133 + }, + { + "name": "minecraft:emerald_ore", + "id": 129 + }, + { + "name": "minecraft:empty_map", + "id": 549 + }, + { + "name": "minecraft:enchanted_book", + "id": 554 + }, + { + "name": "minecraft:enchanted_golden_apple", + "id": 281 + }, + { + "name": "minecraft:enchanting_table", + "id": 116 + }, + { + "name": "minecraft:end_brick_stairs", + "id": -178 + }, + { + "name": "minecraft:end_bricks", + "id": 206 + }, + { + "name": "minecraft:end_crystal", + "id": 789 + }, + { + "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:end_stone_brick_double_slab", + "id": -167 + }, + { + "name": "minecraft:end_stone_brick_slab", + "id": -162 + }, + { + "name": "minecraft:end_stone_brick_wall", + "id": -980 + }, + { + "name": "minecraft:ender_chest", + "id": 130 + }, + { + "name": "minecraft:ender_dragon_spawn_egg", + "id": 535 + }, + { + "name": "minecraft:ender_eye", + "id": 460 + }, + { + "name": "minecraft:ender_pearl", + "id": 448 + }, + { + "name": "minecraft:enderman_spawn_egg", + "id": 469 + }, + { + "name": "minecraft:endermite_spawn_egg", + "id": 487 + }, + { + "name": "minecraft:evoker_spawn_egg", + "id": 503 + }, + { + "name": "minecraft:experience_bottle", + "id": 542 + }, + { + "name": "minecraft:explorer_pottery_sherd", + "id": 699 + }, + { + "name": "minecraft:exposed_chiseled_copper", + "id": -761 + }, + { + "name": "minecraft:exposed_copper", + "id": -341 + }, + { + "name": "minecraft:exposed_copper_bulb", + "id": -777 + }, + { + "name": "minecraft:exposed_copper_door", + "id": -785 + }, + { + "name": "minecraft:exposed_copper_grate", + "id": -769 + }, + { + "name": "minecraft:exposed_copper_trapdoor", + "id": -793 + }, + { + "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:eye_armor_trim_smithing_template", + "id": 722 + }, + { + "name": "minecraft:farmland", + "id": 60 + }, + { + "name": "minecraft:feather", + "id": 352 + }, + { + "name": "minecraft:fence", + "id": 746 + }, + { + "name": "minecraft:fence_gate", + "id": 107 + }, + { + "name": "minecraft:fermented_spider_eye", + "id": 455 + }, + { + "name": "minecraft:fern", + "id": -848 + }, + { + "name": "minecraft:field_masoned_banner_pattern", + "id": 618 + }, + { + "name": "minecraft:filled_map", + "id": 446 + }, + { + "name": "minecraft:fire", + "id": 51 + }, + { + "name": "minecraft:fire_charge", + "id": 543 + }, + { + "name": "minecraft:fire_coral", + "id": -583 + }, + { + "name": "minecraft:fire_coral_block", + "id": -851 + }, + { + "name": "minecraft:fire_coral_fan", + "id": -842 + }, + { + "name": "minecraft:fire_coral_wall_fan", + "id": -907 + }, + { + "name": "minecraft:firework_rocket", + "id": 552 + }, + { + "name": "minecraft:firework_star", + "id": 553 + }, + { + "name": "minecraft:fishing_rod", + "id": 418 + }, + { + "name": "minecraft:fletching_table", + "id": -201 + }, + { + "name": "minecraft:flint", + "id": 381 + }, + { + "name": "minecraft:flint_and_steel", + "id": 323 + }, + { + "name": "minecraft:flow_armor_trim_smithing_template", + "id": 733 + }, + { + "name": "minecraft:flow_banner_pattern", + "id": 622 + }, + { + "name": "minecraft:flow_pottery_sherd", + "id": 700 + }, + { + "name": "minecraft:flower_banner_pattern", + "id": 614 + }, + { + "name": "minecraft:flower_pot", + "id": 548 + }, + { + "name": "minecraft:flowering_azalea", + "id": -338 + }, + { + "name": "minecraft:flowing_lava", + "id": 10 + }, + { + "name": "minecraft:flowing_water", + "id": 8 + }, + { + "name": "minecraft:fox_spawn_egg", + "id": 518 + }, + { + "name": "minecraft:frame", + "id": 547 + }, + { + "name": "minecraft:friend_pottery_sherd", + "id": 701 + }, + { + "name": "minecraft:frog_spawn", + "id": -468 + }, + { + "name": "minecraft:frog_spawn_egg", + "id": 664 + }, + { + "name": "minecraft:frosted_ice", + "id": 207 + }, + { + "name": "minecraft:furnace", + "id": 61 + }, + { + "name": "minecraft:ghast_spawn_egg", + "id": 481 + }, + { + "name": "minecraft:ghast_tear", + "id": 451 + }, + { + "name": "minecraft:gilded_blackstone", + "id": -281 + }, + { + "name": "minecraft:glass", + "id": 20 + }, + { + "name": "minecraft:glass_bottle", + "id": 454 + }, + { + "name": "minecraft:glass_pane", + "id": 102 + }, + { + "name": "minecraft:glistering_melon_slice", + "id": 461 + }, + { + "name": "minecraft:globe_banner_pattern", + "id": 621 + }, + { + "name": "minecraft:glow_berries", + "id": 790 + }, + { + "name": "minecraft:glow_frame", + "id": 659 + }, + { + "name": "minecraft:glow_ink_sac", + "id": 537 + }, + { + "name": "minecraft:glow_lichen", + "id": -411 + }, + { + "name": "minecraft:glow_squid_spawn_egg", + "id": 532 + }, + { + "name": "minecraft:glow_stick", + "id": 637 + }, + { + "name": "minecraft:glowingobsidian", + "id": 246 + }, + { + "name": "minecraft:glowstone", + "id": 89 + }, + { + "name": "minecraft:glowstone_dust", + "id": 420 + }, + { + "name": "minecraft:goat_horn", + "id": 663 + }, + { + "name": "minecraft:goat_spawn_egg", + "id": 531 + }, + { + "name": "minecraft:gold_block", + "id": 41 + }, + { + "name": "minecraft:gold_ingot", + "id": 330 + }, + { + "name": "minecraft:gold_nugget", + "id": 452 + }, + { + "name": "minecraft:gold_ore", + "id": 14 + }, + { + "name": "minecraft:golden_apple", + "id": 280 + }, + { + "name": "minecraft:golden_axe", + "id": 350 + }, + { + "name": "minecraft:golden_boots", + "id": 379 + }, + { + "name": "minecraft:golden_carrot", + "id": 305 + }, + { + "name": "minecraft:golden_chestplate", + "id": 377 + }, + { + "name": "minecraft:golden_helmet", + "id": 376 + }, + { + "name": "minecraft:golden_hoe", + "id": 358 + }, + { + "name": "minecraft:golden_horse_armor", + "id": 565 + }, + { + "name": "minecraft:golden_leggings", + "id": 378 + }, + { + "name": "minecraft:golden_pickaxe", + "id": 349 + }, + { + "name": "minecraft:golden_rail", + "id": 27 + }, + { + "name": "minecraft:golden_shovel", + "id": 348 + }, + { + "name": "minecraft:golden_sword", + "id": 347 + }, + { + "name": "minecraft:granite", + "id": -590 + }, + { + "name": "minecraft:granite_double_slab", + "id": -923 + }, + { + "name": "minecraft:granite_slab", + "id": -896 + }, + { + "name": "minecraft:granite_stairs", + "id": -169 + }, + { + "name": "minecraft:granite_wall", + "id": -972 + }, + { + "name": "minecraft:grass_block", + "id": 2 + }, + { + "name": "minecraft:grass_path", + "id": 198 + }, + { + "name": "minecraft:gravel", + "id": 13 + }, + { + "name": "minecraft:gray_bundle", + "id": 262 + }, + { + "name": "minecraft:gray_candle", + "id": -420 + }, + { + "name": "minecraft:gray_candle_cake", + "id": -437 + }, + { + "name": "minecraft:gray_carpet", + "id": -603 + }, + { + "name": "minecraft:gray_concrete", + "id": -634 + }, + { + "name": "minecraft:gray_concrete_powder", + "id": -715 + }, + { + "name": "minecraft:gray_dye", + "id": 429 + }, + { + "name": "minecraft:gray_glazed_terracotta", + "id": 227 + }, + { + "name": "minecraft:gray_shulker_box", + "id": -619 + }, + { + "name": "minecraft:gray_stained_glass", + "id": -679 + }, + { + "name": "minecraft:gray_stained_glass_pane", + "id": -649 + }, + { + "name": "minecraft:gray_terracotta", + "id": -730 + }, + { + "name": "minecraft:gray_wool", + "id": -553 + }, + { + "name": "minecraft:green_bundle", + "id": 263 + }, + { + "name": "minecraft:green_candle", + "id": -426 + }, + { + "name": "minecraft:green_candle_cake", + "id": -443 + }, + { + "name": "minecraft:green_carpet", + "id": -609 + }, + { + "name": "minecraft:green_concrete", + "id": -640 + }, + { + "name": "minecraft:green_concrete_powder", + "id": -721 + }, + { + "name": "minecraft:green_dye", + "id": 423 + }, + { + "name": "minecraft:green_glazed_terracotta", + "id": 233 + }, + { + "name": "minecraft:green_shulker_box", + "id": -625 + }, + { + "name": "minecraft:green_stained_glass", + "id": -685 + }, + { + "name": "minecraft:green_stained_glass_pane", + "id": -655 + }, + { + "name": "minecraft:green_terracotta", + "id": -736 + }, + { + "name": "minecraft:green_wool", + "id": -560 + }, + { + "name": "minecraft:grindstone", + "id": -195 + }, + { + "name": "minecraft:guardian_spawn_egg", + "id": 488 + }, + { + "name": "minecraft:gunpowder", + "id": 353 + }, + { + "name": "minecraft:guster_banner_pattern", + "id": 623 + }, + { + "name": "minecraft:guster_pottery_sherd", + "id": 702 + }, + { + "name": "minecraft:hanging_roots", + "id": -319 + }, + { + "name": "minecraft:hard_black_stained_glass", + "id": -702 + }, + { + "name": "minecraft:hard_black_stained_glass_pane", + "id": -672 + }, + { + "name": "minecraft:hard_blue_stained_glass", + "id": -698 + }, + { + "name": "minecraft:hard_blue_stained_glass_pane", + "id": -668 + }, + { + "name": "minecraft:hard_brown_stained_glass", + "id": -699 + }, + { + "name": "minecraft:hard_brown_stained_glass_pane", + "id": -669 + }, + { + "name": "minecraft:hard_cyan_stained_glass", + "id": -696 + }, + { + "name": "minecraft:hard_cyan_stained_glass_pane", + "id": -666 + }, + { + "name": "minecraft:hard_glass", + "id": 253 + }, + { + "name": "minecraft:hard_glass_pane", + "id": 190 + }, + { + "name": "minecraft:hard_gray_stained_glass", + "id": -694 + }, + { + "name": "minecraft:hard_gray_stained_glass_pane", + "id": -664 + }, + { + "name": "minecraft:hard_green_stained_glass", + "id": -700 + }, + { + "name": "minecraft:hard_green_stained_glass_pane", + "id": -670 + }, + { + "name": "minecraft:hard_light_blue_stained_glass", + "id": -690 + }, + { + "name": "minecraft:hard_light_blue_stained_glass_pane", + "id": -660 + }, + { + "name": "minecraft:hard_light_gray_stained_glass", + "id": -695 + }, + { + "name": "minecraft:hard_light_gray_stained_glass_pane", + "id": -665 + }, + { + "name": "minecraft:hard_lime_stained_glass", + "id": -692 + }, + { + "name": "minecraft:hard_lime_stained_glass_pane", + "id": -662 + }, + { + "name": "minecraft:hard_magenta_stained_glass", + "id": -689 + }, + { + "name": "minecraft:hard_magenta_stained_glass_pane", + "id": -659 + }, + { + "name": "minecraft:hard_orange_stained_glass", + "id": -688 + }, + { + "name": "minecraft:hard_orange_stained_glass_pane", + "id": -658 + }, + { + "name": "minecraft:hard_pink_stained_glass", + "id": -693 + }, + { + "name": "minecraft:hard_pink_stained_glass_pane", + "id": -663 + }, + { + "name": "minecraft:hard_purple_stained_glass", + "id": -697 + }, + { + "name": "minecraft:hard_purple_stained_glass_pane", + "id": -667 + }, + { + "name": "minecraft:hard_red_stained_glass", + "id": -701 + }, + { + "name": "minecraft:hard_red_stained_glass_pane", + "id": -671 + }, + { + "name": "minecraft:hard_stained_glass", + "id": 780 + }, + { + "name": "minecraft:hard_stained_glass_pane", + "id": 781 + }, + { + "name": "minecraft:hard_white_stained_glass", + "id": 254 + }, + { + "name": "minecraft:hard_white_stained_glass_pane", + "id": 191 + }, + { + "name": "minecraft:hard_yellow_stained_glass", + "id": -691 + }, + { + "name": "minecraft:hard_yellow_stained_glass_pane", + "id": -661 + }, + { + "name": "minecraft:hardened_clay", + "id": 172 + }, + { + "name": "minecraft:hay_block", + "id": 170 + }, + { + "name": "minecraft:heart_of_the_sea", + "id": 604 + }, + { + "name": "minecraft:heart_pottery_sherd", + "id": 703 + }, + { + "name": "minecraft:heartbreak_pottery_sherd", + "id": 704 + }, + { + "name": "minecraft:heavy_core", + "id": -316 + }, + { + "name": "minecraft:heavy_weighted_pressure_plate", + "id": 148 + }, + { + "name": "minecraft:hoglin_spawn_egg", + "id": 524 + }, + { + "name": "minecraft:honey_block", + "id": -220 + }, + { + "name": "minecraft:honey_bottle", + "id": 627 + }, + { + "name": "minecraft:honeycomb", + "id": 626 + }, + { + "name": "minecraft:honeycomb_block", + "id": -221 + }, + { + "name": "minecraft:hopper", + "id": 560 + }, + { + "name": "minecraft:hopper_minecart", + "id": 559 + }, + { + "name": "minecraft:horn_coral", + "id": -584 + }, + { + "name": "minecraft:horn_coral_block", + "id": -852 + }, + { + "name": "minecraft:horn_coral_fan", + "id": -843 + }, + { + "name": "minecraft:horn_coral_wall_fan", + "id": -137 + }, + { + "name": "minecraft:horse_spawn_egg", + "id": 485 + }, + { + "name": "minecraft:host_armor_trim_smithing_template", + "id": 732 + }, + { + "name": "minecraft:howl_pottery_sherd", + "id": 705 + }, + { + "name": "minecraft:husk_spawn_egg", + "id": 491 + }, + { + "name": "minecraft:ice", + "id": 79 + }, + { + "name": "minecraft:ice_bomb", + "id": 631 + }, + { + "name": "minecraft:infested_chiseled_stone_bricks", + "id": -862 + }, + { + "name": "minecraft:infested_cobblestone", + "id": -858 + }, + { + "name": "minecraft:infested_cracked_stone_bricks", + "id": -861 + }, + { + "name": "minecraft:infested_deepslate", + "id": -454 + }, + { + "name": "minecraft:infested_mossy_stone_bricks", + "id": -860 + }, + { + "name": "minecraft:infested_stone", + "id": 97 + }, + { + "name": "minecraft:infested_stone_bricks", + "id": -859 + }, + { + "name": "minecraft:info_update", + "id": 248 + }, + { + "name": "minecraft:info_update2", + "id": 249 + }, + { + "name": "minecraft:ink_sac", + "id": 439 + }, + { + "name": "minecraft:invisible_bedrock", + "id": 95 + }, + { + "name": "minecraft:iron_axe", + "id": 322 + }, + { + "name": "minecraft:iron_bars", + "id": 101 + }, + { + "name": "minecraft:iron_block", + "id": 42 + }, + { + "name": "minecraft:iron_boots", + "id": 371 + }, + { + "name": "minecraft:iron_chestplate", + "id": 369 + }, + { + "name": "minecraft:iron_door", + "id": 397 + }, + { + "name": "minecraft:iron_golem_spawn_egg", + "id": 533 + }, + { + "name": "minecraft:iron_helmet", + "id": 368 + }, + { + "name": "minecraft:iron_hoe", + "id": 356 + }, + { + "name": "minecraft:iron_horse_armor", + "id": 564 + }, + { + "name": "minecraft:iron_ingot", + "id": 329 + }, + { + "name": "minecraft:iron_leggings", + "id": 370 + }, + { + "name": "minecraft:iron_nugget", + "id": 602 + }, + { + "name": "minecraft:iron_ore", + "id": 15 + }, + { + "name": "minecraft:iron_pickaxe", + "id": 321 + }, + { + "name": "minecraft:iron_shovel", + "id": 320 + }, + { + "name": "minecraft:iron_sword", + "id": 331 + }, + { + "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.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": 403 + }, + { + "name": "minecraft:jungle_button", + "id": -143 + }, + { + "name": "minecraft:jungle_chest_boat", + "id": 676 + }, + { + "name": "minecraft:jungle_door", + "id": 588 + }, + { + "name": "minecraft:jungle_double_slab", + "id": -811 + }, + { + "name": "minecraft:jungle_fence", + "id": -578 + }, + { + "name": "minecraft:jungle_fence_gate", + "id": 185 + }, + { + "name": "minecraft:jungle_hanging_sign", + "id": -503 + }, + { + "name": "minecraft:jungle_leaves", + "id": -802 + }, + { + "name": "minecraft:jungle_log", + "id": -571 + }, + { + "name": "minecraft:jungle_planks", + "id": -741 + }, + { + "name": "minecraft:jungle_pressure_plate", + "id": -153 + }, + { + "name": "minecraft:jungle_sapling", + "id": -827 + }, + { + "name": "minecraft:jungle_sign", + "id": 611 + }, + { + "name": "minecraft:jungle_slab", + "id": -806 + }, + { + "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:jungle_wood", + "id": -816 + }, + { + "name": "minecraft:kelp", + "id": 408 + }, + { + "name": "minecraft:lab_table", + "id": -988 + }, + { + "name": "minecraft:ladder", + "id": 65 + }, + { + "name": "minecraft:lantern", + "id": -208 + }, + { + "name": "minecraft:lapis_block", + "id": 22 + }, + { + "name": "minecraft:lapis_lazuli", + "id": 440 + }, + { + "name": "minecraft:lapis_ore", + "id": 21 + }, + { + "name": "minecraft:large_amethyst_bud", + "id": -330 + }, + { + "name": "minecraft:large_fern", + "id": -865 + }, + { + "name": "minecraft:lava", + "id": 11 + }, + { + "name": "minecraft:lava_bucket", + "id": 388 + }, + { + "name": "minecraft:lead", + "id": 580 + }, + { + "name": "minecraft:leather", + "id": 407 + }, + { + "name": "minecraft:leather_boots", + "id": 363 + }, + { + "name": "minecraft:leather_chestplate", + "id": 361 + }, + { + "name": "minecraft:leather_helmet", + "id": 360 + }, + { + "name": "minecraft:leather_horse_armor", + "id": 563 + }, + { + "name": "minecraft:leather_leggings", + "id": 362 + }, + { + "name": "minecraft:leaves", + "id": 760 + }, + { + "name": "minecraft:leaves2", + "id": 761 + }, + { + "name": "minecraft:lectern", + "id": -194 + }, + { + "name": "minecraft:lever", + "id": 69 + }, + { + "name": "minecraft:light_block", + "id": 784 + }, + { + "name": "minecraft:light_block_0", + "id": -215 + }, + { + "name": "minecraft:light_block_1", + "id": -929 + }, + { + "name": "minecraft:light_block_10", + "id": -938 + }, + { + "name": "minecraft:light_block_11", + "id": -939 + }, + { + "name": "minecraft:light_block_12", + "id": -940 + }, + { + "name": "minecraft:light_block_13", + "id": -941 + }, + { + "name": "minecraft:light_block_14", + "id": -942 + }, + { + "name": "minecraft:light_block_15", + "id": -943 + }, + { + "name": "minecraft:light_block_2", + "id": -930 + }, + { + "name": "minecraft:light_block_3", + "id": -931 + }, + { + "name": "minecraft:light_block_4", + "id": -932 + }, + { + "name": "minecraft:light_block_5", + "id": -933 + }, + { + "name": "minecraft:light_block_6", + "id": -934 + }, + { + "name": "minecraft:light_block_7", + "id": -935 + }, + { + "name": "minecraft:light_block_8", + "id": -936 + }, + { + "name": "minecraft:light_block_9", + "id": -937 + }, + { + "name": "minecraft:light_blue_bundle", + "id": 264 + }, + { + "name": "minecraft:light_blue_candle", + "id": -416 + }, + { + "name": "minecraft:light_blue_candle_cake", + "id": -433 + }, + { + "name": "minecraft:light_blue_carpet", + "id": -599 + }, + { + "name": "minecraft:light_blue_concrete", + "id": -630 + }, + { + "name": "minecraft:light_blue_concrete_powder", + "id": -711 + }, + { + "name": "minecraft:light_blue_dye", + "id": 433 + }, + { + "name": "minecraft:light_blue_glazed_terracotta", + "id": 223 + }, + { + "name": "minecraft:light_blue_shulker_box", + "id": -615 + }, + { + "name": "minecraft:light_blue_stained_glass", + "id": -675 + }, + { + "name": "minecraft:light_blue_stained_glass_pane", + "id": -645 + }, + { + "name": "minecraft:light_blue_terracotta", + "id": -726 + }, + { + "name": "minecraft:light_blue_wool", + "id": -562 + }, + { + "name": "minecraft:light_gray_bundle", + "id": 265 + }, + { + "name": "minecraft:light_gray_candle", + "id": -421 + }, + { + "name": "minecraft:light_gray_candle_cake", + "id": -438 + }, + { + "name": "minecraft:light_gray_carpet", + "id": -604 + }, + { + "name": "minecraft:light_gray_concrete", + "id": -635 + }, + { + "name": "minecraft:light_gray_concrete_powder", + "id": -716 + }, + { + "name": "minecraft:light_gray_dye", + "id": 428 + }, + { + "name": "minecraft:light_gray_shulker_box", + "id": -620 + }, + { + "name": "minecraft:light_gray_stained_glass", + "id": -680 + }, + { + "name": "minecraft:light_gray_stained_glass_pane", + "id": -650 + }, + { + "name": "minecraft:light_gray_terracotta", + "id": -731 + }, + { + "name": "minecraft:light_gray_wool", + "id": -552 + }, + { + "name": "minecraft:light_weighted_pressure_plate", + "id": 147 + }, + { + "name": "minecraft:lightning_rod", + "id": -312 + }, + { + "name": "minecraft:lilac", + "id": -863 + }, + { + "name": "minecraft:lily_of_the_valley", + "id": -839 + }, + { + "name": "minecraft:lime_bundle", + "id": 266 + }, + { + "name": "minecraft:lime_candle", + "id": -418 + }, + { + "name": "minecraft:lime_candle_cake", + "id": -435 + }, + { + "name": "minecraft:lime_carpet", + "id": -601 + }, + { + "name": "minecraft:lime_concrete", + "id": -632 + }, + { + "name": "minecraft:lime_concrete_powder", + "id": -713 + }, + { + "name": "minecraft:lime_dye", + "id": 431 + }, + { + "name": "minecraft:lime_glazed_terracotta", + "id": 225 + }, + { + "name": "minecraft:lime_shulker_box", + "id": -617 + }, + { + "name": "minecraft:lime_stained_glass", + "id": -677 + }, + { + "name": "minecraft:lime_stained_glass_pane", + "id": -647 + }, + { + "name": "minecraft:lime_terracotta", + "id": -728 + }, + { + "name": "minecraft:lime_wool", + "id": -559 + }, + { + "name": "minecraft:lingering_potion", + "id": 595 + }, + { + "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": 501 + }, + { + "name": "minecraft:lodestone", + "id": -222 + }, + { + "name": "minecraft:lodestone_compass", + "id": 638 + }, + { + "name": "minecraft:log", + "id": 745 + }, + { + "name": "minecraft:log2", + "id": 768 + }, + { + "name": "minecraft:loom", + "id": -204 + }, + { + "name": "minecraft:mace", + "id": 344 + }, + { + "name": "minecraft:magenta_bundle", + "id": 267 + }, + { + "name": "minecraft:magenta_candle", + "id": -415 + }, + { + "name": "minecraft:magenta_candle_cake", + "id": -432 + }, + { + "name": "minecraft:magenta_carpet", + "id": -598 + }, + { + "name": "minecraft:magenta_concrete", + "id": -629 + }, + { + "name": "minecraft:magenta_concrete_powder", + "id": -710 + }, + { + "name": "minecraft:magenta_dye", + "id": 434 + }, + { + "name": "minecraft:magenta_glazed_terracotta", + "id": 222 + }, + { + "name": "minecraft:magenta_shulker_box", + "id": -614 + }, + { + "name": "minecraft:magenta_stained_glass", + "id": -674 + }, + { + "name": "minecraft:magenta_stained_glass_pane", + "id": -644 + }, + { + "name": "minecraft:magenta_terracotta", + "id": -725 + }, + { + "name": "minecraft:magenta_wool", + "id": -565 + }, + { + "name": "minecraft:magma", + "id": 213 + }, + { + "name": "minecraft:magma_cream", + "id": 457 + }, + { + "name": "minecraft:magma_cube_spawn_egg", + "id": 482 + }, + { + "name": "minecraft:mangrove_boat", + "id": 671 + }, + { + "name": "minecraft:mangrove_button", + "id": -487 + }, + { + "name": "minecraft:mangrove_chest_boat", + "id": 680 + }, + { + "name": "minecraft:mangrove_door", + "id": 669 + }, + { + "name": "minecraft:mangrove_double_slab", + "id": -499 + }, + { + "name": "minecraft:mangrove_fence", + "id": -491 + }, + { + "name": "minecraft:mangrove_fence_gate", + "id": -492 + }, + { + "name": "minecraft:mangrove_hanging_sign", + "id": -508 + }, + { + "name": "minecraft:mangrove_leaves", + "id": -472 + }, + { + "name": "minecraft:mangrove_log", + "id": -484 + }, + { + "name": "minecraft:mangrove_planks", + "id": -486 + }, + { + "name": "minecraft:mangrove_pressure_plate", + "id": -490 + }, + { + "name": "minecraft:mangrove_propagule", + "id": -474 + }, + { + "name": "minecraft:mangrove_roots", + "id": -482 + }, + { + "name": "minecraft:mangrove_sign", + "id": 670 + }, + { + "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:material_reducer", + "id": -986 + }, + { + "name": "minecraft:medicine", + "id": 635 + }, + { + "name": "minecraft:medium_amethyst_bud", + "id": -331 + }, + { + "name": "minecraft:melon_block", + "id": 103 + }, + { + "name": "minecraft:melon_seeds", + "id": 315 + }, + { + "name": "minecraft:melon_slice", + "id": 294 + }, + { + "name": "minecraft:melon_stem", + "id": 105 + }, + { + "name": "minecraft:milk_bucket", + "id": 386 + }, + { + "name": "minecraft:minecart", + "id": 395 + }, + { + "name": "minecraft:miner_pottery_sherd", + "id": 706 + }, + { + "name": "minecraft:mob_spawner", + "id": 52 + }, + { + "name": "minecraft:mojang_banner_pattern", + "id": 617 + }, + { + "name": "minecraft:monster_egg", + "id": 769 + }, + { + "name": "minecraft:mooshroom_spawn_egg", + "id": 467 + }, + { + "name": "minecraft:moss_block", + "id": -320 + }, + { + "name": "minecraft:moss_carpet", + "id": -335 + }, + { + "name": "minecraft:mossy_cobblestone", + "id": 48 + }, + { + "name": "minecraft:mossy_cobblestone_double_slab", + "id": -915 + }, + { + "name": "minecraft:mossy_cobblestone_slab", + "id": -888 + }, + { + "name": "minecraft:mossy_cobblestone_stairs", + "id": -179 + }, + { + "name": "minecraft:mossy_cobblestone_wall", + "id": -971 + }, + { + "name": "minecraft:mossy_stone_brick_double_slab", + "id": -168 + }, + { + "name": "minecraft:mossy_stone_brick_slab", + "id": -166 + }, + { + "name": "minecraft:mossy_stone_brick_stairs", + "id": -175 + }, + { + "name": "minecraft:mossy_stone_brick_wall", + "id": -978 + }, + { + "name": "minecraft:mossy_stone_bricks", + "id": -868 + }, + { + "name": "minecraft:mourner_pottery_sherd", + "id": 707 + }, + { + "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": 494 + }, + { + "name": "minecraft:mushroom_stem", + "id": -1008 + }, + { + "name": "minecraft:mushroom_stew", + "id": 282 + }, + { + "name": "minecraft:music_disc_11", + "id": 577 + }, + { + "name": "minecraft:music_disc_13", + "id": 567 + }, + { + "name": "minecraft:music_disc_5", + "id": 672 + }, + { + "name": "minecraft:music_disc_blocks", + "id": 569 + }, + { + "name": "minecraft:music_disc_cat", + "id": 568 + }, + { + "name": "minecraft:music_disc_chirp", + "id": 570 + }, + { + "name": "minecraft:music_disc_creator", + "id": 776 + }, + { + "name": "minecraft:music_disc_creator_music_box", + "id": 777 + }, + { + "name": "minecraft:music_disc_far", + "id": 571 + }, + { + "name": "minecraft:music_disc_mall", + "id": 572 + }, + { + "name": "minecraft:music_disc_mellohi", + "id": 573 + }, + { + "name": "minecraft:music_disc_otherside", + "id": 662 + }, + { + "name": "minecraft:music_disc_pigstep", + "id": 656 + }, + { + "name": "minecraft:music_disc_precipice", + "id": 778 + }, + { + "name": "minecraft:music_disc_relic", + "id": 735 + }, + { + "name": "minecraft:music_disc_stal", + "id": 574 + }, + { + "name": "minecraft:music_disc_strad", + "id": 575 + }, + { + "name": "minecraft:music_disc_wait", + "id": 578 + }, + { + "name": "minecraft:music_disc_ward", + "id": 576 + }, + { + "name": "minecraft:mutton", + "id": 583 + }, + { + "name": "minecraft:mycelium", + "id": 110 + }, + { + "name": "minecraft:name_tag", + "id": 581 + }, + { + "name": "minecraft:nautilus_shell", + "id": 603 + }, + { + "name": "minecraft:nether_brick", + "id": 112 + }, + { + "name": "minecraft:nether_brick_double_slab", + "id": -883 + }, + { + "name": "minecraft:nether_brick_fence", + "id": 113 + }, + { + "name": "minecraft:nether_brick_slab", + "id": -877 + }, + { + "name": "minecraft:nether_brick_stairs", + "id": 114 + }, + { + "name": "minecraft:nether_brick_wall", + "id": -979 + }, + { + "name": "minecraft:nether_gold_ore", + "id": -288 + }, + { + "name": "minecraft:nether_sprouts", + "id": 657 + }, + { + "name": "minecraft:nether_star", + "id": 551 + }, + { + "name": "minecraft:nether_wart", + "id": 316 + }, + { + "name": "minecraft:nether_wart_block", + "id": 214 + }, + { + "name": "minecraft:netherbrick", + "id": 556 + }, + { + "name": "minecraft:netherite_axe", + "id": 642 + }, + { + "name": "minecraft:netherite_block", + "id": -270 + }, + { + "name": "minecraft:netherite_boots", + "id": 648 + }, + { + "name": "minecraft:netherite_chestplate", + "id": 646 + }, + { + "name": "minecraft:netherite_helmet", + "id": 645 + }, + { + "name": "minecraft:netherite_hoe", + "id": 643 + }, + { + "name": "minecraft:netherite_ingot", + "id": 644 + }, + { + "name": "minecraft:netherite_leggings", + "id": 647 + }, + { + "name": "minecraft:netherite_pickaxe", + "id": 641 + }, + { + "name": "minecraft:netherite_scrap", + "id": 649 + }, + { + "name": "minecraft:netherite_shovel", + "id": 640 + }, + { + "name": "minecraft:netherite_sword", + "id": 639 + }, + { + "name": "minecraft:netherite_upgrade_smithing_template", + "id": 716 + }, + { + "name": "minecraft:netherrack", + "id": 87 + }, + { + "name": "minecraft:netherreactor", + "id": 247 + }, + { + "name": "minecraft:normal_stone_double_slab", + "id": -926 + }, + { + "name": "minecraft:normal_stone_slab", + "id": -899 + }, + { + "name": "minecraft:normal_stone_stairs", + "id": -180 + }, + { + "name": "minecraft:noteblock", + "id": 25 + }, + { + "name": "minecraft:npc_spawn_egg", + "id": 498 + }, + { + "name": "minecraft:oak_boat", + "id": 401 + }, + { + "name": "minecraft:oak_chest_boat", + "id": 674 + }, + { + "name": "minecraft:oak_double_slab", + "id": 157 + }, + { + "name": "minecraft:oak_fence", + "id": 85 + }, + { + "name": "minecraft:oak_hanging_sign", + "id": -500 + }, + { + "name": "minecraft:oak_leaves", + "id": 18 + }, + { + "name": "minecraft:oak_log", + "id": 17 + }, + { + "name": "minecraft:oak_planks", + "id": 5 + }, + { + "name": "minecraft:oak_sapling", + "id": 6 + }, + { + "name": "minecraft:oak_sign", + "id": 383 + }, + { + "name": "minecraft:oak_slab", + "id": 158 + }, + { + "name": "minecraft:oak_stairs", + "id": 53 + }, + { + "name": "minecraft:oak_wood", + "id": -212 + }, + { + "name": "minecraft:observer", + "id": 251 + }, + { + "name": "minecraft:obsidian", + "id": 49 + }, + { + "name": "minecraft:ocelot_spawn_egg", + "id": 478 + }, + { + "name": "minecraft:ochre_froglight", + "id": -471 + }, + { + "name": "minecraft:ominous_bottle", + "id": 628 + }, + { + "name": "minecraft:ominous_trial_key", + "id": 275 + }, + { + "name": "minecraft:orange_bundle", + "id": 268 + }, + { + "name": "minecraft:orange_candle", + "id": -414 + }, + { + "name": "minecraft:orange_candle_cake", + "id": -431 + }, + { + "name": "minecraft:orange_carpet", + "id": -597 + }, + { + "name": "minecraft:orange_concrete", + "id": -628 + }, + { + "name": "minecraft:orange_concrete_powder", + "id": -709 + }, + { + "name": "minecraft:orange_dye", + "id": 435 + }, + { + "name": "minecraft:orange_glazed_terracotta", + "id": 221 + }, + { + "name": "minecraft:orange_shulker_box", + "id": -613 + }, + { + "name": "minecraft:orange_stained_glass", + "id": -673 + }, + { + "name": "minecraft:orange_stained_glass_pane", + "id": -643 + }, + { + "name": "minecraft:orange_terracotta", + "id": -724 + }, + { + "name": "minecraft:orange_tulip", + "id": -834 + }, + { + "name": "minecraft:orange_wool", + "id": -557 + }, + { + "name": "minecraft:oxeye_daisy", + "id": -837 + }, + { + "name": "minecraft:oxidized_chiseled_copper", + "id": -763 + }, + { + "name": "minecraft:oxidized_copper", + "id": -343 + }, + { + "name": "minecraft:oxidized_copper_bulb", + "id": -779 + }, + { + "name": "minecraft:oxidized_copper_door", + "id": -787 + }, + { + "name": "minecraft:oxidized_copper_grate", + "id": -771 + }, + { + "name": "minecraft:oxidized_copper_trapdoor", + "id": -795 + }, + { + "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": 382 + }, + { + "name": "minecraft:panda_spawn_egg", + "id": 517 + }, + { + "name": "minecraft:paper", + "id": 412 + }, + { + "name": "minecraft:parrot_spawn_egg", + "id": 506 + }, + { + "name": "minecraft:pearlescent_froglight", + "id": -469 + }, + { + "name": "minecraft:peony", + "id": -867 + }, + { + "name": "minecraft:petrified_oak_double_slab", + "id": -903 + }, + { + "name": "minecraft:petrified_oak_slab", + "id": -902 + }, + { + "name": "minecraft:phantom_membrane", + "id": 607 + }, + { + "name": "minecraft:phantom_spawn_egg", + "id": 514 + }, + { + "name": "minecraft:pig_spawn_egg", + "id": 464 + }, + { + "name": "minecraft:piglin_banner_pattern", + "id": 620 + }, + { + "name": "minecraft:piglin_brute_spawn_egg", + "id": 527 + }, + { + "name": "minecraft:piglin_head", + "id": -970 + }, + { + "name": "minecraft:piglin_spawn_egg", + "id": 525 + }, + { + "name": "minecraft:pillager_spawn_egg", + "id": 519 + }, + { + "name": "minecraft:pink_bundle", + "id": 269 + }, + { + "name": "minecraft:pink_candle", + "id": -419 + }, + { + "name": "minecraft:pink_candle_cake", + "id": -436 + }, + { + "name": "minecraft:pink_carpet", + "id": -602 + }, + { + "name": "minecraft:pink_concrete", + "id": -633 + }, + { + "name": "minecraft:pink_concrete_powder", + "id": -714 + }, + { + "name": "minecraft:pink_dye", + "id": 430 + }, + { + "name": "minecraft:pink_glazed_terracotta", + "id": 226 + }, + { + "name": "minecraft:pink_petals", + "id": -549 + }, + { + "name": "minecraft:pink_shulker_box", + "id": -618 + }, + { + "name": "minecraft:pink_stained_glass", + "id": -678 + }, + { + "name": "minecraft:pink_stained_glass_pane", + "id": -648 + }, + { + "name": "minecraft:pink_terracotta", + "id": -729 + }, + { + "name": "minecraft:pink_tulip", + "id": -836 + }, + { + "name": "minecraft:pink_wool", + "id": -566 + }, + { + "name": "minecraft:piston", + "id": 33 + }, + { + "name": "minecraft:piston_arm_collision", + "id": 34 + }, + { + "name": "minecraft:pitcher_crop", + "id": -574 + }, + { + "name": "minecraft:pitcher_plant", + "id": -612 + }, + { + "name": "minecraft:pitcher_pod", + "id": 319 + }, + { + "name": "minecraft:planks", + "id": 765 + }, + { + "name": "minecraft:player_head", + "id": -967 + }, + { + "name": "minecraft:plenty_pottery_sherd", + "id": 708 + }, + { + "name": "minecraft:podzol", + "id": 243 + }, + { + "name": "minecraft:pointed_dripstone", + "id": -308 + }, + { + "name": "minecraft:poisonous_potato", + "id": 304 + }, + { + "name": "minecraft:polar_bear_spawn_egg", + "id": 500 + }, + { + "name": "minecraft:polished_andesite", + "id": -595 + }, + { + "name": "minecraft:polished_andesite_double_slab", + "id": -919 + }, + { + "name": "minecraft:polished_andesite_slab", + "id": -892 + }, + { + "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", + "id": -593 + }, + { + "name": "minecraft:polished_diorite_double_slab", + "id": -922 + }, + { + "name": "minecraft:polished_diorite_slab", + "id": -895 + }, + { + "name": "minecraft:polished_diorite_stairs", + "id": -173 + }, + { + "name": "minecraft:polished_granite", + "id": -591 + }, + { + "name": "minecraft:polished_granite_double_slab", + "id": -924 + }, + { + "name": "minecraft:polished_granite_slab", + "id": -897 + }, + { + "name": "minecraft:polished_granite_stairs", + "id": -172 + }, + { + "name": "minecraft:polished_tuff", + "id": -748 + }, + { + "name": "minecraft:polished_tuff_double_slab", + "id": -750 + }, + { + "name": "minecraft:polished_tuff_slab", + "id": -749 + }, + { + "name": "minecraft:polished_tuff_stairs", + "id": -751 + }, + { + "name": "minecraft:polished_tuff_wall", + "id": -752 + }, + { + "name": "minecraft:popped_chorus_fruit", + "id": 592 + }, + { + "name": "minecraft:poppy", + "id": 38 + }, + { + "name": "minecraft:porkchop", + "id": 284 + }, + { + "name": "minecraft:portal", + "id": 90 + }, + { + "name": "minecraft:potato", + "id": 302 + }, + { + "name": "minecraft:potatoes", + "id": 142 + }, + { + "name": "minecraft:potion", + "id": 453 + }, + { + "name": "minecraft:powder_snow", + "id": -306 + }, + { + "name": "minecraft:powder_snow_bucket", + "id": 393 + }, + { + "name": "minecraft:powered_comparator", + "id": 150 + }, + { + "name": "minecraft:powered_repeater", + "id": 94 + }, + { + "name": "minecraft:prismarine", + "id": 168 + }, + { + "name": "minecraft:prismarine_brick_double_slab", + "id": -914 + }, + { + "name": "minecraft:prismarine_brick_slab", + "id": -887 + }, + { + "name": "minecraft:prismarine_bricks", + "id": -948 + }, + { + "name": "minecraft:prismarine_bricks_stairs", + "id": -4 + }, + { + "name": "minecraft:prismarine_crystals", + "id": 582 + }, + { + "name": "minecraft:prismarine_double_slab", + "id": -912 + }, + { + "name": "minecraft:prismarine_shard", + "id": 598 + }, + { + "name": "minecraft:prismarine_slab", + "id": -885 + }, + { + "name": "minecraft:prismarine_stairs", + "id": -2 + }, + { + "name": "minecraft:prismarine_wall", + "id": -981 + }, + { + "name": "minecraft:prize_pottery_sherd", + "id": 709 + }, + { + "name": "minecraft:pufferfish", + "id": 289 + }, + { + "name": "minecraft:pufferfish_bucket", + "id": 392 + }, + { + "name": "minecraft:pufferfish_spawn_egg", + "id": 509 + }, + { + "name": "minecraft:pumpkin", + "id": 86 + }, + { + "name": "minecraft:pumpkin_pie", + "id": 306 + }, + { + "name": "minecraft:pumpkin_seeds", + "id": 314 + }, + { + "name": "minecraft:pumpkin_stem", + "id": 104 + }, + { + "name": "minecraft:purple_bundle", + "id": 270 + }, + { + "name": "minecraft:purple_candle", + "id": -423 + }, + { + "name": "minecraft:purple_candle_cake", + "id": -440 + }, + { + "name": "minecraft:purple_carpet", + "id": -606 + }, + { + "name": "minecraft:purple_concrete", + "id": -637 + }, + { + "name": "minecraft:purple_concrete_powder", + "id": -718 + }, + { + "name": "minecraft:purple_dye", + "id": 426 + }, + { + "name": "minecraft:purple_glazed_terracotta", + "id": 219 + }, + { + "name": "minecraft:purple_shulker_box", + "id": -622 + }, + { + "name": "minecraft:purple_stained_glass", + "id": -682 + }, + { + "name": "minecraft:purple_stained_glass_pane", + "id": -652 + }, + { + "name": "minecraft:purple_terracotta", + "id": -733 + }, + { + "name": "minecraft:purple_wool", + "id": -564 + }, + { + "name": "minecraft:purpur_block", + "id": 201 + }, + { + "name": "minecraft:purpur_double_slab", + "id": -911 + }, + { + "name": "minecraft:purpur_pillar", + "id": -951 + }, + { + "name": "minecraft:purpur_slab", + "id": -884 + }, + { + "name": "minecraft:purpur_stairs", + "id": 203 + }, + { + "name": "minecraft:quartz", + "id": 557 + }, + { + "name": "minecraft:quartz_block", + "id": 155 + }, + { + "name": "minecraft:quartz_bricks", + "id": -304 + }, + { + "name": "minecraft:quartz_double_slab", + "id": -882 + }, + { + "name": "minecraft:quartz_ore", + "id": 153 + }, + { + "name": "minecraft:quartz_pillar", + "id": -954 + }, + { + "name": "minecraft:quartz_slab", + "id": -876 + }, + { + "name": "minecraft:quartz_stairs", + "id": 156 + }, + { + "name": "minecraft:rabbit", + "id": 310 + }, + { + "name": "minecraft:rabbit_foot", + "id": 561 + }, + { + "name": "minecraft:rabbit_hide", + "id": 562 + }, + { + "name": "minecraft:rabbit_spawn_egg", + "id": 486 + }, + { + "name": "minecraft:rabbit_stew", + "id": 312 + }, + { + "name": "minecraft:rail", + "id": 66 + }, + { + "name": "minecraft:raiser_armor_trim_smithing_template", + "id": 730 + }, + { + "name": "minecraft:rapid_fertilizer", + "id": 633 + }, + { + "name": "minecraft:ravager_spawn_egg", + "id": 521 + }, + { + "name": "minecraft:raw_copper", + "id": 541 + }, + { + "name": "minecraft:raw_copper_block", + "id": -452 + }, + { + "name": "minecraft:raw_gold", + "id": 540 + }, + { + "name": "minecraft:raw_gold_block", + "id": -453 + }, + { + "name": "minecraft:raw_iron", + "id": 539 + }, + { + "name": "minecraft:raw_iron_block", + "id": -451 + }, + { + "name": "minecraft:recovery_compass", + "id": 682 + }, + { + "name": "minecraft:red_bundle", + "id": 271 + }, + { + "name": "minecraft:red_candle", + "id": -427 + }, + { + "name": "minecraft:red_candle_cake", + "id": -444 + }, + { + "name": "minecraft:red_carpet", + "id": -610 + }, + { + "name": "minecraft:red_concrete", + "id": -641 + }, + { + "name": "minecraft:red_concrete_powder", + "id": -722 + }, + { + "name": "minecraft:red_dye", + "id": 422 + }, + { + "name": "minecraft:red_flower", + "id": 763 + }, + { + "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_double_slab", + "id": -917 + }, + { + "name": "minecraft:red_nether_brick_slab", + "id": -890 + }, + { + "name": "minecraft:red_nether_brick_stairs", + "id": -184 + }, + { + "name": "minecraft:red_nether_brick_wall", + "id": -983 + }, + { + "name": "minecraft:red_sand", + "id": -949 + }, + { + "name": "minecraft:red_sandstone", + "id": 179 + }, + { + "name": "minecraft:red_sandstone_double_slab", + "id": 181 + }, + { + "name": "minecraft:red_sandstone_slab", + "id": 182 + }, + { + "name": "minecraft:red_sandstone_stairs", + "id": 180 + }, + { + "name": "minecraft:red_sandstone_wall", + "id": -982 + }, + { + "name": "minecraft:red_shulker_box", + "id": -626 + }, + { + "name": "minecraft:red_stained_glass", + "id": -686 + }, + { + "name": "minecraft:red_stained_glass_pane", + "id": -656 + }, + { + "name": "minecraft:red_terracotta", + "id": -737 + }, + { + "name": "minecraft:red_tulip", + "id": -833 + }, + { + "name": "minecraft:red_wool", + "id": -556 + }, + { + "name": "minecraft:redstone", + "id": 398 + }, + { + "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": 445 + }, + { + "name": "minecraft:repeating_command_block", + "id": 188 + }, + { + "name": "minecraft:reserved6", + "id": 255 + }, + { + "name": "minecraft:respawn_anchor", + "id": -272 + }, + { + "name": "minecraft:rib_armor_trim_smithing_template", + "id": 726 + }, + { + "name": "minecraft:rose_bush", + "id": -866 + }, + { + "name": "minecraft:rotten_flesh", + "id": 299 + }, + { + "name": "minecraft:saddle", + "id": 396 + }, + { + "name": "minecraft:salmon", + "id": 287 + }, + { + "name": "minecraft:salmon_bucket", + "id": 390 + }, + { + "name": "minecraft:salmon_spawn_egg", + "id": 510 + }, + { + "name": "minecraft:sand", + "id": 12 + }, + { + "name": "minecraft:sandstone", + "id": 24 + }, + { + "name": "minecraft:sandstone_double_slab", + "id": -878 + }, + { + "name": "minecraft:sandstone_slab", + "id": -872 + }, + { + "name": "minecraft:sandstone_stairs", + "id": 128 + }, + { + "name": "minecraft:sandstone_wall", + "id": -975 + }, + { + "name": "minecraft:sapling", + "id": 759 + }, + { + "name": "minecraft:scaffolding", + "id": -165 + }, + { + "name": "minecraft:scrape_pottery_sherd", + "id": 710 + }, + { + "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:sea_lantern", + "id": 169 + }, + { + "name": "minecraft:sea_pickle", + "id": -156 + }, + { + "name": "minecraft:seagrass", + "id": -130 + }, + { + "name": "minecraft:sentry_armor_trim_smithing_template", + "id": 717 + }, + { + "name": "minecraft:shaper_armor_trim_smithing_template", + "id": 731 + }, + { + "name": "minecraft:sheaf_pottery_sherd", + "id": 711 + }, + { + "name": "minecraft:shears", + "id": 447 + }, + { + "name": "minecraft:sheep_spawn_egg", + "id": 465 + }, + { + "name": "minecraft:shelter_pottery_sherd", + "id": 712 + }, + { + "name": "minecraft:shield", + "id": 380 + }, + { + "name": "minecraft:short_grass", + "id": 31 + }, + { + "name": "minecraft:shroomlight", + "id": -230 + }, + { + "name": "minecraft:shulker_box", + "id": 774 + }, + { + "name": "minecraft:shulker_shell", + "id": 599 + }, + { + "name": "minecraft:shulker_spawn_egg", + "id": 497 + }, + { + "name": "minecraft:silence_armor_trim_smithing_template", + "id": 728 + }, + { + "name": "minecraft:silver_glazed_terracotta", + "id": 228 + }, + { + "name": "minecraft:silverfish_spawn_egg", + "id": 470 + }, + { + "name": "minecraft:skeleton_horse_spawn_egg", + "id": 495 + }, + { + "name": "minecraft:skeleton_skull", + "id": 144 + }, + { + "name": "minecraft:skeleton_spawn_egg", + "id": 471 + }, + { + "name": "minecraft:skull", + "id": 736 + }, + { + "name": "minecraft:skull_banner_pattern", + "id": 616 + }, + { + "name": "minecraft:skull_pottery_sherd", + "id": 713 + }, + { + "name": "minecraft:slime", + "id": 165 + }, + { + "name": "minecraft:slime_ball", + "id": 414 + }, + { + "name": "minecraft:slime_spawn_egg", + "id": 472 + }, + { + "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", + "id": -955 + }, + { + "name": "minecraft:smooth_quartz_double_slab", + "id": -925 + }, + { + "name": "minecraft:smooth_quartz_slab", + "id": -898 + }, + { + "name": "minecraft:smooth_quartz_stairs", + "id": -185 + }, + { + "name": "minecraft:smooth_red_sandstone", + "id": -958 + }, + { + "name": "minecraft:smooth_red_sandstone_double_slab", + "id": -918 + }, + { + "name": "minecraft:smooth_red_sandstone_slab", + "id": -891 + }, + { + "name": "minecraft:smooth_red_sandstone_stairs", + "id": -176 + }, + { + "name": "minecraft:smooth_sandstone", + "id": -946 + }, + { + "name": "minecraft:smooth_sandstone_double_slab", + "id": -916 + }, + { + "name": "minecraft:smooth_sandstone_slab", + "id": -889 + }, + { + "name": "minecraft:smooth_sandstone_stairs", + "id": -177 + }, + { + "name": "minecraft:smooth_stone", + "id": -183 + }, + { + "name": "minecraft:smooth_stone_double_slab", + "id": 43 + }, + { + "name": "minecraft:smooth_stone_slab", + "id": 44 + }, + { + "name": "minecraft:sniffer_egg", + "id": -596 + }, + { + "name": "minecraft:sniffer_spawn_egg", + "id": 528 + }, + { + "name": "minecraft:snort_pottery_sherd", + "id": 714 + }, + { + "name": "minecraft:snout_armor_trim_smithing_template", + "id": 725 + }, + { + "name": "minecraft:snow", + "id": 80 + }, + { + "name": "minecraft:snow_golem_spawn_egg", + "id": 534 + }, + { + "name": "minecraft:snow_layer", + "id": 78 + }, + { + "name": "minecraft:snowball", + "id": 399 + }, + { + "name": "minecraft:soul_campfire", + "id": 658 + }, + { + "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": 636 + }, + { + "name": "minecraft:spawn_egg", + "id": 788 + }, + { + "name": "minecraft:spider_eye", + "id": 300 + }, + { + "name": "minecraft:spider_spawn_egg", + "id": 473 + }, + { + "name": "minecraft:spire_armor_trim_smithing_template", + "id": 727 + }, + { + "name": "minecraft:splash_potion", + "id": 594 + }, + { + "name": "minecraft:sponge", + "id": 19 + }, + { + "name": "minecraft:spore_blossom", + "id": -321 + }, + { + "name": "minecraft:spruce_boat", + "id": 404 + }, + { + "name": "minecraft:spruce_button", + "id": -144 + }, + { + "name": "minecraft:spruce_chest_boat", + "id": 677 + }, + { + "name": "minecraft:spruce_door", + "id": 586 + }, + { + "name": "minecraft:spruce_double_slab", + "id": -809 + }, + { + "name": "minecraft:spruce_fence", + "id": -579 + }, + { + "name": "minecraft:spruce_fence_gate", + "id": 183 + }, + { + "name": "minecraft:spruce_hanging_sign", + "id": -501 + }, + { + "name": "minecraft:spruce_leaves", + "id": -800 + }, + { + "name": "minecraft:spruce_log", + "id": -569 + }, + { + "name": "minecraft:spruce_planks", + "id": -739 + }, + { + "name": "minecraft:spruce_pressure_plate", + "id": -154 + }, + { + "name": "minecraft:spruce_sapling", + "id": -825 + }, + { + "name": "minecraft:spruce_sign", + "id": 609 + }, + { + "name": "minecraft:spruce_slab", + "id": -804 + }, + { + "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:spruce_wood", + "id": -814 + }, + { + "name": "minecraft:spyglass", + "id": 661 + }, + { + "name": "minecraft:squid_spawn_egg", + "id": 477 + }, + { + "name": "minecraft:stained_glass", + "id": 772 + }, + { + "name": "minecraft:stained_glass_pane", + "id": 773 + }, + { + "name": "minecraft:stained_hardened_clay", + "id": 737 + }, + { + "name": "minecraft:standing_banner", + "id": 176 + }, + { + "name": "minecraft:standing_sign", + "id": 63 + }, + { + "name": "minecraft:stick", + "id": 345 + }, + { + "name": "minecraft:sticky_piston", + "id": 29 + }, + { + "name": "minecraft:sticky_piston_arm_collision", + "id": -217 + }, + { + "name": "minecraft:stone", + "id": 1 + }, + { + "name": "minecraft:stone_axe", + "id": 339 + }, + { + "name": "minecraft:stone_block_slab", + "id": 749 + }, + { + "name": "minecraft:stone_block_slab2", + "id": 750 + }, + { + "name": "minecraft:stone_block_slab3", + "id": 751 + }, + { + "name": "minecraft:stone_block_slab4", + "id": 752 + }, + { + "name": "minecraft:stone_brick_double_slab", + "id": -881 + }, + { + "name": "minecraft:stone_brick_slab", + "id": -875 + }, + { + "name": "minecraft:stone_brick_stairs", + "id": 109 + }, + { + "name": "minecraft:stone_brick_wall", + "id": -977 + }, + { + "name": "minecraft:stone_bricks", + "id": 98 + }, + { + "name": "minecraft:stone_button", + "id": 77 + }, + { + "name": "minecraft:stone_hoe", + "id": 355 + }, + { + "name": "minecraft:stone_pickaxe", + "id": 338 + }, + { + "name": "minecraft:stone_pressure_plate", + "id": 70 + }, + { + "name": "minecraft:stone_shovel", + "id": 337 + }, + { + "name": "minecraft:stone_stairs", + "id": 67 + }, + { + "name": "minecraft:stone_sword", + "id": 336 + }, + { + "name": "minecraft:stonebrick", + "id": 747 + }, + { + "name": "minecraft:stonecutter", + "id": 245 + }, + { + "name": "minecraft:stonecutter_block", + "id": -197 + }, + { + "name": "minecraft:stray_spawn_egg", + "id": 489 + }, + { + "name": "minecraft:strider_spawn_egg", + "id": 523 + }, + { + "name": "minecraft:string", + "id": 351 + }, + { + "name": "minecraft:stripped_acacia_log", + "id": -8 + }, + { + "name": "minecraft:stripped_acacia_wood", + "id": -823 + }, + { + "name": "minecraft:stripped_bamboo_block", + "id": -528 + }, + { + "name": "minecraft:stripped_birch_log", + "id": -6 + }, + { + "name": "minecraft:stripped_birch_wood", + "id": -821 + }, + { + "name": "minecraft:stripped_cherry_log", + "id": -535 + }, + { + "name": "minecraft:stripped_cherry_wood", + "id": -545 + }, + { + "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_dark_oak_wood", + "id": -824 + }, + { + "name": "minecraft:stripped_jungle_log", + "id": -7 + }, + { + "name": "minecraft:stripped_jungle_wood", + "id": -822 + }, + { + "name": "minecraft:stripped_mangrove_log", + "id": -485 + }, + { + "name": "minecraft:stripped_mangrove_wood", + "id": -498 + }, + { + "name": "minecraft:stripped_oak_log", + "id": -10 + }, + { + "name": "minecraft:stripped_oak_wood", + "id": -819 + }, + { + "name": "minecraft:stripped_spruce_log", + "id": -5 + }, + { + "name": "minecraft:stripped_spruce_wood", + "id": -820 + }, + { + "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": 442 + }, + { + "name": "minecraft:sugar_cane", + "id": 411 + }, + { + "name": "minecraft:sunflower", + "id": 175 + }, + { + "name": "minecraft:suspicious_gravel", + "id": -573 + }, + { + "name": "minecraft:suspicious_sand", + "id": -529 + }, + { + "name": "minecraft:suspicious_stew", + "id": 625 + }, + { + "name": "minecraft:sweet_berries", + "id": 309 + }, + { + "name": "minecraft:sweet_berry_bush", + "id": -207 + }, + { + "name": "minecraft:tadpole_bucket", + "id": 666 + }, + { + "name": "minecraft:tadpole_spawn_egg", + "id": 665 + }, + { + "name": "minecraft:tall_grass", + "id": -864 + }, + { + "name": "minecraft:tallgrass", + "id": 767 + }, + { + "name": "minecraft:target", + "id": -239 + }, + { + "name": "minecraft:tide_armor_trim_smithing_template", + "id": 724 + }, + { + "name": "minecraft:tinted_glass", + "id": -334 + }, + { + "name": "minecraft:tnt", + "id": 46 + }, + { + "name": "minecraft:tnt_minecart", + "id": 558 + }, + { + "name": "minecraft:torch", + "id": 50 + }, + { + "name": "minecraft:torchflower", + "id": -568 + }, + { + "name": "minecraft:torchflower_crop", + "id": -567 + }, + { + "name": "minecraft:torchflower_seeds", + "id": 318 + }, + { + "name": "minecraft:totem_of_undying", + "id": 601 + }, + { + "name": "minecraft:trader_llama_spawn_egg", + "id": 684 + }, + { + "name": "minecraft:trapdoor", + "id": 96 + }, + { + "name": "minecraft:trapped_chest", + "id": 146 + }, + { + "name": "minecraft:trial_key", + "id": 276 + }, + { + "name": "minecraft:trial_spawner", + "id": -315 + }, + { + "name": "minecraft:trident", + "id": 579 + }, + { + "name": "minecraft:trip_wire", + "id": 132 + }, + { + "name": "minecraft:tripwire_hook", + "id": 131 + }, + { + "name": "minecraft:tropical_fish", + "id": 288 + }, + { + "name": "minecraft:tropical_fish_bucket", + "id": 391 + }, + { + "name": "minecraft:tropical_fish_spawn_egg", + "id": 507 + }, + { + "name": "minecraft:tube_coral", + "id": -131 + }, + { + "name": "minecraft:tube_coral_block", + "id": -132 + }, + { + "name": "minecraft:tube_coral_fan", + "id": -133 + }, + { + "name": "minecraft:tube_coral_wall_fan", + "id": -135 + }, + { + "name": "minecraft:tuff", + "id": -333 + }, + { + "name": "minecraft:tuff_brick_double_slab", + "id": -756 + }, + { + "name": "minecraft:tuff_brick_slab", + "id": -755 + }, + { + "name": "minecraft:tuff_brick_stairs", + "id": -757 + }, + { + "name": "minecraft:tuff_brick_wall", + "id": -758 + }, + { + "name": "minecraft:tuff_bricks", + "id": -754 + }, + { + "name": "minecraft:tuff_double_slab", + "id": -745 + }, + { + "name": "minecraft:tuff_slab", + "id": -744 + }, + { + "name": "minecraft:tuff_stairs", + "id": -746 + }, + { + "name": "minecraft:tuff_wall", + "id": -747 + }, + { + "name": "minecraft:turtle_egg", + "id": -159 + }, + { + "name": "minecraft:turtle_helmet", + "id": 606 + }, + { + "name": "minecraft:turtle_scute", + "id": 605 + }, + { + "name": "minecraft:turtle_spawn_egg", + "id": 513 + }, + { + "name": "minecraft:twisting_vines", + "id": -287 + }, + { + "name": "minecraft:underwater_tnt", + "id": -985 + }, + { + "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:vault", + "id": -314 + }, + { + "name": "minecraft:verdant_froglight", + "id": -470 + }, + { + "name": "minecraft:vex_armor_trim_smithing_template", + "id": 723 + }, + { + "name": "minecraft:vex_spawn_egg", + "id": 504 + }, + { + "name": "minecraft:villager_spawn_egg", + "id": 476 + }, + { + "name": "minecraft:vindicator_spawn_egg", + "id": 502 + }, + { + "name": "minecraft:vine", + "id": 106 + }, + { + "name": "minecraft:wall_banner", + "id": 177 + }, + { + "name": "minecraft:wall_sign", + "id": 68 + }, + { + "name": "minecraft:wandering_trader_spawn_egg", + "id": 520 + }, + { + "name": "minecraft:ward_armor_trim_smithing_template", + "id": 721 + }, + { + "name": "minecraft:warden_spawn_egg", + "id": 668 + }, + { + "name": "minecraft:warped_button", + "id": -261 + }, + { + "name": "minecraft:warped_door", + "id": 653 + }, + { + "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": 654 + }, + { + "name": "minecraft:warped_hanging_sign", + "id": -507 + }, + { + "name": "minecraft:warped_hyphae", + "id": -298 + }, + { + "name": "minecraft:warped_nylium", + "id": -233 + }, + { + "name": "minecraft:warped_planks", + "id": -243 + }, + { + "name": "minecraft:warped_pressure_plate", + "id": -263 + }, + { + "name": "minecraft:warped_roots", + "id": -224 + }, + { + "name": "minecraft:warped_sign", + "id": 651 + }, + { + "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": 387 + }, + { + "name": "minecraft:waterlily", + "id": 111 + }, + { + "name": "minecraft:waxed_chiseled_copper", + "id": -764 + }, + { + "name": "minecraft:waxed_copper", + "id": -344 + }, + { + "name": "minecraft:waxed_copper_bulb", + "id": -780 + }, + { + "name": "minecraft:waxed_copper_door", + "id": -788 + }, + { + "name": "minecraft:waxed_copper_grate", + "id": -772 + }, + { + "name": "minecraft:waxed_copper_trapdoor", + "id": -796 + }, + { + "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_chiseled_copper", + "id": -765 + }, + { + "name": "minecraft:waxed_exposed_copper", + "id": -345 + }, + { + "name": "minecraft:waxed_exposed_copper_bulb", + "id": -781 + }, + { + "name": "minecraft:waxed_exposed_copper_door", + "id": -789 + }, + { + "name": "minecraft:waxed_exposed_copper_grate", + "id": -773 + }, + { + "name": "minecraft:waxed_exposed_copper_trapdoor", + "id": -797 + }, + { + "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_chiseled_copper", + "id": -766 + }, + { + "name": "minecraft:waxed_oxidized_copper", + "id": -446 + }, + { + "name": "minecraft:waxed_oxidized_copper_bulb", + "id": -783 + }, + { + "name": "minecraft:waxed_oxidized_copper_door", + "id": -791 + }, + { + "name": "minecraft:waxed_oxidized_copper_grate", + "id": -775 + }, + { + "name": "minecraft:waxed_oxidized_copper_trapdoor", + "id": -799 + }, + { + "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_chiseled_copper", + "id": -767 + }, + { + "name": "minecraft:waxed_weathered_copper", + "id": -346 + }, + { + "name": "minecraft:waxed_weathered_copper_bulb", + "id": -782 + }, + { + "name": "minecraft:waxed_weathered_copper_door", + "id": -790 + }, + { + "name": "minecraft:waxed_weathered_copper_grate", + "id": -774 + }, + { + "name": "minecraft:waxed_weathered_copper_trapdoor", + "id": -798 + }, + { + "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:wayfinder_armor_trim_smithing_template", + "id": 729 + }, + { + "name": "minecraft:weathered_chiseled_copper", + "id": -762 + }, + { + "name": "minecraft:weathered_copper", + "id": -342 + }, + { + "name": "minecraft:weathered_copper_bulb", + "id": -778 + }, + { + "name": "minecraft:weathered_copper_door", + "id": -786 + }, + { + "name": "minecraft:weathered_copper_grate", + "id": -770 + }, + { + "name": "minecraft:weathered_copper_trapdoor", + "id": -794 + }, + { + "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:wet_sponge", + "id": -984 + }, + { + "name": "minecraft:wheat", + "id": 359 + }, + { + "name": "minecraft:wheat_seeds", + "id": 313 + }, + { + "name": "minecraft:white_bundle", + "id": 272 + }, + { + "name": "minecraft:white_candle", + "id": -413 + }, + { + "name": "minecraft:white_candle_cake", + "id": -430 + }, + { + "name": "minecraft:white_carpet", + "id": 171 + }, + { + "name": "minecraft:white_concrete", + "id": 236 + }, + { + "name": "minecraft:white_concrete_powder", + "id": 237 + }, + { + "name": "minecraft:white_dye", + "id": 436 + }, + { + "name": "minecraft:white_glazed_terracotta", + "id": 220 + }, + { + "name": "minecraft:white_shulker_box", + "id": 218 + }, + { + "name": "minecraft:white_stained_glass", + "id": 241 + }, + { + "name": "minecraft:white_stained_glass_pane", + "id": 160 + }, + { + "name": "minecraft:white_terracotta", + "id": 159 + }, + { + "name": "minecraft:white_tulip", + "id": -835 + }, + { + "name": "minecraft:white_wool", + "id": 35 + }, + { + "name": "minecraft:wild_armor_trim_smithing_template", + "id": 720 + }, + { + "name": "minecraft:wind_charge", + "id": 277 + }, + { + "name": "minecraft:witch_spawn_egg", + "id": 479 + }, + { + "name": "minecraft:wither_rose", + "id": -216 + }, + { + "name": "minecraft:wither_skeleton_skull", + "id": -965 + }, + { + "name": "minecraft:wither_skeleton_spawn_egg", + "id": 492 + }, + { + "name": "minecraft:wither_spawn_egg", + "id": 536 + }, + { + "name": "minecraft:wolf_armor", + "id": 740 + }, + { + "name": "minecraft:wolf_spawn_egg", + "id": 466 + }, + { + "name": "minecraft:wood", + "id": 775 + }, + { + "name": "minecraft:wooden_axe", + "id": 335 + }, + { + "name": "minecraft:wooden_button", + "id": 143 + }, + { + "name": "minecraft:wooden_door", + "id": 384 + }, + { + "name": "minecraft:wooden_hoe", + "id": 354 + }, + { + "name": "minecraft:wooden_pickaxe", + "id": 334 + }, + { + "name": "minecraft:wooden_pressure_plate", + "id": 72 + }, + { + "name": "minecraft:wooden_shovel", + "id": 333 + }, + { + "name": "minecraft:wooden_slab", + "id": 762 + }, + { + "name": "minecraft:wooden_sword", + "id": 332 + }, + { + "name": "minecraft:wool", + "id": 743 + }, + { + "name": "minecraft:writable_book", + "id": 544 + }, + { + "name": "minecraft:written_book", + "id": 545 + }, + { + "name": "minecraft:yellow_bundle", + "id": 273 + }, + { + "name": "minecraft:yellow_candle", + "id": -417 + }, + { + "name": "minecraft:yellow_candle_cake", + "id": -434 + }, + { + "name": "minecraft:yellow_carpet", + "id": -600 + }, + { + "name": "minecraft:yellow_concrete", + "id": -631 + }, + { + "name": "minecraft:yellow_concrete_powder", + "id": -712 + }, + { + "name": "minecraft:yellow_dye", + "id": 432 + }, + { + "name": "minecraft:yellow_glazed_terracotta", + "id": 224 + }, + { + "name": "minecraft:yellow_shulker_box", + "id": -616 + }, + { + "name": "minecraft:yellow_stained_glass", + "id": -676 + }, + { + "name": "minecraft:yellow_stained_glass_pane", + "id": -646 + }, + { + "name": "minecraft:yellow_terracotta", + "id": -727 + }, + { + "name": "minecraft:yellow_wool", + "id": -558 + }, + { + "name": "minecraft:zoglin_spawn_egg", + "id": 526 + }, + { + "name": "minecraft:zombie_head", + "id": -966 + }, + { + "name": "minecraft:zombie_horse_spawn_egg", + "id": 496 + }, + { + "name": "minecraft:zombie_pigman_spawn_egg", + "id": 475 + }, + { + "name": "minecraft:zombie_spawn_egg", + "id": 474 + }, + { + "name": "minecraft:zombie_villager_spawn_egg", + "id": 505 + } +] \ No newline at end of file diff --git a/core/src/main/resources/mappings b/core/src/main/resources/mappings index 93f207e7e..57d204567 160000 --- a/core/src/main/resources/mappings +++ b/core/src/main/resources/mappings @@ -1 +1 @@ -Subproject commit 93f207e7e9d73f58a7c8902f7deda9dcb0524c8e +Subproject commit 57d2045675fc6f456729a89e39c0e676172510be diff --git a/core/src/test/java/org/geysermc/geyser/scoreboard/network/NameVisibilityScoreboardTest.java b/core/src/test/java/org/geysermc/geyser/scoreboard/network/NameVisibilityScoreboardTest.java new file mode 100644 index 000000000..29882ca2e --- /dev/null +++ b/core/src/test/java/org/geysermc/geyser/scoreboard/network/NameVisibilityScoreboardTest.java @@ -0,0 +1,270 @@ +/* + * Copyright (c) 2024 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.scoreboard.network; + +import static org.geysermc.geyser.scoreboard.network.util.AssertUtils.assertNextPacket; +import static org.geysermc.geyser.scoreboard.network.util.AssertUtils.assertNoNextPacket; +import static org.geysermc.geyser.scoreboard.network.util.GeyserMockContextScoreboard.spawnPlayerSilently; +import static org.geysermc.geyser.scoreboard.network.util.GeyserMockContextScoreboard.mockContextScoreboard; + +import net.kyori.adventure.text.Component; +import org.cloudburstmc.protocol.bedrock.data.entity.EntityDataTypes; +import org.cloudburstmc.protocol.bedrock.packet.SetEntityDataPacket; +import org.geysermc.geyser.translator.protocol.java.scoreboard.JavaSetPlayerTeamTranslator; +import org.geysermc.mcprotocollib.protocol.data.game.scoreboard.CollisionRule; +import org.geysermc.mcprotocollib.protocol.data.game.scoreboard.NameTagVisibility; +import org.geysermc.mcprotocollib.protocol.data.game.scoreboard.TeamAction; +import org.geysermc.mcprotocollib.protocol.data.game.scoreboard.TeamColor; +import org.geysermc.mcprotocollib.protocol.packet.ingame.clientbound.scoreboard.ClientboundSetPlayerTeamPacket; +import org.junit.jupiter.api.Test; + +public class NameVisibilityScoreboardTest { + @Test + void playerVisibilityNever() { + mockContextScoreboard(context -> { + var setPlayerTeamTranslator = new JavaSetPlayerTeamTranslator(); + + spawnPlayerSilently(context, "player1", 2); + + context.translate( + setPlayerTeamTranslator, + new ClientboundSetPlayerTeamPacket( + "team1", + Component.text("displayName"), + Component.text("prefix"), + Component.text("suffix"), + false, + false, + NameTagVisibility.NEVER, + CollisionRule.NEVER, + TeamColor.DARK_RED, + new String[]{"player1"} + ) + ); + assertNextPacket(context, () -> { + var packet = new SetEntityDataPacket(); + packet.setRuntimeEntityId(2); + packet.getMetadata().put(EntityDataTypes.NAME, ""); + return packet; + }); + }); + } + + @Test + void playerVisibilityHideForOtherTeam() { + mockContextScoreboard(context -> { + var setPlayerTeamTranslator = new JavaSetPlayerTeamTranslator(); + + spawnPlayerSilently(context, "player1", 2); + + context.translate( + setPlayerTeamTranslator, + new ClientboundSetPlayerTeamPacket( + "team1", + Component.text("displayName"), + Component.text("prefix"), + Component.text("suffix"), + false, + false, + NameTagVisibility.HIDE_FOR_OTHER_TEAMS, + CollisionRule.NEVER, + TeamColor.DARK_RED, + new String[]{"player1"} + ) + ); + // only hidden if session player (Tim203) is in a team as well + assertNextPacket(context, () -> { + var packet = new SetEntityDataPacket(); + packet.setRuntimeEntityId(2); + packet.getMetadata().put(EntityDataTypes.NAME, "§4prefix§r§4player1§r§4suffix"); + return packet; + }); + assertNoNextPacket(context); + + // create another team and add Tim203 to it + context.translate( + setPlayerTeamTranslator, + new ClientboundSetPlayerTeamPacket( + "team2", + Component.text("displayName"), + Component.text("prefix"), + Component.text("suffix"), + false, + false, + NameTagVisibility.NEVER, + CollisionRule.NEVER, + TeamColor.DARK_RED, + new String[]{"Tim203"} + ) + ); + // Tim203 is now in another team, so it should be hidden + assertNextPacket(context, () -> { + var packet = new SetEntityDataPacket(); + packet.setRuntimeEntityId(2); + packet.getMetadata().put(EntityDataTypes.NAME, ""); + return packet; + }); + assertNoNextPacket(context); + + // add Tim203 to same team as player1, score should be visible again + context.translate( + setPlayerTeamTranslator, + new ClientboundSetPlayerTeamPacket("team1", TeamAction.ADD_PLAYER, new String[]{"Tim203"}) + ); + assertNextPacket(context, () -> { + var packet = new SetEntityDataPacket(); + packet.setRuntimeEntityId(2); + packet.getMetadata().put(EntityDataTypes.NAME, "§4prefix§r§4player1§r§4suffix"); + return packet; + }); + }); + } + + @Test + void playerVisibilityHideForOwnTeam() { + mockContextScoreboard(context -> { + var setPlayerTeamTranslator = new JavaSetPlayerTeamTranslator(); + + spawnPlayerSilently(context, "player1", 2); + + context.translate( + setPlayerTeamTranslator, + new ClientboundSetPlayerTeamPacket( + "team1", + Component.text("displayName"), + Component.text("prefix"), + Component.text("suffix"), + false, + false, + NameTagVisibility.HIDE_FOR_OWN_TEAM, + CollisionRule.NEVER, + TeamColor.DARK_RED, + new String[]{"player1"} + ) + ); + // Tim203 is not in a team (let alone the same team), so should be visible + assertNextPacket(context, () -> { + var packet = new SetEntityDataPacket(); + packet.setRuntimeEntityId(2); + packet.getMetadata().put(EntityDataTypes.NAME, "§4prefix§r§4player1§r§4suffix"); + return packet; + }); + assertNoNextPacket(context); + + // Tim203 is now in the same team as player1, so should be hidden + context.translate( + setPlayerTeamTranslator, + new ClientboundSetPlayerTeamPacket("team1", TeamAction.ADD_PLAYER, new String[]{"Tim203"}) + ); + assertNextPacket(context, () -> { + var packet = new SetEntityDataPacket(); + packet.setRuntimeEntityId(2); + packet.getMetadata().put(EntityDataTypes.NAME, ""); + return packet; + }); + assertNoNextPacket(context); + + // create another team and add Tim203 to there, score should be visible again + context.translate( + setPlayerTeamTranslator, + new ClientboundSetPlayerTeamPacket( + "team2", + Component.text("displayName"), + Component.text("prefix"), + Component.text("suffix"), + false, + false, + NameTagVisibility.NEVER, + CollisionRule.NEVER, + TeamColor.DARK_RED, + new String[]{"Tim203"} + ) + ); + assertNextPacket(context, () -> { + var packet = new SetEntityDataPacket(); + packet.setRuntimeEntityId(2); + packet.getMetadata().put(EntityDataTypes.NAME, "§4prefix§r§4player1§r§4suffix"); + return packet; + }); + }); + } + + @Test + void playerVisibilityAlways() { + mockContextScoreboard(context -> { + var setPlayerTeamTranslator = new JavaSetPlayerTeamTranslator(); + + spawnPlayerSilently(context, "player1", 2); + + context.translate( + setPlayerTeamTranslator, + new ClientboundSetPlayerTeamPacket( + "team1", + Component.text("displayName"), + Component.text("prefix"), + Component.text("suffix"), + false, + false, + NameTagVisibility.ALWAYS, + CollisionRule.NEVER, + TeamColor.DARK_RED, + new String[]{"player1"} + ) + ); + assertNextPacket(context, () -> { + var packet = new SetEntityDataPacket(); + packet.setRuntimeEntityId(2); + packet.getMetadata().put(EntityDataTypes.NAME, "§4prefix§r§4player1§r§4suffix"); + return packet; + }); + + // adding self to another team shouldn't make a difference + context.translate( + setPlayerTeamTranslator, + new ClientboundSetPlayerTeamPacket( + "team2", + Component.text("displayName"), + Component.text("prefix"), + Component.text("suffix"), + false, + false, + NameTagVisibility.ALWAYS, + CollisionRule.NEVER, + TeamColor.DARK_RED, + new String[]{"Tim203"} + ) + ); + assertNoNextPacket(context); + + // adding self to player1 team shouldn't matter + context.translate( + setPlayerTeamTranslator, + new ClientboundSetPlayerTeamPacket("team1", TeamAction.ADD_PLAYER, new String[]{"Tim203"}) + ); + assertNoNextPacket(context); + }); + } +} diff --git a/core/src/test/java/org/geysermc/geyser/scoreboard/network/ScoreboardIssueTests.java b/core/src/test/java/org/geysermc/geyser/scoreboard/network/ScoreboardIssueTests.java new file mode 100644 index 000000000..7ab518f1d --- /dev/null +++ b/core/src/test/java/org/geysermc/geyser/scoreboard/network/ScoreboardIssueTests.java @@ -0,0 +1,258 @@ +/* + * Copyright (c) 2024 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.scoreboard.network; + +import net.kyori.adventure.text.Component; +import org.cloudburstmc.protocol.bedrock.data.entity.EntityDataTypes; +import org.cloudburstmc.protocol.bedrock.packet.AddEntityPacket; +import org.cloudburstmc.protocol.bedrock.packet.AddPlayerPacket; +import org.cloudburstmc.protocol.bedrock.packet.MoveEntityAbsolutePacket; +import org.cloudburstmc.protocol.bedrock.packet.PlayerListPacket; +import org.cloudburstmc.protocol.bedrock.packet.RemoveEntityPacket; +import org.cloudburstmc.protocol.bedrock.packet.SetEntityDataPacket; +import org.geysermc.geyser.entity.type.living.monster.EnderDragonPartEntity; +import org.geysermc.geyser.session.cache.EntityCache; +import org.geysermc.geyser.translator.protocol.java.entity.JavaRemoveEntitiesTranslator; +import org.geysermc.geyser.translator.protocol.java.entity.JavaSetEntityDataTranslator; +import org.geysermc.geyser.translator.protocol.java.entity.player.JavaPlayerInfoUpdateTranslator; +import org.geysermc.geyser.translator.protocol.java.entity.spawn.JavaAddEntityTranslator; +import org.geysermc.geyser.translator.protocol.java.entity.spawn.JavaAddExperienceOrbTranslator; +import org.geysermc.geyser.translator.protocol.java.scoreboard.JavaSetPlayerTeamTranslator; +import org.geysermc.mcprotocollib.auth.GameProfile; +import org.geysermc.mcprotocollib.protocol.data.game.PlayerListEntry; +import org.geysermc.mcprotocollib.protocol.data.game.PlayerListEntryAction; +import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.EntityMetadata; +import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.MetadataType; +import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.type.BooleanEntityMetadata; +import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.type.ByteEntityMetadata; +import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.type.ObjectEntityMetadata; +import org.geysermc.mcprotocollib.protocol.data.game.entity.player.GameMode; +import org.geysermc.mcprotocollib.protocol.data.game.entity.type.EntityType; +import org.geysermc.mcprotocollib.protocol.data.game.scoreboard.CollisionRule; +import org.geysermc.mcprotocollib.protocol.data.game.scoreboard.NameTagVisibility; +import org.geysermc.mcprotocollib.protocol.data.game.scoreboard.TeamAction; +import org.geysermc.mcprotocollib.protocol.data.game.scoreboard.TeamColor; +import org.geysermc.mcprotocollib.protocol.packet.ingame.clientbound.ClientboundPlayerInfoUpdatePacket; +import org.geysermc.mcprotocollib.protocol.packet.ingame.clientbound.entity.ClientboundRemoveEntitiesPacket; +import org.geysermc.mcprotocollib.protocol.packet.ingame.clientbound.entity.ClientboundSetEntityDataPacket; +import org.geysermc.mcprotocollib.protocol.packet.ingame.clientbound.entity.spawn.ClientboundAddEntityPacket; +import org.geysermc.mcprotocollib.protocol.packet.ingame.clientbound.entity.spawn.ClientboundAddExperienceOrbPacket; +import org.geysermc.mcprotocollib.protocol.packet.ingame.clientbound.scoreboard.ClientboundSetPlayerTeamPacket; +import org.junit.jupiter.api.Test; + +import java.util.EnumSet; +import java.util.Optional; +import java.util.UUID; + +import static org.geysermc.geyser.scoreboard.network.util.AssertUtils.*; +import static org.geysermc.geyser.scoreboard.network.util.GeyserMockContextScoreboard.mockContextScoreboard; +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; +import static org.junit.jupiter.api.Assertions.assertEquals; + +/** + * Tests for issues reported on GitHub. + */ +public class ScoreboardIssueTests { + /** + * Test for #5075 + */ + @Test + void entityWithoutUuid() { + // experience orbs are the only known entities without an uuid, see Entity#teamIdentifier for more info + mockContextScoreboard(context -> { + var addExperienceOrbTranslator = new JavaAddExperienceOrbTranslator(); + var removeEntitiesTranslator = new JavaRemoveEntitiesTranslator(); + + // Entity#teamIdentifier used to throw because it returned uuid.toString where uuid could be null. + // this would result in both EntityCache#spawnEntity and EntityCache#removeEntity throwing an exception, + // because the entity would be registered and deregistered to the scoreboard. + assertDoesNotThrow(() -> { + context.translate(addExperienceOrbTranslator, new ClientboundAddExperienceOrbPacket(2, 0, 0, 0, 1)); + + String displayName = context.mockOrSpy(EntityCache.class).getEntityByJavaId(2).getDisplayName(); + assertEquals("entity.minecraft.experience_orb", displayName); + + context.translate(removeEntitiesTranslator, new ClientboundRemoveEntitiesPacket(new int[] { 2 })); + }); + + // we know that spawning and removing the entity should be fine + assertNextPacketType(context, AddEntityPacket.class); + assertNextPacketType(context, RemoveEntityPacket.class); + }); + } + + /** + * Test for #5078 + */ + @Test + void entityWithoutType() { + // dragon entity parts are an entity in Geyser, but do not have an entity type + mockContextScoreboard(context -> { + // EntityUtils#translatedEntityName used to not take null EntityType's into account, + // so it used to throw an exception + assertDoesNotThrow(() -> { + // dragon entity parts are not spawned using a packet, so we manually create an instance + var dragonHeadPart = new EnderDragonPartEntity(context.session(), 2, 2, 1, 1); + + String displayName = dragonHeadPart.getDisplayName(); + assertEquals("entity.unregistered_sadface", displayName); + }); + }); + } + + /** + * Test for #5089. + * It follows the reproduction steps with all the packets it sends along its way. + * Tested with the 2.0.0-SNAPSHOT version. + * Note that this exact issue is actually 2 issues: + *
    + *
  • + * An issue caused by remainders of code that was part of the initial PR that added support for players. + * The code is now more streamlined. + *
  • + *
  • Armor stands are excluded from team visibility checks (the only living entity)
  • + *
+ */ + @Test + void nameNotUpdating() { + mockContextScoreboard(context -> { + var playerInfoUpdateTranslator = new JavaPlayerInfoUpdateTranslator(); + var setPlayerTeamTranslator = new JavaSetPlayerTeamTranslator(); + var addEntityTranslator = new JavaAddEntityTranslator(); + var setEntityDataTranslator = new JavaSetEntityDataTranslator(); + + + // first command, create NPC + + + var npcUuid = UUID.fromString("b0eb01d7-52c9-4730-9fd3-2c03fcb00d6e"); + context.translate( + playerInfoUpdateTranslator, + new ClientboundPlayerInfoUpdatePacket( + EnumSet.of(PlayerListEntryAction.ADD_PLAYER, PlayerListEntryAction.UPDATE_LISTED), + new PlayerListEntry[] { + new PlayerListEntry(npcUuid, new GameProfile(npcUuid, "1297"), false, 0, GameMode.SURVIVAL, null, 0, null, 0, null, null) + })); + + //todo we don't have to remove an entry that was never in the playerlist in the first place + assertNextPacket(context, () -> { + var packet = new PlayerListPacket(); + packet.getEntries().add(new PlayerListPacket.Entry(npcUuid)); + packet.setAction(PlayerListPacket.Action.REMOVE); + return packet; + }); + assertNoNextPacket(context); + + context.translate( + setPlayerTeamTranslator, + new ClientboundSetPlayerTeamPacket( + "npc_team_1297", + Component.empty(), + Component.empty(), + Component.empty(), + false, + false, + NameTagVisibility.NEVER, + CollisionRule.NEVER, + TeamColor.WHITE, + new String[0] + ) + ); + context.translate( + setPlayerTeamTranslator, + new ClientboundSetPlayerTeamPacket("npc_team_1297", TeamAction.ADD_PLAYER, new String[]{ "1297" })); + + context.translate(addEntityTranslator, new ClientboundAddEntityPacket(1297, npcUuid, EntityType.PLAYER, 1, 2, 3, 4, 5, 6)); + // then it updates the displayed skin parts, which isn't relevant for us + + assertNextPacketMatch(context, AddPlayerPacket.class, packet -> { + assertEquals(3, packet.getRuntimeEntityId()); + assertEquals(npcUuid, packet.getUuid()); + assertEquals("1297", packet.getUsername()); + assertEquals((byte) 1, packet.getMetadata().get(EntityDataTypes.NAMETAG_ALWAYS_SHOW)); + assertEquals("", packet.getMetadata().get(EntityDataTypes.NAME)); + }); + assertNoNextPacket(context); + + + // second command, create hologram + + + var hologramUuid = UUID.fromString("b1586291-5f68-44dc-847d-6c123c5b8cbf"); + context.translate( + addEntityTranslator, + new ClientboundAddEntityPacket(1298, hologramUuid, EntityType.ARMOR_STAND, 6, 5, 4, 3, 2, 1)); + + assertNextPacketMatch(context, AddEntityPacket.class, packet -> { + assertEquals(4, packet.getRuntimeEntityId()); + assertEquals("minecraft:armor_stand", packet.getIdentifier()); + }); + + // metadata set: invisible, custom name, custom name visible + context.translate(setEntityDataTranslator, new ClientboundSetEntityDataPacket(1298, new EntityMetadata[]{ + new ByteEntityMetadata(0, MetadataType.BYTE, (byte) 0x20), + new ObjectEntityMetadata<>(2, MetadataType.OPTIONAL_CHAT, Optional.of(Component.text("tesss"))), + new BooleanEntityMetadata(3, MetadataType.BOOLEAN, true) + })); + + assertNextPacketMatch(context, SetEntityDataPacket.class, packet -> { + assertEquals(4, packet.getRuntimeEntityId()); + var metadata = packet.getMetadata(); + assertEquals(0.0f, metadata.get(EntityDataTypes.SCALE)); + assertEquals("tesss", metadata.get(EntityDataTypes.NAME)); + assertEquals((byte) 1, metadata.get(EntityDataTypes.NAMETAG_ALWAYS_SHOW)); + }); + // because the armor stand turned invisible and has a nametag (nametag is hidden when invisible) + assertNextPacketType(context, MoveEntityAbsolutePacket.class); + + context.translate( + setPlayerTeamTranslator, + new ClientboundSetPlayerTeamPacket( + "npc_team_1298", + Component.empty(), + Component.empty(), + Component.empty(), + false, + false, + NameTagVisibility.NEVER, + CollisionRule.NEVER, + TeamColor.WHITE, + new String[0] + ) + ); + context.translate( + setPlayerTeamTranslator, + new ClientboundSetPlayerTeamPacket("npc_team_1298", TeamAction.ADD_PLAYER, new String[]{ hologramUuid.toString() })); + + assertNextPacket(context, () -> { + var packet = new SetEntityDataPacket(); + packet.getMetadata().put(EntityDataTypes.NAME, "§f§r§ftesss§r§f"); + packet.setRuntimeEntityId(4); + return packet; + }); + }); + } +} diff --git a/core/src/test/java/org/geysermc/geyser/scoreboard/network/belowname/BasicBelownameScoreboardTests.java b/core/src/test/java/org/geysermc/geyser/scoreboard/network/belowname/BasicBelownameScoreboardTests.java new file mode 100644 index 000000000..dfe85a0ee --- /dev/null +++ b/core/src/test/java/org/geysermc/geyser/scoreboard/network/belowname/BasicBelownameScoreboardTests.java @@ -0,0 +1,227 @@ +/* + * Copyright (c) 2024 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.scoreboard.network.belowname; + +import static org.geysermc.geyser.scoreboard.network.util.AssertUtils.assertNextPacket; +import static org.geysermc.geyser.scoreboard.network.util.AssertUtils.assertNoNextPacket; +import static org.geysermc.geyser.scoreboard.network.util.GeyserMockContextScoreboard.spawnPlayerSilently; +import static org.geysermc.geyser.scoreboard.network.util.GeyserMockContextScoreboard.mockContextScoreboard; + +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.format.NamedTextColor; +import org.cloudburstmc.protocol.bedrock.data.entity.EntityDataTypes; +import org.cloudburstmc.protocol.bedrock.packet.SetEntityDataPacket; +import org.geysermc.geyser.translator.protocol.java.scoreboard.JavaSetDisplayObjectiveTranslator; +import org.geysermc.geyser.translator.protocol.java.scoreboard.JavaSetObjectiveTranslator; +import org.geysermc.mcprotocollib.protocol.data.game.scoreboard.ObjectiveAction; +import org.geysermc.mcprotocollib.protocol.data.game.scoreboard.ScoreType; +import org.geysermc.mcprotocollib.protocol.data.game.scoreboard.ScoreboardPosition; +import org.geysermc.mcprotocollib.protocol.packet.ingame.clientbound.scoreboard.ClientboundSetDisplayObjectivePacket; +import org.geysermc.mcprotocollib.protocol.packet.ingame.clientbound.scoreboard.ClientboundSetObjectivePacket; +import org.junit.jupiter.api.Test; + +public class BasicBelownameScoreboardTests { + @Test + void displayWithNoPlayersAndRemove() { + mockContextScoreboard(context -> { + var setObjectiveTranslator = new JavaSetObjectiveTranslator(); + var setDisplayObjectiveTranslator = new JavaSetDisplayObjectiveTranslator(); + + context.translate( + setObjectiveTranslator, + new ClientboundSetObjectivePacket( + "objective", + ObjectiveAction.ADD, + Component.text("objective"), + ScoreType.INTEGER, + null + ) + ); + + context.translate( + setDisplayObjectiveTranslator, + new ClientboundSetDisplayObjectivePacket(ScoreboardPosition.BELOW_NAME, "objective") + ); + + context.translate( + setDisplayObjectiveTranslator, + new ClientboundSetDisplayObjectivePacket(ScoreboardPosition.BELOW_NAME, "") + ); + assertNoNextPacket(context); + }); + } + + @Test + void displayColorWithOnePlayer() { + mockContextScoreboard(context -> { + var setObjectiveTranslator = new JavaSetObjectiveTranslator(); + var setDisplayObjectiveTranslator = new JavaSetDisplayObjectiveTranslator(); + + spawnPlayerSilently(context, "player1", 2); + + context.translate( + setObjectiveTranslator, + new ClientboundSetObjectivePacket( + "objective", + ObjectiveAction.ADD, + Component.text("objective", NamedTextColor.BLUE), + ScoreType.INTEGER, + null + ) + ); + assertNoNextPacket(context); + + context.translate( + setDisplayObjectiveTranslator, + new ClientboundSetDisplayObjectivePacket(ScoreboardPosition.BELOW_NAME, "objective") + ); + assertNextPacket(context, () -> { + var packet = new SetEntityDataPacket(); + packet.setRuntimeEntityId(2); + packet.getMetadata().put(EntityDataTypes.SCORE, "0 §r§9objective"); + return packet; + }); + }); + } + + @Test + void displayWithOnePlayerAndRemove() { + mockContextScoreboard(context -> { + var setObjectiveTranslator = new JavaSetObjectiveTranslator(); + var setDisplayObjectiveTranslator = new JavaSetDisplayObjectiveTranslator(); + + spawnPlayerSilently(context, "player1", 2); + + context.translate( + setObjectiveTranslator, + new ClientboundSetObjectivePacket( + "objective", + ObjectiveAction.ADD, + Component.text("objective"), + ScoreType.INTEGER, + null + ) + ); + assertNoNextPacket(context); + + context.translate( + setDisplayObjectiveTranslator, + new ClientboundSetDisplayObjectivePacket(ScoreboardPosition.BELOW_NAME, "objective") + ); + assertNextPacket(context, () -> { + var packet = new SetEntityDataPacket(); + packet.setRuntimeEntityId(2); + packet.getMetadata().put(EntityDataTypes.SCORE, "0 §robjective"); + return packet; + }); + assertNoNextPacket(context); + + context.translate( + setDisplayObjectiveTranslator, + new ClientboundSetDisplayObjectivePacket(ScoreboardPosition.BELOW_NAME, "") + ); + assertNextPacket(context, () -> { + var packet = new SetEntityDataPacket(); + packet.setRuntimeEntityId(2); + packet.getMetadata().put(EntityDataTypes.SCORE, ""); + return packet; + }); + }); + } + + @Test + void overrideAndRemove() { + mockContextScoreboard(context -> { + var setObjectiveTranslator = new JavaSetObjectiveTranslator(); + var setDisplayObjectiveTranslator = new JavaSetDisplayObjectiveTranslator(); + + spawnPlayerSilently(context, "player1", 2); + + context.translate( + setObjectiveTranslator, + new ClientboundSetObjectivePacket( + "objective1", + ObjectiveAction.ADD, + Component.text("objective1"), + ScoreType.INTEGER, + null + ) + ); + context.translate( + setObjectiveTranslator, + new ClientboundSetObjectivePacket( + "objective2", + ObjectiveAction.ADD, + Component.text("objective2"), + ScoreType.INTEGER, + null + ) + ); + assertNoNextPacket(context); + + context.translate( + setDisplayObjectiveTranslator, + new ClientboundSetDisplayObjectivePacket(ScoreboardPosition.BELOW_NAME, "objective2") + ); + assertNextPacket(context, () -> { + var packet = new SetEntityDataPacket(); + packet.setRuntimeEntityId(2); + packet.getMetadata().put(EntityDataTypes.SCORE, "0 §robjective2"); + return packet; + }); + assertNoNextPacket(context); + + context.translate( + setDisplayObjectiveTranslator, + new ClientboundSetDisplayObjectivePacket(ScoreboardPosition.BELOW_NAME, "objective1") + ); + assertNextPacket(context, () -> { + var packet = new SetEntityDataPacket(); + packet.setRuntimeEntityId(2); + packet.getMetadata().put(EntityDataTypes.SCORE, ""); + return packet; + }); + assertNextPacket(context, () -> { + var packet = new SetEntityDataPacket(); + packet.setRuntimeEntityId(2); + packet.getMetadata().put(EntityDataTypes.SCORE, "0 §robjective1"); + return packet; + }); + assertNoNextPacket(context); + + context.translate( + setDisplayObjectiveTranslator, + new ClientboundSetDisplayObjectivePacket(ScoreboardPosition.BELOW_NAME, "") + ); + assertNextPacket(context, () -> { + var packet = new SetEntityDataPacket(); + packet.setRuntimeEntityId(2); + packet.getMetadata().put(EntityDataTypes.SCORE, ""); + return packet; + }); + }); + } +} diff --git a/core/src/test/java/org/geysermc/geyser/scoreboard/network/playerlist/BasicPlayerlistScoreboardTests.java b/core/src/test/java/org/geysermc/geyser/scoreboard/network/playerlist/BasicPlayerlistScoreboardTests.java new file mode 100644 index 000000000..4ac5ee098 --- /dev/null +++ b/core/src/test/java/org/geysermc/geyser/scoreboard/network/playerlist/BasicPlayerlistScoreboardTests.java @@ -0,0 +1,203 @@ +/* + * Copyright (c) 2024 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.scoreboard.network.playerlist; + +import static org.geysermc.geyser.scoreboard.network.util.AssertUtils.assertNextPacket; +import static org.geysermc.geyser.scoreboard.network.util.AssertUtils.assertNoNextPacket; +import static org.geysermc.geyser.scoreboard.network.util.GeyserMockContextScoreboard.mockContextScoreboard; + +import java.util.List; +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.format.NamedTextColor; +import net.kyori.adventure.text.format.TextDecoration; +import org.cloudburstmc.protocol.bedrock.data.ScoreInfo; +import org.cloudburstmc.protocol.bedrock.packet.RemoveObjectivePacket; +import org.cloudburstmc.protocol.bedrock.packet.SetDisplayObjectivePacket; +import org.cloudburstmc.protocol.bedrock.packet.SetScorePacket; +import org.geysermc.geyser.translator.protocol.java.scoreboard.JavaSetDisplayObjectiveTranslator; +import org.geysermc.geyser.translator.protocol.java.scoreboard.JavaSetObjectiveTranslator; +import org.geysermc.geyser.translator.protocol.java.scoreboard.JavaSetScoreTranslator; +import org.geysermc.mcprotocollib.protocol.data.game.scoreboard.ObjectiveAction; +import org.geysermc.mcprotocollib.protocol.data.game.scoreboard.ScoreType; +import org.geysermc.mcprotocollib.protocol.data.game.scoreboard.ScoreboardPosition; +import org.geysermc.mcprotocollib.protocol.packet.ingame.clientbound.scoreboard.ClientboundSetDisplayObjectivePacket; +import org.geysermc.mcprotocollib.protocol.packet.ingame.clientbound.scoreboard.ClientboundSetObjectivePacket; +import org.geysermc.mcprotocollib.protocol.packet.ingame.clientbound.scoreboard.ClientboundSetScorePacket; +import org.junit.jupiter.api.Test; + +/* +Identical to sidebar + */ +public class BasicPlayerlistScoreboardTests { + @Test + void display() { + mockContextScoreboard(context -> { + var setObjectiveTranslator = new JavaSetObjectiveTranslator(); + var setDisplayObjectiveTranslator = new JavaSetDisplayObjectiveTranslator(); + + context.translate( + setObjectiveTranslator, + new ClientboundSetObjectivePacket( + "objective", + ObjectiveAction.ADD, + Component.text("objective"), + ScoreType.INTEGER, + null + ) + ); + assertNoNextPacket(context); + + context.translate( + setDisplayObjectiveTranslator, + new ClientboundSetDisplayObjectivePacket(ScoreboardPosition.PLAYER_LIST, "objective") + ); + assertNextPacket(context, () -> { + var packet = new SetDisplayObjectivePacket(); + packet.setObjectiveId("0"); + packet.setDisplayName("objective"); + packet.setCriteria("dummy"); + packet.setDisplaySlot("list"); + packet.setSortOrder(1); + return packet; + }); + }); + } + + @Test + void displayNameColors() { + mockContextScoreboard(context -> { + var setObjectiveTranslator = new JavaSetObjectiveTranslator(); + var setDisplayObjectiveTranslator = new JavaSetDisplayObjectiveTranslator(); + + context.translate( + setObjectiveTranslator, + new ClientboundSetObjectivePacket( + "objective", + ObjectiveAction.ADD, + Component.text("objective", NamedTextColor.AQUA, TextDecoration.BOLD), + ScoreType.INTEGER, + null + ) + ); + assertNoNextPacket(context); + + context.translate( + setDisplayObjectiveTranslator, + new ClientboundSetDisplayObjectivePacket(ScoreboardPosition.PLAYER_LIST, "objective") + ); + assertNextPacket(context, () -> { + var packet = new SetDisplayObjectivePacket(); + packet.setObjectiveId("0"); + packet.setDisplayName("§b§lobjective"); + packet.setCriteria("dummy"); + packet.setDisplaySlot("list"); + packet.setSortOrder(1); + return packet; + }); + }); + } + + @Test + void overrideWithOneScore() { + mockContextScoreboard(context -> { + var setObjectiveTranslator = new JavaSetObjectiveTranslator(); + var setDisplayObjectiveTranslator = new JavaSetDisplayObjectiveTranslator(); + var setScoreTranslator = new JavaSetScoreTranslator(); + + context.translate( + setObjectiveTranslator, + new ClientboundSetObjectivePacket( + "objective1", + ObjectiveAction.ADD, + Component.text("objective1"), + ScoreType.INTEGER, + null + ) + ); + context.translate( + setObjectiveTranslator, + new ClientboundSetObjectivePacket( + "objective2", + ObjectiveAction.ADD, + Component.text("objective2"), + ScoreType.INTEGER, + null + ) + ); + context.translate(setScoreTranslator, new ClientboundSetScorePacket("Tim203", "objective1", 1)); + context.translate(setScoreTranslator, new ClientboundSetScorePacket("Tim203", "objective2", 2)); + assertNoNextPacket(context); + + context.translate( + setDisplayObjectiveTranslator, + new ClientboundSetDisplayObjectivePacket(ScoreboardPosition.PLAYER_LIST, "objective2") + ); + assertNextPacket(context, () -> { + var packet = new SetDisplayObjectivePacket(); + packet.setObjectiveId("0"); + packet.setDisplayName("objective2"); + packet.setCriteria("dummy"); + packet.setDisplaySlot("list"); + packet.setSortOrder(1); + return packet; + }); + assertNextPacket(context, () -> { + var packet = new SetScorePacket(); + packet.setAction(SetScorePacket.Action.SET); + // session player name is Tim203 + packet.setInfos(List.of(new ScoreInfo(1, "0", 2, ScoreInfo.ScorerType.PLAYER, 1))); + return packet; + }); + assertNoNextPacket(context); + + context.translate( + setDisplayObjectiveTranslator, + new ClientboundSetDisplayObjectivePacket(ScoreboardPosition.PLAYER_LIST, "objective1") + ); + assertNextPacket(context, () -> { + var packet = new RemoveObjectivePacket(); + packet.setObjectiveId("0"); + return packet; + }); + assertNextPacket(context, () -> { + var packet = new SetDisplayObjectivePacket(); + packet.setObjectiveId("2"); + packet.setDisplayName("objective1"); + packet.setCriteria("dummy"); + packet.setDisplaySlot("list"); + packet.setSortOrder(1); + return packet; + }); + assertNextPacket(context, () -> { + var packet = new SetScorePacket(); + packet.setAction(SetScorePacket.Action.SET); + // session player name is Tim203 + packet.setInfos(List.of(new ScoreInfo(3, "2", 1, ScoreInfo.ScorerType.PLAYER, 1))); + return packet; + }); + }); + } +} diff --git a/core/src/test/java/org/geysermc/geyser/scoreboard/network/server/CubecraftScoreboardTest.java b/core/src/test/java/org/geysermc/geyser/scoreboard/network/server/CubecraftScoreboardTest.java new file mode 100644 index 000000000..80f562fc3 --- /dev/null +++ b/core/src/test/java/org/geysermc/geyser/scoreboard/network/server/CubecraftScoreboardTest.java @@ -0,0 +1,699 @@ +/* + * Copyright (c) 2024 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.scoreboard.network.server; + +import static org.geysermc.geyser.scoreboard.network.util.AssertUtils.assertNextPacket; +import static org.geysermc.geyser.scoreboard.network.util.AssertUtils.assertNextPacketMatch; +import static org.geysermc.geyser.scoreboard.network.util.AssertUtils.assertNoNextPacket; +import static org.geysermc.geyser.scoreboard.network.util.GeyserMockContextScoreboard.spawnPlayer; +import static org.geysermc.geyser.scoreboard.network.util.GeyserMockContextScoreboard.mockContextScoreboard; +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.util.List; +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.format.NamedTextColor; +import net.kyori.adventure.text.format.TextColor; +import net.kyori.adventure.text.format.TextDecoration; +import org.cloudburstmc.protocol.bedrock.data.ScoreInfo; +import org.cloudburstmc.protocol.bedrock.data.entity.EntityDataTypes; +import org.cloudburstmc.protocol.bedrock.packet.AddPlayerPacket; +import org.cloudburstmc.protocol.bedrock.packet.RemoveObjectivePacket; +import org.cloudburstmc.protocol.bedrock.packet.SetDisplayObjectivePacket; +import org.cloudburstmc.protocol.bedrock.packet.SetScorePacket; +import org.geysermc.geyser.translator.protocol.java.scoreboard.JavaSetDisplayObjectiveTranslator; +import org.geysermc.geyser.translator.protocol.java.scoreboard.JavaSetObjectiveTranslator; +import org.geysermc.geyser.translator.protocol.java.scoreboard.JavaSetPlayerTeamTranslator; +import org.geysermc.geyser.translator.protocol.java.scoreboard.JavaSetScoreTranslator; +import org.geysermc.mcprotocollib.protocol.data.game.scoreboard.CollisionRule; +import org.geysermc.mcprotocollib.protocol.data.game.scoreboard.NameTagVisibility; +import org.geysermc.mcprotocollib.protocol.data.game.scoreboard.ObjectiveAction; +import org.geysermc.mcprotocollib.protocol.data.game.scoreboard.ScoreType; +import org.geysermc.mcprotocollib.protocol.data.game.scoreboard.ScoreboardPosition; +import org.geysermc.mcprotocollib.protocol.data.game.scoreboard.TeamAction; +import org.geysermc.mcprotocollib.protocol.data.game.scoreboard.TeamColor; +import org.geysermc.mcprotocollib.protocol.packet.ingame.clientbound.scoreboard.ClientboundSetDisplayObjectivePacket; +import org.geysermc.mcprotocollib.protocol.packet.ingame.clientbound.scoreboard.ClientboundSetObjectivePacket; +import org.geysermc.mcprotocollib.protocol.packet.ingame.clientbound.scoreboard.ClientboundSetPlayerTeamPacket; +import org.geysermc.mcprotocollib.protocol.packet.ingame.clientbound.scoreboard.ClientboundSetScorePacket; +import org.junit.jupiter.api.Test; + +public class CubecraftScoreboardTest { + @Test + void test() { + mockContextScoreboard(context -> { + var setTeamTranslator = new JavaSetPlayerTeamTranslator(); + var setObjectiveTranslator = new JavaSetObjectiveTranslator(); + var setDisplayObjectiveTranslator = new JavaSetDisplayObjectiveTranslator(); + var setScoreTranslator = new JavaSetScoreTranslator(); + + // unused + context.translate(setTeamTranslator, new ClientboundSetPlayerTeamPacket("SB_NoName", Component.text("SB_NoName"), Component.empty(), Component.empty(), true, true, NameTagVisibility.NEVER, CollisionRule.NEVER, TeamColor.RESET, new String[0])); + assertNoNextPacket(context); + + context.translate( + setObjectiveTranslator, + new ClientboundSetObjectivePacket( + "sidebar", + ObjectiveAction.ADD, + Component.text("sidebar"), + ScoreType.INTEGER, + null + ) + ); + assertNoNextPacket(context); + + context.translate( + setDisplayObjectiveTranslator, + new ClientboundSetDisplayObjectivePacket(ScoreboardPosition.SIDEBAR, "sidebar") + ); + assertNextPacket(context, () -> { + var packet = new SetDisplayObjectivePacket(); + packet.setObjectiveId("0"); + packet.setDisplayName("sidebar"); + packet.setCriteria("dummy"); + packet.setDisplaySlot("sidebar"); + packet.setSortOrder(1); + return packet; + }); + + + // Now they're going to create a bunch of teams and add players to those teams in a very inefficient way. + // Presumably this is a leftover from an old system, as these don't seem to do anything but hide their nametags. + // For which you could just use a single team. + + + context.translate(setTeamTranslator, new ClientboundSetPlayerTeamPacket("2i|1", Component.text("2i|1"), Component.empty(), Component.empty(), true, true, NameTagVisibility.ALWAYS, CollisionRule.ALWAYS, TeamColor.RESET, new String[0])); + context.translate(setTeamTranslator, new ClientboundSetPlayerTeamPacket("2i|1", Component.text("2i|1"), Component.empty(), Component.empty(), true, true, NameTagVisibility.ALWAYS, CollisionRule.ALWAYS, TeamColor.DARK_GRAY)); + context.translate(setTeamTranslator, new ClientboundSetPlayerTeamPacket("2i|1", Component.text("2i|1"), Component.empty(), Component.empty(), true, true, NameTagVisibility.ALWAYS, CollisionRule.ALWAYS, TeamColor.DARK_GRAY)); + context.translate(setTeamTranslator, new ClientboundSetPlayerTeamPacket("2i|1", Component.text("2i|1"), Component.empty(), Component.empty(), true, true, NameTagVisibility.ALWAYS, CollisionRule.ALWAYS, TeamColor.DARK_GRAY)); + context.translate(setTeamTranslator, new ClientboundSetPlayerTeamPacket("2i|1", Component.text("2i|1"), Component.empty(), Component.empty(), false, true, NameTagVisibility.ALWAYS, CollisionRule.ALWAYS, TeamColor.DARK_GRAY)); + context.translate(setTeamTranslator, new ClientboundSetPlayerTeamPacket("2i|1", Component.text("2i|1"), Component.empty(), Component.empty(), false, false, NameTagVisibility.ALWAYS, CollisionRule.ALWAYS, TeamColor.DARK_GRAY)); + context.translate(setTeamTranslator, new ClientboundSetPlayerTeamPacket("2i|1", Component.text("2i|1"), Component.empty(), Component.empty(), false, false, NameTagVisibility.NEVER, CollisionRule.ALWAYS, TeamColor.DARK_GRAY)); + context.translate(setTeamTranslator, new ClientboundSetPlayerTeamPacket("2i|1", Component.text("2i|1"), Component.empty(), Component.empty(), false, false, NameTagVisibility.NEVER, CollisionRule.NEVER, TeamColor.DARK_GRAY)); + context.translate(setTeamTranslator, new ClientboundSetPlayerTeamPacket("2i|1", TeamAction.ADD_PLAYER, new String[] { "A_Player" })); + + context.translate(setTeamTranslator, new ClientboundSetPlayerTeamPacket("1y|11", Component.text("1y|11"), Component.empty(), Component.empty(), true, true, NameTagVisibility.ALWAYS, CollisionRule.ALWAYS, TeamColor.RESET, new String[0])); + context.translate(setTeamTranslator, new ClientboundSetPlayerTeamPacket("1y|11", Component.text("1y|11"), Component.empty(), Component.empty(), true, true, NameTagVisibility.ALWAYS, CollisionRule.ALWAYS, TeamColor.LIGHT_PURPLE)); + context.translate(setTeamTranslator, new ClientboundSetPlayerTeamPacket("1y|11", Component.text("1y|11"), Component.empty(), Component.empty(), true, true, NameTagVisibility.ALWAYS, CollisionRule.ALWAYS, TeamColor.LIGHT_PURPLE)); + context.translate(setTeamTranslator, new ClientboundSetPlayerTeamPacket("1y|11", Component.text("1y|11"), Component.empty(), Component.empty(), true, true, NameTagVisibility.ALWAYS, CollisionRule.ALWAYS, TeamColor.LIGHT_PURPLE)); + context.translate(setTeamTranslator, new ClientboundSetPlayerTeamPacket("1y|11", Component.text("1y|11"), Component.empty(), Component.empty(), false, true, NameTagVisibility.ALWAYS, CollisionRule.ALWAYS, TeamColor.LIGHT_PURPLE)); + context.translate(setTeamTranslator, new ClientboundSetPlayerTeamPacket("1y|11", Component.text("1y|11"), Component.empty(), Component.empty(), false, false, NameTagVisibility.ALWAYS, CollisionRule.ALWAYS, TeamColor.LIGHT_PURPLE)); + context.translate(setTeamTranslator, new ClientboundSetPlayerTeamPacket("1y|11", Component.text("1y|11"), Component.empty(), Component.empty(), false, false, NameTagVisibility.NEVER, CollisionRule.ALWAYS, TeamColor.LIGHT_PURPLE)); + context.translate(setTeamTranslator, new ClientboundSetPlayerTeamPacket("1y|11", Component.text("1y|11"), Component.empty(), Component.empty(), false, false, NameTagVisibility.NEVER, CollisionRule.NEVER, TeamColor.LIGHT_PURPLE)); + context.translate(setTeamTranslator, new ClientboundSetPlayerTeamPacket("1y|11", TeamAction.ADD_PLAYER, new String[] { "B_Player" })); + + context.translate(setTeamTranslator, new ClientboundSetPlayerTeamPacket("2i|1", TeamAction.ADD_PLAYER, new String[] { "C_Player" })); + context.translate(setTeamTranslator, new ClientboundSetPlayerTeamPacket("2i|1", TeamAction.ADD_PLAYER, new String[] { "D_Player" })); + context.translate(setTeamTranslator, new ClientboundSetPlayerTeamPacket("1y|11", TeamAction.ADD_PLAYER, new String[] { "E_Player" })); + context.translate(setTeamTranslator, new ClientboundSetPlayerTeamPacket("2i|1", TeamAction.ADD_PLAYER, new String[] { "F_Player" })); + context.translate(setTeamTranslator, new ClientboundSetPlayerTeamPacket("2i|1", TeamAction.ADD_PLAYER, new String[] { "G_Player" })); + + context.translate(setTeamTranslator, new ClientboundSetPlayerTeamPacket("2e|3", Component.text("2e|3"), Component.empty(), Component.empty(), true, true, NameTagVisibility.ALWAYS, CollisionRule.ALWAYS, TeamColor.RESET, new String[0])); + context.translate(setTeamTranslator, new ClientboundSetPlayerTeamPacket("2e|3", Component.text("2e|3"), Component.empty(), Component.empty(), true, true, NameTagVisibility.ALWAYS, CollisionRule.ALWAYS, TeamColor.BLUE)); + context.translate(setTeamTranslator, new ClientboundSetPlayerTeamPacket("2e|3", Component.text("2e|3"), Component.empty(), Component.empty(), true, true, NameTagVisibility.ALWAYS, CollisionRule.ALWAYS, TeamColor.BLUE)); + context.translate(setTeamTranslator, new ClientboundSetPlayerTeamPacket("2e|3", Component.text("2e|3"), Component.empty(), Component.empty(), true, true, NameTagVisibility.ALWAYS, CollisionRule.ALWAYS, TeamColor.BLUE)); + context.translate(setTeamTranslator, new ClientboundSetPlayerTeamPacket("2e|3", Component.text("2e|3"), Component.empty(), Component.empty(), false, true, NameTagVisibility.ALWAYS, CollisionRule.ALWAYS, TeamColor.BLUE)); + context.translate(setTeamTranslator, new ClientboundSetPlayerTeamPacket("2e|3", Component.text("2e|3"), Component.empty(), Component.empty(), false, false, NameTagVisibility.ALWAYS, CollisionRule.ALWAYS, TeamColor.BLUE)); + context.translate(setTeamTranslator, new ClientboundSetPlayerTeamPacket("2e|3", Component.text("2e|3"), Component.empty(), Component.empty(), false, false, NameTagVisibility.NEVER, CollisionRule.ALWAYS, TeamColor.BLUE)); + context.translate(setTeamTranslator, new ClientboundSetPlayerTeamPacket("2e|3", Component.text("2e|3"), Component.empty(), Component.empty(), false, false, NameTagVisibility.NEVER, CollisionRule.NEVER, TeamColor.BLUE)); + context.translate(setTeamTranslator, new ClientboundSetPlayerTeamPacket("2e|3", TeamAction.ADD_PLAYER, new String[] { "H_Player" })); + context.translate(setTeamTranslator, new ClientboundSetPlayerTeamPacket("2i|1", TeamAction.ADD_PLAYER, new String[] { "I_Player" })); + + context.translate(setTeamTranslator, new ClientboundSetPlayerTeamPacket("22|9", Component.text("22|9"), Component.empty(), Component.empty(), true, true, NameTagVisibility.ALWAYS, CollisionRule.ALWAYS, TeamColor.RESET, new String[0])); + context.translate(setTeamTranslator, new ClientboundSetPlayerTeamPacket("22|9", Component.text("22|9"), Component.empty(), Component.empty(), true, true, NameTagVisibility.ALWAYS, CollisionRule.ALWAYS, TeamColor.AQUA)); + context.translate(setTeamTranslator, new ClientboundSetPlayerTeamPacket("22|9", Component.text("22|9"), Component.empty(), Component.empty(), true, true, NameTagVisibility.ALWAYS, CollisionRule.ALWAYS, TeamColor.AQUA)); + context.translate(setTeamTranslator, new ClientboundSetPlayerTeamPacket("22|9", Component.text("22|9"), Component.empty(), Component.empty(), true, true, NameTagVisibility.ALWAYS, CollisionRule.ALWAYS, TeamColor.AQUA)); + context.translate(setTeamTranslator, new ClientboundSetPlayerTeamPacket("22|9", Component.text("22|9"), Component.empty(), Component.empty(), false, true, NameTagVisibility.ALWAYS, CollisionRule.ALWAYS, TeamColor.AQUA)); + context.translate(setTeamTranslator, new ClientboundSetPlayerTeamPacket("22|9", Component.text("22|9"), Component.empty(), Component.empty(), false, false, NameTagVisibility.ALWAYS, CollisionRule.ALWAYS, TeamColor.AQUA)); + context.translate(setTeamTranslator, new ClientboundSetPlayerTeamPacket("22|9", Component.text("22|9"), Component.empty(), Component.empty(), false, false, NameTagVisibility.NEVER, CollisionRule.ALWAYS, TeamColor.AQUA)); + context.translate(setTeamTranslator, new ClientboundSetPlayerTeamPacket("22|9", Component.text("22|9"), Component.empty(), Component.empty(), false, false, NameTagVisibility.NEVER, CollisionRule.NEVER, TeamColor.AQUA)); + context.translate(setTeamTranslator, new ClientboundSetPlayerTeamPacket("22|9", TeamAction.ADD_PLAYER, new String[] { "J_Player" })); + context.translate(setTeamTranslator, new ClientboundSetPlayerTeamPacket("2i|1", TeamAction.ADD_PLAYER, new String[] { "K_Player" })); + + context.translate(setTeamTranslator, new ClientboundSetPlayerTeamPacket("26|7", Component.text("26|7"), Component.empty(), Component.empty(), true, true, NameTagVisibility.ALWAYS, CollisionRule.ALWAYS, TeamColor.RESET, new String[0])); + context.translate(setTeamTranslator, new ClientboundSetPlayerTeamPacket("26|7", Component.text("26|7"), Component.empty(), Component.empty(), true, true, NameTagVisibility.ALWAYS, CollisionRule.ALWAYS, TeamColor.AQUA)); + context.translate(setTeamTranslator, new ClientboundSetPlayerTeamPacket("26|7", Component.text("26|7"), Component.empty(), Component.empty(), true, true, NameTagVisibility.ALWAYS, CollisionRule.ALWAYS, TeamColor.AQUA)); + context.translate(setTeamTranslator, new ClientboundSetPlayerTeamPacket("26|7", Component.text("26|7"), Component.empty(), Component.empty(), true, true, NameTagVisibility.ALWAYS, CollisionRule.ALWAYS, TeamColor.AQUA)); + context.translate(setTeamTranslator, new ClientboundSetPlayerTeamPacket("26|7", Component.text("26|7"), Component.empty(), Component.empty(), false, true, NameTagVisibility.ALWAYS, CollisionRule.ALWAYS, TeamColor.AQUA)); + context.translate(setTeamTranslator, new ClientboundSetPlayerTeamPacket("26|7", Component.text("26|7"), Component.empty(), Component.empty(), false, false, NameTagVisibility.ALWAYS, CollisionRule.ALWAYS, TeamColor.AQUA)); + context.translate(setTeamTranslator, new ClientboundSetPlayerTeamPacket("26|7", Component.text("26|7"), Component.empty(), Component.empty(), false, false, NameTagVisibility.NEVER, CollisionRule.ALWAYS, TeamColor.AQUA)); + context.translate(setTeamTranslator, new ClientboundSetPlayerTeamPacket("26|7", Component.text("26|7"), Component.empty(), Component.empty(), false, false, NameTagVisibility.NEVER, CollisionRule.NEVER, TeamColor.AQUA)); + context.translate(setTeamTranslator, new ClientboundSetPlayerTeamPacket("26|7", TeamAction.ADD_PLAYER, new String[] { "L_Player" })); + context.translate(setTeamTranslator, new ClientboundSetPlayerTeamPacket("2e|3", TeamAction.ADD_PLAYER, new String[] { "M_Player" })); + context.translate(setTeamTranslator, new ClientboundSetPlayerTeamPacket("2i|1", TeamAction.ADD_PLAYER, new String[] { "N_Player" })); + + context.translate(setTeamTranslator, new ClientboundSetPlayerTeamPacket("1u|13", Component.text("1u|13"), Component.empty(), Component.empty(), true, true, NameTagVisibility.ALWAYS, CollisionRule.ALWAYS, TeamColor.RESET, new String[0])); + context.translate(setTeamTranslator, new ClientboundSetPlayerTeamPacket("1u|13", Component.text("1u|13"), Component.empty(), Component.empty(), true, true, NameTagVisibility.ALWAYS, CollisionRule.ALWAYS, TeamColor.LIGHT_PURPLE)); + context.translate(setTeamTranslator, new ClientboundSetPlayerTeamPacket("1u|13", Component.text("1u|13"), Component.empty(), Component.empty(), true, true, NameTagVisibility.ALWAYS, CollisionRule.ALWAYS, TeamColor.LIGHT_PURPLE)); + context.translate(setTeamTranslator, new ClientboundSetPlayerTeamPacket("1u|13", Component.text("1u|13"), Component.empty(), Component.empty(), true, true, NameTagVisibility.ALWAYS, CollisionRule.ALWAYS, TeamColor.LIGHT_PURPLE)); + context.translate(setTeamTranslator, new ClientboundSetPlayerTeamPacket("1u|13", Component.text("1u|13"), Component.empty(), Component.empty(), false, true, NameTagVisibility.ALWAYS, CollisionRule.ALWAYS, TeamColor.LIGHT_PURPLE)); + context.translate(setTeamTranslator, new ClientboundSetPlayerTeamPacket("1u|13", Component.text("1u|13"), Component.empty(), Component.empty(), false, false, NameTagVisibility.ALWAYS, CollisionRule.ALWAYS, TeamColor.LIGHT_PURPLE)); + context.translate(setTeamTranslator, new ClientboundSetPlayerTeamPacket("1u|13", Component.text("1u|13"), Component.empty(), Component.empty(), false, false, NameTagVisibility.NEVER, CollisionRule.ALWAYS, TeamColor.LIGHT_PURPLE)); + context.translate(setTeamTranslator, new ClientboundSetPlayerTeamPacket("1u|13", Component.text("1u|13"), Component.empty(), Component.empty(), false, false, NameTagVisibility.NEVER, CollisionRule.NEVER, TeamColor.LIGHT_PURPLE)); + context.translate(setTeamTranslator, new ClientboundSetPlayerTeamPacket("1u|13", TeamAction.ADD_PLAYER, new String[] { "O_Player" })); + context.translate(setTeamTranslator, new ClientboundSetPlayerTeamPacket("2i|1", TeamAction.ADD_PLAYER, new String[] { "P_Player" })); + context.translate(setTeamTranslator, new ClientboundSetPlayerTeamPacket("2i|1", TeamAction.ADD_PLAYER, new String[] { "Q_Player" })); + + assertNoNextPacket(context); + + + // Now that those teams are created and people added to it, they set the final sidebar name and add the lines to it. + // They're also not doing this efficiently, because they don't add the players when the team is created. + // Instead, they send an additional packet. + + + context.translate( + setObjectiveTranslator, + new ClientboundSetObjectivePacket( + "sidebar", + ObjectiveAction.UPDATE, + Component.empty() + .append(Component.text( + "CubeCraft", NamedTextColor.WHITE, TextDecoration.BOLD)), + ScoreType.INTEGER, + null)); + assertNextPacket(context, () -> { + var packet = new RemoveObjectivePacket(); + packet.setObjectiveId("0"); + return packet; + }); + assertNextPacket(context, () -> { + var packet = new SetDisplayObjectivePacket(); + packet.setObjectiveId("0"); + packet.setDisplayName("§f§lCubeCraft"); + packet.setCriteria("dummy"); + packet.setDisplaySlot("sidebar"); + packet.setSortOrder(1); + return packet; + }); + + context.translate( + setTeamTranslator, + new ClientboundSetPlayerTeamPacket( + "SB_l-0", + Component.text("SB_l-0"), + Component.empty(), + Component.empty(), + true, + true, + NameTagVisibility.ALWAYS, + CollisionRule.ALWAYS, + TeamColor.RESET, + new String[0])); + context.translate( + setTeamTranslator, + new ClientboundSetPlayerTeamPacket("SB_l-0", TeamAction.ADD_PLAYER, new String[] {"§0§0"})); + context.translate( + setTeamTranslator, + new ClientboundSetPlayerTeamPacket( + "SB_l-0", + Component.text("SB_l-0"), + Component.empty().append(Component.text("", NamedTextColor.BLACK)), + Component.empty(), + true, + true, + NameTagVisibility.ALWAYS, + CollisionRule.ALWAYS, + TeamColor.RESET)); + assertNoNextPacket(context); + + context.translate(setScoreTranslator, new ClientboundSetScorePacket("§0§0", "sidebar", 10)); + assertNextPacket(context, () -> { + var packet = new SetScorePacket(); + packet.setAction(SetScorePacket.Action.SET); + packet.setInfos(List.of(new ScoreInfo(1, "0", 10, "§r§0§0§r"))); + return packet; + }); + + context.translate( + setTeamTranslator, + new ClientboundSetPlayerTeamPacket( + "SB_l-1", + Component.text("SB_l-1"), + Component.empty(), + Component.empty(), + true, + true, + NameTagVisibility.ALWAYS, + CollisionRule.ALWAYS, + TeamColor.RESET, + new String[0])); + context.translate( + setTeamTranslator, + new ClientboundSetPlayerTeamPacket("SB_l-1", TeamAction.ADD_PLAYER, new String[] {"§0§1"})); + context.translate( + setTeamTranslator, + new ClientboundSetPlayerTeamPacket( + "SB_l-1", + Component.text("SB_l-1"), + Component.empty() + .append(Component.textOfChildren( + Component.text("User: ", TextColor.color(0x3aa9ff)), + Component.text("Tim203", NamedTextColor.WHITE))), + Component.empty(), + true, + true, + NameTagVisibility.ALWAYS, + CollisionRule.ALWAYS, + TeamColor.RESET)); + assertNoNextPacket(context); + + context.translate(setScoreTranslator, new ClientboundSetScorePacket("§0§1", "sidebar", 9)); + assertNextPacket(context, () -> { + var packet = new SetScorePacket(); + packet.setAction(SetScorePacket.Action.SET); + packet.setInfos(List.of(new ScoreInfo(2, "0", 9, "§bUser: §r§fTim203§r§0§1§r"))); + return packet; + }); + + context.translate( + setTeamTranslator, + new ClientboundSetPlayerTeamPacket( + "SB_l-2", + Component.text("SB_l-2"), + Component.empty(), + Component.empty(), + true, + true, + NameTagVisibility.ALWAYS, + CollisionRule.ALWAYS, + TeamColor.RESET, + new String[0])); + context.translate( + setTeamTranslator, + new ClientboundSetPlayerTeamPacket("SB_l-2", TeamAction.ADD_PLAYER, new String[] {"§0§2"})); + context.translate( + setTeamTranslator, + new ClientboundSetPlayerTeamPacket( + "SB_l-2", + Component.text("SB_l-2"), + Component.empty() + .append(Component.textOfChildren( + Component.text("Rank: ", TextColor.color(0x3aa9ff)), + Component.text("\uE1AB ", NamedTextColor.WHITE))), + Component.empty(), + true, + true, + NameTagVisibility.ALWAYS, + CollisionRule.ALWAYS, + TeamColor.RESET)); + assertNoNextPacket(context); + + context.translate(setScoreTranslator, new ClientboundSetScorePacket("§0§2", "sidebar", 8)); + assertNextPacket(context, () -> { + var packet = new SetScorePacket(); + packet.setAction(SetScorePacket.Action.SET); + packet.setInfos(List.of(new ScoreInfo(3, "0", 8, "§bRank: §r§f\uE1AB §r§0§2§r"))); + return packet; + }); + + context.translate( + setTeamTranslator, + new ClientboundSetPlayerTeamPacket( + "SB_l-3", + Component.text("SB_l-3"), + Component.empty(), + Component.empty(), + true, + true, + NameTagVisibility.ALWAYS, + CollisionRule.ALWAYS, + TeamColor.RESET, + new String[0])); + context.translate( + setTeamTranslator, + new ClientboundSetPlayerTeamPacket("SB_l-3", TeamAction.ADD_PLAYER, new String[] {"§0§3"})); + context.translate( + setTeamTranslator, + new ClientboundSetPlayerTeamPacket( + "SB_l-3", + Component.text("SB_l-3"), + Component.empty(), + Component.empty(), + true, + true, + NameTagVisibility.ALWAYS, + CollisionRule.ALWAYS, + TeamColor.RESET)); + assertNoNextPacket(context); + + context.translate(setScoreTranslator, new ClientboundSetScorePacket("§0§3", "sidebar", 7)); + assertNextPacket(context, () -> { + var packet = new SetScorePacket(); + packet.setAction(SetScorePacket.Action.SET); + packet.setInfos(List.of(new ScoreInfo(4, "0", 7, "§r§0§3§r"))); + return packet; + }); + + context.translate( + setTeamTranslator, + new ClientboundSetPlayerTeamPacket( + "SB_l-4", + Component.text("SB_l-4"), + Component.empty(), + Component.empty(), + true, + true, + NameTagVisibility.ALWAYS, + CollisionRule.ALWAYS, + TeamColor.RESET, + new String[0])); + context.translate( + setTeamTranslator, + new ClientboundSetPlayerTeamPacket("SB_l-4", TeamAction.ADD_PLAYER, new String[] {"§0§4"})); + context.translate( + setTeamTranslator, + new ClientboundSetPlayerTeamPacket( + "SB_l-4", + Component.text("SB_l-4"), + Component.empty(), + Component.empty(), + true, + true, + NameTagVisibility.ALWAYS, + CollisionRule.ALWAYS, + TeamColor.RESET)); + assertNoNextPacket(context); + + context.translate(setScoreTranslator, new ClientboundSetScorePacket("§0§4", "sidebar", 6)); + assertNextPacket(context, () -> { + var packet = new SetScorePacket(); + packet.setAction(SetScorePacket.Action.SET); + packet.setInfos(List.of(new ScoreInfo(5, "0", 6, "§r§0§4§r"))); + return packet; + }); + + context.translate( + setTeamTranslator, + new ClientboundSetPlayerTeamPacket( + "SB_l-5", + Component.text("SB_l-5"), + Component.empty(), + Component.empty(), + true, + true, + NameTagVisibility.ALWAYS, + CollisionRule.ALWAYS, + TeamColor.RESET, + new String[0])); + context.translate( + setTeamTranslator, + new ClientboundSetPlayerTeamPacket("SB_l-5", TeamAction.ADD_PLAYER, new String[] {"§0§5"})); + context.translate( + setTeamTranslator, + new ClientboundSetPlayerTeamPacket( + "SB_l-5", + Component.text("SB_l-5"), + Component.empty().append(Component.text("", NamedTextColor.DARK_BLUE)), + Component.empty(), + true, + true, + NameTagVisibility.ALWAYS, + CollisionRule.ALWAYS, + TeamColor.RESET)); + assertNoNextPacket(context); + + context.translate(setScoreTranslator, new ClientboundSetScorePacket("§0§5", "sidebar", 5)); + assertNextPacket(context, () -> { + var packet = new SetScorePacket(); + packet.setAction(SetScorePacket.Action.SET); + packet.setInfos(List.of(new ScoreInfo(6, "0", 5, "§r§0§5§r"))); + return packet; + }); + + context.translate( + setTeamTranslator, + new ClientboundSetPlayerTeamPacket( + "SB_l-6", + Component.text("SB_l-6"), + Component.empty(), + Component.empty(), + true, + true, + NameTagVisibility.ALWAYS, + CollisionRule.ALWAYS, + TeamColor.RESET, + new String[0])); + context.translate( + setTeamTranslator, + new ClientboundSetPlayerTeamPacket("SB_l-6", TeamAction.ADD_PLAYER, new String[] {"§0§6"})); + context.translate( + setTeamTranslator, + new ClientboundSetPlayerTeamPacket( + "SB_l-6", + Component.text("SB_l-6"), + Component.empty() + .append(Component.textOfChildren( + Component.text("Lobby: ", TextColor.color(0x3aa9ff)), + Component.text("EU #10", NamedTextColor.WHITE))), + Component.empty(), + true, + true, + NameTagVisibility.ALWAYS, + CollisionRule.ALWAYS, + TeamColor.RESET)); + assertNoNextPacket(context); + + context.translate(setScoreTranslator, new ClientboundSetScorePacket("§0§6", "sidebar", 4)); + assertNextPacket(context, () -> { + var packet = new SetScorePacket(); + packet.setAction(SetScorePacket.Action.SET); + packet.setInfos(List.of(new ScoreInfo(7, "0", 4, "§bLobby: §r§fEU #10§r§0§6§r"))); + return packet; + }); + + context.translate( + setTeamTranslator, + new ClientboundSetPlayerTeamPacket( + "SB_l-7", + Component.text("SB_l-7"), + Component.empty(), + Component.empty(), + true, + true, + NameTagVisibility.ALWAYS, + CollisionRule.ALWAYS, + TeamColor.RESET, + new String[0])); + context.translate( + setTeamTranslator, + new ClientboundSetPlayerTeamPacket("SB_l-7", TeamAction.ADD_PLAYER, new String[] {"§0§7"})); + context.translate( + setTeamTranslator, + new ClientboundSetPlayerTeamPacket( + "SB_l-7", + Component.text("SB_l-7"), + Component.empty() + .append(Component.textOfChildren( + Component.text("Players: ", TextColor.color(0x3aa9ff)), + Component.text("783", NamedTextColor.WHITE))), + Component.empty(), + true, + true, + NameTagVisibility.ALWAYS, + CollisionRule.ALWAYS, + TeamColor.RESET)); + assertNoNextPacket(context); + + context.translate(setScoreTranslator, new ClientboundSetScorePacket("§0§7", "sidebar", 3)); + assertNextPacket(context, () -> { + var packet = new SetScorePacket(); + packet.setAction(SetScorePacket.Action.SET); + packet.setInfos(List.of(new ScoreInfo(8, "0", 3, "§bPlayers: §r§f783§r§0§7§r"))); + return packet; + }); + + context.translate( + setTeamTranslator, + new ClientboundSetPlayerTeamPacket( + "SB_l-8", + Component.text("SB_l-8"), + Component.empty(), + Component.empty(), + true, + true, + NameTagVisibility.ALWAYS, + CollisionRule.ALWAYS, + TeamColor.RESET, + new String[0])); + context.translate( + setTeamTranslator, + new ClientboundSetPlayerTeamPacket("SB_l-8", TeamAction.ADD_PLAYER, new String[] {"§0§8"})); + context.translate( + setTeamTranslator, + new ClientboundSetPlayerTeamPacket( + "SB_l-8", + Component.text("SB_l-8"), + Component.empty().append(Component.text("", NamedTextColor.DARK_GREEN)), + Component.empty(), + true, + true, + NameTagVisibility.ALWAYS, + CollisionRule.ALWAYS, + TeamColor.RESET)); + assertNoNextPacket(context); + + context.translate(setScoreTranslator, new ClientboundSetScorePacket("§0§8", "sidebar", 2)); + assertNextPacket(context, () -> { + var packet = new SetScorePacket(); + packet.setAction(SetScorePacket.Action.SET); + packet.setInfos(List.of(new ScoreInfo(9, "0", 2, "§r§0§8§r"))); + return packet; + }); + + context.translate( + setTeamTranslator, + new ClientboundSetPlayerTeamPacket( + "SB_l-9", + Component.text("SB_l-9"), + Component.empty(), + Component.empty(), + true, + true, + NameTagVisibility.ALWAYS, + CollisionRule.ALWAYS, + TeamColor.RESET, + new String[0])); + context.translate( + setTeamTranslator, + new ClientboundSetPlayerTeamPacket("SB_l-9", TeamAction.ADD_PLAYER, new String[] {"§0§9"})); + context.translate( + setTeamTranslator, + new ClientboundSetPlayerTeamPacket( + "SB_l-9", + Component.text("SB_l-9"), + Component.empty().append(Component.text("24/09/24 (g2208)", TextColor.color(0x777777))), + Component.empty(), + true, + true, + NameTagVisibility.ALWAYS, + CollisionRule.ALWAYS, + TeamColor.RESET)); + assertNoNextPacket(context); + + context.translate(setScoreTranslator, new ClientboundSetScorePacket("§0§9", "sidebar", 1)); + assertNextPacket(context, () -> { + var packet = new SetScorePacket(); + packet.setAction(SetScorePacket.Action.SET); + packet.setInfos(List.of(new ScoreInfo(10, "0", 1, "§824/09/24 (g2208)§r§0§9§r"))); + return packet; + }); + + context.translate( + setTeamTranslator, + new ClientboundSetPlayerTeamPacket( + "SB_l-10", + Component.text("SB_l-10"), + Component.empty(), + Component.empty(), + true, + true, + NameTagVisibility.ALWAYS, + CollisionRule.ALWAYS, + TeamColor.RESET, + new String[0])); + context.translate( + setTeamTranslator, + new ClientboundSetPlayerTeamPacket("SB_l-10", TeamAction.ADD_PLAYER, new String[] {"§0§a"})); + context.translate( + setTeamTranslator, + new ClientboundSetPlayerTeamPacket( + "SB_l-10", + Component.text("SB_l-10"), + Component.empty().append(Component.text("play.cubecraft.net", NamedTextColor.GOLD)), + Component.empty(), + true, + true, + NameTagVisibility.ALWAYS, + CollisionRule.ALWAYS, + TeamColor.RESET)); + assertNoNextPacket(context); + + context.translate(setScoreTranslator, new ClientboundSetScorePacket("§0§a", "sidebar", 0)); + assertNextPacket(context, () -> { + var packet = new SetScorePacket(); + packet.setAction(SetScorePacket.Action.SET); + packet.setInfos(List.of(new ScoreInfo(11, "0", 0, "§6play.cubecraft.net§r§0§a§r"))); + return packet; + }); + + // after this we get a ClientboundPlayerInfoUpdatePacket with the action UPDATE_DISPLAY_NAME, + // but that one is only shown in the tablist so we don't have to handle that. + // And after that we get each player's ClientboundPlayerInfoUpdatePacket with also a UPDATE_DISPLAY_NAME, + // which is also not interesting for us. + // CubeCraft seems to use two armor stands per player: 1 for the rank badge and 1 for the player name. + // So the only thing we have to verify is that the nametag is hidden + + spawnPlayer(context, "A_Player", 2); + assertNextPacketMatch(context, AddPlayerPacket.class, packet -> { + assertEquals(2, packet.getRuntimeEntityId()); + assertEquals("", packet.getMetadata().get(EntityDataTypes.NAME)); + }); + + spawnPlayer(context, "B_Player", 3); + assertNextPacketMatch(context, AddPlayerPacket.class, packet -> { + assertEquals(3, packet.getRuntimeEntityId()); + assertEquals("", packet.getMetadata().get(EntityDataTypes.NAME)); + }); + + spawnPlayer(context, "E_Player", 4); + assertNextPacketMatch(context, AddPlayerPacket.class, packet -> { + assertEquals(4, packet.getRuntimeEntityId()); + assertEquals("", packet.getMetadata().get(EntityDataTypes.NAME)); + }); + + spawnPlayer(context, "H_Player", 5); + assertNextPacketMatch(context, AddPlayerPacket.class, packet -> { + assertEquals(5, packet.getRuntimeEntityId()); + assertEquals("", packet.getMetadata().get(EntityDataTypes.NAME)); + }); + + spawnPlayer(context, "J_Player", 6); + assertNextPacketMatch(context, AddPlayerPacket.class, packet -> { + assertEquals(6, packet.getRuntimeEntityId()); + assertEquals("", packet.getMetadata().get(EntityDataTypes.NAME)); + }); + + spawnPlayer(context, "K_Player", 7); + assertNextPacketMatch(context, AddPlayerPacket.class, packet -> { + assertEquals(7, packet.getRuntimeEntityId()); + assertEquals("", packet.getMetadata().get(EntityDataTypes.NAME)); + }); + + spawnPlayer(context, "L_Player", 8); + assertNextPacketMatch(context, AddPlayerPacket.class, packet -> { + assertEquals(8, packet.getRuntimeEntityId()); + assertEquals("", packet.getMetadata().get(EntityDataTypes.NAME)); + }); + + spawnPlayer(context, "O_Player", 9); + assertNextPacketMatch(context, AddPlayerPacket.class, packet -> { + assertEquals(9, packet.getRuntimeEntityId()); + assertEquals("", packet.getMetadata().get(EntityDataTypes.NAME)); + }); + }); + } +} diff --git a/core/src/test/java/org/geysermc/geyser/scoreboard/network/sidebar/BasicSidebarScoreboardTests.java b/core/src/test/java/org/geysermc/geyser/scoreboard/network/sidebar/BasicSidebarScoreboardTests.java new file mode 100644 index 000000000..bd0d64c80 --- /dev/null +++ b/core/src/test/java/org/geysermc/geyser/scoreboard/network/sidebar/BasicSidebarScoreboardTests.java @@ -0,0 +1,217 @@ +/* + * Copyright (c) 2024 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.scoreboard.network.sidebar; + +import static org.geysermc.geyser.scoreboard.network.util.AssertUtils.assertNextPacket; +import static org.geysermc.geyser.scoreboard.network.util.AssertUtils.assertNoNextPacket; +import static org.geysermc.geyser.scoreboard.network.util.GeyserMockContextScoreboard.mockContextScoreboard; + +import java.util.List; +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.format.NamedTextColor; +import net.kyori.adventure.text.format.TextDecoration; +import org.cloudburstmc.protocol.bedrock.data.ScoreInfo; +import org.cloudburstmc.protocol.bedrock.packet.RemoveObjectivePacket; +import org.cloudburstmc.protocol.bedrock.packet.SetDisplayObjectivePacket; +import org.cloudburstmc.protocol.bedrock.packet.SetScorePacket; +import org.geysermc.geyser.translator.protocol.java.scoreboard.JavaSetDisplayObjectiveTranslator; +import org.geysermc.geyser.translator.protocol.java.scoreboard.JavaSetObjectiveTranslator; +import org.geysermc.geyser.translator.protocol.java.scoreboard.JavaSetScoreTranslator; +import org.geysermc.mcprotocollib.protocol.data.game.scoreboard.ObjectiveAction; +import org.geysermc.mcprotocollib.protocol.data.game.scoreboard.ScoreType; +import org.geysermc.mcprotocollib.protocol.data.game.scoreboard.ScoreboardPosition; +import org.geysermc.mcprotocollib.protocol.packet.ingame.clientbound.scoreboard.ClientboundSetDisplayObjectivePacket; +import org.geysermc.mcprotocollib.protocol.packet.ingame.clientbound.scoreboard.ClientboundSetObjectivePacket; +import org.geysermc.mcprotocollib.protocol.packet.ingame.clientbound.scoreboard.ClientboundSetScorePacket; +import org.junit.jupiter.api.Test; + +/* +Identical to playerlist + */ +public class BasicSidebarScoreboardTests { + @Test + void displayAndRemove() { + mockContextScoreboard(context -> { + var setObjectiveTranslator = new JavaSetObjectiveTranslator(); + var setDisplayObjectiveTranslator = new JavaSetDisplayObjectiveTranslator(); + + context.translate( + setObjectiveTranslator, + new ClientboundSetObjectivePacket( + "objective", + ObjectiveAction.ADD, + Component.text("objective"), + ScoreType.INTEGER, + null + ) + ); + assertNoNextPacket(context); + + context.translate( + setDisplayObjectiveTranslator, + new ClientboundSetDisplayObjectivePacket(ScoreboardPosition.PLAYER_LIST, "objective") + ); + assertNextPacket(context, () -> { + var packet = new SetDisplayObjectivePacket(); + packet.setObjectiveId("0"); + packet.setDisplayName("objective"); + packet.setCriteria("dummy"); + packet.setDisplaySlot("list"); + packet.setSortOrder(1); + return packet; + }); + + context.translate( + setDisplayObjectiveTranslator, + new ClientboundSetDisplayObjectivePacket(ScoreboardPosition.PLAYER_LIST, "") + ); + assertNextPacket(context, () -> { + var packet = new RemoveObjectivePacket(); + packet.setObjectiveId("0"); + return packet; + }); + }); + } + + @Test + void displayNameColors() { + mockContextScoreboard(context -> { + var setObjectiveTranslator = new JavaSetObjectiveTranslator(); + var setDisplayObjectiveTranslator = new JavaSetDisplayObjectiveTranslator(); + + context.translate( + setObjectiveTranslator, + new ClientboundSetObjectivePacket( + "objective", + ObjectiveAction.ADD, + Component.text("objective", NamedTextColor.AQUA, TextDecoration.BOLD), + ScoreType.INTEGER, + null + ) + ); + assertNoNextPacket(context); + + context.translate( + setDisplayObjectiveTranslator, + new ClientboundSetDisplayObjectivePacket(ScoreboardPosition.SIDEBAR, "objective") + ); + assertNextPacket(context, () -> { + var packet = new SetDisplayObjectivePacket(); + packet.setObjectiveId("0"); + packet.setDisplayName("§b§lobjective"); + packet.setCriteria("dummy"); + packet.setDisplaySlot("sidebar"); + packet.setSortOrder(1); + return packet; + }); + }); + } + + @Test + void override() { + mockContextScoreboard(context -> { + var setObjectiveTranslator = new JavaSetObjectiveTranslator(); + var setDisplayObjectiveTranslator = new JavaSetDisplayObjectiveTranslator(); + var setScoreTranslator = new JavaSetScoreTranslator(); + + context.translate( + setObjectiveTranslator, + new ClientboundSetObjectivePacket( + "objective1", + ObjectiveAction.ADD, + Component.text("objective1"), + ScoreType.INTEGER, + null + ) + ); + + context.translate( + setObjectiveTranslator, + new ClientboundSetObjectivePacket( + "objective2", + ObjectiveAction.ADD, + Component.text("objective2"), + ScoreType.INTEGER, + null + ) + ); + + context.translate(setScoreTranslator, new ClientboundSetScorePacket("Tim203", "objective1", 1)); + context.translate(setScoreTranslator, new ClientboundSetScorePacket("Tim203", "objective2", 2)); + assertNoNextPacket(context); + + + context.translate( + setDisplayObjectiveTranslator, + new ClientboundSetDisplayObjectivePacket(ScoreboardPosition.SIDEBAR, "objective2") + ); + + assertNextPacket(context, () -> { + var packet = new SetDisplayObjectivePacket(); + packet.setObjectiveId("0"); + packet.setDisplayName("objective2"); + packet.setCriteria("dummy"); + packet.setDisplaySlot("sidebar"); + packet.setSortOrder(1); + return packet; + }); + assertNextPacket(context, () -> { + var packet = new SetScorePacket(); + packet.setAction(SetScorePacket.Action.SET); + packet.setInfos(List.of(new ScoreInfo(1, "0", 2, "Tim203"))); + return packet; + }); + assertNoNextPacket(context); + + + context.translate( + setDisplayObjectiveTranslator, + new ClientboundSetDisplayObjectivePacket(ScoreboardPosition.SIDEBAR, "objective1") + ); + + assertNextPacket(context, () -> { + var packet = new RemoveObjectivePacket(); + packet.setObjectiveId("0"); + return packet; + }); + assertNextPacket(context, () -> { + var packet = new SetDisplayObjectivePacket(); + packet.setObjectiveId("2"); + packet.setDisplayName("objective1"); + packet.setCriteria("dummy"); + packet.setDisplaySlot("sidebar"); + packet.setSortOrder(1); + return packet; + }); + assertNextPacket(context, () -> { + var packet = new SetScorePacket(); + packet.setAction(SetScorePacket.Action.SET); + packet.setInfos(List.of(new ScoreInfo(3, "2", 1, "Tim203"))); + return packet; + }); + }); + } +} diff --git a/core/src/test/java/org/geysermc/geyser/scoreboard/network/sidebar/OrderAndLimitSidebarScoreboardTests.java b/core/src/test/java/org/geysermc/geyser/scoreboard/network/sidebar/OrderAndLimitSidebarScoreboardTests.java new file mode 100644 index 000000000..aab837456 --- /dev/null +++ b/core/src/test/java/org/geysermc/geyser/scoreboard/network/sidebar/OrderAndLimitSidebarScoreboardTests.java @@ -0,0 +1,533 @@ +/* + * Copyright (c) 2024 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.scoreboard.network.sidebar; + +import static org.geysermc.geyser.scoreboard.network.util.AssertUtils.assertNextPacket; +import static org.geysermc.geyser.scoreboard.network.util.AssertUtils.assertNoNextPacket; +import static org.geysermc.geyser.scoreboard.network.util.GeyserMockContextScoreboard.mockContextScoreboard; + +import java.util.List; +import net.kyori.adventure.text.Component; +import org.cloudburstmc.protocol.bedrock.data.ScoreInfo; +import org.cloudburstmc.protocol.bedrock.packet.SetDisplayObjectivePacket; +import org.cloudburstmc.protocol.bedrock.packet.SetScorePacket; +import org.geysermc.geyser.translator.protocol.java.scoreboard.JavaResetScorePacket; +import org.geysermc.geyser.translator.protocol.java.scoreboard.JavaSetDisplayObjectiveTranslator; +import org.geysermc.geyser.translator.protocol.java.scoreboard.JavaSetObjectiveTranslator; +import org.geysermc.geyser.translator.protocol.java.scoreboard.JavaSetPlayerTeamTranslator; +import org.geysermc.geyser.translator.protocol.java.scoreboard.JavaSetScoreTranslator; +import org.geysermc.mcprotocollib.protocol.data.game.scoreboard.CollisionRule; +import org.geysermc.mcprotocollib.protocol.data.game.scoreboard.NameTagVisibility; +import org.geysermc.mcprotocollib.protocol.data.game.scoreboard.ObjectiveAction; +import org.geysermc.mcprotocollib.protocol.data.game.scoreboard.ScoreType; +import org.geysermc.mcprotocollib.protocol.data.game.scoreboard.ScoreboardPosition; +import org.geysermc.mcprotocollib.protocol.data.game.scoreboard.TeamColor; +import org.geysermc.mcprotocollib.protocol.packet.ingame.clientbound.scoreboard.ClientboundResetScorePacket; +import org.geysermc.mcprotocollib.protocol.packet.ingame.clientbound.scoreboard.ClientboundSetDisplayObjectivePacket; +import org.geysermc.mcprotocollib.protocol.packet.ingame.clientbound.scoreboard.ClientboundSetObjectivePacket; +import org.geysermc.mcprotocollib.protocol.packet.ingame.clientbound.scoreboard.ClientboundSetPlayerTeamPacket; +import org.geysermc.mcprotocollib.protocol.packet.ingame.clientbound.scoreboard.ClientboundSetScorePacket; +import org.junit.jupiter.api.Test; + +public class OrderAndLimitSidebarScoreboardTests { + @Test + void aboveDisplayLimit() { + mockContextScoreboard(context -> { + var setObjectiveTranslator = new JavaSetObjectiveTranslator(); + var setDisplayObjectiveTranslator = new JavaSetDisplayObjectiveTranslator(); + var setScoreTranslator = new JavaSetScoreTranslator(); + var resetScoreTranslator = new JavaResetScorePacket(); + + context.translate( + setObjectiveTranslator, + new ClientboundSetObjectivePacket( + "objective", + ObjectiveAction.ADD, + Component.text("objective"), + ScoreType.INTEGER, + null + ) + ); + + // some are in an odd order to make sure that there is no bias for which score is send first, + // and to make sure that the score value also doesn't influence the order + context.translate(setScoreTranslator, new ClientboundSetScorePacket("a", "objective", 1)); + context.translate(setScoreTranslator, new ClientboundSetScorePacket("b", "objective", 2)); + context.translate(setScoreTranslator, new ClientboundSetScorePacket("c", "objective", 3)); + context.translate(setScoreTranslator, new ClientboundSetScorePacket("d", "objective", 5)); + context.translate(setScoreTranslator, new ClientboundSetScorePacket("e", "objective", 4)); + context.translate(setScoreTranslator, new ClientboundSetScorePacket("f", "objective", 6)); + context.translate(setScoreTranslator, new ClientboundSetScorePacket("g", "objective", 9)); + context.translate(setScoreTranslator, new ClientboundSetScorePacket("h", "objective", 8)); + context.translate(setScoreTranslator, new ClientboundSetScorePacket("i", "objective", 7)); + context.translate(setScoreTranslator, new ClientboundSetScorePacket("p", "objective", 10)); + context.translate(setScoreTranslator, new ClientboundSetScorePacket("o", "objective", 11)); + context.translate(setScoreTranslator, new ClientboundSetScorePacket("n", "objective", 12)); + context.translate(setScoreTranslator, new ClientboundSetScorePacket("m", "objective", 13)); + context.translate(setScoreTranslator, new ClientboundSetScorePacket("k", "objective", 14)); + context.translate(setScoreTranslator, new ClientboundSetScorePacket("l", "objective", 15)); + context.translate(setScoreTranslator, new ClientboundSetScorePacket("j", "objective", 16)); + context.translate(setScoreTranslator, new ClientboundSetScorePacket("q", "objective", 17)); + assertNoNextPacket(context); + + + context.translate( + setDisplayObjectiveTranslator, + new ClientboundSetDisplayObjectivePacket(ScoreboardPosition.SIDEBAR, "objective") + ); + assertNextPacket(context, () -> { + var packet = new SetDisplayObjectivePacket(); + packet.setObjectiveId("0"); + packet.setDisplayName("objective"); + packet.setCriteria("dummy"); + packet.setDisplaySlot("sidebar"); + packet.setSortOrder(1); + return packet; + }); + assertNextPacket(context, () -> { + var packet = new SetScorePacket(); + packet.setAction(SetScorePacket.Action.SET); + packet.setInfos(List.of( + new ScoreInfo(1, "0", 17, "q"), + new ScoreInfo(2, "0", 16, "j"), + new ScoreInfo(3, "0", 15, "l"), + new ScoreInfo(4, "0", 14, "k"), + new ScoreInfo(5, "0", 13, "m"), + new ScoreInfo(6, "0", 12, "n"), + new ScoreInfo(7, "0", 11, "o"), + new ScoreInfo(8, "0", 10, "p"), + new ScoreInfo(9, "0", 9, "g"), + new ScoreInfo(10, "0", 8, "h"), + new ScoreInfo(11, "0", 7, "i"), + new ScoreInfo(12, "0", 6, "f"), + new ScoreInfo(13, "0", 5, "d"), + new ScoreInfo(14, "0", 4, "e"), + new ScoreInfo(15, "0", 3, "c") + )); + return packet; + }); + assertNoNextPacket(context); + + // remove a score + context.translate( + resetScoreTranslator, + new ClientboundResetScorePacket("m", "objective") + ); + assertNextPacket(context, () -> { + var packet = new SetScorePacket(); + packet.setAction(SetScorePacket.Action.REMOVE); + packet.setInfos(List.of(new ScoreInfo(5, "0", 13, "m"))); + return packet; + }); + assertNextPacket(context, () -> { + var packet = new SetScorePacket(); + packet.setAction(SetScorePacket.Action.SET); + packet.setInfos(List.of(new ScoreInfo(16, "0", 2, "b"))); + return packet; + }); + + // add a score + context.translate( + setScoreTranslator, + new ClientboundSetScorePacket("aa", "objective", 13) + ); + assertNextPacket(context, () -> { + var packet = new SetScorePacket(); + packet.setAction(SetScorePacket.Action.REMOVE); + packet.setInfos(List.of(new ScoreInfo(16, "0", 2, "b"))); + return packet; + }); + assertNextPacket(context, () -> { + var packet = new SetScorePacket(); + packet.setAction(SetScorePacket.Action.SET); + packet.setInfos(List.of(new ScoreInfo(17, "0", 13, "aa"))); + return packet; + }); + + // add score with same score value (after) + context.translate( + setScoreTranslator, + new ClientboundSetScorePacket("ga", "objective", 9) + ); + assertNextPacket(context, () -> { + var packet = new SetScorePacket(); + packet.setAction(SetScorePacket.Action.REMOVE); + packet.setInfos(List.of( + new ScoreInfo(15, "0", 3, "c"), + new ScoreInfo(9, "0", 9, "§0§rg") + )); + return packet; + }); + assertNextPacket(context, () -> { + var packet = new SetScorePacket(); + packet.setAction(SetScorePacket.Action.SET); + packet.setInfos(List.of( + new ScoreInfo(9, "0", 9, "§0§rg"), + new ScoreInfo(18, "0", 9, "§1§rga") + )); + return packet; + }); + + // add another score with same score value (before all) + context.translate( + setScoreTranslator, + new ClientboundSetScorePacket("ag", "objective", 9) + ); + assertNextPacket(context, () -> { + var packet = new SetScorePacket(); + packet.setAction(SetScorePacket.Action.REMOVE); + packet.setInfos(List.of( + new ScoreInfo(14, "0", 4, "e"), + new ScoreInfo(9, "0", 9, "§1§rg"), + new ScoreInfo(18, "0", 9, "§2§rga") + )); + return packet; + }); + assertNextPacket(context, () -> { + var packet = new SetScorePacket(); + packet.setAction(SetScorePacket.Action.SET); + packet.setInfos(List.of( + new ScoreInfo(19, "0", 9, "§0§rag"), + new ScoreInfo(9, "0", 9, "§1§rg"), + new ScoreInfo(18, "0", 9, "§2§rga") + )); + return packet; + }); + + // remove score with same value + context.translate( + resetScoreTranslator, + new ClientboundResetScorePacket("g", "objective") + ); + assertNextPacket(context, () -> { + var packet = new SetScorePacket(); + packet.setAction(SetScorePacket.Action.REMOVE); + packet.setInfos(List.of( + new ScoreInfo(9, "0", 9, "§1§rg"), + new ScoreInfo(18, "0", 9, "§1§rga") + )); + return packet; + }); + assertNextPacket(context, () -> { + var packet = new SetScorePacket(); + packet.setAction(SetScorePacket.Action.SET); + packet.setInfos(List.of( + new ScoreInfo(18, "0", 9, "§1§rga"), + new ScoreInfo(20, "0", 4, "e") + )); + return packet; + }); + + // remove the other score with the same value + context.translate( + resetScoreTranslator, + new ClientboundResetScorePacket("ga", "objective") + ); + assertNextPacket(context, () -> { + var packet = new SetScorePacket(); + packet.setAction(SetScorePacket.Action.REMOVE); + packet.setInfos(List.of( + new ScoreInfo(18, "0", 9, "§1§rga"), + new ScoreInfo(19, "0", 9, "ag") + )); + return packet; + }); + assertNextPacket(context, () -> { + var packet = new SetScorePacket(); + packet.setAction(SetScorePacket.Action.SET); + packet.setInfos(List.of( + new ScoreInfo(19, "0", 9, "ag"), + new ScoreInfo(21, "0", 3, "c") + )); + return packet; + }); + }); + } + + @Test + void aboveDisplayLimitWithTeam() { + mockContextScoreboard(context -> { + var setObjectiveTranslator = new JavaSetObjectiveTranslator(); + var setDisplayObjectiveTranslator = new JavaSetDisplayObjectiveTranslator(); + var setScoreTranslator = new JavaSetScoreTranslator(); + var resetScoreTranslator = new JavaResetScorePacket(); + var setPlayerTeamTranslator = new JavaSetPlayerTeamTranslator(); + + context.translate( + setObjectiveTranslator, + new ClientboundSetObjectivePacket( + "objective", + ObjectiveAction.ADD, + Component.text("objective"), + ScoreType.INTEGER, + null + ) + ); + + // some are in an odd order to make sure that there is no bias for which score is send first, + // and to make sure that the score value also doesn't influence the order + context.translate(setScoreTranslator, new ClientboundSetScorePacket("a", "objective", 1)); + context.translate(setScoreTranslator, new ClientboundSetScorePacket("b", "objective", 2)); + context.translate(setScoreTranslator, new ClientboundSetScorePacket("c", "objective", 3)); + context.translate(setScoreTranslator, new ClientboundSetScorePacket("d", "objective", 5)); + context.translate(setScoreTranslator, new ClientboundSetScorePacket("e", "objective", 4)); + context.translate(setScoreTranslator, new ClientboundSetScorePacket("f", "objective", 6)); + context.translate(setScoreTranslator, new ClientboundSetScorePacket("g", "objective", 9)); + context.translate(setScoreTranslator, new ClientboundSetScorePacket("h", "objective", 8)); + context.translate(setScoreTranslator, new ClientboundSetScorePacket("i", "objective", 7)); + context.translate(setScoreTranslator, new ClientboundSetScorePacket("p", "objective", 10)); + context.translate(setScoreTranslator, new ClientboundSetScorePacket("o", "objective", 11)); + context.translate(setScoreTranslator, new ClientboundSetScorePacket("n", "objective", 12)); + context.translate(setScoreTranslator, new ClientboundSetScorePacket("m", "objective", 13)); + context.translate(setScoreTranslator, new ClientboundSetScorePacket("k", "objective", 14)); + context.translate(setScoreTranslator, new ClientboundSetScorePacket("l", "objective", 15)); + context.translate(setScoreTranslator, new ClientboundSetScorePacket("j", "objective", 16)); + context.translate(setScoreTranslator, new ClientboundSetScorePacket("q", "objective", 17)); + context.translate( + setPlayerTeamTranslator, + new ClientboundSetPlayerTeamPacket( + "team1", + Component.text("displayName"), + Component.text("prefix"), + Component.text("suffix"), + false, + false, + NameTagVisibility.ALWAYS, + CollisionRule.NEVER, + TeamColor.DARK_RED, + new String[]{ "f", "o" } + ) + ); + assertNoNextPacket(context); + + context.translate( + setDisplayObjectiveTranslator, + new ClientboundSetDisplayObjectivePacket(ScoreboardPosition.SIDEBAR, "objective") + ); + assertNextPacket(context, () -> { + var packet = new SetDisplayObjectivePacket(); + packet.setObjectiveId("0"); + packet.setDisplayName("objective"); + packet.setCriteria("dummy"); + packet.setDisplaySlot("sidebar"); + packet.setSortOrder(1); + return packet; + }); + assertNextPacket(context, () -> { + var packet = new SetScorePacket(); + packet.setAction(SetScorePacket.Action.SET); + packet.setInfos(List.of( + new ScoreInfo(1, "0", 17, "q"), + new ScoreInfo(2, "0", 16, "j"), + new ScoreInfo(3, "0", 15, "l"), + new ScoreInfo(4, "0", 14, "k"), + new ScoreInfo(5, "0", 13, "m"), + new ScoreInfo(6, "0", 12, "n"), + new ScoreInfo(7, "0", 11, "§4prefix§r§4o§r§4suffix"), + new ScoreInfo(8, "0", 10, "p"), + new ScoreInfo(9, "0", 9, "g"), + new ScoreInfo(10, "0", 8, "h"), + new ScoreInfo(11, "0", 7, "i"), + new ScoreInfo(12, "0", 6, "§4prefix§r§4f§r§4suffix"), + new ScoreInfo(13, "0", 5, "d"), + new ScoreInfo(14, "0", 4, "e"), + new ScoreInfo(15, "0", 3, "c") + )); + return packet; + }); + assertNoNextPacket(context); + + // remove a score + context.translate( + resetScoreTranslator, + new ClientboundResetScorePacket("m", "objective") + ); + assertNextPacket(context, () -> { + var packet = new SetScorePacket(); + packet.setAction(SetScorePacket.Action.REMOVE); + packet.setInfos(List.of(new ScoreInfo(5, "0", 13, "m"))); + return packet; + }); + assertNextPacket(context, () -> { + var packet = new SetScorePacket(); + packet.setAction(SetScorePacket.Action.SET); + packet.setInfos(List.of(new ScoreInfo(16, "0", 2, "b"))); + return packet; + }); + + // add a score + context.translate( + setScoreTranslator, + new ClientboundSetScorePacket("aa", "objective", 13) + ); + assertNextPacket(context, () -> { + var packet = new SetScorePacket(); + packet.setAction(SetScorePacket.Action.REMOVE); + packet.setInfos(List.of(new ScoreInfo(16, "0", 2, "b"))); + return packet; + }); + assertNextPacket(context, () -> { + var packet = new SetScorePacket(); + packet.setAction(SetScorePacket.Action.SET); + packet.setInfos(List.of(new ScoreInfo(17, "0", 13, "aa"))); + return packet; + }); + + // add some teams for the upcoming score adds + context.translate( + setPlayerTeamTranslator, + new ClientboundSetPlayerTeamPacket( + "team2", + Component.text("displayName"), + Component.text("prefix"), + Component.text("suffix"), + false, + false, + NameTagVisibility.ALWAYS, + CollisionRule.NEVER, + TeamColor.DARK_AQUA, + new String[]{ "oa" } + ) + ); + context.translate( + setPlayerTeamTranslator, + new ClientboundSetPlayerTeamPacket( + "team3", + Component.text("displayName"), + Component.text("prefix"), + Component.text("suffix"), + false, + false, + NameTagVisibility.ALWAYS, + CollisionRule.NEVER, + TeamColor.DARK_PURPLE, + new String[]{ "ao" } + ) + ); + assertNoNextPacket(context); + + // add a score that on Java should be after 'o', but would be before on Bedrock without manual order + // due to the team color + context.translate( + setScoreTranslator, + new ClientboundSetScorePacket("oa", "objective", 11) + ); + assertNextPacket(context, () -> { + var packet = new SetScorePacket(); + packet.setAction(SetScorePacket.Action.REMOVE); + packet.setInfos(List.of( + new ScoreInfo(15, "0", 3, "c"), + new ScoreInfo(7, "0", 11, "§0§r§4prefix§r§4o§r§4suffix") + )); + return packet; + }); + assertNextPacket(context, () -> { + var packet = new SetScorePacket(); + packet.setAction(SetScorePacket.Action.SET); + packet.setInfos(List.of( + new ScoreInfo(7, "0", 11, "§0§r§4prefix§r§4o§r§4suffix"), + new ScoreInfo(18, "0", 11, "§1§r§3prefix§r§3oa§r§3suffix") + )); + return packet; + }); + + // add a score that on Java should be before 'o', but would be after on Bedrock without manual order + // due to the team color + context.translate( + setScoreTranslator, + new ClientboundSetScorePacket("ao", "objective", 11) + ); + assertNextPacket(context, () -> { + var packet = new SetScorePacket(); + packet.setAction(SetScorePacket.Action.REMOVE); + packet.setInfos(List.of( + new ScoreInfo(14, "0", 4, "e"), + new ScoreInfo(7, "0", 11, "§1§r§4prefix§r§4o§r§4suffix"), + new ScoreInfo(18, "0", 11, "§2§r§3prefix§r§3oa§r§3suffix") + )); + return packet; + }); + assertNextPacket(context, () -> { + var packet = new SetScorePacket(); + packet.setAction(SetScorePacket.Action.SET); + packet.setInfos(List.of( + new ScoreInfo(19, "0", 11, "§0§r§5prefix§r§5ao§r§5suffix"), + new ScoreInfo(7, "0", 11, "§1§r§4prefix§r§4o§r§4suffix"), + new ScoreInfo(18, "0", 11, "§2§r§3prefix§r§3oa§r§3suffix") + )); + return packet; + }); + + // remove original 'o' score + context.translate( + resetScoreTranslator, + new ClientboundResetScorePacket("o", "objective") + ); + assertNextPacket(context, () -> { + var packet = new SetScorePacket(); + packet.setAction(SetScorePacket.Action.REMOVE); + packet.setInfos(List.of( + new ScoreInfo(7, "0", 11, "§1§r§4prefix§r§4o§r§4suffix"), + new ScoreInfo(18, "0", 11, "§1§r§3prefix§r§3oa§r§3suffix") + )); + return packet; + }); + assertNextPacket(context, () -> { + var packet = new SetScorePacket(); + packet.setAction(SetScorePacket.Action.SET); + packet.setInfos(List.of( + new ScoreInfo(18, "0", 11, "§1§r§3prefix§r§3oa§r§3suffix"), + new ScoreInfo(20, "0", 4, "e") + )); + return packet; + }); + + // remove the other score with the same value as 'o' + context.translate( + resetScoreTranslator, + new ClientboundResetScorePacket("oa", "objective") + ); + assertNextPacket(context, () -> { + var packet = new SetScorePacket(); + packet.setAction(SetScorePacket.Action.REMOVE); + packet.setInfos(List.of( + new ScoreInfo(18, "0", 11, "§1§r§3prefix§r§3oa§r§3suffix"), + new ScoreInfo(19, "0", 11, "§5prefix§r§5ao§r§5suffix") + )); + return packet; + }); + assertNextPacket(context, () -> { + var packet = new SetScorePacket(); + packet.setAction(SetScorePacket.Action.SET); + packet.setInfos(List.of( + new ScoreInfo(19, "0", 11, "§5prefix§r§5ao§r§5suffix"), + new ScoreInfo(21, "0", 3, "c") + )); + return packet; + }); + }); + } +} diff --git a/core/src/test/java/org/geysermc/geyser/scoreboard/network/sidebar/VanillaSidebarScoreboardTests.java b/core/src/test/java/org/geysermc/geyser/scoreboard/network/sidebar/VanillaSidebarScoreboardTests.java new file mode 100644 index 000000000..f511f59c7 --- /dev/null +++ b/core/src/test/java/org/geysermc/geyser/scoreboard/network/sidebar/VanillaSidebarScoreboardTests.java @@ -0,0 +1,265 @@ +/* + * Copyright (c) 2024 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.scoreboard.network.sidebar; + +import static org.geysermc.geyser.scoreboard.network.util.AssertUtils.assertNextPacket; +import static org.geysermc.geyser.scoreboard.network.util.AssertUtils.assertNoNextPacket; +import static org.geysermc.geyser.scoreboard.network.util.GeyserMockContextScoreboard.mockContextScoreboard; + +import java.util.List; +import net.kyori.adventure.text.Component; +import org.cloudburstmc.protocol.bedrock.data.ScoreInfo; +import org.cloudburstmc.protocol.bedrock.packet.SetDisplayObjectivePacket; +import org.cloudburstmc.protocol.bedrock.packet.SetScorePacket; +import org.geysermc.geyser.translator.protocol.java.scoreboard.JavaSetDisplayObjectiveTranslator; +import org.geysermc.geyser.translator.protocol.java.scoreboard.JavaSetObjectiveTranslator; +import org.geysermc.geyser.translator.protocol.java.scoreboard.JavaSetScoreTranslator; +import org.geysermc.mcprotocollib.protocol.data.game.scoreboard.ObjectiveAction; +import org.geysermc.mcprotocollib.protocol.data.game.scoreboard.ScoreType; +import org.geysermc.mcprotocollib.protocol.data.game.scoreboard.ScoreboardPosition; +import org.geysermc.mcprotocollib.protocol.packet.ingame.clientbound.scoreboard.ClientboundSetDisplayObjectivePacket; +import org.geysermc.mcprotocollib.protocol.packet.ingame.clientbound.scoreboard.ClientboundSetObjectivePacket; +import org.geysermc.mcprotocollib.protocol.packet.ingame.clientbound.scoreboard.ClientboundSetScorePacket; +import org.junit.jupiter.api.Test; + +public class VanillaSidebarScoreboardTests { + @Test + void displayAndAddScore() { + mockContextScoreboard(context -> { + var setObjectiveTranslator = new JavaSetObjectiveTranslator(); + var setDisplayObjectiveTranslator = new JavaSetDisplayObjectiveTranslator(); + var setScoreTranslator = new JavaSetScoreTranslator(); + + context.translate( + setObjectiveTranslator, + new ClientboundSetObjectivePacket( + "objective", + ObjectiveAction.ADD, + Component.text("objective"), + ScoreType.INTEGER, + null + ) + ); + assertNoNextPacket(context); + + context.translate( + setDisplayObjectiveTranslator, + new ClientboundSetDisplayObjectivePacket(ScoreboardPosition.SIDEBAR, "objective") + ); + assertNextPacket(context, () -> { + var packet = new SetDisplayObjectivePacket(); + packet.setObjectiveId("0"); + packet.setDisplayName("objective"); + packet.setCriteria("dummy"); + packet.setDisplaySlot("sidebar"); + packet.setSortOrder(1); + return packet; + }); + assertNoNextPacket(context); + + context.translate(setScoreTranslator, new ClientboundSetScorePacket("owner", "objective", 1)); + assertNextPacket(context, () -> { + var packet = new SetScorePacket(); + packet.setAction(SetScorePacket.Action.SET); + packet.setInfos(List.of(new ScoreInfo(1, "0", 1, "owner"))); + return packet; + }); + }); + } + + @Test + void displayAndChangeScoreValue() { + mockContextScoreboard(context -> { + var setObjectiveTranslator = new JavaSetObjectiveTranslator(); + var setDisplayObjectiveTranslator = new JavaSetDisplayObjectiveTranslator(); + var setScoreTranslator = new JavaSetScoreTranslator(); + + context.translate( + setObjectiveTranslator, + new ClientboundSetObjectivePacket( + "objective", + ObjectiveAction.ADD, + Component.text("objective"), + ScoreType.INTEGER, + null + ) + ); + context.translate(setScoreTranslator, new ClientboundSetScorePacket("owner", "objective", 1)); + assertNoNextPacket(context); + + context.translate( + setDisplayObjectiveTranslator, + new ClientboundSetDisplayObjectivePacket(ScoreboardPosition.SIDEBAR, "objective") + ); + assertNextPacket(context, () -> { + var packet = new SetDisplayObjectivePacket(); + packet.setObjectiveId("0"); + packet.setDisplayName("objective"); + packet.setCriteria("dummy"); + packet.setDisplaySlot("sidebar"); + packet.setSortOrder(1); + return packet; + }); + assertNextPacket(context, () -> { + var packet = new SetScorePacket(); + packet.setAction(SetScorePacket.Action.SET); + packet.setInfos(List.of(new ScoreInfo(1, "0", 1, "owner"))); + return packet; + }); + assertNoNextPacket(context); + + context.translate(setScoreTranslator, new ClientboundSetScorePacket("owner", "objective", 2)); + assertNextPacket(context, () -> { + var packet = new SetScorePacket(); + packet.setAction(SetScorePacket.Action.SET); + packet.setInfos(List.of(new ScoreInfo(1, "0", 2, "owner"))); + return packet; + }); + }); + } + + @Test + void displayAndChangeScoreDisplayName() { + // this ensures that MCPE-143063 is properly handled + mockContextScoreboard(context -> { + var setObjectiveTranslator = new JavaSetObjectiveTranslator(); + var setDisplayObjectiveTranslator = new JavaSetDisplayObjectiveTranslator(); + var setScoreTranslator = new JavaSetScoreTranslator(); + + context.translate( + setObjectiveTranslator, + new ClientboundSetObjectivePacket( + "objective", + ObjectiveAction.ADD, + Component.text("objective"), + ScoreType.INTEGER, + null + ) + ); + context.translate(setScoreTranslator, new ClientboundSetScorePacket("owner", "objective", 1)); + assertNoNextPacket(context); + + context.translate( + setDisplayObjectiveTranslator, + new ClientboundSetDisplayObjectivePacket(ScoreboardPosition.SIDEBAR, "objective") + ); + assertNextPacket(context, () -> { + var packet = new SetDisplayObjectivePacket(); + packet.setObjectiveId("0"); + packet.setDisplayName("objective"); + packet.setCriteria("dummy"); + packet.setDisplaySlot("sidebar"); + packet.setSortOrder(1); + return packet; + }); + assertNextPacket(context, () -> { + var packet = new SetScorePacket(); + packet.setAction(SetScorePacket.Action.SET); + packet.setInfos(List.of(new ScoreInfo(1, "0", 1, "owner"))); + return packet; + }); + assertNoNextPacket(context); + + context.translate( + setScoreTranslator, + new ClientboundSetScorePacket("owner", "objective", 1).withDisplay(Component.text("hi")) + ); + assertNextPacket(context, () -> { + var packet = new SetScorePacket(); + packet.setAction(SetScorePacket.Action.REMOVE); + packet.setInfos(List.of(new ScoreInfo(1, "0", 1, "hi"))); + return packet; + }); + assertNextPacket(context, () -> { + var packet = new SetScorePacket(); + packet.setAction(SetScorePacket.Action.SET); + packet.setInfos(List.of(new ScoreInfo(1, "0", 1, "hi"))); + return packet; + }); + }); + } + + @Test + void displayAndChangeScoreDisplayNameAndValue() { + // this ensures that MCPE-143063 is properly handled + mockContextScoreboard(context -> { + var setObjectiveTranslator = new JavaSetObjectiveTranslator(); + var setDisplayObjectiveTranslator = new JavaSetDisplayObjectiveTranslator(); + var setScoreTranslator = new JavaSetScoreTranslator(); + + context.translate( + setObjectiveTranslator, + new ClientboundSetObjectivePacket( + "objective", + ObjectiveAction.ADD, + Component.text("objective"), + ScoreType.INTEGER, + null + ) + ); + context.translate(setScoreTranslator, new ClientboundSetScorePacket("owner", "objective", 1)); + assertNoNextPacket(context); + + context.translate( + setDisplayObjectiveTranslator, + new ClientboundSetDisplayObjectivePacket(ScoreboardPosition.SIDEBAR, "objective") + ); + assertNextPacket(context, () -> { + var packet = new SetDisplayObjectivePacket(); + packet.setObjectiveId("0"); + packet.setDisplayName("objective"); + packet.setCriteria("dummy"); + packet.setDisplaySlot("sidebar"); + packet.setSortOrder(1); + return packet; + }); + assertNextPacket(context, () -> { + var packet = new SetScorePacket(); + packet.setAction(SetScorePacket.Action.SET); + packet.setInfos(List.of(new ScoreInfo(1, "0", 1, "owner"))); + return packet; + }); + assertNoNextPacket(context); + + context.translate( + setScoreTranslator, + new ClientboundSetScorePacket("owner", "objective", 2).withDisplay(Component.text("hi")) + ); + assertNextPacket(context, () -> { + var packet = new SetScorePacket(); + packet.setAction(SetScorePacket.Action.REMOVE); + packet.setInfos(List.of(new ScoreInfo(1, "0", 2, "hi"))); + return packet; + }); + assertNextPacket(context, () -> { + var packet = new SetScorePacket(); + packet.setAction(SetScorePacket.Action.SET); + packet.setInfos(List.of(new ScoreInfo(1, "0", 2, "hi"))); + return packet; + }); + }); + } +} diff --git a/core/src/test/java/org/geysermc/geyser/scoreboard/network/util/AssertUtils.java b/core/src/test/java/org/geysermc/geyser/scoreboard/network/util/AssertUtils.java new file mode 100644 index 000000000..9177f205a --- /dev/null +++ b/core/src/test/java/org/geysermc/geyser/scoreboard/network/util/AssertUtils.java @@ -0,0 +1,71 @@ +/* + * Copyright (c) 2024 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.scoreboard.network.util; + +import java.util.Collections; +import java.util.function.Consumer; +import java.util.function.Supplier; +import org.cloudburstmc.protocol.bedrock.packet.BedrockPacket; +import org.junit.jupiter.api.Assertions; + +public class AssertUtils { + public static void assertContextEquals(Supplier expected, T actual) { + if (actual == null) { + Assertions.fail("Expected another packet! " + expected.get()); + } + Assertions.assertEquals(expected.get(), actual); + } + + public static void assertNextPacket(GeyserMockContext context, Supplier expected) { + assertContextEquals(expected, context.nextPacket()); + } + + public static void assertNextPacketType(GeyserMockContext context, Class type) { + var actual = context.nextPacket(); + if (actual == null) { + Assertions.fail("Expected another packet! " + type); + } + Assertions.assertEquals(type, actual.getClass()); + } + + public static void assertNextPacketMatch(GeyserMockContext context, Class type, Consumer matcher) { + var actual = context.nextPacket(); + if (actual == null) { + Assertions.fail("Expected another packet!"); + } + Assertions.assertEquals(type, actual.getClass(), "Expected packet to be an instance of " + type); + //noinspection unchecked verified in the line above me + matcher.accept((T) actual); + } + + public static void assertNoNextPacket(GeyserMockContext context) { + Assertions.assertEquals( + Collections.emptyList(), + context.packets(), + "Expected no remaining packets, got " + context.packetCount() + ); + } +} diff --git a/core/src/test/java/org/geysermc/geyser/scoreboard/network/util/EmptyGeyserLogger.java b/core/src/test/java/org/geysermc/geyser/scoreboard/network/util/EmptyGeyserLogger.java new file mode 100644 index 000000000..f147e766d --- /dev/null +++ b/core/src/test/java/org/geysermc/geyser/scoreboard/network/util/EmptyGeyserLogger.java @@ -0,0 +1,75 @@ +/* + * Copyright (c) 2024 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.scoreboard.network.util; + +import org.geysermc.geyser.GeyserLogger; + +public class EmptyGeyserLogger implements GeyserLogger { + @Override + public void severe(String message) { + + } + + @Override + public void severe(String message, Throwable error) { + + } + + @Override + public void error(String message) { + + } + + @Override + public void error(String message, Throwable error) { + + } + + @Override + public void warning(String message) { + + } + + @Override + public void info(String message) { + + } + + @Override + public void debug(String message) { + + } + + @Override + public void setDebug(boolean debug) { + + } + + @Override + public boolean isDebug() { + return false; + } +} diff --git a/core/src/test/java/org/geysermc/geyser/scoreboard/network/util/GeyserMockContext.java b/core/src/test/java/org/geysermc/geyser/scoreboard/network/util/GeyserMockContext.java new file mode 100644 index 000000000..6b0fc4dca --- /dev/null +++ b/core/src/test/java/org/geysermc/geyser/scoreboard/network/util/GeyserMockContext.java @@ -0,0 +1,140 @@ +/* + * Copyright (c) 2024 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.scoreboard.network.util; + +import org.cloudburstmc.protocol.bedrock.packet.BedrockPacket; +import org.geysermc.geyser.GeyserImpl; +import org.geysermc.geyser.configuration.AdvancedConfig; +import org.geysermc.geyser.configuration.GeyserConfig; +import org.geysermc.geyser.session.GeyserSession; +import org.geysermc.geyser.translator.protocol.PacketTranslator; +import org.mockito.Mockito; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.function.Consumer; + +import static org.mockito.Mockito.mockStatic; +import static org.mockito.Mockito.when; + +public class GeyserMockContext { + private final List mocksAndSpies = new ArrayList<>(); + private final List storedObjects = new ArrayList<>(); + private final List packets = Collections.synchronizedList(new ArrayList<>()); + + public static void mockContext(Consumer geyserContext) { + var context = new GeyserMockContext(); + + var geyserImpl = context.mock(GeyserImpl.class); + var config = context.mock(GeyserConfig.class); + when(config.advanced()).thenReturn(context.mock(AdvancedConfig.class)); + + when(config.advanced().scoreboardPacketThreshold()).thenReturn(1_000); + + when(geyserImpl.config()).thenReturn(config); + + var logger = context.storeObject(new EmptyGeyserLogger()); + when(geyserImpl.getLogger()).thenReturn(logger); + + try (var geyserImplMock = mockStatic(GeyserImpl.class)) { + geyserImplMock.when(GeyserImpl::getInstance).thenReturn(geyserImpl); + + geyserContext.accept(context); + } + } + + public static void mockContext(Runnable runnable) { + mockContext(context -> runnable.run()); + } + + public T mock(Class type) { + return addMockOrSpy(Mockito.mock(type)); + } + + public T spy(T object) { + return addMockOrSpy(Mockito.spy(object)); + } + + private T addMockOrSpy(T mockOrSpy) { + mocksAndSpies.add(mockOrSpy); + return mockOrSpy; + } + + public T storeObject(T object) { + storedObjects.add(object); + return object; + } + + /** + * Retries the mock or spy that is an instance of the specified type. + * This is only really intended for classes where you only need a single instance of. + */ + public T mockOrSpy(Class type) { + for (Object mock : mocksAndSpies) { + if (type.isInstance(mock)) { + return type.cast(mock); + } + } + return null; + } + + public T storedObject(Class type) { + for (Object storedObject : storedObjects) { + if (type.isInstance(storedObject)) { + return type.cast(storedObject); + } + } + return null; + } + + public GeyserSession session() { + return mockOrSpy(GeyserSession.class); + } + + void addPacket(BedrockPacket packet) { + packets.add(packet); + } + + public int packetCount() { + return packets.size(); + } + + public BedrockPacket nextPacket() { + if (packets.isEmpty()) { + return null; + } + return packets.remove(0); + } + + public List packets() { + return Collections.unmodifiableList(packets); + } + + public void translate(PacketTranslator translator, T packet) { + translator.translate(session(), packet); + } +} diff --git a/core/src/test/java/org/geysermc/geyser/scoreboard/network/util/GeyserMockContextScoreboard.java b/core/src/test/java/org/geysermc/geyser/scoreboard/network/util/GeyserMockContextScoreboard.java new file mode 100644 index 000000000..e0d9918e5 --- /dev/null +++ b/core/src/test/java/org/geysermc/geyser/scoreboard/network/util/GeyserMockContextScoreboard.java @@ -0,0 +1,104 @@ +/* + * Copyright (c) 2024 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.scoreboard.network.util; + +import static org.geysermc.geyser.scoreboard.network.util.AssertUtils.assertNextPacketType; +import static org.geysermc.geyser.scoreboard.network.util.AssertUtils.assertNoNextPacket; +import static org.geysermc.geyser.scoreboard.network.util.GeyserMockContext.mockContext; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.doAnswer; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.when; + +import java.util.UUID; +import java.util.function.Consumer; +import org.cloudburstmc.math.vector.Vector3f; +import org.cloudburstmc.protocol.bedrock.packet.AddPlayerPacket; +import org.cloudburstmc.protocol.bedrock.packet.BedrockPacket; +import org.geysermc.geyser.GeyserImpl; +import org.geysermc.geyser.entity.type.player.PlayerEntity; +import org.geysermc.geyser.entity.type.player.SessionPlayerEntity; +import org.geysermc.geyser.session.GeyserSession; +import org.geysermc.geyser.session.cache.EntityCache; +import org.geysermc.geyser.session.cache.WorldCache; +import org.mockito.stubbing.Answer; + +public class GeyserMockContextScoreboard { + public static void mockContextScoreboard(Consumer geyserContext) { + mockContext(context -> { + createSessionSpy(context); + geyserContext.accept(context); + + assertNoNextPacket(context); + }); + } + + private static void createSessionSpy(GeyserMockContext context) { + // GeyserSession has so many dependencies, it's easier to just mock it + var session = context.mock(GeyserSession.class); + + when(session.getGeyser()).thenReturn(context.mockOrSpy(GeyserImpl.class)); + + when(session.locale()).thenReturn("en_US"); + + doAnswer((Answer) invocation -> { + context.addPacket(invocation.getArgument(0, BedrockPacket.class)); + return null; + }).when(session).sendUpstreamPacket(any()); + + // SessionPlayerEntity loads stuff in like blocks, which is not what we want + var playerEntity = context.mock(SessionPlayerEntity.class); + when(playerEntity.getGeyserId()).thenReturn(1L); + when(playerEntity.getUsername()).thenReturn("Tim203"); + when(session.getPlayerEntity()).thenReturn(playerEntity); + + var entityCache = context.spy(new EntityCache(session)); + when(session.getEntityCache()).thenReturn(entityCache); + + var worldCache = context.spy(new WorldCache(session)); + when(session.getWorldCache()).thenReturn(worldCache); + + // disable global scoreboard updater + when(worldCache.increaseAndGetScoreboardPacketsPerSecond()).thenReturn(0); + } + + public static PlayerEntity spawnPlayerSilently(GeyserMockContext context, String username, long geyserId) { + var player = spawnPlayer(context, username, geyserId); + assertNextPacketType(context, AddPlayerPacket.class); + return player; + } + + public static PlayerEntity spawnPlayer(GeyserMockContext context, String username, long geyserId) { + var playerEntity = spy(new PlayerEntity(context.session(), (int) geyserId, geyserId, UUID.randomUUID(), Vector3f.ZERO, Vector3f.ZERO, 0, 0, 0, username, null)); + + var entityCache = context.mockOrSpy(EntityCache.class); + entityCache.addPlayerEntity(playerEntity); + // called when the player spawns + entityCache.spawnEntity(playerEntity); + + return playerEntity; + } +} diff --git a/gradle.properties b/gradle.properties index 1ee0e12cc..09943725b 100644 --- a/gradle.properties +++ b/gradle.properties @@ -6,7 +6,10 @@ org.gradle.parallel=true org.gradle.caching=true org.gradle.vfs.watch=false +# TODO remove once architectury loom updates to 1.8 +loom.ignoreDependencyLoomVersionValidation=true + group=org.geysermc id=geyser -version=2.4.3-SNAPSHOT +version=2.5.0-SNAPSHOT description=Allows for players from Minecraft: Bedrock Edition to join Minecraft: Java Edition servers. diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index b9b4ff616..b9761d500 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -13,12 +13,12 @@ guava = "29.0-jre" gson = "2.3.1" # Provided by Spigot 1.8.8 TODO bump to 2.8.1 or similar (Spigot 1.16.5 version) after Merge gson-runtime = "2.10.1" websocket = "1.5.1" -protocol-connection = "3.0.0.Beta5-20240916.181041-6" -protocol-common = "3.0.0.Beta5-20240916.181041-6" -protocol-codec = "3.0.0.Beta5-20240916.181041-6" +protocol-connection = "3.0.0.Beta5-20241022.154658-14" +protocol-common = "3.0.0.Beta5-20241022.154658-14" +protocol-codec = "3.0.0.Beta5-20241022.154658-14" raknet = "1.0.0.CR3-20240416.144209-1" minecraftauth = "4.1.1" -mcprotocollib = "1.21-20240725.013034-16" +mcprotocollib = "1.21.2-20241107.110329-3" adventure = "4.14.0" adventure-platform = "4.3.0" junit = "5.9.2" @@ -28,7 +28,7 @@ jline = "3.21.0" terminalconsoleappender = "1.2.0" folia = "1.19.4-R0.1-SNAPSHOT" viaversion = "4.9.2" -adapters = "1.13-SNAPSHOT" +adapters = "1.15-SNAPSHOT" cloud = "2.0.0-rc.2" cloud-minecraft = "2.0.0-beta.9" cloud-minecraft-modded = "2.0.0-beta.7" @@ -36,12 +36,13 @@ commodore = "2.2" bungeecord = "a7c6ede" velocity = "3.3.0-SNAPSHOT" viaproxy = "3.3.2-SNAPSHOT" -fabric-loader = "0.15.11" -fabric-api = "0.100.1+1.21" -neoforge-minecraft = "21.1.1" +fabric-loader = "0.16.7" +fabric-api = "0.106.1+1.21.3" +neoforge-minecraft = "21.3.0-beta" mixin = "0.8.5" mixinextras = "0.3.5" -minecraft = "1.21.1" +minecraft = "1.21.3" +mockito = "5.+" # plugin versions indra = "3.1.3" @@ -109,7 +110,7 @@ mixinextras = { module = "io.github.llamalad7:mixinextras-common", version.ref = minecraft = { group = "com.mojang", name = "minecraft", version.ref = "minecraft" } -# Check these on https://modmuss50.me/fabric.html +# Check these on https://fabricmc.net/develop/ fabric-loader = { group = "net.fabricmc", name = "fabric-loader", version.ref = "fabric-loader" } fabric-api = { group = "net.fabricmc.fabric-api", name = "fabric-api", version.ref = "fabric-api" } @@ -142,6 +143,8 @@ math = { group = "org.cloudburstmc.math", name = "immutable", version = "2.0" } bstats = { group = "org.bstats", name = "bstats-base", version.ref = "bstats"} +mockito = { module = "org.mockito:mockito-core", version.ref = "mockito" } + # plugins lombok = { group = "io.freefair.gradle", name = "lombok-plugin", version.ref = "lombok" } indra = { group = "net.kyori", name = "indra-common", version.ref = "indra" }