Initial work on porting to java-gi, currently broken
ci/woodpecker/push/docs Pipeline was successful Details
ci/woodpecker/push/woodpecker Pipeline was successful Details

This commit is contained in:
Johannes Frohnmeyer 2022-12-17 21:53:21 +01:00
parent fb56c9e922
commit be8252ce58
Signed by: Johannes
GPG Key ID: E76429612C2929F4
54 changed files with 706 additions and 496 deletions

View File

@ -12,8 +12,7 @@ allprojects {
val lwjglVersion by extra("3.3.1")
val imguiVersion by extra("1.86.4")
val jfCommonsVersion by extra("1.0-SNAPSHOT")
val gsonCompileVersion by extra("1.1-SNAPSHOT")
val manifoldVersion by extra("2022.1.27")
val gsonCompileVersion by extra("1.2-SNAPSHOT")
val jlhttpVersion by extra("2.6")
val flavorProp: String by extra(prop("flavor", "custom"))

View File

@ -3,7 +3,7 @@ import de.undercouch.gradle.tasks.download.Download
plugins {
application
id("inceptum.java-conventions")
id("inceptum.java")
id("com.github.johnrengelman.shadow")
id("de.undercouch.download")
}
@ -43,4 +43,8 @@ val nativeExe by tasks.registering(FileOutput::class) {
if (rootProject.extra["flavor"] == "windows") {
tasks.build.get().dependsOn(nativeExe)
}
tasks.runShadow {
workingDir = rootProject.projectDir
}

View File

@ -1,6 +1,6 @@
plugins {
application
id("inceptum.java-conventions")
id("inceptum.java")
}
publishing {
@ -9,4 +9,6 @@ publishing {
from(components["java"])
}
}
}
}
tasks.run.get().workingDir = rootProject.projectDir

View File

