diff --git a/README.md b/README.md index 21cdf945e..f15b36e28 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 joined us here! -### Currently supporting Minecraft Bedrock v1.16.100 - v1.16.220 and Minecraft Java v1.16.4 - v1.16.5. +### Currently supporting Minecraft Bedrock 1.17 and Minecraft Java 1.17. ## Setting Up Take a look [here](https://github.com/GeyserMC/Geyser/wiki#Setup) for how to set up Geyser. @@ -39,6 +39,8 @@ Take a look [here](https://github.com/GeyserMC/Geyser/wiki#Setup) for how to set - Some Entity Flags - Structure block UI +Extended height features can be "supported", but require additional work. + ## What can't be fixed There are a few things Geyser is unable to support due to various differences between Minecraft Bedrock and Java. For a list of these limitations, see the [Current Limitations](https://github.com/GeyserMC/Geyser/wiki/Current-Limitations) page. @@ -53,6 +55,7 @@ Any contributions are appreciated. Please feel free to reach out to us on [Disco you're interested in helping out with Geyser. ## Libraries Used: +- [Adventure Text Library](https://github.com/KyoriPowered/adventure) - [NukkitX Bedrock Protocol Library](https://github.com/NukkitX/Protocol) - [Steveice10's Java Protocol Library](https://github.com/Steveice10/MCProtocolLib) - [TerminalConsoleAppender](https://github.com/Minecrell/TerminalConsoleAppender) diff --git a/bootstrap/bungeecord/pom.xml b/bootstrap/bungeecord/pom.xml index 9cf8b4763..710c8176e 100644 --- a/bootstrap/bungeecord/pom.xml +++ b/bootstrap/bungeecord/pom.xml @@ -6,7 +6,7 @@ org.geysermc bootstrap-parent - 1.2.1-SNAPSHOT + 1.4.0-SNAPSHOT bootstrap-bungeecord @@ -14,7 +14,7 @@ org.geysermc connector - 1.2.1-SNAPSHOT + 1.4.0-SNAPSHOT compile diff --git a/bootstrap/bungeecord/src/main/java/org/geysermc/platform/bungeecord/GeyserBungeeConfiguration.java b/bootstrap/bungeecord/src/main/java/org/geysermc/platform/bungeecord/GeyserBungeeConfiguration.java index af246f6f1..dc29b4bde 100644 --- a/bootstrap/bungeecord/src/main/java/org/geysermc/platform/bungeecord/GeyserBungeeConfiguration.java +++ b/bootstrap/bungeecord/src/main/java/org/geysermc/platform/bungeecord/GeyserBungeeConfiguration.java @@ -41,7 +41,7 @@ public final class GeyserBungeeConfiguration extends GeyserJacksonConfiguration private Path floodgateKeyPath; public void loadFloodgate(GeyserBungeePlugin plugin) { - Plugin floodgate = plugin.getProxy().getPluginManager().getPlugin("floodgate-bungee"); + Plugin floodgate = plugin.getProxy().getPluginManager().getPlugin("floodgate"); Path geyserDataFolder = plugin.getDataFolder().toPath(); Path floodgateDataFolder = floodgate != null ? floodgate.getDataFolder().toPath() : null; 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 8d37bffb1..84db12341 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 @@ -94,10 +94,16 @@ public class GeyserBungeePlugin extends Plugin implements GeyserBootstrap { this.geyserLogger = new GeyserBungeeLogger(getLogger(), geyserConfig.isDebugMode()); GeyserConfiguration.checkGeyserConfiguration(geyserConfig, geyserLogger); - if (geyserConfig.getRemote().getAuthType().equals("floodgate") && getProxy().getPluginManager().getPlugin("floodgate-bungee") == null) { + // Remove this in like a year + if (getProxy().getPluginManager().getPlugin("floodgate-bungee") != null) { + geyserLogger.severe(LanguageUtils.getLocaleStringLog("geyser.bootstrap.floodgate.outdated", "https://ci.opencollab.dev/job/GeyserMC/job/Floodgate/job/master/")); + return; + } + + if (geyserConfig.getRemote().getAuthType().equals("floodgate") && getProxy().getPluginManager().getPlugin("floodgate") == null) { geyserLogger.severe(LanguageUtils.getLocaleStringLog("geyser.bootstrap.floodgate.not_installed") + " " + LanguageUtils.getLocaleStringLog("geyser.bootstrap.floodgate.disabling")); return; - } else if (geyserConfig.isAutoconfiguredRemote() && getProxy().getPluginManager().getPlugin("floodgate-bungee") != null) { + } else if (geyserConfig.isAutoconfiguredRemote() && getProxy().getPluginManager().getPlugin("floodgate") != null) { // Floodgate installed means that the user wants Floodgate authentication geyserLogger.debug("Auto-setting to Floodgate authentication."); geyserConfig.getRemote().setAuthType("floodgate"); diff --git a/bootstrap/pom.xml b/bootstrap/pom.xml index b06fc59cf..bf31f3fea 100644 --- a/bootstrap/pom.xml +++ b/bootstrap/pom.xml @@ -6,7 +6,7 @@ org.geysermc geyser-parent - 1.2.1-SNAPSHOT + 1.4.0-SNAPSHOT bootstrap-parent pom diff --git a/bootstrap/spigot/pom.xml b/bootstrap/spigot/pom.xml index 3dd25312c..4ac236529 100644 --- a/bootstrap/spigot/pom.xml +++ b/bootstrap/spigot/pom.xml @@ -6,15 +6,22 @@ org.geysermc bootstrap-parent - 1.2.1-SNAPSHOT + 1.4.0-SNAPSHOT bootstrap-spigot + + + viaversion-repo + https://repo.viaversion.com + + + org.geysermc connector - 1.2.1-SNAPSHOT + 1.4.0-SNAPSHOT compile @@ -24,9 +31,9 @@ provided - us.myles + com.viaversion viaversion - 3.2.0 + 4.0.0 provided diff --git a/bootstrap/spigot/src/main/java/org/geysermc/platform/spigot/GeyserSpigotConfiguration.java b/bootstrap/spigot/src/main/java/org/geysermc/platform/spigot/GeyserSpigotConfiguration.java index 2dbdbf830..e39c50bca 100644 --- a/bootstrap/spigot/src/main/java/org/geysermc/platform/spigot/GeyserSpigotConfiguration.java +++ b/bootstrap/spigot/src/main/java/org/geysermc/platform/spigot/GeyserSpigotConfiguration.java @@ -42,15 +42,10 @@ public final class GeyserSpigotConfiguration extends GeyserJacksonConfiguration private Path floodgateKeyPath; public void loadFloodgate(GeyserSpigotPlugin plugin) { - Plugin floodgate = Bukkit.getPluginManager().getPlugin("floodgate-bukkit"); + Plugin floodgate = Bukkit.getPluginManager().getPlugin("floodgate"); Path geyserDataFolder = plugin.getDataFolder().toPath(); Path floodgateDataFolder = floodgate != null ? floodgate.getDataFolder().toPath() : null; floodgateKeyPath = FloodgateKeyLoader.getKeyPath(this, floodgate, floodgateDataFolder, geyserDataFolder, plugin.getGeyserLogger()); } - - @Override - public boolean isCacheChunks() { - return true; // We override this as with Bukkit, we have direct access to the server implementation - } } 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 b45cb7000..881a9caf9 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 @@ -26,6 +26,10 @@ package org.geysermc.platform.spigot; import com.github.steveice10.mc.protocol.MinecraftConstants; +import com.viaversion.viaversion.api.Via; +import com.viaversion.viaversion.api.data.MappingData; +import com.viaversion.viaversion.api.protocol.ProtocolPathEntry; +import com.viaversion.viaversion.api.protocol.version.ProtocolVersion; import org.bukkit.Bukkit; import org.bukkit.plugin.java.JavaPlugin; import org.geysermc.common.PlatformType; @@ -46,16 +50,9 @@ import org.geysermc.platform.spigot.command.SpigotCommandSender; import org.geysermc.platform.spigot.world.GeyserSpigot1_11CraftingListener; import org.geysermc.platform.spigot.world.GeyserSpigotBlockPlaceListener; import org.geysermc.platform.spigot.world.manager.*; -import us.myles.ViaVersion.api.Pair; -import us.myles.ViaVersion.api.Via; -import us.myles.ViaVersion.api.data.MappingData; -import us.myles.ViaVersion.api.protocol.Protocol; -import us.myles.ViaVersion.api.protocol.ProtocolRegistry; -import us.myles.ViaVersion.api.protocol.ProtocolVersion; import java.io.File; import java.io.IOException; -import java.nio.file.Files; import java.nio.file.Path; import java.util.List; import java.util.UUID; @@ -81,12 +78,6 @@ public class GeyserSpigotPlugin extends JavaPlugin implements GeyserBootstrap { try { if (!getDataFolder().exists()) { getDataFolder().mkdir(); - File bukkitConfig = new File("plugins/Geyser-Bukkit/config.yml"); - if (bukkitConfig.exists()) { // Copy over old configs - getLogger().log(Level.INFO, LanguageUtils.getLocaleStringLog("geyser.bootstrap.config.copy_bukkit_config")); - Files.copy(bukkitConfig.toPath(), new File(getDataFolder().toString() + "/config.yml").toPath()); - getLogger().log(Level.INFO, LanguageUtils.getLocaleStringLog("geyser.bootstrap.config.copied_bukkit_config")); - } } File configFile = FileUtils.fileOrCopiedFromResource(new File(getDataFolder(), "config.yml"), "config.yml", (x) -> x.replaceAll("generateduuid", UUID.randomUUID().toString())); this.geyserConfig = FileUtils.loadConfig(configFile, GeyserSpigotConfiguration.class); @@ -129,11 +120,18 @@ public class GeyserSpigotPlugin extends JavaPlugin implements GeyserBootstrap { this.geyserLogger = new GeyserSpigotLogger(getLogger(), geyserConfig.isDebugMode()); GeyserConfiguration.checkGeyserConfiguration(geyserConfig, geyserLogger); - if (geyserConfig.getRemote().getAuthType().equals("floodgate") && Bukkit.getPluginManager().getPlugin("floodgate-bukkit") == null) { + // Remove this in like a year + if (Bukkit.getPluginManager().getPlugin("floodgate-bukkit") != null) { + geyserLogger.severe(LanguageUtils.getLocaleStringLog("geyser.bootstrap.floodgate.outdated", "https://ci.opencollab.dev/job/GeyserMC/job/Floodgate/job/master/")); + this.getPluginLoader().disablePlugin(this); + return; + } + + if (geyserConfig.getRemote().getAuthType().equals("floodgate") && Bukkit.getPluginManager().getPlugin("floodgate") == null) { geyserLogger.severe(LanguageUtils.getLocaleStringLog("geyser.bootstrap.floodgate.not_installed") + " " + LanguageUtils.getLocaleStringLog("geyser.bootstrap.floodgate.disabling")); this.getPluginLoader().disablePlugin(this); return; - } else if (geyserConfig.isAutoconfiguredRemote() && Bukkit.getPluginManager().getPlugin("floodgate-bukkit") != null) { + } else if (geyserConfig.isAutoconfiguredRemote() && Bukkit.getPluginManager().getPlugin("floodgate") != null) { // Floodgate installed means that the user wants Floodgate authentication geyserLogger.debug("Auto-setting to Floodgate authentication."); geyserConfig.getRemote().setAuthType("floodgate"); @@ -154,12 +152,18 @@ public class GeyserSpigotPlugin extends JavaPlugin implements GeyserBootstrap { this.geyserCommandManager = new GeyserSpigotCommandManager(this, connector); - boolean isViaVersion = (Bukkit.getPluginManager().getPlugin("ViaVersion") != null); + boolean isViaVersion = Bukkit.getPluginManager().getPlugin("ViaVersion") != null; if (isViaVersion) { - if (!isCompatible(Via.getAPI().getVersion().replace("-SNAPSHOT", ""), "3.2.0")) { + try { + // Ensure that we have the latest 4.0.0 changes and not an older ViaVersion version + Class.forName("com.viaversion.viaversion.api.ViaManager"); + } catch (ClassNotFoundException e) { geyserLogger.warning(LanguageUtils.getLocaleStringLog("geyser.bootstrap.viaversion.too_old", "https://ci.viaversion.com/job/ViaVersion/")); isViaVersion = false; + if (this.geyserConfig.isDebugMode()) { + e.printStackTrace(); + } } } // Used to determine if Block.getBlockData() is present. @@ -167,11 +171,6 @@ public class GeyserSpigotPlugin extends JavaPlugin implements GeyserBootstrap { if (isLegacy) geyserLogger.debug("Legacy version of Minecraft (1.12.2 or older) detected; falling back to ViaVersion for block state retrieval."); - boolean use3dBiomes = isCompatible(Bukkit.getServer().getVersion(), "1.16.0"); - if (!use3dBiomes) { - geyserLogger.debug("Legacy version of Minecraft (1.15.2 or older) detected; not using 3D biomes."); - } - boolean isPre1_12 = !isCompatible(Bukkit.getServer().getVersion(), "1.12.0"); // Set if we need to use a different method for getting a player's locale SpigotCommandSender.setUseLegacyLocaleMethod(isPre1_12); @@ -187,11 +186,11 @@ public class GeyserSpigotPlugin extends JavaPlugin implements GeyserBootstrap { this.geyserWorldManager = new GeyserSpigot1_12NativeWorldManager(this); } else { // Post-1.13 - this.geyserWorldManager = new GeyserSpigotLegacyNativeWorldManager(this, use3dBiomes); + this.geyserWorldManager = new GeyserSpigotLegacyNativeWorldManager(this); } } else { // No ViaVersion - this.geyserWorldManager = new GeyserSpigotNativeWorldManager(this, use3dBiomes); + this.geyserWorldManager = new GeyserSpigotNativeWorldManager(this); } geyserLogger.debug("Using NMS adapter: " + this.geyserWorldManager.getClass() + ", " + nmsVersion); } catch (Exception e) { @@ -213,7 +212,7 @@ public class GeyserSpigotPlugin extends JavaPlugin implements GeyserBootstrap { this.geyserWorldManager = new GeyserSpigotFallbackWorldManager(this); } else { // Post-1.13 - this.geyserWorldManager = new GeyserSpigotWorldManager(this, use3dBiomes); + this.geyserWorldManager = new GeyserSpigotWorldManager(this); } geyserLogger.debug("Using default world manager: " + this.geyserWorldManager.getClass()); } @@ -323,14 +322,14 @@ public class GeyserSpigotPlugin extends JavaPlugin implements GeyserBootstrap { */ private boolean isViaVersionNeeded() { ProtocolVersion serverVersion = getServerProtocolVersion(); - List> protocolList = ProtocolRegistry.getProtocolPath(MinecraftConstants.PROTOCOL_VERSION, + List protocolList = Via.getManager().getProtocolManager().getProtocolPath(MinecraftConstants.PROTOCOL_VERSION, serverVersion.getVersion()); if (protocolList == null) { // No translation needed! return false; } for (int i = protocolList.size() - 1; i >= 0; i--) { - MappingData mappingData = protocolList.get(i).getValue().getMappingData(); + MappingData mappingData = protocolList.get(i).getProtocol().getMappingData(); if (mappingData != null) { return true; } diff --git a/bootstrap/spigot/src/main/java/org/geysermc/platform/spigot/world/GeyserSpigot1_11CraftingListener.java b/bootstrap/spigot/src/main/java/org/geysermc/platform/spigot/world/GeyserSpigot1_11CraftingListener.java index 2ee6457ac..eff79c7cc 100644 --- a/bootstrap/spigot/src/main/java/org/geysermc/platform/spigot/world/GeyserSpigot1_11CraftingListener.java +++ b/bootstrap/spigot/src/main/java/org/geysermc/platform/spigot/world/GeyserSpigot1_11CraftingListener.java @@ -34,6 +34,12 @@ import com.github.steveice10.mc.protocol.data.game.recipe.data.ShapelessRecipeDa import com.nukkitx.protocol.bedrock.data.inventory.CraftingData; import com.nukkitx.protocol.bedrock.data.inventory.ItemData; import com.nukkitx.protocol.bedrock.packet.CraftingDataPacket; +import com.viaversion.viaversion.api.Via; +import com.viaversion.viaversion.api.data.MappingData; +import com.viaversion.viaversion.api.protocol.ProtocolPathEntry; +import com.viaversion.viaversion.api.protocol.version.ProtocolVersion; +import com.viaversion.viaversion.protocols.protocol1_13to1_12_2.Protocol1_13To1_12_2; +import com.viaversion.viaversion.util.Pair; import org.bukkit.Bukkit; import org.bukkit.event.EventHandler; import org.bukkit.event.Listener; @@ -45,12 +51,6 @@ import org.geysermc.connector.GeyserConnector; import org.geysermc.connector.network.session.GeyserSession; import org.geysermc.connector.network.translators.item.ItemTranslator; import org.geysermc.connector.network.translators.item.RecipeRegistry; -import us.myles.ViaVersion.api.Pair; -import us.myles.ViaVersion.api.data.MappingData; -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 java.util.*; @@ -68,12 +68,12 @@ public class GeyserSpigot1_11CraftingListener implements Listener { /** * The list of all protocols from the client's version to 1.13. */ - private final List> protocolList; + private final List protocolList; public GeyserSpigot1_11CraftingListener(GeyserConnector connector) { this.connector = connector; - this.mappingData1_12to1_13 = ProtocolRegistry.getProtocol(Protocol1_13To1_12_2.class).getMappingData(); - this.protocolList = ProtocolRegistry.getProtocolPath(MinecraftConstants.PROTOCOL_VERSION, + this.mappingData1_12to1_13 = Via.getManager().getProtocolManager().getProtocol(Protocol1_13To1_12_2.class).getMappingData(); + this.protocolList = Via.getManager().getProtocolManager().getProtocolPath(MinecraftConstants.PROTOCOL_VERSION, ProtocolVersion.v1_13.getVersion()); } @@ -187,7 +187,7 @@ public class GeyserSpigot1_11CraftingListener implements Listener { } for (int i = protocolList.size() - 1; i >= 0; i--) { - MappingData mappingData = protocolList.get(i).getValue().getMappingData(); + MappingData mappingData = protocolList.get(i).getProtocol().getMappingData(); if (mappingData != null) { itemId = mappingData.getNewItemId(itemId); } diff --git a/bootstrap/spigot/src/main/java/org/geysermc/platform/spigot/world/manager/GeyserSpigot1_12NativeWorldManager.java b/bootstrap/spigot/src/main/java/org/geysermc/platform/spigot/world/manager/GeyserSpigot1_12NativeWorldManager.java index 02347f5de..c59cb0d77 100644 --- a/bootstrap/spigot/src/main/java/org/geysermc/platform/spigot/world/manager/GeyserSpigot1_12NativeWorldManager.java +++ b/bootstrap/spigot/src/main/java/org/geysermc/platform/spigot/world/manager/GeyserSpigot1_12NativeWorldManager.java @@ -25,6 +25,8 @@ package org.geysermc.platform.spigot.world.manager; +import com.viaversion.viaversion.api.Via; +import com.viaversion.viaversion.protocols.protocol1_13to1_12_2.storage.BlockStorage; import org.bukkit.Bukkit; import org.bukkit.entity.Player; import org.bukkit.plugin.Plugin; @@ -32,8 +34,6 @@ import org.geysermc.connector.network.session.GeyserSession; import org.geysermc.connector.network.translators.world.block.BlockTranslator; import org.geysermc.geyser.adapters.spigot.SpigotAdapters; import org.geysermc.geyser.adapters.spigot.SpigotWorldAdapter; -import us.myles.ViaVersion.api.Via; -import us.myles.ViaVersion.protocols.protocol1_13to1_12_2.storage.BlockStorage; /** * Used with ViaVersion and pre-1.13. @@ -54,7 +54,7 @@ public class GeyserSpigot1_12NativeWorldManager extends GeyserSpigot1_12WorldMan return BlockTranslator.JAVA_AIR_ID; } // Get block entity storage - BlockStorage storage = Via.getManager().getConnection(player.getUniqueId()).get(BlockStorage.class); + BlockStorage storage = Via.getManager().getConnectionManager().getConnectedClient(player.getUniqueId()).get(BlockStorage.class); int blockId = adapter.getBlockAt(player.getWorld(), x, y, z); return getLegacyBlock(storage, blockId, x, y, z); } diff --git a/bootstrap/spigot/src/main/java/org/geysermc/platform/spigot/world/manager/GeyserSpigot1_12WorldManager.java b/bootstrap/spigot/src/main/java/org/geysermc/platform/spigot/world/manager/GeyserSpigot1_12WorldManager.java index a28eef5b4..dadab7c46 100644 --- a/bootstrap/spigot/src/main/java/org/geysermc/platform/spigot/world/manager/GeyserSpigot1_12WorldManager.java +++ b/bootstrap/spigot/src/main/java/org/geysermc/platform/spigot/world/manager/GeyserSpigot1_12WorldManager.java @@ -25,23 +25,19 @@ package org.geysermc.platform.spigot.world.manager; -import com.github.steveice10.mc.protocol.data.game.chunk.Chunk; +import com.viaversion.viaversion.api.Via; +import com.viaversion.viaversion.api.data.MappingData; +import com.viaversion.viaversion.api.minecraft.Position; +import com.viaversion.viaversion.api.protocol.ProtocolPathEntry; +import com.viaversion.viaversion.api.protocol.version.ProtocolVersion; +import com.viaversion.viaversion.protocols.protocol1_13to1_12_2.Protocol1_13To1_12_2; +import com.viaversion.viaversion.protocols.protocol1_13to1_12_2.storage.BlockStorage; import org.bukkit.Bukkit; -import org.bukkit.World; import org.bukkit.block.Block; import org.bukkit.entity.Player; import org.bukkit.plugin.Plugin; import org.geysermc.connector.network.session.GeyserSession; import org.geysermc.connector.network.translators.world.block.BlockTranslator; -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.util.List; @@ -60,12 +56,12 @@ public class GeyserSpigot1_12WorldManager extends GeyserSpigotWorldManager { /** * The list of all protocols from the client's version to 1.13. */ - private final List> protocolList; + private final List protocolList; public GeyserSpigot1_12WorldManager(Plugin plugin) { - super(plugin, false); - this.mappingData1_12to1_13 = ProtocolRegistry.getProtocol(Protocol1_13To1_12_2.class).getMappingData(); - this.protocolList = ProtocolRegistry.getProtocolPath(CLIENT_PROTOCOL_VERSION, + super(plugin); + this.mappingData1_12to1_13 = Via.getManager().getProtocolManager().getProtocol(Protocol1_13To1_12_2.class).getMappingData(); + this.protocolList = Via.getManager().getProtocolManager().getProtocolPath(CLIENT_PROTOCOL_VERSION, ProtocolVersion.v1_13.getVersion()); } @@ -81,7 +77,7 @@ public class GeyserSpigot1_12WorldManager extends GeyserSpigotWorldManager { return BlockTranslator.JAVA_AIR_ID; } // Get block entity storage - BlockStorage storage = Via.getManager().getConnection(player.getUniqueId()).get(BlockStorage.class); + BlockStorage storage = Via.getManager().getConnectionManager().getConnectedClient(player.getUniqueId()).get(BlockStorage.class); Block block = player.getWorld().getBlockAt(x, y, z); // Black magic that gets the old block state ID int blockId = (block.getType().getId() << 4) | (block.getData() & 0xF); @@ -109,7 +105,7 @@ public class GeyserSpigot1_12WorldManager extends GeyserSpigotWorldManager { } } for (int i = protocolList.size() - 1; i >= 0; i--) { - MappingData mappingData = protocolList.get(i).getValue().getMappingData(); + MappingData mappingData = protocolList.get(i).getProtocol().getMappingData(); if (mappingData != null) { blockId = mappingData.getNewBlockStateId(blockId); } @@ -117,28 +113,6 @@ public class GeyserSpigot1_12WorldManager extends GeyserSpigotWorldManager { return blockId; } - @SuppressWarnings("deprecation") - @Override - public void getBlocksInSection(GeyserSession session, int x, int y, int z, Chunk chunk) { - Player player = Bukkit.getPlayer(session.getPlayerEntity().getUsername()); - if (player == null) { - return; - } - World world = player.getWorld(); - // Get block entity storage - BlockStorage storage = Via.getManager().getConnection(player.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++) { - Block block = world.getBlockAt((x << 4) + blockX, (y << 4) + blockY, (z << 4) + blockZ); - // Black magic that gets the old block state ID - int blockId = (block.getType().getId() << 4) | (block.getData() & 0xF); - chunk.set(blockX, blockY, blockZ, getLegacyBlock(storage, blockId, (x << 4) + blockX, (y << 4) + blockY, (z << 4) + blockZ)); - } - } - } - } - @Override public boolean isLegacy() { return true; diff --git a/bootstrap/spigot/src/main/java/org/geysermc/platform/spigot/world/manager/GeyserSpigotFallbackWorldManager.java b/bootstrap/spigot/src/main/java/org/geysermc/platform/spigot/world/manager/GeyserSpigotFallbackWorldManager.java index a9de94db5..8bd2c6628 100644 --- a/bootstrap/spigot/src/main/java/org/geysermc/platform/spigot/world/manager/GeyserSpigotFallbackWorldManager.java +++ b/bootstrap/spigot/src/main/java/org/geysermc/platform/spigot/world/manager/GeyserSpigotFallbackWorldManager.java @@ -25,7 +25,6 @@ package org.geysermc.platform.spigot.world.manager; -import com.github.steveice10.mc.protocol.data.game.chunk.Chunk; import org.bukkit.plugin.Plugin; import org.geysermc.connector.network.session.GeyserSession; import org.geysermc.connector.network.translators.world.block.BlockTranslator; @@ -37,8 +36,7 @@ import org.geysermc.connector.network.translators.world.block.BlockTranslator; */ public class GeyserSpigotFallbackWorldManager extends GeyserSpigotWorldManager { public GeyserSpigotFallbackWorldManager(Plugin plugin) { - // Since this is pre-1.13 (and thus pre-1.15), there will never be 3D biomes. - super(plugin, false); + super(plugin); } @Override @@ -46,11 +44,6 @@ public class GeyserSpigotFallbackWorldManager extends GeyserSpigotWorldManager { return BlockTranslator.JAVA_AIR_ID; } - @Override - public void getBlocksInSection(GeyserSession session, int x, int y, int z, Chunk chunk) { - // Do nothing, since we can't do anything with the chunk - } - @Override public boolean hasOwnChunkCache() { return false; diff --git a/bootstrap/spigot/src/main/java/org/geysermc/platform/spigot/world/manager/GeyserSpigotLegacyNativeWorldManager.java b/bootstrap/spigot/src/main/java/org/geysermc/platform/spigot/world/manager/GeyserSpigotLegacyNativeWorldManager.java index 8f407de0a..253caf670 100644 --- a/bootstrap/spigot/src/main/java/org/geysermc/platform/spigot/world/manager/GeyserSpigotLegacyNativeWorldManager.java +++ b/bootstrap/spigot/src/main/java/org/geysermc/platform/spigot/world/manager/GeyserSpigotLegacyNativeWorldManager.java @@ -26,16 +26,15 @@ package org.geysermc.platform.spigot.world.manager; import com.github.steveice10.mc.protocol.MinecraftConstants; +import com.viaversion.viaversion.api.Via; +import com.viaversion.viaversion.api.data.MappingData; +import com.viaversion.viaversion.api.protocol.ProtocolPathEntry; +import com.viaversion.viaversion.api.protocol.version.ProtocolVersion; import it.unimi.dsi.fastutil.ints.Int2IntMap; import it.unimi.dsi.fastutil.ints.Int2IntOpenHashMap; import it.unimi.dsi.fastutil.ints.IntList; import org.geysermc.connector.network.session.GeyserSession; import org.geysermc.platform.spigot.GeyserSpigotPlugin; -import us.myles.ViaVersion.api.Pair; -import us.myles.ViaVersion.api.data.MappingData; -import us.myles.ViaVersion.api.protocol.Protocol; -import us.myles.ViaVersion.api.protocol.ProtocolRegistry; -import us.myles.ViaVersion.api.protocol.ProtocolVersion; import java.util.List; @@ -46,18 +45,18 @@ public class GeyserSpigotLegacyNativeWorldManager extends GeyserSpigotNativeWorl private final Int2IntMap oldToNewBlockId; - public GeyserSpigotLegacyNativeWorldManager(GeyserSpigotPlugin plugin, boolean use3dBiomes) { - super(plugin, use3dBiomes); + public GeyserSpigotLegacyNativeWorldManager(GeyserSpigotPlugin plugin) { + super(plugin); IntList allBlockStates = adapter.getAllBlockStates(); oldToNewBlockId = new Int2IntOpenHashMap(allBlockStates.size()); ProtocolVersion serverVersion = plugin.getServerProtocolVersion(); - List> protocolList = ProtocolRegistry.getProtocolPath(MinecraftConstants.PROTOCOL_VERSION, + List protocolList = Via.getManager().getProtocolManager().getProtocolPath(MinecraftConstants.PROTOCOL_VERSION, serverVersion.getVersion()); for (int oldBlockId : allBlockStates) { int newBlockId = oldBlockId; // protocolList should *not* be null; we checked for that before initializing this class for (int i = protocolList.size() - 1; i >= 0; i--) { - MappingData mappingData = protocolList.get(i).getValue().getMappingData(); + MappingData mappingData = protocolList.get(i).getProtocol().getMappingData(); if (mappingData != null) { newBlockId = mappingData.getNewBlockStateId(newBlockId); } diff --git a/bootstrap/spigot/src/main/java/org/geysermc/platform/spigot/world/manager/GeyserSpigotNativeWorldManager.java b/bootstrap/spigot/src/main/java/org/geysermc/platform/spigot/world/manager/GeyserSpigotNativeWorldManager.java index cc9d5bddc..7e0b9267b 100644 --- a/bootstrap/spigot/src/main/java/org/geysermc/platform/spigot/world/manager/GeyserSpigotNativeWorldManager.java +++ b/bootstrap/spigot/src/main/java/org/geysermc/platform/spigot/world/manager/GeyserSpigotNativeWorldManager.java @@ -36,8 +36,8 @@ import org.geysermc.geyser.adapters.spigot.SpigotWorldAdapter; public class GeyserSpigotNativeWorldManager extends GeyserSpigotWorldManager { protected final SpigotWorldAdapter adapter; - public GeyserSpigotNativeWorldManager(Plugin plugin, boolean use3dBiomes) { - super(plugin, use3dBiomes); + public GeyserSpigotNativeWorldManager(Plugin plugin) { + super(plugin); adapter = SpigotAdapters.getWorldAdapter(); } diff --git a/bootstrap/spigot/src/main/java/org/geysermc/platform/spigot/world/manager/GeyserSpigotWorldManager.java b/bootstrap/spigot/src/main/java/org/geysermc/platform/spigot/world/manager/GeyserSpigotWorldManager.java index ba61eeb72..41e2fd801 100644 --- a/bootstrap/spigot/src/main/java/org/geysermc/platform/spigot/world/manager/GeyserSpigotWorldManager.java +++ b/bootstrap/spigot/src/main/java/org/geysermc/platform/spigot/world/manager/GeyserSpigotWorldManager.java @@ -25,18 +25,13 @@ package org.geysermc.platform.spigot.world.manager; -import com.fasterxml.jackson.databind.JsonNode; import com.github.steveice10.mc.protocol.MinecraftConstants; -import com.github.steveice10.mc.protocol.data.game.chunk.Chunk; import com.nukkitx.math.vector.Vector3i; import com.nukkitx.nbt.NbtMap; import com.nukkitx.nbt.NbtMapBuilder; import com.nukkitx.nbt.NbtType; -import it.unimi.dsi.fastutil.ints.Int2IntMap; -import it.unimi.dsi.fastutil.ints.Int2IntOpenHashMap; import org.bukkit.Bukkit; import org.bukkit.World; -import org.bukkit.block.Biome; import org.bukkit.block.Block; import org.bukkit.block.Lectern; import org.bukkit.block.data.BlockData; @@ -44,17 +39,13 @@ import org.bukkit.entity.Player; import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.meta.BookMeta; import org.bukkit.plugin.Plugin; -import org.geysermc.connector.GeyserConnector; import org.geysermc.connector.network.session.GeyserSession; import org.geysermc.connector.network.translators.inventory.translators.LecternInventoryTranslator; import org.geysermc.connector.network.translators.world.GeyserWorldManager; import org.geysermc.connector.network.translators.world.block.BlockTranslator; import org.geysermc.connector.utils.BlockEntityUtils; -import org.geysermc.connector.utils.FileUtils; import org.geysermc.connector.utils.GameRule; -import org.geysermc.connector.utils.LanguageUtils; -import java.io.InputStream; import java.util.ArrayList; import java.util.List; @@ -67,48 +58,10 @@ public class GeyserSpigotWorldManager extends GeyserWorldManager { */ protected static final int CLIENT_PROTOCOL_VERSION = MinecraftConstants.PROTOCOL_VERSION; - /** - * Whether the server is pre-1.16 and therefore does not support 3D biomes on an API level guaranteed. - */ - private final boolean use3dBiomes; - /** - * Stores a list of {@link Biome} ordinal numbers to Minecraft biome numeric IDs. - * - * Working with the Biome enum in Spigot poses two problems: - * 1: The Biome enum values change in both order and names over the years. - * 2: There is no way to get the Minecraft biome ID from the name itself with Spigot. - * To solve both of these problems, we store a JSON file of every Biome enum that has existed, - * along with its 1.16 biome number. - * - * The key is the Spigot Biome ordinal; the value is the Minecraft Java biome numerical ID - */ - private final Int2IntMap biomeToIdMap = new Int2IntOpenHashMap(Biome.values().length); - private final Plugin plugin; - public GeyserSpigotWorldManager(Plugin plugin, boolean use3dBiomes) { - this.use3dBiomes = use3dBiomes; + public GeyserSpigotWorldManager(Plugin plugin) { this.plugin = plugin; - - // Load the values into the biome-to-ID map - InputStream biomeStream = FileUtils.getResource("biomes.json"); - JsonNode biomes; - try { - biomes = GeyserConnector.JSON_MAPPER.readTree(biomeStream); - } catch (Exception e) { - throw new AssertionError(LanguageUtils.getLocaleStringLog("geyser.toolbox.fail.runtime_java"), e); - } - // Only load in the biomes that are present in this version of Minecraft - for (Biome enumBiome : Biome.values()) { - 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"); - biomeToIdMap.put(enumBiome.ordinal(), 0); - } - } } @Override @@ -121,64 +74,11 @@ public class GeyserSpigotWorldManager extends GeyserWorldManager { return BlockTranslator.getJavaIdBlockMap().getOrDefault(world.getBlockAt(x, y, z).getBlockData().getAsString(), BlockTranslator.JAVA_AIR_ID); } - @Override - public void getBlocksInSection(GeyserSession session, int x, int y, int z, Chunk chunk) { - Player bukkitPlayer; - if ((bukkitPlayer = Bukkit.getPlayer(session.getPlayerEntity().getUsername())) == null) { - return; - } - World world = bukkitPlayer.getWorld(); - 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++) { - Block block = world.getBlockAt((x << 4) + blockX, (y << 4) + blockY, (z << 4) + blockZ); - int id = BlockTranslator.getJavaIdBlockMap().getOrDefault(block.getBlockData().getAsString(), BlockTranslator.JAVA_AIR_ID); - chunk.set(blockX, blockY, blockZ, id); - } - } - } - } - @Override public boolean hasOwnChunkCache() { return true; } - @Override - @SuppressWarnings("deprecation") - public int[] getBiomeDataAt(GeyserSession session, int x, int z) { - int[] biomeData = new int[1024]; - World world = Bukkit.getPlayer(session.getPlayerEntity().getUsername()).getWorld(); - int chunkX = x << 4; - int chunkZ = z << 4; - int chunkXmax = chunkX + 16; - int chunkZmax = chunkZ + 16; - // 3D biomes didn't exist until 1.15 - if (use3dBiomes) { - for (int localX = chunkX; localX < chunkXmax; localX += 4) { - for (int localY = 0; localY < 255; localY += + 4) { - for (int localZ = chunkZ; localZ < chunkZmax; localZ += 4) { - // Index is based on wiki.vg's index requirements - final int i = ((localY >> 2) & 63) << 4 | ((localZ >> 2) & 3) << 2 | ((localX >> 2) & 3); - biomeData[i] = biomeToIdMap.getOrDefault(world.getBiome(localX, localY, localZ).ordinal(), 0); - } - } - } - } else { - // Looks like the same code, but we're not checking the Y coordinate here - for (int localX = chunkX; localX < chunkXmax; localX += 4) { - for (int localY = 0; localY < 255; localY += + 4) { - for (int localZ = chunkZ; localZ < chunkZmax; localZ += 4) { - // Index is based on wiki.vg's index requirements - final int i = ((localY >> 2) & 63) << 4 | ((localZ >> 2) & 3) << 2 | ((localX >> 2) & 3); - biomeData[i] = biomeToIdMap.getOrDefault(world.getBiome(localX, localZ).ordinal(), 0); - } - } - } - } - return biomeData; - } - @Override public NbtMap getLecternDataAt(GeyserSession session, int x, int y, int z, boolean isChunkLoad) { // Run as a task to prevent async issues diff --git a/bootstrap/spigot/src/main/resources/biomes.json b/bootstrap/spigot/src/main/resources/biomes.json deleted file mode 100644 index 56520e914..000000000 --- a/bootstrap/spigot/src/main/resources/biomes.json +++ /dev/null @@ -1,155 +0,0 @@ -{ - "MUTATED_ICE_FLATS" : 140, - "MUTATED_TAIGA" : 133, - "SAVANNA_PLATEAU_MOUNTAINS" : 164, - "DEEP_WARM_OCEAN" : 47, - "REDWOOD_TAIGA_HILLS" : 33, - "THE_VOID" : 127, - "COLD_TAIGA_MOUNTAINS" : 158, - "BAMBOO_JUNGLE_HILLS" : 169, - "MOUNTAINS" : 3, - "MESA_PLATEAU" : 39, - "SNOWY_TAIGA_HILLS" : 31, - "DEEP_FROZEN_OCEAN" : 50, - "EXTREME_HILLS" : 3, - "BIRCH_FOREST_MOUNTAINS" : 155, - "FOREST" : 4, - "BIRCH_FOREST" : 27, - "SNOWY_TUNDRA" : 12, - "ICE_SPIKES" : 140, - "FROZEN_OCEAN" : 10, - "WARPED_FOREST" : 172, - "WOODED_BADLANDS_PLATEAU" : 38, - "BADLANDS_PLATEAU" : 39, - "ICE_PLAINS_SPIKES" : 140, - "MEGA_TAIGA" : 32, - "MUTATED_SAVANNA_ROCK" : 164, - "SAVANNA_PLATEAU" : 36, - "DARK_FOREST_HILLS" : 157, - "END_MIDLANDS" : 41, - "SHATTERED_SAVANNA_PLATEAU" : 164, - "SAVANNA" : 35, - "MUSHROOM_ISLAND_SHORE" : 15, - "SWAMP" : 6, - "ICE_MOUNTAINS" : 13, - "BEACH" : 16, - "MUTATED_MESA_CLEAR_ROCK" : 167, - "END_HIGHLANDS" : 42, - "COLD_BEACH" : 26, - "JUNGLE" : 21, - "MUTATED_TAIGA_COLD" : 158, - "TALL_BIRCH_HILLS" : 156, - "DARK_FOREST" : 29, - "WOODED_HILLS" : 18, - "HELL" : 8, - "MUTATED_REDWOOD_TAIGA" : 160, - "MESA_PLATEAU_FOREST" : 38, - "MUSHROOM_ISLAND" : 14, - "BADLANDS" : 37, - "END_BARRENS" : 43, - "MUTATED_EXTREME_HILLS_WITH_TREES" : 162, - "MUTATED_JUNGLE_EDGE" : 151, - "MODIFIED_BADLANDS_PLATEAU" : 167, - "ROOFED_FOREST_MOUNTAINS" : 157, - "SOUL_SAND_VALLEY" : 170, - "DESERT" : 2, - "MUTATED_PLAINS" : 129, - "MUTATED_BIRCH_FOREST" : 155, - "WOODED_MOUNTAINS" : 34, - "TAIGA_HILLS" : 19, - "BAMBOO_JUNGLE" : 168, - "SWAMPLAND_MOUNTAINS" : 134, - "DESERT_MOUNTAINS" : 130, - "REDWOOD_TAIGA" : 32, - "MUSHROOM_FIELDS" : 14, - "GIANT_TREE_TAIGA_HILLS" : 33, - "PLAINS" : 1, - "JUNGLE_EDGE" : 23, - "SAVANNA_MOUNTAINS" : 163, - "DEEP_COLD_OCEAN" : 49, - "DESERT_LAKES" : 130, - "MOUNTAIN_EDGE" : 20, - "SNOWY_MOUNTAINS" : 13, - "MESA_PLATEAU_MOUNTAINS" : 167, - "JUNGLE_MOUNTAINS" : 149, - "SMALLER_EXTREME_HILLS" : 20, - "MESA_PLATEAU_FOREST_MOUNTAINS" : 166, - "NETHER_WASTES" : 8, - "BIRCH_FOREST_HILLS_MOUNTAINS" : 156, - "MUTATED_JUNGLE" : 149, - "WARM_OCEAN" : 44, - "DEEP_OCEAN" : 24, - "STONE_BEACH" : 25, - "MODIFIED_JUNGLE" : 149, - "MUTATED_SAVANNA" : 163, - "TAIGA_COLD_HILLS" : 31, - "OCEAN" : 0, - "SMALL_END_ISLANDS" : 40, - "MUSHROOM_FIELD_SHORE" : 15, - "GRAVELLY_MOUNTAINS" : 131, - "FROZEN_RIVER" : 11, - "TAIGA_COLD" : 30, - "BASALT_DELTAS" : 173, - "EXTREME_HILLS_WITH_TREES" : 34, - "MEGA_TAIGA_HILLS" : 33, - "MUTATED_FOREST" : 132, - "MUTATED_BIRCH_FOREST_HILLS" : 156, - "SKY" : 9, - "LUKEWARM_OCEAN" : 45, - "EXTREME_HILLS_MOUNTAINS" : 131, - "COLD_TAIGA_HILLS" : 31, - "THE_END" : 9, - "SUNFLOWER_PLAINS" : 129, - "SAVANNA_ROCK" : 36, - "ERODED_BADLANDS" : 165, - "STONE_SHORE" : 25, - "EXTREME_HILLS_PLUS_MOUNTAINS" : 162, - "CRIMSON_FOREST" : 171, - "VOID" : 127, - "SNOWY_TAIGA" : 30, - "SNOWY_TAIGA_MOUNTAINS" : 158, - "FLOWER_FOREST" : 132, - "COLD_OCEAN" : 46, - "BEACHES" : 16, - "MESA" : 37, - "MUSHROOM_SHORE" : 15, - "MESA_CLEAR_ROCK" : 39, - "NETHER" : 8, - "ICE_PLAINS" : 12, - "SHATTERED_SAVANNA" : 163, - "ROOFED_FOREST" : 29, - "GIANT_SPRUCE_TAIGA_HILLS" : 161, - "SNOWY_BEACH" : 26, - "MESA_BRYCE" : 165, - "JUNGLE_EDGE_MOUNTAINS" : 151, - "MUTATED_DESERT" : 130, - "MODIFIED_GRAVELLY_MOUNTAINS" : 158, - "MEGA_SPRUCE_TAIGA" : 160, - "TAIGA_MOUNTAINS" : 133, - "SMALL_MOUNTAINS" : 20, - "EXTREME_HILLS_PLUS" : 34, - "GIANT_SPRUCE_TAIGA" : 160, - "FOREST_HILLS" : 18, - "DESERT_HILLS" : 17, - "MUTATED_REDWOOD_TAIGA_HILLS" : 161, - "MEGA_SPRUCE_TAIGA_HILLS" : 161, - "RIVER" : 7, - "GIANT_TREE_TAIGA" : 32, - "SWAMPLAND" : 6, - "JUNGLE_HILLS" : 22, - "TALL_BIRCH_FOREST" : 155, - "DEEP_LUKEWARM_OCEAN" : 48, - "MESA_ROCK" : 38, - "SWAMP_HILLS" : 134, - "MODIFIED_WOODED_BADLANDS_PLATEAU" : 166, - "MODIFIED_JUNGLE_EDGE" : 151, - "BIRCH_FOREST_HILLS" : 28, - "COLD_TAIGA" : 30, - "TAIGA" : 5, - "MUTATED_MESA_ROCK" : 166, - "MUTATED_SWAMPLAND" : 134, - "ICE_FLATS" : 12, - "MUTATED_ROOFED_FOREST" : 157, - "MUTATED_MESA" : 165, - "MUTATED_EXTREME_HILLS" : 131 -} diff --git a/bootstrap/spigot/src/main/resources/plugin.yml b/bootstrap/spigot/src/main/resources/plugin.yml index e0a3c8bda..0cd78ebe3 100644 --- a/bootstrap/spigot/src/main/resources/plugin.yml +++ b/bootstrap/spigot/src/main/resources/plugin.yml @@ -3,7 +3,7 @@ name: ${outputName}-Spigot author: ${project.organization.name} website: ${project.organization.url} version: ${project.version} -softdepend: ["ViaVersion"] +softdepend: ["ViaVersion", "floodgate"] api-version: 1.13 commands: geyser: diff --git a/bootstrap/sponge/pom.xml b/bootstrap/sponge/pom.xml index 93ae3ab6b..72733a311 100644 --- a/bootstrap/sponge/pom.xml +++ b/bootstrap/sponge/pom.xml @@ -6,7 +6,7 @@ org.geysermc bootstrap-parent - 1.2.1-SNAPSHOT + 1.4.0-SNAPSHOT bootstrap-sponge @@ -14,7 +14,7 @@ org.geysermc connector - 1.2.1-SNAPSHOT + 1.4.0-SNAPSHOT compile diff --git a/bootstrap/standalone/pom.xml b/bootstrap/standalone/pom.xml index 122991357..2d28a7535 100644 --- a/bootstrap/standalone/pom.xml +++ b/bootstrap/standalone/pom.xml @@ -6,7 +6,7 @@ org.geysermc bootstrap-parent - 1.2.1-SNAPSHOT + 1.4.0-SNAPSHOT bootstrap-standalone @@ -14,7 +14,7 @@ org.geysermc connector - 1.2.1-SNAPSHOT + 1.4.0-SNAPSHOT compile diff --git a/bootstrap/velocity/pom.xml b/bootstrap/velocity/pom.xml index b9f2e64f2..0190d128e 100644 --- a/bootstrap/velocity/pom.xml +++ b/bootstrap/velocity/pom.xml @@ -6,7 +6,7 @@ org.geysermc bootstrap-parent - 1.2.1-SNAPSHOT + 1.4.0-SNAPSHOT bootstrap-velocity @@ -14,7 +14,7 @@ org.geysermc connector - 1.2.1-SNAPSHOT + 1.4.0-SNAPSHOT compile 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 bcb388ca6..c7b994d48 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 @@ -107,6 +107,15 @@ public class GeyserVelocityPlugin implements GeyserBootstrap { this.geyserLogger = new GeyserVelocityLogger(logger, geyserConfig.isDebugMode()); GeyserConfiguration.checkGeyserConfiguration(geyserConfig, geyserLogger); + // Remove this in like a year + try { + // Should only exist on 1.0 + Class.forName("org.geysermc.floodgate.FloodgateAPI"); + geyserLogger.severe(LanguageUtils.getLocaleStringLog("geyser.bootstrap.floodgate.outdated", "https://ci.opencollab.dev/job/GeyserMC/job/Floodgate/job/master/")); + return; + } catch (ClassNotFoundException ignored) { + } + if (geyserConfig.getRemote().getAuthType().equals("floodgate") && !proxyServer.getPluginManager().getPlugin("floodgate").isPresent()) { geyserLogger.severe(LanguageUtils.getLocaleStringLog("geyser.bootstrap.floodgate.not_installed") + " " + LanguageUtils.getLocaleStringLog("geyser.bootstrap.floodgate.disabling")); return; diff --git a/common/pom.xml b/common/pom.xml index e40e40c55..8ddcfd981 100644 --- a/common/pom.xml +++ b/common/pom.xml @@ -6,22 +6,20 @@ org.geysermc geyser-parent - 1.2.1-SNAPSHOT + 1.4.0-SNAPSHOT common - com.google.code.gson - gson - 2.8.2 - compile + org.geysermc.cumulus + cumulus + 1.0-SNAPSHOT - com.fasterxml.jackson.datatype - jackson-datatype-jsr310 - 2.9.8 - compile + com.google.code.gson + gson + 2.8.6 \ No newline at end of file diff --git a/common/src/main/java/org/geysermc/common/PlatformType.java b/common/src/main/java/org/geysermc/common/PlatformType.java index a1096f347..098fd3946 100644 --- a/common/src/main/java/org/geysermc/common/PlatformType.java +++ b/common/src/main/java/org/geysermc/common/PlatformType.java @@ -31,7 +31,6 @@ import lombok.Getter; @Getter @AllArgsConstructor public enum PlatformType { - ANDROID("Android"), BUNGEECORD("BungeeCord"), FABRIC("Fabric"), diff --git a/common/src/main/java/org/geysermc/common/window/CustomFormBuilder.java b/common/src/main/java/org/geysermc/common/window/CustomFormBuilder.java deleted file mode 100644 index f4e597f00..000000000 --- a/common/src/main/java/org/geysermc/common/window/CustomFormBuilder.java +++ /dev/null @@ -1,71 +0,0 @@ -/* - * Copyright (c) 2019-2021 GeyserMC. http://geysermc.org - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - * - * @author GeyserMC - * @link https://github.com/GeyserMC/Geyser - */ - -package org.geysermc.common.window; - -import lombok.Getter; -import org.geysermc.common.window.CustomFormWindow; -import org.geysermc.common.window.button.FormImage; -import org.geysermc.common.window.component.FormComponent; -import org.geysermc.common.window.response.CustomFormResponse; - -public class CustomFormBuilder { - - @Getter - private CustomFormWindow form; - - public CustomFormBuilder(String title) { - form = new CustomFormWindow(title); - } - - public CustomFormBuilder setTitle(String title) { - form.setTitle(title); - return this; - } - - public CustomFormBuilder setIcon(FormImage icon) { - form.setIcon(icon); - return this; - } - - public CustomFormBuilder setResponse(String data) { - form.setResponse(data); - return this; - } - - public CustomFormBuilder setResponse(CustomFormResponse response) { - form.setResponse(response); - return this; - } - - public CustomFormBuilder addComponent(FormComponent component) { - form.addComponent(component); - return this; - } - - public CustomFormWindow build() { - return form; - } -} diff --git a/common/src/main/java/org/geysermc/common/window/CustomFormWindow.java b/common/src/main/java/org/geysermc/common/window/CustomFormWindow.java deleted file mode 100644 index 7043dda9f..000000000 --- a/common/src/main/java/org/geysermc/common/window/CustomFormWindow.java +++ /dev/null @@ -1,165 +0,0 @@ -/* - * Copyright (c) 2019-2021 GeyserMC. http://geysermc.org - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - * - * @author GeyserMC - * @link https://github.com/GeyserMC/Geyser - */ - -package org.geysermc.common.window; - -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.core.type.TypeReference; -import com.fasterxml.jackson.databind.ObjectMapper; -import lombok.Getter; -import lombok.Setter; -import org.geysermc.common.window.button.FormImage; -import org.geysermc.common.window.component.*; -import org.geysermc.common.window.response.CustomFormResponse; -import org.geysermc.common.window.response.FormResponseData; - -import java.io.IOException; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -public class CustomFormWindow extends FormWindow { - - @Getter - @Setter - private String title; - - @Getter - @Setter - private FormImage icon; - - @Getter - private List content; - - public CustomFormWindow(String title) { - this(title, new ArrayList<>()); - } - - public CustomFormWindow(String title, List content) { - this(title, content, (FormImage) null); - } - - public CustomFormWindow(String title, List content, String icon) { - this(title, content, new FormImage(FormImage.FormImageType.URL, icon)); - } - - public CustomFormWindow(String title, List content, FormImage icon) { - super("custom_form"); - - this.title = title; - this.content = content; - this.icon = icon; - } - - public void addComponent(FormComponent component) { - content.add(component); - } - - public String getJSONData() { - String toModify = ""; - try { - toModify = new ObjectMapper().writeValueAsString(this); - } catch (JsonProcessingException e) { } - - //We need to replace this due to Java not supporting declaring class field 'default' - return toModify.replace("defaultOptionIndex", "default") - .replace("defaultText", "default") - .replace("defaultValue", "default") - .replace("defaultStepIndex", "default"); - } - - public void setResponse(String data) { - if (data == null || data.trim().equalsIgnoreCase("null") || data.isEmpty()) { - closed = true; - return; - } - - int i = 0; - Map dropdownResponses = new HashMap(); - Map inputResponses = new HashMap(); - Map sliderResponses = new HashMap(); - Map stepSliderResponses = new HashMap(); - Map toggleResponses = new HashMap(); - Map responses = new HashMap(); - Map labelResponses = new HashMap(); - - List componentResponses = new ArrayList<>(); - try { - componentResponses = new ObjectMapper().readValue(data.trim(), new TypeReference>(){}); - } catch (IOException e) { } - - for (String response : componentResponses) { - if (i >= content.size()) { - break; - } - - FormComponent component = content.get(i); - if (component == null) - return; - - if (component instanceof LabelComponent) { - LabelComponent labelComponent = (LabelComponent) component; - labelResponses.put(i, labelComponent.getText()); - } - - if (component instanceof DropdownComponent) { - DropdownComponent dropdownComponent = (DropdownComponent) component; - String option = dropdownComponent.getOptions().get(Integer.parseInt(response)); - - dropdownResponses.put(i, new FormResponseData(Integer.parseInt(response), option)); - responses.put(i, option); - } - - if (component instanceof InputComponent) { - inputResponses.put(i, response); - responses.put(i, response); - } - - if (component instanceof SliderComponent) { - float value = Float.parseFloat(response); - sliderResponses.put(i, value); - responses.put(i, value); - } - - if (component instanceof StepSliderComponent) { - StepSliderComponent stepSliderComponent = (StepSliderComponent) component; - String step = stepSliderComponent.getSteps().get(Integer.parseInt(response)); - stepSliderResponses.put(i, new FormResponseData(Integer.parseInt(response), step)); - responses.put(i, step); - } - - if (component instanceof ToggleComponent) { - boolean answer = Boolean.parseBoolean(response); - toggleResponses.put(i, answer); - responses.put(i, answer); - } - i++; - } - - this.response = new CustomFormResponse(responses, dropdownResponses, inputResponses, - sliderResponses, stepSliderResponses, toggleResponses, labelResponses); - } -} diff --git a/common/src/main/java/org/geysermc/common/window/ModalFormWindow.java b/common/src/main/java/org/geysermc/common/window/ModalFormWindow.java deleted file mode 100644 index 9d8021617..000000000 --- a/common/src/main/java/org/geysermc/common/window/ModalFormWindow.java +++ /dev/null @@ -1,82 +0,0 @@ -/* - * Copyright (c) 2019-2021 GeyserMC. http://geysermc.org - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - * - * @author GeyserMC - * @link https://github.com/GeyserMC/Geyser - */ - -package org.geysermc.common.window; - -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.ObjectMapper; -import lombok.Getter; -import lombok.Setter; -import org.geysermc.common.window.response.ModalFormResponse; - -public class ModalFormWindow extends FormWindow { - - @Getter - @Setter - private String title; - - @Getter - @Setter - private String content; - - @Getter - @Setter - private String button1; - - @Getter - @Setter - private String button2; - - public ModalFormWindow(String title, String content, String button1, String button2) { - super("modal"); - - this.title = title; - this.content = content; - this.button1 = button1; - this.button2 = button2; - } - - @Override - public String getJSONData() { - try { - return new ObjectMapper().writeValueAsString(this); - } catch (JsonProcessingException e) { - return ""; - } - } - - public void setResponse(String data) { - if (data == null || data.equalsIgnoreCase("null")) { - closed = true; - return; - } - - if (Boolean.parseBoolean(data)) { - response = new ModalFormResponse(0, button1); - } else { - response = new ModalFormResponse(1, button2); - } - } -} diff --git a/common/src/main/java/org/geysermc/common/window/SimpleFormWindow.java b/common/src/main/java/org/geysermc/common/window/SimpleFormWindow.java deleted file mode 100644 index 48bda0bd2..000000000 --- a/common/src/main/java/org/geysermc/common/window/SimpleFormWindow.java +++ /dev/null @@ -1,94 +0,0 @@ -/* - * Copyright (c) 2019-2021 GeyserMC. http://geysermc.org - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - * - * @author GeyserMC - * @link https://github.com/GeyserMC/Geyser - */ - -package org.geysermc.common.window; - -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.ObjectMapper; -import lombok.Getter; -import lombok.Setter; -import org.geysermc.common.window.button.FormButton; -import org.geysermc.common.window.response.SimpleFormResponse; - -import java.util.ArrayList; -import java.util.List; - - -public class SimpleFormWindow extends FormWindow { - - @Getter - @Setter - private String title; - - @Getter - @Setter - private String content; - - @Getter - @Setter - private List buttons; - - public SimpleFormWindow(String title, String content) { - this(title, content, new ArrayList()); - } - - public SimpleFormWindow(String title, String content, List buttons) { - super("form"); - - this.title = title; - this.content = content; - this.buttons = buttons; - } - - @Override - public String getJSONData() { - try { - return new ObjectMapper().writeValueAsString(this); - } catch (JsonProcessingException e) { - return ""; - } - } - - public void setResponse(String data) { - if (data == null || data.trim().equalsIgnoreCase("null")) { - closed = true; - return; - } - - int buttonID; - try { - buttonID = Integer.parseInt(data.trim()); - } catch (Exception ex) { - return; - } - - if (buttonID >= buttons.size()) { - response = new SimpleFormResponse(buttonID, null); - return; - } - - response = new SimpleFormResponse(buttonID, buttons.get(buttonID)); - } -} diff --git a/common/src/main/java/org/geysermc/common/window/component/DropdownComponent.java b/common/src/main/java/org/geysermc/common/window/component/DropdownComponent.java deleted file mode 100644 index 8ba17bb4b..000000000 --- a/common/src/main/java/org/geysermc/common/window/component/DropdownComponent.java +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Copyright (c) 2019-2021 GeyserMC. http://geysermc.org - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - * - * @author GeyserMC - * @link https://github.com/GeyserMC/Geyser - */ - -package org.geysermc.common.window.component; - -import lombok.Getter; -import lombok.Setter; - -import java.util.List; - -public class DropdownComponent extends FormComponent { - - @Getter - @Setter - private String text; - - @Getter - @Setter - private List options; - - @Getter - @Setter - private int defaultOptionIndex; - - public DropdownComponent() { - super("dropdown"); - } - - public void addOption(String option, boolean isDefault) { - options.add(option); - if (isDefault) - defaultOptionIndex = options.size() - 1; - } -} diff --git a/common/src/main/java/org/geysermc/common/window/component/InputComponent.java b/common/src/main/java/org/geysermc/common/window/component/InputComponent.java deleted file mode 100644 index 9dfc249a2..000000000 --- a/common/src/main/java/org/geysermc/common/window/component/InputComponent.java +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Copyright (c) 2019-2021 GeyserMC. http://geysermc.org - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - * - * @author GeyserMC - * @link https://github.com/GeyserMC/Geyser - */ - -package org.geysermc.common.window.component; - -import lombok.Getter; -import lombok.Setter; - -public class InputComponent extends FormComponent { - - @Getter - @Setter - private String text; - - @Getter - @Setter - private String placeholder; - - @Getter - @Setter - private String defaultText; - - public InputComponent(String text, String placeholder, String defaultText) { - super("input"); - - this.text = text; - this.placeholder = placeholder; - this.defaultText = defaultText; - } -} diff --git a/common/src/main/java/org/geysermc/common/window/component/SliderComponent.java b/common/src/main/java/org/geysermc/common/window/component/SliderComponent.java deleted file mode 100644 index 3e681664c..000000000 --- a/common/src/main/java/org/geysermc/common/window/component/SliderComponent.java +++ /dev/null @@ -1,65 +0,0 @@ -/* - * Copyright (c) 2019-2021 GeyserMC. http://geysermc.org - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - * - * @author GeyserMC - * @link https://github.com/GeyserMC/Geyser - */ - -package org.geysermc.common.window.component; - -import lombok.Getter; -import lombok.Setter; - -public class SliderComponent extends FormComponent { - - @Getter - @Setter - private String text; - - @Getter - @Setter - private float min; - - @Getter - @Setter - private float max; - - @Getter - @Setter - private int step; - - @Getter - @Setter - private float defaultValue; - - public SliderComponent(String text, float min, float max, int step, float defaultValue) { - super("slider"); - - this.text = text; - this.min = Math.max(min, 0f); - this.max = max > this.min ? max : this.min; - if (step != -1f && step > 0) - this.step = step; - - if (defaultValue != -1f) - this.defaultValue = defaultValue; - } -} diff --git a/common/src/main/java/org/geysermc/common/window/component/ToggleComponent.java b/common/src/main/java/org/geysermc/common/window/component/ToggleComponent.java deleted file mode 100644 index e79e7592a..000000000 --- a/common/src/main/java/org/geysermc/common/window/component/ToggleComponent.java +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Copyright (c) 2019-2021 GeyserMC. http://geysermc.org - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - * - * @author GeyserMC - * @link https://github.com/GeyserMC/Geyser - */ - -package org.geysermc.common.window.component; - -import lombok.Getter; -import lombok.Setter; - -public class ToggleComponent extends FormComponent { - - @Getter - @Setter - private String text; - - @Getter - @Setter - private boolean defaultValue; - - public ToggleComponent(String text) { - this(text, false); - } - - public ToggleComponent(String text, boolean defaultValue) { - super("toggle"); - - this.text = text; - this.defaultValue = defaultValue; - } -} diff --git a/common/src/main/java/org/geysermc/common/window/response/CustomFormResponse.java b/common/src/main/java/org/geysermc/common/window/response/CustomFormResponse.java deleted file mode 100644 index 252a0978b..000000000 --- a/common/src/main/java/org/geysermc/common/window/response/CustomFormResponse.java +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright (c) 2019-2021 GeyserMC. http://geysermc.org - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - * - * @author GeyserMC - * @link https://github.com/GeyserMC/Geyser - */ - -package org.geysermc.common.window.response; - -import lombok.AllArgsConstructor; -import lombok.Getter; -import org.geysermc.common.window.response.FormResponse; -import org.geysermc.common.window.response.FormResponseData; - -import java.util.Map; - -@Getter -@AllArgsConstructor -public class CustomFormResponse implements FormResponse { - - private Map responses; - private Map dropdownResponses; - private Map inputResponses; - private Map sliderResponses; - private Map stepSliderResponses; - private Map toggleResponses; - private Map labelResponses; -} diff --git a/common/src/main/java/org/geysermc/common/window/response/SimpleFormResponse.java b/common/src/main/java/org/geysermc/common/window/response/SimpleFormResponse.java deleted file mode 100644 index c24f72d02..000000000 --- a/common/src/main/java/org/geysermc/common/window/response/SimpleFormResponse.java +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright (c) 2019-2021 GeyserMC. http://geysermc.org - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - * - * @author GeyserMC - * @link https://github.com/GeyserMC/Geyser - */ - -package org.geysermc.common.window.response; - -import lombok.AllArgsConstructor; -import lombok.Getter; -import org.geysermc.common.window.button.FormButton; -import org.geysermc.common.window.response.FormResponse; - -@Getter -@AllArgsConstructor -public class SimpleFormResponse implements FormResponse { - - private int clickedButtonId; - private FormButton clickedButton; -} diff --git a/common/src/main/java/org/geysermc/floodgate/crypto/AesCipher.java b/common/src/main/java/org/geysermc/floodgate/crypto/AesCipher.java new file mode 100644 index 000000000..f602f4be0 --- /dev/null +++ b/common/src/main/java/org/geysermc/floodgate/crypto/AesCipher.java @@ -0,0 +1,125 @@ +/* + * 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/Floodgate + * + */ + +package org.geysermc.floodgate.crypto; + +import lombok.RequiredArgsConstructor; + +import javax.crypto.Cipher; +import javax.crypto.SecretKey; +import javax.crypto.spec.GCMParameterSpec; +import java.nio.Buffer; +import java.nio.ByteBuffer; +import java.security.Key; +import java.security.SecureRandom; + +@RequiredArgsConstructor +public final class AesCipher implements FloodgateCipher { + public static final int IV_LENGTH = 12; + private static final int TAG_BIT_LENGTH = 128; + private static final String CIPHER_NAME = "AES/GCM/NoPadding"; + + private final SecureRandom secureRandom = new SecureRandom(); + private final Topping topping; + private SecretKey secretKey; + + public void init(Key key) { + if (!"AES".equals(key.getAlgorithm())) { + throw new RuntimeException( + "Algorithm was expected to be AES, but got " + key.getAlgorithm() + ); + } + secretKey = (SecretKey) key; + } + + public byte[] encrypt(byte[] data) throws Exception { + Cipher cipher = Cipher.getInstance(CIPHER_NAME); + + byte[] iv = new byte[IV_LENGTH]; + secureRandom.nextBytes(iv); + + GCMParameterSpec spec = new GCMParameterSpec(TAG_BIT_LENGTH, iv); + cipher.init(Cipher.ENCRYPT_MODE, secretKey, spec); + byte[] cipherText = cipher.doFinal(data); + + if (topping != null) { + iv = topping.encode(iv); + cipherText = topping.encode(cipherText); + } + + return ByteBuffer.allocate(iv.length + cipherText.length + HEADER_LENGTH + 1) + .put(IDENTIFIER) // header + .put(iv) + .put((byte) 0x21) + .put(cipherText) + .array(); + } + + public byte[] decrypt(byte[] cipherTextWithIv) throws Exception { + checkHeader(cipherTextWithIv); + + Cipher cipher = Cipher.getInstance(CIPHER_NAME); + + int bufferLength = cipherTextWithIv.length - HEADER_LENGTH; + ByteBuffer buffer = ByteBuffer.wrap(cipherTextWithIv, HEADER_LENGTH, bufferLength); + + int ivLength = IV_LENGTH; + + if (topping != null) { + int mark = buffer.position(); + + // we need the first index, the second is for the optional RawSkin + boolean found = false; + while (buffer.hasRemaining() && !found) { + if (buffer.get() == 0x21) { + found = true; + } + } + + ivLength = buffer.position() - mark - 1; // don't include the splitter itself + // don't remove this cast, it'll cause problems if you remove it + ((Buffer) buffer).position(mark); // reset to the pre-while index + } + + byte[] iv = new byte[ivLength]; + buffer.get(iv); + + // don't remove this cast, it'll cause problems if you remove it + ((Buffer) buffer).position(buffer.position() + 1); // skip splitter + + byte[] cipherText = new byte[buffer.remaining()]; + buffer.get(cipherText); + + if (topping != null) { + iv = topping.decode(iv); + cipherText = topping.decode(cipherText); + } + + GCMParameterSpec spec = new GCMParameterSpec(TAG_BIT_LENGTH, iv); + cipher.init(Cipher.DECRYPT_MODE, secretKey, spec); + return cipher.doFinal(cipherText); + } +} diff --git a/common/src/main/java/org/geysermc/floodgate/crypto/AesKeyProducer.java b/common/src/main/java/org/geysermc/floodgate/crypto/AesKeyProducer.java new file mode 100644 index 000000000..faec0ad10 --- /dev/null +++ b/common/src/main/java/org/geysermc/floodgate/crypto/AesKeyProducer.java @@ -0,0 +1,74 @@ +/* + * 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/Floodgate + * + */ + +package org.geysermc.floodgate.crypto; + +import javax.crypto.KeyGenerator; +import javax.crypto.SecretKey; +import javax.crypto.spec.SecretKeySpec; +import java.security.NoSuchAlgorithmException; +import java.security.SecureRandom; + +public final class AesKeyProducer implements KeyProducer { + public static int KEY_SIZE = 128; + + @Override + public SecretKey produce() { + try { + KeyGenerator keyGenerator = KeyGenerator.getInstance("AES"); + keyGenerator.init(KEY_SIZE, getSecureRandom()); + return keyGenerator.generateKey(); + } catch (Exception exception) { + throw new RuntimeException(exception); + } + } + + @Override + public SecretKey produceFrom(byte[] keyFileData) { + try { + return new SecretKeySpec(keyFileData, "AES"); + } catch (Exception exception) { + throw new RuntimeException(exception); + } + } + + private SecureRandom getSecureRandom() throws NoSuchAlgorithmException { + // use Windows-PRNG for windows (default impl is SHA1PRNG) + if (System.getProperty("os.name").startsWith("Windows")) { + return SecureRandom.getInstance("Windows-PRNG"); + } else { + try { + // NativePRNG (which should be the default on unix-systems) can still block your + // system. Even though it isn't as bad as NativePRNGBlocking, we still try to + // prevent that if possible + return SecureRandom.getInstance("NativePRNGNonBlocking"); + } catch (NoSuchAlgorithmException ignored) { + // at this point we just have to go with the default impl even if it blocks + return new SecureRandom(); + } + } + } +} diff --git a/common/src/main/java/org/geysermc/common/window/response/ModalFormResponse.java b/common/src/main/java/org/geysermc/floodgate/crypto/Base64Topping.java similarity index 78% rename from common/src/main/java/org/geysermc/common/window/response/ModalFormResponse.java rename to common/src/main/java/org/geysermc/floodgate/crypto/Base64Topping.java index 3338df4da..18fc5a04a 100644 --- a/common/src/main/java/org/geysermc/common/window/response/ModalFormResponse.java +++ b/common/src/main/java/org/geysermc/floodgate/crypto/Base64Topping.java @@ -23,16 +23,18 @@ * @link https://github.com/GeyserMC/Geyser */ -package org.geysermc.common.window.response; +package org.geysermc.floodgate.crypto; -import lombok.AllArgsConstructor; -import lombok.Getter; -import org.geysermc.common.window.response.FormResponse; +import java.util.Base64; -@Getter -@AllArgsConstructor -public class ModalFormResponse implements FormResponse { +public final class Base64Topping implements Topping { + @Override + public byte[] encode(byte[] data) { + return Base64.getEncoder().encode(data); + } - private int clickedButtonId; - private String clickedButtonText; + @Override + public byte[] decode(byte[] data) { + return Base64.getDecoder().decode(data); + } } diff --git a/common/src/main/java/org/geysermc/floodgate/crypto/FloodgateCipher.java b/common/src/main/java/org/geysermc/floodgate/crypto/FloodgateCipher.java new file mode 100644 index 000000000..69c8f9f6d --- /dev/null +++ b/common/src/main/java/org/geysermc/floodgate/crypto/FloodgateCipher.java @@ -0,0 +1,163 @@ +/* + * 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/Floodgate + * + */ + +package org.geysermc.floodgate.crypto; + +import lombok.AllArgsConstructor; +import lombok.Data; +import org.geysermc.floodgate.util.InvalidFormatException; + +import java.nio.charset.StandardCharsets; +import java.security.Key; + +/** + * Responsible for both encrypting and decrypting data + */ +public interface FloodgateCipher { + // use invalid username characters at the beginning and the end of the identifier, + // to make sure that it doesn't get messed up with usernames + byte[] IDENTIFIER = "^Floodgate^".getBytes(StandardCharsets.UTF_8); + int HEADER_LENGTH = IDENTIFIER.length; + + static boolean hasHeader(String data) { + if (data.length() < IDENTIFIER.length) { + return false; + } + + for (int i = 0; i < IDENTIFIER.length; i++) { + if (IDENTIFIER[i] != data.charAt(i)) { + return false; + } + } + return true; + } + + /** + * Initializes the instance by giving it the key it needs to encrypt or decrypt data + * + * @param key the key used to encrypt and decrypt data + */ + void init(Key key); + + /** + * Encrypts the given data using the Key provided in {@link #init(Key)} + * + * @param data the data to encrypt + * @return the encrypted data + * @throws Exception when the encryption failed + */ + byte[] encrypt(byte[] data) throws Exception; + + /** + * Encrypts data from a String.
This method internally calls {@link #encrypt(byte[])} + * + * @param data the data to encrypt + * @return the encrypted data + * @throws Exception when the encryption failed + */ + default byte[] encryptFromString(String data) throws Exception { + return encrypt(data.getBytes(StandardCharsets.UTF_8)); + } + + /** + * Decrypts the given data using the Key provided in {@link #init(Key)} + * + * @param data the data to decrypt + * @return the decrypted data + * @throws Exception when the decrypting failed + */ + byte[] decrypt(byte[] data) throws Exception; + + /** + * Decrypts a byte[] and turn it into a String.
This method internally calls {@link + * #decrypt(byte[])} and converts the returned byte[] into a String. + * + * @param data the data to encrypt + * @return the decrypted data in a UTF-8 String + * @throws Exception when the decrypting failed + */ + default String decryptToString(byte[] data) throws Exception { + byte[] decrypted = decrypt(data); + if (decrypted == null) { + return null; + } + return new String(decrypted, StandardCharsets.UTF_8); + } + + /** + * Decrypts a String.
This method internally calls {@link #decrypt(byte[])} by converting + * the UTF-8 String into a byte[] + * + * @param data the data to decrypt + * @return the decrypted data in a byte[] + * @throws Exception when the decrypting failed + */ + default byte[] decryptFromString(String data) throws Exception { + return decrypt(data.getBytes(StandardCharsets.UTF_8)); + } + + /** + * Checks if the header is valid. This method will throw an InvalidFormatException when the + * header is invalid. + * + * @param data the data to check + * @throws InvalidFormatException when the header is invalid + */ + default void checkHeader(byte[] data) throws InvalidFormatException { + final int identifierLength = IDENTIFIER.length; + + if (data.length <= HEADER_LENGTH) { + throw new InvalidFormatException("Data length is smaller then header." + + "Needed " + HEADER_LENGTH + ", got " + data.length, + true + ); + } + + for (int i = 0; i < identifierLength; i++) { + if (IDENTIFIER[i] != data[i]) { + StringBuilder receivedIdentifier = new StringBuilder(); + for (byte b : IDENTIFIER) { + receivedIdentifier.append(b); + } + + throw new InvalidFormatException( + String.format("Expected identifier %s, got %s", + new String(IDENTIFIER, StandardCharsets.UTF_8), + receivedIdentifier.toString() + ), + true + ); + } + } + } + + @Data + @AllArgsConstructor + class HeaderResult { + private int version; + private int startIndex; + } +} diff --git a/common/src/main/java/org/geysermc/floodgate/crypto/KeyProducer.java b/common/src/main/java/org/geysermc/floodgate/crypto/KeyProducer.java new file mode 100644 index 000000000..fc2ac512d --- /dev/null +++ b/common/src/main/java/org/geysermc/floodgate/crypto/KeyProducer.java @@ -0,0 +1,41 @@ +/* + * 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/Floodgate + * + */ + +package org.geysermc.floodgate.crypto; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.security.Key; + +public interface KeyProducer { + Key produce(); + Key produceFrom(byte[] keyFileData); + + default Key produceFrom(Path keyFileLocation) throws IOException { + return produceFrom(Files.readAllBytes(keyFileLocation)); + } +} diff --git a/common/src/main/java/org/geysermc/common/window/response/FormResponseData.java b/common/src/main/java/org/geysermc/floodgate/crypto/Topping.java similarity index 84% rename from common/src/main/java/org/geysermc/common/window/response/FormResponseData.java rename to common/src/main/java/org/geysermc/floodgate/crypto/Topping.java index 013c49859..689274269 100644 --- a/common/src/main/java/org/geysermc/common/window/response/FormResponseData.java +++ b/common/src/main/java/org/geysermc/floodgate/crypto/Topping.java @@ -23,15 +23,9 @@ * @link https://github.com/GeyserMC/Geyser */ -package org.geysermc.common.window.response; +package org.geysermc.floodgate.crypto; -import lombok.AllArgsConstructor; -import lombok.Getter; - -@AllArgsConstructor -@Getter -public class FormResponseData { - - private int elementID; - private String elementContent; +public interface Topping { + byte[] encode(byte[] data); + byte[] decode(byte[] data); } diff --git a/common/src/main/java/org/geysermc/floodgate/news/NewsItem.java b/common/src/main/java/org/geysermc/floodgate/news/NewsItem.java new file mode 100644 index 000000000..be0634a6d --- /dev/null +++ b/common/src/main/java/org/geysermc/floodgate/news/NewsItem.java @@ -0,0 +1,143 @@ +/* + * Copyright (c) 2019-2021 GeyserMC. http://geysermc.org + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * @author GeyserMC + * @link https://github.com/GeyserMC/Geyser + */ + +package org.geysermc.floodgate.news; + +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import org.geysermc.floodgate.news.data.ItemData; + +import java.util.Collections; +import java.util.HashSet; +import java.util.Set; + +public final class NewsItem { + private final int id; + private final String project; + private final boolean active; + private final NewsType type; + private final ItemData data; + private final boolean priority; + private final String message; + private final Set actions; + private final String url; + + private NewsItem(int id, String project, boolean active, NewsType type, ItemData data, + boolean priority, String message, Set actions, String url) { + this.id = id; + this.project = project; + this.active = active; + this.type = type; + this.data = data; + this.priority = priority; + this.message = message; + this.actions = Collections.unmodifiableSet(actions); + this.url = url; + } + + public static NewsItem readItem(JsonObject newsItem) { + NewsType newsType = NewsType.getByName(newsItem.get("type").getAsString()); + if (newsType == null) { + return null; + } + + JsonObject messageObject = newsItem.getAsJsonObject("message"); + NewsItemMessage itemMessage = NewsItemMessage.getById(messageObject.get("id").getAsInt()); + + String message = "Received an unknown news message type. Please update"; + if (itemMessage != null) { + message = itemMessage.getFormattedMessage(messageObject.getAsJsonArray("args")); + } + + Set actions = new HashSet<>(); + for (JsonElement actionElement : newsItem.getAsJsonArray("actions")) { + NewsItemAction action = NewsItemAction.getByName(actionElement.getAsString()); + if (action != null) { + actions.add(action); + } + } + + return new NewsItem( + newsItem.get("id").getAsInt(), + newsItem.get("project").getAsString(), + newsItem.get("active").getAsBoolean(), + newsType, + newsType.read(newsItem.getAsJsonObject("data")), + newsItem.get("priority").getAsBoolean(), + message, + actions, + newsItem.get("url").getAsString() + ); + } + + public int getId() { + return id; + } + + public String getProject() { + return project; + } + + public boolean isGlobal() { + return "all".equals(getProject()); + } + + public boolean isActive() { + return active; + } + + public NewsType getType() { + return type; + } + + public ItemData getData() { + return data; + } + + @SuppressWarnings("unchecked") + public T getDataAs(Class type) { + return (T) data; + } + + public boolean isPriority() { + return priority; + } + + public String getRawMessage() { + return message; + } + + public String getMessage() { + return message + " See " + getUrl() + " for more information."; + } + + public Set getActions() { + return actions; + } + + public String getUrl() { + return url; + } +} diff --git a/common/src/main/java/org/geysermc/floodgate/news/NewsItemAction.java b/common/src/main/java/org/geysermc/floodgate/news/NewsItemAction.java new file mode 100644 index 000000000..78a8e4ed3 --- /dev/null +++ b/common/src/main/java/org/geysermc/floodgate/news/NewsItemAction.java @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2019-2021 GeyserMC. http://geysermc.org + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * @author GeyserMC + * @link https://github.com/GeyserMC/Geyser + */ + +package org.geysermc.floodgate.news; + +public enum NewsItemAction { + ON_SERVER_STARTED, + ON_OPERATOR_JOIN, + BROADCAST_TO_CONSOLE, + BROADCAST_TO_OPERATORS; + + private static final NewsItemAction[] VALUES = values(); + + public static NewsItemAction getByName(String actionName) { + for (NewsItemAction type : VALUES) { + if (type.name().equalsIgnoreCase(actionName)) { + return type; + } + } + return null; + } +} diff --git a/common/src/main/java/org/geysermc/floodgate/news/NewsItemMessage.java b/common/src/main/java/org/geysermc/floodgate/news/NewsItemMessage.java new file mode 100644 index 000000000..b11605fb4 --- /dev/null +++ b/common/src/main/java/org/geysermc/floodgate/news/NewsItemMessage.java @@ -0,0 +1,89 @@ +/* + * Copyright (c) 2019-2021 GeyserMC. http://geysermc.org + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * @author GeyserMC + * @link https://github.com/GeyserMC/Geyser + */ + +package org.geysermc.floodgate.news; + +import com.google.gson.JsonArray; + +// {} is used for things that have to be filled in by the server, +// {@} is for things that have to be filled in by us +public enum NewsItemMessage { + UPDATE_AVAILABLE("There is an update available for {}. The newest version is: {}"), + UPDATE_RECOMMENDED(UPDATE_AVAILABLE + ". Your version is quite old, updating is recommend."), + UPDATE_HIGHLY_RECOMMENDED(UPDATE_AVAILABLE + ". We highly recommend updating because some important changes have been made."), + UPDATE_ANCIENT_VERSION(UPDATE_AVAILABLE + ". You are running an ancient version, updating is recommended."), + + DOWNTIME_GENERIC("The {} is temporarily going down for maintenance soon."), + DOWNTIME_WITH_START("The {} is temporarily going down for maintenance on {}."), + DOWNTIME_TIMEFRAME(DOWNTIME_WITH_START + " The maintenance is expected to last till {}."); + + private static final NewsItemMessage[] VALUES = values(); + + private final String messageFormat; + private final String[] messageSplitted; + + NewsItemMessage(String messageFormat) { + this.messageFormat = messageFormat; + this.messageSplitted = messageFormat.split(" "); + } + + public static NewsItemMessage getById(int id) { + return VALUES.length > id ? VALUES[id] : null; + } + + public String getMessageFormat() { + return messageFormat; + } + + public String getFormattedMessage(JsonArray serverArguments) { + int serverArgumentsIndex = 0; + + StringBuilder message = new StringBuilder(); + for (String split : messageSplitted) { + if (message.length() > 0) { + message.append(' '); + } + + String result = split; + + if (serverArgumentsIndex < serverArguments.size()) { + String argument = serverArguments.get(serverArgumentsIndex).getAsString(); + result = result.replace("{}", argument); + if (!result.equals(split)) { + serverArgumentsIndex++; + } + } + + message.append(result); + } + return message.toString(); + } + + + @Override + public String toString() { + return getMessageFormat(); + } +} diff --git a/common/src/main/java/org/geysermc/common/window/component/StepSliderComponent.java b/common/src/main/java/org/geysermc/floodgate/news/NewsType.java similarity index 56% rename from common/src/main/java/org/geysermc/common/window/component/StepSliderComponent.java rename to common/src/main/java/org/geysermc/floodgate/news/NewsType.java index 8e6748a94..c2848535e 100644 --- a/common/src/main/java/org/geysermc/common/window/component/StepSliderComponent.java +++ b/common/src/main/java/org/geysermc/floodgate/news/NewsType.java @@ -23,47 +23,37 @@ * @link https://github.com/GeyserMC/Geyser */ -package org.geysermc.common.window.component; +package org.geysermc.floodgate.news; -import lombok.Getter; -import lombok.Setter; +import com.google.gson.JsonObject; +import org.geysermc.floodgate.news.data.BuildSpecificData; +import org.geysermc.floodgate.news.data.CheckAfterData; +import org.geysermc.floodgate.news.data.ItemData; -import java.util.ArrayList; -import java.util.List; +import java.util.function.Function; -public class StepSliderComponent extends FormComponent { +public enum NewsType { + BUILD_SPECIFIC(BuildSpecificData::read), + CHECK_AFTER(CheckAfterData::read); - @Getter - @Setter - private String text; + private static final NewsType[] VALUES = values(); - @Getter - private List steps; + private final Function readFunction; - @Getter - @Setter - private int defaultStepIndex; - - public StepSliderComponent(String text) { - this(text, new ArrayList()); + NewsType(Function readFunction) { + this.readFunction = readFunction; } - public StepSliderComponent(String text, List steps) { - this(text, steps, 0); + public static NewsType getByName(String newsType) { + for (NewsType type : VALUES) { + if (type.name().equalsIgnoreCase(newsType)) { + return type; + } + } + return null; } - public StepSliderComponent(String text, List steps, int defaultStepIndex) { - super("step_slider"); - - this.text = text; - this.steps = steps; - this.defaultStepIndex = defaultStepIndex; - } - - public void addStep(String step, boolean isDefault) { - steps.add(step); - - if (isDefault) - defaultStepIndex = steps.size() - 1; + public ItemData read(JsonObject data) { + return readFunction.apply(data); } } diff --git a/common/src/main/java/org/geysermc/floodgate/news/data/BuildSpecificData.java b/common/src/main/java/org/geysermc/floodgate/news/data/BuildSpecificData.java new file mode 100644 index 000000000..d415f4200 --- /dev/null +++ b/common/src/main/java/org/geysermc/floodgate/news/data/BuildSpecificData.java @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2019-2021 GeyserMC. http://geysermc.org + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * @author GeyserMC + * @link https://github.com/GeyserMC/Geyser + */ + +package org.geysermc.floodgate.news.data; + +import com.google.gson.JsonObject; + +public final class BuildSpecificData implements ItemData { + private String branch; + + private boolean allAffected; + private int affectedGreaterThan; + private int affectedLessThan; + + public static BuildSpecificData read(JsonObject data) { + BuildSpecificData updateData = new BuildSpecificData(); + updateData.branch = data.get("branch").getAsString(); + + JsonObject affectedBuilds = data.getAsJsonObject("affected_builds"); + if (affectedBuilds.has("all")) { + updateData.allAffected = affectedBuilds.get("all").getAsBoolean(); + } + if (!updateData.allAffected) { + updateData.affectedGreaterThan = affectedBuilds.get("gt").getAsInt(); + updateData.affectedLessThan = affectedBuilds.get("lt").getAsInt(); + } + return updateData; + } + + public boolean isAffected(String branch, int buildId) { + return this.branch.equals(branch) && + (allAffected || buildId > affectedGreaterThan && buildId < affectedLessThan); + } + + public String getBranch() { + return branch; + } +} diff --git a/common/src/main/java/org/geysermc/floodgate/news/data/CheckAfterData.java b/common/src/main/java/org/geysermc/floodgate/news/data/CheckAfterData.java new file mode 100644 index 000000000..92d01689b --- /dev/null +++ b/common/src/main/java/org/geysermc/floodgate/news/data/CheckAfterData.java @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2019-2021 GeyserMC. http://geysermc.org + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * @author GeyserMC + * @link https://github.com/GeyserMC/Geyser + */ + +package org.geysermc.floodgate.news.data; + +import com.google.gson.JsonObject; + +public class CheckAfterData implements ItemData { + private long checkAfter; + + public static CheckAfterData read(JsonObject data) { + CheckAfterData checkAfterData = new CheckAfterData(); + checkAfterData.checkAfter = data.get("check_after").getAsLong(); + return checkAfterData; + } + + public long getCheckAfter() { + return checkAfter; + } +} diff --git a/common/src/main/java/org/geysermc/common/window/response/FormResponse.java b/common/src/main/java/org/geysermc/floodgate/news/data/ItemData.java similarity index 94% rename from common/src/main/java/org/geysermc/common/window/response/FormResponse.java rename to common/src/main/java/org/geysermc/floodgate/news/data/ItemData.java index 35023f167..122ee775d 100644 --- a/common/src/main/java/org/geysermc/common/window/response/FormResponse.java +++ b/common/src/main/java/org/geysermc/floodgate/news/data/ItemData.java @@ -23,7 +23,7 @@ * @link https://github.com/GeyserMC/Geyser */ -package org.geysermc.common.window.response; +package org.geysermc.floodgate.news.data; -public interface FormResponse { +public interface ItemData { } diff --git a/common/src/main/java/org/geysermc/floodgate/time/SntpClientUtils.java b/common/src/main/java/org/geysermc/floodgate/time/SntpClientUtils.java new file mode 100644 index 000000000..9e7f2c1b0 --- /dev/null +++ b/common/src/main/java/org/geysermc/floodgate/time/SntpClientUtils.java @@ -0,0 +1,91 @@ +/* + * Copyright (c) 2019-2021 GeyserMC. http://geysermc.org + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * @author GeyserMC + * @link https://github.com/GeyserMC/Geyser + */ + +package org.geysermc.floodgate.time; + +import java.net.DatagramPacket; +import java.net.DatagramSocket; +import java.net.InetAddress; +import java.nio.ByteBuffer; + +/* + * Thanks: + * https://datatracker.ietf.org/doc/html/rfc1769 + * https://github.com/jonsagara/SimpleNtpClient + * https://stackoverflow.com/a/29138806 + */ +public final class SntpClientUtils { + private static final int NTP_PORT = 123; + + private static final int NTP_PACKET_SIZE = 48; + private static final int NTP_MODE = 3; // client + private static final int NTP_VERSION = 3; + private static final int RECEIVE_TIME_POSITION = 32; + + private static final long NTP_TIME_OFFSET = ((365L * 70L) + 17L) * 24L * 60L * 60L; + + public static long requestTimeOffset(String host, int timeout) { + try (DatagramSocket socket = new DatagramSocket()) { + socket.setSoTimeout(timeout); + + InetAddress address = InetAddress.getByName(host); + + ByteBuffer buff = ByteBuffer.allocate(NTP_PACKET_SIZE); + + DatagramPacket request = new DatagramPacket( + buff.array(), NTP_PACKET_SIZE, address, NTP_PORT + ); + + // mode is in the least signification 3 bits + // version is in bits 3-5 + buff.put((byte) (NTP_MODE | (NTP_VERSION << 3))); + + long originateTime = System.currentTimeMillis(); + socket.send(request); + + DatagramPacket response = new DatagramPacket(buff.array(), NTP_PACKET_SIZE); + socket.receive(response); + + long responseTime = System.currentTimeMillis(); + + // everything before isn't important for us + buff.position(RECEIVE_TIME_POSITION); + + long receiveTime = readTimestamp(buff); + long transmitTime = readTimestamp(buff); + + return ((receiveTime - originateTime) + (transmitTime - responseTime)) / 2; + } catch (Exception ignored) { + } + return Long.MIN_VALUE; + } + + private static long readTimestamp(ByteBuffer buffer) { + //todo look into the ntp 2036 problem + long seconds = buffer.getInt() & 0xffffffffL; + long fraction = buffer.getInt() & 0xffffffffL; + return ((seconds - NTP_TIME_OFFSET) * 1000) + ((fraction * 1000) / 0x100000000L); + } +} diff --git a/common/src/main/java/org/geysermc/floodgate/time/TimeSyncer.java b/common/src/main/java/org/geysermc/floodgate/time/TimeSyncer.java new file mode 100644 index 000000000..3fe089e06 --- /dev/null +++ b/common/src/main/java/org/geysermc/floodgate/time/TimeSyncer.java @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2019-2021 GeyserMC. http://geysermc.org + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * @author GeyserMC + * @link https://github.com/GeyserMC/Geyser + */ + +package org.geysermc.floodgate.time; + +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.TimeUnit; + +public final class TimeSyncer { + private final ScheduledExecutorService executorService = Executors.newScheduledThreadPool(1); + private long timeOffset = Long.MIN_VALUE; // value when it failed to get the offset + + public TimeSyncer(String timeServer) { + executorService.scheduleWithFixedDelay(() -> { + // 5 tries to get the time offset, since UDP doesn't guaranty a response + for (int i = 0; i < 5; i++) { + long offset = SntpClientUtils.requestTimeOffset(timeServer, 3000); + if (offset != Long.MIN_VALUE) { + timeOffset = offset; + return; + } + } + }, 0, 30, TimeUnit.MINUTES); + } + + public void shutdown() { + executorService.shutdown(); + } + + public long getTimeOffset() { + return timeOffset; + } + + public long getRealMillis() { + if (hasUsefulOffset()) { + return System.currentTimeMillis() + getTimeOffset(); + } + return System.currentTimeMillis(); + } + + public boolean hasUsefulOffset() { + return timeOffset != Long.MIN_VALUE; + } +} diff --git a/common/src/main/java/org/geysermc/common/window/component/FormComponent.java b/common/src/main/java/org/geysermc/floodgate/util/Base64Utils.java similarity index 84% rename from common/src/main/java/org/geysermc/common/window/component/FormComponent.java rename to common/src/main/java/org/geysermc/floodgate/util/Base64Utils.java index 6b503eb49..326fa2590 100644 --- a/common/src/main/java/org/geysermc/common/window/component/FormComponent.java +++ b/common/src/main/java/org/geysermc/floodgate/util/Base64Utils.java @@ -23,16 +23,13 @@ * @link https://github.com/GeyserMC/Geyser */ -package org.geysermc.common.window.component; +package org.geysermc.floodgate.util; -import lombok.Getter; - -public abstract class FormComponent { - - @Getter - private final String type; - - public FormComponent(String type) { - this.type = type; +public final class Base64Utils { + public static int getEncodedLength(int length) { + if (length <= 0) { + return -1; + } + return 4 * ((length + 2) / 3); } } diff --git a/common/src/main/java/org/geysermc/floodgate/util/BedrockData.java b/common/src/main/java/org/geysermc/floodgate/util/BedrockData.java index 22c33f475..4f4325a9b 100644 --- a/common/src/main/java/org/geysermc/floodgate/util/BedrockData.java +++ b/common/src/main/java/org/geysermc/floodgate/util/BedrockData.java @@ -25,47 +25,91 @@ package org.geysermc.floodgate.util; -import lombok.AllArgsConstructor; +import lombok.AccessLevel; import lombok.Getter; +import lombok.RequiredArgsConstructor; +import org.geysermc.floodgate.time.TimeSyncer; -import java.util.UUID; - -@AllArgsConstructor +/** + * This class contains the raw data send by Geyser to Floodgate or from Floodgate to Floodgate. This + * class is only used internally, and you should look at FloodgatePlayer instead (FloodgatePlayer is + * present in the API module of the Floodgate repo) + */ @Getter -public class BedrockData { - public static final int EXPECTED_LENGTH = 7; - public static final String FLOODGATE_IDENTIFIER = "Geyser-Floodgate"; +@RequiredArgsConstructor(access = AccessLevel.PRIVATE) +public final class BedrockData implements Cloneable { + public static final int EXPECTED_LENGTH = 13; - private String version; - private String username; - private String xuid; - private int deviceId; - private String languageCode; - private int inputMode; - private String ip; - private int dataLength; + private final String version; + private final String username; + private final String xuid; + private final int deviceOs; + private final String languageCode; + private final int uiProfile; + private final int inputMode; + private final String ip; + private final LinkedPlayer linkedPlayer; + private final boolean fromProxy; - public BedrockData(String version, String username, String xuid, int deviceId, String languageCode, int inputMode, String ip) { - this(version, username, xuid, deviceId, languageCode, inputMode, ip, EXPECTED_LENGTH); + private final int subscribeId; + private final String verifyCode; + + private final long timestamp; + private final int dataLength; + + public static BedrockData of( + String version, String username, String xuid, int deviceOs, + String languageCode, int uiProfile, int inputMode, String ip, + LinkedPlayer linkedPlayer, boolean fromProxy, int subscribeId, + String verifyCode, TimeSyncer timeSyncer) { + return new BedrockData(version, username, xuid, deviceOs, languageCode, inputMode, + uiProfile, ip, linkedPlayer, fromProxy, subscribeId, verifyCode, + timeSyncer.getRealMillis(), EXPECTED_LENGTH); + } + + public static BedrockData of( + String version, String username, String xuid, int deviceOs, + String languageCode, int uiProfile, int inputMode, String ip, + int subscribeId, String verifyCode, TimeSyncer timeSyncer) { + return of(version, username, xuid, deviceOs, languageCode, uiProfile, inputMode, ip, null, + false, subscribeId, verifyCode, timeSyncer); } public static BedrockData fromString(String data) { String[] split = data.split("\0"); - if (split.length != EXPECTED_LENGTH) return null; + if (split.length != EXPECTED_LENGTH) { + return emptyData(split.length); + } + LinkedPlayer linkedPlayer = LinkedPlayer.fromString(split[8]); + // The format is the same as the order of the fields in this class return new BedrockData( - split[0], split[1], split[2], Integer.parseInt(split[3]), - split[4], Integer.parseInt(split[5]), split[6], split.length + split[0], split[1], split[2], Integer.parseInt(split[3]), split[4], + Integer.parseInt(split[5]), Integer.parseInt(split[6]), split[7], linkedPlayer, + "1".equals(split[9]), Integer.parseInt(split[10]), split[11], Long.parseLong(split[12]), split.length ); } - public static BedrockData fromRawData(byte[] data) { - return fromString(new String(data)); + private static BedrockData emptyData(int dataLength) { + return new BedrockData(null, null, null, -1, null, -1, -1, null, null, false, -1, null, -1, + dataLength); + } + + public boolean hasPlayerLink() { + return linkedPlayer != null; } @Override public String toString() { - return version +'\0'+ username +'\0'+ xuid +'\0'+ deviceId +'\0'+ languageCode +'\0'+ - inputMode +'\0'+ ip; + // The format is the same as the order of the fields in this class + return version + '\0' + username + '\0' + xuid + '\0' + deviceOs + '\0' + + languageCode + '\0' + uiProfile + '\0' + inputMode + '\0' + ip + '\0' + + (linkedPlayer != null ? linkedPlayer.toString() : "null") + '\0' + + (fromProxy ? 1 : 0) + '\0' + subscribeId + '\0' + verifyCode + '\0' + timestamp; + } + + @Override + public BedrockData clone() throws CloneNotSupportedException { + return (BedrockData) super.clone(); } } diff --git a/common/src/main/java/org/geysermc/floodgate/util/DeviceOS.java b/common/src/main/java/org/geysermc/floodgate/util/DeviceOs.java similarity index 70% rename from common/src/main/java/org/geysermc/floodgate/util/DeviceOS.java rename to common/src/main/java/org/geysermc/floodgate/util/DeviceOs.java index 1e0b22cf6..714af6f1f 100644 --- a/common/src/main/java/org/geysermc/floodgate/util/DeviceOS.java +++ b/common/src/main/java/org/geysermc/floodgate/util/DeviceOs.java @@ -25,36 +25,41 @@ package org.geysermc.floodgate.util; -import com.fasterxml.jackson.annotation.JsonEnumDefaultValue; +import lombok.AccessLevel; +import lombok.RequiredArgsConstructor; -public enum DeviceOS { - - @JsonEnumDefaultValue +/** + * The Operation Systems where Bedrock players can connect with + */ +@RequiredArgsConstructor(access = AccessLevel.PRIVATE) +public enum DeviceOs { UNKNOWN("Unknown"), - ANDROID("Android"), + GOOGLE("Android"), IOS("iOS"), OSX("macOS"), - FIREOS("FireOS"), + AMAZON("Amazon"), GEARVR("Gear VR"), HOLOLENS("Hololens"), - WIN10("Windows 10"), - WIN32("Windows"), + UWP("Windows 10"), + WIN32("Windows x86"), DEDICATED("Dedicated"), - ORBIS("PS4"), + TVOS("Apple TV"), + PS4("PS4"), NX("Switch"), - SWITCH("Switch"), - XBOX_ONE("Xbox One"), - WIN_PHONE("Windows Phone"); + XBOX("Xbox One"), + WINDOWS_PHONE("Windows Phone"); - private static final DeviceOS[] VALUES = values(); + private static final DeviceOs[] VALUES = values(); private final String displayName; - DeviceOS(final String displayName) { - this.displayName = displayName; - } - - public static DeviceOS getById(int id) { + /** + * Get the DeviceOs instance from the identifier. + * + * @param id the DeviceOs identifier + * @return The DeviceOs or {@link #UNKNOWN} if the DeviceOs wasn't found + */ + public static DeviceOs getById(int id) { return id < VALUES.length ? VALUES[id] : VALUES[0]; } diff --git a/common/src/main/java/org/geysermc/floodgate/util/EncryptionUtil.java b/common/src/main/java/org/geysermc/floodgate/util/EncryptionUtil.java deleted file mode 100644 index f4dd5d2e3..000000000 --- a/common/src/main/java/org/geysermc/floodgate/util/EncryptionUtil.java +++ /dev/null @@ -1,101 +0,0 @@ -/* - * Copyright (c) 2019-2021 GeyserMC. http://geysermc.org - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - * - * @author GeyserMC - * @link https://github.com/GeyserMC/Geyser - */ - -package org.geysermc.floodgate.util; - -import javax.crypto.*; -import javax.crypto.spec.SecretKeySpec; -import java.io.IOException; -import java.nio.file.Files; -import java.nio.file.Path; -import java.security.*; -import java.security.spec.EncodedKeySpec; -import java.security.spec.InvalidKeySpecException; -import java.security.spec.PKCS8EncodedKeySpec; -import java.security.spec.X509EncodedKeySpec; -import java.util.Base64; - -public class EncryptionUtil { - public static String encrypt(Key key, String data) throws IllegalBlockSizeException, - InvalidKeyException, BadPaddingException, NoSuchAlgorithmException, NoSuchPaddingException { - KeyGenerator generator = KeyGenerator.getInstance("AES"); - generator.init(128); - SecretKey secretKey = generator.generateKey(); - - Cipher cipher = Cipher.getInstance("AES"); - cipher.init(Cipher.ENCRYPT_MODE, secretKey); - byte[] encryptedText = cipher.doFinal(data.getBytes()); - - cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding"); - cipher.init(key instanceof PublicKey ? Cipher.PUBLIC_KEY : Cipher.PRIVATE_KEY, key); - return Base64.getEncoder().encodeToString(cipher.doFinal(secretKey.getEncoded())) + '\0' + - Base64.getEncoder().encodeToString(encryptedText); - } - - public static String encryptBedrockData(Key key, BedrockData data) throws IllegalBlockSizeException, - InvalidKeyException, BadPaddingException, NoSuchAlgorithmException, NoSuchPaddingException { - return encrypt(key, data.toString()); - } - - public static byte[] decrypt(Key key, String encryptedData) throws IllegalBlockSizeException, - InvalidKeyException, BadPaddingException, NoSuchAlgorithmException, NoSuchPaddingException { - String[] split = encryptedData.split("\0"); - if (split.length != 2) { - throw new IllegalArgumentException("Expected two arguments, got " + split.length); - } - - Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding"); - cipher.init(key instanceof PublicKey ? Cipher.PUBLIC_KEY : Cipher.PRIVATE_KEY, key); - byte[] decryptedKey = cipher.doFinal(Base64.getDecoder().decode(split[0])); - - SecretKey secretKey = new SecretKeySpec(decryptedKey, 0, decryptedKey.length, "AES"); - cipher = Cipher.getInstance("AES"); - cipher.init(Cipher.DECRYPT_MODE, secretKey); - return cipher.doFinal(Base64.getDecoder().decode(split[1])); - } - - public static BedrockData decryptBedrockData(Key key, String encryptedData) throws IllegalBlockSizeException, - InvalidKeyException, BadPaddingException, NoSuchAlgorithmException, NoSuchPaddingException { - return BedrockData.fromRawData(decrypt(key, encryptedData)); - } - - @SuppressWarnings("unchecked") - public static T getKeyFromFile(Path fileLocation, Class keyType) throws - IOException, InvalidKeySpecException, NoSuchAlgorithmException { - boolean isPublicKey = keyType == PublicKey.class; - if (!isPublicKey && keyType != PrivateKey.class) { - throw new RuntimeException("I can only read public and private keys!"); - } - - byte[] key = Files.readAllBytes(fileLocation); - - KeyFactory keyFactory = KeyFactory.getInstance("RSA"); - EncodedKeySpec keySpec = isPublicKey ? new X509EncodedKeySpec(key) : new PKCS8EncodedKeySpec(key); - return (T) (isPublicKey ? - keyFactory.generatePublic(keySpec) : - keyFactory.generatePrivate(keySpec) - ); - } -} diff --git a/common/src/main/java/org/geysermc/common/window/component/LabelComponent.java b/common/src/main/java/org/geysermc/floodgate/util/FloodgateInfoHolder.java similarity index 85% rename from common/src/main/java/org/geysermc/common/window/component/LabelComponent.java rename to common/src/main/java/org/geysermc/floodgate/util/FloodgateInfoHolder.java index 4608282fc..c7a681f9d 100644 --- a/common/src/main/java/org/geysermc/common/window/component/LabelComponent.java +++ b/common/src/main/java/org/geysermc/floodgate/util/FloodgateInfoHolder.java @@ -23,20 +23,18 @@ * @link https://github.com/GeyserMC/Geyser */ -package org.geysermc.common.window.component; +package org.geysermc.floodgate.util; import lombok.Getter; import lombok.Setter; -public class LabelComponent extends FormComponent { +import java.util.Properties; +public final class FloodgateInfoHolder { @Getter @Setter - private String text; - - public LabelComponent(String text) { - super("label"); - - this.text = text; - } + private static Object config; + @Getter + @Setter + private static Properties gitProperties; } diff --git a/common/src/main/java/org/geysermc/floodgate/util/InputMode.java b/common/src/main/java/org/geysermc/floodgate/util/InputMode.java new file mode 100644 index 000000000..d49d2ea84 --- /dev/null +++ b/common/src/main/java/org/geysermc/floodgate/util/InputMode.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.floodgate.util; + +public enum InputMode { + UNKNOWN, + KEYBOARD_MOUSE, + TOUCH, + CONTROLLER, + VR; + + private static final InputMode[] VALUES = values(); + + /** + * Get the InputMode instance from the identifier. + * + * @param id the InputMode identifier + * @return The InputMode or {@link #UNKNOWN} if the DeviceOs wasn't found + */ + public static InputMode getById(int id) { + return VALUES.length > id ? VALUES[id] : VALUES[0]; + } +} \ No newline at end of file diff --git a/common/src/main/java/org/geysermc/floodgate/util/InvalidFormatException.java b/common/src/main/java/org/geysermc/floodgate/util/InvalidFormatException.java new file mode 100644 index 000000000..9ec5b1710 --- /dev/null +++ b/common/src/main/java/org/geysermc/floodgate/util/InvalidFormatException.java @@ -0,0 +1,51 @@ +/* + * 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.floodgate.util; + +import lombok.Getter; + +@Getter +public class InvalidFormatException extends Exception { + private boolean header = false; + + public InvalidFormatException() { + super(); + } + + public InvalidFormatException(String message) { + super(message); + } + + public InvalidFormatException(String message, boolean header) { + super(message); + this.header = header; + } + + public InvalidFormatException(String message, Throwable cause) { + super(message, cause); + } +} diff --git a/common/src/main/java/org/geysermc/floodgate/util/LinkedPlayer.java b/common/src/main/java/org/geysermc/floodgate/util/LinkedPlayer.java new file mode 100644 index 000000000..f91bfafbc --- /dev/null +++ b/common/src/main/java/org/geysermc/floodgate/util/LinkedPlayer.java @@ -0,0 +1,82 @@ +/* + * 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.floodgate.util; + +import lombok.AccessLevel; +import lombok.Getter; +import lombok.RequiredArgsConstructor; + +import java.util.UUID; + +@Getter +@RequiredArgsConstructor(access = AccessLevel.PRIVATE) +public final class LinkedPlayer implements Cloneable { + /** + * The Java username of the linked player + */ + private final String javaUsername; + /** + * The Java UUID of the linked player + */ + private final UUID javaUniqueId; + /** + * The UUID of the Bedrock player + */ + private final UUID bedrockId; + /** + * If the LinkedPlayer is sent from a different platform. For example the LinkedPlayer is from + * Bungee but the data has been sent to the Bukkit server. + */ + private boolean fromDifferentPlatform = false; + + public static LinkedPlayer of(String javaUsername, UUID javaUniqueId, UUID bedrockId) { + return new LinkedPlayer(javaUsername, javaUniqueId, bedrockId); + } + + public static LinkedPlayer fromString(String data) { + String[] split = data.split(";"); + if (split.length != 3) { + return null; + } + + LinkedPlayer player = new LinkedPlayer( + split[0], UUID.fromString(split[1]), UUID.fromString(split[2]) + ); + player.fromDifferentPlatform = true; + return player; + } + + @Override + public String toString() { + return javaUsername + ';' + javaUniqueId.toString() + ';' + bedrockId.toString(); + } + + @Override + public LinkedPlayer clone() throws CloneNotSupportedException { + return (LinkedPlayer) super.clone(); + } +} diff --git a/common/src/main/java/org/geysermc/floodgate/util/UiProfile.java b/common/src/main/java/org/geysermc/floodgate/util/UiProfile.java new file mode 100644 index 000000000..af1121f3c --- /dev/null +++ b/common/src/main/java/org/geysermc/floodgate/util/UiProfile.java @@ -0,0 +1,44 @@ +/* + * 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.floodgate.util; + +public enum UiProfile { + CLASSIC, + POCKET; + + private static final UiProfile[] VALUES = values(); + + /** + * Get the UiProfile instance from the identifier. + * + * @param id the UiProfile identifier + * @return The UiProfile or {@link #CLASSIC} if the UiProfile wasn't found + */ + public static UiProfile getById(int id) { + return VALUES.length > id ? VALUES[id] : VALUES[0]; + } +} diff --git a/common/src/main/java/org/geysermc/floodgate/util/WebsocketEventType.java b/common/src/main/java/org/geysermc/floodgate/util/WebsocketEventType.java new file mode 100644 index 000000000..f24dc8145 --- /dev/null +++ b/common/src/main/java/org/geysermc/floodgate/util/WebsocketEventType.java @@ -0,0 +1,91 @@ +/* + * Copyright (c) 2019-2021 GeyserMC. http://geysermc.org + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * @author GeyserMC + * @link https://github.com/GeyserMC/Geyser + */ + +package org.geysermc.floodgate.util; + +public enum WebsocketEventType { + /** + * Sent once we successfully connected to the server + */ + SUBSCRIBER_CREATED(0), + /** + * Sent every time a subscriber got added or disconnected + */ + SUBSCRIBER_COUNT(1), + /** + * Sent once the creator disconnected. After this packet the server will automatically close the + * connection once the queue size (sent in {@link #ADDED_TO_QUEUE} and {@link #SKIN_UPLOADED} + * reaches 0. + */ + CREATOR_DISCONNECTED(4), + + /** + * Sent every time a skin got added to the upload queue + */ + ADDED_TO_QUEUE(2), + /** + * Sent every time a skin got successfully uploaded + */ + SKIN_UPLOADED(3), + + /** + * Sent every time a news item was added + */ + NEWS_ADDED(6), + + /** + * Sent when the server wants you to know something. Currently used for violations that aren't + * bad enough to close the connection + */ + LOG_MESSAGE(5); + + private static final WebsocketEventType[] VALUES; + + static { + WebsocketEventType[] values = values(); + VALUES = new WebsocketEventType[values.length]; + for (WebsocketEventType value : values) { + VALUES[value.id] = value; + } + } + + /** + * The ID is based of the time it got added. However, to keep the enum organized as time goes on, + * it looks nicer to sort the events based of categories. + */ + private final int id; + + WebsocketEventType(int id) { + this.id = id; + } + + public static WebsocketEventType getById(int id) { + return VALUES.length > id ? VALUES[id] : null; + } + + public int getId() { + return id; + } +} diff --git a/connector/pom.xml b/connector/pom.xml index ba0f0cc39..30fec659c 100644 --- a/connector/pom.xml +++ b/connector/pom.xml @@ -6,33 +6,59 @@ org.geysermc geyser-parent - 1.2.1-SNAPSHOT + 1.4.0-SNAPSHOT connector - 4.1.59.Final + 4.8.0 8.5.2 - 4.7.0 + 2.10.2 + 4.1.59.Final org.geysermc common - 1.2.1-SNAPSHOT + 1.4.0-SNAPSHOT + compile + + + + com.fasterxml.jackson.core + jackson-annotations + ${jackson.version} + compile + + + com.fasterxml.jackson.core + jackson-core + ${jackson.version} + compile + + + com.fasterxml.jackson.core + jackson-databind + ${jackson.version} compile com.fasterxml.jackson.dataformat jackson-dataformat-yaml - 2.10.2 + ${jackson.version} + compile + + + org.java-websocket + Java-WebSocket + 1.5.1 compile com.github.CloudburstMC.Protocol - bedrock-v431 - 530a0e3 + bedrock-v440 + 1656151 compile @@ -120,9 +146,15 @@ compile - com.github.steveice10 - mcprotocollib - 8c204eb + com.github.GeyserMC + MCAuthLib + 0e48a094f2 + compile + + + com.github.GeyserMC + MCProtocolLib + e316986 compile @@ -203,12 +235,15 @@ org.reflections reflections 0.9.11 + compile org.dom4j dom4j 2.1.3 + compile + net.kyori adventure-api @@ -239,11 +274,6 @@ 4.13.1 test - - com.github.GeyserMC - MCAuthLib - 0e48a094f2 - @@ -305,6 +335,7 @@ String GIT_VERSION = ".*" + String GIT_VERSION = "git-${git.branch}-${git.commit.id.abbrev}" diff --git a/connector/src/main/java/org/geysermc/connector/FloodgateKeyLoader.java b/connector/src/main/java/org/geysermc/connector/FloodgateKeyLoader.java index 77492fb7a..7f5bca8c2 100644 --- a/connector/src/main/java/org/geysermc/connector/FloodgateKeyLoader.java +++ b/connector/src/main/java/org/geysermc/connector/FloodgateKeyLoader.java @@ -33,11 +33,20 @@ import java.nio.file.Path; public class FloodgateKeyLoader { public static Path getKeyPath(GeyserJacksonConfiguration config, Object floodgate, Path floodgateDataFolder, Path geyserDataFolder, GeyserLogger logger) { + if (!config.getRemote().getAuthType().equals("floodgate")) { + return geyserDataFolder.resolve(config.getFloodgateKeyFile()); + } + Path floodgateKey = geyserDataFolder.resolve(config.getFloodgateKeyFile()); - if (!Files.exists(floodgateKey) && config.getRemote().getAuthType().equals("floodgate")) { + if (config.getFloodgateKeyFile().equals("public-key.pem")) { + logger.info("Floodgate 2.0 doesn't use a public/private key system anymore. We'll search for key.pem instead"); + floodgateKey = geyserDataFolder.resolve("key.pem"); + } + + if (!Files.exists(floodgateKey)) { if (floodgate != null) { - Path autoKey = floodgateDataFolder.resolve("public-key.pem"); + Path autoKey = floodgateDataFolder.resolve("key.pem"); if (Files.exists(autoKey)) { logger.info(LanguageUtils.getLocaleStringLog("geyser.bootstrap.floodgate.auto_loaded")); floodgateKey = autoKey; diff --git a/connector/src/main/java/org/geysermc/connector/GeyserConnector.java b/connector/src/main/java/org/geysermc/connector/GeyserConnector.java index c93cd4e9d..ac77388ff 100644 --- a/connector/src/main/java/org/geysermc/connector/GeyserConnector.java +++ b/connector/src/main/java/org/geysermc/connector/GeyserConnector.java @@ -28,6 +28,7 @@ package org.geysermc.connector; import com.fasterxml.jackson.core.JsonParser; import com.fasterxml.jackson.databind.DeserializationFeature; import com.fasterxml.jackson.databind.ObjectMapper; +import com.github.steveice10.mc.protocol.MinecraftConstants; import com.nukkitx.network.raknet.RakNetConstants; import com.nukkitx.network.util.EventLoops; import com.nukkitx.protocol.bedrock.BedrockServer; @@ -58,7 +59,14 @@ import org.geysermc.connector.network.translators.world.WorldManager; import org.geysermc.connector.network.translators.world.block.BlockTranslator; import org.geysermc.connector.network.translators.world.block.entity.BlockEntityTranslator; import org.geysermc.connector.network.translators.world.block.entity.SkullBlockEntityTranslator; +import org.geysermc.connector.skin.FloodgateSkinUploader; import org.geysermc.connector.utils.*; +import org.geysermc.floodgate.crypto.AesCipher; +import org.geysermc.floodgate.crypto.AesKeyProducer; +import org.geysermc.floodgate.crypto.Base64Topping; +import org.geysermc.floodgate.crypto.FloodgateCipher; +import org.geysermc.floodgate.news.NewsItemAction; +import org.geysermc.floodgate.time.TimeSyncer; import org.jetbrains.annotations.Contract; import javax.naming.directory.Attribute; @@ -66,16 +74,18 @@ import javax.naming.directory.InitialDirContext; import java.net.InetAddress; import java.net.InetSocketAddress; import java.net.UnknownHostException; +import java.security.Key; import java.text.DecimalFormat; import java.util.*; import java.util.concurrent.CompletableFuture; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; +import java.util.regex.Matcher; +import java.util.regex.Pattern; @Getter public class GeyserConnector { - public static final ObjectMapper JSON_MAPPER = new ObjectMapper() .enable(JsonParser.Feature.IGNORE_UNDEFINED) .enable(JsonParser.Feature.ALLOW_COMMENTS) @@ -86,7 +96,7 @@ public class GeyserConnector { public static final String NAME = "Geyser"; public static final String GIT_VERSION = "DEV"; // A fallback for running in IDEs public static final String VERSION = "DEV"; // A fallback for running in IDEs - public static final String MINECRAFT_VERSION = "1.16.4 - 1.16.5"; + public static final String MINECRAFT_VERSION = MinecraftConstants.GAME_VERSION; // Change if multiple version strings are supported /** * Oauth client ID for Microsoft authentication @@ -108,11 +118,16 @@ public class GeyserConnector { @Setter private AuthType defaultAuthType; + private final TimeSyncer timeSyncer; + private FloodgateCipher cipher; + private FloodgateSkinUploader skinUploader; + private final NewsHandler newsHandler; + private boolean shuttingDown = false; private final ScheduledExecutorService generalThreadPool; - private BedrockServer bedrockServer; + private final BedrockServer bedrockServer; private final PlatformType platformType; private final GeyserBootstrap bootstrap; @@ -196,6 +211,36 @@ public class GeyserConnector { defaultAuthType = AuthType.getByName(config.getRemote().getAuthType()); + TimeSyncer timeSyncer = null; + if (defaultAuthType == AuthType.FLOODGATE) { + timeSyncer = new TimeSyncer(Constants.NTP_SERVER); + try { + Key key = new AesKeyProducer().produceFrom(config.getFloodgateKeyPath()); + cipher = new AesCipher(new Base64Topping()); + cipher.init(key); + logger.info(LanguageUtils.getLocaleStringLog("geyser.auth.floodgate.loaded_key")); + skinUploader = new FloodgateSkinUploader(this).start(); + } catch (Exception exception) { + logger.severe(LanguageUtils.getLocaleStringLog("geyser.auth.floodgate.bad_key"), exception); + } + } + this.timeSyncer = timeSyncer; + + String branch = "unknown"; + int buildNumber = -1; + try { + Properties gitProperties = new Properties(); + gitProperties.load(FileUtils.getResource("git.properties")); + branch = gitProperties.getProperty("git.branch"); + String build = gitProperties.getProperty("git.build.number"); + if (build != null) { + buildNumber = Integer.parseInt(build); + } + } catch (Throwable e) { + logger.error("Failed to read git.properties", e); + } + newsHandler = new NewsHandler(branch, buildNumber); + CooldownUtils.setDefaultShowCooldown(config.getShowCooldown()); DimensionUtils.changeBedrockNetherId(config.isAboveBedrockNetherBuilding()); // Apply End dimension ID workaround to Nether SkullBlockEntityTranslator.ALLOW_CUSTOM_SKULLS = config.isAllowCustomSkulls(); @@ -250,7 +295,7 @@ public class GeyserConnector { for (GeyserSession session : players) { if (session == null) continue; if (session.getClientData() == null) continue; - String os = session.getClientData().getDeviceOS().toString(); + String os = session.getClientData().getDeviceOs().toString(); if (!valueMap.containsKey(os)) { valueMap.put(os, 1); } else { @@ -287,6 +332,40 @@ public class GeyserConnector { return versionMap; })); } + + // The following code can be attributed to the PaperMC project + // https://github.com/PaperMC/Paper/blob/master/Spigot-Server-Patches/0005-Paper-Metrics.patch#L614 + metrics.addCustomChart(new Metrics.DrilldownPie("javaVersion", () -> { + Map> map = new HashMap<>(); + String javaVersion = System.getProperty("java.version"); + Map entry = new HashMap<>(); + entry.put(javaVersion, 1); + + // http://openjdk.java.net/jeps/223 + // Java decided to change their versioning scheme and in doing so modified the + // java.version system property to return $major[.$minor][.$security][-ea], as opposed to + // 1.$major.0_$identifier we can handle pre-9 by checking if the "major" is equal to "1", + // otherwise, 9+ + String majorVersion = javaVersion.split("\\.")[0]; + String release; + + int indexOf = javaVersion.lastIndexOf('.'); + + if (majorVersion.equals("1")) { + release = "Java " + javaVersion.substring(0, indexOf); + } else { + // of course, it really wouldn't be all that simple if they didn't add a quirk, now + // would it valid strings for the major may potentially include values such as -ea to + // denote a pre release + Matcher versionMatcher = Pattern.compile("\\d+").matcher(majorVersion); + if (versionMatcher.find()) { + majorVersion = versionMatcher.group(0); + } + release = "Java " + majorVersion; + } + map.put(release, entry); + return map; + })); } boolean isGui = false; @@ -312,6 +391,8 @@ public class GeyserConnector { if (platformType == PlatformType.STANDALONE) { logger.warning(LanguageUtils.getLocaleStringLog("geyser.core.movement_warn")); } + + newsHandler.handleNews(null, NewsItemAction.ON_SERVER_STARTED); } public void shutdown() { @@ -356,6 +437,10 @@ public class GeyserConnector { generalThreadPool.shutdown(); bedrockServer.close(); + if (timeSyncer != null) { + timeSyncer.shutdown(); + } + newsHandler.shutdown(); players.clear(); defaultAuthType = null; this.getCommandManager().getCommands().clear(); @@ -434,6 +519,10 @@ public class GeyserConnector { return bootstrap.getWorldManager(); } + public TimeSyncer getTimeSyncer() { + return timeSyncer; + } + /** * Whether to use XML reflections in the jar or manually find the reflections. * Will return true if the version number is not 'DEV' and the platform is not Fabric. diff --git a/connector/src/main/java/org/geysermc/connector/command/CommandManager.java b/connector/src/main/java/org/geysermc/connector/command/CommandManager.java index 9f675ae81..f62fd8539 100644 --- a/connector/src/main/java/org/geysermc/connector/command/CommandManager.java +++ b/connector/src/main/java/org/geysermc/connector/command/CommandManager.java @@ -53,7 +53,7 @@ public abstract class CommandManager { registerCommand(new VersionCommand(connector, "version", "geyser.commands.version.desc", "geyser.command.version")); registerCommand(new SettingsCommand(connector, "settings", "geyser.commands.settings.desc", "geyser.command.settings")); registerCommand(new StatisticsCommand(connector, "statistics", "geyser.commands.statistics.desc", "geyser.command.statistics")); - registerCommand(new AdvancementsCommand(connector, "advancements", "geyser.commands.advancements.desc", "geyser.command.advancements")); + registerCommand(new AdvancementsCommand("advancements", "geyser.commands.advancements.desc", "geyser.command.advancements")); } public void registerCommand(GeyserCommand command) { diff --git a/connector/src/main/java/org/geysermc/connector/command/defaults/AdvancementsCommand.java b/connector/src/main/java/org/geysermc/connector/command/defaults/AdvancementsCommand.java index 2ef23381b..8ace83840 100644 --- a/connector/src/main/java/org/geysermc/connector/command/defaults/AdvancementsCommand.java +++ b/connector/src/main/java/org/geysermc/connector/command/defaults/AdvancementsCommand.java @@ -25,25 +25,20 @@ package org.geysermc.connector.command.defaults; -import org.geysermc.common.window.SimpleFormWindow; -import org.geysermc.connector.GeyserConnector; import org.geysermc.connector.command.CommandSender; import org.geysermc.connector.command.GeyserCommand; import org.geysermc.connector.network.session.GeyserSession; -import org.geysermc.connector.network.session.cache.AdvancementsCache; public class AdvancementsCommand extends GeyserCommand { - - public AdvancementsCommand(GeyserConnector connector, String name, String description, String permission) { + public AdvancementsCommand(String name, String description, String permission) { super(name, description, permission); } @Override public void execute(GeyserSession session, CommandSender sender, String[] args) { - if (session == null) return; - - SimpleFormWindow window = session.getAdvancementsCache().buildMenuForm(); - session.sendForm(window, AdvancementsCache.ADVANCEMENTS_MENU_FORM_ID); + if (session != null) { + session.getAdvancementsCache().buildAndShowMenuForm(); + } } @Override 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 2f1c7dc9b..d3f3d2f3f 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 @@ -32,6 +32,8 @@ import org.geysermc.connector.command.GeyserCommand; import org.geysermc.connector.network.session.GeyserSession; import org.geysermc.connector.utils.LanguageUtils; +import java.util.ArrayList; + public class ReloadCommand extends GeyserCommand { private final GeyserConnector connector; @@ -51,8 +53,8 @@ public class ReloadCommand extends GeyserCommand { sender.sendMessage(message); - for (GeyserSession otherSession : connector.getPlayers()) { - otherSession.disconnect(LanguageUtils.getPlayerLocaleString("geyser.commands.reload.kick", session.getLocale())); + for (GeyserSession otherSession : new ArrayList<>(connector.getPlayers())) { + otherSession.disconnect(LanguageUtils.getPlayerLocaleString("geyser.commands.reload.kick", otherSession.getLocale())); } connector.reload(); } diff --git a/connector/src/main/java/org/geysermc/connector/command/defaults/SettingsCommand.java b/connector/src/main/java/org/geysermc/connector/command/defaults/SettingsCommand.java index 2aeee1377..0f85e3c8f 100644 --- a/connector/src/main/java/org/geysermc/connector/command/defaults/SettingsCommand.java +++ b/connector/src/main/java/org/geysermc/connector/command/defaults/SettingsCommand.java @@ -32,17 +32,15 @@ import org.geysermc.connector.network.session.GeyserSession; import org.geysermc.connector.utils.SettingsUtils; public class SettingsCommand extends GeyserCommand { - public SettingsCommand(GeyserConnector connector, String name, String description, String permission) { super(name, description, permission); } @Override public void execute(GeyserSession session, CommandSender sender, String[] args) { - if (session == null) return; - - SettingsUtils.buildForm(session); - session.sendForm(session.getSettingsForm(), SettingsUtils.SETTINGS_FORM_ID); + if (session != null) { + session.sendForm(SettingsUtils.buildForm(session)); + } } @Override 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 4ebc048fe..5b910dc68 100644 --- a/connector/src/main/java/org/geysermc/connector/configuration/GeyserConfiguration.java +++ b/connector/src/main/java/org/geysermc/connector/configuration/GeyserConfiguration.java @@ -87,8 +87,6 @@ public interface GeyserConfiguration { boolean isAboveBedrockNetherBuilding(); - boolean isCacheChunks(); - boolean isForceResourcePacks(); boolean isXboxAchievementsEnabled(); 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 9a3024de7..5840561c6 100644 --- a/connector/src/main/java/org/geysermc/connector/configuration/GeyserJacksonConfiguration.java +++ b/connector/src/main/java/org/geysermc/connector/configuration/GeyserJacksonConfiguration.java @@ -111,9 +111,6 @@ public abstract class GeyserJacksonConfiguration implements GeyserConfiguration @JsonProperty("default-locale") private String defaultLocale = null; // is null by default so system language takes priority - @JsonProperty("cache-chunks") - private boolean cacheChunks = false; - @JsonProperty("cache-images") private int cacheImages = 0; 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 b26d117f9..afcbda6a4 100644 --- a/connector/src/main/java/org/geysermc/connector/dump/BootstrapDumpInfo.java +++ b/connector/src/main/java/org/geysermc/connector/dump/BootstrapDumpInfo.java @@ -34,8 +34,7 @@ import java.util.List; @Getter public class BootstrapDumpInfo { - - private PlatformType platform; + private final PlatformType platform; public BootstrapDumpInfo() { this.platform = GeyserConnector.getInstance().getPlatformType(); @@ -44,7 +43,6 @@ public class BootstrapDumpInfo { @Getter @AllArgsConstructor public static class PluginInfo { - public boolean enabled; public String name; public String version; @@ -55,7 +53,6 @@ public class BootstrapDumpInfo { @Getter @AllArgsConstructor public static class ListenerInfo { - public String ip; public int port; } diff --git a/connector/src/main/java/org/geysermc/connector/dump/DumpInfo.java b/connector/src/main/java/org/geysermc/connector/dump/DumpInfo.java index d8d5d7218..fee4ac7a4 100644 --- a/connector/src/main/java/org/geysermc/connector/dump/DumpInfo.java +++ b/connector/src/main/java/org/geysermc/connector/dump/DumpInfo.java @@ -41,7 +41,8 @@ import org.geysermc.connector.network.BedrockProtocol; import org.geysermc.connector.network.session.GeyserSession; import org.geysermc.connector.utils.DockerCheck; import org.geysermc.connector.utils.FileUtils; -import org.geysermc.floodgate.util.DeviceOS; +import org.geysermc.floodgate.util.DeviceOs; +import org.geysermc.floodgate.util.FloodgateInfoHolder; import java.io.File; import java.io.IOException; @@ -53,27 +54,29 @@ import java.util.Properties; @Getter public class DumpInfo { - @JsonIgnore private static final long MEGABYTE = 1024L * 1024L; private final DumpInfo.VersionInfo versionInfo; private Properties gitInfo; private final GeyserConfiguration config; + private final Floodgate floodgate; + private final Object2IntMap userPlatforms; private final HashInfo hashInfo; - private final Object2IntMap userPlatforms; private final RamInfo ramInfo; private final BootstrapDumpInfo bootstrapInfo; public DumpInfo() { - this.versionInfo = new DumpInfo.VersionInfo(); + this.versionInfo = new VersionInfo(); try { this.gitInfo = new Properties(); this.gitInfo.load(FileUtils.getResource("git.properties")); - } catch (IOException ignored) { } + } catch (IOException ignored) { + } this.config = GeyserConnector.getInstance().getConfig(); + this.floodgate = new Floodgate(); String md5Hash = "unknown"; String sha256Hash = "unknown"; @@ -92,14 +95,13 @@ public class DumpInfo { e.printStackTrace(); } } - this.hashInfo = new HashInfo(md5Hash, sha256Hash); this.ramInfo = new DumpInfo.RamInfo(); this.userPlatforms = new Object2IntOpenHashMap<>(); for (GeyserSession session : GeyserConnector.getInstance().getPlayers()) { - DeviceOS device = session.getClientData().getDeviceOS(); + DeviceOs device = session.getClientData().getDeviceOs(); userPlatforms.put(device, userPlatforms.getOrDefault(device, 0) + 1); } @@ -108,7 +110,6 @@ public class DumpInfo { @Getter public static class VersionInfo { - private final String name; private final String version; private final String javaVersion; @@ -123,7 +124,8 @@ public class DumpInfo { this.name = GeyserConnector.NAME; this.version = GeyserConnector.VERSION; this.javaVersion = System.getProperty("java.version"); - this.architecture = System.getProperty("os.arch"); // Usually gives Java architecture but still may be helpful. + // Usually gives Java architecture but still may be helpful. + this.architecture = System.getProperty("os.arch"); this.operatingSystem = System.getProperty("os.name"); this.operatingSystemVersion = System.getProperty("os.version"); @@ -132,18 +134,10 @@ public class DumpInfo { } } - @AllArgsConstructor - @Getter - public static class HashInfo { - private final String md5Hash; - private final String sha256Hash; - } - @Getter public static class NetworkInfo { - - private String internalIP; private final boolean dockerCheck; + private String internalIP; NetworkInfo() { if (AsteriskSerializer.showSensitive) { @@ -156,7 +150,8 @@ public class DumpInfo { try { // Fallback to the normal way of getting the local IP this.internalIP = InetAddress.getLocalHost().getHostAddress(); - } catch (UnknownHostException ignored) { } + } catch (UnknownHostException ignored) { + } } } else { // Sometimes the internal IP is the external IP... @@ -169,7 +164,6 @@ public class DumpInfo { @Getter public static class MCInfo { - private final String bedrockVersion; private final int bedrockProtocol; private final String javaVersion; @@ -184,8 +178,25 @@ public class DumpInfo { } @Getter - public static class RamInfo { + public static class Floodgate { + private final Properties gitInfo; + private final Object config; + Floodgate() { + this.gitInfo = FloodgateInfoHolder.getGitProperties(); + this.config = FloodgateInfoHolder.getConfig(); + } + } + + @AllArgsConstructor + @Getter + public static class HashInfo { + private final String md5Hash; + private final String sha256Hash; + } + + @Getter + public static class RamInfo { private final long free; private final long total; private final long max; diff --git a/connector/src/main/java/org/geysermc/connector/entity/AbstractArrowEntity.java b/connector/src/main/java/org/geysermc/connector/entity/AbstractArrowEntity.java index 25e8d37a1..54bec7257 100644 --- a/connector/src/main/java/org/geysermc/connector/entity/AbstractArrowEntity.java +++ b/connector/src/main/java/org/geysermc/connector/entity/AbstractArrowEntity.java @@ -44,7 +44,7 @@ public class AbstractArrowEntity extends Entity { @Override public void updateBedrockMetadata(EntityMetadata entityMetadata, GeyserSession session) { - if (entityMetadata.getId() == 7) { + if (entityMetadata.getId() == 8) { byte data = (byte) entityMetadata.getValue(); metadata.getFlags().setFlag(EntityFlag.CRITICAL, (data & 0x01) == 0x01); diff --git a/connector/src/main/java/org/geysermc/connector/entity/AreaEffectCloudEntity.java b/connector/src/main/java/org/geysermc/connector/entity/AreaEffectCloudEntity.java index 2e450343e..5a33bc98c 100644 --- a/connector/src/main/java/org/geysermc/connector/entity/AreaEffectCloudEntity.java +++ b/connector/src/main/java/org/geysermc/connector/entity/AreaEffectCloudEntity.java @@ -52,12 +52,12 @@ public class AreaEffectCloudEntity extends Entity { @Override public void updateBedrockMetadata(EntityMetadata entityMetadata, GeyserSession session) { - if (entityMetadata.getId() == 7) { + if (entityMetadata.getId() == 8) { metadata.put(EntityData.AREA_EFFECT_CLOUD_RADIUS, entityMetadata.getValue()); metadata.put(EntityData.BOUNDING_BOX_WIDTH, 2.0f * (float) entityMetadata.getValue()); - } else if (entityMetadata.getId() == 8) { + } else if (entityMetadata.getId() == 9) { metadata.put(EntityData.EFFECT_COLOR, entityMetadata.getValue()); - } else if (entityMetadata.getId() == 10) { + } else if (entityMetadata.getId() == 11) { Particle particle = (Particle) entityMetadata.getValue(); int particleId = EffectRegistry.getParticleId(session, particle.getType()); if (particleId != -1) { diff --git a/connector/src/main/java/org/geysermc/connector/entity/BoatEntity.java b/connector/src/main/java/org/geysermc/connector/entity/BoatEntity.java index 659722675..bc7ad2330 100644 --- a/connector/src/main/java/org/geysermc/connector/entity/BoatEntity.java +++ b/connector/src/main/java/org/geysermc/connector/entity/BoatEntity.java @@ -99,24 +99,24 @@ public class BoatEntity extends Entity { @Override public void updateBedrockMetadata(EntityMetadata entityMetadata, GeyserSession session) { // Time since last hit - if (entityMetadata.getId() == 7) { + if (entityMetadata.getId() == 8) { metadata.put(EntityData.HURT_TIME, entityMetadata.getValue()); } // Rocking direction - if (entityMetadata.getId() == 8) { + if (entityMetadata.getId() == 9) { metadata.put(EntityData.HURT_DIRECTION, entityMetadata.getValue()); } // 'Health' in Bedrock, damage taken in Java - if (entityMetadata.getId() == 9) { + if (entityMetadata.getId() == 10) { // Not exactly health but it makes motion in Bedrock metadata.put(EntityData.HEALTH, 40 - ((int) (float) entityMetadata.getValue())); } - if (entityMetadata.getId() == 10) { + if (entityMetadata.getId() == 11) { metadata.put(EntityData.VARIANT, entityMetadata.getValue()); - } else if (entityMetadata.getId() == 11) { + } else if (entityMetadata.getId() == 12) { isPaddlingLeft = (boolean) entityMetadata.getValue(); if (isPaddlingLeft) { // Java sends simply "true" and "false" (is_paddling_left), Bedrock keeps sending packets as you're rowing @@ -136,7 +136,7 @@ public class BoatEntity extends Entity { metadata.put(EntityData.ROW_TIME_LEFT, 0.0f); } } - else if (entityMetadata.getId() == 12) { + else if (entityMetadata.getId() == 13) { isPaddlingRight = (boolean) entityMetadata.getValue(); if (isPaddlingRight) { paddleTimeRight = 0f; @@ -151,7 +151,7 @@ public class BoatEntity extends Entity { } else { metadata.put(EntityData.ROW_TIME_RIGHT, 0.0f); } - } else if (entityMetadata.getId() == 13) { + } else if (entityMetadata.getId() == 14) { // Possibly - I don't think this does anything? metadata.put(EntityData.BOAT_BUBBLE_TIME, entityMetadata.getValue()); } 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 52183c431..bda4e7972 100644 --- a/connector/src/main/java/org/geysermc/connector/entity/CommandBlockMinecartEntity.java +++ b/connector/src/main/java/org/geysermc/connector/entity/CommandBlockMinecartEntity.java @@ -46,10 +46,10 @@ public class CommandBlockMinecartEntity extends DefaultBlockMinecartEntity { @Override public void updateBedrockMetadata(EntityMetadata entityMetadata, GeyserSession session) { - if (entityMetadata.getId() == 13) { + if (entityMetadata.getId() == 14) { metadata.put(EntityData.COMMAND_BLOCK_COMMAND, entityMetadata.getValue()); } - if (entityMetadata.getId() == 14) { + if (entityMetadata.getId() == 15) { metadata.put(EntityData.COMMAND_BLOCK_LAST_OUTPUT, MessageTranslator.convertMessage((Component) entityMetadata.getValue())); } super.updateBedrockMetadata(entityMetadata, session); diff --git a/connector/src/main/java/org/geysermc/connector/entity/DefaultBlockMinecartEntity.java b/connector/src/main/java/org/geysermc/connector/entity/DefaultBlockMinecartEntity.java index 805105c64..24e5bb259 100644 --- a/connector/src/main/java/org/geysermc/connector/entity/DefaultBlockMinecartEntity.java +++ b/connector/src/main/java/org/geysermc/connector/entity/DefaultBlockMinecartEntity.java @@ -56,7 +56,7 @@ public class DefaultBlockMinecartEntity extends MinecartEntity { public void updateBedrockMetadata(EntityMetadata entityMetadata, GeyserSession session) { // Custom block - if (entityMetadata.getId() == 10) { + if (entityMetadata.getId() == 11) { customBlock = (int) entityMetadata.getValue(); if (showCustomBlock) { @@ -65,7 +65,7 @@ public class DefaultBlockMinecartEntity extends MinecartEntity { } // Custom block offset - if (entityMetadata.getId() == 11) { + if (entityMetadata.getId() == 12) { customBlockOffset = (int) entityMetadata.getValue(); if (showCustomBlock) { @@ -74,7 +74,7 @@ public class DefaultBlockMinecartEntity extends MinecartEntity { } // If the custom block should be enabled - if (entityMetadata.getId() == 12) { + if (entityMetadata.getId() == 13) { if ((boolean) entityMetadata.getValue()) { showCustomBlock = true; metadata.put(EntityData.DISPLAY_ITEM, session.getBlockTranslator().getBedrockBlockId(customBlock)); diff --git a/connector/src/main/java/org/geysermc/connector/entity/EnderCrystalEntity.java b/connector/src/main/java/org/geysermc/connector/entity/EnderCrystalEntity.java index 8997512d9..596ccf089 100644 --- a/connector/src/main/java/org/geysermc/connector/entity/EnderCrystalEntity.java +++ b/connector/src/main/java/org/geysermc/connector/entity/EnderCrystalEntity.java @@ -47,7 +47,7 @@ public class EnderCrystalEntity extends Entity { public void updateBedrockMetadata(EntityMetadata entityMetadata, GeyserSession session) { // Show beam // Usually performed client-side on Bedrock except for Ender Dragon respawn event - if (entityMetadata.getId() == 7) { + if (entityMetadata.getId() == 8) { if (entityMetadata.getValue() instanceof Position) { Position pos = (Position) entityMetadata.getValue(); metadata.put(EntityData.BLOCK_TARGET, Vector3i.from(pos.getX(), pos.getY(), pos.getZ())); @@ -56,7 +56,7 @@ public class EnderCrystalEntity extends Entity { } } // There is a base located on the ender crystal - if (entityMetadata.getId() == 8) { + if (entityMetadata.getId() == 9) { metadata.getFlags().setFlag(EntityFlag.SHOW_BOTTOM, (boolean) entityMetadata.getValue()); } 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 bc371690b..bbe0bee23 100644 --- a/connector/src/main/java/org/geysermc/connector/entity/Entity.java +++ b/connector/src/main/java/org/geysermc/connector/entity/Entity.java @@ -303,31 +303,13 @@ public class Entity { metadata.getFlags().setFlag(EntityFlag.SLEEPING, pose.equals(Pose.SLEEPING)); // Triggered when crawling metadata.getFlags().setFlag(EntityFlag.SWIMMING, pose.equals(Pose.SWIMMING)); - float width = entityType.getWidth(); - float height = entityType.getHeight(); - switch (pose) { - case SLEEPING: - if (this instanceof LivingEntity) { - width = 0.2f; - height = 0.2f; - } - break; - case SNEAKING: - if (entityType == EntityType.PLAYER) { - height = 1.5f; - } - break; - case FALL_FLYING: - case SPIN_ATTACK: - case SWIMMING: - if (entityType == EntityType.PLAYER) { - // Seems like this is only cared about for players; nothing else - height = 0.6f; - } - break; - } - metadata.put(EntityData.BOUNDING_BOX_WIDTH, width); - metadata.put(EntityData.BOUNDING_BOX_HEIGHT, height); + setDimensions(pose); + break; + case 7: // Freezing ticks + // The value that Java edition gives us is in ticks, but Bedrock uses a float percentage of the strength 0.0 -> 1.0 + // The Java client caps its freezing tick percentage at 140 + int freezingTicks = Math.min((int) entityMetadata.getValue(), 140); + metadata.put(EntityData.FREEZING_EFFECT_STRENGTH, (freezingTicks / 140f)); break; } } @@ -345,6 +327,15 @@ public class Entity { session.sendUpstreamPacket(entityDataPacket); } + /** + * Set the height and width of the entity's bounding box + */ + protected void setDimensions(Pose pose) { + // No flexibility options for basic entities + metadata.put(EntityData.BOUNDING_BOX_WIDTH, entityType.getWidth()); + metadata.put(EntityData.BOUNDING_BOX_HEIGHT, entityType.getHeight()); + } + /** * x = Pitch, y = HeadYaw, z = Yaw * diff --git a/connector/src/main/java/org/geysermc/connector/entity/ExpOrbEntity.java b/connector/src/main/java/org/geysermc/connector/entity/ExpOrbEntity.java index 45595f504..f6fbcde9d 100644 --- a/connector/src/main/java/org/geysermc/connector/entity/ExpOrbEntity.java +++ b/connector/src/main/java/org/geysermc/connector/entity/ExpOrbEntity.java @@ -32,7 +32,7 @@ import org.geysermc.connector.network.session.GeyserSession; public class ExpOrbEntity extends Entity { - private int amount; + private final int amount; public ExpOrbEntity(int amount, long entityId, long geyserId, EntityType entityType, Vector3f position, Vector3f motion, Vector3f rotation) { super(entityId, geyserId, entityType, position, motion, rotation); diff --git a/connector/src/main/java/org/geysermc/connector/entity/FireworkEntity.java b/connector/src/main/java/org/geysermc/connector/entity/FireworkEntity.java index deaf3fadf..ad29c4b3f 100644 --- a/connector/src/main/java/org/geysermc/connector/entity/FireworkEntity.java +++ b/connector/src/main/java/org/geysermc/connector/entity/FireworkEntity.java @@ -41,7 +41,7 @@ import org.geysermc.connector.entity.type.EntityType; import org.geysermc.connector.network.session.GeyserSession; import org.geysermc.connector.utils.FireworkColor; import org.geysermc.connector.utils.MathUtils; -import org.geysermc.floodgate.util.DeviceOS; +import org.geysermc.floodgate.util.DeviceOs; import java.util.ArrayList; import java.util.List; @@ -55,7 +55,7 @@ public class FireworkEntity extends Entity { @Override public void updateBedrockMetadata(EntityMetadata entityMetadata, GeyserSession session) { - if (entityMetadata.getId() == 7) { + if (entityMetadata.getId() == 8) { ItemStack item = (ItemStack) entityMetadata.getValue(); if (item == null) { return; @@ -68,7 +68,8 @@ public class FireworkEntity extends Entity { // TODO: Remove once Mojang fixes bugs with fireworks crashing clients on these specific devices. // https://bugs.mojang.com/browse/MCPE-89115 - if (session.getClientData().getDeviceOS() == DeviceOS.XBOX_ONE || session.getClientData().getDeviceOS() == DeviceOS.ORBIS) { + if (session.getClientData().getDeviceOs() == DeviceOs.XBOX + || session.getClientData().getDeviceOs() == DeviceOs.PS4) { return; } @@ -134,7 +135,7 @@ public class FireworkEntity extends Entity { NbtMapBuilder builder = NbtMap.builder(); builder.put("Fireworks", fireworksBuilder.build()); metadata.put(EntityData.DISPLAY_ITEM, builder.build()); - } else if (entityMetadata.getId() == 8 && !entityMetadata.getValue().equals(OptionalInt.empty()) && ((OptionalInt) entityMetadata.getValue()).getAsInt() == session.getPlayerEntity().getEntityId()) { + } else if (entityMetadata.getId() == 9 && !entityMetadata.getValue().equals(OptionalInt.empty()) && ((OptionalInt) entityMetadata.getValue()).getAsInt() == session.getPlayerEntity().getEntityId()) { //Checks if the firework has an entity ID (used when a player is gliding) and checks to make sure the player that is gliding is the one getting sent the packet or else every player near the gliding player will boost too. PlayerEntity entity = session.getPlayerEntity(); float yaw = entity.getRotation().getX(); diff --git a/connector/src/main/java/org/geysermc/connector/entity/FishingHookEntity.java b/connector/src/main/java/org/geysermc/connector/entity/FishingHookEntity.java index 0738c3819..db44a0767 100644 --- a/connector/src/main/java/org/geysermc/connector/entity/FishingHookEntity.java +++ b/connector/src/main/java/org/geysermc/connector/entity/FishingHookEntity.java @@ -67,7 +67,7 @@ public class FishingHookEntity extends ThrowableEntity { @Override public void updateBedrockMetadata(EntityMetadata entityMetadata, GeyserSession session) { - if (entityMetadata.getId() == 7) { // Hooked entity + if (entityMetadata.getId() == 8) { // Hooked entity int hookedEntityId = (int) entityMetadata.getValue() - 1; Entity entity = session.getEntityCache().getEntityByJavaId(hookedEntityId); if (entity == null && session.getPlayerEntity().getEntityId() == hookedEntityId) { @@ -174,11 +174,8 @@ public class FishingHookEntity extends ThrowableEntity { * @return true if this entity is currently in air. */ protected boolean isInAir(GeyserSession session) { - if (session.getConnector().getConfig().isCacheChunks()) { - int block = session.getConnector().getWorldManager().getBlockAt(session, position.toInt()); - return block == BlockTranslator.JAVA_AIR_ID; - } - return false; + int block = session.getConnector().getWorldManager().getBlockAt(session, position.toInt()); + return block == BlockTranslator.JAVA_AIR_ID; } @Override diff --git a/connector/src/main/java/org/geysermc/connector/entity/FurnaceMinecartEntity.java b/connector/src/main/java/org/geysermc/connector/entity/FurnaceMinecartEntity.java index fdf24f176..2eb083883 100644 --- a/connector/src/main/java/org/geysermc/connector/entity/FurnaceMinecartEntity.java +++ b/connector/src/main/java/org/geysermc/connector/entity/FurnaceMinecartEntity.java @@ -42,7 +42,7 @@ public class FurnaceMinecartEntity extends DefaultBlockMinecartEntity { @Override public void updateBedrockMetadata(EntityMetadata entityMetadata, GeyserSession session) { - if (entityMetadata.getId() == 13 && !showCustomBlock) { + if (entityMetadata.getId() == 14 && !showCustomBlock) { hasFuel = (boolean) entityMetadata.getValue(); updateDefaultBlockMetadata(session); } diff --git a/connector/src/main/java/org/geysermc/connector/entity/ItemEntity.java b/connector/src/main/java/org/geysermc/connector/entity/ItemEntity.java index ed48e2670..ad269e41b 100644 --- a/connector/src/main/java/org/geysermc/connector/entity/ItemEntity.java +++ b/connector/src/main/java/org/geysermc/connector/entity/ItemEntity.java @@ -54,7 +54,7 @@ public class ItemEntity extends Entity { @Override public void updateBedrockMetadata(EntityMetadata entityMetadata, GeyserSession session) { - if (entityMetadata.getId() == 7) { + if (entityMetadata.getId() == 8) { AddItemEntityPacket itemPacket = new AddItemEntityPacket(); itemPacket.setRuntimeEntityId(geyserId); itemPacket.setPosition(position.add(0d, this.entityType.getOffset(), 0d)); 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 79711b0cb..f2f36e606 100644 --- a/connector/src/main/java/org/geysermc/connector/entity/ItemFrameEntity.java +++ b/connector/src/main/java/org/geysermc/connector/entity/ItemFrameEntity.java @@ -84,7 +84,7 @@ public class ItemFrameEntity extends Entity { @Override public void spawnEntity(GeyserSession session) { NbtMapBuilder blockBuilder = NbtMap.builder() - .putString("name", "minecraft:frame") + .putString("name", this.entityType == EntityType.GLOW_ITEM_FRAME ? "minecraft:glow_frame" : "minecraft:frame") .putInt("version", session.getBlockTranslator().getBlockStateVersion()); blockBuilder.put("states", NbtMap.builder() .putInt("facing_direction", direction.ordinal()) @@ -105,7 +105,7 @@ public class ItemFrameEntity extends Entity { @Override public void updateBedrockMetadata(EntityMetadata entityMetadata, GeyserSession session) { - if (entityMetadata.getId() == 7 && entityMetadata.getValue() != null) { + if (entityMetadata.getId() == 8 && entityMetadata.getValue() != null) { this.heldItem = (ItemStack) entityMetadata.getValue(); ItemData itemData = ItemTranslator.translateToBedrock(session, heldItem); ItemEntry itemEntry = ItemRegistry.getItem((ItemStack) entityMetadata.getValue()); @@ -124,11 +124,11 @@ public class ItemFrameEntity extends Entity { cachedTag = tag.build(); updateBlock(session); } - else if (entityMetadata.getId() == 7 && entityMetadata.getValue() == null && cachedTag != null) { + else if (entityMetadata.getId() == 8 && entityMetadata.getValue() == null && cachedTag != null) { cachedTag = getDefaultTag(); updateBlock(session); } - else if (entityMetadata.getId() == 8) { + else if (entityMetadata.getId() == 9) { rotation = ((int) entityMetadata.getValue()) * 45; if (cachedTag == null) { updateBlock(session); @@ -167,7 +167,7 @@ public class ItemFrameEntity extends Entity { builder.putInt("y", bedrockPosition.getY()); builder.putInt("z", bedrockPosition.getZ()); builder.putByte("isMovable", (byte) 1); - builder.putString("id", "ItemFrame"); + builder.putString("id", this.entityType == EntityType.GLOW_ITEM_FRAME ? "GlowItemFrame" : "ItemFrame"); return builder.build(); } 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 025cf085b..9ccb91fc1 100644 --- a/connector/src/main/java/org/geysermc/connector/entity/LivingEntity.java +++ b/connector/src/main/java/org/geysermc/connector/entity/LivingEntity.java @@ -26,6 +26,7 @@ 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.Pose; import com.github.steveice10.mc.protocol.data.game.entity.metadata.Position; import com.nukkitx.math.vector.Vector3f; import com.nukkitx.math.vector.Vector3i; @@ -68,7 +69,7 @@ public class LivingEntity extends Entity { @Override public void updateBedrockMetadata(EntityMetadata entityMetadata, GeyserSession session) { switch (entityMetadata.getId()) { - case 7: // blocking + case 8: // blocking byte xd = (byte) entityMetadata.getValue(); //blocking gets triggered when using a bow, but if we set USING_ITEM for all items, it may look like @@ -81,24 +82,22 @@ public class LivingEntity extends Entity { // Riptide spin attack metadata.getFlags().setFlag(EntityFlag.DAMAGE_NEARBY_MOBS, (xd & 0x04) == 0x04); break; - case 8: + case 9: metadata.put(EntityData.HEALTH, entityMetadata.getValue()); break; - case 9: + case 10: metadata.put(EntityData.EFFECT_COLOR, entityMetadata.getValue()); break; - case 10: + case 11: metadata.put(EntityData.EFFECT_AMBIENT, (byte) ((boolean) entityMetadata.getValue() ? 1 : 0)); break; - case 13: // Bed Position + case 14: // 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); - } + 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); // Indicate that the player should enter the sleep cycle // Has to be a byte or it does not work // (Bed position is what actually triggers sleep - "pose" is only optional) @@ -113,6 +112,16 @@ public class LivingEntity extends Entity { super.updateBedrockMetadata(entityMetadata, session); } + @Override + protected void setDimensions(Pose pose) { + if (pose == Pose.SLEEPING) { + metadata.put(EntityData.BOUNDING_BOX_WIDTH, 0.2f); + metadata.put(EntityData.BOUNDING_BOX_HEIGHT, 0.2f); + } else { + super.setDimensions(pose); + } + } + public void updateAllEquipment(GeyserSession session) { if (!valid) return; diff --git a/connector/src/main/java/org/geysermc/connector/entity/MinecartEntity.java b/connector/src/main/java/org/geysermc/connector/entity/MinecartEntity.java index b66b049eb..18d1d92a1 100644 --- a/connector/src/main/java/org/geysermc/connector/entity/MinecartEntity.java +++ b/connector/src/main/java/org/geysermc/connector/entity/MinecartEntity.java @@ -40,33 +40,33 @@ public class MinecartEntity extends Entity { @Override public void updateBedrockMetadata(EntityMetadata entityMetadata, GeyserSession session) { - if (entityMetadata.getId() == 7) { + if (entityMetadata.getId() == 8) { metadata.put(EntityData.HEALTH, entityMetadata.getValue()); } // Direction in which the minecart is shaking - if (entityMetadata.getId() == 8) { + if (entityMetadata.getId() == 9) { metadata.put(EntityData.HURT_DIRECTION, entityMetadata.getValue()); } // Power in Java, time in Bedrock - if (entityMetadata.getId() == 9) { + if (entityMetadata.getId() == 10) { metadata.put(EntityData.HURT_TIME, Math.min((int) (float) entityMetadata.getValue(), 15)); } if (!(this instanceof DefaultBlockMinecartEntity)) { // Handled in the DefaultBlockMinecartEntity class // Custom block - if (entityMetadata.getId() == 10) { + if (entityMetadata.getId() == 11) { metadata.put(EntityData.DISPLAY_ITEM, session.getBlockTranslator().getBedrockBlockId((int) entityMetadata.getValue())); } // Custom block offset - if (entityMetadata.getId() == 11) { + if (entityMetadata.getId() == 12) { metadata.put(EntityData.DISPLAY_OFFSET, entityMetadata.getValue()); } // If the custom block should be enabled - if (entityMetadata.getId() == 12) { + if (entityMetadata.getId() == 13) { // Needs a byte based off of Java's boolean metadata.put(EntityData.CUSTOM_DISPLAY, (byte) ((boolean) entityMetadata.getValue() ? 1 : 0)); } diff --git a/connector/src/main/java/org/geysermc/connector/entity/TNTEntity.java b/connector/src/main/java/org/geysermc/connector/entity/TNTEntity.java index ad73c1d9c..1d70d9d5f 100644 --- a/connector/src/main/java/org/geysermc/connector/entity/TNTEntity.java +++ b/connector/src/main/java/org/geysermc/connector/entity/TNTEntity.java @@ -45,7 +45,7 @@ public class TNTEntity extends Entity { @Override public void updateBedrockMetadata(EntityMetadata entityMetadata, GeyserSession session) { - if (entityMetadata.getId() == 7) { + if (entityMetadata.getId() == 8) { currentTick = (int) entityMetadata.getValue(); metadata.getFlags().setFlag(EntityFlag.IGNITED, true); metadata.put(EntityData.FUSE_LENGTH, currentTick); 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 1088b2a0b..1ba18bfd7 100644 --- a/connector/src/main/java/org/geysermc/connector/entity/ThrowableEntity.java +++ b/connector/src/main/java/org/geysermc/connector/entity/ThrowableEntity.java @@ -166,13 +166,8 @@ public class ThrowableEntity extends Entity implements Tickable { * @return true if this entity is currently in water. */ protected boolean isInWater(GeyserSession session) { - if (session.getConnector().getConfig().isCacheChunks()) { - if (0 <= position.getFloorY() && position.getFloorY() <= 255) { - int block = session.getConnector().getWorldManager().getBlockAt(session, position.toInt()); - return BlockStateValues.getWaterLevel(block) != -1; - } - } - return false; + int block = session.getConnector().getWorldManager().getBlockAt(session, position.toInt()); + return BlockStateValues.getWaterLevel(block) != -1; } @Override diff --git a/connector/src/main/java/org/geysermc/connector/entity/ThrownPotionEntity.java b/connector/src/main/java/org/geysermc/connector/entity/ThrownPotionEntity.java index c1f82836b..9ce218a81 100644 --- a/connector/src/main/java/org/geysermc/connector/entity/ThrownPotionEntity.java +++ b/connector/src/main/java/org/geysermc/connector/entity/ThrownPotionEntity.java @@ -51,7 +51,7 @@ public class ThrownPotionEntity extends ThrowableEntity { @Override public void updateBedrockMetadata(EntityMetadata entityMetadata, GeyserSession session) { - if (entityMetadata.getId() == 7 && entityMetadata.getType() == MetadataType.ITEM) { + if (entityMetadata.getId() == 8 && entityMetadata.getType() == MetadataType.ITEM) { ItemStack itemStack = (ItemStack) entityMetadata.getValue(); ItemEntry itemEntry = ItemRegistry.getItem(itemStack); if (itemEntry.getJavaIdentifier().endsWith("potion") && itemStack.getNbt() != null) { diff --git a/connector/src/main/java/org/geysermc/connector/entity/TippedArrowEntity.java b/connector/src/main/java/org/geysermc/connector/entity/TippedArrowEntity.java index 714a70daa..f7b63435f 100644 --- a/connector/src/main/java/org/geysermc/connector/entity/TippedArrowEntity.java +++ b/connector/src/main/java/org/geysermc/connector/entity/TippedArrowEntity.java @@ -44,17 +44,17 @@ public class TippedArrowEntity extends AbstractArrowEntity { @Override public void updateBedrockMetadata(EntityMetadata entityMetadata, GeyserSession session) { // Arrow potion effect color - if (entityMetadata.getId() == 9) { + if (entityMetadata.getId() == 10) { int potionColor = (int) entityMetadata.getValue(); // -1 means no color if (potionColor == -1) { - metadata.remove(EntityData.CUSTOM_DISPLAY); + metadata.put(EntityData.CUSTOM_DISPLAY, 0); } else { TippedArrowPotion potion = TippedArrowPotion.getByJavaColor(potionColor); if (potion != null && potion.getJavaColor() != -1) { metadata.put(EntityData.CUSTOM_DISPLAY, (byte) potion.getBedrockId()); } else { - metadata.remove(EntityData.CUSTOM_DISPLAY); + metadata.put(EntityData.CUSTOM_DISPLAY, 0); } } } diff --git a/connector/src/main/java/org/geysermc/connector/entity/TridentEntity.java b/connector/src/main/java/org/geysermc/connector/entity/TridentEntity.java index 014c0049e..19ec27692 100644 --- a/connector/src/main/java/org/geysermc/connector/entity/TridentEntity.java +++ b/connector/src/main/java/org/geysermc/connector/entity/TridentEntity.java @@ -39,7 +39,7 @@ public class TridentEntity extends AbstractArrowEntity { @Override public void updateBedrockMetadata(EntityMetadata entityMetadata, GeyserSession session) { - if (entityMetadata.getId() == 10) { + if (entityMetadata.getId() == 11) { metadata.getFlags().setFlag(EntityFlag.ENCHANTED, (boolean) entityMetadata.getValue()); } diff --git a/connector/src/main/java/org/geysermc/connector/entity/WitherSkullEntity.java b/connector/src/main/java/org/geysermc/connector/entity/WitherSkullEntity.java index 3548e0dfe..6209538dc 100644 --- a/connector/src/main/java/org/geysermc/connector/entity/WitherSkullEntity.java +++ b/connector/src/main/java/org/geysermc/connector/entity/WitherSkullEntity.java @@ -46,7 +46,7 @@ public class WitherSkullEntity extends ItemedFireballEntity { @Override public void updateBedrockMetadata(EntityMetadata entityMetadata, GeyserSession session) { - if (entityMetadata.getId() == 7) { + if (entityMetadata.getId() == 8) { boolean newIsCharged = (boolean) entityMetadata.getValue(); if (newIsCharged != isCharged) { isCharged = newIsCharged; diff --git a/connector/src/main/java/org/geysermc/connector/entity/living/AgeableEntity.java b/connector/src/main/java/org/geysermc/connector/entity/living/AgeableEntity.java index 909cb736b..8f8166d07 100644 --- a/connector/src/main/java/org/geysermc/connector/entity/living/AgeableEntity.java +++ b/connector/src/main/java/org/geysermc/connector/entity/living/AgeableEntity.java @@ -40,7 +40,7 @@ public class AgeableEntity extends CreatureEntity { @Override public void updateBedrockMetadata(EntityMetadata entityMetadata, GeyserSession session) { - if (entityMetadata.getId() == 15) { + if (entityMetadata.getId() == 16) { boolean isBaby = (boolean) entityMetadata.getValue(); metadata.put(EntityData.SCALE, isBaby ? .55f : 1f); metadata.getFlags().setFlag(EntityFlag.BABY, isBaby); 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 13f8a4c1d..a616f9269 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 @@ -126,7 +126,7 @@ public class ArmorStandEntity extends LivingEntity { } } else if (entityMetadata.getId() == 2) { updateSecondEntityStatus(false); - } else if (entityMetadata.getId() == 14 && entityMetadata.getType() == MetadataType.BYTE) { + } else if (entityMetadata.getId() == 15 && entityMetadata.getType() == MetadataType.BYTE) { byte xd = (byte) entityMetadata.getValue(); // isSmall @@ -169,37 +169,37 @@ public class ArmorStandEntity extends LivingEntity { EntityFlag negativeYToggle = null; EntityFlag negativeZToggle = null; switch (entityMetadata.getId()) { - case 15: // Head + case 16: // Head dataLeech = EntityData.MARK_VARIANT; negativeXToggle = EntityFlag.INTERESTED; negativeYToggle = EntityFlag.CHARGED; negativeZToggle = EntityFlag.POWERED; break; - case 16: // Body + case 17: // Body dataLeech = EntityData.VARIANT; negativeXToggle = EntityFlag.IN_LOVE; negativeYToggle = EntityFlag.CELEBRATING; negativeZToggle = EntityFlag.CELEBRATING_SPECIAL; break; - case 17: // Left arm + case 18: // Left arm dataLeech = EntityData.TRADE_TIER; negativeXToggle = EntityFlag.CHARGING; negativeYToggle = EntityFlag.CRITICAL; negativeZToggle = EntityFlag.DANCING; break; - case 18: // Right arm + case 19: // Right arm dataLeech = EntityData.MAX_TRADE_TIER; negativeXToggle = EntityFlag.ELDER; negativeYToggle = EntityFlag.EMOTING; negativeZToggle = EntityFlag.IDLING; break; - case 19: // Left leg + case 20: // Left leg dataLeech = EntityData.SKIN_ID; negativeXToggle = EntityFlag.IS_ILLAGER_CAPTAIN; negativeYToggle = EntityFlag.IS_IN_UI; negativeZToggle = EntityFlag.LINGERING; break; - case 20: // Right leg + case 21: // Right leg dataLeech = EntityData.HURT_DIRECTION; negativeXToggle = EntityFlag.IS_PREGNANT; negativeYToggle = EntityFlag.SHEARED; 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 index 80b426b42..110c02e06 100644 --- a/connector/src/main/java/org/geysermc/connector/entity/living/BatEntity.java +++ b/connector/src/main/java/org/geysermc/connector/entity/living/BatEntity.java @@ -39,7 +39,7 @@ public class BatEntity extends AmbientEntity { @Override public void updateBedrockMetadata(EntityMetadata entityMetadata, GeyserSession session) { - if (entityMetadata.getId() == 15) { + if (entityMetadata.getId() == 16) { byte xd = (byte) entityMetadata.getValue(); metadata.getFlags().setFlag(EntityFlag.RESTING, (xd & 0x01) == 0x01); } diff --git a/common/src/main/java/org/geysermc/common/window/FormWindow.java b/connector/src/main/java/org/geysermc/connector/entity/living/GlowSquidEntity.java similarity index 60% rename from common/src/main/java/org/geysermc/common/window/FormWindow.java rename to connector/src/main/java/org/geysermc/connector/entity/living/GlowSquidEntity.java index efc06fe80..b68c5d224 100644 --- a/common/src/main/java/org/geysermc/common/window/FormWindow.java +++ b/connector/src/main/java/org/geysermc/connector/entity/living/GlowSquidEntity.java @@ -23,37 +23,21 @@ * @link https://github.com/GeyserMC/Geyser */ -package org.geysermc.common.window; +package org.geysermc.connector.entity.living; -import com.fasterxml.jackson.annotation.JsonIgnore; -import lombok.Getter; -import lombok.Setter; -import org.geysermc.common.window.response.FormResponse; +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 abstract class FormWindow { - - @Getter - private final String type; - - @Getter - protected FormResponse response; - - @Getter - @Setter - protected boolean closed; - - public FormWindow(String type) { - this.type = type; +public class GlowSquidEntity extends SquidEntity { + public GlowSquidEntity(long entityId, long geyserId, EntityType entityType, Vector3f position, Vector3f motion, Vector3f rotation) { + super(entityId, geyserId, entityType, position, motion, rotation); } - // Lombok won't work here, so we need to make our own method - public void setResponse(FormResponse response) { - this.response = response; + @Override + public void updateBedrockMetadata(EntityMetadata entityMetadata, GeyserSession session) { + super.updateBedrockMetadata(entityMetadata, session); + // TODO "dark ticks remaining" ??? does this have a Bedrock equivalent? } - - @JsonIgnore - public abstract String getJSONData(); - - public abstract void setResponse(String response); - } diff --git a/connector/src/main/java/org/geysermc/connector/entity/living/InsentientEntity.java b/connector/src/main/java/org/geysermc/connector/entity/living/InsentientEntity.java index 90bb373fe..d0f99cdb9 100644 --- a/connector/src/main/java/org/geysermc/connector/entity/living/InsentientEntity.java +++ b/connector/src/main/java/org/geysermc/connector/entity/living/InsentientEntity.java @@ -41,7 +41,7 @@ public class InsentientEntity extends LivingEntity { @Override public void updateBedrockMetadata(EntityMetadata entityMetadata, GeyserSession session) { - if (entityMetadata.getId() == 14 && entityMetadata.getType() == MetadataType.BYTE) { + if (entityMetadata.getId() == 15 && entityMetadata.getType() == MetadataType.BYTE) { byte xd = (byte) entityMetadata.getValue(); metadata.getFlags().setFlag(EntityFlag.NO_AI, (xd & 0x01) == 0x01); } diff --git a/connector/src/main/java/org/geysermc/connector/entity/living/IronGolemEntity.java b/connector/src/main/java/org/geysermc/connector/entity/living/IronGolemEntity.java index 5fca355f5..f47ff2c7f 100644 --- a/connector/src/main/java/org/geysermc/connector/entity/living/IronGolemEntity.java +++ b/connector/src/main/java/org/geysermc/connector/entity/living/IronGolemEntity.java @@ -53,7 +53,7 @@ public class IronGolemEntity extends GolemEntity { @Override public void updateBedrockMetadata(EntityMetadata entityMetadata, GeyserSession session) { super.updateBedrockMetadata(entityMetadata, session); - if (entityMetadata.getId() == 8) { + if (entityMetadata.getId() == 9) { // Required so the resource pack sees the entity health attributes.put(AttributeType.HEALTH, AttributeType.HEALTH.getAttribute(metadata.getFloat(EntityData.HEALTH), 100f)); updateBedrockAttributes(session); diff --git a/connector/src/main/java/org/geysermc/connector/entity/living/SlimeEntity.java b/connector/src/main/java/org/geysermc/connector/entity/living/SlimeEntity.java index da088410a..f08fcc796 100644 --- a/connector/src/main/java/org/geysermc/connector/entity/living/SlimeEntity.java +++ b/connector/src/main/java/org/geysermc/connector/entity/living/SlimeEntity.java @@ -39,7 +39,7 @@ public class SlimeEntity extends InsentientEntity { @Override public void updateBedrockMetadata(EntityMetadata entityMetadata, GeyserSession session) { - if (entityMetadata.getId() == 15) { + if (entityMetadata.getId() == 16) { this.metadata.put(EntityData.SCALE, 0.10f + (int) entityMetadata.getValue()); } 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 index 144b0ed24..6bfb23564 100644 --- a/connector/src/main/java/org/geysermc/connector/entity/living/SnowGolemEntity.java +++ b/connector/src/main/java/org/geysermc/connector/entity/living/SnowGolemEntity.java @@ -39,7 +39,7 @@ public class SnowGolemEntity extends GolemEntity { @Override public void updateBedrockMetadata(EntityMetadata entityMetadata, GeyserSession session) { - if (entityMetadata.getId() == 15) { + if (entityMetadata.getId() == 16) { byte xd = (byte) entityMetadata.getValue(); // Handle the visibility of the pumpkin metadata.getFlags().setFlag(EntityFlag.SHEARED, (xd & 0x10) != 0x10); diff --git a/connector/src/main/java/org/geysermc/connector/entity/living/SquidEntity.java b/connector/src/main/java/org/geysermc/connector/entity/living/SquidEntity.java index d4a409242..df914162d 100644 --- a/connector/src/main/java/org/geysermc/connector/entity/living/SquidEntity.java +++ b/connector/src/main/java/org/geysermc/connector/entity/living/SquidEntity.java @@ -29,7 +29,6 @@ import com.nukkitx.math.vector.Vector3f; import org.geysermc.connector.entity.type.EntityType; public class SquidEntity extends WaterEntity { - public SquidEntity(long entityId, long geyserId, EntityType entityType, Vector3f position, Vector3f motion, Vector3f rotation) { super(entityId, geyserId, entityType, position, motion, rotation); } diff --git a/connector/src/main/java/org/geysermc/connector/entity/living/animal/AxolotlEntity.java b/connector/src/main/java/org/geysermc/connector/entity/living/animal/AxolotlEntity.java new file mode 100644 index 000000000..7c6c81ba2 --- /dev/null +++ b/connector/src/main/java/org/geysermc/connector/entity/living/animal/AxolotlEntity.java @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2019-2021 GeyserMC. http://geysermc.org + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * @author GeyserMC + * @link https://github.com/GeyserMC/Geyser + */ + +package org.geysermc.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.EntityFlag; +import org.geysermc.connector.entity.type.EntityType; +import org.geysermc.connector.network.session.GeyserSession; +import org.geysermc.connector.network.translators.item.ItemEntry; + +public class AxolotlEntity extends AnimalEntity { + public AxolotlEntity(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) { + super.updateBedrockMetadata(entityMetadata, session); + if (entityMetadata.getId() == 17) { + int variant = (int) entityMetadata.getValue(); + switch (variant) { + case 1: // Java - "Wild" (brown) + variant = 3; + break; + case 3: // Java - cyan + variant = 1; + break; + } + metadata.put(EntityData.VARIANT, variant); + } + else if (entityMetadata.getId() == 18) { + metadata.getFlags().setFlag(EntityFlag.PLAYING_DEAD, (boolean) entityMetadata.getValue()); + } + } + + @Override + public boolean canEat(GeyserSession session, String javaIdentifierStripped, ItemEntry itemEntry) { + return javaIdentifierStripped.equals("tropical_fish_bucket"); + } +} 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 b2d5faaad..59035bb85 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 @@ -43,7 +43,7 @@ public class BeeEntity extends AnimalEntity { @Override public void updateBedrockMetadata(EntityMetadata entityMetadata, GeyserSession session) { - if (entityMetadata.getId() == 16) { + if (entityMetadata.getId() == 17) { byte xd = (byte) entityMetadata.getValue(); // Bee is performing sting attack; trigger animation if ((xd & 0x02) == 0x02) { @@ -58,7 +58,7 @@ public class BeeEntity extends AnimalEntity { // If the bee has nectar or not metadata.getFlags().setFlag(EntityFlag.POWERED, (xd & 0x08) == 0x08); } - if (entityMetadata.getId() == 17) { + if (entityMetadata.getId() == 18) { // Converting "anger time" to a boolean metadata.getFlags().setFlag(EntityFlag.ANGRY, (int) entityMetadata.getValue() > 0); } 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 ff71e87f0..2c1934139 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,10 +41,10 @@ public class FoxEntity extends AnimalEntity { @Override public void updateBedrockMetadata(EntityMetadata entityMetadata, GeyserSession session) { - if (entityMetadata.getId() == 16) { + if (entityMetadata.getId() == 17) { metadata.put(EntityData.VARIANT, entityMetadata.getValue()); } - if (entityMetadata.getId() == 17) { + if (entityMetadata.getId() == 18) { byte xd = (byte) entityMetadata.getValue(); metadata.getFlags().setFlag(EntityFlag.SITTING, (xd & 0x01) == 0x01); metadata.getFlags().setFlag(EntityFlag.SNEAKING, (xd & 0x04) == 0x04); @@ -56,6 +56,6 @@ public class FoxEntity extends AnimalEntity { @Override public boolean canEat(GeyserSession session, String javaIdentifierStripped, ItemEntry itemEntry) { - return javaIdentifierStripped.equals("sweet_berries"); + return session.getTagCache().isFoxFood(itemEntry); } } diff --git a/connector/src/main/java/org/geysermc/connector/entity/living/animal/GoatEntity.java b/connector/src/main/java/org/geysermc/connector/entity/living/animal/GoatEntity.java new file mode 100644 index 000000000..a43998f27 --- /dev/null +++ b/connector/src/main/java/org/geysermc/connector/entity/living/animal/GoatEntity.java @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2019-2021 GeyserMC. http://geysermc.org + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * @author GeyserMC + * @link https://github.com/GeyserMC/Geyser + */ + +package org.geysermc.connector.entity.living.animal; + +import com.github.steveice10.mc.protocol.data.game.entity.metadata.EntityMetadata; +import com.github.steveice10.mc.protocol.data.game.entity.metadata.Pose; +import com.nukkitx.math.vector.Vector3f; +import com.nukkitx.protocol.bedrock.data.entity.EntityData; +import lombok.Getter; +import org.geysermc.connector.entity.type.EntityType; +import org.geysermc.connector.network.session.GeyserSession; + +public class GoatEntity extends AnimalEntity { + private static final float LONG_JUMPING_HEIGHT = 1.3f * 0.7f; + private static final float LONG_JUMPING_WIDTH = 0.9f * 0.7f; + + @Getter + private boolean isScreamer; + + public GoatEntity(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) { + super.updateBedrockMetadata(entityMetadata, session); + if (entityMetadata.getId() == 17) { + // Not used in Bedrock Edition + isScreamer = (boolean) entityMetadata.getValue(); + } + } + + @Override + protected void setDimensions(Pose pose) { + if (pose == Pose.LONG_JUMPING) { + metadata.put(EntityData.BOUNDING_BOX_WIDTH, LONG_JUMPING_WIDTH); + metadata.put(EntityData.BOUNDING_BOX_HEIGHT, LONG_JUMPING_HEIGHT); + } else { + super.setDimensions(pose); + } + } +} 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 index 9fbb17725..1534abb0b 100644 --- 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 @@ -41,7 +41,7 @@ public class HoglinEntity extends AnimalEntity { @Override public void updateBedrockMetadata(EntityMetadata entityMetadata, GeyserSession session) { - if (entityMetadata.getId() == 16) { + if (entityMetadata.getId() == 17) { // 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)); 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 index 9e12f3f1e..e2e22c73d 100644 --- 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 @@ -39,7 +39,7 @@ public class MooshroomEntity extends AnimalEntity { @Override public void updateBedrockMetadata(EntityMetadata entityMetadata, GeyserSession session) { - if (entityMetadata.getId() == 16) { + if (entityMetadata.getId() == 17) { 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/OcelotEntity.java b/connector/src/main/java/org/geysermc/connector/entity/living/animal/OcelotEntity.java index 1cf541c8c..48a5e08bf 100644 --- a/connector/src/main/java/org/geysermc/connector/entity/living/animal/OcelotEntity.java +++ b/connector/src/main/java/org/geysermc/connector/entity/living/animal/OcelotEntity.java @@ -40,7 +40,7 @@ public class OcelotEntity extends AnimalEntity { @Override public void updateBedrockMetadata(EntityMetadata entityMetadata, GeyserSession session) { - if (entityMetadata.getId() == 16) { + if (entityMetadata.getId() == 17) { metadata.getFlags().setFlag(EntityFlag.TRUSTING, (boolean) entityMetadata.getValue()); } 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 2f5ced080..83f0ed8d9 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 @@ -47,7 +47,7 @@ public class PandaEntity extends AnimalEntity { @Override public void updateBedrockMetadata(EntityMetadata entityMetadata, GeyserSession session) { - if (entityMetadata.getId() == 18) { + if (entityMetadata.getId() == 19) { metadata.getFlags().setFlag(EntityFlag.EATING, (int) entityMetadata.getValue() > 0); metadata.put(EntityData.EATING_COUNTER, entityMetadata.getValue()); if ((int) entityMetadata.getValue() != 0) { @@ -59,15 +59,15 @@ public class PandaEntity extends AnimalEntity { session.sendUpstreamPacket(packet); } } - if (entityMetadata.getId() == 19) { + if (entityMetadata.getId() == 20) { mainGene = (int) (byte) entityMetadata.getValue(); updateAppearance(); } - if (entityMetadata.getId() == 20) { + if (entityMetadata.getId() == 21) { hiddenGene = (int) (byte) entityMetadata.getValue(); updateAppearance(); } - if (entityMetadata.getId() == 21) { + if (entityMetadata.getId() == 22) { byte xd = (byte) entityMetadata.getValue(); metadata.getFlags().setFlag(EntityFlag.SNEEZING, (xd & 0x02) == 0x02); metadata.getFlags().setFlag(EntityFlag.ROLLING, (xd & 0x04) == 0x04); diff --git a/connector/src/main/java/org/geysermc/connector/entity/living/animal/PigEntity.java b/connector/src/main/java/org/geysermc/connector/entity/living/animal/PigEntity.java index bbb1aed20..27d736b33 100644 --- a/connector/src/main/java/org/geysermc/connector/entity/living/animal/PigEntity.java +++ b/connector/src/main/java/org/geysermc/connector/entity/living/animal/PigEntity.java @@ -40,8 +40,7 @@ public class PigEntity extends AnimalEntity { @Override public void updateBedrockMetadata(EntityMetadata entityMetadata, GeyserSession session) { - - if (entityMetadata.getId() == 16) { + if (entityMetadata.getId() == 17) { metadata.getFlags().setFlag(EntityFlag.SADDLED, (boolean) entityMetadata.getValue()); } super.updateBedrockMetadata(entityMetadata, session); diff --git a/connector/src/main/java/org/geysermc/connector/entity/living/animal/PolarBearEntity.java b/connector/src/main/java/org/geysermc/connector/entity/living/animal/PolarBearEntity.java index f33635aeb..8f292411d 100644 --- a/connector/src/main/java/org/geysermc/connector/entity/living/animal/PolarBearEntity.java +++ b/connector/src/main/java/org/geysermc/connector/entity/living/animal/PolarBearEntity.java @@ -40,7 +40,7 @@ public class PolarBearEntity extends AnimalEntity { @Override public void updateBedrockMetadata(EntityMetadata entityMetadata, GeyserSession session) { - if (entityMetadata.getId() == 16) { + if (entityMetadata.getId() == 17) { metadata.getFlags().setFlag(EntityFlag.STANDING, (boolean) entityMetadata.getValue()); } super.updateBedrockMetadata(entityMetadata, session); diff --git a/connector/src/main/java/org/geysermc/connector/entity/living/animal/PufferFishEntity.java b/connector/src/main/java/org/geysermc/connector/entity/living/animal/PufferFishEntity.java index 9a6a712f9..0ddd581f8 100644 --- a/connector/src/main/java/org/geysermc/connector/entity/living/animal/PufferFishEntity.java +++ b/connector/src/main/java/org/geysermc/connector/entity/living/animal/PufferFishEntity.java @@ -40,10 +40,9 @@ public class PufferFishEntity extends AbstractFishEntity { @Override public void updateBedrockMetadata(EntityMetadata entityMetadata, GeyserSession session) { - if (entityMetadata.getId() == 16) { - // Transfers correctly but doesn't apply on the client + if (entityMetadata.getId() == 17) { int puffsize = (int) entityMetadata.getValue(); - metadata.put(EntityData.PUFFERFISH_SIZE, puffsize); + metadata.put(EntityData.PUFFERFISH_SIZE, (byte) puffsize); metadata.put(EntityData.VARIANT, puffsize); } super.updateBedrockMetadata(entityMetadata, session); 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 9a4691cc0..41579d6a6 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 @@ -42,14 +42,14 @@ public class RabbitEntity extends AnimalEntity { @Override public void updateBedrockMetadata(EntityMetadata entityMetadata, GeyserSession session) { super.updateBedrockMetadata(entityMetadata, session); - if (entityMetadata.getId() == 15) { + if (entityMetadata.getId() == 16) { metadata.put(EntityData.SCALE, .55f); boolean isBaby = (boolean) entityMetadata.getValue(); if (isBaby) { metadata.put(EntityData.SCALE, .35f); metadata.getFlags().setFlag(EntityFlag.BABY, true); } - } else if (entityMetadata.getId() == 16) { + } else if (entityMetadata.getId() == 17) { int variant = (int) entityMetadata.getValue(); // Change the killer bunny to display as white since it only exists on Java Edition diff --git a/connector/src/main/java/org/geysermc/connector/entity/living/animal/SheepEntity.java b/connector/src/main/java/org/geysermc/connector/entity/living/animal/SheepEntity.java index 37bb2fdeb..f723eff2b 100644 --- a/connector/src/main/java/org/geysermc/connector/entity/living/animal/SheepEntity.java +++ b/connector/src/main/java/org/geysermc/connector/entity/living/animal/SheepEntity.java @@ -40,7 +40,7 @@ public class SheepEntity extends AnimalEntity { @Override public void updateBedrockMetadata(EntityMetadata entityMetadata, GeyserSession session) { - if (entityMetadata.getId() == 16) { + if (entityMetadata.getId() == 17) { byte xd = (byte) entityMetadata.getValue(); metadata.getFlags().setFlag(EntityFlag.SHEARED, (xd & 0x10) == 0x10); metadata.put(EntityData.COLOR, xd); diff --git a/connector/src/main/java/org/geysermc/connector/entity/living/animal/StriderEntity.java b/connector/src/main/java/org/geysermc/connector/entity/living/animal/StriderEntity.java index a90a044bc..51b7aacad 100644 --- a/connector/src/main/java/org/geysermc/connector/entity/living/animal/StriderEntity.java +++ b/connector/src/main/java/org/geysermc/connector/entity/living/animal/StriderEntity.java @@ -46,10 +46,10 @@ public class StriderEntity extends AnimalEntity { @Override public void updateBedrockMetadata(EntityMetadata entityMetadata, GeyserSession session) { - if (entityMetadata.getId() == 17) { + if (entityMetadata.getId() == 18) { shaking = (boolean) entityMetadata.getValue(); } - if (entityMetadata.getId() == 18) { + if (entityMetadata.getId() == 19) { metadata.getFlags().setFlag(EntityFlag.SADDLED, (boolean) entityMetadata.getValue()); } diff --git a/connector/src/main/java/org/geysermc/connector/entity/living/animal/TropicalFishEntity.java b/connector/src/main/java/org/geysermc/connector/entity/living/animal/TropicalFishEntity.java index 8d5b476a0..7a5906dd9 100644 --- a/connector/src/main/java/org/geysermc/connector/entity/living/animal/TropicalFishEntity.java +++ b/connector/src/main/java/org/geysermc/connector/entity/living/animal/TropicalFishEntity.java @@ -28,8 +28,6 @@ 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 lombok.AllArgsConstructor; -import lombok.Getter; import org.geysermc.connector.entity.living.AbstractFishEntity; import org.geysermc.connector.entity.type.EntityType; import org.geysermc.connector.network.session.GeyserSession; @@ -42,34 +40,14 @@ public class TropicalFishEntity extends AbstractFishEntity { @Override public void updateBedrockMetadata(EntityMetadata entityMetadata, GeyserSession session) { - if (entityMetadata.getId() == 16) { - TropicalFishVariant variant = TropicalFishVariant.fromVariantNumber((int) entityMetadata.getValue()); + if (entityMetadata.getId() == 17) { + int varNumber = (int) entityMetadata.getValue(); - metadata.put(EntityData.VARIANT, variant.getShape()); // Shape 0-1 - metadata.put(EntityData.MARK_VARIANT, variant.getPattern()); // Pattern 0-5 - metadata.put(EntityData.COLOR, variant.getBaseColor()); // Base color 0-15 - metadata.put(EntityData.COLOR_2, variant.getPatternColor()); // Pattern color 0-15 + metadata.put(EntityData.VARIANT, varNumber & 0xFF); // Shape 0-1 + metadata.put(EntityData.MARK_VARIANT, (varNumber >> 8) & 0xFF); // Pattern 0-5 + metadata.put(EntityData.COLOR, (byte) ((varNumber >> 16) & 0xFF)); // Base color 0-15 + metadata.put(EntityData.COLOR_2, (byte) ((varNumber >> 24) & 0xFF)); // Pattern color 0-15 } super.updateBedrockMetadata(entityMetadata, session); } - - @Getter - @AllArgsConstructor - private static class TropicalFishVariant { - private int shape; - private int pattern; - private byte baseColor; - private byte patternColor; - - /** - * Convert the variant number from Java into separate values - * - * @param varNumber Variant number from Java edition - * - * @return The variant converted into TropicalFishVariant - */ - public static TropicalFishVariant fromVariantNumber(int varNumber) { - return new TropicalFishVariant((varNumber & 0xFF), ((varNumber >> 8) & 0xFF), (byte) ((varNumber >> 16) & 0xFF), (byte) ((varNumber >> 24) & 0xFF)); - } - } } 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 index 536f40755..7e9e3260d 100644 --- 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 @@ -40,9 +40,9 @@ public class TurtleEntity extends AnimalEntity { @Override public void updateBedrockMetadata(EntityMetadata entityMetadata, GeyserSession session) { - if (entityMetadata.getId() == 17) { + if (entityMetadata.getId() == 18) { metadata.getFlags().setFlag(EntityFlag.IS_PREGNANT, (boolean) entityMetadata.getValue()); - } else if (entityMetadata.getId() == 18) { + } else if (entityMetadata.getId() == 19) { 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 324c8229f..5f4082e50 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 @@ -64,7 +64,7 @@ public class AbstractHorseEntity extends AnimalEntity { @Override public void updateBedrockMetadata(EntityMetadata entityMetadata, GeyserSession session) { - if (entityMetadata.getId() == 16) { + if (entityMetadata.getId() == 17) { byte xd = (byte) entityMetadata.getValue(); metadata.getFlags().setFlag(EntityFlag.TAMED, (xd & 0x02) == 0x02); metadata.getFlags().setFlag(EntityFlag.SADDLED, (xd & 0x04) == 0x04); @@ -106,7 +106,7 @@ public class AbstractHorseEntity extends AnimalEntity { super.updateBedrockMetadata(entityMetadata, session); - if (entityMetadata.getId() == 8) { + if (entityMetadata.getId() == 9) { // Update the health attribute updateBedrockAttributes(session); } diff --git a/connector/src/main/java/org/geysermc/connector/entity/living/animal/horse/ChestedHorseEntity.java b/connector/src/main/java/org/geysermc/connector/entity/living/animal/horse/ChestedHorseEntity.java index 461d636bd..e4f0cc241 100644 --- a/connector/src/main/java/org/geysermc/connector/entity/living/animal/horse/ChestedHorseEntity.java +++ b/connector/src/main/java/org/geysermc/connector/entity/living/animal/horse/ChestedHorseEntity.java @@ -42,7 +42,7 @@ public class ChestedHorseEntity extends AbstractHorseEntity { @Override public void updateBedrockMetadata(EntityMetadata entityMetadata, GeyserSession session) { - if (entityMetadata.getId() == 18) { + if (entityMetadata.getId() == 19) { metadata.getFlags().setFlag(EntityFlag.CHESTED, (boolean) entityMetadata.getValue()); } super.updateBedrockMetadata(entityMetadata, session); 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 c687898d0..094726de1 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 @@ -39,7 +39,7 @@ public class HorseEntity extends AbstractHorseEntity { @Override public void updateBedrockMetadata(EntityMetadata entityMetadata, GeyserSession session) { - if (entityMetadata.getId() == 18) { + if (entityMetadata.getId() == 19) { metadata.put(EntityData.VARIANT, entityMetadata.getValue()); metadata.put(EntityData.MARK_VARIANT, (((int) entityMetadata.getValue()) >> 8) % 5); } diff --git a/connector/src/main/java/org/geysermc/connector/entity/living/animal/horse/LlamaEntity.java b/connector/src/main/java/org/geysermc/connector/entity/living/animal/horse/LlamaEntity.java index 8ab1df1a8..67e3304d3 100644 --- a/connector/src/main/java/org/geysermc/connector/entity/living/animal/horse/LlamaEntity.java +++ b/connector/src/main/java/org/geysermc/connector/entity/living/animal/horse/LlamaEntity.java @@ -46,11 +46,11 @@ public class LlamaEntity extends ChestedHorseEntity { @Override public void updateBedrockMetadata(EntityMetadata entityMetadata, GeyserSession session) { // Strength - if (entityMetadata.getId() == 19) { + if (entityMetadata.getId() == 20) { metadata.put(EntityData.STRENGTH, entityMetadata.getValue()); } // Color equipped on the llama - if (entityMetadata.getId() == 20) { + if (entityMetadata.getId() == 21) { // Bedrock treats llama decoration as armor MobArmorEquipmentPacket equipmentPacket = new MobArmorEquipmentPacket(); equipmentPacket.setRuntimeEntityId(geyserId); @@ -71,7 +71,7 @@ public class LlamaEntity extends ChestedHorseEntity { session.sendUpstreamPacket(equipmentPacket); } // Color of the llama - if (entityMetadata.getId() == 21) { + if (entityMetadata.getId() == 22) { metadata.put(EntityData.VARIANT, entityMetadata.getValue()); } 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 6869717a0..7aa702074 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 @@ -49,20 +49,15 @@ public class CatEntity extends TameableEntity { @Override public void updateBedrockMetadata(EntityMetadata entityMetadata, GeyserSession session) { super.updateBedrockMetadata(entityMetadata, session); - if (entityMetadata.getId() == 15) { - metadata.put(EntityData.SCALE, .8f); - boolean isBaby = (boolean) entityMetadata.getValue(); - if (isBaby) { - metadata.put(EntityData.SCALE, .4f); - metadata.getFlags().setFlag(EntityFlag.BABY, true); - } - } else if (entityMetadata.getId() == 16) { + if (entityMetadata.getId() == 16) { + metadata.put(EntityData.SCALE, (boolean) entityMetadata.getValue() ? 0.8f : 0.4f); + } else if (entityMetadata.getId() == 17) { // Update collar color if tamed if (metadata.getFlags().getFlag(EntityFlag.TAMED)) { metadata.put(EntityData.COLOR, collarColor); } } - if (entityMetadata.getId() == 18) { + if (entityMetadata.getId() == 19) { // Different colors in Java and Bedrock for some reason int variantColor; switch ((int) entityMetadata.getValue()) { @@ -83,10 +78,10 @@ public class CatEntity extends TameableEntity { } metadata.put(EntityData.VARIANT, variantColor); } - if (entityMetadata.getId() == 19) { + if (entityMetadata.getId() == 20) { metadata.getFlags().setFlag(EntityFlag.RESTING, (boolean) entityMetadata.getValue()); } - if (entityMetadata.getId() == 21) { + if (entityMetadata.getId() == 22) { collarColor = (byte) (int) entityMetadata.getValue(); // Needed or else wild cats are a red color if (metadata.getFlags().getFlag(EntityFlag.TAMED)) { diff --git a/connector/src/main/java/org/geysermc/connector/entity/living/animal/tameable/ParrotEntity.java b/connector/src/main/java/org/geysermc/connector/entity/living/animal/tameable/ParrotEntity.java index 45327c785..dcc9d6f78 100644 --- a/connector/src/main/java/org/geysermc/connector/entity/living/animal/tameable/ParrotEntity.java +++ b/connector/src/main/java/org/geysermc/connector/entity/living/animal/tameable/ParrotEntity.java @@ -41,7 +41,7 @@ public class ParrotEntity extends TameableEntity { @Override public void updateBedrockMetadata(EntityMetadata entityMetadata, GeyserSession session) { // Parrot color - if (entityMetadata.getId() == 18) { + if (entityMetadata.getId() == 19) { metadata.put(EntityData.VARIANT, entityMetadata.getValue()); } 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 21bf0a1b7..923e13712 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 @@ -44,7 +44,7 @@ public class TameableEntity extends AnimalEntity { @Override public void updateBedrockMetadata(EntityMetadata entityMetadata, GeyserSession session) { - if (entityMetadata.getId() == 16) { + if (entityMetadata.getId() == 17) { byte xd = (byte) entityMetadata.getValue(); metadata.getFlags().setFlag(EntityFlag.SITTING, (xd & 0x01) == 0x01); metadata.getFlags().setFlag(EntityFlag.ANGRY, (xd & 0x02) == 0x02); @@ -52,7 +52,7 @@ public class TameableEntity extends AnimalEntity { } // Note: Must be set for wolf collar color to work - if (entityMetadata.getId() == 17) { + if (entityMetadata.getId() == 18) { if (entityMetadata.getValue() != null) { // Owner UUID of entity Entity entity = session.getEntityCache().getPlayerEntity((UUID) entityMetadata.getValue()); diff --git a/connector/src/main/java/org/geysermc/connector/entity/living/animal/tameable/WolfEntity.java b/connector/src/main/java/org/geysermc/connector/entity/living/animal/tameable/WolfEntity.java index bb7916937..0a5d2a58c 100644 --- a/connector/src/main/java/org/geysermc/connector/entity/living/animal/tameable/WolfEntity.java +++ b/connector/src/main/java/org/geysermc/connector/entity/living/animal/tameable/WolfEntity.java @@ -54,7 +54,7 @@ public class WolfEntity extends TameableEntity { @Override public void updateBedrockMetadata(EntityMetadata entityMetadata, GeyserSession session) { //Reset wolf color - if (entityMetadata.getId() == 16) { + if (entityMetadata.getId() == 17) { byte xd = (byte) entityMetadata.getValue(); boolean angry = (xd & 0x02) == 0x02; if (angry) { @@ -63,13 +63,13 @@ public class WolfEntity extends TameableEntity { } // "Begging" on wiki.vg, "Interested" in Nukkit - the tilt of the head - if (entityMetadata.getId() == 18) { + if (entityMetadata.getId() == 19) { metadata.getFlags().setFlag(EntityFlag.INTERESTED, (boolean) entityMetadata.getValue()); } // Wolf collar color // Relies on EntityData.OWNER_EID being set in TameableEntity.java - if (entityMetadata.getId() == 19 && !metadata.getFlags().getFlag(EntityFlag.ANGRY)) { + if (entityMetadata.getId() == 20 && !metadata.getFlags().getFlag(EntityFlag.ANGRY)) { metadata.put(EntityData.COLOR, collarColor = (byte) (int) entityMetadata.getValue()); if (!metadata.containsKey(EntityData.OWNER_EID)) { // If a color is set and there is no owner entity ID, set one. @@ -79,7 +79,7 @@ public class WolfEntity extends TameableEntity { } // Wolf anger (1.16+) - if (entityMetadata.getId() == 20) { + if (entityMetadata.getId() == 21) { metadata.getFlags().setFlag(EntityFlag.ANGRY, (int) entityMetadata.getValue() != 0); metadata.put(EntityData.COLOR, (int) entityMetadata.getValue() != 0 ? (byte) 0 : collarColor); } 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 be4228830..1ba8b595b 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 @@ -85,7 +85,7 @@ public class VillagerEntity extends AbstractMerchantEntity { @Override public void updateBedrockMetadata(EntityMetadata entityMetadata, GeyserSession session) { - if (entityMetadata.getId() == 17) { + if (entityMetadata.getId() == 18) { VillagerData villagerData = (VillagerData) entityMetadata.getValue(); // Profession metadata.put(EntityData.VARIANT, VILLAGER_PROFESSIONS.get(villagerData.getProfession())); @@ -111,7 +111,7 @@ public class VillagerEntity extends AbstractMerchantEntity { float bedPositionSubtractorW = 0; float bedPositionSubtractorN = 0; Vector3i bedPosition = metadata.getPos(EntityData.BED_POSITION, null); - if (session.getConnector().getConfig().isCacheChunks() && bedPosition != null) { + if (bedPosition != null) { bedId = session.getConnector().getWorldManager().getBlockAt(session, bedPosition); } String bedRotationZ = BlockTranslator.getJavaIdBlockMap().inverse().get(bedId); diff --git a/connector/src/main/java/org/geysermc/connector/entity/living/monster/AbstractSkeletonEntity.java b/connector/src/main/java/org/geysermc/connector/entity/living/monster/AbstractSkeletonEntity.java index 2d1b41765..bc17e27ca 100644 --- a/connector/src/main/java/org/geysermc/connector/entity/living/monster/AbstractSkeletonEntity.java +++ b/connector/src/main/java/org/geysermc/connector/entity/living/monster/AbstractSkeletonEntity.java @@ -39,7 +39,7 @@ public class AbstractSkeletonEntity extends MonsterEntity { @Override public void updateBedrockMetadata(EntityMetadata entityMetadata, GeyserSession session) { - if (entityMetadata.getId() == 14) { + if (entityMetadata.getId() == 15) { byte xd = (byte) entityMetadata.getValue(); // A bit of a loophole so the hands get raised - set the target ID to its own ID metadata.put(EntityData.TARGET_EID, ((xd & 4) == 4) ? geyserId : 0); 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 0dac92077..57eeb208d 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 @@ -40,7 +40,7 @@ public class BasePiglinEntity extends MonsterEntity { @Override public void updateBedrockMetadata(EntityMetadata entityMetadata, GeyserSession session) { - if (entityMetadata.getId() == 15) { + 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)); diff --git a/connector/src/main/java/org/geysermc/connector/entity/living/monster/BlazeEntity.java b/connector/src/main/java/org/geysermc/connector/entity/living/monster/BlazeEntity.java index dcbb39350..6e1bdce53 100644 --- a/connector/src/main/java/org/geysermc/connector/entity/living/monster/BlazeEntity.java +++ b/connector/src/main/java/org/geysermc/connector/entity/living/monster/BlazeEntity.java @@ -39,7 +39,7 @@ public class BlazeEntity extends MonsterEntity { @Override public void updateBedrockMetadata(EntityMetadata entityMetadata, GeyserSession session) { - if (entityMetadata.getId() == 15) { + if (entityMetadata.getId() == 16) { byte xd = (byte) entityMetadata.getValue(); metadata.getFlags().setFlag(EntityFlag.ON_FIRE, (xd & 0x01) == 0x01); } 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 b62337ec2..a1dc02821 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 @@ -45,15 +45,15 @@ public class CreeperEntity extends MonsterEntity { @Override public void updateBedrockMetadata(EntityMetadata entityMetadata, GeyserSession session) { - if (entityMetadata.getId() == 15) { + if (entityMetadata.getId() == 16) { if (!ignitedByFlintAndSteel) { metadata.getFlags().setFlag(EntityFlag.IGNITED, (int) entityMetadata.getValue() == 1); } } - if (entityMetadata.getId() == 16) { + if (entityMetadata.getId() == 17) { metadata.getFlags().setFlag(EntityFlag.POWERED, (boolean) entityMetadata.getValue()); } - if (entityMetadata.getId() == 17) { + if (entityMetadata.getId() == 18) { ignitedByFlintAndSteel = (boolean) entityMetadata.getValue(); metadata.getFlags().setFlag(EntityFlag.IGNITED, ignitedByFlintAndSteel); } diff --git a/connector/src/main/java/org/geysermc/connector/entity/living/monster/EnderDragonEntity.java b/connector/src/main/java/org/geysermc/connector/entity/living/monster/EnderDragonEntity.java index 621679798..74bd5b7ce 100644 --- a/connector/src/main/java/org/geysermc/connector/entity/living/monster/EnderDragonEntity.java +++ b/connector/src/main/java/org/geysermc/connector/entity/living/monster/EnderDragonEntity.java @@ -92,7 +92,7 @@ public class EnderDragonEntity extends InsentientEntity implements Tickable { @Override public void updateBedrockMetadata(EntityMetadata entityMetadata, GeyserSession session) { - if (entityMetadata.getId() == 15) { // Phase + if (entityMetadata.getId() == 16) { // Phase phase = (int) entityMetadata.getValue(); phaseTicks = 0; metadata.getFlags().setFlag(EntityFlag.SITTING, isSitting()); @@ -100,7 +100,7 @@ public class EnderDragonEntity extends InsentientEntity implements Tickable { super.updateBedrockMetadata(entityMetadata, session); - if (entityMetadata.getId() == 8) { // Health + if (entityMetadata.getId() == 9) { // Health // Update the health attribute, so that the death animation gets played // Round health up, so that Bedrock doesn't consider the dragon to be dead when health is between 0 and 1 float health = (float) Math.ceil(metadata.getFloat(EntityData.HEALTH)); diff --git a/connector/src/main/java/org/geysermc/connector/entity/living/monster/EndermanEntity.java b/connector/src/main/java/org/geysermc/connector/entity/living/monster/EndermanEntity.java index 0d265b56e..f11e57a8f 100644 --- a/connector/src/main/java/org/geysermc/connector/entity/living/monster/EndermanEntity.java +++ b/connector/src/main/java/org/geysermc/connector/entity/living/monster/EndermanEntity.java @@ -43,11 +43,11 @@ public class EndermanEntity extends MonsterEntity { @Override public void updateBedrockMetadata(EntityMetadata entityMetadata, GeyserSession session) { // Held block - if (entityMetadata.getId() == 15) { + if (entityMetadata.getId() == 16) { metadata.put(EntityData.CARRIED_BLOCK, session.getBlockTranslator().getBedrockBlockId((int) entityMetadata.getValue())); } // "Is screaming" - controls sound - if (entityMetadata.getId() == 16) { + if (entityMetadata.getId() == 17) { if ((boolean) entityMetadata.getValue()) { LevelSoundEvent2Packet packet = new LevelSoundEvent2Packet(); packet.setSound(SoundEvent.STARE); @@ -58,7 +58,7 @@ public class EndermanEntity extends MonsterEntity { } } // "Is staring/provoked" - controls visuals - if (entityMetadata.getId() == 17) { + if (entityMetadata.getId() == 18) { metadata.getFlags().setFlag(EntityFlag.ANGRY, (boolean) entityMetadata.getValue()); } 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 index 69bb384a6..22d91ec36 100644 --- 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 @@ -40,7 +40,7 @@ public class GhastEntity extends FlyingEntity { @Override public void updateBedrockMetadata(EntityMetadata entityMetadata, GeyserSession session) { - if (entityMetadata.getId() == 15) { + if (entityMetadata.getId() == 16) { // If the ghast is attacking metadata.put(EntityData.CHARGE_AMOUNT, (byte) ((boolean) entityMetadata.getValue() ? 1 : 0)); } diff --git a/connector/src/main/java/org/geysermc/connector/entity/living/monster/GuardianEntity.java b/connector/src/main/java/org/geysermc/connector/entity/living/monster/GuardianEntity.java index d254a3299..0b28cd53e 100644 --- a/connector/src/main/java/org/geysermc/connector/entity/living/monster/GuardianEntity.java +++ b/connector/src/main/java/org/geysermc/connector/entity/living/monster/GuardianEntity.java @@ -40,7 +40,7 @@ public class GuardianEntity extends MonsterEntity { @Override public void updateBedrockMetadata(EntityMetadata entityMetadata, GeyserSession session) { - if (entityMetadata.getId() == 16) { + if (entityMetadata.getId() == 17) { Entity entity = session.getEntityCache().getEntityByJavaId((int) entityMetadata.getValue()); if (entity == null && session.getPlayerEntity().getEntityId() == (Integer) entityMetadata.getValue()) { entity = session.getPlayerEntity(); 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 44fa2c49d..5b1ccd342 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,17 +41,17 @@ public class PiglinEntity extends BasePiglinEntity { @Override public void updateBedrockMetadata(EntityMetadata entityMetadata, GeyserSession session) { - if (entityMetadata.getId() == 16) { + if (entityMetadata.getId() == 17) { boolean isBaby = (boolean) entityMetadata.getValue(); if (isBaby) { metadata.put(EntityData.SCALE, .55f); metadata.getFlags().setFlag(EntityFlag.BABY, true); } } - if (entityMetadata.getId() == 17) { + if (entityMetadata.getId() == 18) { metadata.getFlags().setFlag(EntityFlag.CHARGING, (boolean) entityMetadata.getValue()); } - if (entityMetadata.getId() == 18) { + if (entityMetadata.getId() == 19) { metadata.getFlags().setFlag(EntityFlag.DANCING, (boolean) entityMetadata.getValue()); } diff --git a/connector/src/main/java/org/geysermc/connector/entity/living/monster/ShulkerEntity.java b/connector/src/main/java/org/geysermc/connector/entity/living/monster/ShulkerEntity.java index b99f66ac8..142c0012b 100644 --- a/connector/src/main/java/org/geysermc/connector/entity/living/monster/ShulkerEntity.java +++ b/connector/src/main/java/org/geysermc/connector/entity/living/monster/ShulkerEntity.java @@ -26,10 +26,8 @@ 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.Position; import com.github.steveice10.mc.protocol.data.game.world.block.BlockFace; 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 org.geysermc.connector.entity.living.GolemEntity; @@ -46,16 +44,10 @@ public class ShulkerEntity extends GolemEntity { @Override public void updateBedrockMetadata(EntityMetadata entityMetadata, GeyserSession session) { - if (entityMetadata.getId() == 15) { + if (entityMetadata.getId() == 16) { BlockFace blockFace = (BlockFace) entityMetadata.getValue(); metadata.put(EntityData.SHULKER_ATTACH_FACE, (byte) blockFace.ordinal()); } - if (entityMetadata.getId() == 16) { - Position position = (Position) entityMetadata.getValue(); - if (position != null) { - metadata.put(EntityData.SHULKER_ATTACH_POS, Vector3i.from(position.getX(), position.getY(), position.getZ())); - } - } if (entityMetadata.getId() == 17) { int height = (byte) entityMetadata.getValue(); diff --git a/connector/src/main/java/org/geysermc/connector/entity/living/monster/SpiderEntity.java b/connector/src/main/java/org/geysermc/connector/entity/living/monster/SpiderEntity.java index 5706c1d69..65c30289a 100644 --- a/connector/src/main/java/org/geysermc/connector/entity/living/monster/SpiderEntity.java +++ b/connector/src/main/java/org/geysermc/connector/entity/living/monster/SpiderEntity.java @@ -39,7 +39,7 @@ public class SpiderEntity extends MonsterEntity { @Override public void updateBedrockMetadata(EntityMetadata entityMetadata, GeyserSession session) { - if (entityMetadata.getId() == 15) { + if (entityMetadata.getId() == 16) { byte xd = (byte) entityMetadata.getValue(); metadata.getFlags().setFlag(EntityFlag.WALL_CLIMBING, (xd & 0x01) == 0x01); } 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 index cbf0c149a..990a2f3a9 100644 --- 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 @@ -39,7 +39,7 @@ public class VexEntity extends MonsterEntity { @Override public void updateBedrockMetadata(EntityMetadata entityMetadata, GeyserSession session) { - if (entityMetadata.getId() == 15) { + if (entityMetadata.getId() == 16) { 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 diff --git a/connector/src/main/java/org/geysermc/connector/entity/living/monster/WitherEntity.java b/connector/src/main/java/org/geysermc/connector/entity/living/monster/WitherEntity.java index e024b4e55..d6d7f8074 100644 --- a/connector/src/main/java/org/geysermc/connector/entity/living/monster/WitherEntity.java +++ b/connector/src/main/java/org/geysermc/connector/entity/living/monster/WitherEntity.java @@ -44,7 +44,7 @@ public class WitherEntity extends MonsterEntity { public void updateBedrockMetadata(EntityMetadata entityMetadata, GeyserSession session) { long targetID = 0; - if (entityMetadata.getId() >= 15 && entityMetadata.getId() <= 17) { + if (entityMetadata.getId() >= 16 && entityMetadata.getId() <= 18) { Entity entity = session.getEntityCache().getEntityByJavaId((int) entityMetadata.getValue()); if (entity == null && session.getPlayerEntity().getEntityId() == (int) entityMetadata.getValue()) { entity = session.getPlayerEntity(); @@ -55,13 +55,13 @@ public class WitherEntity extends MonsterEntity { } } - if (entityMetadata.getId() == 15) { + if (entityMetadata.getId() == 16) { metadata.put(EntityData.WITHER_TARGET_1, targetID); - } else if (entityMetadata.getId() == 16) { - metadata.put(EntityData.WITHER_TARGET_2, targetID); } else if (entityMetadata.getId() == 17) { - metadata.put(EntityData.WITHER_TARGET_3, targetID); + metadata.put(EntityData.WITHER_TARGET_2, targetID); } else if (entityMetadata.getId() == 18) { + metadata.put(EntityData.WITHER_TARGET_3, targetID); + } else if (entityMetadata.getId() == 19) { metadata.put(EntityData.WITHER_INVULNERABLE_TICKS, entityMetadata.getValue()); // Show the shield for the first few seconds of spawning (like Java) diff --git a/connector/src/main/java/org/geysermc/connector/entity/living/monster/ZoglinEntity.java b/connector/src/main/java/org/geysermc/connector/entity/living/monster/ZoglinEntity.java index 585a1e2ca..dde19927d 100644 --- a/connector/src/main/java/org/geysermc/connector/entity/living/monster/ZoglinEntity.java +++ b/connector/src/main/java/org/geysermc/connector/entity/living/monster/ZoglinEntity.java @@ -40,7 +40,7 @@ public class ZoglinEntity extends MonsterEntity { @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/ZombieEntity.java b/connector/src/main/java/org/geysermc/connector/entity/living/monster/ZombieEntity.java index f3e0fdad8..83dbd0332 100644 --- a/connector/src/main/java/org/geysermc/connector/entity/living/monster/ZombieEntity.java +++ b/connector/src/main/java/org/geysermc/connector/entity/living/monster/ZombieEntity.java @@ -40,7 +40,7 @@ public class ZombieEntity extends MonsterEntity { @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/ZombieVillagerEntity.java b/connector/src/main/java/org/geysermc/connector/entity/living/monster/ZombieVillagerEntity.java index c7fdc494a..eb4881b15 100644 --- 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 @@ -42,11 +42,11 @@ public class ZombieVillagerEntity extends ZombieEntity { @Override public void updateBedrockMetadata(EntityMetadata entityMetadata, GeyserSession session) { - if (entityMetadata.getId() == 18) { + if (entityMetadata.getId() == 19) { metadata.getFlags().setFlag(EntityFlag.IS_TRANSFORMING, (boolean) entityMetadata.getValue()); metadata.getFlags().setFlag(EntityFlag.SHAKING, (boolean) entityMetadata.getValue()); } - if (entityMetadata.getId() == 19) { + if (entityMetadata.getId() == 20) { VillagerData villagerData = (VillagerData) entityMetadata.getValue(); metadata.put(EntityData.VARIANT, VillagerEntity.VILLAGER_PROFESSIONS.get(villagerData.getProfession())); // Actually works properly with the OptionalPack metadata.put(EntityData.MARK_VARIANT, VillagerEntity.VILLAGER_REGIONS.get(villagerData.getType())); diff --git a/connector/src/main/java/org/geysermc/connector/entity/living/monster/raid/SpellcasterIllagerEntity.java b/connector/src/main/java/org/geysermc/connector/entity/living/monster/raid/SpellcasterIllagerEntity.java index 6d4500a1c..7e92a7569 100644 --- a/connector/src/main/java/org/geysermc/connector/entity/living/monster/raid/SpellcasterIllagerEntity.java +++ b/connector/src/main/java/org/geysermc/connector/entity/living/monster/raid/SpellcasterIllagerEntity.java @@ -45,7 +45,7 @@ public class SpellcasterIllagerEntity extends AbstractIllagerEntity { @Override public void updateBedrockMetadata(EntityMetadata entityMetadata, GeyserSession session) { - if (entityMetadata.getId() == 16) { + if (entityMetadata.getId() == 17) { int spellType = (int) (byte) entityMetadata.getValue(); // Summon vex, attack, or wololo metadata.getFlags().setFlag(EntityFlag.CASTING, spellType == 1 || spellType == 2 || spellType == 3); diff --git a/connector/src/main/java/org/geysermc/connector/entity/living/monster/raid/VindicatorEntity.java b/connector/src/main/java/org/geysermc/connector/entity/living/monster/raid/VindicatorEntity.java index 8705a8030..4a9360393 100644 --- a/connector/src/main/java/org/geysermc/connector/entity/living/monster/raid/VindicatorEntity.java +++ b/connector/src/main/java/org/geysermc/connector/entity/living/monster/raid/VindicatorEntity.java @@ -40,7 +40,7 @@ public class VindicatorEntity extends AbstractIllagerEntity { @Override public void updateBedrockMetadata(EntityMetadata entityMetadata, GeyserSession session) { // Allow the axe to be shown if necessary - if (entityMetadata.getId() == 14) { + if (entityMetadata.getId() == 15) { byte xd = (byte) entityMetadata.getValue(); metadata.getFlags().setFlag(EntityFlag.ANGRY, (xd & 4) == 4); } diff --git a/connector/src/main/java/org/geysermc/connector/entity/player/PlayerEntity.java b/connector/src/main/java/org/geysermc/connector/entity/player/PlayerEntity.java index f8eeef307..3cda303cd 100644 --- a/connector/src/main/java/org/geysermc/connector/entity/player/PlayerEntity.java +++ b/connector/src/main/java/org/geysermc/connector/entity/player/PlayerEntity.java @@ -27,6 +27,7 @@ package org.geysermc.connector.entity.player; 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.entity.metadata.Pose; import com.github.steveice10.opennbt.tag.builtin.CompoundTag; import com.nukkitx.math.vector.Vector3f; import com.nukkitx.math.vector.Vector3i; @@ -273,7 +274,7 @@ public class PlayerEntity extends LivingEntity { } // Extra hearts - is not metadata but an attribute on Bedrock - if (entityMetadata.getId() == 14) { + if (entityMetadata.getId() == 15) { UpdateAttributesPacket attributesPacket = new UpdateAttributesPacket(); attributesPacket.setRuntimeEntityId(geyserId); List attributes = new ArrayList<>(); @@ -283,7 +284,7 @@ public class PlayerEntity extends LivingEntity { session.sendUpstreamPacket(attributesPacket); } - if (entityMetadata.getId() == 16) { + if (entityMetadata.getId() == 17) { // OptionalPack usage for toggling skin bits // In Java Edition, a bit being set means that part should be enabled // However, to ensure that the pack still works on other servers, we invert the bit so all values by default @@ -292,10 +293,10 @@ public class PlayerEntity extends LivingEntity { } // Parrot occupying shoulder - if (entityMetadata.getId() == 18 || entityMetadata.getId() == 19) { + if (entityMetadata.getId() == 19 || entityMetadata.getId() == 20) { CompoundTag tag = (CompoundTag) entityMetadata.getValue(); if (tag != null && !tag.isEmpty()) { - if ((entityMetadata.getId() == 18 && leftParrot != null) || (entityMetadata.getId() == 19 && rightParrot != null)) { + if ((entityMetadata.getId() == 19 && leftParrot != null) || (entityMetadata.getId() == 20 && rightParrot != null)) { // No need to update a parrot's data when it already exists return; } @@ -321,10 +322,10 @@ public class PlayerEntity extends LivingEntity { rightParrot = parrot; } } else { - Entity parrot = (entityMetadata.getId() == 18 ? leftParrot : rightParrot); + Entity parrot = (entityMetadata.getId() == 19 ? leftParrot : rightParrot); if (parrot != null) { parrot.despawnEntity(session); - if (entityMetadata.getId() == 18) { + if (entityMetadata.getId() == 19) { leftParrot = null; } else { rightParrot = null; @@ -334,6 +335,26 @@ public class PlayerEntity extends LivingEntity { } } + @Override + protected void setDimensions(Pose pose) { + float height; + switch (pose) { + case SNEAKING: + height = 1.5f; + break; + case FALL_FLYING: + case SPIN_ATTACK: + case SWIMMING: + height = 0.6f; + break; + default: + super.setDimensions(pose); + return; + } + metadata.put(EntityData.BOUNDING_BOX_WIDTH, entityType.getWidth()); + metadata.put(EntityData.BOUNDING_BOX_HEIGHT, height); + } + @Override public void updateBedrockAttributes(GeyserSession session) { // TODO: Don't use duplicated code if (!valid) return; 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 afcdb7e03..4ca9470b0 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 @@ -161,11 +161,16 @@ public enum EntityType { 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"), + AXOLOTL(AxolotlEntity.class, 0, 0.42f, 0.7f, 0.7f, 0f, "minecraft:axolotl"), + GLOW_SQUID(GlowSquidEntity.class, 0, 0.8f, 0.8f, 0.8f, 0f, "minecraft:glow_squid"), + GOAT(GoatEntity.class, 0, 1.3f, 0.9f, 0.9f, 0f, "minecraft:goat"), + MARKER(Entity.class, 0, 0, 0, 0, 0, "minecraft:marker"), // Only should be used for ALL_JAVA_IDENTIFIERS /** * Item frames are handled differently since they are a block in Bedrock. */ ITEM_FRAME(ItemFrameEntity.class, 0, 0, 0), + GLOW_ITEM_FRAME(ItemFrameEntity.class, 0, 0, 0), /** * Not an entity in Bedrock, so we replace it with an evoker diff --git a/connector/src/main/java/org/geysermc/connector/inventory/Inventory.java b/connector/src/main/java/org/geysermc/connector/inventory/Inventory.java index 11a0034ad..664781763 100644 --- a/connector/src/main/java/org/geysermc/connector/inventory/Inventory.java +++ b/connector/src/main/java/org/geysermc/connector/inventory/Inventory.java @@ -66,9 +66,6 @@ public class Inventory { @Setter protected long holderId = -1; - @Getter - protected short transactionId = 0; - @Getter @Setter private boolean pending = false; @@ -114,10 +111,6 @@ public class Inventory { } } - public short getNextTransactionId() { - return ++transactionId; - } - @Override public String toString() { return "Inventory{" + @@ -127,7 +120,6 @@ public class Inventory { ", items=" + Arrays.toString(items) + ", holderPosition=" + holderPosition + ", holderId=" + holderId + - ", transactionId=" + transactionId + '}'; } } diff --git a/connector/src/main/java/org/geysermc/connector/metrics/Metrics.java b/connector/src/main/java/org/geysermc/connector/metrics/Metrics.java index 1457780b1..6ebca3f8c 100644 --- a/connector/src/main/java/org/geysermc/connector/metrics/Metrics.java +++ b/connector/src/main/java/org/geysermc/connector/metrics/Metrics.java @@ -152,20 +152,15 @@ public class Metrics { */ private ObjectNode getServerData() { // OS specific data - int playerAmount = connector.getPlayers().size(); - String osName = System.getProperty("os.name"); String osArch = System.getProperty("os.arch"); String osVersion = System.getProperty("os.version"); - String javaVersion = System.getProperty("java.version"); int coreCount = Runtime.getRuntime().availableProcessors(); ObjectNode data = mapper.createObjectNode(); data.put("serverUUID", serverUUID); - data.put("playerAmount", playerAmount); - data.put("javaVersion", javaVersion); data.put("osName", osName); data.put("osArch", osArch); data.put("osVersion", osVersion); 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 b7365c05d..84fc449e9 100644 --- a/connector/src/main/java/org/geysermc/connector/network/BedrockProtocol.java +++ b/connector/src/main/java/org/geysermc/connector/network/BedrockProtocol.java @@ -26,10 +26,7 @@ package org.geysermc.connector.network; import com.nukkitx.protocol.bedrock.BedrockPacketCodec; -import com.nukkitx.protocol.bedrock.v419.Bedrock_v419; -import com.nukkitx.protocol.bedrock.v422.Bedrock_v422; -import com.nukkitx.protocol.bedrock.v428.Bedrock_v428; -import com.nukkitx.protocol.bedrock.v431.Bedrock_v431; +import com.nukkitx.protocol.bedrock.v440.Bedrock_v440; import java.util.ArrayList; import java.util.List; @@ -42,20 +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_v431.V431_CODEC; + public static final BedrockPacketCodec DEFAULT_BEDROCK_CODEC = Bedrock_v440.V440_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_v419.V419_CODEC.toBuilder() - .minecraftVersion("1.16.100/1.16.101") // We change this as 1.16.100.60 is a beta - .build()); - SUPPORTED_BEDROCK_CODECS.add(Bedrock_v422.V422_CODEC.toBuilder() - .minecraftVersion("1.16.200/1.16.201") - .build()); - SUPPORTED_BEDROCK_CODECS.add(Bedrock_v428.V428_CODEC); SUPPORTED_BEDROCK_CODECS.add(DEFAULT_BEDROCK_CODEC); } 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 a30de4d07..be34ca81b 100644 --- a/connector/src/main/java/org/geysermc/connector/network/UpstreamPacketHandler.java +++ b/connector/src/main/java/org/geysermc/connector/network/UpstreamPacketHandler.java @@ -30,16 +30,13 @@ import com.nukkitx.protocol.bedrock.BedrockPacketCodec; import com.nukkitx.protocol.bedrock.data.ExperimentData; import com.nukkitx.protocol.bedrock.data.ResourcePackType; import com.nukkitx.protocol.bedrock.packet.*; -import com.nukkitx.protocol.bedrock.v428.Bedrock_v428; import org.geysermc.connector.GeyserConnector; import org.geysermc.connector.common.AuthType; import org.geysermc.connector.configuration.GeyserConfiguration; import org.geysermc.connector.network.session.GeyserSession; -import org.geysermc.connector.network.session.cache.AdvancementsCache; import org.geysermc.connector.network.translators.PacketTranslatorRegistry; import org.geysermc.connector.network.translators.item.ItemRegistry; -import org.geysermc.connector.network.translators.world.block.BlockTranslator1_16_100; -import org.geysermc.connector.network.translators.world.block.BlockTranslator1_16_210; +import org.geysermc.connector.network.translators.world.block.BlockTranslator1_17_0; import org.geysermc.connector.utils.*; import java.io.FileInputStream; @@ -74,8 +71,7 @@ public class UpstreamPacketHandler extends LoggingPacketHandler { session.getUpstream().getSession().setPacketCodec(packetCodec); // Set the block translation based off of version - session.setBlockTranslator(packetCodec.getProtocolVersion() >= Bedrock_v428.V428_CODEC.getProtocolVersion() - ? BlockTranslator1_16_210.INSTANCE : BlockTranslator1_16_100.INSTANCE); + session.setBlockTranslator(BlockTranslator1_17_0.INSTANCE); LoginEncryptionUtils.encryptPlayerConnection(connector, session, loginPacket); @@ -153,22 +149,8 @@ public class UpstreamPacketHandler extends LoggingPacketHandler { @Override public boolean handle(ModalFormResponsePacket packet) { - switch (packet.getFormId()) { - case AdvancementsCache.ADVANCEMENT_INFO_FORM_ID: - return session.getAdvancementsCache().handleInfoForm(packet.getFormData()); - case AdvancementsCache.ADVANCEMENTS_LIST_FORM_ID: - return session.getAdvancementsCache().handleListForm(packet.getFormData()); - case AdvancementsCache.ADVANCEMENTS_MENU_FORM_ID: - return session.getAdvancementsCache().handleMenuForm(packet.getFormData()); - case SettingsUtils.SETTINGS_FORM_ID: - return SettingsUtils.handleSettingsForm(session, packet.getFormData()); - case StatisticsUtils.STATISTICS_LIST_FORM_ID: - return StatisticsUtils.handleListForm(session, packet.getFormData()); - case StatisticsUtils.STATISTICS_MENU_FORM_ID: - return StatisticsUtils.handleMenuForm(session, packet.getFormData()); - } - - return LoginEncryptionUtils.authenticateFromForm(session, connector, packet.getFormId(), packet.getFormData()); + session.getFormCache().handleResponse(packet); + return true; } private boolean couldLoginUserByName(String bedrockUsername) { @@ -196,7 +178,7 @@ public class UpstreamPacketHandler extends LoggingPacketHandler { if (!session.isLoggedIn() && !session.isLoggingIn() && session.getRemoteAuthType() == AuthType.ONLINE) { // TODO it is safer to key authentication on something that won't change (UUID, not username) if (!couldLoginUserByName(session.getAuthData().getName())) { - LoginEncryptionUtils.showLoginWindow(session); + LoginEncryptionUtils.buildAndShowLoginWindow(session); } // else we were able to log the user in } 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 1130356c1..73dad7220 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 @@ -58,20 +58,19 @@ import com.nukkitx.protocol.bedrock.data.command.CommandPermission; import com.nukkitx.protocol.bedrock.data.entity.EntityData; import com.nukkitx.protocol.bedrock.data.entity.EntityFlag; import com.nukkitx.protocol.bedrock.packet.*; -import com.nukkitx.protocol.bedrock.v431.Bedrock_v431; import it.unimi.dsi.fastutil.ints.Int2ObjectMap; import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; import it.unimi.dsi.fastutil.ints.IntList; import it.unimi.dsi.fastutil.longs.Long2ObjectMap; import it.unimi.dsi.fastutil.longs.Long2ObjectMaps; import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap; -import it.unimi.dsi.fastutil.objects.*; +import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap; +import it.unimi.dsi.fastutil.objects.ObjectIterator; +import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet; import lombok.AccessLevel; import lombok.Getter; import lombok.NonNull; import lombok.Setter; -import org.geysermc.common.window.CustomFormWindow; -import org.geysermc.common.window.FormWindow; import org.geysermc.connector.GeyserConnector; import org.geysermc.connector.command.CommandSender; import org.geysermc.connector.common.AuthType; @@ -96,18 +95,17 @@ import org.geysermc.connector.network.translators.collision.CollisionManager; import org.geysermc.connector.network.translators.inventory.InventoryTranslator; import org.geysermc.connector.network.translators.item.ItemRegistry; import org.geysermc.connector.network.translators.world.block.BlockTranslator; +import org.geysermc.connector.skin.FloodgateSkinUploader; import org.geysermc.connector.skin.SkinManager; import org.geysermc.connector.utils.*; +import org.geysermc.cumulus.Form; +import org.geysermc.cumulus.util.FormBuilder; +import org.geysermc.floodgate.crypto.FloodgateCipher; import org.geysermc.floodgate.util.BedrockData; -import org.geysermc.floodgate.util.EncryptionUtil; -import java.io.IOException; import java.net.InetAddress; import java.net.InetSocketAddress; import java.nio.charset.StandardCharsets; -import java.security.NoSuchAlgorithmException; -import java.security.PublicKey; -import java.security.spec.InvalidKeySpecException; import java.util.*; import java.util.concurrent.*; import java.util.concurrent.atomic.AtomicInteger; @@ -143,10 +141,10 @@ public class GeyserSession implements CommandSender { private ChunkCache chunkCache; private EntityCache entityCache; private EntityEffectCache effectCache; + private final FormCache formCache; private final PreferencesCache preferencesCache; private final TagCache tagCache; private WorldCache worldCache; - private WindowCache windowCache; private final Int2ObjectMap teleportMap = new Int2ObjectOpenHashMap<>(); private final PlayerInventory playerInventory; @@ -233,12 +231,6 @@ public class GeyserSession implements CommandSender { @Setter private boolean sprinting; - /** - * Not updated if cache chunks is enabled. - */ - @Setter - private boolean jumping; - /** * Whether the player is swimming in water. * Used to update speed when crawling. @@ -368,9 +360,6 @@ public class GeyserSession implements CommandSender { private boolean reducedDebugInfo = false; - @Setter - private CustomFormWindow settingsForm; - /** * The op permission level set by the server */ @@ -414,7 +403,7 @@ public class GeyserSession implements CommandSender { /** * Stores the last text inputted into a sign. - * + *

* Bedrock sends packets every time you update the sign, Java only wants the final packet. * Until we determine that the user has finished editing, we save the sign's current status. */ @@ -451,10 +440,10 @@ public class GeyserSession implements CommandSender { this.chunkCache = new ChunkCache(this); this.entityCache = new EntityCache(this); this.effectCache = new EntityEffectCache(); + this.formCache = new FormCache(this); this.preferencesCache = new PreferencesCache(this); this.tagCache = new TagCache(); this.worldCache = new WorldCache(this); - this.windowCache = new WindowCache(this); this.collisionManager = new CollisionManager(this); @@ -518,12 +507,7 @@ public class GeyserSession implements CommandSender { upstream.sendPacket(entityPacket); CreativeContentPacket creativePacket = new CreativeContentPacket(); - if (upstream.getSession().getPacketCodec().getProtocolVersion() < Bedrock_v431.V431_CODEC.getProtocolVersion()) { - creativePacket.setContents(ItemRegistry.getPre1_16_220CreativeContents()); - } else { - // No additional work required - creativePacket.setContents(ItemRegistry.CREATIVE_ITEMS); - } + creativePacket.setContents(ItemRegistry.CREATIVE_ITEMS); upstream.sendPacket(creativePacket); PlayStatusPacket playStatusPacket = new PlayStatusPacket(); @@ -588,7 +572,16 @@ public class GeyserSession implements CommandSender { protocol = new MinecraftProtocol(authenticationService.getSelectedProfile(), authenticationService.getAccessToken()); } else { - protocol = new MinecraftProtocol(username); + // always replace spaces when using Floodgate, + // as usernames with spaces cause issues with Bungeecord's login cycle. + // However, this doesn't affect the final username as Floodgate is still in charge of that. + // So if you have (for example) replace spaces enabled on Floodgate the spaces will re-appear. + String validUsername = username; + if (remoteAuthType == AuthType.FLOODGATE) { + validUsername = username.replace(' ', '_'); + } + + protocol = new MinecraftProtocol(validUsername); } connectDownstream(); @@ -617,7 +610,7 @@ public class GeyserSession implements CommandSender { MsaAuthenticationService msaAuthenticationService = new MsaAuthenticationService(GeyserConnector.OAUTH_CLIENT_ID); MsaAuthenticationService.MsCodeResponse response = msaAuthenticationService.getAuthCode(); - LoginEncryptionUtils.showMicrosoftCodeWindow(this, response); + LoginEncryptionUtils.buildAndShowMicrosoftCodeWindow(this, response); // This just looks cool SetTimePacket packet = new SetTimePacket(); @@ -662,24 +655,6 @@ public class GeyserSession implements CommandSender { */ private void connectDownstream() { boolean floodgate = this.remoteAuthType == AuthType.FLOODGATE; - final PublicKey publicKey; - - if (floodgate) { - PublicKey key = null; - try { - key = EncryptionUtil.getKeyFromFile( - connector.getConfig().getFloodgateKeyPath(), - PublicKey.class - ); - } catch (IOException | InvalidKeySpecException | NoSuchAlgorithmException e) { - connector.getLogger().error(LanguageUtils.getLocaleStringLog("geyser.auth.floodgate.bad_key"), e); - } - publicKey = key; - } else publicKey = null; - - if (publicKey != null) { - connector.getLogger().info(LanguageUtils.getLocaleStringLog("geyser.auth.floodgate.loaded_key")); - } // Start ticking tickThread = connector.getGeneralThreadPool().scheduleAtFixedRate(this::tick, 50, 50, TimeUnit.MILLISECONDS); @@ -701,22 +676,40 @@ public class GeyserSession implements CommandSender { if (event.getPacket() instanceof HandshakePacket) { String addressSuffix; if (floodgate) { - String encrypted = ""; + byte[] encryptedData; + try { - encrypted = EncryptionUtil.encryptBedrockData(publicKey, new BedrockData( + FloodgateSkinUploader skinUploader = connector.getSkinUploader(); + FloodgateCipher cipher = connector.getCipher(); + + encryptedData = cipher.encryptFromString(BedrockData.of( clientData.getGameVersion(), authData.getName(), authData.getXboxUUID(), - clientData.getDeviceOS().ordinal(), + clientData.getDeviceOs().ordinal(), clientData.getLanguageCode(), + clientData.getUiProfile().ordinal(), clientData.getCurrentInputMode().ordinal(), - upstream.getAddress().getAddress().getHostAddress() - )); + upstream.getAddress().getAddress().getHostAddress(), + skinUploader.getId(), + skinUploader.getVerifyCode(), + connector.getTimeSyncer() + ).toString()); + + if (!connector.getTimeSyncer().hasUsefulOffset()) { + connector.getLogger().warning( + "We couldn't make sure that your system clock is accurate. " + + "This can cause issues with logging in." + ); + } + } catch (Exception e) { connector.getLogger().error(LanguageUtils.getLocaleStringLog("geyser.auth.floodgate.encrypt_fail"), e); + disconnect(LanguageUtils.getPlayerLocaleString("geyser.auth.floodgate.encryption_fail", getClientData().getLanguageCode())); + return; } - addressSuffix = '\0' + BedrockData.FLOODGATE_IDENTIFIER + '\0' + encrypted; + addressSuffix = '\0' + new String(encryptedData, StandardCharsets.UTF_8); } else { addressSuffix = ""; } @@ -799,6 +792,12 @@ public class GeyserSession implements CommandSender { if (remoteAuthType == AuthType.OFFLINE || playerEntity.getUuid().getMostSignificantBits() == 0) { SkinManager.handleBedrockSkin(playerEntity, clientData); } + + if (remoteAuthType == AuthType.FLOODGATE) { + // We'll send the skin upload a bit after the handshake packet (aka this packet), + // because otherwise the global server returns the data too fast. + getAuthData().upload(connector); + } } PacketTranslatorRegistry.JAVA_TRANSLATOR.translate(event.getPacket().getClass(), event.getPacket(), GeyserSession.this); @@ -843,7 +842,6 @@ public class GeyserSession implements CommandSender { this.entityCache = null; this.effectCache = null; this.worldCache = null; - this.windowCache = null; closed = true; } @@ -984,10 +982,6 @@ public class GeyserSession implements CommandSender { return clientData.getLanguageCode(); } - public void sendForm(FormWindow window, int id) { - windowCache.showWindow(window, id); - } - public void setRenderDistance(int renderDistance) { renderDistance = GenericMath.ceil(++renderDistance * MathUtils.SQRT_OF_TWO); //square to circle this.renderDistance = renderDistance; @@ -1001,8 +995,12 @@ public class GeyserSession implements CommandSender { return this.upstream.getAddress(); } - public void sendForm(FormWindow window) { - windowCache.showWindow(window); + public void sendForm(Form form) { + formCache.showForm(form); + } + + public void sendForm(FormBuilder formBuilder) { + formCache.showForm(formBuilder.build()); } private void startGame() { @@ -1054,14 +1052,14 @@ public class GeyserSession implements CommandSender { startGamePacket.setItemEntries(ItemRegistry.ITEMS); startGamePacket.setVanillaVersion("*"); startGamePacket.setInventoriesServerAuthoritative(true); - startGamePacket.setAuthoritativeMovementMode(AuthoritativeMovementMode.CLIENT); // can be removed once 1.16.200 support is dropped + startGamePacket.setServerEngine(""); // Do we want to fill this in? SyncedPlayerMovementSettings settings = new SyncedPlayerMovementSettings(); settings.setMovementMode(AuthoritativeMovementMode.CLIENT); settings.setRewindHistorySize(0); settings.setServerAuthoritativeBlockBreaking(false); startGamePacket.setPlayerMovementSettings(settings); - + upstream.sendPacket(startGamePacket); } @@ -1179,7 +1177,7 @@ public class GeyserSession implements CommandSender { /** * Queue a packet to be sent to player. - * + * * @param packet the bedrock packet from the NukkitX protocol lib */ public void sendUpstreamPacket(BedrockPacket packet) { @@ -1244,7 +1242,7 @@ public class GeyserSession implements CommandSender { * Send a gamerule value to the client * * @param gameRule The gamerule to send - * @param value The value of the gamerule + * @param value The value of the gamerule */ public void sendGameRule(String gameRule, Object value) { GameRulesChangedPacket gameRulesChangedPacket = new GameRulesChangedPacket(); diff --git a/connector/src/main/java/org/geysermc/connector/network/session/auth/AuthData.java b/connector/src/main/java/org/geysermc/connector/network/session/auth/AuthData.java index ba9e13548..463276891 100644 --- a/connector/src/main/java/org/geysermc/connector/network/session/auth/AuthData.java +++ b/connector/src/main/java/org/geysermc/connector/network/session/auth/AuthData.java @@ -25,16 +25,26 @@ package org.geysermc.connector.network.session.auth; -import lombok.AllArgsConstructor; +import com.fasterxml.jackson.databind.JsonNode; import lombok.Getter; +import lombok.RequiredArgsConstructor; +import org.geysermc.connector.GeyserConnector; import java.util.UUID; -@Getter -@AllArgsConstructor +@RequiredArgsConstructor public class AuthData { + @Getter private final String name; + @Getter private final UUID UUID; + @Getter private final String xboxUUID; - private String name; - private UUID UUID; - private String xboxUUID; + private final JsonNode certChainData; + private final String clientData; + + public void upload(GeyserConnector connector) { + // we can't upload the skin in LoginEncryptionUtil since the global server would return + // the skin too fast, that's why we upload it after we know for sure that the target server + // is ready to handle the result of the global server + connector.getSkinUploader().uploadSkin(certChainData, clientData); + } } diff --git a/connector/src/main/java/org/geysermc/connector/network/session/auth/BedrockClientData.java b/connector/src/main/java/org/geysermc/connector/network/session/auth/BedrockClientData.java index 16e06c066..fc4d1164a 100644 --- a/connector/src/main/java/org/geysermc/connector/network/session/auth/BedrockClientData.java +++ b/connector/src/main/java/org/geysermc/connector/network/session/auth/BedrockClientData.java @@ -25,17 +25,18 @@ package org.geysermc.connector.network.session.auth; -import com.fasterxml.jackson.annotation.JsonEnumDefaultValue; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonProperty; import lombok.Getter; -import org.geysermc.floodgate.util.DeviceOS; +import org.geysermc.floodgate.util.DeviceOs; +import org.geysermc.floodgate.util.InputMode; +import org.geysermc.floodgate.util.UiProfile; import java.util.UUID; @JsonIgnoreProperties(ignoreUnknown = true) @Getter -public class BedrockClientData { +public final class BedrockClientData { @JsonProperty(value = "GameVersion") private String gameVersion; @JsonProperty(value = "ServerAddress") @@ -77,9 +78,9 @@ public class BedrockClientData { @JsonProperty(value = "DeviceModel") private String deviceModel; @JsonProperty(value = "DeviceOS") - private DeviceOS deviceOS; + private DeviceOs deviceOs; @JsonProperty(value = "UIProfile") - private UIProfile uiProfile; + private UiProfile uiProfile; @JsonProperty(value = "GuiScale") private int guiScale; @JsonProperty(value = "CurrentInputMode") @@ -106,18 +107,19 @@ public class BedrockClientData { @JsonProperty(value = "PlayFabId") private String playFabId; - public enum UIProfile { - @JsonEnumDefaultValue - CLASSIC, - POCKET + public DeviceOs getDeviceOs() { + return deviceOs != null ? deviceOs : DeviceOs.UNKNOWN; } - public enum InputMode { - @JsonEnumDefaultValue - UNKNOWN, - KEYBOARD_MOUSE, - TOUCH, // I guess Touch? - CONTROLLER, - VR + public InputMode getCurrentInputMode() { + return currentInputMode != null ? currentInputMode : InputMode.UNKNOWN; + } + + public InputMode getDefaultInputMode() { + return defaultInputMode != null ? defaultInputMode : InputMode.UNKNOWN; + } + + public UiProfile getUiProfile() { + return uiProfile != null ? uiProfile : UiProfile.CLASSIC; } } diff --git a/connector/src/main/java/org/geysermc/connector/network/session/cache/AdvancementsCache.java b/connector/src/main/java/org/geysermc/connector/network/session/cache/AdvancementsCache.java index 369967acc..98ec5b262 100644 --- a/connector/src/main/java/org/geysermc/connector/network/session/cache/AdvancementsCache.java +++ b/connector/src/main/java/org/geysermc/connector/network/session/cache/AdvancementsCache.java @@ -29,26 +29,19 @@ import com.github.steveice10.mc.protocol.data.game.advancement.Advancement; import com.github.steveice10.mc.protocol.packet.ingame.client.window.ClientAdvancementTabPacket; import lombok.Getter; import lombok.Setter; -import org.geysermc.common.window.SimpleFormWindow; -import org.geysermc.common.window.button.FormButton; -import org.geysermc.common.window.response.SimpleFormResponse; import org.geysermc.connector.network.session.GeyserSession; import org.geysermc.connector.network.translators.chat.MessageTranslator; import org.geysermc.connector.utils.GeyserAdvancement; import org.geysermc.connector.utils.LanguageUtils; import org.geysermc.connector.utils.LocaleUtils; +import org.geysermc.cumulus.SimpleForm; +import org.geysermc.cumulus.response.SimpleFormResponse; import java.util.HashMap; import java.util.List; import java.util.Map; public class AdvancementsCache { - - // Different form IDs - public static final int ADVANCEMENTS_MENU_FORM_ID = 1341; - public static final int ADVANCEMENTS_LIST_FORM_ID = 1342; - public static final int ADVANCEMENT_INFO_FORM_ID = 1343; - /** * Stores the player's advancement progress */ @@ -74,73 +67,128 @@ public class AdvancementsCache { } /** - * Build a form with all advancement categories - * - * @return The built advancement category menu + * Build and send a form with all advancement categories */ - public SimpleFormWindow buildMenuForm() { - // Cache the language for cleaner access - String language = session.getClientData().getLanguageCode(); + public void buildAndShowMenuForm() { + SimpleForm.Builder builder = + SimpleForm.builder() + .translator(LocaleUtils::getLocaleString, session.getLocale()) + .title("gui.advancements"); - // Created menu window for advancement categories - SimpleFormWindow window = new SimpleFormWindow(LocaleUtils.getLocaleString("gui.advancements", language), ""); + boolean hasAdvancements = false; for (Map.Entry advancement : storedAdvancements.entrySet()) { if (advancement.getValue().getParentId() == null) { // No parent means this is a root advancement - window.getButtons().add(new FormButton(MessageTranslator.convertMessage(advancement.getValue().getDisplayData().getTitle(), language))); + hasAdvancements = true; + builder.button(MessageTranslator.convertMessage(advancement.getValue().getDisplayData().getTitle(), session.getLocale())); } } - if (window.getButtons().isEmpty()) { - window.setContent(LocaleUtils.getLocaleString("advancements.empty", language)); + if (!hasAdvancements) { + builder.content("advancements.empty"); } - return window; + builder.responseHandler((form, responseData) -> { + SimpleFormResponse response = form.parseResponse(responseData); + if (!response.isCorrect()) { + return; + } + + String id = ""; + + int advancementIndex = 0; + for (Map.Entry advancement : storedAdvancements.entrySet()) { + if (advancement.getValue().getParentId() == null) { // Root advancement + if (advancementIndex == response.getClickedButtonId()) { + id = advancement.getKey(); + break; + } else { + advancementIndex++; + } + } + } + + if (!id.equals("")) { + if (id.equals(currentAdvancementCategoryId)) { + // The server thinks we are already on this tab + buildAndShowListForm(); + } else { + // Send a packet indicating that we intend to open this particular advancement window + ClientAdvancementTabPacket packet = new ClientAdvancementTabPacket(id); + session.sendDownstreamPacket(packet); + // Wait for a response there + } + } + }); + + session.sendForm(builder); } /** - * Builds the list of advancements - * - * @return The built list form + * Build and send the list of advancements */ - public SimpleFormWindow buildListForm() { - // Cache the language for easier access - String language = session.getLocale(); - String id = currentAdvancementCategoryId; + public void buildAndShowListForm() { GeyserAdvancement categoryAdvancement = storedAdvancements.get(currentAdvancementCategoryId); + String language = session.getLocale(); - // Create the window - SimpleFormWindow window = new SimpleFormWindow(MessageTranslator.convertMessage(categoryAdvancement.getDisplayData().getTitle(), language), - MessageTranslator.convertMessage(categoryAdvancement.getDisplayData().getDescription(), language)); + SimpleForm.Builder builder = + SimpleForm.builder() + .title(MessageTranslator.convertMessage(categoryAdvancement.getDisplayData().getTitle(), language)) + .content(MessageTranslator.convertMessage(categoryAdvancement.getDisplayData().getDescription(), language)); - if (id != null) { - for (Map.Entry advancementEntry : storedAdvancements.entrySet()) { - GeyserAdvancement advancement = advancementEntry.getValue(); + if (currentAdvancementCategoryId != null) { + for (GeyserAdvancement advancement : storedAdvancements.values()) { if (advancement != null) { if (advancement.getParentId() != null && currentAdvancementCategoryId.equals(advancement.getRootId(this))) { - boolean earned = isEarned(advancement); - - if (earned || !advancement.getDisplayData().isShowToast()) { - window.getButtons().add(new FormButton("§6" + MessageTranslator.convertMessage(advancementEntry.getValue().getDisplayData().getTitle()) + "\n")); - } else { - window.getButtons().add(new FormButton(MessageTranslator.convertMessage(advancementEntry.getValue().getDisplayData().getTitle()) + "\n")); - } + boolean color = isEarned(advancement) || !advancement.getDisplayData().isShowToast(); + builder.button((color ? "§6" : "") + MessageTranslator.convertMessage(advancement.getDisplayData().getTitle()) + '\n'); } } } } - window.getButtons().add(new FormButton(LanguageUtils.getPlayerLocaleString("gui.back", language))); + builder.button(LanguageUtils.getPlayerLocaleString("gui.back", language)); - return window; + builder.responseHandler((form, responseData) -> { + SimpleFormResponse response = form.parseResponse(responseData); + if (!response.isCorrect()) { + // Indicate that we have closed the current advancement tab + session.sendDownstreamPacket(new ClientAdvancementTabPacket()); + return; + } + + GeyserAdvancement advancement = null; + int advancementIndex = 0; + // Loop around to find the advancement that the client pressed + for (GeyserAdvancement advancementEntry : storedAdvancements.values()) { + if (advancementEntry.getParentId() != null && + currentAdvancementCategoryId.equals(advancementEntry.getRootId(this))) { + if (advancementIndex == response.getClickedButtonId()) { + advancement = advancementEntry; + break; + } else { + advancementIndex++; + } + } + } + + if (advancement != null) { + buildAndShowInfoForm(advancement); + } else { + buildAndShowMenuForm(); + // Indicate that we have closed the current advancement tab + session.sendDownstreamPacket(new ClientAdvancementTabPacket()); + } + }); + + session.sendForm(builder); } /** * Builds the advancement display info based on the chosen category * * @param advancement The advancement used to create the info display - * @return The information for the chosen advancement */ - public SimpleFormWindow buildInfoForm(GeyserAdvancement advancement) { + public void buildAndShowInfoForm(GeyserAdvancement advancement) { // Cache language for easier access String language = session.getLocale(); @@ -160,16 +208,24 @@ public class AdvancementsCache { Parent Advancement: Minecraft // If relevant */ - String content = description + "\n\n§f" + - earnedString + "\n"; + String content = description + "\n\n§f" + earnedString + "\n"; if (!currentAdvancementCategoryId.equals(advancement.getParentId())) { // Only display the parent if it is not the category content += LanguageUtils.getPlayerLocaleString("geyser.advancements.parentid", language, MessageTranslator.convertMessage(storedAdvancements.get(advancement.getParentId()).getDisplayData().getTitle(), language)); } - SimpleFormWindow window = new SimpleFormWindow(MessageTranslator.convertMessage(advancement.getDisplayData().getTitle()), content); - window.getButtons().add(new FormButton(LanguageUtils.getPlayerLocaleString("gui.back", language))); - return window; + session.sendForm( + SimpleForm.builder() + .title(MessageTranslator.convertMessage(advancement.getDisplayData().getTitle())) + .content(content) + .button(LanguageUtils.getPlayerLocaleString("gui.back", language)) + .responseHandler((form, responseData) -> { + SimpleFormResponse response = form.parseResponse(responseData); + if (response.isCorrect()) { + buildAndShowListForm(); + } + }) + ); } /** @@ -209,108 +265,6 @@ public class AdvancementsCache { return earned; } - /** - * Handle the menu form response - * - * @param response The response string to parse - * @return True if the form was parsed correctly, false if not - */ - public boolean handleMenuForm(String response) { - SimpleFormWindow menuForm = (SimpleFormWindow) session.getWindowCache().getWindows().get(ADVANCEMENTS_MENU_FORM_ID); - menuForm.setResponse(response); - - SimpleFormResponse formResponse = (SimpleFormResponse) menuForm.getResponse(); - - String id = ""; - if (formResponse != null && formResponse.getClickedButton() != null) { - int advancementIndex = 0; - for (Map.Entry advancement : storedAdvancements.entrySet()) { - if (advancement.getValue().getParentId() == null) { // Root advancement - if (advancementIndex == formResponse.getClickedButtonId()) { - id = advancement.getKey(); - break; - } else { - advancementIndex++; - } - } - } - } - if (!id.equals("")) { - if (id.equals(currentAdvancementCategoryId)) { - // The server thinks we are already on this tab - session.sendForm(buildListForm(), ADVANCEMENTS_LIST_FORM_ID); - } else { - // Send a packet indicating that we intend to open this particular advancement window - ClientAdvancementTabPacket packet = new ClientAdvancementTabPacket(id); - session.sendDownstreamPacket(packet); - // Wait for a response there - } - } - - return true; - } - - /** - * Handle the list form response (Advancement category choice) - * - * @param response The response string to parse - * @return True if the form was parsed correctly, false if not - */ - public boolean handleListForm(String response) { - SimpleFormWindow listForm = (SimpleFormWindow) session.getWindowCache().getWindows().get(ADVANCEMENTS_LIST_FORM_ID); - listForm.setResponse(response); - - SimpleFormResponse formResponse = (SimpleFormResponse) listForm.getResponse(); - - if (!listForm.isClosed() && formResponse != null && formResponse.getClickedButton() != null) { - GeyserAdvancement advancement = null; - int advancementIndex = 0; - // Loop around to find the advancement that the client pressed - for (GeyserAdvancement advancementEntry : storedAdvancements.values()) { - if (advancementEntry.getParentId() != null && - currentAdvancementCategoryId.equals(advancementEntry.getRootId(this))) { - if (advancementIndex == formResponse.getClickedButtonId()) { - advancement = advancementEntry; - break; - } else { - advancementIndex++; - } - } - } - if (advancement != null) { - session.sendForm(buildInfoForm(advancement), ADVANCEMENT_INFO_FORM_ID); - } else { - session.sendForm(buildMenuForm(), ADVANCEMENTS_MENU_FORM_ID); - // Indicate that we have closed the current advancement tab - session.sendDownstreamPacket(new ClientAdvancementTabPacket()); - } - } else { - // Indicate that we have closed the current advancement tab - session.sendDownstreamPacket(new ClientAdvancementTabPacket()); - } - - return true; - } - - /** - * Handle the info form response - * - * @param response The response string to parse - * @return True if the form was parsed correctly, false if not - */ - public boolean handleInfoForm(String response) { - SimpleFormWindow listForm = (SimpleFormWindow) session.getWindowCache().getWindows().get(ADVANCEMENT_INFO_FORM_ID); - listForm.setResponse(response); - - SimpleFormResponse formResponse = (SimpleFormResponse) listForm.getResponse(); - - if (!listForm.isClosed() && formResponse != null && formResponse.getClickedButton() != null) { - session.sendForm(buildListForm(), ADVANCEMENTS_LIST_FORM_ID); - } - - return true; - } - public String getColorFromAdvancementFrameType(GeyserAdvancement advancement) { String base = "\u00a7"; if (advancement.getDisplayData().getFrameType() == Advancement.DisplayData.FrameType.CHALLENGE) { 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 d182a6f12..e9d97d66d 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 @@ -29,23 +29,20 @@ import com.github.steveice10.mc.protocol.data.game.chunk.Chunk; import com.github.steveice10.mc.protocol.data.game.chunk.Column; import it.unimi.dsi.fastutil.longs.Long2ObjectMap; import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap; +import lombok.Setter; import org.geysermc.connector.network.session.GeyserSession; import org.geysermc.connector.network.translators.world.block.BlockTranslator; import org.geysermc.connector.utils.MathUtils; public class ChunkCache { - private static final int MINIMUM_WORLD_HEIGHT = 0; - private final boolean cache; - private final Long2ObjectMap chunks; + @Setter + private int minY; + public ChunkCache(GeyserSession session) { - if (session.getConnector().getWorldManager().hasOwnChunkCache()) { - this.cache = false; // To prevent Spigot from initializing - } else { - this.cache = session.getConnector().getConfig().isCacheChunks(); - } + this.cache = !session.getConnector().getWorldManager().hasOwnChunkCache(); // To prevent Spigot from initializing chunks = cache ? new Long2ObjectOpenHashMap<>() : null; } @@ -87,12 +84,12 @@ public class ChunkCache { return; } - if (y < MINIMUM_WORLD_HEIGHT || (y >> 4) > column.getChunks().length - 1) { + if (y < minY || (y >> 4) > column.getChunks().length - 1) { // Y likely goes above or below the height limit of this world return; } - Chunk chunk = column.getChunks()[y >> 4]; + Chunk chunk = column.getChunks()[(y >> 4) - getChunkMinY()]; if (chunk != null) { chunk.set(x & 0xF, y & 0xF, z & 0xF, block); } @@ -108,12 +105,12 @@ public class ChunkCache { return BlockTranslator.JAVA_AIR_ID; } - if (y < MINIMUM_WORLD_HEIGHT || (y >> 4) > column.getChunks().length - 1) { + if (y < minY || (y >> 4) > column.getChunks().length - 1) { // Y likely goes above or below the height limit of this world return BlockTranslator.JAVA_AIR_ID; } - Chunk chunk = column.getChunks()[y >> 4]; + Chunk chunk = column.getChunks()[(y >> 4) - getChunkMinY()]; if (chunk != null) { return chunk.get(x & 0xF, y & 0xF, z & 0xF); } @@ -129,4 +126,8 @@ public class ChunkCache { long chunkPosition = MathUtils.chunkPositionToLong(chunkX, chunkZ); chunks.remove(chunkPosition); } + + public int getChunkMinY() { + return minY >> 4; + } } diff --git a/connector/src/main/java/org/geysermc/connector/network/session/cache/FormCache.java b/connector/src/main/java/org/geysermc/connector/network/session/cache/FormCache.java new file mode 100644 index 000000000..1cdcf228b --- /dev/null +++ b/connector/src/main/java/org/geysermc/connector/network/session/cache/FormCache.java @@ -0,0 +1,92 @@ +/* + * 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.session.cache; + +import com.nukkitx.protocol.bedrock.packet.ModalFormRequestPacket; +import com.nukkitx.protocol.bedrock.packet.ModalFormResponsePacket; +import com.nukkitx.protocol.bedrock.packet.NetworkStackLatencyPacket; +import it.unimi.dsi.fastutil.ints.Int2ObjectMap; +import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; +import lombok.RequiredArgsConstructor; +import org.geysermc.connector.network.session.GeyserSession; +import org.geysermc.cumulus.Form; +import org.geysermc.cumulus.SimpleForm; + +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.function.Consumer; + +@RequiredArgsConstructor +public class FormCache { + private final AtomicInteger formId = new AtomicInteger(0); + private final Int2ObjectMap

forms = new Int2ObjectOpenHashMap<>(); + private final GeyserSession session; + + public int addForm(Form form) { + int windowId = formId.getAndIncrement(); + forms.put(windowId, form); + return windowId; + } + + public int showForm(Form form) { + int windowId = addForm(form); + + ModalFormRequestPacket formRequestPacket = new ModalFormRequestPacket(); + formRequestPacket.setFormId(windowId); + formRequestPacket.setFormData(form.getJsonData()); + session.sendUpstreamPacket(formRequestPacket); + + // Hack to fix the (url) image loading bug + if (form instanceof SimpleForm) { + NetworkStackLatencyPacket latencyPacket = new NetworkStackLatencyPacket(); + latencyPacket.setFromServer(true); + latencyPacket.setTimestamp(-System.currentTimeMillis()); + session.getConnector().getGeneralThreadPool().schedule( + () -> session.sendUpstreamPacket(latencyPacket), + 500, TimeUnit.MILLISECONDS); + } + + return windowId; + } + + public void handleResponse(ModalFormResponsePacket response) { + Form form = forms.get(response.getFormId()); + if (form == null) { + return; + } + + Consumer responseConsumer = form.getResponseHandler(); + if (responseConsumer != null) { + responseConsumer.accept(response.getFormData()); + } + + removeWindow(response.getFormId()); + } + + public boolean removeWindow(int id) { + return forms.remove(id) != null; + } +} diff --git a/connector/src/main/java/org/geysermc/connector/network/session/cache/TagCache.java b/connector/src/main/java/org/geysermc/connector/network/session/cache/TagCache.java index c46e37b74..b1427a864 100644 --- a/connector/src/main/java/org/geysermc/connector/network/session/cache/TagCache.java +++ b/connector/src/main/java/org/geysermc/connector/network/session/cache/TagCache.java @@ -39,24 +39,63 @@ import java.util.Map; */ public class TagCache { /* Blocks */ - private IntList wool = IntLists.emptyList(); + private IntList leaves; + private IntList wool; + + private IntList axeEffective; + private IntList hoeEffective; + private IntList pickaxeEffective; + private IntList shovelEffective; + + private IntList requiresStoneTool; + private IntList requiresIronTool; + private IntList requiresDiamondTool; + /* Items */ - private IntList flowers = IntLists.emptyList(); - private IntList piglinLoved = IntLists.emptyList(); + private IntList flowers; + private IntList foxFood; + private IntList piglinLoved; + + public TagCache() { + // Ensure all lists are non-null + clear(); + } public void loadPacket(ServerDeclareTagsPacket packet) { - Map blockTags = packet.getBlockTags(); + Map blockTags = packet.getTags().get("minecraft:block"); + this.leaves = IntList.of(blockTags.get("minecraft:leaves")); this.wool = IntList.of(blockTags.get("minecraft:wool")); - Map itemTags = packet.getItemTags(); + this.axeEffective = IntList.of(blockTags.get("minecraft:mineable/axe")); + this.hoeEffective = IntList.of(blockTags.get("minecraft:mineable/hoe")); + this.pickaxeEffective = IntList.of(blockTags.get("minecraft:mineable/pickaxe")); + this.shovelEffective = IntList.of(blockTags.get("minecraft:mineable/shovel")); + + this.requiresStoneTool = IntList.of(blockTags.get("minecraft:needs_stone_tool")); + this.requiresIronTool = IntList.of(blockTags.get("minecraft:needs_iron_tool")); + this.requiresDiamondTool = IntList.of(blockTags.get("minecraft:needs_diamond_tool")); + + Map itemTags = packet.getTags().get("minecraft:item"); this.flowers = IntList.of(itemTags.get("minecraft:flowers")); + this.foxFood = IntList.of(itemTags.get("minecraft:fox_food")); this.piglinLoved = IntList.of(itemTags.get("minecraft:piglin_loved")); } public void clear() { + this.leaves = IntLists.emptyList(); this.wool = IntLists.emptyList(); + this.axeEffective = IntLists.emptyList(); + this.hoeEffective = IntLists.emptyList(); + this.pickaxeEffective = IntLists.emptyList(); + this.shovelEffective = IntLists.emptyList(); + + this.requiresStoneTool = IntLists.emptyList(); + this.requiresIronTool = IntLists.emptyList(); + this.requiresDiamondTool = IntLists.emptyList(); + this.flowers = IntLists.emptyList(); + this.foxFood = IntLists.emptyList(); this.piglinLoved = IntLists.emptyList(); } @@ -64,11 +103,44 @@ public class TagCache { return flowers.contains(itemEntry.getJavaId()); } + public boolean isFoxFood(ItemEntry itemEntry) { + return foxFood.contains(itemEntry.getJavaId()); + } + public boolean shouldPiglinAdmire(ItemEntry itemEntry) { return piglinLoved.contains(itemEntry.getJavaId()); } - public boolean isWool(BlockMapping blockMapping) { - return wool.contains(blockMapping.getJavaBlockId()); + public boolean isAxeEffective(BlockMapping blockMapping) { + return axeEffective.contains(blockMapping.getJavaBlockId()); + } + + public boolean isHoeEffective(BlockMapping blockMapping) { + return hoeEffective.contains(blockMapping.getJavaBlockId()); + } + + public boolean isPickaxeEffective(BlockMapping blockMapping) { + return pickaxeEffective.contains(blockMapping.getJavaBlockId()); + } + + public boolean isShovelEffective(BlockMapping blockMapping) { + return shovelEffective.contains(blockMapping.getJavaBlockId()); + } + + public boolean isShearsEffective(BlockMapping blockMapping) { + int javaBlockId = blockMapping.getJavaBlockId(); + return leaves.contains(javaBlockId) || wool.contains(javaBlockId); + } + + public boolean requiresStoneTool(BlockMapping blockMapping) { + return requiresStoneTool.contains(blockMapping.getJavaBlockId()); + } + + public boolean requiresIronTool(BlockMapping blockMapping) { + return requiresIronTool.contains(blockMapping.getJavaBlockId()); + } + + public boolean requiresDiamondTool(BlockMapping blockMapping) { + return requiresDiamondTool.contains(blockMapping.getJavaBlockId()); } } diff --git a/connector/src/main/java/org/geysermc/connector/network/session/cache/WindowCache.java b/connector/src/main/java/org/geysermc/connector/network/session/cache/WindowCache.java deleted file mode 100644 index a114b8bbc..000000000 --- a/connector/src/main/java/org/geysermc/connector/network/session/cache/WindowCache.java +++ /dev/null @@ -1,81 +0,0 @@ -/* - * Copyright (c) 2019-2021 GeyserMC. http://geysermc.org - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - * - * @author GeyserMC - * @link https://github.com/GeyserMC/Geyser - */ - -package org.geysermc.connector.network.session.cache; - -import com.nukkitx.protocol.bedrock.packet.ModalFormRequestPacket; - -import it.unimi.dsi.fastutil.ints.Int2ObjectMap; -import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; - -import lombok.Getter; - -import org.geysermc.common.window.FormWindow; -import org.geysermc.connector.network.session.GeyserSession; - -public class WindowCache { - - private final GeyserSession session; - - @Getter - private final Int2ObjectMap windows = new Int2ObjectOpenHashMap<>(); - - public WindowCache(GeyserSession session) { - this.session = session; - } - - public void addWindow(FormWindow window) { - windows.put(windows.size() + 1, window); - } - - public void addWindow(FormWindow window, int id) { - windows.put(id, window); - } - - public void showWindow(FormWindow window) { - showWindow(window, windows.size() + 1); - } - - public void showWindow(int id) { - if (!windows.containsKey(id)) - return; - - ModalFormRequestPacket formRequestPacket = new ModalFormRequestPacket(); - formRequestPacket.setFormId(id); - formRequestPacket.setFormData(windows.get(id).getJSONData()); - - session.sendUpstreamPacket(formRequestPacket); - } - - public void showWindow(FormWindow window, int id) { - ModalFormRequestPacket formRequestPacket = new ModalFormRequestPacket(); - formRequestPacket.setFormId(id); - formRequestPacket.setFormData(window.getJSONData()); - - session.sendUpstreamPacket(formRequestPacket); - - addWindow(window, id); - } -} diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/BiomeTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/BiomeTranslator.java index eed901cdf..d797381ce 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/BiomeTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/BiomeTranslator.java @@ -30,7 +30,6 @@ import com.nukkitx.nbt.NbtMap; import com.nukkitx.nbt.NbtUtils; import org.geysermc.connector.GeyserConnector; import org.geysermc.connector.utils.FileUtils; -import org.geysermc.connector.utils.LanguageUtils; import java.io.InputStream; import java.util.Arrays; @@ -59,7 +58,7 @@ public class BiomeTranslator { biomesTag = (NbtMap) biomenbtInputStream.readTag(); BIOMES = biomesTag; } catch (Exception ex) { - GeyserConnector.getInstance().getLogger().warning(LanguageUtils.getLocaleStringLog("geyser.toolbox.fail.biome_read")); + GeyserConnector.getInstance().getLogger().warning("Failed to get biomes from biome definitions, is there something wrong with the file?"); throw new AssertionError(ex); } } diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/EntityIdentifierRegistry.java b/connector/src/main/java/org/geysermc/connector/network/translators/EntityIdentifierRegistry.java index fb6d5b93d..5bb029882 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/EntityIdentifierRegistry.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/EntityIdentifierRegistry.java @@ -29,7 +29,6 @@ import com.nukkitx.nbt.NBTInputStream; import com.nukkitx.nbt.NbtMap; import com.nukkitx.nbt.NbtUtils; import org.geysermc.connector.utils.FileUtils; -import org.geysermc.connector.utils.LanguageUtils; import java.io.InputStream; @@ -54,7 +53,7 @@ public class EntityIdentifierRegistry { try (NBTInputStream nbtInputStream = NbtUtils.createNetworkReader(stream)) { ENTITY_IDENTIFIERS = (NbtMap) nbtInputStream.readTag(); } catch (Exception e) { - throw new AssertionError(LanguageUtils.getLocaleStringLog("geyser.toolbox.fail.entity"), e); + throw new AssertionError("Unable to get entities from entity identifiers", e); } } } diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/PacketTranslatorRegistry.java b/connector/src/main/java/org/geysermc/connector/network/translators/PacketTranslatorRegistry.java index eb26dbff9..2469f65da 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/PacketTranslatorRegistry.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/PacketTranslatorRegistry.java @@ -68,10 +68,10 @@ public class PacketTranslatorRegistry { BEDROCK_TRANSLATOR.translators.put(targetPacket, translator); } else { - GeyserConnector.getInstance().getLogger().error(LanguageUtils.getLocaleStringLog("geyser.network.translator.invalid_target", clazz.getCanonicalName())); + GeyserConnector.getInstance().getLogger().error("Class " + clazz.getCanonicalName() + " is annotated as a translator but has an invalid target packet."); } } catch (InstantiationException | IllegalAccessException e) { - GeyserConnector.getInstance().getLogger().error(LanguageUtils.getLocaleStringLog("geyser.network.translator.failed", clazz.getCanonicalName())); + GeyserConnector.getInstance().getLogger().error("Could not instantiate annotated translator " + clazz.getCanonicalName()); } } 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 ba74c7769..493789bbb 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 @@ -28,6 +28,7 @@ package org.geysermc.connector.network.translators.bedrock; import com.nukkitx.math.vector.Vector3i; import com.nukkitx.protocol.bedrock.packet.BlockPickRequestPacket; import org.geysermc.connector.entity.ItemFrameEntity; +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; @@ -53,7 +54,7 @@ public class BedrockBlockPickRequestTranslator extends PacketTranslator 0) { + if (session.getConnector().getConfig().isForwardPlayerPing()) { + ClientKeepAlivePacket keepAlivePacket = new ClientKeepAlivePacket(pingId); + session.sendDownstreamPacket(keepAlivePacket); + } + return; + } + + // Hack to fix the url image loading bug + UpdateAttributesPacket attributesPacket = new UpdateAttributesPacket(); + attributesPacket.setRuntimeEntityId(session.getPlayerEntity().getGeyserId()); + + Attribute attribute = session.getPlayerEntity().getAttributes().get(AttributeType.EXPERIENCE_LEVEL); + if (attribute != null) { + attributesPacket.setAttributes(Collections.singletonList(AttributeUtils.getBedrockAttribute(attribute))); + } else { + attributesPacket.setAttributes(Collections.singletonList(AttributeUtils.getBedrockAttribute(AttributeType.EXPERIENCE_LEVEL.getAttribute(0)))); + } + + session.getConnector().getGeneralThreadPool().schedule( + () -> session.sendUpstreamPacket(attributesPacket), + 500, TimeUnit.MILLISECONDS); } } diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockServerSettingsRequestTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockServerSettingsRequestTranslator.java index 3f3226280..501ed4468 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockServerSettingsRequestTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockServerSettingsRequestTranslator.java @@ -31,21 +31,22 @@ 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.SettingsUtils; +import org.geysermc.cumulus.CustomForm; import java.util.concurrent.TimeUnit; @Translator(packet = ServerSettingsRequestPacket.class) public class BedrockServerSettingsRequestTranslator extends PacketTranslator { - @Override public void translate(ServerSettingsRequestPacket packet, GeyserSession session) { - SettingsUtils.buildForm(session); + CustomForm window = SettingsUtils.buildForm(session); + int windowId = session.getFormCache().addForm(window); // Fixes https://bugs.mojang.com/browse/MCPE-94012 because of the delay session.getConnector().getGeneralThreadPool().schedule(() -> { ServerSettingsResponsePacket serverSettingsResponsePacket = new ServerSettingsResponsePacket(); - serverSettingsResponsePacket.setFormData(session.getSettingsForm().getJSONData()); - serverSettingsResponsePacket.setFormId(SettingsUtils.SETTINGS_FORM_ID); + serverSettingsResponsePacket.setFormData(window.getJsonData()); + serverSettingsResponsePacket.setFormId(windowId); session.sendUpstreamPacket(serverSettingsResponsePacket); }, 1, TimeUnit.SECONDS); } diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/entity/player/BedrockActionTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/entity/player/BedrockActionTranslator.java index c590176c0..d094bc353 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/entity/player/BedrockActionTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/entity/player/BedrockActionTranslator.java @@ -51,8 +51,6 @@ import org.geysermc.connector.network.translators.item.ItemRegistry; import org.geysermc.connector.network.translators.world.block.BlockTranslator; import org.geysermc.connector.utils.BlockUtils; -import java.util.concurrent.TimeUnit; - @Translator(packet = PlayerActionPacket.class) public class BedrockActionTranslator extends PacketTranslator { @@ -162,43 +160,42 @@ public class BedrockActionTranslator extends PacketTranslator session.setJumping(false), 1, TimeUnit.SECONDS); - } + // Leaving as a potential placeholder for an event or soul sand fixing break; } } diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/collision/CollisionManager.java b/connector/src/main/java/org/geysermc/connector/network/translators/collision/CollisionManager.java index ff95593bc..b6d0ae176 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/collision/CollisionManager.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/collision/CollisionManager.java @@ -141,32 +141,22 @@ public class CollisionManager { Vector3d position = Vector3d.from(Double.parseDouble(Float.toString(bedrockPosition.getX())), javaY, Double.parseDouble(Float.toString(bedrockPosition.getZ()))); - if (session.getConnector().getConfig().isCacheChunks()) { - // With chunk caching, we can do some proper collision checks - updatePlayerBoundingBox(position); + updatePlayerBoundingBox(position); - // Correct player position - if (!correctPlayerPosition()) { - // Cancel the movement if it needs to be cancelled - recalculatePosition(); - return null; - } + // Correct player position + if (!correctPlayerPosition()) { + // Cancel the movement if it needs to be cancelled + recalculatePosition(); + return null; + } - position = Vector3d.from(playerBoundingBox.getMiddleX(), - playerBoundingBox.getMiddleY() - (playerBoundingBox.getSizeY() / 2), - playerBoundingBox.getMiddleZ()); + position = Vector3d.from(playerBoundingBox.getMiddleX(), + playerBoundingBox.getMiddleY() - (playerBoundingBox.getSizeY() / 2), + playerBoundingBox.getMiddleZ()); - if (!onGround) { - // Trim the position to prevent rounding errors that make Java think we are clipping into a block - position = Vector3d.from(position.getX(), Double.parseDouble(DECIMAL_FORMAT.format(position.getY())), position.getZ()); - } - } else { - // When chunk caching is off, we have to rely on this - // It rounds the Y position up to the nearest 0.5 - // This snaps players to snap to the top of stairs and slabs like on Java Edition - // However, it causes issues such as the player floating on carpets - if (onGround) javaY = Math.ceil(javaY * 2) / 2; - position = position.up(javaY - position.getY()); + if (!onGround) { + // Trim the position to prevent rounding errors that make Java think we are clipping into a block + position = Vector3d.from(position.getX(), Double.parseDouble(DECIMAL_FORMAT.format(position.getY())), position.getZ()); } return position; @@ -268,10 +258,6 @@ public class CollisionManager { * were they not sneaking */ public boolean isUnderSlab() { - if (!session.getConnector().getConfig().isCacheChunks()) { - // We can't reliably determine this - return false; - } Vector3i position = session.getPlayerEntity().getPosition().toInt(); BlockCollision collision = CollisionTranslator.getCollisionAt(session, position.getX(), position.getY(), position.getZ()); if (collision != null) { @@ -289,8 +275,7 @@ public class CollisionManager { * @return if the player is currently in a water block */ public boolean isPlayerInWater() { - return session.getConnector().getConfig().isCacheChunks() - && session.getConnector().getWorldManager().getBlockAt(session, session.getPlayerEntity().getPosition().toInt()) == BlockTranslator.JAVA_WATER_ID; + return session.getConnector().getWorldManager().getBlockAt(session, session.getPlayerEntity().getPosition().toInt()) == BlockTranslator.JAVA_WATER_ID; } /** diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/collision/CollisionTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/collision/CollisionTranslator.java index 2be493214..e5cf52bc8 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/collision/CollisionTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/collision/CollisionTranslator.java @@ -48,11 +48,6 @@ public class CollisionTranslator { private static final Int2ObjectMap COLLISION_MAP = new Int2ObjectOpenHashMap<>(); public static void init() { - // If chunk caching is off then don't initialize - if (!GeyserConnector.getInstance().getConfig().isCacheChunks()) { - return; - } - List> collisionTypes = new ArrayList<>(); Map, CollisionRemapper> annotationMap = new HashMap<>(); diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/inventory/click/ClickPlan.java b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/click/ClickPlan.java index c750baf51..5204bf675 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/inventory/click/ClickPlan.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/click/ClickPlan.java @@ -27,7 +27,6 @@ package org.geysermc.connector.network.translators.inventory.click; import com.github.steveice10.mc.protocol.data.game.entity.metadata.ItemStack; import com.github.steveice10.mc.protocol.data.game.window.WindowAction; -import com.github.steveice10.mc.protocol.packet.ingame.client.window.ClientConfirmTransactionPacket; import com.github.steveice10.mc.protocol.packet.ingame.client.window.ClientWindowActionPacket; import it.unimi.dsi.fastutil.ints.Int2ObjectMap; import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; @@ -43,7 +42,9 @@ import org.geysermc.connector.network.translators.inventory.translators.Crafting import org.geysermc.connector.network.translators.inventory.translators.PlayerInventoryTranslator; import org.geysermc.connector.utils.InventoryUtils; -import java.util.*; +import java.util.ArrayList; +import java.util.List; +import java.util.ListIterator; public class ClickPlan { private final List plan = new ArrayList<>(); @@ -116,22 +117,23 @@ public class ClickPlan { clickedItemStack = getItem(action.slot).getItemStack(); } - short actionId = inventory.getNextTransactionId(); + Int2ObjectMap affectedSlots = new Int2ObjectOpenHashMap<>(); + for (Int2ObjectMap.Entry simulatedSlot : simulatedItems.int2ObjectEntrySet()) { + affectedSlots.put(simulatedSlot.getIntKey(), simulatedSlot.getValue().getItemStack()); + } + ClientWindowActionPacket clickPacket = new ClientWindowActionPacket( inventory.getId(), - actionId, action.slot, - clickedItemStack, action.click.windowAction, - action.click.actionParam + action.click.actionParam, + clickedItemStack, + affectedSlots ); simulateAction(action); session.sendDownstreamPacket(clickPacket); - if (clickedItemStack == InventoryUtils.REFRESH_ITEM || action.force) { - session.sendDownstreamPacket(new ClientConfirmTransactionPacket(inventory.getId(), actionId, true)); - } } session.getPlayerInventory().setCursor(simulatedCursor, session); diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/inventory/translators/AnvilInventoryTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/translators/AnvilInventoryTranslator.java index 38a0935e6..1f36eaabe 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/inventory/translators/AnvilInventoryTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/translators/AnvilInventoryTranslator.java @@ -26,21 +26,14 @@ package org.geysermc.connector.network.translators.inventory.translators; import com.github.steveice10.mc.protocol.data.game.window.WindowType; -import com.github.steveice10.mc.protocol.packet.ingame.client.window.ClientRenameItemPacket; -import com.nukkitx.nbt.NbtMap; -import com.nukkitx.protocol.bedrock.data.inventory.*; -import com.nukkitx.protocol.bedrock.data.inventory.stackrequestactions.CraftResultsDeprecatedStackRequestActionData; -import com.nukkitx.protocol.bedrock.data.inventory.stackrequestactions.StackRequestActionData; -import com.nukkitx.protocol.bedrock.data.inventory.stackrequestactions.StackRequestActionType; -import com.nukkitx.protocol.bedrock.packet.ItemStackResponsePacket; +import com.nukkitx.protocol.bedrock.data.inventory.ContainerSlotType; +import com.nukkitx.protocol.bedrock.data.inventory.ContainerType; +import com.nukkitx.protocol.bedrock.data.inventory.StackRequestSlotInfoData; import org.geysermc.connector.inventory.AnvilContainer; -import org.geysermc.connector.inventory.GeyserItemStack; import org.geysermc.connector.inventory.Inventory; import org.geysermc.connector.inventory.PlayerInventory; -import org.geysermc.connector.network.session.GeyserSession; import org.geysermc.connector.network.translators.inventory.BedrockContainerSlot; import org.geysermc.connector.network.translators.inventory.updater.UIInventoryUpdater; -import org.geysermc.connector.network.translators.item.ItemTranslator; public class AnvilInventoryTranslator extends AbstractBlockInventoryTranslator { public AnvilInventoryTranslator() { @@ -48,55 +41,6 @@ public class AnvilInventoryTranslator extends AbstractBlockInventoryTranslator { "minecraft:chipped_anvil", "minecraft:damaged_anvil"); } - /* 1.16.100 support start */ - @Override - @Deprecated - public boolean shouldHandleRequestFirst(StackRequestActionData action, Inventory inventory) { - return action.getType() == StackRequestActionType.CRAFT_NON_IMPLEMENTED_DEPRECATED; - } - - @Override - @Deprecated - public ItemStackResponsePacket.Response translateSpecialRequest(GeyserSession session, Inventory inventory, ItemStackRequest request) { - if (!(request.getActions()[1] instanceof CraftResultsDeprecatedStackRequestActionData)) { - // Just silently log an error - session.getConnector().getLogger().debug("Something isn't quite right with taking an item out of an anvil."); - return translateRequest(session, inventory, request); - } - CraftResultsDeprecatedStackRequestActionData actionData = (CraftResultsDeprecatedStackRequestActionData) request.getActions()[1]; - ItemData resultItem = actionData.getResultItems()[0]; - if (resultItem.getTag() != null) { - NbtMap displayTag = resultItem.getTag().getCompound("display"); - if (displayTag != null && displayTag.containsKey("Name")) { - ItemData sourceSlot = inventory.getItem(0).getItemData(session); - - if (sourceSlot.getTag() != null) { - NbtMap oldDisplayTag = sourceSlot.getTag().getCompound("display"); - if (oldDisplayTag != null && oldDisplayTag.containsKey("Name")) { - if (!displayTag.getString("Name").equals(oldDisplayTag.getString("Name"))) { - // Name has changed - sendRenamePacket(session, inventory, resultItem, displayTag.getString("Name")); - } - } else { - // No display tag on the old item - sendRenamePacket(session, inventory, resultItem, displayTag.getString("Name")); - } - } else { - // New NBT tag - sendRenamePacket(session, inventory, resultItem, displayTag.getString("Name")); - } - } - } - return translateRequest(session, inventory, request); - } - - private void sendRenamePacket(GeyserSession session, Inventory inventory, ItemData outputItem, String name) { - session.sendDownstreamPacket(new ClientRenameItemPacket(name)); - inventory.setItem(2, GeyserItemStack.from(ItemTranslator.translateToJava(outputItem)), session); - } - - /* 1.16.100 support end */ - @Override public int bedrockSlotToJava(StackRequestSlotInfoData slotInfoData) { switch (slotInfoData.getContainer()) { diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/inventory/translators/BeaconInventoryTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/translators/BeaconInventoryTranslator.java index 5af921f2d..1d40db51f 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/inventory/translators/BeaconInventoryTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/translators/BeaconInventoryTranslator.java @@ -54,15 +54,6 @@ import java.util.Collections; public class BeaconInventoryTranslator extends AbstractBlockInventoryTranslator { public BeaconInventoryTranslator() { super(1, new BlockInventoryHolder("minecraft:beacon", ContainerType.BEACON) { - @Override - public void prepareInventory(InventoryTranslator translator, GeyserSession session, Inventory inventory) { - if (!session.getConnector().getConfig().isCacheChunks()) { - // Beacons cannot work without knowing their physical location - return; - } - super.prepareInventory(translator, session, inventory); - } - @Override protected boolean checkInteractionPosition(GeyserSession session) { // Since we can't fall back to a virtual inventory, let's make opening one easier @@ -71,7 +62,7 @@ public class BeaconInventoryTranslator extends AbstractBlockInventoryTranslator @Override public void openInventory(InventoryTranslator translator, GeyserSession session, Inventory inventory) { - if (!session.getConnector().getConfig().isCacheChunks() || !((BeaconContainer) inventory).isUsingRealBlock()) { + if (!((BeaconContainer) inventory).isUsingRealBlock()) { InventoryUtils.closeInventory(session, inventory.getId(), false); return; } diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/inventory/translators/LecternInventoryTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/translators/LecternInventoryTranslator.java index c08dfd995..b15a73493 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/inventory/translators/LecternInventoryTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/translators/LecternInventoryTranslator.java @@ -78,71 +78,81 @@ public class LecternInventoryTranslator extends BaseInventoryTranslator { @Override public void updateInventory(GeyserSession session, Inventory inventory) { + GeyserItemStack itemStack = inventory.getItem(0); + if (!itemStack.isEmpty()) { + updateBook(session, inventory, itemStack); + } } @Override public void updateSlot(GeyserSession session, Inventory inventory, int slot) { this.updater.updateSlot(this, session, inventory, slot); if (slot == 0) { - LecternContainer lecternContainer = (LecternContainer) inventory; - if (session.isDroppingLecternBook()) { - // We have to enter the inventory GUI to eject the book - ClientClickWindowButtonPacket packet = new ClientClickWindowButtonPacket(inventory.getId(), 3); - session.sendDownstreamPacket(packet); - session.setDroppingLecternBook(false); - InventoryUtils.closeInventory(session, inventory.getId(), false); - } else if (lecternContainer.getBlockEntityTag() == null) { - // If the method returns true, this is already handled for us - GeyserItemStack geyserItemStack = inventory.getItem(0); - CompoundTag tag = geyserItemStack.getNbt(); - // Position has to be the last interacted position... right? - Vector3i position = session.getLastInteractionBlockPosition(); - // shouldRefresh means that we should boot out the client on our side because their lectern GUI isn't updated yet - boolean shouldRefresh = !session.getConnector().getWorldManager().shouldExpectLecternHandled() && !session.getLecternCache().contains(position); + updateBook(session, inventory, inventory.getItem(0)); + } + } - NbtMap blockEntityTag; - if (tag != null) { - int pagesSize = ((ListTag) tag.get("pages")).size(); - ItemData itemData = geyserItemStack.getItemData(session); - NbtMapBuilder lecternTag = getBaseLecternTag(position.getX(), position.getY(), position.getZ(), pagesSize); - lecternTag.putCompound("book", NbtMap.builder() - .putByte("Count", (byte) itemData.getCount()) - .putShort("Damage", (short) 0) - .putString("Name", "minecraft:written_book") - .putCompound("tag", itemData.getTag()) - .build()); - lecternTag.putInt("page", lecternContainer.getCurrentBedrockPage()); - blockEntityTag = lecternTag.build(); - } else { - // There is *a* book here, but... no NBT. - NbtMapBuilder lecternTag = getBaseLecternTag(position.getX(), position.getY(), position.getZ(), 1); - NbtMapBuilder bookTag = NbtMap.builder() - .putByte("Count", (byte) 1) - .putShort("Damage", (short) 0) - .putString("Name", "minecraft:writable_book") - .putCompound("tag", NbtMap.builder().putList("pages", NbtType.COMPOUND, Collections.singletonList( - NbtMap.builder() + /** + * Translate the data of the book in the lectern into a block entity tag. + */ + private void updateBook(GeyserSession session, Inventory inventory, GeyserItemStack book) { + LecternContainer lecternContainer = (LecternContainer) inventory; + if (session.isDroppingLecternBook()) { + // We have to enter the inventory GUI to eject the book + ClientClickWindowButtonPacket packet = new ClientClickWindowButtonPacket(inventory.getId(), 3); + session.sendDownstreamPacket(packet); + session.setDroppingLecternBook(false); + InventoryUtils.closeInventory(session, inventory.getId(), false); + } else if (lecternContainer.getBlockEntityTag() == null) { + CompoundTag tag = book.getNbt(); + // Position has to be the last interacted position... right? + Vector3i position = session.getLastInteractionBlockPosition(); + // If shouldExpectLecternHandled returns true, this is already handled for us + // shouldRefresh means that we should boot out the client on our side because their lectern GUI isn't updated yet + boolean shouldRefresh = !session.getConnector().getWorldManager().shouldExpectLecternHandled() && !session.getLecternCache().contains(position); + + NbtMap blockEntityTag; + if (tag != null) { + int pagesSize = ((ListTag) tag.get("pages")).size(); + ItemData itemData = book.getItemData(session); + NbtMapBuilder lecternTag = getBaseLecternTag(position.getX(), position.getY(), position.getZ(), pagesSize); + lecternTag.putCompound("book", NbtMap.builder() + .putByte("Count", (byte) itemData.getCount()) + .putShort("Damage", (short) 0) + .putString("Name", "minecraft:written_book") + .putCompound("tag", itemData.getTag()) + .build()); + lecternTag.putInt("page", lecternContainer.getCurrentBedrockPage()); + blockEntityTag = lecternTag.build(); + } else { + // There is *a* book here, but... no NBT. + NbtMapBuilder lecternTag = getBaseLecternTag(position.getX(), position.getY(), position.getZ(), 1); + NbtMapBuilder bookTag = NbtMap.builder() + .putByte("Count", (byte) 1) + .putShort("Damage", (short) 0) + .putString("Name", "minecraft:writable_book") + .putCompound("tag", NbtMap.builder().putList("pages", NbtType.COMPOUND, Collections.singletonList( + NbtMap.builder() .putString("photoname", "") .putString("text", "") - .build() - )).build()); + .build() + )).build()); - blockEntityTag = lecternTag.putCompound("book", bookTag.build()).build(); - } - - // Even with serverside access to lecterns, we don't easily know which lectern this is, so we need to rebuild - // the block entity tag - lecternContainer.setBlockEntityTag(blockEntityTag); - lecternContainer.setPosition(position); - if (shouldRefresh) { - // Update the lectern because it's not updated client-side - BlockEntityUtils.updateBlockEntity(session, blockEntityTag, position); - session.getLecternCache().add(position); - // Close the window - we will reopen it once the client has this data synced - ClientCloseWindowPacket closeWindowPacket = new ClientCloseWindowPacket(lecternContainer.getId()); - session.sendDownstreamPacket(closeWindowPacket); - InventoryUtils.closeInventory(session, inventory.getId(), false); - } + blockEntityTag = lecternTag.putCompound("book", bookTag.build()).build(); + } + + // Even with serverside access to lecterns, we don't easily know which lectern this is, so we need to rebuild + // the block entity tag + lecternContainer.setBlockEntityTag(blockEntityTag); + lecternContainer.setPosition(position); + if (shouldRefresh) { + // Update the lectern because it's not updated client-side + BlockEntityUtils.updateBlockEntity(session, blockEntityTag, position); + session.getLecternCache().add(position); + // Close the window - we will reopen it once the client has this data synced + ClientCloseWindowPacket closeWindowPacket = new ClientCloseWindowPacket(lecternContainer.getId()); + session.sendDownstreamPacket(closeWindowPacket); + InventoryUtils.closeInventory(session, inventory.getId(), false); } } } 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 2ac203e87..5acaa2e26 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 @@ -28,7 +28,7 @@ package org.geysermc.connector.network.translators.item; import lombok.AllArgsConstructor; import lombok.Getter; import lombok.ToString; -import org.geysermc.connector.network.translators.world.block.BlockTranslator1_16_210; +import org.geysermc.connector.network.translators.world.block.BlockTranslator1_17_0; @Getter @AllArgsConstructor @@ -36,7 +36,7 @@ import org.geysermc.connector.network.translators.world.block.BlockTranslator1_1 public class ItemEntry { public static ItemEntry AIR = new ItemEntry("minecraft:air", "minecraft:air", 0, 0, 0, - BlockTranslator1_16_210.INSTANCE.getBedrockAirId(), 64); + BlockTranslator1_17_0.INSTANCE.getBedrockAirId(), 64); private final String javaIdentifier; private final String bedrockIdentifier; 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 0e1a28d54..f428d37e9 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 @@ -45,7 +45,7 @@ import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap; import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet; import org.geysermc.connector.GeyserConnector; import org.geysermc.connector.network.translators.world.block.BlockTranslator; -import org.geysermc.connector.network.translators.world.block.BlockTranslator1_16_210; +import org.geysermc.connector.network.translators.world.block.BlockTranslator1_17_0; import org.geysermc.connector.utils.FileUtils; import org.geysermc.connector.utils.LanguageUtils; @@ -148,7 +148,7 @@ public class ItemRegistry { try { itemEntries = GeyserConnector.JSON_MAPPER.readValue(stream, itemEntriesType); } catch (Exception e) { - throw new AssertionError(LanguageUtils.getLocaleStringLog("geyser.toolbox.fail.runtime_bedrock"), e); + throw new AssertionError("Unable to load Bedrock runtime item IDs", e); } int lodestoneCompassId = 0; @@ -172,7 +172,7 @@ public class ItemRegistry { try { creativeItemEntries = GeyserConnector.JSON_MAPPER.readTree(stream).get("items"); } catch (Exception e) { - throw new AssertionError(LanguageUtils.getLocaleStringLog("geyser.toolbox.fail.creative"), e); + throw new AssertionError("Unable to load creative items", e); } int netId = 1; @@ -247,10 +247,10 @@ public class ItemRegistry { try { items = GeyserConnector.JSON_MAPPER.readTree(stream); } catch (Exception e) { - throw new AssertionError(LanguageUtils.getLocaleStringLog("geyser.toolbox.fail.runtime_java"), e); + throw new AssertionError("Unable to load Java runtime item IDs", e); } - BlockTranslator blockTranslator = BlockTranslator1_16_210.INSTANCE; + BlockTranslator blockTranslator = BlockTranslator1_17_0.INSTANCE; int itemIndex = 0; int javaFurnaceMinecartId = 0; @@ -404,13 +404,16 @@ public class ItemRegistry { "", bedrockBlockId, stackSize); } - } else if (entry.getKey().equals("minecraft:spectral_arrow") || entry.getKey().equals("minecraft:knowledge_book")) { - // These items don't exist on Java, so set up a container that indicates they should have custom names + } else if (entry.getKey().equals("minecraft:spectral_arrow") || entry.getKey().equals("minecraft:knowledge_book") + // To remove later... hopefully + || entry.getKey().contains("candle") || entry.getKey().equals("minecraft:bundle") || entry.getKey().equals("minecraft:sculk_sensor")) { + // These items don't exist on Bedrock, so set up a container that indicates they should have custom names itemEntry = new TranslatableItemEntry( entry.getKey(), bedrockIdentifier, itemIndex, bedrockId, entry.getValue().get("bedrock_data").intValue(), bedrockBlockId, stackSize); + GeyserConnector.getInstance().getLogger().debug("Adding " + entry.getKey() + " as an item that needs to be translated."); } else { itemEntry = new ItemEntry( entry.getKey(), bedrockIdentifier, itemIndex, bedrockId, @@ -456,7 +459,7 @@ public class ItemRegistry { 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()); - } else if (entry.getKey().contains("_carpet")) { + } else if (entry.getKey().contains("_carpet") && !entry.getKey().contains("moss")) { // This should be the numerical order Java sends as an integer value for llamas CARPETS.add(ItemData.builder() .id(itemEntry.getBedrockId()) @@ -530,44 +533,19 @@ public class ItemRegistry { Set javaOnlyItems = new ObjectOpenHashSet<>(); Collections.addAll(javaOnlyItems, "minecraft:spectral_arrow", "minecraft:debug_stick", - "minecraft:knowledge_book", "minecraft:tipped_arrow"); + "minecraft:knowledge_book", "minecraft:tipped_arrow", "minecraft:trader_llama_spawn_egg", + // To be removed in Bedrock 1.17.10... right??? RIGHT??? + "minecraft:candle", "minecraft:white_candle", "minecraft:orange_candle", "minecraft:magenta_candle", + "minecraft:light_blue_candle", "minecraft:yellow_candle", "minecraft:lime_candle", "minecraft:pink_candle", + "minecraft:gray_candle", "minecraft:light_gray_candle", "minecraft:cyan_candle", "minecraft:purple_candle", + "minecraft:blue_candle", "minecraft:brown_candle", "minecraft:green_candle", "minecraft:red_candle", "minecraft:black_candle", + "minecraft:bundle", "minecraft:sculk_sensor"); if (!usingFurnaceMinecart) { javaOnlyItems.add("minecraft:furnace_minecart"); } JAVA_ONLY_ITEMS = ImmutableSet.copyOf(javaOnlyItems); } - /* pre-1.16.220 support start */ - - private static ItemData[] LEGACY_CREATIVE_CONTENTS = null; - - /** - * Built on the fly so extra memory isn't used if there are no 1.16.210-or-below clients joining. - * - * @return a list of creative items built for versions before 1.16.220. - */ - public static ItemData[] getPre1_16_220CreativeContents() { - if (LEGACY_CREATIVE_CONTENTS != null) { - return LEGACY_CREATIVE_CONTENTS; - } - - // Pre-1.16.220 relies on item damage values that the creative content packet drops - ItemData[] creativeContents = new ItemData[CREATIVE_ITEMS.length]; - for (int i = 0; i < CREATIVE_ITEMS.length; i++) { - ItemData item = CREATIVE_ITEMS[i]; - if (item.getBlockRuntimeId() != 0) { - creativeContents[i] = item.toBuilder().damage(getItem(item).getBedrockData()).build(); - } else { - // No block runtime ID means that this item is backwards-compatible - creativeContents[i] = item; - } - } - LEGACY_CREATIVE_CONTENTS = creativeContents; - return creativeContents; - } - - /* pre-1.16.220 support end */ - /** * Gets an {@link ItemEntry} from the given {@link ItemStack}. * 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 78bfd07ce..afb520881 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 @@ -39,7 +39,6 @@ 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.utils.FileUtils; -import org.geysermc.connector.utils.LanguageUtils; import org.geysermc.connector.utils.LocaleUtils; import org.reflections.Reflections; @@ -78,13 +77,15 @@ public abstract class ItemTranslator { for (ItemEntry item : appliedItems) { ItemTranslator registered = ITEM_STACK_TRANSLATORS.get(item.getJavaId()); if (registered != null) { - GeyserConnector.getInstance().getLogger().error(LanguageUtils.getLocaleStringLog("geyser.network.translator.item.already_registered", clazz.getCanonicalName(), registered.getClass().getCanonicalName(), item.getJavaIdentifier())); + GeyserConnector.getInstance().getLogger().error("Could not instantiate annotated item translator " + + clazz.getCanonicalName() + ". Item translator " + registered.getClass().getCanonicalName() + + " is already registered for the item " + item.getJavaIdentifier()); continue; } ITEM_STACK_TRANSLATORS.put(item.getJavaId(), itemStackTranslator); } } catch (InstantiationException | IllegalAccessException e) { - GeyserConnector.getInstance().getLogger().error(LanguageUtils.getLocaleStringLog("geyser.network.translator.item.failed", clazz.getCanonicalName())); + GeyserConnector.getInstance().getLogger().error("Could not instantiate annotated item translator " + clazz.getCanonicalName()); } } diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/item/RecipeRegistry.java b/connector/src/main/java/org/geysermc/connector/network/translators/item/RecipeRegistry.java index 8f7a4a8a2..506317ab9 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/item/RecipeRegistry.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/item/RecipeRegistry.java @@ -41,7 +41,6 @@ import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; import it.unimi.dsi.fastutil.objects.ObjectArrayList; import org.geysermc.connector.GeyserConnector; import org.geysermc.connector.utils.FileUtils; -import org.geysermc.connector.utils.LanguageUtils; import java.io.ByteArrayInputStream; import java.io.IOException; @@ -144,7 +143,7 @@ public class RecipeRegistry { try { items = GeyserConnector.JSON_MAPPER.readTree(stream); } catch (Exception e) { - throw new AssertionError(LanguageUtils.getLocaleStringLog("geyser.toolbox.fail.runtime_java"), e); + throw new AssertionError("Unable to load Java runtime item IDs", e); } for (JsonNode entry : items.get("leather_armor")) { diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/item/TranslatableItemEntry.java b/connector/src/main/java/org/geysermc/connector/network/translators/item/TranslatableItemEntry.java index b4ea92a0e..3967abb17 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/item/TranslatableItemEntry.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/item/TranslatableItemEntry.java @@ -36,6 +36,6 @@ public class TranslatableItemEntry extends ItemEntry { public TranslatableItemEntry(String javaIdentifier, String bedrockIdentifier, int javaId, int bedrockId, int bedrockData, int bedrockBlockId, int stackSize) { super(javaIdentifier, bedrockIdentifier, javaId, bedrockId, bedrockData, bedrockBlockId, stackSize); - this.translationString = "item." + javaIdentifier.replace(":", "."); + this.translationString = (isBlock() ? "block." : "item.") + javaIdentifier.replace(":", "."); } } diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/java/JavaAdvancementsTabTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/java/JavaAdvancementsTabTranslator.java index 17a3b3792..aa22ae465 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/java/JavaAdvancementsTabTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/java/JavaAdvancementsTabTranslator.java @@ -36,10 +36,10 @@ import org.geysermc.connector.network.translators.Translator; */ @Translator(packet = ServerAdvancementTabPacket.class) public class JavaAdvancementsTabTranslator extends PacketTranslator { - @Override public void translate(ServerAdvancementTabPacket packet, GeyserSession session) { - session.getAdvancementsCache().setCurrentAdvancementCategoryId(packet.getTabId()); - session.sendForm(session.getAdvancementsCache().buildListForm(), AdvancementsCache.ADVANCEMENTS_LIST_FORM_ID); + AdvancementsCache advancementsCache = session.getAdvancementsCache(); + advancementsCache.setCurrentAdvancementCategoryId(packet.getTabId()); + advancementsCache.buildAndShowListForm(); } } 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 47918440b..53ebd92ff 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 @@ -34,10 +34,12 @@ import com.github.steveice10.mc.protocol.packet.ingame.server.ServerJoinGamePack import com.nukkitx.protocol.bedrock.data.GameRuleData; import com.nukkitx.protocol.bedrock.data.PlayerPermission; import com.nukkitx.protocol.bedrock.packet.*; +import org.geysermc.connector.common.AuthType; import org.geysermc.connector.entity.player.PlayerEntity; 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.ChunkUtils; import org.geysermc.connector.utils.DimensionUtils; import org.geysermc.connector.utils.PluginMessageUtils; @@ -52,6 +54,9 @@ public class JavaJoinGameTranslator extends PacketTranslator { - @Getter - @Setter - private String text; - - @Getter - private FormImage image; - - public FormButton(String text) { - this.text = text; - } - - public FormButton(String text, FormImage image) { - this.text = text; - - if (image.getData() != null && !image.getData().isEmpty()) { - this.image = image; - } - } - - public void setImage(FormImage image) { - if (image.getData() != null && !image.getData().isEmpty()) { - this.image = image; - } + @Override + public void translate(ServerPingPacket packet, GeyserSession session) { + session.sendDownstreamPacket(new ClientPongPacket(packet.getId())); } } diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/java/JavaPluginMessageTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/java/JavaPluginMessageTranslator.java new file mode 100644 index 000000000..cb80a68a4 --- /dev/null +++ b/connector/src/main/java/org/geysermc/connector/network/translators/java/JavaPluginMessageTranslator.java @@ -0,0 +1,80 @@ +/* + * 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.java; + +import com.github.steveice10.mc.protocol.packet.ingame.client.ClientPluginMessagePacket; +import com.github.steveice10.mc.protocol.packet.ingame.server.ServerPluginMessagePacket; +import com.google.common.base.Charsets; +import org.geysermc.connector.common.AuthType; +import org.geysermc.connector.network.session.GeyserSession; +import org.geysermc.connector.network.translators.PacketTranslator; +import org.geysermc.connector.network.translators.Translator; +import org.geysermc.cumulus.Form; +import org.geysermc.cumulus.Forms; +import org.geysermc.cumulus.util.FormType; + +import java.nio.charset.StandardCharsets; + +@Translator(packet = ServerPluginMessagePacket.class) +public class JavaPluginMessageTranslator extends PacketTranslator { + @Override + public void translate(ServerPluginMessagePacket packet, GeyserSession session) { + // The only plugin messages it has to listen for are Floodgate plugin messages + if (session.getConnector().getDefaultAuthType() != AuthType.FLOODGATE) { + return; + } + + String channel = packet.getChannel(); + + if (channel.equals("floodgate:form")) { + byte[] data = packet.getData(); + + // receive: first byte is form type, second and third are the id, remaining is the form data + // respond: first and second byte id, remaining is form response data + + FormType type = FormType.getByOrdinal(data[0]); + if (type == null) { + throw new NullPointerException( + "Got type " + data[0] + " which isn't a valid form type!"); + } + + String dataString = new String(data, 3, data.length - 3, Charsets.UTF_8); + + Form form = Forms.fromJson(dataString, type); + form.setResponseHandler(response -> { + byte[] raw = response.getBytes(StandardCharsets.UTF_8); + byte[] finalData = new byte[raw.length + 2]; + + finalData[0] = data[1]; + finalData[1] = data[2]; + System.arraycopy(raw, 0, finalData, 2, raw.length); + + session.sendDownstreamPacket(new ClientPluginMessagePacket(channel, finalData)); + }); + session.sendForm(form); + } + } +} diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/java/JavaRespawnTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/java/JavaRespawnTranslator.java index 7c8cd0583..908292d36 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/java/JavaRespawnTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/java/JavaRespawnTranslator.java @@ -36,6 +36,7 @@ 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.inventory.InventoryTranslator; +import org.geysermc.connector.utils.ChunkUtils; import org.geysermc.connector.utils.DimensionUtils; @Translator(packet = ServerRespawnPacket.class) @@ -78,6 +79,8 @@ public class JavaRespawnTranslator extends PacketTranslator session.setThunder(false); } + ChunkUtils.applyDimensionHeight(session, packet.getDimension()); + String newDimension = DimensionUtils.getNewDimension(packet.getDimension()); if (!session.getDimension().equals(newDimension) || !packet.getWorldName().equals(session.getWorldName())) { if (!packet.getWorldName().equals(session.getWorldName()) && session.getDimension().equals(newDimension)) { diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/java/JavaStatisticsTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/java/JavaStatisticsTranslator.java index 9bf810e21..247808041 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/java/JavaStatisticsTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/java/JavaStatisticsTranslator.java @@ -40,7 +40,7 @@ public class JavaStatisticsTranslator extends PacketTranslator= Bedrock_v428.V428_CODEC.getProtocolVersion()) { - passenger.getMetadata().put(EntityData.RIDER_MIN_ROTATION, 1f); - passenger.getMetadata().put(EntityData.RIDER_ROTATION_OFFSET, -90f); - } else { - passenger.getMetadata().put(EntityData.RIDER_MIN_ROTATION, -90f); - } + passenger.getMetadata().put(EntityData.RIDER_MIN_ROTATION, 1f); + passenger.getMetadata().put(EntityData.RIDER_ROTATION_OFFSET, -90f); } else { passenger.getMetadata().put(EntityData.RIDER_ROTATION_LOCKED, (byte) 0); passenger.getMetadata().put(EntityData.RIDER_MAX_ROTATION, 0f); @@ -123,13 +112,11 @@ public class JavaEntitySetPassengersTranslator extends PacketTranslator= Bedrock_v428.V428_CODEC.getProtocolVersion()) { - passenger.getMetadata().put(EntityData.RIDER_ROTATION_OFFSET, 0f); - } + passenger.getMetadata().put(EntityData.RIDER_ROTATION_OFFSET, 0f); - this.updateOffset(passenger, entity, session, false, false, (packet.getPassengerIds().length > 1)); + EntityUtils.updateMountOffset(passenger, entity, session, false, false, (packet.getPassengerIds().length > 1)); } else { - this.updateOffset(passenger, entity, session, (packet.getPassengerIds()[0] == passengerId), true, (packet.getPassengerIds().length > 1)); + EntityUtils.updateMountOffset(passenger, entity, session, (packet.getPassengerIds()[0] == passengerId), true, (packet.getPassengerIds().length > 1)); } // Force an update to the passenger metadata @@ -147,144 +134,4 @@ public class JavaEntitySetPassengersTranslator extends PacketTranslator { +@Translator(packet = ServerRemoveEntityPacket.class) +public class JavaRemoveEntityTranslator extends PacketTranslator { @Override - public void translate(ServerEntityDestroyPacket packet, GeyserSession session) { - for (int entityId : packet.getEntityIds()) { - Entity entity = session.getEntityCache().getEntityByJavaId(entityId); + public void translate(ServerRemoveEntityPacket packet, GeyserSession session) { + Entity entity = session.getEntityCache().getEntityByJavaId(packet.getEntityId()); - if (entity != null) { - session.getEntityCache().removeEntity(entity, false); - } + if (entity != null) { + session.getEntityCache().removeEntity(entity, false); } } } diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/java/entity/player/JavaPlayerActionAckTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/java/entity/player/JavaPlayerActionAckTranslator.java index 014ff3290..020c554ff 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/java/entity/player/JavaPlayerActionAckTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/java/entity/player/JavaPlayerActionAckTranslator.java @@ -25,21 +25,15 @@ package org.geysermc.connector.network.translators.java.entity.player; -import com.github.steveice10.mc.protocol.data.game.entity.player.GameMode; import com.github.steveice10.mc.protocol.data.game.entity.player.PlayerAction; import com.github.steveice10.mc.protocol.packet.ingame.server.entity.player.ServerPlayerActionAckPacket; -import com.github.steveice10.opennbt.tag.builtin.CompoundTag; import com.nukkitx.math.vector.Vector3f; import com.nukkitx.protocol.bedrock.data.LevelEventType; import com.nukkitx.protocol.bedrock.packet.LevelEventPacket; -import org.geysermc.connector.inventory.GeyserItemStack; -import org.geysermc.connector.inventory.PlayerInventory; 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.world.block.BlockTranslator; -import org.geysermc.connector.utils.BlockUtils; import org.geysermc.connector.utils.ChunkUtils; @Translator(packet = ServerPlayerActionAckPacket.class) @@ -56,47 +50,5 @@ public class JavaPlayerActionAckTranslator extends PacketTranslator 1.5 || (yDis < 1.45 || yDis > (session.isJumping() ? 4.3 : (session.isSprinting() ? 2.5 : 1.9))) || zDis > 1.5)) { - // Fake confirm the teleport but don't send it to the client - ClientTeleportConfirmPacket teleportConfirmPacket = new ClientTeleportConfirmPacket(packet.getTeleportId()); - session.sendDownstreamPacket(teleportConfirmPacket); - return; - } + if (packet.isDismountVehicle() && session.getRidingVehicleEntity() != null) { + Entity vehicle = session.getRidingVehicleEntity(); + SetEntityLinkPacket linkPacket = new SetEntityLinkPacket(); + linkPacket.setEntityLink(new EntityLinkData(vehicle.getGeyserId(), entity.getGeyserId(), EntityLinkData.Type.REMOVE, false, false)); + session.sendUpstreamPacket(linkPacket); + vehicle.getPassengers().remove(entity.getEntityId()); + entity.getMetadata().put(EntityData.RIDER_ROTATION_LOCKED, (byte) 0); + entity.getMetadata().put(EntityData.RIDER_MAX_ROTATION, 0f); + entity.getMetadata().put(EntityData.RIDER_MIN_ROTATION, 0f); + entity.getMetadata().put(EntityData.RIDER_ROTATION_OFFSET, 0f); + session.setRidingVehicleEntity(null); + entity.updateBedrockMetadata(session); + + EntityUtils.updateMountOffset(entity, vehicle, session, false, false, entity.getPassengers().size() > 1); } // If coordinates are relative, then add to the existing coordinate diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/java/entity/spawn/JavaSpawnEntityTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/java/entity/spawn/JavaSpawnEntityTranslator.java index b6a41fa42..7bce03d72 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/java/entity/spawn/JavaSpawnEntityTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/java/entity/spawn/JavaSpawnEntityTranslator.java @@ -64,7 +64,7 @@ public class JavaSpawnEntityTranslator extends PacketTranslator { + + @Override + public void translate(ServerClearTitlesPacket packet, GeyserSession session) { + SetTitlePacket titlePacket = new SetTitlePacket(); + // TODO handle packet.isResetTimes() + titlePacket.setType(SetTitlePacket.Type.CLEAR); + titlePacket.setText(""); + session.sendUpstreamPacket(titlePacket); + } +} 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/title/JavaSetActionBarTextTranslator.java similarity index 52% rename from connector/src/main/java/org/geysermc/connector/network/translators/java/JavaTitleTranslator.java rename to connector/src/main/java/org/geysermc/connector/network/translators/java/title/JavaSetActionBarTextTranslator.java index ffda57826..a659a3fb2 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/title/JavaSetActionBarTextTranslator.java @@ -23,58 +23,30 @@ * @link https://github.com/GeyserMC/Geyser */ -package org.geysermc.connector.network.translators.java; +package org.geysermc.connector.network.translators.java.title; +import com.github.steveice10.mc.protocol.packet.ingame.server.title.ServerSetActionBarTextPacket; +import com.nukkitx.protocol.bedrock.packet.SetTitlePacket; 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.chat.MessageTranslator; -import com.github.steveice10.mc.protocol.packet.ingame.server.ServerTitlePacket; -import com.nukkitx.protocol.bedrock.packet.SetTitlePacket; - -@Translator(packet = ServerTitlePacket.class) -public class JavaTitleTranslator extends PacketTranslator { +@Translator(packet = ServerSetActionBarTextPacket.class) +public class JavaSetActionBarTextTranslator extends PacketTranslator { @Override - public void translate(ServerTitlePacket packet, GeyserSession session) { - SetTitlePacket titlePacket = new SetTitlePacket(); - String locale = session.getLocale(); - + public void translate(ServerSetActionBarTextPacket packet, GeyserSession session) { String text; - if (packet.getTitle() == null) { + if (packet.getText() == null) { //TODO 1.17 can this happen? text = " "; } else { - text = MessageTranslator.convertMessage(packet.getTitle(), locale); - } - - switch (packet.getAction()) { - case TITLE: - titlePacket.setType(SetTitlePacket.Type.TITLE); - titlePacket.setText(text); - break; - case SUBTITLE: - titlePacket.setType(SetTitlePacket.Type.SUBTITLE); - titlePacket.setText(text); - break; - case CLEAR: - case RESET: - titlePacket.setType(SetTitlePacket.Type.CLEAR); - titlePacket.setText(""); - break; - case ACTION_BAR: - titlePacket.setType(SetTitlePacket.Type.ACTIONBAR); - titlePacket.setText(text); - break; - case TIMES: - titlePacket.setType(SetTitlePacket.Type.TIMES); - titlePacket.setFadeInTime(packet.getFadeIn()); - titlePacket.setFadeOutTime(packet.getFadeOut()); - titlePacket.setStayTime(packet.getStay()); - titlePacket.setText(""); - break; + text = MessageTranslator.convertMessage(packet.getText(), session.getLocale()); } + SetTitlePacket titlePacket = new SetTitlePacket(); + titlePacket.setType(SetTitlePacket.Type.ACTIONBAR); + titlePacket.setText(text); session.sendUpstreamPacket(titlePacket); } } diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/java/title/JavaSetSubtitleTextTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/java/title/JavaSetSubtitleTextTranslator.java new file mode 100644 index 000000000..d0f97cc3d --- /dev/null +++ b/connector/src/main/java/org/geysermc/connector/network/translators/java/title/JavaSetSubtitleTextTranslator.java @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2019-2021 GeyserMC. http://geysermc.org + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * @author GeyserMC + * @link https://github.com/GeyserMC/Geyser + */ + +package org.geysermc.connector.network.translators.java.title; + +import com.github.steveice10.mc.protocol.packet.ingame.server.title.ServerSetSubtitleTextPacket; +import com.nukkitx.protocol.bedrock.packet.SetTitlePacket; +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.chat.MessageTranslator; + +@Translator(packet = ServerSetSubtitleTextPacket.class) +public class JavaSetSubtitleTextTranslator extends PacketTranslator { + + @Override + public void translate(ServerSetSubtitleTextPacket packet, GeyserSession session) { + String text; + if (packet.getText() == null) { //TODO 1.17 can this happen? + text = " "; + } else { + text = MessageTranslator.convertMessage(packet.getText(), session.getLocale()); + } + + SetTitlePacket titlePacket = new SetTitlePacket(); + titlePacket.setType(SetTitlePacket.Type.SUBTITLE); + titlePacket.setText(text); + session.sendUpstreamPacket(titlePacket); + } +} diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/world/block/BlockTranslator1_16_100.java b/connector/src/main/java/org/geysermc/connector/network/translators/java/title/JavaSetTitleTextTranslator.java similarity index 52% rename from connector/src/main/java/org/geysermc/connector/network/translators/world/block/BlockTranslator1_16_100.java rename to connector/src/main/java/org/geysermc/connector/network/translators/java/title/JavaSetTitleTextTranslator.java index e10a503ea..7cf06a4f7 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/world/block/BlockTranslator1_16_100.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/java/title/JavaSetTitleTextTranslator.java @@ -23,37 +23,30 @@ * @link https://github.com/GeyserMC/Geyser */ -package org.geysermc.connector.network.translators.world.block; +package org.geysermc.connector.network.translators.java.title; -import com.google.common.collect.ImmutableSet; -import com.nukkitx.nbt.NbtMapBuilder; +import com.github.steveice10.mc.protocol.packet.ingame.server.title.ServerSetTitleTextPacket; +import com.nukkitx.protocol.bedrock.packet.SetTitlePacket; +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.chat.MessageTranslator; -import java.util.Set; - -public class BlockTranslator1_16_100 extends BlockTranslator { - private static final Set CORRECTED_STATES = ImmutableSet.of("minecraft:stripped_warped_stem", - "minecraft:stripped_warped_hyphae", "minecraft:stripped_crimson_stem", "minecraft:stripped_crimson_hyphae"); - - public static final BlockTranslator1_16_100 INSTANCE = new BlockTranslator1_16_100(); - - public BlockTranslator1_16_100() { - super("bedrock/blockpalette.1_16_100.nbt"); - } +@Translator(packet = ServerSetTitleTextPacket.class) +public class JavaSetTitleTextTranslator extends PacketTranslator { @Override - public int getBlockStateVersion() { - return 17825808; - } - - @Override - protected NbtMapBuilder adjustBlockStateForVersion(String bedrockIdentifier, NbtMapBuilder statesBuilder) { - if (CORRECTED_STATES.contains(bedrockIdentifier)) { - statesBuilder.putInt("deprecated", 0); + public void translate(ServerSetTitleTextPacket packet, GeyserSession session) { + String text; + if (packet.getText() == null) { //TODO 1.17 can this happen? + text = " "; + } else { + text = MessageTranslator.convertMessage(packet.getText(), session.getLocale()); } - return super.adjustBlockStateForVersion(bedrockIdentifier, statesBuilder); - } - public static void init() { - // no-op + SetTitlePacket titlePacket = new SetTitlePacket(); + titlePacket.setType(SetTitlePacket.Type.TITLE); + titlePacket.setText(text); + session.sendUpstreamPacket(titlePacket); } } diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/java/window/JavaConfirmTransactionTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/java/title/JavaSetTitlesAnimationTranslator.java similarity index 62% rename from connector/src/main/java/org/geysermc/connector/network/translators/java/window/JavaConfirmTransactionTranslator.java rename to connector/src/main/java/org/geysermc/connector/network/translators/java/title/JavaSetTitlesAnimationTranslator.java index 3b55733bf..9e2c13eb5 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/java/window/JavaConfirmTransactionTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/java/title/JavaSetTitlesAnimationTranslator.java @@ -23,25 +23,25 @@ * @link https://github.com/GeyserMC/Geyser */ -package org.geysermc.connector.network.translators.java.window; +package org.geysermc.connector.network.translators.java.title; -import com.github.steveice10.mc.protocol.packet.ingame.client.window.ClientConfirmTransactionPacket; -import com.github.steveice10.mc.protocol.packet.ingame.server.window.ServerConfirmTransactionPacket; -import org.geysermc.connector.inventory.Inventory; +import com.github.steveice10.mc.protocol.packet.ingame.server.title.ServerSetTitlesAnimationPacket; +import com.nukkitx.protocol.bedrock.packet.SetTitlePacket; import org.geysermc.connector.network.session.GeyserSession; import org.geysermc.connector.network.translators.PacketTranslator; import org.geysermc.connector.network.translators.Translator; -@Translator(packet = ServerConfirmTransactionPacket.class) -public class JavaConfirmTransactionTranslator extends PacketTranslator { +@Translator(packet = ServerSetTitlesAnimationPacket.class) +public class JavaSetTitlesAnimationTranslator extends PacketTranslator { @Override - public void translate(ServerConfirmTransactionPacket packet, GeyserSession session) { - session.addInventoryTask(() -> { - if (!packet.isAccepted()) { - ClientConfirmTransactionPacket confirmPacket = new ClientConfirmTransactionPacket(packet.getWindowId(), packet.getActionId(), true); - session.sendDownstreamPacket(confirmPacket); - } - }); + public void translate(ServerSetTitlesAnimationPacket packet, GeyserSession session) { + SetTitlePacket titlePacket = new SetTitlePacket(); + titlePacket.setType(SetTitlePacket.Type.TIMES); + titlePacket.setText(""); + titlePacket.setFadeInTime(packet.getFadeIn()); + titlePacket.setFadeOutTime(packet.getFadeOut()); + titlePacket.setStayTime(packet.getStay()); + session.sendUpstreamPacket(titlePacket); } } diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/java/world/JavaBlockChangeTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/java/world/JavaBlockChangeTranslator.java index de9563d9b..d5c54a5c7 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/java/world/JavaBlockChangeTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/java/world/JavaBlockChangeTranslator.java @@ -45,8 +45,7 @@ public class JavaBlockChangeTranslator extends PacketTranslator { - /** - * Determines if we should process non-full chunks - */ - private final boolean cacheChunks; - - public JavaChunkDataTranslator() { - cacheChunks = GeyserConnector.getInstance().getConfig().isCacheChunks(); - } @Override public void translate(ServerChunkDataPacket packet, GeyserSession session) { @@ -60,23 +52,15 @@ public class JavaChunkDataTranslator extends PacketTranslator { try { - ChunkUtils.ChunkData chunkData = ChunkUtils.translateToBedrock(session, mergedColumn, isNonFullChunk); + ChunkUtils.ChunkData chunkData = ChunkUtils.translateToBedrock(session, mergedColumn); ChunkSection[] sections = chunkData.getSections(); // Find highest section diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/java/world/JavaSpawnParticleTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/java/world/JavaSpawnParticleTranslator.java index dabcfd4c1..77f00bb1e 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/java/world/JavaSpawnParticleTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/java/world/JavaSpawnParticleTranslator.java @@ -114,7 +114,8 @@ public class JavaSpawnParticleTranslator extends PacketTranslator { - private final boolean cacheChunks; - - public JavaUpdateTileEntityTranslator() { - cacheChunks = GeyserConnector.getInstance().getConfig().isCacheChunks(); - } @Override public void translate(ServerUpdateTileEntityPacket packet, GeyserSession session) { String id = BlockEntityUtils.getBedrockBlockEntityId(packet.getType().name()); - if (packet.getNbt().isEmpty()) { // Fixes errors in CubeCraft sending empty NBT + if (packet.getNbt().isEmpty()) { // Fixes errors in servers sending empty NBT BlockEntityUtils.updateBlockEntity(session, null, packet.getPosition()); return; } @@ -59,11 +54,12 @@ public class JavaUpdateTileEntityTranslator extends PacketTranslator @@ -97,16 +85,6 @@ public abstract class WorldManager { */ public abstract boolean hasOwnChunkCache(); - /** - * Gets the Java biome data for the specified chunk. - * - * @param session the session of the player - * @param x the chunk's X coordinate - * @param z the chunk's Z coordinate - * @return the biome data for the specified region with a length of 1024. - */ - public abstract int[] getBiomeDataAt(GeyserSession session, int x, int z); - /** * Sigh.
* 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 b9167229c..ed7818924 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 @@ -43,9 +43,7 @@ import org.geysermc.connector.utils.FileUtils; import java.io.DataInputStream; import java.io.InputStream; -import java.util.HashMap; -import java.util.Iterator; -import java.util.Map; +import java.util.*; import java.util.zip.GZIPInputStream; public abstract class BlockTranslator { @@ -143,13 +141,6 @@ public abstract class BlockTranslator { builder.canBreakWithHand(false); } - JsonNode toolTypeNode = entry.getValue().get("tool_type"); - if (toolTypeNode != null) { - builder.toolType(toolTypeNode.textValue()); - } else { - builder.toolType(""); - } - JsonNode collisionIndexNode = entry.getValue().get("collision_index"); if (hardnessNode != null) { builder.collisionIndex(collisionIndexNode.intValue()); @@ -160,6 +151,13 @@ public abstract class BlockTranslator { builder.pickItem(pickItemNode.textValue()); } + boolean waterlogged = entry.getKey().contains("waterlogged=true") + || javaId.contains("minecraft:bubble_column") || javaId.contains("minecraft:kelp") || javaId.contains("seagrass"); + + if (waterlogged) { + WATERLOGGED.add(javaRuntimeId); + } + JAVA_ID_BLOCK_MAP.put(javaId, javaRuntimeId); BlockStateValues.storeBlockStateValues(entry.getKey(), javaRuntimeId, entry.getValue()); @@ -228,8 +226,7 @@ public abstract class BlockTranslator { BlockMapping.AIR = JAVA_RUNTIME_ID_TO_BLOCK_MAPPING.get(JAVA_AIR_ID); - BlockTranslator1_16_100.init(); - BlockTranslator1_16_210.init(); + BlockTranslator1_17_0.init(); BLOCKS_JSON = null; // We no longer require this so let it garbage collect away } @@ -293,7 +290,6 @@ public abstract class BlockTranslator { if (waterlogged) { bedrockToJavaBlockMap.putIfAbsent(bedrockRuntimeId | 1 << 31, javaRuntimeId); - WATERLOGGED.add(javaRuntimeId); } else { bedrockToJavaBlockMap.putIfAbsent(bedrockRuntimeId, javaRuntimeId); } @@ -329,7 +325,8 @@ public abstract class BlockTranslator { // Loop around again to find all item frame runtime IDs for (Object2IntMap.Entry entry : blockStateOrderedMap.object2IntEntrySet()) { - if (entry.getKey().getString("name").equals("minecraft:frame")) { + String name = entry.getKey().getString("name"); + if (name.equals("minecraft:frame") || name.equals("minecraft:glow_frame")) { itemFrames.put(entry.getKey(), entry.getIntValue()); } } diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/world/block/BlockTranslator1_16_210.java b/connector/src/main/java/org/geysermc/connector/network/translators/world/block/BlockTranslator1_17_0.java similarity index 85% rename from connector/src/main/java/org/geysermc/connector/network/translators/world/block/BlockTranslator1_16_210.java rename to connector/src/main/java/org/geysermc/connector/network/translators/world/block/BlockTranslator1_17_0.java index 58861cb9c..d86d0e71f 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/world/block/BlockTranslator1_16_210.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/world/block/BlockTranslator1_17_0.java @@ -25,11 +25,11 @@ package org.geysermc.connector.network.translators.world.block; -public class BlockTranslator1_16_210 extends BlockTranslator { - public static final BlockTranslator1_16_210 INSTANCE = new BlockTranslator1_16_210(); +public class BlockTranslator1_17_0 extends BlockTranslator { + public static final BlockTranslator1_17_0 INSTANCE = new BlockTranslator1_17_0(); - public BlockTranslator1_16_210() { - super("bedrock/blockpalette.1_16_210.nbt"); + public BlockTranslator1_17_0() { + super("bedrock/block_palette.1_17_0.nbt"); } @Override 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 12c0c9b64..7824f1e5b 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 @@ -33,11 +33,6 @@ import org.geysermc.connector.network.translators.world.block.BlockStateValues; @BlockEntity(name = "Banner") public class BannerBlockEntityTranslator extends BlockEntityTranslator implements RequiresBlockState { - @Override - public boolean isBlock(int blockState) { - return BlockStateValues.getBannerColor(blockState) != -1; - } - @Override public void translateTag(NbtMapBuilder builder, CompoundTag tag, int blockState) { int bannerColor = BlockStateValues.getBannerColor(blockState); 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 28cb52f64..f82bab99e 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 @@ -31,11 +31,6 @@ import org.geysermc.connector.network.translators.world.block.BlockStateValues; @BlockEntity(name = "Bed") public class BedBlockEntityTranslator extends BlockEntityTranslator implements RequiresBlockState { - @Override - public boolean isBlock(int blockState) { - return BlockStateValues.getBedColor(blockState) != -1; - } - @Override public void translateTag(NbtMapBuilder builder, CompoundTag tag, int blockState) { byte bedcolor = BlockStateValues.getBedColor(blockState); 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 2417f1e6e..5b96dc70d 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 @@ -32,7 +32,14 @@ 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 { +public interface BedrockOnlyBlockEntity extends RequiresBlockState { + /** + * Determines if block is part of class + * @param blockState BlockState to be compared + * @return true if part of the class + */ + boolean isBlock(int blockState); + /** * Update the block on Bedrock Edition. * @param session GeyserSession. 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 f109ed6b4..983a3d06d 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 @@ -35,7 +35,6 @@ import it.unimi.dsi.fastutil.objects.ObjectArrayList; import org.geysermc.connector.GeyserConnector; import org.geysermc.connector.utils.BlockEntityUtils; import org.geysermc.connector.utils.FileUtils; -import org.geysermc.connector.utils.LanguageUtils; import org.reflections.Reflections; import java.util.HashMap; @@ -47,10 +46,9 @@ import java.util.Map; public abstract class BlockEntityTranslator { public static final Map BLOCK_ENTITY_TRANSLATORS = new HashMap<>(); /** - * 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 + * A list of all block entities that only exist on Bedrock */ - public static final ObjectArrayList REQUIRES_BLOCK_STATE_LIST = new ObjectArrayList<>(); + public static final ObjectArrayList BEDROCK_ONLY_BLOCK_ENTITIES = new ObjectArrayList<>(); /** * Contains a list of irregular block entity name translations that can't be fit into the regex @@ -81,23 +79,17 @@ public abstract class BlockEntityTranslator { try { BLOCK_ENTITY_TRANSLATORS.put(clazz.getAnnotation(BlockEntity.class).name(), (BlockEntityTranslator) clazz.newInstance()); } catch (InstantiationException | IllegalAccessException e) { - GeyserConnector.getInstance().getLogger().error(LanguageUtils.getLocaleStringLog("geyser.network.translator.block_entity.failed", clazz.getCanonicalName())); + GeyserConnector.getInstance().getLogger().error("Could not instantiate annotated block entity" + 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()); + for (Class clazz : ref.getSubTypesOf(BedrockOnlyBlockEntity.class)) { + GeyserConnector.getInstance().getLogger().debug("Found Bedrock-only block entity: " + clazz.getCanonicalName()); try { - 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); + BedrockOnlyBlockEntity bedrockOnlyBlockEntity = (BedrockOnlyBlockEntity) clazz.newInstance(); + BEDROCK_ONLY_BLOCK_ENTITIES.add(bedrockOnlyBlockEntity); } catch (InstantiationException | IllegalAccessException e) { - GeyserConnector.getInstance().getLogger().error(LanguageUtils.getLocaleStringLog("geyser.network.translator.block_state.failed", clazz.getCanonicalName())); + GeyserConnector.getInstance().getLogger().error("Could not instantiate annotated block state " + clazz.getCanonicalName()); } } } 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 a4bb3e691..08c108b78 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 @@ -54,9 +54,4 @@ public class CommandBlockBlockEntityTranslator extends BlockEntityTranslator imp builder.put("LastExecution", (long) 0); } } - - @Override - public boolean isBlock(int blockState) { - return BlockStateValues.getCommandBlockValues().containsKey(blockState); - } } 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 9775017fb..7cda4f619 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 @@ -37,7 +37,7 @@ import org.geysermc.connector.utils.BlockEntityUtils; * Chests have more block entity properties in Bedrock, which is solved by implementing the BedrockOnlyBlockEntity */ @BlockEntity(name = "Chest") -public class DoubleChestBlockEntityTranslator extends BlockEntityTranslator implements BedrockOnlyBlockEntity, RequiresBlockState { +public class DoubleChestBlockEntityTranslator extends BlockEntityTranslator implements BedrockOnlyBlockEntity { @Override public boolean isBlock(int blockState) { return BlockStateValues.getDoubleChestValues().containsKey(blockState); 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 062fd4922..1fa6cab02 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 @@ -33,7 +33,7 @@ import org.geysermc.connector.network.session.GeyserSession; import org.geysermc.connector.network.translators.world.block.BlockStateValues; import org.geysermc.connector.utils.BlockEntityUtils; -public class FlowerPotBlockEntityTranslator implements BedrockOnlyBlockEntity, RequiresBlockState { +public class FlowerPotBlockEntityTranslator implements BedrockOnlyBlockEntity { /** * @param blockState the Java block state of a potential flower pot block * @return true if the block is a flower pot 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 3254565d1..a0c1033c5 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 @@ -30,21 +30,14 @@ import com.nukkitx.math.vector.Vector3i; import com.nukkitx.protocol.bedrock.packet.BlockEventPacket; import org.geysermc.connector.network.session.GeyserSession; import org.geysermc.connector.network.translators.world.block.BlockStateValues; -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 class NoteblockBlockEntityTranslator { public static void translate(GeyserSession session, Position position) { - int blockState = session.getConnector().getConfig().isCacheChunks() ? - session.getConnector().getWorldManager().getBlockAt(session, position) : - ChunkUtils.CACHED_BLOCK_ENTITIES.removeInt(position); + int blockState = session.getConnector().getWorldManager().getBlockAt(session, position); BlockEventPacket blockEventPacket = new BlockEventPacket(); blockEventPacket.setBlockPosition(Vector3i.from(position.getX(), position.getY(), position.getZ())); blockEventPacket.setEventType(0); diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/RequiresBlockState.java b/connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/RequiresBlockState.java index 146e024f2..76fb732b7 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/RequiresBlockState.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/RequiresBlockState.java @@ -29,12 +29,4 @@ package org.geysermc.connector.network.translators.world.block.entity; * Implemented in block entities if their Java block state is required for additional values in Bedrock */ public interface RequiresBlockState { - - /** - * Determines if block is part of class - * @param blockState BlockState to be compared - * @return true if part of the class - */ - boolean isBlock(int blockState); - } 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 04d58fcce..a6ea24f7c 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 @@ -32,7 +32,7 @@ import org.geysermc.connector.network.translators.world.block.BlockStateValues; import javax.annotation.Nullable; @BlockEntity(name = "ShulkerBox") -public class ShulkerBoxBlockEntityTranslator extends BlockEntityTranslator { +public class ShulkerBoxBlockEntityTranslator extends BlockEntityTranslator implements RequiresBlockState { /** * Also used in {@link org.geysermc.connector.network.translators.inventory.translators.ShulkerInventoryTranslator} * where {@code tag} is passed as null. 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 61ca4fa9c..ad6c32d4f 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 @@ -96,6 +96,7 @@ public class SignBlockEntityTranslator extends BlockEntityTranslator { @Override public void translateTag(NbtMapBuilder builder, CompoundTag tag, int blockState) { + //TODO Bedrock 1.17.10 glow text StringBuilder signText = new StringBuilder(); for (int i = 0; i < 4; i++) { int currentLine = i + 1; @@ -132,7 +133,7 @@ public class SignBlockEntityTranslator extends BlockEntityTranslator { signText.append(getBedrockSignColor(color.getValue().toString())); } - signText.append(finalSignLine.toString()); + signText.append(finalSignLine); signText.append("\n"); } 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 b3e25b212..e7139f20c 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 @@ -50,11 +50,6 @@ import java.util.concurrent.TimeUnit; public class SkullBlockEntityTranslator extends BlockEntityTranslator implements RequiresBlockState { public static boolean ALLOW_CUSTOM_SKULLS; - @Override - public boolean isBlock(int blockState) { - return BlockStateValues.getSkullVariant(blockState) != -1; - } - @Override public void translateTag(NbtMapBuilder builder, CompoundTag tag, int blockState) { byte skullVariant = BlockStateValues.getSkullVariant(blockState); diff --git a/connector/src/main/java/org/geysermc/connector/registry/type/BlockMapping.java b/connector/src/main/java/org/geysermc/connector/registry/type/BlockMapping.java index f3eaddd91..99045bb22 100644 --- a/connector/src/main/java/org/geysermc/connector/registry/type/BlockMapping.java +++ b/connector/src/main/java/org/geysermc/connector/registry/type/BlockMapping.java @@ -28,7 +28,6 @@ package org.geysermc.connector.registry.type; import lombok.Builder; import lombok.Value; -import javax.annotation.Nonnull; import javax.annotation.Nullable; @Builder @@ -45,7 +44,6 @@ public class BlockMapping { double hardness; boolean canBreakWithHand; - @Nonnull String toolType; /** * The index of this collision in collision.json */ diff --git a/connector/src/main/java/org/geysermc/connector/skin/FloodgateSkinUploader.java b/connector/src/main/java/org/geysermc/connector/skin/FloodgateSkinUploader.java new file mode 100644 index 000000000..beed78e05 --- /dev/null +++ b/connector/src/main/java/org/geysermc/connector/skin/FloodgateSkinUploader.java @@ -0,0 +1,235 @@ +/* + * Copyright (c) 2019-2021 GeyserMC. http://geysermc.org + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * @author GeyserMC + * @link https://github.com/GeyserMC/Geyser + */ + +package org.geysermc.connector.skin; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.node.ObjectNode; +import lombok.Getter; +import org.geysermc.connector.GeyserConnector; +import org.geysermc.connector.GeyserLogger; +import org.geysermc.connector.network.session.GeyserSession; +import org.geysermc.connector.utils.Constants; +import org.geysermc.connector.utils.PluginMessageUtils; +import org.geysermc.floodgate.util.WebsocketEventType; +import org.java_websocket.client.WebSocketClient; +import org.java_websocket.handshake.ServerHandshake; + +import javax.net.ssl.SSLException; +import java.net.ConnectException; +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; +import java.util.concurrent.ThreadLocalRandom; +import java.util.concurrent.TimeUnit; + +import static org.geysermc.connector.utils.PluginMessageUtils.getSkinChannel; + +public final class FloodgateSkinUploader { + private final ObjectMapper JACKSON = new ObjectMapper(); + private final List skinQueue = new ArrayList<>(); + + private final GeyserLogger logger; + private final WebSocketClient client; + + @Getter private int id; + @Getter private String verifyCode; + @Getter private int subscribersCount; + + public FloodgateSkinUploader(GeyserConnector connector) { + this.logger = connector.getLogger(); + this.client = new WebSocketClient(Constants.GLOBAL_API_WS_URI) { + @Override + public void onOpen(ServerHandshake handshake) { + setConnectionLostTimeout(11); + + Iterator queueIterator = skinQueue.iterator(); + while (isOpen() && queueIterator.hasNext()) { + send(queueIterator.next()); + queueIterator.remove(); + } + } + + @Override + public void onMessage(String message) { + // The reason why I don't like Jackson + try { + JsonNode node = JACKSON.readTree(message); + if (node.has("error")) { + logger.error("Got an error: " + node.get("error").asText()); + return; + } + + int typeId = node.get("event_id").asInt(); + WebsocketEventType type = WebsocketEventType.getById(typeId); + if (type == null) { + logger.warning(String.format( + "Got (unknown) type %s. Ensure that Geyser is on the latest version and report this issue!", + typeId)); + return; + } + + switch (type) { + case SUBSCRIBER_CREATED: + id = node.get("id").asInt(); + verifyCode = node.get("verify_code").asText(); + break; + case SUBSCRIBER_COUNT: + subscribersCount = node.get("subscribers_count").asInt(); + break; + case SKIN_UPLOADED: + // if Geyser is the only subscriber we have send it to the server manually + // otherwise it's handled by the Floodgate plugin subscribers + if (subscribersCount != 1) { + break; + } + + String xuid = node.get("xuid").asText(); + GeyserSession session = connector.getPlayerByXuid(xuid); + + if (session != null) { + if (!node.get("success").asBoolean()) { + logger.info("Failed to upload skin for " + session.getName()); + return; + } + + JsonNode data = node.get("data"); + + String value = data.get("value").asText(); + String signature = data.get("signature").asText(); + + byte[] bytes = (value + '\0' + signature) + .getBytes(StandardCharsets.UTF_8); + PluginMessageUtils.sendMessage(session, getSkinChannel(), bytes); + } + break; + case LOG_MESSAGE: + String logMessage = node.get("message").asText(); + switch (node.get("priority").asInt()) { + case -1: + logger.debug("Got a message from skin uploader: " + logMessage); + break; + case 0: + logger.info("Got a message from skin uploader: " +logMessage); + break; + case 1: + logger.error("Got a message from skin uploader: " + logMessage); + break; + default: + logger.info(logMessage); + break; + } + break; + case NEWS_ADDED: + //todo + } + } catch (Exception e) { + logger.error("Error while receiving a message", e); + } + } + + @Override + public void onClose(int code, String reason, boolean remote) { + if (reason != null && !reason.isEmpty()) { + // The reason why I don't like Jackson + try { + JsonNode node = JACKSON.readTree(reason); + // info means that the uploader itself did nothing wrong + if (node.has("info")) { + String info = node.get("info").asText(); + logger.debug("Got disconnected from the skin uploader: " + info); + } + // error means that the uploader did something wrong + if (node.has("error")) { + String error = node.get("error").asText(); + logger.info("Got disconnected from the skin uploader: " + error); + } + } catch (JsonProcessingException ignored) { + // ignore invalid json + } catch (Exception e) { + logger.error("Error while handling onClose", e); + } + } + // try to reconnect (which will make a new id and verify token) after a few seconds + reconnectLater(connector); + } + + @Override + public void onError(Exception ex) { + if (ex instanceof ConnectException || ex instanceof SSLException) { + if (logger.isDebug()) { + logger.error("[debug] Got an error", ex); + } + return; + } + logger.error("Got an error", ex); + } + }; + } + + public void uploadSkin(JsonNode chainData, String clientData) { + if (chainData == null || !chainData.isArray() || clientData == null) { + return; + } + + ObjectNode node = JACKSON.createObjectNode(); + node.set("chain_data", chainData); + node.put("client_data", clientData); + + // The reason why I don't like Jackson + String jsonString; + try { + jsonString = JACKSON.writeValueAsString(node); + } catch (Exception e) { + logger.error("Failed to upload skin", e); + return; + } + + if (client.isOpen()) { + client.send(jsonString); + return; + } + skinQueue.add(jsonString); + } + + private void reconnectLater(GeyserConnector connector) { + long additionalTime = ThreadLocalRandom.current().nextInt(7); + // we don't have to check the result. onClose will handle that for us + connector.getGeneralThreadPool() + .schedule(client::reconnect, 8 + additionalTime, TimeUnit.SECONDS); + } + + public FloodgateSkinUploader start() { + client.connect(); + return this; + } + + public void stop() { + client.close(); + } +} diff --git a/connector/src/main/java/org/geysermc/connector/skin/SkinProvider.java b/connector/src/main/java/org/geysermc/connector/skin/SkinProvider.java index 745a23ebd..6574c9350 100644 --- a/connector/src/main/java/org/geysermc/connector/skin/SkinProvider.java +++ b/connector/src/main/java/org/geysermc/connector/skin/SkinProvider.java @@ -86,7 +86,7 @@ public class SkinProvider { public static final String EARS_GEOMETRY_SLIM; public static final SkinGeometry SKULL_GEOMETRY; - private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper(); + public static final ObjectMapper OBJECT_MAPPER = new ObjectMapper(); static { /* Load in the normal ears geometry */ @@ -521,7 +521,7 @@ public class SkinProvider { return null; } - private static BufferedImage scale(BufferedImage bufferedImage, int newWidth, int newHeight) { + public static BufferedImage scale(BufferedImage bufferedImage, int newWidth, int newHeight) { BufferedImage resized = new BufferedImage(newWidth, newHeight, BufferedImage.TYPE_INT_ARGB); Graphics2D g2 = resized.createGraphics(); g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR); @@ -581,7 +581,6 @@ public class SkinProvider { outputStream.write((rgba >> 24) & 0xFF); } } - return outputStream.toByteArray(); } diff --git a/connector/src/main/java/org/geysermc/connector/utils/BlockUtils.java b/connector/src/main/java/org/geysermc/connector/utils/BlockUtils.java index 01c85ab43..4dc87d5d8 100644 --- a/connector/src/main/java/org/geysermc/connector/utils/BlockUtils.java +++ b/connector/src/main/java/org/geysermc/connector/utils/BlockUtils.java @@ -41,16 +41,28 @@ public class BlockUtils { */ public static final Position POSITION_ZERO = new Position(0, 0, 0); - private static boolean correctTool(String blockToolType, String itemToolType) { - return (blockToolType.equals("sword") && itemToolType.equals("sword")) || - (blockToolType.equals("shovel") && itemToolType.equals("shovel")) || - (blockToolType.equals("pickaxe") && itemToolType.equals("pickaxe")) || - (blockToolType.equals("axe") && itemToolType.equals("axe")) || - (blockToolType.equals("shears") && itemToolType.equals("shears")); + private static boolean correctTool(GeyserSession session, BlockMapping blockMapping, String itemToolType) { + switch (itemToolType) { + case "axe": + return session.getTagCache().isAxeEffective(blockMapping); + case "hoe": + return session.getTagCache().isHoeEffective(blockMapping); + case "pickaxe": + return session.getTagCache().isPickaxeEffective(blockMapping); + case "shears": + return session.getTagCache().isShearsEffective(blockMapping); + case "shovel": + return session.getTagCache().isShovelEffective(blockMapping); + case "sword": + return blockMapping.getJavaBlockId() == BlockTranslator.JAVA_COBWEB_BLOCK_ID; + default: + session.getConnector().getLogger().warning("Unknown tool type: " + itemToolType); + return false; + } } - private static double toolBreakTimeBonus(String toolType, String toolTier, boolean isWoolBlock) { - if (toolType.equals("shears")) return isWoolBlock ? 5.0 : 15.0; + private static double toolBreakTimeBonus(String toolType, String toolTier, boolean isShearsEffective) { + if (toolType.equals("shears")) return isShearsEffective ? 5.0 : 15.0; if (toolType.equals("")) return 1.0; switch (toolTier) { // https://minecraft.gamepedia.com/Breaking#Speed @@ -71,18 +83,41 @@ public class BlockUtils { } } - //http://minecraft.gamepedia.com/Breaking - private static double calculateBreakTime(double blockHardness, String toolTier, boolean canHarvestWithHand, boolean correctTool, - String toolType, boolean isWoolBlock, boolean isCobweb, int toolEfficiencyLevel, int hasteLevel, int miningFatigueLevel, + private static boolean canToolTierBreakBlock(GeyserSession session, BlockMapping blockMapping, String toolTier) { + if (toolTier.equals("netherite") || toolTier.equals("diamond")) { + // As of 1.17, these tiers can mine everything that is mineable + return true; + } + + switch (toolTier) { + // Use intentional fall-throughs to check each tier with this block + default: + if (session.getTagCache().requiresStoneTool(blockMapping)) { + return false; + } + case "stone": + if (session.getTagCache().requiresIronTool(blockMapping)) { + return false; + } + case "iron": + if (session.getTagCache().requiresDiamondTool(blockMapping)) { + return false; + } + } + + return true; + } + + // https://minecraft.gamepedia.com/Breaking + private static double calculateBreakTime(double blockHardness, String toolTier, boolean canHarvestWithHand, boolean correctTool, boolean canTierMineBlock, + String toolType, boolean isShearsEffective, int toolEfficiencyLevel, int hasteLevel, int miningFatigueLevel, boolean insideOfWaterWithoutAquaAffinity, boolean outOfWaterButNotOnGround, boolean insideWaterAndNotOnGround) { - double baseTime = ((correctTool || canHarvestWithHand) ? 1.5 : 5.0) * blockHardness; + double baseTime = (((correctTool && canTierMineBlock) || canHarvestWithHand) ? 1.5 : 5.0) * blockHardness; double speed = 1.0 / baseTime; if (correctTool) { - speed *= toolBreakTimeBonus(toolType, toolTier, isWoolBlock); + speed *= toolBreakTimeBonus(toolType, toolTier, isShearsEffective); speed += toolEfficiencyLevel == 0 ? 0 : toolEfficiencyLevel * toolEfficiencyLevel + 1; - } else if (toolType.equals("sword")) { - speed*= (isCobweb ? 15.0 : 1.5); } speed *= 1.0 + (0.2 * hasteLevel); @@ -110,18 +145,18 @@ public class BlockUtils { } public static double getBreakTime(GeyserSession session, BlockMapping blockMapping, ItemEntry item, CompoundTag nbtData, boolean isSessionPlayer) { - boolean isWoolBlock = session.getTagCache().isWool(blockMapping); - boolean isCobweb = blockMapping.getJavaBlockId() == BlockTranslator.JAVA_COBWEB_BLOCK_ID; - String blockToolType = blockMapping.getToolType(); + boolean isShearsEffective = session.getTagCache().isShearsEffective(blockMapping); //TODO called twice boolean canHarvestWithHand = blockMapping.isCanBreakWithHand(); String toolType = ""; String toolTier = ""; boolean correctTool = false; + boolean toolCanBreak = false; if (item instanceof ToolItemEntry) { ToolItemEntry toolItem = (ToolItemEntry) item; toolType = toolItem.getToolType(); toolTier = toolItem.getToolTier(); - correctTool = correctTool(blockToolType, toolType); + correctTool = correctTool(session, blockMapping, toolType); + toolCanBreak = canToolTierBreakBlock(session, blockMapping, toolTier); } int toolEfficiencyLevel = ItemUtils.getEnchantmentLevel(nbtData, "minecraft:efficiency"); int hasteLevel = 0; @@ -129,12 +164,12 @@ public class BlockUtils { if (!isSessionPlayer) { // Another entity is currently mining; we have all the information we know - return calculateBreakTime(blockMapping.getHardness(), toolTier, canHarvestWithHand, correctTool, toolType, isWoolBlock, - isCobweb, toolEfficiencyLevel, hasteLevel, miningFatigueLevel, false, + return calculateBreakTime(blockMapping.getHardness(), toolTier, canHarvestWithHand, correctTool, toolCanBreak, toolType, isShearsEffective, + toolEfficiencyLevel, hasteLevel, miningFatigueLevel, false, false, false); } - hasteLevel = session.getEffectCache().getEffectLevel(Effect.FASTER_DIG); + hasteLevel = Math.max(session.getEffectCache().getEffectLevel(Effect.FASTER_DIG), session.getEffectCache().getEffectLevel(Effect.CONDUIT_POWER)); miningFatigueLevel = session.getEffectCache().getEffectLevel(Effect.SLOWER_DIG); boolean isInWater = session.getCollisionManager().isPlayerInWater(); @@ -144,8 +179,8 @@ public class BlockUtils { boolean outOfWaterButNotOnGround = (!isInWater) && (!session.getPlayerEntity().isOnGround()); boolean insideWaterNotOnGround = isInWater && !session.getPlayerEntity().isOnGround(); - return calculateBreakTime(blockMapping.getHardness(), toolTier, canHarvestWithHand, correctTool, toolType, isWoolBlock, - isCobweb, toolEfficiencyLevel, hasteLevel, miningFatigueLevel, insideOfWaterWithoutAquaAffinity, + return calculateBreakTime(blockMapping.getHardness(), toolTier, canHarvestWithHand, correctTool, toolCanBreak, toolType, isShearsEffective, + toolEfficiencyLevel, hasteLevel, miningFatigueLevel, insideOfWaterWithoutAquaAffinity, outOfWaterButNotOnGround, insideWaterNotOnGround); } 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 d7de053ce..9723cd53c 100644 --- a/connector/src/main/java/org/geysermc/connector/utils/ChunkUtils.java +++ b/connector/src/main/java/org/geysermc/connector/utils/ChunkUtils.java @@ -32,6 +32,7 @@ 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; +import com.github.steveice10.opennbt.tag.builtin.IntTag; import com.github.steveice10.opennbt.tag.builtin.StringTag; import com.github.steveice10.opennbt.tag.builtin.Tag; import com.nukkitx.math.vector.Vector2i; @@ -42,8 +43,6 @@ import com.nukkitx.protocol.bedrock.packet.NetworkChunkPublisherUpdatePacket; import com.nukkitx.protocol.bedrock.packet.UpdateBlockPacket; import it.unimi.dsi.fastutil.ints.IntArrayList; import it.unimi.dsi.fastutil.ints.IntList; -import it.unimi.dsi.fastutil.objects.Object2IntMap; -import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap; import lombok.Data; import lombok.experimental.UtilityClass; import org.geysermc.connector.GeyserConnector; @@ -55,7 +54,6 @@ import org.geysermc.connector.network.translators.world.block.BlockStateValues; import org.geysermc.connector.network.translators.world.block.BlockTranslator; import org.geysermc.connector.network.translators.world.block.entity.BedrockOnlyBlockEntity; import org.geysermc.connector.network.translators.world.block.entity.BlockEntityTranslator; -import org.geysermc.connector.network.translators.world.block.entity.RequiresBlockState; import org.geysermc.connector.network.translators.world.block.entity.SkullBlockEntityTranslator; import org.geysermc.connector.network.translators.world.chunk.BlockStorage; import org.geysermc.connector.network.translators.world.chunk.ChunkSection; @@ -71,18 +69,23 @@ import static org.geysermc.connector.network.translators.world.block.BlockTransl @UtilityClass public class ChunkUtils { /** - * Temporarily stores positions of BlockState values that are needed for certain block entities actively. - * Not used if cache chunks is enabled + * The minimum height Bedrock Edition will accept. */ - public static final Object2IntMap CACHED_BLOCK_ENTITIES = new Object2IntOpenHashMap<>(); + private static final int MINIMUM_ACCEPTED_HEIGHT = 0; + /** + * The maximum height Bedrock Edition will accept. + */ + private static final int MAXIMUM_ACCEPTED_HEIGHT = 256; private static int indexYZXtoXZY(int yzx) { return (yzx >> 8) | (yzx & 0x0F0) | ((yzx & 0x00F) << 8); } - public static ChunkData translateToBedrock(GeyserSession session, Column column, boolean isNonFullChunk) { + public static ChunkData translateToBedrock(GeyserSession session, Column column) { Chunk[] javaSections = column.getChunks(); - ChunkSection[] sections = new ChunkSection[javaSections.length]; + // Ensure that, if the player is using lower world heights, the position is not offset + int yOffset = session.getChunkCache().getChunkMinY(); + ChunkSection[] sections = new ChunkSection[javaSections.length - yOffset]; // Temporarily stores compound tags of Bedrock-only block entities List bedrockOnlyBlockEntities = new ArrayList<>(); @@ -90,45 +93,16 @@ public class ChunkUtils { BitSet waterloggedPaletteIds = new BitSet(); BitSet pistonOrFlowerPaletteIds = new BitSet(); - boolean worldManagerHasMoreBlockDataThanCache = session.getConnector().getWorldManager().hasOwnChunkCache(); - - // If the received packet was a full chunk update, null sections in the chunk are guaranteed to also be null in the world manager - boolean shouldCheckWorldManagerOnMissingSections = isNonFullChunk && worldManagerHasMoreBlockDataThanCache; - Chunk temporarySection = null; - for (int sectionY = 0; sectionY < javaSections.length; sectionY++) { - Chunk javaSection = javaSections[sectionY]; - - // Section is null, the cache will not contain anything of use - if (javaSection == null) { - // The column parameter contains all data currently available from the cache. If the chunk is null and the world manager - // reports the ability to access more data than the cache, attempt to fetch from the world manager instead. - if (shouldCheckWorldManagerOnMissingSections) { - // Ensure that temporary chunk is set - if (temporarySection == null) { - temporarySection = new Chunk(); - } - - // Read block data in section - session.getConnector().getWorldManager().getBlocksInSection(session, column.getX(), sectionY, column.getZ(), temporarySection); - - if (temporarySection.isEmpty()) { - // The world manager only contains air for the given section - // We can leave temporarySection as-is to allow it to potentially be re-used for later sections - continue; - } else { - javaSection = temporarySection; - - // Section contents have been modified, we can't re-use it - temporarySection = null; - } - } else { - continue; - } + if (yOffset < 0 && sectionY < -yOffset) { + // Ignore this chunk since it goes below the accepted height limit + continue; } + Chunk javaSection = javaSections[sectionY]; + // No need to encode an empty section... - if (javaSection.isEmpty()) { + if (javaSection == null || javaSection.isEmpty()) { continue; } @@ -156,7 +130,7 @@ public class ChunkUtils { )); } } - sections[sectionY] = section; + sections[sectionY + yOffset] = section; continue; } @@ -229,7 +203,7 @@ public class ChunkUtils { layers = new BlockStorage[]{ layer0, new BlockStorage(BitArrayVersion.V1.createArray(BlockStorage.SIZE, layer1Data), layer1Palette) }; } - sections[sectionY] = new ChunkSection(layers); + sections[sectionY + yOffset] = new ChunkSection(layers); } CompoundTag[] blockEntities = column.getTileEntities(); @@ -262,7 +236,7 @@ public class ChunkUtils { // Get Java blockstate ID from block entity position int blockState = 0; - Chunk section = column.getChunks()[pos.getY() >> 4]; + Chunk section = column.getChunks()[(pos.getY() >> 4) - yOffset]; if (section != null) { blockState = section.get(pos.getX() & 0xF, pos.getY() & 0xF, pos.getZ() & 0xF); } @@ -332,6 +306,8 @@ public class ChunkUtils { if (itemFrameEntity != null) { if (blockState == JAVA_AIR_ID) { // Item frame is still present and no block overrides that; refresh it itemFrameEntity.updateBlock(session); + // Still update the chunk cache with the new block + session.getChunkCache().updateBlock(position.getX(), position.getY(), position.getZ(), blockState); return; } // Otherwise, let's still store our reference to the item frame, but let the new block take precedence for now @@ -388,20 +364,12 @@ public class ChunkUtils { return newLecternHasBook; }); - // Since Java stores bed colors/skull information as part of the namespaced ID and Bedrock stores it as a tag - // This is the only place I could find that interacts with the Java block state and block updates - // Iterates through all block entity translators and determines if the block state needs to be saved - for (RequiresBlockState requiresBlockState : BlockEntityTranslator.REQUIRES_BLOCK_STATE_LIST) { - if (requiresBlockState.isBlock(blockState)) { + // Iterates through all Bedrock-only block entity translators and determines if a manual block entity packet + // needs to be sent + for (BedrockOnlyBlockEntity bedrockOnlyBlockEntity : BlockEntityTranslator.BEDROCK_ONLY_BLOCK_ENTITIES) { + if (bedrockOnlyBlockEntity.isBlock(blockState)) { // Flower pots are block entities only in Bedrock and are not updated anywhere else like note blocks - if (requiresBlockState instanceof BedrockOnlyBlockEntity) { - ((BedrockOnlyBlockEntity) requiresBlockState).updateBlock(session, blockState, position); - break; - } - 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); - } + bedrockOnlyBlockEntity.updateBlock(session, blockState, position); break; //No block will be a part of two classes } } @@ -433,10 +401,31 @@ public class ChunkUtils { } } + /** + * Process the minimum and maximum heights for this dimension + */ + public static void applyDimensionHeight(GeyserSession session, CompoundTag dimensionTag) { + int minY = ((IntTag) dimensionTag.get("min_y")).getValue(); + int maxY = ((IntTag) dimensionTag.get("height")).getValue(); + // Logical height can be ignored probably - seems to be for artificial limits like the Nether. + + if (minY % 16 != 0) { + throw new RuntimeException("Minimum Y must be a multiple of 16!"); + } + if (maxY % 16 != 0) { + throw new RuntimeException("Maximum Y must be a multiple of 16!"); + } + + if (minY < MINIMUM_ACCEPTED_HEIGHT || maxY > MAXIMUM_ACCEPTED_HEIGHT) { + session.getConnector().getLogger().warning(LanguageUtils.getLocaleStringLog("geyser.network.translator.chunk.out_of_bounds")); + } + + session.getChunkCache().setMinY(minY); + } + @Data public static final class ChunkData { private final ChunkSection[] sections; - private final NbtMap[] blockEntities; } } diff --git a/common/src/main/java/org/geysermc/common/window/button/FormImage.java b/connector/src/main/java/org/geysermc/connector/utils/Constants.java similarity index 66% rename from common/src/main/java/org/geysermc/common/window/button/FormImage.java rename to connector/src/main/java/org/geysermc/connector/utils/Constants.java index 034278291..02f5c1ae4 100644 --- a/common/src/main/java/org/geysermc/common/window/button/FormImage.java +++ b/connector/src/main/java/org/geysermc/connector/utils/Constants.java @@ -23,40 +23,25 @@ * @link https://github.com/GeyserMC/Geyser */ -package org.geysermc.common.window.button; +package org.geysermc.connector.utils; -import lombok.Getter; -import lombok.Setter; +import java.net.URI; +import java.net.URISyntaxException; -public class FormImage { +public final class Constants { + public static final URI GLOBAL_API_WS_URI; + public static final String NTP_SERVER = "time.cloudflare.com"; - @Getter - @Setter - private String type; + public static final String NEWS_OVERVIEW_URL = "https://api.geysermc.org/v1/news"; + public static final String NEWS_PROJECT_NAME = "geyser"; - @Getter - @Setter - private String data; - - public FormImage(FormImageType type, String data) { - this.type = type.getName(); - this.data = data; - } - - public enum FormImageType { - PATH("path"), - URL("url"); - - @Getter - private String name; - - FormImageType(String name) { - this.name = name; - } - - @Override - public String toString() { - return name; + static { + URI wsUri = null; + try { + wsUri = new URI("wss://api.geysermc.org/ws"); + } catch (URISyntaxException e) { + e.printStackTrace(); } + GLOBAL_API_WS_URI = wsUri; } } diff --git a/connector/src/main/java/org/geysermc/connector/utils/EntityUtils.java b/connector/src/main/java/org/geysermc/connector/utils/EntityUtils.java index eb712b135..b5b0d24ca 100644 --- a/connector/src/main/java/org/geysermc/connector/utils/EntityUtils.java +++ b/connector/src/main/java/org/geysermc/connector/utils/EntityUtils.java @@ -26,9 +26,16 @@ package org.geysermc.connector.utils; import com.github.steveice10.mc.protocol.data.game.entity.Effect; +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.ArmorStandEntity; +import org.geysermc.connector.entity.living.animal.AnimalEntity; import org.geysermc.connector.entity.type.EntityType; +import org.geysermc.connector.network.session.GeyserSession; -public class EntityUtils { +public final class EntityUtils { /** * Convert Java edition effect IDs to Bedrock edition @@ -72,4 +79,147 @@ public class EntityUtils { return null; } } + + private static float getMountedHeightOffset(Entity mount) { + float height = mount.getMetadata().getFloat(EntityData.BOUNDING_BOX_HEIGHT); + float mountedHeightOffset = height * 0.75f; + switch (mount.getEntityType()) { + case CHICKEN: + case SPIDER: + mountedHeightOffset = height * 0.5f; + break; + case DONKEY: + case MULE: + mountedHeightOffset -= 0.25f; + break; + case LLAMA: + mountedHeightOffset = height * 0.67f; + break; + case MINECART: + case MINECART_HOPPER: + case MINECART_TNT: + case MINECART_CHEST: + case MINECART_FURNACE: + case MINECART_SPAWNER: + case MINECART_COMMAND_BLOCK: + mountedHeightOffset = 0; + break; + case BOAT: + mountedHeightOffset = -0.1f; + break; + case HOGLIN: + case ZOGLIN: + boolean isBaby = mount.getMetadata().getFlags().getFlag(EntityFlag.BABY); + mountedHeightOffset = height - (isBaby ? 0.2f : 0.15f); + break; + case PIGLIN: + mountedHeightOffset = height * 0.92f; + break; + case RAVAGER: + mountedHeightOffset = 2.1f; + break; + case SKELETON_HORSE: + mountedHeightOffset -= 0.1875f; + break; + case STRIDER: + mountedHeightOffset = height - 0.19f; + break; + } + return mountedHeightOffset; + } + + private static float getHeightOffset(Entity passenger) { + boolean isBaby; + switch (passenger.getEntityType()) { + case SKELETON: + case STRAY: + case WITHER_SKELETON: + return -0.6f; + case ARMOR_STAND: + if (((ArmorStandEntity) passenger).isMarker()) { + return 0.0f; + } else { + return 0.1f; + } + case ENDERMITE: + case SILVERFISH: + return 0.1f; + case PIGLIN: + case PIGLIN_BRUTE: + case ZOMBIFIED_PIGLIN: + isBaby = passenger.getMetadata().getFlags().getFlag(EntityFlag.BABY); + return isBaby ? -0.05f : -0.45f; + case ZOMBIE: + isBaby = passenger.getMetadata().getFlags().getFlag(EntityFlag.BABY); + return isBaby ? 0.0f : -0.45f; + case EVOKER: + case ILLUSIONER: + case PILLAGER: + case RAVAGER: + case VINDICATOR: + case WITCH: + return -0.45f; + case PLAYER: + return -0.35f; + } + if (passenger instanceof AnimalEntity) { + return 0.14f; + } + return 0f; + } + + /** + * Adjust an entity's height if they have mounted/dismounted an entity. + */ + public static void updateMountOffset(Entity passenger, Entity mount, GeyserSession session, boolean rider, boolean riding, boolean moreThanOneEntity) { + passenger.getMetadata().getFlags().setFlag(EntityFlag.RIDING, riding); + if (riding) { + // Without the Y offset, Bedrock players will find themselves in the floor when mounting + float mountedHeightOffset = getMountedHeightOffset(mount); + float heightOffset = getHeightOffset(passenger); + + float xOffset = 0; + float yOffset = mountedHeightOffset + heightOffset; + float zOffset = 0; + switch (mount.getEntityType()) { + case BOAT: + // Without the X offset, more than one entity on a boat is stacked on top of each other + if (rider && moreThanOneEntity) { + xOffset = 0.2f; + } else if (moreThanOneEntity) { + xOffset = -0.6f; + } + break; + case CHICKEN: + zOffset = -0.1f; + break; + case LLAMA: + zOffset = -0.3f; + break; + } + /* + * Bedrock Differences + * Zoglin & Hoglin seem to be taller in Bedrock edition + * Horses are tinier + * Players, Minecarts, and Boats have different origins + */ + if (passenger.getEntityType() == EntityType.PLAYER && mount.getEntityType() != EntityType.PLAYER) { + yOffset += EntityType.PLAYER.getOffset(); + } + switch (mount.getEntityType()) { + case MINECART: + case MINECART_HOPPER: + case MINECART_TNT: + case MINECART_CHEST: + case MINECART_FURNACE: + case MINECART_SPAWNER: + case MINECART_COMMAND_BLOCK: + case BOAT: + yOffset -= mount.getEntityType().getHeight() * 0.5f; + } + Vector3f offset = Vector3f.from(xOffset, yOffset, zOffset); + passenger.getMetadata().put(EntityData.RIDER_SEAT_POSITION, offset); + } + passenger.updateBedrockMetadata(session); + } } 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 a96d29da2..eecae8e4f 100644 --- a/connector/src/main/java/org/geysermc/connector/utils/FileUtils.java +++ b/connector/src/main/java/org/geysermc/connector/utils/FileUtils.java @@ -152,7 +152,7 @@ public class FileUtils { public static InputStream getResource(String resource) { InputStream stream = FileUtils.class.getClassLoader().getResourceAsStream(resource); if (stream == null) { - throw new AssertionError(LanguageUtils.getLocaleStringLog("geyser.toolbox.fail.resource", resource)); + throw new AssertionError("Unable to find resource: " + resource); } return stream; } diff --git a/connector/src/main/java/org/geysermc/connector/utils/LanguageUtils.java b/connector/src/main/java/org/geysermc/connector/utils/LanguageUtils.java index 5284bbcff..5f5c1f17f 100644 --- a/connector/src/main/java/org/geysermc/connector/utils/LanguageUtils.java +++ b/connector/src/main/java/org/geysermc/connector/utils/LanguageUtils.java @@ -60,7 +60,9 @@ public class LanguageUtils { public static void loadGeyserLocale(String locale) { locale = formatLocale(locale); // Don't load the locale if it's already loaded. - if (LOCALE_MAPPINGS.containsKey(locale)) return; + if (LOCALE_MAPPINGS.containsKey(locale)) { + return; + } InputStream localeStream = GeyserConnector.class.getClassLoader().getResourceAsStream("languages/texts/" + locale + ".properties"); @@ -113,7 +115,7 @@ public class LanguageUtils { // Try and get the key from the default locale if (formatString == null) { - properties = LOCALE_MAPPINGS.get(formatLocale(getDefaultLocale())); + properties = LOCALE_MAPPINGS.get(getDefaultLocale()); formatString = properties.getProperty(key); } @@ -125,7 +127,7 @@ public class LanguageUtils { // Final fallback if (formatString == null) { - formatString = key; + return key; } return MessageFormat.format(formatString.replace("'", "''").replace("&", "\u00a7"), values); @@ -151,7 +153,10 @@ public class LanguageUtils { * @return the current default locale */ public static String getDefaultLocale() { - if (CACHED_LOCALE != null) return CACHED_LOCALE; // We definitely know the locale the user is using + if (CACHED_LOCALE != null) { + return CACHED_LOCALE; // We definitely know the locale the user is using + } + String locale; boolean isValid = true; if (GeyserConnector.getInstance() != null && diff --git a/connector/src/main/java/org/geysermc/connector/utils/LoginEncryptionUtils.java b/connector/src/main/java/org/geysermc/connector/utils/LoginEncryptionUtils.java index 1614f6191..d3d5fa67d 100644 --- a/connector/src/main/java/org/geysermc/connector/utils/LoginEncryptionUtils.java +++ b/connector/src/main/java/org/geysermc/connector/utils/LoginEncryptionUtils.java @@ -35,18 +35,17 @@ import com.nukkitx.network.util.Preconditions; import com.nukkitx.protocol.bedrock.packet.LoginPacket; import com.nukkitx.protocol.bedrock.packet.ServerToClientHandshakePacket; import com.nukkitx.protocol.bedrock.util.EncryptionUtils; -import org.geysermc.common.window.*; -import org.geysermc.common.window.button.FormButton; -import org.geysermc.common.window.component.InputComponent; -import org.geysermc.common.window.component.LabelComponent; -import org.geysermc.common.window.response.CustomFormResponse; -import org.geysermc.common.window.response.ModalFormResponse; -import org.geysermc.common.window.response.SimpleFormResponse; import org.geysermc.connector.GeyserConnector; +import org.geysermc.connector.configuration.GeyserConfiguration; import org.geysermc.connector.network.session.GeyserSession; import org.geysermc.connector.network.session.auth.AuthData; import org.geysermc.connector.network.session.auth.BedrockClientData; -import org.geysermc.connector.network.session.cache.WindowCache; +import org.geysermc.cumulus.CustomForm; +import org.geysermc.cumulus.ModalForm; +import org.geysermc.cumulus.SimpleForm; +import org.geysermc.cumulus.response.CustomFormResponse; +import org.geysermc.cumulus.response.ModalFormResponse; +import org.geysermc.cumulus.response.SimpleFormResponse; import javax.crypto.SecretKey; import java.io.IOException; @@ -73,7 +72,7 @@ public class LoginEncryptionUtils { } if (lastKey != null) { - if (!EncryptionUtils.verifyJwt(jwt, lastKey)) return false; + if (!EncryptionUtils.verifyJwt(jwt, lastKey)) return false; } JsonNode payloadNode = JSON_MAPPER.readTree(jwt.getPayload().toString()); @@ -121,7 +120,8 @@ public class LoginEncryptionUtils { session.setAuthenticationData(new AuthData( extraData.get("displayName").asText(), UUID.fromString(extraData.get("identity").asText()), - extraData.get("XUID").asText() + extraData.get("XUID").asText(), + certChainData, clientData )); if (payload.get("identityPublicKey").getNodeType() != JsonNodeType.STRING) { @@ -132,7 +132,9 @@ public class LoginEncryptionUtils { JWSObject clientJwt = JWSObject.parse(clientData); EncryptionUtils.verifyJwt(clientJwt, identityPublicKey); - session.setClientData(JSON_MAPPER.convertValue(JSON_MAPPER.readTree(clientJwt.getPayload().toBytes()), BedrockClientData.class)); + JsonNode clientDataJson = JSON_MAPPER.readTree(clientJwt.getPayload().toBytes()); + BedrockClientData data = JSON_MAPPER.convertValue(clientDataJson, BedrockClientData.class); + session.setClientData(data); if (EncryptionUtils.canUseEncryption()) { try { @@ -176,132 +178,118 @@ public class LoginEncryptionUtils { } } - private static final int AUTH_MSA_DETAILS_FORM_ID = 1334; - private static final int AUTH_MSA_CODE_FORM_ID = 1335; - private static final int AUTH_FORM_ID = 1336; - private static final int AUTH_DETAILS_FORM_ID = 1337; - - public static void showLoginWindow(GeyserSession session) { + public static void buildAndShowLoginWindow(GeyserSession session) { // Set DoDaylightCycle to false so the time doesn't accelerate while we're here session.setDaylightCycle(false); - String userLanguage = session.getLocale(); - SimpleFormWindow window = new SimpleFormWindow(LanguageUtils.getPlayerLocaleString("geyser.auth.login.form.notice.title", userLanguage), LanguageUtils.getPlayerLocaleString("geyser.auth.login.form.notice.desc", userLanguage)); - if (session.getConnector().getConfig().getRemote().isPasswordAuthentication()) { - window.getButtons().add(new FormButton(LanguageUtils.getPlayerLocaleString("geyser.auth.login.form.notice.btn_login.mojang", userLanguage))); - } - window.getButtons().add(new FormButton(LanguageUtils.getPlayerLocaleString("geyser.auth.login.form.notice.btn_login.microsoft", userLanguage))); - window.getButtons().add(new FormButton(LanguageUtils.getPlayerLocaleString("geyser.auth.login.form.notice.btn_disconnect", userLanguage))); + GeyserConfiguration config = session.getConnector().getConfig(); + boolean isPasswordAuthEnabled = config.getRemote().isPasswordAuthentication(); - session.sendForm(window, AUTH_FORM_ID); + session.sendForm( + SimpleForm.builder() + .translator(LanguageUtils::getPlayerLocaleString, session.getLocale()) + .title("geyser.auth.login.form.notice.title") + .content("geyser.auth.login.form.notice.desc") + .optionalButton("geyser.auth.login.form.notice.btn_login.mojang", isPasswordAuthEnabled) + .button("geyser.auth.login.form.notice.btn_login.microsoft") + .button("geyser.auth.login.form.notice.btn_disconnect") + .responseHandler((form, responseData) -> { + SimpleFormResponse response = form.parseResponse(responseData); + if (!response.isCorrect()) { + buildAndShowLoginWindow(session); + return; + } + + if (isPasswordAuthEnabled && response.getClickedButtonId() == 0) { + session.setMicrosoftAccount(false); + buildAndShowLoginDetailsWindow(session); + return; + } + + if (isPasswordAuthEnabled && response.getClickedButtonId() == 1) { + session.setMicrosoftAccount(true); + buildAndShowMicrosoftAuthenticationWindow(session); + return; + } + + if (response.getClickedButtonId() == 0) { + // Just show the OAuth code + session.authenticateWithMicrosoftCode(); + return; + } + + session.disconnect(LanguageUtils.getPlayerLocaleString("geyser.auth.login.form.disconnect", session.getLocale())); + })); } - public static void showLoginDetailsWindow(GeyserSession session) { - String userLanguage = session.getLocale(); - CustomFormWindow window = new CustomFormBuilder(LanguageUtils.getPlayerLocaleString("geyser.auth.login.form.details.title", userLanguage)) - .addComponent(new LabelComponent(LanguageUtils.getPlayerLocaleString("geyser.auth.login.form.details.desc", userLanguage))) - .addComponent(new InputComponent(LanguageUtils.getPlayerLocaleString("geyser.auth.login.form.details.email", userLanguage), "account@geysermc.org", "")) - .addComponent(new InputComponent(LanguageUtils.getPlayerLocaleString("geyser.auth.login.form.details.pass", userLanguage), "123456", "")) - .build(); + public static void buildAndShowLoginDetailsWindow(GeyserSession session) { + session.sendForm( + CustomForm.builder() + .translator(LanguageUtils::getPlayerLocaleString, session.getLocale()) + .title("geyser.auth.login.form.details.title") + .label("geyser.auth.login.form.details.desc") + .input("geyser.auth.login.form.details.email", "account@geysermc.org", "") + .input("geyser.auth.login.form.details.pass", "123456", "") + .responseHandler((form, responseData) -> { + CustomFormResponse response = form.parseResponse(responseData); + if (!response.isCorrect()) { + buildAndShowLoginDetailsWindow(session); + return; + } - session.sendForm(window, AUTH_DETAILS_FORM_ID); + session.authenticate(response.next(), response.next()); + })); } /** * Prompts the user between either OAuth code login or manual password authentication */ - public static void showMicrosoftAuthenticationWindow(GeyserSession session) { - String userLanguage = session.getLocale(); - SimpleFormWindow window = new SimpleFormWindow(LanguageUtils.getPlayerLocaleString("geyser.auth.login.form.notice.btn_login.microsoft", userLanguage), ""); - window.getButtons().add(new FormButton(LanguageUtils.getPlayerLocaleString("geyser.auth.login.method.browser", userLanguage))); - window.getButtons().add(new FormButton(LanguageUtils.getPlayerLocaleString("geyser.auth.login.method.password", userLanguage))); // This form won't show if password authentication is disabled - window.getButtons().add(new FormButton(LanguageUtils.getPlayerLocaleString("geyser.auth.login.form.notice.btn_disconnect", userLanguage))); - session.sendForm(window, AUTH_MSA_DETAILS_FORM_ID); + public static void buildAndShowMicrosoftAuthenticationWindow(GeyserSession session) { + session.sendForm( + SimpleForm.builder() + .translator(LanguageUtils::getPlayerLocaleString, session.getLocale()) + .title("geyser.auth.login.form.notice.btn_login.microsoft") + .button("geyser.auth.login.method.browser") + .button("geyser.auth.login.method.password") + .button("geyser.auth.login.form.notice.btn_disconnect") + .responseHandler((form, responseData) -> { + SimpleFormResponse response = form.parseResponse(responseData); + if (!response.isCorrect()) { + buildAndShowLoginWindow(session); + return; + } + + if (response.getClickedButtonId() == 0) { + session.authenticateWithMicrosoftCode(); + } else if (response.getClickedButtonId() == 1) { + buildAndShowLoginDetailsWindow(session); + } else { + session.disconnect(LanguageUtils.getPlayerLocaleString("geyser.auth.login.form.disconnect", session.getLocale())); + } + })); } /** * Shows the code that a user must input into their browser */ - public static void showMicrosoftCodeWindow(GeyserSession session, MsaAuthenticationService.MsCodeResponse response) { - ModalFormWindow msaCodeWindow = new ModalFormWindow("%xbox.signin", "%xbox.signin.website\n%xbox.signin.url\n%xbox.signin.enterCode\n" + - response.user_code, "%gui.done", "%menu.disconnect"); - session.sendForm(msaCodeWindow, LoginEncryptionUtils.AUTH_MSA_CODE_FORM_ID); - } - - public static boolean authenticateFromForm(GeyserSession session, GeyserConnector connector, int formId, String formData) { - WindowCache windowCache = session.getWindowCache(); - if (!windowCache.getWindows().containsKey(formId)) - return false; - - if (formId == AUTH_MSA_DETAILS_FORM_ID || formId == AUTH_FORM_ID || formId == AUTH_DETAILS_FORM_ID || formId == AUTH_MSA_CODE_FORM_ID) { - FormWindow window = windowCache.getWindows().remove(formId); - window.setResponse(formData.trim()); - - if (!session.isLoggedIn()) { - if (formId == AUTH_DETAILS_FORM_ID && window instanceof CustomFormWindow) { - CustomFormWindow customFormWindow = (CustomFormWindow) window; - - CustomFormResponse response = (CustomFormResponse) customFormWindow.getResponse(); - if (response != null) { - String email = response.getInputResponses().get(1); - String password = response.getInputResponses().get(2); - - session.authenticate(email, password); - - // Clear windows so authentication data isn't accidentally cached - windowCache.getWindows().clear(); - } else { - showLoginDetailsWindow(session); - } - } else if (formId == AUTH_FORM_ID && window instanceof SimpleFormWindow) { - boolean isPasswordAuthentication = session.getConnector().getConfig().getRemote().isPasswordAuthentication(); - int microsoftButton = isPasswordAuthentication ? 1 : 0; - int disconnectButton = isPasswordAuthentication ? 2 : 1; - SimpleFormResponse response = (SimpleFormResponse) window.getResponse(); - if (response != null) { - if (isPasswordAuthentication && response.getClickedButtonId() == 0) { - session.setMicrosoftAccount(false); - showLoginDetailsWindow(session); - } else if (response.getClickedButtonId() == microsoftButton) { - session.setMicrosoftAccount(true); - if (isPasswordAuthentication) { - showMicrosoftAuthenticationWindow(session); - } else { - // Just show the OAuth code - session.authenticateWithMicrosoftCode(); + public static void buildAndShowMicrosoftCodeWindow(GeyserSession session, MsaAuthenticationService.MsCodeResponse msCode) { + session.sendForm( + ModalForm.builder() + .title("%xbox.signin") + .content("%xbox.signin.website\n%xbox.signin.url\n%xbox.signin.enterCode\n" + msCode.user_code) + .button1("%gui.done") + .button2("%menu.disconnect") + .responseHandler((form, responseData) -> { + ModalFormResponse response = form.parseResponse(responseData); + if (!response.isCorrect()) { + buildAndShowMicrosoftAuthenticationWindow(session); + return; } - } else if (response.getClickedButtonId() == disconnectButton) { - session.disconnect(LanguageUtils.getPlayerLocaleString("geyser.auth.login.form.disconnect", session.getLocale())); - } - } else { - showLoginWindow(session); - } - } else if (formId == AUTH_MSA_DETAILS_FORM_ID && window instanceof SimpleFormWindow) { - SimpleFormResponse response = (SimpleFormResponse) window.getResponse(); - if (response != null) { - if (response.getClickedButtonId() == 0) { - session.authenticateWithMicrosoftCode(); - } else if (response.getClickedButtonId() == 1) { - showLoginDetailsWindow(session); - } else if (response.getClickedButtonId() == 2) { - session.disconnect(LanguageUtils.getPlayerLocaleString("geyser.auth.login.form.disconnect", session.getLocale())); - } - } else { - showLoginWindow(session); - } - } else if (formId == AUTH_MSA_CODE_FORM_ID && window instanceof ModalFormWindow) { - ModalFormResponse response = (ModalFormResponse) window.getResponse(); - if (response != null) { - if (response.getClickedButtonId() == 1) { - session.disconnect(LanguageUtils.getPlayerLocaleString("geyser.auth.login.form.disconnect", session.getLocale())); - } - } else { - showMicrosoftAuthenticationWindow(session); - } - } - } - } - return true; - } + if (response.getClickedButtonId() == 1) { + session.disconnect(LanguageUtils.getPlayerLocaleString("geyser.auth.login.form.disconnect", session.getLocale())); + } + }) + ); + } } diff --git a/connector/src/main/java/org/geysermc/connector/utils/NewsHandler.java b/connector/src/main/java/org/geysermc/connector/utils/NewsHandler.java new file mode 100644 index 000000000..c70255f52 --- /dev/null +++ b/connector/src/main/java/org/geysermc/connector/utils/NewsHandler.java @@ -0,0 +1,176 @@ +/* + * Copyright (c) 2019-2021 GeyserMC. http://geysermc.org + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * @author GeyserMC + * @link https://github.com/GeyserMC/Geyser + */ + +package org.geysermc.connector.utils; + +import com.google.gson.Gson; +import com.google.gson.JsonArray; +import com.google.gson.JsonElement; +import com.google.gson.JsonSyntaxException; +import org.geysermc.connector.GeyserConnector; +import org.geysermc.connector.GeyserLogger; +import org.geysermc.connector.common.ChatColor; +import org.geysermc.connector.network.session.GeyserSession; +import org.geysermc.floodgate.news.NewsItem; +import org.geysermc.floodgate.news.NewsItemAction; +import org.geysermc.floodgate.news.data.BuildSpecificData; +import org.geysermc.floodgate.news.data.CheckAfterData; + +import java.util.*; +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.TimeUnit; + +public class NewsHandler { + private final ScheduledExecutorService executorService = Executors.newScheduledThreadPool(1); + private final GeyserLogger logger = GeyserConnector.getInstance().getLogger(); + private final Gson gson = new Gson(); + + private final Map activeNewsItems = new HashMap<>(); + private final String branch; + private final int build; + + private boolean firstCheck = true; + + public NewsHandler(String branch, int build) { + this.branch = branch; + this.build = build; + + executorService.scheduleWithFixedDelay(this::checkNews, 0, 30, TimeUnit.MINUTES); + } + + private void schedule(long delayMs) { + executorService.schedule(this::checkNews, delayMs, TimeUnit.MILLISECONDS); + } + + private void checkNews() { + try { + String body = WebUtils.getBody(Constants.NEWS_OVERVIEW_URL); + JsonArray array = gson.fromJson(body, JsonArray.class); + + try { + for (JsonElement newsItemElement : array) { + NewsItem newsItem = NewsItem.readItem(newsItemElement.getAsJsonObject()); + if (newsItem != null) { + addNews(newsItem); + } + } + firstCheck = false; + } catch (Exception e) { + if (logger.isDebug()) { + logger.error("Error while reading news item", e); + } + } + } catch (JsonSyntaxException ignored) {} + } + + public void handleNews(GeyserSession session, NewsItemAction action) { + for (NewsItem news : getActiveNews(action)) { + handleNewsItem(session, news, action); + } + } + + private void handleNewsItem(GeyserSession session, NewsItem news, NewsItemAction action) { + switch (action) { + case ON_SERVER_STARTED: + if (!firstCheck) { + return; + } + case BROADCAST_TO_CONSOLE: + logger.info(news.getMessage()); + break; + case ON_OPERATOR_JOIN: + //todo doesn't work, it's called before we know the op level. +// if (session != null && session.getOpPermissionLevel() >= 2) { +// session.sendMessage(ChatColor.GREEN + news.getMessage()); +// } + break; + case BROADCAST_TO_OPERATORS: + for (GeyserSession player : GeyserConnector.getInstance().getPlayers()) { + if (player.getOpPermissionLevel() >= 2) { + session.sendMessage(ChatColor.GREEN + news.getMessage()); + } + } + break; + } + } + + public Collection getActiveNews() { + return activeNewsItems.values(); + } + + public Collection getActiveNews(NewsItemAction action) { + List news = new ArrayList<>(); + for (NewsItem item : getActiveNews()) { + if (item.getActions().contains(action)) { + news.add(item); + } + } + return news; + } + + public void addNews(NewsItem item) { + if (activeNewsItems.containsKey(item.getId())) { + if (!item.isActive()) { + activeNewsItems.remove(item.getId()); + } + return; + } + + if (!item.isActive()) { + return; + } + + if (!item.isGlobal() && !Constants.NEWS_PROJECT_NAME.equals(item.getProject())) { + return; + } + + switch (item.getType()) { + case BUILD_SPECIFIC: + if (!item.getDataAs(BuildSpecificData.class).isAffected(branch, build)) { + return; + } + break; + case CHECK_AFTER: + long checkAfter = item.getDataAs(CheckAfterData.class).getCheckAfter(); + long delayMs = System.currentTimeMillis() - checkAfter; + schedule(delayMs > 0 ? delayMs : 0); + break; + } + + activeNewsItems.put(item.getId(), item); + activateNews(item); + } + + private void activateNews(NewsItem item) { + for (NewsItemAction action : item.getActions()) { + handleNewsItem(null, item, action); + } + } + + public void shutdown() { + executorService.shutdown(); + } +} diff --git a/connector/src/main/java/org/geysermc/connector/utils/PluginMessageUtils.java b/connector/src/main/java/org/geysermc/connector/utils/PluginMessageUtils.java index c5a7d9e8d..a914f699e 100644 --- a/connector/src/main/java/org/geysermc/connector/utils/PluginMessageUtils.java +++ b/connector/src/main/java/org/geysermc/connector/utils/PluginMessageUtils.java @@ -25,35 +25,63 @@ package org.geysermc.connector.utils; +import com.github.steveice10.mc.protocol.packet.ingame.client.ClientPluginMessagePacket; +import com.google.common.base.Charsets; import org.geysermc.connector.GeyserConnector; +import org.geysermc.connector.network.session.GeyserSession; -import java.nio.charset.StandardCharsets; +import java.nio.ByteBuffer; public class PluginMessageUtils { - - private static final byte[] BRAND_DATA; + private static final String SKIN_CHANNEL = "floodgate:skin"; + private static final byte[] GEYSER_BRAND_DATA; + private static final byte[] FLOODGATE_REGISTER_DATA; static { - byte[] data = GeyserConnector.NAME.getBytes(StandardCharsets.UTF_8); - byte[] varInt = writeVarInt(data.length); - BRAND_DATA = new byte[varInt.length + data.length]; - System.arraycopy(varInt, 0, BRAND_DATA, 0, varInt.length); - System.arraycopy(data, 0, BRAND_DATA, varInt.length, data.length); + byte[] data = GeyserConnector.NAME.getBytes(Charsets.UTF_8); + GEYSER_BRAND_DATA = + ByteBuffer.allocate(data.length + getVarIntLength(data.length)) + .put(writeVarInt(data.length)) + .put(data) + .array(); + + FLOODGATE_REGISTER_DATA = (SKIN_CHANNEL + "\0floodgate:form").getBytes(Charsets.UTF_8); } /** * Get the prebuilt brand as a byte array + * * @return the brand information of the Geyser client */ public static byte[] getGeyserBrandData() { - return BRAND_DATA; + return GEYSER_BRAND_DATA; + } + + /** + * Get the prebuilt register data as a byte array + * + * @return the register data of the Floodgate channels + */ + public static byte[] getFloodgateRegisterData() { + return FLOODGATE_REGISTER_DATA; + } + + /** + * Returns the skin channel used in Floodgate + */ + public static String getSkinChannel() { + return SKIN_CHANNEL; + } + + public static void sendMessage(GeyserSession session, String channel, byte[] data) { + session.sendDownstreamPacket(new ClientPluginMessagePacket(channel, data)); } private static byte[] writeVarInt(int value) { byte[] data = new byte[getVarIntLength(value)]; int index = 0; do { - byte temp = (byte)(value & 0b01111111); + byte temp = (byte) (value & 0b01111111); value >>>= 7; if (value != 0) { temp |= 0b10000000; diff --git a/connector/src/main/java/org/geysermc/connector/utils/SettingsUtils.java b/connector/src/main/java/org/geysermc/connector/utils/SettingsUtils.java index d0cfce862..0e2a54a3c 100644 --- a/connector/src/main/java/org/geysermc/connector/utils/SettingsUtils.java +++ b/connector/src/main/java/org/geysermc/connector/utils/SettingsUtils.java @@ -27,79 +27,69 @@ package org.geysermc.connector.utils; import com.github.steveice10.mc.protocol.data.game.entity.player.GameMode; import com.github.steveice10.mc.protocol.data.game.setting.Difficulty; -import org.geysermc.common.window.CustomFormBuilder; -import org.geysermc.common.window.CustomFormWindow; -import org.geysermc.common.window.button.FormImage; -import org.geysermc.common.window.component.DropdownComponent; -import org.geysermc.common.window.component.InputComponent; -import org.geysermc.common.window.component.LabelComponent; -import org.geysermc.common.window.component.ToggleComponent; -import org.geysermc.common.window.response.CustomFormResponse; import org.geysermc.connector.GeyserConnector; import org.geysermc.connector.network.session.GeyserSession; +import org.geysermc.connector.network.translators.world.WorldManager; +import org.geysermc.cumulus.CustomForm; +import org.geysermc.cumulus.component.DropdownComponent; +import org.geysermc.cumulus.response.CustomFormResponse; import java.util.ArrayList; public class SettingsUtils { - - // Used in UpstreamPacketHandler.java - public static final int SETTINGS_FORM_ID = 1338; - /** * Build a settings form for the given session and store it for later * * @param session The session to build the form for */ - public static void buildForm(GeyserSession session) { + public static CustomForm buildForm(GeyserSession session) { // Cache the language for cleaner access String language = session.getLocale(); - CustomFormBuilder builder = new CustomFormBuilder(LanguageUtils.getPlayerLocaleString("geyser.settings.title.main", language)); - builder.setIcon(new FormImage(FormImage.FormImageType.PATH, "textures/ui/settings_glyph_color_2x.png")); + CustomForm.Builder builder = CustomForm.builder() + .translator(LanguageUtils::getPlayerLocaleString, language) + .title("geyser.settings.title.main") + .iconPath("textures/ui/settings_glyph_color_2x.png"); // Only show the client title if any of the client settings are available if (session.getPreferencesCache().isAllowShowCoordinates() || CooldownUtils.getDefaultShowCooldown() != CooldownUtils.CooldownType.DISABLED) { - builder.addComponent(new LabelComponent(LanguageUtils.getPlayerLocaleString("geyser.settings.title.client", language))); + builder.label("geyser.settings.title.client"); // Client can only see its coordinates if reducedDebugInfo is disabled and coordinates are enabled in geyser config. if (session.getPreferencesCache().isAllowShowCoordinates()) { - builder.addComponent(new ToggleComponent(LanguageUtils.getPlayerLocaleString("geyser.settings.option.coordinates", language), session.getPreferencesCache().isPrefersShowCoordinates())); + builder.toggle("geyser.settings.option.coordinates", session.getPreferencesCache().isPrefersShowCoordinates()); } if (CooldownUtils.getDefaultShowCooldown() != CooldownUtils.CooldownType.DISABLED) { - DropdownComponent cooldownDropdown = new DropdownComponent(); - cooldownDropdown.setText(LocaleUtils.getLocaleString("options.attackIndicator", language)); - cooldownDropdown.setOptions(new ArrayList<>()); - cooldownDropdown.addOption(LocaleUtils.getLocaleString("options.attack.crosshair", language), session.getPreferencesCache().getCooldownPreference() == CooldownUtils.CooldownType.TITLE); - cooldownDropdown.addOption(LocaleUtils.getLocaleString("options.attack.hotbar", language), session.getPreferencesCache().getCooldownPreference() == CooldownUtils.CooldownType.ACTIONBAR); - cooldownDropdown.addOption(LocaleUtils.getLocaleString("options.off", language), session.getPreferencesCache().getCooldownPreference() == CooldownUtils.CooldownType.DISABLED); - builder.addComponent(cooldownDropdown); + DropdownComponent.Builder cooldownDropdown = DropdownComponent.builder("options.attackIndicator"); + cooldownDropdown.option("options.attack.crosshair", session.getPreferencesCache().getCooldownPreference() == CooldownUtils.CooldownType.TITLE); + cooldownDropdown.option("options.attack.hotbar", session.getPreferencesCache().getCooldownPreference() == CooldownUtils.CooldownType.ACTIONBAR); + cooldownDropdown.option("options.off", session.getPreferencesCache().getCooldownPreference() == CooldownUtils.CooldownType.DISABLED); + builder.dropdown(cooldownDropdown); } } - if (session.getOpPermissionLevel() >= 2 || session.hasPermission("geyser.settings.server")) { - builder.addComponent(new LabelComponent(LanguageUtils.getPlayerLocaleString("geyser.settings.title.server", language))); + builder.label("geyser.settings.title.server"); - DropdownComponent gamemodeDropdown = new DropdownComponent(); - gamemodeDropdown.setText("%createWorldScreen.gameMode.personal"); - gamemodeDropdown.setOptions(new ArrayList<>()); + DropdownComponent.Builder gamemodeDropdown = DropdownComponent.builder("%createWorldScreen.gameMode.personal"); for (GameMode gamemode : GameMode.values()) { - gamemodeDropdown.addOption(LocaleUtils.getLocaleString("selectWorld.gameMode." + gamemode.name().toLowerCase(), language), session.getGameMode() == gamemode); + gamemodeDropdown.option("selectWorld.gameMode." + gamemode.name().toLowerCase(), session.getGameMode() == gamemode); } - builder.addComponent(gamemodeDropdown); + builder.dropdown(gamemodeDropdown); - DropdownComponent difficultyDropdown = new DropdownComponent(); - difficultyDropdown.setText("%options.difficulty"); - difficultyDropdown.setOptions(new ArrayList<>()); + DropdownComponent.Builder difficultyDropdown = DropdownComponent.builder("%options.difficulty"); for (Difficulty difficulty : Difficulty.values()) { - difficultyDropdown.addOption("%options.difficulty." + difficulty.name().toLowerCase(), session.getWorldCache().getDifficulty() == difficulty); + difficultyDropdown.option("%options.difficulty." + difficulty.name().toLowerCase(), session.getWorldCache().getDifficulty() == difficulty); } - builder.addComponent(difficultyDropdown); + builder.dropdown(difficultyDropdown); } if (session.getOpPermissionLevel() >= 2 || session.hasPermission("geyser.settings.gamerules")) { - builder.addComponent(new LabelComponent(LanguageUtils.getPlayerLocaleString("geyser.settings.title.game_rules", language))); + builder.label("geyser.settings.title.game_rules") + .translator(LocaleUtils::getLocaleString); // we need translate gamerules next + + WorldManager worldManager = GeyserConnector.getInstance().getWorldManager(); for (GameRule gamerule : GameRule.values()) { if (gamerule.equals(GameRule.UNKNOWN)) { continue; @@ -107,89 +97,69 @@ public class SettingsUtils { // Add the relevant form item based on the gamerule type if (Boolean.class.equals(gamerule.getType())) { - builder.addComponent(new ToggleComponent(LocaleUtils.getLocaleString("gamerule." + gamerule.getJavaID(), language), GeyserConnector.getInstance().getWorldManager().getGameRuleBool(session, gamerule))); + builder.toggle("gamerule." + gamerule.getJavaID(), worldManager.getGameRuleBool(session, gamerule)); } else if (Integer.class.equals(gamerule.getType())) { - builder.addComponent(new InputComponent(LocaleUtils.getLocaleString("gamerule." + gamerule.getJavaID(), language), "", String.valueOf(GeyserConnector.getInstance().getWorldManager().getGameRuleInt(session, gamerule)))); + builder.input("gamerule." + gamerule.getJavaID(), "", String.valueOf(worldManager.getGameRuleInt(session, gamerule))); } } } - session.setSettingsForm(builder.build()); - } - - /** - * Handle the settings form response - * - * @param session The session that sent the response - * @param response The response string to parse - * @return True if the form was parsed correctly, false if not - */ - public static boolean handleSettingsForm(GeyserSession session, String response) { - CustomFormWindow settingsForm = session.getSettingsForm(); - settingsForm.setResponse(response); - - CustomFormResponse settingsResponse = (CustomFormResponse) settingsForm.getResponse(); - if (settingsResponse == null) { - return false; - } - int offset = 0; - - if (session.getPreferencesCache().isAllowShowCoordinates() || CooldownUtils.getDefaultShowCooldown() != CooldownUtils.CooldownType.DISABLED) { - offset++; // Client settings title - - // Client can only see its coordinates if reducedDebugInfo is disabled and coordinates are enabled in geyser config. - if (session.getPreferencesCache().isAllowShowCoordinates()) { - session.getPreferencesCache().setPrefersShowCoordinates(settingsResponse.getToggleResponses().get(offset)); - session.getPreferencesCache().updateShowCoordinates(); - offset++; + builder.responseHandler((form, responseData) -> { + CustomFormResponse response = form.parseResponse(responseData); + if (response.isClosed() || response.isInvalid()) { + return; } - if (CooldownUtils.getDefaultShowCooldown() != CooldownUtils.CooldownType.DISABLED) { - CooldownUtils.CooldownType cooldownType = CooldownUtils.CooldownType.VALUES[settingsResponse.getDropdownResponses().get(offset).getElementID()]; - session.getPreferencesCache().setCooldownPreference(cooldownType); - offset++; - } - } + if (session.getPreferencesCache().isAllowShowCoordinates() || CooldownUtils.getDefaultShowCooldown() != CooldownUtils.CooldownType.DISABLED) { + response.skip(); // Client settings title - if (session.getOpPermissionLevel() >= 2 || session.hasPermission("geyser.settings.server")) { - offset++; // Server settings title - - GameMode gameMode = GameMode.values()[settingsResponse.getDropdownResponses().get(offset).getElementID()]; - if (gameMode != null && gameMode != session.getGameMode()) { - session.getConnector().getWorldManager().setPlayerGameMode(session, gameMode); - } - offset++; - - Difficulty difficulty = Difficulty.values()[settingsResponse.getDropdownResponses().get(offset).getElementID()]; - if (difficulty != null && difficulty != session.getWorldCache().getDifficulty()) { - session.getConnector().getWorldManager().setDifficulty(session, difficulty); - } - offset++; - } - - if (session.getOpPermissionLevel() >= 2 || session.hasPermission("geyser.settings.gamerules")) { - offset++; // Game rule title - - for (GameRule gamerule : GameRule.values()) { - if (gamerule.equals(GameRule.UNKNOWN)) { - continue; + // Client can only see its coordinates if reducedDebugInfo is disabled and coordinates are enabled in geyser config. + if (session.getPreferencesCache().isAllowShowCoordinates()) { + session.getPreferencesCache().setPrefersShowCoordinates(response.next()); + session.getPreferencesCache().updateShowCoordinates(); + response.skip(); } - if (Boolean.class.equals(gamerule.getType())) { - boolean value = settingsResponse.getToggleResponses().get(offset); - if (value != session.getConnector().getWorldManager().getGameRuleBool(session, gamerule)) { - session.getConnector().getWorldManager().setGameRule(session, gamerule.getJavaID(), value); + if (CooldownUtils.getDefaultShowCooldown() != CooldownUtils.CooldownType.DISABLED) { + CooldownUtils.CooldownType cooldownType = CooldownUtils.CooldownType.VALUES[(int) response.next()]; + session.getPreferencesCache().setCooldownPreference(cooldownType); + response.skip(); + } + } + + if (session.getOpPermissionLevel() >= 2 || session.hasPermission("geyser.settings.server")) { + GameMode gameMode = GameMode.values()[(int) response.next()]; + if (gameMode != null && gameMode != session.getGameMode()) { + session.getConnector().getWorldManager().setPlayerGameMode(session, gameMode); + } + + Difficulty difficulty = Difficulty.values()[(int) response.next()]; + if (difficulty != null && difficulty != session.getWorldCache().getDifficulty()) { + session.getConnector().getWorldManager().setDifficulty(session, difficulty); + } + } + + if (session.getOpPermissionLevel() >= 2 || session.hasPermission("geyser.settings.gamerules")) { + for (GameRule gamerule : GameRule.values()) { + if (gamerule.equals(GameRule.UNKNOWN)) { + continue; } - } else if (Integer.class.equals(gamerule.getType())) { - int value = Integer.parseInt(settingsResponse.getInputResponses().get(offset)); - if (value != session.getConnector().getWorldManager().getGameRuleInt(session, gamerule)) { - session.getConnector().getWorldManager().setGameRule(session, gamerule.getJavaID(), value); + + if (Boolean.class.equals(gamerule.getType())) { + boolean value = response.next(); + if (value != session.getConnector().getWorldManager().getGameRuleBool(session, gamerule)) { + session.getConnector().getWorldManager().setGameRule(session, gamerule.getJavaID(), value); + } + } else if (Integer.class.equals(gamerule.getType())) { + int value = Integer.parseInt(response.next()); + if (value != session.getConnector().getWorldManager().getGameRuleInt(session, gamerule)) { + session.getConnector().getWorldManager().setGameRule(session, gamerule.getJavaID(), value); + } } } - offset++; } - } + }); - return true; + return builder.build(); } } diff --git a/connector/src/main/java/org/geysermc/connector/utils/StatisticsUtils.java b/connector/src/main/java/org/geysermc/connector/utils/StatisticsUtils.java index 08521284b..e59807d75 100644 --- a/connector/src/main/java/org/geysermc/connector/utils/StatisticsUtils.java +++ b/connector/src/main/java/org/geysermc/connector/utils/StatisticsUtils.java @@ -28,197 +28,171 @@ package org.geysermc.connector.utils; import com.github.steveice10.mc.protocol.data.MagicValues; import com.github.steveice10.mc.protocol.data.game.entity.type.EntityType; import com.github.steveice10.mc.protocol.data.game.statistic.*; -import org.geysermc.common.window.SimpleFormWindow; -import org.geysermc.common.window.button.FormButton; -import org.geysermc.common.window.button.FormImage; -import org.geysermc.common.window.response.SimpleFormResponse; import org.geysermc.connector.network.session.GeyserSession; import org.geysermc.connector.network.translators.item.ItemRegistry; import org.geysermc.connector.network.translators.world.block.BlockTranslator; +import org.geysermc.cumulus.SimpleForm; +import org.geysermc.cumulus.response.SimpleFormResponse; +import org.geysermc.cumulus.util.FormImage; import java.util.Map; +import java.util.regex.Matcher; +import java.util.regex.Pattern; public class StatisticsUtils { - - // Used in UpstreamPacketHandler.java - public static final int STATISTICS_MENU_FORM_ID = 1339; - public static final int STATISTICS_LIST_FORM_ID = 1340; + private static final Pattern CONTENT_PATTERN = Pattern.compile("^\\S+:", Pattern.MULTILINE); /** * Build a form for the given session with all statistic categories * * @param session The session to build the form for */ - public static SimpleFormWindow buildMenuForm(GeyserSession session) { + public static void buildAndSendStatisticsMenu(GeyserSession session) { // Cache the language for cleaner access - String language = session.getClientData().getLanguageCode(); + String language = session.getLocale(); - SimpleFormWindow window = new SimpleFormWindow(LocaleUtils.getLocaleString("gui.stats", language), ""); + session.sendForm( + SimpleForm.builder() + .translator(StatisticsUtils::translate, language) + .title("gui.stats") + .button("stat.generalButton", FormImage.Type.PATH, "textures/ui/World") + .button("stat.itemsButton - stat_type.minecraft.mined", FormImage.Type.PATH, "textures/items/iron_pickaxe") + .button("stat.itemsButton - stat_type.minecraft.broken", FormImage.Type.PATH, "textures/item/record_11") + .button("stat.itemsButton - stat_type.minecraft.crafted", FormImage.Type.PATH, "textures/blocks/crafting_table_side") + .button("stat.itemsButton - stat_type.minecraft.used", FormImage.Type.PATH, "textures/ui/Wrenches1") + .button("stat.itemsButton - stat_type.minecraft.picked_up", FormImage.Type.PATH, "textures/blocks/chest_front") + .button("stat.itemsButton - stat_type.minecraft.dropped", FormImage.Type.PATH, "textures/ui/trash_default") + .button("stat.mobsButton - geyser.statistics.killed", FormImage.Type.PATH, "textures/items/diamon_sword") + .button("stat.mobsButton - geyser.statistics.killed_by", FormImage.Type.PATH, "textures/ui/wither_heart_flash") + .responseHandler((form, responseData) -> { + SimpleFormResponse response = form.parseResponse(responseData); + if (!response.isCorrect()) { + return; + } - window.getButtons().add(new FormButton(LocaleUtils.getLocaleString("stat.generalButton", language), new FormImage(FormImage.FormImageType.PATH, "textures/ui/World"))); + SimpleForm.Builder builder = + SimpleForm.builder() + .translator(StatisticsUtils::translate, language); - window.getButtons().add(new FormButton(LocaleUtils.getLocaleString("stat.itemsButton", language) + " - " + LocaleUtils.getLocaleString("stat_type.minecraft.mined", language), new FormImage(FormImage.FormImageType.PATH, "textures/items/iron_pickaxe"))); - window.getButtons().add(new FormButton(LocaleUtils.getLocaleString("stat.itemsButton", language) + " - " + LocaleUtils.getLocaleString("stat_type.minecraft.broken", language), new FormImage(FormImage.FormImageType.PATH, "textures/items/record_11"))); - window.getButtons().add(new FormButton(LocaleUtils.getLocaleString("stat.itemsButton", language) + " - " + LocaleUtils.getLocaleString("stat_type.minecraft.crafted", language), new FormImage(FormImage.FormImageType.PATH, "textures/blocks/crafting_table_side"))); - window.getButtons().add(new FormButton(LocaleUtils.getLocaleString("stat.itemsButton", language) + " - " + LocaleUtils.getLocaleString("stat_type.minecraft.used", language), new FormImage(FormImage.FormImageType.PATH, "textures/ui/Wrenches1"))); - window.getButtons().add(new FormButton(LocaleUtils.getLocaleString("stat.itemsButton", language) + " - " + LocaleUtils.getLocaleString("stat_type.minecraft.picked_up", language), new FormImage(FormImage.FormImageType.PATH, "textures/blocks/chest_front"))); - window.getButtons().add(new FormButton(LocaleUtils.getLocaleString("stat.itemsButton", language) + " - " + LocaleUtils.getLocaleString("stat_type.minecraft.dropped", language), new FormImage(FormImage.FormImageType.PATH, "textures/ui/trash_default"))); + StringBuilder content = new StringBuilder(); - window.getButtons().add(new FormButton(LocaleUtils.getLocaleString("stat.mobsButton", language) + " - " + LanguageUtils.getPlayerLocaleString("geyser.statistics.killed", language), new FormImage(FormImage.FormImageType.PATH, "textures/items/diamond_sword"))); - window.getButtons().add(new FormButton(LocaleUtils.getLocaleString("stat.mobsButton", language) + " - " + LanguageUtils.getPlayerLocaleString("geyser.statistics.killed_by", language), new FormImage(FormImage.FormImageType.PATH, "textures/ui/wither_heart_flash"))); + switch (response.getClickedButtonId()) { + case 0: + builder.title("stat.generalButton"); - return window; - } + for (Map.Entry entry : session.getStatistics().entrySet()) { + if (entry.getKey() instanceof GenericStatistic) { + String statName = ((GenericStatistic) entry.getKey()).name().toLowerCase(); + content.append("stat.minecraft.").append(statName).append(": ").append(entry.getValue()).append("\n"); + } + } + break; + case 1: + builder.title("stat.itemsButton - stat_type.minecraft.mined"); - /** - * Handle the menu form response - * - * @param session The session that sent the response - * @param response The response string to parse - * @return True if the form was parsed correctly, false if not - */ - public static boolean handleMenuForm(GeyserSession session, String response) { - SimpleFormWindow menuForm = (SimpleFormWindow) session.getWindowCache().getWindows().get(STATISTICS_MENU_FORM_ID); - menuForm.setResponse(response); - SimpleFormResponse formResponse = (SimpleFormResponse) menuForm.getResponse(); + for (Map.Entry entry : session.getStatistics().entrySet()) { + if (entry.getKey() instanceof BreakBlockStatistic) { + String block = BlockTranslator.JAVA_ID_TO_JAVA_IDENTIFIER_MAP.get(((BreakBlockStatistic) entry.getKey()).getId()); + block = block.replace("minecraft:", "block.minecraft."); + content.append(block).append(": ").append(entry.getValue()).append("\n"); + } + } + break; + case 2: + builder.title("stat.itemsButton - stat_type.minecraft.broken"); - // Cache the language for cleaner access - String language = session.getClientData().getLanguageCode(); + for (Map.Entry entry : session.getStatistics().entrySet()) { + if (entry.getKey() instanceof BreakItemStatistic) { + String item = ItemRegistry.ITEM_ENTRIES.get(((BreakItemStatistic) entry.getKey()).getId()).getJavaIdentifier(); + content.append(getItemTranslateKey(item, language)).append(": ").append(entry.getValue()).append("\n"); + } + } + break; + case 3: + builder.title("stat.itemsButton - stat_type.minecraft.crafted"); - if (formResponse != null && formResponse.getClickedButton() != null) { - String title; - StringBuilder content = new StringBuilder(); + for (Map.Entry entry : session.getStatistics().entrySet()) { + if (entry.getKey() instanceof CraftItemStatistic) { + String item = ItemRegistry.ITEM_ENTRIES.get(((CraftItemStatistic) entry.getKey()).getId()).getJavaIdentifier(); + content.append(getItemTranslateKey(item, language)).append(": ").append(entry.getValue()).append("\n"); + } + } + break; + case 4: + builder.title("stat.itemsButton - stat_type.minecraft.used"); - switch (formResponse.getClickedButtonId()) { - case 0: - title = LocaleUtils.getLocaleString("stat.generalButton", language); + for (Map.Entry entry : session.getStatistics().entrySet()) { + if (entry.getKey() instanceof UseItemStatistic) { + String item = ItemRegistry.ITEM_ENTRIES.get(((UseItemStatistic) entry.getKey()).getId()).getJavaIdentifier(); + content.append(getItemTranslateKey(item, language)).append(": ").append(entry.getValue()).append("\n"); + } + } + break; + case 5: + builder.title("stat.itemsButton - stat_type.minecraft.picked_up"); - for (Map.Entry entry : session.getStatistics().entrySet()) { - if (entry.getKey() instanceof GenericStatistic) { - content.append(LocaleUtils.getLocaleString("stat.minecraft." + ((GenericStatistic) entry.getKey()).name().toLowerCase(), language) + ": " + entry.getValue() + "\n"); - } - } - break; - case 1: - title = LocaleUtils.getLocaleString("stat.itemsButton", language) + " - " + LocaleUtils.getLocaleString("stat_type.minecraft.mined", language); + for (Map.Entry entry : session.getStatistics().entrySet()) { + if (entry.getKey() instanceof PickupItemStatistic) { + String item = ItemRegistry.ITEM_ENTRIES.get(((PickupItemStatistic) entry.getKey()).getId()).getJavaIdentifier(); + content.append(getItemTranslateKey(item, language)).append(": ").append(entry.getValue()).append("\n"); + } + } + break; + case 6: + builder.title("stat.itemsButton - stat_type.minecraft.dropped"); - for (Map.Entry entry : session.getStatistics().entrySet()) { - if (entry.getKey() instanceof BreakBlockStatistic) { - String block = BlockTranslator.JAVA_ID_TO_JAVA_IDENTIFIER_MAP.get(((BreakBlockStatistic) entry.getKey()).getId()); - block = block.replace("minecraft:", "block.minecraft."); - block = LocaleUtils.getLocaleString(block, language); - content.append(block + ": " + entry.getValue() + "\n"); - } - } - break; - case 2: - title = LocaleUtils.getLocaleString("stat.itemsButton", language) + " - " + LocaleUtils.getLocaleString("stat_type.minecraft.broken", language); + for (Map.Entry entry : session.getStatistics().entrySet()) { + if (entry.getKey() instanceof DropItemStatistic) { + String item = ItemRegistry.ITEM_ENTRIES.get(((DropItemStatistic) entry.getKey()).getId()).getJavaIdentifier(); + content.append(getItemTranslateKey(item, language)).append(": ").append(entry.getValue()).append("\n"); + } + } + break; + case 7: + builder.title("stat.mobsButton - geyser.statistics.killed"); - for (Map.Entry entry : session.getStatistics().entrySet()) { - if (entry.getKey() instanceof BreakItemStatistic) { - String item = ItemRegistry.ITEM_ENTRIES.get(((BreakItemStatistic) entry.getKey()).getId()).getJavaIdentifier(); - content.append(getItemTranslateKey(item, language) + ": " + entry.getValue() + "\n"); - } - } - break; - case 3: - title = LocaleUtils.getLocaleString("stat.itemsButton", language) + " - " + LocaleUtils.getLocaleString("stat_type.minecraft.crafted", language); + for (Map.Entry entry : session.getStatistics().entrySet()) { + if (entry.getKey() instanceof KillEntityStatistic) { + String entityName = MagicValues.key(EntityType.class, ((KillEntityStatistic) entry.getKey()).getId()).name().toLowerCase(); + content.append("entity.minecraft.").append(entityName).append(": ").append(entry.getValue()).append("\n"); + } + } + break; + case 8: + builder.title("stat.mobsButton - geyser.statistics.killed_by"); - for (Map.Entry entry : session.getStatistics().entrySet()) { - if (entry.getKey() instanceof CraftItemStatistic) { - String item = ItemRegistry.ITEM_ENTRIES.get(((CraftItemStatistic) entry.getKey()).getId()).getJavaIdentifier(); - content.append(getItemTranslateKey(item, language) + ": " + entry.getValue() + "\n"); - } - } - break; - case 4: - title = LocaleUtils.getLocaleString("stat.itemsButton", language) + " - " + LocaleUtils.getLocaleString("stat_type.minecraft.used", language); + for (Map.Entry entry : session + .getStatistics().entrySet()) { + if (entry.getKey() instanceof KilledByEntityStatistic) { + String entityName = MagicValues.key(EntityType.class, ((KilledByEntityStatistic) entry.getKey()).getId()).name().toLowerCase(); + content.append("entity.minecraft.").append(entityName).append(": ").append(entry.getValue()).append("\n"); + } + } + break; + default: + return; + } - for (Map.Entry entry : session.getStatistics().entrySet()) { - if (entry.getKey() instanceof UseItemStatistic) { - String item = ItemRegistry.ITEM_ENTRIES.get(((UseItemStatistic) entry.getKey()).getId()).getJavaIdentifier(); - content.append(getItemTranslateKey(item, language) + ": " + entry.getValue() + "\n"); - } - } - break; - case 5: - title = LocaleUtils.getLocaleString("stat.itemsButton", language) + " - " + LocaleUtils.getLocaleString("stat_type.minecraft.picked_up", language); + if (content.length() == 0) { + content = new StringBuilder("geyser.statistics.none"); + } - for (Map.Entry entry : session.getStatistics().entrySet()) { - if (entry.getKey() instanceof PickupItemStatistic) { - String item = ItemRegistry.ITEM_ENTRIES.get(((PickupItemStatistic) entry.getKey()).getId()).getJavaIdentifier(); - content.append(getItemTranslateKey(item, language) + ": " + entry.getValue() + "\n"); - } - } - break; - case 6: - title = LocaleUtils.getLocaleString("stat.itemsButton", language) + " - " + LocaleUtils.getLocaleString("stat_type.minecraft.dropped", language); - - for (Map.Entry entry : session.getStatistics().entrySet()) { - if (entry.getKey() instanceof DropItemStatistic) { - String item = ItemRegistry.ITEM_ENTRIES.get(((DropItemStatistic) entry.getKey()).getId()).getJavaIdentifier(); - content.append(getItemTranslateKey(item, language) + ": " + entry.getValue() + "\n"); - } - } - break; - case 7: - title = LocaleUtils.getLocaleString("stat.mobsButton", language) + " - " + LanguageUtils.getPlayerLocaleString("geyser.statistics.killed", language); - - for (Map.Entry entry : session.getStatistics().entrySet()) { - if (entry.getKey() instanceof KillEntityStatistic) { - String mob = LocaleUtils.getLocaleString("entity.minecraft." + MagicValues.key(EntityType.class, ((KillEntityStatistic) entry.getKey()).getId()).name().toLowerCase(), language); - content.append(mob + ": " + entry.getValue() + "\n"); - } - } - break; - case 8: - title = LocaleUtils.getLocaleString("stat.mobsButton", language) + " - " + LanguageUtils.getPlayerLocaleString("geyser.statistics.killed_by", language); - - for (Map.Entry entry : session.getStatistics().entrySet()) { - if (entry.getKey() instanceof KilledByEntityStatistic) { - String mob = LocaleUtils.getLocaleString("entity.minecraft." + MagicValues.key(EntityType.class, ((KilledByEntityStatistic) entry.getKey()).getId()).name().toLowerCase(), language); - content.append(mob + ": " + entry.getValue() + "\n"); - } - } - break; - default: - return false; - } - - if (content.length() == 0) { - content = new StringBuilder(LanguageUtils.getPlayerLocaleString("geyser.statistics.none", language)); - } - - SimpleFormWindow window = new SimpleFormWindow(title, content.toString()); - window.getButtons().add(new FormButton(LocaleUtils.getLocaleString("gui.back", language), new FormImage(FormImage.FormImageType.PATH, "textures/gui/newgui/undo"))); - session.sendForm(window, STATISTICS_LIST_FORM_ID); - } - - return true; - } - - /** - * Handle the list form response - * - * @param session The session that sent the response - * @param response The response string to parse - * @return True if the form was parsed correctly, false if not - */ - public static boolean handleListForm(GeyserSession session, String response) { - SimpleFormWindow listForm = (SimpleFormWindow) session.getWindowCache().getWindows().get(STATISTICS_LIST_FORM_ID); - listForm.setResponse(response); - - if (!listForm.isClosed()) { - session.sendForm(buildMenuForm(session), STATISTICS_MENU_FORM_ID); - } - - return true; + session.sendForm( + builder.content(content.toString()) + .button("gui.back", FormImage.Type.PATH, "textures/gui/newgui/undo") + .responseHandler((form1, subFormResponseData) -> { + SimpleFormResponse response1 = form.parseResponse(subFormResponseData); + if (response1.isCorrect()) { + buildAndSendStatisticsMenu(session); + } + })); + })); } /** * Finds the item translation key from the Java locale. - * - * @param item the namespaced item to search for. + * + * @param item the namespaced item to search for. * @param language the language to search in * @return the full name of the item */ @@ -231,4 +205,31 @@ public class StatisticsUtils { } return translatedItem; } + + private static String translate(String keys, String locale) { + Matcher matcher = CONTENT_PATTERN.matcher(keys); + + StringBuffer buffer = new StringBuffer(); + while (matcher.find()) { + String group = matcher.group(); + matcher.appendReplacement(buffer, translateEntry(group.substring(0, group.length() - 1), locale) + ":"); + } + + if (buffer.length() != 0) { + return matcher.appendTail(buffer).toString(); + } + + String[] keySplitted = keys.split(" - "); + for (int i = 0; i < keySplitted.length; i++) { + keySplitted[i] = translateEntry(keySplitted[i], locale); + } + return String.join(" - ", keySplitted); + } + + private static String translateEntry(String key, String locale) { + if (key.startsWith("geyser.")) { + return LanguageUtils.getPlayerLocaleString(key, locale); + } + return LocaleUtils.getLocaleString(key, locale); + } } diff --git a/connector/src/main/java/org/geysermc/connector/utils/WebUtils.java b/connector/src/main/java/org/geysermc/connector/utils/WebUtils.java index ba008da41..cf9f0e1fb 100644 --- a/connector/src/main/java/org/geysermc/connector/utils/WebUtils.java +++ b/connector/src/main/java/org/geysermc/connector/utils/WebUtils.java @@ -45,9 +45,8 @@ public class WebUtils { * @return Body contents or error message if the request fails */ public static String getBody(String reqURL) { - URL url = null; try { - url = new URL(reqURL); + URL url = new URL(reqURL); HttpURLConnection con = (HttpURLConnection) url.openConnection(); con.setRequestMethod("GET"); con.setRequestProperty("User-Agent", "Geyser-" + GeyserConnector.getInstance().getPlatformType().toString() + "/" + GeyserConnector.VERSION); // Otherwise Java 8 fails on checking updates diff --git a/connector/src/main/resources/bedrock/biome_definitions.dat b/connector/src/main/resources/bedrock/biome_definitions.dat index 6d72cc924..8fe3b95a5 100644 Binary files a/connector/src/main/resources/bedrock/biome_definitions.dat and b/connector/src/main/resources/bedrock/biome_definitions.dat differ diff --git a/connector/src/main/resources/bedrock/block_palette.1_17_0.nbt b/connector/src/main/resources/bedrock/block_palette.1_17_0.nbt new file mode 100644 index 000000000..c4f908b5e Binary files /dev/null and b/connector/src/main/resources/bedrock/block_palette.1_17_0.nbt differ diff --git a/connector/src/main/resources/bedrock/blockpalette.1_16_100.nbt b/connector/src/main/resources/bedrock/blockpalette.1_16_100.nbt deleted file mode 100644 index 4513be031..000000000 Binary files a/connector/src/main/resources/bedrock/blockpalette.1_16_100.nbt and /dev/null differ diff --git a/connector/src/main/resources/bedrock/blockpalette.1_16_210.nbt b/connector/src/main/resources/bedrock/blockpalette.1_16_210.nbt deleted file mode 100644 index 178a370ed..000000000 Binary files a/connector/src/main/resources/bedrock/blockpalette.1_16_210.nbt and /dev/null differ diff --git a/connector/src/main/resources/bedrock/creative_items.json b/connector/src/main/resources/bedrock/creative_items.json index bc5bded52..2226e8272 100644 --- a/connector/src/main/resources/bedrock/creative_items.json +++ b/connector/src/main/resources/bedrock/creative_items.json @@ -2,155 +2,171 @@ "items" : [ { "id" : "minecraft:planks", - "blockRuntimeId" : 4812 + "blockRuntimeId" : 5640 }, { "id" : "minecraft:planks", - "blockRuntimeId" : 4813 + "blockRuntimeId" : 5641 }, { "id" : "minecraft:planks", - "blockRuntimeId" : 4814 + "blockRuntimeId" : 5642 }, { "id" : "minecraft:planks", - "blockRuntimeId" : 4815 + "blockRuntimeId" : 5643 }, { "id" : "minecraft:planks", - "blockRuntimeId" : 4816 + "blockRuntimeId" : 5644 }, { "id" : "minecraft:planks", - "blockRuntimeId" : 4817 + "blockRuntimeId" : 5645 }, { "id" : "minecraft:crimson_planks", - "blockRuntimeId" : 3484 + "blockRuntimeId" : 3799 }, { "id" : "minecraft:warped_planks", - "blockRuntimeId" : 6315 + "blockRuntimeId" : 7352 }, { "id" : "minecraft:cobblestone_wall", - "blockRuntimeId" : 967 + "blockRuntimeId" : 1278 }, { "id" : "minecraft:cobblestone_wall", - "blockRuntimeId" : 968 + "blockRuntimeId" : 1279 }, { "id" : "minecraft:cobblestone_wall", - "blockRuntimeId" : 969 + "blockRuntimeId" : 1280 }, { "id" : "minecraft:cobblestone_wall", - "blockRuntimeId" : 970 + "blockRuntimeId" : 1281 }, { "id" : "minecraft:cobblestone_wall", - "blockRuntimeId" : 971 + "blockRuntimeId" : 1282 }, { "id" : "minecraft:cobblestone_wall", - "blockRuntimeId" : 972 + "blockRuntimeId" : 1283 }, { "id" : "minecraft:cobblestone_wall", - "blockRuntimeId" : 979 + "blockRuntimeId" : 1290 }, { "id" : "minecraft:cobblestone_wall", - "blockRuntimeId" : 974 + "blockRuntimeId" : 1285 }, { "id" : "minecraft:cobblestone_wall", - "blockRuntimeId" : 975 + "blockRuntimeId" : 1286 }, { "id" : "minecraft:cobblestone_wall", - "blockRuntimeId" : 973 + "blockRuntimeId" : 1284 }, { "id" : "minecraft:cobblestone_wall", - "blockRuntimeId" : 976 + "blockRuntimeId" : 1287 }, { "id" : "minecraft:cobblestone_wall", - "blockRuntimeId" : 980 + "blockRuntimeId" : 1291 }, { "id" : "minecraft:cobblestone_wall", - "blockRuntimeId" : 977 + "blockRuntimeId" : 1288 }, { "id" : "minecraft:cobblestone_wall", - "blockRuntimeId" : 978 + "blockRuntimeId" : 1289 }, { "id" : "minecraft:blackstone_wall", - "blockRuntimeId" : 449 + "blockRuntimeId" : 497 }, { "id" : "minecraft:polished_blackstone_wall", - "blockRuntimeId" : 5046 + "blockRuntimeId" : 5884 }, { "id" : "minecraft:polished_blackstone_brick_wall", - "blockRuntimeId" : 4843 + "blockRuntimeId" : 5681 + }, + { + "id" : "minecraft:cobbled_deepslate_wall", + "blockRuntimeId" : 1115 + }, + { + "id" : "minecraft:deepslate_tile_wall", + "blockRuntimeId" : 4247 + }, + { + "id" : "minecraft:polished_deepslate_wall", + "blockRuntimeId" : 6059 + }, + { + "id" : "minecraft:deepslate_brick_wall", + "blockRuntimeId" : 4064 }, { "id" : "minecraft:fence", - "blockRuntimeId" : 4018 + "blockRuntimeId" : 4723 }, { "id" : "minecraft:fence", - "blockRuntimeId" : 4019 + "blockRuntimeId" : 4724 }, { "id" : "minecraft:fence", - "blockRuntimeId" : 4020 + "blockRuntimeId" : 4725 }, { "id" : "minecraft:fence", - "blockRuntimeId" : 4021 + "blockRuntimeId" : 4726 }, { "id" : "minecraft:fence", - "blockRuntimeId" : 4022 + "blockRuntimeId" : 4727 }, { "id" : "minecraft:fence", - "blockRuntimeId" : 4023 + "blockRuntimeId" : 4728 }, { "id" : "minecraft:nether_brick_fence", - "blockRuntimeId" : 4738 + "blockRuntimeId" : 5552 }, { "id" : "minecraft:crimson_fence", - "blockRuntimeId" : 3462 + "blockRuntimeId" : 3777 }, { "id" : "minecraft:warped_fence", - "blockRuntimeId" : 6293 + "blockRuntimeId" : 7330 }, { "id" : "minecraft:fence_gate", - "blockRuntimeId" : 4024 + "blockRuntimeId" : 4729 }, { "id" : "minecraft:spruce_fence_gate", - "blockRuntimeId" : 5729 + "blockRuntimeId" : 6764 }, { "id" : "minecraft:birch_fence_gate", - "blockRuntimeId" : 352 + "blockRuntimeId" : 400 }, { "id" : "minecraft:jungle_fence_gate", - "blockRuntimeId" : 4372 + "blockRuntimeId" : 5158 }, { "id" : "minecraft:acacia_fence_gate", @@ -158,43 +174,43 @@ }, { "id" : "minecraft:dark_oak_fence_gate", - "blockRuntimeId" : 3604 + "blockRuntimeId" : 3930 }, { "id" : "minecraft:crimson_fence_gate", - "blockRuntimeId" : 3463 + "blockRuntimeId" : 3778 }, { "id" : "minecraft:warped_fence_gate", - "blockRuntimeId" : 6294 + "blockRuntimeId" : 7331 }, { "id" : "minecraft:normal_stone_stairs", - "blockRuntimeId" : 4757 + "blockRuntimeId" : 5571 }, { "id" : "minecraft:stone_stairs", - "blockRuntimeId" : 6000 + "blockRuntimeId" : 7035 }, { "id" : "minecraft:mossy_cobblestone_stairs", - "blockRuntimeId" : 4719 + "blockRuntimeId" : 5533 }, { "id" : "minecraft:oak_stairs", - "blockRuntimeId" : 4766 + "blockRuntimeId" : 5580 }, { "id" : "minecraft:spruce_stairs", - "blockRuntimeId" : 5761 + "blockRuntimeId" : 6796 }, { "id" : "minecraft:birch_stairs", - "blockRuntimeId" : 384 + "blockRuntimeId" : 432 }, { "id" : "minecraft:jungle_stairs", - "blockRuntimeId" : 4404 + "blockRuntimeId" : 5190 }, { "id" : "minecraft:acacia_stairs", @@ -202,115 +218,163 @@ }, { "id" : "minecraft:dark_oak_stairs", - "blockRuntimeId" : 3636 + "blockRuntimeId" : 3962 }, { "id" : "minecraft:stone_brick_stairs", - "blockRuntimeId" : 5906 + "blockRuntimeId" : 6941 }, { "id" : "minecraft:mossy_stone_brick_stairs", - "blockRuntimeId" : 4727 + "blockRuntimeId" : 5541 }, { "id" : "minecraft:sandstone_stairs", - "blockRuntimeId" : 5516 + "blockRuntimeId" : 6533 }, { "id" : "minecraft:smooth_sandstone_stairs", - "blockRuntimeId" : 5623 + "blockRuntimeId" : 6657 }, { "id" : "minecraft:red_sandstone_stairs", - "blockRuntimeId" : 5443 + "blockRuntimeId" : 6460 }, { "id" : "minecraft:smooth_red_sandstone_stairs", - "blockRuntimeId" : 5615 + "blockRuntimeId" : 6649 }, { "id" : "minecraft:granite_stairs", - "blockRuntimeId" : 4132 + "blockRuntimeId" : 4914 }, { "id" : "minecraft:polished_granite_stairs", - "blockRuntimeId" : 5216 + "blockRuntimeId" : 6229 }, { "id" : "minecraft:diorite_stairs", - "blockRuntimeId" : 3738 + "blockRuntimeId" : 4425 }, { "id" : "minecraft:polished_diorite_stairs", - "blockRuntimeId" : 5208 + "blockRuntimeId" : 6221 }, { "id" : "minecraft:andesite_stairs", - "blockRuntimeId" : 137 + "blockRuntimeId" : 144 }, { "id" : "minecraft:polished_andesite_stairs", - "blockRuntimeId" : 4819 + "blockRuntimeId" : 5657 }, { "id" : "minecraft:brick_stairs", - "blockRuntimeId" : 808 + "blockRuntimeId" : 856 }, { "id" : "minecraft:nether_brick_stairs", - "blockRuntimeId" : 4739 + "blockRuntimeId" : 5553 }, { "id" : "minecraft:red_nether_brick_stairs", - "blockRuntimeId" : 5431 + "blockRuntimeId" : 6448 }, { "id" : "minecraft:end_brick_stairs", - "blockRuntimeId" : 3978 + "blockRuntimeId" : 4669 }, { "id" : "minecraft:quartz_stairs", - "blockRuntimeId" : 5378 + "blockRuntimeId" : 6392 }, { "id" : "minecraft:smooth_quartz_stairs", - "blockRuntimeId" : 5607 + "blockRuntimeId" : 6641 }, { "id" : "minecraft:purpur_stairs", - "blockRuntimeId" : 5356 + "blockRuntimeId" : 6370 }, { "id" : "minecraft:prismarine_stairs", - "blockRuntimeId" : 5278 + "blockRuntimeId" : 6292 }, { "id" : "minecraft:dark_prismarine_stairs", - "blockRuntimeId" : 3660 + "blockRuntimeId" : 3986 }, { "id" : "minecraft:prismarine_bricks_stairs", - "blockRuntimeId" : 5270 + "blockRuntimeId" : 6284 }, { "id" : "minecraft:crimson_stairs", - "blockRuntimeId" : 3504 + "blockRuntimeId" : 3819 }, { "id" : "minecraft:warped_stairs", - "blockRuntimeId" : 6335 + "blockRuntimeId" : 7372 }, { "id" : "minecraft:blackstone_stairs", - "blockRuntimeId" : 441 + "blockRuntimeId" : 489 }, { "id" : "minecraft:polished_blackstone_stairs", - "blockRuntimeId" : 5038 + "blockRuntimeId" : 5876 }, { "id" : "minecraft:polished_blackstone_brick_stairs", - "blockRuntimeId" : 4835 + "blockRuntimeId" : 5673 + }, + { + "id" : "minecraft:cut_copper_stairs", + "blockRuntimeId" : 3872 + }, + { + "id" : "minecraft:exposed_cut_copper_stairs", + "blockRuntimeId" : 4705 + }, + { + "id" : "minecraft:weathered_cut_copper_stairs", + "blockRuntimeId" : 7499 + }, + { + "id" : "minecraft:oxidized_cut_copper_stairs", + "blockRuntimeId" : 5611 + }, + { + "id" : "minecraft:waxed_cut_copper_stairs", + "blockRuntimeId" : 7443 + }, + { + "id" : "minecraft:waxed_exposed_cut_copper_stairs", + "blockRuntimeId" : 7457 + }, + { + "id" : "minecraft:waxed_weathered_cut_copper_stairs", + "blockRuntimeId" : 7485 + }, + { + "id" : "minecraft:waxed_oxidized_cut_copper_stairs", + "blockRuntimeId" : 7471 + }, + { + "id" : "minecraft:cobbled_deepslate_stairs", + "blockRuntimeId" : 1107 + }, + { + "id" : "minecraft:deepslate_tile_stairs", + "blockRuntimeId" : 4239 + }, + { + "id" : "minecraft:polished_deepslate_stairs", + "blockRuntimeId" : 6051 + }, + { + "id" : "minecraft:deepslate_brick_stairs", + "blockRuntimeId" : 4056 }, { "id" : "minecraft:wooden_door" @@ -341,19 +405,19 @@ }, { "id" : "minecraft:trapdoor", - "blockRuntimeId" : 6081 + "blockRuntimeId" : 7117 }, { "id" : "minecraft:spruce_trapdoor", - "blockRuntimeId" : 5785 + "blockRuntimeId" : 6820 }, { "id" : "minecraft:birch_trapdoor", - "blockRuntimeId" : 408 + "blockRuntimeId" : 456 }, { "id" : "minecraft:jungle_trapdoor", - "blockRuntimeId" : 4428 + "blockRuntimeId" : 5214 }, { "id" : "minecraft:acacia_trapdoor", @@ -361,1243 +425,1451 @@ }, { "id" : "minecraft:dark_oak_trapdoor", - "blockRuntimeId" : 3644 + "blockRuntimeId" : 3970 }, { "id" : "minecraft:iron_trapdoor", - "blockRuntimeId" : 4287 + "blockRuntimeId" : 5073 }, { "id" : "minecraft:crimson_trapdoor", - "blockRuntimeId" : 3531 + "blockRuntimeId" : 3846 }, { "id" : "minecraft:warped_trapdoor", - "blockRuntimeId" : 6362 + "blockRuntimeId" : 7399 }, { "id" : "minecraft:iron_bars", - "blockRuntimeId" : 4252 + "blockRuntimeId" : 5038 }, { "id" : "minecraft:glass", - "blockRuntimeId" : 4114 + "blockRuntimeId" : 4820 }, { "id" : "minecraft:stained_glass", - "blockRuntimeId" : 5807 + "blockRuntimeId" : 6842 }, { "id" : "minecraft:stained_glass", - "blockRuntimeId" : 5815 + "blockRuntimeId" : 6850 }, { "id" : "minecraft:stained_glass", - "blockRuntimeId" : 5814 + "blockRuntimeId" : 6849 }, { "id" : "minecraft:stained_glass", - "blockRuntimeId" : 5822 + "blockRuntimeId" : 6857 }, { "id" : "minecraft:stained_glass", - "blockRuntimeId" : 5819 + "blockRuntimeId" : 6854 }, { "id" : "minecraft:stained_glass", - "blockRuntimeId" : 5821 + "blockRuntimeId" : 6856 }, { "id" : "minecraft:stained_glass", - "blockRuntimeId" : 5808 + "blockRuntimeId" : 6843 }, { "id" : "minecraft:stained_glass", - "blockRuntimeId" : 5811 + "blockRuntimeId" : 6846 }, { "id" : "minecraft:stained_glass", - "blockRuntimeId" : 5812 + "blockRuntimeId" : 6847 }, { "id" : "minecraft:stained_glass", - "blockRuntimeId" : 5820 + "blockRuntimeId" : 6855 }, { "id" : "minecraft:stained_glass", - "blockRuntimeId" : 5816 + "blockRuntimeId" : 6851 }, { "id" : "minecraft:stained_glass", - "blockRuntimeId" : 5810 + "blockRuntimeId" : 6845 }, { "id" : "minecraft:stained_glass", - "blockRuntimeId" : 5818 + "blockRuntimeId" : 6853 }, { "id" : "minecraft:stained_glass", - "blockRuntimeId" : 5817 + "blockRuntimeId" : 6852 }, { "id" : "minecraft:stained_glass", - "blockRuntimeId" : 5809 + "blockRuntimeId" : 6844 }, { "id" : "minecraft:stained_glass", - "blockRuntimeId" : 5813 + "blockRuntimeId" : 6848 + }, + { + "id" : "minecraft:tinted_glass", + "blockRuntimeId" : 7106 }, { "id" : "minecraft:glass_pane", - "blockRuntimeId" : 4115 + "blockRuntimeId" : 4821 }, { "id" : "minecraft:stained_glass_pane", - "blockRuntimeId" : 5823 + "blockRuntimeId" : 6858 }, { "id" : "minecraft:stained_glass_pane", - "blockRuntimeId" : 5831 + "blockRuntimeId" : 6866 }, { "id" : "minecraft:stained_glass_pane", - "blockRuntimeId" : 5830 + "blockRuntimeId" : 6865 }, { "id" : "minecraft:stained_glass_pane", - "blockRuntimeId" : 5838 + "blockRuntimeId" : 6873 }, { "id" : "minecraft:stained_glass_pane", - "blockRuntimeId" : 5835 + "blockRuntimeId" : 6870 }, { "id" : "minecraft:stained_glass_pane", - "blockRuntimeId" : 5837 + "blockRuntimeId" : 6872 }, { "id" : "minecraft:stained_glass_pane", - "blockRuntimeId" : 5824 + "blockRuntimeId" : 6859 }, { "id" : "minecraft:stained_glass_pane", - "blockRuntimeId" : 5827 + "blockRuntimeId" : 6862 }, { "id" : "minecraft:stained_glass_pane", - "blockRuntimeId" : 5828 + "blockRuntimeId" : 6863 }, { "id" : "minecraft:stained_glass_pane", - "blockRuntimeId" : 5836 + "blockRuntimeId" : 6871 }, { "id" : "minecraft:stained_glass_pane", - "blockRuntimeId" : 5832 + "blockRuntimeId" : 6867 }, { "id" : "minecraft:stained_glass_pane", - "blockRuntimeId" : 5826 + "blockRuntimeId" : 6861 }, { "id" : "minecraft:stained_glass_pane", - "blockRuntimeId" : 5834 + "blockRuntimeId" : 6869 }, { "id" : "minecraft:stained_glass_pane", - "blockRuntimeId" : 5833 + "blockRuntimeId" : 6868 }, { "id" : "minecraft:stained_glass_pane", - "blockRuntimeId" : 5825 + "blockRuntimeId" : 6860 }, { "id" : "minecraft:stained_glass_pane", - "blockRuntimeId" : 5829 + "blockRuntimeId" : 6864 }, { "id" : "minecraft:ladder", - "blockRuntimeId" : 4476 + "blockRuntimeId" : 5262 }, { "id" : "minecraft:scaffolding", - "blockRuntimeId" : 5536 - }, - { - "id" : "minecraft:double_stone_slab", - "blockRuntimeId" : 5942 - }, - { - "id" : "minecraft:double_stone_slab4", - "blockRuntimeId" : 5992 - }, - { - "id" : "minecraft:double_stone_slab", - "blockRuntimeId" : 5945 - }, - { - "id" : "minecraft:double_stone_slab2", - "blockRuntimeId" : 5963 - }, - { - "id" : "minecraft:wooden_slab", - "blockRuntimeId" : 6540 - }, - { - "id" : "minecraft:wooden_slab", - "blockRuntimeId" : 6541 - }, - { - "id" : "minecraft:wooden_slab", - "blockRuntimeId" : 6542 - }, - { - "id" : "minecraft:wooden_slab", - "blockRuntimeId" : 6543 - }, - { - "id" : "minecraft:wooden_slab", - "blockRuntimeId" : 6544 - }, - { - "id" : "minecraft:wooden_slab", - "blockRuntimeId" : 6545 - }, - { - "id" : "minecraft:double_stone_slab", - "blockRuntimeId" : 5947 - }, - { - "id" : "minecraft:double_stone_slab4", - "blockRuntimeId" : 5990 - }, - { - "id" : "minecraft:double_stone_slab", - "blockRuntimeId" : 5943 - }, - { - "id" : "minecraft:double_stone_slab4", - "blockRuntimeId" : 5993 - }, - { - "id" : "minecraft:double_stone_slab2", - "blockRuntimeId" : 5964 - }, - { - "id" : "minecraft:double_stone_slab2", - "blockRuntimeId" : 5958 - }, - { - "id" : "minecraft:double_stone_slab4", - "blockRuntimeId" : 5994 - }, - { - "id" : "minecraft:double_stone_slab3", - "blockRuntimeId" : 5975 - }, - { - "id" : "minecraft:double_stone_slab3", - "blockRuntimeId" : 5980 - }, - { - "id" : "minecraft:double_stone_slab3", - "blockRuntimeId" : 5981 - }, - { - "id" : "minecraft:double_stone_slab3", - "blockRuntimeId" : 5978 - }, - { - "id" : "minecraft:double_stone_slab3", - "blockRuntimeId" : 5979 - }, - { - "id" : "minecraft:double_stone_slab3", - "blockRuntimeId" : 5977 - }, - { - "id" : "minecraft:double_stone_slab3", - "blockRuntimeId" : 5976 - }, - { - "id" : "minecraft:double_stone_slab", - "blockRuntimeId" : 5946 - }, - { - "id" : "minecraft:double_stone_slab", - "blockRuntimeId" : 5949 - }, - { - "id" : "minecraft:double_stone_slab2", - "blockRuntimeId" : 5965 - }, - { - "id" : "minecraft:double_stone_slab3", - "blockRuntimeId" : 5974 - }, - { - "id" : "minecraft:double_stone_slab", - "blockRuntimeId" : 5948 - }, - { - "id" : "minecraft:double_stone_slab4", - "blockRuntimeId" : 5991 - }, - { - "id" : "minecraft:double_stone_slab2", - "blockRuntimeId" : 5959 - }, - { - "id" : "minecraft:double_stone_slab2", - "blockRuntimeId" : 5960 - }, - { - "id" : "minecraft:double_stone_slab2", - "blockRuntimeId" : 5961 - }, - { - "id" : "minecraft:double_stone_slab2", - "blockRuntimeId" : 5962 - }, - { - "id" : "minecraft:crimson_slab", - "blockRuntimeId" : 3502 - }, - { - "id" : "minecraft:warped_slab", - "blockRuntimeId" : 6333 - }, - { - "id" : "minecraft:blackstone_slab", - "blockRuntimeId" : 439 - }, - { - "id" : "minecraft:polished_blackstone_slab", - "blockRuntimeId" : 5036 - }, - { - "id" : "minecraft:polished_blackstone_brick_slab", - "blockRuntimeId" : 4833 - }, - { - "id" : "minecraft:brick_block", - "blockRuntimeId" : 807 - }, - { - "id" : "minecraft:chiseled_nether_bricks", - "blockRuntimeId" : 954 - }, - { - "id" : "minecraft:cracked_nether_bricks", - "blockRuntimeId" : 3413 - }, - { - "id" : "minecraft:quartz_bricks", - "blockRuntimeId" : 5376 - }, - { - "id" : "minecraft:stonebrick", - "blockRuntimeId" : 6008 - }, - { - "id" : "minecraft:stonebrick", - "blockRuntimeId" : 6009 - }, - { - "id" : "minecraft:stonebrick", - "blockRuntimeId" : 6010 - }, - { - "id" : "minecraft:stonebrick", - "blockRuntimeId" : 6011 - }, - { - "id" : "minecraft:end_bricks", - "blockRuntimeId" : 3986 - }, - { - "id" : "minecraft:prismarine", - "blockRuntimeId" : 5269 - }, - { - "id" : "minecraft:polished_blackstone_bricks", - "blockRuntimeId" : 5005 - }, - { - "id" : "minecraft:cracked_polished_blackstone_bricks", - "blockRuntimeId" : 3414 - }, - { - "id" : "minecraft:gilded_blackstone", - "blockRuntimeId" : 4113 - }, - { - "id" : "minecraft:chiseled_polished_blackstone", - "blockRuntimeId" : 955 - }, - { - "id" : "minecraft:cobblestone", - "blockRuntimeId" : 966 - }, - { - "id" : "minecraft:mossy_cobblestone", - "blockRuntimeId" : 4718 - }, - { - "id" : "minecraft:smooth_stone", - "blockRuntimeId" : 5631 - }, - { - "id" : "minecraft:sandstone", - "blockRuntimeId" : 5512 - }, - { - "id" : "minecraft:sandstone", - "blockRuntimeId" : 5513 - }, - { - "id" : "minecraft:sandstone", - "blockRuntimeId" : 5514 - }, - { - "id" : "minecraft:sandstone", - "blockRuntimeId" : 5515 - }, - { - "id" : "minecraft:red_sandstone", - "blockRuntimeId" : 5439 - }, - { - "id" : "minecraft:red_sandstone", - "blockRuntimeId" : 5440 - }, - { - "id" : "minecraft:red_sandstone", - "blockRuntimeId" : 5441 - }, - { - "id" : "minecraft:red_sandstone", - "blockRuntimeId" : 5442 - }, - { - "id" : "minecraft:coal_block", - "blockRuntimeId" : 964 - }, - { - "id" : "minecraft:dried_kelp_block", - "blockRuntimeId" : 3843 - }, - { - "id" : "minecraft:gold_block", - "blockRuntimeId" : 4118 - }, - { - "id" : "minecraft:iron_block", - "blockRuntimeId" : 4253 - }, - { - "id" : "minecraft:emerald_block", - "blockRuntimeId" : 3975 - }, - { - "id" : "minecraft:diamond_block", - "blockRuntimeId" : 3736 - }, - { - "id" : "minecraft:lapis_block", - "blockRuntimeId" : 4484 - }, - { - "id" : "minecraft:quartz_block", - "blockRuntimeId" : 5364 - }, - { - "id" : "minecraft:quartz_block", - "blockRuntimeId" : 5366 - }, - { - "id" : "minecraft:quartz_block", - "blockRuntimeId" : 5365 - }, - { - "id" : "minecraft:quartz_block", - "blockRuntimeId" : 5367 - }, - { - "id" : "minecraft:prismarine", - "blockRuntimeId" : 5267 - }, - { - "id" : "minecraft:prismarine", - "blockRuntimeId" : 5268 - }, - { - "id" : "minecraft:slime", - "blockRuntimeId" : 5599 - }, - { - "id" : "minecraft:honey_block", - "blockRuntimeId" : 4234 - }, - { - "id" : "minecraft:honeycomb_block", - "blockRuntimeId" : 4235 - }, - { - "id" : "minecraft:hay_block", - "blockRuntimeId" : 4206 - }, - { - "id" : "minecraft:bone_block", - "blockRuntimeId" : 624 - }, - { - "id" : "minecraft:nether_brick", - "blockRuntimeId" : 4737 - }, - { - "id" : "minecraft:red_nether_brick", - "blockRuntimeId" : 5430 - }, - { - "id" : "minecraft:netherite_block", - "blockRuntimeId" : 4754 - }, - { - "id" : "minecraft:lodestone", - "blockRuntimeId" : 4632 - }, - { - "id" : "minecraft:wool", - "blockRuntimeId" : 6552 - }, - { - "id" : "minecraft:wool", - "blockRuntimeId" : 6560 - }, - { - "id" : "minecraft:wool", - "blockRuntimeId" : 6559 - }, - { - "id" : "minecraft:wool", - "blockRuntimeId" : 6567 - }, - { - "id" : "minecraft:wool", - "blockRuntimeId" : 6564 - }, - { - "id" : "minecraft:wool", - "blockRuntimeId" : 6566 - }, - { - "id" : "minecraft:wool", "blockRuntimeId" : 6553 }, { - "id" : "minecraft:wool", - "blockRuntimeId" : 6556 + "id" : "minecraft:double_stone_slab", + "blockRuntimeId" : 6977 }, { - "id" : "minecraft:wool", - "blockRuntimeId" : 6557 + "id" : "minecraft:double_stone_slab4", + "blockRuntimeId" : 7027 }, { - "id" : "minecraft:wool", - "blockRuntimeId" : 6565 + "id" : "minecraft:double_stone_slab", + "blockRuntimeId" : 6980 }, { - "id" : "minecraft:wool", - "blockRuntimeId" : 6561 + "id" : "minecraft:double_stone_slab2", + "blockRuntimeId" : 6998 }, { - "id" : "minecraft:wool", - "blockRuntimeId" : 6555 + "id" : "minecraft:wooden_slab", + "blockRuntimeId" : 7647 }, { - "id" : "minecraft:wool", - "blockRuntimeId" : 6563 + "id" : "minecraft:wooden_slab", + "blockRuntimeId" : 7648 }, { - "id" : "minecraft:wool", - "blockRuntimeId" : 6562 + "id" : "minecraft:wooden_slab", + "blockRuntimeId" : 7649 }, { - "id" : "minecraft:wool", - "blockRuntimeId" : 6554 + "id" : "minecraft:wooden_slab", + "blockRuntimeId" : 7650 }, { - "id" : "minecraft:wool", - "blockRuntimeId" : 6558 + "id" : "minecraft:wooden_slab", + "blockRuntimeId" : 7651 }, { - "id" : "minecraft:carpet", - "blockRuntimeId" : 873 + "id" : "minecraft:wooden_slab", + "blockRuntimeId" : 7652 }, { - "id" : "minecraft:carpet", - "blockRuntimeId" : 881 + "id" : "minecraft:double_stone_slab", + "blockRuntimeId" : 6982 }, { - "id" : "minecraft:carpet", - "blockRuntimeId" : 880 + "id" : "minecraft:double_stone_slab4", + "blockRuntimeId" : 7025 }, { - "id" : "minecraft:carpet", - "blockRuntimeId" : 888 + "id" : "minecraft:double_stone_slab", + "blockRuntimeId" : 6978 }, { - "id" : "minecraft:carpet", - "blockRuntimeId" : 885 + "id" : "minecraft:double_stone_slab4", + "blockRuntimeId" : 7028 }, { - "id" : "minecraft:carpet", - "blockRuntimeId" : 887 + "id" : "minecraft:double_stone_slab2", + "blockRuntimeId" : 6999 }, { - "id" : "minecraft:carpet", - "blockRuntimeId" : 874 + "id" : "minecraft:double_stone_slab2", + "blockRuntimeId" : 6993 }, { - "id" : "minecraft:carpet", - "blockRuntimeId" : 877 + "id" : "minecraft:double_stone_slab4", + "blockRuntimeId" : 7029 }, { - "id" : "minecraft:carpet", - "blockRuntimeId" : 878 + "id" : "minecraft:double_stone_slab3", + "blockRuntimeId" : 7010 }, { - "id" : "minecraft:carpet", - "blockRuntimeId" : 886 + "id" : "minecraft:double_stone_slab3", + "blockRuntimeId" : 7015 }, { - "id" : "minecraft:carpet", - "blockRuntimeId" : 882 + "id" : "minecraft:double_stone_slab3", + "blockRuntimeId" : 7016 }, { - "id" : "minecraft:carpet", - "blockRuntimeId" : 876 + "id" : "minecraft:double_stone_slab3", + "blockRuntimeId" : 7013 }, { - "id" : "minecraft:carpet", - "blockRuntimeId" : 884 + "id" : "minecraft:double_stone_slab3", + "blockRuntimeId" : 7014 }, { - "id" : "minecraft:carpet", - "blockRuntimeId" : 883 + "id" : "minecraft:double_stone_slab3", + "blockRuntimeId" : 7012 }, { - "id" : "minecraft:carpet", - "blockRuntimeId" : 875 + "id" : "minecraft:double_stone_slab3", + "blockRuntimeId" : 7011 }, { - "id" : "minecraft:carpet", - "blockRuntimeId" : 879 + "id" : "minecraft:double_stone_slab", + "blockRuntimeId" : 6981 }, { - "id" : "minecraft:concrete_powder", - "blockRuntimeId" : 3308 + "id" : "minecraft:double_stone_slab", + "blockRuntimeId" : 6984 }, { - "id" : "minecraft:concrete_powder", - "blockRuntimeId" : 3316 + "id" : "minecraft:double_stone_slab2", + "blockRuntimeId" : 7000 }, { - "id" : "minecraft:concrete_powder", - "blockRuntimeId" : 3315 + "id" : "minecraft:double_stone_slab3", + "blockRuntimeId" : 7009 }, { - "id" : "minecraft:concrete_powder", - "blockRuntimeId" : 3323 + "id" : "minecraft:double_stone_slab", + "blockRuntimeId" : 6983 }, { - "id" : "minecraft:concrete_powder", - "blockRuntimeId" : 3320 + "id" : "minecraft:double_stone_slab4", + "blockRuntimeId" : 7026 }, { - "id" : "minecraft:concrete_powder", - "blockRuntimeId" : 3322 + "id" : "minecraft:double_stone_slab2", + "blockRuntimeId" : 6994 }, { - "id" : "minecraft:concrete_powder", - "blockRuntimeId" : 3309 + "id" : "minecraft:double_stone_slab2", + "blockRuntimeId" : 6995 }, { - "id" : "minecraft:concrete_powder", - "blockRuntimeId" : 3312 + "id" : "minecraft:double_stone_slab2", + "blockRuntimeId" : 6996 }, { - "id" : "minecraft:concrete_powder", - "blockRuntimeId" : 3313 + "id" : "minecraft:double_stone_slab2", + "blockRuntimeId" : 6997 }, { - "id" : "minecraft:concrete_powder", - "blockRuntimeId" : 3321 + "id" : "minecraft:crimson_slab", + "blockRuntimeId" : 3817 }, { - "id" : "minecraft:concrete_powder", - "blockRuntimeId" : 3317 + "id" : "minecraft:warped_slab", + "blockRuntimeId" : 7370 }, { - "id" : "minecraft:concrete_powder", - "blockRuntimeId" : 3311 + "id" : "minecraft:blackstone_slab", + "blockRuntimeId" : 487 }, { - "id" : "minecraft:concrete_powder", - "blockRuntimeId" : 3319 + "id" : "minecraft:polished_blackstone_slab", + "blockRuntimeId" : 5874 }, { - "id" : "minecraft:concrete_powder", - "blockRuntimeId" : 3318 + "id" : "minecraft:polished_blackstone_brick_slab", + "blockRuntimeId" : 5671 }, { - "id" : "minecraft:concrete_powder", - "blockRuntimeId" : 3310 + "id" : "minecraft:cut_copper_slab", + "blockRuntimeId" : 3870 }, { - "id" : "minecraft:concrete_powder", - "blockRuntimeId" : 3314 + "id" : "minecraft:exposed_cut_copper_slab", + "blockRuntimeId" : 4703 }, { - "id" : "minecraft:concrete", - "blockRuntimeId" : 3292 + "id" : "minecraft:weathered_cut_copper_slab", + "blockRuntimeId" : 7497 }, { - "id" : "minecraft:concrete", - "blockRuntimeId" : 3300 + "id" : "minecraft:oxidized_cut_copper_slab", + "blockRuntimeId" : 5609 }, { - "id" : "minecraft:concrete", - "blockRuntimeId" : 3299 + "id" : "minecraft:waxed_cut_copper_slab", + "blockRuntimeId" : 7441 }, { - "id" : "minecraft:concrete", - "blockRuntimeId" : 3307 + "id" : "minecraft:waxed_exposed_cut_copper_slab", + "blockRuntimeId" : 7455 }, { - "id" : "minecraft:concrete", - "blockRuntimeId" : 3304 + "id" : "minecraft:waxed_weathered_cut_copper_slab", + "blockRuntimeId" : 7483 }, { - "id" : "minecraft:concrete", - "blockRuntimeId" : 3306 + "id" : "minecraft:waxed_oxidized_cut_copper_slab", + "blockRuntimeId" : 7469 }, { - "id" : "minecraft:concrete", - "blockRuntimeId" : 3293 + "id" : "minecraft:cobbled_deepslate_slab", + "blockRuntimeId" : 1105 }, { - "id" : "minecraft:concrete", - "blockRuntimeId" : 3296 + "id" : "minecraft:polished_deepslate_slab", + "blockRuntimeId" : 6049 }, { - "id" : "minecraft:concrete", - "blockRuntimeId" : 3297 + "id" : "minecraft:deepslate_tile_slab", + "blockRuntimeId" : 4237 }, { - "id" : "minecraft:concrete", - "blockRuntimeId" : 3305 + "id" : "minecraft:deepslate_brick_slab", + "blockRuntimeId" : 4054 }, { - "id" : "minecraft:concrete", - "blockRuntimeId" : 3301 + "id" : "minecraft:brick_block", + "blockRuntimeId" : 855 }, { - "id" : "minecraft:concrete", - "blockRuntimeId" : 3295 + "id" : "minecraft:chiseled_nether_bricks", + "blockRuntimeId" : 1090 }, { - "id" : "minecraft:concrete", - "blockRuntimeId" : 3303 + "id" : "minecraft:cracked_nether_bricks", + "blockRuntimeId" : 3728 }, { - "id" : "minecraft:concrete", - "blockRuntimeId" : 3302 + "id" : "minecraft:quartz_bricks", + "blockRuntimeId" : 6390 }, { - "id" : "minecraft:concrete", - "blockRuntimeId" : 3294 + "id" : "minecraft:stonebrick", + "blockRuntimeId" : 7043 }, { - "id" : "minecraft:concrete", - "blockRuntimeId" : 3298 + "id" : "minecraft:stonebrick", + "blockRuntimeId" : 7044 }, { - "id" : "minecraft:clay", - "blockRuntimeId" : 963 + "id" : "minecraft:stonebrick", + "blockRuntimeId" : 7045 }, { - "id" : "minecraft:hardened_clay", - "blockRuntimeId" : 4205 + "id" : "minecraft:stonebrick", + "blockRuntimeId" : 7046 }, { - "id" : "minecraft:stained_hardened_clay", - "blockRuntimeId" : 5839 + "id" : "minecraft:end_bricks", + "blockRuntimeId" : 4677 }, { - "id" : "minecraft:stained_hardened_clay", - "blockRuntimeId" : 5847 + "id" : "minecraft:prismarine", + "blockRuntimeId" : 6283 }, { - "id" : "minecraft:stained_hardened_clay", - "blockRuntimeId" : 5846 - }, - { - "id" : "minecraft:stained_hardened_clay", - "blockRuntimeId" : 5854 - }, - { - "id" : "minecraft:stained_hardened_clay", - "blockRuntimeId" : 5851 - }, - { - "id" : "minecraft:stained_hardened_clay", - "blockRuntimeId" : 5853 - }, - { - "id" : "minecraft:stained_hardened_clay", - "blockRuntimeId" : 5840 - }, - { - "id" : "minecraft:stained_hardened_clay", + "id" : "minecraft:polished_blackstone_bricks", "blockRuntimeId" : 5843 }, { - "id" : "minecraft:stained_hardened_clay", - "blockRuntimeId" : 5844 + "id" : "minecraft:cracked_polished_blackstone_bricks", + "blockRuntimeId" : 3729 }, { - "id" : "minecraft:stained_hardened_clay", - "blockRuntimeId" : 5852 + "id" : "minecraft:gilded_blackstone", + "blockRuntimeId" : 4819 }, { - "id" : "minecraft:stained_hardened_clay", - "blockRuntimeId" : 5848 + "id" : "minecraft:chiseled_polished_blackstone", + "blockRuntimeId" : 1091 }, { - "id" : "minecraft:stained_hardened_clay", - "blockRuntimeId" : 5842 + "id" : "minecraft:deepslate_tiles", + "blockRuntimeId" : 4409 }, { - "id" : "minecraft:stained_hardened_clay", - "blockRuntimeId" : 5850 + "id" : "minecraft:cracked_deepslate_tiles", + "blockRuntimeId" : 3727 }, { - "id" : "minecraft:stained_hardened_clay", - "blockRuntimeId" : 5849 + "id" : "minecraft:deepslate_bricks", + "blockRuntimeId" : 4226 }, { - "id" : "minecraft:stained_hardened_clay", - "blockRuntimeId" : 5841 + "id" : "minecraft:cracked_deepslate_bricks", + "blockRuntimeId" : 3726 }, { - "id" : "minecraft:stained_hardened_clay", - "blockRuntimeId" : 5845 + "id" : "minecraft:chiseled_deepslate", + "blockRuntimeId" : 1089 }, { - "id" : "minecraft:white_glazed_terracotta", - "blockRuntimeId" : 6437 + "id" : "minecraft:cobblestone", + "blockRuntimeId" : 1277 }, { - "id" : "minecraft:silver_glazed_terracotta", - "blockRuntimeId" : 5581 + "id" : "minecraft:mossy_cobblestone", + "blockRuntimeId" : 5532 }, { - "id" : "minecraft:gray_glazed_terracotta", - "blockRuntimeId" : 4143 + "id" : "minecraft:cobbled_deepslate", + "blockRuntimeId" : 1102 }, { - "id" : "minecraft:black_glazed_terracotta", - "blockRuntimeId" : 430 + "id" : "minecraft:smooth_stone", + "blockRuntimeId" : 6665 }, { - "id" : "minecraft:brown_glazed_terracotta", - "blockRuntimeId" : 816 + "id" : "minecraft:sandstone", + "blockRuntimeId" : 6529 }, { - "id" : "minecraft:red_glazed_terracotta", - "blockRuntimeId" : 5407 + "id" : "minecraft:sandstone", + "blockRuntimeId" : 6530 }, { - "id" : "minecraft:orange_glazed_terracotta", - "blockRuntimeId" : 4787 + "id" : "minecraft:sandstone", + "blockRuntimeId" : 6531 }, { - "id" : "minecraft:yellow_glazed_terracotta", - "blockRuntimeId" : 6569 + "id" : "minecraft:sandstone", + "blockRuntimeId" : 6532 }, { - "id" : "minecraft:lime_glazed_terracotta", - "blockRuntimeId" : 4602 + "id" : "minecraft:red_sandstone", + "blockRuntimeId" : 6456 }, { - "id" : "minecraft:green_glazed_terracotta", - "blockRuntimeId" : 4149 + "id" : "minecraft:red_sandstone", + "blockRuntimeId" : 6457 }, { - "id" : "minecraft:cyan_glazed_terracotta", - "blockRuntimeId" : 3554 + "id" : "minecraft:red_sandstone", + "blockRuntimeId" : 6458 }, { - "id" : "minecraft:light_blue_glazed_terracotta", - "blockRuntimeId" : 4580 + "id" : "minecraft:red_sandstone", + "blockRuntimeId" : 6459 }, { - "id" : "minecraft:blue_glazed_terracotta", - "blockRuntimeId" : 617 + "id" : "minecraft:coal_block", + "blockRuntimeId" : 1100 }, { - "id" : "minecraft:purple_glazed_terracotta", - "blockRuntimeId" : 5338 - }, - { - "id" : "minecraft:magenta_glazed_terracotta", - "blockRuntimeId" : 4655 - }, - { - "id" : "minecraft:pink_glazed_terracotta", - "blockRuntimeId" : 4794 - }, - { - "id" : "minecraft:purpur_block", - "blockRuntimeId" : 5344 - }, - { - "id" : "minecraft:purpur_block", - "blockRuntimeId" : 5346 - }, - { - "id" : "minecraft:nether_wart_block", - "blockRuntimeId" : 4753 - }, - { - "id" : "minecraft:warped_wart_block", - "blockRuntimeId" : 6384 - }, - { - "id" : "minecraft:shroomlight", - "blockRuntimeId" : 5564 - }, - { - "id" : "minecraft:crimson_nylium", - "blockRuntimeId" : 3483 - }, - { - "id" : "minecraft:warped_nylium", - "blockRuntimeId" : 6314 - }, - { - "id" : "minecraft:basalt", - "blockRuntimeId" : 198 - }, - { - "id" : "minecraft:polished_basalt", - "blockRuntimeId" : 4827 - }, - { - "id" : "minecraft:soul_soil", - "blockRuntimeId" : 5676 - }, - { - "id" : "minecraft:dirt", - "blockRuntimeId" : 3746 - }, - { - "id" : "minecraft:dirt", - "blockRuntimeId" : 3747 - }, - { - "id" : "minecraft:farmland", - "blockRuntimeId" : 4010 - }, - { - "id" : "minecraft:grass", - "blockRuntimeId" : 4140 - }, - { - "id" : "minecraft:grass_path", - "blockRuntimeId" : 4141 - }, - { - "id" : "minecraft:podzol", - "blockRuntimeId" : 4818 - }, - { - "id" : "minecraft:mycelium", - "blockRuntimeId" : 4736 - }, - { - "id" : "minecraft:stone", - "blockRuntimeId" : 5899 - }, - { - "id" : "minecraft:iron_ore", - "blockRuntimeId" : 4286 - }, - { - "id" : "minecraft:gold_ore", - "blockRuntimeId" : 4119 - }, - { - "id" : "minecraft:diamond_ore", - "blockRuntimeId" : 3737 - }, - { - "id" : "minecraft:lapis_ore", - "blockRuntimeId" : 4485 - }, - { - "id" : "minecraft:redstone_ore", - "blockRuntimeId" : 5453 - }, - { - "id" : "minecraft:coal_ore", - "blockRuntimeId" : 965 - }, - { - "id" : "minecraft:emerald_ore", - "blockRuntimeId" : 3976 - }, - { - "id" : "minecraft:quartz_ore", - "blockRuntimeId" : 5377 - }, - { - "id" : "minecraft:nether_gold_ore", - "blockRuntimeId" : 4747 - }, - { - "id" : "minecraft:ancient_debris", - "blockRuntimeId" : 136 - }, - { - "id" : "minecraft:gravel", - "blockRuntimeId" : 4142 - }, - { - "id" : "minecraft:stone", - "blockRuntimeId" : 5900 - }, - { - "id" : "minecraft:stone", - "blockRuntimeId" : 5902 - }, - { - "id" : "minecraft:stone", - "blockRuntimeId" : 5904 - }, - { - "id" : "minecraft:blackstone", - "blockRuntimeId" : 436 - }, - { - "id" : "minecraft:stone", - "blockRuntimeId" : 5901 - }, - { - "id" : "minecraft:stone", - "blockRuntimeId" : 5903 - }, - { - "id" : "minecraft:stone", - "blockRuntimeId" : 5905 - }, - { - "id" : "minecraft:polished_blackstone", - "blockRuntimeId" : 4830 - }, - { - "id" : "minecraft:sand", - "blockRuntimeId" : 5510 - }, - { - "id" : "minecraft:sand", - "blockRuntimeId" : 5511 - }, - { - "id" : "minecraft:cactus", - "blockRuntimeId" : 841 - }, - { - "id" : "minecraft:log", - "blockRuntimeId" : 4633 - }, - { - "id" : "minecraft:stripped_oak_log", - "blockRuntimeId" : 6038 - }, - { - "id" : "minecraft:log", - "blockRuntimeId" : 4634 - }, - { - "id" : "minecraft:stripped_spruce_log", - "blockRuntimeId" : 6041 - }, - { - "id" : "minecraft:log", - "blockRuntimeId" : 4635 - }, - { - "id" : "minecraft:stripped_birch_log", - "blockRuntimeId" : 6023 - }, - { - "id" : "minecraft:log", - "blockRuntimeId" : 4636 - }, - { - "id" : "minecraft:stripped_jungle_log", - "blockRuntimeId" : 6035 - }, - { - "id" : "minecraft:log2", - "blockRuntimeId" : 4645 - }, - { - "id" : "minecraft:stripped_acacia_log", - "blockRuntimeId" : 6020 - }, - { - "id" : "minecraft:log2", - "blockRuntimeId" : 4646 - }, - { - "id" : "minecraft:stripped_dark_oak_log", - "blockRuntimeId" : 6032 - }, - { - "id" : "minecraft:crimson_stem", - "blockRuntimeId" : 3528 - }, - { - "id" : "minecraft:stripped_crimson_stem", - "blockRuntimeId" : 6029 - }, - { - "id" : "minecraft:warped_stem", - "blockRuntimeId" : 6359 - }, - { - "id" : "minecraft:stripped_warped_stem", - "blockRuntimeId" : 6047 - }, - { - "id" : "minecraft:wood", - "blockRuntimeId" : 6444 - }, - { - "id" : "minecraft:wood", - "blockRuntimeId" : 6450 - }, - { - "id" : "minecraft:wood", - "blockRuntimeId" : 6445 - }, - { - "id" : "minecraft:wood", - "blockRuntimeId" : 6451 - }, - { - "id" : "minecraft:wood", - "blockRuntimeId" : 6446 - }, - { - "id" : "minecraft:wood", - "blockRuntimeId" : 6452 - }, - { - "id" : "minecraft:wood", - "blockRuntimeId" : 6447 - }, - { - "id" : "minecraft:wood", - "blockRuntimeId" : 6453 - }, - { - "id" : "minecraft:wood", - "blockRuntimeId" : 6448 - }, - { - "id" : "minecraft:wood", - "blockRuntimeId" : 6454 - }, - { - "id" : "minecraft:wood", - "blockRuntimeId" : 6449 - }, - { - "id" : "minecraft:wood", - "blockRuntimeId" : 6455 - }, - { - "id" : "minecraft:crimson_hyphae", - "blockRuntimeId" : 3480 - }, - { - "id" : "minecraft:stripped_crimson_hyphae", - "blockRuntimeId" : 6026 - }, - { - "id" : "minecraft:warped_hyphae", - "blockRuntimeId" : 6311 - }, - { - "id" : "minecraft:stripped_warped_hyphae", - "blockRuntimeId" : 6044 - }, - { - "id" : "minecraft:leaves", - "blockRuntimeId" : 4516 - }, - { - "id" : "minecraft:leaves", - "blockRuntimeId" : 4517 - }, - { - "id" : "minecraft:leaves", - "blockRuntimeId" : 4518 - }, - { - "id" : "minecraft:leaves", - "blockRuntimeId" : 4519 - }, - { - "id" : "minecraft:leaves2", - "blockRuntimeId" : 4532 - }, - { - "id" : "minecraft:leaves2", + "id" : "minecraft:dried_kelp_block", "blockRuntimeId" : 4533 }, { - "id" : "minecraft:sapling", - "blockRuntimeId" : 5524 + "id" : "minecraft:gold_block", + "blockRuntimeId" : 4900 + }, + { + "id" : "minecraft:iron_block", + "blockRuntimeId" : 5039 + }, + { + "id" : "minecraft:copper_block", + "blockRuntimeId" : 3636 + }, + { + "id" : "minecraft:exposed_copper", + "blockRuntimeId" : 4701 + }, + { + "id" : "minecraft:weathered_copper", + "blockRuntimeId" : 7495 + }, + { + "id" : "minecraft:oxidized_copper", + "blockRuntimeId" : 5607 + }, + { + "id" : "minecraft:waxed_copper", + "blockRuntimeId" : 7439 + }, + { + "id" : "minecraft:waxed_exposed_copper", + "blockRuntimeId" : 7453 + }, + { + "id" : "minecraft:waxed_weathered_copper", + "blockRuntimeId" : 7481 + }, + { + "id" : "minecraft:waxed_oxidized_copper", + "blockRuntimeId" : 7467 + }, + { + "id" : "minecraft:cut_copper", + "blockRuntimeId" : 3869 + }, + { + "id" : "minecraft:exposed_cut_copper", + "blockRuntimeId" : 4702 + }, + { + "id" : "minecraft:weathered_cut_copper", + "blockRuntimeId" : 7496 + }, + { + "id" : "minecraft:oxidized_cut_copper", + "blockRuntimeId" : 5608 + }, + { + "id" : "minecraft:waxed_cut_copper", + "blockRuntimeId" : 7440 + }, + { + "id" : "minecraft:waxed_exposed_cut_copper", + "blockRuntimeId" : 7454 + }, + { + "id" : "minecraft:waxed_weathered_cut_copper", + "blockRuntimeId" : 7482 + }, + { + "id" : "minecraft:waxed_oxidized_cut_copper", + "blockRuntimeId" : 7468 + }, + { + "id" : "minecraft:emerald_block", + "blockRuntimeId" : 4666 + }, + { + "id" : "minecraft:diamond_block", + "blockRuntimeId" : 4423 + }, + { + "id" : "minecraft:lapis_block", + "blockRuntimeId" : 5270 + }, + { + "id" : "minecraft:raw_iron_block", + "blockRuntimeId" : 6412 + }, + { + "id" : "minecraft:raw_copper_block", + "blockRuntimeId" : 6410 + }, + { + "id" : "minecraft:raw_gold_block", + "blockRuntimeId" : 6411 + }, + { + "id" : "minecraft:quartz_block", + "blockRuntimeId" : 6378 + }, + { + "id" : "minecraft:quartz_block", + "blockRuntimeId" : 6380 + }, + { + "id" : "minecraft:quartz_block", + "blockRuntimeId" : 6379 + }, + { + "id" : "minecraft:quartz_block", + "blockRuntimeId" : 6381 + }, + { + "id" : "minecraft:prismarine", + "blockRuntimeId" : 6281 + }, + { + "id" : "minecraft:prismarine", + "blockRuntimeId" : 6282 + }, + { + "id" : "minecraft:slime", + "blockRuntimeId" : 6618 + }, + { + "id" : "minecraft:honey_block", + "blockRuntimeId" : 5017 + }, + { + "id" : "minecraft:honeycomb_block", + "blockRuntimeId" : 5018 + }, + { + "id" : "minecraft:hay_block", + "blockRuntimeId" : 4989 + }, + { + "id" : "minecraft:bone_block", + "blockRuntimeId" : 672 + }, + { + "id" : "minecraft:nether_brick", + "blockRuntimeId" : 5551 + }, + { + "id" : "minecraft:red_nether_brick", + "blockRuntimeId" : 6447 + }, + { + "id" : "minecraft:netherite_block", + "blockRuntimeId" : 5568 + }, + { + "id" : "minecraft:lodestone", + "blockRuntimeId" : 5438 + }, + { + "id" : "minecraft:wool", + "blockRuntimeId" : 7659 + }, + { + "id" : "minecraft:wool", + "blockRuntimeId" : 7667 + }, + { + "id" : "minecraft:wool", + "blockRuntimeId" : 7666 + }, + { + "id" : "minecraft:wool", + "blockRuntimeId" : 7674 + }, + { + "id" : "minecraft:wool", + "blockRuntimeId" : 7671 + }, + { + "id" : "minecraft:wool", + "blockRuntimeId" : 7673 + }, + { + "id" : "minecraft:wool", + "blockRuntimeId" : 7660 + }, + { + "id" : "minecraft:wool", + "blockRuntimeId" : 7663 + }, + { + "id" : "minecraft:wool", + "blockRuntimeId" : 7664 + }, + { + "id" : "minecraft:wool", + "blockRuntimeId" : 7672 + }, + { + "id" : "minecraft:wool", + "blockRuntimeId" : 7668 + }, + { + "id" : "minecraft:wool", + "blockRuntimeId" : 7662 + }, + { + "id" : "minecraft:wool", + "blockRuntimeId" : 7670 + }, + { + "id" : "minecraft:wool", + "blockRuntimeId" : 7669 + }, + { + "id" : "minecraft:wool", + "blockRuntimeId" : 7661 + }, + { + "id" : "minecraft:wool", + "blockRuntimeId" : 7665 + }, + { + "id" : "minecraft:carpet", + "blockRuntimeId" : 923 + }, + { + "id" : "minecraft:carpet", + "blockRuntimeId" : 931 + }, + { + "id" : "minecraft:carpet", + "blockRuntimeId" : 930 + }, + { + "id" : "minecraft:carpet", + "blockRuntimeId" : 938 + }, + { + "id" : "minecraft:carpet", + "blockRuntimeId" : 935 + }, + { + "id" : "minecraft:carpet", + "blockRuntimeId" : 937 + }, + { + "id" : "minecraft:carpet", + "blockRuntimeId" : 924 + }, + { + "id" : "minecraft:carpet", + "blockRuntimeId" : 927 + }, + { + "id" : "minecraft:carpet", + "blockRuntimeId" : 928 + }, + { + "id" : "minecraft:carpet", + "blockRuntimeId" : 936 + }, + { + "id" : "minecraft:carpet", + "blockRuntimeId" : 932 + }, + { + "id" : "minecraft:carpet", + "blockRuntimeId" : 926 + }, + { + "id" : "minecraft:carpet", + "blockRuntimeId" : 934 + }, + { + "id" : "minecraft:carpet", + "blockRuntimeId" : 933 + }, + { + "id" : "minecraft:carpet", + "blockRuntimeId" : 925 + }, + { + "id" : "minecraft:carpet", + "blockRuntimeId" : 929 + }, + { + "id" : "minecraft:concrete_powder", + "blockRuntimeId" : 3619 + }, + { + "id" : "minecraft:concrete_powder", + "blockRuntimeId" : 3627 + }, + { + "id" : "minecraft:concrete_powder", + "blockRuntimeId" : 3626 + }, + { + "id" : "minecraft:concrete_powder", + "blockRuntimeId" : 3634 + }, + { + "id" : "minecraft:concrete_powder", + "blockRuntimeId" : 3631 + }, + { + "id" : "minecraft:concrete_powder", + "blockRuntimeId" : 3633 + }, + { + "id" : "minecraft:concrete_powder", + "blockRuntimeId" : 3620 + }, + { + "id" : "minecraft:concrete_powder", + "blockRuntimeId" : 3623 + }, + { + "id" : "minecraft:concrete_powder", + "blockRuntimeId" : 3624 + }, + { + "id" : "minecraft:concrete_powder", + "blockRuntimeId" : 3632 + }, + { + "id" : "minecraft:concrete_powder", + "blockRuntimeId" : 3628 + }, + { + "id" : "minecraft:concrete_powder", + "blockRuntimeId" : 3622 + }, + { + "id" : "minecraft:concrete_powder", + "blockRuntimeId" : 3630 + }, + { + "id" : "minecraft:concrete_powder", + "blockRuntimeId" : 3629 + }, + { + "id" : "minecraft:concrete_powder", + "blockRuntimeId" : 3621 + }, + { + "id" : "minecraft:concrete_powder", + "blockRuntimeId" : 3625 + }, + { + "id" : "minecraft:concrete", + "blockRuntimeId" : 3603 + }, + { + "id" : "minecraft:concrete", + "blockRuntimeId" : 3611 + }, + { + "id" : "minecraft:concrete", + "blockRuntimeId" : 3610 + }, + { + "id" : "minecraft:concrete", + "blockRuntimeId" : 3618 + }, + { + "id" : "minecraft:concrete", + "blockRuntimeId" : 3615 + }, + { + "id" : "minecraft:concrete", + "blockRuntimeId" : 3617 + }, + { + "id" : "minecraft:concrete", + "blockRuntimeId" : 3604 + }, + { + "id" : "minecraft:concrete", + "blockRuntimeId" : 3607 + }, + { + "id" : "minecraft:concrete", + "blockRuntimeId" : 3608 + }, + { + "id" : "minecraft:concrete", + "blockRuntimeId" : 3616 + }, + { + "id" : "minecraft:concrete", + "blockRuntimeId" : 3612 + }, + { + "id" : "minecraft:concrete", + "blockRuntimeId" : 3606 + }, + { + "id" : "minecraft:concrete", + "blockRuntimeId" : 3614 + }, + { + "id" : "minecraft:concrete", + "blockRuntimeId" : 3613 + }, + { + "id" : "minecraft:concrete", + "blockRuntimeId" : 3605 + }, + { + "id" : "minecraft:concrete", + "blockRuntimeId" : 3609 + }, + { + "id" : "minecraft:clay", + "blockRuntimeId" : 1099 + }, + { + "id" : "minecraft:hardened_clay", + "blockRuntimeId" : 4988 + }, + { + "id" : "minecraft:stained_hardened_clay", + "blockRuntimeId" : 6874 + }, + { + "id" : "minecraft:stained_hardened_clay", + "blockRuntimeId" : 6882 + }, + { + "id" : "minecraft:stained_hardened_clay", + "blockRuntimeId" : 6881 + }, + { + "id" : "minecraft:stained_hardened_clay", + "blockRuntimeId" : 6889 + }, + { + "id" : "minecraft:stained_hardened_clay", + "blockRuntimeId" : 6886 + }, + { + "id" : "minecraft:stained_hardened_clay", + "blockRuntimeId" : 6888 + }, + { + "id" : "minecraft:stained_hardened_clay", + "blockRuntimeId" : 6875 + }, + { + "id" : "minecraft:stained_hardened_clay", + "blockRuntimeId" : 6878 + }, + { + "id" : "minecraft:stained_hardened_clay", + "blockRuntimeId" : 6879 + }, + { + "id" : "minecraft:stained_hardened_clay", + "blockRuntimeId" : 6887 + }, + { + "id" : "minecraft:stained_hardened_clay", + "blockRuntimeId" : 6883 + }, + { + "id" : "minecraft:stained_hardened_clay", + "blockRuntimeId" : 6877 + }, + { + "id" : "minecraft:stained_hardened_clay", + "blockRuntimeId" : 6885 + }, + { + "id" : "minecraft:stained_hardened_clay", + "blockRuntimeId" : 6884 + }, + { + "id" : "minecraft:stained_hardened_clay", + "blockRuntimeId" : 6876 + }, + { + "id" : "minecraft:stained_hardened_clay", + "blockRuntimeId" : 6880 + }, + { + "id" : "minecraft:white_glazed_terracotta", + "blockRuntimeId" : 7544 + }, + { + "id" : "minecraft:silver_glazed_terracotta", + "blockRuntimeId" : 6600 + }, + { + "id" : "minecraft:gray_glazed_terracotta", + "blockRuntimeId" : 4925 + }, + { + "id" : "minecraft:black_glazed_terracotta", + "blockRuntimeId" : 478 + }, + { + "id" : "minecraft:brown_glazed_terracotta", + "blockRuntimeId" : 864 + }, + { + "id" : "minecraft:red_glazed_terracotta", + "blockRuntimeId" : 6424 + }, + { + "id" : "minecraft:orange_glazed_terracotta", + "blockRuntimeId" : 5601 + }, + { + "id" : "minecraft:yellow_glazed_terracotta", + "blockRuntimeId" : 7676 + }, + { + "id" : "minecraft:lime_glazed_terracotta", + "blockRuntimeId" : 5407 + }, + { + "id" : "minecraft:green_glazed_terracotta", + "blockRuntimeId" : 4931 + }, + { + "id" : "minecraft:cyan_glazed_terracotta", + "blockRuntimeId" : 3880 + }, + { + "id" : "minecraft:light_blue_glazed_terracotta", + "blockRuntimeId" : 5379 + }, + { + "id" : "minecraft:blue_glazed_terracotta", + "blockRuntimeId" : 665 + }, + { + "id" : "minecraft:purple_glazed_terracotta", + "blockRuntimeId" : 6352 + }, + { + "id" : "minecraft:magenta_glazed_terracotta", + "blockRuntimeId" : 5461 + }, + { + "id" : "minecraft:pink_glazed_terracotta", + "blockRuntimeId" : 5622 + }, + { + "id" : "minecraft:purpur_block", + "blockRuntimeId" : 6358 + }, + { + "id" : "minecraft:purpur_block", + "blockRuntimeId" : 6360 + }, + { + "id" : "minecraft:nether_wart_block", + "blockRuntimeId" : 5567 + }, + { + "id" : "minecraft:warped_wart_block", + "blockRuntimeId" : 7421 + }, + { + "id" : "minecraft:shroomlight", + "blockRuntimeId" : 6583 + }, + { + "id" : "minecraft:crimson_nylium", + "blockRuntimeId" : 3798 + }, + { + "id" : "minecraft:warped_nylium", + "blockRuntimeId" : 7351 + }, + { + "id" : "minecraft:basalt", + "blockRuntimeId" : 214 + }, + { + "id" : "minecraft:polished_basalt", + "blockRuntimeId" : 5665 + }, + { + "id" : "minecraft:smooth_basalt", + "blockRuntimeId" : 6640 + }, + { + "id" : "minecraft:soul_soil", + "blockRuntimeId" : 6710 + }, + { + "id" : "minecraft:dirt", + "blockRuntimeId" : 4433 + }, + { + "id" : "minecraft:dirt", + "blockRuntimeId" : 4434 + }, + { + "id" : "minecraft:farmland", + "blockRuntimeId" : 4715 + }, + { + "id" : "minecraft:grass", + "blockRuntimeId" : 4922 + }, + { + "id" : "minecraft:grass_path", + "blockRuntimeId" : 4923 + }, + { + "id" : "minecraft:podzol", + "blockRuntimeId" : 5646 + }, + { + "id" : "minecraft:mycelium", + "blockRuntimeId" : 5550 + }, + { + "id" : "minecraft:stone", + "blockRuntimeId" : 6934 + }, + { + "id" : "minecraft:iron_ore", + "blockRuntimeId" : 5072 + }, + { + "id" : "minecraft:gold_ore", + "blockRuntimeId" : 4901 + }, + { + "id" : "minecraft:diamond_ore", + "blockRuntimeId" : 4424 + }, + { + "id" : "minecraft:lapis_ore", + "blockRuntimeId" : 5271 + }, + { + "id" : "minecraft:redstone_ore", + "blockRuntimeId" : 6470 + }, + { + "id" : "minecraft:coal_ore", + "blockRuntimeId" : 1101 + }, + { + "id" : "minecraft:emerald_ore", + "blockRuntimeId" : 4667 + }, + { + "id" : "minecraft:quartz_ore", + "blockRuntimeId" : 6391 + }, + { + "id" : "minecraft:nether_gold_ore", + "blockRuntimeId" : 5561 + }, + { + "id" : "minecraft:ancient_debris", + "blockRuntimeId" : 143 + }, + { + "id" : "minecraft:copper_ore", + "blockRuntimeId" : 3637 + }, + { + "id" : "minecraft:deepslate_iron_ore", + "blockRuntimeId" : 4232 + }, + { + "id" : "minecraft:deepslate_gold_ore", + "blockRuntimeId" : 4231 + }, + { + "id" : "minecraft:deepslate_diamond_ore", + "blockRuntimeId" : 4229 + }, + { + "id" : "minecraft:deepslate_lapis_ore", + "blockRuntimeId" : 4233 + }, + { + "id" : "minecraft:deepslate_redstone_ore", + "blockRuntimeId" : 4234 + }, + { + "id" : "minecraft:deepslate_emerald_ore", + "blockRuntimeId" : 4230 + }, + { + "id" : "minecraft:deepslate_coal_ore", + "blockRuntimeId" : 4227 + }, + { + "id" : "minecraft:deepslate_copper_ore", + "blockRuntimeId" : 4228 + }, + { + "id" : "minecraft:gravel", + "blockRuntimeId" : 4924 + }, + { + "id" : "minecraft:stone", + "blockRuntimeId" : 6935 + }, + { + "id" : "minecraft:stone", + "blockRuntimeId" : 6937 + }, + { + "id" : "minecraft:stone", + "blockRuntimeId" : 6939 + }, + { + "id" : "minecraft:blackstone", + "blockRuntimeId" : 484 + }, + { + "id" : "minecraft:stone", + "blockRuntimeId" : 6936 + }, + { + "id" : "minecraft:stone", + "blockRuntimeId" : 6938 + }, + { + "id" : "minecraft:stone", + "blockRuntimeId" : 6940 + }, + { + "id" : "minecraft:polished_blackstone", + "blockRuntimeId" : 5668 + }, + { + "id" : "minecraft:deepslate", + "blockRuntimeId" : 4049 + }, + { + "id" : "minecraft:polished_deepslate", + "blockRuntimeId" : 6046 + }, + { + "id" : "minecraft:sand", + "blockRuntimeId" : 6527 + }, + { + "id" : "minecraft:sand", + "blockRuntimeId" : 6528 + }, + { + "id" : "minecraft:cactus", + "blockRuntimeId" : 890 + }, + { + "id" : "minecraft:log", + "blockRuntimeId" : 5439 + }, + { + "id" : "minecraft:stripped_oak_log", + "blockRuntimeId" : 7073 + }, + { + "id" : "minecraft:log", + "blockRuntimeId" : 5440 + }, + { + "id" : "minecraft:stripped_spruce_log", + "blockRuntimeId" : 7076 + }, + { + "id" : "minecraft:log", + "blockRuntimeId" : 5441 + }, + { + "id" : "minecraft:stripped_birch_log", + "blockRuntimeId" : 7058 + }, + { + "id" : "minecraft:log", + "blockRuntimeId" : 5442 + }, + { + "id" : "minecraft:stripped_jungle_log", + "blockRuntimeId" : 7070 + }, + { + "id" : "minecraft:log2", + "blockRuntimeId" : 5451 + }, + { + "id" : "minecraft:stripped_acacia_log", + "blockRuntimeId" : 7055 + }, + { + "id" : "minecraft:log2", + "blockRuntimeId" : 5452 + }, + { + "id" : "minecraft:stripped_dark_oak_log", + "blockRuntimeId" : 7067 + }, + { + "id" : "minecraft:crimson_stem", + "blockRuntimeId" : 3843 + }, + { + "id" : "minecraft:stripped_crimson_stem", + "blockRuntimeId" : 7064 + }, + { + "id" : "minecraft:warped_stem", + "blockRuntimeId" : 7396 + }, + { + "id" : "minecraft:stripped_warped_stem", + "blockRuntimeId" : 7082 + }, + { + "id" : "minecraft:wood", + "blockRuntimeId" : 7551 + }, + { + "id" : "minecraft:wood", + "blockRuntimeId" : 7557 + }, + { + "id" : "minecraft:wood", + "blockRuntimeId" : 7552 + }, + { + "id" : "minecraft:wood", + "blockRuntimeId" : 7558 + }, + { + "id" : "minecraft:wood", + "blockRuntimeId" : 7553 + }, + { + "id" : "minecraft:wood", + "blockRuntimeId" : 7559 + }, + { + "id" : "minecraft:wood", + "blockRuntimeId" : 7554 + }, + { + "id" : "minecraft:wood", + "blockRuntimeId" : 7560 + }, + { + "id" : "minecraft:wood", + "blockRuntimeId" : 7555 + }, + { + "id" : "minecraft:wood", + "blockRuntimeId" : 7561 + }, + { + "id" : "minecraft:wood", + "blockRuntimeId" : 7556 + }, + { + "id" : "minecraft:wood", + "blockRuntimeId" : 7562 + }, + { + "id" : "minecraft:crimson_hyphae", + "blockRuntimeId" : 3795 + }, + { + "id" : "minecraft:stripped_crimson_hyphae", + "blockRuntimeId" : 7061 + }, + { + "id" : "minecraft:warped_hyphae", + "blockRuntimeId" : 7348 + }, + { + "id" : "minecraft:stripped_warped_hyphae", + "blockRuntimeId" : 7079 + }, + { + "id" : "minecraft:leaves", + "blockRuntimeId" : 5315 + }, + { + "id" : "minecraft:leaves", + "blockRuntimeId" : 5316 + }, + { + "id" : "minecraft:leaves", + "blockRuntimeId" : 5317 + }, + { + "id" : "minecraft:leaves", + "blockRuntimeId" : 5318 + }, + { + "id" : "minecraft:leaves2", + "blockRuntimeId" : 5331 + }, + { + "id" : "minecraft:leaves2", + "blockRuntimeId" : 5332 + }, + { + "id" : "minecraft:azalea_leaves", + "blockRuntimeId" : 169 + }, + { + "id" : "minecraft:azalea_leaves_flowered", + "blockRuntimeId" : 173 }, { "id" : "minecraft:sapling", - "blockRuntimeId" : 5525 + "blockRuntimeId" : 6541 }, { "id" : "minecraft:sapling", - "blockRuntimeId" : 5526 + "blockRuntimeId" : 6542 }, { "id" : "minecraft:sapling", - "blockRuntimeId" : 5527 + "blockRuntimeId" : 6543 }, { "id" : "minecraft:sapling", - "blockRuntimeId" : 5528 + "blockRuntimeId" : 6544 }, { "id" : "minecraft:sapling", - "blockRuntimeId" : 5529 + "blockRuntimeId" : 6545 + }, + { + "id" : "minecraft:sapling", + "blockRuntimeId" : 6546 }, { "id" : "minecraft:bee_nest", - "blockRuntimeId" : 220 + "blockRuntimeId" : 236 }, { "id" : "minecraft:wheat_seeds" @@ -1640,7 +1912,7 @@ }, { "id" : "minecraft:melon_block", - "blockRuntimeId" : 4662 + "blockRuntimeId" : 5474 }, { "id" : "minecraft:melon_slice" @@ -1651,202 +1923,205 @@ { "id" : "minecraft:sweet_berries" }, + { + "id" : "minecraft:glow_berries" + }, { "id" : "minecraft:pumpkin", - "blockRuntimeId" : 5286 + "blockRuntimeId" : 6300 }, { "id" : "minecraft:carved_pumpkin", - "blockRuntimeId" : 898 + "blockRuntimeId" : 948 }, { "id" : "minecraft:lit_pumpkin", - "blockRuntimeId" : 4620 + "blockRuntimeId" : 5426 }, { "id" : "minecraft:honeycomb" }, { "id" : "minecraft:tallgrass", - "blockRuntimeId" : 6068 + "blockRuntimeId" : 7103 }, { "id" : "minecraft:double_plant", - "blockRuntimeId" : 3763 + "blockRuntimeId" : 4453 }, { "id" : "minecraft:tallgrass", - "blockRuntimeId" : 6067 + "blockRuntimeId" : 7102 }, { "id" : "minecraft:double_plant", - "blockRuntimeId" : 3762 + "blockRuntimeId" : 4452 }, { "id" : "minecraft:nether_sprouts" }, { "id" : "minecraft:coral", - "blockRuntimeId" : 3328 + "blockRuntimeId" : 3641 }, { "id" : "minecraft:coral", - "blockRuntimeId" : 3326 + "blockRuntimeId" : 3639 }, { "id" : "minecraft:coral", - "blockRuntimeId" : 3327 + "blockRuntimeId" : 3640 }, { "id" : "minecraft:coral", - "blockRuntimeId" : 3325 + "blockRuntimeId" : 3638 }, { "id" : "minecraft:coral", - "blockRuntimeId" : 3329 + "blockRuntimeId" : 3642 }, { "id" : "minecraft:coral", - "blockRuntimeId" : 3333 + "blockRuntimeId" : 3646 }, { "id" : "minecraft:coral", - "blockRuntimeId" : 3331 + "blockRuntimeId" : 3644 }, { "id" : "minecraft:coral", - "blockRuntimeId" : 3332 + "blockRuntimeId" : 3645 }, { "id" : "minecraft:coral", - "blockRuntimeId" : 3330 + "blockRuntimeId" : 3643 }, { "id" : "minecraft:coral", - "blockRuntimeId" : 3334 + "blockRuntimeId" : 3647 }, { "id" : "minecraft:coral_fan", - "blockRuntimeId" : 3348 + "blockRuntimeId" : 3661 }, { "id" : "minecraft:coral_fan", - "blockRuntimeId" : 3346 + "blockRuntimeId" : 3659 }, { "id" : "minecraft:coral_fan", - "blockRuntimeId" : 3347 + "blockRuntimeId" : 3660 }, { "id" : "minecraft:coral_fan", - "blockRuntimeId" : 3345 + "blockRuntimeId" : 3658 }, { "id" : "minecraft:coral_fan", - "blockRuntimeId" : 3349 + "blockRuntimeId" : 3662 }, { "id" : "minecraft:coral_fan_dead", - "blockRuntimeId" : 3358 + "blockRuntimeId" : 3671 }, { "id" : "minecraft:coral_fan_dead", - "blockRuntimeId" : 3356 + "blockRuntimeId" : 3669 }, { "id" : "minecraft:coral_fan_dead", - "blockRuntimeId" : 3357 + "blockRuntimeId" : 3670 }, { "id" : "minecraft:coral_fan_dead", - "blockRuntimeId" : 3355 + "blockRuntimeId" : 3668 }, { "id" : "minecraft:coral_fan_dead", - "blockRuntimeId" : 3359 + "blockRuntimeId" : 3672 }, { "id" : "minecraft:kelp" }, { "id" : "minecraft:seagrass", - "blockRuntimeId" : 5560 + "blockRuntimeId" : 6579 }, { "id" : "minecraft:crimson_roots", - "blockRuntimeId" : 3501 + "blockRuntimeId" : 3816 }, { "id" : "minecraft:warped_roots", - "blockRuntimeId" : 6332 + "blockRuntimeId" : 7369 }, { "id" : "minecraft:yellow_flower", - "blockRuntimeId" : 6568 + "blockRuntimeId" : 7675 }, { "id" : "minecraft:red_flower", - "blockRuntimeId" : 5396 + "blockRuntimeId" : 6413 }, { "id" : "minecraft:red_flower", - "blockRuntimeId" : 5397 + "blockRuntimeId" : 6414 }, { "id" : "minecraft:red_flower", - "blockRuntimeId" : 5398 + "blockRuntimeId" : 6415 }, { "id" : "minecraft:red_flower", - "blockRuntimeId" : 5399 + "blockRuntimeId" : 6416 }, { "id" : "minecraft:red_flower", - "blockRuntimeId" : 5400 + "blockRuntimeId" : 6417 }, { "id" : "minecraft:red_flower", - "blockRuntimeId" : 5401 + "blockRuntimeId" : 6418 }, { "id" : "minecraft:red_flower", - "blockRuntimeId" : 5402 + "blockRuntimeId" : 6419 }, { "id" : "minecraft:red_flower", - "blockRuntimeId" : 5403 + "blockRuntimeId" : 6420 }, { "id" : "minecraft:red_flower", - "blockRuntimeId" : 5404 + "blockRuntimeId" : 6421 }, { "id" : "minecraft:red_flower", - "blockRuntimeId" : 5405 + "blockRuntimeId" : 6422 }, { "id" : "minecraft:red_flower", - "blockRuntimeId" : 5406 + "blockRuntimeId" : 6423 }, { "id" : "minecraft:double_plant", - "blockRuntimeId" : 3760 + "blockRuntimeId" : 4450 }, { "id" : "minecraft:double_plant", - "blockRuntimeId" : 3761 + "blockRuntimeId" : 4451 }, { "id" : "minecraft:double_plant", - "blockRuntimeId" : 3764 + "blockRuntimeId" : 4454 }, { "id" : "minecraft:double_plant", - "blockRuntimeId" : 3765 + "blockRuntimeId" : 4455 }, { "id" : "minecraft:wither_rose", - "blockRuntimeId" : 6443 + "blockRuntimeId" : 7550 }, { "id" : "minecraft:white_dye" @@ -1899,6 +2174,9 @@ { "id" : "minecraft:ink_sac" }, + { + "id" : "minecraft:glow_ink_sac" + }, { "id" : "minecraft:cocoa_beans" }, @@ -1910,47 +2188,127 @@ }, { "id" : "minecraft:vine", - "blockRuntimeId" : 6219 + "blockRuntimeId" : 7256 }, { "id" : "minecraft:weeping_vines", - "blockRuntimeId" : 6403 + "blockRuntimeId" : 7510 }, { "id" : "minecraft:twisting_vines", - "blockRuntimeId" : 6147 + "blockRuntimeId" : 7184 }, { "id" : "minecraft:waterlily", - "blockRuntimeId" : 6401 + "blockRuntimeId" : 7438 }, { "id" : "minecraft:deadbush", - "blockRuntimeId" : 3722 + "blockRuntimeId" : 4048 }, { "id" : "minecraft:bamboo", - "blockRuntimeId" : 161 + "blockRuntimeId" : 177 }, { "id" : "minecraft:snow", - "blockRuntimeId" : 5632 + "blockRuntimeId" : 6666 }, { "id" : "minecraft:ice", - "blockRuntimeId" : 4248 + "blockRuntimeId" : 5031 }, { "id" : "minecraft:packed_ice", - "blockRuntimeId" : 4793 + "blockRuntimeId" : 5621 }, { "id" : "minecraft:blue_ice", - "blockRuntimeId" : 623 + "blockRuntimeId" : 671 }, { "id" : "minecraft:snow_layer", - "blockRuntimeId" : 5633 + "blockRuntimeId" : 6667 + }, + { + "id" : "minecraft:pointed_dripstone", + "blockRuntimeId" : 5652 + }, + { + "id" : "minecraft:dripstone_block", + "blockRuntimeId" : 4534 + }, + { + "id" : "minecraft:moss_carpet", + "blockRuntimeId" : 5531 + }, + { + "id" : "minecraft:moss_block", + "blockRuntimeId" : 5530 + }, + { + "id" : "minecraft:dirt_with_roots", + "blockRuntimeId" : 4435 + }, + { + "id" : "minecraft:hanging_roots", + "blockRuntimeId" : 4953 + }, + { + "id" : "minecraft:big_dripleaf", + "blockRuntimeId" : 328 + }, + { + "id" : "minecraft:small_dripleaf_block", + "blockRuntimeId" : 6632 + }, + { + "id" : "minecraft:spore_blossom", + "blockRuntimeId" : 6719 + }, + { + "id" : "minecraft:azalea", + "blockRuntimeId" : 168 + }, + { + "id" : "minecraft:flowering_azalea", + "blockRuntimeId" : 4764 + }, + { + "id" : "minecraft:glow_lichen", + "blockRuntimeId" : 4897 + }, + { + "id" : "minecraft:amethyst_block", + "blockRuntimeId" : 136 + }, + { + "id" : "minecraft:budding_amethyst", + "blockRuntimeId" : 889 + }, + { + "id" : "minecraft:amethyst_cluster", + "blockRuntimeId" : 137 + }, + { + "id" : "minecraft:large_amethyst_bud", + "blockRuntimeId" : 5272 + }, + { + "id" : "minecraft:medium_amethyst_bud", + "blockRuntimeId" : 5468 + }, + { + "id" : "minecraft:small_amethyst_bud", + "blockRuntimeId" : 6619 + }, + { + "id" : "minecraft:tuff", + "blockRuntimeId" : 7171 + }, + { + "id" : "minecraft:calcite", + "blockRuntimeId" : 913 }, { "id" : "minecraft:chicken" @@ -1981,35 +2339,35 @@ }, { "id" : "minecraft:brown_mushroom", - "blockRuntimeId" : 822 + "blockRuntimeId" : 870 }, { "id" : "minecraft:red_mushroom", - "blockRuntimeId" : 5413 + "blockRuntimeId" : 6430 }, { "id" : "minecraft:crimson_fungus", - "blockRuntimeId" : 3479 + "blockRuntimeId" : 3794 }, { "id" : "minecraft:warped_fungus", - "blockRuntimeId" : 6310 + "blockRuntimeId" : 7347 }, { "id" : "minecraft:brown_mushroom_block", - "blockRuntimeId" : 837 + "blockRuntimeId" : 885 }, { "id" : "minecraft:red_mushroom_block", - "blockRuntimeId" : 5428 + "blockRuntimeId" : 6445 }, { "id" : "minecraft:brown_mushroom_block", - "blockRuntimeId" : 838 + "blockRuntimeId" : 886 }, { "id" : "minecraft:brown_mushroom_block", - "blockRuntimeId" : 823 + "blockRuntimeId" : 871 }, { "id" : "minecraft:egg" @@ -2028,46 +2386,50 @@ }, { "id" : "minecraft:web", - "blockRuntimeId" : 6402 + "blockRuntimeId" : 7509 }, { "id" : "minecraft:spider_eye" }, { "id" : "minecraft:mob_spawner", - "blockRuntimeId" : 4711 + "blockRuntimeId" : 5523 }, { "id" : "minecraft:monster_egg", - "blockRuntimeId" : 4712 + "blockRuntimeId" : 5524 }, { "id" : "minecraft:monster_egg", - "blockRuntimeId" : 4713 + "blockRuntimeId" : 5525 }, { "id" : "minecraft:monster_egg", - "blockRuntimeId" : 4714 + "blockRuntimeId" : 5526 }, { "id" : "minecraft:monster_egg", - "blockRuntimeId" : 4715 + "blockRuntimeId" : 5527 }, { "id" : "minecraft:monster_egg", - "blockRuntimeId" : 4716 + "blockRuntimeId" : 5528 }, { "id" : "minecraft:monster_egg", - "blockRuntimeId" : 4717 + "blockRuntimeId" : 5529 + }, + { + "id" : "minecraft:infested_deepslate", + "blockRuntimeId" : 5032 }, { "id" : "minecraft:dragon_egg", - "blockRuntimeId" : 3842 + "blockRuntimeId" : 4532 }, { "id" : "minecraft:turtle_egg", - "blockRuntimeId" : 6135 + "blockRuntimeId" : 7172 }, { "id" : "minecraft:chicken_spawn_egg" @@ -2189,6 +2551,9 @@ { "id" : "minecraft:squid_spawn_egg" }, + { + "id" : "minecraft:glow_squid_spawn_egg" + }, { "id" : "minecraft:cave_spider_spawn_egg" }, @@ -2222,6 +2587,12 @@ { "id" : "minecraft:piglin_brute_spawn_egg" }, + { + "id" : "minecraft:goat_spawn_egg" + }, + { + "id" : "minecraft:axolotl_spawn_egg" + }, { "id" : "minecraft:ghast_spawn_egg" }, @@ -2260,42 +2631,42 @@ }, { "id" : "minecraft:obsidian", - "blockRuntimeId" : 4786 + "blockRuntimeId" : 5600 }, { "id" : "minecraft:crying_obsidian", - "blockRuntimeId" : 3553 + "blockRuntimeId" : 3868 }, { "id" : "minecraft:bedrock", - "blockRuntimeId" : 218 + "blockRuntimeId" : 234 }, { "id" : "minecraft:soul_sand", - "blockRuntimeId" : 5675 + "blockRuntimeId" : 6709 }, { "id" : "minecraft:netherrack", - "blockRuntimeId" : 4755 + "blockRuntimeId" : 5569 }, { "id" : "minecraft:magma", - "blockRuntimeId" : 4661 + "blockRuntimeId" : 5467 }, { "id" : "minecraft:nether_wart" }, { "id" : "minecraft:end_stone", - "blockRuntimeId" : 4003 + "blockRuntimeId" : 4694 }, { "id" : "minecraft:chorus_flower", - "blockRuntimeId" : 956 + "blockRuntimeId" : 1092 }, { "id" : "minecraft:chorus_plant", - "blockRuntimeId" : 962 + "blockRuntimeId" : 1098 }, { "id" : "minecraft:chorus_fruit" @@ -2305,51 +2676,51 @@ }, { "id" : "minecraft:sponge", - "blockRuntimeId" : 5683 + "blockRuntimeId" : 6717 }, { "id" : "minecraft:sponge", - "blockRuntimeId" : 5684 + "blockRuntimeId" : 6718 }, { "id" : "minecraft:coral_block", - "blockRuntimeId" : 3335 + "blockRuntimeId" : 3648 }, { "id" : "minecraft:coral_block", - "blockRuntimeId" : 3336 + "blockRuntimeId" : 3649 }, { "id" : "minecraft:coral_block", - "blockRuntimeId" : 3337 + "blockRuntimeId" : 3650 }, { "id" : "minecraft:coral_block", - "blockRuntimeId" : 3338 + "blockRuntimeId" : 3651 }, { "id" : "minecraft:coral_block", - "blockRuntimeId" : 3339 + "blockRuntimeId" : 3652 }, { "id" : "minecraft:coral_block", - "blockRuntimeId" : 3340 + "blockRuntimeId" : 3653 }, { "id" : "minecraft:coral_block", - "blockRuntimeId" : 3341 + "blockRuntimeId" : 3654 }, { "id" : "minecraft:coral_block", - "blockRuntimeId" : 3342 + "blockRuntimeId" : 3655 }, { "id" : "minecraft:coral_block", - "blockRuntimeId" : 3343 + "blockRuntimeId" : 3656 }, { "id" : "minecraft:coral_block", - "blockRuntimeId" : 3344 + "blockRuntimeId" : 3657 }, { "id" : "minecraft:leather_helmet" @@ -3305,6 +3676,9 @@ "id" : "minecraft:lingering_potion", "damage" : 42 }, + { + "id" : "minecraft:spyglass" + }, { "id" : "minecraft:stick" }, @@ -3373,43 +3747,43 @@ }, { "id" : "minecraft:torch", - "blockRuntimeId" : 6075 + "blockRuntimeId" : 7111 }, { "id" : "minecraft:soul_torch", - "blockRuntimeId" : 5677 + "blockRuntimeId" : 6711 }, { "id" : "minecraft:sea_pickle", - "blockRuntimeId" : 5552 + "blockRuntimeId" : 6571 }, { "id" : "minecraft:lantern", - "blockRuntimeId" : 4482 + "blockRuntimeId" : 5268 }, { "id" : "minecraft:soul_lantern", - "blockRuntimeId" : 5673 + "blockRuntimeId" : 6707 }, { "id" : "minecraft:crafting_table", - "blockRuntimeId" : 3415 + "blockRuntimeId" : 3730 }, { "id" : "minecraft:cartography_table", - "blockRuntimeId" : 897 + "blockRuntimeId" : 947 }, { "id" : "minecraft:fletching_table", - "blockRuntimeId" : 4056 + "blockRuntimeId" : 4761 }, { "id" : "minecraft:smithing_table", - "blockRuntimeId" : 5600 + "blockRuntimeId" : 6633 }, { "id" : "minecraft:beehive", - "blockRuntimeId" : 244 + "blockRuntimeId" : 260 }, { "id" : "minecraft:campfire" @@ -3419,152 +3793,152 @@ }, { "id" : "minecraft:furnace", - "blockRuntimeId" : 4107 + "blockRuntimeId" : 4813 }, { "id" : "minecraft:blast_furnace", - "blockRuntimeId" : 611 + "blockRuntimeId" : 659 }, { "id" : "minecraft:smoker", - "blockRuntimeId" : 5601 + "blockRuntimeId" : 6634 }, { "id" : "minecraft:respawn_anchor", - "blockRuntimeId" : 5505 + "blockRuntimeId" : 6522 }, { "id" : "minecraft:brewing_stand" }, { "id" : "minecraft:anvil", - "blockRuntimeId" : 145 + "blockRuntimeId" : 152 }, { "id" : "minecraft:anvil", - "blockRuntimeId" : 149 + "blockRuntimeId" : 156 }, { "id" : "minecraft:anvil", - "blockRuntimeId" : 153 + "blockRuntimeId" : 160 }, { "id" : "minecraft:grindstone", - "blockRuntimeId" : 4155 + "blockRuntimeId" : 4937 }, { "id" : "minecraft:enchanting_table", - "blockRuntimeId" : 3977 + "blockRuntimeId" : 4668 }, { "id" : "minecraft:bookshelf", - "blockRuntimeId" : 636 + "blockRuntimeId" : 684 }, { "id" : "minecraft:lectern", - "blockRuntimeId" : 4540 + "blockRuntimeId" : 5339 }, { "id" : "minecraft:cauldron" }, { "id" : "minecraft:composter", - "blockRuntimeId" : 3283 + "blockRuntimeId" : 3594 }, { "id" : "minecraft:chest", - "blockRuntimeId" : 948 + "blockRuntimeId" : 1083 }, { "id" : "minecraft:trapped_chest", - "blockRuntimeId" : 6097 + "blockRuntimeId" : 7133 }, { "id" : "minecraft:ender_chest", - "blockRuntimeId" : 4004 + "blockRuntimeId" : 4695 }, { "id" : "minecraft:barrel", - "blockRuntimeId" : 185 + "blockRuntimeId" : 201 }, { "id" : "minecraft:undyed_shulker_box", - "blockRuntimeId" : 6179 + "blockRuntimeId" : 7216 }, { "id" : "minecraft:shulker_box", - "blockRuntimeId" : 5565 + "blockRuntimeId" : 6584 }, { "id" : "minecraft:shulker_box", - "blockRuntimeId" : 5573 + "blockRuntimeId" : 6592 }, { "id" : "minecraft:shulker_box", - "blockRuntimeId" : 5572 + "blockRuntimeId" : 6591 }, { "id" : "minecraft:shulker_box", - "blockRuntimeId" : 5580 + "blockRuntimeId" : 6599 }, { "id" : "minecraft:shulker_box", - "blockRuntimeId" : 5577 + "blockRuntimeId" : 6596 }, { "id" : "minecraft:shulker_box", - "blockRuntimeId" : 5579 + "blockRuntimeId" : 6598 }, { "id" : "minecraft:shulker_box", - "blockRuntimeId" : 5566 + "blockRuntimeId" : 6585 }, { "id" : "minecraft:shulker_box", - "blockRuntimeId" : 5569 + "blockRuntimeId" : 6588 }, { "id" : "minecraft:shulker_box", - "blockRuntimeId" : 5570 + "blockRuntimeId" : 6589 }, { "id" : "minecraft:shulker_box", - "blockRuntimeId" : 5578 + "blockRuntimeId" : 6597 }, { "id" : "minecraft:shulker_box", - "blockRuntimeId" : 5574 + "blockRuntimeId" : 6593 }, { "id" : "minecraft:shulker_box", - "blockRuntimeId" : 5568 + "blockRuntimeId" : 6587 }, { "id" : "minecraft:shulker_box", - "blockRuntimeId" : 5576 + "blockRuntimeId" : 6595 }, { "id" : "minecraft:shulker_box", - "blockRuntimeId" : 5575 + "blockRuntimeId" : 6594 }, { "id" : "minecraft:shulker_box", - "blockRuntimeId" : 5567 + "blockRuntimeId" : 6586 }, { "id" : "minecraft:shulker_box", - "blockRuntimeId" : 5571 + "blockRuntimeId" : 6590 }, { "id" : "minecraft:armor_stand" }, { "id" : "minecraft:noteblock", - "blockRuntimeId" : 4765 + "blockRuntimeId" : 5579 }, { "id" : "minecraft:jukebox", - "blockRuntimeId" : 4327 + "blockRuntimeId" : 5113 }, { "id" : "minecraft:music_disc_13" @@ -3610,15 +3984,15 @@ }, { "id" : "minecraft:glowstone", - "blockRuntimeId" : 4117 + "blockRuntimeId" : 4899 }, { "id" : "minecraft:redstone_lamp", - "blockRuntimeId" : 5452 + "blockRuntimeId" : 6469 }, { "id" : "minecraft:sealantern", - "blockRuntimeId" : 5563 + "blockRuntimeId" : 6582 }, { "id" : "minecraft:oak_sign" @@ -3683,6 +4057,15 @@ { "id" : "minecraft:pufferfish_bucket" }, + { + "id" : "minecraft:powder_snow_bucket" + }, + { + "id" : "minecraft:axolotl_bucket" + }, + { + "id" : "minecraft:glow_frame" + }, { "id" : "minecraft:skull", "damage" : 3 @@ -3708,23 +4091,23 @@ }, { "id" : "minecraft:beacon", - "blockRuntimeId" : 201 + "blockRuntimeId" : 217 }, { "id" : "minecraft:bell", - "blockRuntimeId" : 276 + "blockRuntimeId" : 292 }, { "id" : "minecraft:conduit", - "blockRuntimeId" : 3324 + "blockRuntimeId" : 3635 }, { "id" : "minecraft:stonecutter_block", - "blockRuntimeId" : 6014 + "blockRuntimeId" : 7049 }, { "id" : "minecraft:end_portal_frame", - "blockRuntimeId" : 3989 + "blockRuntimeId" : 4680 }, { "id" : "minecraft:coal" @@ -3738,6 +4121,18 @@ { "id" : "minecraft:iron_nugget" }, + { + "id" : "minecraft:raw_iron" + }, + { + "id" : "minecraft:raw_gold" + }, + { + "id" : "minecraft:raw_copper" + }, + { + "id" : "minecraft:copper_ingot" + }, { "id" : "minecraft:iron_ingot" }, @@ -3771,6 +4166,9 @@ { "id" : "minecraft:prismarine_shard" }, + { + "id" : "minecraft:amethyst_shard" + }, { "id" : "minecraft:prismarine_crystals" }, @@ -3845,7 +4243,11 @@ }, { "id" : "minecraft:end_rod", - "blockRuntimeId" : 3997 + "blockRuntimeId" : 4688 + }, + { + "id" : "minecraft:lightning_rod", + "blockRuntimeId" : 5401 }, { "id" : "minecraft:end_crystal" @@ -4307,15 +4709,15 @@ }, { "id" : "minecraft:rail", - "blockRuntimeId" : 5386 + "blockRuntimeId" : 6400 }, { "id" : "minecraft:golden_rail", - "blockRuntimeId" : 4120 + "blockRuntimeId" : 4902 }, { "id" : "minecraft:detector_rail", - "blockRuntimeId" : 3724 + "blockRuntimeId" : 4411 }, { "id" : "minecraft:activator_rail", @@ -4338,74 +4740,74 @@ }, { "id" : "minecraft:redstone_block", - "blockRuntimeId" : 5451 + "blockRuntimeId" : 6468 }, { "id" : "minecraft:redstone_torch", - "blockRuntimeId" : 5454 + "blockRuntimeId" : 6471 }, { "id" : "minecraft:lever", - "blockRuntimeId" : 4548 + "blockRuntimeId" : 5347 }, { "id" : "minecraft:wooden_button", - "blockRuntimeId" : 6480 + "blockRuntimeId" : 7587 }, { "id" : "minecraft:spruce_button", - "blockRuntimeId" : 5685 + "blockRuntimeId" : 6720 }, { "id" : "minecraft:birch_button", - "blockRuntimeId" : 308 + "blockRuntimeId" : 356 }, { "id" : "minecraft:jungle_button", - "blockRuntimeId" : 4328 + "blockRuntimeId" : 5114 }, { "id" : "minecraft:acacia_button" }, { "id" : "minecraft:dark_oak_button", - "blockRuntimeId" : 3560 + "blockRuntimeId" : 3886 }, { "id" : "minecraft:stone_button", - "blockRuntimeId" : 5914 + "blockRuntimeId" : 6949 }, { "id" : "minecraft:crimson_button", - "blockRuntimeId" : 3416 + "blockRuntimeId" : 3731 }, { "id" : "minecraft:warped_button", - "blockRuntimeId" : 6247 + "blockRuntimeId" : 7284 }, { "id" : "minecraft:polished_blackstone_button", - "blockRuntimeId" : 5006 + "blockRuntimeId" : 5844 }, { "id" : "minecraft:tripwire_hook", - "blockRuntimeId" : 6119 + "blockRuntimeId" : 7155 }, { "id" : "minecraft:wooden_pressure_plate", - "blockRuntimeId" : 6524 + "blockRuntimeId" : 7631 }, { "id" : "minecraft:spruce_pressure_plate", - "blockRuntimeId" : 5745 + "blockRuntimeId" : 6780 }, { "id" : "minecraft:birch_pressure_plate", - "blockRuntimeId" : 368 + "blockRuntimeId" : 416 }, { "id" : "minecraft:jungle_pressure_plate", - "blockRuntimeId" : 4388 + "blockRuntimeId" : 5174 }, { "id" : "minecraft:acacia_pressure_plate", @@ -4413,39 +4815,39 @@ }, { "id" : "minecraft:dark_oak_pressure_plate", - "blockRuntimeId" : 3620 + "blockRuntimeId" : 3946 }, { "id" : "minecraft:crimson_pressure_plate", - "blockRuntimeId" : 3485 + "blockRuntimeId" : 3800 }, { "id" : "minecraft:warped_pressure_plate", - "blockRuntimeId" : 6316 + "blockRuntimeId" : 7353 }, { "id" : "minecraft:stone_pressure_plate", - "blockRuntimeId" : 5926 + "blockRuntimeId" : 6961 }, { "id" : "minecraft:light_weighted_pressure_plate", - "blockRuntimeId" : 4586 + "blockRuntimeId" : 5385 }, { "id" : "minecraft:heavy_weighted_pressure_plate", - "blockRuntimeId" : 4218 + "blockRuntimeId" : 5001 }, { "id" : "minecraft:polished_blackstone_pressure_plate", - "blockRuntimeId" : 5020 + "blockRuntimeId" : 5858 }, { "id" : "minecraft:observer", - "blockRuntimeId" : 4774 + "blockRuntimeId" : 5588 }, { "id" : "minecraft:daylight_detector", - "blockRuntimeId" : 3690 + "blockRuntimeId" : 4016 }, { "id" : "minecraft:repeater" @@ -4458,30 +4860,30 @@ }, { "id" : "minecraft:dropper", - "blockRuntimeId" : 3847 + "blockRuntimeId" : 4538 }, { "id" : "minecraft:dispenser", - "blockRuntimeId" : 3751 + "blockRuntimeId" : 4439 }, { "id" : "minecraft:piston", - "blockRuntimeId" : 4801 + "blockRuntimeId" : 5629 }, { "id" : "minecraft:sticky_piston", - "blockRuntimeId" : 5888 + "blockRuntimeId" : 6923 }, { "id" : "minecraft:tnt", - "blockRuntimeId" : 6071 + "blockRuntimeId" : 7107 }, { "id" : "minecraft:name_tag" }, { "id" : "minecraft:loom", - "blockRuntimeId" : 4651 + "blockRuntimeId" : 5457 }, { "id" : "minecraft:banner" @@ -4724,7 +5126,7 @@ }, { "id" : "minecraft:target", - "blockRuntimeId" : 6070 + "blockRuntimeId" : 7105 }, { "id" : "minecraft:lodestone_compass" diff --git a/connector/src/main/resources/bedrock/entity_identifiers.dat b/connector/src/main/resources/bedrock/entity_identifiers.dat index c9477ba85..b9da310f5 100644 Binary files a/connector/src/main/resources/bedrock/entity_identifiers.dat and b/connector/src/main/resources/bedrock/entity_identifiers.dat differ diff --git a/connector/src/main/resources/bedrock/runtime_item_states.json b/connector/src/main/resources/bedrock/runtime_item_states.json index eaf6656bd..d52495ed4 100644 --- a/connector/src/main/resources/bedrock/runtime_item_states.json +++ b/connector/src/main/resources/bedrock/runtime_item_states.json @@ -1,7 +1,7 @@ [ { "name" : "minecraft:acacia_boat", - "id" : 377 + "id" : 379 }, { "name" : "minecraft:acacia_button", @@ -9,7 +9,7 @@ }, { "name" : "minecraft:acacia_door", - "id" : 546 + "id" : 556 }, { "name" : "minecraft:acacia_fence_gate", @@ -21,7 +21,7 @@ }, { "name" : "minecraft:acacia_sign", - "id" : 569 + "id" : 579 }, { "name" : "minecraft:acacia_stairs", @@ -45,7 +45,7 @@ }, { "name" : "minecraft:agent_spawn_egg", - "id" : 485 + "id" : 487 }, { "name" : "minecraft:air", @@ -55,6 +55,18 @@ "name" : "minecraft:allow", "id" : 210 }, + { + "name" : "minecraft:amethyst_block", + "id" : -327 + }, + { + "name" : "minecraft:amethyst_cluster", + "id" : -329 + }, + { + "name" : "minecraft:amethyst_shard", + "id" : 623 + }, { "name" : "minecraft:ancient_debris", "id" : -271 @@ -73,19 +85,39 @@ }, { "name" : "minecraft:armor_stand", - "id" : 542 + "id" : 552 }, { "name" : "minecraft:arrow", "id" : 301 }, + { + "name" : "minecraft:axolotl_bucket", + "id" : 369 + }, + { + "name" : "minecraft:axolotl_spawn_egg", + "id" : 500 + }, + { + "name" : "minecraft:azalea", + "id" : -337 + }, + { + "name" : "minecraft:azalea_leaves", + "id" : -324 + }, + { + "name" : "minecraft:azalea_leaves_flowered", + "id" : -325 + }, { "name" : "minecraft:baked_potato", "id" : 281 }, { "name" : "minecraft:balloon", - "id" : 587 + "id" : 597 }, { "name" : "minecraft:bamboo", @@ -97,11 +129,11 @@ }, { "name" : "minecraft:banner", - "id" : 557 + "id" : 567 }, { "name" : "minecraft:banner_pattern", - "id" : 613 + "id" : 627 }, { "name" : "minecraft:barrel", @@ -117,7 +149,7 @@ }, { "name" : "minecraft:bat_spawn_egg", - "id" : 451 + "id" : 453 }, { "name" : "minecraft:beacon", @@ -125,7 +157,7 @@ }, { "name" : "minecraft:bed", - "id" : 416 + "id" : 418 }, { "name" : "minecraft:bedrock", @@ -137,7 +169,7 @@ }, { "name" : "minecraft:bee_spawn_egg", - "id" : 492 + "id" : 494 }, { "name" : "minecraft:beef", @@ -163,9 +195,13 @@ "name" : "minecraft:bell", "id" : -206 }, + { + "name" : "minecraft:big_dripleaf", + "id" : -323 + }, { "name" : "minecraft:birch_boat", - "id" : 374 + "id" : 376 }, { "name" : "minecraft:birch_button", @@ -173,7 +209,7 @@ }, { "name" : "minecraft:birch_door", - "id" : 544 + "id" : 554 }, { "name" : "minecraft:birch_fence_gate", @@ -185,7 +221,7 @@ }, { "name" : "minecraft:birch_sign", - "id" : 567 + "id" : 577 }, { "name" : "minecraft:birch_stairs", @@ -205,7 +241,7 @@ }, { "name" : "minecraft:black_dye", - "id" : 393 + "id" : 395 }, { "name" : "minecraft:black_glazed_terracotta", @@ -237,23 +273,23 @@ }, { "name" : "minecraft:blaze_powder", - "id" : 427 + "id" : 429 }, { "name" : "minecraft:blaze_rod", - "id" : 421 + "id" : 423 }, { "name" : "minecraft:blaze_spawn_egg", - "id" : 454 + "id" : 456 }, { "name" : "minecraft:bleach", - "id" : 585 + "id" : 595 }, { "name" : "minecraft:blue_dye", - "id" : 397 + "id" : 399 }, { "name" : "minecraft:blue_glazed_terracotta", @@ -265,11 +301,11 @@ }, { "name" : "minecraft:boat", - "id" : 611 + "id" : 625 }, { "name" : "minecraft:bone", - "id" : 413 + "id" : 415 }, { "name" : "minecraft:bone_block", @@ -277,11 +313,11 @@ }, { "name" : "minecraft:bone_meal", - "id" : 409 + "id" : 411 }, { "name" : "minecraft:book", - "id" : 385 + "id" : 387 }, { "name" : "minecraft:bookshelf", @@ -293,7 +329,7 @@ }, { "name" : "minecraft:bordure_indented_banner_pattern", - "id" : 576 + "id" : 586 }, { "name" : "minecraft:bow", @@ -309,7 +345,7 @@ }, { "name" : "minecraft:brewing_stand", - "id" : 429 + "id" : 431 }, { "name" : "minecraft:brewingstandblock", @@ -317,7 +353,7 @@ }, { "name" : "minecraft:brick", - "id" : 381 + "id" : 383 }, { "name" : "minecraft:brick_block", @@ -329,7 +365,7 @@ }, { "name" : "minecraft:brown_dye", - "id" : 396 + "id" : 398 }, { "name" : "minecraft:brown_glazed_terracotta", @@ -351,21 +387,29 @@ "name" : "minecraft:bucket", "id" : 360 }, + { + "name" : "minecraft:budding_amethyst", + "id" : -328 + }, { "name" : "minecraft:cactus", "id" : 81 }, { "name" : "minecraft:cake", - "id" : 415 + "id" : 417 + }, + { + "name" : "minecraft:calcite", + "id" : -326 }, { "name" : "minecraft:camera", - "id" : 582 + "id" : 592 }, { "name" : "minecraft:campfire", - "id" : 578 + "id" : 588 }, { "name" : "minecraft:carpet", @@ -377,7 +421,7 @@ }, { "name" : "minecraft:carrot_on_a_stick", - "id" : 507 + "id" : 517 }, { "name" : "minecraft:carrots", @@ -393,19 +437,31 @@ }, { "name" : "minecraft:cat_spawn_egg", - "id" : 486 + "id" : 488 }, { "name" : "minecraft:cauldron", - "id" : 430 + "id" : 432 }, { "name" : "minecraft:cave_spider_spawn_egg", - "id" : 455 + "id" : 457 + }, + { + "name" : "minecraft:cave_vines", + "id" : -322 + }, + { + "name" : "minecraft:cave_vines_body_with_berries", + "id" : -375 + }, + { + "name" : "minecraft:cave_vines_head_with_berries", + "id" : -376 }, { "name" : "minecraft:chain", - "id" : 607 + "id" : 617 }, { "name" : "minecraft:chain_command_block", @@ -445,7 +501,7 @@ }, { "name" : "minecraft:chest_minecart", - "id" : 387 + "id" : 389 }, { "name" : "minecraft:chicken", @@ -453,7 +509,11 @@ }, { "name" : "minecraft:chicken_spawn_egg", - "id" : 433 + "id" : 435 + }, + { + "name" : "minecraft:chiseled_deepslate", + "id" : -395 }, { "name" : "minecraft:chiseled_nether_bricks", @@ -469,7 +529,7 @@ }, { "name" : "minecraft:chorus_fruit", - "id" : 548 + "id" : 558 }, { "name" : "minecraft:chorus_plant", @@ -481,11 +541,11 @@ }, { "name" : "minecraft:clay_ball", - "id" : 382 + "id" : 384 }, { "name" : "minecraft:clock", - "id" : 391 + "id" : 393 }, { "name" : "minecraft:coal", @@ -499,6 +559,26 @@ "name" : "minecraft:coal_ore", "id" : 16 }, + { + "name" : "minecraft:cobbled_deepslate", + "id" : -379 + }, + { + "name" : "minecraft:cobbled_deepslate_double_slab", + "id" : -396 + }, + { + "name" : "minecraft:cobbled_deepslate_slab", + "id" : -380 + }, + { + "name" : "minecraft:cobbled_deepslate_stairs", + "id" : -381 + }, + { + "name" : "minecraft:cobbled_deepslate_wall", + "id" : -382 + }, { "name" : "minecraft:cobblestone", "id" : 4 @@ -513,7 +593,7 @@ }, { "name" : "minecraft:cocoa_beans", - "id" : 410 + "id" : 412 }, { "name" : "minecraft:cod", @@ -525,7 +605,7 @@ }, { "name" : "minecraft:cod_spawn_egg", - "id" : 478 + "id" : 480 }, { "name" : "minecraft:colored_torch_bp", @@ -541,15 +621,15 @@ }, { "name" : "minecraft:command_block_minecart", - "id" : 553 + "id" : 563 }, { "name" : "minecraft:comparator", - "id" : 512 + "id" : 522 }, { "name" : "minecraft:compass", - "id" : 389 + "id" : 391 }, { "name" : "minecraft:composter", @@ -557,7 +637,7 @@ }, { "name" : "minecraft:compound", - "id" : 583 + "id" : 593 }, { "name" : "minecraft:concrete", @@ -585,7 +665,7 @@ }, { "name" : "minecraft:cooked_mutton", - "id" : 541 + "id" : 551 }, { "name" : "minecraft:cooked_porkchop", @@ -603,6 +683,18 @@ "name" : "minecraft:cookie", "id" : 271 }, + { + "name" : "minecraft:copper_block", + "id" : -340 + }, + { + "name" : "minecraft:copper_ingot", + "id" : 504 + }, + { + "name" : "minecraft:copper_ore", + "id" : -311 + }, { "name" : "minecraft:coral", "id" : -131 @@ -633,7 +725,15 @@ }, { "name" : "minecraft:cow_spawn_egg", - "id" : 434 + "id" : 436 + }, + { + "name" : "minecraft:cracked_deepslate_bricks", + "id" : -410 + }, + { + "name" : "minecraft:cracked_deepslate_tiles", + "id" : -409 }, { "name" : "minecraft:cracked_nether_bricks", @@ -649,11 +749,11 @@ }, { "name" : "minecraft:creeper_banner_pattern", - "id" : 572 + "id" : 582 }, { "name" : "minecraft:creeper_spawn_egg", - "id" : 439 + "id" : 441 }, { "name" : "minecraft:crimson_button", @@ -661,7 +761,7 @@ }, { "name" : "minecraft:crimson_door", - "id" : 604 + "id" : 614 }, { "name" : "minecraft:crimson_double_slab", @@ -701,7 +801,7 @@ }, { "name" : "minecraft:crimson_sign", - "id" : 602 + "id" : 612 }, { "name" : "minecraft:crimson_slab", @@ -729,15 +829,27 @@ }, { "name" : "minecraft:crossbow", - "id" : 565 + "id" : 575 }, { "name" : "minecraft:crying_obsidian", "id" : -289 }, + { + "name" : "minecraft:cut_copper", + "id" : -347 + }, + { + "name" : "minecraft:cut_copper_slab", + "id" : -361 + }, + { + "name" : "minecraft:cut_copper_stairs", + "id" : -354 + }, { "name" : "minecraft:cyan_dye", - "id" : 399 + "id" : 401 }, { "name" : "minecraft:cyan_glazed_terracotta", @@ -745,7 +857,7 @@ }, { "name" : "minecraft:dark_oak_boat", - "id" : 378 + "id" : 380 }, { "name" : "minecraft:dark_oak_button", @@ -753,7 +865,7 @@ }, { "name" : "minecraft:dark_oak_door", - "id" : 547 + "id" : 557 }, { "name" : "minecraft:dark_oak_fence_gate", @@ -765,7 +877,7 @@ }, { "name" : "minecraft:dark_oak_sign", - "id" : 570 + "id" : 580 }, { "name" : "minecraft:dark_oak_stairs", @@ -799,6 +911,82 @@ "name" : "minecraft:deadbush", "id" : 32 }, + { + "name" : "minecraft:deepslate", + "id" : -378 + }, + { + "name" : "minecraft:deepslate_brick_double_slab", + "id" : -399 + }, + { + "name" : "minecraft:deepslate_brick_slab", + "id" : -392 + }, + { + "name" : "minecraft:deepslate_brick_stairs", + "id" : -393 + }, + { + "name" : "minecraft:deepslate_brick_wall", + "id" : -394 + }, + { + "name" : "minecraft:deepslate_bricks", + "id" : -391 + }, + { + "name" : "minecraft:deepslate_coal_ore", + "id" : -406 + }, + { + "name" : "minecraft:deepslate_copper_ore", + "id" : -408 + }, + { + "name" : "minecraft:deepslate_diamond_ore", + "id" : -405 + }, + { + "name" : "minecraft:deepslate_emerald_ore", + "id" : -407 + }, + { + "name" : "minecraft:deepslate_gold_ore", + "id" : -402 + }, + { + "name" : "minecraft:deepslate_iron_ore", + "id" : -401 + }, + { + "name" : "minecraft:deepslate_lapis_ore", + "id" : -400 + }, + { + "name" : "minecraft:deepslate_redstone_ore", + "id" : -403 + }, + { + "name" : "minecraft:deepslate_tile_double_slab", + "id" : -398 + }, + { + "name" : "minecraft:deepslate_tile_slab", + "id" : -388 + }, + { + "name" : "minecraft:deepslate_tile_stairs", + "id" : -389 + }, + { + "name" : "minecraft:deepslate_tile_wall", + "id" : -390 + }, + { + "name" : "minecraft:deepslate_tiles", + "id" : -387 + }, { "name" : "minecraft:deny", "id" : 211 @@ -837,7 +1025,7 @@ }, { "name" : "minecraft:diamond_horse_armor", - "id" : 523 + "id" : 533 }, { "name" : "minecraft:diamond_leggings", @@ -867,17 +1055,25 @@ "name" : "minecraft:dirt", "id" : 3 }, + { + "name" : "minecraft:dirt_with_roots", + "id" : -318 + }, { "name" : "minecraft:dispenser", "id" : 23 }, { "name" : "minecraft:dolphin_spawn_egg", - "id" : 482 + "id" : 484 }, { "name" : "minecraft:donkey_spawn_egg", - "id" : 463 + "id" : 465 + }, + { + "name" : "minecraft:double_cut_copper_slab", + "id" : -368 }, { "name" : "minecraft:double_plant", @@ -905,7 +1101,7 @@ }, { "name" : "minecraft:dragon_breath", - "id" : 550 + "id" : 560 }, { "name" : "minecraft:dragon_egg", @@ -919,25 +1115,29 @@ "name" : "minecraft:dried_kelp_block", "id" : -139 }, + { + "name" : "minecraft:dripstone_block", + "id" : -317 + }, { "name" : "minecraft:dropper", "id" : 125 }, { "name" : "minecraft:drowned_spawn_egg", - "id" : 481 + "id" : 483 }, { "name" : "minecraft:dye", - "id" : 612 + "id" : 626 }, { "name" : "minecraft:egg", - "id" : 388 + "id" : 390 }, { "name" : "minecraft:elder_guardian_spawn_egg", - "id" : 469 + "id" : 471 }, { "name" : "minecraft:element_0", @@ -1417,11 +1617,11 @@ }, { "name" : "minecraft:elytra", - "id" : 554 + "id" : 564 }, { "name" : "minecraft:emerald", - "id" : 502 + "id" : 512 }, { "name" : "minecraft:emerald_block", @@ -1433,11 +1633,11 @@ }, { "name" : "minecraft:empty_map", - "id" : 505 + "id" : 515 }, { "name" : "minecraft:enchanted_book", - "id" : 511 + "id" : 521 }, { "name" : "minecraft:enchanted_golden_apple", @@ -1457,7 +1657,7 @@ }, { "name" : "minecraft:end_crystal", - "id" : 615 + "id" : 629 }, { "name" : "minecraft:end_gateway", @@ -1485,27 +1685,47 @@ }, { "name" : "minecraft:ender_eye", - "id" : 431 + "id" : 433 }, { "name" : "minecraft:ender_pearl", - "id" : 420 + "id" : 422 }, { "name" : "minecraft:enderman_spawn_egg", - "id" : 440 + "id" : 442 }, { "name" : "minecraft:endermite_spawn_egg", - "id" : 458 + "id" : 460 }, { "name" : "minecraft:evoker_spawn_egg", - "id" : 473 + "id" : 475 }, { "name" : "minecraft:experience_bottle", - "id" : 498 + "id" : 508 + }, + { + "name" : "minecraft:exposed_copper", + "id" : -341 + }, + { + "name" : "minecraft:exposed_cut_copper", + "id" : -348 + }, + { + "name" : "minecraft:exposed_cut_copper_slab", + "id" : -362 + }, + { + "name" : "minecraft:exposed_cut_copper_stairs", + "id" : -355 + }, + { + "name" : "minecraft:exposed_double_cut_copper_slab", + "id" : -369 }, { "name" : "minecraft:farmland", @@ -1525,15 +1745,15 @@ }, { "name" : "minecraft:fermented_spider_eye", - "id" : 426 + "id" : 428 }, { "name" : "minecraft:field_masoned_banner_pattern", - "id" : 575 + "id" : 585 }, { "name" : "minecraft:filled_map", - "id" : 418 + "id" : 420 }, { "name" : "minecraft:fire", @@ -1541,19 +1761,19 @@ }, { "name" : "minecraft:fire_charge", - "id" : 499 - }, - { - "name" : "minecraft:firework_rocket", "id" : 509 }, + { + "name" : "minecraft:firework_rocket", + "id" : 519 + }, { "name" : "minecraft:firework_star", - "id" : 510 + "id" : 520 }, { "name" : "minecraft:fishing_rod", - "id" : 390 + "id" : 392 }, { "name" : "minecraft:fletching_table", @@ -1569,11 +1789,15 @@ }, { "name" : "minecraft:flower_banner_pattern", - "id" : 571 + "id" : 581 }, { "name" : "minecraft:flower_pot", - "id" : 504 + "id" : 514 + }, + { + "name" : "minecraft:flowering_azalea", + "id" : -338 }, { "name" : "minecraft:flowing_lava", @@ -1585,11 +1809,11 @@ }, { "name" : "minecraft:fox_spawn_egg", - "id" : 488 + "id" : 490 }, { "name" : "minecraft:frame", - "id" : 503 + "id" : 513 }, { "name" : "minecraft:frosted_ice", @@ -1601,11 +1825,11 @@ }, { "name" : "minecraft:ghast_spawn_egg", - "id" : 452 + "id" : 454 }, { "name" : "minecraft:ghast_tear", - "id" : 422 + "id" : 424 }, { "name" : "minecraft:gilded_blackstone", @@ -1617,7 +1841,7 @@ }, { "name" : "minecraft:glass_bottle", - "id" : 425 + "id" : 427 }, { "name" : "minecraft:glass_pane", @@ -1625,7 +1849,27 @@ }, { "name" : "minecraft:glistering_melon_slice", - "id" : 432 + "id" : 434 + }, + { + "name" : "minecraft:glow_berries", + "id" : 630 + }, + { + "name" : "minecraft:glow_frame", + "id" : 621 + }, + { + "name" : "minecraft:glow_ink_sac", + "id" : 503 + }, + { + "name" : "minecraft:glow_lichen", + "id" : -411 + }, + { + "name" : "minecraft:glow_squid_spawn_egg", + "id" : 502 }, { "name" : "minecraft:glow_stick", @@ -1641,7 +1885,15 @@ }, { "name" : "minecraft:glowstone_dust", - "id" : 392 + "id" : 394 + }, + { + "name" : "minecraft:goat_horn", + "id" : 622 + }, + { + "name" : "minecraft:goat_spawn_egg", + "id" : 501 }, { "name" : "minecraft:gold_block", @@ -1653,7 +1905,7 @@ }, { "name" : "minecraft:gold_nugget", - "id" : 423 + "id" : 425 }, { "name" : "minecraft:gold_ore", @@ -1689,7 +1941,7 @@ }, { "name" : "minecraft:golden_horse_armor", - "id" : 522 + "id" : 532 }, { "name" : "minecraft:golden_leggings", @@ -1729,7 +1981,7 @@ }, { "name" : "minecraft:gray_dye", - "id" : 401 + "id" : 403 }, { "name" : "minecraft:gray_glazed_terracotta", @@ -1737,7 +1989,7 @@ }, { "name" : "minecraft:green_dye", - "id" : 395 + "id" : 397 }, { "name" : "minecraft:green_glazed_terracotta", @@ -1749,12 +2001,16 @@ }, { "name" : "minecraft:guardian_spawn_egg", - "id" : 459 + "id" : 461 }, { "name" : "minecraft:gunpowder", "id" : 328 }, + { + "name" : "minecraft:hanging_roots", + "id" : -319 + }, { "name" : "minecraft:hard_glass", "id" : 253 @@ -1781,7 +2037,7 @@ }, { "name" : "minecraft:heart_of_the_sea", - "id" : 561 + "id" : 571 }, { "name" : "minecraft:heavy_weighted_pressure_plate", @@ -1789,7 +2045,7 @@ }, { "name" : "minecraft:hoglin_spawn_egg", - "id" : 494 + "id" : 496 }, { "name" : "minecraft:honey_block", @@ -1797,11 +2053,11 @@ }, { "name" : "minecraft:honey_bottle", - "id" : 581 + "id" : 591 }, { "name" : "minecraft:honeycomb", - "id" : 580 + "id" : 590 }, { "name" : "minecraft:honeycomb_block", @@ -1809,19 +2065,19 @@ }, { "name" : "minecraft:hopper", - "id" : 517 + "id" : 527 }, { "name" : "minecraft:hopper_minecart", - "id" : 516 + "id" : 526 }, { "name" : "minecraft:horse_spawn_egg", - "id" : 456 + "id" : 458 }, { "name" : "minecraft:husk_spawn_egg", - "id" : 461 + "id" : 463 }, { "name" : "minecraft:ice", @@ -1829,7 +2085,11 @@ }, { "name" : "minecraft:ice_bomb", - "id" : 584 + "id" : 594 + }, + { + "name" : "minecraft:infested_deepslate", + "id" : -454 }, { "name" : "minecraft:info_update", @@ -1841,7 +2101,7 @@ }, { "name" : "minecraft:ink_sac", - "id" : 411 + "id" : 413 }, { "name" : "minecraft:invisiblebedrock", @@ -1869,7 +2129,7 @@ }, { "name" : "minecraft:iron_door", - "id" : 370 + "id" : 372 }, { "name" : "minecraft:iron_helmet", @@ -1881,7 +2141,7 @@ }, { "name" : "minecraft:iron_horse_armor", - "id" : 521 + "id" : 531 }, { "name" : "minecraft:iron_ingot", @@ -1893,7 +2153,7 @@ }, { "name" : "minecraft:iron_nugget", - "id" : 559 + "id" : 569 }, { "name" : "minecraft:iron_ore", @@ -1967,6 +2227,10 @@ "name" : "minecraft:item.frame", "id" : 199 }, + { + "name" : "minecraft:item.glow_frame", + "id" : -339 + }, { "name" : "minecraft:item.hopper", "id" : 154 @@ -1983,10 +2247,6 @@ "name" : "minecraft:item.kelp", "id" : -138 }, - { - "name" : "minecraft:nether_brick", - "id" : 112 - }, { "name" : "minecraft:item.nether_sprouts", "id" : -238 @@ -2033,7 +2293,7 @@ }, { "name" : "minecraft:jungle_boat", - "id" : 375 + "id" : 377 }, { "name" : "minecraft:jungle_button", @@ -2041,7 +2301,7 @@ }, { "name" : "minecraft:jungle_door", - "id" : 545 + "id" : 555 }, { "name" : "minecraft:jungle_fence_gate", @@ -2053,7 +2313,7 @@ }, { "name" : "minecraft:jungle_sign", - "id" : 568 + "id" : 578 }, { "name" : "minecraft:jungle_stairs", @@ -2073,7 +2333,7 @@ }, { "name" : "minecraft:kelp", - "id" : 380 + "id" : 382 }, { "name" : "minecraft:ladder", @@ -2089,12 +2349,16 @@ }, { "name" : "minecraft:lapis_lazuli", - "id" : 412 + "id" : 414 }, { "name" : "minecraft:lapis_ore", "id" : 21 }, + { + "name" : "minecraft:large_amethyst_bud", + "id" : -330 + }, { "name" : "minecraft:lava", "id" : 11 @@ -2109,11 +2373,11 @@ }, { "name" : "minecraft:lead", - "id" : 537 + "id" : 547 }, { "name" : "minecraft:leather", - "id" : 379 + "id" : 381 }, { "name" : "minecraft:leather_boots", @@ -2129,7 +2393,7 @@ }, { "name" : "minecraft:leather_horse_armor", - "id" : 520 + "id" : 530 }, { "name" : "minecraft:leather_leggings", @@ -2157,7 +2421,7 @@ }, { "name" : "minecraft:light_blue_dye", - "id" : 405 + "id" : 407 }, { "name" : "minecraft:light_blue_glazed_terracotta", @@ -2165,15 +2429,19 @@ }, { "name" : "minecraft:light_gray_dye", - "id" : 400 + "id" : 402 }, { "name" : "minecraft:light_weighted_pressure_plate", "id" : 147 }, + { + "name" : "minecraft:lightning_rod", + "id" : -312 + }, { "name" : "minecraft:lime_dye", - "id" : 403 + "id" : 405 }, { "name" : "minecraft:lime_glazed_terracotta", @@ -2181,12 +2449,16 @@ }, { "name" : "minecraft:lingering_potion", - "id" : 552 + "id" : 562 }, { "name" : "minecraft:lit_blast_furnace", "id" : -214 }, + { + "name" : "minecraft:lit_deepslate_redstone_ore", + "id" : -404 + }, { "name" : "minecraft:lit_furnace", "id" : 62 @@ -2209,7 +2481,7 @@ }, { "name" : "minecraft:llama_spawn_egg", - "id" : 471 + "id" : 473 }, { "name" : "minecraft:lodestone", @@ -2217,7 +2489,7 @@ }, { "name" : "minecraft:lodestone_compass", - "id" : 590 + "id" : 600 }, { "name" : "minecraft:log", @@ -2233,7 +2505,7 @@ }, { "name" : "minecraft:magenta_dye", - "id" : 406 + "id" : 408 }, { "name" : "minecraft:magenta_glazed_terracotta", @@ -2245,15 +2517,19 @@ }, { "name" : "minecraft:magma_cream", - "id" : 428 + "id" : 430 }, { "name" : "minecraft:magma_cube_spawn_egg", - "id" : 453 + "id" : 455 }, { "name" : "minecraft:medicine", - "id" : 588 + "id" : 598 + }, + { + "name" : "minecraft:medium_amethyst_bud", + "id" : -331 }, { "name" : "minecraft:melon_block", @@ -2277,7 +2553,7 @@ }, { "name" : "minecraft:minecart", - "id" : 368 + "id" : 370 }, { "name" : "minecraft:mob_spawner", @@ -2285,7 +2561,7 @@ }, { "name" : "minecraft:mojang_banner_pattern", - "id" : 574 + "id" : 584 }, { "name" : "minecraft:monster_egg", @@ -2293,7 +2569,15 @@ }, { "name" : "minecraft:mooshroom_spawn_egg", - "id" : 438 + "id" : 440 + }, + { + "name" : "minecraft:moss_block", + "id" : -320 + }, + { + "name" : "minecraft:moss_carpet", + "id" : -335 }, { "name" : "minecraft:mossy_cobblestone", @@ -2313,7 +2597,7 @@ }, { "name" : "minecraft:mule_spawn_egg", - "id" : 464 + "id" : 466 }, { "name" : "minecraft:mushroom_stew", @@ -2321,59 +2605,59 @@ }, { "name" : "minecraft:music_disc_11", - "id" : 534 + "id" : 544 }, { "name" : "minecraft:music_disc_13", - "id" : 524 + "id" : 534 }, { "name" : "minecraft:music_disc_blocks", - "id" : 526 + "id" : 536 }, { "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_chirp", + "id" : 537 + }, + { + "name" : "minecraft:music_disc_far", + "id" : 538 + }, + { + "name" : "minecraft:music_disc_mall", + "id" : 539 + }, + { + "name" : "minecraft:music_disc_mellohi", + "id" : 540 + }, + { + "name" : "minecraft:music_disc_pigstep", + "id" : 618 + }, + { + "name" : "minecraft:music_disc_stal", + "id" : 541 + }, + { + "name" : "minecraft:music_disc_strad", + "id" : 542 + }, + { + "name" : "minecraft:music_disc_wait", + "id" : 545 + }, { "name" : "minecraft:music_disc_ward", - "id" : 533 + "id" : 543 }, { "name" : "minecraft:mutton", - "id" : 540 + "id" : 550 }, { "name" : "minecraft:mycelium", @@ -2381,15 +2665,15 @@ }, { "name" : "minecraft:name_tag", - "id" : 538 + "id" : 548 }, { "name" : "minecraft:nautilus_shell", - "id" : 560 + "id" : 570 }, { - "name" : "minecraft:netherbrick", - "id" : 513 + "name" : "minecraft:nether_brick", + "id" : 112 }, { "name" : "minecraft:nether_brick_fence", @@ -2405,11 +2689,11 @@ }, { "name" : "minecraft:nether_sprouts", - "id" : 609 + "id" : 619 }, { "name" : "minecraft:nether_star", - "id" : 508 + "id" : 518 }, { "name" : "minecraft:nether_wart", @@ -2419,9 +2703,13 @@ "name" : "minecraft:nether_wart_block", "id" : 214 }, + { + "name" : "minecraft:netherbrick", + "id" : 523 + }, { "name" : "minecraft:netherite_axe", - "id" : 595 + "id" : 605 }, { "name" : "minecraft:netherite_block", @@ -2429,43 +2717,43 @@ }, { "name" : "minecraft:netherite_boots", - "id" : 600 + "id" : 610 }, { "name" : "minecraft:netherite_chestplate", - "id" : 598 + "id" : 608 }, { "name" : "minecraft:netherite_helmet", - "id" : 597 + "id" : 607 }, { "name" : "minecraft:netherite_hoe", - "id" : 596 + "id" : 606 }, { "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_leggings", + "id" : 609 + }, + { + "name" : "minecraft:netherite_pickaxe", + "id" : 604 + }, + { + "name" : "minecraft:netherite_scrap", + "id" : 611 + }, { "name" : "minecraft:netherite_shovel", - "id" : 593 + "id" : 603 }, { "name" : "minecraft:netherite_sword", - "id" : 592 + "id" : 602 }, { "name" : "minecraft:netherrack", @@ -2485,11 +2773,11 @@ }, { "name" : "minecraft:npc_spawn_egg", - "id" : 468 + "id" : 470 }, { "name" : "minecraft:oak_boat", - "id" : 373 + "id" : 375 }, { "name" : "minecraft:oak_sign", @@ -2509,16 +2797,36 @@ }, { "name" : "minecraft:ocelot_spawn_egg", - "id" : 449 + "id" : 451 }, { "name" : "minecraft:orange_dye", - "id" : 407 + "id" : 409 }, { "name" : "minecraft:orange_glazed_terracotta", "id" : 221 }, + { + "name" : "minecraft:oxidized_copper", + "id" : -343 + }, + { + "name" : "minecraft:oxidized_cut_copper", + "id" : -350 + }, + { + "name" : "minecraft:oxidized_cut_copper_slab", + "id" : -364 + }, + { + "name" : "minecraft:oxidized_cut_copper_stairs", + "id" : -357 + }, + { + "name" : "minecraft:oxidized_double_cut_copper_slab", + "id" : -371 + }, { "name" : "minecraft:packed_ice", "id" : 174 @@ -2529,47 +2837,47 @@ }, { "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:paper", + "id" : 386 + }, + { + "name" : "minecraft:parrot_spawn_egg", + "id" : 478 + }, + { + "name" : "minecraft:phantom_membrane", + "id" : 574 + }, + { + "name" : "minecraft:phantom_spawn_egg", + "id" : 486 + }, + { + "name" : "minecraft:pig_spawn_egg", + "id" : 437 + }, + { + "name" : "minecraft:piglin_banner_pattern", + "id" : 587 + }, + { + "name" : "minecraft:piglin_brute_spawn_egg", + "id" : 499 + }, + { + "name" : "minecraft:piglin_spawn_egg", + "id" : 497 + }, + { + "name" : "minecraft:pillager_spawn_egg", + "id" : 491 + }, { "name" : "minecraft:pink_dye", - "id" : 402 + "id" : 404 }, { "name" : "minecraft:pink_glazed_terracotta", @@ -2591,13 +2899,17 @@ "name" : "minecraft:podzol", "id" : 243 }, + { + "name" : "minecraft:pointed_dripstone", + "id" : -308 + }, { "name" : "minecraft:poisonous_potato", "id" : 282 }, { "name" : "minecraft:polar_bear_spawn_egg", - "id" : 470 + "id" : 472 }, { "name" : "minecraft:polished_andesite_stairs", @@ -2655,6 +2967,26 @@ "name" : "minecraft:polished_blackstone_wall", "id" : -297 }, + { + "name" : "minecraft:polished_deepslate", + "id" : -383 + }, + { + "name" : "minecraft:polished_deepslate_double_slab", + "id" : -397 + }, + { + "name" : "minecraft:polished_deepslate_slab", + "id" : -384 + }, + { + "name" : "minecraft:polished_deepslate_stairs", + "id" : -385 + }, + { + "name" : "minecraft:polished_deepslate_wall", + "id" : -386 + }, { "name" : "minecraft:polished_diorite_stairs", "id" : -173 @@ -2665,7 +2997,7 @@ }, { "name" : "minecraft:popped_chorus_fruit", - "id" : 549 + "id" : 559 }, { "name" : "minecraft:porkchop", @@ -2685,7 +3017,15 @@ }, { "name" : "minecraft:potion", - "id" : 424 + "id" : 426 + }, + { + "name" : "minecraft:powder_snow", + "id" : -306 + }, + { + "name" : "minecraft:powder_snow_bucket", + "id" : 368 }, { "name" : "minecraft:powered_comparator", @@ -2705,11 +3045,11 @@ }, { "name" : "minecraft:prismarine_crystals", - "id" : 539 + "id" : 549 }, { "name" : "minecraft:prismarine_shard", - "id" : 555 + "id" : 565 }, { "name" : "minecraft:prismarine_stairs", @@ -2725,7 +3065,7 @@ }, { "name" : "minecraft:pufferfish_spawn_egg", - "id" : 479 + "id" : 481 }, { "name" : "minecraft:pumpkin", @@ -2745,7 +3085,7 @@ }, { "name" : "minecraft:purple_dye", - "id" : 398 + "id" : 400 }, { "name" : "minecraft:purple_glazed_terracotta", @@ -2761,7 +3101,7 @@ }, { "name" : "minecraft:quartz", - "id" : 514 + "id" : 524 }, { "name" : "minecraft:quartz_block", @@ -2785,15 +3125,15 @@ }, { "name" : "minecraft:rabbit_foot", - "id" : 518 + "id" : 528 }, { "name" : "minecraft:rabbit_hide", - "id" : 519 + "id" : 529 }, { "name" : "minecraft:rabbit_spawn_egg", - "id" : 457 + "id" : 459 }, { "name" : "minecraft:rabbit_stew", @@ -2805,11 +3145,35 @@ }, { "name" : "minecraft:rapid_fertilizer", - "id" : 586 + "id" : 596 }, { "name" : "minecraft:ravager_spawn_egg", - "id" : 491 + "id" : 493 + }, + { + "name" : "minecraft:raw_copper", + "id" : 507 + }, + { + "name" : "minecraft:raw_copper_block", + "id" : -452 + }, + { + "name" : "minecraft:raw_gold", + "id" : 506 + }, + { + "name" : "minecraft:raw_gold_block", + "id" : -453 + }, + { + "name" : "minecraft:raw_iron", + "id" : 505 + }, + { + "name" : "minecraft:raw_iron_block", + "id" : -451 }, { "name" : "minecraft:real_double_stone_slab", @@ -2829,7 +3193,7 @@ }, { "name" : "minecraft:red_dye", - "id" : 394 + "id" : 396 }, { "name" : "minecraft:red_flower", @@ -2865,7 +3229,7 @@ }, { "name" : "minecraft:redstone", - "id" : 371 + "id" : 373 }, { "name" : "minecraft:redstone_block", @@ -2889,7 +3253,7 @@ }, { "name" : "minecraft:repeater", - "id" : 417 + "id" : 419 }, { "name" : "minecraft:repeating_command_block", @@ -2909,7 +3273,7 @@ }, { "name" : "minecraft:saddle", - "id" : 369 + "id" : 371 }, { "name" : "minecraft:salmon", @@ -2921,7 +3285,7 @@ }, { "name" : "minecraft:salmon_spawn_egg", - "id" : 480 + "id" : 482 }, { "name" : "minecraft:sand", @@ -2943,9 +3307,13 @@ "name" : "minecraft:scaffolding", "id" : -165 }, + { + "name" : "minecraft:sculk_sensor", + "id" : -307 + }, { "name" : "minecraft:scute", - "id" : 562 + "id" : 572 }, { "name" : "minecraft:sea_pickle", @@ -2961,11 +3329,11 @@ }, { "name" : "minecraft:shears", - "id" : 419 + "id" : 421 }, { "name" : "minecraft:sheep_spawn_egg", - "id" : 436 + "id" : 438 }, { "name" : "minecraft:shield", @@ -2981,11 +3349,11 @@ }, { "name" : "minecraft:shulker_shell", - "id" : 556 + "id" : 566 }, { "name" : "minecraft:shulker_spawn_egg", - "id" : 467 + "id" : 469 }, { "name" : "minecraft:silver_glazed_terracotta", @@ -2993,23 +3361,23 @@ }, { "name" : "minecraft:silverfish_spawn_egg", - "id" : 441 + "id" : 443 }, { "name" : "minecraft:skeleton_horse_spawn_egg", - "id" : 465 + "id" : 467 }, { "name" : "minecraft:skeleton_spawn_egg", - "id" : 442 + "id" : 444 }, { "name" : "minecraft:skull", - "id" : 506 + "id" : 516 }, { "name" : "minecraft:skull_banner_pattern", - "id" : 573 + "id" : 583 }, { "name" : "minecraft:slime", @@ -3017,11 +3385,19 @@ }, { "name" : "minecraft:slime_ball", - "id" : 386 + "id" : 388 }, { "name" : "minecraft:slime_spawn_egg", - "id" : 443 + "id" : 445 + }, + { + "name" : "minecraft:small_amethyst_bud", + "id" : -332 + }, + { + "name" : "minecraft:small_dripleaf_block", + "id" : -336 }, { "name" : "minecraft:smithing_table", @@ -3031,6 +3407,10 @@ "name" : "minecraft:smoker", "id" : -198 }, + { + "name" : "minecraft:smooth_basalt", + "id" : -377 + }, { "name" : "minecraft:smooth_quartz_stairs", "id" : -185 @@ -3057,11 +3437,11 @@ }, { "name" : "minecraft:snowball", - "id" : 372 + "id" : 374 }, { "name" : "minecraft:soul_campfire", - "id" : 610 + "id" : 620 }, { "name" : "minecraft:soul_fire", @@ -3085,11 +3465,11 @@ }, { "name" : "minecraft:sparkler", - "id" : 589 + "id" : 599 }, { "name" : "minecraft:spawn_egg", - "id" : 614 + "id" : 628 }, { "name" : "minecraft:spider_eye", @@ -3097,19 +3477,23 @@ }, { "name" : "minecraft:spider_spawn_egg", - "id" : 444 + "id" : 446 }, { "name" : "minecraft:splash_potion", - "id" : 551 + "id" : 561 }, { "name" : "minecraft:sponge", "id" : 19 }, + { + "name" : "minecraft:spore_blossom", + "id" : -321 + }, { "name" : "minecraft:spruce_boat", - "id" : 376 + "id" : 378 }, { "name" : "minecraft:spruce_button", @@ -3117,7 +3501,7 @@ }, { "name" : "minecraft:spruce_door", - "id" : 543 + "id" : 553 }, { "name" : "minecraft:spruce_fence_gate", @@ -3129,7 +3513,7 @@ }, { "name" : "minecraft:spruce_sign", - "id" : 566 + "id" : 576 }, { "name" : "minecraft:spruce_stairs", @@ -3147,9 +3531,13 @@ "name" : "minecraft:spruce_wall_sign", "id" : -182 }, + { + "name" : "minecraft:spyglass", + "id" : 624 + }, { "name" : "minecraft:squid_spawn_egg", - "id" : 448 + "id" : 450 }, { "name" : "minecraft:stained_glass", @@ -3237,11 +3625,11 @@ }, { "name" : "minecraft:stray_spawn_egg", - "id" : 460 + "id" : 462 }, { "name" : "minecraft:strider_spawn_egg", - "id" : 493 + "id" : 495 }, { "name" : "minecraft:string", @@ -3297,15 +3685,15 @@ }, { "name" : "minecraft:sugar", - "id" : 414 + "id" : 416 }, { "name" : "minecraft:sugar_cane", - "id" : 383 + "id" : 385 }, { "name" : "minecraft:suspicious_stew", - "id" : 579 + "id" : 589 }, { "name" : "minecraft:sweet_berries", @@ -3323,13 +3711,17 @@ "name" : "minecraft:target", "id" : -239 }, + { + "name" : "minecraft:tinted_glass", + "id" : -334 + }, { "name" : "minecraft:tnt", "id" : 46 }, { "name" : "minecraft:tnt_minecart", - "id" : 515 + "id" : 525 }, { "name" : "minecraft:torch", @@ -3337,7 +3729,7 @@ }, { "name" : "minecraft:totem_of_undying", - "id" : 558 + "id" : 568 }, { "name" : "minecraft:trapdoor", @@ -3349,7 +3741,7 @@ }, { "name" : "minecraft:trident", - "id" : 536 + "id" : 546 }, { "name" : "minecraft:tripwire", @@ -3369,7 +3761,11 @@ }, { "name" : "minecraft:tropical_fish_spawn_egg", - "id" : 477 + "id" : 479 + }, + { + "name" : "minecraft:tuff", + "id" : -333 }, { "name" : "minecraft:turtle_egg", @@ -3377,11 +3773,11 @@ }, { "name" : "minecraft:turtle_helmet", - "id" : 563 + "id" : 573 }, { "name" : "minecraft:turtle_spawn_egg", - "id" : 483 + "id" : 485 }, { "name" : "minecraft:twisting_vines", @@ -3413,15 +3809,15 @@ }, { "name" : "minecraft:vex_spawn_egg", - "id" : 474 + "id" : 476 }, { "name" : "minecraft:villager_spawn_egg", - "id" : 447 + "id" : 449 }, { "name" : "minecraft:vindicator_spawn_egg", - "id" : 472 + "id" : 474 }, { "name" : "minecraft:vine", @@ -3437,7 +3833,7 @@ }, { "name" : "minecraft:wandering_trader_spawn_egg", - "id" : 490 + "id" : 492 }, { "name" : "minecraft:warped_button", @@ -3445,7 +3841,7 @@ }, { "name" : "minecraft:warped_door", - "id" : 605 + "id" : 615 }, { "name" : "minecraft:warped_double_slab", @@ -3465,7 +3861,7 @@ }, { "name" : "minecraft:warped_fungus_on_a_stick", - "id" : 606 + "id" : 616 }, { "name" : "minecraft:warped_hyphae", @@ -3489,7 +3885,7 @@ }, { "name" : "minecraft:warped_sign", - "id" : 603 + "id" : 613 }, { "name" : "minecraft:warped_slab", @@ -3531,6 +3927,106 @@ "name" : "minecraft:waterlily", "id" : 111 }, + { + "name" : "minecraft:waxed_copper", + "id" : -344 + }, + { + "name" : "minecraft:waxed_cut_copper", + "id" : -351 + }, + { + "name" : "minecraft:waxed_cut_copper_slab", + "id" : -365 + }, + { + "name" : "minecraft:waxed_cut_copper_stairs", + "id" : -358 + }, + { + "name" : "minecraft:waxed_double_cut_copper_slab", + "id" : -372 + }, + { + "name" : "minecraft:waxed_exposed_copper", + "id" : -345 + }, + { + "name" : "minecraft:waxed_exposed_cut_copper", + "id" : -352 + }, + { + "name" : "minecraft:waxed_exposed_cut_copper_slab", + "id" : -366 + }, + { + "name" : "minecraft:waxed_exposed_cut_copper_stairs", + "id" : -359 + }, + { + "name" : "minecraft:waxed_exposed_double_cut_copper_slab", + "id" : -373 + }, + { + "name" : "minecraft:waxed_oxidized_copper", + "id" : -446 + }, + { + "name" : "minecraft:waxed_oxidized_cut_copper", + "id" : -447 + }, + { + "name" : "minecraft:waxed_oxidized_cut_copper_slab", + "id" : -449 + }, + { + "name" : "minecraft:waxed_oxidized_cut_copper_stairs", + "id" : -448 + }, + { + "name" : "minecraft:waxed_oxidized_double_cut_copper_slab", + "id" : -450 + }, + { + "name" : "minecraft:waxed_weathered_copper", + "id" : -346 + }, + { + "name" : "minecraft:waxed_weathered_cut_copper", + "id" : -353 + }, + { + "name" : "minecraft:waxed_weathered_cut_copper_slab", + "id" : -367 + }, + { + "name" : "minecraft:waxed_weathered_cut_copper_stairs", + "id" : -360 + }, + { + "name" : "minecraft:waxed_weathered_double_cut_copper_slab", + "id" : -374 + }, + { + "name" : "minecraft:weathered_copper", + "id" : -342 + }, + { + "name" : "minecraft:weathered_cut_copper", + "id" : -349 + }, + { + "name" : "minecraft:weathered_cut_copper_slab", + "id" : -363 + }, + { + "name" : "minecraft:weathered_cut_copper_stairs", + "id" : -356 + }, + { + "name" : "minecraft:weathered_double_cut_copper_slab", + "id" : -370 + }, { "name" : "minecraft:web", "id" : 30 @@ -3549,7 +4045,7 @@ }, { "name" : "minecraft:white_dye", - "id" : 408 + "id" : 410 }, { "name" : "minecraft:white_glazed_terracotta", @@ -3557,7 +4053,7 @@ }, { "name" : "minecraft:witch_spawn_egg", - "id" : 450 + "id" : 452 }, { "name" : "minecraft:wither_rose", @@ -3565,11 +4061,11 @@ }, { "name" : "minecraft:wither_skeleton_spawn_egg", - "id" : 462 + "id" : 464 }, { "name" : "minecraft:wolf_spawn_egg", - "id" : 437 + "id" : 439 }, { "name" : "minecraft:wood", @@ -3617,15 +4113,15 @@ }, { "name" : "minecraft:writable_book", - "id" : 500 + "id" : 510 }, { "name" : "minecraft:written_book", - "id" : 501 + "id" : 511 }, { "name" : "minecraft:yellow_dye", - "id" : 404 + "id" : 406 }, { "name" : "minecraft:yellow_flower", @@ -3637,22 +4133,22 @@ }, { "name" : "minecraft:zoglin_spawn_egg", - "id" : 496 + "id" : 498 }, { "name" : "minecraft:zombie_horse_spawn_egg", - "id" : 466 + "id" : 468 }, { "name" : "minecraft:zombie_pigman_spawn_egg", - "id" : 446 + "id" : 448 }, { "name" : "minecraft:zombie_spawn_egg", - "id" : 445 + "id" : 447 }, { "name" : "minecraft:zombie_villager_spawn_egg", - "id" : 475 + "id" : 477 } ] \ No newline at end of file diff --git a/connector/src/main/resources/config.yml b/connector/src/main/resources/config.yml index 75070b8ee..79f7bb6bf 100644 --- a/connector/src/main/resources/config.yml +++ b/connector/src/main/resources/config.yml @@ -58,9 +58,9 @@ remote: forward-hostname: false # Floodgate uses encryption to ensure use from authorised sources. -# This should point to the public key generated by Floodgate (Bungee or CraftBukkit) +# This should point to the public key generated by Floodgate (BungeeCord, Spigot or Velocity) # You can ignore this when not using Floodgate. -floodgate-key-file: public-key.pem +floodgate-key-file: key.pem # The Xbox/Minecraft Bedrock username is the key for the Java server auth-info. # This allows automatic configuration/login to the remote Java server. @@ -134,18 +134,6 @@ emote-offhand-workaround: "disabled" # The default locale if we dont have the one the client requested. Uncomment to not use the default system language. # default-locale: en_us -# Configures if chunk caching should be enabled or not. This keeps an individual -# record of each block the client loads in. This feature does allow for a few things -# such as more accurate movement that causes less problems with anticheat (meaning -# you're less likely to be banned) and allows block break animations to show up in -# creative mode (and other features). Although this increases RAM usage, it likely -# won't have much of an effect for the vast majority of people. However, if you're -# running out of RAM or are in a RAM-sensitive environment, you may want to disable -# this. When using the Spigot version of Geyser, support for features or -# implementations this allows is automatically enabled without the additional caching -# as Geyser has direct access to the server itself. -cache-chunks: true - # Specify how many days images will be cached to disk to save downloading them from the internet. # A value of 0 is disabled. (Default: 0) cache-images: 0 diff --git a/connector/src/main/resources/languages b/connector/src/main/resources/languages index e1e8fd6c2..fcc8f01e2 160000 --- a/connector/src/main/resources/languages +++ b/connector/src/main/resources/languages @@ -1 +1 @@ -Subproject commit e1e8fd6c2b8abf366e60085c23a55a2c943806ae +Subproject commit fcc8f01e25e481c9c82f2ee9bff23111ec781dd7 diff --git a/connector/src/main/resources/mappings b/connector/src/main/resources/mappings index c5925b01c..cc93558bd 160000 --- a/connector/src/main/resources/mappings +++ b/connector/src/main/resources/mappings @@ -1 +1 @@ -Subproject commit c5925b01cf8e7d8b284cf359e927145b9b4694aa +Subproject commit cc93558bde00d6f081b8339ef53cf157d242d3f1 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 index 7052123fe..efa1a8b3d 100644 --- 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 @@ -46,7 +46,7 @@ public class MessageTranslatorTest { // 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\":\"\"}", - " §r§6The §r§d§k||§r§b§lCubeCraft§r§d§k||§r§6 Network §r§a[1.8/1.9+]\n" + + " §r§6The §r§c§k||§r§b§lCubeCraft§r§c§k||§r§6 Network §r§a[1.8/1.9+]\n" + " §r§e✦ §r§d§lN§r§d§lE§r§d§lW§r§d§l:§r§d§l §r§e§lA§r§e§lM§r§e§lO§r§a§lN§r§a§lG§r§a§l §r§a§lS§r§2§lL§r§2§lI§r§2§lM§r§2§lE§r§2§lS§r§e §r§b(kinda sus) §r§e✦"); // Color code format resetting diff --git a/pom.xml b/pom.xml index 554fd5e3b..5b3e5a5f2 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ 4.0.0 org.geysermc geyser-parent - 1.2.1-SNAPSHOT + 1.4.0-SNAPSHOT pom Geyser Allows for players from Minecraft Bedrock Edition to join Minecraft Java Edition servers. @@ -61,10 +61,6 @@ true - - viaversion-repo - https://repo.viaversion.com - sonatype https://oss.sonatype.org/content/repositories/snapshots/