From c0f82b1ae7661a7e70ec4e26617a222a653eb49a Mon Sep 17 00:00:00 2001 From: huanghongxun Date: Sun, 19 Sep 2021 02:44:51 +0800 Subject: [PATCH] feat: env vars for pre-launch command and post-exit command. --- .../hmcl/game/HMCLGameRepository.java | 3 +- .../hmcl/setting/VersionSetting.java | 21 +++++++ .../jackhuang/hmcl/game/LaunchOptions.java | 19 +++++- .../hmcl/launch/DefaultLauncher.java | 58 +++++++++++++++---- 4 files changed, 87 insertions(+), 14 deletions(-) diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/game/HMCLGameRepository.java b/HMCL/src/main/java/org/jackhuang/hmcl/game/HMCLGameRepository.java index f9b122f7d..5af5a01ef 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/game/HMCLGameRepository.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/game/HMCLGameRepository.java @@ -307,7 +307,8 @@ public class HMCLGameRepository extends DefaultGameRepository { .setFullscreen(vs.isFullscreen()) .setServerIp(vs.getServerIp()) .setWrapper(vs.getWrapper()) - .setPrecalledCommand(vs.getPreLaunchCommand()) + .setPreLaunchCommand(vs.getPreLaunchCommand()) + .setPostExitCommand(vs.getPostExitCommand()) .setNoGeneratedJVMArgs(vs.isNoJVMArgs()) .setNativesDirType(vs.getNativesDirType()) .setNativesDir(vs.getNativesDir()) diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/setting/VersionSetting.java b/HMCL/src/main/java/org/jackhuang/hmcl/setting/VersionSetting.java index c457a7f59..a33ae1f75 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/setting/VersionSetting.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/setting/VersionSetting.java @@ -269,6 +269,24 @@ public final class VersionSetting implements Cloneable { preLaunchCommandProperty.set(preLaunchCommand); } + private final StringProperty postExitCommand = new SimpleStringProperty(this, "postExitCommand", ""); + + public StringProperty postExitCommandProperty() { + return postExitCommand; + } + + /** + * The command that will be executed after game exits. + * Operating system relevant. + */ + public String getPostExitCommand() { + return postExitCommand.get(); + } + + public void setPostExitCommand(String postExitCommand) { + this.postExitCommand.set(postExitCommand); + } + // options private final StringProperty javaArgsProperty = new SimpleStringProperty(this, "javaArgs", ""); @@ -602,6 +620,7 @@ public final class VersionSetting implements Cloneable { minMemoryProperty.addListener(listener); autoMemory.addListener(listener); preLaunchCommandProperty.addListener(listener); + postExitCommand.addListener(listener); javaArgsProperty.addListener(listener); minecraftArgsProperty.addListener(listener); noJVMArgsProperty.addListener(listener); @@ -636,6 +655,7 @@ public final class VersionSetting implements Cloneable { versionSetting.setMinMemory(getMinMemory()); versionSetting.setAutoMemory(isAutoMemory()); versionSetting.setPreLaunchCommand(getPreLaunchCommand()); + versionSetting.setPostExitCommand(getPostExitCommand()); versionSetting.setJavaArgs(getJavaArgs()); versionSetting.setMinecraftArgs(getMinecraftArgs()); versionSetting.setNoJVMArgs(isNoJVMArgs()); @@ -673,6 +693,7 @@ public final class VersionSetting implements Cloneable { obj.addProperty("height", src.getHeight()); obj.addProperty("javaDir", src.getJavaDir()); obj.addProperty("precalledCommand", src.getPreLaunchCommand()); + obj.addProperty("postExitCommand", src.getPostExitCommand()); obj.addProperty("serverIp", src.getServerIp()); obj.addProperty("java", src.getJava()); obj.addProperty("wrapper", src.getWrapper()); diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/game/LaunchOptions.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/game/LaunchOptions.java index 7eaf3a71b..76a50c34c 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/game/LaunchOptions.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/game/LaunchOptions.java @@ -53,6 +53,7 @@ public class LaunchOptions implements Serializable { private String proxyPass; private boolean noGeneratedJVMArgs; private String preLaunchCommand; + private String postExitCommand; private NativesDirectoryType nativesDirType; private String nativesDir; private ProcessPriority processPriority = ProcessPriority.NORMAL; @@ -199,12 +200,19 @@ public class LaunchOptions implements Serializable { } /** - * Called command line before launching the game. + * Command called before game launches. */ public String getPreLaunchCommand() { return preLaunchCommand; } + /** + * Command called after game exits. + */ + public String getPostExitCommand() { + return postExitCommand; + } + /** * 0 - ./minecraft/versions/<version>/natives * 1 - custom natives directory @@ -500,8 +508,13 @@ public class LaunchOptions implements Serializable { return this; } - public Builder setPrecalledCommand(String precalledCommand) { - options.preLaunchCommand = precalledCommand; + public Builder setPreLaunchCommand(String preLaunchCommand) { + options.preLaunchCommand = preLaunchCommand; + return this; + } + + public Builder setPostExitCommand(String postExitCommand) { + options.postExitCommand = postExitCommand; return this; } diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/launch/DefaultLauncher.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/launch/DefaultLauncher.java index e5be56204..f05cb6e2e 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/launch/DefaultLauncher.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/launch/DefaultLauncher.java @@ -18,6 +18,7 @@ package org.jackhuang.hmcl.launch; import org.jackhuang.hmcl.auth.AuthInfo; +import org.jackhuang.hmcl.download.LibraryAnalyzer; import org.jackhuang.hmcl.game.Argument; import org.jackhuang.hmcl.game.Arguments; import org.jackhuang.hmcl.game.GameRepository; @@ -374,16 +375,9 @@ public class DefaultLauncher extends Launcher { File runDirectory = repository.getRunDirectory(version.getId()); if (StringUtils.isNotBlank(options.getPreLaunchCommand())) { - String versionName = Optional.ofNullable(options.getVersionName()).orElse(version.getId()); - String preLaunchCommand = options.getPreLaunchCommand() - .replace("$INST_NAME", versionName) - .replace("$INST_ID", versionName) - .replace("$INST_DIR", repository.getVersionRoot(version.getId()).getAbsolutePath()) - .replace("$INST_MC_DIR", repository.getRunDirectory(version.getId()).getAbsolutePath()) - .replace("$INST_JAVA", options.getJava().getBinary().toString()); - - new ProcessBuilder(StringUtils.tokenize(preLaunchCommand)) - .directory(runDirectory).start().waitFor(); + ProcessBuilder builder = new ProcessBuilder(StringUtils.tokenize(options.getPreLaunchCommand())).directory(runDirectory); + builder.environment().putAll(getEnvVars()); + builder.start().waitFor(); } Process process; @@ -394,6 +388,7 @@ public class DefaultLauncher extends Launcher { } String appdata = options.getGameDir().getAbsoluteFile().getParent(); if (appdata != null) builder.environment().put("APPDATA", appdata); + builder.environment().putAll(getEnvVars()); process = builder.start(); } catch (IOException e) { throw new ProcessCreationException(e); @@ -405,6 +400,31 @@ public class DefaultLauncher extends Launcher { return p; } + private Map getEnvVars() { + String versionName = Optional.ofNullable(options.getVersionName()).orElse(version.getId()); + Map env = new HashMap<>(); + env.put("INST_NAME", versionName); + env.put("INST_ID", versionName); + env.put("INST_DIR", repository.getVersionRoot(version.getId()).getAbsolutePath()); + env.put("INST_MC_DIR", repository.getRunDirectory(version.getId()).getAbsolutePath()); + env.put("INST_JAVA", options.getJava().getBinary().toString()); + + LibraryAnalyzer analyzer = LibraryAnalyzer.analyze(version); + if (analyzer.has(LibraryAnalyzer.LibraryType.FORGE)) { + env.put("INST_FORGE", "1"); + } + if (analyzer.has(LibraryAnalyzer.LibraryType.LITELOADER)) { + env.put("INST_LITELOADER", "1"); + } + if (analyzer.has(LibraryAnalyzer.LibraryType.FABRIC)) { + env.put("INST_FABRIC", "1"); + } + if (analyzer.has(LibraryAnalyzer.LibraryType.OPTIFINE)) { + env.put("INST_OPTIFINE", "1"); + } + return env; + } + @Override public void makeLaunchScript(File scriptFile) throws IOException { boolean isWindows = OperatingSystem.WINDOWS == OperatingSystem.CURRENT_OS; @@ -432,12 +452,20 @@ public class DefaultLauncher extends Launcher { writer.write("@echo off"); writer.newLine(); writer.write("set APPDATA=" + options.getGameDir().getAbsoluteFile().getParent()); + for (Map.Entry entry : getEnvVars().entrySet()) { + writer.write("set " + entry.getKey() + "=" + entry.getValue()); + writer.newLine(); + } writer.newLine(); writer.write(new CommandBuilder().add("cd", "/D", repository.getRunDirectory(version.getId()).getAbsolutePath()).toString()); writer.newLine(); } else if (OperatingSystem.CURRENT_OS == OperatingSystem.OSX || OperatingSystem.CURRENT_OS == OperatingSystem.LINUX) { writer.write("#!/usr/bin/env bash"); writer.newLine(); + for (Map.Entry entry : getEnvVars().entrySet()) { + writer.write("export " + entry.getKey() + "=" + entry.getValue()); + writer.newLine(); + } writer.write(new CommandBuilder().add("cd", repository.getRunDirectory(version.getId()).getAbsolutePath()).toString()); writer.newLine(); } @@ -446,6 +474,16 @@ public class DefaultLauncher extends Launcher { writer.newLine(); } writer.write(generateCommandLine(nativeFolder).toString()); + writer.newLine(); + if (StringUtils.isNotBlank(options.getPostExitCommand())) { + writer.write(options.getPostExitCommand()); + writer.newLine(); + } + + if (isWindows) { + writer.write("pause"); + writer.newLine(); + } } if (!scriptFile.setExecutable(true)) throw new PermissionException();