Compare commits

..

No commits in common. "e9c8ecaaac15318a8790998fa8c9fd71ceb697d1" and "e352bb835fa3569c8b470e9e2d446dd1d980c0c2" have entirely different histories.

22 changed files with 488 additions and 624 deletions

View file

@ -30,11 +30,11 @@ public interface FrogLoader {
}
/**
* Get the currently loaded game plugin.
* Get all loaded game plugins.
*
* @return The game plugin applicable to the current game
* @return A collection of all loaded game plugins
*/
FrogGamePlugin getGamePlugin();
Collection<FrogGamePlugin> getGamePlugins();
/**
* Get all loaded mod providers.
@ -43,6 +43,8 @@ public interface FrogLoader {
*/
Collection<FrogModProvider> getModProviders();
//Collection<FrogPlugin> getPlugins();
/**
* Get the current (physical) environment.
*
@ -68,11 +70,11 @@ public interface FrogLoader {
Path getConfigDir();
/**
* Get the current mods directories.
* Get the current mods directory.
*
* @return The current mods directories
* @return The current mods directory
*/
Collection<Path> getModsDirs();
Path getModsDir();
/**
* Query whether this loader is currently running in a development environment.
@ -106,10 +108,4 @@ public interface FrogLoader {
* @see FrogPlugin
*/
Collection<ModProperties> getMods();
/**
* Get the version of the currently loaded game
* @return The current game version
*/
String getGameVersion();
}

View file

