mirror of
https://github.com/Minestom/Minestom.git
synced 2025-02-03 22:12:20 +01:00
Early loading of Mixin and code modifiers
+ System property to disable early loading if necessary
This commit is contained in:
parent
dd1b67e5de
commit
9b9565dbbd
@ -1,5 +1,6 @@
|
|||||||
package net.minestom.server;
|
package net.minestom.server;
|
||||||
|
|
||||||
|
import net.minestom.server.extensions.ExtensionManager;
|
||||||
import net.minestom.server.extras.selfmodification.MinestomRootClassLoader;
|
import net.minestom.server.extras.selfmodification.MinestomRootClassLoader;
|
||||||
import net.minestom.server.extras.selfmodification.mixins.MixinCodeModifier;
|
import net.minestom.server.extras.selfmodification.mixins.MixinCodeModifier;
|
||||||
import net.minestom.server.extras.selfmodification.mixins.MixinServiceMinestom;
|
import net.minestom.server.extras.selfmodification.mixins.MixinServiceMinestom;
|
||||||
@ -22,6 +23,8 @@ public final class Bootstrap {
|
|||||||
startMixin(args);
|
startMixin(args);
|
||||||
MinestomRootClassLoader.getInstance().addCodeModifier(new MixinCodeModifier());
|
MinestomRootClassLoader.getInstance().addCodeModifier(new MixinCodeModifier());
|
||||||
|
|
||||||
|
ExtensionManager.loadCodeModifiersEarly();
|
||||||
|
|
||||||
MixinServiceMinestom.gotoPreinitPhase();
|
MixinServiceMinestom.gotoPreinitPhase();
|
||||||
// ensure extensions are loaded when starting the server
|
// ensure extensions are loaded when starting the server
|
||||||
Class<?> serverClass = classLoader.loadClass("net.minestom.server.MinecraftServer");
|
Class<?> serverClass = classLoader.loadClass("net.minestom.server.MinecraftServer");
|
||||||
|
@ -27,6 +27,8 @@ import java.util.zip.ZipFile;
|
|||||||
|
|
||||||
public class ExtensionManager {
|
public class ExtensionManager {
|
||||||
|
|
||||||
|
public final static String DISABLE_EARLY_LOAD_SYSTEM_KEY = "minestom.extension.disable_early_load";
|
||||||
|
|
||||||
public final static Logger LOGGER = LoggerFactory.getLogger(ExtensionManager.class);
|
public final static Logger LOGGER = LoggerFactory.getLogger(ExtensionManager.class);
|
||||||
|
|
||||||
private final static String INDEV_CLASSES_FOLDER = "minestom.extension.indevfolder.classes";
|
private final static String INDEV_CLASSES_FOLDER = "minestom.extension.indevfolder.classes";
|
||||||
@ -469,7 +471,7 @@ public class ExtensionManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
public Map<String, URLClassLoader> getExtensionLoaders() {
|
public Map<String, MinestomExtensionClassLoader> getExtensionLoaders() {
|
||||||
return new HashMap<>(extensionLoaders);
|
return new HashMap<>(extensionLoaders);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -485,6 +487,10 @@ public class ExtensionManager {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
MinestomRootClassLoader modifiableClassLoader = (MinestomRootClassLoader) cl;
|
MinestomRootClassLoader modifiableClassLoader = (MinestomRootClassLoader) cl;
|
||||||
|
setupCodeModifiers(extensions, modifiableClassLoader);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setupCodeModifiers(@NotNull List<DiscoveredExtension> extensions, MinestomRootClassLoader modifiableClassLoader) {
|
||||||
LOGGER.info("Start loading code modifiers...");
|
LOGGER.info("Start loading code modifiers...");
|
||||||
for (DiscoveredExtension extension : extensions) {
|
for (DiscoveredExtension extension : extensions) {
|
||||||
try {
|
try {
|
||||||
@ -664,4 +670,33 @@ public class ExtensionManager {
|
|||||||
public void shutdown() {
|
public void shutdown() {
|
||||||
this.extensionList.forEach(this::unload);
|
this.extensionList.forEach(this::unload);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Loads code modifiers early, that is before <code>MinecraftServer.init()</code> is called.
|
||||||
|
*/
|
||||||
|
public static void loadCodeModifiersEarly() {
|
||||||
|
// allow users to disable early code modifier load
|
||||||
|
if("true".equalsIgnoreCase(System.getProperty(DISABLE_EARLY_LOAD_SYSTEM_KEY))) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
LOGGER.info("Early load of code modifiers from extensions.");
|
||||||
|
ExtensionManager manager = new ExtensionManager();
|
||||||
|
|
||||||
|
// discover extensions that are present
|
||||||
|
List<DiscoveredExtension> discovered = manager.discoverExtensions();
|
||||||
|
|
||||||
|
// setup extension class loaders, so that Mixin can load the json configuration file correctly
|
||||||
|
for(DiscoveredExtension e : discovered) {
|
||||||
|
manager.setupClassLoader(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
// setup code modifiers and mixins
|
||||||
|
manager.setupCodeModifiers(discovered, MinestomRootClassLoader.getInstance());
|
||||||
|
|
||||||
|
// setup is done, remove all extension classloaders
|
||||||
|
for(MinestomExtensionClassLoader extensionLoader : manager.getExtensionLoaders().values()) {
|
||||||
|
MinestomRootClassLoader.getInstance().removeChildInHierarchy(extensionLoader);
|
||||||
|
}
|
||||||
|
LOGGER.info("Early load of code modifiers from extensions done!");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -65,6 +65,11 @@ public class MinestomRootClassLoader extends HierarchyClassLoader {
|
|||||||
// TODO: priorities?
|
// TODO: priorities?
|
||||||
private final List<CodeModifier> modifiers = new LinkedList<>();
|
private final List<CodeModifier> modifiers = new LinkedList<>();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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) {
|
private MinestomRootClassLoader(ClassLoader parent) {
|
||||||
super("Minestom Root ClassLoader", extractURLsFromClasspath(), parent);
|
super("Minestom Root ClassLoader", extractURLsFromClasspath(), parent);
|
||||||
asmClassLoader = newChild(new URL[0]);
|
asmClassLoader = newChild(new URL[0]);
|
||||||
@ -246,6 +251,9 @@ public class MinestomRootClassLoader extends HierarchyClassLoader {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void loadModifier(File[] originFiles, String codeModifierClass) {
|
public void loadModifier(File[] originFiles, String codeModifierClass) {
|
||||||
|
if(alreadyLoadedCodeModifiers.contains(codeModifierClass)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
URL[] urls = new URL[originFiles.length];
|
URL[] urls = new URL[originFiles.length];
|
||||||
try {
|
try {
|
||||||
for (int i = 0; i < originFiles.length; i++) {
|
for (int i = 0; i < originFiles.length; i++) {
|
||||||
@ -258,6 +266,7 @@ public class MinestomRootClassLoader extends HierarchyClassLoader {
|
|||||||
synchronized (modifiers) {
|
synchronized (modifiers) {
|
||||||
LOGGER.warn("Added Code modifier: {}", modifier);
|
LOGGER.warn("Added Code modifier: {}", modifier);
|
||||||
addCodeModifier(modifier);
|
addCodeModifier(modifier);
|
||||||
|
alreadyLoadedCodeModifiers.add(codeModifierClass);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (MalformedURLException | ClassNotFoundException | InvocationTargetException | InstantiationException | IllegalAccessException | NoSuchMethodException e) {
|
} catch (MalformedURLException | ClassNotFoundException | InvocationTargetException | InstantiationException | IllegalAccessException | NoSuchMethodException e) {
|
||||||
|
34
src/test/java/improveextensions/DisableEarlyLoad.java
Normal file
34
src/test/java/improveextensions/DisableEarlyLoad.java
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
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, null);
|
||||||
|
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");
|
||||||
|
}
|
||||||
|
}
|
37
src/test/java/improveextensions/MixinIntoMinestomCore.java
Normal file
37
src/test/java/improveextensions/MixinIntoMinestomCore.java
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
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, null);
|
||||||
|
System.out.println(c.toString());
|
||||||
|
try {
|
||||||
|
Assertions.assertTrue(success, "InstanceContainer must have been mixed in with improveextensions.InstanceContainerMixin");
|
||||||
|
Assertions.assertEquals(1, MinecraftServer.getExtensionManager().getExtensionLoaders().size(), "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");
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,15 @@
|
|||||||
|
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
|
||||||
|
public class MixinIntoMinestomCoreLauncher {
|
||||||
|
public static void main(String[] args) {
|
||||||
|
Bootstrap.bootstrap("demo.MainDemo", args);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,19 @@
|
|||||||
|
package improveextensions.mixins;
|
||||||
|
|
||||||
|
import improveextensions.MixinIntoMinestomCore;
|
||||||
|
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"), require = 1)
|
||||||
|
private void constructorHead(CallbackInfo ci) {
|
||||||
|
System.out.println("Mixin into InstanceContainerMixin");
|
||||||
|
MixinIntoMinestomCore.success = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,6 @@
|
|||||||
|
{
|
||||||
|
"entrypoint": "improveextensions.DisableEarlyLoad",
|
||||||
|
"name": "DisableEarlyLoad",
|
||||||
|
"codeModifiers": [],
|
||||||
|
"mixinConfig": "mixins.minestomcore.json"
|
||||||
|
}
|
@ -0,0 +1,10 @@
|
|||||||
|
{
|
||||||
|
"required": true,
|
||||||
|
"minVersion": "0.8",
|
||||||
|
"package": "improveextensions.mixins",
|
||||||
|
"target": "@env(DEFAULT)",
|
||||||
|
"compatibilityLevel": "JAVA_11",
|
||||||
|
"mixins": [
|
||||||
|
"InstanceContainerMixin"
|
||||||
|
]
|
||||||
|
}
|
6
src/test/resources/improveextensions/extension.json
Normal file
6
src/test/resources/improveextensions/extension.json
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
{
|
||||||
|
"entrypoint": "improveextensions.MixinIntoMinestomCore",
|
||||||
|
"name": "MixinIntoMinestomCore",
|
||||||
|
"codeModifiers": [],
|
||||||
|
"mixinConfig": "mixins.minestomcore.json"
|
||||||
|
}
|
@ -0,0 +1,10 @@
|
|||||||
|
{
|
||||||
|
"required": true,
|
||||||
|
"minVersion": "0.8",
|
||||||
|
"package": "improveextensions.mixins",
|
||||||
|
"target": "@env(DEFAULT)",
|
||||||
|
"compatibilityLevel": "JAVA_11",
|
||||||
|
"mixins": [
|
||||||
|
"InstanceContainerMixin"
|
||||||
|
]
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user