From b371c14a273584601971c5f7a69db49707811ff7 Mon Sep 17 00:00:00 2001 From: Myles Date: Mon, 26 Sep 2016 01:44:21 +0100 Subject: [PATCH] Add Sponge Support --- TODOLIST | 3 +- bukkit/pom.xml | 15 +- .../bukkit/BukkitViaBulkChunkTranslator.java | 5 +- .../ViaVersion/listeners/UpdateListener.java | 4 +- bukkit/src/main/resources/plugin.yml | 1 + bungee/pom.xml | 13 ++ .../protocol1_9to1_8/Protocol1_9TO1_8.java | 5 +- .../BulkChunkTranslatorProvider.java | 2 +- jar/pom.xml | 17 +- pom.xml | 3 +- sponge/pom.xml | 72 +++++++ .../myles/ViaVersion/sponge/VersionInfo.java | 5 + .../us/myles/ViaVersion/SpongePlugin.java | 163 +++++++++++++++ .../ViaVersion/sponge/LoggerWrapper.java | 125 +++++++++++ .../ViaVersion/sponge/SpongeBossBar.java | 35 ++++ .../sponge/SpongeCommandHandler.java | 47 +++++ .../sponge/SpongeCommandSender.java | 39 ++++ .../ViaVersion/sponge/SpongeConfigAPI.java | 183 ++++++++++++++++ .../myles/ViaVersion/sponge/SpongeViaAPI.java | 81 +++++++ .../ViaVersion/sponge/SpongeViaInjector.java | 197 ++++++++++++++++++ .../ViaVersion/sponge/SpongeViaLoader.java | 72 +++++++ .../sponge/handlers/ViaDecodeHandler.java | 90 ++++++++ .../sponge/handlers/ViaEncodeHandler.java | 72 +++++++ .../sponge/handlers/ViaPacketHandler.java | 35 ++++ .../handlers/ViaVersionInitializer.java | 48 +++++ .../sponge/listeners/ClientLeaveListener.java | 12 ++ .../sponge/listeners/UpdateListener.java | 16 ++ .../SpongeViaBulkChunkTranslator.java | 55 +++++ .../SpongeViaMovementTransmitter.java | 41 ++++ .../sponge/util/ReflectionUtil.java | 130 ++++++++++++ 30 files changed, 1570 insertions(+), 16 deletions(-) create mode 100644 sponge/pom.xml create mode 100644 sponge/src/main/java-templates/us/myles/ViaVersion/sponge/VersionInfo.java create mode 100644 sponge/src/main/java/us/myles/ViaVersion/SpongePlugin.java create mode 100644 sponge/src/main/java/us/myles/ViaVersion/sponge/LoggerWrapper.java create mode 100644 sponge/src/main/java/us/myles/ViaVersion/sponge/SpongeBossBar.java create mode 100644 sponge/src/main/java/us/myles/ViaVersion/sponge/SpongeCommandHandler.java create mode 100644 sponge/src/main/java/us/myles/ViaVersion/sponge/SpongeCommandSender.java create mode 100644 sponge/src/main/java/us/myles/ViaVersion/sponge/SpongeConfigAPI.java create mode 100644 sponge/src/main/java/us/myles/ViaVersion/sponge/SpongeViaAPI.java create mode 100644 sponge/src/main/java/us/myles/ViaVersion/sponge/SpongeViaInjector.java create mode 100644 sponge/src/main/java/us/myles/ViaVersion/sponge/SpongeViaLoader.java create mode 100644 sponge/src/main/java/us/myles/ViaVersion/sponge/handlers/ViaDecodeHandler.java create mode 100644 sponge/src/main/java/us/myles/ViaVersion/sponge/handlers/ViaEncodeHandler.java create mode 100644 sponge/src/main/java/us/myles/ViaVersion/sponge/handlers/ViaPacketHandler.java create mode 100644 sponge/src/main/java/us/myles/ViaVersion/sponge/handlers/ViaVersionInitializer.java create mode 100644 sponge/src/main/java/us/myles/ViaVersion/sponge/listeners/ClientLeaveListener.java create mode 100644 sponge/src/main/java/us/myles/ViaVersion/sponge/listeners/UpdateListener.java create mode 100644 sponge/src/main/java/us/myles/ViaVersion/sponge/providers/SpongeViaBulkChunkTranslator.java create mode 100644 sponge/src/main/java/us/myles/ViaVersion/sponge/providers/SpongeViaMovementTransmitter.java create mode 100644 sponge/src/main/java/us/myles/ViaVersion/sponge/util/ReflectionUtil.java diff --git a/TODOLIST b/TODOLIST index 177bbbce7..7186f5903 100644 --- a/TODOLIST +++ b/TODOLIST @@ -1 +1,2 @@ -Handle injector errors +PORT STUFF TO GUAVA :D (so we dont need to include commons) +Test on SpongeForge, only tested SpongeVanilla \ No newline at end of file diff --git a/bukkit/pom.xml b/bukkit/pom.xml index a4d179ed1..62680b6dd 100644 --- a/bukkit/pom.xml +++ b/bukkit/pom.xml @@ -16,12 +16,25 @@ 1.8.8-R0.1-SNAPSHOT + + + + . + true + src/main/resources/ + + * + + + + + us.myles viaversion-common - ${parent.version} + ${project.parent.version} provided diff --git a/bukkit/src/main/java/us/myles/ViaVersion/bukkit/BukkitViaBulkChunkTranslator.java b/bukkit/src/main/java/us/myles/ViaVersion/bukkit/BukkitViaBulkChunkTranslator.java index 66b6ab226..7f9a6bcd2 100644 --- a/bukkit/src/main/java/us/myles/ViaVersion/bukkit/BukkitViaBulkChunkTranslator.java +++ b/bukkit/src/main/java/us/myles/ViaVersion/bukkit/BukkitViaBulkChunkTranslator.java @@ -19,7 +19,6 @@ public class BukkitViaBulkChunkTranslator extends BulkChunkTranslatorProvider { static { try { - // TODO: Abstract this ? mapChunkBulkRef = new ReflectionUtil.ClassReflection(ReflectionUtil.nms("PacketPlayOutMapChunkBulk")); mapChunkRef = new ReflectionUtil.ClassReflection(ReflectionUtil.nms("PacketPlayOutMapChunk")); if (((ViaVersionPlugin) Via.getPlatform()).isSpigot()) { @@ -74,7 +73,7 @@ public class BukkitViaBulkChunkTranslator extends BulkChunkTranslatorProvider { } @Override - public boolean isEnabled() { - return true; + public boolean isFiltered(Class packetClass) { + return packetClass.getName().endsWith("PacketPlayOutMapChunkBulk"); } } diff --git a/bukkit/src/main/java/us/myles/ViaVersion/listeners/UpdateListener.java b/bukkit/src/main/java/us/myles/ViaVersion/listeners/UpdateListener.java index c7c9c044c..dc0942192 100644 --- a/bukkit/src/main/java/us/myles/ViaVersion/listeners/UpdateListener.java +++ b/bukkit/src/main/java/us/myles/ViaVersion/listeners/UpdateListener.java @@ -3,7 +3,7 @@ package us.myles.ViaVersion.listeners; import org.bukkit.event.EventHandler; import org.bukkit.event.Listener; import org.bukkit.event.player.PlayerJoinEvent; -import us.myles.ViaVersion.api.ViaVersion; +import us.myles.ViaVersion.api.Via; import us.myles.ViaVersion.update.UpdateUtil; public class UpdateListener implements Listener { @@ -11,7 +11,7 @@ public class UpdateListener implements Listener { @EventHandler public void onJoin(PlayerJoinEvent e) { if (e.getPlayer().hasPermission("viaversion.update") - && ViaVersion.getConfig().isCheckForUpdates()) { + && Via.getConfig().isCheckForUpdates()) { UpdateUtil.sendUpdateMessage(e.getPlayer().getUniqueId()); } } diff --git a/bukkit/src/main/resources/plugin.yml b/bukkit/src/main/resources/plugin.yml index 764d152a8..524051b93 100644 --- a/bukkit/src/main/resources/plugin.yml +++ b/bukkit/src/main/resources/plugin.yml @@ -2,6 +2,7 @@ name: ViaVersion main: us.myles.ViaVersion.ViaVersionPlugin authors: [_MylesC, Matsv] version: ${project.version} +description: Allow newer Minecraft versions to connect to an older server version. load: postworld loadbefore: [ProtocolLib, ProxyPipe, SpigotLib, SkinRestorer] softdepend: [ProtocolSupport, PacketListenerApi] diff --git a/bungee/pom.xml b/bungee/pom.xml index a0dd671ca..bfad5ebd0 100644 --- a/bungee/pom.xml +++ b/bungee/pom.xml @@ -11,6 +11,19 @@ viaversion-bungee + + + + . + true + src/main/resources/ + + * + + + + + diff --git a/common/src/main/java/us/myles/ViaVersion/protocols/protocol1_9to1_8/Protocol1_9TO1_8.java b/common/src/main/java/us/myles/ViaVersion/protocols/protocol1_9to1_8/Protocol1_9TO1_8.java index 8926966ff..517ab5acb 100644 --- a/common/src/main/java/us/myles/ViaVersion/protocols/protocol1_9to1_8/Protocol1_9TO1_8.java +++ b/common/src/main/java/us/myles/ViaVersion/protocols/protocol1_9to1_8/Protocol1_9TO1_8.java @@ -91,9 +91,8 @@ public class Protocol1_9TO1_8 extends Protocol { @Override public boolean isFiltered(Class packetClass) { - if (!Via.getManager().getProviders().get(BulkChunkTranslatorProvider.class).isEnabled()) - return false; - return packetClass.getName().endsWith("PacketPlayOutMapChunkBulk"); + return Via.getManager().getProviders().get(BulkChunkTranslatorProvider.class).isFiltered(packetClass); + } @Override diff --git a/common/src/main/java/us/myles/ViaVersion/protocols/protocol1_9to1_8/providers/BulkChunkTranslatorProvider.java b/common/src/main/java/us/myles/ViaVersion/protocols/protocol1_9to1_8/providers/BulkChunkTranslatorProvider.java index c84698fdb..aa8982a27 100644 --- a/common/src/main/java/us/myles/ViaVersion/protocols/protocol1_9to1_8/providers/BulkChunkTranslatorProvider.java +++ b/common/src/main/java/us/myles/ViaVersion/protocols/protocol1_9to1_8/providers/BulkChunkTranslatorProvider.java @@ -11,7 +11,7 @@ public class BulkChunkTranslatorProvider implements Provider { return Arrays.asList(packet); } - public boolean isEnabled() { + public boolean isFiltered(Class packet) { return false; } } diff --git a/jar/pom.xml b/jar/pom.xml index bcfabc952..36f1ac584 100644 --- a/jar/pom.xml +++ b/jar/pom.xml @@ -18,7 +18,7 @@ . false - . + ../ LICENSE @@ -57,6 +57,10 @@ org.javassist us.myles.viaversion.libs.javassist + + org.apache + us.myles.viaversion.libs.apache + @@ -75,17 +79,22 @@ us.myles viaversion-common - ${parent.version} + ${project.parent.version} us.myles viaversion-bukkit - ${parent.version} + ${project.parent.version} us.myles viaversion-bungee - ${parent.version} + ${project.parent.version} + + + us.myles + viaversion-sponge + ${project.parent.version} diff --git a/pom.xml b/pom.xml index 2f923cd7d..3cd69f629 100644 --- a/pom.xml +++ b/pom.xml @@ -19,6 +19,7 @@ common bukkit bungee + sponge jar @@ -104,7 +105,7 @@ commons-lang commons-lang 2.6 - provided + compile diff --git a/sponge/pom.xml b/sponge/pom.xml new file mode 100644 index 000000000..115e29b0f --- /dev/null +++ b/sponge/pom.xml @@ -0,0 +1,72 @@ + + + + viaversion-parent + us.myles + 1.0.0-ALPHA-modules + + 4.0.0 + + viaversion-sponge + + + 1.8 + 1.8 + + + + + sponge + http://repo.spongepowered.org/maven + + + + + + + . + true + src/main/resources/ + + * + + + + + + org.codehaus.mojo + templating-maven-plugin + 1.0.0 + + + filter-src + + filter-sources + + + + + + + + + + + us.myles + viaversion-common + ${project.parent.version} + provided + + + + + org.spongepowered + spongeapi + 4.1.0 + provided + + + + \ No newline at end of file diff --git a/sponge/src/main/java-templates/us/myles/ViaVersion/sponge/VersionInfo.java b/sponge/src/main/java-templates/us/myles/ViaVersion/sponge/VersionInfo.java new file mode 100644 index 000000000..960f6a88d --- /dev/null +++ b/sponge/src/main/java-templates/us/myles/ViaVersion/sponge/VersionInfo.java @@ -0,0 +1,5 @@ +package us.myles.ViaVersion.sponge; + +public class VersionInfo { + public static final String VERSION = "${project.version}"; +} diff --git a/sponge/src/main/java/us/myles/ViaVersion/SpongePlugin.java b/sponge/src/main/java/us/myles/ViaVersion/SpongePlugin.java new file mode 100644 index 000000000..e4567df6c --- /dev/null +++ b/sponge/src/main/java/us/myles/ViaVersion/SpongePlugin.java @@ -0,0 +1,163 @@ +package us.myles.ViaVersion; + +import com.google.gson.JsonObject; +import com.google.inject.Inject; +import org.spongepowered.api.Game; +import org.spongepowered.api.entity.living.player.Player; +import org.spongepowered.api.event.Listener; +import org.spongepowered.api.event.game.state.GameStartedServerEvent; +import org.spongepowered.api.plugin.Plugin; +import org.spongepowered.api.plugin.PluginContainer; +import org.spongepowered.api.scheduler.SpongeExecutorService; +import org.spongepowered.api.text.serializer.TextSerializers; +import us.myles.ViaVersion.api.Via; +import us.myles.ViaVersion.api.ViaAPI; +import us.myles.ViaVersion.api.ViaVersionConfig; +import us.myles.ViaVersion.api.command.ViaCommandSender; +import us.myles.ViaVersion.api.configuration.ConfigurationProvider; +import us.myles.ViaVersion.api.platform.ViaPlatform; +import us.myles.ViaVersion.sponge.*; + +import java.util.Arrays; +import java.util.UUID; +import java.util.concurrent.TimeUnit; +import java.util.logging.Logger; + +@Plugin(id = "viaversion", + name = "ViaVersion", + version = VersionInfo.VERSION, + authors = {"_MylesC", "Matsv"}, + description = "Allow newer Minecraft versions to connect to an older server version." +) +public class SpongePlugin implements ViaPlatform { + @Inject + private Game game; + @Inject + private PluginContainer container; + + private SpongeExecutorService asyncExecutor; + private SpongeExecutorService syncExecutor; + private SpongeConfigAPI conf = new SpongeConfigAPI(this); + private SpongeViaAPI api = new SpongeViaAPI(); + private Logger logger; + + @Listener + public void onServerStart(GameStartedServerEvent event) { + // Setup Logger + logger = new LoggerWrapper(container.getLogger()); + // Setup Plugin + syncExecutor = game.getScheduler().createSyncExecutor(this); + asyncExecutor = game.getScheduler().createAsyncExecutor(this); + SpongeCommandHandler commandHandler = new SpongeCommandHandler(); + game.getCommandManager().register(this, commandHandler, Arrays.asList("viaversion", "viaver")); + // Init platform + Via.init(ViaManager.builder() + .platform(this) + .commandHandler(commandHandler) + .injector(new SpongeViaInjector()) + .loader(new SpongeViaLoader(this)) + .build()); + + // Inject! + Via.getManager().init(); + } + + @Override + public Logger getLogger() { + return logger; + } + + @Override + public String getPlatformName() { + return "Sponge"; + } + + @Override + public String getPluginVersion() { + return container.getVersion().orElse("Unknown Version"); + } + + @Override + public int runAsync(Runnable runnable) { + asyncExecutor.execute(runnable); + return -1; + } + + @Override + public int runSync(Runnable runnable) { + syncExecutor.execute(runnable); + return -1; + } + + @Override + public int runRepeatingSync(Runnable runnable, Long ticks) { + Long time = ticks * 50L; + syncExecutor.scheduleAtFixedRate(runnable, time, time, TimeUnit.MILLISECONDS); + // use id? + return -1; + } + + @Override + public void cancelTask(int taskId) { + // oh. + } + + @Override + public ViaCommandSender[] getOnlinePlayers() { + ViaCommandSender[] array = new ViaCommandSender[game.getServer().getOnlinePlayers().size()]; + int i = 0; + for (Player player : game.getServer().getOnlinePlayers()) { + array[i++] = new SpongeCommandSender(player); + } + return array; + } + + @Override + public void sendMessage(UUID uuid, String message) { + for (Player player : game.getServer().getOnlinePlayers()) { + if (player.getUniqueId().equals(uuid)) + player.sendMessage(TextSerializers.LEGACY_FORMATTING_CODE.deserialize(message)); + } + } + + @Override + public boolean kickPlayer(UUID uuid, String message) { + for (Player player : game.getServer().getOnlinePlayers()) { + if (player.getUniqueId().equals(uuid)) { + player.kick(TextSerializers.LEGACY_FORMATTING_CODE.deserialize(message)); + return true; + } + } + return false; + } + + @Override + public boolean isPluginEnabled() { + return true; + } + + @Override + public ViaAPI getApi() { + return api; + } + + @Override + public ViaVersionConfig getConf() { + return conf; + } + + @Override + public ConfigurationProvider getConfigurationProvider() { + return conf; + } + + @Override + public void onReload() { + // TODO: Warning? + } + + @Override + public JsonObject getDump() { + return new JsonObject(); + } +} diff --git a/sponge/src/main/java/us/myles/ViaVersion/sponge/LoggerWrapper.java b/sponge/src/main/java/us/myles/ViaVersion/sponge/LoggerWrapper.java new file mode 100644 index 000000000..63050bf9a --- /dev/null +++ b/sponge/src/main/java/us/myles/ViaVersion/sponge/LoggerWrapper.java @@ -0,0 +1,125 @@ +package us.myles.ViaVersion.sponge; + +import org.slf4j.Logger; + +import java.util.logging.Level; +import java.util.logging.LogRecord; + +public class LoggerWrapper extends java.util.logging.Logger { + private final Logger base; + + public LoggerWrapper(Logger logger) { + super("logger", null); + this.base = logger; + } + + @Override + public void log(LogRecord record) { + log(record.getLevel(), record.getMessage()); + } + + @Override + public void log(Level level, String msg) { + if (level == Level.FINEST) { + base.trace(msg); + return; + } + if (level == Level.FINE) { + base.debug(msg); + return; + } + if (level == Level.WARNING) { + base.warn(msg); + return; + } + if (level == Level.SEVERE) { + base.error(msg); + return; + } + if (level == Level.INFO) { + base.info(msg); + return; + } + base.trace(msg); + return; + } + + @Override + public void log(Level level, String msg, Object param1) { + if (level == Level.FINEST) { + base.trace(msg, param1); + return; + } + if (level == Level.FINE) { + base.debug(msg, param1); + return; + } + if (level == Level.WARNING) { + base.warn(msg, param1); + return; + } + if (level == Level.SEVERE) { + base.error(msg, param1); + return; + } + if (level == Level.INFO) { + base.info(msg, param1); + return; + } + base.trace(msg, param1); + return; + } + + @Override + public void log(Level level, String msg, Object[] params) { + if (level == Level.FINEST) { + base.trace(msg, params); + return; + } + if (level == Level.FINE) { + base.debug(msg, params); + return; + } + if (level == Level.WARNING) { + base.warn(msg, params); + return; + } + if (level == Level.SEVERE) { + base.error(msg, params); + return; + } + if (level == Level.INFO) { + base.info(msg, params); + return; + } + base.trace(msg, params); + return; + } + + @Override + public void log(Level level, String msg, Throwable params) { + if (level == Level.FINEST) { + base.trace(msg, params); + return; + } + if (level == Level.FINE) { + base.debug(msg, params); + return; + } + if (level == Level.WARNING) { + base.warn(msg, params); + return; + } + if (level == Level.SEVERE) { + base.error(msg, params); + return; + } + if (level == Level.INFO) { + base.info(msg, params); + return; + } + base.trace(msg, params); + return; + } + +} diff --git a/sponge/src/main/java/us/myles/ViaVersion/sponge/SpongeBossBar.java b/sponge/src/main/java/us/myles/ViaVersion/sponge/SpongeBossBar.java new file mode 100644 index 000000000..ae29531c7 --- /dev/null +++ b/sponge/src/main/java/us/myles/ViaVersion/sponge/SpongeBossBar.java @@ -0,0 +1,35 @@ +package us.myles.ViaVersion.sponge; + +import lombok.Getter; +import org.spongepowered.api.entity.living.player.Player; +import us.myles.ViaVersion.api.boss.BossBar; +import us.myles.ViaVersion.api.boss.BossColor; +import us.myles.ViaVersion.api.boss.BossStyle; +import us.myles.ViaVersion.boss.CommonBoss; + +@Getter +public class SpongeBossBar extends CommonBoss { + + public SpongeBossBar(String title, float health, BossColor color, BossStyle style) { + super(title, health, color, style); + } + + @Override + public BossBar addPlayer(Player player) { + addPlayer(player.getUniqueId()); + return this; + } + + @Override + public BossBar addPlayers(Player... players) { + for (Player p : players) + addPlayer(p); + return this; + } + + @Override + public BossBar removePlayer(Player player) { + removePlayer(player.getUniqueId()); + return this; + } +} diff --git a/sponge/src/main/java/us/myles/ViaVersion/sponge/SpongeCommandHandler.java b/sponge/src/main/java/us/myles/ViaVersion/sponge/SpongeCommandHandler.java new file mode 100644 index 000000000..a87c03805 --- /dev/null +++ b/sponge/src/main/java/us/myles/ViaVersion/sponge/SpongeCommandHandler.java @@ -0,0 +1,47 @@ +package us.myles.ViaVersion.sponge; + +import org.spongepowered.api.command.CommandCallable; +import org.spongepowered.api.command.CommandException; +import org.spongepowered.api.command.CommandResult; +import org.spongepowered.api.command.CommandSource; +import org.spongepowered.api.text.Text; +import us.myles.ViaVersion.commands.ViaCommandHandler; + +import java.util.List; +import java.util.Optional; + +public class SpongeCommandHandler extends ViaCommandHandler implements CommandCallable { + + @Override + public CommandResult process(CommandSource source, String arguments) throws CommandException { + String[] args = arguments.length() > 0 ? arguments.split(" ") : new String[0]; + onCommand(new SpongeCommandSender(source), args); + return CommandResult.success(); + } + + @Override + public List getSuggestions(CommandSource source, String arguments) throws CommandException { + String[] args = arguments.length() > 0 ? arguments.split(" ") : new String[0]; + return onTabComplete(new SpongeCommandSender(source), args); + } + + @Override + public boolean testPermission(CommandSource source) { + return source.hasPermission("viaversion.admin"); + } + + @Override + public Optional getShortDescription(CommandSource source) { + return Optional.of(Text.of("Shows ViaVersion Version and more.")); + } + + @Override + public Optional getHelp(CommandSource source) { + return Optional.empty(); + } + + @Override + public Text getUsage(CommandSource source) { + return Text.of("Usage /viaversion"); + } +} diff --git a/sponge/src/main/java/us/myles/ViaVersion/sponge/SpongeCommandSender.java b/sponge/src/main/java/us/myles/ViaVersion/sponge/SpongeCommandSender.java new file mode 100644 index 000000000..b4c29a260 --- /dev/null +++ b/sponge/src/main/java/us/myles/ViaVersion/sponge/SpongeCommandSender.java @@ -0,0 +1,39 @@ +package us.myles.ViaVersion.sponge; + +import lombok.AllArgsConstructor; +import org.spongepowered.api.command.CommandSource; +import org.spongepowered.api.entity.living.player.Player; +import org.spongepowered.api.text.serializer.TextSerializer; +import org.spongepowered.api.text.serializer.TextSerializers; +import us.myles.ViaVersion.api.command.ViaCommandSender; + +import java.util.UUID; + +@AllArgsConstructor +public class SpongeCommandSender implements ViaCommandSender { + private CommandSource source; + + @Override + public boolean hasPermission(String permission) { + return source.hasPermission(permission); + } + + @Override + public void sendMessage(String msg) { + source.sendMessage(TextSerializers.LEGACY_FORMATTING_CODE.deserialize(msg)); + } + + @Override + public UUID getUUID() { + if (source instanceof Player) { + return ((Player) source).getUniqueId(); + } else { + return UUID.fromString(getName()); + } + } + + @Override + public String getName() { + return source.getName(); + } +} diff --git a/sponge/src/main/java/us/myles/ViaVersion/sponge/SpongeConfigAPI.java b/sponge/src/main/java/us/myles/ViaVersion/sponge/SpongeConfigAPI.java new file mode 100644 index 000000000..df02666e9 --- /dev/null +++ b/sponge/src/main/java/us/myles/ViaVersion/sponge/SpongeConfigAPI.java @@ -0,0 +1,183 @@ +package us.myles.ViaVersion.sponge; + +import us.myles.ViaVersion.SpongePlugin; +import us.myles.ViaVersion.api.ViaVersionConfig; +import us.myles.ViaVersion.api.configuration.ConfigurationProvider; + +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public class SpongeConfigAPI implements ViaVersionConfig, ConfigurationProvider{ + private final SpongePlugin spongePlugin; + + public SpongeConfigAPI(SpongePlugin spongePlugin) { + this.spongePlugin = spongePlugin; + } + + @Override + public boolean isCheckForUpdates() { + return false; + } + + @Override + public boolean isPreventCollision() { + return false; + } + + @Override + public boolean isNewEffectIndicator() { + return false; + } + + @Override + public boolean isShowNewDeathMessages() { + return false; + } + + @Override + public boolean isSuppressMetadataErrors() { + return false; + } + + @Override + public boolean isShieldBlocking() { + return false; + } + + @Override + public boolean isHologramPatch() { + return false; + } + + @Override + public boolean isBossbarPatch() { + return false; + } + + @Override + public boolean isBossbarAntiflicker() { + return false; + } + + @Override + public boolean isUnknownEntitiesSuppressed() { + return false; + } + + @Override + public double getHologramYOffset() { + return 0; + } + + @Override + public boolean isAutoTeam() { + return false; + } + + @Override + public boolean isBlockBreakPatch() { + return false; + } + + @Override + public int getMaxPPS() { + return 0; + } + + @Override + public String getMaxPPSKickMessage() { + return null; + } + + @Override + public int getTrackingPeriod() { + return 0; + } + + @Override + public int getWarningPPS() { + return 0; + } + + @Override + public int getMaxWarnings() { + return 0; + } + + @Override + public String getMaxWarningsKickMessage() { + return null; + } + + @Override + public boolean isAntiXRay() { + return false; + } + + @Override + public boolean isSendSupportedVersions() { + return false; + } + + @Override + public boolean isStimulatePlayerTick() { + return false; + } + + @Override + public boolean isItemCache() { + return false; + } + + @Override + public boolean isNMSPlayerTicking() { + return false; + } + + @Override + public boolean isReplacePistons() { + return false; + } + + @Override + public int getPistonReplacementId() { + return 0; + } + + @Override + public boolean isForceJsonTransform() { + return false; + } + + @Override + public List getBlockedProtocols() { + return Arrays.asList(0); + } + + @Override + public String getBlockedDisconnectMsg() { + return "Boop"; + } + + @Override + public String getReloadDisconnectMsg() { + return "Beep"; + } + + @Override + public void set(String path, Object value) { + + } + + @Override + public void saveConfig() { + + } + + @Override + public Map getValues() { + return new HashMap<>(); + } +} diff --git a/sponge/src/main/java/us/myles/ViaVersion/sponge/SpongeViaAPI.java b/sponge/src/main/java/us/myles/ViaVersion/sponge/SpongeViaAPI.java new file mode 100644 index 000000000..f0628a7f1 --- /dev/null +++ b/sponge/src/main/java/us/myles/ViaVersion/sponge/SpongeViaAPI.java @@ -0,0 +1,81 @@ +package us.myles.ViaVersion.sponge; + +import io.netty.buffer.ByteBuf; +import lombok.AllArgsConstructor; +import lombok.NonNull; +import org.spongepowered.api.entity.living.player.Player; +import us.myles.ViaVersion.SpongePlugin; +import us.myles.ViaVersion.api.Via; +import us.myles.ViaVersion.api.ViaAPI; +import us.myles.ViaVersion.api.boss.BossBar; +import us.myles.ViaVersion.api.boss.BossColor; +import us.myles.ViaVersion.api.boss.BossStyle; +import us.myles.ViaVersion.api.data.UserConnection; +import us.myles.ViaVersion.api.protocol.ProtocolRegistry; +import us.myles.ViaVersion.protocols.base.ProtocolInfo; + +import java.util.Map; +import java.util.SortedSet; +import java.util.TreeSet; +import java.util.UUID; + +public class SpongeViaAPI implements ViaAPI { + + @Override + public int getPlayerVersion(@NonNull Player player) { + if (!isPorted(player.getUniqueId())) + return ProtocolRegistry.SERVER_PROTOCOL; + return getPortedPlayers().get(player.getUniqueId()).get(ProtocolInfo.class).getProtocolVersion(); + } + + @Override + public int getPlayerVersion(@NonNull UUID uuid) { + if (!isPorted(uuid)) + return ProtocolRegistry.SERVER_PROTOCOL; + return getPortedPlayers().get(uuid).get(ProtocolInfo.class).getProtocolVersion(); + } + + @Override + public boolean isPorted(UUID playerUUID) { + return getPortedPlayers().containsKey(playerUUID); + } + + @Override + public String getVersion() { + return Via.getPlatform().getPluginVersion(); + } + + @Override + public void sendRawPacket(UUID uuid, ByteBuf packet) throws IllegalArgumentException { + if (!isPorted(uuid)) throw new IllegalArgumentException("This player is not controlled by ViaVersion!"); + UserConnection ci = getPortedPlayers().get(uuid); + ci.sendRawPacket(packet); + } + + @Override + public void sendRawPacket(Player player, ByteBuf packet) throws IllegalArgumentException { + sendRawPacket(player.getUniqueId(), packet); + } + + @Override + public BossBar createBossBar(String title, BossColor color, BossStyle style) { + return new SpongeBossBar(title, 1F, color, style); + } + + @Override + public BossBar createBossBar(String title, float health, BossColor color, BossStyle style) { + return new SpongeBossBar(title, health, color, style); + } + + @Override + public SortedSet getSupportedVersions() { + SortedSet outputSet = new TreeSet<>(ProtocolRegistry.getSupportedVersions()); + outputSet.removeAll(Via.getPlatform().getConf().getBlockedProtocols()); + + return outputSet; + } + + public Map getPortedPlayers() { + return Via.getManager().getPortedPlayers(); + } +} diff --git a/sponge/src/main/java/us/myles/ViaVersion/sponge/SpongeViaInjector.java b/sponge/src/main/java/us/myles/ViaVersion/sponge/SpongeViaInjector.java new file mode 100644 index 000000000..e704cc356 --- /dev/null +++ b/sponge/src/main/java/us/myles/ViaVersion/sponge/SpongeViaInjector.java @@ -0,0 +1,197 @@ +package us.myles.ViaVersion.sponge; + +import io.netty.channel.ChannelFuture; +import io.netty.channel.ChannelHandler; +import io.netty.channel.ChannelInitializer; +import io.netty.channel.socket.SocketChannel; +import us.myles.ViaVersion.api.Pair; +import us.myles.ViaVersion.api.Via; +import us.myles.ViaVersion.api.platform.ViaInjector; +import us.myles.ViaVersion.sponge.handlers.ViaVersionInitializer; +import us.myles.ViaVersion.sponge.util.ReflectionUtil; +import us.myles.ViaVersion.util.ListWrapper; + +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.util.ArrayList; +import java.util.List; + +public class SpongeViaInjector implements ViaInjector { + private List injectedFutures = new ArrayList<>(); + private List> injectedLists = new ArrayList<>(); + + @Override + public void inject() throws Exception { + try { + Object connection = getServerConnection(); + if (connection == null) { + throw new Exception("We failed to find the core component 'ServerConnection', please file an issue on our GitHub."); + } + for (Field field : connection.getClass().getDeclaredFields()) { + field.setAccessible(true); + final Object value = field.get(connection); + if (value instanceof List) { + // Inject the list + List wrapper = new ListWrapper((List) value) { + @Override + public synchronized void handleAdd(Object o) { + synchronized (this) { + if (o instanceof ChannelFuture) { + try { + injectChannelFuture((ChannelFuture) o); + } catch (Exception e) { + e.printStackTrace(); + } + } + } + } + }; + injectedLists.add(new Pair<>(field, connection)); + field.set(connection, wrapper); + // Iterate through current list + synchronized (wrapper) { + for (Object o : (List) value) { + if (o instanceof ChannelFuture) { + injectChannelFuture((ChannelFuture) o); + } else { + break; // not the right list. + } + } + } + } + } + + } catch (Exception e) { + Via.getPlatform().getLogger().severe("Unable to inject ViaVersion, please post these details on our GitHub and ensure you're using a compatible server version."); + throw e; + } + } + + private void injectChannelFuture(ChannelFuture future) throws Exception { + try { + ChannelHandler bootstrapAcceptor = future.channel().pipeline().first(); + try { + ChannelInitializer oldInit = ReflectionUtil.get(bootstrapAcceptor, "childHandler", ChannelInitializer.class); + ChannelInitializer newInit = new ViaVersionInitializer(oldInit); + + ReflectionUtil.set(bootstrapAcceptor, "childHandler", newInit); + injectedFutures.add(future); + } catch (NoSuchFieldException e) { + throw new Exception("Unable to find core component 'childHandler', please check your plugins. issue: " + bootstrapAcceptor.getClass().getName()); + + } + } catch (Exception e) { + Via.getPlatform().getLogger().severe("We failed to inject ViaVersion, have you got late-bind enabled with something else?"); + throw e; + } + } + + @Override + public void uninject() { + // TODO: Uninject from players currently online + for (ChannelFuture future : injectedFutures) { + ChannelHandler bootstrapAcceptor = future.channel().pipeline().first(); + try { + ChannelInitializer oldInit = ReflectionUtil.get(bootstrapAcceptor, "childHandler", ChannelInitializer.class); + if (oldInit instanceof ViaVersionInitializer) { + ReflectionUtil.set(bootstrapAcceptor, "childHandler", ((ViaVersionInitializer) oldInit).getOriginal()); + } + } catch (Exception e) { + System.out.println("Failed to remove injection handler, reload won't work with connections, please reboot!"); + } + } + injectedFutures.clear(); + + for (Pair pair : injectedLists) { + try { + Object o = pair.getKey().get(pair.getValue()); + if (o instanceof ListWrapper) { + pair.getKey().set(pair.getValue(), ((ListWrapper) o).getOriginalList()); + } + } catch (IllegalAccessException e) { + System.out.println("Failed to remove injection, reload won't work with connections, please reboot!"); + } + } + + injectedLists.clear(); + } + + public static Object getServer() throws Exception { + Class serverClazz = Class.forName("net.minecraft.server.MinecraftServer"); + for (Method m : serverClazz.getDeclaredMethods()) { + if (m.getParameterCount() == 0) { + if ((m.getModifiers() & Modifier.STATIC) == Modifier.STATIC) { + if (m.getReturnType().equals(serverClazz)) { + return m.invoke(null); + } + } + } + } + throw new Exception("Could not find MinecraftServer static field!"); + } + + @Override + public int getServerProtocolVersion() throws Exception { + try { + Class serverClazz = Class.forName("net.minecraft.server.MinecraftServer"); + Object server = getServer(); + Class pingClazz = Class.forName("net.minecraft.network.ServerStatusResponse"); + Object ping = null; + // Search for ping method + for (Field f : serverClazz.getDeclaredFields()) { + if (f.getType() != null) { + if (f.getType().getSimpleName().equals("ServerStatusResponse")) { + f.setAccessible(true); + ping = f.get(server); + } + } + } + if (ping != null) { + Object serverData = null; + for (Field f : pingClazz.getDeclaredFields()) { + if (f.getType() != null) { + if (f.getType().getSimpleName().endsWith("MinecraftProtocolVersionIdentifier")) { + f.setAccessible(true); + serverData = f.get(ping); + } + } + } + if (serverData != null) { + int protocolVersion = -1; + for (Field f : serverData.getClass().getDeclaredFields()) { + if (f.getType() != null) { + if (f.getType() == int.class) { + f.setAccessible(true); + protocolVersion = (int) f.get(serverData); + } + } + } + if (protocolVersion != -1) { + return protocolVersion; + } + } + } + } catch (Exception e) { + throw new Exception("Failed to get server", e); + } + throw new Exception("Failed to get server"); + } + + public static Object getServerConnection() throws Exception { + Class serverClazz = Class.forName("net.minecraft.server.MinecraftServer"); + Object server = getServer(); + Object connection = null; + for (Method m : serverClazz.getDeclaredMethods()) { + if (m.getReturnType() != null) { + if (m.getReturnType().getSimpleName().equals("NetworkSystem")) { + if (m.getParameterTypes().length == 0) { + connection = m.invoke(server); + } + } + } + } + return connection; + } + +} diff --git a/sponge/src/main/java/us/myles/ViaVersion/sponge/SpongeViaLoader.java b/sponge/src/main/java/us/myles/ViaVersion/sponge/SpongeViaLoader.java new file mode 100644 index 000000000..b11eb5948 --- /dev/null +++ b/sponge/src/main/java/us/myles/ViaVersion/sponge/SpongeViaLoader.java @@ -0,0 +1,72 @@ +package us.myles.ViaVersion.sponge; + +import lombok.AllArgsConstructor; +import org.spongepowered.api.Sponge; +import us.myles.ViaVersion.SpongePlugin; +import us.myles.ViaVersion.api.Via; +import us.myles.ViaVersion.api.platform.ViaPlatformLoader; +import us.myles.ViaVersion.protocols.protocol1_9to1_8.providers.BulkChunkTranslatorProvider; +import us.myles.ViaVersion.protocols.protocol1_9to1_8.providers.MovementTransmitterProvider; +import us.myles.ViaVersion.sponge.listeners.ClientLeaveListener; +import us.myles.ViaVersion.sponge.listeners.UpdateListener; +import us.myles.ViaVersion.sponge.providers.SpongeViaBulkChunkTranslator; +import us.myles.ViaVersion.sponge.providers.SpongeViaMovementTransmitter; + +@AllArgsConstructor +public class SpongeViaLoader implements ViaPlatformLoader { + private SpongePlugin plugin; + + @Override + public void load() { + // Update Listener + Sponge.getEventManager().registerListeners(plugin, new UpdateListener()); +// + /* Base Protocol */ + Sponge.getEventManager().registerListeners(plugin, new ClientLeaveListener()); +// /* 1.9 client to 1.8 server */ +// +// new ArmorListener(plugin).register(); +// new CommandBlockListener(plugin).register(); +// new DeathListener(plugin).register(); +// new BlockListener(plugin).register(); +// +// if (Bukkit.getVersion().toLowerCase().contains("paper") || Bukkit.getVersion().toLowerCase().contains("taco")) { +// plugin.getLogger().info("Enabling PaperSpigot/TacoSpigot patch: Fixes block placement."); +// new PaperPatch(plugin).register(); +// } +// if (plugin.getConf().isItemCache()) { +// new HandItemCache().runTaskTimerAsynchronously(plugin, 2L, 2L); // Updates player's items :) +// HandItemCache.CACHE = true; +// } +// +// /* Providers */ + Via.getManager().getProviders().use(BulkChunkTranslatorProvider.class, new SpongeViaBulkChunkTranslator()); + Via.getManager().getProviders().use(MovementTransmitterProvider.class, new SpongeViaMovementTransmitter()); +// Via.getManager().getProviders().use(HandItemProvider.class, new HandItemProvider() { +// @Override +// public Item getHandItem(final UserConnection info) { +// if (HandItemCache.CACHE) { +// return HandItemCache.getHandItem(info.get(ProtocolInfo.class).getUuid()); +// } else { +// try { +// return Bukkit.getScheduler().callSyncMethod(Bukkit.getPluginManager().getPlugin("ViaVersion"), new Callable() { +// @Override +// public Item call() throws Exception { +// UUID playerUUID = info.get(ProtocolInfo.class).getUuid(); +// if (Bukkit.getPlayer(playerUUID) != null) { +// return HandItemCache.convert(Bukkit.getPlayer(playerUUID).getItemInHand()); +// } +// return null; +// } +// }).get(10, TimeUnit.SECONDS); +// } catch (Exception e) { +// System.out.println("Error fetching hand item: " + e.getClass().getName()); +// if (Via.getManager().isDebug()) +// e.printStackTrace(); +// return null; +// } +// } +// } +// }); + } +} diff --git a/sponge/src/main/java/us/myles/ViaVersion/sponge/handlers/ViaDecodeHandler.java b/sponge/src/main/java/us/myles/ViaVersion/sponge/handlers/ViaDecodeHandler.java new file mode 100644 index 000000000..b2bce7c0b --- /dev/null +++ b/sponge/src/main/java/us/myles/ViaVersion/sponge/handlers/ViaDecodeHandler.java @@ -0,0 +1,90 @@ +package us.myles.ViaVersion.sponge.handlers; + +import io.netty.buffer.ByteBuf; +import io.netty.channel.ChannelHandlerContext; +import io.netty.handler.codec.ByteToMessageDecoder; +import us.myles.ViaVersion.api.PacketWrapper; +import us.myles.ViaVersion.api.data.UserConnection; +import us.myles.ViaVersion.api.type.Type; +import us.myles.ViaVersion.exception.CancelException; +import us.myles.ViaVersion.packets.Direction; +import us.myles.ViaVersion.protocols.base.ProtocolInfo; +import us.myles.ViaVersion.util.PipelineUtil; + +import java.lang.reflect.InvocationTargetException; +import java.util.List; + +public class ViaDecodeHandler extends ByteToMessageDecoder { + + private final ByteToMessageDecoder minecraftDecoder; + private final UserConnection info; + + public ViaDecodeHandler(UserConnection info, ByteToMessageDecoder minecraftDecoder) { + this.info = info; + this.minecraftDecoder = minecraftDecoder; + } + + @Override + protected void decode(ChannelHandlerContext ctx, ByteBuf bytebuf, List list) throws Exception { + // use transformers + if (bytebuf.readableBytes() > 0) { + // Ignore if pending disconnect + if (info.isPendingDisconnect()) { + return; + } + // Increment received + boolean second = info.incrementReceived(); + // Check PPS + // TODO implement pps +// if (second) { +// if (((ViaVersionPlugin) Via.getPlatform()).handlePPS(info)) +// return; +// } + + if (info.isActive()) { + // Handle ID + int id = Type.VAR_INT.read(bytebuf); + // Transform + ByteBuf newPacket = ctx.alloc().buffer(); + try { + if (id == PacketWrapper.PASSTHROUGH_ID) { + newPacket.writeBytes(bytebuf); + } else { + PacketWrapper wrapper = new PacketWrapper(id, bytebuf, info); + ProtocolInfo protInfo = info.get(ProtocolInfo.class); + protInfo.getPipeline().transform(Direction.INCOMING, protInfo.getState(), wrapper); + wrapper.writeToBuffer(newPacket); + } + + bytebuf.clear(); + bytebuf = newPacket; + } catch (Exception e) { + // Clear Buffer + bytebuf.clear(); + // Release Packet, be free! + newPacket.release(); + throw e; + } + } + + // call minecraft decoder + try { + list.addAll(PipelineUtil.callDecode(this.minecraftDecoder, ctx, bytebuf)); + } catch (InvocationTargetException e) { + if (e.getCause() instanceof Exception) { + throw (Exception) e.getCause(); + } + } finally { + if (info.isActive()) { + bytebuf.release(); + } + } + } + } + + @Override + public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { + if (PipelineUtil.containsCause(cause, CancelException.class)) return; + super.exceptionCaught(ctx, cause); + } +} diff --git a/sponge/src/main/java/us/myles/ViaVersion/sponge/handlers/ViaEncodeHandler.java b/sponge/src/main/java/us/myles/ViaVersion/sponge/handlers/ViaEncodeHandler.java new file mode 100644 index 000000000..ce6faef49 --- /dev/null +++ b/sponge/src/main/java/us/myles/ViaVersion/sponge/handlers/ViaEncodeHandler.java @@ -0,0 +1,72 @@ +package us.myles.ViaVersion.sponge.handlers; + +import io.netty.buffer.ByteBuf; +import io.netty.channel.ChannelHandlerContext; +import io.netty.handler.codec.MessageToByteEncoder; +import us.myles.ViaVersion.api.PacketWrapper; +import us.myles.ViaVersion.api.Via; +import us.myles.ViaVersion.api.data.UserConnection; +import us.myles.ViaVersion.api.type.Type; +import us.myles.ViaVersion.exception.CancelException; +import us.myles.ViaVersion.packets.Direction; +import us.myles.ViaVersion.protocols.base.ProtocolInfo; +import us.myles.ViaVersion.util.PipelineUtil; + +import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; + +public class ViaEncodeHandler extends MessageToByteEncoder { + private final UserConnection info; + private final MessageToByteEncoder minecraftEncoder; + + public ViaEncodeHandler(UserConnection info, MessageToByteEncoder minecraftEncoder) { + this.info = info; + this.minecraftEncoder = minecraftEncoder; + } + + + @Override + protected void encode(final ChannelHandlerContext ctx, Object o, final ByteBuf bytebuf) throws Exception { + // handle the packet type + if (!(o instanceof ByteBuf)) { + // call minecraft encoder + try { + PipelineUtil.callEncode(this.minecraftEncoder, ctx, o, bytebuf); + } catch (InvocationTargetException e) { + if (e.getCause() instanceof Exception) { + throw (Exception) e.getCause(); + } + } + } + if (bytebuf.readableBytes() == 0) { + throw new CancelException(); + } + // Increment sent + info.incrementSent(); + if (info.isActive()) { + // Handle ID + int id = Type.VAR_INT.read(bytebuf); + // Transform + ByteBuf oldPacket = bytebuf.copy(); + bytebuf.clear(); + + try { + PacketWrapper wrapper = new PacketWrapper(id, oldPacket, info); + ProtocolInfo protInfo = info.get(ProtocolInfo.class); + protInfo.getPipeline().transform(Direction.OUTGOING, protInfo.getState(), wrapper); + wrapper.writeToBuffer(bytebuf); + } catch (Exception e) { + bytebuf.clear(); + throw e; + } finally { + oldPacket.release(); + } + } + } + + @Override + public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { + if (PipelineUtil.containsCause(cause, CancelException.class)) return; + super.exceptionCaught(ctx, cause); + } +} diff --git a/sponge/src/main/java/us/myles/ViaVersion/sponge/handlers/ViaPacketHandler.java b/sponge/src/main/java/us/myles/ViaVersion/sponge/handlers/ViaPacketHandler.java new file mode 100644 index 000000000..07e4deb03 --- /dev/null +++ b/sponge/src/main/java/us/myles/ViaVersion/sponge/handlers/ViaPacketHandler.java @@ -0,0 +1,35 @@ +package us.myles.ViaVersion.sponge.handlers; + +import io.netty.buffer.ByteBuf; +import io.netty.channel.ChannelHandlerContext; +import io.netty.handler.codec.MessageToMessageEncoder; +import us.myles.ViaVersion.api.data.UserConnection; +import us.myles.ViaVersion.protocols.base.ProtocolInfo; + +import java.util.List; + +public class ViaPacketHandler extends MessageToMessageEncoder { + private final UserConnection info; + + public ViaPacketHandler(UserConnection info) { + this.info = info; + } + + @Override + protected void encode(ChannelHandlerContext ctx, Object o, List list) throws Exception { + // Split chunks bulk packet up in to single chunks packets before it reached the encoder. + // This will prevent issues with several plugins and other protocol handlers due to the chunks being sent twice. + // It also sends the chunks in the right order possible resolving some issues with added chunks/block/entity data. + if (!(o instanceof ByteBuf)) { + info.setLastPacket(o); + /* This transformer is more for fixing issues which we find hard at packet level :) */ + if (info.isActive()) { + if (info.get(ProtocolInfo.class).getPipeline().filter(o, list)) { + return; + } + } + } + + list.add(o); + } +} diff --git a/sponge/src/main/java/us/myles/ViaVersion/sponge/handlers/ViaVersionInitializer.java b/sponge/src/main/java/us/myles/ViaVersion/sponge/handlers/ViaVersionInitializer.java new file mode 100644 index 000000000..2a29c110b --- /dev/null +++ b/sponge/src/main/java/us/myles/ViaVersion/sponge/handlers/ViaVersionInitializer.java @@ -0,0 +1,48 @@ +package us.myles.ViaVersion.sponge.handlers; + +import io.netty.channel.Channel; +import io.netty.channel.ChannelInitializer; +import io.netty.channel.socket.SocketChannel; +import io.netty.handler.codec.ByteToMessageDecoder; +import io.netty.handler.codec.MessageToByteEncoder; +import us.myles.ViaVersion.api.data.UserConnection; +import us.myles.ViaVersion.api.protocol.ProtocolPipeline; + +import java.lang.reflect.Method; + +public class ViaVersionInitializer extends ChannelInitializer { + + private final ChannelInitializer original; + private Method method; + + public ViaVersionInitializer(ChannelInitializer oldInit) { + this.original = oldInit; + try { + this.method = ChannelInitializer.class.getDeclaredMethod("initChannel", Channel.class); + this.method.setAccessible(true); + } catch (NoSuchMethodException e) { + e.printStackTrace(); + } + } + + public ChannelInitializer getOriginal() { + return original; + } + + @Override + protected void initChannel(SocketChannel socketChannel) throws Exception { + UserConnection info = new UserConnection(socketChannel); + // init protocol + new ProtocolPipeline(info); + // Add originals + this.method.invoke(this.original, socketChannel); + // Add our transformers + MessageToByteEncoder encoder = new ViaEncodeHandler(info, (MessageToByteEncoder) socketChannel.pipeline().get("encoder")); + ByteToMessageDecoder decoder = new ViaDecodeHandler(info, (ByteToMessageDecoder) socketChannel.pipeline().get("decoder")); + ViaPacketHandler chunkHandler = new ViaPacketHandler(info); + + socketChannel.pipeline().replace("encoder", "encoder", encoder); + socketChannel.pipeline().replace("decoder", "decoder", decoder); + socketChannel.pipeline().addAfter("packet_handler", "viaversion_packet_handler", chunkHandler); + } +} diff --git a/sponge/src/main/java/us/myles/ViaVersion/sponge/listeners/ClientLeaveListener.java b/sponge/src/main/java/us/myles/ViaVersion/sponge/listeners/ClientLeaveListener.java new file mode 100644 index 000000000..20ef79688 --- /dev/null +++ b/sponge/src/main/java/us/myles/ViaVersion/sponge/listeners/ClientLeaveListener.java @@ -0,0 +1,12 @@ +package us.myles.ViaVersion.sponge.listeners; + +import org.spongepowered.api.event.Listener; +import org.spongepowered.api.event.network.ClientConnectionEvent; +import us.myles.ViaVersion.api.Via; + +public class ClientLeaveListener { + @Listener + public void onDisconnect(ClientConnectionEvent.Disconnect disconnect) { + Via.getManager().removePortedClient(disconnect.getTargetEntity().getUniqueId()); + } +} diff --git a/sponge/src/main/java/us/myles/ViaVersion/sponge/listeners/UpdateListener.java b/sponge/src/main/java/us/myles/ViaVersion/sponge/listeners/UpdateListener.java new file mode 100644 index 000000000..010570a63 --- /dev/null +++ b/sponge/src/main/java/us/myles/ViaVersion/sponge/listeners/UpdateListener.java @@ -0,0 +1,16 @@ +package us.myles.ViaVersion.sponge.listeners; + +import org.spongepowered.api.event.Listener; +import org.spongepowered.api.event.network.ClientConnectionEvent; +import us.myles.ViaVersion.api.Via; +import us.myles.ViaVersion.update.UpdateUtil; + +public class UpdateListener { + @Listener + public void onJoin(ClientConnectionEvent.Join join) { + if (join.getTargetEntity().hasPermission("viaversion.update") + && Via.getConfig().isCheckForUpdates()) { + UpdateUtil.sendUpdateMessage(join.getTargetEntity().getUniqueId()); + } + } +} diff --git a/sponge/src/main/java/us/myles/ViaVersion/sponge/providers/SpongeViaBulkChunkTranslator.java b/sponge/src/main/java/us/myles/ViaVersion/sponge/providers/SpongeViaBulkChunkTranslator.java new file mode 100644 index 000000000..f10b3597d --- /dev/null +++ b/sponge/src/main/java/us/myles/ViaVersion/sponge/providers/SpongeViaBulkChunkTranslator.java @@ -0,0 +1,55 @@ +package us.myles.ViaVersion.sponge.providers; + +import com.google.common.collect.Lists; +import us.myles.ViaVersion.api.Via; +import us.myles.ViaVersion.protocols.protocol1_9to1_8.providers.BulkChunkTranslatorProvider; +import us.myles.ViaVersion.protocols.protocol1_9to1_8.storage.ClientChunks; +import us.myles.ViaVersion.sponge.util.ReflectionUtil; + +import java.util.List; +import java.util.logging.Level; + +public class SpongeViaBulkChunkTranslator extends BulkChunkTranslatorProvider { + // Reflection + private static ReflectionUtil.ClassReflection mapChunkBulkRef; + private static ReflectionUtil.ClassReflection mapChunkRef; + + static { + try { + mapChunkBulkRef = new ReflectionUtil.ClassReflection(Class.forName("net.minecraft.network.play.server.S26PacketMapChunkBulk")); + mapChunkRef = new ReflectionUtil.ClassReflection(Class.forName("net.minecraft.network.play.server.S21PacketChunkData")); + } catch (Exception e) { + Via.getPlatform().getLogger().log(Level.WARNING, "Failed to initialise chunks reflection", e); + } + } + + @Override + public List transformMapChunkBulk(Object packet, ClientChunks clientChunks) { + List list = Lists.newArrayList(); + try { + int[] xcoords = mapChunkBulkRef.getFieldValue("field_149266_a", packet, int[].class); + int[] zcoords = mapChunkBulkRef.getFieldValue("field_149264_b", packet, int[].class); + Object[] chunkMaps = mapChunkBulkRef.getFieldValue("field_179755_c", packet, Object[].class); + for (int i = 0; i < chunkMaps.length; i++) { + int x = xcoords[i]; + int z = zcoords[i]; + Object chunkMap = chunkMaps[i]; + Object chunkPacket = mapChunkRef.newInstance(); + mapChunkRef.setFieldValue("field_149284_a", chunkPacket, x); + mapChunkRef.setFieldValue("field_149282_b", chunkPacket, z); + mapChunkRef.setFieldValue("field_179758_c", chunkPacket, chunkMap); + mapChunkRef.setFieldValue("field_149279_g", chunkPacket, true); // Chunk bulk chunks are always ground-up + clientChunks.getBulkChunks().add(ClientChunks.toLong(x, z)); // Store for later + list.add(chunkPacket); + } + } catch (Exception e) { + Via.getPlatform().getLogger().log(Level.WARNING, "Failed to transform chunks bulk", e); + } + return list; + } + + @Override + public boolean isFiltered(Class packetClass) { + return packetClass.getName().endsWith("S26PacketMapChunkBulk"); + } +} diff --git a/sponge/src/main/java/us/myles/ViaVersion/sponge/providers/SpongeViaMovementTransmitter.java b/sponge/src/main/java/us/myles/ViaVersion/sponge/providers/SpongeViaMovementTransmitter.java new file mode 100644 index 000000000..fb469a259 --- /dev/null +++ b/sponge/src/main/java/us/myles/ViaVersion/sponge/providers/SpongeViaMovementTransmitter.java @@ -0,0 +1,41 @@ +package us.myles.ViaVersion.sponge.providers; + +import us.myles.ViaVersion.protocols.protocol1_9to1_8.providers.MovementTransmitterProvider; + +import java.lang.reflect.Field; + +public class SpongeViaMovementTransmitter extends MovementTransmitterProvider { + // Used for packet mode + private Object idlePacket; + private Object idlePacket2; + + public SpongeViaMovementTransmitter() { + Class idlePacketClass; + try { + idlePacketClass = Class.forName("net.minecraft.network.play.client.C03PacketPlayer"); + } catch (ClassNotFoundException e) { + throw new RuntimeException("Couldn't find idle packet, help!", e); + } + try { + idlePacket = idlePacketClass.newInstance(); + idlePacket2 = idlePacketClass.newInstance(); + + Field flying = idlePacketClass.getDeclaredField("field_149474_g"); + flying.setAccessible(true); + + flying.set(idlePacket2, true); + } catch (NoSuchFieldException | InstantiationException | IllegalArgumentException | IllegalAccessException e) { + throw new RuntimeException("Couldn't make player idle packet, help!", e); + } + } + + @Override + public Object getFlyingPacket() { + return idlePacket2; + } + + @Override + public Object getGroundPacket() { + return idlePacket; + } +} diff --git a/sponge/src/main/java/us/myles/ViaVersion/sponge/util/ReflectionUtil.java b/sponge/src/main/java/us/myles/ViaVersion/sponge/util/ReflectionUtil.java new file mode 100644 index 000000000..cb025a014 --- /dev/null +++ b/sponge/src/main/java/us/myles/ViaVersion/sponge/util/ReflectionUtil.java @@ -0,0 +1,130 @@ +package us.myles.ViaVersion.sponge.util; + +import com.google.common.collect.Maps; + +import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.Collection; +import java.util.Collections; +import java.util.Map; + +public class ReflectionUtil { + + public static Object invokeStatic(Class clazz, String method) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException { + Method m = clazz.getDeclaredMethod(method); + return m.invoke(null); + } + + public static Object invoke(Object o, String method) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException { + Method m = o.getClass().getDeclaredMethod(method); + return m.invoke(o); + } + + public static T getStatic(Class clazz, String f, Class t) throws NoSuchFieldException, IllegalAccessException { + Field field = clazz.getDeclaredField(f); + field.setAccessible(true); + return (T) field.get(null); + } + + public static T getSuper(Object o, String f, Class t) throws NoSuchFieldException, IllegalAccessException { + Field field = o.getClass().getSuperclass().getDeclaredField(f); + field.setAccessible(true); + return (T) field.get(o); + } + + public static T get(Object instance, Class clazz, String f, Class t) throws NoSuchFieldException, IllegalAccessException { + Field field = clazz.getDeclaredField(f); + field.setAccessible(true); + return (T) field.get(instance); + } + + public static T get(Object o, String f, Class t) throws NoSuchFieldException, IllegalAccessException { + Field field = o.getClass().getDeclaredField(f); + field.setAccessible(true); + return (T) field.get(o); + } + + public static T getPublic(Object o, String f, Class t) throws NoSuchFieldException, IllegalAccessException { + Field field = o.getClass().getField(f); + field.setAccessible(true); + return (T) field.get(o); + } + + + public static void set(Object o, String f, Object value) throws NoSuchFieldException, IllegalAccessException { + Field field = o.getClass().getDeclaredField(f); + field.setAccessible(true); + field.set(o, value); + } + + public static final class ClassReflection { + private final Class handle; + private final Map fields = Maps.newConcurrentMap(); + private final Map methods = Maps.newConcurrentMap(); + + public ClassReflection(Class handle) { + this(handle, true); + } + + public ClassReflection(Class handle, boolean recursive) { + this.handle = handle; + scanFields(handle, recursive); + scanMethods(handle, recursive); + } + + private void scanFields(Class host, boolean recursive) { + if (host.getSuperclass() != null && recursive) { + scanFields(host.getSuperclass(), true); + } + + for (Field field : host.getDeclaredFields()) { + field.setAccessible(true); + fields.put(field.getName(), field); + } + } + + private void scanMethods(Class host, boolean recursive) { + if (host.getSuperclass() != null && recursive) { + scanMethods(host.getSuperclass(), true); + } + + for (Method method : host.getDeclaredMethods()) { + method.setAccessible(true); + methods.put(method.getName(), method); + } + } + + public Object newInstance() throws IllegalAccessException, InstantiationException { + return handle.newInstance(); + } + + public Field getField(String name) { + return fields.get(name); + } + + public void setFieldValue(String fieldName, Object instance, Object value) throws IllegalAccessException { + getField(fieldName).set(instance, value); + } + + public T getFieldValue(String fieldName, Object instance, Class type) throws IllegalAccessException { + return type.cast(getField(fieldName).get(instance)); + } + + public T invokeMethod(Class type, String methodName, Object instance, Object... args) throws InvocationTargetException, IllegalAccessException { + return type.cast(getMethod(methodName).invoke(instance, args)); + } + + public Method getMethod(String name) { + return methods.get(name); + } + + public Collection getFields() { + return Collections.unmodifiableCollection(fields.values()); + } + + public Collection getMethods() { + return Collections.unmodifiableCollection(methods.values()); + } + } +}