Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Added nms utilities to better detect incorrect nms usage
- Loading branch information
1 parent
96bf507
commit 2d928d9
Showing
6 changed files
with
267 additions
and
0 deletions.
There are no files selected for viewing
55 changes: 55 additions & 0 deletions
55
src/main/java/com/bgsoftware/superiorskyblock/nms/mapping/MappingParser.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,55 @@ | ||
package com.bgsoftware.superiorskyblock.nms.mapping; | ||
|
||
import java.io.BufferedReader; | ||
import java.io.File; | ||
import java.io.FileReader; | ||
import java.io.IOException; | ||
import java.util.HashMap; | ||
import java.util.Map; | ||
import java.util.regex.Matcher; | ||
import java.util.regex.Pattern; | ||
|
||
public class MappingParser { | ||
|
||
private static final Pattern NEW_CLASS_REMAP_PATTERN = Pattern.compile("^([^ ]+) -> [^ ]+:$"); | ||
private static final Pattern FIELD_REMAP_PATTERN = Pattern.compile("^[ ]{4}[^ ]+ ([^ (]+)+ -> ([^ ]+)$"); | ||
private static final Pattern METHOD_REMAP_PATTERN = Pattern.compile("^[ ]{4}[^ ]+ ([^ ]+)\\(.*\\) -> ([^ ]+)$"); | ||
|
||
private MappingParser() { | ||
|
||
} | ||
|
||
public static Map<String, Remapped> parseRemappedMap(File mappingsFile) throws IOException { | ||
Map<String, Remapped> remappedMap = new HashMap<>(); | ||
|
||
try (BufferedReader reader = new BufferedReader(new FileReader(mappingsFile))) { | ||
Remapped currentRemapped = null; | ||
String currentLine; | ||
|
||
while ((currentLine = reader.readLine()) != null) { | ||
if (currentLine.startsWith("#")) // Comment | ||
continue; | ||
|
||
Matcher matcher; | ||
|
||
if ((matcher = NEW_CLASS_REMAP_PATTERN.matcher(currentLine)).matches()) { | ||
currentRemapped = new Remapped(); | ||
remappedMap.put(matcher.group(1), currentRemapped); | ||
} else if (currentRemapped != null) { | ||
if ((matcher = FIELD_REMAP_PATTERN.matcher(currentLine)).matches()) { | ||
String fieldName = matcher.group(1); | ||
String obfuscatedName = matcher.group(2); | ||
currentRemapped.put(Remap.Type.FIELD, fieldName, obfuscatedName); | ||
} else if ((matcher = METHOD_REMAP_PATTERN.matcher(currentLine)).matches()) { | ||
String methodName = matcher.group(1); | ||
String obfuscatedName = matcher.group(2); | ||
currentRemapped.put(Remap.Type.METHOD, methodName, obfuscatedName); | ||
} | ||
} | ||
} | ||
} | ||
|
||
return remappedMap; | ||
} | ||
|
||
} |
30 changes: 30 additions & 0 deletions
30
src/main/java/com/bgsoftware/superiorskyblock/nms/mapping/Remap.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
package com.bgsoftware.superiorskyblock.nms.mapping; | ||
|
||
import java.lang.annotation.ElementType; | ||
import java.lang.annotation.Repeatable; | ||
import java.lang.annotation.Retention; | ||
import java.lang.annotation.RetentionPolicy; | ||
import java.lang.annotation.Target; | ||
|
||
@Target({ElementType.FIELD, ElementType.METHOD, ElementType.CONSTRUCTOR}) | ||
@Retention(RetentionPolicy.RUNTIME) | ||
@Repeatable(Remaps.class) | ||
public @interface Remap { | ||
|
||
String classPath(); | ||
|
||
String name(); | ||
|
||
Type type(); | ||
|
||
String remappedName() default ""; | ||
|
||
enum Type { | ||
|
||
FIELD, | ||
METHOD | ||
|
||
} | ||
|
||
|
||
} |
9 changes: 9 additions & 0 deletions
9
src/main/java/com/bgsoftware/superiorskyblock/nms/mapping/RemapFailure.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
package com.bgsoftware.superiorskyblock.nms.mapping; | ||
|
||
public class RemapFailure extends RuntimeException { | ||
|
||
public RemapFailure(String message) { | ||
super(message); | ||
} | ||
|
||
} |
32 changes: 32 additions & 0 deletions
32
src/main/java/com/bgsoftware/superiorskyblock/nms/mapping/Remapped.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
package com.bgsoftware.superiorskyblock.nms.mapping; | ||
|
||
import java.util.ArrayList; | ||
import java.util.Collections; | ||
import java.util.EnumMap; | ||
import java.util.HashMap; | ||
import java.util.List; | ||
import java.util.Map; | ||
|
||
public class Remapped { | ||
|
||
private final EnumMap<Remap.Type, Map<String, List<String>>> mappings = new EnumMap<>(Remap.Type.class); | ||
|
||
public List<String> getObfuscatedNames(String name, Remap.Type type) { | ||
Map<String, List<String>> typeMappings = mappings.get(type); | ||
|
||
if (typeMappings != null) { | ||
List<String> obfuscatedNames = typeMappings.get(name); | ||
if (obfuscatedNames != null) | ||
return obfuscatedNames; | ||
} | ||
|
||
return Collections.emptyList(); | ||
} | ||
|
||
public void put(Remap.Type type, String name, String obfuscated) { | ||
mappings.computeIfAbsent(type, t -> new HashMap<>()) | ||
.computeIfAbsent(name, n -> new ArrayList<>()) | ||
.add(obfuscated); | ||
} | ||
|
||
} |
14 changes: 14 additions & 0 deletions
14
src/main/java/com/bgsoftware/superiorskyblock/nms/mapping/Remaps.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
package com.bgsoftware.superiorskyblock.nms.mapping; | ||
|
||
import java.lang.annotation.ElementType; | ||
import java.lang.annotation.Retention; | ||
import java.lang.annotation.RetentionPolicy; | ||
import java.lang.annotation.Target; | ||
|
||
@Target({ElementType.FIELD, ElementType.METHOD, ElementType.CONSTRUCTOR}) | ||
@Retention(RetentionPolicy.RUNTIME) | ||
public @interface Remaps { | ||
|
||
Remap[] value(); | ||
|
||
} |
127 changes: 127 additions & 0 deletions
127
src/main/java/com/bgsoftware/superiorskyblock/nms/mapping/TestRemaps.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,127 @@ | ||
package com.bgsoftware.superiorskyblock.nms.mapping; | ||
|
||
import com.bgsoftware.common.reflection.ReflectField; | ||
import com.bgsoftware.common.reflection.ReflectMethod; | ||
import com.google.common.reflect.ClassPath; | ||
|
||
import javax.annotation.Nullable; | ||
import java.io.File; | ||
import java.io.IOException; | ||
import java.lang.reflect.Field; | ||
import java.lang.reflect.Method; | ||
import java.lang.reflect.Modifier; | ||
import java.util.List; | ||
import java.util.Map; | ||
import java.util.function.IntFunction; | ||
import java.util.logging.Logger; | ||
|
||
public class TestRemaps { | ||
|
||
private static final Logger logger = Logger.getLogger("TestRemaps"); | ||
|
||
private static final ReflectField<Field> REFLECT_FIELD_INNER_FIELD = new ReflectField<>(ReflectField.class, Field.class, "field"); | ||
private static final ReflectField<Method> REFLECT_METHOD_INNER_METHOD = new ReflectField<>(ReflectMethod.class, Method.class, "method"); | ||
|
||
private TestRemaps() { | ||
|
||
} | ||
|
||
@SuppressWarnings("UnstableApiUsage") | ||
public static void testRemapsForClassesInPackage(File mappingsFile, ClassLoader classLoader, String packageName) throws | ||
IllegalAccessException, NullPointerException, IOException, RemapFailure { | ||
Class<?>[] classes = ClassPath.from(classLoader) | ||
.getAllClasses() | ||
.stream() | ||
.filter(clazz -> clazz.getPackageName().startsWith(packageName)) | ||
.map(ClassPath.ClassInfo::load) | ||
.toArray((IntFunction<Class<?>[]>) Class[]::new); | ||
testRemapsForClasses(mappingsFile, classes); | ||
} | ||
|
||
public static void testRemapsForClasses(File mappingsFile, Class<?>... classes) throws | ||
IllegalAccessException, NullPointerException, IOException, RemapFailure { | ||
for (Class<?> clazz : classes) { | ||
logger.info("Starting remaps test for " + clazz.getName()); | ||
|
||
Map<String, Remapped> remappedMap = MappingParser.parseRemappedMap(mappingsFile); | ||
|
||
try { | ||
for (Field field : clazz.getDeclaredFields()) { | ||
if (Modifier.isStatic(field.getModifiers())) { | ||
Remap[] remaps = field.getAnnotationsByType(Remap.class); | ||
if (remaps.length > 0) { | ||
logger.info("Testing field " + field.getName()); | ||
for (Remap remap : remaps) { | ||
try { | ||
testRemap(remappedMap, remap, getRemappedName(field)); | ||
} catch (Throwable error) { | ||
error.printStackTrace(); | ||
} | ||
} | ||
} | ||
} | ||
} | ||
|
||
for (Method method : clazz.getDeclaredMethods()) { | ||
Remap[] remaps = method.getAnnotationsByType(Remap.class); | ||
if (remaps.length > 0) { | ||
logger.info("Testing method " + method.getName()); | ||
for (Remap remap : remaps) { | ||
try { | ||
testRemap(remappedMap, remap, null); | ||
} catch (Throwable error) { | ||
error.printStackTrace(); | ||
} | ||
} | ||
} | ||
} | ||
} catch (Throwable error) { | ||
logger.info("Failed remaps test for " + clazz.getName() + ":"); | ||
error.printStackTrace(); | ||
} | ||
|
||
logger.info("Finished remaps tests for " + clazz.getName()); | ||
} | ||
} | ||
|
||
@Nullable | ||
private static String getRemappedName(Field field) throws IllegalAccessException { | ||
field.setAccessible(true); | ||
Object fieldValue = field.get(null); | ||
|
||
if (fieldValue instanceof ReflectField) { | ||
Field innerField = REFLECT_FIELD_INNER_FIELD.get(fieldValue); | ||
if (innerField != null) | ||
return innerField.getName(); | ||
} else if (fieldValue instanceof ReflectMethod) { | ||
Method innerMethod = REFLECT_METHOD_INNER_METHOD.get(fieldValue); | ||
if (innerMethod != null) | ||
return innerMethod.getName(); | ||
} | ||
|
||
return null; | ||
} | ||
|
||
private static void testRemap(Map<String, Remapped> remappedMap, Remap remap, @Nullable String remappedName) { | ||
String classPath = remap.classPath(); | ||
|
||
Remapped remapped = remappedMap.get(classPath); | ||
|
||
if (remapped == null) | ||
throw new NullPointerException("Cannot find remapped object for classPath " + classPath); | ||
|
||
String name = remap.name(); | ||
Remap.Type type = remap.type(); | ||
|
||
List<String> obfuscatedNames = remapped.getObfuscatedNames(name, type); | ||
|
||
if (obfuscatedNames.isEmpty()) | ||
throw new NullPointerException("Cannot find obfuscated name for " + name + ":" + type); | ||
|
||
String remappedNameOrDefault = remappedName == null ? remap.remappedName() : remappedName; | ||
|
||
if (!obfuscatedNames.contains(remappedNameOrDefault)) | ||
throw new RemapFailure("Incorrect remap: Expected " + obfuscatedNames + ", found " + remappedNameOrDefault); | ||
} | ||
|
||
} |