Skip to content

Commit

Permalink
Rework SpongeForge mod locators and load resources in dev
Browse files Browse the repository at this point in the history
  • Loading branch information
Yeregorix committed Dec 21, 2024
1 parent 6ad9559 commit a35bb81
Show file tree
Hide file tree
Showing 11 changed files with 192 additions and 68 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -161,26 +161,26 @@ public boolean process(final List<Path[]> classpath) {
});
}

final StringBuilder gameResourcesEnvBuilder = new StringBuilder();
final StringBuilder resourcesEnvBuilder = new StringBuilder();
for (final Path resource : gameLibs) {
gameResourcesEnvBuilder.append(resource).append(';');
resourcesEnvBuilder.append(resource).append(File.pathSeparator);
}
for (final List<Path> project : unknownProjects.values()) {
for (final Path resource : project) {
gameResourcesEnvBuilder.append(resource).append('&');
resourcesEnvBuilder.append(resource).append('&');
}
gameResourcesEnvBuilder.setCharAt(gameResourcesEnvBuilder.length() - 1, ';');
resourcesEnvBuilder.setCharAt(resourcesEnvBuilder.length() - 1, File.pathSeparatorChar);
}
for (final Path resource : spongeImplUnion) {
gameResourcesEnvBuilder.append(resource).append('&');
resourcesEnvBuilder.append(resource).append('&');
}
gameResourcesEnvBuilder.setLength(gameResourcesEnvBuilder.length() - 1);
final String gameResourcesEnv = gameResourcesEnvBuilder.toString();
resourcesEnvBuilder.setLength(resourcesEnvBuilder.length() - 1);
final String resourcesEnv = resourcesEnvBuilder.toString();

if (DEBUG) {
System.out.println("Game resources env: " + gameResourcesEnv);
System.out.println("Resources env: " + resourcesEnv);
}
System.setProperty("sponge.gameResources", gameResourcesEnv);
System.setProperty("sponge.resources", resourcesEnv);
return true;
}
}
5 changes: 1 addition & 4 deletions forge/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -223,12 +223,9 @@ extensions.configure(UserDevExtension::class) {
environment("MOD_CLASSES", "nop")
}

create("client") {
workingDirectory(file("run/client"))
}
create("client")

