Skip to content

Commit 09fe22a

Browse files
committed
always run hamsterapi before lpx and other plugins, reorder handlers system
1 parent b561cfd commit 09fe22a

2 files changed

Lines changed: 94 additions & 4 deletions

File tree

src/dev/_2lstudios/hamsterapi/hamsterplayer/HamsterPlayer.java

Lines changed: 82 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44
import java.lang.reflect.InvocationTargetException;
55
import java.lang.reflect.Method;
66
import java.nio.channels.ClosedChannelException;
7+
import java.util.List;
8+
import java.util.NoSuchElementException;
79
import java.util.UUID;
810

911
import org.bukkit.Server;
@@ -18,6 +20,7 @@
1820
import dev._2lstudios.hamsterapi.utils.Reflection;
1921
import io.netty.channel.Channel;
2022
import io.netty.channel.ChannelDuplexHandler;
23+
import io.netty.channel.ChannelHandler;
2124
import io.netty.channel.ChannelPipeline;
2225
import io.netty.handler.codec.ByteToMessageDecoder;
2326

@@ -270,12 +273,14 @@ public void inject() throws IllegalAccessException, InvocationTargetException, N
270273
final ByteToMessageDecoder hamsterDecoderHandler = new HamsterDecoderHandler(this);
271274
final ChannelDuplexHandler hamsterChannelHandler = new HamsterChannelHandler(this);
272275

276+
// Inject after compression
273277
if (pipeline.get("decompress") != null) {
274278
pipeline.addAfter("decompress", HamsterHandler.HAMSTER_DECODER, hamsterDecoderHandler);
275279
Debug.info("Added HAMSTER_DECODER in pipeline after decompress (" + this.player.getName() + ")");
280+
// Compression not enabled, so inject after splitter
276281
} else if (pipeline.get("splitter") != null) {
277282
pipeline.addAfter("splitter", HamsterHandler.HAMSTER_DECODER, hamsterDecoderHandler);
278-
Debug.info("Added HAMSTER_DECODER in pipeline after spliter (" + this.player.getName() + ")");
283+
Debug.info("Added HAMSTER_DECODER in pipeline after splitter (" + this.player.getName() + ")");
279284
} else {
280285
Debug.crit("No ChannelHandler was found on the pipeline to inject HAMSTER_DECODER ("
281286
+ this.player.getName() + ")");
@@ -297,6 +302,82 @@ public void inject() throws IllegalAccessException, InvocationTargetException, N
297302
}
298303
}
299304

