An attempt to implement a working updater

This commit is contained in:
JFronny 2021-10-31 16:59:25 +01:00
parent bb8e90457e
commit abc33b3331
No known key found for this signature in database
GPG Key ID: BEC5ACBBD4EE17E5
40 changed files with 759 additions and 108 deletions

View File

@ -20,6 +20,9 @@ build_test:
- gradle --build-cache build publish -Pflavor=macos -PpublicMaven -Ppipeline=$CI_PIPELINE_ID
- cp build/libs/*-*-*.jar ./
- cp build/libs/*.exe ./
- cp wrapper/build/libs/* build/libs/
- cp wrapper/build/libs/*.exe wrapper.exe
- cp wrapper/build/libs/*-all.jar wrapper.jar
- for f in *-*-*.jar; do mv "$f" "latest-${f##*-}";done
- mv latest-fat.jar latest.jar
- mv *-*.exe latest.exe

View File

@ -2,4 +2,22 @@
A FOSS Launcher for Minecraft written in Java
Inceptum is a WIP minecraft launcher\
Since it is very bare-bones currently, I would not recommend using it
Since it is very bare-bones currently, I would not recommend using it
## Licenses
Inceptum utilizes code/libraries/assets from:
- [ATLauncher](https://github.com/ATLauncher/ATLauncher): Microsoft authentication
- [MultiMC](https://github.com/MultiMC/Launcher): Used as a reference for some implementations
- [imgui-java](https://github.com/SpaiR/imgui-java): The library used for UI
- [Dear ImGui](https://github.com/ocornut/imgui): Included and wrapped in imgui-java, UI library
- [LWJGL](https://github.com/LWJGL/lwjgl3): Used as a backend for imgui-java
- [gson](https://github.com/google/gson): Used for interacting with various APIs and configs
- [slf4j](https://github.com/qos-ch/slf4j): Used for logging
- [logback](https://github.com/qos-ch/logback): An implementation of lsf4j
- [JLHTTP](https://github.com/curtcox/JLHTTP): Used for ATLaunchers Microsoft Authentication implementation
- [JGit](https://www.eclipse.org/jgit/): Used in instance creation, hopefully also for sync soon
- [fabric-meta](https://github.com/FabricMC/fabric-meta): Code for launching servers from PR#16
- [Ubuntu](https://design.ubuntu.com/font/): Used with nerd font symbols as the font
- [Nerd Fonts](https://www.nerdfonts.com/): Extra symbols for the font, currently unused
- [meteor-client](https://github.com/MeteorDevelopment/meteor-client): A simple HTTP client
- Several of [my other projects](https://gitlab.com/jfmods)

View File

@ -5,12 +5,8 @@ plugins {
id 'application'
id 'com.github.johnrengelman.shadow' version '7.0.0'
id "maven-publish"
id "de.undercouch.download" version "4.1.1"
}
group 'io.gitlab.jfronny'
version '0.1' + (project.hasProperty('pipeline') ? "+" + project.getProperty('pipeline') : "")
application {
mainClass = 'io.gitlab.jfronny.inceptum.Inceptum'
}
@ -19,6 +15,11 @@ repositories {
mavenCentral()
}
allprojects {
version "$project.ver" + (project.hasProperty('pipeline') ? "+" + project.getProperty('pipeline') : "")
group 'io.gitlab.jfronny.inceptum'
}
ext {
lwjglVersion = '3.2.3'
imguiVersion = '1.84.1.3'
@ -48,6 +49,7 @@ dependencies {
implementation 'ch.qos.logback:logback-classic:1.2.6'
implementation 'net.freeutils:jlhttp:2.6'
implementation 'org.eclipse.jgit:org.eclipse.jgit:5.13.0.202109080827-r'
implementation project(":wrapper")
implementation platform("org.lwjgl:lwjgl-bom:$lwjglVersion")
@ -80,35 +82,6 @@ shadowJar {
archiveClassifier.set(flavorProp)
}
class FileOutput extends DefaultTask {
@OutputFile
File output
}
def bootstrapVersion = "0.1.6"
def bootstrapArch = "i686"
task downloadBootstrap(type: Download) {
src "https://maven.fabricmc.net/net/fabricmc/fabric-installer-native-bootstrap/windows-${bootstrapArch}/${bootstrapVersion}/windows-${bootstrapArch}-${bootstrapVersion}.exe"
dest project.buildDir
}
task nativeExe(dependsOn: [downloadBootstrap, shadowJar], type: FileOutput) {
output = file("${projectDir}/build/libs/${archivesBaseName}-${project.version}.exe")
outputs.upToDateWhen { false }
doFirst {
output.delete()
}
doLast {
output.createNewFile()
output.setBytes downloadBootstrap.outputFiles.first().readBytes()
output.append shadowJar.archiveFile.get().getAsFile().readBytes()
}
}
if (flavor == 'windows') build.dependsOn nativeExe
publishing {
publications {
mavenJava(MavenPublication) {

1
gradle.properties Normal file
View File

@ -0,0 +1 @@
ver=0.1

View File

@ -1,2 +1,3 @@
rootProject.name = 'Inceptum'
include 'wrapper'

View File

@ -3,10 +3,10 @@ package io.gitlab.jfronny.inceptum;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import io.gitlab.jfronny.inceptum.cli.*;
import io.gitlab.jfronny.inceptum.gson.GsonIgnoreExclusionStrategy;
import io.gitlab.jfronny.inceptum.gson.MinecraftArgumentDeserializer;
import io.gitlab.jfronny.inceptum.gson.OauthTokenResponseDeserializer;
import io.gitlab.jfronny.inceptum.gson.RulesDeserializer;
import io.gitlab.jfronny.inceptum.gson.*;
import io.gitlab.jfronny.inceptum.model.ComparableVersion;
import io.gitlab.jfronny.inceptum.model.inceptum.CommandArguments;
import io.gitlab.jfronny.inceptum.model.inceptum.Config;
import io.gitlab.jfronny.inceptum.model.inceptum.InceptumVersion;
import io.gitlab.jfronny.inceptum.model.microsoft.OauthTokenResponse;
import io.gitlab.jfronny.inceptum.model.mojang.MinecraftArgument;
@ -26,9 +26,8 @@ import java.util.Set;
//TODO mods browser
//TODO allow instance sync through metadata
//TODO update checker
public class Inceptum {
public static final Set<Command> COMMANDS = Set.of(new HelpCommand(), new GuiCommand(), new LaunchCommand(), new JvmStateCommand());
public static final Set<Command> COMMANDS = Set.of(new HelpCommand(), new GuiCommand(), new LaunchCommand(), new UpdateCheckCommand(), new JvmStateCommand());
public static boolean IS_GUI;
public static final Logger LOGGER = LoggerFactory.getLogger("Inceptum");
@ -36,6 +35,7 @@ public class Inceptum {
.registerTypeAdapter(MinecraftArgument.class, new MinecraftArgumentDeserializer())
.registerTypeAdapter(Rules.class, new RulesDeserializer())
.registerTypeAdapter(OauthTokenResponse.class, new OauthTokenResponseDeserializer())
.registerTypeAdapter(ComparableVersion.class, new ComparableVersionAdapter())
.excludeFieldsWithModifiers(Modifier.TRANSIENT)
.excludeFieldsWithModifiers(Modifier.PRIVATE)
.addSerializationExclusionStrategy(new GsonIgnoreExclusionStrategy())
@ -82,7 +82,7 @@ public class Inceptum {
if (!Files.exists(ASSETS_DIR)) Files.createDirectories(ASSETS_DIR);
if (!Files.exists(LIBRARIES_DIR)) Files.createDirectories(LIBRARIES_DIR);
cmd.invoke(args);
cmd.invoke(new CommandArguments(args));
}
public static void saveConfig() {
@ -109,8 +109,12 @@ public class Inceptum {
}
public static void showOkCancel(String message, String title, Runnable ok, Runnable cancel) {
showOkCancel(message, title, ok, cancel, false);
}
public static void showOkCancel(String message, String title, Runnable ok, Runnable cancel, boolean defaultCancel) {
LOGGER.info(message);
if (IS_GUI) InceptumGui.WINDOWS.add(new AlertWindow(title, message, ok, cancel));
else ok.run();
else if (!defaultCancel) ok.run();
}
}

View File

@ -5,6 +5,7 @@ import imgui.ImGuiIO;
import imgui.flag.ImGuiConfigFlags;
import imgui.gl3.ImGuiImplGl3;
import imgui.glfw.ImGuiImplGlfw;
import io.gitlab.jfronny.inceptum.model.inceptum.CommandArguments;
import io.gitlab.jfronny.inceptum.util.api.account.AccountManager;
import io.gitlab.jfronny.inceptum.windows.MainWindow;
import io.gitlab.jfronny.inceptum.windows.Window;
@ -34,7 +35,7 @@ public class InceptumGui {
protected static long handle;
private static String glslVersion = null;
public static void main(String[] args) {
public static void main(CommandArguments args) {
AccountManager.loadAccounts();
Inceptum.LOGGER.info("Initializing UI");
InceptumGui.WINDOWS.add(new MainWindow());

View File

@ -1,5 +1,7 @@
package io.gitlab.jfronny.inceptum.cli;
import io.gitlab.jfronny.inceptum.model.inceptum.CommandArguments;
import java.util.Arrays;
import java.util.List;
import java.util.Locale;
@ -29,5 +31,5 @@ public abstract class Command {
return false;
}
public abstract void invoke(String[] args);
public abstract void invoke(CommandArguments args);
}

View File

@ -2,6 +2,11 @@ package io.gitlab.jfronny.inceptum.cli;
import io.gitlab.jfronny.inceptum.Inceptum;
import io.gitlab.jfronny.inceptum.InceptumGui;
import io.gitlab.jfronny.inceptum.model.inceptum.CommandArguments;
import io.gitlab.jfronny.inceptum.model.inceptum.UpdateInfo;
import java.io.IOException;
import java.net.URISyntaxException;
public class GuiCommand extends Command {
public GuiCommand() {
@ -9,9 +14,21 @@ public class GuiCommand extends Command {
}
@Override
public void invoke(String[] args) {
public void invoke(CommandArguments args) {
Inceptum.IS_GUI = true;
InceptumGui.main(args);
UpdateInfo update = UpdateCheckCommand.getUpdate();
if (update == null) {
InceptumGui.main(args);
} else {
Inceptum.showOkCancel("An update was found. Should it be installed automatically?", "Update found", () -> {
try {
UpdateCheckCommand.update(update, true);
InceptumGui.exit();
} catch (IOException | URISyntaxException e) {
Inceptum.showError("Could not download update", e);
}
}, () -> InceptumGui.main(args));
}
}
@Override

View File

@ -1,13 +1,14 @@
package io.gitlab.jfronny.inceptum.cli;
import io.gitlab.jfronny.inceptum.Inceptum;
import io.gitlab.jfronny.inceptum.model.inceptum.CommandArguments;
public class HelpCommand extends Command {
public HelpCommand() {
super("Displays this screen", "help");
}
@Override
public void invoke(String[] args) {
public void invoke(CommandArguments args) {
System.out.println("Inceptum v" + Inceptum.VERSION.version + " (" + Inceptum.VERSION.flavor + ")\n\nCommands:");
for (Command command : Inceptum.COMMANDS) {
System.out.println(" " + command.getName() + " - " + command.getHelp());

View File

@ -1,5 +1,7 @@
package io.gitlab.jfronny.inceptum.cli;
import io.gitlab.jfronny.inceptum.model.inceptum.CommandArguments;
import java.net.URLClassLoader;
import java.util.Arrays;
@ -9,7 +11,7 @@ public class JvmStateCommand extends Command {
}
@Override
public void invoke(String[] args) {
public void invoke(CommandArguments args) {
System.out.println(System.getProperty("java.class.path"));
dumpClasspath(JvmStateCommand.class.getClassLoader());
}

View File

@ -1,6 +1,7 @@
package io.gitlab.jfronny.inceptum.cli;
import io.gitlab.jfronny.inceptum.Inceptum;
import io.gitlab.jfronny.inceptum.model.inceptum.CommandArguments;
import io.gitlab.jfronny.inceptum.model.inceptum.InstanceMeta;
import io.gitlab.jfronny.inceptum.util.ClientLauncher;
import io.gitlab.jfronny.inceptum.util.ProcessUtils;
@ -11,7 +12,6 @@ import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Locale;
public class LaunchCommand extends Command {
public LaunchCommand() {
@ -19,12 +19,12 @@ public class LaunchCommand extends Command {
}
@Override
public void invoke(String[] args) {
if (args.length < 2) {
public void invoke(CommandArguments args) {
if (args.length == 0) {
Inceptum.LOGGER.error("You must provide an instance name or path");
return;
}
String pArg = args.length == 2 ? args[1] : args[2];
String pArg = args.last();
Path instanceDir = Files.exists(Path.of(pArg)) ? Path.of(pArg) : Inceptum.INSTANCE_DIR.resolve(pArg);
if (!Files.exists(instanceDir.resolve("instance.json"))) {
Inceptum.LOGGER.error("Not a valid instance");
@ -52,13 +52,11 @@ public class LaunchCommand extends Command {
Inceptum.showError("Not a valid instance", e);
return;
}
if (args.length > 3) {
if (args.length > 2) {
instance.gameArgsCustom = instance.gameArgsCustom == null ? new ArrayList<>() : new ArrayList<>(instance.gameArgsCustom);
for (int i = 3, argsLength = args.length; i < argsLength; i++) {
instance.gameArgsCustom.add(args[i]);
}
instance.gameArgsCustom.addAll(args.after(1));
}
if (args.length >= 3 && "server".equals(args[1].toLowerCase(Locale.ROOT)))
if (args.length >= 2 && args.contains("server"))
ServerLauncher.launch(instanceDir, instance);
else ClientLauncher.launch(instanceDir, instance);
}

View File

@ -0,0 +1,79 @@
package io.gitlab.jfronny.inceptum.cli;
import io.gitlab.jfronny.inceptum.Inceptum;
import io.gitlab.jfronny.inceptum.model.OSType;
import io.gitlab.jfronny.inceptum.model.inceptum.CommandArguments;
import io.gitlab.jfronny.inceptum.model.inceptum.UpdateInfo;
import io.gitlab.jfronny.inceptum.util.JvmUtils;
import io.gitlab.jfronny.inceptum.util.OSCheck;
import io.gitlab.jfronny.inceptum.util.UpdateChecker;
import io.gitlab.jfronny.inceptum.util.Utils;
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.nio.file.Path;
public class UpdateCheckCommand extends Command {
public UpdateCheckCommand() {
super("Checks for updates, allows updating if an update is discovered via the \"install\" flag", "update");
}
@Override
public void invoke(CommandArguments args) {
if (args.contains("install") && OSCheck.OS == OSType.WINDOWS) {
System.err.println("Automatic update installation is not possible on windows");
return;
}
UpdateInfo updateUrl = getUpdate();
if (updateUrl == null) {
System.out.println("No update was found");
} else {
if (args.length > 1 && args.contains("install")) {
System.out.println("Installing from " + updateUrl);
try {
update(updateUrl, false);
} catch (IOException | URISyntaxException e) {
e.printStackTrace();
System.err.println("Could not download update");
}
}
else {
System.out.println("An update was found: " + updateUrl);
}
}
}
public static UpdateInfo getUpdate() {
return UpdateChecker.check(Inceptum.CONFIG.channel, Inceptum.VERSION.version, Inceptum.VERSION.flavor, channel -> {
Inceptum.LOGGER.error("No stable version was found, switching to experimental channel");
Inceptum.CONFIG.channel = channel;
Inceptum.saveConfig();
}, Inceptum.LOGGER::error, Inceptum.LOGGER::error);
}
public static void update(UpdateInfo source, boolean relaunch) throws IOException, URISyntaxException {
if (OSCheck.OS == OSType.WINDOWS) {
String path = UpdateCheckCommand.class.getProtectionDomain().getCodeSource().getLocation().getPath();
if (!path.endsWith(".jar")) throw new IOException("Inceptum is not running in a proper jar file");
Utils.downloadFile(source.url(), source.sha1(), Path.of(path));
}
else {
Utils.openWebBrowser(new URI(source.url()));
Runtime.getRuntime().addShutdownHook(new Thread(() -> {
try {
Runtime.getRuntime().exec(new String[] {
JvmUtils.getJvm(),
"-jar",
Inceptum.LIBRARIES_DIR.resolve("io/gitlab/jfronny/inceptum/Inceptum")
.resolve(source.newVersion().toString())
.resolve("Inceptum-" + source.newVersion() + '-' + OSCheck.OS.getMojName() + ".jar")
.toString()
});
} catch (IOException e) {
e.printStackTrace();
}
}));
}
}
}

View File

@ -0,0 +1,20 @@
package io.gitlab.jfronny.inceptum.gson;
import com.google.gson.*;
import io.gitlab.jfronny.inceptum.model.ComparableVersion;
import java.lang.reflect.Type;
public class ComparableVersionAdapter implements JsonDeserializer<ComparableVersion>, JsonSerializer<ComparableVersion> {
@Override
public ComparableVersion deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
if (json.isJsonPrimitive() && json.getAsJsonPrimitive().isString())
return new ComparableVersion(json.getAsString());
else throw new JsonParseException("Expected Version to be a string");
}
@Override
public JsonElement serialize(ComparableVersion src, Type typeOfSrc, JsonSerializationContext context) {
return new JsonPrimitive(src.toString());
}
}

View File

@ -2,7 +2,7 @@ package io.gitlab.jfronny.inceptum.gson;
import com.google.gson.*;
import io.gitlab.jfronny.inceptum.model.mojang.Rules;
import io.gitlab.jfronny.inceptum.util.Utils;
import io.gitlab.jfronny.inceptum.util.OSCheck;
import java.lang.reflect.Type;
@ -23,7 +23,7 @@ public class RulesDeserializer implements JsonDeserializer<Rules> {
if (ro.has("os")) {
JsonObject osObject = ro.get("os").getAsJsonObject();
if (osObject.has("name")) {
if (!Utils.OS.equals(osObject.get("name").getAsString())) {
if (!OSCheck.OS.getMojName().equals(osObject.get("name").getAsString())) {
matched = false;
}
}

View File

@ -0,0 +1,38 @@
package io.gitlab.jfronny.inceptum.model.inceptum;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Locale;
public class CommandArguments {
private final List<String> args;
public final int length;
public CommandArguments(String[] args) {
this.args = Arrays.asList(args).subList(1, args.length);
this.length = args.length;
}
public boolean contains(String param) {
return args.contains(param.replaceAll("^[-/]*", "").toLowerCase(Locale.ROOT));
}
public String last() {
return args.get(args.size() - 1);
}
public List<String> after(String param) {
List<String> yes = null;
for (String arg : args) {
if (yes != null)
yes.add(arg);
else if (arg.equals(param.replaceAll("^[-/]*", "").toLowerCase(Locale.ROOT)))
yes = new ArrayList<>();
}
return yes;
}
public List<String> after(int index) {
return index + 1 < length ? args.subList(index + 1, length) : new ArrayList<>();
}
}

View File

@ -1,7 +1,8 @@
package io.gitlab.jfronny.inceptum;
package io.gitlab.jfronny.inceptum.model.inceptum;
public class Config {
public boolean snapshots = false;
public boolean darkTheme = false;
public String lastAccount;
public UpdateChannel channel = UpdateChannel.Stable;
}

View File

@ -1,6 +1,8 @@
package io.gitlab.jfronny.inceptum.model.inceptum;
import io.gitlab.jfronny.inceptum.model.ComparableVersion;
public class InceptumVersion {
public String version;
public ComparableVersion version;
public String flavor;
}

View File

@ -33,7 +33,7 @@ public class ClientLauncher {
FabricMetaApi.addFabric(info, instance.getLoaderVersion(), FabricMetaApi.FabricVersionInfoType.Client);
}
args.add(Objects.requireNonNullElseGet(instance.java, () ->
Utils.getJvmMain(Inceptum.NATIVES_DIR
JvmUtils.getJvmMain(Inceptum.NATIVES_DIR
.resolve(info.javaVersion.component)
.resolve(Integer.toString(info.javaVersion.majorVersion)))
.toAbsolutePath().toString()));
@ -106,7 +106,7 @@ public class ClientLauncher {
// jvm args
.replace("${natives_directory}", Inceptum.NATIVES_DIR.resolve(instance.getMinecraftVersion()).toAbsolutePath().toString())
.replace("${launcher_name}", "Inceptum")
.replace("${launcher_version}", Inceptum.VERSION.version)
.replace("${launcher_version}", Inceptum.VERSION.version.toString())
.replace("${classpath}", classPath)
.replace("${user_properties}", "{}");
}

View File

@ -1,13 +1,14 @@
package io.gitlab.jfronny.inceptum.util;
import io.gitlab.jfronny.inceptum.Inceptum;
import io.gitlab.jfronny.inceptum.model.OSType;
import java.io.BufferedReader;
import java.io.InputStreamReader;
public class ProcessUtils {
public static boolean isProcessAlive(String pid) {
return isProcessIdRunning(pid, Utils.OS.equals("windows")
return isProcessIdRunning(pid, OSCheck.OS == OSType.WINDOWS
? "cmd /c tasklist /FI \"PID eq " + pid + "\""
: "ps -p " + pid);
}

View File

@ -33,7 +33,7 @@ public class ServerLauncher {
VersionInfo info = McApi.getVersionInfo(version);
info.libraries = new ArrayList<>();
args.add(Objects.requireNonNullElseGet(instance.java, () ->
Utils.getJvmMain(Inceptum.NATIVES_DIR
JvmUtils.getJvmMain(Inceptum.NATIVES_DIR
.resolve(info.javaVersion.component)
.resolve(Integer.toString(info.javaVersion.majorVersion)))
.toAbsolutePath().toString()));

View File

@ -1,6 +1,7 @@
package io.gitlab.jfronny.inceptum.util;
import io.gitlab.jfronny.inceptum.Inceptum;
import io.gitlab.jfronny.inceptum.model.OSType;
import java.awt.*;
import java.io.*;
@ -13,25 +14,11 @@ import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Arrays;
import java.util.Formatter;
import java.util.Locale;
import java.util.function.Function;
import java.util.regex.Pattern;
public class Utils {
public static final Pattern VALID_FILENAME = Pattern.compile("[a-zA-Z0-9_\\-.][a-zA-Z0-9 _\\-.]*[a-zA-Z0-9_\\-.]");
public static final String OS;
static {
String os = System.getProperty("os.name", "generic").toLowerCase(Locale.ROOT);
if ((os.contains("mac")) || (os.contains("darwin"))) {
OS = "osx";
} else if (os.contains("win")) {
OS = "windows";
} else if (os.contains("nux")) {
OS = "linux";
} else {
throw new RuntimeException("Unrecognized OS");
}
}
public static String hash(byte[] data) {
Formatter formatter = new Formatter();
@ -193,7 +180,7 @@ public class Utils {
public static void openWebBrowser(URI uri) {
try {
if (OS.equals("linux") && Utils.executableInPath("xdg-open")) {
if (OSCheck.OS == OSType.LINUX && JvmUtils.executableInPath("xdg-open")) {
Runtime.getRuntime().exec("xdg-open " + uri);
} else if (Desktop.isDesktopSupported() && Desktop.getDesktop().isSupported(Desktop.Action.BROWSE)) {
Desktop.getDesktop().browse(uri);
@ -203,24 +190,4 @@ public class Utils {
}
}
public static boolean executableInPath(String executableName) {
try {
return java.util.stream.Stream
.of(System.getenv("PATH").split(java.util.regex.Pattern.quote(File.pathSeparator)))
.map(path -> path.replace("\"", "")).map(Paths::get)
.anyMatch(path -> Files.exists(path.resolve(executableName))
&& Files.isExecutable(path.resolve(executableName)));
} catch (Exception e) {
return false;
}
}
public static Path getJvmMain(Path jvmDir) {
Path f = jvmDir.resolve("bin");
Path t = f.resolve("java");
if (!Files.exists(t)) t = f.resolve("javaw");
if (!Files.exists(t)) t = f.resolve("java.exe");
if (!Files.exists(t)) t = f.resolve("javaw.exe");
return t;
}
}

View File

@ -12,8 +12,8 @@ public class VersionInfoLibraryResolver {
Set<ArtifactInfo> artifacts = new LinkedHashSet<>();
for (VersionInfo.Library library : version.libraries) {
if (library.rules != null && !library.rules.allow()) continue;
if (library.downloads.classifiers != null && library.natives != null && library.natives.containsKey(Utils.OS)) {
artifacts.add(new ArtifactInfo(library.downloads.classifiers.get(library.natives.get(Utils.OS)), true));
if (library.downloads.classifiers != null && library.natives != null && library.natives.containsKey(OSCheck.OS.getMojName())) {
artifacts.add(new ArtifactInfo(library.downloads.classifiers.get(library.natives.get(OSCheck.OS.getMojName())), true));
}
if (library.downloads.artifact == null) {
Inceptum.LOGGER.info("Null library artifact @ " + library.name);

View File

@ -2,6 +2,7 @@ package io.gitlab.jfronny.inceptum.util.api;
import io.gitlab.jfronny.inceptum.Inceptum;
import io.gitlab.jfronny.inceptum.model.mojang.*;
import io.gitlab.jfronny.inceptum.util.OSCheck;
import io.gitlab.jfronny.inceptum.util.Utils;
import java.io.IOException;
@ -41,11 +42,10 @@ public class McApi {
public static Map<String, JvmFileInfo.File> getJvm(String component, int majorVersion) throws IOException {
// https://github.com/ATLauncher/ATLauncher/blob/master/src/main/java/com/atlauncher/constants/Constants.java#L116
JvmInfo info = Utils.downloadObject("https://launchermeta.mojang.com/v1/products/java-runtime/2ec0cc96c44e5a76b9c8b7c39df7210883d12871/all.json", JvmInfo.class);
JvmInfo.Jvms vms = switch (Utils.OS) {
case "windows" -> info.windowsX64;
case "linux" -> info.linux;
case "macos" -> info.macOs;
default -> throw new IOException("Invalid OS");
JvmInfo.Jvms vms = switch (OSCheck.OS) {
case WINDOWS -> info.windowsX64;
case LINUX -> info.linux;
case MAC_OS -> info.macOs;
};
List<JvmInfo.Jvms.Jvm> vmList = switch (component) {
case "java-runtime-alpha" -> vms.javaRuntimeAlpha;

View File

@ -7,6 +7,7 @@ import io.gitlab.jfronny.inceptum.Inceptum;
import io.gitlab.jfronny.inceptum.InceptumGui;
import io.gitlab.jfronny.inceptum.install.Steps;
import io.gitlab.jfronny.inceptum.model.inceptum.InstanceMeta;
import io.gitlab.jfronny.inceptum.util.JvmUtils;
import io.gitlab.jfronny.inceptum.util.Utils;
import io.gitlab.jfronny.inceptum.windows.control.InstanceManageControls;
@ -60,7 +61,7 @@ public class InstanceEditWindow extends Window {
}, () -> {});
if (ImGui.checkbox("Custom Java", customJava)) {
if (customJava.get()) {
instance.java = Utils.getJvmMain(Path.of(System.getProperty("java.home"))).toAbsolutePath().toString();
instance.java = JvmUtils.getJvm();
customJavaPath.set(instance.java);
} else {
instance.java = null;

74
wrapper/build.gradle Normal file
View File

@ -0,0 +1,74 @@
plugins {
id 'java'
id 'application'
id "de.undercouch.download" version "4.1.1"
id 'com.github.johnrengelman.shadow'
id "maven-publish"
}
application {
mainClass = 'io.gitlab.jfronny.inceptum.wrapper.Wrapper'
}
repositories {
mavenCentral()
}
dependencies {
implementation 'com.google.code.gson:gson:2.8.8'
}
class FileOutput extends DefaultTask {
@OutputFile
File output
}
def bootstrapVersion = "0.1.6"
def bootstrapArch = "i686"
task downloadBootstrap(type: Download) {
src "https://maven.fabricmc.net/net/fabricmc/fabric-installer-native-bootstrap/windows-${bootstrapArch}/${bootstrapVersion}/windows-${bootstrapArch}-${bootstrapVersion}.exe"
dest project.buildDir
}
task nativeExe(dependsOn: [downloadBootstrap, shadowJar], type: FileOutput) {
output = file("${projectDir}/build/libs/${archivesBaseName}-${project.version}.exe")
outputs.upToDateWhen { false }
doFirst {
output.delete()
}
doLast {
output.createNewFile()
output.setBytes downloadBootstrap.outputFiles.first().readBytes()
output.append shadowJar.archiveFile.get().getAsFile().readBytes()
}
}
if (flavor == 'windows') build.dependsOn nativeExe
publishing {
publications {
mavenJava(MavenPublication) {
artifact(shadowJar) {
builtBy shadowJar
}
}
}
if (project.parent.ext.isPublicMaven) {
repositories.maven {
url = "https://gitlab.com/api/v4/projects/30862253/packages/maven"
name = "gitlab"
credentials(HttpHeaderCredentials) {
name = "Job-Token"
value = System.getenv().CI_JOB_TOKEN
}
authentication {
header(HttpHeaderAuthentication)
}
}
}
repositories.mavenLocal()
}

View File

@ -0,0 +1,69 @@
package io.gitlab.jfronny.inceptum;
import io.gitlab.jfronny.inceptum.model.ComparableVersion;
import io.gitlab.jfronny.inceptum.model.inceptum.UpdateChannel;
import io.gitlab.jfronny.inceptum.model.inceptum.UpdateInfo;
import io.gitlab.jfronny.inceptum.util.JvmUtils;
import io.gitlab.jfronny.inceptum.util.OSCheck;
import io.gitlab.jfronny.inceptum.util.UpdateChecker;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.nio.file.Files;
import java.nio.file.Path;
public class Wrapper {
public static void main(String[] args) throws IOException {
String path = Wrapper.class.getProtectionDomain().getCodeSource().getLocation().getPath();
Path p;
if (path.endsWith(".jar")) {
p = Path.of(path).getParent();
} else {
System.out.println("Not running in a jar, using ./run");
p = Path.of("./run");
}
p = p.resolve("libraries/io/gitlab/jfronny/inceptum/Inceptum");
if (!Files.exists(p)) {
System.err.println("No Inceptum jar was identified");
UpdateInfo ui = UpdateChecker.check(UpdateChannel.Stable, new ComparableVersion("0"), OSCheck.OS.getMojName(), c -> {}, System.err::println, (s, e) -> {
e.printStackTrace();
System.err.println(s);
});
if (ui == null) {
System.err.println("Couldn't download");
return;
} else {
System.err.println("Downloading " + ui.url());
Path dir = p.resolve(ui.newVersion().toString());
Files.createDirectories(dir);
try (InputStream is = new URL(ui.url()).openStream()) {
Files.write(dir.resolve("Inceptum-" + ui.newVersion() + '-' + OSCheck.OS.getMojName() + ".jar"), is.readAllBytes());
}
catch (Throwable t) {
Files.delete(dir);
Files.delete(p);
}
}
}
Path pathChosen = null;
ComparableVersion chosenVer = new ComparableVersion("0");
for (Path path1 : Files.list(p).toList()) {
ComparableVersion cv = new ComparableVersion(path1.getFileName().toString());
if (cv.compareTo(chosenVer) > 0) {
chosenVer = cv;
pathChosen = path1;
}
}
if (pathChosen == null) {
Files.delete(p);
System.err.println("Something went wrong, please try again");
return;
}
Runtime.getRuntime().exec(new String[] {
JvmUtils.getJvm(),
"-jar",
pathChosen.resolve("Inceptum-" + chosenVer + '-' + OSCheck.OS.getMojName() + ".jar").toString()
});
}
}

View File

@ -0,0 +1,49 @@
package io.gitlab.jfronny.inceptum.model;
public class ComparableVersion implements Comparable<ComparableVersion> {
private final String string;
private final int[] numbers;
public ComparableVersion(String string) {
this.string = string;
this.numbers = new int[3];
String[] split = string.split("[.+]+");
for (int i = 0; i < split.length; i++) {
try {
numbers[i] = Integer.parseInt(split[i]);
} catch (NumberFormatException e) {
throw new IllegalArgumentException("Failed to parse version string.");
}
}
}
@Override
public String toString() {
return string;
}
@Override
public int compareTo(ComparableVersion version) {
final int maxLength = Math.max(numbers.length, version.numbers.length);
for (int i = 0; i < maxLength; i++) {
final int left = i < numbers.length ? numbers[i] : 0;
final int right = i < version.numbers.length ? version.numbers[i] : 0;
if (left != right) {
return left < right ? -1 : 1;
}
}
return 0;
}
@Override
public int hashCode() {
return string.hashCode();
}
@Override
public boolean equals(Object obj) {
return obj instanceof ComparableVersion cv && compareTo(cv) == 0;
}
}

View File

@ -0,0 +1,15 @@
package io.gitlab.jfronny.inceptum.model;
public enum OSType {
WINDOWS("windows"), MAC_OS("osx"), LINUX("linux");
private final String mojName;
OSType(String mojName) {
this.mojName = mojName;
}
public String getMojName() {
return mojName;
}
}

View File

@ -0,0 +1,86 @@
package io.gitlab.jfronny.inceptum.model.gitlab;
import java.util.Date;
public class GitlabProject {
public Long id;
public String description;
public String name;
public String name_with_namespace;
public String path;
public String path_with_namespace;
public String created_at;
public String default_branch;
public String ssh_url_to_repo;
public String http_url_to_repo;
public String web_url;
public String readme_url;
public String avatar_url;
public Integer forks_count;
public Integer star_count;
public Date last_activity_at;
public Boolean packages_enabled;
public Boolean empty_repo;
public Boolean archived;
public String visibility;
public GitlabUser owner;
public Boolean resolve_outdated_diff_discussions;
public Boolean issues_enabled;
public Boolean merge_requests_enabled;
public Boolean wiki_enabled;
public Boolean jobs_enabled;
public Boolean snippets_enabled;
public Boolean container_registry_enabled;
public Boolean service_desk_enabled;
public String service_desk_address;
public Boolean can_create_merge_request_in;
public String issues_access_level;
public String repository_access_level;
public String merge_request_access_level;
public String forking_access_level;
public String wiki_access_level;
public String builds_access_level;
public String snippets_access_level;
public String pages_access_level;
public String operations_access_level;
public String analytics_access_level;
public String container_registry_access_level;
public Boolean emails_disabled;
public Boolean shared_runners_enabled;
public Boolean lfs_enabled;
public Long creator_id;
public String import_status;
public Integer open_issues_count;
public String runners_token;
public Integer ci_default_git_depth;
public Boolean ci_forward_deployment_enabled;
public Boolean ci_job_token_scope_enabled;
public Boolean public_jobs;
public String build_git_strategy;
public Integer build_timeout;
public String auto_cancel_pending_pipelines;
public String build_coverage_regex;
public String ci_config_path;
public Boolean only_allow_merge_if_pipeline_succeeds;
public Boolean restrict_user_defined_variables;
public Boolean request_access_enabled;
public Boolean only_allow_merge_if_all_discussions_are_resolved;
public Boolean remove_source_branch_after_merge;
public Boolean printing_merge_request_link_enabled;
public String merge_method;
public String squash_option;
public String suggestion_commit_message;
public Boolean auto_devops_enabled;
public String auto_devops_strategy;
public Boolean autoclose_referenced_issues;
public Boolean keep_latest_artifact;
public Integer approvals_before_merge;
public Boolean mirror;
public String external_authorization_classification_label;
public Integer requirements_enabled;
public Integer security_and_compliance_enabled;
public String issues_template;
public String merge_requests_template;
public Boolean merge_pipelines_enabled;
public Boolean merge_trains_enabled;
}

View File

@ -0,0 +1,10 @@
package io.gitlab.jfronny.inceptum.model.gitlab;
public class GitlabUser {
public long id;
public String name;
public String username;
public String state;
public String avatar_url;
public String web_url;
}

View File

@ -0,0 +1,16 @@
package io.gitlab.jfronny.inceptum.model.gitlab;
import java.util.Date;
import java.util.List;
public class PackageFileInfo {
public Long id;
public Long package_id;
public Date created_at;
public String file_name;
public Integer size;
public String file_md5;
public String file_sha1;
public String file_sha256;
public List<Pipeline> pipelines;
}

View File

@ -0,0 +1,14 @@
package io.gitlab.jfronny.inceptum.model.gitlab;
import java.util.Date;
import java.util.List;
public class PackageInfo {
public Long id;
public String name;
public String version;
public String package_type;
public String status;
public Date created_at;
public List<Pipeline> pipelines;
}

View File

@ -0,0 +1,16 @@
package io.gitlab.jfronny.inceptum.model.gitlab;
import java.util.Date;
public class Pipeline {
public long id;
public long project_id;
public String sha;
public String ref;
public String status;
public String source;
public Date created_at;
public Date updated_at;
public String web_url;
public GitlabUser user;
}

View File

@ -0,0 +1,5 @@
package io.gitlab.jfronny.inceptum.model.inceptum;
public enum UpdateChannel {
Stable, CI
}

View File

@ -0,0 +1,6 @@
package io.gitlab.jfronny.inceptum.model.inceptum;
import io.gitlab.jfronny.inceptum.model.ComparableVersion;
public record UpdateInfo(String url, String sha1, ComparableVersion newVersion) {
}

View File

@ -0,0 +1,33 @@
package io.gitlab.jfronny.inceptum.util;
import java.io.File;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
public class JvmUtils {
public static boolean executableInPath(String executableName) {
try {
return java.util.stream.Stream
.of(System.getenv("PATH").split(java.util.regex.Pattern.quote(File.pathSeparator)))
.map(path -> path.replace("\"", "")).map(Paths::get)
.anyMatch(path -> Files.exists(path.resolve(executableName))
&& Files.isExecutable(path.resolve(executableName)));
} catch (Exception e) {
return false;
}
}
public static Path getJvmMain(Path jvmDir) {
Path f = jvmDir.resolve("bin");
Path t = f.resolve("java");
if (!Files.exists(t)) t = f.resolve("javaw");
if (!Files.exists(t)) t = f.resolve("java.exe");
if (!Files.exists(t)) t = f.resolve("javaw.exe");
return t;
}
public static String getJvm() {
return getJvmMain(Path.of(System.getProperty("java.home"))).toAbsolutePath().toString();
}
}

View File

@ -0,0 +1,22 @@
package io.gitlab.jfronny.inceptum.util;
import io.gitlab.jfronny.inceptum.model.OSType;
import java.util.Locale;
public class OSCheck {
public static final OSType OS;
static {
String os = System.getProperty("os.name", "generic").toLowerCase(Locale.ROOT);
if ((os.contains("mac")) || (os.contains("darwin"))) {
OS = OSType.MAC_OS;
} else if (os.contains("win")) {
OS = OSType.WINDOWS;
} else if (os.contains("nux")) {
OS = OSType.LINUX;
} else {
throw new RuntimeException("Unrecognized OS");
}
}
}

View File

@ -0,0 +1,58 @@
package io.gitlab.jfronny.inceptum.util;
import io.gitlab.jfronny.inceptum.model.ComparableVersion;
import io.gitlab.jfronny.inceptum.model.gitlab.GitlabProject;
import io.gitlab.jfronny.inceptum.model.gitlab.PackageFileInfo;
import io.gitlab.jfronny.inceptum.model.gitlab.PackageInfo;
import io.gitlab.jfronny.inceptum.model.gitlab.Pipeline;
import io.gitlab.jfronny.inceptum.model.inceptum.UpdateChannel;
import io.gitlab.jfronny.inceptum.model.inceptum.UpdateInfo;
import io.gitlab.jfronny.inceptum.util.api.GitlabApi;
import java.io.IOException;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
public class UpdateChecker {
private static final long PROJECT_ID = 30862253L;
public static UpdateInfo check(UpdateChannel channel, ComparableVersion current, String flavor, Consumer<UpdateChannel> channelInvalid, Consumer<String> error, BiConsumer<String, Exception> showError) {
try {
if (flavor.equals("custom")) return null;
GitlabProject project = GitlabApi.getProject(PROJECT_ID);
PackageInfo experimental = null;
PackageInfo stable = null;
packageIter: for (PackageInfo info : GitlabApi.getPackages(project)) {
if (info.status.equals("default"))
for (Pipeline pipeline : info.pipelines) {
if (pipeline.ref.equals("master") && pipeline.status.equals("success")) {
if (experimental == null) experimental = info;
if (!info.version.contains("+")) {
stable = info;
break packageIter;
}
}
}
}
if (experimental == null) {
throw new IOException("No version could be found");
}
else if (stable == null && channel == UpdateChannel.Stable) {
channel = UpdateChannel.CI;
channelInvalid.accept(channel);
}
PackageInfo info = switch (channel) {
case CI -> experimental;
case Stable -> stable;
};
if (current.compareTo(new ComparableVersion(info.version)) >= 0) return null;
PackageFileInfo file = GitlabApi.getFile(project, info, f -> f.file_name.endsWith('-' + flavor + ".jar"));
if (file == null)
error.accept("No valid package was discovered");
else return new UpdateInfo("https://gitlab.com/" + project.path_with_namespace + "/-/package_files/" + file.id + "/download", file.file_sha1, new ComparableVersion(info.version));
} catch (IOException e) {
showError.accept("Could not check for updates", e);
}
return null;
}
}

View File

@ -0,0 +1,48 @@
package io.gitlab.jfronny.inceptum.util.api;
import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;
import io.gitlab.jfronny.inceptum.model.gitlab.GitlabProject;
import io.gitlab.jfronny.inceptum.model.gitlab.PackageFileInfo;
import io.gitlab.jfronny.inceptum.model.gitlab.PackageInfo;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.lang.reflect.Type;
import java.net.URL;
import java.util.List;
import java.util.function.Predicate;
public class GitlabApi {
private static final Gson GSON = new Gson();
private static final Type packageInfoListType = new TypeToken<List<PackageInfo>>() {}.getType();
private static final Type packageFileInfoListType = new TypeToken<List<PackageFileInfo>>() {}.getType();
private static final Type gitlabProjectType = new TypeToken<GitlabProject>() {}.getType();
public static GitlabProject getProject(Long projectId) throws IOException {
return downloadObject("https://gitlab.com/api/v4/projects/" + projectId, gitlabProjectType);
}
public static List<PackageInfo> getPackages(GitlabProject project) throws IOException {
return downloadObject("https://gitlab.com/api/v4/projects/" + project.id + "/packages", packageInfoListType);
}
public static PackageFileInfo getFile(GitlabProject project, PackageInfo packageInfo, Predicate<PackageFileInfo> isValid) throws IOException {
int page = 0;
while (true) {
List<PackageFileInfo> files = downloadObject("https://gitlab.com/api/v4/projects/" + project.id + "/packages/" + packageInfo.id + "/package_files?per_page=100&page=" + ++page, packageFileInfoListType);
if (files.isEmpty()) return null;
for (PackageFileInfo file : files) {
if (isValid.test(file))
return file;
}
}
}
private static <T> T downloadObject(String source, Type type) throws IOException {
try (InputStream is = new URL(source).openStream(); InputStreamReader isr = new InputStreamReader(is)) {
return GSON.fromJson(isr, type);
}
}
}