GTK: Start working on instance creation flow
This commit is contained in:
parent
4967410f51
commit
c027885364
|
@ -19,7 +19,7 @@ public class ImportCommand extends Command {
|
|||
if (args.length == 0) throw new IllegalAccessException("You must specify a pack file");
|
||||
if (args.length != 1) throw new IllegalAccessException("Too many arguments");
|
||||
ProcessState state = new ProcessState();
|
||||
String name = Importers.importPack(Paths.get(args[0]), state).fileName.toString();
|
||||
String name = Importers.importPack(Paths.get(args[0]), state).path().fileName.toString();
|
||||
System.out.println(OutputColors.GREEN_BOLD + "Imported as " + name + OutputColors.RESET);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,9 +5,11 @@ import io.gitlab.jfronny.inceptum.common.MetaHolder;
|
|||
import io.gitlab.jfronny.inceptum.gtk.window.dialog.MicrosoftLoginDialog;
|
||||
import io.gitlab.jfronny.inceptum.gtk.window.dialog.ProcessStateWatcherDialog;
|
||||
import io.gitlab.jfronny.inceptum.gtk.window.settings.launcher.LauncherSettingsWindow;
|
||||
import io.gitlab.jfronny.inceptum.launcher.system.importer.Importers;
|
||||
import io.gitlab.jfronny.inceptum.launcher.system.launch.*;
|
||||
import io.gitlab.jfronny.inceptum.launcher.util.ProcessState;
|
||||
import org.gtk.gtk.Application;
|
||||
import org.gtk.gio.Menu;
|
||||
import org.gtk.gtk.*;
|
||||
import io.gitlab.jfronny.commons.ref.R;
|
||||
import io.gitlab.jfronny.inceptum.common.Utils;
|
||||
import io.gitlab.jfronny.inceptum.gtk.menu.MenuBuilder;
|
||||
|
@ -21,17 +23,23 @@ import io.gitlab.jfronny.inceptum.launcher.system.instance.Instance;
|
|||
import io.gitlab.jfronny.inceptum.launcher.system.instance.InstanceList;
|
||||
import io.gitlab.jfronny.inceptum.launcher.system.setup.Steps;
|
||||
|
||||
import java.awt.*;
|
||||
import java.awt.datatransfer.DataFlavor;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Path;
|
||||
import java.util.*;
|
||||
import java.util.List;
|
||||
|
||||
public class GtkMenubar {
|
||||
public static MenuBuilder newMenu;
|
||||
public static MenuBuilder accountsMenu;
|
||||
public static MenuBuilder launchMenu;
|
||||
|
||||
public static void create(Application app) {
|
||||
var menu = new MenuBuilder(app);
|
||||
var file = menu.submenu("file");
|
||||
file.button("new", () -> new NewInstanceWindow(app).show());
|
||||
newMenu = file.submenu("new");
|
||||
generateNewMenu(app);
|
||||
file.button("redownload", () -> {
|
||||
ProcessState state = new ProcessState(3 + Steps.STEPS.size() * InstanceList.size(), "Initializing");
|
||||
ProcessStateWatcherDialog.show(
|
||||
|
@ -39,24 +47,24 @@ public class GtkMenubar {
|
|||
"Reloading data",
|
||||
"Could not execute refresh task",
|
||||
state,
|
||||
cancel -> {
|
||||
() -> {
|
||||
state.incrementStep("Clearing cache directories");
|
||||
JFiles.clearDirectory(MetaHolder.ASSETS_DIR);
|
||||
JFiles.clearDirectory(MetaHolder.LIBRARIES_DIR, path -> !path.startsWith(MetaHolder.LIBRARIES_DIR.resolve("io/gitlab/jfronny")));
|
||||
JFiles.clearDirectory(MetaHolder.NATIVES_DIR, path -> !path.startsWith(MetaHolder.NATIVES_DIR.resolve("forceload")));
|
||||
JFiles.clearDirectory(MetaHolder.CACHE_DIR);
|
||||
if (cancel.get()) return;
|
||||
if (state.isCancelled) return;
|
||||
state.incrementStep("Reloading instance list");
|
||||
InstanceList.reset();
|
||||
InstanceList.forEach(instance -> {
|
||||
if (cancel.get()) return;
|
||||
Steps.reDownload(instance, state, cancel);
|
||||
if (state.isCancelled) return;
|
||||
Steps.reDownload(instance, state);
|
||||
});
|
||||
}, R::nop);
|
||||
});
|
||||
});
|
||||
file.button("exit", app::quit);
|
||||
launchMenu = menu.submenu("launch");
|
||||
generateLaunchMenu();
|
||||
generateLaunchMenu(app);
|
||||
accountsMenu = menu.submenu("account");
|
||||
generateAccountsMenu(app);
|
||||
var help = menu.submenu("help");
|
||||
|
@ -66,9 +74,62 @@ public class GtkMenubar {
|
|||
});
|
||||
}
|
||||
|
||||
public static void generateLaunchMenu() {
|
||||
Objects.requireNonNull(launchMenu);
|
||||
launchMenu.clear();
|
||||
public static void generateNewMenu(Application app) {
|
||||
Objects.requireNonNull(newMenu).clear();
|
||||
newMenu.button("new", () -> new NewInstanceWindow(app).show());
|
||||
newMenu.button("file", () -> {
|
||||
FileChooserNative dialog = new FileChooserNative(
|
||||
I18n.get("menu.file.new.file"),
|
||||
GtkEnvBackend.INSTANCE.dialogParent,
|
||||
FileChooserAction.OPEN,
|
||||
"_" + I18n.get("select"),
|
||||
"_" + I18n.get("cancel")
|
||||
);
|
||||
var filter = new FileFilter();
|
||||
filter.addPattern("*.zip");
|
||||
filter.addPattern("*.mrpack");
|
||||
dialog.addFilter(filter);
|
||||
dialog.onResponse(responseId -> {
|
||||
if (responseId == ResponseType.ACCEPT.value) {
|
||||
var file = dialog.file.path;
|
||||
if (file == null) {
|
||||
LauncherEnv.showError("The path returned by the file dialog is null", "Could not import");
|
||||
return;
|
||||
}
|
||||
ProcessState state = new ProcessState(Importers.MAX_STEPS, "Initializing");
|
||||
ProcessStateWatcherDialog.show(
|
||||
GtkEnvBackend.INSTANCE.dialogParent,
|
||||
I18n.get("menu.file.new.file"),
|
||||
I18n.get("menu.file.new.file.error"),
|
||||
state,
|
||||
() -> Importers.importPack(Path.of(file), state)
|
||||
);
|
||||
}
|
||||
});
|
||||
dialog.show();
|
||||
});
|
||||
newMenu.button("url", () -> {
|
||||
LauncherEnv.getInput(
|
||||
I18n.get("menu.file.new.url"),
|
||||
I18n.get("menu.file.new.url.details"),
|
||||
(String) Toolkit.getDefaultToolkit().getSystemClipboard().getData(DataFlavor.stringFlavor),
|
||||
s -> {
|
||||
ProcessState state = new ProcessState(Importers.MAX_STEPS, "Initializing");
|
||||
ProcessStateWatcherDialog.show(
|
||||
GtkEnvBackend.INSTANCE.dialogParent,
|
||||
I18n.get("menu.file.new.url"),
|
||||
I18n.get("menu.file.new.url.error"),
|
||||
state,
|
||||
() -> Importers.importPack(s, state)
|
||||
);
|
||||
},
|
||||
R::nop
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
public static void generateLaunchMenu(Application app) {
|
||||
Objects.requireNonNull(launchMenu).clear();
|
||||
try {
|
||||
InstanceList.forEach(entry -> {
|
||||
launchMenu.literalButton(entry.id() + ".launch", entry.toString(), () -> launch(entry, LaunchType.Client));
|
||||
|
@ -98,13 +159,13 @@ public class GtkMenubar {
|
|||
I18n.get("instance.launch.title"),
|
||||
I18n.get("instance.launch.error"),
|
||||
state,
|
||||
cancel -> {
|
||||
() -> {
|
||||
try {
|
||||
Steps.reDownload(instance, state, cancel);
|
||||
Steps.reDownload(instance, state);
|
||||
} catch (IOException e) {
|
||||
Utils.LOGGER.error("Could not fetch instance, trying to start anyways", e);
|
||||
}
|
||||
if (cancel.get()) return;
|
||||
if (state.isCancelled) return;
|
||||
state.updateStep("Starting Game");
|
||||
try {
|
||||
if (launchType == LaunchType.Client) InstanceLauncher.launchClient(instance);
|
||||
|
@ -112,14 +173,12 @@ public class GtkMenubar {
|
|||
} catch (Throwable e) {
|
||||
LauncherEnv.showError("Could not start instance", e);
|
||||
}
|
||||
},
|
||||
R::nop
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
public static void generateAccountsMenu(Application app) {
|
||||
Objects.requireNonNull(accountsMenu);
|
||||
accountsMenu.clear();
|
||||
Objects.requireNonNull(accountsMenu).clear();
|
||||
accountsMenu.button("new", () -> new MicrosoftLoginDialog(GtkEnvBackend.INSTANCE.dialogParent).show());
|
||||
accountsMenu.button("manage", () -> {
|
||||
var window = new LauncherSettingsWindow(app);
|
||||
|
|
|
@ -36,8 +36,9 @@ public class MainWindow extends ApplicationWindow {
|
|||
super(app);
|
||||
|
||||
HeaderBar header = new HeaderBar();
|
||||
Button newButton = Button.newFromIconName("list-add-symbolic");
|
||||
newButton.onClicked(() -> new NewInstanceWindow(app).show());
|
||||
MenuButton newButton = new MenuButton();
|
||||
newButton.iconName = "list-add-symbolic";
|
||||
newButton.menuModel = GtkMenubar.newMenu.menu;
|
||||
|
||||
MenuButton accountsButton = new MenuButton();
|
||||
accountsButton.iconName = "avatar-default-symbolic";
|
||||
|
|
|
@ -1,14 +1,16 @@
|
|||
package io.gitlab.jfronny.inceptum.gtk.window;
|
||||
|
||||
import io.gitlab.jfronny.inceptum.gtk.GtkMain;
|
||||
import org.gtk.gtk.*;
|
||||
|
||||
public class NewInstanceWindow extends Window {
|
||||
public class NewInstanceWindow extends Assistant {
|
||||
public NewInstanceWindow(Application app) {
|
||||
this.application = app;
|
||||
|
||||
child = new Label("This feature has not (yet) been implemented for this UI. Please try the CLI or ImGUI UI");
|
||||
|
||||
//TODO setup wizard (--> NC Tasks)
|
||||
{
|
||||
var initialPage = new Box(Orientation.VERTICAL, 8);
|
||||
initialPage.append(new Label("Importing instances via this assistant is not yet supported, use the ImGUI"));
|
||||
|
||||
appendPage(initialPage);
|
||||
setPageType(initialPage, AssistantPageType.INTRO);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,44 +1,37 @@
|
|||
package io.gitlab.jfronny.inceptum.gtk.window.dialog;
|
||||
|
||||
import io.gitlab.jfronny.commons.StringFormatter;
|
||||
import io.gitlab.jfronny.commons.throwable.ThrowingConsumer;
|
||||
import io.gitlab.jfronny.commons.throwable.ThrowingRunnable;
|
||||
import io.gitlab.jfronny.inceptum.common.Utils;
|
||||
import io.gitlab.jfronny.inceptum.gtk.GtkEnvBackend;
|
||||
import io.gitlab.jfronny.inceptum.gtk.GtkMain;
|
||||
import io.gitlab.jfronny.inceptum.gtk.util.I18n;
|
||||
import io.gitlab.jfronny.inceptum.launcher.LauncherEnv;
|
||||
import io.gitlab.jfronny.inceptum.launcher.util.ProcessState;
|
||||
import org.gtk.glib.GLib;
|
||||
import org.gtk.gtk.*;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
public class ProcessStateWatcherDialog extends MessageDialog {
|
||||
private final ProcessState state;
|
||||
private final Runnable cancel;
|
||||
private final AtomicBoolean canceled = new AtomicBoolean(false);
|
||||
private final AtomicBoolean finished = new AtomicBoolean(false);
|
||||
private boolean finished = false;
|
||||
private State cachedState = null;
|
||||
|
||||
public static ProcessStateWatcherDialog show(Window parent, String title, String errorMessage, ProcessState state, ThrowingConsumer<AtomicBoolean, ?> executor, @Nullable Runnable cancel) {
|
||||
ProcessStateWatcherDialog dialog = new ProcessStateWatcherDialog(parent, title, errorMessage, state, executor, cancel);
|
||||
public static ProcessStateWatcherDialog show(Window parent, String title, String errorMessage, ProcessState state, ThrowingRunnable<?> executor) {
|
||||
ProcessStateWatcherDialog dialog = new ProcessStateWatcherDialog(parent, title, errorMessage, state, executor);
|
||||
dialog.show();
|
||||
return dialog;
|
||||
}
|
||||
|
||||
public ProcessStateWatcherDialog(Window parent, String title, String errorMessage, ProcessState state, ThrowingConsumer<AtomicBoolean, ?> executor, @Nullable Runnable cancel) {
|
||||
public ProcessStateWatcherDialog(Window parent, String title, String errorMessage, ProcessState state, ThrowingRunnable<?> executor) {
|
||||
//TODO alternate UI: Only show progress bar by default, but have a dropdown to a "console" with the actual steps
|
||||
// this should make visualizing parallelized steps easier
|
||||
super(parent, DialogFlags.MODAL.or(DialogFlags.DESTROY_WITH_PARENT), MessageType.INFO, ButtonsType.NONE, null);
|
||||
this.state = state;
|
||||
this.cancel = cancel;
|
||||
this.title = title;
|
||||
if (cancel != null) addButton(I18n.get("cancel"), ResponseType.CANCEL.value);
|
||||
addButton(I18n.get("cancel"), ResponseType.CANCEL.value);
|
||||
onResponse(responseId -> {
|
||||
switch (ResponseType.of(responseId)) {
|
||||
case CLOSE, CANCEL -> {
|
||||
canceled.set(true);
|
||||
state.cancel();
|
||||
close();
|
||||
}
|
||||
case DELETE_EVENT -> destroy();
|
||||
|
@ -46,19 +39,14 @@ public class ProcessStateWatcherDialog extends MessageDialog {
|
|||
}
|
||||
});
|
||||
onCloseRequest(() -> {
|
||||
if (finished.get()) return false;
|
||||
if (cancel == null) return true;
|
||||
canceled.set(true);
|
||||
cancel.run();
|
||||
if (finished) return false;
|
||||
state.cancel();
|
||||
return false;
|
||||
});
|
||||
onClose(() -> {
|
||||
if (canceled.get() && cancel != null) cancel.run();
|
||||
});
|
||||
var progress = new ProgressBar();
|
||||
((Box) messageArea).append(progress);
|
||||
addTickCallback((widget, clock) -> {
|
||||
if (finished.get()) return GLib.SOURCE_REMOVE;
|
||||
if (finished) return GLib.SOURCE_REMOVE;
|
||||
var nc = new State(state);
|
||||
if (!nc.equals(cachedState)) {
|
||||
cachedState = nc;
|
||||
|
@ -70,9 +58,10 @@ public class ProcessStateWatcherDialog extends MessageDialog {
|
|||
}, null);
|
||||
new Thread(() -> {
|
||||
try {
|
||||
executor.accept(canceled);
|
||||
executor.run();
|
||||
} catch (Throwable e) {
|
||||
canceled.set(true);
|
||||
state.cancel();
|
||||
Utils.LOGGER.error(errorMessage, e);
|
||||
GtkEnvBackend.simpleDialog(
|
||||
parent,
|
||||
StringFormatter.toString(e),
|
||||
|
@ -83,7 +72,7 @@ public class ProcessStateWatcherDialog extends MessageDialog {
|
|||
null
|
||||
);
|
||||
} finally {
|
||||
finished.set(true);
|
||||
finished = true;
|
||||
GtkMain.schedule(this::close);
|
||||
}
|
||||
}).start();
|
||||
|
|
|
@ -75,7 +75,7 @@ public class ExportTab extends SettingsTab {
|
|||
I18n.get("instance.settings.export.dialog.title", exporter.name),
|
||||
I18n.get("instance.settings.export.dialog.error", instance.name),
|
||||
state,
|
||||
cancel -> {
|
||||
() -> {
|
||||
exporter.generate(state, instance, path);
|
||||
GtkMain.schedule(() -> {
|
||||
MessageDialog success = new MessageDialog(
|
||||
|
@ -100,8 +100,7 @@ public class ExportTab extends SettingsTab {
|
|||
});
|
||||
success.show();
|
||||
});
|
||||
},
|
||||
R::nop
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -103,4 +103,10 @@ instance.copy.fail=Could not copy instance
|
|||
instance.kill.fail=Could not kill the Instance
|
||||
instance.kill.prompt=Are you sure?
|
||||
instance.kill.details=Killing this Instance may cause data corruption and should be avoided if possible
|
||||
instance.kill=Kill
|
||||
instance.kill=Kill
|
||||
menu.file.new.url=From URL
|
||||
menu.file.new.url.details=Select the URL to import from
|
||||
menu.file.new.url.error=Could not import Instance
|
||||
menu.file.new.file=From File
|
||||
menu.file.new.file.error=Could not import Instance
|
||||
menu.file.new.new=Create
|
|
@ -103,4 +103,10 @@ instance.copy.fail=Konnte die Instanz nicht kopieren
|
|||
instance.kill.fail=Ausführung konnte nicht abgebrochen werden
|
||||
instance.kill.prompt=Sind Sie sicher?
|
||||
instance.kill.details=Das vorzeitige Beenden von Instanzen kann zu Datenverlust führen und sollte vermieden werden
|
||||
instance.kill=Beenden
|
||||
instance.kill=Beenden
|
||||
menu.file.new.url=Von einem URL
|
||||
menu.file.new.url.details=Wähle den URL, von dem die Instanz importiert werden soll
|
||||
menu.file.new.url.error=Konnte Instanz nicht importieren
|
||||
menu.file.new.file=Aus einer Datei
|
||||
menu.file.new.file.error=Konnte Instanz nicht importieren
|
||||
menu.file.new.new=Erstellen
|
|
@ -22,10 +22,10 @@ public class GuiUtil {
|
|||
try {
|
||||
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 -> {
|
||||
GuiMain.open(new ProcessStateWatcherWindow("Reloading data", "Could not reload resources", state, () -> {
|
||||
InstanceList.reset();
|
||||
InstanceList.forEach(instance -> Steps.reDownload(instance, state));
|
||||
}, null));
|
||||
}));
|
||||
} catch (IOException e) {
|
||||
LauncherEnv.showError("Could not reload", e);
|
||||
}
|
||||
|
@ -33,27 +33,28 @@ public class GuiUtil {
|
|||
|
||||
public static void reload(Instance instance) {
|
||||
ProcessState state = Steps.createProcessState();
|
||||
GuiMain.open(new ProcessStateWatcherWindow("Reloading data", "Could not reload resources", state, cToken -> {
|
||||
GuiMain.open(new ProcessStateWatcherWindow("Reloading data", "Could not reload resources", state, () -> {
|
||||
Steps.reDownload(instance, state);
|
||||
}, null));
|
||||
}));
|
||||
}
|
||||
|
||||
public static void createInstance(SetupStepInfo state) {
|
||||
ProcessState pState = Steps.createProcessState();
|
||||
pState.updateStep("Starting install process");
|
||||
GuiMain.open(new ProcessStateWatcherWindow("Creating Instance", "Could not create instance", pState, cToken -> {
|
||||
GuiMain.open(new ProcessStateWatcherWindow("Creating Instance", "Could not create instance", pState, () -> {
|
||||
for (Step step : Steps.STEPS) {
|
||||
if (cToken.get()) return;
|
||||
if (state.isCancelled) {
|
||||
try {
|
||||
JFiles.deleteRecursive(MetaHolder.INSTANCE_DIR.resolve(state.name));
|
||||
} catch (IOException e) {
|
||||
Utils.LOGGER.error("Could not delete instance dir", e);
|
||||
}
|
||||
return;
|
||||
}
|
||||
pState.incrementStep(step.name);
|
||||
step.execute(state, cToken);
|
||||
step.execute(state);
|
||||
}
|
||||
LauncherEnv.showInfo("The instance was successfully created. You can now launch it using the main menu", "Successfully installed");
|
||||
}, () -> {
|
||||
try {
|
||||
JFiles.deleteRecursive(MetaHolder.INSTANCE_DIR.resolve(state.name));
|
||||
} catch (IOException e) {
|
||||
Utils.LOGGER.error("Could not delete instance dir", e);
|
||||
}
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -51,12 +51,13 @@ public class NewInstanceWindow extends Window {
|
|||
List<Path> packs = GuiMain.openFileDialog("Import Pack", null, new String[]{"*.zip", "*.mrpack"}, "Modpack", true);
|
||||
if (!packs.isEmpty()) {
|
||||
ProcessState state = new ProcessState(Importers.MAX_STEPS * packs.size(), "Initializing");
|
||||
GuiMain.open(new ProcessStateWatcherWindow("Importing", "Could not import packs", state, cToken -> {
|
||||
GuiMain.open(new ProcessStateWatcherWindow("Importing", "Could not import packs", state, () -> {
|
||||
for (Path pack : packs) {
|
||||
Path imported = Importers.importPack(pack, state);
|
||||
LauncherEnv.showInfo(pack.fileName + " has been successfully imported as " + imported.fileName, "Imported pack");
|
||||
if (state.isCancelled) return;
|
||||
Path imported = Importers.importPack(pack, state).path();
|
||||
if (!state.isCancelled) LauncherEnv.showInfo(pack.fileName + " has been successfully imported as " + imported.fileName, "Imported pack");
|
||||
}
|
||||
}, null));
|
||||
}));
|
||||
}
|
||||
}
|
||||
ImGui.endTabItem();
|
||||
|
|
|
@ -1,30 +1,26 @@
|
|||
package io.gitlab.jfronny.inceptum.imgui.window.dialog;
|
||||
|
||||
import imgui.ImGui;
|
||||
import io.gitlab.jfronny.commons.throwable.ThrowingConsumer;
|
||||
import io.gitlab.jfronny.commons.throwable.ThrowingRunnable;
|
||||
import io.gitlab.jfronny.inceptum.imgui.window.Window;
|
||||
import io.gitlab.jfronny.inceptum.launcher.LauncherEnv;
|
||||
import io.gitlab.jfronny.inceptum.launcher.util.ProcessState;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
public class ProcessStateWatcherWindow extends Window {
|
||||
private final ProcessState state;
|
||||
private final Runnable cancel;
|
||||
private final AtomicBoolean canceled = new AtomicBoolean(false);
|
||||
private boolean finished;
|
||||
|
||||
public ProcessStateWatcherWindow(String title, String errorMessage, ProcessState state, ThrowingConsumer<AtomicBoolean, ?> executor, @Nullable Runnable cancel) {
|
||||
public ProcessStateWatcherWindow(String title, String errorMessage, ProcessState state, ThrowingRunnable<?> executor) {
|
||||
super(title);
|
||||
this.state = state;
|
||||
this.cancel = cancel;
|
||||
new Thread(() -> {
|
||||
try {
|
||||
executor.accept(canceled);
|
||||
executor.run();
|
||||
} catch (Throwable e) {
|
||||
canceled.set(true);
|
||||
state.cancel();
|
||||
LauncherEnv.showError(errorMessage, e);
|
||||
} finally {
|
||||
finished = true;
|
||||
close();
|
||||
}
|
||||
}).start();
|
||||
|
@ -38,8 +34,8 @@ public class ProcessStateWatcherWindow extends Window {
|
|||
public void draw() {
|
||||
ImGui.progressBar(state.progress);
|
||||
ImGui.textUnformatted(state.currentStep);
|
||||
if (cancel != null && ImGui.button("Cancel")) {
|
||||
canceled.set(true);
|
||||
if (ImGui.button("Cancel")) {
|
||||
state.cancel();
|
||||
close();
|
||||
}
|
||||
}
|
||||
|
@ -47,7 +43,7 @@ public class ProcessStateWatcherWindow extends Window {
|
|||
@Override
|
||||
public void close() {
|
||||
super.close();
|
||||
if (canceled.get() && cancel != null) cancel.run();
|
||||
if (!finished) state.cancel();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -39,10 +39,10 @@ public class ExportTab extends Tab {
|
|||
Path exportPath = GuiMain.saveFileDialog("Export " + exporter.name + " Pack", defaultName, new String[]{filter}, exporter.name + " packs (" + filter + ")");
|
||||
if (exportPath != null) {
|
||||
ProcessState state = new ProcessState(Exporters.STEP_COUNT, "Initializing...");
|
||||
GuiMain.open(new ProcessStateWatcherWindow("Exporting", "Could not export pack", state, cToken -> {
|
||||
GuiMain.open(new ProcessStateWatcherWindow("Exporting", "Could not export pack", state, () -> {
|
||||
exporter.generate(state, window.instance, exportPath);
|
||||
LauncherEnv.showInfo(window.instance.name + " has been successfully exported to " + exportPath, "Successfully exported");
|
||||
}, null));
|
||||
if (!state.isCancelled) LauncherEnv.showInfo(window.instance.name + " has been successfully exported to " + exportPath, "Successfully exported");
|
||||
}));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -23,7 +23,7 @@ public abstract class Importer<T> {
|
|||
private final String manifestFile;
|
||||
private final ThrowingFunction<Path, T, IOException> manifestClass;
|
||||
|
||||
public Importer(String name, String manifestFile, ThrowingFunction<Path, T, IOException> manifestClass) {
|
||||
protected Importer(String name, String manifestFile, ThrowingFunction<Path, T, IOException> manifestClass) {
|
||||
this.name = name;
|
||||
this.manifestFile = manifestFile;
|
||||
this.manifestClass = manifestClass;
|
||||
|
@ -49,7 +49,8 @@ public abstract class Importer<T> {
|
|||
}
|
||||
}
|
||||
|
||||
public Path importPack(Path sourceRoot, ProcessState state) throws IOException {
|
||||
public Instance importPack(Path sourceRoot, ProcessState state) throws IOException {
|
||||
if (state.isCancelled) return null;
|
||||
state.incrementStep("Generating skeleton");
|
||||
T manifest = manifestClass.apply(sourceRoot.resolve(manifestFile));
|
||||
IntermediaryManifest man = validateManifest(manifest, sourceRoot);
|
||||
|
@ -62,9 +63,17 @@ public abstract class Importer<T> {
|
|||
meta.gameVersion = createVersionString(man.gameVersion, man.fabricVersion);
|
||||
GC_InstanceMeta.write(meta, iDir.resolve(Instance.CONFIG_NAME));
|
||||
|
||||
if (state.isCancelled) {
|
||||
JFiles.deleteRecursive(iDir);
|
||||
return null;
|
||||
}
|
||||
state.incrementStep("Downloading mods");
|
||||
downloadMods(manifest, iDir, state);
|
||||
|
||||
if (state.isCancelled) {
|
||||
JFiles.deleteRecursive(iDir);
|
||||
return null;
|
||||
}
|
||||
state.incrementStep("Adding overrides");
|
||||
if (man.overridesPath() != null) {
|
||||
Path overridesPath = sourceRoot.resolve(man.overridesPath);
|
||||
|
@ -72,9 +81,18 @@ public abstract class Importer<T> {
|
|||
JFiles.copyRecursive(path, iDir.resolve(path.fileName.toString()));
|
||||
});
|
||||
}
|
||||
if (state.isCancelled) {
|
||||
JFiles.deleteRecursive(iDir);
|
||||
return null;
|
||||
}
|
||||
Instance.setSetupLock(iDir, false);
|
||||
Steps.reDownload(InstanceList.read(iDir), state);
|
||||
return iDir;
|
||||
Instance instance = InstanceList.read(iDir);
|
||||
Steps.reDownload(instance, state);
|
||||
if (state.isCancelled) {
|
||||
JFiles.deleteRecursive(iDir);
|
||||
return null;
|
||||
}
|
||||
return instance;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
|
|
|
@ -1,14 +1,18 @@
|
|||
package io.gitlab.jfronny.inceptum.launcher.system.importer;
|
||||
|
||||
import io.gitlab.jfronny.inceptum.common.Net;
|
||||
import io.gitlab.jfronny.inceptum.common.Utils;
|
||||
import io.gitlab.jfronny.inceptum.launcher.api.CurseforgeApi;
|
||||
import io.gitlab.jfronny.inceptum.launcher.system.instance.Instance;
|
||||
import io.gitlab.jfronny.inceptum.launcher.system.setup.Steps;
|
||||
import io.gitlab.jfronny.inceptum.launcher.util.ProcessState;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.URISyntaxException;
|
||||
import java.nio.file.FileSystem;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.*;
|
||||
import java.util.List;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
public class Importers {
|
||||
public static final int MAX_STEPS = Steps.STEPS.size() + 3;
|
||||
|
@ -17,7 +21,7 @@ public class Importers {
|
|||
public static final MultiMCImporter MULTI_MC = new MultiMCImporter();
|
||||
public static final List<Importer<?>> IMPORTERS = List.of(CURSE_FORGE, MODRINTH, MULTI_MC);
|
||||
|
||||
public static Path importPack(Path zipPath, ProcessState state) throws IOException {
|
||||
public static Instance importPack(Path zipPath, ProcessState state) throws IOException {
|
||||
try (FileSystem fs = Utils.openZipFile(zipPath, false)) {
|
||||
for (Importer<?> importer : IMPORTERS) {
|
||||
if (importer.canImport(fs.getPath("."))) {
|
||||
|
@ -29,4 +33,23 @@ public class Importers {
|
|||
}
|
||||
throw new IOException("Could not import pack: unsupported format");
|
||||
}
|
||||
|
||||
private static final Pattern CURSEFORGE_URL = Pattern.compile("curseforge://install\\?addonId=(\\d+)&fileId=(\\d+)");
|
||||
|
||||
public static Instance importPack(String url, ProcessState state) throws IOException {
|
||||
Path tmp = Files.createTempFile("inceptum", url.endsWith(".mrpack") ? ".mrpack" : ".zip");
|
||||
try {
|
||||
state.updateStep("Downloading Pack");
|
||||
Matcher m = CURSEFORGE_URL.matcher(url);
|
||||
if (m.matches()) {
|
||||
url = CurseforgeApi.getFile(Integer.parseInt(m.group(1)), Integer.parseInt(m.group(2))).downloadUrl;
|
||||
}
|
||||
Net.downloadFile(url, tmp);
|
||||
return importPack(tmp, state);
|
||||
} catch (URISyntaxException e) {
|
||||
throw new IOException("Invalid URL", e);
|
||||
} finally {
|
||||
Files.deleteIfExists(tmp);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -21,7 +21,6 @@ import java.io.IOException;
|
|||
import java.net.URISyntaxException;
|
||||
import java.nio.file.*;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
public class InstanceLauncher {
|
||||
|
@ -65,7 +64,7 @@ public class InstanceLauncher {
|
|||
versionInfo = FabricMetaApi.addFabric(versionInfo, instance.loaderVersion, launchType.fabricMetaType);
|
||||
}
|
||||
// Ensure libs/assets are present
|
||||
DownloadLibrariesStep.execute(versionInfo, new AtomicBoolean(false), new ProcessState());
|
||||
DownloadLibrariesStep.execute(versionInfo, new ProcessState());
|
||||
// Prepare arguments
|
||||
List<String> args = new LinkedList<>();
|
||||
// JVM path
|
||||
|
|
|
@ -11,4 +11,8 @@ public record SetupStepInfo(VersionInfo version,
|
|||
public void setState(String state) {
|
||||
currentState.updateStep(state);
|
||||
}
|
||||
|
||||
public boolean isCancelled() {
|
||||
return currentState.isCancelled;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,9 +1,8 @@
|
|||
package io.gitlab.jfronny.inceptum.launcher.system.setup;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
public interface Step {
|
||||
void execute(SetupStepInfo info, AtomicBoolean stopThread) throws IOException;
|
||||
void execute(SetupStepInfo info) throws IOException;
|
||||
String getName();
|
||||
}
|
||||
|
|
|
@ -11,7 +11,6 @@ import io.gitlab.jfronny.inceptum.launcher.util.ProcessState;
|
|||
|
||||
import java.io.IOException;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
public class Steps {
|
||||
public static Set<Step> STEPS = new LinkedHashSet<>(List.of(
|
||||
|
@ -29,10 +28,6 @@ public class Steps {
|
|||
}
|
||||
|
||||
public static void reDownload(Instance instance, ProcessState state) throws IOException {
|
||||
reDownload(instance, state, new AtomicBoolean(false));
|
||||
}
|
||||
|
||||
public static void reDownload(Instance instance, ProcessState state, AtomicBoolean cancel) throws IOException {
|
||||
if (instance.isLocked) return;
|
||||
boolean found = false;
|
||||
for (VersionsListInfo version : McApi.getVersions().versions) {
|
||||
|
@ -47,8 +42,8 @@ public class Steps {
|
|||
SetupStepInfo info = new SetupStepInfo(vi, li, instance.name, state);
|
||||
for (Step step : Steps.STEPS) {
|
||||
state.incrementStep(step.name);
|
||||
step.execute(info, cancel);
|
||||
if (cancel.get()) return;
|
||||
step.execute(info);
|
||||
if (state.isCancelled) return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,24 +2,20 @@ package io.gitlab.jfronny.inceptum.launcher.system.setup.steps;
|
|||
|
||||
import io.gitlab.jfronny.inceptum.common.MetaHolder;
|
||||
import io.gitlab.jfronny.inceptum.launcher.api.McApi;
|
||||
import io.gitlab.jfronny.inceptum.launcher.model.mojang.AssetIndex;
|
||||
import io.gitlab.jfronny.inceptum.launcher.system.setup.SetupStepInfo;
|
||||
import io.gitlab.jfronny.inceptum.launcher.system.setup.Step;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.URISyntaxException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
public class DownloadAssetsStep implements Step {
|
||||
@Override
|
||||
public void execute(SetupStepInfo info, AtomicBoolean stopThread) throws IOException {
|
||||
public void execute(SetupStepInfo info) throws IOException {
|
||||
Path o = MetaHolder.ASSETS_DIR.resolve("objects");
|
||||
try {
|
||||
for (var entry : McApi.getAssetIndex(info.version).objects) {
|
||||
if (stopThread.get()) return;
|
||||
if (info.isCancelled) return;
|
||||
Path fPath = o.resolve(entry.value.hash.substring(0, 2));
|
||||
if (!Files.exists(fPath)) Files.createDirectories(fPath);
|
||||
fPath = fPath.resolve(entry.value.hash);
|
||||
|
|
|
@ -10,11 +10,10 @@ import io.gitlab.jfronny.inceptum.launcher.util.GameVersionParser;
|
|||
import java.io.IOException;
|
||||
import java.net.URISyntaxException;
|
||||
import java.nio.file.*;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
public class DownloadClientStep implements Step {
|
||||
@Override
|
||||
public void execute(SetupStepInfo info, AtomicBoolean stopThread) throws IOException {
|
||||
public void execute(SetupStepInfo info) throws IOException {
|
||||
Path clientPath = MetaHolder.LIBRARIES_DIR.resolve("net/minecraft/client");
|
||||
Path serverPath = MetaHolder.LIBRARIES_DIR.resolve("net/minecraft/server");
|
||||
if (!Files.exists(clientPath)) Files.createDirectories(clientPath);
|
||||
|
|
|
@ -11,12 +11,10 @@ import java.io.IOException;
|
|||
import java.net.URISyntaxException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
public class DownloadJavaStep implements Step {
|
||||
@Override
|
||||
public void execute(SetupStepInfo info, AtomicBoolean stopThread) throws IOException {
|
||||
public void execute(SetupStepInfo info) throws IOException {
|
||||
VersionInfo.JavaVersion ver = info.version.javaVersion;
|
||||
Path jvmDir = MetaHolder.NATIVES_DIR.resolve(ver.component).resolve(Integer.toString(ver.majorVersion));
|
||||
if (Files.exists(jvmDir)) return;
|
||||
|
|
|
@ -15,12 +15,11 @@ import java.io.FileNotFoundException;
|
|||
import java.io.IOException;
|
||||
import java.net.URISyntaxException;
|
||||
import java.nio.file.*;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
public class DownloadLibrariesStep implements Step {
|
||||
@Override
|
||||
public void execute(SetupStepInfo info, AtomicBoolean stopThread) throws IOException {
|
||||
execute(info.version, stopThread, info.currentState);
|
||||
public void execute(SetupStepInfo info) throws IOException {
|
||||
execute(info.version, info.currentState);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -28,9 +27,9 @@ public class DownloadLibrariesStep implements Step {
|
|||
return "Downloading Libraries";
|
||||
}
|
||||
|
||||
public static void execute(VersionInfo version, AtomicBoolean stopThread, ProcessState currentState) throws IOException {
|
||||
public static void execute(VersionInfo version, ProcessState currentState) throws IOException {
|
||||
for (ArtifactInfo artifact : VersionInfoLibraryResolver.getRelevant(version)) {
|
||||
if (stopThread.get()) return;
|
||||
if (currentState.isCancelled) return;
|
||||
Path path = MetaHolder.LIBRARIES_DIR.resolve(artifact.path);
|
||||
if (!Files.exists(path)) {
|
||||
currentState.updateStep("Downloading library: " + artifact.path);
|
||||
|
|
|
@ -9,11 +9,10 @@ import io.gitlab.jfronny.inceptum.launcher.system.setup.Step;
|
|||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Path;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
public class RunMdsStep implements Step {
|
||||
@Override
|
||||
public void execute(SetupStepInfo info, AtomicBoolean stopThread) throws IOException {
|
||||
public void execute(SetupStepInfo info) throws IOException {
|
||||
info.setState("Running MDS");
|
||||
Path instance = MetaHolder.INSTANCE_DIR.resolve(info.name);
|
||||
ModsDirScanner.get(instance.resolve("mods"), GC_InstanceMeta.read(instance.resolve(Instance.CONFIG_NAME)))
|
||||
|
|
|
@ -10,11 +10,10 @@ import io.gitlab.jfronny.inceptum.launcher.util.GameVersionParser;
|
|||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
public class SetupDirsStep implements Step {
|
||||
@Override
|
||||
public void execute(SetupStepInfo info, AtomicBoolean stopThread) throws IOException {
|
||||
public void execute(SetupStepInfo info) throws IOException {
|
||||
info.setState("Setting up instance dirs");
|
||||
Path iDir = MetaHolder.INSTANCE_DIR.resolve(info.name);
|
||||
Instance.setSetupLock(iDir, true);
|
||||
|
|
|
@ -10,11 +10,10 @@ import io.gitlab.jfronny.inceptum.launcher.system.setup.Step;
|
|||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
public class WriteMetadataStep implements Step {
|
||||
@Override
|
||||
public void execute(SetupStepInfo info, AtomicBoolean stopThread) throws IOException {
|
||||
public void execute(SetupStepInfo info) throws IOException {
|
||||
info.setState("Writing metadata");
|
||||
Path instance = MetaHolder.INSTANCE_DIR.resolve(info.name);
|
||||
Path metaPath = instance.resolve(Instance.CONFIG_NAME);
|
||||
|
|
|
@ -6,6 +6,7 @@ public class ProcessState {
|
|||
private final int maxSteps;
|
||||
private int stepIndex;
|
||||
private String stepDescription;
|
||||
private boolean isCancelled = false;
|
||||
|
||||
public ProcessState() {
|
||||
this(0, "");
|
||||
|
@ -43,4 +44,12 @@ public class ProcessState {
|
|||
public float getProgress() {
|
||||
return ((float) stepIndex) / maxSteps;
|
||||
}
|
||||
|
||||
public boolean isCancelled() {
|
||||
return isCancelled;
|
||||
}
|
||||
|
||||
public void cancel() {
|
||||
isCancelled = true;
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue