add ability for mods to provide further mod providers
This commit is contained in:
parent
6dd1bf04ed
commit
565e4df74e
|
@ -0,0 +1,10 @@
|
|||
package dev.frogmc.frogloader.example;
|
||||
|
||||
import dev.frogmc.frogloader.api.plugin.ModProvider;
|
||||
|
||||
public class ExampleModProvider implements ModProvider {
|
||||
@Override
|
||||
public String id() {
|
||||
return "example_mod:example";
|
||||
}
|
||||
}
|
|
@ -14,4 +14,4 @@ credits = [
|
|||
prelaunch = "dev.frogmc.frogloader.example.ExamplePreLaunchExtension"
|
||||
mixin = "example_mod.mixins.json"
|
||||
accesswidener = "example_mod.accesswidener"
|
||||
|
||||
modprovider = "dev.frogmc.frogloader.example.ExampleModProvider"
|
||||
|
|
|
@ -1,11 +1,8 @@
|
|||
package dev.frogmc.frogloader.impl;
|
||||
|
||||
import java.nio.file.Path;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.ServiceLoader;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
import dev.frogmc.frogloader.api.FrogLoader;
|
||||
import dev.frogmc.frogloader.api.mod.ModProperties;
|
||||
|
@ -26,37 +23,50 @@ public class PluginLoader {
|
|||
public static void discoverModProviders(Path gameDir, Map<String, Map<String, ModProperties>> mods, Collection<String> modIds, Collection<ModProvider> modProviders) throws ModDependencyResolver.ResolverException {
|
||||
LOGGER.info("Discovering mod providers...");
|
||||
|
||||
for (ModProvider plugin : ServiceLoader.load(ModProvider.class)) {
|
||||
List<ModProvider> providers = new ArrayList<>(ServiceLoader.load(ModProvider.class).stream().map(ServiceLoader.Provider::get).toList()); // we need mutability & random access
|
||||
Map<String, Collection<ModProperties>> properties = new HashMap<>();
|
||||
AtomicInteger size = new AtomicInteger(providers.size()); // use a separate size variable to not have to iterate over the list over and over again
|
||||
for (int i = 0; i < size.get(); i++) {
|
||||
ModProvider plugin = providers.get(i); // use random access to work around concurrent access (through modifications during iteration)
|
||||
LOGGER.debug("Found mod provider: {}", plugin.getClass().getName());
|
||||
if (!plugin.isApplicable()) {
|
||||
continue;
|
||||
}
|
||||
try {
|
||||
LOGGER.debug("Initialising mod provider: {}", plugin.id());
|
||||
Map<String, ModProperties> modsFromProvider = new HashMap<>();
|
||||
Collection<ModProperties> loadedMods = plugin.loadMods(Discovery.find(gameDir.resolve(plugin.loadDirectory()), plugin::isDirectoryApplicable, plugin::isFileApplicable), FrogLoaderImpl.getInstance().getClassloader()::addURL);
|
||||
Collection<ModProperties> loadedMods = plugin.loadMods(Discovery.find(gameDir.resolve(plugin.loadDirectory()),
|
||||
plugin::isDirectoryApplicable,
|
||||
plugin::isFileApplicable),
|
||||
FrogLoaderImpl.getInstance().getClassloader()::addURL);
|
||||
initializeModMixins(loadedMods);
|
||||
AWProcessor.load(loadedMods);
|
||||
|
||||
loadedMods.forEach(m -> modsFromProvider.put(m.id(), m));
|
||||
properties.put(plugin.id(), loadedMods);
|
||||
|
||||
LOGGER.debug("Loaded {} mod(s) from provider: {}", modsFromProvider.size(), plugin.id());
|
||||
mods.put(plugin.id(), modsFromProvider);
|
||||
modIds.addAll(modsFromProvider.keySet());
|
||||
modProviders.add(plugin);
|
||||
LOGGER.debug("Loaded {} mod(s) from provider: {}", loadedMods.size(), plugin.id());
|
||||
loadedMods.forEach(p -> p.extensions().runIfPresent(BuiltinExtensions.MOD_PROVIDER, ModProvider.class, provider -> {
|
||||
providers.add(provider);
|
||||
size.getAndIncrement();
|
||||
}));
|
||||
} catch (Throwable e) {
|
||||
LOGGER.error("Error during plugin initialisation: ", e);
|
||||
}
|
||||
}
|
||||
|
||||
Collection<ModProperties> properties = mods.values().stream().map(Map::values).flatMap(Collection::stream).collect(Collectors.toUnmodifiableSet());
|
||||
Collection<ModProperties> flattened = properties.values().stream().flatMap(Collection::stream).toList();
|
||||
try {
|
||||
Collection<ModProperties> solved = new ModDependencyResolver(properties).solve();
|
||||
mods.forEach((s, map) -> map.values().retainAll(solved));
|
||||
Collection<ModProperties> solved = new ModDependencyResolver(flattened).solve();
|
||||
properties.forEach((s, c) -> c.retainAll(solved));
|
||||
properties.forEach((s, c) -> {
|
||||
Map<String, ModProperties> map = mods.computeIfAbsent(s, u -> new HashMap<>());
|
||||
c.forEach(p -> map.put(p.id(), p));
|
||||
});
|
||||
modIds.addAll(solved.stream().map(ModProperties::id).toList());
|
||||
modProviders.addAll(providers);
|
||||
} catch (ModDependencyResolver.UnfulfilledDependencyException e) {
|
||||
LoaderGui.execUnfulfilledDep(CrashReportGenerator.writeReport(e, properties), e, false);
|
||||
LoaderGui.execUnfulfilledDep(CrashReportGenerator.writeReport(e, flattened), e, false);
|
||||
} catch (ModDependencyResolver.BreakingModException e) {
|
||||
LoaderGui.execBreakingDep(CrashReportGenerator.writeReport(e, properties), e, false);
|
||||
LoaderGui.execBreakingDep(CrashReportGenerator.writeReport(e, flattened), e, false);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -9,4 +9,5 @@ public class BuiltinExtensions {
|
|||
public final String PRE_LAUNCH = "prelaunch";
|
||||
public final String ACCESSWIDENER = "accesswidener";
|
||||
public final String LOADING_TYPE = "loading_type";
|
||||
public final String MOD_PROVIDER = "modprovider";
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue