Quick getEntity Method and ReflectionUtil improvements

This commit is contained in:
Olof Larsson 2016-01-19 23:09:00 +01:00
parent 8dbded4192
commit 89eea76365
5 changed files with 351 additions and 74 deletions

View File

@ -147,14 +147,13 @@ public class EngineMassiveCoreCommandRegistration extends EngineAbstract
public static SimpleCommandMap getSimpleCommandMap()
{
Server server = Bukkit.getServer();
return (SimpleCommandMap) ReflectionUtil.getField(SERVER_DOT_COMMAND_MAP, server);
return ReflectionUtil.getField(SERVER_DOT_COMMAND_MAP, server);
}
protected static Field SIMPLE_COMMAND_MAP_DOT_KNOWN_COMMANDS = ReflectionUtil.getField(SimpleCommandMap.class, "knownCommands");
@SuppressWarnings("unchecked")
public static Map<String, Command> getSimpleCommandMapDotKnownCommands(SimpleCommandMap simpleCommandMap)
{
return (Map<String, Command>) ReflectionUtil.getField(SIMPLE_COMMAND_MAP_DOT_KNOWN_COMMANDS, simpleCommandMap);
return ReflectionUtil.getField(SIMPLE_COMMAND_MAP_DOT_KNOWN_COMMANDS, simpleCommandMap);
}
// -------------------------------------------- //

View File

@ -0,0 +1,145 @@
package com.massivecraft.massivecore.nms;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Map;
import java.util.UUID;
import java.util.WeakHashMap;
import org.bukkit.Bukkit;
import org.bukkit.World;
import org.bukkit.entity.Entity;
import com.massivecraft.massivecore.particleeffect.ReflectionUtils.PackageType;
import com.massivecraft.massivecore.util.ReflectionUtil;
public class NmsEntity extends NmsAbstract
{
// -------------------------------------------- //
// INSTANCE & CONSTRUCT
// -------------------------------------------- //
private static NmsEntity i = new NmsEntity();
public static NmsEntity get () { return i; }
// -------------------------------------------- //
// FIELDS
// -------------------------------------------- //
// org.bukkit.craftbukkit.CraftWorld#world
public static Class<?> classCraftWorld;
public static Field fieldCraftWorldDotWorld;
// net.minecraft.server.WorldServer#entitiesByUUID
public static Class<?> classWorldServer;
public static Field fieldWorldServerDotEntitiesByUUID;
// net.minecraft.server.Entity#getBukkitEntity()
public static Class<?> classEntity;
public static Method methodEntityDotGetBukkitEntity;
// -------------------------------------------- //
// OVERRIDE
// -------------------------------------------- //
// NOTE: This has been properly researched.
// NOTE: The field "WorldServer.entitiesByUUID" was added in 1.8.
@Override
public int getRequiredVersion()
{
return 8;
}
@Override
protected void setup() throws Throwable
{
classCraftWorld = PackageType.CRAFTBUKKIT.getClass("CraftWorld");
fieldCraftWorldDotWorld = ReflectionUtil.getField(classCraftWorld, "world");
classWorldServer = PackageType.MINECRAFT_SERVER.getClass("WorldServer");
fieldWorldServerDotEntitiesByUUID = ReflectionUtil.getField(classWorldServer, "entitiesByUUID");
classEntity = PackageType.MINECRAFT_SERVER.getClass("Entity");
methodEntityDotGetBukkitEntity = ReflectionUtil.getMethod(classEntity, "getBukkitEntity");
}
// -------------------------------------------- //
// REFLECTION METHODS > WORLD MAP
// -------------------------------------------- //
public Object getNmsWorld(World world)
{
if (world == null) throw new NullPointerException("world");
return ReflectionUtil.getField(fieldCraftWorldDotWorld, world);
}
public Map<UUID, Object> getNmsWorldMap(Object nmsWorld)
{
if (nmsWorld == null) throw new NullPointerException("nmsWorld");
return ReflectionUtil.getField(fieldWorldServerDotEntitiesByUUID, nmsWorld);
}
// -------------------------------------------- //
// REFLECTION METHODS > ENTITY
// -------------------------------------------- //
public Entity getBukkitEntity(Object nmsEntity)
{
if (nmsEntity == null) throw new NullPointerException("nmsEntity");
return ReflectionUtil.invokeMethod(methodEntityDotGetBukkitEntity, nmsEntity);
}
// -------------------------------------------- //
// WORLD MAP
// -------------------------------------------- //
protected Map<World, Map<UUID, Object>> worldMaps = new WeakHashMap<World, Map<UUID, Object>>();
public Map<UUID, Object> getWorldMap(World world)
{
if (world == null) throw new NullPointerException("world");
Map<UUID, Object> ret = this.worldMaps.get(world);
if (ret == null)
{
Object nmsWorld = this.getNmsWorld(world);
ret = this.getNmsWorldMap(nmsWorld);
this.worldMaps.put(world, ret);
}
return ret;
}
// -------------------------------------------- //
// GET ENTITY
// -------------------------------------------- //
public Entity getEntity(World world, UUID uuid)
{
if (world == null) throw new NullPointerException("world");
if (uuid == null) return null;
Map<UUID, Object> worldMap = this.getWorldMap(world);
Object nmsEntity = worldMap.get(uuid);
if (nmsEntity == null) return null;
return this.getBukkitEntity(nmsEntity);
}
public Entity getEntity(UUID uuid)
{
if (uuid == null) return null;
for (World world : Bukkit.getWorlds())
{
Entity ret = this.getEntity(world, uuid);
if (ret != null) return ret;
}
return null;
}
}

