Package de.kumpelblase2.remoteentities.utilities

Source Code of de.kumpelblase2.remoteentities.utilities.ReflectionUtil

package de.kumpelblase2.remoteentities.utilities;

import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.net.SocketAddress;
import java.util.*;
import net.minecraft.util.io.netty.channel.Channel;
import org.bukkit.Bukkit;
import de.kumpelblase2.remoteentities.RemoteEntities;
import de.kumpelblase2.remoteentities.api.thinking.Desire;
import de.kumpelblase2.remoteentities.persistence.ParameterData;
import de.kumpelblase2.remoteentities.persistence.SerializeAs;

public final class ReflectionUtil
{
  private static final Map<String, Field> s_cachedFields = new HashMap<String, Field>();

  /**
   * Replaces the goal selector of an entity with a new one
   *
   * @param inEntity      entity
   * @param inSelectorName  name of the selector (targetSelector or movementSelector)
   * @param inNewSelector    new selector
   */
  public static void replaceGoalSelector(Object inEntity, String inSelectorName, Object inNewSelector)
  {
    try
    {
      Field field = getOrRegisterField(inEntity.getClass(), inSelectorName);
      field.set(inEntity, inNewSelector);
    }
    catch(Exception e)
    {
      e.printStackTrace();
    }
  }

  /**
   * Registers custom entity class at the native minecraft entity enum.
   * Automatically clears internal maps first @see ReflectionUtil#clearEntityType(String, int)
   *
   * @param inClass  class of the entity
   * @param inName  minecraft entity name
   * @param inID    minecraft entity id
   */
  public static void registerEntityType(Class<?> inClass, String inName, int inID)
  {
    try
    {
      clearEntityType(inName, inID);
            @SuppressWarnings("rawtypes")
            Class[] args = new Class[3];
            args[0] = Class.class;
            args[1] = String.class;
            args[2] = int.class;

            Method a = getNMSClassByName("EntityTypes").getDeclaredMethod("a", args);
            a.setAccessible(true);

            a.invoke(a, inClass, inName, inID);
        }
    catch (Exception e)
    {
            e.printStackTrace();
        }
  }

  /**
   * Clears the entity name and entity id from the EntityTypes internal c and e map to allow registering of those names with different values.
   * The other maps are not touched and stay as they are.
   *
   * @param inName    The internal name of the entity
   * @param inID      The internal id of the entity
   */
  public static void clearEntityType(String inName, int inID)
  {
    try
    {
      Field cMap = getOrRegisterNMSField("EntityTypes", "c");
      Field eMap = getOrRegisterNMSField("EntityTypes", "e");
      ((Map)cMap.get(null)).remove(inName);
      ((Map)eMap.get(null)).remove(inID);
    }
    catch(Exception e)
    {
      e.printStackTrace();
    }
  }

  /**
   * Checks if the entity is jumping.
   *
   * @param inEntity  The entity to check
   * @return          True if it is, otherwise false
   */
  public static boolean isJumping(Object inEntity)
  {
    try
    {
      Field jump = getOrRegisterNMSField("EntityLiving", "bd");
      return jump.getBoolean(inEntity);
    }
    catch(Exception e)
    {
      return false;
    }
  }

  /**
   * Gets the data for the parameters of the classes' constructor
   *
   * @param inClass   The class to get the data for
   * @return          List of data for each parameter in order
   */
  public static List<ParameterData> getParameterDataForClass(Object inClass)
  {
    Class<?> clazz = inClass.getClass();
    List<ParameterData> parameters = new ArrayList<ParameterData>();
    Set<String> membersLooked = new HashSet<String>();
    while(clazz != Object.class && clazz != Desire.class)
    {
      for(Field field : clazz.getDeclaredFields())
      {
        field.setAccessible(true);
        if(membersLooked.contains(field.getName()))
          continue;

        membersLooked.add(field.getName());
        for(Annotation an : field.getAnnotations())
        {
          if(an instanceof SerializeAs)
          {
            SerializeAs sas = (SerializeAs)an;
            try
            {
              Object value = field.get(inClass);
              parameters.add(new ParameterData(Math.max(0, sas.pos() - 1), field.getType().getName(), value, sas.special()));
              break;
            }
            catch(Exception e)
            {
              RemoteEntities.getInstance().getLogger().warning("Unable to add desire parameter. " + e.getMessage());
            }
          }
        }
      }

      clazz = clazz.getSuperclass();
    }
    return parameters;
  }

  /**
   * Gets the current minecraft revision
   *
   * @return  The revision as string in the format "X.X_RX"
   */
  public static String getMinecraftRevision()
  {
    Class serverClass = Bukkit.getServer().getClass();
    String remaining = serverClass.getPackage().getName().replace("org.bukkit.craftbukkit.", "");
    return remaining.split("\\.")[0];
  }

  /**
   * Gets the nms class with the given name
   *
   * @param inName    The internal name of the class
   * @return          The class
   */
  public static Class<?> getNMSClassByName(String inName)
  {
    try
    {
      return Class.forName("net.minecraft.server." + RemoteEntities.getMinecraftRevision() + "." + inName);
    }
    catch(Exception e)
    {
      e.printStackTrace();
    }

    return null;
  }

  /**
   * Sets the new socket channel of the given manager.
   *
   * @param inManager The manager to change the channel of
   * @param inChannel The new channel
   */
  public static void setNetworkChannel(Object inManager, Channel inChannel)
  {
    try
    {
      Field channel = getOrRegisterNMSField("NetworkManager", "k");
      channel.set(inManager, inChannel);
    }
    catch(Exception e)
    {
    }
  }

  /**
   * Sets the network address of the given network manager.
   *
   * @param inManager The manager to change the address of
   * @param inAddress The new address
   */
  public static void setNetworkAddress(Object inManager, SocketAddress inAddress)
  {
    try
    {
      Field address = getOrRegisterNMSField("NetworkManager", "l");
      address.set(inManager, inAddress);
    }
    catch(Exception e)
    {
    }
  }

  /**
   * Gets a declared field of the given class and caches it.
   * If a field is not cached it will attempt to get it from the given class.
   *
   * @param inSource  The class which has the field
   * @param inField   The field name
   * @return          The field
   */
  public static Field getOrRegisterField(Class<?> inSource, String inField)
  {
    Field field;
    try
    {
      String id = inSource.getName() + "_" + inField;
      if(s_cachedFields.containsKey(id))
        field = s_cachedFields.get(id);
      else
      {
        field = inSource.getDeclaredField(inField);
        field.setAccessible(true);
        s_cachedFields.put(id, field);
      }

      return field;
    }
    catch(Exception e)
    {
      e.printStackTrace();
      return null;
    }
  }

  // We split up in two methods so we don't cause much overhead because otherwise we'd need to search for the nms class every time when calling the method
  // instead of just once when we need it
  static Field getOrRegisterNMSField(String inNMSClass, String inField)
  {
    Field field;
    try
    {
      String id = inNMSClass + "_" + inField;
      if(s_cachedFields.containsKey(id))
        field = s_cachedFields.get(id);
      else
      {
        field = getNMSClassByName(inNMSClass).getDeclaredField(inField);
        field.setAccessible(true);
        s_cachedFields.put(id, field);
      }

      return field;
    }
    catch(Exception e)
    {
      e.printStackTrace();
      return null;
    }
  }
}
TOP

Related Classes of de.kumpelblase2.remoteentities.utilities.ReflectionUtil

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.