diff --git a/README.md b/README.md index 874c79f3..9878fa5a 100644 --- a/README.md +++ b/README.md @@ -37,7 +37,6 @@ Take a look [here](https://github.com/GeyserMC/Geyser/wiki#Setup) for how to set - [ ] Beacon - [ ] Cartography Table - [ ] Stonecutter - - [ ] Villager Trading - Some Entity Flags ## Compiling 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 d983aec1..839fc185 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 @@ -25,216 +25,29 @@ package org.geysermc.platform.bungeecord; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import lombok.Getter; import net.md_5.bungee.api.plugin.Plugin; import net.md_5.bungee.config.Configuration; import org.geysermc.connector.FloodgateKeyLoader; -import org.geysermc.connector.GeyserConfiguration; +import org.geysermc.connector.configuration.GeyserJacksonConfiguration; -import java.io.File; import java.nio.file.Path; import java.nio.file.Paths; -import java.util.HashMap; -import java.util.Map; -public class GeyserBungeeConfiguration implements GeyserConfiguration { - - private File dataFolder; - private Configuration config; - - private BungeeBedrockConfiguration bedrockConfig; - private BungeeRemoteConfiguration remoteConfig; - private BungeeMetricsInfo metricsInfo; - - private Map userAuthInfo = new HashMap<>(); +@Getter +@JsonIgnoreProperties(ignoreUnknown = true) +public class GeyserBungeeConfiguration extends GeyserJacksonConfiguration { private Path floodgateKey; - public GeyserBungeeConfiguration(File dataFolder, Configuration config) { - this.dataFolder = dataFolder; - this.config = config; - - bedrockConfig = new BungeeBedrockConfiguration(); - remoteConfig = new BungeeRemoteConfiguration(); - metricsInfo = new BungeeMetricsInfo(); - - if (!config.contains("userAuths")) - return; - - for (String key : config.getSection("userAuths").getKeys()) { - userAuthInfo.put(key, new BungeeUserAuthenticationInfo(key)); - } - } - - public void loadFloodgate(GeyserBungeePlugin plugin) { + public void loadFloodgate(GeyserBungeePlugin plugin, Configuration configuration) { Plugin floodgate = plugin.getProxy().getPluginManager().getPlugin("floodgate-bungee"); - floodgateKey = FloodgateKeyLoader.getKey(plugin.getGeyserLogger(), this, Paths.get(dataFolder.toString(), config.getString("floodgate-key-file", "public-key.pem")), floodgate, floodgate != null ? floodgate.getDataFolder().toPath() : null); - } - - @Override - public BungeeBedrockConfiguration getBedrock() { - return bedrockConfig; - } - - @Override - public BungeeRemoteConfiguration getRemote() { - return remoteConfig; - } - - @Override - public Map getUserAuths() { - return userAuthInfo; - } - - @Override - public boolean isCommandSuggestions() { - return config.getBoolean("command-suggestions", true); - } - - @Override - public boolean isPassthroughMotd() { - return config.getBoolean("passthrough-motd", false); - } - - @Override - public boolean isPassthroughPlayerCounts() { - return config.getBoolean("passthrough-player-counts", false); - } - - @Override - public boolean isLegacyPingPassthrough() { - return config.getBoolean("legacy-ping-passthrough", false); - } - - @Override - public int getPingPassthroughInterval() { - return config.getInt("ping-passthrough-interval", 3); - } - - @Override - public int getMaxPlayers() { - return config.getInt("max-players", 10); - } - - @Override - public boolean isDebugMode() { - return config.getBoolean("debug-mode", false); - } - - @Override - public int getGeneralThreadPool() { - return config.getInt("general-thread-pool", 32); - } - - @Override - public boolean isAllowThirdPartyCapes() { - return config.getBoolean("allow-third-party-capes", true); - } - - @Override - public boolean isAllowThirdPartyEars() { - return config.getBoolean("allow-third-party-ears", false); - } - - @Override - public String getDefaultLocale() { - return config.getString("default-locale", "en_us"); + floodgateKey = FloodgateKeyLoader.getKey(plugin.getGeyserLogger(), this, Paths.get(plugin.getDataFolder().toString(), configuration.getString("floodgate-key-file"), "public-key.pem"), floodgate, floodgate != null ? floodgate.getDataFolder().toPath() : null); } @Override public Path getFloodgateKeyFile() { return floodgateKey; } - - @Override - public boolean isCacheChunks() { - return config.getBoolean("cache-chunks", false); - } - - @Override - public boolean isAboveBedrockNetherBuilding() { - return config.getBoolean("above-bedrock-nether-building", false); - } - - @Override - public BungeeMetricsInfo getMetrics() { - return metricsInfo; - } - - public class BungeeBedrockConfiguration implements IBedrockConfiguration { - - @Override - public String getAddress() { - return config.getString("bedrock.address", "0.0.0.0"); - } - - @Override - public int getPort() { - return config.getInt("bedrock.port", 25565); - } - - @Override - public String getMotd1() { - return config.getString("bedrock.motd1", "GeyserMC"); - } - - @Override - public String getMotd2() { - return config.getString("bedrock.motd2", "GeyserMC"); - } - } - - public class BungeeRemoteConfiguration implements IRemoteConfiguration { - - @Override - public String getAddress() { - return config.getString("remote.address", "127.0.0.1"); - } - - @Override - public int getPort() { - return config.getInt("remote.port", 25565); - } - - @Override - public String getAuthType() { - return config.getString("remote.auth-type", "online"); - } - } - - public class BungeeUserAuthenticationInfo implements IUserAuthenticationInfo { - - private String key; - - public BungeeUserAuthenticationInfo(String key) { - this.key = key; - } - - @Override - public String getEmail() { - return config.getString("userAuths." + key + ".email"); - } - - @Override - public String getPassword() { - return config.getString("userAuths." + key + ".password"); - } - } - - public class BungeeMetricsInfo implements IMetricsInfo { - - @Override - public boolean isEnabled() { - return config.getBoolean("metrics.enabled", true); - } - - @Override - public String getUniqueId() { - return config.getString("metrics.uuid", "generateduuid"); - } - } - - @Override - public int getConfigVersion() { - return config.getInt("config-version", 0); - } } 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 525b9b6d..24793839 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 @@ -31,20 +31,19 @@ import net.md_5.bungee.config.Configuration; import net.md_5.bungee.config.ConfigurationProvider; import net.md_5.bungee.config.YamlConfiguration; import org.geysermc.common.PlatformType; -import org.geysermc.connector.GeyserConfiguration; import org.geysermc.connector.GeyserConnector; import org.geysermc.connector.bootstrap.GeyserBootstrap; import org.geysermc.connector.command.CommandManager; +import org.geysermc.connector.configuration.GeyserConfiguration; import org.geysermc.connector.ping.GeyserLegacyPingPassthrough; import org.geysermc.connector.ping.IGeyserPingPassthrough; +import org.geysermc.connector.utils.FileUtils; import org.geysermc.platform.bungeecord.command.GeyserBungeeCommandExecutor; import org.geysermc.platform.bungeecord.command.GeyserBungeeCommandManager; import java.io.File; import java.io.IOException; -import java.io.InputStream; import java.net.InetSocketAddress; -import java.nio.file.Files; import java.util.UUID; import java.util.logging.Level; @@ -62,32 +61,18 @@ public class GeyserBungeePlugin extends Plugin implements GeyserBootstrap { if (!getDataFolder().exists()) getDataFolder().mkdir(); - File file = new File(getDataFolder(), "config.yml"); Configuration configuration = null; - - if (!file.exists()) { - try (InputStream in = getResourceAsStream("config.yml")) { - Files.copy(in, file.toPath()); - } catch (IOException ex) { - getLogger().log(Level.SEVERE, "Failed to read/create config.yml! Make sure it's up to date and/or readable+writable!", ex); - return; - } - } try { + if (!getDataFolder().exists()) + getDataFolder().mkdir(); + File configFile = FileUtils.fileOrCopiedFromResource(new File(getDataFolder(), "config.yml"), "config.yml", (x) -> x.replaceAll("generateduuid", UUID.randomUUID().toString())); + this.geyserConfig = FileUtils.loadConfig(configFile, GeyserBungeeConfiguration.class); configuration = ConfigurationProvider.getProvider(YamlConfiguration.class).load(new File(getDataFolder(), "config.yml")); - } catch(IOException e) { - e.printStackTrace(); + } catch (IOException ex) { + getLogger().log(Level.WARNING, "Failed to read/create config.yml! Make sure it's up to date and/or readable+writable!", ex); + ex.printStackTrace(); } - if (configuration == null) { - getLogger().severe("Failed to read/create config.yml! Make sure it's up to date and/or readable+writable!"); - return; - } - - this.geyserConfig = new GeyserBungeeConfiguration(getDataFolder(), configuration); - - boolean configHasChanged = false; - if (getProxy().getConfig().getListeners().size() == 1) { ListenerInfo listener = getProxy().getConfig().getListeners().toArray(new ListenerInfo[0])[0]; @@ -96,33 +81,21 @@ public class GeyserBungeePlugin extends Plugin implements GeyserBootstrap { // Don't change the ip if its listening on all interfaces // By default this should be 127.0.0.1 but may need to be changed in some circumstances if (!javaAddr.getHostString().equals("0.0.0.0") && !javaAddr.getHostString().equals("")) { - configuration.set("remote.address", javaAddr.getHostString()); + this.geyserConfig.getRemote().setAddress(javaAddr.getHostString()); } - configuration.set("remote.port", javaAddr.getPort()); - - configHasChanged = true; - } - - if (geyserConfig.getMetrics().getUniqueId().equals("generateduuid")) { - configuration.set("metrics.uuid", UUID.randomUUID().toString()); - - configHasChanged = true; - } - - if (configHasChanged) { - try { - ConfigurationProvider.getProvider(YamlConfiguration.class).save(configuration, new File(getDataFolder(), "config.yml")); - } catch (IOException ex) { - getLogger().log(Level.SEVERE, "Failed to read/create config.yml! Make sure it's up to date and/or readable+writable!", ex); - return; - } + this.geyserConfig.getRemote().setPort(javaAddr.getPort()); } this.geyserLogger = new GeyserBungeeLogger(getLogger(), geyserConfig.isDebugMode()); GeyserConfiguration.checkGeyserConfiguration(geyserConfig, geyserLogger); - geyserConfig.loadFloodgate(this); + if (geyserConfig.getRemote().getAuthType().equals("floodgate") && getProxy().getPluginManager().getPlugin("floodgate-bungee") == null) { + geyserLogger.severe("Auth type set to Floodgate but Floodgate not found! Disabling..."); + return; + } + + geyserConfig.loadFloodgate(this, configuration); this.connector = GeyserConnector.start(PlatformType.BUNGEECORD, this); diff --git a/bootstrap/spigot/pom.xml b/bootstrap/spigot/pom.xml index d9ce9f8d..6ad7a637 100644 --- a/bootstrap/spigot/pom.xml +++ b/bootstrap/spigot/pom.xml @@ -71,6 +71,10 @@ it.unimi.dsi.fastutil org.geysermc.platform.spigot.shaded.fastutil + + com.fasterxml.jackson + org.geysermc.platform.bukkit.shaded.jackson + 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 ffa9c7b3..9a681156 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 @@ -25,120 +25,29 @@ package org.geysermc.platform.spigot; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.Getter; import org.bukkit.Bukkit; -import org.bukkit.configuration.file.FileConfiguration; import org.bukkit.plugin.Plugin; import org.geysermc.connector.FloodgateKeyLoader; -import org.geysermc.connector.GeyserConfiguration; +import org.geysermc.connector.configuration.GeyserJacksonConfiguration; -import java.io.File; import java.nio.file.Path; import java.nio.file.Paths; -import java.util.HashMap; -import java.util.Map; -public class GeyserSpigotConfiguration implements GeyserConfiguration { +@Getter +@JsonIgnoreProperties(ignoreUnknown = true) +public class GeyserSpigotConfiguration extends GeyserJacksonConfiguration { - private FileConfiguration config; - private File dataFolder; - - private BukkitBedrockConfiguration bedrockConfig; - private BukkitRemoteConfiguration remoteConfig; - private BukkitMetricsInfo metricsInfo; - - private Map userAuthInfo = new HashMap<>(); + @JsonProperty("floodgate-key-file") + private String floodgateKeyFile; private Path floodgateKey; - public GeyserSpigotConfiguration(File dataFolder, FileConfiguration config) { - this.dataFolder = dataFolder; - this.config = config; - - bedrockConfig = new BukkitBedrockConfiguration(); - remoteConfig = new BukkitRemoteConfiguration(); - metricsInfo = new BukkitMetricsInfo(); - - if (!config.contains("userAuths")) - return; - - for (String key : config.getConfigurationSection("userAuths").getKeys(false)) { - userAuthInfo.put(key, new BukkitUserAuthenticationInfo(key)); - } - } - public void loadFloodgate(GeyserSpigotPlugin plugin) { Plugin floodgate = Bukkit.getPluginManager().getPlugin("floodgate-bukkit"); - floodgateKey = FloodgateKeyLoader.getKey(plugin.getGeyserLogger(), this, Paths.get(dataFolder.toString(), config.getString("floodgate-key-file", "public-key.pem")), floodgate, floodgate != null ? floodgate.getDataFolder().toPath() : null); - } - - @Override - public IBedrockConfiguration getBedrock() { - return bedrockConfig; - } - - @Override - public IRemoteConfiguration getRemote() { - return remoteConfig; - } - - @Override - public Map getUserAuths() { - return userAuthInfo; - } - - @Override - public boolean isCommandSuggestions() { - return config.getBoolean("command-suggestions", true); - } - - @Override - public boolean isPassthroughMotd() { - return config.getBoolean("passthrough-motd", false); - } - - @Override - public boolean isPassthroughPlayerCounts() { - return config.getBoolean("passthrough-player-counts", false); - } - - @Override - public boolean isLegacyPingPassthrough() { - return config.getBoolean("legacy-ping-passthrough", false); - } - - @Override - public int getPingPassthroughInterval() { - return config.getInt("ping-passthrough-interval", 3); - } - - @Override - public int getMaxPlayers() { - return config.getInt("max-players", 10); - } - - @Override - public boolean isDebugMode() { - return config.getBoolean("debug-mode", false); - } - - @Override - public int getGeneralThreadPool() { - return config.getInt("general-thread-pool", 32); - } - - @Override - public boolean isAllowThirdPartyCapes() { - return config.getBoolean("allow-third-party-capes", true); - } - - @Override - public boolean isAllowThirdPartyEars() { - return config.getBoolean("allow-third-party-ears", false); - } - - @Override - public String getDefaultLocale() { - return config.getString("default-locale", "en_us"); + floodgateKey = FloodgateKeyLoader.getKey(plugin.getGeyserLogger(), this, Paths.get(plugin.getDataFolder().toString(), plugin.getConfig().getString("floodgate-key-file", "public-key.pem")), floodgate, floodgate != null ? floodgate.getDataFolder().toPath() : null); } @Override @@ -150,92 +59,4 @@ public class GeyserSpigotConfiguration implements GeyserConfiguration { public boolean isCacheChunks() { return true; // We override this as with Bukkit, we have direct access to the server implementation } - - @Override - public boolean isAboveBedrockNetherBuilding() { - return config.getBoolean("above-bedrock-nether-building", false); - } - - @Override - public IMetricsInfo getMetrics() { - return metricsInfo; - } - - public class BukkitBedrockConfiguration implements IBedrockConfiguration { - - @Override - public String getAddress() { - return config.getString("bedrock.address", "0.0.0.0"); - } - - @Override - public int getPort() { - return config.getInt("bedrock.port", 25565); - } - - @Override - public String getMotd1() { - return config.getString("bedrock.motd1", "GeyserMC"); - } - - @Override - public String getMotd2() { - return config.getString("bedrock.motd2", "GeyserMC"); - } - } - - public class BukkitRemoteConfiguration implements IRemoteConfiguration { - - @Override - public String getAddress() { - return config.getString("remote.address", "127.0.0.1"); - } - - @Override - public int getPort() { - return config.getInt("remote.port", 25565); - } - - @Override - public String getAuthType() { - return config.getString("remote.auth-type", "online"); - } - } - - public class BukkitUserAuthenticationInfo implements IUserAuthenticationInfo { - - private String key; - - public BukkitUserAuthenticationInfo(String key) { - this.key = key; - } - - @Override - public String getEmail() { - return config.getString("userAuths." + key + ".email"); - } - - @Override - public String getPassword() { - return config.getString("userAuths." + key + ".password"); - } - } - - public class BukkitMetricsInfo implements IMetricsInfo { - - @Override - public boolean isEnabled() { - return config.getBoolean("metrics.enabled", true); - } - - @Override - public String getUniqueId() { - return config.getString("metrics.uuid", "generateduuid"); - } - } - - @Override - public int getConfigVersion() { - return config.getInt("config-version", 0); - } } 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 cc054965..d8f44f36 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 @@ -28,10 +28,10 @@ package org.geysermc.platform.spigot; import org.bukkit.Bukkit; import org.bukkit.plugin.java.JavaPlugin; import org.geysermc.common.PlatformType; -import org.geysermc.connector.GeyserConfiguration; import org.geysermc.connector.GeyserConnector; import org.geysermc.connector.bootstrap.GeyserBootstrap; import org.geysermc.connector.command.CommandManager; +import org.geysermc.connector.configuration.GeyserConfiguration; import org.geysermc.connector.network.translators.world.WorldManager; import org.geysermc.connector.ping.GeyserLegacyPingPassthrough; import org.geysermc.connector.ping.IGeyserPingPassthrough; @@ -39,9 +39,14 @@ import org.geysermc.platform.spigot.command.GeyserSpigotCommandExecutor; import org.geysermc.platform.spigot.command.GeyserSpigotCommandManager; import org.geysermc.platform.spigot.world.GeyserSpigotBlockPlaceListener; import org.geysermc.platform.spigot.world.GeyserSpigotWorldManager; +import org.geysermc.connector.utils.FileUtils; import us.myles.ViaVersion.api.Via; +import java.io.File; +import java.io.IOException; +import java.nio.file.Files; import java.util.UUID; +import java.util.logging.Level; public class GeyserSpigotPlugin extends JavaPlugin implements GeyserBootstrap { @@ -56,26 +61,41 @@ public class GeyserSpigotPlugin extends JavaPlugin implements GeyserBootstrap { @Override public void onEnable() { - saveDefaultConfig(); - - this.geyserConfig = new GeyserSpigotConfiguration(getDataFolder(), getConfig()); - if (geyserConfig.getMetrics().getUniqueId().equals("generateduuid")) { - getConfig().set("metrics.uuid", UUID.randomUUID().toString()); - saveConfig(); + // This is manually done instead of using Bukkit methods to save the config because otherwise comments get removed + 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, "Existing config found in the Geyser-Bukkit folder; copying over..."); + Files.copy(bukkitConfig.toPath(), new File(getDataFolder().toString() + "/config.yml").toPath()); + getLogger().log(Level.INFO, "Copied!"); + } + } + 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); + } catch (IOException ex) { + getLogger().log(Level.WARNING, "Failed to read/create config.yml! Make sure it's up to date and/or readable+writable!", ex); + ex.printStackTrace(); } // Don't change the ip if its listening on all interfaces // By default this should be 127.0.0.1 but may need to be changed in some circumstances if (!Bukkit.getIp().equals("0.0.0.0") && !Bukkit.getIp().equals("")) { - getConfig().set("remote.address", Bukkit.getIp()); + geyserConfig.getRemote().setAddress(Bukkit.getIp()); } - getConfig().set("remote.port", Bukkit.getPort()); - saveConfig(); + geyserConfig.getRemote().setPort(Bukkit.getPort()); this.geyserLogger = new GeyserSpigotLogger(getLogger(), geyserConfig.isDebugMode()); GeyserConfiguration.checkGeyserConfiguration(geyserConfig, geyserLogger); + if (geyserConfig.getRemote().getAuthType().equals("floodgate") && Bukkit.getPluginManager().getPlugin("floodgate-bukkit") == null) { + geyserLogger.severe("Auth type set to Floodgate but Floodgate not found! Disabling..."); + this.getPluginLoader().disablePlugin(this); + return; + } + geyserConfig.loadFloodgate(this); this.connector = GeyserConnector.start(PlatformType.SPIGOT, this); @@ -113,7 +133,8 @@ public class GeyserSpigotPlugin extends JavaPlugin implements GeyserBootstrap { @Override public void onDisable() { - connector.shutdown(); + if (connector != null) + connector.shutdown(); } @Override diff --git a/bootstrap/sponge/src/main/java/org/geysermc/platform/sponge/GeyserSpongeConfiguration.java b/bootstrap/sponge/src/main/java/org/geysermc/platform/sponge/GeyserSpongeConfiguration.java index fc148470..2b02ec0d 100644 --- a/bootstrap/sponge/src/main/java/org/geysermc/platform/sponge/GeyserSpongeConfiguration.java +++ b/bootstrap/sponge/src/main/java/org/geysermc/platform/sponge/GeyserSpongeConfiguration.java @@ -29,7 +29,7 @@ import lombok.AllArgsConstructor; import ninja.leaping.configurate.ConfigurationNode; -import org.geysermc.connector.GeyserConfiguration; +import org.geysermc.connector.configuration.GeyserConfiguration; import java.io.File; import java.nio.file.Path; diff --git a/bootstrap/sponge/src/main/java/org/geysermc/platform/sponge/GeyserSpongePlugin.java b/bootstrap/sponge/src/main/java/org/geysermc/platform/sponge/GeyserSpongePlugin.java index d226add7..2288fc67 100644 --- a/bootstrap/sponge/src/main/java/org/geysermc/platform/sponge/GeyserSpongePlugin.java +++ b/bootstrap/sponge/src/main/java/org/geysermc/platform/sponge/GeyserSpongePlugin.java @@ -30,7 +30,7 @@ import ninja.leaping.configurate.ConfigurationNode; import ninja.leaping.configurate.loader.ConfigurationLoader; import ninja.leaping.configurate.yaml.YAMLConfigurationLoader; import org.geysermc.common.PlatformType; -import org.geysermc.connector.GeyserConfiguration; +import org.geysermc.connector.configuration.GeyserConfiguration; import org.geysermc.connector.GeyserConnector; import org.geysermc.connector.bootstrap.GeyserBootstrap; import org.geysermc.connector.command.CommandManager; diff --git a/bootstrap/standalone/src/main/java/org/geysermc/platform/standalone/GeyserStandaloneBootstrap.java b/bootstrap/standalone/src/main/java/org/geysermc/platform/standalone/GeyserStandaloneBootstrap.java index aa0d2392..a1c47238 100644 --- a/bootstrap/standalone/src/main/java/org/geysermc/platform/standalone/GeyserStandaloneBootstrap.java +++ b/bootstrap/standalone/src/main/java/org/geysermc/platform/standalone/GeyserStandaloneBootstrap.java @@ -26,12 +26,12 @@ package org.geysermc.platform.standalone; import org.geysermc.common.PlatformType; -import org.geysermc.connector.ping.GeyserLegacyPingPassthrough; -import org.geysermc.connector.GeyserConfiguration; -import org.geysermc.connector.bootstrap.GeyserBootstrap; import org.geysermc.connector.GeyserConnector; +import org.geysermc.connector.bootstrap.GeyserBootstrap; +import org.geysermc.connector.configuration.GeyserConfiguration; import org.geysermc.connector.command.CommandManager; import org.geysermc.connector.ping.IGeyserPingPassthrough; +import org.geysermc.connector.ping.GeyserLegacyPingPassthrough; import org.geysermc.connector.utils.FileUtils; import org.geysermc.platform.standalone.command.GeyserCommandManager; diff --git a/bootstrap/standalone/src/main/java/org/geysermc/platform/standalone/GeyserStandaloneConfiguration.java b/bootstrap/standalone/src/main/java/org/geysermc/platform/standalone/GeyserStandaloneConfiguration.java index bd029204..29e18d08 100644 --- a/bootstrap/standalone/src/main/java/org/geysermc/platform/standalone/GeyserStandaloneConfiguration.java +++ b/bootstrap/standalone/src/main/java/org/geysermc/platform/standalone/GeyserStandaloneConfiguration.java @@ -27,110 +27,21 @@ package org.geysermc.platform.standalone; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonProperty; - import lombok.Getter; -import org.geysermc.connector.GeyserConfiguration; +import org.geysermc.connector.configuration.GeyserJacksonConfiguration; import java.nio.file.Path; import java.nio.file.Paths; -import java.util.Map; -@JsonIgnoreProperties(ignoreUnknown = true) @Getter -public class GeyserStandaloneConfiguration implements GeyserConfiguration { - - private BedrockConfiguration bedrock; - private RemoteConfiguration remote; +@JsonIgnoreProperties(ignoreUnknown = true) +public class GeyserStandaloneConfiguration extends GeyserJacksonConfiguration { @JsonProperty("floodgate-key-file") private String floodgateKeyFile; - private Map userAuths; - - @JsonProperty("command-suggestions") - private boolean isCommandSuggestions; - - @JsonProperty("passthrough-motd") - private boolean isPassthroughMotd; - - @JsonProperty("passthrough-player-counts") - private boolean isPassthroughPlayerCounts; - - @JsonProperty("legacy-ping-passthrough") - private boolean isLegacyPingPassthrough; - - @JsonProperty("ping-passthrough-interval") - private int pingPassthroughInterval; - - @JsonProperty("max-players") - private int maxPlayers; - - @JsonProperty("debug-mode") - private boolean debugMode; - - @JsonProperty("general-thread-pool") - private int generalThreadPool; - - @JsonProperty("allow-third-party-capes") - private boolean allowThirdPartyCapes; - - @JsonProperty("allow-third-party-ears") - private boolean allowThirdPartyEars; - - @JsonProperty("default-locale") - private String defaultLocale; - - @JsonProperty("cache-chunks") - private boolean cacheChunks; - - @JsonProperty("above-bedrock-nether-building") - private boolean isAboveBedrockNetherBuilding; - - private MetricsInfo metrics; - @Override public Path getFloodgateKeyFile() { return Paths.get(floodgateKeyFile); } - - @Getter - public static class BedrockConfiguration implements IBedrockConfiguration { - - private String address; - private int port; - - private String motd1; - private String motd2; - } - - @Getter - public static class RemoteConfiguration implements IRemoteConfiguration { - - private String address; - private int port; - - private String motd1; - private String motd2; - - @JsonProperty("auth-type") - private String authType; - } - - @Getter - public static class UserAuthenticationInfo implements IUserAuthenticationInfo { - private String email; - private String password; - } - - @Getter - public static class MetricsInfo implements IMetricsInfo { - - private boolean enabled; - - @JsonProperty("uuid") - private String uniqueId; - } - - @JsonProperty("config-version") - private int configVersion; } diff --git a/bootstrap/velocity/src/main/java/org/geysermc/platform/velocity/GeyserVelocityConfiguration.java b/bootstrap/velocity/src/main/java/org/geysermc/platform/velocity/GeyserVelocityConfiguration.java index aef0edaa..574941c4 100644 --- a/bootstrap/velocity/src/main/java/org/geysermc/platform/velocity/GeyserVelocityConfiguration.java +++ b/bootstrap/velocity/src/main/java/org/geysermc/platform/velocity/GeyserVelocityConfiguration.java @@ -30,122 +30,30 @@ import com.fasterxml.jackson.annotation.JsonProperty; import com.velocitypowered.api.plugin.PluginContainer; import com.velocitypowered.api.proxy.ProxyServer; import lombok.Getter; -import lombok.Setter; import org.geysermc.connector.FloodgateKeyLoader; -import org.geysermc.connector.GeyserConfiguration; +import org.geysermc.connector.configuration.GeyserJacksonConfiguration; import java.io.File; import java.nio.file.Path; import java.nio.file.Paths; -import java.util.Map; import java.util.Optional; -@JsonIgnoreProperties(ignoreUnknown = true) @Getter -public class GeyserVelocityConfiguration implements GeyserConfiguration { - - private BedrockConfiguration bedrock; - private RemoteConfiguration remote; +@JsonIgnoreProperties(ignoreUnknown = true) +public class GeyserVelocityConfiguration extends GeyserJacksonConfiguration { @JsonProperty("floodgate-key-file") private String floodgateKeyFile; - private Map userAuths; - - @JsonProperty("command-suggestions") - private boolean commandSuggestions; - - @JsonProperty("passthrough-motd") - private boolean isPassthroughMotd; - - @JsonProperty("passthrough-player-counts") - private boolean isPassthroughPlayerCounts; - - @JsonProperty("legacy-ping-passthrough") - private boolean isLegacyPingPassthrough; - - @JsonProperty("ping-passthrough-interval") - private int pingPassthroughInterval; - - @JsonProperty("max-players") - private int maxPlayers; - - @JsonProperty("debug-mode") - private boolean debugMode; - - @JsonProperty("general-thread-pool") - private int generalThreadPool; - - @JsonProperty("allow-third-party-capes") - private boolean allowThirdPartyCapes; - - @JsonProperty("allow-third-party-ears") - private boolean allowThirdPartyEars; - - @JsonProperty("default-locale") - private String defaultLocale; - - @JsonProperty("cache-chunks") - private boolean cacheChunks; - - @JsonProperty("above-bedrock-nether-building") - private boolean aboveBedrockNetherBuilding; - - private MetricsInfo metrics; - private Path floodgateKey; - public void loadFloodgate(GeyserVelocityPlugin plugin, ProxyServer proxyServer, File dataFolder) { - Optional floodgate = proxyServer.getPluginManager().getPlugin("floodgate"); - floodgate.ifPresent(it -> floodgateKey = FloodgateKeyLoader.getKey(plugin.getGeyserLogger(), this, Paths.get(dataFolder.toString(), floodgateKeyFile.isEmpty() ? floodgateKeyFile : "public-key.pem"), it, Paths.get("plugins/floodgate/"))); - } - @Override public Path getFloodgateKeyFile() { return floodgateKey; } - @Getter - public static class BedrockConfiguration implements IBedrockConfiguration { - - private String address; - private int port; - - private String motd1; - private String motd2; + public void loadFloodgate(GeyserVelocityPlugin plugin, ProxyServer proxyServer, File dataFolder) { + Optional floodgate = proxyServer.getPluginManager().getPlugin("floodgate"); + floodgate.ifPresent(it -> floodgateKey = FloodgateKeyLoader.getKey(plugin.getGeyserLogger(), this, Paths.get(dataFolder.toString(), floodgateKeyFile.isEmpty() ? floodgateKeyFile : "public-key.pem"), it, Paths.get("plugins/floodgate/"))); } - - @Getter - public static class RemoteConfiguration implements IRemoteConfiguration { - - @Setter - private String address; - - @Setter - private int port; - - private String motd1; - private String motd2; - - @JsonProperty("auth-type") - private String authType; - } - - @Getter - public static class UserAuthenticationInfo implements IUserAuthenticationInfo { - private String email; - private String password; - } - - @Getter - public static class MetricsInfo implements IMetricsInfo { - - private boolean enabled; - - @JsonProperty("uuid") - private String uniqueId; - } - - @JsonProperty("config-version") - private int configVersion; } 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 e7b44da5..e2a4787b 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 @@ -35,7 +35,7 @@ import com.velocitypowered.api.plugin.Plugin; import com.velocitypowered.api.proxy.ProxyServer; import org.geysermc.common.PlatformType; -import org.geysermc.connector.GeyserConfiguration; +import org.geysermc.connector.configuration.GeyserConfiguration; import org.geysermc.connector.GeyserConnector; import org.geysermc.connector.bootstrap.GeyserBootstrap; import org.geysermc.connector.ping.GeyserLegacyPingPassthrough; @@ -96,6 +96,11 @@ public class GeyserVelocityPlugin implements GeyserBootstrap { this.geyserLogger = new GeyserVelocityLogger(logger, geyserConfig.isDebugMode()); GeyserConfiguration.checkGeyserConfiguration(geyserConfig, geyserLogger); + if (geyserConfig.getRemote().getAuthType().equals("floodgate") && !proxyServer.getPluginManager().getPlugin("floodgate").isPresent()) { + geyserLogger.severe("Auth type set to Floodgate but Floodgate not found! Disabling..."); + return; + } + geyserConfig.loadFloodgate(this, proxyServer, configDir); this.connector = GeyserConnector.start(PlatformType.VELOCITY, this); diff --git a/connector/pom.xml b/connector/pom.xml index 229761ad..91cf6f3d 100644 --- a/connector/pom.xml +++ b/connector/pom.xml @@ -98,14 +98,8 @@ com.github.steveice10 - opennbt - 1.4-SNAPSHOT - compile - - - com.github.steveice10 - packetlib - 1.5-SNAPSHOT + mcprotocollib + 4c315aa206 compile @@ -115,31 +109,11 @@ - com.github.steveice10 - mcauthlib - 1.3-SNAPSHOT + io.netty + netty-resolver-dns + 4.1.43.Final compile - - com.github.steveice10 - mcprotocollib - 1.15.2-1-SNAPSHOT - compile - - - com.github.steveice10 - opennbt - - - com.github.steveice10 - packetlib - - - com.github.steveice10 - mcauthlib - - - org.reflections reflections diff --git a/connector/src/main/java/org/geysermc/connector/FloodgateKeyLoader.java b/connector/src/main/java/org/geysermc/connector/FloodgateKeyLoader.java index 0b631b2d..617ac83e 100644 --- a/connector/src/main/java/org/geysermc/connector/FloodgateKeyLoader.java +++ b/connector/src/main/java/org/geysermc/connector/FloodgateKeyLoader.java @@ -26,6 +26,8 @@ package org.geysermc.connector; +import org.geysermc.connector.configuration.GeyserConfiguration; + import java.nio.file.Files; import java.nio.file.Path; diff --git a/connector/src/main/java/org/geysermc/connector/GeyserConnector.java b/connector/src/main/java/org/geysermc/connector/GeyserConnector.java index 02e0c500..abd48bc2 100644 --- a/connector/src/main/java/org/geysermc/connector/GeyserConnector.java +++ b/connector/src/main/java/org/geysermc/connector/GeyserConnector.java @@ -35,6 +35,7 @@ import org.geysermc.common.AuthType; import org.geysermc.common.PlatformType; import org.geysermc.connector.bootstrap.GeyserBootstrap; import org.geysermc.connector.command.CommandManager; +import org.geysermc.connector.configuration.GeyserConfiguration; import org.geysermc.connector.metrics.Metrics; import org.geysermc.connector.network.ConnectorServerEventHandler; import org.geysermc.connector.network.remote.RemoteServer; diff --git a/connector/src/main/java/org/geysermc/connector/bootstrap/GeyserBootstrap.java b/connector/src/main/java/org/geysermc/connector/bootstrap/GeyserBootstrap.java index 24ce81cf..5cc6d06f 100644 --- a/connector/src/main/java/org/geysermc/connector/bootstrap/GeyserBootstrap.java +++ b/connector/src/main/java/org/geysermc/connector/bootstrap/GeyserBootstrap.java @@ -27,7 +27,7 @@ package org.geysermc.connector.bootstrap; import org.geysermc.connector.ping.IGeyserPingPassthrough; -import org.geysermc.connector.GeyserConfiguration; +import org.geysermc.connector.configuration.GeyserConfiguration; import org.geysermc.connector.GeyserLogger; import org.geysermc.connector.command.CommandManager; import org.geysermc.connector.network.translators.world.CachedChunkManager; diff --git a/connector/src/main/java/org/geysermc/connector/GeyserConfiguration.java b/connector/src/main/java/org/geysermc/connector/configuration/GeyserConfiguration.java similarity index 97% rename from connector/src/main/java/org/geysermc/connector/GeyserConfiguration.java rename to connector/src/main/java/org/geysermc/connector/configuration/GeyserConfiguration.java index 8a39323b..8e59644e 100644 --- a/connector/src/main/java/org/geysermc/connector/GeyserConfiguration.java +++ b/connector/src/main/java/org/geysermc/connector/configuration/GeyserConfiguration.java @@ -24,7 +24,9 @@ * */ -package org.geysermc.connector; +package org.geysermc.connector.configuration; + +import org.geysermc.connector.GeyserLogger; import java.nio.file.Path; import java.util.Map; diff --git a/connector/src/main/java/org/geysermc/connector/configuration/GeyserJacksonConfiguration.java b/connector/src/main/java/org/geysermc/connector/configuration/GeyserJacksonConfiguration.java new file mode 100644 index 00000000..a1d0db21 --- /dev/null +++ b/connector/src/main/java/org/geysermc/connector/configuration/GeyserJacksonConfiguration.java @@ -0,0 +1,135 @@ +/* + * 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.configuration; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.Getter; +import lombok.Setter; + +import java.nio.file.Path; +import java.util.Map; + +@Getter +@JsonIgnoreProperties(ignoreUnknown = true) +public abstract class GeyserJacksonConfiguration implements GeyserConfiguration { + + private BedrockConfiguration bedrock; + private RemoteConfiguration remote; + + @JsonProperty("floodgate-key-file") + private String floodgateKeyFile; + + public abstract Path getFloodgateKeyFile(); + + private Map userAuths; + + @JsonProperty("command-suggestions") + private boolean commandSuggestions; + + @JsonProperty("passthrough-motd") + private boolean isPassthroughMotd; + + @JsonProperty("passthrough-player-counts") + private boolean isPassthroughPlayerCounts; + + @JsonProperty("legacy-ping-passthrough") + private boolean isLegacyPingPassthrough; + + @JsonProperty("ping-passthrough-interval") + private int pingPassthroughInterval; + + @JsonProperty("max-players") + private int maxPlayers; + + @JsonProperty("debug-mode") + private boolean debugMode; + + @JsonProperty("general-thread-pool") + private int generalThreadPool; + + @JsonProperty("allow-third-party-capes") + private boolean allowThirdPartyCapes; + + @JsonProperty("allow-third-party-ears") + private boolean allowThirdPartyEars; + + @JsonProperty("default-locale") + private String defaultLocale; + + @JsonProperty("cache-chunks") + private boolean cacheChunks; + + @JsonProperty("above-bedrock-nether-building") + private boolean aboveBedrockNetherBuilding; + + private MetricsInfo metrics; + + @Getter + public static class BedrockConfiguration implements IBedrockConfiguration { + + private String address; + private int port; + + private String motd1; + private String motd2; + } + + @Getter + public static class RemoteConfiguration implements IRemoteConfiguration { + + @Setter + private String address; + + @Setter + private int port; + + private String motd1; + private String motd2; + + @JsonProperty("auth-type") + private String authType; + } + + @Getter + public static class UserAuthenticationInfo implements IUserAuthenticationInfo { + private String email; + private String password; + } + + @Getter + public static class MetricsInfo implements IMetricsInfo { + + private boolean enabled; + + @JsonProperty("uuid") + private String uniqueId; + } + + @JsonProperty("config-version") + private int configVersion; +} diff --git a/connector/src/main/java/org/geysermc/connector/entity/DefaultBlockMinecartEntity.java b/connector/src/main/java/org/geysermc/connector/entity/DefaultBlockMinecartEntity.java new file mode 100644 index 00000000..b774af98 --- /dev/null +++ b/connector/src/main/java/org/geysermc/connector/entity/DefaultBlockMinecartEntity.java @@ -0,0 +1,88 @@ +/* + * Copyright (c) 2019-2020 GeyserMC. http://geysermc.org + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * @author GeyserMC + * @link https://github.com/GeyserMC/Geyser + */ + +package org.geysermc.connector.entity; + +import com.github.steveice10.mc.protocol.data.game.entity.metadata.EntityMetadata; +import com.nukkitx.math.vector.Vector3f; +import com.nukkitx.protocol.bedrock.data.EntityData; +import org.geysermc.connector.entity.type.EntityType; +import org.geysermc.connector.network.session.GeyserSession; +import org.geysermc.connector.network.translators.world.block.BlockTranslator; + +/** + * This class is used as a base for minecarts with a default block to display like furnaces and spawners + */ +public class DefaultBlockMinecartEntity extends MinecartEntity { + + public int customBlock = 0; + public int customBlockOffset = 0; + public boolean showCustomBlock = false; + + public DefaultBlockMinecartEntity(long entityId, long geyserId, EntityType entityType, Vector3f position, Vector3f motion, Vector3f rotation) { + super(entityId, geyserId, entityType, position, motion, rotation); + + updateDefaultBlockMetadata(); + metadata.put(EntityData.HAS_DISPLAY, (byte) 1); + } + + @Override + public void updateBedrockMetadata(EntityMetadata entityMetadata, GeyserSession session) { + + // Custom block + if (entityMetadata.getId() == 10) { + customBlock = (int) entityMetadata.getValue(); + + if (showCustomBlock) { + metadata.put(EntityData.DISPLAY_ITEM, BlockTranslator.getBedrockBlockId(customBlock)); + } + } + + // Custom block offset + if (entityMetadata.getId() == 11) { + customBlockOffset = (int) entityMetadata.getValue(); + + if (showCustomBlock) { + metadata.put(EntityData.DISPLAY_OFFSET, customBlockOffset); + } + } + + // If the custom block should be enabled + if (entityMetadata.getId() == 12) { + if ((boolean) entityMetadata.getValue()) { + showCustomBlock = true; + metadata.put(EntityData.DISPLAY_ITEM, BlockTranslator.getBedrockBlockId(customBlock)); + metadata.put(EntityData.DISPLAY_OFFSET, customBlockOffset); + } else { + showCustomBlock = false; + updateDefaultBlockMetadata(); + } + } + + super.updateBedrockMetadata(entityMetadata, session); + } + + public void updateDefaultBlockMetadata() { } +} 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 434c983b..c5fcde9e 100644 --- a/connector/src/main/java/org/geysermc/connector/entity/Entity.java +++ b/connector/src/main/java/org/geysermc/connector/entity/Entity.java @@ -34,6 +34,7 @@ import com.github.steveice10.mc.protocol.data.game.entity.player.PlayerAction; import com.github.steveice10.mc.protocol.data.game.world.block.BlockFace; import com.github.steveice10.mc.protocol.data.game.world.block.BlockState; import com.github.steveice10.mc.protocol.data.message.TextMessage; +import com.github.steveice10.mc.protocol.data.message.TranslationMessage; import com.github.steveice10.mc.protocol.packet.ingame.client.player.ClientPlayerActionPacket; import com.github.steveice10.mc.protocol.packet.ingame.client.player.ClientPlayerUseItemPacket; import com.nukkitx.math.vector.Vector3f; @@ -209,13 +210,12 @@ public class Entity { metadata.getFlags().setFlag(EntityFlag.ON_FIRE, (xd & 0x01) == 0x01); metadata.getFlags().setFlag(EntityFlag.SNEAKING, (xd & 0x02) == 0x02); metadata.getFlags().setFlag(EntityFlag.SPRINTING, (xd & 0x08) == 0x08); - metadata.getFlags().setFlag(EntityFlag.SWIMMING, (xd & 0x10) == 0x10); + metadata.getFlags().setFlag(EntityFlag.SWIMMING, ((xd & 0x10) == 0x10) && metadata.getFlags().getFlag(EntityFlag.SPRINTING)); // Otherwise swimming is enabled on older servers metadata.getFlags().setFlag(EntityFlag.GLIDING, (xd & 0x80) == 0x80); if ((xd & 0x20) == 0x20) { - if (this.is(ArmorStandEntity.class)) { - metadata.put(EntityData.SCALE, 0.0f); - } else { + // Armour stands are handled in their own class + if (!this.is(ArmorStandEntity.class)) { metadata.getFlags().setFlag(EntityFlag.INVISIBLE, true); } } else { @@ -253,9 +253,15 @@ public class Entity { } break; case 2: // custom name - TextMessage name = (TextMessage) entityMetadata.getValue(); - if (name != null) - metadata.put(EntityData.NAMETAG, MessageUtils.getBedrockMessage(name)); + if (entityMetadata.getValue() instanceof TextMessage) { + TextMessage name = (TextMessage) entityMetadata.getValue(); + if (name != null) + metadata.put(EntityData.NAMETAG, MessageUtils.getBedrockMessage(name)); + } else if (entityMetadata.getValue() instanceof TranslationMessage) { + TranslationMessage message = (TranslationMessage) entityMetadata.getValue(); + if (message != null) + metadata.put(EntityData.NAMETAG, MessageUtils.getTranslatedBedrockMessage(message, session.getClientData().getLanguageCode(), true)); + } break; case 3: // is custom name visible if (!this.is(PlayerEntity.class)) 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 cb050e86..1db4f757 100644 --- a/connector/src/main/java/org/geysermc/connector/entity/FireworkEntity.java +++ b/connector/src/main/java/org/geysermc/connector/entity/FireworkEntity.java @@ -37,6 +37,7 @@ import com.nukkitx.protocol.bedrock.packet.SetEntityMotionPacket; 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 java.util.ArrayList; import java.util.List; @@ -63,7 +64,7 @@ public class FireworkEntity extends Entity { CompoundTagBuilder fireworksBuilder = CompoundTagBuilder.builder(); if (fireworks.get("Flight") != null) { - fireworksBuilder.byteTag("Flight", (Byte) fireworks.get("Flight").getValue()); + fireworksBuilder.byteTag("Flight", MathUtils.convertByte(fireworks.get("Flight").getValue())); } List explosions = new ArrayList<>(); @@ -73,7 +74,7 @@ public class FireworkEntity extends Entity { CompoundTagBuilder effectBuilder = CompoundTagBuilder.builder(); if (effectData.get("Type") != null) { - effectBuilder.byteTag("FireworkType", (Byte) effectData.get("Type").getValue()); + effectBuilder.byteTag("FireworkType", MathUtils.convertByte(effectData.get("Type").getValue())); } if (effectData.get("Colors") != null) { @@ -101,11 +102,11 @@ public class FireworkEntity extends Entity { } if (effectData.get("Trail") != null) { - effectBuilder.byteTag("FireworkTrail", (Byte) effectData.get("Trail").getValue()); + effectBuilder.byteTag("FireworkTrail", MathUtils.convertByte(effectData.get("Trail").getValue())); } if (effectData.get("Flicker") != null) { - effectBuilder.byteTag("FireworkFlicker", (Byte) effectData.get("Flicker").getValue()); + effectBuilder.byteTag("FireworkFlicker", MathUtils.convertByte(effectData.get("Flicker").getValue())); } explosions.add(effectBuilder.buildRootTag()); diff --git a/connector/src/main/java/org/geysermc/connector/entity/FurnaceMinecartEntity.java b/connector/src/main/java/org/geysermc/connector/entity/FurnaceMinecartEntity.java new file mode 100644 index 00000000..29ade193 --- /dev/null +++ b/connector/src/main/java/org/geysermc/connector/entity/FurnaceMinecartEntity.java @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2019-2020 GeyserMC. http://geysermc.org + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * @author GeyserMC + * @link https://github.com/GeyserMC/Geyser + */ + +package org.geysermc.connector.entity; + +import com.github.steveice10.mc.protocol.data.game.entity.metadata.EntityMetadata; +import com.nukkitx.math.vector.Vector3f; +import com.nukkitx.protocol.bedrock.data.EntityData; +import org.geysermc.connector.entity.type.EntityType; +import org.geysermc.connector.network.session.GeyserSession; +import org.geysermc.connector.network.translators.world.block.BlockTranslator; + +public class FurnaceMinecartEntity extends DefaultBlockMinecartEntity { + + private boolean hasFuel = false; + + public FurnaceMinecartEntity(long entityId, long geyserId, EntityType entityType, Vector3f position, Vector3f motion, Vector3f rotation) { + super(entityId, geyserId, entityType, position, motion, rotation); + } + + @Override + public void updateBedrockMetadata(EntityMetadata entityMetadata, GeyserSession session) { + if (entityMetadata.getId() == 13 && !showCustomBlock) { + hasFuel = (boolean) entityMetadata.getValue(); + updateDefaultBlockMetadata(); + } + + super.updateBedrockMetadata(entityMetadata, session); + } + + @Override + public void updateDefaultBlockMetadata() { + metadata.put(EntityData.DISPLAY_ITEM, BlockTranslator.getBedrockBlockId(hasFuel ? BlockTranslator.JAVA_RUNTIME_FURNACE_LIT_ID : BlockTranslator.JAVA_RUNTIME_FURNACE_ID)); + metadata.put(EntityData.DISPLAY_OFFSET, 6); + } +} 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 bd18dc46..41308a0d 100644 --- a/connector/src/main/java/org/geysermc/connector/entity/ItemEntity.java +++ b/connector/src/main/java/org/geysermc/connector/entity/ItemEntity.java @@ -49,7 +49,7 @@ public class ItemEntity extends Entity { itemPacket.setUniqueEntityId(geyserId); itemPacket.setFromFishing(false); itemPacket.getMetadata().putAll(metadata); - itemPacket.setItemInHand(ItemTranslator.translateToBedrock((ItemStack) entityMetadata.getValue())); + itemPacket.setItemInHand(ItemTranslator.translateToBedrock(session, (ItemStack) entityMetadata.getValue())); session.sendUpstreamPacket(itemPacket); } 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 a6d8af15..3680945c 100644 --- a/connector/src/main/java/org/geysermc/connector/entity/ItemFrameEntity.java +++ b/connector/src/main/java/org/geysermc/connector/entity/ItemFrameEntity.java @@ -98,7 +98,7 @@ public class ItemFrameEntity extends Entity { @Override public void updateBedrockMetadata(EntityMetadata entityMetadata, GeyserSession session) { if (entityMetadata.getId() == 7 && entityMetadata.getValue() != null) { - ItemData itemData = ItemTranslator.translateToBedrock((ItemStack) entityMetadata.getValue()); + ItemData itemData = ItemTranslator.translateToBedrock(session, (ItemStack) entityMetadata.getValue()); ItemEntry itemEntry = ItemRegistry.getItem((ItemStack) entityMetadata.getValue()); CompoundTagBuilder builder = CompoundTag.builder(); 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 ee004a25..a67c0be5 100644 --- a/connector/src/main/java/org/geysermc/connector/entity/MinecartEntity.java +++ b/connector/src/main/java/org/geysermc/connector/entity/MinecartEntity.java @@ -30,6 +30,7 @@ import com.nukkitx.math.vector.Vector3f; import com.nukkitx.protocol.bedrock.data.EntityData; import org.geysermc.connector.entity.type.EntityType; import org.geysermc.connector.network.session.GeyserSession; +import org.geysermc.connector.network.translators.world.block.BlockTranslator; public class MinecartEntity extends Entity { @@ -54,6 +55,24 @@ public class MinecartEntity extends Entity { 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) { + metadata.put(EntityData.DISPLAY_ITEM, BlockTranslator.getBedrockBlockId((int) entityMetadata.getValue())); + } + + // Custom block offset + if (entityMetadata.getId() == 11) { + metadata.put(EntityData.DISPLAY_OFFSET, entityMetadata.getValue()); + } + + // If the custom block should be enabled + if (entityMetadata.getId() == 12) { + // Needs a byte based off of Java's boolean + metadata.put(EntityData.HAS_DISPLAY, (byte) ((boolean) entityMetadata.getValue() ? 1 : 0)); + } + } + super.updateBedrockMetadata(entityMetadata, session); } diff --git a/connector/src/main/java/org/geysermc/connector/entity/SpawnerMinecartEntity.java b/connector/src/main/java/org/geysermc/connector/entity/SpawnerMinecartEntity.java new file mode 100644 index 00000000..6be138c0 --- /dev/null +++ b/connector/src/main/java/org/geysermc/connector/entity/SpawnerMinecartEntity.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.connector.entity; + +import com.nukkitx.math.vector.Vector3f; +import com.nukkitx.protocol.bedrock.data.EntityData; +import org.geysermc.connector.entity.type.EntityType; +import org.geysermc.connector.network.translators.world.block.BlockTranslator; + +public class SpawnerMinecartEntity extends DefaultBlockMinecartEntity { + + public SpawnerMinecartEntity(long entityId, long geyserId, EntityType entityType, Vector3f position, Vector3f motion, Vector3f rotation) { + super(entityId, geyserId, entityType, position, motion, rotation); + } + + @Override + public void updateDefaultBlockMetadata() { + metadata.put(EntityData.DISPLAY_ITEM, BlockTranslator.getBedrockBlockId(BlockTranslator.JAVA_RUNTIME_SPAWNER_ID)); + metadata.put(EntityData.DISPLAY_OFFSET, 6); + } +} 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 8d3c8f8f..47faad36 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 @@ -35,17 +35,42 @@ import org.geysermc.connector.network.session.GeyserSession; public class ArmorStandEntity extends LivingEntity { + // These are used to store the state of the armour stand for use when handling invisibility + private boolean isMarker = false; + private boolean isInvisible = false; + private boolean isSmall = false; + public ArmorStandEntity(long entityId, long geyserId, EntityType entityType, Vector3f position, Vector3f motion, Vector3f rotation) { super(entityId, geyserId, entityType, position, motion, rotation); } + @Override + public void moveAbsolute(GeyserSession session, Vector3f position, Vector3f rotation, boolean isOnGround, boolean teleported) { + // Fake the height to be above where it is so the nametag appears in the right location for invisible non-marker armour stands + if (!isMarker && isInvisible) { + position = position.add(0d, entityType.getHeight() * (isSmall ? 0.55d : 1d), 0d); + } + + super.moveAbsolute(session, position, rotation, isOnGround, teleported); + } + @Override public void updateBedrockMetadata(EntityMetadata entityMetadata, GeyserSession session) { - if (entityMetadata.getType() == MetadataType.BYTE) { + if (entityMetadata.getId() == 0 && entityMetadata.getType() == MetadataType.BYTE) { + byte xd = (byte) entityMetadata.getValue(); + + // Check if the armour stand is invisible and store accordingly + if ((xd & 0x20) == 0x20) { + metadata.put(EntityData.SCALE, 0.0f); + isInvisible = true; + } + } else if (entityMetadata.getId() == 14 && entityMetadata.getType() == MetadataType.BYTE) { byte xd = (byte) entityMetadata.getValue(); // isSmall if ((xd & 0x01) == 0x01) { + isSmall = true; + if (metadata.getFloat(EntityData.SCALE) != 0.55f && metadata.getFloat(EntityData.SCALE) != 0.0f) { metadata.put(EntityData.SCALE, 0.55f); } @@ -60,9 +85,10 @@ public class ArmorStandEntity extends LivingEntity { } // setMarker - if ((xd & 0x10) == 0x10 && (metadata.get(EntityData.BOUNDING_BOX_WIDTH) != null && !metadata.get(EntityData.BOUNDING_BOX_WIDTH).equals(0.0f))) { + if ((xd & 0x10) == 0x10 && (metadata.get(EntityData.BOUNDING_BOX_WIDTH) == null || !metadata.get(EntityData.BOUNDING_BOX_WIDTH).equals(0.0f))) { metadata.put(EntityData.BOUNDING_BOX_WIDTH, 0.0f); metadata.put(EntityData.BOUNDING_BOX_HEIGHT, 0.0f); + isMarker = true; } } super.updateBedrockMetadata(entityMetadata, session); diff --git a/connector/src/main/java/org/geysermc/connector/entity/type/EntityType.java b/connector/src/main/java/org/geysermc/connector/entity/type/EntityType.java index 88934196..3acc17c3 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 @@ -105,7 +105,7 @@ public enum EntityType { THROWN_EXP_BOTTLE(ThrowableEntity.class, 68, 0.25f, 0.25f, 0f, 0f, "minecraft:xp_bottle"), EXPERIENCE_ORB(ExpOrbEntity.class, 69, 0f, 0f, 0f, 0f, "minecraft:xp_orb"), EYE_OF_ENDER(Entity.class, 70, 0.25f, 0.25f, 0f, 0f, "minecraft:eye_of_ender_signal"), - END_CRYSTAL(EnderCrystalEntity.class, 71, 0f, 0f, 0f, 0f, "minecraft:ender_crystal"), + END_CRYSTAL(EnderCrystalEntity.class, 71, 2.0f, 2.0f, 2.0f, 0f, "minecraft:ender_crystal"), FIREWORK_ROCKET(FireworkEntity.class, 72, 0.25f, 0.25f, 0.25f, 0f, "minecraft:fireworks_rocket"), TRIDENT(TridentEntity.class, 73, 0f, 0f, 0f, 0f, "minecraft:thrown_trident"), TURTLE(AnimalEntity.class, 74, 0.4f, 1.2f), @@ -133,12 +133,13 @@ public enum EntityType { MINECART_HOPPER(MinecartEntity.class, 96, 0.7f, 0.98f, 0.98f, 0.35f, "minecraft:hopper_minecart"), MINECART_TNT(MinecartEntity.class, 97, 0.7f, 0.98f, 0.98f, 0.35f, "minecraft:tnt_minecart"), MINECART_CHEST(MinecartEntity.class, 98, 0.7f, 0.98f, 0.98f, 0.35f, "minecraft:chest_minecart"), - + MINECART_FURNACE(FurnaceMinecartEntity.class, 98, 0.7f, 0.98f, 0.98f, 0.35f, "minecraft:minecart"), + MINECART_SPAWNER(SpawnerMinecartEntity.class, 98, 0.7f, 0.98f, 0.98f, 0.35f, "minecraft:minecart"), MINECART_COMMAND_BLOCK(MinecartEntity.class, 100, 0.7f, 0.98f, 0.98f, 0.35f, "minecraft:command_block_minecart"), LINGERING_POTION(ThrowableEntity.class, 101, 0f), LLAMA_SPIT(Entity.class, 102, 0.25f), - EVOKER_FANGS(Entity.class, 103, 0.8f, 0.5f), - EVOKER(SpellcasterIllagerEntity.class, 104, 1.95f, 0.5f), + EVOKER_FANGS(Entity.class, 103, 0.8f, 0.5f, 0.5f, 0f, "minecraft:evocation_fang"), + EVOKER(SpellcasterIllagerEntity.class, 104, 1.95f, 0.6f, 0.6f, 0f, "minecraft:evocation_illager"), VEX(MonsterEntity.class, 105, 0.8f, 0.4f), ICE_BOMB(Entity.class, 106, 0f), BALLOON(Entity.class, 107, 0f), //TODO diff --git a/connector/src/main/java/org/geysermc/connector/network/ConnectorServerEventHandler.java b/connector/src/main/java/org/geysermc/connector/network/ConnectorServerEventHandler.java index 29ba1567..49f81e3f 100644 --- a/connector/src/main/java/org/geysermc/connector/network/ConnectorServerEventHandler.java +++ b/connector/src/main/java/org/geysermc/connector/network/ConnectorServerEventHandler.java @@ -31,7 +31,7 @@ import io.netty.channel.ChannelHandlerContext; import io.netty.channel.socket.DatagramPacket; import org.geysermc.common.ping.GeyserPingInfo; import org.geysermc.connector.ping.IGeyserPingPassthrough; -import org.geysermc.connector.GeyserConfiguration; +import org.geysermc.connector.configuration.GeyserConfiguration; import org.geysermc.connector.GeyserConnector; import org.geysermc.connector.network.session.GeyserSession; import org.geysermc.connector.utils.MessageUtils; diff --git a/connector/src/main/java/org/geysermc/connector/network/QueryPacketHandler.java b/connector/src/main/java/org/geysermc/connector/network/QueryPacketHandler.java index 061e0f86..6ad206b3 100644 --- a/connector/src/main/java/org/geysermc/connector/network/QueryPacketHandler.java +++ b/connector/src/main/java/org/geysermc/connector/network/QueryPacketHandler.java @@ -60,6 +60,7 @@ public class QueryPacketHandler { /** * The Query packet handler instance + * * @param connector Geyser Connector * @param sender The Sender IP/Port for the Query * @param buffer The Query data @@ -79,11 +80,12 @@ public class QueryPacketHandler { /** * Checks the packet is in fact a query packet + * * @param buffer Query data * @return if the packet is a query packet */ private boolean isQueryPacket(ByteBuf buffer) { - return (buffer.readableBytes() >= 2) ? buffer.readUnsignedShort() == 65277 : false; + return (buffer.readableBytes() >= 2) ? buffer.readUnsignedShort() == 0xFEFD : false; } /** @@ -130,6 +132,7 @@ public class QueryPacketHandler { /** * Gets the game data for the query + * * @return the game data for the query */ private byte[] getGameData() { @@ -177,7 +180,7 @@ public class QueryPacketHandler { // Blank Buffer Bytes query.write("GeyserMC".getBytes()); query.write((byte) 0x00); - query.write((byte) 128); + query.write((byte) 0x80); query.write((byte) 0x00); // Fills the game data @@ -189,7 +192,7 @@ public class QueryPacketHandler { } // Final byte to show the end of the game data - query.write(new byte[]{0x00, 0x01}); + query.write(new byte[] { 0x00, 0x01 }); return query.toByteArray(); } catch (IOException e) { e.printStackTrace(); @@ -197,6 +200,11 @@ public class QueryPacketHandler { } } + /** + * Generate a byte[] storing the player names + * + * @return The byte[] representation of players + */ private byte[] getPlayers() { ByteArrayOutputStream query = new ByteArrayOutputStream(); @@ -208,7 +216,7 @@ public class QueryPacketHandler { try { // Start the player section query.write("player_".getBytes()); - query.write(new byte[]{0x00, 0x00}); + query.write(new byte[] { 0x00, 0x00 }); // Fill player names if(pingInfo != null) { @@ -229,6 +237,7 @@ public class QueryPacketHandler { /** * Sends a packet to the sender + * * @param data packet data */ private void sendPacket(ByteBuf data) { @@ -251,18 +260,28 @@ public class QueryPacketHandler { * Gets an MD5 token for the current IP/Port. * This should reset every 30 seconds but a new one is generated per instance * Seems wasteful to code something in to clear it when it has no use. + * * @param token the token * @param address the address * @return an MD5 token for the current IP/Port */ public static byte[] getTokenString(byte[] token, InetAddress address) { try { + // Generate an MD5 hash from the address MessageDigest digest = MessageDigest.getInstance("MD5"); digest.update(address.toString().getBytes(StandardCharsets.UTF_8)); digest.update(token); - return Arrays.copyOf(digest.digest(), 4); + + // Get the first 4 bytes of the digest + byte[] digestBytes = Arrays.copyOf(digest.digest(), 4); + + // Convert the bytes to a buffer + ByteBuffer byteBuffer = ByteBuffer.wrap(digestBytes); + + // Turn the number into a null terminated string + return (byteBuffer.getInt() + "\0").getBytes(); } catch (NoSuchAlgorithmException e) { - return ByteBuffer.allocate(4).putInt(ThreadLocalRandom.current().nextInt()).array(); + return (ByteBuffer.allocate(4).putInt(ThreadLocalRandom.current().nextInt()).getInt() + "\0").getBytes(); } } } 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 67862d0e..5a40b246 100644 --- a/connector/src/main/java/org/geysermc/connector/network/UpstreamPacketHandler.java +++ b/connector/src/main/java/org/geysermc/connector/network/UpstreamPacketHandler.java @@ -28,7 +28,7 @@ package org.geysermc.connector.network; import com.nukkitx.protocol.bedrock.BedrockPacket; import com.nukkitx.protocol.bedrock.packet.*; import org.geysermc.common.AuthType; -import org.geysermc.connector.GeyserConfiguration; +import org.geysermc.connector.configuration.GeyserConfiguration; import org.geysermc.connector.GeyserConnector; import org.geysermc.connector.network.session.GeyserSession; import org.geysermc.connector.network.translators.PacketTranslatorRegistry; 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 cc052509..5491d6e5 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 @@ -31,6 +31,7 @@ import com.github.steveice10.mc.auth.exception.request.RequestException; import com.github.steveice10.mc.protocol.MinecraftProtocol; import com.github.steveice10.mc.protocol.data.SubProtocol; import com.github.steveice10.mc.protocol.data.game.entity.player.GameMode; +import com.github.steveice10.mc.protocol.data.game.window.VillagerTrade; import com.github.steveice10.mc.protocol.data.game.world.block.BlockState; import com.github.steveice10.mc.protocol.packet.handshake.client.HandshakePacket; import com.github.steveice10.mc.protocol.packet.ingame.client.world.ClientTeleportConfirmPacket; @@ -41,14 +42,10 @@ import com.github.steveice10.packetlib.event.session.*; import com.github.steveice10.packetlib.packet.Packet; import com.github.steveice10.packetlib.tcp.TcpSessionFactory; import com.nukkitx.math.GenericMath; -import com.nukkitx.math.TrigMath; import com.nukkitx.math.vector.*; import com.nukkitx.protocol.bedrock.BedrockPacket; import com.nukkitx.protocol.bedrock.BedrockServerSession; -import com.nukkitx.protocol.bedrock.data.ContainerId; -import com.nukkitx.protocol.bedrock.data.GamePublishSetting; -import com.nukkitx.protocol.bedrock.data.GameRuleData; -import com.nukkitx.protocol.bedrock.data.PlayerPermission; +import com.nukkitx.protocol.bedrock.data.*; import com.nukkitx.protocol.bedrock.packet.*; import it.unimi.dsi.fastutil.objects.Object2LongMap; import it.unimi.dsi.fastutil.objects.Object2LongOpenHashMap; @@ -60,6 +57,7 @@ import org.geysermc.connector.GeyserConnector; import org.geysermc.connector.command.CommandSender; import org.geysermc.connector.entity.Entity; import org.geysermc.connector.entity.PlayerEntity; +import org.geysermc.connector.entity.attribute.AttributeType; import org.geysermc.connector.inventory.PlayerInventory; import org.geysermc.connector.network.remote.RemoteServer; import org.geysermc.connector.network.session.auth.AuthData; @@ -72,6 +70,7 @@ import org.geysermc.connector.network.translators.item.ItemRegistry; import org.geysermc.connector.network.translators.world.block.BlockTranslator; import org.geysermc.connector.utils.ChunkUtils; import org.geysermc.connector.utils.LocaleUtils; +import org.geysermc.connector.utils.MathUtils; import org.geysermc.connector.utils.SkinUtils; import org.geysermc.floodgate.util.BedrockData; import org.geysermc.floodgate.util.EncryptionUtil; @@ -81,6 +80,8 @@ import java.net.InetSocketAddress; import java.security.NoSuchAlgorithmException; import java.security.PublicKey; import java.security.spec.InvalidKeySpecException; +import java.util.ArrayList; +import java.util.List; import java.util.UUID; import java.util.concurrent.atomic.AtomicInteger; @@ -166,6 +167,14 @@ public class GeyserSession implements CommandSender { @Setter private int craftSlot = 0; + @Setter + private long lastWindowCloseTime = 0; + + @Setter + private VillagerTrade[] villagerTrades; + @Setter + private long lastInteractedVillagerEid; + private MinecraftProtocol protocol; public GeyserSession(GeyserConnector connector, BedrockServerSession bedrockServerSession) { @@ -211,6 +220,15 @@ public class GeyserSession implements CommandSender { PlayStatusPacket playStatusPacket = new PlayStatusPacket(); playStatusPacket.setStatus(PlayStatusPacket.Status.PLAYER_SPAWN); upstream.sendPacket(playStatusPacket); + + UpdateAttributesPacket attributesPacket = new UpdateAttributesPacket(); + attributesPacket.setRuntimeEntityId(getPlayerEntity().getGeyserId()); + List attributes = new ArrayList<>(); + // Default move speed + // Bedrock clients move very fast by default until they get an attribute packet correcting the speed + attributes.add(new Attribute("minecraft:movement", 0.0f, 1024f, 0.1f, 0.1f)); + attributesPacket.setAttributes(attributes); + upstream.sendPacket(attributesPacket); } public void fetchOurSkin(PlayerListPacket.Entry entry) { @@ -366,6 +384,14 @@ public class GeyserSession implements CommandSender { PacketTranslatorRegistry.JAVA_TRANSLATOR.translate(event.getPacket().getClass(), event.getPacket(), GeyserSession.this); } } + + @Override + public void packetError(PacketErrorEvent event) { + connector.getLogger().warning("Downstream packet error! " + event.getCause().getMessage()); + if (connector.getConfig().isDebugMode()) + event.getCause().printStackTrace(); + event.setSuppress(true); + } }); downstream.getSession().connect(); @@ -436,7 +462,7 @@ public class GeyserSession implements CommandSender { } public void setRenderDistance(int renderDistance) { - renderDistance = GenericMath.ceil(++renderDistance * TrigMath.SQRT_OF_TWO); //square to circle + renderDistance = GenericMath.ceil(++renderDistance * MathUtils.SQRT_OF_TWO); //square to circle if (renderDistance > 32) renderDistance = 32; // <3 u ViaVersion but I don't like crashing clients x) this.renderDistance = renderDistance; 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 13136331..a11dd40a 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 @@ -25,18 +25,19 @@ package org.geysermc.connector.network.translators; -import java.util.HashMap; -import java.util.Map; - +import com.github.steveice10.mc.protocol.packet.ingame.server.ServerKeepAlivePacket; +import com.github.steveice10.mc.protocol.packet.ingame.server.ServerPlayerListDataPacket; import com.github.steveice10.mc.protocol.packet.ingame.server.world.ServerUpdateLightPacket; +import com.github.steveice10.packetlib.packet.Packet; +import com.nukkitx.protocol.bedrock.BedrockPacket; import it.unimi.dsi.fastutil.objects.ObjectArrayList; import org.geysermc.connector.GeyserConnector; import org.geysermc.connector.network.session.GeyserSession; - -import com.github.steveice10.packetlib.packet.Packet; -import com.nukkitx.protocol.bedrock.BedrockPacket; import org.reflections.Reflections; +import java.util.HashMap; +import java.util.Map; + public class PacketTranslatorRegistry { private final Map, PacketTranslator> translators = new HashMap<>(); @@ -72,7 +73,9 @@ public class PacketTranslatorRegistry { } } - IGNORED_PACKETS.add(ServerUpdateLightPacket.class); + IGNORED_PACKETS.add(ServerKeepAlivePacket.class); // Handled by MCProtocolLib + IGNORED_PACKETS.add(ServerUpdateLightPacket.class); // Light is handled on Bedrock for us + IGNORED_PACKETS.add(ServerPlayerListDataPacket.class); // Cant be implemented in bedrock } private PacketTranslatorRegistry() { diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockAdventureSettingsTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockAdventureSettingsTranslator.java new file mode 100644 index 00000000..75a1547f --- /dev/null +++ b/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockAdventureSettingsTranslator.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.connector.network.translators.bedrock; + +import com.github.steveice10.mc.protocol.data.game.entity.player.GameMode; +import com.github.steveice10.mc.protocol.packet.ingame.client.player.ClientPlayerAbilitiesPacket; +import com.nukkitx.protocol.bedrock.packet.AdventureSettingsPacket; +import org.geysermc.connector.network.session.GeyserSession; +import org.geysermc.connector.network.translators.PacketTranslator; +import org.geysermc.connector.network.translators.Translator; + +@Translator(packet = AdventureSettingsPacket.class) +public class BedrockAdventureSettingsTranslator extends PacketTranslator { + + @Override + public void translate(AdventureSettingsPacket packet, GeyserSession session) { + // Only canFly and flying are used by the server + // https://wiki.vg/Protocol#Player_Abilities_.28serverbound.29 + boolean canFly = packet.getFlags().contains(AdventureSettingsPacket.Flag.MAY_FLY); + boolean flying = packet.getFlags().contains(AdventureSettingsPacket.Flag.FLYING); + boolean creative = session.getGameMode() == GameMode.CREATIVE; + ClientPlayerAbilitiesPacket abilitiesPacket = new ClientPlayerAbilitiesPacket( + false, canFly, flying, creative, 0.05f, 0.1f + ); + session.sendDownstreamPacket(abilitiesPacket); + } +} diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockContainerCloseTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockContainerCloseTranslator.java index 6f4a243f..4c531c48 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockContainerCloseTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockContainerCloseTranslator.java @@ -38,17 +38,21 @@ public class BedrockContainerCloseTranslator extends PacketTranslator= 0 && packet.getData() < trades.length) { + VillagerTrade trade = session.getVillagerTrades()[packet.getData()]; + openInventory.setItem(2, trade.getOutput()); + villager.getMetadata().put(EntityData.TRADE_XP, trade.getXp() + villager.getMetadata().getInt(EntityData.TRADE_XP)); + villager.updateBedrockMetadata(session); + } + } + return; } session.getConnector().getLogger().debug("Did not translate incoming EntityEventPacket: " + packet.toString()); } diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockInventoryTransactionTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockInventoryTransactionTranslator.java index 8f96b800..777d4d9e 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockInventoryTransactionTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockInventoryTransactionTranslator.java @@ -45,6 +45,7 @@ import com.nukkitx.protocol.bedrock.packet.InventoryTransactionPacket; import org.geysermc.connector.entity.Entity; import org.geysermc.connector.entity.ItemFrameEntity; +import org.geysermc.connector.entity.living.merchant.AbstractMerchantEntity; import org.geysermc.connector.inventory.Inventory; import org.geysermc.connector.network.session.GeyserSession; import org.geysermc.connector.network.translators.PacketTranslator; @@ -198,6 +199,10 @@ public class BedrockInventoryTransactionTranslator extends PacketTranslator actions) { + for (InventoryActionData action : actions) { + if (action.getSource().getContainerId() == inventory.getId()) { + if (action.getSlot() >= size) { + updateInventory(session, inventory); + InventoryUtils.updateCursor(session); + return; + } + } + } + + super.translateActions(session, inventory, actions); + } +} diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/inventory/CraftingInventoryTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/CraftingInventoryTranslator.java index 3f887001..e31eb1e3 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/inventory/CraftingInventoryTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/CraftingInventoryTranslator.java @@ -92,7 +92,10 @@ public class CraftingInventoryTranslator extends BaseInventoryTranslator { @Override public int javaSlotToBedrock(int slot) { - return slot == 0 ? 50 : slot + 31; + if (slot < size) { + return slot == 0 ? 50 : slot + 31; + } + return super.javaSlotToBedrock(slot); } @Override diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/inventory/DoubleChestInventoryTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/DoubleChestInventoryTranslator.java index 8c5b2cf2..6d6cadd7 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/inventory/DoubleChestInventoryTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/DoubleChestInventoryTranslator.java @@ -39,15 +39,13 @@ import org.geysermc.connector.network.translators.world.block.BlockTranslator; import org.geysermc.connector.network.translators.inventory.updater.ChestInventoryUpdater; import org.geysermc.connector.network.translators.inventory.updater.InventoryUpdater; -public class DoubleChestInventoryTranslator extends BaseInventoryTranslator { +public class DoubleChestInventoryTranslator extends ChestInventoryTranslator { private final int blockId; - private final InventoryUpdater updater; public DoubleChestInventoryTranslator(int size) { - super(size); + super(size, 54); BlockState javaBlockState = BlockTranslator.getJavaBlockState("minecraft:chest[facing=north,type=single,waterlogged=false]"); this.blockId = BlockTranslator.getBedrockBlockId(javaBlockState); - this.updater = new ChestInventoryUpdater(54); } @Override @@ -128,14 +126,4 @@ public class DoubleChestInventoryTranslator extends BaseInventoryTranslator { blockPacket.setRuntimeId(BlockTranslator.getBedrockBlockId(realBlock)); session.sendUpstreamPacket(blockPacket); } - - @Override - public void updateInventory(GeyserSession session, Inventory inventory) { - updater.updateInventory(this, session, inventory); - } - - @Override - public void updateSlot(GeyserSession session, Inventory inventory, int slot) { - updater.updateSlot(this, session, inventory, slot); - } } diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/inventory/InventoryTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/InventoryTranslator.java index 7b4b26e6..97594907 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/inventory/InventoryTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/InventoryTranslator.java @@ -54,6 +54,7 @@ public abstract class InventoryTranslator { put(WindowType.ANVIL, new AnvilInventoryTranslator()); put(WindowType.CRAFTING, new CraftingInventoryTranslator()); put(WindowType.GRINDSTONE, new GrindstoneInventoryTranslator()); + put(WindowType.MERCHANT, new MerchantInventoryTranslator()); //put(WindowType.ENCHANTMENT, new EnchantmentInventoryTranslator()); //TODO InventoryTranslator furnace = new FurnaceInventoryTranslator(); diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/inventory/MerchantInventoryTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/MerchantInventoryTranslator.java new file mode 100644 index 00000000..3f7636b9 --- /dev/null +++ b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/MerchantInventoryTranslator.java @@ -0,0 +1,120 @@ +/* + * 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.inventory; + +import com.nukkitx.protocol.bedrock.data.ContainerId; +import com.nukkitx.protocol.bedrock.data.InventoryActionData; +import com.nukkitx.protocol.bedrock.data.InventorySource; +import org.geysermc.connector.inventory.Inventory; +import org.geysermc.connector.network.session.GeyserSession; +import org.geysermc.connector.network.translators.inventory.updater.CursorInventoryUpdater; +import org.geysermc.connector.network.translators.inventory.updater.InventoryUpdater; + +import java.util.List; + +public class MerchantInventoryTranslator extends BaseInventoryTranslator { + + private final InventoryUpdater updater; + + public MerchantInventoryTranslator() { + super(3); + this.updater = new CursorInventoryUpdater(); + } + + @Override + public int javaSlotToBedrock(int slot) { + switch (slot) { + case 0: + return 4; + case 1: + return 5; + case 2: + return 50; + } + return super.javaSlotToBedrock(slot); + } + + @Override + public int bedrockSlotToJava(InventoryActionData action) { + if (action.getSource().getContainerId() == ContainerId.CURSOR) { + switch (action.getSlot()) { + case 4: + return 0; + case 5: + return 1; + case 50: + return 2; + } + } + return super.bedrockSlotToJava(action); + } + + @Override + public SlotType getSlotType(int javaSlot) { + if (javaSlot == 2) { + return SlotType.OUTPUT; + } + return SlotType.NORMAL; + } + + @Override + public void prepareInventory(GeyserSession session, Inventory inventory) { + + } + + @Override + public void openInventory(GeyserSession session, Inventory inventory) { + + } + + @Override + public void closeInventory(GeyserSession session, Inventory inventory) { + session.setLastInteractedVillagerEid(-1); + session.setVillagerTrades(null); + } + + @Override + public void updateInventory(GeyserSession session, Inventory inventory) { + updater.updateInventory(this, session, inventory); + } + + @Override + public void updateSlot(GeyserSession session, Inventory inventory, int slot) { + updater.updateSlot(this, session, inventory, slot); + } + + @Override + public void translateActions(GeyserSession session, Inventory inventory, List actions) { + for (InventoryActionData action : actions) { + if (action.getSource().getType() == InventorySource.Type.NON_IMPLEMENTED_TODO) { + return; + } + } + + super.translateActions(session, inventory, actions); + } +} diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/inventory/PlayerInventoryTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/PlayerInventoryTranslator.java index 055b69ef..28986e58 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/inventory/PlayerInventoryTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/PlayerInventoryTranslator.java @@ -59,11 +59,11 @@ public class PlayerInventoryTranslator extends InventoryTranslator { ItemData[] contents = new ItemData[36]; // Inventory for (int i = 9; i < 36; i++) { - contents[i] = ItemTranslator.translateToBedrock(inventory.getItem(i)); + contents[i] = ItemTranslator.translateToBedrock(session, inventory.getItem(i)); } // Hotbar for (int i = 36; i < 45; i++) { - contents[i - 36] = ItemTranslator.translateToBedrock(inventory.getItem(i)); + contents[i - 36] = ItemTranslator.translateToBedrock(session, inventory.getItem(i)); } inventoryContentPacket.setContents(contents); session.sendUpstreamPacket(inventoryContentPacket); @@ -73,7 +73,7 @@ public class PlayerInventoryTranslator extends InventoryTranslator { armorContentPacket.setContainerId(ContainerId.ARMOR); contents = new ItemData[4]; for (int i = 5; i < 9; i++) { - contents[i - 5] = ItemTranslator.translateToBedrock(inventory.getItem(i)); + contents[i - 5] = ItemTranslator.translateToBedrock(session, inventory.getItem(i)); } armorContentPacket.setContents(contents); session.sendUpstreamPacket(armorContentPacket); @@ -81,7 +81,7 @@ public class PlayerInventoryTranslator extends InventoryTranslator { // Offhand InventoryContentPacket offhandPacket = new InventoryContentPacket(); offhandPacket.setContainerId(ContainerId.OFFHAND); - offhandPacket.setContents(new ItemData[]{ItemTranslator.translateToBedrock(inventory.getItem(45))}); + offhandPacket.setContents(new ItemData[]{ItemTranslator.translateToBedrock(session, inventory.getItem(45))}); session.sendUpstreamPacket(offhandPacket); } @@ -100,7 +100,7 @@ public class PlayerInventoryTranslator extends InventoryTranslator { if (session.getGameMode() == GameMode.CREATIVE) { slotPacket.setItem(UNUSUABLE_CRAFTING_SPACE_BLOCK); }else{ - slotPacket.setItem(ItemTranslator.translateToBedrock(inventory.getItem(i))); + slotPacket.setItem(ItemTranslator.translateToBedrock(session, inventory.getItem(i))); } session.sendUpstreamPacket(slotPacket); @@ -125,12 +125,12 @@ public class PlayerInventoryTranslator extends InventoryTranslator { slotPacket.setContainerId(ContainerId.CURSOR); slotPacket.setSlot(slot + 27); } - slotPacket.setItem(ItemTranslator.translateToBedrock(inventory.getItem(slot))); + slotPacket.setItem(ItemTranslator.translateToBedrock(session, inventory.getItem(slot))); session.sendUpstreamPacket(slotPacket); } else if (slot == 45) { InventoryContentPacket offhandPacket = new InventoryContentPacket(); offhandPacket.setContainerId(ContainerId.OFFHAND); - offhandPacket.setContents(new ItemData[]{ItemTranslator.translateToBedrock(inventory.getItem(slot))}); + offhandPacket.setContents(new ItemData[]{ItemTranslator.translateToBedrock(session, inventory.getItem(slot))}); session.sendUpstreamPacket(offhandPacket); } } diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/inventory/SingleChestInventoryTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/SingleChestInventoryTranslator.java index 5c99b012..3f1a58f4 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/inventory/SingleChestInventoryTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/SingleChestInventoryTranslator.java @@ -25,11 +25,35 @@ package org.geysermc.connector.network.translators.inventory; +import com.github.steveice10.mc.protocol.data.game.world.block.BlockState; import com.nukkitx.protocol.bedrock.data.ContainerType; -import org.geysermc.connector.network.translators.inventory.updater.ChestInventoryUpdater; +import org.geysermc.connector.inventory.Inventory; +import org.geysermc.connector.network.session.GeyserSession; +import org.geysermc.connector.network.translators.inventory.holder.BlockInventoryHolder; +import org.geysermc.connector.network.translators.inventory.holder.InventoryHolder; +import org.geysermc.connector.network.translators.world.block.BlockTranslator; + +public class SingleChestInventoryTranslator extends ChestInventoryTranslator { + private final InventoryHolder holder; -public class SingleChestInventoryTranslator extends BlockInventoryTranslator { public SingleChestInventoryTranslator(int size) { - super(size, "minecraft:chest[facing=north,type=single,waterlogged=false]", ContainerType.CONTAINER, new ChestInventoryUpdater(27)); + super(size, 27); + BlockState javaBlockState = BlockTranslator.getJavaBlockState("minecraft:chest[facing=north,type=single,waterlogged=false]"); + this.holder = new BlockInventoryHolder(BlockTranslator.getBedrockBlockId(javaBlockState), ContainerType.CONTAINER); + } + + @Override + public void prepareInventory(GeyserSession session, Inventory inventory) { + holder.prepareInventory(this, session, inventory); + } + + @Override + public void openInventory(GeyserSession session, Inventory inventory) { + holder.openInventory(this, session, inventory); + } + + @Override + public void closeInventory(GeyserSession session, Inventory inventory) { + holder.closeInventory(this, session, inventory); } } diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/inventory/action/InventoryActionDataTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/action/InventoryActionDataTranslator.java index 9139da1f..209df074 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/inventory/action/InventoryActionDataTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/action/InventoryActionDataTranslator.java @@ -61,13 +61,13 @@ public class InventoryActionDataTranslator { worldAction = action; } else if (action.getSource().getContainerId() == ContainerId.CURSOR && action.getSlot() == 0) { cursorAction = action; - ItemData translatedCursor = ItemTranslator.translateToBedrock(session.getInventory().getCursor()); + ItemData translatedCursor = ItemTranslator.translateToBedrock(session, session.getInventory().getCursor()); if (!translatedCursor.equals(action.getFromItem())) { refresh = true; } } else { containerAction = action; - ItemData translatedItem = ItemTranslator.translateToBedrock(inventory.getItem(translator.bedrockSlotToJava(action))); + ItemData translatedItem = ItemTranslator.translateToBedrock(session, inventory.getItem(translator.bedrockSlotToJava(action))); if (!translatedItem.equals(action.getFromItem())) { refresh = true; } @@ -187,11 +187,12 @@ public class InventoryActionDataTranslator { } else if (translator.getSlotType(javaSlot) == SlotType.OUTPUT) { plan.add(Click.LEFT, javaSlot); } else { - int cursorSlot = findTempSlot(inventory, session.getInventory().getCursor(), Collections.singletonList(javaSlot)); + int cursorSlot = findTempSlot(inventory, session.getInventory().getCursor(), Collections.singletonList(javaSlot), false); if (cursorSlot != -1) { plan.add(Click.LEFT, cursorSlot); } else { translator.updateInventory(session, inventory); + InventoryUtils.updateCursor(session); return; } plan.add(Click.LEFT, javaSlot); @@ -245,11 +246,15 @@ public class InventoryActionDataTranslator { int cursorSlot = -1; if (session.getInventory().getCursor() != null) { //move cursor contents to a temporary slot - cursorSlot = findTempSlot(inventory, session.getInventory().getCursor(), Arrays.asList(fromSlot, toSlot)); + cursorSlot = findTempSlot(inventory, + session.getInventory().getCursor(), + Arrays.asList(fromSlot, toSlot), + translator.getSlotType(fromSlot) == SlotType.OUTPUT); if (cursorSlot != -1) { plan.add(Click.LEFT, cursorSlot); } else { translator.updateInventory(session, inventory); + InventoryUtils.updateCursor(session); return; } } @@ -298,7 +303,7 @@ public class InventoryActionDataTranslator { InventoryUtils.updateCursor(session); } - private static int findTempSlot(Inventory inventory, ItemStack item, List slotBlacklist) { + private static int findTempSlot(Inventory inventory, ItemStack item, List slotBlacklist, boolean emptyOnly) { /*try and find a slot that can temporarily store the given item only look in the main inventory and hotbar only slots that are empty or contain a different type of item are valid*/ @@ -314,6 +319,9 @@ public class InventoryActionDataTranslator { ItemStack testItem = inventory.getItem(i); boolean acceptable = true; if (testItem != null) { + if (emptyOnly) { + continue; + } for (ItemStack blacklistItem : itemBlacklist) { if (InventoryUtils.canStack(testItem, blacklistItem)) { acceptable = false; diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/inventory/updater/ChestInventoryUpdater.java b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/updater/ChestInventoryUpdater.java index 61bbda50..6ec8d481 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/inventory/updater/ChestInventoryUpdater.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/updater/ChestInventoryUpdater.java @@ -49,7 +49,7 @@ public class ChestInventoryUpdater extends InventoryUpdater { ItemData[] bedrockItems = new ItemData[paddedSize]; for (int i = 0; i < bedrockItems.length; i++) { if (i < translator.size) { - bedrockItems[i] = ItemTranslator.translateToBedrock(inventory.getItem(i)); + bedrockItems[i] = ItemTranslator.translateToBedrock(session, inventory.getItem(i)); } else { bedrockItems[i] = UNUSUABLE_SPACE_BLOCK; } @@ -69,7 +69,7 @@ public class ChestInventoryUpdater extends InventoryUpdater { InventorySlotPacket slotPacket = new InventorySlotPacket(); slotPacket.setContainerId(inventory.getId()); slotPacket.setSlot(translator.javaSlotToBedrock(javaSlot)); - slotPacket.setItem(ItemTranslator.translateToBedrock(inventory.getItem(javaSlot))); + slotPacket.setItem(ItemTranslator.translateToBedrock(session, inventory.getItem(javaSlot))); session.sendUpstreamPacket(slotPacket); return true; } diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/inventory/updater/ContainerInventoryUpdater.java b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/updater/ContainerInventoryUpdater.java index e84c2931..ec6175c3 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/inventory/updater/ContainerInventoryUpdater.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/updater/ContainerInventoryUpdater.java @@ -40,7 +40,7 @@ public class ContainerInventoryUpdater extends InventoryUpdater { ItemData[] bedrockItems = new ItemData[translator.size]; for (int i = 0; i < bedrockItems.length; i++) { - bedrockItems[translator.javaSlotToBedrock(i)] = ItemTranslator.translateToBedrock(inventory.getItem(i)); + bedrockItems[translator.javaSlotToBedrock(i)] = ItemTranslator.translateToBedrock(session, inventory.getItem(i)); } InventoryContentPacket contentPacket = new InventoryContentPacket(); @@ -57,7 +57,7 @@ public class ContainerInventoryUpdater extends InventoryUpdater { InventorySlotPacket slotPacket = new InventorySlotPacket(); slotPacket.setContainerId(inventory.getId()); slotPacket.setSlot(translator.javaSlotToBedrock(javaSlot)); - slotPacket.setItem(ItemTranslator.translateToBedrock(inventory.getItem(javaSlot))); + slotPacket.setItem(ItemTranslator.translateToBedrock(session, inventory.getItem(javaSlot))); session.sendUpstreamPacket(slotPacket); return true; } diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/inventory/updater/CursorInventoryUpdater.java b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/updater/CursorInventoryUpdater.java index aa7a0174..adbbdbac 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/inventory/updater/CursorInventoryUpdater.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/updater/CursorInventoryUpdater.java @@ -44,7 +44,7 @@ public class CursorInventoryUpdater extends InventoryUpdater { InventorySlotPacket slotPacket = new InventorySlotPacket(); slotPacket.setContainerId(ContainerId.CURSOR); slotPacket.setSlot(bedrockSlot); - slotPacket.setItem(ItemTranslator.translateToBedrock(inventory.getItem(i))); + slotPacket.setItem(ItemTranslator.translateToBedrock(session, inventory.getItem(i))); session.sendUpstreamPacket(slotPacket); } } @@ -57,7 +57,7 @@ public class CursorInventoryUpdater extends InventoryUpdater { InventorySlotPacket slotPacket = new InventorySlotPacket(); slotPacket.setContainerId(ContainerId.CURSOR); slotPacket.setSlot(translator.javaSlotToBedrock(javaSlot)); - slotPacket.setItem(ItemTranslator.translateToBedrock(inventory.getItem(javaSlot))); + slotPacket.setItem(ItemTranslator.translateToBedrock(session, inventory.getItem(javaSlot))); session.sendUpstreamPacket(slotPacket); return true; } diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/inventory/updater/InventoryUpdater.java b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/updater/InventoryUpdater.java index 9710d805..88157df0 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/inventory/updater/InventoryUpdater.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/updater/InventoryUpdater.java @@ -39,7 +39,7 @@ public abstract class InventoryUpdater { ItemData[] bedrockItems = new ItemData[36]; for (int i = 0; i < 36; i++) { final int offset = i < 9 ? 27 : -9; - bedrockItems[i] = ItemTranslator.translateToBedrock(inventory.getItem(translator.size + i + offset)); + bedrockItems[i] = ItemTranslator.translateToBedrock(session, inventory.getItem(translator.size + i + offset)); } InventoryContentPacket contentPacket = new InventoryContentPacket(); contentPacket.setContainerId(ContainerId.INVENTORY); @@ -52,7 +52,7 @@ public abstract class InventoryUpdater { InventorySlotPacket slotPacket = new InventorySlotPacket(); slotPacket.setContainerId(ContainerId.INVENTORY); slotPacket.setSlot(translator.javaSlotToBedrock(javaSlot)); - slotPacket.setItem(ItemTranslator.translateToBedrock(inventory.getItem(javaSlot))); + slotPacket.setItem(ItemTranslator.translateToBedrock(session, inventory.getItem(javaSlot))); session.sendUpstreamPacket(slotPacket); return true; } 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 490681d8..cb8613c8 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 @@ -27,6 +27,8 @@ package org.geysermc.connector.network.translators.item; import com.github.steveice10.mc.protocol.data.game.entity.metadata.ItemStack; +import com.github.steveice10.mc.protocol.data.message.Message; +import com.nukkitx.nbt.CompoundTagBuilder; import com.github.steveice10.opennbt.tag.builtin.*; import com.nukkitx.nbt.tag.CompoundTag; import com.nukkitx.nbt.tag.Tag; @@ -34,7 +36,9 @@ import com.nukkitx.protocol.bedrock.data.ItemData; import it.unimi.dsi.fastutil.ints.Int2ObjectMap; import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; import org.geysermc.connector.GeyserConnector; +import org.geysermc.connector.network.session.GeyserSession; import org.geysermc.connector.network.translators.ItemRemapper; +import org.geysermc.connector.utils.MessageUtils; import org.reflections.Reflections; import java.util.ArrayList; @@ -115,7 +119,7 @@ public abstract class ItemTranslator { return itemStack; } - public static ItemData translateToBedrock(ItemStack stack) { + public static ItemData translateToBedrock(GeyserSession session, ItemStack stack) { if (stack == null) { return ItemData.AIR; } @@ -132,12 +136,42 @@ public abstract class ItemTranslator { } } + ItemData itemData; ItemTranslator itemStackTranslator = ITEM_STACK_TRANSLATORS.get(bedrockItem.getJavaId()); if (itemStackTranslator != null) { - return itemStackTranslator.translateToBedrock(itemStack, bedrockItem); + itemData = itemStackTranslator.translateToBedrock(itemStack, bedrockItem); } else { - return DEFAULT_TRANSLATOR.translateToBedrock(itemStack, bedrockItem); + itemData = DEFAULT_TRANSLATOR.translateToBedrock(itemStack, bedrockItem); } + + + // Get the display name of the item + CompoundTag tag = itemData.getTag(); + if (tag != null) { + CompoundTag display = tag.getCompound("display"); + if (display != null) { + String name = display.getString("Name"); + + // Check if its a message to translate + if (MessageUtils.isMessage(name)) { + // Get the translated name + name = MessageUtils.getTranslatedBedrockMessage(Message.fromString(name), session.getClientData().getLanguageCode()); + + // Build the new display tag + CompoundTagBuilder displayBuilder = display.toBuilder(); + displayBuilder.stringTag("Name", name); + + // Build the new root tag + CompoundTagBuilder builder = tag.toBuilder(); + builder.tag(displayBuilder.build("display")); + + // Create a new item with the original data + updated name + itemData = ItemData.of(itemData.getId(), itemData.getDamage(), itemData.getCount(), builder.buildRootTag()); + } + } + } + + return itemData; } private static final ItemTranslator DEFAULT_TRANSLATOR = new ItemTranslator() { @@ -351,5 +385,49 @@ public abstract class ItemTranslator { return null; } + /** + * Checks if an {@link ItemStack} is equal to another item stack + * + * @param itemStack the item stack to check + * @param equalsItemStack the item stack to check if equal to + * @param checkAmount if the amount should be taken into account + * @param trueIfAmountIsGreater if this should return true if the amount of the + * first item stack is greater than that of the second + * @param checkNbt if NBT data should be checked + * @return if an item stack is equal to another item stack + */ + public boolean equals(ItemStack itemStack, ItemStack equalsItemStack, boolean checkAmount, boolean trueIfAmountIsGreater, boolean checkNbt) { + if (itemStack.getId() != equalsItemStack.getId()) { + return false; + } + if (checkAmount) { + if (trueIfAmountIsGreater) { + if (itemStack.getAmount() < equalsItemStack.getAmount()) { + return false; + } + } else { + if (itemStack.getAmount() != equalsItemStack.getAmount()) { + return false; + } + } + } + + if (!checkNbt) { + return true; + } + if ((itemStack.getNbt() == null || itemStack.getNbt().isEmpty()) && (equalsItemStack.getNbt() != null && !equalsItemStack.getNbt().isEmpty())) { + return false; + } + + if ((itemStack.getNbt() != null && !itemStack.getNbt().isEmpty() && (equalsItemStack.getNbt() == null || !equalsItemStack.getNbt().isEmpty()))) { + return false; + } + + if (itemStack.getNbt() != null && equalsItemStack.getNbt() != null) { + return itemStack.getNbt().equals(equalsItemStack.getNbt()); + } + + return true; + } } diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/item/translators/nbt/FireworkTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/item/translators/nbt/FireworkTranslator.java index 268fe393..5b5182a5 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/item/translators/nbt/FireworkTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/item/translators/nbt/FireworkTranslator.java @@ -27,9 +27,10 @@ package org.geysermc.connector.network.translators.item.translators.nbt; import com.github.steveice10.opennbt.tag.builtin.*; import org.geysermc.connector.network.translators.ItemRemapper; -import org.geysermc.connector.network.translators.item.NbtItemStackTranslator; import org.geysermc.connector.network.translators.item.ItemEntry; +import org.geysermc.connector.network.translators.item.NbtItemStackTranslator; import org.geysermc.connector.utils.FireworkColor; +import org.geysermc.connector.utils.MathUtils; @ItemRemapper public class FireworkTranslator extends NbtItemStackTranslator { @@ -41,6 +42,10 @@ public class FireworkTranslator extends NbtItemStackTranslator { } CompoundTag fireworks = itemTag.get("Fireworks"); + if (fireworks.get("Flight") != null) { + fireworks.put(new ByteTag("Flight", MathUtils.convertByte(fireworks.get("Flight").getValue()))); + } + ListTag explosions = fireworks.get("Explosions"); if (explosions == null) { return; @@ -51,7 +56,7 @@ public class FireworkTranslator extends NbtItemStackTranslator { CompoundTag newEffectData = new CompoundTag(""); if (effectData.get("Type") != null) { - newEffectData.put(new ByteTag("FireworkType", (Byte) effectData.get("Type").getValue())); + newEffectData.put(new ByteTag("FireworkType", MathUtils.convertByte(effectData.get("Type").getValue()))); } if (effectData.get("Colors") != null) { @@ -79,11 +84,11 @@ public class FireworkTranslator extends NbtItemStackTranslator { } if (effectData.get("Trail") != null) { - newEffectData.put(new ByteTag("FireworkTrail", (Byte) effectData.get("Trail").getValue())); + newEffectData.put(new ByteTag("FireworkTrail", MathUtils.convertByte(effectData.get("Trail").getValue()))); } if (effectData.get("Flicker") != null) { - newEffectData.put(new ByteTag("FireworkFlicker", (Byte) effectData.get("Flicker").getValue())); + newEffectData.put(new ByteTag("FireworkFlicker", MathUtils.convertByte(effectData.get("Flicker").getValue()))); } explosions.remove(effect); @@ -94,6 +99,9 @@ public class FireworkTranslator extends NbtItemStackTranslator { @Override public void translateToJava(CompoundTag itemTag, ItemEntry itemEntry) { CompoundTag fireworks = itemTag.get("Fireworks"); + if (fireworks.get("Flight") != null) { + fireworks.put(new ByteTag("Flight", MathUtils.convertByte(fireworks.get("Flight").getValue()))); + } ListTag explosions = fireworks.get("Explosions"); for (Tag effect : explosions.getValue()) { @@ -102,7 +110,7 @@ public class FireworkTranslator extends NbtItemStackTranslator { CompoundTag newEffectData = new CompoundTag(""); if (effectData.get("FireworkType") != null) { - newEffectData.put(new ByteTag("Type", (Byte) effectData.get("FireworkType").getValue())); + newEffectData.put(new ByteTag("Type", MathUtils.convertByte(effectData.get("FireworkType").getValue()))); } if (effectData.get("FireworkColor") != null) { @@ -130,11 +138,11 @@ public class FireworkTranslator extends NbtItemStackTranslator { } if (effectData.get("FireworkTrail") != null) { - newEffectData.put(new ByteTag("Trail", (Byte) effectData.get("FireworkTrail").getValue())); + newEffectData.put(new ByteTag("Trail", MathUtils.convertByte(effectData.get("FireworkTrail").getValue()))); } if (effectData.get("FireworkFlicker") != null) { - newEffectData.put(new ByteTag("Flicker", (Byte) effectData.get("FireworkFlicker").getValue())); + newEffectData.put(new ByteTag("Flicker", MathUtils.convertByte(effectData.get("FireworkFlicker").getValue()))); } explosions.remove(effect); diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/java/JavaDeclareRecipesTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/java/JavaDeclareRecipesTranslator.java index 7763a2c1..b0637722 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/java/JavaDeclareRecipesTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/java/JavaDeclareRecipesTranslator.java @@ -64,7 +64,7 @@ public class JavaDeclareRecipesTranslator extends PacketTranslator> groupedByIds = Arrays.stream(ingredient.getOptions()) - .map(ItemTranslator::translateToBedrock) + .map(item -> ItemTranslator.translateToBedrock(session, item)) .collect(Collectors.groupingBy(item -> new GroupedItem(item.getId(), item.getCount(), item.getTag()))); Set optionSet = new HashSet<>(groupedByIds.size()); for (Map.Entry> entry : groupedByIds.entrySet()) { @@ -136,7 +136,7 @@ public class JavaDeclareRecipesTranslator extends PacketTranslator 0) { - translatedItems[i] = ItemTranslator.translateToBedrock(ingredients[i].getOptions()[0]); + translatedItems[i] = ItemTranslator.translateToBedrock(session, ingredients[i].getOptions()[0]); } else { translatedItems[i] = ItemData.AIR; } diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/java/entity/JavaEntityEquipmentTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/java/entity/JavaEntityEquipmentTranslator.java index e977e9b0..8918217a 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/java/entity/JavaEntityEquipmentTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/java/entity/JavaEntityEquipmentTranslator.java @@ -55,7 +55,7 @@ public class JavaEntityEquipmentTranslator extends PacketTranslator 1.5 || (yDis < 1.45 || yDis > (session.isJumping() ? 4.3 : (session.isSprinting() ? 2.5 : 1.9))) || zDis > 1.5) { - session.setTeleportCache(new TeleportCache(packet.getX(), packet.getY(), packet.getZ(), packet.getTeleportId())); - entity.moveAbsolute(session, Vector3f.from(packet.getX(), packet.getY(), packet.getZ()), packet.getYaw(), packet.getPitch(), true, true); - } else { + if (!(xDis > 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 coordinates are relative, then add to the existing coordinate + double newX = packet.getX() + + (packet.getRelative().contains(PositionElement.X) ? entity.getPosition().getX() : 0); + double newY = packet.getY() + + (packet.getRelative().contains(PositionElement.Y) ? entity.getPosition().getY() - EntityType.PLAYER.getOffset() : 0); + double newZ = packet.getZ() + + (packet.getRelative().contains(PositionElement.Z) ? entity.getPosition().getZ() : 0); + + double newPitch = packet.getPitch() + + (packet.getRelative().contains(PositionElement.PITCH) ? entity.getBedrockRotation().getX() : 0); + double newYaw = packet.getYaw() + + (packet.getRelative().contains(PositionElement.YAW) ? entity.getBedrockRotation().getY() : 0); + + + session.setTeleportCache(new TeleportCache(newX, newY, newZ, packet.getTeleportId())); + entity.moveAbsolute(session, Vector3f.from(newX, newY, newZ), (float) newYaw, (float) newPitch, true, true); } } diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/java/window/JavaCloseWindowTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/java/window/JavaCloseWindowTranslator.java index 7360cbb2..93cfa08e 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/java/window/JavaCloseWindowTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/java/window/JavaCloseWindowTranslator.java @@ -26,7 +26,6 @@ package org.geysermc.connector.network.translators.java.window; import com.github.steveice10.mc.protocol.packet.ingame.server.window.ServerCloseWindowPacket; -import com.nukkitx.protocol.bedrock.packet.ContainerClosePacket; import org.geysermc.connector.network.session.GeyserSession; import org.geysermc.connector.network.translators.PacketTranslator; import org.geysermc.connector.network.translators.Translator; @@ -37,9 +36,7 @@ public class JavaCloseWindowTranslator extends PacketTranslator { @@ -52,10 +50,8 @@ public class JavaOpenWindowTranslator extends PacketTranslator InventoryUtils.openInventory(session, newInventory), 500, TimeUnit.MILLISECONDS); - return; } } diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/java/window/JavaSetSlotTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/java/window/JavaSetSlotTranslator.java index 5d44069f..19d7db21 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/java/window/JavaSetSlotTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/java/window/JavaSetSlotTranslator.java @@ -41,8 +41,6 @@ public class JavaSetSlotTranslator extends PacketTranslator @Override public void translate(ServerSetSlotPacket packet, GeyserSession session) { if (packet.getWindowId() == 255 && packet.getSlot() == -1) { //cursor - if (Objects.equals(session.getInventory().getCursor(), packet.getItem())) - return; if (session.getCraftSlot() != 0) return; diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/java/world/JavaExplosionTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/java/world/JavaExplosionTranslator.java index 49a17f98..79b27f6a 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/java/world/JavaExplosionTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/java/world/JavaExplosionTranslator.java @@ -46,14 +46,17 @@ public class JavaExplosionTranslator extends PacketTranslator= 2.0f ? LevelEventType.PARTICLE_HUGE_EXPLODE : LevelEventType.PARTICLE_LARGE_EXPLOSION); + levelEventPacket.setData(0); + levelEventPacket.setPosition(pos.toFloat()); + session.sendUpstreamPacket(levelEventPacket); + LevelSoundEventPacket levelSoundEventPacket = new LevelSoundEventPacket(); levelSoundEventPacket.setRelativeVolumeDisabled(false); levelSoundEventPacket.setBabySound(false); 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 cc45e345..63512047 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 @@ -64,7 +64,7 @@ public class JavaSpawnParticleTranslator extends PacketTranslator { + + @Override + public void translate(ServerTradeListPacket packet, GeyserSession session) { + Entity villager = session.getPlayerEntity(); + session.setVillagerTrades(packet.getTrades()); + villager.getMetadata().put(EntityData.TRADE_TIER, packet.getVillagerLevel() - 1); + villager.getMetadata().put(EntityData.MAX_TRADE_TIER, 4); + villager.getMetadata().put(EntityData.TRADE_XP, packet.getExperience()); + villager.updateBedrockMetadata(session); + + UpdateTradePacket updateTradePacket = new UpdateTradePacket(); + updateTradePacket.setTradeTier(packet.getVillagerLevel() - 1); + updateTradePacket.setWindowId((short) packet.getWindowId()); + updateTradePacket.setWindowType((short) ContainerType.TRADING.id()); + String displayName; + Entity realVillager = session.getEntityCache().getEntityByGeyserId(session.getLastInteractedVillagerEid()); + if (realVillager != null && realVillager.getMetadata().containsKey(EntityData.NAMETAG) && realVillager.getMetadata().getString(EntityData.NAMETAG) != null) { + displayName = realVillager.getMetadata().getString(EntityData.NAMETAG); + } else { + displayName = packet.isRegularVillager() ? "Villager" : "Wandering Trader"; + } + updateTradePacket.setDisplayName(displayName); + updateTradePacket.setUnknownInt(0); + updateTradePacket.setScreen2(true); + updateTradePacket.setWilling(true); + updateTradePacket.setPlayerUniqueEntityId(session.getPlayerEntity().getGeyserId()); + updateTradePacket.setTraderUniqueEntityId(session.getPlayerEntity().getGeyserId()); + CompoundTagBuilder builder = CompoundTagBuilder.builder(); + List tags = new ArrayList<>(); + for (VillagerTrade trade : packet.getTrades()) { + CompoundTagBuilder recipe = CompoundTagBuilder.builder(); + recipe.intTag("maxUses", trade.getMaxUses()); + recipe.intTag("traderExp", trade.getXp()); + recipe.floatTag("priceMultiplierA", trade.getPriceMultiplier()); + recipe.tag(getItemTag(session, trade.getOutput(), "sell", 0)); + recipe.floatTag("priceMultiplierB", 0.0f); + recipe.intTag("buyCountB", trade.getSecondInput() != null ? trade.getSecondInput().getAmount() : 0); + recipe.intTag("buyCountA", trade.getFirstInput().getAmount()); + recipe.intTag("demand", trade.getDemand()); + recipe.intTag("tier", packet.getVillagerLevel() - 1); + recipe.tag(getItemTag(session, trade.getFirstInput(), "buyA", trade.getSpecialPrice())); + if (trade.getSecondInput() != null) { + recipe.tag(getItemTag(session, trade.getSecondInput(), "buyB", 0)); + } + recipe.intTag("uses", trade.getNumUses()); + recipe.byteTag("rewardExp", (byte) 1); + tags.add(recipe.buildRootTag()); + } + + //Hidden trade to fix visual experience bug + if (packet.isRegularVillager() && packet.getVillagerLevel() < 5) { + tags.add(CompoundTagBuilder.builder() + .intTag("maxUses", 0) + .intTag("traderExp", 0) + .floatTag("priceMultiplierA", 0.0f) + .floatTag("priceMultiplierB", 0.0f) + .intTag("buyCountB", 0) + .intTag("buyCountA", 0) + .intTag("demand", 0) + .intTag("tier", 5) + .intTag("uses", 0) + .byteTag("rewardExp", (byte) 0) + .buildRootTag()); + } + + builder.listTag("Recipes", CompoundTag.class, tags); + List expTags = new ArrayList<>(); + expTags.add(CompoundTagBuilder.builder().intTag("0", 0).buildRootTag()); + expTags.add(CompoundTagBuilder.builder().intTag("1", 10).buildRootTag()); + expTags.add(CompoundTagBuilder.builder().intTag("2", 70).buildRootTag()); + expTags.add(CompoundTagBuilder.builder().intTag("3", 150).buildRootTag()); + expTags.add(CompoundTagBuilder.builder().intTag("4", 250).buildRootTag()); + builder.listTag("TierExpRequirements", CompoundTag.class, expTags); + updateTradePacket.setOffers(builder.buildRootTag()); + session.sendUpstreamPacket(updateTradePacket); + } + + private CompoundTag getItemTag(GeyserSession session, ItemStack stack, String name, int specialPrice) { + ItemData itemData = ItemTranslator.translateToBedrock(session, stack); + ItemEntry itemEntry = ItemRegistry.getItem(stack); + CompoundTagBuilder builder = CompoundTagBuilder.builder(); + builder.byteTag("Count", (byte) (Math.max(itemData.getCount() + specialPrice, 1))); + builder.shortTag("Damage", itemData.getDamage()); + builder.shortTag("id", (short) itemEntry.getBedrockId()); + if (itemData.getTag() != null) { + CompoundTag tag = itemData.getTag().toBuilder().build("tag"); + builder.tag(tag); + } + return builder.build(name); + } +} 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 a40dd15f..d6f446f0 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 @@ -69,6 +69,11 @@ public class BlockTranslator { public static final IntSet JAVA_RUNTIME_WOOL_IDS = new IntOpenHashSet(); public static final int JAVA_RUNTIME_COBWEB_ID; + public static final int JAVA_RUNTIME_FURNACE_ID; + public static final int JAVA_RUNTIME_FURNACE_LIT_ID; + + public static final int JAVA_RUNTIME_SPAWNER_ID; + private static final int BLOCK_STATE_VERSION = 17760256; static { @@ -108,6 +113,9 @@ public class BlockTranslator { int javaRuntimeId = -1; int bedrockRuntimeId = 0; int cobwebRuntimeId = -1; + int furnaceRuntimeId = -1; + int furnaceLitRuntimeId = -1; + int spawnerRuntimeId = -1; Iterator> blocksIterator = blocks.fields(); while (blocksIterator.hasNext()) { javaRuntimeId++; @@ -186,6 +194,18 @@ public class BlockTranslator { } JAVA_TO_BEDROCK_BLOCK_MAP.put(javaRuntimeId, bedrockRuntimeId); + if (javaId.startsWith("minecraft:furnace[facing=north")) { + if (javaId.contains("lit=true")) { + furnaceLitRuntimeId = javaRuntimeId; + } else { + furnaceRuntimeId = javaRuntimeId; + } + } + + if (javaId.startsWith("minecraft:spawner")) { + spawnerRuntimeId = javaRuntimeId; + } + bedrockRuntimeId++; } @@ -194,6 +214,21 @@ public class BlockTranslator { } JAVA_RUNTIME_COBWEB_ID = cobwebRuntimeId; + if (furnaceRuntimeId == -1) { + throw new AssertionError("Unable to find furnace in palette"); + } + JAVA_RUNTIME_FURNACE_ID = furnaceRuntimeId; + + if (furnaceLitRuntimeId == -1) { + throw new AssertionError("Unable to find lit furnace in palette"); + } + JAVA_RUNTIME_FURNACE_LIT_ID = furnaceLitRuntimeId; + + if (spawnerRuntimeId == -1) { + throw new AssertionError("Unable to find spawner in palette"); + } + JAVA_RUNTIME_SPAWNER_ID = spawnerRuntimeId; + if (waterRuntimeId == -1) { throw new AssertionError("Unable to find water in palette"); } 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 3d663926..c87938dd 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 @@ -46,6 +46,19 @@ public abstract class BlockEntityTranslator { public static final Map BLOCK_ENTITY_TRANSLATORS = new HashMap<>(); public static ObjectArrayList REQUIRES_BLOCK_STATE_LIST = new ObjectArrayList<>(); + /** + * Contains a list of irregular block entity name translations that can't be fit into the regex + */ + public static final Map BLOCK_ENTITY_TRANSLATIONS = new HashMap() { + { + // Bedrock/Java differences + put("minecraft:enchanting_table", "EnchantTable"); + put("minecraft:piston_head", "PistonArm"); + put("minecraft:trapped_chest", "Chest"); + // There are some legacy IDs sent but as far as I can tell they are not needed for things to work properly + } + }; + protected BlockEntityTranslator() { } diff --git a/connector/src/main/java/org/geysermc/connector/ping/GeyserLegacyPingPassthrough.java b/connector/src/main/java/org/geysermc/connector/ping/GeyserLegacyPingPassthrough.java index ea0d67c2..6a323d54 100644 --- a/connector/src/main/java/org/geysermc/connector/ping/GeyserLegacyPingPassthrough.java +++ b/connector/src/main/java/org/geysermc/connector/ping/GeyserLegacyPingPassthrough.java @@ -33,9 +33,7 @@ import com.github.steveice10.mc.protocol.data.status.handler.ServerInfoHandler; import com.github.steveice10.packetlib.Client; import com.github.steveice10.packetlib.tcp.TcpSessionFactory; import org.geysermc.common.ping.GeyserPingInfo; -import org.geysermc.connector.GeyserConfiguration; import org.geysermc.connector.GeyserConnector; -import org.geysermc.connector.GeyserLogger; import java.util.concurrent.TimeUnit; diff --git a/connector/src/main/java/org/geysermc/connector/utils/BlockEntityUtils.java b/connector/src/main/java/org/geysermc/connector/utils/BlockEntityUtils.java index 038084c3..3a356e03 100644 --- a/connector/src/main/java/org/geysermc/connector/utils/BlockEntityUtils.java +++ b/connector/src/main/java/org/geysermc/connector/utils/BlockEntityUtils.java @@ -12,29 +12,24 @@ public class BlockEntityUtils { public static String getBedrockBlockEntityId(String id) { // These are the only exceptions when it comes to block entity ids - if (id.contains("piston_head")) - return "PistonArm"; - - if (id.contains("trapped_chest")) - return "Chest"; - - if (id.contains("EnderChest")) - return "EnderChest"; - - if (id.contains("enchanting_table")) { - return "EnchantTable"; + if (BlockEntityTranslator.BLOCK_ENTITY_TRANSLATIONS.containsKey(id)) { + return BlockEntityTranslator.BLOCK_ENTITY_TRANSLATIONS.get(id); } - id = id.toLowerCase() - .replace("minecraft:", "") + id = id.replace("minecraft:", "") .replace("_", " "); - String[] words = id.split(" "); + // Split at every space or capital letter - for the latter, some legacy Java block entity tags are the correct format already + String[] words; + if (!id.toUpperCase().equals(id)) { // Otherwise we get [S, K, U, L, L] + words = id.split("(?=[A-Z])| "); // Split at every space or note or before every capital letter + } else { + words = id.split(" "); + } for (int i = 0; i < words.length; i++) { words[i] = words[i].substring(0, 1).toUpperCase() + words[i].substring(1).toLowerCase(); } - id = String.join(" ", words); - return id.replace(" ", ""); + return String.join("", words); } public static BlockEntityTranslator getBlockEntityTranslator(String name) { 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 010a87af..f86173e2 100644 --- a/connector/src/main/java/org/geysermc/connector/utils/ChunkUtils.java +++ b/connector/src/main/java/org/geysermc/connector/utils/ChunkUtils.java @@ -30,6 +30,8 @@ import com.github.steveice10.mc.protocol.data.game.chunk.Column; import com.github.steveice10.mc.protocol.data.game.entity.metadata.Position; import com.github.steveice10.mc.protocol.data.game.world.block.BlockState; import com.github.steveice10.opennbt.tag.builtin.CompoundTag; +import com.github.steveice10.opennbt.tag.builtin.StringTag; +import com.github.steveice10.opennbt.tag.builtin.Tag; import com.nukkitx.math.vector.Vector2i; import com.nukkitx.math.vector.Vector3i; import com.nukkitx.nbt.CompoundTagBuilder; @@ -137,11 +139,23 @@ public class ChunkUtils { while (i < blockEntities.length) { CompoundTag tag = blockEntities[i]; String tagName; - if (!tag.contains("id")) { - GeyserConnector.getInstance().getLogger().debug("Got tag with no id: " + tag.getValue()); - tagName = "Empty"; - } else { + if (tag.contains("id")) { tagName = (String) tag.get("id").getValue(); + } else { + tagName = "Empty"; + // Sometimes legacy tags have their ID be a StringTag with empty value + for (Tag subTag : tag) { + if (subTag instanceof StringTag) { + StringTag stringTag = (StringTag) subTag; + if (stringTag.getValue().equals("")) { + tagName = stringTag.getName(); + break; + } + } + } + if (tagName.equals("Empty")) { + GeyserConnector.getInstance().getLogger().debug("Got tag with no id: " + tag.getValue()); + } } String id = BlockEntityUtils.getBedrockBlockEntityId(tagName); diff --git a/connector/src/main/java/org/geysermc/connector/utils/InventoryUtils.java b/connector/src/main/java/org/geysermc/connector/utils/InventoryUtils.java index fd4c5e4e..ded47723 100644 --- a/connector/src/main/java/org/geysermc/connector/utils/InventoryUtils.java +++ b/connector/src/main/java/org/geysermc/connector/utils/InventoryUtils.java @@ -31,6 +31,7 @@ import com.nukkitx.nbt.CompoundTagBuilder; import com.nukkitx.nbt.tag.StringTag; import com.nukkitx.protocol.bedrock.data.ContainerId; import com.nukkitx.protocol.bedrock.data.ItemData; +import com.nukkitx.protocol.bedrock.packet.ContainerClosePacket; import com.nukkitx.protocol.bedrock.packet.InventorySlotPacket; import org.geysermc.common.ChatColor; import org.geysermc.connector.GeyserConnector; @@ -53,12 +54,18 @@ public class InventoryUtils { if (translator != null) { session.getInventoryCache().setOpenInventory(inventory); translator.prepareInventory(session, inventory); + //Ensure at least half a second passes between closing and opening a new window + //The client will not open the new window if it is still closing the old one + long delay = 500 - (System.currentTimeMillis() - session.getLastWindowCloseTime()); //TODO: find better way to handle double chest delay if (translator instanceof DoubleChestInventoryTranslator) { + delay = Math.max(delay, 200); + } + if (delay > 0) { GeyserConnector.getInstance().getGeneralThreadPool().schedule(() -> { translator.openInventory(session, inventory); translator.updateInventory(session, inventory); - }, 200, TimeUnit.MILLISECONDS); + }, delay, TimeUnit.MILLISECONDS); } else { translator.openInventory(session, inventory); translator.updateInventory(session, inventory); @@ -69,26 +76,41 @@ public class InventoryUtils { public static void closeInventory(GeyserSession session, int windowId) { if (windowId != 0) { Inventory inventory = session.getInventoryCache().getInventories().get(windowId); - if (inventory != null) { + Inventory openInventory = session.getInventoryCache().getOpenInventory(); + session.getInventoryCache().uncacheInventory(windowId); + if (inventory != null && openInventory != null && inventory.getId() == openInventory.getId()) { InventoryTranslator translator = InventoryTranslator.INVENTORY_TRANSLATORS.get(inventory.getWindowType()); translator.closeInventory(session, inventory); - session.getInventoryCache().uncacheInventory(windowId); session.getInventoryCache().setOpenInventory(null); + } else { + return; } } else { Inventory inventory = session.getInventory(); InventoryTranslator translator = InventoryTranslator.INVENTORY_TRANSLATORS.get(inventory.getWindowType()); translator.updateInventory(session, inventory); } + session.setCraftSlot(0); session.getInventory().setCursor(null); + updateCursor(session); + } + + public static void closeWindow(GeyserSession session, int windowId) { + //Spamming close window packets can bug the client + if (System.currentTimeMillis() - session.getLastWindowCloseTime() > 500) { + ContainerClosePacket closePacket = new ContainerClosePacket(); + closePacket.setWindowId((byte) windowId); + session.sendUpstreamPacket(closePacket); + session.setLastWindowCloseTime(System.currentTimeMillis()); + } } public static void updateCursor(GeyserSession session) { InventorySlotPacket cursorPacket = new InventorySlotPacket(); cursorPacket.setContainerId(ContainerId.CURSOR); cursorPacket.setSlot(0); - cursorPacket.setItem(ItemTranslator.translateToBedrock(session.getInventory().getCursor())); + cursorPacket.setItem(ItemTranslator.translateToBedrock(session, session.getInventory().getCursor())); session.sendUpstreamPacket(cursorPacket); } diff --git a/connector/src/main/java/org/geysermc/connector/utils/MathUtils.java b/connector/src/main/java/org/geysermc/connector/utils/MathUtils.java index 4e8497c6..48702492 100644 --- a/connector/src/main/java/org/geysermc/connector/utils/MathUtils.java +++ b/connector/src/main/java/org/geysermc/connector/utils/MathUtils.java @@ -27,6 +27,8 @@ package org.geysermc.connector.utils; public class MathUtils { + public static final double SQRT_OF_TWO = Math.sqrt(2); + /** * Round the given float to the next whole number * @@ -37,4 +39,19 @@ public class MathUtils { int truncated = (int) floatNumber; return floatNumber > truncated ? truncated + 1 : truncated; } + + /** + * Converts the given object from an int or byte to byte. + * This is used for NBT data that might be either an int + * or byte and bedrock only takes it as an byte + * + * @param value The value to convert + * @return The converted byte + */ + public static Byte convertByte(Object value) { + if (value instanceof Integer) { + return ((Integer) value).byteValue(); + } + return (Byte) value; + } } diff --git a/pom.xml b/pom.xml index a82d6217..3e119eb1 100644 --- a/pom.xml +++ b/pom.xml @@ -38,14 +38,8 @@ - CodeMC-repo - https://repo.codemc.org/repository/maven-public - - true - - - true - + jitpack.io + https://jitpack.io nukkitx-release-repo