Introduce cleaner "Instance" abstraction

This commit is contained in:
Johannes Frohnmeyer 2022-10-19 21:26:06 +02:00
parent 390f6bc59b
commit 5c6266634a
Signed by: Johannes
GPG Key ID: E76429612C2929F4
38 changed files with 456 additions and 401 deletions

View File

@ -4,6 +4,8 @@ import io.gitlab.jfronny.commons.io.JFiles;
import io.gitlab.jfronny.inceptum.common.MetaHolder;
import io.gitlab.jfronny.inceptum.common.Utils;
import io.gitlab.jfronny.inceptum.launcher.model.inceptum.InstanceMeta;
import io.gitlab.jfronny.inceptum.launcher.model.inceptum.rt.Instance;
import io.gitlab.jfronny.inceptum.launcher.util.InstanceList;
import java.io.IOException;
import java.nio.file.Files;
@ -37,25 +39,24 @@ public abstract class BaseInstanceCommand extends Command {
return;
}
Path instancePath = MetaHolder.INSTANCE_DIR.resolve(args.get(0));
Path instanceMetaPath = instancePath.resolve("instance.json");
if (!Files.exists(instanceMetaPath)) {
if (!Files.exists(instancePath)) {
Utils.LOGGER.error("Invalid instance: \"" + args.get(0) + "\"");
return;
}
InstanceMeta meta;
Instance instance;
try {
meta = JFiles.readObject(instanceMetaPath, InstanceMeta.class);
instance = InstanceList.read(instancePath);
} catch (IOException e) {
Utils.LOGGER.error("Could not read instance metadata", e);
return;
}
try {
invoke(args.subArgs(), instancePath, meta);
invoke(args.subArgs(), instance);
} catch (Exception e) {
Utils.LOGGER.error("Could not execute command", e);
return;
}
}
protected abstract void invoke(CommandArgs args, Path instancePath, InstanceMeta meta) throws Exception;
protected abstract void invoke(CommandArgs args, Instance instance) throws Exception;
}

View File

@ -2,12 +2,11 @@ package io.gitlab.jfronny.inceptum.cli.commands;
import io.gitlab.jfronny.inceptum.cli.*;
import io.gitlab.jfronny.inceptum.common.R;
import io.gitlab.jfronny.inceptum.launcher.model.inceptum.InstanceMeta;
import io.gitlab.jfronny.inceptum.launcher.model.inceptum.rt.Instance;
import io.gitlab.jfronny.inceptum.launcher.system.export.Exporters;
import io.gitlab.jfronny.inceptum.launcher.system.mds.ModsDirScanner;
import io.gitlab.jfronny.inceptum.launcher.util.ProcessState;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.List;
@ -25,14 +24,13 @@ public class ExportCommand extends BaseInstanceCommand {
}
@Override
protected void invoke(CommandArgs args, Path instancePath, InstanceMeta meta) throws Exception {
protected void invoke(CommandArgs args, Instance instance) throws Exception {
if (args.length == 0) throw new IllegalAccessException("You must specify a target path");
if (args.length == 1) throw new IllegalAccessException("You must specify a version number");
if (args.length != 2) throw new IllegalAccessException("Too many arguments");
ProcessState state = new ProcessState();
ModsDirScanner mds = ModsDirScanner.get(instancePath.resolve("mods"), meta);
mds.runOnce(R::nop);
Exporters.CURSE_FORGE.generate(state, instancePath, meta, mds, Paths.get(args.get(0)), args.get(1));
instance.getMds().runOnce(R::nop);
Exporters.CURSE_FORGE.generate(state, instance, Paths.get(args.get(0)), args.get(1));
}
private static class MultiMCExportCommand extends BaseInstanceCommand {
@ -41,13 +39,12 @@ public class ExportCommand extends BaseInstanceCommand {
}
@Override
protected void invoke(CommandArgs args, Path instancePath, InstanceMeta meta) throws Exception {
protected void invoke(CommandArgs args, Instance instance) throws Exception {
if (args.length == 0) throw new IllegalAccessException("You must specify a target path");
if (args.length != 1) throw new IllegalAccessException("Too many arguments");
ProcessState state = new ProcessState();
ModsDirScanner mds = ModsDirScanner.get(instancePath.resolve("mods"), meta);
mds.runOnce(R::nop);
Exporters.MULTI_MC.generate(state, instancePath, meta, mds, Paths.get(args.get(0)), "1.0");
instance.getMds().runOnce(R::nop);
Exporters.MULTI_MC.generate(state, instance, Paths.get(args.get(0)), "1.0");
}
}
@ -57,14 +54,13 @@ public class ExportCommand extends BaseInstanceCommand {
}
@Override
protected void invoke(CommandArgs args, Path instancePath, InstanceMeta meta) throws Exception {
protected void invoke(CommandArgs args, Instance instance) throws Exception {
if (args.length == 0) throw new IllegalAccessException("You must specify a target path");
if (args.length == 1) throw new IllegalAccessException("You must specify a version number");
if (args.length != 2) throw new IllegalAccessException("Too many arguments");
ProcessState state = new ProcessState();
ModsDirScanner mds = ModsDirScanner.get(instancePath.resolve("mods"), meta);
mds.runOnce(R::nop);
Exporters.MODRINTH.generate(state, instancePath, meta, mds, Paths.get(args.get(0)), args.get(1));
instance.getMds().runOnce(R::nop);
Exporters.MODRINTH.generate(state, instance, Paths.get(args.get(0)), args.get(1));
}
}
}

View File

@ -3,13 +3,12 @@ package io.gitlab.jfronny.inceptum.cli.commands;
import io.gitlab.jfronny.inceptum.cli.*;
import io.gitlab.jfronny.inceptum.common.Utils;
import io.gitlab.jfronny.inceptum.launcher.model.inceptum.InstanceMeta;
import io.gitlab.jfronny.inceptum.launcher.model.inceptum.rt.Instance;
import io.gitlab.jfronny.inceptum.launcher.system.launch.InstanceLauncher;
import io.gitlab.jfronny.inceptum.launcher.util.InstanceLock;
import io.gitlab.jfronny.inceptum.launcher.api.account.AccountManager;
import io.gitlab.jfronny.inceptum.launcher.system.install.Steps;
import java.io.IOException;
import java.nio.file.Path;
import java.util.*;
public class LaunchCommand extends BaseInstanceCommand {
@ -37,16 +36,17 @@ public class LaunchCommand extends BaseInstanceCommand {
}
@Override
protected void invoke(CommandArgs args, Path instancePath, InstanceMeta meta) throws IOException, InstanceLauncher.LaunchException {
if (InstanceLock.isSetupLocked(instancePath)) {
protected void invoke(CommandArgs args, Instance instance) throws IOException, InstanceLauncher.LaunchException {
if (instance.isSetupLocked()) {
Utils.LOGGER.error("This instance is still being set up");
return;
}
if (InstanceLock.isRunningLocked(instancePath)) {
if (instance.isRunningLocked()) {
Utils.LOGGER.error("This instance is already running");
return;
}
if (args.length > 1) {
InstanceMeta meta = instance.meta();
if (meta.arguments == null) meta.arguments = new InstanceMeta.Arguments();
meta.arguments.client = meta.arguments.client == null ? new ArrayList<>() : new ArrayList<>(meta.arguments.client);
meta.arguments.server = meta.arguments.server == null ? new ArrayList<>() : new ArrayList<>(meta.arguments.server);
@ -54,12 +54,12 @@ public class LaunchCommand extends BaseInstanceCommand {
meta.arguments.client.addAll(args.after(0));
meta.arguments.server.addAll(args.after(0));
}
Steps.reDownload(instancePath, Steps.createProcessState());
Steps.reDownload(instance, Steps.createProcessState());
if (server) {
InstanceLauncher.launch(instancePath, meta, InstanceLauncher.LaunchType.Server, restart, AccountManager.NULL_AUTH);
InstanceLauncher.launch(instance, InstanceLauncher.LaunchType.Server, restart, AccountManager.NULL_AUTH);
} else {
AccountManager.loadAccounts();
InstanceLauncher.launchClient(instancePath, meta);
InstanceLauncher.launchClient(instance);
}
}
}

View File

@ -5,8 +5,8 @@ import io.gitlab.jfronny.inceptum.common.MetaHolder;
import io.gitlab.jfronny.inceptum.common.Utils;
import io.gitlab.jfronny.inceptum.cli.Command;
import io.gitlab.jfronny.inceptum.cli.CommandArgs;
import io.gitlab.jfronny.inceptum.launcher.model.inceptum.InstanceMeta;
import io.gitlab.jfronny.inceptum.launcher.util.InstanceLock;
import io.gitlab.jfronny.inceptum.launcher.model.inceptum.rt.Instance;
import io.gitlab.jfronny.inceptum.launcher.util.InstanceList;
import java.io.IOException;
import java.nio.file.Files;
@ -28,24 +28,24 @@ public class ListCommand extends Command {
continue;
}
System.out.println("- \"" + path.getFileName().toString() + "\"");
if (InstanceLock.isSetupLocked(path)) {
System.out.println(" Status: Setting up");
continue;
}
InstanceMeta instance;
Instance instance;
try {
instance = JFiles.readObject(path.resolve("instance.json"), InstanceMeta.class);
instance = InstanceList.read(path);
} 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 : ""));
if (instance.isSetupLocked()) {
System.out.println(" Status: Setting up");
continue;
}
System.out.println(" Status: " + (instance.isRunningLocked() ? "Running" : "Stopped"));
System.out.println(" Version: " + instance.meta().getMinecraftVersion());
if (instance.isFabric()) System.out.println(" Fabric Loader: " + instance.meta().getLoaderVersion());
if (instance.meta().java != null) System.out.println(" Custom Java: " + instance.meta().java);
if (instance.meta().minMem != null || instance.meta().maxMem != null)
System.out.println(" Memory:" + (instance.meta().minMem != null ? " Minimum: " + instance.meta().minMem : "")
+ (instance.meta().maxMem != null ? " Maximum: " + instance.meta().maxMem : ""));
}
}
}

View File

