117 lines
5.0 KiB
Java
117 lines
5.0 KiB
Java
package com.earth2me.essentials;
|
|
|
|
import net.ess3.api.IEssentials;
|
|
import net.ess3.nms.refl.ReflUtil;
|
|
import org.bukkit.entity.Player;
|
|
import org.bukkit.event.EventHandler;
|
|
import org.bukkit.event.EventPriority;
|
|
import org.bukkit.event.Listener;
|
|
import org.bukkit.event.server.ServerListPingEvent;
|
|
|
|
import java.lang.reflect.InvocationTargetException;
|
|
import java.lang.reflect.Method;
|
|
import java.util.Arrays;
|
|
import java.util.Iterator;
|
|
import java.util.List;
|
|
import java.util.logging.Level;
|
|
|
|
public class EssentialsServerListener implements Listener {
|
|
private static final List<String> ignoredSLPECallers = Arrays.asList(
|
|
".LegacyPingHandler.channelRead(", // CB responding to pings from pre-Netty clients
|
|
"de.dytanic.cloudnet.bridge.BukkitBootstrap", // CloudNet v2 doing... something
|
|
"de.dytanic.cloudnet.ext.bridge.bukkit.BukkitCloudNetBridgePlugin" // CloudNet v3 doing... something else
|
|
);
|
|
|
|
private final transient IEssentials ess;
|
|
private final boolean isPaperSample;
|
|
private boolean unsupportedLogged = false;
|
|
private boolean npeWarned = false;
|
|
private Method setSampleText;
|
|
private Method getSampleText;
|
|
|
|
public EssentialsServerListener(final IEssentials ess) {
|
|
this.ess = ess;
|
|
|
|
if (ReflUtil.getClassCached("com.destroystokyo.paper.event.server.PaperServerListPingEvent") == null) {
|
|
// This workaround is only necessary for older Paper builds
|
|
setSampleText = ReflUtil.getMethodCached(ServerListPingEvent.class, "setSampleText", List.class);
|
|
getSampleText = ReflUtil.getMethodCached(ServerListPingEvent.class, "getSampleText");
|
|
if (setSampleText != null && getSampleText != null) {
|
|
ess.getLogger().info("ServerListPingEvent: Paper 1.12.2 setSampleText API");
|
|
isPaperSample = true;
|
|
return;
|
|
}
|
|
}
|
|
|
|
ess.getLogger().info("ServerListPingEvent: Spigot iterator API");
|
|
isPaperSample = false;
|
|
}
|
|
|
|
@EventHandler(priority = EventPriority.LOWEST)
|
|
public void onServerListPing(final ServerListPingEvent event) throws Exception {
|
|
if (isPaperSample) {
|
|
try {
|
|
final List<String> playerNames = (List<String>) getSampleText.invoke(event, null);
|
|
playerNames.removeIf(player -> ess.getUser(player).isVanished());
|
|
setSampleText.invoke(event, playerNames);
|
|
} catch (final IllegalAccessException | InvocationTargetException | ClassCastException e) {
|
|
if (!unsupportedLogged && shouldWarnSLPECaller(e)) {
|
|
ess.getLogger().log(Level.WARNING, "Unable to hide players from server list ping "
|
|
+ "using Paper 1.12 method!", e);
|
|
unsupportedLogged = true;
|
|
}
|
|
} catch (final NullPointerException e) {
|
|
if (!npeWarned && shouldWarnSLPECaller(e)) {
|
|
npeWarned = true;
|
|
final Exception ex = new Exception("A plugin has fired a ServerListPingEvent "
|
|
+ "without implementing Paper's methods. Point the author to https://git.io/v7Xzl.");
|
|
ex.setStackTrace(e.getStackTrace());
|
|
throw ex;
|
|
}
|
|
}
|
|
} else {
|
|
try {
|
|
final Iterator<Player> iterator = event.iterator();
|
|
while (iterator.hasNext()) {
|
|
final Player player = iterator.next();
|
|
if (ess.getUser(player).isVanished()) {
|
|
iterator.remove();
|
|
}
|
|
}
|
|
} catch (final UnsupportedOperationException e) {
|
|
if (!unsupportedLogged && shouldWarnSLPECaller(e)) {
|
|
ess.getLogger().log(Level.WARNING, "Could not hide vanished players while handling " + event.getClass().getName(), e);
|
|
unsupportedLogged = true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Should we warn about this SLPE caller, or should we silently ignore it?
|
|
* This checks against the ignoredSLPECallers strings, and if it matches one of those, we
|
|
* return false.
|
|
*
|
|
* @param throwable A throwable caught by a catch block
|
|
* @return Whether or not to send a warning about this particular caller
|
|
*/
|
|
private boolean shouldWarnSLPECaller(final Throwable throwable) {
|
|
final int maxStackDepth = 20; // Limit the depth when searching through the stack trace
|
|
int depth = 0;
|
|
for (final StackTraceElement element : throwable.getStackTrace()) {
|
|
depth++;
|
|
if (depth > maxStackDepth) {
|
|
break;
|
|
}
|
|
|
|
for (final String ignoredString : ignoredSLPECallers) {
|
|
if (element.toString().contains(ignoredString)) {
|
|
return false; // We know about this error and should ignore it, so don't warn
|
|
}
|
|
}
|
|
}
|
|
|
|
return true; // We don't know for certain that we can ignore this, so warn just to be safe
|
|
}
|
|
}
|