Added nms utilities to better detect incorrect usage of nms

This commit is contained in:
OmerBenGera 2022-08-03 19:30:05 +03:00
parent 2750455435
commit c0a137ff01
6 changed files with 267 additions and 0 deletions

View File

@ -0,0 +1,55 @@
package com.bgsoftware.wildloaders.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;
}
}

View File

@ -0,0 +1,30 @@
package com.bgsoftware.wildloaders.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
}
}

View File

@ -0,0 +1,9 @@
package com.bgsoftware.wildloaders.nms.mapping;
public class RemapFailure extends RuntimeException {
public RemapFailure(String message) {
super(message);
}
}

View File

@ -0,0 +1,32 @@
package com.bgsoftware.wildloaders.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);
}
}

View File

@ -0,0 +1,14 @@
package com.bgsoftware.wildloaders.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();
}

View File

@ -0,0 +1,127 @@
package com.bgsoftware.wildloaders.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);
}
}