@ -1,7 +1,7 @@
import org.gradle.kotlin.dsl.extra
plugins {
id("inceptum.library-conventions")
id("inceptum.library")
}
dependencies {

View File

@ -1,5 +1,5 @@
plugins {
id("inceptum.java-conventions")
id("inceptum.java")
}
publishing {

View File

@ -1,5 +1,5 @@
plugins {
id("inceptum.java-conventions")
id("inceptum.java")
id("jf.manifold")
}

View File

@ -2,7 +2,7 @@ import io.gitlab.jfronny.scripts.*
import javax.lang.model.element.Modifier
plugins {
id("inceptum.library-conventions")
id("inceptum.library")
id("jf.codegen")
id("inceptum.gson-compile")
id("inceptum.manifold")

View File

@ -1,5 +1,8 @@
package io.gitlab.jfronny.inceptum.common;
import gsoncompile.extensions.io.gitlab.jfronny.inceptum.common.InceptumConfig.GC_InceptumConfig;
import io.gitlab.jfronny.gson.compile.annotations.GComment;
import io.gitlab.jfronny.gson.compile.annotations.GSerializable;
import io.gitlab.jfronny.gson.stream.*;
import io.gitlab.jfronny.inceptum.common.model.inceptum.UpdateChannel;
@ -7,14 +10,23 @@ import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
@GSerializable(configure = GsonPreset.Config.class, isStatic = true)
public class InceptumConfig {
@GComment("Whether to show snapshots in the version selector for new instances")
public static boolean snapshots = false;
@GComment("Whether to launch the GUI in dark mode\nConfigurable in Settings->Dark Theme")
public static boolean darkTheme = false;
@GComment("Whether the GTK UI should default to a list view instead of a grid")
public static boolean listView = false;
@GComment("Whether to require an account to launch the game\nIntended to allow running the game from USB sticks on constrained networks")
public static boolean enforceAccount = true;
@GComment("The currently selected account\nUsed to launch the game")
public static String lastAccount = null;
@GComment("The last name used for an offline session")
public static String offlineAccountLastName = null;
@GComment("The update channel. Either \"CI\" or \"Stable\"\nI personally recommend the CI channel as it gets the latest fixes and features quicker")
public static UpdateChannel channel = UpdateChannel.Stable;
@GComment("The author name to add to packs where the metadata format requires specifying one")
public static String authorName = "Inceptum";
public static void load() throws IOException {
@ -31,78 +43,14 @@ public class InceptumConfig {
saveConfig();
}
}
try (JsonReader jr = new JsonReader(Files.newBufferedReader(MetaHolder.CONFIG_PATH))) {
GsonPreset.Config.configure(jr);
jr.beginObject();
while (jr.peek() != JsonToken.END_OBJECT) {
String name = null;
try {
name = jr.nextName();
switch (name) {
case "snapshots" -> snapshots = jr.nextBoolean();
case "darkTheme" -> darkTheme = jr.nextBoolean();
case "listView" -> listView = jr.nextBoolean();
case "enforceAccount" -> enforceAccount = jr.nextBoolean();
case "lastAccount" -> lastAccount = nullableString(jr);
case "offlineAccountLastName" -> offlineAccountLastName = nullableString(jr);
case "channel" -> {
try {
channel = UpdateChannel.valueOf(jr.nextString());
} catch (IllegalArgumentException e) {
Utils.LOGGER.error("Could not read channel", e);
}
}
case "authorName" -> authorName = jr.nextString();
default -> {
Utils.LOGGER.error("Unexpected entry name: " + name);
jr.skipValue();
}
}
} catch (Throwable t) {
if (name == null) Utils.LOGGER.error("Could not read config entry", t);
else Utils.LOGGER.error("Could not read config entry: " + name, t);
return;
}
}
jr.endObject();
}
GC_InceptumConfig.read(MetaHolder.CONFIG_PATH);
}
public static void saveConfig() {
try (JsonWriter jw = new JsonWriter(Files.newBufferedWriter(MetaHolder.CONFIG_PATH))) {
GsonPreset.Config.configure(jw);
jw.beginObject()
.comment("Whether to show snapshots in the version selector for new instances")
.name("snapshots").value(snapshots)
.comment("Whether to launch the GUI in dark mode")
.comment("Configurable in Settings->Dark Theme")
.name("darkTheme").value(darkTheme)
.comment("Whether the GTK UI should default to a list view instead of a grid")
.name("listView").value(listView)
.comment("Whether to require an account to launch the game")
.comment("Intended to allow running the game from USB sticks on constrained networks")
.name("enforceAccount").value(enforceAccount)
.comment("The currently selected account")
.comment("Used to launch the game")
.name("lastAccount").value(lastAccount)
.comment("The last name used for an offline session")
.name("offlineAccountLastName").value(offlineAccountLastName)
.comment("The update channel. Either \"CI\" or \"Stable\"")
.comment("I personally recommend the CI channel as it gets the latest fixes and features quicker")
.name("channel").value(channel.toString())
.comment("The author name to add to packs where the metadata format requires specifying one")
.name("authorName").value(authorName)
.endObject();
try {
GC_InceptumConfig.write(MetaHolder.CONFIG_PATH);
} catch (IOException e) {
Utils.LOGGER.error("Could not save config", e);
}
}
private static String nullableString(JsonReader jr) throws IOException {
if (jr.peek() == JsonToken.NULL) {
jr.nextNull();
return null;
}
return jr.nextString();
}
}

View File

@ -1,5 +1,5 @@
plugins {
id("inceptum.application-conventions")
id("inceptum.application")
id("inceptum.manifold")
}

View File

@ -1,5 +1,5 @@
plugins {
id("inceptum.application-standalone-conventions")
id("inceptum.application-standalone")
}
application {

View File

@ -1,6 +1,7 @@
plugins {
id("inceptum.application-conventions")
id("inceptum.application")
id("com.github.johnrengelman.shadow")
id("jf.manifold")
}
application {
@ -8,12 +9,21 @@ application {
}
repositories {
maven { url = uri("https://jitpack.io") }
mavenLocal()
maven { url = uri("https://maven.frohnmeyer-wds.de/java-gi") }
}
dependencies {
implementation("com.github.bailuk:java-gtk:0.2")
implementation("io.github.jwharm.javagi:gtk4:0.3-SNAPSHOT")
implementation("io.github.jwharm.javagi:glib:0.3-SNAPSHOT")
implementation(project(":launcher"))
}
tasks.runShadow.get().workingDir = rootProject.projectDir
tasks.compileJava {
options.compilerArgs.add("--enable-preview")
}
tasks.runShadow {
workingDir = rootProject.projectDir
jvmArgs!!.addAll(listOf("--enable-preview", "--enable-native-access=ALL-UNNAMED"))
}

View File

@ -1,8 +1,6 @@
package io.gitlab.jfronny.inceptum.gtk;
import ch.bailu.gtk.GTK;
import ch.bailu.gtk.gtk.*;
import ch.bailu.gtk.type.Str;
import org.gtk.gtk.*;
import io.gitlab.jfronny.commons.StringFormatter;
import io.gitlab.jfronny.inceptum.common.Utils;
import io.gitlab.jfronny.inceptum.gtk.util.I18n;
@ -46,19 +44,19 @@ public enum GtkEnvBackend implements LauncherEnv.EnvBackend { //TODO test
//TODO run on main thread
GtkMain.schedule(() -> {
Dialog dialog = new Dialog();
if (dialogParent != null) dialog.setTransientFor(dialogParent);
dialog.setModal(GTK.TRUE);
if (dialogParent != null) dialog.setDestroyWithParent(GTK.TRUE);
dialog.setTitle(new Str(prompt));
Box box = dialog.getContentArea();
box.append(new Label(new Str(details)));
if (dialogParent != null) dialog.transientFor = dialogParent;
dialog.modal = true;
if (dialogParent != null) dialog.destroyWithParent = true;
dialog.title = prompt;
Box box = dialog.contentArea;
box.append(new Label(details));
Entry entry = new Entry();
Editable entryEditable = new Editable(entry.cast());
entryEditable.setText(new Str(defaultValue));
Editable entryEditable = Editable.castFrom(entry);
entryEditable.text = defaultValue;
box.append(entry);
dialog.addButton(I18n.str("ok"), ResponseType.OK);
dialog.addButton(I18n.str("cancel"), ResponseType.CANCEL);
dialog.onResponse(processResponses(dialog, () -> ok.accept(entryEditable.getText().toString()), cancel));
dialog.addButton(I18n.get("ok"), ResponseType.OK.getValue());
dialog.addButton(I18n.get("cancel"), ResponseType.CANCEL.getValue());
dialog.onResponse(processResponses(dialog, () -> ok.accept(entryEditable.text.toString()), cancel));
dialog.show();
});
}
@ -68,28 +66,28 @@ public enum GtkEnvBackend implements LauncherEnv.EnvBackend { //TODO test
//TODO
}
private void simpleDialog(String markup, String title, int type, int buttons, Runnable ok, Runnable cancel) {
private void simpleDialog(String markup, String title, MessageType type, ButtonsType buttons, Runnable ok, Runnable cancel) {
GtkMain.schedule(() -> {
MessageDialog dialog = new MessageDialog(dialogParent, DialogFlags.MODAL | DialogFlags.DESTROY_WITH_PARENT, type, buttons, null);
dialog.setTitle(new Str(title));
dialog.setMarkup(new Str(markup));
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();
});
}
private Dialog.OnResponse processResponses(Dialog dialog, @Nullable Runnable ok, @Nullable Runnable cancel) {
return response_id -> {
switch (response_id) {
case ResponseType.OK -> {
private Dialog.Response processResponses(Dialog dialog, @Nullable Runnable ok, @Nullable Runnable cancel) {
return ($, response_id) -> {
switch (ResponseType.of(response_id)) {
case OK -> {
dialog.close();
if (ok != null) ok.run();
}
case ResponseType.CLOSE, ResponseType.CANCEL -> {
case CLOSE, CANCEL -> {
dialog.close();
if (cancel != null) cancel.run();
}
case ResponseType.DELETE_EVENT -> dialog.destroy();
case DELETE_EVENT -> dialog.destroy();
default -> Utils.LOGGER.error("Unexpected response type: " + response_id);
}
};

View File

@ -1,11 +1,8 @@
package io.gitlab.jfronny.inceptum.gtk;
import ch.bailu.gtk.GTK;
import ch.bailu.gtk.gio.ApplicationFlags;
import ch.bailu.gtk.glib.Glib;
import ch.bailu.gtk.gtk.Application;
import ch.bailu.gtk.type.Str;
import ch.bailu.gtk.type.Strs;
import org.gtk.gio.ApplicationFlags;
import org.gtk.glib.GLib;
import org.gtk.gtk.Application;
import io.gitlab.jfronny.inceptum.common.*;
import io.gitlab.jfronny.inceptum.gtk.window.MainWindow;
import io.gitlab.jfronny.inceptum.launcher.LauncherEnv;
@ -14,7 +11,7 @@ import java.io.IOException;
import java.util.*;
public class GtkMain {
public static final Str ID = new Str("io.gitlab.jfronny.inceptum");
public static final String ID = "io.gitlab.jfronny.inceptum";
private static final Queue<Runnable> SCHEDULED = new ArrayDeque<>();
public static void main(String[] args) throws IOException {
@ -37,11 +34,11 @@ public class GtkMain {
public static int showGui(String[] args) throws IOException {
var app = new Application(ID, ApplicationFlags.FLAGS_NONE);
app.onActivate(() -> {
app.onActivate($ -> {
GtkMenubar.create(app);
var window = new MainWindow(app);
window.show();
Glib.idleAdd(user_data -> {
GLib.idleAdd(data -> {
Runnable r;
while ((r = SCHEDULED.poll()) != null) {
try {
@ -50,14 +47,15 @@ public class GtkMain {
Utils.LOGGER.error("Could not run scheduled task", t);
}
}
return GTK.TRUE;
return true;
}, null);
GtkEnvBackend.INSTANCE.dialogParent = window;
window.onCloseRequest(() -> {
window.onCloseRequest($1 -> {
GtkEnvBackend.INSTANCE.dialogParent = null;
return GTK.FALSE;
app.quit();
return false;
});
});
return app.run(args.length, new Strs(args));
return app.run(args.length, args);
}
}

View File

@ -1,6 +1,6 @@
package io.gitlab.jfronny.inceptum.gtk;
import ch.bailu.gtk.gtk.Application;
import org.gtk.gtk.Application;
import io.gitlab.jfronny.commons.ref.R;
import io.gitlab.jfronny.inceptum.common.Utils;
import io.gitlab.jfronny.inceptum.gtk.menu.MenuBuilder;
@ -63,11 +63,11 @@ public class GtkMenubar {
}
InstanceLauncher.launchClient(instance);
};
if (instance.isSetupLocked()) {
if (instance.isSetupLocked) {
LauncherEnv.showError(I18n.get("instance.launch.locked.setup"), I18n.get("instance.launch.locked"));
return;
}
if (instance.isRunningLocked()) {
if (instance.isRunningLocked) {
LauncherEnv.showOkCancel(I18n.get("instance.launch.locked.running"), I18n.get("instance.launch.locked"), () -> {
new Thread(launch).start(); //TODO loom
}, R::nop);
@ -84,9 +84,9 @@ public class GtkMenubar {
accountsMenu.button("manage", () -> {
//TODO UI to add/remove accounts
});
List<MicrosoftAccount> accounts = new ArrayList<>(AccountManager.getAccounts());
List<MicrosoftAccount> accounts = new ArrayList<>(AccountManager.accounts);
accounts.add(null);
accountsMenu.literalRadio("account", accounts.get(AccountManager.getSelectedIndex()), accounts, (i, acc) -> {
accountsMenu.literalRadio("account", accounts.get(AccountManager.selectedIndex), accounts, (i, acc) -> {
if (acc == null) return I18n.get("account.none");
return acc.minecraftUsername;
}, AccountManager::switchAccount);

View File

@ -1,34 +1,31 @@
package io.gitlab.jfronny.inceptum.gtk;
import ch.bailu.gtk.GTK;
import ch.bailu.gtk.gio.ApplicationFlags;
import ch.bailu.gtk.gtk.*;
import ch.bailu.gtk.type.Str;
import ch.bailu.gtk.type.Strs;
import org.gtk.gio.ApplicationFlags;
import org.gtk.gtk.*;
import io.gitlab.jfronny.commons.ref.R;
public class TestStart {
public static void main(String[] args) {
var app = new Application(GtkMain.ID, ApplicationFlags.FLAGS_NONE);
app.onActivate(() -> {
var button = Button.newWithLabelButton(new Str("Test"));
app.onActivate($ -> {
var button = Button.newWithLabel("Test");
button.onClicked(TestStart::test);
var window = new ApplicationWindow(app);
window.setDefaultSize(200, 50);
window.setTitle(new Str("Inceptum"));
window.setChild(button);
window.title = "Inceptum";
window.child = button;
window.show();
GtkEnvBackend.INSTANCE.dialogParent = window;
window.onCloseRequest(() -> {
window.onCloseRequest($1 -> {
GtkEnvBackend.INSTANCE.dialogParent = null;
return GTK.FALSE;
return false;
});
});
System.exit(app.run(args.length, new Strs(args)));
System.exit(app.run(args.length, args));
}
private static void test() {
private static void test(Button $) {
GtkEnvBackend backend = GtkEnvBackend.INSTANCE;
backend.getInput("Ae", "IoU\naee", "Def", R::nop, R::nop);

View File

@ -0,0 +1,24 @@
package io.gitlab.jfronny.inceptum.gtk.callback;
import io.github.jwharm.javagi.CallbackGenerator;
import io.github.jwharm.javagi.Interop;
import org.jetbrains.annotations.ApiStatus;
import java.lang.foreign.FunctionDescriptor;
import java.lang.foreign.Linker;
import java.lang.foreign.MemoryAddress;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
public interface DisposeCallback {
void upcall(MemoryAddress pointer);
@ApiStatus.Internal
FunctionDescriptor DESCRIPTOR = FunctionDescriptor.ofVoid(Interop.valueLayout.ADDRESS);
@ApiStatus.Internal
MethodHandle HANDLE = CallbackGenerator.getHandle(MethodHandles.lookup(), DisposeCallback.class, DESCRIPTOR);
default MemoryAddress toCallback() {
return Linker.nativeLinker().upcallStub(HANDLE.bindTo(this), DESCRIPTOR, Interop.getScope()).address();
}
}

View File

@ -0,0 +1,24 @@
package io.gitlab.jfronny.inceptum.gtk.callback;
import io.github.jwharm.javagi.CallbackGenerator;
import io.github.jwharm.javagi.Interop;
import org.jetbrains.annotations.ApiStatus;
import java.lang.foreign.Addressable;
import java.lang.foreign.FunctionDescriptor;
import java.lang.foreign.Linker;
import java.lang.foreign.MemoryAddress;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
@FunctionalInterface
public interface GetItemCallback {
Addressable upcall(MemoryAddress inst, int position);
@ApiStatus.Internal FunctionDescriptor DESCRIPTOR = FunctionDescriptor.of(Interop.valueLayout.ADDRESS, Interop.valueLayout.ADDRESS, Interop.valueLayout.C_INT);
@ApiStatus.Internal MethodHandle HANDLE = CallbackGenerator.getHandle(MethodHandles.lookup(), GetItemCallback.class, DESCRIPTOR);
default MemoryAddress toCallback() {
return Linker.nativeLinker().upcallStub(HANDLE.bindTo(this), DESCRIPTOR, Interop.getScope()).address();
}
}

View File

@ -0,0 +1,24 @@
package io.gitlab.jfronny.inceptum.gtk.callback;
import io.github.jwharm.javagi.CallbackGenerator;
import io.github.jwharm.javagi.Interop;
import org.jetbrains.annotations.ApiStatus;
import java.lang.foreign.FunctionDescriptor;
import java.lang.foreign.Linker;
import java.lang.foreign.MemoryAddress;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
public interface GetItemTypeCallback {
long upcall(MemoryAddress address);
@ApiStatus.Internal
FunctionDescriptor DESCRIPTOR = FunctionDescriptor.of(Interop.valueLayout.C_LONG, Interop.valueLayout.ADDRESS);
@ApiStatus.Internal
MethodHandle HANDLE = CallbackGenerator.getHandle(MethodHandles.lookup(), GetItemTypeCallback.class, DESCRIPTOR);
default MemoryAddress toCallback() {
return Linker.nativeLinker().upcallStub(HANDLE.bindTo(this), DESCRIPTOR, Interop.getScope()).address();
}
}

View File

@ -0,0 +1,24 @@
package io.gitlab.jfronny.inceptum.gtk.callback;
import io.github.jwharm.javagi.CallbackGenerator;
import io.github.jwharm.javagi.Interop;
import org.jetbrains.annotations.ApiStatus;
import java.lang.foreign.FunctionDescriptor;
import java.lang.foreign.Linker;
import java.lang.foreign.MemoryAddress;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
public interface GetNItemsCallback {
int upcall(MemoryAddress inst);
@ApiStatus.Internal
FunctionDescriptor DESCRIPTOR = FunctionDescriptor.of(Interop.valueLayout.C_INT, Interop.valueLayout.ADDRESS);
@ApiStatus.Internal
MethodHandle HANDLE = CallbackGenerator.getHandle(MethodHandles.lookup(), GetNItemsCallback.class, DESCRIPTOR);
default MemoryAddress toCallback() {
return Linker.nativeLinker().upcallStub(HANDLE.bindTo(this), DESCRIPTOR, Interop.getScope()).address();
}
}

View File

@ -0,0 +1,36 @@
package io.gitlab.jfronny.inceptum.gtk.callback;
import io.github.jwharm.javagi.CallbackGenerator;
import io.github.jwharm.javagi.Interop;
import io.github.jwharm.javagi.Marshal;
import io.github.jwharm.javagi.Ownership;
import org.gtk.gobject.GiObject;
import org.gtk.gobject.ParamSpec;
import org.gtk.gobject.Value;
import org.jetbrains.annotations.ApiStatus;
import java.lang.foreign.FunctionDescriptor;
import java.lang.foreign.Linker;
import java.lang.foreign.MemoryAddress;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
public interface GetPropertyCallback {
void invoke(GiObject object, int propertyId, Value value, ParamSpec paramSpec);
default void upcall(MemoryAddress object, int propertyId, MemoryAddress value, MemoryAddress pspec) {
invoke(GiObject.fromAddress.marshal(object, Ownership.NONE),
propertyId,
Value.fromAddress.marshal(value, Ownership.NONE),
ParamSpec.fromAddress.marshal(pspec, Ownership.NONE));
}
@ApiStatus.Internal
FunctionDescriptor DESCRIPTOR = FunctionDescriptor.ofVoid(Interop.valueLayout.ADDRESS, Interop.valueLayout.C_INT, Interop.valueLayout.ADDRESS, Interop.valueLayout.ADDRESS);
@ApiStatus.Internal
MethodHandle HANDLE = CallbackGenerator.getHandle(MethodHandles.lookup(), GetPropertyCallback.class, DESCRIPTOR);
default MemoryAddress toCallback() {
return Linker.nativeLinker().upcallStub(HANDLE.bindTo(this), DESCRIPTOR, Interop.getScope()).address();
}
}

View File

@ -0,0 +1,36 @@
package io.gitlab.jfronny.inceptum.gtk.callback;
import io.github.jwharm.javagi.CallbackGenerator;
import io.github.jwharm.javagi.Interop;
import io.github.jwharm.javagi.Marshal;
import io.github.jwharm.javagi.Ownership;
import org.gtk.gobject.GiObject;
import org.gtk.gobject.ParamSpec;
import org.gtk.gobject.Value;
import org.jetbrains.annotations.ApiStatus;
import java.lang.foreign.FunctionDescriptor;
import java.lang.foreign.Linker;
import java.lang.foreign.MemoryAddress;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
public interface SetPropertyCallback {
void invoke(GiObject object, int propertyId, Value value, ParamSpec paramSpec);
default void upcall(MemoryAddress object, int propertyId, MemoryAddress value, MemoryAddress pspec) {
invoke(GiObject.fromAddress.marshal(object, Ownership.NONE),
propertyId,
Value.fromAddress.marshal(value, Ownership.NONE),
ParamSpec.fromAddress.marshal(pspec, Ownership.NONE));
}
@ApiStatus.Internal
FunctionDescriptor DESCRIPTOR = FunctionDescriptor.ofVoid(Interop.valueLayout.ADDRESS, Interop.valueLayout.C_INT, Interop.valueLayout.ADDRESS, Interop.valueLayout.ADDRESS);
@ApiStatus.Internal
MethodHandle HANDLE = CallbackGenerator.getHandle(MethodHandles.lookup(), SetPropertyCallback.class, DESCRIPTOR);
default MemoryAddress toCallback() {
return Linker.nativeLinker().upcallStub(HANDLE.bindTo(this), DESCRIPTOR, Interop.getScope()).address();
}
}

View File

@ -1,11 +1,10 @@
package io.gitlab.jfronny.inceptum.gtk.control;
import ch.bailu.gtk.GTK;
import ch.bailu.gtk.bridge.ListIndex;
import ch.bailu.gtk.gtk.*;
import ch.bailu.gtk.pango.EllipsizeMode;
import ch.bailu.gtk.type.Str;
import io.gitlab.jfronny.inceptum.gtk.util.ListIndex;
import org.gtk.gtk.*;
import io.gitlab.jfronny.inceptum.launcher.system.instance.Instance;
import org.pango.EllipsizeMode;
import org.pango.WrapMode;
import java.util.List;
@ -13,24 +12,24 @@ public class InstanceGridEntryFactory extends SignalListItemFactory {
public InstanceGridEntryFactory(List<Instance> instanceList) {
super();
//TODO better design
onSetup(item -> {
onSetup(($, item) -> {
var box = new Box(Orientation.VERTICAL, 5);
var thumbnail = new InstanceThumbnail();
box.append(thumbnail);
var label = new Label(Str.NULL);
var label = new Label(null);
label.setSizeRequest(192, -1);
label.setMaxWidthChars(20);
label.setJustify(Justification.CENTER);
label.setHalign(Align.START);
label.setHexpand(GTK.TRUE);
label.setValign(Align.CENTER);
label.setEllipsize(EllipsizeMode.MIDDLE);
label.setLines(3);
label.setWrap(GTK.TRUE);
label.setWrapMode(WrapMode.WORD_CHAR);
label.setMarginTop(10);
label.maxWidthChars = 20;
label.justify = Justification.CENTER;
label.halign = Align.START;
label.hexpand = true;
label.valign = Align.CENTER;
label.ellipsize = EllipsizeMode.MIDDLE;
label.lines = 3;
label.wrap = true;
label.wrapMode = WrapMode.WORD_CHAR;
label.marginTop = 10;
box.append(label);
// Label label = new Label(Str.NULL);
// label.setXalign(0);
@ -50,11 +49,11 @@ public class InstanceGridEntryFactory extends SignalListItemFactory {
// openDir.setHasTooltip(GTK.TRUE);
// box.append(openDir);
item.setChild(box);
ListItem.castFrom(item).setChild(box);
//TODO server launch with network-server-symbolic
//TODO kill current instance
});
onBind(item -> {
onBind(($, item) -> {
// Label label = new Label(item.getChild().getFirstChild().cast());
// Button launch = new Button(label.getNextSibling().cast());
// Button openDir = new Button(launch.getNextSibling().cast());
@ -62,13 +61,16 @@ public class InstanceGridEntryFactory extends SignalListItemFactory {
// label.setText(new Str(instance.toString()));
// launch.onClicked(() -> GtkMenubar.launch(instance));
// openDir.onClicked(() -> Utils.openFile(instance.path().toFile()));
Box box = new Box(item.getChild().cast());
InstanceThumbnail thumbnail = new InstanceThumbnail(box.getFirstChild().cast());
Label label = new Label(thumbnail.getNextSibling().cast());
Instance instance = instanceList.get(ListIndex.toIndex(item));
ListItem li = ListItem.castFrom(item);
Box box = Box.castFrom(li.getChild());
InstanceThumbnail thumbnail = InstanceThumbnail.castFrom(box.firstChild);
Label label = Label.castFrom(thumbnail.nextSibling);
Instance instance = instanceList.get(ListIndex.toIndex(li));
thumbnail.bind(instance);
label.setText(new Str(instance.toString()));
label.text = instance.toString();
//TODO right click menu + double click action
//TODO edit button document-edit-symbolic -> edit-delete-symbolic, edit-copy-symbolic
});

View File

@ -1,10 +1,8 @@
package io.gitlab.jfronny.inceptum.gtk.control;
import ch.bailu.gtk.GTK;
import ch.bailu.gtk.adw.ActionRow;
import ch.bailu.gtk.bridge.ListIndex;
import ch.bailu.gtk.gtk.*;
import ch.bailu.gtk.type.Str;
import io.gitlab.jfronny.inceptum.gtk.util.ListIndex;
import org.gnome.adw.ActionRow;
import org.gtk.gtk.*;
import io.gitlab.jfronny.inceptum.common.Utils;
import io.gitlab.jfronny.inceptum.gtk.GtkMenubar;
import io.gitlab.jfronny.inceptum.gtk.util.I18n;
@ -15,44 +13,46 @@ import java.util.List;
public class InstanceListEntryFactory extends SignalListItemFactory {
public InstanceListEntryFactory(List<Instance> instanceList) {
super();
onSetup(item -> {
onSetup(($, item) -> {
var thumbnail = new InstanceThumbnail();
thumbnail.setName(new Str("inceptum-thumbnail"));
thumbnail.name = "inceptum-thumbnail";
var launch = new Button();
launch.setName(new Str("inceptum-launch"));
launch.setIconName(new Str("computer-symbolic"));
launch.setTooltipText(I18n.str("instance.launch"));
launch.setHasTooltip(GTK.TRUE);
launch.name = "inceptum-launch";
launch.iconName = "computer-symbolic";
launch.tooltipText = I18n.get("instance.launch");
launch.hasTooltip = true;
var openDir = new Button();
openDir.setName(new Str("inceptum-open-dir"));
openDir.setIconName(new Str("folder-symbolic"));
openDir.setTooltipText(I18n.str("instance.directory"));
openDir.setHasTooltip(GTK.TRUE);
openDir.name = "inceptum-open-dir";
openDir.iconName = "folder-symbolic";
openDir.tooltipText = I18n.get("instance.directory");
openDir.hasTooltip = true;
var row = new ActionRow();
row.setName(new Str("inceptum-row"));
row.setActivatableWidget(launch);
row.name = "inceptum-row";
row.activatableWidget = launch;
row.addPrefix(thumbnail);
row.addSuffix(launch);
row.addSuffix(openDir);
item.setChild(row);
ListItem.castFrom(item).setChild(row);
//TODO server launch with network-server-symbolic
//TODO kill current instance
});
onBind(item -> {
Instance instance = instanceList.get(ListIndex.toIndex(item));
ActionRow row = new ActionRow(item.getChild().cast());
Box prefixes = new Box(row.getFirstChild().getFirstChild().cast());
Box suffixes = new Box(row.getFirstChild().getLastChild().cast());
onBind(($, item) -> {
ListItem li = ListItem.castFrom(item);
InstanceThumbnail thumbnail = new InstanceThumbnail(prefixes.getFirstChild().cast());
Button launch = new Button(suffixes.getFirstChild().cast());
Button openDir = new Button(launch.getNextSibling().cast());
row.setTitle(new Str(instance.toString()));
Instance instance = instanceList.get(ListIndex.toIndex(li));
ActionRow row = ActionRow.castFrom(li.child);
Box prefixes = Box.castFrom(row.firstChild.firstChild);
Box suffixes = Box.castFrom(row.firstChild.lastChild);
InstanceThumbnail thumbnail = InstanceThumbnail.castFrom(prefixes.firstChild);
Button launch = Button.castFrom(suffixes.firstChild);
Button openDir = Button.castFrom(launch.nextSibling);
row.title = instance.toString();
// InstanceThumbnail thumbnail = new InstanceThumbnail(row.getFirstChild().cast());
// Label label = new Label(thumbnail.getNextSibling().cast());
@ -61,8 +61,8 @@ public class InstanceListEntryFactory extends SignalListItemFactory {
thumbnail.bind(instance);
// label.setText(new Str(instance.toString()));
launch.onClicked(() -> GtkMenubar.launch(instance));
openDir.onClicked(() -> Utils.openFile(instance.path().toFile()));
launch.onClicked($1 -> GtkMenubar.launch(instance));
openDir.onClicked($1 -> Utils.openFile(instance.path().toFile()));
//TODO why the hell does this crash the VM?
//TODO GestureClick.setButton(GDK_BUTTON_SECONDARY)

View File

@ -1,16 +1,30 @@
package io.gitlab.jfronny.inceptum.gtk.control;
import ch.bailu.gtk.gtk.*;
import ch.bailu.gtk.type.*;
import io.github.jwharm.javagi.Ownership;
import io.gitlab.jfronny.inceptum.launcher.system.instance.Instance;
import org.gtk.gobject.GObject;
import org.gtk.gobject.TypeInstance;
import org.gtk.gtk.Image;
import org.gtk.gtk.Spinner;
import org.gtk.gtk.Stack;
import java.lang.foreign.Addressable;
public class InstanceThumbnail extends Stack {
private static final Str SPINNER = new Str("spinner");
private static final Str IMAGE = new Str("image");
private static final Str GENERIC = new Str("generic");
private static final String SPINNER = "spinner";
private static final String IMAGE = "image";
private static final String GENERIC = "generic";
public InstanceThumbnail(CPointer pointer) {
super(pointer);
private InstanceThumbnail(Addressable address, Ownership ownership) {
super(address, ownership);
}
public static InstanceThumbnail castFrom(org.gtk.gobject.GiObject gobject) {
if (GObject.typeCheckInstanceIsA(TypeInstance.fromAddress.marshal(gobject.handle(), Ownership.NONE), Stack.getType())) {
return new InstanceThumbnail(gobject.handle(), gobject.yieldOwnership());
} else {
throw new ClassCastException("Object type is not an instance of GtkStack");
}
}
public InstanceThumbnail() {
@ -18,26 +32,26 @@ public class InstanceThumbnail extends Stack {
var spinner = new Spinner();
var image = new Image();
var generic = new Image();
spinner.setName(SPINNER);
image.setName(IMAGE);
generic.setName(GENERIC);
generic.setFromIconName(new Str("media-playback-start-symbolic")); //TODO better default icon
spinner.name = SPINNER;
image.name = IMAGE;
generic.name = GENERIC;
generic.setFromIconName("media-playback-start-symbolic"); //TODO better default icon
addNamed(spinner, SPINNER);
addNamed(image, IMAGE);
addNamed(generic, GENERIC);
}
public void bind(Instance entry) {
var spinner = new Spinner(getChildByName(SPINNER).cast());
var image = new Image(getChildByName(IMAGE).cast()); //TODO
var generic = new Image(getChildByName(GENERIC).cast());
var spinner = Spinner.castFrom(getChildByName(SPINNER));
var image = Image.castFrom(getChildByName(IMAGE)); //TODO
var generic = Image.castFrom(getChildByName(GENERIC));
//TODO mark instance being played
if (entry.isSetupLocked()) {
setVisibleChild(spinner);
if (entry.isSetupLocked) {
visibleChild = spinner;
} else if (false) { // if the instance has an image, load the image data and set it as the visible child
setVisibleChild(image);
visibleChild = image;
} else {
setVisibleChild(generic);
visibleChild = generic;
}
}
}

View File

@ -1,6 +1,6 @@
package io.gitlab.jfronny.inceptum.gtk.menu;
import ch.bailu.gtk.gio.SimpleAction;
import org.gtk.gio.SimpleAction;
public class ButtonItem extends MenuItem {
public ButtonItem(SimpleAction action) {

View File

@ -1,19 +1,19 @@
package io.gitlab.jfronny.inceptum.gtk.menu;
import ch.bailu.gtk.GTK;
import ch.bailu.gtk.gio.MenuItem;
import ch.bailu.gtk.gio.*;
import ch.bailu.gtk.glib.Variant;
import ch.bailu.gtk.glib.VariantType;
import ch.bailu.gtk.gtk.Application;
import ch.bailu.gtk.gtk.PopoverMenu;
import ch.bailu.gtk.type.Pointer;
import ch.bailu.gtk.type.Str;
import io.gitlab.jfronny.commons.throwable.ThrowingRunnable;
import io.gitlab.jfronny.inceptum.common.Utils;
import io.gitlab.jfronny.inceptum.gtk.util.I18n;
import org.gtk.gio.MenuItem;
import org.gtk.gio.*;
import org.gtk.glib.Variant;
import org.gtk.glib.VariantType;
import org.gtk.gtk.Application;
import org.gtk.gtk.PopoverMenu;
import java.util.*;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.function.BiFunction;
import java.util.function.Consumer;
@ -22,13 +22,13 @@ public class MenuBuilder {
private static Menu getRootMenu(Application app) {
synchronized (LOCK) {
var currentMenu = app.getMenubar();
if (currentMenu.equals(Pointer.NULL)) {
var currentMenu = app.menubar;
if (currentMenu == null) {
var menu = new Menu();
app.setMenubar(menu);
app.menubar = menu;
return menu;
} else {
return new Menu(currentMenu.cast());
return Menu.castFrom(currentMenu);
}
}
}
@ -42,10 +42,6 @@ public class MenuBuilder {
this(app, getRootMenu(app), "");
}
public MenuBuilder(Application app, Menu menu, String prefix) {
this(new ActionMap(app.cast()), menu, prefix);
}
public MenuBuilder(ActionMap map, Menu menu, String prefix) {
if (!prefix.isEmpty() && !prefix.endsWith(".")) prefix += ".";
this.map = Objects.requireNonNull(map);
@ -59,29 +55,29 @@ public class MenuBuilder {
public ButtonItem literalButton(String internalName, String label, ThrowingRunnable<?> onClick) {
internalName = prefix + internalName;
SimpleAction sAct = new SimpleAction(new Str(internalName), null);
SimpleAction sAct = new SimpleAction(internalName, null);
addAction(internalName, sAct);
sAct.onActivate(v -> {
sAct.onActivate(($, variant) -> {
try {
onClick.run();
} catch (Throwable e) {
Utils.LOGGER.error("Could not execute action", e);
}
});
menu.appendItem(new MenuItem(new Str(label), new Str("app." + internalName)));
menu.appendItem(new MenuItem(label, "app." + internalName));
return new ButtonItem(sAct);
}
public ToggleItem toggle(String name, boolean initial, Consumer<Boolean> action) {
name = prefix + name;
SimpleAction sAct = SimpleAction.newStatefulSimpleAction(new Str(name), null, Variant.newBooleanVariant(GTK.is(initial)));
Action rAct = addAction(name, sAct);
sAct.onActivate(parameter -> {
boolean state = !GTK.is(rAct.getState().getBoolean());
sAct.setState(Variant.newBooleanVariant(GTK.is(state)));
SimpleAction sAct = SimpleAction.newStateful(name, null, Variant.newBoolean(initial));
addAction(name, sAct);
sAct.onActivate(($, variant) -> {
boolean state = !sAct.getState().getBoolean();
sAct.state = Variant.newBoolean(state);
action.accept(state);
});
menu.appendItem(new MenuItem(I18n.str("menu." + name), new Str("app." + name)));
menu.appendItem(new MenuItem(I18n.get("menu." + name), "app." + name));
return new ToggleItem(sAct);
}
@ -92,15 +88,15 @@ public class MenuBuilder {
public <T> RadioItem<T> literalRadio(String name, T initial, List<T> options, BiFunction<Integer, T, String> stringifier, Consumer<T> action) {
Objects.requireNonNull(options);
name = prefix + name;
SimpleAction sAct = SimpleAction.newStatefulSimpleAction(new Str(name), new VariantType(new Str("i")), Variant.newInt32Variant(options.indexOf(initial)));
SimpleAction sAct = SimpleAction.newStateful(name, new VariantType("i"), Variant.newInt32(options.indexOf(initial)));
addAction(name, sAct);
sAct.onActivate(parameter -> {
sAct.setState(parameter);
action.accept(options.get(parameter.getInt32()));
sAct.onActivate(($, variant) -> {
sAct.state = variant;
action.accept(options.get(variant.getInt32()));
});
int i = 0;
for (T option : options) {
menu.appendItem(new MenuItem(new Str(stringifier.apply(i, option)), new Str("app." + name + "(" + i + ")")));
menu.appendItem(new MenuItem(stringifier.apply(i, option), "app." + name + "(" + i + ")"));
i++;
}
return new RadioItem<>(sAct, options);
@ -109,23 +105,21 @@ public class MenuBuilder {
public MenuBuilder submenu(String name) {
name = prefix + name;
Menu submenu = new Menu();
menu.appendSubmenu(I18n.str("menu." + name), submenu);
menu.appendSubmenu(I18n.get("menu." + name), submenu);
return new MenuBuilder(map, submenu, name);
}
public void clear() {
menu.removeAll();
refs.forEach((name, action) -> {
map.removeAction(new Str(name));
map.removeAction(name);
});
refs.clear();
}
private Action addAction(String name, SimpleAction action) {
Action rAct = new Action(action.cast());
map.addAction(rAct);
refs.put(name, rAct);
return rAct;
private void addAction(String name, SimpleAction action) {
map.addAction(action);
refs.put(name, action);
}
public Menu getMenu() {
@ -133,6 +127,6 @@ public class MenuBuilder {
}
public PopoverMenu asPopover() {
return PopoverMenu.newFromModelPopoverMenu(menu);
return PopoverMenu.newFromModel(menu);
}
}

View File

@ -1,23 +1,19 @@
package io.gitlab.jfronny.inceptum.gtk.menu;
import ch.bailu.gtk.GTK;
import ch.bailu.gtk.gio.Action;
import ch.bailu.gtk.gio.SimpleAction;
import org.gtk.gio.SimpleAction;
public abstract class MenuItem {
protected final SimpleAction sAction;
protected final Action action;
protected final SimpleAction action;
public MenuItem(SimpleAction action) {
this.sAction = action;
this.action = new Action(action.cast());
this.action = action;
}
public boolean getEnabled() {
return GTK.is(action.getEnabled());
return action.getEnabled();
}
public void setEnabled(boolean enabled) {
sAction.setEnabled(GTK.is(enabled));
action.enabled = enabled;
}
}

View File

@ -1,7 +1,7 @@
package io.gitlab.jfronny.inceptum.gtk.menu;
import ch.bailu.gtk.gio.SimpleAction;
import ch.bailu.gtk.glib.Variant;
import org.gtk.gio.SimpleAction;
import org.gtk.glib.Variant;
import java.util.List;
@ -14,7 +14,7 @@ public class RadioItem<T> extends MenuItem {
}
public void setSelected(T selected) {
sAction.setState(Variant.newInt32Variant(options.indexOf(selected)));
action.setState(Variant.newInt32(options.indexOf(selected)));
}
public T getSelected() {

View File

@ -1,8 +1,7 @@
package io.gitlab.jfronny.inceptum.gtk.menu;
import ch.bailu.gtk.GTK;
import ch.bailu.gtk.gio.SimpleAction;
import ch.bailu.gtk.glib.Variant;
import org.gtk.gio.SimpleAction;
import org.gtk.glib.Variant;
public class ToggleItem extends MenuItem {
public ToggleItem(SimpleAction action) {
@ -10,11 +9,11 @@ public class ToggleItem extends MenuItem {
}
public boolean getState() {
return GTK.is(action.getState().getBoolean());
return action.getState().getBoolean();
}
public void setState(boolean state) {
sAction.setState(Variant.newBooleanVariant(GTK.is(state)));
action.state = Variant.newBoolean(state);
}
public boolean toggle() {

View File

@ -1,31 +1,23 @@
package io.gitlab.jfronny.inceptum.gtk.util;
import ch.bailu.gtk.gtk.Widget;
import ch.bailu.gtk.type.Pointer;
import java.util.Arrays;
import java.util.stream.Collectors;
import org.gtk.gtk.Widget;
public class Dbg {
public static String inspect(Widget ptr) {
if (ptr.isNull()) return "<null>";
if (ptr == null) return "<null>";
StringBuilder sb = new StringBuilder();
inspect(ptr, sb, "");
return sb.toString();
}
private static void inspect(Widget ptr, StringBuilder bld, String indent) {
bld.append(indent).append("<").append(ptr.getName().toString()).append("#").append(ptr.getCPointer()).append("> ")
.append(Arrays.stream(Pointer.toJnaPointer(ptr.getCssClasses().getCPointer()).getPointerArray(0))
.map(p -> p.getString(0))
.collect(Collectors.joining(", ")));
ptr = ptr.getFirstChild();
if (ptr.isNotNull()) {
while (ptr.isNotNull()) {
bld.append("\n");
inspect(ptr, bld, indent + " ");
ptr = ptr.getNextSibling();
}
bld.append(indent).append("<").append(ptr.name).append("#").append(ptr.handle()).append("> ")
.append(ptr.getCssClasses().get());
ptr = ptr.firstChild;
while (ptr != null) {
bld.append("\n");
inspect(ptr, bld, indent + " ");
ptr = ptr.nextSibling;
}
}
}

View File

@ -1,6 +1,5 @@
package io.gitlab.jfronny.inceptum.gtk.util;
import ch.bailu.gtk.type.Str;
import org.jetbrains.annotations.PropertyKey;
import java.util.ResourceBundle;
@ -16,12 +15,4 @@ public class I18n {
public static String get(@PropertyKey(resourceBundle = BUNDLE) String key, Object... args) {
return String.format(bundle.getString(key), args);
}
public static Str str(@PropertyKey(resourceBundle = BUNDLE) String key) {
return new Str(get(key));
}
public static Str str(@PropertyKey(resourceBundle = BUNDLE) String key, Object... args) {
return new Str(get(key, args));
}
}

View File

@ -0,0 +1,188 @@
package io.gitlab.jfronny.inceptum.gtk.util;
import io.github.jwharm.javagi.*;
import io.gitlab.jfronny.inceptum.gtk.GtkMain;
import io.gitlab.jfronny.inceptum.gtk.callback.*;
import org.gtk.gio.ListModel;
import org.gtk.gio.ListModelInterface;
import org.gtk.glib.Type;
import org.gtk.gobject.*;
import org.gtk.gtk.*;
import java.lang.foreign.*;
import java.lang.invoke.VarHandle;
public class ListIndex extends GiObject {
private static final int PROP_ITEM_TYPE = 1;
private static final String PROP_NAME = "item-type";
private static final String TYPE_NAME = "ListIndex";
private static final Type PARENT_TYPE = GiObject.getType();
private static final MemoryLayout memoryLayout = MemoryLayout.structLayout(
GiObject.getMemoryLayout().withName("parent_instance"),
Interop.valueLayout.C_INT.withName("index"),
Interop.valueLayout.C_INT.withName("size")
).withName(TYPE_NAME);
private static Type type;
public static Type getType() {
if (type == null) {
// Register the new gtype
type = GObject.typeRegisterStaticSimple(
PARENT_TYPE,
TYPE_NAME,
(short) ObjectClass.getMemoryLayout().byteSize(),
classInit,
(short) memoryLayout.byteSize(),
instanceInit,
TypeFlags.NONE
);
GObject.typeAddInterfaceStatic(type, ListModel.getType(), InterfaceInfo.builder()
.setInterfaceInit(interfaceInit)
.setInterfaceData(null)
.setInterfaceFinalize(null)
.build());
}
return type;
}
private static final VarHandle index = memoryLayout.varHandle(MemoryLayout.PathElement.groupElement("index"));
private static final VarHandle size = memoryLayout.varHandle(MemoryLayout.PathElement.groupElement("size"));
public static ListIndex castFrom(GiObject gobject) {
if (GObject.typeCheckInstanceIsA(TypeInstance.fromAddress.marshal(gobject.handle(), Ownership.NONE), getType())) {
return new ListIndex(gobject.handle(), gobject.yieldOwnership());
} else {
throw new ClassCastException("Object type is not an instance of ListIndex");
}
}
public static final Marshal<Addressable, ListIndex> fromAddress = (input, ownership) -> input.equals(MemoryAddress.NULL) ? null : new ListIndex(input, ownership);
protected ListIndex(Addressable address, Ownership ownership) {
super(address, ownership);
}
public ListIndex(int size) {
this();
setSize(size);
}
public ListIndex() {
super(new GiObject(getType(), PROP_NAME, getType()).handle(),
Ownership.FULL);
}
private static final InstanceInitFunc instanceInit = (instance, gClass) -> fromAddress.marshal(instance.handle(), Ownership.NONE).initInstance();
private void initInstance() {
setIndex(0);
setSize(0);
}
private static TypeClass parentClass = null;
private static final DisposeCallback instanceDispose = pointer -> {
if (parentClass == null) System.out.println("ListIndex::instanceDispose (no parent)");
else {
InteropException ie = new InteropException("Could not dispose ListIndex");
GtkMain.schedule(() -> {
try {
var func = (MemoryAddress) ObjectClass.getMemoryLayout()
.varHandle(MemoryLayout.PathElement.groupElement("dispose"))
.get(MemorySegment.ofAddress(parentClass.handle().address(), ObjectClass.getMemoryLayout().byteSize(), Interop.getScope()));
var linked = Linker.nativeLinker().downcallHandle(func, DisposeCallback.DESCRIPTOR);
linked.invoke(pointer);
} catch (Throwable e) {
throw (InteropException) ie.initCause(e);
}
});
}
};
private static final SetPropertyCallback setProperty = (object, propertyId, value, paramSpec) -> {
if (propertyId != PROP_ITEM_TYPE) System.out.println("ListIndex::setProperty (unknown property)");
};
private static final GetPropertyCallback getProperty = (object, propertyId, value, paramSpec) -> {
if (propertyId == PROP_ITEM_TYPE) value.setGtype(getType());
else System.out.println("ListIndex::getProperty (unknown property)");
};
private static final ClassInitFunc classInit = (klass, data) -> {
System.out.println("ListIndex::classInit");
parentClass = klass.peekParent();
ObjectClass objectClass = ObjectClass.fromAddress.marshal(GObject.typeCheckClassCast(klass, PARENT_TYPE).handle(), Ownership.NONE);
objectClass.setDispose(instanceDispose.toCallback());
objectClass.setGetProperty(getProperty.toCallback());
objectClass.setSetProperty(setProperty.toCallback());
ParamSpec paramType = GObject.paramSpecGtype(PROP_NAME, "", "", PARENT_TYPE,
ParamFlags.CONSTRUCT
.or(ParamFlags.READWRITE,
ParamFlags.STATIC_NAME,
ParamFlags.STATIC_NICK,
ParamFlags.STATIC_BLURB)
);
objectClass.installProperty(PROP_ITEM_TYPE, paramType);
};
private static final GetItemTypeCallback getItemType = address -> {
System.out.println("ListIndex::getItemType");
return getType().getValue();
};
private static final GetNItemsCallback getNItems = address -> fromAddress.marshal(address, Ownership.NONE).getSize();
private static final GetItemCallback getItem = (inst, position) -> {
ListIndex item = fromAddress.marshal(inst, Ownership.NONE).getItem(position);
if (item == null) return MemoryAddress.NULL;
return item.handle();
};
public ListIndex getItem(int position) {
if (position >= getSize() || position <= -1) return null;
ListIndex result = new ListIndex(getSize());
result.setIndex(position);
return result;
}
private static final InterfaceInitFunc interfaceInit = (iface, data) -> {
System.out.println("ListIndex::interfaceInit");
ListModelInterface lmi = ListModelInterface.fromAddress.marshal(iface.handle(), Ownership.NONE);
lmi.setGetItem(getItem.toCallback());
lmi.setGetNItems(getNItems.toCallback());
lmi.setGetItemType(getItemType.toCallback());
};
public int getIndex() {
return (int) ListIndex.index.get(MemorySegment.ofAddress((MemoryAddress) handle(), memoryLayout.byteSize(), Interop.getScope()));
}
public void setIndex(int index) {
ListIndex.index.set(MemorySegment.ofAddress((MemoryAddress) handle(), memoryLayout.byteSize(), Interop.getScope()), index);
}
public int getSize() {
return (int) ListIndex.size.get(MemorySegment.ofAddress((MemoryAddress) handle(), memoryLayout.byteSize(), Interop.getScope()));
}
public void setSize(int size) {
int oldSize = getSize();
ListIndex.size.set(MemorySegment.ofAddress((MemoryAddress) handle(), memoryLayout.byteSize(), Interop.getScope()), size);
asListModel().itemsChanged(0, oldSize, size);
}
public ListModel asListModel() {
return ListModel.castFrom(this);
}
public SingleSelection inSingleSelection() {
return new SingleSelection(asListModel());
}
public SelectionModel inSelectionModel() {
return SelectionModel.castFrom(inSingleSelection());
}
public static int toIndex(ListItem item) {
return castFrom(item.getItem()).getIndex();
}
}

View File

@ -1,25 +1,24 @@
package io.gitlab.jfronny.inceptum.gtk.window;
import ch.bailu.gtk.gtk.AboutDialog;
import ch.bailu.gtk.gtk.License;
import ch.bailu.gtk.type.Str;
import org.gtk.gtk.AboutDialog;
import org.gtk.gtk.License;
import io.gitlab.jfronny.inceptum.common.BuildMetadata;
import io.gitlab.jfronny.inceptum.gtk.util.I18n;
public class AboutWindow extends AboutDialog {
public AboutWindow() {
setProgramName(new Str("Inceptum"));
setCopyright(new Str("Copyright (C) 2021 JFronny"));
setVersion(new Str(BuildMetadata.VERSION));
setLicenseType(License.MIT_X11);
setLicense(I18n.str("about.license"));
setWebsiteLabel(I18n.str("about.contact"));
setWebsite(new Str("https://jfronny.gitlab.io/contact.html"));
programName = "Inceptum";
copyright = "Copyright (C) 2021 JFronny";
version = BuildMetadata.VERSION;
licenseType = License.MIT_X11;
license = I18n.get("about.license");
websiteLabel = I18n.get("about.contact");
website = "https://jfronny.gitlab.io/contact.html";
if (!BuildMetadata.IS_PUBLIC) {
setComments(I18n.str("about.unsupported-build"));
comments = I18n.get("about.unsupported-build");
}
int vm = Runtime.version().feature();
setSystemInformation(I18n.str(BuildMetadata.VM_VERSION == vm ? "about.jvm" : "about.jvm.unsupported", vm));
systemInformation = I18n.get(BuildMetadata.VM_VERSION == vm ? "about.jvm" : "about.jvm.unsupported", vm);
//TODO setLogo
}

View File

@ -1,13 +1,11 @@
package io.gitlab.jfronny.inceptum.gtk.window;
import ch.bailu.gtk.GTK;
import ch.bailu.gtk.adw.Clamp;
import ch.bailu.gtk.adw.StatusPage;
import ch.bailu.gtk.bridge.ListIndex;
import ch.bailu.gtk.gio.Menu;
import ch.bailu.gtk.glib.Glib;
import ch.bailu.gtk.gtk.*;
import ch.bailu.gtk.type.Str;
import io.gitlab.jfronny.inceptum.gtk.util.ListIndex;
import org.gnome.adw.Clamp;
import org.gnome.adw.StatusPage;
import org.gtk.gio.Menu;
import org.gtk.glib.GLib;
import org.gtk.gtk.*;
import io.gitlab.jfronny.inceptum.common.*;
import io.gitlab.jfronny.inceptum.gtk.GtkMenubar;
import io.gitlab.jfronny.inceptum.gtk.control.InstanceGridEntryFactory;
@ -40,24 +38,24 @@ public class MainWindow extends ApplicationWindow {
HeaderBar header = new HeaderBar();
Button newButton = new Button();
newButton.setIconName(new Str("list-add-symbolic"));
newButton.onClicked(NewInstanceWindow::createAndShow);
newButton.setIconName("list-add-symbolic");
newButton.onClicked($ -> NewInstanceWindow.createAndShow());
MenuButton accountsButton = new MenuButton();
accountsButton.setIconName(new Str("avatar-default-symbolic"));
accountsButton.setIconName("avatar-default-symbolic");
accountsButton.setPopover(GtkMenubar.accountsMenu.asPopover());
listButton = new Button();
listButton.setIconName(new Str("view-list-symbolic"));
listButton.onClicked(() -> {
listButton.setIconName("view-list-symbolic");
listButton.onClicked($ -> {
InceptumConfig.listView = true;
InceptumConfig.saveConfig();
generateWindowBody();
});
gridButton = new Button();
gridButton.setIconName(new Str("view-grid-symbolic"));
gridButton.onClicked(() -> {
gridButton.setIconName("view-grid-symbolic");
gridButton.onClicked($ -> {
InceptumConfig.listView = false;
InceptumConfig.saveConfig();
generateWindowBody();
@ -70,7 +68,7 @@ public class MainWindow extends ApplicationWindow {
uiMenu.button("preferences", () -> {}); //TODO preferences UI inspired by boxes
uiMenu.button("about", AboutWindow::createAndShow);
MenuButton menuButton = new MenuButton();
menuButton.setIconName(new Str("open-menu-symbolic"));
menuButton.setIconName("open-menu-symbolic");
menuButton.setPopover(uiMenu.asPopover());
header.packStart(newButton);
@ -84,26 +82,27 @@ public class MainWindow extends ApplicationWindow {
instanceListIndex = new ListIndex();
listView = new Clamp();
listView.setMaximumSize(900);
listView.setChild(new ListView(instanceListIndex.inSelectionModel(), new InstanceListEntryFactory(instanceList)));
listView.maximumSize = 900;
listView.child = new ListView(instanceListIndex.inSelectionModel(), new InstanceListEntryFactory(instanceList));
gridView = new GridView(instanceListIndex.inSelectionModel(), new InstanceGridEntryFactory(instanceList));
empty = new StatusPage();
empty.setTitle(I18n.str("main.empty.title"));
empty.setDescription(I18n.str("main.empty.description"));
empty.title = I18n.get("main.empty.title");
empty.description = I18n.get("main.empty.description");
//TODO empty.setIconName(new Str());
stack = new Stack();
stack.addChild(listView);
stack.addChild(gridView);
stack.addChild(empty);
ScrolledWindow scroll = new ScrolledWindow();
scroll.setPolicy(PolicyType.NEVER, PolicyType.AUTOMATIC);
scroll.setChild(stack);
scroll.child = stack;
setDefaultSize(360, 720);
setTitle(new Str("Inceptum"));
setTitlebar(header);
setShowMenubar(GTK.FALSE);
setChild(scroll);
title = "Inceptum";
titlebar = header;
showMenubar = false;
child = scroll;
generateWindowBody();
//TODO DropTarget to add mods/instances
@ -118,7 +117,7 @@ public class MainWindow extends ApplicationWindow {
private void setupDirWatcher() throws IOException { //TODO test (including after lock state change)
WatchService ws = FileSystems.getDefault().newWatchService();
MetaHolder.INSTANCE_DIR.register(ws, ENTRY_MODIFY, ENTRY_CREATE, ENTRY_DELETE);
int source = Glib.idleAdd(user_data -> {
int source = GLib.idleAdd(data -> {
//TODO watch instance dirs for locks
WatchKey key = ws.poll();
boolean instancesChanged = false;
@ -131,29 +130,29 @@ public class MainWindow extends ApplicationWindow {
}
}
if (instancesChanged) generateWindowBody();
return GTK.TRUE;
return true;
}, null);
onCloseRequest(() -> {
onCloseRequest($ -> {
try {
ws.close();
} catch (IOException ignored) {
}
Glib.sourceRemove(source);
return GTK.FALSE;
GLib.sourceRemove(source);
return false;
});
}
private void generateWindowBody() {
if (listButton != null) listButton.setVisible(GTK.is(!InceptumConfig.listView));
if (gridButton != null) gridButton.setVisible(GTK.is(InceptumConfig.listView));
if (listButton != null) listButton.visible = !InceptumConfig.listView;
if (gridButton != null) gridButton.visible = InceptumConfig.listView;
try {
instanceList.clear();
instanceList.addAll(InstanceList.ordered());
instanceListIndex.setSize(instanceList.size());
if (InstanceList.isEmpty()) stack.setVisibleChild(empty);
else if (InceptumConfig.listView) stack.setVisibleChild(listView);
else stack.setVisibleChild(gridView);
if (InstanceList.isEmpty) stack.visibleChild = empty;
else if (InceptumConfig.listView) stack.visibleChild = listView;
else stack.visibleChild = gridView;
} catch (IOException e) {
Utils.LOGGER.error("Could not generate window body", e);
}

View File

@ -28,6 +28,6 @@ instance.launch.locked.running=This instance is currently running.\
Click OK to start a second instance of the game.\
Please be aware that doing so is unsupported and WILL cause issues!
instance.launch=Launch
main.empty.title=# Welcome to Inceptum
main.empty.title=Welcome to Inceptum
main.empty.description=To get started, create (or import) a new instance using the + button
instance.directory=Open Directory

View File

@ -28,6 +28,6 @@ instance.launch.locked.running=Diese Instanz l
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.
instance.launch=Starten
main.empty.title=# Willkommen bei Inceptum
main.empty.title=Willkommen bei Inceptum
main.empty.description=Importiere oder erstelle um anzufangen eine Instanz mit dem +
instance.directory=Verzeichnis öffnen

View File

@ -1,5 +1,5 @@
plugins {
id("inceptum.application-conventions")
id("inceptum.application")
id("inceptum.manifold")
}

View File

@ -1,5 +1,5 @@
plugins {
id("inceptum.library-conventions")
id("inceptum.library")
id("inceptum.gson-compile")
id("inceptum.manifold")
}

View File

@ -8,7 +8,7 @@ import io.gitlab.jfronny.inceptum.launcher.api.account.MicrosoftAccount;
import java.io.IOException;
public class MicrosoftAccountAdapter {
public static void write(JsonWriter writer, MicrosoftAccount value) throws IOException {
public static void write(MicrosoftAccount value, JsonWriter writer) throws IOException {
GC_MicrosoftAccountMeta.write(value == null ? null : value.toMeta(), writer);
}

View File

@ -11,7 +11,7 @@ import java.util.List;
import java.util.Set;
public class MinecraftArgumentAdapter {
public static void write(JsonWriter writer, MinecraftArgument rules) throws IOException {
public static void write(MinecraftArgument rules, JsonWriter writer) throws IOException {
throw new UnsupportedOperationException();
}

View File

@ -3,22 +3,22 @@ package io.gitlab.jfronny.inceptum.launcher.gson;
import gsoncompile.extensions.io.gitlab.jfronny.inceptum.launcher.system.source.ModSource.GC_ModSource;
import io.gitlab.jfronny.gson.stream.JsonReader;
import io.gitlab.jfronny.gson.stream.JsonWriter;
import io.gitlab.jfronny.inceptum.launcher.model.inceptum.ModMeta$Sources;
import io.gitlab.jfronny.inceptum.launcher.model.inceptum.ModMeta.Sources;
import io.gitlab.jfronny.inceptum.launcher.system.source.ModSource;
import java.io.IOException;
import java.util.Optional;
public class ModMetaSourcesAdapter {
public static void write(JsonWriter writer, ModMeta$Sources value) throws IOException {
public static void write(Sources value, JsonWriter writer) throws IOException {
writer.beginArray();
for (ModSource source : value.keySet()) GC_ModSource.write(source, writer);
writer.endArray();
}
public static ModMeta$Sources read(JsonReader reader) throws IOException {
public static Sources read(JsonReader reader) throws IOException {
reader.beginArray();
ModMeta$Sources sources = new ModMeta$Sources();
Sources sources = new Sources();
while (reader.hasNext()) {
sources.put(GC_ModSource.read(reader), Optional.empty());
}

View File

@ -10,7 +10,7 @@ import java.util.LinkedHashSet;
import java.util.Set;
public class ModSourceAdapter {
public static void write(JsonWriter writer, ModSource src) throws IOException {
public static void write(ModSource src, JsonWriter writer) throws IOException {
writer.beginObject();
if (src instanceof ModrinthModSource mo) {
writer.name("type").value("modrinth")
@ -22,7 +22,7 @@ public class ModSourceAdapter {
.name("dependencies");
writer.beginArray();
for (ModSource dependency : di.dependencies) {
write(writer, dependency);
write(dependency, writer);
}
writer.endArray();
} else if (src instanceof CurseforgeModSource cu) {

View File

@ -9,7 +9,7 @@ import io.gitlab.jfronny.inceptum.launcher.model.mojang.Rules;
import java.io.IOException;
public class RulesAdapter {
public static void write(JsonWriter writer, Rules rules) throws IOException {
public static void write(Rules rules, JsonWriter writer) throws IOException {
throw new UnsupportedOperationException();
}

View File

@ -1,79 +0,0 @@
package io.gitlab.jfronny.inceptum.launcher.model.inceptum;
import io.gitlab.jfronny.gson.compile.annotations.GSerializable;
import io.gitlab.jfronny.inceptum.common.GsonPreset;
import io.gitlab.jfronny.inceptum.launcher.gson.ModMetaSourcesAdapter;
import io.gitlab.jfronny.inceptum.launcher.system.source.ModSource;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.*;
@GSerializable(with = ModMetaSourcesAdapter.class, configure = GsonPreset.Config.class)
public class ModMeta$Sources implements Map<ModSource, Optional<ModSource>> {
private Map<ModSource, Optional<ModSource>> delegate = new LinkedHashMap<>();
@Override
public int size() {
return delegate.size();
}
@Override
public boolean isEmpty() {
return delegate.isEmpty();
}
@Override
public boolean containsKey(Object o) {
return delegate.containsKey(o);
}
@Override
public boolean containsValue(Object o) {
return delegate.containsValue(o);
}
@Override
public Optional<ModSource> get(Object o) {
return delegate[o];
}
@Nullable
@Override
public Optional<ModSource> put(ModSource modSource, Optional<ModSource> modSource2) {
return delegate.put(modSource, modSource2);
}
@Override
public Optional<ModSource> remove(Object o) {
return delegate.remove(o);
}
@Override
public void putAll(@NotNull Map<? extends ModSource, ? extends Optional<ModSource>> map) {
delegate.putAll(map);
}
@Override
public void clear() {
delegate.clear();
}
@NotNull
@Override
public Set<ModSource> keySet() {
return delegate.keySet();
}
@NotNull
@Override
public Collection<Optional<ModSource>> values() {
return delegate.values();
}
@NotNull
@Override
public Set<Entry<ModSource, Optional<ModSource>>> entrySet() {
return delegate.entrySet();
}
}

View File

@ -1,14 +1,19 @@
package io.gitlab.jfronny.inceptum.launcher.model.inceptum;
import io.gitlab.jfronny.commons.HashUtils;
import io.gitlab.jfronny.commons.data.MutCollection;
import io.gitlab.jfronny.commons.data.delegate.DelegateMap;
import io.gitlab.jfronny.gson.compile.annotations.GPrefer;
import io.gitlab.jfronny.gson.compile.annotations.GSerializable;
import io.gitlab.jfronny.inceptum.common.GsonPreset;
import io.gitlab.jfronny.inceptum.common.Utils;
import io.gitlab.jfronny.inceptum.launcher.api.CurseforgeApi;
import io.gitlab.jfronny.inceptum.launcher.api.ModrinthApi;
import io.gitlab.jfronny.inceptum.launcher.gson.ModMetaSourcesAdapter;
import io.gitlab.jfronny.inceptum.launcher.model.curseforge.response.FingerprintMatchesResponse;
import io.gitlab.jfronny.inceptum.launcher.system.source.*;
import io.gitlab.jfronny.inceptum.launcher.system.source.CurseforgeModSource;
import io.gitlab.jfronny.inceptum.launcher.system.source.ModSource;
import io.gitlab.jfronny.inceptum.launcher.system.source.ModrinthModSource;
import org.jetbrains.annotations.Nullable;
import java.io.IOException;
@ -17,16 +22,24 @@ import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
@GSerializable(configure = GsonPreset.Config.class)
public record ModMeta(
ModMeta$Sources sources, //key: source, value: update
Sources sources, //key: source, value: update
String sha1,
Long murmur2,
List<String> dependents, // by file name
List<String> dependencies, // by file name
boolean explicit
) {
@GSerializable(with = ModMetaSourcesAdapter.class, configure = GsonPreset.Config.class)
public static class Sources extends DelegateMap<ModSource, Optional<ModSource>> {
public Sources() {
super(MutCollection.mapOf());
}
}
@GPrefer
public ModMeta {}
@ -43,7 +56,7 @@ public record ModMeta(
}
}
return new ModMeta(
new ModMeta$Sources(),
new Sources(),
sha1,
murmur2,
new ArrayList<>(),
@ -54,7 +67,7 @@ public record ModMeta(
public static ModMeta of(String sha1, Long murmur2, @Nullable ModSource knownSource, String gameVersion) {
ModMeta res = new ModMeta(
new ModMeta$Sources(),
new Sources(),
sha1,
murmur2,
new ArrayList<>(),

View File

@ -19,7 +19,7 @@ public class InstanceList {
public static void reset() {
synchronized (metas) {
for (var entry : metas) {
for (var entry : metas.entrySet()) {
try {
entry.value.close();
} catch (IOException e) {

View File

@ -6,6 +6,7 @@ import io.gitlab.jfronny.gson.JsonParseException;
import io.gitlab.jfronny.inceptum.common.Utils;
import io.gitlab.jfronny.inceptum.launcher.model.fabric.FabricModJson;
import io.gitlab.jfronny.inceptum.launcher.model.inceptum.ModMeta;
import io.gitlab.jfronny.inceptum.launcher.system.instance.Instance;
import io.gitlab.jfronny.inceptum.launcher.system.instance.Mod;
import io.gitlab.jfronny.inceptum.launcher.system.instance.ModPath;
import io.gitlab.jfronny.inceptum.launcher.system.mds.noop.NoopMod;
@ -26,75 +27,53 @@ public record FileScanTask(ProtoInstance instance, Path file, BiConsumer<Path, M
if (!Files.exists(file)) return;
if (Files.isDirectory(file)) return; // Directories are not supported
try {
if (ModPath.isJar(file)) {
final MetadataRef imod = new MetadataRef(file.parent.resolve(file.fileName + ModPath.EXT_IMOD), ModMeta::of);
evaluateSources(file, imod);
discovered.accept(imod.imodPath, new MdsMod(instance, imod.imodPath, file, false, imod.meta, getFmj(file, imod.meta)));
} else if (ModPath.isImod(file)) {
String fn = file.fileName.toString();
Path modFile = file.parent.resolve(fn.substring(0, fn.length() - ModPath.EXT_IMOD.length()));
final MetadataRef imod = new MetadataRef(file, null);
evaluateSources(modFile, imod);
boolean managedJar = !Files.exists(modFile);
discovered.accept(imod.imodPath, new MdsMod(instance, imod.imodPath, managedJar ? imod.jarPath : modFile, managedJar, imod.meta, getFmj(modFile, imod.meta)));
} else discovered.accept(file, new NoopMod(file));
if (ModPath.isJar(file)) discover(file, ModPath.appendImod(file));
else if (ModPath.isImod(file)) discover(ModPath.trimImod(file), file);
else discovered.accept(file, new NoopMod(file));
} catch (IOException | URISyntaxException | JsonParseException e) {
Utils.LOGGER.error("Could not scan file for mod info", e);
}
}
private <TEx extends Throwable> void evaluateSources(Path modFile, MetadataRef ref) throws IOException, TEx {
private void discover(Path jarPath, Path imodPath) throws IOException, URISyntaxException {
boolean managed = false;
ModMeta meta;
if (Files.exists(imodPath)) meta = GC_ModMeta.read(imodPath);
else {
meta = ModMeta.of(jarPath);
GC_ModMeta.write(meta, imodPath);
}
boolean modified = false;
if (ref.meta.initialize(gameVersion)) {
GC_ModMeta.write(ref.meta, ref.imodPath);
if (meta.initialize(gameVersion)) {
GC_ModMeta.write(meta, imodPath);
modified = true;
}
ModSource selectedSource = null;
for (ModSource source : ref.meta.sources.keySet()) {
for (ModSource source : meta.sources.keySet()) {
source.getUpdate(gameVersion);
if (!Files.exists(source.jarPath)) source.download();
selectedSource = source;
}
if (selectedSource != null) {
if (Files.exists(modFile)) {
Files.delete(modFile);
Path newImod = ref.imodPath.parent.resolve(selectedSource.shortName + ModPath.EXT_IMOD);
Files.move(ref.imodPath, newImod);
ref.imodPath = newImod;
if (Files.exists(jarPath)) {
Files.delete(jarPath);
Path newImod = imodPath.parent.resolve(selectedSource.shortName + ModPath.EXT_IMOD);
Files.move(imodPath, newImod);
imodPath = newImod;
modified = true;
}
ref.jarPath = selectedSource.jarPath;
}
if (modified) ref.update();
}
jarPath = selectedSource.jarPath;
managed = true;
} else if (!Files.exists(jarPath)) throw new IOException("Mod has no jar and no sources");
if (modified) meta = GC_ModMeta.read(imodPath);
private @Nullable FabricModJson getFmj(Path modJarDefault, ModMeta md) throws IOException, URISyntaxException {
if (!Files.exists(modJarDefault)) {
if (md.sources.isEmpty()) {
throw new FileNotFoundException("Mod " + modJarDefault.fileName.toString() + " doesn't specify a source and has no file");
}
modJarDefault = List.copyOf(md.sources.keySet())[0].jarPath;
}
try (FileSystem fs = Utils.openZipFile(modJarDefault, false)) {
FabricModJson fmj;
try (FileSystem fs = Utils.openZipFile(jarPath, false)) {
Path fmjPath = fs.getPath("fabric.mod.json");
if (!Files.exists(fmjPath)) return null;
return GC_FabricModJson.read(fmjPath);
}
}
private static class MetadataRef {
public MetadataRef(Path imodPath, @Nullable Function<Path, ModMeta> defaultMeta) throws IOException {
this.imodPath = imodPath;
if (!Files.exists(imodPath) && defaultMeta != null) GC_ModMeta.write(defaultMeta.apply(imodPath), imodPath);
if (Files.exists(imodPath)) update();
if (Files.exists(fmjPath)) fmj = GC_FabricModJson.read(fmjPath);
else fmj = null;
}
public Path imodPath;
public Path jarPath; // filled in from evaluateSources
public ModMeta meta;
public void update() throws IOException {
this.meta = GC_ModMeta.read(imodPath);
}
discovered.accept(imodPath, new MdsMod(instance, imodPath, jarPath, managed, meta, fmj));
}
}

View File

@ -131,8 +131,7 @@ class ModsDirScannerImpl implements ModsDirScanner {
if (!key.reset()) Utils.LOGGER.warn("Could not reset config watch key");
}
JFiles.listTo(instance.modsDir, path -> {
if (!descriptions.containsKey(path))
toScan.add(path);
if (!descriptions.containsKey(path)) toScan.add(path);
});
}
for (Path p : toScan) {

View File

@ -27,7 +27,7 @@ public class DownloadAssetsStep implements Step {
info.setState("Downloading asset: " + entry.key);
McApi.downloadAsset(entry.value, fPath);
}
} catch (URISyntaxException e) {
} catch (Throwable e) {
throw new IOException("Could not download assets", e);
}
}

View File

@ -8,12 +8,12 @@ import java.io.InputStreamReader;
public class ProcessUtils {
public static boolean isProcessAlive(String pid) {
return isProcessIdRunning(pid, OSUtils.TYPE == OSUtils.Type.WINDOWS
? "cmd /c tasklist /FI \"PID eq " + pid + "\""
: "ps -p " + pid);
if (OSUtils.TYPE == OSUtils.Type.WINDOWS)
return isProcessIdRunning(pid, "tasklist", "/FI", "PID eq " + pid);
return isProcessIdRunning(pid, "ps", "-p", pid);
}
private static boolean isProcessIdRunning(String pid, String command) {
private static boolean isProcessIdRunning(String pid, String... command) {
try {
Runtime rt = Runtime.getRuntime();
Process pr = rt.exec(command);

View File

@ -1,5 +1,5 @@
plugins {
id("inceptum.library-conventions")
id("inceptum.library")
}
java {

@ -1 +1 @@
Subproject commit 11fb5ad2ad266bac007e7b11178fc3585cac45b9
Subproject commit 28770e5269128412d8d51a03aa6c072a8eff10cb

View File

@ -1,5 +1,5 @@
plugins {
id("inceptum.application-standalone-conventions")
id("inceptum.application-standalone")
}
application {