+ * The most important part of this code is `OneConfigSourceFile` in the manifest. If a mod creates a custom tweaker
+ * but still wants the Mixin tweaker to be injected, they can add this attribute to their jar manifest to signal
+ * to OneConfig that it should inject the Mixin tweaker regardless of the tweaker class.
+ *
*/
@SuppressWarnings("unused")
public class OneConfigTweaker implements ITweaker {
private static final Logger LOGGER = LogManager.getLogger("OneConfig/Tweaker");
private static final String MIXIN_TWEAKER = "org.spongepowered.asm.launch.MixinTweaker";
- private static MethodHandle loadCoreMod;
- private static Consumer addContainer;
public OneConfigTweaker() {
final List sourceFiles = getSourceFiles();
@@ -86,7 +88,7 @@ public OneConfigTweaker() {
}
@SuppressWarnings("unchecked")
- private static void setupSourceFile(SourceFile sourceFile) throws Throwable {
+ private void setupSourceFile(SourceFile sourceFile) throws Exception {
String path = sourceFile.path.toString();
// Forge will by default ignore a mod file if it contains a tweaker
// So we need to remove ourselves from that exclusion list
@@ -101,50 +103,75 @@ private static void setupSourceFile(SourceFile sourceFile) throws Throwable {
// Mixin takes care of this as well, so we mustn't if it will.
String coreMod = sourceFile.coreMod;
if (coreMod != null && !sourceFile.mixin) {
- ITweaker tweaker = loadCoreMod(Launch.classLoader, coreMod, sourceFile.path.toFile());
+ Method loadCoreMod = CoreModManager.class.getDeclaredMethod("loadCoreMod", LaunchClassLoader.class, String.class, File.class);
+ loadCoreMod.setAccessible(true);
+ ITweaker tweaker = (ITweaker) loadCoreMod.invoke(null, Launch.classLoader, coreMod, sourceFile.path.toFile());
((List) Launch.blackboard.get("Tweaks")).add(tweaker);
}
- // Mixin will only look at jar files which declare the MixinTweaker as their tweaker class, so we need
- // to manually add our source files for inspection.
- if (sourceFile.mixin) addContainer(sourceFile.path.toUri());
- }
+ // If they declared our tweaker but also want to use mixin, then we'll inject the mixin tweaker
+ // for them.
+ if (sourceFile.mixin) {
+ // Mixin will only look at jar files which declare the MixinTweaker as their tweaker class, so we need
+ // to manually add our source files for inspection.
+ try {
+ injectMixinTweaker();
- private static ITweaker loadCoreMod(LaunchClassLoader loader, String cls, File src) throws Throwable {
- MethodHandle mh = loadCoreMod == null ? loadCoreMod = createLoadCoreMod() : loadCoreMod;
- return (ITweaker) mh.invoke(loader, src, src);
+ Class> MixinBootstrap = Class.forName("org.spongepowered.asm.launch.MixinBootstrap");
+ Class> MixinPlatformManager = Class.forName("org.spongepowered.asm.launch.platform.MixinPlatformManager");
+ Object platformManager = MixinBootstrap.getDeclaredMethod("getPlatform").invoke(null);
+ Method addContainer;
+ Object arg;
+ try {
+ // Mixin 0.7
+ addContainer = MixinPlatformManager.getDeclaredMethod("addContainer", URI.class);
+ arg = sourceFile.path.toUri();
+ } catch (NoSuchMethodException ignored) {
+ // Mixin 0.8
+ Class> IContainerHandle = Class.forName("org.spongepowered.asm.launch.platform.container.IContainerHandle");
+ Class> ContainerHandleURI = Class.forName("org.spongepowered.asm.launch.platform.container.ContainerHandleURI");
+ addContainer = MixinPlatformManager.getDeclaredMethod("addContainer", IContainerHandle);
+ arg = ContainerHandleURI.getDeclaredConstructor(URI.class).newInstance(sourceFile.path.toUri());
+ }
+ addContainer.invoke(platformManager, arg);
+ } catch (Exception e) {
+ LOGGER.error("failed to inject mixin tweaker for {}", path, e);
+ }
+ }
}
- private static MethodHandle createLoadCoreMod() {
- return MHUtils.getStaticMethodHandle(CoreModManager.class, "loadCoreMod", ITweaker.class, LaunchClassLoader.class, String.class, File.class).getOrThrow();
- }
+ private void injectMixinTweaker() throws ClassNotFoundException, IllegalAccessException, InstantiationException {
+ @SuppressWarnings("unchecked")
+ List tweakClasses = (List) Launch.blackboard.get("TweakClasses");
- private static void addContainer(URI uri) {
- Consumer c = addContainer == null ? addContainer = createAddContainer() : addContainer;
- c.accept(uri);
- }
+ // If the MixinTweaker is already queued (because of another mod), then there's nothing we need to to
+ if (tweakClasses.contains(MIXIN_TWEAKER)) {
+ // Except we do need to initialize the MixinTweaker immediately so we can add containers
+ // for our mods.
+ // This is idempotent, so we can call it without adding to the tweaks list (and we must not add to
+ // it because the queued tweaker will already get added and there is nothing we can do about that).
+ initMixinTweaker();
+ return;
+ }
- @SuppressWarnings("unchecked")
- private static Consumer createAddContainer() {
- try {
- Class> MixinBootstrap = Class.forName("org.spongepowered.asm.launch.MixinBootstrap");
- Class> MixinPlatformManager = Class.forName("org.spongepowered.asm.launch.platform.MixinPlatformManager");
- Object platformManager = MHUtils.invokeStatic(MixinBootstrap, "getPlatform");
- try {
- // Mixin 0.7
- return MHUtils.getConsumerHandle(platformManager, "addContainer", URI.class).getOrThrow();
- } catch (Throwable ignored) {
- // Mixin 0.8
- Class> IContainerHandle = Class.forName("org.spongepowered.asm.launch.platform.container.IContainerHandle");
- Class> ContainerHandleURI = Class.forName("org.spongepowered.asm.launch.platform.container.ContainerHandleURI");
- // as we can't refer to IContainerHandle, we have to use unchecked casts here.
- Consumer