View File

@ -22,7 +22,7 @@ public class NmsHead extends NmsAbstract
public static NmsHead get () { return i; }
// -------------------------------------------- //
// REFLECTION CACHE
// FIELDS
// -------------------------------------------- //
public static Class<?> classCraftMetaSkull;
@ -73,12 +73,12 @@ public class NmsHead extends NmsAbstract
public static String getGameProfileName(Object gameProfile)
{
return (String) ReflectionUtil.getField(fieldGameProfileDotName, gameProfile);
return ReflectionUtil.getField(fieldGameProfileDotName, gameProfile);
}
public static UUID getGameProfileId(Object gameProfile)
{
return (UUID) ReflectionUtil.getField(fieldGameProfileDotId, gameProfile);
return ReflectionUtil.getField(fieldGameProfileDotId, gameProfile);
}
// -------------------------------------------- //

View File

@ -68,6 +68,7 @@ import com.massivecraft.massivecore.collections.MassiveTreeSet;
import com.massivecraft.massivecore.engine.EngineMassiveCoreDatabase;
import com.massivecraft.massivecore.engine.EngineMassiveCoreMain;
import com.massivecraft.massivecore.engine.EngineMassiveCoreWorldNameSet;
import com.massivecraft.massivecore.nms.NmsEntity;
import com.massivecraft.massivecore.util.extractor.Extractor;
import com.massivecraft.massivecore.util.extractor.ExtractorPlayer;
import com.massivecraft.massivecore.util.extractor.ExtractorPlayerName;
@ -174,6 +175,65 @@ public class MUtil
}
}
// -------------------------------------------- //
// GET ENTITY
// -------------------------------------------- //
public static Entity getEntity(World world, UUID uuid)
{
if (world == null) throw new NullPointerException("world");
if (uuid == null) return null;
if (NmsEntity.get().isAvailable())
{
return NmsEntity.get().getEntity(world, uuid);
}
else
{
return getEntityFallback(world, uuid);
}
}
public static Entity getEntity(UUID uuid)
{
if (uuid == null) return null;
if (NmsEntity.get().isAvailable())
{
return NmsEntity.get().getEntity(uuid);
}
else
{
return getEntityFallback(uuid);
}
}
private static Entity getEntityFallback(World world, UUID uuid)
{
if (world == null) throw new NullPointerException("world");
if (uuid == null) return null;
for (Entity entity : world.getEntities())
{
if (entity.getUniqueId().equals(uuid)) return entity;
}
return null;
}
private static Entity getEntityFallback(UUID uuid)
{
if (uuid == null) return null;
for (World world : Bukkit.getWorlds())
{
Entity ret = getEntityFallback(world, uuid);
if (ret != null) return ret;
}
return null;
}
// -------------------------------------------- //
// IS VALID PLAYER NAME
// -------------------------------------------- //

View File

