diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml
index b4b93dcac..19b655c29 100644
--- a/.github/FUNDING.yml
+++ b/.github/FUNDING.yml
@@ -1,7 +1,7 @@
# These are supported funding model platforms
github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
-patreon: GeyserMC
+patreon: #GeyserMC # Disabled currently
open_collective: # Replace with a single Open Collective username
ko_fi: # Replace with a single Ko-fi username
tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
diff --git a/.gitignore b/.gitignore
index e3c3b0a56..85f8a6e9e 100644
--- a/.gitignore
+++ b/.gitignore
@@ -242,4 +242,5 @@ logs/
public-key.pem
locales/
/cache/
-/packs/
\ No newline at end of file
+/packs/
+/dump.json
\ No newline at end of file
diff --git a/Jenkinsfile b/Jenkinsfile
index 1a93391db..7dfdaf304 100644
--- a/Jenkinsfile
+++ b/Jenkinsfile
@@ -24,6 +24,7 @@ pipeline {
when {
branch "master"
}
+
steps {
sh 'mvn javadoc:jar source:jar deploy -DskipTests'
}
diff --git a/README.md b/README.md
index f7201bb08..4f6a6d434 100644
--- a/README.md
+++ b/README.md
@@ -18,7 +18,7 @@ The ultimate goal of this project is to allow Minecraft: Bedrock Edition users t
Special thanks to the DragonProxy project for being a trailblazer in protocol translation and for all the team members who have now joined us here!
-### Currently supporting Minecraft Bedrock v1.16.x and Minecraft Java v1.16.3.
+### Currently supporting Minecraft Bedrock v1.16.100/v1.16.101/v1.16.200 and Minecraft Java v1.16.4.
## Setting Up
Take a look [here](https://github.com/GeyserMC/Geyser/wiki#Setup) for how to set up Geyser.
@@ -34,16 +34,26 @@ Take a look [here](https://github.com/GeyserMC/Geyser/wiki#Setup) for how to set
- Test Server: `test.geysermc.org` port `25565` for Java and `19132` for Bedrock
## What's Left to be Added/Fixed
-- The Following Inventories
- - [ ] Enchantment Table (as a proper GUI)
- - [ ] Beacon
- - [ ] Cartography Table
- - [ ] Stonecutter
- - [ ] Structure Block
- - [ ] Horse Inventory
- - [ ] Loom
- - [ ] Smithing Table
+- Lecterns
+- Near-perfect movement (to the point where anticheat on large servers is unlikely to ban you)
+- Resource pack conversion/CustomModelData
- Some Entity Flags
+- The Following Inventories
+ - Enchantment Table (as a proper GUI)
+ - Beacon
+ - Cartography Table
+ - Stonecutter
+ - Structure Block
+ - Horse Inventory
+ - Loom
+ - Smithing Table
+
+## What can't be fixed
+The following things can't be fixed because of Bedrock limitations. They might be fixable in the future, but not as of now.
+
+- Custom heads in inventories
+- Clickable links in chat
+- Glowing effect
## Compiling
1. Clone the repo to your computer
diff --git a/bootstrap/bungeecord/pom.xml b/bootstrap/bungeecord/pom.xml
index 44b28e931..124967b0a 100644
--- a/bootstrap/bungeecord/pom.xml
+++ b/bootstrap/bungeecord/pom.xml
@@ -6,15 +6,15 @@
org.geysermc
bootstrap-parent
- 1.1.0
- ../
+ 1.2.0-SNAPSHOT
bootstrap-bungeecord
+
org.geysermc
connector
- 1.1.0
+ 1.2.0-SNAPSHOT
compile
diff --git a/bootstrap/bungeecord/src/main/java/org/geysermc/platform/bungeecord/GeyserBungeePingPassthrough.java b/bootstrap/bungeecord/src/main/java/org/geysermc/platform/bungeecord/GeyserBungeePingPassthrough.java
index 15c8fa9e0..884bd7c35 100644
--- a/bootstrap/bungeecord/src/main/java/org/geysermc/platform/bungeecord/GeyserBungeePingPassthrough.java
+++ b/bootstrap/bungeecord/src/main/java/org/geysermc/platform/bungeecord/GeyserBungeePingPassthrough.java
@@ -47,14 +47,12 @@ import java.util.concurrent.CompletableFuture;
@AllArgsConstructor
public class GeyserBungeePingPassthrough implements IGeyserPingPassthrough, Listener {
- private static final GeyserPendingConnection PENDING_CONNECTION = new GeyserPendingConnection();
-
private final ProxyServer proxyServer;
@Override
- public GeyserPingInfo getPingInformation() {
+ public GeyserPingInfo getPingInformation(InetSocketAddress inetSocketAddress) {
CompletableFuture future = new CompletableFuture<>();
- proxyServer.getPluginManager().callEvent(new ProxyPingEvent(PENDING_CONNECTION, getPingInfo(), (event, throwable) -> {
+ proxyServer.getPluginManager().callEvent(new ProxyPingEvent(new GeyserPendingConnection(inetSocketAddress), getPingInfo(), (event, throwable) -> {
if (throwable != null) future.completeExceptionally(throwable);
else future.complete(event);
}));
@@ -89,7 +87,12 @@ public class GeyserBungeePingPassthrough implements IGeyserPingPassthrough, List
private static class GeyserPendingConnection implements PendingConnection {
private static final UUID FAKE_UUID = UUID.nameUUIDFromBytes("geyser!internal".getBytes());
- private static final InetSocketAddress FAKE_REMOTE = new InetSocketAddress(Inet4Address.getLoopbackAddress(), 69);
+
+ private final InetSocketAddress remote;
+
+ public GeyserPendingConnection(InetSocketAddress remote) {
+ this.remote = remote;
+ }
@Override
public String getName() {
@@ -143,7 +146,7 @@ public class GeyserBungeePingPassthrough implements IGeyserPingPassthrough, List
@Override
public InetSocketAddress getAddress() {
- return FAKE_REMOTE;
+ return remote;
}
@Override
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 abb9789e1..a65646bf9 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
@@ -27,10 +27,10 @@ package org.geysermc.platform.bungeecord;
import net.md_5.bungee.api.config.ListenerInfo;
import net.md_5.bungee.api.plugin.Plugin;
+import org.geysermc.common.PlatformType;
import org.geysermc.connector.GeyserConnector;
import org.geysermc.connector.bootstrap.GeyserBootstrap;
import org.geysermc.connector.command.CommandManager;
-import org.geysermc.connector.common.PlatformType;
import org.geysermc.connector.configuration.GeyserConfiguration;
import org.geysermc.connector.dump.BootstrapDumpInfo;
import org.geysermc.connector.ping.GeyserLegacyPingPassthrough;
diff --git a/bootstrap/bungeecord/src/main/java/org/geysermc/platform/bungeecord/command/BungeeCommandSender.java b/bootstrap/bungeecord/src/main/java/org/geysermc/platform/bungeecord/command/BungeeCommandSender.java
index d40dc902b..3ad8b54fd 100644
--- a/bootstrap/bungeecord/src/main/java/org/geysermc/platform/bungeecord/command/BungeeCommandSender.java
+++ b/bootstrap/bungeecord/src/main/java/org/geysermc/platform/bungeecord/command/BungeeCommandSender.java
@@ -25,17 +25,20 @@
package org.geysermc.platform.bungeecord.command;
-import lombok.AllArgsConstructor;
-
import net.md_5.bungee.api.chat.TextComponent;
import net.md_5.bungee.api.connection.ProxiedPlayer;
-
import org.geysermc.connector.command.CommandSender;
+import org.geysermc.connector.utils.LanguageUtils;
-@AllArgsConstructor
public class BungeeCommandSender implements CommandSender {
- private net.md_5.bungee.api.CommandSender handle;
+ private final net.md_5.bungee.api.CommandSender handle;
+
+ public BungeeCommandSender(net.md_5.bungee.api.CommandSender handle) {
+ this.handle = handle;
+ // Ensure even Java players' languages are loaded
+ LanguageUtils.loadGeyserLocale(getLocale());
+ }
@Override
public String getName() {
@@ -51,4 +54,14 @@ public class BungeeCommandSender implements CommandSender {
public boolean isConsole() {
return !(handle instanceof ProxiedPlayer);
}
+
+ @Override
+ public String getLocale() {
+ if (handle instanceof ProxiedPlayer) {
+ ProxiedPlayer player = (ProxiedPlayer) handle;
+ String locale = player.getLocale().getLanguage() + "_" + player.getLocale().getCountry();
+ return LanguageUtils.formatLocale(locale);
+ }
+ return LanguageUtils.getDefaultLocale();
+ }
}
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 f673a3f51..c25da0869 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
@@ -27,13 +27,10 @@ package org.geysermc.platform.bungeecord.command;
import net.md_5.bungee.api.ChatColor;
import net.md_5.bungee.api.CommandSender;
-import net.md_5.bungee.api.chat.TextComponent;
import net.md_5.bungee.api.plugin.Command;
import net.md_5.bungee.api.plugin.TabExecutor;
-
import org.geysermc.connector.GeyserConnector;
import org.geysermc.connector.command.GeyserCommand;
-import org.geysermc.connector.network.session.GeyserSession;
import org.geysermc.connector.utils.LanguageUtils;
import java.util.ArrayList;
@@ -41,7 +38,7 @@ import java.util.Arrays;
public class GeyserBungeeCommandExecutor extends Command implements TabExecutor {
- private GeyserConnector connector;
+ private final GeyserConnector connector;
public GeyserBungeeCommandExecutor(GeyserConnector connector) {
super("geyser");
@@ -54,14 +51,10 @@ public class GeyserBungeeCommandExecutor extends Command implements TabExecutor
if (args.length > 0) {
if (getCommand(args[0]) != null) {
if (!sender.hasPermission(getCommand(args[0]).getPermission())) {
- String message = "";
- if (sender instanceof GeyserSession) {
- message = LanguageUtils.getPlayerLocaleString("geyser.bootstrap.command.permission_fail", ((GeyserSession) sender).getClientData().getLanguageCode());
- } else {
- message = LanguageUtils.getLocaleStringLog("geyser.bootstrap.command.permission_fail");
- }
+ BungeeCommandSender commandSender = new BungeeCommandSender(sender);
+ String message = LanguageUtils.getPlayerLocaleString("geyser.bootstrap.command.permission_fail", commandSender.getLocale());
- sender.sendMessage(TextComponent.fromLegacyText(ChatColor.RED + message));
+ commandSender.sendMessage(ChatColor.RED + message);
return;
}
getCommand(args[0]).execute(new BungeeCommandSender(sender), args.length > 1 ? Arrays.copyOfRange(args, 1, args.length) : new String[0]);
@@ -74,7 +67,7 @@ public class GeyserBungeeCommandExecutor extends Command implements TabExecutor
@Override
public Iterable onTabComplete(CommandSender sender, String[] args) {
if (args.length == 1) {
- return Arrays.asList("?", "help", "reload", "shutdown", "stop");
+ return connector.getCommandManager().getCommandNames();
}
return new ArrayList<>();
}
diff --git a/bootstrap/pom.xml b/bootstrap/pom.xml
index d9bac67d1..a5ad53cb5 100644
--- a/bootstrap/pom.xml
+++ b/bootstrap/pom.xml
@@ -6,12 +6,11 @@
org.geysermc
geyser-parent
- parent
- ../
+ 1.2.0-SNAPSHOT
bootstrap-parent
- 1.1.0
pom
+
spigot-public
diff --git a/bootstrap/spigot/pom.xml b/bootstrap/spigot/pom.xml
index d4dc33260..adaa7557f 100644
--- a/bootstrap/spigot/pom.xml
+++ b/bootstrap/spigot/pom.xml
@@ -6,15 +6,15 @@
org.geysermc
bootstrap-parent
- 1.1.0
- ../
+ 1.2.0-SNAPSHOT
bootstrap-spigot
+
org.geysermc
connector
- 1.1.0
+ 1.2.0-SNAPSHOT
compile
@@ -26,9 +26,14 @@
us.myles
viaversion
- 3.1.1
+ 3.2.0
provided
+
+ org.geysermc.adapters
+ spigot-all
+ 1.0-SNAPSHOT
+
${outputName}-Spigot
diff --git a/bootstrap/spigot/src/main/java/org/geysermc/platform/spigot/GeyserSpigotPingPassthrough.java b/bootstrap/spigot/src/main/java/org/geysermc/platform/spigot/GeyserSpigotPingPassthrough.java
index 7196f4290..5673d85ba 100644
--- a/bootstrap/spigot/src/main/java/org/geysermc/platform/spigot/GeyserSpigotPingPassthrough.java
+++ b/bootstrap/spigot/src/main/java/org/geysermc/platform/spigot/GeyserSpigotPingPassthrough.java
@@ -35,6 +35,7 @@ import org.geysermc.connector.common.ping.GeyserPingInfo;
import org.geysermc.connector.ping.IGeyserPingPassthrough;
import java.net.InetAddress;
+import java.net.InetSocketAddress;
import java.util.Collections;
import java.util.Iterator;
@@ -44,9 +45,9 @@ public class GeyserSpigotPingPassthrough implements IGeyserPingPassthrough {
private final GeyserSpigotLogger logger;
@Override
- public GeyserPingInfo getPingInformation() {
+ public GeyserPingInfo getPingInformation(InetSocketAddress inetSocketAddress) {
try {
- ServerListPingEvent event = new GeyserPingEvent(InetAddress.getLocalHost(), Bukkit.getMotd(), Bukkit.getOnlinePlayers().size(), Bukkit.getMaxPlayers());
+ ServerListPingEvent event = new GeyserPingEvent(inetSocketAddress.getAddress(), Bukkit.getMotd(), Bukkit.getOnlinePlayers().size(), Bukkit.getMaxPlayers());
Bukkit.getPluginManager().callEvent(event);
GeyserPingInfo geyserPingInfo = new GeyserPingInfo(event.getMotd(),
new GeyserPingInfo.Players(event.getMaxPlayers(), event.getNumPlayers()),
diff --git a/bootstrap/spigot/src/main/java/org/geysermc/platform/spigot/GeyserSpigotPlugin.java b/bootstrap/spigot/src/main/java/org/geysermc/platform/spigot/GeyserSpigotPlugin.java
index 9cc0bc064..39d4f993b 100644
--- a/bootstrap/spigot/src/main/java/org/geysermc/platform/spigot/GeyserSpigotPlugin.java
+++ b/bootstrap/spigot/src/main/java/org/geysermc/platform/spigot/GeyserSpigotPlugin.java
@@ -25,12 +25,14 @@
package org.geysermc.platform.spigot;
+import com.github.steveice10.mc.protocol.MinecraftConstants;
import org.bukkit.Bukkit;
import org.bukkit.plugin.java.JavaPlugin;
+import org.geysermc.adapters.spigot.SpigotAdapters;
+import org.geysermc.common.PlatformType;
import org.geysermc.connector.GeyserConnector;
import org.geysermc.connector.bootstrap.GeyserBootstrap;
import org.geysermc.connector.command.CommandManager;
-import org.geysermc.connector.common.PlatformType;
import org.geysermc.connector.configuration.GeyserConfiguration;
import org.geysermc.connector.dump.BootstrapDumpInfo;
import org.geysermc.connector.network.translators.world.WorldManager;
@@ -40,18 +42,25 @@ import org.geysermc.connector.utils.FileUtils;
import org.geysermc.connector.utils.LanguageUtils;
import org.geysermc.platform.spigot.command.GeyserSpigotCommandExecutor;
import org.geysermc.platform.spigot.command.GeyserSpigotCommandManager;
+import org.geysermc.platform.spigot.command.SpigotCommandSender;
import org.geysermc.platform.spigot.world.GeyserSpigotBlockPlaceListener;
-import org.geysermc.platform.spigot.world.GeyserSpigotWorldManager;
+import org.geysermc.platform.spigot.world.manager.*;
+import us.myles.ViaVersion.api.Pair;
+import us.myles.ViaVersion.api.Via;
+import us.myles.ViaVersion.api.data.MappingData;
+import us.myles.ViaVersion.api.protocol.Protocol;
+import us.myles.ViaVersion.api.protocol.ProtocolRegistry;
+import us.myles.ViaVersion.api.protocol.ProtocolVersion;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
+import java.util.List;
import java.util.UUID;
import java.util.logging.Level;
public class GeyserSpigotPlugin extends JavaPlugin implements GeyserBootstrap {
-
private GeyserSpigotCommandManager geyserCommandManager;
private GeyserSpigotConfiguration geyserConfig;
private GeyserSpigotLogger geyserLogger;
@@ -120,6 +129,13 @@ public class GeyserSpigotPlugin extends JavaPlugin implements GeyserBootstrap {
this.geyserCommandManager = new GeyserSpigotCommandManager(this, connector);
boolean isViaVersion = (Bukkit.getPluginManager().getPlugin("ViaVersion") != null);
+ if (isViaVersion) {
+ if (!isCompatible(Via.getAPI().getVersion().replace("-SNAPSHOT", ""), "3.2.0")) {
+ geyserLogger.warning(LanguageUtils.getLocaleStringLog("geyser.bootstrap.viaversion.too_old",
+ "https://ci.viaversion.com/job/ViaVersion/"));
+ isViaVersion = false;
+ }
+ }
// Used to determine if Block.getBlockData() is present.
boolean isLegacy = !isCompatible(Bukkit.getServer().getVersion(), "1.13.0");
if (isLegacy)
@@ -130,8 +146,51 @@ public class GeyserSpigotPlugin extends JavaPlugin implements GeyserBootstrap {
geyserLogger.debug("Legacy version of Minecraft (1.15.2 or older) detected; not using 3D biomes.");
}
- this.geyserWorldManager = new GeyserSpigotWorldManager(isLegacy, use3dBiomes, isViaVersion);
- GeyserSpigotBlockPlaceListener blockPlaceListener = new GeyserSpigotBlockPlaceListener(connector, isLegacy, isViaVersion);
+ // Set if we need to use a different method for getting a player's locale
+ SpigotCommandSender.setUseLegacyLocaleMethod(!isCompatible(Bukkit.getServer().getVersion(), "1.12.0"));
+
+ if (connector.getConfig().isUseAdapters()) {
+ try {
+ String name = Bukkit.getServer().getClass().getPackage().getName();
+ String nmsVersion = name.substring(name.lastIndexOf('.') + 1);
+ SpigotAdapters.registerWorldAdapter(nmsVersion);
+ if (isViaVersion && isViaVersionNeeded()) {
+ if (isLegacy) {
+ // Pre-1.13
+ this.geyserWorldManager = new GeyserSpigot1_12NativeWorldManager();
+ } else {
+ // Post-1.13
+ this.geyserWorldManager = new GeyserSpigotLegacyNativeWorldManager(this, use3dBiomes);
+ }
+ } else {
+ // No ViaVersion
+ this.geyserWorldManager = new GeyserSpigotNativeWorldManager(use3dBiomes);
+ }
+ geyserLogger.debug("Using NMS adapter: " + this.geyserWorldManager.getClass() + ", " + nmsVersion);
+ } catch (Exception e) {
+ if (geyserConfig.isDebugMode()) {
+ geyserLogger.debug("Error while attempting to find NMS adapter. Most likely, this can be safely ignored. :)");
+ e.printStackTrace();
+ }
+ }
+ } else {
+ geyserLogger.debug("Not using NMS adapter as it is disabled in the config.");
+ }
+ if (this.geyserWorldManager == null) {
+ // No NMS adapter
+ if (isLegacy && isViaVersion) {
+ // Use ViaVersion for converting pre-1.13 block states
+ this.geyserWorldManager = new GeyserSpigot1_12WorldManager();
+ } else if (isLegacy) {
+ // Not sure how this happens - without ViaVersion, we don't know any block states, so just assume everything is air
+ this.geyserWorldManager = new GeyserSpigotFallbackWorldManager();
+ } else {
+ // Post-1.13
+ this.geyserWorldManager = new GeyserSpigotWorldManager(use3dBiomes);
+ }
+ geyserLogger.debug("Using default world manager: " + this.geyserWorldManager.getClass());
+ }
+ GeyserSpigotBlockPlaceListener blockPlaceListener = new GeyserSpigotBlockPlaceListener(connector, this.geyserWorldManager);
Bukkit.getServer().getPluginManager().registerEvents(blockPlaceListener, this);
@@ -140,8 +199,9 @@ public class GeyserSpigotPlugin extends JavaPlugin implements GeyserBootstrap {
@Override
public void onDisable() {
- if (connector != null)
+ if (connector != null) {
connector.shutdown();
+ }
}
@Override
@@ -174,6 +234,11 @@ public class GeyserSpigotPlugin extends JavaPlugin implements GeyserBootstrap {
return getDataFolder().toPath();
}
+ @Override
+ public BootstrapDumpInfo getDumpInfo() {
+ return new GeyserSpigotDumpInfo();
+ }
+
public boolean isCompatible(String version, String whichVersion) {
int[] currentVersion = parseVersion(version);
int[] otherVersion = parseVersion(whichVersion);
@@ -201,15 +266,43 @@ public class GeyserSpigotPlugin extends JavaPlugin implements GeyserBootstrap {
String t = stringArray[index].replaceAll("\\D", "");
try {
temp[index] = Integer.parseInt(t);
- } catch(NumberFormatException ex) {
+ } catch (NumberFormatException ex) {
temp[index] = 0;
}
}
return temp;
}
- @Override
- public BootstrapDumpInfo getDumpInfo() {
- return new GeyserSpigotDumpInfo();
+ /**
+ * @return the server version before ViaVersion finishes initializing
+ */
+ public ProtocolVersion getServerProtocolVersion() {
+ String bukkitVersion = Bukkit.getServer().getVersion();
+ // Turn "(MC: 1.16.4)" into 1.16.4.
+ String version = bukkitVersion.split("\\(MC: ")[1].split("\\)")[0];
+ return ProtocolVersion.getClosest(version);
+ }
+
+ /**
+ * This function should not run unless ViaVersion is installed on the server.
+ *
+ * @return true if there is any block mappings difference between the server and client.
+ */
+ private boolean isViaVersionNeeded() {
+ ProtocolVersion serverVersion = getServerProtocolVersion();
+ List> protocolList = ProtocolRegistry.getProtocolPath(MinecraftConstants.PROTOCOL_VERSION,
+ serverVersion.getVersion());
+ if (protocolList == null) {
+ // No translation needed!
+ return false;
+ }
+ for (int i = protocolList.size() - 1; i >= 0; i--) {
+ MappingData mappingData = protocolList.get(i).getValue().getMappingData();
+ if (mappingData != null) {
+ return true;
+ }
+ }
+ // All mapping data is null, which means client and server block states are the same
+ return false;
}
}
diff --git a/bootstrap/spigot/src/main/java/org/geysermc/platform/spigot/command/GeyserSpigotCommandExecutor.java b/bootstrap/spigot/src/main/java/org/geysermc/platform/spigot/command/GeyserSpigotCommandExecutor.java
index 2dba29015..1ff3e7fe5 100644
--- a/bootstrap/spigot/src/main/java/org/geysermc/platform/spigot/command/GeyserSpigotCommandExecutor.java
+++ b/bootstrap/spigot/src/main/java/org/geysermc/platform/spigot/command/GeyserSpigotCommandExecutor.java
@@ -32,7 +32,6 @@ import org.bukkit.command.CommandSender;
import org.bukkit.command.TabExecutor;
import org.geysermc.connector.GeyserConnector;
import org.geysermc.connector.command.GeyserCommand;
-import org.geysermc.connector.network.session.GeyserSession;
import org.geysermc.connector.utils.LanguageUtils;
import java.util.ArrayList;
@@ -42,21 +41,17 @@ import java.util.List;
@AllArgsConstructor
public class GeyserSpigotCommandExecutor implements TabExecutor {
- private GeyserConnector connector;
+ private final GeyserConnector connector;
@Override
public boolean onCommand(CommandSender sender, Command command, String label, String[] args) {
if (args.length > 0) {
if (getCommand(args[0]) != null) {
if (!sender.hasPermission(getCommand(args[0]).getPermission())) {
- String message = "";
- if (sender instanceof GeyserSession) {
- message = LanguageUtils.getPlayerLocaleString("geyser.bootstrap.command.permission_fail", ((GeyserSession) sender).getClientData().getLanguageCode());
- } else {
- message = LanguageUtils.getLocaleStringLog("geyser.bootstrap.command.permission_fail");
- }
+ SpigotCommandSender commandSender = new SpigotCommandSender(sender);
+ String message = LanguageUtils.getPlayerLocaleString("geyser.bootstrap.command.permission_fail", commandSender.getLocale());;
- sender.sendMessage(ChatColor.RED + message);
+ commandSender.sendMessage(ChatColor.RED + message);
return true;
}
getCommand(args[0]).execute(new SpigotCommandSender(sender), args.length > 1 ? Arrays.copyOfRange(args, 1, args.length) : new String[0]);
@@ -72,7 +67,7 @@ public class GeyserSpigotCommandExecutor implements TabExecutor {
@Override
public List onTabComplete(CommandSender sender, Command command, String label, String[] args) {
if (args.length == 1) {
- return Arrays.asList("?", "help", "reload", "shutdown", "stop");
+ return connector.getCommandManager().getCommandNames();
}
return new ArrayList<>();
}
diff --git a/bootstrap/spigot/src/main/java/org/geysermc/platform/spigot/command/SpigotCommandSender.java b/bootstrap/spigot/src/main/java/org/geysermc/platform/spigot/command/SpigotCommandSender.java
index 55475a303..93a500669 100644
--- a/bootstrap/spigot/src/main/java/org/geysermc/platform/spigot/command/SpigotCommandSender.java
+++ b/bootstrap/spigot/src/main/java/org/geysermc/platform/spigot/command/SpigotCommandSender.java
@@ -25,15 +25,33 @@
package org.geysermc.platform.spigot.command;
-import lombok.AllArgsConstructor;
-
import org.bukkit.command.ConsoleCommandSender;
+import org.bukkit.entity.Player;
+import org.geysermc.connector.GeyserConnector;
import org.geysermc.connector.command.CommandSender;
+import org.geysermc.connector.utils.LanguageUtils;
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
-@AllArgsConstructor
public class SpigotCommandSender implements CommandSender {
- private org.bukkit.command.CommandSender handle;
+ /**
+ * Whether to use {@code Player.getLocale()} or {@code Player.spigot().getLocale()}, depending on version.
+ * 1.12 or greater should not use the legacy method.
+ */
+ private static boolean USE_LEGACY_METHOD = false;
+ private static Method LOCALE_METHOD;
+
+ private final org.bukkit.command.CommandSender handle;
+ private final String locale;
+
+ public SpigotCommandSender(org.bukkit.command.CommandSender handle) {
+ this.handle = handle;
+ this.locale = getSpigotLocale();
+ // Ensure even Java players' languages are loaded
+ LanguageUtils.loadGeyserLocale(locale);
+ }
@Override
public String getName() {
@@ -49,4 +67,49 @@ public class SpigotCommandSender implements CommandSender {
public boolean isConsole() {
return handle instanceof ConsoleCommandSender;
}
+
+ @Override
+ public String getLocale() {
+ return locale;
+ }
+
+ /**
+ * Set if we are on pre-1.12, and therefore {@code player.getLocale()} doesn't exist and we have to get
+ * {@code player.spigot().getLocale()}.
+ *
+ * @param useLegacyMethod if we are running pre-1.12 and therefore need to use reflection to get the player locale
+ */
+ public static void setUseLegacyLocaleMethod(boolean useLegacyMethod) {
+ USE_LEGACY_METHOD = useLegacyMethod;
+ if (USE_LEGACY_METHOD) {
+ try {
+ //noinspection JavaReflectionMemberAccess - of course it doesn't exist; that's why we're doing it
+ LOCALE_METHOD = Player.Spigot.class.getMethod("getLocale");
+ } catch (NoSuchMethodException e) {
+ GeyserConnector.getInstance().getLogger().debug("Player.Spigot.getLocale() doesn't exist? Not a big deal but if you're seeing this please report it to the developers!");
+ }
+ }
+ }
+
+ /**
+ * So we only have to do nasty reflection stuff once per command
+ *
+ * @return the locale of the Spigot player
+ */
+ private String getSpigotLocale() {
+ if (handle instanceof Player) {
+ Player player = (Player) handle;
+ if (USE_LEGACY_METHOD) {
+ try {
+ // sigh
+ // This was the only option on older Spigot instances and now it's gone
+ return (String) LOCALE_METHOD.invoke(player.spigot());
+ } catch (IllegalAccessException | InvocationTargetException ignored) {
+ }
+ } else {
+ return player.getLocale();
+ }
+ }
+ return LanguageUtils.getDefaultLocale();
+ }
}
diff --git a/bootstrap/spigot/src/main/java/org/geysermc/platform/spigot/world/GeyserSpigotBlockPlaceListener.java b/bootstrap/spigot/src/main/java/org/geysermc/platform/spigot/world/GeyserSpigotBlockPlaceListener.java
index cb59e202b..55a368be4 100644
--- a/bootstrap/spigot/src/main/java/org/geysermc/platform/spigot/world/GeyserSpigotBlockPlaceListener.java
+++ b/bootstrap/spigot/src/main/java/org/geysermc/platform/spigot/world/GeyserSpigotBlockPlaceListener.java
@@ -36,13 +36,13 @@ import org.bukkit.event.block.BlockPlaceEvent;
import org.geysermc.connector.GeyserConnector;
import org.geysermc.connector.network.session.GeyserSession;
import org.geysermc.connector.network.translators.world.block.BlockTranslator;
+import org.geysermc.platform.spigot.world.manager.GeyserSpigotWorldManager;
@AllArgsConstructor
public class GeyserSpigotBlockPlaceListener implements Listener {
private final GeyserConnector connector;
- private final boolean isLegacy;
- private final boolean isViaVersion;
+ private final GeyserSpigotWorldManager worldManager;
@EventHandler
public void place(final BlockPlaceEvent event) {
@@ -52,14 +52,13 @@ public class GeyserSpigotBlockPlaceListener implements Listener {
placeBlockSoundPacket.setSound(SoundEvent.PLACE);
placeBlockSoundPacket.setPosition(Vector3f.from(event.getBlockPlaced().getX(), event.getBlockPlaced().getY(), event.getBlockPlaced().getZ()));
placeBlockSoundPacket.setBabySound(false);
- String javaBlockId;
- if (isLegacy) {
- javaBlockId = BlockTranslator.getJavaIdBlockMap().inverse().get(GeyserSpigotWorldManager.getLegacyBlock(session,
- event.getBlockPlaced().getX(), event.getBlockPlaced().getY(), event.getBlockPlaced().getZ(), isViaVersion));
+ if (worldManager.isLegacy()) {
+ placeBlockSoundPacket.setExtraData(BlockTranslator.getBedrockBlockId(worldManager.getBlockAt(session,
+ event.getBlockPlaced().getX(), event.getBlockPlaced().getY(), event.getBlockPlaced().getZ())));
} else {
- javaBlockId = event.getBlockPlaced().getBlockData().getAsString();
+ String javaBlockId = event.getBlockPlaced().getBlockData().getAsString();
+ placeBlockSoundPacket.setExtraData(BlockTranslator.getBedrockBlockId(BlockTranslator.getJavaIdBlockMap().getOrDefault(javaBlockId, BlockTranslator.JAVA_AIR_ID)));
}
- placeBlockSoundPacket.setExtraData(BlockTranslator.getBedrockBlockId(BlockTranslator.getJavaIdBlockMap().getOrDefault(javaBlockId, 0)));
placeBlockSoundPacket.setIdentifier(":");
session.sendUpstreamPacket(placeBlockSoundPacket);
session.setLastBlockPlacePosition(null);
diff --git a/bootstrap/spigot/src/main/java/org/geysermc/platform/spigot/world/manager/GeyserSpigot1_12NativeWorldManager.java b/bootstrap/spigot/src/main/java/org/geysermc/platform/spigot/world/manager/GeyserSpigot1_12NativeWorldManager.java
new file mode 100644
index 000000000..f58b75cdd
--- /dev/null
+++ b/bootstrap/spigot/src/main/java/org/geysermc/platform/spigot/world/manager/GeyserSpigot1_12NativeWorldManager.java
@@ -0,0 +1,59 @@
+/*
+ * 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.spigot.world.manager;
+
+import org.bukkit.Bukkit;
+import org.bukkit.entity.Player;
+import org.geysermc.adapters.spigot.SpigotAdapters;
+import org.geysermc.adapters.spigot.SpigotWorldAdapter;
+import org.geysermc.connector.network.session.GeyserSession;
+import org.geysermc.connector.network.translators.world.block.BlockTranslator;
+import us.myles.ViaVersion.api.Via;
+import us.myles.ViaVersion.protocols.protocol1_13to1_12_2.storage.BlockStorage;
+
+/**
+ * Used with ViaVersion and pre-1.13.
+ */
+public class GeyserSpigot1_12NativeWorldManager extends GeyserSpigot1_12WorldManager {
+ private final SpigotWorldAdapter adapter;
+
+ public GeyserSpigot1_12NativeWorldManager() {
+ this.adapter = SpigotAdapters.getWorldAdapter();
+ // Unlike post-1.13, we can't build up a cache of block states, because block entities need some special conversion
+ }
+
+ @Override
+ public int getBlockAt(GeyserSession session, int x, int y, int z) {
+ Player player = Bukkit.getPlayer(session.getPlayerEntity().getUsername());
+ if (player == null) {
+ return BlockTranslator.JAVA_AIR_ID;
+ }
+ // Get block entity storage
+ BlockStorage storage = Via.getManager().getConnection(player.getUniqueId()).get(BlockStorage.class);
+ int blockId = adapter.getBlockAt(player.getWorld(), x, y, z);
+ return getLegacyBlock(storage, blockId, x, y, z);
+ }
+}
diff --git a/bootstrap/spigot/src/main/java/org/geysermc/platform/spigot/world/manager/GeyserSpigot1_12WorldManager.java b/bootstrap/spigot/src/main/java/org/geysermc/platform/spigot/world/manager/GeyserSpigot1_12WorldManager.java
new file mode 100644
index 000000000..b00ddafaa
--- /dev/null
+++ b/bootstrap/spigot/src/main/java/org/geysermc/platform/spigot/world/manager/GeyserSpigot1_12WorldManager.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.platform.spigot.world.manager;
+
+import com.github.steveice10.mc.protocol.data.game.chunk.Chunk;
+import org.bukkit.Bukkit;
+import org.bukkit.World;
+import org.bukkit.block.Block;
+import org.bukkit.entity.Player;
+import org.geysermc.connector.network.session.GeyserSession;
+import org.geysermc.connector.network.translators.world.block.BlockTranslator;
+import us.myles.ViaVersion.api.Pair;
+import us.myles.ViaVersion.api.Via;
+import us.myles.ViaVersion.api.data.MappingData;
+import us.myles.ViaVersion.api.minecraft.Position;
+import us.myles.ViaVersion.api.protocol.Protocol;
+import us.myles.ViaVersion.api.protocol.ProtocolRegistry;
+import us.myles.ViaVersion.api.protocol.ProtocolVersion;
+import us.myles.ViaVersion.protocols.protocol1_13to1_12_2.Protocol1_13To1_12_2;
+import us.myles.ViaVersion.protocols.protocol1_13to1_12_2.storage.BlockStorage;
+
+import java.util.List;
+
+/**
+ * Should be used when ViaVersion is present, no NMS adapter is being used, and we are pre-1.13.
+ *
+ * You need ViaVersion to connect to an older server with the Geyser-Spigot plugin.
+ */
+public class GeyserSpigot1_12WorldManager extends GeyserSpigotWorldManager {
+ /**
+ * Specific mapping data for 1.12 to 1.13. Used to convert the 1.12 block into the 1.13 block state.
+ * (Block IDs did not change between server versions until 1.13 and after)
+ */
+ private final MappingData mappingData1_12to1_13;
+
+ /**
+ * The list of all protocols from the client's version to 1.13.
+ */
+ private final List> protocolList;
+
+ public GeyserSpigot1_12WorldManager() {
+ super(false);
+ this.mappingData1_12to1_13 = ProtocolRegistry.getProtocol(Protocol1_13To1_12_2.class).getMappingData();
+ this.protocolList = ProtocolRegistry.getProtocolPath(CLIENT_PROTOCOL_VERSION,
+ ProtocolVersion.v1_13.getVersion());
+ }
+
+ @Override
+ @SuppressWarnings("deprecation")
+ public int getBlockAt(GeyserSession session, int x, int y, int z) {
+ Player player = Bukkit.getPlayer(session.getPlayerEntity().getUsername());
+ if (player == null) {
+ return BlockTranslator.JAVA_AIR_ID;
+ }
+ // Get block entity storage
+ BlockStorage storage = Via.getManager().getConnection(player.getUniqueId()).get(BlockStorage.class);
+ Block block = player.getWorld().getBlockAt(x, y, z);
+ // Black magic that gets the old block state ID
+ int blockId = (block.getType().getId() << 4) | (block.getData() & 0xF);
+ return getLegacyBlock(storage, blockId, x, y, z);
+ }
+
+ /**
+ *
+ * @param storage ViaVersion's block entity storage (used to fix block entity state differences)
+ * @param blockId the pre-1.13 block id
+ * @param x X coordinate of block
+ * @param y Y coordinate of block
+ * @param z Z coordinate of block
+ * @return the block state updated to the latest Minecraft version
+ */
+ @SuppressWarnings("deprecation")
+ public int getLegacyBlock(BlockStorage storage, int blockId, int x, int y, int z) {
+ // Convert block state from old version (1.12.2) -> 1.13 -> 1.13.1 -> 1.14 -> 1.15 -> 1.16 -> 1.16.2
+ blockId = mappingData1_12to1_13.getNewBlockId(blockId);
+ // Translate block entity differences - some information was stored in block tags and not block states
+ if (storage.isWelcome(blockId)) { // No getOrDefault method
+ BlockStorage.ReplacementData data = storage.get(new Position(x, (short) y, z));
+ if (data != null && data.getReplacement() != -1) {
+ blockId = data.getReplacement();
+ }
+ }
+ for (int i = protocolList.size() - 1; i >= 0; i--) {
+ MappingData mappingData = protocolList.get(i).getValue().getMappingData();
+ if (mappingData != null) {
+ blockId = mappingData.getNewBlockStateId(blockId);
+ }
+ }
+ return blockId;
+ }
+
+ @SuppressWarnings("deprecation")
+ @Override
+ public void getBlocksInSection(GeyserSession session, int x, int y, int z, Chunk chunk) {
+ Player player = Bukkit.getPlayer(session.getPlayerEntity().getUsername());
+ if (player == null) {
+ return;
+ }
+ World world = player.getWorld();
+ // Get block entity storage
+ BlockStorage storage = Via.getManager().getConnection(player.getUniqueId()).get(BlockStorage.class);
+ for (int blockY = 0; blockY < 16; blockY++) { // Cache-friendly iteration order
+ for (int blockZ = 0; blockZ < 16; blockZ++) {
+ for (int blockX = 0; blockX < 16; blockX++) {
+ Block block = world.getBlockAt(x, y, z);
+ // Black magic that gets the old block state ID
+ int blockId = (block.getType().getId() << 4) | (block.getData() & 0xF);
+ chunk.set(blockX, blockY, blockZ, getLegacyBlock(storage, blockId, (x << 4) + blockX, (y << 4) + blockY, (z << 4) + blockZ));
+ }
+ }
+ }
+ }
+
+ @Override
+ public boolean isLegacy() {
+ return true;
+ }
+}
diff --git a/bootstrap/spigot/src/main/java/org/geysermc/platform/spigot/world/manager/GeyserSpigotFallbackWorldManager.java b/bootstrap/spigot/src/main/java/org/geysermc/platform/spigot/world/manager/GeyserSpigotFallbackWorldManager.java
new file mode 100644
index 000000000..49c675a1d
--- /dev/null
+++ b/bootstrap/spigot/src/main/java/org/geysermc/platform/spigot/world/manager/GeyserSpigotFallbackWorldManager.java
@@ -0,0 +1,62 @@
+/*
+ * 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.spigot.world.manager;
+
+import com.github.steveice10.mc.protocol.data.game.chunk.Chunk;
+import org.geysermc.connector.network.session.GeyserSession;
+import org.geysermc.connector.network.translators.world.block.BlockTranslator;
+
+/**
+ * Should only be used when we know {@link GeyserSpigotWorldManager#getBlockAt(GeyserSession, int, int, int)}
+ * cannot be accurate. Typically, this is when ViaVersion is not installed but a client still manages to connect.
+ * If this occurs to you somehow, please let us know!!
+ */
+public class GeyserSpigotFallbackWorldManager extends GeyserSpigotWorldManager {
+ public GeyserSpigotFallbackWorldManager() {
+ // Since this is pre-1.13 (and thus pre-1.15), there will never be 3D biomes.
+ super(false);
+ }
+
+ @Override
+ public int getBlockAt(GeyserSession session, int x, int y, int z) {
+ return BlockTranslator.JAVA_AIR_ID;
+ }
+
+ @Override
+ public void getBlocksInSection(GeyserSession session, int x, int y, int z, Chunk chunk) {
+ // Do nothing, since we can't do anything with the chunk
+ }
+
+ @Override
+ public boolean hasMoreBlockDataThanChunkCache() {
+ return false;
+ }
+
+ @Override
+ public boolean isLegacy() {
+ return true;
+ }
+}
diff --git a/bootstrap/spigot/src/main/java/org/geysermc/platform/spigot/world/manager/GeyserSpigotLegacyNativeWorldManager.java b/bootstrap/spigot/src/main/java/org/geysermc/platform/spigot/world/manager/GeyserSpigotLegacyNativeWorldManager.java
new file mode 100644
index 000000000..dec9b4141
--- /dev/null
+++ b/bootstrap/spigot/src/main/java/org/geysermc/platform/spigot/world/manager/GeyserSpigotLegacyNativeWorldManager.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.platform.spigot.world.manager;
+
+import com.github.steveice10.mc.protocol.MinecraftConstants;
+import it.unimi.dsi.fastutil.ints.Int2IntMap;
+import it.unimi.dsi.fastutil.ints.Int2IntOpenHashMap;
+import it.unimi.dsi.fastutil.ints.IntList;
+import org.geysermc.connector.network.session.GeyserSession;
+import org.geysermc.platform.spigot.GeyserSpigotPlugin;
+import us.myles.ViaVersion.api.Pair;
+import us.myles.ViaVersion.api.data.MappingData;
+import us.myles.ViaVersion.api.protocol.Protocol;
+import us.myles.ViaVersion.api.protocol.ProtocolRegistry;
+import us.myles.ViaVersion.api.protocol.ProtocolVersion;
+
+import java.util.List;
+
+/**
+ * Used when block IDs need to be translated to the latest version
+ */
+public class GeyserSpigotLegacyNativeWorldManager extends GeyserSpigotNativeWorldManager {
+
+ private final Int2IntMap oldToNewBlockId;
+
+ public GeyserSpigotLegacyNativeWorldManager(GeyserSpigotPlugin plugin, boolean use3dBiomes) {
+ super(use3dBiomes);
+ IntList allBlockStates = adapter.getAllBlockStates();
+ oldToNewBlockId = new Int2IntOpenHashMap(allBlockStates.size());
+ ProtocolVersion serverVersion = plugin.getServerProtocolVersion();
+ List> protocolList = ProtocolRegistry.getProtocolPath(MinecraftConstants.PROTOCOL_VERSION,
+ serverVersion.getVersion());
+ for (int oldBlockId : allBlockStates) {
+ int newBlockId = oldBlockId;
+ // protocolList should *not* be null; we checked for that before initializing this class
+ for (int i = protocolList.size() - 1; i >= 0; i--) {
+ MappingData mappingData = protocolList.get(i).getValue().getMappingData();
+ if (mappingData != null) {
+ newBlockId = mappingData.getNewBlockStateId(newBlockId);
+ }
+ }
+ oldToNewBlockId.put(oldBlockId, newBlockId);
+ }
+ }
+
+ @Override
+ public int getBlockAt(GeyserSession session, int x, int y, int z) {
+ int nativeBlockId = super.getBlockAt(session, x, y, z);
+ return oldToNewBlockId.getOrDefault(nativeBlockId, nativeBlockId);
+ }
+
+ @Override
+ public boolean isLegacy() {
+ return true;
+ }
+}
diff --git a/bootstrap/spigot/src/main/java/org/geysermc/platform/spigot/world/manager/GeyserSpigotNativeWorldManager.java b/bootstrap/spigot/src/main/java/org/geysermc/platform/spigot/world/manager/GeyserSpigotNativeWorldManager.java
new file mode 100644
index 000000000..f703ecdb5
--- /dev/null
+++ b/bootstrap/spigot/src/main/java/org/geysermc/platform/spigot/world/manager/GeyserSpigotNativeWorldManager.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.platform.spigot.world.manager;
+
+import org.bukkit.Bukkit;
+import org.bukkit.entity.Player;
+import org.geysermc.adapters.spigot.SpigotAdapters;
+import org.geysermc.adapters.spigot.SpigotWorldAdapter;
+import org.geysermc.connector.network.session.GeyserSession;
+import org.geysermc.connector.network.translators.world.block.BlockTranslator;
+
+public class GeyserSpigotNativeWorldManager extends GeyserSpigotWorldManager {
+ protected final SpigotWorldAdapter adapter;
+
+ public GeyserSpigotNativeWorldManager(boolean use3dBiomes) {
+ super(use3dBiomes);
+ adapter = SpigotAdapters.getWorldAdapter();
+ }
+
+ @Override
+ public int getBlockAt(GeyserSession session, int x, int y, int z) {
+ Player player = Bukkit.getPlayer(session.getPlayerEntity().getUsername());
+ if (player == null) {
+ return BlockTranslator.JAVA_AIR_ID;
+ }
+ return adapter.getBlockAt(player.getWorld(), x, y, z);
+ }
+}
diff --git a/bootstrap/spigot/src/main/java/org/geysermc/platform/spigot/world/GeyserSpigotWorldManager.java b/bootstrap/spigot/src/main/java/org/geysermc/platform/spigot/world/manager/GeyserSpigotWorldManager.java
similarity index 61%
rename from bootstrap/spigot/src/main/java/org/geysermc/platform/spigot/world/GeyserSpigotWorldManager.java
rename to bootstrap/spigot/src/main/java/org/geysermc/platform/spigot/world/manager/GeyserSpigotWorldManager.java
index 8a92526f1..cd1774baf 100644
--- a/bootstrap/spigot/src/main/java/org/geysermc/platform/spigot/world/GeyserSpigotWorldManager.java
+++ b/bootstrap/spigot/src/main/java/org/geysermc/platform/spigot/world/manager/GeyserSpigotWorldManager.java
@@ -23,9 +23,10 @@
* @link https://github.com/GeyserMC/Geyser
*/
-package org.geysermc.platform.spigot.world;
+package org.geysermc.platform.spigot.world.manager;
import com.fasterxml.jackson.databind.JsonNode;
+import com.github.steveice10.mc.protocol.MinecraftConstants;
import com.github.steveice10.mc.protocol.data.game.chunk.Chunk;
import it.unimi.dsi.fastutil.ints.Int2IntMap;
import it.unimi.dsi.fastutil.ints.Int2IntOpenHashMap;
@@ -33,6 +34,7 @@ import org.bukkit.Bukkit;
import org.bukkit.World;
import org.bukkit.block.Biome;
import org.bukkit.block.Block;
+import org.bukkit.block.data.BlockData;
import org.bukkit.entity.Player;
import org.geysermc.connector.GeyserConnector;
import org.geysermc.connector.network.session.GeyserSession;
@@ -41,20 +43,22 @@ import org.geysermc.connector.network.translators.world.block.BlockTranslator;
import org.geysermc.connector.utils.FileUtils;
import org.geysermc.connector.utils.GameRule;
import org.geysermc.connector.utils.LanguageUtils;
-import us.myles.ViaVersion.protocols.protocol1_13_1to1_13.Protocol1_13_1To1_13;
-import us.myles.ViaVersion.protocols.protocol1_16_2to1_16_1.data.MappingData;
import java.io.InputStream;
+/**
+ * The base world manager to use when there is no supported NMS revision
+ */
public class GeyserSpigotWorldManager extends GeyserWorldManager {
-
- private final boolean isLegacy;
- private final boolean use3dBiomes;
/**
- * You need ViaVersion to connect to an older server with Geyser.
- * However, we still check for ViaVersion in case there's some other way that gets Geyser on a pre-1.13 Bukkit server
+ * The current client protocol version for ViaVersion usage.
*/
- private final boolean isViaVersion;
+ protected static final int CLIENT_PROTOCOL_VERSION = MinecraftConstants.PROTOCOL_VERSION;
+
+ /**
+ * Whether the server is pre-1.16 and therefore does not support 3D biomes on an API level guaranteed.
+ */
+ private final boolean use3dBiomes;
/**
* Stores a list of {@link Biome} ordinal numbers to Minecraft biome numeric IDs.
*
@@ -68,10 +72,8 @@ public class GeyserSpigotWorldManager extends GeyserWorldManager {
*/
private final Int2IntMap biomeToIdMap = new Int2IntOpenHashMap(Biome.values().length);
- public GeyserSpigotWorldManager(boolean isLegacy, boolean use3dBiomes, boolean isViaVersion) {
- this.isLegacy = isLegacy;
+ public GeyserSpigotWorldManager(boolean use3dBiomes) {
this.use3dBiomes = use3dBiomes;
- this.isViaVersion = isViaVersion;
// Load the values into the biome-to-ID map
InputStream biomeStream = FileUtils.getResource("biomes.json");
@@ -83,8 +85,9 @@ public class GeyserSpigotWorldManager extends GeyserWorldManager {
}
// Only load in the biomes that are present in this version of Minecraft
for (Biome enumBiome : Biome.values()) {
- if (biomes.has(enumBiome.toString())) {
- biomeToIdMap.put(enumBiome.ordinal(), biomes.get(enumBiome.toString()).intValue());
+ JsonNode biome = biomes.get(enumBiome.toString());
+ if (biome != null) {
+ biomeToIdMap.put(enumBiome.ordinal(), biome.intValue());
} else {
GeyserConnector.getInstance().getLogger().debug("No biome mapping found for " + enumBiome.toString() +
", defaulting to 0");
@@ -96,71 +99,26 @@ public class GeyserSpigotWorldManager extends GeyserWorldManager {
@Override
public int getBlockAt(GeyserSession session, int x, int y, int z) {
Player bukkitPlayer;
- if ((this.isLegacy && !this.isViaVersion)
- || session.getPlayerEntity() == null
- || (bukkitPlayer = Bukkit.getPlayer(session.getPlayerEntity().getUsername())) == null) {
- return BlockTranslator.AIR;
+ if ((bukkitPlayer = Bukkit.getPlayer(session.getPlayerEntity().getUsername())) == null) {
+ return BlockTranslator.JAVA_AIR_ID;
}
World world = bukkitPlayer.getWorld();
- if (isLegacy) {
- return getLegacyBlock(session, x, y, z, true);
- }
- //TODO possibly: detect server version for all versions and use ViaVersion for block state mappings like below
- return BlockTranslator.getJavaIdBlockMap().getOrDefault(world.getBlockAt(x, y, z).getBlockData().getAsString(), 0);
- }
-
- public static int getLegacyBlock(GeyserSession session, int x, int y, int z, boolean isViaVersion) {
- if (isViaVersion) {
- return getLegacyBlock(Bukkit.getPlayer(session.getPlayerEntity().getUsername()).getWorld(), x, y, z, true);
- } else {
- return BlockTranslator.AIR;
- }
- }
-
- @SuppressWarnings("deprecation")
- public static int getLegacyBlock(World world, int x, int y, int z, boolean isViaVersion) {
- if (isViaVersion) {
- Block block = world.getBlockAt(x, y, z);
- // Black magic that gets the old block state ID
- int oldBlockId = (block.getType().getId() << 4) | (block.getData() & 0xF);
- // Convert block state from old version -> 1.13 -> 1.13.1 -> 1.14 -> 1.15 -> 1.16 -> 1.16.2
- int thirteenBlockId = us.myles.ViaVersion.protocols.protocol1_13to1_12_2.data.MappingData.blockMappings.getNewId(oldBlockId);
- int thirteenPointOneBlockId = Protocol1_13_1To1_13.getNewBlockStateId(thirteenBlockId);
- int fourteenBlockId = us.myles.ViaVersion.protocols.protocol1_14to1_13_2.data.MappingData.blockStateMappings.getNewId(thirteenPointOneBlockId);
- int fifteenBlockId = us.myles.ViaVersion.protocols.protocol1_15to1_14_4.data.MappingData.blockStateMappings.getNewId(fourteenBlockId);
- int sixteenBlockId = us.myles.ViaVersion.protocols.protocol1_16to1_15_2.data.MappingData.blockStateMappings.getNewId(fifteenBlockId);
- return MappingData.blockStateMappings.getNewId(sixteenBlockId);
- } else {
- return BlockTranslator.AIR;
- }
+ return BlockTranslator.getJavaIdBlockMap().getOrDefault(world.getBlockAt(x, y, z).getBlockData().getAsString(), BlockTranslator.JAVA_AIR_ID);
}
@Override
public void getBlocksInSection(GeyserSession session, int x, int y, int z, Chunk chunk) {
Player bukkitPlayer;
- if ((this.isLegacy && !this.isViaVersion)
- || session.getPlayerEntity() == null
- || (bukkitPlayer = Bukkit.getPlayer(session.getPlayerEntity().getUsername())) == null) {
+ if ((bukkitPlayer = Bukkit.getPlayer(session.getPlayerEntity().getUsername())) == null) {
return;
}
World world = bukkitPlayer.getWorld();
- if (this.isLegacy) {
- for (int blockY = 0; blockY < 16; blockY++) { // Cache-friendly iteration order
- for (int blockZ = 0; blockZ < 16; blockZ++) {
- for (int blockX = 0; blockX < 16; blockX++) {
- chunk.set(blockX, blockY, blockZ, getLegacyBlock(world, (x << 4) + blockX, (y << 4) + blockY, (z << 4) + blockZ, true));
- }
- }
- }
- } else {
- //TODO: see above TODO in getBlockAt
- for (int blockY = 0; blockY < 16; blockY++) { // Cache-friendly iteration order
- for (int blockZ = 0; blockZ < 16; blockZ++) {
- for (int blockX = 0; blockX < 16; blockX++) {
- Block block = world.getBlockAt((x << 4) + blockX, (y << 4) + blockY, (z << 4) + blockZ);
- int id = BlockTranslator.getJavaIdBlockMap().getOrDefault(block.getBlockData().getAsString(), 0);
- chunk.set(blockX, blockY, blockZ, id);
- }
+ for (int blockY = 0; blockY < 16; blockY++) { // Cache-friendly iteration order
+ for (int blockZ = 0; blockZ < 16; blockZ++) {
+ for (int blockX = 0; blockX < 16; blockX++) {
+ Block block = world.getBlockAt((x << 4) + blockX, (y << 4) + blockY, (z << 4) + blockZ);
+ int id = BlockTranslator.getJavaIdBlockMap().getOrDefault(block.getBlockData().getAsString(), BlockTranslator.JAVA_AIR_ID);
+ chunk.set(blockX, blockY, blockZ, id);
}
}
}
@@ -222,4 +180,16 @@ public class GeyserSpigotWorldManager extends GeyserWorldManager {
public boolean hasPermission(GeyserSession session, String permission) {
return Bukkit.getPlayer(session.getPlayerEntity().getUsername()).hasPermission(permission);
}
+
+ /**
+ * This must be set to true if we are pre-1.13, and {@link BlockData#getAsString() does not exist}.
+ *
+ * This should be set to true if we are post-1.13 but before the latest version, and we should convert the old block state id
+ * to the current one.
+ *
+ * @return whether there is a difference between client block state and server block state that requires extra processing
+ */
+ public boolean isLegacy() {
+ return false;
+ }
}
diff --git a/bootstrap/sponge/pom.xml b/bootstrap/sponge/pom.xml
index 132f38173..e6ce8f851 100644
--- a/bootstrap/sponge/pom.xml
+++ b/bootstrap/sponge/pom.xml
@@ -6,15 +6,15 @@
org.geysermc
bootstrap-parent
- 1.1.0
- ../
+ 1.2.0-SNAPSHOT
bootstrap-sponge
+
org.geysermc
connector
- 1.1.0
+ 1.2.0-SNAPSHOT
compile
diff --git a/bootstrap/sponge/src/main/java/org/geysermc/platform/sponge/GeyserSpongePingPassthrough.java b/bootstrap/sponge/src/main/java/org/geysermc/platform/sponge/GeyserSpongePingPassthrough.java
index 251b9703d..c5b6b8314 100644
--- a/bootstrap/sponge/src/main/java/org/geysermc/platform/sponge/GeyserSpongePingPassthrough.java
+++ b/bootstrap/sponge/src/main/java/org/geysermc/platform/sponge/GeyserSpongePingPassthrough.java
@@ -38,31 +38,29 @@ import org.spongepowered.api.network.status.StatusClient;
import org.spongepowered.api.profile.GameProfile;
import java.lang.reflect.Method;
-import java.net.Inet4Address;
import java.net.InetSocketAddress;
import java.util.Optional;
public class GeyserSpongePingPassthrough implements IGeyserPingPassthrough {
- private static final GeyserStatusClient STATUS_CLIENT = new GeyserStatusClient();
private static final Cause CAUSE = Cause.of(EventContext.empty(), Sponge.getServer());
private static Method SpongeStatusResponse_create;
@SuppressWarnings({"unchecked", "rawtypes"})
@Override
- public GeyserPingInfo getPingInformation() {
+ public GeyserPingInfo getPingInformation(InetSocketAddress inetSocketAddress) {
// come on Sponge, this is in commons, why not expose it :(
ClientPingServerEvent event;
try {
- if(SpongeStatusResponse_create == null) {
+ if (SpongeStatusResponse_create == null) {
Class SpongeStatusResponse = Class.forName("org.spongepowered.common.network.status.SpongeStatusResponse");
Class MinecraftServer = Class.forName("net.minecraft.server.MinecraftServer");
SpongeStatusResponse_create = SpongeStatusResponse.getDeclaredMethod("create", MinecraftServer);
}
Object response = SpongeStatusResponse_create.invoke(null, Sponge.getServer());
- event = SpongeEventFactory.createClientPingServerEvent(CAUSE, STATUS_CLIENT, (ClientPingServerEvent.Response) response);
+ event = SpongeEventFactory.createClientPingServerEvent(CAUSE, new GeyserStatusClient(inetSocketAddress), (ClientPingServerEvent.Response) response);
} catch (ReflectiveOperationException e) {
throw new RuntimeException(e);
}
@@ -76,7 +74,7 @@ public class GeyserSpongePingPassthrough implements IGeyserPingPassthrough {
new GeyserPingInfo.Version(
event.getResponse().getVersion().getName(),
MinecraftConstants.PROTOCOL_VERSION) // thanks for also not exposing this sponge
- );
+ );
event.getResponse().getPlayers().get().getProfiles().stream()
.map(GameProfile::getName)
.map(op -> op.orElseThrow(IllegalStateException::new))
@@ -87,11 +85,15 @@ public class GeyserSpongePingPassthrough implements IGeyserPingPassthrough {
@SuppressWarnings("NullableProblems")
private static class GeyserStatusClient implements StatusClient {
- private static final InetSocketAddress FAKE_REMOTE = new InetSocketAddress(Inet4Address.getLoopbackAddress(), 69);
+ private final InetSocketAddress remote;
+
+ public GeyserStatusClient(InetSocketAddress remote) {
+ this.remote = remote;
+ }
@Override
public InetSocketAddress getAddress() {
- return FAKE_REMOTE;
+ return this.remote;
}
@Override
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 106d2b155..c3231f3bf 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
@@ -26,10 +26,10 @@
package org.geysermc.platform.sponge;
import com.google.inject.Inject;
+import org.geysermc.common.PlatformType;
import org.geysermc.connector.GeyserConnector;
import org.geysermc.connector.bootstrap.GeyserBootstrap;
import org.geysermc.connector.command.CommandManager;
-import org.geysermc.connector.common.PlatformType;
import org.geysermc.connector.configuration.GeyserConfiguration;
import org.geysermc.connector.dump.BootstrapDumpInfo;
import org.geysermc.connector.ping.GeyserLegacyPingPassthrough;
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 c77e82718..3171a1a91 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
@@ -70,7 +70,7 @@ public class GeyserSpongeCommandExecutor implements CommandCallable {
@Override
public List getSuggestions(CommandSource source, String arguments, @Nullable Location targetPosition) throws CommandException {
if (arguments.split(" ").length == 1) {
- return Arrays.asList("?", "help", "reload", "shutdown", "stop");
+ return connector.getCommandManager().getCommandNames();
}
return new ArrayList<>();
}
diff --git a/bootstrap/standalone/pom.xml b/bootstrap/standalone/pom.xml
index 07458f730..831239f66 100644
--- a/bootstrap/standalone/pom.xml
+++ b/bootstrap/standalone/pom.xml
@@ -6,15 +6,15 @@
org.geysermc
bootstrap-parent
- 1.1.0
- ../
+ 1.2.0-SNAPSHOT
bootstrap-standalone
+
org.geysermc
connector
- 1.1.0
+ 1.2.0-SNAPSHOT
compile
diff --git a/bootstrap/standalone/src/main/java/org/geysermc/platform/standalone/GeyserStandaloneBootstrap.java b/bootstrap/standalone/src/main/java/org/geysermc/platform/standalone/GeyserStandaloneBootstrap.java
index 123a9a600..a10b20d90 100644
--- a/bootstrap/standalone/src/main/java/org/geysermc/platform/standalone/GeyserStandaloneBootstrap.java
+++ b/bootstrap/standalone/src/main/java/org/geysermc/platform/standalone/GeyserStandaloneBootstrap.java
@@ -25,17 +25,23 @@
package org.geysermc.platform.standalone;
+import com.fasterxml.jackson.databind.BeanDescription;
+import com.fasterxml.jackson.databind.JavaType;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.introspect.AnnotatedField;
+import com.fasterxml.jackson.databind.introspect.BeanPropertyDefinition;
import lombok.Getter;
import net.minecrell.terminalconsole.TerminalConsoleAppender;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.core.Appender;
import org.apache.logging.log4j.core.Logger;
import org.apache.logging.log4j.core.appender.ConsoleAppender;
+import org.geysermc.common.PlatformType;
import org.geysermc.connector.GeyserConnector;
import org.geysermc.connector.bootstrap.GeyserBootstrap;
import org.geysermc.connector.command.CommandManager;
-import org.geysermc.connector.common.PlatformType;
import org.geysermc.connector.configuration.GeyserConfiguration;
+import org.geysermc.connector.configuration.GeyserJacksonConfiguration;
import org.geysermc.connector.dump.BootstrapDumpInfo;
import org.geysermc.connector.ping.GeyserLegacyPingPassthrough;
import org.geysermc.connector.ping.IGeyserPingPassthrough;
@@ -50,7 +56,8 @@ import java.lang.reflect.Method;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.text.MessageFormat;
-import java.util.UUID;
+import java.util.*;
+import java.util.stream.Collectors;
public class GeyserStandaloneBootstrap implements GeyserBootstrap {
@@ -67,6 +74,9 @@ public class GeyserStandaloneBootstrap implements GeyserBootstrap {
private GeyserConnector connector;
+ private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();
+
+ private static final Map argsConfigKeys = new HashMap<>();
public static void main(String[] args) {
GeyserStandaloneBootstrap bootstrap = new GeyserStandaloneBootstrap();
@@ -74,6 +84,8 @@ public class GeyserStandaloneBootstrap implements GeyserBootstrap {
boolean useGuiOpts = bootstrap.useGui;
String configFilenameOpt = bootstrap.configFilename;
+ List availableProperties = getPOJOForClass(GeyserJacksonConfiguration.class);
+
for (int i = 0; i < args.length; i++) {
// By default, standalone Geyser will check if it should open the GUI based on if the GUI is null
// Optionally, you can force the use of a GUI or no GUI by specifying args
@@ -91,11 +103,11 @@ public class GeyserStandaloneBootstrap implements GeyserBootstrap {
case "--config":
case "-c":
if (i >= args.length - 1) {
- System.err.println(MessageFormat.format(LanguageUtils.getLocaleStringLog("geyser.bootstrap.args.confignotspecified"), "-c"));
+ System.err.println(MessageFormat.format(LanguageUtils.getLocaleStringLog("geyser.bootstrap.args.config_not_specified"), "-c"));
return;
}
configFilenameOpt = args[i+1]; i++;
- System.out.println(MessageFormat.format(LanguageUtils.getLocaleStringLog("geyser.bootstrap.args.configspecified"), configFilenameOpt));
+ System.out.println(MessageFormat.format(LanguageUtils.getLocaleStringLog("geyser.bootstrap.args.config_specified"), configFilenameOpt));
break;
case "--help":
case "-h":
@@ -106,8 +118,43 @@ public class GeyserStandaloneBootstrap implements GeyserBootstrap {
System.out.println(" --gui, --nogui " + LanguageUtils.getLocaleStringLog("geyser.bootstrap.args.gui"));
return;
default:
- String badArgMsg = LanguageUtils.getLocaleStringLog("geyser.bootstrap.args.unrecognised");
- System.err.println(MessageFormat.format(badArgMsg, arg));
+ // We have likely added a config option argument
+ if (arg.startsWith("--")) {
+ // Split the argument by an =
+ String[] argParts = arg.substring(2).split("=");
+ if (argParts.length == 2) {
+ // Split the config key by . to allow for nested options
+ String[] configKeyParts = argParts[0].split("\\.");
+
+ // Loop the possible config options to check the passed key is valid
+ boolean found = false;
+ for (BeanPropertyDefinition property : availableProperties) {
+ if (configKeyParts[0].equals(property.getName())) {
+ if (configKeyParts.length > 1) {
+ // Loop sub-section options to check the passed key is valid
+ for (BeanPropertyDefinition subProperty : getPOJOForClass(property.getRawPrimaryType())) {
+ if (configKeyParts[1].equals(subProperty.getName())) {
+ found = true;
+ break;
+ }
+ }
+ } else {
+ found = true;
+ }
+
+ break;
+ }
+ }
+
+ // Add the found key to the stored list for later usage
+ if (found) {
+ argsConfigKeys.put(argParts[0], argParts[1]);
+ break;
+ }
+ }
+ }
+
+ System.err.println(LanguageUtils.getLocaleStringLog("geyser.bootstrap.args.unrecognised", arg));
return;
}
}
@@ -148,13 +195,21 @@ public class GeyserStandaloneBootstrap implements GeyserBootstrap {
try {
File configFile = FileUtils.fileOrCopiedFromResource(new File(configFilename), "config.yml", (x) -> x.replaceAll("generateduuid", UUID.randomUUID().toString()));
geyserConfig = FileUtils.loadConfig(configFile, GeyserStandaloneConfiguration.class);
+
+ handleArgsConfigOptions();
+
if (this.geyserConfig.getRemote().getAddress().equalsIgnoreCase("auto")) {
geyserConfig.setAutoconfiguredRemote(true); // Doesn't really need to be set but /shrug
geyserConfig.getRemote().setAddress("127.0.0.1");
}
} catch (IOException ex) {
geyserLogger.severe(LanguageUtils.getLocaleStringLog("geyser.config.failed"), ex);
- System.exit(0);
+ if (gui == null) {
+ System.exit(1);
+ } else {
+ // Leave the process running so the GUI is still visible
+ return;
+ }
}
GeyserConfiguration.checkGeyserConfiguration(geyserConfig, geyserLogger);
@@ -223,4 +278,99 @@ public class GeyserStandaloneBootstrap implements GeyserBootstrap {
public BootstrapDumpInfo getDumpInfo() {
return new GeyserStandaloneDumpInfo(this);
}
+
+ /**
+ * Get the {@link BeanPropertyDefinition}s for the given class
+ *
+ * @param clazz The class to get the definitions for
+ * @return A list of {@link BeanPropertyDefinition} for the given class
+ */
+ public static List getPOJOForClass(Class> clazz) {
+ JavaType javaType = OBJECT_MAPPER.getTypeFactory().constructType(clazz);
+
+ // Introspect the given type
+ BeanDescription beanDescription = OBJECT_MAPPER.getSerializationConfig().introspect(javaType);
+
+ // Find properties
+ List properties = beanDescription.findProperties();
+
+ // Get the ignored properties
+ Set ignoredProperties = OBJECT_MAPPER.getSerializationConfig().getAnnotationIntrospector()
+ .findPropertyIgnorals(beanDescription.getClassInfo()).getIgnored();
+
+ // Filter properties removing the ignored ones
+ return properties.stream()
+ .filter(property -> !ignoredProperties.contains(property.getName()))
+ .collect(Collectors.toList());
+ }
+
+ /**
+ * Set a POJO property value on an object
+ *
+ * @param property The {@link BeanPropertyDefinition} to set
+ * @param parentObject The object to alter
+ * @param value The new value of the property
+ */
+ private static void setConfigOption(BeanPropertyDefinition property, Object parentObject, Object value) {
+ Object parsedValue = value;
+
+ // Change the values type if needed
+ if (int.class.equals(property.getRawPrimaryType())) {
+ parsedValue = Integer.valueOf((String) parsedValue);
+ } else if (boolean.class.equals(property.getRawPrimaryType())) {
+ parsedValue = Boolean.valueOf((String) parsedValue);
+ }
+
+ // Force the value to be set
+ AnnotatedField field = property.getField();
+ field.fixAccess(true);
+ field.setValue(parentObject, parsedValue);
+ }
+
+ /**
+ * Update the loaded {@link GeyserStandaloneConfiguration} with any values passed in the command line arguments
+ */
+ private void handleArgsConfigOptions() {
+ // Get the available properties from the class
+ List availableProperties = getPOJOForClass(GeyserJacksonConfiguration.class);
+
+ for (Map.Entry configKey : argsConfigKeys.entrySet()) {
+ String[] configKeyParts = configKey.getKey().split("\\.");
+
+ // Loop over the properties looking for any matches against the stored one from the argument
+ for (BeanPropertyDefinition property : availableProperties) {
+ if (configKeyParts[0].equals(property.getName())) {
+ if (configKeyParts.length > 1) {
+ // Loop through the sub property if the first part matches
+ for (BeanPropertyDefinition subProperty : getPOJOForClass(property.getRawPrimaryType())) {
+ if (configKeyParts[1].equals(subProperty.getName())) {
+ geyserLogger.info(LanguageUtils.getLocaleStringLog("geyser.bootstrap.args.set_config_option", configKey.getKey(), configKey.getValue()));
+
+ // Set the sub property value on the config
+ try {
+ Object subConfig = property.getGetter().callOn(geyserConfig);
+ setConfigOption(subProperty, subConfig, configKey.getValue());
+ } catch (Exception e) {
+ geyserLogger.error("Failed to set config option: " + property.getFullName());
+ }
+
+ break;
+ }
+ }
+ } else {
+ geyserLogger.info(LanguageUtils.getLocaleStringLog("geyser.bootstrap.args.set_config_option", configKey.getKey(), configKey.getValue()));
+
+ // Set the property value on the config
+ try {
+ setConfigOption(property, geyserConfig, configKey.getValue());
+ } catch (Exception e) {
+ geyserLogger.error("Failed to set config option: " + property.getFullName());
+ }
+ }
+
+ break;
+ }
+ }
+ }
+ }
}
diff --git a/bootstrap/standalone/src/main/java/org/geysermc/platform/standalone/gui/GeyserStandaloneGUI.java b/bootstrap/standalone/src/main/java/org/geysermc/platform/standalone/gui/GeyserStandaloneGUI.java
index 50deeb1bc..aeee84624 100644
--- a/bootstrap/standalone/src/main/java/org/geysermc/platform/standalone/gui/GeyserStandaloneGUI.java
+++ b/bootstrap/standalone/src/main/java/org/geysermc/platform/standalone/gui/GeyserStandaloneGUI.java
@@ -261,14 +261,30 @@ public class GeyserStandaloneGUI {
for (Map.Entry command : geyserCommandManager.getCommands().entrySet()) {
// Remove the offhand command and any alias commands to prevent duplicates in the list
- if ("offhand".equals(command.getValue().getName()) || command.getValue().getAliases().contains(command.getKey())) {
+ if (!command.getValue().isExecutableOnConsole() || command.getValue().getAliases().contains(command.getKey())) {
continue;
}
// Create the button that runs the command
- JMenuItem commandButton = new JMenuItem(command.getValue().getName());
+ boolean hasSubCommands = command.getValue().hasSubCommands();
+ // Add an extra menu if there are more commands that can be run
+ JMenuItem commandButton = hasSubCommands ? new JMenu(command.getValue().getName()) : new JMenuItem(command.getValue().getName());
commandButton.getAccessibleContext().setAccessibleDescription(command.getValue().getDescription());
- commandButton.addActionListener(e -> command.getValue().execute(geyserStandaloneLogger, new String[]{ }));
+ if (!hasSubCommands) {
+ commandButton.addActionListener(e -> command.getValue().execute(geyserStandaloneLogger, new String[]{ }));
+ } else {
+ // Add a submenu that's the same name as the menu can't be pressed
+ JMenuItem otherCommandButton = new JMenuItem(command.getValue().getName());
+ otherCommandButton.getAccessibleContext().setAccessibleDescription(command.getValue().getDescription());
+ otherCommandButton.addActionListener(e -> command.getValue().execute(geyserStandaloneLogger, new String[]{ }));
+ commandButton.add(otherCommandButton);
+ // Add a menu option for all possible subcommands
+ for (String subCommandName : command.getValue().getSubCommands()) {
+ JMenuItem item = new JMenuItem(subCommandName);
+ item.addActionListener(e -> command.getValue().execute(geyserStandaloneLogger, new String[]{subCommandName}));
+ commandButton.add(item);
+ }
+ }
commandsMenu.add(commandButton);
}
@@ -291,7 +307,7 @@ public class GeyserStandaloneGUI {
playerTableModel.getDataVector().removeAllElements();
for (GeyserSession player : GeyserConnector.getInstance().getPlayers()) {
- Vector row = new Vector();
+ Vector row = new Vector<>();
row.add(player.getSocketAddress().getHostName());
row.add(player.getPlayerEntity().getUsername());
diff --git a/bootstrap/velocity/pom.xml b/bootstrap/velocity/pom.xml
index ee445b6e7..2fedca71a 100644
--- a/bootstrap/velocity/pom.xml
+++ b/bootstrap/velocity/pom.xml
@@ -6,21 +6,21 @@
org.geysermc
bootstrap-parent
- 1.1.0
- ../
+ 1.2.0-SNAPSHOT
bootstrap-velocity
+
org.geysermc
connector
- 1.1.0
+ 1.2.0-SNAPSHOT
compile
com.velocitypowered
velocity-api
- 1.0.0-SNAPSHOT
+ 1.1.0
provided
@@ -93,7 +93,16 @@
com.google.code.gson:*
- io.netty:*
+
+ io.netty:netty-transport-native-epoll:*
+ io.netty:netty-transport-native-unix-common:*
+ io.netty:netty-transport-native-kqueue:*
+ io.netty:netty-handler:*
+ io.netty:netty-common:*
+ io.netty:netty-buffer:*
+ io.netty:netty-resolver:*
+ io.netty:netty-transport:*
+ io.netty:netty-codec:*
org.slf4j:*
org.ow2.asm:*
diff --git a/bootstrap/velocity/src/main/java/org/geysermc/platform/velocity/GeyserVelocityPingPassthrough.java b/bootstrap/velocity/src/main/java/org/geysermc/platform/velocity/GeyserVelocityPingPassthrough.java
index ff8376e4b..9674d27ef 100644
--- a/bootstrap/velocity/src/main/java/org/geysermc/platform/velocity/GeyserVelocityPingPassthrough.java
+++ b/bootstrap/velocity/src/main/java/org/geysermc/platform/velocity/GeyserVelocityPingPassthrough.java
@@ -43,15 +43,13 @@ import java.util.concurrent.ExecutionException;
@AllArgsConstructor
public class GeyserVelocityPingPassthrough implements IGeyserPingPassthrough {
- private static final GeyserInboundConnection FAKE_INBOUND_CONNECTION = new GeyserInboundConnection();
-
private final ProxyServer server;
@Override
- public GeyserPingInfo getPingInformation() {
+ public GeyserPingInfo getPingInformation(InetSocketAddress inetSocketAddress) {
ProxyPingEvent event;
try {
- event = server.getEventManager().fire(new ProxyPingEvent(FAKE_INBOUND_CONNECTION, ServerPing.builder()
+ event = server.getEventManager().fire(new ProxyPingEvent(new GeyserInboundConnection(inetSocketAddress), ServerPing.builder()
.description(server.getConfiguration().getMotdComponent()).onlinePlayers(server.getPlayerCount())
.maximumPlayers(server.getConfiguration().getShowMaxPlayers()).build())).get();
} catch (ExecutionException | InterruptedException e) {
@@ -74,11 +72,15 @@ public class GeyserVelocityPingPassthrough implements IGeyserPingPassthrough {
private static class GeyserInboundConnection implements InboundConnection {
- private static final InetSocketAddress FAKE_REMOTE = new InetSocketAddress(Inet4Address.getLoopbackAddress(), 69);
+ private final InetSocketAddress remote;
+
+ public GeyserInboundConnection(InetSocketAddress remote) {
+ this.remote = remote;
+ }
@Override
public InetSocketAddress getRemoteAddress() {
- return FAKE_REMOTE;
+ return this.remote;
}
@Override
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 291d7a001..b5255e623 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,9 +33,9 @@ import com.velocitypowered.api.event.proxy.ProxyShutdownEvent;
import com.velocitypowered.api.plugin.Plugin;
import com.velocitypowered.api.proxy.ProxyServer;
import lombok.Getter;
+import org.geysermc.common.PlatformType;
import org.geysermc.connector.GeyserConnector;
import org.geysermc.connector.bootstrap.GeyserBootstrap;
-import org.geysermc.connector.common.PlatformType;
import org.geysermc.connector.configuration.GeyserConfiguration;
import org.geysermc.connector.dump.BootstrapDumpInfo;
import org.geysermc.connector.ping.GeyserLegacyPingPassthrough;
@@ -121,7 +121,7 @@ public class GeyserVelocityPlugin implements GeyserBootstrap {
this.connector = GeyserConnector.start(PlatformType.VELOCITY, this);
this.geyserCommandManager = new GeyserVelocityCommandManager(connector);
- this.commandManager.register(new GeyserVelocityCommandExecutor(connector), "geyser");
+ this.commandManager.register("geyser", new GeyserVelocityCommandExecutor(connector));
if (geyserConfig.isLegacyPingPassthrough()) {
this.geyserPingPassthrough = GeyserLegacyPingPassthrough.init(connector);
} else {
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 afd6c3bfd..30c474139 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
@@ -25,41 +25,48 @@
package org.geysermc.platform.velocity.command;
-import com.velocitypowered.api.command.Command;
import com.velocitypowered.api.command.CommandSource;
-
+import com.velocitypowered.api.command.SimpleCommand;
import lombok.AllArgsConstructor;
-
-import net.kyori.text.TextComponent;
-
-import org.geysermc.connector.common.ChatColor;
import org.geysermc.connector.GeyserConnector;
+import org.geysermc.connector.command.CommandSender;
import org.geysermc.connector.command.GeyserCommand;
+import org.geysermc.connector.common.ChatColor;
import org.geysermc.connector.utils.LanguageUtils;
+import java.util.ArrayList;
import java.util.Arrays;
+import java.util.List;
@AllArgsConstructor
-public class GeyserVelocityCommandExecutor implements Command {
+public class GeyserVelocityCommandExecutor implements SimpleCommand {
- private GeyserConnector connector;
+ private final GeyserConnector connector;
@Override
- public void execute(CommandSource source, String[] args) {
- if (args.length > 0) {
- if (getCommand(args[0]) != null) {
- if (!source.hasPermission(getCommand(args[0]).getPermission())) {
- // Not ideal to use log here but we dont get a session
- source.sendMessage(TextComponent.of(ChatColor.RED + LanguageUtils.getLocaleStringLog("geyser.bootstrap.command.permission_fail")));
+ public void execute(Invocation invocation) {
+ if (invocation.arguments().length > 0) {
+ if (getCommand(invocation.arguments()[0]) != null) {
+ if (!invocation.source().hasPermission(getCommand(invocation.arguments()[0]).getPermission())) {
+ CommandSender sender = new VelocityCommandSender(invocation.source());
+ sender.sendMessage(ChatColor.RED + LanguageUtils.getPlayerLocaleString("geyser.bootstrap.command.permission_fail", sender.getLocale()));
return;
}
- getCommand(args[0]).execute(new VelocityCommandSender(source), args.length > 1 ? Arrays.copyOfRange(args, 1, args.length) : new String[0]);
+ getCommand(invocation.arguments()[0]).execute(new VelocityCommandSender(invocation.source()), invocation.arguments().length > 1 ? Arrays.copyOfRange(invocation.arguments(), 1, invocation.arguments().length) : new String[0]);
}
} else {
- getCommand("help").execute(new VelocityCommandSender(source), new String[0]);
+ getCommand("help").execute(new VelocityCommandSender(invocation.source()), new String[0]);
}
}
+ @Override
+ public List suggest(Invocation invocation) {
+ if (invocation.arguments().length == 0) {
+ return connector.getCommandManager().getCommandNames();
+ }
+ return new ArrayList<>();
+ }
+
private GeyserCommand getCommand(String label) {
return connector.getCommandManager().getCommands().get(label);
}
diff --git a/bootstrap/velocity/src/main/java/org/geysermc/platform/velocity/command/VelocityCommandSender.java b/bootstrap/velocity/src/main/java/org/geysermc/platform/velocity/command/VelocityCommandSender.java
index 1b0d6f3e6..3a1c46033 100644
--- a/bootstrap/velocity/src/main/java/org/geysermc/platform/velocity/command/VelocityCommandSender.java
+++ b/bootstrap/velocity/src/main/java/org/geysermc/platform/velocity/command/VelocityCommandSender.java
@@ -28,17 +28,21 @@ package org.geysermc.platform.velocity.command;
import com.velocitypowered.api.command.CommandSource;
import com.velocitypowered.api.proxy.ConsoleCommandSource;
import com.velocitypowered.api.proxy.Player;
-
-import lombok.AllArgsConstructor;
-
import net.kyori.text.TextComponent;
-
import org.geysermc.connector.command.CommandSender;
+import org.geysermc.connector.utils.LanguageUtils;
+
+import java.util.Locale;
-@AllArgsConstructor
public class VelocityCommandSender implements CommandSender {
- private CommandSource handle;
+ private final CommandSource handle;
+
+ public VelocityCommandSender(CommandSource handle) {
+ this.handle = handle;
+ // Ensure even Java players' languages are loaded
+ LanguageUtils.loadGeyserLocale(getLocale());
+ }
@Override
public String getName() {
@@ -59,4 +63,13 @@ public class VelocityCommandSender implements CommandSender {
public boolean isConsole() {
return handle instanceof ConsoleCommandSource;
}
+
+ @Override
+ public String getLocale() {
+ if (handle instanceof Player) {
+ Locale locale = ((Player) handle).getPlayerSettings().getLocale();
+ return LanguageUtils.formatLocale(locale.getLanguage() + "_" + locale.getCountry());
+ }
+ return LanguageUtils.getDefaultLocale();
+ }
}
diff --git a/common/pom.xml b/common/pom.xml
index 85dde12c6..32c4b1876 100644
--- a/common/pom.xml
+++ b/common/pom.xml
@@ -6,11 +6,10 @@
org.geysermc
geyser-parent
- parent
- ../
+ 1.2.0-SNAPSHOT
common
- 1.1.0
+
com.google.code.gson
diff --git a/connector/src/main/java/org/geysermc/connector/common/PlatformType.java b/common/src/main/java/org/geysermc/common/PlatformType.java
similarity index 97%
rename from connector/src/main/java/org/geysermc/connector/common/PlatformType.java
rename to common/src/main/java/org/geysermc/common/PlatformType.java
index 3e945d3ad..883490208 100644
--- a/connector/src/main/java/org/geysermc/connector/common/PlatformType.java
+++ b/common/src/main/java/org/geysermc/common/PlatformType.java
@@ -23,7 +23,7 @@
* @link https://github.com/GeyserMC/Geyser
*/
-package org.geysermc.connector.common;
+package org.geysermc.common;
import lombok.AllArgsConstructor;
import lombok.Getter;
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 efc71ae8d..045552b6e 100644
--- a/common/src/main/java/org/geysermc/common/window/CustomFormWindow.java
+++ b/common/src/main/java/org/geysermc/common/window/CustomFormWindow.java
@@ -92,7 +92,7 @@ public class CustomFormWindow extends FormWindow {
}
public void setResponse(String data) {
- if (data == null || data.equalsIgnoreCase("null") || data.isEmpty()) {
+ if (data == null || data.trim().equalsIgnoreCase("null") || data.isEmpty()) {
closed = true;
return;
}
@@ -108,7 +108,7 @@ public class CustomFormWindow extends FormWindow {
List componentResponses = new ArrayList<>();
try {
- componentResponses = new ObjectMapper().readValue(data, new TypeReference>(){});
+ componentResponses = new ObjectMapper().readValue(data.trim(), new TypeReference>(){});
} catch (IOException e) { }
for (String response : componentResponses) {
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 7c1acc26f..3101f5fb3 100644
--- a/common/src/main/java/org/geysermc/common/window/SimpleFormWindow.java
+++ b/common/src/main/java/org/geysermc/common/window/SimpleFormWindow.java
@@ -72,14 +72,14 @@ public class SimpleFormWindow extends FormWindow {
}
public void setResponse(String data) {
- if (data == null || data.equalsIgnoreCase("null")) {
+ if (data == null || data.trim().equalsIgnoreCase("null")) {
closed = true;
return;
}
int buttonID;
try {
- buttonID = Integer.parseInt(data);
+ buttonID = Integer.parseInt(data.trim());
} catch (Exception ex) {
return;
}
diff --git a/connector/pom.xml b/connector/pom.xml
index 5df525567..24e2983bc 100644
--- a/connector/pom.xml
+++ b/connector/pom.xml
@@ -6,16 +6,15 @@
org.geysermc
geyser-parent
- parent
- ../
+ 1.2.0-SNAPSHOT
connector
- 1.1.0
+
org.geysermc
common
- 1.1.0
+ 1.2.0-SNAPSHOT
compile
@@ -31,17 +30,28 @@
compile
- com.github.CloudburstMC.Protocol
- bedrock-v408
- 02f46a8700
+ com.nukkitx.protocol
+ bedrock-v422
+ 2.6.1-SNAPSHOT
compile
net.sf.trove4j
trove
+
+
+ com.nukkitx.network
+ raknet
+
+
+ com.nukkitx.network
+ raknet
+ 1.6.20
+ compile
+
com.nukkitx.fastutil
fastutil-int-int-maps
@@ -111,13 +121,17 @@
com.github.steveice10
mcprotocollib
- 3a69a0614c
+ 26201a4
compile
io.netty
netty-all
+
+ net.kyori
+ adventure-text-serializer-gson
+
@@ -137,23 +151,35 @@
2.1.3
- com.github.kyoripowered.adventure
+ net.kyori
adventure-api
- 557865caef
+ 4.3.0
compile
- com.github.kyoripowered.adventure
+ net.kyori
adventure-text-serializer-gson
- 557865caef
+ 4.3.0
compile
- com.github.kyoripowered.adventure
+ net.kyori
adventure-text-serializer-legacy
- 557865caef
+ 4.3.0
compile
+
+ net.kyori
+ adventure-text-serializer-gson-legacy-impl
+ 4.3.0
+ compile
+
+
+ junit
+ junit
+ 4.13.1
+ test
+
@@ -210,8 +236,12 @@
- VERSION = ".*"
- VERSION = "${project.version} (git-${git.branch}-${git.commit.id.abbrev})"
+ String VERSION = ".*"
+ String VERSION = "${project.version} (" + GIT_VERSION + ")"
+
+
+ String GIT_VERSION = ".*"
+ String GIT_VERSION = "git-${git.branch}-${git.commit.id.abbrev}"
@@ -229,8 +259,12 @@
- VERSION = ".*"
- VERSION = "DEV"
+ String VERSION = ".*"
+ String VERSION = "DEV"
+
+
+ String GIT_VERSION = ".*"
+ String GIT_VERSION = "DEV"
@@ -252,6 +286,8 @@