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 27, 2024
1 parent 0aa8991 commit 7e59340
Show file tree
Hide file tree
Showing 9 changed files with 153 additions and 60 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
/*
* Copyright (c) NeoForged and contributors
* SPDX-License-Identifier: LGPL-2.1-only
*/

package net.neoforged.fml.loading;

import cpw.mods.modlauncher.api.ITransformer;
import java.io.IOException;
import java.io.Reader;
import java.nio.file.Files;
import java.util.List;
import java.util.Objects;
import net.neoforged.coremod.CoreModScriptingEngine;
import net.neoforged.coremod.ICoreModScriptSource;
import net.neoforged.fml.loading.moddiscovery.CoreModFile;
import net.neoforged.fml.loading.moddiscovery.ModFileInfo;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
* Uses the coremod scripting engine from https://github.com/neoforged/CoreMods
* to load JS-based coremods. Avoids loading any of the classes unless a mod
* contains a JS-based coremod.
*/
class CoreModScriptLoader {
private static final Logger LOGGER = LoggerFactory.getLogger(CoreModScriptLoader.class);

private CoreModScriptLoader() {}

/**
* Enumerate script-based coremods.
*/
public static List<ITransformer<?>> loadCoreModScripts(List<ModFileInfo> modFileInfos) {
CoreModScriptingEngine engine;
try {
engine = new CoreModScriptingEngine();
} catch (NoClassDefFoundError e) {
// Fail for all mods that require a coremod scripting engine to be present
throw new IllegalStateException("Could not find the coremod script-engine, but the following mods require it: " + modFileInfos, e);
}

LOGGER.debug(LogMarkers.CORE, "Loading coremod scripts");
for (var modFile : modFileInfos) {
for (var coreMod : modFile.getFile().getCoreMods()) {
engine.loadCoreMod(new ScriptSourceAdapter(coreMod));
}
}

return engine.initializeCoreMods();
}

private record ScriptSourceAdapter(CoreModFile coreMod) implements ICoreModScriptSource {
@Override
public Reader readCoreMod() throws IOException {
return Files.newBufferedReader(coreMod.path());
}

@Override
public String getDebugSource() {
return coreMod.path().toString();
}

@Override
public Reader getAdditionalFile(final String fileName) throws IOException {
return Files.newBufferedReader(coreMod.file().findResource(fileName));
}

@Override
public String getOwnerId() {
return this.coreMod.file().getModInfos().get(0).getModId();
}

@Override
public String toString() {
return "{Name: " + coreMod.name() + ", Owner: " + getOwnerId() + " @ " + getDebugSource() + "}";
}

@Override
public boolean equals(Object obj) {
if (obj == this) return true;
if (obj == null || obj.getClass() != this.getClass()) return false;
var that = (ScriptSourceAdapter) obj;
return Objects.equals(this.coreMod, that.coreMod);
}
}
}
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 @@ -37,7 +36,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 @@ -88,9 +86,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());

try {
Class.forName("com.electronwill.nightconfig.core.Config", false, environment.getClass().getClassLoader());
Class.forName("com.electronwill.nightconfig.toml.TomlFormat", false, environment.getClass().getClassLoader());
Expand Down Expand Up @@ -148,10 +143,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,27 @@
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.Collections;
import java.util.HashMap;
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.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 +134,45 @@ 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<>(loadCoreModScripts());

// 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;
}

private List<ITransformer<?>> loadCoreModScripts() {
var filesWithCoreModScripts = LoadingModList.get().getModFiles()
.stream()
.filter(mf -> !mf.getFile().getCoreMods().isEmpty())
.toList();

if (filesWithCoreModScripts.isEmpty()) {
// Don't even bother starting the scripting engine if no mod contains scripting core mods
LOGGER.debug(LogMarkers.CORE, "Not loading coremod script-engine since no mod requested it");
return Collections.emptyList();
}

LOGGER.info(LogMarkers.CORE, "Loading coremod script-engine for {}", filesWithCoreModScripts);
try {
return CoreModScriptLoader.loadCoreModScripts(filesWithCoreModScripts);
} catch (NoClassDefFoundError e) {
var message = "Could not find the coremod script-engine, but the following mods require it: " + filesWithCoreModScripts;
ImmediateWindowHandler.crash(message);
throw new IllegalStateException(message, e);
}
}
}
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 @@ -5,45 +5,6 @@

package net.neoforged.fml.loading.moddiscovery;

import java.io.IOException;
import java.io.Reader;
import java.nio.file.Files;
import java.nio.file.Path;
import net.neoforged.coremod.ICoreModScriptSource;

public class CoreModFile implements ICoreModScriptSource {
private final Path internalPath;
private final ModFile file;
private final String name;

CoreModFile(final String name, final Path path, final ModFile file) {
this.name = name;
this.internalPath = path;
this.file = file;
}

@Override
public Reader readCoreMod() throws IOException {
return Files.newBufferedReader(this.internalPath);
}

@Override
public String getDebugSource() {
return this.internalPath.toString();
}

@Override
public Reader getAdditionalFile(final String fileName) throws IOException {
return Files.newBufferedReader(file.findResource(fileName));
}

@Override
public String getOwnerId() {
return this.file.getModInfos().get(0).getModId();
}

@Override
public String toString() {
return "{Name: " + name + ", Owner: " + getOwnerId() + " @ " + getDebugSource() + "}";
}
}
public record CoreModFile(String name, Path path, ModFile file) {}
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,6 @@ public boolean identifyMods() {
if (this.modFileInfo == null) return this.getType() != Type.MOD;
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.getDebugSource()));
this.mixinConfigs = ModFileParser.getMixinConfigs(this.modFileInfo);
this.mixinConfigs.forEach(mc -> LOGGER.debug(LogMarkers.LOADING, "Found mixin config {}", mc));
this.accessTransformers = ModFileParser.getAccessTransformers(this.modFileInfo)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -204,4 +204,9 @@ public String versionString() {
public List<String> usesServices() {
return usesServices;
}

@Override
public String toString() {
return modFile.toString();
}
}
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
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 7e59340

Please sign in to comment.