diff --git a/pom.xml b/pom.xml index 93d7a6c..9cfd47d 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ io.github.diona-testserver PluginHooker - 0.6.0 + 1.0.0 jar PluginHooker diff --git a/src/main/java/io/github/dionatestserver/pluginhooker/events/DionaBukkitListenerEvent.java b/src/main/java/io/github/dionatestserver/pluginhooker/events/BukkitListenerEvent.java similarity index 81% rename from src/main/java/io/github/dionatestserver/pluginhooker/events/DionaBukkitListenerEvent.java rename to src/main/java/io/github/dionatestserver/pluginhooker/events/BukkitListenerEvent.java index c8024be..2f07408 100644 --- a/src/main/java/io/github/dionatestserver/pluginhooker/events/DionaBukkitListenerEvent.java +++ b/src/main/java/io/github/dionatestserver/pluginhooker/events/BukkitListenerEvent.java @@ -7,7 +7,7 @@ import org.bukkit.event.HandlerList; import org.bukkit.plugin.Plugin; -public class DionaBukkitListenerEvent extends Event implements Cancellable { +public class BukkitListenerEvent extends Event implements Cancellable { private static final HandlerList handlers = new HandlerList(); @@ -20,11 +20,11 @@ public class DionaBukkitListenerEvent extends Event implements Cancellable { @Getter private final DionaPlayer dionaPlayer; - public DionaBukkitListenerEvent(Plugin plugin, Event event) { + public BukkitListenerEvent(Plugin plugin, Event event) { this(plugin, event, null); } - public DionaBukkitListenerEvent(Plugin plugin, Event event, DionaPlayer dionaPlayer) { + public BukkitListenerEvent(Plugin plugin, Event event, DionaPlayer dionaPlayer) { super(event.isAsynchronous()); this.plugin = plugin; this.event = event; diff --git a/src/main/java/io/github/dionatestserver/pluginhooker/events/DionaProtocolLibPacketEvent.java b/src/main/java/io/github/dionatestserver/pluginhooker/events/ProtocolLibPacketEvent.java similarity index 84% rename from src/main/java/io/github/dionatestserver/pluginhooker/events/DionaProtocolLibPacketEvent.java rename to src/main/java/io/github/dionatestserver/pluginhooker/events/ProtocolLibPacketEvent.java index c37b928..394a17e 100644 --- a/src/main/java/io/github/dionatestserver/pluginhooker/events/DionaProtocolLibPacketEvent.java +++ b/src/main/java/io/github/dionatestserver/pluginhooker/events/ProtocolLibPacketEvent.java @@ -7,7 +7,7 @@ import org.bukkit.event.Event; import org.bukkit.event.HandlerList; -public class DionaProtocolLibPacketEvent extends Event implements Cancellable { +public class ProtocolLibPacketEvent extends Event implements Cancellable { private static final HandlerList handlers = new HandlerList(); @@ -20,7 +20,7 @@ public class DionaProtocolLibPacketEvent extends Event implements Cancellable { @Getter private final boolean outbound; - public DionaProtocolLibPacketEvent(PacketListener packetListener, PacketEvent packetEvent, boolean outbound) { + public ProtocolLibPacketEvent(PacketListener packetListener, PacketEvent packetEvent, boolean outbound) { super(packetEvent.isAsynchronous()); this.packetListener = packetListener; this.packetEvent = packetEvent; diff --git a/src/main/java/io/github/dionatestserver/pluginhooker/hook/HookerManager.java b/src/main/java/io/github/dionatestserver/pluginhooker/hook/HookerManager.java index b6e7615..b7a1735 100644 --- a/src/main/java/io/github/dionatestserver/pluginhooker/hook/HookerManager.java +++ b/src/main/java/io/github/dionatestserver/pluginhooker/hook/HookerManager.java @@ -1,27 +1,18 @@ package io.github.dionatestserver.pluginhooker.hook; -import com.sun.tools.attach.VirtualMachine; import io.github.dionatestserver.pluginhooker.DionaPluginHooker; -import javassist.ClassPool; +import io.github.dionatestserver.pluginhooker.utils.AgentUtils; import org.reflections.Reflections; import java.io.File; -import java.io.OutputStream; -import java.lang.management.ManagementFactory; -import java.lang.management.RuntimeMXBean; -import java.nio.file.Files; import java.util.List; -import java.util.jar.Attributes; -import java.util.jar.JarEntry; -import java.util.jar.JarOutputStream; -import java.util.jar.Manifest; +import java.util.Objects; import java.util.logging.Logger; import java.util.stream.Collectors; public class HookerManager { private final Logger logger = DionaPluginHooker.getInstance().getLogger(); - private static final String AGENT_CLASS = "io.github.dionatestserver.pluginhooker.hook.PluginHookerAgent"; public HookerManager() { List injectors = this.getInjectorList(); @@ -36,26 +27,32 @@ public HookerManager() { try { injector.predefineClass(); logger.info( injector.getClassNameWithoutPackage() + " is now predefined!"); + return false; } catch (Exception e) { logger.severe("Error while predefining " + injector.getClassNameWithoutPackage()); e.printStackTrace(); return true; } - return false; }) .collect(Collectors.toList()); if (definedClasses.size() == 0) return; - //init instrumentation field - File agentFile = this.generateAgentFile(); - this.attachAgent(agentFile); + //init instrumentation field + try { + String agentClass = "io.github.dionatestserver.pluginhooker.hook.PluginHookerAgent"; + File agentFile = AgentUtils.generateAgentFile(agentClass); + AgentUtils.attachSelf(Objects.requireNonNull(agentFile)); + } catch (Exception e) { + logger.severe("Error while attaching agent"); + e.printStackTrace(); + } definedClasses.forEach(injector -> { try { injector.redefineClass(PluginHookerAgent.instrumentation); - logger.info( injector.getClassNameWithoutPackage() + " is now redefined!"); + logger.info(injector.getClassNameWithoutPackage() + " is now redefined!"); } catch (Exception e) { logger.severe("Error while redefining " + injector.getClassNameWithoutPackage()); e.printStackTrace(); @@ -73,53 +70,4 @@ private List getInjectorList() { } }).collect(Collectors.toList()); } - - private File generateAgentFile() { - Manifest manifest = new Manifest(); - manifest.getMainAttributes().put(Attributes.Name.MANIFEST_VERSION, "1.0"); - manifest.getMainAttributes().put(new Attributes.Name("Agent-Class"), AGENT_CLASS); - manifest.getMainAttributes().put(new Attributes.Name("Can-Redefine-Classes"), "true"); - - try { - File agentFile = new File(DionaPluginHooker.getInstance().getDataFolder(), "agent.jar"); - if (!agentFile.exists()) { - agentFile.createNewFile(); - } - - OutputStream outputStream = Files.newOutputStream(agentFile.toPath()); - JarOutputStream jarOutputStream = new JarOutputStream(outputStream, manifest); - jarOutputStream.putNextEntry(new JarEntry(AGENT_CLASS.replace(".", "/") + ".class")); - - - ClassPool pool = ClassPool.getDefault(); - jarOutputStream.write(pool.get(AGENT_CLASS).toBytecode()); - - jarOutputStream.finish(); - return agentFile; - } catch (Exception e) { - e.printStackTrace(); - return null; - } - } - - private String getPid() { - RuntimeMXBean bean = ManagementFactory.getRuntimeMXBean(); - String pid = bean.getName(); - if (pid.contains("@")) { - pid = pid.substring(0, pid.indexOf("@")); - } - return pid; - } - - private void attachAgent(File agentFile) { - try { - System.loadLibrary("attach"); - VirtualMachine vm = VirtualMachine.attach(this.getPid()); - vm.loadAgent(agentFile.getAbsolutePath()); - vm.detach(); - } catch (Exception e) { - logger.severe("Error while attaching agent"); - e.printStackTrace(); - } - } } diff --git a/src/main/java/io/github/dionatestserver/pluginhooker/hook/Injector.java b/src/main/java/io/github/dionatestserver/pluginhooker/hook/Injector.java index bf1e3f2..1e14dfc 100644 --- a/src/main/java/io/github/dionatestserver/pluginhooker/hook/Injector.java +++ b/src/main/java/io/github/dionatestserver/pluginhooker/hook/Injector.java @@ -1,14 +1,12 @@ package io.github.dionatestserver.pluginhooker.hook; import io.github.dionatestserver.pluginhooker.DionaPluginHooker; -import javassist.CannotCompileException; import javassist.ClassPool; import javassist.CtClass; import javassist.LoaderClassPath; import javassist.util.proxy.DefineClassHelper; import lombok.Getter; -import java.io.IOException; import java.lang.instrument.ClassDefinition; import java.lang.instrument.Instrumentation; import java.lang.reflect.Field; @@ -18,24 +16,37 @@ public abstract class Injector { protected static final ClassPool classPool = ClassPool.getDefault(); + static { + classPool.appendClassPath(new LoaderClassPath(DionaPluginHooker.class.getClassLoader())); + } + + protected final Class neighbor; + + protected final CtClass targetClass; + @Getter - protected final String targetClass; + protected final String targetClassName; @Getter protected final String classNameWithoutPackage; - protected final Class neighbor; - - public Injector(String targetClass, Class neighbor) { - this.targetClass = targetClass; + public Injector(String targetClassName, Class neighbor) { + this.targetClassName = targetClassName; // split the class name - String[] className = this.getTargetClass().split("\\."); + String[] className = this.getTargetClassName().split("\\."); // get the class name without the package classNameWithoutPackage = className[className.length - 1]; this.neighbor = neighbor; - classPool.appendClassPath(new LoaderClassPath(DionaPluginHooker.class.getClassLoader())); + + try { + this.initClassPath(); + this.targetClass = classPool.get(targetClassName); + this.hookClass(); + } catch (Exception e) { + throw new RuntimeException(e); + } } public boolean isTargetClassDefined() { @@ -43,25 +54,32 @@ public boolean isTargetClassDefined() { Field classesField = ClassLoader.class.getDeclaredField("classes"); classesField.setAccessible(true); AbstractList> classes = (AbstractList) classesField.get(neighbor.getClassLoader()); - return classes.stream().anyMatch(clazz -> clazz.getName().equals(targetClass)); + return classes.stream().anyMatch(clazz -> clazz.getName().equals(targetClassName)); } catch (Exception e) { return false; } } public void predefineClass() throws Exception { - CtClass hookedClass = this.generateHookedClass(); - - DefineClassHelper.toClass(targetClass, neighbor, neighbor.getClassLoader(), null, hookedClass.toBytecode()); + DefineClassHelper.toClass( + targetClassName, + neighbor, + neighbor.getClassLoader(), + null, + targetClass.toBytecode() + ); } public void redefineClass(Instrumentation instrumentation) throws Exception { - CtClass hookedClass = this.generateHookedClass(); - - instrumentation.redefineClasses(new ClassDefinition(Class.forName(targetClass), hookedClass.toBytecode())); + instrumentation.redefineClasses( + new ClassDefinition(Class.forName(targetClassName), targetClass.toBytecode()) + ); } - public abstract CtClass generateHookedClass(); + public abstract void hookClass() throws Exception; public abstract boolean canHook(); + + protected abstract void initClassPath(); + } diff --git a/src/main/java/io/github/dionatestserver/pluginhooker/hook/impl/bukkit/BukkitCallbackHandler.java b/src/main/java/io/github/dionatestserver/pluginhooker/hook/impl/bukkit/BukkitCallbackHandler.java index 1c0b33c..b98a5bf 100644 --- a/src/main/java/io/github/dionatestserver/pluginhooker/hook/impl/bukkit/BukkitCallbackHandler.java +++ b/src/main/java/io/github/dionatestserver/pluginhooker/hook/impl/bukkit/BukkitCallbackHandler.java @@ -2,12 +2,13 @@ import io.github.dionatestserver.pluginhooker.DionaPluginHooker; import io.github.dionatestserver.pluginhooker.config.DionaConfig; -import io.github.dionatestserver.pluginhooker.events.DionaBukkitListenerEvent; +import io.github.dionatestserver.pluginhooker.events.BukkitListenerEvent; import io.github.dionatestserver.pluginhooker.player.DionaPlayer; import org.bukkit.Bukkit; import org.bukkit.entity.Entity; import org.bukkit.entity.HumanEntity; import org.bukkit.entity.Player; +import org.bukkit.entity.Projectile; import org.bukkit.event.Event; import org.bukkit.event.block.*; import org.bukkit.event.enchantment.EnchantItemEvent; @@ -22,13 +23,19 @@ import org.bukkit.projectiles.ProjectileSource; import java.lang.reflect.Field; +import java.util.HashSet; import java.util.LinkedHashMap; import java.util.Map; +import java.util.Set; import java.util.function.Function; public class BukkitCallbackHandler { private final Map, Function> eventMap = new LinkedHashMap<>(); + private final Map, Field> eventFieldCache = new LinkedHashMap<>(); + + private final Set> failedFieldCache = new HashSet<>(); + public BukkitCallbackHandler() { this.initEventMap(); } @@ -50,13 +57,13 @@ public boolean handleBukkitEvent(Plugin plugin, Event event) { DionaPlayer dionaPlayer = DionaPluginHooker.getPlayerManager().getDionaPlayer(this.getPlayerByEvent(event)); if (dionaPlayer == null) { - DionaBukkitListenerEvent bukkitListenerEvent = new DionaBukkitListenerEvent(plugin, event); + BukkitListenerEvent bukkitListenerEvent = new BukkitListenerEvent(plugin, event); Bukkit.getPluginManager().callEvent(bukkitListenerEvent); return bukkitListenerEvent.isCancelled(); } else { if (dionaPlayer.getEnabledPlugins().contains(plugin)) { - DionaBukkitListenerEvent bukkitListenerEvent = new DionaBukkitListenerEvent(plugin, event, dionaPlayer); + BukkitListenerEvent bukkitListenerEvent = new BukkitListenerEvent(plugin, event, dionaPlayer); Bukkit.getPluginManager().callEvent(bukkitListenerEvent); return bukkitListenerEvent.isCancelled(); } else { @@ -75,6 +82,12 @@ private Player getPlayerByEvent(Event event) { Entity damager = ((EntityDamageByEntityEvent) event).getDamager(); if (damager instanceof Player) return (Player) damager; + if (damager instanceof Projectile) { + Projectile projectile = (Projectile) damager; + ProjectileSource projectileSource = projectile.getShooter(); + if (projectileSource instanceof Player) + return (Player) projectileSource; + } } Entity entity = ((EntityEvent) event).getEntity(); if (entity instanceof Player) @@ -91,15 +104,29 @@ else if (event instanceof ProjectileLaunchEvent) { // Try to get the player field from the event if (DionaConfig.useReflectionToGetEventPlayer) { - try { - Field playerField = event.getClass().getDeclaredField("player"); - if (!playerField.isAccessible()) playerField.setAccessible(true); - Object player = playerField.get(event); - if (player instanceof Player) { - return (Player) player; + if (this.failedFieldCache.contains(event.getClass())) { + return null; + } + Field playerField = this.eventFieldCache.getOrDefault(event.getClass(), null); + if (playerField == null) { + try { + playerField = event.getClass().getDeclaredField("player"); + playerField.setAccessible(true); + Player player = (Player) playerField.get(event); + this.eventFieldCache.put(event.getClass(), playerField); + return player; + } catch (Exception e) { + this.failedFieldCache.add(event.getClass()); + return null; } - } catch (Exception ignored) { } + + try { + return (Player) playerField.get(event); + } catch (Exception e) { + return null; + } + } return null; diff --git a/src/main/java/io/github/dionatestserver/pluginhooker/hook/impl/bukkit/BukkitEventInjector.java b/src/main/java/io/github/dionatestserver/pluginhooker/hook/impl/bukkit/BukkitEventInjector.java index 45bdd4f..557f9f5 100644 --- a/src/main/java/io/github/dionatestserver/pluginhooker/hook/impl/bukkit/BukkitEventInjector.java +++ b/src/main/java/io/github/dionatestserver/pluginhooker/hook/impl/bukkit/BukkitEventInjector.java @@ -4,7 +4,7 @@ import io.github.dionatestserver.pluginhooker.hook.HookerManager; import io.github.dionatestserver.pluginhooker.hook.Injector; import io.github.dionatestserver.pluginhooker.utils.ClassUtils; -import javassist.CtClass; +import javassist.CannotCompileException; import javassist.CtMethod; import javassist.util.proxy.DefineClassHelper; import lombok.Getter; @@ -17,7 +17,7 @@ public class BukkitEventInjector extends Injector { @Getter - private BukkitCallbackHandler callbackHandler = new BukkitCallbackHandler(); + private final BukkitCallbackHandler callbackHandler = new BukkitCallbackHandler(); public BukkitEventInjector() { super("org.bukkit.plugin.RegisteredListener", Plugin.class); @@ -40,18 +40,11 @@ public BukkitEventInjector() { } @Override - public CtClass generateHookedClass() { - try { - CtClass registeredListener = classPool.get(targetClass); - CtMethod callEvent = ClassUtils.getMethodByName(registeredListener.getMethods(), "callEvent"); - callEvent.insertBefore( - "if(" + BukkitEventHooker.class.getName() + ".getInstance().onCallEvent(this.plugin,$1))return;" - ); - return registeredListener; - } catch (Exception e) { - e.printStackTrace(); - } - return null; + public void hookClass() throws CannotCompileException { + CtMethod callEvent = ClassUtils.getMethodByName(targetClass.getMethods(), "callEvent"); + callEvent.insertBefore( + "if(" + BukkitEventHooker.class.getName() + ".getInstance().onCallEvent(this.plugin,$1))return;" + ); } @Override @@ -59,6 +52,11 @@ public boolean canHook() { return DionaConfig.hookBukkitEvent; } + @Override + protected void initClassPath() { + // empty + } + public static class BukkitEventHooker { @Getter diff --git a/src/main/java/io/github/dionatestserver/pluginhooker/hook/impl/protocollib/ListenerMultimapInjector.java b/src/main/java/io/github/dionatestserver/pluginhooker/hook/impl/protocollib/ListenerMultimapInjector.java index 68ec8a9..671646c 100644 --- a/src/main/java/io/github/dionatestserver/pluginhooker/hook/impl/protocollib/ListenerMultimapInjector.java +++ b/src/main/java/io/github/dionatestserver/pluginhooker/hook/impl/protocollib/ListenerMultimapInjector.java @@ -16,26 +16,23 @@ public ListenerMultimapInjector() { } @Override - public CtClass generateHookedClass() { - classPool.appendClassPath(new LoaderClassPath(PacketTypeSet.class.getClassLoader())); - - try { - CtClass targetClass = classPool.get(this.targetClass); - CtMethod addListener = ClassUtils.getMethodByName(targetClass.getMethods(), "addListener"); - CtMethod removeListener = ClassUtils.getMethodByName(targetClass.getMethods(), "removeListener"); - - String src = DionaPluginHooker.class.getName() + ".getPlayerManager().removeAllPlayerCachedListener();"; - addListener.insertBefore(src); - removeListener.insertBefore(src); - - return targetClass; - } catch (Exception e) { - throw new RuntimeException(e); - } + public void hookClass() throws Exception { + CtClass targetClass = classPool.get(this.targetClassName); + CtMethod addListener = ClassUtils.getMethodByName(targetClass.getMethods(), "addListener"); + CtMethod removeListener = ClassUtils.getMethodByName(targetClass.getMethods(), "removeListener"); + + String src = DionaPluginHooker.class.getName() + ".getPlayerManager().removeAllPlayerCachedListener();"; + addListener.insertBefore(src); + removeListener.insertBefore(src); } @Override public boolean canHook() { return DionaConfig.hookProtocolLibPacket; } + + @Override + protected void initClassPath() { + classPool.appendClassPath(new LoaderClassPath(PacketTypeSet.class.getClassLoader())); + } } diff --git a/src/main/java/io/github/dionatestserver/pluginhooker/hook/impl/protocollib/ProtocolLibCallbackHandler.java b/src/main/java/io/github/dionatestserver/pluginhooker/hook/impl/protocollib/ProtocolLibCallbackHandler.java index 02bd1bc..25109be 100644 --- a/src/main/java/io/github/dionatestserver/pluginhooker/hook/impl/protocollib/ProtocolLibCallbackHandler.java +++ b/src/main/java/io/github/dionatestserver/pluginhooker/hook/impl/protocollib/ProtocolLibCallbackHandler.java @@ -6,7 +6,7 @@ import com.comphenix.protocol.injector.PrioritizedListener; import com.comphenix.protocol.injector.SortedPacketListenerList; import io.github.dionatestserver.pluginhooker.DionaPluginHooker; -import io.github.dionatestserver.pluginhooker.events.DionaProtocolLibPacketEvent; +import io.github.dionatestserver.pluginhooker.events.ProtocolLibPacketEvent; import io.github.dionatestserver.pluginhooker.player.DionaPlayer; import org.bukkit.Bukkit; import org.bukkit.plugin.Plugin; @@ -38,7 +38,7 @@ public SortedPacketListenerList handleProtocolLibPacket(SortedPacketListenerList if (dionaPlayer.getEnabledPlugins().contains(plugin)) { - DionaProtocolLibPacketEvent packetEvent = new DionaProtocolLibPacketEvent(listener, event, outbound); + ProtocolLibPacketEvent packetEvent = new ProtocolLibPacketEvent(listener, event, outbound); Bukkit.getPluginManager().callEvent(packetEvent); if (packetEvent.isCancelled()) { diff --git a/src/main/java/io/github/dionatestserver/pluginhooker/hook/impl/protocollib/ProtocolLibInjector.java b/src/main/java/io/github/dionatestserver/pluginhooker/hook/impl/protocollib/ProtocolLibInjector.java index 54a8bc2..00e8846 100644 --- a/src/main/java/io/github/dionatestserver/pluginhooker/hook/impl/protocollib/ProtocolLibInjector.java +++ b/src/main/java/io/github/dionatestserver/pluginhooker/hook/impl/protocollib/ProtocolLibInjector.java @@ -12,28 +12,20 @@ public class ProtocolLibInjector extends Injector { @Getter - private static ProtocolLibCallbackHandler callbackHandler = new ProtocolLibCallbackHandler(); + private static final ProtocolLibCallbackHandler callbackHandler = new ProtocolLibCallbackHandler(); public ProtocolLibInjector() { super("com.comphenix.protocol.injector.PacketFilterManager", PacketFilterBuilder.class); } @Override - public CtClass generateHookedClass() { - classPool.appendClassPath(new LoaderClassPath(PacketFilterBuilder.class.getClassLoader())); + public void hookClass() throws Exception { + CtClass packetFilterManager = classPool.get(targetClassName); - try { - CtClass packetFilterManager = classPool.get(targetClass); - - CtMethod postPacketToListeners = ClassUtils.getMethodBySignature(packetFilterManager.getDeclaredMethods(), "(Lcom/comphenix/protocol/injector/SortedPacketListenerList;Lcom/comphenix/protocol/events/PacketEvent;Z)V"); - postPacketToListeners.insertBefore( - "$1=" + ProtocolLibInjector.class.getName() + ".getCallbackHandler().handleProtocolLibPacket($1,$2,$3);" - ); - return packetFilterManager; - } catch (Exception e) { - e.printStackTrace(); - } - return null; + CtMethod postPacketToListeners = ClassUtils.getMethodBySignature(packetFilterManager.getDeclaredMethods(), "(Lcom/comphenix/protocol/injector/SortedPacketListenerList;Lcom/comphenix/protocol/events/PacketEvent;Z)V"); + postPacketToListeners.insertBefore( + "$1=" + ProtocolLibInjector.class.getName() + ".getCallbackHandler().handleProtocolLibPacket($1,$2,$3);" + ); } @Override @@ -41,4 +33,9 @@ public boolean canHook() { return DionaConfig.hookProtocolLibPacket; } + @Override + protected void initClassPath() { + classPool.appendClassPath(new LoaderClassPath(PacketFilterBuilder.class.getClassLoader())); + } + } diff --git a/src/main/java/io/github/dionatestserver/pluginhooker/player/DionaPlayer.java b/src/main/java/io/github/dionatestserver/pluginhooker/player/DionaPlayer.java index f8cedc9..b959128 100644 --- a/src/main/java/io/github/dionatestserver/pluginhooker/player/DionaPlayer.java +++ b/src/main/java/io/github/dionatestserver/pluginhooker/player/DionaPlayer.java @@ -9,6 +9,7 @@ import org.bukkit.plugin.Plugin; import java.util.HashSet; +import java.util.Objects; import java.util.Set; @Getter @@ -16,7 +17,7 @@ public class DionaPlayer { private final Player player; - private final Set enabledPlugins; + private final Set enabledPlugins = new HashSet<>(); // cached Protocollib listener list @Setter @@ -24,7 +25,6 @@ public class DionaPlayer { public DionaPlayer(Player player) { this.player = player; - this.enabledPlugins = new HashSet<>(); } public void enablePlugin(Plugin plugin) { @@ -45,5 +45,16 @@ public void removeCachedListener() { cachedListeners = null; } + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + DionaPlayer that = (DionaPlayer) o; + return player.equals(that.player); + } + @Override + public int hashCode() { + return Objects.hash(player); + } } diff --git a/src/main/java/io/github/dionatestserver/pluginhooker/player/PlayerManager.java b/src/main/java/io/github/dionatestserver/pluginhooker/player/PlayerManager.java index c35a2f9..4f116de 100644 --- a/src/main/java/io/github/dionatestserver/pluginhooker/player/PlayerManager.java +++ b/src/main/java/io/github/dionatestserver/pluginhooker/player/PlayerManager.java @@ -3,14 +3,15 @@ import lombok.Getter; import org.bukkit.entity.Player; -import java.util.ArrayList; -import java.util.List; +import java.util.HashSet; +import java.util.LinkedHashSet; +import java.util.Set; @Getter public class PlayerManager { @Getter - private final List players = new ArrayList<>(); + private final Set players = new HashSet<>(); public void addPlayer(Player player) { players.add(new DionaPlayer(player)); @@ -22,7 +23,12 @@ public void removePlayer(Player player) { public DionaPlayer getDionaPlayer(Player player) { if (player == null) return null; - return players.stream().filter(dionaPlayer -> dionaPlayer.getPlayer() == player).findFirst().orElse(null); + for (DionaPlayer dionaPlayer : players) { + if (dionaPlayer.getPlayer().equals(player)) { + return dionaPlayer; + } + } + return null; } public void removeAllPlayerCachedListener() { diff --git a/src/main/java/io/github/dionatestserver/pluginhooker/utils/AgentUtils.java b/src/main/java/io/github/dionatestserver/pluginhooker/utils/AgentUtils.java new file mode 100644 index 0000000..bc3b86c --- /dev/null +++ b/src/main/java/io/github/dionatestserver/pluginhooker/utils/AgentUtils.java @@ -0,0 +1,61 @@ +package io.github.dionatestserver.pluginhooker.utils; + +import com.sun.tools.attach.VirtualMachine; +import io.github.dionatestserver.pluginhooker.DionaPluginHooker; +import javassist.ClassPool; + +import java.io.File; +import java.io.OutputStream; +import java.lang.management.ManagementFactory; +import java.lang.management.RuntimeMXBean; +import java.nio.file.Files; +import java.util.jar.Attributes; +import java.util.jar.JarEntry; +import java.util.jar.JarOutputStream; +import java.util.jar.Manifest; + +public class AgentUtils { + public static File generateAgentFile(String agentClass) { + Manifest manifest = new Manifest(); + manifest.getMainAttributes().put(Attributes.Name.MANIFEST_VERSION, "1.0"); + manifest.getMainAttributes().put(new Attributes.Name("Agent-Class"), agentClass); + manifest.getMainAttributes().put(new Attributes.Name("Can-Redefine-Classes"), "true"); + + try { + File agentFile = new File(DionaPluginHooker.getInstance().getDataFolder(), "agent.jar"); + if (!agentFile.exists()) { + agentFile.createNewFile(); + } + + OutputStream outputStream = Files.newOutputStream(agentFile.toPath()); + JarOutputStream jarOutputStream = new JarOutputStream(outputStream, manifest); + jarOutputStream.putNextEntry(new JarEntry(agentClass.replace(".", "/") + ".class")); + + + ClassPool pool = ClassPool.getDefault(); + jarOutputStream.write(pool.get(agentClass).toBytecode()); + + jarOutputStream.finish(); + return agentFile; + } catch (Exception e) { + e.printStackTrace(); + return null; + } + } + + private static String getPid() { + RuntimeMXBean bean = ManagementFactory.getRuntimeMXBean(); + String pid = bean.getName(); + if (pid.contains("@")) { + pid = pid.substring(0, pid.indexOf("@")); + } + return pid; + } + + public static void attachSelf(File agentFile) throws Exception { + System.loadLibrary("attach"); + VirtualMachine vm = VirtualMachine.attach(getPid()); + vm.loadAgent(agentFile.getAbsolutePath()); + vm.detach(); + } +} \ No newline at end of file