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
- [ ] Cartography Table
- [ ] Stonecutter
- [ ] Villager Trading
- Some Entity Flags
## Compiling

View File

@ -25,216 +25,29 @@
package org.geysermc.platform.bungeecord;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import lombok.Getter;
import net.md_5.bungee.api.plugin.Plugin;
import net.md_5.bungee.config.Configuration;
import org.geysermc.connector.FloodgateKeyLoader;
import org.geysermc.connector.GeyserConfiguration;
import org.geysermc.connector.configuration.GeyserJacksonConfiguration;
import java.io.File;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.HashMap;
import java.util.Map;
public class GeyserBungeeConfiguration implements GeyserConfiguration {
private File dataFolder;
private Configuration config;
private BungeeBedrockConfiguration bedrockConfig;
private BungeeRemoteConfiguration remoteConfig;
private BungeeMetricsInfo metricsInfo;
private Map<String, BungeeUserAuthenticationInfo> userAuthInfo = new HashMap<>();
@Getter
@JsonIgnoreProperties(ignoreUnknown = true)
public class GeyserBungeeConfiguration extends GeyserJacksonConfiguration {
private Path floodgateKey;
public GeyserBungeeConfiguration(File dataFolder, Configuration config) {
this.dataFolder = dataFolder;
this.config = config;
bedrockConfig = new BungeeBedrockConfiguration();
remoteConfig = new BungeeRemoteConfiguration();
metricsInfo = new BungeeMetricsInfo();
if (!config.contains("userAuths"))
return;
for (String key : config.getSection("userAuths").getKeys()) {
userAuthInfo.put(key, new BungeeUserAuthenticationInfo(key));
}
}
public void loadFloodgate(GeyserBungeePlugin plugin) {
public void loadFloodgate(GeyserBungeePlugin plugin, Configuration configuration) {
Plugin floodgate = plugin.getProxy().getPluginManager().getPlugin("floodgate-bungee");
floodgateKey = FloodgateKeyLoader.getKey(plugin.getGeyserLogger(), this, Paths.get(dataFolder.toString(), config.getString("floodgate-key-file", "public-key.pem")), floodgate, floodgate != null ? floodgate.getDataFolder().toPath() : null);
}
@Override
public BungeeBedrockConfiguration getBedrock() {
return bedrockConfig;
}
@Override
public BungeeRemoteConfiguration getRemote() {
return remoteConfig;
}
@Override
public Map<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");
floodgateKey = FloodgateKeyLoader.getKey(plugin.getGeyserLogger(), this, Paths.get(plugin.getDataFolder().toString(), configuration.getString("floodgate-key-file"), "public-key.pem"), floodgate, floodgate != null ? floodgate.getDataFolder().toPath() : null);
}
@Override
public Path getFloodgateKeyFile() {
return floodgateKey;
}
@Override
public boolean isCacheChunks() {
return config.getBoolean("cache-chunks", false);
}
@Override
public boolean isAboveBedrockNetherBuilding() {
return config.getBoolean("above-bedrock-nether-building", false);
}
@Override
public BungeeMetricsInfo getMetrics() {
return metricsInfo;
}
public class BungeeBedrockConfiguration implements IBedrockConfiguration {
@Override
public String getAddress() {
return config.getString("bedrock.address", "0.0.0.0");
}
@Override
public int getPort() {
return config.getInt("bedrock.port", 25565);
}
@Override
public String getMotd1() {
return config.getString("bedrock.motd1", "GeyserMC");
}
@Override
public String getMotd2() {
return config.getString("bedrock.motd2", "GeyserMC");
}
}
public class BungeeRemoteConfiguration implements IRemoteConfiguration {
@Override
public String getAddress() {
return config.getString("remote.address", "127.0.0.1");
}
@Override
public int getPort() {
return config.getInt("remote.port", 25565);
}
@Override
public String getAuthType() {
return config.getString("remote.auth-type", "online");
}
}
public class BungeeUserAuthenticationInfo implements IUserAuthenticationInfo {
private String key;
public BungeeUserAuthenticationInfo(String key) {
this.key = key;
}
@Override
public String getEmail() {
return config.getString("userAuths." + key + ".email");
}
@Override
public String getPassword() {
return config.getString("userAuths." + key + ".password");
}
}
public class BungeeMetricsInfo implements IMetricsInfo {
@Override
public boolean isEnabled() {
return config.getBoolean("metrics.enabled", true);
}
@Override
public String getUniqueId() {
return config.getString("metrics.uuid", "generateduuid");
}
}
@Override
public int getConfigVersion() {
return config.getInt("config-version", 0);
}
}

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.YamlConfiguration;
import org.geysermc.common.PlatformType;
import org.geysermc.connector.GeyserConfiguration;
import org.geysermc.connector.GeyserConnector;
import org.geysermc.connector.bootstrap.GeyserBootstrap;
import org.geysermc.connector.command.CommandManager;
import org.geysermc.connector.configuration.GeyserConfiguration;
import org.geysermc.connector.ping.GeyserLegacyPingPassthrough;
import org.geysermc.connector.ping.IGeyserPingPassthrough;
import org.geysermc.connector.utils.FileUtils;
import org.geysermc.platform.bungeecord.command.GeyserBungeeCommandExecutor;
import org.geysermc.platform.bungeecord.command.GeyserBungeeCommandManager;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.net.InetSocketAddress;
import java.nio.file.Files;
import java.util.UUID;
import java.util.logging.Level;
@ -62,32 +61,18 @@ public class GeyserBungeePlugin extends Plugin implements GeyserBootstrap {
if (!getDataFolder().exists())
getDataFolder().mkdir();
File file = new File(getDataFolder(), "config.yml");
Configuration configuration = null;
if (!file.exists()) {
try (InputStream in = getResourceAsStream("config.yml")) {
Files.copy(in, file.toPath());
} catch (IOException ex) {
getLogger().log(Level.SEVERE, "Failed to read/create config.yml! Make sure it's up to date and/or readable+writable!", ex);
return;
}
}
try {
if (!getDataFolder().exists())
getDataFolder().mkdir();
File configFile = FileUtils.fileOrCopiedFromResource(new File(getDataFolder(), "config.yml"), "config.yml", (x) -> x.replaceAll("generateduuid", UUID.randomUUID().toString()));
this.geyserConfig = FileUtils.loadConfig(configFile, GeyserBungeeConfiguration.class);
configuration = ConfigurationProvider.getProvider(YamlConfiguration.class).load(new File(getDataFolder(), "config.yml"));
} catch(IOException e) {
e.printStackTrace();
} catch (IOException ex) {
getLogger().log(Level.WARNING, "Failed to read/create config.yml! Make sure it's up to date and/or readable+writable!", ex);
ex.printStackTrace();
}
if (configuration == null) {
getLogger().severe("Failed to read/create config.yml! Make sure it's up to date and/or readable+writable!");
return;
}
this.geyserConfig = new GeyserBungeeConfiguration(getDataFolder(), configuration);
boolean configHasChanged = false;
if (getProxy().getConfig().getListeners().size() == 1) {
ListenerInfo listener = getProxy().getConfig().getListeners().toArray(new ListenerInfo[0])[0];
@ -96,33 +81,21 @@ public class GeyserBungeePlugin extends Plugin implements GeyserBootstrap {
// Don't change the ip if its listening on all interfaces
// By default this should be 127.0.0.1 but may need to be changed in some circumstances
if (!javaAddr.getHostString().equals("0.0.0.0") && !javaAddr.getHostString().equals("")) {
configuration.set("remote.address", javaAddr.getHostString());
this.geyserConfig.getRemote().setAddress(javaAddr.getHostString());
}
configuration.set("remote.port", javaAddr.getPort());
configHasChanged = true;
}
if (geyserConfig.getMetrics().getUniqueId().equals("generateduuid")) {
configuration.set("metrics.uuid", UUID.randomUUID().toString());
configHasChanged = true;
}
if (configHasChanged) {
try {
ConfigurationProvider.getProvider(YamlConfiguration.class).save(configuration, new File(getDataFolder(), "config.yml"));
} catch (IOException ex) {
getLogger().log(Level.SEVERE, "Failed to read/create config.yml! Make sure it's up to date and/or readable+writable!", ex);
return;
}
this.geyserConfig.getRemote().setPort(javaAddr.getPort());
}
this.geyserLogger = new GeyserBungeeLogger(getLogger(), geyserConfig.isDebugMode());
GeyserConfiguration.checkGeyserConfiguration(geyserConfig, geyserLogger);
geyserConfig.loadFloodgate(this);
if (geyserConfig.getRemote().getAuthType().equals("floodgate") && getProxy().getPluginManager().getPlugin("floodgate-bungee") == null) {
geyserLogger.severe("Auth type set to Floodgate but Floodgate not found! Disabling...");
return;
}
geyserConfig.loadFloodgate(this, configuration);
this.connector = GeyserConnector.start(PlatformType.BUNGEECORD, this);

View File

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

View File

@ -25,120 +25,29 @@
package org.geysermc.platform.spigot;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Getter;
import org.bukkit.Bukkit;
import org.bukkit.configuration.file.FileConfiguration;
import org.bukkit.plugin.Plugin;
import org.geysermc.connector.FloodgateKeyLoader;
import org.geysermc.connector.GeyserConfiguration;
import org.geysermc.connector.configuration.GeyserJacksonConfiguration;
import java.io.File;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.HashMap;
import java.util.Map;
public class GeyserSpigotConfiguration implements GeyserConfiguration {
@Getter
@JsonIgnoreProperties(ignoreUnknown = true)
public class GeyserSpigotConfiguration extends GeyserJacksonConfiguration {
private FileConfiguration config;
private File dataFolder;
private BukkitBedrockConfiguration bedrockConfig;
private BukkitRemoteConfiguration remoteConfig;
private BukkitMetricsInfo metricsInfo;
private Map<String, BukkitUserAuthenticationInfo> userAuthInfo = new HashMap<>();
@JsonProperty("floodgate-key-file")
private String floodgateKeyFile;
private Path floodgateKey;
public GeyserSpigotConfiguration(File dataFolder, FileConfiguration config) {
this.dataFolder = dataFolder;
this.config = config;
bedrockConfig = new BukkitBedrockConfiguration();
remoteConfig = new BukkitRemoteConfiguration();
metricsInfo = new BukkitMetricsInfo();
if (!config.contains("userAuths"))
return;
for (String key : config.getConfigurationSection("userAuths").getKeys(false)) {
userAuthInfo.put(key, new BukkitUserAuthenticationInfo(key));
}
}
public void loadFloodgate(GeyserSpigotPlugin plugin) {
Plugin floodgate = Bukkit.getPluginManager().getPlugin("floodgate-bukkit");
floodgateKey = FloodgateKeyLoader.getKey(plugin.getGeyserLogger(), this, Paths.get(dataFolder.toString(), config.getString("floodgate-key-file", "public-key.pem")), floodgate, floodgate != null ? floodgate.getDataFolder().toPath() : null);
}
@Override
public IBedrockConfiguration getBedrock() {
return bedrockConfig;
}
@Override
public IRemoteConfiguration getRemote() {
return remoteConfig;
}
@Override
public Map<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");
floodgateKey = FloodgateKeyLoader.getKey(plugin.getGeyserLogger(), this, Paths.get(plugin.getDataFolder().toString(), plugin.getConfig().getString("floodgate-key-file", "public-key.pem")), floodgate, floodgate != null ? floodgate.getDataFolder().toPath() : null);
}
@Override
@ -150,92 +59,4 @@ public class GeyserSpigotConfiguration implements GeyserConfiguration {
public boolean isCacheChunks() {
return true; // We override this as with Bukkit, we have direct access to the server implementation
}
@Override
public boolean isAboveBedrockNetherBuilding() {
return config.getBoolean("above-bedrock-nether-building", false);
}
@Override
public IMetricsInfo getMetrics() {
return metricsInfo;
}
public class BukkitBedrockConfiguration implements IBedrockConfiguration {
@Override
public String getAddress() {
return config.getString("bedrock.address", "0.0.0.0");
}
@Override
public int getPort() {
return config.getInt("bedrock.port", 25565);
}
@Override
public String getMotd1() {
return config.getString("bedrock.motd1", "GeyserMC");
}
@Override
public String getMotd2() {
return config.getString("bedrock.motd2", "GeyserMC");
}
}
public class BukkitRemoteConfiguration implements IRemoteConfiguration {
@Override
public String getAddress() {
return config.getString("remote.address", "127.0.0.1");
}
@Override
public int getPort() {
return config.getInt("remote.port", 25565);
}
@Override
public String getAuthType() {
return config.getString("remote.auth-type", "online");
}
}
public class BukkitUserAuthenticationInfo implements IUserAuthenticationInfo {
private String key;
public BukkitUserAuthenticationInfo(String key) {
this.key = key;
}
@Override
public String getEmail() {
return config.getString("userAuths." + key + ".email");
}
@Override
public String getPassword() {
return config.getString("userAuths." + key + ".password");
}
}
public class BukkitMetricsInfo implements IMetricsInfo {
@Override
public boolean isEnabled() {
return config.getBoolean("metrics.enabled", true);
}
@Override
public String getUniqueId() {
return config.getString("metrics.uuid", "generateduuid");
}
}
@Override
public int getConfigVersion() {
return config.getInt("config-version", 0);
}
}

View File

@ -28,10 +28,10 @@ package org.geysermc.platform.spigot;
import org.bukkit.Bukkit;
import org.bukkit.plugin.java.JavaPlugin;
import org.geysermc.common.PlatformType;
import org.geysermc.connector.GeyserConfiguration;
import org.geysermc.connector.GeyserConnector;
import org.geysermc.connector.bootstrap.GeyserBootstrap;
import org.geysermc.connector.command.CommandManager;
import org.geysermc.connector.configuration.GeyserConfiguration;
import org.geysermc.connector.network.translators.world.WorldManager;
import org.geysermc.connector.ping.GeyserLegacyPingPassthrough;
import org.geysermc.connector.ping.IGeyserPingPassthrough;
@ -39,9 +39,14 @@ import org.geysermc.platform.spigot.command.GeyserSpigotCommandExecutor;
import org.geysermc.platform.spigot.command.GeyserSpigotCommandManager;
import org.geysermc.platform.spigot.world.GeyserSpigotBlockPlaceListener;
import org.geysermc.platform.spigot.world.GeyserSpigotWorldManager;
import org.geysermc.connector.utils.FileUtils;
import us.myles.ViaVersion.api.Via;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.util.UUID;
import java.util.logging.Level;
public class GeyserSpigotPlugin extends JavaPlugin implements GeyserBootstrap {
@ -56,26 +61,41 @@ public class GeyserSpigotPlugin extends JavaPlugin implements GeyserBootstrap {
@Override
public void onEnable() {
saveDefaultConfig();
this.geyserConfig = new GeyserSpigotConfiguration(getDataFolder(), getConfig());
if (geyserConfig.getMetrics().getUniqueId().equals("generateduuid")) {
getConfig().set("metrics.uuid", UUID.randomUUID().toString());
saveConfig();
// This is manually done instead of using Bukkit methods to save the config because otherwise comments get removed
try {
if (!getDataFolder().exists()) {
getDataFolder().mkdir();
File bukkitConfig = new File("plugins/Geyser-Bukkit/config.yml");
if (bukkitConfig.exists()) { // Copy over old configs
getLogger().log(Level.INFO, "Existing config found in the Geyser-Bukkit folder; copying over...");
Files.copy(bukkitConfig.toPath(), new File(getDataFolder().toString() + "/config.yml").toPath());
getLogger().log(Level.INFO, "Copied!");
}
}
File configFile = FileUtils.fileOrCopiedFromResource(new File(getDataFolder(), "config.yml"), "config.yml", (x) -> x.replaceAll("generateduuid", UUID.randomUUID().toString()));
this.geyserConfig = FileUtils.loadConfig(configFile, GeyserSpigotConfiguration.class);
} catch (IOException ex) {
getLogger().log(Level.WARNING, "Failed to read/create config.yml! Make sure it's up to date and/or readable+writable!", ex);
ex.printStackTrace();
}
// Don't change the ip if its listening on all interfaces
// By default this should be 127.0.0.1 but may need to be changed in some circumstances
if (!Bukkit.getIp().equals("0.0.0.0") && !Bukkit.getIp().equals("")) {
getConfig().set("remote.address", Bukkit.getIp());
geyserConfig.getRemote().setAddress(Bukkit.getIp());
}
getConfig().set("remote.port", Bukkit.getPort());
saveConfig();
geyserConfig.getRemote().setPort(Bukkit.getPort());
this.geyserLogger = new GeyserSpigotLogger(getLogger(), geyserConfig.isDebugMode());
GeyserConfiguration.checkGeyserConfiguration(geyserConfig, geyserLogger);
if (geyserConfig.getRemote().getAuthType().equals("floodgate") && Bukkit.getPluginManager().getPlugin("floodgate-bukkit") == null) {
geyserLogger.severe("Auth type set to Floodgate but Floodgate not found! Disabling...");
this.getPluginLoader().disablePlugin(this);
return;
}
geyserConfig.loadFloodgate(this);
this.connector = GeyserConnector.start(PlatformType.SPIGOT, this);
@ -113,7 +133,8 @@ public class GeyserSpigotPlugin extends JavaPlugin implements GeyserBootstrap {
@Override
public void onDisable() {
connector.shutdown();
if (connector != null)
connector.shutdown();
}
@Override

View File

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

View File

@ -30,7 +30,7 @@ import ninja.leaping.configurate.ConfigurationNode;
import ninja.leaping.configurate.loader.ConfigurationLoader;
import ninja.leaping.configurate.yaml.YAMLConfigurationLoader;
import org.geysermc.common.PlatformType;
import org.geysermc.connector.GeyserConfiguration;
import org.geysermc.connector.configuration.GeyserConfiguration;
import org.geysermc.connector.GeyserConnector;
import org.geysermc.connector.bootstrap.GeyserBootstrap;
import org.geysermc.connector.command.CommandManager;

View File

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

View File

@ -27,110 +27,21 @@ package org.geysermc.platform.standalone;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Getter;
import org.geysermc.connector.GeyserConfiguration;
import org.geysermc.connector.configuration.GeyserJacksonConfiguration;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Map;
@JsonIgnoreProperties(ignoreUnknown = true)
@Getter
public class GeyserStandaloneConfiguration implements GeyserConfiguration {
private BedrockConfiguration bedrock;
private RemoteConfiguration remote;
@JsonIgnoreProperties(ignoreUnknown = true)
public class GeyserStandaloneConfiguration extends GeyserJacksonConfiguration {
@JsonProperty("floodgate-key-file")
private String floodgateKeyFile;
private Map<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
public Path getFloodgateKeyFile() {
return Paths.get(floodgateKeyFile);
}
@Getter
public static class BedrockConfiguration implements IBedrockConfiguration {
private String address;
private int port;
private String motd1;
private String motd2;
}
@Getter
public static class RemoteConfiguration implements IRemoteConfiguration {
private String address;
private int port;
private String motd1;
private String motd2;
@JsonProperty("auth-type")
private String authType;
}
@Getter
public static class UserAuthenticationInfo implements IUserAuthenticationInfo {
private String email;
private String password;
}
@Getter
public static class MetricsInfo implements IMetricsInfo {
private boolean enabled;
@JsonProperty("uuid")
private String uniqueId;
}
@JsonProperty("config-version")
private int configVersion;
}

View File

@ -30,122 +30,30 @@ import com.fasterxml.jackson.annotation.JsonProperty;
import com.velocitypowered.api.plugin.PluginContainer;
import com.velocitypowered.api.proxy.ProxyServer;
import lombok.Getter;
import lombok.Setter;
import org.geysermc.connector.FloodgateKeyLoader;
import org.geysermc.connector.GeyserConfiguration;
import org.geysermc.connector.configuration.GeyserJacksonConfiguration;
import java.io.File;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Map;
import java.util.Optional;
@JsonIgnoreProperties(ignoreUnknown = true)
@Getter
public class GeyserVelocityConfiguration implements GeyserConfiguration {
private BedrockConfiguration bedrock;
private RemoteConfiguration remote;
@JsonIgnoreProperties(ignoreUnknown = true)
public class GeyserVelocityConfiguration extends GeyserJacksonConfiguration {
@JsonProperty("floodgate-key-file")
private String floodgateKeyFile;
private Map<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;
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
public Path getFloodgateKeyFile() {
return floodgateKey;
}
@Getter
public static class BedrockConfiguration implements IBedrockConfiguration {
private String address;
private int port;
private String motd1;
private String motd2;
public void loadFloodgate(GeyserVelocityPlugin plugin, ProxyServer proxyServer, File dataFolder) {
Optional<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/")));
}
@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 org.geysermc.common.PlatformType;
import org.geysermc.connector.GeyserConfiguration;
import org.geysermc.connector.configuration.GeyserConfiguration;
import org.geysermc.connector.GeyserConnector;
import org.geysermc.connector.bootstrap.GeyserBootstrap;
import org.geysermc.connector.ping.GeyserLegacyPingPassthrough;
@ -96,6 +96,11 @@ public class GeyserVelocityPlugin implements GeyserBootstrap {
this.geyserLogger = new GeyserVelocityLogger(logger, geyserConfig.isDebugMode());
GeyserConfiguration.checkGeyserConfiguration(geyserConfig, geyserLogger);
if (geyserConfig.getRemote().getAuthType().equals("floodgate") && !proxyServer.getPluginManager().getPlugin("floodgate").isPresent()) {
geyserLogger.severe("Auth type set to Floodgate but Floodgate not found! Disabling...");
return;
}
geyserConfig.loadFloodgate(this, proxyServer, configDir);
this.connector = GeyserConnector.start(PlatformType.VELOCITY, this);

View File

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

View File

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

View File

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

View File

@ -27,7 +27,7 @@
package org.geysermc.connector.bootstrap;
import org.geysermc.connector.ping.IGeyserPingPassthrough;
import org.geysermc.connector.GeyserConfiguration;
import org.geysermc.connector.configuration.GeyserConfiguration;
import org.geysermc.connector.GeyserLogger;
import org.geysermc.connector.command.CommandManager;
import org.geysermc.connector.network.translators.world.CachedChunkManager;

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

View File

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

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.setFromFishing(false);
itemPacket.getMetadata().putAll(metadata);
itemPacket.setItemInHand(ItemTranslator.translateToBedrock((ItemStack) entityMetadata.getValue()));
itemPacket.setItemInHand(ItemTranslator.translateToBedrock(session, (ItemStack) entityMetadata.getValue()));
session.sendUpstreamPacket(itemPacket);
}

View File

@ -98,7 +98,7 @@ public class ItemFrameEntity extends Entity {
@Override
public void updateBedrockMetadata(EntityMetadata entityMetadata, GeyserSession session) {
if (entityMetadata.getId() == 7 && entityMetadata.getValue() != null) {
ItemData itemData = ItemTranslator.translateToBedrock((ItemStack) entityMetadata.getValue());
ItemData itemData = ItemTranslator.translateToBedrock(session, (ItemStack) entityMetadata.getValue());
ItemEntry itemEntry = ItemRegistry.getItem((ItemStack) entityMetadata.getValue());
CompoundTagBuilder builder = CompoundTag.builder();

View File

@ -30,6 +30,7 @@ import com.nukkitx.math.vector.Vector3f;
import com.nukkitx.protocol.bedrock.data.EntityData;
import org.geysermc.connector.entity.type.EntityType;
import org.geysermc.connector.network.session.GeyserSession;
import org.geysermc.connector.network.translators.world.block.BlockTranslator;
public class MinecartEntity extends Entity {
@ -54,6 +55,24 @@ public class MinecartEntity extends Entity {
metadata.put(EntityData.HURT_TIME, Math.min((int) (float) entityMetadata.getValue(), 15));
}
if (!(this instanceof DefaultBlockMinecartEntity)) { // Handled in the DefaultBlockMinecartEntity class
// Custom block
if (entityMetadata.getId() == 10) {
metadata.put(EntityData.DISPLAY_ITEM, BlockTranslator.getBedrockBlockId((int) entityMetadata.getValue()));
}
// Custom block offset
if (entityMetadata.getId() == 11) {
metadata.put(EntityData.DISPLAY_OFFSET, entityMetadata.getValue());
}
// If the custom block should be enabled
if (entityMetadata.getId() == 12) {
// Needs a byte based off of Java's boolean
metadata.put(EntityData.HAS_DISPLAY, (byte) ((boolean) entityMetadata.getValue() ? 1 : 0));
}
}
super.updateBedrockMetadata(entityMetadata, session);
}

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 {
// These are used to store the state of the armour stand for use when handling invisibility
private boolean isMarker = false;
private boolean isInvisible = false;
private boolean isSmall = false;
public ArmorStandEntity(long entityId, long geyserId, EntityType entityType, Vector3f position, Vector3f motion, Vector3f rotation) {
super(entityId, geyserId, entityType, position, motion, rotation);
}
@Override
public void moveAbsolute(GeyserSession session, Vector3f position, Vector3f rotation, boolean isOnGround, boolean teleported) {
// Fake the height to be above where it is so the nametag appears in the right location for invisible non-marker armour stands
if (!isMarker && isInvisible) {
position = position.add(0d, entityType.getHeight() * (isSmall ? 0.55d : 1d), 0d);
}
super.moveAbsolute(session, position, rotation, isOnGround, teleported);
}
@Override
public void updateBedrockMetadata(EntityMetadata entityMetadata, GeyserSession session) {
if (entityMetadata.getType() == MetadataType.BYTE) {
if (entityMetadata.getId() == 0 && entityMetadata.getType() == MetadataType.BYTE) {
byte xd = (byte) entityMetadata.getValue();
// Check if the armour stand is invisible and store accordingly
if ((xd & 0x20) == 0x20) {
metadata.put(EntityData.SCALE, 0.0f);
isInvisible = true;
}
} else if (entityMetadata.getId() == 14 && entityMetadata.getType() == MetadataType.BYTE) {
byte xd = (byte) entityMetadata.getValue();
// isSmall
if ((xd & 0x01) == 0x01) {
isSmall = true;
if (metadata.getFloat(EntityData.SCALE) != 0.55f && metadata.getFloat(EntityData.SCALE) != 0.0f) {
metadata.put(EntityData.SCALE, 0.55f);
}
@ -60,9 +85,10 @@ public class ArmorStandEntity extends LivingEntity {
}
// setMarker
if ((xd & 0x10) == 0x10 && (metadata.get(EntityData.BOUNDING_BOX_WIDTH) != null && !metadata.get(EntityData.BOUNDING_BOX_WIDTH).equals(0.0f))) {
if ((xd & 0x10) == 0x10 && (metadata.get(EntityData.BOUNDING_BOX_WIDTH) == null || !metadata.get(EntityData.BOUNDING_BOX_WIDTH).equals(0.0f))) {
metadata.put(EntityData.BOUNDING_BOX_WIDTH, 0.0f);
metadata.put(EntityData.BOUNDING_BOX_HEIGHT, 0.0f);
isMarker = true;
}
}
super.updateBedrockMetadata(entityMetadata, session);

View File

@ -105,7 +105,7 @@ public enum EntityType {
THROWN_EXP_BOTTLE(ThrowableEntity.class, 68, 0.25f, 0.25f, 0f, 0f, "minecraft:xp_bottle"),
EXPERIENCE_ORB(ExpOrbEntity.class, 69, 0f, 0f, 0f, 0f, "minecraft:xp_orb"),
EYE_OF_ENDER(Entity.class, 70, 0.25f, 0.25f, 0f, 0f, "minecraft:eye_of_ender_signal"),
END_CRYSTAL(EnderCrystalEntity.class, 71, 0f, 0f, 0f, 0f, "minecraft:ender_crystal"),
END_CRYSTAL(EnderCrystalEntity.class, 71, 2.0f, 2.0f, 2.0f, 0f, "minecraft:ender_crystal"),
FIREWORK_ROCKET(FireworkEntity.class, 72, 0.25f, 0.25f, 0.25f, 0f, "minecraft:fireworks_rocket"),
TRIDENT(TridentEntity.class, 73, 0f, 0f, 0f, 0f, "minecraft:thrown_trident"),
TURTLE(AnimalEntity.class, 74, 0.4f, 1.2f),
@ -133,12 +133,13 @@ public enum EntityType {
MINECART_HOPPER(MinecartEntity.class, 96, 0.7f, 0.98f, 0.98f, 0.35f, "minecraft:hopper_minecart"),
MINECART_TNT(MinecartEntity.class, 97, 0.7f, 0.98f, 0.98f, 0.35f, "minecraft:tnt_minecart"),
MINECART_CHEST(MinecartEntity.class, 98, 0.7f, 0.98f, 0.98f, 0.35f, "minecraft:chest_minecart"),
MINECART_FURNACE(FurnaceMinecartEntity.class, 98, 0.7f, 0.98f, 0.98f, 0.35f, "minecraft:minecart"),
MINECART_SPAWNER(SpawnerMinecartEntity.class, 98, 0.7f, 0.98f, 0.98f, 0.35f, "minecraft:minecart"),
MINECART_COMMAND_BLOCK(MinecartEntity.class, 100, 0.7f, 0.98f, 0.98f, 0.35f, "minecraft:command_block_minecart"),
LINGERING_POTION(ThrowableEntity.class, 101, 0f),
LLAMA_SPIT(Entity.class, 102, 0.25f),
EVOKER_FANGS(Entity.class, 103, 0.8f, 0.5f),
EVOKER(SpellcasterIllagerEntity.class, 104, 1.95f, 0.5f),
EVOKER_FANGS(Entity.class, 103, 0.8f, 0.5f, 0.5f, 0f, "minecraft:evocation_fang"),
EVOKER(SpellcasterIllagerEntity.class, 104, 1.95f, 0.6f, 0.6f, 0f, "minecraft:evocation_illager"),
VEX(MonsterEntity.class, 105, 0.8f, 0.4f),
ICE_BOMB(Entity.class, 106, 0f),
BALLOON(Entity.class, 107, 0f), //TODO

View File

@ -31,7 +31,7 @@ import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.socket.DatagramPacket;
import org.geysermc.common.ping.GeyserPingInfo;
import org.geysermc.connector.ping.IGeyserPingPassthrough;
import org.geysermc.connector.GeyserConfiguration;
import org.geysermc.connector.configuration.GeyserConfiguration;
import org.geysermc.connector.GeyserConnector;
import org.geysermc.connector.network.session.GeyserSession;
import org.geysermc.connector.utils.MessageUtils;

View File

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

View File

@ -28,7 +28,7 @@ package org.geysermc.connector.network;
import com.nukkitx.protocol.bedrock.BedrockPacket;
import com.nukkitx.protocol.bedrock.packet.*;
import org.geysermc.common.AuthType;
import org.geysermc.connector.GeyserConfiguration;
import org.geysermc.connector.configuration.GeyserConfiguration;
import org.geysermc.connector.GeyserConnector;
import org.geysermc.connector.network.session.GeyserSession;
import org.geysermc.connector.network.translators.PacketTranslatorRegistry;

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.data.SubProtocol;
import com.github.steveice10.mc.protocol.data.game.entity.player.GameMode;
import com.github.steveice10.mc.protocol.data.game.window.VillagerTrade;
import com.github.steveice10.mc.protocol.data.game.world.block.BlockState;
import com.github.steveice10.mc.protocol.packet.handshake.client.HandshakePacket;
import com.github.steveice10.mc.protocol.packet.ingame.client.world.ClientTeleportConfirmPacket;
@ -41,14 +42,10 @@ import com.github.steveice10.packetlib.event.session.*;
import com.github.steveice10.packetlib.packet.Packet;
import com.github.steveice10.packetlib.tcp.TcpSessionFactory;
import com.nukkitx.math.GenericMath;
import com.nukkitx.math.TrigMath;
import com.nukkitx.math.vector.*;
import com.nukkitx.protocol.bedrock.BedrockPacket;
import com.nukkitx.protocol.bedrock.BedrockServerSession;
import com.nukkitx.protocol.bedrock.data.ContainerId;
import com.nukkitx.protocol.bedrock.data.GamePublishSetting;
import com.nukkitx.protocol.bedrock.data.GameRuleData;
import com.nukkitx.protocol.bedrock.data.PlayerPermission;
import com.nukkitx.protocol.bedrock.data.*;
import com.nukkitx.protocol.bedrock.packet.*;
import it.unimi.dsi.fastutil.objects.Object2LongMap;
import it.unimi.dsi.fastutil.objects.Object2LongOpenHashMap;
@ -60,6 +57,7 @@ import org.geysermc.connector.GeyserConnector;
import org.geysermc.connector.command.CommandSender;
import org.geysermc.connector.entity.Entity;
import org.geysermc.connector.entity.PlayerEntity;
import org.geysermc.connector.entity.attribute.AttributeType;
import org.geysermc.connector.inventory.PlayerInventory;
import org.geysermc.connector.network.remote.RemoteServer;
import org.geysermc.connector.network.session.auth.AuthData;
@ -72,6 +70,7 @@ import org.geysermc.connector.network.translators.item.ItemRegistry;
import org.geysermc.connector.network.translators.world.block.BlockTranslator;
import org.geysermc.connector.utils.ChunkUtils;
import org.geysermc.connector.utils.LocaleUtils;
import org.geysermc.connector.utils.MathUtils;
import org.geysermc.connector.utils.SkinUtils;
import org.geysermc.floodgate.util.BedrockData;
import org.geysermc.floodgate.util.EncryptionUtil;
@ -81,6 +80,8 @@ import java.net.InetSocketAddress;
import java.security.NoSuchAlgorithmException;
import java.security.PublicKey;
import java.security.spec.InvalidKeySpecException;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import java.util.concurrent.atomic.AtomicInteger;
@ -166,6 +167,14 @@ public class GeyserSession implements CommandSender {
@Setter
private int craftSlot = 0;
@Setter
private long lastWindowCloseTime = 0;
@Setter
private VillagerTrade[] villagerTrades;
@Setter
private long lastInteractedVillagerEid;
private MinecraftProtocol protocol;
public GeyserSession(GeyserConnector connector, BedrockServerSession bedrockServerSession) {
@ -211,6 +220,15 @@ public class GeyserSession implements CommandSender {
PlayStatusPacket playStatusPacket = new PlayStatusPacket();
playStatusPacket.setStatus(PlayStatusPacket.Status.PLAYER_SPAWN);
upstream.sendPacket(playStatusPacket);
UpdateAttributesPacket attributesPacket = new UpdateAttributesPacket();
attributesPacket.setRuntimeEntityId(getPlayerEntity().getGeyserId());
List<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) {
@ -366,6 +384,14 @@ public class GeyserSession implements CommandSender {
PacketTranslatorRegistry.JAVA_TRANSLATOR.translate(event.getPacket().getClass(), event.getPacket(), GeyserSession.this);
}
}
@Override
public void packetError(PacketErrorEvent event) {
connector.getLogger().warning("Downstream packet error! " + event.getCause().getMessage());
if (connector.getConfig().isDebugMode())
event.getCause().printStackTrace();
event.setSuppress(true);
}
});
downstream.getSession().connect();
@ -436,7 +462,7 @@ public class GeyserSession implements CommandSender {
}
public void setRenderDistance(int renderDistance) {
renderDistance = GenericMath.ceil(++renderDistance * TrigMath.SQRT_OF_TWO); //square to circle
renderDistance = GenericMath.ceil(++renderDistance * MathUtils.SQRT_OF_TWO); //square to circle
if (renderDistance > 32) renderDistance = 32; // <3 u ViaVersion but I don't like crashing clients x)
this.renderDistance = renderDistance;

View File

@ -25,18 +25,19 @@
package org.geysermc.connector.network.translators;
import java.util.HashMap;
import java.util.Map;
import com.github.steveice10.mc.protocol.packet.ingame.server.ServerKeepAlivePacket;
import com.github.steveice10.mc.protocol.packet.ingame.server.ServerPlayerListDataPacket;
import com.github.steveice10.mc.protocol.packet.ingame.server.world.ServerUpdateLightPacket;
import com.github.steveice10.packetlib.packet.Packet;
import com.nukkitx.protocol.bedrock.BedrockPacket;
import it.unimi.dsi.fastutil.objects.ObjectArrayList;
import org.geysermc.connector.GeyserConnector;
import org.geysermc.connector.network.session.GeyserSession;
import com.github.steveice10.packetlib.packet.Packet;
import com.nukkitx.protocol.bedrock.BedrockPacket;
import org.reflections.Reflections;
import java.util.HashMap;
import java.util.Map;
public class PacketTranslatorRegistry<T> {
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() {

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
public void translate(ContainerClosePacket packet, GeyserSession session) {
session.setLastWindowCloseTime(0);
byte windowId = packet.getWindowId();
Inventory openInventory = session.getInventoryCache().getOpenInventory();
if (windowId == -1) { //player inventory or crafting table
Inventory openInventory = session.getInventoryCache().getOpenInventory();
if (openInventory != null) {
windowId = (byte) openInventory.getId();
} else {
windowId = 0;
}
}
ClientCloseWindowPacket closeWindowPacket = new ClientCloseWindowPacket(windowId);
session.sendDownstreamPacket(closeWindowPacket);
InventoryUtils.closeInventory(session, windowId);
if (windowId == 0 || (openInventory != null && openInventory.getId() == windowId)) {
ClientCloseWindowPacket closeWindowPacket = new ClientCloseWindowPacket(windowId);
session.getDownstream().getSession().send(closeWindowPacket);
InventoryUtils.closeInventory(session, windowId);
}
}
}

View File

@ -26,7 +26,13 @@
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 org.geysermc.connector.entity.Entity;
import org.geysermc.connector.inventory.Inventory;
import org.geysermc.connector.network.session.GeyserSession;
import org.geysermc.connector.network.translators.PacketTranslator;
import org.geysermc.connector.network.translators.Translator;
@ -41,6 +47,22 @@ public class BedrockEntityEventTranslator extends PacketTranslator<EntityEventPa
case EATING_ITEM:
session.sendUpstreamPacket(packet);
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());
}

View File

@ -45,6 +45,7 @@ import com.nukkitx.protocol.bedrock.packet.InventoryTransactionPacket;
import org.geysermc.connector.entity.Entity;
import org.geysermc.connector.entity.ItemFrameEntity;
import org.geysermc.connector.entity.living.merchant.AbstractMerchantEntity;
import org.geysermc.connector.inventory.Inventory;
import org.geysermc.connector.network.session.GeyserSession;
import org.geysermc.connector.network.translators.PacketTranslator;
@ -198,6 +199,10 @@ public class BedrockInventoryTransactionTranslator extends PacketTranslator<Inve
session.sendDownstreamPacket(interactAtPacket);
EntitySoundInteractionHandler.handleEntityInteraction(session, vector, entity);
if (entity instanceof AbstractMerchantEntity) {
session.setLastInteractedVillagerEid(packet.getRuntimeEntityId());
}
break;
case 1: //Attack
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,7 +92,10 @@ public class CraftingInventoryTranslator extends BaseInventoryTranslator {
@Override
public int javaSlotToBedrock(int slot) {
return slot == 0 ? 50 : slot + 31;
if (slot < size) {
return slot == 0 ? 50 : slot + 31;
}
return super.javaSlotToBedrock(slot);
}
@Override

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

View File

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

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

View File

@ -25,11 +25,35 @@
package org.geysermc.connector.network.translators.inventory;
import com.github.steveice10.mc.protocol.data.game.world.block.BlockState;
import com.nukkitx.protocol.bedrock.data.ContainerType;
import org.geysermc.connector.network.translators.inventory.updater.ChestInventoryUpdater;
import org.geysermc.connector.inventory.Inventory;
import org.geysermc.connector.network.session.GeyserSession;
import org.geysermc.connector.network.translators.inventory.holder.BlockInventoryHolder;
import org.geysermc.connector.network.translators.inventory.holder.InventoryHolder;
import org.geysermc.connector.network.translators.world.block.BlockTranslator;
public class SingleChestInventoryTranslator extends ChestInventoryTranslator {
private final InventoryHolder holder;
public class SingleChestInventoryTranslator extends BlockInventoryTranslator {
public SingleChestInventoryTranslator(int size) {
super(size, "minecraft:chest[facing=north,type=single,waterlogged=false]", ContainerType.CONTAINER, new ChestInventoryUpdater(27));
super(size, 27);
BlockState javaBlockState = BlockTranslator.getJavaBlockState("minecraft:chest[facing=north,type=single,waterlogged=false]");
this.holder = new BlockInventoryHolder(BlockTranslator.getBedrockBlockId(javaBlockState), ContainerType.CONTAINER);
}
@Override
public void prepareInventory(GeyserSession session, Inventory inventory) {
holder.prepareInventory(this, session, inventory);
}
@Override
public void openInventory(GeyserSession session, Inventory inventory) {
holder.openInventory(this, session, inventory);
}
@Override
public void closeInventory(GeyserSession session, Inventory inventory) {
holder.closeInventory(this, session, inventory);
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -27,6 +27,8 @@
package org.geysermc.connector.network.translators.item;
import com.github.steveice10.mc.protocol.data.game.entity.metadata.ItemStack;
import com.github.steveice10.mc.protocol.data.message.Message;
import com.nukkitx.nbt.CompoundTagBuilder;
import com.github.steveice10.opennbt.tag.builtin.*;
import com.nukkitx.nbt.tag.CompoundTag;
import com.nukkitx.nbt.tag.Tag;
@ -34,7 +36,9 @@ import com.nukkitx.protocol.bedrock.data.ItemData;
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
import org.geysermc.connector.GeyserConnector;
import org.geysermc.connector.network.session.GeyserSession;
import org.geysermc.connector.network.translators.ItemRemapper;
import org.geysermc.connector.utils.MessageUtils;
import org.reflections.Reflections;
import java.util.ArrayList;
@ -115,7 +119,7 @@ public abstract class ItemTranslator {
return itemStack;
}
public static ItemData translateToBedrock(ItemStack stack) {
public static ItemData translateToBedrock(GeyserSession session, ItemStack stack) {
if (stack == null) {
return ItemData.AIR;
}
@ -132,12 +136,42 @@ public abstract class ItemTranslator {
}
}
ItemData itemData;
ItemTranslator itemStackTranslator = ITEM_STACK_TRANSLATORS.get(bedrockItem.getJavaId());
if (itemStackTranslator != null) {
return itemStackTranslator.translateToBedrock(itemStack, bedrockItem);
itemData = itemStackTranslator.translateToBedrock(itemStack, bedrockItem);
} else {
return DEFAULT_TRANSLATOR.translateToBedrock(itemStack, bedrockItem);
itemData = DEFAULT_TRANSLATOR.translateToBedrock(itemStack, bedrockItem);
}
// Get the display name of the item
CompoundTag tag = itemData.getTag();
if (tag != null) {
CompoundTag display = tag.getCompound("display");
if (display != null) {
String name = display.getString("Name");
// Check if its a message to translate
if (MessageUtils.isMessage(name)) {
// Get the translated name
name = MessageUtils.getTranslatedBedrockMessage(Message.fromString(name), session.getClientData().getLanguageCode());
// Build the new display tag
CompoundTagBuilder displayBuilder = display.toBuilder();
displayBuilder.stringTag("Name", name);
// Build the new root tag
CompoundTagBuilder builder = tag.toBuilder();
builder.tag(displayBuilder.build("display"));
// Create a new item with the original data + updated name
itemData = ItemData.of(itemData.getId(), itemData.getDamage(), itemData.getCount(), builder.buildRootTag());
}
}
}
return itemData;
}
private static final ItemTranslator DEFAULT_TRANSLATOR = new ItemTranslator() {
@ -351,5 +385,49 @@ public abstract class ItemTranslator {
return null;
}
/**
* Checks if an {@link ItemStack} is equal to another item stack
*
* @param itemStack the item stack to check
* @param equalsItemStack the item stack to check if equal to
* @param checkAmount if the amount should be taken into account
* @param trueIfAmountIsGreater if this should return true if the amount of the
* first item stack is greater than that of the second
* @param checkNbt if NBT data should be checked
* @return if an item stack is equal to another item stack
*/
public boolean equals(ItemStack itemStack, ItemStack equalsItemStack, boolean checkAmount, boolean trueIfAmountIsGreater, boolean checkNbt) {
if (itemStack.getId() != equalsItemStack.getId()) {
return false;
}
if (checkAmount) {
if (trueIfAmountIsGreater) {
if (itemStack.getAmount() < equalsItemStack.getAmount()) {
return false;
}
} else {
if (itemStack.getAmount() != equalsItemStack.getAmount()) {
return false;
}
}
}
if (!checkNbt) {
return true;
}
if ((itemStack.getNbt() == null || itemStack.getNbt().isEmpty()) && (equalsItemStack.getNbt() != null && !equalsItemStack.getNbt().isEmpty())) {
return false;
}
if ((itemStack.getNbt() != null && !itemStack.getNbt().isEmpty() && (equalsItemStack.getNbt() == null || !equalsItemStack.getNbt().isEmpty()))) {
return false;
}
if (itemStack.getNbt() != null && equalsItemStack.getNbt() != null) {
return itemStack.getNbt().equals(equalsItemStack.getNbt());
}
return true;
}
}

View File

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

View File

@ -64,7 +64,7 @@ public class JavaDeclareRecipesTranslator extends PacketTranslator<ServerDeclare
switch (recipe.getType()) {
case CRAFTING_SHAPELESS: {
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
ItemData[][] inputCombinations = combinations(session, shapelessRecipeData.getIngredients());
for (ItemData[] inputs : inputCombinations) {
@ -76,7 +76,7 @@ public class JavaDeclareRecipesTranslator extends PacketTranslator<ServerDeclare
}
case CRAFTING_SHAPED: {
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
ItemData[][] inputCombinations = combinations(session, shapedRecipeData.getIngredients());
for (ItemData[] inputs : inputCombinations) {
@ -103,7 +103,7 @@ public class JavaDeclareRecipesTranslator extends PacketTranslator<ServerDeclare
}
Ingredient ingredient = ingredients[i];
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())));
Set<ItemData> optionSet = new HashSet<>(groupedByIds.size());
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];
for (int i = 0; i < ingredients.length; i++) {
if (ingredients[i].getOptions().length > 0) {
translatedItems[i] = ItemTranslator.translateToBedrock(ingredients[i].getOptions()[0]);
translatedItems[i] = ItemTranslator.translateToBedrock(session, ingredients[i].getOptions()[0]);
} else {
translatedItems[i] = ItemData.AIR;
}

View File

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

View File

@ -25,6 +25,7 @@
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.server.entity.player.ServerPlayerPositionRotationPacket;
import com.nukkitx.math.vector.Vector3f;
@ -95,20 +96,35 @@ public class JavaPlayerPositionRotationTranslator extends PacketTranslator<Serve
session.setSpawned(true);
if (!packet.getRelative().isEmpty()) {
session.setTeleportCache(new TeleportCache(packet.getX(), packet.getY(), packet.getZ(), packet.getTeleportId()));
entity.moveRelative(session, packet.getX(), packet.getY() + EntityType.PLAYER.getOffset() + 0.1f, packet.getZ(), packet.getYaw(), packet.getPitch(), true);
} else {
// Ignore certain move correction packets for smoother movement
// These are never relative
if (packet.getRelative().isEmpty()) {
double xDis = Math.abs(entity.getPosition().getX() - packet.getX());
double yDis = entity.getPosition().getY() - packet.getY();
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) {
session.setTeleportCache(new TeleportCache(packet.getX(), packet.getY(), packet.getZ(), packet.getTeleportId()));
entity.moveAbsolute(session, Vector3f.from(packet.getX(), packet.getY(), packet.getZ()), packet.getYaw(), packet.getPitch(), true, true);
} else {
if (!(xDis > 1.5 || (yDis < 1.45 || yDis > (session.isJumping() ? 4.3 : (session.isSprinting() ? 2.5 : 1.9))) || zDis > 1.5)) {
// Fake confirm the teleport but don't send it to the client
ClientTeleportConfirmPacket teleportConfirmPacket = new ClientTeleportConfirmPacket(packet.getTeleportId());
session.sendDownstreamPacket(teleportConfirmPacket);
return;
}
}
// If coordinates are relative, then add to the existing coordinate
double newX = packet.getX() +
(packet.getRelative().contains(PositionElement.X) ? entity.getPosition().getX() : 0);
double newY = packet.getY() +
(packet.getRelative().contains(PositionElement.Y) ? entity.getPosition().getY() - EntityType.PLAYER.getOffset() : 0);
double newZ = packet.getZ() +
(packet.getRelative().contains(PositionElement.Z) ? entity.getPosition().getZ() : 0);
double newPitch = packet.getPitch() +
(packet.getRelative().contains(PositionElement.PITCH) ? entity.getBedrockRotation().getX() : 0);
double newYaw = packet.getYaw() +
(packet.getRelative().contains(PositionElement.YAW) ? entity.getBedrockRotation().getY() : 0);
session.setTeleportCache(new TeleportCache(newX, newY, newZ, packet.getTeleportId()));
entity.moveAbsolute(session, Vector3f.from(newX, newY, newZ), (float) newYaw, (float) newPitch, true, true);
}
}

View File

@ -26,7 +26,6 @@
package org.geysermc.connector.network.translators.java.window;
import com.github.steveice10.mc.protocol.packet.ingame.server.window.ServerCloseWindowPacket;
import com.nukkitx.protocol.bedrock.packet.ContainerClosePacket;
import org.geysermc.connector.network.session.GeyserSession;
import org.geysermc.connector.network.translators.PacketTranslator;
import org.geysermc.connector.network.translators.Translator;
@ -37,9 +36,7 @@ public class JavaCloseWindowTranslator extends PacketTranslator<ServerCloseWindo
@Override
public void translate(ServerCloseWindowPacket packet, GeyserSession session) {
ContainerClosePacket closePacket = new ContainerClosePacket();
closePacket.setWindowId((byte)packet.getWindowId());
session.sendUpstreamPacket(closePacket);
InventoryUtils.closeWindow(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.utils.InventoryUtils;
import java.util.concurrent.TimeUnit;
@Translator(packet = ServerOpenWindowPacket.class)
public class JavaOpenWindowTranslator extends PacketTranslator<ServerOpenWindowPacket> {
@ -52,10 +50,8 @@ public class JavaOpenWindowTranslator extends PacketTranslator<ServerOpenWindowP
Inventory openInventory = session.getInventoryCache().getOpenInventory();
if (newTranslator == null) {
if (openInventory != null) {
ContainerClosePacket closePacket = new ContainerClosePacket();
closePacket.setWindowId((byte)openInventory.getId());
session.sendUpstreamPacket(closePacket);
InventoryTranslator.INVENTORY_TRANSLATORS.get(openInventory.getWindowType()).closeInventory(session, openInventory);
InventoryUtils.closeWindow(session, openInventory.getId());
InventoryUtils.closeInventory(session, openInventory.getId());
}
ClientCloseWindowPacket closeWindowPacket = new ClientCloseWindowPacket(packet.getWindowId());
session.sendDownstreamPacket(closeWindowPacket);
@ -80,9 +76,8 @@ public class JavaOpenWindowTranslator extends PacketTranslator<ServerOpenWindowP
if (openInventory != null) {
InventoryTranslator openTranslator = InventoryTranslator.INVENTORY_TRANSLATORS.get(openInventory.getWindowType());
if (!openTranslator.getClass().equals(newTranslator.getClass())) {
InventoryUtils.closeWindow(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
public void translate(ServerSetSlotPacket packet, GeyserSession session) {
if (packet.getWindowId() == 255 && packet.getSlot() == -1) { //cursor
if (Objects.equals(session.getInventory().getCursor(), packet.getItem()))
return;
if (session.getCraftSlot() != 0)
return;

View File

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

View File

@ -64,7 +64,7 @@ public class JavaSpawnParticleTranslator extends PacketTranslator<ServerSpawnPar
break;
case ITEM:
ItemStack javaItem = ((ItemParticleData)packet.getParticle().getData()).getItemStack();
ItemData bedrockItem = ItemTranslator.translateToBedrock(javaItem);
ItemData bedrockItem = ItemTranslator.translateToBedrock(session, javaItem);
int id = bedrockItem.getId();
short damage = bedrockItem.getDamage();
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 int JAVA_RUNTIME_COBWEB_ID;
public static final int JAVA_RUNTIME_FURNACE_ID;
public static final int JAVA_RUNTIME_FURNACE_LIT_ID;
public static final int JAVA_RUNTIME_SPAWNER_ID;
private static final int BLOCK_STATE_VERSION = 17760256;
static {
@ -108,6 +113,9 @@ public class BlockTranslator {
int javaRuntimeId = -1;
int bedrockRuntimeId = 0;
int cobwebRuntimeId = -1;
int furnaceRuntimeId = -1;
int furnaceLitRuntimeId = -1;
int spawnerRuntimeId = -1;
Iterator<Map.Entry<String, JsonNode>> blocksIterator = blocks.fields();
while (blocksIterator.hasNext()) {
javaRuntimeId++;
@ -186,6 +194,18 @@ public class BlockTranslator {
}
JAVA_TO_BEDROCK_BLOCK_MAP.put(javaRuntimeId, bedrockRuntimeId);
if (javaId.startsWith("minecraft:furnace[facing=north")) {
if (javaId.contains("lit=true")) {
furnaceLitRuntimeId = javaRuntimeId;
} else {
furnaceRuntimeId = javaRuntimeId;
}
}
if (javaId.startsWith("minecraft:spawner")) {
spawnerRuntimeId = javaRuntimeId;
}
bedrockRuntimeId++;
}
@ -194,6 +214,21 @@ public class BlockTranslator {
}
JAVA_RUNTIME_COBWEB_ID = cobwebRuntimeId;
if (furnaceRuntimeId == -1) {
throw new AssertionError("Unable to find furnace in palette");
}
JAVA_RUNTIME_FURNACE_ID = furnaceRuntimeId;
if (furnaceLitRuntimeId == -1) {
throw new AssertionError("Unable to find lit furnace in palette");
}
JAVA_RUNTIME_FURNACE_LIT_ID = furnaceLitRuntimeId;
if (spawnerRuntimeId == -1) {
throw new AssertionError("Unable to find spawner in palette");
}
JAVA_RUNTIME_SPAWNER_ID = spawnerRuntimeId;
if (waterRuntimeId == -1) {
throw new AssertionError("Unable to find water in palette");
}

View File

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

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

View File

@ -12,29 +12,24 @@ public class BlockEntityUtils {
public static String getBedrockBlockEntityId(String id) {
// These are the only exceptions when it comes to block entity ids
if (id.contains("piston_head"))
return "PistonArm";
if (id.contains("trapped_chest"))
return "Chest";
if (id.contains("EnderChest"))
return "EnderChest";
if (id.contains("enchanting_table")) {
return "EnchantTable";
if (BlockEntityTranslator.BLOCK_ENTITY_TRANSLATIONS.containsKey(id)) {
return BlockEntityTranslator.BLOCK_ENTITY_TRANSLATIONS.get(id);
}
id = id.toLowerCase()
.replace("minecraft:", "")
id = id.replace("minecraft:", "")
.replace("_", " ");
String[] words = id.split(" ");
// Split at every space or capital letter - for the latter, some legacy Java block entity tags are the correct format already
String[] words;
if (!id.toUpperCase().equals(id)) { // Otherwise we get [S, K, U, L, L]
words = id.split("(?=[A-Z])| "); // Split at every space or note or before every capital letter
} else {
words = id.split(" ");
}
for (int i = 0; i < words.length; i++) {
words[i] = words[i].substring(0, 1).toUpperCase() + words[i].substring(1).toLowerCase();
}
id = String.join(" ", words);
return id.replace(" ", "");
return String.join("", words);
}
public static BlockEntityTranslator getBlockEntityTranslator(String name) {

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.world.block.BlockState;
import com.github.steveice10.opennbt.tag.builtin.CompoundTag;
import com.github.steveice10.opennbt.tag.builtin.StringTag;
import com.github.steveice10.opennbt.tag.builtin.Tag;
import com.nukkitx.math.vector.Vector2i;
import com.nukkitx.math.vector.Vector3i;
import com.nukkitx.nbt.CompoundTagBuilder;
@ -137,11 +139,23 @@ public class ChunkUtils {
while (i < blockEntities.length) {
CompoundTag tag = blockEntities[i];
String tagName;
if (!tag.contains("id")) {
GeyserConnector.getInstance().getLogger().debug("Got tag with no id: " + tag.getValue());
tagName = "Empty";
} else {
if (tag.contains("id")) {
tagName = (String) tag.get("id").getValue();
} else {
tagName = "Empty";
// Sometimes legacy tags have their ID be a StringTag with empty value
for (Tag subTag : tag) {
if (subTag instanceof StringTag) {
StringTag stringTag = (StringTag) subTag;
if (stringTag.getValue().equals("")) {
tagName = stringTag.getName();
break;
}
}
}
if (tagName.equals("Empty")) {
GeyserConnector.getInstance().getLogger().debug("Got tag with no id: " + tag.getValue());
}
}
String id = BlockEntityUtils.getBedrockBlockEntityId(tagName);

View File

@ -31,6 +31,7 @@ import com.nukkitx.nbt.CompoundTagBuilder;
import com.nukkitx.nbt.tag.StringTag;
import com.nukkitx.protocol.bedrock.data.ContainerId;
import com.nukkitx.protocol.bedrock.data.ItemData;
import com.nukkitx.protocol.bedrock.packet.ContainerClosePacket;
import com.nukkitx.protocol.bedrock.packet.InventorySlotPacket;
import org.geysermc.common.ChatColor;
import org.geysermc.connector.GeyserConnector;
@ -53,12 +54,18 @@ public class InventoryUtils {
if (translator != null) {
session.getInventoryCache().setOpenInventory(inventory);
translator.prepareInventory(session, inventory);
//Ensure at least half a second passes between closing and opening a new window
//The client will not open the new window if it is still closing the old one
long delay = 500 - (System.currentTimeMillis() - session.getLastWindowCloseTime());
//TODO: find better way to handle double chest delay
if (translator instanceof DoubleChestInventoryTranslator) {
delay = Math.max(delay, 200);
}
if (delay > 0) {
GeyserConnector.getInstance().getGeneralThreadPool().schedule(() -> {
translator.openInventory(session, inventory);
translator.updateInventory(session, inventory);
}, 200, TimeUnit.MILLISECONDS);
}, delay, TimeUnit.MILLISECONDS);
} else {
translator.openInventory(session, inventory);
translator.updateInventory(session, inventory);
@ -69,26 +76,41 @@ public class InventoryUtils {
public static void closeInventory(GeyserSession session, int windowId) {
if (windowId != 0) {
Inventory inventory = session.getInventoryCache().getInventories().get(windowId);
if (inventory != null) {
Inventory openInventory = session.getInventoryCache().getOpenInventory();
session.getInventoryCache().uncacheInventory(windowId);
if (inventory != null && openInventory != null && inventory.getId() == openInventory.getId()) {
InventoryTranslator translator = InventoryTranslator.INVENTORY_TRANSLATORS.get(inventory.getWindowType());
translator.closeInventory(session, inventory);
session.getInventoryCache().uncacheInventory(windowId);
session.getInventoryCache().setOpenInventory(null);
} else {
return;
}
} else {
Inventory inventory = session.getInventory();
InventoryTranslator translator = InventoryTranslator.INVENTORY_TRANSLATORS.get(inventory.getWindowType());
translator.updateInventory(session, inventory);
}
session.setCraftSlot(0);
session.getInventory().setCursor(null);
updateCursor(session);
}
public static void closeWindow(GeyserSession session, int windowId) {
//Spamming close window packets can bug the client
if (System.currentTimeMillis() - session.getLastWindowCloseTime() > 500) {
ContainerClosePacket closePacket = new ContainerClosePacket();
closePacket.setWindowId((byte) windowId);
session.sendUpstreamPacket(closePacket);
session.setLastWindowCloseTime(System.currentTimeMillis());
}
}
public static void updateCursor(GeyserSession session) {
InventorySlotPacket cursorPacket = new InventorySlotPacket();
cursorPacket.setContainerId(ContainerId.CURSOR);
cursorPacket.setSlot(0);
cursorPacket.setItem(ItemTranslator.translateToBedrock(session.getInventory().getCursor()));
cursorPacket.setItem(ItemTranslator.translateToBedrock(session, session.getInventory().getCursor()));
session.sendUpstreamPacket(cursorPacket);
}

View File

@ -27,6 +27,8 @@ package org.geysermc.connector.utils;
public class MathUtils {
public static final double SQRT_OF_TWO = Math.sqrt(2);
/**
* Round the given float to the next whole number
*
@ -37,4 +39,19 @@ public class MathUtils {
int truncated = (int) floatNumber;
return floatNumber > truncated ? truncated + 1 : truncated;
}
/**
* Converts the given object from an int or byte to byte.
* This is used for NBT data that might be either an int
* or byte and bedrock only takes it as an byte
*
* @param value The value to convert
* @return The converted byte
*/
public static Byte convertByte(Object value) {
if (value instanceof Integer) {
return ((Integer) value).byteValue();
}
return (Byte) value;
}
}

10
pom.xml
View File

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