diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md
index 8001187ca..7a1fdba58 100644
--- a/.github/ISSUE_TEMPLATE/bug_report.md
+++ b/.github/ISSUE_TEMPLATE/bug_report.md
@@ -31,10 +31,10 @@ assignees: ''
**Geyser Version**
-
+
**Minecraft: Bedrock Edition Version**
**Additional Context**
-
+
diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml
new file mode 100644
index 000000000..92085a35b
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/config.yml
@@ -0,0 +1,5 @@
+blank_issues_enabled: false
+contact_links:
+ - name: GeyserMC Discord
+ url: http://discord.geysermc.org/
+ about: If your issue seems like it could possibly be an easy fix due to configuration, please hop on our Discord.
diff --git a/.idea/copyright/Geyser.xml b/.idea/copyright/Geyser.xml
new file mode 100644
index 000000000..568fa7b84
--- /dev/null
+++ b/.idea/copyright/Geyser.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/copyright/profiles_settings.xml b/.idea/copyright/profiles_settings.xml
new file mode 100644
index 000000000..f2d5911c9
--- /dev/null
+++ b/.idea/copyright/profiles_settings.xml
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/README.md b/README.md
index 8a0197972..f11b9bfb3 100644
--- a/README.md
+++ b/README.md
@@ -13,7 +13,7 @@ Geyser is a bridge between Minecraft: Bedrock Edition and Minecraft: Java Editio
Geyser is a proxy, bridging the gap between Minecraft: Bedrock Edition and Minecraft: Java Edition servers.
The ultimate goal of this project is to allow Minecraft: Bedrock Edition users to join Minecraft: Java Edition servers as seamlessly as possible. **Please note, this project is still a work in progress and should not be used on production. Expect bugs!**
-### Currently supporting Minecraft Bedrock v1.14.X and Minecraft Java v1.15.2.
+### Currently supporting Minecraft Bedrock v1.14.6(0) and Minecraft Java v1.15.2.
## Setting Up
Take a look [here](https://github.com/GeyserMC/Geyser/wiki#Setup) for how to set up Geyser.
@@ -28,12 +28,14 @@ Take a look [here](https://github.com/GeyserMC/Geyser/wiki#Setup) for how to set
- Donate: https://patreon.com/GeyserMC
## What's Left to be Added/Fixed
-- Inventories ([`inventory`](https://github.com/GeyserMC/Geyser/tree/inventory))
-- Crafting ([`inventory`](https://github.com/GeyserMC/Geyser/tree/inventory))
-- Creative Mode ([`inventory`](https://github.com/GeyserMC/Geyser/tree/inventory))
+- The Following Inventories
+ - [ ] Enchantment Table
+ - [ ] Beacon
+ - [ ] Cartography Table
+ - [ ] Stonecutter
+ - [ ] Villager Trading
- Sounds
- Block Particles
-- Block Entities ([`inventory`](https://github.com/GeyserMC/Geyser/tree/inventory))
- Some Entity Flags
## Compiling
diff --git a/bootstrap/bukkit/src/main/java/org/geysermc/platform/bukkit/GeyserBukkitConfiguration.java b/bootstrap/bukkit/src/main/java/org/geysermc/platform/bukkit/GeyserBukkitConfiguration.java
index 1ddda6528..5b8842b4e 100644
--- a/bootstrap/bukkit/src/main/java/org/geysermc/platform/bukkit/GeyserBukkitConfiguration.java
+++ b/bootstrap/bukkit/src/main/java/org/geysermc/platform/bukkit/GeyserBukkitConfiguration.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2019 GeyserMC. http://geysermc.org
+ * 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
diff --git a/bootstrap/bukkit/src/main/java/org/geysermc/platform/bukkit/GeyserBukkitLogger.java b/bootstrap/bukkit/src/main/java/org/geysermc/platform/bukkit/GeyserBukkitLogger.java
index 05fd2c6c8..454ec9f6e 100644
--- a/bootstrap/bukkit/src/main/java/org/geysermc/platform/bukkit/GeyserBukkitLogger.java
+++ b/bootstrap/bukkit/src/main/java/org/geysermc/platform/bukkit/GeyserBukkitLogger.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2019 GeyserMC. http://geysermc.org
+ * 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
diff --git a/bootstrap/bukkit/src/main/java/org/geysermc/platform/bukkit/GeyserBukkitPlugin.java b/bootstrap/bukkit/src/main/java/org/geysermc/platform/bukkit/GeyserBukkitPlugin.java
index 3c7ae8391..fa58f66c5 100644
--- a/bootstrap/bukkit/src/main/java/org/geysermc/platform/bukkit/GeyserBukkitPlugin.java
+++ b/bootstrap/bukkit/src/main/java/org/geysermc/platform/bukkit/GeyserBukkitPlugin.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2019 GeyserMC. http://geysermc.org
+ * 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
@@ -25,16 +25,20 @@
package org.geysermc.platform.bukkit;
+import org.bukkit.Bukkit;
import org.bukkit.plugin.java.JavaPlugin;
import org.geysermc.common.PlatformType;
-import org.geysermc.connector.GeyserConnector;
import org.geysermc.common.bootstrap.IGeyserBootstrap;
+import org.geysermc.connector.GeyserConnector;
+import org.geysermc.connector.command.CommandManager;
import org.geysermc.platform.bukkit.command.GeyserBukkitCommandExecutor;
+import org.geysermc.platform.bukkit.command.GeyserBukkitCommandManager;
import java.util.UUID;
public class GeyserBukkitPlugin extends JavaPlugin implements IGeyserBootstrap {
+ private GeyserBukkitCommandManager geyserCommandManager;
private GeyserBukkitConfiguration geyserConfig;
private GeyserBukkitLogger geyserLogger;
@@ -50,9 +54,20 @@ public class GeyserBukkitPlugin extends JavaPlugin implements IGeyserBootstrap {
saveConfig();
}
+ // 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")) {
+ getConfig().set("remote.address", Bukkit.getIp());
+ }
+
+ getConfig().set("remote.port", Bukkit.getPort());
+ saveConfig();
+
this.geyserLogger = new GeyserBukkitLogger(getLogger(), geyserConfig.isDebugMode());
this.connector = GeyserConnector.start(PlatformType.BUKKIT, this);
+ this.geyserCommandManager = new GeyserBukkitCommandManager(this, connector);
+
this.getCommand("geyser").setExecutor(new GeyserBukkitCommandExecutor(connector));
}
@@ -70,4 +85,9 @@ public class GeyserBukkitPlugin extends JavaPlugin implements IGeyserBootstrap {
public GeyserBukkitLogger getGeyserLogger() {
return geyserLogger;
}
+
+ @Override
+ public CommandManager getGeyserCommandManager() {
+ return this.geyserCommandManager;
+ }
}
diff --git a/bootstrap/bukkit/src/main/java/org/geysermc/platform/bukkit/command/GeyserBukkitCommandExecutor.java b/bootstrap/bukkit/src/main/java/org/geysermc/platform/bukkit/command/GeyserBukkitCommandExecutor.java
index 84920db72..d2603f7c5 100644
--- a/bootstrap/bukkit/src/main/java/org/geysermc/platform/bukkit/command/GeyserBukkitCommandExecutor.java
+++ b/bootstrap/bukkit/src/main/java/org/geysermc/platform/bukkit/command/GeyserBukkitCommandExecutor.java
@@ -70,6 +70,6 @@ public class GeyserBukkitCommandExecutor implements TabExecutor {
}
private GeyserCommand getCommand(String label) {
- return connector.getCommandMap().getCommands().get(label);
+ return connector.getCommandManager().getCommands().get(label);
}
}
diff --git a/bootstrap/bukkit/src/main/java/org/geysermc/platform/bukkit/command/GeyserBukkitCommandManager.java b/bootstrap/bukkit/src/main/java/org/geysermc/platform/bukkit/command/GeyserBukkitCommandManager.java
new file mode 100644
index 000000000..b826ab1f5
--- /dev/null
+++ b/bootstrap/bukkit/src/main/java/org/geysermc/platform/bukkit/command/GeyserBukkitCommandManager.java
@@ -0,0 +1,64 @@
+/*
+ * 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.platform.bukkit.command;
+
+import org.bukkit.Bukkit;
+import org.bukkit.command.Command;
+import org.bukkit.command.CommandMap;
+import org.geysermc.connector.GeyserConnector;
+import org.geysermc.connector.command.CommandManager;
+import org.geysermc.platform.bukkit.GeyserBukkitPlugin;
+
+import java.lang.reflect.Field;
+
+public class GeyserBukkitCommandManager extends CommandManager {
+
+ private static CommandMap COMMAND_MAP;
+
+ static {
+ try {
+ Field cmdMapField = Bukkit.getServer().getClass().getDeclaredField("commandMap");
+ cmdMapField.setAccessible(true);
+ COMMAND_MAP = (CommandMap) cmdMapField.get(Bukkit.getServer());
+ } catch (NoSuchFieldException | IllegalAccessException ex) {
+ ex.printStackTrace();
+ }
+ }
+
+ private GeyserBukkitPlugin plugin;
+
+ public GeyserBukkitCommandManager(GeyserBukkitPlugin plugin, GeyserConnector connector) {
+ super(connector);
+
+ this.plugin = plugin;
+ }
+
+ @Override
+ public String getDescription(String command) {
+ Command cmd = COMMAND_MAP.getCommand(command.replace("/", ""));
+ return cmd != null ? cmd.getDescription() : "";
+ }
+}
diff --git a/bootstrap/bungeecord/src/main/java/org/geysermc/platform/bungeecord/GeyserBungeeConfiguration.java b/bootstrap/bungeecord/src/main/java/org/geysermc/platform/bungeecord/GeyserBungeeConfiguration.java
index 420f8347e..e0f6a6eff 100644
--- a/bootstrap/bungeecord/src/main/java/org/geysermc/platform/bungeecord/GeyserBungeeConfiguration.java
+++ b/bootstrap/bungeecord/src/main/java/org/geysermc/platform/bungeecord/GeyserBungeeConfiguration.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2019 GeyserMC. http://geysermc.org
+ * 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
diff --git a/bootstrap/bungeecord/src/main/java/org/geysermc/platform/bungeecord/GeyserBungeeLogger.java b/bootstrap/bungeecord/src/main/java/org/geysermc/platform/bungeecord/GeyserBungeeLogger.java
index 2ebc069b1..7aba88bcd 100644
--- a/bootstrap/bungeecord/src/main/java/org/geysermc/platform/bungeecord/GeyserBungeeLogger.java
+++ b/bootstrap/bungeecord/src/main/java/org/geysermc/platform/bungeecord/GeyserBungeeLogger.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2019 GeyserMC. http://geysermc.org
+ * 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
diff --git a/bootstrap/bungeecord/src/main/java/org/geysermc/platform/bungeecord/GeyserBungeePlugin.java b/bootstrap/bungeecord/src/main/java/org/geysermc/platform/bungeecord/GeyserBungeePlugin.java
index d6974dafa..4c5953900 100644
--- a/bootstrap/bungeecord/src/main/java/org/geysermc/platform/bungeecord/GeyserBungeePlugin.java
+++ b/bootstrap/bungeecord/src/main/java/org/geysermc/platform/bungeecord/GeyserBungeePlugin.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2019 GeyserMC. http://geysermc.org
+ * 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
@@ -25,25 +25,29 @@
package org.geysermc.platform.bungeecord;
+import net.md_5.bungee.api.config.ListenerInfo;
import net.md_5.bungee.api.plugin.Plugin;
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.GeyserConnector;
import org.geysermc.common.bootstrap.IGeyserBootstrap;
+import org.geysermc.connector.GeyserConnector;
+import org.geysermc.connector.command.CommandManager;
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;
public class GeyserBungeePlugin extends Plugin implements IGeyserBootstrap {
+ private GeyserBungeeCommandManager geyserCommandManager;
private GeyserBungeeConfiguration geyserConfig;
private GeyserBungeeLogger geyserLogger;
@@ -78,8 +82,31 @@ public class GeyserBungeePlugin extends Plugin implements IGeyserBootstrap {
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];
+
+ InetSocketAddress javaAddr = listener.getHost();
+
+ // 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")) {
+ configuration.set("remote.address", 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) {
@@ -91,6 +118,8 @@ public class GeyserBungeePlugin extends Plugin implements IGeyserBootstrap {
this.geyserLogger = new GeyserBungeeLogger(getLogger(), geyserConfig.isDebugMode());
this.connector = GeyserConnector.start(PlatformType.BUNGEECORD, this);
+ this.geyserCommandManager = new GeyserBungeeCommandManager(connector);
+
this.getProxy().getPluginManager().registerCommand(this, new GeyserBungeeCommandExecutor(connector));
}
@@ -108,4 +137,9 @@ public class GeyserBungeePlugin extends Plugin implements IGeyserBootstrap {
public GeyserBungeeLogger getGeyserLogger() {
return geyserLogger;
}
+
+ @Override
+ public CommandManager getGeyserCommandManager() {
+ return this.geyserCommandManager;
+ }
}
diff --git a/bootstrap/bungeecord/src/main/java/org/geysermc/platform/bungeecord/command/GeyserBungeeCommandExecutor.java b/bootstrap/bungeecord/src/main/java/org/geysermc/platform/bungeecord/command/GeyserBungeeCommandExecutor.java
index 8149d2917..d1c8473bd 100644
--- a/bootstrap/bungeecord/src/main/java/org/geysermc/platform/bungeecord/command/GeyserBungeeCommandExecutor.java
+++ b/bootstrap/bungeecord/src/main/java/org/geysermc/platform/bungeecord/command/GeyserBungeeCommandExecutor.java
@@ -71,6 +71,6 @@ public class GeyserBungeeCommandExecutor extends Command implements TabExecutor
}
private GeyserCommand getCommand(String label) {
- return connector.getCommandMap().getCommands().get(label);
+ return connector.getCommandManager().getCommands().get(label);
}
}
diff --git a/bootstrap/bungeecord/src/main/java/org/geysermc/platform/bungeecord/command/GeyserBungeeCommandManager.java b/bootstrap/bungeecord/src/main/java/org/geysermc/platform/bungeecord/command/GeyserBungeeCommandManager.java
new file mode 100644
index 000000000..bb79c5779
--- /dev/null
+++ b/bootstrap/bungeecord/src/main/java/org/geysermc/platform/bungeecord/command/GeyserBungeeCommandManager.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2019-2020 GeyserMC. http://geysermc.org
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ * @author GeyserMC
+ * @link https://github.com/GeyserMC/Geyser
+ */
+
+package org.geysermc.platform.bungeecord.command;
+
+import org.geysermc.connector.GeyserConnector;
+import org.geysermc.connector.command.CommandManager;
+
+public class GeyserBungeeCommandManager extends CommandManager {
+
+ public GeyserBungeeCommandManager(GeyserConnector connector) {
+ super(connector);
+ }
+
+ @Override
+ public String getDescription(String command) {
+ return ""; // no support for command descriptions in bungee
+ }
+}
diff --git a/bootstrap/sponge/src/main/java/org/geysermc/platform/sponge/GeyserSpongeConfiguration.java b/bootstrap/sponge/src/main/java/org/geysermc/platform/sponge/GeyserSpongeConfiguration.java
index 7b6a89f15..e60a5c3ef 100644
--- a/bootstrap/sponge/src/main/java/org/geysermc/platform/sponge/GeyserSpongeConfiguration.java
+++ b/bootstrap/sponge/src/main/java/org/geysermc/platform/sponge/GeyserSpongeConfiguration.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2019 GeyserMC. http://geysermc.org
+ * 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
@@ -26,17 +26,13 @@
package org.geysermc.platform.sponge;
import lombok.AllArgsConstructor;
-
import ninja.leaping.configurate.ConfigurationNode;
-
import org.geysermc.common.IGeyserConfiguration;
import java.io.File;
import java.nio.file.Path;
import java.nio.file.Paths;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
+import java.util.*;
public class GeyserSpongeConfiguration implements IGeyserConfiguration {
@@ -60,7 +56,8 @@ public class GeyserSpongeConfiguration implements IGeyserConfiguration {
if (node.getNode("userAuths").getValue() == null)
return;
- for (String key : (List) node.getNode("userAuths").getValue()) {
+ List userAuths = new ArrayList(((LinkedHashMap)node.getNode("userAuths").getValue()).keySet());
+ for (String key : userAuths) {
userAuthInfo.put(key, new SpongeUserAuthenticationInfo(key));
}
}
diff --git a/bootstrap/sponge/src/main/java/org/geysermc/platform/sponge/GeyserSpongeLogger.java b/bootstrap/sponge/src/main/java/org/geysermc/platform/sponge/GeyserSpongeLogger.java
index 1df7fbf9b..758ac98d3 100644
--- a/bootstrap/sponge/src/main/java/org/geysermc/platform/sponge/GeyserSpongeLogger.java
+++ b/bootstrap/sponge/src/main/java/org/geysermc/platform/sponge/GeyserSpongeLogger.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2019 GeyserMC. http://geysermc.org
+ * 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
diff --git a/bootstrap/sponge/src/main/java/org/geysermc/platform/sponge/GeyserSpongePlugin.java b/bootstrap/sponge/src/main/java/org/geysermc/platform/sponge/GeyserSpongePlugin.java
index a2074b158..cc26abb9c 100644
--- a/bootstrap/sponge/src/main/java/org/geysermc/platform/sponge/GeyserSpongePlugin.java
+++ b/bootstrap/sponge/src/main/java/org/geysermc/platform/sponge/GeyserSpongePlugin.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2019 GeyserMC. http://geysermc.org
+ * 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
@@ -26,15 +26,16 @@
package org.geysermc.platform.sponge;
import com.google.inject.Inject;
-
+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.common.bootstrap.IGeyserBootstrap;
import org.geysermc.connector.GeyserConnector;
+import org.geysermc.connector.command.CommandManager;
import org.geysermc.connector.utils.FileUtils;
import org.geysermc.platform.sponge.command.GeyserSpongeCommandExecutor;
+import org.geysermc.platform.sponge.command.GeyserSpongeCommandManager;
import org.slf4j.Logger;
import org.spongepowered.api.Sponge;
import org.spongepowered.api.config.ConfigDir;
@@ -45,6 +46,7 @@ import org.spongepowered.api.plugin.Plugin;
import java.io.File;
import java.io.IOException;
+import java.net.InetSocketAddress;
import java.util.UUID;
@Plugin(id = "geyser", name = GeyserConnector.NAME + "-Sponge", version = GeyserConnector.VERSION, url = "https://geysermc.org", authors = "GeyserMC")
@@ -57,6 +59,7 @@ public class GeyserSpongePlugin implements IGeyserBootstrap {
@ConfigDir(sharedRoot = false)
private File configDir;
+ private GeyserSpongeCommandManager geyserCommandManager;
private GeyserSpongeConfiguration geyserConfig;
private GeyserSpongeLogger geyserLogger;
@@ -76,16 +79,34 @@ public class GeyserSpongePlugin implements IGeyserBootstrap {
}
ConfigurationLoader loader = YAMLConfigurationLoader.builder().setPath(configFile.toPath()).build();
+ ConfigurationNode config;
try {
- this.geyserConfig = new GeyserSpongeConfiguration(configDir, loader.load());
+ config = loader.load();
+ this.geyserConfig = new GeyserSpongeConfiguration(configDir, config);
} catch (IOException ex) {
logger.warn("Failed to load config.yml!");
ex.printStackTrace();
return;
}
+ ConfigurationNode serverIP = config.getNode("remote").getNode("address");
+ ConfigurationNode serverPort = config.getNode("remote").getNode("port");
+
+ if (Sponge.getServer().getBoundAddress().isPresent()) {
+ InetSocketAddress javaAddr = Sponge.getServer().getBoundAddress().get();
+
+ // 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")) {
+ serverIP.setValue("127.0.0.1");
+ }
+
+ serverPort.setValue(javaAddr.getPort());
+ }
+
this.geyserLogger = new GeyserSpongeLogger(logger, geyserConfig.isDebugMode());
this.connector = GeyserConnector.start(PlatformType.SPONGE, this);
+ this.geyserCommandManager = new GeyserSpongeCommandManager(Sponge.getCommandManager(), connector);
Sponge.getCommandManager().register(this, new GeyserSpongeCommandExecutor(connector), "geyser");
}
@@ -105,6 +126,11 @@ public class GeyserSpongePlugin implements IGeyserBootstrap {
return geyserLogger;
}
+ @Override
+ public CommandManager getGeyserCommandManager() {
+ return this.geyserCommandManager;
+ }
+
@Listener
public void onServerStart(GameStartedServerEvent event) {
onEnable();
diff --git a/bootstrap/sponge/src/main/java/org/geysermc/platform/sponge/command/GeyserSpongeCommandExecutor.java b/bootstrap/sponge/src/main/java/org/geysermc/platform/sponge/command/GeyserSpongeCommandExecutor.java
index c68ff4e47..91cb59b0f 100644
--- a/bootstrap/sponge/src/main/java/org/geysermc/platform/sponge/command/GeyserSpongeCommandExecutor.java
+++ b/bootstrap/sponge/src/main/java/org/geysermc/platform/sponge/command/GeyserSpongeCommandExecutor.java
@@ -95,6 +95,6 @@ public class GeyserSpongeCommandExecutor implements CommandCallable {
}
private GeyserCommand getCommand(String label) {
- return connector.getCommandMap().getCommands().get(label);
+ return connector.getCommandManager().getCommands().get(label);
}
}
diff --git a/bootstrap/sponge/src/main/java/org/geysermc/platform/sponge/command/GeyserSpongeCommandManager.java b/bootstrap/sponge/src/main/java/org/geysermc/platform/sponge/command/GeyserSpongeCommandManager.java
new file mode 100644
index 000000000..c36511a4c
--- /dev/null
+++ b/bootstrap/sponge/src/main/java/org/geysermc/platform/sponge/command/GeyserSpongeCommandManager.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright (c) 2019-2020 GeyserMC. http://geysermc.org
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ * @author GeyserMC
+ * @link https://github.com/GeyserMC/Geyser
+ */
+
+package org.geysermc.platform.sponge.command;
+
+import org.geysermc.connector.GeyserConnector;
+import org.geysermc.connector.command.CommandManager;
+import org.spongepowered.api.Sponge;
+import org.spongepowered.api.command.CommandMapping;
+import org.spongepowered.api.text.Text;
+
+public class GeyserSpongeCommandManager extends CommandManager {
+
+ private org.spongepowered.api.command.CommandManager handle;
+
+ public GeyserSpongeCommandManager(org.spongepowered.api.command.CommandManager handle, GeyserConnector connector) {
+ super(connector);
+
+ this.handle = handle;
+ }
+
+ @Override
+ public String getDescription(String command) {
+ return handle.get(command).map(CommandMapping::getCallable).map(callable -> callable.getShortDescription(Sponge.getServer().getConsole()).orElse(Text.EMPTY)).orElse(Text.EMPTY).toPlain();
+ }
+}
diff --git a/bootstrap/standalone/src/main/java/org/geysermc/platform/standalone/GeyserBootstrap.java b/bootstrap/standalone/src/main/java/org/geysermc/platform/standalone/GeyserBootstrap.java
index 284d94072..20b7f5ea7 100644
--- a/bootstrap/standalone/src/main/java/org/geysermc/platform/standalone/GeyserBootstrap.java
+++ b/bootstrap/standalone/src/main/java/org/geysermc/platform/standalone/GeyserBootstrap.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2019 GeyserMC. http://geysermc.org
+ * 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
@@ -25,18 +25,21 @@
package org.geysermc.platform.standalone;
+import org.geysermc.common.PlatformType;
+import org.geysermc.common.bootstrap.IGeyserBootstrap;
+import org.geysermc.connector.GeyserConnector;
+import org.geysermc.connector.command.CommandManager;
+import org.geysermc.connector.utils.FileUtils;
+import org.geysermc.platform.standalone.command.GeyserCommandManager;
+import org.geysermc.platform.standalone.console.GeyserLogger;
+
import java.io.File;
import java.io.IOException;
import java.util.UUID;
-import org.geysermc.common.PlatformType;
-import org.geysermc.common.bootstrap.IGeyserBootstrap;
-import org.geysermc.connector.GeyserConnector;
-import org.geysermc.connector.utils.FileUtils;
-import org.geysermc.platform.standalone.console.GeyserLogger;
-
public class GeyserBootstrap implements IGeyserBootstrap {
-
+
+ private GeyserCommandManager geyserCommandManager;
private GeyserConfiguration geyserConfig;
private GeyserLogger geyserLogger;
@@ -49,7 +52,7 @@ public class GeyserBootstrap implements IGeyserBootstrap {
@Override
public void onEnable() {
geyserLogger = new GeyserLogger();
-
+
LoopbackUtil.checkLoopback(geyserLogger);
try {
@@ -61,6 +64,7 @@ public class GeyserBootstrap implements IGeyserBootstrap {
}
connector = GeyserConnector.start(PlatformType.STANDALONE, this);
+ geyserCommandManager = new GeyserCommandManager(connector);
geyserLogger.start();
}
@@ -79,4 +83,9 @@ public class GeyserBootstrap implements IGeyserBootstrap {
public GeyserLogger getGeyserLogger() {
return geyserLogger;
}
+
+ @Override
+ public CommandManager getGeyserCommandManager() {
+ return geyserCommandManager;
+ }
}
diff --git a/bootstrap/standalone/src/main/java/org/geysermc/platform/standalone/GeyserConfiguration.java b/bootstrap/standalone/src/main/java/org/geysermc/platform/standalone/GeyserConfiguration.java
index a1362c7a7..afd6179e8 100644
--- a/bootstrap/standalone/src/main/java/org/geysermc/platform/standalone/GeyserConfiguration.java
+++ b/bootstrap/standalone/src/main/java/org/geysermc/platform/standalone/GeyserConfiguration.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2019 GeyserMC. http://geysermc.org
+ * 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
diff --git a/bootstrap/standalone/src/main/java/org/geysermc/platform/standalone/command/GeyserCommandManager.java b/bootstrap/standalone/src/main/java/org/geysermc/platform/standalone/command/GeyserCommandManager.java
new file mode 100644
index 000000000..41bf61c12
--- /dev/null
+++ b/bootstrap/standalone/src/main/java/org/geysermc/platform/standalone/command/GeyserCommandManager.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2019-2020 GeyserMC. http://geysermc.org
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ * @author GeyserMC
+ * @link https://github.com/GeyserMC/Geyser
+ */
+
+package org.geysermc.platform.standalone.command;
+
+import org.geysermc.connector.GeyserConnector;
+import org.geysermc.connector.command.CommandManager;
+
+public class GeyserCommandManager extends CommandManager {
+
+ public GeyserCommandManager(GeyserConnector connector) {
+ super(connector);
+ }
+
+ @Override
+ public String getDescription(String command) {
+ return ""; // this is not sent over the protocol, so we return none
+ }
+}
diff --git a/bootstrap/standalone/src/main/java/org/geysermc/platform/standalone/console/GeyserLogger.java b/bootstrap/standalone/src/main/java/org/geysermc/platform/standalone/console/GeyserLogger.java
index ac21215cb..631de9052 100644
--- a/bootstrap/standalone/src/main/java/org/geysermc/platform/standalone/console/GeyserLogger.java
+++ b/bootstrap/standalone/src/main/java/org/geysermc/platform/standalone/console/GeyserLogger.java
@@ -47,7 +47,7 @@ public class GeyserLogger extends SimpleTerminalConsole implements IGeyserLogger
@Override
protected void runCommand(String line) {
- GeyserConnector.getInstance().getCommandMap().runCommand(this, line);
+ GeyserConnector.getInstance().getCommandManager().runCommand(this, line);
}
@Override
diff --git a/bootstrap/velocity/src/main/java/org/geysermc/platform/velocity/GeyserVelocityConfiguration.java b/bootstrap/velocity/src/main/java/org/geysermc/platform/velocity/GeyserVelocityConfiguration.java
index 920c65379..03f7cbeeb 100644
--- a/bootstrap/velocity/src/main/java/org/geysermc/platform/velocity/GeyserVelocityConfiguration.java
+++ b/bootstrap/velocity/src/main/java/org/geysermc/platform/velocity/GeyserVelocityConfiguration.java
@@ -27,9 +27,8 @@ package org.geysermc.platform.velocity;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;
-
import lombok.Getter;
-
+import lombok.Setter;
import org.geysermc.common.IGeyserConfiguration;
import java.nio.file.Path;
@@ -86,7 +85,10 @@ public class GeyserVelocityConfiguration implements IGeyserConfiguration {
@Getter
public static class RemoteConfiguration implements IRemoteConfiguration {
+ @Setter
private String address;
+
+ @Setter
private int port;
private String motd1;
diff --git a/bootstrap/velocity/src/main/java/org/geysermc/platform/velocity/GeyserVelocityPlugin.java b/bootstrap/velocity/src/main/java/org/geysermc/platform/velocity/GeyserVelocityPlugin.java
index 5ab411f3e..38378f01c 100644
--- a/bootstrap/velocity/src/main/java/org/geysermc/platform/velocity/GeyserVelocityPlugin.java
+++ b/bootstrap/velocity/src/main/java/org/geysermc/platform/velocity/GeyserVelocityPlugin.java
@@ -33,15 +33,18 @@ import com.velocitypowered.api.event.proxy.ProxyInitializeEvent;
import com.velocitypowered.api.event.proxy.ProxyShutdownEvent;
import com.velocitypowered.api.plugin.Plugin;
+import com.velocitypowered.api.proxy.ProxyServer;
import org.geysermc.common.PlatformType;
import org.geysermc.common.bootstrap.IGeyserBootstrap;
import org.geysermc.connector.GeyserConnector;
import org.geysermc.connector.utils.FileUtils;
import org.geysermc.platform.velocity.command.GeyserVelocityCommandExecutor;
+import org.geysermc.platform.velocity.command.GeyserVelocityCommandManager;
import org.slf4j.Logger;
import java.io.File;
import java.io.IOException;
+import java.net.InetSocketAddress;
import java.util.UUID;
@Plugin(id = "geyser", name = GeyserConnector.NAME + "-Velocity", version = GeyserConnector.VERSION, url = "https://geysermc.org", authors = "GeyserMC")
@@ -50,9 +53,13 @@ public class GeyserVelocityPlugin implements IGeyserBootstrap {
@Inject
private Logger logger;
+ @Inject
+ private ProxyServer server;
+
@Inject
private CommandManager commandManager;
+ private GeyserVelocityCommandManager geyserCommandManager;
private GeyserVelocityConfiguration geyserConfig;
private GeyserVelocityLogger geyserLogger;
@@ -71,9 +78,20 @@ public class GeyserVelocityPlugin implements IGeyserBootstrap {
ex.printStackTrace();
}
+ InetSocketAddress javaAddr = server.getBoundAddress();
+
+ // 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")) {
+ geyserConfig.getRemote().setAddress(javaAddr.getHostString());
+ }
+
+ geyserConfig.getRemote().setPort(javaAddr.getPort());
+
this.geyserLogger = new GeyserVelocityLogger(logger, geyserConfig.isDebugMode());
this.connector = GeyserConnector.start(PlatformType.VELOCITY, this);
+ this.geyserCommandManager = new GeyserVelocityCommandManager(connector);
this.commandManager.register(new GeyserVelocityCommandExecutor(connector), "geyser");
}
@@ -92,6 +110,11 @@ public class GeyserVelocityPlugin implements IGeyserBootstrap {
return geyserLogger;
}
+ @Override
+ public org.geysermc.connector.command.CommandManager getGeyserCommandManager() {
+ return this.geyserCommandManager;
+ }
+
@Subscribe
public void onInit(ProxyInitializeEvent event) {
onEnable();
diff --git a/bootstrap/velocity/src/main/java/org/geysermc/platform/velocity/command/GeyserVelocityCommandExecutor.java b/bootstrap/velocity/src/main/java/org/geysermc/platform/velocity/command/GeyserVelocityCommandExecutor.java
index 39a0af97c..940c52244 100644
--- a/bootstrap/velocity/src/main/java/org/geysermc/platform/velocity/command/GeyserVelocityCommandExecutor.java
+++ b/bootstrap/velocity/src/main/java/org/geysermc/platform/velocity/command/GeyserVelocityCommandExecutor.java
@@ -57,6 +57,6 @@ public class GeyserVelocityCommandExecutor implements Command {
}
private GeyserCommand getCommand(String label) {
- return connector.getCommandMap().getCommands().get(label);
+ return connector.getCommandManager().getCommands().get(label);
}
}
diff --git a/bootstrap/velocity/src/main/java/org/geysermc/platform/velocity/command/GeyserVelocityCommandManager.java b/bootstrap/velocity/src/main/java/org/geysermc/platform/velocity/command/GeyserVelocityCommandManager.java
new file mode 100644
index 000000000..76655d0ac
--- /dev/null
+++ b/bootstrap/velocity/src/main/java/org/geysermc/platform/velocity/command/GeyserVelocityCommandManager.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2019-2020 GeyserMC. http://geysermc.org
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ * @author GeyserMC
+ * @link https://github.com/GeyserMC/Geyser
+ */
+
+package org.geysermc.platform.velocity.command;
+
+import org.geysermc.connector.GeyserConnector;
+import org.geysermc.connector.command.CommandManager;
+
+public class GeyserVelocityCommandManager extends CommandManager {
+
+ public GeyserVelocityCommandManager(GeyserConnector connector) {
+ super(connector);
+ }
+
+ @Override
+ public String getDescription(String command) {
+ return ""; // no support for command descriptions in velocity
+ }
+}
diff --git a/common/src/main/java/org/geysermc/common/AuthType.java b/common/src/main/java/org/geysermc/common/AuthType.java
index bec515600..8edbc4d55 100644
--- a/common/src/main/java/org/geysermc/common/AuthType.java
+++ b/common/src/main/java/org/geysermc/common/AuthType.java
@@ -14,6 +14,13 @@ public enum AuthType {
return id < VALUES.length ? VALUES[id] : OFFLINE;
}
+ /**
+ * Convert the AuthType string (from config) to the enum, OFFLINE on fail
+ *
+ * @param name AuthType string
+ *
+ * @return The converted AuthType
+ */
public static AuthType getByName(String name) {
String upperCase = name.toUpperCase();
for (AuthType type : VALUES) {
diff --git a/common/src/main/java/org/geysermc/common/ChatColor.java b/common/src/main/java/org/geysermc/common/ChatColor.java
index 20632770c..8868b063c 100644
--- a/common/src/main/java/org/geysermc/common/ChatColor.java
+++ b/common/src/main/java/org/geysermc/common/ChatColor.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2019 GeyserMC. http://geysermc.org
+ * 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
@@ -51,6 +51,13 @@ public class ChatColor {
public static final String ITALIC = ESCAPE + "o";
public static final String RESET = ESCAPE + "r";
+ /**
+ * Convert chat colour codes to terminal colours
+ *
+ * @param string The text to replace colours for
+ *
+ * @return A string ready for terminal printing
+ */
public static String toANSI(String string) {
string = string.replace(BOLD, (char) 0x1b + "[1m");
string = string.replace(OBFUSCATED, (char) 0x1b + "[5m");
@@ -81,6 +88,13 @@ public class ChatColor {
return message.replace(color, ESCAPE);
}
+ /**
+ * Remove all colour formatting tags from a message
+ *
+ * @param message Message to remove colour tags from
+ *
+ * @return The sanitised message
+ */
public static String stripColors(String message) {
return message = message.replaceAll("(&([a-fk-or0-9]))","").replaceAll("(ยง([a-fk-or0-9]))","").replaceAll("s/\\x1b\\[[0-9;]*[a-zA-Z]//g","");
}
diff --git a/common/src/main/java/org/geysermc/common/IGeyserConfiguration.java b/common/src/main/java/org/geysermc/common/IGeyserConfiguration.java
index 41534457e..774e3394d 100644
--- a/common/src/main/java/org/geysermc/common/IGeyserConfiguration.java
+++ b/common/src/main/java/org/geysermc/common/IGeyserConfiguration.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2019 GeyserMC. http://geysermc.org
+ * 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
diff --git a/common/src/main/java/org/geysermc/common/bootstrap/IGeyserBootstrap.java b/common/src/main/java/org/geysermc/common/bootstrap/IGeyserBootstrap.java
index cf4b3991e..5df61953f 100644
--- a/common/src/main/java/org/geysermc/common/bootstrap/IGeyserBootstrap.java
+++ b/common/src/main/java/org/geysermc/common/bootstrap/IGeyserBootstrap.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2019 GeyserMC. http://geysermc.org
+ * 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
@@ -26,15 +26,39 @@
package org.geysermc.common.bootstrap;
import org.geysermc.common.IGeyserConfiguration;
+import org.geysermc.common.command.ICommandManager;
import org.geysermc.common.logger.IGeyserLogger;
public interface IGeyserBootstrap {
+ /**
+ * Called when the GeyserBootstrap is enabled
+ */
void onEnable();
+ /**
+ * Called when the GeyserBootstrap is disabled
+ */
void onDisable();
+ /**
+ * Returns the current GeyserConfig
+ *
+ * @return The current GeyserConfig
+ */
IGeyserConfiguration getGeyserConfig();
+ /**
+ * Returns the current GeyserLogger
+ *
+ * @return The current GeyserLogger
+ */
IGeyserLogger getGeyserLogger();
+
+ /**
+ * Returns the current GeyserCommandManager
+ *
+ * @return The current GeyserCommandManager
+ */
+ ICommandManager getGeyserCommandManager();
}
diff --git a/common/src/main/java/org/geysermc/common/command/ICommandManager.java b/common/src/main/java/org/geysermc/common/command/ICommandManager.java
new file mode 100644
index 000000000..f46dfafcd
--- /dev/null
+++ b/common/src/main/java/org/geysermc/common/command/ICommandManager.java
@@ -0,0 +1,38 @@
+/*
+ * 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.common.command;
+
+public interface ICommandManager {
+
+ /**
+ * Returns the description of the given command
+ *
+ * @param command Command to get the description for
+ *
+ * @return Command description
+ */
+ String getDescription(String command);
+}
diff --git a/common/src/main/java/org/geysermc/common/logger/IGeyserLogger.java b/common/src/main/java/org/geysermc/common/logger/IGeyserLogger.java
index b4d143e43..ad571ebbb 100644
--- a/common/src/main/java/org/geysermc/common/logger/IGeyserLogger.java
+++ b/common/src/main/java/org/geysermc/common/logger/IGeyserLogger.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2019 GeyserMC. http://geysermc.org
+ * 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
diff --git a/common/src/main/java/org/geysermc/common/window/CustomFormBuilder.java b/common/src/main/java/org/geysermc/common/window/CustomFormBuilder.java
index fb6794120..004b00a96 100644
--- a/common/src/main/java/org/geysermc/common/window/CustomFormBuilder.java
+++ b/common/src/main/java/org/geysermc/common/window/CustomFormBuilder.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2019 GeyserMC. http://geysermc.org
+ * 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
diff --git a/common/src/main/java/org/geysermc/common/window/CustomFormWindow.java b/common/src/main/java/org/geysermc/common/window/CustomFormWindow.java
index a6bc72781..efc71ae8d 100644
--- a/common/src/main/java/org/geysermc/common/window/CustomFormWindow.java
+++ b/common/src/main/java/org/geysermc/common/window/CustomFormWindow.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2019 GeyserMC. http://geysermc.org
+ * 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
@@ -25,8 +25,9 @@
package org.geysermc.common.window;
-import com.google.gson.Gson;
-import com.google.gson.reflect.TypeToken;
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.core.type.TypeReference;
+import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.Getter;
import lombok.Setter;
import org.geysermc.common.window.button.FormImage;
@@ -34,6 +35,7 @@ import org.geysermc.common.window.component.*;
import org.geysermc.common.window.response.CustomFormResponse;
import org.geysermc.common.window.response.FormResponseData;
+import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
@@ -77,7 +79,11 @@ public class CustomFormWindow extends FormWindow {
}
public String getJSONData() {
- String toModify = new Gson().toJson(this);
+ String toModify = "";
+ try {
+ toModify = new ObjectMapper().writeValueAsString(this);
+ } catch (JsonProcessingException e) { }
+
//We need to replace this due to Java not supporting declaring class field 'default'
return toModify.replace("defaultOptionIndex", "default")
.replace("defaultText", "default")
@@ -100,7 +106,11 @@ public class CustomFormWindow extends FormWindow {
Map responses = new HashMap();
Map labelResponses = new HashMap();
- List componentResponses = new Gson().fromJson(data, new TypeToken>() { }.getType());
+ List componentResponses = new ArrayList<>();
+ try {
+ componentResponses = new ObjectMapper().readValue(data, new TypeReference>(){});
+ } catch (IOException e) { }
+
for (String response : componentResponses) {
if (i >= content.size()) {
break;
diff --git a/common/src/main/java/org/geysermc/common/window/FormWindow.java b/common/src/main/java/org/geysermc/common/window/FormWindow.java
index 968d9349d..c3cc4258b 100644
--- a/common/src/main/java/org/geysermc/common/window/FormWindow.java
+++ b/common/src/main/java/org/geysermc/common/window/FormWindow.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2019 GeyserMC. http://geysermc.org
+ * 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
@@ -25,6 +25,7 @@
package org.geysermc.common.window;
+import com.fasterxml.jackson.annotation.JsonIgnore;
import lombok.Getter;
import lombok.Setter;
import org.geysermc.common.window.response.FormResponse;
@@ -50,6 +51,7 @@ public abstract class FormWindow {
this.response = response;
}
+ @JsonIgnore
public abstract String getJSONData();
public abstract void setResponse(String response);
diff --git a/common/src/main/java/org/geysermc/common/window/ModalFormWindow.java b/common/src/main/java/org/geysermc/common/window/ModalFormWindow.java
index 934660395..bfeafa1b0 100644
--- a/common/src/main/java/org/geysermc/common/window/ModalFormWindow.java
+++ b/common/src/main/java/org/geysermc/common/window/ModalFormWindow.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2019 GeyserMC. http://geysermc.org
+ * 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
@@ -25,7 +25,8 @@
package org.geysermc.common.window;
-import com.google.gson.Gson;
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.Getter;
import lombok.Setter;
import org.geysermc.common.window.response.ModalFormResponse;
@@ -59,7 +60,11 @@ public class ModalFormWindow extends FormWindow {
@Override
public String getJSONData() {
- return new Gson().toJson(this);
+ try {
+ return new ObjectMapper().writeValueAsString(this);
+ } catch (JsonProcessingException e) {
+ return "";
+ }
}
public void setResponse(String data) {
diff --git a/common/src/main/java/org/geysermc/common/window/SimpleFormWindow.java b/common/src/main/java/org/geysermc/common/window/SimpleFormWindow.java
index cc31f061a..7c1acc26f 100644
--- a/common/src/main/java/org/geysermc/common/window/SimpleFormWindow.java
+++ b/common/src/main/java/org/geysermc/common/window/SimpleFormWindow.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2019 GeyserMC. http://geysermc.org
+ * 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
@@ -25,7 +25,8 @@
package org.geysermc.common.window;
-import com.google.gson.Gson;
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.Getter;
import lombok.Setter;
import org.geysermc.common.window.button.FormButton;
@@ -63,7 +64,11 @@ public class SimpleFormWindow extends FormWindow {
@Override
public String getJSONData() {
- return new Gson().toJson(this);
+ try {
+ return new ObjectMapper().writeValueAsString(this);
+ } catch (JsonProcessingException e) {
+ return "";
+ }
}
public void setResponse(String data) {
diff --git a/common/src/main/java/org/geysermc/common/window/button/FormButton.java b/common/src/main/java/org/geysermc/common/window/button/FormButton.java
index 4f710d483..6daa2feae 100644
--- a/common/src/main/java/org/geysermc/common/window/button/FormButton.java
+++ b/common/src/main/java/org/geysermc/common/window/button/FormButton.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2019 GeyserMC. http://geysermc.org
+ * 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
@@ -37,6 +37,10 @@ public class FormButton {
@Getter
private FormImage image;
+ public FormButton(String text) {
+ this.text = text;
+ }
+
public FormButton(String text, FormImage image) {
this.text = text;
diff --git a/common/src/main/java/org/geysermc/common/window/button/FormImage.java b/common/src/main/java/org/geysermc/common/window/button/FormImage.java
index a3f83a0c5..b700b046b 100644
--- a/common/src/main/java/org/geysermc/common/window/button/FormImage.java
+++ b/common/src/main/java/org/geysermc/common/window/button/FormImage.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2019 GeyserMC. http://geysermc.org
+ * 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
diff --git a/common/src/main/java/org/geysermc/common/window/component/DropdownComponent.java b/common/src/main/java/org/geysermc/common/window/component/DropdownComponent.java
index 8abe72ff8..4dac6b043 100644
--- a/common/src/main/java/org/geysermc/common/window/component/DropdownComponent.java
+++ b/common/src/main/java/org/geysermc/common/window/component/DropdownComponent.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2019 GeyserMC. http://geysermc.org
+ * 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
diff --git a/common/src/main/java/org/geysermc/common/window/component/FormComponent.java b/common/src/main/java/org/geysermc/common/window/component/FormComponent.java
index fb5b9d18c..5a56ae0cc 100644
--- a/common/src/main/java/org/geysermc/common/window/component/FormComponent.java
+++ b/common/src/main/java/org/geysermc/common/window/component/FormComponent.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2019 GeyserMC. http://geysermc.org
+ * 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
diff --git a/common/src/main/java/org/geysermc/common/window/component/InputComponent.java b/common/src/main/java/org/geysermc/common/window/component/InputComponent.java
index 53ec2b5eb..fad6a0fed 100644
--- a/common/src/main/java/org/geysermc/common/window/component/InputComponent.java
+++ b/common/src/main/java/org/geysermc/common/window/component/InputComponent.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2019 GeyserMC. http://geysermc.org
+ * 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
diff --git a/common/src/main/java/org/geysermc/common/window/component/LabelComponent.java b/common/src/main/java/org/geysermc/common/window/component/LabelComponent.java
index 7d2aaa420..a76b313fa 100644
--- a/common/src/main/java/org/geysermc/common/window/component/LabelComponent.java
+++ b/common/src/main/java/org/geysermc/common/window/component/LabelComponent.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2019 GeyserMC. http://geysermc.org
+ * 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
diff --git a/common/src/main/java/org/geysermc/common/window/component/SliderComponent.java b/common/src/main/java/org/geysermc/common/window/component/SliderComponent.java
index fd82b3e26..a7a78362e 100644
--- a/common/src/main/java/org/geysermc/common/window/component/SliderComponent.java
+++ b/common/src/main/java/org/geysermc/common/window/component/SliderComponent.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2019 GeyserMC. http://geysermc.org
+ * 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
diff --git a/common/src/main/java/org/geysermc/common/window/component/StepSliderComponent.java b/common/src/main/java/org/geysermc/common/window/component/StepSliderComponent.java
index b61e416d0..8f128d1a4 100644
--- a/common/src/main/java/org/geysermc/common/window/component/StepSliderComponent.java
+++ b/common/src/main/java/org/geysermc/common/window/component/StepSliderComponent.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2019 GeyserMC. http://geysermc.org
+ * 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
diff --git a/common/src/main/java/org/geysermc/common/window/component/ToggleComponent.java b/common/src/main/java/org/geysermc/common/window/component/ToggleComponent.java
index 614ecf8fb..50a5c631a 100644
--- a/common/src/main/java/org/geysermc/common/window/component/ToggleComponent.java
+++ b/common/src/main/java/org/geysermc/common/window/component/ToggleComponent.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2019 GeyserMC. http://geysermc.org
+ * 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
diff --git a/common/src/main/java/org/geysermc/common/window/response/CustomFormResponse.java b/common/src/main/java/org/geysermc/common/window/response/CustomFormResponse.java
index 36b2922f9..6cdd70978 100644
--- a/common/src/main/java/org/geysermc/common/window/response/CustomFormResponse.java
+++ b/common/src/main/java/org/geysermc/common/window/response/CustomFormResponse.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2019 GeyserMC. http://geysermc.org
+ * 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
diff --git a/common/src/main/java/org/geysermc/common/window/response/FormResponse.java b/common/src/main/java/org/geysermc/common/window/response/FormResponse.java
index 58c8c8e87..2be646837 100644
--- a/common/src/main/java/org/geysermc/common/window/response/FormResponse.java
+++ b/common/src/main/java/org/geysermc/common/window/response/FormResponse.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2019 GeyserMC. http://geysermc.org
+ * 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
diff --git a/common/src/main/java/org/geysermc/common/window/response/FormResponseData.java b/common/src/main/java/org/geysermc/common/window/response/FormResponseData.java
index 826aba208..fd40be0fb 100644
--- a/common/src/main/java/org/geysermc/common/window/response/FormResponseData.java
+++ b/common/src/main/java/org/geysermc/common/window/response/FormResponseData.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2019 GeyserMC. http://geysermc.org
+ * 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
diff --git a/common/src/main/java/org/geysermc/common/window/response/ModalFormResponse.java b/common/src/main/java/org/geysermc/common/window/response/ModalFormResponse.java
index 844c9a5b8..e1a14039d 100644
--- a/common/src/main/java/org/geysermc/common/window/response/ModalFormResponse.java
+++ b/common/src/main/java/org/geysermc/common/window/response/ModalFormResponse.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2019 GeyserMC. http://geysermc.org
+ * 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
diff --git a/common/src/main/java/org/geysermc/common/window/response/SimpleFormResponse.java b/common/src/main/java/org/geysermc/common/window/response/SimpleFormResponse.java
index 5a53d0423..e80d58e78 100644
--- a/common/src/main/java/org/geysermc/common/window/response/SimpleFormResponse.java
+++ b/common/src/main/java/org/geysermc/common/window/response/SimpleFormResponse.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2019 GeyserMC. http://geysermc.org
+ * 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
diff --git a/connector/pom.xml b/connector/pom.xml
index 25ef50730..9de533b6a 100644
--- a/connector/pom.xml
+++ b/connector/pom.xml
@@ -30,16 +30,10 @@
2.9.8compile
-
- io.sentry
- sentry
- 1.7.0
- compile
- com.nukkitx.protocol
- bedrock-v389
- 2.5.4
+ bedrock-v390
+ 2.5.6-SNAPSHOTcompile
@@ -72,10 +66,34 @@
8.3.1compile
+
+ com.nukkitx.fastutil
+ fastutil-int-double-maps
+ 8.3.1
+ compile
+
+
+ com.nukkitx.fastutil
+ fastutil-int-boolean-maps
+ 8.3.1
+ compile
+
+
+ com.nukkitx.fastutil
+ fastutil-object-int-maps
+ 8.3.1
+ compile
+
+
+ com.nukkitx.fastutil
+ fastutil-object-byte-maps
+ 8.3.1
+ compile
+ com.github.steveice10opennbt
- 1.3-SNAPSHOT
+ 1.4-SNAPSHOTcompile
@@ -93,7 +111,7 @@
com.github.steveice10mcauthlib
- 1.1-SNAPSHOT
+ 1.3-SNAPSHOTcompile
@@ -121,5 +139,23 @@
reflections0.9.12
+
+ net.kyori
+ text-api
+ 3.0.3
+ compile
+
+
+ net.kyori
+ text-serializer-gson
+ 3.0.3
+ compile
+
+
+ net.kyori
+ text-serializer-legacy
+ 3.0.3
+ compile
+
diff --git a/connector/src/main/java/org/geysermc/connector/GeyserConnector.java b/connector/src/main/java/org/geysermc/connector/GeyserConnector.java
index b839c1af7..101b66cf4 100644
--- a/connector/src/main/java/org/geysermc/connector/GeyserConnector.java
+++ b/connector/src/main/java/org/geysermc/connector/GeyserConnector.java
@@ -27,15 +27,16 @@ package org.geysermc.connector;
import com.nukkitx.protocol.bedrock.BedrockPacketCodec;
import com.nukkitx.protocol.bedrock.BedrockServer;
-import com.nukkitx.protocol.bedrock.v389.Bedrock_v389;
+import com.nukkitx.protocol.bedrock.v390.Bedrock_v390;
import lombok.Getter;
import org.geysermc.common.AuthType;
+import org.geysermc.common.IGeyserConfiguration;
import org.geysermc.common.PlatformType;
import org.geysermc.common.bootstrap.IGeyserBootstrap;
import org.geysermc.common.logger.IGeyserLogger;
-import org.geysermc.connector.command.GeyserCommandMap;
+import org.geysermc.connector.command.CommandManager;
import org.geysermc.connector.metrics.Metrics;
import org.geysermc.connector.network.ConnectorServerEventHandler;
import org.geysermc.connector.network.remote.RemoteServer;
@@ -44,14 +45,12 @@ import org.geysermc.connector.network.translators.Translators;
import org.geysermc.connector.thread.PingPassthroughThread;
import org.geysermc.connector.utils.ResourcePack;
import org.geysermc.connector.utils.Toolbox;
-import org.geysermc.common.IGeyserConfiguration;
import java.net.InetSocketAddress;
import java.text.DecimalFormat;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
-import java.util.UUID;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
@@ -59,7 +58,7 @@ import java.util.concurrent.TimeUnit;
@Getter
public class GeyserConnector {
- public static final BedrockPacketCodec BEDROCK_PACKET_CODEC = Bedrock_v389.V389_CODEC;
+ public static final BedrockPacketCodec BEDROCK_PACKET_CODEC = Bedrock_v390.V390_CODEC;
public static final String NAME = "Geyser";
public static final String VERSION = "1.0-SNAPSHOT";
@@ -71,8 +70,6 @@ public class GeyserConnector {
private RemoteServer remoteServer;
private AuthType authType;
- private GeyserCommandMap commandMap;
-
private boolean shuttingDown = false;
private final ScheduledExecutorService generalThreadPool;
@@ -110,7 +107,6 @@ public class GeyserConnector {
Toolbox.init();
ResourcePack.loadPacks();
- commandMap = new GeyserCommandMap(this);
remoteServer = new RemoteServer(config.getRemote().getAddress(), config.getRemote().getPort());
authType = AuthType.getByName(config.getRemote().getAuthType());
@@ -184,8 +180,7 @@ public class GeyserConnector {
players.clear();
remoteServer = null;
authType = null;
- commandMap.getCommands().clear();
- commandMap = null;
+ this.getCommandManager().getCommands().clear();
bootstrap.getGeyserLogger().info("Geyser shutdown successfully.");
}
@@ -215,6 +210,10 @@ public class GeyserConnector {
return bootstrap.getGeyserConfig();
}
+ public CommandManager getCommandManager() {
+ return (CommandManager) bootstrap.getGeyserCommandManager();
+ }
+
public static GeyserConnector getInstance() {
return instance;
}
diff --git a/connector/src/main/java/org/geysermc/connector/command/GeyserCommandMap.java b/connector/src/main/java/org/geysermc/connector/command/CommandManager.java
similarity index 86%
rename from connector/src/main/java/org/geysermc/connector/command/GeyserCommandMap.java
rename to connector/src/main/java/org/geysermc/connector/command/CommandManager.java
index c4bf488ee..7b1b4d69b 100644
--- a/connector/src/main/java/org/geysermc/connector/command/GeyserCommandMap.java
+++ b/connector/src/main/java/org/geysermc/connector/command/CommandManager.java
@@ -26,28 +26,29 @@
package org.geysermc.connector.command;
import lombok.Getter;
+import org.geysermc.common.command.ICommandManager;
import org.geysermc.connector.GeyserConnector;
-import org.geysermc.connector.command.defaults.HelpCommand;
-import org.geysermc.connector.command.defaults.ReloadCommand;
-import org.geysermc.connector.command.defaults.StopCommand;
+import org.geysermc.connector.command.defaults.*;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
-public class GeyserCommandMap {
+public abstract class CommandManager implements ICommandManager {
@Getter
private final Map commands = Collections.synchronizedMap(new HashMap<>());
private GeyserConnector connector;
- public GeyserCommandMap(GeyserConnector connector) {
+ public CommandManager(GeyserConnector connector) {
this.connector = connector;
registerCommand(new HelpCommand(connector, "help", "Shows help for all registered commands.", "geyser.command.help"));
+ registerCommand(new ListCommand(connector, "list", "List all players connected through Geyser.", "geyser.command.list"));
registerCommand(new ReloadCommand(connector, "reload", "Reloads the Geyser configurations. Kicks all players when used!", "geyser.command.reload"));
registerCommand(new StopCommand(connector, "stop", "Shuts down Geyser.", "geyser.command.stop"));
+ registerCommand(new OffhandCommand(connector, "offhand", "Puts an items in your offhand.", "geyser.command.offhand"));
}
public void registerCommand(GeyserCommand command) {
diff --git a/connector/src/main/java/org/geysermc/connector/command/defaults/HelpCommand.java b/connector/src/main/java/org/geysermc/connector/command/defaults/HelpCommand.java
index cdfb612a6..6acb7822b 100644
--- a/connector/src/main/java/org/geysermc/connector/command/defaults/HelpCommand.java
+++ b/connector/src/main/java/org/geysermc/connector/command/defaults/HelpCommand.java
@@ -49,8 +49,8 @@ public class HelpCommand extends GeyserCommand {
@Override
public void execute(CommandSender sender, String[] args) {
sender.sendMessage("---- Showing Help For: Geyser (Page 1/1) ----");
- Map cmds = connector.getCommandMap().getCommands();
- List commands = connector.getCommandMap().getCommands().keySet().stream().sorted().collect(Collectors.toList());
+ Map cmds = connector.getCommandManager().getCommands();
+ List commands = connector.getCommandManager().getCommands().keySet().stream().sorted().collect(Collectors.toList());
commands.forEach(cmd -> sender.sendMessage(ChatColor.YELLOW + "/geyser " + cmd + ChatColor.WHITE + ": " + cmds.get(cmd).getDescription()));
}
}
diff --git a/connector/src/main/java/org/geysermc/connector/command/defaults/ListCommand.java b/connector/src/main/java/org/geysermc/connector/command/defaults/ListCommand.java
new file mode 100644
index 000000000..21fa42535
--- /dev/null
+++ b/connector/src/main/java/org/geysermc/connector/command/defaults/ListCommand.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright (c) 2019-2020 GeyserMC. http://geysermc.org
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ * @author GeyserMC
+ * @link https://github.com/GeyserMC/Geyser
+ */
+
+package org.geysermc.connector.command.defaults;
+
+import org.geysermc.common.ChatColor;
+import org.geysermc.connector.GeyserConnector;
+import org.geysermc.connector.command.CommandSender;
+import org.geysermc.connector.command.GeyserCommand;
+import org.geysermc.connector.network.session.GeyserSession;
+
+import java.util.stream.Collectors;
+
+public class ListCommand extends GeyserCommand {
+
+ private GeyserConnector connector;
+
+ public ListCommand(GeyserConnector connector, String name, String description, String permission) {
+ super(name, description, permission);
+
+ this.connector = connector;
+ }
+
+ @Override
+ public void execute(CommandSender sender, String[] args) {
+ sender.sendMessage(ChatColor.YELLOW + "Online Players (" + connector.getPlayers().size() + "): " + ChatColor.WHITE + connector.getPlayers().values().stream().map(GeyserSession::getName).collect(Collectors.joining(" ")));
+ }
+}
diff --git a/connector/src/main/java/org/geysermc/connector/command/defaults/OffhandCommand.java b/connector/src/main/java/org/geysermc/connector/command/defaults/OffhandCommand.java
new file mode 100644
index 000000000..d181d07e2
--- /dev/null
+++ b/connector/src/main/java/org/geysermc/connector/command/defaults/OffhandCommand.java
@@ -0,0 +1,61 @@
+/*
+ * 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.command.defaults;
+
+import com.github.steveice10.mc.protocol.data.game.entity.metadata.Position;
+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.packet.ingame.client.player.ClientPlayerActionPacket;
+import org.geysermc.connector.GeyserConnector;
+import org.geysermc.connector.command.CommandSender;
+import org.geysermc.connector.command.GeyserCommand;
+import org.geysermc.connector.network.session.GeyserSession;
+
+public class OffhandCommand extends GeyserCommand {
+
+ private GeyserConnector connector;
+
+ public OffhandCommand(GeyserConnector connector, String name, String description, String permission) {
+ super(name, description, permission);
+
+ this.connector = connector;
+ }
+
+ @Override
+ public void execute(CommandSender sender, String[] args) {
+ if (sender.isConsole()) {
+ return;
+ }
+
+ // Make sure the sender is a Bedrock edition client
+ if (sender instanceof GeyserSession) {
+ GeyserSession session = (GeyserSession) sender;
+ ClientPlayerActionPacket releaseItemPacket = new ClientPlayerActionPacket(PlayerAction.SWAP_HANDS, new Position(0,0,0),
+ BlockFace.DOWN);
+ session.getDownstream().getSession().send(releaseItemPacket);
+ }
+ }
+}
diff --git a/connector/src/main/java/org/geysermc/connector/command/defaults/ReloadCommand.java b/connector/src/main/java/org/geysermc/connector/command/defaults/ReloadCommand.java
index bd8444c79..c38a0c23d 100644
--- a/connector/src/main/java/org/geysermc/connector/command/defaults/ReloadCommand.java
+++ b/connector/src/main/java/org/geysermc/connector/command/defaults/ReloadCommand.java
@@ -48,7 +48,7 @@ public class ReloadCommand extends GeyserCommand {
}
sender.sendMessage(ChatColor.YELLOW + "Reloading Geyser configurations... all connected bedrock clients will be kicked.");
for (GeyserSession session : connector.getPlayers().values()) {
- session.getUpstream().disconnect("Geyser has been reloaded... sorry for the inconvenience!");
+ session.disconnect("Geyser has been reloaded... sorry for the inconvenience!");
}
connector.reload();
}
diff --git a/connector/src/main/java/org/geysermc/connector/entity/EnderCrystalEntity.java b/connector/src/main/java/org/geysermc/connector/entity/EnderCrystalEntity.java
new file mode 100644
index 000000000..b3ce22783
--- /dev/null
+++ b/connector/src/main/java/org/geysermc/connector/entity/EnderCrystalEntity.java
@@ -0,0 +1,82 @@
+/*
+ * Copyright (c) 2019-2020 GeyserMC. http://geysermc.org
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ * @author GeyserMC
+ * @link https://github.com/GeyserMC/Geyser
+ */
+
+package org.geysermc.connector.entity;
+
+import com.github.steveice10.mc.protocol.data.game.entity.metadata.EntityMetadata;
+import com.github.steveice10.mc.protocol.data.game.entity.metadata.Position;
+import com.nukkitx.math.vector.Vector3f;
+import com.nukkitx.math.vector.Vector3i;
+import com.nukkitx.protocol.bedrock.data.EntityData;
+import com.nukkitx.protocol.bedrock.data.EntityFlag;
+import com.nukkitx.protocol.bedrock.packet.AddEntityPacket;
+import org.geysermc.connector.entity.type.EntityType;
+import org.geysermc.connector.network.session.GeyserSession;
+
+public class EnderCrystalEntity extends Entity {
+
+ public EnderCrystalEntity(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) {
+ // Show beam
+ // Usually performed client-side on Bedrock except for Ender Dragon respawn event
+ if (entityMetadata.getId() == 7) {
+ if (entityMetadata.getValue() instanceof Position) {
+ Position pos = (Position) entityMetadata.getValue();
+ metadata.put(EntityData.BLOCK_TARGET, Vector3i.from(pos.getX(), pos.getY(), pos.getZ()));
+ } else {
+ metadata.put(EntityData.BLOCK_TARGET, Vector3i.ZERO);
+ }
+ }
+ // There is a base located on the ender crystal
+ if (entityMetadata.getId() == 8) {
+ metadata.getFlags().setFlag(EntityFlag.SHOW_BOTTOM, (boolean) entityMetadata.getValue());
+ }
+ super.updateBedrockMetadata(entityMetadata, session);
+ }
+
+ @Override
+ public void spawnEntity(GeyserSession session) {
+ AddEntityPacket addEntityPacket = new AddEntityPacket();
+ // Not end crystal but ender crystal
+ addEntityPacket.setIdentifier("minecraft:ender_crystal");
+ addEntityPacket.setRuntimeEntityId(geyserId);
+ addEntityPacket.setUniqueEntityId(geyserId);
+ addEntityPacket.setPosition(position);
+ addEntityPacket.setMotion(motion);
+ addEntityPacket.setRotation(getBedrockRotation());
+ addEntityPacket.setEntityType(entityType.getType());
+ addEntityPacket.getMetadata().putAll(metadata);
+
+ valid = true;
+ session.getUpstream().sendPacket(addEntityPacket);
+
+ session.getConnector().getLogger().debug("Spawned entity " + entityType + " at location " + position + " with id " + geyserId + " (java id " + entityId + ")");
+ }
+}
diff --git a/connector/src/main/java/org/geysermc/connector/entity/Entity.java b/connector/src/main/java/org/geysermc/connector/entity/Entity.java
index 177d0d7a9..5596ca7ae 100644
--- a/connector/src/main/java/org/geysermc/connector/entity/Entity.java
+++ b/connector/src/main/java/org/geysermc/connector/entity/Entity.java
@@ -27,12 +27,15 @@ package org.geysermc.connector.entity;
import com.github.steveice10.mc.protocol.data.game.entity.metadata.EntityMetadata;
import com.github.steveice10.mc.protocol.data.game.entity.metadata.MetadataType;
+import com.github.steveice10.mc.protocol.data.game.entity.metadata.Position;
+import com.github.steveice10.mc.protocol.data.game.entity.player.Hand;
+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.message.TextMessage;
+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;
-import com.nukkitx.protocol.bedrock.data.EntityData;
-import com.nukkitx.protocol.bedrock.data.EntityDataMap;
-import com.nukkitx.protocol.bedrock.data.EntityFlag;
-import com.nukkitx.protocol.bedrock.data.EntityFlags;
+import com.nukkitx.protocol.bedrock.data.*;
import com.nukkitx.protocol.bedrock.packet.*;
import it.unimi.dsi.fastutil.longs.LongOpenHashSet;
@@ -45,6 +48,7 @@ import org.geysermc.connector.entity.attribute.Attribute;
import org.geysermc.connector.entity.attribute.AttributeType;
import org.geysermc.connector.entity.type.EntityType;
import org.geysermc.connector.network.session.GeyserSession;
+import org.geysermc.connector.network.translators.item.ItemTranslator;
import org.geysermc.connector.utils.AttributeUtils;
import org.geysermc.connector.utils.MessageUtils;
@@ -120,6 +124,9 @@ public class Entity {
}
/**
+ * Despawns the entity
+ *
+ * @param session The GeyserSession
* @return can be deleted
*/
public boolean despawnEntity(GeyserSession session) {
@@ -196,6 +203,28 @@ public class Entity {
metadata.getFlags().setFlag(EntityFlag.SPRINTING, (xd & 0x08) == 0x08);
metadata.getFlags().setFlag(EntityFlag.SWIMMING, (xd & 0x10) == 0x10);
metadata.getFlags().setFlag(EntityFlag.GLIDING, (xd & 0x80) == 0x80);
+
+ // Shield code
+ if (session.getPlayerEntity().getEntityId() == entityId && metadata.getFlags().getFlag(EntityFlag.SNEAKING)) {
+ if ((session.getInventory().getItemInHand() != null && session.getInventory().getItemInHand().getId() == ItemTranslator.SHIELD) ||
+ (session.getInventoryCache().getPlayerInventory().getItem(45) != null && session.getInventoryCache().getPlayerInventory().getItem(45).getId() == ItemTranslator.SHIELD)) {
+ ClientPlayerUseItemPacket useItemPacket;
+ metadata.getFlags().setFlag(EntityFlag.BLOCKING, true);
+ if (session.getInventory().getItemInHand() != null && session.getInventory().getItemInHand().getId() == ItemTranslator.SHIELD) {
+ useItemPacket = new ClientPlayerUseItemPacket(Hand.MAIN_HAND);
+ }
+ // Else we just assume it's the offhand, to simplify logic and to assure the packet gets sent
+ else {
+ useItemPacket = new ClientPlayerUseItemPacket(Hand.OFF_HAND);
+ }
+ session.getDownstream().getSession().send(useItemPacket);
+ }
+ } else if (session.getPlayerEntity().getEntityId() == entityId && !metadata.getFlags().getFlag(EntityFlag.SNEAKING) && metadata.getFlags().getFlag(EntityFlag.BLOCKING)) {
+ metadata.getFlags().setFlag(EntityFlag.BLOCKING, false);
+ metadata.getFlags().setFlag(EntityFlag.DISABLE_BLOCKING, true);
+ ClientPlayerActionPacket releaseItemPacket = new ClientPlayerActionPacket(PlayerAction.RELEASE_USE_ITEM, new Position(0,0,0), BlockFace.DOWN);
+ session.getDownstream().getSession().send(releaseItemPacket);
+ }
// metadata.getFlags().setFlag(EntityFlag.INVISIBLE, (xd & 0x20) == 0x20);
if ((xd & 0x20) == 0x20)
metadata.put(EntityData.SCALE, 0.0f);
@@ -218,6 +247,12 @@ public class Entity {
case 5: // no gravity
metadata.getFlags().setFlag(EntityFlag.HAS_GRAVITY, !(boolean) entityMetadata.getValue());
break;
+ case 7: // blocking
+ if (entityMetadata.getType() == MetadataType.BYTE) {
+ byte xd = (byte) entityMetadata.getValue();
+ metadata.getFlags().setFlag(EntityFlag.BLOCKING, (xd & 0x01) == 0x01);
+ }
+ break;
}
updateBedrockMetadata(session);
@@ -234,6 +269,7 @@ public class Entity {
/**
* x = Pitch, y = HeadYaw, z = Yaw
+ * @return the bedrock rotation
*/
public Vector3f getBedrockRotation() {
return Vector3f.from(rotation.getY(), rotation.getZ(), rotation.getX());
diff --git a/connector/src/main/java/org/geysermc/connector/entity/FishingHookEntity.java b/connector/src/main/java/org/geysermc/connector/entity/FishingHookEntity.java
new file mode 100644
index 000000000..3a77292fe
--- /dev/null
+++ b/connector/src/main/java/org/geysermc/connector/entity/FishingHookEntity.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright (c) 2019-2020 GeyserMC. http://geysermc.org
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ * @author GeyserMC
+ * @link https://github.com/GeyserMC/Geyser
+ */
+
+package org.geysermc.connector.entity;
+
+import com.nukkitx.math.vector.Vector3f;
+import com.nukkitx.protocol.bedrock.packet.AddEntityPacket;
+import org.geysermc.connector.entity.type.EntityType;
+import org.geysermc.connector.network.session.GeyserSession;
+
+public class FishingHookEntity extends Entity {
+ public FishingHookEntity(long entityId, long geyserId, EntityType entityType, Vector3f position, Vector3f motion, Vector3f rotation) {
+ super(entityId, geyserId, entityType, position, motion, rotation);
+ }
+
+ @Override
+ public void spawnEntity(GeyserSession session) {
+ AddEntityPacket addEntityPacket = new AddEntityPacket();
+ // Different ID in Bedrock
+ addEntityPacket.setIdentifier("minecraft:fishing_hook");
+ addEntityPacket.setRuntimeEntityId(geyserId);
+ addEntityPacket.setUniqueEntityId(geyserId);
+ addEntityPacket.setPosition(position);
+ addEntityPacket.setMotion(motion);
+ addEntityPacket.setRotation(getBedrockRotation());
+ addEntityPacket.setEntityType(entityType.getType());
+ addEntityPacket.getMetadata().putAll(metadata);
+
+ valid = true;
+ session.getUpstream().sendPacket(addEntityPacket);
+
+ session.getConnector().getLogger().debug("Spawned entity " + entityType + " at location " + position + " with id " + geyserId + " (java id " + entityId + ")");
+ }
+}
diff --git a/connector/src/main/java/org/geysermc/connector/entity/ItemEntity.java b/connector/src/main/java/org/geysermc/connector/entity/ItemEntity.java
index a80323076..a28b563b4 100644
--- a/connector/src/main/java/org/geysermc/connector/entity/ItemEntity.java
+++ b/connector/src/main/java/org/geysermc/connector/entity/ItemEntity.java
@@ -49,7 +49,7 @@ public class ItemEntity extends Entity {
itemPacket.setUniqueEntityId(geyserId);
itemPacket.setFromFishing(false);
itemPacket.getMetadata().putAll(metadata);
- itemPacket.setItemInHand(Translators.getItemTranslator().translateToBedrock((ItemStack) entityMetadata.getValue()));
+ itemPacket.setItemInHand(Translators.getItemTranslator().translateToBedrock(session, (ItemStack) entityMetadata.getValue()));
session.getUpstream().sendPacket(itemPacket);
}
diff --git a/connector/src/main/java/org/geysermc/connector/entity/PlayerEntity.java b/connector/src/main/java/org/geysermc/connector/entity/PlayerEntity.java
index fb48871b3..d0ac2aeb8 100644
--- a/connector/src/main/java/org/geysermc/connector/entity/PlayerEntity.java
+++ b/connector/src/main/java/org/geysermc/connector/entity/PlayerEntity.java
@@ -44,6 +44,7 @@ import org.geysermc.connector.entity.type.EntityType;
import org.geysermc.connector.network.session.GeyserSession;
import org.geysermc.connector.scoreboard.Team;
import org.geysermc.connector.utils.MessageUtils;
+import org.geysermc.connector.network.session.cache.EntityEffectCache;
import org.geysermc.connector.utils.SkinUtils;
import java.util.UUID;
@@ -55,6 +56,7 @@ public class PlayerEntity extends LivingEntity {
private String username;
private long lastSkinUpdate = -1;
private boolean playerList = true;
+ private final EntityEffectCache effectCache;
public PlayerEntity(GameProfile gameProfile, long entityId, long geyserId, Vector3f position, Vector3f motion, Vector3f rotation) {
super(entityId, geyserId, EntityType.PLAYER, position, motion, rotation);
@@ -62,6 +64,7 @@ public class PlayerEntity extends LivingEntity {
profile = gameProfile;
uuid = gameProfile.getId();
username = gameProfile.getName();
+ effectCache = new EntityEffectCache();
if (geyserId == 1) valid = true;
}
diff --git a/connector/src/main/java/org/geysermc/connector/entity/living/AbstractFishEntity.java b/connector/src/main/java/org/geysermc/connector/entity/living/AbstractFishEntity.java
index 92acb1179..54443825a 100644
--- a/connector/src/main/java/org/geysermc/connector/entity/living/AbstractFishEntity.java
+++ b/connector/src/main/java/org/geysermc/connector/entity/living/AbstractFishEntity.java
@@ -25,12 +25,28 @@
package org.geysermc.connector.entity.living;
+import com.github.steveice10.mc.protocol.data.game.entity.metadata.EntityMetadata;
import com.nukkitx.math.vector.Vector3f;
+import com.nukkitx.protocol.bedrock.data.EntityData;
+import com.nukkitx.protocol.bedrock.data.EntityFlag;
import org.geysermc.connector.entity.type.EntityType;
+import org.geysermc.connector.network.session.GeyserSession;
public class AbstractFishEntity extends WaterEntity {
public AbstractFishEntity(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) {
+ metadata.getFlags().setFlag(EntityFlag.CAN_SWIM, true);
+ metadata.getFlags().setFlag(EntityFlag.BREATHING, true);
+ metadata.getFlags().setFlag(EntityFlag.CAN_CLIMB, false);
+ metadata.getFlags().setFlag(EntityFlag.HAS_GRAVITY, false);
+
+ metadata.put(EntityData.AIR, (short) 400);
+
+ super.updateBedrockMetadata(entityMetadata, session);
+ }
}
diff --git a/connector/src/main/java/org/geysermc/connector/entity/living/AgeableEntity.java b/connector/src/main/java/org/geysermc/connector/entity/living/AgeableEntity.java
index a9c60e457..634f06743 100644
--- a/connector/src/main/java/org/geysermc/connector/entity/living/AgeableEntity.java
+++ b/connector/src/main/java/org/geysermc/connector/entity/living/AgeableEntity.java
@@ -26,7 +26,6 @@
package org.geysermc.connector.entity.living;
import com.github.steveice10.mc.protocol.data.game.entity.metadata.EntityMetadata;
-import com.github.steveice10.mc.protocol.data.game.entity.metadata.MetadataType;
import com.nukkitx.math.vector.Vector3f;
import com.nukkitx.protocol.bedrock.data.EntityData;
import com.nukkitx.protocol.bedrock.data.EntityFlag;
@@ -43,10 +42,8 @@ public class AgeableEntity extends CreatureEntity {
public void updateBedrockMetadata(EntityMetadata entityMetadata, GeyserSession session) {
if (entityMetadata.getId() == 15) {
boolean isBaby = (boolean) entityMetadata.getValue();
- if (isBaby) {
- metadata.put(EntityData.SCALE, .55f);
- metadata.getFlags().setFlag(EntityFlag.BABY, true);
- }
+ metadata.put(EntityData.SCALE, isBaby ? .55f : 1f);
+ metadata.getFlags().setFlag(EntityFlag.BABY, isBaby);
}
super.updateBedrockMetadata(entityMetadata, session);
diff --git a/connector/src/main/java/org/geysermc/connector/entity/living/animal/PufferFishEntity.java b/connector/src/main/java/org/geysermc/connector/entity/living/animal/PufferFishEntity.java
new file mode 100644
index 000000000..d5503dc06
--- /dev/null
+++ b/connector/src/main/java/org/geysermc/connector/entity/living/animal/PufferFishEntity.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright (c) 2019-2020 GeyserMC. http://geysermc.org
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ * @author GeyserMC
+ * @link https://github.com/GeyserMC/Geyser
+ */
+
+package org.geysermc.connector.entity.living.animal;
+
+import com.github.steveice10.mc.protocol.data.game.entity.metadata.EntityMetadata;
+import com.nukkitx.math.vector.Vector3f;
+import com.nukkitx.protocol.bedrock.data.EntityData;
+import org.geysermc.connector.entity.living.AbstractFishEntity;
+import org.geysermc.connector.entity.type.EntityType;
+import org.geysermc.connector.network.session.GeyserSession;
+
+public class PufferFishEntity extends AbstractFishEntity {
+
+ public PufferFishEntity(long entityId, long geyserId, EntityType entityType, Vector3f position, Vector3f motion, Vector3f rotation) {
+ super(entityId, geyserId, entityType, position, motion, rotation);
+ }
+
+ @Override
+ public void updateBedrockMetadata(EntityMetadata entityMetadata, GeyserSession session) {
+ if (entityMetadata.getId() == 16) {
+ // Transfers correctly but doesn't apply on the client
+ int puffsize = (int) entityMetadata.getValue();
+ metadata.put(EntityData.PUFFERFISH_SIZE, puffsize);
+ metadata.put(EntityData.VARIANT, puffsize);
+ }
+ super.updateBedrockMetadata(entityMetadata, session);
+ }
+}
diff --git a/connector/src/main/java/org/geysermc/connector/entity/living/animal/TropicalFishEntity.java b/connector/src/main/java/org/geysermc/connector/entity/living/animal/TropicalFishEntity.java
new file mode 100644
index 000000000..3ded4cbfe
--- /dev/null
+++ b/connector/src/main/java/org/geysermc/connector/entity/living/animal/TropicalFishEntity.java
@@ -0,0 +1,94 @@
+/*
+ * Copyright (c) 2019-2020 GeyserMC. http://geysermc.org
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ * @author GeyserMC
+ * @link https://github.com/GeyserMC/Geyser
+ */
+
+package org.geysermc.connector.entity.living.animal;
+
+import com.github.steveice10.mc.protocol.data.game.entity.metadata.EntityMetadata;
+import com.nukkitx.math.vector.Vector3f;
+import com.nukkitx.protocol.bedrock.data.EntityData;
+import com.nukkitx.protocol.bedrock.packet.AddEntityPacket;
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+import org.geysermc.connector.entity.living.AbstractFishEntity;
+import org.geysermc.connector.entity.type.EntityType;
+import org.geysermc.connector.network.session.GeyserSession;
+
+public class TropicalFishEntity extends AbstractFishEntity {
+
+ public TropicalFishEntity(long entityId, long geyserId, EntityType entityType, Vector3f position, Vector3f motion, Vector3f rotation) {
+ super(entityId, geyserId, entityType, position, motion, rotation);
+ }
+
+ @Override
+ public void updateBedrockMetadata(EntityMetadata entityMetadata, GeyserSession session) {
+ if (entityMetadata.getId() == 16) {
+ TropicalFishVariant variant = TropicalFishVariant.fromVariantNumber((int) entityMetadata.getValue());
+
+ metadata.put(EntityData.VARIANT, variant.getShape()); // Shape 0-1
+ metadata.put(EntityData.MARK_VARIANT, variant.getPattern()); // Pattern 0-5
+ metadata.put(EntityData.COLOR, variant.getBaseColor()); // Base color 0-15
+ metadata.put(EntityData.COLOR_2, variant.getPatternColor()); // Pattern color 0-15
+ }
+ super.updateBedrockMetadata(entityMetadata, session);
+ }
+
+ @Override
+ public void spawnEntity(GeyserSession session) {
+ AddEntityPacket addEntityPacket = new AddEntityPacket();
+ addEntityPacket.setIdentifier("minecraft:tropicalfish");
+ addEntityPacket.setRuntimeEntityId(geyserId);
+ addEntityPacket.setUniqueEntityId(geyserId);
+ addEntityPacket.setPosition(position);
+ addEntityPacket.setMotion(motion);
+ addEntityPacket.setRotation(getBedrockRotation());
+ addEntityPacket.setEntityType(entityType.getType());
+ addEntityPacket.getMetadata().putAll(metadata);
+
+ valid = true;
+ session.getUpstream().sendPacket(addEntityPacket);
+
+ session.getConnector().getLogger().debug("Spawned entity " + entityType + " at location " + position + " with id " + geyserId + " (java id " + entityId + ")");
+ }
+
+ @Getter
+ @AllArgsConstructor
+ private static class TropicalFishVariant {
+ private int shape;
+ private int pattern;
+ private byte baseColor;
+ private byte patternColor;
+
+ /**
+ * Convert the variant number from Java into separate values
+ *
+ * @param varNumber Variant number from Java edition
+ *
+ * @return The variant converted into TropicalFishVariant
+ */
+ public static TropicalFishVariant fromVariantNumber(int varNumber) {
+ return new TropicalFishVariant((varNumber & 0xFF), ((varNumber >> 8) & 0xFF), (byte) ((varNumber >> 16) & 0xFF), (byte) ((varNumber >> 24) & 0xFF));
+ }
+ }
+}
diff --git a/connector/src/main/java/org/geysermc/connector/entity/living/animal/horse/HorseEntity.java b/connector/src/main/java/org/geysermc/connector/entity/living/animal/horse/HorseEntity.java
index 95d91ab80..b6a50520a 100644
--- a/connector/src/main/java/org/geysermc/connector/entity/living/animal/horse/HorseEntity.java
+++ b/connector/src/main/java/org/geysermc/connector/entity/living/animal/horse/HorseEntity.java
@@ -45,4 +45,4 @@ public class HorseEntity extends AbstractHorseEntity {
super.updateBedrockMetadata(entityMetadata, session);
}
-}
+}
\ No newline at end of file
diff --git a/connector/src/main/java/org/geysermc/connector/entity/living/animal/tameable/WolfEntity.java b/connector/src/main/java/org/geysermc/connector/entity/living/animal/tameable/WolfEntity.java
index 0ac49d55d..118262dcd 100644
--- a/connector/src/main/java/org/geysermc/connector/entity/living/animal/tameable/WolfEntity.java
+++ b/connector/src/main/java/org/geysermc/connector/entity/living/animal/tameable/WolfEntity.java
@@ -44,9 +44,19 @@ public class WolfEntity extends TameableEntity {
if (entityMetadata.getId() == 18) {
metadata.getFlags().setFlag(EntityFlag.INTERESTED, (boolean) entityMetadata.getValue());
}
+
+ //Reset wolf color
+ if (entityMetadata.getId() == 16) {
+ byte xd = (byte) entityMetadata.getValue();
+ boolean angry = (xd & 0x02) == 0x02;
+ if (angry) {
+ metadata.put(EntityData.COLOR, (byte) 0);
+ }
+ }
+
// Wolf collar color
// Relies on EntityData.OWNER_EID being set in TameableEntity.java
- if (entityMetadata.getId() == 19) {
+ if (entityMetadata.getId() == 19 && !metadata.getFlags().getFlag(EntityFlag.ANGRY)) {
metadata.put(EntityData.COLOR, (byte) (int) entityMetadata.getValue());
}
super.updateBedrockMetadata(entityMetadata, session);
diff --git a/connector/src/main/java/org/geysermc/connector/entity/living/monster/EnderDragonEntity.java b/connector/src/main/java/org/geysermc/connector/entity/living/monster/EnderDragonEntity.java
new file mode 100644
index 000000000..b07377389
--- /dev/null
+++ b/connector/src/main/java/org/geysermc/connector/entity/living/monster/EnderDragonEntity.java
@@ -0,0 +1,86 @@
+/*
+ * Copyright (c) 2019-2020 GeyserMC. http://geysermc.org
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ * @author GeyserMC
+ * @link https://github.com/GeyserMC/Geyser
+ */
+
+package org.geysermc.connector.entity.living.monster;
+
+import com.github.steveice10.mc.protocol.data.game.entity.metadata.EntityMetadata;
+import com.nukkitx.math.vector.Vector3f;
+import com.nukkitx.protocol.bedrock.data.Attribute;
+import com.nukkitx.protocol.bedrock.data.EntityEventType;
+import com.nukkitx.protocol.bedrock.data.EntityFlag;
+import com.nukkitx.protocol.bedrock.packet.AddEntityPacket;
+import com.nukkitx.protocol.bedrock.packet.EntityEventPacket;
+import org.geysermc.connector.entity.living.InsentientEntity;
+import org.geysermc.connector.entity.type.EntityType;
+import org.geysermc.connector.network.session.GeyserSession;
+
+public class EnderDragonEntity extends InsentientEntity {
+
+ public EnderDragonEntity(long entityId, long geyserId, EntityType entityType, Vector3f position, Vector3f motion, Vector3f rotation) {
+ super(entityId, geyserId, entityType, position, motion, rotation);
+ }
+
+ @Override
+ public void updateBedrockMetadata(EntityMetadata entityMetadata, GeyserSession session) {
+ if (entityMetadata.getId() == 15) {
+ metadata.getFlags().setFlag(EntityFlag.FIRE_IMMUNE, true);
+ switch ((int) entityMetadata.getValue()) {
+ // Performing breath attack
+ case 5:
+ EntityEventPacket entityEventPacket = new EntityEventPacket();
+ entityEventPacket.setType(EntityEventType.DRAGON_FLAMING);
+ entityEventPacket.setRuntimeEntityId(geyserId);
+ entityEventPacket.setData(0);
+ session.getUpstream().sendPacket(entityEventPacket);
+ case 6:
+ case 7:
+ metadata.getFlags().setFlag(EntityFlag.SITTING, true);
+ break;
+ }
+ }
+ super.updateBedrockMetadata(entityMetadata, session);
+ }
+
+ @Override
+ public void spawnEntity(GeyserSession session) {
+ AddEntityPacket addEntityPacket = new AddEntityPacket();
+ addEntityPacket.setIdentifier("minecraft:" + entityType.name().toLowerCase());
+ addEntityPacket.setRuntimeEntityId(geyserId);
+ addEntityPacket.setUniqueEntityId(geyserId);
+ addEntityPacket.setPosition(position);
+ addEntityPacket.setMotion(motion);
+ addEntityPacket.setRotation(getBedrockRotation());
+ addEntityPacket.setEntityType(entityType.getType());
+ addEntityPacket.getMetadata().putAll(metadata);
+
+ // Otherwise dragon is always 'dying'
+ addEntityPacket.getAttributes().add(new Attribute("minecraft:health", 0.0f, 200f, 200f, 200f));
+
+ valid = true;
+ session.getUpstream().sendPacket(addEntityPacket);
+
+ session.getConnector().getLogger().debug("Spawned entity " + entityType + " at location " + position + " with id " + geyserId + " (java id " + entityId + ")");
+ }
+}
diff --git a/connector/src/main/java/org/geysermc/connector/entity/living/monster/EndermanEntity.java b/connector/src/main/java/org/geysermc/connector/entity/living/monster/EndermanEntity.java
new file mode 100644
index 000000000..a423013cb
--- /dev/null
+++ b/connector/src/main/java/org/geysermc/connector/entity/living/monster/EndermanEntity.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright (c) 2019-2020 GeyserMC. http://geysermc.org
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ * @author GeyserMC
+ * @link https://github.com/GeyserMC/Geyser
+ */
+
+package org.geysermc.connector.entity.living.monster;
+
+import com.github.steveice10.mc.protocol.data.game.entity.metadata.EntityMetadata;
+import com.github.steveice10.mc.protocol.data.game.world.block.BlockState;
+import com.nukkitx.math.vector.Vector3f;
+import com.nukkitx.protocol.bedrock.data.EntityData;
+import com.nukkitx.protocol.bedrock.data.EntityFlag;
+import org.geysermc.connector.entity.type.EntityType;
+import org.geysermc.connector.network.session.GeyserSession;
+import org.geysermc.connector.network.translators.block.BlockTranslator;
+
+public class EndermanEntity extends MonsterEntity {
+
+ public EndermanEntity(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) {
+ // Held block
+ if (entityMetadata.getId() == 15) {
+ metadata.put(EntityData.ENDERMAN_HELD_ITEM_ID, BlockTranslator.getBedrockBlockId((BlockState) entityMetadata.getValue()));
+ }
+ // 'Angry' - mouth open
+ if (entityMetadata.getId() == 16) {
+ metadata.getFlags().setFlag(EntityFlag.ANGRY, (boolean) entityMetadata.getValue());
+ }
+ // TODO: ID 17 is stared at but I don't believe it's used - maybe only for the sound effect. Check after particle merge
+ super.updateBedrockMetadata(entityMetadata, session);
+ }
+}
diff --git a/connector/src/main/java/org/geysermc/connector/entity/living/monster/ShulkerEntity.java b/connector/src/main/java/org/geysermc/connector/entity/living/monster/ShulkerEntity.java
new file mode 100644
index 000000000..bca9e6891
--- /dev/null
+++ b/connector/src/main/java/org/geysermc/connector/entity/living/monster/ShulkerEntity.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright (c) 2019-2020 GeyserMC. http://geysermc.org
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ * @author GeyserMC
+ * @link https://github.com/GeyserMC/Geyser
+ */
+
+package org.geysermc.connector.entity.living.monster;
+
+import com.github.steveice10.mc.protocol.data.game.entity.metadata.EntityMetadata;
+import com.github.steveice10.mc.protocol.data.game.entity.metadata.Position;
+import com.github.steveice10.mc.protocol.data.game.world.block.BlockFace;
+import com.nukkitx.math.vector.Vector3f;
+import com.nukkitx.math.vector.Vector3i;
+import com.nukkitx.protocol.bedrock.data.EntityData;
+import org.geysermc.connector.entity.living.GolemEntity;
+import org.geysermc.connector.entity.type.EntityType;
+import org.geysermc.connector.network.session.GeyserSession;
+
+public class ShulkerEntity extends GolemEntity {
+
+ public ShulkerEntity(long entityId, long geyserId, EntityType entityType, Vector3f position, Vector3f motion, Vector3f rotation) {
+ super(entityId, geyserId, entityType, position, motion, rotation);
+ }
+
+ @Override
+ public void updateBedrockMetadata(EntityMetadata entityMetadata, GeyserSession session) {
+ if (entityMetadata.getId() == 15) {
+ BlockFace blockFace = (BlockFace) entityMetadata.getValue();
+ metadata.put(EntityData.SHULKER_ATTACH_FACE, (byte) blockFace.ordinal());
+ }
+ if (entityMetadata.getId() == 16) {
+ Position position = (Position) entityMetadata.getValue();
+ if (position != null) {
+ metadata.put(EntityData.SHULKER_ATTACH_POS, Vector3i.from(position.getX(), position.getY(), position.getZ()));
+ }
+ }
+ //TODO Outdated metadata flag SHULKER_PEAK_HEIGHT
+// if (entityMetadata.getId() == 17) {
+// int height = (byte) entityMetadata.getValue();
+// metadata.put(EntityData.SHULKER_PEAK_HEIGHT, height);
+// }
+ if (entityMetadata.getId() == 18) {
+ int color = Math.abs((byte) entityMetadata.getValue() - 15);
+ metadata.put(EntityData.VARIANT, color);
+ }
+ super.updateBedrockMetadata(entityMetadata, session);
+ }
+}
diff --git a/connector/src/main/java/org/geysermc/connector/entity/type/EntityType.java b/connector/src/main/java/org/geysermc/connector/entity/type/EntityType.java
index 38f99abcf..7a7e13c06 100644
--- a/connector/src/main/java/org/geysermc/connector/entity/type/EntityType.java
+++ b/connector/src/main/java/org/geysermc/connector/entity/type/EntityType.java
@@ -69,7 +69,7 @@ public enum EntityType {
SPIDER(SpiderEntity.class, 35, 0.9f, 1.4f, 1.4f, 1f),
ZOMBIE_PIGMAN(MonsterEntity.class, 36, 1.8f, 0.6f, 0.6f, 1.62f),
SLIME(InsentientEntity.class, 37, 0.51f),
- ENDERMAN(MonsterEntity.class, 38, 2.9f, 0.6f),
+ ENDERMAN(EndermanEntity.class, 38, 2.9f, 0.6f),
SILVERFISH(MonsterEntity.class, 39, 0.3f, 0.4f),
CAVE_SPIDER(MonsterEntity.class, 40, 0.5f, 0.7f),
GHAST(FlyingEntity.class, 41, 4.0f),
@@ -84,8 +84,8 @@ public enum EntityType {
ELDER_GUARDIAN(GuardianEntity.class, 50, 1.9975f),
NPC(PlayerEntity.class, 51, 1.8f, 0.6f, 0.6f, 1.62f),
WITHER(MonsterEntity.class, 52, 3.5f, 0.9f),
- ENDER_DRAGON(InsentientEntity.class, 53, 4f, 13f),
- SHULKER(GolemEntity.class, 54, 1f, 1f),
+ ENDER_DRAGON(EnderDragonEntity.class, 53, 4f, 13f),
+ SHULKER(ShulkerEntity.class, 54, 1f, 1f),
ENDERMITE(MonsterEntity.class, 55, 0.3f, 0.4f),
AGENT(Entity.class, 56, 0f),
VINDICATOR(AbstractIllagerEntity.class, 57, 1.8f, 0.6f, 0.6f, 1.62f),
@@ -104,13 +104,13 @@ public enum EntityType {
EXPERIENCE_BOTTLE(ThrowableEntity.class, 68, 0.25f, 0.25f),
EXPERIENCE_ORB(ExpOrbEntity.class, 69, 0f),
EYE_OF_ENDER(Entity.class, 70, 0f),
- END_CRYSTAL(Entity.class, 71, 0f),
+ END_CRYSTAL(EnderCrystalEntity.class, 71, 0f),
FIREWORK_ROCKET(Entity.class, 72, 0f),
TRIDENT(ArrowEntity.class, 73, 0f),
TURTLE(AnimalEntity.class, 74, 0.4f, 1.2f),
CAT(CatEntity.class, 75, 0.35f, 0.3f),
SHULKER_BULLET(Entity.class, 76, 0f),
- FISHING_BOBBER(Entity.class, 77, 0f),
+ FISHING_BOBBER(FishingHookEntity.class, 77, 0f),
CHALKBOARD(Entity.class, 78, 0f),
DRAGON_FIREBALL(ItemedFireballEntity.class, 79, 0f),
ARROW(ArrowEntity.class, 80, 0.25f, 0.25f),
@@ -140,10 +140,10 @@ public enum EntityType {
VEX(MonsterEntity.class, 105, 0f),
ICE_BOMB(Entity.class, 106, 0f),
BALLOON(Entity.class, 107, 0f), //TODO
- PUFFERFISH(AbstractFishEntity.class, 108, 0.7f, 0.7f),
+ PUFFERFISH(PufferFishEntity.class, 108, 0.7f, 0.7f),
SALMON(AbstractFishEntity.class, 109, 0.5f, 0.7f),
DROWNED(ZombieEntity.class, 110, 1.95f, 0.6f),
- TROPICAL_FISH(AbstractFishEntity.class, 111, 0.6f, 0.6f),
+ TROPICAL_FISH(TropicalFishEntity.class, 111, 0.6f, 0.6f),
COD(AbstractFishEntity.class, 112, 0.25f, 0.5f),
PANDA(PandaEntity.class, 113, 1.25f, 1.125f, 1.825f),
FOX(FoxEntity.class, 121, 0.5f, 1.25f),
diff --git a/connector/src/main/java/org/geysermc/connector/inventory/Inventory.java b/connector/src/main/java/org/geysermc/connector/inventory/Inventory.java
index 801f670cd..539fe1e26 100644
--- a/connector/src/main/java/org/geysermc/connector/inventory/Inventory.java
+++ b/connector/src/main/java/org/geysermc/connector/inventory/Inventory.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2019 GeyserMC. http://geysermc.org
+ * 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
@@ -27,9 +27,12 @@ package org.geysermc.connector.inventory;
import com.github.steveice10.mc.protocol.data.game.entity.metadata.ItemStack;
import com.github.steveice10.mc.protocol.data.game.window.WindowType;
+import com.nukkitx.math.vector.Vector3i;
import lombok.Getter;
import lombok.Setter;
+import java.util.concurrent.atomic.AtomicInteger;
+
public class Inventory {
@Getter
@@ -43,16 +46,26 @@ public class Inventory {
protected WindowType windowType;
@Getter
- protected int size;
+ protected final int size;
@Getter
@Setter
protected String title;
- @Getter
@Setter
protected ItemStack[] items;
+ @Getter
+ @Setter
+ protected Vector3i holderPosition = Vector3i.ZERO;
+
+ @Getter
+ @Setter
+ protected long holderId = -1;
+
+ @Getter
+ protected AtomicInteger transactionId = new AtomicInteger(1);
+
public Inventory(int id, WindowType windowType, int size) {
this("Inventory", id, windowType, size);
}
@@ -62,11 +75,16 @@ public class Inventory {
this.id = id;
this.windowType = windowType;
this.size = size;
-
this.items = new ItemStack[size];
}
public ItemStack getItem(int slot) {
return items[slot];
}
+
+ public void setItem(int slot, ItemStack item) {
+ if (item != null && (item.getId() == 0 || item.getAmount() < 1))
+ item = null;
+ items[slot] = item;
+ }
}
diff --git a/connector/src/main/java/org/geysermc/connector/inventory/PlayerInventory.java b/connector/src/main/java/org/geysermc/connector/inventory/PlayerInventory.java
index 424570b9e..432ca8270 100644
--- a/connector/src/main/java/org/geysermc/connector/inventory/PlayerInventory.java
+++ b/connector/src/main/java/org/geysermc/connector/inventory/PlayerInventory.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2019 GeyserMC. http://geysermc.org
+ * 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
@@ -35,13 +35,21 @@ public class PlayerInventory extends Inventory {
@Setter
private int heldItemSlot;
- public PlayerInventory() {
- super(0, null, 45);
+ @Getter
+ private ItemStack cursor;
+ public PlayerInventory() {
+ super(0, null, 46);
heldItemSlot = 0;
}
+ public void setCursor(ItemStack stack) {
+ if (stack != null && (stack.getId() == 0 || stack.getAmount() < 1))
+ stack = null;
+ cursor = stack;
+ }
+
public ItemStack getItemInHand() {
- return items[heldItemSlot];
+ return items[36 + heldItemSlot];
}
}
diff --git a/connector/src/main/java/org/geysermc/connector/metrics/Metrics.java b/connector/src/main/java/org/geysermc/connector/metrics/Metrics.java
index ff2cd097c..36aa32c36 100644
--- a/connector/src/main/java/org/geysermc/connector/metrics/Metrics.java
+++ b/connector/src/main/java/org/geysermc/connector/metrics/Metrics.java
@@ -25,8 +25,10 @@
package org.geysermc.connector.metrics;
-import com.google.gson.JsonArray;
-import com.google.gson.JsonObject;
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.node.ArrayNode;
+import com.fasterxml.jackson.databind.node.ObjectNode;
import org.geysermc.connector.GeyserConnector;
import javax.net.ssl.HttpsURLConnection;
@@ -71,6 +73,8 @@ public class Metrics {
// A list with all custom charts
private final List charts = new ArrayList<>();
+ private final static ObjectMapper mapper = new ObjectMapper();
+
private GeyserConnector connector;
/**
@@ -110,7 +114,7 @@ public class Metrics {
*/
private void startSubmitting() {
connector.getGeneralThreadPool().scheduleAtFixedRate(this::submitData, 1, 30, TimeUnit.MINUTES);
- // Submit the data every 30 minutes, first time after 5 minutes to give other plugins enough time to start
+ // Submit the data every 30 minutes, first time after 1 minutes to give other plugins enough time to start
// WARNING: Changing the frequency has no effect but your plugin WILL be blocked/deleted!
// WARNING: Just don't do it!
}
@@ -120,22 +124,22 @@ public class Metrics {
*
* @return The plugin specific data.
*/
- private JsonObject getPluginData() {
- JsonObject data = new JsonObject();
+ private ObjectNode getPluginData() {
+ ObjectNode data = mapper.createObjectNode();
- data.addProperty("pluginName", name); // Append the name of the server software
- data.addProperty("pluginVersion", GeyserConnector.VERSION); // Append the name of the server software
+ data.put("pluginName", name); // Append the name of the server software
+ data.put("pluginVersion", GeyserConnector.VERSION); // Append the name of the server software
- JsonArray customCharts = new JsonArray();
+ ArrayNode customCharts = mapper.createArrayNode();
for (CustomChart customChart : charts) {
// Add the data of the custom charts
- JsonObject chart = customChart.getRequestJsonObject();
+ JsonNode chart = customChart.getRequestJsonNode();
if (chart == null) { // If the chart is null, we skip it
continue;
}
customCharts.add(chart);
}
- data.add("customCharts", customCharts);
+ data.put("customCharts", customCharts);
return data;
}
@@ -145,7 +149,7 @@ public class Metrics {
*
* @return The server specific data.
*/
- private JsonObject getServerData() {
+ private ObjectNode getServerData() {
// OS specific data
int playerAmount = connector.getPlayers().size();
@@ -154,15 +158,15 @@ public class Metrics {
String osVersion = System.getProperty("os.version");
int coreCount = Runtime.getRuntime().availableProcessors();
- JsonObject data = new JsonObject();
+ ObjectNode data = mapper.createObjectNode();
- data.addProperty("serverUUID", serverUUID);
+ data.put("serverUUID", serverUUID);
- data.addProperty("playerAmount", playerAmount);
- data.addProperty("osName", osName);
- data.addProperty("osArch", osArch);
- data.addProperty("osVersion", osVersion);
- data.addProperty("coreCount", coreCount);
+ data.put("playerAmount", playerAmount);
+ data.put("osName", osName);
+ data.put("osArch", osArch);
+ data.put("osVersion", osVersion);
+ data.put("coreCount", coreCount);
return data;
}
@@ -171,11 +175,11 @@ public class Metrics {
* Collects the data and sends it afterwards.
*/
private void submitData() {
- final JsonObject data = getServerData();
+ final ObjectNode data = getServerData();
- JsonArray pluginData = new JsonArray();
+ ArrayNode pluginData = mapper.createArrayNode();
pluginData.add(getPluginData());
- data.add("plugins", pluginData);
+ data.putPOJO("plugins", pluginData);
new Thread(() -> {
try {
@@ -196,7 +200,7 @@ public class Metrics {
* @param data The data to send.
* @throws Exception If the request failed.
*/
- private static void sendData(JsonObject data) throws Exception {
+ private static void sendData(ObjectNode data) throws Exception {
if (data == null) {
throw new IllegalArgumentException("Data cannot be null!");
}
@@ -262,16 +266,16 @@ public class Metrics {
this.chartId = chartId;
}
- private JsonObject getRequestJsonObject() {
- JsonObject chart = new JsonObject();
- chart.addProperty("chartId", chartId);
+ private ObjectNode getRequestJsonNode() {
+ ObjectNode chart = new ObjectMapper().createObjectNode();
+ chart.put("chartId", chartId);
try {
- JsonObject data = getChartData();
+ ObjectNode data = getChartData();
if (data == null) {
// If the data is null we don't send the chart.
return null;
}
- chart.add("data", data);
+ chart.putPOJO("data", data);
} catch (Throwable t) {
if (logFailedRequests) {
logger.log(Level.WARNING, "Failed to get data for custom chart with id " + chartId, t);
@@ -281,7 +285,9 @@ public class Metrics {
return chart;
}
- protected abstract JsonObject getChartData() throws Exception;
+
+
+ protected abstract ObjectNode getChartData() throws Exception;
}
@@ -304,14 +310,14 @@ public class Metrics {
}
@Override
- protected JsonObject getChartData() throws Exception {
- JsonObject data = new JsonObject();
+ protected ObjectNode getChartData() throws Exception {
+ ObjectNode data = mapper.createObjectNode();
String value = callable.call();
if (value == null || value.isEmpty()) {
// Null = skip the chart
return null;
}
- data.addProperty("value", value);
+ data.put("value", value);
return data;
}
}
@@ -335,9 +341,9 @@ public class Metrics {
}
@Override
- protected JsonObject getChartData() throws Exception {
- JsonObject data = new JsonObject();
- JsonObject values = new JsonObject();
+ protected ObjectNode getChartData() throws Exception {
+ ObjectNode data = mapper.createObjectNode();
+ ObjectNode values = mapper.createObjectNode();
Map map = callable.call();
if (map == null || map.isEmpty()) {
// Null = skip the chart
@@ -349,13 +355,13 @@ public class Metrics {
continue; // Skip this invalid
}
allSkipped = false;
- values.addProperty(entry.getKey(), entry.getValue());
+ values.put(entry.getKey(), entry.getValue());
}
if (allSkipped) {
// Null = skip the chart
return null;
}
- data.add("values", values);
+ data.putPOJO("values", values);
return data;
}
}
@@ -379,9 +385,9 @@ public class Metrics {
}
@Override
- public JsonObject getChartData() throws Exception {
- JsonObject data = new JsonObject();
- JsonObject values = new JsonObject();
+ public ObjectNode getChartData() throws Exception {
+ ObjectNode data = mapper.createObjectNode();
+ ObjectNode values = mapper.createObjectNode();
Map> map = callable.call();
if (map == null || map.isEmpty()) {
// Null = skip the chart
@@ -389,22 +395,22 @@ public class Metrics {
}
boolean reallyAllSkipped = true;
for (Map.Entry> entryValues : map.entrySet()) {
- JsonObject value = new JsonObject();
+ ObjectNode value = mapper.createObjectNode();
boolean allSkipped = true;
for (Map.Entry valueEntry : map.get(entryValues.getKey()).entrySet()) {
- value.addProperty(valueEntry.getKey(), valueEntry.getValue());
+ value.put(valueEntry.getKey(), valueEntry.getValue());
allSkipped = false;
}
if (!allSkipped) {
reallyAllSkipped = false;
- values.add(entryValues.getKey(), value);
+ values.putPOJO(entryValues.getKey(), value);
}
}
if (reallyAllSkipped) {
// Null = skip the chart
return null;
}
- data.add("values", values);
+ data.putPOJO("values", values);
return data;
}
}
@@ -428,14 +434,14 @@ public class Metrics {
}
@Override
- protected JsonObject getChartData() throws Exception {
- JsonObject data = new JsonObject();
+ protected ObjectNode getChartData() throws Exception {
+ ObjectNode data = mapper.createObjectNode();
int value = callable.call();
if (value == 0) {
// Null = skip the chart
return null;
}
- data.addProperty("value", value);
+ data.put("value", value);
return data;
}
@@ -460,9 +466,9 @@ public class Metrics {
}
@Override
- protected JsonObject getChartData() throws Exception {
- JsonObject data = new JsonObject();
- JsonObject values = new JsonObject();
+ protected ObjectNode getChartData() throws Exception {
+ ObjectNode data = mapper.createObjectNode();
+ ObjectNode values = mapper.createObjectNode();
Map map = callable.call();
if (map == null || map.isEmpty()) {
// Null = skip the chart
@@ -474,13 +480,13 @@ public class Metrics {
continue; // Skip this invalid
}
allSkipped = false;
- values.addProperty(entry.getKey(), entry.getValue());
+ values.put(entry.getKey(), entry.getValue());
}
if (allSkipped) {
// Null = skip the chart
return null;
}
- data.add("values", values);
+ data.putPOJO("values", values);
return data;
}
@@ -505,20 +511,20 @@ public class Metrics {
}
@Override
- protected JsonObject getChartData() throws Exception {
- JsonObject data = new JsonObject();
- JsonObject values = new JsonObject();
+ protected ObjectNode getChartData() throws Exception {
+ ObjectNode data = mapper.createObjectNode();
+ ObjectNode values = mapper.createObjectNode();
Map map = callable.call();
if (map == null || map.isEmpty()) {
// Null = skip the chart
return null;
}
for (Map.Entry entry : map.entrySet()) {
- JsonArray categoryValues = new JsonArray();
+ ArrayNode categoryValues = mapper.createArrayNode();
categoryValues.add(entry.getValue());
- values.add(entry.getKey(), categoryValues);
+ values.putPOJO(entry.getKey(), categoryValues);
}
- data.add("values", values);
+ data.putPOJO("values", values);
return data;
}
@@ -543,9 +549,9 @@ public class Metrics {
}
@Override
- protected JsonObject getChartData() throws Exception {
- JsonObject data = new JsonObject();
- JsonObject values = new JsonObject();
+ protected ObjectNode getChartData() throws Exception {
+ ObjectNode data = mapper.createObjectNode();
+ ObjectNode values = mapper.createObjectNode();
Map map = callable.call();
if (map == null || map.isEmpty()) {
// Null = skip the chart
@@ -557,17 +563,17 @@ public class Metrics {
continue; // Skip this invalid
}
allSkipped = false;
- JsonArray categoryValues = new JsonArray();
+ ArrayNode categoryValues = mapper.createArrayNode();
for (int categoryValue : entry.getValue()) {
categoryValues.add(categoryValue);
}
- values.add(entry.getKey(), categoryValues);
+ values.putPOJO(entry.getKey(), categoryValues);
}
if (allSkipped) {
// Null = skip the chart
return null;
}
- data.add("values", values);
+ data.putPOJO("values", values);
return data;
}
diff --git a/connector/src/main/java/org/geysermc/connector/metrics/SentryMetrics.java b/connector/src/main/java/org/geysermc/connector/metrics/SentryMetrics.java
deleted file mode 100644
index 88926fd5e..000000000
--- a/connector/src/main/java/org/geysermc/connector/metrics/SentryMetrics.java
+++ /dev/null
@@ -1,107 +0,0 @@
-package org.geysermc.connector.metrics;
-
-import io.sentry.Sentry;
-import io.sentry.SentryClient;
-import io.sentry.SentryClientFactory;
-import io.sentry.context.Context;
-import io.sentry.event.BreadcrumbBuilder;
-import io.sentry.event.UserBuilder;
-
-public class SentryMetrics {
- private static SentryClient sentry;
-
- public static void init() {
- /*
- It is recommended that you use the DSN detection system, which
- will check the environment variable "SENTRY_DSN", the Java
- System Property "sentry.dsn", or the "sentry.properties" file
- in your classpath. This makes it easier to provide and adjust
- your DSN without needing to change your code. See the configuration
- page for more information.
- */
- Sentry.init();
-
- // You can also manually provide the DSN to the ``init`` method.
- Sentry.init();
-
- /*
- It is possible to go around the static ``Sentry`` API, which means
- you are responsible for making the SentryClient instance available
- to your code.
- */
- sentry = SentryClientFactory.sentryClient();
-
- SentryMetrics metrics = new SentryMetrics();
- metrics.logWithStaticAPI();
- metrics.logWithInstanceAPI();
- }
-
- /**
- * An example method that throws an exception.
- */
- void unsafeMethod() {
- throw new UnsupportedOperationException("You shouldn't call this!");
- }
-
- /**
- * Examples using the (recommended) static API.
- */
- void logWithStaticAPI() {
- // Note that all fields set on the context are optional. Context data is copied onto
- // all future events in the current context (until the context is cleared).
-
- // Record a breadcrumb in the current context. By default the last 100 breadcrumbs are kept.
- Sentry.getContext().recordBreadcrumb(
- new BreadcrumbBuilder().setMessage("User made an action").build()
- );
-
- // Set the user in the current context.
- Sentry.getContext().setUser(
- new UserBuilder().setEmail("hello@sentry.io").build()
- );
-
- // Add extra data to future events in this context.
- Sentry.getContext().addExtra("extra", "thing");
-
- // Add an additional tag to future events in this context.
- Sentry.getContext().addTag("tagName", "tagValue");
-
- /*
- This sends a simple event to Sentry using the statically stored instance
- that was created in the ``main`` method.
- */
- Sentry.capture("This is a test");
-
- try {
- unsafeMethod();
- } catch (Exception e) {
- // This sends an exception event to Sentry using the statically stored instance
- // that was created in the ``main`` method.
- Sentry.capture(e);
- }
- }
-
- /**
- * Examples that use the SentryClient instance directly.
- */
- void logWithInstanceAPI() {
- // Retrieve the current context.
- Context context = sentry.getContext();
-
- // Record a breadcrumb in the current context. By default the last 100 breadcrumbs are kept.
- context.recordBreadcrumb(new BreadcrumbBuilder().setMessage("User made an action").build());
-
- // Set the user in the current context.
- context.setUser(new UserBuilder().setEmail("geyser.project@gmail.com").build());
-
- // This sends a simple event to Sentry.
- sentry.sendMessage("This is a test");
-
- try {
- unsafeMethod();
- } catch (Exception e) {
- // This sends an exception event to Sentry.
- sentry.sendException(e);
- }
- }
-}
diff --git a/connector/src/main/java/org/geysermc/connector/network/ConnectorServerEventHandler.java b/connector/src/main/java/org/geysermc/connector/network/ConnectorServerEventHandler.java
index f0ae14a5c..60ad28d47 100644
--- a/connector/src/main/java/org/geysermc/connector/network/ConnectorServerEventHandler.java
+++ b/connector/src/main/java/org/geysermc/connector/network/ConnectorServerEventHandler.java
@@ -80,6 +80,13 @@ public class ConnectorServerEventHandler implements BedrockServerEventHandler {
pong.setMotd(config.getBedrock().getMotd1());
pong.setMotd(config.getBedrock().getMotd2());
}
+
+ //Bedrock will not even attempt a connection if the client thinks the server is full
+ //so we have to fake it not being full
+ if (pong.getPlayerCount() >= pong.getMaximumPlayerCount()) {
+ pong.setMaximumPlayerCount(pong.getPlayerCount() + 1);
+ }
+
return pong;
}
diff --git a/connector/src/main/java/org/geysermc/connector/network/UpstreamPacketHandler.java b/connector/src/main/java/org/geysermc/connector/network/UpstreamPacketHandler.java
index 56f6f8e1a..553f90f9e 100644
--- a/connector/src/main/java/org/geysermc/connector/network/UpstreamPacketHandler.java
+++ b/connector/src/main/java/org/geysermc/connector/network/UpstreamPacketHandler.java
@@ -50,8 +50,11 @@ public class UpstreamPacketHandler extends LoggingPacketHandler {
@Override
public boolean handle(LoginPacket loginPacket) {
- if (loginPacket.getProtocolVersion() != GeyserConnector.BEDROCK_PACKET_CODEC.getProtocolVersion()) {
- session.getUpstream().disconnect("Unsupported Bedrock version. Are you running an outdated version?");
+ if (loginPacket.getProtocolVersion() > GeyserConnector.BEDROCK_PACKET_CODEC.getProtocolVersion()) {
+ session.disconnect("Outdated Geyser proxy! I'm still on " + GeyserConnector.BEDROCK_PACKET_CODEC.getMinecraftVersion());
+ return true;
+ } else if (loginPacket.getProtocolVersion() < GeyserConnector.BEDROCK_PACKET_CODEC.getProtocolVersion()) {
+ session.disconnect("Outdated Bedrock client! Please use " + GeyserConnector.BEDROCK_PACKET_CODEC.getMinecraftVersion());
return true;
}
@@ -111,7 +114,7 @@ public class UpstreamPacketHandler extends LoggingPacketHandler {
break;
default:
- session.getUpstream().disconnect("disconnectionScreen.resourcePack");
+ session.disconnect("disconnectionScreen.resourcePack");
break;
}
@@ -120,7 +123,7 @@ public class UpstreamPacketHandler extends LoggingPacketHandler {
@Override
public boolean handle(ModalFormResponsePacket packet) {
- return LoginEncryptionUtils.authenticateFromForm(session, connector, packet.getFormData());
+ return LoginEncryptionUtils.authenticateFromForm(session, connector, packet.getFormId(), packet.getFormData());
}
private boolean couldLoginUserByName(String bedrockUsername) {
diff --git a/connector/src/main/java/org/geysermc/connector/network/session/GeyserSession.java b/connector/src/main/java/org/geysermc/connector/network/session/GeyserSession.java
index 02427042b..16c7e7fed 100644
--- a/connector/src/main/java/org/geysermc/connector/network/session/GeyserSession.java
+++ b/connector/src/main/java/org/geysermc/connector/network/session/GeyserSession.java
@@ -30,8 +30,8 @@ import com.github.steveice10.mc.auth.exception.request.InvalidCredentialsExcepti
import com.github.steveice10.mc.auth.exception.request.RequestException;
import com.github.steveice10.mc.protocol.MinecraftProtocol;
import com.github.steveice10.mc.protocol.data.game.entity.player.GameMode;
-import com.github.steveice10.mc.protocol.packet.ingame.server.ServerRespawnPacket;
import com.github.steveice10.mc.protocol.packet.handshake.client.HandshakePacket;
+import com.github.steveice10.mc.protocol.packet.ingame.server.ServerRespawnPacket;
import com.github.steveice10.packetlib.Client;
import com.github.steveice10.packetlib.event.session.*;
import com.github.steveice10.packetlib.packet.Packet;
@@ -42,16 +42,14 @@ import com.nukkitx.math.vector.Vector2f;
import com.nukkitx.math.vector.Vector2i;
import com.nukkitx.math.vector.Vector3f;
import com.nukkitx.math.vector.Vector3i;
-import com.nukkitx.nbt.tag.CompoundTag;
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.packet.*;
-
import lombok.Getter;
import lombok.Setter;
-
import org.geysermc.common.AuthType;
import org.geysermc.common.window.FormWindow;
import org.geysermc.connector.GeyserConnector;
@@ -125,6 +123,9 @@ public class GeyserSession implements CommandSender {
private boolean manyDimPackets = false;
private ServerRespawnPacket lastDimPacket = null;
+ @Setter
+ private int craftSlot = 0;
+
public GeyserSession(GeyserConnector connector, BedrockServerSession bedrockServerSession) {
this.connector = connector;
this.upstream = new UpstreamSession(bedrockServerSession);
@@ -157,9 +158,14 @@ public class GeyserSession implements CommandSender {
upstream.sendPacket(biomeDefinitionListPacket);
AvailableEntityIdentifiersPacket entityPacket = new AvailableEntityIdentifiersPacket();
- entityPacket.setTag(CompoundTag.EMPTY);
+ entityPacket.setTag(Toolbox.ENTITY_IDENTIFIERS);
upstream.sendPacket(entityPacket);
+ InventoryContentPacket creativePacket = new InventoryContentPacket();
+ creativePacket.setContainerId(ContainerId.CREATIVE);
+ creativePacket.setContents(Toolbox.CREATIVE_ITEMS);
+ upstream.sendPacket(creativePacket);
+
PlayStatusPacket playStatusPacket = new PlayStatusPacket();
playStatusPacket.setStatus(PlayStatusPacket.Status.PLAYER_SPAWN);
upstream.sendPacket(playStatusPacket);
@@ -274,6 +280,9 @@ public class GeyserSession implements CommandSender {
loggingIn = false;
loggedIn = false;
connector.getLogger().info(authData.getName() + " has disconnected from remote java server on address " + remoteServer.getAddress() + " because of " + event.getReason());
+ if (event.getCause() != null) {
+ event.getCause().printStackTrace();
+ }
upstream.disconnect(event.getReason());
}
@@ -297,7 +306,7 @@ public class GeyserSession implements CommandSender {
downstream.getSession().connect();
connector.addPlayer(this);
- } catch (InvalidCredentialsException e) {
+ } catch (InvalidCredentialsException | IllegalArgumentException e) {
connector.getLogger().info("User '" + username + "' entered invalid login info, kicking.");
disconnect("Invalid/incorrect login info");
} catch (RequestException ex) {
@@ -313,10 +322,16 @@ public class GeyserSession implements CommandSender {
downstream.getSession().disconnect(reason);
}
if (upstream != null && !upstream.isClosed()) {
+ connector.getPlayers().remove(this.upstream.getAddress());
upstream.disconnect(reason);
}
}
+ this.entityCache.getEntities().clear();
+ this.scoreboardCache.removeScoreboard();
+ this.inventoryCache.getInventories().clear();
+ this.windowCache.getWindows().clear();
+
closed = true;
}
diff --git a/connector/src/main/java/org/geysermc/connector/network/session/cache/BossBar.java b/connector/src/main/java/org/geysermc/connector/network/session/cache/BossBar.java
new file mode 100644
index 000000000..abb9016a2
--- /dev/null
+++ b/connector/src/main/java/org/geysermc/connector/network/session/cache/BossBar.java
@@ -0,0 +1,121 @@
+/*
+ * Copyright (c) 2019-2020 GeyserMC. http://geysermc.org
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ * @author GeyserMC
+ * @link https://github.com/GeyserMC/Geyser
+ */
+
+package org.geysermc.connector.network.session.cache;
+
+import com.github.steveice10.mc.protocol.data.message.Message;
+import com.nukkitx.math.vector.Vector3f;
+import com.nukkitx.protocol.bedrock.data.EntityData;
+import com.nukkitx.protocol.bedrock.packet.AddEntityPacket;
+import com.nukkitx.protocol.bedrock.packet.BossEventPacket;
+import com.nukkitx.protocol.bedrock.packet.RemoveEntityPacket;
+import lombok.AllArgsConstructor;
+import org.geysermc.connector.network.session.GeyserSession;
+import org.geysermc.connector.utils.MessageUtils;
+
+@AllArgsConstructor
+public class BossBar {
+
+ private GeyserSession session;
+
+ private long entityId;
+ private Message title;
+ private float health;
+ private int color;
+ private int overlay;
+ private int darkenSky;
+
+ public void addBossBar() {
+ addBossEntity();
+ updateBossBar();
+ }
+
+ public void updateBossBar() {
+ BossEventPacket bossEventPacket = new BossEventPacket();
+ bossEventPacket.setBossUniqueEntityId(entityId);
+ bossEventPacket.setAction(BossEventPacket.Action.SHOW);
+ bossEventPacket.setTitle(MessageUtils.getTranslatedBedrockMessage(title, session.getClientData().getLanguageCode()));
+ bossEventPacket.setHealthPercentage(health);
+ bossEventPacket.setColor(color); //ignored by client
+ bossEventPacket.setOverlay(overlay);
+ bossEventPacket.setDarkenSky(darkenSky);
+
+ session.getUpstream().sendPacket(bossEventPacket);
+ }
+
+ public void updateTitle(Message title) {
+ this.title = title;
+ BossEventPacket bossEventPacket = new BossEventPacket();
+ bossEventPacket.setBossUniqueEntityId(entityId);
+ bossEventPacket.setAction(BossEventPacket.Action.TITLE);
+ bossEventPacket.setTitle(MessageUtils.getTranslatedBedrockMessage(title, session.getClientData().getLanguageCode()));
+
+ session.getUpstream().sendPacket(bossEventPacket);
+ }
+
+ public void updateHealth(float health) {
+ this.health = health;
+ BossEventPacket bossEventPacket = new BossEventPacket();
+ bossEventPacket.setBossUniqueEntityId(entityId);
+ bossEventPacket.setAction(BossEventPacket.Action.HEALTH_PERCENTAGE);
+ bossEventPacket.setHealthPercentage(health);
+
+ session.getUpstream().sendPacket(bossEventPacket);
+ }
+
+ public void removeBossBar() {
+ BossEventPacket bossEventPacket = new BossEventPacket();
+ bossEventPacket.setBossUniqueEntityId(entityId);
+ bossEventPacket.setAction(BossEventPacket.Action.HIDE);
+
+ session.getUpstream().sendPacket(bossEventPacket);
+ removeBossEntity();
+ }
+
+ /**
+ * Bedrock still needs an entity to display the BossBar.
+ * Just like 1.8 but it doesn't care about which entity
+ */
+ private void addBossEntity() {
+ AddEntityPacket addEntityPacket = new AddEntityPacket();
+ addEntityPacket.setUniqueEntityId(entityId);
+ addEntityPacket.setRuntimeEntityId(entityId);
+ addEntityPacket.setIdentifier("minecraft:creeper");
+ addEntityPacket.setEntityType(33);
+ addEntityPacket.setPosition(session.getPlayerEntity().getPosition());
+ addEntityPacket.setRotation(Vector3f.ZERO);
+ addEntityPacket.setMotion(Vector3f.ZERO);
+ addEntityPacket.getMetadata().put(EntityData.SCALE, 0.01F); // scale = 0 doesn't work?
+
+ session.getUpstream().sendPacket(addEntityPacket);
+ }
+
+ private void removeBossEntity() {
+ RemoveEntityPacket removeEntityPacket = new RemoveEntityPacket();
+ removeEntityPacket.setUniqueEntityId(entityId);
+
+ session.getUpstream().sendPacket(removeEntityPacket);
+ }
+}
diff --git a/connector/src/main/java/org/geysermc/connector/network/session/cache/EntityCache.java b/connector/src/main/java/org/geysermc/connector/network/session/cache/EntityCache.java
index f32ee2a5c..80d10b1a0 100644
--- a/connector/src/main/java/org/geysermc/connector/network/session/cache/EntityCache.java
+++ b/connector/src/main/java/org/geysermc/connector/network/session/cache/EntityCache.java
@@ -48,7 +48,7 @@ public class EntityCache {
private Long2ObjectMap entities = Long2ObjectMaps.synchronize(new Long2ObjectOpenHashMap<>());
private Long2LongMap entityIdTranslations = Long2LongMaps.synchronize(new Long2LongOpenHashMap());
private Map playerEntities = Collections.synchronizedMap(new HashMap<>());
- private Object2LongMap bossbars = new Object2LongOpenHashMap<>();
+ private Map bossBars = Collections.synchronizedMap(new HashMap<>());
@Getter
private AtomicLong nextEntityId = new AtomicLong(2L);
@@ -58,13 +58,19 @@ public class EntityCache {
}
public void spawnEntity(Entity entity) {
- cacheEntity(entity);
- entity.spawnEntity(session);
+ if (cacheEntity(entity)) {
+ entity.spawnEntity(session);
+ }
}
- public void cacheEntity(Entity entity) {
- entityIdTranslations.put(entity.getEntityId(), entity.getGeyserId());
- entities.put(entity.getGeyserId(), entity);
+ public boolean cacheEntity(Entity entity) {
+ // Check to see if the entity exists, otherwise we can end up with duplicated mobs
+ if (!entityIdTranslations.containsKey(entity.getEntityId())) {
+ entityIdTranslations.put(entity.getEntityId(), entity.getGeyserId());
+ entities.put(entity.getGeyserId(), entity);
+ return true;
+ }
+ return false;
}
public boolean removeEntity(Entity entity, boolean force) {
@@ -116,24 +122,30 @@ public class EntityCache {
playerEntities.remove(uuid);
}
- public long addBossBar(UUID uuid) {
- long entityId = getNextEntityId().incrementAndGet();
- bossbars.put(uuid, entityId);
- return entityId;
+ public void addBossBar(UUID uuid, BossBar bossBar) {
+ bossBars.put(uuid, bossBar);
+ bossBar.addBossBar();
}
- public long getBossBar(UUID uuid) {
- return bossbars.containsKey(uuid) ? bossbars.get(uuid) : -1;
+ public BossBar getBossBar(UUID uuid) {
+ return bossBars.get(uuid);
}
- public long removeBossBar(UUID uuid) {
- return bossbars.remove(uuid);
+ public void removeBossBar(UUID uuid) {
+ BossBar bossBar = bossBars.remove(uuid);
+ if (bossBar != null) {
+ bossBar.removeBossBar();
+ }
+ }
+
+ public void updateBossBars() {
+ bossBars.values().forEach(BossBar::updateBossBar);
}
public void clear() {
entities = null;
entityIdTranslations = null;
playerEntities = null;
- bossbars = null;
+ bossBars = null;
}
}
diff --git a/connector/src/main/java/org/geysermc/connector/network/session/cache/EntityEffectCache.java b/connector/src/main/java/org/geysermc/connector/network/session/cache/EntityEffectCache.java
new file mode 100644
index 000000000..a16ef6902
--- /dev/null
+++ b/connector/src/main/java/org/geysermc/connector/network/session/cache/EntityEffectCache.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright (c) 2019-2020 GeyserMC. http://geysermc.org
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ * @author GeyserMC
+ * @link https://github.com/GeyserMC/Geyser
+ */
+
+package org.geysermc.connector.network.session.cache;
+
+import com.github.steveice10.mc.protocol.data.game.entity.Effect;
+import it.unimi.dsi.fastutil.objects.Object2IntMap;
+import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap;
+import lombok.Getter;
+
+public class EntityEffectCache {
+
+ @Getter
+ private final Object2IntMap entityEffects = new Object2IntOpenHashMap<>();
+
+ public void addEffect(Effect effect, int effectAmplifier) {
+ if (effect != null) {
+ entityEffects.putIfAbsent(effect, effectAmplifier + 1);
+ }
+ }
+
+ public void removeEffect(Effect effect) {
+ if (entityEffects.containsKey(effect)) {
+ int effectLevel = entityEffects.getInt(effect);
+ entityEffects.remove(effect, effectLevel);
+ }
+ }
+
+ public int getEffectLevel(Effect effect) {
+ return entityEffects.getOrDefault(effect, 0);
+ }
+}
diff --git a/connector/src/main/java/org/geysermc/connector/network/session/cache/InventoryCache.java b/connector/src/main/java/org/geysermc/connector/network/session/cache/InventoryCache.java
index 7a505878e..032f64024 100644
--- a/connector/src/main/java/org/geysermc/connector/network/session/cache/InventoryCache.java
+++ b/connector/src/main/java/org/geysermc/connector/network/session/cache/InventoryCache.java
@@ -25,17 +25,13 @@
package org.geysermc.connector.network.session.cache;
-import com.github.steveice10.packetlib.packet.Packet;
+import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
+import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
import lombok.Getter;
import lombok.Setter;
import org.geysermc.connector.inventory.Inventory;
import org.geysermc.connector.network.session.GeyserSession;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-
public class InventoryCache {
private GeyserSession session;
@@ -45,10 +41,7 @@ public class InventoryCache {
private Inventory openInventory;
@Getter
- private Map inventories = new HashMap();
-
- @Getter
- private Map> cachedPackets = new HashMap>();
+ private Int2ObjectMap inventories = new Int2ObjectOpenHashMap<>();
public InventoryCache(GeyserSession session) {
this.session = session;
@@ -65,10 +58,4 @@ public class InventoryCache {
public void uncacheInventory(int id) {
inventories.remove(id);
}
-
- public void cachePacket(int id, Packet packet) {
- List packets = cachedPackets.getOrDefault(id, new ArrayList());
- packets.add(packet);
- cachedPackets.put(id, packets);
- }
}
diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/ItemRemapper.java b/connector/src/main/java/org/geysermc/connector/network/translators/ItemRemapper.java
new file mode 100644
index 000000000..6c286da2f
--- /dev/null
+++ b/connector/src/main/java/org/geysermc/connector/network/translators/ItemRemapper.java
@@ -0,0 +1,34 @@
+/*
+ * 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;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+@Retention(value = RetentionPolicy.RUNTIME)
+public @interface ItemRemapper {
+ int priority() default 0;
+}
diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/ItemStackTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/ItemStackTranslator.java
new file mode 100644
index 000000000..356dcf982
--- /dev/null
+++ b/connector/src/main/java/org/geysermc/connector/network/translators/ItemStackTranslator.java
@@ -0,0 +1,250 @@
+/*
+ * 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;
+
+import com.github.steveice10.mc.protocol.data.game.entity.metadata.ItemStack;
+import com.github.steveice10.mc.protocol.data.message.Message;
+import com.github.steveice10.opennbt.tag.builtin.*;
+import com.nukkitx.nbt.tag.CompoundTag;
+import com.nukkitx.nbt.tag.Tag;
+import com.nukkitx.protocol.bedrock.data.ItemData;
+import org.geysermc.connector.network.translators.item.ItemEntry;
+import org.geysermc.connector.utils.MessageUtils;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+public abstract class ItemStackTranslator {
+
+ public ItemData translateToBedrock(ItemStack itemStack, ItemEntry itemEntry) {
+ if (itemStack == null) {
+ return ItemData.AIR;
+ }
+ if (itemStack.getNbt() == null) {
+ return ItemData.of(itemEntry.getBedrockId(), (short) itemEntry.getBedrockData(), itemStack.getAmount());
+ }
+ return ItemData.of(itemEntry.getBedrockId(), (short) itemEntry.getBedrockData(), itemStack.getAmount(), this.translateNbtToBedrock(itemStack.getNbt()));
+ }
+
+ public ItemStack translateToJava(ItemData itemData, ItemEntry itemEntry) {
+ if (itemData == null) return null;
+ if (itemData.getTag() == null) {
+ return new ItemStack(itemEntry.getJavaId(), itemData.getCount(), new com.github.steveice10.opennbt.tag.builtin.CompoundTag(""));
+ }
+ return new ItemStack(itemEntry.getJavaId(), itemData.getCount(), this.translateToJavaNBT(itemData.getTag()));
+ }
+
+ public abstract List getAppliedItems();
+
+ public CompoundTag translateNbtToBedrock(com.github.steveice10.opennbt.tag.builtin.CompoundTag tag) {
+ Map> javaValue = new HashMap>();
+ if (tag.getValue() != null && !tag.getValue().isEmpty()) {
+ for (String str : tag.getValue().keySet()) {
+ com.github.steveice10.opennbt.tag.builtin.Tag javaTag = tag.get(str);
+ com.nukkitx.nbt.tag.Tag translatedTag = translateToBedrockNBT(javaTag);
+ if (translatedTag == null)
+ continue;
+
+ javaValue.put(translatedTag.getName(), translatedTag);
+ }
+ }
+
+ com.nukkitx.nbt.tag.CompoundTag bedrockTag = new com.nukkitx.nbt.tag.CompoundTag(tag.getName(), javaValue);
+ return bedrockTag;
+ }
+
+ private com.nukkitx.nbt.tag.Tag translateToBedrockNBT(com.github.steveice10.opennbt.tag.builtin.Tag tag) {
+ if (tag instanceof ByteArrayTag) {
+ ByteArrayTag byteArrayTag = (ByteArrayTag) tag;
+ return new com.nukkitx.nbt.tag.ByteArrayTag(byteArrayTag.getName(), byteArrayTag.getValue());
+ }
+
+ if (tag instanceof ByteTag) {
+ ByteTag byteTag = (ByteTag) tag;
+ return new com.nukkitx.nbt.tag.ByteTag(byteTag.getName(), byteTag.getValue());
+ }
+
+ if (tag instanceof DoubleTag) {
+ DoubleTag doubleTag = (DoubleTag) tag;
+ return new com.nukkitx.nbt.tag.DoubleTag(doubleTag.getName(), doubleTag.getValue());
+ }
+
+ if (tag instanceof FloatTag) {
+ FloatTag floatTag = (FloatTag) tag;
+ return new com.nukkitx.nbt.tag.FloatTag(floatTag.getName(), floatTag.getValue());
+ }
+
+ if (tag instanceof IntArrayTag) {
+ IntArrayTag intArrayTag = (IntArrayTag) tag;
+ return new com.nukkitx.nbt.tag.IntArrayTag(intArrayTag.getName(), intArrayTag.getValue());
+ }
+
+ if (tag instanceof IntTag) {
+ IntTag intTag = (IntTag) tag;
+ return new com.nukkitx.nbt.tag.IntTag(intTag.getName(), intTag.getValue());
+ }
+
+ if (tag instanceof LongArrayTag) {
+ LongArrayTag longArrayTag = (LongArrayTag) tag;
+ return new com.nukkitx.nbt.tag.LongArrayTag(longArrayTag.getName(), longArrayTag.getValue());
+ }
+
+ if (tag instanceof LongTag) {
+ LongTag longTag = (LongTag) tag;
+ return new com.nukkitx.nbt.tag.LongTag(longTag.getName(), longTag.getValue());
+ }
+
+ if (tag instanceof ShortTag) {
+ ShortTag shortTag = (ShortTag) tag;
+ return new com.nukkitx.nbt.tag.ShortTag(shortTag.getName(), shortTag.getValue());
+ }
+
+ if (tag instanceof StringTag) {
+ StringTag stringTag = (StringTag) tag;
+ return new com.nukkitx.nbt.tag.StringTag(stringTag.getName(), MessageUtils.getBedrockMessage(Message.fromString(stringTag.getValue())));
+ }
+
+ if (tag instanceof ListTag) {
+ ListTag listTag = (ListTag) tag;
+
+ List tagList = new ArrayList<>();
+ for (com.github.steveice10.opennbt.tag.builtin.Tag value : listTag) {
+ tagList.add(translateToBedrockNBT(value));
+ }
+ Class clazz = CompoundTag.class;
+ if (!tagList.isEmpty()) {
+ clazz = tagList.get(0).getClass();
+ }
+ return new com.nukkitx.nbt.tag.ListTag(listTag.getName(), clazz, tagList);
+ }
+
+ if (tag instanceof com.github.steveice10.opennbt.tag.builtin.CompoundTag) {
+ com.github.steveice10.opennbt.tag.builtin.CompoundTag compoundTag = (com.github.steveice10.opennbt.tag.builtin.CompoundTag) tag;
+
+ return translateNbtToBedrock(compoundTag);
+ }
+
+ return null;
+ }
+
+ public com.github.steveice10.opennbt.tag.builtin.CompoundTag translateToJavaNBT(com.nukkitx.nbt.tag.CompoundTag tag) {
+ com.github.steveice10.opennbt.tag.builtin.CompoundTag javaTag = new com.github.steveice10.opennbt.tag.builtin.CompoundTag(tag.getName());
+ Map javaValue = javaTag.getValue();
+ if (tag.getValue() != null && !tag.getValue().isEmpty()) {
+ for (String str : tag.getValue().keySet()) {
+ com.nukkitx.nbt.tag.Tag bedrockTag = tag.get(str);
+ com.github.steveice10.opennbt.tag.builtin.Tag translatedTag = translateToJavaNBT(bedrockTag);
+ if (translatedTag == null)
+ continue;
+
+ javaValue.put(translatedTag.getName(), translatedTag);
+ }
+ }
+
+ javaTag.setValue(javaValue);
+ return javaTag;
+ }
+
+ private com.github.steveice10.opennbt.tag.builtin.Tag translateToJavaNBT(com.nukkitx.nbt.tag.Tag tag) {
+ if (tag instanceof com.nukkitx.nbt.tag.ByteArrayTag) {
+ com.nukkitx.nbt.tag.ByteArrayTag byteArrayTag = (com.nukkitx.nbt.tag.ByteArrayTag) tag;
+ return new ByteArrayTag(byteArrayTag.getName(), byteArrayTag.getValue());
+ }
+
+ if (tag instanceof com.nukkitx.nbt.tag.ByteTag) {
+ com.nukkitx.nbt.tag.ByteTag byteTag = (com.nukkitx.nbt.tag.ByteTag) tag;
+ return new ByteTag(byteTag.getName(), byteTag.getValue());
+ }
+
+ if (tag instanceof com.nukkitx.nbt.tag.DoubleTag) {
+ com.nukkitx.nbt.tag.DoubleTag doubleTag = (com.nukkitx.nbt.tag.DoubleTag) tag;
+ return new DoubleTag(doubleTag.getName(), doubleTag.getValue());
+ }
+
+ if (tag instanceof com.nukkitx.nbt.tag.FloatTag) {
+ com.nukkitx.nbt.tag.FloatTag floatTag = (com.nukkitx.nbt.tag.FloatTag) tag;
+ return new FloatTag(floatTag.getName(), floatTag.getValue());
+ }
+
+ if (tag instanceof com.nukkitx.nbt.tag.IntArrayTag) {
+ com.nukkitx.nbt.tag.IntArrayTag intArrayTag = (com.nukkitx.nbt.tag.IntArrayTag) tag;
+ return new IntArrayTag(intArrayTag.getName(), intArrayTag.getValue());
+ }
+
+ if (tag instanceof com.nukkitx.nbt.tag.IntTag) {
+ com.nukkitx.nbt.tag.IntTag intTag = (com.nukkitx.nbt.tag.IntTag) tag;
+ return new IntTag(intTag.getName(), intTag.getValue());
+ }
+
+ if (tag instanceof com.nukkitx.nbt.tag.LongArrayTag) {
+ com.nukkitx.nbt.tag.LongArrayTag longArrayTag = (com.nukkitx.nbt.tag.LongArrayTag) tag;
+ return new LongArrayTag(longArrayTag.getName(), longArrayTag.getValue());
+ }
+
+ if (tag instanceof com.nukkitx.nbt.tag.LongTag) {
+ com.nukkitx.nbt.tag.LongTag longTag = (com.nukkitx.nbt.tag.LongTag) tag;
+ return new LongTag(longTag.getName(), longTag.getValue());
+ }
+
+ if (tag instanceof com.nukkitx.nbt.tag.ShortTag) {
+ com.nukkitx.nbt.tag.ShortTag shortTag = (com.nukkitx.nbt.tag.ShortTag) tag;
+ return new ShortTag(shortTag.getName(), shortTag.getValue());
+ }
+
+ if (tag instanceof com.nukkitx.nbt.tag.StringTag) {
+ com.nukkitx.nbt.tag.StringTag stringTag = (com.nukkitx.nbt.tag.StringTag) tag;
+ return new StringTag(stringTag.getName(), stringTag.getValue());
+ }
+
+ if (tag instanceof com.nukkitx.nbt.tag.ListTag) {
+ com.nukkitx.nbt.tag.ListTag listTag = (com.nukkitx.nbt.tag.ListTag) tag;
+
+ List tags = new ArrayList<>();
+
+ for (Object value : listTag.getValue()) {
+ if (!(value instanceof com.nukkitx.nbt.tag.Tag))
+ continue;
+
+ com.nukkitx.nbt.tag.Tag tagValue = (com.nukkitx.nbt.tag.Tag) value;
+ com.github.steveice10.opennbt.tag.builtin.Tag javaTag = translateToJavaNBT(tagValue);
+ if (javaTag != null)
+ tags.add(javaTag);
+ }
+ return new ListTag(listTag.getName(), tags);
+ }
+
+ if (tag instanceof com.nukkitx.nbt.tag.CompoundTag) {
+ com.nukkitx.nbt.tag.CompoundTag compoundTag = (com.nukkitx.nbt.tag.CompoundTag) tag;
+ return translateToJavaNBT(compoundTag);
+ }
+
+ return null;
+ }
+
+
+}
diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/NbtItemStackTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/NbtItemStackTranslator.java
new file mode 100644
index 000000000..56c780f44
--- /dev/null
+++ b/connector/src/main/java/org/geysermc/connector/network/translators/NbtItemStackTranslator.java
@@ -0,0 +1,45 @@
+/*
+ * 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;
+
+import com.github.steveice10.opennbt.tag.builtin.CompoundTag;
+import org.geysermc.connector.network.translators.item.ItemEntry;
+
+public class NbtItemStackTranslator {
+
+ public void translateToBedrock(CompoundTag itemTag, ItemEntry itemEntry) {
+
+ }
+
+ public void translateToJava(CompoundTag itemTag, ItemEntry itemEntry) {
+
+ }
+
+ public boolean acceptItem(ItemEntry itemEntry) {
+ return true;
+ }
+
+}
diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/Translators.java b/connector/src/main/java/org/geysermc/connector/network/translators/Translators.java
index d4e5dae8b..f0a3fd28c 100644
--- a/connector/src/main/java/org/geysermc/connector/network/translators/Translators.java
+++ b/connector/src/main/java/org/geysermc/connector/network/translators/Translators.java
@@ -27,11 +27,17 @@ package org.geysermc.connector.network.translators;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
+import java.util.HashMap;
+import java.util.Map;
+import com.github.steveice10.mc.protocol.data.game.window.WindowType;
+import com.nukkitx.protocol.bedrock.data.ContainerType;
import org.geysermc.connector.GeyserConnector;
import org.geysermc.connector.network.translators.block.BlockTranslator;
-import org.geysermc.connector.network.translators.inventory.GenericInventoryTranslator;
-import org.geysermc.connector.network.translators.inventory.InventoryTranslator;
+import org.geysermc.connector.network.translators.block.entity.*;
+import org.geysermc.connector.network.translators.inventory.*;
+import org.geysermc.connector.network.translators.inventory.updater.ContainerInventoryUpdater;
+import org.geysermc.connector.network.translators.inventory.updater.InventoryUpdater;
import org.geysermc.connector.network.translators.item.ItemTranslator;
import org.reflections.Reflections;
@@ -50,7 +56,10 @@ public class Translators {
private static ItemTranslator itemTranslator;
@Getter
- private static InventoryTranslator inventoryTranslator = new GenericInventoryTranslator();
+ private static Map inventoryTranslators = new HashMap<>();
+
+ @Getter
+ private static Map blockEntityTranslators = new HashMap<>();
private static final CompoundTag EMPTY_TAG = CompoundTagBuilder.builder().buildRootTag();
public static final byte[] EMPTY_LEVEL_CHUNK_DATA;
@@ -82,15 +91,15 @@ public class Translators {
if (Packet.class.isAssignableFrom(packet)) {
Class extends Packet> targetPacket = (Class extends Packet>) packet;
PacketTranslator extends Packet> translator = (PacketTranslator extends Packet>) clazz.newInstance();
-
+
Registry.registerJava(targetPacket, translator);
-
+
} else if (BedrockPacket.class.isAssignableFrom(packet)) {
Class extends BedrockPacket> targetPacket = (Class extends BedrockPacket>) packet;
PacketTranslator extends BedrockPacket> translator = (PacketTranslator extends BedrockPacket>) clazz.newInstance();
-
+
Registry.registerBedrock(targetPacket, translator);
-
+
} else {
GeyserConnector.getInstance().getLogger().error("Class " + clazz.getCanonicalName() + " is annotated as a translator but has an invalid target packet.");
}
@@ -100,17 +109,51 @@ public class Translators {
}
itemTranslator = new ItemTranslator();
+ itemTranslator.init();
BlockTranslator.init();
+ registerBlockEntityTranslators();
registerInventoryTranslators();
}
+ private static void registerBlockEntityTranslators() {
+ Reflections ref = new Reflections("org.geysermc.connector.network.translators.block.entity");
+
+ for (Class> clazz : ref.getTypesAnnotatedWith(BlockEntity.class)) {
+
+ GeyserConnector.getInstance().getLogger().debug("Found annotated block entity: " + clazz.getCanonicalName());
+
+ try {
+ blockEntityTranslators.put(clazz.getAnnotation(BlockEntity.class).name(), (BlockEntityTranslator) clazz.newInstance());
+ } catch (InstantiationException | IllegalAccessException e) {
+ GeyserConnector.getInstance().getLogger().error("Could not instantiate annotated block entity " + clazz.getCanonicalName() + ".");
+ }
+ }
+ }
+
private static void registerInventoryTranslators() {
- /*inventoryTranslators.put(WindowType.GENERIC_9X1, new GenericInventoryTranslator());
- inventoryTranslators.put(WindowType.GENERIC_9X2, new GenericInventoryTranslator());
- inventoryTranslators.put(WindowType.GENERIC_9X3, new GenericInventoryTranslator());
- inventoryTranslators.put(WindowType.GENERIC_9X4, new GenericInventoryTranslator());
- inventoryTranslators.put(WindowType.GENERIC_9X5, new GenericInventoryTranslator());
- inventoryTranslators.put(WindowType.GENERIC_9X6, new GenericInventoryTranslator());*/
+ inventoryTranslators.put(null, new PlayerInventoryTranslator()); //player inventory
+ inventoryTranslators.put(WindowType.GENERIC_9X1, new SingleChestInventoryTranslator(9));
+ inventoryTranslators.put(WindowType.GENERIC_9X2, new SingleChestInventoryTranslator(18));
+ inventoryTranslators.put(WindowType.GENERIC_9X3, new SingleChestInventoryTranslator(27));
+ inventoryTranslators.put(WindowType.GENERIC_9X4, new DoubleChestInventoryTranslator(36));
+ inventoryTranslators.put(WindowType.GENERIC_9X5, new DoubleChestInventoryTranslator(45));
+ inventoryTranslators.put(WindowType.GENERIC_9X6, new DoubleChestInventoryTranslator(54));
+ inventoryTranslators.put(WindowType.BREWING_STAND, new BrewingInventoryTranslator());
+ inventoryTranslators.put(WindowType.ANVIL, new AnvilInventoryTranslator());
+ inventoryTranslators.put(WindowType.CRAFTING, new CraftingInventoryTranslator());
+ inventoryTranslators.put(WindowType.GRINDSTONE, new GrindstoneInventoryTranslator());
+ //inventoryTranslators.put(WindowType.ENCHANTMENT, new EnchantmentInventoryTranslator()); //TODO
+
+ InventoryTranslator furnace = new FurnaceInventoryTranslator();
+ inventoryTranslators.put(WindowType.FURNACE, furnace);
+ inventoryTranslators.put(WindowType.BLAST_FURNACE, furnace);
+ inventoryTranslators.put(WindowType.SMOKER, furnace);
+
+ InventoryUpdater containerUpdater = new ContainerInventoryUpdater();
+ inventoryTranslators.put(WindowType.GENERIC_3X3, new BlockInventoryTranslator(9, "minecraft:dispenser[facing=north,triggered=false]", ContainerType.DISPENSER, containerUpdater));
+ inventoryTranslators.put(WindowType.HOPPER, new BlockInventoryTranslator(5, "minecraft:hopper[enabled=false,facing=down]", ContainerType.HOPPER, containerUpdater));
+ inventoryTranslators.put(WindowType.SHULKER_BOX, new BlockInventoryTranslator(27, "minecraft:shulker_box[facing=north]", ContainerType.CONTAINER, containerUpdater));
+ //inventoryTranslators.put(WindowType.BEACON, new BlockInventoryTranslator(1, "minecraft:beacon", ContainerType.BEACON)); //TODO
}
}
diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockActionTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockActionTranslator.java
index 206f42d1f..7ab713893 100644
--- a/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockActionTranslator.java
+++ b/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockActionTranslator.java
@@ -123,6 +123,7 @@ public class BedrockActionTranslator extends PacketTranslator {
@Override
public void translate(AnimatePacket packet, GeyserSession session) {
+ // Stop the player sending animations before they have fully spawned into the server
+ if (!session.isSpawned()) {
+ return;
+ }
+
switch (packet.getAction()) {
case SWING_ARM:
// Delay so entity damage can be processed first
diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockBlockEntityDataTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockBlockEntityDataTranslator.java
new file mode 100644
index 000000000..dc076a910
--- /dev/null
+++ b/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockBlockEntityDataTranslator.java
@@ -0,0 +1,92 @@
+/*
+ * Copyright (c) 2019-2020 GeyserMC. http://geysermc.org
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ * @author GeyserMC
+ * @link https://github.com/GeyserMC/Geyser
+ */
+
+package org.geysermc.connector.network.translators.bedrock;
+
+import com.github.steveice10.mc.protocol.data.game.entity.metadata.Position;
+import com.github.steveice10.mc.protocol.packet.ingame.client.world.ClientUpdateSignPacket;
+import com.nukkitx.nbt.tag.CompoundTag;
+import com.nukkitx.protocol.bedrock.packet.BlockEntityDataPacket;
+import org.geysermc.connector.network.session.GeyserSession;
+import org.geysermc.connector.network.translators.PacketTranslator;
+import org.geysermc.connector.network.translators.Translator;
+
+import java.util.HashMap;
+import java.util.Map;
+
+@Translator(packet = BlockEntityDataPacket.class)
+public class BedrockBlockEntityDataTranslator extends PacketTranslator {
+
+ // In case two people are editing signs at the same time this array holds the temporary messages to be sent
+ // Position -> Message being held
+ protected static Map lastMessages = new HashMap<>();
+
+ @Override
+ public void translate(BlockEntityDataPacket packet, GeyserSession session) {
+ if (packet.getData() instanceof CompoundTag) {
+ CompoundTag tag = (CompoundTag) packet.getData();
+ if (tag.getString("id").equals("Sign")) {
+ // This is the reason why this all works - Bedrock sends packets every time you update the sign, Java only wants the final packet
+ // But Bedrock sends one final packet when you're done editing the sign, which should be equal to the last message since there's no edits
+ // So if the latest update does not match the last cached update then it's still being edited
+ Position pos = new Position(tag.getInt("x"), tag.getInt("y"), tag.getInt("z"));
+ if (!tag.getString("Text").equals(lastMessages.get(pos))) {
+ lastMessages.put(pos, tag.getString("Text"));
+ return;
+ }
+ // Otherwise the two messages are identical and we can get to work deconstructing
+ StringBuilder newMessage = new StringBuilder();
+ // While Bedrock's sign lines are one string, Java's is an array of each line
+ // (Initialized all with empty strings because it complains about null)
+ String[] lines = new String[] {"", "", "", ""};
+ int iterator = 0;
+ // This converts the message into the array'd message Java wants
+ for (char character : tag.getString("Text").toCharArray()) {
+ // If we get a return in Bedrock, that signals to use the next line.
+ if (character == '\n') {
+ lines[iterator] = newMessage.toString();
+ iterator++;
+ // Bedrock, for whatever reason, can hold a message out of bounds
+ // We don't care about that so we discard that
+ if (iterator > lines.length - 1) {
+ break;
+ }
+ newMessage = new StringBuilder();
+ } else newMessage.append(character);
+ }
+ // Put the final line on since it isn't done in the for loop
+ if (iterator < lines.length) lines[iterator] = newMessage.toString();
+ ClientUpdateSignPacket clientUpdateSignPacket = new ClientUpdateSignPacket(pos, lines);
+ session.getDownstream().getSession().send(clientUpdateSignPacket);
+ //TODO (potentially): originally I was going to update the sign blocks so Bedrock and Java users would match visually
+ // However Java can still store a lot per-line and visuals are still messed up so that doesn't work
+
+ // We remove the sign position from map to indicate there is no work-in-progress sign
+ lastMessages.remove(pos);
+ }
+ }
+
+ }
+}
diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockCommandRequestTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockCommandRequestTranslator.java
index 28cbf4c41..c8e117410 100644
--- a/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockCommandRequestTranslator.java
+++ b/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockCommandRequestTranslator.java
@@ -27,13 +27,14 @@ package org.geysermc.connector.network.translators.bedrock;
import org.geysermc.common.PlatformType;
import org.geysermc.connector.GeyserConnector;
-import org.geysermc.connector.command.GeyserCommandMap;
+import org.geysermc.connector.command.CommandManager;
import org.geysermc.connector.network.session.GeyserSession;
import org.geysermc.connector.network.translators.PacketTranslator;
import org.geysermc.connector.network.translators.Translator;
import com.github.steveice10.mc.protocol.packet.ingame.client.ClientChatPacket;
import com.nukkitx.protocol.bedrock.packet.CommandRequestPacket;
+import org.geysermc.connector.utils.MessageUtils;
@Translator(packet = CommandRequestPacket.class)
public class BedrockCommandRequestTranslator extends PacketTranslator {
@@ -41,11 +42,17 @@ public class BedrockCommandRequestTranslator extends PacketTranslator {
+
+ @Override
+ public void translate(ContainerClosePacket packet, GeyserSession session) {
+ byte windowId = packet.getWindowId();
+ 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.getDownstream().getSession().send(closeWindowPacket);
+ InventoryUtils.closeInventory(session, windowId);
+ }
+}
diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockInteractTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockInteractTranslator.java
index 012ef90ea..0d1c08b22 100644
--- a/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockInteractTranslator.java
+++ b/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockInteractTranslator.java
@@ -34,6 +34,7 @@ import com.github.steveice10.mc.protocol.data.game.entity.player.Hand;
import com.github.steveice10.mc.protocol.data.game.entity.player.InteractAction;
import com.github.steveice10.mc.protocol.packet.ingame.client.player.ClientPlayerInteractEntityPacket;
import com.nukkitx.protocol.bedrock.packet.InteractPacket;
+import org.geysermc.connector.network.translators.item.ItemTranslator;
@Translator(packet = InteractPacket.class)
public class BedrockInteractTranslator extends PacketTranslator {
@@ -46,6 +47,9 @@ public class BedrockInteractTranslator extends PacketTranslator
switch (packet.getAction()) {
case INTERACT:
+ if (session.getInventory().getItem(session.getInventory().getHeldItemSlot() + 36).getId() == ItemTranslator.SHIELD) {
+ break;
+ }
ClientPlayerInteractEntityPacket interactPacket = new ClientPlayerInteractEntityPacket((int) entity.getEntityId(),
InteractAction.INTERACT, Hand.MAIN_HAND);
session.getDownstream().getSession().send(interactPacket);
diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockInventoryTransactionTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockInventoryTransactionTranslator.java
index 94903246b..3f6eba55d 100644
--- a/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockInventoryTransactionTranslator.java
+++ b/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockInventoryTransactionTranslator.java
@@ -27,10 +27,15 @@ package org.geysermc.connector.network.translators.bedrock;
import com.github.steveice10.mc.protocol.packet.ingame.client.player.ClientPlayerPlaceBlockPacket;
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;
+import org.geysermc.connector.network.translators.Translators;
+import org.geysermc.connector.network.translators.item.ItemTranslator;
+import org.geysermc.connector.utils.InventoryUtils;
+import com.nukkitx.math.vector.Vector3f;
import com.github.steveice10.mc.protocol.data.game.entity.metadata.Position;
import com.github.steveice10.mc.protocol.data.game.entity.player.GameMode;
import com.github.steveice10.mc.protocol.data.game.entity.player.Hand;
@@ -40,7 +45,6 @@ import com.github.steveice10.mc.protocol.data.game.world.block.BlockFace;
import com.github.steveice10.mc.protocol.packet.ingame.client.player.ClientPlayerActionPacket;
import com.github.steveice10.mc.protocol.packet.ingame.client.player.ClientPlayerInteractEntityPacket;
import com.github.steveice10.mc.protocol.packet.ingame.client.player.ClientPlayerUseItemPacket;
-import com.nukkitx.math.vector.Vector3f;
import com.nukkitx.protocol.bedrock.packet.InventoryTransactionPacket;
@Translator(packet = InventoryTransactionPacket.class)
@@ -49,6 +53,17 @@ public class BedrockInventoryTransactionTranslator extends PacketTranslator {
@@ -38,12 +39,24 @@ public class BedrockTextTranslator extends PacketTranslator {
@Override
public void translate(TextPacket packet, GeyserSession session) {
if (packet.getMessage().charAt(0) == '.') {
- ClientChatPacket chatPacket = new ClientChatPacket(packet.getMessage().replace(".", "/"));
+ String message = packet.getMessage().replace(".", "/").trim();
+
+ if (MessageUtils.isTooLong(message, session)) {
+ return;
+ }
+
+ ClientChatPacket chatPacket = new ClientChatPacket(message);
session.getDownstream().getSession().send(chatPacket);
return;
}
- ClientChatPacket chatPacket = new ClientChatPacket(packet.getMessage());
+ String message = packet.getMessage().trim();
+
+ if (MessageUtils.isTooLong(message, session)) {
+ return;
+ }
+
+ ClientChatPacket chatPacket = new ClientChatPacket(message);
session.getDownstream().getSession().send(chatPacket);
}
}
diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/block/BlockStateValues.java b/connector/src/main/java/org/geysermc/connector/network/translators/block/BlockStateValues.java
new file mode 100644
index 000000000..25d6070fe
--- /dev/null
+++ b/connector/src/main/java/org/geysermc/connector/network/translators/block/BlockStateValues.java
@@ -0,0 +1,127 @@
+/*
+ * 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.block;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import com.github.steveice10.mc.protocol.data.game.world.block.BlockState;
+import it.unimi.dsi.fastutil.objects.Object2ByteMap;
+import it.unimi.dsi.fastutil.objects.Object2ByteOpenHashMap;
+import it.unimi.dsi.fastutil.objects.Object2IntMap;
+import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap;
+
+import java.util.Map;
+
+/**
+ * Used for block entities if the Java block state contains Bedrock block information.
+ */
+public class BlockStateValues {
+
+ private static final Object2IntMap BANNER_COLORS = new Object2IntOpenHashMap<>();
+ private static final Object2ByteMap BED_COLORS = new Object2ByteOpenHashMap<>();
+ private static final Object2ByteMap SKULL_VARIANTS = new Object2ByteOpenHashMap<>();
+ private static final Object2ByteMap SKULL_ROTATIONS = new Object2ByteOpenHashMap<>();
+
+ /**
+ * Determines if the block state contains Bedrock block information
+ * @param entry The String to JsonNode map used in BlockTranslator
+ * @param javaBlockState the Java Block State of the block
+ */
+ public static void storeBlockStateValues(Map.Entry entry, BlockState javaBlockState) {
+ JsonNode bannerColor = entry.getValue().get("banner_color");
+ if (bannerColor != null) {
+ BlockStateValues.BANNER_COLORS.put(javaBlockState, (byte) bannerColor.intValue());
+ return; // There will never be a banner color and a skull variant
+ }
+
+ JsonNode bedColor = entry.getValue().get("bed_color");
+ if (bedColor != null) {
+ BlockStateValues.BED_COLORS.put(javaBlockState, (byte) bedColor.intValue());
+ return;
+ }
+
+ JsonNode skullVariation = entry.getValue().get("variation");
+ if(skullVariation != null) {
+ BlockStateValues.SKULL_VARIANTS.put(javaBlockState, (byte) skullVariation.intValue());
+ }
+
+ JsonNode skullRotation = entry.getValue().get("skull_rotation");
+ if (skullRotation != null) {
+ BlockStateValues.SKULL_ROTATIONS.put(javaBlockState, (byte) skullRotation.intValue());
+ }
+ }
+
+ /**
+ * Banner colors are part of the namespaced ID in Java Edition, but part of the block entity tag in Bedrock.
+ * This gives an integer color that Bedrock can use.
+ * @param state BlockState of the block
+ * @return banner color integer or -1 if no color
+ */
+ public static int getBannerColor(BlockState state) {
+ if (BANNER_COLORS.containsKey(state)) {
+ return BANNER_COLORS.getInt(state);
+ }
+ return -1;
+ }
+
+ /**
+ * Bed colors are part of the namespaced ID in Java Edition, but part of the block entity tag in Bedrock.
+ * This gives a byte color that Bedrock can use - Bedrock needs a byte in the final tag.
+ * @param state BlockState of the block
+ * @return bed color byte or -1 if no color
+ */
+ public static byte getBedColor(BlockState state) {
+ if (BED_COLORS.containsKey(state)) {
+ return BED_COLORS.getByte(state);
+ }
+ return -1;
+ }
+
+ /**
+ * Skull variations are part of the namespaced ID in Java Edition, but part of the block entity tag in Bedrock.
+ * This gives a byte variant ID that Bedrock can use.
+ * @param state BlockState of the block
+ * @return skull variant byte or -1 if no variant
+ */
+ public static byte getSkullVariant(BlockState state) {
+ if (SKULL_VARIANTS.containsKey(state)) {
+ return SKULL_VARIANTS.getByte(state);
+ }
+ return -1;
+ }
+
+ /**
+ *
+ * @param state BlockState of the block
+ * @return skull rotation value or -1 if no value
+ */
+ public static byte getSkullRotation(BlockState state) {
+ if (SKULL_ROTATIONS.containsKey(state)) {
+ return SKULL_ROTATIONS.getByte(state);
+ }
+ return -1;
+ }
+
+}
diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/block/BlockTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/block/BlockTranslator.java
index 1ac9a9fe1..cf0da17b8 100644
--- a/connector/src/main/java/org/geysermc/connector/network/translators/block/BlockTranslator.java
+++ b/connector/src/main/java/org/geysermc/connector/network/translators/block/BlockTranslator.java
@@ -32,19 +32,14 @@ import com.nukkitx.nbt.NbtUtils;
import com.nukkitx.nbt.stream.NBTInputStream;
import com.nukkitx.nbt.tag.CompoundTag;
import com.nukkitx.nbt.tag.ListTag;
-import it.unimi.dsi.fastutil.ints.Int2IntMap;
-import it.unimi.dsi.fastutil.ints.Int2IntOpenHashMap;
-import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
-import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
-import it.unimi.dsi.fastutil.ints.IntOpenHashSet;
-import it.unimi.dsi.fastutil.ints.IntSet;
+import it.unimi.dsi.fastutil.ints.*;
import it.unimi.dsi.fastutil.objects.Object2IntMap;
import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap;
import org.geysermc.connector.GeyserConnector;
-//import org.geysermc.connector.network.translators.forge.Blocks;
+import org.geysermc.connector.network.translators.block.entity.BlockEntity;
import org.geysermc.connector.utils.Toolbox;
+import org.reflections.Reflections;
-import java.io.File;
import java.io.InputStream;
import java.util.*;
@@ -55,11 +50,22 @@ public class BlockTranslator {
private static final Int2IntMap JAVA_TO_BEDROCK_BLOCK_MAP = new Int2IntOpenHashMap();
private static final Int2ObjectMap BEDROCK_TO_JAVA_BLOCK_MAP = new Int2ObjectOpenHashMap<>();
+ private static final Map JAVA_ID_BLOCK_MAP = new HashMap<>();
private static final IntSet WATERLOGGED = new IntOpenHashSet();
// Bedrock carpet ID, used in LlamaEntity.java for decoration
public static final int CARPET = 171;
+ private static final Map JAVA_ID_TO_BLOCK_ENTITY_MAP = new HashMap<>();
+
+ public static final Int2DoubleMap JAVA_RUNTIME_ID_TO_HARDNESS = new Int2DoubleOpenHashMap();
+ public static final Int2BooleanMap JAVA_RUNTIME_ID_TO_CAN_HARVEST_WITH_HAND = new Int2BooleanOpenHashMap();
+ public static final Int2ObjectMap JAVA_RUNTIME_ID_TO_TOOL_TYPE = new Int2ObjectOpenHashMap<>();
+
+ // For block breaking animation math
+ public static final IntSet JAVA_RUNTIME_WOOL_IDS = new IntOpenHashSet();
+ public static final int JAVA_RUNTIME_COBWEB_ID;
+
private static final int BLOCK_STATE_VERSION = 17760256;
static {
@@ -93,16 +99,58 @@ public class BlockTranslator {
addedStatesMap.defaultReturnValue(-1);
List paletteList = new ArrayList<>();
+ Reflections ref = new Reflections("org.geysermc.connector.network.translators.block.entity");
+ ref.getTypesAnnotatedWith(BlockEntity.class);
+
int waterRuntimeId = -1;
int javaRuntimeId = -1;
int bedrockRuntimeId = 0;
+ int cobwebRuntimeId = -1;
Iterator> blocksIterator = blocks.fields();
while (blocksIterator.hasNext()) {
javaRuntimeId++;
Map.Entry entry = blocksIterator.next();
String javaId = entry.getKey();
+ BlockState javaBlockState = new BlockState(javaRuntimeId);
CompoundTag blockTag = buildBedrockState(entry.getValue());
+ // TODO fix this, (no block should have a null hardness)
+ JsonNode hardnessNode = entry.getValue().get("block_hardness");
+ if (hardnessNode != null) {
+ JAVA_RUNTIME_ID_TO_HARDNESS.put(javaRuntimeId, hardnessNode.doubleValue());
+ }
+
+ JAVA_RUNTIME_ID_TO_CAN_HARVEST_WITH_HAND.put(javaRuntimeId, entry.getValue().get("can_break_with_hand").booleanValue());
+
+ JsonNode toolTypeNode = entry.getValue().get("tool_type");
+ if (toolTypeNode != null) {
+ JAVA_RUNTIME_ID_TO_TOOL_TYPE.put(javaRuntimeId, toolTypeNode.textValue());
+ }
+
+ if (javaId.contains("wool")) {
+ JAVA_RUNTIME_WOOL_IDS.add(javaRuntimeId);
+ }
+
+ if (javaId.contains("cobweb")) {
+ cobwebRuntimeId = javaRuntimeId;
+ }
+
+ JAVA_ID_BLOCK_MAP.put(javaId, javaBlockState);
+
+ // Used for adding all "special" Java block states to block state map
+ String identifier;
+ String bedrock_identifer = entry.getValue().get("bedrock_identifier").asText();
+ for (Class> clazz : ref.getTypesAnnotatedWith(BlockEntity.class)) {
+ identifier = clazz.getAnnotation(BlockEntity.class).regex();
+ // Endswith, or else the block bedrock gets picked up for bed
+ if (bedrock_identifer.endsWith(identifier) && !identifier.equals("")) {
+ JAVA_ID_TO_BLOCK_ENTITY_MAP.put(javaBlockState, clazz.getAnnotation(BlockEntity.class).name());
+ break;
+ }
+ }
+
+ BlockStateValues.storeBlockStateValues(entry, javaBlockState);
+
if ("minecraft:water[level=0]".equals(javaId)) {
waterRuntimeId = bedrockRuntimeId;
}
@@ -110,10 +158,10 @@ public class BlockTranslator {
|| javaId.contains("minecraft:bubble_column") || javaId.contains("minecraft:kelp") || javaId.contains("seagrass");
if (waterlogged) {
- BEDROCK_TO_JAVA_BLOCK_MAP.putIfAbsent(bedrockRuntimeId | 1 << 31, new BlockState(javaRuntimeId));
+ BEDROCK_TO_JAVA_BLOCK_MAP.putIfAbsent(bedrockRuntimeId | 1 << 31, javaBlockState);
WATERLOGGED.add(javaRuntimeId);
} else {
- BEDROCK_TO_JAVA_BLOCK_MAP.putIfAbsent(bedrockRuntimeId, new BlockState(javaRuntimeId));
+ BEDROCK_TO_JAVA_BLOCK_MAP.putIfAbsent(bedrockRuntimeId, javaBlockState);
}
CompoundTag runtimeTag = blockStateMap.remove(blockTag);
@@ -121,7 +169,7 @@ public class BlockTranslator {
addedStatesMap.put(blockTag, bedrockRuntimeId);
paletteList.add(runtimeTag);
} else {
- int duplicateRuntimeId = addedStatesMap.get(blockTag);
+ int duplicateRuntimeId = addedStatesMap.getOrDefault(blockTag, -1);
if (duplicateRuntimeId == -1) {
GeyserConnector.getInstance().getLogger().debug("Mapping " + javaId + " was not found for bedrock edition!");
} else {
@@ -134,6 +182,11 @@ public class BlockTranslator {
bedrockRuntimeId++;
}
+ if (cobwebRuntimeId == -1) {
+ throw new AssertionError("Unable to find cobwebs in palette");
+ }
+ JAVA_RUNTIME_COBWEB_ID = cobwebRuntimeId;
+
if (waterRuntimeId == -1) {
throw new AssertionError("Unable to find water in palette");
}
@@ -192,6 +245,14 @@ public class BlockTranslator {
return BEDROCK_TO_JAVA_BLOCK_MAP.get(bedrockId);
}
+ public static BlockState getJavaBlockState(String javaId) {
+ return JAVA_ID_BLOCK_MAP.get(javaId);
+ }
+
+ public static String getBlockEntityString(BlockState javaId) {
+ return JAVA_ID_TO_BLOCK_ENTITY_MAP.get(javaId);
+ }
+
public static boolean isWaterlogged(BlockState state) {
return WATERLOGGED.contains(state.getId());
}
diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/block/entity/BannerBlockEntityTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/block/entity/BannerBlockEntityTranslator.java
new file mode 100644
index 000000000..e81191a39
--- /dev/null
+++ b/connector/src/main/java/org/geysermc/connector/network/translators/block/entity/BannerBlockEntityTranslator.java
@@ -0,0 +1,108 @@
+/*
+ * 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.block.entity;
+
+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.ListTag;
+import com.nukkitx.nbt.CompoundTagBuilder;
+import com.nukkitx.nbt.tag.IntTag;
+import com.nukkitx.nbt.tag.StringTag;
+import com.nukkitx.nbt.tag.Tag;
+import org.geysermc.connector.network.translators.block.BlockStateValues;
+
+import java.util.ArrayList;
+import java.util.List;
+
+@BlockEntity(name = "Banner", delay = false, regex = "banner")
+public class BannerBlockEntityTranslator extends BlockEntityTranslator implements RequiresBlockState {
+
+ @Override
+ public boolean isBlock(BlockState blockState) {
+ return BlockStateValues.getBannerColor(blockState) != -1;
+ }
+
+ @Override
+ public List> translateTag(CompoundTag tag, BlockState blockState) {
+ List> tags = new ArrayList<>();
+ int bannerColor = BlockStateValues.getBannerColor(blockState);
+ if (bannerColor != -1) {
+ tags.add(new IntTag("Base", 15 - bannerColor));
+ }
+ ListTag patterns = tag.get("Patterns");
+ List tagsList = new ArrayList<>();
+ if (tag.contains("Patterns")) {
+ for (com.github.steveice10.opennbt.tag.builtin.Tag patternTag : patterns.getValue()) {
+ com.nukkitx.nbt.tag.CompoundTag newPatternTag = getPattern((CompoundTag) patternTag);
+ if (newPatternTag != null) {
+ tagsList.add(newPatternTag);
+ }
+ }
+ com.nukkitx.nbt.tag.ListTag bedrockPatterns =
+ new com.nukkitx.nbt.tag.ListTag<>("Patterns", com.nukkitx.nbt.tag.CompoundTag.class, tagsList);
+ tags.add(bedrockPatterns);
+ }
+ if (tag.contains("CustomName")) {
+ tags.add(new StringTag("CustomName", (String) tag.get("CustomName").getValue()));
+ }
+ return tags;
+ }
+
+ @Override
+ public CompoundTag getDefaultJavaTag(String javaId, int x, int y, int z) {
+ CompoundTag tag = getConstantJavaTag(javaId, x, y, z);
+ tag.put(new ListTag("Patterns"));
+ return tag;
+ }
+
+ @Override
+ public com.nukkitx.nbt.tag.CompoundTag getDefaultBedrockTag(String bedrockId, int x, int y, int z) {
+ CompoundTagBuilder tagBuilder = getConstantBedrockTag(bedrockId, x, y, z).toBuilder();
+ tagBuilder.listTag("Patterns", com.nukkitx.nbt.tag.CompoundTag.class, new ArrayList<>());
+ return tagBuilder.buildRootTag();
+ }
+
+ /**
+ * Convert the Java edition pattern nbt to Bedrock edition, null if the pattern doesn't exist
+ *
+ * @param pattern Java edition pattern nbt
+ * @return The Bedrock edition format pattern nbt
+ */
+ protected com.nukkitx.nbt.tag.CompoundTag getPattern(CompoundTag pattern) {
+ String patternName = (String) pattern.get("Pattern").getValue();
+
+ // Return null if its the globe pattern as it doesn't exist on bedrock
+ if (patternName.equals("glb")) {
+ return null;
+ }
+
+ return CompoundTagBuilder.builder()
+ .intTag("Color", 15 - (int) pattern.get("Color").getValue())
+ .stringTag("Pattern", (String) pattern.get("Pattern").getValue())
+ .stringTag("Pattern", patternName)
+ .buildRootTag();
+ }
+}
diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/block/entity/BedBlockEntityTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/block/entity/BedBlockEntityTranslator.java
new file mode 100644
index 000000000..a8dae2532
--- /dev/null
+++ b/connector/src/main/java/org/geysermc/connector/network/translators/block/entity/BedBlockEntityTranslator.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright (c) 2019-2020 GeyserMC. http://geysermc.org
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ * @author GeyserMC
+ * @link https://github.com/GeyserMC/Geyser
+ */
+
+package org.geysermc.connector.network.translators.block.entity;
+
+import com.github.steveice10.mc.protocol.data.game.world.block.BlockState;
+import com.github.steveice10.opennbt.tag.builtin.CompoundTag;
+import com.nukkitx.nbt.CompoundTagBuilder;
+import com.nukkitx.nbt.tag.ByteTag;
+import com.nukkitx.nbt.tag.Tag;
+import org.geysermc.connector.network.translators.block.BlockStateValues;
+
+import java.util.ArrayList;
+import java.util.List;
+
+@BlockEntity(name = "Bed", delay = false, regex = "bed")
+public class BedBlockEntityTranslator extends BlockEntityTranslator implements RequiresBlockState {
+
+ @Override
+ public boolean isBlock(BlockState blockState) {
+ return BlockStateValues.getBedColor(blockState) != -1;
+ }
+
+ @Override
+ public List> translateTag(CompoundTag tag, BlockState blockState) {
+ List> tags = new ArrayList<>();
+ byte bedcolor = BlockStateValues.getBedColor(blockState);
+ // Just in case...
+ if (bedcolor == -1) bedcolor = 0;
+ tags.add(new ByteTag("color", bedcolor));
+ return tags;
+ }
+
+ @Override
+ public CompoundTag getDefaultJavaTag(String javaId, int x, int y, int z) {
+ return null;
+ }
+
+ @Override
+ public com.nukkitx.nbt.tag.CompoundTag getDefaultBedrockTag(String bedrockId, int x, int y, int z) {
+ CompoundTagBuilder tagBuilder = getConstantBedrockTag(bedrockId, x, y, z).toBuilder();
+ tagBuilder.byteTag("color", (byte) 0);
+ return tagBuilder.buildRootTag();
+ }
+}
diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/block/entity/BlockEntity.java b/connector/src/main/java/org/geysermc/connector/network/translators/block/entity/BlockEntity.java
new file mode 100644
index 000000000..47cbbaf30
--- /dev/null
+++ b/connector/src/main/java/org/geysermc/connector/network/translators/block/entity/BlockEntity.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright (c) 2019-2020 GeyserMC. http://geysermc.org
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ * @author GeyserMC
+ * @link https://github.com/GeyserMC/Geyser
+ */
+
+package org.geysermc.connector.network.translators.block.entity;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+@Retention(value = RetentionPolicy.RUNTIME)
+public @interface BlockEntity {
+
+ /**
+ * Whether to delay the sending of the block entity
+ * @return the delay for when sending the block entity
+ */
+ boolean delay();
+
+ /**
+ * The block entity name
+ * @return the name of the block entity
+ */
+ String name();
+
+ /**
+ * The search term used in BlockTranslator
+ * @return the search term used in BlockTranslator
+ */
+ String regex();
+}
diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/block/entity/BlockEntityTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/block/entity/BlockEntityTranslator.java
new file mode 100644
index 000000000..f28257898
--- /dev/null
+++ b/connector/src/main/java/org/geysermc/connector/network/translators/block/entity/BlockEntityTranslator.java
@@ -0,0 +1,79 @@
+/*
+ * 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.block.entity;
+
+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.IntTag;
+import com.github.steveice10.opennbt.tag.builtin.StringTag;
+import com.nukkitx.nbt.CompoundTagBuilder;
+import com.nukkitx.nbt.tag.Tag;
+
+import org.geysermc.connector.utils.BlockEntityUtils;
+
+import java.util.List;
+
+public abstract class BlockEntityTranslator {
+
+ public abstract List> translateTag(CompoundTag tag, BlockState blockState);
+
+ public abstract CompoundTag getDefaultJavaTag(String javaId, int x, int y, int z);
+
+ public abstract com.nukkitx.nbt.tag.CompoundTag getDefaultBedrockTag(String bedrockId, int x, int y, int z);
+
+ public com.nukkitx.nbt.tag.CompoundTag getBlockEntityTag(String id, CompoundTag tag, BlockState blockState) {
+ int x = Integer.parseInt(String.valueOf(tag.getValue().get("x").getValue()));
+ int y = Integer.parseInt(String.valueOf(tag.getValue().get("y").getValue()));
+ int z = Integer.parseInt(String.valueOf(tag.getValue().get("z").getValue()));
+
+ CompoundTagBuilder tagBuilder = getConstantBedrockTag(BlockEntityUtils.getBedrockBlockEntityId(id), x, y, z).toBuilder();
+ translateTag(tag, blockState).forEach(tagBuilder::tag);
+ return tagBuilder.buildRootTag();
+ }
+
+ protected CompoundTag getConstantJavaTag(String javaId, int x, int y, int z) {
+ CompoundTag tag = new CompoundTag("");
+ tag.put(new IntTag("x", x));
+ tag.put(new IntTag("y", y));
+ tag.put(new IntTag("z", z));
+ tag.put(new StringTag("id", javaId));
+ return tag;
+ }
+
+ protected com.nukkitx.nbt.tag.CompoundTag getConstantBedrockTag(String bedrockId, int x, int y, int z) {
+ CompoundTagBuilder tagBuilder = CompoundTagBuilder.builder()
+ .intTag("x", x)
+ .intTag("y", y)
+ .intTag("z", z)
+ .stringTag("id", bedrockId);
+ return tagBuilder.buildRootTag();
+ }
+
+ @SuppressWarnings("unchecked")
+ protected T getOrDefault(com.github.steveice10.opennbt.tag.builtin.Tag tag, T defaultValue) {
+ return (tag != null && tag.getValue() != null) ? (T) tag.getValue() : defaultValue;
+ }
+}
diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/block/entity/CampfireBlockEntityTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/block/entity/CampfireBlockEntityTranslator.java
new file mode 100644
index 000000000..11b7e8649
--- /dev/null
+++ b/connector/src/main/java/org/geysermc/connector/network/translators/block/entity/CampfireBlockEntityTranslator.java
@@ -0,0 +1,82 @@
+/*
+ * Copyright (c) 2019-2020 GeyserMC. http://geysermc.org
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ * @author GeyserMC
+ * @link https://github.com/GeyserMC/Geyser
+ */
+
+package org.geysermc.connector.network.translators.block.entity;
+
+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.ListTag;
+import com.nukkitx.nbt.CompoundTagBuilder;
+import com.nukkitx.nbt.tag.Tag;
+
+import org.geysermc.connector.network.translators.Translators;
+import org.geysermc.connector.network.translators.item.ItemEntry;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+
+@BlockEntity(name = "Campfire", delay = false, regex = "campfire")
+public class CampfireBlockEntityTranslator extends BlockEntityTranslator {
+
+ @Override
+ public List> translateTag(CompoundTag tag, BlockState blockState) {
+ List> tags = new ArrayList<>();
+ ListTag items = tag.get("Items");
+ int i = 1;
+ for (com.github.steveice10.opennbt.tag.builtin.Tag itemTag : items.getValue()) {
+ tags.add(getItem((CompoundTag) itemTag).toBuilder().build("Item" + i));
+ i++;
+ }
+ return tags;
+ }
+
+ @Override
+ public CompoundTag getDefaultJavaTag(String javaId, int x, int y, int z) {
+ CompoundTag tag = getConstantJavaTag(javaId, x, y, z);
+ tag.put(new ListTag("Items"));
+ return tag;
+ }
+
+ @Override
+ public com.nukkitx.nbt.tag.CompoundTag getDefaultBedrockTag(String bedrockId, int x, int y, int z) {
+ CompoundTagBuilder tagBuilder = getConstantBedrockTag(bedrockId, x, y, z).toBuilder();
+ tagBuilder.tag(new com.nukkitx.nbt.tag.CompoundTag("Item1", new HashMap<>()));
+ tagBuilder.tag(new com.nukkitx.nbt.tag.CompoundTag("Item2", new HashMap<>()));
+ tagBuilder.tag(new com.nukkitx.nbt.tag.CompoundTag("Item3", new HashMap<>()));
+ tagBuilder.tag(new com.nukkitx.nbt.tag.CompoundTag("Item4", new HashMap<>()));
+ return tagBuilder.buildRootTag();
+ }
+
+ protected com.nukkitx.nbt.tag.CompoundTag getItem(CompoundTag tag) {
+ ItemEntry entry = Translators.getItemTranslator().getItemEntry((String) tag.get("id").getValue());
+ CompoundTagBuilder tagBuilder = CompoundTagBuilder.builder()
+ .shortTag("id", (short) entry.getBedrockId())
+ .byteTag("Count", (byte) tag.get("Count").getValue())
+ .shortTag("Damage", (short) entry.getBedrockData())
+ .tag(CompoundTagBuilder.builder().build("tag"));
+ return tagBuilder.buildRootTag();
+ }
+}
diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/block/entity/EmptyBlockEntityTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/block/entity/EmptyBlockEntityTranslator.java
new file mode 100644
index 000000000..e46014008
--- /dev/null
+++ b/connector/src/main/java/org/geysermc/connector/network/translators/block/entity/EmptyBlockEntityTranslator.java
@@ -0,0 +1,52 @@
+/*
+ * 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.block.entity;
+
+import com.github.steveice10.mc.protocol.data.game.world.block.BlockState;
+import com.github.steveice10.opennbt.tag.builtin.CompoundTag;
+import com.nukkitx.nbt.tag.Tag;
+
+import java.util.ArrayList;
+import java.util.List;
+
+@BlockEntity(name = "Empty", delay = false, regex = "")
+public class EmptyBlockEntityTranslator extends BlockEntityTranslator {
+
+ @Override
+ public List> translateTag(CompoundTag tag, BlockState blockState) {
+ return new ArrayList<>();
+ }
+
+ @Override
+ public CompoundTag getDefaultJavaTag(String javaId, int x, int y, int z) {
+ return getConstantJavaTag(javaId, x, y, z);
+ }
+
+ @Override
+ public com.nukkitx.nbt.tag.CompoundTag getDefaultBedrockTag(String bedrockId, int x, int y, int z) {
+ return getConstantBedrockTag(bedrockId, x, y, z);
+ }
+}
diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/block/entity/EndGatewayBlockEntityTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/block/entity/EndGatewayBlockEntityTranslator.java
new file mode 100644
index 000000000..de5868a4b
--- /dev/null
+++ b/connector/src/main/java/org/geysermc/connector/network/translators/block/entity/EndGatewayBlockEntityTranslator.java
@@ -0,0 +1,85 @@
+/*
+ * 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.block.entity;
+
+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.LongTag;
+import com.nukkitx.nbt.CompoundTagBuilder;
+import com.nukkitx.nbt.tag.IntTag;
+import com.nukkitx.nbt.tag.Tag;
+
+import java.util.ArrayList;
+import java.util.LinkedHashMap;
+import java.util.List;
+
+@BlockEntity(name = "EndGateway", delay = true, regex = "end_gateway")
+public class EndGatewayBlockEntityTranslator extends BlockEntityTranslator {
+
+ @Override
+ public List> translateTag(CompoundTag tag, BlockState blockState) {
+ List> tags = new ArrayList<>();
+ tags.add(new IntTag("Age", (int) (long) tag.get("Age").getValue()));
+ // Java sometimes does not provide this tag, but Bedrock crashes if it doesn't exist
+ // Linked coordinates
+ List tagsList = new ArrayList<>();
+ // Yes, the axis letters are capitalized
+ tagsList.add(new IntTag("", getExitPortalCoordinate(tag, "X")));
+ tagsList.add(new IntTag("", getExitPortalCoordinate(tag, "Y")));
+ tagsList.add(new IntTag("", getExitPortalCoordinate(tag, "Z")));
+ com.nukkitx.nbt.tag.ListTag exitPortal =
+ new com.nukkitx.nbt.tag.ListTag<>("ExitPortal", IntTag.class, tagsList);
+ tags.add(exitPortal);
+ return tags;
+ }
+
+ @Override
+ public CompoundTag getDefaultJavaTag(String javaId, int x, int y, int z) {
+ CompoundTag tag = getConstantJavaTag(javaId, x, y, z);
+ tag.put(new LongTag("Age"));
+ return tag;
+ }
+
+ @Override
+ public com.nukkitx.nbt.tag.CompoundTag getDefaultBedrockTag(String bedrockId, int x, int y, int z) {
+ CompoundTagBuilder tagBuilder = getConstantBedrockTag(bedrockId, x, y, z).toBuilder();
+ List tagsList = new ArrayList<>();
+ tagsList.add(new IntTag("", 0));
+ tagsList.add(new IntTag("", 0));
+ tagsList.add(new IntTag("", 0));
+ tagBuilder.listTag("ExitPortal", IntTag.class, tagsList);
+ return tagBuilder.buildRootTag();
+ }
+
+ private int getExitPortalCoordinate(CompoundTag tag, String axis) {
+ // Return 0 if it doesn't exist, otherwise give proper value
+ if (tag.get("ExitPortal") != null) {
+ LinkedHashMap compoundTag = (LinkedHashMap) tag.get("ExitPortal").getValue();
+ com.github.steveice10.opennbt.tag.builtin.IntTag intTag = (com.github.steveice10.opennbt.tag.builtin.IntTag) compoundTag.get(axis);
+ return intTag.getValue();
+ } return 0;
+ }
+}
diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/block/entity/RequiresBlockState.java b/connector/src/main/java/org/geysermc/connector/network/translators/block/entity/RequiresBlockState.java
new file mode 100644
index 000000000..ed8e6ede9
--- /dev/null
+++ b/connector/src/main/java/org/geysermc/connector/network/translators/block/entity/RequiresBlockState.java
@@ -0,0 +1,42 @@
+/*
+ * 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.block.entity;
+
+import com.github.steveice10.mc.protocol.data.game.world.block.BlockState;
+
+/**
+ * Implemented in block entities if their Java block state is required for additional values in Bedrock
+ */
+public interface RequiresBlockState {
+
+ /**
+ * Determines if block is part of class
+ * @param blockState BlockState to be compared
+ * @return true if part of the class
+ */
+ boolean isBlock(BlockState blockState);
+
+}
diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/block/entity/SignBlockEntityTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/block/entity/SignBlockEntityTranslator.java
new file mode 100644
index 000000000..74dcdd13e
--- /dev/null
+++ b/connector/src/main/java/org/geysermc/connector/network/translators/block/entity/SignBlockEntityTranslator.java
@@ -0,0 +1,76 @@
+/*
+ * 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.block.entity;
+
+import com.github.steveice10.mc.protocol.data.game.world.block.BlockState;
+import com.github.steveice10.mc.protocol.data.message.Message;
+import com.github.steveice10.opennbt.tag.builtin.CompoundTag;
+import com.nukkitx.nbt.CompoundTagBuilder;
+import com.nukkitx.nbt.tag.StringTag;
+import com.nukkitx.nbt.tag.Tag;
+import org.geysermc.connector.utils.MessageUtils;
+
+import java.util.ArrayList;
+import java.util.List;
+
+@BlockEntity(name = "Sign", delay = true, regex = "sign")
+public class SignBlockEntityTranslator extends BlockEntityTranslator {
+
+ @Override
+ public List> translateTag(CompoundTag tag, BlockState blockState) {
+ List> tags = new ArrayList<>();
+
+ String line1 = getOrDefault(tag.getValue().get("Text1"), "");
+ String line2 = getOrDefault(tag.getValue().get("Text2"), "");
+ String line3 = getOrDefault(tag.getValue().get("Text3"), "");
+ String line4 = getOrDefault(tag.getValue().get("Text4"), "");
+
+ tags.add(new StringTag("Text", MessageUtils.getBedrockMessage(Message.fromString(line1))
+ + "\n" + MessageUtils.getBedrockMessage(Message.fromString(line2))
+ + "\n" + MessageUtils.getBedrockMessage(Message.fromString(line3))
+ + "\n" + MessageUtils.getBedrockMessage(Message.fromString(line4))
+ ));
+
+ return tags;
+ }
+
+ @Override
+ public CompoundTag getDefaultJavaTag(String javaId, int x, int y, int z) {
+ CompoundTag tag = getConstantJavaTag(javaId, x, y, z);
+ tag.put(new com.github.steveice10.opennbt.tag.builtin.StringTag("Text1", "{\"text\":\"\"}"));
+ tag.put(new com.github.steveice10.opennbt.tag.builtin.StringTag("Text2", "{\"text\":\"\"}"));
+ tag.put(new com.github.steveice10.opennbt.tag.builtin.StringTag("Text3", "{\"text\":\"\"}"));
+ tag.put(new com.github.steveice10.opennbt.tag.builtin.StringTag("Text4", "{\"text\":\"\"}"));
+ return tag;
+ }
+
+ @Override
+ public com.nukkitx.nbt.tag.CompoundTag getDefaultBedrockTag(String bedrockId, int x, int y, int z) {
+ CompoundTagBuilder tagBuilder = getConstantBedrockTag(bedrockId, x, y, z).toBuilder();
+ tagBuilder.stringTag("Text", "");
+ return tagBuilder.buildRootTag();
+ }
+}
diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/block/entity/SkullBlockEntityTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/block/entity/SkullBlockEntityTranslator.java
new file mode 100644
index 000000000..2380663b1
--- /dev/null
+++ b/connector/src/main/java/org/geysermc/connector/network/translators/block/entity/SkullBlockEntityTranslator.java
@@ -0,0 +1,71 @@
+/*
+ * 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.block.entity;
+
+import com.github.steveice10.mc.protocol.data.game.world.block.BlockState;
+import com.nukkitx.nbt.CompoundTagBuilder;
+import com.nukkitx.nbt.tag.ByteTag;
+import com.nukkitx.nbt.tag.CompoundTag;
+import com.nukkitx.nbt.tag.FloatTag;
+import com.nukkitx.nbt.tag.Tag;
+import org.geysermc.connector.network.translators.block.BlockStateValues;
+
+import java.util.ArrayList;
+import java.util.List;
+
+@BlockEntity(name = "Skull", delay = false, regex = "skull")
+public class SkullBlockEntityTranslator extends BlockEntityTranslator implements RequiresBlockState {
+
+ @Override
+ public boolean isBlock(BlockState blockState) {
+ return BlockStateValues.getSkullVariant(blockState) != -1;
+ }
+
+ @Override
+ public List> translateTag(com.github.steveice10.opennbt.tag.builtin.CompoundTag tag, BlockState blockState) {
+ List> tags = new ArrayList<>();
+ byte skullVariant = BlockStateValues.getSkullVariant(blockState);
+ float rotation = BlockStateValues.getSkullRotation(blockState) * 22.5f;
+ // Just in case...
+ if (skullVariant == -1) skullVariant = 0;
+ tags.add(new FloatTag("Rotation", rotation));
+ tags.add(new ByteTag("SkullType", skullVariant));
+ return tags;
+ }
+
+ @Override
+ public com.github.steveice10.opennbt.tag.builtin.CompoundTag getDefaultJavaTag(String javaId, int x, int y, int z) {
+ return null;
+ }
+
+ @Override
+ public CompoundTag getDefaultBedrockTag(String bedrockId, int x, int y, int z) {
+ CompoundTagBuilder tagBuilder = getConstantBedrockTag(bedrockId, x, y, z).toBuilder();
+ tagBuilder.floatTag("Rotation", 0);
+ tagBuilder.byteTag("SkullType", (byte) 0);
+ return tagBuilder.buildRootTag();
+ }
+}
diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/inventory/AnvilInventoryTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/AnvilInventoryTranslator.java
new file mode 100644
index 000000000..8df72d362
--- /dev/null
+++ b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/AnvilInventoryTranslator.java
@@ -0,0 +1,146 @@
+/*
+ * 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.github.steveice10.mc.protocol.data.game.entity.metadata.ItemStack;
+import com.github.steveice10.mc.protocol.data.message.Message;
+import com.github.steveice10.mc.protocol.packet.ingame.client.window.ClientRenameItemPacket;
+import com.github.steveice10.opennbt.tag.builtin.CompoundTag;
+import com.nukkitx.protocol.bedrock.data.ContainerId;
+import com.nukkitx.protocol.bedrock.data.ContainerType;
+import com.nukkitx.protocol.bedrock.data.InventoryActionData;
+import com.nukkitx.protocol.bedrock.data.ItemData;
+import org.geysermc.connector.inventory.Inventory;
+import org.geysermc.connector.network.session.GeyserSession;
+import org.geysermc.connector.network.translators.inventory.updater.CursorInventoryUpdater;
+
+import java.util.List;
+
+public class AnvilInventoryTranslator extends BlockInventoryTranslator {
+ public AnvilInventoryTranslator() {
+ super(3, "minecraft:anvil[facing=north]", ContainerType.ANVIL, new CursorInventoryUpdater());
+ }
+
+ @Override
+ public int bedrockSlotToJava(InventoryActionData action) {
+ if (action.getSource().getContainerId() == ContainerId.CURSOR) {
+ switch (action.getSlot()) {
+ case 1:
+ return 0;
+ case 2:
+ return 1;
+ case 50:
+ return 2;
+ }
+ }
+ return super.bedrockSlotToJava(action);
+ }
+
+ @Override
+ public int javaSlotToBedrock(int slot) {
+ switch (slot) {
+ case 0:
+ return 1;
+ case 1:
+ return 2;
+ case 2:
+ return 50;
+ }
+ return super.javaSlotToBedrock(slot);
+ }
+
+ @Override
+ public SlotType getSlotType(int javaSlot) {
+ if (javaSlot == 2)
+ return SlotType.OUTPUT;
+ return SlotType.NORMAL;
+ }
+
+ @Override
+ public void translateActions(GeyserSession session, Inventory inventory, List actions) {
+ InventoryActionData anvilResult = null;
+ InventoryActionData anvilInput = null;
+ for (InventoryActionData action : actions) {
+ if (action.getSource().getContainerId() == ContainerId.ANVIL_MATERIAL) {
+ //useless packet
+ return;
+ } else if (action.getSource().getContainerId() == ContainerId.ANVIL_RESULT) {
+ anvilResult = action;
+ } else if (bedrockSlotToJava(action) == 0) {
+ anvilInput = action;
+ }
+ }
+ ItemData itemName = null;
+ if (anvilResult != null) {
+ itemName = anvilResult.getFromItem();
+ } else if (anvilInput != null) {
+ itemName = anvilInput.getToItem();
+ }
+ if (itemName != null) {
+ String rename;
+ com.nukkitx.nbt.tag.CompoundTag tag = itemName.getTag();
+ if (tag != null) {
+ rename = tag.getCompound("display").getString("Name");
+ } else {
+ rename = "";
+ }
+ ClientRenameItemPacket renameItemPacket = new ClientRenameItemPacket(rename);
+ session.getDownstream().getSession().send(renameItemPacket);
+ }
+ if (anvilResult != null) {
+ //client will send another packet to grab anvil output
+ return;
+ }
+
+ super.translateActions(session, inventory, actions);
+ }
+
+ @Override
+ public void updateSlot(GeyserSession session, Inventory inventory, int slot) {
+ if (slot >= 0 && slot <= 2) {
+ ItemStack item = inventory.getItem(slot);
+ if (item != null) {
+ String rename;
+ CompoundTag tag = item.getNbt();
+ if (tag != null) {
+ CompoundTag displayTag = tag.get("display");
+ if (displayTag != null) {
+ String itemName = displayTag.get("Name").getValue().toString();
+ Message message = Message.fromString(itemName);
+ rename = message.getText();
+ } else {
+ rename = "";
+ }
+ } else {
+ rename = "";
+ }
+ ClientRenameItemPacket renameItemPacket = new ClientRenameItemPacket(rename);
+ session.getDownstream().getSession().send(renameItemPacket);
+ }
+ }
+ super.updateSlot(session, inventory, slot);
+ }
+}
diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/inventory/BaseInventoryTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/BaseInventoryTranslator.java
new file mode 100644
index 000000000..5deb0370a
--- /dev/null
+++ b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/BaseInventoryTranslator.java
@@ -0,0 +1,82 @@
+/*
+ * Copyright (c) 2019-2020 GeyserMC. http://geysermc.org
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ * @author GeyserMC
+ * @link https://github.com/GeyserMC/Geyser
+ */
+
+package org.geysermc.connector.network.translators.inventory;
+
+import com.nukkitx.protocol.bedrock.data.ContainerId;
+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.action.InventoryActionDataTranslator;
+
+import java.util.List;
+
+public abstract class BaseInventoryTranslator extends InventoryTranslator{
+ BaseInventoryTranslator(int size) {
+ super(size);
+ }
+
+ @Override
+ public void updateProperty(GeyserSession session, Inventory inventory, int key, int value) {
+ //
+ }
+
+ @Override
+ public int bedrockSlotToJava(InventoryActionData action) {
+ int slotnum = action.getSlot();
+ if (action.getSource().getContainerId() == ContainerId.INVENTORY) {
+ //hotbar
+ if (slotnum >= 9) {
+ return slotnum + this.size - 9;
+ } else {
+ return slotnum + this.size + 27;
+ }
+ }
+ return slotnum;
+ }
+
+ @Override
+ public int javaSlotToBedrock(int slot) {
+ if (slot >= this.size) {
+ final int tmp = slot - this.size;
+ if (tmp < 27) {
+ return tmp + 9;
+ } else {
+ return tmp - 27;
+ }
+ }
+ return slot;
+ }
+
+ @Override
+ public SlotType getSlotType(int javaSlot) {
+ return SlotType.NORMAL;
+ }
+
+ @Override
+ public void translateActions(GeyserSession session, Inventory inventory, List actions) {
+ InventoryActionDataTranslator.translate(this, session, inventory, actions);
+ }
+}
diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/inventory/BlockInventoryTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/BlockInventoryTranslator.java
new file mode 100644
index 000000000..5f6274f0a
--- /dev/null
+++ b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/BlockInventoryTranslator.java
@@ -0,0 +1,73 @@
+/*
+ * 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.github.steveice10.mc.protocol.data.game.world.block.BlockState;
+import com.nukkitx.protocol.bedrock.data.ContainerType;
+import org.geysermc.connector.inventory.Inventory;
+import org.geysermc.connector.network.session.GeyserSession;
+import org.geysermc.connector.network.translators.block.BlockTranslator;
+import org.geysermc.connector.network.translators.inventory.holder.BlockInventoryHolder;
+import org.geysermc.connector.network.translators.inventory.holder.InventoryHolder;
+import org.geysermc.connector.network.translators.inventory.updater.InventoryUpdater;
+
+public class BlockInventoryTranslator extends BaseInventoryTranslator {
+ private final InventoryHolder holder;
+ private final InventoryUpdater updater;
+
+ public BlockInventoryTranslator(int size, String javaBlockIdentifier, ContainerType containerType, InventoryUpdater updater) {
+ super(size);
+ BlockState javaBlockState = BlockTranslator.getJavaBlockState(javaBlockIdentifier);
+ int blockId = BlockTranslator.getBedrockBlockId(javaBlockState);
+ this.holder = new BlockInventoryHolder(blockId, containerType);
+ this.updater = updater;
+ }
+
+ @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);
+ }
+
+ @Override
+ public void updateInventory(GeyserSession session, Inventory inventory) {
+ updater.updateInventory(this, session, inventory);
+ }
+
+ @Override
+ public void updateSlot(GeyserSession session, Inventory inventory, int slot) {
+ updater.updateSlot(this, session, inventory, slot);
+ }
+}
diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/inventory/BrewingInventoryTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/BrewingInventoryTranslator.java
new file mode 100644
index 000000000..bd143698f
--- /dev/null
+++ b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/BrewingInventoryTranslator.java
@@ -0,0 +1,99 @@
+/*
+ * 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.ContainerType;
+import com.nukkitx.protocol.bedrock.data.InventoryActionData;
+import com.nukkitx.protocol.bedrock.packet.ContainerSetDataPacket;
+import org.geysermc.connector.inventory.Inventory;
+import org.geysermc.connector.network.session.GeyserSession;
+import org.geysermc.connector.network.translators.inventory.updater.ContainerInventoryUpdater;
+
+public class BrewingInventoryTranslator extends BlockInventoryTranslator {
+ public BrewingInventoryTranslator() {
+ super(5, "minecraft:brewing_stand[has_bottle_0=false,has_bottle_1=false,has_bottle_2=false]", ContainerType.BREWING_STAND, new ContainerInventoryUpdater());
+ }
+
+ @Override
+ public void openInventory(GeyserSession session, Inventory inventory) {
+ super.openInventory(session, inventory);
+ ContainerSetDataPacket dataPacket = new ContainerSetDataPacket();
+ dataPacket.setWindowId((byte) inventory.getId());
+ dataPacket.setProperty(ContainerSetDataPacket.BREWING_STAND_FUEL_TOTAL);
+ dataPacket.setValue(20);
+ session.getUpstream().sendPacket(dataPacket);
+ }
+
+ @Override
+ public void updateProperty(GeyserSession session, Inventory inventory, int key, int value) {
+ ContainerSetDataPacket dataPacket = new ContainerSetDataPacket();
+ dataPacket.setWindowId((byte) inventory.getId());
+ switch (key) {
+ case 0:
+ dataPacket.setProperty(ContainerSetDataPacket.BREWING_STAND_BREW_TIME);
+ break;
+ case 1:
+ dataPacket.setProperty(ContainerSetDataPacket.BREWING_STAND_FUEL_AMOUNT);
+ break;
+ default:
+ return;
+ }
+ dataPacket.setValue(value);
+ session.getUpstream().sendPacket(dataPacket);
+ }
+
+ @Override
+ public int bedrockSlotToJava(InventoryActionData action) {
+ final int slot = super.bedrockSlotToJava(action);
+ switch (slot) {
+ case 0:
+ return 3;
+ case 1:
+ return 0;
+ case 2:
+ return 1;
+ case 3:
+ return 2;
+ default:
+ return slot;
+ }
+ }
+
+ @Override
+ public int javaSlotToBedrock(int slot) {
+ switch (slot) {
+ case 0:
+ return 1;
+ case 1:
+ return 2;
+ case 2:
+ return 3;
+ case 3:
+ return 0;
+ }
+ return super.javaSlotToBedrock(slot);
+ }
+}
diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/inventory/CraftingInventoryTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/CraftingInventoryTranslator.java
new file mode 100644
index 000000000..92a1d90ec
--- /dev/null
+++ b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/CraftingInventoryTranslator.java
@@ -0,0 +1,118 @@
+/*
+ * 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.github.steveice10.mc.protocol.data.game.entity.player.GameMode;
+import com.nukkitx.protocol.bedrock.data.ContainerId;
+import com.nukkitx.protocol.bedrock.data.ContainerType;
+import com.nukkitx.protocol.bedrock.data.InventoryActionData;
+import com.nukkitx.protocol.bedrock.data.InventorySource;
+import com.nukkitx.protocol.bedrock.packet.ContainerOpenPacket;
+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 org.geysermc.connector.utils.InventoryUtils;
+
+import java.util.List;
+
+public class CraftingInventoryTranslator extends BaseInventoryTranslator {
+ private final InventoryUpdater updater;
+
+ public CraftingInventoryTranslator() {
+ super(10);
+ this.updater = new CursorInventoryUpdater();
+ }
+
+ @Override
+ public void prepareInventory(GeyserSession session, Inventory inventory) {
+ //
+ }
+
+ @Override
+ public void openInventory(GeyserSession session, Inventory inventory) {
+ ContainerOpenPacket containerOpenPacket = new ContainerOpenPacket();
+ containerOpenPacket.setWindowId((byte) inventory.getId());
+ containerOpenPacket.setType((byte) ContainerType.WORKBENCH.id());
+ containerOpenPacket.setBlockPosition(inventory.getHolderPosition());
+ containerOpenPacket.setUniqueEntityId(inventory.getHolderId());
+ session.getUpstream().sendPacket(containerOpenPacket);
+ }
+
+ @Override
+ public void closeInventory(GeyserSession session, Inventory inventory) {
+ //
+ }
+
+ @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 int bedrockSlotToJava(InventoryActionData action) {
+ if (action.getSource().getContainerId() == ContainerId.CURSOR) {
+ int slotnum = action.getSlot();
+ if (slotnum >= 32 && 42 >= slotnum) {
+ return slotnum - 31;
+ } else if (slotnum == 50) {
+ return 0;
+ }
+ }
+ return super.bedrockSlotToJava(action);
+ }
+
+ @Override
+ public int javaSlotToBedrock(int slot) {
+ return slot == 0 ? 50 : slot + 31;
+ }
+
+ @Override
+ public SlotType getSlotType(int javaSlot) {
+ if (javaSlot == 0)
+ return SlotType.OUTPUT;
+ return SlotType.NORMAL;
+ }
+
+ @Override
+ public void translateActions(GeyserSession session, Inventory inventory, List actions) {
+ if (session.getGameMode() == GameMode.CREATIVE) {
+ for (InventoryActionData action : actions) {
+ if (action.getSource().getType() == InventorySource.Type.CREATIVE) {
+ updateInventory(session, inventory);
+ InventoryUtils.updateCursor(session);
+ return;
+ }
+ }
+ }
+ super.translateActions(session, inventory, actions);
+ }
+}
diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/inventory/DoubleChestInventoryTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/DoubleChestInventoryTranslator.java
new file mode 100644
index 000000000..c70a89955
--- /dev/null
+++ b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/DoubleChestInventoryTranslator.java
@@ -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.inventory;
+
+import com.github.steveice10.mc.protocol.data.game.entity.metadata.Position;
+import com.github.steveice10.mc.protocol.data.game.world.block.BlockState;
+import com.nukkitx.math.vector.Vector3i;
+import com.nukkitx.nbt.tag.CompoundTag;
+import com.nukkitx.protocol.bedrock.data.ContainerType;
+import com.nukkitx.protocol.bedrock.packet.BlockEntityDataPacket;
+import com.nukkitx.protocol.bedrock.packet.ContainerOpenPacket;
+import com.nukkitx.protocol.bedrock.packet.UpdateBlockPacket;
+import org.geysermc.connector.inventory.Inventory;
+import org.geysermc.connector.network.session.GeyserSession;
+import org.geysermc.connector.network.translators.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 {
+ private final int blockId;
+ private final InventoryUpdater updater;
+
+ public DoubleChestInventoryTranslator(int size) {
+ super(size);
+ BlockState javaBlockState = BlockTranslator.getJavaBlockState("minecraft:chest[facing=north,type=single,waterlogged=false]");
+ this.blockId = BlockTranslator.getBedrockBlockId(javaBlockState);
+ this.updater = new ChestInventoryUpdater(54);
+ }
+
+ @Override
+ public void prepareInventory(GeyserSession session, Inventory inventory) {
+ Vector3i position = session.getPlayerEntity().getPosition().toInt().add(Vector3i.UP);
+ Vector3i pairPosition = position.add(Vector3i.UNIT_X);
+
+ UpdateBlockPacket blockPacket = new UpdateBlockPacket();
+ blockPacket.setDataLayer(0);
+ blockPacket.setBlockPosition(position);
+ blockPacket.setRuntimeId(blockId);
+ blockPacket.getFlags().addAll(UpdateBlockPacket.FLAG_ALL_PRIORITY);
+ session.getUpstream().sendPacket(blockPacket);
+
+ CompoundTag tag = CompoundTag.builder()
+ .stringTag("id", "Chest")
+ .intTag("x", position.getX())
+ .intTag("y", position.getY())
+ .intTag("z", position.getZ())
+ .intTag("pairx", pairPosition.getX())
+ .intTag("pairz", pairPosition.getZ())
+ .stringTag("CustomName", inventory.getTitle()).buildRootTag();
+ BlockEntityDataPacket dataPacket = new BlockEntityDataPacket();
+ dataPacket.setData(tag);
+ dataPacket.setBlockPosition(position);
+ session.getUpstream().sendPacket(dataPacket);
+
+ blockPacket = new UpdateBlockPacket();
+ blockPacket.setDataLayer(0);
+ blockPacket.setBlockPosition(pairPosition);
+ blockPacket.setRuntimeId(blockId);
+ blockPacket.getFlags().addAll(UpdateBlockPacket.FLAG_ALL_PRIORITY);
+ session.getUpstream().sendPacket(blockPacket);
+
+ tag = CompoundTag.builder()
+ .stringTag("id", "Chest")
+ .intTag("x", pairPosition.getX())
+ .intTag("y", pairPosition.getY())
+ .intTag("z", pairPosition.getZ())
+ .intTag("pairx", position.getX())
+ .intTag("pairz", position.getZ())
+ .stringTag("CustomName", inventory.getTitle()).buildRootTag();
+ dataPacket = new BlockEntityDataPacket();
+ dataPacket.setData(tag);
+ dataPacket.setBlockPosition(pairPosition);
+ session.getUpstream().sendPacket(dataPacket);
+
+ inventory.setHolderPosition(position);
+ }
+
+ @Override
+ public void openInventory(GeyserSession session, Inventory inventory) {
+ ContainerOpenPacket containerOpenPacket = new ContainerOpenPacket();
+ containerOpenPacket.setWindowId((byte) inventory.getId());
+ containerOpenPacket.setType((byte) ContainerType.CONTAINER.id());
+ containerOpenPacket.setBlockPosition(inventory.getHolderPosition());
+ containerOpenPacket.setUniqueEntityId(inventory.getHolderId());
+ session.getUpstream().sendPacket(containerOpenPacket);
+ }
+
+ @Override
+ public void closeInventory(GeyserSession session, Inventory inventory) {
+ Vector3i holderPos = inventory.getHolderPosition();
+ Position pos = new Position(holderPos.getX(), holderPos.getY(), holderPos.getZ());
+ BlockState realBlock = session.getChunkCache().getBlockAt(pos);
+ UpdateBlockPacket blockPacket = new UpdateBlockPacket();
+ blockPacket.setDataLayer(0);
+ blockPacket.setBlockPosition(holderPos);
+ blockPacket.setRuntimeId(BlockTranslator.getBedrockBlockId(realBlock));
+ session.getUpstream().sendPacket(blockPacket);
+
+ holderPos = holderPos.add(Vector3i.UNIT_X);
+ pos = new Position(holderPos.getX(), holderPos.getY(), holderPos.getZ());
+ realBlock = session.getChunkCache().getBlockAt(pos);
+ blockPacket = new UpdateBlockPacket();
+ blockPacket.setDataLayer(0);
+ blockPacket.setBlockPosition(holderPos);
+ blockPacket.setRuntimeId(BlockTranslator.getBedrockBlockId(realBlock));
+ session.getUpstream().sendPacket(blockPacket);
+ }
+
+ @Override
+ public void updateInventory(GeyserSession session, Inventory inventory) {
+ updater.updateInventory(this, session, inventory);
+ }
+
+ @Override
+ public void updateSlot(GeyserSession session, Inventory inventory, int slot) {
+ updater.updateSlot(this, session, inventory, slot);
+ }
+}
diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/inventory/EnchantmentInventoryTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/EnchantmentInventoryTranslator.java
new file mode 100644
index 000000000..ba7f8cc7a
--- /dev/null
+++ b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/EnchantmentInventoryTranslator.java
@@ -0,0 +1,42 @@
+/*
+ * 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.ContainerType;
+import org.geysermc.connector.inventory.Inventory;
+import org.geysermc.connector.network.session.GeyserSession;
+import org.geysermc.connector.network.translators.inventory.updater.ContainerInventoryUpdater;
+
+public class EnchantmentInventoryTranslator extends BlockInventoryTranslator {
+ public EnchantmentInventoryTranslator() {
+ super(2, "minecraft:enchanting_table", ContainerType.ENCHANTMENT, new ContainerInventoryUpdater());
+ }
+
+ @Override
+ public void updateProperty(GeyserSession session, Inventory inventory, int key, int value) {
+
+ }
+}
diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/inventory/FurnaceInventoryTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/FurnaceInventoryTranslator.java
new file mode 100644
index 000000000..9b45201ed
--- /dev/null
+++ b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/FurnaceInventoryTranslator.java
@@ -0,0 +1,70 @@
+/*
+ * 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.github.steveice10.mc.protocol.data.game.window.WindowType;
+import com.nukkitx.protocol.bedrock.data.ContainerType;
+import com.nukkitx.protocol.bedrock.packet.ContainerSetDataPacket;
+import org.geysermc.connector.inventory.Inventory;
+import org.geysermc.connector.network.session.GeyserSession;
+import org.geysermc.connector.network.translators.inventory.updater.ContainerInventoryUpdater;
+
+public class FurnaceInventoryTranslator extends BlockInventoryTranslator {
+ public FurnaceInventoryTranslator() {
+ super(3, "minecraft:furnace[facing=north,lit=false]", ContainerType.FURNACE, new ContainerInventoryUpdater());
+ }
+
+ @Override
+ public void updateProperty(GeyserSession session, Inventory inventory, int key, int value) {
+ ContainerSetDataPacket dataPacket = new ContainerSetDataPacket();
+ dataPacket.setWindowId((byte) inventory.getId());
+ switch (key) {
+ case 0:
+ dataPacket.setProperty(ContainerSetDataPacket.FURNACE_LIT_TIME);
+ break;
+ case 1:
+ dataPacket.setProperty(ContainerSetDataPacket.FURNACE_LIT_DURATION);
+ break;
+ case 2:
+ dataPacket.setProperty(ContainerSetDataPacket.FURNACE_TICK_COUNT);
+ if (inventory.getWindowType() == WindowType.BLAST_FURNACE || inventory.getWindowType() == WindowType.SMOKER) {
+ value *= 2;
+ }
+ break;
+ default:
+ return;
+ }
+ dataPacket.setValue(value);
+ session.getUpstream().sendPacket(dataPacket);
+ }
+
+ @Override
+ public SlotType getSlotType(int javaSlot) {
+ if (javaSlot == 2)
+ return SlotType.FURNACE_OUTPUT;
+ return SlotType.NORMAL;
+ }
+}
diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/inventory/GrindstoneInventoryTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/GrindstoneInventoryTranslator.java
new file mode 100644
index 000000000..174cfbc11
--- /dev/null
+++ b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/GrindstoneInventoryTranslator.java
@@ -0,0 +1,68 @@
+/*
+ * 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.ContainerType;
+import com.nukkitx.protocol.bedrock.data.InventoryActionData;
+import org.geysermc.connector.network.translators.inventory.updater.CursorInventoryUpdater;
+
+public class GrindstoneInventoryTranslator extends BlockInventoryTranslator {
+
+ public GrindstoneInventoryTranslator() {
+ super(3, "minecraft:grindstone[face=floor,facing=north]", ContainerType.GRINDSTONE, new CursorInventoryUpdater());
+ }
+
+ @Override
+ public int bedrockSlotToJava(InventoryActionData action) {
+ final int slot = super.bedrockSlotToJava(action);
+ if (action.getSource().getContainerId() == 124) {
+ switch (slot) {
+ case 16:
+ return 0;
+ case 17:
+ return 1;
+ case 50:
+ return 2;
+ default:
+ return slot;
+ }
+ } return slot;
+ }
+
+ @Override
+ public int javaSlotToBedrock(int slot) {
+ switch (slot) {
+ case 0:
+ return 16;
+ case 1:
+ return 17;
+ case 2:
+ return 50;
+ }
+ return super.javaSlotToBedrock(slot);
+ }
+
+}
diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/inventory/InventoryTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/InventoryTranslator.java
index f17aba57e..2a5afb8c0 100644
--- a/connector/src/main/java/org/geysermc/connector/network/translators/inventory/InventoryTranslator.java
+++ b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/InventoryTranslator.java
@@ -25,14 +25,25 @@
package org.geysermc.connector.network.translators.inventory;
+import com.nukkitx.protocol.bedrock.data.InventoryActionData;
+import lombok.AllArgsConstructor;
import org.geysermc.connector.inventory.Inventory;
import org.geysermc.connector.network.session.GeyserSession;
+import java.util.List;
+
+@AllArgsConstructor
public abstract class InventoryTranslator {
+ public final int size;
public abstract void prepareInventory(GeyserSession session, Inventory inventory);
public abstract void openInventory(GeyserSession session, Inventory inventory);
+ public abstract void closeInventory(GeyserSession session, Inventory inventory);
+ public abstract void updateProperty(GeyserSession session, Inventory inventory, int key, int value);
public abstract void updateInventory(GeyserSession session, Inventory inventory);
public abstract void updateSlot(GeyserSession session, Inventory inventory, int slot);
-
+ public abstract int bedrockSlotToJava(InventoryActionData action);
+ public abstract int javaSlotToBedrock(int slot);
+ public abstract SlotType getSlotType(int javaSlot);
+ public abstract void translateActions(GeyserSession session, Inventory inventory, List actions);
}
diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/inventory/PlayerInventoryTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/PlayerInventoryTranslator.java
new file mode 100644
index 000000000..668612c53
--- /dev/null
+++ b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/PlayerInventoryTranslator.java
@@ -0,0 +1,246 @@
+/*
+ * 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.github.steveice10.mc.protocol.data.game.entity.metadata.ItemStack;
+import com.github.steveice10.mc.protocol.data.game.entity.player.GameMode;
+import com.github.steveice10.mc.protocol.packet.ingame.client.window.ClientCreativeInventoryActionPacket;
+import com.nukkitx.protocol.bedrock.data.ContainerId;
+import com.nukkitx.protocol.bedrock.data.InventoryActionData;
+import com.nukkitx.protocol.bedrock.data.InventorySource;
+import com.nukkitx.protocol.bedrock.data.ItemData;
+import com.nukkitx.protocol.bedrock.packet.InventoryContentPacket;
+import com.nukkitx.protocol.bedrock.packet.InventorySlotPacket;
+import it.unimi.dsi.fastutil.longs.LongArraySet;
+import org.geysermc.connector.inventory.Inventory;
+import org.geysermc.connector.network.session.GeyserSession;
+import org.geysermc.connector.network.translators.Translators;
+import org.geysermc.connector.network.translators.inventory.action.InventoryActionDataTranslator;
+import org.geysermc.connector.utils.InventoryUtils;
+import org.geysermc.connector.utils.Toolbox;
+
+import java.util.List;
+
+public class PlayerInventoryTranslator extends InventoryTranslator {
+
+ public PlayerInventoryTranslator() {
+ super(46);
+ }
+
+ @Override
+ public void updateInventory(GeyserSession session, Inventory inventory) {
+ updateCraftingGrid(session, inventory);
+
+ InventoryContentPacket inventoryContentPacket = new InventoryContentPacket();
+ inventoryContentPacket.setContainerId(ContainerId.INVENTORY);
+ ItemData[] contents = new ItemData[36];
+ // Inventory
+ for (int i = 9; i < 36; i++) {
+ contents[i] = Translators.getItemTranslator().translateToBedrock(session, inventory.getItem(i));
+ }
+ // Hotbar
+ for (int i = 36; i < 45; i++) {
+ contents[i - 36] = Translators.getItemTranslator().translateToBedrock(session, inventory.getItem(i));
+ }
+ inventoryContentPacket.setContents(contents);
+ session.getUpstream().sendPacket(inventoryContentPacket);
+
+ // Armor
+ InventoryContentPacket armorContentPacket = new InventoryContentPacket();
+ armorContentPacket.setContainerId(ContainerId.ARMOR);
+ contents = new ItemData[4];
+ for (int i = 5; i < 9; i++) {
+ contents[i - 5] = Translators.getItemTranslator().translateToBedrock(session, inventory.getItem(i));
+ }
+ armorContentPacket.setContents(contents);
+ session.getUpstream().sendPacket(armorContentPacket);
+
+ // Offhand
+ InventoryContentPacket offhandPacket = new InventoryContentPacket();
+ offhandPacket.setContainerId(ContainerId.OFFHAND);
+ offhandPacket.setContents(new ItemData[]{Translators.getItemTranslator().translateToBedrock(session, inventory.getItem(45))});
+ session.getUpstream().sendPacket(offhandPacket);
+ }
+
+ /**
+ * Update the crafting grid for the player to hide/show the barriers in the creative inventory
+ * @param session Session of the player
+ * @param inventory Inventory of the player
+ */
+ public static void updateCraftingGrid(GeyserSession session, Inventory inventory) {
+ // Crafting grid
+ for (int i = 1; i < 5; i++) {
+ InventorySlotPacket slotPacket = new InventorySlotPacket();
+ slotPacket.setContainerId(ContainerId.CURSOR);
+ slotPacket.setSlot(i + 27);
+
+ if (session.getGameMode() == GameMode.CREATIVE) {
+ slotPacket.setItem(Translators.getItemTranslator().translateToBedrock(session, new ItemStack(Toolbox.BARRIER_INDEX)));
+ }else{
+ slotPacket.setItem(Translators.getItemTranslator().translateToBedrock(session, inventory.getItem(i)));
+ }
+
+ session.getUpstream().sendPacket(slotPacket);
+ }
+ }
+
+ @Override
+ public void updateSlot(GeyserSession session, Inventory inventory, int slot) {
+ if (slot >= 1 && slot <= 44) {
+ InventorySlotPacket slotPacket = new InventorySlotPacket();
+ if (slot >= 9) {
+ slotPacket.setContainerId(ContainerId.INVENTORY);
+ if (slot >= 36) {
+ slotPacket.setSlot(slot - 36);
+ } else {
+ slotPacket.setSlot(slot);
+ }
+ } else if (slot >= 5) {
+ slotPacket.setContainerId(ContainerId.ARMOR);
+ slotPacket.setSlot(slot - 5);
+ } else {
+ slotPacket.setContainerId(ContainerId.CURSOR);
+ slotPacket.setSlot(slot + 27);
+ }
+ slotPacket.setItem(Translators.getItemTranslator().translateToBedrock(session, inventory.getItem(slot)));
+ session.getUpstream().sendPacket(slotPacket);
+ } else if (slot == 45) {
+ InventoryContentPacket offhandPacket = new InventoryContentPacket();
+ offhandPacket.setContainerId(ContainerId.OFFHAND);
+ offhandPacket.setContents(new ItemData[]{Translators.getItemTranslator().translateToBedrock(session, inventory.getItem(slot))});
+ session.getUpstream().sendPacket(offhandPacket);
+ }
+ }
+
+ @Override
+ public int bedrockSlotToJava(InventoryActionData action) {
+ int slotnum = action.getSlot();
+ switch (action.getSource().getContainerId()) {
+ case ContainerId.INVENTORY:
+ // Inventory
+ if (slotnum >= 9 && slotnum <= 35) {
+ return slotnum;
+ }
+ // Hotbar
+ if (slotnum >= 0 && slotnum <= 8) {
+ return slotnum + 36;
+ }
+ break;
+ case ContainerId.ARMOR:
+ if (slotnum >= 0 && slotnum <= 3) {
+ return slotnum + 5;
+ }
+ break;
+ case ContainerId.OFFHAND:
+ return 45;
+ case ContainerId.CURSOR:
+ if (slotnum >= 28 && 31 >= slotnum) {
+ return slotnum - 27;
+ } else if (slotnum == 50) {
+ return 0;
+ }
+ break;
+ }
+ return slotnum;
+ }
+
+ @Override
+ public int javaSlotToBedrock(int slot) {
+ return slot;
+ }
+
+ @Override
+ public SlotType getSlotType(int javaSlot) {
+ if (javaSlot == 0)
+ return SlotType.OUTPUT;
+ return SlotType.NORMAL;
+ }
+
+ @Override
+ public void translateActions(GeyserSession session, Inventory inventory, List actions) {
+ if (session.getGameMode() == GameMode.CREATIVE) {
+ //crafting grid is not visible in creative mode in java edition
+ for (InventoryActionData action : actions) {
+ if (action.getSource().getContainerId() == ContainerId.CURSOR && (action.getSlot() >= 28 && 31 >= action.getSlot())) {
+ updateInventory(session, inventory);
+ InventoryUtils.updateCursor(session);
+ return;
+ }
+ }
+
+ ItemStack javaItem;
+ for (InventoryActionData action : actions) {
+ switch (action.getSource().getContainerId()) {
+ case ContainerId.INVENTORY:
+ case ContainerId.ARMOR:
+ case ContainerId.OFFHAND:
+ int javaSlot = bedrockSlotToJava(action);
+ if (action.getToItem().getId() == 0) {
+ javaItem = new ItemStack(-1, 0, null);
+ } else {
+ javaItem = Translators.getItemTranslator().translateToJava(session, action.getToItem());
+ }
+ ClientCreativeInventoryActionPacket creativePacket = new ClientCreativeInventoryActionPacket(javaSlot, javaItem);
+ session.getDownstream().getSession().send(creativePacket);
+ inventory.setItem(javaSlot, javaItem);
+ break;
+ case ContainerId.CURSOR:
+ if (action.getSlot() == 0) {
+ session.getInventory().setCursor(Translators.getItemTranslator().translateToJava(session, action.getToItem()));
+ }
+ break;
+ case ContainerId.NONE:
+ if (action.getSource().getType() == InventorySource.Type.WORLD_INTERACTION
+ && action.getSource().getFlag() == InventorySource.Flag.DROP_ITEM) {
+ javaItem = Translators.getItemTranslator().translateToJava(session, action.getToItem());
+ ClientCreativeInventoryActionPacket creativeDropPacket = new ClientCreativeInventoryActionPacket(-1, javaItem);
+ session.getDownstream().getSession().send(creativeDropPacket);
+ }
+ break;
+ }
+ }
+ return;
+ }
+
+ InventoryActionDataTranslator.translate(this, session, inventory, actions);
+ }
+
+ @Override
+ public void prepareInventory(GeyserSession session, Inventory inventory) {
+ }
+
+ @Override
+ public void openInventory(GeyserSession session, Inventory inventory) {
+ }
+
+ @Override
+ public void closeInventory(GeyserSession session, Inventory inventory) {
+ }
+
+ @Override
+ public void updateProperty(GeyserSession session, Inventory inventory, int key, int value) {
+ }
+}
diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/inventory/SingleChestInventoryTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/SingleChestInventoryTranslator.java
new file mode 100644
index 000000000..5c99b0126
--- /dev/null
+++ b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/SingleChestInventoryTranslator.java
@@ -0,0 +1,35 @@
+/*
+ * 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.ContainerType;
+import org.geysermc.connector.network.translators.inventory.updater.ChestInventoryUpdater;
+
+public class SingleChestInventoryTranslator extends BlockInventoryTranslator {
+ public SingleChestInventoryTranslator(int size) {
+ super(size, "minecraft:chest[facing=north,type=single,waterlogged=false]", ContainerType.CONTAINER, new ChestInventoryUpdater(27));
+ }
+}
diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/inventory/SlotType.java b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/SlotType.java
new file mode 100644
index 000000000..045adbd32
--- /dev/null
+++ b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/SlotType.java
@@ -0,0 +1,32 @@
+/*
+ * 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;
+
+public enum SlotType {
+ NORMAL,
+ OUTPUT,
+ FURNACE_OUTPUT
+}
diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/inventory/action/Click.java b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/action/Click.java
new file mode 100644
index 000000000..1fdfa3640
--- /dev/null
+++ b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/action/Click.java
@@ -0,0 +1,38 @@
+/*
+ * 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.action;
+
+import com.github.steveice10.mc.protocol.data.game.window.ClickItemParam;
+import com.github.steveice10.mc.protocol.data.game.window.WindowActionParam;
+import lombok.AllArgsConstructor;
+
+@AllArgsConstructor
+enum Click {
+ LEFT(ClickItemParam.LEFT_CLICK),
+ RIGHT(ClickItemParam.RIGHT_CLICK);
+
+ public final WindowActionParam actionParam;
+}
diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/inventory/action/ClickPlan.java b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/action/ClickPlan.java
new file mode 100644
index 000000000..cdc42f961
--- /dev/null
+++ b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/action/ClickPlan.java
@@ -0,0 +1,125 @@
+/*
+ * Copyright (c) 2019-2020 GeyserMC. http://geysermc.org
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ * @author GeyserMC
+ * @link https://github.com/GeyserMC/Geyser
+ */
+
+package org.geysermc.connector.network.translators.inventory.action;
+
+import com.github.steveice10.mc.protocol.data.game.entity.metadata.ItemStack;
+import com.github.steveice10.mc.protocol.data.game.window.WindowAction;
+import com.github.steveice10.mc.protocol.packet.ingame.client.window.ClientConfirmTransactionPacket;
+import com.github.steveice10.mc.protocol.packet.ingame.client.window.ClientWindowActionPacket;
+import org.geysermc.connector.inventory.Inventory;
+import org.geysermc.connector.inventory.PlayerInventory;
+import org.geysermc.connector.network.session.GeyserSession;
+import org.geysermc.connector.network.translators.inventory.InventoryTranslator;
+import org.geysermc.connector.network.translators.inventory.SlotType;
+import org.geysermc.connector.utils.InventoryUtils;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.ListIterator;
+
+class ClickPlan {
+ private final List plan = new ArrayList<>();
+
+ public void add(Click click, int slot) {
+ plan.add(new ClickAction(click, slot));
+ }
+
+ public void execute(GeyserSession session, InventoryTranslator translator, Inventory inventory, boolean refresh) {
+ PlayerInventory playerInventory = session.getInventory();
+ ListIterator planIter = plan.listIterator();
+ while (planIter.hasNext()) {
+ final ClickAction action = planIter.next();
+ final ItemStack cursorItem = playerInventory.getCursor();
+ final ItemStack clickedItem = inventory.getItem(action.slot);
+ final short actionId = (short) inventory.getTransactionId().getAndIncrement();
+
+ //TODO: stop relying on refreshing the inventory for crafting to work properly
+ if (translator.getSlotType(action.slot) != SlotType.NORMAL)
+ refresh = true;
+
+ ClientWindowActionPacket clickPacket = new ClientWindowActionPacket(inventory.getId(),
+ actionId, action.slot, !planIter.hasNext() && refresh ? InventoryUtils.REFRESH_ITEM : clickedItem,
+ WindowAction.CLICK_ITEM, action.click.actionParam);
+
+ if (translator.getSlotType(action.slot) == SlotType.OUTPUT) {
+ if (cursorItem == null && clickedItem != null) {
+ playerInventory.setCursor(clickedItem);
+ } else if (InventoryUtils.canStack(cursorItem, clickedItem)) {
+ playerInventory.setCursor(new ItemStack(cursorItem.getId(),
+ cursorItem.getAmount() + clickedItem.getAmount(), cursorItem.getNbt()));
+ }
+ } else {
+ switch (action.click) {
+ case LEFT:
+ if (!InventoryUtils.canStack(cursorItem, clickedItem)) {
+ playerInventory.setCursor(clickedItem);
+ inventory.setItem(action.slot, cursorItem);
+ } else {
+ playerInventory.setCursor(null);
+ inventory.setItem(action.slot, new ItemStack(clickedItem.getId(),
+ clickedItem.getAmount() + cursorItem.getAmount(), clickedItem.getNbt()));
+ }
+ break;
+ case RIGHT:
+ if (cursorItem == null && clickedItem != null) {
+ ItemStack halfItem = new ItemStack(clickedItem.getId(),
+ clickedItem.getAmount() / 2, clickedItem.getNbt());
+ inventory.setItem(action.slot, halfItem);
+ playerInventory.setCursor(new ItemStack(clickedItem.getId(),
+ clickedItem.getAmount() - halfItem.getAmount(), clickedItem.getNbt()));
+ } else if (cursorItem != null && clickedItem == null) {
+ playerInventory.setCursor(new ItemStack(cursorItem.getId(),
+ cursorItem.getAmount() - 1, cursorItem.getNbt()));
+ inventory.setItem(action.slot, new ItemStack(cursorItem.getId(),
+ 1, cursorItem.getNbt()));
+ } else if (InventoryUtils.canStack(cursorItem, clickedItem)) {
+ playerInventory.setCursor(new ItemStack(cursorItem.getId(),
+ cursorItem.getAmount() - 1, cursorItem.getNbt()));
+ inventory.setItem(action.slot, new ItemStack(clickedItem.getId(),
+ clickedItem.getAmount() + 1, clickedItem.getNbt()));
+ }
+ break;
+ }
+ }
+ session.getDownstream().getSession().send(clickPacket);
+ session.getDownstream().getSession().send(new ClientConfirmTransactionPacket(inventory.getId(), actionId, true));
+ }
+
+ /*if (refresh) {
+ translator.updateInventory(session, inventory);
+ InventoryUtils.updateCursor(session);
+ }*/
+ }
+
+ private static class ClickAction {
+ final Click click;
+ final int slot;
+ ClickAction(Click click, int slot) {
+ this.click = click;
+ this.slot = slot;
+ }
+ }
+}
diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/inventory/action/InventoryActionDataTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/action/InventoryActionDataTranslator.java
new file mode 100644
index 000000000..370d41777
--- /dev/null
+++ b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/action/InventoryActionDataTranslator.java
@@ -0,0 +1,330 @@
+/*
+ * 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.action;
+
+import com.github.steveice10.mc.protocol.data.game.entity.metadata.ItemStack;
+import com.github.steveice10.mc.protocol.data.game.entity.metadata.Position;
+import com.github.steveice10.mc.protocol.data.game.entity.player.PlayerAction;
+import com.github.steveice10.mc.protocol.data.game.window.*;
+import com.github.steveice10.mc.protocol.data.game.world.block.BlockFace;
+import com.github.steveice10.mc.protocol.packet.ingame.client.player.ClientPlayerActionPacket;
+import com.github.steveice10.mc.protocol.packet.ingame.client.window.ClientWindowActionPacket;
+import com.nukkitx.protocol.bedrock.data.ContainerId;
+import com.nukkitx.protocol.bedrock.data.InventoryActionData;
+import com.nukkitx.protocol.bedrock.data.InventorySource;
+import com.nukkitx.protocol.bedrock.data.ItemData;
+import org.geysermc.connector.inventory.Inventory;
+import org.geysermc.connector.network.session.GeyserSession;
+import org.geysermc.connector.network.translators.Translators;
+import org.geysermc.connector.network.translators.inventory.InventoryTranslator;
+import org.geysermc.connector.network.translators.inventory.SlotType;
+import org.geysermc.connector.utils.InventoryUtils;
+
+import java.util.*;
+
+public class InventoryActionDataTranslator {
+ public static void translate(InventoryTranslator translator, GeyserSession session, Inventory inventory, List actions) {
+ if (actions.size() != 2)
+ return;
+
+ InventoryActionData worldAction = null;
+ InventoryActionData cursorAction = null;
+ InventoryActionData containerAction = null;
+ boolean refresh = false;
+ for (InventoryActionData action : actions) {
+ if (action.getSource().getContainerId() == ContainerId.CRAFTING_USE_INGREDIENT || action.getSource().getContainerId() == ContainerId.CRAFTING_RESULT) {
+ return;
+ } else if (action.getSource().getType() == InventorySource.Type.WORLD_INTERACTION) {
+ worldAction = action;
+ } else if (action.getSource().getContainerId() == ContainerId.CURSOR && action.getSlot() == 0) {
+ cursorAction = action;
+ ItemData translatedCursor = Translators.getItemTranslator().translateToBedrock(session, session.getInventory().getCursor());
+ if (!translatedCursor.equals(action.getFromItem())) {
+ refresh = true;
+ }
+ } else {
+ containerAction = action;
+ ItemData translatedItem = Translators.getItemTranslator().translateToBedrock(session, inventory.getItem(translator.bedrockSlotToJava(action)));
+ if (!translatedItem.equals(action.getFromItem())) {
+ refresh = true;
+ }
+ }
+ }
+
+ final int craftSlot = session.getCraftSlot();
+ session.setCraftSlot(0);
+
+ if (worldAction != null) {
+ InventoryActionData sourceAction;
+ if (cursorAction != null) {
+ sourceAction = cursorAction;
+ } else {
+ sourceAction = containerAction;
+ }
+
+ if (sourceAction != null) {
+ if (worldAction.getSource().getFlag() == InventorySource.Flag.DROP_ITEM) {
+ //quick dropping from hotbar?
+ if (session.getInventoryCache().getOpenInventory() == null && sourceAction.getSource().getContainerId() == ContainerId.INVENTORY) {
+ int heldSlot = session.getInventory().getHeldItemSlot();
+ if (sourceAction.getSlot() == heldSlot) {
+ ClientPlayerActionPacket actionPacket = new ClientPlayerActionPacket(
+ sourceAction.getToItem().getCount() == 0 ? PlayerAction.DROP_ITEM_STACK : PlayerAction.DROP_ITEM,
+ new Position(0, 0, 0), BlockFace.DOWN);
+ session.getDownstream().getSession().send(actionPacket);
+ ItemStack item = session.getInventory().getItem(heldSlot);
+ if (item != null) {
+ session.getInventory().setItem(heldSlot, new ItemStack(item.getId(), item.getAmount() - 1, item.getNbt()));
+ }
+ return;
+ }
+ }
+ int dropAmount = sourceAction.getFromItem().getCount() - sourceAction.getToItem().getCount();
+ if (sourceAction != cursorAction) { //dropping directly from inventory
+ int javaSlot = translator.bedrockSlotToJava(sourceAction);
+ if (dropAmount == sourceAction.getFromItem().getCount()) {
+ ClientWindowActionPacket dropPacket = new ClientWindowActionPacket(inventory.getId(),
+ inventory.getTransactionId().getAndIncrement(),
+ javaSlot, null, WindowAction.DROP_ITEM,
+ DropItemParam.DROP_SELECTED_STACK);
+ session.getDownstream().getSession().send(dropPacket);
+ } else {
+ for (int i = 0; i < dropAmount; i++) {
+ ClientWindowActionPacket dropPacket = new ClientWindowActionPacket(inventory.getId(),
+ inventory.getTransactionId().getAndIncrement(),
+ javaSlot, null, WindowAction.DROP_ITEM,
+ DropItemParam.DROP_FROM_SELECTED);
+ session.getDownstream().getSession().send(dropPacket);
+ }
+ }
+ ItemStack item = session.getInventory().getItem(javaSlot);
+ if (item != null) {
+ session.getInventory().setItem(javaSlot, new ItemStack(item.getId(), item.getAmount() - dropAmount, item.getNbt()));
+ }
+ return;
+ } else { //clicking outside of inventory
+ ClientWindowActionPacket dropPacket = new ClientWindowActionPacket(inventory.getId(), inventory.getTransactionId().getAndIncrement(),
+ -999, null, WindowAction.CLICK_ITEM,
+ dropAmount > 1 ? ClickItemParam.LEFT_CLICK : ClickItemParam.RIGHT_CLICK);
+ session.getDownstream().getSession().send(dropPacket);
+ ItemStack cursor = session.getInventory().getCursor();
+ if (cursor != null) {
+ session.getInventory().setCursor(new ItemStack(cursor.getId(), dropAmount > 1 ? 0 : cursor.getAmount() - 1, cursor.getNbt()));
+ }
+ return;
+ }
+ }
+ }
+ } else if (cursorAction != null && containerAction != null) {
+ //left/right click
+ ClickPlan plan = new ClickPlan();
+ int javaSlot = translator.bedrockSlotToJava(containerAction);
+ if (cursorAction.getFromItem().equals(containerAction.getToItem())
+ && containerAction.getFromItem().equals(cursorAction.getToItem())
+ && !InventoryUtils.canStack(cursorAction.getFromItem(), containerAction.getFromItem())) { //simple swap
+ plan.add(Click.LEFT, javaSlot);
+ } else if (cursorAction.getFromItem().getCount() > cursorAction.getToItem().getCount()) { //release
+ if (cursorAction.getToItem().getCount() == 0) {
+ plan.add(Click.LEFT, javaSlot);
+ } else {
+ int difference = cursorAction.getFromItem().getCount() - cursorAction.getToItem().getCount();
+ for (int i = 0; i < difference; i++) {
+ plan.add(Click.RIGHT, javaSlot);
+ }
+ }
+ } else { //pickup
+ if (cursorAction.getFromItem().getCount() == 0) {
+ if (containerAction.getToItem().getCount() == 0) { //pickup all
+ plan.add(Click.LEFT, javaSlot);
+ } else { //pickup some
+ if (translator.getSlotType(javaSlot) == SlotType.FURNACE_OUTPUT
+ || containerAction.getToItem().getCount() == containerAction.getFromItem().getCount() / 2) { //right click
+ plan.add(Click.RIGHT, javaSlot);
+ } else {
+ plan.add(Click.LEFT, javaSlot);
+ int difference = containerAction.getFromItem().getCount() - cursorAction.getToItem().getCount();
+ for (int i = 0; i < difference; i++) {
+ plan.add(Click.RIGHT, javaSlot);
+ }
+ }
+ }
+ } else { //pickup into non-empty cursor
+ if (translator.getSlotType(javaSlot) == SlotType.FURNACE_OUTPUT) {
+ if (containerAction.getToItem().getCount() == 0) {
+ plan.add(Click.LEFT, javaSlot);
+ } else {
+ ClientWindowActionPacket shiftClickPacket = new ClientWindowActionPacket(inventory.getId(),
+ inventory.getTransactionId().getAndIncrement(),
+ javaSlot, InventoryUtils.REFRESH_ITEM, WindowAction.SHIFT_CLICK_ITEM,
+ ShiftClickItemParam.LEFT_CLICK);
+ session.getDownstream().getSession().send(shiftClickPacket);
+ translator.updateInventory(session, inventory);
+ return;
+ }
+ } else if (translator.getSlotType(javaSlot) == SlotType.OUTPUT) {
+ plan.add(Click.LEFT, javaSlot);
+ } else {
+ int cursorSlot = findTempSlot(inventory, session.getInventory().getCursor(), Collections.singletonList(javaSlot));
+ if (cursorSlot != -1) {
+ plan.add(Click.LEFT, cursorSlot);
+ } else {
+ translator.updateInventory(session, inventory);
+ return;
+ }
+ plan.add(Click.LEFT, javaSlot);
+ int difference = cursorAction.getToItem().getCount() - cursorAction.getFromItem().getCount();
+ for (int i = 0; i < difference; i++) {
+ plan.add(Click.RIGHT, cursorSlot);
+ }
+ plan.add(Click.LEFT, javaSlot);
+ plan.add(Click.LEFT, cursorSlot);
+ }
+ }
+ }
+ plan.execute(session, translator, inventory, refresh);
+ return;
+ } else {
+ ClickPlan plan = new ClickPlan();
+ InventoryActionData fromAction;
+ InventoryActionData toAction;
+ if (actions.get(0).getFromItem().getCount() >= actions.get(0).getToItem().getCount()) {
+ fromAction = actions.get(0);
+ toAction = actions.get(1);
+ } else {
+ fromAction = actions.get(1);
+ toAction = actions.get(0);
+ }
+ int fromSlot = translator.bedrockSlotToJava(fromAction);
+ int toSlot = translator.bedrockSlotToJava(toAction);
+
+ if (translator.getSlotType(fromSlot) == SlotType.OUTPUT) {
+ if ((craftSlot != 0 && craftSlot != -2) && (inventory.getItem(toSlot) == null
+ || InventoryUtils.canStack(session.getInventory().getCursor(), inventory.getItem(toSlot)))) {
+ if (fromAction.getToItem().getCount() == 0) {
+ refresh = true;
+ plan.add(Click.LEFT, toSlot);
+ if (craftSlot != -1) {
+ plan.add(Click.LEFT, craftSlot);
+ }
+ } else {
+ int difference = toAction.getToItem().getCount() - toAction.getFromItem().getCount();
+ for (int i = 0; i < difference; i++) {
+ plan.add(Click.RIGHT, toSlot);
+ }
+ session.setCraftSlot(craftSlot);
+ }
+ plan.execute(session, translator, inventory, refresh);
+ return;
+ } else {
+ session.setCraftSlot(-2);
+ }
+ }
+
+ 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));
+ if (cursorSlot != -1) {
+ plan.add(Click.LEFT, cursorSlot);
+ } else {
+ translator.updateInventory(session, inventory);
+ return;
+ }
+ }
+ if ((fromAction.getFromItem().equals(toAction.getToItem()) && !InventoryUtils.canStack(fromAction.getFromItem(), toAction.getFromItem()))
+ || fromAction.getToItem().getId() == 0) { //slot swap
+ plan.add(Click.LEFT, fromSlot);
+ plan.add(Click.LEFT, toSlot);
+ if (fromAction.getToItem().getId() != 0) {
+ plan.add(Click.LEFT, fromSlot);
+ }
+ } else if (InventoryUtils.canStack(fromAction.getFromItem(), toAction.getToItem())) { //partial item move
+ if (translator.getSlotType(fromSlot) == SlotType.FURNACE_OUTPUT) {
+ ClientWindowActionPacket shiftClickPacket = new ClientWindowActionPacket(inventory.getId(),
+ inventory.getTransactionId().getAndIncrement(),
+ fromSlot, InventoryUtils.REFRESH_ITEM, WindowAction.SHIFT_CLICK_ITEM,
+ ShiftClickItemParam.LEFT_CLICK);
+ session.getDownstream().getSession().send(shiftClickPacket);
+ translator.updateInventory(session, inventory);
+ return;
+ } else if (translator.getSlotType(fromSlot) == SlotType.OUTPUT) {
+ session.setCraftSlot(cursorSlot);
+ plan.add(Click.LEFT, fromSlot);
+ int difference = toAction.getToItem().getCount() - toAction.getFromItem().getCount();
+ for (int i = 0; i < difference; i++) {
+ plan.add(Click.RIGHT, toSlot);
+ }
+ //client will send additional packets later to finish transferring crafting output
+ //translator will know how to handle this using the craftSlot variable
+ } else {
+ plan.add(Click.LEFT, fromSlot);
+ int difference = toAction.getToItem().getCount() - toAction.getFromItem().getCount();
+ for (int i = 0; i < difference; i++) {
+ plan.add(Click.RIGHT, toSlot);
+ }
+ plan.add(Click.LEFT, fromSlot);
+ }
+ }
+ if (cursorSlot != -1) {
+ plan.add(Click.LEFT, cursorSlot);
+ }
+ plan.execute(session, translator, inventory, refresh);
+ return;
+ }
+
+ translator.updateInventory(session, inventory);
+ InventoryUtils.updateCursor(session);
+ }
+
+ private static int findTempSlot(Inventory inventory, ItemStack item, List slotBlacklist) {
+ /*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*/
+ int offset = inventory.getId() == 0 ? 1 : 0; //offhand is not a viable slot (some servers disable it)
+ List itemBlacklist = new ArrayList<>(slotBlacklist.size() + 1);
+ itemBlacklist.add(item);
+ for (int slot : slotBlacklist) {
+ ItemStack blacklistItem = inventory.getItem(slot);
+ if (blacklistItem != null)
+ itemBlacklist.add(blacklistItem);
+ }
+ for (int i = inventory.getSize() - (36 + offset); i < inventory.getSize() - offset; i++) {
+ ItemStack testItem = inventory.getItem(i);
+ boolean acceptable = true;
+ if (testItem != null) {
+ for (ItemStack blacklistItem : itemBlacklist) {
+ if (InventoryUtils.canStack(testItem, blacklistItem)) {
+ acceptable = false;
+ break;
+ }
+ }
+ }
+ if (acceptable && !slotBlacklist.contains(i))
+ return i;
+ }
+ //could not find a viable temp slot
+ return -1;
+ }
+}
diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/inventory/holder/BlockInventoryHolder.java b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/holder/BlockInventoryHolder.java
new file mode 100644
index 000000000..a32354453
--- /dev/null
+++ b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/holder/BlockInventoryHolder.java
@@ -0,0 +1,92 @@
+/*
+ * Copyright (c) 2019-2020 GeyserMC. http://geysermc.org
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ * @author GeyserMC
+ * @link https://github.com/GeyserMC/Geyser
+ */
+
+package org.geysermc.connector.network.translators.inventory.holder;
+
+import com.github.steveice10.mc.protocol.data.game.entity.metadata.Position;
+import com.github.steveice10.mc.protocol.data.game.world.block.BlockState;
+import com.nukkitx.math.vector.Vector3i;
+import com.nukkitx.nbt.tag.CompoundTag;
+import com.nukkitx.protocol.bedrock.data.ContainerType;
+import com.nukkitx.protocol.bedrock.packet.BlockEntityDataPacket;
+import com.nukkitx.protocol.bedrock.packet.ContainerOpenPacket;
+import com.nukkitx.protocol.bedrock.packet.UpdateBlockPacket;
+import lombok.AllArgsConstructor;
+import org.geysermc.connector.inventory.Inventory;
+import org.geysermc.connector.network.session.GeyserSession;
+import org.geysermc.connector.network.translators.block.BlockTranslator;
+import org.geysermc.connector.network.translators.inventory.InventoryTranslator;
+import org.geysermc.connector.utils.LocaleUtils;
+
+@AllArgsConstructor
+public class BlockInventoryHolder extends InventoryHolder {
+ private final int blockId;
+ private final ContainerType containerType;
+
+ @Override
+ public void prepareInventory(InventoryTranslator translator, GeyserSession session, Inventory inventory) {
+ Vector3i position = session.getPlayerEntity().getPosition().toInt();
+ position = position.add(Vector3i.UP);
+ UpdateBlockPacket blockPacket = new UpdateBlockPacket();
+ blockPacket.setDataLayer(0);
+ blockPacket.setBlockPosition(position);
+ blockPacket.setRuntimeId(blockId);
+ blockPacket.getFlags().addAll(UpdateBlockPacket.FLAG_ALL_PRIORITY);
+ session.getUpstream().sendPacket(blockPacket);
+ inventory.setHolderPosition(position);
+
+ CompoundTag tag = CompoundTag.builder()
+ .intTag("x", position.getX())
+ .intTag("y", position.getY())
+ .intTag("z", position.getZ())
+ .stringTag("CustomName", LocaleUtils.getLocaleString(inventory.getTitle(), session.getClientData().getLanguageCode())).buildRootTag();
+ BlockEntityDataPacket dataPacket = new BlockEntityDataPacket();
+ dataPacket.setData(tag);
+ dataPacket.setBlockPosition(position);
+ session.getUpstream().sendPacket(dataPacket);
+ }
+
+ @Override
+ public void openInventory(InventoryTranslator translator, GeyserSession session, Inventory inventory) {
+ ContainerOpenPacket containerOpenPacket = new ContainerOpenPacket();
+ containerOpenPacket.setWindowId((byte) inventory.getId());
+ containerOpenPacket.setType((byte) containerType.id());
+ containerOpenPacket.setBlockPosition(inventory.getHolderPosition());
+ containerOpenPacket.setUniqueEntityId(inventory.getHolderId());
+ session.getUpstream().sendPacket(containerOpenPacket);
+ }
+
+ @Override
+ public void closeInventory(InventoryTranslator translator, GeyserSession session, Inventory inventory) {
+ Vector3i holderPos = inventory.getHolderPosition();
+ Position pos = new Position(holderPos.getX(), holderPos.getY(), holderPos.getZ());
+ BlockState realBlock = session.getChunkCache().getBlockAt(pos);
+ UpdateBlockPacket blockPacket = new UpdateBlockPacket();
+ blockPacket.setDataLayer(0);
+ blockPacket.setBlockPosition(holderPos);
+ blockPacket.setRuntimeId(BlockTranslator.getBedrockBlockId(realBlock));
+ session.getUpstream().sendPacket(blockPacket);
+ }
+}
diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/inventory/holder/InventoryHolder.java b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/holder/InventoryHolder.java
new file mode 100644
index 000000000..5a9e736e9
--- /dev/null
+++ b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/holder/InventoryHolder.java
@@ -0,0 +1,36 @@
+/*
+ * 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.holder;
+
+import org.geysermc.connector.inventory.Inventory;
+import org.geysermc.connector.network.session.GeyserSession;
+import org.geysermc.connector.network.translators.inventory.InventoryTranslator;
+
+public abstract class InventoryHolder {
+ public abstract void prepareInventory(InventoryTranslator translator, GeyserSession session, Inventory inventory);
+ public abstract void openInventory(InventoryTranslator translator, GeyserSession session, Inventory inventory);
+ public abstract void closeInventory(InventoryTranslator translator, GeyserSession session, Inventory inventory);
+}
diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/inventory/GenericInventoryTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/updater/ChestInventoryUpdater.java
similarity index 64%
rename from connector/src/main/java/org/geysermc/connector/network/translators/inventory/GenericInventoryTranslator.java
rename to connector/src/main/java/org/geysermc/connector/network/translators/inventory/updater/ChestInventoryUpdater.java
index f7be13c39..e8e0fc455 100644
--- a/connector/src/main/java/org/geysermc/connector/network/translators/inventory/GenericInventoryTranslator.java
+++ b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/updater/ChestInventoryUpdater.java
@@ -23,38 +23,32 @@
* @link https://github.com/GeyserMC/Geyser
*/
-package org.geysermc.connector.network.translators.inventory;
+package org.geysermc.connector.network.translators.inventory.updater;
-import com.nukkitx.math.vector.Vector3i;
import com.nukkitx.protocol.bedrock.data.ItemData;
-import com.nukkitx.protocol.bedrock.packet.ContainerOpenPacket;
import com.nukkitx.protocol.bedrock.packet.InventoryContentPacket;
import com.nukkitx.protocol.bedrock.packet.InventorySlotPacket;
+import lombok.AllArgsConstructor;
import org.geysermc.connector.inventory.Inventory;
import org.geysermc.connector.network.session.GeyserSession;
import org.geysermc.connector.network.translators.Translators;
+import org.geysermc.connector.network.translators.inventory.InventoryTranslator;
-public class GenericInventoryTranslator extends InventoryTranslator {
+@AllArgsConstructor
+public class ChestInventoryUpdater extends InventoryUpdater {
+ private final int paddedSize;
@Override
- public void prepareInventory(GeyserSession session, Inventory inventory) {
- // TODO: Add code here
- }
+ public void updateInventory(InventoryTranslator translator, GeyserSession session, Inventory inventory) {
+ super.updateInventory(translator, session, inventory);
- @Override
- public void openInventory(GeyserSession session, Inventory inventory) {
- ContainerOpenPacket containerOpenPacket = new ContainerOpenPacket();
- containerOpenPacket.setWindowId((byte) inventory.getId());
- containerOpenPacket.setType((byte) 0);
- containerOpenPacket.setBlockPosition(Vector3i.ZERO);
- session.getUpstream().sendPacket(containerOpenPacket);
- }
-
- @Override
- public void updateInventory(GeyserSession session, Inventory inventory) {
- ItemData[] bedrockItems = new ItemData[inventory.getItems().length];
+ ItemData[] bedrockItems = new ItemData[paddedSize];
for (int i = 0; i < bedrockItems.length; i++) {
- bedrockItems[i] = Translators.getItemTranslator().translateToBedrock(inventory.getItems()[i]);
+ if (i <= translator.size) {
+ bedrockItems[i] = Translators.getItemTranslator().translateToBedrock(session, inventory.getItem(i));
+ } else {
+ bedrockItems[i] = ItemData.AIR;
+ }
}
InventoryContentPacket contentPacket = new InventoryContentPacket();
@@ -64,11 +58,15 @@ public class GenericInventoryTranslator extends InventoryTranslator {
}
@Override
- public void updateSlot(GeyserSession session, Inventory inventory, int slot) {
+ public boolean updateSlot(InventoryTranslator translator, GeyserSession session, Inventory inventory, int javaSlot) {
+ if (super.updateSlot(translator, session, inventory, javaSlot))
+ return true;
+
InventorySlotPacket slotPacket = new InventorySlotPacket();
slotPacket.setContainerId(inventory.getId());
- slotPacket.setItem(Translators.getItemTranslator().translateToBedrock(inventory.getItems()[slot]));
- slotPacket.setSlot(slot);
+ slotPacket.setSlot(translator.javaSlotToBedrock(javaSlot));
+ slotPacket.setItem(Translators.getItemTranslator().translateToBedrock(session, inventory.getItem(javaSlot)));
session.getUpstream().sendPacket(slotPacket);
+ return true;
}
}
diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/inventory/updater/ContainerInventoryUpdater.java b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/updater/ContainerInventoryUpdater.java
new file mode 100644
index 000000000..d187ffd95
--- /dev/null
+++ b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/updater/ContainerInventoryUpdater.java
@@ -0,0 +1,64 @@
+/*
+ * 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.updater;
+
+import com.nukkitx.protocol.bedrock.data.ItemData;
+import com.nukkitx.protocol.bedrock.packet.InventoryContentPacket;
+import com.nukkitx.protocol.bedrock.packet.InventorySlotPacket;
+import org.geysermc.connector.inventory.Inventory;
+import org.geysermc.connector.network.session.GeyserSession;
+import org.geysermc.connector.network.translators.Translators;
+import org.geysermc.connector.network.translators.inventory.InventoryTranslator;
+
+public class ContainerInventoryUpdater extends InventoryUpdater {
+ @Override
+ public void updateInventory(InventoryTranslator translator, GeyserSession session, Inventory inventory) {
+ super.updateInventory(translator, session, inventory);
+
+ ItemData[] bedrockItems = new ItemData[translator.size];
+ for (int i = 0; i < bedrockItems.length; i++) {
+ bedrockItems[translator.javaSlotToBedrock(i)] = Translators.getItemTranslator().translateToBedrock(session, inventory.getItem(i));
+ }
+
+ InventoryContentPacket contentPacket = new InventoryContentPacket();
+ contentPacket.setContainerId(inventory.getId());
+ contentPacket.setContents(bedrockItems);
+ session.getUpstream().sendPacket(contentPacket);
+ }
+
+ @Override
+ public boolean updateSlot(InventoryTranslator translator, GeyserSession session, Inventory inventory, int javaSlot) {
+ if (super.updateSlot(translator, session, inventory, javaSlot))
+ return true;
+
+ InventorySlotPacket slotPacket = new InventorySlotPacket();
+ slotPacket.setContainerId(inventory.getId());
+ slotPacket.setSlot(translator.javaSlotToBedrock(javaSlot));
+ slotPacket.setItem(Translators.getItemTranslator().translateToBedrock(session, inventory.getItem(javaSlot)));
+ session.getUpstream().sendPacket(slotPacket);
+ return true;
+ }
+}
diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/inventory/updater/CursorInventoryUpdater.java b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/updater/CursorInventoryUpdater.java
new file mode 100644
index 000000000..e3dc18648
--- /dev/null
+++ b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/updater/CursorInventoryUpdater.java
@@ -0,0 +1,64 @@
+/*
+ * 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.updater;
+
+import com.nukkitx.protocol.bedrock.data.ContainerId;
+import com.nukkitx.protocol.bedrock.packet.InventorySlotPacket;
+import org.geysermc.connector.inventory.Inventory;
+import org.geysermc.connector.network.session.GeyserSession;
+import org.geysermc.connector.network.translators.Translators;
+import org.geysermc.connector.network.translators.inventory.InventoryTranslator;
+
+public class CursorInventoryUpdater extends InventoryUpdater {
+ @Override
+ public void updateInventory(InventoryTranslator translator, GeyserSession session, Inventory inventory) {
+ super.updateInventory(translator, session, inventory);
+
+ for (int i = 0; i < translator.size; i++) {
+ final int bedrockSlot = translator.javaSlotToBedrock(i);
+ if (bedrockSlot == 50)
+ continue;
+ InventorySlotPacket slotPacket = new InventorySlotPacket();
+ slotPacket.setContainerId(ContainerId.CURSOR);
+ slotPacket.setSlot(bedrockSlot);
+ slotPacket.setItem(Translators.getItemTranslator().translateToBedrock(session, inventory.getItem(i)));
+ session.getUpstream().sendPacket(slotPacket);
+ }
+ }
+
+ @Override
+ public boolean updateSlot(InventoryTranslator translator, GeyserSession session, Inventory inventory, int javaSlot) {
+ if (super.updateSlot(translator, session, inventory, javaSlot))
+ return true;
+
+ InventorySlotPacket slotPacket = new InventorySlotPacket();
+ slotPacket.setContainerId(ContainerId.CURSOR);
+ slotPacket.setSlot(translator.javaSlotToBedrock(javaSlot));
+ slotPacket.setItem(Translators.getItemTranslator().translateToBedrock(session, inventory.getItem(javaSlot)));
+ session.getUpstream().sendPacket(slotPacket);
+ return true;
+ }
+}
diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/inventory/updater/InventoryUpdater.java b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/updater/InventoryUpdater.java
new file mode 100644
index 000000000..2f139e27a
--- /dev/null
+++ b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/updater/InventoryUpdater.java
@@ -0,0 +1,61 @@
+/*
+ * 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.updater;
+
+import com.nukkitx.protocol.bedrock.data.ContainerId;
+import com.nukkitx.protocol.bedrock.data.ItemData;
+import com.nukkitx.protocol.bedrock.packet.InventoryContentPacket;
+import com.nukkitx.protocol.bedrock.packet.InventorySlotPacket;
+import org.geysermc.connector.inventory.Inventory;
+import org.geysermc.connector.network.session.GeyserSession;
+import org.geysermc.connector.network.translators.Translators;
+import org.geysermc.connector.network.translators.inventory.InventoryTranslator;
+
+public abstract class InventoryUpdater {
+ public void updateInventory(InventoryTranslator translator, GeyserSession session, Inventory inventory) {
+ ItemData[] bedrockItems = new ItemData[36];
+ for (int i = 0; i < 36; i++) {
+ final int offset = i < 9 ? 27 : -9;
+ bedrockItems[i] = Translators.getItemTranslator().translateToBedrock(session, inventory.getItem(translator.size + i + offset));
+ }
+ InventoryContentPacket contentPacket = new InventoryContentPacket();
+ contentPacket.setContainerId(ContainerId.INVENTORY);
+ contentPacket.setContents(bedrockItems);
+ session.getUpstream().sendPacket(contentPacket);
+ }
+
+ public boolean updateSlot(InventoryTranslator translator, GeyserSession session, Inventory inventory, int javaSlot) {
+ if (javaSlot >= translator.size) {
+ InventorySlotPacket slotPacket = new InventorySlotPacket();
+ slotPacket.setContainerId(ContainerId.INVENTORY);
+ slotPacket.setSlot(translator.javaSlotToBedrock(javaSlot));
+ slotPacket.setItem(Translators.getItemTranslator().translateToBedrock(session, inventory.getItem(javaSlot)));
+ session.getUpstream().sendPacket(slotPacket);
+ return true;
+ }
+ return false;
+ }
+}
diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/item/Enchantment.java b/connector/src/main/java/org/geysermc/connector/network/translators/item/Enchantment.java
new file mode 100644
index 000000000..b9348e25f
--- /dev/null
+++ b/connector/src/main/java/org/geysermc/connector/network/translators/item/Enchantment.java
@@ -0,0 +1,92 @@
+/*
+ * Copyright (c) 2019-2020 GeyserMC. http://geysermc.org
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ * @author GeyserMC
+ * @link https://github.com/GeyserMC/Geyser
+ */
+
+package org.geysermc.connector.network.translators.item;
+
+import lombok.Getter;
+
+import java.util.Locale;
+
+@Getter
+public enum Enchantment {
+ PROTECTION,
+ FIRE_PROTECTION,
+ FEATHER_FALLING,
+ BLAST_PROTECTION,
+ PROJECTILE_PROTECTION,
+ THORNS,
+ RESPIRATION,
+ DEPTH_STRIDER,
+ AQUA_AFFINITY,
+ SHARPNESS,
+ SMITE,
+ BANE_OF_ARTHROPODS,
+ KNOCKBACK,
+ FIRE_ASPECT,
+ LOOTING,
+ EFFICIENCY,
+ SILK_TOUCH,
+ UNBREAKING,
+ FORTUNE,
+ POWER,
+ PUNCH,
+ FLAME,
+ INFINITY,
+ LUCK_OF_THE_SEA,
+ LURE,
+ FROST_WALKER,
+ MENDING,
+ BINDING_CURSE,
+ VANISHING_CURSE,
+ IMPALING,
+ RIPTIDE,
+ LOYALTY,
+ CHANNELING,
+ MULTISHOT,
+ PIERCING,
+ QUICK_CHARGE;
+
+ private final String javaIdentifier;
+
+ Enchantment() {
+ this.javaIdentifier = "minecraft:" + this.name().toLowerCase(Locale.ENGLISH);
+ }
+
+ public static Enchantment getByJavaIdentifier(String javaIdentifier) {
+ for (Enchantment enchantment : Enchantment.values()) {
+ if (enchantment.javaIdentifier.equals(javaIdentifier)) {
+ return enchantment;
+ }
+ }
+ return null;
+ }
+
+ public static Enchantment getByBedrockId(int bedrockId) {
+ if (bedrockId >= 0 && bedrockId < Enchantment.values().length) {
+ return Enchantment.values()[bedrockId];
+ }
+ return null;
+ }
+}
diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/item/ItemEntry.java b/connector/src/main/java/org/geysermc/connector/network/translators/item/ItemEntry.java
index fd4f0b020..e579c20ee 100644
--- a/connector/src/main/java/org/geysermc/connector/network/translators/item/ItemEntry.java
+++ b/connector/src/main/java/org/geysermc/connector/network/translators/item/ItemEntry.java
@@ -34,11 +34,11 @@ public class ItemEntry {
public static ItemEntry AIR = new ItemEntry("minecraft:air", 0, 0, 0);
- private String javaIdentifier;
- private int javaId;
+ private final String javaIdentifier;
+ private final int javaId;
- private int bedrockId;
- private int bedrockData;
+ private final int bedrockId;
+ private final int bedrockData;
@Override
public boolean equals(Object obj) {
diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/item/ItemTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/item/ItemTranslator.java
index 68f88bb21..5e0361c06 100644
--- a/connector/src/main/java/org/geysermc/connector/network/translators/item/ItemTranslator.java
+++ b/connector/src/main/java/org/geysermc/connector/network/translators/item/ItemTranslator.java
@@ -26,64 +26,110 @@
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.github.steveice10.opennbt.tag.builtin.ByteArrayTag;
-import com.github.steveice10.opennbt.tag.builtin.ByteTag;
-import com.github.steveice10.opennbt.tag.builtin.CompoundTag;
-import com.github.steveice10.opennbt.tag.builtin.DoubleTag;
-import com.github.steveice10.opennbt.tag.builtin.FloatTag;
-import com.github.steveice10.opennbt.tag.builtin.IntArrayTag;
-import com.github.steveice10.opennbt.tag.builtin.IntTag;
-import com.github.steveice10.opennbt.tag.builtin.ListTag;
-import com.github.steveice10.opennbt.tag.builtin.LongArrayTag;
-import com.github.steveice10.opennbt.tag.builtin.LongTag;
-import com.github.steveice10.opennbt.tag.builtin.ShortTag;
-import com.github.steveice10.opennbt.tag.builtin.StringTag;
-import com.github.steveice10.opennbt.tag.builtin.Tag;
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.utils.MessageUtils;
-import org.geysermc.connector.utils.Toolbox;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
+import org.geysermc.connector.network.session.GeyserSession;
+import org.geysermc.connector.network.translators.*;
+import org.geysermc.connector.utils.Toolbox;
+import org.reflections.Reflections;
+
+import java.util.*;
+import java.util.stream.Collectors;
public class ItemTranslator {
- public ItemStack translateToJava(ItemData data) {
- ItemEntry javaItem = getItem(data);
+ private Int2ObjectMap itemTranslators = new Int2ObjectOpenHashMap();
+ private List nbtItemTranslators;
+ private Map javaIdentifierMap = new HashMap<>();
+
+ // Shield ID, used in Entity.java
+ public static final int SHIELD = 829;
- if (data.getTag() == null) {
- return new ItemStack(javaItem.getJavaId(), data.getCount());
+ public void init() {
+ Reflections ref = new Reflections("org.geysermc.connector.network.translators.item");
+
+ Map loadedNbtItemTranslators = new HashMap<>();
+ for (Class> clazz : ref.getTypesAnnotatedWith(ItemRemapper.class)) {
+ int priority = clazz.getAnnotation(ItemRemapper.class).priority();
+
+ GeyserConnector.getInstance().getLogger().debug("Found annotated item translator: " + clazz.getCanonicalName());
+
+ try {
+ if (NbtItemStackTranslator.class.isAssignableFrom(clazz)) {
+ NbtItemStackTranslator nbtItemTranslator = (NbtItemStackTranslator) clazz.newInstance();
+ loadedNbtItemTranslators.put(nbtItemTranslator, priority);
+ continue;
+ }
+ ItemStackTranslator itemStackTranslator = (ItemStackTranslator) clazz.newInstance();
+ List appliedItems = itemStackTranslator.getAppliedItems();
+ for (ItemEntry item : appliedItems) {
+ ItemStackTranslator registered = itemTranslators.get(item.getJavaId());
+ if (registered != null) {
+ GeyserConnector.getInstance().getLogger().error("Could not instantiate annotated item translator " + clazz.getCanonicalName() + "." +
+ " Item translator " + registered.getClass().getCanonicalName() + " is already registered for the item " + item.getJavaIdentifier());
+ continue;
+ }
+ itemTranslators.put(item.getJavaId(), itemStackTranslator);
+ }
+ } catch (InstantiationException | IllegalAccessException e) {
+ GeyserConnector.getInstance().getLogger().error("Could not instantiate annotated item translator " + clazz.getCanonicalName() + ".");
+ }
}
- return new ItemStack(javaItem.getJavaId(), data.getCount(), translateToJavaNBT(data.getTag()));
+
+ nbtItemTranslators = loadedNbtItemTranslators.keySet().stream()
+ .sorted(Comparator.comparingInt(value -> loadedNbtItemTranslators.get(value))).collect(Collectors.toList());
}
- public ItemData translateToBedrock(ItemStack stack) {
- // Most likely dirt if null
+ public ItemStack translateToJava(GeyserSession session, ItemData data) {
+ if (data == null) {
+ return new ItemStack(0);
+ }
+ ItemEntry javaItem = getItem(data);
+
+ ItemStack itemStack;
+
+ ItemStackTranslator itemStackTranslator = itemTranslators.get(javaItem.getJavaId());
+ if (itemStackTranslator != null) {
+ itemStack = itemStackTranslator.translateToJava(data, javaItem);
+ } else {
+ itemStack = DEFAULT_TRANSLATOR.translateToJava(data, javaItem);
+ }
+
+ if (itemStack != null && itemStack.getNbt() != null) {
+ for (NbtItemStackTranslator translator : nbtItemTranslators) {
+ if (translator.acceptItem(javaItem)) {
+ translator.translateToJava(itemStack.getNbt(), javaItem);
+ }
+ }
+ }
+ return itemStack;
+ }
+
+ public ItemData translateToBedrock(GeyserSession session, ItemStack stack) {
if (stack == null) {
- return ItemData.of(3, (short)0, 0);
+ return ItemData.AIR;
}
ItemEntry bedrockItem = getItem(stack);
- if (stack.getNbt() == null) {
- return ItemData.of(bedrockItem.getBedrockId(), (short) bedrockItem.getBedrockData(), stack.getAmount());
+
+ if (stack != null && stack.getNbt() != null) {
+ for (NbtItemStackTranslator translator : nbtItemTranslators) {
+ if (translator.acceptItem(bedrockItem)) {
+ translator.translateToBedrock(stack.getNbt(), bedrockItem);
+ }
+ }
}
- // TODO: Create proper transformers instead of shoving everything here
- CompoundTag tag = stack.getNbt();
- IntTag mapId = tag.get("map");
-
- if (mapId != null) {
- tag.put(new StringTag("map_uuid", mapId.getValue().toString()));
- tag.put(new IntTag("map_name_index", mapId.getValue()));
+ ItemStackTranslator itemStackTranslator = itemTranslators.get(bedrockItem.getJavaId());
+ if (itemStackTranslator != null) {
+ return itemStackTranslator.translateToBedrock(stack, bedrockItem);
+ } else {
+ return DEFAULT_TRANSLATOR.translateToBedrock(stack, bedrockItem);
}
-
-
- return ItemData.of(bedrockItem.getBedrockId(), (short) bedrockItem.getBedrockData(), stack.getAmount(), translateToBedrockNBT(tag));
}
public ItemEntry getItem(ItemStack stack) {
@@ -92,7 +138,14 @@ public class ItemTranslator {
public ItemEntry getItem(ItemData data) {
for (ItemEntry itemEntry : Toolbox.ITEM_ENTRIES.values()) {
- if (itemEntry.getBedrockId() == data.getId() && itemEntry.getBedrockData() == data.getDamage()) {
+ if (itemEntry.getBedrockId() == data.getId() && (itemEntry.getBedrockData() == data.getDamage() || itemEntry.getJavaIdentifier().endsWith("potion"))) {
+ return itemEntry;
+ }
+ }
+ // If item find was unsuccessful first time, we try again while ignoring damage
+ // Fixes piston, sticky pistons, dispensers and droppers turning into air from creative inventory
+ for (ItemEntry itemEntry : Toolbox.ITEM_ENTRIES.values()) {
+ if (itemEntry.getBedrockId() == data.getId()) {
return itemEntry;
}
}
@@ -101,185 +154,15 @@ public class ItemTranslator {
return ItemEntry.AIR;
}
- private CompoundTag translateToJavaNBT(com.nukkitx.nbt.tag.CompoundTag tag) {
- CompoundTag javaTag = new CompoundTag(tag.getName());
- Map javaValue = javaTag.getValue();
- if (tag.getValue() != null && !tag.getValue().isEmpty()) {
- for (String str : tag.getValue().keySet()) {
- com.nukkitx.nbt.tag.Tag bedrockTag = tag.get(str);
- Tag translatedTag = translateToJavaNBT(bedrockTag);
- if (translatedTag == null)
- continue;
-
- javaValue.put(str, translatedTag);
- }
- }
-
- return javaTag;
+ public ItemEntry getItemEntry(String javaIdentifier) {
+ return javaIdentifierMap.computeIfAbsent(javaIdentifier, key -> Toolbox.ITEM_ENTRIES.values()
+ .stream().filter(itemEntry -> itemEntry.getJavaIdentifier().equals(key)).findFirst().orElse(null));
}
- private Tag translateToJavaNBT(com.nukkitx.nbt.tag.Tag tag) {
- if (tag instanceof com.nukkitx.nbt.tag.ByteArrayTag) {
- com.nukkitx.nbt.tag.ByteArrayTag byteArrayTag = (com.nukkitx.nbt.tag.ByteArrayTag) tag;
- return new ByteArrayTag(byteArrayTag.getName(), byteArrayTag.getValue());
+ private static final ItemStackTranslator DEFAULT_TRANSLATOR = new ItemStackTranslator() {
+ @Override
+ public List getAppliedItems() {
+ return null;
}
-
- if (tag instanceof com.nukkitx.nbt.tag.ByteTag) {
- com.nukkitx.nbt.tag.ByteTag byteTag = (com.nukkitx.nbt.tag.ByteTag) tag;
- return new ByteTag(byteTag.getName(), byteTag.getValue());
- }
-
- if (tag instanceof com.nukkitx.nbt.tag.DoubleTag) {
- com.nukkitx.nbt.tag.DoubleTag doubleTag = (com.nukkitx.nbt.tag.DoubleTag) tag;
- return new DoubleTag(doubleTag.getName(), doubleTag.getValue());
- }
-
- if (tag instanceof com.nukkitx.nbt.tag.FloatTag) {
- com.nukkitx.nbt.tag.FloatTag floatTag = (com.nukkitx.nbt.tag.FloatTag) tag;
- return new FloatTag(floatTag.getName(), floatTag.getValue());
- }
-
- if (tag instanceof com.nukkitx.nbt.tag.IntArrayTag) {
- com.nukkitx.nbt.tag.IntArrayTag intArrayTag = (com.nukkitx.nbt.tag.IntArrayTag) tag;
- return new IntArrayTag(intArrayTag.getName(), intArrayTag.getValue());
- }
-
- if (tag instanceof com.nukkitx.nbt.tag.IntTag) {
- com.nukkitx.nbt.tag.IntTag intTag = (com.nukkitx.nbt.tag.IntTag) tag;
- return new IntTag(intTag.getName(), intTag.getValue());
- }
-
- if (tag instanceof com.nukkitx.nbt.tag.LongArrayTag) {
- com.nukkitx.nbt.tag.LongArrayTag longArrayTag = (com.nukkitx.nbt.tag.LongArrayTag) tag;
- return new LongArrayTag(longArrayTag.getName(), longArrayTag.getValue());
- }
-
- if (tag instanceof com.nukkitx.nbt.tag.LongTag) {
- com.nukkitx.nbt.tag.LongTag longTag = (com.nukkitx.nbt.tag.LongTag) tag;
- return new LongTag(longTag.getName(), longTag.getValue());
- }
-
- if (tag instanceof com.nukkitx.nbt.tag.ShortTag) {
- com.nukkitx.nbt.tag.ShortTag shortTag = (com.nukkitx.nbt.tag.ShortTag) tag;
- return new ShortTag(shortTag.getName(), shortTag.getValue());
- }
-
- if (tag instanceof com.nukkitx.nbt.tag.StringTag) {
- com.nukkitx.nbt.tag.StringTag stringTag = (com.nukkitx.nbt.tag.StringTag) tag;
- return new StringTag(stringTag.getName(), stringTag.getValue());
- }
-
- if (tag instanceof com.nukkitx.nbt.tag.ListTag) {
- com.nukkitx.nbt.tag.ListTag listTag = (com.nukkitx.nbt.tag.ListTag) tag;
-
- List tags = new ArrayList<>();
- for (Object value : listTag.getValue()) {
- if (!(value instanceof com.nukkitx.nbt.tag.Tag))
- continue;
-
- com.nukkitx.nbt.tag.Tag tagValue = (com.nukkitx.nbt.tag.Tag) value;
- Tag javaTag = translateToJavaNBT(tagValue);
- if (javaTag != null)
- tags.add(javaTag);
- }
- return new ListTag(listTag.getName(), tags);
- }
-
- if (tag instanceof com.nukkitx.nbt.tag.CompoundTag) {
- return translateToJavaNBT((com.nukkitx.nbt.tag.CompoundTag) tag);
- }
-
- return null;
- }
-
- private com.nukkitx.nbt.tag.CompoundTag translateToBedrockNBT(CompoundTag tag) {
- Map> javaValue = new HashMap>();
- if (tag.getValue() != null && !tag.getValue().isEmpty()) {
- for (String str : tag.getValue().keySet()) {
- Tag javaTag = tag.get(str);
- com.nukkitx.nbt.tag.Tag translatedTag = translateToBedrockNBT(javaTag);
- if (translatedTag == null)
- continue;
-
- javaValue.put(str, translatedTag);
- }
- }
-
- com.nukkitx.nbt.tag.CompoundTag bedrockTag = new com.nukkitx.nbt.tag.CompoundTag(tag.getName(), javaValue);
- return bedrockTag;
- }
-
- private com.nukkitx.nbt.tag.Tag translateToBedrockNBT(Tag tag) {
- if (tag instanceof ByteArrayTag) {
- ByteArrayTag byteArrayTag = (ByteArrayTag) tag;
- return new com.nukkitx.nbt.tag.ByteArrayTag(byteArrayTag.getName(), byteArrayTag.getValue());
- }
-
- if (tag instanceof ByteTag) {
- ByteTag byteTag = (ByteTag) tag;
- return new com.nukkitx.nbt.tag.ByteTag(byteTag.getName(), byteTag.getValue());
- }
-
- if (tag instanceof DoubleTag) {
- DoubleTag doubleTag = (DoubleTag) tag;
- return new com.nukkitx.nbt.tag.DoubleTag(doubleTag.getName(), doubleTag.getValue());
- }
-
- if (tag instanceof FloatTag) {
- FloatTag floatTag = (FloatTag) tag;
- return new com.nukkitx.nbt.tag.FloatTag(floatTag.getName(), floatTag.getValue());
- }
-
- if (tag instanceof IntArrayTag) {
- IntArrayTag intArrayTag = (IntArrayTag) tag;
- return new com.nukkitx.nbt.tag.IntArrayTag(intArrayTag.getName(), intArrayTag.getValue());
- }
-
- if (tag instanceof IntTag) {
- IntTag intTag = (IntTag) tag;
- return new com.nukkitx.nbt.tag.IntTag(intTag.getName(), intTag.getValue());
- }
-
- if (tag instanceof LongArrayTag) {
- LongArrayTag longArrayTag = (LongArrayTag) tag;
- return new com.nukkitx.nbt.tag.LongArrayTag(longArrayTag.getName(), longArrayTag.getValue());
- }
-
- if (tag instanceof LongTag) {
- LongTag longTag = (LongTag) tag;
- return new com.nukkitx.nbt.tag.LongTag(longTag.getName(), longTag.getValue());
- }
-
- if (tag instanceof ShortTag) {
- ShortTag shortTag = (ShortTag) tag;
- return new com.nukkitx.nbt.tag.ShortTag(shortTag.getName(), shortTag.getValue());
- }
-
- if (tag instanceof StringTag) {
- StringTag stringTag = (StringTag) tag;
- return new com.nukkitx.nbt.tag.StringTag(stringTag.getName(), MessageUtils.getBedrockMessage(Message.fromString(stringTag.getValue())));
- }
-
- if (tag instanceof ListTag) {
- ListTag listTag = (ListTag) tag;
- if (listTag.getName().equalsIgnoreCase("Lore")) {
- List tags = new ArrayList<>();
- for (Object value : listTag.getValue()) {
- if (!(value instanceof Tag))
- continue;
-
- com.nukkitx.nbt.tag.StringTag bedrockTag = (com.nukkitx.nbt.tag.StringTag) translateToBedrockNBT((Tag) value);
- if (bedrockTag != null)
- tags.add(bedrockTag);
- }
- return new com.nukkitx.nbt.tag.ListTag<>(listTag.getName(), com.nukkitx.nbt.tag.StringTag.class, tags);
- }
- }
-
- if (tag instanceof CompoundTag) {
- return translateToBedrockNBT((CompoundTag) tag);
- }
-
- return null;
- }
+ };
}
diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/item/Potion.java b/connector/src/main/java/org/geysermc/connector/network/translators/item/Potion.java
new file mode 100644
index 000000000..51ae36e49
--- /dev/null
+++ b/connector/src/main/java/org/geysermc/connector/network/translators/item/Potion.java
@@ -0,0 +1,102 @@
+/*
+ * 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.item;
+
+import lombok.Getter;
+
+import java.util.Locale;
+
+@Getter
+public enum Potion {
+ WATER(0),
+ MUNDANE(1),
+ THICK(3),
+ AWKWARD(4),
+ NIGHT_VISION(5),
+ LONG_NIGHT_VISION(6),
+ INVISIBILITY(7),
+ LONG_INVISIBILITY(8),
+ LEAPING(9),
+ STRONG_LEAPING(11),
+ LONG_LEAPING(10),
+ FIRE_RESISTANCE(12),
+ LONG_FIRE_RESISTANCE(13),
+ SWIFTNESS(14),
+ STRONG_SWIFTNESS(16),
+ LONG_SWIFTNESS(15),
+ SLOWNESS(17),
+ STRONG_SLOWNESS(18), //does not exist
+ LONG_SLOWNESS(18),
+ WATER_BREATHING(19),
+ LONG_WATER_BREATHING(20),
+ HEALING(21),
+ STRONG_HEALING(22),
+ HARMING(23),
+ STRONG_HARMING(24),
+ POISON(25),
+ STRONG_POISON(27),
+ LONG_POISON(26),
+ REGENERATION(28),
+ STRONG_REGENERATION(30),
+ LONG_REGENERATION(29),
+ STRENGTH(31),
+ STRONG_STRENGTH(33),
+ LONG_STRENGTH(32),
+ WEAKNESS(34),
+ LONG_WEAKNESS(35),
+ LUCK(2), //does not exist
+ TURTLE_MASTER(37),
+ STRONG_TURTLE_MASTER(39),
+ LONG_TURTLE_MASTER(38),
+ SLOW_FALLING(40),
+ LONG_SLOW_FALLING(41);
+
+ private final String javaIdentifier;
+ private final short bedrockId;
+
+ Potion(int bedrockId) {
+ this.javaIdentifier = "minecraft:" + this.name().toLowerCase(Locale.ENGLISH);
+ this.bedrockId = (short) bedrockId;
+ }
+
+ public static Potion getByJavaIdentifier(String javaIdentifier) {
+ for (Potion potion : Potion.values()) {
+ if (potion.javaIdentifier.equals(javaIdentifier)) {
+ return potion;
+ }
+ }
+ return null;
+ }
+
+ public static Potion getByBedrockId(short bedrockId) {
+ for (Potion potion : Potion.values()) {
+ if (potion.bedrockId == bedrockId) {
+ return potion;
+ }
+ }
+ return null;
+ }
+}
diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/item/ToolItemEntry.java b/connector/src/main/java/org/geysermc/connector/network/translators/item/ToolItemEntry.java
new file mode 100644
index 000000000..5d1ddd262
--- /dev/null
+++ b/connector/src/main/java/org/geysermc/connector/network/translators/item/ToolItemEntry.java
@@ -0,0 +1,15 @@
+package org.geysermc.connector.network.translators.item;
+
+import lombok.Getter;
+
+@Getter
+public class ToolItemEntry extends ItemEntry {
+ private final String toolType;
+ private final String toolTier;
+
+ public ToolItemEntry(String javaIdentifier, int javaId, int bedrockId, int bedrockData, String toolType, String toolTier) {
+ super(javaIdentifier, javaId, bedrockId, bedrockData);
+ this.toolType = toolType;
+ this.toolTier = toolTier;
+ }
+}
diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/item/translators/PotionTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/item/translators/PotionTranslator.java
new file mode 100644
index 000000000..e528b448e
--- /dev/null
+++ b/connector/src/main/java/org/geysermc/connector/network/translators/item/translators/PotionTranslator.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright (c) 2019-2020 GeyserMC. http://geysermc.org
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ * @author GeyserMC
+ * @link https://github.com/GeyserMC/Geyser
+ */
+
+package org.geysermc.connector.network.translators.item.translators;
+
+import com.github.steveice10.mc.protocol.data.game.entity.metadata.ItemStack;
+import com.github.steveice10.opennbt.tag.builtin.StringTag;
+import com.github.steveice10.opennbt.tag.builtin.Tag;
+import com.nukkitx.protocol.bedrock.data.ItemData;
+import org.geysermc.connector.GeyserConnector;
+import org.geysermc.connector.network.translators.ItemStackTranslator;
+import org.geysermc.connector.network.translators.ItemRemapper;
+import org.geysermc.connector.network.translators.item.ItemEntry;
+import org.geysermc.connector.network.translators.item.Potion;
+import org.geysermc.connector.utils.Toolbox;
+
+import java.util.List;
+import java.util.stream.Collectors;
+
+@ItemRemapper
+public class PotionTranslator extends ItemStackTranslator {
+
+ private List appliedItems;
+
+ public PotionTranslator() {
+ appliedItems = Toolbox.ITEM_ENTRIES.values().stream().filter(entry -> entry.getJavaIdentifier().endsWith("potion")).collect(Collectors.toList());
+ }
+
+ @Override
+ public ItemData translateToBedrock(ItemStack itemStack, ItemEntry itemEntry) {
+ if (itemStack.getNbt() == null) return super.translateToBedrock(itemStack, itemEntry);
+ Tag potionTag = itemStack.getNbt().get("Potion");
+ if (potionTag instanceof StringTag) {
+ Potion potion = Potion.getByJavaIdentifier(((StringTag) potionTag).getValue());
+ if (potion != null) {
+ return ItemData.of(itemEntry.getBedrockId(), potion.getBedrockId(), itemStack.getAmount(), translateNbtToBedrock(itemStack.getNbt()));
+ }
+ GeyserConnector.getInstance().getLogger().debug("Unknown java potion: " + potionTag.getValue());
+ }
+ return super.translateToBedrock(itemStack, itemEntry);
+ }
+
+ @Override
+ public ItemStack translateToJava(ItemData itemData, ItemEntry itemEntry) {
+ Potion potion = Potion.getByBedrockId(itemData.getDamage());
+ ItemStack itemStack = super.translateToJava(itemData, itemEntry);
+ if (potion != null) {
+ StringTag potionTag = new StringTag("Potion", potion.getJavaIdentifier());
+ itemStack.getNbt().put(potionTag);
+ }
+ return itemStack;
+ }
+
+ @Override
+ public List getAppliedItems() {
+ return appliedItems;
+ }
+}
diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/item/translators/nbt/BookPagesTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/item/translators/nbt/BookPagesTranslator.java
new file mode 100644
index 000000000..f29f16fea
--- /dev/null
+++ b/connector/src/main/java/org/geysermc/connector/network/translators/item/translators/nbt/BookPagesTranslator.java
@@ -0,0 +1,84 @@
+/*
+ * 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.item.translators.nbt;
+
+import com.github.steveice10.opennbt.tag.builtin.CompoundTag;
+import com.github.steveice10.opennbt.tag.builtin.ListTag;
+import com.github.steveice10.opennbt.tag.builtin.StringTag;
+import com.github.steveice10.opennbt.tag.builtin.Tag;
+import org.geysermc.connector.network.translators.ItemRemapper;
+import org.geysermc.connector.network.translators.NbtItemStackTranslator;
+import org.geysermc.connector.network.translators.item.ItemEntry;
+import org.geysermc.connector.utils.MessageUtils;
+
+import java.util.ArrayList;
+import java.util.List;
+
+@ItemRemapper
+public class BookPagesTranslator extends NbtItemStackTranslator {
+
+ @Override
+ public void translateToBedrock(CompoundTag itemTag, ItemEntry itemEntry) {
+ if (itemTag.contains("pages")) {
+ List pages = new ArrayList<>();
+ ListTag pagesTag = itemTag.get("pages");
+ for (Tag tag : pagesTag.getValue()) {
+ if (!(tag instanceof StringTag))
+ continue;
+
+ StringTag textTag = (StringTag) tag;
+
+ CompoundTag pageTag = new CompoundTag("");
+ pageTag.put(new StringTag("photoname", ""));
+ pageTag.put(new StringTag("text", MessageUtils.getBedrockMessage(textTag.getValue())));
+ pages.add(pageTag);
+ }
+
+ itemTag.remove("pages");
+ itemTag.put(new ListTag("pages", pages));
+ }
+ }
+
+ @Override
+ public void translateToJava(CompoundTag itemTag, ItemEntry itemEntry) {
+ if (itemTag.contains("pages")) {
+ List pages = new ArrayList<>();
+ ListTag pagesTag = itemTag.get("pages");
+ for (Tag tag : pagesTag.getValue()) {
+ if (!(tag instanceof CompoundTag))
+ continue;
+
+ CompoundTag pageTag = (CompoundTag) tag;
+
+ StringTag textTag = pageTag.get("text");
+ pages.add(new StringTag(MessageUtils.getJavaMessage(textTag.getValue())));
+ }
+
+ itemTag.remove("pages");
+ itemTag.put(new ListTag("pages", pages));
+ }
+ }
+}
diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/item/translators/nbt/EnchantedBookTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/item/translators/nbt/EnchantedBookTranslator.java
new file mode 100644
index 000000000..2ca6e35cc
--- /dev/null
+++ b/connector/src/main/java/org/geysermc/connector/network/translators/item/translators/nbt/EnchantedBookTranslator.java
@@ -0,0 +1,66 @@
+/*
+ * 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.item.translators.nbt;
+
+import com.github.steveice10.opennbt.tag.builtin.CompoundTag;
+import com.github.steveice10.opennbt.tag.builtin.ListTag;
+import com.github.steveice10.opennbt.tag.builtin.Tag;
+import org.geysermc.connector.network.translators.ItemRemapper;
+import org.geysermc.connector.network.translators.NbtItemStackTranslator;
+import org.geysermc.connector.network.translators.item.ItemEntry;
+
+@ItemRemapper(priority = 1)
+public class EnchantedBookTranslator extends NbtItemStackTranslator {
+
+ @Override
+ public void translateToBedrock(CompoundTag itemTag, ItemEntry itemEntry) {
+ if (itemTag.contains("StoredEnchantments")) {
+ Tag enchTag = itemTag.get("StoredEnchantments");
+ if (enchTag instanceof ListTag) {
+ enchTag = new ListTag("Enchantments", ((ListTag) enchTag).getValue());
+ itemTag.remove("StoredEnchantments");
+ itemTag.put(enchTag);
+ }
+ }
+ }
+
+ @Override
+ public void translateToJava(CompoundTag itemTag, ItemEntry itemEntry) {
+ if (itemTag.contains("Enchantments")) {
+ Tag enchTag = itemTag.get("Enchantments");
+ if (enchTag instanceof ListTag) {
+ enchTag = new ListTag("StoredEnchantments", ((ListTag) enchTag).getValue());
+ itemTag.remove("Enchantments");
+ itemTag.put(enchTag);
+ }
+ }
+ }
+
+ @Override
+ public boolean acceptItem(ItemEntry itemEntry) {
+ return "minecraft:enchanted_book".equals(itemEntry.getJavaIdentifier());
+ }
+}
diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/item/translators/nbt/EnchantmentTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/item/translators/nbt/EnchantmentTranslator.java
new file mode 100644
index 000000000..41e1ae36a
--- /dev/null
+++ b/connector/src/main/java/org/geysermc/connector/network/translators/item/translators/nbt/EnchantmentTranslator.java
@@ -0,0 +1,142 @@
+/*
+ * 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.item.translators.nbt;
+
+import com.github.steveice10.opennbt.tag.builtin.*;
+import org.geysermc.connector.GeyserConnector;
+import org.geysermc.connector.network.translators.ItemRemapper;
+import org.geysermc.connector.network.translators.NbtItemStackTranslator;
+import org.geysermc.connector.network.translators.item.Enchantment;
+import org.geysermc.connector.network.translators.item.ItemEntry;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+@ItemRemapper
+public class EnchantmentTranslator extends NbtItemStackTranslator {
+
+ @Override
+ public void translateToBedrock(CompoundTag itemTag, ItemEntry itemEntry) {
+ List newTags = new ArrayList<>();
+ if (itemTag.contains("Enchantments")) {
+ ListTag enchantmentTag = itemTag.get("Enchantments");
+ for (Tag tag : enchantmentTag.getValue()) {
+ if (!(tag instanceof CompoundTag)) continue;
+
+ CompoundTag bedrockTag = remapEnchantment((CompoundTag) tag);
+ newTags.add(bedrockTag);
+ }
+ itemTag.remove("Enchantments");
+ }
+ if (itemTag.contains("StoredEnchantments")) {
+ ListTag enchantmentTag = itemTag.get("StoredEnchantments");
+ for (Tag tag : enchantmentTag.getValue()) {
+ if (!(tag instanceof CompoundTag)) continue;
+
+ CompoundTag bedrockTag = remapEnchantment((CompoundTag) tag);
+ if (bedrockTag != null) {
+ bedrockTag.put(new ShortTag("GeyserStoredEnchantment", (short) 0));
+ newTags.add(bedrockTag);
+ }
+ }
+ itemTag.remove("StoredEnchantments");
+ }
+
+ if (!newTags.isEmpty()) {
+ itemTag.put(new ListTag("ench", newTags));
+ }
+ }
+
+ @Override
+ public void translateToJava(CompoundTag itemTag, ItemEntry itemEntry) {
+ if (itemTag.contains("ench")) {
+ ListTag enchantmentTag = itemTag.get("ench");
+ List enchantments = new ArrayList<>();
+ List storedEnchantments = new ArrayList<>();
+ for (Tag value : enchantmentTag.getValue()) {
+ if (!(value instanceof CompoundTag))
+ continue;
+
+ CompoundTag tagValue = (CompoundTag) value;
+ ShortTag bedrockId = tagValue.get("id");
+ if (bedrockId == null) continue;
+
+ ShortTag geyserStoredEnchantmentTag = tagValue.get("GeyserStoredEnchantment");
+
+ Enchantment enchantment = Enchantment.getByBedrockId(bedrockId.getValue());
+ if (enchantment != null) {
+ CompoundTag javaTag = new CompoundTag("");
+ Map javaValue = javaTag.getValue();
+ javaValue.put("id", new StringTag("id", enchantment.getJavaIdentifier()));
+ ShortTag levelTag = tagValue.get("lvl");
+ javaValue.put("lvl", new IntTag("lvl", levelTag != null ? levelTag.getValue() : 1));
+ javaTag.setValue(javaValue);
+
+
+ if (geyserStoredEnchantmentTag != null) {
+ tagValue.remove("GeyserStoredEnchantment");
+ storedEnchantments.add(javaTag);
+ } else {
+ enchantments.add(javaTag);
+ }
+ } else {
+ GeyserConnector.getInstance().getLogger().debug("Unknown bedrock enchantment: " + bedrockId);
+ }
+ }
+ if (!enchantments.isEmpty()) {
+ itemTag.put(new ListTag("Enchantments", enchantments));
+ }
+ if (!storedEnchantments.isEmpty()) {
+ itemTag.put(new ListTag("StoredEnchantments", enchantments));
+ }
+ itemTag.remove("ench");
+ }
+ }
+
+
+ private CompoundTag remapEnchantment(CompoundTag tag) {
+ Tag javaEnchLvl = tag.get("lvl");
+ if (!(javaEnchLvl instanceof ShortTag))
+ return null;
+
+ Tag javaEnchId = tag.get("id");
+ if (!(javaEnchId instanceof StringTag))
+ return null;
+
+ Enchantment enchantment = Enchantment.getByJavaIdentifier(((StringTag) javaEnchId).getValue());
+ if (enchantment == null) {
+ GeyserConnector.getInstance().getLogger().debug("Unknown java enchantment: " + javaEnchId.getValue());
+ return null;
+ }
+
+ CompoundTag bedrockTag = new CompoundTag("");
+ bedrockTag.put(new ShortTag("id", (short) enchantment.ordinal()));
+ bedrockTag.put(new ShortTag("lvl", ((ShortTag) javaEnchLvl).getValue()));
+ return bedrockTag;
+ }
+
+}
diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/item/translators/nbt/LeatherArmorTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/item/translators/nbt/LeatherArmorTranslator.java
new file mode 100644
index 000000000..4b01d912d
--- /dev/null
+++ b/connector/src/main/java/org/geysermc/connector/network/translators/item/translators/nbt/LeatherArmorTranslator.java
@@ -0,0 +1,73 @@
+/*
+ * 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.item.translators.nbt;
+
+import com.github.steveice10.opennbt.tag.builtin.CompoundTag;
+import com.github.steveice10.opennbt.tag.builtin.IntTag;
+import org.geysermc.connector.network.translators.ItemRemapper;
+import org.geysermc.connector.network.translators.NbtItemStackTranslator;
+import org.geysermc.connector.network.translators.item.ItemEntry;
+
+@ItemRemapper
+public class LeatherArmorTranslator extends NbtItemStackTranslator {
+
+ private static final String[] ITEMS = new String[]{"minecraft:leather_helmet", "minecraft:leather_chestplate", "minecraft:leather_leggings", "minecraft:leather_boots"};
+
+ @Override
+ public void translateToBedrock(CompoundTag itemTag, ItemEntry itemEntry) {
+ if (itemTag.contains("display")) {
+ CompoundTag displayTag = itemTag.get("display");
+ if (displayTag.contains("color")) {
+ IntTag color = displayTag.get("color");
+ if (color != null) {
+ itemTag.put(new IntTag("customColor", color.getValue()));
+ displayTag.remove("color");
+ }
+ }
+ }
+ }
+
+ @Override
+ public void translateToJava(CompoundTag itemTag, ItemEntry itemEntry) {
+ if (itemTag.contains("customColor")) {
+ IntTag color = itemTag.get("customColor");
+ CompoundTag displayTag = itemTag.get("display");
+ if (displayTag == null) {
+ displayTag = new CompoundTag("display");
+ }
+ displayTag.put(color);
+ itemTag.remove("customColor");
+ }
+ }
+
+ @Override
+ public boolean acceptItem(ItemEntry itemEntry) {
+ for (String item : ITEMS) {
+ if (itemEntry.getJavaIdentifier().equals(item)) return true;
+ }
+ return false;
+ }
+}
diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/item/translators/nbt/MapItemTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/item/translators/nbt/MapItemTranslator.java
new file mode 100644
index 000000000..cdf272ece
--- /dev/null
+++ b/connector/src/main/java/org/geysermc/connector/network/translators/item/translators/nbt/MapItemTranslator.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright (c) 2019-2020 GeyserMC. http://geysermc.org
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ * @author GeyserMC
+ * @link https://github.com/GeyserMC/Geyser
+ */
+
+package org.geysermc.connector.network.translators.item.translators.nbt;
+
+import com.github.steveice10.opennbt.tag.builtin.CompoundTag;
+import com.github.steveice10.opennbt.tag.builtin.IntTag;
+import com.github.steveice10.opennbt.tag.builtin.StringTag;
+import org.geysermc.connector.network.translators.ItemRemapper;
+import org.geysermc.connector.network.translators.NbtItemStackTranslator;
+import org.geysermc.connector.network.translators.item.ItemEntry;
+
+@ItemRemapper
+public class MapItemTranslator extends NbtItemStackTranslator {
+
+ @Override
+ public void translateToBedrock(CompoundTag itemTag, ItemEntry itemEntry) {
+ IntTag mapId = itemTag.get("map");
+
+ if (mapId != null) {
+ itemTag.put(new StringTag("map_uuid", mapId.getValue().toString()));
+ itemTag.put(new IntTag("map_name_index", mapId.getValue()));
+ itemTag.remove("map");
+ }
+ }
+
+ @Override
+ public void translateToJava(CompoundTag itemTag, ItemEntry itemEntry) {
+ IntTag tag = itemTag.get("map_name_index");
+ if (tag != null) {
+ itemTag.put(new IntTag("map", tag.getValue()));
+ itemTag.remove("map_name_index");
+ itemTag.remove("map_uuid");
+ }
+ }
+}
diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/java/JavaBossBarTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/java/JavaBossBarTranslator.java
index 582f0b5aa..2c32ef6fe 100644
--- a/connector/src/main/java/org/geysermc/connector/network/translators/java/JavaBossBarTranslator.java
+++ b/connector/src/main/java/org/geysermc/connector/network/translators/java/JavaBossBarTranslator.java
@@ -26,80 +26,38 @@
package org.geysermc.connector.network.translators.java;
import org.geysermc.connector.network.session.GeyserSession;
+import org.geysermc.connector.network.session.cache.BossBar;
import org.geysermc.connector.network.translators.PacketTranslator;
import org.geysermc.connector.network.translators.Translator;
-import org.geysermc.connector.utils.MessageUtils;
import com.github.steveice10.mc.protocol.packet.ingame.server.ServerBossBarPacket;
-import com.nukkitx.math.vector.Vector3f;
-import com.nukkitx.protocol.bedrock.data.EntityData;
-import com.nukkitx.protocol.bedrock.packet.AddEntityPacket;
-import com.nukkitx.protocol.bedrock.packet.BossEventPacket;
-import com.nukkitx.protocol.bedrock.packet.RemoveEntityPacket;
+
+import java.awt.*;
@Translator(packet = ServerBossBarPacket.class)
public class JavaBossBarTranslator extends PacketTranslator {
@Override
public void translate(ServerBossBarPacket packet, GeyserSession session) {
- BossEventPacket bossEventPacket = new BossEventPacket();
- bossEventPacket.setBossUniqueEntityId(session.getEntityCache().getBossBar(packet.getUuid()));
-
+ BossBar bossBar = session.getEntityCache().getBossBar(packet.getUuid());
switch (packet.getAction()) {
case ADD:
- long entityId = session.getEntityCache().addBossBar(packet.getUuid());
- addBossEntity(session, entityId);
-
- bossEventPacket.setAction(BossEventPacket.Action.SHOW);
- bossEventPacket.setBossUniqueEntityId(entityId);
- bossEventPacket.setTitle(MessageUtils.getTranslatedBedrockMessage(packet.getTitle(), session.getClientData().getLanguageCode()));
- bossEventPacket.setHealthPercentage(packet.getHealth());
- bossEventPacket.setColor(0); //ignored by client
- bossEventPacket.setOverlay(1);
- bossEventPacket.setDarkenSky(0);
+ long entityId = session.getEntityCache().getNextEntityId().incrementAndGet();
+ bossBar = new BossBar(session, entityId, packet.getTitle(), packet.getHealth(), 0, 1, 0);
+ session.getEntityCache().addBossBar(packet.getUuid(), bossBar);
break;
case UPDATE_TITLE:
- bossEventPacket.setAction(BossEventPacket.Action.TITLE);
- bossEventPacket.setTitle(MessageUtils.getTranslatedBedrockMessage(packet.getTitle(), session.getClientData().getLanguageCode()));
+ if (bossBar != null) bossBar.updateTitle(packet.getTitle());
break;
case UPDATE_HEALTH:
- bossEventPacket.setAction(BossEventPacket.Action.HEALTH_PERCENTAGE);
- bossEventPacket.setHealthPercentage(packet.getHealth());
+ if (bossBar != null) bossBar.updateHealth(packet.getHealth());
break;
case REMOVE:
- bossEventPacket.setAction(BossEventPacket.Action.HIDE);
- removeBossEntity(session, session.getEntityCache().removeBossBar(packet.getUuid()));
+ session.getEntityCache().removeBossBar(packet.getUuid());
break;
case UPDATE_STYLE:
case UPDATE_FLAGS:
//todo
return;
}
-
- session.getUpstream().sendPacket(bossEventPacket);
- }
-
- /**
- * Bedrock still needs an entity to display the BossBar.
- * Just like 1.8 but it doesn't care about which entity
- */
- private void addBossEntity(GeyserSession session, long entityId) {
- AddEntityPacket addEntityPacket = new AddEntityPacket();
- addEntityPacket.setUniqueEntityId(entityId);
- addEntityPacket.setRuntimeEntityId(entityId);
- addEntityPacket.setIdentifier("minecraft:creeper");
- addEntityPacket.setEntityType(33);
- addEntityPacket.setPosition(session.getPlayerEntity().getPosition());
- addEntityPacket.setRotation(Vector3f.ZERO);
- addEntityPacket.setMotion(Vector3f.ZERO);
- addEntityPacket.getMetadata().put(EntityData.SCALE, 0.01F); // scale = 0 doesn't work?
-
- session.getUpstream().sendPacket(addEntityPacket);
- }
-
- private void removeBossEntity(GeyserSession session, long entityId) {
- RemoveEntityPacket removeEntityPacket = new RemoveEntityPacket();
- removeEntityPacket.setUniqueEntityId(entityId);
-
- session.getUpstream().sendPacket(removeEntityPacket);
}
}
diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/java/JavaDeclareRecipesTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/java/JavaDeclareRecipesTranslator.java
new file mode 100644
index 000000000..c78493a36
--- /dev/null
+++ b/connector/src/main/java/org/geysermc/connector/network/translators/java/JavaDeclareRecipesTranslator.java
@@ -0,0 +1,174 @@
+/*
+ * Copyright (c) 2019-2020 GeyserMC. http://geysermc.org
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ * @author GeyserMC
+ * @link https://github.com/GeyserMC/Geyser
+ */
+
+package org.geysermc.connector.network.translators.java;
+
+import com.github.steveice10.mc.protocol.data.game.recipe.Ingredient;
+import com.github.steveice10.mc.protocol.data.game.recipe.Recipe;
+import com.github.steveice10.mc.protocol.data.game.recipe.data.ShapedRecipeData;
+import com.github.steveice10.mc.protocol.data.game.recipe.data.ShapelessRecipeData;
+import com.github.steveice10.mc.protocol.packet.ingame.server.ServerDeclareRecipesPacket;
+import com.nukkitx.nbt.tag.CompoundTag;
+import com.nukkitx.protocol.bedrock.data.CraftingData;
+import com.nukkitx.protocol.bedrock.data.ItemData;
+import com.nukkitx.protocol.bedrock.data.PotionMixData;
+import com.nukkitx.protocol.bedrock.packet.CraftingDataPacket;
+import it.unimi.dsi.fastutil.ints.IntOpenHashSet;
+import it.unimi.dsi.fastutil.ints.IntSet;
+import lombok.AllArgsConstructor;
+import lombok.EqualsAndHashCode;
+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.Translators;
+import org.geysermc.connector.network.translators.item.ItemEntry;
+import org.geysermc.connector.utils.Toolbox;
+
+import java.util.*;
+import java.util.stream.Collectors;
+
+@Translator(packet = ServerDeclareRecipesPacket.class)
+public class JavaDeclareRecipesTranslator extends PacketTranslator {
+ private static final Collection POTION_MIXES =
+ Arrays.stream(new int[]{372, 331, 348, 376, 289, 437, 353, 414, 382, 375, 462, 378, 396, 377, 370, 469, 470})
+ .mapToObj(ingredient -> new PotionMixData(0, ingredient, 0))
+ .collect(Collectors.toList());
+
+ @Override
+ public void translate(ServerDeclareRecipesPacket packet, GeyserSession session) {
+ CraftingDataPacket craftingDataPacket = new CraftingDataPacket();
+ craftingDataPacket.setCleanRecipes(true);
+ for (Recipe recipe : packet.getRecipes()) {
+ switch (recipe.getType()) {
+ case CRAFTING_SHAPELESS: {
+ ShapelessRecipeData shapelessRecipeData = (ShapelessRecipeData) recipe.getData();
+ ItemData output = Translators.getItemTranslator().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) {
+ UUID uuid = UUID.randomUUID();
+ craftingDataPacket.getCraftingData().add(CraftingData.fromShapeless(uuid.toString(),
+ inputs, new ItemData[]{output}, uuid, "crafting_table", 0));
+ }
+ break;
+ }
+ case CRAFTING_SHAPED: {
+ ShapedRecipeData shapedRecipeData = (ShapedRecipeData) recipe.getData();
+ ItemData output = Translators.getItemTranslator().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) {
+ UUID uuid = UUID.randomUUID();
+ craftingDataPacket.getCraftingData().add(CraftingData.fromShaped(uuid.toString(),
+ shapedRecipeData.getWidth(), shapedRecipeData.getHeight(), inputs,
+ new ItemData[]{output}, uuid, "crafting_table", 0));
+ }
+ break;
+ }
+ }
+ }
+ craftingDataPacket.getPotionMixData().addAll(POTION_MIXES);
+ session.getUpstream().sendPacket(craftingDataPacket);
+ }
+
+ //TODO: rewrite
+ private ItemData[][] combinations(GeyserSession session, Ingredient[] ingredients) {
+ Map, IntSet> squashedOptions = new HashMap<>();
+ for (int i = 0; i < ingredients.length; i++) {
+ if (ingredients[i].getOptions().length == 0) {
+ squashedOptions.computeIfAbsent(Collections.singleton(ItemData.AIR), k -> new IntOpenHashSet()).add(i);
+ continue;
+ }
+ Ingredient ingredient = ingredients[i];
+ Map> groupedByIds = Arrays.stream(ingredient.getOptions())
+ .map(item -> Translators.getItemTranslator().translateToBedrock(session, item))
+ .collect(Collectors.groupingBy(item -> new GroupedItem(item.getId(), item.getCount(), item.getTag())));
+ Set optionSet = new HashSet<>(groupedByIds.size());
+ for (Map.Entry> entry : groupedByIds.entrySet()) {
+ if (entry.getValue().size() > 1) {
+ GroupedItem groupedItem = entry.getKey();
+ int idCount = 0;
+ //not optimal
+ for (ItemEntry itemEntry : Toolbox.ITEM_ENTRIES.values()) {
+ if (itemEntry.getBedrockId() == groupedItem.id) {
+ idCount++;
+ }
+ }
+ if (entry.getValue().size() < idCount) {
+ optionSet.addAll(entry.getValue());
+ } else {
+ optionSet.add(ItemData.of(groupedItem.id, (short) -1, groupedItem.count, groupedItem.tag));
+ }
+ } else {
+ ItemData item = entry.getValue().get(0);
+ optionSet.add(item);
+ }
+ }
+ squashedOptions.computeIfAbsent(optionSet, k -> new IntOpenHashSet()).add(i);
+ }
+ int totalCombinations = 1;
+ for (Set optionSet : squashedOptions.keySet()) {
+ totalCombinations *= optionSet.size();
+ }
+ if (totalCombinations > 500) {
+ ItemData[] translatedItems = new ItemData[ingredients.length];
+ for (int i = 0; i < ingredients.length; i++) {
+ if (ingredients[i].getOptions().length > 0) {
+ translatedItems[i] = Translators.getItemTranslator().translateToBedrock(session, ingredients[i].getOptions()[0]);
+ } else {
+ translatedItems[i] = ItemData.AIR;
+ }
+ }
+ return new ItemData[][]{translatedItems};
+ }
+ List> sortedSets = new ArrayList<>(squashedOptions.keySet());
+ sortedSets.sort(Comparator.comparing(Set::size, Comparator.reverseOrder()));
+ ItemData[][] combinations = new ItemData[totalCombinations][ingredients.length];
+ int x = 1;
+ for (Set set : sortedSets) {
+ IntSet slotSet = squashedOptions.get(set);
+ int i = 0;
+ for (ItemData item : set) {
+ for (int j = 0; j < totalCombinations / set.size(); j++) {
+ final int comboIndex = (i * x) + (j % x) + ((j / x) * set.size() * x);
+ for (int slot : slotSet) {
+ combinations[comboIndex][slot] = item;
+ }
+ }
+ i++;
+ }
+ x *= set.size();
+ }
+ return combinations;
+ }
+
+ @EqualsAndHashCode
+ @AllArgsConstructor
+ private static class GroupedItem {
+ int id;
+ int count;
+ CompoundTag tag;
+ }
+}
diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/java/JavaPluginMessageTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/java/JavaPluginMessageTranslator.java
index 60227aa4a..cd0161dbc 100644
--- a/connector/src/main/java/org/geysermc/connector/network/translators/java/JavaPluginMessageTranslator.java
+++ b/connector/src/main/java/org/geysermc/connector/network/translators/java/JavaPluginMessageTranslator.java
@@ -33,14 +33,56 @@ import org.geysermc.connector.network.translators.Translator;
import com.github.steveice10.mc.protocol.packet.ingame.client.ClientPluginMessagePacket;
import com.github.steveice10.mc.protocol.packet.ingame.server.ServerPluginMessagePacket;
+import java.nio.charset.StandardCharsets;
+
@Translator(packet = ServerPluginMessagePacket.class)
public class JavaPluginMessageTranslator extends PacketTranslator {
+
+ private static byte[] brandData;
+
+ static {
+ byte[] data = GeyserConnector.NAME.getBytes(StandardCharsets.UTF_8);
+ byte[] varInt = writeVarInt(data.length);
+ brandData = new byte[varInt.length + data.length];
+ System.arraycopy(varInt, 0, brandData, 0, varInt.length);
+ System.arraycopy(data, 0, brandData, varInt.length, data.length);
+ }
+
+
@Override
public void translate(ServerPluginMessagePacket packet, GeyserSession session) {
if (packet.getChannel().equals("minecraft:brand")) {
session.getDownstream().getSession().send(
- new ClientPluginMessagePacket(packet.getChannel(), GeyserConnector.NAME.getBytes())
+ new ClientPluginMessagePacket(packet.getChannel(), brandData)
);
}
}
+
+ private static byte[] writeVarInt(int value) {
+ byte[] data = new byte[getVarIntLength(value)];
+ int index = 0;
+ do {
+ byte temp = (byte)(value & 0b01111111);
+ value >>>= 7;
+ if (value != 0) {
+ temp |= 0b10000000;
+ }
+ data[index] = temp;
+ index++;
+ } while (value != 0);
+ return data;
+ }
+
+ private static int getVarIntLength(int number) {
+ if ((number & 0xFFFFFF80) == 0) {
+ return 1;
+ } else if ((number & 0xFFFFC000) == 0) {
+ return 2;
+ } else if ((number & 0xFFE00000) == 0) {
+ return 3;
+ } else if ((number & 0xF0000000) == 0) {
+ return 4;
+ }
+ return 5;
+ }
}
diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/java/JavaRespawnTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/java/JavaRespawnTranslator.java
index 185aab540..cc2c540f6 100644
--- a/connector/src/main/java/org/geysermc/connector/network/translators/java/JavaRespawnTranslator.java
+++ b/connector/src/main/java/org/geysermc/connector/network/translators/java/JavaRespawnTranslator.java
@@ -25,6 +25,9 @@
package org.geysermc.connector.network.translators.java;
+import com.nukkitx.math.vector.Vector3f;
+import com.nukkitx.protocol.bedrock.data.LevelEventType;
+import com.nukkitx.protocol.bedrock.packet.LevelEventPacket;
import org.geysermc.connector.entity.Entity;
import org.geysermc.connector.entity.attribute.AttributeType;
import org.geysermc.connector.network.session.GeyserSession;
@@ -35,6 +38,8 @@ import org.geysermc.connector.utils.DimensionUtils;
import com.github.steveice10.mc.protocol.packet.ingame.server.ServerRespawnPacket;
import com.nukkitx.protocol.bedrock.packet.SetPlayerGameTypePacket;
+import java.util.concurrent.ThreadLocalRandom;
+
@Translator(packet = ServerRespawnPacket.class)
public class JavaRespawnTranslator extends PacketTranslator {
@@ -48,11 +53,19 @@ public class JavaRespawnTranslator extends PacketTranslator
// Max health must be divisible by two in bedrock
entity.getAttributes().put(AttributeType.HEALTH, AttributeType.HEALTH.getAttribute(maxHealth, (maxHealth % 2 == 1 ? maxHealth + 1 : maxHealth)));
+ session.getInventoryCache().setOpenInventory(null);
+
SetPlayerGameTypePacket playerGameTypePacket = new SetPlayerGameTypePacket();
playerGameTypePacket.setGamemode(packet.getGamemode().ordinal());
session.getUpstream().sendPacket(playerGameTypePacket);
session.setGameMode(packet.getGamemode());
+ LevelEventPacket stopRainPacket = new LevelEventPacket();
+ stopRainPacket.setType(LevelEventType.STOP_RAIN);
+ stopRainPacket.setData(ThreadLocalRandom.current().nextInt(50000) + 10000);
+ stopRainPacket.setPosition(Vector3f.ZERO);
+ session.getUpstream().sendPacket(stopRainPacket);
+
if (entity.getDimension() != DimensionUtils.javaToBedrock(packet.getDimension())) {
DimensionUtils.switchDimension(session, packet.getDimension());
} else {
diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/java/JavaServerDeclareCommandsTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/java/JavaServerDeclareCommandsTranslator.java
new file mode 100644
index 000000000..d57b89487
--- /dev/null
+++ b/connector/src/main/java/org/geysermc/connector/network/translators/java/JavaServerDeclareCommandsTranslator.java
@@ -0,0 +1,311 @@
+/*
+ * Copyright (c) 2019-2020 GeyserMC. http://geysermc.org
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ * @author GeyserMC
+ * @link https://github.com/GeyserMC/Geyser
+ */
+
+package org.geysermc.connector.network.translators.java;
+
+import com.github.steveice10.mc.protocol.data.game.command.CommandNode;
+import com.github.steveice10.mc.protocol.data.game.command.CommandParser;
+import com.github.steveice10.mc.protocol.packet.ingame.server.ServerDeclareCommandsPacket;
+import com.nukkitx.protocol.bedrock.data.CommandData;
+import com.nukkitx.protocol.bedrock.data.CommandEnumData;
+import com.nukkitx.protocol.bedrock.data.CommandParamData;
+import com.nukkitx.protocol.bedrock.packet.AvailableCommandsPacket;
+import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
+import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
+import lombok.Getter;
+import org.geysermc.connector.GeyserConnector;
+import org.geysermc.connector.network.session.GeyserSession;
+import org.geysermc.connector.network.translators.PacketTranslator;
+import org.geysermc.connector.network.translators.Translator;
+
+import java.util.*;
+
+@Translator(packet = ServerDeclareCommandsPacket.class)
+public class JavaServerDeclareCommandsTranslator extends PacketTranslator {
+ @Override
+ public void translate(ServerDeclareCommandsPacket packet, GeyserSession session) {
+ List commandData = new ArrayList<>();
+ Int2ObjectMap commands = new Int2ObjectOpenHashMap<>();
+ Int2ObjectMap> commandArgs = new Int2ObjectOpenHashMap<>();
+
+ // Get the first node, it should be a root node
+ CommandNode rootNode = packet.getNodes()[packet.getFirstNodeIndex()];
+
+ // Loop through the root nodes to get all commands
+ for (int nodeIndex : rootNode.getChildIndices()) {
+ CommandNode node = packet.getNodes()[nodeIndex];
+
+ // Make sure we don't have duplicated commands (happens if there is more than 1 root node)
+ if (commands.containsKey(nodeIndex)) { continue; }
+
+ // Get and update the commandArgs list with the found arguments
+ if (node.getChildIndices().length >= 1) {
+ for (int childIndex : node.getChildIndices()) {
+ commandArgs.putIfAbsent(nodeIndex, new ArrayList<>());
+ commandArgs.get(nodeIndex).add(packet.getNodes()[childIndex]);
+ }
+ }
+
+ // Insert the command name into the list
+ commands.put(nodeIndex, node.getName());
+ }
+
+ // The command flags, not sure what these do apart from break things
+ List flags = new ArrayList<>();
+
+ // Loop through all the found commands
+ for (int commandID : commands.keySet()) {
+ String commandName = commands.get(commandID);
+
+ // Create a basic alias
+ CommandEnumData aliases = new CommandEnumData( commandName + "Aliases", new String[] { commandName.toLowerCase() }, false);
+
+ // Get and parse all params
+ CommandParamData[][] params = getParams(packet.getNodes()[commandID], packet.getNodes());
+
+ // Build the completed command and add it to the final list
+ CommandData data = new CommandData(commandName, session.getConnector().getCommandManager().getDescription(commandName), flags, (byte) 0, aliases, params);
+ commandData.add(data);
+ }
+
+ // Add our commands to the AvailableCommandsPacket for the bedrock client
+ AvailableCommandsPacket availableCommandsPacket = new AvailableCommandsPacket();
+ for (CommandData data : commandData) {
+ availableCommandsPacket.getCommands().add(data);
+ }
+
+ GeyserConnector.getInstance().getLogger().debug("Sending command packet of " + commandData.size() + " commands");
+
+ // Finally, send the commands to the client
+ session.getUpstream().sendPacket(availableCommandsPacket);
+ }
+
+ /**
+ * Build the command parameter array for the given command
+ *
+ * @param commandNode The command to build the parameters for
+ * @param allNodes Every command node
+ *
+ * @return An array of parameter option arrays
+ */
+ private CommandParamData[][] getParams(CommandNode commandNode, CommandNode[] allNodes) {
+ // Check if the command is an alias and redirect it
+ if (commandNode.getRedirectIndex() != -1) {
+ GeyserConnector.getInstance().getLogger().debug("Redirecting command " + commandNode.getName() + " to " + allNodes[commandNode.getRedirectIndex()].getName());
+ commandNode = allNodes[commandNode.getRedirectIndex()];
+ }
+
+ if (commandNode.getChildIndices().length >= 1) {
+ // Create the root param node and build all the children
+ ParamInfo rootParam = new ParamInfo(commandNode, null);
+ rootParam.buildChildren(allNodes);
+
+ List treeData = rootParam.getTree();
+ CommandParamData[][] params = new CommandParamData[treeData.size()][];
+
+ // Fill the nested params array
+ int i = 0;
+ for (CommandParamData[] tree : treeData) {
+ params[i] = tree;
+ i++;
+ }
+
+ return params;
+ }
+
+ return new CommandParamData[0][0];
+ }
+
+ /**
+ * Convert Java edition command types to Bedrock edition
+ *
+ * @param parser Command type to convert
+ *
+ * @return Bedrock parameter data type
+ */
+ private CommandParamData.Type mapCommandType(CommandParser parser) {
+ if (parser == null) { return CommandParamData.Type.STRING; }
+
+ switch (parser) {
+ case FLOAT:
+ return CommandParamData.Type.FLOAT;
+
+ case INTEGER:
+ return CommandParamData.Type.INT;
+
+ case ENTITY:
+ case GAME_PROFILE:
+ return CommandParamData.Type.TARGET;
+
+ case BLOCK_POS:
+ return CommandParamData.Type.BLOCK_POSITION;
+
+ case COLUMN_POS:
+ case VEC3:
+ return CommandParamData.Type.POSITION;
+
+ case MESSAGE:
+ return CommandParamData.Type.MESSAGE;
+
+ case NBT:
+ case NBT_COMPOUND_TAG:
+ case NBT_TAG:
+ case NBT_PATH:
+ return CommandParamData.Type.JSON;
+
+ case RESOURCE_LOCATION:
+ return CommandParamData.Type.FILE_PATH;
+
+ case INT_RANGE:
+ return CommandParamData.Type.INT_RANGE;
+
+ case BOOL:
+ case DOUBLE:
+ case STRING:
+ case VEC2:
+ case BLOCK_STATE:
+ case BLOCK_PREDICATE:
+ case ITEM_STACK:
+ case ITEM_PREDICATE:
+ case COLOR:
+ case COMPONENT:
+ case OBJECTIVE:
+ case OBJECTIVE_CRITERIA:
+ case OPERATION: // Possibly OPERATOR
+ case PARTICLE:
+ case ROTATION:
+ case SCOREBOARD_SLOT:
+ case SCORE_HOLDER:
+ case SWIZZLE:
+ case TEAM:
+ case ITEM_SLOT:
+ case MOB_EFFECT:
+ case FUNCTION:
+ case ENTITY_ANCHOR:
+ case RANGE:
+ case FLOAT_RANGE:
+ case ITEM_ENCHANTMENT:
+ case ENTITY_SUMMON:
+ case DIMENSION:
+ case TIME:
+ default:
+ return CommandParamData.Type.STRING;
+ }
+ }
+
+ @Getter
+ private class ParamInfo {
+ private CommandNode paramNode;
+ private CommandParamData paramData;
+ private List children;
+
+ /**
+ * Create a new parameter info object
+ *
+ * @param paramNode CommandNode the parameter is for
+ * @param paramData The existing parameters for the command
+ */
+ public ParamInfo(CommandNode paramNode, CommandParamData paramData) {
+ this.paramNode = paramNode;
+ this.paramData = paramData;
+ this.children = new ArrayList<>();
+ }
+
+ /**
+ * Build the array of all the child parameters (recursive)
+ *
+ * @param allNodes Every command node
+ */
+ public void buildChildren(CommandNode[] allNodes) {
+ int enumIndex = -1;
+
+ for (int paramID : paramNode.getChildIndices()) {
+ CommandNode paramNode = allNodes[paramID];
+
+ if (paramNode.getParser() == null) {
+ if (enumIndex == -1) {
+ enumIndex = children.size();
+
+ // Create the new enum command
+ CommandEnumData enumData = new CommandEnumData(paramNode.getName(), new String[] { paramNode.getName() }, false);
+ children.add(new ParamInfo(paramNode, new CommandParamData(paramNode.getName(), false, enumData, mapCommandType(paramNode.getParser()), null, Collections.emptyList())));
+ } else {
+ // Get the existing enum
+ ParamInfo enumParamInfo = children.get(enumIndex);
+
+ // Extend the current list of enum values
+ String[] enumOptions = Arrays.copyOf(enumParamInfo.getParamData().getEnumData().getValues(), enumParamInfo.getParamData().getEnumData().getValues().length + 1);
+ enumOptions[enumOptions.length - 1] = paramNode.getName();
+
+ // Re-create the command using the updated values
+ CommandEnumData enumData = new CommandEnumData(enumParamInfo.getParamData().getEnumData().getName(), enumOptions, false);
+ children.set(enumIndex, new ParamInfo(enumParamInfo.getParamNode(), new CommandParamData(enumParamInfo.getParamData().getName(), false, enumData, enumParamInfo.getParamData().getType(), null, Collections.emptyList())));
+ }
+ }else{
+ // Put the non-enum param into the list
+ children.add(new ParamInfo(paramNode, new CommandParamData(paramNode.getName(), false, null, mapCommandType(paramNode.getParser()), null, Collections.emptyList())));
+ }
+ }
+
+ // Recursively build all child options
+ for (ParamInfo child : children) {
+ child.buildChildren(allNodes);
+ }
+ }
+
+ /**
+ * Get the tree of every parameter node (recursive)
+ *
+ * @return List of parameter options arrays for the command
+ */
+ public List getTree() {
+ List treeParamData = new ArrayList<>();
+
+ for (ParamInfo child : children) {
+ // Get the tree from the child
+ List childTree = child.getTree();
+
+ // Un-pack the tree append the child node to it and push into the list
+ for (CommandParamData[] subchild : childTree) {
+ CommandParamData[] tmpTree = new ArrayList() {
+ {
+ add(child.getParamData());
+ addAll(Arrays.asList(subchild));
+ }
+ }.toArray(new CommandParamData[0]);
+
+ treeParamData.add(tmpTree);
+ }
+
+ // If we have no more child parameters just the child
+ if (childTree.size() == 0) {
+ treeParamData.add(new CommandParamData[] { child.getParamData() });
+ }
+ }
+
+ return treeParamData;
+ }
+ }
+}
diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/java/entity/JavaEntityEffectTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/java/entity/JavaEntityEffectTranslator.java
index be54e967f..88e0969e3 100644
--- a/connector/src/main/java/org/geysermc/connector/network/translators/java/entity/JavaEntityEffectTranslator.java
+++ b/connector/src/main/java/org/geysermc/connector/network/translators/java/entity/JavaEntityEffectTranslator.java
@@ -26,6 +26,7 @@
package org.geysermc.connector.network.translators.java.entity;
import org.geysermc.connector.entity.Entity;
+import org.geysermc.connector.entity.PlayerEntity;
import org.geysermc.connector.network.session.GeyserSession;
import org.geysermc.connector.network.translators.PacketTranslator;
import org.geysermc.connector.network.translators.Translator;
@@ -42,6 +43,7 @@ public class JavaEntityEffectTranslator extends PacketTranslator {
@Override
public void translate(ServerPlayerActionAckPacket packet, GeyserSession session) {
+ LevelEventPacket levelEvent = new LevelEventPacket();
switch (packet.getAction()) {
case FINISH_DIGGING:
ChunkUtils.updateBlock(session, packet.getNewState(), packet.getPosition());
break;
+
+ case START_DIGGING:
+ levelEvent.setType(LevelEventType.BLOCK_START_BREAK);
+ levelEvent.setPosition(Vector3f.from(
+ packet.getPosition().getX(),
+ packet.getPosition().getY(),
+ packet.getPosition().getZ()
+ ));
+ double blockHardness = BlockTranslator.JAVA_RUNTIME_ID_TO_HARDNESS.get(packet.getNewState().getId());
+ PlayerInventory inventory = session.getInventory();
+ ItemStack item = inventory.getItemInHand();
+ ItemEntry itemEntry = null;
+ CompoundTag nbtData = new CompoundTag("");
+ if (item != null) {
+ itemEntry = Translators.getItemTranslator().getItem(item);
+ nbtData = item.getNbt();
+ }
+ double breakTime = Math.ceil(BlockUtils.getBreakTime(blockHardness, packet.getNewState().getId(), itemEntry, nbtData, session.getPlayerEntity()) * 20);
+ levelEvent.setData((int) (65535 / breakTime));
+ session.getUpstream().sendPacket(levelEvent);
+ break;
+
+ case CANCEL_DIGGING:
+ levelEvent.setType(LevelEventType.BLOCK_STOP_BREAK);
+ levelEvent.setPosition(Vector3f.from(
+ packet.getPosition().getX(),
+ packet.getPosition().getY(),
+ packet.getPosition().getZ()
+ ));
+ levelEvent.setData(0);
+ session.getUpstream().sendPacket(levelEvent);
+ break;
}
}
-}
+}
\ No newline at end of file
diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/java/entity/player/JavaPlayerChangeHeldItemTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/java/entity/player/JavaPlayerChangeHeldItemTranslator.java
new file mode 100644
index 000000000..c4c1a3af4
--- /dev/null
+++ b/connector/src/main/java/org/geysermc/connector/network/translators/java/entity/player/JavaPlayerChangeHeldItemTranslator.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 2019-2020 GeyserMC. http://geysermc.org
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ * @author GeyserMC
+ * @link https://github.com/GeyserMC/Geyser
+ */
+
+package org.geysermc.connector.network.translators.java.entity.player;
+
+import com.github.steveice10.mc.protocol.packet.ingame.server.entity.player.ServerPlayerChangeHeldItemPacket;
+import com.nukkitx.protocol.bedrock.packet.PlayerHotbarPacket;
+import org.geysermc.connector.network.session.GeyserSession;
+import org.geysermc.connector.network.translators.PacketTranslator;
+import org.geysermc.connector.network.translators.Translator;
+
+@Translator(packet = ServerPlayerChangeHeldItemPacket.class)
+public class JavaPlayerChangeHeldItemTranslator extends PacketTranslator {
+
+ @Override
+ public void translate(ServerPlayerChangeHeldItemPacket packet, GeyserSession session) {
+ PlayerHotbarPacket hotbarPacket = new PlayerHotbarPacket();
+ hotbarPacket.setContainerId(0);
+ hotbarPacket.setSelectedHotbarSlot(packet.getSlot());
+ hotbarPacket.setSelectHotbarSlot(true);
+ session.getUpstream().sendPacket(hotbarPacket);
+
+ session.getInventory().setHeldItemSlot(packet.getSlot());
+ }
+}
diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/java/inventory/OpenWindowPacketTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/java/inventory/OpenWindowPacketTranslator.java
deleted file mode 100644
index 2719a72a0..000000000
--- a/connector/src/main/java/org/geysermc/connector/network/translators/java/inventory/OpenWindowPacketTranslator.java
+++ /dev/null
@@ -1,22 +0,0 @@
-package org.geysermc.connector.network.translators.java.inventory;
-
-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;
-import org.geysermc.connector.network.translators.Translators;
-import org.geysermc.connector.network.translators.inventory.InventoryTranslator;
-
-import com.github.steveice10.mc.protocol.packet.ingame.server.window.ServerOpenWindowPacket;
-
-@Translator(packet = ServerOpenWindowPacket.class)
-public class OpenWindowPacketTranslator extends PacketTranslator {
- @Override
- public void translate(ServerOpenWindowPacket packet, GeyserSession session) {
- System.out.println("debug: " + packet.getType());
- InventoryTranslator translator = Translators.getInventoryTranslator();
-
- translator.openInventory(session, new Inventory(packet.getName(), packet.getWindowId(), packet.getType(), 54));
-
- }
-}
diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/java/scoreboard/JavaTeamTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/java/scoreboard/JavaTeamTranslator.java
index c9d1ccfe2..d3bc6b4e7 100644
--- a/connector/src/main/java/org/geysermc/connector/network/translators/java/scoreboard/JavaTeamTranslator.java
+++ b/connector/src/main/java/org/geysermc/connector/network/translators/java/scoreboard/JavaTeamTranslator.java
@@ -33,6 +33,7 @@ import org.geysermc.connector.network.session.GeyserSession;
import org.geysermc.connector.network.translators.PacketTranslator;
import org.geysermc.connector.network.translators.Translator;
import org.geysermc.connector.scoreboard.Scoreboard;
+import org.geysermc.connector.scoreboard.Team;
import org.geysermc.connector.scoreboard.UpdateType;
import org.geysermc.connector.utils.MessageUtils;
@@ -45,9 +46,10 @@ public class JavaTeamTranslator extends PacketTranslator {
@Override
public void translate(ServerTeamPacket packet, GeyserSession session) {
- GeyserConnector.getInstance().getLogger().debug("Team packet " + packet.getTeamName() + " " + packet.getAction()+" "+ Arrays.toString(packet.getPlayers()));
+ GeyserConnector.getInstance().getLogger().debug("Team packet " + packet.getTeamName() + " " + packet.getAction() + " " + Arrays.toString(packet.getPlayers()));
Scoreboard scoreboard = session.getScoreboardCache().getScoreboard();
+ Team team = scoreboard.getTeam(packet.getTeamName());
switch (packet.getAction()) {
case CREATE:
scoreboard.registerNewTeam(packet.getTeamName(), toPlayerSet(packet.getPlayers()))
@@ -57,22 +59,33 @@ public class JavaTeamTranslator extends PacketTranslator {
.setSuffix(MessageUtils.getBedrockMessage(packet.getSuffix()));
break;
case UPDATE:
- scoreboard.getTeam(packet.getTeamName())
- .setName(MessageUtils.getBedrockMessage(packet.getDisplayName()))
- .setColor(packet.getColor())
- .setPrefix(MessageUtils.getBedrockMessage(packet.getPrefix()))
- .setSuffix(MessageUtils.getBedrockMessage(packet.getSuffix()))
- .setUpdateType(UpdateType.UPDATE);
+ if (team != null) {
+ team.setName(MessageUtils.getBedrockMessage(packet.getDisplayName()))
+ .setColor(packet.getColor())
+ .setPrefix(MessageUtils.getBedrockMessage(packet.getPrefix()))
+ .setSuffix(MessageUtils.getBedrockMessage(packet.getSuffix()))
+ .setUpdateType(UpdateType.UPDATE);
+ } else {
+ GeyserConnector.getInstance().getLogger().error("Error while translating Team Packet " + packet.getAction() + "! Scoreboard Team " + packet.getTeamName() + " is not registered.");
+ }
break;
case ADD_PLAYER:
- scoreboard.getTeam(packet.getTeamName()).addEntities(packet.getPlayers());
+ if(team != null){
+ team.addEntities(packet.getPlayers());
+ } else {
+ GeyserConnector.getInstance().getLogger().error("Error while translating Team Packet " + packet.getAction() + "! Scoreboard Team " + packet.getTeamName() + " is not registered.");
+ }
break;
case REMOVE_PLAYER:
- scoreboard.getTeam(packet.getTeamName()).removeEntities(packet.getPlayers());
+ if(team != null){
+ team.removeEntities(packet.getPlayers());
+ } else {
+ GeyserConnector.getInstance().getLogger().error("Error while translating Team Packet " + packet.getAction() + "! Scoreboard Team " + packet.getTeamName() + " is not registered.");
+ }
break;
case REMOVE:
- scoreboard.removeTeam(packet.getTeamName());
- break;
+ scoreboard.removeTeam(packet.getTeamName());
+ break;
}
scoreboard.onUpdate();
}
diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/java/window/JavaCloseWindowTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/java/window/JavaCloseWindowTranslator.java
new file mode 100644
index 000000000..8162b82a4
--- /dev/null
+++ b/connector/src/main/java/org/geysermc/connector/network/translators/java/window/JavaCloseWindowTranslator.java
@@ -0,0 +1,45 @@
+/*
+ * 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.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;
+import org.geysermc.connector.utils.InventoryUtils;
+
+@Translator(packet = ServerCloseWindowPacket.class)
+public class JavaCloseWindowTranslator extends PacketTranslator {
+
+ @Override
+ public void translate(ServerCloseWindowPacket packet, GeyserSession session) {
+ ContainerClosePacket closePacket = new ContainerClosePacket();
+ closePacket.setWindowId((byte)packet.getWindowId());
+ session.getUpstream().sendPacket(closePacket);
+ InventoryUtils.closeInventory(session, packet.getWindowId());
+ }
+}
diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/java/window/JavaConfirmTransactionTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/java/window/JavaConfirmTransactionTranslator.java
new file mode 100644
index 000000000..9ecf45598
--- /dev/null
+++ b/connector/src/main/java/org/geysermc/connector/network/translators/java/window/JavaConfirmTransactionTranslator.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 2019-2020 GeyserMC. http://geysermc.org
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ * @author GeyserMC
+ * @link https://github.com/GeyserMC/Geyser
+ */
+
+package org.geysermc.connector.network.translators.java.window;
+
+import com.github.steveice10.mc.protocol.packet.ingame.client.window.ClientConfirmTransactionPacket;
+import com.github.steveice10.mc.protocol.packet.ingame.server.window.ServerConfirmTransactionPacket;
+import org.geysermc.connector.inventory.Inventory;
+import org.geysermc.connector.network.session.GeyserSession;
+import org.geysermc.connector.network.translators.PacketTranslator;
+import org.geysermc.connector.network.translators.Translator;
+import org.geysermc.connector.network.translators.inventory.InventoryTranslator;
+import org.geysermc.connector.utils.InventoryUtils;
+
+@Translator(packet = ServerConfirmTransactionPacket.class)
+public class JavaConfirmTransactionTranslator extends PacketTranslator {
+
+ @Override
+ public void translate(ServerConfirmTransactionPacket packet, GeyserSession session) {
+ if (!packet.isAccepted()) {
+ ClientConfirmTransactionPacket confirmPacket = new ClientConfirmTransactionPacket(packet.getWindowId(), packet.getActionId(), true);
+ session.getDownstream().getSession().send(confirmPacket);
+ }
+ }
+}
diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/java/window/JavaOpenWindowTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/java/window/JavaOpenWindowTranslator.java
index 9495bed48..38276193f 100644
--- a/connector/src/main/java/org/geysermc/connector/network/translators/java/window/JavaOpenWindowTranslator.java
+++ b/connector/src/main/java/org/geysermc/connector/network/translators/java/window/JavaOpenWindowTranslator.java
@@ -25,18 +25,68 @@
package org.geysermc.connector.network.translators.java.window;
+import com.github.steveice10.mc.protocol.packet.ingame.client.window.ClientCloseWindowPacket;
+import com.github.steveice10.mc.protocol.packet.ingame.server.window.ServerOpenWindowPacket;
+import com.google.gson.JsonObject;
+import com.google.gson.JsonParser;
+import com.nukkitx.protocol.bedrock.packet.ContainerClosePacket;
+import org.geysermc.connector.GeyserConnector;
+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;
+import org.geysermc.connector.network.translators.Translators;
+import org.geysermc.connector.network.translators.inventory.InventoryTranslator;
import org.geysermc.connector.utils.InventoryUtils;
-import com.github.steveice10.mc.protocol.packet.ingame.server.window.ServerOpenWindowPacket;
+import java.util.concurrent.TimeUnit;
@Translator(packet = ServerOpenWindowPacket.class)
public class JavaOpenWindowTranslator extends PacketTranslator {
@Override
public void translate(ServerOpenWindowPacket packet, GeyserSession session) {
- InventoryUtils.openInventory(session, packet);
+ if (packet.getWindowId() == 0) {
+ return;
+ }
+ InventoryTranslator newTranslator = Translators.getInventoryTranslators().get(packet.getType());
+ Inventory openInventory = session.getInventoryCache().getOpenInventory();
+ if (newTranslator == null) {
+ if (openInventory != null) {
+ ContainerClosePacket closePacket = new ContainerClosePacket();
+ closePacket.setWindowId((byte)openInventory.getId());
+ session.getUpstream().sendPacket(closePacket);
+ Translators.getInventoryTranslators().get(openInventory.getWindowType()).closeInventory(session, openInventory);
+ }
+ ClientCloseWindowPacket closeWindowPacket = new ClientCloseWindowPacket(packet.getWindowId());
+ session.getDownstream().getSession().send(closeWindowPacket);
+ return;
+ }
+
+ String name = packet.getName();
+ try {
+ JsonParser parser = new JsonParser();
+ JsonObject jsonObject = parser.parse(packet.getName()).getAsJsonObject();
+ if (jsonObject.has("text")) {
+ name = jsonObject.get("text").getAsString();
+ } else if (jsonObject.has("translate")) {
+ name = jsonObject.get("translate").getAsString();
+ }
+ } catch (Exception e) {
+ GeyserConnector.getInstance().getLogger().debug("JavaOpenWindowTranslator: " + e.toString());
+ }
+
+ Inventory newInventory = new Inventory(name, packet.getWindowId(), packet.getType(), newTranslator.size + 36);
+ session.getInventoryCache().cacheInventory(newInventory);
+ if (openInventory != null) {
+ InventoryTranslator openTranslator = Translators.getInventoryTranslators().get(openInventory.getWindowType());
+ if (!openTranslator.getClass().equals(newTranslator.getClass())) {
+ InventoryUtils.closeInventory(session, openInventory.getId());
+ GeyserConnector.getInstance().getGeneralThreadPool().schedule(() -> InventoryUtils.openInventory(session, newInventory), 500, TimeUnit.MILLISECONDS);
+ return;
+ }
+ }
+
+ InventoryUtils.openInventory(session, newInventory);
}
}
diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/java/window/JavaSetSlotTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/java/window/JavaSetSlotTranslator.java
index 93df65e82..6fafa2a47 100644
--- a/connector/src/main/java/org/geysermc/connector/network/translators/java/window/JavaSetSlotTranslator.java
+++ b/connector/src/main/java/org/geysermc/connector/network/translators/java/window/JavaSetSlotTranslator.java
@@ -25,47 +25,41 @@
package org.geysermc.connector.network.translators.java.window;
+import com.github.steveice10.mc.protocol.packet.ingame.server.window.ServerSetSlotPacket;
import org.geysermc.connector.inventory.Inventory;
import org.geysermc.connector.network.session.GeyserSession;
-import org.geysermc.connector.network.session.cache.InventoryCache;
import org.geysermc.connector.network.translators.PacketTranslator;
+import org.geysermc.connector.network.translators.Translators;
+import org.geysermc.connector.network.translators.inventory.InventoryTranslator;
import org.geysermc.connector.network.translators.Translator;
import org.geysermc.connector.utils.InventoryUtils;
-import com.github.steveice10.mc.protocol.data.game.entity.metadata.ItemStack;
-import com.github.steveice10.mc.protocol.packet.ingame.server.window.ServerSetSlotPacket;
+import java.util.Objects;
@Translator(packet = ServerSetSlotPacket.class)
public class JavaSetSlotTranslator extends PacketTranslator {
@Override
public void translate(ServerSetSlotPacket packet, GeyserSession session) {
- InventoryCache inventoryCache = session.getInventoryCache();
- if (!inventoryCache.getInventories().containsKey(packet.getWindowId())) {
- inventoryCache.cachePacket(packet.getWindowId(), packet);
+ if (packet.getWindowId() == 255 && packet.getSlot() == -1) { //cursor
+ if (Objects.equals(session.getInventory().getCursor(), packet.getItem()))
+ return;
+ if (session.getCraftSlot() != 0)
+ return;
+
+ session.getInventory().setCursor(packet.getItem());
+ InventoryUtils.updateCursor(session);
return;
}
- Inventory inventory = inventoryCache.getInventories().get(packet.getWindowId());
- if (packet.getWindowId() != 0 && inventory.getWindowType() == null)
+ Inventory inventory = session.getInventoryCache().getInventories().get(packet.getWindowId());
+ if (inventory == null || (packet.getWindowId() != 0 && inventory.getWindowType() == null))
return;
- // Player inventory
- if (packet.getWindowId() == 0) {
- if (packet.getSlot() >= inventory.getItems().length)
- return; // Most likely not a player inventory
-
- ItemStack[] items = inventory.getItems();
- items[packet.getSlot()] = packet.getItem();
- inventory.setItems(items);
-
- InventoryUtils.refreshPlayerInventory(session, inventory);
-
- if (inventory.isOpen()) {
- InventoryUtils.updateSlot(session, packet);
- } else {
- inventoryCache.cachePacket(packet.getWindowId(), packet);
- }
+ InventoryTranslator translator = Translators.getInventoryTranslators().get(inventory.getWindowType());
+ if (translator != null) {
+ inventory.setItem(packet.getSlot(), packet.getItem());
+ translator.updateSlot(session, inventory, packet.getSlot());
}
}
}
diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/java/window/JavaWindowItemsTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/java/window/JavaWindowItemsTranslator.java
index f2287f6dc..eab57a644 100644
--- a/connector/src/main/java/org/geysermc/connector/network/translators/java/window/JavaWindowItemsTranslator.java
+++ b/connector/src/main/java/org/geysermc/connector/network/translators/java/window/JavaWindowItemsTranslator.java
@@ -25,34 +25,34 @@
package org.geysermc.connector.network.translators.java.window;
+import com.github.steveice10.mc.protocol.packet.ingame.server.window.ServerWindowItemsPacket;
import org.geysermc.connector.inventory.Inventory;
import org.geysermc.connector.network.session.GeyserSession;
-import org.geysermc.connector.network.session.cache.InventoryCache;
import org.geysermc.connector.network.translators.PacketTranslator;
import org.geysermc.connector.network.translators.Translator;
-import org.geysermc.connector.utils.InventoryUtils;
+import org.geysermc.connector.network.translators.Translators;
+import org.geysermc.connector.network.translators.inventory.InventoryTranslator;
-import com.github.steveice10.mc.protocol.packet.ingame.server.window.ServerWindowItemsPacket;
+import java.util.Arrays;
@Translator(packet = ServerWindowItemsPacket.class)
public class JavaWindowItemsTranslator extends PacketTranslator {
@Override
public void translate(ServerWindowItemsPacket packet, GeyserSession session) {
- InventoryCache inventoryCache = session.getInventoryCache();
- if (!inventoryCache.getInventories().containsKey(packet.getWindowId())) {
- inventoryCache.cachePacket(packet.getWindowId(), packet);
+ Inventory inventory = session.getInventoryCache().getInventories().get(packet.getWindowId());
+ if (inventory == null || (packet.getWindowId() != 0 && inventory.getWindowType() == null))
return;
- }
- Inventory inventory = inventoryCache.getInventories().get(packet.getWindowId());
- // Player inventory
- if (packet.getWindowId() == 0) {
+ if (packet.getItems().length < inventory.getSize()) {
+ inventory.setItems(Arrays.copyOf(packet.getItems(), inventory.getSize()));
+ } else {
inventory.setItems(packet.getItems());
- InventoryUtils.refreshPlayerInventory(session, inventory);
- return;
}
- InventoryUtils.updateInventory(session, packet);
+ InventoryTranslator translator = Translators.getInventoryTranslators().get(inventory.getWindowType());
+ if (translator != null) {
+ translator.updateInventory(session, inventory);
+ }
}
}
diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/java/window/JavaWindowPropertyTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/java/window/JavaWindowPropertyTranslator.java
new file mode 100644
index 000000000..827da7b7c
--- /dev/null
+++ b/connector/src/main/java/org/geysermc/connector/network/translators/java/window/JavaWindowPropertyTranslator.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright (c) 2019-2020 GeyserMC. http://geysermc.org
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ * @author GeyserMC
+ * @link https://github.com/GeyserMC/Geyser
+ */
+
+package org.geysermc.connector.network.translators.java.window;
+
+import com.github.steveice10.mc.protocol.packet.ingame.server.window.ServerWindowPropertyPacket;
+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;
+import org.geysermc.connector.network.translators.Translators;
+import org.geysermc.connector.network.translators.inventory.InventoryTranslator;
+
+@Translator(packet = ServerWindowPropertyPacket.class)
+public class JavaWindowPropertyTranslator extends PacketTranslator {
+
+ @Override
+ public void translate(ServerWindowPropertyPacket packet, GeyserSession session) {
+ Inventory inventory = session.getInventoryCache().getInventories().get(packet.getWindowId());
+ if (inventory == null || (packet.getWindowId() != 0 && inventory.getWindowType() == null))
+ return;
+
+ InventoryTranslator translator = Translators.getInventoryTranslators().get(inventory.getWindowType());
+ if (translator != null) {
+ translator.updateProperty(session, inventory, packet.getRawProperty(), packet.getValue());
+ }
+ }
+}
diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/java/world/JavaBlockValueTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/java/world/JavaBlockValueTranslator.java
new file mode 100644
index 000000000..f4a4d9efa
--- /dev/null
+++ b/connector/src/main/java/org/geysermc/connector/network/translators/java/world/JavaBlockValueTranslator.java
@@ -0,0 +1,57 @@
+/*
+ * 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.world.block.value.ChestValue;
+import com.github.steveice10.mc.protocol.data.game.world.block.value.EndGatewayValue;
+import com.github.steveice10.mc.protocol.packet.ingame.server.world.ServerBlockValuePacket;
+import com.nukkitx.math.vector.Vector3i;
+import com.nukkitx.protocol.bedrock.packet.BlockEventPacket;
+
+import org.geysermc.connector.network.session.GeyserSession;
+import org.geysermc.connector.network.translators.PacketTranslator;
+import org.geysermc.connector.network.translators.Translator;
+
+@Translator(packet = ServerBlockValuePacket.class)
+public class JavaBlockValueTranslator extends PacketTranslator {
+
+ @Override
+ public void translate(ServerBlockValuePacket packet, GeyserSession session) {
+ BlockEventPacket blockEventPacket = new BlockEventPacket();
+ blockEventPacket.setBlockPosition(Vector3i.from(packet.getPosition().getX(),
+ packet.getPosition().getY(), packet.getPosition().getZ()));
+ if (packet.getValue() instanceof ChestValue) {
+ ChestValue value = (ChestValue) packet.getValue() ;
+ blockEventPacket.setEventType(1);
+ blockEventPacket.setEventData(value.getViewers() > 0 ? 1 : 0);
+ session.getUpstream().sendPacket(blockEventPacket);
+ }
+ if (packet.getValue() instanceof EndGatewayValue) {
+ blockEventPacket.setEventType(1);
+ session.getUpstream().sendPacket(blockEventPacket);
+ }
+ }
+}
diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/java/world/JavaChunkDataTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/java/world/JavaChunkDataTranslator.java
index bb73c5f02..e72038c51 100644
--- a/connector/src/main/java/org/geysermc/connector/network/translators/java/world/JavaChunkDataTranslator.java
+++ b/connector/src/main/java/org/geysermc/connector/network/translators/java/world/JavaChunkDataTranslator.java
@@ -25,6 +25,19 @@
package org.geysermc.connector.network.translators.java.world;
+import com.github.steveice10.mc.protocol.data.game.world.block.BlockState;
+import com.github.steveice10.mc.protocol.data.game.entity.metadata.Position;
+import com.github.steveice10.mc.protocol.packet.ingame.server.world.ServerChunkDataPacket;
+import com.nukkitx.nbt.NbtUtils;
+import com.nukkitx.nbt.stream.NBTOutputStream;
+import com.nukkitx.nbt.tag.CompoundTag;
+import com.nukkitx.network.VarInts;
+import com.nukkitx.protocol.bedrock.packet.LevelChunkPacket;
+
+import io.netty.buffer.ByteBuf;
+import io.netty.buffer.ByteBufOutputStream;
+import io.netty.buffer.Unpooled;
+import it.unimi.dsi.fastutil.objects.Object2IntMap;
import org.geysermc.connector.GeyserConnector;
import org.geysermc.connector.network.session.GeyserSession;
import org.geysermc.connector.network.translators.BiomeTranslator;
@@ -33,14 +46,7 @@ import org.geysermc.connector.network.translators.Translator;
import org.geysermc.connector.utils.ChunkUtils;
import org.geysermc.connector.world.chunk.ChunkSection;
-import com.github.steveice10.mc.protocol.data.game.chunk.Chunk;
-import com.github.steveice10.mc.protocol.data.game.world.block.BlockState;
-import com.github.steveice10.mc.protocol.packet.ingame.server.world.ServerChunkDataPacket;
-import com.nukkitx.network.VarInts;
-import com.nukkitx.protocol.bedrock.packet.LevelChunkPacket;
-
-import io.netty.buffer.ByteBuf;
-import io.netty.buffer.Unpooled;
+import java.util.Map;
@Translator(packet = ServerChunkDataPacket.class)
public class JavaChunkDataTranslator extends PacketTranslator {
@@ -54,7 +60,6 @@ public class JavaChunkDataTranslator extends PacketTranslator {
try {
ChunkUtils.ChunkData chunkData = ChunkUtils.translateToBedrock(packet.getColumn());
@@ -78,6 +83,14 @@ public class JavaChunkDataTranslator extends PacketTranslator blockEntityEntry : chunkData.getLoadBlockEntitiesLater().object2IntEntrySet()) {
+ int x = blockEntityEntry.getKey().getInt("x");
+ int y = blockEntityEntry.getKey().getInt("y");
+ int z = blockEntityEntry.getKey().getInt("z");
+ ChunkUtils.updateBlock(session, new BlockState(blockEntityEntry.getIntValue()), new Position(x, y, z));
+ }
+ chunkData.getLoadBlockEntitiesLater().clear();
+
} catch (Exception ex) {
ex.printStackTrace();
}
diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/java/world/JavaMapDataTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/java/world/JavaMapDataTranslator.java
index 28022c16d..78681f8fb 100644
--- a/connector/src/main/java/org/geysermc/connector/network/translators/java/world/JavaMapDataTranslator.java
+++ b/connector/src/main/java/org/geysermc/connector/network/translators/java/world/JavaMapDataTranslator.java
@@ -51,13 +51,12 @@ public class JavaMapDataTranslator extends PacketTranslator
mapItemDataPacket.setWidth(data.getColumns());
mapItemDataPacket.setHeight(data.getRows());
- // Every int entry is an ARGB color
+ // Every int entry is an ABGR color
int[] colors = new int[data.getData().length];
int idx = 0;
for (byte colorId : data.getData()) {
- colors[idx] = MapColor.fromId(colorId).toARGB();
- idx++;
+ colors[idx++] = MapColor.fromId(colorId & 0xFF).toABGR();
}
mapItemDataPacket.setColors(colors);
diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/java/world/JavaNotifyClientTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/java/world/JavaNotifyClientTranslator.java
index 6c7eeaf92..3c11d87b6 100644
--- a/connector/src/main/java/org/geysermc/connector/network/translators/java/world/JavaNotifyClientTranslator.java
+++ b/connector/src/main/java/org/geysermc/connector/network/translators/java/world/JavaNotifyClientTranslator.java
@@ -25,14 +25,6 @@
package org.geysermc.connector.network.translators.java.world;
-import java.util.Set;
-import java.util.concurrent.ThreadLocalRandom;
-
-import org.geysermc.connector.entity.Entity;
-import org.geysermc.connector.network.session.GeyserSession;
-import org.geysermc.connector.network.translators.PacketTranslator;
-import org.geysermc.connector.network.translators.Translator;
-
import com.github.steveice10.mc.protocol.data.game.ClientRequest;
import com.github.steveice10.mc.protocol.data.game.entity.player.GameMode;
import com.github.steveice10.mc.protocol.data.game.world.notify.EnterCreditsValue;
@@ -43,13 +35,16 @@ import com.nukkitx.protocol.bedrock.data.EntityDataMap;
import com.nukkitx.protocol.bedrock.data.EntityFlag;
import com.nukkitx.protocol.bedrock.data.LevelEventType;
import com.nukkitx.protocol.bedrock.data.PlayerPermission;
-import com.nukkitx.protocol.bedrock.packet.AdventureSettingsPacket;
-import com.nukkitx.protocol.bedrock.packet.LevelEventPacket;
-import com.nukkitx.protocol.bedrock.packet.SetEntityDataPacket;
-import com.nukkitx.protocol.bedrock.packet.SetPlayerGameTypePacket;
-import com.nukkitx.protocol.bedrock.packet.ShowCreditsPacket;
-
+import com.nukkitx.protocol.bedrock.packet.*;
import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet;
+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.inventory.PlayerInventoryTranslator;
+
+import java.util.Set;
+import java.util.concurrent.ThreadLocalRandom;
@Translator(packet = ServerNotifyClientPacket.class)
public class JavaNotifyClientTranslator extends PacketTranslator {
@@ -110,6 +105,10 @@ public class JavaNotifyClientTranslator extends PacketTranslator {
+
+ // This should be modified if sign text is not showing up
+ private static final int DELAY = 500;
+
+ @Override
+ public void translate(ServerUpdateTileEntityPacket packet, GeyserSession session) {
+ String id = BlockEntityUtils.getBedrockBlockEntityId(packet.getType().name());
+ BlockEntityTranslator translator = BlockEntityUtils.getBlockEntityTranslator(id);
+ // If not null then the BlockState is used in BlockEntityTranslator.translateTag()
+ if (ChunkUtils.CACHED_BLOCK_ENTITIES.get(packet.getPosition()) != null) {
+ BlockEntityUtils.updateBlockEntity(session, translator.getBlockEntityTag(id, packet.getNbt(),
+ ChunkUtils.CACHED_BLOCK_ENTITIES.get(packet.getPosition())), packet.getPosition());
+ ChunkUtils.CACHED_BLOCK_ENTITIES.remove(packet.getPosition());
+ } else if (translator.getClass().getAnnotation(BlockEntity.class).delay()) {
+ // Delay so chunks can finish sending
+ session.getConnector().getGeneralThreadPool().schedule(() ->
+ BlockEntityUtils.updateBlockEntity(session, translator.getBlockEntityTag(id, packet.getNbt(), null), packet.getPosition()),
+ DELAY, TimeUnit.MILLISECONDS);
+ } else {
+ BlockEntityUtils.updateBlockEntity(session, translator.getBlockEntityTag(id, packet.getNbt(), null), packet.getPosition());
+ }
+ }
+}
diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/java/world/JavaUpdateTimeTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/java/world/JavaUpdateTimeTranslator.java
index ccb69856c..365338443 100644
--- a/connector/src/main/java/org/geysermc/connector/network/translators/java/world/JavaUpdateTimeTranslator.java
+++ b/connector/src/main/java/org/geysermc/connector/network/translators/java/world/JavaUpdateTimeTranslator.java
@@ -25,6 +25,10 @@
package org.geysermc.connector.network.translators.java.world;
+import com.nukkitx.protocol.bedrock.data.GameRuleData;
+import com.nukkitx.protocol.bedrock.packet.GameRulesChangedPacket;
+import it.unimi.dsi.fastutil.longs.Long2LongMap;
+import it.unimi.dsi.fastutil.longs.Long2LongOpenHashMap;
import org.geysermc.connector.network.session.GeyserSession;
import org.geysermc.connector.network.translators.PacketTranslator;
import org.geysermc.connector.network.translators.Translator;
@@ -35,11 +39,37 @@ import com.nukkitx.protocol.bedrock.packet.SetTimePacket;
@Translator(packet = ServerUpdateTimePacket.class)
public class JavaUpdateTimeTranslator extends PacketTranslator {
+ // If negative, the last time is stored so we know it's not some plugin behavior doing weird things.
+ // Per-player for multi-world support
+ private static final Long2LongMap LAST_RECORDED_TIMES = new Long2LongOpenHashMap();
+
@Override
public void translate(ServerUpdateTimePacket packet, GeyserSession session) {
- // https://minecraft.gamepedia.com/Day-night_cycle#24-hour_Minecraft_day
- SetTimePacket setTimePacket = new SetTimePacket();
- setTimePacket.setTime((int) Math.abs(packet.getTime()) % 24000);
- session.getUpstream().sendPacket(setTimePacket);
+
+ // Bedrock sends a GameRulesChangedPacket if there is no daylight cycle
+ // Java just sends a negative long if there is no daylight cycle
+ long lastTime = LAST_RECORDED_TIMES.getOrDefault(session.getPlayerEntity().getEntityId(), 0);
+ long time = packet.getTime();
+
+ if (lastTime != time) {
+ // https://minecraft.gamepedia.com/Day-night_cycle#24-hour_Minecraft_day
+ SetTimePacket setTimePacket = new SetTimePacket();
+ setTimePacket.setTime((int) Math.abs(time) % 24000);
+ session.getUpstream().sendPacket(setTimePacket);
+ // TODO: Performance efficient to always do this?
+ LAST_RECORDED_TIMES.put(session.getPlayerEntity().getEntityId(), time);
+ }
+ if (lastTime < 0 && time >= 0) {
+ setDoDaylightCycleGamerule(session, true);
+ } else if (lastTime != time && time < 0) {
+ setDoDaylightCycleGamerule(session, false);
+ }
}
+
+ private void setDoDaylightCycleGamerule(GeyserSession session, boolean doCycle) {
+ GameRulesChangedPacket gameRulesChangedPacket = new GameRulesChangedPacket();
+ gameRulesChangedPacket.getGameRules().add(new GameRuleData<>("dodaylightcycle", doCycle));
+ session.getUpstream().sendPacket(gameRulesChangedPacket);
+ }
+
}
diff --git a/connector/src/main/java/org/geysermc/connector/scoreboard/Objective.java b/connector/src/main/java/org/geysermc/connector/scoreboard/Objective.java
index d9d78dfd1..c3e6c863c 100644
--- a/connector/src/main/java/org/geysermc/connector/scoreboard/Objective.java
+++ b/connector/src/main/java/org/geysermc/connector/scoreboard/Objective.java
@@ -54,6 +54,8 @@ public class Objective {
/**
* /!\ This method is made for temporary objectives until the real objective is received
+ * @param scoreboard the scoreboard
+ * @param objectiveName the name of the objective
*/
public Objective(Scoreboard scoreboard, String objectiveName) {
this(scoreboard);
diff --git a/connector/src/main/java/org/geysermc/connector/utils/BlockEntityUtils.java b/connector/src/main/java/org/geysermc/connector/utils/BlockEntityUtils.java
new file mode 100644
index 000000000..0dcd13ad9
--- /dev/null
+++ b/connector/src/main/java/org/geysermc/connector/utils/BlockEntityUtils.java
@@ -0,0 +1,47 @@
+package org.geysermc.connector.utils;
+
+import com.github.steveice10.mc.protocol.data.game.entity.metadata.Position;
+import com.nukkitx.math.vector.Vector3i;
+import com.nukkitx.protocol.bedrock.packet.BlockEntityDataPacket;
+
+import org.geysermc.connector.network.session.GeyserSession;
+import org.geysermc.connector.network.translators.Translators;
+import org.geysermc.connector.network.translators.block.entity.BlockEntityTranslator;
+
+public class BlockEntityUtils {
+
+ private static final BlockEntityTranslator EMPTY_TRANSLATOR = Translators.getBlockEntityTranslators().get("Empty");
+
+ public static String getBedrockBlockEntityId(String id) {
+ // This is the only exception when it comes to block entity ids
+ if (id.contains("piston_head"))
+ return "PistonArm";
+
+ id = id.toLowerCase()
+ .replace("minecraft:", "")
+ .replace("_", " ");
+ String[] 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(" ", "");
+ }
+
+ public static BlockEntityTranslator getBlockEntityTranslator(String name) {
+ BlockEntityTranslator blockEntityTranslator = Translators.getBlockEntityTranslators().get(name);
+ if (blockEntityTranslator == null) {
+ return EMPTY_TRANSLATOR;
+ }
+
+ return blockEntityTranslator;
+ }
+
+ public static void updateBlockEntity(GeyserSession session, com.nukkitx.nbt.tag.CompoundTag blockEntity, Position position) {
+ BlockEntityDataPacket blockEntityPacket = new BlockEntityDataPacket();
+ blockEntityPacket.setBlockPosition(Vector3i.from(position.getX(), position.getY(), position.getZ()));
+ blockEntityPacket.setData(blockEntity);
+ session.getUpstream().sendPacket(blockEntityPacket);
+ }
+}
diff --git a/connector/src/main/java/org/geysermc/connector/utils/BlockUtils.java b/connector/src/main/java/org/geysermc/connector/utils/BlockUtils.java
new file mode 100644
index 000000000..34287073e
--- /dev/null
+++ b/connector/src/main/java/org/geysermc/connector/utils/BlockUtils.java
@@ -0,0 +1,128 @@
+/*
+ * Copyright (c) 2019-2020 GeyserMC. http://geysermc.org
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ * @author GeyserMC
+ * @link https://github.com/GeyserMC/Geyser
+ */
+
+package org.geysermc.connector.utils;
+
+import com.github.steveice10.mc.protocol.data.game.entity.Effect;
+import com.github.steveice10.opennbt.tag.builtin.CompoundTag;
+import org.geysermc.connector.entity.PlayerEntity;
+import org.geysermc.connector.network.translators.block.BlockTranslator;
+import org.geysermc.connector.network.translators.item.ItemEntry;
+import org.geysermc.connector.network.translators.item.ToolItemEntry;
+
+public class BlockUtils {
+
+ private static boolean correctTool(String blockToolType, String itemToolType) {
+ return (blockToolType.equals("sword") && itemToolType.equals("sword")) ||
+ (blockToolType.equals("shovel") && itemToolType.equals("shovel")) ||
+ (blockToolType.equals("pickaxe") && itemToolType.equals("pickaxe")) ||
+ (blockToolType.equals("axe") && itemToolType.equals("axe")) ||
+ (blockToolType.equals("shears") && itemToolType.equals("shears"));
+ }
+
+ private static double toolBreakTimeBonus(String toolType, String toolTier, boolean isWoolBlock) {
+ if (toolType.equals("shears")) return isWoolBlock ? 5.0 : 15.0;
+ if (toolType.equals("")) return 1.0;
+ switch (toolTier) {
+ case "wooden":
+ return 2.0;
+ case "stone":
+ return 4.0;
+ case "iron":
+ return 6.0;
+ case "diamond":
+ return 8.0;
+ case "golden":
+ return 12.0;
+ default:
+ return 1.0;
+ }
+ }
+
+ //http://minecraft.gamepedia.com/Breaking
+ private static double calculateBreakTime(double blockHardness, String toolTier, boolean canHarvestWithHand, boolean correctTool,
+ String toolType, boolean isWoolBlock, boolean isCobweb, int toolEfficiencyLevel, int hasteLevel, int miningFatigueLevel
+ /*boolean insideOfWaterWithoutAquaAffinity, boolean outOfWaterButNotOnGround*/) {
+ double baseTime = ((correctTool || canHarvestWithHand) ? 1.5 : 5.0) * blockHardness;
+ double speed = 1.0 / baseTime;
+
+ if (correctTool) {
+ speed *= toolBreakTimeBonus(toolType, toolTier, isWoolBlock);
+ speed += toolEfficiencyLevel == 0 ? 0 : toolEfficiencyLevel * toolEfficiencyLevel + 1;
+ } else if (toolType.equals("sword")) {
+ speed*= (isCobweb ? 15.0 : 1.5);
+ }
+ speed *= 1.0 + (0.2 * hasteLevel);
+
+ switch (miningFatigueLevel) {
+ case 0:
+ break;
+ case 1:
+ speed -= (speed * 0.7);
+ break;
+ case 2:
+ speed -= (speed * 0.91);
+ break;
+ case 3:
+ speed -= (speed * 0.9973);
+ break;
+ default:
+ speed -= (speed * 0.99919);
+ break;
+ }
+
+ //if (insideOfWaterWithoutAquaAffinity) speed *= 0.2;
+ //if (outOfWaterButNotOnGround) speed *= 0.2;
+ // else if insideWaterAndNotOnGround speed *= 0.2;
+ return 1.0 / speed;
+ }
+
+ public static double getBreakTime(double blockHardness, int blockId, ItemEntry item, CompoundTag nbtData, PlayerEntity player) {
+ boolean isWoolBlock = BlockTranslator.JAVA_RUNTIME_WOOL_IDS.contains(blockId);
+ boolean isCobweb = blockId == BlockTranslator.JAVA_RUNTIME_COBWEB_ID;
+ String blockToolType = BlockTranslator.JAVA_RUNTIME_ID_TO_TOOL_TYPE.getOrDefault(blockId, "");
+ boolean canHarvestWithHand = BlockTranslator.JAVA_RUNTIME_ID_TO_CAN_HARVEST_WITH_HAND.get(blockId);
+ String toolType = "";
+ String toolTier = "";
+ boolean correctTool = false;
+ if (item instanceof ToolItemEntry) {
+ ToolItemEntry toolItem = (ToolItemEntry) item;
+ toolType = toolItem.getToolType();
+ toolTier = toolItem.getToolTier();
+ correctTool = correctTool(blockToolType, toolType);
+ }
+ int toolEfficiencyLevel = ItemUtils.getEnchantmentLevel(nbtData, "minecraft:efficiency");
+ int hasteLevel = player.getEffectCache().getEffectLevel(Effect.FASTER_DIG);
+ int miningFatigueLevel = player.getEffectCache().getEffectLevel(Effect.SLOWER_DIG);
+
+ // TODO implement these checks and material check if possible
+ //boolean insideOfWaterWithoutAquaAffinity = player.isInsideOfWater() &&
+ // Optional.ofNullable(player.getInventory().getHelmet().getEnchantment(Enchantment.ID_WATER_WORKER))
+ // .map(Enchantment::getLevel).map(l -> l >= 1).orElse(false);
+ //boolean outOfWaterButNotOnGround = (!player.isInsideOfWater()) && (!player.isOnGround());
+ return calculateBreakTime(blockHardness, toolTier, canHarvestWithHand, correctTool, toolType, isWoolBlock, isCobweb, toolEfficiencyLevel, hasteLevel, miningFatigueLevel);
+ }
+
+}
diff --git a/connector/src/main/java/org/geysermc/connector/utils/ChunkUtils.java b/connector/src/main/java/org/geysermc/connector/utils/ChunkUtils.java
index a35b2cc58..d496215ac 100644
--- a/connector/src/main/java/org/geysermc/connector/utils/ChunkUtils.java
+++ b/connector/src/main/java/org/geysermc/connector/utils/ChunkUtils.java
@@ -29,27 +29,47 @@ import com.github.steveice10.mc.protocol.data.game.chunk.Chunk;
import com.github.steveice10.mc.protocol.data.game.chunk.Column;
import com.github.steveice10.mc.protocol.data.game.entity.metadata.Position;
import com.github.steveice10.mc.protocol.data.game.world.block.BlockState;
+import com.github.steveice10.opennbt.tag.builtin.CompoundTag;
import com.nukkitx.math.vector.Vector2i;
import com.nukkitx.math.vector.Vector3i;
import com.nukkitx.protocol.bedrock.packet.LevelChunkPacket;
import com.nukkitx.protocol.bedrock.packet.NetworkChunkPublisherUpdatePacket;
import com.nukkitx.protocol.bedrock.packet.UpdateBlockPacket;
+
+import it.unimi.dsi.fastutil.objects.Object2IntMap;
+import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap;
+
+import lombok.Getter;
+import org.geysermc.connector.GeyserConnector;
import org.geysermc.connector.network.session.GeyserSession;
+import org.geysermc.connector.network.translators.block.entity.*;
import org.geysermc.connector.network.translators.Translators;
import org.geysermc.connector.network.translators.block.BlockTranslator;
+import org.geysermc.connector.world.chunk.ChunkPosition;
import org.geysermc.connector.world.chunk.ChunkSection;
+import java.util.HashMap;
+import java.util.Map;
+
import static org.geysermc.connector.network.translators.block.BlockTranslator.BEDROCK_WATER_ID;
public class ChunkUtils {
+
+ /**
+ * Temporarily stores positions of BlockState values that are needed for certain block entities actively
+ */
+ public static final Map CACHED_BLOCK_ENTITIES = new HashMap<>();
+
public static ChunkData translateToBedrock(Column column) {
ChunkData chunkData = new ChunkData();
-
Chunk[] chunks = column.getChunks();
- int chunkSectionCount = chunks.length;
- chunkData.sections = new ChunkSection[chunkSectionCount];
+ chunkData.sections = new ChunkSection[chunks.length];
- for (int chunkY = 0; chunkY < chunkSectionCount; chunkY++) {
+ CompoundTag[] blockEntities = column.getTileEntities();
+ // Temporarily stores positions of BlockState values per chunk load
+ Map blockEntityPositions = new HashMap<>();
+
+ for (int chunkY = 0; chunkY < chunks.length; chunkY++) {
chunkData.sections[chunkY] = new ChunkSection();
Chunk chunk = chunks[chunkY];
@@ -57,14 +77,28 @@ public class ChunkUtils {
continue;
ChunkSection section = chunkData.sections[chunkY];
-
for (int x = 0; x < 16; x++) {
for (int y = 0; y < 16; y++) {
for (int z = 0; z < 16; z++) {
BlockState blockState = chunk.get(x, y, z);
int id = BlockTranslator.getBedrockBlockId(blockState);
- section.getBlockStorageArray()[0].setFullBlock(ChunkSection.blockPosition(x, y, z), id);
+ // Check to see if the name is in BlockTranslator.getBlockEntityString, and therefore must be handled differently
+ if (BlockTranslator.getBlockEntityString(blockState) != null) {
+ // Get the block entity translator
+ BlockEntityTranslator blockEntityTranslator = BlockEntityUtils.getBlockEntityTranslator(BlockTranslator.getBlockEntityString(blockState));
+ Position pos = new ChunkPosition(column.getX(), column.getZ()).getBlock(x, (chunkY << 4) + y, z);
+ blockEntityPositions.put(pos, blockState);
+ // If there is a delay required for the block, allow it.
+ if (blockEntityTranslator.getClass().getAnnotation(BlockEntity.class).delay()) {
+ chunkData.loadBlockEntitiesLater.put(blockEntityTranslator.getDefaultBedrockTag(BlockEntityUtils.getBedrockBlockEntityId(BlockTranslator.getBlockEntityString(blockState)),
+ pos.getX(), pos.getY(), pos.getZ()), blockState.getId());
+ } else {
+ section.getBlockStorageArray()[0].setFullBlock(ChunkSection.blockPosition(x, y, z), id);
+ }
+ } else {
+ section.getBlockStorageArray()[0].setFullBlock(ChunkSection.blockPosition(x, y, z), id);
+ }
if (BlockTranslator.isWaterlogged(blockState)) {
section.getBlockStorageArray()[1].setFullBlock(ChunkSection.blockPosition(x, y, z), BEDROCK_WATER_ID);
@@ -72,7 +106,27 @@ public class ChunkUtils {
}
}
}
+
}
+
+ com.nukkitx.nbt.tag.CompoundTag[] bedrockBlockEntities = new com.nukkitx.nbt.tag.CompoundTag[blockEntities.length];
+ for (int i = 0; i < blockEntities.length; i++) {
+ CompoundTag tag = blockEntities[i];
+ String tagName;
+ if (!tag.contains("id")) {
+ GeyserConnector.getInstance().getLogger().debug("Got tag with no id: " + tag.getValue());
+ tagName = "Empty";
+ } else {
+ tagName = (String) tag.get("id").getValue();
+ }
+
+ String id = BlockEntityUtils.getBedrockBlockEntityId(tagName);
+ BlockEntityTranslator blockEntityTranslator = BlockEntityUtils.getBlockEntityTranslator(id);
+ BlockState blockState = blockEntityPositions.get(new Position((int) tag.get("x").getValue(), (int) tag.get("y").getValue(), (int) tag.get("z").getValue()));
+ bedrockBlockEntities[i] = blockEntityTranslator.getBlockEntityTag(tagName, tag, blockState);
+ }
+
+ chunkData.blockEntities = bedrockBlockEntities;
return chunkData;
}
@@ -114,6 +168,19 @@ public class ChunkUtils {
waterPacket.setRuntimeId(0);
}
session.getUpstream().sendPacket(waterPacket);
+
+ // Since Java stores bed colors/skull information as part of the namespaced ID and Bedrock stores it as a tag
+ // This is the only place I could find that interacts with the Java block state and block updates
+ // Iterates through all block entity translators and determines if the block state needs to be saved
+ for (Map.Entry entry : Translators.getBlockEntityTranslators().entrySet()) {
+ if (entry.getValue() instanceof RequiresBlockState) {
+ RequiresBlockState requiresBlockState = (RequiresBlockState) entry.getValue();
+ if (requiresBlockState.isBlock(blockState)) {
+ CACHED_BLOCK_ENTITIES.put(new Position(position.getX(), position.getY(), position.getZ()), blockState);
+ break; //No block will be a part of two classes
+ }
+ }
+ }
}
public static void sendEmptyChunks(GeyserSession session, Vector3i position, int radius, boolean forceUpdate) {
@@ -144,6 +211,9 @@ public class ChunkUtils {
public static final class ChunkData {
public ChunkSection[] sections;
- public byte[] blockEntities = new byte[0];
+ @Getter
+ private com.nukkitx.nbt.tag.CompoundTag[] blockEntities = new com.nukkitx.nbt.tag.CompoundTag[0];
+ @Getter
+ private Object2IntMap loadBlockEntitiesLater = new Object2IntOpenHashMap<>();
}
}
diff --git a/connector/src/main/java/org/geysermc/connector/utils/DimensionUtils.java b/connector/src/main/java/org/geysermc/connector/utils/DimensionUtils.java
index 199c5a5c6..9d874f13d 100644
--- a/connector/src/main/java/org/geysermc/connector/utils/DimensionUtils.java
+++ b/connector/src/main/java/org/geysermc/connector/utils/DimensionUtils.java
@@ -61,6 +61,12 @@ public class DimensionUtils {
session.getUpstream().sendPacket(stopSoundPacket);
}
+ /**
+ * Map the Java edition dimension IDs to Bedrock edition
+ *
+ * @param javaDimension Dimension ID to convert
+ * @return Converted Bedrock edition dimension ID
+ */
public static int javaToBedrock(int javaDimension) {
switch (javaDimension) {
case -1:
diff --git a/connector/src/main/java/org/geysermc/connector/utils/EntityUtils.java b/connector/src/main/java/org/geysermc/connector/utils/EntityUtils.java
index ec1ff4512..79ce9e8b9 100644
--- a/connector/src/main/java/org/geysermc/connector/utils/EntityUtils.java
+++ b/connector/src/main/java/org/geysermc/connector/utils/EntityUtils.java
@@ -32,6 +32,12 @@ import org.geysermc.connector.entity.type.EntityType;
public class EntityUtils {
+ /**
+ * Convert Java edition effect IDs to Bedrock edition
+ *
+ * @param effect Effect to convert
+ * @return The numeric ID for the Bedrock edition effect
+ */
public static int toBedrockEffectId(Effect effect) {
switch (effect) {
case GLOWING:
@@ -51,6 +57,13 @@ public class EntityUtils {
return effect.ordinal() + 1;
}
}
+
+ /**
+ * Converts a MobType to a Bedrock edition EntityType, returns null if the EntityType is not found
+ *
+ * @param type The MobType to convert
+ * @return Converted EntityType
+ */
public static EntityType toBedrockEntity(MobType type) {
try {
return EntityType.valueOf(type.name());
@@ -59,6 +72,12 @@ public class EntityUtils {
}
}
+ /**
+ * Converts a ObjectType to a Bedrock edition EntityType, returns null if the EntityType is not found
+ *
+ * @param type The ObjectType to convert
+ * @return Converted EntityType
+ */
public static EntityType toBedrockEntity(ObjectType type) {
try {
return EntityType.valueOf(type.name());
diff --git a/connector/src/main/java/org/geysermc/connector/utils/FileUtils.java b/connector/src/main/java/org/geysermc/connector/utils/FileUtils.java
index cf48f0055..5144628d5 100644
--- a/connector/src/main/java/org/geysermc/connector/utils/FileUtils.java
+++ b/connector/src/main/java/org/geysermc/connector/utils/FileUtils.java
@@ -30,14 +30,24 @@ import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.dataformat.yaml.YAMLFactory;
-
import org.geysermc.connector.GeyserConnector;
-import java.io.*;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
import java.util.function.Function;
public class FileUtils {
+ /**
+ * Load the given YAML file into the given class
+ *
+ * @param src File to load
+ * @param valueType Class to load file into
+ * @return The data as the given class
+ * @throws IOException if the config could not be loaded
+ */
public static T loadConfig(File src, Class valueType) throws IOException {
ObjectMapper objectMapper = new ObjectMapper(new YAMLFactory());
return objectMapper.readValue(src, valueType);
@@ -53,11 +63,28 @@ public class FileUtils {
return objectMapper.readValue(src, valueType);
}
- public static File fileOrCopiedFromResource(String name, Function s) throws IOException {
- return fileOrCopiedFromResource(new File(name), name, s);
+ /**
+ * Open the specified file or copy if from resources
+ *
+ * @param name File and resource name
+ * @param fallback Formatting callback
+ * @return File handle of the specified file
+ * @throws IOException if the file failed to copy from resource
+ */
+ public static File fileOrCopiedFromResource(String name, Function fallback) throws IOException {
+ return fileOrCopiedFromResource(new File(name), name, fallback);
}
- public static File fileOrCopiedFromResource(File file, String name, Function s) throws IOException {
+ /**
+ * Open the specified file or copy if from resources
+ *
+ * @param file File to open
+ * @param name Name of the resource get if needed
+ * @param format Formatting callback
+ * @return File handle of the specified file
+ * @throws IOException if the file failed to copy from resource
+ */
+ public static File fileOrCopiedFromResource(File file, String name, Function format) throws IOException {
if (!file.exists()) {
file.createNewFile();
FileOutputStream fos = new FileOutputStream(file);
@@ -67,7 +94,7 @@ public class FileUtils {
input.read(bytes);
- for(char c : s.apply(new String(bytes)).toCharArray()) {
+ for(char c : format.apply(new String(bytes)).toCharArray()) {
fos.write(c);
}
@@ -79,7 +106,14 @@ public class FileUtils {
return file;
}
- private static void writeFile(File file, char[] data) throws IOException {
+ /**
+ * Writes the given data to the specified file on disk
+ *
+ * @param file File to write to
+ * @param data Data to write to the file
+ * @throws IOException if the file failed to write
+ */
+ public static void writeFile(File file, char[] data) throws IOException {
if (!file.exists()) {
file.createNewFile();
}
@@ -94,6 +128,13 @@ public class FileUtils {
fos.close();
}
+ /**
+ * Writes the given data to the specified file on disk
+ *
+ * @param name File path to write to
+ * @param data Data to write to the file
+ * @throws IOException if the file failed to write
+ */
public static void writeFile(String name, char[] data) throws IOException {
writeFile(new File(name), data);
}
diff --git a/connector/src/main/java/org/geysermc/connector/utils/InventoryUtils.java b/connector/src/main/java/org/geysermc/connector/utils/InventoryUtils.java
index 045b81d16..3717b432a 100644
--- a/connector/src/main/java/org/geysermc/connector/utils/InventoryUtils.java
+++ b/connector/src/main/java/org/geysermc/connector/utils/InventoryUtils.java
@@ -26,113 +26,76 @@
package org.geysermc.connector.utils;
import com.github.steveice10.mc.protocol.data.game.entity.metadata.ItemStack;
-import com.github.steveice10.mc.protocol.packet.ingame.client.window.ClientCloseWindowPacket;
-import com.github.steveice10.mc.protocol.packet.ingame.server.window.ServerOpenWindowPacket;
-import com.github.steveice10.mc.protocol.packet.ingame.server.window.ServerSetSlotPacket;
-import com.github.steveice10.mc.protocol.packet.ingame.server.window.ServerWindowItemsPacket;
-import com.github.steveice10.packetlib.packet.Packet;
+import com.github.steveice10.opennbt.tag.builtin.CompoundTag;
import com.nukkitx.protocol.bedrock.data.ContainerId;
import com.nukkitx.protocol.bedrock.data.ItemData;
-import com.nukkitx.protocol.bedrock.packet.InventoryContentPacket;
+import com.nukkitx.protocol.bedrock.packet.InventorySlotPacket;
import org.geysermc.connector.GeyserConnector;
import org.geysermc.connector.inventory.Inventory;
import org.geysermc.connector.network.session.GeyserSession;
import org.geysermc.connector.network.translators.Translators;
+import org.geysermc.connector.network.translators.inventory.DoubleChestInventoryTranslator;
import org.geysermc.connector.network.translators.inventory.InventoryTranslator;
-import java.util.List;
+import java.util.Objects;
import java.util.concurrent.TimeUnit;
public class InventoryUtils {
+ public static final ItemStack REFRESH_ITEM = new ItemStack(1, 127, new CompoundTag("")); //TODO: stop using this
- public static void refreshPlayerInventory(GeyserSession session, Inventory inventory) {
- InventoryContentPacket inventoryContentPacket = new InventoryContentPacket();
- inventoryContentPacket.setContainerId(ContainerId.INVENTORY);
-
- ItemData[] contents = new ItemData[40];
- // Inventory
- for (int i = 9; i < 36; i++) {
- contents[i] = Translators.getItemTranslator().translateToBedrock(inventory.getItems()[i]);
+ public static void openInventory(GeyserSession session, Inventory inventory) {
+ InventoryTranslator translator = Translators.getInventoryTranslators().get(inventory.getWindowType());
+ if (translator != null) {
+ session.getInventoryCache().setOpenInventory(inventory);
+ translator.prepareInventory(session, inventory);
+ //TODO: find better way to handle double chest delay
+ if (translator instanceof DoubleChestInventoryTranslator) {
+ GeyserConnector.getInstance().getGeneralThreadPool().schedule(() -> {
+ translator.openInventory(session, inventory);
+ translator.updateInventory(session, inventory);
+ }, 200, TimeUnit.MILLISECONDS);
+ } else {
+ translator.openInventory(session, inventory);
+ translator.updateInventory(session, inventory);
+ }
}
-
- // Hotbar
- for (int i = 36; i < 45; i++) {
- contents[i - 36] = Translators.getItemTranslator().translateToBedrock(inventory.getItems()[i]);
- }
-
- // Armor
- for (int i = 5; i < 9; i++) {
- contents[i + 31] = Translators.getItemTranslator().translateToBedrock(inventory.getItems()[i]);
- }
-
- inventoryContentPacket.setContents(contents);
- session.getUpstream().sendPacket(inventoryContentPacket);
}
- public static void openInventory(GeyserSession session, ServerOpenWindowPacket packet) {
- Inventory inventory = new Inventory(packet.getWindowId(), packet.getType(), 45); // TODO: Find a way to set this value
- session.getInventoryCache().getInventories().put(packet.getWindowId(), inventory);
- session.getInventoryCache().setOpenInventory(inventory);
-
- InventoryTranslator translator = Translators.getInventoryTranslator();
- translator.prepareInventory(session, inventory);
- GeyserConnector.getInstance().getGeneralThreadPool().schedule(() -> {
- List packets = session.getInventoryCache().getCachedPackets().get(inventory.getId());
- packets.forEach(itemPacket -> {
- if (itemPacket != null) {
- if (ServerWindowItemsPacket.class.isAssignableFrom(itemPacket.getClass())) {
- updateInventory(session, (ServerWindowItemsPacket) itemPacket);
- }
- }
- });
- }, 200, TimeUnit.MILLISECONDS);
+ public static void closeInventory(GeyserSession session, int windowId) {
+ if (windowId != 0) {
+ Inventory inventory = session.getInventoryCache().getInventories().get(windowId);
+ if (inventory != null) {
+ InventoryTranslator translator = Translators.getInventoryTranslators().get(inventory.getWindowType());
+ translator.closeInventory(session, inventory);
+ session.getInventoryCache().uncacheInventory(windowId);
+ session.getInventoryCache().setOpenInventory(null);
+ }
+ } else {
+ Inventory inventory = session.getInventory();
+ InventoryTranslator translator = Translators.getInventoryTranslators().get(inventory.getWindowType());
+ translator.updateInventory(session, inventory);
+ }
+ session.setCraftSlot(0);
+ session.getInventory().setCursor(null);
}
- public static void updateInventory(GeyserSession session, ServerWindowItemsPacket packet) {
- if (packet.getWindowId() == 0)
- return;
-
- if (session.getInventoryCache().getOpenInventory() == null || !session.getInventoryCache().getInventories().containsKey(packet.getWindowId()))
- return;
-
- Inventory openInventory = session.getInventoryCache().getOpenInventory();
- if (packet.getWindowId() != openInventory.getId())
- return;
-
- InventoryTranslator translator = Translators.getInventoryTranslator();
- if (translator == null) {
- session.getDownstream().getSession().send(new ClientCloseWindowPacket(packet.getWindowId()));
- return;
- }
-
- openInventory.setItems(packet.getItems());
- translator.updateInventory(session, openInventory);
+ public static void updateCursor(GeyserSession session) {
+ InventorySlotPacket cursorPacket = new InventorySlotPacket();
+ cursorPacket.setContainerId(ContainerId.CURSOR);
+ cursorPacket.setSlot(0);
+ cursorPacket.setItem(Translators.getItemTranslator().translateToBedrock(session, session.getInventory().getCursor()));
+ session.getUpstream().sendPacket(cursorPacket);
}
- public static void updateSlot(GeyserSession session, ServerSetSlotPacket packet) {
- if (packet.getWindowId() == 0)
- return;
+ public static boolean canStack(ItemStack item1, ItemStack item2) {
+ if (item1 == null || item2 == null)
+ return false;
+ return item1.getId() == item2.getId() && Objects.equals(item1.getNbt(), item2.getNbt());
+ }
- if (session.getInventoryCache().getOpenInventory() == null || !session.getInventoryCache().getInventories().containsKey(packet.getWindowId()))
- return;
-
- Inventory openInventory = session.getInventoryCache().getOpenInventory();
- if (packet.getWindowId() != openInventory.getId())
- return;
-
- InventoryTranslator translator = Translators.getInventoryTranslator();
- if (translator == null) {
- session.getDownstream().getSession().send(new ClientCloseWindowPacket(packet.getWindowId()));
- return;
- }
-
- if (packet.getSlot() >= openInventory.getSize()) {
- session.getDownstream().getSession().send(new ClientCloseWindowPacket(packet.getWindowId()));
- return;
- }
-
- ItemStack[] items = openInventory.getItems();
- items[packet.getSlot()] = packet.getItem();
- translator.updateSlot(session, openInventory, packet.getSlot());
+ public static boolean canStack(ItemData item1, ItemData item2) {
+ if (item1 == null || item2 == null)
+ return false;
+ return item1.equals(item2, false, true, true);
}
}
diff --git a/connector/src/main/java/org/geysermc/connector/utils/ItemUtils.java b/connector/src/main/java/org/geysermc/connector/utils/ItemUtils.java
new file mode 100644
index 000000000..bb3cf0ed0
--- /dev/null
+++ b/connector/src/main/java/org/geysermc/connector/utils/ItemUtils.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 2019-2020 GeyserMC. http://geysermc.org
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ * @author GeyserMC
+ * @link https://github.com/GeyserMC/Geyser
+ */
+
+package org.geysermc.connector.utils;
+
+import com.github.steveice10.opennbt.tag.builtin.*;
+
+public class ItemUtils {
+
+ public static int getEnchantmentLevel(CompoundTag itemNBTData, String enchantmentId) {
+ ListTag enchantments = (itemNBTData == null ? null : itemNBTData.get("Enchantments"));
+ if (enchantments != null) {
+ int enchantmentLevel = 0;
+ for (Tag tag : enchantments) {
+ CompoundTag enchantment = (CompoundTag) tag;
+ StringTag enchantId = enchantment.get("id");
+ if (enchantId.getValue().equals(enchantmentId)) {
+ enchantmentLevel = (int) ((ShortTag) enchantment.get("lvl")).getValue();
+ }
+ }
+ return enchantmentLevel;
+ }
+ return 0;
+ }
+}
diff --git a/connector/src/main/java/org/geysermc/connector/utils/LocaleUtils.java b/connector/src/main/java/org/geysermc/connector/utils/LocaleUtils.java
index e8555eb02..726ad1c19 100644
--- a/connector/src/main/java/org/geysermc/connector/utils/LocaleUtils.java
+++ b/connector/src/main/java/org/geysermc/connector/utils/LocaleUtils.java
@@ -57,9 +57,15 @@ public class LocaleUtils {
downloadAndLoadLocale(DEFAULT_LOCALE);
}
+ /**
+ * Fetch the latest versions asset cache from Mojang so we can grab the locale files later
+ */
private static void generateAssetCache() {
try {
+ // Get the version manifest from Mojang
VersionManifest versionManifest = Toolbox.JSON_MAPPER.readValue(WebUtils.getBody("https://launchermeta.mojang.com/mc/game/version_manifest.json"), VersionManifest.class);
+
+ // Get the url for the latest version of the games manifest
String latestInfoURL = "";
for (Version version : versionManifest.getVersions()) {
if (version.getId().equals(versionManifest.getLatestVersion().getRelease())) {
@@ -68,12 +74,15 @@ public class LocaleUtils {
}
}
+ // Make sure we definitely got a version
if (latestInfoURL.isEmpty()) {
throw new Exception("Unable to get latest Minecraft version");
}
+ // Get the individual version manifest
VersionInfo versionInfo = Toolbox.JSON_MAPPER.readValue(WebUtils.getBody(latestInfoURL), VersionInfo.class);
+ // Get the smallest jar for use when downloading the en_us locale, will be either the server or client
int currentSize = Integer.MAX_VALUE;
for (VersionDownload download : versionInfo.getDownloads().values()) {
if (download.getUrl().endsWith(".jar") && download.getSize() < currentSize) {
@@ -82,8 +91,10 @@ public class LocaleUtils {
}
}
+ // Get the assets list
JsonNode assets = Toolbox.JSON_MAPPER.readTree(WebUtils.getBody(versionInfo.getAssetIndex().getUrl())).get("objects");
+ // Put each asset into an array for use later
Iterator> assetIterator = assets.fields();
while (assetIterator.hasNext()) {
Map.Entry entry = assetIterator.next();
@@ -95,8 +106,15 @@ public class LocaleUtils {
}
}
+ /**
+ * Downloads a locale from Mojang if its not already loaded
+ *
+ * @param locale Locale to download and load
+ */
public static void downloadAndLoadLocale(String locale) {
locale = locale.toLowerCase();
+
+ // Check the locale isn't already loaded
if (!ASSET_MAP.containsKey("minecraft/lang/" + locale + ".json") && !locale.equals("en_us")) {
GeyserConnector.getInstance().getLogger().warning("Invalid locale requested to download and load: " + locale);
return;
@@ -108,9 +126,15 @@ public class LocaleUtils {
loadLocale(locale);
}
+ /**
+ * Downloads the specified locale if its not already downloaded
+ *
+ * @param locale Locale to download
+ */
private static void downloadLocale(String locale) {
File localeFile = new File("locales/" + locale + ".json");
+ // Check if we have already downloaded the locale file
if (localeFile.exists()) {
GeyserConnector.getInstance().getLogger().debug("Locale already downloaded: " + locale);
return;
@@ -128,6 +152,11 @@ public class LocaleUtils {
WebUtils.downloadFile("http://resources.download.minecraft.net/" + hash.substring(0, 2) + "/" + hash, "locales/" + locale + ".json");
}
+ /**
+ * Loads a locale already downloaded, if the file doesn't exist it just logs a warning
+ *
+ * @param locale Locale to load
+ */
private static void loadLocale(String locale) {
File localeFile = new File("locales/" + locale + ".json");
@@ -146,7 +175,7 @@ public class LocaleUtils {
try {
localeObj = Toolbox.JSON_MAPPER.readTree(localeStream);
} catch (Exception e) {
- throw new AssertionError("Unable to load Java lang map for " + locale, e);
+ throw new AssertionError("Unable to load Java edition lang map for " + locale, e);
}
// Parse all the locale fields
@@ -164,6 +193,11 @@ public class LocaleUtils {
}
}
+ /**
+ * Download then en_us locale by downloading the server jar and extracting it from there.
+ *
+ * @param localeFile File to save the locale to
+ */
private static void downloadEN_US(File localeFile) {
try {
// Let the user know we are downloading the JAR
@@ -199,6 +233,13 @@ public class LocaleUtils {
}
}
+ /**
+ * Translate the given language string into the given locale, or falls back to the default locale
+ *
+ * @param messageText Language string to translate
+ * @param locale Locale to translate to
+ * @return Translated string or the original message if it was not found in the given locale
+ */
public static String getLocaleString(String messageText, String locale) {
Map localeStrings = LocaleUtils.LOCALE_MAPPINGS.get(locale.toLowerCase());
if (localeStrings == null)
diff --git a/connector/src/main/java/org/geysermc/connector/utils/LoginEncryptionUtils.java b/connector/src/main/java/org/geysermc/connector/utils/LoginEncryptionUtils.java
index 7f6c8eda1..300294a2f 100644
--- a/connector/src/main/java/org/geysermc/connector/utils/LoginEncryptionUtils.java
+++ b/connector/src/main/java/org/geysermc/connector/utils/LoginEncryptionUtils.java
@@ -40,9 +40,12 @@ import net.minidev.json.JSONObject;
import org.geysermc.common.window.CustomFormBuilder;
import org.geysermc.common.window.CustomFormWindow;
import org.geysermc.common.window.FormWindow;
+import org.geysermc.common.window.SimpleFormWindow;
+import org.geysermc.common.window.button.FormButton;
import org.geysermc.common.window.component.InputComponent;
import org.geysermc.common.window.component.LabelComponent;
import org.geysermc.common.window.response.CustomFormResponse;
+import org.geysermc.common.window.response.SimpleFormResponse;
import org.geysermc.connector.GeyserConnector;
import org.geysermc.connector.network.session.GeyserSession;
import org.geysermc.connector.network.session.auth.AuthData;
@@ -152,43 +155,60 @@ public class LoginEncryptionUtils {
session.getUpstream().sendPacketImmediately(packet);
}
- private static int AUTH_FORM_ID = 1337;
+ private static int AUTH_FORM_ID = 1336;
+ private static int AUTH_DETAILS_FORM_ID = 1337;
public static void showLoginWindow(GeyserSession session) {
- CustomFormWindow window = new CustomFormBuilder("Login")
- .addComponent(new LabelComponent("Minecraft: Java Edition account authentication."))
+ SimpleFormWindow window = new SimpleFormWindow("Login", "You need a Java Edition account to play on this server.");
+ window.getButtons().add(new FormButton("Login with Minecraft"));
+ window.getButtons().add(new FormButton("Disconnect"));
+
+ session.sendForm(window, AUTH_FORM_ID);
+ }
+
+ public static void showLoginDetailsWindow(GeyserSession session) {
+ CustomFormWindow window = new CustomFormBuilder("Login Details")
.addComponent(new LabelComponent("Enter the credentials for your Minecraft: Java Edition account below."))
.addComponent(new InputComponent("Email/Username", "account@geysermc.org", ""))
.addComponent(new InputComponent("Password", "123456", ""))
.build();
- session.sendForm(window, AUTH_FORM_ID);
+ session.sendForm(window, AUTH_DETAILS_FORM_ID);
}
- public static boolean authenticateFromForm(GeyserSession session, GeyserConnector connector, String formData) {
+ public static boolean authenticateFromForm(GeyserSession session, GeyserConnector connector, int formId, String formData) {
WindowCache windowCache = session.getWindowCache();
- if (!windowCache.getWindows().containsKey(AUTH_FORM_ID))
+ if (!windowCache.getWindows().containsKey(formId))
return false;
- FormWindow window = windowCache.getWindows().remove(AUTH_FORM_ID);
- window.setResponse(formData.trim());
+ if(formId == AUTH_FORM_ID || formId == AUTH_DETAILS_FORM_ID) {
+ FormWindow window = windowCache.getWindows().remove(formId);
+ window.setResponse(formData.trim());
- if (!session.isLoggedIn()) {
- if (window instanceof CustomFormWindow) {
- CustomFormWindow customFormWindow = (CustomFormWindow) window;
- if (!customFormWindow.getTitle().equals("Login"))
- return false;
+ if (!session.isLoggedIn()) {
+ if (formId == AUTH_DETAILS_FORM_ID && window instanceof CustomFormWindow) {
+ CustomFormWindow customFormWindow = (CustomFormWindow) window;
- CustomFormResponse response = (CustomFormResponse) customFormWindow.getResponse();
- if (response != null) {
- String email = response.getInputResponses().get(2);
- String password = response.getInputResponses().get(3);
+ CustomFormResponse response = (CustomFormResponse) customFormWindow.getResponse();
+ if (response != null) {
+ String email = response.getInputResponses().get(1);
+ String password = response.getInputResponses().get(2);
- session.authenticate(email, password);
+ session.authenticate(email, password);
+ }
+
+ // Clear windows so authentication data isn't accidentally cached
+ windowCache.getWindows().clear();
+ } else if (formId == AUTH_FORM_ID && window instanceof SimpleFormWindow) {
+ SimpleFormResponse response = (SimpleFormResponse) window.getResponse();
+ if(response != null) {
+ if(response.getClickedButtonId() == 0) {
+ showLoginDetailsWindow(session);
+ } else if(response.getClickedButtonId() == 1) {
+ session.disconnect("Login is required");
+ }
+ }
}
-
- // Clear windows so authentication data isn't accidentally cached
- windowCache.getWindows().clear();
}
}
return true;
diff --git a/connector/src/main/java/org/geysermc/connector/utils/MapColor.java b/connector/src/main/java/org/geysermc/connector/utils/MapColor.java
index 2c4a13b9f..b011edc71 100644
--- a/connector/src/main/java/org/geysermc/connector/utils/MapColor.java
+++ b/connector/src/main/java/org/geysermc/connector/utils/MapColor.java
@@ -1,7 +1,5 @@
package org.geysermc.connector.utils;
-import java.util.Arrays;
-
public enum MapColor {
COLOR_0(-1, -1, -1),
COLOR_1(-1, -1, -1),
@@ -212,6 +210,8 @@ public enum MapColor {
COLOR_206(37, 22, 16),
COLOR_207(19, 11, 8);
+ private static final MapColor[] VALUES = values();
+
private final int red;
private final int green;
private final int blue;
@@ -222,23 +222,18 @@ public enum MapColor {
this.blue = blue;
}
- int getId() {
- return ordinal();
- }
-
public static MapColor fromId(int id) {
- return Arrays.stream(values()).filter(color -> color.getId() == id).findFirst().get();
+ return id >= 0 && id < VALUES.length ? VALUES[id] : COLOR_0;
}
- public int toARGB() {
+ public int toABGR() {
int alpha = 255;
if (red == -1 && green == -1 && blue == -1)
alpha = 0; // transparent
- long result = red & 0xff;
- result |= (green & 0xff) << 8;
- result |= (blue & 0xff) << 16;
- result |= (alpha & 0xff) << 24;
- return (int) (result & 0xFFFFFFFFL);
+ return ((alpha & 0xFF) << 24) |
+ ((blue & 0xFF) << 16) |
+ ((green & 0xFF) << 8) |
+ ((red & 0xFF) << 0);
}
}
\ No newline at end of file
diff --git a/connector/src/main/java/org/geysermc/connector/utils/MathUtils.java b/connector/src/main/java/org/geysermc/connector/utils/MathUtils.java
index 272dadd84..27a3fdd3f 100644
--- a/connector/src/main/java/org/geysermc/connector/utils/MathUtils.java
+++ b/connector/src/main/java/org/geysermc/connector/utils/MathUtils.java
@@ -27,6 +27,12 @@ package org.geysermc.connector.utils;
public class MathUtils {
+ /**
+ * Round the given float to the next whole number
+ *
+ * @param floatNumber Float to round
+ * @return Rounded number
+ */
public static int ceil(float floatNumber) {
int truncated = (int) floatNumber;
return floatNumber > truncated ? truncated + 1 : truncated;
diff --git a/connector/src/main/java/org/geysermc/connector/utils/MessageUtils.java b/connector/src/main/java/org/geysermc/connector/utils/MessageUtils.java
index a28d6a7a1..ac111c71e 100644
--- a/connector/src/main/java/org/geysermc/connector/utils/MessageUtils.java
+++ b/connector/src/main/java/org/geysermc/connector/utils/MessageUtils.java
@@ -32,6 +32,10 @@ import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import com.google.gson.JsonPrimitive;
+import net.kyori.text.Component;
+import net.kyori.text.serializer.gson.GsonComponentSerializer;
+import net.kyori.text.serializer.legacy.LegacyComponentSerializer;
+import org.geysermc.connector.network.session.GeyserSession;
import java.util.*;
import java.util.regex.Matcher;
@@ -58,10 +62,10 @@ public class MessageUtils {
strings.add(" - no permission or invalid command!");
}
- List furtherParams = getTranslationParams(translation.getTranslationParams());
+ List furtherParams = getTranslationParams(translation.getTranslationParams(), locale);
if (locale != null) {
strings.add(insertParams(LocaleUtils.getLocaleString(translation.getTranslationKey(), locale), furtherParams));
- }else{
+ } else {
strings.addAll(furtherParams);
}
} else {
@@ -96,7 +100,11 @@ public class MessageUtils {
messageText = LocaleUtils.getLocaleString(messageText, locale);
}
- StringBuilder builder = new StringBuilder(messageText);
+ StringBuilder builder = new StringBuilder();
+ builder.append(getFormat(message.getStyle().getFormats()));
+ builder.append(getColorOrParent(message.getStyle()));
+ builder.append(messageText);
+
for (Message msg : message.getExtra()) {
builder.append(getFormat(msg.getStyle().getFormats()));
builder.append(getColorOrParent(msg.getStyle()));
@@ -113,9 +121,32 @@ public class MessageUtils {
}
public static String getBedrockMessage(Message message) {
- return getTranslatedBedrockMessage(message, null, false);
+ Component component;
+ if (isMessage(message.getText())) {
+ component = GsonComponentSerializer.INSTANCE.deserialize(message.getText());
+ } else {
+ component = GsonComponentSerializer.INSTANCE.deserialize(message.toJsonString());
+ }
+ return LegacyComponentSerializer.legacy().serialize(component);
}
+ public static String getBedrockMessage(String message) {
+ Component component = GsonComponentSerializer.INSTANCE.deserialize(message);
+ return LegacyComponentSerializer.legacy().serialize(component);
+ }
+
+ public static String getJavaMessage(String message) {
+ Component component = LegacyComponentSerializer.legacy().deserialize(message);
+ return GsonComponentSerializer.INSTANCE.serialize(component);
+ }
+
+ /**
+ * Inserts the given parameters into the given message both in sequence and as requested
+ *
+ * @param message Message containing possible parameter replacement strings
+ * @param params A list of parameter strings
+ * @return Parsed message with all params inserted as needed
+ */
public static String insertParams(String message, List params) {
String newMessage = message;
@@ -123,9 +154,9 @@ public class MessageUtils {
Matcher m = p.matcher(message);
while (m.find()) {
try {
- newMessage = newMessage.replaceFirst("%" + m.group(1) + "\\$s" , params.get(Integer.parseInt(m.group(1)) - 1));
+ newMessage = newMessage.replaceFirst("%" + m.group(1) + "\\$s", params.get(Integer.parseInt(m.group(1)) - 1));
} catch (Exception e) {
- // Couldnt find the param to replace
+ // Couldn't find the param to replace
}
}
@@ -136,16 +167,28 @@ public class MessageUtils {
return newMessage;
}
+ /**
+ * Gets the colour for the message style or fetches it from the parent (recursive)
+ *
+ * @param style The style to get the colour from
+ * @return Colour string to be used
+ */
private static String getColorOrParent(MessageStyle style) {
ChatColor chatColor = style.getColor();
- if (chatColor == ChatColor.NONE) {
- return getColor(style.getParent().getColor());
+ if (chatColor == ChatColor.NONE && style.getParent() != null) {
+ return getColorOrParent(style.getParent());
}
return getColor(chatColor);
}
+ /**
+ * Convert a ChatColor into a string for inserting into messages
+ *
+ * @param color ChatColor to convert
+ * @return The converted color string
+ */
private static String getColor(ChatColor color) {
String base = "\u00a7";
switch (color) {
@@ -208,6 +251,12 @@ public class MessageUtils {
return base;
}
+ /**
+ * Convert a list of ChatFormats into a string for inserting into messages
+ *
+ * @param formats ChatFormats to convert
+ * @return The converted chat formatting string
+ */
private static String getFormat(List formats) {
StringBuilder str = new StringBuilder();
for (ChatFormat cf : formats) {
@@ -238,6 +287,12 @@ public class MessageUtils {
return str.toString();
}
+ /**
+ * Checks if the given text string is a json message
+ *
+ * @param text String to test
+ * @return True if its a valid message json string, false if not
+ */
public static boolean isMessage(String text) {
JsonParser parser = new JsonParser();
try {
@@ -290,4 +345,21 @@ public class MessageUtils {
}
return "";
}
+
+ /**
+ * Checks if the given message is over 256 characters (Java edition server chat limit) and sends a message to the user if it is
+ *
+ * @param message Message to check
+ * @param session GeyserSession for the user
+ * @return True if the message is too long, false if not
+ */
+ public static boolean isTooLong(String message, GeyserSession session) {
+ if (message.length() > 256) {
+ // TODO: Add Geyser localization and translate this based on language
+ session.sendMessage("Your message is bigger than 256 characters (" + message.length() + ") so it has not been sent.");
+ return true;
+ }
+
+ return false;
+ }
}
diff --git a/connector/src/main/java/org/geysermc/connector/utils/SkinProvider.java b/connector/src/main/java/org/geysermc/connector/utils/SkinProvider.java
index c6f53ae9b..ade03c54d 100644
--- a/connector/src/main/java/org/geysermc/connector/utils/SkinProvider.java
+++ b/connector/src/main/java/org/geysermc/connector/utils/SkinProvider.java
@@ -25,9 +25,6 @@
package org.geysermc.connector.utils;
-import com.google.gson.Gson;
-import com.google.gson.GsonBuilder;
-
import lombok.AllArgsConstructor;
import lombok.Getter;
@@ -43,7 +40,6 @@ import java.util.UUID;
import java.util.concurrent.*;
public class SkinProvider {
- public static final Gson GSON = new GsonBuilder().create();
public static final boolean ALLOW_THIRD_PARTY_CAPES = GeyserConnector.getInstance().getConfig().isAllowThirdPartyCapes();
private static final ExecutorService EXECUTOR_SERVICE = Executors.newFixedThreadPool(ALLOW_THIRD_PARTY_CAPES ? 21 : 14);
diff --git a/connector/src/main/java/org/geysermc/connector/utils/SkinUtils.java b/connector/src/main/java/org/geysermc/connector/utils/SkinUtils.java
index 6e1444983..012bd73a4 100644
--- a/connector/src/main/java/org/geysermc/connector/utils/SkinUtils.java
+++ b/connector/src/main/java/org/geysermc/connector/utils/SkinUtils.java
@@ -25,8 +25,9 @@
package org.geysermc.connector.utils;
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
import com.github.steveice10.mc.auth.data.GameProfile;
-import com.google.gson.JsonObject;
import com.nukkitx.protocol.bedrock.data.ImageData;
import com.nukkitx.protocol.bedrock.data.SerializedSkin;
import com.nukkitx.protocol.bedrock.packet.PlayerListPacket;
@@ -104,22 +105,28 @@ public class SkinUtils {
private String capeUrl;
private boolean alex;
+ /**
+ * Generate the GameProfileData from the given GameProfile
+ *
+ * @param profile GameProfile to build the GameProfileData from
+ * @return The built GameProfileData
+ */
public static GameProfileData from(GameProfile profile) {
try {
GameProfile.Property skinProperty = profile.getProperty("textures");
- JsonObject skinObject = SkinProvider.GSON.fromJson(new String(Base64.getDecoder().decode(skinProperty.getValue()), StandardCharsets.UTF_8), JsonObject.class);
- JsonObject textures = skinObject.getAsJsonObject("textures");
+ JsonNode skinObject = new ObjectMapper().readTree(new String(Base64.getDecoder().decode(skinProperty.getValue()), StandardCharsets.UTF_8));
+ JsonNode textures = skinObject.get("textures");
- JsonObject skinTexture = textures.getAsJsonObject("SKIN");
- String skinUrl = skinTexture.get("url").getAsString();
+ JsonNode skinTexture = textures.get("SKIN");
+ String skinUrl = skinTexture.get("url").asText();
boolean isAlex = skinTexture.has("metadata");
String capeUrl = null;
if (textures.has("CAPE")) {
- JsonObject capeTexture = textures.getAsJsonObject("CAPE");
- capeUrl = capeTexture.get("url").getAsString();
+ JsonNode capeTexture = textures.get("CAPE");
+ capeUrl = capeTexture.get("url").asText();
}
return new GameProfileData(skinUrl, capeUrl, isAlex);
@@ -184,10 +191,15 @@ public class SkinUtils {
if (skinAndCapeConsumer != null) skinAndCapeConsumer.accept(skinAndCape);
});
-
});
}
+ /**
+ * Create a basic geometry json for the given name
+ *
+ * @param geometryName Geometry name to use
+ * @return Geometry data as a json string
+ */
private static String getLegacySkinGeometry(String geometryName) {
return "{\"geometry\" :{\"default\" :\"" + geometryName + "\"}}";
}
diff --git a/connector/src/main/java/org/geysermc/connector/utils/Toolbox.java b/connector/src/main/java/org/geysermc/connector/utils/Toolbox.java
index 7815fd087..b17d45785 100644
--- a/connector/src/main/java/org/geysermc/connector/utils/Toolbox.java
+++ b/connector/src/main/java/org/geysermc/connector/utils/Toolbox.java
@@ -32,6 +32,7 @@ import com.fasterxml.jackson.databind.ObjectMapper;
import com.nukkitx.nbt.NbtUtils;
import com.nukkitx.nbt.stream.NBTInputStream;
import com.nukkitx.nbt.tag.CompoundTag;
+import com.nukkitx.protocol.bedrock.data.ItemData;
import com.nukkitx.protocol.bedrock.packet.StartGamePacket;
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
@@ -39,6 +40,7 @@ import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
import org.geysermc.connector.GeyserConnector;
import org.geysermc.connector.network.translators.item.ItemEntry;
+import org.geysermc.connector.network.translators.item.ToolItemEntry;
import java.io.*;
import java.util.*;
@@ -47,12 +49,15 @@ public class Toolbox {
public static final ObjectMapper JSON_MAPPER = new ObjectMapper().disable(DeserializationFeature.FAIL_ON_IGNORED_PROPERTIES);
public static final CompoundTag BIOMES;
+ public static final ItemData[] CREATIVE_ITEMS;
public static final List ITEMS = new ArrayList<>();
public static final Int2ObjectMap ITEM_ENTRIES = new Int2ObjectOpenHashMap<>();
- public static final Map> LOCALE_MAPPINGS = new HashMap<>();
+ public static CompoundTag ENTITY_IDENTIFIERS;
+
+ public static int BARRIER_INDEX = 0;
static {
/* Load biomes */
@@ -102,15 +107,86 @@ public class Toolbox {
Iterator> iterator = items.fields();
while (iterator.hasNext()) {
Map.Entry entry = iterator.next();
- ITEM_ENTRIES.put(itemIndex, new ItemEntry(entry.getKey(), itemIndex,
- entry.getValue().get("bedrock_id").intValue(), entry.getValue().get("bedrock_data").intValue()));
+ if (entry.getValue().has("tool_type")) {
+ if (entry.getValue().has("tool_tier")) {
+ ITEM_ENTRIES.put(itemIndex, new ToolItemEntry(
+ entry.getKey(), itemIndex,
+ entry.getValue().get("bedrock_id").intValue(),
+ entry.getValue().get("bedrock_data").intValue(),
+ entry.getValue().get("tool_type").textValue(),
+ entry.getValue().get("tool_tier").textValue()));
+ } else {
+ ITEM_ENTRIES.put(itemIndex, new ToolItemEntry(
+ entry.getKey(), itemIndex,
+ entry.getValue().get("bedrock_id").intValue(),
+ entry.getValue().get("bedrock_data").intValue(),
+ entry.getValue().get("tool_type").textValue(),
+ ""));
+ }
+ } else {
+ ITEM_ENTRIES.put(itemIndex, new ItemEntry(
+ entry.getKey(), itemIndex,
+ entry.getValue().get("bedrock_id").intValue(),
+ entry.getValue().get("bedrock_data").intValue()));
+ }
+ if (entry.getKey().equals("minecraft:barrier")) {
+ BARRIER_INDEX = itemIndex;
+ }
+
itemIndex++;
}
// Load the locale data
LocaleUtils.init();
+
+ /* Load creative items */
+ stream = getResource("bedrock/creative_items.json");
+
+ JsonNode creativeItemEntries;
+ try {
+ creativeItemEntries = JSON_MAPPER.readTree(stream).get("items");
+ } catch (Exception e) {
+ throw new AssertionError("Unable to load creative items", e);
+ }
+
+ List creativeItems = new ArrayList<>();
+ for (JsonNode itemNode : creativeItemEntries) {
+ short damage = 0;
+ if (itemNode.has("damage")) {
+ damage = itemNode.get("damage").numberValue().shortValue();
+ }
+ if (itemNode.has("nbt_b64")) {
+ byte[] bytes = Base64.getDecoder().decode(itemNode.get("nbt_b64").asText());
+ ByteArrayInputStream bais = new ByteArrayInputStream(bytes);
+ try {
+ com.nukkitx.nbt.tag.CompoundTag tag = (com.nukkitx.nbt.tag.CompoundTag) NbtUtils.createReaderLE(bais).readTag();
+ creativeItems.add(ItemData.of(itemNode.get("id").asInt(), damage, 1, tag));
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ } else {
+ creativeItems.add(ItemData.of(itemNode.get("id").asInt(), damage, 1));
+ }
+ }
+ CREATIVE_ITEMS = creativeItems.toArray(new ItemData[0]);
+
+
+ /* Load entity identifiers */
+ stream = Toolbox.getResource("bedrock/entity_identifiers.dat");
+
+ try (NBTInputStream nbtInputStream = NbtUtils.createNetworkReader(stream)) {
+ ENTITY_IDENTIFIERS = (CompoundTag) nbtInputStream.readTag();
+ } catch (Exception e) {
+ throw new AssertionError("Unable to get entities from entity identifiers", e);
+ }
}
+ /**
+ * Get an InputStream for the given resource path, throws AssertionError if resource is not found
+ *
+ * @param resource Resource to get
+ * @return InputStream of the given resource
+ */
public static InputStream getResource(String resource) {
InputStream stream = Toolbox.class.getClassLoader().getResourceAsStream(resource);
if (stream == null) {
diff --git a/connector/src/main/java/org/geysermc/connector/utils/WebUtils.java b/connector/src/main/java/org/geysermc/connector/utils/WebUtils.java
index 065d683a0..50ab76d0b 100644
--- a/connector/src/main/java/org/geysermc/connector/utils/WebUtils.java
+++ b/connector/src/main/java/org/geysermc/connector/utils/WebUtils.java
@@ -36,6 +36,12 @@ import java.nio.file.StandardCopyOption;
public class WebUtils {
+ /**
+ * Makes a web request to the given URL and returns the body as a string
+ *
+ * @param reqURL URL to fetch
+ * @return Body contents or error message if the request fails
+ */
public static String getBody(String reqURL) {
URL url = null;
try {
@@ -61,6 +67,12 @@ public class WebUtils {
}
}
+ /**
+ * Downloads a file from the given URL and saves it to disk
+ *
+ * @param reqURL File to fetch
+ * @param fileLocation Location to save on disk
+ */
public static void downloadFile(String reqURL, String fileLocation) {
try {
InputStream in = new URL(reqURL).openStream();
diff --git a/connector/src/main/java/org/geysermc/connector/world/chunk/ChunkPosition.java b/connector/src/main/java/org/geysermc/connector/world/chunk/ChunkPosition.java
index cafb7ab04..c45a7c942 100644
--- a/connector/src/main/java/org/geysermc/connector/world/chunk/ChunkPosition.java
+++ b/connector/src/main/java/org/geysermc/connector/world/chunk/ChunkPosition.java
@@ -48,7 +48,6 @@ public class ChunkPosition {
int chunkX = x & 15;
int chunkY = y & 15;
int chunkZ = z & 15;
-
return new Position(chunkX, chunkY, chunkZ);
}
}
diff --git a/connector/src/main/resources/bedrock/creative_items.json b/connector/src/main/resources/bedrock/creative_items.json
index d55839bac..8045b219c 100644
--- a/connector/src/main/resources/bedrock/creative_items.json
+++ b/connector/src/main/resources/bedrock/creative_items.json
@@ -3008,21 +3008,6 @@
{
"id" : -198
},
- {
- "id" : 238,
- "damage" : 8
- },
- {
- "id" : 238
- },
- {
- "id" : 238,
- "damage" : 12
- },
- {
- "id" : 238,
- "damage" : 4
- },
{
"id" : 379
},
@@ -3419,363 +3404,6 @@
{
"id" : 386
},
- {
- "id" : 36
- },
- {
- "id" : -12
- },
- {
- "id" : -13
- },
- {
- "id" : -14
- },
- {
- "id" : -15
- },
- {
- "id" : -16
- },
- {
- "id" : -17
- },
- {
- "id" : -18
- },
- {
- "id" : -19
- },
- {
- "id" : -20
- },
- {
- "id" : -21
- },
- {
- "id" : -22
- },
- {
- "id" : -23
- },
- {
- "id" : -24
- },
- {
- "id" : -25
- },
- {
- "id" : -26
- },
- {
- "id" : -27
- },
- {
- "id" : -28
- },
- {
- "id" : -29
- },
- {
- "id" : -30
- },
- {
- "id" : -31
- },
- {
- "id" : -32
- },
- {
- "id" : -33
- },
- {
- "id" : -34
- },
- {
- "id" : -35
- },
- {
- "id" : -36
- },
- {
- "id" : -37
- },
- {
- "id" : -38
- },
- {
- "id" : -39
- },
- {
- "id" : -40
- },
- {
- "id" : -41
- },
- {
- "id" : -42
- },
- {
- "id" : -43
- },
- {
- "id" : -44
- },
- {
- "id" : -45
- },
- {
- "id" : -46
- },
- {
- "id" : -47
- },
- {
- "id" : -48
- },
- {
- "id" : -49
- },
- {
- "id" : -50
- },
- {
- "id" : -51
- },
- {
- "id" : -52
- },
- {
- "id" : -53
- },
- {
- "id" : -54
- },
- {
- "id" : -55
- },
- {
- "id" : -56
- },
- {
- "id" : -57
- },
- {
- "id" : -58
- },
- {
- "id" : -59
- },
- {
- "id" : -60
- },
- {
- "id" : -61
- },
- {
- "id" : -62
- },
- {
- "id" : -63
- },
- {
- "id" : -64
- },
- {
- "id" : -65
- },
- {
- "id" : -66
- },
- {
- "id" : -67
- },
- {
- "id" : -68
- },
- {
- "id" : -69
- },
- {
- "id" : -70
- },
- {
- "id" : -71
- },
- {
- "id" : -72
- },
- {
- "id" : -73
- },
- {
- "id" : -74
- },
- {
- "id" : -75
- },
- {
- "id" : -76
- },
- {
- "id" : -77
- },
- {
- "id" : -78
- },
- {
- "id" : -79
- },
- {
- "id" : -80
- },
- {
- "id" : -81
- },
- {
- "id" : -82
- },
- {
- "id" : -83
- },
- {
- "id" : -84
- },
- {
- "id" : -85
- },
- {
- "id" : -86
- },
- {
- "id" : -87
- },
- {
- "id" : -88
- },
- {
- "id" : -89
- },
- {
- "id" : -90
- },
- {
- "id" : -91
- },
- {
- "id" : -92
- },
- {
- "id" : -93
- },
- {
- "id" : -94
- },
- {
- "id" : -95
- },
- {
- "id" : -96
- },
- {
- "id" : -97
- },
- {
- "id" : -98
- },
- {
- "id" : -99
- },
- {
- "id" : -100
- },
- {
- "id" : -101
- },
- {
- "id" : -102
- },
- {
- "id" : -103
- },
- {
- "id" : -104
- },
- {
- "id" : -105
- },
- {
- "id" : -106
- },
- {
- "id" : -107
- },
- {
- "id" : -108
- },
- {
- "id" : -109
- },
- {
- "id" : -110
- },
- {
- "id" : -111
- },
- {
- "id" : -112
- },
- {
- "id" : -113
- },
- {
- "id" : -114
- },
- {
- "id" : -115
- },
- {
- "id" : -116
- },
- {
- "id" : -117
- },
- {
- "id" : -118
- },
- {
- "id" : -119
- },
- {
- "id" : -120
- },
- {
- "id" : -121
- },
- {
- "id" : -122
- },
- {
- "id" : -123
- },
- {
- "id" : -124
- },
- {
- "id" : -125
- },
- {
- "id" : -126
- },
- {
- "id" : -127
- },
- {
- "id" : -128
- },
- {
- "id" : -129
- },
{
"id" : 403,
"nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgMAbHZsAQACAgBpZAAAAAA="
diff --git a/connector/src/main/resources/bedrock/entity_identifiers.dat b/connector/src/main/resources/bedrock/entity_identifiers.dat
index c4f1524a7..cb8f0481b 100644
Binary files a/connector/src/main/resources/bedrock/entity_identifiers.dat and b/connector/src/main/resources/bedrock/entity_identifiers.dat differ