From 42972154207ecb582b00338930f41b3ece13c450 Mon Sep 17 00:00:00 2001 From: rtm516 Date: Wed, 18 Nov 2020 23:18:36 +0000 Subject: [PATCH 01/43] More chat fixes (#1557) * Fix positional translation arguments not being handled * Fix locale fallback * Fix command completion * Remove the expensive call to `containsKey` * Unify adventure versions * Fix some more formatting issues due to parity * Fix and update tests * Update adventure * Add Javadoc for getCommandNames * Formatting Co-authored-by: Camotoy <20743703+DoctorMacc@users.noreply.github.com> --- .../command/GeyserBungeeCommandExecutor.java | 2 +- .../command/GeyserSpigotCommandExecutor.java | 2 +- .../command/GeyserSpongeCommandExecutor.java | 2 +- .../GeyserVelocityCommandExecutor.java | 28 +++++++++++++------ connector/pom.xml | 8 +++--- .../connector/command/CommandManager.java | 11 ++++++-- .../translators/chat/MessageTranslator.java | 14 ++++++++-- .../chat/MinecraftTranslationRegistry.java | 10 +++++++ .../connector/utils/LanguageUtils.java | 6 +++- .../chat/MessageTranslatorTest.java | 20 +++++++++---- 10 files changed, 76 insertions(+), 27 deletions(-) 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 ff7a2e3dd..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 @@ -67,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/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 0edb8448d..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 @@ -67,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/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/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 c329fb193..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,8 +25,8 @@ 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 org.geysermc.connector.GeyserConnector; import org.geysermc.connector.command.CommandSender; @@ -34,29 +34,39 @@ 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 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())) { - CommandSender sender = new VelocityCommandSender(source); + 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/connector/pom.xml b/connector/pom.xml index 7c44ddfd2..95794ff25 100644 --- a/connector/pom.xml +++ b/connector/pom.xml @@ -136,21 +136,21 @@ 2.1.3 - net.kyori + com.github.kyoripowered.adventure adventure-api - 4.1.1 + 7acd956 compile com.github.kyoripowered.adventure adventure-text-serializer-gson - 4d8a67d798 + 7acd956 compile com.github.kyoripowered.adventure adventure-text-serializer-legacy - 0599048 + 7acd956 compile diff --git a/connector/src/main/java/org/geysermc/connector/command/CommandManager.java b/connector/src/main/java/org/geysermc/connector/command/CommandManager.java index 7adce4300..822b6deae 100644 --- a/connector/src/main/java/org/geysermc/connector/command/CommandManager.java +++ b/connector/src/main/java/org/geysermc/connector/command/CommandManager.java @@ -31,9 +31,7 @@ import org.geysermc.connector.GeyserConnector; import org.geysermc.connector.command.defaults.*; import org.geysermc.connector.utils.LanguageUtils; -import java.util.Collections; -import java.util.HashMap; -import java.util.Map; +import java.util.*; public abstract class CommandManager { @@ -92,6 +90,13 @@ public abstract class CommandManager { cmd.execute(sender, args); } + /** + * @return a list of all subcommands under {@code /geyser}. + */ + public List getCommandNames() { + return Arrays.asList(connector.getCommandManager().getCommands().keySet().toArray(new String[0])); + } + /** * Returns the description of the given command * diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/chat/MessageTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/chat/MessageTranslator.java index be01362fa..b7e6838e4 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/chat/MessageTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/chat/MessageTranslator.java @@ -84,7 +84,17 @@ public class MessageTranslator { Locale localeCode = Locale.forLanguageTag(locale.replace('_', '-')); component = RENDERER.render(component, localeCode); - return LegacyComponentSerializer.legacySection().serialize(component); + String legacy = LegacyComponentSerializer.legacySection().serialize(component); + + // Strip strikethrough and underline as they are not supported on bedrock + legacy = legacy.replaceAll("\u00a7[mn]", ""); + + // Make color codes reset formatting like Java + // See https://minecraft.gamepedia.com/Formatting_codes#Usage + legacy = legacy.replaceAll("\u00a7([0-9a-f])", "\u00a7r\u00a7$1"); + legacy = legacy.replaceAll("\u00a7r\u00a7r", "\u00a7r"); + + return legacy; } public static String convertMessage(String message) { @@ -106,7 +116,7 @@ public class MessageTranslator { String convertedMessage = convertMessage(convertToJavaMessage(message), locale); // We have to do this since Adventure strips the starting reset character - if (message.startsWith(getColor(ChatColor.RESET))) { + if (message.startsWith(getColor(ChatColor.RESET)) && !convertedMessage.startsWith(getColor(ChatColor.RESET))) { convertedMessage = getColor(ChatColor.RESET) + convertedMessage; } diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/chat/MinecraftTranslationRegistry.java b/connector/src/main/java/org/geysermc/connector/network/translators/chat/MinecraftTranslationRegistry.java index a23167ac2..127e00603 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/chat/MinecraftTranslationRegistry.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/chat/MinecraftTranslationRegistry.java @@ -61,6 +61,16 @@ public class MinecraftTranslationRegistry implements TranslationRegistry { } m.appendTail(sb); + // Replace the `%x$s` with numbered inserts `{x}` + p = Pattern.compile("%([0-9]+)\\$s"); + m = p.matcher(sb.toString()); + sb = new StringBuffer(); + while (m.find()) { + i = Integer.parseInt(m.group(1)) - 1; + m.appendReplacement(sb, "{" + i + "}"); + } + m.appendTail(sb); + return new MessageFormat(sb.toString(), locale); } diff --git a/connector/src/main/java/org/geysermc/connector/utils/LanguageUtils.java b/connector/src/main/java/org/geysermc/connector/utils/LanguageUtils.java index 06d2936ef..87722e5b8 100644 --- a/connector/src/main/java/org/geysermc/connector/utils/LanguageUtils.java +++ b/connector/src/main/java/org/geysermc/connector/utils/LanguageUtils.java @@ -105,7 +105,11 @@ public class LanguageUtils { locale = formatLocale(locale); Properties properties = LOCALE_MAPPINGS.get(locale); - String formatString = properties.getProperty(key); + String formatString = null; + + if (properties != null) { + formatString = properties.getProperty(key); + } // Try and get the key from the default locale if (formatString == null) { diff --git a/connector/src/test/java/org/geysermc/connector/network/translators/chat/MessageTranslatorTest.java b/connector/src/test/java/org/geysermc/connector/network/translators/chat/MessageTranslatorTest.java index 5d52c79b9..4dd6a04cc 100644 --- a/connector/src/test/java/org/geysermc/connector/network/translators/chat/MessageTranslatorTest.java +++ b/connector/src/test/java/org/geysermc/connector/network/translators/chat/MessageTranslatorTest.java @@ -39,22 +39,31 @@ public class MessageTranslatorTest { @Before public void setUp() throws Exception { messages.put("{\"text\":\"\",\"extra\":[{\"text\":\"DoctorMad9952 joined the game\",\"color\":\"yellow\"}]}", - "§eDoctorMad9952 joined the game"); + "§r§eDoctorMad9952 joined the game"); messages.put("{\"text\":\"\",\"extra\":[\"Plugins (3): \",{\"text\":\"WorldEdit\",\"color\":\"green\"},{\"text\":\", \",\"color\":\"white\"},{\"text\":\"ViaVersion\",\"color\":\"green\"},{\"text\":\", \",\"color\":\"white\"},{\"text\":\"Geyser-Spigot\",\"color\":\"green\"}]}", - "Plugins (3): §aWorldEdit§f, §aViaVersion§f, §aGeyser-Spigot"); + "Plugins (3): §r§aWorldEdit§r§f, §r§aViaVersion§r§f, §r§aGeyser-Spigot"); // RGB downgrade test messages.put("{\"extra\":[{\"text\":\" \"},{\"color\":\"gold\",\"text\":\"The \"},{\"color\":\"#E14248\",\"obfuscated\":true,\"text\":\"||\"},{\"color\":\"#3AA9FF\",\"bold\":true,\"text\":\"CubeCraft\"},{\"color\":\"#E14248\",\"obfuscated\":true,\"text\":\"||\"},{\"color\":\"gold\",\"text\":\" Network \"},{\"color\":\"green\",\"text\":\"[1.8/1.9+]\\n \"},{\"color\":\"#f5e342\",\"text\":\"✦ \"},{\"color\":\"#b042f5\",\"bold\":true,\"text\":\"N\"},{\"color\":\"#c142f5\",\"bold\":true,\"text\":\"E\"},{\"color\":\"#d342f5\",\"bold\":true,\"text\":\"W\"},{\"color\":\"#e442f5\",\"bold\":true,\"text\":\":\"},{\"color\":\"#f542f5\",\"bold\":true,\"text\":\" \"},{\"color\":\"#bcf542\",\"bold\":true,\"text\":\"A\"},{\"color\":\"#acee3f\",\"bold\":true,\"text\":\"M\"},{\"color\":\"#9ce73c\",\"bold\":true,\"text\":\"O\"},{\"color\":\"#8ce039\",\"bold\":true,\"text\":\"N\"},{\"color\":\"#7cd936\",\"bold\":true,\"text\":\"G\"},{\"color\":\"#6cd233\",\"bold\":true,\"text\":\" \"},{\"color\":\"#5ccb30\",\"bold\":true,\"text\":\"S\"},{\"color\":\"#4cc42d\",\"bold\":true,\"text\":\"L\"},{\"color\":\"#3cbd2a\",\"bold\":true,\"text\":\"I\"},{\"color\":\"#2cb627\",\"bold\":true,\"text\":\"M\"},{\"color\":\"#1caf24\",\"bold\":true,\"text\":\"E\"},{\"color\":\"#0ca821\",\"bold\":true,\"text\":\"S\"},{\"color\":\"#f5e342\",\"text\":\" \"},{\"color\":\"#6d7c87\",\"text\":\"(kinda sus) \"},{\"color\":\"#f5e342\",\"text\":\"✦\"}],\"text\":\"\"}", - " §6The §c§k||§r§3§lCubeCraft§r§c§k||§r§6 Network §a[1.8/1.9+]\n" + - " §e✦ §d§lN§r§d§lE§r§d§lW§r§d§l:§r§d§l §r§e§lA§r§e§lM§r§a§lO§r§a§lN§r§a§lG§r§a§l §r§a§lS§r§a§lL§r§2§lI§r§2§lM§r§2§lE§r§2§lS§r§e §8(kinda sus) §e✦"); + " §r§6The §r§c§k||§r§3§lCubeCraft§r§c§k||§r§6 Network §r§a[1.8/1.9+]\n" + + " §r§e✦ §r§d§lN§r§d§lE§r§d§lW§r§d§l:§r§d§l §r§e§lA§r§e§lM§r§a§lO§r§a§lN§r§a§lG§r§a§l §r§a§lS§r§a§lL§r§2§lI§r§2§lM§r§2§lE§r§2§lS§r§e §r§8(kinda sus) §r§e✦"); + + // Color code format resetting + messages.put("{\"text\":\"\",\"extra\":[{\"text\":\"\",\"extra\":[{\"text\":\"[\",\"color\":\"gray\"},{\"text\":\"H\",\"color\":\"yellow\"},{\"text\":\"]\",\"color\":\"gray\"},{\"text\":\" \",\"color\":\"white\"},{\"text\":\"GUEST\",\"color\":\"#b7b7b7\",\"bold\":true}]},{\"text\":\"\",\"extra\":[{\"text\":\" \",\"bold\":true},{\"text\":\"»\",\"color\":\"blue\"},{\"text\":\" \",\"color\":\"gray\"}]},{\"text\":\"\",\"extra\":[{\"text\":\"rtm516\",\"color\":\"white\"},{\"text\":\": \",\"color\":\"gray\"},{\"text\":\"\",\"color\":\"white\"}]},{\"text\":\"\",\"extra\":[{\"text\":\"This is an amazing bedrock test message\",\"color\":\"white\"}]}]}\n", + "§r§7[§r§eH§r§7]§r§f §r§7§lGUEST§r§l §r§9»§r§7 §r§frtm516§r§7: §r§fThis is an amazing bedrock test message"); + + // Test translation and positional arguments + // Disabled due to not having an GeyserConnector instance, hence it fails + //messages.put("{\"translate\":\"death.attack.player\",\"with\":[{\"text\":\"rtm516\",\"insertion\":\"rtm516\"},{\"text\":\"*invincible_rt\",\"insertion\":\"*invincible_rt\"}]}", + // "rtm516 was slain by *invincible_rt"); } @Test public void convertMessage() { for (Map.Entry entry : messages.entrySet()) { String bedrockMessage = MessageTranslator.convertMessage(entry.getKey(), "en_US"); - Assert.assertEquals("Translation of messages is incorrect", bedrockMessage, entry.getValue()); + Assert.assertEquals("Translation of messages is incorrect", entry.getValue(), bedrockMessage); } } @@ -63,5 +72,6 @@ public class MessageTranslatorTest { Assert.assertEquals("All newline message is not handled properly", "\n\n\n\n", MessageTranslator.convertMessageLenient("\n\n\n\n")); Assert.assertEquals("Empty message is not handled properly", "", MessageTranslator.convertMessageLenient("")); Assert.assertEquals("Reset before message is not handled properly", "§r§eGame Selector", MessageTranslator.convertMessageLenient("§r§eGame Selector")); + Assert.assertEquals("Unimplemented formatting chars not stripped", "Bold Underline", MessageTranslator.convertMessageLenient("§m§nBold Underline")); } } From a70d3e2150e8b50c3fd2b74710d6e8fa504a6d07 Mon Sep 17 00:00:00 2001 From: circuit10 Date: Fri, 20 Nov 2020 19:56:39 +0000 Subject: [PATCH 02/43] Fix inconsistencies with movement and position (#699) Movement is now significant better, especially on slabs, stairs, and other half-blocks. Co-authored-by: RednedEpic Co-authored-by: DoctorMacc Co-authored-by: Tim203 Co-authored-by: Camotoy <20743703+DoctorMacc@users.noreply.github.com> --- bootstrap/spigot/pom.xml | 5 + .../platform/spigot/GeyserSpigotPlugin.java | 99 ++++++++- .../world/GeyserSpigotBlockPlaceListener.java | 15 +- .../GeyserSpigot1_12NativeWorldManager.java | 59 +++++ .../manager/GeyserSpigot1_12WorldManager.java | 141 ++++++++++++ .../GeyserSpigotFallbackWorldManager.java | 62 ++++++ .../GeyserSpigotLegacyNativeWorldManager.java | 79 +++++++ .../GeyserSpigotNativeWorldManager.java | 51 +++++ .../GeyserSpigotWorldManager.java | 118 +++------- connector/pom.xml | 2 + .../geysermc/connector/GeyserConnector.java | 2 + .../configuration/GeyserConfiguration.java | 2 + .../GeyserJacksonConfiguration.java | 3 + .../org/geysermc/connector/entity/Entity.java | 3 +- .../connector/entity/FireworkEntity.java | 1 + .../entity/{ => player}/PlayerEntity.java | 27 ++- .../entity/player/SessionPlayerEntity.java | 67 ++++++ .../connector/entity/type/EntityType.java | 1 + .../network/session/GeyserSession.java | 112 ++++++++-- .../network/session/cache/EntityCache.java | 2 +- .../network/session/cache/TeleportCache.java | 42 +++- .../bedrock/BedrockRespawnTranslator.java | 2 +- ...SetLocalPlayerAsInitializedTranslator.java | 2 +- .../player/BedrockActionTranslator.java | 1 + .../player/BedrockMovePlayerTranslator.java | 163 +++++++++----- .../translators/collision/BoundingBox.java | 52 +++++ .../collision/CollisionManager.java | 208 ++++++++++++++++++ .../collision/CollisionRemapper.java | 57 +++++ .../collision/CollisionTranslator.java | 192 ++++++++++++++++ .../collision/translators/BlockCollision.java | 154 +++++++++++++ .../collision/translators/DoorCollision.java | 92 ++++++++ .../collision/translators/EmptyCollision.java | 35 +++ .../translators/GrassPathCollision.java | 49 +++++ .../collision/translators/OtherCollision.java | 53 +++++ .../translators/ScaffoldingCollision.java | 72 ++++++ .../collision/translators/SnowCollision.java | 103 +++++++++ .../collision/translators/SolidCollision.java | 39 ++++ .../translators/TrapdoorCollision.java | 105 +++++++++ .../java/JavaJoinGameTranslator.java | 2 +- .../entity/JavaEntityEffectTranslator.java | 8 +- .../JavaEntityRemoveEffectTranslator.java | 8 +- .../player/JavaPlayerAbilitiesTranslator.java | 10 +- .../player/JavaPlayerListEntryTranslator.java | 2 +- .../JavaPlayerPositionRotationTranslator.java | 16 +- .../spawn/JavaSpawnPlayerTranslator.java | 2 +- .../world/JavaNotifyClientTranslator.java | 2 +- .../world/block/BlockTranslator.java | 8 + .../geysermc/connector/utils/BlockUtils.java | 4 +- .../connector/utils/DimensionUtils.java | 4 +- .../geysermc/connector/utils/SkinUtils.java | 2 +- connector/src/main/resources/config.yml | 21 +- connector/src/main/resources/languages | 2 +- connector/src/main/resources/mappings | 2 +- 53 files changed, 2117 insertions(+), 248 deletions(-) create mode 100644 bootstrap/spigot/src/main/java/org/geysermc/platform/spigot/world/manager/GeyserSpigot1_12NativeWorldManager.java create mode 100644 bootstrap/spigot/src/main/java/org/geysermc/platform/spigot/world/manager/GeyserSpigot1_12WorldManager.java create mode 100644 bootstrap/spigot/src/main/java/org/geysermc/platform/spigot/world/manager/GeyserSpigotFallbackWorldManager.java create mode 100644 bootstrap/spigot/src/main/java/org/geysermc/platform/spigot/world/manager/GeyserSpigotLegacyNativeWorldManager.java create mode 100644 bootstrap/spigot/src/main/java/org/geysermc/platform/spigot/world/manager/GeyserSpigotNativeWorldManager.java rename bootstrap/spigot/src/main/java/org/geysermc/platform/spigot/world/{ => manager}/GeyserSpigotWorldManager.java (59%) rename connector/src/main/java/org/geysermc/connector/entity/{ => player}/PlayerEntity.java (94%) create mode 100644 connector/src/main/java/org/geysermc/connector/entity/player/SessionPlayerEntity.java create mode 100644 connector/src/main/java/org/geysermc/connector/network/translators/collision/BoundingBox.java create mode 100644 connector/src/main/java/org/geysermc/connector/network/translators/collision/CollisionManager.java create mode 100644 connector/src/main/java/org/geysermc/connector/network/translators/collision/CollisionRemapper.java create mode 100644 connector/src/main/java/org/geysermc/connector/network/translators/collision/CollisionTranslator.java create mode 100644 connector/src/main/java/org/geysermc/connector/network/translators/collision/translators/BlockCollision.java create mode 100644 connector/src/main/java/org/geysermc/connector/network/translators/collision/translators/DoorCollision.java create mode 100644 connector/src/main/java/org/geysermc/connector/network/translators/collision/translators/EmptyCollision.java create mode 100644 connector/src/main/java/org/geysermc/connector/network/translators/collision/translators/GrassPathCollision.java create mode 100644 connector/src/main/java/org/geysermc/connector/network/translators/collision/translators/OtherCollision.java create mode 100644 connector/src/main/java/org/geysermc/connector/network/translators/collision/translators/ScaffoldingCollision.java create mode 100644 connector/src/main/java/org/geysermc/connector/network/translators/collision/translators/SnowCollision.java create mode 100644 connector/src/main/java/org/geysermc/connector/network/translators/collision/translators/SolidCollision.java create mode 100644 connector/src/main/java/org/geysermc/connector/network/translators/collision/translators/TrapdoorCollision.java diff --git a/bootstrap/spigot/pom.xml b/bootstrap/spigot/pom.xml index f3b9cf88d..adaa7557f 100644 --- a/bootstrap/spigot/pom.xml +++ b/bootstrap/spigot/pom.xml @@ -29,6 +29,11 @@ 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/GeyserSpigotPlugin.java b/bootstrap/spigot/src/main/java/org/geysermc/platform/spigot/GeyserSpigotPlugin.java index 741763ec1..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,8 +25,10 @@ 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; @@ -42,18 +44,23 @@ 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; @@ -142,8 +149,48 @@ public class GeyserSpigotPlugin extends JavaPlugin implements GeyserBootstrap { // Set if we need to use a different method for getting a player's locale SpigotCommandSender.setUseLegacyLocaleMethod(!isCompatible(Bukkit.getServer().getVersion(), "1.12.0")); - this.geyserWorldManager = new GeyserSpigotWorldManager(isLegacy, use3dBiomes, isViaVersion); - GeyserSpigotBlockPlaceListener blockPlaceListener = new GeyserSpigotBlockPlaceListener(connector, isLegacy, isViaVersion); + 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); @@ -152,8 +199,9 @@ public class GeyserSpigotPlugin extends JavaPlugin implements GeyserBootstrap { @Override public void onDisable() { - if (connector != null) + if (connector != null) { connector.shutdown(); + } } @Override @@ -186,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); @@ -213,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/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 59% 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 3493fc25a..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,7 +23,7 @@ * @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; @@ -34,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; @@ -42,38 +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.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.io.InputStream; -import java.util.List; +/** + * The base world manager to use when there is no supported NMS revision + */ public class GeyserSpigotWorldManager extends GeyserWorldManager { /** * The current client protocol version for ViaVersion usage. */ - private static final int CLIENT_PROTOCOL_VERSION = MinecraftConstants.PROTOCOL_VERSION; + protected static final int CLIENT_PROTOCOL_VERSION = MinecraftConstants.PROTOCOL_VERSION; - /** - * Whether the server is pre-1.13. - */ - private final boolean isLegacy; /** * Whether the server is pre-1.16 and therefore does not support 3D biomes on an API level guaranteed. */ 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 - */ - private final boolean isViaVersion; /** * Stores a list of {@link Biome} ordinal numbers to Minecraft biome numeric IDs. * @@ -87,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"); @@ -116,83 +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) { + 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) { - Player bukkitPlayer = Bukkit.getPlayer(session.getPlayerEntity().getUsername()); - // Get block entity storage - BlockStorage storage = Via.getManager().getConnection(bukkitPlayer.getUniqueId()).get(BlockStorage.class); - return getLegacyBlock(storage, bukkitPlayer.getWorld(), x, y, z); - } else { - return BlockTranslator.JAVA_AIR_ID; - } - } - - @SuppressWarnings("deprecation") - public static int getLegacyBlock(BlockStorage storage, World world, int x, int y, int z) { - 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); - // Convert block state from old version (1.12.2) -> 1.13 -> 1.13.1 -> 1.14 -> 1.15 -> 1.16 -> 1.16.2 - blockId = ProtocolRegistry.getProtocol(Protocol1_13To1_12_2.class).getMappingData().getNewBlockId(blockId); - List> protocolList = ProtocolRegistry.getProtocolPath(CLIENT_PROTOCOL_VERSION, - ProtocolVersion.v1_13.getId()); - // 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; + 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) { - // Get block entity storage - BlockStorage storage = Via.getManager().getConnection(bukkitPlayer.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++) { - chunk.set(blockX, blockY, blockZ, getLegacyBlock(storage, world, (x << 4) + blockX, (y << 4) + blockY, (z << 4) + blockZ)); - } - } - } - } 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(), BlockTranslator.JAVA_AIR_ID); - 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); } } } @@ -254,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/connector/pom.xml b/connector/pom.xml index 95794ff25..d7017b0d8 100644 --- a/connector/pom.xml +++ b/connector/pom.xml @@ -257,6 +257,8 @@