mirror of
https://github.com/Minestom/Minestom.git
synced 2024-12-26 19:18:12 +01:00
Remove Bootstrap
, MinestomRootClassLoader
. Cleanup
This commit is contained in:
parent
b558125bee
commit
0c6e4c0129
@ -1,25 +0,0 @@
|
||||
package net.minestom.server;
|
||||
|
||||
import net.minestom.server.extras.selfmodification.MinestomRootClassLoader;
|
||||
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
/**
|
||||
* Used to launch Minestom with the {@link MinestomRootClassLoader} to allow for self-modifications
|
||||
*/
|
||||
public final class Bootstrap {
|
||||
|
||||
public static void bootstrap(String mainClassFullName, String[] args) {
|
||||
try {
|
||||
ClassLoader classLoader = MinestomRootClassLoader.getInstance();
|
||||
|
||||
Class<?> mainClass = classLoader.loadClass(mainClassFullName);
|
||||
Method main = mainClass.getDeclaredMethod("main", String[].class);
|
||||
main.setAccessible(true);
|
||||
main.invoke(null, new Object[]{args});
|
||||
} catch (InvocationTargetException | NoSuchMethodException | IllegalAccessException | ClassNotFoundException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
@ -2,8 +2,7 @@ package net.minestom.server.extensions;
|
||||
|
||||
import com.google.gson.JsonObject;
|
||||
import net.minestom.server.MinecraftServer;
|
||||
import net.minestom.server.extras.selfmodification.MinestomExtensionClassLoader;
|
||||
import net.minestom.server.extras.selfmodification.MinestomRootClassLoader;
|
||||
import net.minestom.server.extensions.isolation.MinestomExtensionClassLoader;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.slf4j.Logger;
|
||||
@ -15,7 +14,6 @@ import java.nio.file.Path;
|
||||
import java.util.Arrays;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
|
||||
/**
|
||||
* Represents an extension from an `extension.json` that is capable of powering an Extension object.
|
||||
@ -211,7 +209,7 @@ public final class DiscoveredExtension {
|
||||
public MinestomExtensionClassLoader makeClassLoader(List<DiscoveredExtension> discoveredExtensions) {
|
||||
final URL[] urls = this.files.toArray(new URL[0]);
|
||||
|
||||
MinestomExtensionClassLoader loader = new MinestomExtensionClassLoader(this.getName(), this.getEntrypoint(), urls, MinecraftServer.class.getClassLoader());
|
||||
MinestomExtensionClassLoader loader = new MinestomExtensionClassLoader(this.getName(), urls);
|
||||
|
||||
System.out.println("CREATED " + loader + " WITH " + Arrays.toString(urls));
|
||||
|
||||
|
@ -7,8 +7,7 @@ import net.minestom.dependencies.maven.MavenRepository;
|
||||
import net.minestom.server.MinecraftServer;
|
||||
import net.minestom.server.event.Event;
|
||||
import net.minestom.server.event.EventNode;
|
||||
import net.minestom.server.extras.selfmodification.MinestomExtensionClassLoader;
|
||||
import net.minestom.server.extras.selfmodification.MinestomRootClassLoader;
|
||||
import net.minestom.server.extensions.isolation.MinestomExtensionClassLoader;
|
||||
import net.minestom.server.ping.ResponseDataConsumer;
|
||||
import net.minestom.server.utils.time.TimeUnit;
|
||||
import net.minestom.server.utils.validate.Check;
|
||||
@ -644,7 +643,8 @@ public class ExtensionManager {
|
||||
} catch (IOException e) {
|
||||
MinecraftServer.getExceptionManager().handleException(e);
|
||||
}
|
||||
MinestomRootClassLoader.getInstance().removeChildInHierarchy(classloader);
|
||||
//TODO : Remove extension from dependents
|
||||
// MinestomRootClassLoader.getInstance().removeChildInHierarchy(classloader);
|
||||
}
|
||||
|
||||
public boolean reload(@NotNull String extensionName) {
|
||||
|
@ -1,4 +1,4 @@
|
||||
package net.minestom.server.extras.selfmodification;
|
||||
package net.minestom.server.extensions.isolation;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
@ -22,6 +22,24 @@ public abstract class HierarchyClassLoader extends URLClassLoader {
|
||||
children.add(loader);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
|
||||
try {
|
||||
System.out.println("TRYING 2 LOAD " + name + " IN " + getName());
|
||||
return super.loadClass(name, resolve);
|
||||
} catch (ClassNotFoundException e) {
|
||||
System.out.println("COULD NOT LOAD 2, TRYING CHILDREN");
|
||||
for (MinestomExtensionClassLoader child : children) {
|
||||
try {
|
||||
return child.loadClass(name, resolve);
|
||||
} catch (ClassNotFoundException ignored) {
|
||||
System.out.println("NOT FOUND IN " + child.getName() + " EITHER");
|
||||
}
|
||||
}
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
public InputStream getResourceAsStreamWithChildren(@NotNull String name) {
|
||||
InputStream in = getResourceAsStream(name);
|
||||
if (in != null) return in;
|
@ -0,0 +1,47 @@
|
||||
package net.minestom.server.extensions.isolation;
|
||||
|
||||
import net.minestom.server.MinecraftServer;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.net.URL;
|
||||
|
||||
public class MinestomExtensionClassLoader extends HierarchyClassLoader {
|
||||
private final Logger logger = LoggerFactory.getLogger(MinestomExtensionClassLoader.class);
|
||||
|
||||
public MinestomExtensionClassLoader(String extensionName, URL[] urls) {
|
||||
super("Ext_" + extensionName, urls, MinecraftServer.class.getClassLoader());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the name of the extension linked to this classloader
|
||||
* @return the name of the extension linked to this classloader
|
||||
*/
|
||||
public String getExtensionName() {
|
||||
// simply calls ClassLoader#getName as the extension name is used to name this classloader
|
||||
// this method is simply for ease-of-use
|
||||
return getName();
|
||||
}
|
||||
|
||||
/**
|
||||
* Tries to know which extension created this object, based on the classloader of the object. This can only check that the class of the object has been loaded
|
||||
* by an extension.
|
||||
*
|
||||
* While not perfect, this should detect any callback created via extension code.
|
||||
* It is possible this current version of the implementation might struggle with callbacks created through external
|
||||
* libraries, but as libraries are loaded separately for each extension, this *should not*(tm) be a problem.
|
||||
*
|
||||
* @param obj the object to get the extension of
|
||||
* @return <code>null</code> if no extension has been found, otherwise the extension name
|
||||
*/
|
||||
@Nullable
|
||||
public static String findExtensionObjectOwner(@NotNull Object obj) {
|
||||
ClassLoader cl = obj.getClass().getClassLoader();
|
||||
if (cl instanceof MinestomExtensionClassLoader extensionClassLoader) {
|
||||
return extensionClassLoader.getExtensionName();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
@ -1,146 +0,0 @@
|
||||
package net.minestom.server.extras.selfmodification;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.net.URL;
|
||||
|
||||
public class MinestomExtensionClassLoader extends HierarchyClassLoader {
|
||||
/**
|
||||
* Root ClassLoader, everything goes through it before any attempt at loading is done inside this classloader
|
||||
*/
|
||||
private final ClassLoader root;
|
||||
|
||||
/**
|
||||
* Main of the main class of the extension linked to this classloader
|
||||
*/
|
||||
private final String mainClassName;
|
||||
|
||||
private final Logger logger = LoggerFactory.getLogger(MinestomExtensionClassLoader.class);
|
||||
|
||||
public MinestomExtensionClassLoader(String extensionName, String mainClassName, URL[] urls, ClassLoader root) {
|
||||
super(extensionName, urls, root);
|
||||
this.root = root;
|
||||
this.mainClassName = mainClassName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the name of the extension linked to this classloader
|
||||
* @return the name of the extension linked to this classloader
|
||||
*/
|
||||
public String getExtensionName() {
|
||||
// simply calls ClassLoader#getName as the extension name is used to name this classloader
|
||||
// this method is simply for ease-of-use
|
||||
return getName();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the main class name linked to the extension responsible for this classloader.
|
||||
* Used by the root classloader to let extensions load themselves in a dev environment.
|
||||
* @return the main class name linked to the extension responsible for this classloader
|
||||
*/
|
||||
public String getMainClassName() {
|
||||
return mainClassName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<?> loadClass(String name) throws ClassNotFoundException {
|
||||
try {
|
||||
System.out.println("TRYING TO LOAD " + name + " IN " + getName());
|
||||
return super.loadClass(name);
|
||||
} catch (ClassNotFoundException e) {
|
||||
System.out.println("COULD NOT LOAD, TRYING CHILDREN");
|
||||
for (MinestomExtensionClassLoader child : children) {
|
||||
try {
|
||||
return child.loadClass(name);
|
||||
} catch (ClassNotFoundException ignored) {
|
||||
System.out.println("NOT FOUND IN " + child.getName() + " EITHER");
|
||||
}
|
||||
}
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
|
||||
try {
|
||||
System.out.println("TRYING 2 LOAD " + name + " IN " + getName());
|
||||
return super.loadClass(name, resolve);
|
||||
} catch (ClassNotFoundException e) {
|
||||
System.out.println("COULD NOT LOAD 2, TRYING CHILDREN");
|
||||
for (MinestomExtensionClassLoader child : children) {
|
||||
try {
|
||||
return child.loadClass(name, resolve);
|
||||
} catch (ClassNotFoundException ignored) {
|
||||
System.out.println("NOT FOUND IN " + child.getName() + " EITHER");
|
||||
}
|
||||
}
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Assumes the name is not null, nor it does represent a protected class
|
||||
* @param name
|
||||
* @return
|
||||
* @throws ClassNotFoundException if the class is not found inside this classloader
|
||||
*/
|
||||
public Class<?> loadClassAsChild(String name, boolean resolve) throws ClassNotFoundException {
|
||||
throw new RuntimeException("Cannot load " + name + " using old mechanism");
|
||||
// logger.info("Loading class " + name + " as child of " + getName());
|
||||
// Class<?> loadedClass = findLoadedClass(name);
|
||||
// if(loadedClass != null) {
|
||||
// logger.info("Found loaded class");
|
||||
// return loadedClass;
|
||||
// }
|
||||
//
|
||||
// try {
|
||||
// // not in children, attempt load in this classloader
|
||||
// String path = name.replace(".", "/") + ".class";
|
||||
// InputStream in = getResourceAsStream(path);
|
||||
// if (in == null) {
|
||||
// throw new ClassNotFoundException("Could not load class " + name);
|
||||
// }
|
||||
// logger.info("Found in resources");
|
||||
// try (in) {
|
||||
// byte[] bytes = in.readAllBytes();
|
||||
// Class<?> clazz = defineClass(name, bytes, 0, bytes.length);
|
||||
// if (resolve) {
|
||||
// resolveClass(clazz);
|
||||
// }
|
||||
// return clazz;
|
||||
// } catch (IOException e) {
|
||||
// throw new ClassNotFoundException("Could not load class " + name, e);
|
||||
// }
|
||||
// } catch (ClassNotFoundException e) {
|
||||
// for(MinestomExtensionClassLoader child : children) {
|
||||
// try {
|
||||
// return child.loadClassAsChild(name, resolve);
|
||||
// } catch (ClassNotFoundException e1) {
|
||||
// // move on to next child
|
||||
// }
|
||||
// }
|
||||
// throw e;
|
||||
// }
|
||||
}
|
||||
|
||||
/**
|
||||
* Is the given class name the name of the entry point of one the extensions from this classloader chain?
|
||||
* @param name the class name to check
|
||||
* @return whether the given class name the name of the entry point of one the extensions from this classloader chain
|
||||
* @see MinestomRootClassLoader#loadBytes(String, boolean) for more information
|
||||
*/
|
||||
protected boolean isMainExtensionClass(String name) {
|
||||
|
||||
if (mainClassName.equals(name))
|
||||
return true;
|
||||
|
||||
for (MinestomExtensionClassLoader child : children) {
|
||||
if (child.isMainExtensionClass(name)) return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
@ -1,262 +0,0 @@
|
||||
package net.minestom.server.extras.selfmodification;
|
||||
|
||||
import net.minestom.server.MinecraftServer;
|
||||
import net.minestom.server.extensions.ExtensionManager;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
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;
|
||||
|
||||
/**
|
||||
* Class Loader that can modify class bytecode when they are loaded
|
||||
*/
|
||||
public class MinestomRootClassLoader extends HierarchyClassLoader {
|
||||
|
||||
public final static Logger LOGGER = LoggerFactory.getLogger(MinestomRootClassLoader.class);
|
||||
|
||||
private static volatile MinestomRootClassLoader INSTANCE;
|
||||
|
||||
/**
|
||||
* Classes that cannot be loaded/modified by this classloader.
|
||||
* Will go through parent class loader
|
||||
*/
|
||||
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.j2objc");
|
||||
add("com.google.common"); // guava
|
||||
add("com.google.errorprone");
|
||||
add("com.google.gson");
|
||||
add("com.mojang");
|
||||
add("org.objectweb.asm");
|
||||
add("org.slf4j");
|
||||
add("org.apache.logging");
|
||||
add("org.spongepowered.asm"); // Mixin
|
||||
add("org.spongepowered.tools"); // Mixin
|
||||
add("net.minestom.server.extras.selfmodification");
|
||||
add("org.jboss.shrinkwrap.resolver");
|
||||
add("kotlin");
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Used to let ASM find out common super types, without actually commiting to loading them
|
||||
* Otherwise ASM would accidentally load classes we might want to modify
|
||||
*/
|
||||
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
|
||||
// TODO: eg. Node("java", Node("lang"), Node("io")). Loading "java.nio.Channel" would apply modifiers from "java", but not "java.io" or "java.lang".
|
||||
// TODO: that's an example, please don't modify standard library classes. And this classloader should not let you do it because it first asks the platform classloader
|
||||
|
||||
/**
|
||||
* Whether Minestom detected that it is running in a dev environment.
|
||||
* Determined by the existence of the system property {@link ExtensionManager#INDEV_CLASSES_FOLDER}
|
||||
*/
|
||||
private boolean inDevEnvironment = false;
|
||||
|
||||
/**
|
||||
* List of already loaded code modifier class names. This prevents loading the same class twice.
|
||||
*/
|
||||
private final Set<String> alreadyLoadedCodeModifiers = new HashSet<>();
|
||||
|
||||
private MinestomRootClassLoader(ClassLoader parent) {
|
||||
super("Minestom Root ClassLoader", extractURLsFromClasspath(), parent);
|
||||
asmClassLoader = newChild(new URL[0]);
|
||||
inDevEnvironment = System.getProperty(ExtensionManager.INDEV_CLASSES_FOLDER) != null;
|
||||
}
|
||||
|
||||
public static MinestomRootClassLoader getInstance() {
|
||||
if (INSTANCE == null) {
|
||||
synchronized (MinestomRootClassLoader.class) {
|
||||
if (INSTANCE == null) {
|
||||
INSTANCE = new MinestomRootClassLoader(MinestomRootClassLoader.class.getClassLoader());
|
||||
}
|
||||
}
|
||||
}
|
||||
return INSTANCE;
|
||||
}
|
||||
|
||||
private static URL[] extractURLsFromClasspath() {
|
||||
String classpath = System.getProperty("java.class.path");
|
||||
String[] parts = classpath.split(";");
|
||||
URL[] urls = new URL[parts.length];
|
||||
for (int i = 0; i < urls.length; i++) {
|
||||
try {
|
||||
String part = parts[i];
|
||||
String protocol;
|
||||
if (part.contains("!")) {
|
||||
protocol = "jar://";
|
||||
} else {
|
||||
protocol = "file://";
|
||||
}
|
||||
urls[i] = new URL(protocol + part);
|
||||
} catch (MalformedURLException e) {
|
||||
throw new Error(e);
|
||||
}
|
||||
}
|
||||
return urls;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
|
||||
Class<?> loadedClass = findLoadedClass(name);
|
||||
if (loadedClass != null)
|
||||
return loadedClass;
|
||||
|
||||
try {
|
||||
// we do not load system classes by ourselves
|
||||
Class<?> systemClass = ClassLoader.getPlatformClassLoader().loadClass(name);
|
||||
LOGGER.info("System class: {}", systemClass);
|
||||
return systemClass;
|
||||
} catch (ClassNotFoundException e) {
|
||||
try {
|
||||
if (isProtected(name)) {
|
||||
LOGGER.info("Protected: {}", name);
|
||||
return super.loadClass(name, resolve);
|
||||
}
|
||||
|
||||
LOGGER.info("Define: {}", name);
|
||||
return define(name, resolve);
|
||||
} catch (Exception ex) {
|
||||
LOGGER.info("Fail to load class, resorting to parent loader: " + name, ex);
|
||||
// fail to load class, let parent load
|
||||
// this forbids code modification, but at least it will load
|
||||
return super.loadClass(name, resolve);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isProtected(String name) {
|
||||
if (!protectedClasses.contains(name)) {
|
||||
for (String start : protectedPackages) {
|
||||
if (name.startsWith(start))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private Class<?> define(String name, boolean resolve) throws IOException, ClassNotFoundException {
|
||||
try {
|
||||
byte[] bytes = loadBytes(name, true);
|
||||
Class<?> defined = defineClass(name, bytes, 0, bytes.length);
|
||||
LOGGER.trace("Loaded with code modifiers: {}", name);
|
||||
if (resolve) {
|
||||
resolveClass(defined);
|
||||
}
|
||||
return defined;
|
||||
} catch (ClassNotFoundException e) {
|
||||
// could not load inside this classloader, attempt with children
|
||||
Class<?> defined = null;
|
||||
for (MinestomExtensionClassLoader subloader : children) {
|
||||
try {
|
||||
defined = subloader.loadClassAsChild(name, resolve);
|
||||
LOGGER.trace("Loaded from child {}: {}", subloader, name);
|
||||
return defined;
|
||||
} catch (ClassNotFoundException e1) {
|
||||
// not found inside this child, move on to next
|
||||
}
|
||||
}
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads and possibly transforms class bytecode corresponding to the given binary name.
|
||||
*
|
||||
* @param name
|
||||
* @param transform
|
||||
* @return
|
||||
* @throws IOException
|
||||
* @throws ClassNotFoundException
|
||||
*/
|
||||
public byte[] loadBytes(String name, boolean transform) throws IOException, ClassNotFoundException {
|
||||
if (name == null)
|
||||
throw new ClassNotFoundException();
|
||||
String path = name.replace(".", "/") + ".class";
|
||||
|
||||
if(inDevEnvironment) {
|
||||
// check if the class to load is the entry point of the extension
|
||||
boolean isMainExtensionClass = false;
|
||||
for(MinestomExtensionClassLoader c : children) {
|
||||
if(c.isMainExtensionClass(name)) {
|
||||
isMainExtensionClass = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(isMainExtensionClass) { // entry point of the extension, force load through extension classloader
|
||||
throw new ClassNotFoundException("The class "+name+" is the entry point of an extension. " +
|
||||
"Because we are in a dev environment, we force its load through its extension classloader, " +
|
||||
"even though the root classloader has access.");
|
||||
}
|
||||
}
|
||||
InputStream input = getResourceAsStream(path);
|
||||
if (input == null) {
|
||||
throw new ClassNotFoundException("Could not find resource " + path);
|
||||
}
|
||||
return input.readAllBytes();
|
||||
}
|
||||
|
||||
public byte[] loadBytesWithChildren(@NotNull String name, boolean transform) throws IOException, ClassNotFoundException {
|
||||
String path = name.replace(".", "/") + ".class";
|
||||
InputStream input = getResourceAsStreamWithChildren(path);
|
||||
if (input == null) {
|
||||
throw new ClassNotFoundException("Could not find resource " + path);
|
||||
}
|
||||
return input.readAllBytes();
|
||||
}
|
||||
|
||||
// overriden to increase access (from protected to public)
|
||||
@Override
|
||||
public Class<?> findClass(String name) throws ClassNotFoundException {
|
||||
return super.findClass(name);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public URLClassLoader newChild(@NotNull URL[] urls) {
|
||||
return URLClassLoader.newInstance(urls, this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addURL(URL url) {
|
||||
super.addURL(url);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tries to know which extension created this object, based on the classloader of the object. This can only check that the class of the object has been loaded
|
||||
* by an extension.
|
||||
*
|
||||
* While not perfect, this should detect any callback created via extension code.
|
||||
* It is possible this current version of the implementation might struggle with callbacks created through external
|
||||
* libraries, but as libraries are loaded separately for each extension, this *should not*(tm) be a problem.
|
||||
*
|
||||
* @param obj the object to get the extension of
|
||||
* @return <code>null</code> if no extension has been found, otherwise the extension name
|
||||
*/
|
||||
@Nullable
|
||||
public static String findExtensionObjectOwner(@NotNull Object obj) {
|
||||
ClassLoader cl = obj.getClass().getClassLoader();
|
||||
if (cl instanceof MinestomExtensionClassLoader) {
|
||||
return ((MinestomExtensionClassLoader) cl).getExtensionName();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
@ -1,11 +0,0 @@
|
||||
package demo;
|
||||
|
||||
import net.minestom.server.Bootstrap;
|
||||
|
||||
public class Start {
|
||||
|
||||
public static void main(String[] args) {
|
||||
Bootstrap.bootstrap("demo.Main", new String[0]);
|
||||
}
|
||||
|
||||
}
|
@ -1,34 +0,0 @@
|
||||
package improveextensions;
|
||||
|
||||
import net.minestom.server.MinecraftServer;
|
||||
import net.minestom.server.extensions.Extension;
|
||||
import net.minestom.server.instance.InstanceContainer;
|
||||
import net.minestom.server.world.DimensionType;
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.opentest4j.AssertionFailedError;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* Extensions should be able to use Mixins for classes loaded very early by Minestom (InstanceContainer for instance)
|
||||
*/
|
||||
public class DisableEarlyLoad extends Extension {
|
||||
|
||||
@Override
|
||||
public void initialize() {
|
||||
// force load of InstanceContainer class
|
||||
InstanceContainer c = new InstanceContainer(UUID.randomUUID(), DimensionType.OVERWORLD);
|
||||
System.out.println(c.toString());
|
||||
try {
|
||||
Assertions.assertFalse(MixinIntoMinestomCore.success, "InstanceContainer must NOT have been mixed in with improveextensions.InstanceContainerMixin, because early loading has been disabled");
|
||||
} catch (AssertionFailedError e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
MinecraftServer.stopCleanly();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void terminate() {
|
||||
getLogger().info("Terminate extension");
|
||||
}
|
||||
}
|
@ -1,37 +0,0 @@
|
||||
package improveextensions;
|
||||
|
||||
import net.minestom.server.MinecraftServer;
|
||||
import net.minestom.server.extensions.Extension;
|
||||
import net.minestom.server.instance.InstanceContainer;
|
||||
import net.minestom.server.world.DimensionType;
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.opentest4j.AssertionFailedError;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* Extensions should be able to use Mixins for classes loaded very early by Minestom (InstanceContainer for instance)
|
||||
*/
|
||||
public class MixinIntoMinestomCore extends Extension {
|
||||
|
||||
public static boolean success = false;
|
||||
|
||||
@Override
|
||||
public void initialize() {
|
||||
// force load of InstanceContainer class
|
||||
InstanceContainer c = new InstanceContainer(UUID.randomUUID(), DimensionType.OVERWORLD);
|
||||
System.out.println(c.toString());
|
||||
// try {
|
||||
// Assertions.assertTrue(success, "InstanceContainer must have been mixed in with improveextensions.InstanceContainerMixin");
|
||||
// Assertions.assertEquals(1, MinecraftServer.getExtensionManager().getExtensions().stream().map(extension -> extension.getOrigin().getMinestomExtensionClassLoader()).toArray().length, "Only one extension classloader (this extension's) must be active.");
|
||||
// } catch (AssertionFailedError e) {
|
||||
// e.printStackTrace();
|
||||
// }
|
||||
MinecraftServer.stopCleanly();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void terminate() {
|
||||
getLogger().info("Terminate extension");
|
||||
}
|
||||
}
|
@ -1,21 +0,0 @@
|
||||
package improveextensions;
|
||||
|
||||
import net.minestom.server.Bootstrap;
|
||||
|
||||
// To launch with VM arguments:
|
||||
|
||||
// To test early Mixin injections:
|
||||
// -Dminestom.extension.indevfolder.classes=build/classes/java/test/ -Dminestom.extension.indevfolder.resources=build/resources/test/improveextensions
|
||||
// To test disabling early Mixin injections:
|
||||
// -Dminestom.extension.disable_early_load=true -Dminestom.extension.indevfolder.classes=build/classes/java/test/ -Dminestom.extension.indevfolder.resources=build/resources/test/improveextensions/disableearlyload
|
||||
|
||||
// To test extension termination when the server quits:
|
||||
// -Dminestom.extension.indevfolder.classes=build/classes/java/test/ -Dminestom.extension.indevfolder.resources=build/resources/test/improveextensions/unloadonstop
|
||||
|
||||
// To test report of failure when a mixin configuration cannot be loaded, or code modifiers are missing
|
||||
// -Dminestom.extension.indevfolder.classes=build/classes/java/test/ -Dminestom.extension.indevfolder.resources=build/resources/test/improveextensions/missingmodifiers
|
||||
public class MixinIntoMinestomCoreLauncher {
|
||||
public static void main(String[] args) {
|
||||
Bootstrap.bootstrap("demo.MainDemo", args);
|
||||
}
|
||||
}
|
@ -1,43 +0,0 @@
|
||||
package improveextensions;
|
||||
|
||||
import net.minestom.server.MinecraftServer;
|
||||
import net.minestom.server.extensions.Extension;
|
||||
import net.minestom.server.instance.InstanceContainer;
|
||||
import net.minestom.server.world.DimensionType;
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.opentest4j.AssertionFailedError;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
/**
|
||||
* Extensions should be able to use Mixins for classes loaded very early by Minestom (InstanceContainer for instance)
|
||||
*/
|
||||
public class MixinIntoMinestomCoreWithJava9ModuleOnClasspath extends Extension {
|
||||
|
||||
@Override
|
||||
public void initialize() {
|
||||
// use Mockito only to ensure J9 modules on the classpath are supported
|
||||
List mockedList = mock(List.class);
|
||||
when(mockedList.get(0)).thenReturn("Test");
|
||||
// force load of InstanceContainer class
|
||||
InstanceContainer c = new InstanceContainer(UUID.randomUUID(), DimensionType.OVERWORLD);
|
||||
System.out.println(c.toString());
|
||||
try {
|
||||
Assertions.assertTrue(MixinIntoMinestomCore.success, "InstanceContainer must have been mixed in with improveextensions.InstanceContainerMixin");
|
||||
Assertions.assertEquals(1, MinecraftServer.getExtensionManager().getExtensions().stream().map(extension -> extension.getOrigin().getMinestomExtensionClassLoader()).toArray().length, "Only one extension classloader (this extension's) must be active.");
|
||||
Assertions.assertEquals("Test", mockedList.get(0));
|
||||
} catch (AssertionFailedError e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
MinecraftServer.stopCleanly();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void terminate() {
|
||||
getLogger().info("Terminate extension");
|
||||
}
|
||||
}
|
@ -1,31 +0,0 @@
|
||||
package improveextensions.missingmodifiers;
|
||||
|
||||
import net.minestom.server.MinecraftServer;
|
||||
import net.minestom.server.extensions.Extension;
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.opentest4j.AssertionFailedError;
|
||||
|
||||
public class MissingCodeModifiersExtension extends Extension {
|
||||
|
||||
@Override
|
||||
public void initialize() {
|
||||
// force load of InstanceContainer class
|
||||
try {
|
||||
// Assertions.assertFalse(areCodeModifiersAllLoadedCorrectly(), "Mixin configuration could not be loaded and code modifiers are unavailable, the failure should be reported");
|
||||
// Assertions.assertTrue(getOrigin().hasFailedToLoadMixin(), "Mixin configuration does not exist and should not be loaded");
|
||||
// Assertions.assertEquals(1, getOrigin().getMissingCodeModifiers().size(), "Code modifier does not exist, it should be reported as missing");
|
||||
// Assertions.assertEquals("InvalidCodeModifierClass", getOrigin().getMissingCodeModifiers().get(0));
|
||||
System.out.println("All tests passed.");
|
||||
} catch (AssertionFailedError e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
MinecraftServer.stopCleanly();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void terminate() {
|
||||
getLogger().info("Terminate extension");
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -1,12 +0,0 @@
|
||||
package improveextensions.mixins;
|
||||
|
||||
//@Mixin(InstanceContainer.class)
|
||||
public class InstanceContainerMixin {
|
||||
|
||||
// @Inject(method = "<init>", at = @At("RETURN"), require = 1)
|
||||
// private void constructorHead(CallbackInfo ci) {
|
||||
// System.out.println("Mixin into InstanceContainerMixin");
|
||||
// MixinIntoMinestomCore.success = true;
|
||||
// }
|
||||
|
||||
}
|
@ -8,7 +8,7 @@ import net.minestom.server.event.GlobalEventHandler;
|
||||
import net.minestom.server.event.entity.EntityTickEvent;
|
||||
import net.minestom.server.event.instance.InstanceTickEvent;
|
||||
import net.minestom.server.extensions.Extension;
|
||||
import net.minestom.server.extras.selfmodification.MinestomRootClassLoader;
|
||||
import net.minestom.server.extensions.isolation.MinestomExtensionClassLoader;
|
||||
import net.minestom.server.instance.Instance;
|
||||
import net.minestom.server.utils.time.TimeUnit;
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
@ -65,8 +65,8 @@ public class UnloadCallbacksExtension extends Extension {
|
||||
}).repeat(100L, TimeUnit.MILLISECOND).schedule();
|
||||
|
||||
try {
|
||||
Assertions.assertNotNull(MinestomRootClassLoader.findExtensionObjectOwner(callback));
|
||||
Assertions.assertEquals("UnloadCallbacksExtension", MinestomRootClassLoader.findExtensionObjectOwner(callback));
|
||||
Assertions.assertNotNull(MinestomExtensionClassLoader.findExtensionObjectOwner(callback));
|
||||
Assertions.assertEquals("UnloadCallbacksExtension", MinestomExtensionClassLoader.findExtensionObjectOwner(callback));
|
||||
} catch (AssertionFailedError e) {
|
||||
e.printStackTrace();
|
||||
System.exit(-1);
|
||||
|
@ -1,11 +0,0 @@
|
||||
package testextension;
|
||||
|
||||
import net.minestom.server.Bootstrap;
|
||||
|
||||
public class TestDemoLauncher {
|
||||
|
||||
public static void main(String[] args) {
|
||||
Bootstrap.bootstrap("demo.Main", args);
|
||||
}
|
||||
|
||||
}
|
@ -1,17 +0,0 @@
|
||||
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("demo.MainDemo", argsWithMixins);
|
||||
}
|
||||
|
||||
}
|
@ -1,13 +0,0 @@
|
||||
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 TestExtensionLauncherNoSetup {
|
||||
|
||||
public static void main(String[] args) {
|
||||
Bootstrap.bootstrap("demo.MainDemo", args);
|
||||
}
|
||||
|
||||
}
|
@ -1,37 +0,0 @@
|
||||
package testextension;
|
||||
|
||||
//import net.minestom.server.extras.selfmodification.CodeModifier;
|
||||
//import org.objectweb.asm.Opcodes;
|
||||
//import org.objectweb.asm.tree.*;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
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 of "+source.name);
|
||||
// MethodNode constructor = findConstructor(source.methods);
|
||||
// constructor.instructions.insert(constructor.instructions.getFirst(), buildInjectionCode());
|
||||
// return true;
|
||||
// }
|
||||
// return false;
|
||||
// }
|
||||
//
|
||||
// private InsnList buildInjectionCode() {
|
||||
// InsnList list = new InsnList();
|
||||
// list.add(new FieldInsnNode(GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;"));
|
||||
// list.add(new LdcInsnNode("Hello from modified code!!"));
|
||||
// list.add(new MethodInsnNode(INVOKEVIRTUAL, "java/io/PrintStream", "println", "(Ljava/lang/String;)V", false));
|
||||
// return list;
|
||||
// }
|
||||
//
|
||||
// private MethodNode findConstructor(List<MethodNode> methods) {
|
||||
// return methods.stream().filter(m -> m.name.equals("<init>")).findFirst().orElseThrow();
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public String getNamespace() {
|
||||
// return "net.minestom.server";
|
||||
// }
|
||||
}
|
@ -1,18 +0,0 @@
|
||||
package testextension.mixins;
|
||||
|
||||
import net.minestom.server.instance.DynamicChunk;
|
||||
import net.minestom.server.instance.block.Block;
|
||||
//import org.spongepowered.asm.mixin.Mixin;
|
||||
//import org.spongepowered.asm.mixin.injection.At;
|
||||
//import org.spongepowered.asm.mixin.injection.ModifyVariable;
|
||||
|
||||
//@Mixin(DynamicChunk.class)
|
||||
public class DynamicChunkMixin {
|
||||
|
||||
// @ModifyVariable(method = "setBlock", at = @At("HEAD"), index = 4, require = 1, argsOnly = true, remap = false)
|
||||
// public int oopsAllTnt(short blockStateId) {
|
||||
// if(blockStateId != 0)
|
||||
// return Block.TNT.id();
|
||||
// return 0;
|
||||
// }
|
||||
}
|
@ -1,17 +0,0 @@
|
||||
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!!!");
|
||||
// }
|
||||
|
||||
}
|
@ -1,8 +1,4 @@
|
||||
{
|
||||
"entrypoint": "testextension.TestExtension",
|
||||
"name": "Test_extension",
|
||||
"codeModifiers": [
|
||||
"testextension.TestModifier"
|
||||
],
|
||||
"mixinConfig": "mixins.testextension.json"
|
||||
"name": "Test_extension"
|
||||
}
|
@ -1,6 +0,0 @@
|
||||
{
|
||||
"entrypoint": "improveextensions.DisableEarlyLoad",
|
||||
"name": "DisableEarlyLoad",
|
||||
"codeModifiers": [],
|
||||
"mixinConfig": "mixins.minestomcore.json"
|
||||
}
|
@ -1,10 +0,0 @@
|
||||
{
|
||||
"required": true,
|
||||
"minVersion": "0.8",
|
||||
"package": "improveextensions.mixins",
|
||||
"target": "@env(DEFAULT)",
|
||||
"compatibilityLevel": "JAVA_11",
|
||||
"mixins": [
|
||||
"InstanceContainerMixin"
|
||||
]
|
||||
}
|
@ -1,6 +0,0 @@
|
||||
{
|
||||
"entrypoint": "improveextensions.MixinIntoMinestomCore",
|
||||
"name": "MixinIntoMinestomCore",
|
||||
"codeModifiers": [],
|
||||
"mixinConfig": "mixins.minestomcore.json"
|
||||
}
|
@ -1,14 +0,0 @@
|
||||
{
|
||||
"entrypoint": "improveextensions.MixinIntoMinestomCoreWithJava9ModuleOnClasspath",
|
||||
"name": "MixinIntoMinestomCoreWithJava9ModuleOnClasspath",
|
||||
"codeModifiers": [],
|
||||
"mixinConfig": "mixins.minestomcore.json",
|
||||
"externalDependencies": {
|
||||
"repositories": [
|
||||
{"name": "JCentral", "url": "https://jcenter.bintray.com/"}
|
||||
],
|
||||
"artifacts": [
|
||||
"org.mockito:mockito-core:2.28.2"
|
||||
]
|
||||
}
|
||||
}
|
@ -1,10 +0,0 @@
|
||||
{
|
||||
"required": true,
|
||||
"minVersion": "0.8",
|
||||
"package": "improveextensions.mixins",
|
||||
"target": "@env(DEFAULT)",
|
||||
"compatibilityLevel": "JAVA_11",
|
||||
"mixins": [
|
||||
"InstanceContainerMixin"
|
||||
]
|
||||
}
|
@ -1,8 +0,0 @@
|
||||
{
|
||||
"entrypoint": "improveextensions.missingmodifiers.MissingCodeModifiersExtension",
|
||||
"name": "MissingCodeModifiersExtension",
|
||||
"codeModifiers": [
|
||||
"InvalidCodeModifierClass"
|
||||
],
|
||||
"mixinConfig": "mixins.$invalid$.json"
|
||||
}
|
@ -1,10 +0,0 @@
|
||||
{
|
||||
"required": true,
|
||||
"minVersion": "0.8",
|
||||
"package": "improveextensions.mixins",
|
||||
"target": "@env(DEFAULT)",
|
||||
"compatibilityLevel": "JAVA_11",
|
||||
"mixins": [
|
||||
"InstanceContainerMixin"
|
||||
]
|
||||
}
|
@ -1,11 +0,0 @@
|
||||
{
|
||||
"required": true,
|
||||
"minVersion": "0.8",
|
||||
"package": "testextension.mixins",
|
||||
"target": "@env(DEFAULT)",
|
||||
"compatibilityLevel": "JAVA_11",
|
||||
"mixins": [
|
||||
"InstanceContainerMixin",
|
||||
"DynamicChunkMixin"
|
||||
]
|
||||
}
|
Loading…
Reference in New Issue
Block a user