GTK: Implement instance exporting, break launching
ci/woodpecker/push/docs Pipeline was successful Details
ci/woodpecker/push/woodpecker Pipeline was successful Details

This commit is contained in:
Johannes Frohnmeyer 2023-01-28 13:26:50 +01:00
parent e15ef8c485
commit a93f2c8411
Signed by: Johannes
GPG Key ID: E76429612C2929F4
20 changed files with 355 additions and 92 deletions

View File

@ -24,9 +24,9 @@ public class ExportCommand extends BaseInstanceCommand {
@Override
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");
Exporters.CURSE_FORGE.generate(new ProcessState(), instance, Paths.get(args[0]), args[1]);
if (args.length > 2) throw new IllegalAccessException("Too many arguments");
if (args.length > 1) instance.meta.instanceVersion = args[1];
Exporters.CURSE_FORGE.generate(new ProcessState(), instance, Paths.get(args[0]));
}
private static class MultiMCExportCommand extends BaseInstanceCommand {
@ -38,7 +38,7 @@ public class ExportCommand extends BaseInstanceCommand {
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");
Exporters.MULTI_MC.generate(new ProcessState(), instance, Paths.get(args[0]), "1.0");
Exporters.MULTI_MC.generate(new ProcessState(), instance, Paths.get(args[0]));
}
}
@ -50,9 +50,9 @@ public class ExportCommand extends BaseInstanceCommand {
@Override
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");
Exporters.MODRINTH.generate(new ProcessState(), instance, Paths.get(args[0]), args[1]);
if (args.length > 2) throw new IllegalAccessException("Too many arguments");
if (args.length > 1) instance.meta.instanceVersion = args[1];
Exporters.MODRINTH.generate(new ProcessState(), instance, Paths.get(args[0]));
}
}
}

View File