@ -3,8 +3,8 @@ package dev.frogmc.frogloader.api.mod;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
import java.util.Collection;
import java.util.Map;
import java.util.Collection;
import java.util.function.Consumer;
import org.slf4j.Logger;
@ -70,13 +70,10 @@ public final class ModExtensions {
* @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
*/
@SuppressWarnings({"rawtypes", "unchecked"})
public <T> void runIfPresent(String key, Consumer<T> action) {
Object value = get(key);
if (value instanceof Collection c){
((Collection<T>)c).forEach(action);
} else if (value != null) {
action.accept((T)value);
T value = get(key);
if (value != null) {
action.accept(value);
}
}
@ -119,9 +116,9 @@ public final class ModExtensions {
}
};
if (value instanceof String s) {
if (value instanceof String s){
c.accept(s);
} else if (value instanceof Collection l) {
} else if (value instanceof Collection l){
((Collection<String>) l).forEach(c);
}
}

View file

@ -79,7 +79,6 @@ public interface ModProperties {
/**
* Get this mod's paths
*
* @return Where this mod is loaded from
*/
Collection<Path> paths();

View file

@ -1,7 +1,6 @@
package dev.frogmc.frogloader.api.plugin;
import dev.frogmc.frogloader.api.FrogLoader;
import dev.frogmc.frogloader.api.mod.ModProperties;
public interface FrogGamePlugin {
default void run() {
@ -13,10 +12,4 @@ public interface FrogGamePlugin {
default void init(FrogLoader loader) throws Exception {
}
String queryVersion();
default ModProperties getGameMod() {
return null;
}
}

View file

@ -1,10 +1,9 @@
package dev.frogmc.frogloader.api.plugin;
import dev.frogmc.frogloader.api.mod.ModProperties;
import java.nio.file.Path;
import java.util.Collection;
import java.util.Collections;
import dev.frogmc.frogloader.api.mod.ModProperties;
public interface FrogModProvider {
String id();
@ -21,14 +20,9 @@ public interface FrogModProvider {
return false;
}
default boolean isDirectoryApplicable(Path path) {
return false;
}
default void initMods(Collection<ModProperties> mods) {};
default void preLaunch(Collection<ModProperties> mods) {
}
default Collection<ModProperties> loadMods(Collection<Path> modFiles) throws Exception {
return Collections.emptySet();
default ModProperties loadMod(Path path) {
return null;
}
}

View file

@ -2,6 +2,7 @@ package dev.frogmc.frogloader.api.plugin;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import dev.frogmc.frogloader.api.FrogLoader;
import dev.frogmc.frogloader.api.mod.ModProperties;

View file

@ -1,5 +1,22 @@
package dev.frogmc.frogloader.impl;
import com.google.gson.Gson;
import dev.frogmc.frogloader.api.FrogLoader;
import dev.frogmc.frogloader.api.env.Env;
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.api.plugin.FrogPlugin;
import dev.frogmc.frogloader.impl.gui.LoaderGui;
import dev.frogmc.frogloader.impl.launch.MixinClassLoader;
import dev.frogmc.frogloader.impl.mod.ModUtil;
import dev.frogmc.frogloader.impl.util.CrashReportGenerator;
import dev.frogmc.frogloader.impl.util.SystemProperties;
import lombok.Getter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.spongepowered.asm.mixin.MixinEnvironment;
import java.io.IOException;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
@ -10,27 +27,8 @@ import java.nio.file.Paths;
import java.util.*;
import java.util.stream.Collectors;
import com.google.gson.Gson;
import dev.frogmc.frogloader.api.FrogLoader;
import dev.frogmc.frogloader.api.env.Env;
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.gui.LoaderGui;
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.util.CrashReportGenerator;
import dev.frogmc.frogloader.impl.util.SystemProperties;
import lombok.Getter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.spongepowered.asm.mixin.MixinEnvironment;
import org.spongepowered.asm.mixin.Mixins;
public class FrogLoaderImpl implements FrogLoader {
public static final String MOD_FILE_EXTENSION = ".frogmod";
private static final boolean DEV_ENV = Boolean.getBoolean(SystemProperties.DEVELOPMENT);
@Getter
private static FrogLoaderImpl instance;
@ -40,16 +38,17 @@ public class FrogLoaderImpl implements FrogLoader {
private final Env env;
private final Logger LOGGER = LoggerFactory.getLogger("FrogLoader");
// @Getter
// private final List<FrogPlugin> plugins = new ArrayList<>();
@Getter
private FrogGamePlugin gamePlugin;
private final Collection<FrogGamePlugin> gamePlugins = new ArrayList<>();
@Getter
private final Collection<FrogModProvider> modProviders = new ArrayList<>();
@Getter
private final Path gameDir, configDir;
@Getter
private final Collection<Path> modsDirs = new HashSet<>();
private final Path gameDir, configDir, modsDir;
@Getter
private final MixinClassLoader classloader;
@ -57,10 +56,9 @@ public class FrogLoaderImpl implements FrogLoader {
@Getter
private final Gson gson = new Gson();
@Getter
private String gameVersion;
private final Map<String, Map<String, ModProperties>> mods = new HashMap<>();
// Map<Provider ID, Map<Mod ID, ModProperties>>
private Map<String, Map<String, ModProperties>> mods = new HashMap<>();
// private Map<String, ModProperties> mods;
private Collection<String> modIds = new ArrayList<>();
@ -72,10 +70,12 @@ public class FrogLoaderImpl implements FrogLoader {
gameDir = Paths.get(getArgumentOrElse("gameDir", "."));
configDir = gameDir.resolve("config");
modsDir = gameDir.resolve("mods");
try {
Files.createDirectories(gameDir);
Files.createDirectories(configDir);
Files.createDirectories(modsDir);
} catch (IOException e) {
LOGGER.warn("Failed to create essential directories ", e);
}
@ -83,13 +83,12 @@ public class FrogLoaderImpl implements FrogLoader {
try {
discoverGamePlugins();
discoverModProviders();
modProviders.stream().map(FrogModProvider::loadDirectory).map(gameDir::resolve).forEach(modsDirs::add);
advanceMixinState();
modIds = collectModIds();
LOGGER.info(ModUtil.getModList(getMods()));
LOGGER.info("Launching...");
modProviders.forEach(plugin -> plugin.preLaunch(mods.get(plugin.id()).values()));
gamePlugin.run();
gamePlugins.forEach(FrogGamePlugin::run);
modProviders.forEach(m -> m.initMods(mods.get(m.id()).values()));
} catch (Throwable t) {
LoaderGui.execReport(CrashReportGenerator.writeReport(t, getMods()), false);
}
@ -116,47 +115,40 @@ public class FrogLoaderImpl implements FrogLoader {
private void discoverModProviders() {
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 : ServiceLoader.load(FrogModProvider.class)) {
LOGGER.debug("Found mod provider: {}", plugin.getClass().getName());
if (!plugin.isApplicable()) {
continue;
}
for (FrogModProvider plugin : applicableProviders) {
try {
LOGGER.debug("Initialising mod provider: {}", plugin.id());
LOGGER.info("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());
Collection<Path> paths = Discovery.find(gameDir.resolve(plugin.loadDirectory()), p -> false, plugin::isFileApplicable);
paths.forEach(p -> {
LOGGER.info("Loading mod: " + p);
try {
ModProperties mod = plugin.loadMod(p);
modsFromProvider.put(mod.id(), mod);
} catch (Throwable e) {
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);
modIds.addAll(modsFromProvider.keySet());
modProviders.add(plugin);
} catch (Throwable 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() {
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()));
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!");
@ -164,22 +156,35 @@ public class FrogLoaderImpl implements FrogLoader {
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
for (FrogGamePlugin plugin : applicablePlugins) {
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;
gamePlugins.add(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) {
for (int i = 0; i < args.length - 1; i += 2) {
@ -213,6 +218,10 @@ public class FrogLoaderImpl implements FrogLoader {
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() {
return mods.values().stream().flatMap(m -> m.keySet().stream()).collect(Collectors.toSet());
}

View file

@ -76,7 +76,7 @@ public class LoaderGui extends JFrame {
public static void execUnfulfilledDep(Path reportPath, ModDependencyResolver.UnfulfilledDependencyException ex, boolean keepRunning) {
exec(gui -> {
int count = ex.getDependencies().size();
gui.setHeader("Found " + count + " problem" + (count > 1 ? "s" : ""));
gui.setHeader("Found " + count + " problem"+(count > 1 ? "s" : ""));
gui.addTab("Info", new UnfulfilledDepPage(ex));
addReport(gui, reportPath);
}, keepRunning);
@ -85,7 +85,7 @@ public class LoaderGui extends JFrame {
public static void execBreakingDep(Path reportPath, ModDependencyResolver.BreakingModException ex, boolean keepRunning) {
exec(gui -> {
int count = ex.getBreaks().size();
gui.setHeader("Found " + count + " problem" + (count > 1 ? "s" : ""));
gui.setHeader("Found " + count + " problem"+(count > 1 ? "s" : ""));
gui.addTab("Info", new BreakingDepPage(ex));
addReport(gui, reportPath);
}, keepRunning);

View file

@ -1,14 +1,14 @@
package dev.frogmc.frogloader.impl.gui.component;
import dev.frogmc.frogloader.impl.mod.ModDependencyResolver;
import org.jetbrains.annotations.Nullable;
import javax.swing.*;
import javax.swing.plaf.basic.BasicBorders;
import java.awt.*;
import java.awt.event.ActionListener;
import java.net.URL;
import dev.frogmc.frogloader.impl.mod.ModDependencyResolver;
import org.jetbrains.annotations.Nullable;
public class DependencyErrorEntry extends JPanel {
private final JPanel actions;

View file

@ -1,12 +1,12 @@
package dev.frogmc.frogloader.impl.gui.page;
import dev.frogmc.frogloader.impl.gui.component.DependencyErrorEntry;
import dev.frogmc.frogloader.impl.mod.ModDependencyResolver;
import javax.swing.*;
import java.awt.*;
import java.util.Objects;
import dev.frogmc.frogloader.impl.gui.component.DependencyErrorEntry;
import dev.frogmc.frogloader.impl.mod.ModDependencyResolver;
public class BreakingDepPage extends JScrollPane {
public BreakingDepPage(ModDependencyResolver.BreakingModException ex) {

View file

@ -1,15 +1,15 @@
package dev.frogmc.frogloader.impl.gui.page;
import dev.frogmc.frogloader.impl.gui.component.DependencyErrorEntry;
import dev.frogmc.frogloader.impl.mod.ModDependencyResolver;
import dev.frogmc.frogloader.impl.util.PlatformUtil;
import javax.swing.*;
import java.awt.*;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.Objects;
import dev.frogmc.frogloader.impl.gui.component.DependencyErrorEntry;
import dev.frogmc.frogloader.impl.mod.ModDependencyResolver;
import dev.frogmc.frogloader.impl.util.PlatformUtil;
public class UnfulfilledDepPage extends JScrollPane {
public UnfulfilledDepPage(ModDependencyResolver.UnfulfilledDependencyException ex) {

View file

@ -58,15 +58,15 @@ public class FrogLauncher {
new FrogLauncher(args, env);
}
public void putProperty(IPropertyKey key, Object value) {
public void putProperty(IPropertyKey key, Object value){
globalProperties.put(key, value);
}
public Object getProperty(IPropertyKey key) {
public Object getProperty(IPropertyKey key){
return globalProperties.get(key);
}
public Object getProperty(IPropertyKey key, Object defaultValue) {
public Object getProperty(IPropertyKey key, Object defaultValue){
return globalProperties.getOrDefault(key, defaultValue);
}
}

View file

@ -14,15 +14,15 @@ public class FrogGlobalPropertyService implements IGlobalPropertyService {
}
@Override
public boolean equals(Object other) {
if (other instanceof IPropertyKey k) {
public boolean equals(Object other){
if (other instanceof IPropertyKey k){
return name.equals(k.toString());
}
return false;
}
@Override
public int hashCode() {
public int hashCode(){
return name.hashCode();
}
};

View file

@ -6,8 +6,7 @@ import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import com.google.common.collect.Multimap;
import com.google.common.collect.MultimapBuilder;
import com.google.common.collect.*;
import dev.frogmc.frogloader.api.mod.ModDependencies;
import dev.frogmc.frogloader.api.mod.ModProperties;
import dev.frogmc.frogloader.api.mod.SemVer;

View file

@ -49,8 +49,8 @@ public class ModPropertiesReader {
CommentedConfig props = PARSER.parse(in);
String url = in.toString();
Path source = Path.of(url.substring(url.lastIndexOf(":") + 1).split("!")[0]).toAbsolutePath();
if (!source.getFileName().toString().endsWith(".jar")) {
Path source = Path.of(url.substring(url.lastIndexOf(":")+1).split("!")[0]).toAbsolutePath();
if (!source.getFileName().toString().endsWith(".jar")){
source = source.getParent();
} else {
// TODO will this result in a memory leak?

View file

@ -60,7 +60,7 @@ public class ModUtil {
}
}
} else {
children.putIfAbsent(mod, Collections.emptySet());
children.computeIfAbsent(mod, m -> new HashSet<>());
}
}

View file

@ -1,26 +1,20 @@
package dev.frogmc.frogloader.impl.plugin.game.minecraft;
import com.google.gson.JsonObject;
import dev.frogmc.frogloader.api.FrogLoader;
import dev.frogmc.frogloader.api.plugin.FrogGamePlugin;
import dev.frogmc.frogloader.impl.FrogLoaderImpl;
import dev.frogmc.frogloader.impl.util.SystemProperties;
import dev.frogmc.thyroxine.Thyroxine;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.File;
import java.io.IOException;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
import java.nio.file.*;
import java.util.Collections;
import java.util.Map;
import com.google.gson.JsonObject;
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.impl.FrogLoaderImpl;
import dev.frogmc.frogloader.impl.mod.ModPropertiesImpl;
import dev.frogmc.frogloader.impl.util.SystemProperties;
import dev.frogmc.thyroxine.Thyroxine;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class MinecraftGamePlugin implements FrogGamePlugin {
@ -45,7 +39,7 @@ public class MinecraftGamePlugin implements FrogGamePlugin {
foundMainClass = n.substring(0, n.length() - 6).replace("/", ".");
try {
version = FrogLoaderImpl.getInstance().getGson().fromJson(Files.readString(fs.getPath("version.json")), JsonObject.class).get("id").getAsString();
} catch (Exception e) {
} catch (Exception e){
version = FrogLoaderImpl.getInstance().getArgument("version");
}
return true;
@ -124,17 +118,4 @@ public class MinecraftGamePlugin implements FrogGamePlugin {
var runtimePath = loader.isDevelopment() ? gamePath : remappedGamePath;
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());
}
}

View file

@ -1,28 +1,22 @@
package dev.frogmc.frogloader.impl.plugin.mod;
import java.io.IOException;
import java.net.MalformedURLException;
import java.nio.file.*;
import java.nio.file.attribute.BasicFileAttributes;
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.mod.ModProperties;
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 org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.nio.file.FileSystem;
import java.nio.file.FileSystems;
import java.nio.file.Path;
import java.util.Collection;
@SuppressWarnings("unused")
public class FrogmodModProvider implements FrogModProvider {
public static final String MOD_FILE_EXTENSION = ".frogmod";
Logger LOGGER = LoggerFactory.getLogger("FrogModProvider");
Logger LOGGER = org.slf4j.LoggerFactory.getLogger("FrogModProvider");
@Override
public String id() {
@ -36,11 +30,12 @@ public class FrogmodModProvider implements FrogModProvider {
@Override
public boolean isFileApplicable(Path path) {
if (!path.toString().endsWith(MOD_FILE_EXTENSION)) {
if (!path.toString().endsWith(".frogmod")) {
LOGGER.info("File {} is not a frogmod file", path.toString());
return false;
}
try (FileSystem fs = FileSystems.newFileSystem(path)) {
return Files.exists(fs.getPath("frog.mod.toml"));
return fs.getPath("frog.mod.toml").toFile().exists();
} catch (Exception e) {
LOGGER.error("Error while checking file {}", path, e);
return false;
@ -48,93 +43,20 @@ public class FrogmodModProvider implements FrogModProvider {
}
@Override
public boolean isDirectoryApplicable(Path path) {
return path.getFileName().toString().equals(FrogLoader.getInstance().getGameVersion());
public ModProperties loadMod(Path path) {
try (FileSystem fs = FileSystems.newFileSystem(path)) {
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;
} catch (Exception e) {
throw new RuntimeException(e);
}
}
@Override
public Collection<ModProperties> loadMods(Collection<Path> paths) throws Exception {
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);
}
public void initMods(Collection<ModProperties> mods) {
mods.forEach(mod -> {
mod.extensions().runIfPresent(PreLaunchExtension.ID, PreLaunchExtension.class, PreLaunchExtension::onPreLaunch);
});
} 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
public void preLaunch(Collection<ModProperties> mods) {
mods.forEach(mod -> mod.extensions().runIfPresent(PreLaunchExtension.ID, PreLaunchExtension.class, PreLaunchExtension::onPreLaunch));
}
}

View file

@ -1,26 +0,0 @@
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());
}
}

View file

@ -1,2 +1 @@
dev.frogmc.frogloader.impl.plugin.mod.JavaModProvider
dev.frogmc.frogloader.impl.plugin.mod.FrogmodModProvider