@ -4,10 +4,10 @@ import io.gitlab.jfronny.commons.io.JFiles;
import io.gitlab.jfronny.commons.throwable.ThrowingBiFunction;
import io.gitlab.jfronny.inceptum.cli.*;
import io.gitlab.jfronny.inceptum.common.Utils;
import io.gitlab.jfronny.inceptum.launcher.model.inceptum.InstanceMeta;
import io.gitlab.jfronny.inceptum.launcher.model.inceptum.rt.Instance;
import io.gitlab.jfronny.inceptum.launcher.util.ModManager;
import io.gitlab.jfronny.inceptum.launcher.util.ModPath;
import io.gitlab.jfronny.inceptum.launcher.system.mds.IWModDescription;
import io.gitlab.jfronny.inceptum.launcher.model.inceptum.rt.Mod;
import io.gitlab.jfronny.inceptum.launcher.system.mds.ModsDirScanner;
import io.gitlab.jfronny.inceptum.launcher.system.source.ModSource;
@ -47,14 +47,13 @@ public class ModCommand extends Command {
}
@Override
protected void invoke(CommandArgs args, Path instancePath, InstanceMeta meta) throws IOException {
if (!meta.isFabric()) {
protected void invoke(CommandArgs args, Instance instance) throws IOException {
if (!instance.isFabric()) {
System.err.println("This is not a fabric instance");
return;
}
System.out.println("Scanning installed mods, this might take a while");
ModsDirScanner mds = ModsDirScanner.get(instancePath.resolve("mods"), meta);
mds.runOnce((path, mod) -> {
instance.getMds().runOnce((path, mod) -> {
boolean hasSources = mod.mod().isPresent() && !mod.mod().get().sources.isEmpty();
boolean updatable = hasSources && mod.mod().get().sources.values().stream().anyMatch(Optional::isPresent);
if (filterUpdatable && !updatable) return;
@ -91,8 +90,8 @@ public class ModCommand extends Command {
}
@Override
protected void invoke(CommandArgs args, Path instancePath, InstanceMeta meta) throws IOException {
if (!meta.isFabric()) {
protected void invoke(CommandArgs args, Instance instance) throws IOException {
if (!instance.isFabric()) {
Utils.LOGGER.error("This is not a fabric instance");
return;
}
@ -102,22 +101,22 @@ public class ModCommand extends Command {
}
Set<Path> mods = new HashSet<>();
for (String arg : args) {
Path p = instancePath.resolve("mods").resolve(arg);
if (!Files.exists(p)) p = instancePath.resolve("mods").resolve(arg + ".imod");
Path p = instance.modsDir().resolve(arg);
if (!Files.exists(p)) p = instance.modsDir().resolve(arg + ".imod");
if (!Files.exists(p)) {
Utils.LOGGER.error("Nonexistant mod file: " + instancePath.resolve("mods").resolve(arg));
Utils.LOGGER.error("Nonexistant mod file: " + instance.modsDir().resolve(arg));
return;
}
mods.add(p);
}
ModsDirScanner mds = ModsDirScanner.get(instancePath.resolve("mods"), meta);
ModsDirScanner mds = instance.getMds();
if (!ignoreDependencies && !mds.isComplete()) {
Utils.LOGGER.error("Scanning mods dir to search for dependencies. This might take a while");
mds.runOnce((path, mod) -> System.out.println("Scanned " + path));
}
for (Path mod : mods) {
try {
ModManager.delete(mds.get(mod), instancePath.resolve("mods"), mds);
ModManager.delete(mds.get(mod), instance.modsDir(), mds);
} catch (IOException e) {
Utils.LOGGER.error("Could not delete " + mod, e);
return;
@ -152,15 +151,15 @@ public class ModCommand extends Command {
}
@Override
protected void invoke(CommandArgs args, Path instancePath, InstanceMeta meta) throws IOException {
if (!meta.isFabric()) {
protected void invoke(CommandArgs args, Instance instance) throws IOException {
if (!instance.isFabric()) {
throw new IOException("This is not a fabric instance");
}
if (args.length == 0) {
throw new IllegalArgumentException("You must specify mods to remove");
}
Set<Path> mods = pathSupplier.apply(args, instancePath);
ModsDirScanner mds = ModsDirScanner.get(instancePath.resolve("mods"), meta);
Set<Path> mods = pathSupplier.apply(args, instance.path());
ModsDirScanner mds = instance.getMds();
if (!mds.isComplete()) {
Utils.LOGGER.error("Scanning mods dir to search for dependencies. This might take a while");
mds.runOnce((path, mod) -> System.out.println("Scanned " + path));
@ -168,8 +167,8 @@ public class ModCommand extends Command {
for (Path mod : mods) {
try {
Utils.LOGGER.info("Updating " + mod);
ModManager.delete(mds.get(mod), instancePath.resolve("mods"), mds);
IWModDescription md = mds.get(mod);
ModManager.delete(mds.get(mod), instance.modsDir(), mds);
Mod md = mds.get(mod);
if (md.mod().isEmpty()) {
throw new IOException("Could not load mod description");
}
@ -179,7 +178,7 @@ public class ModCommand extends Command {
if (ms.isPresent()) {
try {
Utils.LOGGER.info("Updating to " + ms.get().getVersion());
Path imodPath = md.imod().isPresent() ? md.imod().get() : instancePath.resolve("mods").resolve(ms.get().getShortName() + ModPath.EXT_IMOD);
Path imodPath = md.imod().isPresent() ? md.imod().get() : instance.modsDir().resolve(ms.get().getShortName() + ModPath.EXT_IMOD);
ModManager.DownloadMeta dm = ModManager.download(ms.get(), imodPath, mds);
Files.delete(md.path());
if (md.imod().isPresent() && Files.exists(md.imod().get()))

View File

@ -10,10 +10,10 @@ import io.gitlab.jfronny.inceptum.gtk.window.NewInstanceWindow;
import io.gitlab.jfronny.inceptum.launcher.LauncherEnv;
import io.gitlab.jfronny.inceptum.launcher.api.account.AccountManager;
import io.gitlab.jfronny.inceptum.launcher.api.account.MicrosoftAccount;
import io.gitlab.jfronny.inceptum.launcher.model.inceptum.rt.Instance;
import io.gitlab.jfronny.inceptum.launcher.system.install.Steps;
import io.gitlab.jfronny.inceptum.launcher.system.launch.InstanceLauncher;
import io.gitlab.jfronny.inceptum.launcher.util.InstanceList;
import io.gitlab.jfronny.inceptum.launcher.util.InstanceLock;
import java.io.IOException;
import java.util.*;
@ -53,21 +53,21 @@ public class GtkMenubar {
}
}
public static void launch(InstanceList.Entry instance) {
public static void launch(Instance instance) {
//TODO show popup during launch w/ cancel option (lock main UI)
Runnable launch = () -> {
try {
Steps.reDownload(instance.path(), Steps.createProcessState());
Steps.reDownload(instance, Steps.createProcessState());
} catch (IOException e) {
Utils.LOGGER.error("Could not redownload instance, trying to start anyways", e);
}
InstanceLauncher.launchClient(instance.path(), instance.meta());
InstanceLauncher.launchClient(instance);
};
if (InstanceLock.isSetupLocked(instance.path())) {
if (instance.isSetupLocked()) {
LauncherEnv.showError(I18n.get("instance.launch.locked.setup"), I18n.get("instance.launch.locked"));
return;
}
if (InstanceLock.isRunningLocked(instance.path())) {
if (instance.isRunningLocked()) {
LauncherEnv.showOkCancel(I18n.get("instance.launch.locked.running"), I18n.get("instance.launch.locked"), () -> {
new Thread(launch).start(); //TODO loom
}, R::nop);

View File

@ -4,17 +4,13 @@ import ch.bailu.gtk.GTK;
import ch.bailu.gtk.bridge.ListIndex;
import ch.bailu.gtk.gtk.*;
import ch.bailu.gtk.pango.EllipsizeMode;
import ch.bailu.gtk.type.Int;
import ch.bailu.gtk.type.Str;
import io.gitlab.jfronny.inceptum.common.Utils;
import io.gitlab.jfronny.inceptum.gtk.GtkMenubar;
import io.gitlab.jfronny.inceptum.gtk.util.I18n;
import io.gitlab.jfronny.inceptum.launcher.util.InstanceList;
import io.gitlab.jfronny.inceptum.launcher.model.inceptum.rt.Instance;
import java.util.List;
public class InstanceGridEntryFactory extends SignalListItemFactory {
public InstanceGridEntryFactory(List<InstanceList.Entry> instanceList) {
public InstanceGridEntryFactory(List<Instance> instanceList) {
super();
//TODO better design
onSetup(item -> {
@ -70,7 +66,7 @@ public class InstanceGridEntryFactory extends SignalListItemFactory {
InstanceThumbnail thumbnail = new InstanceThumbnail(box.getFirstChild().cast());
Label label = new Label(thumbnail.getNextSibling().cast());
InstanceList.Entry instance = instanceList.get(ListIndex.toIndex(item));
Instance instance = instanceList.get(ListIndex.toIndex(item));
thumbnail.bind(instance);
label.setText(new Str(instance.toString()));
//TODO right click menu + double click action

View File

@ -8,12 +8,12 @@ import ch.bailu.gtk.type.Str;
import io.gitlab.jfronny.inceptum.common.Utils;
import io.gitlab.jfronny.inceptum.gtk.GtkMenubar;
import io.gitlab.jfronny.inceptum.gtk.util.I18n;
import io.gitlab.jfronny.inceptum.launcher.util.InstanceList;
import io.gitlab.jfronny.inceptum.launcher.model.inceptum.rt.Instance;
import java.util.List;
public class InstanceListEntryFactory extends SignalListItemFactory {
public InstanceListEntryFactory(List<InstanceList.Entry> instanceList) {
public InstanceListEntryFactory(List<Instance> instanceList) {
super();
onSetup(item -> {
var thumbnail = new InstanceThumbnail();
@ -44,7 +44,7 @@ public class InstanceListEntryFactory extends SignalListItemFactory {
//TODO kill current instance
});
onBind(item -> {
InstanceList.Entry instance = instanceList.get(ListIndex.toIndex(item));
Instance instance = instanceList.get(ListIndex.toIndex(item));
ActionRow row = new ActionRow(item.getChild().cast());
Box prefixes = new Box(row.getFirstChild().getFirstChild().cast());
Box suffixes = new Box(row.getFirstChild().getLastChild().cast());

View File

@ -2,8 +2,7 @@ package io.gitlab.jfronny.inceptum.gtk.control;
import ch.bailu.gtk.gtk.*;
import ch.bailu.gtk.type.*;
import io.gitlab.jfronny.inceptum.launcher.util.InstanceList;
import io.gitlab.jfronny.inceptum.launcher.util.InstanceLock;
import io.gitlab.jfronny.inceptum.launcher.model.inceptum.rt.Instance;
public class InstanceThumbnail extends Stack {
private static final Str SPINNER = new Str("spinner");
@ -28,12 +27,12 @@ public class InstanceThumbnail extends Stack {
addNamed(generic, GENERIC);
}
public void bind(InstanceList.Entry entry) {
public void bind(Instance entry) {
var spinner = new Spinner(getChildByName(SPINNER).cast());
var image = new Image(getChildByName(IMAGE).cast()); //TODO
var generic = new Image(getChildByName(GENERIC).cast());
//TODO mark instance being played
if (InstanceLock.isSetupLocked(entry.path())) {
if (entry.isSetupLocked()) {
setVisibleChild(spinner);
} else if (false) { // if the instance has an image, load the image data and set it as the visible child
setVisibleChild(image);

View File

@ -14,6 +14,7 @@ import io.gitlab.jfronny.inceptum.gtk.control.InstanceGridEntryFactory;
import io.gitlab.jfronny.inceptum.gtk.control.InstanceListEntryFactory;
import io.gitlab.jfronny.inceptum.gtk.menu.MenuBuilder;
import io.gitlab.jfronny.inceptum.gtk.util.I18n;
import io.gitlab.jfronny.inceptum.launcher.model.inceptum.rt.Instance;
import io.gitlab.jfronny.inceptum.launcher.util.InstanceList;
import java.io.IOException;
@ -31,7 +32,7 @@ public class MainWindow extends ApplicationWindow {
private final StatusPage empty;
private final Clamp listView;
private final GridView gridView;
private final List<InstanceList.Entry> instanceList;
private final List<Instance> instanceList;
private final ListIndex instanceListIndex;
public MainWindow(Application app) {

View File

@ -9,10 +9,12 @@ import io.gitlab.jfronny.inceptum.imgui.window.GuiUtil;
import io.gitlab.jfronny.inceptum.launcher.model.fabric.FabricVersionLoaderInfo;
import io.gitlab.jfronny.inceptum.launcher.model.inceptum.InstanceMeta;
import io.gitlab.jfronny.inceptum.launcher.model.inceptum.LoaderInfo;
import io.gitlab.jfronny.inceptum.launcher.model.inceptum.rt.Instance;
import io.gitlab.jfronny.inceptum.launcher.model.mojang.*;
import io.gitlab.jfronny.inceptum.launcher.api.FabricMetaApi;
import io.gitlab.jfronny.inceptum.launcher.api.McApi;
import io.gitlab.jfronny.inceptum.launcher.util.InstanceNameTool;
import org.jetbrains.annotations.Nullable;
import java.io.IOException;
import java.nio.file.Files;
@ -34,22 +36,22 @@ public class InstanceManageControls {
private VersionsListInfo selected;
private FabricVersionLoaderInfo selectedFabric;
public InstanceManageControls(InstanceMeta meta, String defaultName) {
public InstanceManageControls(@Nullable Instance instance) {
selected = getVersions(false).get(0);
if (meta != null) {
if (instance != null) {
for (VersionsListInfo ver : getVersions(true)) {
if (ver.id.equals(meta.getMinecraftVersion()))
if (ver.id.equals(instance.meta().getMinecraftVersion()))
selected = ver;
}
}
version.set(getVersions(snapshots.get()).indexOf(selected));
name.set(defaultName == null ? InstanceNameTool.getDefaultName(selected.id, fabric.get()) : defaultName);
fabric.set(meta == null || meta.isFabric());
name.set(instance == null ? InstanceNameTool.getDefaultName(selected.id, fabric.get()) : instance.getName());
fabric.set(instance == null || instance.isFabric());
List<FabricVersionLoaderInfo> versions = getFabricLoaderInfo();
for (int i = 0, fabricLoaderInfoSize = versions.size(); i < fabricLoaderInfoSize; i++) {
FabricVersionLoaderInfo version = versions.get(i);
if (meta != null && meta.isFabric()
? version.loader.version.equals(meta.getLoaderVersion())
if (instance != null && instance.isFabric()
? version.loader.version.equals(instance.meta().getLoaderVersion())
: version.loader.stable) {
selectedFabric = version;
fabricVersion.set(i);

View File

@ -2,58 +2,56 @@ package io.gitlab.jfronny.inceptum.imgui.control;
import imgui.ImGui;
import imgui.flag.ImGuiTableFlags;
import io.gitlab.jfronny.commons.cache.FileBackedRef;
import io.gitlab.jfronny.inceptum.common.Utils;
import io.gitlab.jfronny.inceptum.imgui.GuiMain;
import io.gitlab.jfronny.inceptum.imgui.window.edit.InstanceEditWindow;
import io.gitlab.jfronny.inceptum.launcher.model.inceptum.InstanceMeta;
import io.gitlab.jfronny.inceptum.launcher.model.inceptum.rt.Instance;
import io.gitlab.jfronny.inceptum.launcher.system.launch.InstanceLauncher;
import io.gitlab.jfronny.inceptum.launcher.util.InstanceList;
import io.gitlab.jfronny.inceptum.launcher.util.InstanceLock;
import io.gitlab.jfronny.inceptum.launcher.system.install.Steps;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.*;
public class InstanceView {
public static void draw() throws IOException {
List<InstanceList.Entry> entries = new ArrayList<>();
//TODO clean up
List<Instance> entries = new ArrayList<>();
InstanceList.forEach(entries::add);
if (entries.isEmpty()) {
ImGui.text("You have not yet created an instance");
ImGui.text("Use File->New Instance to do so");
}
if (ImGui.beginTable("Instances", 2, ImGuiTableFlags.SizingFixedFit | ImGuiTableFlags.Borders)) {
for (InstanceList.Entry entry : entries) {
if (InstanceLock.isSetupLocked(entry.path())) {
for (Instance instance : entries) {
if (instance.isSetupLocked()) {
ImGui.tableNextColumn();
ImGui.text("Setting up");
ImGui.tableNextColumn();
ImGui.text("This instance is currently being set up");
continue;
}
if (!Files.exists(entry.path().resolve("instance.json"))) {
Utils.LOGGER.error("Invalid instance (doesn't contain instance.json): " + entry);
if (!Files.exists(instance.path().resolve("instance.json"))) {
Utils.LOGGER.error("Invalid instance (doesn't contain instance.json): " + instance);
continue;
}
ImGui.tableNextColumn();
boolean runDisabled = InstanceLock.isRunningLocked(entry.path());
boolean runDisabled = instance.isRunningLocked();
if (runDisabled) ImGui.beginDisabled();
if (ImGui.button(entry.toString())) {
if (ImGui.button(instance.toString())) {
try {
Steps.reDownload(entry.path(), Steps.createProcessState());
Steps.reDownload(instance, Steps.createProcessState());
} catch (IOException e) {
Utils.LOGGER.error("Could not redownload instance, trying to start anyways", e);
}
InstanceLauncher.launchClient(entry.path(), entry.meta());
InstanceLauncher.launchClient(instance);
}
if (runDisabled) ImGui.endDisabled();
ImGui.tableNextColumn();
if (ImGui.button("Edit##" + entry.path())) {
if (ImGui.button("Edit##" + instance.id())) {
try {
GuiMain.open(new InstanceEditWindow(entry.path(), entry.meta()));
GuiMain.open(new InstanceEditWindow(instance));
} catch (IOException e) {
Utils.LOGGER.error("Could not open instance edit window", e);
}

View File

@ -7,13 +7,14 @@ import io.gitlab.jfronny.inceptum.launcher.LauncherEnv;
import io.gitlab.jfronny.inceptum.common.Utils;
import io.gitlab.jfronny.inceptum.launcher.model.curseforge.CurseforgeMod;
import io.gitlab.jfronny.inceptum.launcher.model.inceptum.InstanceMeta;
import io.gitlab.jfronny.inceptum.launcher.model.inceptum.rt.Instance;
import io.gitlab.jfronny.inceptum.launcher.model.modrinth.*;
import io.gitlab.jfronny.inceptum.launcher.system.source.*;
import io.gitlab.jfronny.inceptum.launcher.util.ModManager;
import io.gitlab.jfronny.inceptum.launcher.util.ModPath;
import io.gitlab.jfronny.inceptum.launcher.api.CurseforgeApi;
import io.gitlab.jfronny.inceptum.launcher.api.ModrinthApi;
import io.gitlab.jfronny.inceptum.launcher.system.mds.IWModDescription;
import io.gitlab.jfronny.inceptum.launcher.model.inceptum.rt.Mod;
import io.gitlab.jfronny.inceptum.launcher.system.mds.ModsDirScanner;
import java.io.IOException;
@ -32,11 +33,11 @@ public class AddModWindow extends Window {
private ModrinthSearchResult mr = null;
private List<CurseforgeMod> cf = null;
public AddModWindow(Path modsDir, InstanceMeta instance, ModsDirScanner mds) {
super(modsDir.getParent().getFileName().toString() + " - Add Mods");
this.modsDir = modsDir;
this.instance = instance;
this.mds = mds;
public AddModWindow(Instance instance) throws IOException {
super(instance.getName() + " - Add Mods");
this.modsDir = instance.modsDir();
this.instance = instance.meta();
this.mds = instance.getMds();
}
private void reSearch() throws IOException {
@ -92,7 +93,7 @@ public class AddModWindow extends Window {
ImGui.text(mod.description);
ImGui.tableNextColumn();
boolean alreadyPresent = false;
for (IWModDescription mdsMod : mds.getMods()) {
for (Mod mdsMod : mds.getMods()) {
alreadyPresent = mdsMod.mod().isPresent()
&& mdsMod.mod().get().sources.keySet().stream()
.anyMatch(s -> s instanceof ModrinthModSource ms
@ -168,7 +169,7 @@ public class AddModWindow extends Window {
ImGui.text(mod.summary);
ImGui.tableNextColumn();
boolean alreadyPresent = false;
for (IWModDescription mdsMod : mds.getMods()) {
for (Mod mdsMod : mds.getMods()) {
alreadyPresent = mdsMod.mod().isPresent()
&& mdsMod.mod().get().sources.keySet().stream()
.anyMatch(s -> s instanceof CurseforgeModSource ms

View File

@ -6,7 +6,9 @@ import io.gitlab.jfronny.inceptum.common.MetaHolder;
import io.gitlab.jfronny.inceptum.common.Utils;
import io.gitlab.jfronny.inceptum.imgui.GuiMain;
import io.gitlab.jfronny.inceptum.imgui.window.dialog.ProcessStateWatcherWindow;
import io.gitlab.jfronny.inceptum.launcher.model.inceptum.rt.Instance;
import io.gitlab.jfronny.inceptum.launcher.system.install.*;
import io.gitlab.jfronny.inceptum.launcher.util.InstanceList;
import io.gitlab.jfronny.inceptum.launcher.util.ProcessState;
import java.io.IOException;
@ -21,19 +23,18 @@ public class GuiUtil {
List<Path> paths = JFiles.list(MetaHolder.INSTANCE_DIR);
ProcessState state = Steps.createProcessState().extend(paths.size());
GuiMain.open(new ProcessStateWatcherWindow("Reloading data", "Could not reload resources", state, cToken -> {
for (Path path : paths) {
Steps.reDownload(path, state);
}
InstanceList.reset();
InstanceList.forEach(instance -> Steps.reDownload(instance, state));
}, null));
} catch (IOException e) {
LauncherEnv.showError("Could not reload", e);
}
}
public static void reload(Path instanceDir) {
public static void reload(Instance instance) {
ProcessState state = Steps.createProcessState();
GuiMain.open(new ProcessStateWatcherWindow("Reloading data", "Could not reload resources", state, cToken -> {
Steps.reDownload(instanceDir, state);
Steps.reDownload(instance, state);
}, null));
}

View File

@ -17,7 +17,7 @@ import java.nio.file.Path;
import java.util.List;
public class NewInstanceWindow extends Window {
private final InstanceManageControls imc = new InstanceManageControls(null, null);
private final InstanceManageControls imc = new InstanceManageControls(null);
public NewInstanceWindow() {
super("New Instance");

View File

@ -18,28 +18,29 @@ public class ArgumentsTab extends Tab {
public ArgumentsTab(InstanceEditWindow window) {
super("Arguments");
this.window = window;
if (window.instance.arguments == null) window.instance.arguments = new InstanceMeta.Arguments();
if (window.instance.arguments.jvm == null) window.instance.arguments.jvm = new LinkedList<>();
jvm.set(String.join("\n", window.instance.arguments.jvm));
if (window.instance.arguments.client == null) window.instance.arguments.client = new LinkedList<>();
client.set(String.join("\n", window.instance.arguments.client));
if (window.instance.arguments.server == null) window.instance.arguments.server = new LinkedList<>();
server.set(String.join("\n", window.instance.arguments.server));
InstanceMeta meta = window.instance.meta();
if (meta.arguments == null) meta.arguments = new InstanceMeta.Arguments();
if (meta.arguments.jvm == null) meta.arguments.jvm = new LinkedList<>();
jvm.set(String.join("\n", meta.arguments.jvm));
if (meta.arguments.client == null) meta.arguments.client = new LinkedList<>();
client.set(String.join("\n", meta.arguments.client));
if (meta.arguments.server == null) meta.arguments.server = new LinkedList<>();
server.set(String.join("\n", meta.arguments.server));
}
@Override
protected void renderInner() {
if (ImGui.inputTextMultiline("JVM", jvm)) {
window.instance.arguments.jvm = List.of(jvm.get().split("[\r\n]+"));
window.save();
window.instance.meta().arguments.jvm = List.of(jvm.get().split("[\r\n]+"));
window.instance.writeMeta();
}
if (ImGui.inputTextMultiline("Client", client)) {
window.instance.arguments.client = List.of(client.get().split("[\r\n]+"));
window.save();
window.instance.meta().arguments.client = List.of(client.get().split("[\r\n]+"));
window.instance.writeMeta();
}
if (ImGui.inputTextMultiline("Server", server)) {
window.instance.arguments.server = List.of(server.get().split("[\r\n]+"));
window.save();
window.instance.meta().arguments.server = List.of(server.get().split("[\r\n]+"));
window.instance.writeMeta();
}
}
}

View File

@ -28,14 +28,14 @@ public class ExportTab extends Tab {
for (Exporter<?> exporter : Exporters.EXPORTERS) {
if (ImGui.button(exporter.getName())) {
GuiMain.open(new TextBoxWindow("Version", "Please enter the current version of your modpack", "1.0", version -> {
String defaultName = window.name + " " + version + " (" + exporter.getName() + ")." + exporter.getFileExtension();
String defaultName = window.instance.getName() + " " + version + " (" + exporter.getName() + ")." + exporter.getFileExtension();
String filter = "*." + exporter.getFileExtension();
Path exportPath = GuiMain.saveFileDialog("Export " + exporter.getName() + " Pack", defaultName, new String[] {filter}, exporter.getName() + " packs (" + filter + ")");
if (exportPath != null) {
ProcessState state = new ProcessState(Exporters.STEP_COUNT, "Initializing...");
GuiMain.open(new ProcessStateWatcherWindow("Exporting", "Could not export pack", state, cToken -> {
exporter.generate(state, window.path, window.instance, window.mds, exportPath, version);
LauncherEnv.showInfo(window.name + " has been successfully exported to " + exportPath, "Successfully exported");
exporter.generate(state, window.instance, exportPath, version);
LauncherEnv.showInfo(window.instance.getName() + " has been successfully exported to " + exportPath, "Successfully exported");
}, null));
}
}, R::nop));

View File

@ -25,20 +25,20 @@ public class GeneralTab extends Tab {
public GeneralTab(InstanceEditWindow window) {
super("General");
this.window = window;
imc = new InstanceManageControls(window.instance, window.name);
customJava = new ImBoolean(window.instance.java != null);
imc = new InstanceManageControls(window.instance);
customJava = new ImBoolean(window.instance.meta().java != null);
}
@Override
protected void renderInner() {
if (ImGui.button("Open Directory")) {
Utils.openFile(window.path.toFile());
Utils.openFile(window.instance.path().toFile());
}
imc.nameBox("Rename", name -> {
try {
Path newPath = MetaHolder.INSTANCE_DIR.resolve(name);
Files.move(window.path, newPath);
GuiMain.open(new InstanceEditWindow(newPath, window.instance));
Files.move(window.instance.path(), newPath);
GuiMain.open(new InstanceEditWindow(window.instance));
window.close();
} catch (IOException e) {
LauncherEnv.showError("Could not rename", e);
@ -47,13 +47,13 @@ public class GeneralTab extends Tab {
imc.snapshotsBox();
imc.versionBox(ver -> {
window.reDownload = true;
window.instance.version = ver;
window.save();
window.instance.meta().version = ver;
window.instance.writeMeta();
});
if (ImGui.button("Delete"))
LauncherEnv.showOkCancel("This instance will be removed forever (a long time)", "Are you sure?", () -> {
try {
JFiles.deleteRecursive(window.path);
JFiles.deleteRecursive(window.instance.path());
} catch (IOException e) {
LauncherEnv.showError("Could not delete the instance", e);
}
@ -61,16 +61,16 @@ public class GeneralTab extends Tab {
}, R::nop);
if (ImGui.checkbox("Custom Java", customJava)) {
if (customJava.get()) {
window.instance.java = OSUtils.getJvmBinary();
customJavaPath.set(window.instance.java);
window.instance.meta().java = OSUtils.getJvmBinary();
customJavaPath.set(window.instance.meta().java);
} else {
window.instance.java = null;
window.instance.meta().java = null;
}
window.save();
window.instance.writeMeta();
}
if (customJava.get() && ImGui.inputText("Path", customJavaPath)) {
window.instance.java = customJavaPath.get();
window.save();
window.instance.meta().java = customJavaPath.get();
window.instance.writeMeta();
}
}
}

View File

@ -1,34 +1,26 @@
package io.gitlab.jfronny.inceptum.imgui.window.edit;
import imgui.ImGui;
import io.gitlab.jfronny.commons.io.JFiles;
import io.gitlab.jfronny.inceptum.common.Utils;
import io.gitlab.jfronny.inceptum.imgui.control.Tab;
import io.gitlab.jfronny.inceptum.imgui.window.GuiUtil;
import io.gitlab.jfronny.inceptum.imgui.window.Window;
import io.gitlab.jfronny.inceptum.launcher.model.inceptum.InstanceMeta;
import io.gitlab.jfronny.inceptum.launcher.util.InstanceLock;
import io.gitlab.jfronny.inceptum.launcher.model.inceptum.rt.Instance;
import io.gitlab.jfronny.inceptum.launcher.system.mds.ModsDirScanner;
import java.io.IOException;
import java.nio.file.Path;
import java.util.List;
public class InstanceEditWindow extends Window {
protected final Path path;
protected final String name;
protected final InstanceMeta instance;
protected final Instance instance;
protected final ModsDirScanner mds;
private final List<Tab> tabs;
protected boolean reDownload = false;
protected boolean lastTabWasMods = false;
public InstanceEditWindow(Path path, InstanceMeta instance) throws IOException {
super(path.getFileName().toString() + " - Edit");
this.name = path.getFileName().toString();
this.path = path;
public InstanceEditWindow(Instance instance) throws IOException {
super(instance.getName() + " - Edit");
this.instance = instance;
this.mds = ModsDirScanner.get(path.resolve("mods"), instance);
this.mds = instance.getMds();
this.mds.start();
this.tabs = List.of(
new GeneralTab(this),
@ -40,33 +32,25 @@ public class InstanceEditWindow extends Window {
@Override
public void draw() {
if (InstanceLock.isSetupLocked(path)) {
if (instance.isSetupLocked()) {
ImGui.text("This instance is still being set up.");
return;
}
if (InstanceLock.isRunningLocked(path)) {
if (instance.isRunningLocked()) {
ImGui.text("This instance is running. Edits in this state will result in breakage.");
}
lastTabWasMods = false;
if (ImGui.beginTabBar("InstanceEdit" + path)) {
if (ImGui.beginTabBar("InstanceEdit" + instance.id())) {
for (Tab tab : tabs) tab.render();
ImGui.endTabBar();
}
}
protected void save() {
try {
JFiles.writeObject(path.resolve("instance.json"), instance);
} catch (IOException e) {
Utils.LOGGER.error("Could not write instance config", e);
}
}
@Override
public void close() {
super.close();
if (reDownload) {
GuiUtil.reload(path);
GuiUtil.reload(instance);
}
}

View File

@ -9,7 +9,7 @@ import io.gitlab.jfronny.inceptum.imgui.control.Tab;
import io.gitlab.jfronny.inceptum.imgui.window.AddModWindow;
import io.gitlab.jfronny.inceptum.launcher.util.ModManager;
import io.gitlab.jfronny.inceptum.launcher.util.ModPath;
import io.gitlab.jfronny.inceptum.launcher.system.mds.IWModDescription;
import io.gitlab.jfronny.inceptum.launcher.model.inceptum.rt.Mod;
import io.gitlab.jfronny.inceptum.launcher.system.source.ModSource;
import java.io.IOException;
@ -30,31 +30,35 @@ public class ModsTab extends Tab {
@Override
protected void renderInner() {
window.lastTabWasMods = true;
if (!Files.exists(window.path.resolve("mods"))) {
if (!Files.exists(window.instance.modsDir())) {
try {
Files.createDirectories(window.path.resolve("mods"));
Files.createDirectories(window.instance.modsDir());
} catch (IOException e) {
Utils.LOGGER.error("Could not create mods directory which was missing from this modded instance", e);
}
}
ImGui.beginChild("mods select", 200, 0);
if (ImGui.button("Add")) {
GuiMain.WINDOWS.add(new AddModWindow(window.path.resolve("mods"), window.instance, window.mds));
try {
GuiMain.WINDOWS.add(new AddModWindow(window.instance));
} catch (IOException e) {
Utils.LOGGER.error("Could not open window", e);
}
}
ImGui.sameLine();
if (Files.exists(window.path.resolve("mods")) && ImGui.button("Show")) {
Utils.openFile(window.path.resolve("mods").toFile());
if (Files.exists(window.instance.modsDir()) && ImGui.button("Show")) {
Utils.openFile(window.instance.modsDir().toFile());
}
ImGui.sameLine();
if (Files.exists(window.path.resolve("config")) && ImGui.button("Configs")) {
Utils.openFile(window.path.resolve("config").toFile());
if (Files.exists(window.instance.configDir()) && ImGui.button("Configs")) {
Utils.openFile(window.instance.configDir().toFile());
}
try {
Set<IWModDescription> modSet = window.mds.getMods();
Set<Mod> modSet = window.mds.getMods();
boolean updatesFound = false;
float scannedPercentage = 0;
boolean hasUnScanned = false;
for (IWModDescription mod : modSet) {
for (Mod mod : modSet) {
if (window.mds.hasScanned(mod.path())) scannedPercentage++;
else hasUnScanned = true;
if (mod.mod().isEmpty()) continue;
@ -74,7 +78,7 @@ public class ModsTab extends Tab {
else
filterUpdates.set(false);
ImGui.separator();
for (IWModDescription mod : modSet) {
for (Mod mod : modSet) {
updatesFound = false;
if (mod.mod().isPresent()) {
for (Optional<ModSource> value : mod.mod().get().sources.values()) {
@ -88,7 +92,7 @@ public class ModsTab extends Tab {
try {
Files.move(mod.path(), newSel);
if (mod.path().equals(selected)) selected = newSel;
mod = new IWModDescription(newSel, mod);
mod = new Mod(newSel, mod);
} catch (IOException e) {
LauncherEnv.showError("Could not change disabled state", e);
}
@ -105,7 +109,7 @@ public class ModsTab extends Tab {
if (selected == null) {
ImGui.text("Select a mod to view settings");
} else if (window.mds.hasScanned(selected)) {
IWModDescription md = window.mds.get(selected);
Mod md = window.mds.get(selected);
ImGui.text(md.getName());
ImGui.separator();
for (String s : md.getDescription()) {
@ -125,7 +129,7 @@ public class ModsTab extends Tab {
ImGui.sameLine();
if (ImGui.button("Update to " + ms.get().getVersion())) {
try {
Path imodPath = md.imod().isPresent() ? md.imod().get() : window.path.resolve("mods").resolve(ms.get().getShortName() + ModPath.EXT_IMOD);
Path imodPath = md.imod().isPresent() ? md.imod().get() : window.instance.modsDir().resolve(ms.get().getShortName() + ModPath.EXT_IMOD);
ModManager.DownloadMeta dm = ModManager.download(ms.get(), imodPath, window.mds);
Files.delete(md.path());
if (md.imod().isPresent() && Files.exists(md.imod().get()))
@ -159,8 +163,8 @@ public class ModsTab extends Tab {
ImGui.endGroup();
}
private void delete(IWModDescription md) throws IOException {
ModManager.delete(md, window.path.resolve("mods"), window.mds);
private void delete(Mod md) throws IOException {
ModManager.delete(md, window.instance.modsDir(), window.mds);
}
@Override

View File

@ -0,0 +1,126 @@
package io.gitlab.jfronny.inceptum.launcher.model.inceptum.rt;
import io.gitlab.jfronny.commons.io.JFiles;
import io.gitlab.jfronny.inceptum.common.Utils;
import io.gitlab.jfronny.inceptum.launcher.model.inceptum.InstanceMeta;
import io.gitlab.jfronny.inceptum.launcher.system.mds.ModsDirScanner;
import io.gitlab.jfronny.inceptum.launcher.util.ProcessUtils;
import org.jetbrains.annotations.NotNull;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
public record Instance(String id, Path path, InstanceMeta meta) implements Comparable<Instance> {
private static final String INCEPTUM_LOCK = "inceptum.lock";
private static final String INCEPTUM_SETUP_LOCK = "inceptum.setup.lock";
public Instance(Path path, InstanceMeta meta) {
this(generateId(path.getFileName().toString()), path, meta);
}
public Instance(String id, Path path, InstanceMeta meta) {
this.id = id;
this.path = path.toAbsolutePath().normalize();
this.meta = meta;
}
/**
* Converts any string into a set of lowercase ascii chars
* @param input string to convert
* @return a string matching [a-p]*
*/
private static String generateId(String input) {
StringBuilder result = new StringBuilder();
for (byte b : input.getBytes(StandardCharsets.UTF_8)) {
int by = Byte.toUnsignedInt(b);
int ch2 = by & 15; // right bits
int ch1 = (by - ch2) / 16; // left bits
result.append((char)('a' + ch1));
result.append((char)('a' + ch2));
}
return result.toString();
}
@Override
public int compareTo(@NotNull Instance entry) {
long time1 = meta.lastLaunched == null ? 0 : meta.lastLaunched;
long time2 = entry.meta.lastLaunched == null ? 0 : entry.meta.lastLaunched;
if (time1 == 0) {
if (time2 == 0) return path.getFileName().toString().compareTo(entry.path.getFileName().toString());
return -1;
}
if (time2 == 0) return 1;
return Long.compare(time1, time2);
}
@Override
public String toString() {
return path.getFileName().toString();
}
public Path modsDir() {
return path.resolve("mods");
}
public Path configDir() {
return path.resolve("config");
}
public ModsDirScanner getMds() throws IOException {
return ModsDirScanner.get(modsDir(), meta);
}
public String getName() {
return path.getFileName().toString();
}
public boolean isFabric() {
return meta.isFabric();
}
public boolean isSetupLocked() {
return Files.exists(path.resolve(INCEPTUM_SETUP_LOCK));
}
public void setSetupLock(boolean state) throws IOException {
setSetupLock(path, state);
}
public static void setSetupLock(Path instanceDir, boolean state) throws IOException {
if (Files.exists(instanceDir.resolve(INCEPTUM_SETUP_LOCK))) {
if (!state) Files.delete(instanceDir.resolve(INCEPTUM_SETUP_LOCK));
} else {
if (state) Files.createDirectories(instanceDir.resolve(INCEPTUM_SETUP_LOCK));
}
}
public boolean isRunningLocked() {
if (!Files.exists(path.resolve(INCEPTUM_LOCK))) return false;
try {
if (ProcessUtils.isProcessAlive(Files.readString(path.resolve(INCEPTUM_LOCK))))
return true;
Files.delete(path.resolve(INCEPTUM_LOCK));
} catch (IOException e) {
Utils.LOGGER.error("Could not read running lock of " + getName(), e);
}
return false;
}
public void setRunningLock(long pid) throws IOException {
Files.writeString(path.resolve(INCEPTUM_LOCK), Long.toString(pid));
}
public boolean isLocked() {
return isSetupLocked() || isRunningLocked();
}
public void writeMeta() {
try {
JFiles.writeObject(path.resolve("instance.json"), meta);
} catch (IOException e) {
Utils.LOGGER.error("Could not write instance config", e);
}
}
}

View File

@ -1,4 +1,4 @@
package io.gitlab.jfronny.inceptum.launcher.system.mds;
package io.gitlab.jfronny.inceptum.launcher.model.inceptum.rt;
import io.gitlab.jfronny.commons.io.JFiles;
import io.gitlab.jfronny.inceptum.common.Utils;
@ -11,8 +11,8 @@ import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Optional;
public record IWModDescription(Path path, Optional<ModDescription> mod, Optional<FabricModJson> fmj,
Optional<Path> imod) implements Comparable<IWModDescription> {
public record Mod(Path path, Optional<ModDescription> mod, Optional<FabricModJson> fmj,
Optional<Path> imod) implements Comparable<Mod> {
public String getName() {
if (fmj.isEmpty()) return path.getFileName().toString();
String base;
@ -45,7 +45,7 @@ public record IWModDescription(Path path, Optional<ModDescription> mod, Optional
*
* @param path The path of the mod entry
*/
public IWModDescription(Path path) {
public Mod(Path path) {
this(path,
Files.isDirectory(path) ? Optional.empty() : Optional.of(ModDescription.of(path)),
Optional.empty(),
@ -58,12 +58,12 @@ public record IWModDescription(Path path, Optional<ModDescription> mod, Optional
* @param path The new path to use
* @param base The mod description to copy
*/
public IWModDescription(Path path, IWModDescription base) {
public Mod(Path path, Mod base) {
this(path, base.mod, base.fmj, base.imod);
}
@Override
public int compareTo(IWModDescription o) {
public int compareTo(Mod o) {
return getName().compareTo(o.getName());
}
}

View File

@ -4,7 +4,8 @@ import io.gitlab.jfronny.commons.io.JFiles;
import io.gitlab.jfronny.inceptum.common.InceptumConfig;
import io.gitlab.jfronny.inceptum.launcher.model.curseforge.CurseforgeModpackManifest;
import io.gitlab.jfronny.inceptum.launcher.model.inceptum.InstanceMeta;
import io.gitlab.jfronny.inceptum.launcher.system.mds.IWModDescription;
import io.gitlab.jfronny.inceptum.launcher.model.inceptum.rt.Instance;
import io.gitlab.jfronny.inceptum.launcher.model.inceptum.rt.Mod;
import io.gitlab.jfronny.inceptum.launcher.system.source.CurseforgeModSource;
import io.gitlab.jfronny.inceptum.launcher.system.source.ModSource;
import io.gitlab.jfronny.inceptum.launcher.util.ModPath;
@ -23,20 +24,20 @@ public class CurseForgeExporter extends Exporter<CurseforgeModpackManifest> {
}
@Override
protected CurseforgeModpackManifest generateManifests(Path root, InstanceMeta instance, String name, String version) throws IOException {
protected CurseforgeModpackManifest generateManifests(Path root, Instance instance, String version) throws IOException {
CurseforgeModpackManifest manifest = new CurseforgeModpackManifest();
manifest.minecraft = new CurseforgeModpackManifest.Minecraft();
manifest.minecraft.version = instance.getMinecraftVersion();
manifest.minecraft.version = instance.meta().getMinecraftVersion();
manifest.manifestType = "minecraftModpack";
manifest.manifestVersion = 1;
manifest.name = name;
manifest.name = instance.getName();
manifest.version = version;
manifest.author = InceptumConfig.authorName;
manifest.overrides = OVERRIDES_DIR_DEFAULT;
manifest.minecraft.modLoaders = new LinkedHashSet<>();
if (instance.isFabric()) {
CurseforgeModpackManifest.Minecraft.ModLoader loader = new CurseforgeModpackManifest.Minecraft.ModLoader();
loader.id = "fabric-" + instance.getLoaderVersion();
loader.id = "fabric-" + instance.meta().getLoaderVersion();
loader.primary = true;
manifest.minecraft.modLoaders.add(loader);
}
@ -45,8 +46,8 @@ public class CurseForgeExporter extends Exporter<CurseforgeModpackManifest> {
}
@Override
protected void addMods(Path root, InstanceMeta instance, Iterable<IWModDescription> mods, CurseforgeModpackManifest manifest, Path modsOverrides) throws IOException {
modsLoop: for(IWModDescription mod : mods) {
protected void addMods(Path root, Instance instance, Iterable<Mod> mods, CurseforgeModpackManifest manifest, Path modsOverrides) throws IOException {
modsLoop: for(Mod mod : mods) {
if (ModPath.isImod(mod.path())) {
Set<ModSource> sources = mod.mod().orElseThrow().sources.keySet();
for (ModSource source : sources) {

View File

@ -2,7 +2,8 @@ package io.gitlab.jfronny.inceptum.launcher.system.export;
import io.gitlab.jfronny.inceptum.common.Utils;
import io.gitlab.jfronny.inceptum.launcher.model.inceptum.InstanceMeta;
import io.gitlab.jfronny.inceptum.launcher.system.mds.IWModDescription;
import io.gitlab.jfronny.inceptum.launcher.model.inceptum.rt.Instance;
import io.gitlab.jfronny.inceptum.launcher.model.inceptum.rt.Mod;
import io.gitlab.jfronny.inceptum.launcher.system.mds.ModsDirScanner;
import io.gitlab.jfronny.inceptum.launcher.util.*;
import io.gitlab.jfronny.inceptum.launcher.util.ignore.IgnoringWalk;
@ -23,28 +24,29 @@ public abstract class Exporter<Manifest> {
this.overridesDirName = Objects.requireNonNull(overridesDirName);
}
protected abstract Manifest generateManifests(Path root, InstanceMeta instance, String name, String version) throws IOException;
protected abstract void addMods(Path root, InstanceMeta instance, Iterable<IWModDescription> mods, Manifest manifest, Path modsOverrides) throws IOException;
protected abstract Manifest generateManifests(Path root, Instance instance, String version) throws IOException;
protected abstract void addMods(Path root, Instance instance, Iterable<Mod> mods, Manifest manifest, Path modsOverrides) throws IOException;
public void generate(ProcessState state, Path instanceDir, InstanceMeta meta, ModsDirScanner mds, Path exportPath, String version) throws IOException {
public void generate(ProcessState state, Instance instance, Path exportPath, String version) throws IOException {
if (Files.exists(exportPath)) Files.delete(exportPath);
try (FileSystem fs = Utils.openZipFile(exportPath, true)) {
Path root = fs.getPath(".");
Path overrides = fs.getPath(overridesDirName);
state.incrementStep("Preparing manifests");
Manifest manifest = generateManifests(root, meta, instanceDir.getFileName().toString(), version);
if (meta.isFabric()) {
Manifest manifest = generateManifests(root, instance, version);
if (instance.isFabric()) {
state.incrementStep("Adding mods");
ModsDirScanner mds = instance.getMds();
if (!mds.isComplete()) throw new IOException("Mods dir scan is not yet completed");
addMods(root, meta, new StreamIterable<>(mds.getMods().stream().filter(mod -> {
addMods(root, instance, new StreamIterable<>(mds.getMods().stream().filter(mod -> {
if (!ModPath.isEnabled(mod.path())) return false;
state.updateStep(mod.path().toString());
return true;
})), manifest, overrides.resolve("mods"));
}
state.incrementStep("Adding files");
filesLoop: for (Path path : new StreamIterable<>(IgnoringWalk.walk(instanceDir))) {
Path relativePath = instanceDir.relativize(path).normalize();
filesLoop: for (Path path : new StreamIterable<>(IgnoringWalk.walk(instance.path()))) {
Path relativePath = instance.path().relativize(path).normalize();
Path target = overrides;
for (Path segment : relativePath) {
if (target == overrides && segment.toString().equals("mods")) continue filesLoop;

View File

@ -2,8 +2,9 @@ package io.gitlab.jfronny.inceptum.launcher.system.export;
import io.gitlab.jfronny.commons.io.JFiles;
import io.gitlab.jfronny.inceptum.launcher.model.inceptum.InstanceMeta;
import io.gitlab.jfronny.inceptum.launcher.model.inceptum.rt.Instance;
import io.gitlab.jfronny.inceptum.launcher.model.modrinth.ModrinthModpackManifest;
import io.gitlab.jfronny.inceptum.launcher.system.mds.IWModDescription;
import io.gitlab.jfronny.inceptum.launcher.model.inceptum.rt.Mod;
import io.gitlab.jfronny.inceptum.launcher.system.source.ModSource;
import io.gitlab.jfronny.inceptum.launcher.system.source.ModrinthModSource;
import io.gitlab.jfronny.inceptum.launcher.util.ModPath;
@ -21,25 +22,25 @@ public class ModrinthExporter extends Exporter<ModrinthModpackManifest> {
}
@Override
protected ModrinthModpackManifest generateManifests(Path root, InstanceMeta instance, String name, String version) throws IOException {
protected ModrinthModpackManifest generateManifests(Path root, Instance instance, String version) throws IOException {
ModrinthModpackManifest manifest = new ModrinthModpackManifest();
manifest.formatVersion = 1;
manifest.game = "minecraft";
manifest.versionId = version;
manifest.name = name;
manifest.name = instance.getName();
manifest.files = new ArrayList<>();
manifest.dependencies = new ModrinthModpackManifest.Dependencies();
manifest.dependencies.minecraft = instance.getMinecraftVersion();
manifest.dependencies.minecraft = instance.meta().getMinecraftVersion();
if (instance.isFabric()) {
manifest.dependencies.fabricLoader = instance.getLoaderVersion();
manifest.dependencies.fabricLoader = instance.meta().getLoaderVersion();
}
JFiles.writeObject(root.resolve("modrinth.index.json"), manifest);
return manifest;
}
@Override
protected void addMods(Path root, InstanceMeta instance, Iterable<IWModDescription> mods, ModrinthModpackManifest manifest, Path modsOverrides) throws IOException {
modsLoop: for(IWModDescription mod : mods) {
protected void addMods(Path root, Instance instance, Iterable<Mod> mods, ModrinthModpackManifest manifest, Path modsOverrides) throws IOException {
modsLoop: for(Mod mod : mods) {
if (ModPath.isImod(mod.path())) {
Set<ModSource> sources = mod.mod().orElseThrow().sources.keySet();
for (ModSource source : sources) {

View File

@ -2,8 +2,9 @@ package io.gitlab.jfronny.inceptum.launcher.system.export;
import io.gitlab.jfronny.commons.io.JFiles;
import io.gitlab.jfronny.inceptum.launcher.model.inceptum.InstanceMeta;
import io.gitlab.jfronny.inceptum.launcher.model.inceptum.rt.Instance;
import io.gitlab.jfronny.inceptum.launcher.model.multimc.MMCPackMeta;
import io.gitlab.jfronny.inceptum.launcher.system.mds.IWModDescription;
import io.gitlab.jfronny.inceptum.launcher.model.inceptum.rt.Mod;
import io.gitlab.jfronny.inceptum.launcher.system.source.ModSource;
import io.gitlab.jfronny.inceptum.launcher.util.ModPath;
@ -21,7 +22,7 @@ public class MultiMCExporter extends Exporter<MMCPackMeta> {
}
@Override
protected MMCPackMeta generateManifests(Path root, InstanceMeta instance, String name, String version) throws IOException {
protected MMCPackMeta generateManifests(Path root, Instance instance, String version) throws IOException {
{
Files.writeString(root.resolve("instance.cfg"), String.format("""
ForgeVersion=
@ -47,7 +48,7 @@ public class MultiMCExporter extends Exporter<MMCPackMeta> {
name=%s
notes=
totalTimePlayed=0
""", Instant.now().toEpochMilli(), name));
""", Instant.now().toEpochMilli(), instance.getName()));
}
{
MMCPackMeta manifest = new MMCPackMeta();
@ -61,17 +62,17 @@ public class MultiMCExporter extends Exporter<MMCPackMeta> {
MMCPackMeta.Component minecraft = new MMCPackMeta.Component();
minecraft.important = true;
minecraft.uid = "net.minecraft";
minecraft.version = instance.getMinecraftVersion();
minecraft.version = instance.meta().getMinecraftVersion();
manifest.components.add(minecraft);
if (instance.isFabric()) {
MMCPackMeta.Component intermediary = new MMCPackMeta.Component();
intermediary.dependencyOnly = true;
intermediary.uid = "net.fabricmc.intermediary";
intermediary.version = instance.getMinecraftVersion();
intermediary.version = instance.meta().getMinecraftVersion();
manifest.components.add(intermediary);
MMCPackMeta.Component fabric = new MMCPackMeta.Component();
fabric.uid = "net.fabricmc.fabric-loader";
fabric.version = instance.getLoaderVersion();
fabric.version = instance.meta().getLoaderVersion();
manifest.components.add(fabric);
}
JFiles.writeObject(root.resolve("mmc-pack.json"), manifest);
@ -80,8 +81,8 @@ public class MultiMCExporter extends Exporter<MMCPackMeta> {
}
@Override
protected void addMods(Path root, InstanceMeta instance, Iterable<IWModDescription> mods, MMCPackMeta mmcPackMeta, Path modsOverrides) throws IOException {
modsLoop: for (IWModDescription mod : mods) {
protected void addMods(Path root, Instance instance, Iterable<Mod> mods, MMCPackMeta mmcPackMeta, Path modsOverrides) throws IOException {
modsLoop: for (Mod mod : mods) {
if (ModPath.isImod(mod.path())) {
Set<ModSource> sources = mod.mod().orElseThrow().sources.keySet();
for (ModSource source : sources) {

View File

@ -6,6 +6,7 @@ import io.gitlab.jfronny.inceptum.common.Utils;
import io.gitlab.jfronny.inceptum.launcher.api.FabricMetaApi;
import io.gitlab.jfronny.inceptum.launcher.api.McApi;
import io.gitlab.jfronny.inceptum.launcher.model.inceptum.InstanceMeta;
import io.gitlab.jfronny.inceptum.launcher.model.inceptum.rt.Instance;
import io.gitlab.jfronny.inceptum.launcher.system.install.Steps;
import io.gitlab.jfronny.inceptum.launcher.util.*;
import org.jetbrains.annotations.Nullable;
@ -52,7 +53,7 @@ public abstract class Importer<T> {
if (name == null || !Utils.VALID_FILENAME.matcher(name).matches()) name = "New Pack";
name = InstanceNameTool.getNextValid(name);
final Path iDir = MetaHolder.INSTANCE_DIR.resolve(name).toAbsolutePath().normalize();
InstanceLock.setSetupLock(iDir, true);
Instance.setSetupLock(iDir, true);
InstanceMeta meta = new InstanceMeta();
meta.version = createVersionString(man.gameVersion(), man.fabricVersion());
JFiles.writeObject(iDir.resolve("instance.json"), meta);
@ -67,8 +68,8 @@ public abstract class Importer<T> {
JFiles.copyRecursive(path, iDir.resolve(path.getFileName().toString()));
});
}
InstanceLock.setSetupLock(iDir, false);
Steps.reDownload(iDir, state);
Instance.setSetupLock(iDir, false);
Steps.reDownload(InstanceList.read(iDir), state);
return iDir;
}

View File

@ -1,18 +1,15 @@
package io.gitlab.jfronny.inceptum.launcher.system.install;
import io.gitlab.jfronny.commons.io.JFiles;
import io.gitlab.jfronny.inceptum.launcher.model.inceptum.InstanceMeta;
import io.gitlab.jfronny.inceptum.launcher.api.FabricMetaApi;
import io.gitlab.jfronny.inceptum.launcher.api.McApi;
import io.gitlab.jfronny.inceptum.launcher.model.inceptum.LoaderInfo;
import io.gitlab.jfronny.inceptum.launcher.model.inceptum.rt.Instance;
import io.gitlab.jfronny.inceptum.launcher.model.mojang.VersionInfo;
import io.gitlab.jfronny.inceptum.launcher.model.mojang.VersionsListInfo;
import io.gitlab.jfronny.inceptum.launcher.system.install.steps.*;
import io.gitlab.jfronny.inceptum.launcher.util.InstanceLock;
import io.gitlab.jfronny.inceptum.launcher.util.ProcessState;
import io.gitlab.jfronny.inceptum.launcher.api.FabricMetaApi;
import io.gitlab.jfronny.inceptum.launcher.api.McApi;
import java.io.IOException;
import java.nio.file.Path;
import java.util.*;
import java.util.concurrent.atomic.AtomicBoolean;
@ -31,26 +28,25 @@ public class Steps {
return new ProcessState(STEPS.size(), "Initializing");
}
public static void reDownload(Path instance, ProcessState state) throws IOException {
if (InstanceLock.isLocked(instance)) return;
InstanceMeta im = JFiles.readObject(instance.resolve("instance.json"), InstanceMeta.class);
public static void reDownload(Instance instance, ProcessState state) throws IOException {
if (instance.isLocked()) return;
boolean found = false;
for (VersionsListInfo version : McApi.getVersions().versions) {
if (version.id.equals(im.getMinecraftVersion())) {
if (version.id.equals(instance.meta().getMinecraftVersion())) {
found = true;
VersionInfo vi = McApi.getVersionInfo(version);
if (im.isFabric())
vi = FabricMetaApi.addFabric(vi, im.getLoaderVersion(), FabricMetaApi.FabricVersionInfoType.Both);
LoaderInfo li = im.isFabric()
? new LoaderInfo(LoaderInfo.Type.Fabric, im.getLoaderVersion())
if (instance.isFabric())
vi = FabricMetaApi.addFabric(vi, instance.meta().getLoaderVersion(), FabricMetaApi.FabricVersionInfoType.Both);
LoaderInfo li = instance.isFabric()
? new LoaderInfo(LoaderInfo.Type.Fabric, instance.meta().getLoaderVersion())
: LoaderInfo.NONE;
SetupStepInfo info = new SetupStepInfo(vi, li, instance.getFileName().toString(), state);
SetupStepInfo info = new SetupStepInfo(vi, li, instance.getName(), state);
for (Step step : Steps.STEPS) {
state.incrementStep("Starting " + step.getClass().getSimpleName());
step.execute(info, new AtomicBoolean(false));
}
}
}
if (!found) throw new IOException("Could not identify minecraft version " + im.getMinecraftVersion());
if (!found) throw new IOException("Could not identify minecraft version " + instance.meta().getMinecraftVersion());
}
}

View File

@ -3,8 +3,8 @@ package io.gitlab.jfronny.inceptum.launcher.system.install.steps;
import io.gitlab.jfronny.inceptum.common.MetaHolder;
import io.gitlab.jfronny.inceptum.launcher.model.inceptum.InstanceMeta;
import io.gitlab.jfronny.inceptum.launcher.model.inceptum.LoaderInfo;
import io.gitlab.jfronny.inceptum.launcher.model.inceptum.rt.Instance;
import io.gitlab.jfronny.inceptum.launcher.system.install.Step;
import io.gitlab.jfronny.inceptum.launcher.util.InstanceLock;
import io.gitlab.jfronny.inceptum.launcher.system.install.SetupStepInfo;
import java.io.IOException;
@ -17,7 +17,7 @@ public class SetupDirsStep implements Step {
public void execute(SetupStepInfo info, AtomicBoolean stopThread) throws IOException {
info.setState("Setting up instance dirs");
Path iDir = MetaHolder.INSTANCE_DIR.resolve(info.name());
InstanceLock.setSetupLock(iDir, true);
Instance.setSetupLock(iDir, true);
if (!Files.exists(iDir)) {
Files.createDirectories(iDir.resolve("resourcepacks"));
Files.createDirectories(iDir.resolve("saves"));

View File

@ -3,8 +3,8 @@ package io.gitlab.jfronny.inceptum.launcher.system.install.steps;
import io.gitlab.jfronny.commons.io.JFiles;
import io.gitlab.jfronny.inceptum.common.MetaHolder;
import io.gitlab.jfronny.inceptum.launcher.model.inceptum.InstanceMeta;
import io.gitlab.jfronny.inceptum.launcher.model.inceptum.rt.Instance;
import io.gitlab.jfronny.inceptum.launcher.system.install.Step;
import io.gitlab.jfronny.inceptum.launcher.util.InstanceLock;
import io.gitlab.jfronny.inceptum.launcher.system.install.SetupStepInfo;
import java.io.IOException;
@ -23,7 +23,7 @@ public class WriteMetadataStep implements Step {
meta.version = info.version().id;
JFiles.writeObject(metaPath, meta);
}
InstanceLock.setSetupLock(instance, false);
Instance.setSetupLock(instance, false);
if (!Files.exists(instance.resolve(".gitignore"))) {
Files.writeString(instance.resolve(".gitignore"), """
realms_persistence.json

View File

@ -6,6 +6,7 @@ import io.gitlab.jfronny.inceptum.common.api.MavenApi;
import io.gitlab.jfronny.inceptum.launcher.LauncherEnv;
import io.gitlab.jfronny.inceptum.common.*;
import io.gitlab.jfronny.inceptum.launcher.model.inceptum.*;
import io.gitlab.jfronny.inceptum.launcher.model.inceptum.rt.Instance;
import io.gitlab.jfronny.inceptum.launcher.model.mojang.*;
import io.gitlab.jfronny.inceptum.launcher.api.FabricMetaApi;
import io.gitlab.jfronny.inceptum.launcher.api.McApi;
@ -24,7 +25,7 @@ import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicReference;
public class InstanceLauncher {
public static void launchClient(Path path, InstanceMeta instance) {
public static void launchClient(Instance instance) {
if (AccountManager.accountMissing() && InceptumConfig.enforceAccount) {
LauncherEnv.showError("You have not set up an account.\nDoing so is required to play Minecraft", "Not authenticated");
return;
@ -39,29 +40,29 @@ public class InstanceLauncher {
InceptumConfig.offlineAccountLastName = name.equals(sysUser) ? null : name;
InceptumConfig.saveConfig();
AuthInfo infoNew = new AuthInfo(name, authInfo.uuid(), authInfo.accessToken(), authInfo.userType());
launchClient(path, instance, infoNew);
launchClient(instance, infoNew);
}, R::nop);
} catch (IOException e) {
LauncherEnv.showError("Failed to request input", e);
}
} else launchClient(path, instance, authInfo);
} else launchClient(instance, authInfo);
}
private static void launchClient(Path path, InstanceMeta instance, AuthInfo authInfo) {
private static void launchClient(Instance instance, AuthInfo authInfo) {
try {
launch(path, instance, LaunchType.Client, false, authInfo);
launch(instance, LaunchType.Client, false, authInfo);
} catch (LaunchException | IOException e) {
LauncherEnv.showError("Could not launch client", e);
}
}
public static void launch(Path instancePath, InstanceMeta instance, LaunchType launchType, boolean restart, AuthInfo authInfo) throws LaunchException, IOException {
public static void launch(Instance instance, LaunchType launchType, boolean restart, AuthInfo authInfo) throws LaunchException, IOException {
if (authInfo == null) throw new LaunchException("authInfo is null");
VersionsListInfo versionDataSimple = getVersion(instance.getMinecraftVersion());
VersionsListInfo versionDataSimple = getVersion(instance.meta().getMinecraftVersion());
VersionInfo versionInfo = McApi.getVersionInfo(versionDataSimple);
// Add fabric metadata if using fabric
if (instance.isFabric()) {
versionInfo = FabricMetaApi.addFabric(versionInfo, instance.getLoaderVersion(), launchType.fabricMetaType);
versionInfo = FabricMetaApi.addFabric(versionInfo, instance.meta().getLoaderVersion(), launchType.fabricMetaType);
}
// Ensure libs/assets are present
DownloadLibrariesStep.execute(versionInfo, new AtomicBoolean(false), new ProcessState());
@ -70,7 +71,7 @@ public class InstanceLauncher {
// JVM path
{
final VersionInfo lambdaVersionInfo = versionInfo;
args.add(Objects.requireNonNullElseGet(instance.java, () ->
args.add(Objects.requireNonNullElseGet(instance.meta().java, () ->
OSUtils.getJvmBinary(MetaHolder.NATIVES_DIR
.resolve(lambdaVersionInfo.javaVersion.component)
.resolve(Integer.toString(lambdaVersionInfo.javaVersion.majorVersion)))
@ -88,10 +89,10 @@ public class InstanceLauncher {
classPath.append(MetaHolder.LIBRARIES_DIR.resolve(MavenApi.mavenNotationToJarPath(DownloadLibrariesStep.getLaunchWrapperArtifact())));
// JVM arguments
if (launchType == LaunchType.Client && versionInfo.arguments != null)
args.addAll(parse(versionInfo.arguments.jvm, versionInfo, instance, classPath.toString(), instancePath.toAbsolutePath().toString(), authInfo));
if (instance.minMem != null) args.add("-Xms" + instance.minMem);
if (instance.maxMem != null) args.add("-Xmx" + instance.maxMem);
if (instance.arguments != null && instance.arguments.jvm != null) args.addAll(instance.arguments.jvm);
args.addAll(parse(versionInfo.arguments.jvm, versionInfo, instance, classPath.toString(), authInfo));
if (instance.meta().minMem != null) args.add("-Xms" + instance.meta().minMem);
if (instance.meta().maxMem != null) args.add("-Xmx" + instance.meta().maxMem);
if (instance.meta().arguments != null && instance.meta().arguments.jvm != null) args.addAll(instance.meta().arguments.jvm);
// Forceload natives
if (Files.exists(MetaHolder.FORCE_LOAD_PATH)) {
args.add("-Dinceptum.forceloadNatives=" + MetaHolder.FORCE_LOAD_PATH);
@ -99,9 +100,8 @@ public class InstanceLauncher {
// Fabric imods
if (instance.isFabric()) {
StringBuilder fabricAddMods = new StringBuilder("-Dfabric.addMods=");
Path mods = instancePath.resolve("mods");
if (Files.exists(mods)) {
for (Path imod : JFiles.list(mods, path -> ModPath.isImod(path) && ModPath.isEnabled(path))) {
if (Files.exists(instance.modsDir())) {
for (Path imod : JFiles.list(instance.modsDir(), path -> ModPath.isImod(path) && ModPath.isEnabled(path))) {
String fn = imod.getFileName().toString();
if (Files.exists(imod.getParent().resolve(fn.substring(0, fn.length() - 5))))
continue;
@ -123,44 +123,40 @@ public class InstanceLauncher {
// Game arguments
if (launchType == LaunchType.Client) {
if (versionInfo.arguments != null)
args.addAll(parse(versionInfo.arguments.game, versionInfo, instance, classPath.toString(), instancePath.toAbsolutePath().toString(), authInfo));
args.addAll(parse(versionInfo.arguments.game, versionInfo, instance, classPath.toString(), authInfo));
else if (versionInfo.minecraftArguments != null) {
for (String s : versionInfo.minecraftArguments.split(" ")) {
args.add(expandArg(s, versionInfo, instance, classPath.toString(), instancePath.toAbsolutePath().toString(), authInfo));
args.add(expandArg(s, versionInfo, instance, classPath.toString(), authInfo));
}
} else throw new LaunchException("Could not launch: No valid source for client arguments found");
}
if (instance.arguments != null) {
if (instance.meta().arguments != null) {
switch (launchType) {
case Client -> {
if (instance.arguments.client != null)
args.addAll(instance.arguments.client);
if (instance.meta().arguments.client != null)
args.addAll(instance.meta().arguments.client);
}
case Server -> {
if (instance.arguments.server != null)
args.addAll(instance.arguments.server);
if (instance.meta().arguments.server != null)
args.addAll(instance.meta().arguments.server);
}
}
}
// Write launch time
instance.lastLaunched = System.currentTimeMillis() / 1000L;
try {
JFiles.writeObject(instancePath.resolve("instance.json"), instance);
} catch (IOException e) {
Utils.LOGGER.error("Could not write instance config", e);
}
instance.meta().lastLaunched = System.currentTimeMillis() / 1000L;
instance.writeMeta();
// Log command used to start
Utils.LOGGER.info(String.join(" ", args));
// Create process
ProcessBuilder pb = new ProcessBuilder(args.toArray(new String[0]));
pb.directory(instancePath.toFile());
pb.directory(instance.path().toFile());
pb.redirectOutput(ProcessBuilder.Redirect.INHERIT);
pb.redirectError(ProcessBuilder.Redirect.INHERIT);
AtomicReference<Process> proc = new AtomicReference<>();
Runnable starterRunner = () -> {
try {
proc.set(pb.start());
InstanceLock.setRunningLock(instancePath, proc.get().pid());
instance.setRunningLock(proc.get().pid());
} catch (IOException e) {
Utils.LOGGER.error("Could not start " + launchType.name, e);
}
@ -185,7 +181,7 @@ public class InstanceLauncher {
}
}
private static String resolveMainClass(InstanceMeta instance, VersionInfo versionInfo, Path gameJar, LaunchType launchType) throws LaunchException {
private static String resolveMainClass(Instance instance, VersionInfo versionInfo, Path gameJar, LaunchType launchType) throws LaunchException {
if (launchType == LaunchType.Client || instance.isFabric()) return versionInfo.mainClass;
// Identify main class using MANIFEST.MF
final String linePrefix = "Main-Class: ";
@ -209,22 +205,22 @@ public class InstanceLauncher {
throw new LaunchException("Could not find data for minecraft version: " + minecraftVersion);
}
private static List<String> parse(List<MinecraftArgument> arguments, VersionInfo info, InstanceMeta instance, String classPath, String gameDirectory, AuthInfo authInfo) {
private static List<String> parse(List<MinecraftArgument> arguments, VersionInfo info, Instance instance, String classPath, AuthInfo authInfo) {
List<String> res = new ArrayList<>();
for (MinecraftArgument argument : arguments) {
for (String s : argument.arg()) {
res.add(expandArg(s, info, instance, classPath, gameDirectory, authInfo));
res.add(expandArg(s, info, instance, classPath, authInfo));
}
}
return res;
}
private static String expandArg(String arg, VersionInfo info, InstanceMeta instance, String classPath, String gameDirectory, AuthInfo authInfo) {
private static String expandArg(String arg, VersionInfo info, Instance instance, String classPath, AuthInfo authInfo) {
return arg
// game args
.replace("${auth_player_name}", authInfo.name())
.replace("${version_name}", instance.getMinecraftVersion())
.replace("${game_directory}", gameDirectory)
.replace("${version_name}", instance.meta().getMinecraftVersion())
.replace("${game_directory}", instance.path().toString())
.replace("${assets_root}", MetaHolder.ASSETS_DIR.toAbsolutePath().toString())
.replace("${assets_index_name}", info.assets)
.replace("${auth_uuid}", authInfo.uuid())
@ -234,7 +230,7 @@ public class InstanceLauncher {
.replace("${resolution_width}", "1920") //TODO has_custom_resolution
.replace("${resolution_height}", "1080") //TODO has_custom_resolution
// jvm args
.replace("${natives_directory}", MetaHolder.NATIVES_DIR.resolve(instance.getMinecraftVersion()).toAbsolutePath().toString())
.replace("${natives_directory}", MetaHolder.NATIVES_DIR.resolve(instance.meta().getMinecraftVersion()).toAbsolutePath().toString())
.replace("${launcher_name}", "Inceptum")
.replace("${launcher_version}", BuildMetadata.VERSION.toString())
.replace("${classpath}", classPath)

View File

@ -6,6 +6,7 @@ import io.gitlab.jfronny.gson.JsonParseException;
import io.gitlab.jfronny.inceptum.common.Utils;
import io.gitlab.jfronny.inceptum.launcher.model.fabric.FabricModJson;
import io.gitlab.jfronny.inceptum.launcher.model.inceptum.ModDescription;
import io.gitlab.jfronny.inceptum.launcher.model.inceptum.rt.Mod;
import io.gitlab.jfronny.inceptum.launcher.util.ModPath;
import io.gitlab.jfronny.inceptum.launcher.system.source.ModSource;
@ -17,13 +18,13 @@ import java.util.List;
import java.util.Optional;
import java.util.function.BiConsumer;
public record FileScanTask(Path file, BiConsumer<Path, IWModDescription> discovered,
public record FileScanTask(Path file, BiConsumer<Path, Mod> discovered,
String gameVersion) implements Runnable {
@Override
public void run() {
if (!Files.exists(file)) return;
if (Files.isDirectory(file)) {
discovered.accept(file, new IWModDescription(file));
discovered.accept(file, new Mod(file));
return;
}
try {
@ -41,7 +42,7 @@ public record FileScanTask(Path file, BiConsumer<Path, IWModDescription> discove
imod.i = newImod;
imod.md = JFiles.readObject(imod.i, ModDescription.class);
});
discovered.accept(imod.i, new IWModDescription(file, Optional.of(imod.md), getFmj(file, imod.md), Optional.of(imod.i)));
discovered.accept(imod.i, new Mod(file, Optional.of(imod.md), getFmj(file, imod.md), Optional.of(imod.i)));
return;
}
if (ModPath.isImod(file)) {
@ -55,10 +56,10 @@ public record FileScanTask(Path file, BiConsumer<Path, IWModDescription> discove
imod.i = newImod;
imod.md = JFiles.readObject(imod.i, ModDescription.class);
});
discovered.accept(imod.i, new IWModDescription(imod.i, Optional.of(imod.md), getFmj(modFile, imod.md), Optional.of(imod.i)));
discovered.accept(imod.i, new Mod(imod.i, Optional.of(imod.md), getFmj(modFile, imod.md), Optional.of(imod.i)));
return;
}
discovered.accept(file, new IWModDescription(file));
discovered.accept(file, new Mod(file));
} catch (IOException | URISyntaxException | JsonParseException e) {
Utils.LOGGER.error("Could not scan file for mod info", e);
}

View File

@ -1,6 +1,7 @@
package io.gitlab.jfronny.inceptum.launcher.system.mds;
import io.gitlab.jfronny.inceptum.launcher.model.inceptum.InstanceMeta;
import io.gitlab.jfronny.inceptum.launcher.model.inceptum.rt.Mod;
import java.io.Closeable;
import java.io.IOException;
@ -25,13 +26,13 @@ public interface ModsDirScanner extends Closeable {
String getGameVersion();
Set<IWModDescription> getMods() throws IOException;
Set<Mod> getMods() throws IOException;
IWModDescription get(Path path);
Mod get(Path path);
void invalidate(Path path);
boolean hasScanned(Path path);
void runOnce(BiConsumer<Path, IWModDescription> discovered);
void runOnce(BiConsumer<Path, Mod> discovered);
}

View File

@ -4,6 +4,7 @@ import io.gitlab.jfronny.commons.io.JFiles;
import io.gitlab.jfronny.inceptum.common.R;
import io.gitlab.jfronny.inceptum.common.Utils;
import io.gitlab.jfronny.inceptum.launcher.model.inceptum.InstanceMeta;
import io.gitlab.jfronny.inceptum.launcher.model.inceptum.rt.Mod;
import io.gitlab.jfronny.inceptum.launcher.util.ModPath;
import java.io.IOException;
@ -17,7 +18,7 @@ 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 Map<Path, Mod> descriptions = new HashMap<>();
private final Set<Path> scannedPaths = new HashSet<>();
private final Thread th;
private final Path modsDir;
@ -70,8 +71,8 @@ class ModsDirScannerImpl implements ModsDirScanner {
}
@Override
public Set<IWModDescription> getMods() throws IOException {
Set<IWModDescription> mods = new TreeSet<>();
public Set<Mod> getMods() throws IOException {
Set<Mod> mods = new TreeSet<>();
if (Files.isDirectory(modsDir)) {
for (Path path : JFiles.list(modsDir)) {
if (ModPath.isImod(path) && Files.exists(ModPath.trimImod(path)))
@ -83,10 +84,10 @@ class ModsDirScannerImpl implements ModsDirScanner {
}
@Override
public IWModDescription get(Path path) {
public Mod get(Path path) {
if (!descriptions.containsKey(path)) {
// not yet scanned
descriptions.put(path, new IWModDescription(path));
descriptions.put(path, new Mod(path));
}
return descriptions.get(path);
}
@ -109,7 +110,7 @@ class ModsDirScannerImpl implements ModsDirScanner {
}
@Override
public void runOnce(BiConsumer<Path, IWModDescription> discovered) {
public void runOnce(BiConsumer<Path, Mod> discovered) {
try {
if (!Files.isDirectory(modsDir)) {
return;

View File

@ -1,5 +1,7 @@
package io.gitlab.jfronny.inceptum.launcher.system.mds;
import io.gitlab.jfronny.inceptum.launcher.model.inceptum.rt.Mod;
import java.io.IOException;
import java.nio.file.Path;
import java.util.Set;
@ -25,13 +27,13 @@ public record NoopMds(String gameVersion) implements ModsDirScanner {
}
@Override
public Set<IWModDescription> getMods() throws IOException {
public Set<Mod> getMods() throws IOException {
return Set.of();
}
@Override
public IWModDescription get(Path path) {
return new IWModDescription(path);
public Mod get(Path path) {
return new Mod(path);
}
@Override
@ -44,6 +46,6 @@ public record NoopMds(String gameVersion) implements ModsDirScanner {
}
@Override
public void runOnce(BiConsumer<Path, IWModDescription> discovered) {
public void runOnce(BiConsumer<Path, Mod> discovered) {
}
}

View File

@ -2,27 +2,46 @@ package io.gitlab.jfronny.inceptum.launcher.util;
import io.gitlab.jfronny.commons.cache.FileBackedRef;
import io.gitlab.jfronny.commons.io.JFiles;
import io.gitlab.jfronny.commons.throwable.ThrowingConsumer;
import io.gitlab.jfronny.inceptum.common.MetaHolder;
import io.gitlab.jfronny.inceptum.common.Utils;
import io.gitlab.jfronny.inceptum.launcher.model.inceptum.InstanceMeta;
import org.jetbrains.annotations.NotNull;
import io.gitlab.jfronny.inceptum.launcher.model.inceptum.rt.Instance;
import java.io.Closeable;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.*;
import java.util.function.Consumer;
public class InstanceList {
private static final Map<Path, IEntry> metas = new LinkedHashMap<>();
public static void forEach(Consumer<Entry> target) throws IOException {
public static void reset() {
synchronized (metas) {
for (IEntry value : metas.values()) {
try {
value.close();
} catch (IOException e) {
Utils.LOGGER.error("Could not close reference to instance meta", e);
}
}
metas.clear();
}
}
public static <TEx extends Exception> void forEach(ThrowingConsumer<Instance, TEx> target) throws IOException, TEx {
Objects.requireNonNull(target);
if (!Files.exists(MetaHolder.INSTANCE_DIR)) Files.createDirectories(MetaHolder.INSTANCE_DIR);
JFiles.listTo(MetaHolder.INSTANCE_DIR, path -> {
if (!Files.isDirectory(path)) return;
target.accept(read(path));
});
try {
JFiles.listTo(MetaHolder.INSTANCE_DIR, path -> {
if (!Files.isDirectory(path)) return;
target.accept(read(path));
});
} catch (Exception e) {
//noinspection unchecked
throw (TEx) e;
}
}
public static boolean isEmpty() throws IOException {
@ -30,12 +49,11 @@ public class InstanceList {
return JFiles.list(MetaHolder.INSTANCE_DIR, Files::isDirectory).isEmpty();
}
public static Entry read(Path instancePath) throws IOException {
public static Instance read(Path instancePath) throws IOException {
Objects.requireNonNull(instancePath);
synchronized (metas) {
if (!metas.containsKey(instancePath)) {
metas.put(instancePath, new IEntry(
toId(instancePath.getFileName().toString()),
instancePath,
new FileBackedRef<>(instancePath.resolve("instance.json"), InstanceMeta.class)
));
@ -44,50 +62,19 @@ public class InstanceList {
}
}
/**
* Converts any string into a set of lowercase ascii chars
* @param input string to convert
* @return a string matching [a-p]*
*/
private static String toId(String input) {
StringBuilder result = new StringBuilder();
for (byte b : input.getBytes(StandardCharsets.UTF_8)) {
int by = Byte.toUnsignedInt(b);
int ch2 = by & 15; // right bits
int ch1 = (by - ch2) / 16; // left bits
result.append((char)('a' + ch1));
result.append((char)('a' + ch2));
}
return result.toString();
}
private record IEntry(String id, Path path, FileBackedRef<InstanceMeta> meta) {
private record IEntry(Path path, FileBackedRef<InstanceMeta> meta) implements Closeable {
@Override
public String toString() {
return path.getFileName().toString();
}
public Entry toPub() throws IOException {
return new Entry(id, path, meta.get());
}
}
public record Entry(String id, Path path, InstanceMeta meta) implements Comparable<Entry> {
@Override
public int compareTo(@NotNull InstanceList.Entry entry) {
long time1 = meta.lastLaunched == null ? 0 : meta.lastLaunched;
long time2 = entry.meta.lastLaunched == null ? 0 : entry.meta.lastLaunched;
if (time1 == 0) {
if (time2 == 0) return path.getFileName().toString().compareTo(entry.path.getFileName().toString());
return -1;
}
if (time2 == 0) return 1;
return Long.compare(time1, time2);
public Instance toPub() throws IOException {
return new Instance(path, meta.get());
}
@Override
public String toString() {
return path.getFileName().toString();
public void close() throws IOException {
meta.close();
}
}
}

View File

@ -1,44 +0,0 @@
package io.gitlab.jfronny.inceptum.launcher.util;
import io.gitlab.jfronny.inceptum.common.Utils;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
public class InstanceLock {
private static final String INCEPTUM_LOCK = "inceptum.lock";
private static final String INCEPTUM_SETUP_LOCK = "inceptum.setup.lock";
public static void setSetupLock(Path instancePath, boolean state) throws IOException {
if (isSetupLocked(instancePath)) {
if (!state) Files.delete(instancePath.resolve(INCEPTUM_SETUP_LOCK));
} else {
if (state) Files.createDirectories(instancePath.resolve(INCEPTUM_SETUP_LOCK));
}
}
public static boolean isSetupLocked(Path instancePath) {
return Files.exists(instancePath.resolve(INCEPTUM_SETUP_LOCK));
}
public static void setRunningLock(Path instancePath, long pid) throws IOException {
Files.writeString(instancePath.resolve(INCEPTUM_LOCK), Long.toString(pid));
}
public static boolean isRunningLocked(Path instancePath) {
if (!Files.exists(instancePath.resolve(INCEPTUM_LOCK))) return false;
try {
if (ProcessUtils.isProcessAlive(Files.readString(instancePath.resolve(INCEPTUM_LOCK))))
return true;
Files.delete(instancePath.resolve(INCEPTUM_LOCK));
} catch (IOException e) {
Utils.LOGGER.error("Could not read running lock of " + instancePath.getFileName().toString(), e);
}
return false;
}
public static boolean isLocked(Path instancePath) {
return isSetupLocked(instancePath) || isRunningLocked(instancePath);
}
}

View File

@ -2,7 +2,7 @@ package io.gitlab.jfronny.inceptum.launcher.util;
import io.gitlab.jfronny.commons.io.JFiles;
import io.gitlab.jfronny.inceptum.launcher.model.inceptum.ModDescription;
import io.gitlab.jfronny.inceptum.launcher.system.mds.IWModDescription;
import io.gitlab.jfronny.inceptum.launcher.model.inceptum.rt.Mod;
import io.gitlab.jfronny.inceptum.launcher.system.mds.ModsDirScanner;
import io.gitlab.jfronny.inceptum.launcher.system.source.ModDownload;
import io.gitlab.jfronny.inceptum.launcher.system.source.ModSource;
@ -12,13 +12,13 @@ import java.nio.file.Files;
import java.nio.file.Path;
public class ModManager {
public static void delete(IWModDescription md, Path modsDirectory, ModsDirScanner mds) throws IOException {
public static void delete(Mod md, Path modsDirectory, ModsDirScanner mds) throws IOException {
JFiles.deleteRecursive(md.path());
if (md.imod().isPresent() && Files.exists(md.imod().get())) Files.delete(md.imod().get());
if (md.mod().isPresent()) {
for (String dependency : md.mod().get().dependencies) {
Path dep = modsDirectory.resolve(dependency);
IWModDescription dmd = mds.get(dep);
Mod dmd = mds.get(dep);
if (dmd.mod().isPresent()) {
dmd.mod().get().dependencies.remove(md.path().getFileName().toString());
if (dmd.mod().get().dependencies.isEmpty()) delete(dmd, modsDirectory, mds);
@ -29,7 +29,7 @@ public class ModManager {
}
public static DownloadMeta download(ModSource ms, Path metaFile, ModsDirScanner mds) throws IOException {
for (IWModDescription value : mds.getMods()) {
for (Mod value : mds.getMods()) {
if (value.mod().isEmpty()) continue;
for (ModSource source : value.mod().get().sources.keySet()) {
if (ms.equals(source)) {