Skip to content

Commit

Permalink
Add Java-based core mods
Browse files Browse the repository at this point in the history
Mods must ship a separate Jar-file (for example via jar-in-jar) and mark it as FMLModType: LIBRARY to make it load above the GAME layer. They then must provide an implementation of ICoreMod via the Java ServiceLoader to contribute their transformers.
  • Loading branch information
shartte committed Apr 5, 2024
1 parent df388c3 commit d00c627
Show file tree
Hide file tree
Showing 5 changed files with 78 additions and 19 deletions.
9 changes: 0 additions & 9 deletions loader/src/main/java/net/neoforged/fml/loading/FMLLoader.java
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@
import net.neoforged.accesstransformer.api.AccessTransformerEngine;
import net.neoforged.accesstransformer.ml.AccessTransformerService;
import net.neoforged.api.distmarker.Dist;
import net.neoforged.coremod.CoreModScriptingEngine;
import net.neoforged.fml.common.asm.RuntimeDistCleaner;
import net.neoforged.fml.loading.mixin.DeferredMixinConfigRegistration;
import net.neoforged.fml.loading.moddiscovery.BackgroundScanHandler;
Expand All @@ -38,7 +37,6 @@ public class FMLLoader {
private static final Logger LOGGER = LogUtils.getLogger();
private static AccessTransformerEngine accessTransformer;
private static ModDiscoverer modDiscoverer;
private static CoreModScriptingEngine coreModEngine;
private static LanguageLoadingProvider languageLoadingProvider;
private static Dist dist;
private static LoadingModList loadingModList;
Expand Down Expand Up @@ -89,9 +87,6 @@ static void onInitialLoad(IEnvironment environment, Set<String> otherServices) t
});
LOGGER.debug(LogMarkers.CORE, "Found Runtime Dist Cleaner");

coreModEngine = new CoreModScriptingEngine();
LOGGER.debug(LogMarkers.CORE, "FML found CoreMods version : {}", coreModEngine.getClass().getPackage().getImplementationVersion());

LOGGER.debug(LogMarkers.CORE, "Found ForgeSPI package implementation version {}", Environment.class.getPackage().getImplementationVersion());
LOGGER.debug(LogMarkers.CORE, "Found ForgeSPI package specification {}", Environment.class.getPackage().getSpecificationVersion());
if (Integer.parseInt(Environment.class.getPackage().getSpecificationVersion()) < 2) {
Expand Down Expand Up @@ -156,10 +151,6 @@ public static List<ITransformationService.Resource> completeScan(IModuleLayerMan
return List.of(modValidator.getModResources());
}

static CoreModScriptingEngine getCoreModEngine() {
return coreModEngine;
}

public static LanguageLoadingProvider getLanguageLoadingProvider() {
return languageLoadingProvider;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,22 +8,28 @@
import static net.neoforged.fml.loading.LogMarkers.CORE;

import com.mojang.logging.LogUtils;
import cpw.mods.modlauncher.Launcher;
import cpw.mods.modlauncher.api.IEnvironment;
import cpw.mods.modlauncher.api.IModuleLayerManager;
import cpw.mods.modlauncher.api.ITransformationService;
import cpw.mods.modlauncher.api.ITransformer;
import cpw.mods.modlauncher.api.IncompatibleEnvironmentException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.ServiceLoader;
import java.util.Set;
import java.util.function.BiFunction;
import joptsimple.ArgumentAcceptingOptionSpec;
import joptsimple.OptionSpecBuilder;
import net.neoforged.coremod.CoreModScriptingEngine;
import net.neoforged.fml.loading.moddiscovery.ModFile;
import net.neoforged.neoforgespi.Environment;
import net.neoforged.neoforgespi.coremod.ICoreMod;
import org.slf4j.Logger;

public class FMLServiceProvider implements ITransformationService {
Expand Down Expand Up @@ -129,6 +135,62 @@ public void argumentValues(OptionResult option) {
@Override
public List<? extends ITransformer<?>> transformers() {
LOGGER.debug(CORE, "Loading coremod transformers");
return FMLLoader.getCoreModEngine().initializeCoreMods();

var result = new ArrayList<ITransformer<?>>();
addCoreModScripts(result);

// Find all Java core mods
var pluginLayer = Launcher.INSTANCE.findLayerManager()
.flatMap(m -> m.getLayer(IModuleLayerManager.Layer.PLUGIN))
.orElseThrow();
for (var coreMod : ServiceLoader.load(pluginLayer, ICoreMod.class)) {
for (var transformer : coreMod.getTransformers()) {
if (transformer == null) {
throw new IllegalStateException("Core mod " + coreMod + " is trying to add null transformer");
}
LOGGER.debug(CORE, "Adding {} transformer from core-mod {}", transformer.targets(), coreMod);
result.add(transformer);
}
}

return result;
}

/**
* Enumerate script-based coremods.
*/
private void addCoreModScripts(List<ITransformer<?>> transformers) {
var modFiles = LoadingModList.get().getModFiles();

var modsThatNeedCoreModScripting = new HashSet<>();
for (var modFile : modFiles) {
if (!modFile.getFile().getCoreMods().isEmpty()) {
modsThatNeedCoreModScripting.add(modFile.getFile().getFileName());
}
}

if (modsThatNeedCoreModScripting.isEmpty()) {
// Don't even bother starting the scripting engine if no mod contains scripting core mods
return;
}

CoreModScriptingEngine engine;
try {
engine = new CoreModScriptingEngine();
} catch (NoClassDefFoundError e) {
LOGGER.debug(LogMarkers.CORE, "No core mod script engine is installed");

// Fail for all mods that require a coremod scripting engine to be present
throw new IllegalStateException("Could not find a coremod scripting engine, but the following mods require it: " + modsThatNeedCoreModScripting);
}

LOGGER.debug(LogMarkers.CORE, "Loading script-based core mods");
for (var modFile : modFiles) {
for (var coreMod : modFile.getFile().getCoreMods()) {
engine.loadCoreMod(coreMod);
}
}

transformers.addAll(engine.initializeCoreMods());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -66,14 +66,6 @@ public static LoadingModList get() {
return INSTANCE;
}

public void addCoreMods() {
modFiles.stream()
.map(ModFileInfo::getFile)
.map(ModFile::getCoreMods)
.flatMap(List::stream)
.forEach(FMLLoader.getCoreModEngine()::loadCoreMod);
}

public void addMixinConfigs() {
modFiles.stream()
.map(ModFileInfo::getFile)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,6 @@ public BackgroundScanHandler stage2Validation() {
allErrors.addAll(this.discoveryErrorData);

loadingModList = ModSorter.sort(candidateMods, allErrors);
loadingModList.addCoreMods();
loadingModList.addAccessTransformers();
loadingModList.addMixinConfigs();
loadingModList.setBrokenFiles(brokenFiles);
Expand Down
15 changes: 15 additions & 0 deletions spi/src/main/java/net/neoforged/neoforgespi/coremod/ICoreMod.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
/*
* Copyright (c) NeoForged and contributors
* SPDX-License-Identifier: LGPL-2.1-only
*/

package net.neoforged.neoforgespi.coremod;

import cpw.mods.modlauncher.api.ITransformer;

/**
* Provide using the Java {@link java.util.ServiceLoader} mechanism.
*/
public interface ICoreMod {
Iterable<? extends ITransformer<?>> getTransformers();
}

0 comments on commit d00c627

Please sign in to comment.