@ -1,8 +1,11 @@
package com.massivecraft.massivecore.util;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.lang.reflect.UndeclaredThrowableException;
import java.util.ArrayList;
import java.util.List;
@ -33,60 +36,127 @@ public class ReflectionUtil
// MAKE ACCESSIBLE
// -------------------------------------------- //
public static boolean makeAccessible(Field field)
public static void makeAccessible(Field field)
{
try
{
// Mark the field as accessible using reflection.
// Mark as accessible using reflection.
field.setAccessible(true);
// Remove the final modifier from the field.
// http://stackoverflow.com/questions/2474017/using-reflection-to-change-static-final-file-separatorchar-for-unit-testing
FIELD_DOT_MODIFIERS.setInt(field, field.getModifiers() & ~Modifier.FINAL);
return true;
}
catch (Exception e)
{
e.printStackTrace();
return false;
rethrow(e);
}
}
// -------------------------------------------- //
// FIELD GET
// -------------------------------------------- //
public static Field getField(Class<?> clazz, String fieldName)
public static void makeAccessible(Method method)
{
try
{
Field field = clazz.getDeclaredField(fieldName);
makeAccessible(field);
return field;
// Mark as accessible using reflection.
method.setAccessible(true);
}
catch (Exception e)
{
e.printStackTrace();
return null;
rethrow(e);
}
}
public static Object getField(Field field, Object object)
public static void makeAccessible(Constructor<?> constructor)
{
try
{
return field.get(object);
// Mark as accessible using reflection.
constructor.setAccessible(true);
}
catch (Exception e)
{
e.printStackTrace();
return null;
rethrow(e);
}
}
// -------------------------------------------- //
// FIELD SET
// METHOD
// -------------------------------------------- //
public static Method getMethod(Class<?> clazz, String name, Class<?>... parameterTypes)
{
try
{
Method ret = clazz.getMethod(name, parameterTypes);
makeAccessible(ret);
return ret;
}
catch (Exception e)
{
rethrow(e);
}
throw new RuntimeException();
}
public static Method getMethod(Class<?> clazz, String name)
{
return getMethod(clazz, name, new Class<?>[0]);
}
@SuppressWarnings("unchecked")
public static <T> T invokeMethod(Method method, Object target, Object... arguments)
{
try
{
return (T) method.invoke(target, arguments);
}
catch (Exception e)
{
rethrow(e);
}
throw new RuntimeException();
}
@SuppressWarnings("unchecked")
public static <T> T invokeMethod(Method method, Object target)
{
return (T) invokeMethod(method, target, new Object[0]);
}
// -------------------------------------------- //
// FIELD > GET
// -------------------------------------------- //
public static Field getField(Class<?> clazz, String name)
{
try
{
Field ret = clazz.getDeclaredField(name);
makeAccessible(ret);
return ret;
}
catch (Exception e)
{
rethrow(e);
}
throw new RuntimeException();
}
@SuppressWarnings("unchecked")
public static <T> T getField(Field field, Object object)
{
try
{
return (T) field.get(object);
}
catch (Exception e)
{
rethrow(e);
}
throw new RuntimeException();
}
// -------------------------------------------- //
// FIELD > SET
// -------------------------------------------- //
public static void setField(Field field, Object object, Object value)
@ -97,63 +167,38 @@ public class ReflectionUtil
}
catch (Exception e)
{
e.printStackTrace();
rethrow(e);
}
}
// -------------------------------------------- //
// FIELD SIMPLE: GET & SET & TRANSFER
// FIELD > SIMPLE
// -------------------------------------------- //
public static Object getField(Class<?> clazz, String fieldName, Object object)
public static <T> T getField(Class<?> clazz, String name, Object object)
{
try
{
Field field = clazz.getDeclaredField(fieldName);
makeAccessible(field);
return field.get(object);
}
catch (Exception e)
{
e.printStackTrace();
return null;
}
Field field = getField(clazz, name);
return getField(field, object);
}
public static boolean setField(Class<?> clazz, String fieldName, Object object, Object value)
public static void setField(Class<?> clazz, String name, Object object, Object value)
{
try
{
Field field = clazz.getDeclaredField(fieldName);
makeAccessible(field);
field.set(object, value);
return true;
}
catch (Exception e)
{
e.printStackTrace();
return false;
}
Field field = getField(clazz, name);
setField(field, object, value);
}
public static boolean transferField(Class<?> clazz, Object from, Object to, String fieldName)
// -------------------------------------------- //
// FIELD > TRANSFER
// -------------------------------------------- //
public static void transferField(Class<?> clazz, Object from, Object to, String name)
{
try
{
Field field = clazz.getDeclaredField(fieldName);
makeAccessible(field);
Object value = field.get(from);
field.set(to, value);
return true;
}
catch (Exception e)
{
e.printStackTrace();
return false;
}
Field field = getField(clazz, name);
Object value = getField(field, from);
setField(field, to, value);
}
public static boolean transferFields(Class<?> clazz, Object from, Object to, List<String> fieldNames)
public static void transferFields(Class<?> clazz, Object from, Object to, List<String> fieldNames)
{
if (fieldNames == null)
{
@ -166,15 +211,13 @@ public class ReflectionUtil
for (String fieldName : fieldNames)
{
if ( ! transferField(clazz, from, to, fieldName)) return false;
transferField(clazz, from, to, fieldName);
}
return true;
}
public static boolean transferFields(Class<?> clazz, Object from, Object to)
public static void transferFields(Class<?> clazz, Object from, Object to)
{
return transferFields(clazz, from, to, null);
transferFields(clazz, from, to, null);
}
// -------------------------------------------- //
@ -223,4 +266,34 @@ public class ReflectionUtil
});
}
// -------------------------------------------- //
// RETHROW
// -------------------------------------------- //
// This method is used to convert throwables into nicer runtime versions.
public static void rethrow(Throwable e)
{
// Error
if (e instanceof Error) throw (Error)e;
// Not Found
if (e instanceof ClassNotFoundException) throw new IllegalStateException("Class not Found: " + e.getMessage());
if (e instanceof NoSuchMethodException) throw new IllegalStateException("Method not Found: " + e.getMessage());
if (e instanceof NoSuchFieldException) throw new IllegalStateException("Field not Found: " + e.getMessage());
// Security
if (e instanceof SecurityException) throw new IllegalStateException("Security was Violated: " + e.getMessage());
// Derp
if (e instanceof IllegalAccessException) throw new IllegalStateException("Access was Illegal: " + e.getMessage());
if (e instanceof IllegalArgumentException) throw new IllegalStateException("Inappropriate Arguments: " + e.getMessage());
if (e instanceof InvocationTargetException) rethrow(((InvocationTargetException)e).getCause());
// Runtime
if (e instanceof RuntimeException) throw (RuntimeException)e;
// Other
throw new UndeclaredThrowableException(e);
}
}