Separate plugin types #10
|
@ -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 {
|
||||||
|
|
|
@ -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,71 +104,19 @@ public class FrogLoaderImpl implements FrogLoader {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void discoverModProviders() {
|
private void loadGamePlugin() {
|
||||||
LOGGER.info("Discovering mod providers...");
|
FrogGamePlugin plugin = PluginLoader.discoverGamePlugins();
|
||||||
|
gameVersion = plugin.queryVersion();
|
||||||
for (FrogModProvider plugin : ServiceLoader.load(FrogModProvider.class)) {
|
ModProperties gameMod = plugin.getGameMod();
|
||||||
LOGGER.debug("Found mod provider: {}", plugin.getClass().getName());
|
if (gameMod != null) {
|
||||||
if (!plugin.isApplicable()) {
|
mods.put("integrated", Map.of(gameMod.id(), gameMod));
|
||||||
continue;
|
modIds.add(gameMod.id());
|
||||||
}
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
gamePlugin = plugin;
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings({"rawtypes", "unchecked"})
|
private void loadModProviders() {
|
||||||
private void initModMixins(Collection<ModProperties> loadedMods) {
|
PluginLoader.discoverModProviders(gameDir, mods, modIds, modProviders);
|
||||||
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();
|
|
||||||
ModProperties gameMod = plugin.getGameMod();
|
|
||||||
if (gameMod != null) {
|
|
||||||
mods.put("integrated", Map.of(gameMod.id(), gameMod));
|
|
||||||
modIds.add(gameMod.id());
|
|
||||||
}
|
|
||||||
gamePlugin = plugin;
|
|
||||||
} catch (Throwable e) {
|
|
||||||
LOGGER.error("Error during plugin initialisation: ", e);
|
|
||||||
throw new RuntimeException(e);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getArgument(String name) {
|
public String getArgument(String name) {
|
||||||
|
|
81
src/main/java/dev/frogmc/frogloader/impl/PluginLoader.java
Normal file
81
src/main/java/dev/frogmc/frogloader/impl/PluginLoader.java
Normal 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
|
|||||||
|
}
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue
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
The only difference here would be a insignificantly smaller memory footprint - this isn't an issue
It's not about performance, i think it's bad practice to use AtomicInteger when you don't need it
Resolved in
39ba054fcb