305+
/**
306+
* Periodically verifies that our channel handlers are in the correct position
307+
* in the pipeline. If another plugin has injected a handler before ours,
308+
* this method will "heal" the pipeline by reordering our handlers back to
309+
* their intended, dominant position.
310+
*
311+
* This should be run a short time after the initial injection (e.g., 20 ticks later)
312+
* to ensure priority.
313+
*/
314+
public void checkAndReorderHandlers() {
315+
// 1. --- Pre-flight Checks ---
316+
// Don't do anything if we were never injected or if the player is disconnected.
317+
if (!injected || channel == null || !channel.isActive()) {
318+
return;
319+
}
320+
321+
try {
322+
final ChannelPipeline pipeline = channel.pipeline();
323+
324+
// 2. --- Verify and Reorder HAMSTER_DECODER ---
325+
String decoderBaseName = (pipeline.get("decompress") != null) ? "decompress" : "splitter";
326+
reorderHandlerIfNeeded(pipeline, HamsterHandler.HAMSTER_DECODER, decoderBaseName);
327+
328+
// 3. --- Verify and Reorder HAMSTER_CHANNEL ---
329+
String channelBaseName = "decoder";
330+
reorderHandlerIfNeeded(pipeline, HamsterHandler.HAMSTER_CHANNEL, channelBaseName);
331+
332+
} catch (NoSuchElementException e) {
333+
// This can happen if a handler was removed while we were iterating. It's safe to ignore.
334+
Debug.warn("A handler was removed from the pipeline during reordering for " + this.player.getName() + ". This is usually safe.");
335+
} catch (Exception e) {
336+
Debug.crit("An unexpected error occurred while reordering pipeline handlers for "
337+
+ this.player.getName() + ": " + e.getMessage());
338+
}
339+
}
340+
341+
/**
342+
* A private helper to check if a handler is correctly positioned right after its base,
343+
* and if not, removes and re-adds it.
344+
*
345+
* @param pipeline The player's channel pipeline.
346+
* @param handlerName The name of our handler to check (e.g., "hamster_decoder").
347+
* @param baseName The name of the handler it must follow (e.g., "decompress").
348+
*/
349+
private void reorderHandlerIfNeeded(final ChannelPipeline pipeline, final String handlerName, final String baseName) {
350+
// Get our handler instance and the list of current handler names.
351+
final ChannelHandler ourHandler = pipeline.get(handlerName);
352+
final List<String> names = pipeline.names();
353+
354+
// If our handler or its base is missing, we can't do anything.
355+
if (ourHandler == null) {
356+
Debug.warn("Cannot reorder " + handlerName + " because it is missing from the pipeline for " + this.player.getName());
357+
return;
358+
}
359+
if (pipeline.get(baseName) == null) {
360+
Debug.warn("Cannot reorder " + handlerName + " because its base '" + baseName + "' is missing for " + this.player.getName());
361+
return;
362+
}
363+
364+
// Find the positions of our handler and its intended base.
365+
final int ourHandlerIndex = names.indexOf(handlerName);
366+
final int baseHandlerIndex = names.indexOf(baseName);
367+
368+
// If our handler is not directly after the base, it's out of order.
369+
if (ourHandlerIndex != baseHandlerIndex + 1) {
370+
Debug.warn(handlerName + " for player " + this.player.getName()
371+
+ " is out of order. Forcing re-injection to correct position.");
372+
373+
// Re-inject the handler to its rightful place.
374+
pipeline.remove(handlerName);
375+
pipeline.addAfter(baseName, handlerName, ourHandler);
376+
377+
Debug.info(handlerName + " was successfully reordered for " + this.player.getName());
378+
}
379+
}
380+
300381
// Injects but instead of returning an exception returns sucess (Boolean)
301382
public boolean tryInject() {
302383
try {

src/dev/_2lstudios/hamsterapi/listeners/PlayerJoinListener.java

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,18 +26,27 @@ public PlayerJoinListener(final HamsterAPI hamsterAPI) {
2626
this.hamsterAPI = hamsterAPI;
2727
}
2828

29-
@EventHandler(priority = EventPriority.LOWEST, ignoreCancelled = true)
29+
@EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true)
3030
public void onPlayerJoin(final PlayerJoinEvent event) {
3131
final Player player = event.getPlayer();
3232
final HamsterPlayer hamsterPlayer = hamsterPlayerManager.add(player);
3333

3434
if (!hamsterPlayer.tryInject()) {
3535
logger.warning("Failed to inject player " + player.getName()
36-
+ " please contact 2LStudios for support about HamsterAPI as this can lead to vulnerabilities.");
36+
+ ". Retrying...");
3737
// Retry after 1 tick
3838
scheduler.runTaskLater(hamsterAPI, () -> {
3939
if (player != null && player.isOnline() && hamsterPlayerManager.get(player) != null) {
40-
hamsterPlayer.tryInject();
40+
if (hamsterPlayer.tryInject()) {
41+
logger.info("Successfully injected player " + player.getName() + " after failing!");
42+
} else {
43+
logger.severe("Failed to inject player " + player.getName() + " after retrying! Please contact ArkFlame Development for support as this can lead to SERVER CRASH!");
44+
}
45+
}
46+
}, 1);
47+
scheduler.runTaskLater(hamsterAPI, () -> {
48+
if (player != null && player.isOnline() && hamsterPlayerManager.get(player) != null) {
49+
hamsterPlayer.checkAndReorderHandlers();
4150
}
4251
}, 1);
4352
}

0 commit comments

Comments
 (0)