GTK: Start working on instance creation flow
ci/woodpecker/push/docs Pipeline was successful Details
ci/woodpecker/push/woodpecker Pipeline failed Details

This commit is contained in:
Johannes Frohnmeyer 2023-01-29 18:34:33 +01:00
parent 4967410f51
commit c027885364
Signed by: Johannes
GPG Key ID: E76429612C2929F4
26 changed files with 226 additions and 130 deletions

View File

@ -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);
}
}

View File

@ -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);

View File

@ -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";

View File

@ -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);
}
}
}

View File

@ -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();

View File

@ -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
}
);
}
}

View File

@ -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

View File

@ -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

View File

@ -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);
}
}));
}
}

View File

@ -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();

View File

@ -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

View File

@ -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");
}));
}
}
}

View File

@ -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() {

View File

@ -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);
}
}
}

View File

@ -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

View File

@ -11,4 +11,8 @@ public record SetupStepInfo(VersionInfo version,
public void setState(String state) {
currentState.updateStep(state);
}
public boolean isCancelled() {
return currentState.isCancelled;
}
}

View File

@ -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();
}

View File

@ -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;
}
}
}

View File

@ -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);

View File

@ -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);

View File

@ -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;

View File

@ -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);

View File

@ -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)))

View File

@ -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);

View File

@ -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);

View File

@ -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;
}
}