mirror of
https://github.com/dmulloy2/ProtocolLib.git
synced 2024-11-24 11:36:51 +01:00
Permit cross edges when validating dependencies. FIXES 91.
This commit is contained in:
parent
6af440789c
commit
02b5dec304
@ -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.
|
||||
* <p>
|
||||
@ -183,15 +192,15 @@ class PluginVerifier {
|
||||
return Sets.newHashSet(list);
|
||||
}
|
||||
|
||||
// Avoid cycles
|
||||
private boolean hasDependency(Plugin plugin, Plugin dependency, Set<String> checked) {
|
||||
// Avoid cycles. DFS.
|
||||
private boolean hasDependency(Plugin plugin, Plugin dependency, Set<String> checking) {
|
||||
Set<String> 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;
|
||||
}
|
||||
|
@ -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<Object> {
|
||||
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<Object, Object> delegateLookup = new MapMaker().weakKeys().makeMap();
|
||||
|
||||
private transient PlayerInjector injector;
|
||||
private transient Set<Object> ignoredPackets;
|
||||
private transient ClassLoader classLoader;
|
||||
|
||||
private transient InvertedIntegerCallback callback;
|
||||
|
||||
public InjectedArrayList(ClassLoader classLoader, PlayerInjector injector, Set<Object> 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<Object> {
|
||||
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<Object, Object> delegateLookup = new MapMaker().weakKeys().makeMap();
|
||||
|
||||
private transient PlayerInjector injector;
|
||||
private transient Set<Object> ignoredPackets;
|
||||
private transient ClassLoader classLoader;
|
||||
|
||||
private transient InvertedIntegerCallback callback;
|
||||
|
||||
public InjectedArrayList(ClassLoader classLoader, PlayerInjector injector, Set<Object> 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user