Skip to content

Commit

Permalink
Add masking of classpath resources for in-dev
Browse files Browse the repository at this point in the history
  • Loading branch information
shartte committed Jan 5, 2025
1 parent 332e48b commit 4072d7e
Show file tree
Hide file tree
Showing 18 changed files with 604 additions and 150 deletions.
37 changes: 29 additions & 8 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ plugins {
id 'com.diffplug.spotless' version '6.25.0' apply false
id 'net.neoforged.gradleutils' version "${gradleutils_version}"
id 'net.neoforged.gradleutils.spotless' version "${gradleutils_version}" apply false
id 'org.jetbrains.gradle.plugin.idea-ext'
}

changelog {
Expand Down Expand Up @@ -72,19 +73,19 @@ allprojects {
subprojects { subProject ->
subProject.version = rootProject.version

apply plugin : fmlbuild.InDevModulePlugin
apply plugin: fmlbuild.InDevModulePlugin

jar {
manifest.attributes(
'Git-Commit' : gradleutils.gitInfo.abbreviatedId,
'Git-Commit': gradleutils.gitInfo.abbreviatedId,
'Build-Number': "${subProject.version}",
'Automatic-Module-Name' : "fml_${subProject.name.replace("-", "_")}",
'Specification-Title' : "FML${subProject.name}",
'Specification-Vendor' : 'NeoForged',
'Specification-Version' : "${subProject.version.toString().split('\\.')[0]}",
'Implementation-Title' : "FML $subProject.name",
'Automatic-Module-Name': "fml_${subProject.name.replace("-", "_")}",
'Specification-Title': "FML${subProject.name}",
'Specification-Vendor': 'NeoForged',
'Specification-Version': "${subProject.version.toString().split('\\.')[0]}",
'Implementation-Title': "FML $subProject.name",
'Implementation-Version': "${subProject.version.toString().split('\\.')[0]}.${subProject.version.toString().split('\\.')[1]}",
'Implementation-Vendor' : 'NeoForged'
'Implementation-Vendor': 'NeoForged'
)

if (subProject.name != 'junit') {
Expand Down Expand Up @@ -120,3 +121,23 @@ subprojects { subProject ->
}
}
}

configurations {
testJavaAgents {
description = "Used to resolve java agents attached to all JUnit configurations in IntelliJ"
}
}
dependencies {
// This should actually match the bytebuddy version used by Mockito, but the Mockito BOM sadly does not specify
testJavaAgents files("loader/devagent.jar")
testJavaAgents "net.bytebuddy:byte-buddy-agent:1.15.11"
}

// Make every JUnit test in IntelliJ load the devagent
idea.project.settings {
runConfigurations {
defaults(org.jetbrains.gradle.ext.JUnit) {
vmParameters = configurations.testJavaAgents.collect { "-javaagent:" + it.absolutePath }.join(" ")
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
package cpw.mods.modlauncher;

import java.io.IOException;
import java.net.URL;
import java.nio.file.Path;
import java.util.Enumeration;
import java.util.NoSuchElementException;
import java.util.Objects;
import java.util.Set;
import net.neoforged.fml.util.ClasspathResourceUtils;
import org.jetbrains.annotations.Nullable;

public class ResourceMaskingClassLoader extends ClassLoader {
static {
ClassLoader.registerAsParallelCapable();
}

private final Set<Path> maskedClasspathElements;

public ResourceMaskingClassLoader(ClassLoader parent, Set<Path> maskedClasspathElements) {
super(Objects.requireNonNull(parent, "parent"));
this.maskedClasspathElements = maskedClasspathElements;
}

@Override
public @Nullable URL getResource(String name) {
// This is a very tricky thing: if the resource points to one of the filtered paths,
// We need to use getResources() and find the next resource *not* matching the filter.
var resource = getParent().getResource(name);
if (resource == null) {
return null;
}

var resourceRoot = ClasspathResourceUtils.getRootFromResourceUrl(name, resource);
if (maskedClasspathElements.contains(resourceRoot)) {
try {
var resources = getResources(name);
resource = resources.hasMoreElements() ? resources.nextElement() : null;
} catch (IOException e) {
resource = null;
}
}

return resource;
}

@Override
public Enumeration<URL> getResources(String name) throws IOException {
return new FilteringEnumeration(super.getResources(name), name);
}

/**
* Filters an enumeration of URLs by using the given predicate.
*/
class FilteringEnumeration implements Enumeration<URL> {
private final Enumeration<URL> delegate;
// Relative path that resources were requested for.
private final String relativePath;

@Nullable
private URL nextElement;

public FilteringEnumeration(Enumeration<URL> delegate, String relativePath) {
this.delegate = delegate;
this.relativePath = relativePath;
seekNextElement();
}

@Override
public boolean hasMoreElements() {
return nextElement != null;
}

@Override
public URL nextElement() {
var result = nextElement;
if (result == null) {
throw new NoSuchElementException();
}
seekNextElement();
return result;
}

// Find the next element not within a masked classpath element
private void seekNextElement() {
while (delegate.hasMoreElements()) {
var el = delegate.nextElement();
Path root = ClasspathResourceUtils.getRootFromResourceUrl(relativePath, el);
if (!maskedClasspathElements.contains(root)) {
nextElement = el;
return;
}
}
nextElement = null; // No more elements
}
}
}
20 changes: 19 additions & 1 deletion loader/src/main/java/net/neoforged/fml/ModList.java
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@
import net.neoforged.neoforgespi.language.IModInfo;
import net.neoforged.neoforgespi.language.ModFileScanData;
import net.neoforged.neoforgespi.locating.IModFile;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.VisibleForTesting;

/**
* Master list of all mods - game-side version. This is classloaded in the game scope and
Expand All @@ -45,11 +47,21 @@ public class ModList {
private List<ModFileScanData> modFileScanData;
private List<ModContainer> sortedContainers;

static {
// Do this statically to avoid capturing a global reference to the mod list in the lambda function,
// which CrashReportCallables is going to store statically.
CrashReportCallables.registerCrashCallable("Mod List", () -> {
if (INSTANCE != null) {
return INSTANCE.crashReport();
}
return "-";
});
}

private ModList(final List<ModFile> modFiles, final List<ModInfo> sortedList) {
this.modFiles = modFiles.stream().map(ModFile::getModFileInfo).map(ModFileInfo.class::cast).collect(Collectors.toList());
this.sortedList = new ArrayList<>(sortedList);
this.fileById = this.modFiles.stream().map(IModFileInfo::getMods).flatMap(Collection::stream).map(ModInfo.class::cast).collect(Collectors.toMap(ModInfo::getModId, ModInfo::getOwningFile));
CrashReportCallables.registerCrashCallable("Mod List", this::crashReport);
}

private String fileToLine(IModFile mf) {
Expand Down Expand Up @@ -177,4 +189,10 @@ public void forEachModInOrder(Consumer<ModContainer> containerConsumer) {
public <T> Stream<T> applyForEachModContainer(Function<ModContainer, T> function) {
return indexedMods.values().stream().map(function);
}

@ApiStatus.OverrideOnly
@VisibleForTesting
public static void clear() {
INSTANCE = null;
}
}
8 changes: 5 additions & 3 deletions loader/src/main/java/net/neoforged/fml/ModLoader.java
Original file line number Diff line number Diff line change
Expand Up @@ -90,12 +90,13 @@ private static String computeModLauncherServiceList() {
* @param periodicTask Optional periodic task to perform on the main thread while other activities run
*/
public static void gatherAndInitializeMods(final Executor syncExecutor, final Executor parallelExecutor, final Runnable periodicTask) {
var loader = FMLLoader.current();
var loadingModList = FMLLoader.getLoadingModList();
loadingIssues.addAll(loadingModList.getModLoadingIssues());

ForgeFeature.registerFeature("javaVersion", ForgeFeature.VersionFeatureTest.forVersionString(IModInfo.DependencySide.BOTH, System.getProperty("java.version")));
ForgeFeature.registerFeature("openGLVersion", ForgeFeature.VersionFeatureTest.forVersionString(IModInfo.DependencySide.CLIENT, ImmediateWindowHandler.getGLVersion()));
FMLLoader.backgroundScanHandler.waitForScanToComplete(periodicTask);
loader.backgroundScanHandler.waitForScanToComplete(periodicTask);
final ModList modList = ModList.of(loadingModList.getModFiles().stream().map(ModFileInfo::getFile).toList(),
loadingModList.getMods());

Expand Down Expand Up @@ -406,9 +407,10 @@ public static List<ModLoadingIssue> getLoadingIssues() {

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

@ApiStatus.Internal
Expand Down

This file was deleted.

Loading

0 comments on commit 4072d7e

Please sign in to comment.