Skip to content

Commit

Permalink
Added nms utilities to better detect incorrect nms usage
Browse files Browse the repository at this point in the history
  • Loading branch information
OmerBenGera committed Aug 3, 2022
1 parent 96bf507 commit 2d928d9
Show file tree
Hide file tree
Showing 6 changed files with 267 additions and 0 deletions.
@@ -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;
}

}
@@ -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

}


}
@@ -0,0 +1,9 @@
package com.bgsoftware.superiorskyblock.nms.mapping;

public class RemapFailure extends RuntimeException {

public RemapFailure(String message) {
super(message);
}

}
@@ -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);
}

}
@@ -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();

}
@@ -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);
}

}

0 comments on commit 2d928d9

Please sign in to comment.