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() public static SimpleCommandMap getSimpleCommandMap()
{ {
Server server = Bukkit.getServer(); 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"); protected static Field SIMPLE_COMMAND_MAP_DOT_KNOWN_COMMANDS = ReflectionUtil.getField(SimpleCommandMap.class, "knownCommands");
@SuppressWarnings("unchecked")
public static Map<String, Command> getSimpleCommandMapDotKnownCommands(SimpleCommandMap simpleCommandMap) 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; } public static NmsHead get () { return i; }
// -------------------------------------------- // // -------------------------------------------- //
// REFLECTION CACHE // FIELDS
// -------------------------------------------- // // -------------------------------------------- //
public static Class<?> classCraftMetaSkull; public static Class<?> classCraftMetaSkull;
@ -73,12 +73,12 @@ public class NmsHead extends NmsAbstract
public static String getGameProfileName(Object gameProfile) public static String getGameProfileName(Object gameProfile)
{ {
return (String) ReflectionUtil.getField(fieldGameProfileDotName, gameProfile); return ReflectionUtil.getField(fieldGameProfileDotName, gameProfile);
} }
public static UUID getGameProfileId(Object 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.EngineMassiveCoreDatabase;
import com.massivecraft.massivecore.engine.EngineMassiveCoreMain; import com.massivecraft.massivecore.engine.EngineMassiveCoreMain;
import com.massivecraft.massivecore.engine.EngineMassiveCoreWorldNameSet; 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.Extractor;
import com.massivecraft.massivecore.util.extractor.ExtractorPlayer; import com.massivecraft.massivecore.util.extractor.ExtractorPlayer;
import com.massivecraft.massivecore.util.extractor.ExtractorPlayerName; 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 // IS VALID PLAYER NAME
// -------------------------------------------- // // -------------------------------------------- //

View File

@ -1,8 +1,11 @@
package com.massivecraft.massivecore.util; package com.massivecraft.massivecore.util;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field; import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import java.lang.reflect.Modifier; import java.lang.reflect.Modifier;
import java.lang.reflect.UndeclaredThrowableException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
@ -33,60 +36,127 @@ public class ReflectionUtil
// MAKE ACCESSIBLE // MAKE ACCESSIBLE
// -------------------------------------------- // // -------------------------------------------- //
public static boolean makeAccessible(Field field) public static void makeAccessible(Field field)
{ {
try try
{ {
// Mark the field as accessible using reflection. // Mark as accessible using reflection.
field.setAccessible(true); field.setAccessible(true);
// Remove the final modifier from the field. // Remove the final modifier from the field.
// http://stackoverflow.com/questions/2474017/using-reflection-to-change-static-final-file-separatorchar-for-unit-testing // 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); FIELD_DOT_MODIFIERS.setInt(field, field.getModifiers() & ~Modifier.FINAL);
return true;
} }
catch (Exception e) catch (Exception e)
{ {
e.printStackTrace(); rethrow(e);
return false;
} }
} }
// -------------------------------------------- // public static void makeAccessible(Method method)
// FIELD GET
// -------------------------------------------- //
public static Field getField(Class<?> clazz, String fieldName)
{ {
try try
{ {
Field field = clazz.getDeclaredField(fieldName); // Mark as accessible using reflection.
makeAccessible(field); method.setAccessible(true);
return field;
} }
catch (Exception e) catch (Exception e)
{ {
e.printStackTrace(); rethrow(e);
return null;
} }
} }
public static Object getField(Field field, Object object) public static void makeAccessible(Constructor<?> constructor)
{ {
try try
{ {
return field.get(object); // Mark as accessible using reflection.
constructor.setAccessible(true);
} }
catch (Exception e) catch (Exception e)
{ {
e.printStackTrace(); rethrow(e);
return null;
} }
} }
// -------------------------------------------- // // -------------------------------------------- //
// 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) public static void setField(Field field, Object object, Object value)
@ -97,63 +167,38 @@ public class ReflectionUtil
} }
catch (Exception e) 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 = getField(clazz, name);
{ return getField(field, object);
Field field = clazz.getDeclaredField(fieldName);
makeAccessible(field);
return field.get(object);
}
catch (Exception e)
{
e.printStackTrace();
return null;
}
} }
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 = getField(clazz, name);
{ setField(field, object, value);
Field field = clazz.getDeclaredField(fieldName);
makeAccessible(field);
field.set(object, value);
return true;
}
catch (Exception e)
{
e.printStackTrace();
return false;
}
} }
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 = getField(clazz, name);
{ Object value = getField(field, from);
Field field = clazz.getDeclaredField(fieldName); setField(field, to, value);
makeAccessible(field);
Object value = field.get(from);
field.set(to, value);
return true;
}
catch (Exception e)
{
e.printStackTrace();
return false;
}
} }
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) if (fieldNames == null)
{ {
@ -166,15 +211,13 @@ public class ReflectionUtil
for (String fieldName : fieldNames) 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);
}
} }