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;
    }
  }
示例#2
0
/**
 * 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;
  }
}