Separate plugin types #10

Merged
owlsys merged 14 commits from ecorous/plugin-types into main 2024-06-16 17:13:54 -04:00
3 changed files with 99 additions and 80 deletions
Showing only changes of commit a3aab3d87c - Show all commits

View file

@ -1,6 +1,6 @@
plugins { plugins {
java java
id("dev.frogmc.phytotelma") version "0.0.1-alpha.6" id("dev.frogmc.phytotelma") version "0.0.1-alpha.7"
} }
repositories { repositories {
@ -20,7 +20,7 @@ dependencies {
} }
phytotelma { phytotelma {
minecraft("1.20.6") minecraft("1.21", "1.20.6")
} }
java { java {

View file

@ -18,8 +18,6 @@ import dev.frogmc.frogloader.api.plugin.FrogGamePlugin;
import dev.frogmc.frogloader.api.plugin.FrogModProvider; import dev.frogmc.frogloader.api.plugin.FrogModProvider;
import dev.frogmc.frogloader.impl.gui.LoaderGui; import dev.frogmc.frogloader.impl.gui.LoaderGui;
import dev.frogmc.frogloader.impl.launch.MixinClassLoader; import dev.frogmc.frogloader.impl.launch.MixinClassLoader;
import dev.frogmc.frogloader.impl.mixin.AWProcessor;
import dev.frogmc.frogloader.impl.mod.BuiltinExtensions;
import dev.frogmc.frogloader.impl.mod.ModUtil; import dev.frogmc.frogloader.impl.mod.ModUtil;
import dev.frogmc.frogloader.impl.util.CrashReportGenerator; import dev.frogmc.frogloader.impl.util.CrashReportGenerator;
import dev.frogmc.frogloader.impl.util.SystemProperties; import dev.frogmc.frogloader.impl.util.SystemProperties;
@ -27,40 +25,32 @@ import lombok.Getter;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.spongepowered.asm.mixin.MixinEnvironment; import org.spongepowered.asm.mixin.MixinEnvironment;
import org.spongepowered.asm.mixin.Mixins;
public class FrogLoaderImpl implements FrogLoader { public class FrogLoaderImpl implements FrogLoader {
private static final boolean DEV_ENV = Boolean.getBoolean(SystemProperties.DEVELOPMENT); private static final boolean DEV_ENV = Boolean.getBoolean(SystemProperties.DEVELOPMENT);
private static final Logger LOGGER = LoggerFactory.getLogger("FrogLoader");
@Getter @Getter
private static FrogLoaderImpl instance; private static FrogLoaderImpl instance;
@Getter @Getter
private final String[] args; private final String[] args;
@Getter @Getter
private final Env env; private final Env env;
private final Logger LOGGER = LoggerFactory.getLogger("FrogLoader");
@Getter
private FrogGamePlugin gamePlugin;
@Getter @Getter
private final Collection<FrogModProvider> modProviders = new ArrayList<>(); private final Collection<FrogModProvider> modProviders = new ArrayList<>();
@Getter @Getter
private final Path gameDir, configDir; private final Path gameDir, configDir;
@Getter @Getter
private final Collection<Path> modsDirs = new HashSet<>(); private final Collection<Path> modsDirs = new HashSet<>();
@Getter @Getter
private final MixinClassLoader classloader; private final MixinClassLoader classloader;
@Getter @Getter
private final Gson gson = new Gson(); private final Gson gson = new Gson();
private final Map<String, Map<String, ModProperties>> mods = new HashMap<>();
@Getter
private FrogGamePlugin gamePlugin;
@Getter @Getter
private String gameVersion; private String gameVersion;
private final Map<String, Map<String, ModProperties>> mods = new HashMap<>();
private Collection<String> modIds = new ArrayList<>(); private Collection<String> modIds = new ArrayList<>();
@ -81,8 +71,8 @@ public class FrogLoaderImpl implements FrogLoader {
} }
try { try {
discoverGamePlugins(); loadGamePlugin();
discoverModProviders(); loadModProviders();
modProviders.stream().map(FrogModProvider::loadDirectory).map(gameDir::resolve).forEach(modsDirs::add); modProviders.stream().map(FrogModProvider::loadDirectory).map(gameDir::resolve).forEach(modsDirs::add);
advanceMixinState(); advanceMixinState();
modIds = collectModIds(); modIds = collectModIds();
@ -114,59 +104,8 @@ public class FrogLoaderImpl implements FrogLoader {
} }
} }
private void discoverModProviders() { private void loadGamePlugin() {
LOGGER.info("Discovering mod providers..."); FrogGamePlugin plugin = PluginLoader.discoverGamePlugins();
for (FrogModProvider plugin : ServiceLoader.load(FrogModProvider.class)) {
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));
initModMixins(loadedMods);
AWProcessor.load(loadedMods);
loadedMods.forEach(m -> modsFromProvider.put(m.id(), m));
LOGGER.debug("Loaded {} mod(s) from provider: {}", modsFromProvider.size(), plugin.id());
mods.put(plugin.id(), modsFromProvider);
modIds.addAll(modsFromProvider.keySet());
modProviders.add(plugin);
} catch (Throwable e) {
LOGGER.error("Error during plugin initialisation: ", e);
}
}
}
@SuppressWarnings({"rawtypes", "unchecked"})
private void initModMixins(Collection<ModProperties> loadedMods) {
loadedMods.forEach(props -> {
Object o = props.extensions().get(BuiltinExtensions.MIXIN_CONFIG);
if (o instanceof String name) {
Mixins.addConfiguration(name);
} else if (o instanceof Collection l) {
((Collection<String>) l).forEach(Mixins::addConfiguration);
}
});
}
private void discoverGamePlugins() {
LOGGER.info("Discovering game plugins...");
ServiceLoader<FrogGamePlugin> loader = ServiceLoader.load(FrogGamePlugin.class);
loader.stream().map(ServiceLoader.Provider::get).forEach(p -> LOGGER.info("Found game plugin: {}", p.getClass().getName()));
FrogGamePlugin[] applicablePlugins = ServiceLoader.load(FrogGamePlugin.class).stream().map(ServiceLoader.Provider::get).filter(FrogGamePlugin::isApplicable).toArray(FrogGamePlugin[]::new);
if (applicablePlugins.length > 1) {
throw new IllegalStateException("Multiple applicable game plugins found!");
} else if (applicablePlugins.length == 0) {
throw new IllegalStateException("No applicable game plugin found!");
}
FrogGamePlugin plugin = applicablePlugins[0]; // we can skip the loop as we always will only have one element
try {
plugin.init(this);
gameVersion = plugin.queryVersion(); gameVersion = plugin.queryVersion();
ModProperties gameMod = plugin.getGameMod(); ModProperties gameMod = plugin.getGameMod();
if (gameMod != null) { if (gameMod != null) {
@ -174,11 +113,10 @@ public class FrogLoaderImpl implements FrogLoader {
modIds.add(gameMod.id()); modIds.add(gameMod.id());
} }
gamePlugin = plugin; gamePlugin = plugin;
} catch (Throwable e) {
LOGGER.error("Error during plugin initialisation: ", e);
throw new RuntimeException(e);
} }
private void loadModProviders() {
PluginLoader.discoverModProviders(gameDir, mods, modIds, modProviders);
} }
public String getArgument(String name) { public String getArgument(String name) {

View file

@ -0,0 +1,81 @@
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 dev.frogmc.frogloader.api.FrogLoader;
import dev.frogmc.frogloader.api.mod.ModProperties;
import dev.frogmc.frogloader.api.plugin.FrogGamePlugin;
import dev.frogmc.frogloader.api.plugin.FrogModProvider;
import dev.frogmc.frogloader.impl.mixin.AWProcessor;
import dev.frogmc.frogloader.impl.mod.BuiltinExtensions;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.spongepowered.asm.mixin.Mixins;
public class PluginLoader {
private static final Logger LOGGER = LoggerFactory.getLogger("FrogLoader/Plugins");
public static void discoverModProviders(Path gameDir, Map<String, Map<String, ModProperties>> mods, Collection<String> modIds, Collection<FrogModProvider> modProviders) {
LOGGER.info("Discovering mod providers...");
for (FrogModProvider plugin : ServiceLoader.load(FrogModProvider.class)) {
LOGGER.debug("Found mod provider: {}", plugin.getClass().getName());
if (!plugin.isApplicable()) {
continue;
Ecorous marked this conversation as resolved Outdated
Outdated
Review

wouldn't int[] size = new int[] { providers.size() } be better as there's no need for thread safety?

wouldn't `int[] size = new int[] { providers.size() }` be better as there's no need for thread safety?

I believe this works better - but I wouldn't know as I didn't write it

I believe this works better - but I wouldn't know as I didn't write it

The only difference here would be a insignificantly smaller memory footprint - this isn't an issue

The only difference here would be a insignificantly smaller memory footprint - this isn't an issue
Outdated
Review

It's not about performance, i think it's bad practice to use AtomicInteger when you don't need it

It's not about performance, i think it's bad practice to use AtomicInteger when you don't need it

Resolved in 39ba054fcb

Resolved in 39ba054fcbb139134081d9bf776e035c4d893182
}
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));
initializeModMixins(loadedMods);
AWProcessor.load(loadedMods);
loadedMods.forEach(m -> modsFromProvider.put(m.id(), m));
LOGGER.debug("Loaded {} mod(s) from provider: {}", modsFromProvider.size(), plugin.id());
mods.put(plugin.id(), modsFromProvider);
modIds.addAll(modsFromProvider.keySet());
modProviders.add(plugin);
} catch (Throwable e) {
LOGGER.error("Error during plugin initialisation: ", e);
}
}
}
@SuppressWarnings({"rawtypes", "unchecked"})
private static void initializeModMixins(Collection<ModProperties> loadedMods) {
loadedMods.forEach(props -> {
Object o = props.extensions().get(BuiltinExtensions.MIXIN_CONFIG);
if (o instanceof String name) {
Mixins.addConfiguration(name);
} else if (o instanceof Collection l) {
((Collection<String>) l).forEach(Mixins::addConfiguration);
}
});
}
public static FrogGamePlugin discoverGamePlugins() {
LOGGER.info("Discovering game plugins...");
ServiceLoader<FrogGamePlugin> loader = ServiceLoader.load(FrogGamePlugin.class);
loader.stream().map(ServiceLoader.Provider::get).forEach(p -> LOGGER.info("Found game plugin: {}", p.getClass().getName()));
FrogGamePlugin[] applicablePlugins = ServiceLoader.load(FrogGamePlugin.class).stream().map(ServiceLoader.Provider::get).filter(FrogGamePlugin::isApplicable).toArray(FrogGamePlugin[]::new);
if (applicablePlugins.length > 1) {
throw new IllegalStateException("Multiple applicable game plugins found!");
} else if (applicablePlugins.length == 0) {
throw new IllegalStateException("No applicable game plugin found!");
}
FrogGamePlugin plugin = applicablePlugins[0]; // we can skip the loop as we always will only have one element
try {
plugin.init(FrogLoader.getInstance());
return plugin;
} catch (Throwable e) {
LOGGER.error("Error during plugin initialisation: ", e);
throw new RuntimeException(e);
}
}
}