Support path containing unicode characters on Linux
This commit is contained in:
@@ -45,6 +45,7 @@ import java.nio.charset.Charset;
|
|||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
import java.nio.file.Files;
|
import java.nio.file.Files;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
|
import java.nio.file.Paths;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
import java.util.function.Supplier;
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
@@ -68,7 +69,7 @@ public class DefaultLauncher extends Launcher {
|
|||||||
super(repository, version, authInfo, options, listener, daemon);
|
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();
|
CommandBuilder res = new CommandBuilder();
|
||||||
|
|
||||||
switch (options.getProcessPriority()) {
|
switch (options.getProcessPriority()) {
|
||||||
@@ -208,10 +209,19 @@ public class DefaultLauncher extends Launcher {
|
|||||||
Path gameAssets = repository.getActualAssetDirectory(version.getId(), version.getAssetIndex().getId());
|
Path gameAssets = repository.getActualAssetDirectory(version.getId(), version.getAssetIndex().getId());
|
||||||
Map<String, String> configuration = getConfigurations();
|
Map<String, String> configuration = getConfigurations();
|
||||||
configuration.put("${classpath}", String.join(OperatingSystem.PATH_SEPARATOR, classpath));
|
configuration.put("${classpath}", String.join(OperatingSystem.PATH_SEPARATOR, classpath));
|
||||||
configuration.put("${natives_directory}", nativeFolder.getAbsolutePath());
|
|
||||||
configuration.put("${game_assets}", gameAssets.toAbsolutePath().toString());
|
configuration.put("${game_assets}", gameAssets.toAbsolutePath().toString());
|
||||||
configuration.put("${assets_root}", 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));
|
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())
|
if (authInfo.getArguments() != null && authInfo.getArguments().getJvm() != null && !authInfo.getArguments().getJvm().isEmpty())
|
||||||
res.addAll(Arguments.parseArguments(authInfo.getArguments().getJvm(), configuration));
|
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.addAllWithoutParsing(Arguments.parseStringArguments(options.getGameArguments(), configuration));
|
||||||
|
|
||||||
res.removeIf(it -> getForbiddens().containsKey(it) && getForbiddens().get(it).get());
|
res.removeIf(it -> getForbiddens().containsKey(it) && getForbiddens().get(it).get());
|
||||||
return res;
|
return new Command(res, tempNativeFolder);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Map<String, Boolean> getFeatures() {
|
public Map<String, Boolean> getFeatures() {
|
||||||
@@ -363,8 +373,10 @@ public class DefaultLauncher extends Launcher {
|
|||||||
nativeFolder = new File(options.getNativesDir());
|
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
|
// 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
|
// Pass classpath using the environment variable, to reduce the command length
|
||||||
String classpath = null;
|
String classpath = null;
|
||||||
@@ -374,6 +386,12 @@ public class DefaultLauncher extends Launcher {
|
|||||||
classpath = rawCommandLine.remove(cpIndex);
|
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)) {
|
if (rawCommandLine.stream().anyMatch(StringUtils::isBlank)) {
|
||||||
throw new IllegalStateException("Illegal command line " + rawCommandLine);
|
throw new IllegalStateException("Illegal command line " + rawCommandLine);
|
||||||
}
|
}
|
||||||
@@ -464,8 +482,8 @@ public class DefaultLauncher extends Launcher {
|
|||||||
if (!FileUtils.makeFile(scriptFile))
|
if (!FileUtils.makeFile(scriptFile))
|
||||||
throw new IOException("Script file: " + scriptFile + " cannot be created.");
|
throw new IOException("Script file: " + scriptFile + " cannot be created.");
|
||||||
|
|
||||||
final CommandBuilder commandLine = generateCommandLine(nativeFolder);
|
final Command commandLine = generateCommandLine(nativeFolder);
|
||||||
final String command = usePowerShell ? null : commandLine.toString();
|
final String command = usePowerShell ? null : commandLine.commandLine.toString();
|
||||||
|
|
||||||
if (!usePowerShell && isWindows) {
|
if (!usePowerShell && isWindows) {
|
||||||
if (command.length() > 8192) { // maximum length of the command in cmd
|
if (command.length() > 8192) { // maximum length of the command in cmd
|
||||||
@@ -509,7 +527,7 @@ public class DefaultLauncher extends Launcher {
|
|||||||
writer.newLine();
|
writer.newLine();
|
||||||
|
|
||||||
writer.write('&');
|
writer.write('&');
|
||||||
for (String rawCommand : commandLine.asList()) {
|
for (String rawCommand : commandLine.commandLine.asList()) {
|
||||||
writer.write(' ');
|
writer.write(' ');
|
||||||
writer.write(CommandBuilder.pwshString(rawCommand));
|
writer.write(CommandBuilder.pwshString(rawCommand));
|
||||||
}
|
}
|
||||||
@@ -533,6 +551,10 @@ public class DefaultLauncher extends Launcher {
|
|||||||
writer.write("export " + entry.getKey() + "=" + entry.getValue());
|
writer.write("export " + entry.getKey() + "=" + entry.getValue());
|
||||||
writer.newLine();
|
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.write(new CommandBuilder().add("cd", repository.getRunDirectory(version.getId()).getAbsolutePath()).toString());
|
||||||
}
|
}
|
||||||
writer.newLine();
|
writer.newLine();
|
||||||
@@ -551,6 +573,10 @@ public class DefaultLauncher extends Launcher {
|
|||||||
writer.write("pause");
|
writer.write("pause");
|
||||||
writer.newLine();
|
writer.newLine();
|
||||||
}
|
}
|
||||||
|
if (commandLine.tempNativeFolder != null) {
|
||||||
|
writer.write(new CommandBuilder().add("rm", commandLine.tempNativeFolder.toString()).toString());
|
||||||
|
writer.newLine();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!scriptFile.setExecutable(true))
|
if (!scriptFile.setExecutable(true))
|
||||||
@@ -574,4 +600,14 @@ public class DefaultLauncher extends Launcher {
|
|||||||
managedProcess.addRelatedThread(stderr);
|
managedProcess.addRelatedThread(stderr);
|
||||||
managedProcess.addRelatedThread(Lang.thread(new ExitWaiter(managedProcess, Arrays.asList(stdout, stderr), processListener::onExit), "exit-waiter", isDaemon));
|
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.ByteArrayOutputStream;
|
||||||
import java.io.PrintStream;
|
import java.io.PrintStream;
|
||||||
import java.io.UnsupportedEncodingException;
|
import java.io.UnsupportedEncodingException;
|
||||||
|
import java.nio.charset.CharsetEncoder;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
import java.util.regex.Pattern;
|
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));
|
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.
|
* 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 Pattern CHINESE_PATTERN = Pattern.compile("[\\u4e00-\\u9fa5]");
|
||||||
|
|
||||||
|
public static final CharsetEncoder US_ASCII_ENCODER = StandardCharsets.US_ASCII.newEncoder();
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user