Implement more caching where the profiler showed bottlenecks
This commit is contained in:
parent
86e0f00253
commit
d59e0ef0df
|
@ -9,10 +9,10 @@ import io.gitlab.jfronny.inceptum.frontend.cli.CommandArgs;
|
|||
import io.gitlab.jfronny.inceptum.model.inceptum.InstanceMeta;
|
||||
import io.gitlab.jfronny.inceptum.util.ConfigHolder;
|
||||
import io.gitlab.jfronny.inceptum.util.MetaHolder;
|
||||
import io.gitlab.jfronny.inceptum.util.mds.ModsDirScanner;
|
||||
import io.gitlab.jfronny.inceptum.util.Utils;
|
||||
import io.gitlab.jfronny.inceptum.util.api.account.AccountManager;
|
||||
import io.gitlab.jfronny.inceptum.frontend.gui.window.Window;
|
||||
import io.gitlab.jfronny.inceptum.util.mds.ModsDirScanner;
|
||||
import org.lwjgl.glfw.Callbacks;
|
||||
import org.lwjgl.glfw.GLFW;
|
||||
import org.lwjgl.glfw.GLFWErrorCallback;
|
||||
|
|
|
@ -48,8 +48,13 @@ public abstract class BaseInstanceCommand extends Command {
|
|||
Utils.LOGGER.error("Could not read instance metadata", e);
|
||||
return;
|
||||
}
|
||||
invoke(args.subArgs(), instancePath, meta);
|
||||
try {
|
||||
invoke(args.subArgs(), instancePath, meta);
|
||||
} catch (Exception e) {
|
||||
Utils.LOGGER.error("Could not execute command", e);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
protected abstract void invoke(CommandArgs args, Path instancePath, InstanceMeta meta);
|
||||
protected abstract void invoke(CommandArgs args, Path instancePath, InstanceMeta meta) throws Exception;
|
||||
}
|
||||
|
|
|
@ -31,7 +31,7 @@ public abstract class Command {
|
|||
return false;
|
||||
}
|
||||
|
||||
protected abstract void invoke(CommandArgs args);
|
||||
protected abstract void invoke(CommandArgs args) throws Exception;
|
||||
|
||||
public CommandResolution resolve(CommandArgs args) {
|
||||
if (args.length != 0) {
|
||||
|
|
|
@ -3,7 +3,7 @@ package io.gitlab.jfronny.inceptum.frontend.cli;
|
|||
import java.util.List;
|
||||
|
||||
public record CommandResolution(Command command, CommandArgs args, List<String> resolvePath) {
|
||||
public void invoke() {
|
||||
public void invoke() throws Exception {
|
||||
command.invoke(args);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -18,7 +18,7 @@ public class BatchCommand extends Command {
|
|||
}
|
||||
|
||||
@Override
|
||||
protected void invoke(CommandArgs args) {
|
||||
protected void invoke(CommandArgs args) throws Exception {
|
||||
if (args.length == 0) {
|
||||
Utils.LOGGER.error("Could not start batch execution: No source file specified");
|
||||
return;
|
||||
|
|
|
@ -46,18 +46,16 @@ public class GitCommand extends Command {
|
|||
}
|
||||
|
||||
@Override
|
||||
protected void invoke(CommandArgs args, Path instancePath, InstanceMeta meta) {
|
||||
protected void invoke(CommandArgs args, Path instancePath, InstanceMeta meta) throws Exception {
|
||||
try (Git git = Git.open(instancePath.toFile())) {
|
||||
Config.GitConfig.GitAuth config = ConfigHolder.CONFIG.git.instanceAuths.get(instancePath.getFileName().toString());
|
||||
invoke(args.subArgs(), instancePath, git, config.username != null && config.password != null
|
||||
? new UsernamePasswordCredentialsProvider(config.username, config.password)
|
||||
: CredentialsProvider.getDefault());
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
protected abstract void invoke(CommandArgs args, Path instancePath, Git git, CredentialsProvider credentials);
|
||||
protected abstract void invoke(CommandArgs args, Path instancePath, Git git, CredentialsProvider credentials) throws Exception;
|
||||
}
|
||||
|
||||
private static class CommitCommand extends BaseGitCommand {
|
||||
|
@ -66,20 +64,16 @@ public class GitCommand extends Command {
|
|||
}
|
||||
|
||||
@Override
|
||||
protected void invoke(CommandArgs args, Path instancePath, Git git, CredentialsProvider credentials) {
|
||||
try {
|
||||
git.commit()
|
||||
.setAll(true)
|
||||
.setMessage(args.length > 1
|
||||
? String.join(" ", args.getArgs())
|
||||
: "Commit at t" + System.currentTimeMillis())
|
||||
.setSign(ConfigHolder.CONFIG.git.signCommits)
|
||||
.setAuthor(ConfigHolder.CONFIG.git.commitUsername, ConfigHolder.CONFIG.git.commitMail)
|
||||
.setCredentialsProvider(credentials)
|
||||
.call();
|
||||
} catch (GitAPIException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
protected void invoke(CommandArgs args, Path instancePath, Git git, CredentialsProvider credentials) throws GitAPIException {
|
||||
git.commit()
|
||||
.setAll(true)
|
||||
.setMessage(args.length > 1
|
||||
? String.join(" ", args.getArgs())
|
||||
: "Commit at t" + System.currentTimeMillis())
|
||||
.setSign(ConfigHolder.CONFIG.git.signCommits)
|
||||
.setAuthor(ConfigHolder.CONFIG.git.commitUsername, ConfigHolder.CONFIG.git.commitMail)
|
||||
.setCredentialsProvider(credentials)
|
||||
.call();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -89,14 +83,10 @@ public class GitCommand extends Command {
|
|||
}
|
||||
|
||||
@Override
|
||||
protected void invoke(CommandArgs args, Path instancePath, Git git, CredentialsProvider credentials) {
|
||||
try {
|
||||
git.push()
|
||||
.setCredentialsProvider(credentials)
|
||||
.call();
|
||||
} catch (GitAPIException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
protected void invoke(CommandArgs args, Path instancePath, Git git, CredentialsProvider credentials) throws GitAPIException {
|
||||
git.push()
|
||||
.setCredentialsProvider(credentials)
|
||||
.call();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -106,15 +96,11 @@ public class GitCommand extends Command {
|
|||
}
|
||||
|
||||
@Override
|
||||
protected void invoke(CommandArgs args, Path instancePath, Git git, CredentialsProvider credentials) {
|
||||
try {
|
||||
git.pull()
|
||||
.setRebase(true)
|
||||
.setCredentialsProvider(credentials)
|
||||
.call();
|
||||
} catch (GitAPIException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
protected void invoke(CommandArgs args, Path instancePath, Git git, CredentialsProvider credentials) throws GitAPIException {
|
||||
git.pull()
|
||||
.setRebase(true)
|
||||
.setCredentialsProvider(credentials)
|
||||
.call();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -124,18 +110,14 @@ public class GitCommand extends Command {
|
|||
}
|
||||
|
||||
@Override
|
||||
protected void invoke(CommandArgs args, Path instancePath, Git git, CredentialsProvider credentials) {
|
||||
try {
|
||||
git.checkout()
|
||||
.setAllPaths(true)
|
||||
.call();
|
||||
git.clean()
|
||||
.setForce(true)
|
||||
.setCleanDirectories(true)
|
||||
.call();
|
||||
} catch (GitAPIException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
protected void invoke(CommandArgs args, Path instancePath, Git git, CredentialsProvider credentials) throws GitAPIException {
|
||||
git.checkout()
|
||||
.setAllPaths(true)
|
||||
.call();
|
||||
git.clean()
|
||||
.setForce(true)
|
||||
.setCleanDirectories(true)
|
||||
.call();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -145,24 +127,14 @@ public class GitCommand extends Command {
|
|||
}
|
||||
|
||||
@Override
|
||||
protected void invoke(CommandArgs args) {
|
||||
protected void invoke(CommandArgs args) throws Exception {
|
||||
if (args.length == 0) {
|
||||
System.err.println("You haven't specified a remote");
|
||||
return;
|
||||
}
|
||||
String name;
|
||||
try {
|
||||
name = args.length > 1 ? args.get(1) : getName(args.get(0));
|
||||
} catch (URISyntaxException e) {
|
||||
e.printStackTrace();
|
||||
return;
|
||||
}
|
||||
String name = args.length > 1 ? args.get(1) : getName(args.get(0));
|
||||
name = name.replaceAll("^\\.*", "");
|
||||
try {
|
||||
clone(args.get(0), name);
|
||||
} catch (GitAPIException | IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
clone(args.get(0), name);
|
||||
}
|
||||
|
||||
public static String getName(String url) throws URISyntaxException {
|
||||
|
|
|
@ -1,16 +1,15 @@
|
|||
package io.gitlab.jfronny.inceptum.frontend.cli.commands;
|
||||
|
||||
import io.gitlab.jfronny.inceptum.Inceptum;
|
||||
import io.gitlab.jfronny.inceptum.frontend.cli.BaseInstanceCommand;
|
||||
import io.gitlab.jfronny.inceptum.frontend.cli.Command;
|
||||
import io.gitlab.jfronny.inceptum.frontend.cli.CommandArgs;
|
||||
import io.gitlab.jfronny.inceptum.util.install.Steps;
|
||||
import io.gitlab.jfronny.inceptum.model.inceptum.InstanceMeta;
|
||||
import io.gitlab.jfronny.inceptum.util.InstanceLauncher;
|
||||
import io.gitlab.jfronny.inceptum.util.InstanceLock;
|
||||
import io.gitlab.jfronny.inceptum.util.MetaHolder;
|
||||
import io.gitlab.jfronny.inceptum.util.Utils;
|
||||
import io.gitlab.jfronny.inceptum.util.api.account.AccountManager;
|
||||
import io.gitlab.jfronny.inceptum.util.InstanceLauncher;
|
||||
import io.gitlab.jfronny.inceptum.util.install.Steps;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
|
@ -44,7 +43,7 @@ public class LaunchCommand extends BaseInstanceCommand {
|
|||
}
|
||||
|
||||
@Override
|
||||
protected void invoke(CommandArgs args, Path instancePath, InstanceMeta meta) {
|
||||
protected void invoke(CommandArgs args, Path instancePath, InstanceMeta meta) throws IOException, InstanceLauncher.LaunchException {
|
||||
if (args.length == 0) {
|
||||
Utils.LOGGER.error("You must provide an instance name or path");
|
||||
return;
|
||||
|
@ -59,21 +58,15 @@ public class LaunchCommand extends BaseInstanceCommand {
|
|||
Utils.LOGGER.error("This instance is still being set up");
|
||||
return;
|
||||
}
|
||||
try {
|
||||
if (InstanceLock.isRunningLocked(instanceDir)) {
|
||||
Utils.LOGGER.error("This instance is already running");
|
||||
return;
|
||||
}
|
||||
} catch (IOException e) {
|
||||
Inceptum.showError("Could not read inceptum lock", e);
|
||||
if (InstanceLock.isRunningLocked(instanceDir)) {
|
||||
Utils.LOGGER.error("This instance is already running");
|
||||
return;
|
||||
}
|
||||
InstanceMeta instance;
|
||||
try {
|
||||
instance = Utils.loadObject(instanceDir.resolve("instance.json"), InstanceMeta.class);
|
||||
} catch (IOException e) {
|
||||
Inceptum.showError("Not a valid instance", e);
|
||||
return;
|
||||
throw new IOException("Not a valid instance", e);
|
||||
}
|
||||
if (args.length > 1) {
|
||||
if (instance.arguments == null) instance.arguments = new InstanceMeta.Arguments();
|
||||
|
@ -83,17 +76,9 @@ public class LaunchCommand extends BaseInstanceCommand {
|
|||
instance.arguments.client.addAll(args.after(0));
|
||||
instance.arguments.server.addAll(args.after(0));
|
||||
}
|
||||
try {
|
||||
Steps.reDownload(instanceDir, Steps.createProcessState());
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
Steps.reDownload(instanceDir, Steps.createProcessState());
|
||||
if (server) {
|
||||
try {
|
||||
InstanceLauncher.launch(instanceDir, instance, InstanceLauncher.LaunchType.Server, restart, AccountManager.NULL_AUTH);
|
||||
} catch (InstanceLauncher.LaunchException | IOException e) {
|
||||
Inceptum.showError("Could not launch instance", e);
|
||||
}
|
||||
InstanceLauncher.launch(instanceDir, instance, InstanceLauncher.LaunchType.Server, restart, AccountManager.NULL_AUTH);
|
||||
}
|
||||
else {
|
||||
AccountManager.loadAccounts();
|
||||
|
|
|
@ -18,37 +18,33 @@ public class ListCommand extends Command {
|
|||
}
|
||||
|
||||
@Override
|
||||
protected void invoke(CommandArgs args) {
|
||||
try {
|
||||
List<Path> paths = Utils.ls(MetaHolder.INSTANCE_DIR);
|
||||
if (paths.isEmpty()) System.out.println("No instances are currently present");
|
||||
for (Path path : paths) {
|
||||
if (!Files.exists(path.resolve("instance.json"))) {
|
||||
System.out.println("- Invalid instance: " + path + " (no instance metadata)");
|
||||
continue;
|
||||
}
|
||||
System.out.println("- \"" + path.getFileName().toString() + "\"");
|
||||
if (InstanceLock.isSetupLocked(path)) {
|
||||
System.out.println(" Status: Setting up");
|
||||
continue;
|
||||
}
|
||||
InstanceMeta instance;
|
||||
try {
|
||||
instance = Utils.loadObject(path.resolve("instance.json"), InstanceMeta.class);
|
||||
} catch (IOException e) {
|
||||
Utils.LOGGER.error(" Could not load instance.json", e);
|
||||
continue;
|
||||
}
|
||||
System.out.println(" Status: " + (InstanceLock.isRunningLocked(path) ? "Running" : "Stopped"));
|
||||
System.out.println(" Version: " + instance.getMinecraftVersion());
|
||||
if (instance.isFabric()) System.out.println(" Fabric Loader: " + instance.getLoaderVersion());
|
||||
if (instance.java != null) System.out.println(" Custom Java: " + instance.java);
|
||||
if (instance.minMem != null || instance.maxMem != null)
|
||||
System.out.println(" Memory:" + (instance.minMem != null ? " Minimum: " + instance.minMem : "")
|
||||
+ (instance.maxMem != null ? " Maximum: " + instance.maxMem : ""));
|
||||
protected void invoke(CommandArgs args) throws IOException {
|
||||
List<Path> paths = Utils.ls(MetaHolder.INSTANCE_DIR);
|
||||
if (paths.isEmpty()) System.out.println("No instances are currently present");
|
||||
for (Path path : paths) {
|
||||
if (!Files.exists(path.resolve("instance.json"))) {
|
||||
System.out.println("- Invalid instance: " + path + " (no instance metadata)");
|
||||
continue;
|
||||
}
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
System.out.println("- \"" + path.getFileName().toString() + "\"");
|
||||
if (InstanceLock.isSetupLocked(path)) {
|
||||
System.out.println(" Status: Setting up");
|
||||
continue;
|
||||
}
|
||||
InstanceMeta instance;
|
||||
try {
|
||||
instance = Utils.loadObject(path.resolve("instance.json"), InstanceMeta.class);
|
||||
} catch (IOException e) {
|
||||
Utils.LOGGER.error(" Could not load instance.json", e);
|
||||
continue;
|
||||
}
|
||||
System.out.println(" Status: " + (InstanceLock.isRunningLocked(path) ? "Running" : "Stopped"));
|
||||
System.out.println(" Version: " + instance.getMinecraftVersion());
|
||||
if (instance.isFabric()) System.out.println(" Fabric Loader: " + instance.getLoaderVersion());
|
||||
if (instance.java != null) System.out.println(" Custom Java: " + instance.java);
|
||||
if (instance.minMem != null || instance.maxMem != null)
|
||||
System.out.println(" Memory:" + (instance.minMem != null ? " Minimum: " + instance.minMem : "")
|
||||
+ (instance.maxMem != null ? " Maximum: " + instance.maxMem : ""));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,12 +6,12 @@ import io.gitlab.jfronny.inceptum.frontend.cli.Command;
|
|||
import io.gitlab.jfronny.inceptum.frontend.cli.CommandArgs;
|
||||
import io.gitlab.jfronny.inceptum.frontend.gui.window.AddModWindow;
|
||||
import io.gitlab.jfronny.inceptum.model.inceptum.InstanceMeta;
|
||||
import io.gitlab.jfronny.inceptum.util.PathUtil;
|
||||
import io.gitlab.jfronny.inceptum.util.mds.IWModDescription;
|
||||
import io.gitlab.jfronny.inceptum.util.source.ModSource;
|
||||
import io.gitlab.jfronny.inceptum.util.ModManager;
|
||||
import io.gitlab.jfronny.inceptum.util.mds.ModsDirScanner;
|
||||
import io.gitlab.jfronny.inceptum.util.PathUtil;
|
||||
import io.gitlab.jfronny.inceptum.util.Utils;
|
||||
import io.gitlab.jfronny.inceptum.util.mds.IWModDescription;
|
||||
import io.gitlab.jfronny.inceptum.util.mds.ModsDirScanner;
|
||||
import io.gitlab.jfronny.inceptum.util.source.ModSource;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
|
@ -48,7 +48,7 @@ public class ModCommand extends Command {
|
|||
}
|
||||
|
||||
@Override
|
||||
protected void invoke(CommandArgs args, Path instancePath, InstanceMeta meta) {
|
||||
protected void invoke(CommandArgs args, Path instancePath, InstanceMeta meta) throws IOException {
|
||||
if (!meta.isFabric()) {
|
||||
System.err.println("This is not a fabric instance");
|
||||
return;
|
||||
|
@ -91,7 +91,7 @@ public class ModCommand extends Command {
|
|||
}
|
||||
|
||||
@Override
|
||||
protected void invoke(CommandArgs args, Path instancePath, InstanceMeta meta) {
|
||||
protected void invoke(CommandArgs args, Path instancePath, InstanceMeta meta) throws IOException {
|
||||
if (!meta.isFabric()) {
|
||||
Utils.LOGGER.error("This is not a fabric instance");
|
||||
return;
|
||||
|
@ -132,7 +132,7 @@ public class ModCommand extends Command {
|
|||
}
|
||||
|
||||
@Override
|
||||
protected void invoke(CommandArgs args, Path instancePath, InstanceMeta meta) {
|
||||
protected void invoke(CommandArgs args, Path instancePath, InstanceMeta meta) throws IOException {
|
||||
if (!meta.isFabric()) {
|
||||
Utils.LOGGER.error("This is not a fabric instance");
|
||||
return;
|
||||
|
|
|
@ -13,28 +13,29 @@ import io.gitlab.jfronny.inceptum.model.mojang.VersionsList;
|
|||
import io.gitlab.jfronny.inceptum.model.mojang.VersionsListInfo;
|
||||
import io.gitlab.jfronny.inceptum.util.ConfigHolder;
|
||||
import io.gitlab.jfronny.inceptum.util.MetaHolder;
|
||||
import io.gitlab.jfronny.inceptum.util.Tuple;
|
||||
import io.gitlab.jfronny.inceptum.util.Utils;
|
||||
import io.gitlab.jfronny.inceptum.util.api.FabricMetaApi;
|
||||
import io.gitlab.jfronny.inceptum.util.api.McApi;
|
||||
import io.gitlab.jfronny.inceptum.util.cache.MemoryCache;
|
||||
import io.gitlab.jfronny.inceptum.util.tuple.Tuple;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
public class InstanceManageControls {
|
||||
private final VersionsList manifest = McApi.getVersions();
|
||||
private final Map<VersionsListInfo, List<FabricVersionLoaderInfo>> loaderInfoCache = new HashMap<>();
|
||||
private static final VersionsList VERSIONS = McApi.getVersions();
|
||||
private static final MemoryCache<VersionsListInfo, List<FabricVersionLoaderInfo>> LOADER_INFO_CACHE = new MemoryCache<>();
|
||||
private static final MemoryCache<Tuple<String, String>, VersionInfo> VERSION_INFO_CACHE = new MemoryCache<>();
|
||||
|
||||
private final ImInt version = new ImInt(0);
|
||||
private final ImString name = new ImString("", GuiUtil.INPUT_FIELD_LENGTH);
|
||||
private final ImInt fabricVersion = new ImInt(0);
|
||||
private final ImBoolean snapshots = new ImBoolean(ConfigHolder.CONFIG.snapshots);
|
||||
private final ImBoolean fabric = new ImBoolean(true);
|
||||
private final Map<Tuple<String, String>, VersionInfo> versionInfoCache = new HashMap<>();
|
||||
|
||||
private VersionsListInfo selected;
|
||||
private FabricVersionLoaderInfo selectedFabric;
|
||||
|
||||
|
@ -128,13 +129,11 @@ public class InstanceManageControls {
|
|||
}
|
||||
|
||||
public VersionInfo getVersionInfo() throws IOException {
|
||||
Tuple<String, String> key = new Tuple<>(selected.id, fabric.get() ? selectedFabric.loader.version : "");
|
||||
if (!versionInfoCache.containsKey(key)) {
|
||||
return VERSION_INFO_CACHE.get(Tuple.of(selected.id, fabric.get() ? selectedFabric.loader.version : ""), () -> {
|
||||
VersionInfo vi = McApi.getVersionInfo(selected);
|
||||
if (fabric.get()) FabricMetaApi.addFabric(vi, selectedFabric.loader.version, FabricMetaApi.FabricVersionInfoType.Both);
|
||||
versionInfoCache.put(key, vi);
|
||||
}
|
||||
return versionInfoCache.get(key);
|
||||
return vi;
|
||||
});
|
||||
}
|
||||
|
||||
public LoaderInfo getLoaderInfo() {
|
||||
|
@ -142,7 +141,7 @@ public class InstanceManageControls {
|
|||
}
|
||||
|
||||
private List<VersionsListInfo> getVersions(boolean snapshots) {
|
||||
ArrayList<VersionsListInfo> res = new ArrayList<>(manifest.versions);
|
||||
ArrayList<VersionsListInfo> res = new ArrayList<>(VERSIONS.versions);
|
||||
res.removeIf(info -> !snapshots && !info.type.equals("release"));
|
||||
return res;
|
||||
}
|
||||
|
@ -152,8 +151,6 @@ public class InstanceManageControls {
|
|||
}
|
||||
|
||||
private List<FabricVersionLoaderInfo> getFabricLoaderInfo() {
|
||||
if (!loaderInfoCache.containsKey(selected))
|
||||
loaderInfoCache.put(selected, FabricMetaApi.getLoaderVersions(selected));
|
||||
return loaderInfoCache.get(selected);
|
||||
return LOADER_INFO_CACHE.get(selected, () -> FabricMetaApi.getLoaderVersions(selected));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,20 +2,26 @@ package io.gitlab.jfronny.inceptum.frontend.gui.control;
|
|||
|
||||
import imgui.ImGui;
|
||||
import imgui.flag.ImGuiTableFlags;
|
||||
import io.gitlab.jfronny.inceptum.Inceptum;
|
||||
import io.gitlab.jfronny.inceptum.InceptumGui;
|
||||
import io.gitlab.jfronny.inceptum.util.install.Steps;
|
||||
import io.gitlab.jfronny.inceptum.frontend.gui.window.edit.InstanceEditWindow;
|
||||
import io.gitlab.jfronny.inceptum.model.inceptum.InstanceMeta;
|
||||
import io.gitlab.jfronny.inceptum.util.InstanceLauncher;
|
||||
import io.gitlab.jfronny.inceptum.util.InstanceLock;
|
||||
import io.gitlab.jfronny.inceptum.util.Utils;
|
||||
import io.gitlab.jfronny.inceptum.frontend.gui.window.edit.InstanceEditWindow;
|
||||
import io.gitlab.jfronny.inceptum.util.InstanceLauncher;
|
||||
import io.gitlab.jfronny.inceptum.util.cache.CachingGsonFile;
|
||||
import io.gitlab.jfronny.inceptum.util.install.Steps;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public class InstanceView {
|
||||
private static final Map<Path, CachingGsonFile<InstanceMeta>> metas = new HashMap<>();
|
||||
|
||||
public static void draw(List<Path> paths) {
|
||||
if (ImGui.beginTable("Instances", 2, ImGuiTableFlags.SizingFixedFit | ImGuiTableFlags.Borders)) {
|
||||
for (Path path : paths) {
|
||||
|
@ -32,7 +38,8 @@ public class InstanceView {
|
|||
}
|
||||
InstanceMeta instance;
|
||||
try {
|
||||
instance = Utils.loadObject(path.resolve("instance.json"), InstanceMeta.class); //TODO investigate the perf impact of this
|
||||
if (!metas.containsKey(path)) metas.put(path, new CachingGsonFile<>(path.resolve("instance.json"), InstanceMeta.class));
|
||||
instance = metas.get(path).get();
|
||||
} catch (IOException e) {
|
||||
Utils.LOGGER.error("Could not load instance.json", e);
|
||||
continue;
|
||||
|
@ -56,7 +63,13 @@ public class InstanceView {
|
|||
}
|
||||
if (runDisabled) ImGui.endDisabled();
|
||||
ImGui.tableNextColumn();
|
||||
if (ImGui.button("Edit##" + path)) InceptumGui.open(new InstanceEditWindow(path, instance));
|
||||
if (ImGui.button("Edit##" + path)) {
|
||||
try {
|
||||
InceptumGui.open(new InstanceEditWindow(path, instance));
|
||||
} catch (IOException e) {
|
||||
Utils.LOGGER.error("Could not open instance edit window", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
ImGui.endTable();
|
||||
}
|
||||
|
|
|
@ -9,13 +9,13 @@ import io.gitlab.jfronny.inceptum.model.inceptum.InstanceMeta;
|
|||
import io.gitlab.jfronny.inceptum.model.inceptum.ModDescription;
|
||||
import io.gitlab.jfronny.inceptum.util.PathUtil;
|
||||
import io.gitlab.jfronny.inceptum.util.mds.IWModDescription;
|
||||
import io.gitlab.jfronny.inceptum.util.mds.ModsDirScanner;
|
||||
import io.gitlab.jfronny.inceptum.util.source.CurseforgeModSource;
|
||||
import io.gitlab.jfronny.inceptum.util.source.ModDownload;
|
||||
import io.gitlab.jfronny.inceptum.util.source.ModSource;
|
||||
import io.gitlab.jfronny.inceptum.util.source.ModrinthModSource;
|
||||
import io.gitlab.jfronny.inceptum.model.modrinth.ModrinthSearchResult;
|
||||
import io.gitlab.jfronny.inceptum.model.modrinth.ModrinthVersion;
|
||||
import io.gitlab.jfronny.inceptum.util.mds.ModsDirScanner;
|
||||
import io.gitlab.jfronny.inceptum.util.Utils;
|
||||
import io.gitlab.jfronny.inceptum.util.api.CurseforgeApi;
|
||||
import io.gitlab.jfronny.inceptum.util.api.ModrinthApi;
|
||||
|
|
|
@ -12,9 +12,7 @@ import org.eclipse.jgit.api.Git;
|
|||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Path;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
public class InstanceEditWindow extends Window {
|
||||
protected final Path path;
|
||||
|
@ -26,20 +24,14 @@ public class InstanceEditWindow extends Window {
|
|||
protected boolean reDownload = false;
|
||||
protected boolean lastTabWasMods = false;
|
||||
|
||||
public InstanceEditWindow(Path path, InstanceMeta instance) {
|
||||
public InstanceEditWindow(Path path, InstanceMeta instance) throws IOException {
|
||||
super(path.getFileName().toString() + " - Edit");
|
||||
name = path.getFileName().toString();
|
||||
this.name = path.getFileName().toString();
|
||||
this.path = path;
|
||||
this.instance = instance;
|
||||
mds = ModsDirScanner.get(path.resolve("mods"), instance);
|
||||
mds.start();
|
||||
Git git = null;
|
||||
try {
|
||||
git = Git.open(path.toFile());
|
||||
} catch (IOException e) {
|
||||
Utils.LOGGER.error("Could not open instance as git repo", e);
|
||||
}
|
||||
this.git = git;
|
||||
this.mds = ModsDirScanner.get(path.resolve("mods"), instance);
|
||||
this.mds.start();
|
||||
this.git = Git.open(path.toFile());
|
||||
this.tabs = List.of(
|
||||
new GeneralTab(this),
|
||||
new ArgumentsTab(this),
|
||||
|
|
|
@ -86,7 +86,7 @@ public class ModDescription {
|
|||
|
||||
public void addSource(ModSource source, String gameVersion) {
|
||||
try {
|
||||
sources.put(source, source.getUpdate(gameVersion)); //TODO only fetch updates on first check
|
||||
sources.put(source, source.getUpdate(gameVersion));
|
||||
} catch (IOException e) {
|
||||
Utils.LOGGER.error("Could not check " + source.getName() + " for updates", e);
|
||||
}
|
||||
|
|
|
@ -1,4 +0,0 @@
|
|||
package io.gitlab.jfronny.inceptum.util;
|
||||
|
||||
public record Tuple<T1, T2>(T1 left, T2 right) {
|
||||
}
|
|
@ -72,7 +72,7 @@ public record FileScanTask(Path file, BiConsumer<Path, IWModDescription> discove
|
|||
}
|
||||
ModSource selectedSource = null;
|
||||
for (ModSource source : md.sources.keySet()) {
|
||||
source.getUpdate(gameVersion); //TODO only check once for ID
|
||||
source.getUpdate(gameVersion);
|
||||
if (!Files.exists(source.getJarPath())) source.download();
|
||||
selectedSource = source;
|
||||
}
|
||||
|
|
|
@ -1,143 +1,30 @@
|
|||
package io.gitlab.jfronny.inceptum.util.mds;
|
||||
|
||||
import io.gitlab.jfronny.inceptum.model.inceptum.InstanceMeta;
|
||||
import io.gitlab.jfronny.inceptum.util.PathUtil;
|
||||
import io.gitlab.jfronny.inceptum.util.Utils;
|
||||
|
||||
import java.io.Closeable;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.*;
|
||||
import java.util.Set;
|
||||
import java.util.function.BiConsumer;
|
||||
|
||||
public class ModsDirScanner implements Closeable {
|
||||
private static final Map<Path, ModsDirScanner> SCANNERS = new HashMap<>();
|
||||
private static final ExecutorService POOL = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors(), new NamedThreadFactory("mds"));
|
||||
private final Map<Path, IWModDescription> descriptions = new HashMap<>();
|
||||
private final Set<Path> scannedPaths = new HashSet<>();
|
||||
private final Thread th;
|
||||
private final Path modsDir;
|
||||
private final InstanceMeta instance;
|
||||
private boolean disposed = false;
|
||||
|
||||
private ModsDirScanner(Path modsDir, InstanceMeta instance) {
|
||||
this.modsDir = modsDir;
|
||||
this.instance = instance;
|
||||
this.th = new Thread(this::scanTaskInternal, "mds-" + modsDir.getParent().getFileName());
|
||||
public interface ModsDirScanner extends Closeable {
|
||||
static ModsDirScanner get(Path modsDir, InstanceMeta instance) throws IOException {
|
||||
if (Files.exists(modsDir)) return ModsDirScannerImpl.get(modsDir, instance);
|
||||
return new NoopMds(instance.getMinecraftVersion());
|
||||
}
|
||||
|
||||
public static ModsDirScanner get(Path modsDir, InstanceMeta instance) {
|
||||
if (SCANNERS.containsKey(modsDir)) {
|
||||
ModsDirScanner mds = SCANNERS.get(modsDir);
|
||||
if (mds.instance.equals(instance))
|
||||
return mds;
|
||||
mds.close();
|
||||
}
|
||||
ModsDirScanner mds = new ModsDirScanner(modsDir, instance);
|
||||
SCANNERS.put(modsDir, mds);
|
||||
return mds;
|
||||
static void closeAll() {
|
||||
ModsDirScannerImpl.closeAll();
|
||||
}
|
||||
|
||||
public boolean isComplete() {
|
||||
if (!Files.isDirectory(modsDir)) return true;
|
||||
try {
|
||||
for (Path path : Utils.ls(modsDir)) {
|
||||
if (!descriptions.containsKey(path)) return false;
|
||||
}
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public void start() {
|
||||
if (!th.isAlive())
|
||||
th.start();
|
||||
}
|
||||
|
||||
public String getGameVersion() {
|
||||
return instance.getMinecraftVersion();
|
||||
}
|
||||
|
||||
public Set<IWModDescription> getMods() throws IOException {
|
||||
Set<IWModDescription> mods = new TreeSet<>();
|
||||
if (Files.isDirectory(modsDir)) {
|
||||
for (Path path : Utils.ls(modsDir)) {
|
||||
if (PathUtil.isImod(path) && Files.exists(PathUtil.trimImod(path)))
|
||||
continue;
|
||||
mods.add(get(path));
|
||||
}
|
||||
}
|
||||
return mods;
|
||||
}
|
||||
|
||||
public IWModDescription get(Path path) {
|
||||
if (!descriptions.containsKey(path)) {
|
||||
// not yet scanned
|
||||
descriptions.put(path, new IWModDescription(path));
|
||||
}
|
||||
return descriptions.get(path);
|
||||
}
|
||||
|
||||
public void invalidate(Path path) {
|
||||
descriptions.remove(path);
|
||||
scannedPaths.remove(path);
|
||||
}
|
||||
|
||||
public boolean hasScanned(Path path) {
|
||||
return scannedPaths.contains(path);
|
||||
}
|
||||
|
||||
private void scanTaskInternal() {
|
||||
while (!disposed) {
|
||||
runOnce((path, iwModDescription) -> {});
|
||||
try {
|
||||
Thread.sleep(100);
|
||||
} catch (InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void runOnce(BiConsumer<Path, IWModDescription> discovered) {
|
||||
try {
|
||||
if (!Files.isDirectory(modsDir)) {
|
||||
return;
|
||||
}
|
||||
Set<Future<?>> tasks = new HashSet<>();
|
||||
String mc = instance.getMinecraftVersion();
|
||||
discovered = discovered.andThen((path, iwModDescription) -> {
|
||||
scannedPaths.add(path);
|
||||
descriptions.put(path, iwModDescription);
|
||||
});
|
||||
for (Path mods : Utils.ls(modsDir)) {
|
||||
if (disposed) return;
|
||||
tasks.add(POOL.submit(new FileScanTask(mods, discovered, mc)));
|
||||
}
|
||||
for (Future<?> task : tasks) {
|
||||
try {
|
||||
task.get();
|
||||
} catch (ExecutionException e) {
|
||||
Utils.LOGGER.error("Failed to execute ScanTask", e);
|
||||
}
|
||||
}
|
||||
} catch (IOException | InterruptedException e) {
|
||||
Utils.LOGGER.error("Could not list mods", e);
|
||||
}
|
||||
}
|
||||
|
||||
public static void closeAll() {
|
||||
for (ModsDirScanner value : SCANNERS.values().toArray(ModsDirScanner[]::new)) {
|
||||
value.close();
|
||||
}
|
||||
POOL.shutdown();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
disposed = true;
|
||||
SCANNERS.remove(modsDir);
|
||||
}
|
||||
boolean isComplete();
|
||||
void start();
|
||||
String getGameVersion();
|
||||
Set<IWModDescription> getMods() throws IOException;
|
||||
IWModDescription get(Path path);
|
||||
void invalidate(Path path);
|
||||
boolean hasScanned(Path path);
|
||||
void runOnce(BiConsumer<Path, IWModDescription> discovered);
|
||||
}
|
||||
|
|
|
@ -0,0 +1,168 @@
|
|||
package io.gitlab.jfronny.inceptum.util.mds;
|
||||
|
||||
import io.gitlab.jfronny.inceptum.model.inceptum.InstanceMeta;
|
||||
import io.gitlab.jfronny.inceptum.util.PathUtil;
|
||||
import io.gitlab.jfronny.inceptum.util.Utils;
|
||||
|
||||
import java.io.Closeable;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.*;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.*;
|
||||
import java.util.function.BiConsumer;
|
||||
|
||||
import static java.nio.file.StandardWatchEventKinds.*;
|
||||
|
||||
class ModsDirScannerImpl implements ModsDirScanner {
|
||||
private static final Map<Path, ModsDirScannerImpl> SCANNERS = new HashMap<>();
|
||||
private static final ExecutorService POOL = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors(), new NamedThreadFactory("mds"));
|
||||
private final Map<Path, IWModDescription> descriptions = new HashMap<>();
|
||||
private final Set<Path> scannedPaths = new HashSet<>();
|
||||
private final Thread th;
|
||||
private final Path modsDir;
|
||||
private final InstanceMeta instance;
|
||||
private final WatchService service;
|
||||
private boolean disposed = false;
|
||||
|
||||
private ModsDirScannerImpl(Path modsDir, InstanceMeta instance) throws IOException {
|
||||
this.modsDir = modsDir;
|
||||
this.instance = instance;
|
||||
this.th = new Thread(this::scanTaskInternal, "mds-" + modsDir.getParent().getFileName());
|
||||
this.service = FileSystems.getDefault().newWatchService();
|
||||
modsDir.register(service, ENTRY_MODIFY, ENTRY_CREATE, ENTRY_DELETE);
|
||||
}
|
||||
|
||||
protected static ModsDirScannerImpl get(Path modsDir, InstanceMeta instance) throws IOException {
|
||||
if (SCANNERS.containsKey(modsDir)) {
|
||||
ModsDirScannerImpl mds = SCANNERS.get(modsDir);
|
||||
if (mds.instance.equals(instance))
|
||||
return mds;
|
||||
mds.close();
|
||||
}
|
||||
ModsDirScannerImpl mds = new ModsDirScannerImpl(modsDir, instance);
|
||||
SCANNERS.put(modsDir, mds);
|
||||
return mds;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isComplete() {
|
||||
if (!Files.isDirectory(modsDir)) return true;
|
||||
try {
|
||||
for (Path path : Utils.ls(modsDir)) {
|
||||
if (!descriptions.containsKey(path)) return false;
|
||||
}
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void start() {
|
||||
if (!th.isAlive())
|
||||
th.start();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getGameVersion() {
|
||||
return instance.getMinecraftVersion();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<IWModDescription> getMods() throws IOException {
|
||||
Set<IWModDescription> mods = new TreeSet<>();
|
||||
if (Files.isDirectory(modsDir)) {
|
||||
for (Path path : Utils.ls(modsDir)) {
|
||||
if (PathUtil.isImod(path) && Files.exists(PathUtil.trimImod(path)))
|
||||
continue;
|
||||
mods.add(get(path));
|
||||
}
|
||||
}
|
||||
return mods;
|
||||
}
|
||||
|
||||
@Override
|
||||
public IWModDescription get(Path path) {
|
||||
if (!descriptions.containsKey(path)) {
|
||||
// not yet scanned
|
||||
descriptions.put(path, new IWModDescription(path));
|
||||
}
|
||||
return descriptions.get(path);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void invalidate(Path path) {
|
||||
descriptions.remove(path);
|
||||
scannedPaths.remove(path);
|
||||
}
|
||||
|
||||
public boolean hasScanned(Path path) {
|
||||
return scannedPaths.contains(path);
|
||||
}
|
||||
|
||||
private void scanTaskInternal() {
|
||||
while (!disposed) {
|
||||
runOnce((path, iwModDescription) -> {});
|
||||
try {
|
||||
Thread.sleep(100);
|
||||
} catch (InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void runOnce(BiConsumer<Path, IWModDescription> discovered) {
|
||||
try {
|
||||
if (!Files.isDirectory(modsDir)) {
|
||||
return;
|
||||
}
|
||||
Set<Future<?>> tasks = new HashSet<>();
|
||||
String mc = instance.getMinecraftVersion();
|
||||
discovered = discovered.andThen((path, iwModDescription) -> {
|
||||
scannedPaths.add(path);
|
||||
descriptions.put(path, iwModDescription);
|
||||
});
|
||||
List<Path> toScan;
|
||||
if (descriptions.isEmpty()) {
|
||||
toScan = Utils.ls(modsDir);
|
||||
}
|
||||
else {
|
||||
toScan = new ArrayList<>();
|
||||
WatchKey key = service.poll();
|
||||
if (key != null) {
|
||||
for (WatchEvent<?> event : key.pollEvents()) {
|
||||
if (event.context() instanceof Path p)
|
||||
toScan.add(p);
|
||||
}
|
||||
key.reset();
|
||||
}
|
||||
}
|
||||
for (Path p : toScan) {
|
||||
tasks.add(POOL.submit(new FileScanTask(p, discovered, mc)));
|
||||
}
|
||||
for (Future<?> task : tasks) {
|
||||
try {
|
||||
task.get();
|
||||
} catch (ExecutionException e) {
|
||||
Utils.LOGGER.error("Failed to execute ScanTask", e);
|
||||
}
|
||||
}
|
||||
} catch (IOException | InterruptedException e) {
|
||||
Utils.LOGGER.error("Could not list mods", e);
|
||||
}
|
||||
}
|
||||
|
||||
public static void closeAll() {
|
||||
for (ModsDirScannerImpl value : SCANNERS.values().toArray(ModsDirScannerImpl[]::new)) {
|
||||
value.close();
|
||||
}
|
||||
POOL.shutdown();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
disposed = true;
|
||||
SCANNERS.remove(modsDir);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,49 @@
|
|||
package io.gitlab.jfronny.inceptum.util.mds;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Path;
|
||||
import java.util.Set;
|
||||
import java.util.function.BiConsumer;
|
||||
|
||||
public record NoopMds(String gameVersion) implements ModsDirScanner {
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isComplete() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void start() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getGameVersion() {
|
||||
return gameVersion;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<IWModDescription> getMods() throws IOException {
|
||||
return Set.of();
|
||||
}
|
||||
|
||||
@Override
|
||||
public IWModDescription get(Path path) {
|
||||
return new IWModDescription(path);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void invalidate(Path path) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasScanned(Path path) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void runOnce(BiConsumer<Path, IWModDescription> discovered) {
|
||||
}
|
||||
}
|
|
@ -6,6 +6,9 @@ import io.gitlab.jfronny.inceptum.model.curseforge.CurseforgeMod;
|
|||
import io.gitlab.jfronny.inceptum.util.HashUtils;
|
||||
import io.gitlab.jfronny.inceptum.util.Utils;
|
||||
import io.gitlab.jfronny.inceptum.util.api.CurseforgeApi;
|
||||
import io.gitlab.jfronny.inceptum.util.cache.MemoryCache;
|
||||
import io.gitlab.jfronny.inceptum.util.tuple.Triple;
|
||||
import io.gitlab.jfronny.inceptum.util.tuple.Tuple;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.URISyntaxException;
|
||||
|
@ -16,6 +19,9 @@ import java.util.Optional;
|
|||
import java.util.Set;
|
||||
|
||||
public final class CurseforgeModSource implements ModSource {
|
||||
private static final MemoryCache<Triple<Integer, Integer, String>, Optional<ModSource>> UPDATE_CACHE = new MemoryCache<>();
|
||||
private static final MemoryCache<Tuple<Integer, Integer>, Set<ModSource>> DEPENDENCIES_CACHE = new MemoryCache<>();
|
||||
|
||||
private final int projectId;
|
||||
private final int fileId;
|
||||
private final CurseforgeFile current;
|
||||
|
@ -42,24 +48,28 @@ public final class CurseforgeModSource implements ModSource {
|
|||
|
||||
@Override
|
||||
public Set<ModSource> getDependencies() throws IOException {
|
||||
Set<ModSource> deps = new HashSet<>();
|
||||
for (CurseforgeDependency dependency : current.dependencies) {
|
||||
if (dependency.type == 3) //TODO support other types (3=required, 2=optional)
|
||||
deps.add(new CurseforgeModSource(dependency.addonId, dependency.fileId));
|
||||
}
|
||||
return deps;
|
||||
return DEPENDENCIES_CACHE.get(Tuple.of(projectId, fileId), () -> {
|
||||
Set<ModSource> deps = new HashSet<>();
|
||||
for (CurseforgeDependency dependency : current.dependencies) {
|
||||
if (dependency.type == 3) //TODO support other types (3=required, 2=optional)
|
||||
deps.add(new CurseforgeModSource(dependency.addonId, dependency.fileId));
|
||||
}
|
||||
return deps;
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<ModSource> getUpdate(String gameVersion) throws IOException {
|
||||
for (CurseforgeMod.GameVersionLatestFile file : mod.gameVersionLatestFiles) {
|
||||
if (file.gameVersion.equals(gameVersion)) {
|
||||
return file.projectFileId == fileId
|
||||
? Optional.empty()
|
||||
: Optional.of(new CurseforgeModSource(projectId, file.projectFileId));
|
||||
return UPDATE_CACHE.get(Tuple.of(projectId, fileId, gameVersion), () -> {
|
||||
for (CurseforgeMod.GameVersionLatestFile file : mod.gameVersionLatestFiles) {
|
||||
if (file.gameVersion.equals(gameVersion)) {
|
||||
return file.projectFileId == fileId
|
||||
? Optional.empty()
|
||||
: Optional.of(new CurseforgeModSource(projectId, file.projectFileId));
|
||||
}
|
||||
}
|
||||
}
|
||||
return Optional.empty();
|
||||
return Optional.empty();
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -5,6 +5,8 @@ import io.gitlab.jfronny.inceptum.model.modrinth.ModrinthVersion;
|
|||
import io.gitlab.jfronny.inceptum.util.HashUtils;
|
||||
import io.gitlab.jfronny.inceptum.util.Utils;
|
||||
import io.gitlab.jfronny.inceptum.util.api.ModrinthApi;
|
||||
import io.gitlab.jfronny.inceptum.util.cache.MemoryCache;
|
||||
import io.gitlab.jfronny.inceptum.util.tuple.Tuple;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.URISyntaxException;
|
||||
|
@ -15,6 +17,9 @@ import java.util.Optional;
|
|||
import java.util.Set;
|
||||
|
||||
public final class ModrinthModSource implements ModSource {
|
||||
private static final MemoryCache<Tuple<String, String>, Optional<ModSource>> UPDATE_CACHE = new MemoryCache<>();
|
||||
private static final MemoryCache<String, Set<ModSource>> DEPENDENCIES_CACHE = new MemoryCache<>();
|
||||
|
||||
private final String versionId;
|
||||
private final ModrinthVersion current;
|
||||
private final ModrinthMod mod;
|
||||
|
@ -39,37 +44,41 @@ public final class ModrinthModSource implements ModSource {
|
|||
|
||||
@Override
|
||||
public Set<ModSource> getDependencies() throws IOException {
|
||||
Set<ModSource> deps = new HashSet<>();
|
||||
for (ModrinthVersion.Dependency dependency : current.dependencies) {
|
||||
//TODO show optional dependencies
|
||||
if (dependency.dependency_type == ModrinthVersion.Dependency.DependencyType.required)
|
||||
deps.add(new ModrinthModSource(dependency.version_id));
|
||||
}
|
||||
return deps;
|
||||
return DEPENDENCIES_CACHE.get(versionId, () -> {
|
||||
Set<ModSource> deps = new HashSet<>();
|
||||
for (ModrinthVersion.Dependency dependency : current.dependencies) {
|
||||
//TODO show optional dependencies
|
||||
if (dependency.dependency_type == ModrinthVersion.Dependency.DependencyType.required)
|
||||
deps.add(new ModrinthModSource(dependency.version_id));
|
||||
}
|
||||
return Set.copyOf(deps);
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<ModSource> getUpdate(String gameVersion) throws IOException {
|
||||
ModrinthVersion stable = null;
|
||||
ModrinthVersion beta = null;
|
||||
ModrinthVersion latest = null;
|
||||
for (ModrinthVersion version : ModrinthApi.getVersions(getModId())) {
|
||||
if (version.game_versions.contains(gameVersion) && version.loaders.contains("fabric")) {
|
||||
latest = version;
|
||||
if (version.version_type == ModrinthVersion.VersionType.beta || version.version_type == ModrinthVersion.VersionType.release)
|
||||
beta = version;
|
||||
if (version.version_type == ModrinthVersion.VersionType.release)
|
||||
stable = version;
|
||||
return UPDATE_CACHE.get(Tuple.of(versionId, gameVersion), () -> {
|
||||
ModrinthVersion stable = null;
|
||||
ModrinthVersion beta = null;
|
||||
ModrinthVersion latest = null;
|
||||
for (ModrinthVersion version : ModrinthApi.getVersions(getModId())) {
|
||||
if (version.game_versions.contains(gameVersion) && version.loaders.contains("fabric")) {
|
||||
latest = version;
|
||||
if (version.version_type == ModrinthVersion.VersionType.beta || version.version_type == ModrinthVersion.VersionType.release)
|
||||
beta = version;
|
||||
if (version.version_type == ModrinthVersion.VersionType.release)
|
||||
stable = version;
|
||||
}
|
||||
}
|
||||
}
|
||||
ModrinthVersion next = switch (current.version_type) {
|
||||
case alpha -> latest;
|
||||
case beta -> beta;
|
||||
case release -> stable;
|
||||
};
|
||||
if (next == null) return Optional.empty();
|
||||
if (next.version_number.equals(current.version_number)) return Optional.empty();
|
||||
return Optional.of(new ModrinthModSource(next.id));
|
||||
ModrinthVersion next = switch (current.version_type) {
|
||||
case alpha -> latest;
|
||||
case beta -> beta;
|
||||
case release -> stable;
|
||||
};
|
||||
if (next == null) return Optional.empty();
|
||||
if (next.version_number.equals(current.version_number)) return Optional.empty();
|
||||
return Optional.of(new ModrinthModSource(next.id));
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -3,6 +3,7 @@ package io.gitlab.jfronny.inceptum.util;
|
|||
import io.gitlab.jfronny.inceptum.WrapperStrap;
|
||||
import io.gitlab.jfronny.inceptum.gson.GsonHolder;
|
||||
import io.gitlab.jfronny.inceptum.model.OSType;
|
||||
import io.gitlab.jfronny.inceptum.util.cache.GsonFileCache;
|
||||
import io.gitlab.jfronny.inceptum.util.lambda.ThrowingConsumer;
|
||||
import io.gitlab.jfronny.inceptum.util.lambda.ThrowingSupplier;
|
||||
import org.slf4j.Logger;
|
||||
|
@ -22,7 +23,6 @@ import java.util.Arrays;
|
|||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.regex.Pattern;
|
||||
import java.util.stream.Stream;
|
||||
|
@ -30,6 +30,7 @@ import java.util.stream.Stream;
|
|||
public class Utils {
|
||||
public static final Pattern VALID_FILENAME = Pattern.compile("[a-zA-Z0-9_\\-.][a-zA-Z0-9 _\\-.]*[a-zA-Z0-9_\\-.]");
|
||||
public static final Logger LOGGER = LoggerFactory.getLogger("Inceptum");
|
||||
private static final GsonFileCache OBJECT_CACHE = new GsonFileCache(MetaHolder.CACHE_DIR);
|
||||
|
||||
public static byte[] downloadData(String url) throws IOException, URISyntaxException {
|
||||
try (InputStream is = HttpUtils.get(url).sendInputStream()) {
|
||||
|
@ -45,28 +46,22 @@ public class Utils {
|
|||
}
|
||||
|
||||
public static <T> T downloadObject(String url, Class<T> type) throws IOException {
|
||||
return downloadObject(url, () -> HttpUtils.get(url).sendString(), s -> GsonHolder.getGson().fromJson(s, type));
|
||||
return downloadObject(url, () -> HttpUtils.get(url).sendString(), type);
|
||||
}
|
||||
|
||||
public static <T> T downloadObject(String url, Type type) throws IOException {
|
||||
return downloadObject(url, () -> HttpUtils.get(url).sendString(), s -> GsonHolder.getGson().fromJson(s, type));
|
||||
return downloadObject(url, () -> HttpUtils.get(url).sendString(), type);
|
||||
}
|
||||
|
||||
public static <T> T downloadObject(String url, String sha1, Class<T> type) throws IOException {
|
||||
return downloadObject(url, () -> downloadString(url, sha1), s -> GsonHolder.getGson().fromJson(s, type));
|
||||
return downloadObject(url, () -> downloadString(url, sha1), type);
|
||||
}
|
||||
|
||||
//TODO memory-based cache to avoid gson
|
||||
private static <T> T downloadObject(String url, ThrowingSupplier<String, Exception> sourceString, Function<String, T> builder) throws IOException {
|
||||
Path cache = MetaHolder.CACHE_DIR.resolve(HashUtils.sha1(url.getBytes(StandardCharsets.UTF_8)));
|
||||
if (Files.exists(cache))
|
||||
return builder.apply(Files.readString(cache));
|
||||
private static <T> T downloadObject(String url, ThrowingSupplier<String, Exception> sourceString, Type type) throws IOException {
|
||||
try {
|
||||
String download = sourceString.get();
|
||||
Files.writeString(cache, download);
|
||||
return builder.apply(download);
|
||||
return OBJECT_CACHE.get(HashUtils.sha1(url.getBytes(StandardCharsets.UTF_8)), () -> GsonHolder.getGson().fromJson(sourceString.get(), type), type);
|
||||
} catch (Exception e) {
|
||||
throw new IOException("Could not download object and no cache exists (URL=" + url + ", Cache=" + cache + ")", e);
|
||||
throw new IOException("Could not download object and no cache exists", e);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
41
wrapper/src/main/java/io/gitlab/jfronny/inceptum/util/cache/CachingGsonFile.java
vendored
Normal file
41
wrapper/src/main/java/io/gitlab/jfronny/inceptum/util/cache/CachingGsonFile.java
vendored
Normal file
|
@ -0,0 +1,41 @@
|
|||
package io.gitlab.jfronny.inceptum.util.cache;
|
||||
|
||||
import io.gitlab.jfronny.inceptum.util.Utils;
|
||||
|
||||
import java.io.Closeable;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.*;
|
||||
|
||||
import static java.nio.file.StandardWatchEventKinds.*;
|
||||
|
||||
public class CachingGsonFile<T> implements Closeable {
|
||||
private final Path filePath;
|
||||
private final Class<T> type;
|
||||
private final WatchService service;
|
||||
private T cache = null;
|
||||
|
||||
public CachingGsonFile(Path filePath, Class<T> type) throws IOException {
|
||||
this.filePath = filePath;
|
||||
this.type = type;
|
||||
this.service = FileSystems.getDefault().newWatchService();
|
||||
filePath.getParent().register(service, ENTRY_MODIFY);
|
||||
}
|
||||
|
||||
public T get() throws IOException {
|
||||
WatchKey key = service.poll();
|
||||
boolean update = cache == null;
|
||||
if (key != null) {
|
||||
for (WatchEvent<?> event : key.pollEvents()) {
|
||||
update |= event.context().equals(filePath);
|
||||
}
|
||||
key.reset();
|
||||
}
|
||||
if (update) cache = Utils.loadObject(filePath, type);
|
||||
return cache;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
service.close();
|
||||
}
|
||||
}
|
54
wrapper/src/main/java/io/gitlab/jfronny/inceptum/util/cache/GsonFileCache.java
vendored
Normal file
54
wrapper/src/main/java/io/gitlab/jfronny/inceptum/util/cache/GsonFileCache.java
vendored
Normal file
|
@ -0,0 +1,54 @@
|
|||
package io.gitlab.jfronny.inceptum.util.cache;
|
||||
|
||||
import io.gitlab.jfronny.inceptum.util.Utils;
|
||||
import io.gitlab.jfronny.inceptum.util.lambda.ThrowingSupplier;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.Type;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
public class GsonFileCache {
|
||||
private final ConcurrentHashMap<String, Object> container = new ConcurrentHashMap<>();
|
||||
private final Path cacheDir;
|
||||
|
||||
public GsonFileCache(Path cacheDir) {
|
||||
this.cacheDir = cacheDir;
|
||||
}
|
||||
|
||||
public void remove(String key) {
|
||||
container.remove(key);
|
||||
try {
|
||||
Files.delete(cacheDir.resolve(key));
|
||||
} catch (IOException e) {
|
||||
Utils.LOGGER.error("Could not remove cache entry", e);
|
||||
}
|
||||
}
|
||||
|
||||
public void clear() {
|
||||
container.clear();
|
||||
try {
|
||||
Utils.ls(cacheDir, Files::delete);
|
||||
} catch (IOException e) {
|
||||
Utils.LOGGER.error("Could not clear cache backend", e);
|
||||
}
|
||||
}
|
||||
|
||||
public <T, TEx extends Throwable> T get(String key, ThrowingSupplier<T, TEx> builder, Type klazz) throws TEx {
|
||||
if (!container.containsKey(key)) {
|
||||
Path cd = cacheDir.resolve(key);
|
||||
if (Files.exists(cd)) {
|
||||
try {
|
||||
T value = Utils.loadObject(cd, klazz);
|
||||
Utils.writeObject(cd, value);
|
||||
container.put(key, value);
|
||||
} catch (IOException e) {
|
||||
Utils.LOGGER.error("Could not read cache", e);
|
||||
}
|
||||
}
|
||||
else container.put(key, builder.get());
|
||||
}
|
||||
return (T) container.get(key);
|
||||
}
|
||||
}
|
25
wrapper/src/main/java/io/gitlab/jfronny/inceptum/util/cache/MemoryCache.java
vendored
Normal file
25
wrapper/src/main/java/io/gitlab/jfronny/inceptum/util/cache/MemoryCache.java
vendored
Normal file
|
@ -0,0 +1,25 @@
|
|||
package io.gitlab.jfronny.inceptum.util.cache;
|
||||
|
||||
import io.gitlab.jfronny.inceptum.util.lambda.ThrowingSupplier;
|
||||
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
public class MemoryCache<TKey, TValue> {
|
||||
private final ConcurrentHashMap<TKey, TValue> container = new ConcurrentHashMap<>();
|
||||
|
||||
public void remove(TKey key) {
|
||||
container.remove(key);
|
||||
}
|
||||
|
||||
public void clear() {
|
||||
container.clear();
|
||||
}
|
||||
|
||||
public void write() {
|
||||
}
|
||||
|
||||
public <TEx extends Throwable> TValue get(TKey key, ThrowingSupplier<TValue, TEx> builder) throws TEx {
|
||||
if (!container.containsKey(key)) container.put(key, builder.get());
|
||||
return container.get(key);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,4 @@
|
|||
package io.gitlab.jfronny.inceptum.util.tuple;
|
||||
|
||||
public record Quadruple<T1, T2, T3, T4>(T1 val1, T2 val2, T3 val3, T4 val4) {
|
||||
}
|
|
@ -0,0 +1,4 @@
|
|||
package io.gitlab.jfronny.inceptum.util.tuple;
|
||||
|
||||
public record Single<T1>(T1 val1) {
|
||||
}
|
|
@ -0,0 +1,4 @@
|
|||
package io.gitlab.jfronny.inceptum.util.tuple;
|
||||
|
||||
public record Triple<T1, T2, T3>(T1 val1, T2 val2, T3 val3) {
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
package io.gitlab.jfronny.inceptum.util.tuple;
|
||||
|
||||
public record Tuple<T1, T2>(T1 val1, T2 val2) {
|
||||
public static <T1> Single<T1> of(T1 val1) {
|
||||
return new Single<>(val1);
|
||||
}
|
||||
|
||||
public static <T1, T2> Tuple<T1, T2> of(T1 left, T2 right) {
|
||||
return new Tuple<>(left, right);
|
||||
}
|
||||
|
||||
public static <T1, T2, T3> Triple<T1, T2, T3> of(T1 t1, T2 t2, T3 t3) {
|
||||
return new Triple<>(t1, t2, t3);
|
||||
}
|
||||
|
||||
public static <T1, T2, T3, T4> Quadruple<T1, T2, T3, T4> of(T1 t1, T2 t2, T3 t3, T4 t4) {
|
||||
return new Quadruple<>(t1, t2, t3, t4);
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue