mirror of
https://github.com/Minestom/Minestom.git
synced 2024-12-26 19:18:12 +01:00
Very basic Mixin support
This commit is contained in:
parent
223af361d8
commit
26b8ad125e
13
build.gradle
13
build.gradle
@ -30,6 +30,10 @@ allprojects {
|
||||
mavenCentral()
|
||||
maven { url 'https://libraries.minecraft.net' }
|
||||
maven { url 'https://jitpack.io' }
|
||||
maven {
|
||||
name 'sponge'
|
||||
url 'http://repo.spongepowered.org/maven'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -112,8 +116,13 @@ dependencies {
|
||||
annotationProcessor 'org.projectlombok:lombok:1.18.12'
|
||||
|
||||
// Code modification
|
||||
api "org.ow2.asm:asm:6.2.1"
|
||||
api "org.ow2.asm:asm-tree:6.2.1"
|
||||
api "org.ow2.asm:asm:${asmVersion}"
|
||||
api "org.ow2.asm:asm-tree:${asmVersion}"
|
||||
api "org.ow2.asm:asm-analysis:${asmVersion}"
|
||||
api "org.ow2.asm:asm-util:${asmVersion}"
|
||||
api "org.ow2.asm:asm-commons:${asmVersion}"
|
||||
implementation 'com.google.guava:guava:21.0'
|
||||
api "org.spongepowered:mixin:${mixinVersion}"
|
||||
|
||||
// Path finding
|
||||
api 'com.github.MadMartian:hydrazine-path-finding:1.3.1'
|
||||
|
2
gradle.properties
Normal file
2
gradle.properties
Normal file
@ -0,0 +1,2 @@
|
||||
asmVersion=8.0.1
|
||||
mixinVersion=0.8
|
@ -1,9 +1,16 @@
|
||||
package net.minestom.server;
|
||||
|
||||
import net.minestom.server.extras.selfmodification.MinestomOverwriteClassLoader;
|
||||
import net.minestom.server.extras.selfmodification.mixins.MixinCodeModifier;
|
||||
import org.spongepowered.asm.launch.MixinBootstrap;
|
||||
import org.spongepowered.asm.launch.platform.CommandLineOptions;
|
||||
import org.spongepowered.asm.launch.platform.MixinPlatformManager;
|
||||
import org.spongepowered.asm.mixin.MixinEnvironment;
|
||||
import org.spongepowered.asm.mixin.Mixins;
|
||||
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* Used to launch Minestom with the {@link MinestomOverwriteClassLoader} to allow for self-modifications
|
||||
@ -12,13 +19,17 @@ public class Bootstrap {
|
||||
|
||||
public static void bootstrap(String mainClassFullName, String[] args) {
|
||||
try {
|
||||
ClassLoader classLoader = new MinestomOverwriteClassLoader(Bootstrap.class.getClassLoader());
|
||||
ClassLoader classLoader = MinestomOverwriteClassLoader.getInstance();
|
||||
startMixin(args);
|
||||
MixinEnvironment.init(MixinEnvironment.Phase.INIT);
|
||||
MinestomOverwriteClassLoader.getInstance().addCodeModifier(new MixinCodeModifier());
|
||||
|
||||
// ensure extensions are loaded when starting the server
|
||||
Class<?> serverClass = classLoader.loadClass("net.minestom.server.MinecraftServer");
|
||||
Method init = serverClass.getMethod("init");
|
||||
init.invoke(null);
|
||||
|
||||
|
||||
Class<?> mainClass = classLoader.loadClass(mainClassFullName);
|
||||
Method main = mainClass.getDeclaredMethod("main", String[].class);
|
||||
main.invoke(null, new Object[] { args });
|
||||
@ -26,4 +37,21 @@ public class Bootstrap {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
private static void startMixin(String[] args) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
|
||||
Method start = MixinBootstrap.class.getDeclaredMethod("start");
|
||||
start.setAccessible(true);
|
||||
if (! ((boolean)start.invoke(null)) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
Method doInit = MixinBootstrap.class.getDeclaredMethod("doInit", CommandLineOptions.class);
|
||||
doInit.setAccessible(true);
|
||||
doInit.invoke(null, CommandLineOptions.ofArgs(Arrays.asList(args)));
|
||||
|
||||
MixinBootstrap.getPlatform().inject();
|
||||
Mixins.getConfigs().forEach(c -> {
|
||||
MinestomOverwriteClassLoader.getInstance().protectedPackages.add(c.getConfig().getMixinPackage());
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -7,11 +7,13 @@ import org.objectweb.asm.ClassWriter;
|
||||
import org.objectweb.asm.tree.ClassNode;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
import java.net.URLClassLoader;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
@ -19,14 +21,25 @@ import java.util.Set;
|
||||
@Slf4j
|
||||
public class MinestomOverwriteClassLoader extends URLClassLoader {
|
||||
|
||||
private static MinestomOverwriteClassLoader INSTANCE;
|
||||
|
||||
/**
|
||||
* Classes that cannot be loaded/modified by this classloader.
|
||||
* Will go through parent class loader
|
||||
*/
|
||||
private static final Set<String> protectedClasses = Set.of(
|
||||
"net.minestom.server.extras.selfmodification.CodeModifier",
|
||||
"net.minestom.server.extras.selfmodification.MinestomOverwriteClassLoader"
|
||||
);
|
||||
public final Set<String> protectedClasses = new HashSet<>() {
|
||||
{
|
||||
add("net.minestom.server.extras.selfmodification.CodeModifier");
|
||||
add("net.minestom.server.extras.selfmodification.MinestomOverwriteClassLoader");
|
||||
}
|
||||
};
|
||||
public final Set<String> protectedPackages = new HashSet<>() {
|
||||
{
|
||||
add("com.google");
|
||||
add("com.mojang");
|
||||
add("org.objectweb.asm");
|
||||
}
|
||||
};
|
||||
private final URLClassLoader asmClassLoader;
|
||||
|
||||
// TODO: replace by tree to optimize lookup times. We can use the fact that package names are split with '.' to allow for fast lookup
|
||||
@ -38,7 +51,7 @@ public class MinestomOverwriteClassLoader extends URLClassLoader {
|
||||
private final Method findParentLoadedClass;
|
||||
private final Class<?> loadedCodeModifier;
|
||||
|
||||
public MinestomOverwriteClassLoader(ClassLoader parent) {
|
||||
private MinestomOverwriteClassLoader(ClassLoader parent) {
|
||||
super("Minestom ClassLoader", loadURLs(), parent);
|
||||
try {
|
||||
findParentLoadedClass = ClassLoader.class.getDeclaredMethod("findLoadedClass", String.class);
|
||||
@ -58,6 +71,17 @@ public class MinestomOverwriteClassLoader extends URLClassLoader {
|
||||
asmClassLoader = newChild(new URL[0]);
|
||||
}
|
||||
|
||||
public static MinestomOverwriteClassLoader getInstance() {
|
||||
if(INSTANCE == null) {
|
||||
synchronized (MinestomOverwriteClassLoader.class) {
|
||||
if(INSTANCE == null) {
|
||||
INSTANCE = new MinestomOverwriteClassLoader(MinestomOverwriteClassLoader.class.getClassLoader());
|
||||
}
|
||||
}
|
||||
}
|
||||
return INSTANCE;
|
||||
}
|
||||
|
||||
private static URL[] loadURLs() {
|
||||
String classpath = System.getProperty("java.class.path");
|
||||
String[] parts = classpath.split(";");
|
||||
@ -110,9 +134,7 @@ public class MinestomOverwriteClassLoader extends URLClassLoader {
|
||||
return super.loadClass(name, resolve);
|
||||
}
|
||||
|
||||
String path = name.replace(".", "/") + ".class";
|
||||
byte[] bytes = getResourceAsStream(path).readAllBytes();
|
||||
return transformAndLoad(name, bytes, resolve);
|
||||
return define(name, loadBytes(name, true), resolve);
|
||||
} catch (Exception ex) {
|
||||
log.trace("Fail to load class, resorting to parent loader: "+name, ex);
|
||||
// fail to load class, let parent load
|
||||
@ -123,32 +145,17 @@ public class MinestomOverwriteClassLoader extends URLClassLoader {
|
||||
}
|
||||
|
||||
private boolean isProtected(String name) {
|
||||
return protectedClasses.contains(name);
|
||||
if(!protectedClasses.contains(name)) {
|
||||
for(String start : protectedPackages) {
|
||||
if(name.startsWith(start))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private Class<?> transformAndLoad(String name, byte[] bytes, boolean resolve) throws ClassNotFoundException {
|
||||
ClassReader reader = new ClassReader(bytes);
|
||||
ClassNode node = new ClassNode();
|
||||
reader.accept(node, 0);
|
||||
boolean modified = false;
|
||||
synchronized (modifiers) {
|
||||
for(CodeModifier modifier : modifiers) {
|
||||
boolean shouldModify = modifier.getNamespace() == null || name.startsWith(modifier.getNamespace());
|
||||
if(shouldModify) {
|
||||
modified |= modifier.transform(node);
|
||||
}
|
||||
}
|
||||
}
|
||||
if(modified) {
|
||||
ClassWriter writer = new ClassWriter(ClassWriter.COMPUTE_FRAMES) {
|
||||
@Override
|
||||
protected ClassLoader getClassLoader() {
|
||||
return asmClassLoader;
|
||||
}
|
||||
};
|
||||
node.accept(writer);
|
||||
bytes = writer.toByteArray();
|
||||
}
|
||||
private Class<?> define(String name, byte[] bytes, boolean resolve) throws ClassNotFoundException {
|
||||
Class<?> defined = defineClass(name, bytes, 0, bytes.length);
|
||||
log.trace("Loaded with code modifiers: "+name);
|
||||
if(resolve) {
|
||||
@ -157,6 +164,46 @@ public class MinestomOverwriteClassLoader extends URLClassLoader {
|
||||
return defined;
|
||||
}
|
||||
|
||||
public byte[] loadBytes(String name, boolean transform) throws IOException {
|
||||
String path = name.replace(".", "/") + ".class";
|
||||
byte[] bytes = getResourceAsStream(path).readAllBytes();
|
||||
if(transform && !isProtected(name)) {
|
||||
ClassReader reader = new ClassReader(bytes);
|
||||
ClassNode node = new ClassNode();
|
||||
reader.accept(node, 0);
|
||||
boolean modified = false;
|
||||
synchronized (modifiers) {
|
||||
for(CodeModifier modifier : modifiers) {
|
||||
boolean shouldModify = modifier.getNamespace() == null || name.startsWith(modifier.getNamespace());
|
||||
if(shouldModify) {
|
||||
modified |= modifier.transform(node);
|
||||
}
|
||||
}
|
||||
}
|
||||
if(modified) {
|
||||
ClassWriter writer = new ClassWriter(ClassWriter.COMPUTE_FRAMES) {
|
||||
@Override
|
||||
protected ClassLoader getClassLoader() {
|
||||
return asmClassLoader;
|
||||
}
|
||||
};
|
||||
node.accept(writer);
|
||||
bytes = writer.toByteArray();
|
||||
log.trace("Modified "+name);
|
||||
}
|
||||
}
|
||||
return bytes;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<?> findClass(String name) throws ClassNotFoundException {
|
||||
return super.findClass(name);
|
||||
}
|
||||
|
||||
public void resolve(Class<?> clazz) {
|
||||
resolveClass(clazz);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public URLClassLoader newChild(@NotNull URL[] urls) {
|
||||
return URLClassLoader.newInstance(urls, this);
|
||||
@ -174,11 +221,21 @@ public class MinestomOverwriteClassLoader extends URLClassLoader {
|
||||
CodeModifier modifier = (CodeModifier) modifierClass.getDeclaredConstructor().newInstance();
|
||||
synchronized (modifiers) {
|
||||
log.warn("Added Code modifier: "+modifier);
|
||||
modifiers.add(modifier);
|
||||
addCodeModifier(modifier);
|
||||
}
|
||||
}
|
||||
} catch (MalformedURLException | ClassNotFoundException | InvocationTargetException | InstantiationException | IllegalAccessException | NoSuchMethodException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
public void addCodeModifier(CodeModifier modifier) {
|
||||
synchronized (modifiers) {
|
||||
modifiers.add(modifier);
|
||||
}
|
||||
}
|
||||
|
||||
public List<CodeModifier> getModifiers() {
|
||||
return modifiers;
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,68 @@
|
||||
package net.minestom.server.extras.selfmodification.mixins;
|
||||
|
||||
import org.spongepowered.asm.service.IGlobalPropertyService;
|
||||
import org.spongepowered.asm.service.IPropertyKey;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
|
||||
public class GlobalPropertyServiceMinestom implements IGlobalPropertyService {
|
||||
|
||||
private class BasicProperty implements IPropertyKey {
|
||||
|
||||
private final String name;
|
||||
|
||||
public BasicProperty(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
BasicProperty that = (BasicProperty) o;
|
||||
return Objects.equals(name, that.name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "BasicProperty{" +
|
||||
"name='" + name + '\'' +
|
||||
'}';
|
||||
}
|
||||
}
|
||||
|
||||
private final Map<String, IPropertyKey> keys = new HashMap<>();
|
||||
private final Map<IPropertyKey, Object> values = new HashMap<>();
|
||||
|
||||
@Override
|
||||
public IPropertyKey resolveKey(String name) {
|
||||
return keys.computeIfAbsent(name, k -> new BasicProperty(k));
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> T getProperty(IPropertyKey key) {
|
||||
return (T) values.get(key);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setProperty(IPropertyKey key, Object value) {
|
||||
values.put(key, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> T getProperty(IPropertyKey key, T defaultValue) {
|
||||
return (T) values.getOrDefault(key, defaultValue);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPropertyString(IPropertyKey key, String defaultValue) {
|
||||
return (String) values.getOrDefault(key, defaultValue);
|
||||
}
|
||||
}
|
@ -0,0 +1,38 @@
|
||||
package net.minestom.server.extras.selfmodification.mixins;
|
||||
|
||||
import net.minestom.server.extras.selfmodification.MinestomOverwriteClassLoader;
|
||||
import org.objectweb.asm.ClassReader;
|
||||
import org.objectweb.asm.tree.ClassNode;
|
||||
import org.spongepowered.asm.service.IClassBytecodeProvider;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
public class MinestomBytecodeProvider implements IClassBytecodeProvider {
|
||||
private final MinestomOverwriteClassLoader classLoader;
|
||||
|
||||
public MinestomBytecodeProvider(MinestomOverwriteClassLoader classLoader) {
|
||||
this.classLoader = classLoader;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ClassNode getClassNode(String name) throws ClassNotFoundException, IOException {
|
||||
return getClassNode(name, false);
|
||||
}
|
||||
|
||||
private ClassNode loadNode(String name, boolean transform) throws ClassNotFoundException {
|
||||
ClassNode node = new ClassNode();
|
||||
ClassReader reader;
|
||||
try {
|
||||
reader = new ClassReader(classLoader.loadBytes(name, transform));
|
||||
} catch (IOException e) {
|
||||
throw new ClassNotFoundException("Could not load ClassNode with name "+name, e);
|
||||
}
|
||||
reader.accept(node, 0);
|
||||
return node;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ClassNode getClassNode(String name, boolean runTransformers) throws ClassNotFoundException, IOException {
|
||||
return loadNode(name, runTransformers);
|
||||
}
|
||||
}
|
@ -0,0 +1,34 @@
|
||||
package net.minestom.server.extras.selfmodification.mixins;
|
||||
|
||||
import net.minestom.server.extras.selfmodification.MinestomOverwriteClassLoader;
|
||||
import org.spongepowered.asm.service.IClassProvider;
|
||||
|
||||
import java.net.URL;
|
||||
|
||||
public class MinestomClassProvider implements IClassProvider {
|
||||
private final MinestomOverwriteClassLoader classLoader;
|
||||
|
||||
public MinestomClassProvider(MinestomOverwriteClassLoader classLoader) {
|
||||
this.classLoader = classLoader;
|
||||
}
|
||||
|
||||
@Override
|
||||
public URL[] getClassPath() {
|
||||
return classLoader.getURLs();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<?> findClass(String name) throws ClassNotFoundException {
|
||||
return classLoader.findClass(name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<?> findClass(String name, boolean initialize) throws ClassNotFoundException {
|
||||
return Class.forName(name, initialize, Thread.currentThread().getContextClassLoader());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<?> findAgentClass(String name, boolean initialize) throws ClassNotFoundException {
|
||||
return Class.forName(name, initialize, classLoader);
|
||||
}
|
||||
}
|
@ -0,0 +1,75 @@
|
||||
package net.minestom.server.extras.selfmodification.mixins;
|
||||
|
||||
import net.minestom.server.extras.selfmodification.CodeModifier;
|
||||
import net.minestom.server.extras.selfmodification.MinestomOverwriteClassLoader;
|
||||
import org.objectweb.asm.tree.ClassNode;
|
||||
import org.spongepowered.asm.service.ITransformer;
|
||||
import org.spongepowered.asm.service.ITransformerProvider;
|
||||
import org.spongepowered.asm.service.ITreeClassTransformer;
|
||||
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.util.Collection;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
public class MinestomTransformerProvider implements ITransformerProvider {
|
||||
private final MinestomOverwriteClassLoader classLoader;
|
||||
private List<ITransformer> transformers;
|
||||
|
||||
public MinestomTransformerProvider(MinestomOverwriteClassLoader classLoader) {
|
||||
this.classLoader = classLoader;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addTransformerExclusion(String name) {
|
||||
classLoader.protectedClasses.add(name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<ITransformer> getTransformers() {
|
||||
return getDelegatedTransformers();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<ITransformer> getDelegatedTransformers() {
|
||||
if(transformers == null) {
|
||||
transformers = buildTransformerList();
|
||||
}
|
||||
return transformers;
|
||||
}
|
||||
|
||||
private List<ITransformer> buildTransformerList() {
|
||||
List<ITransformer> result = new LinkedList<>();
|
||||
for(CodeModifier modifier : classLoader.getModifiers()) {
|
||||
result.add(toMixin(modifier));
|
||||
}
|
||||
|
||||
try {
|
||||
Class<?> clazz = classLoader.loadClass("org.spongepowered.asm.mixin.transformer.MixingTransformer");
|
||||
ITransformer mixinTransformer = (ITransformer) clazz.getDeclaredConstructor().newInstance();
|
||||
result.add(mixinTransformer);
|
||||
} catch (ClassNotFoundException | InstantiationException | InvocationTargetException | NoSuchMethodException | IllegalAccessException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private ITransformer toMixin(CodeModifier modifier) {
|
||||
return new ITreeClassTransformer() {
|
||||
@Override
|
||||
public boolean transformClassNode(String name, String transformedName, ClassNode classNode) {
|
||||
return modifier.transform(classNode);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return modifier.getClass().getName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isDelegationExcluded() {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
@ -0,0 +1,59 @@
|
||||
package net.minestom.server.extras.selfmodification.mixins;
|
||||
|
||||
import net.minestom.server.extras.selfmodification.CodeModifier;
|
||||
import org.objectweb.asm.tree.ClassNode;
|
||||
import org.spongepowered.asm.mixin.MixinEnvironment;
|
||||
import org.spongepowered.asm.mixin.transformer.MixinProcessor;
|
||||
import org.spongepowered.asm.mixin.transformer.ext.Extensions;
|
||||
import org.spongepowered.asm.mixin.transformer.ext.IHotSwap;
|
||||
import org.spongepowered.asm.service.ISyntheticClassInfo;
|
||||
import org.spongepowered.asm.service.ISyntheticClassRegistry;
|
||||
import org.spongepowered.asm.transformers.TreeTransformer;
|
||||
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
public class MixinCodeModifier extends CodeModifier {
|
||||
|
||||
private Method transformClassMethod;
|
||||
private TreeTransformer processor;
|
||||
|
||||
public MixinCodeModifier() {
|
||||
try {
|
||||
Class<?> mixinTransformerClass = Class.forName("org.spongepowered.asm.mixin.transformer.MixinTransformer");
|
||||
Constructor<?> ctor = mixinTransformerClass.getDeclaredConstructor();
|
||||
ctor.setAccessible(true);
|
||||
this.processor = (TreeTransformer) ctor.newInstance();
|
||||
transformClassMethod = mixinTransformerClass.getDeclaredMethod("transformClass", MixinEnvironment.class, String.class, ClassNode.class);
|
||||
transformClassMethod.setAccessible(true);
|
||||
} catch (NoSuchMethodException e) {
|
||||
e.printStackTrace();
|
||||
} catch (IllegalAccessException e) {
|
||||
e.printStackTrace();
|
||||
} catch (InstantiationException e) {
|
||||
e.printStackTrace();
|
||||
} catch (InvocationTargetException e) {
|
||||
e.printStackTrace();
|
||||
} catch (ClassNotFoundException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean transform(ClassNode source) {
|
||||
try {
|
||||
return (boolean) transformClassMethod.invoke(processor, MixinEnvironment.getEnvironment(MixinEnvironment.Phase.DEFAULT), source.name.replace("/", "."), source);
|
||||
} catch (IllegalAccessException e) {
|
||||
e.printStackTrace();
|
||||
} catch (InvocationTargetException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getNamespace() {
|
||||
return null;
|
||||
}
|
||||
}
|
@ -0,0 +1,29 @@
|
||||
package net.minestom.server.extras.selfmodification.mixins;
|
||||
|
||||
import org.spongepowered.asm.launch.platform.IMixinPlatformServiceAgent;
|
||||
import org.spongepowered.asm.launch.platform.MixinPlatformAgentAbstract;
|
||||
import org.spongepowered.asm.launch.platform.MixinPlatformManager;
|
||||
import org.spongepowered.asm.launch.platform.container.IContainerHandle;
|
||||
import org.spongepowered.asm.util.Constants;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
public class MixinPlatformAgentMinestom extends MixinPlatformAgentAbstract implements IMixinPlatformServiceAgent {
|
||||
@Override
|
||||
public void init() { }
|
||||
|
||||
@Override
|
||||
public String getSideName() {
|
||||
return Constants.SIDE_SERVER;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AcceptResult accept(MixinPlatformManager manager, IContainerHandle handle) {
|
||||
return AcceptResult.ACCEPTED;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<IContainerHandle> getMixinContainers() {
|
||||
return null;
|
||||
}
|
||||
}
|
@ -0,0 +1,86 @@
|
||||
package net.minestom.server.extras.selfmodification.mixins;
|
||||
|
||||
import net.minestom.server.extras.selfmodification.MinestomOverwriteClassLoader;
|
||||
import org.spongepowered.asm.launch.platform.container.ContainerHandleVirtual;
|
||||
import org.spongepowered.asm.launch.platform.container.IContainerHandle;
|
||||
import org.spongepowered.asm.mixin.MixinEnvironment;
|
||||
import org.spongepowered.asm.service.*;
|
||||
import org.spongepowered.asm.util.IConsumer;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
|
||||
public class MixinServiceMinestom extends MixinServiceAbstract {
|
||||
|
||||
private final MinestomOverwriteClassLoader classLoader;
|
||||
private final MinestomClassProvider classProvider;
|
||||
private final MinestomBytecodeProvider bytecodeProvider;
|
||||
private final MinestomTransformerProvider transformerProvider;
|
||||
|
||||
public MixinServiceMinestom() {
|
||||
this.classLoader = MinestomOverwriteClassLoader.getInstance();
|
||||
classProvider = new MinestomClassProvider(classLoader);
|
||||
bytecodeProvider = new MinestomBytecodeProvider(classLoader);
|
||||
transformerProvider = new MinestomTransformerProvider(classLoader);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "Minestom";
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isValid() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public IClassProvider getClassProvider() {
|
||||
return classProvider;
|
||||
}
|
||||
|
||||
@Override
|
||||
public IClassBytecodeProvider getBytecodeProvider() {
|
||||
return bytecodeProvider;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ITransformerProvider getTransformerProvider() {
|
||||
return transformerProvider;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<String> getPlatformAgents() {
|
||||
return Collections.singletonList("net.minestom.server.extras.selfmodification.mixins.MixinPlatformAgentMinestom");
|
||||
}
|
||||
|
||||
@Override
|
||||
public IContainerHandle getPrimaryContainer() {
|
||||
return new ContainerHandleVirtual("Minestom");
|
||||
}
|
||||
|
||||
@Override
|
||||
public InputStream getResourceAsStream(String name) {
|
||||
return classLoader.getResourceAsStream(name);
|
||||
}
|
||||
|
||||
// TODO: everything below
|
||||
|
||||
@Override
|
||||
public IClassTracker getClassTracker() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public IMixinAuditTrail getAuditTrail() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void wire(MixinEnvironment.Phase phase, IConsumer<MixinEnvironment.Phase> phaseConsumer) {
|
||||
super.wire(phase, phaseConsumer);
|
||||
phaseConsumer.accept(MixinEnvironment.Phase.PREINIT);
|
||||
phaseConsumer.accept(MixinEnvironment.Phase.INIT);
|
||||
}
|
||||
}
|
@ -0,0 +1,20 @@
|
||||
package net.minestom.server.extras.selfmodification.mixins;
|
||||
|
||||
import org.spongepowered.asm.service.IMixinServiceBootstrap;
|
||||
|
||||
public class MixinServiceMinestomBootstrap implements IMixinServiceBootstrap {
|
||||
@Override
|
||||
public String getName() {
|
||||
return "MinestomBootstrap";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getServiceClassName() {
|
||||
return "net.minestom.server.extras.selfmodification.mixins.MixinServiceMinestom";
|
||||
}
|
||||
|
||||
@Override
|
||||
public void bootstrap() {
|
||||
|
||||
}
|
||||
}
|
@ -0,0 +1 @@
|
||||
net.minestom.server.extras.selfmodification.mixins.GlobalPropertyServiceMinestom
|
@ -0,0 +1 @@
|
||||
net.minestom.server.extras.selfmodification.mixins.MixinServiceMinestom
|
@ -0,0 +1 @@
|
||||
net.minestom.server.extras.selfmodification.mixins.MixinServiceMinestomBootstrap
|
@ -2,13 +2,16 @@
|
||||
<Configuration status="WARN" strict="true" name="Minestom">
|
||||
<Appenders>
|
||||
<Appender type="Console" name="STDOUT">
|
||||
<Layout type="PatternLayout" pattern="[%t] [%d{HH:mm:ss}] %p - %m%n"/>
|
||||
<Layout type="PatternLayout" pattern="[%t] [%d{HH:mm:ss}] - %p - %m%n"/>
|
||||
</Appender>
|
||||
<Appender type="Console" name="STDOUT-WithCaller">
|
||||
<Layout type="PatternLayout" pattern="[%t] [%d{HH:mm:ss}] (%C{1}.%M) - %p - %m%n"/>
|
||||
</Appender>
|
||||
</Appenders>
|
||||
|
||||
<Loggers>
|
||||
<Root level="trace">
|
||||
<AppenderRef ref="STDOUT" level="info"/>
|
||||
<AppenderRef ref="STDOUT-WithCaller" level="info"/>
|
||||
</Root>
|
||||
</Loggers>
|
||||
</Configuration>
|
@ -1,12 +1,16 @@
|
||||
package testextension;
|
||||
|
||||
import net.minestom.server.Bootstrap;
|
||||
import org.spongepowered.asm.launch.MixinBootstrap;
|
||||
import org.spongepowered.asm.mixin.Mixins;
|
||||
|
||||
// To launch with VM arguments:
|
||||
// -Dminestom.extension.indevfolder.classes=build/classes/java/test/ -Dminestom.extension.indevfolder.resources=build/resources/test/
|
||||
public class TestExtensionLauncher {
|
||||
|
||||
public static void main(String[] args) {
|
||||
MixinBootstrap.init();
|
||||
Mixins.addConfiguration("mixins.testextension.json");
|
||||
Bootstrap.bootstrap("fr.themode.demo.MainDemo", args);
|
||||
}
|
||||
|
||||
|
17
src/test/java/testextension/TestExtensionLauncherArgs.java
Normal file
17
src/test/java/testextension/TestExtensionLauncherArgs.java
Normal file
@ -0,0 +1,17 @@
|
||||
package testextension;
|
||||
|
||||
import net.minestom.server.Bootstrap;
|
||||
|
||||
// To launch with VM arguments:
|
||||
// -Dminestom.extension.indevfolder.classes=build/classes/java/test/ -Dminestom.extension.indevfolder.resources=build/resources/test/
|
||||
public class TestExtensionLauncherArgs {
|
||||
|
||||
public static void main(String[] args) {
|
||||
String[] argsWithMixins = new String[args.length+2];
|
||||
System.arraycopy(args, 0, argsWithMixins, 0, args.length);
|
||||
argsWithMixins[argsWithMixins.length-2] = "--mixin";
|
||||
argsWithMixins[argsWithMixins.length-1] = "mixins.testextension.json";
|
||||
Bootstrap.bootstrap("fr.themode.demo.MainDemo", argsWithMixins);
|
||||
}
|
||||
|
||||
}
|
@ -10,7 +10,7 @@ public class TestModifier extends CodeModifier implements Opcodes {
|
||||
@Override
|
||||
public boolean transform(ClassNode source) {
|
||||
if(source.name.equals("net/minestom/server/instance/InstanceContainer")) {
|
||||
System.out.println("Modifying code source of "+source.name);
|
||||
System.out.println("Modifying code of "+source.name);
|
||||
MethodNode constructor = findConstructor(source.methods);
|
||||
constructor.instructions.insert(constructor.instructions.getFirst(), buildInjectionCode());
|
||||
return true;
|
||||
|
@ -0,0 +1,18 @@
|
||||
package testextension.mixins;
|
||||
|
||||
import net.minestom.server.instance.InstanceContainer;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
|
||||
@Mixin(InstanceContainer.class)
|
||||
public class InstanceContainerMixin {
|
||||
|
||||
@Inject(method = "<init>", at = @At("RETURN"))
|
||||
private void onRunHead(CallbackInfo ci) {
|
||||
System.out.println("Hello from Mixin!!!");
|
||||
}
|
||||
|
||||
|
||||
}
|
10
src/test/resources/mixins.testextension.json
Normal file
10
src/test/resources/mixins.testextension.json
Normal file
@ -0,0 +1,10 @@
|
||||
{
|
||||
"required": true,
|
||||
"minVersion": "0.8",
|
||||
"package": "testextension.mixins",
|
||||
"target": "@env(DEFAULT)",
|
||||
"compatibilityLevel": "JAVA_11",
|
||||
"mixins": [
|
||||
"InstanceContainerMixin"
|
||||
]
|
||||
}
|
Loading…
Reference in New Issue
Block a user