diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml index b4b93dcac..19b655c29 100644 --- a/.github/FUNDING.yml +++ b/.github/FUNDING.yml @@ -1,7 +1,7 @@ # These are supported funding model platforms github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2] -patreon: GeyserMC +patreon: #GeyserMC # Disabled currently open_collective: # Replace with a single Open Collective username ko_fi: # Replace with a single Ko-fi username tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel diff --git a/Jenkinsfile b/Jenkinsfile index 1a93391db..7dfdaf304 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -24,6 +24,7 @@ pipeline { when { branch "master" } + steps { sh 'mvn javadoc:jar source:jar deploy -DskipTests' } diff --git a/README.md b/README.md index f7201bb08..ab1ff24ac 100644 --- a/README.md +++ b/README.md @@ -18,7 +18,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 now joined us here! -### Currently supporting Minecraft Bedrock v1.16.x and Minecraft Java v1.16.3. +### Currently supporting Minecraft Bedrock v1.16.100 and Minecraft Java v1.16.4. ## Setting Up Take a look [here](https://github.com/GeyserMC/Geyser/wiki#Setup) for how to set up Geyser. diff --git a/bootstrap/bungeecord/pom.xml b/bootstrap/bungeecord/pom.xml index 44b28e931..124967b0a 100644 --- a/bootstrap/bungeecord/pom.xml +++ b/bootstrap/bungeecord/pom.xml @@ -6,15 +6,15 @@ org.geysermc bootstrap-parent - 1.1.0 - ../ + 1.2.0-SNAPSHOT bootstrap-bungeecord + org.geysermc connector - 1.1.0 + 1.2.0-SNAPSHOT compile diff --git a/bootstrap/bungeecord/src/main/java/org/geysermc/platform/bungeecord/GeyserBungeePlugin.java b/bootstrap/bungeecord/src/main/java/org/geysermc/platform/bungeecord/GeyserBungeePlugin.java index e06640896..61bec3b37 100644 --- a/bootstrap/bungeecord/src/main/java/org/geysermc/platform/bungeecord/GeyserBungeePlugin.java +++ b/bootstrap/bungeecord/src/main/java/org/geysermc/platform/bungeecord/GeyserBungeePlugin.java @@ -27,10 +27,10 @@ package org.geysermc.platform.bungeecord; import net.md_5.bungee.api.config.ListenerInfo; import net.md_5.bungee.api.plugin.Plugin; +import org.geysermc.common.PlatformType; import org.geysermc.connector.GeyserConnector; import org.geysermc.connector.bootstrap.GeyserBootstrap; import org.geysermc.connector.command.CommandManager; -import org.geysermc.connector.common.PlatformType; import org.geysermc.connector.configuration.GeyserConfiguration; import org.geysermc.connector.dump.BootstrapDumpInfo; import org.geysermc.connector.ping.GeyserLegacyPingPassthrough; diff --git a/bootstrap/pom.xml b/bootstrap/pom.xml index d9bac67d1..a5ad53cb5 100644 --- a/bootstrap/pom.xml +++ b/bootstrap/pom.xml @@ -6,12 +6,11 @@ org.geysermc geyser-parent - parent - ../ + 1.2.0-SNAPSHOT bootstrap-parent - 1.1.0 pom + spigot-public diff --git a/bootstrap/spigot/pom.xml b/bootstrap/spigot/pom.xml index d4dc33260..f3b9cf88d 100644 --- a/bootstrap/spigot/pom.xml +++ b/bootstrap/spigot/pom.xml @@ -6,15 +6,15 @@ org.geysermc bootstrap-parent - 1.1.0 - ../ + 1.2.0-SNAPSHOT bootstrap-spigot + org.geysermc connector - 1.1.0 + 1.2.0-SNAPSHOT compile @@ -26,7 +26,7 @@ us.myles viaversion - 3.1.1 + 3.2.0 provided diff --git a/bootstrap/spigot/src/main/java/org/geysermc/platform/spigot/GeyserSpigotPlugin.java b/bootstrap/spigot/src/main/java/org/geysermc/platform/spigot/GeyserSpigotPlugin.java index 7aeb6edc5..9d90e68c7 100644 --- a/bootstrap/spigot/src/main/java/org/geysermc/platform/spigot/GeyserSpigotPlugin.java +++ b/bootstrap/spigot/src/main/java/org/geysermc/platform/spigot/GeyserSpigotPlugin.java @@ -27,10 +27,10 @@ package org.geysermc.platform.spigot; import org.bukkit.Bukkit; import org.bukkit.plugin.java.JavaPlugin; +import org.geysermc.common.PlatformType; import org.geysermc.connector.GeyserConnector; import org.geysermc.connector.bootstrap.GeyserBootstrap; import org.geysermc.connector.command.CommandManager; -import org.geysermc.connector.common.PlatformType; import org.geysermc.connector.configuration.GeyserConfiguration; import org.geysermc.connector.dump.BootstrapDumpInfo; import org.geysermc.connector.network.translators.world.WorldManager; @@ -43,6 +43,7 @@ import org.geysermc.platform.spigot.command.GeyserSpigotCommandManager; import org.geysermc.platform.spigot.command.SpigotCommandSender; import org.geysermc.platform.spigot.world.GeyserSpigotBlockPlaceListener; import org.geysermc.platform.spigot.world.GeyserSpigotWorldManager; +import us.myles.ViaVersion.api.Via; import java.io.File; import java.io.IOException; @@ -121,6 +122,13 @@ public class GeyserSpigotPlugin extends JavaPlugin implements GeyserBootstrap { this.geyserCommandManager = new GeyserSpigotCommandManager(this, connector); boolean isViaVersion = (Bukkit.getPluginManager().getPlugin("ViaVersion") != null); + if (isViaVersion) { + if (!isCompatible(Via.getAPI().getVersion().replace("-SNAPSHOT", ""), "3.2.0")) { + geyserLogger.warning(LanguageUtils.getLocaleStringLog("geyser.bootstrap.viaversion.too_old", + "https://ci.viaversion.com/job/ViaVersion/")); + isViaVersion = false; + } + } // Used to determine if Block.getBlockData() is present. boolean isLegacy = !isCompatible(Bukkit.getServer().getVersion(), "1.13.0"); if (isLegacy) diff --git a/bootstrap/spigot/src/main/java/org/geysermc/platform/spigot/world/GeyserSpigotWorldManager.java b/bootstrap/spigot/src/main/java/org/geysermc/platform/spigot/world/GeyserSpigotWorldManager.java index 8a92526f1..3493fc25a 100644 --- a/bootstrap/spigot/src/main/java/org/geysermc/platform/spigot/world/GeyserSpigotWorldManager.java +++ b/bootstrap/spigot/src/main/java/org/geysermc/platform/spigot/world/GeyserSpigotWorldManager.java @@ -26,6 +26,7 @@ package org.geysermc.platform.spigot.world; import com.fasterxml.jackson.databind.JsonNode; +import com.github.steveice10.mc.protocol.MinecraftConstants; import com.github.steveice10.mc.protocol.data.game.chunk.Chunk; import it.unimi.dsi.fastutil.ints.Int2IntMap; import it.unimi.dsi.fastutil.ints.Int2IntOpenHashMap; @@ -41,14 +42,32 @@ import org.geysermc.connector.network.translators.world.block.BlockTranslator; import org.geysermc.connector.utils.FileUtils; import org.geysermc.connector.utils.GameRule; import org.geysermc.connector.utils.LanguageUtils; -import us.myles.ViaVersion.protocols.protocol1_13_1to1_13.Protocol1_13_1To1_13; -import us.myles.ViaVersion.protocols.protocol1_16_2to1_16_1.data.MappingData; +import us.myles.ViaVersion.api.Pair; +import us.myles.ViaVersion.api.Via; +import us.myles.ViaVersion.api.data.MappingData; +import us.myles.ViaVersion.api.minecraft.Position; +import us.myles.ViaVersion.api.protocol.Protocol; +import us.myles.ViaVersion.api.protocol.ProtocolRegistry; +import us.myles.ViaVersion.api.protocol.ProtocolVersion; +import us.myles.ViaVersion.protocols.protocol1_13to1_12_2.Protocol1_13To1_12_2; +import us.myles.ViaVersion.protocols.protocol1_13to1_12_2.storage.BlockStorage; import java.io.InputStream; +import java.util.List; public class GeyserSpigotWorldManager extends GeyserWorldManager { + /** + * The current client protocol version for ViaVersion usage. + */ + private static final int CLIENT_PROTOCOL_VERSION = MinecraftConstants.PROTOCOL_VERSION; + /** + * Whether the server is pre-1.13. + */ private final boolean isLegacy; + /** + * Whether the server is pre-1.16 and therefore does not support 3D biomes on an API level guaranteed. + */ private final boolean use3dBiomes; /** * You need ViaVersion to connect to an older server with Geyser. @@ -83,8 +102,9 @@ public class GeyserSpigotWorldManager extends GeyserWorldManager { } // Only load in the biomes that are present in this version of Minecraft for (Biome enumBiome : Biome.values()) { - if (biomes.has(enumBiome.toString())) { - biomeToIdMap.put(enumBiome.ordinal(), biomes.get(enumBiome.toString()).intValue()); + JsonNode biome = biomes.get(enumBiome.toString()); + if (biome != null) { + biomeToIdMap.put(enumBiome.ordinal(), biome.intValue()); } else { GeyserConnector.getInstance().getLogger().debug("No biome mapping found for " + enumBiome.toString() + ", defaulting to 0"); @@ -99,7 +119,7 @@ public class GeyserSpigotWorldManager extends GeyserWorldManager { if ((this.isLegacy && !this.isViaVersion) || session.getPlayerEntity() == null || (bukkitPlayer = Bukkit.getPlayer(session.getPlayerEntity().getUsername())) == null) { - return BlockTranslator.AIR; + return BlockTranslator.JAVA_AIR_ID; } World world = bukkitPlayer.getWorld(); if (isLegacy) { @@ -111,28 +131,38 @@ public class GeyserSpigotWorldManager extends GeyserWorldManager { public static int getLegacyBlock(GeyserSession session, int x, int y, int z, boolean isViaVersion) { if (isViaVersion) { - return getLegacyBlock(Bukkit.getPlayer(session.getPlayerEntity().getUsername()).getWorld(), x, y, z, true); + Player bukkitPlayer = Bukkit.getPlayer(session.getPlayerEntity().getUsername()); + // Get block entity storage + BlockStorage storage = Via.getManager().getConnection(bukkitPlayer.getUniqueId()).get(BlockStorage.class); + return getLegacyBlock(storage, bukkitPlayer.getWorld(), x, y, z); } else { - return BlockTranslator.AIR; + return BlockTranslator.JAVA_AIR_ID; } } @SuppressWarnings("deprecation") - public static int getLegacyBlock(World world, int x, int y, int z, boolean isViaVersion) { - if (isViaVersion) { - Block block = world.getBlockAt(x, y, z); - // Black magic that gets the old block state ID - int oldBlockId = (block.getType().getId() << 4) | (block.getData() & 0xF); - // Convert block state from old version -> 1.13 -> 1.13.1 -> 1.14 -> 1.15 -> 1.16 -> 1.16.2 - int thirteenBlockId = us.myles.ViaVersion.protocols.protocol1_13to1_12_2.data.MappingData.blockMappings.getNewId(oldBlockId); - int thirteenPointOneBlockId = Protocol1_13_1To1_13.getNewBlockStateId(thirteenBlockId); - int fourteenBlockId = us.myles.ViaVersion.protocols.protocol1_14to1_13_2.data.MappingData.blockStateMappings.getNewId(thirteenPointOneBlockId); - int fifteenBlockId = us.myles.ViaVersion.protocols.protocol1_15to1_14_4.data.MappingData.blockStateMappings.getNewId(fourteenBlockId); - int sixteenBlockId = us.myles.ViaVersion.protocols.protocol1_16to1_15_2.data.MappingData.blockStateMappings.getNewId(fifteenBlockId); - return MappingData.blockStateMappings.getNewId(sixteenBlockId); - } else { - return BlockTranslator.AIR; + public static int getLegacyBlock(BlockStorage storage, World world, int x, int y, int z) { + Block block = world.getBlockAt(x, y, z); + // Black magic that gets the old block state ID + int blockId = (block.getType().getId() << 4) | (block.getData() & 0xF); + // Convert block state from old version (1.12.2) -> 1.13 -> 1.13.1 -> 1.14 -> 1.15 -> 1.16 -> 1.16.2 + blockId = ProtocolRegistry.getProtocol(Protocol1_13To1_12_2.class).getMappingData().getNewBlockId(blockId); + List> protocolList = ProtocolRegistry.getProtocolPath(CLIENT_PROTOCOL_VERSION, + ProtocolVersion.v1_13.getId()); + // Translate block entity differences - some information was stored in block tags and not block states + if (storage.isWelcome(blockId)) { // No getOrDefault method + BlockStorage.ReplacementData data = storage.get(new Position(x, (short) y, z)); + if (data != null && data.getReplacement() != -1) { + blockId = data.getReplacement(); + } } + for (int i = protocolList.size() - 1; i >= 0; i--) { + MappingData mappingData = protocolList.get(i).getValue().getMappingData(); + if (mappingData != null) { + blockId = mappingData.getNewBlockStateId(blockId); + } + } + return blockId; } @Override @@ -144,11 +174,13 @@ public class GeyserSpigotWorldManager extends GeyserWorldManager { return; } World world = bukkitPlayer.getWorld(); - if (this.isLegacy) { + if (this.isLegacy) { + // Get block entity storage + BlockStorage storage = Via.getManager().getConnection(bukkitPlayer.getUniqueId()).get(BlockStorage.class); for (int blockY = 0; blockY < 16; blockY++) { // Cache-friendly iteration order for (int blockZ = 0; blockZ < 16; blockZ++) { for (int blockX = 0; blockX < 16; blockX++) { - chunk.set(blockX, blockY, blockZ, getLegacyBlock(world, (x << 4) + blockX, (y << 4) + blockY, (z << 4) + blockZ, true)); + chunk.set(blockX, blockY, blockZ, getLegacyBlock(storage, world, (x << 4) + blockX, (y << 4) + blockY, (z << 4) + blockZ)); } } } @@ -158,7 +190,7 @@ public class GeyserSpigotWorldManager extends GeyserWorldManager { for (int blockZ = 0; blockZ < 16; blockZ++) { for (int blockX = 0; blockX < 16; blockX++) { Block block = world.getBlockAt((x << 4) + blockX, (y << 4) + blockY, (z << 4) + blockZ); - int id = BlockTranslator.getJavaIdBlockMap().getOrDefault(block.getBlockData().getAsString(), 0); + int id = BlockTranslator.getJavaIdBlockMap().getOrDefault(block.getBlockData().getAsString(), BlockTranslator.JAVA_AIR_ID); chunk.set(blockX, blockY, blockZ, id); } } diff --git a/bootstrap/sponge/pom.xml b/bootstrap/sponge/pom.xml index 132f38173..e6ce8f851 100644 --- a/bootstrap/sponge/pom.xml +++ b/bootstrap/sponge/pom.xml @@ -6,15 +6,15 @@ org.geysermc bootstrap-parent - 1.1.0 - ../ + 1.2.0-SNAPSHOT bootstrap-sponge + org.geysermc connector - 1.1.0 + 1.2.0-SNAPSHOT compile diff --git a/bootstrap/sponge/src/main/java/org/geysermc/platform/sponge/GeyserSpongePlugin.java b/bootstrap/sponge/src/main/java/org/geysermc/platform/sponge/GeyserSpongePlugin.java index 106d2b155..c3231f3bf 100644 --- a/bootstrap/sponge/src/main/java/org/geysermc/platform/sponge/GeyserSpongePlugin.java +++ b/bootstrap/sponge/src/main/java/org/geysermc/platform/sponge/GeyserSpongePlugin.java @@ -26,10 +26,10 @@ package org.geysermc.platform.sponge; import com.google.inject.Inject; +import org.geysermc.common.PlatformType; import org.geysermc.connector.GeyserConnector; import org.geysermc.connector.bootstrap.GeyserBootstrap; import org.geysermc.connector.command.CommandManager; -import org.geysermc.connector.common.PlatformType; import org.geysermc.connector.configuration.GeyserConfiguration; import org.geysermc.connector.dump.BootstrapDumpInfo; import org.geysermc.connector.ping.GeyserLegacyPingPassthrough; diff --git a/bootstrap/standalone/pom.xml b/bootstrap/standalone/pom.xml index 07458f730..831239f66 100644 --- a/bootstrap/standalone/pom.xml +++ b/bootstrap/standalone/pom.xml @@ -6,15 +6,15 @@ org.geysermc bootstrap-parent - 1.1.0 - ../ + 1.2.0-SNAPSHOT bootstrap-standalone + org.geysermc connector - 1.1.0 + 1.2.0-SNAPSHOT compile diff --git a/bootstrap/standalone/src/main/java/org/geysermc/platform/standalone/GeyserStandaloneBootstrap.java b/bootstrap/standalone/src/main/java/org/geysermc/platform/standalone/GeyserStandaloneBootstrap.java index 123a9a600..a0a8a3aea 100644 --- a/bootstrap/standalone/src/main/java/org/geysermc/platform/standalone/GeyserStandaloneBootstrap.java +++ b/bootstrap/standalone/src/main/java/org/geysermc/platform/standalone/GeyserStandaloneBootstrap.java @@ -25,17 +25,23 @@ package org.geysermc.platform.standalone; +import com.fasterxml.jackson.databind.BeanDescription; +import com.fasterxml.jackson.databind.JavaType; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.introspect.AnnotatedField; +import com.fasterxml.jackson.databind.introspect.BeanPropertyDefinition; import lombok.Getter; import net.minecrell.terminalconsole.TerminalConsoleAppender; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.core.Appender; import org.apache.logging.log4j.core.Logger; import org.apache.logging.log4j.core.appender.ConsoleAppender; +import org.geysermc.common.PlatformType; import org.geysermc.connector.GeyserConnector; import org.geysermc.connector.bootstrap.GeyserBootstrap; import org.geysermc.connector.command.CommandManager; -import org.geysermc.connector.common.PlatformType; import org.geysermc.connector.configuration.GeyserConfiguration; +import org.geysermc.connector.configuration.GeyserJacksonConfiguration; import org.geysermc.connector.dump.BootstrapDumpInfo; import org.geysermc.connector.ping.GeyserLegacyPingPassthrough; import org.geysermc.connector.ping.IGeyserPingPassthrough; @@ -50,7 +56,8 @@ import java.lang.reflect.Method; import java.nio.file.Path; import java.nio.file.Paths; import java.text.MessageFormat; -import java.util.UUID; +import java.util.*; +import java.util.stream.Collectors; public class GeyserStandaloneBootstrap implements GeyserBootstrap { @@ -67,6 +74,9 @@ public class GeyserStandaloneBootstrap implements GeyserBootstrap { private GeyserConnector connector; + private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper(); + + private static final Map argsConfigKeys = new HashMap<>(); public static void main(String[] args) { GeyserStandaloneBootstrap bootstrap = new GeyserStandaloneBootstrap(); @@ -74,6 +84,8 @@ public class GeyserStandaloneBootstrap implements GeyserBootstrap { boolean useGuiOpts = bootstrap.useGui; String configFilenameOpt = bootstrap.configFilename; + List availableProperties = getPOJOForClass(GeyserJacksonConfiguration.class); + for (int i = 0; i < args.length; i++) { // By default, standalone Geyser will check if it should open the GUI based on if the GUI is null // Optionally, you can force the use of a GUI or no GUI by specifying args @@ -91,11 +103,11 @@ public class GeyserStandaloneBootstrap implements GeyserBootstrap { case "--config": case "-c": if (i >= args.length - 1) { - System.err.println(MessageFormat.format(LanguageUtils.getLocaleStringLog("geyser.bootstrap.args.confignotspecified"), "-c")); + System.err.println(MessageFormat.format(LanguageUtils.getLocaleStringLog("geyser.bootstrap.args.config_not_specified"), "-c")); return; } configFilenameOpt = args[i+1]; i++; - System.out.println(MessageFormat.format(LanguageUtils.getLocaleStringLog("geyser.bootstrap.args.configspecified"), configFilenameOpt)); + System.out.println(MessageFormat.format(LanguageUtils.getLocaleStringLog("geyser.bootstrap.args.config_specified"), configFilenameOpt)); break; case "--help": case "-h": @@ -106,8 +118,43 @@ public class GeyserStandaloneBootstrap implements GeyserBootstrap { System.out.println(" --gui, --nogui " + LanguageUtils.getLocaleStringLog("geyser.bootstrap.args.gui")); return; default: - String badArgMsg = LanguageUtils.getLocaleStringLog("geyser.bootstrap.args.unrecognised"); - System.err.println(MessageFormat.format(badArgMsg, arg)); + // We have likely added a config option argument + if (arg.startsWith("--")) { + // Split the argument by an = + String[] argParts = arg.substring(2).split("="); + if (argParts.length == 2) { + // Split the config key by . to allow for nested options + String[] configKeyParts = argParts[0].split("\\."); + + // Loop the possible config options to check the passed key is valid + boolean found = false; + for (BeanPropertyDefinition property : availableProperties) { + if (configKeyParts[0].equals(property.getName())) { + if (configKeyParts.length > 1) { + // Loop sub-section options to check the passed key is valid + for (BeanPropertyDefinition subProperty : getPOJOForClass(property.getRawPrimaryType())) { + if (configKeyParts[1].equals(subProperty.getName())) { + found = true; + break; + } + } + } else { + found = true; + } + + break; + } + } + + // Add the found key to the stored list for later usage + if (found) { + argsConfigKeys.put(argParts[0], argParts[1]); + break; + } + } + } + + System.err.println(LanguageUtils.getLocaleStringLog("geyser.bootstrap.args.unrecognised", arg)); return; } } @@ -148,6 +195,9 @@ public class GeyserStandaloneBootstrap implements GeyserBootstrap { try { File configFile = FileUtils.fileOrCopiedFromResource(new File(configFilename), "config.yml", (x) -> x.replaceAll("generateduuid", UUID.randomUUID().toString())); geyserConfig = FileUtils.loadConfig(configFile, GeyserStandaloneConfiguration.class); + + handleArgsConfigOptions(); + if (this.geyserConfig.getRemote().getAddress().equalsIgnoreCase("auto")) { geyserConfig.setAutoconfiguredRemote(true); // Doesn't really need to be set but /shrug geyserConfig.getRemote().setAddress("127.0.0.1"); @@ -223,4 +273,99 @@ public class GeyserStandaloneBootstrap implements GeyserBootstrap { public BootstrapDumpInfo getDumpInfo() { return new GeyserStandaloneDumpInfo(this); } + + /** + * Get the {@link BeanPropertyDefinition}s for the given class + * + * @param clazz The class to get the definitions for + * @return A list of {@link BeanPropertyDefinition} for the given class + */ + public static List getPOJOForClass(Class clazz) { + JavaType javaType = OBJECT_MAPPER.getTypeFactory().constructType(clazz); + + // Introspect the given type + BeanDescription beanDescription = OBJECT_MAPPER.getSerializationConfig().introspect(javaType); + + // Find properties + List properties = beanDescription.findProperties(); + + // Get the ignored properties + Set ignoredProperties = OBJECT_MAPPER.getSerializationConfig().getAnnotationIntrospector() + .findPropertyIgnorals(beanDescription.getClassInfo()).getIgnored(); + + // Filter properties removing the ignored ones + return properties.stream() + .filter(property -> !ignoredProperties.contains(property.getName())) + .collect(Collectors.toList()); + } + + /** + * Set a POJO property value on an object + * + * @param property The {@link BeanPropertyDefinition} to set + * @param parentObject The object to alter + * @param value The new value of the property + */ + private static void setConfigOption(BeanPropertyDefinition property, Object parentObject, Object value) { + Object parsedValue = value; + + // Change the values type if needed + if (int.class.equals(property.getRawPrimaryType())) { + parsedValue = Integer.valueOf((String) parsedValue); + } else if (boolean.class.equals(property.getRawPrimaryType())) { + parsedValue = Boolean.valueOf((String) parsedValue); + } + + // Force the value to be set + AnnotatedField field = property.getField(); + field.fixAccess(true); + field.setValue(parentObject, parsedValue); + } + + /** + * Update the loaded {@link GeyserStandaloneConfiguration} with any values passed in the command line arguments + */ + private void handleArgsConfigOptions() { + // Get the available properties from the class + List availableProperties = getPOJOForClass(GeyserJacksonConfiguration.class); + + for (Map.Entry configKey : argsConfigKeys.entrySet()) { + String[] configKeyParts = configKey.getKey().split("\\."); + + // Loop over the properties looking for any matches against the stored one from the argument + for (BeanPropertyDefinition property : availableProperties) { + if (configKeyParts[0].equals(property.getName())) { + if (configKeyParts.length > 1) { + // Loop through the sub property if the first part matches + for (BeanPropertyDefinition subProperty : getPOJOForClass(property.getRawPrimaryType())) { + if (configKeyParts[1].equals(subProperty.getName())) { + geyserLogger.info(LanguageUtils.getLocaleStringLog("geyser.bootstrap.args.set_config_option", configKey.getKey(), configKey.getValue())); + + // Set the sub property value on the config + try { + Object subConfig = property.getGetter().callOn(geyserConfig); + setConfigOption(subProperty, subConfig, configKey.getValue()); + } catch (Exception e) { + geyserLogger.error("Failed to set config option: " + property.getFullName()); + } + + break; + } + } + } else { + geyserLogger.info(LanguageUtils.getLocaleStringLog("geyser.bootstrap.args.set_config_option", configKey.getKey(), configKey.getValue())); + + // Set the property value on the config + try { + setConfigOption(property, geyserConfig, configKey.getValue()); + } catch (Exception e) { + geyserLogger.error("Failed to set config option: " + property.getFullName()); + } + } + + break; + } + } + } + } } diff --git a/bootstrap/velocity/pom.xml b/bootstrap/velocity/pom.xml index ee445b6e7..86de99ba6 100644 --- a/bootstrap/velocity/pom.xml +++ b/bootstrap/velocity/pom.xml @@ -6,21 +6,21 @@ org.geysermc bootstrap-parent - 1.1.0 - ../ + 1.2.0-SNAPSHOT bootstrap-velocity + org.geysermc connector - 1.1.0 + 1.2.0-SNAPSHOT compile com.velocitypowered velocity-api - 1.0.0-SNAPSHOT + 1.1.0 provided diff --git a/bootstrap/velocity/src/main/java/org/geysermc/platform/velocity/GeyserVelocityPlugin.java b/bootstrap/velocity/src/main/java/org/geysermc/platform/velocity/GeyserVelocityPlugin.java index 291d7a001..b5255e623 100644 --- a/bootstrap/velocity/src/main/java/org/geysermc/platform/velocity/GeyserVelocityPlugin.java +++ b/bootstrap/velocity/src/main/java/org/geysermc/platform/velocity/GeyserVelocityPlugin.java @@ -33,9 +33,9 @@ import com.velocitypowered.api.event.proxy.ProxyShutdownEvent; import com.velocitypowered.api.plugin.Plugin; import com.velocitypowered.api.proxy.ProxyServer; import lombok.Getter; +import org.geysermc.common.PlatformType; import org.geysermc.connector.GeyserConnector; import org.geysermc.connector.bootstrap.GeyserBootstrap; -import org.geysermc.connector.common.PlatformType; import org.geysermc.connector.configuration.GeyserConfiguration; import org.geysermc.connector.dump.BootstrapDumpInfo; import org.geysermc.connector.ping.GeyserLegacyPingPassthrough; @@ -121,7 +121,7 @@ public class GeyserVelocityPlugin implements GeyserBootstrap { this.connector = GeyserConnector.start(PlatformType.VELOCITY, this); this.geyserCommandManager = new GeyserVelocityCommandManager(connector); - this.commandManager.register(new GeyserVelocityCommandExecutor(connector), "geyser"); + this.commandManager.register("geyser", new GeyserVelocityCommandExecutor(connector)); if (geyserConfig.isLegacyPingPassthrough()) { this.geyserPingPassthrough = GeyserLegacyPingPassthrough.init(connector); } else { diff --git a/common/pom.xml b/common/pom.xml index 9223331be..af5664ca1 100644 --- a/common/pom.xml +++ b/common/pom.xml @@ -6,11 +6,10 @@ org.geysermc geyser-parent - parent - ../ + 1.2.0-SNAPSHOT common - 1.1.0 + com.google.code.gson diff --git a/connector/src/main/java/org/geysermc/connector/common/PlatformType.java b/common/src/main/java/org/geysermc/common/PlatformType.java similarity index 97% rename from connector/src/main/java/org/geysermc/connector/common/PlatformType.java rename to common/src/main/java/org/geysermc/common/PlatformType.java index 3e945d3ad..883490208 100644 --- a/connector/src/main/java/org/geysermc/connector/common/PlatformType.java +++ b/common/src/main/java/org/geysermc/common/PlatformType.java @@ -23,7 +23,7 @@ * @link https://github.com/GeyserMC/Geyser */ -package org.geysermc.connector.common; +package org.geysermc.common; import lombok.AllArgsConstructor; import lombok.Getter; diff --git a/connector/pom.xml b/connector/pom.xml index 197319875..7c44ddfd2 100644 --- a/connector/pom.xml +++ b/connector/pom.xml @@ -6,16 +6,15 @@ org.geysermc geyser-parent - parent - ../ + 1.2.0-SNAPSHOT connector - 1.1.0 + org.geysermc common - 1.1.0 + 1.2.0-SNAPSHOT compile @@ -32,8 +31,8 @@ com.github.CloudburstMC.Protocol - bedrock-v408 - 02f46a8700 + bedrock-v419 + ce59d39118 compile @@ -111,7 +110,7 @@ com.github.steveice10 mcprotocollib - 1b01b1ffef + 86e1901be5 compile @@ -143,17 +142,23 @@ compile - net.kyori + com.github.kyoripowered.adventure adventure-text-serializer-gson - 4.1.1 + 4d8a67d798 compile - net.kyori + com.github.kyoripowered.adventure adventure-text-serializer-legacy - 4.1.1 + 0599048 compile + + junit + junit + 4.13.1 + test + @@ -283,6 +288,15 @@ + + org.apache.maven.plugins + maven-surefire-plugin + 2.22.0 + + + -Dfile.encoding=${project.build.sourceEncoding} + + diff --git a/connector/src/main/java/org/geysermc/connector/GeyserConnector.java b/connector/src/main/java/org/geysermc/connector/GeyserConnector.java index c44f8f434..fa5f1ec74 100644 --- a/connector/src/main/java/org/geysermc/connector/GeyserConnector.java +++ b/connector/src/main/java/org/geysermc/connector/GeyserConnector.java @@ -32,10 +32,10 @@ import com.nukkitx.network.raknet.RakNetConstants; import com.nukkitx.protocol.bedrock.BedrockServer; import lombok.Getter; import lombok.Setter; +import org.geysermc.common.PlatformType; import org.geysermc.connector.bootstrap.GeyserBootstrap; import org.geysermc.connector.command.CommandManager; import org.geysermc.connector.common.AuthType; -import org.geysermc.connector.common.PlatformType; import org.geysermc.connector.configuration.GeyserConfiguration; import org.geysermc.connector.metrics.Metrics; import org.geysermc.connector.network.ConnectorServerEventHandler; @@ -197,8 +197,7 @@ public class GeyserConnector { } } - if (config.isAboveBedrockNetherBuilding()) - DimensionUtils.changeBedrockNetherId(); // Apply End dimension ID workaround to Nether + DimensionUtils.changeBedrockNetherId(config.isAboveBedrockNetherBuilding()); // Apply End dimension ID workaround to Nether // https://github.com/GeyserMC/Geyser/issues/957 RakNetConstants.MAXIMUM_MTU_SIZE = (short) config.getMtu(); diff --git a/connector/src/main/java/org/geysermc/connector/command/defaults/ReloadCommand.java b/connector/src/main/java/org/geysermc/connector/command/defaults/ReloadCommand.java index 6b2be2944..64d0017ec 100644 --- a/connector/src/main/java/org/geysermc/connector/command/defaults/ReloadCommand.java +++ b/connector/src/main/java/org/geysermc/connector/command/defaults/ReloadCommand.java @@ -25,7 +25,7 @@ package org.geysermc.connector.command.defaults; -import org.geysermc.connector.common.PlatformType; +import org.geysermc.common.PlatformType; import org.geysermc.connector.GeyserConnector; import org.geysermc.connector.command.CommandSender; import org.geysermc.connector.command.GeyserCommand; diff --git a/connector/src/main/java/org/geysermc/connector/command/defaults/StopCommand.java b/connector/src/main/java/org/geysermc/connector/command/defaults/StopCommand.java index c69a8705f..5be6253eb 100644 --- a/connector/src/main/java/org/geysermc/connector/command/defaults/StopCommand.java +++ b/connector/src/main/java/org/geysermc/connector/command/defaults/StopCommand.java @@ -25,7 +25,7 @@ package org.geysermc.connector.command.defaults; -import org.geysermc.connector.common.PlatformType; +import org.geysermc.common.PlatformType; import org.geysermc.connector.GeyserConnector; import org.geysermc.connector.command.CommandSender; import org.geysermc.connector.command.GeyserCommand; diff --git a/connector/src/main/java/org/geysermc/connector/configuration/GeyserConfiguration.java b/connector/src/main/java/org/geysermc/connector/configuration/GeyserConfiguration.java index c1cc4d036..153a91746 100644 --- a/connector/src/main/java/org/geysermc/connector/configuration/GeyserConfiguration.java +++ b/connector/src/main/java/org/geysermc/connector/configuration/GeyserConfiguration.java @@ -81,6 +81,8 @@ public interface GeyserConfiguration { boolean isForceResourcePacks(); + boolean isXboxAchievementsEnabled(); + int getCacheImages(); IMetricsInfo getMetrics(); diff --git a/connector/src/main/java/org/geysermc/connector/configuration/GeyserJacksonConfiguration.java b/connector/src/main/java/org/geysermc/connector/configuration/GeyserJacksonConfiguration.java index 45676fbd8..d19cfe49f 100644 --- a/connector/src/main/java/org/geysermc/connector/configuration/GeyserJacksonConfiguration.java +++ b/connector/src/main/java/org/geysermc/connector/configuration/GeyserJacksonConfiguration.java @@ -107,6 +107,9 @@ public abstract class GeyserJacksonConfiguration implements GeyserConfiguration @JsonProperty("force-resource-packs") private boolean forceResourcePacks = true; + @JsonProperty("xbox-achievements-enabled") + private boolean xboxAchievementsEnabled = false; + private MetricsInfo metrics = new MetricsInfo(); @Getter diff --git a/connector/src/main/java/org/geysermc/connector/dump/BootstrapDumpInfo.java b/connector/src/main/java/org/geysermc/connector/dump/BootstrapDumpInfo.java index 9fbe82cc8..585565533 100644 --- a/connector/src/main/java/org/geysermc/connector/dump/BootstrapDumpInfo.java +++ b/connector/src/main/java/org/geysermc/connector/dump/BootstrapDumpInfo.java @@ -27,7 +27,7 @@ package org.geysermc.connector.dump; import lombok.AllArgsConstructor; import lombok.Getter; -import org.geysermc.connector.common.PlatformType; +import org.geysermc.common.PlatformType; import org.geysermc.connector.GeyserConnector; import java.util.List; diff --git a/connector/src/main/java/org/geysermc/connector/entity/CommandBlockMinecartEntity.java b/connector/src/main/java/org/geysermc/connector/entity/CommandBlockMinecartEntity.java index 8cabba645..7d34cc795 100644 --- a/connector/src/main/java/org/geysermc/connector/entity/CommandBlockMinecartEntity.java +++ b/connector/src/main/java/org/geysermc/connector/entity/CommandBlockMinecartEntity.java @@ -26,13 +26,12 @@ package org.geysermc.connector.entity; import com.github.steveice10.mc.protocol.data.game.entity.metadata.EntityMetadata; -import com.github.steveice10.mc.protocol.data.message.Message; import com.nukkitx.math.vector.Vector3f; import com.nukkitx.protocol.bedrock.data.entity.EntityData; import org.geysermc.connector.entity.type.EntityType; import org.geysermc.connector.network.session.GeyserSession; import org.geysermc.connector.network.translators.world.block.BlockTranslator; -import org.geysermc.connector.utils.MessageUtils; +import org.geysermc.connector.network.translators.chat.MessageTranslator; public class CommandBlockMinecartEntity extends DefaultBlockMinecartEntity { @@ -51,7 +50,7 @@ public class CommandBlockMinecartEntity extends DefaultBlockMinecartEntity { metadata.put(EntityData.COMMAND_BLOCK_COMMAND, entityMetadata.getValue()); } if (entityMetadata.getId() == 14) { - metadata.put(EntityData.COMMAND_BLOCK_LAST_OUTPUT, MessageUtils.getBedrockMessage((Message) entityMetadata.getValue())); + metadata.put(EntityData.COMMAND_BLOCK_LAST_OUTPUT, MessageTranslator.convertMessage(entityMetadata.getValue().toString())); } super.updateBedrockMetadata(entityMetadata, session); } diff --git a/connector/src/main/java/org/geysermc/connector/entity/Entity.java b/connector/src/main/java/org/geysermc/connector/entity/Entity.java index 2dfb0c04f..7b1fa1cf0 100644 --- a/connector/src/main/java/org/geysermc/connector/entity/Entity.java +++ b/connector/src/main/java/org/geysermc/connector/entity/Entity.java @@ -54,7 +54,7 @@ import org.geysermc.connector.network.session.GeyserSession; import org.geysermc.connector.network.translators.item.ItemRegistry; import org.geysermc.connector.utils.AttributeUtils; import org.geysermc.connector.utils.ChunkUtils; -import org.geysermc.connector.utils.MessageUtils; +import org.geysermc.connector.network.translators.chat.MessageTranslator; import java.util.ArrayList; import java.util.HashMap; @@ -67,8 +67,6 @@ public class Entity { protected long entityId; protected long geyserId; - protected String dimension; - protected Vector3f position; protected Vector3f motion; @@ -100,7 +98,6 @@ public class Entity { this.rotation = rotation; this.valid = false; - this.dimension = "minecraft:overworld"; setPosition(position); @@ -321,7 +318,7 @@ public class Entity { Message message = (Message) entityMetadata.getValue(); if (message != null) // Always translate even if it's a TextMessage since there could be translatable parameters - metadata.put(EntityData.NAMETAG, MessageUtils.getTranslatedBedrockMessage(message, session.getLocale(), true)); + metadata.put(EntityData.NAMETAG, MessageTranslator.convertMessage(message.toString(), session.getLocale())); } break; case 3: // is custom name visible @@ -339,18 +336,6 @@ public class Entity { metadata.getFlags().setFlag(EntityFlag.SLEEPING, true); // Has to be a byte or it does not work metadata.put(EntityData.PLAYER_FLAGS, (byte) 2); - if (entityId == session.getPlayerEntity().getEntityId()) { - Vector3i lastInteractionPos = session.getLastInteractionPosition(); - metadata.put(EntityData.BED_POSITION, lastInteractionPos); - if (session.getConnector().getConfig().isCacheChunks()) { - int bed = session.getConnector().getWorldManager().getBlockAt(session, lastInteractionPos.getX(), - lastInteractionPos.getY(), lastInteractionPos.getZ()); - // Bed has to be updated, or else player is floating in the air - ChunkUtils.updateBlock(session, bed, lastInteractionPos); - } - } else { - metadata.put(EntityData.BED_POSITION, Vector3i.from(position.getFloorX(), position.getFloorY() - 2, position.getFloorZ())); - } metadata.put(EntityData.BOUNDING_BOX_WIDTH, 0.2f); metadata.put(EntityData.BOUNDING_BOX_HEIGHT, 0.2f); } else if (metadata.getFlags().getFlag(EntityFlag.SLEEPING)) { diff --git a/connector/src/main/java/org/geysermc/connector/entity/ItemFrameEntity.java b/connector/src/main/java/org/geysermc/connector/entity/ItemFrameEntity.java index f9d2ace46..972fa8d02 100644 --- a/connector/src/main/java/org/geysermc/connector/entity/ItemFrameEntity.java +++ b/connector/src/main/java/org/geysermc/connector/entity/ItemFrameEntity.java @@ -69,7 +69,6 @@ public class ItemFrameEntity extends Entity { public ItemFrameEntity(long entityId, long geyserId, EntityType entityType, Vector3f position, Vector3f motion, Vector3f rotation, HangingDirection direction) { super(entityId, geyserId, entityType, position, motion, rotation); - NbtMapBuilder builder = NbtMap.builder(); NbtMapBuilder blockBuilder = NbtMap.builder() .putString("name", "minecraft:frame") .putInt("version", BlockTranslator.getBlockStateVersion()); @@ -77,9 +76,7 @@ public class ItemFrameEntity extends Entity { .putInt("facing_direction", direction.ordinal()) .putByte("item_frame_map_bit", (byte) 0) .build()); - builder.put("block", blockBuilder.build()); - builder.putShort("id", (short) 199); - bedrockRuntimeId = BlockTranslator.getItemFrame(builder.build()); + bedrockRuntimeId = BlockTranslator.getItemFrame(blockBuilder.build()); bedrockPosition = Vector3i.from(position.getFloorX(), position.getFloorY(), position.getFloorZ()); } @@ -101,14 +98,12 @@ public class ItemFrameEntity extends Entity { ItemEntry itemEntry = ItemRegistry.getItem((ItemStack) entityMetadata.getValue()); NbtMapBuilder builder = NbtMap.builder(); - String blockName = ItemRegistry.getBedrockIdentifer(itemEntry); - builder.putByte("Count", (byte) itemData.getCount()); if (itemData.getTag() != null) { builder.put("tag", itemData.getTag().toBuilder().build()); } builder.putShort("Damage", itemData.getDamage()); - builder.putString("Name", blockName); + builder.putString("Name", itemEntry.getBedrockIdentifier()); NbtMapBuilder tag = getDefaultTag().toBuilder(); tag.put("Item", builder.build()); tag.putFloat("ItemDropChance", 1.0f); @@ -141,7 +136,7 @@ public class ItemFrameEntity extends Entity { UpdateBlockPacket updateBlockPacket = new UpdateBlockPacket(); updateBlockPacket.setDataLayer(0); updateBlockPacket.setBlockPosition(bedrockPosition); - updateBlockPacket.setRuntimeId(0); + updateBlockPacket.setRuntimeId(BlockTranslator.BEDROCK_AIR_ID); updateBlockPacket.getFlags().add(UpdateBlockPacket.Flag.PRIORITY); updateBlockPacket.getFlags().add(UpdateBlockPacket.Flag.NETWORK); updateBlockPacket.getFlags().add(UpdateBlockPacket.Flag.NEIGHBORS); @@ -196,18 +191,6 @@ public class ItemFrameEntity extends Entity { return session.getItemFrameCache().getOrDefault(position, -1); } - /** - * Determines if the position contains an item frame. - * Does largely the same thing as getItemFrameEntityId, but for speed purposes is implemented separately, - * since every block destroy packet has to check for an item frame. - * @param position position of block. - * @param session GeyserSession. - * @return true if position contains item frame, false if not. - */ - public static boolean positionContainsItemFrame(GeyserSession session, Vector3i position) { - return session.getItemFrameCache().containsKey(position); - } - /** * Force-remove from the position-to-ID map so it doesn't cause conflicts. * @param session GeyserSession. diff --git a/connector/src/main/java/org/geysermc/connector/entity/ItemedFireballEntity.java b/connector/src/main/java/org/geysermc/connector/entity/ItemedFireballEntity.java index e04e04111..1544f767a 100644 --- a/connector/src/main/java/org/geysermc/connector/entity/ItemedFireballEntity.java +++ b/connector/src/main/java/org/geysermc/connector/entity/ItemedFireballEntity.java @@ -27,10 +27,24 @@ package org.geysermc.connector.entity; import com.nukkitx.math.vector.Vector3f; import org.geysermc.connector.entity.type.EntityType; +import org.geysermc.connector.network.session.GeyserSession; -public class ItemedFireballEntity extends Entity { +public class ItemedFireballEntity extends ThrowableEntity { + private final Vector3f acceleration; public ItemedFireballEntity(long entityId, long geyserId, EntityType entityType, Vector3f position, Vector3f motion, Vector3f rotation) { - super(entityId, geyserId, entityType, position, motion, rotation); + super(entityId, geyserId, entityType, position, Vector3f.ZERO, rotation); + acceleration = motion; + } + + @Override + protected void updatePosition(GeyserSession session) { + position = position.add(motion); + // TODO: While this reduces latency in position updating (needed for better fireball reflecting), + // TODO: movement is incredibly stiff. See if the MoveEntityDeltaPacket in 1.16.100 fixes this, and if not, + // TODO: only use this laggy movement for fireballs that be reflected + moveAbsoluteImmediate(session, position, rotation, false, true); + float drag = getDrag(session); + motion = motion.add(acceleration).mul(drag); } } diff --git a/connector/src/main/java/org/geysermc/connector/entity/LivingEntity.java b/connector/src/main/java/org/geysermc/connector/entity/LivingEntity.java index ae9eaee5f..345c19dea 100644 --- a/connector/src/main/java/org/geysermc/connector/entity/LivingEntity.java +++ b/connector/src/main/java/org/geysermc/connector/entity/LivingEntity.java @@ -26,7 +26,9 @@ package org.geysermc.connector.entity; import com.github.steveice10.mc.protocol.data.game.entity.metadata.EntityMetadata; +import com.github.steveice10.mc.protocol.data.game.entity.metadata.Position; import com.nukkitx.math.vector.Vector3f; +import com.nukkitx.math.vector.Vector3i; import com.nukkitx.protocol.bedrock.data.AttributeData; import com.nukkitx.protocol.bedrock.data.entity.EntityData; import com.nukkitx.protocol.bedrock.data.entity.EntityFlag; @@ -42,6 +44,7 @@ import org.geysermc.connector.entity.type.EntityType; import org.geysermc.connector.network.session.GeyserSession; import org.geysermc.connector.network.translators.item.ItemRegistry; import org.geysermc.connector.utils.AttributeUtils; +import org.geysermc.connector.utils.ChunkUtils; import java.util.ArrayList; import java.util.List; @@ -84,6 +87,17 @@ public class LivingEntity extends Entity { case 10: metadata.put(EntityData.EFFECT_AMBIENT, (byte) ((boolean) entityMetadata.getValue() ? 1 : 0)); break; + case 13: // Bed Position + Position bedPosition = (Position) entityMetadata.getValue(); + if (bedPosition != null) { + metadata.put(EntityData.BED_POSITION, Vector3i.from(bedPosition.getX(), bedPosition.getY(), bedPosition.getZ())); + if (session.getConnector().getConfig().isCacheChunks()) { + int bed = session.getConnector().getWorldManager().getBlockAt(session, bedPosition); + // Bed has to be updated, or else player is floating in the air + ChunkUtils.updateBlock(session, bed, bedPosition); + } + } + break; } super.updateBedrockMetadata(entityMetadata, session); diff --git a/connector/src/main/java/org/geysermc/connector/entity/PlayerEntity.java b/connector/src/main/java/org/geysermc/connector/entity/PlayerEntity.java index 390110d18..be65525cb 100644 --- a/connector/src/main/java/org/geysermc/connector/entity/PlayerEntity.java +++ b/connector/src/main/java/org/geysermc/connector/entity/PlayerEntity.java @@ -27,14 +27,15 @@ package org.geysermc.connector.entity; import com.github.steveice10.mc.auth.data.GameProfile; import com.github.steveice10.mc.protocol.data.game.entity.metadata.EntityMetadata; -import com.github.steveice10.mc.protocol.data.game.scoreboard.NameTagVisibility; import com.github.steveice10.mc.protocol.data.message.TextMessage; import com.github.steveice10.opennbt.tag.builtin.CompoundTag; import com.nukkitx.math.vector.Vector3f; +import com.nukkitx.math.vector.Vector3i; import com.nukkitx.protocol.bedrock.data.AttributeData; import com.nukkitx.protocol.bedrock.data.PlayerPermission; import com.nukkitx.protocol.bedrock.data.command.CommandPermission; import com.nukkitx.protocol.bedrock.data.entity.EntityData; +import com.nukkitx.protocol.bedrock.data.entity.EntityFlag; import com.nukkitx.protocol.bedrock.data.entity.EntityLinkData; import com.nukkitx.protocol.bedrock.packet.AddPlayerPacket; import com.nukkitx.protocol.bedrock.packet.MovePlayerPacket; @@ -50,7 +51,7 @@ import org.geysermc.connector.network.session.GeyserSession; import org.geysermc.connector.network.session.cache.EntityEffectCache; import org.geysermc.connector.scoreboard.Team; import org.geysermc.connector.utils.AttributeUtils; -import org.geysermc.connector.utils.MessageUtils; +import org.geysermc.connector.network.translators.chat.MessageTranslator; import java.util.ArrayList; import java.util.List; @@ -168,6 +169,17 @@ public class PlayerEntity extends LivingEntity { movePlayerPacket.setRotation(getBedrockRotation()); movePlayerPacket.setOnGround(isOnGround); movePlayerPacket.setMode(MovePlayerPacket.Mode.NORMAL); + // If the player is moved while sleeping, we have to adjust their y, so it appears + // correctly on Bedrock. This fixes GSit's lay. + if (metadata.getFlags().getFlag(EntityFlag.SLEEPING)) { + Vector3i bedPosition = metadata.getPos(EntityData.BED_POSITION); + if (bedPosition != null && (bedPosition.getY() == 0 || bedPosition.distanceSquared(position.toInt()) > 4)) { + // Force the player movement by using a teleport + movePlayerPacket.setPosition(Vector3f.from(position.getX(), position.getY() - entityType.getOffset() + 0.2f, position.getZ())); + movePlayerPacket.setMode(MovePlayerPacket.Mode.TELEPORT); + movePlayerPacket.setTeleportationCause(MovePlayerPacket.TeleportationCause.UNKNOWN); + } + } session.sendUpstreamPacket(movePlayerPacket); if (leftParrot != null) { leftParrot.moveRelative(session, relX, relY, relZ, rotation, true); @@ -231,22 +243,16 @@ public class PlayerEntity extends LivingEntity { String username = this.username; TextMessage name = (TextMessage) entityMetadata.getValue(); if (name != null) { - username = MessageUtils.getBedrockMessage(name); + username = MessageTranslator.convertMessage(name.toString()); } Team team = session.getWorldCache().getScoreboard().getTeamFor(username); if (team != null) { - // Cover different visibility settings - if (team.getNameTagVisibility() == NameTagVisibility.NEVER) { - metadata.put(EntityData.NAMETAG, ""); - } else if (team.getNameTagVisibility() == NameTagVisibility.HIDE_FOR_OTHER_TEAMS && - !team.getEntities().contains(session.getPlayerEntity().getUsername())) { - metadata.put(EntityData.NAMETAG, ""); - } else if (team.getNameTagVisibility() == NameTagVisibility.HIDE_FOR_OWN_TEAM && - team.getEntities().contains(session.getPlayerEntity().getUsername())) { - metadata.put(EntityData.NAMETAG, ""); - } else { - metadata.put(EntityData.NAMETAG, team.getPrefix() + MessageUtils.toChatColor(team.getColor()) + username + team.getSuffix()); + String displayName = ""; + if (team.isVisibleFor(session.getPlayerEntity().getUsername())) { + displayName = MessageTranslator.toChatColor(team.getColor()) + username; + displayName = team.getCurrentData().getDisplayName(displayName); } + metadata.put(EntityData.NAMETAG, displayName); } } diff --git a/connector/src/main/java/org/geysermc/connector/entity/ThrowableEntity.java b/connector/src/main/java/org/geysermc/connector/entity/ThrowableEntity.java index b3632606b..5b7ba5c03 100644 --- a/connector/src/main/java/org/geysermc/connector/entity/ThrowableEntity.java +++ b/connector/src/main/java/org/geysermc/connector/entity/ThrowableEntity.java @@ -31,14 +31,23 @@ import com.nukkitx.protocol.bedrock.data.entity.EntityFlag; import com.nukkitx.protocol.bedrock.packet.LevelEventPacket; import org.geysermc.connector.entity.type.EntityType; import org.geysermc.connector.network.session.GeyserSession; +import org.geysermc.connector.network.translators.world.block.BlockTranslator; import java.util.concurrent.ScheduledFuture; import java.util.concurrent.TimeUnit; +/** + * Used as a class for any object-like entity that moves as a projectile + */ public class ThrowableEntity extends Entity { private Vector3f lastPosition; - private ScheduledFuture positionUpdater; + /** + * Updates the position for the Bedrock client. + * + * Java clients assume the next positions of moving items. Bedrock needs to be explicitly told positions + */ + protected ScheduledFuture positionUpdater; public ThrowableEntity(long entityId, long geyserId, EntityType entityType, Vector3f position, Vector3f motion, Vector3f rotation) { super(entityId, geyserId, entityType, position, motion, rotation); @@ -49,20 +58,86 @@ public class ThrowableEntity extends Entity { public void spawnEntity(GeyserSession session) { super.spawnEntity(session); positionUpdater = session.getConnector().getGeneralThreadPool().scheduleAtFixedRate(() -> { - super.moveRelative(session, motion.getX(), motion.getY(), motion.getZ(), rotation, onGround); - - if (metadata.getFlags().getFlag(EntityFlag.HAS_GRAVITY)) { - float gravity = 0.03f; // Snowball, Egg, and Ender Pearl - if (entityType == EntityType.THROWN_POTION || entityType == EntityType.LINGERING_POTION) { - gravity = 0.05f; - } else if (entityType == EntityType.THROWN_EXP_BOTTLE) { - gravity = 0.07f; - } - motion = motion.down(gravity); + if (session.isClosed()) { + positionUpdater.cancel(true); + return; } + updatePosition(session); }, 0, 50, TimeUnit.MILLISECONDS); } + protected void moveAbsoluteImmediate(GeyserSession session, Vector3f position, Vector3f rotation, boolean isOnGround, boolean teleported) { + super.moveAbsolute(session, position, rotation, isOnGround, teleported); + } + + protected void updatePosition(GeyserSession session) { + super.moveRelative(session, motion.getX(), motion.getY(), motion.getZ(), rotation, onGround); + float drag = getDrag(session); + float gravity = getGravity(); + motion = motion.mul(drag).down(gravity); + } + + /** + * Get the gravity of this entity type. Used for applying gravity while the entity is in motion. + * + * @return the amount of gravity to apply to this entity while in motion. + */ + protected float getGravity() { + if (metadata.getFlags().getFlag(EntityFlag.HAS_GRAVITY)) { + switch (entityType) { + case THROWN_POTION: + case LINGERING_POTION: + return 0.05f; + case THROWN_EXP_BOTTLE: + return 0.07f; + case FIREBALL: + return 0; + case SNOWBALL: + case THROWN_EGG: + case THROWN_ENDERPEARL: + return 0.03f; + } + } + return 0; + } + + /** + * @param session the session of the Bedrock client. + * @return the drag that should be multiplied to the entity's motion + */ + protected float getDrag(GeyserSession session) { + if (isInWater(session)) { + return 0.8f; + } else { + switch (entityType) { + case THROWN_POTION: + case LINGERING_POTION: + case THROWN_EXP_BOTTLE: + case SNOWBALL: + case THROWN_EGG: + case THROWN_ENDERPEARL: + return 0.99f; + case FIREBALL: + case SMALL_FIREBALL: + case DRAGON_FIREBALL: + return 0.95f; + } + } + return 1; + } + + /** + * @param session the session of the Bedrock client. + * @return true if this entity is currently in water. + */ + protected boolean isInWater(GeyserSession session) { + if (session.getConnector().getConfig().isCacheChunks()) { + int block = session.getConnector().getWorldManager().getBlockAt(session, position.toInt()); + return block == BlockTranslator.BEDROCK_WATER_ID; + } + return false; + } + @Override public boolean despawnEntity(GeyserSession session) { positionUpdater.cancel(true); diff --git a/connector/src/main/java/org/geysermc/connector/entity/WitherSkullEntity.java b/connector/src/main/java/org/geysermc/connector/entity/WitherSkullEntity.java new file mode 100644 index 000000000..99b3df3d0 --- /dev/null +++ b/connector/src/main/java/org/geysermc/connector/entity/WitherSkullEntity.java @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2019-2020 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.connector.entity; + +import com.github.steveice10.mc.protocol.data.game.entity.metadata.EntityMetadata; +import com.nukkitx.math.vector.Vector3f; +import org.geysermc.connector.entity.type.EntityType; +import org.geysermc.connector.network.session.GeyserSession; + +public class WitherSkullEntity extends ItemedFireballEntity { + private boolean isCharged; + + public WitherSkullEntity(long entityId, long geyserId, EntityType entityType, Vector3f position, Vector3f motion, Vector3f rotation) { + super(entityId, geyserId, entityType, position, motion, rotation); + } + + @Override + protected float getDrag(GeyserSession session) { + return isCharged ? 0.73f : super.getDrag(session); + } + + @Override + public void updateBedrockMetadata(EntityMetadata entityMetadata, GeyserSession session) { + if (entityMetadata.getId() == 7) { + boolean newIsCharged = (boolean) entityMetadata.getValue(); + if (newIsCharged != isCharged) { + isCharged = newIsCharged; + entityType = isCharged ? EntityType.WITHER_SKULL_DANGEROUS : EntityType.WITHER_SKULL; + despawnEntity(session); + spawnEntity(session); + } + } + super.updateBedrockMetadata(entityMetadata, session); + } +} diff --git a/connector/src/main/java/org/geysermc/connector/entity/living/ArmorStandEntity.java b/connector/src/main/java/org/geysermc/connector/entity/living/ArmorStandEntity.java index 07496093f..b61aeda92 100644 --- a/connector/src/main/java/org/geysermc/connector/entity/living/ArmorStandEntity.java +++ b/connector/src/main/java/org/geysermc/connector/entity/living/ArmorStandEntity.java @@ -29,6 +29,7 @@ import com.github.steveice10.mc.protocol.data.game.entity.metadata.EntityMetadat import com.github.steveice10.mc.protocol.data.game.entity.metadata.MetadataType; import com.nukkitx.math.vector.Vector3f; import com.nukkitx.protocol.bedrock.data.entity.EntityData; +import lombok.Getter; import org.geysermc.connector.entity.LivingEntity; import org.geysermc.connector.entity.type.EntityType; import org.geysermc.connector.network.session.GeyserSession; @@ -36,6 +37,7 @@ import org.geysermc.connector.network.session.GeyserSession; public class ArmorStandEntity extends LivingEntity { // These are used to store the state of the armour stand for use when handling invisibility + @Getter private boolean isMarker = false; private boolean isInvisible = false; private boolean isSmall = false; @@ -47,7 +49,7 @@ public class ArmorStandEntity extends LivingEntity { @Override public void moveAbsolute(GeyserSession session, Vector3f position, Vector3f rotation, boolean isOnGround, boolean teleported) { // Fake the height to be above where it is so the nametag appears in the right location for invisible non-marker armour stands - if (!isMarker && isInvisible) { + if (!isMarker && isInvisible && passengers.isEmpty()) { position = position.add(0d, entityType.getHeight() * (isSmall ? 0.55d : 1d), 0d); } diff --git a/connector/src/main/java/org/geysermc/connector/entity/living/BatEntity.java b/connector/src/main/java/org/geysermc/connector/entity/living/BatEntity.java new file mode 100644 index 000000000..b7b7534c8 --- /dev/null +++ b/connector/src/main/java/org/geysermc/connector/entity/living/BatEntity.java @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2019-2020 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.connector.entity.living; + +import com.github.steveice10.mc.protocol.data.game.entity.metadata.EntityMetadata; +import com.nukkitx.math.vector.Vector3f; +import com.nukkitx.protocol.bedrock.data.entity.EntityFlag; +import org.geysermc.connector.entity.type.EntityType; +import org.geysermc.connector.network.session.GeyserSession; + +public class BatEntity extends AmbientEntity { + + public BatEntity(long entityId, long geyserId, EntityType entityType, Vector3f position, Vector3f motion, Vector3f rotation) { + super(entityId, geyserId, entityType, position, motion, rotation); + } + + @Override + public void updateBedrockMetadata(EntityMetadata entityMetadata, GeyserSession session) { + if (entityMetadata.getId() == 15) { + byte xd = (byte) entityMetadata.getValue(); + metadata.getFlags().setFlag(EntityFlag.RESTING, (xd & 0x01) == 0x01); + } + super.updateBedrockMetadata(entityMetadata, session); + } +} diff --git a/connector/src/main/java/org/geysermc/connector/entity/living/SnowGolemEntity.java b/connector/src/main/java/org/geysermc/connector/entity/living/SnowGolemEntity.java new file mode 100644 index 000000000..2f75e6458 --- /dev/null +++ b/connector/src/main/java/org/geysermc/connector/entity/living/SnowGolemEntity.java @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2019-2020 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.connector.entity.living; + +import com.github.steveice10.mc.protocol.data.game.entity.metadata.EntityMetadata; +import com.nukkitx.math.vector.Vector3f; +import com.nukkitx.protocol.bedrock.data.entity.EntityFlag; +import org.geysermc.connector.entity.type.EntityType; +import org.geysermc.connector.network.session.GeyserSession; + +public class SnowGolemEntity extends GolemEntity { + + public SnowGolemEntity(long entityId, long geyserId, EntityType entityType, Vector3f position, Vector3f motion, Vector3f rotation) { + super(entityId, geyserId, entityType, position, motion, rotation); + } + + @Override + public void updateBedrockMetadata(EntityMetadata entityMetadata, GeyserSession session) { + if (entityMetadata.getId() == 15) { + byte xd = (byte) entityMetadata.getValue(); + // Handle the visibility of the pumpkin + metadata.getFlags().setFlag(EntityFlag.SHEARED, (xd & 0x10) != 0x10); + } + super.updateBedrockMetadata(entityMetadata, session); + } +} diff --git a/connector/src/main/java/org/geysermc/connector/entity/living/animal/BeeEntity.java b/connector/src/main/java/org/geysermc/connector/entity/living/animal/BeeEntity.java index c46f00fe8..ee17e2a2c 100644 --- a/connector/src/main/java/org/geysermc/connector/entity/living/animal/BeeEntity.java +++ b/connector/src/main/java/org/geysermc/connector/entity/living/animal/BeeEntity.java @@ -27,7 +27,10 @@ package org.geysermc.connector.entity.living.animal; import com.github.steveice10.mc.protocol.data.game.entity.metadata.EntityMetadata; import com.nukkitx.math.vector.Vector3f; +import com.nukkitx.protocol.bedrock.data.entity.EntityData; +import com.nukkitx.protocol.bedrock.data.entity.EntityEventType; import com.nukkitx.protocol.bedrock.data.entity.EntityFlag; +import com.nukkitx.protocol.bedrock.packet.EntityEventPacket; import org.geysermc.connector.entity.type.EntityType; import org.geysermc.connector.network.session.GeyserSession; @@ -41,10 +44,23 @@ public class BeeEntity extends AnimalEntity { public void updateBedrockMetadata(EntityMetadata entityMetadata, GeyserSession session) { if (entityMetadata.getId() == 16) { byte xd = (byte) entityMetadata.getValue(); - metadata.getFlags().setFlag(EntityFlag.ANGRY, (xd & 0x02) == 0x02); + // Bee is performing sting attack; trigger animation + if ((xd & 0x02) == 0x02) { + EntityEventPacket packet = new EntityEventPacket(); + packet.setRuntimeEntityId(geyserId); + packet.setType(EntityEventType.ATTACK_START); + packet.setData(0); + session.sendUpstreamPacket(packet); + } + // If the bee has stung + metadata.put(EntityData.MARK_VARIANT, (xd & 0x04) == 0x04 ? 1 : 0); // If the bee has nectar or not metadata.getFlags().setFlag(EntityFlag.POWERED, (xd & 0x08) == 0x08); } + if (entityMetadata.getId() == 17) { + // Converting "anger time" to a boolean + metadata.getFlags().setFlag(EntityFlag.ANGRY, (int) entityMetadata.getValue() > 0); + } super.updateBedrockMetadata(entityMetadata, session); } } diff --git a/connector/src/main/java/org/geysermc/connector/entity/living/animal/FoxEntity.java b/connector/src/main/java/org/geysermc/connector/entity/living/animal/FoxEntity.java index 88c30cbfa..bbc2d7def 100644 --- a/connector/src/main/java/org/geysermc/connector/entity/living/animal/FoxEntity.java +++ b/connector/src/main/java/org/geysermc/connector/entity/living/animal/FoxEntity.java @@ -41,12 +41,13 @@ public class FoxEntity extends AnimalEntity { @Override public void updateBedrockMetadata(EntityMetadata entityMetadata, GeyserSession session) { if (entityMetadata.getId() == 16) { - metadata.put(EntityData.VARIANT, (int) entityMetadata.getValue()); + metadata.put(EntityData.VARIANT, entityMetadata.getValue()); } if (entityMetadata.getId() == 17) { byte xd = (byte) entityMetadata.getValue(); metadata.getFlags().setFlag(EntityFlag.SITTING, (xd & 0x01) == 0x01); metadata.getFlags().setFlag(EntityFlag.SNEAKING, (xd & 0x04) == 0x04); + metadata.getFlags().setFlag(EntityFlag.INTERESTED, (xd & 0x08) == 0x08); metadata.getFlags().setFlag(EntityFlag.SLEEPING, (xd & 0x20) == 0x20); } super.updateBedrockMetadata(entityMetadata, session); diff --git a/connector/src/main/java/org/geysermc/connector/entity/living/animal/HoglinEntity.java b/connector/src/main/java/org/geysermc/connector/entity/living/animal/HoglinEntity.java new file mode 100644 index 000000000..3fd291724 --- /dev/null +++ b/connector/src/main/java/org/geysermc/connector/entity/living/animal/HoglinEntity.java @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2019-2020 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.connector.entity.living.animal; + +import com.github.steveice10.mc.protocol.data.game.entity.metadata.EntityMetadata; +import com.nukkitx.math.vector.Vector3f; +import com.nukkitx.protocol.bedrock.data.entity.EntityFlag; +import org.geysermc.connector.entity.type.EntityType; +import org.geysermc.connector.network.session.GeyserSession; +import org.geysermc.connector.utils.DimensionUtils; + +public class HoglinEntity extends AnimalEntity { + + public HoglinEntity(long entityId, long geyserId, EntityType entityType, Vector3f position, Vector3f motion, Vector3f rotation) { + super(entityId, geyserId, entityType, position, motion, rotation); + } + + @Override + public void updateBedrockMetadata(EntityMetadata entityMetadata, GeyserSession session) { + if (entityMetadata.getId() == 16) { + // Immune to zombification? + // Apply shaking effect if not in the nether and zombification is possible + metadata.getFlags().setFlag(EntityFlag.SHAKING, !((boolean) entityMetadata.getValue()) && !session.getDimension().equals(DimensionUtils.NETHER)); + } + super.updateBedrockMetadata(entityMetadata, session); + } +} diff --git a/connector/src/main/java/org/geysermc/connector/entity/living/animal/MooshroomEntity.java b/connector/src/main/java/org/geysermc/connector/entity/living/animal/MooshroomEntity.java new file mode 100644 index 000000000..69fb55fb4 --- /dev/null +++ b/connector/src/main/java/org/geysermc/connector/entity/living/animal/MooshroomEntity.java @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2019-2020 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.connector.entity.living.animal; + +import com.github.steveice10.mc.protocol.data.game.entity.metadata.EntityMetadata; +import com.nukkitx.math.vector.Vector3f; +import com.nukkitx.protocol.bedrock.data.entity.EntityData; +import org.geysermc.connector.entity.type.EntityType; +import org.geysermc.connector.network.session.GeyserSession; + +public class MooshroomEntity extends AnimalEntity { + + public MooshroomEntity(long entityId, long geyserId, EntityType entityType, Vector3f position, Vector3f motion, Vector3f rotation) { + super(entityId, geyserId, entityType, position, motion, rotation); + } + + @Override + public void updateBedrockMetadata(EntityMetadata entityMetadata, GeyserSession session) { + if (entityMetadata.getId() == 16) { + metadata.put(EntityData.VARIANT, entityMetadata.getValue().equals("brown") ? 1 : 0); + } + super.updateBedrockMetadata(entityMetadata, session); + } +} diff --git a/connector/src/main/java/org/geysermc/connector/entity/living/animal/PandaEntity.java b/connector/src/main/java/org/geysermc/connector/entity/living/animal/PandaEntity.java index 7e5551226..ed3ed80b7 100644 --- a/connector/src/main/java/org/geysermc/connector/entity/living/animal/PandaEntity.java +++ b/connector/src/main/java/org/geysermc/connector/entity/living/animal/PandaEntity.java @@ -27,23 +27,75 @@ package org.geysermc.connector.entity.living.animal; import com.github.steveice10.mc.protocol.data.game.entity.metadata.EntityMetadata; import com.nukkitx.math.vector.Vector3f; +import com.nukkitx.protocol.bedrock.data.entity.EntityData; +import com.nukkitx.protocol.bedrock.data.entity.EntityEventType; import com.nukkitx.protocol.bedrock.data.entity.EntityFlag; +import com.nukkitx.protocol.bedrock.packet.EntityEventPacket; import org.geysermc.connector.entity.type.EntityType; import org.geysermc.connector.network.session.GeyserSession; +import org.geysermc.connector.network.translators.item.ItemRegistry; public class PandaEntity extends AnimalEntity { + private int mainGene; + private int hiddenGene; + public PandaEntity(long entityId, long geyserId, EntityType entityType, Vector3f position, Vector3f motion, Vector3f rotation) { super(entityId, geyserId, entityType, position, motion, rotation); } @Override public void updateBedrockMetadata(EntityMetadata entityMetadata, GeyserSession session) { + if (entityMetadata.getId() == 18) { + metadata.getFlags().setFlag(EntityFlag.EATING, (int) entityMetadata.getValue() > 0); + metadata.put(EntityData.EATING_COUNTER, entityMetadata.getValue()); + if ((int) entityMetadata.getValue() != 0) { + // Particles and sound + EntityEventPacket packet = new EntityEventPacket(); + packet.setRuntimeEntityId(geyserId); + packet.setType(EntityEventType.EATING_ITEM); + packet.setData(ItemRegistry.BAMBOO.getBedrockId() << 16); + session.sendUpstreamPacket(packet); + } + } + if (entityMetadata.getId() == 19) { + mainGene = (int) (byte) entityMetadata.getValue(); + updateAppearance(); + } + if (entityMetadata.getId() == 20) { + hiddenGene = (int) (byte) entityMetadata.getValue(); + updateAppearance(); + } if (entityMetadata.getId() == 21) { byte xd = (byte) entityMetadata.getValue(); metadata.getFlags().setFlag(EntityFlag.SNEEZING, (xd & 0x02) == 0x02); - metadata.getFlags().setFlag(EntityFlag.EATING, (xd & 0x04) == 0x04); + metadata.getFlags().setFlag(EntityFlag.ROLLING, (xd & 0x04) == 0x04); + metadata.getFlags().setFlag(EntityFlag.SITTING, (xd & 0x08) == 0x08); + // Required to put these both for sitting to actually show + metadata.put(EntityData.SITTING_AMOUNT, (xd & 0x08) == 0x08 ? 1f : 0f); + metadata.put(EntityData.SITTING_AMOUNT_PREVIOUS, (xd & 0x08) == 0x08 ? 1f : 0f); + metadata.getFlags().setFlag(EntityFlag.LAYING_DOWN, (xd & 0x10) == 0x10); } super.updateBedrockMetadata(entityMetadata, session); } + + /** + * Update the panda's appearance, and take into consideration the recessive brown and weak traits that only show up + * when both main and hidden genes match + */ + private void updateAppearance() { + if (mainGene == 4 || mainGene == 5) { + // Main gene is a recessive trait + if (mainGene == hiddenGene) { + // Main and hidden genes match; this is what the panda looks like. + metadata.put(EntityData.VARIANT, mainGene); + } else { + // Genes have no effect on appearance + metadata.put(EntityData.VARIANT, 0); + } + } else { + // No need to worry about hidden gene + metadata.put(EntityData.VARIANT, mainGene); + } + } } diff --git a/connector/src/main/java/org/geysermc/connector/entity/living/animal/RabbitEntity.java b/connector/src/main/java/org/geysermc/connector/entity/living/animal/RabbitEntity.java index 0b61713aa..792027926 100644 --- a/connector/src/main/java/org/geysermc/connector/entity/living/animal/RabbitEntity.java +++ b/connector/src/main/java/org/geysermc/connector/entity/living/animal/RabbitEntity.java @@ -48,6 +48,15 @@ public class RabbitEntity extends AnimalEntity { metadata.put(EntityData.SCALE, .35f); metadata.getFlags().setFlag(EntityFlag.BABY, true); } + } else if (entityMetadata.getId() == 16) { + int variant = (int) entityMetadata.getValue(); + + // Change the killer bunny to display as white since it only exists on Java Edition + if (variant == 99) { + variant = 1; + } + + metadata.put(EntityData.VARIANT, variant); } } } \ No newline at end of file diff --git a/connector/src/main/java/org/geysermc/connector/entity/living/animal/TurtleEntity.java b/connector/src/main/java/org/geysermc/connector/entity/living/animal/TurtleEntity.java new file mode 100644 index 000000000..555e22684 --- /dev/null +++ b/connector/src/main/java/org/geysermc/connector/entity/living/animal/TurtleEntity.java @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2019-2020 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.connector.entity.living.animal; + +import com.github.steveice10.mc.protocol.data.game.entity.metadata.EntityMetadata; +import com.nukkitx.math.vector.Vector3f; +import com.nukkitx.protocol.bedrock.data.entity.EntityFlag; +import org.geysermc.connector.entity.type.EntityType; +import org.geysermc.connector.network.session.GeyserSession; + +public class TurtleEntity extends AnimalEntity { + + public TurtleEntity(long entityId, long geyserId, EntityType entityType, Vector3f position, Vector3f motion, Vector3f rotation) { + super(entityId, geyserId, entityType, position, motion, rotation); + } + + @Override + public void updateBedrockMetadata(EntityMetadata entityMetadata, GeyserSession session) { + if (entityMetadata.getId() == 17) { + metadata.getFlags().setFlag(EntityFlag.IS_PREGNANT, (boolean) entityMetadata.getValue()); + } else if (entityMetadata.getId() == 18) { + metadata.getFlags().setFlag(EntityFlag.LAYING_EGG, (boolean) entityMetadata.getValue()); + } + super.updateBedrockMetadata(entityMetadata, session); + } +} diff --git a/connector/src/main/java/org/geysermc/connector/entity/living/animal/horse/AbstractHorseEntity.java b/connector/src/main/java/org/geysermc/connector/entity/living/animal/horse/AbstractHorseEntity.java index e08f9adf0..cf9f84b42 100644 --- a/connector/src/main/java/org/geysermc/connector/entity/living/animal/horse/AbstractHorseEntity.java +++ b/connector/src/main/java/org/geysermc/connector/entity/living/animal/horse/AbstractHorseEntity.java @@ -27,10 +27,14 @@ package org.geysermc.connector.entity.living.animal.horse; import com.github.steveice10.mc.protocol.data.game.entity.metadata.EntityMetadata; import com.nukkitx.math.vector.Vector3f; +import com.nukkitx.protocol.bedrock.data.entity.EntityData; +import com.nukkitx.protocol.bedrock.data.entity.EntityEventType; import com.nukkitx.protocol.bedrock.data.entity.EntityFlag; +import com.nukkitx.protocol.bedrock.packet.EntityEventPacket; import org.geysermc.connector.entity.living.animal.AnimalEntity; import org.geysermc.connector.entity.type.EntityType; import org.geysermc.connector.network.session.GeyserSession; +import org.geysermc.connector.network.translators.item.ItemRegistry; public class AbstractHorseEntity extends AnimalEntity { @@ -47,6 +51,30 @@ public class AbstractHorseEntity extends AnimalEntity { metadata.getFlags().setFlag(EntityFlag.SADDLED, (xd & 0x04) == 0x04); metadata.getFlags().setFlag(EntityFlag.EATING, (xd & 0x10) == 0x10); metadata.getFlags().setFlag(EntityFlag.STANDING, (xd & 0x20) == 0x20); + + // HorseFlags + // Bred 0x10 + // Eating 0x20 + // Open mouth 0x80 + int horseFlags = 0x0; + horseFlags = (xd & 0x40) == 0x40 ? horseFlags | 0x80 : horseFlags; + + // Only set eating when we don't have mouth open so a player interaction doesn't trigger the eating animation + horseFlags = (xd & 0x10) == 0x10 && (xd & 0x40) != 0x40 ? horseFlags | 0x20 : horseFlags; + + // Set the flags into the display item + metadata.put(EntityData.DISPLAY_ITEM, horseFlags); + + // Send the eating particles + // We use the wheat metadata as static particles since Java + // doesn't send over what item was used to feed the horse + if ((xd & 0x40) == 0x40) { + EntityEventPacket entityEventPacket = new EntityEventPacket(); + entityEventPacket.setRuntimeEntityId(geyserId); + entityEventPacket.setType(EntityEventType.EATING_ITEM); + entityEventPacket.setData(ItemRegistry.WHEAT.getBedrockId() << 16); + session.sendUpstreamPacket(entityEventPacket); + } } // Needed to control horses diff --git a/connector/src/main/java/org/geysermc/connector/entity/living/animal/horse/HorseEntity.java b/connector/src/main/java/org/geysermc/connector/entity/living/animal/horse/HorseEntity.java index da3ff3493..349da5e05 100644 --- a/connector/src/main/java/org/geysermc/connector/entity/living/animal/horse/HorseEntity.java +++ b/connector/src/main/java/org/geysermc/connector/entity/living/animal/horse/HorseEntity.java @@ -28,7 +28,6 @@ package org.geysermc.connector.entity.living.animal.horse; import com.github.steveice10.mc.protocol.data.game.entity.metadata.EntityMetadata; import com.nukkitx.math.vector.Vector3f; import com.nukkitx.protocol.bedrock.data.entity.EntityData; -import com.nukkitx.protocol.bedrock.data.entity.EntityFlag; import org.geysermc.connector.entity.type.EntityType; import org.geysermc.connector.network.session.GeyserSession; @@ -41,7 +40,7 @@ public class HorseEntity extends AbstractHorseEntity { @Override public void updateBedrockMetadata(EntityMetadata entityMetadata, GeyserSession session) { if (entityMetadata.getId() == 18) { - metadata.put(EntityData.VARIANT, (int) entityMetadata.getValue()); + metadata.put(EntityData.VARIANT, entityMetadata.getValue()); metadata.put(EntityData.MARK_VARIANT, (((int) entityMetadata.getValue()) >> 8) % 5); } super.updateBedrockMetadata(entityMetadata, session); diff --git a/connector/src/main/java/org/geysermc/connector/entity/living/animal/tameable/CatEntity.java b/connector/src/main/java/org/geysermc/connector/entity/living/animal/tameable/CatEntity.java index 067a360cf..5c5de5466 100644 --- a/connector/src/main/java/org/geysermc/connector/entity/living/animal/tameable/CatEntity.java +++ b/connector/src/main/java/org/geysermc/connector/entity/living/animal/tameable/CatEntity.java @@ -34,6 +34,8 @@ import org.geysermc.connector.network.session.GeyserSession; public class CatEntity extends TameableEntity { + private byte collarColor; + public CatEntity(long entityId, long geyserId, EntityType entityType, Vector3f position, Vector3f motion, Vector3f rotation) { super(entityId, geyserId, entityType, position, motion, rotation); } @@ -45,6 +47,13 @@ public class CatEntity extends TameableEntity { @Override public void updateBedrockMetadata(EntityMetadata entityMetadata, GeyserSession session) { + super.updateBedrockMetadata(entityMetadata, session); + if (entityMetadata.getId() == 16) { + // Update collar color if tamed + if (metadata.getFlags().getFlag(EntityFlag.TAMED)) { + metadata.put(EntityData.COLOR, collarColor); + } + } if (entityMetadata.getId() == 18) { // Different colors in Java and Bedrock for some reason int variantColor; @@ -67,11 +76,11 @@ public class CatEntity extends TameableEntity { metadata.put(EntityData.VARIANT, variantColor); } if (entityMetadata.getId() == 21) { + collarColor = (byte) (int) entityMetadata.getValue(); // Needed or else wild cats are a red color if (metadata.getFlags().getFlag(EntityFlag.TAMED)) { - metadata.put(EntityData.COLOR, (byte) (int) entityMetadata.getValue()); + metadata.put(EntityData.COLOR, collarColor); } } - super.updateBedrockMetadata(entityMetadata, session); } } diff --git a/connector/src/main/java/org/geysermc/connector/entity/living/animal/tameable/TameableEntity.java b/connector/src/main/java/org/geysermc/connector/entity/living/animal/tameable/TameableEntity.java index 2e8ab816c..9e73ebe57 100644 --- a/connector/src/main/java/org/geysermc/connector/entity/living/animal/tameable/TameableEntity.java +++ b/connector/src/main/java/org/geysermc/connector/entity/living/animal/tameable/TameableEntity.java @@ -29,10 +29,13 @@ import com.github.steveice10.mc.protocol.data.game.entity.metadata.EntityMetadat import com.nukkitx.math.vector.Vector3f; import com.nukkitx.protocol.bedrock.data.entity.EntityData; import com.nukkitx.protocol.bedrock.data.entity.EntityFlag; +import org.geysermc.connector.entity.Entity; import org.geysermc.connector.entity.living.animal.AnimalEntity; import org.geysermc.connector.entity.type.EntityType; import org.geysermc.connector.network.session.GeyserSession; +import java.util.UUID; + public class TameableEntity extends AnimalEntity { public TameableEntity(long entityId, long geyserId, EntityType entityType, Vector3f position, Vector3f motion, Vector3f rotation) { @@ -46,11 +49,22 @@ public class TameableEntity extends AnimalEntity { metadata.getFlags().setFlag(EntityFlag.SITTING, (xd & 0x01) == 0x01); metadata.getFlags().setFlag(EntityFlag.ANGRY, (xd & 0x02) == 0x02); metadata.getFlags().setFlag(EntityFlag.TAMED, (xd & 0x04) == 0x04); - // Must be set for wolf collar color to work - // Extending it to all entities to prevent future bugs - if (metadata.getFlags().getFlag(EntityFlag.TAMED)) { - metadata.put(EntityData.OWNER_EID, session.getPlayerEntity().getGeyserId()); - } // Can't de-tame an entity so no resetting the owner ID + } + + // Note: Must be set for wolf collar color to work + if (entityMetadata.getId() == 17) { + if (entityMetadata.getValue() != null) { + // Owner UUID of entity + Entity entity = session.getEntityCache().getPlayerEntity((UUID) entityMetadata.getValue()); + // Used as both a check since the player isn't in the entity cache and a normal fallback + if (entity == null) { + entity = session.getPlayerEntity(); + } + // Translate to entity ID + metadata.put(EntityData.OWNER_EID, entity.getGeyserId()); + } else { + metadata.put(EntityData.OWNER_EID, 0L); // Reset + } } super.updateBedrockMetadata(entityMetadata, session); } diff --git a/connector/src/main/java/org/geysermc/connector/entity/living/merchant/VillagerEntity.java b/connector/src/main/java/org/geysermc/connector/entity/living/merchant/VillagerEntity.java index 7ada302c2..028d18312 100644 --- a/connector/src/main/java/org/geysermc/connector/entity/living/merchant/VillagerEntity.java +++ b/connector/src/main/java/org/geysermc/connector/entity/living/merchant/VillagerEntity.java @@ -26,26 +26,32 @@ package org.geysermc.connector.entity.living.merchant; import com.github.steveice10.mc.protocol.data.game.entity.metadata.EntityMetadata; -import com.github.steveice10.mc.protocol.data.game.entity.metadata.VillagerData; import com.github.steveice10.mc.protocol.data.game.entity.metadata.Position; +import com.github.steveice10.mc.protocol.data.game.entity.metadata.VillagerData; import com.nukkitx.math.vector.Vector3f; +import com.nukkitx.math.vector.Vector3i; import com.nukkitx.protocol.bedrock.data.entity.EntityData; import com.nukkitx.protocol.bedrock.data.entity.EntityFlag; -import com.nukkitx.protocol.bedrock.data.entity.EntityFlags; -import com.nukkitx.protocol.bedrock.packet.*; +import com.nukkitx.protocol.bedrock.packet.MoveEntityAbsolutePacket; import it.unimi.dsi.fastutil.ints.Int2IntMap; import it.unimi.dsi.fastutil.ints.Int2IntOpenHashMap; import org.geysermc.connector.entity.type.EntityType; import org.geysermc.connector.network.session.GeyserSession; -import org.geysermc.connector.network.translators.world.WorldManager; import org.geysermc.connector.network.translators.world.block.BlockTranslator; + import java.util.regex.Matcher; import java.util.regex.Pattern; public class VillagerEntity extends AbstractMerchantEntity { + /** + * A map of Java profession IDs to Bedrock IDs + */ private static final Int2IntMap VILLAGER_VARIANTS = new Int2IntOpenHashMap(); - private static final Int2IntMap VILLAGER_REGIONS = new Int2IntOpenHashMap(); + /** + * A map of all Java region IDs (plains, savanna...) to Bedrock + */ + public static final Int2IntMap VILLAGER_REGIONS = new Int2IntOpenHashMap(); static { // Java villager profession IDs -> Bedrock @@ -99,9 +105,9 @@ public class VillagerEntity extends AbstractMerchantEntity { int bedId = 0; float bedPositionSubtractorW = 0; float bedPositionSubtractorN = 0; - if (session.getConnector().getConfig().isCacheChunks()) { - Position bedLocation = new Position((int) position.getFloorX(), (int) position.getFloorY(), (int) position.getFloorZ()); - bedId = session.getConnector().getWorldManager().getBlockAt(session, bedLocation); + Vector3i bedPosition = metadata.getPos(EntityData.BED_POSITION); + if (session.getConnector().getConfig().isCacheChunks() && bedPosition != null) { + bedId = session.getConnector().getWorldManager().getBlockAt(session, bedPosition); } String bedRotationZ = BlockTranslator.getJavaIdBlockMap().inverse().get(bedId); setRotation(rotation); diff --git a/connector/src/main/java/org/geysermc/connector/entity/living/monster/BasePiglinEntity.java b/connector/src/main/java/org/geysermc/connector/entity/living/monster/BasePiglinEntity.java index 830c7ea3d..b83a2ca73 100644 --- a/connector/src/main/java/org/geysermc/connector/entity/living/monster/BasePiglinEntity.java +++ b/connector/src/main/java/org/geysermc/connector/entity/living/monster/BasePiglinEntity.java @@ -1,11 +1,25 @@ package org.geysermc.connector.entity.living.monster; +import com.github.steveice10.mc.protocol.data.game.entity.metadata.EntityMetadata; import com.nukkitx.math.vector.Vector3f; +import com.nukkitx.protocol.bedrock.data.entity.EntityFlag; import org.geysermc.connector.entity.type.EntityType; +import org.geysermc.connector.network.session.GeyserSession; +import org.geysermc.connector.utils.DimensionUtils; public class BasePiglinEntity extends MonsterEntity { public BasePiglinEntity(long entityId, long geyserId, EntityType entityType, Vector3f position, Vector3f motion, Vector3f rotation) { super(entityId, geyserId, entityType, position, motion, rotation); } -} \ No newline at end of file + + @Override + public void updateBedrockMetadata(EntityMetadata entityMetadata, GeyserSession session) { + if (entityMetadata.getId() == 15) { + // Immune to zombification? + // Apply shaking effect if not in the nether and zombification is possible + metadata.getFlags().setFlag(EntityFlag.SHAKING, !((boolean) entityMetadata.getValue()) && !session.getDimension().equals(DimensionUtils.NETHER)); + } + super.updateBedrockMetadata(entityMetadata, session); + } +} diff --git a/connector/src/main/java/org/geysermc/connector/entity/living/monster/CreeperEntity.java b/connector/src/main/java/org/geysermc/connector/entity/living/monster/CreeperEntity.java index 3c3a76bd7..f4931861c 100644 --- a/connector/src/main/java/org/geysermc/connector/entity/living/monster/CreeperEntity.java +++ b/connector/src/main/java/org/geysermc/connector/entity/living/monster/CreeperEntity.java @@ -33,6 +33,12 @@ import org.geysermc.connector.network.session.GeyserSession; public class CreeperEntity extends MonsterEntity { + /** + * Whether the creeper has been ignited and is using ID 17. + * In this instance we ignore ID 15 since it's sending us -1 which confuses poor Bedrock. + */ + private boolean ignitedByFlintAndSteel = false; + public CreeperEntity(long entityId, long geyserId, EntityType entityType, Vector3f position, Vector3f motion, Vector3f rotation) { super(entityId, geyserId, entityType, position, motion, rotation); } @@ -40,13 +46,16 @@ public class CreeperEntity extends MonsterEntity { @Override public void updateBedrockMetadata(EntityMetadata entityMetadata, GeyserSession session) { if (entityMetadata.getId() == 15) { - metadata.getFlags().setFlag(EntityFlag.IGNITED, (int) entityMetadata.getValue() == 1); + if (!ignitedByFlintAndSteel) { + metadata.getFlags().setFlag(EntityFlag.IGNITED, (int) entityMetadata.getValue() == 1); + } } if (entityMetadata.getId() == 16) { metadata.getFlags().setFlag(EntityFlag.POWERED, (boolean) entityMetadata.getValue()); } if (entityMetadata.getId() == 17) { - metadata.getFlags().setFlag(EntityFlag.IGNITED, (boolean) entityMetadata.getValue()); + ignitedByFlintAndSteel = (boolean) entityMetadata.getValue(); + metadata.getFlags().setFlag(EntityFlag.IGNITED, ignitedByFlintAndSteel); } super.updateBedrockMetadata(entityMetadata, session); diff --git a/connector/src/main/java/org/geysermc/connector/entity/living/monster/GhastEntity.java b/connector/src/main/java/org/geysermc/connector/entity/living/monster/GhastEntity.java new file mode 100644 index 000000000..3d3be87ce --- /dev/null +++ b/connector/src/main/java/org/geysermc/connector/entity/living/monster/GhastEntity.java @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2019-2020 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.connector.entity.living.monster; + +import com.github.steveice10.mc.protocol.data.game.entity.metadata.EntityMetadata; +import com.nukkitx.math.vector.Vector3f; +import com.nukkitx.protocol.bedrock.data.entity.EntityData; +import org.geysermc.connector.entity.living.FlyingEntity; +import org.geysermc.connector.entity.type.EntityType; +import org.geysermc.connector.network.session.GeyserSession; + +public class GhastEntity extends FlyingEntity { + + public GhastEntity(long entityId, long geyserId, EntityType entityType, Vector3f position, Vector3f motion, Vector3f rotation) { + super(entityId, geyserId, entityType, position, motion, rotation); + } + + @Override + public void updateBedrockMetadata(EntityMetadata entityMetadata, GeyserSession session) { + if (entityMetadata.getId() == 15) { + // If the ghast is attacking + metadata.put(EntityData.CHARGE_AMOUNT, (byte) ((boolean) entityMetadata.getValue() ? 1 : 0)); + } + super.updateBedrockMetadata(entityMetadata, session); + } +} diff --git a/connector/src/main/java/org/geysermc/connector/entity/living/monster/PiglinEntity.java b/connector/src/main/java/org/geysermc/connector/entity/living/monster/PiglinEntity.java index 7b0d71e17..e0b443d3b 100644 --- a/connector/src/main/java/org/geysermc/connector/entity/living/monster/PiglinEntity.java +++ b/connector/src/main/java/org/geysermc/connector/entity/living/monster/PiglinEntity.java @@ -41,7 +41,7 @@ public class PiglinEntity extends BasePiglinEntity { @Override public void updateBedrockMetadata(EntityMetadata entityMetadata, GeyserSession session) { - if (entityMetadata.getId() == 15) { + if (entityMetadata.getId() == 16) { boolean isBaby = (boolean) entityMetadata.getValue(); if (isBaby) { metadata.put(EntityData.SCALE, .55f); diff --git a/connector/src/main/java/org/geysermc/connector/entity/living/monster/VexEntity.java b/connector/src/main/java/org/geysermc/connector/entity/living/monster/VexEntity.java new file mode 100644 index 000000000..70e413298 --- /dev/null +++ b/connector/src/main/java/org/geysermc/connector/entity/living/monster/VexEntity.java @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2019-2020 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.connector.entity.living.monster; + +import com.github.steveice10.mc.protocol.data.game.entity.metadata.EntityMetadata; +import com.nukkitx.math.vector.Vector3f; +import com.nukkitx.protocol.bedrock.data.entity.EntityData; +import org.geysermc.connector.entity.type.EntityType; +import org.geysermc.connector.network.session.GeyserSession; + +public class VexEntity extends MonsterEntity { + + public VexEntity(long entityId, long geyserId, EntityType entityType, Vector3f position, Vector3f motion, Vector3f rotation) { + super(entityId, geyserId, entityType, position, motion, rotation); + } + + @Override + public void updateBedrockMetadata(EntityMetadata entityMetadata, GeyserSession session) { + if (entityMetadata.getId() == 15) { + byte xd = (byte) entityMetadata.getValue(); + // Set the target to the player to force the attack animation + // even if the player isn't the target as we dont get the target on Java + metadata.put(EntityData.TARGET_EID, (xd & 0x01) == 0x01 ? session.getPlayerEntity().getGeyserId() : 0); + } + super.updateBedrockMetadata(entityMetadata, session); + } +} diff --git a/connector/src/main/java/org/geysermc/connector/entity/living/monster/ZombieVillagerEntity.java b/connector/src/main/java/org/geysermc/connector/entity/living/monster/ZombieVillagerEntity.java new file mode 100644 index 000000000..b8a628170 --- /dev/null +++ b/connector/src/main/java/org/geysermc/connector/entity/living/monster/ZombieVillagerEntity.java @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2019-2020 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.connector.entity.living.monster; + +import com.github.steveice10.mc.protocol.data.game.entity.metadata.EntityMetadata; +import com.github.steveice10.mc.protocol.data.game.entity.metadata.VillagerData; +import com.nukkitx.math.vector.Vector3f; +import com.nukkitx.protocol.bedrock.data.entity.EntityData; +import com.nukkitx.protocol.bedrock.data.entity.EntityFlag; +import org.geysermc.connector.entity.living.merchant.VillagerEntity; +import org.geysermc.connector.entity.type.EntityType; +import org.geysermc.connector.network.session.GeyserSession; + +public class ZombieVillagerEntity extends ZombieEntity { + + public ZombieVillagerEntity(long entityId, long geyserId, EntityType entityType, Vector3f position, Vector3f motion, Vector3f rotation) { + super(entityId, geyserId, entityType, position, motion, rotation); + } + + @Override + public void updateBedrockMetadata(EntityMetadata entityMetadata, GeyserSession session) { + if (entityMetadata.getId() == 18) { + metadata.getFlags().setFlag(EntityFlag.IS_TRANSFORMING, (boolean) entityMetadata.getValue()); + metadata.getFlags().setFlag(EntityFlag.SHAKING, (boolean) entityMetadata.getValue()); + } + if (entityMetadata.getId() == 19) { + VillagerData villagerData = (VillagerData) entityMetadata.getValue(); + // Region - only one used on Bedrock + metadata.put(EntityData.MARK_VARIANT, VillagerEntity.VILLAGER_REGIONS.get(villagerData.getType())); + } + super.updateBedrockMetadata(entityMetadata, session); + } +} diff --git a/connector/src/main/java/org/geysermc/connector/entity/type/EntityType.java b/connector/src/main/java/org/geysermc/connector/entity/type/EntityType.java index fddab5a42..05447760d 100644 --- a/connector/src/main/java/org/geysermc/connector/entity/type/EntityType.java +++ b/connector/src/main/java/org/geysermc/connector/entity/type/EntityType.java @@ -47,12 +47,12 @@ public enum EntityType { SHEEP(SheepEntity.class, 13, 1.3f, 0.9f), WOLF(WolfEntity.class, 14, 0.85f, 0.6f), VILLAGER(VillagerEntity.class, 15, 1.8f, 0.6f, 0.6f, 1.62f, "minecraft:villager_v2"), - MOOSHROOM(AnimalEntity.class, 16, 1.4f, 0.9f), + MOOSHROOM(MooshroomEntity.class, 16, 1.4f, 0.9f), SQUID(SquidEntity.class, 17, 0.8f), RABBIT(RabbitEntity.class, 18, 0.5f, 0.4f), - BAT(AmbientEntity.class, 19, 0.9f, 0.5f), + BAT(BatEntity.class, 19, 0.9f, 0.5f), IRON_GOLEM(GolemEntity.class, 20, 2.7f, 1.4f), - SNOW_GOLEM(GolemEntity.class, 21, 1.9f, 0.7f), + SNOW_GOLEM(SnowGolemEntity.class, 21, 1.9f, 0.7f), OCELOT(OcelotEntity.class, 22, 0.35f, 0.3f), HORSE(HorseEntity.class, 23, 1.6f, 1.3965f), DONKEY(ChestedHorseEntity.class, 24, 1.6f, 1.3965f), @@ -74,10 +74,10 @@ public enum EntityType { ENDERMAN(EndermanEntity.class, 38, 2.9f, 0.6f), SILVERFISH(MonsterEntity.class, 39, 0.3f, 0.4f), CAVE_SPIDER(MonsterEntity.class, 40, 0.5f, 0.7f), - GHAST(FlyingEntity.class, 41, 4.0f), + GHAST(GhastEntity.class, 41, 4.0f), MAGMA_CUBE(MagmaCubeEntity.class, 42, 0.51f), BLAZE(BlazeEntity.class, 43, 1.8f, 0.6f), - ZOMBIE_VILLAGER(ZombieEntity.class, 44, 1.8f, 0.6f, 0.6f, 1.62f), + ZOMBIE_VILLAGER(ZombieVillagerEntity.class, 44, 1.8f, 0.6f, 0.6f, 1.62f, "minecraft:zombie_villager_v2"), WITCH(RaidParticipantEntity.class, 45, 1.8f, 0.6f, 0.6f, 1.62f), STRAY(AbstractSkeletonEntity.class, 46, 1.8f, 0.6f, 0.6f, 1.62f), HUSK(ZombieEntity.class, 47, 1.8f, 0.6f, 0.6f, 1.62f), @@ -109,7 +109,7 @@ public enum EntityType { END_CRYSTAL(EnderCrystalEntity.class, 71, 2.0f, 2.0f, 2.0f, 0f, "minecraft:ender_crystal"), FIREWORK_ROCKET(FireworkEntity.class, 72, 0.25f, 0.25f, 0.25f, 0f, "minecraft:fireworks_rocket"), TRIDENT(TridentEntity.class, 73, 0f, 0f, 0f, 0f, "minecraft:thrown_trident"), - TURTLE(AnimalEntity.class, 74, 0.4f, 1.2f), + TURTLE(TurtleEntity.class, 74, 0.4f, 1.2f), CAT(CatEntity.class, 75, 0.35f, 0.3f), SHULKER_BULLET(Entity.class, 76, 0.3125f), FISHING_BOBBER(FishingHookEntity.class, 77, 0f, 0f, 0f, 0f, "minecraft:fishing_hook"), @@ -125,9 +125,9 @@ public enum EntityType { THROWN_POTION(ThrowableEntity.class, 86, 0.25f, 0.25f, 0.25f, 0f, "minecraft:splash_potion"), THROWN_ENDERPEARL(ThrowableEntity.class, 87, 0.25f, 0.25f, 0.25f, 0f, "minecraft:ender_pearl"), LEASH_KNOT(LeashKnotEntity.class, 88, 0.5f, 0.375f), - WITHER_SKULL(Entity.class, 89, 0.3125f), + WITHER_SKULL(WitherSkullEntity.class, 89, 0.3125f), BOAT(BoatEntity.class, 90, 0.7f, 1.6f, 1.6f, 0.35f), - WITHER_SKULL_DANGEROUS(Entity.class, 91, 0f), + WITHER_SKULL_DANGEROUS(WitherSkullEntity.class, 91, 0f), LIGHTNING_BOLT(Entity.class, 93, 0f), SMALL_FIREBALL(ItemedFireballEntity.class, 94, 0.3125f), AREA_EFFECT_CLOUD(AreaEffectCloudEntity.class, 95, 0.5f, 1.0f), @@ -141,7 +141,7 @@ public enum EntityType { LLAMA_SPIT(Entity.class, 102, 0.25f), EVOKER_FANGS(Entity.class, 103, 0.8f, 0.5f, 0.5f, 0f, "minecraft:evocation_fang"), EVOKER(SpellcasterIllagerEntity.class, 104, 1.95f, 0.6f, 0.6f, 0f, "minecraft:evocation_illager"), - VEX(MonsterEntity.class, 105, 0.8f, 0.4f), + VEX(VexEntity.class, 105, 0.8f, 0.4f), ICE_BOMB(Entity.class, 106, 0f), BALLOON(Entity.class, 107, 0f), //TODO PUFFERFISH(PufferFishEntity.class, 108, 0.7f, 0.7f), @@ -153,7 +153,7 @@ public enum EntityType { FOX(FoxEntity.class, 121, 0.5f, 1.25f), BEE(BeeEntity.class, 122, 0.6f, 0.6f), STRIDER(StriderEntity.class, 125, 1.7f, 0.9f, 0f, 0f, "minecraft:strider"), - HOGLIN(AnimalEntity.class, 124, 1.4f, 1.3965f, 1.3965f, 0f, "minecraft:hoglin"), + HOGLIN(HoglinEntity.class, 124, 1.4f, 1.3965f, 1.3965f, 0f, "minecraft:hoglin"), ZOGLIN(ZoglinEntity.class, 126, 1.4f, 1.3965f, 1.3965f, 0f, "minecraft:zoglin"), PIGLIN(PiglinEntity.class, 123, 1.95f, 0.6f, 0.6f, 0f, "minecraft:piglin"), PIGLIN_BRUTE(BasePiglinEntity.class, 127, 1.95f, 0.6f, 0.6f, 0f, "minecraft:piglin_brute"), diff --git a/connector/src/main/java/org/geysermc/connector/network/BedrockProtocol.java b/connector/src/main/java/org/geysermc/connector/network/BedrockProtocol.java index 5d4462b45..85043d378 100644 --- a/connector/src/main/java/org/geysermc/connector/network/BedrockProtocol.java +++ b/connector/src/main/java/org/geysermc/connector/network/BedrockProtocol.java @@ -26,8 +26,7 @@ package org.geysermc.connector.network; import com.nukkitx.protocol.bedrock.BedrockPacketCodec; -import com.nukkitx.protocol.bedrock.v407.Bedrock_v407; -import com.nukkitx.protocol.bedrock.v408.Bedrock_v408; +import com.nukkitx.protocol.bedrock.v419.Bedrock_v419; import java.util.ArrayList; import java.util.List; @@ -40,14 +39,13 @@ public class BedrockProtocol { * Default Bedrock codec that should act as a fallback. Should represent the latest available * release of the game that Geyser supports. */ - public static final BedrockPacketCodec DEFAULT_BEDROCK_CODEC = Bedrock_v408.V408_CODEC; + public static final BedrockPacketCodec DEFAULT_BEDROCK_CODEC = Bedrock_v419.V419_CODEC; /** * A list of all supported Bedrock versions that can join Geyser */ public static final List SUPPORTED_BEDROCK_CODECS = new ArrayList<>(); static { - SUPPORTED_BEDROCK_CODECS.add(Bedrock_v407.V407_CODEC); SUPPORTED_BEDROCK_CODECS.add(DEFAULT_BEDROCK_CODEC); } diff --git a/connector/src/main/java/org/geysermc/connector/network/ConnectorServerEventHandler.java b/connector/src/main/java/org/geysermc/connector/network/ConnectorServerEventHandler.java index 9fb4ad9e1..150d298c7 100644 --- a/connector/src/main/java/org/geysermc/connector/network/ConnectorServerEventHandler.java +++ b/connector/src/main/java/org/geysermc/connector/network/ConnectorServerEventHandler.java @@ -25,7 +25,6 @@ package org.geysermc.connector.network; -import com.github.steveice10.mc.protocol.data.message.MessageSerializer; import com.nukkitx.protocol.bedrock.BedrockPong; import com.nukkitx.protocol.bedrock.BedrockServerEventHandler; import com.nukkitx.protocol.bedrock.BedrockServerSession; @@ -36,7 +35,7 @@ import org.geysermc.connector.GeyserConnector; import org.geysermc.connector.configuration.GeyserConfiguration; import org.geysermc.connector.network.session.GeyserSession; import org.geysermc.connector.ping.IGeyserPingPassthrough; -import org.geysermc.connector.utils.MessageUtils; +import org.geysermc.connector.network.translators.chat.MessageTranslator; import org.geysermc.connector.utils.LanguageUtils; import java.net.InetSocketAddress; @@ -76,7 +75,7 @@ public class ConnectorServerEventHandler implements BedrockServerEventHandler { pong.setIpv4Port(config.getBedrock().getPort()); if (config.isPassthroughMotd() && pingInfo != null && pingInfo.getDescription() != null) { - String[] motd = MessageUtils.getBedrockMessage(MessageSerializer.fromString(pingInfo.getDescription())).split("\n"); + String[] motd = MessageTranslator.convertMessageLenient(pingInfo.getDescription()).split("\n"); String mainMotd = motd[0]; // First line of the motd. String subMotd = (motd.length != 1) ? motd[1] : ""; // Second line of the motd if present, otherwise blank. diff --git a/connector/src/main/java/org/geysermc/connector/network/QueryPacketHandler.java b/connector/src/main/java/org/geysermc/connector/network/QueryPacketHandler.java index 7faf36bdd..510bba2d2 100644 --- a/connector/src/main/java/org/geysermc/connector/network/QueryPacketHandler.java +++ b/connector/src/main/java/org/geysermc/connector/network/QueryPacketHandler.java @@ -25,12 +25,11 @@ package org.geysermc.connector.network; -import com.github.steveice10.mc.protocol.data.message.MessageSerializer; import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBufAllocator; import org.geysermc.connector.common.ping.GeyserPingInfo; import org.geysermc.connector.GeyserConnector; -import org.geysermc.connector.utils.MessageUtils; +import org.geysermc.connector.network.translators.chat.MessageTranslator; import java.io.ByteArrayOutputStream; import java.io.IOException; @@ -148,7 +147,7 @@ public class QueryPacketHandler { } if (connector.getConfig().isPassthroughMotd() && pingInfo != null) { - String[] javaMotd = MessageUtils.getBedrockMessage(MessageSerializer.fromString(pingInfo.getDescription())).split("\n"); + String[] javaMotd = MessageTranslator.convertMessageLenient(pingInfo.getDescription()).split("\n"); motd = javaMotd[0].trim(); // First line of the motd. } else { motd = connector.getConfig().getBedrock().getMotd1(); diff --git a/connector/src/main/java/org/geysermc/connector/network/UpstreamPacketHandler.java b/connector/src/main/java/org/geysermc/connector/network/UpstreamPacketHandler.java index 20d2bb9e7..4ff6fcba6 100644 --- a/connector/src/main/java/org/geysermc/connector/network/UpstreamPacketHandler.java +++ b/connector/src/main/java/org/geysermc/connector/network/UpstreamPacketHandler.java @@ -59,9 +59,11 @@ public class UpstreamPacketHandler extends LoggingPacketHandler { if (packetCodec == null) { if (loginPacket.getProtocolVersion() > BedrockProtocol.DEFAULT_BEDROCK_CODEC.getProtocolVersion()) { // Too early to determine session locale + session.getConnector().getLogger().info(LanguageUtils.getLocaleStringLog("geyser.network.outdated.server", BedrockProtocol.DEFAULT_BEDROCK_CODEC.getMinecraftVersion())); session.disconnect(LanguageUtils.getLocaleStringLog("geyser.network.outdated.server", BedrockProtocol.DEFAULT_BEDROCK_CODEC.getMinecraftVersion())); return true; } else if (loginPacket.getProtocolVersion() < BedrockProtocol.DEFAULT_BEDROCK_CODEC.getProtocolVersion()) { + session.getConnector().getLogger().info(LanguageUtils.getLocaleStringLog("geyser.network.outdated.client", BedrockProtocol.DEFAULT_BEDROCK_CODEC.getMinecraftVersion())); session.disconnect(LanguageUtils.getLocaleStringLog("geyser.network.outdated.client", BedrockProtocol.DEFAULT_BEDROCK_CODEC.getMinecraftVersion())); return true; } diff --git a/connector/src/main/java/org/geysermc/connector/network/session/GeyserSession.java b/connector/src/main/java/org/geysermc/connector/network/session/GeyserSession.java index 99aada2a5..d3bb2436f 100644 --- a/connector/src/main/java/org/geysermc/connector/network/session/GeyserSession.java +++ b/connector/src/main/java/org/geysermc/connector/network/session/GeyserSession.java @@ -34,7 +34,6 @@ import com.github.steveice10.mc.protocol.data.SubProtocol; import com.github.steveice10.mc.protocol.data.game.entity.player.GameMode; import com.github.steveice10.mc.protocol.data.game.statistic.Statistic; import com.github.steveice10.mc.protocol.data.game.window.VillagerTrade; -import com.github.steveice10.mc.protocol.data.message.MessageSerializer; import com.github.steveice10.mc.protocol.packet.handshake.client.HandshakePacket; import com.github.steveice10.mc.protocol.packet.ingame.client.world.ClientTeleportConfirmPacket; import com.github.steveice10.mc.protocol.packet.ingame.server.ServerRespawnPacket; @@ -65,6 +64,7 @@ import org.geysermc.connector.common.AuthType; import org.geysermc.connector.entity.Entity; import org.geysermc.connector.entity.PlayerEntity; import org.geysermc.connector.inventory.PlayerInventory; +import org.geysermc.connector.network.translators.chat.MessageTranslator; import org.geysermc.connector.network.remote.RemoteServer; import org.geysermc.connector.network.session.auth.AuthData; import org.geysermc.connector.network.session.auth.BedrockClientData; @@ -74,7 +74,6 @@ import org.geysermc.connector.network.translators.EntityIdentifierRegistry; import org.geysermc.connector.network.translators.PacketTranslatorRegistry; import org.geysermc.connector.network.translators.inventory.EnchantmentInventoryTranslator; import org.geysermc.connector.network.translators.item.ItemRegistry; -import org.geysermc.connector.network.translators.world.block.BlockTranslator; import org.geysermc.connector.utils.*; import org.geysermc.floodgate.crypto.FloodgateCipher; import org.geysermc.floodgate.util.BedrockData; @@ -142,6 +141,13 @@ public class GeyserSession implements CommandSender { @Setter private boolean jumping; + /** + * The dimension of the player. + * As all entities are in the same world, this can be safely applied to all other entities. + */ + @Setter + private String dimension = DimensionUtils.OVERWORLD; + @Setter private int breakingBlock; @@ -217,6 +223,12 @@ public class GeyserSession implements CommandSender { @Setter private ScheduledFuture bucketScheduledFuture; + /** + * Sends a movement packet every three seconds if the player hasn't moved. Prevents timeouts when AFK in certain instances. + */ + @Setter + private ScheduledFuture movementSendIfIdle; + private boolean reducedDebugInfo = false; /** @@ -471,7 +483,7 @@ public class GeyserSession implements CommandSender { event.getCause().printStackTrace(); } - upstream.disconnect(MessageUtils.getBedrockMessage(MessageSerializer.fromString(event.getReason()))); + upstream.disconnect(MessageTranslator.convertMessageLenient(event.getReason())); } @Override @@ -611,12 +623,12 @@ public class GeyserSession implements CommandSender { startGamePacket.setRotation(Vector2f.from(1, 1)); startGamePacket.setSeed(-1); - startGamePacket.setDimensionId(DimensionUtils.javaToBedrock(playerEntity.getDimension())); + startGamePacket.setDimensionId(DimensionUtils.javaToBedrock(dimension)); startGamePacket.setGeneratorId(1); startGamePacket.setLevelGameType(GameType.SURVIVAL); startGamePacket.setDifficulty(1); startGamePacket.setDefaultSpawn(Vector3i.ZERO); - startGamePacket.setAchievementsDisabled(true); + startGamePacket.setAchievementsDisabled(!connector.getConfig().isXboxAchievementsEnabled()); startGamePacket.setCurrentTick(-1); startGamePacket.setEduEditionOffers(0); startGamePacket.setEduFeaturesEnabled(false); @@ -627,7 +639,7 @@ public class GeyserSession implements CommandSender { startGamePacket.getGamerules().add(new GameRuleData<>("showcoordinates", true)); startGamePacket.setPlatformBroadcastMode(GamePublishSetting.PUBLIC); startGamePacket.setXblBroadcastMode(GamePublishSetting.PUBLIC); - startGamePacket.setCommandsEnabled(true); + startGamePacket.setCommandsEnabled(!connector.getConfig().isXboxAchievementsEnabled()); startGamePacket.setTexturePacksRequired(false); startGamePacket.setBonusChestEnabled(false); startGamePacket.setStartingWithMap(false); @@ -649,7 +661,6 @@ public class GeyserSession implements CommandSender { // startGamePacket.setCurrentTick(0); startGamePacket.setEnchantmentSeed(0); startGamePacket.setMultiplayerCorrelationId(""); - startGamePacket.setBlockPalette(BlockTranslator.BLOCKS); startGamePacket.setItemEntries(ItemRegistry.ITEMS); startGamePacket.setVanillaVersion("*"); startGamePacket.setAuthoritativeMovementMode(AuthoritativeMovementMode.CLIENT); diff --git a/connector/src/main/java/org/geysermc/connector/network/session/cache/BossBar.java b/connector/src/main/java/org/geysermc/connector/network/session/cache/BossBar.java index fdc609ab9..7eadb7942 100644 --- a/connector/src/main/java/org/geysermc/connector/network/session/cache/BossBar.java +++ b/connector/src/main/java/org/geysermc/connector/network/session/cache/BossBar.java @@ -33,7 +33,7 @@ import com.nukkitx.protocol.bedrock.packet.BossEventPacket; import com.nukkitx.protocol.bedrock.packet.RemoveEntityPacket; import lombok.AllArgsConstructor; import org.geysermc.connector.network.session.GeyserSession; -import org.geysermc.connector.utils.MessageUtils; +import org.geysermc.connector.network.translators.chat.MessageTranslator; @AllArgsConstructor public class BossBar { @@ -58,7 +58,7 @@ public class BossBar { BossEventPacket bossEventPacket = new BossEventPacket(); bossEventPacket.setBossUniqueEntityId(entityId); bossEventPacket.setAction(BossEventPacket.Action.CREATE); - bossEventPacket.setTitle(MessageUtils.getTranslatedBedrockMessage(title, session.getLocale())); + bossEventPacket.setTitle(MessageTranslator.convertMessage(title.toString(), session.getLocale())); bossEventPacket.setHealthPercentage(health); bossEventPacket.setColor(color); //ignored by client bossEventPacket.setOverlay(overlay); @@ -72,7 +72,7 @@ public class BossBar { BossEventPacket bossEventPacket = new BossEventPacket(); bossEventPacket.setBossUniqueEntityId(entityId); bossEventPacket.setAction(BossEventPacket.Action.UPDATE_NAME); - bossEventPacket.setTitle(MessageUtils.getTranslatedBedrockMessage(title, session.getLocale())); + bossEventPacket.setTitle(MessageTranslator.convertMessage(title.toString(), session.getLocale())); session.sendUpstreamPacket(bossEventPacket); } diff --git a/connector/src/main/java/org/geysermc/connector/network/session/cache/ChunkCache.java b/connector/src/main/java/org/geysermc/connector/network/session/cache/ChunkCache.java index 7bf84b8db..cbf3721f7 100644 --- a/connector/src/main/java/org/geysermc/connector/network/session/cache/ChunkCache.java +++ b/connector/src/main/java/org/geysermc/connector/network/session/cache/ChunkCache.java @@ -94,12 +94,12 @@ public class ChunkCache { public int getBlockAt(int x, int y, int z) { if (!cache) { - return BlockTranslator.AIR; + return BlockTranslator.JAVA_AIR_ID; } Column column = this.getChunk(x >> 4, z >> 4); if (column == null) { - return BlockTranslator.AIR; + return BlockTranslator.JAVA_AIR_ID; } Chunk chunk = column.getChunks()[y >> 4]; @@ -107,7 +107,7 @@ public class ChunkCache { return chunk.get(x & 0xF, y & 0xF, z & 0xF); } - return BlockTranslator.AIR; + return BlockTranslator.JAVA_AIR_ID; } public void removeChunk(int chunkX, int chunkZ) { diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockBlockPickRequestTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockBlockPickRequestTranslator.java index 023a83afe..3e40ddd6f 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockBlockPickRequestTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockBlockPickRequestTranslator.java @@ -42,7 +42,7 @@ public class BedrockBlockPickRequestTranslator extends PacketTranslator { @@ -48,7 +48,7 @@ public class BedrockCommandRequestTranslator extends PacketTranslator { @@ -40,7 +40,7 @@ public class BedrockTextTranslator extends PacketTranslator { public void translate(TextPacket packet, GeyserSession session) { String message = packet.getMessage().replaceAll("^\\.", "/").trim(); - if (MessageUtils.isTooLong(message, session)) { + if (MessageTranslator.isTooLong(message, session)) { return; } diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/entity/player/BedrockInteractTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/entity/player/BedrockInteractTranslator.java index c5d6f2dda..6c03cd033 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/entity/player/BedrockInteractTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/entity/player/BedrockInteractTranslator.java @@ -25,27 +25,65 @@ package org.geysermc.connector.network.translators.bedrock.entity.player; -import com.nukkitx.protocol.bedrock.data.entity.EntityData; -import com.nukkitx.protocol.bedrock.data.entity.EntityDataMap; -import com.nukkitx.protocol.bedrock.data.entity.EntityFlag; -import com.nukkitx.protocol.bedrock.data.inventory.ContainerType; -import com.nukkitx.protocol.bedrock.packet.ContainerOpenPacket; -import org.geysermc.connector.entity.Entity; -import org.geysermc.connector.network.session.GeyserSession; -import org.geysermc.connector.network.translators.PacketTranslator; -import org.geysermc.connector.network.translators.Translator; - import com.github.steveice10.mc.protocol.data.game.entity.player.Hand; import com.github.steveice10.mc.protocol.data.game.entity.player.InteractAction; import com.github.steveice10.mc.protocol.data.game.entity.player.PlayerState; import com.github.steveice10.mc.protocol.packet.ingame.client.player.ClientPlayerInteractEntityPacket; import com.github.steveice10.mc.protocol.packet.ingame.client.player.ClientPlayerStatePacket; +import com.nukkitx.protocol.bedrock.data.entity.EntityData; +import com.nukkitx.protocol.bedrock.data.entity.EntityDataMap; +import com.nukkitx.protocol.bedrock.data.entity.EntityFlag; +import com.nukkitx.protocol.bedrock.data.inventory.ContainerType; +import com.nukkitx.protocol.bedrock.packet.ContainerOpenPacket; import com.nukkitx.protocol.bedrock.packet.InteractPacket; +import lombok.Getter; +import org.geysermc.connector.entity.Entity; +import org.geysermc.connector.entity.type.EntityType; +import org.geysermc.connector.network.session.GeyserSession; +import org.geysermc.connector.network.translators.PacketTranslator; +import org.geysermc.connector.network.translators.Translator; +import org.geysermc.connector.network.translators.item.ItemEntry; import org.geysermc.connector.network.translators.item.ItemRegistry; +import java.util.Arrays; +import java.util.List; + @Translator(packet = InteractPacket.class) public class BedrockInteractTranslator extends PacketTranslator { + /** + * A list of all foods a horse/donkey can eat on Java Edition. + * Used to display interactive tag if needed. + */ + private static final List DONKEY_AND_HORSE_FOODS = Arrays.asList("golden_apple", "enchanted_golden_apple", + "golden_carrot", "sugar", "apple", "wheat", "hay_block"); + + /** + * A list of all flowers. Used for feeding bees. + */ + private static final List FLOWERS = Arrays.asList("dandelion", "poppy", "blue_orchid", "allium", "azure_bluet", + "red_tulip", "pink_tulip", "white_tulip", "orange_tulip", "cornflower", "lily_of_the_valley", "wither_rose", + "sunflower", "lilac", "rose_bush", "peony"); + + /** + * All entity types that can be leashed on Java Edition + */ + private static final List LEASHABLE_MOB_TYPES = Arrays.asList(EntityType.BEE, EntityType.CAT, EntityType.CHICKEN, + EntityType.COW, EntityType.DOLPHIN, EntityType.DONKEY, EntityType.FOX, EntityType.HOGLIN, EntityType.HORSE, EntityType.SKELETON_HORSE, + EntityType.ZOMBIE_HORSE, EntityType.IRON_GOLEM, EntityType.LLAMA, EntityType.TRADER_LLAMA, EntityType.MOOSHROOM, + EntityType.MULE, EntityType.OCELOT, EntityType.PARROT, EntityType.PIG, EntityType.POLAR_BEAR, EntityType.RABBIT, + EntityType.SHEEP, EntityType.SNOW_GOLEM, EntityType.STRIDER, EntityType.WOLF, EntityType.ZOGLIN); + + private static final List SADDLEABLE_WHEN_TAMED_MOB_TYPES = Arrays.asList(EntityType.DONKEY, EntityType.HORSE, + EntityType.ZOMBIE_HORSE, EntityType.MULE); + /** + * A list of all foods a wolf can eat on Java Edition. + * Used to display interactive tag if needed. + */ + private static final List WOLF_FOODS = Arrays.asList("pufferfish", "tropical_fish", "chicken", "cooked_chicken", + "porkchop", "beef", "rabbit", "cooked_porkchop", "cooked_beef", "rotten_flesh", "mutton", "cooked_mutton", + "cooked_rabbit"); + @Override public void translate(InteractPacket packet, GeyserSession session) { Entity entity; @@ -84,50 +122,232 @@ public class BedrockInteractTranslator extends PacketTranslator if (interactEntity == null) return; EntityDataMap entityMetadata = interactEntity.getMetadata(); + ItemEntry itemEntry = session.getInventory().getItemInHand() == null ? ItemEntry.AIR : ItemRegistry.getItem(session.getInventory().getItemInHand()); + String javaIdentifierStripped = itemEntry.getJavaIdentifier().replace("minecraft:", ""); - String interactiveTag; - switch (interactEntity.getEntityType()) { - case BOAT: - interactiveTag = "action.interact.ride.boat"; - break; - case DONKEY: - case HORSE: - case LLAMA: - case MULE: - case SKELETON_HORSE: - case TRADER_LLAMA: - case ZOMBIE_HORSE: - if (entityMetadata.getFlags().getFlag(EntityFlag.TAMED)) { - interactiveTag = "action.interact.ride.horse"; - } else { - interactiveTag = "action.interact.mount"; - } - break; - case MINECART: - interactiveTag = "action.interact.ride.minecart"; - break; - case PIG: - if (entityMetadata.getFlags().getFlag(EntityFlag.SADDLED)) { - interactiveTag = "action.interact.mount"; - } else interactiveTag = ""; - break; - case VILLAGER: - if (entityMetadata.getInt(EntityData.VARIANT) != 14 && entityMetadata.getInt(EntityData.VARIANT) != 0 - && entityMetadata.getFloat(EntityData.SCALE) >= 0.75f) { // Not a nitwit, has a profession and is not a baby - interactiveTag = "action.interact.trade"; - } else interactiveTag = ""; - break; - case WANDERING_TRADER: - interactiveTag = "action.interact.trade"; // Since you can always trade with a wandering villager, presumably. - break; - default: - return; // No need to process any further since there is no interactive tag + // TODO - in the future, update these in the metadata? So the client doesn't have to wiggle their cursor around for it to happen + // TODO - also, might be good to abstract out the eating thing. I know there will need to be food tracked for https://github.com/GeyserMC/Geyser/issues/1005 but not all food is breeding food + InteractiveTag interactiveTag = InteractiveTag.NONE; + if (entityMetadata.getLong(EntityData.LEASH_HOLDER_EID) == session.getPlayerEntity().getGeyserId()) { + // Unleash the entity + interactiveTag = InteractiveTag.REMOVE_LEASH; + } else if (javaIdentifierStripped.equals("saddle") && !entityMetadata.getFlags().getFlag(EntityFlag.SADDLED) && + ((SADDLEABLE_WHEN_TAMED_MOB_TYPES.contains(interactEntity.getEntityType()) && entityMetadata.getFlags().getFlag(EntityFlag.TAMED)) || + interactEntity.getEntityType() == EntityType.PIG || interactEntity.getEntityType() == EntityType.STRIDER)) { + // Entity can be saddled and the conditions meet (entity can be saddled and, if needed, is tamed) + interactiveTag = InteractiveTag.SADDLE; + } else if (javaIdentifierStripped.equals("name_tag") && session.getInventory().getItemInHand().getNbt() != null && + session.getInventory().getItemInHand().getNbt().contains("display")) { + // Holding a named name tag + interactiveTag = InteractiveTag.NAME; + } else if (javaIdentifierStripped.equals("lead") && LEASHABLE_MOB_TYPES.contains(interactEntity.getEntityType()) && + entityMetadata.getLong(EntityData.LEASH_HOLDER_EID) == -1L) { + // Holding a leash and the mob is leashable for sure + // (Plugins can change this behavior so that's something to look into in the far far future) + interactiveTag = InteractiveTag.LEASH; + } else { + switch (interactEntity.getEntityType()) { + case BEE: + if (FLOWERS.contains(javaIdentifierStripped)) { + interactiveTag = InteractiveTag.FEED; + } + break; + case BOAT: + interactiveTag = InteractiveTag.BOARD_BOAT; + break; + case CAT: + if (javaIdentifierStripped.equals("cod") || javaIdentifierStripped.equals("salmon")) { + interactiveTag = InteractiveTag.FEED; + } else if (entityMetadata.getFlags().getFlag(EntityFlag.TAMED) && + entityMetadata.getLong(EntityData.OWNER_EID) == session.getPlayerEntity().getGeyserId()) { + // Tamed and owned by player - can sit/stand + interactiveTag = entityMetadata.getFlags().getFlag(EntityFlag.SITTING) ? InteractiveTag.STAND : InteractiveTag.SIT; + break; + } + break; + case CHICKEN: + if (javaIdentifierStripped.contains("seeds")) { + interactiveTag = InteractiveTag.FEED; + } + break; + case MOOSHROOM: + // Shear the mooshroom + if (javaIdentifierStripped.equals("shears")) { + interactiveTag = InteractiveTag.MOOSHROOM_SHEAR; + break; + } + // Bowls are acceptable here + else if (javaIdentifierStripped.equals("bowl")) { + interactiveTag = InteractiveTag.MOOSHROOM_MILK_STEW; + break; + } + // Fall down to COW as this works on mooshrooms + case COW: + if (javaIdentifierStripped.equals("wheat")) { + interactiveTag = InteractiveTag.FEED; + } else if (javaIdentifierStripped.equals("bucket")) { + // Milk the cow + interactiveTag = InteractiveTag.MILK; + } + break; + case CREEPER: + if (javaIdentifierStripped.equals("flint_and_steel")) { + // Today I learned that you can ignite a creeper with flint and steel! Huh. + interactiveTag = InteractiveTag.IGNITE_CREEPER; + } + break; + case DONKEY: + case LLAMA: + case MULE: + if (entityMetadata.getFlags().getFlag(EntityFlag.TAMED) && !entityMetadata.getFlags().getFlag(EntityFlag.CHESTED) + && javaIdentifierStripped.equals("chest")) { + // Can attach a chest + interactiveTag = InteractiveTag.ATTACH_CHEST; + break; + } + // Intentional fall-through + case HORSE: + case SKELETON_HORSE: + case TRADER_LLAMA: + case ZOMBIE_HORSE: + // have another switch statement as, while these share mount attributes they don't share food + switch (interactEntity.getEntityType()) { + case LLAMA: + case TRADER_LLAMA: + if (javaIdentifierStripped.equals("wheat") || javaIdentifierStripped.equals("hay_block")) { + interactiveTag = InteractiveTag.FEED; + break; + } + case DONKEY: + case HORSE: + // Undead can't eat + if (DONKEY_AND_HORSE_FOODS.contains(javaIdentifierStripped)) { + interactiveTag = InteractiveTag.FEED; + break; + } + } + if (!entityMetadata.getFlags().getFlag(EntityFlag.BABY)) { + // Can't ride a baby + if (entityMetadata.getFlags().getFlag(EntityFlag.TAMED)) { + interactiveTag = InteractiveTag.RIDE_HORSE; + } else if (!entityMetadata.getFlags().getFlag(EntityFlag.TAMED) && itemEntry.equals(ItemEntry.AIR)) { + // Can't hide an untamed entity without having your hand empty + interactiveTag = InteractiveTag.MOUNT; + } + } + break; + case FOX: + if (javaIdentifierStripped.equals("sweet_berries")) { + interactiveTag = InteractiveTag.FEED; + } + break; + case HOGLIN: + if (javaIdentifierStripped.equals("crimson_fungus")) { + interactiveTag = InteractiveTag.FEED; + } + break; + case MINECART: + interactiveTag = InteractiveTag.RIDE_MINECART; + break; + case MINECART_CHEST: + case MINECART_COMMAND_BLOCK: + case MINECART_HOPPER: + interactiveTag = InteractiveTag.OPEN_CONTAINER; + break; + case OCELOT: + if (javaIdentifierStripped.equals("cod") || javaIdentifierStripped.equals("salmon")) { + interactiveTag = InteractiveTag.FEED; + } + break; + case PANDA: + if (javaIdentifierStripped.equals("bamboo")) { + interactiveTag = InteractiveTag.FEED; + } + break; + case PARROT: + if (javaIdentifierStripped.contains("seeds") || javaIdentifierStripped.equals("cookie")) { + interactiveTag = InteractiveTag.FEED; + } + break; + case PIG: + if (javaIdentifierStripped.equals("carrot") || javaIdentifierStripped.equals("potato") || javaIdentifierStripped.equals("beetroot")) { + interactiveTag = InteractiveTag.FEED; + } else if (entityMetadata.getFlags().getFlag(EntityFlag.SADDLED)) { + interactiveTag = InteractiveTag.MOUNT; + } + break; + case PIGLIN: + if (!entityMetadata.getFlags().getFlag(EntityFlag.BABY) && javaIdentifierStripped.equals("gold_ingot")) { + interactiveTag = InteractiveTag.BARTER; + } + break; + case RABBIT: + if (javaIdentifierStripped.equals("dandelion") || javaIdentifierStripped.equals("carrot") || javaIdentifierStripped.equals("golden_carrot")) { + interactiveTag = InteractiveTag.FEED; + } + break; + case SHEEP: + if (javaIdentifierStripped.equals("wheat")) { + interactiveTag = InteractiveTag.FEED; + } else if (!entityMetadata.getFlags().getFlag(EntityFlag.SHEARED)) { + if (javaIdentifierStripped.equals("shears")) { + // Shear the sheep + interactiveTag = InteractiveTag.SHEAR; + } else if (javaIdentifierStripped.contains("_dye")) { + // Dye the sheep + interactiveTag = InteractiveTag.DYE; + } + } + break; + case STRIDER: + if (javaIdentifierStripped.equals("warped_fungus")) { + interactiveTag = InteractiveTag.FEED; + } else if (entityMetadata.getFlags().getFlag(EntityFlag.SADDLED)) { + interactiveTag = InteractiveTag.RIDE_STRIDER; + } + break; + case TURTLE: + if (javaIdentifierStripped.equals("seagrass")) { + interactiveTag = InteractiveTag.FEED; + } + break; + case VILLAGER: + if (entityMetadata.getInt(EntityData.VARIANT) != 14 && entityMetadata.getInt(EntityData.VARIANT) != 0 + && entityMetadata.getFloat(EntityData.SCALE) >= 0.75f) { // Not a nitwit, has a profession and is not a baby + interactiveTag = InteractiveTag.TRADE; + } + break; + case WANDERING_TRADER: + interactiveTag = InteractiveTag.TRADE; // Since you can always trade with a wandering villager, presumably. + break; + case WOLF: + if (javaIdentifierStripped.equals("bone") && !entityMetadata.getFlags().getFlag(EntityFlag.TAMED)) { + // Bone and untamed - can tame + interactiveTag = InteractiveTag.TAME; + } else if (WOLF_FOODS.contains(javaIdentifierStripped)) { + // Compatible food in hand - feed + // Sometimes just sits/stands when the wolf isn't hungry - there doesn't appear to be a way to fix this + interactiveTag = InteractiveTag.FEED; + } else if (entityMetadata.getFlags().getFlag(EntityFlag.TAMED) && + entityMetadata.getLong(EntityData.OWNER_EID) == session.getPlayerEntity().getGeyserId()) { + // Tamed and owned by player - can sit/stand + interactiveTag = entityMetadata.getFlags().getFlag(EntityFlag.SITTING) ? InteractiveTag.STAND : InteractiveTag.SIT; + } + break; + case ZOMBIE_VILLAGER: + // We can't guarantee the existence of the weakness effect so we just always show it. + if (javaIdentifierStripped.equals("golden_apple")) { + interactiveTag = InteractiveTag.CURE; + } + break; + default: + break; + } } - session.getPlayerEntity().getMetadata().put(EntityData.INTERACTIVE_TAG, interactiveTag); + session.getPlayerEntity().getMetadata().put(EntityData.INTERACTIVE_TAG, interactiveTag.getValue()); session.getPlayerEntity().updateBedrockMetadata(session); } else { - if (!(session.getPlayerEntity().getMetadata().get(EntityData.INTERACTIVE_TAG) == null) || - !(session.getPlayerEntity().getMetadata().get(EntityData.INTERACTIVE_TAG) == "")) { + if (!session.getPlayerEntity().getMetadata().getString(EntityData.INTERACTIVE_TAG).isEmpty()) { // No interactive tag should be sent session.getPlayerEntity().getMetadata().remove(EntityData.INTERACTIVE_TAG); session.getPlayerEntity().updateBedrockMetadata(session); @@ -147,4 +367,65 @@ public class BedrockInteractTranslator extends PacketTranslator break; } } + + /** + * All interactive tags in enum form. For potential API usage. + */ + public enum InteractiveTag { + NONE(true), + IGNITE_CREEPER("creeper"), + EDIT, + LEAVE_BOAT("exit.boat"), + FEED, + FISH("fishing"), + MILK, + MOOSHROOM_SHEAR("mooshear"), + MOOSHROOM_MILK_STEW("moostew"), + BOARD_BOAT("ride.boat"), + RIDE_MINECART("ride.minecart"), + RIDE_HORSE("ride.horse"), + RIDE_STRIDER("ride.strider"), + SHEAR, + SIT, + STAND, + TALK, + TAME, + DYE, + CURE, + OPEN_CONTAINER("opencontainer"), + CREATE_MAP("createMap"), + TAKE_PICTURE("takepicture"), + SADDLE, + MOUNT, + BOOST, + WRITE, + LEASH, + REMOVE_LEASH("unleash"), + NAME, + ATTACH_CHEST("attachchest"), + TRADE, + POSE_ARMOR_STAND("armorstand.pose"), + EQUIP_ARMOR_STAND("armorstand.equip"), + READ, + WAKE_VILLAGER("wakevillager"), + BARTER; + + /** + * The full string that should be passed on to the client. + */ + @Getter + private final String value; + + InteractiveTag(boolean isNone) { + this.value = ""; + } + + InteractiveTag(String value) { + this.value = "action.interact." + value; + } + + InteractiveTag() { + this.value = "action.interact." + name().toLowerCase(); + } + } } diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/entity/player/BedrockMovePlayerTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/entity/player/BedrockMovePlayerTranslator.java index 8809941b9..c5988bf0b 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/entity/player/BedrockMovePlayerTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/entity/player/BedrockMovePlayerTranslator.java @@ -25,7 +25,13 @@ package org.geysermc.connector.network.translators.bedrock.entity.player; +import com.github.steveice10.mc.protocol.packet.ingame.client.player.ClientPlayerPositionPacket; +import com.github.steveice10.mc.protocol.packet.ingame.client.player.ClientPlayerPositionRotationPacket; import com.nukkitx.math.vector.Vector3d; +import com.nukkitx.math.vector.Vector3f; +import com.nukkitx.protocol.bedrock.packet.MoveEntityAbsolutePacket; +import com.nukkitx.protocol.bedrock.packet.MovePlayerPacket; +import com.nukkitx.protocol.bedrock.packet.SetEntityDataPacket; import org.geysermc.connector.common.ChatColor; import org.geysermc.connector.entity.Entity; import org.geysermc.connector.entity.PlayerEntity; @@ -34,11 +40,7 @@ import org.geysermc.connector.network.session.GeyserSession; import org.geysermc.connector.network.translators.PacketTranslator; import org.geysermc.connector.network.translators.Translator; -import com.github.steveice10.mc.protocol.packet.ingame.client.player.ClientPlayerPositionRotationPacket; -import com.nukkitx.math.vector.Vector3f; -import com.nukkitx.protocol.bedrock.packet.MoveEntityAbsolutePacket; -import com.nukkitx.protocol.bedrock.packet.MovePlayerPacket; -import com.nukkitx.protocol.bedrock.packet.SetEntityDataPacket; +import java.util.concurrent.TimeUnit; @Translator(packet = MovePlayerPacket.class) public class BedrockMovePlayerTranslator extends PacketTranslator { @@ -59,13 +61,11 @@ public class BedrockMovePlayerTranslator extends PacketTranslator sendPositionIfIdle(session), + 3, TimeUnit.SECONDS)); + } + + /** + * Adjust the Bedrock position before sending to the Java server to account for inaccuracies in movement between + * the two versions. + * + * @param position the current Bedrock position of the client + * @param onGround whether the Bedrock player is on the ground + * @return the position to send to the Java server. + */ + private Vector3d adjustBedrockPosition(Vector3f position, boolean onGround) { + // We need to parse the float as a string since casting a float to a double causes us to + // lose precision and thus, causes players to get stuck when walking near walls + double javaY = position.getY() - EntityType.PLAYER.getOffset(); + if (onGround) javaY = Math.ceil(javaY * 2) / 2; + + return Vector3d.from(Double.parseDouble(Float.toString(position.getX())), javaY, + Double.parseDouble(Float.toString(position.getZ()))); } public boolean isValidMove(GeyserSession session, MovePlayerPacket.Mode mode, Vector3f currentPosition, Vector3f newPosition) { @@ -147,4 +169,16 @@ public class BedrockMovePlayerTranslator extends PacketTranslator sendPositionIfIdle(session), + 3, TimeUnit.SECONDS)); + } } diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/chat/MessageTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/chat/MessageTranslator.java new file mode 100644 index 000000000..be01362fa --- /dev/null +++ b/connector/src/main/java/org/geysermc/connector/network/translators/chat/MessageTranslator.java @@ -0,0 +1,278 @@ +/* + * Copyright (c) 2019-2020 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.connector.network.translators.chat; + +import com.github.steveice10.mc.protocol.data.game.scoreboard.TeamColor; +import com.github.steveice10.mc.protocol.data.message.style.ChatColor; +import com.github.steveice10.mc.protocol.data.message.style.ChatFormat; +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.renderer.TranslatableComponentRenderer; +import net.kyori.adventure.text.serializer.gson.GsonComponentSerializer; +import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer; +import net.kyori.adventure.translation.TranslationRegistry; +import org.geysermc.connector.network.session.GeyserSession; +import org.geysermc.connector.utils.LanguageUtils; + +import java.util.*; + +public class MessageTranslator { + + // These are used for handling the translations of the messages + private static final TranslationRegistry REGISTRY = new MinecraftTranslationRegistry(); + private static final TranslatableComponentRenderer RENDERER = TranslatableComponentRenderer.usingTranslationSource(REGISTRY); + + // Store team colors for player names + private static final Map TEAM_COLORS = new HashMap<>(); + + static { + TEAM_COLORS.put(TeamColor.BLACK, getColor(ChatColor.BLACK)); + TEAM_COLORS.put(TeamColor.DARK_BLUE, getColor(ChatColor.DARK_BLUE)); + TEAM_COLORS.put(TeamColor.DARK_GREEN, getColor(ChatColor.DARK_GREEN)); + TEAM_COLORS.put(TeamColor.DARK_AQUA, getColor(ChatColor.DARK_AQUA)); + TEAM_COLORS.put(TeamColor.DARK_RED, getColor(ChatColor.DARK_RED)); + TEAM_COLORS.put(TeamColor.DARK_PURPLE, getColor(ChatColor.DARK_PURPLE)); + TEAM_COLORS.put(TeamColor.GOLD, getColor(ChatColor.GOLD)); + TEAM_COLORS.put(TeamColor.GRAY, getColor(ChatColor.GRAY)); + TEAM_COLORS.put(TeamColor.DARK_GRAY, getColor(ChatColor.DARK_GRAY)); + TEAM_COLORS.put(TeamColor.BLUE, getColor(ChatColor.BLUE)); + TEAM_COLORS.put(TeamColor.GREEN, getColor(ChatColor.GREEN)); + TEAM_COLORS.put(TeamColor.AQUA, getColor(ChatColor.AQUA)); + TEAM_COLORS.put(TeamColor.RED, getColor(ChatColor.RED)); + TEAM_COLORS.put(TeamColor.LIGHT_PURPLE, getColor(ChatColor.LIGHT_PURPLE)); + TEAM_COLORS.put(TeamColor.YELLOW, getColor(ChatColor.YELLOW)); + TEAM_COLORS.put(TeamColor.WHITE, getColor(ChatColor.WHITE)); + TEAM_COLORS.put(TeamColor.OBFUSCATED, getFormat(ChatFormat.OBFUSCATED)); + TEAM_COLORS.put(TeamColor.BOLD, getFormat(ChatFormat.BOLD)); + TEAM_COLORS.put(TeamColor.STRIKETHROUGH, getFormat(ChatFormat.STRIKETHROUGH)); + TEAM_COLORS.put(TeamColor.ITALIC, getFormat(ChatFormat.ITALIC)); + } + + /** + * Convert a Java message to the legacy format ready for bedrock + * + * @param message Java message + * @param locale Locale to use for translation strings + * @return Parsed and formatted message for bedrock + */ + public static String convertMessage(String message, String locale) { + Component component = GsonComponentSerializer.gson().deserialize(message); + + // Get a Locale from the given locale string + Locale localeCode = Locale.forLanguageTag(locale.replace('_', '-')); + component = RENDERER.render(component, localeCode); + + return LegacyComponentSerializer.legacySection().serialize(component); + } + + public static String convertMessage(String message) { + return convertMessage(message, LanguageUtils.getDefaultLocale()); + } + + /** + * Verifies the message is valid JSON in case it's plaintext. Works around GsonComponentSeraializer not using lenient mode. + * See https://wiki.vg/Chat for messages sent in lenient mode, and for a description on leniency. + * + * @param message Potentially lenient JSON message + * @param locale Locale to use for translation strings + * @return Bedrock formatted message + */ + public static String convertMessageLenient(String message, String locale) { + if (isMessage(message)) { + return convertMessage(message, locale); + } else { + String convertedMessage = convertMessage(convertToJavaMessage(message), locale); + + // We have to do this since Adventure strips the starting reset character + if (message.startsWith(getColor(ChatColor.RESET))) { + convertedMessage = getColor(ChatColor.RESET) + convertedMessage; + } + + return convertedMessage; + } + } + + public static String convertMessageLenient(String message) { + return convertMessageLenient(message, LanguageUtils.getDefaultLocale()); + } + + /** + * Convert a Bedrock message string back to a format Java can understand + * + * @param message Message to convert + * @return The formatted JSON string + */ + public static String convertToJavaMessage(String message) { + Component component = LegacyComponentSerializer.legacySection().deserialize(message); + return GsonComponentSerializer.gson().serialize(component); + } + + /** + * Checks if the given text string is a JSON message + * + * @param text String to test + * @return True if its a valid message JSON string, false if not + */ + public static boolean isMessage(String text) { + if (text.trim().isEmpty()) { + return false; + } + + try { + GsonComponentSerializer.gson().deserialize(text); + } catch (Exception ex) { + return false; + } + + return true; + } + + /** + * Convert a {@link ChatColor} into a string for inserting into messages + * + * @param color {@link ChatColor} to convert + * @return The converted color string + */ + private static String getColor(String color) { + String base = "\u00a7"; + switch (color) { + case ChatColor.BLACK: + base += "0"; + break; + case ChatColor.DARK_BLUE: + base += "1"; + break; + case ChatColor.DARK_GREEN: + base += "2"; + break; + case ChatColor.DARK_AQUA: + base += "3"; + break; + case ChatColor.DARK_RED: + base += "4"; + break; + case ChatColor.DARK_PURPLE: + base += "5"; + break; + case ChatColor.GOLD: + base += "6"; + break; + case ChatColor.GRAY: + base += "7"; + break; + case ChatColor.DARK_GRAY: + base += "8"; + break; + case ChatColor.BLUE: + base += "9"; + break; + case ChatColor.GREEN: + base += "a"; + break; + case ChatColor.AQUA: + base += "b"; + break; + case ChatColor.RED: + base += "c"; + break; + case ChatColor.LIGHT_PURPLE: + base += "d"; + break; + case ChatColor.YELLOW: + base += "e"; + break; + case ChatColor.WHITE: + base += "f"; + break; + case ChatColor.RESET: + base += "r"; + break; + default: + return ""; + } + + return base; + } + + /** + * Convert a {@link ChatFormat} into a string for inserting into messages + * + * @param format {@link ChatFormat} to convert + * @return The converted chat formatting string + */ + private static String getFormat(ChatFormat format) { + StringBuilder str = new StringBuilder(); + String base = "\u00a7"; + switch (format) { + case OBFUSCATED: + base += "k"; + break; + case BOLD: + base += "l"; + break; + case STRIKETHROUGH: + base += "m"; + break; + case UNDERLINED: + base += "n"; + break; + case ITALIC: + base += "o"; + break; + default: + return ""; + } + + str.append(base); + + return str.toString(); + } + + /** + * Convert a team color to a chat color + * + * @param teamColor + * @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 + * + * @param message Message to check + * @param session {@link GeyserSession} for the user + * @return True if the message is too long, false if not + */ + public static boolean isTooLong(String message, GeyserSession session) { + if (message.length() > 256) { + session.sendMessage(LanguageUtils.getPlayerLocaleString("geyser.chat.too_long", session.getLocale(), message.length())); + return true; + } + + return false; + } +} diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/chat/MinecraftTranslationRegistry.java b/connector/src/main/java/org/geysermc/connector/network/translators/chat/MinecraftTranslationRegistry.java new file mode 100644 index 000000000..a23167ac2 --- /dev/null +++ b/connector/src/main/java/org/geysermc/connector/network/translators/chat/MinecraftTranslationRegistry.java @@ -0,0 +1,81 @@ +/* + * Copyright (c) 2019-2020 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.connector.network.translators.chat; + +import net.kyori.adventure.key.Key; +import net.kyori.adventure.translation.TranslationRegistry; +import org.checkerframework.checker.nullness.qual.NonNull; +import org.checkerframework.checker.nullness.qual.Nullable; +import org.geysermc.connector.utils.LocaleUtils; + +import java.text.MessageFormat; +import java.util.Locale; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * This class is used for mapping a translation key with the already loaded Java locale data + * Used in MessageTranslator.java as part of the KyoriPowered/Adventure library + */ +public class MinecraftTranslationRegistry implements TranslationRegistry { + @Override + public @NonNull Key name() { + return Key.key("", ""); + } + + @Override + public @Nullable MessageFormat translate(@NonNull String key, @NonNull Locale locale) { + // Get the locale string + String localeString = LocaleUtils.getLocaleString(key, locale.toString()); + + // Replace the `%s` with numbered inserts `{0}` + Pattern p = Pattern.compile("%s"); + Matcher m = p.matcher(localeString); + StringBuffer sb = new StringBuffer(); + int i = 0; + while (m.find()) { + m.appendReplacement(sb, "{" + (i++) + "}"); + } + m.appendTail(sb); + + return new MessageFormat(sb.toString(), locale); + } + + @Override + public void defaultLocale(@NonNull Locale locale) { + + } + + @Override + public void register(@NonNull String key, @NonNull Locale locale, @NonNull MessageFormat format) { + + } + + @Override + public void unregister(@NonNull String key) { + + } +} diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/inventory/EnchantmentInventoryTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/EnchantmentInventoryTranslator.java index 8aa91b360..20a47fd02 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/inventory/EnchantmentInventoryTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/EnchantmentInventoryTranslator.java @@ -42,6 +42,7 @@ import org.geysermc.connector.common.ChatColor; import org.geysermc.connector.inventory.Inventory; import org.geysermc.connector.network.session.GeyserSession; import org.geysermc.connector.network.translators.inventory.updater.InventoryUpdater; +import org.geysermc.connector.network.translators.item.ItemRegistry; import org.geysermc.connector.network.translators.item.ItemTranslator; import org.geysermc.connector.utils.InventoryUtils; import org.geysermc.connector.utils.LocaleUtils; @@ -57,9 +58,8 @@ import java.util.List; */ public class EnchantmentInventoryTranslator extends BlockInventoryTranslator { - private static final int DYE_ID = 351; - private static final short LAPIS_DAMAGE = 4; - private static final int ENCHANTED_BOOK_ID = 403; + private static final int DYE_ID = ItemRegistry.getItemEntry("minecraft:lapis_lazuli").getBedrockId(); + private static final int ENCHANTED_BOOK_ID = ItemRegistry.getItemEntry("minecraft:enchanted_book").getBedrockId(); public EnchantmentInventoryTranslator(InventoryUpdater updater) { super(2, "minecraft:hopper[enabled=false,facing=down]", ContainerType.HOPPER, updater); @@ -73,8 +73,7 @@ public class EnchantmentInventoryTranslator extends BlockInventoryTranslator { switch (action.getSlot()) { case 1: // Don't allow the slot to be put through if the item isn't lapis - if ((action.getToItem().getId() != DYE_ID - && action.getToItem().getDamage() != LAPIS_DAMAGE) && action.getToItem() != ItemData.AIR) { + if ((action.getToItem().getId() != DYE_ID) && action.getToItem() != ItemData.AIR) { updateInventory(session, inventory); InventoryUtils.updateCursor(session); return; diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/inventory/InventoryTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/InventoryTranslator.java index e44e4bd0b..3016871f0 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/inventory/InventoryTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/InventoryTranslator.java @@ -53,9 +53,9 @@ public abstract class InventoryTranslator { put(WindowType.BREWING_STAND, new BrewingInventoryTranslator()); put(WindowType.ANVIL, new AnvilInventoryTranslator()); put(WindowType.CRAFTING, new CraftingInventoryTranslator()); - put(WindowType.GRINDSTONE, new GrindstoneInventoryTranslator()); + //put(WindowType.GRINDSTONE, new GrindstoneInventoryTranslator()); //FIXME put(WindowType.MERCHANT, new MerchantInventoryTranslator()); - put(WindowType.SMITHING, new SmithingInventoryTranslator()); + //put(WindowType.SMITHING, new SmithingInventoryTranslator()); //TODO for server authoritative inventories InventoryTranslator furnace = new FurnaceInventoryTranslator(); put(WindowType.FURNACE, furnace); diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/item/ItemEntry.java b/connector/src/main/java/org/geysermc/connector/network/translators/item/ItemEntry.java index 19f83cd96..3fcae17db 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/item/ItemEntry.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/item/ItemEntry.java @@ -34,9 +34,10 @@ import lombok.ToString; @ToString public class ItemEntry { - public static ItemEntry AIR = new ItemEntry("minecraft:air", 0, 0, 0, false); + public static ItemEntry AIR = new ItemEntry("minecraft:air", "minecraft:air", 0, 0, 0, false); private final String javaIdentifier; + private final String bedrockIdentifier; private final int javaId; private final int bedrockId; private final int bedrockData; diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/item/ItemRegistry.java b/connector/src/main/java/org/geysermc/connector/network/translators/item/ItemRegistry.java index 597c87b26..a4a2fb750 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/item/ItemRegistry.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/item/ItemRegistry.java @@ -34,6 +34,8 @@ import com.nukkitx.protocol.bedrock.data.inventory.ItemData; import com.nukkitx.protocol.bedrock.packet.StartGamePacket; import it.unimi.dsi.fastutil.ints.Int2ObjectMap; import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; +import it.unimi.dsi.fastutil.ints.IntArrayList; +import it.unimi.dsi.fastutil.ints.IntList; import org.geysermc.connector.GeyserConnector; import org.geysermc.connector.utils.FileUtils; import org.geysermc.connector.utils.LanguageUtils; @@ -56,13 +58,21 @@ public class ItemRegistry { public static final Int2ObjectMap ITEM_ENTRIES = new Int2ObjectOpenHashMap<>(); /** - * Boat item entry, used in BedrockInventoryTransactionTranslator.java + * Bamboo item entry, used in PandaEntity.java */ - public static ItemEntry BOAT; + public static ItemEntry BAMBOO; /** - * Bucket item entry, used in BedrockInventoryTransactionTranslator.java + * Boat item entries, used in BedrockInventoryTransactionTranslator.java */ - public static ItemEntry BUCKET; + public static IntList BOATS = new IntArrayList(); + /** + * Bucket item entries (excluding the milk bucket), used in BedrockInventoryTransactionTranslator.java + */ + public static IntList BUCKETS = new IntArrayList(); + /** + * Empty item bucket, used in BedrockInventoryTransactionTranslator.java + */ + public static ItemEntry MILK_BUCKET; /** * Egg item entry, used in JavaEntityStatusTranslator.java */ @@ -75,6 +85,10 @@ public class ItemRegistry { * Shield item entry, used in Entity.java and LivingEntity.java */ public static ItemEntry SHIELD; + /** + * Wheat item entry, used in AbstractHorseEntity.java + */ + public static ItemEntry WHEAT; public static int BARRIER_INDEX = 0; @@ -84,11 +98,14 @@ public class ItemRegistry { static { /* Load item palette */ - InputStream stream = FileUtils.getResource("bedrock/items.json"); + InputStream stream = FileUtils.getResource("bedrock/runtime_item_states.json"); TypeReference> itemEntriesType = new TypeReference>() { }; + // Used to get the Bedrock namespaced ID (in instances where there are small differences) + Int2ObjectMap bedrockIdToIdentifier = new Int2ObjectOpenHashMap<>(); + List itemEntries; try { itemEntries = GeyserConnector.JSON_MAPPER.readValue(stream, itemEntriesType); @@ -96,8 +113,14 @@ public class ItemRegistry { throw new AssertionError(LanguageUtils.getLocaleStringLog("geyser.toolbox.fail.runtime_bedrock"), e); } + int lodestoneCompassId = 0; + for (JsonNode entry : itemEntries) { ITEMS.add(new StartGamePacket.ItemEntry(entry.get("name").textValue(), (short) entry.get("id").intValue())); + bedrockIdToIdentifier.put(entry.get("id").intValue(), entry.get("name").textValue()); + if (entry.get("name").textValue().equals("minecraft:lodestone_compass")) { + lodestoneCompassId = entry.get("id").intValue(); + } } stream = FileUtils.getResource("mappings/items.json"); @@ -113,28 +136,29 @@ public class ItemRegistry { Iterator> iterator = items.fields(); while (iterator.hasNext()) { Map.Entry entry = iterator.next(); + int bedrockId = entry.getValue().get("bedrock_id").intValue(); + String bedrockIdentifier = bedrockIdToIdentifier.get(bedrockId); + if (bedrockIdentifier == null) { + throw new RuntimeException("Missing Bedrock ID in mappings!: " + bedrockId); + } if (entry.getValue().has("tool_type")) { if (entry.getValue().has("tool_tier")) { ITEM_ENTRIES.put(itemIndex, new ToolItemEntry( - entry.getKey(), itemIndex, - entry.getValue().get("bedrock_id").intValue(), + entry.getKey(), bedrockIdentifier, itemIndex, bedrockId, entry.getValue().get("bedrock_data").intValue(), entry.getValue().get("tool_type").textValue(), entry.getValue().get("tool_tier").textValue(), entry.getValue().get("is_block") != null && entry.getValue().get("is_block").booleanValue())); } else { ITEM_ENTRIES.put(itemIndex, new ToolItemEntry( - entry.getKey(), itemIndex, - entry.getValue().get("bedrock_id").intValue(), + entry.getKey(), bedrockIdentifier, itemIndex, bedrockId, entry.getValue().get("bedrock_data").intValue(), entry.getValue().get("tool_type").textValue(), - "", - entry.getValue().get("is_block").booleanValue())); + "", entry.getValue().get("is_block").booleanValue())); } } else { ITEM_ENTRIES.put(itemIndex, new ItemEntry( - entry.getKey(), itemIndex, - entry.getValue().get("bedrock_id").intValue(), + entry.getKey(), bedrockIdentifier, itemIndex, bedrockId, entry.getValue().get("bedrock_data").intValue(), entry.getValue().get("is_block") != null && entry.getValue().get("is_block").booleanValue())); } @@ -142,8 +166,8 @@ public class ItemRegistry { case "minecraft:barrier": BARRIER_INDEX = itemIndex; break; - case "minecraft:oak_boat": - BOAT = ITEM_ENTRIES.get(itemIndex); + case "minecraft:bamboo": + BAMBOO = ITEM_ENTRIES.get(itemIndex); break; case "minecraft:egg": EGG = ITEM_ENTRIES.get(itemIndex); @@ -154,18 +178,32 @@ public class ItemRegistry { case "minecraft:shield": SHIELD = ITEM_ENTRIES.get(itemIndex); break; - case "minecraft:bucket": - BUCKET = ITEM_ENTRIES.get(itemIndex); + case "minecraft:milk_bucket": + MILK_BUCKET = ITEM_ENTRIES.get(itemIndex); + break; + case "minecraft:wheat": + WHEAT = ITEM_ENTRIES.get(itemIndex); break; default: break; } + if (entry.getKey().contains("boat")) { + BOATS.add(entry.getValue().get("bedrock_id").intValue()); + } else if (entry.getKey().contains("bucket") && !entry.getKey().contains("milk")) { + BUCKETS.add(entry.getValue().get("bedrock_id").intValue()); + } + itemIndex++; } - // Add the loadstonecompass since it doesn't exist on java but we need it for item conversion - ITEM_ENTRIES.put(itemIndex, new ItemEntry("minecraft:lodestonecompass", itemIndex, 741, 0, false)); + if (lodestoneCompassId == 0) { + throw new RuntimeException("Lodestone compass not found in item palette!"); + } + + // Add the loadstone compass since it doesn't exist on java but we need it for item conversion + ITEM_ENTRIES.put(itemIndex, new ItemEntry("minecraft:lodestone_compass", "minecraft:lodestone_compass", itemIndex, + lodestoneCompassId, 0, false)); /* Load creative items */ stream = FileUtils.getResource("bedrock/creative_items.json"); @@ -226,25 +264,14 @@ public class ItemRegistry { * @return an item entry from the given java edition identifier */ public static ItemEntry getItemEntry(String javaIdentifier) { - return JAVA_IDENTIFIER_MAP.computeIfAbsent(javaIdentifier, key -> ITEM_ENTRIES.values() - .stream().filter(itemEntry -> itemEntry.getJavaIdentifier().equals(key)).findFirst().orElse(null)); - } - - /** - * Finds the Bedrock string identifier of an ItemEntry - * - * @param entry the ItemEntry to search for - * @return the Bedrock identifier - */ - public static String getBedrockIdentifer(ItemEntry entry) { - String blockName = ""; - for (StartGamePacket.ItemEntry startGamePacketItemEntry : ItemRegistry.ITEMS) { - if (startGamePacketItemEntry.getId() == (short) entry.getBedrockId()) { - blockName = startGamePacketItemEntry.getIdentifier(); // Find the Bedrock string name - break; + return JAVA_IDENTIFIER_MAP.computeIfAbsent(javaIdentifier, key -> { + for (ItemEntry entry : ITEM_ENTRIES.values()) { + if (entry.getJavaIdentifier().equals(key)) { + return entry; + } } - } - return blockName; + return null; + }); } /** diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/item/ItemTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/item/ItemTranslator.java index f95a0ccc6..0df179cf4 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/item/ItemTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/item/ItemTranslator.java @@ -26,7 +26,6 @@ package org.geysermc.connector.network.translators.item; import com.github.steveice10.mc.protocol.data.game.entity.metadata.ItemStack; -import com.github.steveice10.mc.protocol.data.message.MessageSerializer; import com.github.steveice10.opennbt.tag.builtin.*; import com.nukkitx.nbt.NbtList; import com.nukkitx.nbt.NbtMap; @@ -35,23 +34,19 @@ import com.nukkitx.nbt.NbtType; import com.nukkitx.protocol.bedrock.data.inventory.ItemData; import it.unimi.dsi.fastutil.ints.Int2ObjectMap; import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; -import net.kyori.adventure.text.Component; -import net.kyori.adventure.text.serializer.gson.GsonComponentSerializer; -import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer; import org.geysermc.connector.GeyserConnector; import org.geysermc.connector.network.session.GeyserSession; import org.geysermc.connector.network.translators.ItemRemapper; +import org.geysermc.connector.network.translators.chat.MessageTranslator; import org.geysermc.connector.network.translators.world.block.BlockTranslator; import org.geysermc.connector.utils.FileUtils; import org.geysermc.connector.utils.LanguageUtils; -import org.geysermc.connector.utils.MessageUtils; import org.reflections.Reflections; import java.util.*; import java.util.stream.Collectors; public abstract class ItemTranslator { - private static final Int2ObjectMap ITEM_STACK_TRANSLATORS = new Int2ObjectOpenHashMap<>(); private static final List NBT_TRANSLATORS; @@ -220,7 +215,7 @@ public abstract class ItemTranslator { public abstract List getAppliedItems(); public NbtMap translateNbtToBedrock(com.github.steveice10.opennbt.tag.builtin.CompoundTag tag) { - Map javaValue = new HashMap<>(); + NbtMapBuilder builder = NbtMap.builder(); if (tag.getValue() != null && !tag.getValue().isEmpty()) { for (String str : tag.getValue().keySet()) { com.github.steveice10.opennbt.tag.builtin.Tag javaTag = tag.get(str); @@ -228,11 +223,9 @@ public abstract class ItemTranslator { if (translatedTag == null) continue; - javaValue.put(javaTag.getName(), translatedTag); + builder.put(javaTag.getName(), translatedTag); } } - NbtMapBuilder builder = NbtMap.builder(); - javaValue.forEach(builder::put); return builder.build(); } @@ -388,26 +381,17 @@ public abstract class ItemTranslator { public static void translateDisplayProperties(GeyserSession session, CompoundTag tag) { if (tag != null) { CompoundTag display = tag.get("display"); - if (display != null && !display.isEmpty() && display.contains("Name")) { + if (display != null && display.contains("Name")) { String name = ((StringTag) display.get("Name")).getValue(); - // If its not a message convert it - if (!MessageUtils.isMessage(name)) { - Component component = LegacyComponentSerializer.legacySection().deserialize(name); - name = GsonComponentSerializer.gson().serialize(component); - } + // Get the translated name and prefix it with a reset char + name = MessageTranslator.convertMessageLenient(name, session.getLocale()); - // Check if its a message to translate - if (MessageUtils.isMessage(name)) { - // Get the translated name - name = MessageUtils.getTranslatedBedrockMessage(MessageSerializer.fromString(name), session.getLocale()); + // Add the new name tag + display.put(new StringTag("Name", name)); - // Add the new name tag - display.put(new StringTag("Name", name)); - - // Add to the new root tag - tag.put(display); - } + // Add to the new root tag + tag.put(display); } } } diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/item/ToolItemEntry.java b/connector/src/main/java/org/geysermc/connector/network/translators/item/ToolItemEntry.java index 10edcdec8..dde577005 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/item/ToolItemEntry.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/item/ToolItemEntry.java @@ -32,8 +32,8 @@ public class ToolItemEntry extends ItemEntry { private final String toolType; private final String toolTier; - public ToolItemEntry(String javaIdentifier, int javaId, int bedrockId, int bedrockData, String toolType, String toolTier, boolean isBlock) { - super(javaIdentifier, javaId, bedrockId, bedrockData, isBlock); + public ToolItemEntry(String javaIdentifier, String bedrockIdentifier, int javaId, int bedrockId, int bedrockData, String toolType, String toolTier, boolean isBlock) { + super(javaIdentifier, bedrockIdentifier, javaId, bedrockId, bedrockData, isBlock); this.toolType = toolType; this.toolTier = toolTier; } diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/item/translators/BannerTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/item/translators/BannerTranslator.java index f4bfdfb64..14b934362 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/item/translators/BannerTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/item/translators/BannerTranslator.java @@ -26,20 +26,16 @@ package org.geysermc.connector.network.translators.item.translators; import com.github.steveice10.mc.protocol.data.game.entity.metadata.ItemStack; -import com.github.steveice10.opennbt.tag.builtin.CompoundTag; -import com.github.steveice10.opennbt.tag.builtin.IntTag; -import com.github.steveice10.opennbt.tag.builtin.ListTag; -import com.github.steveice10.opennbt.tag.builtin.StringTag; -import com.github.steveice10.opennbt.tag.builtin.Tag; +import com.github.steveice10.opennbt.tag.builtin.*; import com.nukkitx.nbt.NbtList; import com.nukkitx.nbt.NbtMap; import com.nukkitx.nbt.NbtMapBuilder; import com.nukkitx.nbt.NbtType; import com.nukkitx.protocol.bedrock.data.inventory.ItemData; import org.geysermc.connector.network.translators.ItemRemapper; +import org.geysermc.connector.network.translators.item.ItemEntry; import org.geysermc.connector.network.translators.item.ItemRegistry; import org.geysermc.connector.network.translators.item.ItemTranslator; -import org.geysermc.connector.network.translators.item.ItemEntry; import java.util.ArrayList; import java.util.HashMap; @@ -49,54 +45,13 @@ import java.util.stream.Collectors; @ItemRemapper public class BannerTranslator extends ItemTranslator { - private final List appliedItems; public BannerTranslator() { - appliedItems = ItemRegistry.ITEM_ENTRIES.values().stream().filter(entry -> entry.getJavaIdentifier().endsWith("banner")).collect(Collectors.toList()); - } - - @Override - public ItemData translateToBedrock(ItemStack itemStack, ItemEntry itemEntry) { - if (itemStack.getNbt() == null) return super.translateToBedrock(itemStack, itemEntry); - - ItemData itemData = super.translateToBedrock(itemStack, itemEntry); - - CompoundTag blockEntityTag = itemStack.getNbt().get("BlockEntityTag"); - if (blockEntityTag.contains("Patterns")) { - ListTag patterns = blockEntityTag.get("Patterns"); - - NbtMapBuilder builder = itemData.getTag().toBuilder(); - builder.put("Patterns", convertBannerPattern(patterns)); - - itemData = ItemData.of(itemData.getId(), itemData.getDamage(), itemData.getCount(), builder.build()); - } - - return itemData; - } - - @Override - public ItemStack translateToJava(ItemData itemData, ItemEntry itemEntry) { - if (itemData.getTag() == null) return super.translateToJava(itemData, itemEntry); - - ItemStack itemStack = super.translateToJava(itemData, itemEntry); - - NbtMap nbtTag = itemData.getTag(); - if (nbtTag.containsKey("Patterns", NbtType.COMPOUND)) { - List patterns = nbtTag.getList("Patterns", NbtType.COMPOUND); - - CompoundTag blockEntityTag = new CompoundTag("BlockEntityTag"); - blockEntityTag.put(convertBannerPattern(patterns)); - - itemStack.getNbt().put(blockEntityTag); - } - - return itemStack; - } - - @Override - public List getAppliedItems() { - return appliedItems; + appliedItems = ItemRegistry.ITEM_ENTRIES.values() + .stream() + .filter(entry -> entry.getJavaIdentifier().endsWith("banner")) + .collect(Collectors.toList()); } /** @@ -133,7 +88,6 @@ public class BannerTranslator extends ItemTranslator { return NbtMap.builder() .putInt("Color", 15 - (int) pattern.get("Color").getValue()) - .putString("Pattern", (String) pattern.get("Pattern").getValue()) .putString("Pattern", patternName) .build(); } @@ -147,8 +101,7 @@ public class BannerTranslator extends ItemTranslator { public static ListTag convertBannerPattern(List patterns) { List tagsList = new ArrayList<>(); for (Object patternTag : patterns) { - CompoundTag newPatternTag = getJavaBannerPattern((NbtMap) patternTag); - tagsList.add(newPatternTag); + tagsList.add(getJavaBannerPattern((NbtMap) patternTag)); } return new ListTag("Patterns", tagsList); @@ -167,4 +120,51 @@ public class BannerTranslator extends ItemTranslator { return new CompoundTag("", tags); } + + @Override + public ItemData translateToBedrock(ItemStack itemStack, ItemEntry itemEntry) { + if (itemStack.getNbt() == null) { + return super.translateToBedrock(itemStack, itemEntry); + } + + ItemData itemData = super.translateToBedrock(itemStack, itemEntry); + + CompoundTag blockEntityTag = itemStack.getNbt().get("BlockEntityTag"); + if (blockEntityTag != null && blockEntityTag.contains("Patterns")) { + ListTag patterns = blockEntityTag.get("Patterns"); + + NbtMapBuilder builder = itemData.getTag().toBuilder(); + builder.put("Patterns", convertBannerPattern(patterns)); + + itemData = ItemData.of(itemData.getId(), itemData.getDamage(), itemData.getCount(), builder.build()); + } + + return itemData; + } + + @Override + public ItemStack translateToJava(ItemData itemData, ItemEntry itemEntry) { + if (itemData.getTag() == null) { + return super.translateToJava(itemData, itemEntry); + } + + ItemStack itemStack = super.translateToJava(itemData, itemEntry); + + NbtMap nbtTag = itemData.getTag(); + if (nbtTag.containsKey("Patterns", NbtType.COMPOUND)) { + List patterns = nbtTag.getList("Patterns", NbtType.COMPOUND); + + CompoundTag blockEntityTag = new CompoundTag("BlockEntityTag"); + blockEntityTag.put(convertBannerPattern(patterns)); + + itemStack.getNbt().put(blockEntityTag); + } + + return itemStack; + } + + @Override + public List getAppliedItems() { + return appliedItems; + } } diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/item/translators/CompassTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/item/translators/CompassTranslator.java index 159b9ab49..92ec67dd4 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/item/translators/CompassTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/item/translators/CompassTranslator.java @@ -53,7 +53,7 @@ public class CompassTranslator extends ItemTranslator { Tag lodestoneTag = itemStack.getNbt().get("LodestoneTracked"); if (lodestoneTag instanceof ByteTag) { // Get the fake lodestonecompass entry - itemEntry = ItemRegistry.getItemEntry("minecraft:lodestonecompass"); + itemEntry = ItemRegistry.getItemEntry("minecraft:lodestone_compass"); // Get the loadstone pos CompoundTag loadstonePos = itemStack.getNbt().get("LodestonePos"); @@ -83,7 +83,7 @@ public class CompassTranslator extends ItemTranslator { @Override public ItemStack translateToJava(ItemData itemData, ItemEntry itemEntry) { boolean isLoadstone = false; - if (itemEntry.getJavaIdentifier().equals("minecraft:lodestonecompass")) { + if (itemEntry.getBedrockIdentifier().equals("minecraft:lodestone_compass")) { // Revert the entry back to the compass itemEntry = ItemRegistry.getItemEntry("minecraft:compass"); diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/item/translators/nbt/BasicItemTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/item/translators/nbt/BasicItemTranslator.java index 1d21bbfb7..3fd9df8a0 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/item/translators/nbt/BasicItemTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/item/translators/nbt/BasicItemTranslator.java @@ -37,7 +37,6 @@ import org.geysermc.connector.network.session.GeyserSession; import org.geysermc.connector.network.translators.ItemRemapper; import org.geysermc.connector.network.translators.item.ItemEntry; import org.geysermc.connector.network.translators.item.NbtItemStackTranslator; -import org.geysermc.connector.utils.MessageUtils; import java.util.ArrayList; import java.util.List; @@ -108,7 +107,7 @@ public class BasicItemTranslator extends NbtItemStackTranslator { private String toBedrockMessage(StringTag tag) { String message = tag.getValue(); if (message == null) return null; - TextComponent component = (TextComponent) MessageUtils.phraseJavaMessage(message); + TextComponent component = (TextComponent) GsonComponentSerializer.gson().deserialize(message); String legacy = LegacyComponentSerializer.legacySection().serialize(component); if (hasFormatting(LegacyComponentSerializer.legacySection().deserialize(legacy))) { return "§r" + legacy; diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/item/translators/nbt/BookPagesTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/item/translators/nbt/BookPagesTranslator.java index 41ee4fbca..294dd81ed 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/item/translators/nbt/BookPagesTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/item/translators/nbt/BookPagesTranslator.java @@ -33,7 +33,7 @@ import org.geysermc.connector.network.session.GeyserSession; import org.geysermc.connector.network.translators.ItemRemapper; import org.geysermc.connector.network.translators.item.NbtItemStackTranslator; import org.geysermc.connector.network.translators.item.ItemEntry; -import org.geysermc.connector.utils.MessageUtils; +import org.geysermc.connector.network.translators.chat.MessageTranslator; import java.util.ArrayList; import java.util.List; @@ -56,7 +56,7 @@ public class BookPagesTranslator extends NbtItemStackTranslator { CompoundTag pageTag = new CompoundTag(""); pageTag.put(new StringTag("photoname", "")); - pageTag.put(new StringTag("text", MessageUtils.getBedrockMessageLenient(textTag.getValue()))); + pageTag.put(new StringTag("text", MessageTranslator.convertMessageLenient(textTag.getValue()))); pages.add(pageTag); } @@ -78,7 +78,7 @@ public class BookPagesTranslator extends NbtItemStackTranslator { CompoundTag pageTag = (CompoundTag) tag; StringTag textTag = pageTag.get("text"); - pages.add(new StringTag(MessageUtils.getJavaMessage(textTag.getValue()))); + pages.add(new StringTag(MessageTranslator.convertToJavaMessage(textTag.getValue()))); } itemTag.remove("pages"); diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/item/translators/nbt/CrossbowTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/item/translators/nbt/CrossbowTranslator.java index 67f137ff9..97da86696 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/item/translators/nbt/CrossbowTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/item/translators/nbt/CrossbowTranslator.java @@ -45,15 +45,15 @@ public class CrossbowTranslator extends NbtItemStackTranslator { if (!chargedProjectiles.getValue().isEmpty()) { CompoundTag projectile = (CompoundTag) chargedProjectiles.getValue().get(0); - ItemEntry entry = ItemRegistry.getItemEntry((String) projectile.get("id").getValue()); - if (entry == null) return; + ItemEntry projectileEntry = ItemRegistry.getItemEntry((String) projectile.get("id").getValue()); + if (projectileEntry == null) return; CompoundTag tag = projectile.get("tag"); ItemStack itemStack = new ItemStack(itemEntry.getJavaId(), (byte) projectile.get("Count").getValue(), tag); ItemData itemData = ItemTranslator.translateToBedrock(session, itemStack); CompoundTag newProjectile = new CompoundTag("chargedItem"); newProjectile.put(new ByteTag("Count", (byte) itemData.getCount())); - newProjectile.put(new StringTag("Name", ItemRegistry.getBedrockIdentifer(entry))); + newProjectile.put(new StringTag("Name", projectileEntry.getBedrockIdentifier())); newProjectile.put(new ShortTag("Damage", itemData.getDamage())); diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/item/translators/nbt/MapItemTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/item/translators/nbt/MapItemTranslator.java index c9b49efd4..d325af486 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/item/translators/nbt/MapItemTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/item/translators/nbt/MapItemTranslator.java @@ -25,10 +25,7 @@ package org.geysermc.connector.network.translators.item.translators.nbt; -import com.github.steveice10.opennbt.tag.builtin.ByteTag; -import com.github.steveice10.opennbt.tag.builtin.CompoundTag; -import com.github.steveice10.opennbt.tag.builtin.IntTag; -import com.github.steveice10.opennbt.tag.builtin.LongTag; +import com.github.steveice10.opennbt.tag.builtin.*; import org.geysermc.connector.network.session.GeyserSession; import org.geysermc.connector.network.translators.ItemRemapper; import org.geysermc.connector.network.translators.item.ItemEntry; @@ -39,14 +36,22 @@ public class MapItemTranslator extends NbtItemStackTranslator { @Override public void translateToBedrock(GeyserSession session, CompoundTag itemTag, ItemEntry itemEntry) { - IntTag mapId = itemTag.get("map"); + // Can be either an IntTag or ShortTag + Tag mapId = itemTag.get("map"); + if (mapId == null) return; - if (mapId != null) { - itemTag.put(new LongTag("map_uuid", mapId.getValue())); - itemTag.put(new IntTag("map_name_index", mapId.getValue())); - itemTag.put(new ByteTag("map_display_players", (byte) 1)); - itemTag.remove("map"); + int mapValue; + if (mapId.getValue() instanceof Short) { + // Convert to int if necessary + mapValue = (int) (short) mapId.getValue(); + } else { + mapValue = (int) mapId.getValue(); } + + itemTag.put(new LongTag("map_uuid", mapValue)); + itemTag.put(new IntTag("map_name_index", mapValue)); + itemTag.put(new ByteTag("map_display_players", (byte) 1)); + itemTag.remove("map"); } @Override diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/item/translators/nbt/ShulkerBoxItemTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/item/translators/nbt/ShulkerBoxItemTranslator.java index a9930f698..126d2e1f5 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/item/translators/nbt/ShulkerBoxItemTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/item/translators/nbt/ShulkerBoxItemTranslator.java @@ -50,9 +50,8 @@ public class ShulkerBoxItemTranslator extends NbtItemStackTranslator { boxItemTag.put(new ByteTag("WasPickedUp", (byte) 0)); // ??? ItemEntry boxItemEntry = ItemRegistry.getItemEntry(((StringTag) itemData.get("id")).getValue()); - String blockName = ItemRegistry.getBedrockIdentifer(boxItemEntry); - boxItemTag.put(new StringTag("Name", blockName)); + boxItemTag.put(new StringTag("Name", boxItemEntry.getBedrockIdentifier())); boxItemTag.put(new ShortTag("Damage", (short) boxItemEntry.getBedrockData())); boxItemTag.put(new ByteTag("Count", ((ByteTag) itemData.get("Count")).getValue())); if (itemData.contains("tag")) { diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/java/JavaChatTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/java/JavaChatTranslator.java index 186aaf660..f5128ed6f 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/java/JavaChatTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/java/JavaChatTranslator.java @@ -25,15 +25,12 @@ package org.geysermc.connector.network.translators.java; -import com.github.steveice10.mc.protocol.data.message.TranslationMessage; import com.github.steveice10.mc.protocol.packet.ingame.server.ServerChatPacket; import com.nukkitx.protocol.bedrock.packet.TextPacket; import org.geysermc.connector.network.session.GeyserSession; import org.geysermc.connector.network.translators.PacketTranslator; import org.geysermc.connector.network.translators.Translator; -import org.geysermc.connector.utils.MessageUtils; - -import java.util.List; +import org.geysermc.connector.network.translators.chat.MessageTranslator; @Translator(packet = ServerChatPacket.class) public class JavaChatTranslator extends PacketTranslator { @@ -59,21 +56,8 @@ public class JavaChatTranslator extends PacketTranslator { break; } - String locale = session.getLocale(); - - if (packet.getMessage() instanceof TranslationMessage) { - textPacket.setType(TextPacket.Type.TRANSLATION); - textPacket.setNeedsTranslation(true); - - List paramsTranslated = MessageUtils.getTranslationParams(((TranslationMessage) packet.getMessage()).getWith(), locale, packet.getMessage()); - textPacket.setParameters(paramsTranslated); - - textPacket.setMessage(MessageUtils.insertParams(MessageUtils.getTranslatedBedrockMessage(packet.getMessage(), locale, true, packet.getMessage()), paramsTranslated)); - } else { - textPacket.setNeedsTranslation(false); - - textPacket.setMessage(MessageUtils.getTranslatedBedrockMessage(packet.getMessage(), locale, false, packet.getMessage())); - } + textPacket.setNeedsTranslation(false); + textPacket.setMessage(MessageTranslator.convertMessage(packet.getMessage().toString(), session.getLocale())); session.sendUpstreamPacket(textPacket); } diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/java/JavaDisconnectPacket.java b/connector/src/main/java/org/geysermc/connector/network/translators/java/JavaDisconnectPacket.java index f36da367b..1945a8e10 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/java/JavaDisconnectPacket.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/java/JavaDisconnectPacket.java @@ -29,13 +29,13 @@ import com.github.steveice10.mc.protocol.packet.ingame.server.ServerDisconnectPa import org.geysermc.connector.network.session.GeyserSession; import org.geysermc.connector.network.translators.PacketTranslator; import org.geysermc.connector.network.translators.Translator; -import org.geysermc.connector.utils.MessageUtils; +import org.geysermc.connector.network.translators.chat.MessageTranslator; @Translator(packet = ServerDisconnectPacket.class) public class JavaDisconnectPacket extends PacketTranslator { @Override public void translate(ServerDisconnectPacket packet, GeyserSession session) { - session.disconnect(MessageUtils.getTranslatedBedrockMessage(packet.getReason(), session.getLocale(), true)); + session.disconnect(MessageTranslator.convertMessage(packet.getReason().toString(), session.getLocale())); } } diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/java/JavaJoinGameTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/java/JavaJoinGameTranslator.java index c0207841d..6f489979c 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/java/JavaJoinGameTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/java/JavaJoinGameTranslator.java @@ -55,7 +55,7 @@ public class JavaJoinGameTranslator extends PacketTranslator { @@ -37,6 +37,6 @@ public class JavaLoginDisconnectTranslator extends PacketTranslator } String newDimension = DimensionUtils.getNewDimension(packet.getDimension()); - if (!entity.getDimension().equals(newDimension)) { + if (!session.getDimension().equals(newDimension)) { DimensionUtils.switchDimension(session, newDimension); } else { if (session.isManyDimPackets()) { //reloading world - String fakeDim = entity.getDimension().equals(DimensionUtils.OVERWORLD) ? DimensionUtils.NETHER : DimensionUtils.OVERWORLD; + String fakeDim = session.getDimension().equals(DimensionUtils.OVERWORLD) ? DimensionUtils.NETHER : DimensionUtils.OVERWORLD; DimensionUtils.switchDimension(session, fakeDim); DimensionUtils.switchDimension(session, newDimension); } else { diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/java/JavaTitleTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/java/JavaTitleTranslator.java index 12ef10d0a..7fe4d7b31 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/java/JavaTitleTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/java/JavaTitleTranslator.java @@ -28,7 +28,7 @@ package org.geysermc.connector.network.translators.java; import org.geysermc.connector.network.session.GeyserSession; import org.geysermc.connector.network.translators.PacketTranslator; import org.geysermc.connector.network.translators.Translator; -import org.geysermc.connector.utils.MessageUtils; +import org.geysermc.connector.network.translators.chat.MessageTranslator; import com.github.steveice10.mc.protocol.packet.ingame.server.ServerTitlePacket; import com.nukkitx.protocol.bedrock.packet.SetTitlePacket; @@ -41,14 +41,21 @@ public class JavaTitleTranslator extends PacketTranslator { SetTitlePacket titlePacket = new SetTitlePacket(); String locale = session.getLocale(); + String text; + if (packet.getTitle() == null) { + text = " "; + } else { + text = MessageTranslator.convertMessage(packet.getTitle().toString(), locale); + } + switch (packet.getAction()) { case TITLE: titlePacket.setType(SetTitlePacket.Type.TITLE); - titlePacket.setText(MessageUtils.getTranslatedBedrockMessage(packet.getTitle(), locale)); + titlePacket.setText(text); break; case SUBTITLE: titlePacket.setType(SetTitlePacket.Type.SUBTITLE); - titlePacket.setText(MessageUtils.getTranslatedBedrockMessage(packet.getTitle(), locale)); + titlePacket.setText(text); break; case CLEAR: case RESET: @@ -57,9 +64,10 @@ public class JavaTitleTranslator extends PacketTranslator { break; case ACTION_BAR: titlePacket.setType(SetTitlePacket.Type.ACTIONBAR); - titlePacket.setText(MessageUtils.getTranslatedBedrockMessage(packet.getTitle(), locale)); + titlePacket.setText(text); break; case TIMES: + titlePacket.setType(SetTitlePacket.Type.TIMES); titlePacket.setFadeInTime(packet.getFadeIn()); titlePacket.setFadeOutTime(packet.getFadeOut()); titlePacket.setStayTime(packet.getStay()); diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/java/entity/JavaEntityAttachTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/java/entity/JavaEntityAttachTranslator.java index b642a75ba..1a6630efa 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/java/entity/JavaEntityAttachTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/java/entity/JavaEntityAttachTranslator.java @@ -62,7 +62,7 @@ public class JavaEntityAttachTranslator extends PacketTranslator { @@ -141,9 +137,15 @@ public class JavaEntityStatusTranslator extends PacketTranslator { switch (packet.getAction()) { case CREATE: scoreboard.registerNewTeam(packet.getTeamName(), toPlayerSet(packet.getPlayers())) - .setName(MessageUtils.getBedrockMessage(packet.getDisplayName())) + .setName(MessageTranslator.convertMessage(packet.getDisplayName().toString())) .setColor(packet.getColor()) .setNameTagVisibility(packet.getNameTagVisibility()) - .setPrefix(MessageUtils.getTranslatedBedrockMessage(packet.getPrefix(), session.getLocale())) - .setSuffix(MessageUtils.getTranslatedBedrockMessage(packet.getSuffix(), session.getLocale())); + .setPrefix(MessageTranslator.convertMessage(packet.getPrefix().toString(), session.getLocale())) + .setSuffix(MessageTranslator.convertMessage(packet.getSuffix().toString(), session.getLocale())); break; case UPDATE: if (team == null) { @@ -74,11 +74,11 @@ public class JavaTeamTranslator extends PacketTranslator { return; } - team.setName(MessageUtils.getBedrockMessage(packet.getDisplayName())) + team.setName(MessageTranslator.convertMessage(packet.getDisplayName().toString())) .setColor(packet.getColor()) .setNameTagVisibility(packet.getNameTagVisibility()) - .setPrefix(MessageUtils.getTranslatedBedrockMessage(packet.getPrefix(), session.getLocale())) - .setSuffix(MessageUtils.getTranslatedBedrockMessage(packet.getSuffix(), session.getLocale())) + .setPrefix(MessageTranslator.convertMessage(packet.getPrefix().toString(), session.getLocale())) + .setSuffix(MessageTranslator.convertMessage(packet.getSuffix().toString(), session.getLocale())) .setUpdateType(UpdateType.UPDATE); break; case ADD_PLAYER: diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/java/window/JavaOpenWindowTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/java/window/JavaOpenWindowTranslator.java index 2c10ded60..1fb088717 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/java/window/JavaOpenWindowTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/java/window/JavaOpenWindowTranslator.java @@ -25,7 +25,6 @@ package org.geysermc.connector.network.translators.java.window; -import com.github.steveice10.mc.protocol.data.message.MessageSerializer; import com.github.steveice10.mc.protocol.packet.ingame.client.window.ClientCloseWindowPacket; import com.github.steveice10.mc.protocol.packet.ingame.server.window.ServerOpenWindowPacket; import org.geysermc.connector.inventory.Inventory; @@ -35,7 +34,7 @@ import org.geysermc.connector.network.translators.Translator; import org.geysermc.connector.network.translators.inventory.InventoryTranslator; import org.geysermc.connector.utils.InventoryUtils; import org.geysermc.connector.utils.LocaleUtils; -import org.geysermc.connector.utils.MessageUtils; +import org.geysermc.connector.network.translators.chat.MessageTranslator; @Translator(packet = ServerOpenWindowPacket.class) public class JavaOpenWindowTranslator extends PacketTranslator { @@ -57,8 +56,7 @@ public class JavaOpenWindowTranslator extends PacketTranslator 0 ? 1 : 0); session.sendUpstreamPacket(blockEventPacket); - } - if (packet.getValue() instanceof EndGatewayValue) { + } else if (packet.getValue() instanceof EndGatewayValue) { blockEventPacket.setEventType(1); session.sendUpstreamPacket(blockEventPacket); - } - if (packet.getValue() instanceof NoteBlockValue) { + } else if (packet.getValue() instanceof NoteBlockValue) { NoteblockBlockEntityTranslator.translate(session, packet.getPosition()); - return; - } - if (packet.getValue() instanceof PistonValue) { + } else if (packet.getValue() instanceof PistonValue) { PistonValueType type = (PistonValueType) packet.getType(); // Unlike everything else, pistons need a block entity packet to convey motion @@ -73,14 +70,46 @@ public class JavaBlockValueTranslator extends PacketTranslator { - /** * Determines if we should process non-full chunks */ - private final boolean isCacheChunks; + private final boolean cacheChunks; public JavaChunkDataTranslator() { - isCacheChunks = GeyserConnector.getInstance().getConfig().isCacheChunks(); + cacheChunks = GeyserConnector.getInstance().getConfig().isCacheChunks(); } @Override @@ -61,7 +60,7 @@ public class JavaChunkDataTranslator extends PacketTranslator boolean shouldStore = false; mapItemDataPacket.setUniqueMapId(packet.getMapId()); - mapItemDataPacket.setDimensionId(DimensionUtils.javaToBedrock(session.getPlayerEntity().getDimension())); + mapItemDataPacket.setDimensionId(DimensionUtils.javaToBedrock(session.getDimension())); mapItemDataPacket.setLocked(packet.isLocked()); mapItemDataPacket.setScale(packet.getScale()); diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/java/world/JavaNotifyClientTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/java/world/JavaNotifyClientTranslator.java index 1e102a606..26ecb1e1f 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/java/world/JavaNotifyClientTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/java/world/JavaNotifyClientTranslator.java @@ -103,13 +103,13 @@ public class JavaNotifyClientTranslator extends PacketTranslator { + private final boolean cacheChunks; + + public JavaUpdateTileEntityTranslator() { + cacheChunks = GeyserConnector.getInstance().getConfig().isCacheChunks(); + } @Override public void translate(ServerUpdateTileEntityPacket packet, GeyserSession session) { @@ -48,16 +54,17 @@ public class JavaUpdateTileEntityTranslator extends PacketTranslator= 2 && session.getGameMode() == GameMode.CREATIVE && packet.getNbt().size() > 5) { diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/world/WorldManager.java b/connector/src/main/java/org/geysermc/connector/network/translators/world/WorldManager.java index fec3bb33a..aaafe2fe9 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/world/WorldManager.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/world/WorldManager.java @@ -43,7 +43,7 @@ import org.geysermc.connector.utils.GameRule; public abstract class WorldManager { /** - * Gets the block state at the specified location + * Gets the Java block state at the specified location * * @param session the session * @param position the position @@ -54,7 +54,7 @@ public abstract class WorldManager { } /** - * Gets the block state at the specified location + * Gets the Java block state at the specified location * * @param session the session * @param vector the position @@ -65,7 +65,7 @@ public abstract class WorldManager { } /** - * Gets the block state at the specified location + * Gets the Java block state at the specified location * * @param session the session * @param x the x coordinate to get the block at @@ -97,7 +97,7 @@ public abstract class WorldManager { public abstract boolean hasMoreBlockDataThanChunkCache(); /** - * Gets the biome data for the specified chunk. + * Gets the Java biome data for the specified chunk. * * @param session the session of the player * @param x the chunk's X coordinate @@ -116,7 +116,7 @@ public abstract class WorldManager { public abstract void setGameRule(GeyserSession session, String name, Object value); /** - * Get a gamerule value as a boolean + * Gets a gamerule value as a boolean * * @param session The session of the user that requested the value * @param gameRule The gamerule to fetch the value of diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/world/block/BlockStateValues.java b/connector/src/main/java/org/geysermc/connector/network/translators/world/block/BlockStateValues.java index 305118e6f..bfd59cc7c 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/world/block/BlockStateValues.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/world/block/BlockStateValues.java @@ -36,7 +36,6 @@ import java.util.Map; * Used for block entities if the Java block state contains Bedrock block information. */ public class BlockStateValues { - private static final Int2IntMap BANNER_COLORS = new Int2IntOpenHashMap(); private static final Int2ByteMap BED_COLORS = new Int2ByteOpenHashMap(); private static final Int2ByteMap COMMAND_BLOCK_VALUES = new Int2ByteOpenHashMap(); @@ -52,7 +51,8 @@ public class BlockStateValues { /** * Determines if the block state contains Bedrock block information - * @param entry The String to JsonNode map used in BlockTranslator + * + * @param entry The String to JsonNode map used in BlockTranslator * @param javaBlockState the Java Block State of the block */ public static void storeBlockStateValues(Map.Entry entry, int javaBlockState) { @@ -101,7 +101,7 @@ public class BlockStateValues { } JsonNode skullVariation = entry.getValue().get("variation"); - if(skullVariation != null) { + if (skullVariation != null) { SKULL_VARIANTS.put(javaBlockState, (byte) skullVariation.intValue()); } @@ -124,10 +124,7 @@ public class BlockStateValues { * @return Banner color integer or -1 if no color */ public static int getBannerColor(int state) { - if (BANNER_COLORS.containsKey(state)) { - return BANNER_COLORS.get(state); - } - return -1; + return BANNER_COLORS.getOrDefault(state, -1); } /** @@ -138,10 +135,7 @@ public class BlockStateValues { * @return Bed color byte or -1 if no color */ public static byte getBedColor(int state) { - if (BED_COLORS.containsKey(state)) { - return BED_COLORS.get(state); - } - return -1; + return BED_COLORS.getOrDefault(state, (byte) -1); } /** @@ -157,6 +151,7 @@ public class BlockStateValues { /** * All double chest values are part of the block state in Java and part of the block entity tag in Bedrock. * This gives the DoubleChestValue that can be calculated into the final tag. + * * @return The map of all DoubleChestValues. */ public static Int2ObjectMap getDoubleChestValues() { @@ -165,6 +160,7 @@ public class BlockStateValues { /** * Get the Int2ObjectMap of flower pot block states to containing plant + * * @return Int2ObjectMap of flower pot values */ public static Int2ObjectMap getFlowerPotValues() { @@ -173,6 +169,7 @@ public class BlockStateValues { /** * Get the map of contained flower pot plants to Bedrock CompoundTag + * * @return Map of flower pot blocks. */ public static Map getFlowerPotBlocks() { @@ -182,18 +179,17 @@ public class BlockStateValues { /** * The note that noteblocks output when hit is part of the block state in Java but sent as a BlockEventPacket in Bedrock. * This gives an integer pitch that Bedrock can use. + * * @param state BlockState of the block * @return note block note integer or -1 if not present */ public static int getNoteblockPitch(int state) { - if (NOTEBLOCK_PITCHES.containsKey(state)) { - return NOTEBLOCK_PITCHES.get(state); - } - return -1; + return NOTEBLOCK_PITCHES.getOrDefault(state, -1); } /** * Get the Int2BooleanMap showing if a piston block state is extended or not. + * * @return the Int2BooleanMap of piston extensions. */ public static Int2BooleanMap getPistonValues() { @@ -212,10 +208,7 @@ public class BlockStateValues { * @return Skull variant byte or -1 if no variant */ public static byte getSkullVariant(int state) { - if (SKULL_VARIANTS.containsKey(state)) { - return SKULL_VARIANTS.get(state); - } - return -1; + return SKULL_VARIANTS.getOrDefault(state, (byte) -1); } /** @@ -226,10 +219,7 @@ public class BlockStateValues { * @return Skull rotation value or -1 if no value */ public static byte getSkullRotation(int state) { - if (SKULL_ROTATIONS.containsKey(state)) { - return SKULL_ROTATIONS.get(state); - } - return -1; + return SKULL_ROTATIONS.getOrDefault(state, (byte) -1); } @@ -241,9 +231,6 @@ public class BlockStateValues { * @return Shulker direction value or -1 if no value */ public static byte getShulkerBoxDirection(int state) { - if (SHULKERBOX_DIRECTIONS.containsKey(state)) { - return SHULKERBOX_DIRECTIONS.get(state); - } - return -1; + return SHULKERBOX_DIRECTIONS.getOrDefault(state, (byte) -1); } } diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/world/block/BlockTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/world/block/BlockTranslator.java index 5314292a1..eb09d63e3 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/world/block/BlockTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/world/block/BlockTranslator.java @@ -30,21 +30,25 @@ import com.google.common.collect.BiMap; import com.google.common.collect.HashBiMap; import com.nukkitx.nbt.*; import it.unimi.dsi.fastutil.ints.*; -import it.unimi.dsi.fastutil.objects.Object2IntMap; -import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap; -import it.unimi.dsi.fastutil.objects.Object2ObjectMap; -import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap; +import it.unimi.dsi.fastutil.objects.*; import org.geysermc.connector.GeyserConnector; import org.geysermc.connector.network.translators.world.block.entity.BlockEntity; import org.geysermc.connector.utils.FileUtils; import org.reflections.Reflections; +import java.io.DataInputStream; import java.io.InputStream; import java.util.*; public class BlockTranslator { - public static final NbtList BLOCKS; - public static final int AIR = 0; + /** + * The Java block runtime ID of air + */ + public static final int JAVA_AIR_ID = 0; + /** + * The Bedrock block runtime ID of air + */ + public static final int BEDROCK_AIR_ID; public static final int BEDROCK_WATER_ID; private static final Int2IntMap JAVA_TO_BEDROCK_BLOCK_MAP = new Int2IntOpenHashMap(); @@ -86,25 +90,31 @@ public class BlockTranslator { public static final int JAVA_RUNTIME_SPAWNER_ID; - private static final int BLOCK_STATE_VERSION = 17825806; + private static final int BLOCK_STATE_VERSION = 17825808; static { /* Load block palette */ - InputStream stream = FileUtils.getResource("bedrock/runtime_block_states.dat"); + InputStream stream = FileUtils.getResource("bedrock/blockpalette.nbt"); NbtList blocksTag; - try (NBTInputStream nbtInputStream = NbtUtils.createNetworkReader(stream)) { - blocksTag = (NbtList) nbtInputStream.readTag(); + try (NBTInputStream nbtInputStream = new NBTInputStream(new DataInputStream(stream))) { + NbtMap blockPalette = (NbtMap) nbtInputStream.readTag(); + blocksTag = (NbtList) blockPalette.getList("blocks", NbtType.COMPOUND); } catch (Exception e) { throw new AssertionError("Unable to get blocks from runtime block states", e); } - Map blockStateMap = new HashMap<>(); + // New since 1.16.100 - find the block runtime ID by the order given to us in the block palette, + // as we no longer send a block palette + Object2IntMap blockStateOrderedMap = new Object2IntOpenHashMap<>(blocksTag.size()); - for (NbtMap tag : blocksTag) { - if (blockStateMap.putIfAbsent(tag.getCompound("block"), tag) != null) { + for (int i = 0; i < blocksTag.size(); i++) { + NbtMap tag = blocksTag.get(i); + NbtMap blockTag = tag.getCompound("block"); + if (blockStateOrderedMap.containsKey(blockTag)) { throw new AssertionError("Duplicate block states in Bedrock palette"); } + blockStateOrderedMap.put(blockTag, i); } stream = FileUtils.getResource("mappings/blocks.json"); @@ -114,16 +124,13 @@ public class BlockTranslator { } catch (Exception e) { throw new AssertionError("Unable to load Java block mappings", e); } - Object2IntMap addedStatesMap = new Object2IntOpenHashMap<>(); - addedStatesMap.defaultReturnValue(-1); - List paletteList = new ArrayList<>(); - Reflections ref = GeyserConnector.getInstance().useXmlReflections() ? FileUtils.getReflections("org.geysermc.connector.network.translators.world.block.entity") : new Reflections("org.geysermc.connector.network.translators.world.block.entity"); - ref.getTypesAnnotatedWith(BlockEntity.class); + Reflections ref = GeyserConnector.getInstance().useXmlReflections() ? FileUtils.getReflections("org.geysermc.connector.network.translators.world.block.entity") + : new Reflections("org.geysermc.connector.network.translators.world.block.entity"); int waterRuntimeId = -1; int javaRuntimeId = -1; - int bedrockRuntimeId = 0; + int airRuntimeId = -1; int cobwebRuntimeId = -1; int commandBlockRuntimeId = -1; int furnaceRuntimeId = -1; @@ -136,6 +143,10 @@ public class BlockTranslator { Map.Entry entry = blocksIterator.next(); String javaId = entry.getKey(); NbtMap blockTag = buildBedrockState(entry.getValue()); + int bedrockRuntimeId = blockStateOrderedMap.getOrDefault(blockTag, -1); + if (bedrockRuntimeId == -1) { + throw new RuntimeException("Unable to find " + javaId + " Bedrock runtime ID!"); + } // TODO fix this, (no block should have a null hardness) JsonNode hardnessNode = entry.getValue().get("block_hardness"); @@ -199,22 +210,12 @@ public class BlockTranslator { BEDROCK_TO_JAVA_BLOCK_MAP.putIfAbsent(bedrockRuntimeId, javaRuntimeId); } - NbtMap runtimeTag = blockStateMap.remove(blockTag); - if (runtimeTag != null) { - addedStatesMap.put(blockTag, bedrockRuntimeId); - paletteList.add(runtimeTag); - } else { - int duplicateRuntimeId = addedStatesMap.getOrDefault(blockTag, -1); - if (duplicateRuntimeId == -1) { - GeyserConnector.getInstance().getLogger().debug("Mapping " + javaId + " was not found for bedrock edition!"); - } else { - JAVA_TO_BEDROCK_BLOCK_MAP.put(javaRuntimeId, duplicateRuntimeId); - } - continue; - } JAVA_TO_BEDROCK_BLOCK_MAP.put(javaRuntimeId, bedrockRuntimeId); - if (javaId.contains("wool")) { + if (bedrockIdentifier.equals("minecraft:air")) { + airRuntimeId = bedrockRuntimeId; + + } else if (javaId.contains("wool")) { JAVA_RUNTIME_WOOL_IDS.add(javaRuntimeId); } else if (javaId.contains("cobweb")) { @@ -233,8 +234,6 @@ public class BlockTranslator { } else if (javaId.startsWith("minecraft:spawner")) { spawnerRuntimeId = javaRuntimeId; } - - bedrockRuntimeId++; } if (cobwebRuntimeId == -1) { @@ -267,19 +266,17 @@ public class BlockTranslator { } BEDROCK_WATER_ID = waterRuntimeId; - paletteList.addAll(blockStateMap.values()); // Add any missing mappings that could crash the client + if (airRuntimeId == -1) { + throw new AssertionError("Unable to find air in palette"); + } + BEDROCK_AIR_ID = airRuntimeId; // Loop around again to find all item frame runtime IDs - int frameRuntimeId = 0; - for (NbtMap tag : paletteList) { - NbtMap blockTag = tag.getCompound("block"); - if (blockTag.getString("name").equals("minecraft:frame")) { - ITEM_FRAMES.put(tag, frameRuntimeId); + for (Object2IntMap.Entry entry : blockStateOrderedMap.object2IntEntrySet()) { + if (entry.getKey().getString("name").equals("minecraft:frame")) { + ITEM_FRAMES.put(entry.getKey(), entry.getIntValue()); } - frameRuntimeId++; } - - BLOCKS = new NbtList<>(NbtType.COMPOUND, paletteList); } private BlockTranslator() { diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/BannerBlockEntityTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/BannerBlockEntityTranslator.java index 57393a6c5..f5e1d5948 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/BannerBlockEntityTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/BannerBlockEntityTranslator.java @@ -27,39 +27,31 @@ package org.geysermc.connector.network.translators.world.block.entity; import com.github.steveice10.opennbt.tag.builtin.CompoundTag; import com.github.steveice10.opennbt.tag.builtin.ListTag; +import com.nukkitx.nbt.NbtMapBuilder; import org.geysermc.connector.network.translators.item.translators.BannerTranslator; import org.geysermc.connector.network.translators.world.block.BlockStateValues; -import java.util.HashMap; -import java.util.Map; - @BlockEntity(name = "Banner", regex = "banner") public class BannerBlockEntityTranslator extends BlockEntityTranslator implements RequiresBlockState { - @Override public boolean isBlock(int blockState) { return BlockStateValues.getBannerColor(blockState) != -1; } @Override - public Map translateTag(CompoundTag tag, int blockState) { - Map tags = new HashMap<>(); - + public void translateTag(NbtMapBuilder builder, CompoundTag tag, int blockState) { int bannerColor = BlockStateValues.getBannerColor(blockState); if (bannerColor != -1) { - tags.put("Base", 15 - bannerColor); + builder.put("Base", 15 - bannerColor); } if (tag.contains("Patterns")) { ListTag patterns = tag.get("Patterns"); - tags.put("Patterns", BannerTranslator.convertBannerPattern(patterns)); + builder.put("Patterns", BannerTranslator.convertBannerPattern(patterns)); } if (tag.contains("CustomName")) { - tags.put("CustomName", tag.get("CustomName").getValue()); + builder.put("CustomName", tag.get("CustomName").getValue()); } - - return tags; } - } diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/BedBlockEntityTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/BedBlockEntityTranslator.java index 080bdc3b2..0067cc41f 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/BedBlockEntityTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/BedBlockEntityTranslator.java @@ -26,27 +26,23 @@ package org.geysermc.connector.network.translators.world.block.entity; import com.github.steveice10.opennbt.tag.builtin.CompoundTag; +import com.nukkitx.nbt.NbtMapBuilder; import org.geysermc.connector.network.translators.world.block.BlockStateValues; -import java.util.HashMap; -import java.util.Map; - @BlockEntity(name = "Bed", regex = "bed") public class BedBlockEntityTranslator extends BlockEntityTranslator implements RequiresBlockState { - @Override public boolean isBlock(int blockState) { return BlockStateValues.getBedColor(blockState) != -1; } @Override - public Map translateTag(CompoundTag tag, int blockState) { - Map tags = new HashMap<>(); + public void translateTag(NbtMapBuilder builder, CompoundTag tag, int blockState) { byte bedcolor = BlockStateValues.getBedColor(blockState); // Just in case... - if (bedcolor == -1) bedcolor = 0; - tags.put("color", bedcolor); - return tags; + if (bedcolor == -1) { + bedcolor = 0; + } + builder.put("color", bedcolor); } - } diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/BedrockOnlyBlockEntity.java b/connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/BedrockOnlyBlockEntity.java index d2e4537f5..646929f32 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/BedrockOnlyBlockEntity.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/BedrockOnlyBlockEntity.java @@ -33,7 +33,6 @@ import org.geysermc.connector.network.session.GeyserSession; * Implemented only if a block is a block entity in Bedrock and not Java Edition. */ public interface BedrockOnlyBlockEntity { - /** * Update the block on Bedrock Edition. * @param session GeyserSession. @@ -49,7 +48,7 @@ public interface BedrockOnlyBlockEntity { * @return Bedrock tag, or null if not a Bedrock-only Block Entity */ static NbtMap getTag(Vector3i position, int blockState) { - if (new FlowerPotBlockEntityTranslator().isBlock(blockState)) { + if (FlowerPotBlockEntityTranslator.isFlowerBlock(blockState)) { return FlowerPotBlockEntityTranslator.getTag(blockState, position); } else if (PistonBlockEntityTranslator.isBlock(blockState)) { return PistonBlockEntityTranslator.getTag(blockState, position); diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/BlockEntityTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/BlockEntityTranslator.java index 4df4fd95e..679636524 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/BlockEntityTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/BlockEntityTranslator.java @@ -41,10 +41,16 @@ import org.reflections.Reflections; import java.util.HashMap; import java.util.Map; +/** + * The class that all block entities (on both Java and Bedrock) should translate with + */ public abstract class BlockEntityTranslator { - public static final Map BLOCK_ENTITY_TRANSLATORS = new HashMap<>(); - public static ObjectArrayList REQUIRES_BLOCK_STATE_LIST = new ObjectArrayList<>(); + /** + * A list of all block entities that require the Java block state in order to fill out their block entity information. + * This list will be smaller with cache chunks on as we don't need to double-cache data + */ + public static final ObjectArrayList REQUIRES_BLOCK_STATE_LIST = new ObjectArrayList<>(); /** * Contains a list of irregular block entity name translations that can't be fit into the regex @@ -78,27 +84,33 @@ public abstract class BlockEntityTranslator { GeyserConnector.getInstance().getLogger().error(LanguageUtils.getLocaleStringLog("geyser.network.translator.block_entity.failed", clazz.getCanonicalName())); } } + boolean cacheChunks = GeyserConnector.getInstance().getConfig().isCacheChunks(); for (Class clazz : ref.getSubTypesOf(RequiresBlockState.class)) { GeyserConnector.getInstance().getLogger().debug("Found block entity that requires block state: " + clazz.getCanonicalName()); try { - REQUIRES_BLOCK_STATE_LIST.add((RequiresBlockState) clazz.newInstance()); + RequiresBlockState requiresBlockState = (RequiresBlockState) clazz.newInstance(); + if (cacheChunks && !(requiresBlockState instanceof BedrockOnlyBlockEntity)) { + // Not needed to put this one in the map; cache chunks takes care of that for us + GeyserConnector.getInstance().getLogger().debug("Not adding because cache chunks is enabled."); + continue; + } + REQUIRES_BLOCK_STATE_LIST.add(requiresBlockState); } catch (InstantiationException | IllegalAccessException e) { GeyserConnector.getInstance().getLogger().error(LanguageUtils.getLocaleStringLog("geyser.network.translator.block_state.failed", clazz.getCanonicalName())); } } } - public abstract Map translateTag(CompoundTag tag, int blockState); + public abstract void translateTag(NbtMapBuilder builder, CompoundTag tag, int blockState); public NbtMap getBlockEntityTag(String id, CompoundTag tag, int blockState) { - int x = Integer.parseInt(String.valueOf(tag.getValue().get("x").getValue())); - int y = Integer.parseInt(String.valueOf(tag.getValue().get("y").getValue())); - int z = Integer.parseInt(String.valueOf(tag.getValue().get("z").getValue())); + int x = ((IntTag) tag.getValue().get("x")).getValue(); + int y = ((IntTag) tag.getValue().get("y")).getValue(); + int z = ((IntTag) tag.getValue().get("z")).getValue(); NbtMapBuilder tagBuilder = getConstantBedrockTag(BlockEntityUtils.getBedrockBlockEntityId(id), x, y, z).toBuilder(); - Map translatedTags = translateTag(tag, blockState); - translatedTags.forEach(tagBuilder::put); + translateTag(tagBuilder, tag, blockState); return tagBuilder.build(); } diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/CampfireBlockEntityTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/CampfireBlockEntityTranslator.java index d6ac0281b..3e4f9fb90 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/CampfireBlockEntityTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/CampfireBlockEntityTranslator.java @@ -32,22 +32,16 @@ import com.nukkitx.nbt.NbtMapBuilder; import org.geysermc.connector.network.translators.item.ItemEntry; import org.geysermc.connector.network.translators.item.ItemRegistry; -import java.util.HashMap; -import java.util.Map; - @BlockEntity(name = "Campfire", regex = "campfire") public class CampfireBlockEntityTranslator extends BlockEntityTranslator { - @Override - public Map translateTag(CompoundTag tag, int blockState) { - Map tags = new HashMap<>(); + public void translateTag(NbtMapBuilder builder, CompoundTag tag, int blockState) { ListTag items = tag.get("Items"); int i = 1; for (com.github.steveice10.opennbt.tag.builtin.Tag itemTag : items.getValue()) { - tags.put("Item" + i, getItem((CompoundTag) itemTag)); + builder.put("Item" + i, getItem((CompoundTag) itemTag)); i++; } - return tags; } protected NbtMap getItem(CompoundTag tag) { diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/CommandBlockBlockEntityTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/CommandBlockBlockEntityTranslator.java index 6bc940adb..1eb50ffe7 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/CommandBlockBlockEntityTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/CommandBlockBlockEntityTranslator.java @@ -26,38 +26,33 @@ package org.geysermc.connector.network.translators.world.block.entity; import com.github.steveice10.opennbt.tag.builtin.*; +import com.nukkitx.nbt.NbtMapBuilder; import org.geysermc.connector.network.translators.world.block.BlockStateValues; -import org.geysermc.connector.utils.MessageUtils; - -import java.util.HashMap; -import java.util.Map; +import org.geysermc.connector.network.translators.chat.MessageTranslator; @BlockEntity(name = "CommandBlock", regex = "command_block") public class CommandBlockBlockEntityTranslator extends BlockEntityTranslator implements RequiresBlockState { - @Override - public Map translateTag(CompoundTag tag, int blockState) { - Map map = new HashMap<>(); + public void translateTag(NbtMapBuilder builder, CompoundTag tag, int blockState) { if (tag.size() < 5) { - return map; // These values aren't here + return; // These values aren't here } // Java infers from the block state, but Bedrock needs it in the tag - map.put("conditionalMode", BlockStateValues.getCommandBlockValues().getOrDefault(blockState, (byte) 0)); + builder.put("conditionalMode", BlockStateValues.getCommandBlockValues().getOrDefault(blockState, (byte) 0)); // Java and Bedrock values - map.put("conditionMet", ((ByteTag) tag.get("conditionMet")).getValue()); - map.put("auto", ((ByteTag) tag.get("auto")).getValue()); - map.put("CustomName", MessageUtils.getBedrockMessage(((StringTag) tag.get("CustomName")).getValue())); - map.put("powered", ((ByteTag) tag.get("powered")).getValue()); - map.put("Command", ((StringTag) tag.get("Command")).getValue()); - map.put("SuccessCount", ((IntTag) tag.get("SuccessCount")).getValue()); - map.put("TrackOutput", ((ByteTag) tag.get("TrackOutput")).getValue()); - map.put("UpdateLastExecution", ((ByteTag) tag.get("UpdateLastExecution")).getValue()); + builder.put("conditionMet", ((ByteTag) tag.get("conditionMet")).getValue()); + builder.put("auto", ((ByteTag) tag.get("auto")).getValue()); + builder.put("CustomName", MessageTranslator.convertMessage(((StringTag) tag.get("CustomName")).getValue())); + builder.put("powered", ((ByteTag) tag.get("powered")).getValue()); + builder.put("Command", ((StringTag) tag.get("Command")).getValue()); + builder.put("SuccessCount", ((IntTag) tag.get("SuccessCount")).getValue()); + builder.put("TrackOutput", ((ByteTag) tag.get("TrackOutput")).getValue()); + builder.put("UpdateLastExecution", ((ByteTag) tag.get("UpdateLastExecution")).getValue()); if (tag.get("LastExecution") != null) { - map.put("LastExecution", ((LongTag) tag.get("LastExecution")).getValue()); + builder.put("LastExecution", ((LongTag) tag.get("LastExecution")).getValue()); } else { - map.put("LastExecution", (long) 0); + builder.put("LastExecution", (long) 0); } - return map; } @Override diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/DoubleChestBlockEntityTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/DoubleChestBlockEntityTranslator.java index 5b59420e0..47bcf4897 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/DoubleChestBlockEntityTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/DoubleChestBlockEntityTranslator.java @@ -33,15 +33,11 @@ import org.geysermc.connector.network.translators.world.block.BlockStateValues; import org.geysermc.connector.network.translators.world.block.DoubleChestValue; import org.geysermc.connector.utils.BlockEntityUtils; -import java.util.HashMap; -import java.util.Map; - /** * Chests have more block entity properties in Bedrock, which is solved by implementing the BedrockOnlyBlockEntity */ @BlockEntity(name = "Chest", regex = "chest") public class DoubleChestBlockEntityTranslator extends BlockEntityTranslator implements BedrockOnlyBlockEntity, RequiresBlockState { - @Override public boolean isBlock(int blockState) { return BlockStateValues.getDoubleChestValues().containsKey(blockState); @@ -51,44 +47,39 @@ public class DoubleChestBlockEntityTranslator extends BlockEntityTranslator impl public void updateBlock(GeyserSession session, int blockState, Vector3i position) { CompoundTag javaTag = getConstantJavaTag("chest", position.getX(), position.getY(), position.getZ()); NbtMapBuilder tagBuilder = getConstantBedrockTag(BlockEntityUtils.getBedrockBlockEntityId("chest"), position.getX(), position.getY(), position.getZ()).toBuilder(); - translateTag(javaTag, blockState).forEach(tagBuilder::put); + translateTag(tagBuilder, javaTag, blockState); BlockEntityUtils.updateBlockEntity(session, tagBuilder.build(), position); } @Override - public Map translateTag(CompoundTag tag, int blockState) { - Map tags = new HashMap<>(); - if (BlockStateValues.getDoubleChestValues().containsKey(blockState)) { - DoubleChestValue chestValues = BlockStateValues.getDoubleChestValues().get(blockState); - if (chestValues != null) { - int x = (int) tag.getValue().get("x").getValue(); - int z = (int) tag.getValue().get("z").getValue(); - // Calculate the position of the other chest based on the Java block state - if (chestValues.isFacingEast) { - if (chestValues.isDirectionPositive) { - // East - z = z + (chestValues.isLeft ? 1 : -1); - } else { - // West - z = z + (chestValues.isLeft ? -1 : 1); - } + public void translateTag(NbtMapBuilder builder, CompoundTag tag, int blockState) { + DoubleChestValue chestValues = BlockStateValues.getDoubleChestValues().getOrDefault(blockState, null); + if (chestValues != null) { + int x = (int) tag.getValue().get("x").getValue(); + int z = (int) tag.getValue().get("z").getValue(); + // Calculate the position of the other chest based on the Java block state + if (chestValues.isFacingEast) { + if (chestValues.isDirectionPositive) { + // East + z = z + (chestValues.isLeft ? 1 : -1); } else { - if (chestValues.isDirectionPositive) { - // South - x = x + (chestValues.isLeft ? -1 : 1); - } else { - // North - x = x + (chestValues.isLeft ? 1 : -1); - } + // West + z = z + (chestValues.isLeft ? -1 : 1); } - tags.put("pairx", x); - tags.put("pairz", z); - if (!chestValues.isLeft) { - tags.put("pairlead", (byte) 1); + } else { + if (chestValues.isDirectionPositive) { + // South + x = x + (chestValues.isLeft ? -1 : 1); + } else { + // North + x = x + (chestValues.isLeft ? 1 : -1); } } + builder.put("pairx", x); + builder.put("pairz", z); + if (!chestValues.isLeft) { + builder.put("pairlead", (byte) 1); + } } - return tags; } - } diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/EmptyBlockEntityTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/EmptyBlockEntityTranslator.java index e9715bd32..3926b8664 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/EmptyBlockEntityTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/EmptyBlockEntityTranslator.java @@ -26,16 +26,11 @@ package org.geysermc.connector.network.translators.world.block.entity; import com.github.steveice10.opennbt.tag.builtin.CompoundTag; - -import java.util.HashMap; -import java.util.Map; +import com.nukkitx.nbt.NbtMapBuilder; @BlockEntity(name = "Empty", regex = "") public class EmptyBlockEntityTranslator extends BlockEntityTranslator { - @Override - public Map translateTag(CompoundTag tag, int blockState) { - return new HashMap<>(); + public void translateTag(NbtMapBuilder builder, CompoundTag tag, int blockState) { } - } diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/EndGatewayBlockEntityTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/EndGatewayBlockEntityTranslator.java index af94c560d..0bf588226 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/EndGatewayBlockEntityTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/EndGatewayBlockEntityTranslator.java @@ -28,21 +28,18 @@ package org.geysermc.connector.network.translators.world.block.entity; import com.github.steveice10.opennbt.tag.builtin.CompoundTag; import com.github.steveice10.opennbt.tag.builtin.IntTag; import com.nukkitx.nbt.NbtList; +import com.nukkitx.nbt.NbtMapBuilder; import com.nukkitx.nbt.NbtType; import it.unimi.dsi.fastutil.ints.IntArrayList; import it.unimi.dsi.fastutil.ints.IntList; -import java.util.HashMap; import java.util.LinkedHashMap; -import java.util.Map; @BlockEntity(name = "EndGateway", regex = "end_gateway") public class EndGatewayBlockEntityTranslator extends BlockEntityTranslator { - @Override - public Map translateTag(CompoundTag tag, int blockState) { - Map tags = new HashMap<>(); - tags.put("Age", (int) ((long) tag.get("Age").getValue())); + public void translateTag(NbtMapBuilder builder, CompoundTag tag, int blockState) { + builder.put("Age", (int) ((long) tag.get("Age").getValue())); // Java sometimes does not provide this tag, but Bedrock crashes if it doesn't exist // Linked coordinates IntList tagsList = new IntArrayList(); @@ -50,8 +47,7 @@ public class EndGatewayBlockEntityTranslator extends BlockEntityTranslator { tagsList.add(getExitPortalCoordinate(tag, "X")); tagsList.add(getExitPortalCoordinate(tag, "Y")); tagsList.add(getExitPortalCoordinate(tag, "Z")); - tags.put("ExitPortal", new NbtList<>(NbtType.INT, tagsList)); - return tags; + builder.put("ExitPortal", new NbtList<>(NbtType.INT, tagsList)); } private int getExitPortalCoordinate(CompoundTag tag, String axis) { @@ -60,6 +56,7 @@ public class EndGatewayBlockEntityTranslator extends BlockEntityTranslator { LinkedHashMap compoundTag = (LinkedHashMap) tag.get("ExitPortal").getValue(); IntTag intTag = (IntTag) compoundTag.get(axis); return intTag.getValue(); - } return 0; + } + return 0; } } diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/FlowerPotBlockEntityTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/FlowerPotBlockEntityTranslator.java index 7bfcc7ee9..f64474ae1 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/FlowerPotBlockEntityTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/FlowerPotBlockEntityTranslator.java @@ -35,30 +35,19 @@ import org.geysermc.connector.network.translators.world.block.BlockTranslator; import org.geysermc.connector.utils.BlockEntityUtils; public class FlowerPotBlockEntityTranslator implements BedrockOnlyBlockEntity, RequiresBlockState { - - @Override - public boolean isBlock(int blockState) { - return (BlockStateValues.getFlowerPotValues().containsKey(blockState)); - } - - @Override - public void updateBlock(GeyserSession session, int blockState, Vector3i position) { - BlockEntityUtils.updateBlockEntity(session, getTag(blockState, position), position); - UpdateBlockPacket updateBlockPacket = new UpdateBlockPacket(); - updateBlockPacket.setDataLayer(0); - updateBlockPacket.setRuntimeId(BlockTranslator.getBedrockBlockId(blockState)); - updateBlockPacket.setBlockPosition(position); - updateBlockPacket.getFlags().add(UpdateBlockPacket.Flag.NEIGHBORS); - updateBlockPacket.getFlags().add(UpdateBlockPacket.Flag.NETWORK); - updateBlockPacket.getFlags().add(UpdateBlockPacket.Flag.PRIORITY); - session.sendUpstreamPacket(updateBlockPacket); - BlockEntityUtils.updateBlockEntity(session, getTag(blockState, position), position); + /** + * @param blockState the Java block state of a potential flower pot block + * @return true if the block is a flower pot + */ + public static boolean isFlowerBlock(int blockState) { + return BlockStateValues.getFlowerPotValues().containsKey(blockState); } /** * Get the Nukkit CompoundTag of the flower pot. + * * @param blockState Java block state of flower pot. - * @param position Bedrock position of flower pot. + * @param position Bedrock position of flower pot. * @return Bedrock tag of flower pot. */ public static NbtMap getTag(int blockState, Vector3i position) { @@ -80,4 +69,23 @@ public class FlowerPotBlockEntityTranslator implements BedrockOnlyBlockEntity, R } return tagBuilder.build(); } + + @Override + public boolean isBlock(int blockState) { + return isFlowerBlock(blockState); + } + + @Override + public void updateBlock(GeyserSession session, int blockState, Vector3i position) { + BlockEntityUtils.updateBlockEntity(session, getTag(blockState, position), position); + UpdateBlockPacket updateBlockPacket = new UpdateBlockPacket(); + updateBlockPacket.setDataLayer(0); + updateBlockPacket.setRuntimeId(BlockTranslator.getBedrockBlockId(blockState)); + updateBlockPacket.setBlockPosition(position); + updateBlockPacket.getFlags().add(UpdateBlockPacket.Flag.NEIGHBORS); + updateBlockPacket.getFlags().add(UpdateBlockPacket.Flag.NETWORK); + updateBlockPacket.getFlags().add(UpdateBlockPacket.Flag.PRIORITY); + session.sendUpstreamPacket(updateBlockPacket); + BlockEntityUtils.updateBlockEntity(session, getTag(blockState, position), position); + } } diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/JigsawBlockBlockEntityTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/JigsawBlockBlockEntityTranslator.java index 43ac1a96f..4fcdfe54d 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/JigsawBlockBlockEntityTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/JigsawBlockBlockEntityTranslator.java @@ -27,21 +27,16 @@ package org.geysermc.connector.network.translators.world.block.entity; import com.github.steveice10.opennbt.tag.builtin.CompoundTag; import com.github.steveice10.opennbt.tag.builtin.StringTag; - -import java.util.HashMap; -import java.util.Map; +import com.nukkitx.nbt.NbtMapBuilder; @BlockEntity(name = "JigsawBlock", regex = "jigsaw") public class JigsawBlockBlockEntityTranslator extends BlockEntityTranslator { - @Override - public Map translateTag(CompoundTag tag, int blockState) { - Map map = new HashMap<>(); - map.put("joint", ((StringTag) tag.get("joint")).getValue()); - map.put("name", ((StringTag) tag.get("name")).getValue()); - map.put("target_pool", ((StringTag) tag.get("pool")).getValue()); - map.put("final_state", ((StringTag) tag.get("final_state")).getValue()); - map.put("target", ((StringTag) tag.get("target")).getValue()); - return map; + public void translateTag(NbtMapBuilder builder, CompoundTag tag, int blockState) { + builder.put("joint", ((StringTag) tag.get("joint")).getValue()); + builder.put("name", ((StringTag) tag.get("name")).getValue()); + builder.put("target_pool", ((StringTag) tag.get("pool")).getValue()); + builder.put("final_state", ((StringTag) tag.get("final_state")).getValue()); + builder.put("target", ((StringTag) tag.get("target")).getValue()); } } diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/NoteblockBlockEntityTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/NoteblockBlockEntityTranslator.java index f3e430090..fce0a0561 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/NoteblockBlockEntityTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/NoteblockBlockEntityTranslator.java @@ -36,21 +36,20 @@ import org.geysermc.connector.utils.ChunkUtils; * Does not implement BlockEntityTranslator because it's only a block entity in Bedrock */ public class NoteblockBlockEntityTranslator implements RequiresBlockState { - @Override public boolean isBlock(int blockState) { return BlockStateValues.getNoteblockPitch(blockState) != -1; } public static void translate(GeyserSession session, Position position) { - int blockState = ChunkUtils.CACHED_BLOCK_ENTITIES.getOrDefault(position, 0); + int blockState = session.getConnector().getConfig().isCacheChunks() ? + session.getConnector().getWorldManager().getBlockAt(session, position) : + ChunkUtils.CACHED_BLOCK_ENTITIES.removeInt(position); BlockEventPacket blockEventPacket = new BlockEventPacket(); blockEventPacket.setBlockPosition(Vector3i.from(position.getX(), position.getY(), position.getZ())); blockEventPacket.setEventType(0); blockEventPacket.setEventData(BlockStateValues.getNoteblockPitch(blockState)); session.sendUpstreamPacket(blockEventPacket); - - ChunkUtils.CACHED_BLOCK_ENTITIES.remove(position); } } diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/PistonBlockEntityTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/PistonBlockEntityTranslator.java index 54feacbeb..c8a6e868f 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/PistonBlockEntityTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/PistonBlockEntityTranslator.java @@ -34,9 +34,9 @@ import org.geysermc.connector.network.translators.world.block.BlockStateValues; * Pistons are a special case where they are only a block entity on Bedrock. */ public class PistonBlockEntityTranslator { - /** * Used in ChunkUtils to determine if the block is a piston. + * * @param blockState Java BlockState of block. * @return if block is a piston or not. */ @@ -46,8 +46,9 @@ public class PistonBlockEntityTranslator { /** * Calculates the Nukkit CompoundTag to send to the client on chunk + * * @param blockState Java block state of block. - * @param position Bedrock position of piston. + * @param position Bedrock position of piston. * @return Bedrock tag of piston. */ public static NbtMap getTag(int blockState, Vector3i position) { @@ -57,14 +58,13 @@ public class PistonBlockEntityTranslator { .putInt("z", position.getZ()) .putByte("isMovable", (byte) 1) .putString("id", "PistonArm"); - if (BlockStateValues.getPistonValues().containsKey(blockState)) { - boolean extended = BlockStateValues.getPistonValues().get(blockState); - // 1f if extended, otherwise 0f - tagBuilder.putFloat("Progress", (extended) ? 1.0f : 0.0f); - // 1 if sticky, 0 if not - tagBuilder.putByte("Sticky", (byte)((BlockStateValues.isStickyPiston(blockState)) ? 1 : 0)); - } + + boolean extended = BlockStateValues.getPistonValues().get(blockState); + // 1f if extended, otherwise 0f + tagBuilder.putFloat("Progress", (extended) ? 1.0f : 0.0f); + // 1 if sticky, 0 if not + tagBuilder.putByte("Sticky", (byte) ((BlockStateValues.isStickyPiston(blockState)) ? 1 : 0)); + return tagBuilder.build(); } - } diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/ShulkerBoxBlockEntityTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/ShulkerBoxBlockEntityTranslator.java index 08e3abaab..69fa10845 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/ShulkerBoxBlockEntityTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/ShulkerBoxBlockEntityTranslator.java @@ -26,23 +26,18 @@ package org.geysermc.connector.network.translators.world.block.entity; import com.github.steveice10.opennbt.tag.builtin.CompoundTag; +import com.nukkitx.nbt.NbtMapBuilder; import org.geysermc.connector.network.translators.world.block.BlockStateValues; -import java.util.HashMap; -import java.util.Map; - @BlockEntity(name = "ShulkerBox", regex = "shulker_box") public class ShulkerBoxBlockEntityTranslator extends BlockEntityTranslator { - @Override - public Map translateTag(CompoundTag tag, int blockState) { - Map tags = new HashMap<>(); - + public void translateTag(NbtMapBuilder builder, CompoundTag tag, int blockState) { byte direction = BlockStateValues.getShulkerBoxDirection(blockState); // Just in case... - if (direction == -1) direction = 1; - tags.put("facing", direction); - return tags; + if (direction == -1) { + direction = 1; + } + builder.put("facing", direction); } - } diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/SignBlockEntityTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/SignBlockEntityTranslator.java index b40ed42c9..a9641d772 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/SignBlockEntityTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/SignBlockEntityTranslator.java @@ -25,62 +25,22 @@ package org.geysermc.connector.network.translators.world.block.entity; -import com.github.steveice10.mc.protocol.data.message.MessageSerializer; import com.github.steveice10.opennbt.tag.builtin.CompoundTag; -import org.geysermc.connector.utils.MessageUtils; +import com.nukkitx.nbt.NbtMapBuilder; +import org.geysermc.connector.network.translators.chat.MessageTranslator; import org.geysermc.connector.utils.SignUtils; -import java.util.HashMap; -import java.util.Map; - @BlockEntity(name = "Sign", regex = "sign") public class SignBlockEntityTranslator extends BlockEntityTranslator { - - @Override - public Map translateTag(CompoundTag tag, int blockState) { - Map tags = new HashMap<>(); - - StringBuilder signText = new StringBuilder(); - for(int i = 0; i < 4; i++) { - int currentLine = i+1; - String signLine = getOrDefault(tag.getValue().get("Text" + currentLine), ""); - signLine = MessageUtils.getBedrockMessage(MessageSerializer.fromString(signLine)); - - // Check the character width on the sign to ensure there is no overflow that is usually hidden - // to Java Edition clients but will appear to Bedrock clients - int signWidth = 0; - StringBuilder finalSignLine = new StringBuilder(); - for (char c : signLine.toCharArray()) { - signWidth += SignUtils.getCharacterWidth(c); - if (signWidth <= SignUtils.BEDROCK_CHARACTER_WIDTH_MAX) { - finalSignLine.append(c); - } else { - break; - } - } - - // Java Edition 1.14 added the ability to change the text color of the whole sign using dye - if (tag.contains("Color")) { - signText.append(getBedrockSignColor(tag.get("Color").getValue().toString())); - } - - signText.append(finalSignLine.toString()); - signText.append("\n"); - } - - tags.put("Text", MessageUtils.getBedrockMessage(MessageSerializer.fromString(signText.toString()))); - return tags; - } - /** * Maps a color stored in a sign's Color tag to a Bedrock Edition formatting code. *
- * The color names correspond to dye names, because of this we can't use {@link MessageUtils#getColor(String)}. + * The color names correspond to dye names, because of this we can't use {@link MessageTranslator#getColor(String)}. * * @param javaColor The dye color stored in the sign's Color tag. * @return A Bedrock Edition formatting code for valid dye colors, otherwise an empty string. */ - private static String getBedrockSignColor(String javaColor) { + private String getBedrockSignColor(String javaColor) { String base = "\u00a7"; switch (javaColor) { case "white": @@ -133,4 +93,41 @@ public class SignBlockEntityTranslator extends BlockEntityTranslator { return base; } + @Override + public void translateTag(NbtMapBuilder builder, CompoundTag tag, int blockState) { + StringBuilder signText = new StringBuilder(); + for (int i = 0; i < 4; i++) { + int currentLine = i + 1; + String signLine = getOrDefault(tag.getValue().get("Text" + currentLine), ""); + signLine = MessageTranslator.convertMessageLenient(signLine); + + // Trim any trailing formatting codes + if (signLine.length() > 2 && signLine.toCharArray()[signLine.length() - 2] == '\u00a7') { + signLine = signLine.substring(0, signLine.length() - 2); + } + + // Check the character width on the sign to ensure there is no overflow that is usually hidden + // to Java Edition clients but will appear to Bedrock clients + int signWidth = 0; + StringBuilder finalSignLine = new StringBuilder(); + for (char c : signLine.toCharArray()) { + signWidth += SignUtils.getCharacterWidth(c); + if (signWidth <= SignUtils.BEDROCK_CHARACTER_WIDTH_MAX) { + finalSignLine.append(c); + } else { + break; + } + } + + // Java Edition 1.14 added the ability to change the text color of the whole sign using dye + if (tag.contains("Color")) { + signText.append(getBedrockSignColor(tag.get("Color").getValue().toString())); + } + + signText.append(finalSignLine.toString()); + signText.append("\n"); + } + + builder.put("Text", signText.toString()); + } } diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/SkullBlockEntityTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/SkullBlockEntityTranslator.java index 6d350c0cc..c5f479948 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/SkullBlockEntityTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/SkullBlockEntityTranslator.java @@ -25,29 +25,26 @@ package org.geysermc.connector.network.translators.world.block.entity; +import com.github.steveice10.opennbt.tag.builtin.CompoundTag; +import com.nukkitx.nbt.NbtMapBuilder; import org.geysermc.connector.network.translators.world.block.BlockStateValues; -import java.util.HashMap; -import java.util.Map; - @BlockEntity(name = "Skull", regex = "skull") public class SkullBlockEntityTranslator extends BlockEntityTranslator implements RequiresBlockState { - @Override public boolean isBlock(int blockState) { return BlockStateValues.getSkullVariant(blockState) != -1; } @Override - public Map translateTag(com.github.steveice10.opennbt.tag.builtin.CompoundTag tag, int blockState) { - Map tags = new HashMap<>(); + public void translateTag(NbtMapBuilder builder, CompoundTag tag, int blockState) { byte skullVariant = BlockStateValues.getSkullVariant(blockState); float rotation = BlockStateValues.getSkullRotation(blockState) * 22.5f; // Just in case... - if (skullVariant == -1) skullVariant = 0; - tags.put("Rotation", rotation); - tags.put("SkullType", skullVariant); - return tags; + if (skullVariant == -1) { + skullVariant = 0; + } + builder.put("Rotation", rotation); + builder.put("SkullType", skullVariant); } - } diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/SpawnerBlockEntityTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/SpawnerBlockEntityTranslator.java index 2601e3de9..38507f54a 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/SpawnerBlockEntityTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/SpawnerBlockEntityTranslator.java @@ -26,63 +26,58 @@ package org.geysermc.connector.network.translators.world.block.entity; import com.github.steveice10.opennbt.tag.builtin.CompoundTag; +import com.github.steveice10.opennbt.tag.builtin.Tag; +import com.nukkitx.nbt.NbtMapBuilder; import org.geysermc.connector.entity.type.EntityType; -import java.util.HashMap; -import java.util.Map; - @BlockEntity(name = "MobSpawner", regex = "mob_spawner") public class SpawnerBlockEntityTranslator extends BlockEntityTranslator { - @Override - public Map translateTag(CompoundTag tag, int blockState) { - Map tags = new HashMap<>(); + public void translateTag(NbtMapBuilder builder, CompoundTag tag, int blockState) { + Tag current; - if (tag.get("MaxNearbyEntities") != null) { - tags.put("MaxNearbyEntities", (short) tag.get("MaxNearbyEntities").getValue()); + if ((current = tag.get("MaxNearbyEntities")) != null) { + builder.put("MaxNearbyEntities", current.getValue()); } - if (tag.get("RequiredPlayerRange") != null) { - tags.put("RequiredPlayerRange", (short) tag.get("RequiredPlayerRange").getValue()); + if ((current = tag.get("RequiredPlayerRange")) != null) { + builder.put("RequiredPlayerRange", current.getValue()); } - if (tag.get("SpawnCount") != null) { - tags.put("SpawnCount", (short) tag.get("SpawnCount").getValue()); + if ((current = tag.get("SpawnCount")) != null) { + builder.put("SpawnCount", current.getValue()); } - if (tag.get("MaxSpawnDelay") != null) { - tags.put("MaxSpawnDelay", (short) tag.get("MaxSpawnDelay").getValue()); + if ((current = tag.get("MaxSpawnDelay")) != null) { + builder.put("MaxSpawnDelay", current.getValue()); } - if (tag.get("Delay") != null) { - tags.put("Delay", (short) tag.get("Delay").getValue()); + if ((current = tag.get("Delay")) != null) { + builder.put("Delay", current.getValue()); } - if (tag.get("SpawnRange") != null) { - tags.put("SpawnRange", (short) tag.get("SpawnRange").getValue()); + if ((current = tag.get("SpawnRange")) != null) { + builder.put("SpawnRange", current.getValue()); } - if (tag.get("MinSpawnDelay") != null) { - tags.put("MinSpawnDelay", (short) tag.get("MinSpawnDelay").getValue()); + if ((current = tag.get("MinSpawnDelay")) != null) { + builder.put("MinSpawnDelay", current.getValue()); } - if (tag.get("SpawnData") != null) { - CompoundTag spawnData = tag.get("SpawnData"); + CompoundTag spawnData = tag.get("SpawnData"); + if (spawnData != null) { String entityID = (String) spawnData.get("id").getValue(); - tags.put("EntityIdentifier", entityID); + builder.put("EntityIdentifier", entityID); EntityType type = EntityType.getFromIdentifier(entityID); if (type != null) { - tags.put("DisplayEntityWidth", type.getWidth()); - tags.put("DisplayEntityHeight", type.getHeight()); - tags.put("DisplayEntityScale", 1.0f); + builder.put("DisplayEntityWidth", type.getWidth()); + builder.put("DisplayEntityHeight", type.getHeight()); + builder.put("DisplayEntityScale", 1.0f); } } - tags.put("id", "MobSpawner"); - tags.put("isMovable", (byte) 1); - - return tags; + builder.put("id", "MobSpawner"); + builder.put("isMovable", (byte) 1); } - } diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/world/chunk/BlockStorage.java b/connector/src/main/java/org/geysermc/connector/network/translators/world/chunk/BlockStorage.java index d8cd75206..f195394db 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/world/chunk/BlockStorage.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/world/chunk/BlockStorage.java @@ -30,6 +30,7 @@ import io.netty.buffer.ByteBuf; import it.unimi.dsi.fastutil.ints.IntArrayList; import it.unimi.dsi.fastutil.ints.IntList; import lombok.Getter; +import org.geysermc.connector.network.translators.world.block.BlockTranslator; import org.geysermc.connector.network.translators.world.chunk.bitarray.BitArray; import org.geysermc.connector.network.translators.world.chunk.bitarray.BitArrayVersion; @@ -50,7 +51,7 @@ public class BlockStorage { public BlockStorage(BitArrayVersion version) { this.bitArray = version.createArray(SIZE); this.palette = new IntArrayList(16); - this.palette.add(0); // Air is at the start of every palette. + this.palette.add(BlockTranslator.BEDROCK_AIR_ID); // Air is at the start of every palette and controls what the default block is in second-layer non-air block spaces. } public BlockStorage(BitArray bitArray, IntList palette) { diff --git a/connector/src/main/java/org/geysermc/connector/scoreboard/Objective.java b/connector/src/main/java/org/geysermc/connector/scoreboard/Objective.java index 3accbc120..419e99fd0 100644 --- a/connector/src/main/java/org/geysermc/connector/scoreboard/Objective.java +++ b/connector/src/main/java/org/geysermc/connector/scoreboard/Objective.java @@ -33,14 +33,16 @@ import java.util.Map; import java.util.concurrent.ConcurrentHashMap; @Getter -public class Objective { +public final class Objective { private final Scoreboard scoreboard; private final long id; private boolean active = true; @Setter private UpdateType updateType = UpdateType.ADD; + private String objectiveName; + private ScoreboardPosition displaySlot; private String displaySlotName; private String displayName = "unknown"; private int type = 0; // 0 = integer, 1 = heart @@ -67,39 +69,59 @@ public class Objective { public Objective(Scoreboard scoreboard, String objectiveName, ScoreboardPosition displaySlot, String displayName, int type) { this(scoreboard); this.objectiveName = objectiveName; + this.displaySlot = correctDisplaySlot(displaySlot); this.displaySlotName = translateDisplaySlot(displaySlot); this.displayName = displayName; this.type = type; } + private static String translateDisplaySlot(ScoreboardPosition displaySlot) { + switch (displaySlot) { + case BELOW_NAME: + return "belowname"; + case PLAYER_LIST: + return "list"; + default: + return "sidebar"; + } + } + + private static ScoreboardPosition correctDisplaySlot(ScoreboardPosition displaySlot) { + switch (displaySlot) { + case BELOW_NAME: + return ScoreboardPosition.BELOW_NAME; + case PLAYER_LIST: + return ScoreboardPosition.PLAYER_LIST; + default: + return ScoreboardPosition.SIDEBAR; + } + } + public void registerScore(String id, int score) { if (!scores.containsKey(id)) { - Score score1 = new Score(this, id) + long scoreId = scoreboard.getNextId().getAndIncrement(); + Score scoreObject = new Score(scoreId, id) .setScore(score) .setTeam(scoreboard.getTeamFor(id)) .setUpdateType(UpdateType.ADD); - scores.put(id, score1); + scores.put(id, scoreObject); } } public void setScore(String id, int score) { - if (scores.containsKey(id)) { - scores.get(id).setScore(score); + Score stored = scores.get(id); + if (stored != null) { + stored.setScore(score) + .setUpdateType(UpdateType.UPDATE); return; } registerScore(id, score); } - public int getScore(String id) { - if (scores.containsKey(id)) { - return scores.get(id).getScore(); - } - return 0; - } - public void removeScore(String id) { - if (scores.containsKey(id)) { - scores.get(id).setUpdateType(UpdateType.REMOVE); + Score stored = scores.get(id); + if (stored != null) { + stored.setUpdateType(UpdateType.REMOVE); } } @@ -129,6 +151,7 @@ public class Objective { public void setActive(ScoreboardPosition displaySlot) { if (!active) { active = true; + this.displaySlot = correctDisplaySlot(displaySlot); displaySlotName = translateDisplaySlot(displaySlot); } } @@ -136,15 +159,4 @@ public class Objective { public void removed() { scores = null; } - - private static String translateDisplaySlot(ScoreboardPosition displaySlot) { - switch (displaySlot) { - case BELOW_NAME: - return "belowname"; - case PLAYER_LIST: - return "list"; - default: - return "sidebar"; - } - } } diff --git a/connector/src/main/java/org/geysermc/connector/scoreboard/Score.java b/connector/src/main/java/org/geysermc/connector/scoreboard/Score.java index 3dfd6ed38..e5f97b459 100644 --- a/connector/src/main/java/org/geysermc/connector/scoreboard/Score.java +++ b/connector/src/main/java/org/geysermc/connector/scoreboard/Score.java @@ -27,60 +27,107 @@ package org.geysermc.connector.scoreboard; import com.nukkitx.protocol.bedrock.data.ScoreInfo; import lombok.Getter; -import lombok.Setter; import lombok.experimental.Accessors; @Getter @Accessors(chain = true) -public class Score { - private final Objective objective; - private ScoreInfo cachedInfo; +public final class Score { private final long id; - - @Setter - private UpdateType updateType = UpdateType.ADD; private final String name; - private Team team; - private int score; - @Setter - private int oldScore = Integer.MIN_VALUE; + private ScoreInfo cachedInfo; - public Score(Objective objective, String name) { - this.id = objective.getScoreboard().getNextId().getAndIncrement(); - this.objective = objective; + private ScoreData currentData; + private ScoreData cachedData; + + public Score(long id, String name) { + this.id = id; this.name = name; + this.currentData = new ScoreData(); } public String getDisplayName() { + Team team = cachedData.team; if (team != null) { - return team.getPrefix() + name + team.getSuffix(); + return team.getDisplayName(name); } return name; } public Score setScore(int score) { - this.score = score; - updateType = UpdateType.UPDATE; + currentData.score = score; return this; } + public Team getTeam() { + return currentData.team; + } + public Score setTeam(Team team) { - if (this.team != null && team != null) { - if (!this.team.equals(team)) { - this.team = team; - updateType = UpdateType.UPDATE; + if (currentData.team != null && team != null) { + if (!currentData.team.equals(team)) { + currentData.team = team; + currentData.updateType = UpdateType.UPDATE; } return this; } // simplified from (this.team != null && team == null) || (this.team == null && team != null) - if (this.team != null || team != null) { - this.team = team; - updateType = UpdateType.UPDATE; + if (currentData.team != null || team != null) { + currentData.team = team; + currentData.updateType = UpdateType.UPDATE; } return this; } - public void update() { - cachedInfo = new ScoreInfo(id, objective.getObjectiveName(), score, getDisplayName()); + public UpdateType getUpdateType() { + return cachedData != null ? cachedData.updateType : currentData.updateType; + } + + public Score setUpdateType(UpdateType updateType) { + if (updateType != UpdateType.NOTHING) { + currentData.updateTime = System.currentTimeMillis(); + } + currentData.updateType = updateType; + return this; + } + + public boolean shouldUpdate() { + return cachedData == null || currentData.updateTime > cachedData.updateTime || + (currentData.team != null && currentData.team.shouldUpdate()); + } + + public void update(String objectiveName) { + if (cachedData == null) { + cachedData = new ScoreData(); + cachedData.updateType = UpdateType.ADD; + if (currentData.updateType == UpdateType.REMOVE) { + cachedData.updateType = UpdateType.REMOVE; + } + } else { + cachedData.updateType = currentData.updateType; + } + + cachedData.updateTime = currentData.updateTime; + cachedData.team = currentData.team; + cachedData.score = currentData.score; + + String name = this.name; + if (cachedData.team != null) { + cachedData.team.prepareUpdate(); + name = cachedData.team.getDisplayName(name); + } + cachedInfo = new ScoreInfo(id, objectiveName, cachedData.score, name); + } + + @Getter + public static final class ScoreData { + protected UpdateType updateType; + protected long updateTime; + + private Team team; + private int score; + + protected ScoreData() { + updateType = UpdateType.ADD; + } } } diff --git a/connector/src/main/java/org/geysermc/connector/scoreboard/Scoreboard.java b/connector/src/main/java/org/geysermc/connector/scoreboard/Scoreboard.java index 732a056eb..8eaa2e277 100644 --- a/connector/src/main/java/org/geysermc/connector/scoreboard/Scoreboard.java +++ b/connector/src/main/java/org/geysermc/connector/scoreboard/Scoreboard.java @@ -43,7 +43,7 @@ import java.util.concurrent.atomic.AtomicLong; import static org.geysermc.connector.scoreboard.UpdateType.*; @Getter -public class Scoreboard { +public final class Scoreboard { private final GeyserSession session; private final GeyserLogger logger; private final AtomicLong nextId = new AtomicLong(0); @@ -51,7 +51,8 @@ public class Scoreboard { private final Map objectives = new ConcurrentHashMap<>(); private final Map teams = new HashMap<>(); - private int lastScoreCount = 0; + private int lastAddScoreCount = 0; + private int lastRemoveScoreCount = 0; public Scoreboard(GeyserSession session) { this.session = session; @@ -59,19 +60,21 @@ public class Scoreboard { } public Objective registerNewObjective(String objectiveId, boolean active) { - if (active || objectives.containsKey(objectiveId)) { - return objectives.get(objectiveId); + Objective objective = objectives.get(objectiveId); + if (active || objective != null) { + return objective; } - Objective objective = new Objective(this, objectiveId); + objective = new Objective(this, objectiveId); objectives.put(objectiveId, objective); return objective; } - public Objective registerNewObjective(String objectiveId, ScoreboardPosition displaySlot) { + public Objective displayObjective(String objectiveId, ScoreboardPosition displaySlot) { Objective objective = objectives.get(objectiveId); if (objective != null) { if (!objective.isActive()) { objective.setActive(displaySlot); + removeOldObjectives(objective); return objective; } despawnObjective(objective); @@ -79,9 +82,21 @@ public class Scoreboard { objective = new Objective(this, objectiveId, displaySlot, "unknown", 0); objectives.put(objectiveId, objective); + removeOldObjectives(objective); return objective; } + private void removeOldObjectives(Objective newObjective) { + for (Objective next : objectives.values()) { + if (next.getId() == newObjective.getId()) { + continue; + } + if (next.getDisplaySlot() == newObjective.getDisplaySlot()) { + next.setUpdateType(REMOVE); + } + } + } + public Team registerNewTeam(String teamName, Set players) { Team team = teams.get(teamName); if (team != null) { @@ -89,7 +104,7 @@ public class Scoreboard { return team; } - team = new Team(this, teamName).setEntities(players); + team = new Team(this, teamName).addEntities(players); teams.put(teamName, team); return team; } @@ -117,8 +132,9 @@ public class Scoreboard { } public void onUpdate() { - List addScores = new ArrayList<>(getLastScoreCount()); - List removeScores = new ArrayList<>(getLastScoreCount()); + List addScores = new ArrayList<>(getLastAddScoreCount()); + List removeScores = new ArrayList<>(getLastRemoveScoreCount()); + List removedObjectives = new ArrayList<>(); for (Objective objective : objectives.values()) { if (!objective.isActive()) { @@ -129,65 +145,58 @@ public class Scoreboard { // hearts can't hold teams, so we treat them differently if (objective.getType() == 1) { for (Score score : objective.getScores().values()) { - if (score.getUpdateType() == NOTHING) { - continue; - } + boolean update = score.shouldUpdate(); - boolean update = score.getUpdateType() == UPDATE; if (update) { - score.update(); + score.update(objective.getObjectiveName()); } - if (score.getUpdateType() == ADD || update) { + if (score.getUpdateType() != REMOVE && update) { addScores.add(score.getCachedInfo()); } - if (score.getUpdateType() == REMOVE || update) { + if (score.getUpdateType() != ADD && update) { removeScores.add(score.getCachedInfo()); } } continue; } - boolean globalUpdate = objective.getUpdateType() == UPDATE; - boolean globalAdd = objective.getUpdateType() == ADD; - boolean globalRemove = objective.getUpdateType() == REMOVE; + boolean objectiveUpdate = objective.getUpdateType() == UPDATE; + boolean objectiveAdd = objective.getUpdateType() == ADD; + boolean objectiveRemove = objective.getUpdateType() == REMOVE; for (Score score : objective.getScores().values()) { Team team = score.getTeam(); - boolean add = globalAdd || globalUpdate; - boolean remove = globalRemove; - boolean teamChanged = false; + boolean add = objectiveAdd || objectiveUpdate; + boolean remove = false; if (team != null) { if (team.getUpdateType() == REMOVE || !team.hasEntity(score.getName())) { score.setTeam(null); - teamChanged = true; + add = true; + remove = true; } - - teamChanged |= team.getUpdateType() == UPDATE; - - add |= team.getUpdateType() == ADD || team.getUpdateType() == UPDATE; - remove |= team.getUpdateType() != NOTHING; } - add |= score.getUpdateType() == ADD || score.getUpdateType() == UPDATE; - remove |= score.getUpdateType() == REMOVE || score.getUpdateType() == UPDATE; + add |= score.shouldUpdate(); + remove |= score.shouldUpdate(); - if (score.getUpdateType() == REMOVE || globalRemove) { + if (score.getUpdateType() == REMOVE || objectiveRemove) { add = false; } - if (score.getUpdateType() == ADD) { + if (score.getUpdateType() == ADD || objectiveRemove) { remove = false; } - if (score.getUpdateType() == ADD || score.getUpdateType() == UPDATE || teamChanged) { - score.update(); + if (score.shouldUpdate()) { + score.update(objective.getObjectiveName()); } if (add) { addScores.add(score.getCachedInfo()); } + if (remove) { removeScores.add(score.getCachedInfo()); } @@ -200,17 +209,17 @@ public class Scoreboard { score.setUpdateType(NOTHING); } - if (globalRemove || globalUpdate) { + if (objectiveRemove) { + removedObjectives.add(objective); + } + + if (objectiveUpdate) { RemoveObjectivePacket removeObjectivePacket = new RemoveObjectivePacket(); removeObjectivePacket.setObjectiveId(objective.getObjectiveName()); session.sendUpstreamPacket(removeObjectivePacket); - if (globalRemove) { - objectives.remove(objective.getObjectiveName()); // now we can deregister - objective.removed(); - } } - if ((globalAdd || globalUpdate) && !globalRemove) { + if ((objectiveAdd || objectiveUpdate) && !objectiveRemove) { SetDisplayObjectivePacket displayObjectivePacket = new SetDisplayObjectivePacket(); displayObjectivePacket.setObjectiveId(objective.getObjectiveName()); displayObjectivePacket.setDisplayName(objective.getDisplayName()); @@ -223,6 +232,20 @@ public class Scoreboard { objective.setUpdateType(NOTHING); } + Iterator teamIterator = teams.values().iterator(); + while (teamIterator.hasNext()) { + Team current = teamIterator.next(); + + switch (current.getUpdateType()) { + case ADD: + case UPDATE: + current.markUpdated(); + break; + case REMOVE: + teamIterator.remove(); + } + } + if (!removeScores.isEmpty()) { SetScorePacket setScorePacket = new SetScorePacket(); setScorePacket.setAction(SetScorePacket.Action.REMOVE); @@ -237,37 +260,27 @@ public class Scoreboard { session.sendUpstreamPacket(setScorePacket); } - lastScoreCount = addScores.size(); + // prevents crashes in some cases + for (Objective objective : removedObjectives) { + despawnObjective(objective); + } + + lastAddScoreCount = addScores.size(); + lastRemoveScoreCount = removeScores.size(); } public void despawnObjective(Objective objective) { + objectives.remove(objective.getObjectiveName()); + objective.removed(); + RemoveObjectivePacket removeObjectivePacket = new RemoveObjectivePacket(); removeObjectivePacket.setObjectiveId(objective.getObjectiveName()); session.sendUpstreamPacket(removeObjectivePacket); - objectives.remove(objective.getDisplayName()); - - List toRemove = new ArrayList<>(); - for (String identifier : objective.getScores().keySet()) { - Score score = objective.getScores().get(identifier); - toRemove.add(new ScoreInfo( - score.getId(), score.getObjective().getObjectiveName(), - 0, "" - )); - } - - objective.removed(); - - if (!toRemove.isEmpty()) { - SetScorePacket setScorePacket = new SetScorePacket(); - setScorePacket.setAction(SetScorePacket.Action.REMOVE); - setScorePacket.setInfos(toRemove); - session.sendUpstreamPacket(setScorePacket); - } } public Team getTeamFor(String entity) { for (Team team : teams.values()) { - if (team.getEntities().contains(entity)) { + if (team.hasEntity(entity)) { return team; } } diff --git a/connector/src/main/java/org/geysermc/connector/scoreboard/Team.java b/connector/src/main/java/org/geysermc/connector/scoreboard/Team.java index a073e2e99..377a15f1e 100644 --- a/connector/src/main/java/org/geysermc/connector/scoreboard/Team.java +++ b/connector/src/main/java/org/geysermc/connector/scoreboard/Team.java @@ -28,6 +28,7 @@ package org.geysermc.connector.scoreboard; import com.github.steveice10.mc.protocol.data.game.scoreboard.NameTagVisibility; import com.github.steveice10.mc.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; @@ -36,62 +37,90 @@ import java.util.ArrayList; import java.util.List; import java.util.Set; -@Getter @Setter +@Getter @Accessors(chain = true) -public class Team { +public final class Team { private final Scoreboard scoreboard; private final String id; - private UpdateType updateType = UpdateType.ADD; - private String name; + @Getter(AccessLevel.NONE) + private final Set entities; + @Setter private NameTagVisibility nameTagVisibility; + @Setter private TeamColor color; - private NameTagVisibility nameTagVisibility; - private String prefix; - private TeamColor color; - private String suffix; - private Set entities = new ObjectOpenHashSet<>(); + private TeamData currentData; + private TeamData cachedData; + + private boolean updating; public Team(Scoreboard scoreboard, String id) { this.scoreboard = scoreboard; this.id = id; + currentData = new TeamData(); + entities = new ObjectOpenHashSet<>(); } - public void addEntities(String... names) { - List added = new ArrayList<>(); - for (String name : names) { - if (entities.add(name)) { - added.add(name); - } + private void checkAddedEntities(List added) { + if (added.size() == 0) { + return; } - setUpdateType(UpdateType.UPDATE); + // we don't have to change the updateType, + // because the scores itself need updating, not the team for (Objective objective : scoreboard.getObjectives().values()) { - for (Score score : objective.getScores().values()) { - if (added.contains(score.getName())) { + for (String addedEntity : added) { + Score score = objective.getScores().get(addedEntity); + if (score != null) { score.setTeam(this); } } } } + public Team addEntities(String... names) { + List added = new ArrayList<>(); + for (String name : names) { + if (entities.add(name)) { + added.add(name); + } + } + checkAddedEntities(added); + return this; + } + + public Team addEntities(Set names) { + List added = new ArrayList<>(); + for (String name : names) { + if (entities.add(name)) { + added.add(name); + } + } + checkAddedEntities(added); + return this; + } + public void removeEntities(String... names) { for (String name : names) { entities.remove(name); } - setUpdateType(UpdateType.UPDATE); } 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)) { - this.prefix = ""; + currentData.prefix = ""; return this; } - this.prefix = prefix; + currentData.prefix = prefix; return this; } @@ -99,15 +128,92 @@ public class Team { // replace "null" to an empty string, // we do this here to improve the performance of Score#getDisplayName if (suffix.length() == 4 && "null".equals(suffix)) { - this.suffix = ""; + currentData.suffix = ""; return this; } - this.suffix = suffix; + 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.updateTime > cachedData.updateTime; + } + + 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; + } + + cachedData.updateTime = currentData.updateTime; + cachedData.name = currentData.name; + cachedData.prefix = currentData.prefix; + cachedData.suffix = currentData.suffix; + } + + public UpdateType getUpdateType() { + return cachedData != null ? cachedData.updateType : currentData.updateType; + } + + public Team setUpdateType(UpdateType updateType) { + if (updateType != UpdateType.NOTHING) { + currentData.updateTime = System.currentTimeMillis(); + } + currentData.updateType = updateType; + return this; + } + + public boolean isVisibleFor(String entity) { + switch (nameTagVisibility) { + case HIDE_FOR_OTHER_TEAMS: + return hasEntity(entity); + case HIDE_FOR_OWN_TEAM: + return !hasEntity(entity); + case ALWAYS: + return true; + case NEVER: + return false; + } + return true; + } + @Override public int hashCode() { return id.hashCode(); } + + @Getter + public static final class TeamData { + protected UpdateType updateType; + protected long updateTime; + + protected String name; + protected String prefix; + protected String suffix; + + protected TeamData() { + updateType = UpdateType.ADD; + } + + public String getDisplayName(String score) { + return prefix + score + suffix; + } + } } diff --git a/connector/src/main/java/org/geysermc/connector/utils/BlockEntityUtils.java b/connector/src/main/java/org/geysermc/connector/utils/BlockEntityUtils.java index 0ae31b333..e8fd82918 100644 --- a/connector/src/main/java/org/geysermc/connector/utils/BlockEntityUtils.java +++ b/connector/src/main/java/org/geysermc/connector/utils/BlockEntityUtils.java @@ -33,17 +33,17 @@ import org.geysermc.connector.network.session.GeyserSession; import org.geysermc.connector.network.translators.world.block.entity.BlockEntityTranslator; public class BlockEntityUtils { - private static final BlockEntityTranslator EMPTY_TRANSLATOR = BlockEntityTranslator.BLOCK_ENTITY_TRANSLATORS.get("Empty"); public static String getBedrockBlockEntityId(String id) { // These are the only exceptions when it comes to block entity ids - if (BlockEntityTranslator.BLOCK_ENTITY_TRANSLATIONS.containsKey(id)) { - return BlockEntityTranslator.BLOCK_ENTITY_TRANSLATIONS.get(id); + String value = BlockEntityTranslator.BLOCK_ENTITY_TRANSLATIONS.get(id); + if (value != null) { + return value; } id = id.replace("minecraft:", "") - .replace("_", " "); + .replace("_", " "); // Split at every space or capital letter - for the latter, some legacy Java block entity tags are the correct format already String[] words; if (!id.toUpperCase().equals(id)) { // Otherwise we get [S, K, U, L, L] @@ -60,11 +60,10 @@ public class BlockEntityUtils { public static BlockEntityTranslator getBlockEntityTranslator(String name) { BlockEntityTranslator blockEntityTranslator = BlockEntityTranslator.BLOCK_ENTITY_TRANSLATORS.get(name); - if (blockEntityTranslator == null) { - return EMPTY_TRANSLATOR; + if (blockEntityTranslator != null) { + return blockEntityTranslator; } - - return blockEntityTranslator; + return EMPTY_TRANSLATOR; } public static void updateBlockEntity(GeyserSession session, NbtMap blockEntity, Position position) { diff --git a/connector/src/main/java/org/geysermc/connector/utils/ChunkUtils.java b/connector/src/main/java/org/geysermc/connector/utils/ChunkUtils.java index a63eeb424..005a4960e 100644 --- a/connector/src/main/java/org/geysermc/connector/utils/ChunkUtils.java +++ b/connector/src/main/java/org/geysermc/connector/utils/ChunkUtils.java @@ -28,6 +28,7 @@ package org.geysermc.connector.utils; import com.github.steveice10.mc.protocol.data.game.chunk.BitStorage; import com.github.steveice10.mc.protocol.data.game.chunk.Chunk; import com.github.steveice10.mc.protocol.data.game.chunk.Column; +import com.github.steveice10.mc.protocol.data.game.chunk.palette.GlobalPalette; import com.github.steveice10.mc.protocol.data.game.chunk.palette.Palette; import com.github.steveice10.mc.protocol.data.game.entity.metadata.Position; import com.github.steveice10.opennbt.tag.builtin.CompoundTag; @@ -65,16 +66,17 @@ import java.io.ByteArrayOutputStream; import java.io.IOException; import java.util.ArrayList; import java.util.BitSet; -import java.util.Collections; import java.util.List; -import static org.geysermc.connector.network.translators.world.block.BlockTranslator.*; +import static org.geysermc.connector.network.translators.world.block.BlockTranslator.JAVA_AIR_ID; +import static org.geysermc.connector.network.translators.world.block.BlockTranslator.BEDROCK_AIR_ID; +import static org.geysermc.connector.network.translators.world.block.BlockTranslator.BEDROCK_WATER_ID; @UtilityClass public class ChunkUtils { - /** - * Temporarily stores positions of BlockState values that are needed for certain block entities actively + * Temporarily stores positions of BlockState values that are needed for certain block entities actively. + * Not used if cache chunks is enabled */ public static final Object2IntMap CACHED_BLOCK_ENTITIES = new Object2IntOpenHashMap<>(); @@ -107,7 +109,7 @@ public class ChunkUtils { ChunkSection[] sections = new ChunkSection[javaSections.length]; // Temporarily stores compound tags of Bedrock-only block entities - List bedrockOnlyBlockEntities = Collections.emptyList(); + List bedrockOnlyBlockEntities = new ArrayList<>(); BitSet waterloggedPaletteIds = new BitSet(); BitSet pistonOrFlowerPaletteIds = new BitSet(); @@ -155,6 +157,33 @@ public class ChunkUtils { } Palette javaPalette = javaSection.getPalette(); + BitStorage javaData = javaSection.getStorage(); + + if (javaPalette instanceof GlobalPalette) { + // As this is the global palette, simply iterate through the whole chunk section once + ChunkSection section = new ChunkSection(); + for (int yzx = 0; yzx < BlockStorage.SIZE; yzx++) { + int javaId = javaData.get(yzx); + int bedrockId = BlockTranslator.getBedrockBlockId(javaId); + int xzy = indexYZXtoXZY(yzx); + section.getBlockStorageArray()[0].setFullBlock(xzy, bedrockId); + + if (BlockTranslator.isWaterlogged(javaId)) { + section.getBlockStorageArray()[1].setFullBlock(xzy, BEDROCK_WATER_ID); + } + + // Check if block is piston or flower to see if we'll need to create additional block entities, as they're only block entities in Bedrock + if (BlockStateValues.getFlowerPotValues().containsKey(javaId) || BlockStateValues.getPistonValues().containsKey(javaId)) { + bedrockOnlyBlockEntities.add(BedrockOnlyBlockEntity.getTag( + Vector3i.from((column.getX() << 4) + (yzx & 0xF), (sectionY << 4) + ((yzx >> 8) & 0xF), (column.getZ() << 4) + ((yzx >> 4) & 0xF)), + javaId + )); + } + } + sections[sectionY] = section; + continue; + } + IntList bedrockPalette = new IntArrayList(javaPalette.size()); waterloggedPaletteIds.clear(); pistonOrFlowerPaletteIds.clear(); @@ -174,13 +203,10 @@ public class ChunkUtils { } } - BitStorage javaData = javaSection.getStorage(); - // Add Bedrock-exclusive block entities // We only if the palette contained any blocks that are Bedrock-exclusive block entities to avoid iterating through the whole block data // for no reason, as most sections will not contain any pistons or flower pots if (!pistonOrFlowerPaletteIds.isEmpty()) { - bedrockOnlyBlockEntities = new ArrayList<>(); for (int yzx = 0; yzx < BlockStorage.SIZE; yzx++) { int paletteId = javaData.get(yzx); if (pistonOrFlowerPaletteIds.get(paletteId)) { @@ -221,7 +247,7 @@ public class ChunkUtils { // V1 palette IntList layer1Palette = new IntArrayList(2); - layer1Palette.add(0); // Air + layer1Palette.add(BEDROCK_AIR_ID); // Air - see BlockStorage's constructor for more information layer1Palette.add(BEDROCK_WATER_ID); layers = new BlockStorage[]{ layer0, new BlockStorage(BitArrayVersion.V1.createArray(BlockStorage.SIZE, layer1Data), layer1Palette) }; @@ -293,18 +319,37 @@ public class ChunkUtils { } } + /** + * Sends a block update to the Bedrock client. If chunk caching is enabled and the platform is not Spigot, this also + * adds that block to the cache. + * @param session the Bedrock session to send/register the block to + * @param blockState the Java block state of the block + * @param position the position of the block + */ public static void updateBlock(GeyserSession session, int blockState, Position position) { Vector3i pos = Vector3i.from(position.getX(), position.getY(), position.getZ()); updateBlock(session, blockState, pos); } + /** + * Sends a block update to the Bedrock client. If chunk caching is enabled and the platform is not Spigot, this also + * adds that block to the cache. + * @param session the Bedrock session to send/register the block to + * @param blockState the Java block state of the block + * @param position the position of the block + */ public static void updateBlock(GeyserSession session, int blockState, Vector3i position) { // Checks for item frames so they aren't tripped up and removed - if (ItemFrameEntity.positionContainsItemFrame(session, position) && blockState == AIR) { - ((ItemFrameEntity) session.getEntityCache().getEntityByJavaId(ItemFrameEntity.getItemFrameEntityId(session, position))).updateBlock(session); - return; - } else if (ItemFrameEntity.positionContainsItemFrame(session, position)) { - Entity entity = session.getEntityCache().getEntityByJavaId(ItemFrameEntity.getItemFrameEntityId(session, position)); + long frameEntityId = ItemFrameEntity.getItemFrameEntityId(session, position); + if (frameEntityId != -1) { + // TODO: Very occasionally the item frame doesn't sync up when destroyed + Entity entity = session.getEntityCache().getEntityByJavaId(frameEntityId); + if (blockState == JAVA_AIR_ID && entity != null) { // Item frame is still present and no block overrides that; refresh it + ((ItemFrameEntity) entity).updateBlock(session); + return; + } + + // Otherwise the item frame is gone if (entity != null) { session.getEntityCache().removeEntity(entity, false); } else { @@ -328,7 +373,7 @@ public class ChunkUtils { if (BlockTranslator.isWaterlogged(blockState)) { waterPacket.setRuntimeId(BEDROCK_WATER_ID); } else { - waterPacket.setRuntimeId(0); + waterPacket.setRuntimeId(BEDROCK_AIR_ID); } session.sendUpstreamPacket(waterPacket); @@ -342,7 +387,10 @@ public class ChunkUtils { ((BedrockOnlyBlockEntity) requiresBlockState).updateBlock(session, blockState, position); break; } - CACHED_BLOCK_ENTITIES.put(new Position(position.getX(), position.getY(), position.getZ()), blockState); + if (!session.getConnector().getConfig().isCacheChunks()) { + // Blocks aren't saved to a chunk cache; resort to this smaller cache + CACHED_BLOCK_ENTITIES.put(new Position(position.getX(), position.getY(), position.getZ()), blockState); + } break; //No block will be a part of two classes } } diff --git a/connector/src/main/java/org/geysermc/connector/utils/DimensionUtils.java b/connector/src/main/java/org/geysermc/connector/utils/DimensionUtils.java index 7b283e9cb..f8083108c 100644 --- a/connector/src/main/java/org/geysermc/connector/utils/DimensionUtils.java +++ b/connector/src/main/java/org/geysermc/connector/utils/DimensionUtils.java @@ -41,17 +41,29 @@ public class DimensionUtils { // Changes if the above-bedrock Nether building workaround is applied private static int BEDROCK_NETHER_ID = 1; - // Static references to all vanilla dimensions + /** + * String reference to vanilla Java overworld dimension identifier + */ public static final String OVERWORLD = "minecraft:overworld"; + /** + * String reference to vanilla Java nether dimension identifier + */ public static final String NETHER = "minecraft:the_nether"; + /** + * String reference to vanilla Java end dimension identifier + */ public static final String THE_END = "minecraft:the_end"; public static void switchDimension(GeyserSession session, String javaDimension) { int bedrockDimension = javaToBedrock(javaDimension); Entity player = session.getPlayerEntity(); - if (javaDimension.equals(player.getDimension())) + if (javaDimension.equals(session.getDimension())) return; + if (session.getMovementSendIfIdle() != null) { + session.getMovementSendIfIdle().cancel(true); + } + session.getEntityCache().removeAllEntities(); session.getItemFrameCache().clear(); if (session.getPendingDimSwitches().getAndIncrement() > 0) { @@ -65,7 +77,7 @@ public class DimensionUtils { changeDimensionPacket.setRespawn(true); changeDimensionPacket.setPosition(pos.toFloat()); session.sendUpstreamPacket(changeDimensionPacket); - player.setDimension(javaDimension); + session.setDimension(javaDimension); player.setPosition(pos.toFloat()); session.setSpawned(false); session.setLastChunkPosition(null); @@ -111,23 +123,30 @@ public class DimensionUtils { /** * Determines the new dimension based on the {@link CompoundTag} sent by either the {@link com.github.steveice10.mc.protocol.packet.ingame.server.ServerJoinGamePacket} * or {@link com.github.steveice10.mc.protocol.packet.ingame.server.ServerRespawnPacket}. + * * @param dimensionTag the packet's dimension tag. * @return the dimension identifier. */ public static String getNewDimension(CompoundTag dimensionTag) { if (dimensionTag == null || dimensionTag.isEmpty()) { GeyserConnector.getInstance().getLogger().debug("Dimension tag was null or empty."); - return "minecraft:overworld"; + return OVERWORLD; } if (dimensionTag.getValue().get("effects") != null) { return ((StringTag) dimensionTag.getValue().get("effects")).getValue(); } GeyserConnector.getInstance().getLogger().debug("Effects portion of the tag was null or empty."); - return "minecraft:overworld"; + return OVERWORLD; } - public static void changeBedrockNetherId() { + /** + * 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 = 2; + BEDROCK_NETHER_ID = isAboveNetherBedrockBuilding ? 2 : 1; } } diff --git a/connector/src/main/java/org/geysermc/connector/utils/FileUtils.java b/connector/src/main/java/org/geysermc/connector/utils/FileUtils.java index 63255cfa0..0b2b132ab 100644 --- a/connector/src/main/java/org/geysermc/connector/utils/FileUtils.java +++ b/connector/src/main/java/org/geysermc/connector/utils/FileUtils.java @@ -159,7 +159,8 @@ public class FileUtils { } /** - * Calculate the SHA256 hash of the resource pack file + * Calculate the SHA256 hash of a file + * * @param file File to calculate the hash for * @return A byte[] representation of the hash */ @@ -175,6 +176,24 @@ public class FileUtils { return sha256; } + /** + * Calculate the SHA1 hash of a file + * + * @param file File to calculate the hash for + * @return A byte[] representation of the hash + */ + public static byte[] calculateSHA1(File file) { + byte[] sha1; + + try { + sha1 = MessageDigest.getInstance("SHA-1").digest(Files.readAllBytes(file.toPath())); + } catch (Exception e) { + throw new RuntimeException("Could not calculate pack hash", e); + } + + return sha1; + } + /** * Get the stored reflection data for a given path * diff --git a/connector/src/main/java/org/geysermc/connector/utils/LocaleUtils.java b/connector/src/main/java/org/geysermc/connector/utils/LocaleUtils.java index 11e81eee1..4e9e4b003 100644 --- a/connector/src/main/java/org/geysermc/connector/utils/LocaleUtils.java +++ b/connector/src/main/java/org/geysermc/connector/utils/LocaleUtils.java @@ -47,7 +47,7 @@ public class LocaleUtils { private static final Map ASSET_MAP = new HashMap<>(); - private static String smallestURL = ""; + private static VersionDownload clientJarInfo; static { // Create the locales folder @@ -87,9 +87,8 @@ public class LocaleUtils { // Get the client jar for use when downloading the en_us locale GeyserConnector.getInstance().getLogger().debug(GeyserConnector.JSON_MAPPER.writeValueAsString(versionInfo.getDownloads())); - VersionDownload download = versionInfo.getDownloads().get("client"); - GeyserConnector.getInstance().getLogger().debug(GeyserConnector.JSON_MAPPER.writeValueAsString(download)); - smallestURL = download.getUrl(); + clientJarInfo = versionInfo.getDownloads().get("client"); + GeyserConnector.getInstance().getLogger().debug(GeyserConnector.JSON_MAPPER.writeValueAsString(clientJarInfo)); // Get the assets list JsonNode assets = GeyserConnector.JSON_MAPPER.readTree(WebUtils.getBody(versionInfo.getAssetIndex().getUrl())).get("objects"); @@ -136,8 +135,28 @@ public class LocaleUtils { // Check if we have already downloaded the locale file if (localeFile.exists()) { - GeyserConnector.getInstance().getLogger().debug("Locale already downloaded: " + locale); - return; + String curHash = ""; + String targetHash = ""; + + if (locale.equals("en_us")) { + try { + Path hashFile = localeFile.getParentFile().toPath().resolve("en_us.hash"); + if (hashFile.toFile().exists()) { + curHash = String.join("", Files.readAllLines(hashFile)); + } + } catch (IOException ignored) { } + targetHash = clientJarInfo.getSha1(); + } else { + curHash = byteArrayToHexString(FileUtils.calculateSHA1(localeFile)); + targetHash = ASSET_MAP.get("minecraft/lang/" + locale + ".json").getHash(); + } + + if (!curHash.equals(targetHash)) { + GeyserConnector.getInstance().getLogger().debug("Locale out of date; re-downloading: " + locale); + } else { + GeyserConnector.getInstance().getLogger().debug("Locale already downloaded and up-to date: " + locale); + return; + } } // Create the en_us locale @@ -202,11 +221,11 @@ public class LocaleUtils { try { // Let the user know we are downloading the JAR GeyserConnector.getInstance().getLogger().info(LanguageUtils.getLocaleStringLog("geyser.locale.download.en_us")); - GeyserConnector.getInstance().getLogger().debug("Download URL: " + smallestURL); + GeyserConnector.getInstance().getLogger().debug("Download URL: " + clientJarInfo.getUrl()); // Download the smallest JAR (client or server) Path tmpFilePath = GeyserConnector.getInstance().getBootstrap().getConfigFolder().resolve("tmp_locale.jar"); - WebUtils.downloadFile(smallestURL, tmpFilePath.toString()); + WebUtils.downloadFile(clientJarInfo.getUrl(), tmpFilePath.toString()); // Load in the JAR as a zip and extract the file ZipFile localeJar = new ZipFile(tmpFilePath.toString()); @@ -227,6 +246,9 @@ public class LocaleUtils { fileStream.close(); localeJar.close(); + // Store the latest jar hash + FileUtils.writeFile(localeFile.getParentFile().toPath().resolve("en_us.hash").toString(), clientJarInfo.getSha1().toCharArray()); + // Delete the nolonger needed client/server jar Files.delete(tmpFilePath); } catch (Exception e) { @@ -243,17 +265,32 @@ public class LocaleUtils { */ public static String getLocaleString(String messageText, String locale) { Map localeStrings = LocaleUtils.LOCALE_MAPPINGS.get(locale.toLowerCase()); - if (localeStrings == null) - localeStrings = LocaleUtils.LOCALE_MAPPINGS.get(LanguageUtils.getDefaultLocale()); if (localeStrings == null) { - // Don't cause a NPE if the locale is STILL missing - GeyserConnector.getInstance().getLogger().debug("MISSING DEFAULT LOCALE: " + LanguageUtils.getDefaultLocale()); - return messageText; + localeStrings = LocaleUtils.LOCALE_MAPPINGS.get(LanguageUtils.getDefaultLocale()); + if (localeStrings == null) { + // Don't cause a NPE if the locale is STILL missing + GeyserConnector.getInstance().getLogger().debug("MISSING DEFAULT LOCALE: " + LanguageUtils.getDefaultLocale()); + return messageText; + } } return localeStrings.getOrDefault(messageText, messageText); } + /** + * Convert a byte array into a hex string + * + * @param b Byte array to convert + * @return The hex representation of the given byte array + */ + private static String byteArrayToHexString(byte[] b) { + StringBuilder result = new StringBuilder(); + for (byte value : b) { + result.append(Integer.toString((value & 0xff) + 0x100, 16).substring(1)); + } + return result.toString(); + } + public static void init() { // no-op } diff --git a/connector/src/main/java/org/geysermc/connector/utils/MessageUtils.java b/connector/src/main/java/org/geysermc/connector/utils/MessageUtils.java deleted file mode 100644 index b5a2bfdcc..000000000 --- a/connector/src/main/java/org/geysermc/connector/utils/MessageUtils.java +++ /dev/null @@ -1,494 +0,0 @@ -/* - * Copyright (c) 2019-2020 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.connector.utils; - -import com.github.steveice10.mc.protocol.data.game.scoreboard.TeamColor; -import com.github.steveice10.mc.protocol.data.message.Message; -import com.github.steveice10.mc.protocol.data.message.MessageSerializer; -import com.github.steveice10.mc.protocol.data.message.TextMessage; -import com.github.steveice10.mc.protocol.data.message.TranslationMessage; -import com.github.steveice10.mc.protocol.data.message.style.ChatColor; -import com.github.steveice10.mc.protocol.data.message.style.ChatFormat; -import com.github.steveice10.mc.protocol.data.message.style.MessageStyle; -import com.google.gson.JsonObject; -import com.google.gson.JsonParser; -import net.kyori.adventure.text.Component; -import net.kyori.adventure.text.serializer.gson.GsonComponentSerializer; -import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer; -import org.geysermc.connector.network.session.GeyserSession; - -import java.util.*; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -public class MessageUtils { - - private static final Map COLORS = new HashMap<>(); - private static final Map TEAM_COLORS = new HashMap<>(); - - static { - COLORS.put(ChatColor.BLACK, 0x000000); - COLORS.put(ChatColor.DARK_BLUE, 0x0000aa); - COLORS.put(ChatColor.DARK_GREEN, 0x00aa00); - COLORS.put(ChatColor.DARK_AQUA, 0x00aaaa); - COLORS.put(ChatColor.DARK_RED, 0xaa0000); - COLORS.put(ChatColor.DARK_PURPLE, 0xaa00aa); - COLORS.put(ChatColor.GOLD, 0xffaa00); - COLORS.put(ChatColor.GRAY, 0xaaaaaa); - COLORS.put(ChatColor.DARK_GRAY, 0x555555); - COLORS.put(ChatColor.BLUE, 0x5555ff); - COLORS.put(ChatColor.GREEN, 0x55ff55); - COLORS.put(ChatColor.AQUA, 0x55ffff); - COLORS.put(ChatColor.RED, 0xff5555); - COLORS.put(ChatColor.LIGHT_PURPLE, 0xff55ff); - COLORS.put(ChatColor.YELLOW, 0xffff55); - COLORS.put(ChatColor.WHITE, 0xffffff); - - TEAM_COLORS.put(TeamColor.BLACK, getColor(ChatColor.BLACK)); - TEAM_COLORS.put(TeamColor.DARK_BLUE, getColor(ChatColor.DARK_BLUE)); - TEAM_COLORS.put(TeamColor.DARK_GREEN, getColor(ChatColor.DARK_GREEN)); - TEAM_COLORS.put(TeamColor.DARK_AQUA, getColor(ChatColor.DARK_AQUA)); - TEAM_COLORS.put(TeamColor.DARK_RED, getColor(ChatColor.DARK_RED)); - TEAM_COLORS.put(TeamColor.DARK_PURPLE, getColor(ChatColor.DARK_PURPLE)); - TEAM_COLORS.put(TeamColor.GOLD, getColor(ChatColor.GOLD)); - TEAM_COLORS.put(TeamColor.GRAY, getColor(ChatColor.GRAY)); - TEAM_COLORS.put(TeamColor.DARK_GRAY, getColor(ChatColor.DARK_GRAY)); - TEAM_COLORS.put(TeamColor.BLUE, getColor(ChatColor.BLUE)); - TEAM_COLORS.put(TeamColor.GREEN, getColor(ChatColor.GREEN)); - TEAM_COLORS.put(TeamColor.AQUA, getColor(ChatColor.AQUA)); - TEAM_COLORS.put(TeamColor.RED, getColor(ChatColor.RED)); - TEAM_COLORS.put(TeamColor.LIGHT_PURPLE, getColor(ChatColor.LIGHT_PURPLE)); - TEAM_COLORS.put(TeamColor.YELLOW, getColor(ChatColor.YELLOW)); - TEAM_COLORS.put(TeamColor.WHITE, getColor(ChatColor.WHITE)); - TEAM_COLORS.put(TeamColor.OBFUSCATED, getFormat(Collections.singletonList(ChatFormat.OBFUSCATED))); - TEAM_COLORS.put(TeamColor.BOLD, getFormat(Collections.singletonList(ChatFormat.BOLD))); - TEAM_COLORS.put(TeamColor.STRIKETHROUGH, getFormat(Collections.singletonList(ChatFormat.STRIKETHROUGH))); - TEAM_COLORS.put(TeamColor.ITALIC, getFormat(Collections.singletonList(ChatFormat.ITALIC))); - } - - /** - * Recursively parse each message from a list for usage in a {@link TranslationMessage} - * - * @param messages A {@link List} of {@link Message} to parse - * @param locale A locale loaded to get the message for - * @param parent A {@link Message} to use as the parent (can be null) - * @return the translation parameters - */ - public static List getTranslationParams(List messages, String locale, Message parent) { - List strings = new ArrayList<>(); - for (Message message : messages) { - message = fixMessageStyle(message, parent); - - if (message instanceof TranslationMessage) { - TranslationMessage translation = (TranslationMessage) message; - - if (locale == null) { - String builder = "%" + translation.getKey(); - strings.add(builder); - } - - // Collect all params and add format corrections to the end of them - List furtherParams = new ArrayList<>(); - for (String param : getTranslationParams(translation.getWith(), locale, message)) { - String newParam = param; - if (parent.getStyle().getFormats().size() != 0) { - newParam += getFormat(parent.getStyle().getFormats()); - } - if (parent.getStyle().getColor() != ChatColor.NONE) { - newParam += getColor(parent.getStyle().getColor()); - } - - furtherParams.add(newParam); - } - - if (locale != null) { - String builder = getFormat(message.getStyle().getFormats()) + - getColor(message.getStyle().getColor()); - builder += insertParams(LocaleUtils.getLocaleString(translation.getKey(), locale), furtherParams); - strings.add(builder); - } else { - String format = getFormat(message.getStyle().getFormats()) + - getColor(message.getStyle().getColor()); - for (String param : furtherParams) { - strings.add(format + param); - } - } - } else { - String builder = getFormat(message.getStyle().getFormats()) + - getColor(message.getStyle().getColor()); - builder += getTranslatedBedrockMessage(message, locale, false, parent); - strings.add(builder); - } - } - - return strings; - } - - public static String getTranslatedBedrockMessage(Message message, String locale) { - return getTranslatedBedrockMessage(message, locale, true); - } - - public static String getTranslatedBedrockMessage(Message message, String locale, boolean shouldTranslate) { - return getTranslatedBedrockMessage(message, locale, shouldTranslate, null); - } - - /** - * Translate a given {@link TranslationMessage} to the given locale - * - * @param message The {@link Message} to send - * @param locale the locale - * @param shouldTranslate if the message should be translated - * @param parent the parent message - * @return the given translation message translated from the given locale - */ - public static String getTranslatedBedrockMessage(Message message, String locale, boolean shouldTranslate, Message parent) { - JsonParser parser = new JsonParser(); - if (isMessage(message.toString())) { - JsonObject object = parser.parse(message.toString()).getAsJsonObject(); - message = MessageSerializer.fromJson(object); - } - - message = fixMessageStyle(message, parent); - - String messageText = (message instanceof TranslationMessage) ? ((TranslationMessage) message).getKey() : ((TextMessage) message).getText(); - if (locale != null && shouldTranslate) { - messageText = LocaleUtils.getLocaleString(messageText, locale); - } - - StringBuilder builder = new StringBuilder(); - builder.append(getFormat(message.getStyle().getFormats())); - builder.append(getColor(message.getStyle().getColor())); - builder.append(messageText); - - for (Message msg : message.getExtra()) { - builder.append(getFormat(msg.getStyle().getFormats())); - builder.append(getColor(msg.getStyle().getColor())); - if (!(msg.toString() == null)) { - boolean isTranslationMessage = (msg instanceof TranslationMessage); - String extraText = ""; - - if (isTranslationMessage) { - List paramsTranslated = getTranslationParams(((TranslationMessage) msg).getWith(), locale, message); - extraText = insertParams(getTranslatedBedrockMessage(msg, locale, isTranslationMessage, message), paramsTranslated); - } else { - extraText = getTranslatedBedrockMessage(msg, locale, isTranslationMessage, message); - } - - builder.append(extraText); - builder.append("\u00a7r"); - } - } - - return builder.toString(); - } - - /** - * If the passed {@link Message} color or format are empty then copy from parent - * - * @param message {@link Message} to update - * @param parent Parent {@link Message} for style - * @return The updated {@link Message} - */ - private static Message fixMessageStyle(Message message, Message parent) { - if (parent == null) { - return message; - } - MessageStyle.Builder styleBuilder = message.getStyle().toBuilder(); - - // Copy color from parent - if (message.getStyle().getColor() == ChatColor.NONE) { - styleBuilder.color(parent.getStyle().getColor()); - } - - // Copy formatting from parent - if (message.getStyle().getFormats().size() == 0) { - styleBuilder.formats(parent.getStyle().getFormats()); - } - - return message.toBuilder().style(styleBuilder.build()).build(); - } - - public static String getBedrockMessage(Message message) { - if (isMessage(((TextMessage) message).getText())) { - return getBedrockMessage(((TextMessage) message).getText()); - } else { - return getBedrockMessage(MessageSerializer.toJsonString(message)); - } - } - - /** - * Verifies the message is valid JSON in case it's plaintext. Works around GsonComponentSeraializer not using lenient mode. - * See https://wiki.vg/Chat for messages sent in lenient mode, and for a description on leniency. - * - * @param message Potentially lenient JSON message - * @return Bedrock formatted message - */ - public static String getBedrockMessageLenient(String message) { - if (isMessage(message)) { - return getBedrockMessage(message); - } else { - final JsonObject obj = new JsonObject(); - obj.addProperty("text", message); - return getBedrockMessage(obj.toString()); - } - } - - public static String getBedrockMessage(String message) { - Component component = phraseJavaMessage(message); - return LegacyComponentSerializer.legacySection().serialize(component); - } - - public static Component phraseJavaMessage(String message) { - return GsonComponentSerializer.gson().deserialize(message); - } - - public static String getJavaMessage(String message) { - Component component = LegacyComponentSerializer.legacySection().deserialize(message); - return GsonComponentSerializer.gson().serialize(component); - } - - /** - * Inserts the given parameters into the given message both in sequence and as requested - * - * @param message Message containing possible parameter replacement strings - * @param params A list of parameter strings - * @return Parsed message with all params inserted as needed - */ - public static String insertParams(String message, List params) { - String newMessage = message; - - Pattern p = Pattern.compile("%([1-9])\\$s"); - Matcher m = p.matcher(message); - while (m.find()) { - try { - newMessage = newMessage.replaceFirst("%" + m.group(1) + "\\$s", params.get(Integer.parseInt(m.group(1)) - 1)); - } catch (Exception e) { - // Couldn't find the param to replace - } - } - - for (String text : params) { - newMessage = newMessage.replaceFirst("%s", text.replaceAll("%s", "%r")); - } - - newMessage = newMessage.replaceAll("%r", "MISSING!"); - - return newMessage; - } - - /** - * Convert a ChatColor into a string for inserting into messages - * - * @param color ChatColor to convert - * @return The converted color string - */ - private static String getColor(String color) { - String base = "\u00a7"; - switch (color) { - case ChatColor.BLACK: - base += "0"; - break; - case ChatColor.DARK_BLUE: - base += "1"; - break; - case ChatColor.DARK_GREEN: - base += "2"; - break; - case ChatColor.DARK_AQUA: - base += "3"; - break; - case ChatColor.DARK_RED: - base += "4"; - break; - case ChatColor.DARK_PURPLE: - base += "5"; - break; - case ChatColor.GOLD: - base += "6"; - break; - case ChatColor.GRAY: - base += "7"; - break; - case ChatColor.DARK_GRAY: - base += "8"; - break; - case ChatColor.BLUE: - base += "9"; - break; - case ChatColor.GREEN: - base += "a"; - break; - case ChatColor.AQUA: - base += "b"; - break; - case ChatColor.RED: - base += "c"; - break; - case ChatColor.LIGHT_PURPLE: - base += "d"; - break; - case ChatColor.YELLOW: - base += "e"; - break; - case ChatColor.WHITE: - base += "f"; - break; - case ChatColor.RESET: - //case NONE: - base += "r"; - break; - case "": // To stop recursion - return ""; - default: - return getClosestColor(color); - } - - return base; - } - - /** - * Based on https://github.com/ViaVersion/ViaBackwards/blob/master/core/src/main/java/nl/matsv/viabackwards/protocol/protocol1_15_2to1_16/chat/TranslatableRewriter1_16.java - * - * @param color A color string - * @return The closest color to that string - */ - private static String getClosestColor(String color) { - if (!color.startsWith("#")) { - return ""; - } - - int rgb = Integer.parseInt(color.substring(1), 16); - int r = (rgb >> 16) & 0xFF; - int g = (rgb >> 8) & 0xFF; - int b = rgb & 0xFF; - - String closest = null; - int smallestDiff = 0; - - for (Map.Entry testColor : COLORS.entrySet()) { - if (testColor.getValue() == rgb) { - closest = testColor.getKey(); - break; - } - - int testR = (testColor.getValue() >> 16) & 0xFF; - int testG = (testColor.getValue() >> 8) & 0xFF; - int testB = testColor.getValue() & 0xFF; - - // Check by the greatest diff of the 3 values - int rAverage = (testR + r) / 2; - int rDiff = testR - r; - int gDiff = testG - g; - int bDiff = testB - b; - int diff = ((2 + (rAverage >> 8)) * rDiff * rDiff) - + (4 * gDiff * gDiff) - + ((2 + ((255 - rAverage) >> 8)) * bDiff * bDiff); - if (closest == null || diff < smallestDiff) { - closest = testColor.getKey(); - smallestDiff = diff; - } - } - - return getColor(closest); - } - - /** - * Convert a list of ChatFormats into a string for inserting into messages - * - * @param formats ChatFormats to convert - * @return The converted chat formatting string - */ - private static String getFormat(List formats) { - StringBuilder str = new StringBuilder(); - for (ChatFormat cf : formats) { - String base = "\u00a7"; - switch (cf) { - case OBFUSCATED: - base += "k"; - break; - case BOLD: - base += "l"; - break; - case STRIKETHROUGH: - base += "m"; - break; - case UNDERLINED: - base += "n"; - break; - case ITALIC: - base += "o"; - break; - default: - return ""; - } - - str.append(base); - } - - return str.toString(); - } - - /** - * Checks if the given text string is a json message - * - * @param text String to test - * @return True if its a valid message json string, false if not - */ - public static boolean isMessage(String text) { - JsonParser parser = new JsonParser(); - try { - JsonObject object = parser.parse(text).getAsJsonObject(); - try { - MessageSerializer.fromJson(object); - } catch (Exception ex) { - return false; - } - } catch (Exception ex) { - return false; - } - return true; - } - - 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 - * - * @param message Message to check - * @param session GeyserSession for the user - * @return True if the message is too long, false if not - */ - public static boolean isTooLong(String message, GeyserSession session) { - if (message.length() > 256) { - session.sendMessage(LanguageUtils.getPlayerLocaleString("geyser.chat.too_long", session.getLocale(), message.length())); - return true; - } - - return false; - } -} diff --git a/connector/src/main/java/org/geysermc/connector/utils/SkinProvider.java b/connector/src/main/java/org/geysermc/connector/utils/SkinProvider.java index 530c551ab..8b9e7ee5e 100644 --- a/connector/src/main/java/org/geysermc/connector/utils/SkinProvider.java +++ b/connector/src/main/java/org/geysermc/connector/utils/SkinProvider.java @@ -606,7 +606,7 @@ public class SkinProvider { public enum CapeProvider { MINECRAFT, OPTIFINE("https://optifine.net/capes/%s.png", CapeUrlType.USERNAME), - LABYMOD("https://www.labymod.net/page/php/getCapeTexture.php?uuid=%s", CapeUrlType.UUID_DASHED), + LABYMOD("https://dl.labymod.net/capes/%s", CapeUrlType.UUID_DASHED), FIVEZIG("https://textures.5zigreborn.eu/profile/%s", CapeUrlType.UUID_DASHED), MINECRAFTCAPES("https://minecraftcapes.net/profile/%s/cape", CapeUrlType.UUID); diff --git a/connector/src/main/resources/bedrock/blockpalette.nbt b/connector/src/main/resources/bedrock/blockpalette.nbt new file mode 100644 index 000000000..b92a06260 Binary files /dev/null and b/connector/src/main/resources/bedrock/blockpalette.nbt differ diff --git a/connector/src/main/resources/bedrock/creative_items.json b/connector/src/main/resources/bedrock/creative_items.json index eea92a950..90839b0dc 100644 --- a/connector/src/main/resources/bedrock/creative_items.json +++ b/connector/src/main/resources/bedrock/creative_items.json @@ -258,31 +258,31 @@ "id" : -275 }, { - "id" : 324 + "id" : 359 }, { - "id" : 427 + "id" : 543 }, { - "id" : 428 + "id" : 544 }, { - "id" : 429 + "id" : 545 }, { - "id" : 430 + "id" : 546 }, { - "id" : 431 + "id" : 547 }, { - "id" : 330 + "id" : 370 }, { - "id" : 755 + "id" : 604 }, { - "id" : 756 + "id" : 605 }, { "id" : 96 @@ -314,9 +314,6 @@ { "id" : 101 }, - { - "id" : 758 - }, { "id" : 20 }, @@ -741,66 +738,15 @@ { "id" : 170 }, - { - "id" : -239 - }, { "id" : 216 }, - { - "id" : 214 - }, - { - "id" : -227 - }, { "id" : 112 }, { "id" : 215 }, - { - "id" : -225 - }, - { - "id" : -226 - }, - { - "id" : -240 - }, - { - "id" : -241 - }, - { - "id" : -299 - }, - { - "id" : -298 - }, - { - "id" : -300 - }, - { - "id" : -301 - }, - { - "id" : -230 - }, - { - "id" : -232 - }, - { - "id" : -233 - }, - { - "id" : -234 - }, - { - "id" : -235 - }, - { - "id" : -236 - }, { "id" : -270 }, @@ -1183,6 +1129,30 @@ "id" : 201, "damage" : 2 }, + { + "id" : 214 + }, + { + "id" : -227 + }, + { + "id" : -230 + }, + { + "id" : -232 + }, + { + "id" : -233 + }, + { + "id" : -234 + }, + { + "id" : -235 + }, + { + "id" : -236 + }, { "id" : 3 }, @@ -1190,6 +1160,9 @@ "id" : 3, "damage" : 1 }, + { + "id" : 60 + }, { "id" : 2 }, @@ -1318,6 +1291,18 @@ { "id" : -9 }, + { + "id" : -225 + }, + { + "id" : -240 + }, + { + "id" : -226 + }, + { + "id" : -241 + }, { "id" : -212 }, @@ -1365,6 +1350,18 @@ "id" : -212, "damage" : 13 }, + { + "id" : -299 + }, + { + "id" : -300 + }, + { + "id" : -298 + }, + { + "id" : -301 + }, { "id" : 18 }, @@ -1411,59 +1408,58 @@ "damage" : 5 }, { - "id" : -218, - "damage" : 3 + "id" : -218 + }, + { + "id" : 291 + }, + { + "id" : 292 + }, + { + "id" : 293 }, { "id" : 295 }, { - "id" : 361 + "id" : 334 }, { - "id" : 362 + "id" : 285 }, { - "id" : 458 + "id" : 280 }, { - "id" : 296 + "id" : 282 }, { - "id" : 457 + "id" : 279 }, { - "id" : 392 + "id" : 283 }, { - "id" : 394 + "id" : 257 }, { - "id" : 391 + "id" : 258 }, { - "id" : 396 - }, - { - "id" : 260 - }, - { - "id" : 322 - }, - { - "id" : 466 + "id" : 259 }, { "id" : 103 }, { - "id" : 360 + "id" : 272 }, { - "id" : 382 + "id" : 432 }, { - "id" : 477 + "id" : 287 }, { "id" : 86 @@ -1475,7 +1471,7 @@ "id" : 91 }, { - "id" : 736 + "id" : 580 }, { "id" : 31, @@ -1494,7 +1490,7 @@ "damage" : 2 }, { - "id" : 760 + "id" : 609 }, { "id" : -131, @@ -1574,7 +1570,7 @@ "damage" : 4 }, { - "id" : 335 + "id" : 380 }, { "id" : -130 @@ -1650,83 +1646,64 @@ "id" : -216 }, { - "id" : 351, - "damage" : 19 + "id" : 408 }, { - "id" : 351, - "damage" : 7 + "id" : 400 }, { - "id" : 351, - "damage" : 8 + "id" : 401 }, { - "id" : 351, - "damage" : 16 + "id" : 393 }, { - "id" : 351, - "damage" : 17 + "id" : 396 }, { - "id" : 351, - "damage" : 1 + "id" : 394 }, { - "id" : 351, - "damage" : 14 + "id" : 407 }, { - "id" : 351, - "damage" : 11 + "id" : 404 }, { - "id" : 351, - "damage" : 10 + "id" : 403 }, { - "id" : 351, - "damage" : 2 + "id" : 395 }, { - "id" : 351, - "damage" : 6 + "id" : 399 }, { - "id" : 351, - "damage" : 12 + "id" : 405 }, { - "id" : 351, - "damage" : 18 + "id" : 397 }, { - "id" : 351, - "damage" : 5 + "id" : 398 }, { - "id" : 351, - "damage" : 13 + "id" : 406 }, { - "id" : 351, - "damage" : 9 + "id" : 402 }, { - "id" : 351 + "id" : 411 }, { - "id" : 351, - "damage" : 3 + "id" : 410 }, { - "id" : 351, - "damage" : 4 + "id" : 412 }, { - "id" : 351, - "damage" : 15 + "id" : 409 }, { "id" : 106 @@ -1762,31 +1739,31 @@ "id" : 78 }, { - "id" : 365 + "id" : 275 }, { - "id" : 319 + "id" : 262 }, { - "id" : 363 + "id" : 273 }, { - "id" : 423 + "id" : 540 }, { - "id" : 411 + "id" : 288 }, { - "id" : 349 + "id" : 264 }, { - "id" : 460 + "id" : 265 }, { - "id" : 461 + "id" : 266 }, { - "id" : 462 + "id" : 267 }, { "id" : 39 @@ -1816,25 +1793,25 @@ "id" : 99 }, { - "id" : 344 + "id" : 388 }, { - "id" : 338 + "id" : 383 }, { - "id" : 353 + "id" : 414 }, { - "id" : 367 + "id" : 277 }, { - "id" : 352 + "id" : 413 }, { "id" : 30 }, { - "id" : 375 + "id" : 278 }, { "id" : 52 @@ -1869,252 +1846,193 @@ "id" : -159 }, { - "id" : 383, - "damage" : 10 + "id" : 433 }, { - "id" : 383, - "damage" : 122 + "id" : 492 }, { - "id" : 383, - "damage" : 11 + "id" : 434 }, { - "id" : 383, - "damage" : 12 + "id" : 435 }, { - "id" : 383, - "damage" : 13 + "id" : 436 }, { - "id" : 383, - "damage" : 14 + "id" : 437 }, { - "id" : 383, - "damage" : 28 + "id" : 470 }, { - "id" : 383, - "damage" : 22 + "id" : 449 }, { - "id" : 383, - "damage" : 75 + "id" : 486 }, { - "id" : 383, - "damage" : 16 + "id" : 438 }, { - "id" : 383, - "damage" : 19 + "id" : 451 }, { - "id" : 383, - "damage" : 30 + "id" : 476 }, { - "id" : 383, - "damage" : 18 + "id" : 457 }, { - "id" : 383, - "damage" : 29 + "id" : 471 }, { - "id" : 383, - "damage" : 23 + "id" : 456 }, { - "id" : 383, - "damage" : 24 + "id" : 463 }, { - "id" : 383, - "damage" : 25 + "id" : 464 }, { - "id" : 383, - "damage" : 26 + "id" : 465 }, { - "id" : 383, - "damage" : 27 + "id" : 466 }, { - "id" : 383, - "damage" : 111 + "id" : 477 }, { - "id" : 383, - "damage" : 112 + "id" : 478 }, { - "id" : 383, - "damage" : 108 + "id" : 479 }, { - "id" : 383, - "damage" : 109 + "id" : 480 }, { - "id" : 383, - "damage" : 31 + "id" : 482 }, { - "id" : 383, - "damage" : 74 + "id" : 483 }, { - "id" : 383, - "damage" : 113 + "id" : 487 }, { - "id" : 383, - "damage" : 121 + "id" : 488 }, { - "id" : 383, - "damage" : 33 + "id" : 439 }, { - "id" : 383, - "damage" : 38 + "id" : 440 }, { - "id" : 383, - "damage" : 39 + "id" : 441 }, { - "id" : 383, - "damage" : 34 + "id" : 442 }, { - "id" : 383, - "damage" : 48 + "id" : 462 }, { - "id" : 383, - "damage" : 46 + "id" : 460 }, { - "id" : 383, - "damage" : 37 + "id" : 443 }, { - "id" : 383, - "damage" : 35 + "id" : 444 }, { - "id" : 383, - "damage" : 32 + "id" : 445 }, { - "id" : 383, - "damage" : 36 + "id" : 446 }, { - "id" : 383, - "damage" : 47 + "id" : 461 }, { - "id" : 383, - "damage" : 110 + "id" : 481 }, { - "id" : 383, - "damage" : 17 + "id" : 448 }, { - "id" : 383, - "damage" : 40 + "id" : 455 }, { - "id" : 383, - "damage" : 45 + "id" : 450 }, { - "id" : 383, - "damage" : 49 + "id" : 459 }, { - "id" : 383, - "damage" : 50 + "id" : 469 }, { - "id" : 383, - "damage" : 55 + "id" : 458 }, { - "id" : 383, - "damage" : 42 + "id" : 453 }, { - "id" : 383, - "damage" : 125 + "id" : 493 }, { - "id" : 383, - "damage" : 124 + "id" : 494 }, { - "id" : 383, - "damage" : 123 + "id" : 495 }, { - "id" : 383, - "damage" : 126 + "id" : 496 }, { - "id" : 383, - "damage" : 41 + "id" : 497 }, { - "id" : 383, - "damage" : 43 + "id" : 452 }, { - "id" : 383, - "damage" : 54 + "id" : 454 }, { - "id" : 383, - "damage" : 57 + "id" : 467 }, { - "id" : 383, - "damage" : 104 + "id" : 472 }, { - "id" : 383, - "damage" : 105 + "id" : 473 }, { - "id" : 383, - "damage" : 115 + "id" : 474 }, { - "id" : 383, - "damage" : 118 + "id" : 447 }, { - "id" : 383, - "damage" : 116 + "id" : 490 }, { - "id" : 383, - "damage" : 58 + "id" : 475 }, { - "id" : 383, - "damage" : 114 + "id" : 484 }, { - "id" : 383, - "damage" : 59 + "id" : 489 + }, + { + "id" : 491 }, { "id" : 49 @@ -2135,7 +2053,7 @@ "id" : 213 }, { - "id" : 372 + "id" : 294 }, { "id" : 121 @@ -2147,10 +2065,10 @@ "id" : 240 }, { - "id" : 432 + "id" : 548 }, { - "id" : 433 + "id" : 549 }, { "id" : 19 @@ -2199,1023 +2117,1023 @@ "damage" : 12 }, { - "id" : 298 + "id" : 335 }, { - "id" : 302 + "id" : 339 }, { - "id" : 306 + "id" : 343 }, { - "id" : 314 - }, - { - "id" : 310 - }, - { - "id" : 748 - }, - { - "id" : 299 - }, - { - "id" : 303 - }, - { - "id" : 307 - }, - { - "id" : 315 - }, - { - "id" : 311 - }, - { - "id" : 749 - }, - { - "id" : 300 - }, - { - "id" : 304 - }, - { - "id" : 308 - }, - { - "id" : 316 - }, - { - "id" : 312 - }, - { - "id" : 750 - }, - { - "id" : 301 - }, - { - "id" : 305 - }, - { - "id" : 309 - }, - { - "id" : 317 - }, - { - "id" : 313 - }, - { - "id" : 751 - }, - { - "id" : 268 - }, - { - "id" : 272 - }, - { - "id" : 267 - }, - { - "id" : 283 - }, - { - "id" : 276 - }, - { - "id" : 743 - }, - { - "id" : 271 - }, - { - "id" : 275 - }, - { - "id" : 258 - }, - { - "id" : 286 - }, - { - "id" : 279 - }, - { - "id" : 746 - }, - { - "id" : 270 - }, - { - "id" : 274 - }, - { - "id" : 257 - }, - { - "id" : 285 - }, - { - "id" : 278 - }, - { - "id" : 745 - }, - { - "id" : 269 - }, - { - "id" : 273 - }, - { - "id" : 256 - }, - { - "id" : 284 - }, - { - "id" : 277 - }, - { - "id" : 744 - }, - { - "id" : 290 - }, - { - "id" : 291 - }, - { - "id" : 292 - }, - { - "id" : 294 - }, - { - "id" : 293 - }, - { - "id" : 747 - }, - { - "id" : 261 - }, - { - "id" : 471 - }, - { - "id" : 262 - }, - { - "id" : 262, - "damage" : 6 - }, - { - "id" : 262, - "damage" : 7 - }, - { - "id" : 262, - "damage" : 8 - }, - { - "id" : 262, - "damage" : 9 - }, - { - "id" : 262, - "damage" : 10 - }, - { - "id" : 262, - "damage" : 11 - }, - { - "id" : 262, - "damage" : 12 - }, - { - "id" : 262, - "damage" : 13 - }, - { - "id" : 262, - "damage" : 14 - }, - { - "id" : 262, - "damage" : 15 - }, - { - "id" : 262, - "damage" : 16 - }, - { - "id" : 262, - "damage" : 17 - }, - { - "id" : 262, - "damage" : 18 - }, - { - "id" : 262, - "damage" : 19 - }, - { - "id" : 262, - "damage" : 20 - }, - { - "id" : 262, - "damage" : 21 - }, - { - "id" : 262, - "damage" : 22 - }, - { - "id" : 262, - "damage" : 23 - }, - { - "id" : 262, - "damage" : 24 - }, - { - "id" : 262, - "damage" : 25 - }, - { - "id" : 262, - "damage" : 26 - }, - { - "id" : 262, - "damage" : 27 - }, - { - "id" : 262, - "damage" : 28 - }, - { - "id" : 262, - "damage" : 29 - }, - { - "id" : 262, - "damage" : 30 - }, - { - "id" : 262, - "damage" : 31 - }, - { - "id" : 262, - "damage" : 32 - }, - { - "id" : 262, - "damage" : 33 - }, - { - "id" : 262, - "damage" : 34 - }, - { - "id" : 262, - "damage" : 35 - }, - { - "id" : 262, - "damage" : 36 - }, - { - "id" : 262, - "damage" : 37 - }, - { - "id" : 262, - "damage" : 38 - }, - { - "id" : 262, - "damage" : 39 - }, - { - "id" : 262, - "damage" : 40 - }, - { - "id" : 262, - "damage" : 41 - }, - { - "id" : 262, - "damage" : 42 - }, - { - "id" : 262, - "damage" : 43 - }, - { - "id" : 513 - }, - { - "id" : 366 - }, - { - "id" : 320 - }, - { - "id" : 364 - }, - { - "id" : 424 - }, - { - "id" : 412 - }, - { - "id" : 350 - }, - { - "id" : 463 - }, - { - "id" : 297 - }, - { - "id" : 282 - }, - { - "id" : 459 - }, - { - "id" : 413 - }, - { - "id" : 393 - }, - { - "id" : 357 - }, - { - "id" : 400 - }, - { - "id" : 354 - }, - { - "id" : 464 - }, - { - "id" : 346 - }, - { - "id" : 398 - }, - { - "id" : 757 - }, - { - "id" : 332 - }, - { - "id" : 359 - }, - { - "id" : 259 - }, - { - "id" : 420 + "id" : 351 }, { "id" : 347 }, + { + "id" : 597 + }, + { + "id" : 336 + }, + { + "id" : 340 + }, + { + "id" : 344 + }, + { + "id" : 352 + }, + { + "id" : 348 + }, + { + "id" : 598 + }, + { + "id" : 337 + }, + { + "id" : 341 + }, { "id" : 345 }, { - "id" : 395 + "id" : 353 }, { - "id" : 395, - "damage" : 2 + "id" : 349 + }, + { + "id" : 599 + }, + { + "id" : 338 + }, + { + "id" : 342 + }, + { + "id" : 346 + }, + { + "id" : 354 + }, + { + "id" : 350 + }, + { + "id" : 600 + }, + { + "id" : 308 + }, + { + "id" : 312 + }, + { + "id" : 307 + }, + { + "id" : 322 + }, + { + "id" : 316 + }, + { + "id" : 592 + }, + { + "id" : 311 + }, + { + "id" : 315 + }, + { + "id" : 298 + }, + { + "id" : 325 + }, + { + "id" : 319 + }, + { + "id" : 595 + }, + { + "id" : 310 + }, + { + "id" : 314 + }, + { + "id" : 297 + }, + { + "id" : 324 + }, + { + "id" : 318 + }, + { + "id" : 594 + }, + { + "id" : 309 + }, + { + "id" : 313 + }, + { + "id" : 296 + }, + { + "id" : 323 + }, + { + "id" : 317 + }, + { + "id" : 593 }, { "id" : 329 }, { - "id" : 416 + "id" : 330 }, { - "id" : 417 + "id" : 331 }, { - "id" : 418 + "id" : 333 }, { - "id" : 419 + "id" : 332 }, { - "id" : 455 + "id" : 596 }, { - "id" : 469 + "id" : 300 }, { - "id" : 444 + "id" : 565 }, { - "id" : 450 + "id" : 301 }, { - "id" : 374 - }, - { - "id" : 384 - }, - { - "id" : 373 - }, - { - "id" : 373, - "damage" : 1 - }, - { - "id" : 373, - "damage" : 2 - }, - { - "id" : 373, - "damage" : 3 - }, - { - "id" : 373, - "damage" : 4 - }, - { - "id" : 373, - "damage" : 5 - }, - { - "id" : 373, + "id" : 301, "damage" : 6 }, { - "id" : 373, + "id" : 301, "damage" : 7 }, { - "id" : 373, + "id" : 301, "damage" : 8 }, { - "id" : 373, + "id" : 301, "damage" : 9 }, { - "id" : 373, + "id" : 301, "damage" : 10 }, { - "id" : 373, + "id" : 301, "damage" : 11 }, { - "id" : 373, + "id" : 301, "damage" : 12 }, { - "id" : 373, + "id" : 301, "damage" : 13 }, { - "id" : 373, + "id" : 301, "damage" : 14 }, { - "id" : 373, + "id" : 301, "damage" : 15 }, { - "id" : 373, + "id" : 301, "damage" : 16 }, { - "id" : 373, + "id" : 301, "damage" : 17 }, { - "id" : 373, + "id" : 301, "damage" : 18 }, { - "id" : 373, + "id" : 301, "damage" : 19 }, { - "id" : 373, + "id" : 301, "damage" : 20 }, { - "id" : 373, + "id" : 301, "damage" : 21 }, { - "id" : 373, + "id" : 301, "damage" : 22 }, { - "id" : 373, + "id" : 301, "damage" : 23 }, { - "id" : 373, + "id" : 301, "damage" : 24 }, { - "id" : 373, + "id" : 301, "damage" : 25 }, { - "id" : 373, + "id" : 301, "damage" : 26 }, { - "id" : 373, + "id" : 301, "damage" : 27 }, { - "id" : 373, + "id" : 301, "damage" : 28 }, { - "id" : 373, + "id" : 301, "damage" : 29 }, { - "id" : 373, + "id" : 301, "damage" : 30 }, { - "id" : 373, + "id" : 301, "damage" : 31 }, { - "id" : 373, + "id" : 301, "damage" : 32 }, { - "id" : 373, + "id" : 301, "damage" : 33 }, { - "id" : 373, + "id" : 301, "damage" : 34 }, { - "id" : 373, + "id" : 301, "damage" : 35 }, { - "id" : 373, + "id" : 301, "damage" : 36 }, { - "id" : 373, + "id" : 301, "damage" : 37 }, { - "id" : 373, + "id" : 301, "damage" : 38 }, { - "id" : 373, + "id" : 301, "damage" : 39 }, { - "id" : 373, + "id" : 301, "damage" : 40 }, { - "id" : 373, + "id" : 301, "damage" : 41 }, { - "id" : 373, + "id" : 301, "damage" : 42 }, { - "id" : 438 - }, - { - "id" : 438, - "damage" : 1 - }, - { - "id" : 438, - "damage" : 2 - }, - { - "id" : 438, - "damage" : 3 - }, - { - "id" : 438, - "damage" : 4 - }, - { - "id" : 438, - "damage" : 5 - }, - { - "id" : 438, - "damage" : 6 - }, - { - "id" : 438, - "damage" : 7 - }, - { - "id" : 438, - "damage" : 8 - }, - { - "id" : 438, - "damage" : 9 - }, - { - "id" : 438, - "damage" : 10 - }, - { - "id" : 438, - "damage" : 11 - }, - { - "id" : 438, - "damage" : 12 - }, - { - "id" : 438, - "damage" : 13 - }, - { - "id" : 438, - "damage" : 14 - }, - { - "id" : 438, - "damage" : 15 - }, - { - "id" : 438, - "damage" : 16 - }, - { - "id" : 438, - "damage" : 17 - }, - { - "id" : 438, - "damage" : 18 - }, - { - "id" : 438, - "damage" : 19 - }, - { - "id" : 438, - "damage" : 20 - }, - { - "id" : 438, - "damage" : 21 - }, - { - "id" : 438, - "damage" : 22 - }, - { - "id" : 438, - "damage" : 23 - }, - { - "id" : 438, - "damage" : 24 - }, - { - "id" : 438, - "damage" : 25 - }, - { - "id" : 438, - "damage" : 26 - }, - { - "id" : 438, - "damage" : 27 - }, - { - "id" : 438, - "damage" : 28 - }, - { - "id" : 438, - "damage" : 29 - }, - { - "id" : 438, - "damage" : 30 - }, - { - "id" : 438, - "damage" : 31 - }, - { - "id" : 438, - "damage" : 32 - }, - { - "id" : 438, - "damage" : 33 - }, - { - "id" : 438, - "damage" : 34 - }, - { - "id" : 438, - "damage" : 35 - }, - { - "id" : 438, - "damage" : 36 - }, - { - "id" : 438, - "damage" : 37 - }, - { - "id" : 438, - "damage" : 38 - }, - { - "id" : 438, - "damage" : 39 - }, - { - "id" : 438, - "damage" : 40 - }, - { - "id" : 438, - "damage" : 41 - }, - { - "id" : 438, - "damage" : 42 - }, - { - "id" : 441 - }, - { - "id" : 441, - "damage" : 1 - }, - { - "id" : 441, - "damage" : 2 - }, - { - "id" : 441, - "damage" : 3 - }, - { - "id" : 441, - "damage" : 4 - }, - { - "id" : 441, - "damage" : 5 - }, - { - "id" : 441, - "damage" : 6 - }, - { - "id" : 441, - "damage" : 7 - }, - { - "id" : 441, - "damage" : 8 - }, - { - "id" : 441, - "damage" : 9 - }, - { - "id" : 441, - "damage" : 10 - }, - { - "id" : 441, - "damage" : 11 - }, - { - "id" : 441, - "damage" : 12 - }, - { - "id" : 441, - "damage" : 13 - }, - { - "id" : 441, - "damage" : 14 - }, - { - "id" : 441, - "damage" : 15 - }, - { - "id" : 441, - "damage" : 16 - }, - { - "id" : 441, - "damage" : 17 - }, - { - "id" : 441, - "damage" : 18 - }, - { - "id" : 441, - "damage" : 19 - }, - { - "id" : 441, - "damage" : 20 - }, - { - "id" : 441, - "damage" : 21 - }, - { - "id" : 441, - "damage" : 22 - }, - { - "id" : 441, - "damage" : 23 - }, - { - "id" : 441, - "damage" : 24 - }, - { - "id" : 441, - "damage" : 25 - }, - { - "id" : 441, - "damage" : 26 - }, - { - "id" : 441, - "damage" : 27 - }, - { - "id" : 441, - "damage" : 28 - }, - { - "id" : 441, - "damage" : 29 - }, - { - "id" : 441, - "damage" : 30 - }, - { - "id" : 441, - "damage" : 31 - }, - { - "id" : 441, - "damage" : 32 - }, - { - "id" : 441, - "damage" : 33 - }, - { - "id" : 441, - "damage" : 34 - }, - { - "id" : 441, - "damage" : 35 - }, - { - "id" : 441, - "damage" : 36 - }, - { - "id" : 441, - "damage" : 37 - }, - { - "id" : 441, - "damage" : 38 - }, - { - "id" : 441, - "damage" : 39 - }, - { - "id" : 441, - "damage" : 40 - }, - { - "id" : 441, - "damage" : 41 - }, - { - "id" : 441, - "damage" : 42 - }, - { - "id" : 280 + "id" : 301, + "damage" : 43 }, { "id" : 355 }, { - "id" : 355, - "damage" : 8 + "id" : 276 }, { - "id" : 355, - "damage" : 7 + "id" : 263 }, { - "id" : 355, - "damage" : 15 + "id" : 274 }, { - "id" : 355, - "damage" : 12 + "id" : 541 }, { - "id" : 355, - "damage" : 14 + "id" : 289 }, { - "id" : 355, - "damage" : 1 + "id" : 268 }, { - "id" : 355, - "damage" : 4 + "id" : 269 }, { - "id" : 355, - "damage" : 5 + "id" : 261 }, { - "id" : 355, - "damage" : 13 + "id" : 260 }, { - "id" : 355, - "damage" : 9 + "id" : 286 }, { - "id" : 355, - "damage" : 3 + "id" : 290 }, { - "id" : 355, - "damage" : 11 + "id" : 281 }, { - "id" : 355, - "damage" : 10 + "id" : 271 }, { - "id" : 355, + "id" : 284 + }, + { + "id" : 415 + }, + { + "id" : 270 + }, + { + "id" : 390 + }, + { + "id" : 507 + }, + { + "id" : 606 + }, + { + "id" : 372 + }, + { + "id" : 419 + }, + { + "id" : 299 + }, + { + "id" : 537 + }, + { + "id" : 391 + }, + { + "id" : 389 + }, + { + "id" : 505 + }, + { + "id" : 505, "damage" : 2 }, { - "id" : 355, + "id" : 369 + }, + { + "id" : 520 + }, + { + "id" : 521 + }, + { + "id" : 522 + }, + { + "id" : 523 + }, + { + "id" : 536 + }, + { + "id" : 563 + }, + { + "id" : 554 + }, + { + "id" : 558 + }, + { + "id" : 425 + }, + { + "id" : 498 + }, + { + "id" : 424 + }, + { + "id" : 424, + "damage" : 1 + }, + { + "id" : 424, + "damage" : 2 + }, + { + "id" : 424, + "damage" : 3 + }, + { + "id" : 424, + "damage" : 4 + }, + { + "id" : 424, + "damage" : 5 + }, + { + "id" : 424, + "damage" : 6 + }, + { + "id" : 424, + "damage" : 7 + }, + { + "id" : 424, + "damage" : 8 + }, + { + "id" : 424, + "damage" : 9 + }, + { + "id" : 424, + "damage" : 10 + }, + { + "id" : 424, + "damage" : 11 + }, + { + "id" : 424, + "damage" : 12 + }, + { + "id" : 424, + "damage" : 13 + }, + { + "id" : 424, + "damage" : 14 + }, + { + "id" : 424, + "damage" : 15 + }, + { + "id" : 424, + "damage" : 16 + }, + { + "id" : 424, + "damage" : 17 + }, + { + "id" : 424, + "damage" : 18 + }, + { + "id" : 424, + "damage" : 19 + }, + { + "id" : 424, + "damage" : 20 + }, + { + "id" : 424, + "damage" : 21 + }, + { + "id" : 424, + "damage" : 22 + }, + { + "id" : 424, + "damage" : 23 + }, + { + "id" : 424, + "damage" : 24 + }, + { + "id" : 424, + "damage" : 25 + }, + { + "id" : 424, + "damage" : 26 + }, + { + "id" : 424, + "damage" : 27 + }, + { + "id" : 424, + "damage" : 28 + }, + { + "id" : 424, + "damage" : 29 + }, + { + "id" : 424, + "damage" : 30 + }, + { + "id" : 424, + "damage" : 31 + }, + { + "id" : 424, + "damage" : 32 + }, + { + "id" : 424, + "damage" : 33 + }, + { + "id" : 424, + "damage" : 34 + }, + { + "id" : 424, + "damage" : 35 + }, + { + "id" : 424, + "damage" : 36 + }, + { + "id" : 424, + "damage" : 37 + }, + { + "id" : 424, + "damage" : 38 + }, + { + "id" : 424, + "damage" : 39 + }, + { + "id" : 424, + "damage" : 40 + }, + { + "id" : 424, + "damage" : 41 + }, + { + "id" : 424, + "damage" : 42 + }, + { + "id" : 551 + }, + { + "id" : 551, + "damage" : 1 + }, + { + "id" : 551, + "damage" : 2 + }, + { + "id" : 551, + "damage" : 3 + }, + { + "id" : 551, + "damage" : 4 + }, + { + "id" : 551, + "damage" : 5 + }, + { + "id" : 551, + "damage" : 6 + }, + { + "id" : 551, + "damage" : 7 + }, + { + "id" : 551, + "damage" : 8 + }, + { + "id" : 551, + "damage" : 9 + }, + { + "id" : 551, + "damage" : 10 + }, + { + "id" : 551, + "damage" : 11 + }, + { + "id" : 551, + "damage" : 12 + }, + { + "id" : 551, + "damage" : 13 + }, + { + "id" : 551, + "damage" : 14 + }, + { + "id" : 551, + "damage" : 15 + }, + { + "id" : 551, + "damage" : 16 + }, + { + "id" : 551, + "damage" : 17 + }, + { + "id" : 551, + "damage" : 18 + }, + { + "id" : 551, + "damage" : 19 + }, + { + "id" : 551, + "damage" : 20 + }, + { + "id" : 551, + "damage" : 21 + }, + { + "id" : 551, + "damage" : 22 + }, + { + "id" : 551, + "damage" : 23 + }, + { + "id" : 551, + "damage" : 24 + }, + { + "id" : 551, + "damage" : 25 + }, + { + "id" : 551, + "damage" : 26 + }, + { + "id" : 551, + "damage" : 27 + }, + { + "id" : 551, + "damage" : 28 + }, + { + "id" : 551, + "damage" : 29 + }, + { + "id" : 551, + "damage" : 30 + }, + { + "id" : 551, + "damage" : 31 + }, + { + "id" : 551, + "damage" : 32 + }, + { + "id" : 551, + "damage" : 33 + }, + { + "id" : 551, + "damage" : 34 + }, + { + "id" : 551, + "damage" : 35 + }, + { + "id" : 551, + "damage" : 36 + }, + { + "id" : 551, + "damage" : 37 + }, + { + "id" : 551, + "damage" : 38 + }, + { + "id" : 551, + "damage" : 39 + }, + { + "id" : 551, + "damage" : 40 + }, + { + "id" : 551, + "damage" : 41 + }, + { + "id" : 551, + "damage" : 42 + }, + { + "id" : 552 + }, + { + "id" : 552, + "damage" : 1 + }, + { + "id" : 552, + "damage" : 2 + }, + { + "id" : 552, + "damage" : 3 + }, + { + "id" : 552, + "damage" : 4 + }, + { + "id" : 552, + "damage" : 5 + }, + { + "id" : 552, + "damage" : 6 + }, + { + "id" : 552, + "damage" : 7 + }, + { + "id" : 552, + "damage" : 8 + }, + { + "id" : 552, + "damage" : 9 + }, + { + "id" : 552, + "damage" : 10 + }, + { + "id" : 552, + "damage" : 11 + }, + { + "id" : 552, + "damage" : 12 + }, + { + "id" : 552, + "damage" : 13 + }, + { + "id" : 552, + "damage" : 14 + }, + { + "id" : 552, + "damage" : 15 + }, + { + "id" : 552, + "damage" : 16 + }, + { + "id" : 552, + "damage" : 17 + }, + { + "id" : 552, + "damage" : 18 + }, + { + "id" : 552, + "damage" : 19 + }, + { + "id" : 552, + "damage" : 20 + }, + { + "id" : 552, + "damage" : 21 + }, + { + "id" : 552, + "damage" : 22 + }, + { + "id" : 552, + "damage" : 23 + }, + { + "id" : 552, + "damage" : 24 + }, + { + "id" : 552, + "damage" : 25 + }, + { + "id" : 552, + "damage" : 26 + }, + { + "id" : 552, + "damage" : 27 + }, + { + "id" : 552, + "damage" : 28 + }, + { + "id" : 552, + "damage" : 29 + }, + { + "id" : 552, + "damage" : 30 + }, + { + "id" : 552, + "damage" : 31 + }, + { + "id" : 552, + "damage" : 32 + }, + { + "id" : 552, + "damage" : 33 + }, + { + "id" : 552, + "damage" : 34 + }, + { + "id" : 552, + "damage" : 35 + }, + { + "id" : 552, + "damage" : 36 + }, + { + "id" : 552, + "damage" : 37 + }, + { + "id" : 552, + "damage" : 38 + }, + { + "id" : 552, + "damage" : 39 + }, + { + "id" : 552, + "damage" : 40 + }, + { + "id" : 552, + "damage" : 41 + }, + { + "id" : 552, + "damage" : 42 + }, + { + "id" : 320 + }, + { + "id" : 416 + }, + { + "id" : 416, + "damage" : 8 + }, + { + "id" : 416, + "damage" : 7 + }, + { + "id" : 416, + "damage" : 15 + }, + { + "id" : 416, + "damage" : 12 + }, + { + "id" : 416, + "damage" : 14 + }, + { + "id" : 416, + "damage" : 1 + }, + { + "id" : 416, + "damage" : 4 + }, + { + "id" : 416, + "damage" : 5 + }, + { + "id" : 416, + "damage" : 13 + }, + { + "id" : 416, + "damage" : 9 + }, + { + "id" : 416, + "damage" : 3 + }, + { + "id" : 416, + "damage" : 11 + }, + { + "id" : 416, + "damage" : 10 + }, + { + "id" : 416, + "damage" : 2 + }, + { + "id" : 416, "damage" : 6 }, { @@ -3246,14 +3164,13 @@ "id" : -202 }, { - "id" : -219, - "damage" : 3 + "id" : -219 }, { - "id" : 720 + "id" : 578 }, { - "id" : 801 + "id" : 610 }, { "id" : 61 @@ -3268,7 +3185,7 @@ "id" : -272 }, { - "id" : 379 + "id" : 429 }, { "id" : 145 @@ -3294,7 +3211,7 @@ "id" : -194 }, { - "id" : 380 + "id" : 430 }, { "id" : -213 @@ -3378,7 +3295,7 @@ "damage" : 6 }, { - "id" : 425 + "id" : 542 }, { "id" : 25 @@ -3387,46 +3304,46 @@ "id" : 84 }, { - "id" : 500 + "id" : 524 }, { - "id" : 501 + "id" : 525 }, { - "id" : 502 + "id" : 526 }, { - "id" : 503 + "id" : 527 }, { - "id" : 504 + "id" : 528 }, { - "id" : 505 + "id" : 529 }, { - "id" : 506 + "id" : 530 }, { - "id" : 507 + "id" : 531 }, { - "id" : 508 + "id" : 532 }, { - "id" : 509 + "id" : 533 }, { - "id" : 510 + "id" : 534 }, { - "id" : 511 + "id" : 535 }, { - "id" : 759 + "id" : 608 }, { - "id" : 348 + "id" : 392 }, { "id" : 89 @@ -3438,96 +3355,89 @@ "id" : 169 }, { - "id" : 323 + "id" : 358 }, { - "id" : 472 + "id" : 566 }, { - "id" : 473 + "id" : 567 }, { - "id" : 474 + "id" : 568 }, { - "id" : 475 + "id" : 569 }, { - "id" : 476 + "id" : 570 }, { - "id" : 753 + "id" : 602 }, { - "id" : 754 + "id" : 603 + }, + { + "id" : 357 + }, + { + "id" : 503 + }, + { + "id" : 581 + }, + { + "id" : 504 }, { "id" : 321 }, { - "id" : 389 + "id" : 360 }, { - "id" : 737 + "id" : 361 }, { - "id" : 390 + "id" : 362 }, { - "id" : 281 + "id" : 363 }, { - "id" : 325 + "id" : 364 }, { - "id" : 325, - "damage" : 1 + "id" : 365 }, { - "id" : 325, - "damage" : 8 + "id" : 366 }, { - "id" : 325, - "damage" : 10 + "id" : 367 }, { - "id" : 325, - "damage" : 2 - }, - { - "id" : 325, + "id" : 506, "damage" : 3 }, { - "id" : 325, - "damage" : 4 - }, - { - "id" : 325, - "damage" : 5 - }, - { - "id" : 397, - "damage" : 3 - }, - { - "id" : 397, + "id" : 506, "damage" : 2 }, { - "id" : 397, + "id" : 506, "damage" : 4 }, { - "id" : 397, + "id" : 506, "damage" : 5 }, { - "id" : 397 + "id" : 506 }, { - "id" : 397, + "id" : 506, "damage" : 1 }, { @@ -3546,92 +3456,576 @@ "id" : 120 }, { - "id" : 263 + "id" : 302 }, { - "id" : 263, - "damage" : 1 + "id" : 303 }, { - "id" : 264 + "id" : 304 }, { - "id" : 452 + "id" : 559 }, { - "id" : 265 + "id" : 305 }, { - "id" : 752 + "id" : 601 }, { - "id" : 742 + "id" : 591 }, { - "id" : 371 + "id" : 423 }, { - "id" : 266 + "id" : 306 }, { - "id" : 388 + "id" : 502 }, { - "id" : 406 + "id" : 514 }, { - "id" : 337 + "id" : 382 }, { - "id" : 336 + "id" : 381 }, { - "id" : 405 + "id" : 513 }, { - "id" : 409 + "id" : 555 + }, + { + "id" : 539 + }, + { + "id" : 560 + }, + { + "id" : 561 + }, + { + "id" : 562 + }, + { + "id" : 564 + }, + { + "id" : 326 + }, + { + "id" : 327 + }, + { + "id" : 356 + }, + { + "id" : 328 + }, + { + "id" : 379 + }, + { + "id" : 519 + }, + { + "id" : 518 + }, + { + "id" : 499 + }, + { + "id" : 421 + }, + { + "id" : 427 + }, + { + "id" : 428 + }, + { + "id" : 426 + }, + { + "id" : 550 + }, + { + "id" : 556 }, { "id" : 422 }, { - "id" : 465 + "id" : 386 }, { - "id" : 467 + "id" : 420 }, { - "id" : 468 + "id" : 431 }, { - "id" : 470 + "id" : 508 }, { - "id" : 287 + "id" : 208 }, { - "id" : 288 + "id" : 615 }, { - "id" : 318 - }, - { - "id" : 289 - }, - { - "id" : 334 - }, - { - "id" : 415 - }, - { - "id" : 414 + "id" : 384 }, { "id" : 385 }, { - "id" : 369 + "id" : 500 + }, + { + "id" : 511, + "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgIAaWQAAAIDAGx2bAEAAAA=" + }, + { + "id" : 511, + "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgIAaWQAAAIDAGx2bAIAAAA=" + }, + { + "id" : 511, + "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgIAaWQAAAIDAGx2bAMAAAA=" + }, + { + "id" : 511, + "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgIAaWQAAAIDAGx2bAQAAAA=" + }, + { + "id" : 511, + "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgIAaWQBAAIDAGx2bAEAAAA=" + }, + { + "id" : 511, + "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgIAaWQBAAIDAGx2bAIAAAA=" + }, + { + "id" : 511, + "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgIAaWQBAAIDAGx2bAMAAAA=" + }, + { + "id" : 511, + "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgIAaWQBAAIDAGx2bAQAAAA=" + }, + { + "id" : 511, + "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgIAaWQCAAIDAGx2bAEAAAA=" + }, + { + "id" : 511, + "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgIAaWQCAAIDAGx2bAIAAAA=" + }, + { + "id" : 511, + "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgIAaWQCAAIDAGx2bAMAAAA=" + }, + { + "id" : 511, + "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgIAaWQCAAIDAGx2bAQAAAA=" + }, + { + "id" : 511, + "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgIAaWQDAAIDAGx2bAEAAAA=" + }, + { + "id" : 511, + "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgIAaWQDAAIDAGx2bAIAAAA=" + }, + { + "id" : 511, + "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgIAaWQDAAIDAGx2bAMAAAA=" + }, + { + "id" : 511, + "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgIAaWQDAAIDAGx2bAQAAAA=" + }, + { + "id" : 511, + "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgIAaWQEAAIDAGx2bAEAAAA=" + }, + { + "id" : 511, + "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgIAaWQEAAIDAGx2bAIAAAA=" + }, + { + "id" : 511, + "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgIAaWQEAAIDAGx2bAMAAAA=" + }, + { + "id" : 511, + "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgIAaWQEAAIDAGx2bAQAAAA=" + }, + { + "id" : 511, + "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgIAaWQFAAIDAGx2bAEAAAA=" + }, + { + "id" : 511, + "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgIAaWQFAAIDAGx2bAIAAAA=" + }, + { + "id" : 511, + "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgIAaWQFAAIDAGx2bAMAAAA=" + }, + { + "id" : 511, + "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgIAaWQGAAIDAGx2bAEAAAA=" + }, + { + "id" : 511, + "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgIAaWQGAAIDAGx2bAIAAAA=" + }, + { + "id" : 511, + "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgIAaWQGAAIDAGx2bAMAAAA=" + }, + { + "id" : 511, + "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgIAaWQHAAIDAGx2bAEAAAA=" + }, + { + "id" : 511, + "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgIAaWQHAAIDAGx2bAIAAAA=" + }, + { + "id" : 511, + "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgIAaWQHAAIDAGx2bAMAAAA=" + }, + { + "id" : 511, + "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgIAaWQIAAIDAGx2bAEAAAA=" + }, + { + "id" : 511, + "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgIAaWQJAAIDAGx2bAEAAAA=" + }, + { + "id" : 511, + "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgIAaWQJAAIDAGx2bAIAAAA=" + }, + { + "id" : 511, + "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgIAaWQJAAIDAGx2bAMAAAA=" + }, + { + "id" : 511, + "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgIAaWQJAAIDAGx2bAQAAAA=" + }, + { + "id" : 511, + "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgIAaWQJAAIDAGx2bAUAAAA=" + }, + { + "id" : 511, + "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgIAaWQKAAIDAGx2bAEAAAA=" + }, + { + "id" : 511, + "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgIAaWQKAAIDAGx2bAIAAAA=" + }, + { + "id" : 511, + "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgIAaWQKAAIDAGx2bAMAAAA=" + }, + { + "id" : 511, + "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgIAaWQKAAIDAGx2bAQAAAA=" + }, + { + "id" : 511, + "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgIAaWQKAAIDAGx2bAUAAAA=" + }, + { + "id" : 511, + "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgIAaWQLAAIDAGx2bAEAAAA=" + }, + { + "id" : 511, + "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgIAaWQLAAIDAGx2bAIAAAA=" + }, + { + "id" : 511, + "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgIAaWQLAAIDAGx2bAMAAAA=" + }, + { + "id" : 511, + "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgIAaWQLAAIDAGx2bAQAAAA=" + }, + { + "id" : 511, + "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgIAaWQLAAIDAGx2bAUAAAA=" + }, + { + "id" : 511, + "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgIAaWQMAAIDAGx2bAEAAAA=" + }, + { + "id" : 511, + "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgIAaWQMAAIDAGx2bAIAAAA=" + }, + { + "id" : 511, + "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgIAaWQNAAIDAGx2bAEAAAA=" + }, + { + "id" : 511, + "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgIAaWQNAAIDAGx2bAIAAAA=" + }, + { + "id" : 511, + "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgIAaWQOAAIDAGx2bAEAAAA=" + }, + { + "id" : 511, + "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgIAaWQOAAIDAGx2bAIAAAA=" + }, + { + "id" : 511, + "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgIAaWQOAAIDAGx2bAMAAAA=" + }, + { + "id" : 511, + "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgIAaWQPAAIDAGx2bAEAAAA=" + }, + { + "id" : 511, + "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgIAaWQPAAIDAGx2bAIAAAA=" + }, + { + "id" : 511, + "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgIAaWQPAAIDAGx2bAMAAAA=" + }, + { + "id" : 511, + "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgIAaWQPAAIDAGx2bAQAAAA=" + }, + { + "id" : 511, + "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgIAaWQPAAIDAGx2bAUAAAA=" + }, + { + "id" : 511, + "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgIAaWQQAAIDAGx2bAEAAAA=" + }, + { + "id" : 511, + "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgIAaWQRAAIDAGx2bAEAAAA=" + }, + { + "id" : 511, + "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgIAaWQRAAIDAGx2bAIAAAA=" + }, + { + "id" : 511, + "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgIAaWQRAAIDAGx2bAMAAAA=" + }, + { + "id" : 511, + "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgIAaWQSAAIDAGx2bAEAAAA=" + }, + { + "id" : 511, + "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgIAaWQSAAIDAGx2bAIAAAA=" + }, + { + "id" : 511, + "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgIAaWQSAAIDAGx2bAMAAAA=" + }, + { + "id" : 511, + "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgIAaWQTAAIDAGx2bAEAAAA=" + }, + { + "id" : 511, + "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgIAaWQTAAIDAGx2bAIAAAA=" + }, + { + "id" : 511, + "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgIAaWQTAAIDAGx2bAMAAAA=" + }, + { + "id" : 511, + "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgIAaWQTAAIDAGx2bAQAAAA=" + }, + { + "id" : 511, + "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgIAaWQTAAIDAGx2bAUAAAA=" + }, + { + "id" : 511, + "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgIAaWQUAAIDAGx2bAEAAAA=" + }, + { + "id" : 511, + "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgIAaWQUAAIDAGx2bAIAAAA=" + }, + { + "id" : 511, + "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgIAaWQVAAIDAGx2bAEAAAA=" + }, + { + "id" : 511, + "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgIAaWQWAAIDAGx2bAEAAAA=" + }, + { + "id" : 511, + "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgIAaWQXAAIDAGx2bAEAAAA=" + }, + { + "id" : 511, + "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgIAaWQXAAIDAGx2bAIAAAA=" + }, + { + "id" : 511, + "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgIAaWQXAAIDAGx2bAMAAAA=" + }, + { + "id" : 511, + "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgIAaWQYAAIDAGx2bAEAAAA=" + }, + { + "id" : 511, + "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgIAaWQYAAIDAGx2bAIAAAA=" + }, + { + "id" : 511, + "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgIAaWQYAAIDAGx2bAMAAAA=" + }, + { + "id" : 511, + "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgIAaWQZAAIDAGx2bAEAAAA=" + }, + { + "id" : 511, + "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgIAaWQZAAIDAGx2bAIAAAA=" + }, + { + "id" : 511, + "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgIAaWQaAAIDAGx2bAEAAAA=" + }, + { + "id" : 511, + "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgIAaWQbAAIDAGx2bAEAAAA=" + }, + { + "id" : 511, + "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgIAaWQcAAIDAGx2bAEAAAA=" + }, + { + "id" : 511, + "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgIAaWQdAAIDAGx2bAEAAAA=" + }, + { + "id" : 511, + "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgIAaWQdAAIDAGx2bAIAAAA=" + }, + { + "id" : 511, + "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgIAaWQdAAIDAGx2bAMAAAA=" + }, + { + "id" : 511, + "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgIAaWQdAAIDAGx2bAQAAAA=" + }, + { + "id" : 511, + "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgIAaWQdAAIDAGx2bAUAAAA=" + }, + { + "id" : 511, + "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgIAaWQeAAIDAGx2bAEAAAA=" + }, + { + "id" : 511, + "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgIAaWQeAAIDAGx2bAIAAAA=" + }, + { + "id" : 511, + "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgIAaWQeAAIDAGx2bAMAAAA=" + }, + { + "id" : 511, + "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgIAaWQfAAIDAGx2bAEAAAA=" + }, + { + "id" : 511, + "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgIAaWQfAAIDAGx2bAIAAAA=" + }, + { + "id" : 511, + "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgIAaWQfAAIDAGx2bAMAAAA=" + }, + { + "id" : 511, + "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgIAaWQgAAIDAGx2bAEAAAA=" + }, + { + "id" : 511, + "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgIAaWQhAAIDAGx2bAEAAAA=" + }, + { + "id" : 511, + "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgIAaWQiAAIDAGx2bAEAAAA=" + }, + { + "id" : 511, + "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgIAaWQiAAIDAGx2bAIAAAA=" + }, + { + "id" : 511, + "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgIAaWQiAAIDAGx2bAMAAAA=" + }, + { + "id" : 511, + "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgIAaWQiAAIDAGx2bAQAAAA=" + }, + { + "id" : 511, + "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgIAaWQjAAIDAGx2bAEAAAA=" + }, + { + "id" : 511, + "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgIAaWQjAAIDAGx2bAIAAAA=" + }, + { + "id" : 511, + "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgIAaWQjAAIDAGx2bAMAAAA=" + }, + { + "id" : 511, + "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgIAaWQkAAIDAGx2bAEAAAA=" + }, + { + "id" : 511, + "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgIAaWQkAAIDAGx2bAIAAAA=" + }, + { + "id" : 511, + "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgIAaWQkAAIDAGx2bAMAAAA=" + }, + { + "id" : 373 + }, + { + "id" : 376 + }, + { + "id" : 374 + }, + { + "id" : 375 }, { "id" : 377 @@ -3639,496 +4033,6 @@ { "id" : 378 }, - { - "id" : 376 - }, - { - "id" : 437 - }, - { - "id" : 445 - }, - { - "id" : 370 - }, - { - "id" : 341 - }, - { - "id" : 368 - }, - { - "id" : 381 - }, - { - "id" : 399 - }, - { - "id" : 208 - }, - { - "id" : 426 - }, - { - "id" : 339 - }, - { - "id" : 340 - }, - { - "id" : 386 - }, - { - "id" : 403, - "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgMAbHZsAQACAgBpZAAAAAA=" - }, - { - "id" : 403, - "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgMAbHZsAgACAgBpZAAAAAA=" - }, - { - "id" : 403, - "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgMAbHZsAwACAgBpZAAAAAA=" - }, - { - "id" : 403, - "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgMAbHZsBAACAgBpZAAAAAA=" - }, - { - "id" : 403, - "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgMAbHZsAQACAgBpZAEAAAA=" - }, - { - "id" : 403, - "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgMAbHZsAgACAgBpZAEAAAA=" - }, - { - "id" : 403, - "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgMAbHZsAwACAgBpZAEAAAA=" - }, - { - "id" : 403, - "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgMAbHZsBAACAgBpZAEAAAA=" - }, - { - "id" : 403, - "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgMAbHZsAQACAgBpZAIAAAA=" - }, - { - "id" : 403, - "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgMAbHZsAgACAgBpZAIAAAA=" - }, - { - "id" : 403, - "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgMAbHZsAwACAgBpZAIAAAA=" - }, - { - "id" : 403, - "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgMAbHZsBAACAgBpZAIAAAA=" - }, - { - "id" : 403, - "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgMAbHZsAQACAgBpZAMAAAA=" - }, - { - "id" : 403, - "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgMAbHZsAgACAgBpZAMAAAA=" - }, - { - "id" : 403, - "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgMAbHZsAwACAgBpZAMAAAA=" - }, - { - "id" : 403, - "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgMAbHZsBAACAgBpZAMAAAA=" - }, - { - "id" : 403, - "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgMAbHZsAQACAgBpZAQAAAA=" - }, - { - "id" : 403, - "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgMAbHZsAgACAgBpZAQAAAA=" - }, - { - "id" : 403, - "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgMAbHZsAwACAgBpZAQAAAA=" - }, - { - "id" : 403, - "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgMAbHZsBAACAgBpZAQAAAA=" - }, - { - "id" : 403, - "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgMAbHZsAQACAgBpZAUAAAA=" - }, - { - "id" : 403, - "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgMAbHZsAgACAgBpZAUAAAA=" - }, - { - "id" : 403, - "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgMAbHZsAwACAgBpZAUAAAA=" - }, - { - "id" : 403, - "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgMAbHZsAQACAgBpZAYAAAA=" - }, - { - "id" : 403, - "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgMAbHZsAgACAgBpZAYAAAA=" - }, - { - "id" : 403, - "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgMAbHZsAwACAgBpZAYAAAA=" - }, - { - "id" : 403, - "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgMAbHZsAQACAgBpZAcAAAA=" - }, - { - "id" : 403, - "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgMAbHZsAgACAgBpZAcAAAA=" - }, - { - "id" : 403, - "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgMAbHZsAwACAgBpZAcAAAA=" - }, - { - "id" : 403, - "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgMAbHZsAQACAgBpZAgAAAA=" - }, - { - "id" : 403, - "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgMAbHZsAQACAgBpZAkAAAA=" - }, - { - "id" : 403, - "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgMAbHZsAgACAgBpZAkAAAA=" - }, - { - "id" : 403, - "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgMAbHZsAwACAgBpZAkAAAA=" - }, - { - "id" : 403, - "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgMAbHZsBAACAgBpZAkAAAA=" - }, - { - "id" : 403, - "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgMAbHZsBQACAgBpZAkAAAA=" - }, - { - "id" : 403, - "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgMAbHZsAQACAgBpZAoAAAA=" - }, - { - "id" : 403, - "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgMAbHZsAgACAgBpZAoAAAA=" - }, - { - "id" : 403, - "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgMAbHZsAwACAgBpZAoAAAA=" - }, - { - "id" : 403, - "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgMAbHZsBAACAgBpZAoAAAA=" - }, - { - "id" : 403, - "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgMAbHZsBQACAgBpZAoAAAA=" - }, - { - "id" : 403, - "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgMAbHZsAQACAgBpZAsAAAA=" - }, - { - "id" : 403, - "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgMAbHZsAgACAgBpZAsAAAA=" - }, - { - "id" : 403, - "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgMAbHZsAwACAgBpZAsAAAA=" - }, - { - "id" : 403, - "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgMAbHZsBAACAgBpZAsAAAA=" - }, - { - "id" : 403, - "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgMAbHZsBQACAgBpZAsAAAA=" - }, - { - "id" : 403, - "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgMAbHZsAQACAgBpZAwAAAA=" - }, - { - "id" : 403, - "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgMAbHZsAgACAgBpZAwAAAA=" - }, - { - "id" : 403, - "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgMAbHZsAQACAgBpZA0AAAA=" - }, - { - "id" : 403, - "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgMAbHZsAgACAgBpZA0AAAA=" - }, - { - "id" : 403, - "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgMAbHZsAQACAgBpZA4AAAA=" - }, - { - "id" : 403, - "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgMAbHZsAgACAgBpZA4AAAA=" - }, - { - "id" : 403, - "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgMAbHZsAwACAgBpZA4AAAA=" - }, - { - "id" : 403, - "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgMAbHZsAQACAgBpZA8AAAA=" - }, - { - "id" : 403, - "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgMAbHZsAgACAgBpZA8AAAA=" - }, - { - "id" : 403, - "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgMAbHZsAwACAgBpZA8AAAA=" - }, - { - "id" : 403, - "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgMAbHZsBAACAgBpZA8AAAA=" - }, - { - "id" : 403, - "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgMAbHZsBQACAgBpZA8AAAA=" - }, - { - "id" : 403, - "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgMAbHZsAQACAgBpZBAAAAA=" - }, - { - "id" : 403, - "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgMAbHZsAQACAgBpZBEAAAA=" - }, - { - "id" : 403, - "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgMAbHZsAgACAgBpZBEAAAA=" - }, - { - "id" : 403, - "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgMAbHZsAwACAgBpZBEAAAA=" - }, - { - "id" : 403, - "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgMAbHZsAQACAgBpZBIAAAA=" - }, - { - "id" : 403, - "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgMAbHZsAgACAgBpZBIAAAA=" - }, - { - "id" : 403, - "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgMAbHZsAwACAgBpZBIAAAA=" - }, - { - "id" : 403, - "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgMAbHZsAQACAgBpZBMAAAA=" - }, - { - "id" : 403, - "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgMAbHZsAgACAgBpZBMAAAA=" - }, - { - "id" : 403, - "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgMAbHZsAwACAgBpZBMAAAA=" - }, - { - "id" : 403, - "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgMAbHZsBAACAgBpZBMAAAA=" - }, - { - "id" : 403, - "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgMAbHZsBQACAgBpZBMAAAA=" - }, - { - "id" : 403, - "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgMAbHZsAQACAgBpZBQAAAA=" - }, - { - "id" : 403, - "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgMAbHZsAgACAgBpZBQAAAA=" - }, - { - "id" : 403, - "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgMAbHZsAQACAgBpZBUAAAA=" - }, - { - "id" : 403, - "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgMAbHZsAQACAgBpZBYAAAA=" - }, - { - "id" : 403, - "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgMAbHZsAQACAgBpZBcAAAA=" - }, - { - "id" : 403, - "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgMAbHZsAgACAgBpZBcAAAA=" - }, - { - "id" : 403, - "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgMAbHZsAwACAgBpZBcAAAA=" - }, - { - "id" : 403, - "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgMAbHZsAQACAgBpZBgAAAA=" - }, - { - "id" : 403, - "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgMAbHZsAgACAgBpZBgAAAA=" - }, - { - "id" : 403, - "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgMAbHZsAwACAgBpZBgAAAA=" - }, - { - "id" : 403, - "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgMAbHZsAQACAgBpZBkAAAA=" - }, - { - "id" : 403, - "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgMAbHZsAgACAgBpZBkAAAA=" - }, - { - "id" : 403, - "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgMAbHZsAQACAgBpZBoAAAA=" - }, - { - "id" : 403, - "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgMAbHZsAQACAgBpZBsAAAA=" - }, - { - "id" : 403, - "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgMAbHZsAQACAgBpZBwAAAA=" - }, - { - "id" : 403, - "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgMAbHZsAQACAgBpZB0AAAA=" - }, - { - "id" : 403, - "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgMAbHZsAgACAgBpZB0AAAA=" - }, - { - "id" : 403, - "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgMAbHZsAwACAgBpZB0AAAA=" - }, - { - "id" : 403, - "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgMAbHZsBAACAgBpZB0AAAA=" - }, - { - "id" : 403, - "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgMAbHZsBQACAgBpZB0AAAA=" - }, - { - "id" : 403, - "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgMAbHZsAQACAgBpZB4AAAA=" - }, - { - "id" : 403, - "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgMAbHZsAgACAgBpZB4AAAA=" - }, - { - "id" : 403, - "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgMAbHZsAwACAgBpZB4AAAA=" - }, - { - "id" : 403, - "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgMAbHZsAQACAgBpZB8AAAA=" - }, - { - "id" : 403, - "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgMAbHZsAgACAgBpZB8AAAA=" - }, - { - "id" : 403, - "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgMAbHZsAwACAgBpZB8AAAA=" - }, - { - "id" : 403, - "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgMAbHZsAQACAgBpZCAAAAA=" - }, - { - "id" : 403, - "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgMAbHZsAQACAgBpZCEAAAA=" - }, - { - "id" : 403, - "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgMAbHZsAQACAgBpZCIAAAA=" - }, - { - "id" : 403, - "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgMAbHZsAgACAgBpZCIAAAA=" - }, - { - "id" : 403, - "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgMAbHZsAwACAgBpZCIAAAA=" - }, - { - "id" : 403, - "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgMAbHZsBAACAgBpZCIAAAA=" - }, - { - "id" : 403, - "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgMAbHZsAQACAgBpZCMAAAA=" - }, - { - "id" : 403, - "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgMAbHZsAgACAgBpZCMAAAA=" - }, - { - "id" : 403, - "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgMAbHZsAwACAgBpZCMAAAA=" - }, - { - "id" : 403, - "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgMAbHZsAQACAgBpZCQAAAA=" - }, - { - "id" : 403, - "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgMAbHZsAgACAgBpZCQAAAA=" - }, - { - "id" : 403, - "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgMAbHZsAwACAgBpZCQAAAA=" - }, - { - "id" : 333 - }, - { - "id" : 333, - "damage" : 1 - }, - { - "id" : 333, - "damage" : 2 - }, - { - "id" : 333, - "damage" : 3 - }, - { - "id" : 333, - "damage" : 4 - }, - { - "id" : 333, - "damage" : 5 - }, { "id" : 66 }, @@ -4142,19 +4046,19 @@ "id" : 126 }, { - "id" : 328 + "id" : 368 }, { - "id" : 342 + "id" : 387 }, { - "id" : 408 + "id" : 516 }, { - "id" : 407 + "id" : 515 }, { - "id" : 331 + "id" : 371 }, { "id" : 152 @@ -4241,13 +4145,13 @@ "id" : 151 }, { - "id" : 356 + "id" : 417 }, { - "id" : 404 + "id" : 512 }, { - "id" : 410 + "id" : 517 }, { "id" : 125, @@ -4269,252 +4173,255 @@ "id" : 46 }, { - "id" : 421 + "id" : 538 }, { "id" : -204 }, { - "id" : 446 + "id" : 557 }, { - "id" : 446, + "id" : 557, "damage" : 8 }, { - "id" : 446, + "id" : 557, "damage" : 7 }, { - "id" : 446, + "id" : 557, "damage" : 15 }, { - "id" : 446, + "id" : 557, "damage" : 12 }, { - "id" : 446, + "id" : 557, "damage" : 14 }, { - "id" : 446, + "id" : 557, "damage" : 1 }, { - "id" : 446, + "id" : 557, "damage" : 4 }, { - "id" : 446, + "id" : 557, "damage" : 5 }, { - "id" : 446, + "id" : 557, "damage" : 13 }, { - "id" : 446, + "id" : 557, "damage" : 9 }, { - "id" : 446, + "id" : 557, "damage" : 3 }, { - "id" : 446, + "id" : 557, "damage" : 11 }, { - "id" : 446, + "id" : 557, "damage" : 10 }, { - "id" : 446, + "id" : 557, "damage" : 2 }, { - "id" : 446, + "id" : 557, "damage" : 6 }, { - "id" : 446, + "id" : 557, "damage" : 15, "nbt_b64" : "CgAAAwQAVHlwZQEAAAAA" }, { - "id" : 434 + "id" : 572 }, { - "id" : 434, - "damage" : 1 + "id" : 573 }, { - "id" : 434, - "damage" : 2 + "id" : 571 }, { - "id" : 434, - "damage" : 3 + "id" : 574 }, { - "id" : 434, - "damage" : 4 + "id" : 575 }, { - "id" : 434, - "damage" : 5 + "id" : 576 }, { - "id" : 434, - "damage" : 6 + "id" : 577 }, { - "id" : 401, - "nbt_b64" : "CgAACgkARmlyZXdvcmtzAQYARmxpZ2h0AQkKAEV4cGxvc2lvbnMAAAAAAAAA" + "id" : 509, + "nbt_b64" : "CgAACgkARmlyZXdvcmtzCQoARXhwbG9zaW9ucwAAAAAAAQYARmxpZ2h0AQAA" }, { - "id" : 401, - "nbt_b64" : "CgAACgkARmlyZXdvcmtzAQYARmxpZ2h0AQkKAEV4cGxvc2lvbnMKAQAAAAcNAEZpcmV3b3JrQ29sb3IBAAAAAAEMAEZpcmV3b3JrVHlwZQAHDABGaXJld29ya0ZhZGUAAAAAAQ0ARmlyZXdvcmtUcmFpbAABDwBGaXJld29ya0ZsaWNrZXIAAAAA" + "id" : 509, + "nbt_b64" : "CgAACgkARmlyZXdvcmtzCQoARXhwbG9zaW9ucwoBAAAABw0ARmlyZXdvcmtDb2xvcgEAAAAABwwARmlyZXdvcmtGYWRlAAAAAAEPAEZpcmV3b3JrRmxpY2tlcgABDQBGaXJld29ya1RyYWlsAAEMAEZpcmV3b3JrVHlwZQAAAQYARmxpZ2h0AQAA" }, { - "id" : 401, - "nbt_b64" : "CgAACgkARmlyZXdvcmtzAQYARmxpZ2h0AQkKAEV4cGxvc2lvbnMKAQAAAAcNAEZpcmV3b3JrQ29sb3IBAAAACAEMAEZpcmV3b3JrVHlwZQAHDABGaXJld29ya0ZhZGUAAAAAAQ0ARmlyZXdvcmtUcmFpbAABDwBGaXJld29ya0ZsaWNrZXIAAAAA" + "id" : 509, + "nbt_b64" : "CgAACgkARmlyZXdvcmtzCQoARXhwbG9zaW9ucwoBAAAABw0ARmlyZXdvcmtDb2xvcgEAAAAIBwwARmlyZXdvcmtGYWRlAAAAAAEPAEZpcmV3b3JrRmxpY2tlcgABDQBGaXJld29ya1RyYWlsAAEMAEZpcmV3b3JrVHlwZQAAAQYARmxpZ2h0AQAA" }, { - "id" : 401, - "nbt_b64" : "CgAACgkARmlyZXdvcmtzAQYARmxpZ2h0AQkKAEV4cGxvc2lvbnMKAQAAAAcNAEZpcmV3b3JrQ29sb3IBAAAABwEMAEZpcmV3b3JrVHlwZQAHDABGaXJld29ya0ZhZGUAAAAAAQ0ARmlyZXdvcmtUcmFpbAABDwBGaXJld29ya0ZsaWNrZXIAAAAA" + "id" : 509, + "nbt_b64" : "CgAACgkARmlyZXdvcmtzCQoARXhwbG9zaW9ucwoBAAAABw0ARmlyZXdvcmtDb2xvcgEAAAAHBwwARmlyZXdvcmtGYWRlAAAAAAEPAEZpcmV3b3JrRmxpY2tlcgABDQBGaXJld29ya1RyYWlsAAEMAEZpcmV3b3JrVHlwZQAAAQYARmxpZ2h0AQAA" }, { - "id" : 401, - "nbt_b64" : "CgAACgkARmlyZXdvcmtzAQYARmxpZ2h0AQkKAEV4cGxvc2lvbnMKAQAAAAcNAEZpcmV3b3JrQ29sb3IBAAAADwEMAEZpcmV3b3JrVHlwZQAHDABGaXJld29ya0ZhZGUAAAAAAQ0ARmlyZXdvcmtUcmFpbAABDwBGaXJld29ya0ZsaWNrZXIAAAAA" + "id" : 509, + "nbt_b64" : "CgAACgkARmlyZXdvcmtzCQoARXhwbG9zaW9ucwoBAAAABw0ARmlyZXdvcmtDb2xvcgEAAAAPBwwARmlyZXdvcmtGYWRlAAAAAAEPAEZpcmV3b3JrRmxpY2tlcgABDQBGaXJld29ya1RyYWlsAAEMAEZpcmV3b3JrVHlwZQAAAQYARmxpZ2h0AQAA" }, { - "id" : 401, - "nbt_b64" : "CgAACgkARmlyZXdvcmtzAQYARmxpZ2h0AQkKAEV4cGxvc2lvbnMKAQAAAAcNAEZpcmV3b3JrQ29sb3IBAAAADAEMAEZpcmV3b3JrVHlwZQAHDABGaXJld29ya0ZhZGUAAAAAAQ0ARmlyZXdvcmtUcmFpbAABDwBGaXJld29ya0ZsaWNrZXIAAAAA" + "id" : 509, + "nbt_b64" : "CgAACgkARmlyZXdvcmtzCQoARXhwbG9zaW9ucwoBAAAABw0ARmlyZXdvcmtDb2xvcgEAAAAMBwwARmlyZXdvcmtGYWRlAAAAAAEPAEZpcmV3b3JrRmxpY2tlcgABDQBGaXJld29ya1RyYWlsAAEMAEZpcmV3b3JrVHlwZQAAAQYARmxpZ2h0AQAA" }, { - "id" : 401, - "nbt_b64" : "CgAACgkARmlyZXdvcmtzAQYARmxpZ2h0AQkKAEV4cGxvc2lvbnMKAQAAAAcNAEZpcmV3b3JrQ29sb3IBAAAADgEMAEZpcmV3b3JrVHlwZQAHDABGaXJld29ya0ZhZGUAAAAAAQ0ARmlyZXdvcmtUcmFpbAABDwBGaXJld29ya0ZsaWNrZXIAAAAA" + "id" : 509, + "nbt_b64" : "CgAACgkARmlyZXdvcmtzCQoARXhwbG9zaW9ucwoBAAAABw0ARmlyZXdvcmtDb2xvcgEAAAAOBwwARmlyZXdvcmtGYWRlAAAAAAEPAEZpcmV3b3JrRmxpY2tlcgABDQBGaXJld29ya1RyYWlsAAEMAEZpcmV3b3JrVHlwZQAAAQYARmxpZ2h0AQAA" }, { - "id" : 401, - "nbt_b64" : "CgAACgkARmlyZXdvcmtzAQYARmxpZ2h0AQkKAEV4cGxvc2lvbnMKAQAAAAcNAEZpcmV3b3JrQ29sb3IBAAAAAQEMAEZpcmV3b3JrVHlwZQAHDABGaXJld29ya0ZhZGUAAAAAAQ0ARmlyZXdvcmtUcmFpbAABDwBGaXJld29ya0ZsaWNrZXIAAAAA" + "id" : 509, + "nbt_b64" : "CgAACgkARmlyZXdvcmtzCQoARXhwbG9zaW9ucwoBAAAABw0ARmlyZXdvcmtDb2xvcgEAAAABBwwARmlyZXdvcmtGYWRlAAAAAAEPAEZpcmV3b3JrRmxpY2tlcgABDQBGaXJld29ya1RyYWlsAAEMAEZpcmV3b3JrVHlwZQAAAQYARmxpZ2h0AQAA" }, { - "id" : 401, - "nbt_b64" : "CgAACgkARmlyZXdvcmtzAQYARmxpZ2h0AQkKAEV4cGxvc2lvbnMKAQAAAAcNAEZpcmV3b3JrQ29sb3IBAAAABAEMAEZpcmV3b3JrVHlwZQAHDABGaXJld29ya0ZhZGUAAAAAAQ0ARmlyZXdvcmtUcmFpbAABDwBGaXJld29ya0ZsaWNrZXIAAAAA" + "id" : 509, + "nbt_b64" : "CgAACgkARmlyZXdvcmtzCQoARXhwbG9zaW9ucwoBAAAABw0ARmlyZXdvcmtDb2xvcgEAAAAEBwwARmlyZXdvcmtGYWRlAAAAAAEPAEZpcmV3b3JrRmxpY2tlcgABDQBGaXJld29ya1RyYWlsAAEMAEZpcmV3b3JrVHlwZQAAAQYARmxpZ2h0AQAA" }, { - "id" : 401, - "nbt_b64" : "CgAACgkARmlyZXdvcmtzAQYARmxpZ2h0AQkKAEV4cGxvc2lvbnMKAQAAAAcNAEZpcmV3b3JrQ29sb3IBAAAABQEMAEZpcmV3b3JrVHlwZQAHDABGaXJld29ya0ZhZGUAAAAAAQ0ARmlyZXdvcmtUcmFpbAABDwBGaXJld29ya0ZsaWNrZXIAAAAA" + "id" : 509, + "nbt_b64" : "CgAACgkARmlyZXdvcmtzCQoARXhwbG9zaW9ucwoBAAAABw0ARmlyZXdvcmtDb2xvcgEAAAAFBwwARmlyZXdvcmtGYWRlAAAAAAEPAEZpcmV3b3JrRmxpY2tlcgABDQBGaXJld29ya1RyYWlsAAEMAEZpcmV3b3JrVHlwZQAAAQYARmxpZ2h0AQAA" }, { - "id" : 401, - "nbt_b64" : "CgAACgkARmlyZXdvcmtzAQYARmxpZ2h0AQkKAEV4cGxvc2lvbnMKAQAAAAcNAEZpcmV3b3JrQ29sb3IBAAAADQEMAEZpcmV3b3JrVHlwZQAHDABGaXJld29ya0ZhZGUAAAAAAQ0ARmlyZXdvcmtUcmFpbAABDwBGaXJld29ya0ZsaWNrZXIAAAAA" + "id" : 509, + "nbt_b64" : "CgAACgkARmlyZXdvcmtzCQoARXhwbG9zaW9ucwoBAAAABw0ARmlyZXdvcmtDb2xvcgEAAAANBwwARmlyZXdvcmtGYWRlAAAAAAEPAEZpcmV3b3JrRmxpY2tlcgABDQBGaXJld29ya1RyYWlsAAEMAEZpcmV3b3JrVHlwZQAAAQYARmxpZ2h0AQAA" }, { - "id" : 401, - "nbt_b64" : "CgAACgkARmlyZXdvcmtzAQYARmxpZ2h0AQkKAEV4cGxvc2lvbnMKAQAAAAcNAEZpcmV3b3JrQ29sb3IBAAAACQEMAEZpcmV3b3JrVHlwZQAHDABGaXJld29ya0ZhZGUAAAAAAQ0ARmlyZXdvcmtUcmFpbAABDwBGaXJld29ya0ZsaWNrZXIAAAAA" + "id" : 509, + "nbt_b64" : "CgAACgkARmlyZXdvcmtzCQoARXhwbG9zaW9ucwoBAAAABw0ARmlyZXdvcmtDb2xvcgEAAAAJBwwARmlyZXdvcmtGYWRlAAAAAAEPAEZpcmV3b3JrRmxpY2tlcgABDQBGaXJld29ya1RyYWlsAAEMAEZpcmV3b3JrVHlwZQAAAQYARmxpZ2h0AQAA" }, { - "id" : 401, - "nbt_b64" : "CgAACgkARmlyZXdvcmtzAQYARmxpZ2h0AQkKAEV4cGxvc2lvbnMKAQAAAAcNAEZpcmV3b3JrQ29sb3IBAAAAAwEMAEZpcmV3b3JrVHlwZQAHDABGaXJld29ya0ZhZGUAAAAAAQ0ARmlyZXdvcmtUcmFpbAABDwBGaXJld29ya0ZsaWNrZXIAAAAA" + "id" : 509, + "nbt_b64" : "CgAACgkARmlyZXdvcmtzCQoARXhwbG9zaW9ucwoBAAAABw0ARmlyZXdvcmtDb2xvcgEAAAADBwwARmlyZXdvcmtGYWRlAAAAAAEPAEZpcmV3b3JrRmxpY2tlcgABDQBGaXJld29ya1RyYWlsAAEMAEZpcmV3b3JrVHlwZQAAAQYARmxpZ2h0AQAA" }, { - "id" : 401, - "nbt_b64" : "CgAACgkARmlyZXdvcmtzAQYARmxpZ2h0AQkKAEV4cGxvc2lvbnMKAQAAAAcNAEZpcmV3b3JrQ29sb3IBAAAACwEMAEZpcmV3b3JrVHlwZQAHDABGaXJld29ya0ZhZGUAAAAAAQ0ARmlyZXdvcmtUcmFpbAABDwBGaXJld29ya0ZsaWNrZXIAAAAA" + "id" : 509, + "nbt_b64" : "CgAACgkARmlyZXdvcmtzCQoARXhwbG9zaW9ucwoBAAAABw0ARmlyZXdvcmtDb2xvcgEAAAALBwwARmlyZXdvcmtGYWRlAAAAAAEPAEZpcmV3b3JrRmxpY2tlcgABDQBGaXJld29ya1RyYWlsAAEMAEZpcmV3b3JrVHlwZQAAAQYARmxpZ2h0AQAA" }, { - "id" : 401, - "nbt_b64" : "CgAACgkARmlyZXdvcmtzAQYARmxpZ2h0AQkKAEV4cGxvc2lvbnMKAQAAAAcNAEZpcmV3b3JrQ29sb3IBAAAACgEMAEZpcmV3b3JrVHlwZQAHDABGaXJld29ya0ZhZGUAAAAAAQ0ARmlyZXdvcmtUcmFpbAABDwBGaXJld29ya0ZsaWNrZXIAAAAA" + "id" : 509, + "nbt_b64" : "CgAACgkARmlyZXdvcmtzCQoARXhwbG9zaW9ucwoBAAAABw0ARmlyZXdvcmtDb2xvcgEAAAAKBwwARmlyZXdvcmtGYWRlAAAAAAEPAEZpcmV3b3JrRmxpY2tlcgABDQBGaXJld29ya1RyYWlsAAEMAEZpcmV3b3JrVHlwZQAAAQYARmxpZ2h0AQAA" }, { - "id" : 401, - "nbt_b64" : "CgAACgkARmlyZXdvcmtzAQYARmxpZ2h0AQkKAEV4cGxvc2lvbnMKAQAAAAcNAEZpcmV3b3JrQ29sb3IBAAAAAgEMAEZpcmV3b3JrVHlwZQAHDABGaXJld29ya0ZhZGUAAAAAAQ0ARmlyZXdvcmtUcmFpbAABDwBGaXJld29ya0ZsaWNrZXIAAAAA" + "id" : 509, + "nbt_b64" : "CgAACgkARmlyZXdvcmtzCQoARXhwbG9zaW9ucwoBAAAABw0ARmlyZXdvcmtDb2xvcgEAAAACBwwARmlyZXdvcmtGYWRlAAAAAAEPAEZpcmV3b3JrRmxpY2tlcgABDQBGaXJld29ya1RyYWlsAAEMAEZpcmV3b3JrVHlwZQAAAQYARmxpZ2h0AQAA" }, { - "id" : 401, - "nbt_b64" : "CgAACgkARmlyZXdvcmtzAQYARmxpZ2h0AQkKAEV4cGxvc2lvbnMKAQAAAAcNAEZpcmV3b3JrQ29sb3IBAAAABgEMAEZpcmV3b3JrVHlwZQAHDABGaXJld29ya0ZhZGUAAAAAAQ0ARmlyZXdvcmtUcmFpbAABDwBGaXJld29ya0ZsaWNrZXIAAAAA" + "id" : 509, + "nbt_b64" : "CgAACgkARmlyZXdvcmtzCQoARXhwbG9zaW9ucwoBAAAABw0ARmlyZXdvcmtDb2xvcgEAAAAGBwwARmlyZXdvcmtGYWRlAAAAAAEPAEZpcmV3b3JrRmxpY2tlcgABDQBGaXJld29ya1RyYWlsAAEMAEZpcmV3b3JrVHlwZQAAAQYARmxpZ2h0AQAA" }, { - "id" : 402, - "nbt_b64" : "CgAAAwsAY3VzdG9tQ29sb3IhHR3/Cg0ARmlyZXdvcmtzSXRlbQcNAEZpcmV3b3JrQ29sb3IBAAAAAAEMAEZpcmV3b3JrVHlwZQAHDABGaXJld29ya0ZhZGUAAAAAAQ0ARmlyZXdvcmtUcmFpbAABDwBGaXJld29ya0ZsaWNrZXIAAAA=" + "id" : 510, + "nbt_b64" : "CgAACg0ARmlyZXdvcmtzSXRlbQcNAEZpcmV3b3JrQ29sb3IBAAAAAAcMAEZpcmV3b3JrRmFkZQAAAAABDwBGaXJld29ya0ZsaWNrZXIAAQ0ARmlyZXdvcmtUcmFpbAABDABGaXJld29ya1R5cGUAAAMLAGN1c3RvbUNvbG9yIR0d/wA=" }, { - "id" : 402, + "id" : 510, "damage" : 8, - "nbt_b64" : "CgAAAwsAY3VzdG9tQ29sb3JST0f/Cg0ARmlyZXdvcmtzSXRlbQcNAEZpcmV3b3JrQ29sb3IBAAAACAEMAEZpcmV3b3JrVHlwZQAHDABGaXJld29ya0ZhZGUAAAAAAQ0ARmlyZXdvcmtUcmFpbAABDwBGaXJld29ya0ZsaWNrZXIAAAA=" + "nbt_b64" : "CgAACg0ARmlyZXdvcmtzSXRlbQcNAEZpcmV3b3JrQ29sb3IBAAAACAcMAEZpcmV3b3JrRmFkZQAAAAABDwBGaXJld29ya0ZsaWNrZXIAAQ0ARmlyZXdvcmtUcmFpbAABDABGaXJld29ya1R5cGUAAAMLAGN1c3RvbUNvbG9yUk9H/wA=" }, { - "id" : 402, + "id" : 510, "damage" : 7, - "nbt_b64" : "CgAAAwsAY3VzdG9tQ29sb3KXnZ3/Cg0ARmlyZXdvcmtzSXRlbQcNAEZpcmV3b3JrQ29sb3IBAAAABwEMAEZpcmV3b3JrVHlwZQAHDABGaXJld29ya0ZhZGUAAAAAAQ0ARmlyZXdvcmtUcmFpbAABDwBGaXJld29ya0ZsaWNrZXIAAAA=" + "nbt_b64" : "CgAACg0ARmlyZXdvcmtzSXRlbQcNAEZpcmV3b3JrQ29sb3IBAAAABwcMAEZpcmV3b3JrRmFkZQAAAAABDwBGaXJld29ya0ZsaWNrZXIAAQ0ARmlyZXdvcmtUcmFpbAABDABGaXJld29ya1R5cGUAAAMLAGN1c3RvbUNvbG9yl52d/wA=" }, { - "id" : 402, + "id" : 510, "damage" : 15, - "nbt_b64" : "CgAAAwsAY3VzdG9tQ29sb3Lw8PD/Cg0ARmlyZXdvcmtzSXRlbQcNAEZpcmV3b3JrQ29sb3IBAAAADwEMAEZpcmV3b3JrVHlwZQAHDABGaXJld29ya0ZhZGUAAAAAAQ0ARmlyZXdvcmtUcmFpbAABDwBGaXJld29ya0ZsaWNrZXIAAAA=" + "nbt_b64" : "CgAACg0ARmlyZXdvcmtzSXRlbQcNAEZpcmV3b3JrQ29sb3IBAAAADwcMAEZpcmV3b3JrRmFkZQAAAAABDwBGaXJld29ya0ZsaWNrZXIAAQ0ARmlyZXdvcmtUcmFpbAABDABGaXJld29ya1R5cGUAAAMLAGN1c3RvbUNvbG9y8PDw/wA=" }, { - "id" : 402, + "id" : 510, "damage" : 12, - "nbt_b64" : "CgAAAwsAY3VzdG9tQ29sb3Laszr/Cg0ARmlyZXdvcmtzSXRlbQcNAEZpcmV3b3JrQ29sb3IBAAAADAEMAEZpcmV3b3JrVHlwZQAHDABGaXJld29ya0ZhZGUAAAAAAQ0ARmlyZXdvcmtUcmFpbAABDwBGaXJld29ya0ZsaWNrZXIAAAA=" + "nbt_b64" : "CgAACg0ARmlyZXdvcmtzSXRlbQcNAEZpcmV3b3JrQ29sb3IBAAAADAcMAEZpcmV3b3JrRmFkZQAAAAABDwBGaXJld29ya0ZsaWNrZXIAAQ0ARmlyZXdvcmtUcmFpbAABDABGaXJld29ya1R5cGUAAAMLAGN1c3RvbUNvbG9y2rM6/wA=" }, { - "id" : 402, + "id" : 510, "damage" : 14, - "nbt_b64" : "CgAAAwsAY3VzdG9tQ29sb3IdgPn/Cg0ARmlyZXdvcmtzSXRlbQcNAEZpcmV3b3JrQ29sb3IBAAAADgEMAEZpcmV3b3JrVHlwZQAHDABGaXJld29ya0ZhZGUAAAAAAQ0ARmlyZXdvcmtUcmFpbAABDwBGaXJld29ya0ZsaWNrZXIAAAA=" + "nbt_b64" : "CgAACg0ARmlyZXdvcmtzSXRlbQcNAEZpcmV3b3JrQ29sb3IBAAAADgcMAEZpcmV3b3JrRmFkZQAAAAABDwBGaXJld29ya0ZsaWNrZXIAAQ0ARmlyZXdvcmtUcmFpbAABDABGaXJld29ya1R5cGUAAAMLAGN1c3RvbUNvbG9yHYD5/wA=" }, { - "id" : 402, + "id" : 510, "damage" : 1, - "nbt_b64" : "CgAAAwsAY3VzdG9tQ29sb3ImLrD/Cg0ARmlyZXdvcmtzSXRlbQcNAEZpcmV3b3JrQ29sb3IBAAAAAQEMAEZpcmV3b3JrVHlwZQAHDABGaXJld29ya0ZhZGUAAAAAAQ0ARmlyZXdvcmtUcmFpbAABDwBGaXJld29ya0ZsaWNrZXIAAAA=" + "nbt_b64" : "CgAACg0ARmlyZXdvcmtzSXRlbQcNAEZpcmV3b3JrQ29sb3IBAAAAAQcMAEZpcmV3b3JrRmFkZQAAAAABDwBGaXJld29ya0ZsaWNrZXIAAQ0ARmlyZXdvcmtUcmFpbAABDABGaXJld29ya1R5cGUAAAMLAGN1c3RvbUNvbG9yJi6w/wA=" }, { - "id" : 402, + "id" : 510, "damage" : 4, - "nbt_b64" : "CgAAAwsAY3VzdG9tQ29sb3KqRDz/Cg0ARmlyZXdvcmtzSXRlbQcNAEZpcmV3b3JrQ29sb3IBAAAABAEMAEZpcmV3b3JrVHlwZQAHDABGaXJld29ya0ZhZGUAAAAAAQ0ARmlyZXdvcmtUcmFpbAABDwBGaXJld29ya0ZsaWNrZXIAAAA=" + "nbt_b64" : "CgAACg0ARmlyZXdvcmtzSXRlbQcNAEZpcmV3b3JrQ29sb3IBAAAABAcMAEZpcmV3b3JrRmFkZQAAAAABDwBGaXJld29ya0ZsaWNrZXIAAQ0ARmlyZXdvcmtUcmFpbAABDABGaXJld29ya1R5cGUAAAMLAGN1c3RvbUNvbG9yqkQ8/wA=" }, { - "id" : 402, + "id" : 510, "damage" : 5, - "nbt_b64" : "CgAAAwsAY3VzdG9tQ29sb3K4Mon/Cg0ARmlyZXdvcmtzSXRlbQcNAEZpcmV3b3JrQ29sb3IBAAAABQEMAEZpcmV3b3JrVHlwZQAHDABGaXJld29ya0ZhZGUAAAAAAQ0ARmlyZXdvcmtUcmFpbAABDwBGaXJld29ya0ZsaWNrZXIAAAA=" + "nbt_b64" : "CgAACg0ARmlyZXdvcmtzSXRlbQcNAEZpcmV3b3JrQ29sb3IBAAAABQcMAEZpcmV3b3JrRmFkZQAAAAABDwBGaXJld29ya0ZsaWNrZXIAAQ0ARmlyZXdvcmtUcmFpbAABDABGaXJld29ya1R5cGUAAAMLAGN1c3RvbUNvbG9yuDKJ/wA=" }, { - "id" : 402, + "id" : 510, "damage" : 13, - "nbt_b64" : "CgAAAwsAY3VzdG9tQ29sb3K9Tsf/Cg0ARmlyZXdvcmtzSXRlbQcNAEZpcmV3b3JrQ29sb3IBAAAADQEMAEZpcmV3b3JrVHlwZQAHDABGaXJld29ya0ZhZGUAAAAAAQ0ARmlyZXdvcmtUcmFpbAABDwBGaXJld29ya0ZsaWNrZXIAAAA=" + "nbt_b64" : "CgAACg0ARmlyZXdvcmtzSXRlbQcNAEZpcmV3b3JrQ29sb3IBAAAADQcMAEZpcmV3b3JrRmFkZQAAAAABDwBGaXJld29ya0ZsaWNrZXIAAQ0ARmlyZXdvcmtUcmFpbAABDABGaXJld29ya1R5cGUAAAMLAGN1c3RvbUNvbG9yvU7H/wA=" }, { - "id" : 402, + "id" : 510, "damage" : 9, - "nbt_b64" : "CgAAAwsAY3VzdG9tQ29sb3Kqi/P/Cg0ARmlyZXdvcmtzSXRlbQcNAEZpcmV3b3JrQ29sb3IBAAAACQEMAEZpcmV3b3JrVHlwZQAHDABGaXJld29ya0ZhZGUAAAAAAQ0ARmlyZXdvcmtUcmFpbAABDwBGaXJld29ya0ZsaWNrZXIAAAA=" + "nbt_b64" : "CgAACg0ARmlyZXdvcmtzSXRlbQcNAEZpcmV3b3JrQ29sb3IBAAAACQcMAEZpcmV3b3JrRmFkZQAAAAABDwBGaXJld29ya0ZsaWNrZXIAAQ0ARmlyZXdvcmtUcmFpbAABDABGaXJld29ya1R5cGUAAAMLAGN1c3RvbUNvbG9yqovz/wA=" }, { - "id" : 402, + "id" : 510, "damage" : 3, - "nbt_b64" : "CgAAAwsAY3VzdG9tQ29sb3IyVIP/Cg0ARmlyZXdvcmtzSXRlbQcNAEZpcmV3b3JrQ29sb3IBAAAAAwEMAEZpcmV3b3JrVHlwZQAHDABGaXJld29ya0ZhZGUAAAAAAQ0ARmlyZXdvcmtUcmFpbAABDwBGaXJld29ya0ZsaWNrZXIAAAA=" + "nbt_b64" : "CgAACg0ARmlyZXdvcmtzSXRlbQcNAEZpcmV3b3JrQ29sb3IBAAAAAwcMAEZpcmV3b3JrRmFkZQAAAAABDwBGaXJld29ya0ZsaWNrZXIAAQ0ARmlyZXdvcmtUcmFpbAABDABGaXJld29ya1R5cGUAAAMLAGN1c3RvbUNvbG9yMlSD/wA=" }, { - "id" : 402, + "id" : 510, "damage" : 11, - "nbt_b64" : "CgAAAwsAY3VzdG9tQ29sb3I92P7/Cg0ARmlyZXdvcmtzSXRlbQcNAEZpcmV3b3JrQ29sb3IBAAAACwEMAEZpcmV3b3JrVHlwZQAHDABGaXJld29ya0ZhZGUAAAAAAQ0ARmlyZXdvcmtUcmFpbAABDwBGaXJld29ya0ZsaWNrZXIAAAA=" + "nbt_b64" : "CgAACg0ARmlyZXdvcmtzSXRlbQcNAEZpcmV3b3JrQ29sb3IBAAAACwcMAEZpcmV3b3JrRmFkZQAAAAABDwBGaXJld29ya0ZsaWNrZXIAAQ0ARmlyZXdvcmtUcmFpbAABDABGaXJld29ya1R5cGUAAAMLAGN1c3RvbUNvbG9yPdj+/wA=" }, { - "id" : 402, + "id" : 510, "damage" : 10, - "nbt_b64" : "CgAAAwsAY3VzdG9tQ29sb3Ifx4D/Cg0ARmlyZXdvcmtzSXRlbQcNAEZpcmV3b3JrQ29sb3IBAAAACgEMAEZpcmV3b3JrVHlwZQAHDABGaXJld29ya0ZhZGUAAAAAAQ0ARmlyZXdvcmtUcmFpbAABDwBGaXJld29ya0ZsaWNrZXIAAAA=" + "nbt_b64" : "CgAACg0ARmlyZXdvcmtzSXRlbQcNAEZpcmV3b3JrQ29sb3IBAAAACgcMAEZpcmV3b3JrRmFkZQAAAAABDwBGaXJld29ya0ZsaWNrZXIAAQ0ARmlyZXdvcmtUcmFpbAABDABGaXJld29ya1R5cGUAAAMLAGN1c3RvbUNvbG9yH8eA/wA=" }, { - "id" : 402, + "id" : 510, "damage" : 2, - "nbt_b64" : "CgAAAwsAY3VzdG9tQ29sb3IWfF7/Cg0ARmlyZXdvcmtzSXRlbQcNAEZpcmV3b3JrQ29sb3IBAAAAAgEMAEZpcmV3b3JrVHlwZQAHDABGaXJld29ya0ZhZGUAAAAAAQ0ARmlyZXdvcmtUcmFpbAABDwBGaXJld29ya0ZsaWNrZXIAAAA=" + "nbt_b64" : "CgAACg0ARmlyZXdvcmtzSXRlbQcNAEZpcmV3b3JrQ29sb3IBAAAAAgcMAEZpcmV3b3JrRmFkZQAAAAABDwBGaXJld29ya0ZsaWNrZXIAAQ0ARmlyZXdvcmtUcmFpbAABDABGaXJld29ya1R5cGUAAAMLAGN1c3RvbUNvbG9yFnxe/wA=" }, { - "id" : 402, + "id" : 510, "damage" : 6, - "nbt_b64" : "CgAAAwsAY3VzdG9tQ29sb3KcnBb/Cg0ARmlyZXdvcmtzSXRlbQcNAEZpcmV3b3JrQ29sb3IBAAAABgEMAEZpcmV3b3JrVHlwZQAHDABGaXJld29ya0ZhZGUAAAAAAQ0ARmlyZXdvcmtUcmFpbAABDwBGaXJld29ya0ZsaWNrZXIAAAA=" + "nbt_b64" : "CgAACg0ARmlyZXdvcmtzSXRlbQcNAEZpcmV3b3JrQ29sb3IBAAAABgcMAEZpcmV3b3JrRmFkZQAAAAABDwBGaXJld29ya0ZsaWNrZXIAAQ0ARmlyZXdvcmtUcmFpbAABDABGaXJld29ya1R5cGUAAAMLAGN1c3RvbUNvbG9ynJwW/wA=" + }, + { + "id" : 607 + }, + { + "id" : -239 + }, + { + "id" : 590 } ] } \ No newline at end of file diff --git a/connector/src/main/resources/bedrock/legacy_block_ids.json b/connector/src/main/resources/bedrock/legacy_block_ids.json deleted file mode 100644 index 275949157..000000000 --- a/connector/src/main/resources/bedrock/legacy_block_ids.json +++ /dev/null @@ -1,555 +0,0 @@ -{ - "minecraft:air" : 0, - "minecraft:stone" : 1, - "minecraft:grass" : 2, - "minecraft:dirt" : 3, - "minecraft:cobblestone" : 4, - "minecraft:planks" : 5, - "minecraft:sapling" : 6, - "minecraft:bedrock" : 7, - "minecraft:flowing_water" : 8, - "minecraft:water" : 9, - "minecraft:flowing_lava" : 10, - "minecraft:lava" : 11, - "minecraft:sand" : 12, - "minecraft:gravel" : 13, - "minecraft:gold_ore" : 14, - "minecraft:iron_ore" : 15, - "minecraft:coal_ore" : 16, - "minecraft:log" : 17, - "minecraft:leaves" : 18, - "minecraft:sponge" : 19, - "minecraft:glass" : 20, - "minecraft:lapis_ore" : 21, - "minecraft:lapis_block" : 22, - "minecraft:dispenser" : 23, - "minecraft:sandstone" : 24, - "minecraft:noteblock" : 25, - "minecraft:bed" : 26, - "minecraft:golden_rail" : 27, - "minecraft:detector_rail" : 28, - "minecraft:sticky_piston" : 29, - "minecraft:web" : 30, - "minecraft:tallgrass" : 31, - "minecraft:deadbush" : 32, - "minecraft:piston" : 33, - "minecraft:pistonArmCollision" : 34, - "minecraft:wool" : 35, - "minecraft:element_0" : 36, - "minecraft:yellow_flower" : 37, - "minecraft:red_flower" : 38, - "minecraft:brown_mushroom" : 39, - "minecraft:red_mushroom" : 40, - "minecraft:gold_block" : 41, - "minecraft:iron_block" : 42, - "minecraft:double_stone_slab" : 43, - "minecraft:stone_slab" : 44, - "minecraft:brick_block" : 45, - "minecraft:tnt" : 46, - "minecraft:bookshelf" : 47, - "minecraft:mossy_cobblestone" : 48, - "minecraft:obsidian" : 49, - "minecraft:torch" : 50, - "minecraft:fire" : 51, - "minecraft:mob_spawner" : 52, - "minecraft:oak_stairs" : 53, - "minecraft:chest" : 54, - "minecraft:redstone_wire" : 55, - "minecraft:diamond_ore" : 56, - "minecraft:diamond_block" : 57, - "minecraft:crafting_table" : 58, - "minecraft:wheat" : 59, - "minecraft:farmland" : 60, - "minecraft:furnace" : 61, - "minecraft:lit_furnace" : 62, - "minecraft:standing_sign" : 63, - "minecraft:wooden_door" : 64, - "minecraft:ladder" : 65, - "minecraft:rail" : 66, - "minecraft:stone_stairs" : 67, - "minecraft:wall_sign" : 68, - "minecraft:lever" : 69, - "minecraft:stone_pressure_plate" : 70, - "minecraft:iron_door" : 71, - "minecraft:wooden_pressure_plate" : 72, - "minecraft:redstone_ore" : 73, - "minecraft:lit_redstone_ore" : 74, - "minecraft:unlit_redstone_torch" : 75, - "minecraft:redstone_torch" : 76, - "minecraft:stone_button" : 77, - "minecraft:snow_layer" : 78, - "minecraft:ice" : 79, - "minecraft:snow" : 80, - "minecraft:cactus" : 81, - "minecraft:clay" : 82, - "minecraft:reeds" : 83, - "minecraft:jukebox" : 84, - "minecraft:fence" : 85, - "minecraft:pumpkin" : 86, - "minecraft:netherrack" : 87, - "minecraft:soul_sand" : 88, - "minecraft:glowstone" : 89, - "minecraft:portal" : 90, - "minecraft:lit_pumpkin" : 91, - "minecraft:cake" : 92, - "minecraft:unpowered_repeater" : 93, - "minecraft:powered_repeater" : 94, - "minecraft:invisibleBedrock" : 95, - "minecraft:trapdoor" : 96, - "minecraft:monster_egg" : 97, - "minecraft:stonebrick" : 98, - "minecraft:brown_mushroom_block" : 99, - "minecraft:red_mushroom_block" : 100, - "minecraft:iron_bars" : 101, - "minecraft:glass_pane" : 102, - "minecraft:melon_block" : 103, - "minecraft:pumpkin_stem" : 104, - "minecraft:melon_stem" : 105, - "minecraft:vine" : 106, - "minecraft:fence_gate" : 107, - "minecraft:brick_stairs" : 108, - "minecraft:stone_brick_stairs" : 109, - "minecraft:mycelium" : 110, - "minecraft:waterlily" : 111, - "minecraft:nether_brick" : 112, - "minecraft:nether_brick_fence" : 113, - "minecraft:nether_brick_stairs" : 114, - "minecraft:nether_wart" : 115, - "minecraft:enchanting_table" : 116, - "minecraft:brewing_stand" : 117, - "minecraft:cauldron" : 118, - "minecraft:end_portal" : 119, - "minecraft:end_portal_frame" : 120, - "minecraft:end_stone" : 121, - "minecraft:dragon_egg" : 122, - "minecraft:redstone_lamp" : 123, - "minecraft:lit_redstone_lamp" : 124, - "minecraft:dropper" : 125, - "minecraft:activator_rail" : 126, - "minecraft:cocoa" : 127, - "minecraft:sandstone_stairs" : 128, - "minecraft:emerald_ore" : 129, - "minecraft:ender_chest" : 130, - "minecraft:tripwire_hook" : 131, - "minecraft:tripWire" : 132, - "minecraft:emerald_block" : 133, - "minecraft:spruce_stairs" : 134, - "minecraft:birch_stairs" : 135, - "minecraft:jungle_stairs" : 136, - "minecraft:command_block" : 137, - "minecraft:beacon" : 138, - "minecraft:cobblestone_wall" : 139, - "minecraft:flower_pot" : 140, - "minecraft:carrots" : 141, - "minecraft:potatoes" : 142, - "minecraft:wooden_button" : 143, - "minecraft:skull" : 144, - "minecraft:anvil" : 145, - "minecraft:trapped_chest" : 146, - "minecraft:light_weighted_pressure_plate" : 147, - "minecraft:heavy_weighted_pressure_plate" : 148, - "minecraft:unpowered_comparator" : 149, - "minecraft:powered_comparator" : 150, - "minecraft:daylight_detector" : 151, - "minecraft:redstone_block" : 152, - "minecraft:quartz_ore" : 153, - "minecraft:hopper" : 154, - "minecraft:quartz_block" : 155, - "minecraft:quartz_stairs" : 156, - "minecraft:double_wooden_slab" : 157, - "minecraft:wooden_slab" : 158, - "minecraft:stained_hardened_clay" : 159, - "minecraft:stained_glass_pane" : 160, - "minecraft:leaves2" : 161, - "minecraft:log2" : 162, - "minecraft:acacia_stairs" : 163, - "minecraft:dark_oak_stairs" : 164, - "minecraft:slime" : 165, - "minecraft:iron_trapdoor" : 167, - "minecraft:prismarine" : 168, - "minecraft:seaLantern" : 169, - "minecraft:hay_block" : 170, - "minecraft:carpet" : 171, - "minecraft:hardened_clay" : 172, - "minecraft:coal_block" : 173, - "minecraft:packed_ice" : 174, - "minecraft:double_plant" : 175, - "minecraft:standing_banner" : 176, - "minecraft:wall_banner" : 177, - "minecraft:daylight_detector_inverted" : 178, - "minecraft:red_sandstone" : 179, - "minecraft:red_sandstone_stairs" : 180, - "minecraft:double_stone_slab2" : 181, - "minecraft:stone_slab2" : 182, - "minecraft:spruce_fence_gate" : 183, - "minecraft:birch_fence_gate" : 184, - "minecraft:jungle_fence_gate" : 185, - "minecraft:dark_oak_fence_gate" : 186, - "minecraft:acacia_fence_gate" : 187, - "minecraft:repeating_command_block" : 188, - "minecraft:chain_command_block" : 189, - "minecraft:hard_glass_pane" : 190, - "minecraft:hard_stained_glass_pane" : 191, - "minecraft:chemical_heat" : 192, - "minecraft:spruce_door" : 193, - "minecraft:birch_door" : 194, - "minecraft:jungle_door" : 195, - "minecraft:acacia_door" : 196, - "minecraft:dark_oak_door" : 197, - "minecraft:grass_path" : 198, - "minecraft:frame" : 199, - "minecraft:chorus_flower" : 200, - "minecraft:purpur_block" : 201, - "minecraft:colored_torch_rg" : 202, - "minecraft:purpur_stairs" : 203, - "minecraft:colored_torch_bp" : 204, - "minecraft:undyed_shulker_box" : 205, - "minecraft:end_bricks" : 206, - "minecraft:frosted_ice" : 207, - "minecraft:end_rod" : 208, - "minecraft:end_gateway" : 209, - "minecraft:allow" : 210, - "minecraft:deny" : 211, - "minecraft:border_block" : 212, - "minecraft:magma" : 213, - "minecraft:nether_wart_block" : 214, - "minecraft:red_nether_brick" : 215, - "minecraft:bone_block" : 216, - "minecraft:structure_void" : 217, - "minecraft:shulker_box" : 218, - "minecraft:purple_glazed_terracotta" : 219, - "minecraft:white_glazed_terracotta" : 220, - "minecraft:orange_glazed_terracotta" : 221, - "minecraft:magenta_glazed_terracotta" : 222, - "minecraft:light_blue_glazed_terracotta" : 223, - "minecraft:yellow_glazed_terracotta" : 224, - "minecraft:lime_glazed_terracotta" : 225, - "minecraft:pink_glazed_terracotta" : 226, - "minecraft:gray_glazed_terracotta" : 227, - "minecraft:silver_glazed_terracotta" : 228, - "minecraft:cyan_glazed_terracotta" : 229, - "minecraft:blue_glazed_terracotta" : 231, - "minecraft:brown_glazed_terracotta" : 232, - "minecraft:green_glazed_terracotta" : 233, - "minecraft:red_glazed_terracotta" : 234, - "minecraft:black_glazed_terracotta" : 235, - "minecraft:concrete" : 236, - "minecraft:concretePowder" : 237, - "minecraft:chemistry_table" : 238, - "minecraft:underwater_torch" : 239, - "minecraft:chorus_plant" : 240, - "minecraft:stained_glass" : 241, - "minecraft:camera" : 242, - "minecraft:podzol" : 243, - "minecraft:beetroot" : 244, - "minecraft:stonecutter" : 245, - "minecraft:glowingobsidian" : 246, - "minecraft:netherreactor" : 247, - "minecraft:info_update" : 248, - "minecraft:info_update2" : 249, - "minecraft:movingBlock" : 250, - "minecraft:observer" : 251, - "minecraft:structure_block" : 252, - "minecraft:hard_glass" : 253, - "minecraft:hard_stained_glass" : 254, - "minecraft:reserved6" : 255, - "minecraft:prismarine_stairs" : 257, - "minecraft:dark_prismarine_stairs" : 258, - "minecraft:prismarine_bricks_stairs" : 259, - "minecraft:stripped_spruce_log" : 260, - "minecraft:stripped_birch_log" : 261, - "minecraft:stripped_jungle_log" : 262, - "minecraft:stripped_acacia_log" : 263, - "minecraft:stripped_dark_oak_log" : 264, - "minecraft:stripped_oak_log" : 265, - "minecraft:blue_ice" : 266, - "minecraft:element_1" : 267, - "minecraft:element_2" : 268, - "minecraft:element_3" : 269, - "minecraft:element_4" : 270, - "minecraft:element_5" : 271, - "minecraft:element_6" : 272, - "minecraft:element_7" : 273, - "minecraft:element_8" : 274, - "minecraft:element_9" : 275, - "minecraft:element_10" : 276, - "minecraft:element_11" : 277, - "minecraft:element_12" : 278, - "minecraft:element_13" : 279, - "minecraft:element_14" : 280, - "minecraft:element_15" : 281, - "minecraft:element_16" : 282, - "minecraft:element_17" : 283, - "minecraft:element_18" : 284, - "minecraft:element_19" : 285, - "minecraft:element_20" : 286, - "minecraft:element_21" : 287, - "minecraft:element_22" : 288, - "minecraft:element_23" : 289, - "minecraft:element_24" : 290, - "minecraft:element_25" : 291, - "minecraft:element_26" : 292, - "minecraft:element_27" : 293, - "minecraft:element_28" : 294, - "minecraft:element_29" : 295, - "minecraft:element_30" : 296, - "minecraft:element_31" : 297, - "minecraft:element_32" : 298, - "minecraft:element_33" : 299, - "minecraft:element_34" : 300, - "minecraft:element_35" : 301, - "minecraft:element_36" : 302, - "minecraft:element_37" : 303, - "minecraft:element_38" : 304, - "minecraft:element_39" : 305, - "minecraft:element_40" : 306, - "minecraft:element_41" : 307, - "minecraft:element_42" : 308, - "minecraft:element_43" : 309, - "minecraft:element_44" : 310, - "minecraft:element_45" : 311, - "minecraft:element_46" : 312, - "minecraft:element_47" : 313, - "minecraft:element_48" : 314, - "minecraft:element_49" : 315, - "minecraft:element_50" : 316, - "minecraft:element_51" : 317, - "minecraft:element_52" : 318, - "minecraft:element_53" : 319, - "minecraft:element_54" : 320, - "minecraft:element_55" : 321, - "minecraft:element_56" : 322, - "minecraft:element_57" : 323, - "minecraft:element_58" : 324, - "minecraft:element_59" : 325, - "minecraft:element_60" : 326, - "minecraft:element_61" : 327, - "minecraft:element_62" : 328, - "minecraft:element_63" : 329, - "minecraft:element_64" : 330, - "minecraft:element_65" : 331, - "minecraft:element_66" : 332, - "minecraft:element_67" : 333, - "minecraft:element_68" : 334, - "minecraft:element_69" : 335, - "minecraft:element_70" : 336, - "minecraft:element_71" : 337, - "minecraft:element_72" : 338, - "minecraft:element_73" : 339, - "minecraft:element_74" : 340, - "minecraft:element_75" : 341, - "minecraft:element_76" : 342, - "minecraft:element_77" : 343, - "minecraft:element_78" : 344, - "minecraft:element_79" : 345, - "minecraft:element_80" : 346, - "minecraft:element_81" : 347, - "minecraft:element_82" : 348, - "minecraft:element_83" : 349, - "minecraft:element_84" : 350, - "minecraft:element_85" : 351, - "minecraft:element_86" : 352, - "minecraft:element_87" : 353, - "minecraft:element_88" : 354, - "minecraft:element_89" : 355, - "minecraft:element_90" : 356, - "minecraft:element_91" : 357, - "minecraft:element_92" : 358, - "minecraft:element_93" : 359, - "minecraft:element_94" : 360, - "minecraft:element_95" : 361, - "minecraft:element_96" : 362, - "minecraft:element_97" : 363, - "minecraft:element_98" : 364, - "minecraft:element_99" : 365, - "minecraft:element_100" : 366, - "minecraft:element_101" : 367, - "minecraft:element_102" : 368, - "minecraft:element_103" : 369, - "minecraft:element_104" : 370, - "minecraft:element_105" : 371, - "minecraft:element_106" : 372, - "minecraft:element_107" : 373, - "minecraft:element_108" : 374, - "minecraft:element_109" : 375, - "minecraft:element_110" : 376, - "minecraft:element_111" : 377, - "minecraft:element_112" : 378, - "minecraft:element_113" : 379, - "minecraft:element_114" : 380, - "minecraft:element_115" : 381, - "minecraft:element_116" : 382, - "minecraft:element_117" : 383, - "minecraft:element_118" : 384, - "minecraft:seagrass" : 385, - "minecraft:coral" : 386, - "minecraft:coral_block" : 387, - "minecraft:coral_fan" : 388, - "minecraft:coral_fan_dead" : 389, - "minecraft:coral_fan_hang" : 390, - "minecraft:coral_fan_hang2" : 391, - "minecraft:coral_fan_hang3" : 392, - "minecraft:kelp" : 393, - "minecraft:dried_kelp_block" : 394, - "minecraft:acacia_button" : 395, - "minecraft:birch_button" : 396, - "minecraft:dark_oak_button" : 397, - "minecraft:jungle_button" : 398, - "minecraft:spruce_button" : 399, - "minecraft:acacia_trapdoor" : 400, - "minecraft:birch_trapdoor" : 401, - "minecraft:dark_oak_trapdoor" : 402, - "minecraft:jungle_trapdoor" : 403, - "minecraft:spruce_trapdoor" : 404, - "minecraft:acacia_pressure_plate" : 405, - "minecraft:birch_pressure_plate" : 406, - "minecraft:dark_oak_pressure_plate" : 407, - "minecraft:jungle_pressure_plate" : 408, - "minecraft:spruce_pressure_plate" : 409, - "minecraft:carved_pumpkin" : 410, - "minecraft:sea_pickle" : 411, - "minecraft:conduit" : 412, - "minecraft:turtle_egg" : 414, - "minecraft:bubble_column" : 415, - "minecraft:barrier" : 416, - "minecraft:stone_slab3" : 417, - "minecraft:bamboo" : 418, - "minecraft:bamboo_sapling" : 419, - "minecraft:scaffolding" : 420, - "minecraft:stone_slab4" : 421, - "minecraft:double_stone_slab3" : 422, - "minecraft:double_stone_slab4" : 423, - "minecraft:granite_stairs" : 424, - "minecraft:diorite_stairs" : 425, - "minecraft:andesite_stairs" : 426, - "minecraft:polished_granite_stairs" : 427, - "minecraft:polished_diorite_stairs" : 428, - "minecraft:polished_andesite_stairs" : 429, - "minecraft:mossy_stone_brick_stairs" : 430, - "minecraft:smooth_red_sandstone_stairs" : 431, - "minecraft:smooth_sandstone_stairs" : 432, - "minecraft:end_brick_stairs" : 433, - "minecraft:mossy_cobblestone_stairs" : 434, - "minecraft:normal_stone_stairs" : 435, - "minecraft:spruce_standing_sign" : 436, - "minecraft:spruce_wall_sign" : 437, - "minecraft:smooth_stone" : 438, - "minecraft:red_nether_brick_stairs" : 439, - "minecraft:smooth_quartz_stairs" : 440, - "minecraft:birch_standing_sign" : 441, - "minecraft:birch_wall_sign" : 442, - "minecraft:jungle_standing_sign" : 443, - "minecraft:jungle_wall_sign" : 444, - "minecraft:acacia_standing_sign" : 445, - "minecraft:acacia_wall_sign" : 446, - "minecraft:darkoak_standing_sign" : 447, - "minecraft:darkoak_wall_sign" : 448, - "minecraft:lectern" : 449, - "minecraft:grindstone" : 450, - "minecraft:blast_furnace" : 451, - "minecraft:stonecutter_block" : 452, - "minecraft:smoker" : 453, - "minecraft:lit_smoker" : 454, - "minecraft:cartography_table" : 455, - "minecraft:fletching_table" : 456, - "minecraft:smithing_table" : 457, - "minecraft:barrel" : 458, - "minecraft:loom" : 459, - "minecraft:bell" : 461, - "minecraft:sweet_berry_bush" : 462, - "minecraft:lantern" : 463, - "minecraft:campfire" : 464, - "minecraft:lava_cauldron" : 465, - "minecraft:jigsaw" : 466, - "minecraft:wood" : 467, - "minecraft:composter" : 468, - "minecraft:lit_blast_furnace" : 469, - "minecraft:light_block" : 470, - "minecraft:wither_rose" : 471, - "minecraft:stickyPistonArmCollision" : 472, - "minecraft:bee_nest" : 473, - "minecraft:beehive" : 474, - "minecraft:honey_block" : 475, - "minecraft:honeycomb_block" : 476, - "minecraft:lodestone" : 477, - "minecraft:crimson_roots" : 478, - "minecraft:warped_roots" : 479, - "minecraft:crimson_stem" : 480, - "minecraft:warped_stem" : 481, - "minecraft:warped_wart_block" : 482, - "minecraft:crimson_fungus" : 483, - "minecraft:warped_fungus" : 484, - "minecraft:shroomlight" : 485, - "minecraft:weeping_vines" : 486, - "minecraft:crimson_nylium" : 487, - "minecraft:warped_nylium" : 488, - "minecraft:basalt" : 489, - "minecraft:polished_basalt" : 490, - "minecraft:soul_soil" : 491, - "minecraft:soul_fire" : 492, - "minecraft:nether_sprouts" : 493, - "minecraft:target" : 494, - "minecraft:stripped_crimson_stem" : 495, - "minecraft:stripped_warped_stem" : 496, - "minecraft:crimson_planks" : 497, - "minecraft:warped_planks" : 498, - "minecraft:crimson_door" : 499, - "minecraft:warped_door" : 500, - "minecraft:crimson_trapdoor" : 501, - "minecraft:warped_trapdoor" : 502, - "minecraft:crimson_standing_sign" : 505, - "minecraft:warped_standing_sign" : 506, - "minecraft:crimson_wall_sign" : 507, - "minecraft:warped_wall_sign" : 508, - "minecraft:crimson_stairs" : 509, - "minecraft:warped_stairs" : 510, - "minecraft:crimson_fence" : 511, - "minecraft:warped_fence" : 512, - "minecraft:crimson_fence_gate" : 513, - "minecraft:warped_fence_gate" : 514, - "minecraft:crimson_button" : 515, - "minecraft:warped_button" : 516, - "minecraft:crimson_pressure_plate" : 517, - "minecraft:warped_pressure_plate" : 518, - "minecraft:crimson_slab" : 519, - "minecraft:warped_slab" : 520, - "minecraft:crimson_double_slab" : 521, - "minecraft:warped_double_slab" : 522, - "minecraft:soul_torch" : 523, - "minecraft:soul_lantern" : 524, - "minecraft:netherite_block" : 525, - "minecraft:ancient_debris" : 526, - "minecraft:respawn_anchor" : 527, - "minecraft:blackstone" : 528, - "minecraft:polished_blackstone_bricks" : 529, - "minecraft:polished_blackstone_brick_stairs" : 530, - "minecraft:blackstone_stairs" : 531, - "minecraft:blackstone_wall" : 532, - "minecraft:polished_blackstone_brick_wall" : 533, - "minecraft:chiseled_polished_blackstone" : 534, - "minecraft:cracked_polished_blackstone_bricks" : 535, - "minecraft:gilded_blackstone" : 536, - "minecraft:blackstone_slab" : 537, - "minecraft:blackstone_double_slab" : 538, - "minecraft:polished_blackstone_brick_slab" : 539, - "minecraft:polished_blackstone_brick_double_slab" : 540, - "minecraft:chain" : 541, - "minecraft:twisting_vines" : 542, - "minecraft:nether_gold_ore" : 543, - "minecraft:crying_obsidian" : 544, - "minecraft:soul_campfire" : 545, - "minecraft:polished_blackstone" : 546, - "minecraft:polished_blackstone_stairs" : 547, - "minecraft:polished_blackstone_slab" : 548, - "minecraft:polished_blackstone_double_slab" : 549, - "minecraft:polished_blackstone_pressure_plate" : 550, - "minecraft:polished_blackstone_button" : 551, - "minecraft:polished_blackstone_wall" : 552, - "minecraft:warped_hyphae" : 553, - "minecraft:crimson_hyphae" : 554, - "minecraft:stripped_crimson_hyphae" : 555, - "minecraft:stripped_warped_hyphae" : 556, - "minecraft:chiseled_nether_bricks" : 557, - "minecraft:cracked_nether_bricks" : 558, - "minecraft:quartz_bricks" : 559 -} \ No newline at end of file diff --git a/connector/src/main/resources/bedrock/legacy_item_ids.json b/connector/src/main/resources/bedrock/legacy_item_ids.json deleted file mode 100644 index 3f22c91c3..000000000 --- a/connector/src/main/resources/bedrock/legacy_item_ids.json +++ /dev/null @@ -1,255 +0,0 @@ -{ - "minecraft:iron_shovel" : 256, - "minecraft:iron_pickaxe" : 257, - "minecraft:iron_axe" : 258, - "minecraft:flint_and_steel" : 259, - "minecraft:apple" : 260, - "minecraft:bow" : 261, - "minecraft:arrow" : 262, - "minecraft:coal" : 263, - "minecraft:diamond" : 264, - "minecraft:iron_ingot" : 265, - "minecraft:gold_ingot" : 266, - "minecraft:iron_sword" : 267, - "minecraft:wooden_sword" : 268, - "minecraft:wooden_shovel" : 269, - "minecraft:wooden_pickaxe" : 270, - "minecraft:wooden_axe" : 271, - "minecraft:stone_sword" : 272, - "minecraft:stone_shovel" : 273, - "minecraft:stone_pickaxe" : 274, - "minecraft:stone_axe" : 275, - "minecraft:diamond_sword" : 276, - "minecraft:diamond_shovel" : 277, - "minecraft:diamond_pickaxe" : 278, - "minecraft:diamond_axe" : 279, - "minecraft:stick" : 280, - "minecraft:bowl" : 281, - "minecraft:mushroom_stew" : 282, - "minecraft:golden_sword" : 283, - "minecraft:golden_shovel" : 284, - "minecraft:golden_pickaxe" : 285, - "minecraft:golden_axe" : 286, - "minecraft:string" : 287, - "minecraft:feather" : 288, - "minecraft:gunpowder" : 289, - "minecraft:wooden_hoe" : 290, - "minecraft:stone_hoe" : 291, - "minecraft:iron_hoe" : 292, - "minecraft:diamond_hoe" : 293, - "minecraft:golden_hoe" : 294, - "minecraft:wheat_seeds" : 295, - "minecraft:wheat" : 296, - "minecraft:bread" : 297, - "minecraft:leather_helmet" : 298, - "minecraft:leather_chestplate" : 299, - "minecraft:leather_leggings" : 300, - "minecraft:leather_boots" : 301, - "minecraft:chainmail_helmet" : 302, - "minecraft:chainmail_chestplate" : 303, - "minecraft:chainmail_leggings" : 304, - "minecraft:chainmail_boots" : 305, - "minecraft:iron_helmet" : 306, - "minecraft:iron_chestplate" : 307, - "minecraft:iron_leggings" : 308, - "minecraft:iron_boots" : 309, - "minecraft:diamond_helmet" : 310, - "minecraft:diamond_chestplate" : 311, - "minecraft:diamond_leggings" : 312, - "minecraft:diamond_boots" : 313, - "minecraft:golden_helmet" : 314, - "minecraft:golden_chestplate" : 315, - "minecraft:golden_leggings" : 316, - "minecraft:golden_boots" : 317, - "minecraft:flint" : 318, - "minecraft:porkchop" : 319, - "minecraft:cooked_porkchop" : 320, - "minecraft:painting" : 321, - "minecraft:golden_apple" : 322, - "minecraft:sign" : 323, - "minecraft:wooden_door" : 324, - "minecraft:bucket" : 325, - "minecraft:minecart" : 328, - "minecraft:saddle" : 329, - "minecraft:iron_door" : 330, - "minecraft:redstone" : 331, - "minecraft:snowball" : 332, - "minecraft:boat" : 333, - "minecraft:leather" : 334, - "minecraft:kelp" : 335, - "minecraft:brick" : 336, - "minecraft:clay_ball" : 337, - "minecraft:reeds" : 338, - "minecraft:paper" : 339, - "minecraft:book" : 340, - "minecraft:slime_ball" : 341, - "minecraft:chest_minecart" : 342, - "minecraft:egg" : 344, - "minecraft:compass" : 345, - "minecraft:fishing_rod" : 346, - "minecraft:clock" : 347, - "minecraft:glowstone_dust" : 348, - "minecraft:fish" : 349, - "minecraft:cooked_fish" : 350, - "minecraft:dye" : 351, - "minecraft:bone" : 352, - "minecraft:sugar" : 353, - "minecraft:cake" : 354, - "minecraft:bed" : 355, - "minecraft:repeater" : 356, - "minecraft:cookie" : 357, - "minecraft:map" : 358, - "minecraft:shears" : 359, - "minecraft:melon" : 360, - "minecraft:pumpkin_seeds" : 361, - "minecraft:melon_seeds" : 362, - "minecraft:beef" : 363, - "minecraft:cooked_beef" : 364, - "minecraft:chicken" : 365, - "minecraft:cooked_chicken" : 366, - "minecraft:rotten_flesh" : 367, - "minecraft:ender_pearl" : 368, - "minecraft:blaze_rod" : 369, - "minecraft:ghast_tear" : 370, - "minecraft:gold_nugget" : 371, - "minecraft:nether_wart" : 372, - "minecraft:potion" : 373, - "minecraft:glass_bottle" : 374, - "minecraft:spider_eye" : 375, - "minecraft:fermented_spider_eye" : 376, - "minecraft:blaze_powder" : 377, - "minecraft:magma_cream" : 378, - "minecraft:brewing_stand" : 379, - "minecraft:cauldron" : 380, - "minecraft:ender_eye" : 381, - "minecraft:speckled_melon" : 382, - "minecraft:spawn_egg" : 383, - "minecraft:experience_bottle" : 384, - "minecraft:fireball" : 385, - "minecraft:writable_book" : 386, - "minecraft:written_book" : 387, - "minecraft:emerald" : 388, - "minecraft:frame" : 389, - "minecraft:flower_pot" : 390, - "minecraft:carrot" : 391, - "minecraft:potato" : 392, - "minecraft:baked_potato" : 393, - "minecraft:poisonous_potato" : 394, - "minecraft:emptymap" : 395, - "minecraft:golden_carrot" : 396, - "minecraft:skull" : 397, - "minecraft:carrotonastick" : 398, - "minecraft:netherstar" : 399, - "minecraft:pumpkin_pie" : 400, - "minecraft:fireworks" : 401, - "minecraft:fireworkscharge" : 402, - "minecraft:enchanted_book" : 403, - "minecraft:comparator" : 404, - "minecraft:netherbrick" : 405, - "minecraft:quartz" : 406, - "minecraft:tnt_minecart" : 407, - "minecraft:hopper_minecart" : 408, - "minecraft:prismarine_shard" : 409, - "minecraft:hopper" : 410, - "minecraft:rabbit" : 411, - "minecraft:cooked_rabbit" : 412, - "minecraft:rabbit_stew" : 413, - "minecraft:rabbit_foot" : 414, - "minecraft:rabbit_hide" : 415, - "minecraft:horsearmorleather" : 416, - "minecraft:horsearmoriron" : 417, - "minecraft:horsearmorgold" : 418, - "minecraft:horsearmordiamond" : 419, - "minecraft:lead" : 420, - "minecraft:name_tag" : 421, - "minecraft:prismarine_crystals" : 422, - "minecraft:muttonraw" : 423, - "minecraft:muttoncooked" : 424, - "minecraft:armor_stand" : 425, - "minecraft:end_crystal" : 426, - "minecraft:spruce_door" : 427, - "minecraft:birch_door" : 428, - "minecraft:jungle_door" : 429, - "minecraft:acacia_door" : 430, - "minecraft:dark_oak_door" : 431, - "minecraft:chorus_fruit" : 432, - "minecraft:chorus_fruit_popped" : 433, - "minecraft:banner_pattern" : 434, - "minecraft:dragon_breath" : 437, - "minecraft:splash_potion" : 438, - "minecraft:lingering_potion" : 441, - "minecraft:sparkler" : 442, - "minecraft:command_block_minecart" : 443, - "minecraft:elytra" : 444, - "minecraft:shulker_shell" : 445, - "minecraft:banner" : 446, - "minecraft:medicine" : 447, - "minecraft:balloon" : 448, - "minecraft:rapid_fertilizer" : 449, - "minecraft:totem" : 450, - "minecraft:bleach" : 451, - "minecraft:iron_nugget" : 452, - "minecraft:ice_bomb" : 453, - "minecraft:trident" : 455, - "minecraft:beetroot" : 457, - "minecraft:beetroot_seeds" : 458, - "minecraft:beetroot_soup" : 459, - "minecraft:salmon" : 460, - "minecraft:clownfish" : 461, - "minecraft:pufferfish" : 462, - "minecraft:cooked_salmon" : 463, - "minecraft:dried_kelp" : 464, - "minecraft:nautilus_shell" : 465, - "minecraft:appleenchanted" : 466, - "minecraft:heart_of_the_sea" : 467, - "minecraft:turtle_shell_piece" : 468, - "minecraft:turtle_helmet" : 469, - "minecraft:phantom_membrane" : 470, - "minecraft:crossbow" : 471, - "minecraft:spruce_sign" : 472, - "minecraft:birch_sign" : 473, - "minecraft:jungle_sign" : 474, - "minecraft:acacia_sign" : 475, - "minecraft:darkoak_sign" : 476, - "minecraft:sweet_berries" : 477, - "minecraft:camera" : 498, - "minecraft:compound" : 499, - "minecraft:record_13" : 500, - "minecraft:record_cat" : 501, - "minecraft:record_blocks" : 502, - "minecraft:record_chirp" : 503, - "minecraft:record_far" : 504, - "minecraft:record_mall" : 505, - "minecraft:record_mellohi" : 506, - "minecraft:record_stal" : 507, - "minecraft:record_strad" : 508, - "minecraft:record_ward" : 509, - "minecraft:record_11" : 510, - "minecraft:record_wait" : 511, - "minecraft:shield" : 513, - "minecraft:campfire" : 720, - "minecraft:suspicious_stew" : 734, - "minecraft:honeycomb" : 736, - "minecraft:honey_bottle" : 737, - "minecraft:lodestonecompass" : 741, - "minecraft:netherite_ingot" : 742, - "minecraft:netherite_sword" : 743, - "minecraft:netherite_shovel" : 744, - "minecraft:netherite_pickaxe" : 745, - "minecraft:netherite_axe" : 746, - "minecraft:netherite_hoe" : 747, - "minecraft:netherite_helmet" : 748, - "minecraft:netherite_chestplate" : 749, - "minecraft:netherite_leggings" : 750, - "minecraft:netherite_boots" : 751, - "minecraft:netherite_scrap" : 752, - "minecraft:crimson_sign" : 753, - "minecraft:warped_sign" : 754, - "minecraft:crimson_door" : 755, - "minecraft:warped_door" : 756, - "minecraft:warped_fungus_on_a_stick" : 757, - "minecraft:chain" : 758, - "minecraft:record_pigstep" : 759, - "minecraft:nether_sprouts" : 760, - "minecraft:soul_campfire" : 801 -} \ No newline at end of file diff --git a/connector/src/main/resources/bedrock/runtime_block_states.dat b/connector/src/main/resources/bedrock/runtime_block_states.dat deleted file mode 100644 index feeeda664..000000000 Binary files a/connector/src/main/resources/bedrock/runtime_block_states.dat and /dev/null differ diff --git a/connector/src/main/resources/bedrock/items.json b/connector/src/main/resources/bedrock/runtime_item_states.json similarity index 84% rename from connector/src/main/resources/bedrock/items.json rename to connector/src/main/resources/bedrock/runtime_item_states.json index 0614b3641..eaf6656bd 100644 --- a/connector/src/main/resources/bedrock/items.json +++ b/connector/src/main/resources/bedrock/runtime_item_states.json @@ -1,2315 +1,975 @@ [ { - "name" : "minecraft:purpur_block", - "id" : 201 - }, - { - "name" : "minecraft:bow", - "id" : 261 - }, - { - "name" : "minecraft:end_bricks", - "id" : 206 - }, - { - "name" : "minecraft:air", - "id" : -158 - }, - { - "name" : "minecraft:element_94", - "id" : -105 - }, - { - "name" : "minecraft:rabbit", - "id" : 411 - }, - { - "name" : "minecraft:element_25", - "id" : -36 - }, - { - "name" : "minecraft:mushroom_stew", - "id" : 282 - }, - { - "name" : "minecraft:polished_blackstone_brick_slab", - "id" : -284 - }, - { - "name" : "minecraft:cooked_porkchop", - "id" : 320 - }, - { - "name" : "minecraft:record_ward", - "id" : 509 - }, - { - "name" : "minecraft:appleenchanted", - "id" : 466 - }, - { - "name" : "minecraft:pumpkin", - "id" : 86 - }, - { - "name" : "minecraft:slime", - "id" : 165 - }, - { - "name" : "minecraft:apple", - "id" : 260 - }, - { - "name" : "minecraft:element_50", - "id" : -61 - }, - { - "name" : "minecraft:stripped_oak_log", - "id" : -10 - }, - { - "name" : "minecraft:golden_apple", - "id" : 322 - }, - { - "name" : "minecraft:fish", - "id" : 349 - }, - { - "name" : "minecraft:item.dark_oak_door", - "id" : 197 - }, - { - "name" : "minecraft:light_block", - "id" : -215 - }, - { - "name" : "minecraft:yellow_glazed_terracotta", - "id" : 224 - }, - { - "name" : "minecraft:stone_brick_stairs", - "id" : 109 - }, - { - "name" : "minecraft:portal", - "id" : 90 - }, - { - "name" : "minecraft:gold_ingot", - "id" : 266 - }, - { - "name" : "minecraft:iron_ingot", - "id" : 265 - }, - { - "name" : "minecraft:cookie", - "id" : 357 - }, - { - "name" : "minecraft:porkchop", - "id" : 319 - }, - { - "name" : "minecraft:bread", - "id" : 297 - }, - { - "name" : "minecraft:element_7", - "id" : -18 - }, - { - "name" : "minecraft:diamond_block", - "id" : 57 - }, - { - "name" : "minecraft:iron_pickaxe", - "id" : 257 - }, - { - "name" : "minecraft:element_27", - "id" : -38 - }, - { - "name" : "minecraft:beef", - "id" : 363 - }, - { - "name" : "minecraft:salmon", - "id" : 460 - }, - { - "name" : "minecraft:melon", - "id" : 360 - }, - { - "name" : "minecraft:clownfish", - "id" : 461 - }, - { - "name" : "minecraft:element_16", - "id" : -27 - }, - { - "name" : "minecraft:tripwire", - "id" : 132 - }, - { - "name" : "minecraft:stone_axe", - "id" : 275 - }, - { - "name" : "minecraft:stained_glass_pane", - "id" : 160 - }, - { - "name" : "minecraft:trapped_chest", - "id" : 146 - }, - { - "name" : "minecraft:pufferfish", - "id" : 462 - }, - { - "name" : "minecraft:bucket", - "id" : 325 - }, - { - "name" : "minecraft:ancient_debris", - "id" : -271 - }, - { - "name" : "minecraft:anvil", - "id" : 145 - }, - { - "name" : "minecraft:stick", - "id" : 280 - }, - { - "name" : "minecraft:cooked_fish", - "id" : 350 - }, - { - "name" : "minecraft:cooked_salmon", - "id" : 463 - }, - { - "name" : "minecraft:element_61", - "id" : -72 - }, - { - "name" : "minecraft:sparkler", - "id" : 442 - }, - { - "name" : "minecraft:warped_door", - "id" : 756 - }, - { - "name" : "minecraft:dried_kelp", - "id" : 464 - }, - { - "name" : "minecraft:hay_block", - "id" : 170 - }, - { - "name" : "minecraft:wooden_shovel", - "id" : 269 - }, - { - "name" : "minecraft:nautilus_shell", - "id" : 465 - }, - { - "name" : "minecraft:element_1", - "id" : -12 - }, - { - "name" : "minecraft:stonecutter_block", - "id" : -197 - }, - { - "name" : "minecraft:cooked_beef", - "id" : 364 - }, - { - "name" : "minecraft:comparator", - "id" : 404 - }, - { - "name" : "minecraft:carrot", - "id" : 391 - }, - { - "name" : "minecraft:command_block", - "id" : 137 - }, - { - "name" : "minecraft:chicken", - "id" : 365 - }, - { - "name" : "minecraft:potion", - "id" : 373 - }, - { - "name" : "minecraft:rotten_flesh", - "id" : 367 - }, - { - "name" : "minecraft:dirt", - "id" : 3 - }, - { - "name" : "minecraft:element_62", - "id" : -73 - }, - { - "name" : "minecraft:daylight_detector", - "id" : 151 - }, - { - "name" : "minecraft:snow_layer", - "id" : 78 - }, - { - "name" : "minecraft:rabbit_foot", - "id" : 414 - }, - { - "name" : "minecraft:lingering_potion", - "id" : 441 - }, - { - "name" : "minecraft:campfire", - "id" : 720 - }, - { - "name" : "minecraft:smoker", - "id" : -198 - }, - { - "name" : "minecraft:warped_fence", - "id" : -257 - }, - { - "name" : "minecraft:cooked_chicken", - "id" : 366 - }, - { - "name" : "minecraft:light_blue_glazed_terracotta", - "id" : 223 - }, - { - "name" : "minecraft:stone_sword", - "id" : 272 - }, - { - "name" : "minecraft:record_far", - "id" : 504 - }, - { - "name" : "minecraft:spider_eye", - "id" : 375 - }, - { - "name" : "minecraft:smooth_quartz_stairs", - "id" : -185 - }, - { - "name" : "minecraft:potato", - "id" : 392 - }, - { - "name" : "minecraft:baked_potato", - "id" : 393 - }, - { - "name" : "minecraft:element_88", - "id" : -99 - }, - { - "name" : "minecraft:golden_carrot", - "id" : 396 - }, - { - "name" : "minecraft:spruce_stairs", - "id" : 134 - }, - { - "name" : "minecraft:poisonous_potato", - "id" : 394 - }, - { - "name" : "minecraft:element_13", - "id" : -24 - }, - { - "name" : "minecraft:obsidian", - "id" : 49 - }, - { - "name" : "minecraft:pumpkin_pie", - "id" : 400 - }, - { - "name" : "minecraft:diamond_pickaxe", - "id" : 278 - }, - { - "name" : "minecraft:lantern", - "id" : -208 - }, - { - "name" : "minecraft:iron_sword", - "id" : 267 - }, - { - "name" : "minecraft:smooth_stone", - "id" : -183 - }, - { - "name" : "minecraft:beetroot", - "id" : 457 - }, - { - "name" : "minecraft:element_43", - "id" : -54 - }, - { - "name" : "minecraft:beetroot_soup", - "id" : 459 - }, - { - "name" : "minecraft:red_mushroom", - "id" : 40 - }, - { - "name" : "minecraft:wooden_pickaxe", - "id" : 270 - }, - { - "name" : "minecraft:invisiblebedrock", - "id" : 95 - }, - { - "name" : "minecraft:sweet_berries", - "id" : 477 - }, - { - "name" : "minecraft:prismarine_bricks_stairs", - "id" : -4 - }, - { - "name" : "minecraft:cooked_rabbit", - "id" : 412 - }, - { - "name" : "minecraft:rabbit_stew", - "id" : 413 - }, - { - "name" : "minecraft:birch_fence_gate", - "id" : 184 - }, - { - "name" : "minecraft:wheat_seeds", - "id" : 295 - }, - { - "name" : "minecraft:chest", - "id" : 54 - }, - { - "name" : "minecraft:pumpkin_seeds", - "id" : 361 - }, - { - "name" : "minecraft:element_2", - "id" : -13 - }, - { - "name" : "minecraft:item.crimson_door", - "id" : -244 - }, - { - "name" : "minecraft:command_block_minecart", - "id" : 443 - }, - { - "name" : "minecraft:melon_seeds", - "id" : 362 - }, - { - "name" : "minecraft:iron_axe", - "id" : 258 - }, - { - "name" : "minecraft:spawn_egg", - "id" : 383 - }, - { - "name" : "minecraft:element_93", - "id" : -104 - }, - { - "name" : "minecraft:nether_wart", - "id" : 372 - }, - { - "name" : "minecraft:beetroot_seeds", - "id" : 458 - }, - { - "name" : "minecraft:element_35", - "id" : -46 - }, - { - "name" : "minecraft:iron_shovel", - "id" : 256 - }, - { - "name" : "minecraft:element_104", - "id" : -115 - }, - { - "name" : "minecraft:granite_stairs", - "id" : -169 - }, - { - "name" : "minecraft:flint_and_steel", - "id" : 259 - }, - { - "name" : "minecraft:stone_shovel", - "id" : 273 - }, - { - "name" : "minecraft:horsearmorleather", - "id" : 416 - }, - { - "name" : "minecraft:item.cauldron", - "id" : 118 - }, - { - "name" : "minecraft:melon_block", - "id" : 103 - }, - { - "name" : "minecraft:arrow", - "id" : 262 - }, - { - "name" : "minecraft:coal", - "id" : 263 - }, - { - "name" : "minecraft:real_double_stone_slab2", - "id" : 181 - }, - { - "name" : "minecraft:chorus_plant", - "id" : 240 - }, - { - "name" : "minecraft:gold_block", - "id" : 41 - }, - { - "name" : "minecraft:carrots", - "id" : 141 - }, - { - "name" : "minecraft:diamond", - "id" : 264 - }, - { - "name" : "minecraft:wooden_sword", - "id" : 268 - }, - { - "name" : "minecraft:record_strad", - "id" : 508 - }, - { - "name" : "minecraft:netherite_boots", - "id" : 751 - }, - { - "name" : "minecraft:dark_oak_stairs", - "id" : 164 - }, - { - "name" : "minecraft:farmland", - "id" : 60 - }, - { - "name" : "minecraft:wooden_axe", - "id" : 271 - }, - { - "name" : "minecraft:stone_pickaxe", - "id" : 274 - }, - { - "name" : "minecraft:planks", - "id" : 5 - }, - { - "name" : "minecraft:chainmail_helmet", - "id" : 302 - }, - { - "name" : "minecraft:diamond_shovel", - "id" : 277 - }, - { - "name" : "minecraft:diamond_sword", - "id" : 276 - }, - { - "name" : "minecraft:smithing_table", - "id" : -202 - }, - { - "name" : "minecraft:diamond_axe", - "id" : 279 - }, - { - "name" : "minecraft:bowl", - "id" : 281 - }, - { - "name" : "minecraft:flowing_water", - "id" : 8 - }, - { - "name" : "minecraft:golden_sword", - "id" : 283 - }, - { - "name" : "minecraft:honey_block", - "id" : -220 - }, - { - "name" : "minecraft:golden_shovel", - "id" : 284 - }, - { - "name" : "minecraft:golden_pickaxe", - "id" : 285 - }, - { - "name" : "minecraft:lit_redstone_lamp", - "id" : 124 - }, - { - "name" : "minecraft:elytra", - "id" : 444 - }, - { - "name" : "minecraft:golden_axe", - "id" : 286 - }, - { - "name" : "minecraft:element_52", - "id" : -63 - }, - { - "name" : "minecraft:string", - "id" : 287 - }, - { - "name" : "minecraft:real_double_stone_slab4", - "id" : -168 - }, - { - "name" : "minecraft:feather", - "id" : 288 - }, - { - "name" : "minecraft:gunpowder", - "id" : 289 - }, - { - "name" : "minecraft:acacia_stairs", - "id" : 163 - }, - { - "name" : "minecraft:wooden_hoe", - "id" : 290 - }, - { - "name" : "minecraft:stone_hoe", - "id" : 291 - }, - { - "name" : "minecraft:iron_hoe", - "id" : 292 - }, - { - "name" : "minecraft:diamond_hoe", - "id" : 293 - }, - { - "name" : "minecraft:element_86", - "id" : -97 - }, - { - "name" : "minecraft:golden_hoe", - "id" : 294 - }, - { - "name" : "minecraft:wheat", - "id" : 296 - }, - { - "name" : "minecraft:leather_helmet", - "id" : 298 - }, - { - "name" : "minecraft:leather_chestplate", - "id" : 299 - }, - { - "name" : "minecraft:leather_leggings", - "id" : 300 - }, - { - "name" : "minecraft:lodestone", - "id" : -222 - }, - { - "name" : "minecraft:brown_mushroom", - "id" : 39 - }, - { - "name" : "minecraft:leather_boots", - "id" : 301 - }, - { - "name" : "minecraft:chainmail_chestplate", - "id" : 303 - }, - { - "name" : "minecraft:end_gateway", - "id" : 209 - }, - { - "name" : "minecraft:item.beetroot", - "id" : 244 - }, - { - "name" : "minecraft:chainmail_leggings", - "id" : 304 - }, - { - "name" : "minecraft:element_101", - "id" : -112 - }, - { - "name" : "minecraft:chainmail_boots", - "id" : 305 - }, - { - "name" : "minecraft:soul_sand", - "id" : 88 - }, - { - "name" : "minecraft:iron_helmet", - "id" : 306 - }, - { - "name" : "minecraft:snowball", - "id" : 332 - }, - { - "name" : "minecraft:element_49", - "id" : -60 - }, - { - "name" : "minecraft:record_mellohi", - "id" : 506 - }, - { - "name" : "minecraft:iron_chestplate", - "id" : 307 - }, - { - "name" : "minecraft:barrel", - "id" : -203 - }, - { - "name" : "minecraft:iron_leggings", - "id" : 308 - }, - { - "name" : "minecraft:crimson_double_slab", - "id" : -266 - }, - { - "name" : "minecraft:iron_boots", - "id" : 309 - }, - { - "name" : "minecraft:real_double_stone_slab3", - "id" : -167 - }, - { - "name" : "minecraft:ender_eye", - "id" : 381 - }, - { - "name" : "minecraft:stickypistonarmcollision", - "id" : -217 - }, - { - "name" : "minecraft:iron_trapdoor", - "id" : 167 - }, - { - "name" : "minecraft:diamond_helmet", - "id" : 310 - }, - { - "name" : "minecraft:stone_pressure_plate", - "id" : 70 - }, - { - "name" : "minecraft:diamond_chestplate", - "id" : 311 - }, - { - "name" : "minecraft:sand", - "id" : 12 - }, - { - "name" : "minecraft:light_weighted_pressure_plate", - "id" : 147 - }, - { - "name" : "minecraft:piston", - "id" : 33 - }, - { - "name" : "minecraft:diamond_leggings", - "id" : 312 - }, - { - "name" : "minecraft:element_30", - "id" : -41 - }, - { - "name" : "minecraft:diamond_boots", - "id" : 313 - }, - { - "name" : "minecraft:golden_helmet", - "id" : 314 - }, - { - "name" : "minecraft:element_51", - "id" : -62 - }, - { - "name" : "minecraft:double_wooden_slab", - "id" : 157 - }, - { - "name" : "minecraft:hard_stained_glass", - "id" : 254 - }, - { - "name" : "minecraft:element_84", - "id" : -95 - }, - { - "name" : "minecraft:golden_chestplate", - "id" : 315 - }, - { - "name" : "minecraft:sealantern", - "id" : 169 - }, - { - "name" : "minecraft:bedrock", - "id" : 7 - }, - { - "name" : "minecraft:glowstone", - "id" : 89 - }, - { - "name" : "minecraft:golden_leggings", - "id" : 316 - }, - { - "name" : "minecraft:golden_boots", - "id" : 317 - }, - { - "name" : "minecraft:shield", - "id" : 513 - }, - { - "name" : "minecraft:jungle_fence_gate", - "id" : 185 - }, - { - "name" : "minecraft:carpet", - "id" : 171 - }, - { - "name" : "minecraft:flowing_lava", - "id" : 10 - }, - { - "name" : "minecraft:flint", - "id" : 318 - }, - { - "name" : "minecraft:painting", - "id" : 321 - }, - { - "name" : "minecraft:heart_of_the_sea", - "id" : 467 - }, - { - "name" : "minecraft:sign", - "id" : 323 - }, - { - "name" : "minecraft:muttonraw", - "id" : 423 - }, - { - "name" : "minecraft:element_55", - "id" : -66 - }, - { - "name" : "minecraft:wooden_door", - "id" : 324 - }, - { - "name" : "minecraft:concrete_powder", - "id" : 237 - }, - { - "name" : "minecraft:minecart", - "id" : 328 - }, - { - "name" : "minecraft:saddle", - "id" : 329 - }, - { - "name" : "minecraft:nether_wart_block", - "id" : 214 - }, - { - "name" : "minecraft:crimson_roots", - "id" : -223 - }, - { - "name" : "minecraft:element_116", - "id" : -127 - }, - { - "name" : "minecraft:iron_door", - "id" : 330 - }, - { - "name" : "minecraft:redstone", - "id" : 331 - }, - { - "name" : "minecraft:boat", - "id" : 333 - }, - { - "name" : "minecraft:written_book", - "id" : 387 - }, - { - "name" : "minecraft:iron_ore", - "id" : 15 - }, - { - "name" : "minecraft:leather", - "id" : 334 - }, - { - "name" : "minecraft:kelp", - "id" : 335 - }, - { - "name" : "minecraft:gold_nugget", - "id" : 371 - }, - { - "name" : "minecraft:brick", - "id" : 336 - }, - { - "name" : "minecraft:element_68", - "id" : -79 - }, - { - "name" : "minecraft:clay_ball", - "id" : 337 - }, - { - "name" : "minecraft:carrotonastick", - "id" : 398 - }, - { - "name" : "minecraft:reeds", - "id" : 338 - }, - { - "name" : "minecraft:paper", - "id" : 339 - }, - { - "name" : "minecraft:element_23", - "id" : -34 - }, - { - "name" : "minecraft:coral", - "id" : -131 - }, - { - "name" : "minecraft:book", - "id" : 340 - }, - { - "name" : "minecraft:end_portal", - "id" : 119 - }, - { - "name" : "minecraft:trident", - "id" : 455 - }, - { - "name" : "minecraft:slime_ball", - "id" : 341 - }, - { - "name" : "minecraft:chest_minecart", - "id" : 342 - }, - { - "name" : "minecraft:element_71", - "id" : -82 - }, - { - "name" : "minecraft:egg", - "id" : 344 - }, - { - "name" : "minecraft:netherite_sword", - "id" : 743 - }, - { - "name" : "minecraft:item.reeds", - "id" : 83 - }, - { - "name" : "minecraft:compass", - "id" : 345 - }, - { - "name" : "minecraft:crimson_stairs", - "id" : -254 - }, - { - "name" : "minecraft:fishing_rod", - "id" : 346 - }, - { - "name" : "minecraft:andesite_stairs", - "id" : -171 - }, - { - "name" : "minecraft:reserved6", - "id" : 255 - }, - { - "name" : "minecraft:clock", - "id" : 347 - }, - { - "name" : "minecraft:red_sandstone", - "id" : 179 - }, - { - "name" : "minecraft:spruce_button", - "id" : -144 - }, - { - "name" : "minecraft:glowstone_dust", - "id" : 348 - }, - { - "name" : "minecraft:blaze_rod", - "id" : 369 - }, - { - "name" : "minecraft:dye", - "id" : 351 - }, - { - "name" : "minecraft:element_74", - "id" : -85 - }, - { - "name" : "minecraft:bone", - "id" : 352 - }, - { - "name" : "minecraft:map", - "id" : 358 - }, - { - "name" : "minecraft:sugar", - "id" : 353 - }, - { - "name" : "minecraft:name_tag", - "id" : 421 - }, - { - "name" : "minecraft:cake", - "id" : 354 - }, - { - "name" : "minecraft:bed", - "id" : 355 - }, - { - "name" : "minecraft:stained_glass", - "id" : 241 - }, - { - "name" : "minecraft:repeater", - "id" : 356 - }, - { - "name" : "minecraft:beacon", - "id" : 138 - }, - { - "name" : "minecraft:netherite_chestplate", - "id" : 749 - }, - { - "name" : "minecraft:unpowered_comparator", - "id" : 149 - }, - { - "name" : "minecraft:shears", - "id" : 359 - }, - { - "name" : "minecraft:element_31", - "id" : -42 - }, - { - "name" : "minecraft:ender_pearl", - "id" : 368 - }, - { - "name" : "minecraft:red_sandstone_stairs", - "id" : 180 - }, - { - "name" : "minecraft:carved_pumpkin", - "id" : -155 - }, - { - "name" : "minecraft:ghast_tear", - "id" : 370 - }, - { - "name" : "minecraft:glass_bottle", - "id" : 374 - }, - { - "name" : "minecraft:element_44", - "id" : -55 - }, - { - "name" : "minecraft:lava", - "id" : 11 - }, - { - "name" : "minecraft:polished_blackstone_brick_stairs", - "id" : -275 - }, - { - "name" : "minecraft:jungle_pressure_plate", - "id" : -153 - }, - { - "name" : "minecraft:fermented_spider_eye", - "id" : 376 - }, - { - "name" : "minecraft:honeycomb_block", - "id" : -221 - }, - { - "name" : "minecraft:blaze_powder", + "name" : "minecraft:acacia_boat", "id" : 377 }, { - "name" : "minecraft:magma_cream", - "id" : 378 - }, - { - "name" : "minecraft:jigsaw", - "id" : -211 - }, - { - "name" : "minecraft:brewing_stand", - "id" : 379 - }, - { - "name" : "minecraft:cauldron", - "id" : 380 - }, - { - "name" : "minecraft:element_111", - "id" : -122 - }, - { - "name" : "minecraft:rapid_fertilizer", - "id" : 449 - }, - { - "name" : "minecraft:clay", - "id" : 82 - }, - { - "name" : "minecraft:speckled_melon", - "id" : 382 - }, - { - "name" : "minecraft:experience_bottle", - "id" : 384 - }, - { - "name" : "minecraft:element_48", - "id" : -59 - }, - { - "name" : "minecraft:coal_block", - "id" : 173 - }, - { - "name" : "minecraft:fireball", - "id" : 385 - }, - { - "name" : "minecraft:writable_book", - "id" : 386 - }, - { - "name" : "minecraft:element_69", - "id" : -80 - }, - { - "name" : "minecraft:emerald", - "id" : 388 - }, - { - "name" : "minecraft:record_pigstep", - "id" : 759 - }, - { - "name" : "minecraft:element_66", - "id" : -77 - }, - { - "name" : "minecraft:frame", - "id" : 389 - }, - { - "name" : "minecraft:brewingstandblock", - "id" : 117 - }, - { - "name" : "minecraft:flower_pot", - "id" : 390 - }, - { - "name" : "minecraft:emptymap", - "id" : 395 - }, - { - "name" : "minecraft:element_110", - "id" : -121 - }, - { - "name" : "minecraft:element_75", - "id" : -86 - }, - { - "name" : "minecraft:skull", - "id" : 397 - }, - { - "name" : "minecraft:crimson_door", - "id" : 755 - }, - { - "name" : "minecraft:sponge", - "id" : 19 - }, - { - "name" : "minecraft:netherstar", - "id" : 399 - }, - { - "name" : "minecraft:fireworks", - "id" : 401 - }, - { - "name" : "minecraft:hopper_minecart", - "id" : 408 - }, - { - "name" : "minecraft:fireworkscharge", - "id" : 402 - }, - { - "name" : "minecraft:enchanted_book", - "id" : 403 - }, - { - "name" : "minecraft:netherbrick", - "id" : 405 - }, - { - "name" : "minecraft:cobblestone_wall", - "id" : 139 - }, - { - "name" : "minecraft:quartz", - "id" : 406 - }, - { - "name" : "minecraft:tnt_minecart", - "id" : 407 - }, - { - "name" : "minecraft:element_63", - "id" : -74 - }, - { - "name" : "minecraft:hopper", - "id" : 410 - }, - { - "name" : "minecraft:cobblestone", - "id" : 4 - }, - { - "name" : "minecraft:dragon_breath", - "id" : 437 - }, - { - "name" : "minecraft:rabbit_hide", - "id" : 415 - }, - { - "name" : "minecraft:horsearmoriron", - "id" : 417 - }, - { - "name" : "minecraft:horsearmorgold", - "id" : 418 - }, - { - "name" : "minecraft:colored_torch_bp", - "id" : 204 - }, - { - "name" : "minecraft:element_102", - "id" : -113 - }, - { - "name" : "minecraft:quartz_ore", - "id" : 153 - }, - { - "name" : "minecraft:netherite_shovel", - "id" : 744 - }, - { - "name" : "minecraft:horsearmordiamond", - "id" : 419 - }, - { - "name" : "minecraft:record_13", - "id" : 500 - }, - { - "name" : "minecraft:record_cat", - "id" : 501 - }, - { - "name" : "minecraft:element_3", - "id" : -14 - }, - { - "name" : "minecraft:polished_diorite_stairs", - "id" : -173 - }, - { - "name" : "minecraft:monster_egg", - "id" : 97 - }, - { - "name" : "minecraft:record_blocks", - "id" : 502 - }, - { - "name" : "minecraft:crimson_standing_sign", - "id" : -250 - }, - { - "name" : "minecraft:record_chirp", - "id" : 503 - }, - { - "name" : "minecraft:record_mall", - "id" : 505 - }, - { - "name" : "minecraft:respawn_anchor", - "id" : -272 - }, - { - "name" : "minecraft:record_stal", - "id" : 507 - }, - { - "name" : "minecraft:record_11", - "id" : 510 - }, - { - "name" : "minecraft:record_wait", - "id" : 511 - }, - { - "name" : "minecraft:info_update2", - "id" : 249 - }, - { - "name" : "minecraft:lead", - "id" : 420 - }, - { - "name" : "minecraft:prismarine_crystals", - "id" : 422 - }, - { - "name" : "minecraft:acacia_sign", - "id" : 475 - }, - { - "name" : "minecraft:muttoncooked", - "id" : 424 - }, - { - "name" : "minecraft:armor_stand", - "id" : 425 - }, - { - "name" : "minecraft:coal_ore", - "id" : 16 - }, - { - "name" : "minecraft:element_32", - "id" : -43 - }, - { - "name" : "minecraft:spruce_door", - "id" : 427 - }, - { - "name" : "minecraft:phantom_membrane", - "id" : 470 - }, - { - "name" : "minecraft:birch_door", - "id" : 428 - }, - { - "name" : "minecraft:element_85", - "id" : -96 - }, - { - "name" : "minecraft:polished_blackstone_wall", - "id" : -297 - }, - { - "name" : "minecraft:jungle_door", - "id" : 429 + "name" : "minecraft:acacia_button", + "id" : -140 }, { "name" : "minecraft:acacia_door", - "id" : 430 + "id" : 546 }, { - "name" : "minecraft:element_42", - "id" : -53 - }, - { - "name" : "minecraft:dark_oak_door", - "id" : 431 - }, - { - "name" : "minecraft:netherite_leggings", - "id" : 750 - }, - { - "name" : "minecraft:stripped_crimson_stem", - "id" : -240 - }, - { - "name" : "minecraft:chorus_fruit", - "id" : 432 - }, - { - "name" : "minecraft:camera", - "id" : 498 - }, - { - "name" : "minecraft:suspicious_stew", - "id" : 734 - }, - { - "name" : "minecraft:chorus_fruit_popped", - "id" : 433 - }, - { - "name" : "minecraft:element_98", - "id" : -109 - }, - { - "name" : "minecraft:splash_potion", - "id" : 438 - }, - { - "name" : "minecraft:element_73", - "id" : -84 - }, - { - "name" : "minecraft:prismarine_shard", - "id" : 409 - }, - { - "name" : "minecraft:seagrass", - "id" : -130 - }, - { - "name" : "minecraft:dark_oak_pressure_plate", - "id" : -152 - }, - { - "name" : "minecraft:shulker_shell", - "id" : 445 - }, - { - "name" : "minecraft:redstone_block", - "id" : 152 - }, - { - "name" : "minecraft:banner", - "id" : 446 - }, - { - "name" : "minecraft:totem", - "id" : 450 - }, - { - "name" : "minecraft:blackstone_slab", - "id" : -282 - }, - { - "name" : "minecraft:element_118", - "id" : -129 - }, - { - "name" : "minecraft:iron_nugget", - "id" : 452 - }, - { - "name" : "minecraft:netherite_pickaxe", - "id" : 745 - }, - { - "name" : "minecraft:jukebox", - "id" : 84 - }, - { - "name" : "minecraft:turtle_shell_piece", - "id" : 468 - }, - { - "name" : "minecraft:turtle_helmet", - "id" : 469 - }, - { - "name" : "minecraft:crossbow", - "id" : 471 - }, - { - "name" : "minecraft:glowingobsidian", - "id" : 246 - }, - { - "name" : "minecraft:leaves2", - "id" : 161 - }, - { - "name" : "minecraft:spruce_sign", - "id" : 472 - }, - { - "name" : "minecraft:element_38", - "id" : -49 - }, - { - "name" : "minecraft:coral_fan_hang2", - "id" : -136 - }, - { - "name" : "minecraft:birch_sign", - "id" : 473 - }, - { - "name" : "minecraft:coral_fan_dead", - "id" : -134 - }, - { - "name" : "minecraft:balloon", - "id" : 448 - }, - { - "name" : "minecraft:jungle_sign", - "id" : 474 - }, - { - "name" : "minecraft:darkoak_sign", - "id" : 476 - }, - { - "name" : "minecraft:element_24", - "id" : -35 - }, - { - "name" : "minecraft:banner_pattern", - "id" : 434 - }, - { - "name" : "minecraft:honeycomb", - "id" : 736 - }, - { - "name" : "minecraft:element_78", - "id" : -89 - }, - { - "name" : "minecraft:red_nether_brick", - "id" : 215 - }, - { - "name" : "minecraft:honey_bottle", - "id" : 737 - }, - { - "name" : "minecraft:compound", - "id" : 499 - }, - { - "name" : "minecraft:ice_bomb", - "id" : 453 - }, - { - "name" : "minecraft:brick_block", - "id" : 45 - }, - { - "name" : "minecraft:bleach", - "id" : 451 - }, - { - "name" : "minecraft:colored_torch_rg", - "id" : 202 - }, - { - "name" : "minecraft:medicine", - "id" : 447 - }, - { - "name" : "minecraft:warped_fungus", - "id" : -229 - }, - { - "name" : "minecraft:end_portal_frame", - "id" : 120 - }, - { - "name" : "minecraft:element_92", - "id" : -103 - }, - { - "name" : "minecraft:glow_stick", - "id" : 166 - }, - { - "name" : "minecraft:lodestonecompass", - "id" : 741 - }, - { - "name" : "minecraft:element_17", - "id" : -28 - }, - { - "name" : "minecraft:lit_pumpkin", - "id" : 91 - }, - { - "name" : "minecraft:netherite_ingot", - "id" : 742 - }, - { - "name" : "minecraft:chain_command_block", - "id" : 189 - }, - { - "name" : "minecraft:loom", - "id" : -204 - }, - { - "name" : "minecraft:item.warped_door", - "id" : -245 - }, - { - "name" : "minecraft:netherite_axe", - "id" : 746 - }, - { - "name" : "minecraft:netherite_hoe", - "id" : 747 - }, - { - "name" : "minecraft:dark_oak_fence_gate", - "id" : 186 - }, - { - "name" : "minecraft:element_115", - "id" : -126 - }, - { - "name" : "minecraft:netherite_helmet", - "id" : 748 - }, - { - "name" : "minecraft:element_117", - "id" : -128 - }, - { - "name" : "minecraft:netherite_scrap", - "id" : 752 - }, - { - "name" : "minecraft:crimson_sign", - "id" : 753 - }, - { - "name" : "minecraft:concrete", - "id" : 236 - }, - { - "name" : "minecraft:chiseled_nether_bricks", - "id" : -302 - }, - { - "name" : "minecraft:mob_spawner", - "id" : 52 - }, - { - "name" : "minecraft:warped_sign", - "id" : 754 - }, - { - "name" : "minecraft:chain", - "id" : 758 - }, - { - "name" : "minecraft:warped_fungus_on_a_stick", - "id" : 757 - }, - { - "name" : "minecraft:nether_sprouts", - "id" : 760 - }, - { - "name" : "minecraft:cartography_table", - "id" : -200 - }, - { - "name" : "minecraft:polished_blackstone_slab", - "id" : -293 - }, - { - "name" : "minecraft:soul_campfire", - "id" : 801 - }, - { - "name" : "minecraft:stone", - "id" : 1 - }, - { - "name" : "minecraft:wool", - "id" : 35 - }, - { - "name" : "minecraft:yellow_flower", - "id" : 37 - }, - { - "name" : "minecraft:stained_hardened_clay", - "id" : 159 - }, - { - "name" : "minecraft:log", - "id" : 17 - }, - { - "name" : "minecraft:fence", - "id" : 85 - }, - { - "name" : "minecraft:element_53", - "id" : -64 - }, - { - "name" : "minecraft:stonebrick", - "id" : 98 - }, - { - "name" : "minecraft:lit_blast_furnace", - "id" : -214 - }, - { - "name" : "minecraft:coral_block", - "id" : -132 - }, - { - "name" : "minecraft:polished_blackstone_bricks", - "id" : -274 - }, - { - "name" : "minecraft:double_stone_slab", - "id" : 44 - }, - { - "name" : "minecraft:element_100", - "id" : -111 - }, - { - "name" : "minecraft:double_stone_slab2", - "id" : 182 - }, - { - "name" : "minecraft:fence_gate", - "id" : 107 - }, - { - "name" : "minecraft:double_stone_slab3", - "id" : -162 - }, - { - "name" : "minecraft:rail", - "id" : 66 - }, - { - "name" : "minecraft:double_stone_slab4", - "id" : -166 - }, - { - "name" : "minecraft:stripped_acacia_log", - "id" : -8 - }, - { - "name" : "minecraft:real_double_stone_slab", - "id" : 43 - }, - { - "name" : "minecraft:coral_fan", - "id" : -133 - }, - { - "name" : "minecraft:sea_pickle", - "id" : -156 - }, - { - "name" : "minecraft:polished_blackstone_button", - "id" : -296 - }, - { - "name" : "minecraft:element_90", - "id" : -101 - }, - { - "name" : "minecraft:polished_blackstone_double_slab", - "id" : -294 - }, - { - "name" : "minecraft:sapling", - "id" : 6 - }, - { - "name" : "minecraft:leaves", - "id" : 18 - }, - { - "name" : "minecraft:sandstone", - "id" : 24 - }, - { - "name" : "minecraft:silver_glazed_terracotta", - "id" : 228 - }, - { - "name" : "minecraft:wooden_slab", - "id" : 158 - }, - { - "name" : "minecraft:warped_roots", - "id" : -224 - }, - { - "name" : "minecraft:element_11", - "id" : -22 - }, - { - "name" : "minecraft:red_flower", - "id" : 38 - }, - { - "name" : "minecraft:element_59", - "id" : -70 - }, - { - "name" : "minecraft:double_plant", - "id" : 175 - }, - { - "name" : "minecraft:waterlily", - "id" : 111 - }, - { - "name" : "minecraft:quartz_block", - "id" : 155 - }, - { - "name" : "minecraft:element_95", - "id" : -106 - }, - { - "name" : "minecraft:soul_soil", - "id" : -236 + "name" : "minecraft:acacia_fence_gate", + "id" : 187 }, { "name" : "minecraft:acacia_pressure_plate", "id" : -150 }, { - "name" : "minecraft:tallgrass", - "id" : 31 + "name" : "minecraft:acacia_sign", + "id" : 569 }, { - "name" : "minecraft:brown_mushroom_block", - "id" : 99 + "name" : "minecraft:acacia_stairs", + "id" : 163 }, { - "name" : "minecraft:element_103", - "id" : -114 + "name" : "minecraft:acacia_standing_sign", + "id" : -190 }, { - "name" : "minecraft:crimson_fungus", - "id" : -228 + "name" : "minecraft:acacia_trapdoor", + "id" : -145 }, { - "name" : "minecraft:item.frame", - "id" : 199 + "name" : "minecraft:acacia_wall_sign", + "id" : -191 }, { - "name" : "minecraft:red_mushroom_block", - "id" : 100 + "name" : "minecraft:activator_rail", + "id" : 126 }, { - "name" : "minecraft:log2", - "id" : 162 + "name" : "minecraft:agent_spawn_egg", + "id" : 485 }, { - "name" : "minecraft:conduit", - "id" : -157 + "name" : "minecraft:air", + "id" : -158 }, { - "name" : "minecraft:prismarine", - "id" : 168 + "name" : "minecraft:allow", + "id" : 210 }, { - "name" : "minecraft:magma", - "id" : 213 + "name" : "minecraft:ancient_debris", + "id" : -271 }, { - "name" : "minecraft:element_22", - "id" : -33 + "name" : "minecraft:andesite_stairs", + "id" : -171 }, { - "name" : "minecraft:undyed_shulker_box", - "id" : 205 + "name" : "minecraft:anvil", + "id" : 145 }, { - "name" : "minecraft:shulker_box", - "id" : 218 + "name" : "minecraft:apple", + "id" : 257 }, { - "name" : "minecraft:spruce_standing_sign", - "id" : -181 + "name" : "minecraft:armor_stand", + "id" : 542 }, { - "name" : "minecraft:sticky_piston", - "id" : 29 + "name" : "minecraft:arrow", + "id" : 301 }, { - "name" : "minecraft:element_10", - "id" : -21 + "name" : "minecraft:baked_potato", + "id" : 281 }, { - "name" : "minecraft:turtle_egg", - "id" : -159 + "name" : "minecraft:balloon", + "id" : 587 }, { "name" : "minecraft:bamboo", "id" : -163 }, { - "name" : "minecraft:observer", - "id" : 251 + "name" : "minecraft:bamboo_sapling", + "id" : -164 }, { - "name" : "minecraft:scaffolding", - "id" : -165 + "name" : "minecraft:banner", + "id" : 557 }, { - "name" : "minecraft:blast_furnace", - "id" : -196 + "name" : "minecraft:banner_pattern", + "id" : 613 }, { - "name" : "minecraft:grindstone", - "id" : -195 + "name" : "minecraft:barrel", + "id" : -203 + }, + { + "name" : "minecraft:barrier", + "id" : -161 + }, + { + "name" : "minecraft:basalt", + "id" : -234 + }, + { + "name" : "minecraft:bat_spawn_egg", + "id" : 451 + }, + { + "name" : "minecraft:beacon", + "id" : 138 + }, + { + "name" : "minecraft:bed", + "id" : 416 + }, + { + "name" : "minecraft:bedrock", + "id" : 7 + }, + { + "name" : "minecraft:bee_nest", + "id" : -218 + }, + { + "name" : "minecraft:bee_spawn_egg", + "id" : 492 + }, + { + "name" : "minecraft:beef", + "id" : 273 + }, + { + "name" : "minecraft:beehive", + "id" : -219 + }, + { + "name" : "minecraft:beetroot", + "id" : 285 + }, + { + "name" : "minecraft:beetroot_seeds", + "id" : 295 + }, + { + "name" : "minecraft:beetroot_soup", + "id" : 286 }, { "name" : "minecraft:bell", "id" : -206 }, { - "name" : "minecraft:end_rod", - "id" : 208 + "name" : "minecraft:birch_boat", + "id" : 374 }, { - "name" : "minecraft:fletching_table", - "id" : -201 + "name" : "minecraft:birch_button", + "id" : -141 }, { - "name" : "minecraft:item.hopper", - "id" : 154 + "name" : "minecraft:birch_door", + "id" : 544 }, { - "name" : "minecraft:wood", - "id" : -212 + "name" : "minecraft:birch_fence_gate", + "id" : 184 }, { - "name" : "minecraft:chemistry_table", - "id" : 238 + "name" : "minecraft:birch_pressure_plate", + "id" : -151 }, { - "name" : "minecraft:tnt", - "id" : 46 + "name" : "minecraft:birch_sign", + "id" : 567 }, { - "name" : "minecraft:hard_stained_glass_pane", - "id" : 191 + "name" : "minecraft:birch_stairs", + "id" : 135 }, { - "name" : "minecraft:crimson_slab", - "id" : -264 + "name" : "minecraft:birch_standing_sign", + "id" : -186 }, { - "name" : "minecraft:element_87", - "id" : -98 + "name" : "minecraft:birch_trapdoor", + "id" : -146 }, { - "name" : "minecraft:warped_slab", - "id" : -265 + "name" : "minecraft:birch_wall_sign", + "id" : -187 }, { - "name" : "minecraft:element_0", - "id" : 36 - }, - { - "name" : "minecraft:element_4", - "id" : -15 - }, - { - "name" : "minecraft:ender_chest", - "id" : 130 - }, - { - "name" : "minecraft:element_5", - "id" : -16 - }, - { - "name" : "minecraft:element_6", - "id" : -17 - }, - { - "name" : "minecraft:element_8", - "id" : -19 - }, - { - "name" : "minecraft:element_9", - "id" : -20 - }, - { - "name" : "minecraft:element_12", - "id" : -23 - }, - { - "name" : "minecraft:element_14", - "id" : -25 - }, - { - "name" : "minecraft:element_15", - "id" : -26 - }, - { - "name" : "minecraft:element_18", - "id" : -29 - }, - { - "name" : "minecraft:element_19", - "id" : -30 - }, - { - "name" : "minecraft:element_20", - "id" : -31 - }, - { - "name" : "minecraft:element_21", - "id" : -32 - }, - { - "name" : "minecraft:element_26", - "id" : -37 - }, - { - "name" : "minecraft:element_28", - "id" : -39 - }, - { - "name" : "minecraft:element_29", - "id" : -40 - }, - { - "name" : "minecraft:element_33", - "id" : -44 - }, - { - "name" : "minecraft:element_34", - "id" : -45 - }, - { - "name" : "minecraft:element_36", - "id" : -47 - }, - { - "name" : "minecraft:ice", - "id" : 79 - }, - { - "name" : "minecraft:element_37", - "id" : -48 - }, - { - "name" : "minecraft:element_39", - "id" : -50 - }, - { - "name" : "minecraft:element_40", - "id" : -51 - }, - { - "name" : "minecraft:element_41", - "id" : -52 - }, - { - "name" : "minecraft:element_45", - "id" : -56 - }, - { - "name" : "minecraft:element_46", - "id" : -57 - }, - { - "name" : "minecraft:netherite_block", - "id" : -270 - }, - { - "name" : "minecraft:element_47", - "id" : -58 - }, - { - "name" : "minecraft:element_54", - "id" : -65 - }, - { - "name" : "minecraft:element_56", - "id" : -67 + "name" : "minecraft:black_dye", + "id" : 393 }, { "name" : "minecraft:black_glazed_terracotta", "id" : 235 }, { - "name" : "minecraft:lit_redstone_ore", - "id" : 74 + "name" : "minecraft:blackstone", + "id" : -273 }, { - "name" : "minecraft:crafting_table", - "id" : 58 + "name" : "minecraft:blackstone_double_slab", + "id" : -283 }, { - "name" : "minecraft:element_57", - "id" : -68 + "name" : "minecraft:blackstone_slab", + "id" : -282 }, { - "name" : "minecraft:element_58", - "id" : -69 + "name" : "minecraft:blackstone_stairs", + "id" : -276 }, { - "name" : "minecraft:element_60", - "id" : -71 + "name" : "minecraft:blackstone_wall", + "id" : -277 }, { - "name" : "minecraft:element_64", - "id" : -75 + "name" : "minecraft:blast_furnace", + "id" : -196 }, { - "name" : "minecraft:element_65", - "id" : -76 + "name" : "minecraft:blaze_powder", + "id" : 427 }, { - "name" : "minecraft:element_67", - "id" : -78 + "name" : "minecraft:blaze_rod", + "id" : 421 }, { - "name" : "minecraft:element_70", - "id" : -81 + "name" : "minecraft:blaze_spawn_egg", + "id" : 454 }, { - "name" : "minecraft:element_72", - "id" : -83 + "name" : "minecraft:bleach", + "id" : 585 }, { - "name" : "minecraft:element_76", - "id" : -87 + "name" : "minecraft:blue_dye", + "id" : 397 }, { - "name" : "minecraft:dark_oak_button", - "id" : -142 - }, - { - "name" : "minecraft:element_77", - "id" : -88 - }, - { - "name" : "minecraft:diorite_stairs", - "id" : -170 - }, - { - "name" : "minecraft:redstone_torch", - "id" : 76 - }, - { - "name" : "minecraft:element_79", - "id" : -90 - }, - { - "name" : "minecraft:iron_bars", - "id" : 101 - }, - { - "name" : "minecraft:element_80", - "id" : -91 - }, - { - "name" : "minecraft:element_81", - "id" : -92 - }, - { - "name" : "minecraft:element_82", - "id" : -93 - }, - { - "name" : "minecraft:underwater_torch", - "id" : 239 + "name" : "minecraft:blue_glazed_terracotta", + "id" : 231 }, { "name" : "minecraft:blue_ice", "id" : -11 }, { - "name" : "minecraft:element_83", - "id" : -94 + "name" : "minecraft:boat", + "id" : 611 }, { - "name" : "minecraft:element_89", - "id" : -100 + "name" : "minecraft:bone", + "id" : 413 }, { - "name" : "minecraft:element_91", - "id" : -102 + "name" : "minecraft:bone_block", + "id" : 216 }, { - "name" : "minecraft:element_96", - "id" : -107 + "name" : "minecraft:bone_meal", + "id" : 409 }, { - "name" : "minecraft:element_97", - "id" : -108 + "name" : "minecraft:book", + "id" : 385 + }, + { + "name" : "minecraft:bookshelf", + "id" : 47 + }, + { + "name" : "minecraft:border_block", + "id" : 212 + }, + { + "name" : "minecraft:bordure_indented_banner_pattern", + "id" : 576 + }, + { + "name" : "minecraft:bow", + "id" : 300 + }, + { + "name" : "minecraft:bowl", + "id" : 321 + }, + { + "name" : "minecraft:bread", + "id" : 261 + }, + { + "name" : "minecraft:brewing_stand", + "id" : 429 + }, + { + "name" : "minecraft:brewingstandblock", + "id" : 117 + }, + { + "name" : "minecraft:brick", + "id" : 381 + }, + { + "name" : "minecraft:brick_block", + "id" : 45 + }, + { + "name" : "minecraft:brick_stairs", + "id" : 108 + }, + { + "name" : "minecraft:brown_dye", + "id" : 396 + }, + { + "name" : "minecraft:brown_glazed_terracotta", + "id" : 232 + }, + { + "name" : "minecraft:brown_mushroom", + "id" : 39 + }, + { + "name" : "minecraft:brown_mushroom_block", + "id" : 99 + }, + { + "name" : "minecraft:bubble_column", + "id" : -160 + }, + { + "name" : "minecraft:bucket", + "id" : 360 }, { "name" : "minecraft:cactus", "id" : 81 }, { - "name" : "minecraft:element_99", - "id" : -110 + "name" : "minecraft:cake", + "id" : 415 + }, + { + "name" : "minecraft:camera", + "id" : 582 + }, + { + "name" : "minecraft:campfire", + "id" : 578 + }, + { + "name" : "minecraft:carpet", + "id" : 171 + }, + { + "name" : "minecraft:carrot", + "id" : 279 + }, + { + "name" : "minecraft:carrot_on_a_stick", + "id" : 507 + }, + { + "name" : "minecraft:carrots", + "id" : 141 + }, + { + "name" : "minecraft:cartography_table", + "id" : -200 + }, + { + "name" : "minecraft:carved_pumpkin", + "id" : -155 + }, + { + "name" : "minecraft:cat_spawn_egg", + "id" : 486 + }, + { + "name" : "minecraft:cauldron", + "id" : 430 + }, + { + "name" : "minecraft:cave_spider_spawn_egg", + "id" : 455 + }, + { + "name" : "minecraft:chain", + "id" : 607 + }, + { + "name" : "minecraft:chain_command_block", + "id" : 189 + }, + { + "name" : "minecraft:chainmail_boots", + "id" : 342 + }, + { + "name" : "minecraft:chainmail_chestplate", + "id" : 340 + }, + { + "name" : "minecraft:chainmail_helmet", + "id" : 339 + }, + { + "name" : "minecraft:chainmail_leggings", + "id" : 341 + }, + { + "name" : "minecraft:charcoal", + "id" : 303 + }, + { + "name" : "minecraft:chemical_heat", + "id" : 192 + }, + { + "name" : "minecraft:chemistry_table", + "id" : 238 + }, + { + "name" : "minecraft:chest", + "id" : 54 + }, + { + "name" : "minecraft:chest_minecart", + "id" : 387 + }, + { + "name" : "minecraft:chicken", + "id" : 275 + }, + { + "name" : "minecraft:chicken_spawn_egg", + "id" : 433 + }, + { + "name" : "minecraft:chiseled_nether_bricks", + "id" : -302 + }, + { + "name" : "minecraft:chiseled_polished_blackstone", + "id" : -279 + }, + { + "name" : "minecraft:chorus_flower", + "id" : 200 + }, + { + "name" : "minecraft:chorus_fruit", + "id" : 548 + }, + { + "name" : "minecraft:chorus_plant", + "id" : 240 + }, + { + "name" : "minecraft:clay", + "id" : 82 + }, + { + "name" : "minecraft:clay_ball", + "id" : 382 + }, + { + "name" : "minecraft:clock", + "id" : 391 + }, + { + "name" : "minecraft:coal", + "id" : 302 + }, + { + "name" : "minecraft:coal_block", + "id" : 173 + }, + { + "name" : "minecraft:coal_ore", + "id" : 16 + }, + { + "name" : "minecraft:cobblestone", + "id" : 4 + }, + { + "name" : "minecraft:cobblestone_wall", + "id" : 139 + }, + { + "name" : "minecraft:cocoa", + "id" : 127 + }, + { + "name" : "minecraft:cocoa_beans", + "id" : 410 + }, + { + "name" : "minecraft:cod", + "id" : 264 + }, + { + "name" : "minecraft:cod_bucket", + "id" : 364 + }, + { + "name" : "minecraft:cod_spawn_egg", + "id" : 478 + }, + { + "name" : "minecraft:colored_torch_bp", + "id" : 204 + }, + { + "name" : "minecraft:colored_torch_rg", + "id" : 202 + }, + { + "name" : "minecraft:command_block", + "id" : 137 + }, + { + "name" : "minecraft:command_block_minecart", + "id" : 553 + }, + { + "name" : "minecraft:comparator", + "id" : 512 + }, + { + "name" : "minecraft:compass", + "id" : 389 + }, + { + "name" : "minecraft:composter", + "id" : -213 + }, + { + "name" : "minecraft:compound", + "id" : 583 + }, + { + "name" : "minecraft:concrete", + "id" : 236 + }, + { + "name" : "minecraft:concrete_powder", + "id" : 237 + }, + { + "name" : "minecraft:conduit", + "id" : -157 + }, + { + "name" : "minecraft:cooked_beef", + "id" : 274 + }, + { + "name" : "minecraft:cooked_chicken", + "id" : 276 + }, + { + "name" : "minecraft:cooked_cod", + "id" : 268 + }, + { + "name" : "minecraft:cooked_mutton", + "id" : 541 + }, + { + "name" : "minecraft:cooked_porkchop", + "id" : 263 + }, + { + "name" : "minecraft:cooked_rabbit", + "id" : 289 + }, + { + "name" : "minecraft:cooked_salmon", + "id" : 269 + }, + { + "name" : "minecraft:cookie", + "id" : 271 + }, + { + "name" : "minecraft:coral", + "id" : -131 + }, + { + "name" : "minecraft:coral_block", + "id" : -132 + }, + { + "name" : "minecraft:coral_fan", + "id" : -133 + }, + { + "name" : "minecraft:coral_fan_dead", + "id" : -134 + }, + { + "name" : "minecraft:coral_fan_hang", + "id" : -135 + }, + { + "name" : "minecraft:coral_fan_hang2", + "id" : -136 + }, + { + "name" : "minecraft:coral_fan_hang3", + "id" : -137 + }, + { + "name" : "minecraft:cow_spawn_egg", + "id" : 434 + }, + { + "name" : "minecraft:cracked_nether_bricks", + "id" : -303 + }, + { + "name" : "minecraft:cracked_polished_blackstone_bricks", + "id" : -280 + }, + { + "name" : "minecraft:crafting_table", + "id" : 58 + }, + { + "name" : "minecraft:creeper_banner_pattern", + "id" : 572 + }, + { + "name" : "minecraft:creeper_spawn_egg", + "id" : 439 + }, + { + "name" : "minecraft:crimson_button", + "id" : -260 + }, + { + "name" : "minecraft:crimson_door", + "id" : 604 + }, + { + "name" : "minecraft:crimson_double_slab", + "id" : -266 + }, + { + "name" : "minecraft:crimson_fence", + "id" : -256 + }, + { + "name" : "minecraft:crimson_fence_gate", + "id" : -258 + }, + { + "name" : "minecraft:crimson_fungus", + "id" : -228 + }, + { + "name" : "minecraft:crimson_hyphae", + "id" : -299 + }, + { + "name" : "minecraft:crimson_nylium", + "id" : -232 + }, + { + "name" : "minecraft:crimson_planks", + "id" : -242 + }, + { + "name" : "minecraft:crimson_pressure_plate", + "id" : -262 + }, + { + "name" : "minecraft:crimson_roots", + "id" : -223 + }, + { + "name" : "minecraft:crimson_sign", + "id" : 602 + }, + { + "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" : 565 + }, + { + "name" : "minecraft:crying_obsidian", + "id" : -289 + }, + { + "name" : "minecraft:cyan_dye", + "id" : 399 + }, + { + "name" : "minecraft:cyan_glazed_terracotta", + "id" : 229 + }, + { + "name" : "minecraft:dark_oak_boat", + "id" : 378 + }, + { + "name" : "minecraft:dark_oak_button", + "id" : -142 + }, + { + "name" : "minecraft:dark_oak_door", + "id" : 547 + }, + { + "name" : "minecraft:dark_oak_fence_gate", + "id" : 186 + }, + { + "name" : "minecraft:dark_oak_pressure_plate", + "id" : -152 + }, + { + "name" : "minecraft:dark_oak_sign", + "id" : 570 + }, + { + "name" : "minecraft:dark_oak_stairs", + "id" : 164 + }, + { + "name" : "minecraft:dark_oak_trapdoor", + "id" : -147 + }, + { + "name" : "minecraft:dark_prismarine_stairs", + "id" : -3 + }, + { + "name" : "minecraft:darkoak_standing_sign", + "id" : -192 + }, + { + "name" : "minecraft:darkoak_wall_sign", + "id" : -193 + }, + { + "name" : "minecraft:daylight_detector", + "id" : 151 + }, + { + "name" : "minecraft:daylight_detector_inverted", + "id" : 178 + }, + { + "name" : "minecraft:deadbush", + "id" : 32 + }, + { + "name" : "minecraft:deny", + "id" : 211 + }, + { + "name" : "minecraft:detector_rail", + "id" : 28 + }, + { + "name" : "minecraft:diamond", + "id" : 304 + }, + { + "name" : "minecraft:diamond_axe", + "id" : 319 + }, + { + "name" : "minecraft:diamond_block", + "id" : 57 + }, + { + "name" : "minecraft:diamond_boots", + "id" : 350 + }, + { + "name" : "minecraft:diamond_chestplate", + "id" : 348 + }, + { + "name" : "minecraft:diamond_helmet", + "id" : 347 + }, + { + "name" : "minecraft:diamond_hoe", + "id" : 332 + }, + { + "name" : "minecraft:diamond_horse_armor", + "id" : 523 + }, + { + "name" : "minecraft:diamond_leggings", + "id" : 349 + }, + { + "name" : "minecraft:diamond_ore", + "id" : 56 + }, + { + "name" : "minecraft:diamond_pickaxe", + "id" : 318 + }, + { + "name" : "minecraft:diamond_shovel", + "id" : 317 + }, + { + "name" : "minecraft:diamond_sword", + "id" : 316 + }, + { + "name" : "minecraft:diorite_stairs", + "id" : -170 + }, + { + "name" : "minecraft:dirt", + "id" : 3 + }, + { + "name" : "minecraft:dispenser", + "id" : 23 + }, + { + "name" : "minecraft:dolphin_spawn_egg", + "id" : 482 + }, + { + "name" : "minecraft:donkey_spawn_egg", + "id" : 463 + }, + { + "name" : "minecraft:double_plant", + "id" : 175 + }, + { + "name" : "minecraft:double_stone_slab", + "id" : 44 + }, + { + "name" : "minecraft:double_stone_slab2", + "id" : 182 + }, + { + "name" : "minecraft:double_stone_slab3", + "id" : -162 + }, + { + "name" : "minecraft:double_stone_slab4", + "id" : -166 + }, + { + "name" : "minecraft:double_wooden_slab", + "id" : 157 + }, + { + "name" : "minecraft:dragon_breath", + "id" : 550 + }, + { + "name" : "minecraft:dragon_egg", + "id" : 122 + }, + { + "name" : "minecraft:dried_kelp", + "id" : 270 + }, + { + "name" : "minecraft:dried_kelp_block", + "id" : -139 + }, + { + "name" : "minecraft:dropper", + "id" : 125 + }, + { + "name" : "minecraft:drowned_spawn_egg", + "id" : 481 + }, + { + "name" : "minecraft:dye", + "id" : 612 + }, + { + "name" : "minecraft:egg", + "id" : 388 + }, + { + "name" : "minecraft:elder_guardian_spawn_egg", + "id" : 469 + }, + { + "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", @@ -2319,10 +979,6 @@ "name" : "minecraft:element_106", "id" : -117 }, - { - "name" : "minecraft:cyan_glazed_terracotta", - "id" : 229 - }, { "name" : "minecraft:element_107", "id" : -118 @@ -2336,895 +992,2667 @@ "id" : -120 }, { - "name" : "minecraft:element_112", - "id" : -123 + "name" : "minecraft:element_11", + "id" : -22 }, { - "name" : "minecraft:warped_button", - "id" : -261 + "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:birch_stairs", - "id" : 135 - }, { "name" : "minecraft:element_114", "id" : -125 }, { - "name" : "minecraft:composter", - "id" : -213 + "name" : "minecraft:element_115", + "id" : -126 }, { - "name" : "minecraft:crying_obsidian", - "id" : -289 + "name" : "minecraft:element_116", + "id" : -127 }, { - "name" : "minecraft:end_crystal", - "id" : 426 + "name" : "minecraft:element_117", + "id" : -128 }, { - "name" : "minecraft:tripwire_hook", - "id" : 131 + "name" : "minecraft:element_118", + "id" : -129 }, { - "name" : "minecraft:blue_glazed_terracotta", - "id" : 231 + "name" : "minecraft:element_12", + "id" : -23 }, { - "name" : "minecraft:daylight_detector_inverted", - "id" : 178 + "name" : "minecraft:element_13", + "id" : -24 }, { - "name" : "minecraft:warped_trapdoor", - "id" : -247 + "name" : "minecraft:element_14", + "id" : -25 }, { - "name" : "minecraft:twisting_vines", - "id" : -287 + "name" : "minecraft:element_15", + "id" : -26 }, { - "name" : "minecraft:noteblock", - "id" : 25 + "name" : "minecraft:element_16", + "id" : -27 }, { - "name" : "minecraft:gravel", - "id" : 13 + "name" : "minecraft:element_17", + "id" : -28 }, { - "name" : "minecraft:golden_rail", - "id" : 27 + "name" : "minecraft:element_18", + "id" : -29 }, { - "name" : "minecraft:warped_wall_sign", - "id" : -253 + "name" : "minecraft:element_19", + "id" : -30 }, { - "name" : "minecraft:oak_stairs", - "id" : 53 + "name" : "minecraft:element_2", + "id" : -13 }, { - "name" : "minecraft:grass", - "id" : 2 + "name" : "minecraft:element_20", + "id" : -31 }, { - "name" : "minecraft:acacia_button", - "id" : -140 + "name" : "minecraft:element_21", + "id" : -32 }, { - "name" : "minecraft:snow", - "id" : 80 + "name" : "minecraft:element_22", + "id" : -33 }, { - "name" : "minecraft:detector_rail", - "id" : 28 + "name" : "minecraft:element_23", + "id" : -34 }, { - "name" : "minecraft:dark_oak_trapdoor", - "id" : -147 + "name" : "minecraft:element_24", + "id" : -35 }, { - "name" : "minecraft:spruce_pressure_plate", - "id" : -154 + "name" : "minecraft:element_25", + "id" : -36 }, { - "name" : "minecraft:water", - "id" : 9 + "name" : "minecraft:element_26", + "id" : -37 }, { - "name" : "minecraft:furnace", - "id" : 61 + "name" : "minecraft:element_27", + "id" : -38 }, { - "name" : "minecraft:item.wooden_door", - "id" : 64 + "name" : "minecraft:element_28", + "id" : -39 }, { - "name" : "minecraft:gold_ore", - "id" : 14 + "name" : "minecraft:element_29", + "id" : -40 }, { - "name" : "minecraft:web", - "id" : 30 + "name" : "minecraft:element_3", + "id" : -14 }, { - "name" : "minecraft:unlit_redstone_torch", - "id" : 75 + "name" : "minecraft:element_30", + "id" : -41 }, { - "name" : "minecraft:ladder", - "id" : 65 + "name" : "minecraft:element_31", + "id" : -42 }, { - "name" : "minecraft:sweet_berry_bush", - "id" : -207 + "name" : "minecraft:element_32", + "id" : -43 }, { - "name" : "minecraft:standing_sign", - "id" : 63 + "name" : "minecraft:element_33", + "id" : -44 }, { - "name" : "minecraft:glass", - "id" : 20 + "name" : "minecraft:element_34", + "id" : -45 }, { - "name" : "minecraft:lapis_ore", - "id" : 21 + "name" : "minecraft:element_35", + "id" : -46 }, { - "name" : "minecraft:bookshelf", - "id" : 47 + "name" : "minecraft:element_36", + "id" : -47 }, { - "name" : "minecraft:item.bed", - "id" : 26 + "name" : "minecraft:element_37", + "id" : -48 }, { - "name" : "minecraft:stripped_warped_hyphae", - "id" : -301 + "name" : "minecraft:element_38", + "id" : -49 }, { - "name" : "minecraft:wither_rose", - "id" : -216 + "name" : "minecraft:element_39", + "id" : -50 }, { - "name" : "minecraft:wooden_pressure_plate", - "id" : 72 + "name" : "minecraft:element_4", + "id" : -15 }, { - "name" : "minecraft:powered_comparator", - "id" : 150 + "name" : "minecraft:element_40", + "id" : -51 }, { - "name" : "minecraft:lapis_block", - "id" : 22 + "name" : "minecraft:element_41", + "id" : -52 }, { - "name" : "minecraft:dispenser", - "id" : 23 + "name" : "minecraft:element_42", + "id" : -53 }, { - "name" : "minecraft:item.wheat", - "id" : 59 + "name" : "minecraft:element_43", + "id" : -54 }, { - "name" : "minecraft:item.spruce_door", - "id" : 193 + "name" : "minecraft:element_44", + "id" : -55 }, { - "name" : "minecraft:diamond_ore", - "id" : 56 + "name" : "minecraft:element_45", + "id" : -56 }, { - "name" : "minecraft:deadbush", - "id" : 32 + "name" : "minecraft:element_46", + "id" : -57 }, { - "name" : "minecraft:pistonarmcollision", - "id" : 34 + "name" : "minecraft:element_47", + "id" : -58 }, { - "name" : "minecraft:blackstone_stairs", - "id" : -276 + "name" : "minecraft:element_48", + "id" : -59 }, { - "name" : "minecraft:dried_kelp_block", - "id" : -139 + "name" : "minecraft:element_49", + "id" : -60 }, { - "name" : "minecraft:item.soul_campfire", - "id" : -290 + "name" : "minecraft:element_5", + "id" : -16 }, { - "name" : "minecraft:green_glazed_terracotta", - "id" : 233 + "name" : "minecraft:element_50", + "id" : -61 }, { - "name" : "minecraft:crimson_pressure_plate", - "id" : -262 + "name" : "minecraft:element_51", + "id" : -62 }, { - "name" : "minecraft:spruce_fence_gate", - "id" : 183 + "name" : "minecraft:element_52", + "id" : -63 }, { - "name" : "minecraft:iron_block", - "id" : 42 + "name" : "minecraft:element_53", + "id" : -64 }, { - "name" : "minecraft:lever", - "id" : 69 + "name" : "minecraft:element_54", + "id" : -65 }, { - "name" : "minecraft:mossy_cobblestone", - "id" : 48 + "name" : "minecraft:element_55", + "id" : -66 }, { - "name" : "minecraft:torch", - "id" : 50 + "name" : "minecraft:element_56", + "id" : -67 }, { - "name" : "minecraft:acacia_fence_gate", - "id" : 187 + "name" : "minecraft:element_57", + "id" : -68 }, { - "name" : "minecraft:quartz_stairs", - "id" : 156 + "name" : "minecraft:element_58", + "id" : -69 }, { - "name" : "minecraft:dragon_egg", - "id" : 122 + "name" : "minecraft:element_59", + "id" : -70 }, { - "name" : "minecraft:lava_cauldron", - "id" : -210 + "name" : "minecraft:element_6", + "id" : -17 }, { - "name" : "minecraft:jungle_standing_sign", - "id" : -188 + "name" : "minecraft:element_60", + "id" : -71 }, { - "name" : "minecraft:redstone_wire", - "id" : 55 + "name" : "minecraft:element_61", + "id" : -72 }, { - "name" : "minecraft:jungle_wall_sign", - "id" : -189 + "name" : "minecraft:element_62", + "id" : -73 }, { - "name" : "minecraft:lit_furnace", - "id" : 62 + "name" : "minecraft:element_63", + "id" : -74 }, { - "name" : "minecraft:beehive", - "id" : -219 + "name" : "minecraft:element_64", + "id" : -75 }, { - "name" : "minecraft:crimson_wall_sign", - "id" : -252 + "name" : "minecraft:element_65", + "id" : -76 }, { - "name" : "minecraft:stone_stairs", - "id" : 67 + "name" : "minecraft:element_66", + "id" : -77 }, { - "name" : "minecraft:orange_glazed_terracotta", - "id" : 221 + "name" : "minecraft:element_67", + "id" : -78 }, { - "name" : "minecraft:brick_stairs", - "id" : 108 + "name" : "minecraft:element_68", + "id" : -79 }, { - "name" : "minecraft:wall_sign", - "id" : 68 + "name" : "minecraft:element_69", + "id" : -80 }, { - "name" : "minecraft:warped_nylium", - "id" : -233 + "name" : "minecraft:element_7", + "id" : -18 }, { - "name" : "minecraft:quartz_bricks", - "id" : -304 + "name" : "minecraft:element_70", + "id" : -81 }, { - "name" : "minecraft:item.iron_door", - "id" : 71 + "name" : "minecraft:element_71", + "id" : -82 }, { - "name" : "minecraft:redstone_ore", - "id" : 73 + "name" : "minecraft:element_72", + "id" : -83 }, { - "name" : "minecraft:lectern", - "id" : -194 + "name" : "minecraft:element_73", + "id" : -84 }, { - "name" : "minecraft:gilded_blackstone", - "id" : -281 + "name" : "minecraft:element_74", + "id" : -85 }, { - "name" : "minecraft:red_nether_brick_stairs", - "id" : -184 + "name" : "minecraft:element_75", + "id" : -86 }, { - "name" : "minecraft:basalt", - "id" : -234 + "name" : "minecraft:element_76", + "id" : -87 }, { - "name" : "minecraft:stone_button", - "id" : 77 + "name" : "minecraft:element_77", + "id" : -88 }, { - "name" : "minecraft:netherrack", - "id" : 87 + "name" : "minecraft:element_78", + "id" : -89 }, { - "name" : "minecraft:nether_brick_stairs", - "id" : 114 + "name" : "minecraft:element_79", + "id" : -90 }, { - "name" : "minecraft:item.acacia_door", - "id" : 196 + "name" : "minecraft:element_8", + "id" : -19 }, { - "name" : "minecraft:item.cake", - "id" : 92 + "name" : "minecraft:element_80", + "id" : -91 }, { - "name" : "minecraft:unpowered_repeater", - "id" : 93 + "name" : "minecraft:element_81", + "id" : -92 }, { - "name" : "minecraft:powered_repeater", - "id" : 94 + "name" : "minecraft:element_82", + "id" : -93 }, { - "name" : "minecraft:trapdoor", - "id" : 96 + "name" : "minecraft:element_83", + "id" : -94 }, { - "name" : "minecraft:coral_fan_hang3", - "id" : -137 + "name" : "minecraft:element_84", + "id" : -95 }, { - "name" : "minecraft:item.jungle_door", - "id" : 195 + "name" : "minecraft:element_85", + "id" : -96 }, { - "name" : "minecraft:glass_pane", - "id" : 102 + "name" : "minecraft:element_86", + "id" : -97 }, { - "name" : "minecraft:emerald_ore", - "id" : 129 + "name" : "minecraft:element_87", + "id" : -98 }, { - "name" : "minecraft:crimson_planks", - "id" : -242 + "name" : "minecraft:element_88", + "id" : -99 }, { - "name" : "minecraft:crimson_stem", - "id" : -225 + "name" : "minecraft:element_89", + "id" : -100 }, { - "name" : "minecraft:weeping_vines", - "id" : -231 + "name" : "minecraft:element_9", + "id" : -20 }, { - "name" : "minecraft:pumpkin_stem", - "id" : 104 + "name" : "minecraft:element_90", + "id" : -101 + }, + { + "name" : "minecraft:element_91", + "id" : -102 + }, + { + "name" : "minecraft:element_92", + "id" : -103 + }, + { + "name" : "minecraft:element_93", + "id" : -104 + }, + { + "name" : "minecraft:element_94", + "id" : -105 + }, + { + "name" : "minecraft:element_95", + "id" : -106 + }, + { + "name" : "minecraft:element_96", + "id" : -107 + }, + { + "name" : "minecraft:element_97", + "id" : -108 + }, + { + "name" : "minecraft:element_98", + "id" : -109 + }, + { + "name" : "minecraft:element_99", + "id" : -110 + }, + { + "name" : "minecraft:elytra", + "id" : 554 + }, + { + "name" : "minecraft:emerald", + "id" : 502 }, { "name" : "minecraft:emerald_block", "id" : 133 }, { - "name" : "minecraft:melon_stem", - "id" : 105 + "name" : "minecraft:emerald_ore", + "id" : 129 }, { - "name" : "minecraft:chemical_heat", - "id" : 192 + "name" : "minecraft:empty_map", + "id" : 505 }, { - "name" : "minecraft:warped_wart_block", - "id" : -227 + "name" : "minecraft:enchanted_book", + "id" : 511 }, { - "name" : "minecraft:vine", - "id" : 106 - }, - { - "name" : "minecraft:bamboo_sapling", - "id" : -164 - }, - { - "name" : "minecraft:standing_banner", - "id" : 176 - }, - { - "name" : "minecraft:mycelium", - "id" : 110 - }, - { - "name" : "minecraft:nether_gold_ore", - "id" : -288 - }, - { - "name" : "minecraft:nether_brick", - "id" : 112 - }, - { - "name" : "minecraft:warped_double_slab", - "id" : -267 - }, - { - "name" : "minecraft:nether_brick_fence", - "id" : 113 - }, - { - "name" : "minecraft:sandstone_stairs", - "id" : 128 - }, - { - "name" : "minecraft:item.nether_wart", - "id" : 115 + "name" : "minecraft:enchanted_golden_apple", + "id" : 259 }, { "name" : "minecraft:enchanting_table", "id" : 116 }, + { + "name" : "minecraft:end_brick_stairs", + "id" : -178 + }, + { + "name" : "minecraft:end_bricks", + "id" : 206 + }, + { + "name" : "minecraft:end_crystal", + "id" : 615 + }, + { + "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:redstone_lamp", - "id" : 123 + "name" : "minecraft:ender_chest", + "id" : 130 }, { - "name" : "minecraft:jungle_stairs", - "id" : 136 + "name" : "minecraft:ender_eye", + "id" : 431 }, { - "name" : "minecraft:dropper", - "id" : 125 + "name" : "minecraft:ender_pearl", + "id" : 420 }, { - "name" : "minecraft:activator_rail", - "id" : 126 + "name" : "minecraft:enderman_spawn_egg", + "id" : 440 }, { - "name" : "minecraft:cocoa", - "id" : 127 + "name" : "minecraft:endermite_spawn_egg", + "id" : 458 }, { - "name" : "minecraft:soul_torch", - "id" : -268 + "name" : "minecraft:evoker_spawn_egg", + "id" : 473 }, { - "name" : "minecraft:info_update", - "id" : 248 + "name" : "minecraft:experience_bottle", + "id" : 498 }, { - "name" : "minecraft:packed_ice", - "id" : 174 + "name" : "minecraft:farmland", + "id" : 60 }, { - "name" : "minecraft:coral_fan_hang", - "id" : -135 + "name" : "minecraft:feather", + "id" : 327 }, { - "name" : "minecraft:item.flower_pot", - "id" : 140 + "name" : "minecraft:fence", + "id" : 85 }, { - "name" : "minecraft:potatoes", - "id" : 142 + "name" : "minecraft:fence_gate", + "id" : 107 }, { - "name" : "minecraft:wooden_button", - "id" : 143 + "name" : "minecraft:fermented_spider_eye", + "id" : 426 }, { - "name" : "minecraft:item.skull", - "id" : 144 + "name" : "minecraft:field_masoned_banner_pattern", + "id" : 575 }, { - "name" : "minecraft:heavy_weighted_pressure_plate", - "id" : 148 - }, - { - "name" : "minecraft:purple_glazed_terracotta", - "id" : 219 - }, - { - "name" : "minecraft:stripped_jungle_log", - "id" : -7 - }, - { - "name" : "minecraft:hardened_clay", - "id" : 172 - }, - { - "name" : "minecraft:warped_planks", - "id" : -243 - }, - { - "name" : "minecraft:acacia_wall_sign", - "id" : -191 - }, - { - "name" : "minecraft:purpur_stairs", - "id" : 203 - }, - { - "name" : "minecraft:wall_banner", - "id" : 177 - }, - { - "name" : "minecraft:spruce_trapdoor", - "id" : -149 - }, - { - "name" : "minecraft:repeating_command_block", - "id" : 188 - }, - { - "name" : "minecraft:item.chain", - "id" : -286 - }, - { - "name" : "minecraft:item.birch_door", - "id" : 194 - }, - { - "name" : "minecraft:grass_path", - "id" : 198 - }, - { - "name" : "minecraft:blackstone", - "id" : -273 - }, - { - "name" : "minecraft:chorus_flower", - "id" : 200 - }, - { - "name" : "minecraft:normal_stone_stairs", - "id" : -180 - }, - { - "name" : "minecraft:barrier", - "id" : -161 - }, - { - "name" : "minecraft:frosted_ice", - "id" : 207 - }, - { - "name" : "minecraft:structure_block", - "id" : 252 - }, - { - "name" : "minecraft:allow", - "id" : 210 - }, - { - "name" : "minecraft:pink_glazed_terracotta", - "id" : 226 - }, - { - "name" : "minecraft:deny", - "id" : 211 - }, - { - "name" : "minecraft:border_block", - "id" : 212 - }, - { - "name" : "minecraft:movingblock", - "id" : 250 - }, - { - "name" : "minecraft:bone_block", - "id" : 216 - }, - { - "name" : "minecraft:structure_void", - "id" : 217 - }, - { - "name" : "minecraft:white_glazed_terracotta", - "id" : 220 - }, - { - "name" : "minecraft:magenta_glazed_terracotta", - "id" : 222 - }, - { - "name" : "minecraft:lime_glazed_terracotta", - "id" : 225 - }, - { - "name" : "minecraft:gray_glazed_terracotta", - "id" : 227 - }, - { - "name" : "minecraft:brown_glazed_terracotta", - "id" : 232 - }, - { - "name" : "minecraft:red_glazed_terracotta", - "id" : 234 - }, - { - "name" : "minecraft:crimson_nylium", - "id" : -232 - }, - { - "name" : "minecraft:acacia_trapdoor", - "id" : -145 - }, - { - "name" : "minecraft:smooth_sandstone_stairs", - "id" : -177 - }, - { - "name" : "minecraft:item.camera", - "id" : 242 - }, - { - "name" : "minecraft:podzol", - "id" : 243 - }, - { - "name" : "minecraft:stonecutter", - "id" : 245 - }, - { - "name" : "minecraft:netherreactor", - "id" : 247 - }, - { - "name" : "minecraft:prismarine_stairs", - "id" : -2 - }, - { - "name" : "minecraft:dark_prismarine_stairs", - "id" : -3 - }, - { - "name" : "minecraft:stripped_spruce_log", - "id" : -5 - }, - { - "name" : "minecraft:stripped_birch_log", - "id" : -6 - }, - { - "name" : "minecraft:stripped_dark_oak_log", - "id" : -9 + "name" : "minecraft:filled_map", + "id" : 418 }, { "name" : "minecraft:fire", "id" : 51 }, { - "name" : "minecraft:hard_glass", - "id" : 253 + "name" : "minecraft:fire_charge", + "id" : 499 }, { - "name" : "minecraft:acacia_standing_sign", - "id" : -190 + "name" : "minecraft:firework_rocket", + "id" : 509 + }, + { + "name" : "minecraft:firework_star", + "id" : 510 + }, + { + "name" : "minecraft:fishing_rod", + "id" : 390 + }, + { + "name" : "minecraft:fletching_table", + "id" : -201 + }, + { + "name" : "minecraft:flint", + "id" : 356 + }, + { + "name" : "minecraft:flint_and_steel", + "id" : 299 + }, + { + "name" : "minecraft:flower_banner_pattern", + "id" : 571 + }, + { + "name" : "minecraft:flower_pot", + "id" : 504 + }, + { + "name" : "minecraft:flowing_lava", + "id" : 10 + }, + { + "name" : "minecraft:flowing_water", + "id" : 8 + }, + { + "name" : "minecraft:fox_spawn_egg", + "id" : 488 + }, + { + "name" : "minecraft:frame", + "id" : 503 + }, + { + "name" : "minecraft:frosted_ice", + "id" : 207 + }, + { + "name" : "minecraft:furnace", + "id" : 61 + }, + { + "name" : "minecraft:ghast_spawn_egg", + "id" : 452 + }, + { + "name" : "minecraft:ghast_tear", + "id" : 422 + }, + { + "name" : "minecraft:gilded_blackstone", + "id" : -281 + }, + { + "name" : "minecraft:glass", + "id" : 20 + }, + { + "name" : "minecraft:glass_bottle", + "id" : 425 + }, + { + "name" : "minecraft:glass_pane", + "id" : 102 + }, + { + "name" : "minecraft:glistering_melon_slice", + "id" : 432 + }, + { + "name" : "minecraft:glow_stick", + "id" : 166 + }, + { + "name" : "minecraft:glowingobsidian", + "id" : 246 + }, + { + "name" : "minecraft:glowstone", + "id" : 89 + }, + { + "name" : "minecraft:glowstone_dust", + "id" : 392 + }, + { + "name" : "minecraft:gold_block", + "id" : 41 + }, + { + "name" : "minecraft:gold_ingot", + "id" : 306 + }, + { + "name" : "minecraft:gold_nugget", + "id" : 423 + }, + { + "name" : "minecraft:gold_ore", + "id" : 14 + }, + { + "name" : "minecraft:golden_apple", + "id" : 258 + }, + { + "name" : "minecraft:golden_axe", + "id" : 325 + }, + { + "name" : "minecraft:golden_boots", + "id" : 354 + }, + { + "name" : "minecraft:golden_carrot", + "id" : 283 + }, + { + "name" : "minecraft:golden_chestplate", + "id" : 352 + }, + { + "name" : "minecraft:golden_helmet", + "id" : 351 + }, + { + "name" : "minecraft:golden_hoe", + "id" : 333 + }, + { + "name" : "minecraft:golden_horse_armor", + "id" : 522 + }, + { + "name" : "minecraft:golden_leggings", + "id" : 353 + }, + { + "name" : "minecraft:golden_pickaxe", + "id" : 324 + }, + { + "name" : "minecraft:golden_rail", + "id" : 27 + }, + { + "name" : "minecraft:golden_shovel", + "id" : 323 + }, + { + "name" : "minecraft:golden_sword", + "id" : 322 + }, + { + "name" : "minecraft:granite_stairs", + "id" : -169 + }, + { + "name" : "minecraft:grass", + "id" : 2 + }, + { + "name" : "minecraft:grass_path", + "id" : 198 + }, + { + "name" : "minecraft:gravel", + "id" : 13 + }, + { + "name" : "minecraft:gray_dye", + "id" : 401 + }, + { + "name" : "minecraft:gray_glazed_terracotta", + "id" : 227 + }, + { + "name" : "minecraft:green_dye", + "id" : 395 + }, + { + "name" : "minecraft:green_glazed_terracotta", + "id" : 233 + }, + { + "name" : "minecraft:grindstone", + "id" : -195 + }, + { + "name" : "minecraft:guardian_spawn_egg", + "id" : 459 + }, + { + "name" : "minecraft:gunpowder", + "id" : 328 + }, + { + "name" : "minecraft:hard_glass", + "id" : 253 }, { "name" : "minecraft:hard_glass_pane", "id" : 190 }, { - "name" : "minecraft:mossy_cobblestone_stairs", - "id" : -179 + "name" : "minecraft:hard_stained_glass", + "id" : 254 }, { - "name" : "minecraft:crimson_fence_gate", - "id" : -258 + "name" : "minecraft:hard_stained_glass_pane", + "id" : 191 }, { - "name" : "minecraft:mossy_stone_brick_stairs", - "id" : -175 + "name" : "minecraft:hardened_clay", + "id" : 172 }, { - "name" : "minecraft:item.nether_sprouts", - "id" : -238 + "name" : "minecraft:hay_block", + "id" : 170 }, { - "name" : "minecraft:polished_blackstone_brick_double_slab", - "id" : -285 + "name" : "minecraft:heart_of_the_sea", + "id" : 561 }, { - "name" : "minecraft:cracked_polished_blackstone_bricks", - "id" : -280 + "name" : "minecraft:heavy_weighted_pressure_plate", + "id" : 148 }, { - "name" : "minecraft:smooth_red_sandstone_stairs", - "id" : -176 + "name" : "minecraft:hoglin_spawn_egg", + "id" : 494 }, { - "name" : "minecraft:shroomlight", - "id" : -230 + "name" : "minecraft:honey_block", + "id" : -220 }, { - "name" : "minecraft:stripped_crimson_hyphae", - "id" : -300 + "name" : "minecraft:honey_bottle", + "id" : 581 }, { - "name" : "minecraft:crimson_button", - "id" : -260 + "name" : "minecraft:honeycomb", + "id" : 580 }, { - "name" : "minecraft:soul_fire", - "id" : -237 + "name" : "minecraft:honeycomb_block", + "id" : -221 }, { - "name" : "minecraft:polished_basalt", - "id" : -235 + "name" : "minecraft:hopper", + "id" : 517 }, { - "name" : "minecraft:jungle_button", - "id" : -143 + "name" : "minecraft:hopper_minecart", + "id" : 516 }, { - "name" : "minecraft:birch_pressure_plate", - "id" : -151 + "name" : "minecraft:horse_spawn_egg", + "id" : 456 }, { - "name" : "minecraft:stripped_warped_stem", - "id" : -241 + "name" : "minecraft:husk_spawn_egg", + "id" : 461 }, { - "name" : "minecraft:birch_wall_sign", - "id" : -187 + "name" : "minecraft:ice", + "id" : 79 }, { - "name" : "minecraft:jungle_trapdoor", - "id" : -148 + "name" : "minecraft:ice_bomb", + "id" : 584 }, { - "name" : "minecraft:item.kelp", - "id" : -138 + "name" : "minecraft:info_update", + "id" : 248 }, { - "name" : "minecraft:birch_button", - "id" : -141 + "name" : "minecraft:info_update2", + "id" : 249 }, { - "name" : "minecraft:birch_trapdoor", - "id" : -146 + "name" : "minecraft:ink_sac", + "id" : 411 }, { - "name" : "minecraft:bubble_column", - "id" : -160 + "name" : "minecraft:invisiblebedrock", + "id" : 95 }, { - "name" : "minecraft:polished_granite_stairs", - "id" : -172 + "name" : "minecraft:iron_axe", + "id" : 298 }, { - "name" : "minecraft:polished_andesite_stairs", - "id" : -174 + "name" : "minecraft:iron_bars", + "id" : 101 }, { - "name" : "minecraft:end_brick_stairs", - "id" : -178 + "name" : "minecraft:iron_block", + "id" : 42 }, { - "name" : "minecraft:spruce_wall_sign", - "id" : -182 + "name" : "minecraft:iron_boots", + "id" : 346 }, { - "name" : "minecraft:chiseled_polished_blackstone", - "id" : -279 + "name" : "minecraft:iron_chestplate", + "id" : 344 }, { - "name" : "minecraft:birch_standing_sign", - "id" : -186 + "name" : "minecraft:iron_door", + "id" : 370 }, { - "name" : "minecraft:darkoak_standing_sign", - "id" : -192 + "name" : "minecraft:iron_helmet", + "id" : 343 }, { - "name" : "minecraft:darkoak_wall_sign", - "id" : -193 + "name" : "minecraft:iron_hoe", + "id" : 331 }, { - "name" : "minecraft:lit_smoker", - "id" : -199 + "name" : "minecraft:iron_horse_armor", + "id" : 521 + }, + { + "name" : "minecraft:iron_ingot", + "id" : 305 + }, + { + "name" : "minecraft:iron_leggings", + "id" : 345 + }, + { + "name" : "minecraft:iron_nugget", + "id" : 559 + }, + { + "name" : "minecraft:iron_ore", + "id" : 15 + }, + { + "name" : "minecraft:iron_pickaxe", + "id" : 297 + }, + { + "name" : "minecraft:iron_shovel", + "id" : 296 + }, + { + "name" : "minecraft:iron_sword", + "id" : 307 + }, + { + "name" : "minecraft:iron_trapdoor", + "id" : 167 + }, + { + "name" : "minecraft:item.acacia_door", + "id" : 196 + }, + { + "name" : "minecraft:item.bed", + "id" : 26 + }, + { + "name" : "minecraft:item.beetroot", + "id" : 244 + }, + { + "name" : "minecraft:item.birch_door", + "id" : 194 + }, + { + "name" : "minecraft:item.cake", + "id" : 92 + }, + { + "name" : "minecraft:item.camera", + "id" : 242 }, { "name" : "minecraft:item.campfire", "id" : -209 }, { - "name" : "minecraft:bee_nest", - "id" : -218 + "name" : "minecraft:item.cauldron", + "id" : 118 }, { - "name" : "minecraft:warped_fence_gate", - "id" : -259 + "name" : "minecraft:item.chain", + "id" : -286 }, { - "name" : "minecraft:warped_stem", - "id" : -226 + "name" : "minecraft:item.crimson_door", + "id" : -244 }, { - "name" : "minecraft:blackstone_double_slab", - "id" : -283 + "name" : "minecraft:item.dark_oak_door", + "id" : 197 }, { - "name" : "minecraft:target", - "id" : -239 + "name" : "minecraft:item.flower_pot", + "id" : 140 }, { - "name" : "minecraft:crimson_trapdoor", - "id" : -246 + "name" : "minecraft:item.frame", + "id" : 199 }, { - "name" : "minecraft:polished_blackstone_brick_wall", - "id" : -278 + "name" : "minecraft:item.hopper", + "id" : 154 }, { - "name" : "minecraft:warped_standing_sign", - "id" : -251 + "name" : "minecraft:item.iron_door", + "id" : 71 }, { - "name" : "minecraft:warped_stairs", - "id" : -255 + "name" : "minecraft:item.jungle_door", + "id" : 195 }, { - "name" : "minecraft:crimson_fence", - "id" : -256 + "name" : "minecraft:item.kelp", + "id" : -138 }, { - "name" : "minecraft:warped_pressure_plate", - "id" : -263 + "name" : "minecraft:nether_brick", + "id" : 112 }, { - "name" : "minecraft:soul_lantern", - "id" : -269 + "name" : "minecraft:item.nether_sprouts", + "id" : -238 }, { - "name" : "minecraft:blackstone_wall", - "id" : -277 + "name" : "minecraft:item.nether_wart", + "id" : 115 + }, + { + "name" : "minecraft:item.reeds", + "id" : 83 + }, + { + "name" : "minecraft:item.skull", + "id" : 144 + }, + { + "name" : "minecraft:item.soul_campfire", + "id" : -290 + }, + { + "name" : "minecraft:item.spruce_door", + "id" : 193 + }, + { + "name" : "minecraft:item.warped_door", + "id" : -245 + }, + { + "name" : "minecraft:item.wheat", + "id" : 59 + }, + { + "name" : "minecraft:item.wooden_door", + "id" : 64 + }, + { + "name" : "minecraft:jigsaw", + "id" : -211 + }, + { + "name" : "minecraft:jukebox", + "id" : 84 + }, + { + "name" : "minecraft:jungle_boat", + "id" : 375 + }, + { + "name" : "minecraft:jungle_button", + "id" : -143 + }, + { + "name" : "minecraft:jungle_door", + "id" : 545 + }, + { + "name" : "minecraft:jungle_fence_gate", + "id" : 185 + }, + { + "name" : "minecraft:jungle_pressure_plate", + "id" : -153 + }, + { + "name" : "minecraft:jungle_sign", + "id" : 568 + }, + { + "name" : "minecraft:jungle_stairs", + "id" : 136 + }, + { + "name" : "minecraft:jungle_standing_sign", + "id" : -188 + }, + { + "name" : "minecraft:jungle_trapdoor", + "id" : -148 + }, + { + "name" : "minecraft:jungle_wall_sign", + "id" : -189 + }, + { + "name" : "minecraft:kelp", + "id" : 380 + }, + { + "name" : "minecraft:ladder", + "id" : 65 + }, + { + "name" : "minecraft:lantern", + "id" : -208 + }, + { + "name" : "minecraft:lapis_block", + "id" : 22 + }, + { + "name" : "minecraft:lapis_lazuli", + "id" : 412 + }, + { + "name" : "minecraft:lapis_ore", + "id" : 21 + }, + { + "name" : "minecraft:lava", + "id" : 11 + }, + { + "name" : "minecraft:lava_bucket", + "id" : 363 + }, + { + "name" : "minecraft:lava_cauldron", + "id" : -210 + }, + { + "name" : "minecraft:lead", + "id" : 537 + }, + { + "name" : "minecraft:leather", + "id" : 379 + }, + { + "name" : "minecraft:leather_boots", + "id" : 338 + }, + { + "name" : "minecraft:leather_chestplate", + "id" : 336 + }, + { + "name" : "minecraft:leather_helmet", + "id" : 335 + }, + { + "name" : "minecraft:leather_horse_armor", + "id" : 520 + }, + { + "name" : "minecraft:leather_leggings", + "id" : 337 + }, + { + "name" : "minecraft:leaves", + "id" : 18 + }, + { + "name" : "minecraft:leaves2", + "id" : 161 + }, + { + "name" : "minecraft:lectern", + "id" : -194 + }, + { + "name" : "minecraft:lever", + "id" : 69 + }, + { + "name" : "minecraft:light_block", + "id" : -215 + }, + { + "name" : "minecraft:light_blue_dye", + "id" : 405 + }, + { + "name" : "minecraft:light_blue_glazed_terracotta", + "id" : 223 + }, + { + "name" : "minecraft:light_gray_dye", + "id" : 400 + }, + { + "name" : "minecraft:light_weighted_pressure_plate", + "id" : 147 + }, + { + "name" : "minecraft:lime_dye", + "id" : 403 + }, + { + "name" : "minecraft:lime_glazed_terracotta", + "id" : 225 + }, + { + "name" : "minecraft:lingering_potion", + "id" : 552 + }, + { + "name" : "minecraft:lit_blast_furnace", + "id" : -214 + }, + { + "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" : 471 + }, + { + "name" : "minecraft:lodestone", + "id" : -222 + }, + { + "name" : "minecraft:lodestone_compass", + "id" : 590 + }, + { + "name" : "minecraft:log", + "id" : 17 + }, + { + "name" : "minecraft:log2", + "id" : 162 + }, + { + "name" : "minecraft:loom", + "id" : -204 + }, + { + "name" : "minecraft:magenta_dye", + "id" : 406 + }, + { + "name" : "minecraft:magenta_glazed_terracotta", + "id" : 222 + }, + { + "name" : "minecraft:magma", + "id" : 213 + }, + { + "name" : "minecraft:magma_cream", + "id" : 428 + }, + { + "name" : "minecraft:magma_cube_spawn_egg", + "id" : 453 + }, + { + "name" : "minecraft:medicine", + "id" : 588 + }, + { + "name" : "minecraft:melon_block", + "id" : 103 + }, + { + "name" : "minecraft:melon_seeds", + "id" : 293 + }, + { + "name" : "minecraft:melon_slice", + "id" : 272 + }, + { + "name" : "minecraft:melon_stem", + "id" : 105 + }, + { + "name" : "minecraft:milk_bucket", + "id" : 361 + }, + { + "name" : "minecraft:minecart", + "id" : 368 + }, + { + "name" : "minecraft:mob_spawner", + "id" : 52 + }, + { + "name" : "minecraft:mojang_banner_pattern", + "id" : 574 + }, + { + "name" : "minecraft:monster_egg", + "id" : 97 + }, + { + "name" : "minecraft:mooshroom_spawn_egg", + "id" : 438 + }, + { + "name" : "minecraft:mossy_cobblestone", + "id" : 48 + }, + { + "name" : "minecraft:mossy_cobblestone_stairs", + "id" : -179 + }, + { + "name" : "minecraft:mossy_stone_brick_stairs", + "id" : -175 + }, + { + "name" : "minecraft:movingblock", + "id" : 250 + }, + { + "name" : "minecraft:mule_spawn_egg", + "id" : 464 + }, + { + "name" : "minecraft:mushroom_stew", + "id" : 260 + }, + { + "name" : "minecraft:music_disc_11", + "id" : 534 + }, + { + "name" : "minecraft:music_disc_13", + "id" : 524 + }, + { + "name" : "minecraft:music_disc_blocks", + "id" : 526 + }, + { + "name" : "minecraft:music_disc_cat", + "id" : 525 + }, + { + "name" : "minecraft:music_disc_chirp", + "id" : 527 + }, + { + "name" : "minecraft:music_disc_far", + "id" : 528 + }, + { + "name" : "minecraft:music_disc_mall", + "id" : 529 + }, + { + "name" : "minecraft:music_disc_mellohi", + "id" : 530 + }, + { + "name" : "minecraft:music_disc_pigstep", + "id" : 608 + }, + { + "name" : "minecraft:music_disc_stal", + "id" : 531 + }, + { + "name" : "minecraft:music_disc_strad", + "id" : 532 + }, + { + "name" : "minecraft:music_disc_wait", + "id" : 535 + }, + { + "name" : "minecraft:music_disc_ward", + "id" : 533 + }, + { + "name" : "minecraft:mutton", + "id" : 540 + }, + { + "name" : "minecraft:mycelium", + "id" : 110 + }, + { + "name" : "minecraft:name_tag", + "id" : 538 + }, + { + "name" : "minecraft:nautilus_shell", + "id" : 560 + }, + { + "name" : "minecraft:netherbrick", + "id" : 513 + }, + { + "name" : "minecraft:nether_brick_fence", + "id" : 113 + }, + { + "name" : "minecraft:nether_brick_stairs", + "id" : 114 + }, + { + "name" : "minecraft:nether_gold_ore", + "id" : -288 + }, + { + "name" : "minecraft:nether_sprouts", + "id" : 609 + }, + { + "name" : "minecraft:nether_star", + "id" : 508 + }, + { + "name" : "minecraft:nether_wart", + "id" : 294 + }, + { + "name" : "minecraft:nether_wart_block", + "id" : 214 + }, + { + "name" : "minecraft:netherite_axe", + "id" : 595 + }, + { + "name" : "minecraft:netherite_block", + "id" : -270 + }, + { + "name" : "minecraft:netherite_boots", + "id" : 600 + }, + { + "name" : "minecraft:netherite_chestplate", + "id" : 598 + }, + { + "name" : "minecraft:netherite_helmet", + "id" : 597 + }, + { + "name" : "minecraft:netherite_hoe", + "id" : 596 + }, + { + "name" : "minecraft:netherite_ingot", + "id" : 591 + }, + { + "name" : "minecraft:netherite_leggings", + "id" : 599 + }, + { + "name" : "minecraft:netherite_pickaxe", + "id" : 594 + }, + { + "name" : "minecraft:netherite_scrap", + "id" : 601 + }, + { + "name" : "minecraft:netherite_shovel", + "id" : 593 + }, + { + "name" : "minecraft:netherite_sword", + "id" : 592 + }, + { + "name" : "minecraft:netherrack", + "id" : 87 + }, + { + "name" : "minecraft:netherreactor", + "id" : 247 + }, + { + "name" : "minecraft:normal_stone_stairs", + "id" : -180 + }, + { + "name" : "minecraft:noteblock", + "id" : 25 + }, + { + "name" : "minecraft:npc_spawn_egg", + "id" : 468 + }, + { + "name" : "minecraft:oak_boat", + "id" : 373 + }, + { + "name" : "minecraft:oak_sign", + "id" : 358 + }, + { + "name" : "minecraft:oak_stairs", + "id" : 53 + }, + { + "name" : "minecraft:observer", + "id" : 251 + }, + { + "name" : "minecraft:obsidian", + "id" : 49 + }, + { + "name" : "minecraft:ocelot_spawn_egg", + "id" : 449 + }, + { + "name" : "minecraft:orange_dye", + "id" : 407 + }, + { + "name" : "minecraft:orange_glazed_terracotta", + "id" : 221 + }, + { + "name" : "minecraft:packed_ice", + "id" : 174 + }, + { + "name" : "minecraft:painting", + "id" : 357 + }, + { + "name" : "minecraft:panda_spawn_egg", + "id" : 487 + }, + { + "name" : "minecraft:paper", + "id" : 384 + }, + { + "name" : "minecraft:parrot_spawn_egg", + "id" : 476 + }, + { + "name" : "minecraft:phantom_membrane", + "id" : 564 + }, + { + "name" : "minecraft:phantom_spawn_egg", + "id" : 484 + }, + { + "name" : "minecraft:pig_spawn_egg", + "id" : 435 + }, + { + "name" : "minecraft:piglin_banner_pattern", + "id" : 577 + }, + { + "name" : "minecraft:piglin_brute_spawn_egg", + "id" : 497 + }, + { + "name" : "minecraft:piglin_spawn_egg", + "id" : 495 + }, + { + "name" : "minecraft:pillager_spawn_egg", + "id" : 489 + }, + { + "name" : "minecraft:pink_dye", + "id" : 402 + }, + { + "name" : "minecraft:pink_glazed_terracotta", + "id" : 226 + }, + { + "name" : "minecraft:piston", + "id" : 33 + }, + { + "name" : "minecraft:pistonarmcollision", + "id" : 34 + }, + { + "name" : "minecraft:planks", + "id" : 5 + }, + { + "name" : "minecraft:podzol", + "id" : 243 + }, + { + "name" : "minecraft:poisonous_potato", + "id" : 282 + }, + { + "name" : "minecraft:polar_bear_spawn_egg", + "id" : 470 + }, + { + "name" : "minecraft:polished_andesite_stairs", + "id" : -174 + }, + { + "name" : "minecraft:polished_basalt", + "id" : -235 }, { "name" : "minecraft:polished_blackstone", "id" : -291 }, { - "name" : "minecraft:polished_blackstone_stairs", - "id" : -292 + "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_diorite_stairs", + "id" : -173 + }, + { + "name" : "minecraft:polished_granite_stairs", + "id" : -172 + }, + { + "name" : "minecraft:popped_chorus_fruit", + "id" : 549 + }, + { + "name" : "minecraft:porkchop", + "id" : 262 + }, + { + "name" : "minecraft:portal", + "id" : 90 + }, + { + "name" : "minecraft:potato", + "id" : 280 + }, + { + "name" : "minecraft:potatoes", + "id" : 142 + }, + { + "name" : "minecraft:potion", + "id" : 424 + }, + { + "name" : "minecraft:powered_comparator", + "id" : 150 + }, + { + "name" : "minecraft:powered_repeater", + "id" : 94 + }, + { + "name" : "minecraft:prismarine", + "id" : 168 + }, + { + "name" : "minecraft:prismarine_bricks_stairs", + "id" : -4 + }, + { + "name" : "minecraft:prismarine_crystals", + "id" : 539 + }, + { + "name" : "minecraft:prismarine_shard", + "id" : 555 + }, + { + "name" : "minecraft:prismarine_stairs", + "id" : -2 + }, + { + "name" : "minecraft:pufferfish", + "id" : 267 + }, + { + "name" : "minecraft:pufferfish_bucket", + "id" : 367 + }, + { + "name" : "minecraft:pufferfish_spawn_egg", + "id" : 479 + }, + { + "name" : "minecraft:pumpkin", + "id" : 86 + }, + { + "name" : "minecraft:pumpkin_pie", + "id" : 284 + }, + { + "name" : "minecraft:pumpkin_seeds", + "id" : 292 + }, + { + "name" : "minecraft:pumpkin_stem", + "id" : 104 + }, + { + "name" : "minecraft:purple_dye", + "id" : 398 + }, + { + "name" : "minecraft:purple_glazed_terracotta", + "id" : 219 + }, + { + "name" : "minecraft:purpur_block", + "id" : 201 + }, + { + "name" : "minecraft:purpur_stairs", + "id" : 203 + }, + { + "name" : "minecraft:quartz", + "id" : 514 + }, + { + "name" : "minecraft:quartz_block", + "id" : 155 + }, + { + "name" : "minecraft:quartz_bricks", + "id" : -304 + }, + { + "name" : "minecraft:quartz_ore", + "id" : 153 + }, + { + "name" : "minecraft:quartz_stairs", + "id" : 156 + }, + { + "name" : "minecraft:rabbit", + "id" : 288 + }, + { + "name" : "minecraft:rabbit_foot", + "id" : 518 + }, + { + "name" : "minecraft:rabbit_hide", + "id" : 519 + }, + { + "name" : "minecraft:rabbit_spawn_egg", + "id" : 457 + }, + { + "name" : "minecraft:rabbit_stew", + "id" : 290 + }, + { + "name" : "minecraft:rail", + "id" : 66 + }, + { + "name" : "minecraft:rapid_fertilizer", + "id" : 586 + }, + { + "name" : "minecraft:ravager_spawn_egg", + "id" : 491 + }, + { + "name" : "minecraft:real_double_stone_slab", + "id" : 43 + }, + { + "name" : "minecraft:real_double_stone_slab2", + "id" : 181 + }, + { + "name" : "minecraft:real_double_stone_slab3", + "id" : -167 + }, + { + "name" : "minecraft:real_double_stone_slab4", + "id" : -168 + }, + { + "name" : "minecraft:red_dye", + "id" : 394 + }, + { + "name" : "minecraft:red_flower", + "id" : 38 + }, + { + "name" : "minecraft:red_glazed_terracotta", + "id" : 234 + }, + { + "name" : "minecraft:red_mushroom", + "id" : 40 + }, + { + "name" : "minecraft:red_mushroom_block", + "id" : 100 + }, + { + "name" : "minecraft:red_nether_brick", + "id" : 215 + }, + { + "name" : "minecraft:red_nether_brick_stairs", + "id" : -184 + }, + { + "name" : "minecraft:red_sandstone", + "id" : 179 + }, + { + "name" : "minecraft:red_sandstone_stairs", + "id" : 180 + }, + { + "name" : "minecraft:redstone", + "id" : 371 + }, + { + "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:repeater", + "id" : 417 + }, + { + "name" : "minecraft:repeating_command_block", + "id" : 188 + }, + { + "name" : "minecraft:reserved6", + "id" : 255 + }, + { + "name" : "minecraft:respawn_anchor", + "id" : -272 + }, + { + "name" : "minecraft:rotten_flesh", + "id" : 277 + }, + { + "name" : "minecraft:saddle", + "id" : 369 + }, + { + "name" : "minecraft:salmon", + "id" : 265 + }, + { + "name" : "minecraft:salmon_bucket", + "id" : 365 + }, + { + "name" : "minecraft:salmon_spawn_egg", + "id" : 480 + }, + { + "name" : "minecraft:sand", + "id" : 12 + }, + { + "name" : "minecraft:sandstone", + "id" : 24 + }, + { + "name" : "minecraft:sandstone_stairs", + "id" : 128 + }, + { + "name" : "minecraft:sapling", + "id" : 6 + }, + { + "name" : "minecraft:scaffolding", + "id" : -165 + }, + { + "name" : "minecraft:scute", + "id" : 562 + }, + { + "name" : "minecraft:sea_pickle", + "id" : -156 + }, + { + "name" : "minecraft:seagrass", + "id" : -130 + }, + { + "name" : "minecraft:sealantern", + "id" : 169 + }, + { + "name" : "minecraft:shears", + "id" : 419 + }, + { + "name" : "minecraft:sheep_spawn_egg", + "id" : 436 + }, + { + "name" : "minecraft:shield", + "id" : 355 + }, + { + "name" : "minecraft:shroomlight", + "id" : -230 + }, + { + "name" : "minecraft:shulker_box", + "id" : 218 + }, + { + "name" : "minecraft:shulker_shell", + "id" : 556 + }, + { + "name" : "minecraft:shulker_spawn_egg", + "id" : 467 + }, + { + "name" : "minecraft:silver_glazed_terracotta", + "id" : 228 + }, + { + "name" : "minecraft:silverfish_spawn_egg", + "id" : 441 + }, + { + "name" : "minecraft:skeleton_horse_spawn_egg", + "id" : 465 + }, + { + "name" : "minecraft:skeleton_spawn_egg", + "id" : 442 + }, + { + "name" : "minecraft:skull", + "id" : 506 + }, + { + "name" : "minecraft:skull_banner_pattern", + "id" : 573 + }, + { + "name" : "minecraft:slime", + "id" : 165 + }, + { + "name" : "minecraft:slime_ball", + "id" : 386 + }, + { + "name" : "minecraft:slime_spawn_egg", + "id" : 443 + }, + { + "name" : "minecraft:smithing_table", + "id" : -202 + }, + { + "name" : "minecraft:smoker", + "id" : -198 + }, + { + "name" : "minecraft:smooth_quartz_stairs", + "id" : -185 + }, + { + "name" : "minecraft:smooth_red_sandstone_stairs", + "id" : -176 + }, + { + "name" : "minecraft:smooth_sandstone_stairs", + "id" : -177 + }, + { + "name" : "minecraft:smooth_stone", + "id" : -183 + }, + { + "name" : "minecraft:snow", + "id" : 80 + }, + { + "name" : "minecraft:snow_layer", + "id" : 78 + }, + { + "name" : "minecraft:snowball", + "id" : 372 + }, + { + "name" : "minecraft:soul_campfire", + "id" : 610 + }, + { + "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" : 589 + }, + { + "name" : "minecraft:spawn_egg", + "id" : 614 + }, + { + "name" : "minecraft:spider_eye", + "id" : 278 + }, + { + "name" : "minecraft:spider_spawn_egg", + "id" : 444 + }, + { + "name" : "minecraft:splash_potion", + "id" : 551 + }, + { + "name" : "minecraft:sponge", + "id" : 19 + }, + { + "name" : "minecraft:spruce_boat", + "id" : 376 + }, + { + "name" : "minecraft:spruce_button", + "id" : -144 + }, + { + "name" : "minecraft:spruce_door", + "id" : 543 + }, + { + "name" : "minecraft:spruce_fence_gate", + "id" : 183 + }, + { + "name" : "minecraft:spruce_pressure_plate", + "id" : -154 + }, + { + "name" : "minecraft:spruce_sign", + "id" : 566 + }, + { + "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:squid_spawn_egg", + "id" : 448 + }, + { + "name" : "minecraft:stained_glass", + "id" : 241 + }, + { + "name" : "minecraft:stained_glass_pane", + "id" : 160 + }, + { + "name" : "minecraft:stained_hardened_clay", + "id" : 159 + }, + { + "name" : "minecraft:standing_banner", + "id" : 176 + }, + { + "name" : "minecraft:standing_sign", + "id" : 63 + }, + { + "name" : "minecraft:stick", + "id" : 320 + }, + { + "name" : "minecraft:sticky_piston", + "id" : 29 + }, + { + "name" : "minecraft:stickypistonarmcollision", + "id" : -217 + }, + { + "name" : "minecraft:stone", + "id" : 1 + }, + { + "name" : "minecraft:stone_axe", + "id" : 315 + }, + { + "name" : "minecraft:stone_brick_stairs", + "id" : 109 + }, + { + "name" : "minecraft:stone_button", + "id" : 77 + }, + { + "name" : "minecraft:stone_hoe", + "id" : 330 + }, + { + "name" : "minecraft:stone_pickaxe", + "id" : 314 + }, + { + "name" : "minecraft:stone_pressure_plate", + "id" : 70 + }, + { + "name" : "minecraft:stone_shovel", + "id" : 313 + }, + { + "name" : "minecraft:stone_stairs", + "id" : 67 + }, + { + "name" : "minecraft:stone_sword", + "id" : 312 + }, + { + "name" : "minecraft:stonebrick", + "id" : 98 + }, + { + "name" : "minecraft:stonecutter", + "id" : 245 + }, + { + "name" : "minecraft:stonecutter_block", + "id" : -197 + }, + { + "name" : "minecraft:stray_spawn_egg", + "id" : 460 + }, + { + "name" : "minecraft:strider_spawn_egg", + "id" : 493 + }, + { + "name" : "minecraft:string", + "id" : 326 + }, + { + "name" : "minecraft:stripped_acacia_log", + "id" : -8 + }, + { + "name" : "minecraft:stripped_birch_log", + "id" : -6 + }, + { + "name" : "minecraft:stripped_crimson_hyphae", + "id" : -300 + }, + { + "name" : "minecraft:stripped_crimson_stem", + "id" : -240 + }, + { + "name" : "minecraft:stripped_dark_oak_log", + "id" : -9 + }, + { + "name" : "minecraft:stripped_jungle_log", + "id" : -7 + }, + { + "name" : "minecraft:stripped_oak_log", + "id" : -10 + }, + { + "name" : "minecraft:stripped_spruce_log", + "id" : -5 + }, + { + "name" : "minecraft:stripped_warped_hyphae", + "id" : -301 + }, + { + "name" : "minecraft:stripped_warped_stem", + "id" : -241 + }, + { + "name" : "minecraft:structure_block", + "id" : 252 + }, + { + "name" : "minecraft:structure_void", + "id" : 217 + }, + { + "name" : "minecraft:sugar", + "id" : 414 + }, + { + "name" : "minecraft:sugar_cane", + "id" : 383 + }, + { + "name" : "minecraft:suspicious_stew", + "id" : 579 + }, + { + "name" : "minecraft:sweet_berries", + "id" : 287 + }, + { + "name" : "minecraft:sweet_berry_bush", + "id" : -207 + }, + { + "name" : "minecraft:tallgrass", + "id" : 31 + }, + { + "name" : "minecraft:target", + "id" : -239 + }, + { + "name" : "minecraft:tnt", + "id" : 46 + }, + { + "name" : "minecraft:tnt_minecart", + "id" : 515 + }, + { + "name" : "minecraft:torch", + "id" : 50 + }, + { + "name" : "minecraft:totem_of_undying", + "id" : 558 + }, + { + "name" : "minecraft:trapdoor", + "id" : 96 + }, + { + "name" : "minecraft:trapped_chest", + "id" : 146 + }, + { + "name" : "minecraft:trident", + "id" : 536 + }, + { + "name" : "minecraft:tripwire", + "id" : 132 + }, + { + "name" : "minecraft:tripwire_hook", + "id" : 131 + }, + { + "name" : "minecraft:tropical_fish", + "id" : 266 + }, + { + "name" : "minecraft:tropical_fish_bucket", + "id" : 366 + }, + { + "name" : "minecraft:tropical_fish_spawn_egg", + "id" : 477 + }, + { + "name" : "minecraft:turtle_egg", + "id" : -159 + }, + { + "name" : "minecraft:turtle_helmet", + "id" : 563 + }, + { + "name" : "minecraft:turtle_spawn_egg", + "id" : 483 + }, + { + "name" : "minecraft:twisting_vines", + "id" : -287 + }, + { + "name" : "minecraft:underwater_torch", + "id" : 239 + }, + { + "name" : "minecraft:undyed_shulker_box", + "id" : 205 + }, + { + "name" : "minecraft:unknown", + "id" : -305 + }, + { + "name" : "minecraft:unlit_redstone_torch", + "id" : 75 + }, + { + "name" : "minecraft:unpowered_comparator", + "id" : 149 + }, + { + "name" : "minecraft:unpowered_repeater", + "id" : 93 + }, + { + "name" : "minecraft:vex_spawn_egg", + "id" : 474 + }, + { + "name" : "minecraft:villager_spawn_egg", + "id" : 447 + }, + { + "name" : "minecraft:vindicator_spawn_egg", + "id" : 472 + }, + { + "name" : "minecraft:vine", + "id" : 106 + }, + { + "name" : "minecraft:wall_banner", + "id" : 177 + }, + { + "name" : "minecraft:wall_sign", + "id" : 68 + }, + { + "name" : "minecraft:wandering_trader_spawn_egg", + "id" : 490 + }, + { + "name" : "minecraft:warped_button", + "id" : -261 + }, + { + "name" : "minecraft:warped_door", + "id" : 605 + }, + { + "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" : 606 + }, { "name" : "minecraft:warped_hyphae", "id" : -298 }, { - "name" : "minecraft:crimson_hyphae", - "id" : -299 + "name" : "minecraft:warped_nylium", + "id" : -233 }, { - "name" : "minecraft:cracked_nether_bricks", - "id" : -303 + "name" : "minecraft:warped_planks", + "id" : -243 + }, + { + "name" : "minecraft:warped_pressure_plate", + "id" : -263 + }, + { + "name" : "minecraft:warped_roots", + "id" : -224 + }, + { + "name" : "minecraft:warped_sign", + "id" : 603 + }, + { + "name" : "minecraft:warped_slab", + "id" : -265 + }, + { + "name" : "minecraft:warped_stairs", + "id" : -255 + }, + { + "name" : "minecraft:warped_standing_sign", + "id" : -251 + }, + { + "name" : "minecraft:warped_stem", + "id" : -226 + }, + { + "name" : "minecraft:warped_trapdoor", + "id" : -247 + }, + { + "name" : "minecraft:warped_wall_sign", + "id" : -253 + }, + { + "name" : "minecraft:warped_wart_block", + "id" : -227 + }, + { + "name" : "minecraft:water", + "id" : 9 + }, + { + "name" : "minecraft:water_bucket", + "id" : 362 + }, + { + "name" : "minecraft:waterlily", + "id" : 111 + }, + { + "name" : "minecraft:web", + "id" : 30 + }, + { + "name" : "minecraft:weeping_vines", + "id" : -231 + }, + { + "name" : "minecraft:wheat", + "id" : 334 + }, + { + "name" : "minecraft:wheat_seeds", + "id" : 291 + }, + { + "name" : "minecraft:white_dye", + "id" : 408 + }, + { + "name" : "minecraft:white_glazed_terracotta", + "id" : 220 + }, + { + "name" : "minecraft:witch_spawn_egg", + "id" : 450 + }, + { + "name" : "minecraft:wither_rose", + "id" : -216 + }, + { + "name" : "minecraft:wither_skeleton_spawn_egg", + "id" : 462 + }, + { + "name" : "minecraft:wolf_spawn_egg", + "id" : 437 + }, + { + "name" : "minecraft:wood", + "id" : -212 + }, + { + "name" : "minecraft:wooden_axe", + "id" : 311 + }, + { + "name" : "minecraft:wooden_button", + "id" : 143 + }, + { + "name" : "minecraft:wooden_door", + "id" : 359 + }, + { + "name" : "minecraft:wooden_hoe", + "id" : 329 + }, + { + "name" : "minecraft:wooden_pickaxe", + "id" : 310 + }, + { + "name" : "minecraft:wooden_pressure_plate", + "id" : 72 + }, + { + "name" : "minecraft:wooden_shovel", + "id" : 309 + }, + { + "name" : "minecraft:wooden_slab", + "id" : 158 + }, + { + "name" : "minecraft:wooden_sword", + "id" : 308 + }, + { + "name" : "minecraft:wool", + "id" : 35 + }, + { + "name" : "minecraft:writable_book", + "id" : 500 + }, + { + "name" : "minecraft:written_book", + "id" : 501 + }, + { + "name" : "minecraft:yellow_dye", + "id" : 404 + }, + { + "name" : "minecraft:yellow_flower", + "id" : 37 + }, + { + "name" : "minecraft:yellow_glazed_terracotta", + "id" : 224 + }, + { + "name" : "minecraft:zoglin_spawn_egg", + "id" : 496 + }, + { + "name" : "minecraft:zombie_horse_spawn_egg", + "id" : 466 + }, + { + "name" : "minecraft:zombie_pigman_spawn_egg", + "id" : 446 + }, + { + "name" : "minecraft:zombie_spawn_egg", + "id" : 445 + }, + { + "name" : "minecraft:zombie_villager_spawn_egg", + "id" : 475 } ] \ No newline at end of file diff --git a/connector/src/main/resources/config.yml b/connector/src/main/resources/config.yml index 6be502a73..7dc9766fb 100644 --- a/connector/src/main/resources/config.yml +++ b/connector/src/main/resources/config.yml @@ -117,6 +117,11 @@ above-bedrock-nether-building: false # want to download the resource packs force-resource-packs: true +# Allows Xbox achievements to be unlocked. +# This disables certain commands so the Bedrock client can't to "cheat" to get them. +# Commands such as /gamemode and /give will not work from Bedrock with this enabled +xbox-achievements-enabled: false + # bStats is a stat tracker that is entirely anonymous and tracks only basic information # about Geyser, such as how many people are online, how many servers are using Geyser, # what OS is being used, etc. You can learn more about bStats here: https://bstats.org/. diff --git a/connector/src/main/resources/languages b/connector/src/main/resources/languages index a4125be98..bf4b0b710 160000 --- a/connector/src/main/resources/languages +++ b/connector/src/main/resources/languages @@ -1 +1 @@ -Subproject commit a4125be98fefea6cefd43dc52ccb2ade4e70573e +Subproject commit bf4b0b7103193154dd0b06e0459dc375c753069a diff --git a/connector/src/main/resources/mappings b/connector/src/main/resources/mappings index c87cc5fb0..618a9b981 160000 --- a/connector/src/main/resources/mappings +++ b/connector/src/main/resources/mappings @@ -1 +1 @@ -Subproject commit c87cc5fb05eb92beac638234906d836f9f1178ed +Subproject commit 618a9b981398647125b1b63494cb49ad93433243 diff --git a/connector/src/test/java/org/geysermc/connector/network/translators/chat/MessageTranslatorTest.java b/connector/src/test/java/org/geysermc/connector/network/translators/chat/MessageTranslatorTest.java new file mode 100644 index 000000000..5d52c79b9 --- /dev/null +++ b/connector/src/test/java/org/geysermc/connector/network/translators/chat/MessageTranslatorTest.java @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2019-2020 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.connector.network.translators.chat; + +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; + +import java.util.HashMap; +import java.util.Map; + +public class MessageTranslatorTest { + + private Map messages = new HashMap<>(); + + @Before + public void setUp() throws Exception { + messages.put("{\"text\":\"\",\"extra\":[{\"text\":\"DoctorMad9952 joined the game\",\"color\":\"yellow\"}]}", + "§eDoctorMad9952 joined the game"); + + messages.put("{\"text\":\"\",\"extra\":[\"Plugins (3): \",{\"text\":\"WorldEdit\",\"color\":\"green\"},{\"text\":\", \",\"color\":\"white\"},{\"text\":\"ViaVersion\",\"color\":\"green\"},{\"text\":\", \",\"color\":\"white\"},{\"text\":\"Geyser-Spigot\",\"color\":\"green\"}]}", + "Plugins (3): §aWorldEdit§f, §aViaVersion§f, §aGeyser-Spigot"); + + // RGB downgrade test + messages.put("{\"extra\":[{\"text\":\" \"},{\"color\":\"gold\",\"text\":\"The \"},{\"color\":\"#E14248\",\"obfuscated\":true,\"text\":\"||\"},{\"color\":\"#3AA9FF\",\"bold\":true,\"text\":\"CubeCraft\"},{\"color\":\"#E14248\",\"obfuscated\":true,\"text\":\"||\"},{\"color\":\"gold\",\"text\":\" Network \"},{\"color\":\"green\",\"text\":\"[1.8/1.9+]\\n \"},{\"color\":\"#f5e342\",\"text\":\"✦ \"},{\"color\":\"#b042f5\",\"bold\":true,\"text\":\"N\"},{\"color\":\"#c142f5\",\"bold\":true,\"text\":\"E\"},{\"color\":\"#d342f5\",\"bold\":true,\"text\":\"W\"},{\"color\":\"#e442f5\",\"bold\":true,\"text\":\":\"},{\"color\":\"#f542f5\",\"bold\":true,\"text\":\" \"},{\"color\":\"#bcf542\",\"bold\":true,\"text\":\"A\"},{\"color\":\"#acee3f\",\"bold\":true,\"text\":\"M\"},{\"color\":\"#9ce73c\",\"bold\":true,\"text\":\"O\"},{\"color\":\"#8ce039\",\"bold\":true,\"text\":\"N\"},{\"color\":\"#7cd936\",\"bold\":true,\"text\":\"G\"},{\"color\":\"#6cd233\",\"bold\":true,\"text\":\" \"},{\"color\":\"#5ccb30\",\"bold\":true,\"text\":\"S\"},{\"color\":\"#4cc42d\",\"bold\":true,\"text\":\"L\"},{\"color\":\"#3cbd2a\",\"bold\":true,\"text\":\"I\"},{\"color\":\"#2cb627\",\"bold\":true,\"text\":\"M\"},{\"color\":\"#1caf24\",\"bold\":true,\"text\":\"E\"},{\"color\":\"#0ca821\",\"bold\":true,\"text\":\"S\"},{\"color\":\"#f5e342\",\"text\":\" \"},{\"color\":\"#6d7c87\",\"text\":\"(kinda sus) \"},{\"color\":\"#f5e342\",\"text\":\"✦\"}],\"text\":\"\"}", + " §6The §c§k||§r§3§lCubeCraft§r§c§k||§r§6 Network §a[1.8/1.9+]\n" + + " §e✦ §d§lN§r§d§lE§r§d§lW§r§d§l:§r§d§l §r§e§lA§r§e§lM§r§a§lO§r§a§lN§r§a§lG§r§a§l §r§a§lS§r§a§lL§r§2§lI§r§2§lM§r§2§lE§r§2§lS§r§e §8(kinda sus) §e✦"); + } + + @Test + public void convertMessage() { + for (Map.Entry entry : messages.entrySet()) { + String bedrockMessage = MessageTranslator.convertMessage(entry.getKey(), "en_US"); + Assert.assertEquals("Translation of messages is incorrect", bedrockMessage, entry.getValue()); + } + } + + @Test + public void convertMessageLenient() { + Assert.assertEquals("All newline message is not handled properly", "\n\n\n\n", MessageTranslator.convertMessageLenient("\n\n\n\n")); + Assert.assertEquals("Empty message is not handled properly", "", MessageTranslator.convertMessageLenient("")); + Assert.assertEquals("Reset before message is not handled properly", "§r§eGame Selector", MessageTranslator.convertMessageLenient("§r§eGame Selector")); + } +} diff --git a/pom.xml b/pom.xml index 345933ac5..1b544f9ee 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ 4.0.0 org.geysermc geyser-parent - parent + 1.2.0-SNAPSHOT pom Geyser Allows for players from Minecraft Bedrock Edition to join Minecraft Java Edition servers.