Archiviert
13
0

Prevented map chunk listeners from crashing the server.

If the NetServerHandler injector fails and we revert to the network
field injector instead, any map chunk listener will crash the server
due to complications with Bukkit's ChunkCompressionThread. 

We avoid this by disabling map chunk listening entirely.
Dieser Commit ist enthalten in:
Kristian S. Stangeland 2012-10-29 16:54:53 +01:00
Ursprung 8636215b98
Commit cbf4def71b
12 geänderte Dateien mit 177 neuen und 47 gelöschten Zeilen

Datei anzeigen

@ -43,7 +43,7 @@ class CleanupStaticMembers {
// This list must always be updated // This list must always be updated
Class<?>[] publicClasses = { Class<?>[] publicClasses = {
AsyncListenerHandler.class, ListeningWhitelist.class, PacketContainer.class, AsyncListenerHandler.class, ListeningWhitelist.class, PacketContainer.class,
BukkitUnwrapper.class, CollectionGenerator.class, DefaultInstances.class, BukkitUnwrapper.class, DefaultInstances.class, CollectionGenerator.class,
PrimitiveGenerator.class, FuzzyReflection.class, MethodUtils.class, PrimitiveGenerator.class, FuzzyReflection.class, MethodUtils.class,
BackgroundCompiler.class, StructureCompiler.class, BackgroundCompiler.class, StructureCompiler.class,
ObjectCloner.class, PrimitiveUtils.class, Packets.Server.class, ObjectCloner.class, PrimitiveUtils.class, Packets.Server.class,

Datei anzeigen

@ -46,4 +46,25 @@ public interface ListenerInvoker {
* @return The packet ID. * @return The packet ID.
*/ */
public abstract int getPacketID(Packet packet); public abstract int getPacketID(Packet packet);
/**
* Associate a given class with the given packet ID. Internal method.
* @param clazz - class to associate.
* @param packetID - the packet ID.
*/
public abstract void unregisterPacketClass(Class<?> clazz);
/**
* Remove a given class from the packet registry. Internal method.
* @param clazz - class to remove.
*/
public abstract void registerPacketClass(Class<?> clazz, int packetID);
/**
* Retrieves the correct packet class from a given packet ID.
* @param packetID - the packet ID.
* @param forceVanilla - whether or not to look for vanilla classes, not injected classes.
* @return The associated class.
*/
public abstract Class<?> getPacketClassFromID(int packetID, boolean forceVanilla);
} }

Datei anzeigen

@ -157,7 +157,7 @@ class MinecraftRegistry {
/** /**
* Retrieves the correct packet class from a given packet ID. * Retrieves the correct packet class from a given packet ID.
* @param packetID - the packet ID. * @param packetID - the packet ID.
* @param vanilla - whether or not to look for vanilla classes, not injected classes. * @param forceVanilla - whether or not to look for vanilla classes, not injected classes.
* @return The associated class. * @return The associated class.
*/ */
public static Class getPacketClassFromID(int packetID, boolean forceVanilla) { public static Class getPacketClassFromID(int packetID, boolean forceVanilla) {
@ -172,7 +172,9 @@ class MinecraftRegistry {
// Will most likely not be used // Will most likely not be used
for (Map.Entry<Class, Integer> entry : getPacketToID().entrySet()) { for (Map.Entry<Class, Integer> entry : getPacketToID().entrySet()) {
if (Objects.equal(entry.getValue(), packetID)) { if (Objects.equal(entry.getValue(), packetID)) {
return entry.getKey(); // Attempt to get the vanilla class here too
if (!forceVanilla || entry.getKey().getName().startsWith("net.minecraft.server"))
return entry.getKey();
} }
} }

Datei anzeigen

@ -174,7 +174,7 @@ public final class PacketFilterManager implements ProtocolManager, ListenerInvok
try { try {
// Initialize injection mangers // Initialize injection mangers
this.playerInjection = new PlayerInjectionHandler(classLoader, reporter, isInjectionNecessary, this, server); this.playerInjection = new PlayerInjectionHandler(classLoader, reporter, isInjectionNecessary, this, packetListeners, server);
this.packetInjector = new PacketInjector(classLoader, this, playerInjection); this.packetInjector = new PacketInjector(classLoader, this, playerInjection);
this.asyncFilterManager = new AsyncFilterManager(reporter, server.getScheduler(), this); this.asyncFilterManager = new AsyncFilterManager(reporter, server.getScheduler(), this);
@ -210,9 +210,6 @@ public final class PacketFilterManager implements ProtocolManager, ListenerInvok
*/ */
public void setPlayerHook(PlayerInjectHooks playerHook) { public void setPlayerHook(PlayerInjectHooks playerHook) {
playerInjection.setPlayerHook(playerHook); playerInjection.setPlayerHook(playerHook);
// Make sure the current listeners are compatible
playerInjection.checkListener(packetListeners);
} }
@Override @Override
@ -662,6 +659,21 @@ public final class PacketFilterManager implements ProtocolManager, ListenerInvok
return MinecraftRegistry.getPacketToID().get(packet.getClass()); return MinecraftRegistry.getPacketToID().get(packet.getClass());
} }
@Override
public void registerPacketClass(Class<?> clazz, int packetID) {
MinecraftRegistry.getPacketToID().put(clazz, packetID);
}
@Override
public void unregisterPacketClass(Class<?> clazz) {
MinecraftRegistry.getPacketToID().remove(clazz);
}
@Override
public Class<?> getPacketClassFromID(int packetID, boolean forceVanilla) {
return MinecraftRegistry.getPacketClassFromID(packetID, forceVanilla);
}
// Yes, this is crazy. // Yes, this is crazy.
@SuppressWarnings({ "unchecked", "rawtypes" }) @SuppressWarnings({ "unchecked", "rawtypes" })
private void registerOld(PluginManager manager, Plugin plugin) { private void registerOld(PluginManager manager, Plugin plugin) {

Datei anzeigen

@ -141,10 +141,10 @@ class PacketInjector {
try { try {
// Override values // Override values
putMethod.invoke(intHashMap, packetID, proxy);
previous.put(packetID, old); previous.put(packetID, old);
registry.put(proxy, packetID); registry.put(proxy, packetID);
overwritten.put(packetID, proxy); overwritten.put(packetID, proxy);
putMethod.invoke(intHashMap, packetID, proxy);
return true; return true;
} catch (IllegalArgumentException e) { } catch (IllegalArgumentException e) {

Datei anzeigen

@ -22,6 +22,8 @@ import java.lang.reflect.Method;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Set; import java.util.Set;
import com.comphenix.protocol.Packets;
import com.comphenix.protocol.injector.ListenerInvoker;
import com.comphenix.protocol.injector.player.NetworkFieldInjector.FakePacket; import com.comphenix.protocol.injector.player.NetworkFieldInjector.FakePacket;
import net.minecraft.server.Packet; import net.minecraft.server.Packet;
@ -73,7 +75,7 @@ class InjectedArrayList extends ArrayList<Packet> {
// We'll use the FakePacket marker instead of preventing the filters // We'll use the FakePacket marker instead of preventing the filters
injector.sendServerPacket(createNegativePacket(packet), true); injector.sendServerPacket(createNegativePacket(packet), true);
} }
// Collection.add contract // Collection.add contract
return true; return true;
@ -90,8 +92,10 @@ class InjectedArrayList extends ArrayList<Packet> {
* @return The inverted packet. * @return The inverted packet.
*/ */
Packet createNegativePacket(Packet source) { Packet createNegativePacket(Packet source) {
Enhancer ex = new Enhancer(); ListenerInvoker invoker = injector.getInvoker();
Class<?> type = source.getClass();
int packetID = invoker.getPacketID(source);
Class<?> type = invoker.getPacketClassFromID(packetID, true);
// We want to subtract the byte amount that were added to the running // We want to subtract the byte amount that were added to the running
// total of outstanding packets. Otherwise, cancelling too many packets // total of outstanding packets. Otherwise, cancelling too many packets
@ -111,22 +115,38 @@ class InjectedArrayList extends ArrayList<Packet> {
// } // }
// ect. // ect.
// } // }
Enhancer ex = new Enhancer();
ex.setSuperclass(type);
ex.setInterfaces(new Class[] { FakePacket.class } ); ex.setInterfaces(new Class[] { FakePacket.class } );
ex.setUseCache(true); ex.setUseCache(true);
ex.setClassLoader(classLoader); ex.setClassLoader(classLoader);
ex.setSuperclass(type); ex.setCallbackType(InvertedIntegerCallback.class);
ex.setCallback(new MethodInterceptor() {
@Override Class<?> proxyClass = ex.createClass();
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
if (method.getReturnType().equals(int.class) && args.length == 0) { // Temporarily associate the fake packet class
Integer result = (Integer) proxy.invokeSuper(obj, args); invoker.registerPacketClass(proxyClass, packetID);
return -result;
} else { Packet fake = (Packet) Enhancer.create(proxyClass, new InvertedIntegerCallback());
return proxy.invokeSuper(obj, args);
}
}
});
return (Packet) ex.create(); // Remove this association
invoker.unregisterPacketClass(proxyClass);
return fake;
}
/**
* Inverts the integer result of every integer method.
* @author Kristian
*/
private class InvertedIntegerCallback implements MethodInterceptor {
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
if (method.getReturnType().equals(int.class) && args.length == 0) {
Integer result = (Integer) proxy.invokeSuper(obj, args);
return -result;
} else {
return proxy.invokeSuper(obj, args);
}
}
} }
} }

Datei anzeigen

@ -123,11 +123,14 @@ class NetworkFieldInjector extends PlayerInjector {
} }
@Override @Override
public void checkListener(PacketListener listener) { public UnsupportedListener checkListener(PacketListener listener) {
int[] unsupported = { Packets.Server.MAP_CHUNK, Packets.Server.MAP_CHUNK_BULK };
// Unfortunately, we don't support chunk packets // Unfortunately, we don't support chunk packets
if (ListeningWhitelist.containsAny(listener.getSendingWhitelist(), if (ListeningWhitelist.containsAny(listener.getSendingWhitelist(), unsupported)) {
Packets.Server.MAP_CHUNK, Packets.Server.MAP_CHUNK_BULK)) { return new UnsupportedListener("The NETWORK_FIELD_INJECTOR hook doesn't support map chunk listeners.", unsupported);
throw new IllegalStateException("The NETWORK_FIELD_INJECTOR hook doesn't support map chunk listeners."); } else {
return null;
} }
} }

Datei anzeigen

@ -88,11 +88,14 @@ class NetworkObjectInjector extends PlayerInjector {
} }
@Override @Override
public void checkListener(PacketListener listener) { public UnsupportedListener checkListener(PacketListener listener) {
int[] unsupported = { Packets.Server.MAP_CHUNK, Packets.Server.MAP_CHUNK_BULK };
// Unfortunately, we don't support chunk packets // Unfortunately, we don't support chunk packets
if (ListeningWhitelist.containsAny(listener.getSendingWhitelist(), if (ListeningWhitelist.containsAny(listener.getSendingWhitelist(), unsupported)) {
Packets.Server.MAP_CHUNK, Packets.Server.MAP_CHUNK_BULK)) { return new UnsupportedListener("The NETWORK_OBJECT_INJECTOR hook doesn't support map chunk listeners.", unsupported);
throw new IllegalStateException("The NETWORK_FIELD_INJECTOR hook doesn't support map chunk listeners."); } else {
return null;
} }
} }

Datei anzeigen

@ -301,8 +301,9 @@ public class NetworkServerInjector extends PlayerInjector {
} }
@Override @Override
public void checkListener(PacketListener listener) { public UnsupportedListener checkListener(PacketListener listener) {
// We support everything // We support everything
return null;
} }
@Override @Override

Datei anzeigen

@ -83,6 +83,9 @@ public class PlayerInjectionHandler {
// Enabled packet filters // Enabled packet filters
private IntegerSet sendingFilters = new IntegerSet(MAXIMUM_PACKET_ID + 1); private IntegerSet sendingFilters = new IntegerSet(MAXIMUM_PACKET_ID + 1);
// List of packet listeners
private Set<PacketListener> packetListeners;
// The class loader we're using // The class loader we're using
private ClassLoader classLoader; private ClassLoader classLoader;
@ -90,12 +93,13 @@ public class PlayerInjectionHandler {
private Predicate<GamePhase> injectionFilter; private Predicate<GamePhase> injectionFilter;
public PlayerInjectionHandler(ClassLoader classLoader, ErrorReporter reporter, Predicate<GamePhase> injectionFilter, public PlayerInjectionHandler(ClassLoader classLoader, ErrorReporter reporter, Predicate<GamePhase> injectionFilter,
ListenerInvoker invoker, Server server) { ListenerInvoker invoker, Set<PacketListener> packetListeners, Server server) {
this.classLoader = classLoader; this.classLoader = classLoader;
this.reporter = reporter; this.reporter = reporter;
this.invoker = invoker; this.invoker = invoker;
this.injectionFilter = injectionFilter; this.injectionFilter = injectionFilter;
this.packetListeners = packetListeners;
this.netLoginInjector = new NetLoginInjector(reporter, this, server); this.netLoginInjector = new NetLoginInjector(reporter, this, server);
this.serverInjection = new InjectedServerConnection(reporter, server, netLoginInjector); this.serverInjection = new InjectedServerConnection(reporter, server, netLoginInjector);
serverInjection.injectList(); serverInjection.injectList();
@ -143,6 +147,9 @@ public class PlayerInjectionHandler {
loginPlayerHook = playerHook; loginPlayerHook = playerHook;
if (phase.hasPlaying()) if (phase.hasPlaying())
playingPlayerHook = playerHook; playingPlayerHook = playerHook;
// Make sure the current listeners are compatible
checkListener(packetListeners);
} }
/** /**
@ -534,26 +541,30 @@ public class PlayerInjectionHandler {
// Make sure the current listeners are compatible // Make sure the current listeners are compatible
if (lastSuccessfulHook != null) { if (lastSuccessfulHook != null) {
for (PacketListener listener : listeners) { for (PacketListener listener : listeners) {
try { checkListener(listener);
checkListener(listener);
} catch (IllegalStateException e) {
reporter.reportWarning(this, "Unsupported listener.", e);
}
} }
} }
} }
/** /**
* Determine if a listener is valid or not. * Determine if a listener is valid or not.
* <p>
* If not, a warning will be printed to the console.
* @param listener - listener to check. * @param listener - listener to check.
* @throws IllegalStateException If the given listener's whitelist cannot be fulfilled.
*/ */
public void checkListener(PacketListener listener) { public void checkListener(PacketListener listener) {
try { if (lastSuccessfulHook != null) {
if (lastSuccessfulHook != null) UnsupportedListener result = lastSuccessfulHook.checkListener(listener);
lastSuccessfulHook.checkListener(listener);
} catch (Exception e) { // We won't prevent the listener, as it may still have valid packets
throw new IllegalStateException("Registering listener " + PacketAdapter.getPluginName(listener) + " failed", e); if (result != null) {
reporter.reportWarning(this, "Cannot fully register listener for " +
PacketAdapter.getPluginName(listener) + ": " + result.toString());
// These are illegal
for (int packetID : result.getPackets())
removePacketHandler(packetID);
}
} }
} }

Datei anzeigen

@ -489,10 +489,12 @@ abstract class PlayerInjector {
/** /**
* Invoked before a new listener is registered. * Invoked before a new listener is registered.
* <p> * <p>
* The player injector should throw an exception if this listener cannot be properly supplied with packet events. * The player injector should only return a non-null value if some or all of the packet IDs are unsupported.
*
* @param listener - the listener that is about to be registered. * @param listener - the listener that is about to be registered.
* @return A error message with the unsupported packet IDs, or NULL if this listener is valid.
*/ */
public abstract void checkListener(PacketListener listener); public abstract UnsupportedListener checkListener(PacketListener listener);
/** /**
* Allows a packet to be sent by the listeners. * Allows a packet to be sent by the listeners.
@ -589,6 +591,14 @@ abstract class PlayerInjector {
return player; return player;
} }
/**
* Object that can invoke the packet events.
* @return Packet event invoker.
*/
public ListenerInvoker getInvoker() {
return invoker;
}
/** /**
* Retrieve the hooked player object OR the more up-to-date player instance. * Retrieve the hooked player object OR the more up-to-date player instance.
* @return The hooked player, or a more up-to-date instance. * @return The hooked player, or a more up-to-date instance.

Datei anzeigen

@ -0,0 +1,47 @@
package com.comphenix.protocol.injector.player;
import java.util.Arrays;
import com.google.common.base.Joiner;
/**
* Represents an error message from a player injector.
*
* @author Kristian
*/
public class UnsupportedListener {
private String message;
private int[] packets;
/**
* Create a new error message.
* @param message - the message.
* @param packets - unsupported packets.
*/
public UnsupportedListener(String message, int[] packets) {
super();
this.message = message;
this.packets = packets;
}
/**
* Retrieve the error message.
* @return Error message.
*/
public String getMessage() {
return message;
}
/**
* Retrieve all unsupported packets.
* @return Unsupported packets.
*/
public int[] getPackets() {
return packets;
}
@Override
public String toString() {
return String.format("%s (%s)", message, Joiner.on(", ").join(Arrays.asList(packets)));
}
}