diff --git a/LICENSE-header.txt b/LICENSE-header.txt index 299818fdb..a88b79cc1 100644 --- a/LICENSE-header.txt +++ b/LICENSE-header.txt @@ -1,2 +1,2 @@ -Copyright (c) Forge Development LLC and contributors -SPDX-License-Identifier: LGPL-2.1-only \ No newline at end of file +Copyright (c) NeoForged and contributors +SPDX-License-Identifier: LGPL-2.1-only diff --git a/build.gradle b/build.gradle index 78077dc02..faa4ce8a2 100644 --- a/build.gradle +++ b/build.gradle @@ -1,7 +1,7 @@ plugins { id 'com.github.ben-manes.versions' version '0.39.0' id 'org.javamodularity.moduleplugin' version '1.8.7' - id 'org.cadixdev.licenser' version '0.6.0' + id 'net.neoforged.licenser' version '0.7.2' apply false id 'net.neoforged.gradleutils' version '[2.0.18,3)' } @@ -17,7 +17,7 @@ allprojects { apply plugin: 'com.github.ben-manes.versions' apply plugin: 'org.javamodularity.moduleplugin' - apply plugin: 'org.cadixdev.licenser' + apply plugin: 'net.neoforged.licenser' apply plugin: 'net.neoforged.gradleutils' group = 'net.neoforged.fancymodloader' @@ -84,6 +84,7 @@ subprojects { subProject -> license { header = rootProject.file('LICENSE-header.txt') + skipExistingHeaders true include 'net/neoforged/' } @@ -117,4 +118,4 @@ subprojects { subProject -> changelog { fromTag "1.0" } -} \ No newline at end of file +} diff --git a/gradle.properties b/gradle.properties index 9495eeaa1..b2792257a 100644 --- a/gradle.properties +++ b/gradle.properties @@ -7,6 +7,7 @@ modlauncher_version=10.0.9 securejarhandler_version=2.1.10 bootstraplauncher_version=1.1.2 asm_version=9.5 +mixin_version=0.8.5 terminalconsoleappender_version=1.2.0 nightconfig_version=3.6.4 jetbrains_annotations_version=24.0.1 @@ -23,4 +24,4 @@ guava_version=31.1-jre gson_version=2.10 apache_commons_lang3_version=3.12.0 jopt_simple_version=5.0.4 -commons_io_version=2.11.0 \ No newline at end of file +commons_io_version=2.11.0 diff --git a/loader/build.gradle b/loader/build.gradle index 75be47d84..6344216bb 100644 --- a/loader/build.gradle +++ b/loader/build.gradle @@ -23,6 +23,7 @@ dependencies { implementation("net.sf.jopt-simple:jopt-simple:${jopt_simple_version}") implementation("cpw.mods:securejarhandler:${securejarhandler_version}") implementation("net.neoforged:accesstransformers:${accesstransformers_version}") + implementation("org.spongepowered:mixin:${mixin_version}") implementation("net.minecrell:terminalconsoleappender:${terminalconsoleappender_version}") implementation("org.apache.logging.log4j:log4j-core:${log4j_version}") diff --git a/loader/src/main/java/net/neoforged/fml/loading/FMLLoader.java b/loader/src/main/java/net/neoforged/fml/loading/FMLLoader.java index 557eab954..c373ed88b 100644 --- a/loader/src/main/java/net/neoforged/fml/loading/FMLLoader.java +++ b/loader/src/main/java/net/neoforged/fml/loading/FMLLoader.java @@ -8,8 +8,8 @@ import com.mojang.logging.LogUtils; import cpw.mods.modlauncher.Launcher; import cpw.mods.modlauncher.api.*; -import cpw.mods.modlauncher.serviceapi.ILaunchPluginService; import cpw.mods.modlauncher.util.ServiceLoaderUtils; +import net.neoforged.fml.loading.mixin.DeferredMixinConfigRegistration; import net.neoforged.fml.loading.moddiscovery.BackgroundScanHandler; import net.neoforged.fml.loading.moddiscovery.ModDiscoverer; import net.neoforged.fml.loading.moddiscovery.ModFile; @@ -157,11 +157,15 @@ public static List beginModScan(final Map completeScan(IModuleLayerManager layerManager) { + public static List completeScan(IModuleLayerManager layerManager, List extraMixinConfigs) { moduleLayerManager = layerManager; languageLoadingProvider = new LanguageLoadingProvider(); backgroundScanHandler = modValidator.stage2Validation(); loadingModList = backgroundScanHandler.getLoadingModList(); + if (loadingModList.getErrors().isEmpty()) { + // Add extra mixin configs + extraMixinConfigs.forEach(DeferredMixinConfigRegistration::addMixinConfig); + } return List.of(modValidator.getModResources()); } diff --git a/loader/src/main/java/net/neoforged/fml/loading/FMLServiceProvider.java b/loader/src/main/java/net/neoforged/fml/loading/FMLServiceProvider.java index fb83ae6d7..475923762 100644 --- a/loader/src/main/java/net/neoforged/fml/loading/FMLServiceProvider.java +++ b/loader/src/main/java/net/neoforged/fml/loading/FMLServiceProvider.java @@ -30,6 +30,7 @@ public class FMLServiceProvider implements ITransformationService private ArgumentAcceptingOptionSpec modsOption; private ArgumentAcceptingOptionSpec modListsOption; private ArgumentAcceptingOptionSpec mavenRootsOption; + private ArgumentAcceptingOptionSpec mixinConfigsOption; private ArgumentAcceptingOptionSpec fmlOption; private ArgumentAcceptingOptionSpec forgeOption; private ArgumentAcceptingOptionSpec mcOption; @@ -38,6 +39,7 @@ public class FMLServiceProvider implements ITransformationService private List modsArgumentList; private List modListsArgumentList; private List mavenRootsArgumentList; + private List mixinConfigsArgumentList; private String targetForgeVersion; private String targetFMLVersion; private String targetMcVersion; @@ -88,7 +90,7 @@ public List beginScanning(final IEnvironment environment) { @Override public List completeScan(final IModuleLayerManager layerManager) { - return FMLLoader.completeScan(layerManager); + return FMLLoader.completeScan(layerManager, mixinConfigsArgumentList); } @Override @@ -111,6 +113,7 @@ public void arguments(BiFunction argumentBuil modsOption = argumentBuilder.apply("mods", "List of mods to add").withRequiredArg().ofType(String.class).withValuesSeparatedBy(","); modListsOption = argumentBuilder.apply("modLists", "JSON modlists").withRequiredArg().ofType(String.class).withValuesSeparatedBy(","); mavenRootsOption = argumentBuilder.apply("mavenRoots", "Maven root directories").withRequiredArg().ofType(String.class).withValuesSeparatedBy(","); + mixinConfigsOption = argumentBuilder.apply("mixinConfig", "Additional mixin config files to load").withRequiredArg().ofType(String.class); } @Override @@ -119,6 +122,7 @@ public void argumentValues(OptionResult option) modsArgumentList = option.values(modsOption); modListsArgumentList = option.values(modListsOption); mavenRootsArgumentList = option.values(mavenRootsOption); + mixinConfigsArgumentList = option.values(mixinConfigsOption); targetFMLVersion = option.value(fmlOption); targetForgeVersion = option.value(forgeOption); targetMcVersion = option.value(mcOption); diff --git a/loader/src/main/java/net/neoforged/fml/loading/LoadingModList.java b/loader/src/main/java/net/neoforged/fml/loading/LoadingModList.java index 1d73d297c..7d2cfa09f 100644 --- a/loader/src/main/java/net/neoforged/fml/loading/LoadingModList.java +++ b/loader/src/main/java/net/neoforged/fml/loading/LoadingModList.java @@ -6,6 +6,7 @@ package net.neoforged.fml.loading; import cpw.mods.modlauncher.api.LamdbaExceptionUtils; +import net.neoforged.fml.loading.mixin.DeferredMixinConfigRegistration; import net.neoforged.fml.loading.moddiscovery.BackgroundScanHandler; import net.neoforged.fml.loading.moddiscovery.ModFile; import net.neoforged.fml.loading.moddiscovery.ModFileInfo; @@ -76,6 +77,14 @@ public void addCoreMods() .forEach(FMLLoader.getCoreModProvider()::addCoreMod); } + public void addMixinConfigs() { + modFiles.stream() + .map(ModFileInfo::getFile) + .map(ModFile::getMixinConfigs) + .flatMap(List::stream) + .forEach(DeferredMixinConfigRegistration::addMixinConfig); + } + public void addAccessTransformers() { modFiles.stream() diff --git a/loader/src/main/java/net/neoforged/fml/loading/mixin/DeferredMixinConfigRegistration.java b/loader/src/main/java/net/neoforged/fml/loading/mixin/DeferredMixinConfigRegistration.java new file mode 100644 index 000000000..ef821f703 --- /dev/null +++ b/loader/src/main/java/net/neoforged/fml/loading/mixin/DeferredMixinConfigRegistration.java @@ -0,0 +1,40 @@ +/* + * Copyright (c) NeoForged and contributors + * SPDX-License-Identifier: LGPL-2.1-only + */ + +package net.neoforged.fml.loading.mixin; + +import org.spongepowered.asm.launch.GlobalProperties; +import org.spongepowered.asm.launch.MixinBootstrap; +import org.spongepowered.asm.mixin.Mixins; + +import java.util.ArrayList; +import java.util.List; + +public class DeferredMixinConfigRegistration { + private static boolean added = false; + private static final List mixinConfigs = new ArrayList<>(); + + static { + // Register our platform agent first + List agentClassNames = GlobalProperties.get(GlobalProperties.Keys.AGENTS); + agentClassNames.add(FMLMixinPlatformAgent.class.getName()); + // Register the container (will use the platform agent) + MixinBootstrap.getPlatform().addContainer(new FMLMixinContainerHandle()); + } + + public static void addMixinConfig(String config) { + if (added) { + throw new IllegalStateException("Too late to add mixin configs!"); + } + + mixinConfigs.add(config); + } + + static void registerConfigs() { + added = true; + mixinConfigs.forEach(Mixins::addConfiguration); + mixinConfigs.clear(); + } +} diff --git a/loader/src/main/java/net/neoforged/fml/loading/mixin/FMLMixinContainerHandle.java b/loader/src/main/java/net/neoforged/fml/loading/mixin/FMLMixinContainerHandle.java new file mode 100644 index 000000000..b0f0a5ab5 --- /dev/null +++ b/loader/src/main/java/net/neoforged/fml/loading/mixin/FMLMixinContainerHandle.java @@ -0,0 +1,27 @@ +/* + * Copyright (c) NeoForged and contributors + * SPDX-License-Identifier: LGPL-2.1-only + */ + +package net.neoforged.fml.loading.mixin; + +import org.spongepowered.asm.launch.platform.container.IContainerHandle; + +import java.util.Collection; +import java.util.List; + +/** + * Container handle representing all of FML's mixin configs. + * No attribute because we directly load the mixin configs in {@link FMLMixinPlatformAgent}. + */ +public class FMLMixinContainerHandle implements IContainerHandle { + @Override + public String getAttribute(String name) { + return null; + } + + @Override + public Collection getNestedContainers() { + return List.of(); + } +} diff --git a/loader/src/main/java/net/neoforged/fml/loading/mixin/FMLMixinPlatformAgent.java b/loader/src/main/java/net/neoforged/fml/loading/mixin/FMLMixinPlatformAgent.java new file mode 100644 index 000000000..89c9b99c2 --- /dev/null +++ b/loader/src/main/java/net/neoforged/fml/loading/mixin/FMLMixinPlatformAgent.java @@ -0,0 +1,26 @@ +/* + * Copyright (c) NeoForged and contributors + * SPDX-License-Identifier: LGPL-2.1-only + */ + +package net.neoforged.fml.loading.mixin; + +import org.spongepowered.asm.launch.platform.MixinPlatformAgentAbstract; +import org.spongepowered.asm.launch.platform.MixinPlatformManager; +import org.spongepowered.asm.launch.platform.container.IContainerHandle; + +public class FMLMixinPlatformAgent extends MixinPlatformAgentAbstract { + @Override + public AcceptResult accept(MixinPlatformManager manager, IContainerHandle handle) { + if (handle instanceof FMLMixinContainerHandle) { + return AcceptResult.ACCEPTED; + } + return AcceptResult.REJECTED; + } + + @Override + public void prepare() { + // Load all the mixin configs!! + DeferredMixinConfigRegistration.registerConfigs(); + } +} diff --git a/loader/src/main/java/net/neoforged/fml/loading/moddiscovery/BackgroundScanHandler.java b/loader/src/main/java/net/neoforged/fml/loading/moddiscovery/BackgroundScanHandler.java index 9e38e02f6..c1da512a6 100644 --- a/loader/src/main/java/net/neoforged/fml/loading/moddiscovery/BackgroundScanHandler.java +++ b/loader/src/main/java/net/neoforged/fml/loading/moddiscovery/BackgroundScanHandler.java @@ -34,12 +34,10 @@ private enum ScanStatus { private final List pendingFiles; private final List scannedFiles; private final List allFiles; - private final List modFiles; private ScanStatus status; private LoadingModList loadingModList; - public BackgroundScanHandler(final List modFiles) { - this.modFiles = modFiles; + public BackgroundScanHandler() { modContentScanner = Executors.newSingleThreadExecutor(r -> { final Thread thread = Executors.defaultThreadFactory().newThread(r); thread.setDaemon(true); @@ -52,10 +50,6 @@ public BackgroundScanHandler(final List modFiles) { status = ScanStatus.NOT_STARTED; } - public List getModFiles() { - return modFiles; - } - public void submitForScanning(final ModFile file) { if (modContentScanner.isShutdown()) { status = ScanStatus.ERRORED; diff --git a/loader/src/main/java/net/neoforged/fml/loading/moddiscovery/ModFile.java b/loader/src/main/java/net/neoforged/fml/loading/moddiscovery/ModFile.java index 321dc0a72..efb154286 100644 --- a/loader/src/main/java/net/neoforged/fml/loading/moddiscovery/ModFile.java +++ b/loader/src/main/java/net/neoforged/fml/loading/moddiscovery/ModFile.java @@ -58,6 +58,7 @@ public class ModFile implements IModFile { private ModFileScanData fileModFileScanData; private CompletableFuture futureScanResult; private List coreMods; + private List mixinConfigs; private Path accessTransformer; static final Attributes.Name TYPE = new Attributes.Name("FMLModType"); @@ -112,6 +113,8 @@ public boolean identifyMods() { LOGGER.debug(LogMarkers.LOADING,"Loading mod file {} with languages {}", this.getFilePath(), this.modFileInfo.requiredLanguageLoaders()); this.coreMods = ModFileParser.getCoreMods(this); this.coreMods.forEach(mi-> LOGGER.debug(LogMarkers.LOADING,"Found coremod {}", mi.getPath())); + this.mixinConfigs = ModFileParser.getMixinConfigs(this.modFileInfo); + this.mixinConfigs.forEach(mc -> LOGGER.debug(LogMarkers.LOADING,"Found mixin config {}", mc)); this.accessTransformer = findResource("META-INF", "accesstransformer.cfg"); return true; } @@ -120,6 +123,10 @@ public List getCoreMods() { return coreMods; } + public List getMixinConfigs() { + return mixinConfigs; + } + /** * Run in an executor thread to harvest the class and annotation list */ diff --git a/loader/src/main/java/net/neoforged/fml/loading/moddiscovery/ModFileParser.java b/loader/src/main/java/net/neoforged/fml/loading/moddiscovery/ModFileParser.java index c7a83b7dd..713cbc429 100644 --- a/loader/src/main/java/net/neoforged/fml/loading/moddiscovery/ModFileParser.java +++ b/loader/src/main/java/net/neoforged/fml/loading/moddiscovery/ModFileParser.java @@ -66,4 +66,21 @@ protected static List getCoreMods(final ModFile modFile) { .map(e -> new CoreModFile(e.getKey(), modFile.findResource(e.getValue()),modFile)) .toList(); } + + protected static List getMixinConfigs(IModFileInfo modFileInfo) { + try { + var config = modFileInfo.getConfig(); + var mixinsEntries = config.getConfigList("mixins"); + return mixinsEntries + .stream() + .map(entry -> entry + .getConfigElement("config") + .orElseThrow( + () -> new InvalidModFileException("Missing \"config\" in [[mixins]] entry", modFileInfo))) + .toList(); + } catch (Exception exception) { + LOGGER.error("Failed to load mixin configs from mod file", exception); + return List.of(); + } + } } diff --git a/loader/src/main/java/net/neoforged/fml/loading/moddiscovery/ModValidator.java b/loader/src/main/java/net/neoforged/fml/loading/moddiscovery/ModValidator.java index c4fde0cc9..91b52eb79 100644 --- a/loader/src/main/java/net/neoforged/fml/loading/moddiscovery/ModValidator.java +++ b/loader/src/main/java/net/neoforged/fml/loading/moddiscovery/ModValidator.java @@ -19,6 +19,7 @@ import java.util.List; import java.util.Map; import java.util.stream.Collectors; +import java.util.stream.Stream; public class ModValidator { private static final Logger LOGGER = LogUtils.getLogger(); @@ -71,7 +72,12 @@ public ITransformationService.Resource getPluginResources() { } public ITransformationService.Resource getModResources() { - return new ITransformationService.Resource(IModuleLayerManager.Layer.GAME, this.candidateMods.stream().map(IModFile::getSecureJar).toList()); + var modFilesToLoad = Stream.concat( + // mods + this.loadingModList.getModFiles().stream().map(ModFileInfo::getFile), + // game libraries + this.modFiles.get(IModFile.Type.GAMELIBRARY).stream()); + return new ITransformationService.Resource(IModuleLayerManager.Layer.GAME, modFilesToLoad.map(ModFile::getSecureJar).toList()); } private List validateLanguages() { @@ -97,8 +103,9 @@ public BackgroundScanHandler stage2Validation() { loadingModList = ModSorter.sort(candidateMods, allErrors); loadingModList.addCoreMods(); loadingModList.addAccessTransformers(); + loadingModList.addMixinConfigs(); loadingModList.setBrokenFiles(brokenFiles); - BackgroundScanHandler backgroundScanHandler = new BackgroundScanHandler(candidateMods); + BackgroundScanHandler backgroundScanHandler = new BackgroundScanHandler(); loadingModList.addForScanning(backgroundScanHandler); return backgroundScanHandler; }