Rework instance launching (removes support for all but the latest fabric loader versions)

This commit is contained in:
Johannes Frohnmeyer 2021-12-08 21:45:46 +01:00
parent 7ac9cb1058
commit c0607a7f19
Signed by: Johannes
GPG Key ID: E76429612C2929F4
10 changed files with 271 additions and 295 deletions

View File

@ -3,16 +3,17 @@ package io.gitlab.jfronny.inceptum.cli;
import io.gitlab.jfronny.inceptum.Inceptum;
import io.gitlab.jfronny.inceptum.install.Steps;
import io.gitlab.jfronny.inceptum.model.inceptum.InstanceMeta;
import io.gitlab.jfronny.inceptum.util.ClientLauncher;
import io.gitlab.jfronny.inceptum.util.launch.ClientLauncher;
import io.gitlab.jfronny.inceptum.util.ProcessUtils;
import io.gitlab.jfronny.inceptum.util.ServerLauncher;
import io.gitlab.jfronny.inceptum.util.Utils;
import io.gitlab.jfronny.inceptum.util.api.account.AccountManager;
import io.gitlab.jfronny.inceptum.util.launch.InstanceLauncher;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Optional;
public class LaunchCommand extends Command {
public LaunchCommand() {
@ -70,15 +71,25 @@ public class LaunchCommand extends Command {
return;
}
if (args.length > 2) {
instance.gameArgsCustom = instance.gameArgsCustom == null ? new ArrayList<>() : new ArrayList<>(instance.gameArgsCustom);
instance.gameArgsCustom.addAll(args.after(pArgIndex));
if (instance.arguments == null) instance.arguments = new InstanceMeta.Arguments();
instance.arguments.client = instance.arguments.client == null ? new ArrayList<>() : new ArrayList<>(instance.arguments.client);
instance.arguments.server = instance.arguments.server == null ? new ArrayList<>() : new ArrayList<>(instance.arguments.server);
instance.arguments.jvm = instance.arguments.jvm == null ? new ArrayList<>() : new ArrayList<>(instance.arguments.jvm);
instance.arguments.client.addAll(args.after(pArgIndex));
instance.arguments.server.addAll(args.after(pArgIndex));
}
try {
Steps.reDownload(instanceDir);
} catch (IOException e) {
e.printStackTrace();
}
if (server) ServerLauncher.launch(instanceDir, instance, restart);
if (server) {
try {
InstanceLauncher.launch(instanceDir, instance, InstanceLauncher.LaunchType.Server, restart, Optional.empty());
} catch (InstanceLauncher.LaunchException | IOException e) {
Inceptum.showError("Could not launch instance", e);
}
}
else {
AccountManager.loadAccounts();
ClientLauncher.launch(instanceDir, instance);

View File

@ -24,15 +24,11 @@ public class DownloadClientStep implements Step {
public void execute(SetupStepInfo info, AtomicBoolean stopThread) throws IOException {
Path clientPath = Inceptum.LIBRARIES_DIR.resolve("net/minecraft/client");
Path serverPath = Inceptum.LIBRARIES_DIR.resolve("net/minecraft/server");
Path installerPath = Inceptum.LIBRARIES_DIR.resolve("net/fabricmc/fabric-installer");
if (!Files.exists(clientPath)) Files.createDirectories(clientPath);
if (!Files.exists(serverPath)) Files.createDirectories(serverPath);
if (!Files.exists(installerPath)) Files.createDirectories(installerPath);
String minecraftVersion = InstanceMeta.getMinecraftVersion(info.version().id);
clientPath = clientPath.resolve(minecraftVersion + ".jar");
serverPath = serverPath.resolve(minecraftVersion + ".jar");
FabricInstallerVersion installerVersion = FabricMetaApi.getInstallerVersion();
installerPath = installerPath.resolve("fabric-installer-" + installerVersion.version + "-" + info.loader().version() + "-" + minecraftVersion + ".jar");
if (!Files.exists(clientPath)) {
MojangFileDownload client = info.version().downloads.client;
info.setState("Downloading Client");
@ -43,19 +39,5 @@ public class DownloadClientStep implements Step {
info.setState("Downloading Server");
Utils.downloadFile(client.url, client.sha1, serverPath);
}
//TODO simplify once https://github.com/FabricMC/fabric-meta/pull/16 is merged
if (!Files.exists(installerPath) && info.loader().type() == LoaderInfo.Type.Fabric) {
info.setState("Downloading Fabric Installer");
Utils.downloadFile(installerVersion.url.replace(".jar", "-server.jar"), installerPath);
try (FileSystem fs = Utils.openZipFile(installerPath, true)) {
Files.writeString(fs.getPath("install.properties"),
String.format("fabric-loader-version=%s\ngame-version=%s", info.loader().version(), minecraftVersion)
);
}
catch (Throwable t) {
Files.delete(installerPath);
throw new IOException("Could not create fabric installer", t);
}
}
}
}

View File

@ -1,8 +0,0 @@
package io.gitlab.jfronny.inceptum.model.fabric;
public class FabricInstallerVersion {
public String url;
public String maven;
public String version;
public boolean stable;
}

View File

@ -10,8 +10,7 @@ public class InstanceMeta {
public String java;
public Long minMem;
public Long maxMem;
public List<String> jvmArgsCustom; //TODO allow configuring in UI
public List<String> gameArgsCustom; //TODO allow configuring in UI
public Arguments arguments; //TODO allow configuring in UI
public boolean isFabric() {
return version.startsWith(floaderPrefix);
@ -28,4 +27,10 @@ public class InstanceMeta {
public String getLoaderVersion() {
return isFabric() ? version.substring(floaderPrefix.length()).split("-")[0] : "";
}
public static class Arguments {
public List<String> jvm;
public List<String> client;
public List<String> server;
}
}

View File

@ -1,141 +0,0 @@
package io.gitlab.jfronny.inceptum.util;
import io.gitlab.jfronny.inceptum.Inceptum;
import io.gitlab.jfronny.inceptum.install.steps.DownloadLibrariesStep;
import io.gitlab.jfronny.inceptum.model.inceptum.ArtifactInfo;
import io.gitlab.jfronny.inceptum.model.inceptum.InstanceMeta;
import io.gitlab.jfronny.inceptum.model.mojang.MinecraftArgument;
import io.gitlab.jfronny.inceptum.model.mojang.VersionInfo;
import io.gitlab.jfronny.inceptum.model.mojang.VersionsListInfo;
import io.gitlab.jfronny.inceptum.util.api.FabricMetaApi;
import io.gitlab.jfronny.inceptum.util.api.McApi;
import io.gitlab.jfronny.inceptum.util.api.account.AccountManager;
import io.gitlab.jfronny.inceptum.util.api.account.AuthInfo;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicReference;
public class ClientLauncher {
public static void launch(Path path, InstanceMeta instance) {
if (AccountManager.accountMissing() && Inceptum.CONFIG.enforceAccount) {
Inceptum.showError("You have not set up an account.\nDoing so is required to play Minecraft", "Not authenticated");
return;
}
AuthInfo authInfo = AccountManager.getSelectedAccount();
if (authInfo.equals(AccountManager.NULL_AUTH)) {
try {
String sysUser = System.getProperty("user.name");
if (Inceptum.CONFIG.offlineAccountLastName == null)
Inceptum.CONFIG.offlineAccountLastName = sysUser;
Inceptum.getInput("User name", Inceptum.CONFIG.offlineAccountLastName, name -> {
Inceptum.CONFIG.offlineAccountLastName = name.equals(sysUser) ? null : name;
Inceptum.saveConfig();
AuthInfo infoNew = new AuthInfo(name, authInfo.uuid(), authInfo.accessToken(), authInfo.userType());
launchI(path, instance, infoNew);
}, () -> {});
} catch (IOException e) {
Inceptum.showError("Failed to request input", e);
}
}
else launchI(path, instance, authInfo);
}
private static void launchI(Path path, InstanceMeta instance, AuthInfo authInfo) {
boolean found = false;
for (VersionsListInfo version : McApi.getVersions().versions) {
if (version.id.equals(instance.getMinecraftVersion())) {
found = true;
try {
List<String> args = new ArrayList<>();
VersionInfo info = McApi.getVersionInfo(version);
if (instance.isFabric()) {
FabricMetaApi.addFabric(info, instance.getLoaderVersion(), FabricMetaApi.FabricVersionInfoType.Client);
}
args.add(Objects.requireNonNullElseGet(instance.java, () ->
JvmUtils.getJvmMain(Inceptum.NATIVES_DIR
.resolve(info.javaVersion.component)
.resolve(Integer.toString(info.javaVersion.majorVersion)))
.toAbsolutePath().toString()));
DownloadLibrariesStep.execute(info, new AtomicBoolean(false), new AtomicReference<>());
StringBuilder classPath = new StringBuilder();
for (ArtifactInfo artifact : VersionInfoLibraryResolver.getRelevant(info)) {
classPath.append(Inceptum.LIBRARIES_DIR.resolve(artifact.path).toAbsolutePath());
classPath.append(File.pathSeparatorChar);
}
classPath.append(Inceptum.LIBRARIES_DIR.resolve("net/minecraft/client").resolve(version.id + ".jar").toAbsolutePath());
if (info.arguments != null) args.addAll(parse(info.arguments.jvm, info, instance, classPath.toString(), path.toAbsolutePath().toString(), authInfo));
if (instance.minMem != null) args.add("-Xms" + instance.minMem);
if (instance.maxMem != null) args.add("-Xmx" + instance.maxMem);
if (instance.jvmArgsCustom != null) args.addAll(instance.jvmArgsCustom);
if (info.minecraftArguments != null) {
args.add("-cp");
args.add(classPath.toString());
args.add("-Djava.library.path=" + Inceptum.NATIVES_DIR.resolve(instance.getMinecraftVersion()).toAbsolutePath());
}
args.add(info.mainClass);
if (info.arguments != null) args.addAll(parse(info.arguments.game, info, instance, classPath.toString(), path.toAbsolutePath().toString(), authInfo));
else if (info.minecraftArguments != null) {
for (String s : info.minecraftArguments.split(" ")) {
args.add(expandArg(s, info, instance, classPath.toString(), path.toAbsolutePath().toString(), authInfo));
}
}
else {
Inceptum.LOGGER.error("Could not launch: No valid argument source found");
return;
}
if (instance.gameArgsCustom != null) args.addAll(instance.gameArgsCustom);
Inceptum.LOGGER.info(String.join(" ", args));
ProcessBuilder pb = new ProcessBuilder(args.toArray(new String[0]));
pb.directory(path.toFile());
pb.redirectOutput(ProcessBuilder.Redirect.INHERIT);
pb.redirectError(ProcessBuilder.Redirect.INHERIT);
Files.writeString(path.resolve("inceptum.lock"), Long.toString(pb.start().pid()));
} catch (IOException e) {
Inceptum.showError("Could not launch client", e);
}
}
}
if (!found) {
Inceptum.showError("The version number specified for this instance could not be matched with a known version", "Could not launch client");
}
}
private static List<String> parse(List<MinecraftArgument> arguments, VersionInfo info, InstanceMeta instance, String classPath, String gameDirectory, AuthInfo authInfo) {
List<String> res = new ArrayList<>();
for (MinecraftArgument argument : arguments) {
for (String s : argument.arg()) {
res.add(expandArg(s, info, instance, classPath, gameDirectory, authInfo));
}
}
return res;
}
private static String expandArg(String arg, VersionInfo info, InstanceMeta instance, String classPath, String gameDirectory, AuthInfo authInfo) {
return arg
// game args
.replace("${auth_player_name}", authInfo.name())
.replace("${version_name}", instance.getMinecraftVersion())
.replace("${game_directory}", gameDirectory)
.replace("${assets_root}", Inceptum.ASSETS_DIR.toAbsolutePath().toString())
.replace("${assets_index_name}", info.assets)
.replace("${auth_uuid}", authInfo.uuid())
.replace("${auth_access_token}", authInfo.accessToken())
.replace("${user_type}", authInfo.userType())
.replace("${version_type}", info.type)
.replace("${resolution_width}", "1920") //TODO has_custom_resolution
.replace("${resolution_height}", "1080") //TODO has_custom_resolution
// jvm args
.replace("${natives_directory}", Inceptum.NATIVES_DIR.resolve(instance.getMinecraftVersion()).toAbsolutePath().toString())
.replace("${launcher_name}", "Inceptum")
.replace("${launcher_version}", MetaHolder.VERSION.version.toString())
.replace("${classpath}", classPath)
.replace("${user_properties}", "{}");
}
}

View File

@ -1,107 +0,0 @@
package io.gitlab.jfronny.inceptum.util;
import io.gitlab.jfronny.inceptum.Inceptum;
import io.gitlab.jfronny.inceptum.install.steps.DownloadLibrariesStep;
import io.gitlab.jfronny.inceptum.model.inceptum.ArtifactInfo;
import io.gitlab.jfronny.inceptum.model.inceptum.InstanceMeta;
import io.gitlab.jfronny.inceptum.model.mojang.VersionInfo;
import io.gitlab.jfronny.inceptum.model.mojang.VersionsListInfo;
import io.gitlab.jfronny.inceptum.util.api.FabricMetaApi;
import io.gitlab.jfronny.inceptum.util.api.McApi;
import java.io.File;
import java.io.IOException;
import java.net.URISyntaxException;
import java.nio.file.FileSystem;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicReference;
public class ServerLauncher {
public static void launch(Path path, InstanceMeta instance, boolean restart) {
boolean found = false;
for (VersionsListInfo version : McApi.getVersions().versions) {
if (version.id.equals(instance.getMinecraftVersion())) {
found = true;
try {
List<String> args = new ArrayList<>();
VersionInfo info = McApi.getVersionInfo(version);
info.libraries = new ArrayList<>();
args.add(Objects.requireNonNullElseGet(instance.java, () ->
JvmUtils.getJvmMain(Inceptum.NATIVES_DIR
.resolve(info.javaVersion.component)
.resolve(Integer.toString(info.javaVersion.majorVersion)))
.toAbsolutePath().toString()));
DownloadLibrariesStep.execute(info, new AtomicBoolean(false), new AtomicReference<>());
StringBuilder classPath = new StringBuilder();
for (ArtifactInfo artifact : VersionInfoLibraryResolver.getRelevant(info)) {
classPath.append(Inceptum.LIBRARIES_DIR.resolve(artifact.path).toAbsolutePath());
classPath.append(File.pathSeparatorChar);
}
Path serverJar = instance.isFabric()
? Inceptum.LIBRARIES_DIR.resolve("net/fabricmc/fabric-installer").resolve("fabric-installer-" + FabricMetaApi.getInstallerVersion().version + "-" + instance.getLoaderVersion() + "-" + version.id + ".jar")
: Inceptum.LIBRARIES_DIR.resolve("net/minecraft/server").resolve(version.id + ".jar");
classPath.append(serverJar.toAbsolutePath());
if (instance.minMem != null) args.add("-Xms" + instance.minMem);
if (instance.maxMem != null) args.add("-Xmx" + instance.maxMem);
if (instance.jvmArgsCustom != null) args.addAll(instance.jvmArgsCustom);
args.add("-cp");
args.add(classPath.toString());
String mainClass = null;
final String linePrefix = "Main-Class: ";
try (FileSystem fs = Utils.openZipFile(serverJar, false)) {
for (String line : Files.readAllLines(fs.getPath("META-INF/MANIFEST.MF"))) {
if (line.startsWith(linePrefix)) {
mainClass = line.substring(linePrefix.length());
}
}
}
if (mainClass == null) throw new IOException("Could not identify entrypoint");
args.add(mainClass);
if (instance.gameArgsCustom != null) args.addAll(instance.gameArgsCustom);
Inceptum.LOGGER.info(String.join(" ", args));
ProcessBuilder pb = new ProcessBuilder(args.toArray(new String[0]));
pb.directory(path.toFile());
pb.redirectOutput(ProcessBuilder.Redirect.INHERIT);
pb.redirectError(ProcessBuilder.Redirect.INHERIT);
AtomicReference<Process> proc = new AtomicReference<>();
Runnable starterRunner = () -> {
try {
proc.set(pb.start());
Files.writeString(path.resolve("inceptum.lock"), Long.toString(proc.get().pid()));
} catch (IOException e) {
Inceptum.LOGGER.error("Could not start server", e);
}
};
starterRunner.run();
if (restart) {
new Thread(() -> {
while (true) {
try {
proc.get().waitFor();
} catch (InterruptedException e) {
Inceptum.LOGGER.error("Could not wait for server to finish", e);
}
Inceptum.LOGGER.info("Restarting server");
starterRunner.run();
if (!proc.get().isAlive()) {
Inceptum.LOGGER.error("Could not restart server");
return;
}
}
}).start();
}
} catch (IOException | URISyntaxException e) {
Inceptum.showError("Could not launch server", e);
}
}
}
if (!found) {
Inceptum.showError("The version number specified for this instance could not be matched with a known version", "Could not launch server");
}
}
}

View File

@ -1,7 +1,6 @@
package io.gitlab.jfronny.inceptum.util.api;
import com.google.gson.reflect.TypeToken;
import io.gitlab.jfronny.inceptum.model.fabric.FabricInstallerVersion;
import io.gitlab.jfronny.inceptum.model.fabric.FabricVersionLoaderInfo;
import io.gitlab.jfronny.inceptum.model.inceptum.InstanceMeta;
import io.gitlab.jfronny.inceptum.model.mojang.Rules;
@ -9,7 +8,6 @@ import io.gitlab.jfronny.inceptum.model.mojang.VersionInfo;
import io.gitlab.jfronny.inceptum.model.mojang.VersionsListInfo;
import io.gitlab.jfronny.inceptum.util.Utils;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.lang.reflect.Type;
import java.util.ArrayList;
@ -18,7 +16,6 @@ import java.util.List;
public class FabricMetaApi {
private static final Type fabricLoaderVersionListType = new TypeToken<List<FabricVersionLoaderInfo>>() {}.getType();
private static final Type fabricInstallerVersionListType = new TypeToken<List<FabricInstallerVersion>>() {}.getType();
private static final String META_URL = "https://meta.fabricmc.net/";
public static List<FabricVersionLoaderInfo> getLoaderVersions(VersionsListInfo version) {
@ -55,20 +52,11 @@ public class FabricMetaApi {
floader.url = "https://maven.fabricmc.net/";
libs.add(convertLib(floader));
floader.name = ver.intermediary.maven;
libs.add(convertLib(floader));//https://maven.fabricmc.net/net/fabricmc/fabric-installer/0.8.3/fabric-installer-0.8.3-server.jar
libs.add(convertLib(floader));
version.libraries = List.copyOf(libs);
version.id = InstanceMeta.floaderPrefix + fabricVersion + "-" + version.id;
}
public static FabricInstallerVersion getInstallerVersion() throws IOException {
List<FabricInstallerVersion> installerVersions = Utils.downloadObject(META_URL + "v2/versions/installer", fabricInstallerVersionListType);
for (FabricInstallerVersion installerVersion : installerVersions) {
if (installerVersion.stable)
return installerVersion;
}
throw new FileNotFoundException("No stable installer was found");
}
private static VersionInfo.Library convertLib(FabricVersionLoaderInfo.WithMeta.LauncherMeta.Libraries.Library library) {
VersionInfo.Library res = new VersionInfo.Library();
res.name = library.name;

View File

@ -0,0 +1,44 @@
package io.gitlab.jfronny.inceptum.util.launch;
import io.gitlab.jfronny.inceptum.Inceptum;
import io.gitlab.jfronny.inceptum.model.inceptum.InstanceMeta;
import io.gitlab.jfronny.inceptum.util.api.account.AccountManager;
import io.gitlab.jfronny.inceptum.util.api.account.AuthInfo;
import java.io.IOException;
import java.nio.file.Path;
import java.util.Optional;
public class ClientLauncher {
public static void launch(Path path, InstanceMeta instance) {
if (AccountManager.accountMissing() && Inceptum.CONFIG.enforceAccount) {
Inceptum.showError("You have not set up an account.\nDoing so is required to play Minecraft", "Not authenticated");
return;
}
AuthInfo authInfo = AccountManager.getSelectedAccount();
if (authInfo.equals(AccountManager.NULL_AUTH)) {
try {
String sysUser = System.getProperty("user.name");
if (Inceptum.CONFIG.offlineAccountLastName == null)
Inceptum.CONFIG.offlineAccountLastName = sysUser;
Inceptum.getInput("User name", Inceptum.CONFIG.offlineAccountLastName, name -> {
Inceptum.CONFIG.offlineAccountLastName = name.equals(sysUser) ? null : name;
Inceptum.saveConfig();
AuthInfo infoNew = new AuthInfo(name, authInfo.uuid(), authInfo.accessToken(), authInfo.userType());
launchI(path, instance, infoNew);
}, () -> {});
} catch (IOException e) {
Inceptum.showError("Failed to request input", e);
}
}
else launchI(path, instance, authInfo);
}
private static void launchI(Path path, InstanceMeta instance, AuthInfo authInfo) {
try {
InstanceLauncher.launch(path, instance, InstanceLauncher.LaunchType.Client, false, Optional.of(authInfo));
} catch (InstanceLauncher.LaunchException | IOException e) {
Inceptum.showError("Could not launch client", e);
}
}
}

View File

@ -0,0 +1,202 @@
package io.gitlab.jfronny.inceptum.util.launch;
import io.gitlab.jfronny.inceptum.Inceptum;
import io.gitlab.jfronny.inceptum.install.steps.DownloadLibrariesStep;
import io.gitlab.jfronny.inceptum.model.inceptum.ArtifactInfo;
import io.gitlab.jfronny.inceptum.model.inceptum.InstanceMeta;
import io.gitlab.jfronny.inceptum.model.mojang.MinecraftArgument;
import io.gitlab.jfronny.inceptum.model.mojang.VersionInfo;
import io.gitlab.jfronny.inceptum.model.mojang.VersionsListInfo;
import io.gitlab.jfronny.inceptum.util.JvmUtils;
import io.gitlab.jfronny.inceptum.util.MetaHolder;
import io.gitlab.jfronny.inceptum.util.Utils;
import io.gitlab.jfronny.inceptum.util.VersionInfoLibraryResolver;
import io.gitlab.jfronny.inceptum.util.api.FabricMetaApi;
import io.gitlab.jfronny.inceptum.util.api.McApi;
import io.gitlab.jfronny.inceptum.util.api.account.AccountManager;
import io.gitlab.jfronny.inceptum.util.api.account.AuthInfo;
import java.io.File;
import java.io.IOException;
import java.net.URISyntaxException;
import java.nio.file.FileSystem;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.*;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicReference;
public class InstanceLauncher {
public static void launch(Path instancePath, InstanceMeta instance, LaunchType launchType, boolean restart, Optional<AuthInfo> authInfo) throws LaunchException, IOException {
if (authInfo.isEmpty()) authInfo = Optional.of(AccountManager.NULL_AUTH);
VersionsListInfo versionDataSimple = getVersion(instance.getMinecraftVersion());
VersionInfo versionInfo = McApi.getVersionInfo(versionDataSimple);
// Add fabric metadata if using fabric
if (instance.isFabric()) {
FabricMetaApi.addFabric(versionInfo, instance.getLoaderVersion(), launchType.fabricMetaType);
}
// Ensure libs/assets are present
DownloadLibrariesStep.execute(versionInfo, new AtomicBoolean(false), new AtomicReference<>());
// Prepare arguments
List<String> args = new LinkedList<>();
// JVM path
args.add(Objects.requireNonNullElseGet(instance.java, () ->
JvmUtils.getJvmMain(Inceptum.NATIVES_DIR
.resolve(versionInfo.javaVersion.component)
.resolve(Integer.toString(versionInfo.javaVersion.majorVersion)))
.toAbsolutePath().toString()));
// Java classpath
StringBuilder classPath = new StringBuilder();
for (ArtifactInfo artifact : VersionInfoLibraryResolver.getRelevant(versionInfo)) {
classPath.append(Inceptum.LIBRARIES_DIR.resolve(artifact.path).toAbsolutePath());
classPath.append(File.pathSeparatorChar);
}
Path gameJar = Inceptum.LIBRARIES_DIR.resolve("net/minecraft/" + launchType.name).resolve(versionDataSimple.id + ".jar").toAbsolutePath();
classPath.append(gameJar);
// JVM arguments
if (launchType == LaunchType.Client && versionInfo.arguments != null) args.addAll(parse(versionInfo.arguments.jvm, versionInfo, instance, classPath.toString(), instancePath.toAbsolutePath().toString(), authInfo.get()));
if (instance.minMem != null) args.add("-Xms" + instance.minMem);
if (instance.maxMem != null) args.add("-Xmx" + instance.maxMem);
if (instance.arguments != null && instance.arguments.jvm != null) args.addAll(instance.arguments.jvm);
// Add classpath to args
args.add("-cp");
args.add(classPath.toString());
// Native library path
args.add("-Djava.library.path=" + Inceptum.NATIVES_DIR.resolve(instance.getMinecraftVersion()).toAbsolutePath());
// Main class
args.add(resolveMainClass(instance, versionInfo, gameJar, launchType));
// Game arguments
if (launchType == LaunchType.Client) {
if (versionInfo.arguments != null) args.addAll(parse(versionInfo.arguments.game, versionInfo, instance, classPath.toString(), instancePath.toAbsolutePath().toString(), authInfo.get()));
else if (versionInfo.minecraftArguments != null) {
for (String s : versionInfo.minecraftArguments.split(" ")) {
args.add(expandArg(s, versionInfo, instance, classPath.toString(), instancePath.toAbsolutePath().toString(), authInfo.get()));
}
}
else throw new LaunchException("Could not launch: No valid source for client arguments found");
}
if (instance.arguments != null) {
switch (launchType) {
case Client -> {
if (instance.arguments.client != null)
args.addAll(instance.arguments.client);
}
case Server -> {
if (instance.arguments.server != null)
args.addAll(instance.arguments.server);
}
}
}
// Log command used to start
Inceptum.LOGGER.info(String.join(" ", args));
// Create process
ProcessBuilder pb = new ProcessBuilder(args.toArray(new String[0]));
pb.directory(instancePath.toFile());
pb.redirectOutput(ProcessBuilder.Redirect.INHERIT);
pb.redirectError(ProcessBuilder.Redirect.INHERIT);
AtomicReference<Process> proc = new AtomicReference<>();
Runnable starterRunner = () -> {
try {
proc.set(pb.start());
Files.writeString(instancePath.resolve("inceptum.lock"), Long.toString(proc.get().pid()));
} catch (IOException e) {
Inceptum.LOGGER.error("Could not start " + launchType.name, e);
}
};
starterRunner.run();
if (restart) {
new Thread(() -> {
while (true) {
try {
proc.get().waitFor();
} catch (InterruptedException e) {
Inceptum.LOGGER.error("Could not wait for server to finish", e);
}
Inceptum.LOGGER.info("Restarting server");
starterRunner.run();
if (!proc.get().isAlive()) {
Inceptum.LOGGER.error("Could not restart server");
return;
}
}
}).start();
}
}
private static String resolveMainClass(InstanceMeta instance, VersionInfo versionInfo, Path gameJar, LaunchType launchType) throws LaunchException {
if (launchType == LaunchType.Client || instance.isFabric()) return versionInfo.mainClass;
// Identify main class using MANIFEST.MF
final String linePrefix = "Main-Class: ";
try (FileSystem fs = Utils.openZipFile(gameJar, false)) {
for (String line : Files.readAllLines(fs.getPath("META-INF/MANIFEST.MF"))) {
if (line.startsWith(linePrefix)) {
return line.substring(linePrefix.length());
}
}
} catch (IOException | URISyntaxException e) {
throw new LaunchException("IO Exception while trying to identify entrypoint", e);
}
throw new LaunchException("Could not identify entrypoint");
}
private static VersionsListInfo getVersion(String minecraftVersion) throws LaunchException {
for (VersionsListInfo version : McApi.getVersions().versions) {
if (version.id.equals(minecraftVersion))
return version;
}
throw new LaunchException("Could not find data for minecraft version: " + minecraftVersion);
}
private static List<String> parse(List<MinecraftArgument> arguments, VersionInfo info, InstanceMeta instance, String classPath, String gameDirectory, AuthInfo authInfo) {
List<String> res = new ArrayList<>();
for (MinecraftArgument argument : arguments) {
for (String s : argument.arg()) {
res.add(expandArg(s, info, instance, classPath, gameDirectory, authInfo));
}
}
return res;
}
private static String expandArg(String arg, VersionInfo info, InstanceMeta instance, String classPath, String gameDirectory, AuthInfo authInfo) {
return arg
// game args
.replace("${auth_player_name}", authInfo.name())
.replace("${version_name}", instance.getMinecraftVersion())
.replace("${game_directory}", gameDirectory)
.replace("${assets_root}", Inceptum.ASSETS_DIR.toAbsolutePath().toString())
.replace("${assets_index_name}", info.assets)
.replace("${auth_uuid}", authInfo.uuid())
.replace("${auth_access_token}", authInfo.accessToken())
.replace("${user_type}", authInfo.userType())
.replace("${version_type}", info.type)
.replace("${resolution_width}", "1920") //TODO has_custom_resolution
.replace("${resolution_height}", "1080") //TODO has_custom_resolution
// jvm args
.replace("${natives_directory}", Inceptum.NATIVES_DIR.resolve(instance.getMinecraftVersion()).toAbsolutePath().toString())
.replace("${launcher_name}", "Inceptum")
.replace("${launcher_version}", MetaHolder.VERSION.version.toString())
.replace("${classpath}", classPath)
.replace("${user_properties}", "{}");
}
public enum LaunchType {
Server("server", FabricMetaApi.FabricVersionInfoType.Server), Client("client", FabricMetaApi.FabricVersionInfoType.Client);
LaunchType(String name, FabricMetaApi.FabricVersionInfoType fabricMetaType) {
this.name = name;
this.fabricMetaType = fabricMetaType;
}
public final String name;
public final FabricMetaApi.FabricVersionInfoType fabricMetaType;
}
public static class LaunchException extends Exception {
public LaunchException(String message) {
super(message);
}
public LaunchException(String message, Throwable cause) {
super(message, cause);
}
}
}

View File

@ -6,7 +6,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.ClientLauncher;
import io.gitlab.jfronny.inceptum.util.launch.ClientLauncher;
import io.gitlab.jfronny.inceptum.util.ProcessUtils;
import io.gitlab.jfronny.inceptum.util.Utils;
import io.gitlab.jfronny.inceptum.windows.InstanceEditWindow;