Merge latest master; copy over old Geyser-Bukkit configs

This commit is contained in:
DoctorMacc 2020-06-11 16:39:29 -04:00
commit cc3b4c3eda
68 changed files with 1308 additions and 829 deletions

View file

@ -37,7 +37,6 @@ Take a look [here](https://github.com/GeyserMC/Geyser/wiki#Setup) for how to set
- [ ] Beacon - [ ] Beacon
- [ ] Cartography Table - [ ] Cartography Table
- [ ] Stonecutter - [ ] Stonecutter
- [ ] Villager Trading
- Some Entity Flags - Some Entity Flags
## Compiling ## Compiling

View file

@ -25,216 +25,29 @@
package org.geysermc.platform.bungeecord; 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.api.plugin.Plugin;
import net.md_5.bungee.config.Configuration; import net.md_5.bungee.config.Configuration;
import org.geysermc.connector.FloodgateKeyLoader; 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.Path;
import java.nio.file.Paths; import java.nio.file.Paths;
import java.util.HashMap;
import java.util.Map;
public class GeyserBungeeConfiguration implements GeyserConfiguration { @Getter
@JsonIgnoreProperties(ignoreUnknown = true)
private File dataFolder; public class GeyserBungeeConfiguration extends GeyserJacksonConfiguration {
private Configuration config;
private BungeeBedrockConfiguration bedrockConfig;
private BungeeRemoteConfiguration remoteConfig;
private BungeeMetricsInfo metricsInfo;
private Map<String, BungeeUserAuthenticationInfo> userAuthInfo = new HashMap<>();
private Path floodgateKey; private Path floodgateKey;
public GeyserBungeeConfiguration(File dataFolder, Configuration config) { public void loadFloodgate(GeyserBungeePlugin plugin, Configuration configuration) {
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) {
Plugin floodgate = plugin.getProxy().getPluginManager().getPlugin("floodgate-bungee"); 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); 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 BungeeBedrockConfiguration getBedrock() {
return bedrockConfig;
}
@Override
public BungeeRemoteConfiguration getRemote() {
return remoteConfig;
}
@Override
public Map<String, BungeeUserAuthenticationInfo> 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");
} }
@Override @Override
public Path getFloodgateKeyFile() { public Path getFloodgateKeyFile() {
return floodgateKey; 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);
}
} }

View file

@ -31,20 +31,19 @@ import net.md_5.bungee.config.Configuration;
import net.md_5.bungee.config.ConfigurationProvider; import net.md_5.bungee.config.ConfigurationProvider;
import net.md_5.bungee.config.YamlConfiguration; import net.md_5.bungee.config.YamlConfiguration;
import org.geysermc.common.PlatformType; import org.geysermc.common.PlatformType;
import org.geysermc.connector.GeyserConfiguration;
import org.geysermc.connector.GeyserConnector; import org.geysermc.connector.GeyserConnector;
import org.geysermc.connector.bootstrap.GeyserBootstrap; import org.geysermc.connector.bootstrap.GeyserBootstrap;
import org.geysermc.connector.command.CommandManager; import org.geysermc.connector.command.CommandManager;
import org.geysermc.connector.configuration.GeyserConfiguration;
import org.geysermc.connector.ping.GeyserLegacyPingPassthrough; import org.geysermc.connector.ping.GeyserLegacyPingPassthrough;
import org.geysermc.connector.ping.IGeyserPingPassthrough; 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.GeyserBungeeCommandExecutor;
import org.geysermc.platform.bungeecord.command.GeyserBungeeCommandManager; import org.geysermc.platform.bungeecord.command.GeyserBungeeCommandManager;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream;
import java.net.InetSocketAddress; import java.net.InetSocketAddress;
import java.nio.file.Files;
import java.util.UUID; import java.util.UUID;
import java.util.logging.Level; import java.util.logging.Level;
@ -62,32 +61,18 @@ public class GeyserBungeePlugin extends Plugin implements GeyserBootstrap {
if (!getDataFolder().exists()) if (!getDataFolder().exists())
getDataFolder().mkdir(); getDataFolder().mkdir();
File file = new File(getDataFolder(), "config.yml");
Configuration configuration = null; 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 { 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")); configuration = ConfigurationProvider.getProvider(YamlConfiguration.class).load(new File(getDataFolder(), "config.yml"));
} catch(IOException e) { } catch (IOException ex) {
e.printStackTrace(); 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) { if (getProxy().getConfig().getListeners().size() == 1) {
ListenerInfo listener = getProxy().getConfig().getListeners().toArray(new ListenerInfo[0])[0]; 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 // 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 // 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("")) { 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()); this.geyserConfig.getRemote().setPort(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.geyserLogger = new GeyserBungeeLogger(getLogger(), geyserConfig.isDebugMode()); this.geyserLogger = new GeyserBungeeLogger(getLogger(), geyserConfig.isDebugMode());
GeyserConfiguration.checkGeyserConfiguration(geyserConfig, geyserLogger); 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); this.connector = GeyserConnector.start(PlatformType.BUNGEECORD, this);

View file

@ -71,6 +71,10 @@
<pattern>it.unimi.dsi.fastutil</pattern> <pattern>it.unimi.dsi.fastutil</pattern>
<shadedPattern>org.geysermc.platform.spigot.shaded.fastutil</shadedPattern> <shadedPattern>org.geysermc.platform.spigot.shaded.fastutil</shadedPattern>
</relocation> </relocation>
<relocation>
<pattern>com.fasterxml.jackson</pattern>
<shadedPattern>org.geysermc.platform.bukkit.shaded.jackson</shadedPattern>
</relocation>
</relocations> </relocations>
</configuration> </configuration>
</execution> </execution>

View file

@ -25,120 +25,29 @@
package org.geysermc.platform.spigot; 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.Bukkit;
import org.bukkit.configuration.file.FileConfiguration;
import org.bukkit.plugin.Plugin; import org.bukkit.plugin.Plugin;
import org.geysermc.connector.FloodgateKeyLoader; 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.Path;
import java.nio.file.Paths; 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; @JsonProperty("floodgate-key-file")
private File dataFolder; private String floodgateKeyFile;
private BukkitBedrockConfiguration bedrockConfig;
private BukkitRemoteConfiguration remoteConfig;
private BukkitMetricsInfo metricsInfo;
private Map<String, BukkitUserAuthenticationInfo> userAuthInfo = new HashMap<>();
private Path floodgateKey; 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) { public void loadFloodgate(GeyserSpigotPlugin plugin) {
Plugin floodgate = Bukkit.getPluginManager().getPlugin("floodgate-bukkit"); 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); 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
public IBedrockConfiguration getBedrock() {
return bedrockConfig;
}
@Override
public IRemoteConfiguration getRemote() {
return remoteConfig;
}
@Override
public Map<String, BukkitUserAuthenticationInfo> 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");
} }
@Override @Override
@ -150,92 +59,4 @@ public class GeyserSpigotConfiguration implements GeyserConfiguration {
public boolean isCacheChunks() { public boolean isCacheChunks() {
return true; // We override this as with Bukkit, we have direct access to the server implementation 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);
}
} }

View file