@ -67,15 +67,19 @@ public enum GtkEnvBackend implements LauncherEnv.EnvBackend { //TODO test
private void simpleDialog(String markup, String title, MessageType type, ButtonsType buttons, Runnable ok, Runnable cancel) {
GtkMain.schedule(() -> {
MessageDialog dialog = new MessageDialog(dialogParent, DialogFlags.MODAL.or(DialogFlags.DESTROY_WITH_PARENT), type, buttons, null);
dialog.title = title;
dialog.markup = markup;
dialog.onResponse(processResponses(dialog, ok, cancel));
dialog.show();
simpleDialog(dialogParent, markup, title, type, buttons, ok, cancel);
});
}
private Dialog.Response processResponses(Dialog dialog, @Nullable Runnable ok, @Nullable Runnable cancel) {
public static void simpleDialog(Window parent, String markup, String title, MessageType type, ButtonsType buttons, @Nullable Runnable ok, @Nullable Runnable cancel) {
MessageDialog dialog = new MessageDialog(parent, DialogFlags.MODAL.or(DialogFlags.DESTROY_WITH_PARENT), type, buttons, null);
dialog.title = title;
dialog.markup = markup;
dialog.onResponse(processResponses(dialog, ok, cancel));
dialog.show();
}
private static Dialog.Response processResponses(Dialog dialog, @Nullable Runnable ok, @Nullable Runnable cancel) {
return responseId -> {
switch (ResponseType.of(responseId)) {
case OK -> {

View File

@ -9,6 +9,7 @@ import org.gtk.gtk.Application;
import java.io.IOException;
import java.util.*;
import java.util.function.Consumer;
public class GtkMain {
public static final String ID = "io.gitlab.jfronny.inceptum";
@ -23,7 +24,6 @@ public class GtkMain {
LauncherEnv.initialize(GtkEnvBackend.INSTANCE);
Utils.LOGGER.info("Launching Inceptum v" + BuildMetadata.VERSION);
Utils.LOGGER.info("Loading from " + MetaHolder.BASE_PATH);
//TODO look into java-gtk samples for window architecture
int statusCode = -1;
try {
statusCode = showGui(args);
@ -33,12 +33,23 @@ public class GtkMain {
}
}
public static int showGui(String[] args) throws IOException {
var app = new Application(ID, ApplicationFlags.FLAGS_NONE);
app.onActivate(() -> {
public static int showGui(String[] args) {
return setupApplication(args, app -> {
GtkMenubar.create(app);
var window = new MainWindow(app);
window.show();
GtkEnvBackend.INSTANCE.dialogParent = window;
window.onCloseRequest(() -> {
GtkEnvBackend.INSTANCE.dialogParent = null;
app.quit();
return false;
});
});
}
public static int setupApplication(String[] args, Consumer<Application> onActivate) {
var app = new Application(ID, ApplicationFlags.FLAGS_NONE);
app.onActivate(() -> {
GLib.idleAdd(() -> {
Runnable r;
while ((r = SCHEDULED.poll()) != null) {
@ -50,12 +61,7 @@ public class GtkMain {
}
return true;
});
GtkEnvBackend.INSTANCE.dialogParent = window;
window.onCloseRequest(() -> {
GtkEnvBackend.INSTANCE.dialogParent = null;
app.quit();
return false;
});
onActivate.accept(app);
});
return app.run(args);
}

View File

@ -1,6 +1,8 @@
package io.gitlab.jfronny.inceptum.gtk;
import io.gitlab.jfronny.inceptum.gtk.window.dialog.ProcessStateWatcherDialog;
import io.gitlab.jfronny.inceptum.launcher.system.launch.*;
import io.gitlab.jfronny.inceptum.launcher.util.ProcessState;
import org.gtk.gtk.Application;
import io.gitlab.jfronny.commons.ref.R;
import io.gitlab.jfronny.inceptum.common.Utils;
@ -54,32 +56,47 @@ public class GtkMenubar {
}
public static void launch(Instance instance, LaunchType launchType) {
//TODO show popup during launch w/ cancel option (lock main UI)
Runnable launch = () -> {
try {
Steps.reDownload(instance, Steps.createProcessState());
} catch (IOException e) {
Utils.LOGGER.error("Could not redownload instance, trying to start anyways", e);
}
if (launchType == LaunchType.Client) InstanceLauncher.launchClient(instance);
else {
try {
InstanceLauncher.launch(instance, launchType, false, AccountManager.NULL_AUTH);
} catch (LaunchException | IOException e) {
LauncherEnv.showError("Could not start instance", e);
}
}
};
if (instance.isSetupLocked) {
LauncherEnv.showError(I18n.get("instance.launch.locked.setup"), I18n.get("instance.launch.locked"));
return;
}
if (instance.isRunningLocked) {
LauncherEnv.showOkCancel(I18n.get("instance.launch.locked.running"), I18n.get("instance.launch.locked"), () -> {
new Thread(launch).start(); //TODO loom
}, R::nop);
LauncherEnv.showOkCancel(
I18n.get("instance.launch.locked.running"),
I18n.get("instance.launch.locked"),
() -> forceLaunch(instance, launchType),
R::nop
);
}
new Thread(launch).start();
forceLaunch(instance, launchType);
}
private static void forceLaunch(Instance instance, LaunchType launchType) {
ProcessState state = Steps.createProcessState();
ProcessStateWatcherDialog.show(
GtkEnvBackend.INSTANCE.dialogParent,
I18n.get("instance.launch.title"),
I18n.get("instance.launch.error"),
state,
cancel -> {
try {
Steps.reDownload(instance, Steps.createProcessState(), cancel);
} catch (IOException e) {
Utils.LOGGER.error("Could not fetch instance, trying to start anyways", e);
}
if (!cancel.get()) {
if (launchType == LaunchType.Client) InstanceLauncher.launchClient(instance);
else {
try {
InstanceLauncher.launch(instance, launchType, false, AccountManager.NULL_AUTH);
} catch (LaunchException | IOException e) {
LauncherEnv.showError("Could not start instance", e);
}
}
}
},
R::nop
);
}
public static void generateAccountsMenu() {

View File

@ -31,12 +31,12 @@ public class ILabel extends Label {
}
}
public ILabel(@PropertyKey(resourceBundle = I18n.BUNDLE) String str) {
this(str, Mode.NORMAL);
public ILabel(@PropertyKey(resourceBundle = I18n.BUNDLE) String str, Object... args) {
this(str, Mode.NORMAL, args);
}
public ILabel(@PropertyKey(resourceBundle = I18n.BUNDLE) String str, Mode mode) {
super(I18n.get(str));
public ILabel(@PropertyKey(resourceBundle = I18n.BUNDLE) String str, Mode mode, Object... args) {
super(I18n.get(str, args));
theme(this, mode);
}

View File

@ -6,16 +6,16 @@ import org.jetbrains.annotations.Nullable;
import org.jetbrains.annotations.PropertyKey;
public class IRow extends Box {
public IRow(@PropertyKey(resourceBundle = I18n.BUNDLE) String title, @PropertyKey(resourceBundle = I18n.BUNDLE) @Nullable String subtitle) {
public IRow(@PropertyKey(resourceBundle = I18n.BUNDLE) String title, @PropertyKey(resourceBundle = I18n.BUNDLE) @Nullable String subtitle, Object... args) {
super(Orientation.HORIZONTAL, 40);
margin = 8;
Widget head;
ILabel lab = new ILabel(title);
ILabel lab = new ILabel(title, args);
lab.halign = Align.START;
if (subtitle != null) {
Box headB = new Box(Orientation.VERTICAL, 0);
headB.append(lab);
ILabel lab1 = new ILabel(subtitle, ILabel.Mode.SUBTITLE);
ILabel lab1 = new ILabel(subtitle, ILabel.Mode.SUBTITLE, args);
lab1.halign = Align.START;
headB.append(lab1);
head = headB;

View File

@ -1,18 +1,25 @@
package io.gitlab.jfronny.inceptum.gtk.window;
import io.gitlab.jfronny.commons.ArgumentsTokenizer;
import io.gitlab.jfronny.commons.StringFormatter;
import io.gitlab.jfronny.commons.ref.R;
import io.gitlab.jfronny.inceptum.common.MetaHolder;
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.control.*;
import io.gitlab.jfronny.inceptum.gtk.util.I18n;
import io.gitlab.jfronny.inceptum.launcher.LauncherEnv;
import io.gitlab.jfronny.inceptum.gtk.window.dialog.ProcessStateWatcherDialog;
import io.gitlab.jfronny.inceptum.launcher.system.exporter.Exporter;
import io.gitlab.jfronny.inceptum.launcher.system.exporter.Exporters;
import io.gitlab.jfronny.inceptum.launcher.system.instance.*;
import org.gnome.adw.*;
import io.gitlab.jfronny.inceptum.launcher.util.ProcessState;
import org.gnome.adw.HeaderBar;
import org.gnome.adw.*;
import org.gtk.gobject.BindingFlags;
import org.gtk.gtk.*;
import org.gtk.gtk.Application;
import org.gtk.gtk.Window;
import org.gtk.gtk.*;
import java.io.IOException;
import java.nio.file.Files;
@ -20,8 +27,11 @@ import java.nio.file.Path;
import java.util.List;
public class InstanceSettingsWindow extends Window {
private final Instance instance;
public InstanceSettingsWindow(Application app, Instance instance) {
this.application = app;
this.instance = instance;
ViewStack stack = new ViewStack();
@ -71,7 +81,15 @@ public class InstanceSettingsWindow extends Window {
close();
new InstanceSettingsWindow(application, InstanceList.read(newPath)).show();
} catch (IOException e) {
LauncherEnv.showError("Could not rename", e);
GtkEnvBackend.simpleDialog(
this,
StringFormatter.toString(e),
"Could not rename",
MessageType.ERROR,
ButtonsType.CLOSE,
null,
null
);
}
});
entry.onChanged(s -> {
@ -133,7 +151,8 @@ public class InstanceSettingsWindow extends Window {
var box = new Box(Orientation.VERTICAL, 0);
box.marginHorizontal = 24;
box.marginTop = 12;
//TODO show "currently unsupported in GTK UI (idk how I'd visualize this)
box.append(new ILabel("instance.settings.mods.unsupported"));
//TODO implement this, somehow
stack.addTitledWithIcon(box, null, I18n.get("instance.settings.mods"), "package-x-generic-symbolic");
}
@ -142,10 +161,100 @@ public class InstanceSettingsWindow extends Window {
var box = new Box(Orientation.VERTICAL, 0);
box.marginHorizontal = 24;
box.marginTop = 12;
//TODO CurseForge (button) -> see imgui
//TODO Modrinth (button) -> see imgui
//TODO MultiMC (button) -> see imgui
{
Frame frame = new Frame(null);
ListBox lb = new ListBox();
lb.selectionMode = SelectionMode.NONE;
frame.child = lb;
box.append(frame);
{
var row = new IRow("instance.settings.export.version", "instance.settings.export.version.subtitle");
ISEntry entry = new ISEntry(instance.meta.instanceVersion);
entry.onChanged(s -> {
instance.meta.instanceVersion = s;
instance.writeMeta();
});
row.append(entry);
lb.append(row);
}
for (Exporter<?> exporter : Exporters.EXPORTERS) {
var row = new IRow("instance.settings.export.title", "instance.settings.export.subtitle", exporter.name, exporter.fileExtension);
row.firstChild.hexpand = true;
Button btn = Button.newWithLabel(I18n.get("instance.settings.export"));
btn.valign = Align.CENTER;
btn.halign = Align.END;
btn.onClicked(() -> {
FileChooserNative dialog = new FileChooserNative(
I18n.get("instance.settings.export.dialog.title", exporter.name),
this,
FileChooserAction.SAVE,
"_" + I18n.get("save"),
"_" + I18n.get("cancel")
);
dialog.currentName = exporter.getDefaultFileName(instance);
dialog.onResponse(responseId -> {
if (responseId == ResponseType.ACCEPT.value) {
var file = dialog.getFile().path;
if (file == null) {
GtkEnvBackend.simpleDialog(
this,
"The path returned by the file dialog is null",
"Could not export",
MessageType.ERROR,
ButtonsType.CLOSE,
null,
null
);
return;
}
export(exporter, Path.of(file));
}
});
dialog.show();
});
row.append(btn);
lb.append(row);
}
}
stack.addTitledWithIcon(box, null, I18n.get("instance.settings.export"), "send-to-symbolic");
}
}
private void export(Exporter<?> exporter, Path path) {
ProcessState state = new ProcessState(Exporters.STEP_COUNT, "Initializing...");
ProcessStateWatcherDialog.show(
this,
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(() -> {
Dialog success = Dialog.newWithButtons(
I18n.get("instance.settings.export.dialog.success.title"),
this,
DialogFlags.MODAL.or(DialogFlags.DESTROY_WITH_PARENT),
I18n.get("show"),
ResponseType.OK,
I18n.get("ok"),
ResponseType.CANCEL,
null
);
success.contentArea.append(new ILabel("instance.settings.export.dialog.success", instance.name, path.toString()));
success.onResponse(responseId1 -> {
switch (ResponseType.of(responseId1)) {
case OK -> {
success.close();
Utils.openFile(path.toFile());
}
case CLOSE, CANCEL -> success.close();
case DELETE_EVENT -> success.destroy();
}
});
success.show();
});
},
R::nop
);
}
}

View File

@ -0,0 +1,79 @@
package io.gitlab.jfronny.inceptum.gtk.window.dialog;
import io.gitlab.jfronny.commons.throwable.ThrowingConsumer;
import io.gitlab.jfronny.inceptum.gtk.GtkMain;
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 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);
dialog.show();
return dialog;
}
public ProcessStateWatcherDialog(Window parent, String title, String errorMessage, ProcessState state, ThrowingConsumer<AtomicBoolean, ?> executor, @Nullable Runnable cancel) {
super(parent, DialogFlags.MODAL.or(DialogFlags.DESTROY_WITH_PARENT), MessageType.INFO, ButtonsType.CANCEL, null);
this.state = state;
this.cancel = cancel;
this.title = title;
if (cancel != null) addButton("Cancel", ResponseType.CANCEL.value);
onResponse(responseId -> {
if (responseId == ResponseType.CANCEL.value) {
canceled.set(true);
close();
}
});
onCloseRequest(() -> {
if (finished.get()) return false;
if (cancel == null) return true;
canceled.set(true);
cancel.run();
return false;
});
onClose(() -> {
if (canceled.get() && cancel != null) cancel.run();
});
var progress = new ProgressBar();
contentArea.append(progress);
addTickCallback((widget, clock) -> {
if (finished.get()) return GLib.SOURCE_REMOVE;
var nc = new State(state);
if (!nc.equals(cachedState)) {
cachedState = nc;
setMarkup(cachedState.msg);
progress.fraction = cachedState.progress;
widget.queueDraw();
}
return GLib.SOURCE_CONTINUE;
}, null);
new Thread(() -> {
try {
executor.accept(canceled);
} catch (Throwable e) {
canceled.set(true);
LauncherEnv.showError(errorMessage, e);
} finally {
finished.set(true);
GtkMain.schedule(this::close);
}
}).start();
}
record State(String msg, float progress) {
State(ProcessState source) {
this(source.currentStep, source.progress);
}
}
}

View File

@ -50,4 +50,18 @@ instance.settings.general.args.server=Server
instance.settings.general.args.client.subtitle=Arguments to add to Minecraft Clients
instance.settings.general.args.server.subtitle=Arguments to add to Minecraft Servers
instance.settings.export=Export
instance.settings.mods=Mods
instance.settings.mods=Mods
instance.settings.mods.unsupported=Mod management is currently unavailable in the GTK UI.\
Please use the ImGUI or CLI to manage mods for now.
instance.settings.export.title=%1$s
instance.settings.export.subtitle=Export this Pack as a %1$s %2$s file
instance.settings.export.version=Version
instance.settings.export.version.subtitle=Version of the exported Pack
save=Save
instance.settings.export.dialog.title=Export %1$s Pack
instance.settings.export.dialog.success=%1$s has been successfully exported to %2$s
instance.settings.export.dialog.success.title=Sucessfully exported
instance.settings.export.dialog.error=Could not export %1$s
instance.launch.error=Could not start instance
instance.launch.title=Starting instance
show=Show

View File

@ -1,6 +1,6 @@
account.none=Keiner
about.contact=Kontakt
about.license=Dieses Programm komt OHNE JEGLICHE GARANTIE.\
about.license=Dieses Programm kommt OHNE JEGLICHE GARANTIE.\
Dies ist freie Software, die Sie unter bestimmten Bedingungen gerne weitergeben dürfen.
menu.account=Konto
menu.account.manage=Verwalten
@ -22,11 +22,11 @@ menu.hamburger.preferences=Einstellungen
menu.hamburger.about=Über
instance.launch.locked=Instanz kann momentan nicht gestartet werden
instance.launch.locked.setup=Diese Instanz wird noch eingerichtet.\
Bitte warte, bis dieser Prozess abgeschlossen ist,\
bevor du sie startest.
Bitte warten Sie, bis dieser Prozess abgeschlossen ist,\
bevor Sie die Instanz starten.
instance.launch.locked.running=Diese Instanz läuft bereits.\
Bestätigen sie den Start mit OK, um sie ein zweites mal zu starten.\
Bitte seien sie sich bewusst, dass dies zu Problemen führen wird und NICHT UNTERSÜTZT ist.
Bestätigen Sie den Start mit OK, um Sie ein zweites Mal zu starten.\
Bitte seien Sie sich bewusst, dass dies zu Problemen führen wird und NICHT UNTERSTÜTZT ist.
instance.launch=Starten
main.empty.title=Willkommen bei Inceptum
main.empty.description=Importiere oder erstelle um anzufangen eine Instanz mit dem +
@ -50,4 +50,18 @@ instance.settings.general.args.server=Server
instance.settings.general.args.client.subtitle=Argumente für Minecraft-Clients
instance.settings.general.args.server.subtitle=Argumente für Minecraft-Server
instance.settings.export=Export
instance.settings.mods=Mods
instance.settings.mods=Mods
instance.settings.mods.unsupported=Die Verwaltung von Modifikationen ist momentan noch in Entwicklung.\
Bitte verwenden Sie vorerst die ImGUI oder CLI, um Mods zu verwalten.
instance.settings.export.title=%1$s
instance.settings.export.subtitle=Dieses Pack als %2$s für %1$s exportieren
instance.settings.export.version=Version
instance.settings.export.version.subtitle=Version des exportierten Packs
save=Speichern
instance.settings.export.dialog.title=%1$s-Pack exportieren
instance.settings.export.dialog.success=%1$s wurde erfolgreich nach %2$s exportiert
instance.settings.export.dialog.success.title=Erfolgreich exportiert
instance.settings.export.dialog.error=Konnte %1$s nicht exportieren
instance.launch.error=Konnte Instanz nicht starten
instance.launch.title=Starting instance
show=Anzeigen

View File

@ -1,9 +1,11 @@
package io.gitlab.jfronny.inceptum.imgui.window.edit;
import imgui.ImGui;
import imgui.type.ImString;
import io.gitlab.jfronny.commons.ref.R;
import io.gitlab.jfronny.inceptum.imgui.GuiMain;
import io.gitlab.jfronny.inceptum.imgui.control.Tab;
import io.gitlab.jfronny.inceptum.imgui.window.GuiUtil;
import io.gitlab.jfronny.inceptum.imgui.window.dialog.ProcessStateWatcherWindow;
import io.gitlab.jfronny.inceptum.imgui.window.dialog.TextBoxWindow;
import io.gitlab.jfronny.inceptum.launcher.LauncherEnv;
@ -15,29 +17,33 @@ import java.nio.file.Path;
public class ExportTab extends Tab {
private final InstanceEditWindow window;
private final ImString instanceVersion = new ImString("", GuiUtil.INPUT_FIELD_LENGTH);
public ExportTab(InstanceEditWindow window) {
super("Export");
this.window = window;
instanceVersion.set(window.instance.meta.instanceVersion);
}
@Override
protected void renderInner() {
if (ImGui.inputTextWithHint("Version", "Version of the exported Pack", instanceVersion)) {
window.instance.meta.instanceVersion = instanceVersion.get();
window.instance.writeMeta();
}
if (window.instance.mds.isComplete) {
for (Exporter<?> exporter : Exporters.EXPORTERS) {
if (ImGui.button(exporter.name)) {
GuiMain.open(new TextBoxWindow("Version", "Please enter the current version of your modpack", "1.0", version -> {
String defaultName = window.instance.name + " " + version + " (" + exporter.name + ")." + exporter.fileExtension;
String filter = "*." + exporter.fileExtension;
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 -> {
exporter.generate(state, window.instance, exportPath, version);
LauncherEnv.showInfo(window.instance.name + " has been successfully exported to " + exportPath, "Successfully exported");
}, null));
}
}, R::nop));
String defaultName = exporter.getDefaultFileName(window.instance);
String filter = "*." + exporter.fileExtension;
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 -> {
exporter.generate(state, window.instance, exportPath);
LauncherEnv.showInfo(window.instance.name + " has been successfully exported to " + exportPath, "Successfully exported");
}, null));
}
}
}
} else {

View File

@ -50,7 +50,7 @@ public class GeneralTab extends Tab {
imc.snapshotsBox();
imc.versionBox(ver -> {
window.reDownload = true;
window.instance.meta().version = ver;
window.instance.meta.gameVersion = ver;
window.instance.writeMeta();
});
if (ImGui.button("Delete"))

View File

@ -8,19 +8,24 @@ import java.util.Objects;
@GSerializable(configure = GsonPreset.Config.class)
public class InstanceMeta {
public String version;
public String instanceVersion = "1.0";
public String gameVersion;
public String java;
public Long minMem;
public Long maxMem;
public Long lastLaunched;
public Arguments arguments;
public void setVersion(String version) {
this.gameVersion = version;
}
@Override
public boolean equals(Object obj) {
if (obj == this) return true;
if (obj == null || obj.getClass() != this.getClass()) return false;
var that = (InstanceMeta) obj;
return Objects.equals(this.version, that.version) &&
return Objects.equals(this.gameVersion, that.gameVersion) &&
Objects.equals(this.java, that.java) &&
Objects.equals(this.minMem, that.minMem) &&
Objects.equals(this.maxMem, that.maxMem) &&
@ -30,7 +35,7 @@ public class InstanceMeta {
@Override
public int hashCode() {
return Objects.hash(version, java, minMem, maxMem, lastLaunched, arguments);
return Objects.hash(gameVersion, java, minMem, maxMem, lastLaunched, arguments);
}
@GSerializable(configure = GsonPreset.Config.class)

View File

@ -28,20 +28,20 @@ public abstract class Exporter<Manifest> {
protected abstract void addMods(Path root, Instance instance, Iterable<Mod> mods, Manifest manifest, Path modsOverrides) throws IOException;
public void generate(ProcessState state, Instance instance, Path exportPath, String version) throws IOException {
public void generate(ProcessState state, Instance instance, Path exportPath) 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, instance, version);
Manifest manifest = generateManifests(root, instance, instance.meta.instanceVersion);
if (instance.isFabric) {
state.incrementStep("Adding mods");
addMods(root, instance, instance.mods.stream().filter(mod -> {
if (!mod.isEnabled) return false;
state.updateStep(mod.name);
return true;
}), manifest, overrides.resolve("mods"));
}).toList(), manifest, overrides.resolve("mods"));
}
state.incrementStep("Adding files");
filesLoop: for (Path path : IgnoringWalk.walk(instance.path)) {
@ -74,4 +74,8 @@ public abstract class Exporter<Manifest> {
public String getFileExtension() {
return fileExtension;
}
public String getDefaultFileName(Instance instance) {
return instance.name + " " + instance.meta.instanceVersion + " (" + name + ")." + fileExtension;
}
}

View File

@ -59,7 +59,7 @@ public abstract class Importer<T> {
final Path iDir = MetaHolder.INSTANCE_DIR.resolve(name).toAbsolutePath().normalize();
Instance.setSetupLock(iDir, true);
InstanceMeta meta = new InstanceMeta();
meta.version = createVersionString(man.gameVersion, man.fabricVersion);
meta.gameVersion = createVersionString(man.gameVersion, man.fabricVersion);
GC_InstanceMeta.write(meta, iDir.resolve(InstanceList.INSTANCE_CONFIG_NAME));
state.incrementStep("Downloading mods");

View File

@ -83,15 +83,15 @@ public record Instance(String id, Path path, InstanceMeta meta, ModsDirScanner m
}
public boolean isFabric() {
return GameVersionParser.isFabric(meta.version);
return GameVersionParser.isFabric(meta.gameVersion);
}
public String getGameVersion() {
return GameVersionParser.getGameVersion(meta.version);
return GameVersionParser.getGameVersion(meta.gameVersion);
}
public String getLoaderVersion() {
return GameVersionParser.getLoaderVersion(meta.version);
return GameVersionParser.getLoaderVersion(meta.gameVersion);
}
public boolean isSetupLocked() {

View File

@ -15,7 +15,7 @@ import java.util.function.BiConsumer;
public interface ModsDirScanner extends Closeable {
static ModsDirScanner get(Path modsDir, InstanceMeta meta) throws IOException {
if (Files.exists(modsDir)) return ModsDirScannerImpl.get(modsDir, meta);
return new NoopMds(GameVersionParser.getGameVersion(meta.version));
return new NoopMds(GameVersionParser.getGameVersion(meta.gameVersion));
}
static void closeAll() {

View File

@ -65,7 +65,7 @@ class ModsDirScannerImpl implements ModsDirScanner {
@Override
public String getGameVersion() {
return GameVersionParser.getGameVersion(instance.meta.version);
return GameVersionParser.getGameVersion(instance.meta.gameVersion);
}
@Override

View File

@ -29,6 +29,10 @@ 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) {
@ -43,7 +47,8 @@ public class Steps {
SetupStepInfo info = new SetupStepInfo(vi, li, instance.name, state);
for (Step step : Steps.STEPS) {
state.incrementStep("Starting " + step.getClass().getSimpleName());
step.execute(info, new AtomicBoolean(false));
step.execute(info, cancel);
if (cancel.get()) return;
}
}
}

View File

@ -21,7 +21,7 @@ public class WriteMetadataStep implements Step {
Path metaPath = instance.resolve(InstanceList.INSTANCE_CONFIG_NAME);
if (!Files.exists(metaPath)) {
InstanceMeta meta = new InstanceMeta();
meta.version = info.version.id;
meta.gameVersion = info.version.id;
GC_InstanceMeta.write(meta, metaPath);
}
Instance.setSetupLock(instance, false);