diff --git a/pom.xml b/pom.xml index 592e3168..a48d286a 100644 --- a/pom.xml +++ b/pom.xml @@ -7,6 +7,7 @@ src + test clean package target LibsDisguises @@ -90,6 +91,12 @@ spigot 1.7.8-R0.1-SNAPSHOT + + junit + junit + 4.11 + test + diff --git a/src/me/libraryaddict/disguise/utilities/DisguiseUtilities.java b/src/me/libraryaddict/disguise/utilities/DisguiseUtilities.java index 38243e00..7f6dc637 100644 --- a/src/me/libraryaddict/disguise/utilities/DisguiseUtilities.java +++ b/src/me/libraryaddict/disguise/utilities/DisguiseUtilities.java @@ -631,7 +631,7 @@ public class DisguiseUtilities { } int fakeId = selfDisguisesIds.get(player.getUniqueId()); // Add himself to his own entity tracker - ((HashSet) ReflectionManager.getNmsField("EntityTrackerEntry", "trackedPlayers").get(entityTrackerEntry)).add(ReflectionManager.getNmsEntity(player)); + ((HashSet) ReflectionManager.getNmsField("EntityTrackerEntry", "trackedPlayers").get(entityTrackerEntry)).add(ReflectionManager.getNmsEntity(player)); ProtocolManager manager = ProtocolLibrary.getProtocolManager(); // Send the player a packet with himself being spawned manager.sendServerPacket(player, manager.createPacketConstructor(PacketType.Play.Server.NAMED_ENTITY_SPAWN, player) diff --git a/src/me/libraryaddict/disguise/utilities/PacketsManager.java b/src/me/libraryaddict/disguise/utilities/PacketsManager.java index 806f23b8..d75aeecc 100644 --- a/src/me/libraryaddict/disguise/utilities/PacketsManager.java +++ b/src/me/libraryaddict/disguise/utilities/PacketsManager.java @@ -530,6 +530,7 @@ public class PacketsManager { Object obj = null; if (entity instanceof LivingEntity) { try { + // Use reflection so that this works for either int or double methods obj = LivingEntity.class.getMethod("getHealth").invoke(entity); if (obj instanceof Double ? (Double) obj == 0 : (Integer) obj == 0) { soundType = SoundType.DEATH; diff --git a/src/me/libraryaddict/disguise/utilities/ReflectionManager.java b/src/me/libraryaddict/disguise/utilities/ReflectionManager.java index 4c850b35..14358d49 100644 --- a/src/me/libraryaddict/disguise/utilities/ReflectionManager.java +++ b/src/me/libraryaddict/disguise/utilities/ReflectionManager.java @@ -1,15 +1,21 @@ package me.libraryaddict.disguise.utilities; -import java.io.*; +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.lang.reflect.Modifier; +import java.util.ArrayList; import java.util.HashMap; +import java.util.List; import java.util.Map; import java.util.UUID; import java.util.regex.Matcher; import java.util.regex.Pattern; +import com.google.common.collect.ImmutableMap; import org.bukkit.Art; import org.bukkit.Bukkit; import org.bukkit.Location; @@ -81,11 +87,26 @@ public class ReflectionManager { */ private static Map[], String>>> ForgeMethodMappings; - private static String dir2fqn(String s) { - return s.replaceAll("/", "."); - } + private static Map> primitiveTypes; + private static Pattern signatureSegment; static { + final String nameseg_class = "a-zA-Z0-9$_"; + final String fqn_class = "a-zA-Z0-9$_/"; + final String fqn_component = "L[" + fqn_class + "]+;"; + + signatureSegment = Pattern.compile("\\[*(?:Z|B|C|S|I|J|F|D|V|" + fqn_component + ")"); + primitiveTypes = ImmutableMap.>builder() + .put("Z", boolean.class) + .put("B", byte.class) + .put("C", char.class) + .put("S", short.class) + .put("I", int.class) + .put("J", long.class) + .put("F", float.class) + .put("D", double.class) + .put("V", void.class).build(); + if (isForge) { // Initialize the maps by reading the srg file ForgeClassMappings = new HashMap(); @@ -95,16 +116,25 @@ public class ReflectionManager { InputStream stream = Class.forName("net.minecraftforge.common.MinecraftForge").getClassLoader() .getResourceAsStream("mappings/" + getBukkitVersion() + "/cb2numpkg.srg"); BufferedReader reader = new BufferedReader(new InputStreamReader(stream)); + // 1: cb-simpleName // 2: forge-fullName (Needs dir2fqn()) - Pattern classPattern = Pattern.compile("^CL: net/minecraft/server/(\\w+) ([a-zA-Z0-9$/_]+)$"); + Pattern classPattern = Pattern.compile("^CL: net/minecraft/server/([" + nameseg_class + "]+) ([" + fqn_class + "]+)$"); // 1: cb-simpleName // 2: cb-fieldName // 3: forge-fullName (Needs dir2fqn()) // 4: forge-fieldName - Pattern fieldPattern = Pattern.compile("^FD: net/minecraft/server/(\\w+)/(\\w+) ([a-zA-Z0-9$/_]+)/([a-zA-Z0-9$/_]+)$"); - - Pattern methodPattern = Pattern.compile("!XXX todo"); + Pattern fieldPattern = Pattern.compile("^FD: net/minecraft/server/([" + nameseg_class + "]+)/([" + nameseg_class + "]+) ([" + fqn_class + "]+)/([" + nameseg_class + "]+)$"); + // 1: cb-simpleName + // 2: cb-methodName + // 3: cb-signature-args + // 4: cb-signature-ret + // 5: forge-fullName (Needs dir2fqn()) + // 6: forge-methodName + // 7: forge-signature-args + // 8: forge-signature-ret + Pattern methodPattern = Pattern.compile("^MD: net/minecraft/server/([" + fqn_class + "]+)/([" + nameseg_class + "]+) \\(([;\\[" + fqn_class + "]*)\\)([;\\[" + fqn_class + "]+) " + + "([" + fqn_class + "]+)/([" + nameseg_class + "]+) \\(([;\\[" + fqn_class + "]*)\\)([;\\[" + fqn_class + "]+)$"); String line; System.out.println("Reading"); @@ -162,6 +192,36 @@ public class ReflectionManager { } } + private static String dir2fqn(String s) { + return s.replaceAll("/", "."); + } + + public static List> parseSignatureArguments(String args) throws ClassNotFoundException { + List> classes = new ArrayList>(); + Matcher matcher = signatureSegment.matcher(args); + while (matcher.find()) { + classes.add(parseClass(matcher.group())); + } + return classes; + } + + private static Class parseClass(String str) throws ClassNotFoundException { + if (str.startsWith("[")) { + // Array + // http://stackoverflow.com/a/4901192/1210278 + return java.lang.reflect.Array.newInstance(parseClass(str.substring(1)), 0).getClass(); + } else if (str.length() == 1) { + return primitiveTypes.get(str); + } else if (str.startsWith("L")) { + // Chop off L and ; + return Class.forName(str.substring(1, str.length() - 1)); + } else { + throw new ClassNotFoundException("Malformed method signature fragment? Argument: " + str); + } + } + + // === + public static Object createEntityInstance(String entityName) { try { Class entityClass = getNmsClass("Entity" + entityName); diff --git a/test/me/libraryaddict/disguise/utilities/ReflectionManagerTests.java b/test/me/libraryaddict/disguise/utilities/ReflectionManagerTests.java new file mode 100644 index 00000000..c2244f4a --- /dev/null +++ b/test/me/libraryaddict/disguise/utilities/ReflectionManagerTests.java @@ -0,0 +1,32 @@ +package me.libraryaddict.disguise.utilities; + +import com.google.common.collect.ImmutableList; +import org.junit.Test; + +import java.util.List; + +import static org.junit.Assert.*; + +public class ReflectionManagerTests { + + @Test + public void testParseSignatureArguments() throws Exception { + List> expect, actual; + + expect = ImmutableList.>of(boolean.class, byte.class, char.class, short.class, int.class, long.class, float.class, double.class); + actual = ReflectionManager.parseSignatureArguments("ZBCSIJFD"); + assertEquals(expect, actual); + + expect = ImmutableList.>of(int.class, String[].class, int.class); + actual = ReflectionManager.parseSignatureArguments("I[Ljava/lang/String;I"); + assertEquals(expect, actual); + + expect = ImmutableList.>of(); + actual = ReflectionManager.parseSignatureArguments(""); + assertEquals(expect, actual); + + expect = ImmutableList.>of(boolean[][][][][][].class); + actual = ReflectionManager.parseSignatureArguments("[[[[[[Z"); + assertEquals(expect, actual); + } +}