@ -28,10 +28,10 @@ package org.geysermc.platform.spigot;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;
import org.bukkit.plugin.java.JavaPlugin; import org.bukkit.plugin.java.JavaPlugin;
import org.geysermc.common.PlatformType; import org.geysermc.common.PlatformType;
import org.geysermc.connector.GeyserConfiguration;
import org.geysermc.connector.GeyserConnector; import org.geysermc.connector.GeyserConnector;
import org.geysermc.connector.bootstrap.GeyserBootstrap; import org.geysermc.connector.bootstrap.GeyserBootstrap;
import org.geysermc.connector.command.CommandManager; import org.geysermc.connector.command.CommandManager;
import org.geysermc.connector.configuration.GeyserConfiguration;
import org.geysermc.connector.network.translators.world.WorldManager; import org.geysermc.connector.network.translators.world.WorldManager;
import org.geysermc.connector.ping.GeyserLegacyPingPassthrough; import org.geysermc.connector.ping.GeyserLegacyPingPassthrough;
import org.geysermc.connector.ping.IGeyserPingPassthrough; 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.command.GeyserSpigotCommandManager;
import org.geysermc.platform.spigot.world.GeyserSpigotBlockPlaceListener; import org.geysermc.platform.spigot.world.GeyserSpigotBlockPlaceListener;
import org.geysermc.platform.spigot.world.GeyserSpigotWorldManager; import org.geysermc.platform.spigot.world.GeyserSpigotWorldManager;
import org.geysermc.connector.utils.FileUtils;
import us.myles.ViaVersion.api.Via; 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.UUID;
import java.util.logging.Level;
public class GeyserSpigotPlugin extends JavaPlugin implements GeyserBootstrap { public class GeyserSpigotPlugin extends JavaPlugin implements GeyserBootstrap {
@ -56,26 +61,41 @@ public class GeyserSpigotPlugin extends JavaPlugin implements GeyserBootstrap {
@Override @Override
public void onEnable() { public void onEnable() {
saveDefaultConfig(); // This is manually done instead of using Bukkit methods to save the config because otherwise comments get removed
try {
this.geyserConfig = new GeyserSpigotConfiguration(getDataFolder(), getConfig()); if (!getDataFolder().exists()) {
if (geyserConfig.getMetrics().getUniqueId().equals("generateduuid")) { getDataFolder().mkdir();
getConfig().set("metrics.uuid", UUID.randomUUID().toString()); File bukkitConfig = new File("plugins/Geyser-Bukkit/config.yml");
saveConfig(); 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 // 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 // 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("")) { 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()); geyserConfig.getRemote().setPort(Bukkit.getPort());
saveConfig();
this.geyserLogger = new GeyserSpigotLogger(getLogger(), geyserConfig.isDebugMode()); this.geyserLogger = new GeyserSpigotLogger(getLogger(), geyserConfig.isDebugMode());
GeyserConfiguration.checkGeyserConfiguration(geyserConfig, geyserLogger); 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); geyserConfig.loadFloodgate(this);
this.connector = GeyserConnector.start(PlatformType.SPIGOT, this); this.connector = GeyserConnector.start(PlatformType.SPIGOT, this);
@ -113,6 +133,7 @@ public class GeyserSpigotPlugin extends JavaPlugin implements GeyserBootstrap {
@Override @Override
public void onDisable() { public void onDisable() {
if (connector != null)
connector.shutdown(); connector.shutdown();
} }

View file

@ -29,7 +29,7 @@ import lombok.AllArgsConstructor;
import ninja.leaping.configurate.ConfigurationNode; import ninja.leaping.configurate.ConfigurationNode;
import org.geysermc.connector.GeyserConfiguration; import org.geysermc.connector.configuration.GeyserConfiguration;
import java.io.File; import java.io.File;
import java.nio.file.Path; import java.nio.file.Path;

View file

@ -30,7 +30,7 @@ import ninja.leaping.configurate.ConfigurationNode;
import ninja.leaping.configurate.loader.ConfigurationLoader; import ninja.leaping.configurate.loader.ConfigurationLoader;
import ninja.leaping.configurate.yaml.YAMLConfigurationLoader; import ninja.leaping.configurate.yaml.YAMLConfigurationLoader;
import org.geysermc.common.PlatformType; 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.GeyserConnector;
import org.geysermc.connector.bootstrap.GeyserBootstrap; import org.geysermc.connector.bootstrap.GeyserBootstrap;
import org.geysermc.connector.command.CommandManager; import org.geysermc.connector.command.CommandManager;

View file

@ -26,12 +26,12 @@
package org.geysermc.platform.standalone; package org.geysermc.platform.standalone;
import org.geysermc.common.PlatformType; 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.GeyserConnector;
import org.geysermc.connector.bootstrap.GeyserBootstrap;
import org.geysermc.connector.configuration.GeyserConfiguration;
import org.geysermc.connector.command.CommandManager; import org.geysermc.connector.command.CommandManager;
import org.geysermc.connector.ping.IGeyserPingPassthrough; import org.geysermc.connector.ping.IGeyserPingPassthrough;
import org.geysermc.connector.ping.GeyserLegacyPingPassthrough;
import org.geysermc.connector.utils.FileUtils; import org.geysermc.connector.utils.FileUtils;
import org.geysermc.platform.standalone.command.GeyserCommandManager; import org.geysermc.platform.standalone.command.GeyserCommandManager;

View file

@ -27,110 +27,21 @@ package org.geysermc.platform.standalone;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Getter; import lombok.Getter;
import org.geysermc.connector.GeyserConfiguration; import org.geysermc.connector.configuration.GeyserJacksonConfiguration;
import java.nio.file.Path; import java.nio.file.Path;
import java.nio.file.Paths; import java.nio.file.Paths;
import java.util.Map;
@JsonIgnoreProperties(ignoreUnknown = true)
@Getter @Getter
public class GeyserStandaloneConfiguration implements GeyserConfiguration { @JsonIgnoreProperties(ignoreUnknown = true)
public class GeyserStandaloneConfiguration extends GeyserJacksonConfiguration {
private BedrockConfiguration bedrock;
private RemoteConfiguration remote;
@JsonProperty("floodgate-key-file") @JsonProperty("floodgate-key-file")
private String floodgateKeyFile; private String floodgateKeyFile;
private Map<String, UserAuthenticationInfo> 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 @Override
public Path getFloodgateKeyFile() { public Path getFloodgateKeyFile() {
return Paths.get(floodgateKeyFile); 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;
} }

View file

@ -30,122 +30,30 @@ import com.fasterxml.jackson.annotation.JsonProperty;
import com.velocitypowered.api.plugin.PluginContainer; import com.velocitypowered.api.plugin.PluginContainer;
import com.velocitypowered.api.proxy.ProxyServer; import com.velocitypowered.api.proxy.ProxyServer;
import lombok.Getter; import lombok.Getter;
import lombok.Setter;
import org.geysermc.connector.FloodgateKeyLoader; import org.geysermc.connector.FloodgateKeyLoader;
import org.geysermc.connector.GeyserConfiguration; import org.geysermc.connector.configuration.GeyserJacksonConfiguration;
import java.io.File; import java.io.File;
import java.nio.file.Path; import java.nio.file.Path;
import java.nio.file.Paths; import java.nio.file.Paths;
import java.util.Map;
import java.util.Optional; import java.util.Optional;
@JsonIgnoreProperties(ignoreUnknown = true)
@Getter @Getter
public class GeyserVelocityConfiguration implements GeyserConfiguration { @JsonIgnoreProperties(ignoreUnknown = true)
public class GeyserVelocityConfiguration extends GeyserJacksonConfiguration {
private BedrockConfiguration bedrock;
private RemoteConfiguration remote;
@JsonProperty("floodgate-key-file") @JsonProperty("floodgate-key-file")
private String floodgateKeyFile; private String floodgateKeyFile;
private Map<String, UserAuthenticationInfo> 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; private Path floodgateKey;
public void loadFloodgate(GeyserVelocityPlugin plugin, ProxyServer proxyServer, File dataFolder) {
Optional<PluginContainer> 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 @Override
public Path getFloodgateKeyFile() { public Path getFloodgateKeyFile() {
return floodgateKey; return floodgateKey;
} }
@Getter public void loadFloodgate(GeyserVelocityPlugin plugin, ProxyServer proxyServer, File dataFolder) {
public static class BedrockConfiguration implements IBedrockConfiguration { Optional<PluginContainer> 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/")));
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;
} }

View file

@ -35,7 +35,7 @@ import com.velocitypowered.api.plugin.Plugin;
import com.velocitypowered.api.proxy.ProxyServer; import com.velocitypowered.api.proxy.ProxyServer;
import org.geysermc.common.PlatformType; 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.GeyserConnector;
import org.geysermc.connector.bootstrap.GeyserBootstrap; import org.geysermc.connector.bootstrap.GeyserBootstrap;
import org.geysermc.connector.ping.GeyserLegacyPingPassthrough; import org.geysermc.connector.ping.GeyserLegacyPingPassthrough;
@ -96,6 +96,11 @@ public class GeyserVelocityPlugin implements GeyserBootstrap {
this.geyserLogger = new GeyserVelocityLogger(logger, geyserConfig.isDebugMode()); this.geyserLogger = new GeyserVelocityLogger(logger, geyserConfig.isDebugMode());
GeyserConfiguration.checkGeyserConfiguration(geyserConfig, geyserLogger); 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); geyserConfig.loadFloodgate(this, proxyServer, configDir);
this.connector = GeyserConnector.start(PlatformType.VELOCITY, this); this.connector = GeyserConnector.start(PlatformType.VELOCITY, this);

View file

@ -98,14 +98,8 @@
</dependency> </dependency>
<dependency> <dependency>
<groupId>com.github.steveice10</groupId> <groupId>com.github.steveice10</groupId>
<artifactId>opennbt</artifactId> <artifactId>mcprotocollib</artifactId>
<version>1.4-SNAPSHOT</version> <version>4c315aa206</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>com.github.steveice10</groupId>
<artifactId>packetlib</artifactId>
<version>1.5-SNAPSHOT</version>
<scope>compile</scope> <scope>compile</scope>
<exclusions> <exclusions>
<exclusion> <exclusion>
@ -115,31 +109,11 @@
</exclusions> </exclusions>
</dependency> </dependency>
<dependency> <dependency>
<groupId>com.github.steveice10</groupId> <groupId>io.netty</groupId>
<artifactId>mcauthlib</artifactId> <artifactId>netty-resolver-dns</artifactId>
<version>1.3-SNAPSHOT</version> <version>4.1.43.Final</version>
<scope>compile</scope> <scope>compile</scope>
</dependency> </dependency>
<dependency>
<groupId>com.github.steveice10</groupId>
<artifactId>mcprotocollib</artifactId>
<version>1.15.2-1-SNAPSHOT</version>
<scope>compile</scope>
<exclusions>
<exclusion>
<groupId>com.github.steveice10</groupId>
<artifactId>opennbt</artifactId>
</exclusion>
<exclusion>
<groupId>com.github.steveice10</groupId>
<artifactId>packetlib</artifactId>
</exclusion>
<exclusion>
<groupId>com.github.steveice10</groupId>
<artifactId>mcauthlib</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency> <dependency>
<groupId>org.reflections</groupId> <groupId>org.reflections</groupId>
<artifactId>reflections</artifactId> <artifactId>reflections</artifactId>

View file

@ -26,6 +26,8 @@
package org.geysermc.connector; package org.geysermc.connector;
import org.geysermc.connector.configuration.GeyserConfiguration;
import java.nio.file.Files; import java.nio.file.Files;
import java.nio.file.Path; import java.nio.file.Path;

View file

@ -35,6 +35,7 @@ import org.geysermc.common.AuthType;
import org.geysermc.common.PlatformType; import org.geysermc.common.PlatformType;
import org.geysermc.connector.bootstrap.GeyserBootstrap; import org.geysermc.connector.bootstrap.GeyserBootstrap;
import org.geysermc.connector.command.CommandManager; import org.geysermc.connector.command.CommandManager;
import org.geysermc.connector.configuration.GeyserConfiguration;
import org.geysermc.connector.metrics.Metrics; import org.geysermc.connector.metrics.Metrics;
import org.geysermc.connector.network.ConnectorServerEventHandler; import org.geysermc.connector.network.ConnectorServerEventHandler;
import org.geysermc.connector.network.remote.RemoteServer; import org.geysermc.connector.network.remote.RemoteServer;

View file

@ -27,7 +27,7 @@
package org.geysermc.connector.bootstrap; package org.geysermc.connector.bootstrap;
import org.geysermc.connector.ping.IGeyserPingPassthrough; 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.GeyserLogger;
import org.geysermc.connector.command.CommandManager; import org.geysermc.connector.command.CommandManager;
import org.geysermc.connector.network.translators.world.CachedChunkManager; import org.geysermc.connector.network.translators.world.CachedChunkManager;

View file

@ -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.nio.file.Path;
import java.util.Map; import java.util.Map;

View file

@ -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<String, UserAuthenticationInfo> 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;
}

View file

@ -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() { }
}

View file

@ -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.BlockFace;
import com.github.steveice10.mc.protocol.data.game.world.block.BlockState; 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.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.ClientPlayerActionPacket;
import com.github.steveice10.mc.protocol.packet.ingame.client.player.ClientPlayerUseItemPacket; import com.github.steveice10.mc.protocol.packet.ingame.client.player.ClientPlayerUseItemPacket;
import com.nukkitx.math.vector.Vector3f; 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.ON_FIRE, (xd & 0x01) == 0x01);
metadata.getFlags().setFlag(EntityFlag.SNEAKING, (xd & 0x02) == 0x02); metadata.getFlags().setFlag(EntityFlag.SNEAKING, (xd & 0x02) == 0x02);
metadata.getFlags().setFlag(EntityFlag.SPRINTING, (xd & 0x08) == 0x08); 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); metadata.getFlags().setFlag(EntityFlag.GLIDING, (xd & 0x80) == 0x80);
if ((xd & 0x20) == 0x20) { if ((xd & 0x20) == 0x20) {
if (this.is(ArmorStandEntity.class)) { // Armour stands are handled in their own class
metadata.put(EntityData.SCALE, 0.0f); if (!this.is(ArmorStandEntity.class)) {
} else {
metadata.getFlags().setFlag(EntityFlag.INVISIBLE, true); metadata.getFlags().setFlag(EntityFlag.INVISIBLE, true);
} }
} else { } else {
@ -253,9 +253,15 @@ public class Entity {
} }
break; break;
case 2: // custom name case 2: // custom name
if (entityMetadata.getValue() instanceof TextMessage) {
TextMessage name = (TextMessage) entityMetadata.getValue(); TextMessage name = (TextMessage) entityMetadata.getValue();
if (name != null) if (name != null)
metadata.put(EntityData.NAMETAG, MessageUtils.getBedrockMessage(name)); 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; break;
case 3: // is custom name visible case 3: // is custom name visible
if (!this.is(PlayerEntity.class)) if (!this.is(PlayerEntity.class))

View file

@ -37,6 +37,7 @@ import com.nukkitx.protocol.bedrock.packet.SetEntityMotionPacket;
import org.geysermc.connector.entity.type.EntityType; import org.geysermc.connector.entity.type.EntityType;
import org.geysermc.connector.network.session.GeyserSession; import org.geysermc.connector.network.session.GeyserSession;
import org.geysermc.connector.utils.FireworkColor; import org.geysermc.connector.utils.FireworkColor;
import org.geysermc.connector.utils.MathUtils;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
@ -63,7 +64,7 @@ public class FireworkEntity extends Entity {
CompoundTagBuilder fireworksBuilder = CompoundTagBuilder.builder(); CompoundTagBuilder fireworksBuilder = CompoundTagBuilder.builder();
if (fireworks.get("Flight") != null) { if (fireworks.get("Flight") != null) {
fireworksBuilder.byteTag("Flight", (Byte) fireworks.get("Flight").getValue()); fireworksBuilder.byteTag("Flight", MathUtils.convertByte(fireworks.get("Flight").getValue()));
} }
List<com.nukkitx.nbt.tag.CompoundTag> explosions = new ArrayList<>(); List<com.nukkitx.nbt.tag.CompoundTag> explosions = new ArrayList<>();
@ -73,7 +74,7 @@ public class FireworkEntity extends Entity {
CompoundTagBuilder effectBuilder = CompoundTagBuilder.builder(); CompoundTagBuilder effectBuilder = CompoundTagBuilder.builder();
if (effectData.get("Type") != null) { 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) { if (effectData.get("Colors") != null) {
@ -101,11 +102,11 @@ public class FireworkEntity extends Entity {
} }
if (effectData.get("Trail") != null) { 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) { 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()); explosions.add(effectBuilder.buildRootTag());

View file

@ -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);
}
}

View file

@ -49,7 +49,7 @@ public class ItemEntity extends Entity {
itemPacket.setUniqueEntityId(geyserId); itemPacket.setUniqueEntityId(geyserId);
itemPacket.setFromFishing(false); itemPacket.setFromFishing(false);
itemPacket.getMetadata().putAll(metadata); itemPacket.getMetadata().putAll(metadata);
itemPacket.setItemInHand(ItemTranslator.translateToBedrock((ItemStack) entityMetadata.getValue())); itemPacket.setItemInHand(ItemTranslator.translateToBedrock(session, (ItemStack) entityMetadata.getValue()));
session.sendUpstreamPacket(itemPacket); session.sendUpstreamPacket(itemPacket);
} }

View file

@ -98,7 +98,7 @@ public class ItemFrameEntity extends Entity {
@Override @Override
public void updateBedrockMetadata(EntityMetadata entityMetadata, GeyserSession session) { public void updateBedrockMetadata(EntityMetadata entityMetadata, GeyserSession session) {
if (entityMetadata.getId() == 7 && entityMetadata.getValue() != null) { 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()); ItemEntry itemEntry = ItemRegistry.getItem((ItemStack) entityMetadata.getValue());
CompoundTagBuilder builder = CompoundTag.builder(); CompoundTagBuilder builder = CompoundTag.builder();

View file

@ -30,6 +30,7 @@ import com.nukkitx.math.vector.Vector3f;
import com.nukkitx.protocol.bedrock.data.EntityData; import com.nukkitx.protocol.bedrock.data.EntityData;
import org.geysermc.connector.entity.type.EntityType; import org.geysermc.connector.entity.type.EntityType;
import org.geysermc.connector.network.session.GeyserSession; import org.geysermc.connector.network.session.GeyserSession;
import org.geysermc.connector.network.translators.world.block.BlockTranslator;
public class MinecartEntity extends Entity { 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)); 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); super.updateBedrockMetadata(entityMetadata, session);
} }

View file

@ -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);
}
}

View file

@ -35,17 +35,42 @@ import org.geysermc.connector.network.session.GeyserSession;
public class ArmorStandEntity extends LivingEntity { 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) { public ArmorStandEntity(long entityId, long geyserId, EntityType entityType, Vector3f position, Vector3f motion, Vector3f rotation) {
super(entityId, geyserId, entityType, position, motion, 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 @Override
public void updateBedrockMetadata(EntityMetadata entityMetadata, GeyserSession session) { 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(); byte xd = (byte) entityMetadata.getValue();
// isSmall // isSmall
if ((xd & 0x01) == 0x01) { if ((xd & 0x01) == 0x01) {
isSmall = true;
if (metadata.getFloat(EntityData.SCALE) != 0.55f && metadata.getFloat(EntityData.SCALE) != 0.0f) { if (metadata.getFloat(EntityData.SCALE) != 0.55f && metadata.getFloat(EntityData.SCALE) != 0.0f) {
metadata.put(EntityData.SCALE, 0.55f); metadata.put(EntityData.SCALE, 0.55f);
} }
@ -60,9 +85,10 @@ public class ArmorStandEntity extends LivingEntity {
} }
// setMarker // 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_WIDTH, 0.0f);
metadata.put(EntityData.BOUNDING_BOX_HEIGHT, 0.0f); metadata.put(EntityData.BOUNDING_BOX_HEIGHT, 0.0f);
isMarker = true;
} }
} }
super.updateBedrockMetadata(entityMetadata, session); super.updateBedrockMetadata(entityMetadata, session);

View file

@ -105,7 +105,7 @@ public enum EntityType {
THROWN_EXP_BOTTLE(ThrowableEntity.class, 68, 0.25f, 0.25f, 0f, 0f, "minecraft:xp_bottle"), 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"), 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"), 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"), 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"), TRIDENT(TridentEntity.class, 73, 0f, 0f, 0f, 0f, "minecraft:thrown_trident"),
TURTLE(AnimalEntity.class, 74, 0.4f, 1.2f), 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_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_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_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"), MINECART_COMMAND_BLOCK(MinecartEntity.class, 100, 0.7f, 0.98f, 0.98f, 0.35f, "minecraft:command_block_minecart"),
LINGERING_POTION(ThrowableEntity.class, 101, 0f), LINGERING_POTION(ThrowableEntity.class, 101, 0f),
LLAMA_SPIT(Entity.class, 102, 0.25f), LLAMA_SPIT(Entity.class, 102, 0.25f),
EVOKER_FANGS(Entity.class, 103, 0.8f, 0.5f), EVOKER_FANGS(Entity.class, 103, 0.8f, 0.5f, 0.5f, 0f, "minecraft:evocation_fang"),
EVOKER(SpellcasterIllagerEntity.class, 104, 1.95f, 0.5f), EVOKER(SpellcasterIllagerEntity.class, 104, 1.95f, 0.6f, 0.6f, 0f, "minecraft:evocation_illager"),
VEX(MonsterEntity.class, 105, 0.8f, 0.4f), VEX(MonsterEntity.class, 105, 0.8f, 0.4f),
ICE_BOMB(Entity.class, 106, 0f), ICE_BOMB(Entity.class, 106, 0f),
BALLOON(Entity.class, 107, 0f), //TODO BALLOON(Entity.class, 107, 0f), //TODO

View file

@ -31,7 +31,7 @@ import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.socket.DatagramPacket; import io.netty.channel.socket.DatagramPacket;
import org.geysermc.common.ping.GeyserPingInfo; import org.geysermc.common.ping.GeyserPingInfo;
import org.geysermc.connector.ping.IGeyserPingPassthrough; 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.GeyserConnector;
import org.geysermc.connector.network.session.GeyserSession; import org.geysermc.connector.network.session.GeyserSession;
import org.geysermc.connector.utils.MessageUtils; import org.geysermc.connector.utils.MessageUtils;

View file

@ -60,6 +60,7 @@ public class QueryPacketHandler {
/** /**
* The Query packet handler instance * The Query packet handler instance
*
* @param connector Geyser Connector * @param connector Geyser Connector
* @param sender The Sender IP/Port for the Query * @param sender The Sender IP/Port for the Query
* @param buffer The Query data * @param buffer The Query data
@ -79,11 +80,12 @@ public class QueryPacketHandler {
/** /**
* Checks the packet is in fact a query packet * Checks the packet is in fact a query packet
*
* @param buffer Query data * @param buffer Query data
* @return if the packet is a query packet * @return if the packet is a query packet
*/ */
private boolean isQueryPacket(ByteBuf buffer) { 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 * Gets the game data for the query
*
* @return the game data for the query * @return the game data for the query
*/ */
private byte[] getGameData() { private byte[] getGameData() {
@ -177,7 +180,7 @@ public class QueryPacketHandler {
// Blank Buffer Bytes // Blank Buffer Bytes
query.write("GeyserMC".getBytes()); query.write("GeyserMC".getBytes());
query.write((byte) 0x00); query.write((byte) 0x00);
query.write((byte) 128); query.write((byte) 0x80);
query.write((byte) 0x00); query.write((byte) 0x00);
// Fills the game data // Fills the game data
@ -189,7 +192,7 @@ public class QueryPacketHandler {
} }
// Final byte to show the end of the game data // 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(); return query.toByteArray();
} catch (IOException e) { } catch (IOException e) {
e.printStackTrace(); 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() { private byte[] getPlayers() {
ByteArrayOutputStream query = new ByteArrayOutputStream(); ByteArrayOutputStream query = new ByteArrayOutputStream();
@ -208,7 +216,7 @@ public class QueryPacketHandler {
try { try {
// Start the player section // Start the player section
query.write("player_".getBytes()); query.write("player_".getBytes());
query.write(new byte[]{0x00, 0x00}); query.write(new byte[] { 0x00, 0x00 });
// Fill player names // Fill player names
if(pingInfo != null) { if(pingInfo != null) {
@ -229,6 +237,7 @@ public class QueryPacketHandler {
/** /**
* Sends a packet to the sender * Sends a packet to the sender
*
* @param data packet data * @param data packet data
*/ */
private void sendPacket(ByteBuf data) { private void sendPacket(ByteBuf data) {
@ -251,18 +260,28 @@ public class QueryPacketHandler {
* Gets an MD5 token for the current IP/Port. * Gets an MD5 token for the current IP/Port.
* This should reset every 30 seconds but a new one is generated per instance * 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. * Seems wasteful to code something in to clear it when it has no use.
*
* @param token the token * @param token the token
* @param address the address * @param address the address
* @return an MD5 token for the current IP/Port * @return an MD5 token for the current IP/Port
*/ */
public static byte[] getTokenString(byte[] token, InetAddress address) { public static byte[] getTokenString(byte[] token, InetAddress address) {
try { try {
// Generate an MD5 hash from the address
MessageDigest digest = MessageDigest.getInstance("MD5"); MessageDigest digest = MessageDigest.getInstance("MD5");
digest.update(address.toString().getBytes(StandardCharsets.UTF_8)); digest.update(address.toString().getBytes(StandardCharsets.UTF_8));
digest.update(token); 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) { } catch (NoSuchAlgorithmException e) {
return ByteBuffer.allocate(4).putInt(ThreadLocalRandom.current().nextInt()).array(); return (ByteBuffer.allocate(4).putInt(ThreadLocalRandom.current().nextInt()).getInt() + "\0").getBytes();
} }
} }
} }

View file

@ -28,7 +28,7 @@ package org.geysermc.connector.network;
import com.nukkitx.protocol.bedrock.BedrockPacket; import com.nukkitx.protocol.bedrock.BedrockPacket;
import com.nukkitx.protocol.bedrock.packet.*; import com.nukkitx.protocol.bedrock.packet.*;
import org.geysermc.common.AuthType; 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.GeyserConnector;
import org.geysermc.connector.network.session.GeyserSession; import org.geysermc.connector.network.session.GeyserSession;
import org.geysermc.connector.network.translators.PacketTranslatorRegistry; import org.geysermc.connector.network.translators.PacketTranslatorRegistry;

View file

@ -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.MinecraftProtocol;
import com.github.steveice10.mc.protocol.data.SubProtocol; 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.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.data.game.world.block.BlockState;
import com.github.steveice10.mc.protocol.packet.handshake.client.HandshakePacket; import com.github.steveice10.mc.protocol.packet.handshake.client.HandshakePacket;
import com.github.steveice10.mc.protocol.packet.ingame.client.world.ClientTeleportConfirmPacket; import com.github.steveice10.mc.protocol.packet.ingame.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.packet.Packet;
import com.github.steveice10.packetlib.tcp.TcpSessionFactory; import com.github.steveice10.packetlib.tcp.TcpSessionFactory;
import com.nukkitx.math.GenericMath; import com.nukkitx.math.GenericMath;
import com.nukkitx.math.TrigMath;
import com.nukkitx.math.vector.*; import com.nukkitx.math.vector.*;
import com.nukkitx.protocol.bedrock.BedrockPacket; import com.nukkitx.protocol.bedrock.BedrockPacket;
import com.nukkitx.protocol.bedrock.BedrockServerSession; import com.nukkitx.protocol.bedrock.BedrockServerSession;
import com.nukkitx.protocol.bedrock.data.ContainerId; import com.nukkitx.protocol.bedrock.data.*;
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.packet.*; import com.nukkitx.protocol.bedrock.packet.*;
import it.unimi.dsi.fastutil.objects.Object2LongMap; import it.unimi.dsi.fastutil.objects.Object2LongMap;
import it.unimi.dsi.fastutil.objects.Object2LongOpenHashMap; 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.command.CommandSender;
import org.geysermc.connector.entity.Entity; import org.geysermc.connector.entity.Entity;
import org.geysermc.connector.entity.PlayerEntity; import org.geysermc.connector.entity.PlayerEntity;
import org.geysermc.connector.entity.attribute.AttributeType;
import org.geysermc.connector.inventory.PlayerInventory; import org.geysermc.connector.inventory.PlayerInventory;
import org.geysermc.connector.network.remote.RemoteServer; import org.geysermc.connector.network.remote.RemoteServer;
import org.geysermc.connector.network.session.auth.AuthData; 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.network.translators.world.block.BlockTranslator;
import org.geysermc.connector.utils.ChunkUtils; import org.geysermc.connector.utils.ChunkUtils;
import org.geysermc.connector.utils.LocaleUtils; import org.geysermc.connector.utils.LocaleUtils;
import org.geysermc.connector.utils.MathUtils;
import org.geysermc.connector.utils.SkinUtils; import org.geysermc.connector.utils.SkinUtils;
import org.geysermc.floodgate.util.BedrockData; import org.geysermc.floodgate.util.BedrockData;
import org.geysermc.floodgate.util.EncryptionUtil; import org.geysermc.floodgate.util.EncryptionUtil;
@ -81,6 +80,8 @@ import java.net.InetSocketAddress;
import java.security.NoSuchAlgorithmException; import java.security.NoSuchAlgorithmException;
import java.security.PublicKey; import java.security.PublicKey;
import java.security.spec.InvalidKeySpecException; import java.security.spec.InvalidKeySpecException;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID; import java.util.UUID;
import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicInteger;
@ -166,6 +167,14 @@ public class GeyserSession implements CommandSender {
@Setter @Setter
private int craftSlot = 0; private int craftSlot = 0;
@Setter
private long lastWindowCloseTime = 0;
@Setter
private VillagerTrade[] villagerTrades;
@Setter
private long lastInteractedVillagerEid;
private MinecraftProtocol protocol; private MinecraftProtocol protocol;
public GeyserSession(GeyserConnector connector, BedrockServerSession bedrockServerSession) { public GeyserSession(GeyserConnector connector, BedrockServerSession bedrockServerSession) {
@ -211,6 +220,15 @@ public class GeyserSession implements CommandSender {
PlayStatusPacket playStatusPacket = new PlayStatusPacket(); PlayStatusPacket playStatusPacket = new PlayStatusPacket();
playStatusPacket.setStatus(PlayStatusPacket.Status.PLAYER_SPAWN); playStatusPacket.setStatus(PlayStatusPacket.Status.PLAYER_SPAWN);
upstream.sendPacket(playStatusPacket); upstream.sendPacket(playStatusPacket);
UpdateAttributesPacket attributesPacket = new UpdateAttributesPacket();
attributesPacket.setRuntimeEntityId(getPlayerEntity().getGeyserId());
List<Attribute> 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) { 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); 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(); downstream.getSession().connect();
@ -436,7 +462,7 @@ public class GeyserSession implements CommandSender {
} }
public void setRenderDistance(int renderDistance) { 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) if (renderDistance > 32) renderDistance = 32; // <3 u ViaVersion but I don't like crashing clients x)
this.renderDistance = renderDistance; this.renderDistance = renderDistance;

View file

@ -25,18 +25,19 @@
package org.geysermc.connector.network.translators; package org.geysermc.connector.network.translators;
import java.util.HashMap; import com.github.steveice10.mc.protocol.packet.ingame.server.ServerKeepAlivePacket;
import java.util.Map; 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.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 it.unimi.dsi.fastutil.objects.ObjectArrayList;
import org.geysermc.connector.GeyserConnector; import org.geysermc.connector.GeyserConnector;
import org.geysermc.connector.network.session.GeyserSession; 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 org.reflections.Reflections;
import java.util.HashMap;
import java.util.Map;
public class PacketTranslatorRegistry<T> { public class PacketTranslatorRegistry<T> {
private final Map<Class<? extends T>, PacketTranslator<? extends T>> translators = new HashMap<>(); private final Map<Class<? extends T>, PacketTranslator<? extends T>> translators = new HashMap<>();
@ -72,7 +73,9 @@ public class PacketTranslatorRegistry<T> {
} }
} }
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() { private PacketTranslatorRegistry() {

View file

@ -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<AdventureSettingsPacket> {
@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);
}
}

View file

@ -38,17 +38,21 @@ public class BedrockContainerCloseTranslator extends PacketTranslator<ContainerC
@Override @Override
public void translate(ContainerClosePacket packet, GeyserSession session) { public void translate(ContainerClosePacket packet, GeyserSession session) {
session.setLastWindowCloseTime(0);
byte windowId = packet.getWindowId(); byte windowId = packet.getWindowId();
if (windowId == -1) { //player inventory or crafting table
Inventory openInventory = session.getInventoryCache().getOpenInventory(); Inventory openInventory = session.getInventoryCache().getOpenInventory();
if (windowId == -1) { //player inventory or crafting table
if (openInventory != null) { if (openInventory != null) {
windowId = (byte) openInventory.getId(); windowId = (byte) openInventory.getId();
} else { } else {
windowId = 0; windowId = 0;
} }
} }
if (windowId == 0 || (openInventory != null && openInventory.getId() == windowId)) {
ClientCloseWindowPacket closeWindowPacket = new ClientCloseWindowPacket(windowId); ClientCloseWindowPacket closeWindowPacket = new ClientCloseWindowPacket(windowId);
session.sendDownstreamPacket(closeWindowPacket); session.getDownstream().getSession().send(closeWindowPacket);
InventoryUtils.closeInventory(session, windowId); InventoryUtils.closeInventory(session, windowId);
} }
}
} }

View file

@ -26,7 +26,13 @@
package org.geysermc.connector.network.translators.bedrock; package org.geysermc.connector.network.translators.bedrock;
import com.github.steveice10.mc.protocol.data.game.window.VillagerTrade;
import com.github.steveice10.mc.protocol.data.game.window.WindowType;
import com.github.steveice10.mc.protocol.packet.ingame.client.window.ClientSelectTradePacket;
import com.nukkitx.protocol.bedrock.data.EntityData;
import com.nukkitx.protocol.bedrock.packet.EntityEventPacket; import com.nukkitx.protocol.bedrock.packet.EntityEventPacket;
import org.geysermc.connector.entity.Entity;
import org.geysermc.connector.inventory.Inventory;
import org.geysermc.connector.network.session.GeyserSession; import org.geysermc.connector.network.session.GeyserSession;
import org.geysermc.connector.network.translators.PacketTranslator; import org.geysermc.connector.network.translators.PacketTranslator;
import org.geysermc.connector.network.translators.Translator; import org.geysermc.connector.network.translators.Translator;
@ -41,6 +47,22 @@ public class BedrockEntityEventTranslator extends PacketTranslator<EntityEventPa
case EATING_ITEM: case EATING_ITEM:
session.sendUpstreamPacket(packet); session.sendUpstreamPacket(packet);
return; return;
case COMPLETE_TRADE:
ClientSelectTradePacket selectTradePacket = new ClientSelectTradePacket(packet.getData());
session.getDownstream().getSession().send(selectTradePacket);
Entity villager = session.getPlayerEntity();
Inventory openInventory = session.getInventoryCache().getOpenInventory();
if (openInventory != null && openInventory.getWindowType() == WindowType.MERCHANT) {
VillagerTrade[] trades = session.getVillagerTrades();
if (trades != null && packet.getData() >= 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()); session.getConnector().getLogger().debug("Did not translate incoming EntityEventPacket: " + packet.toString());
} }

View file

@ -45,6 +45,7 @@ import com.nukkitx.protocol.bedrock.packet.InventoryTransactionPacket;
import org.geysermc.connector.entity.Entity; import org.geysermc.connector.entity.Entity;
import org.geysermc.connector.entity.ItemFrameEntity; import org.geysermc.connector.entity.ItemFrameEntity;
import org.geysermc.connector.entity.living.merchant.AbstractMerchantEntity;
import org.geysermc.connector.inventory.Inventory; import org.geysermc.connector.inventory.Inventory;
import org.geysermc.connector.network.session.GeyserSession; import org.geysermc.connector.network.session.GeyserSession;
import org.geysermc.connector.network.translators.PacketTranslator; import org.geysermc.connector.network.translators.PacketTranslator;
@ -198,6 +199,10 @@ public class BedrockInventoryTransactionTranslator extends PacketTranslator<Inve
session.sendDownstreamPacket(interactAtPacket); session.sendDownstreamPacket(interactAtPacket);
EntitySoundInteractionHandler.handleEntityInteraction(session, vector, entity); EntitySoundInteractionHandler.handleEntityInteraction(session, vector, entity);
if (entity instanceof AbstractMerchantEntity) {
session.setLastInteractedVillagerEid(packet.getRuntimeEntityId());
}
break; break;
case 1: //Attack case 1: //Attack
ClientPlayerInteractEntityPacket attackPacket = new ClientPlayerInteractEntityPacket((int) entity.getEntityId(), ClientPlayerInteractEntityPacket attackPacket = new ClientPlayerInteractEntityPacket((int) entity.getEntityId(),

View file

@ -0,0 +1,69 @@
/*
* 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.InventoryActionData;
import org.geysermc.connector.inventory.Inventory;
import org.geysermc.connector.network.session.GeyserSession;
import org.geysermc.connector.network.translators.inventory.updater.ChestInventoryUpdater;
import org.geysermc.connector.network.translators.inventory.updater.InventoryUpdater;
import org.geysermc.connector.utils.InventoryUtils;
import java.util.List;
public abstract class ChestInventoryTranslator extends BaseInventoryTranslator {
private final InventoryUpdater updater;
public ChestInventoryTranslator(int size, int paddedSize) {
super(size);
this.updater = new ChestInventoryUpdater(paddedSize);
}
@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<InventoryActionData> 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);
}
}

View file

@ -92,8 +92,11 @@ public class CraftingInventoryTranslator extends BaseInventoryTranslator {
@Override @Override
public int javaSlotToBedrock(int slot) { public int javaSlotToBedrock(int slot) {
if (slot < size) {
return slot == 0 ? 50 : slot + 31; return slot == 0 ? 50 : slot + 31;
} }
return super.javaSlotToBedrock(slot);
}
@Override @Override
public SlotType getSlotType(int javaSlot) { public SlotType getSlotType(int javaSlot) {

View file

@ -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.ChestInventoryUpdater;
import org.geysermc.connector.network.translators.inventory.updater.InventoryUpdater; 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 int blockId;
private final InventoryUpdater updater;
public DoubleChestInventoryTranslator(int size) { public DoubleChestInventoryTranslator(int size) {
super(size); super(size, 54);
BlockState javaBlockState = BlockTranslator.getJavaBlockState("minecraft:chest[facing=north,type=single,waterlogged=false]"); BlockState javaBlockState = BlockTranslator.getJavaBlockState("minecraft:chest[facing=north,type=single,waterlogged=false]");
this.blockId = BlockTranslator.getBedrockBlockId(javaBlockState); this.blockId = BlockTranslator.getBedrockBlockId(javaBlockState);
this.updater = new ChestInventoryUpdater(54);
} }
@Override @Override
@ -128,14 +126,4 @@ public class DoubleChestInventoryTranslator extends BaseInventoryTranslator {
blockPacket.setRuntimeId(BlockTranslator.getBedrockBlockId(realBlock)); blockPacket.setRuntimeId(BlockTranslator.getBedrockBlockId(realBlock));
session.sendUpstreamPacket(blockPacket); 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);
}
} }

View file

@ -54,6 +54,7 @@ public abstract class InventoryTranslator {
put(WindowType.ANVIL, new AnvilInventoryTranslator()); put(WindowType.ANVIL, new AnvilInventoryTranslator());
put(WindowType.CRAFTING, new CraftingInventoryTranslator()); put(WindowType.CRAFTING, new CraftingInventoryTranslator());
put(WindowType.GRINDSTONE, new GrindstoneInventoryTranslator()); put(WindowType.GRINDSTONE, new GrindstoneInventoryTranslator());
put(WindowType.MERCHANT, new MerchantInventoryTranslator());
//put(WindowType.ENCHANTMENT, new EnchantmentInventoryTranslator()); //TODO //put(WindowType.ENCHANTMENT, new EnchantmentInventoryTranslator()); //TODO
InventoryTranslator furnace = new FurnaceInventoryTranslator(); InventoryTranslator furnace = new FurnaceInventoryTranslator();

View file

@ -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<InventoryActionData> actions) {
for (InventoryActionData action : actions) {
if (action.getSource().getType() == InventorySource.Type.NON_IMPLEMENTED_TODO) {
return;
}
}
super.translateActions(session, inventory, actions);
}
}

View file

@ -59,11 +59,11 @@ public class PlayerInventoryTranslator extends InventoryTranslator {
ItemData[] contents = new ItemData[36]; ItemData[] contents = new ItemData[36];
// Inventory // Inventory
for (int i = 9; i < 36; i++) { for (int i = 9; i < 36; i++) {
contents[i] = ItemTranslator.translateToBedrock(inventory.getItem(i)); contents[i] = ItemTranslator.translateToBedrock(session, inventory.getItem(i));
} }
// Hotbar // Hotbar
for (int i = 36; i < 45; i++) { 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); inventoryContentPacket.setContents(contents);
session.sendUpstreamPacket(inventoryContentPacket); session.sendUpstreamPacket(inventoryContentPacket);
@ -73,7 +73,7 @@ public class PlayerInventoryTranslator extends InventoryTranslator {
armorContentPacket.setContainerId(ContainerId.ARMOR); armorContentPacket.setContainerId(ContainerId.ARMOR);
contents = new ItemData[4]; contents = new ItemData[4];
for (int i = 5; i < 9; i++) { 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); armorContentPacket.setContents(contents);
session.sendUpstreamPacket(armorContentPacket); session.sendUpstreamPacket(armorContentPacket);
@ -81,7 +81,7 @@ public class PlayerInventoryTranslator extends InventoryTranslator {
// Offhand // Offhand
InventoryContentPacket offhandPacket = new InventoryContentPacket(); InventoryContentPacket offhandPacket = new InventoryContentPacket();
offhandPacket.setContainerId(ContainerId.OFFHAND); 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); session.sendUpstreamPacket(offhandPacket);
} }
@ -100,7 +100,7 @@ public class PlayerInventoryTranslator extends InventoryTranslator {
if (session.getGameMode() == GameMode.CREATIVE) { if (session.getGameMode() == GameMode.CREATIVE) {
slotPacket.setItem(UNUSUABLE_CRAFTING_SPACE_BLOCK); slotPacket.setItem(UNUSUABLE_CRAFTING_SPACE_BLOCK);
}else{ }else{
slotPacket.setItem(ItemTranslator.translateToBedrock(inventory.getItem(i))); slotPacket.setItem(ItemTranslator.translateToBedrock(session, inventory.getItem(i)));
} }
session.sendUpstreamPacket(slotPacket); session.sendUpstreamPacket(slotPacket);
@ -125,12 +125,12 @@ public class PlayerInventoryTranslator extends InventoryTranslator {
slotPacket.setContainerId(ContainerId.CURSOR); slotPacket.setContainerId(ContainerId.CURSOR);
slotPacket.setSlot(slot + 27); slotPacket.setSlot(slot + 27);
} }
slotPacket.setItem(ItemTranslator.translateToBedrock(inventory.getItem(slot))); slotPacket.setItem(ItemTranslator.translateToBedrock(session, inventory.getItem(slot)));
session.sendUpstreamPacket(slotPacket); session.sendUpstreamPacket(slotPacket);
} else if (slot == 45) { } else if (slot == 45) {
InventoryContentPacket offhandPacket = new InventoryContentPacket(); InventoryContentPacket offhandPacket = new InventoryContentPacket();
offhandPacket.setContainerId(ContainerId.OFFHAND); 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); session.sendUpstreamPacket(offhandPacket);
} }
} }

View file

@ -25,11 +25,35 @@
package org.geysermc.connector.network.translators.inventory; 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 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) { 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);
} }
} }

View file

@ -61,13 +61,13 @@ public class InventoryActionDataTranslator {
worldAction = action; worldAction = action;
} else if (action.getSource().getContainerId() == ContainerId.CURSOR && action.getSlot() == 0) { } else if (action.getSource().getContainerId() == ContainerId.CURSOR && action.getSlot() == 0) {
cursorAction = action; cursorAction = action;
ItemData translatedCursor = ItemTranslator.translateToBedrock(session.getInventory().getCursor()); ItemData translatedCursor = ItemTranslator.translateToBedrock(session, session.getInventory().getCursor());
if (!translatedCursor.equals(action.getFromItem())) { if (!translatedCursor.equals(action.getFromItem())) {
refresh = true; refresh = true;
} }
} else { } else {
containerAction = action; 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())) { if (!translatedItem.equals(action.getFromItem())) {
refresh = true; refresh = true;
} }
@ -187,11 +187,12 @@ public class InventoryActionDataTranslator {
} else if (translator.getSlotType(javaSlot) == SlotType.OUTPUT) { } else if (translator.getSlotType(javaSlot) == SlotType.OUTPUT) {
plan.add(Click.LEFT, javaSlot); plan.add(Click.LEFT, javaSlot);
} else { } 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) { if (cursorSlot != -1) {
plan.add(Click.LEFT, cursorSlot); plan.add(Click.LEFT, cursorSlot);
} else { } else {
translator.updateInventory(session, inventory); translator.updateInventory(session, inventory);
InventoryUtils.updateCursor(session);
return; return;
} }
plan.add(Click.LEFT, javaSlot); plan.add(Click.LEFT, javaSlot);
@ -245,11 +246,15 @@ public class InventoryActionDataTranslator {
int cursorSlot = -1; int cursorSlot = -1;
if (session.getInventory().getCursor() != null) { //move cursor contents to a temporary slot 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) { if (cursorSlot != -1) {
plan.add(Click.LEFT, cursorSlot); plan.add(Click.LEFT, cursorSlot);
} else { } else {
translator.updateInventory(session, inventory); translator.updateInventory(session, inventory);
InventoryUtils.updateCursor(session);
return; return;
} }
} }
@ -298,7 +303,7 @@ public class InventoryActionDataTranslator {
InventoryUtils.updateCursor(session); InventoryUtils.updateCursor(session);
} }
private static int findTempSlot(Inventory inventory, ItemStack item, List<Integer> slotBlacklist) { private static int findTempSlot(Inventory inventory, ItemStack item, List<Integer> slotBlacklist, boolean emptyOnly) {
/*try and find a slot that can temporarily store the given item /*try and find a slot that can temporarily store the given item
only look in the main inventory and hotbar only look in the main inventory and hotbar
only slots that are empty or contain a different type of item are valid*/ 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); ItemStack testItem = inventory.getItem(i);
boolean acceptable = true; boolean acceptable = true;
if (testItem != null) { if (testItem != null) {
if (emptyOnly) {
continue;
}
for (ItemStack blacklistItem : itemBlacklist) { for (ItemStack blacklistItem : itemBlacklist) {
if (InventoryUtils.canStack(testItem, blacklistItem)) { if (InventoryUtils.canStack(testItem, blacklistItem)) {
acceptable = false; acceptable = false;

View file

@ -49,7 +49,7 @@ public class ChestInventoryUpdater extends InventoryUpdater {
ItemData[] bedrockItems = new ItemData[paddedSize]; ItemData[] bedrockItems = new ItemData[paddedSize];
for (int i = 0; i < bedrockItems.length; i++) { for (int i = 0; i < bedrockItems.length; i++) {
if (i < translator.size) { if (i < translator.size) {
bedrockItems[i] = ItemTranslator.translateToBedrock(inventory.getItem(i)); bedrockItems[i] = ItemTranslator.translateToBedrock(session, inventory.getItem(i));
} else { } else {
bedrockItems[i] = UNUSUABLE_SPACE_BLOCK; bedrockItems[i] = UNUSUABLE_SPACE_BLOCK;
} }
@ -69,7 +69,7 @@ public class ChestInventoryUpdater extends InventoryUpdater {
InventorySlotPacket slotPacket = new InventorySlotPacket(); InventorySlotPacket slotPacket = new InventorySlotPacket();
slotPacket.setContainerId(inventory.getId()); slotPacket.setContainerId(inventory.getId());
slotPacket.setSlot(translator.javaSlotToBedrock(javaSlot)); slotPacket.setSlot(translator.javaSlotToBedrock(javaSlot));
slotPacket.setItem(ItemTranslator.translateToBedrock(inventory.getItem(javaSlot))); slotPacket.setItem(ItemTranslator.translateToBedrock(session, inventory.getItem(javaSlot)));
session.sendUpstreamPacket(slotPacket); session.sendUpstreamPacket(slotPacket);
return true; return true;
} }

View file

@ -40,7 +40,7 @@ public class ContainerInventoryUpdater extends InventoryUpdater {
ItemData[] bedrockItems = new ItemData[translator.size]; ItemData[] bedrockItems = new ItemData[translator.size];
for (int i = 0; i < bedrockItems.length; i++) { 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(); InventoryContentPacket contentPacket = new InventoryContentPacket();
@ -57,7 +57,7 @@ public class ContainerInventoryUpdater extends InventoryUpdater {
InventorySlotPacket slotPacket = new InventorySlotPacket(); InventorySlotPacket slotPacket = new InventorySlotPacket();
slotPacket.setContainerId(inventory.getId()); slotPacket.setContainerId(inventory.getId());
slotPacket.setSlot(translator.javaSlotToBedrock(javaSlot)); slotPacket.setSlot(translator.javaSlotToBedrock(javaSlot));
slotPacket.setItem(ItemTranslator.translateToBedrock(inventory.getItem(javaSlot))); slotPacket.setItem(ItemTranslator.translateToBedrock(session, inventory.getItem(javaSlot)));
session.sendUpstreamPacket(slotPacket); session.sendUpstreamPacket(slotPacket);
return true; return true;
} }

View file

@ -44,7 +44,7 @@ public class CursorInventoryUpdater extends InventoryUpdater {
InventorySlotPacket slotPacket = new InventorySlotPacket(); InventorySlotPacket slotPacket = new InventorySlotPacket();
slotPacket.setContainerId(ContainerId.CURSOR); slotPacket.setContainerId(ContainerId.CURSOR);
slotPacket.setSlot(bedrockSlot); slotPacket.setSlot(bedrockSlot);
slotPacket.setItem(ItemTranslator.translateToBedrock(inventory.getItem(i))); slotPacket.setItem(ItemTranslator.translateToBedrock(session, inventory.getItem(i)));
session.sendUpstreamPacket(slotPacket); session.sendUpstreamPacket(slotPacket);
} }
} }
@ -57,7 +57,7 @@ public class CursorInventoryUpdater extends InventoryUpdater {
InventorySlotPacket slotPacket = new InventorySlotPacket(); InventorySlotPacket slotPacket = new InventorySlotPacket();
slotPacket.setContainerId(ContainerId.CURSOR); slotPacket.setContainerId(ContainerId.CURSOR);
slotPacket.setSlot(translator.javaSlotToBedrock(javaSlot)); slotPacket.setSlot(translator.javaSlotToBedrock(javaSlot));
slotPacket.setItem(ItemTranslator.translateToBedrock(inventory.getItem(javaSlot))); slotPacket.setItem(ItemTranslator.translateToBedrock(session, inventory.getItem(javaSlot)));
session.sendUpstreamPacket(slotPacket); session.sendUpstreamPacket(slotPacket);
return true; return true;
} }

View file

@ -39,7 +39,7 @@ public abstract class InventoryUpdater {
ItemData[] bedrockItems = new ItemData[36]; ItemData[] bedrockItems = new ItemData[36];
for (int i = 0; i < 36; i++) { for (int i = 0; i < 36; i++) {
final int offset = i < 9 ? 27 : -9; 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(); InventoryContentPacket contentPacket = new InventoryContentPacket();
contentPacket.setContainerId(ContainerId.INVENTORY); contentPacket.setContainerId(ContainerId.INVENTORY);
@ -52,7 +52,7 @@ public abstract class InventoryUpdater {
InventorySlotPacket slotPacket = new InventorySlotPacket(); InventorySlotPacket slotPacket = new InventorySlotPacket();
slotPacket.setContainerId(ContainerId.INVENTORY); slotPacket.setContainerId(ContainerId.INVENTORY);
slotPacket.setSlot(translator.javaSlotToBedrock(javaSlot)); slotPacket.setSlot(translator.javaSlotToBedrock(javaSlot));
slotPacket.setItem(ItemTranslator.translateToBedrock(inventory.getItem(javaSlot))); slotPacket.setItem(ItemTranslator.translateToBedrock(session, inventory.getItem(javaSlot)));
session.sendUpstreamPacket(slotPacket); session.sendUpstreamPacket(slotPacket);
return true; return true;
} }

View file

@ -27,6 +27,8 @@
package org.geysermc.connector.network.translators.item; 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.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.github.steveice10.opennbt.tag.builtin.*;
import com.nukkitx.nbt.tag.CompoundTag; import com.nukkitx.nbt.tag.CompoundTag;
import com.nukkitx.nbt.tag.Tag; 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.Int2ObjectMap;
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
import org.geysermc.connector.GeyserConnector; import org.geysermc.connector.GeyserConnector;
import org.geysermc.connector.network.session.GeyserSession;
import org.geysermc.connector.network.translators.ItemRemapper; import org.geysermc.connector.network.translators.ItemRemapper;
import org.geysermc.connector.utils.MessageUtils;
import org.reflections.Reflections; import org.reflections.Reflections;
import java.util.ArrayList; import java.util.ArrayList;
@ -115,7 +119,7 @@ public abstract class ItemTranslator {
return itemStack; return itemStack;
} }
public static ItemData translateToBedrock(ItemStack stack) { public static ItemData translateToBedrock(GeyserSession session, ItemStack stack) {
if (stack == null) { if (stack == null) {
return ItemData.AIR; return ItemData.AIR;
} }
@ -132,12 +136,42 @@ public abstract class ItemTranslator {
} }
} }
ItemData itemData;
ItemTranslator itemStackTranslator = ITEM_STACK_TRANSLATORS.get(bedrockItem.getJavaId()); ItemTranslator itemStackTranslator = ITEM_STACK_TRANSLATORS.get(bedrockItem.getJavaId());
if (itemStackTranslator != null) { if (itemStackTranslator != null) {
return itemStackTranslator.translateToBedrock(itemStack, bedrockItem); itemData = itemStackTranslator.translateToBedrock(itemStack, bedrockItem);
} else { } 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() { private static final ItemTranslator DEFAULT_TRANSLATOR = new ItemTranslator() {
@ -351,5 +385,49 @@ public abstract class ItemTranslator {
return null; 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;
}
} }

View file

@ -27,9 +27,10 @@ package org.geysermc.connector.network.translators.item.translators.nbt;
import com.github.steveice10.opennbt.tag.builtin.*; import com.github.steveice10.opennbt.tag.builtin.*;
import org.geysermc.connector.network.translators.ItemRemapper; 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.ItemEntry;
import org.geysermc.connector.network.translators.item.NbtItemStackTranslator;
import org.geysermc.connector.utils.FireworkColor; import org.geysermc.connector.utils.FireworkColor;
import org.geysermc.connector.utils.MathUtils;
@ItemRemapper @ItemRemapper
public class FireworkTranslator extends NbtItemStackTranslator { public class FireworkTranslator extends NbtItemStackTranslator {
@ -41,6 +42,10 @@ public class FireworkTranslator extends NbtItemStackTranslator {
} }
CompoundTag fireworks = itemTag.get("Fireworks"); 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"); ListTag explosions = fireworks.get("Explosions");
if (explosions == null) { if (explosions == null) {
return; return;
@ -51,7 +56,7 @@ public class FireworkTranslator extends NbtItemStackTranslator {
CompoundTag newEffectData = new CompoundTag(""); CompoundTag newEffectData = new CompoundTag("");
if (effectData.get("Type") != null) { 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) { if (effectData.get("Colors") != null) {
@ -79,11 +84,11 @@ public class FireworkTranslator extends NbtItemStackTranslator {
} }
if (effectData.get("Trail") != null) { 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) { 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); explosions.remove(effect);
@ -94,6 +99,9 @@ public class FireworkTranslator extends NbtItemStackTranslator {
@Override @Override
public void translateToJava(CompoundTag itemTag, ItemEntry itemEntry) { public void translateToJava(CompoundTag itemTag, ItemEntry itemEntry) {
CompoundTag fireworks = itemTag.get("Fireworks"); 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"); ListTag explosions = fireworks.get("Explosions");
for (Tag effect : explosions.getValue()) { for (Tag effect : explosions.getValue()) {
@ -102,7 +110,7 @@ public class FireworkTranslator extends NbtItemStackTranslator {
CompoundTag newEffectData = new CompoundTag(""); CompoundTag newEffectData = new CompoundTag("");
if (effectData.get("FireworkType") != null) { 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) { if (effectData.get("FireworkColor") != null) {
@ -130,11 +138,11 @@ public class FireworkTranslator extends NbtItemStackTranslator {
} }
if (effectData.get("FireworkTrail") != null) { 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) { 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); explosions.remove(effect);

View file

@ -64,7 +64,7 @@ public class JavaDeclareRecipesTranslator extends PacketTranslator<ServerDeclare
switch (recipe.getType()) { switch (recipe.getType()) {
case CRAFTING_SHAPELESS: { case CRAFTING_SHAPELESS: {
ShapelessRecipeData shapelessRecipeData = (ShapelessRecipeData) recipe.getData(); ShapelessRecipeData shapelessRecipeData = (ShapelessRecipeData) recipe.getData();
ItemData output = ItemTranslator.translateToBedrock(shapelessRecipeData.getResult()); ItemData output = ItemTranslator.translateToBedrock(session, shapelessRecipeData.getResult());
output = ItemData.of(output.getId(), output.getDamage(), output.getCount()); //strip NBT output = ItemData.of(output.getId(), output.getDamage(), output.getCount()); //strip NBT
ItemData[][] inputCombinations = combinations(session, shapelessRecipeData.getIngredients()); ItemData[][] inputCombinations = combinations(session, shapelessRecipeData.getIngredients());
for (ItemData[] inputs : inputCombinations) { for (ItemData[] inputs : inputCombinations) {
@ -76,7 +76,7 @@ public class JavaDeclareRecipesTranslator extends PacketTranslator<ServerDeclare
} }
case CRAFTING_SHAPED: { case CRAFTING_SHAPED: {
ShapedRecipeData shapedRecipeData = (ShapedRecipeData) recipe.getData(); ShapedRecipeData shapedRecipeData = (ShapedRecipeData) recipe.getData();
ItemData output = ItemTranslator.translateToBedrock(shapedRecipeData.getResult()); ItemData output = ItemTranslator.translateToBedrock(session, shapedRecipeData.getResult());
output = ItemData.of(output.getId(), output.getDamage(), output.getCount()); //strip NBT output = ItemData.of(output.getId(), output.getDamage(), output.getCount()); //strip NBT
ItemData[][] inputCombinations = combinations(session, shapedRecipeData.getIngredients()); ItemData[][] inputCombinations = combinations(session, shapedRecipeData.getIngredients());
for (ItemData[] inputs : inputCombinations) { for (ItemData[] inputs : inputCombinations) {
@ -103,7 +103,7 @@ public class JavaDeclareRecipesTranslator extends PacketTranslator<ServerDeclare
} }
Ingredient ingredient = ingredients[i]; Ingredient ingredient = ingredients[i];
Map<GroupedItem, List<ItemData>> groupedByIds = Arrays.stream(ingredient.getOptions()) Map<GroupedItem, List<ItemData>> 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()))); .collect(Collectors.groupingBy(item -> new GroupedItem(item.getId(), item.getCount(), item.getTag())));
Set<ItemData> optionSet = new HashSet<>(groupedByIds.size()); Set<ItemData> optionSet = new HashSet<>(groupedByIds.size());
for (Map.Entry<GroupedItem, List<ItemData>> entry : groupedByIds.entrySet()) { for (Map.Entry<GroupedItem, List<ItemData>> entry : groupedByIds.entrySet()) {
@ -136,7 +136,7 @@ public class JavaDeclareRecipesTranslator extends PacketTranslator<ServerDeclare
ItemData[] translatedItems = new ItemData[ingredients.length]; ItemData[] translatedItems = new ItemData[ingredients.length];
for (int i = 0; i < ingredients.length; i++) { for (int i = 0; i < ingredients.length; i++) {
if (ingredients[i].getOptions().length > 0) { if (ingredients[i].getOptions().length > 0) {
translatedItems[i] = ItemTranslator.translateToBedrock(ingredients[i].getOptions()[0]); translatedItems[i] = ItemTranslator.translateToBedrock(session, ingredients[i].getOptions()[0]);
} else { } else {
translatedItems[i] = ItemData.AIR; translatedItems[i] = ItemData.AIR;
} }

View file

@ -55,7 +55,7 @@ public class JavaEntityEquipmentTranslator extends PacketTranslator<ServerEntity
} }
LivingEntity livingEntity = (LivingEntity) entity; LivingEntity livingEntity = (LivingEntity) entity;
ItemData item = ItemTranslator.translateToBedrock(packet.getItem()); ItemData item = ItemTranslator.translateToBedrock(session, packet.getItem());
switch (packet.getSlot()) { switch (packet.getSlot()) {
case HELMET: case HELMET:
livingEntity.setHelmet(item); livingEntity.setHelmet(item);

View file

@ -25,6 +25,7 @@
package org.geysermc.connector.network.translators.java.entity.player; package org.geysermc.connector.network.translators.java.entity.player;
import com.github.steveice10.mc.protocol.data.game.entity.player.PositionElement;
import com.github.steveice10.mc.protocol.packet.ingame.client.world.ClientTeleportConfirmPacket; import com.github.steveice10.mc.protocol.packet.ingame.client.world.ClientTeleportConfirmPacket;
import com.github.steveice10.mc.protocol.packet.ingame.server.entity.player.ServerPlayerPositionRotationPacket; import com.github.steveice10.mc.protocol.packet.ingame.server.entity.player.ServerPlayerPositionRotationPacket;
import com.nukkitx.math.vector.Vector3f; import com.nukkitx.math.vector.Vector3f;
@ -95,20 +96,35 @@ public class JavaPlayerPositionRotationTranslator extends PacketTranslator<Serve
session.setSpawned(true); session.setSpawned(true);
if (!packet.getRelative().isEmpty()) { // Ignore certain move correction packets for smoother movement
session.setTeleportCache(new TeleportCache(packet.getX(), packet.getY(), packet.getZ(), packet.getTeleportId())); // These are never relative
entity.moveRelative(session, packet.getX(), packet.getY() + EntityType.PLAYER.getOffset() + 0.1f, packet.getZ(), packet.getYaw(), packet.getPitch(), true); if (packet.getRelative().isEmpty()) {
} else {
double xDis = Math.abs(entity.getPosition().getX() - packet.getX()); double xDis = Math.abs(entity.getPosition().getX() - packet.getX());
double yDis = entity.getPosition().getY() - packet.getY(); double yDis = entity.getPosition().getY() - packet.getY();
double zDis = Math.abs(entity.getPosition().getZ() - packet.getZ()); double zDis = Math.abs(entity.getPosition().getZ() - packet.getZ());
if (xDis > 1.5 || (yDis < 1.45 || yDis > (session.isJumping() ? 4.3 : (session.isSprinting() ? 2.5 : 1.9))) || zDis > 1.5) { if (!(xDis > 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())); // Fake confirm the teleport but don't send it to the client
entity.moveAbsolute(session, Vector3f.from(packet.getX(), packet.getY(), packet.getZ()), packet.getYaw(), packet.getPitch(), true, true);
} else {
ClientTeleportConfirmPacket teleportConfirmPacket = new ClientTeleportConfirmPacket(packet.getTeleportId()); ClientTeleportConfirmPacket teleportConfirmPacket = new ClientTeleportConfirmPacket(packet.getTeleportId());
session.sendDownstreamPacket(teleportConfirmPacket); 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);
} }
} }

View file

@ -26,7 +26,6 @@
package org.geysermc.connector.network.translators.java.window; package org.geysermc.connector.network.translators.java.window;
import com.github.steveice10.mc.protocol.packet.ingame.server.window.ServerCloseWindowPacket; 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.session.GeyserSession;
import org.geysermc.connector.network.translators.PacketTranslator; import org.geysermc.connector.network.translators.PacketTranslator;
import org.geysermc.connector.network.translators.Translator; import org.geysermc.connector.network.translators.Translator;
@ -37,9 +36,7 @@ public class JavaCloseWindowTranslator extends PacketTranslator<ServerCloseWindo
@Override @Override
public void translate(ServerCloseWindowPacket packet, GeyserSession session) { public void translate(ServerCloseWindowPacket packet, GeyserSession session) {
ContainerClosePacket closePacket = new ContainerClosePacket(); InventoryUtils.closeWindow(session, packet.getWindowId());
closePacket.setWindowId((byte)packet.getWindowId());
session.sendUpstreamPacket(closePacket);
InventoryUtils.closeInventory(session, packet.getWindowId()); InventoryUtils.closeInventory(session, packet.getWindowId());
} }
} }

View file

@ -38,8 +38,6 @@ import org.geysermc.connector.network.translators.Translator;
import org.geysermc.connector.network.translators.inventory.InventoryTranslator; import org.geysermc.connector.network.translators.inventory.InventoryTranslator;
import org.geysermc.connector.utils.InventoryUtils; import org.geysermc.connector.utils.InventoryUtils;
import java.util.concurrent.TimeUnit;
@Translator(packet = ServerOpenWindowPacket.class) @Translator(packet = ServerOpenWindowPacket.class)
public class JavaOpenWindowTranslator extends PacketTranslator<ServerOpenWindowPacket> { public class JavaOpenWindowTranslator extends PacketTranslator<ServerOpenWindowPacket> {
@ -52,10 +50,8 @@ public class JavaOpenWindowTranslator extends PacketTranslator<ServerOpenWindowP
Inventory openInventory = session.getInventoryCache().getOpenInventory(); Inventory openInventory = session.getInventoryCache().getOpenInventory();
if (newTranslator == null) { if (newTranslator == null) {
if (openInventory != null) { if (openInventory != null) {
ContainerClosePacket closePacket = new ContainerClosePacket(); InventoryUtils.closeWindow(session, openInventory.getId());
closePacket.setWindowId((byte)openInventory.getId()); InventoryUtils.closeInventory(session, openInventory.getId());
session.sendUpstreamPacket(closePacket);
InventoryTranslator.INVENTORY_TRANSLATORS.get(openInventory.getWindowType()).closeInventory(session, openInventory);
} }
ClientCloseWindowPacket closeWindowPacket = new ClientCloseWindowPacket(packet.getWindowId()); ClientCloseWindowPacket closeWindowPacket = new ClientCloseWindowPacket(packet.getWindowId());
session.sendDownstreamPacket(closeWindowPacket); session.sendDownstreamPacket(closeWindowPacket);
@ -80,9 +76,8 @@ public class JavaOpenWindowTranslator extends PacketTranslator<ServerOpenWindowP
if (openInventory != null) { if (openInventory != null) {
InventoryTranslator openTranslator = InventoryTranslator.INVENTORY_TRANSLATORS.get(openInventory.getWindowType()); InventoryTranslator openTranslator = InventoryTranslator.INVENTORY_TRANSLATORS.get(openInventory.getWindowType());
if (!openTranslator.getClass().equals(newTranslator.getClass())) { if (!openTranslator.getClass().equals(newTranslator.getClass())) {
InventoryUtils.closeWindow(session, openInventory.getId());
InventoryUtils.closeInventory(session, openInventory.getId()); InventoryUtils.closeInventory(session, openInventory.getId());
GeyserConnector.getInstance().getGeneralThreadPool().schedule(() -> InventoryUtils.openInventory(session, newInventory), 500, TimeUnit.MILLISECONDS);
return;
} }
} }

View file

@ -41,8 +41,6 @@ public class JavaSetSlotTranslator extends PacketTranslator<ServerSetSlotPacket>
@Override @Override
public void translate(ServerSetSlotPacket packet, GeyserSession session) { public void translate(ServerSetSlotPacket packet, GeyserSession session) {
if (packet.getWindowId() == 255 && packet.getSlot() == -1) { //cursor if (packet.getWindowId() == 255 && packet.getSlot() == -1) { //cursor
if (Objects.equals(session.getInventory().getCursor(), packet.getItem()))
return;
if (session.getCraftSlot() != 0) if (session.getCraftSlot() != 0)
return; return;

View file

@ -46,14 +46,17 @@ public class JavaExplosionTranslator extends PacketTranslator<ServerExplosionPac
public void translate(ServerExplosionPacket packet, GeyserSession session) { public void translate(ServerExplosionPacket packet, GeyserSession session) {
for (ExplodedBlockRecord record : packet.getExploded()) { for (ExplodedBlockRecord record : packet.getExploded()) {
Vector3f pos = Vector3f.from(packet.getX() + record.getX(), packet.getY() + record.getY(), packet.getZ() + record.getZ()); Vector3f pos = Vector3f.from(packet.getX() + record.getX(), packet.getY() + record.getY(), packet.getZ() + record.getZ());
ChunkUtils.updateBlock(session, BlockTranslator.AIR, pos.toInt());
}
Vector3f pos = Vector3f.from(packet.getX(), packet.getY(), packet.getZ());
// Since bedrock does not play an explosion sound and particles sound, we have to manually do so // Since bedrock does not play an explosion sound and particles sound, we have to manually do so
LevelEventPacket levelEventPacket = new LevelEventPacket(); LevelEventPacket levelEventPacket = new LevelEventPacket();
levelEventPacket.setType(LevelEventType.PARTICLE_LARGE_EXPLOSION); levelEventPacket.setType(packet.getRadius() >= 2.0f ? LevelEventType.PARTICLE_HUGE_EXPLODE : LevelEventType.PARTICLE_LARGE_EXPLOSION);
levelEventPacket.setData(0); levelEventPacket.setData(0);
levelEventPacket.setPosition(pos.toFloat()); levelEventPacket.setPosition(pos.toFloat());
session.sendUpstreamPacket(levelEventPacket); session.sendUpstreamPacket(levelEventPacket);
ChunkUtils.updateBlock(session, BlockTranslator.AIR, pos.toInt());
}
LevelSoundEventPacket levelSoundEventPacket = new LevelSoundEventPacket(); LevelSoundEventPacket levelSoundEventPacket = new LevelSoundEventPacket();
levelSoundEventPacket.setRelativeVolumeDisabled(false); levelSoundEventPacket.setRelativeVolumeDisabled(false);
levelSoundEventPacket.setBabySound(false); levelSoundEventPacket.setBabySound(false);

View file

@ -64,7 +64,7 @@ public class JavaSpawnParticleTranslator extends PacketTranslator<ServerSpawnPar
break; break;
case ITEM: case ITEM:
ItemStack javaItem = ((ItemParticleData)packet.getParticle().getData()).getItemStack(); ItemStack javaItem = ((ItemParticleData)packet.getParticle().getData()).getItemStack();
ItemData bedrockItem = ItemTranslator.translateToBedrock(javaItem); ItemData bedrockItem = ItemTranslator.translateToBedrock(session, javaItem);
int id = bedrockItem.getId(); int id = bedrockItem.getId();
short damage = bedrockItem.getDamage(); short damage = bedrockItem.getDamage();
particle.setType(LevelEventType.PARTICLE_ITEM_BREAK); particle.setType(LevelEventType.PARTICLE_ITEM_BREAK);

View file

@ -0,0 +1,141 @@
/*
* Copyright (c) 2019-2020 GeyserMC. http://geysermc.org
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* @author GeyserMC
* @link https://github.com/GeyserMC/Geyser
*
*/
package org.geysermc.connector.network.translators.java.world;
import com.github.steveice10.mc.protocol.data.game.entity.metadata.ItemStack;
import com.github.steveice10.mc.protocol.data.game.window.VillagerTrade;
import com.github.steveice10.mc.protocol.packet.ingame.server.window.ServerTradeListPacket;
import com.nukkitx.nbt.CompoundTagBuilder;
import com.nukkitx.nbt.tag.CompoundTag;
import com.nukkitx.protocol.bedrock.data.ContainerType;
import com.nukkitx.protocol.bedrock.data.EntityData;
import com.nukkitx.protocol.bedrock.data.ItemData;
import com.nukkitx.protocol.bedrock.packet.UpdateTradePacket;
import org.geysermc.connector.entity.Entity;
import org.geysermc.connector.network.session.GeyserSession;
import org.geysermc.connector.network.translators.PacketTranslator;
import org.geysermc.connector.network.translators.Translator;
import org.geysermc.connector.network.translators.item.ItemEntry;
import org.geysermc.connector.network.translators.item.ItemRegistry;
import org.geysermc.connector.network.translators.item.ItemTranslator;
import java.util.ArrayList;
import java.util.List;
@Translator(packet = ServerTradeListPacket.class)
public class JavaTradeListTranslator extends PacketTranslator<ServerTradeListPacket> {
@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<CompoundTag> 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<CompoundTag> 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);
}
}

View file

@ -69,6 +69,11 @@ public class BlockTranslator {
public static final IntSet JAVA_RUNTIME_WOOL_IDS = new IntOpenHashSet(); public static final IntSet JAVA_RUNTIME_WOOL_IDS = new IntOpenHashSet();
public static final int JAVA_RUNTIME_COBWEB_ID; 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; private static final int BLOCK_STATE_VERSION = 17760256;
static { static {
@ -108,6 +113,9 @@ public class BlockTranslator {
int javaRuntimeId = -1; int javaRuntimeId = -1;
int bedrockRuntimeId = 0; int bedrockRuntimeId = 0;
int cobwebRuntimeId = -1; int cobwebRuntimeId = -1;
int furnaceRuntimeId = -1;
int furnaceLitRuntimeId = -1;
int spawnerRuntimeId = -1;
Iterator<Map.Entry<String, JsonNode>> blocksIterator = blocks.fields(); Iterator<Map.Entry<String, JsonNode>> blocksIterator = blocks.fields();
while (blocksIterator.hasNext()) { while (blocksIterator.hasNext()) {
javaRuntimeId++; javaRuntimeId++;
@ -186,6 +194,18 @@ public class BlockTranslator {
} }
JAVA_TO_BEDROCK_BLOCK_MAP.put(javaRuntimeId, bedrockRuntimeId); 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++; bedrockRuntimeId++;
} }
@ -194,6 +214,21 @@ public class BlockTranslator {
} }
JAVA_RUNTIME_COBWEB_ID = cobwebRuntimeId; 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) { if (waterRuntimeId == -1) {
throw new AssertionError("Unable to find water in palette"); throw new AssertionError("Unable to find water in palette");
} }

View file

@ -46,6 +46,19 @@ public abstract class BlockEntityTranslator {
public static final Map<String, BlockEntityTranslator> BLOCK_ENTITY_TRANSLATORS = new HashMap<>(); public static final Map<String, BlockEntityTranslator> BLOCK_ENTITY_TRANSLATORS = new HashMap<>();
public static ObjectArrayList<RequiresBlockState> REQUIRES_BLOCK_STATE_LIST = new ObjectArrayList<>(); public static ObjectArrayList<RequiresBlockState> 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<String, String> BLOCK_ENTITY_TRANSLATIONS = new HashMap<String, String>() {
{
// 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() { protected BlockEntityTranslator() {
} }

View file

@ -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.Client;
import com.github.steveice10.packetlib.tcp.TcpSessionFactory; import com.github.steveice10.packetlib.tcp.TcpSessionFactory;
import org.geysermc.common.ping.GeyserPingInfo; import org.geysermc.common.ping.GeyserPingInfo;
import org.geysermc.connector.GeyserConfiguration;
import org.geysermc.connector.GeyserConnector; import org.geysermc.connector.GeyserConnector;
import org.geysermc.connector.GeyserLogger;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;

View file

@ -12,29 +12,24 @@ public class BlockEntityUtils {
public static String getBedrockBlockEntityId(String id) { public static String getBedrockBlockEntityId(String id) {
// These are the only exceptions when it comes to block entity ids // These are the only exceptions when it comes to block entity ids
if (id.contains("piston_head")) if (BlockEntityTranslator.BLOCK_ENTITY_TRANSLATIONS.containsKey(id)) {
return "PistonArm"; return BlockEntityTranslator.BLOCK_ENTITY_TRANSLATIONS.get(id);
if (id.contains("trapped_chest"))
return "Chest";
if (id.contains("EnderChest"))
return "EnderChest";
if (id.contains("enchanting_table")) {
return "EnchantTable";
} }
id = id.toLowerCase() id = id.replace("minecraft:", "")
.replace("minecraft:", "")
.replace("_", " "); .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++) { for (int i = 0; i < words.length; i++) {
words[i] = words[i].substring(0, 1).toUpperCase() + words[i].substring(1).toLowerCase(); words[i] = words[i].substring(0, 1).toUpperCase() + words[i].substring(1).toLowerCase();
} }
id = String.join(" ", words); return String.join("", words);
return id.replace(" ", "");
} }
public static BlockEntityTranslator getBlockEntityTranslator(String name) { public static BlockEntityTranslator getBlockEntityTranslator(String name) {

View file

@ -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.entity.metadata.Position;
import com.github.steveice10.mc.protocol.data.game.world.block.BlockState; 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.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.Vector2i;
import com.nukkitx.math.vector.Vector3i; import com.nukkitx.math.vector.Vector3i;
import com.nukkitx.nbt.CompoundTagBuilder; import com.nukkitx.nbt.CompoundTagBuilder;
@ -137,11 +139,23 @@ public class ChunkUtils {
while (i < blockEntities.length) { while (i < blockEntities.length) {
CompoundTag tag = blockEntities[i]; CompoundTag tag = blockEntities[i];
String tagName; String tagName;
if (!tag.contains("id")) { if (tag.contains("id")) {
GeyserConnector.getInstance().getLogger().debug("Got tag with no id: " + tag.getValue());
tagName = "Empty";
} else {
tagName = (String) tag.get("id").getValue(); 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); String id = BlockEntityUtils.getBedrockBlockEntityId(tagName);

View file

@ -31,6 +31,7 @@ import com.nukkitx.nbt.CompoundTagBuilder;
import com.nukkitx.nbt.tag.StringTag; import com.nukkitx.nbt.tag.StringTag;
import com.nukkitx.protocol.bedrock.data.ContainerId; import com.nukkitx.protocol.bedrock.data.ContainerId;
import com.nukkitx.protocol.bedrock.data.ItemData; import com.nukkitx.protocol.bedrock.data.ItemData;
import com.nukkitx.protocol.bedrock.packet.ContainerClosePacket;
import com.nukkitx.protocol.bedrock.packet.InventorySlotPacket; import com.nukkitx.protocol.bedrock.packet.InventorySlotPacket;
import org.geysermc.common.ChatColor; import org.geysermc.common.ChatColor;
import org.geysermc.connector.GeyserConnector; import org.geysermc.connector.GeyserConnector;
@ -53,12 +54,18 @@ public class InventoryUtils {
if (translator != null) { if (translator != null) {
session.getInventoryCache().setOpenInventory(inventory); session.getInventoryCache().setOpenInventory(inventory);
translator.prepareInventory(session, 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 //TODO: find better way to handle double chest delay
if (translator instanceof DoubleChestInventoryTranslator) { if (translator instanceof DoubleChestInventoryTranslator) {
delay = Math.max(delay, 200);
}
if (delay > 0) {
GeyserConnector.getInstance().getGeneralThreadPool().schedule(() -> { GeyserConnector.getInstance().getGeneralThreadPool().schedule(() -> {
translator.openInventory(session, inventory); translator.openInventory(session, inventory);
translator.updateInventory(session, inventory); translator.updateInventory(session, inventory);
}, 200, TimeUnit.MILLISECONDS); }, delay, TimeUnit.MILLISECONDS);
} else { } else {
translator.openInventory(session, inventory); translator.openInventory(session, inventory);
translator.updateInventory(session, inventory); translator.updateInventory(session, inventory);
@ -69,26 +76,41 @@ public class InventoryUtils {
public static void closeInventory(GeyserSession session, int windowId) { public static void closeInventory(GeyserSession session, int windowId) {
if (windowId != 0) { if (windowId != 0) {
Inventory inventory = session.getInventoryCache().getInventories().get(windowId); 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()); InventoryTranslator translator = InventoryTranslator.INVENTORY_TRANSLATORS.get(inventory.getWindowType());
translator.closeInventory(session, inventory); translator.closeInventory(session, inventory);
session.getInventoryCache().uncacheInventory(windowId);
session.getInventoryCache().setOpenInventory(null); session.getInventoryCache().setOpenInventory(null);
} else {
return;
} }
} else { } else {
Inventory inventory = session.getInventory(); Inventory inventory = session.getInventory();
InventoryTranslator translator = InventoryTranslator.INVENTORY_TRANSLATORS.get(inventory.getWindowType()); InventoryTranslator translator = InventoryTranslator.INVENTORY_TRANSLATORS.get(inventory.getWindowType());
translator.updateInventory(session, inventory); translator.updateInventory(session, inventory);
} }
session.setCraftSlot(0); session.setCraftSlot(0);
session.getInventory().setCursor(null); 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) { public static void updateCursor(GeyserSession session) {
InventorySlotPacket cursorPacket = new InventorySlotPacket(); InventorySlotPacket cursorPacket = new InventorySlotPacket();
cursorPacket.setContainerId(ContainerId.CURSOR); cursorPacket.setContainerId(ContainerId.CURSOR);
cursorPacket.setSlot(0); cursorPacket.setSlot(0);
cursorPacket.setItem(ItemTranslator.translateToBedrock(session.getInventory().getCursor())); cursorPacket.setItem(ItemTranslator.translateToBedrock(session, session.getInventory().getCursor()));
session.sendUpstreamPacket(cursorPacket); session.sendUpstreamPacket(cursorPacket);
} }

View file

@ -27,6 +27,8 @@ package org.geysermc.connector.utils;
public class MathUtils { public class MathUtils {
public static final double SQRT_OF_TWO = Math.sqrt(2);
/** /**
* Round the given float to the next whole number * Round the given float to the next whole number
* *
@ -37,4 +39,19 @@ public class MathUtils {
int truncated = (int) floatNumber; int truncated = (int) floatNumber;
return floatNumber > truncated ? truncated + 1 : truncated; 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;
}
} }

10
pom.xml
View file

@ -38,14 +38,8 @@
<repositories> <repositories>
<repository> <repository>
<id>CodeMC-repo</id> <id>jitpack.io</id>
<url>https://repo.codemc.org/repository/maven-public</url> <url>https://jitpack.io</url>
<releases>
<enabled>true</enabled>
</releases>
<snapshots>
<enabled>true</enabled>
</snapshots>
</repository> </repository>
<repository> <repository>
<id>nukkitx-release-repo</id> <id>nukkitx-release-repo</id>