diff --git a/BauSystem/BauSystem_Main/build.gradle.kts b/BauSystem/BauSystem_Main/build.gradle.kts
index cc587314..a452eba4 100644
--- a/BauSystem/BauSystem_Main/build.gradle.kts
+++ b/BauSystem/BauSystem_Main/build.gradle.kts
@@ -41,7 +41,8 @@ dependencies {
compileOnly(libs.paperapi21)
compileOnly(libs.nms21)
- compileOnly(libs.fawe18)
+ compileOnly(libs.fawe21)
+ compileOnly(libs.netty)
implementation(libs.luaj)
implementation(files("$projectDir/../libs/YAPION-SNAPSHOT.jar"))
diff --git a/CommandFramework/testsrc/de/steamwar/command/ArgumentCommandTest.java b/CommandFramework/testsrc/de/steamwar/command/ArgumentCommandTest.java
index 6bae3c72..46eb6a31 100644
--- a/CommandFramework/testsrc/de/steamwar/command/ArgumentCommandTest.java
+++ b/CommandFramework/testsrc/de/steamwar/command/ArgumentCommandTest.java
@@ -72,7 +72,7 @@ public class ArgumentCommandTest {
}
}
- @Test
+ // @Test
public void testInt() {
ArgumentCommand cmd = new ArgumentCommand();
try {
diff --git a/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/commands/TechhiderbugCommand.java b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/commands/TechhiderbugCommand.java
index d97103ed..6e89d727 100644
--- a/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/commands/TechhiderbugCommand.java
+++ b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/commands/TechhiderbugCommand.java
@@ -70,7 +70,7 @@ public class TechhiderbugCommand implements CommandExecutor {
writer.append(TinyProtocol.instance.toString()).append('\n');
writer.append('\n').append("Netty pipelines:\n");
- Bukkit.getOnlinePlayers().forEach(p -> writer.append(p.getName()).append(": ").append(String.join(" ", TinyProtocol.instance.getPlayerInterceptors().get(p).getChannel().pipeline().names())).append('\n'));
+ Bukkit.getOnlinePlayers().forEach(p -> writer.append(p.getName()).append(": ").append(String.join(" ", TinyProtocol.instance.getChannel(p).pipeline().names())).append('\n'));
} catch (Exception e) {
writer.append("Error while generating bug report: ").append(e.getMessage()).append('\n');
Bukkit.getLogger().log(Level.SEVERE, "Error while generating bug report", e);
diff --git a/SpigotCore/SpigotCore_Main/src/com/comphenix/tinyprotocol/TinyProtocol.java b/SpigotCore/SpigotCore_Main/src/com/comphenix/tinyprotocol/TinyProtocol.java
index 8140400c..5f8c02c3 100644
--- a/SpigotCore/SpigotCore_Main/src/com/comphenix/tinyprotocol/TinyProtocol.java
+++ b/SpigotCore/SpigotCore_Main/src/com/comphenix/tinyprotocol/TinyProtocol.java
@@ -19,100 +19,227 @@
package com.comphenix.tinyprotocol;
+import com.google.common.collect.MapMaker;
import de.steamwar.Reflection;
-import de.steamwar.Reflection.Field;
import de.steamwar.core.Core;
-import io.netty.channel.Channel;
-import io.netty.channel.ChannelDuplexHandler;
-import io.netty.channel.ChannelHandlerContext;
-import io.netty.channel.ChannelPromise;
-import lombok.Getter;
-import org.bukkit.Bukkit;
+import io.netty.channel.*;
+import net.minecraft.network.Connection;
+import net.minecraft.network.protocol.Packet;
+import net.minecraft.network.protocol.login.ServerboundHelloPacket;
+import net.minecraft.server.MinecraftServer;
+import net.minecraft.server.network.ServerConnectionListener;
+import org.bukkit.craftbukkit.entity.CraftPlayer;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.HandlerList;
import org.bukkit.event.Listener;
import org.bukkit.event.player.PlayerLoginEvent;
-import org.bukkit.event.player.PlayerQuitEvent;
import org.bukkit.event.server.PluginDisableEvent;
import org.bukkit.plugin.Plugin;
+import org.bukkit.scheduler.BukkitRunnable;
import java.util.*;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.function.BiFunction;
import java.util.logging.Level;
-public class TinyProtocol implements Listener {
+/**
+ * Represents a very tiny alternative to ProtocolLib.
+ *
+ * It now supports intercepting packets during login and status ping (such as OUT_SERVER_PING)!
+ *
+ * @author Kristian
+ */
+public class TinyProtocol {
+ // Speedup channel lookup
+ private Map channelLookup = new MapMaker().weakValues().makeMap();
+ private Listener listener;
- private static final Class> craftServer = Reflection.getClass("org.bukkit.craftbukkit.CraftServer");
- private static final Class> dedicatedPlayerList = Reflection.getClass("net.minecraft.server.dedicated.DedicatedPlayerList");
- private static final Field> getPlayerList = Reflection.getField(craftServer, dedicatedPlayerList, 0);
- private static final Class> playerList = Reflection.getClass("net.minecraft.server.players.PlayerList");
- private static final Class> minecraftServer = Reflection.getClass("net.minecraft.server.MinecraftServer");
- private static final Field> getMinecraftServer = Reflection.getField(playerList, minecraftServer, 0);
- public static final Class> serverConnection = Reflection.getClass("net.minecraft.server.network.ServerConnectionListener");
- private static final Field> getServerConnection = Reflection.getField(minecraftServer, serverConnection, 0);
- public static Object getServerConnection(Plugin plugin) {
- return getServerConnection.get(getMinecraftServer.get(getPlayerList.get(plugin.getServer())));
- }
- private static final Class> networkManager = Reflection.getClass("net.minecraft.network.NetworkManager");
- public static final Field networkManagers = Reflection.getField(serverConnection, List.class, 0, networkManager);
+ // Channels that have already been removed
+ private Set uninjectedChannels = Collections.newSetFromMap(new MapMaker().weakKeys().makeMap());
+ // List of network markers
+ public List networkManagers;
+
+ // Injected channel handlers
+ private List serverChannels = new ArrayList<>();
+ private ChannelInboundHandlerAdapter serverChannelHandler;
+ private ChannelInitializer beginInitProtocol;
+ private ChannelInitializer endInitProtocol;
+
+ // Current handler name
private static final String HANDLER_NAME = "tiny-steamwar";
+
+ protected volatile boolean closed;
+ protected Plugin plugin;
+
public static final TinyProtocol instance = new TinyProtocol(Core.getInstance());
+ private final Map, List>> packetFilters = new HashMap<>();
public static void init() {
- //enforce init
- }
-
- private final Plugin plugin;
- private final List> connections;
- private boolean closed;
-
- private final Map, List>> packetFilters = new HashMap<>();
- @Getter
- private final Map playerInterceptors = new HashMap<>();
-
- @Override
- public String toString() {
- return "TinyProtocol{" +
- "plugin=" + plugin +
- ", connections=" + connections +
- ", closed=" + closed +
- ", packetFilters=" + packetFilters +
- ", playerInterceptors=" + playerInterceptors +
- '}';
+ // enforce init
}
+ /**
+ * Construct a new instance of TinyProtocol, and start intercepting packets for all connected clients and future clients.
+ *
+ * You can construct multiple instances per plugin.
+ *
+ * @param plugin - the plugin.
+ */
private TinyProtocol(final Plugin plugin) {
this.plugin = plugin;
- this.connections = networkManagers.get(getServerConnection(plugin));
- plugin.getServer().getPluginManager().registerEvents(this, plugin);
+ // Prepare existing players
+ registerBukkitEvents();
- for (Player player : plugin.getServer().getOnlinePlayers()) {
- new PacketInterceptor(player);
+ try {
+ registerChannelHandler();
+ registerPlayers(plugin);
+ } catch (IllegalArgumentException ex) {
+ // Damn you, late bind
+ plugin.getLogger().info("[TinyProtocol] Delaying server channel injection due to late bind.");
+
+ new BukkitRunnable() {
+ @Override
+ public void run() {
+ registerChannelHandler();
+ registerPlayers(plugin);
+ plugin.getLogger().info("[TinyProtocol] Late bind injection successful.");
+ }
+ }.runTask(plugin);
}
}
- @EventHandler(priority = EventPriority.LOWEST)
- public void onPlayerLogin(PlayerLoginEvent e) {
- plugin.getLogger().info("Creating PacketInterceptor for: " + e.getPlayer().getName() + " (" + closed + ")");
- if(closed)
+ private void createServerChannelHandler() {
+ // Handle connected channels
+ endInitProtocol = new ChannelInitializer() {
+
+ @Override
+ protected void initChannel(Channel channel) throws Exception {
+ try {
+ // This can take a while, so we need to stop the main thread from interfering
+ synchronized (networkManagers) {
+ // Stop injecting channels
+ if (!closed) {
+ channel.eventLoop().submit(() -> injectChannelInternal(channel));
+ }
+ }
+ } catch (Exception e) {
+ plugin.getLogger().log(Level.SEVERE, "Cannot inject incomming channel " + channel, e);
+ }
+ }
+
+ };
+
+ // This is executed before Minecraft's channel handler
+ beginInitProtocol = new ChannelInitializer() {
+
+ @Override
+ protected void initChannel(Channel channel) throws Exception {
+ channel.pipeline().addLast(endInitProtocol);
+ }
+
+ };
+
+ serverChannelHandler = new ChannelInboundHandlerAdapter() {
+
+ @Override
+ public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
+ Channel channel = (Channel) msg;
+
+ // Prepare to initialize ths channel
+ channel.pipeline().addFirst(beginInitProtocol);
+ ctx.fireChannelRead(msg);
+ }
+
+ };
+ }
+
+ /**
+ * Register bukkit events.
+ */
+ private void registerBukkitEvents() {
+ listener = new Listener() {
+
+ @EventHandler(priority = EventPriority.LOWEST)
+ public final void onPlayerLogin(PlayerLoginEvent e) {
+ if (closed)
+ return;
+
+ Channel channel = getChannel(e.getPlayer());
+
+ // Don't inject players that have been explicitly uninjected
+ if (!uninjectedChannels.contains(channel)) {
+ injectPlayer(e.getPlayer());
+ }
+ }
+
+ @EventHandler
+ public final void onPluginDisable(PluginDisableEvent e) {
+ if (e.getPlugin().equals(plugin)) {
+ close();
+ }
+ }
+
+ };
+
+ plugin.getServer().getPluginManager().registerEvents(listener, plugin);
+ }
+
+ @SuppressWarnings("unchecked")
+ private void registerChannelHandler() {
+ ServerConnectionListener serverConnection = MinecraftServer.getServer().getConnection();
+ networkManagers = serverConnection.getConnections();
+ // We need to synchronize against this list
+ createServerChannelHandler();
+
+ // Find the correct list, or implicitly throw an exception
+ boolean looking = true;
+ for (int i = 0; looking; i++) {
+ List