Rework instance launching (removes support for all but the latest fabric loader versions)
This commit is contained in:
parent
7ac9cb1058
commit
c0607a7f19
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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}", "{}");
|
||||
}
|
||||
}
|
|
@ -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");
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
|
|
Loading…
Reference in New Issue