Add SteamWar adaption for TinyProtocol

This commit is contained in:
2026-05-16 11:52:06 +02:00
parent 604657a084
commit b216438a47
2 changed files with 48 additions and 43 deletions
@@ -24,6 +24,7 @@ import de.steamwar.Reflection;
import de.steamwar.core.Core; import de.steamwar.core.Core;
import io.netty.channel.*; import io.netty.channel.*;
import net.minecraft.network.Connection; import net.minecraft.network.Connection;
import net.minecraft.network.protocol.Packet;
import net.minecraft.network.protocol.login.ServerboundHelloPacket; import net.minecraft.network.protocol.login.ServerboundHelloPacket;
import net.minecraft.server.MinecraftServer; import net.minecraft.server.MinecraftServer;
import net.minecraft.server.network.ServerConnectionListener; import net.minecraft.server.network.ServerConnectionListener;
@@ -39,6 +40,8 @@ import org.bukkit.plugin.Plugin;
import org.bukkit.scheduler.BukkitRunnable; import org.bukkit.scheduler.BukkitRunnable;
import java.util.*; import java.util.*;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.function.BiFunction;
import java.util.logging.Level; import java.util.logging.Level;
/** /**
@@ -57,7 +60,7 @@ public class TinyProtocol {
private Set<Channel> uninjectedChannels = Collections.newSetFromMap(new MapMaker().weakKeys().<Channel, Boolean>makeMap()); private Set<Channel> uninjectedChannels = Collections.newSetFromMap(new MapMaker().weakKeys().<Channel, Boolean>makeMap());
// List of network markers // List of network markers
private List<Connection> networkManagers; public List<Connection> networkManagers;
// Injected channel handlers // Injected channel handlers
private List<Channel> serverChannels = new ArrayList<>(); private List<Channel> serverChannels = new ArrayList<>();
@@ -72,6 +75,11 @@ public class TinyProtocol {
protected Plugin plugin; protected Plugin plugin;
public static final TinyProtocol instance = new TinyProtocol(Core.getInstance()); public static final TinyProtocol instance = new TinyProtocol(Core.getInstance());
private final Map<Class<?>, List<BiFunction<Player, Object, Object>>> packetFilters = new HashMap<>();
public static void init() {
// enforce init
}
/** /**
* Construct a new instance of TinyProtocol, and start intercepting packets for all connected clients and future clients. * Construct a new instance of TinyProtocol, and start intercepting packets for all connected clients and future clients.
@@ -235,79 +243,63 @@ public class TinyProtocol {
} }
} }
/** public <T> void addTypedFilter(Class<T> packetType, BiFunction<Player, T, Object> filter) {
* Invoked when the server is starting to send a packet to a player. packetFilters.computeIfAbsent(packetType, c -> new CopyOnWriteArrayList<>()).add((BiFunction) filter);
* <p>
* Note that this is not executed on the main thread.
*
* @param receiver - the receiving player, NULL for early login/status packets.
* @param channel - the channel that received the packet. Never NULL.
* @param packet - the packet being sent.
* @return The packet to send instead, or NULL to cancel the transmission.
*/
public Object onPacketOutAsync(Player receiver, Channel channel, Object packet) {
return packet;
} }
/** @Deprecated
* Invoked when the server has received a packet from a given player. public void addFilter(Class<?> packetType, BiFunction<Player, Object, Object> filter) {
* <p> packetFilters.computeIfAbsent(packetType, c -> new CopyOnWriteArrayList<>()).add(filter);
* Use {@link Channel#remoteAddress()} to get the remote address of the client. }
*
* @param sender - the player that sent the packet, NULL for early login/status packets. public void removeFilter(Class<?> packetType, BiFunction<Player, Object, Object> filter) {
* @param channel - channel that received the packet. Never NULL. packetFilters.getOrDefault(packetType, Collections.emptyList()).remove(filter);
* @param packet - the packet being received.
* @return The packet to recieve instead, or NULL to cancel.
*/
public Object onPacketInAsync(Player sender, Channel channel, Object packet) {
return packet;
} }
/** /**
* Send a packet to a particular player. * Send a packet to a particular player.
* <p>
* Note that {@link #onPacketOutAsync(Player, Channel, Object)} will be invoked with this packet.
* *
* @param player - the destination player. * @param player - the destination player.
* @param packet - the packet to send. * @param packet - the packet to send.
*/ */
public void sendPacket(Player player, Object packet) { public void sendPacket(Player player, Packet<?> packet) {
sendPacket(getChannel(player), packet); sendPacket(getChannel(player), packet);
} }
@Deprecated
public void sendPacket(Player player, Object object) {
if (object instanceof Packet<?> packet) {
sendPacket(getChannel(player), packet);
}
}
/** /**
* Send a packet to a particular client. * Send a packet to a particular client.
* <p>
* Note that {@link #onPacketOutAsync(Player, Channel, Object)} will be invoked with this packet.
* *
* @param channel - client identified by a channel. * @param channel - client identified by a channel.
* @param packet - the packet to send. * @param packet - the packet to send.
*/ */
public void sendPacket(Channel channel, Object packet) { public void sendPacket(Channel channel, Packet<?> packet) {
channel.pipeline().writeAndFlush(packet); channel.pipeline().writeAndFlush(packet);
} }
/** /**
* Pretend that a given packet has been received from a player. * Pretend that a given packet has been received from a player.
* <p>
* Note that {@link #onPacketInAsync(Player, Channel, Object)} will be invoked with this packet.
* *
* @param player - the player that sent the packet. * @param player - the player that sent the packet.
* @param packet - the packet that will be received by the server. * @param packet - the packet that will be received by the server.
*/ */
public void receivePacket(Player player, Object packet) { public void receivePacket(Player player, Packet<?> packet) {
receivePacket(getChannel(player), packet); receivePacket(getChannel(player), packet);
} }
/** /**
* Pretend that a given packet has been received from a given client. * Pretend that a given packet has been received from a given client.
* <p>
* Note that {@link #onPacketInAsync(Player, Channel, Object)} will be invoked with this packet.
* *
* @param channel - client identified by a channel. * @param channel - client identified by a channel.
* @param packet - the packet that will be received by the server. * @param packet - the packet that will be received by the server.
*/ */
public void receivePacket(Channel channel, Object packet) { public void receivePacket(Channel channel, Packet<?> packet) {
channel.pipeline().context("encoder").fireChannelRead(packet); channel.pipeline().context("encoder").fireChannelRead(packet);
} }
@@ -461,7 +453,7 @@ public class TinyProtocol {
handleLoginStart(channel, msg); handleLoginStart(channel, msg);
try { try {
msg = onPacketInAsync(player, channel, msg); msg = filterPacket(player, msg);
} catch (Exception e) { } catch (Exception e) {
plugin.getLogger().log(Level.SEVERE, "Error in onPacketInAsync().", e); plugin.getLogger().log(Level.SEVERE, "Error in onPacketInAsync().", e);
} }
@@ -474,7 +466,7 @@ public class TinyProtocol {
@Override @Override
public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception { public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception {
try { try {
msg = onPacketOutAsync(player, ctx.channel(), msg); msg = filterPacket(player, msg);
} catch (Exception e) { } catch (Exception e) {
plugin.getLogger().log(Level.SEVERE, "Error in onPacketOutAsync().", e); plugin.getLogger().log(Level.SEVERE, "Error in onPacketOutAsync().", e);
} }
@@ -489,5 +481,16 @@ public class TinyProtocol {
channelLookup.put(name, channel); channelLookup.put(name, channel);
} }
} }
private Object filterPacket(Player player, Object packet) {
List<BiFunction<Player, Object, Object>> filters = packetFilters.getOrDefault(packet.getClass(), Collections.emptyList());
for(BiFunction<Player, Object, Object> filter : filters) {
packet = filter.apply(player, packet);
if(packet == null) break;
}
return packet;
}
} }
} }
@@ -23,6 +23,8 @@ import com.comphenix.tinyprotocol.TinyProtocol;
import de.steamwar.Reflection; import de.steamwar.Reflection;
import de.steamwar.sql.internal.Statement; import de.steamwar.sql.internal.Statement;
import io.netty.channel.ChannelFuture; import io.netty.channel.ChannelFuture;
import net.minecraft.server.MinecraftServer;
import net.minecraft.server.network.ServerConnectionListener;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;
import org.eclipse.openj9.criu.CRIUSupport; import org.eclipse.openj9.criu.CRIUSupport;
import org.eclipse.openj9.criu.JVMCRIUException; import org.eclipse.openj9.criu.JVMCRIUException;
@@ -56,7 +58,7 @@ class CheckpointUtilsJ9 {
Bukkit.getOnlinePlayers().forEach(player -> player.kickPlayer(null)); Bukkit.getOnlinePlayers().forEach(player -> player.kickPlayer(null));
List<?> networkManagers = TinyProtocol.networkManagers.get(TinyProtocol.getServerConnection(Core.getInstance())); List<?> networkManagers = TinyProtocol.instance.networkManagers;
if(!Bukkit.getOnlinePlayers().isEmpty() || !networkManagers.isEmpty()) { if(!Bukkit.getOnlinePlayers().isEmpty() || !networkManagers.isEmpty()) {
Core.getInstance().getLogger().log(Level.INFO, "Waiting for players to disconnect for checkpointing"); Core.getInstance().getLogger().log(Level.INFO, "Waiting for players to disconnect for checkpointing");
Bukkit.getScheduler().runTaskLater(Core.getInstance(), CheckpointUtils::freeze, 1); Bukkit.getScheduler().runTaskLater(Core.getInstance(), CheckpointUtils::freeze, 1);
@@ -90,8 +92,8 @@ class CheckpointUtilsJ9 {
} }
private static final Reflection.Field<List> channelFutures = Reflection.getField(TinyProtocol.serverConnection, List.class, 0, ChannelFuture.class); private static final Reflection.Field<List> channelFutures = Reflection.getField(ServerConnectionListener.class, List.class, 0, ChannelFuture.class);
private static final Reflection.Method bind = Reflection.getMethod(TinyProtocol.serverConnection, null, InetAddress.class, int.class); private static final Reflection.Method bind = Reflection.getMethod(ServerConnectionListener.class, null, InetAddress.class, int.class);
private static void freezeInternal(Path path) throws Exception { private static void freezeInternal(Path path) throws Exception {
Bukkit.getPluginManager().callEvent(new CRIUSleepEvent()); Bukkit.getPluginManager().callEvent(new CRIUSleepEvent());
@@ -99,7 +101,7 @@ class CheckpointUtilsJ9 {
Statement.closeAll(); Statement.closeAll();
// Close socket // Close socket
Object serverConnection = TinyProtocol.getServerConnection(Core.getInstance()); ServerConnectionListener serverConnection = MinecraftServer.getServer().getConnection();
List<?> channels = channelFutures.get(serverConnection); List<?> channels = channelFutures.get(serverConnection);
for(Object future : channels) { for(Object future : channels) {
((ChannelFuture) future).channel().close().syncUninterruptibly(); ((ChannelFuture) future).channel().close().syncUninterruptibly();