Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Java Core Mods #79

Merged
merged 8 commits into from
Jun 2, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion loader/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ dependencies {
api "net.neoforged:JarJarSelector:${jarjar_version}"
api "net.neoforged:JarJarMetadata:${jarjar_version}"
api("net.neoforged:bus:${eventbus_version}")
implementation("net.neoforged:JarJarFileSystems:$jarjar_version")

implementation("net.sf.jopt-simple:jopt-simple:${jopt_simple_version}")
implementation("cpw.mods:securejarhandler:${securejarhandler_version}")
Expand All @@ -44,7 +45,6 @@ dependencies {
testCompileOnly("org.jetbrains:annotations:${jetbrains_annotations_version}")
testRuntimeOnly("cpw.mods:bootstraplauncher:${bootstraplauncher_version}")
testRuntimeOnly("org.apache.logging.log4j:log4j-core:$log4j_version")
testRuntimeOnly("net.neoforged:JarJarFileSystems:$jarjar_version")
testImplementation("org.junit.jupiter:junit-jupiter-api:$jupiter_version")
testImplementation("org.junit.jupiter:junit-jupiter-params:$jupiter_version")
testImplementation("org.mockito:mockito-junit-jupiter:$mockito_version")
Expand Down
9 changes: 8 additions & 1 deletion loader/src/main/java/net/neoforged/fml/ModLoader.java
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.VisibleForTesting;

/**
* Contains the logic to load mods, i.e. turn the {@link LoadingModList} into the {@link ModList},
Expand Down Expand Up @@ -91,7 +92,6 @@ private static String computeModLauncherServiceList() {
*/
public static void gatherAndInitializeMods(final Executor syncExecutor, final Executor parallelExecutor, final Runnable periodicTask) {
var loadingModList = FMLLoader.getLoadingModList();
loadingIssues.clear();
loadingIssues.addAll(loadingModList.getModLoadingIssues());

ForgeFeature.registerFeature("javaVersion", ForgeFeature.VersionFeatureTest.forVersionString(IModInfo.DependencySide.BOTH, System.getProperty("java.version")));
Expand Down Expand Up @@ -410,6 +410,13 @@ public static List<ModLoadingIssue> getLoadingIssues() {
return List.copyOf(loadingIssues);
}

@VisibleForTesting
@ApiStatus.Internal
public static void clearLoadingIssues() {
LOGGER.info("Clearing {} loading issues", loadingIssues.size());
loadingIssues.clear();
}

@ApiStatus.Internal
public static void addLoadingIssue(ModLoadingIssue issue) {
loadingIssues.add(issue);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -139,10 +139,12 @@ private static Object[] getTranslationArgs(ModLoadingIssue issue) {
var args = new ArrayList<>(3 + issue.translationArgs().size());

var modInfo = issue.affectedMod();
if (modInfo == null && issue.affectedModFile() != null) {
if (!issue.affectedModFile().getModInfos().isEmpty()) {
modInfo = issue.affectedModFile().getModInfos().getFirst();
var file = issue.affectedModFile();
while (modInfo == null && file != null) {
if (!file.getModInfos().isEmpty()) {
modInfo = file.getModInfos().getFirst();
}
file = file.getDiscoveryAttributes().parent();
}
args.add(modInfo);
args.add(null); // Previously mod-loading phase
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
/*
* 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 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().getFirst().getModId();
}

@Override
public String toString() {
return "{Name: " + coreMod.name() + ", Owner: " + getOwnerId() + " @ " + getDebugSource() + "}";
}
}
}
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 @@ -22,7 +22,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.ModDiscoverer;
Expand All @@ -38,7 +37,6 @@
public class FMLLoader {
private static final Logger LOGGER = LogUtils.getLogger();
private static AccessTransformerEngine accessTransformer;
private static CoreModScriptingEngine coreModEngine;
private static LanguageProviderLoader languageProviderLoader;
private static Dist dist;
private static LoadingModList loadingModList;
Expand Down Expand Up @@ -90,9 +88,6 @@ static void onInitialLoad(IEnvironment environment) throws IncompatibleEnvironme
});
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 @@ -147,10 +142,6 @@ public static List<ITransformationService.Resource> completeScan(ILaunchContext
return List.of(modValidator.getModResources());
}

static CoreModScriptingEngine getCoreModEngine() {
return coreModEngine;
}

public static LanguageProviderLoader getLanguageLoadingProvider() {
return languageProviderLoader;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,15 @@
package net.neoforged.fml.loading;

import static net.neoforged.fml.loading.LogMarkers.CORE;
import static net.neoforged.fml.loading.LogMarkers.LOADING;

import com.mojang.logging.LogUtils;
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.List;
import java.util.Locale;
Expand All @@ -21,8 +23,13 @@
import java.util.function.Supplier;
import joptsimple.ArgumentAcceptingOptionSpec;
import joptsimple.OptionSpecBuilder;
import net.neoforged.fml.ModLoader;
import net.neoforged.fml.ModLoadingIssue;
import net.neoforged.fml.util.ServiceLoaderUtil;
import net.neoforged.neoforgespi.Environment;
import net.neoforged.neoforgespi.ILaunchContext;
import net.neoforged.neoforgespi.coremod.ICoreMod;
import org.jetbrains.annotations.VisibleForTesting;
import org.slf4j.Logger;

public class FMLServiceProvider implements ITransformationService {
Expand All @@ -40,7 +47,8 @@ public class FMLServiceProvider implements ITransformationService {
private List<String> mavenRootsArgumentList;
private List<String> mixinConfigsArgumentList;
private VersionInfo versionInfo;
private ILaunchContext launchContext;
@VisibleForTesting
ILaunchContext launchContext;

public FMLServiceProvider() {
final String markerselection = System.getProperty("forge.logging.markers", "");
Expand Down Expand Up @@ -115,7 +123,50 @@ public void argumentValues(OptionResult option) {

@Override
public List<? extends ITransformer<?>> transformers() {
LOGGER.debug(CORE, "Loading coremod transformers");
return FMLLoader.getCoreModEngine().initializeCoreMods();
LOGGER.debug(LOADING, "Loading coremod transformers");

var result = new ArrayList<>(loadCoreModScripts());

// Find all Java core mods
for (var coreMod : ServiceLoaderUtil.loadServices(launchContext, ICoreMod.class)) {
// Try to identify the mod-file this is from
var sourceFile = ServiceLoaderUtil.identifySourcePath(launchContext, coreMod);

try {
for (var transformer : coreMod.getTransformers()) {
LOGGER.debug(CORE, "Adding {} transformer from core-mod {} in {}", transformer.targets(), coreMod, sourceFile);
result.add(transformer);
}
} catch (Exception e) {
// Throwing here would cause the game to immediately crash without a proper error screen,
// since this method is called by ModLauncher directly.
ModLoader.addLoadingIssue(
ModLoadingIssue.error("fml.modloading.coremod_error", coreMod.getClass().getName(), sourceFile).withCause(e));
}
}

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 List.of();
}

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);
}
}
}
23 changes: 12 additions & 11 deletions loader/src/main/java/net/neoforged/fml/loading/LoadingModList.java
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
import net.neoforged.fml.loading.moddiscovery.ModFileInfo;
import net.neoforged.fml.loading.moddiscovery.ModInfo;
import net.neoforged.fml.loading.modscan.BackgroundScanHandler;
import net.neoforged.neoforgespi.language.IModFileInfo;
import net.neoforged.neoforgespi.language.IModInfo;

/**
Expand All @@ -31,13 +32,17 @@
*/
public class LoadingModList {
private static LoadingModList INSTANCE;
private final List<IModFileInfo> plugins;
private final List<ModFileInfo> modFiles;
private final List<ModInfo> sortedList;
private final Map<ModInfo, List<ModInfo>> modDependencies;
private final Map<String, ModFileInfo> fileById;
private final List<ModLoadingIssue> modLoadingIssues;

private LoadingModList(final List<ModFile> modFiles, final List<ModInfo> sortedList, Map<ModInfo, List<ModInfo>> modDependencies) {
private LoadingModList(final List<ModFile> plugins, final List<ModFile> modFiles, final List<ModInfo> sortedList, Map<ModInfo, List<ModInfo>> modDependencies) {
this.plugins = plugins.stream()
.map(ModFile::getModFileInfo)
.collect(Collectors.toList());
this.modFiles = modFiles.stream()
.map(ModFile::getModFileInfo)
.map(ModFileInfo.class::cast)
Expand All @@ -54,8 +59,8 @@ private LoadingModList(final List<ModFile> modFiles, final List<ModInfo> sortedL
this.modLoadingIssues = new ArrayList<>();
}

public static LoadingModList of(List<ModFile> modFiles, List<ModInfo> sortedList, List<ModLoadingIssue> issues, Map<ModInfo, List<ModInfo>> modDependencies) {
INSTANCE = new LoadingModList(modFiles, sortedList, modDependencies);
public static LoadingModList of(List<ModFile> plugins, List<ModFile> modFiles, List<ModInfo> sortedList, List<ModLoadingIssue> issues, Map<ModInfo, List<ModInfo>> modDependencies) {
INSTANCE = new LoadingModList(plugins, modFiles, sortedList, modDependencies);
INSTANCE.modLoadingIssues.addAll(issues);
return INSTANCE;
}
Expand All @@ -64,14 +69,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 All @@ -94,6 +91,10 @@ public void addForScanning(BackgroundScanHandler backgroundScanHandler) {
.forEach(backgroundScanHandler::submitForScanning);
}

public List<IModFileInfo> getPlugins() {
return plugins;
}

public List<ModFileInfo> getModFiles() {
return modFiles;
}
Expand Down
10 changes: 5 additions & 5 deletions loader/src/main/java/net/neoforged/fml/loading/ModSorter.java
Original file line number Diff line number Diff line change
Expand Up @@ -54,13 +54,13 @@ private ModSorter(final List<ModFile> modFiles) {
this.uniqueModListBuilder = new UniqueModListBuilder(modFiles);
}

public static LoadingModList sort(List<ModFile> mods, final List<ModLoadingIssue> issues) {
public static LoadingModList sort(List<ModFile> plugins, List<ModFile> mods, final List<ModLoadingIssue> issues) {
final ModSorter ms = new ModSorter(mods);
try {
ms.buildUniqueList();
} catch (ModLoadingException e) {
// We cannot build any list with duped mods. We have to abort immediately and report it
return LoadingModList.of(ms.systemMods, ms.systemMods.stream().map(mf -> (ModInfo) mf.getModInfos().get(0)).collect(toList()), e.getIssues(), Map.of());
return LoadingModList.of(plugins, ms.systemMods, ms.systemMods.stream().map(mf -> (ModInfo) mf.getModInfos().get(0)).collect(toList()), concat(issues, e.getIssues()), Map.of());
}

// try and validate dependencies
Expand All @@ -70,7 +70,7 @@ public static LoadingModList sort(List<ModFile> mods, final List<ModLoadingIssue

// if we miss a dependency or detect an incompatibility, we abort now
if (!resolutionResult.versionResolution.isEmpty() || !resolutionResult.incompatibilities.isEmpty()) {
list = LoadingModList.of(ms.systemMods, ms.systemMods.stream().map(mf -> (ModInfo) mf.getModInfos().get(0)).collect(toList()), concat(issues, resolutionResult.buildErrorMessages()), Map.of());
list = LoadingModList.of(plugins, ms.systemMods, ms.systemMods.stream().map(mf -> (ModInfo) mf.getModInfos().get(0)).collect(toList()), concat(issues, resolutionResult.buildErrorMessages()), Map.of());
} else {
// Otherwise, lets try and sort the modlist and proceed
ModLoadingException modLoadingException = null;
Expand All @@ -80,9 +80,9 @@ public static LoadingModList sort(List<ModFile> mods, final List<ModLoadingIssue
modLoadingException = e;
}
if (modLoadingException == null) {
list = LoadingModList.of(ms.modFiles, ms.sortedList, issues, ms.modDependencies);
list = LoadingModList.of(plugins, ms.modFiles, ms.sortedList, issues, ms.modDependencies);
} else {
list = LoadingModList.of(ms.modFiles, ms.sortedList, concat(issues, modLoadingException.getIssues()), Map.of());
list = LoadingModList.of(plugins, ms.modFiles, ms.sortedList, concat(issues, modLoadingException.getIssues()), Map.of());
}
}

Expand Down
Loading
Loading