mirror of
https://github.com/HMCL-dev/HMCL.git
synced 2024-11-21 03:10:58 +08:00
Support path containing unicode characters on Linux
This commit is contained in:
parent
6aef034880
commit
9f617c33f5
@ -45,6 +45,7 @@ import java.nio.charset.Charset;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.*;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
@ -68,7 +69,7 @@ public class DefaultLauncher extends Launcher {
|
||||
super(repository, version, authInfo, options, listener, daemon);
|
||||
}
|
||||
|
||||
private CommandBuilder generateCommandLine(File nativeFolder) throws IOException {
|
||||
private Command generateCommandLine(File nativeFolder) throws IOException {
|
||||
CommandBuilder res = new CommandBuilder();
|
||||
|
||||
switch (options.getProcessPriority()) {
|
||||
@ -208,10 +209,19 @@ public class DefaultLauncher extends Launcher {
|
||||
Path gameAssets = repository.getActualAssetDirectory(version.getId(), version.getAssetIndex().getId());
|
||||
Map<String, String> configuration = getConfigurations();
|
||||
configuration.put("${classpath}", String.join(OperatingSystem.PATH_SEPARATOR, classpath));
|
||||
configuration.put("${natives_directory}", nativeFolder.getAbsolutePath());
|
||||
configuration.put("${game_assets}", gameAssets.toAbsolutePath().toString());
|
||||
configuration.put("${assets_root}", gameAssets.toAbsolutePath().toString());
|
||||
|
||||
|
||||
String nativeFolderPath = nativeFolder.getAbsolutePath();
|
||||
Path tempNativeFolder = null;
|
||||
if (OperatingSystem.CURRENT_OS == OperatingSystem.LINUX
|
||||
&& !StringUtils.isASCII(nativeFolderPath)) {
|
||||
tempNativeFolder = Paths.get("/", "tmp", "hmcl-natives-" + UUID.randomUUID());
|
||||
nativeFolderPath = tempNativeFolder + File.pathSeparator + nativeFolderPath;
|
||||
}
|
||||
configuration.put("${natives_directory}", nativeFolderPath);
|
||||
|
||||
res.addAll(Arguments.parseArguments(version.getArguments().map(Arguments::getJvm).orElseGet(this::getDefaultJVMArguments), configuration));
|
||||
if (authInfo.getArguments() != null && authInfo.getArguments().getJvm() != null && !authInfo.getArguments().getJvm().isEmpty())
|
||||
res.addAll(Arguments.parseArguments(authInfo.getArguments().getJvm(), configuration));
|
||||
@ -258,7 +268,7 @@ public class DefaultLauncher extends Launcher {
|
||||
res.addAllWithoutParsing(Arguments.parseStringArguments(options.getGameArguments(), configuration));
|
||||
|
||||
res.removeIf(it -> getForbiddens().containsKey(it) && getForbiddens().get(it).get());
|
||||
return res;
|
||||
return new Command(res, tempNativeFolder);
|
||||
}
|
||||
|
||||
public Map<String, Boolean> getFeatures() {
|
||||
@ -363,8 +373,10 @@ public class DefaultLauncher extends Launcher {
|
||||
nativeFolder = new File(options.getNativesDir());
|
||||
}
|
||||
|
||||
final Command command = generateCommandLine(nativeFolder);
|
||||
|
||||
// To guarantee that when failed to generate launch command line, we will not call pre-launch command
|
||||
List<String> rawCommandLine = generateCommandLine(nativeFolder).asMutableList();
|
||||
List<String> rawCommandLine = command.commandLine.asMutableList();
|
||||
|
||||
// Pass classpath using the environment variable, to reduce the command length
|
||||
String classpath = null;
|
||||
@ -374,6 +386,12 @@ public class DefaultLauncher extends Launcher {
|
||||
classpath = rawCommandLine.remove(cpIndex);
|
||||
}
|
||||
|
||||
if (command.tempNativeFolder != null) {
|
||||
Files.deleteIfExists(command.tempNativeFolder);
|
||||
Files.createSymbolicLink(command.tempNativeFolder, nativeFolder.toPath().toAbsolutePath());
|
||||
command.tempNativeFolder.toFile().deleteOnExit();
|
||||
}
|
||||
|
||||
if (rawCommandLine.stream().anyMatch(StringUtils::isBlank)) {
|
||||
throw new IllegalStateException("Illegal command line " + rawCommandLine);
|
||||
}
|
||||
@ -464,8 +482,8 @@ public class DefaultLauncher extends Launcher {
|
||||
if (!FileUtils.makeFile(scriptFile))
|
||||
throw new IOException("Script file: " + scriptFile + " cannot be created.");
|
||||
|
||||
final CommandBuilder commandLine = generateCommandLine(nativeFolder);
|
||||
final String command = usePowerShell ? null : commandLine.toString();
|
||||
final Command commandLine = generateCommandLine(nativeFolder);
|
||||
final String command = usePowerShell ? null : commandLine.commandLine.toString();
|
||||
|
||||
if (!usePowerShell && isWindows) {
|
||||
if (command.length() > 8192) { // maximum length of the command in cmd
|
||||
@ -509,7 +527,7 @@ public class DefaultLauncher extends Launcher {
|
||||
writer.newLine();
|
||||
|
||||
writer.write('&');
|
||||
for (String rawCommand : commandLine.asList()) {
|
||||
for (String rawCommand : commandLine.commandLine.asList()) {
|
||||
writer.write(' ');
|
||||
writer.write(CommandBuilder.pwshString(rawCommand));
|
||||
}
|
||||
@ -533,6 +551,10 @@ public class DefaultLauncher extends Launcher {
|
||||
writer.write("export " + entry.getKey() + "=" + entry.getValue());
|
||||
writer.newLine();
|
||||
}
|
||||
if (commandLine.tempNativeFolder != null) {
|
||||
writer.write(new CommandBuilder().add("ln", "-s", nativeFolder.getAbsolutePath(), commandLine.tempNativeFolder.toString()).toString());
|
||||
writer.newLine();
|
||||
}
|
||||
writer.write(new CommandBuilder().add("cd", repository.getRunDirectory(version.getId()).getAbsolutePath()).toString());
|
||||
}
|
||||
writer.newLine();
|
||||
@ -551,6 +573,10 @@ public class DefaultLauncher extends Launcher {
|
||||
writer.write("pause");
|
||||
writer.newLine();
|
||||
}
|
||||
if (commandLine.tempNativeFolder != null) {
|
||||
writer.write(new CommandBuilder().add("rm", commandLine.tempNativeFolder.toString()).toString());
|
||||
writer.newLine();
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!scriptFile.setExecutable(true))
|
||||
@ -574,4 +600,14 @@ public class DefaultLauncher extends Launcher {
|
||||
managedProcess.addRelatedThread(stderr);
|
||||
managedProcess.addRelatedThread(Lang.thread(new ExitWaiter(managedProcess, Arrays.asList(stdout, stderr), processListener::onExit), "exit-waiter", isDaemon));
|
||||
}
|
||||
|
||||
private static final class Command {
|
||||
final CommandBuilder commandLine;
|
||||
final Path tempNativeFolder;
|
||||
|
||||
Command(CommandBuilder commandBuilder, Path tempNativeFolder) {
|
||||
this.commandLine = commandBuilder;
|
||||
this.tempNativeFolder = tempNativeFolder;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -22,6 +22,8 @@ import org.jackhuang.hmcl.util.platform.OperatingSystem;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.PrintStream;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.nio.charset.CharsetEncoder;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.*;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
@ -258,6 +260,10 @@ public final class StringUtils {
|
||||
return Optional.of(str.substring(0, halfLength) + " ... " + str.substring(str.length() - halfLength));
|
||||
}
|
||||
|
||||
public static boolean isASCII(CharSequence cs) {
|
||||
return US_ASCII_ENCODER.canEncode(cs);
|
||||
}
|
||||
|
||||
/**
|
||||
* Class for computing the longest common subsequence between strings.
|
||||
*/
|
||||
@ -295,4 +301,5 @@ public final class StringUtils {
|
||||
|
||||
public static final Pattern CHINESE_PATTERN = Pattern.compile("[\\u4e00-\\u9fa5]");
|
||||
|
||||
public static final CharsetEncoder US_ASCII_ENCODER = StandardCharsets.US_ASCII.newEncoder();
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user