create("server") {
workingDirectory(file("run/client"))
args("--nogui")
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
/*
* This file is part of Sponge, licensed under the MIT License (MIT).
*
* Copyright (c) SpongePowered <https://www.spongepowered.org>
* Copyright (c) contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package org.spongepowered.forge.applaunch.loading.moddiscovery;

import cpw.mods.jarhandling.SecureJar;
import net.minecraftforge.forgespi.locating.IModFile;
import net.minecraftforge.forgespi.locating.IModProvider;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Map;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.Consumer;
import java.util.stream.Stream;

public abstract class AbstractModProvider implements IModProvider {
private static final Logger LOGGER = LogManager.getLogger();

@Override
public boolean isValid(final IModFile modFile) {
return true;
}

@Override
public void initArguments(final Map<String, ?> arguments) {
}

@Override
public void scanFile(final IModFile file, final Consumer<Path> pathConsumer) {
LOGGER.debug("Scan started: {}", file);
final SecureJar jar = file.getSecureJar();

Consumer<Path> consumer = pathConsumer;
final AtomicReference<SecureJar.Status> minStatus = new AtomicReference<>(SecureJar.Status.NONE);
if (jar.hasSecurityData()) {
minStatus.set(SecureJar.Status.VERIFIED);
consumer = path -> {
pathConsumer.accept(path);
SecureJar.Status status = jar.verifyPath(path);
if (status.ordinal() < minStatus.get().ordinal())
minStatus.set(status);
};
}

try (final Stream<Path> files = Files.walk(jar.getRootPath())) {
files.filter(p -> p.toString().endsWith(".class")).forEach(consumer);
} catch (IOException e) {
LOGGER.debug("Scan failed: {}", file);
minStatus.set(SecureJar.Status.NONE);
}

file.setSecurityStatus(minStatus.get());
LOGGER.debug("Scan finished: {}", file);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,14 +24,16 @@
*/
package org.spongepowered.forge.applaunch.loading.moddiscovery;

import cpw.mods.jarhandling.JarMetadata;
import cpw.mods.jarhandling.SecureJar;
import net.minecraftforge.fml.loading.moddiscovery.AbstractModProvider;
import net.minecraftforge.fml.loading.moddiscovery.ModFile;
import net.minecraftforge.fml.loading.moddiscovery.ModFileInfo;
import net.minecraftforge.fml.loading.moddiscovery.ModFileParser;
import net.minecraftforge.fml.loading.moddiscovery.ModJarMetadata;
import net.minecraftforge.forgespi.language.IModFileInfo;
import net.minecraftforge.forgespi.locating.IModFile;
import net.minecraftforge.forgespi.locating.IModLocator;
import net.minecraftforge.forgespi.locating.ModFileFactory;
import net.minecraftforge.forgespi.locating.IModProvider;
import org.spongepowered.common.applaunch.AppLaunch;
import org.spongepowered.common.applaunch.metadata.PluginMetadataFixer;
import org.spongepowered.common.applaunch.plugin.PluginPlatformConstants;
Expand All @@ -46,7 +48,11 @@
import java.nio.file.Path;
import java.util.List;

@SuppressWarnings("UnstableApiUsage")
public final class PluginFileParser {
private static final String MODS_TOML = "META-INF/mods.toml";
private static final String MODULE_INFO = "module-info.class";

private static Constructor<ModJarMetadata> modJarMetadataConstructor;

static {
Expand All @@ -58,7 +64,7 @@ public final class PluginFileParser {
}
}

private static IModFileInfo parsePluginMetadata(final IModFile iModFile) {
private static IModFileInfo parsePluginFileInfo(final IModFile iModFile) {
final ModFile modFile = (ModFile) iModFile;
AppLaunch.logger().debug("Considering plugin file candidate {}", modFile.getFilePath());

Expand All @@ -82,6 +88,14 @@ private static IModFileInfo parsePluginMetadata(final IModFile iModFile) {
}
}

private static IModFileInfo parseModFileInfo(final IModFile iModFile) {
return ModFileParser.modsTomlParser(iModFile);
}

private static IModFileInfo parseLibraryFileInfo(final IModFile iModFile) {
return DummyModProvider.INSTANCE.manifestParser(iModFile);
}

private static ModJarMetadata newModJarMetadata() {
try {
return modJarMetadataConstructor.newInstance();
Expand All @@ -90,13 +104,56 @@ private static ModJarMetadata newModJarMetadata() {
}
}

public static ModFile newPluginInstance(final IModLocator locator, final Path... path) {
ModJarMetadata mjm = newModJarMetadata();
ModFile modFile = (ModFile) ModFileFactory.FACTORY.build(SecureJar.from(jar -> mjm, path), locator, PluginFileParser::parsePluginMetadata);
mjm.setModFile(modFile);
return modFile;
private static boolean useModJarMetadata(final SecureJar jar) {
final SecureJar.ModuleDataProvider data = jar.moduleDataProvider();
if (data.findFile(PluginFileParser.MODULE_INFO).isPresent()) {
return false;
}
return data.findFile(PluginFileParser.MODS_TOML).isPresent() || data.findFile(PluginPlatformConstants.METADATA_FILE_LOCATION).isPresent();
}

public static ModFile newModFile(final IModProvider provider, boolean allowUnknown, final Path... paths) {
final ModJarMetadata mjm = newModJarMetadata();
final SecureJar jar = SecureJar.from(j -> useModJarMetadata(j) ? mjm : JarMetadata.from(j, paths), paths);

final SecureJar.ModuleDataProvider data = jar.moduleDataProvider();
final String type = data.getManifest().getMainAttributes().getValue("FMLModType");

if (data.findFile(PluginFileParser.MODS_TOML).isPresent()) {
ModFile modFile = new ModFile(jar, provider, PluginFileParser::parseModFileInfo, type == null ? "MOD" : type);
mjm.setModFile(modFile);
return modFile;
}

if (data.findFile(PluginPlatformConstants.METADATA_FILE_LOCATION).isPresent()) {
ModFile modFile = new ModFile(jar, provider, PluginFileParser::parsePluginFileInfo, type == null ? "MOD" : type);
mjm.setModFile(modFile);
return modFile;
}

if (!allowUnknown && type == null) {
throw new IllegalArgumentException("Unknown mod file type");
}

return new ModFile(jar, provider, PluginFileParser::parseLibraryFileInfo, type == null ? "GAMELIBRARY" : type);
}

public static ModFile newLibraryFile(final IModProvider provider, final Path... paths) {
final SecureJar jar = SecureJar.from(paths);
return new ModFile(jar, provider, PluginFileParser::parseLibraryFileInfo, "GAMELIBRARY");
}

private PluginFileParser() {
private static class DummyModProvider extends AbstractModProvider {
private static final DummyModProvider INSTANCE = new DummyModProvider();

@Override
public String name() {
return "dummy";
}

@Override
public IModFileInfo manifestParser(IModFile mod) {
return super.manifestParser(mod);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,8 @@
import cpw.mods.modlauncher.Environment;
import cpw.mods.modlauncher.Launcher;
import net.minecraftforge.fml.loading.FMLEnvironment;
import net.minecraftforge.fml.loading.moddiscovery.AbstractModProvider;
import net.minecraftforge.forgespi.locating.IDependencyLocator;
import net.minecraftforge.forgespi.locating.IModFile;
import net.minecraftforge.forgespi.locating.IModLocator;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.spongepowered.forge.applaunch.loading.moddiscovery.library.Log4JLogger;
Expand All @@ -42,14 +40,13 @@
import java.util.List;
import java.util.Map;

// works with ForgeProductionBootstrap to make this whole thing go
public class SpongeForgeDependencyLocator extends AbstractModProvider implements IDependencyLocator {
private static final Logger LOGGER = LogManager.getLogger();

private LibraryManager libraryManager;

@Override
public List<IModFile> scanMods(Iterable<IModFile> loadedMods) {
public List<IModFile> scanMods(final Iterable<IModFile> loadedMods) {
final List<IModFile> modFiles = new ArrayList<>();

// Add Sponge-specific libraries
Expand All @@ -64,23 +61,13 @@ public List<IModFile> scanMods(Iterable<IModFile> loadedMods) {
for (final LibraryManager.Library library : this.libraryManager.getAll("main")) {
final Path path = library.file();
SpongeForgeDependencyLocator.LOGGER.debug("Proposing jar {} as a game library", path);

final IModLocator.ModFileOrException fileOrException = createMod(path);
if (fileOrException.ex() != null) {
throw fileOrException.ex();
}
modFiles.add(fileOrException.file());
modFiles.add(PluginFileParser.newLibraryFile(this, path));
}
}

return modFiles;
}

@Override
protected String getDefaultJarModType() {
return IModFile.Type.GAMELIBRARY.name();
}

@Override
public String name() {
return "spongeforge";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,28 +28,43 @@
import cpw.mods.modlauncher.Environment;
import cpw.mods.modlauncher.Launcher;
import net.minecraftforge.fml.loading.FMLEnvironment;
import net.minecraftforge.fml.loading.moddiscovery.AbstractModProvider;
import net.minecraftforge.forgespi.locating.IModLocator;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.spongepowered.forge.applaunch.plugin.ForgePluginPlatform;

import java.io.File;
import java.net.URI;
import java.net.URL;
import java.nio.file.FileSystem;
import java.nio.file.FileSystems;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.stream.Stream;

public final class SpongeForgeModLocator extends AbstractModProvider implements IModLocator {
private static final Logger LOGGER = LogManager.getLogger();

@Override
public List<ModFileOrException> scanMods() {
if (!FMLEnvironment.production) {
return List.of();
final List<ModFileOrException> resources = new ArrayList<>();
final String resourcesProp = System.getProperty("sponge.resources");
if (resourcesProp != null) {
for (final String entry : resourcesProp.split(File.pathSeparator)) {
if (entry.isBlank()) {
continue;
}

final Path[] paths = Stream.of(entry.split("&")).map(Path::of).toArray(Path[]::new);
resources.add(new ModFileOrException(PluginFileParser.newModFile(this, true, paths), null));
}
}
return resources;
}

try {
Expand All @@ -66,7 +81,7 @@ public List<ModFileOrException> scanMods() {
} catch (Exception e) {
throw new RuntimeException(e);
}
}).map(this::createMod).toList();
}).map((path -> new ModFileOrException(PluginFileParser.newModFile(this, false, path), null))).toList();
} catch (Exception e) {
LOGGER.error("Failed to scan mod candidates", e);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,15 +24,14 @@
*/
package org.spongepowered.forge.applaunch.loading.moddiscovery.locator;

import net.minecraftforge.fml.loading.moddiscovery.AbstractModProvider;
import net.minecraftforge.forgespi.locating.IModLocator;
import org.spongepowered.forge.applaunch.loading.moddiscovery.AbstractModProvider;
import org.spongepowered.forge.applaunch.loading.moddiscovery.PluginFileParser;

import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.stream.Stream;

public final class EnvironmentPluginLocator extends AbstractModProvider implements IModLocator {
Expand All @@ -41,25 +40,16 @@ public final class EnvironmentPluginLocator extends AbstractModProvider implemen
public List<ModFileOrException> scanMods() {
final List<ModFileOrException> modFiles = new ArrayList<>();
for (final Path[] paths : EnvironmentPluginLocator.getPluginsPaths()) {
modFiles.add(new ModFileOrException(PluginFileParser.newPluginInstance(this, paths), null));
modFiles.add(new ModFileOrException(PluginFileParser.newModFile(this, false, paths), null));
}
return modFiles;
}

@Override
protected ModFileOrException createMod(Path path) {
return new ModFileOrException(PluginFileParser.newPluginInstance(this, path), null);
}

@Override
public String name() {
return "environment plugin";
}

@Override
public void initArguments(final Map<String, ?> arguments) {
}

private static List<Path[]> getPluginsPaths() {
final String env = System.getenv("SPONGE_PLUGINS");
if (env == null) {
Expand Down
Loading

0 comments on commit a35bb81

Please sign in to comment.