private boolean tryInjectManager() { Class<?> serverClass = serverHandler.getClass(); Enhancer ex = new Enhancer(); Callback sendPacketCallback = new MethodInterceptor() { @Override public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable { Object packet = args[0]; if (packet != null) { packet = handlePacketSending(packet); // A NULL packet indicate cancelling if (packet != null) args[0] = packet; else return null; } // Call the method directly return proxy.invokeSuper(obj, args); }; }; Callback noOpCallback = NoOp.INSTANCE; // Share callback filter - that way, we avoid generating a new class for // every logged in player. if (callbackFilter == null) { callbackFilter = new CallbackFilter() { @Override public int accept(Method method) { if (method.equals(sendPacketMethod)) return 0; else return 1; } }; } ex.setClassLoader(classLoader); ex.setSuperclass(serverClass); ex.setCallbacks(new Callback[] {sendPacketCallback, noOpCallback}); ex.setCallbackFilter(callbackFilter); // Find the Minecraft NetServerHandler superclass Class<?> minecraftSuperClass = getFirstMinecraftSuperClass(serverHandler.getClass()); ExistingGenerator generator = ExistingGenerator.fromObjectFields(serverHandler, minecraftSuperClass); DefaultInstances serverInstances = null; // Maybe the proxy instance can help? Object proxyInstance = getProxyServerHandler(); // Use the existing server proxy when we create one if (proxyInstance != null && proxyInstance != serverHandler) { serverInstances = DefaultInstances.fromArray( generator, ExistingGenerator.fromObjectArray(new Object[] {proxyInstance})); } else { serverInstances = DefaultInstances.fromArray(generator); } serverInstances.setNonNull(true); serverInstances.setMaximumRecursion(1); Object proxyObject = serverInstances.forEnhancer(ex).getDefault(serverClass); // Inject it now if (proxyObject != null) { // This will be done by InjectedServerConnection instead // copyTo(serverHandler, proxyObject); serverInjection.replaceServerHandler(serverHandler, proxyObject); serverHandlerRef.setValue(proxyObject); return true; } else { return false; } }
/** * Used to construct default instances of any type. * * @author Kristian */ public class DefaultInstances { /** Standard default instance provider. */ public static DefaultInstances DEFAULT = DefaultInstances.fromArray(PrimitiveGenerator.INSTANCE, CollectionGenerator.INSTANCE); /** The maximum height of the hierachy of creates types. Used to prevent cycles. */ private static final int MAXIMUM_RECURSION = 20; /** Ordered list of instance provider, from highest priority to lowest. */ private ImmutableList<InstanceProvider> registered; /** * Construct a default instance generator using the given instance providers. * * @param registered - list of instance providers. */ public DefaultInstances(ImmutableList<InstanceProvider> registered) { this.registered = registered; } /** * Construct a default instance generator using the given instance providers. * * @param instaceProviders - array of instance providers. */ public DefaultInstances(InstanceProvider... instaceProviders) { this(ImmutableList.copyOf(instaceProviders)); } /** * Construct a default instance generator using the given instance providers. * * @param instaceProviders - array of instance providers. * @return An default instance generator. */ public static DefaultInstances fromArray(InstanceProvider... instaceProviders) { return new DefaultInstances(ImmutableList.copyOf(instaceProviders)); } /** * Retrieves a immutable list of every default object providers that generates instances. * * @return Table of instance providers. */ public ImmutableList<InstanceProvider> getRegistered() { return registered; } /** * Retrieves a default instance or value that is assignable to this type. * * <p>This includes, but isn't limited too: * * <ul> * <li>Primitive types. Returns either zero or null. * <li>Primitive wrappers. * <li>String types. Returns an empty string. * <li>Arrays. Returns a zero-length array of the same type. * <li>Enums. Returns the first declared element. * <li>Collection interfaces, such as List and Set. Returns the most appropriate empty * container. * <li>Any type with a public constructor that has parameters with defaults. * </ul> * * </ul> * * @param type - the type to construct a default value. * @return A default value/instance, or NULL if not possible. */ public <T> T getDefault(Class<T> type) { return getDefaultInternal(type, registered, 0); } /** * Retrieves a default instance or value that is assignable to this type. * * <p>This includes, but isn't limited too: * * <ul> * <li>Primitive types. Returns either zero or null. * <li>Primitive wrappers. * <li>String types. Returns an empty string. * <li>Arrays. Returns a zero-length array of the same type. * <li>Enums. Returns the first declared element. * <li>Collection interfaces, such as List and Set. Returns the most appropriate empty * container. * <li>Any type with a public constructor that has parameters with defaults. * </ul> * * </ul> * * @param type - the type to construct a default value. * @param providers - instance providers used during the * @return A default value/instance, or NULL if not possible. */ public <T> T getDefault(Class<T> type, List<InstanceProvider> providers) { return getDefaultInternal(type, providers, 0); } @SuppressWarnings("unchecked") private <T> T getDefaultInternal( Class<T> type, List<InstanceProvider> providers, int recursionLevel) { // Guard against recursion if (recursionLevel > MAXIMUM_RECURSION) { return null; } for (InstanceProvider generator : providers) { Object value = generator.create(type); if (value != null) return (T) value; } Constructor<T> minimum = null; int lastCount = Integer.MAX_VALUE; // Find the constructor with the fewest parameters for (Constructor<?> candidate : type.getConstructors()) { Class<?>[] types = candidate.getParameterTypes(); // Note that we don't allow recursive types - that is, types that // require itself in the constructor. if (types.length < lastCount) { if (!contains(types, type)) { minimum = (Constructor<T>) candidate; lastCount = types.length; // Don't loop again if we've already found the best possible constructor if (lastCount == 0) break; } } } // Create the type with this constructor using default values. This might fail, though. try { if (minimum != null) { Object[] params = new Object[lastCount]; Class<?>[] types = minimum.getParameterTypes(); // Fill out for (int i = 0; i < lastCount; i++) { params[i] = getDefaultInternal(types[i], providers, recursionLevel + 1); } return createInstance(type, minimum, types, params); } } catch (Exception e) { // Nope, we couldn't create this type } // No suitable default value could be found return null; } /** * Used by the default instance provider to create a class from a given constructor. The default * method uses reflection. * * @param type - the type to create. * @param constructor - the constructor to use. * @param types - type of each parameter in order. * @param params - value of each parameter in order. * @return The constructed instance. */ protected <T> T createInstance( Class<T> type, Constructor<T> constructor, Class<?>[] types, Object[] params) { try { return (T) constructor.newInstance(params); } catch (Exception e) { // Cannot create it return null; } } // We avoid Apache's utility methods to stay backwards compatible protected <T> boolean contains(T[] elements, T elementToFind) { // Search for the given element in the array for (T element : elements) { if (Objects.equal(elementToFind, element)) return true; } return false; } }