Fixed a serious misuse causing the creation of too many new classes.
The different injectors using CGLib was using a custom CallbackFilter to optimize the resulting generated class. Unfortunately, the custom filter didn't properly implement equals() and hashCode(), and so every time a player logged, a new injector class had to be generated. This was fixed by making the injectors share a single CallbackFilter. In addition, I've begun adding cleanup code that will reset all static fields once the plugin has unloaded.
Dieser Commit ist enthalten in:
Ursprung
eaf0b73c00
Commit
bdc41221db
@ -4,7 +4,7 @@
|
|||||||
<groupId>com.comphenix.protocol</groupId>
|
<groupId>com.comphenix.protocol</groupId>
|
||||||
<artifactId>ProtocolLib</artifactId>
|
<artifactId>ProtocolLib</artifactId>
|
||||||
<name>ProtocolLib</name>
|
<name>ProtocolLib</name>
|
||||||
<version>1.4.3-SNAPSHOT</version>
|
<version>1.4.4-SNAPSHOT</version>
|
||||||
<description>Provides read/write access to the Minecraft protocol.</description>
|
<description>Provides read/write access to the Minecraft protocol.</description>
|
||||||
<url>http://dev.bukkit.org/server-mods/protocollib/</url>
|
<url>http://dev.bukkit.org/server-mods/protocollib/</url>
|
||||||
<developers>
|
<developers>
|
||||||
|
@ -190,6 +190,10 @@ public class ProtocolLibrary extends JavaPlugin {
|
|||||||
protocolManager.close();
|
protocolManager.close();
|
||||||
protocolManager = null;
|
protocolManager = null;
|
||||||
statistisc = null;
|
statistisc = null;
|
||||||
|
|
||||||
|
// Leaky ClassLoader begone!
|
||||||
|
CleanupStaticMembers cleanup = new CleanupStaticMembers(getClassLoader(), logger);
|
||||||
|
cleanup.resetAll();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -54,7 +54,7 @@ public class AsyncListenerHandler {
|
|||||||
private static final AtomicInteger nextID = new AtomicInteger();
|
private static final AtomicInteger nextID = new AtomicInteger();
|
||||||
|
|
||||||
// Default queue capacity
|
// Default queue capacity
|
||||||
private static int DEFAULT_CAPACITY = 1024;
|
private static final int DEFAULT_CAPACITY = 1024;
|
||||||
|
|
||||||
// Cancel the async handler
|
// Cancel the async handler
|
||||||
private volatile boolean cancelled;
|
private volatile boolean cancelled;
|
||||||
|
@ -41,7 +41,7 @@ public class PacketConstructor {
|
|||||||
* <p>
|
* <p>
|
||||||
* Remember to call withPacket().
|
* Remember to call withPacket().
|
||||||
*/
|
*/
|
||||||
public static final PacketConstructor DEFAULT = new PacketConstructor(null);
|
public static PacketConstructor DEFAULT = new PacketConstructor(null);
|
||||||
|
|
||||||
// The constructor method that's actually responsible for creating the packet
|
// The constructor method that's actually responsible for creating the packet
|
||||||
private Constructor<?> constructorMethod;
|
private Constructor<?> constructorMethod;
|
||||||
|
@ -51,6 +51,9 @@ class NetworkObjectInjector extends PlayerInjector {
|
|||||||
// Used to construct proxy objects
|
// Used to construct proxy objects
|
||||||
private ClassLoader classLoader;
|
private ClassLoader classLoader;
|
||||||
|
|
||||||
|
// Shared callback filter - avoid creating a new class every time
|
||||||
|
private static CallbackFilter callbackFilter;
|
||||||
|
|
||||||
public NetworkObjectInjector(ClassLoader classLoader, Logger logger, Player player,
|
public NetworkObjectInjector(ClassLoader classLoader, Logger logger, Player player,
|
||||||
ListenerInvoker invoker, IntegerSet sendingFilters) throws IllegalAccessException {
|
ListenerInvoker invoker, IntegerSet sendingFilters) throws IllegalAccessException {
|
||||||
super(logger, player, invoker);
|
super(logger, player, invoker);
|
||||||
@ -131,12 +134,9 @@ class NetworkObjectInjector extends PlayerInjector {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Create our proxy object
|
// Share callback filter - that way, we avoid generating a new class every time.
|
||||||
Enhancer ex = new Enhancer();
|
if (callbackFilter == null) {
|
||||||
ex.setClassLoader(classLoader);
|
callbackFilter = new CallbackFilter() {
|
||||||
ex.setSuperclass(networkInterface);
|
|
||||||
ex.setCallbacks(new Callback[] { queueFilter, dispatch });
|
|
||||||
ex.setCallbackFilter(new CallbackFilter() {
|
|
||||||
@Override
|
@Override
|
||||||
public int accept(Method method) {
|
public int accept(Method method) {
|
||||||
if (method.equals(queueMethod))
|
if (method.equals(queueMethod))
|
||||||
@ -144,7 +144,15 @@ class NetworkObjectInjector extends PlayerInjector {
|
|||||||
else
|
else
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
});
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create our proxy object
|
||||||
|
Enhancer ex = new Enhancer();
|
||||||
|
ex.setClassLoader(classLoader);
|
||||||
|
ex.setSuperclass(networkInterface);
|
||||||
|
ex.setCallbacks(new Callback[] { queueFilter, dispatch });
|
||||||
|
ex.setCallbackFilter(callbackFilter);
|
||||||
|
|
||||||
// Inject it, if we can.
|
// Inject it, if we can.
|
||||||
networkManagerRef.setValue(ex.create());
|
networkManagerRef.setValue(ex.create());
|
||||||
|
@ -52,6 +52,8 @@ import com.comphenix.protocol.reflect.instances.ExistingGenerator;
|
|||||||
*/
|
*/
|
||||||
public class NetworkServerInjector extends PlayerInjector {
|
public class NetworkServerInjector extends PlayerInjector {
|
||||||
|
|
||||||
|
private volatile static CallbackFilter callbackFilter;
|
||||||
|
|
||||||
private static Field disconnectField;
|
private static Field disconnectField;
|
||||||
private static Method sendPacketMethod;
|
private static Method sendPacketMethod;
|
||||||
private InjectedServerConnection serverInjection;
|
private InjectedServerConnection serverInjection;
|
||||||
@ -170,10 +172,10 @@ public class NetworkServerInjector extends PlayerInjector {
|
|||||||
};
|
};
|
||||||
Callback noOpCallback = NoOp.INSTANCE;
|
Callback noOpCallback = NoOp.INSTANCE;
|
||||||
|
|
||||||
ex.setClassLoader(classLoader);
|
// Share callback filter - that way, we avoid generating a new class for
|
||||||
ex.setSuperclass(serverClass);
|
// every logged in player.
|
||||||
ex.setCallbacks(new Callback[] { sendPacketCallback, noOpCallback });
|
if (callbackFilter == null) {
|
||||||
ex.setCallbackFilter(new CallbackFilter() {
|
callbackFilter = new CallbackFilter() {
|
||||||
@Override
|
@Override
|
||||||
public int accept(Method method) {
|
public int accept(Method method) {
|
||||||
if (method.equals(sendPacketMethod))
|
if (method.equals(sendPacketMethod))
|
||||||
@ -181,7 +183,13 @@ public class NetworkServerInjector extends PlayerInjector {
|
|||||||
else
|
else
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
});
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
ex.setClassLoader(classLoader);
|
||||||
|
ex.setSuperclass(serverClass);
|
||||||
|
ex.setCallbacks(new Callback[] { sendPacketCallback, noOpCallback });
|
||||||
|
ex.setCallbackFilter(callbackFilter);
|
||||||
|
|
||||||
// Find the Minecraft NetServerHandler superclass
|
// Find the Minecraft NetServerHandler superclass
|
||||||
Class<?> minecraftSuperClass = getFirstMinecraftSuperClass(serverHandler.getClass());
|
Class<?> minecraftSuperClass = getFirstMinecraftSuperClass(serverHandler.getClass());
|
||||||
@ -208,6 +216,7 @@ public class NetworkServerInjector extends PlayerInjector {
|
|||||||
if (proxyObject != null) {
|
if (proxyObject != null) {
|
||||||
// This will be done by InjectedServerConnection instead
|
// This will be done by InjectedServerConnection instead
|
||||||
//copyTo(serverHandler, proxyObject);
|
//copyTo(serverHandler, proxyObject);
|
||||||
|
|
||||||
serverInjection.replaceServerHandler(serverHandler, proxyObject);
|
serverInjection.replaceServerHandler(serverHandler, proxyObject);
|
||||||
serverHandlerRef.setValue(proxyObject);
|
serverHandlerRef.setValue(proxyObject);
|
||||||
return true;
|
return true;
|
||||||
|
@ -42,6 +42,9 @@ class TemporaryPlayerFactory {
|
|||||||
// Helpful constructors
|
// Helpful constructors
|
||||||
private final PacketConstructor chatPacket;
|
private final PacketConstructor chatPacket;
|
||||||
|
|
||||||
|
// Prevent too many class creations
|
||||||
|
private static CallbackFilter callbackFilter;
|
||||||
|
|
||||||
public TemporaryPlayerFactory() {
|
public TemporaryPlayerFactory() {
|
||||||
chatPacket = PacketConstructor.DEFAULT.withPacket(3, new Object[] { "DEMO" });
|
chatPacket = PacketConstructor.DEFAULT.withPacket(3, new Object[] { "DEMO" });
|
||||||
}
|
}
|
||||||
@ -120,12 +123,9 @@ class TemporaryPlayerFactory {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// CGLib is amazing
|
// Shared callback filter
|
||||||
Enhancer ex = new Enhancer();
|
if (callbackFilter == null) {
|
||||||
ex.setSuperclass(InjectContainer.class);
|
callbackFilter = new CallbackFilter() {
|
||||||
ex.setInterfaces(new Class[] { Player.class });
|
|
||||||
ex.setCallbacks(new Callback[] { NoOp.INSTANCE, implementation });
|
|
||||||
ex.setCallbackFilter(new CallbackFilter() {
|
|
||||||
@Override
|
@Override
|
||||||
public int accept(Method method) {
|
public int accept(Method method) {
|
||||||
// Do not override the object method or the superclass methods
|
// Do not override the object method or the superclass methods
|
||||||
@ -135,7 +135,15 @@ class TemporaryPlayerFactory {
|
|||||||
else
|
else
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
});
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// CGLib is amazing
|
||||||
|
Enhancer ex = new Enhancer();
|
||||||
|
ex.setSuperclass(InjectContainer.class);
|
||||||
|
ex.setInterfaces(new Class[] { Player.class });
|
||||||
|
ex.setCallbacks(new Callback[] { NoOp.INSTANCE, implementation });
|
||||||
|
ex.setCallbackFilter(callbackFilter);
|
||||||
|
|
||||||
return (Player) ex.create();
|
return (Player) ex.create();
|
||||||
}
|
}
|
||||||
|
In neuem Issue referenzieren
Einen Benutzer sperren