From 02b5dec3044a16eab7ac2cd54526236cb6fcda3a Mon Sep 17 00:00:00 2001 From: "Kristian S. Stangeland" Date: Mon, 13 May 2013 03:49:48 +0200 Subject: [PATCH] Permit cross edges when validating dependencies. FIXES 91. --- .../protocol/injector/PluginVerifier.java | 24 +- .../injector/player/InjectedArrayList.java | 387 +++++++++--------- 2 files changed, 211 insertions(+), 200 deletions(-) diff --git a/ProtocolLib/src/main/java/com/comphenix/protocol/injector/PluginVerifier.java b/ProtocolLib/src/main/java/com/comphenix/protocol/injector/PluginVerifier.java index ebb98a26..2e66cf5d 100644 --- a/ProtocolLib/src/main/java/com/comphenix/protocol/injector/PluginVerifier.java +++ b/ProtocolLib/src/main/java/com/comphenix/protocol/injector/PluginVerifier.java @@ -83,7 +83,7 @@ class PluginVerifier { * @throws PluginNotFoundException If a plugin with the given name cannot be found. */ private Plugin getPlugin(String pluginName) { - Plugin plugin = Bukkit.getPluginManager().getPlugin(pluginName); + Plugin plugin = getPluginOrDefault(pluginName); // Ensure that the plugin exists if (plugin != null) @@ -92,6 +92,15 @@ class PluginVerifier { throw new PluginNotFoundException("Cannot find plugin " + pluginName); } + /** + * Retrieve a plugin by name. + * @param pluginName - the non-null name of the plugin to retrieve. + * @return The retrieved plugin, or NULL if not found. + */ + private Plugin getPluginOrDefault(String pluginName) { + return Bukkit.getPluginManager().getPlugin(pluginName); + } + /** * Performs simple verifications on the given plugin. *

@@ -183,15 +192,15 @@ class PluginVerifier { return Sets.newHashSet(list); } - // Avoid cycles - private boolean hasDependency(Plugin plugin, Plugin dependency, Set checked) { + // Avoid cycles. DFS. + private boolean hasDependency(Plugin plugin, Plugin dependency, Set checking) { Set childNames = Sets.union( safeConversion(plugin.getDescription().getDepend()), safeConversion(plugin.getDescription().getSoftDepend()) ); // Ensure that the same plugin isn't processed twice - if (!checked.add(plugin.getName())) { + if (!checking.add(plugin.getName())) { throw new IllegalStateException("Cycle detected in dependency graph: " + plugin); } // Look for the dependency in the immediate children @@ -201,13 +210,16 @@ class PluginVerifier { // Recurse through their dependencies for (String childName : childNames) { - Plugin childPlugin = getPlugin(childName); + Plugin childPlugin = getPluginOrDefault(childName); - if (hasDependency(childPlugin, dependency, checked)) { + if (childPlugin != null && hasDependency(childPlugin, dependency, checking)) { return true; } } + // Cross edges are permitted + checking.remove(plugin.getName()); + // No dependency found! return false; } diff --git a/ProtocolLib/src/main/java/com/comphenix/protocol/injector/player/InjectedArrayList.java b/ProtocolLib/src/main/java/com/comphenix/protocol/injector/player/InjectedArrayList.java index bc4cf76f..4884dce0 100644 --- a/ProtocolLib/src/main/java/com/comphenix/protocol/injector/player/InjectedArrayList.java +++ b/ProtocolLib/src/main/java/com/comphenix/protocol/injector/player/InjectedArrayList.java @@ -1,194 +1,193 @@ -/* - * ProtocolLib - Bukkit server library that allows access to the Minecraft protocol. - * Copyright (C) 2012 Kristian S. Stangeland - * - * This program is free software; you can redistribute it and/or modify it under the terms of the - * GNU General Public License as published by the Free Software Foundation; either version 2 of - * the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; - * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along with this program; - * if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA - * 02111-1307 USA - */ - -package com.comphenix.protocol.injector.player; - -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; -import java.util.ArrayList; -import java.util.Set; -import java.util.concurrent.ConcurrentMap; - -import com.comphenix.protocol.Packets; -import com.comphenix.protocol.ProtocolLibrary; -import com.comphenix.protocol.error.Report; -import com.comphenix.protocol.error.ReportType; -import com.comphenix.protocol.injector.ListenerInvoker; -import com.comphenix.protocol.injector.player.NetworkFieldInjector.FakePacket; -import com.comphenix.protocol.utility.MinecraftReflection; -import com.google.common.collect.MapMaker; - -import net.sf.cglib.proxy.Callback; -import net.sf.cglib.proxy.Enhancer; -import net.sf.cglib.proxy.MethodInterceptor; -import net.sf.cglib.proxy.MethodProxy; - -/** - * The array list that notifies when packets are sent by the server. - * - * @author Kristian - */ -class InjectedArrayList extends ArrayList { - public static final ReportType REPORT_CANNOT_REVERT_CANCELLED_PACKET = new ReportType("Reverting cancelled packet failed."); - - /** - * Silly Eclipse. - */ - private static final long serialVersionUID = -1173865905404280990L; - - // Fake inverted proxy objects - private static ConcurrentMap delegateLookup = new MapMaker().weakKeys().makeMap(); - - private transient PlayerInjector injector; - private transient Set ignoredPackets; - private transient ClassLoader classLoader; - - private transient InvertedIntegerCallback callback; - - public InjectedArrayList(ClassLoader classLoader, PlayerInjector injector, Set ignoredPackets) { - this.classLoader = classLoader; - this.injector = injector; - this.ignoredPackets = ignoredPackets; - this.callback = new InvertedIntegerCallback(); - } - - @Override - public boolean add(Object packet) { - - Object result = null; - - // Check for fake packets and ignored packets - if (packet instanceof FakePacket) { - return true; - } else if (ignoredPackets.contains(packet)) { - // Don't send it to the filters - result = ignoredPackets.remove(packet); - } else { - result = injector.handlePacketSending(packet); - } - - // A NULL packet indicate cancelling - try { - if (result != null) { - super.add(result); - } else { - // We'll use the FakePacket marker instead of preventing the filters - injector.sendServerPacket(createNegativePacket(packet), true); - } - - // Collection.add contract - return true; - - } catch (InvocationTargetException e) { - // Prefer to report this to the user, instead of risking sending it to Minecraft - ProtocolLibrary.getErrorReporter().reportDetailed(this, - Report.newBuilder(REPORT_CANNOT_REVERT_CANCELLED_PACKET).error(e).callerParam(packet) - ); - - // Failure - return false; - } - } - - /** - * Used by a hack that reverses the effect of a cancelled packet. Returns a packet - * whereby every int method's return value is inverted (a => -a). - * - * @param source - packet to invert. - * @return The inverted packet. - */ - Object createNegativePacket(Object source) { - ListenerInvoker invoker = injector.getInvoker(); - - int packetID = invoker.getPacketID(source); - - // We want to subtract the byte amount that were added to the running - // total of outstanding packets. Otherwise, cancelling too many packets - // might cause a "disconnect.overflow" error. - // - // We do that by constructing a special packet of the same type that returns - // a negative integer for all zero-parameter integer methods. This includes the - // size() method, which is used by the queue method to count the number of - // bytes to add. - // - // Essentially, we have: - // - // public class NegativePacket extends [a packet] { - // @Override - // public int size() { - // return -super.size(); - // } - // ect. - // } - Enhancer ex = new Enhancer(); - ex.setSuperclass(MinecraftReflection.getPacketClass()); - ex.setInterfaces(new Class[] { FakePacket.class } ); - ex.setUseCache(true); - ex.setClassLoader(classLoader); - ex.setCallbackType(InvertedIntegerCallback.class); - - Class proxyClass = ex.createClass(); - Enhancer.registerCallbacks(proxyClass, new Callback[] { callback }); - - try { - // Temporarily associate the fake packet class - invoker.registerPacketClass(proxyClass, packetID); - Object proxy = proxyClass.newInstance(); - - InjectedArrayList.registerDelegate(proxy, source); - return proxy; - - } catch (Exception e) { - // Don't pollute the throws tree - throw new RuntimeException("Cannot create fake class.", e); - } finally { - // Remove this association - invoker.unregisterPacketClass(proxyClass); - } - } - - /** - * Ensure that the inverted integer proxy uses the given object as source. - * @param proxy - inverted integer proxy. - * @param source - source object. - */ - private static void registerDelegate(Object proxy, Object source) { - delegateLookup.put(proxy, source); - } - - /** - * 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 { - final Object delegate = delegateLookup.get(obj); - - if (delegate == null) { - throw new IllegalStateException("Unable to find delegate source for " + obj); - } - - if (method.getReturnType().equals(int.class) && args.length == 0) { - Integer result = (Integer) proxy.invoke(delegate, args); - return -result; - } else { - return proxy.invoke(delegate, args); - } - } - } -} +/* + * ProtocolLib - Bukkit server library that allows access to the Minecraft protocol. + * Copyright (C) 2012 Kristian S. Stangeland + * + * This program is free software; you can redistribute it and/or modify it under the terms of the + * GNU General Public License as published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with this program; + * if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + * 02111-1307 USA + */ + +package com.comphenix.protocol.injector.player; + +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.Set; +import java.util.concurrent.ConcurrentMap; + +import com.comphenix.protocol.ProtocolLibrary; +import com.comphenix.protocol.error.Report; +import com.comphenix.protocol.error.ReportType; +import com.comphenix.protocol.injector.ListenerInvoker; +import com.comphenix.protocol.injector.player.NetworkFieldInjector.FakePacket; +import com.comphenix.protocol.utility.MinecraftReflection; +import com.google.common.collect.MapMaker; + +import net.sf.cglib.proxy.Callback; +import net.sf.cglib.proxy.Enhancer; +import net.sf.cglib.proxy.MethodInterceptor; +import net.sf.cglib.proxy.MethodProxy; + +/** + * The array list that notifies when packets are sent by the server. + * + * @author Kristian + */ +class InjectedArrayList extends ArrayList { + public static final ReportType REPORT_CANNOT_REVERT_CANCELLED_PACKET = new ReportType("Reverting cancelled packet failed."); + + /** + * Silly Eclipse. + */ + private static final long serialVersionUID = -1173865905404280990L; + + // Fake inverted proxy objects + private static ConcurrentMap delegateLookup = new MapMaker().weakKeys().makeMap(); + + private transient PlayerInjector injector; + private transient Set ignoredPackets; + private transient ClassLoader classLoader; + + private transient InvertedIntegerCallback callback; + + public InjectedArrayList(ClassLoader classLoader, PlayerInjector injector, Set ignoredPackets) { + this.classLoader = classLoader; + this.injector = injector; + this.ignoredPackets = ignoredPackets; + this.callback = new InvertedIntegerCallback(); + } + + @Override + public boolean add(Object packet) { + + Object result = null; + + // Check for fake packets and ignored packets + if (packet instanceof FakePacket) { + return true; + } else if (ignoredPackets.contains(packet)) { + // Don't send it to the filters + result = ignoredPackets.remove(packet); + } else { + result = injector.handlePacketSending(packet); + } + + // A NULL packet indicate cancelling + try { + if (result != null) { + super.add(result); + } else { + // We'll use the FakePacket marker instead of preventing the filters + injector.sendServerPacket(createNegativePacket(packet), true); + } + + // Collection.add contract + return true; + + } catch (InvocationTargetException e) { + // Prefer to report this to the user, instead of risking sending it to Minecraft + ProtocolLibrary.getErrorReporter().reportDetailed(this, + Report.newBuilder(REPORT_CANNOT_REVERT_CANCELLED_PACKET).error(e).callerParam(packet) + ); + + // Failure + return false; + } + } + + /** + * Used by a hack that reverses the effect of a cancelled packet. Returns a packet + * whereby every int method's return value is inverted (a => -a). + * + * @param source - packet to invert. + * @return The inverted packet. + */ + Object createNegativePacket(Object source) { + ListenerInvoker invoker = injector.getInvoker(); + + int packetID = invoker.getPacketID(source); + + // We want to subtract the byte amount that were added to the running + // total of outstanding packets. Otherwise, cancelling too many packets + // might cause a "disconnect.overflow" error. + // + // We do that by constructing a special packet of the same type that returns + // a negative integer for all zero-parameter integer methods. This includes the + // size() method, which is used by the queue method to count the number of + // bytes to add. + // + // Essentially, we have: + // + // public class NegativePacket extends [a packet] { + // @Override + // public int size() { + // return -super.size(); + // } + // ect. + // } + Enhancer ex = new Enhancer(); + ex.setSuperclass(MinecraftReflection.getPacketClass()); + ex.setInterfaces(new Class[] { FakePacket.class } ); + ex.setUseCache(true); + ex.setClassLoader(classLoader); + ex.setCallbackType(InvertedIntegerCallback.class); + + Class proxyClass = ex.createClass(); + Enhancer.registerCallbacks(proxyClass, new Callback[] { callback }); + + try { + // Temporarily associate the fake packet class + invoker.registerPacketClass(proxyClass, packetID); + Object proxy = proxyClass.newInstance(); + + InjectedArrayList.registerDelegate(proxy, source); + return proxy; + + } catch (Exception e) { + // Don't pollute the throws tree + throw new RuntimeException("Cannot create fake class.", e); + } finally { + // Remove this association + invoker.unregisterPacketClass(proxyClass); + } + } + + /** + * Ensure that the inverted integer proxy uses the given object as source. + * @param proxy - inverted integer proxy. + * @param source - source object. + */ + private static void registerDelegate(Object proxy, Object source) { + delegateLookup.put(proxy, source); + } + + /** + * 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 { + final Object delegate = delegateLookup.get(obj); + + if (delegate == null) { + throw new IllegalStateException("Unable to find delegate source for " + obj); + } + + if (method.getReturnType().equals(int.class) && args.length == 0) { + Integer result = (Integer) proxy.invoke(delegate, args); + return -result; + } else { + return proxy.invoke(delegate, args); + } + } + } +}