Separate plugin types #10
|
@ -30,11 +30,11 @@ public interface FrogLoader {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get all loaded game plugins.
|
* Get the currently loaded game plugin.
|
||||||
*
|
*
|
||||||
* @return A collection of all loaded game plugins
|
* @return The game plugin applicable to the current game
|
||||||
*/
|
*/
|
||||||
Collection<FrogGamePlugin> getGamePlugins();
|
FrogGamePlugin getGamePlugin();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get all loaded mod providers.
|
* Get all loaded mod providers.
|
||||||
|
@ -43,8 +43,6 @@ public interface FrogLoader {
|
||||||
*/
|
*/
|
||||||
Collection<FrogModProvider> getModProviders();
|
Collection<FrogModProvider> getModProviders();
|
||||||
|
|
||||||
//Collection<FrogPlugin> getPlugins();
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the current (physical) environment.
|
* Get the current (physical) environment.
|
||||||
*
|
*
|
||||||
|
@ -70,11 +68,11 @@ public interface FrogLoader {
|
||||||
Path getConfigDir();
|
Path getConfigDir();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the current mods directory.
|
* Get the current mods directories.
|
||||||
*
|
*
|
||||||
* @return The current mods directory
|
* @return The current mods directories
|
||||||
*/
|
*/
|
||||||
Path getModsDir();
|
Collection<Path> getModsDirs();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Query whether this loader is currently running in a development environment.
|
* Query whether this loader is currently running in a development environment.
|
||||||
|
@ -108,4 +106,10 @@ public interface FrogLoader {
|
||||||
* @see FrogPlugin
|
* @see FrogPlugin
|
||||||
*/
|
*/
|
||||||
Collection<ModProperties> getMods();
|
Collection<ModProperties> getMods();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the version of the currently loaded game
|
||||||
|
* @return The current game version
|
||||||
|
*/
|
||||||
|
String getGameVersion();
|
||||||
}
|
}
|
||||||
|
|
|
@ -70,10 +70,13 @@ public final class ModExtensions {
|
||||||
* @param action the action to run on the value of the extension if it is present
|
* @param action the action to run on the value of the extension if it is present
|
||||||
* @param <T> The type of the value of this extension
|
* @param <T> The type of the value of this extension
|
||||||
*/
|
*/
|
||||||
|
@SuppressWarnings({"rawtypes", "unchecked"})
|
||||||
public <T> void runIfPresent(String key, Consumer<T> action) {
|
public <T> void runIfPresent(String key, Consumer<T> action) {
|
||||||
T value = get(key);
|
Object value = get(key);
|
||||||
if (value != null) {
|
if (value instanceof Collection c){
|
||||||
action.accept(value);
|
((Collection<T>)c).forEach(action);
|
||||||
|
} else if (value != null) {
|
||||||
|
action.accept((T)value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package dev.frogmc.frogloader.api.plugin;
|
package dev.frogmc.frogloader.api.plugin;
|
||||||
|
|
||||||
import dev.frogmc.frogloader.api.FrogLoader;
|
import dev.frogmc.frogloader.api.FrogLoader;
|
||||||
|
import dev.frogmc.frogloader.api.mod.ModProperties;
|
||||||
|
|
||||||
public interface FrogGamePlugin {
|
public interface FrogGamePlugin {
|
||||||
default void run() {
|
default void run() {
|
||||||
|
@ -12,4 +13,10 @@ public interface FrogGamePlugin {
|
||||||
|
|
||||||
default void init(FrogLoader loader) throws Exception {
|
default void init(FrogLoader loader) throws Exception {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
String queryVersion();
|
||||||
|
|
||||||
|
default ModProperties getGameMod() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,6 +2,7 @@ package dev.frogmc.frogloader.api.plugin;
|
||||||
|
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
|
import java.util.Collections;
|
||||||
|
|
||||||
import dev.frogmc.frogloader.api.mod.ModProperties;
|
import dev.frogmc.frogloader.api.mod.ModProperties;
|
||||||
|
|
||||||
|
@ -20,10 +21,14 @@ public interface FrogModProvider {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
default void initMods(Collection<ModProperties> mods) {
|
default boolean isDirectoryApplicable(Path path) {
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
default ModProperties loadMod(Path path) {
|
default void preLaunch(Collection<ModProperties> mods) {
|
||||||
return null;
|
}
|
||||||
|
|
||||||
|
default Collection<ModProperties> loadMods(Collection<Path> modFiles) throws Exception {
|
||||||
|
return Collections.emptySet();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,6 +18,8 @@ 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;
|
||||||
|
@ -25,9 +27,10 @@ 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 {
|
||||||
public static final String MOD_FILE_EXTENSION = ".frogmod";
|
|
||||||
private static final boolean DEV_ENV = Boolean.getBoolean(SystemProperties.DEVELOPMENT);
|
private static final boolean DEV_ENV = Boolean.getBoolean(SystemProperties.DEVELOPMENT);
|
||||||
@Getter
|
@Getter
|
||||||
private static FrogLoaderImpl instance;
|
private static FrogLoaderImpl instance;
|
||||||
|
@ -37,17 +40,16 @@ public class FrogLoaderImpl implements FrogLoader {
|
||||||
private final Env env;
|
private final Env env;
|
||||||
private final Logger LOGGER = LoggerFactory.getLogger("FrogLoader");
|
private final Logger LOGGER = LoggerFactory.getLogger("FrogLoader");
|
||||||
|
|
||||||
// @Getter
|
|
||||||
// private final List<FrogPlugin> plugins = new ArrayList<>();
|
|
||||||
|
|
||||||
@Getter
|
@Getter
|
||||||
private final Collection<FrogGamePlugin> gamePlugins = new ArrayList<>();
|
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, modsDir;
|
private final Path gameDir, configDir;
|
||||||
|
@Getter
|
||||||
|
private final Collection<Path> modsDirs = new HashSet<>();
|
||||||
|
|
||||||
@Getter
|
@Getter
|
||||||
private final MixinClassLoader classloader;
|
private final MixinClassLoader classloader;
|
||||||
|
@ -55,9 +57,10 @@ public class FrogLoaderImpl implements FrogLoader {
|
||||||
@Getter
|
@Getter
|
||||||
private final Gson gson = new Gson();
|
private final Gson gson = new Gson();
|
||||||
|
|
||||||
// Map<Provider ID, Map<Mod ID, ModProperties>>
|
@Getter
|
||||||
|
private String gameVersion;
|
||||||
|
|
||||||
private final Map<String, Map<String, ModProperties>> mods = new HashMap<>();
|
private final Map<String, Map<String, ModProperties>> mods = new HashMap<>();
|
||||||
// private Map<String, ModProperties> mods;
|
|
||||||
private Collection<String> modIds = new ArrayList<>();
|
private Collection<String> modIds = new ArrayList<>();
|
||||||
|
|
||||||
|
|
||||||
|
@ -69,12 +72,10 @@ public class FrogLoaderImpl implements FrogLoader {
|
||||||
|
|
||||||
gameDir = Paths.get(getArgumentOrElse("gameDir", "."));
|
gameDir = Paths.get(getArgumentOrElse("gameDir", "."));
|
||||||
configDir = gameDir.resolve("config");
|
configDir = gameDir.resolve("config");
|
||||||
modsDir = gameDir.resolve("mods");
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
Files.createDirectories(gameDir);
|
Files.createDirectories(gameDir);
|
||||||
Files.createDirectories(configDir);
|
Files.createDirectories(configDir);
|
||||||
Files.createDirectories(modsDir);
|
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
LOGGER.warn("Failed to create essential directories ", e);
|
LOGGER.warn("Failed to create essential directories ", e);
|
||||||
}
|
}
|
||||||
|
@ -82,12 +83,13 @@ public class FrogLoaderImpl implements FrogLoader {
|
||||||
try {
|
try {
|
||||||
discoverGamePlugins();
|
discoverGamePlugins();
|
||||||
discoverModProviders();
|
discoverModProviders();
|
||||||
|
modProviders.stream().map(FrogModProvider::loadDirectory).map(gameDir::resolve).forEach(modsDirs::add);
|
||||||
advanceMixinState();
|
advanceMixinState();
|
||||||
modIds = collectModIds();
|
modIds = collectModIds();
|
||||||
LOGGER.info(ModUtil.getModList(getMods()));
|
LOGGER.info(ModUtil.getModList(getMods()));
|
||||||
LOGGER.info("Launching...");
|
LOGGER.info("Launching...");
|
||||||
gamePlugins.forEach(FrogGamePlugin::run);
|
modProviders.forEach(plugin -> plugin.preLaunch(mods.get(plugin.id()).values()));
|
||||||
modProviders.forEach(m -> m.initMods(mods.get(m.id()).values()));
|
gamePlugin.run();
|
||||||
} catch (Throwable t) {
|
} catch (Throwable t) {
|
||||||
LoaderGui.execReport(CrashReportGenerator.writeReport(t, getMods()), false);
|
LoaderGui.execReport(CrashReportGenerator.writeReport(t, getMods()), false);
|
||||||
}
|
}
|
||||||
|
@ -114,40 +116,47 @@ public class FrogLoaderImpl implements FrogLoader {
|
||||||
|
|
||||||
private void discoverModProviders() {
|
private void discoverModProviders() {
|
||||||
LOGGER.info("Discovering mod providers...");
|
LOGGER.info("Discovering mod providers...");
|
||||||
ServiceLoader<FrogModProvider> loader = ServiceLoader.load(FrogModProvider.class);
|
|
||||||
loader.stream().map(ServiceLoader.Provider::get).forEach(p -> LOGGER.info("Found mod provider: " + p.getClass().getName()));
|
|
||||||
FrogModProvider[] applicableProviders = ServiceLoader.load(FrogModProvider.class).stream().map(ServiceLoader.Provider::get).filter(FrogModProvider::isApplicable).toArray(FrogModProvider[]::new);
|
|
||||||
|
|
||||||
for (FrogModProvider plugin : applicableProviders) {
|
for (FrogModProvider plugin : ServiceLoader.load(FrogModProvider.class)) {
|
||||||
|
LOGGER.debug("Found mod provider: {}", plugin.getClass().getName());
|
||||||
|
if (!plugin.isApplicable()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
try {
|
try {
|
||||||
LOGGER.info("Initialising mod provider: " + plugin.id());
|
LOGGER.debug("Initialising mod provider: {}", plugin.id());
|
||||||
Map<String, ModProperties> modsFromProvider = new HashMap<>();
|
Map<String, ModProperties> modsFromProvider = new HashMap<>();
|
||||||
Collection<Path> paths = Discovery.find(gameDir.resolve(plugin.loadDirectory()), p -> false, plugin::isFileApplicable);
|
Collection<ModProperties> loadedMods = plugin.loadMods(Discovery.find(gameDir.resolve(plugin.loadDirectory()), plugin::isDirectoryApplicable, plugin::isFileApplicable));
|
||||||
paths.forEach(p -> {
|
initModMixins(loadedMods);
|
||||||
LOGGER.info("Loading mod: " + p);
|
AWProcessor.load(loadedMods);
|
||||||
try {
|
|
||||||
ModProperties mod = plugin.loadMod(p);
|
loadedMods.forEach(m -> modsFromProvider.put(m.id(), m));
|
||||||
modsFromProvider.put(mod.id(), mod);
|
|
||||||
} catch (Throwable e) {
|
LOGGER.debug("Loaded {} mod(s) from provider: {}", modsFromProvider.size(), plugin.id());
|
||||||
LOGGER.error("Error during mod initialisation: ", e);
|
|
||||||
throw new RuntimeException(e);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
LOGGER.info("Loaded " + modsFromProvider.size() + " mods from provider: " + plugin.id());
|
|
||||||
mods.put(plugin.id(), modsFromProvider);
|
mods.put(plugin.id(), modsFromProvider);
|
||||||
modIds.addAll(modsFromProvider.keySet());
|
modIds.addAll(modsFromProvider.keySet());
|
||||||
modProviders.add(plugin);
|
modProviders.add(plugin);
|
||||||
} catch (Throwable e) {
|
} catch (Throwable e) {
|
||||||
LOGGER.error("Error during plugin initialisation: ", e);
|
LOGGER.error("Error during plugin initialisation: ", e);
|
||||||
throw new RuntimeException(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() {
|
private void discoverGamePlugins() {
|
||||||
LOGGER.info("Discovering game plugins...");
|
LOGGER.info("Discovering game plugins...");
|
||||||
ServiceLoader<FrogGamePlugin> loader = ServiceLoader.load(FrogGamePlugin.class);
|
ServiceLoader<FrogGamePlugin> loader = ServiceLoader.load(FrogGamePlugin.class);
|
||||||
loader.stream().map(ServiceLoader.Provider::get).forEach(p -> LOGGER.info("Found game plugin: " + p.getClass().getName()));
|
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);
|
FrogGamePlugin[] applicablePlugins = ServiceLoader.load(FrogGamePlugin.class).stream().map(ServiceLoader.Provider::get).filter(FrogGamePlugin::isApplicable).toArray(FrogGamePlugin[]::new);
|
||||||
if (applicablePlugins.length > 1) {
|
if (applicablePlugins.length > 1) {
|
||||||
throw new IllegalStateException("Multiple applicable game plugins found!");
|
throw new IllegalStateException("Multiple applicable game plugins found!");
|
||||||
|
@ -155,36 +164,23 @@ public class FrogLoaderImpl implements FrogLoader {
|
||||||
throw new IllegalStateException("No applicable game plugin found!");
|
throw new IllegalStateException("No applicable game plugin found!");
|
||||||
}
|
}
|
||||||
|
|
||||||
for (FrogGamePlugin plugin : applicablePlugins) {
|
FrogGamePlugin plugin = applicablePlugins[0]; // we can skip the loop as we always will only have one element
|
||||||
try {
|
try {
|
||||||
plugin.init(this);
|
plugin.init(this);
|
||||||
gamePlugins.add(plugin);
|
gameVersion = plugin.queryVersion();
|
||||||
} catch (Throwable e) {
|
ModProperties gameMod = plugin.getGameMod();
|
||||||
LOGGER.error("Error during plugin initialisation: ", e);
|
if (gameMod != null) {
|
||||||
throw new RuntimeException(e);
|
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);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/*private void discoverPlugins() {
|
|
||||||
ServiceLoader.load(FrogPlugin.class).forEach(plugin -> {
|
|
||||||
try {
|
|
||||||
if (plugin.isApplicable()) {
|
|
||||||
plugin.init(this);
|
|
||||||
plugins.add(plugin);
|
|
||||||
}
|
|
||||||
} catch (Throwable e) {
|
|
||||||
LOGGER.error("Error during plugin initialisation: ", e);
|
|
||||||
throw new RuntimeException(e);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
if (plugins.isEmpty()) {
|
|
||||||
throw new IllegalStateException("No plugin applicable to the current state was found!");
|
|
||||||
}
|
|
||||||
}*/
|
|
||||||
|
|
||||||
public String getArgument(String name) {
|
public String getArgument(String name) {
|
||||||
for (int i = 0; i < args.length - 1; i += 2) {
|
for (int i = 0; i < args.length - 1; i += 2) {
|
||||||
if (args[i].equals("--" + name)) {
|
if (args[i].equals("--" + name)) {
|
||||||
|
@ -217,10 +213,6 @@ public class FrogLoaderImpl implements FrogLoader {
|
||||||
return mods.values().stream().flatMap(m -> m.values().stream()).filter(m -> m.id().equals(id)).findFirst();
|
return mods.values().stream().flatMap(m -> m.values().stream()).filter(m -> m.id().equals(id)).findFirst();
|
||||||
}
|
}
|
||||||
|
|
||||||
/*private Map<String, ModProperties> collectMods() {
|
|
||||||
return plugins.stream().map(FrogPlugin::getMods).flatMap(Collection::stream).collect(Collectors.toMap(ModProperties::id, m -> m));
|
|
||||||
}*/
|
|
||||||
|
|
||||||
private Collection<String> collectModIds() {
|
private Collection<String> collectModIds() {
|
||||||
return mods.values().stream().flatMap(m -> m.keySet().stream()).collect(Collectors.toSet());
|
return mods.values().stream().flatMap(m -> m.keySet().stream()).collect(Collectors.toSet());
|
||||||
}
|
}
|
||||||
|
|
|
@ -60,7 +60,7 @@ public class ModUtil {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
children.computeIfAbsent(mod, m -> new HashSet<>());
|
children.putIfAbsent(mod, Collections.emptySet());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -6,11 +6,17 @@ import java.lang.invoke.MethodHandle;
|
||||||
import java.lang.invoke.MethodHandles;
|
import java.lang.invoke.MethodHandles;
|
||||||
import java.lang.invoke.MethodType;
|
import java.lang.invoke.MethodType;
|
||||||
import java.nio.file.*;
|
import java.nio.file.*;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
import com.google.gson.JsonObject;
|
import com.google.gson.JsonObject;
|
||||||
import dev.frogmc.frogloader.api.FrogLoader;
|
import dev.frogmc.frogloader.api.FrogLoader;
|
||||||
|
import dev.frogmc.frogloader.api.mod.ModDependencies;
|
||||||
|
import dev.frogmc.frogloader.api.mod.ModExtensions;
|
||||||
|
import dev.frogmc.frogloader.api.mod.ModProperties;
|
||||||
import dev.frogmc.frogloader.api.plugin.FrogGamePlugin;
|
import dev.frogmc.frogloader.api.plugin.FrogGamePlugin;
|
||||||
import dev.frogmc.frogloader.impl.FrogLoaderImpl;
|
import dev.frogmc.frogloader.impl.FrogLoaderImpl;
|
||||||
|
import dev.frogmc.frogloader.impl.mod.ModPropertiesImpl;
|
||||||
import dev.frogmc.frogloader.impl.util.SystemProperties;
|
import dev.frogmc.frogloader.impl.util.SystemProperties;
|
||||||
import dev.frogmc.thyroxine.Thyroxine;
|
import dev.frogmc.thyroxine.Thyroxine;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
|
@ -118,4 +124,17 @@ public class MinecraftGamePlugin implements FrogGamePlugin {
|
||||||
var runtimePath = loader.isDevelopment() ? gamePath : remappedGamePath;
|
var runtimePath = loader.isDevelopment() ? gamePath : remappedGamePath;
|
||||||
FrogLoaderImpl.getInstance().getClassloader().addURL(runtimePath.toUri().toURL());
|
FrogLoaderImpl.getInstance().getClassloader().addURL(runtimePath.toUri().toURL());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String queryVersion() {
|
||||||
|
return version;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ModProperties getGameMod() {
|
||||||
|
return new ModPropertiesImpl("minecraft", "Minecraft", "/assets/minecraft/textures/block/grass_block_side.png",
|
||||||
|
MinecraftSemVerImpl.get(version), "MC-EULA",
|
||||||
|
Map.of("Mojang AB", Collections.singleton("Author")),
|
||||||
|
new ModDependencies(Collections.emptySet(), Collections.emptySet(), Collections.emptySet(), Collections.emptySet()),
|
||||||
|
ModExtensions.of(Collections.emptyMap()), Collections.emptySet());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,21 +1,28 @@
|
||||||
package dev.frogmc.frogloader.impl.plugin.mod;
|
package dev.frogmc.frogloader.impl.plugin.mod;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.nio.file.FileSystem;
|
import java.net.MalformedURLException;
|
||||||
import java.nio.file.FileSystems;
|
import java.nio.file.*;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.attribute.BasicFileAttributes;
|
||||||
import java.util.Collection;
|
import java.util.*;
|
||||||
|
|
||||||
|
import com.electronwill.nightconfig.core.UnmodifiableConfig;
|
||||||
|
import dev.frogmc.frogloader.api.FrogLoader;
|
||||||
import dev.frogmc.frogloader.api.extensions.PreLaunchExtension;
|
import dev.frogmc.frogloader.api.extensions.PreLaunchExtension;
|
||||||
import dev.frogmc.frogloader.api.mod.ModProperties;
|
import dev.frogmc.frogloader.api.mod.ModProperties;
|
||||||
import dev.frogmc.frogloader.api.plugin.FrogModProvider;
|
import dev.frogmc.frogloader.api.plugin.FrogModProvider;
|
||||||
|
import dev.frogmc.frogloader.impl.FrogLoaderImpl;
|
||||||
|
import dev.frogmc.frogloader.impl.mod.BuiltinExtensions;
|
||||||
import dev.frogmc.frogloader.impl.mod.ModPropertiesReader;
|
import dev.frogmc.frogloader.impl.mod.ModPropertiesReader;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
@SuppressWarnings("unused")
|
@SuppressWarnings("unused")
|
||||||
public class FrogmodModProvider implements FrogModProvider {
|
public class FrogmodModProvider implements FrogModProvider {
|
||||||
|
|
||||||
Logger LOGGER = org.slf4j.LoggerFactory.getLogger("FrogModProvider");
|
public static final String MOD_FILE_EXTENSION = ".frogmod";
|
||||||
|
|
||||||
|
Logger LOGGER = LoggerFactory.getLogger("FrogModProvider");
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String id() {
|
public String id() {
|
||||||
|
@ -29,12 +36,11 @@ public class FrogmodModProvider implements FrogModProvider {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isFileApplicable(Path path) {
|
public boolean isFileApplicable(Path path) {
|
||||||
if (!path.toString().endsWith(".frogmod")) {
|
if (!path.toString().endsWith(MOD_FILE_EXTENSION)) {
|
||||||
LOGGER.info("File {} is not a frogmod file", path);
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
try (FileSystem fs = FileSystems.newFileSystem(path)) {
|
try (FileSystem fs = FileSystems.newFileSystem(path)) {
|
||||||
return fs.getPath("frog.mod.toml").toFile().exists();
|
return Files.exists(fs.getPath("frog.mod.toml"));
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
LOGGER.error("Error while checking file {}", path, e);
|
LOGGER.error("Error while checking file {}", path, e);
|
||||||
return false;
|
return false;
|
||||||
|
@ -42,20 +48,93 @@ public class FrogmodModProvider implements FrogModProvider {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ModProperties loadMod(Path path) {
|
public boolean isDirectoryApplicable(Path path) {
|
||||||
try (FileSystem fs = FileSystems.newFileSystem(path)) {
|
return path.getFileName().toString().equals(FrogLoader.getInstance().getGameVersion());
|
||||||
ModProperties prop = ModPropertiesReader.readFile(fs.getPath("frog.mod.toml").toUri().toURL()).orElseThrow(IOException::new);
|
}
|
||||||
// prop.extensions().runIfPresent(PreLaunchExtension.ID, PreLaunchExtension.class, PreLaunchExtension::onPreLaunch);
|
|
||||||
return prop;
|
@Override
|
||||||
} catch (Exception e) {
|
public Collection<ModProperties> loadMods(Collection<Path> paths) throws Exception {
|
||||||
throw new RuntimeException(e);
|
Path jijCache = getJijCacheDir();
|
||||||
|
|
||||||
|
Map<Path, ModProperties> mods = new HashMap<>();
|
||||||
|
Collection<Path> set = new HashSet<>(paths);
|
||||||
|
Collection<ModProperties> loadedMods = new HashSet<>();
|
||||||
|
|
||||||
|
this.getClass().getClassLoader().resources(ModPropertiesReader.PROPERTIES_FILE_NAME).map(ModPropertiesReader::readFile)
|
||||||
|
.map(o -> o.orElse(null)).filter(Objects::nonNull).forEach(loadedMods::add);
|
||||||
|
|
||||||
|
for (Path p : paths) {
|
||||||
|
findJiJMods(p, set, mods, jijCache);
|
||||||
|
}
|
||||||
|
|
||||||
|
set.stream().filter(mods::containsKey).map(path -> {
|
||||||
|
try {
|
||||||
|
return path.toUri().toURL();
|
||||||
|
} catch (MalformedURLException e) {
|
||||||
|
LOGGER.warn("Failed to resolve url for {}", path, e);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}).filter(Objects::nonNull).forEach(FrogLoaderImpl.getInstance().getClassloader()::addURL);
|
||||||
|
|
||||||
|
loadedMods.addAll(mods.values());
|
||||||
|
|
||||||
|
return loadedMods;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Path getJijCacheDir(){
|
||||||
|
Path dir = FrogLoader.getInstance().getGameDir().resolve(".frogmc").resolve("jijcache");
|
||||||
|
|
||||||
|
Runtime.getRuntime().addShutdownHook(new Thread(() -> {
|
||||||
|
try {
|
||||||
|
Files.walkFileTree(dir, new SimpleFileVisitor<>(){
|
||||||
|
@Override
|
||||||
|
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
|
||||||
|
Files.delete(file);
|
||||||
|
return super.visitFile(file, attrs);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException {
|
||||||
|
Files.delete(dir);
|
||||||
|
return super.postVisitDirectory(dir, exc);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} catch (IOException e) {
|
||||||
|
LOGGER.error("Failed to clear extracted jij mods!", e);
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
|
||||||
|
return dir;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void findJiJMods(Path mod, Collection<Path> mods, Map<Path, ModProperties> modPaths, Path jijCache) throws IOException {
|
||||||
|
Optional<ModProperties> opt = ModPropertiesReader.read(mod);
|
||||||
|
if (opt.isPresent()) {
|
||||||
|
ModProperties p = opt.get();
|
||||||
|
modPaths.put(mod, p);
|
||||||
|
List<List<UnmodifiableConfig>> entries = p.extensions().getOrDefault(BuiltinExtensions.INCLUDED_JARS, Collections.emptyList());
|
||||||
|
if (entries.isEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
try (FileSystem fs = FileSystems.newFileSystem(mod)) {
|
||||||
|
for (List<UnmodifiableConfig> jars : entries) {
|
||||||
|
for (UnmodifiableConfig jar : jars) {
|
||||||
|
Path path = fs.getPath(jar.get("path")).toAbsolutePath();
|
||||||
|
Path extracted = jijCache.resolve((String) jar.get("id"));
|
||||||
|
if (!Files.exists(extracted)){
|
||||||
|
Files.createDirectories(jijCache);
|
||||||
|
Files.copy(path, extracted);
|
||||||
|
}
|
||||||
|
mods.add(extracted);
|
||||||
|
findJiJMods(extracted, mods, modPaths, jijCache);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void initMods(Collection<ModProperties> mods) {
|
public void preLaunch(Collection<ModProperties> mods) {
|
||||||
mods.forEach(mod -> {
|
mods.forEach(mod -> mod.extensions().runIfPresent(PreLaunchExtension.ID, PreLaunchExtension.class, PreLaunchExtension::onPreLaunch));
|
||||||
mod.extensions().runIfPresent(PreLaunchExtension.ID, PreLaunchExtension.class, PreLaunchExtension::onPreLaunch);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,26 @@
|
||||||
|
package dev.frogmc.frogloader.impl.plugin.mod;
|
||||||
|
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.Collections;
|
||||||
|
|
||||||
|
import dev.frogmc.frogloader.api.mod.ModProperties;
|
||||||
|
import dev.frogmc.frogloader.api.plugin.FrogModProvider;
|
||||||
|
import dev.frogmc.frogloader.impl.mod.JavaModProperties;
|
||||||
|
|
||||||
|
public class JavaModProvider implements FrogModProvider {
|
||||||
|
@Override
|
||||||
|
public String id() {
|
||||||
|
return "frogloader:integrated/java";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isApplicable() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Collection<ModProperties> loadMods(Collection<Path> modFiles) throws Exception {
|
||||||
|
return Collections.singleton(JavaModProperties.get());
|
||||||
|
}
|
||||||
|
}
|
|
@ -1 +1,2 @@
|
||||||
dev.frogmc.frogloader.impl.plugin.mod.FrogmodModProvider
|
dev.frogmc.frogloader.impl.plugin.mod.JavaModProvider
|
||||||
|
dev.frogmc.frogloader.impl.plugin.mod.FrogmodModProvider
|
||||||
|
|
Loading…
Reference in a new issue