Skip to content

Commit

Permalink
Basic NMS Support
Browse files Browse the repository at this point in the history
  • Loading branch information
Hexeption committed Aug 19, 2019
1 parent 930ffac commit 9582a7a
Show file tree
Hide file tree
Showing 10 changed files with 26,402 additions and 53 deletions.
1 change: 1 addition & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ configurations{
dependencies {
testCompile group: 'junit', name: 'junit', version: '4.12'
compile(group: 'bukkit', name: 'bukkit', version: '1.12.2-R0.1')
compile 'net.md-5:SpecialSource:1.8.5'
}

jar{
Expand Down
Binary file modified release/libraries.zip
Binary file not shown.
35 changes: 35 additions & 0 deletions src/main/java/com/maxqia/remapper/ClassInheritanceProvider.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package com.maxqia.remapper;

import net.md_5.specialsource.provider.InheritanceProvider;

import java.util.Collection;
import java.util.HashSet;

public class ClassInheritanceProvider implements InheritanceProvider {

@Override
public Collection<String> getParents(String className) {
className = Transformer.remapper.map(className);

try {
Collection<String> parents = new HashSet<>();
Class<?> reference = Class.forName(className.replace('/', '.').replace('$', '.'), false, this.getClass().getClassLoader()/*RemappedMethods.loader*/);
Class<?> extend = reference.getSuperclass();
if (extend != null) {
parents.add(Utils.reverseMap(extend));
}

for (Class<?> inter : reference.getInterfaces()) {
if (inter != null) {
parents.add(Utils.reverseMap(inter));
}
}

return parents;
} catch (Exception e) {
// Empty catch block
}
return null;
}

}
37 changes: 37 additions & 0 deletions src/main/java/com/maxqia/remapper/RemapUtils.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package com.maxqia.remapper;

import java.lang.reflect.Method;

/**
* Based on Apache's ReflectionUtil
*
* @author Maxqia
*/
public class RemapUtils {

private static final Method GET_CALLER_CLASS;

static {
try {
final Class<?> reflection = Class.forName("sun.reflect.Reflection");
GET_CALLER_CLASS = reflection.getMethod("getCallerClass", int.class);
} catch (ReflectiveOperationException e) {
throw new RuntimeException("getCallerClass doesn't exist!");
}
}

public static Class<?> getCallerClass(int skip) {
if (GET_CALLER_CLASS != null) {
try {
return (Class<?>) GET_CALLER_CLASS.invoke(null, skip + 1);
} catch (ReflectiveOperationException e) {
throw new AssertionError(e.getMessage());
}
}
throw new RuntimeException("getCallerClass doesn't exist!");
}

public static ClassLoader getCallerClassloader() {
return getCallerClass(3).getClassLoader();
}
}
112 changes: 112 additions & 0 deletions src/main/java/com/maxqia/remapper/RemappedMethods.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
package com.maxqia.remapper;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Map;

import static com.maxqia.remapper.Utils.*;

public class RemappedMethods {

// Classes
public static Class<?> forName(String className) throws ClassNotFoundException {
return forName(className, true, RemapUtils.getCallerClassloader());
} // Class.forName(String) grabs the caller's classloader, we replicate that

public static Class<?> forName(String className, boolean initialize, ClassLoader classLoader) throws ClassNotFoundException {
if (!className.startsWith("net.minecraft.server.v1_12_R1")) {
return Class.forName(className, initialize, classLoader);
}
className = Transformer.jarMapping.classes.getOrDefault(className.replace('.', '/'), className).replace('/', '.');
return Class.forName(className, initialize, classLoader);
}

// Get Fields
public static Field getField(Class<?> inst, String name) throws NoSuchFieldException, SecurityException {
if (!inst.getName().startsWith("net.minecraft.")) {
return inst.getField(name);
}
return inst.getField(Transformer.remapper.mapFieldName(reverseMap(inst), name, null));
}

public static Field getDeclaredField(Class<?> inst, String name) throws NoSuchFieldException, SecurityException {
if (!inst.getName().startsWith("net.minecraft.")) {
return inst.getDeclaredField(name);
}
return inst.getDeclaredField(Transformer.remapper.mapFieldName(reverseMap(inst), name, null));
}

// Get Methods
public static Method getMethod(Class<?> inst, String name, Class<?>... parameterTypes) throws NoSuchMethodException, SecurityException {
if (!inst.getName().startsWith("net.minecraft.")) {
return inst.getMethod(name, parameterTypes);
}
return inst.getMethod(mapMethod(inst, name, parameterTypes), parameterTypes);
}

public static Method getDeclaredMethod(Class<?> inst, String name, Class<?>... parameterTypes) throws NoSuchMethodException, SecurityException {
if (!inst.getName().startsWith("net.minecraft.")) {
return inst.getDeclaredMethod(name, parameterTypes);
}
return inst.getDeclaredMethod(mapMethod(inst, name, parameterTypes), parameterTypes);
}

// getName
public static String getName(Field field) {
if (!field.getDeclaringClass().getName().startsWith("net.minecraft.")) {
return field.getName();
}
String name = field.getName();
String match = reverseMap(field.getDeclaringClass());
Map<String, String> map = Transformer.jarMapping.fields;
for (String value : map.keySet()) {
if (map.get(name).startsWith(match)) {
String[] matched = value.split("/");
return matched[matched.length - 1];
}
}

return name;
}

public static String getName(Method method) {
if (!method.getDeclaringClass().getName().startsWith("net.minecraft.")) {
return method.getName();
}
String name = method.getName();
String match = reverseMap(method.getDeclaringClass());
Map<String, String> map = Transformer.jarMapping.methods;
for (String value : map.keySet()) {
if (map.get(name).startsWith(match)) {
String[] matched = value.split("\\s+")[0].split("/");
String rtr = matched[matched.length - 1];
return rtr;
}
}

return name;
}

// getSimpleName
public static String getSimpleName(Class<?> inst) {
if (!inst.getName().startsWith("net.minecraft.")) {
return inst.getSimpleName();
}
String[] name = getName(inst).split("\\.");
return name[name.length - 1];
}

public static String getName(Class<?> inst) {
if (!inst.getName().startsWith("net.minecraft.")) {
return inst.getName();
}
return reverseMapExternal(inst);
}

public static Class<?> loadClass(ClassLoader inst, String className) throws ClassNotFoundException {
if (className.startsWith("net.minecraft.")) {
className = Transformer.mapClass(className.replace('.', '/')).replace('/', '.');
}
return inst.loadClass(className);
}
}
106 changes: 106 additions & 0 deletions src/main/java/com/maxqia/remapper/Transformer.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
package com.maxqia.remapper;

import net.md_5.specialsource.JarMapping;
import net.md_5.specialsource.JarRemapper;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.Type;
import org.objectweb.asm.tree.AbstractInsnNode;
import org.objectweb.asm.tree.ClassNode;
import org.objectweb.asm.tree.MethodInsnNode;
import org.objectweb.asm.tree.MethodNode;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.ListIterator;

public class Transformer {

public static JarMapping jarMapping;
public static JarRemapper remapper;

public static void init(JarMapping mapping, JarRemapper remapper) {
if (Transformer.jarMapping == null) {
Transformer.jarMapping = mapping;
}
if (Transformer.remapper == null) {
Transformer.remapper = remapper;
}
}

/**
* Convert code from using Class.X methods to our remapped versions
*/
public static byte[] transform(byte[] code) {
ClassReader reader = new ClassReader(code); // Turn from bytes into visitor
ClassNode node = new ClassNode();
reader.accept(node, 0); // Visit using ClassNode

for (MethodNode method : node.methods) { // Taken from SpecialSource
ListIterator<AbstractInsnNode> insnIterator = method.instructions.iterator();
while (insnIterator.hasNext()) {
AbstractInsnNode insn = insnIterator.next();
if (!(insn instanceof MethodInsnNode)) {
continue;
}
MethodInsnNode mi = (MethodInsnNode) insn;
switch (mi.getOpcode()) {
case Opcodes.INVOKEVIRTUAL:
remapVirtual(mi);
break;
case Opcodes.INVOKESTATIC:
remapForName(mi);
break;
}
}
}

ClassWriter writer = new ClassWriter(0);
node.accept(writer);
return writer.toByteArray();
}

public static String mapClass(String className) {
String tRemapped = JarRemapper.mapTypeName(className, jarMapping.packages, jarMapping.classes, className);
if (tRemapped.equals(className) && className.startsWith("net/minecraft/server/") && !className.contains("v1_12_R1")) {
String tNewClassStr = "net/minecraft/server/v1_12_R1" + "/" + className.substring("v1_12_R1".length());
return JarRemapper.mapTypeName(tNewClassStr, jarMapping.packages, jarMapping.classes, className);
}
return tRemapped;
}

private static void remapForName(AbstractInsnNode insn) {
MethodInsnNode method = (MethodInsnNode) insn;
if (!"java/lang/Class".equals(method.owner) || !"forName".equals(method.name)) {
return;
}
method.owner = "com/maxqia/remapper/RemappedMethods";
}

private static void remapVirtual(AbstractInsnNode insn) {
MethodInsnNode method = (MethodInsnNode) insn;

if (!(("java/lang/Class".equals(method.owner) && ("getField".equals(method.name)
|| "getDeclaredField".equals(method.name)
|| "getMethod".equals(method.name)
|| "getDeclaredMethod".equals(method.name)
|| "getSimpleName".equals(method.name))
)
|| ("getName".equals(method.name) && ("java/lang/reflect/Field".equals(method.owner)
|| "java/lang/reflect/Method".equals(method.owner)))
|| ("java/lang/ClassLoader".equals(method.owner) && "loadClass".equals(method.name)))) {
return;
}

Type returnType = Type.getReturnType(method.desc);

ArrayList<Type> args = new ArrayList<>();
args.add(Type.getObjectType(method.owner));
args.addAll(Arrays.asList(Type.getArgumentTypes(method.desc)));

method.setOpcode(Opcodes.INVOKESTATIC);
method.owner = "com/maxqia/remapper/RemappedMethods";
method.desc = Type.getMethodDescriptor(returnType, args.toArray(new Type[args.size()]));
}
}
57 changes: 57 additions & 0 deletions src/main/java/com/maxqia/remapper/Utils.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
package com.maxqia.remapper;

import org.objectweb.asm.Type;

import java.util.Map;

public class Utils {

public static String reverseMapExternal(Class<?> name) {
return reverseMap(name).replace('$', '.').replace('/', '.');
}

public static String reverseMap(Class<?> name) {
return reverseMap(Type.getInternalName(name));
}

public static String reverseMap(String check) {
return Transformer.jarMapping.classes.getOrDefault(check, check);
}

public static String mapMethod(Class<?> inst, String name, Class<?>... parameterTypes) {
String result = mapMethodInternal(inst, name, parameterTypes);
if (result != null) {
return result;
}
return name;
}

public static String mapMethodInternal(Class<?> inst, String name, Class<?>... parameterTypes) {
String match = reverseMap(inst) + "/" + name;

Map<String, String> map = Transformer.jarMapping.methods;
for (String value : map.keySet()) {
String[] str = value.split("\\s+");
int i = 0;
for (Type type : Type.getArgumentTypes(str[1])) {
if (i >= parameterTypes.length || !type.getClassName().equals(reverseMapExternal(parameterTypes[i]))) {
i = -1;
break;
}
i++;
}

if (i >= parameterTypes.length) {
return map.get(value);
}
}

// return superMethodName
Class interfaces = inst.getSuperclass();
if (interfaces != null) {
String superMethodName = mapMethodInternal(interfaces, name, parameterTypes);
return String.valueOf(superMethodName);
}
return null;
}
}
Loading

0 comments on commit 9582a7a

Please sign in to comment.