将 GameRepository 从 java.io.File 迁移至 NIO (#4496)
https://github.com/HMCL-dev/HMCL/issues/2987
This commit is contained in:
@@ -64,7 +64,7 @@ public final class HMCLGameLauncher extends DefaultLauncher {
|
||||
if (config().isDisableAutoGameOptions())
|
||||
return;
|
||||
|
||||
Path runDir = repository.getRunDirectory(version.getId()).toPath();
|
||||
Path runDir = repository.getRunDirectory(version.getId());
|
||||
Path optionsFile = runDir.resolve("options.txt");
|
||||
Path configFolder = runDir.resolve("config");
|
||||
|
||||
|
||||
@@ -44,7 +44,6 @@ import org.jackhuang.hmcl.util.platform.SystemInfo;
|
||||
import org.jackhuang.hmcl.util.versioning.VersionNumber;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.InvalidPathException;
|
||||
@@ -58,7 +57,7 @@ import static org.jackhuang.hmcl.setting.ConfigHolder.config;
|
||||
import static org.jackhuang.hmcl.util.logging.Logger.LOG;
|
||||
import static org.jackhuang.hmcl.util.Pair.pair;
|
||||
|
||||
public class HMCLGameRepository extends DefaultGameRepository {
|
||||
public final class HMCLGameRepository extends DefaultGameRepository {
|
||||
private final Profile profile;
|
||||
|
||||
// local version settings
|
||||
@@ -67,7 +66,7 @@ public class HMCLGameRepository extends DefaultGameRepository {
|
||||
|
||||
public final EventManager<Event> onVersionIconChanged = new EventManager<>();
|
||||
|
||||
public HMCLGameRepository(Profile profile, File baseDirectory) {
|
||||
public HMCLGameRepository(Profile profile, Path baseDirectory) {
|
||||
super(baseDirectory);
|
||||
this.profile = profile;
|
||||
}
|
||||
@@ -86,7 +85,7 @@ public class HMCLGameRepository extends DefaultGameRepository {
|
||||
}
|
||||
|
||||
@Override
|
||||
public File getRunDirectory(String id) {
|
||||
public Path getRunDirectory(String id) {
|
||||
switch (getGameDirectoryType(id)) {
|
||||
case VERSION_FOLDER:
|
||||
return getVersionRoot(id);
|
||||
@@ -94,7 +93,7 @@ public class HMCLGameRepository extends DefaultGameRepository {
|
||||
return super.getRunDirectory(id);
|
||||
case CUSTOM:
|
||||
try {
|
||||
return Path.of(getVersionSetting(id).getGameDir()).toFile();
|
||||
return Path.of(getVersionSetting(id).getGameDir());
|
||||
} catch (InvalidPathException ignored) {
|
||||
return getVersionRoot(id);
|
||||
}
|
||||
@@ -122,7 +121,7 @@ public class HMCLGameRepository extends DefaultGameRepository {
|
||||
});
|
||||
|
||||
try {
|
||||
Path file = getBaseDirectory().toPath().resolve("launcher_profiles.json");
|
||||
Path file = getBaseDirectory().resolve("launcher_profiles.json");
|
||||
if (!Files.exists(file) && !versions.isEmpty()) {
|
||||
Files.createDirectories(file.getParent());
|
||||
Files.writeString(file, PROFILE);
|
||||
@@ -132,7 +131,7 @@ public class HMCLGameRepository extends DefaultGameRepository {
|
||||
}
|
||||
}
|
||||
|
||||
public void changeDirectory(File newDirectory) {
|
||||
public void changeDirectory(Path newDirectory) {
|
||||
setBaseDirectory(newDirectory);
|
||||
refreshVersionsAsync().start();
|
||||
}
|
||||
@@ -143,13 +142,13 @@ public class HMCLGameRepository extends DefaultGameRepository {
|
||||
}
|
||||
|
||||
public void clean(String id) throws IOException {
|
||||
clean(getBaseDirectory().toPath());
|
||||
clean(getRunDirectory(id).toPath());
|
||||
clean(getBaseDirectory());
|
||||
clean(getRunDirectory(id));
|
||||
}
|
||||
|
||||
public void duplicateVersion(String srcId, String dstId, boolean copySaves) throws IOException {
|
||||
Path srcDir = getVersionRoot(srcId).toPath();
|
||||
Path dstDir = getVersionRoot(dstId).toPath();
|
||||
Path srcDir = getVersionRoot(srcId);
|
||||
Path dstDir = getVersionRoot(dstId);
|
||||
|
||||
Version fromVersion = getVersion(srcId);
|
||||
|
||||
@@ -183,22 +182,22 @@ public class HMCLGameRepository extends DefaultGameRepository {
|
||||
VersionSetting newVersionSetting = initLocalVersionSetting(dstId, oldVersionSetting);
|
||||
saveVersionSetting(dstId);
|
||||
|
||||
File srcGameDir = getRunDirectory(srcId);
|
||||
File dstGameDir = getRunDirectory(dstId);
|
||||
Path srcGameDir = getRunDirectory(srcId);
|
||||
Path dstGameDir = getRunDirectory(dstId);
|
||||
|
||||
if (originalGameDirType != GameDirectoryType.VERSION_FOLDER)
|
||||
FileUtils.copyDirectory(srcGameDir.toPath(), dstGameDir.toPath(), path -> Modpack.acceptFile(path, blackList, null));
|
||||
FileUtils.copyDirectory(srcGameDir, dstGameDir, path -> Modpack.acceptFile(path, blackList, null));
|
||||
}
|
||||
|
||||
private File getLocalVersionSettingFile(String id) {
|
||||
return new File(getVersionRoot(id), "hmclversion.cfg");
|
||||
private Path getLocalVersionSettingFile(String id) {
|
||||
return getVersionRoot(id).resolve("hmclversion.cfg");
|
||||
}
|
||||
|
||||
private void loadLocalVersionSetting(String id) {
|
||||
File file = getLocalVersionSettingFile(id);
|
||||
if (file.exists())
|
||||
Path file = getLocalVersionSettingFile(id);
|
||||
if (Files.exists(file))
|
||||
try {
|
||||
VersionSetting versionSetting = GSON.fromJson(Files.readString(file.toPath()), VersionSetting.class);
|
||||
VersionSetting versionSetting = JsonUtils.fromJsonFile(file, VersionSetting.class);
|
||||
initLocalVersionSetting(id, versionSetting);
|
||||
} catch (Exception ex) {
|
||||
// If [JsonParseException], [IOException] or [NullPointerException] happens, the json file is malformed and needed to be recreated.
|
||||
@@ -261,12 +260,12 @@ public class HMCLGameRepository extends DefaultGameRepository {
|
||||
return vs;
|
||||
}
|
||||
|
||||
public Optional<File> getVersionIconFile(String id) {
|
||||
File root = getVersionRoot(id);
|
||||
public Optional<Path> getVersionIconFile(String id) {
|
||||
Path root = getVersionRoot(id);
|
||||
|
||||
for (String extension : FXUtils.IMAGE_EXTENSIONS) {
|
||||
File file = new File(root, "icon." + extension);
|
||||
if (file.exists()) {
|
||||
Path file = root.resolve("icon." + extension);
|
||||
if (Files.exists(file)) {
|
||||
return Optional.of(file);
|
||||
}
|
||||
}
|
||||
@@ -274,21 +273,26 @@ public class HMCLGameRepository extends DefaultGameRepository {
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
public void setVersionIconFile(String id, File iconFile) throws IOException {
|
||||
String ext = FileUtils.getExtension(iconFile.getName()).toLowerCase(Locale.ROOT);
|
||||
public void setVersionIconFile(String id, Path iconFile) throws IOException {
|
||||
String ext = FileUtils.getExtension(iconFile).toLowerCase(Locale.ROOT);
|
||||
if (!FXUtils.IMAGE_EXTENSIONS.contains(ext)) {
|
||||
throw new IllegalArgumentException("Unsupported icon file: " + ext);
|
||||
}
|
||||
|
||||
deleteIconFile(id);
|
||||
|
||||
FileUtils.copyFile(iconFile.toPath(), getVersionRoot(id).toPath().resolve("icon." + ext));
|
||||
FileUtils.copyFile(iconFile, getVersionRoot(id).resolve("icon." + ext));
|
||||
}
|
||||
|
||||
public void deleteIconFile(String id) {
|
||||
File root = getVersionRoot(id);
|
||||
Path root = getVersionRoot(id);
|
||||
for (String extension : FXUtils.IMAGE_EXTENSIONS) {
|
||||
new File(root, "icon." + extension).delete();
|
||||
Path file = root.resolve("icon." + extension);
|
||||
try {
|
||||
Files.deleteIfExists(file);
|
||||
} catch (IOException e) {
|
||||
LOG.warning("Failed to delete icon file: " + file, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -301,10 +305,10 @@ public class HMCLGameRepository extends DefaultGameRepository {
|
||||
|
||||
if (iconType == VersionIconType.DEFAULT) {
|
||||
Version version = getVersion(id).resolve(this);
|
||||
Optional<File> iconFile = getVersionIconFile(id);
|
||||
Optional<Path> iconFile = getVersionIconFile(id);
|
||||
if (iconFile.isPresent()) {
|
||||
try {
|
||||
return FXUtils.loadImage(iconFile.get().toPath());
|
||||
return FXUtils.loadImage(iconFile.get());
|
||||
} catch (Exception e) {
|
||||
LOG.warning("Failed to load version icon of " + id, e);
|
||||
}
|
||||
@@ -339,7 +343,7 @@ public class HMCLGameRepository extends DefaultGameRepository {
|
||||
public void saveVersionSetting(String id) {
|
||||
if (!localVersionSettings.containsKey(id))
|
||||
return;
|
||||
Path file = getLocalVersionSettingFile(id).toPath().toAbsolutePath().normalize();
|
||||
Path file = getLocalVersionSettingFile(id).toAbsolutePath().normalize();
|
||||
try {
|
||||
Files.createDirectories(file.getParent());
|
||||
} catch (IOException e) {
|
||||
@@ -373,7 +377,7 @@ public class HMCLGameRepository extends DefaultGameRepository {
|
||||
vs.setUsesGlobal(true);
|
||||
}
|
||||
|
||||
public LaunchOptions getLaunchOptions(String version, JavaRuntime javaVersion, File gameDir, List<String> javaAgents, List<String> javaArguments, boolean makeLaunchScript) {
|
||||
public LaunchOptions getLaunchOptions(String version, JavaRuntime javaVersion, Path gameDir, List<String> javaAgents, List<String> javaArguments, boolean makeLaunchScript) {
|
||||
VersionSetting vs = getVersionSetting(version);
|
||||
|
||||
LaunchOptions.Builder builder = new LaunchOptions.Builder()
|
||||
@@ -384,7 +388,7 @@ public class HMCLGameRepository extends DefaultGameRepository {
|
||||
.setProfileName(Metadata.TITLE)
|
||||
.setGameArguments(StringUtils.tokenize(vs.getMinecraftArgs()))
|
||||
.setOverrideJavaArguments(StringUtils.tokenize(vs.getJavaArgs()))
|
||||
.setMaxMemory(vs.isNoJVMArgs() && vs.isAutoMemory() ? null : (int)(getAllocatedMemory(
|
||||
.setMaxMemory(vs.isNoJVMArgs() && vs.isAutoMemory() ? null : (int) (getAllocatedMemory(
|
||||
vs.getMaxMemory() * 1024L * 1024L,
|
||||
SystemInfo.getPhysicalMemoryStatus().getAvailable(),
|
||||
vs.isAutoMemory()
|
||||
@@ -430,15 +434,15 @@ public class HMCLGameRepository extends DefaultGameRepository {
|
||||
}
|
||||
}
|
||||
|
||||
File json = getModpackConfiguration(version);
|
||||
if (json.exists()) {
|
||||
Path json = getModpackConfiguration(version);
|
||||
if (Files.exists(json)) {
|
||||
try {
|
||||
String jsonText = Files.readString(json.toPath());
|
||||
String jsonText = Files.readString(json);
|
||||
ModpackConfiguration<?> modpackConfiguration = JsonUtils.GSON.fromJson(jsonText, ModpackConfiguration.class);
|
||||
ModpackProvider provider = ModpackHelper.getProviderByType(modpackConfiguration.getType());
|
||||
if (provider != null) provider.injectLaunchOptions(jsonText, builder);
|
||||
} catch (IOException | JsonParseException e) {
|
||||
e.printStackTrace();
|
||||
LOG.warning("Failed to parse modpack configuration file " + json, e);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -449,8 +453,8 @@ public class HMCLGameRepository extends DefaultGameRepository {
|
||||
}
|
||||
|
||||
@Override
|
||||
public File getModpackConfiguration(String version) {
|
||||
return new File(getVersionRoot(version), "modpack.cfg");
|
||||
public Path getModpackConfiguration(String version) {
|
||||
return getVersionRoot(version).resolve("modpack.cfg");
|
||||
}
|
||||
|
||||
public void markVersionAsModpack(String id) {
|
||||
@@ -463,16 +467,24 @@ public class HMCLGameRepository extends DefaultGameRepository {
|
||||
|
||||
public void markVersionLaunchedAbnormally(String id) {
|
||||
try {
|
||||
Files.createFile(getVersionRoot(id).toPath().resolve(".abnormal"));
|
||||
Files.createFile(getVersionRoot(id).resolve(".abnormal"));
|
||||
} catch (IOException ignored) {
|
||||
}
|
||||
}
|
||||
|
||||
public boolean unmarkVersionLaunchedAbnormally(String id) {
|
||||
File file = new File(getVersionRoot(id), ".abnormal");
|
||||
boolean result = file.isFile();
|
||||
file.delete();
|
||||
return result;
|
||||
Path file = getVersionRoot(id).resolve(".abnormal");
|
||||
if (Files.isRegularFile(file)) {
|
||||
try {
|
||||
Files.delete(file);
|
||||
} catch (IOException e) {
|
||||
LOG.warning("Failed to delete abnormal mark file: " + file, e);
|
||||
}
|
||||
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private static final Gson GSON = new GsonBuilder()
|
||||
|
||||
@@ -31,6 +31,8 @@ import org.jackhuang.hmcl.util.io.CompressingUtils;
|
||||
|
||||
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.Collections;
|
||||
import java.util.List;
|
||||
@@ -51,9 +53,9 @@ public final class HMCLModpackInstallTask extends Task<Void> {
|
||||
this.name = name;
|
||||
this.modpack = modpack;
|
||||
|
||||
File run = repository.getRunDirectory(name);
|
||||
File json = repository.getModpackConfiguration(name);
|
||||
if (repository.hasVersion(name) && !json.exists())
|
||||
Path run = repository.getRunDirectory(name);
|
||||
Path json = repository.getModpackConfiguration(name);
|
||||
if (repository.hasVersion(name) && Files.notExists(json))
|
||||
throw new IllegalArgumentException("Version " + name + " already exists");
|
||||
|
||||
dependents.add(dependency.gameBuilder().name(name).gameVersion(modpack.getGameVersion()).buildAsync());
|
||||
@@ -64,16 +66,16 @@ public final class HMCLModpackInstallTask extends Task<Void> {
|
||||
|
||||
ModpackConfiguration<Modpack> config = null;
|
||||
try {
|
||||
if (json.exists()) {
|
||||
config = JsonUtils.fromJsonFile(json.toPath(), ModpackConfiguration.typeOf(Modpack.class));
|
||||
if (Files.exists(json)) {
|
||||
config = JsonUtils.fromJsonFile(json, ModpackConfiguration.typeOf(Modpack.class));
|
||||
|
||||
if (!HMCLModpackProvider.INSTANCE.getName().equals(config.getType()))
|
||||
throw new IllegalArgumentException("Version " + name + " is not a HMCL modpack. Cannot update this version.");
|
||||
}
|
||||
} catch (JsonParseException | IOException ignore) {
|
||||
}
|
||||
dependents.add(new ModpackInstallTask<>(zipFile, run, modpack.getEncoding(), Collections.singletonList("/minecraft"), it -> !"pack.json".equals(it), config));
|
||||
dependents.add(new MinecraftInstanceTask<>(zipFile, modpack.getEncoding(), Collections.singletonList("/minecraft"), modpack, HMCLModpackProvider.INSTANCE, modpack.getName(), modpack.getVersion(), repository.getModpackConfiguration(name)).withStage("hmcl.modpack"));
|
||||
dependents.add(new ModpackInstallTask<>(zipFile, run.toFile(), modpack.getEncoding(), Collections.singletonList("/minecraft"), it -> !"pack.json".equals(it), config));
|
||||
dependents.add(new MinecraftInstanceTask<>(zipFile, modpack.getEncoding(), Collections.singletonList("/minecraft"), modpack, HMCLModpackProvider.INSTANCE, modpack.getName(), modpack.getVersion(), repository.getModpackConfiguration(name).toFile()).withStage("hmcl.modpack"));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -55,6 +55,7 @@ import java.io.IOException;
|
||||
import java.net.SocketTimeoutException;
|
||||
import java.net.URI;
|
||||
import java.nio.file.AccessDeniedException;
|
||||
import java.nio.file.Path;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.*;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
@@ -150,7 +151,7 @@ public final class LauncherHelper {
|
||||
dependencyManager.checkGameCompletionAsync(version.get(), integrityCheck),
|
||||
Task.composeAsync(() -> {
|
||||
try {
|
||||
ModpackConfiguration<?> configuration = ModpackHelper.readModpackConfiguration(repository.getModpackConfiguration(selectedVersion));
|
||||
ModpackConfiguration<?> configuration = ModpackHelper.readModpackConfiguration(repository.getModpackConfiguration(selectedVersion).toFile());
|
||||
ModpackProvider provider = ModpackHelper.getProviderByType(configuration.getType());
|
||||
if (provider == null) return null;
|
||||
else return provider.createCompletionTask(dependencyManager, selectedVersion);
|
||||
@@ -164,16 +165,16 @@ public final class LauncherHelper {
|
||||
Library lib = NativePatcher.getWindowsMesaLoader(java, renderer, OperatingSystem.SYSTEM_VERSION);
|
||||
if (lib == null)
|
||||
return null;
|
||||
File file = dependencyManager.getGameRepository().getLibraryFile(version.get(), lib);
|
||||
if (file.getAbsolutePath().indexOf('=') >= 0) {
|
||||
Path file = dependencyManager.getGameRepository().getLibraryFile(version.get(), lib);
|
||||
if (file.toAbsolutePath().toString().indexOf('=') >= 0) {
|
||||
LOG.warning("Invalid character '=' in the libraries directory path, unable to attach software renderer loader");
|
||||
return null;
|
||||
}
|
||||
|
||||
String agent = file.getAbsolutePath() + "=" + renderer.name().toLowerCase(Locale.ROOT);
|
||||
String agent = FileUtils.getAbsolutePath(file) + "=" + renderer.name().toLowerCase(Locale.ROOT);
|
||||
|
||||
if (GameLibrariesTask.shouldDownloadLibrary(repository, version.get(), lib, integrityCheck)) {
|
||||
return new LibraryDownloadTask(dependencyManager, file.toPath(), lib)
|
||||
return new LibraryDownloadTask(dependencyManager, file, lib)
|
||||
.thenRunAsync(() -> javaAgents.add(agent));
|
||||
} else {
|
||||
javaAgents.add(agent);
|
||||
@@ -189,7 +190,7 @@ public final class LauncherHelper {
|
||||
.thenComposeAsync(() -> logIn(account).withStage("launch.state.logging_in"))
|
||||
.thenComposeAsync(authInfo -> Task.supplyAsync(() -> {
|
||||
LaunchOptions launchOptions = repository.getLaunchOptions(
|
||||
selectedVersion, javaVersionRef.get(), profile.getGameDir(), javaAgents, javaArguments, scriptFile != null);
|
||||
selectedVersion, javaVersionRef.get(), profile.getGameDir().toPath(), javaAgents, javaArguments, scriptFile != null);
|
||||
|
||||
LOG.info("Here's the structure of game mod directory:\n" + FileUtils.printFileStructure(repository.getModManager(selectedVersion).getModsDirectory(), 10));
|
||||
|
||||
|
||||
@@ -44,8 +44,8 @@ public final class LogExporter {
|
||||
}
|
||||
|
||||
public static CompletableFuture<Void> exportLogs(Path zipFile, DefaultGameRepository gameRepository, String versionId, String logs, String launchScript) {
|
||||
Path runDirectory = gameRepository.getRunDirectory(versionId).toPath();
|
||||
Path baseDirectory = gameRepository.getBaseDirectory().toPath();
|
||||
Path runDirectory = gameRepository.getRunDirectory(versionId);
|
||||
Path baseDirectory = gameRepository.getBaseDirectory();
|
||||
List<String> versions = new ArrayList<>();
|
||||
|
||||
String currentVersionId = versionId;
|
||||
|
||||
@@ -132,12 +132,12 @@ public final class Profile implements Observable {
|
||||
public Profile(String name, File initialGameDir, VersionSetting global, String selectedVersion, boolean useRelativePath) {
|
||||
this.name = new SimpleStringProperty(this, "name", name);
|
||||
gameDir = new SimpleObjectProperty<>(this, "gameDir", initialGameDir);
|
||||
repository = new HMCLGameRepository(this, initialGameDir);
|
||||
repository = new HMCLGameRepository(this, initialGameDir.toPath());
|
||||
this.global.set(global == null ? new VersionSetting() : global);
|
||||
this.selectedVersion.set(selectedVersion);
|
||||
this.useRelativePath.set(useRelativePath);
|
||||
|
||||
gameDir.addListener((a, b, newValue) -> repository.changeDirectory(newValue));
|
||||
gameDir.addListener((a, b, newValue) -> repository.changeDirectory(newValue.toPath()));
|
||||
this.selectedVersion.addListener(o -> checkSelectedVersion());
|
||||
listenerHolder.add(EventBus.EVENT_BUS.channel(RefreshedVersionsEvent.class).registerWeak(event -> checkSelectedVersion(), EventPriority.HIGHEST));
|
||||
|
||||
|
||||
@@ -129,7 +129,7 @@ public class GameCrashWindow extends Stage {
|
||||
|
||||
return pair(CrashReportAnalyzer.analyze(rawLog), crashReport != null ? CrashReportAnalyzer.findKeywordsFromCrashReport(crashReport) : new HashSet<>());
|
||||
}), Task.supplyAsync(() -> {
|
||||
Path latestLog = repository.getRunDirectory(version.getId()).toPath().resolve("logs/latest.log");
|
||||
Path latestLog = repository.getRunDirectory(version.getId()).resolve("logs/latest.log");
|
||||
if (!Files.isReadable(latestLog)) {
|
||||
return pair(new HashSet<CrashReportAnalyzer.Result>(), new HashSet<String>());
|
||||
}
|
||||
@@ -379,7 +379,7 @@ public class GameCrashWindow extends Stage {
|
||||
TwoLineListItem gameDir = new TwoLineListItem();
|
||||
gameDir.getStyleClass().setAll("two-line-item-second-large");
|
||||
gameDir.setTitle(i18n("game.directory"));
|
||||
gameDir.setSubtitle(launchOptions.getGameDir().getAbsolutePath());
|
||||
gameDir.setSubtitle(launchOptions.getGameDir().toAbsolutePath().toString());
|
||||
FXUtils.installFastTooltip(gameDir, i18n("game.directory"));
|
||||
|
||||
TwoLineListItem javaDir = new TwoLineListItem();
|
||||
|
||||
@@ -145,7 +145,7 @@ public class DownloadPage extends DecoratorAnimatedPage implements DecoratorPage
|
||||
public static void download(Profile profile, @Nullable String version, RemoteMod.Version file, String subdirectoryName) {
|
||||
if (version == null) version = profile.getSelectedVersion();
|
||||
|
||||
Path runDirectory = profile.getRepository().hasVersion(version) ? profile.getRepository().getRunDirectory(version).toPath() : profile.getRepository().getBaseDirectory().toPath();
|
||||
Path runDirectory = profile.getRepository().hasVersion(version) ? profile.getRepository().getRunDirectory(version) : profile.getRepository().getBaseDirectory();
|
||||
|
||||
Controllers.prompt(i18n("archive.file.name"), (result, resolve, reject) -> {
|
||||
if (!FileUtils.isNameValid(result)) {
|
||||
|
||||
@@ -95,9 +95,9 @@ public class ModpackInstallWizardProvider implements WizardProvider {
|
||||
}
|
||||
try {
|
||||
if (serverModpackManifest != null) {
|
||||
return ModpackHelper.getUpdateTask(profile, serverModpackManifest, modpack.getEncoding(), name, ModpackHelper.readModpackConfiguration(profile.getRepository().getModpackConfiguration(name)));
|
||||
return ModpackHelper.getUpdateTask(profile, serverModpackManifest, modpack.getEncoding(), name, ModpackHelper.readModpackConfiguration(profile.getRepository().getModpackConfiguration(name).toFile()));
|
||||
} else {
|
||||
return ModpackHelper.getUpdateTask(profile, selected, modpack.getEncoding(), name, ModpackHelper.readModpackConfiguration(profile.getRepository().getModpackConfiguration(name)));
|
||||
return ModpackHelper.getUpdateTask(profile, selected, modpack.getEncoding(), name, ModpackHelper.readModpackConfiguration(profile.getRepository().getModpackConfiguration(name).toFile()));
|
||||
}
|
||||
} catch (UnsupportedModpackException | ManuallyCreatedModpackException e) {
|
||||
Controllers.dialog(i18n("modpack.unsupported"), i18n("message.error"), MessageType.ERROR);
|
||||
|
||||
@@ -61,7 +61,7 @@ public final class ModpackFileSelectionPage extends BorderPane implements Wizard
|
||||
this.adviser = adviser;
|
||||
|
||||
JFXTreeView<String> treeView = new JFXTreeView<>();
|
||||
rootNode = getTreeItem(profile.getRepository().getRunDirectory(version), "minecraft");
|
||||
rootNode = getTreeItem(profile.getRepository().getRunDirectory(version).toFile(), "minecraft");
|
||||
treeView.setRoot(rootNode);
|
||||
treeView.setSelectionModel(new NoneMultipleSelectionModel<>());
|
||||
this.setCenter(treeView);
|
||||
|
||||
@@ -245,12 +245,12 @@ public final class AdvancedVersionSettingPage extends StackPane implements Decor
|
||||
String nativesDirName = "natives-" + Platform.SYSTEM_PLATFORM;
|
||||
if (versionId == null) {
|
||||
return String.format("%s/%s/%s",
|
||||
profile.getRepository().getBaseDirectory().toPath().resolve("versions").toAbsolutePath().normalize(),
|
||||
profile.getRepository().getBaseDirectory().resolve("versions").toAbsolutePath().normalize(),
|
||||
i18n("settings.advanced.natives_directory.default.version_id"),
|
||||
nativesDirName
|
||||
);
|
||||
} else {
|
||||
return profile.getRepository().getVersionRoot(versionId).toPath()
|
||||
return profile.getRepository().getVersionRoot(versionId)
|
||||
.toAbsolutePath().normalize()
|
||||
.resolve(nativesDirName)
|
||||
.toString();
|
||||
|
||||
@@ -175,7 +175,7 @@ public final class ModListPage extends ListPageBase<ModListPageSkin.ModInfoObjec
|
||||
}
|
||||
|
||||
public void openModFolder() {
|
||||
FXUtils.openFolder(new File(profile.getRepository().getRunDirectory(versionId), "mods"));
|
||||
FXUtils.openFolder(profile.getRepository().getRunDirectory(versionId).resolve("mods").toFile());
|
||||
}
|
||||
|
||||
public void checkUpdates() {
|
||||
|
||||
@@ -78,7 +78,7 @@ public class VersionIconDialog extends DialogPane {
|
||||
File selectedFile = chooser.showOpenDialog(Controllers.getStage());
|
||||
if (selectedFile != null) {
|
||||
try {
|
||||
profile.getRepository().setVersionIconFile(versionId, selectedFile);
|
||||
profile.getRepository().setVersionIconFile(versionId, selectedFile.toPath());
|
||||
|
||||
if (vs != null) {
|
||||
vs.setVersionIcon(VersionIconType.DEFAULT);
|
||||
|
||||
@@ -45,7 +45,6 @@ import org.jackhuang.hmcl.ui.decorator.DecoratorAnimatedPage;
|
||||
import org.jackhuang.hmcl.ui.decorator.DecoratorPage;
|
||||
import org.jackhuang.hmcl.util.io.FileUtils;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.Optional;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
@@ -158,7 +157,7 @@ public class VersionPage extends DecoratorAnimatedPage implements DecoratorPage
|
||||
}
|
||||
|
||||
private void onBrowse(String sub) {
|
||||
FXUtils.openFolder(new File(getProfile().getRepository().getRunDirectory(getVersion()), sub));
|
||||
FXUtils.openFolder(getProfile().getRepository().getRunDirectory(getVersion()).resolve(sub).toFile());
|
||||
}
|
||||
|
||||
private void redownloadAssetIndex() {
|
||||
@@ -166,14 +165,14 @@ public class VersionPage extends DecoratorAnimatedPage implements DecoratorPage
|
||||
}
|
||||
|
||||
private void clearLibraries() {
|
||||
FileUtils.deleteDirectoryQuietly(getProfile().getRepository().getBaseDirectory().toPath().resolve("libraries"));
|
||||
FileUtils.deleteDirectoryQuietly(getProfile().getRepository().getBaseDirectory().resolve("libraries"));
|
||||
}
|
||||
|
||||
private void clearAssets() {
|
||||
HMCLGameRepository baseDirectory = getProfile().getRepository();
|
||||
FileUtils.deleteDirectoryQuietly(baseDirectory.getBaseDirectory().toPath().resolve("assets"));
|
||||
FileUtils.deleteDirectoryQuietly(baseDirectory.getBaseDirectory().resolve("assets"));
|
||||
if (version.get() != null) {
|
||||
FileUtils.deleteDirectoryQuietly(baseDirectory.getRunDirectory(version.get().getVersion()).toPath().resolve("resources"));
|
||||
FileUtils.deleteDirectoryQuietly(baseDirectory.getRunDirectory(version.get().getVersion()).resolve("resources"));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -56,7 +56,6 @@ import org.jackhuang.hmcl.util.platform.SystemInfo;
|
||||
import org.jackhuang.hmcl.util.platform.hardware.PhysicalMemoryStatus;
|
||||
import org.jackhuang.hmcl.util.versioning.GameVersionNumber;
|
||||
|
||||
import java.nio.file.Paths;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
@@ -635,7 +634,7 @@ public final class VersionSettingsPage extends StackPane implements DecoratorPag
|
||||
versionSetting.javaVersionProperty().addListener(javaListener);
|
||||
|
||||
gameDirItem.selectedDataProperty().bindBidirectional(versionSetting.gameDirTypeProperty());
|
||||
gameDirSublist.subtitleProperty().bind(Bindings.createStringBinding(() -> Paths.get(profile.getRepository().getRunDirectory(versionId).getAbsolutePath()).normalize().toString(),
|
||||
gameDirSublist.subtitleProperty().bind(Bindings.createStringBinding(() -> profile.getRepository().getRunDirectory(versionId).toAbsolutePath().normalize().toString(),
|
||||
versionSetting.gameDirProperty(), versionSetting.gameDirTypeProperty()));
|
||||
|
||||
lastVersionSetting = versionSetting;
|
||||
|
||||
@@ -143,7 +143,7 @@ public final class Versions {
|
||||
}
|
||||
|
||||
public static void openFolder(Profile profile, String version) {
|
||||
FXUtils.openFolder(profile.getRepository().getRunDirectory(version));
|
||||
FXUtils.openFolder(profile.getRepository().getRunDirectory(version).toFile());
|
||||
}
|
||||
|
||||
public static void duplicateVersion(Profile profile, String version) {
|
||||
@@ -193,8 +193,8 @@ public final class Versions {
|
||||
ensureSelectedAccount(account -> {
|
||||
GameRepository repository = profile.getRepository();
|
||||
FileChooser chooser = new FileChooser();
|
||||
if (repository.getRunDirectory(id).isDirectory())
|
||||
chooser.setInitialDirectory(repository.getRunDirectory(id));
|
||||
if (Files.isDirectory(repository.getRunDirectory(id)))
|
||||
chooser.setInitialDirectory(repository.getRunDirectory(id).toFile());
|
||||
chooser.setTitle(i18n("version.launch_script.save"));
|
||||
chooser.getExtensionFilters().add(OperatingSystem.CURRENT_OS == OperatingSystem.WINDOWS
|
||||
? new FileChooser.ExtensionFilter(i18n("extension.bat"), "*.bat")
|
||||
|
||||
@@ -31,7 +31,7 @@ import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.io.File;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Paths;
|
||||
import java.nio.file.Path;
|
||||
import java.util.Arrays;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.stream.Collectors;
|
||||
@@ -49,11 +49,13 @@ public class GameCrashWindowTest {
|
||||
|
||||
CountDownLatch latch = new CountDownLatch(1);
|
||||
FXUtils.runInFX(() -> {
|
||||
Path workingPath = Path.of(System.getProperty("user.dir"));
|
||||
|
||||
GameCrashWindow window = new GameCrashWindow(process, ProcessListener.ExitType.APPLICATION_ERROR, null,
|
||||
new ClassicVersion(),
|
||||
new LaunchOptions.Builder()
|
||||
.setJava(new JavaRuntime(Paths.get("."), new JavaInfo(Platform.SYSTEM_PLATFORM, "16", null), false, false))
|
||||
.setGameDir(new File("."))
|
||||
.setJava(new JavaRuntime(workingPath, new JavaInfo(Platform.SYSTEM_PLATFORM, "16", null), false, false))
|
||||
.setGameDir(workingPath)
|
||||
.create(),
|
||||
Arrays.stream(logs.split("\\n"))
|
||||
.map(Log::new)
|
||||
|
||||
@@ -28,9 +28,10 @@ import org.jackhuang.hmcl.game.DefaultGameRepository;
|
||||
import org.jackhuang.hmcl.game.Library;
|
||||
import org.jackhuang.hmcl.game.Version;
|
||||
import org.jackhuang.hmcl.task.Task;
|
||||
import org.jackhuang.hmcl.util.io.FileUtils;
|
||||
|
||||
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;
|
||||
@@ -79,11 +80,11 @@ public class DefaultDependencyManager extends AbstractDependencyManager {
|
||||
public Task<?> checkGameCompletionAsync(Version version, boolean integrityCheck) {
|
||||
return Task.allOf(
|
||||
Task.composeAsync(() -> {
|
||||
File versionJar = repository.getVersionJar(version);
|
||||
if (!versionJar.exists() || versionJar.length() == 0)
|
||||
return new GameDownloadTask(this, null, version);
|
||||
else
|
||||
return null;
|
||||
Path versionJar = repository.getVersionJar(version);
|
||||
|
||||
return Files.notExists(versionJar) || FileUtils.size(versionJar) == 0L
|
||||
? new GameDownloadTask(this, null, version)
|
||||
: null;
|
||||
}).thenComposeAsync(checkPatchCompletionAsync(version, integrityCheck)),
|
||||
new GameAssetDownloadTask(this, version, GameAssetDownloadTask.DOWNLOAD_INDEX_IF_NECESSARY, integrityCheck)
|
||||
.setSignificance(Task.TaskSignificance.MODERATE),
|
||||
@@ -134,7 +135,7 @@ public class DefaultDependencyManager extends AbstractDependencyManager {
|
||||
if (GameLibrariesTask.shouldDownloadLibrary(repository, version, installer, integrityCheck)) {
|
||||
tasks.add(installLibraryAsync(gameVersion, original, "optifine", optifinePatchVersion));
|
||||
} else {
|
||||
tasks.add(OptiFineInstallTask.install(this, original, repository.getLibraryFile(version, installer).toPath()));
|
||||
tasks.add(OptiFineInstallTask.install(this, original, repository.getLibraryFile(version, installer)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -160,7 +160,7 @@ public class MaintainTask extends Task<Version> {
|
||||
optiFine.ifPresent(library -> {
|
||||
builder.addJvmArgument("-Dhmcl.transformer.candidates=${library_directory}/" + library.getPath());
|
||||
if (!libraryExisting) builder.addLibrary(hmclTransformerDiscoveryService);
|
||||
Path libraryPath = repository.getLibraryFile(version, hmclTransformerDiscoveryService).toPath();
|
||||
Path libraryPath = repository.getLibraryFile(version, hmclTransformerDiscoveryService);
|
||||
try (InputStream input = MaintainTask.class.getResourceAsStream("/assets/game/HMCLTransformerDiscoveryService-1.0.jar")) {
|
||||
Files.createDirectories(libraryPath.getParent());
|
||||
Files.copy(Objects.requireNonNull(input, "Bundled HMCLTransformerDiscoveryService is missing."), libraryPath, StandardCopyOption.REPLACE_EXISTING);
|
||||
@@ -181,7 +181,7 @@ public class MaintainTask extends Task<Version> {
|
||||
// we need to manually ignore ${primary_jar}.
|
||||
newIgnoreList.add("${primary_jar}");
|
||||
|
||||
Path libraryDirectory = repository.getLibrariesDirectory(version).toPath().toAbsolutePath();
|
||||
Path libraryDirectory = repository.getLibrariesDirectory(version).toAbsolutePath().normalize();
|
||||
|
||||
// The default ignoreList is too loose and may cause some problems, we replace them with the absolute version.
|
||||
// For example, if "client-extra" is in ignoreList, and game directory contains "client-extra" component, all
|
||||
@@ -260,7 +260,7 @@ public class MaintainTask extends Task<Version> {
|
||||
Library library = libraries.get(i);
|
||||
if (library.is("optifine", "OptiFine")) {
|
||||
Library newLibrary = new Library(new Artifact("optifine", "OptiFine", library.getVersion(), "installer"));
|
||||
if (repository.getLibraryFile(version, newLibrary).exists()) {
|
||||
if (Files.exists(repository.getLibraryFile(version, newLibrary))) {
|
||||
libraries.set(i, null);
|
||||
// OptiFine should be loaded after Forge in classpath.
|
||||
// Although we have altered priority of OptiFine higher than Forge,
|
||||
|
||||
@@ -59,7 +59,7 @@ public final class FabricAPIInstallTask extends Task<Version> {
|
||||
public void execute() throws IOException {
|
||||
dependencies.add(new FileDownloadTask(
|
||||
remote.getVersion().getFile().getUrl(),
|
||||
dependencyManager.getGameRepository().getRunDirectory(version.getId()).toPath().resolve("mods").resolve("fabric-api-" + remote.getVersion().getVersion() + ".jar"),
|
||||
dependencyManager.getGameRepository().getRunDirectory(version.getId()).resolve("mods").resolve("fabric-api-" + remote.getVersion().getVersion() + ".jar"),
|
||||
remote.getVersion().getFile().getIntegrityCheck())
|
||||
);
|
||||
}
|
||||
|
||||
@@ -284,7 +284,7 @@ public class ForgeNewInstallTask extends Task<Version> {
|
||||
for (Library library : profile.getLibraries()) {
|
||||
Path file = fs.getPath("maven").resolve(library.getPath());
|
||||
if (Files.exists(file)) {
|
||||
Path dest = gameRepository.getLibraryFile(version, library).toPath();
|
||||
Path dest = gameRepository.getLibraryFile(version, library);
|
||||
FileUtils.copyFile(file, dest);
|
||||
}
|
||||
}
|
||||
@@ -391,11 +391,11 @@ public class ForgeNewInstallTask extends Task<Version> {
|
||||
}
|
||||
|
||||
vars.put("SIDE", "client");
|
||||
vars.put("MINECRAFT_JAR", gameRepository.getVersionJar(version).getAbsolutePath());
|
||||
vars.put("MINECRAFT_VERSION", gameRepository.getVersionJar(version).getAbsolutePath());
|
||||
vars.put("ROOT", gameRepository.getBaseDirectory().getAbsolutePath());
|
||||
vars.put("MINECRAFT_JAR", FileUtils.getAbsolutePath(gameRepository.getVersionJar(version)));
|
||||
vars.put("MINECRAFT_VERSION", FileUtils.getAbsolutePath(gameRepository.getVersionJar(version)));
|
||||
vars.put("ROOT", FileUtils.getAbsolutePath(gameRepository.getBaseDirectory()));
|
||||
vars.put("INSTALLER", installer.toAbsolutePath().toString());
|
||||
vars.put("LIBRARY_DIR", gameRepository.getLibrariesDirectory(version).getAbsolutePath());
|
||||
vars.put("LIBRARY_DIR", FileUtils.getAbsolutePath(gameRepository.getLibrariesDirectory(version)));
|
||||
|
||||
updateProgress(0, processors.size());
|
||||
|
||||
|
||||
@@ -72,7 +72,7 @@ public class ForgeOldInstallTask extends Task<Version> {
|
||||
|
||||
// unpack the universal jar in the installer file.
|
||||
Library forgeLibrary = new Library(installProfile.getInstall().getPath());
|
||||
Path forgeFile = dependencyManager.getGameRepository().getLibraryFile(version, forgeLibrary).toPath();
|
||||
Path forgeFile = dependencyManager.getGameRepository().getLibraryFile(version, forgeLibrary);
|
||||
Files.createDirectories(forgeFile.getParent());
|
||||
|
||||
ZipEntry forgeEntry = zipFile.getEntry(installProfile.getInstall().getFilePath());
|
||||
|
||||
@@ -53,7 +53,7 @@ public final class GameDownloadTask extends Task<Void> {
|
||||
|
||||
@Override
|
||||
public void execute() {
|
||||
Path jar = dependencyManager.getGameRepository().getVersionJar(version).toPath();
|
||||
Path jar = dependencyManager.getGameRepository().getVersionJar(version);
|
||||
|
||||
var task = new FileDownloadTask(
|
||||
dependencyManager.getDownloadProvider().injectURLWithCandidates(version.getDownloadInfo().getUrl()),
|
||||
|
||||
@@ -88,7 +88,7 @@ public final class GameLibrariesTask extends Task<Void> {
|
||||
}
|
||||
|
||||
public static boolean shouldDownloadLibrary(GameRepository gameRepository, Version version, Library library, boolean integrityCheck) {
|
||||
Path file = gameRepository.getLibraryFile(version, library).toPath();
|
||||
Path file = gameRepository.getLibraryFile(version, library);
|
||||
if (!Files.isRegularFile(file)) return true;
|
||||
|
||||
if (!integrityCheck) {
|
||||
@@ -142,8 +142,7 @@ public final class GameLibrariesTask extends Task<Void> {
|
||||
&& gameRepository instanceof DefaultGameRepository defaultGameRepository) {
|
||||
List<FMLLib> fmlLibs = getFMLLibs(library.getVersion());
|
||||
if (fmlLibs != null) {
|
||||
Path libDir = defaultGameRepository.getBaseDirectory().toPath()
|
||||
.resolve("lib")
|
||||
Path libDir = defaultGameRepository.getBaseDirectory().resolve("lib")
|
||||
.toAbsolutePath().normalize();
|
||||
|
||||
for (FMLLib fmlLib : fmlLibs) {
|
||||
@@ -158,7 +157,7 @@ public final class GameLibrariesTask extends Task<Void> {
|
||||
}
|
||||
}
|
||||
|
||||
Path file = gameRepository.getLibraryFile(version, library).toPath();
|
||||
Path file = gameRepository.getLibraryFile(version, library);
|
||||
if ("optifine".equals(library.getGroupId()) && Files.exists(file) && GameVersionNumber.asGameVersion(gameRepository.getGameVersion(version)).compareTo("1.20.4") == 0) {
|
||||
String forgeVersion = LibraryAnalyzer.analyze(version, "1.20.4")
|
||||
.getVersion(LibraryAnalyzer.LibraryType.FORGE)
|
||||
|
||||
@@ -62,7 +62,7 @@ public final class GameVerificationFixTask extends Task<Void> {
|
||||
|
||||
@Override
|
||||
public void execute() throws IOException {
|
||||
Path jar = dependencyManager.getGameRepository().getVersionJar(version).toPath();
|
||||
Path jar = dependencyManager.getGameRepository().getVersionJar(version);
|
||||
LibraryAnalyzer analyzer = LibraryAnalyzer.analyze(version, gameVersion);
|
||||
|
||||
if (Files.exists(jar) && GameVersionNumber.compare(gameVersion, "1.6") < 0 && analyzer.has(LibraryAnalyzer.LibraryType.FORGE)) {
|
||||
|
||||
@@ -51,7 +51,7 @@ public final class VersionJsonSaveTask extends Task<Version> {
|
||||
|
||||
@Override
|
||||
public void execute() throws Exception {
|
||||
Path json = repository.getVersionJson(version.getId()).toPath().toAbsolutePath();
|
||||
Path json = repository.getVersionJson(version.getId()).toAbsolutePath();
|
||||
Files.createDirectories(json.getParent());
|
||||
JsonUtils.writeToJsonFile(json, version);
|
||||
}
|
||||
|
||||
@@ -280,7 +280,7 @@ public class NeoForgeOldInstallTask extends Task<Version> {
|
||||
for (Library library : profile.getLibraries()) {
|
||||
Path file = fs.getPath("maven").resolve(library.getPath());
|
||||
if (Files.exists(file)) {
|
||||
Path dest = gameRepository.getLibraryFile(version, library).toPath();
|
||||
Path dest = gameRepository.getLibraryFile(version, library);
|
||||
FileUtils.copyFile(file, dest);
|
||||
}
|
||||
}
|
||||
@@ -387,11 +387,11 @@ public class NeoForgeOldInstallTask extends Task<Version> {
|
||||
}
|
||||
|
||||
vars.put("SIDE", "client");
|
||||
vars.put("MINECRAFT_JAR", gameRepository.getVersionJar(version).getAbsolutePath());
|
||||
vars.put("MINECRAFT_VERSION", gameRepository.getVersionJar(version).getAbsolutePath());
|
||||
vars.put("ROOT", gameRepository.getBaseDirectory().getAbsolutePath());
|
||||
vars.put("MINECRAFT_JAR", FileUtils.getAbsolutePath(gameRepository.getVersionJar(version)));
|
||||
vars.put("MINECRAFT_VERSION", FileUtils.getAbsolutePath(gameRepository.getVersionJar(version)));
|
||||
vars.put("ROOT", FileUtils.getAbsolutePath(gameRepository.getBaseDirectory()));
|
||||
vars.put("INSTALLER", installer.toAbsolutePath().toString());
|
||||
vars.put("LIBRARY_DIR", gameRepository.getLibrariesDirectory(version).getAbsolutePath());
|
||||
vars.put("LIBRARY_DIR", FileUtils.getAbsolutePath(gameRepository.getLibrariesDirectory(version)));
|
||||
|
||||
updateProgress(0, processors.size());
|
||||
|
||||
|
||||
@@ -35,7 +35,6 @@ import org.jenkinsci.constant_pool_scanner.ConstantPoolScanner;
|
||||
import org.jenkinsci.constant_pool_scanner.ConstantType;
|
||||
import org.jenkinsci.constant_pool_scanner.Utf8Constant;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.FileSystem;
|
||||
import java.nio.file.Files;
|
||||
@@ -131,7 +130,7 @@ public final class OptiFineInstallTask extends Task<Version> {
|
||||
List<Library> libraries = new ArrayList<>(4);
|
||||
libraries.add(optiFineLibrary);
|
||||
|
||||
Path optiFineInstallerLibraryPath = gameRepository.getLibraryFile(version, optiFineInstallerLibrary).toPath();
|
||||
Path optiFineInstallerLibraryPath = gameRepository.getLibraryFile(version, optiFineInstallerLibrary);
|
||||
FileUtils.copyFile(dest, optiFineInstallerLibraryPath);
|
||||
|
||||
try (FileSystem fs2 = CompressingUtils.createWritableZipFileSystem(optiFineInstallerLibraryPath)) {
|
||||
@@ -141,14 +140,14 @@ public final class OptiFineInstallTask extends Task<Version> {
|
||||
// Install launch wrapper modified by OptiFine
|
||||
boolean hasLaunchWrapper = false;
|
||||
try (FileSystem fs = CompressingUtils.createReadOnlyZipFileSystem(dest)) {
|
||||
Path optiFineLibraryPath = gameRepository.getLibraryFile(version, optiFineLibrary).toPath();
|
||||
Path optiFineLibraryPath = gameRepository.getLibraryFile(version, optiFineLibrary);
|
||||
if (Files.exists(fs.getPath("optifine/Patcher.class"))) {
|
||||
String[] command = {
|
||||
JavaRuntime.getDefault().getBinary().toString(),
|
||||
"-cp",
|
||||
dest.toString(),
|
||||
"optifine.Patcher",
|
||||
gameRepository.getVersionJar(version).getAbsolutePath(),
|
||||
gameRepository.getVersionJar(version).toAbsolutePath().normalize().toString(),
|
||||
dest.toString(),
|
||||
optiFineLibraryPath.toString()
|
||||
};
|
||||
@@ -166,9 +165,9 @@ public final class OptiFineInstallTask extends Task<Version> {
|
||||
Path launchWrapper2 = fs.getPath("launchwrapper-2.0.jar");
|
||||
if (Files.exists(launchWrapper2)) {
|
||||
Library launchWrapper = new Library(new Artifact("optifine", "launchwrapper", "2.0"));
|
||||
File launchWrapperFile = gameRepository.getLibraryFile(version, launchWrapper);
|
||||
Files.createDirectories(launchWrapperFile.toPath().toAbsolutePath().getParent());
|
||||
FileUtils.copyFile(launchWrapper2, launchWrapperFile.toPath());
|
||||
Path launchWrapperFile = gameRepository.getLibraryFile(version, launchWrapper);
|
||||
Files.createDirectories(launchWrapperFile.toAbsolutePath().getParent());
|
||||
FileUtils.copyFile(launchWrapper2, launchWrapperFile);
|
||||
hasLaunchWrapper = true;
|
||||
libraries.add(launchWrapper);
|
||||
}
|
||||
@@ -181,9 +180,9 @@ public final class OptiFineInstallTask extends Task<Version> {
|
||||
Library launchWrapper = new Library(new Artifact("optifine", "launchwrapper-of", launchWrapperVersion));
|
||||
|
||||
if (Files.exists(launchWrapperJar)) {
|
||||
File launchWrapperFile = gameRepository.getLibraryFile(version, launchWrapper);
|
||||
Files.createDirectories(launchWrapperFile.toPath().toAbsolutePath().getParent());
|
||||
FileUtils.copyFile(launchWrapperJar, launchWrapperFile.toPath());
|
||||
Path launchWrapperFile = gameRepository.getLibraryFile(version, launchWrapper);
|
||||
Files.createDirectories(launchWrapperFile.toAbsolutePath().getParent());
|
||||
FileUtils.copyFile(launchWrapperJar, launchWrapperFile);
|
||||
|
||||
hasLaunchWrapper = true;
|
||||
libraries.add(launchWrapper);
|
||||
|
||||
@@ -59,7 +59,7 @@ public final class QuiltAPIInstallTask extends Task<Version> {
|
||||
public void execute() throws IOException {
|
||||
dependencies.add(new FileDownloadTask(
|
||||
remote.getVersion().getFile().getUrl(),
|
||||
dependencyManager.getGameRepository().getRunDirectory(version.getId()).toPath().resolve("mods").resolve("quilt-api-" + remote.getVersion().getVersion() + ".jar"),
|
||||
dependencyManager.getGameRepository().getRunDirectory(version.getId()).resolve("mods").resolve("quilt-api-" + remote.getVersion().getVersion() + ".jar"),
|
||||
remote.getVersion().getFile().getIntegrityCheck())
|
||||
);
|
||||
}
|
||||
|
||||
@@ -17,16 +17,15 @@
|
||||
*/
|
||||
package org.jackhuang.hmcl.game;
|
||||
|
||||
import java.io.File;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.time.Instant;
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* The Minecraft version for 1.5.x and earlier.
|
||||
*
|
||||
* @author huangyuhui
|
||||
*/
|
||||
public class ClassicVersion extends Version {
|
||||
/// The Minecraft version for 1.5.x and earlier.
|
||||
///
|
||||
/// @author huangyuhui
|
||||
public final class ClassicVersion extends Version {
|
||||
|
||||
public ClassicVersion() {
|
||||
super(true, "Classic", null, null, "${auth_player_name} ${auth_session} --workDir ${game_directory}",
|
||||
@@ -35,8 +34,7 @@ public class ClassicVersion extends Version {
|
||||
null, null, null, ReleaseType.UNKNOWN, Instant.now(), Instant.now(), 0, false, false, null);
|
||||
}
|
||||
|
||||
private static class ClassicLibrary extends Library {
|
||||
|
||||
private static final class ClassicLibrary extends Library {
|
||||
public ClassicLibrary(String name) {
|
||||
super(new Artifact("", "", ""), null,
|
||||
new LibrariesDownloadInfo(new LibraryDownloadInfo("bin/" + name + ".jar"), null),
|
||||
@@ -44,11 +42,11 @@ public class ClassicVersion extends Version {
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean hasClassicVersion(File baseDirectory) {
|
||||
File bin = new File(baseDirectory, "bin");
|
||||
return bin.exists()
|
||||
&& new File(bin, "lwjgl.jar").exists()
|
||||
&& new File(bin, "jinput.jar").exists()
|
||||
&& new File(bin, "lwjgl_util.jar").exists();
|
||||
public static boolean hasClassicVersion(Path baseDirectory) {
|
||||
Path bin = baseDirectory.resolve("bin");
|
||||
return Files.isDirectory(bin)
|
||||
&& Files.exists(bin.resolve("lwjgl.jar"))
|
||||
&& Files.exists(bin.resolve("jinput.jar"))
|
||||
&& Files.exists(bin.resolve("lwjgl_util.jar"));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,11 +32,11 @@ import org.jackhuang.hmcl.util.io.FileUtils;
|
||||
import org.jackhuang.hmcl.util.platform.Platform;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.InvalidPathException;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.StandardCopyOption;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.stream.Stream;
|
||||
@@ -50,19 +50,19 @@ import static org.jackhuang.hmcl.util.logging.Logger.LOG;
|
||||
*/
|
||||
public class DefaultGameRepository implements GameRepository {
|
||||
|
||||
private File baseDirectory;
|
||||
private Path baseDirectory;
|
||||
protected Map<String, Version> versions;
|
||||
private final ConcurrentHashMap<File, Optional<String>> gameVersions = new ConcurrentHashMap<>();
|
||||
private final ConcurrentHashMap<Path, Optional<String>> gameVersions = new ConcurrentHashMap<>();
|
||||
|
||||
public DefaultGameRepository(File baseDirectory) {
|
||||
public DefaultGameRepository(Path baseDirectory) {
|
||||
this.baseDirectory = baseDirectory;
|
||||
}
|
||||
|
||||
public File getBaseDirectory() {
|
||||
public Path getBaseDirectory() {
|
||||
return baseDirectory;
|
||||
}
|
||||
|
||||
public void setBaseDirectory(File baseDirectory) {
|
||||
public void setBaseDirectory(Path baseDirectory) {
|
||||
this.baseDirectory = baseDirectory;
|
||||
}
|
||||
|
||||
@@ -89,25 +89,25 @@ public class DefaultGameRepository implements GameRepository {
|
||||
}
|
||||
|
||||
@Override
|
||||
public File getLibrariesDirectory(Version version) {
|
||||
return new File(getBaseDirectory(), "libraries");
|
||||
public Path getLibrariesDirectory(Version version) {
|
||||
return getBaseDirectory().resolve("libraries");
|
||||
}
|
||||
|
||||
@Override
|
||||
public File getLibraryFile(Version version, Library lib) {
|
||||
public Path getLibraryFile(Version version, Library lib) {
|
||||
if ("local".equals(lib.getHint())) {
|
||||
if (lib.getFileName() != null) {
|
||||
return new File(getVersionRoot(version.getId()), "libraries/" + lib.getFileName());
|
||||
return getVersionRoot(version.getId()).resolve("libraries/" + lib.getFileName());
|
||||
}
|
||||
|
||||
return new File(getVersionRoot(version.getId()), "libraries/" + lib.getArtifact().getFileName());
|
||||
return getVersionRoot(version.getId()).resolve("libraries/" + lib.getArtifact().getFileName());
|
||||
}
|
||||
|
||||
return new File(getLibrariesDirectory(version), lib.getPath());
|
||||
return getLibrariesDirectory(version).resolve(lib.getPath());
|
||||
}
|
||||
|
||||
public Path getArtifactFile(Version version, Artifact artifact) {
|
||||
return artifact.getPath(getBaseDirectory().toPath().resolve("libraries"));
|
||||
return artifact.getPath(getBaseDirectory().resolve("libraries"));
|
||||
}
|
||||
|
||||
public GameDirectoryType getGameDirectoryType(String id) {
|
||||
@@ -115,22 +115,19 @@ public class DefaultGameRepository implements GameRepository {
|
||||
}
|
||||
|
||||
@Override
|
||||
public File getRunDirectory(String id) {
|
||||
switch (getGameDirectoryType(id)) {
|
||||
case VERSION_FOLDER:
|
||||
return getVersionRoot(id);
|
||||
case ROOT_FOLDER:
|
||||
return getBaseDirectory();
|
||||
default:
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
public Path getRunDirectory(String id) {
|
||||
return switch (getGameDirectoryType(id)) {
|
||||
case VERSION_FOLDER -> getVersionRoot(id);
|
||||
case ROOT_FOLDER -> getBaseDirectory();
|
||||
default -> throw new IllegalStateException();
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public File getVersionJar(Version version) {
|
||||
public Path getVersionJar(Version version) {
|
||||
Version v = version.resolve(this);
|
||||
String id = Optional.ofNullable(v.getJar()).orElse(v.getId());
|
||||
return new File(getVersionRoot(id), id + ".jar");
|
||||
return getVersionRoot(id).resolve(id + ".jar");
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -140,33 +137,33 @@ public class DefaultGameRepository implements GameRepository {
|
||||
// be consistent.
|
||||
return gameVersions.computeIfAbsent(getVersionJar(version), versionJar -> {
|
||||
Optional<String> gameVersion = GameVersion.minecraftVersion(versionJar);
|
||||
if (!gameVersion.isPresent()) {
|
||||
LOG.warning("Cannot find out game version of " + version.getId() + ", primary jar: " + versionJar.toString() + ", jar exists: " + versionJar.exists());
|
||||
if (gameVersion.isEmpty()) {
|
||||
LOG.warning("Cannot find out game version of " + version.getId() + ", primary jar: " + versionJar.toString() + ", jar exists: " + Files.exists(versionJar));
|
||||
}
|
||||
return gameVersion;
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public File getNativeDirectory(String id, Platform platform) {
|
||||
return new File(getVersionRoot(id), "natives-" + platform);
|
||||
public Path getNativeDirectory(String id, Platform platform) {
|
||||
return getVersionRoot(id).resolve("natives-" + platform);
|
||||
}
|
||||
|
||||
@Override
|
||||
public File getVersionRoot(String id) {
|
||||
return new File(getBaseDirectory(), "versions/" + id);
|
||||
public Path getVersionRoot(String id) {
|
||||
return getBaseDirectory().resolve("versions/" + id);
|
||||
}
|
||||
|
||||
public File getVersionJson(String id) {
|
||||
return new File(getVersionRoot(id), id + ".json");
|
||||
public Path getVersionJson(String id) {
|
||||
return getVersionRoot(id).resolve(id + ".json");
|
||||
}
|
||||
|
||||
public Version readVersionJson(String id) throws IOException, JsonParseException {
|
||||
return readVersionJson(getVersionJson(id));
|
||||
}
|
||||
|
||||
public Version readVersionJson(File file) throws IOException, JsonParseException {
|
||||
String jsonText = Files.readString(file.toPath());
|
||||
public Version readVersionJson(Path file) throws IOException, JsonParseException {
|
||||
String jsonText = Files.readString(file);
|
||||
try {
|
||||
// Try TLauncher version json format
|
||||
return JsonUtils.fromNonNullJson(jsonText, TLauncherVersion.class).toVersion();
|
||||
@@ -179,7 +176,7 @@ public class DefaultGameRepository implements GameRepository {
|
||||
} catch (JsonParseException ignored) {
|
||||
}
|
||||
|
||||
LOG.warning("Cannot parse version json: " + file.toString() + "\n" + jsonText);
|
||||
LOG.warning("Cannot parse version json: " + file + "\n" + jsonText);
|
||||
throw new JsonParseException("Version json incorrect");
|
||||
}
|
||||
|
||||
@@ -190,8 +187,8 @@ public class DefaultGameRepository implements GameRepository {
|
||||
|
||||
try {
|
||||
Version fromVersion = getVersion(from);
|
||||
Path fromDir = getVersionRoot(from).toPath();
|
||||
Path toDir = getVersionRoot(to).toPath();
|
||||
Path fromDir = getVersionRoot(from);
|
||||
Path toDir = getVersionRoot(to);
|
||||
Files.move(fromDir, toDir);
|
||||
|
||||
Path fromJson = toDir.resolve(from + ".json");
|
||||
@@ -219,7 +216,7 @@ public class DefaultGameRepository implements GameRepository {
|
||||
// fix inheritsFrom of versions that inherits from version [from].
|
||||
for (Version version : getVersions()) {
|
||||
if (from.equals(version.getInheritsFrom())) {
|
||||
Path targetPath = getVersionJson(version.getId()).toPath();
|
||||
Path targetPath = getVersionJson(version.getId());
|
||||
Files.createDirectories(targetPath.getParent());
|
||||
JsonUtils.writeToJsonFile(targetPath, version.setInheritsFrom(to));
|
||||
}
|
||||
@@ -235,25 +232,29 @@ public class DefaultGameRepository implements GameRepository {
|
||||
if (EventBus.EVENT_BUS.fireEvent(new RemoveVersionEvent(this, id)) == Event.Result.DENY)
|
||||
return false;
|
||||
if (!versions.containsKey(id))
|
||||
return FileUtils.deleteDirectoryQuietly(getVersionRoot(id).toPath());
|
||||
File file = getVersionRoot(id);
|
||||
if (!file.exists())
|
||||
return FileUtils.deleteDirectoryQuietly(getVersionRoot(id));
|
||||
Path file = getVersionRoot(id);
|
||||
if (Files.notExists(file))
|
||||
return true;
|
||||
// test if no file in this version directory is occupied.
|
||||
File removedFile = new File(file.getAbsoluteFile().getParentFile(), file.getName() + "_removed");
|
||||
if (!file.renameTo(removedFile))
|
||||
Path removedFile = file.toAbsolutePath().resolveSibling(FileUtils.getName(file) + "_removed");
|
||||
try {
|
||||
Files.move(file, removedFile, StandardCopyOption.REPLACE_EXISTING);
|
||||
} catch (IOException e) {
|
||||
LOG.warning("Failed to rename file " + file, e);
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
versions.remove(id);
|
||||
|
||||
if (FileUtils.moveToTrash(removedFile.toPath())) {
|
||||
if (FileUtils.moveToTrash(removedFile)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// remove json files first to ensure HMCL will not recognize this folder as a valid version.
|
||||
|
||||
for (Path path : FileUtils.listFilesByExtension(removedFile.toPath(), "json")) {
|
||||
for (Path path : FileUtils.listFilesByExtension(removedFile, "json")) {
|
||||
try {
|
||||
Files.delete(path);
|
||||
} catch (IOException e) {
|
||||
@@ -263,7 +264,7 @@ public class DefaultGameRepository implements GameRepository {
|
||||
|
||||
// remove the version from version list regardless of whether the directory was removed successfully or not.
|
||||
try {
|
||||
FileUtils.deleteDirectory(removedFile.toPath());
|
||||
FileUtils.deleteDirectory(removedFile);
|
||||
} catch (IOException e) {
|
||||
LOG.warning("Unable to remove version folder: " + file, e);
|
||||
}
|
||||
@@ -283,83 +284,95 @@ public class DefaultGameRepository implements GameRepository {
|
||||
|
||||
SimpleVersionProvider provider = new SimpleVersionProvider();
|
||||
|
||||
File[] files = new File(getBaseDirectory(), "versions").listFiles();
|
||||
if (files != null)
|
||||
Arrays.stream(files).parallel().filter(File::isDirectory).flatMap(dir -> {
|
||||
String id = dir.getName();
|
||||
File json = new File(dir, id + ".json");
|
||||
Path versionsDir = getBaseDirectory().resolve("versions");
|
||||
if (Files.isDirectory(versionsDir)) {
|
||||
try (Stream<Path> stream = Files.list(versionsDir)) {
|
||||
stream.parallel().filter(Files::isDirectory).flatMap(dir -> {
|
||||
String id = FileUtils.getName(dir);
|
||||
Path json = dir.resolve(id + ".json");
|
||||
|
||||
// If user renamed the json file by mistake or created the json file in a wrong name,
|
||||
// we will find the only json and rename it to correct name.
|
||||
if (!json.exists()) {
|
||||
List<Path> jsons = FileUtils.listFilesByExtension(dir.toPath(), "json");
|
||||
if (jsons.size() == 1) {
|
||||
LOG.info("Renaming json file " + jsons.get(0) + " to " + json);
|
||||
if (!jsons.get(0).toFile().renameTo(json)) {
|
||||
LOG.warning("Cannot rename json file, ignoring version " + id);
|
||||
// If user renamed the json file by mistake or created the json file in a wrong name,
|
||||
// we will find the only json and rename it to correct name.
|
||||
if (Files.notExists(json)) {
|
||||
List<Path> jsons = FileUtils.listFilesByExtension(dir, "json");
|
||||
if (jsons.size() == 1) {
|
||||
LOG.info("Renaming json file " + jsons.get(0) + " to " + json);
|
||||
|
||||
try {
|
||||
Files.move(jsons.get(0), json);
|
||||
} catch (IOException e) {
|
||||
LOG.warning("Cannot rename json file, ignoring version " + id, e);
|
||||
return Stream.empty();
|
||||
}
|
||||
|
||||
Path jar = dir.resolve(FileUtils.getNameWithoutExtension(jsons.get(0)) + ".jar");
|
||||
if (Files.exists(jar)) {
|
||||
try {
|
||||
Files.move(jar, dir.resolve(id + ".jar"));
|
||||
} catch (IOException e) {
|
||||
LOG.warning("Cannot rename jar file, ignoring version " + id, e);
|
||||
return Stream.empty();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
LOG.info("No available json file found, ignoring version " + id);
|
||||
return Stream.empty();
|
||||
}
|
||||
|
||||
File jar = new File(dir, FileUtils.getNameWithoutExtension(jsons.get(0)) + ".jar");
|
||||
if (jar.exists() && !jar.renameTo(new File(dir, id + ".jar"))) {
|
||||
LOG.warning("Cannot rename jar file, ignoring version " + id);
|
||||
return Stream.empty();
|
||||
}
|
||||
} else {
|
||||
LOG.info("No available json file found, ignoring version " + id);
|
||||
return Stream.empty();
|
||||
}
|
||||
}
|
||||
|
||||
Version version;
|
||||
try {
|
||||
version = readVersionJson(json);
|
||||
} catch (Exception e) {
|
||||
LOG.warning("Malformed version json " + id, e);
|
||||
// JsonSyntaxException or IOException or NullPointerException(!!)
|
||||
if (EventBus.EVENT_BUS.fireEvent(new GameJsonParseFailedEvent(this, json, id)) != Event.Result.ALLOW)
|
||||
return Stream.empty();
|
||||
|
||||
Version version;
|
||||
try {
|
||||
version = readVersionJson(json);
|
||||
} catch (Exception e2) {
|
||||
LOG.error("User corrected version json is still malformed", e2);
|
||||
return Stream.empty();
|
||||
}
|
||||
}
|
||||
|
||||
if (!id.equals(version.getId())) {
|
||||
try {
|
||||
String from = id;
|
||||
String to = version.getId();
|
||||
Path fromDir = getVersionRoot(from).toPath();
|
||||
Path toDir = getVersionRoot(to).toPath();
|
||||
Files.move(fromDir, toDir);
|
||||
|
||||
Path fromJson = toDir.resolve(from + ".json");
|
||||
Path fromJar = toDir.resolve(from + ".jar");
|
||||
Path toJson = toDir.resolve(to + ".json");
|
||||
Path toJar = toDir.resolve(to + ".jar");
|
||||
} catch (Exception e) {
|
||||
LOG.warning("Malformed version json " + id, e);
|
||||
// JsonSyntaxException or IOException or NullPointerException(!!)
|
||||
if (EventBus.EVENT_BUS.fireEvent(new GameJsonParseFailedEvent(this, json.toFile(), id)) != Event.Result.ALLOW)
|
||||
return Stream.empty();
|
||||
|
||||
try {
|
||||
Files.move(fromJson, toJson);
|
||||
if (Files.exists(fromJar))
|
||||
Files.move(fromJar, toJar);
|
||||
} catch (IOException e) {
|
||||
// recovery
|
||||
Lang.ignoringException(() -> Files.move(toJson, fromJson));
|
||||
Lang.ignoringException(() -> Files.move(toJar, fromJar));
|
||||
Lang.ignoringException(() -> Files.move(toDir, fromDir));
|
||||
throw e;
|
||||
version = readVersionJson(json);
|
||||
} catch (Exception e2) {
|
||||
LOG.error("User corrected version json is still malformed", e2);
|
||||
return Stream.empty();
|
||||
}
|
||||
} catch (IOException e) {
|
||||
LOG.warning("Ignoring version " + version.getId() + " because version id does not match folder name " + id + ", and we cannot correct it.", e);
|
||||
return Stream.empty();
|
||||
}
|
||||
}
|
||||
|
||||
return Stream.of(version);
|
||||
}).forEachOrdered(provider::addVersion);
|
||||
if (!id.equals(version.getId())) {
|
||||
try {
|
||||
String from = id;
|
||||
String to = version.getId();
|
||||
Path fromDir = getVersionRoot(from);
|
||||
Path toDir = getVersionRoot(to);
|
||||
Files.move(fromDir, toDir);
|
||||
|
||||
Path fromJson = toDir.resolve(from + ".json");
|
||||
Path fromJar = toDir.resolve(from + ".jar");
|
||||
Path toJson = toDir.resolve(to + ".json");
|
||||
Path toJar = toDir.resolve(to + ".jar");
|
||||
|
||||
try {
|
||||
Files.move(fromJson, toJson);
|
||||
if (Files.exists(fromJar))
|
||||
Files.move(fromJar, toJar);
|
||||
} catch (IOException e) {
|
||||
// recovery
|
||||
Lang.ignoringException(() -> Files.move(toJson, fromJson));
|
||||
Lang.ignoringException(() -> Files.move(toJar, fromJar));
|
||||
Lang.ignoringException(() -> Files.move(toDir, fromDir));
|
||||
throw e;
|
||||
}
|
||||
} catch (IOException e) {
|
||||
LOG.warning("Ignoring version " + version.getId() + " because version id does not match folder name " + id + ", and we cannot correct it.", e);
|
||||
return Stream.empty();
|
||||
}
|
||||
}
|
||||
|
||||
return Stream.of(version);
|
||||
}).forEachOrdered(provider::addVersion);
|
||||
} catch (IOException e) {
|
||||
LOG.warning("Failed to load versions from " + versionsDir, e);
|
||||
}
|
||||
}
|
||||
|
||||
for (Version version : provider.getVersionMap().values()) {
|
||||
try {
|
||||
@@ -407,7 +420,7 @@ public class DefaultGameRepository implements GameRepository {
|
||||
|
||||
@Override
|
||||
public Path getAssetDirectory(String version, String assetId) {
|
||||
return getBaseDirectory().toPath().resolve("assets");
|
||||
return getBaseDirectory().resolve("assets");
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -456,7 +469,7 @@ public class DefaultGameRepository implements GameRepository {
|
||||
return assetsDir;
|
||||
|
||||
if (index.isVirtual()) {
|
||||
Path resourcesDir = getRunDirectory(version).toPath().resolve("resources");
|
||||
Path resourcesDir = getRunDirectory(version).resolve("resources");
|
||||
|
||||
int cnt = 0;
|
||||
int tot = index.getObjects().size();
|
||||
@@ -499,8 +512,8 @@ public class DefaultGameRepository implements GameRepository {
|
||||
return versions != null;
|
||||
}
|
||||
|
||||
public File getModpackConfiguration(String version) {
|
||||
return new File(getVersionRoot(version), "modpack.json");
|
||||
public Path getModpackConfiguration(String version) {
|
||||
return getVersionRoot(version).resolve("modpack.json");
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -514,13 +527,13 @@ public class DefaultGameRepository implements GameRepository {
|
||||
@Nullable
|
||||
public ModpackConfiguration<?> readModpackConfiguration(String version) throws IOException, VersionNotFoundException {
|
||||
if (!hasVersion(version)) throw new VersionNotFoundException(version);
|
||||
File file = getModpackConfiguration(version);
|
||||
if (!file.exists()) return null;
|
||||
return JsonUtils.fromJsonFile(file.toPath(), ModpackConfiguration.class);
|
||||
Path file = getModpackConfiguration(version);
|
||||
if (Files.notExists(file)) return null;
|
||||
return JsonUtils.fromJsonFile(file, ModpackConfiguration.class);
|
||||
}
|
||||
|
||||
public boolean isModpack(String version) {
|
||||
return getModpackConfiguration(version).exists();
|
||||
return Files.exists(getModpackConfiguration(version));
|
||||
}
|
||||
|
||||
public ModManager getModManager(String version) {
|
||||
@@ -528,15 +541,15 @@ public class DefaultGameRepository implements GameRepository {
|
||||
}
|
||||
|
||||
public Path getSavesDirectory(String id) {
|
||||
return getRunDirectory(id).toPath().resolve("saves");
|
||||
return getRunDirectory(id).resolve("saves");
|
||||
}
|
||||
|
||||
public Path getBackupsDirectory(String id) {
|
||||
return getRunDirectory(id).toPath().resolve("backups");
|
||||
return getRunDirectory(id).resolve("backups");
|
||||
}
|
||||
|
||||
public Path getSchematicsDirectory(String id) {
|
||||
return getRunDirectory(id).toPath().resolve("schematics");
|
||||
return getRunDirectory(id).resolve("schematics");
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -18,10 +18,11 @@
|
||||
package org.jackhuang.hmcl.game;
|
||||
|
||||
import org.jackhuang.hmcl.task.Task;
|
||||
import org.jackhuang.hmcl.util.io.FileUtils;
|
||||
import org.jackhuang.hmcl.util.platform.Platform;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.Collection;
|
||||
import java.util.LinkedHashSet;
|
||||
@@ -30,7 +31,7 @@ import java.util.Set;
|
||||
|
||||
/**
|
||||
* Supports operations on versioning.
|
||||
*
|
||||
* <p>
|
||||
* Note that game repository will not do any operations which need connection with Internet, if do,
|
||||
* see {@link org.jackhuang.hmcl.download.DependencyManager}
|
||||
*
|
||||
@@ -79,7 +80,7 @@ public interface GameRepository extends VersionProvider {
|
||||
|
||||
/**
|
||||
* Load version list.
|
||||
*
|
||||
* <p>
|
||||
* This method should be called before launching a version.
|
||||
* A time-costly operation.
|
||||
* You'd better execute this method in a new thread.
|
||||
@@ -95,16 +96,16 @@ public interface GameRepository extends VersionProvider {
|
||||
* The root folders the versions must be unique.
|
||||
* For example, .minecraft/versions/<version name>/.
|
||||
*/
|
||||
File getVersionRoot(String id);
|
||||
Path getVersionRoot(String id);
|
||||
|
||||
/**
|
||||
* Gets the current running directory of the given version for game.
|
||||
*
|
||||
* @param id the version id
|
||||
*/
|
||||
File getRunDirectory(String id);
|
||||
Path getRunDirectory(String id);
|
||||
|
||||
File getLibrariesDirectory(Version version);
|
||||
Path getLibrariesDirectory(Version version);
|
||||
|
||||
/**
|
||||
* Get the library file in disk.
|
||||
@@ -114,11 +115,11 @@ public interface GameRepository extends VersionProvider {
|
||||
* @param lib the library, {@link Version#getLibraries()}
|
||||
* @return the library file
|
||||
*/
|
||||
File getLibraryFile(Version version, Library lib);
|
||||
Path getLibraryFile(Version version, Library lib);
|
||||
|
||||
/**
|
||||
* Get the directory that native libraries will be unzipped to.
|
||||
*
|
||||
* <p>
|
||||
* You'd better return a unique directory.
|
||||
* Or if it returns a temporary directory, {@link org.jackhuang.hmcl.launch.Launcher#makeLaunchScript} will fail.
|
||||
* If you do want to return a temporary directory, make {@link org.jackhuang.hmcl.launch.Launcher#makeLaunchScript}
|
||||
@@ -128,7 +129,7 @@ public interface GameRepository extends VersionProvider {
|
||||
* @param platform the platform of native libraries
|
||||
* @return the native directory
|
||||
*/
|
||||
File getNativeDirectory(String id, Platform platform);
|
||||
Path getNativeDirectory(String id, Platform platform);
|
||||
|
||||
/**
|
||||
* Get minecraft jar
|
||||
@@ -136,11 +137,11 @@ public interface GameRepository extends VersionProvider {
|
||||
* @param version resolvedVersion
|
||||
* @return the minecraft jar
|
||||
*/
|
||||
File getVersionJar(Version version);
|
||||
Path getVersionJar(Version version);
|
||||
|
||||
/**
|
||||
* Detect game version.
|
||||
*
|
||||
* <p>
|
||||
* This method is time-consuming, but the result will be cached.
|
||||
* Consider running this job in IO scheduler.
|
||||
*
|
||||
@@ -151,7 +152,7 @@ public interface GameRepository extends VersionProvider {
|
||||
|
||||
/**
|
||||
* Detect game version.
|
||||
*
|
||||
* <p>
|
||||
* This method is time-consuming, but the result will be cached.
|
||||
* Consider running this job in IO scheduler.
|
||||
*
|
||||
@@ -168,7 +169,7 @@ public interface GameRepository extends VersionProvider {
|
||||
* @param version version id
|
||||
* @return the minecraft jar
|
||||
*/
|
||||
default File getVersionJar(String version) throws VersionNotFoundException {
|
||||
default Path getVersionJar(String version) throws VersionNotFoundException {
|
||||
return getVersionJar(getVersion(version).resolve(this));
|
||||
}
|
||||
|
||||
@@ -254,9 +255,9 @@ public interface GameRepository extends VersionProvider {
|
||||
Set<String> classpath = new LinkedHashSet<>();
|
||||
for (Library library : version.getLibraries())
|
||||
if (library.appliesToCurrentEnvironment() && !library.isNative()) {
|
||||
File f = getLibraryFile(version, library);
|
||||
if (f.exists() && f.isFile())
|
||||
classpath.add(f.getAbsolutePath());
|
||||
Path f = getLibraryFile(version, library);
|
||||
if (Files.isRegularFile(f))
|
||||
classpath.add(FileUtils.getAbsolutePath(f));
|
||||
}
|
||||
return classpath;
|
||||
}
|
||||
|
||||
@@ -24,9 +24,10 @@ import org.jenkinsci.constant_pool_scanner.ConstantPoolScanner;
|
||||
import org.jenkinsci.constant_pool_scanner.ConstantType;
|
||||
import org.jenkinsci.constant_pool_scanner.StringConstant;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
@@ -89,11 +90,11 @@ final class GameVersion {
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
public static Optional<String> minecraftVersion(File file) {
|
||||
if (file == null || !file.exists() || !file.isFile() || !file.canRead())
|
||||
public static Optional<String> minecraftVersion(Path file) {
|
||||
if (file == null || !Files.isRegularFile(file))
|
||||
return Optional.empty();
|
||||
|
||||
try (ZipFile gameJar = new ZipFile(file)) {
|
||||
try (var gameJar = new ZipFile(file.toFile())) {
|
||||
ZipEntry versionJson = gameJar.getEntry("version.json");
|
||||
if (versionJson != null) {
|
||||
Optional<String> result = getVersionFromJson(gameJar.getInputStream(versionJson));
|
||||
|
||||
@@ -20,9 +20,9 @@ package org.jackhuang.hmcl.game;
|
||||
import org.jackhuang.hmcl.java.JavaRuntime;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.Serializable;
|
||||
import java.net.Proxy;
|
||||
import java.nio.file.Path;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
@@ -31,7 +31,7 @@ import java.util.*;
|
||||
*/
|
||||
public class LaunchOptions implements Serializable {
|
||||
|
||||
private File gameDir;
|
||||
private Path gameDir;
|
||||
private JavaRuntime java;
|
||||
private String versionName;
|
||||
private String versionType;
|
||||
@@ -68,7 +68,7 @@ public class LaunchOptions implements Serializable {
|
||||
/**
|
||||
* The game directory
|
||||
*/
|
||||
public File getGameDir() {
|
||||
public Path getGameDir() {
|
||||
return gameDir;
|
||||
}
|
||||
|
||||
@@ -314,7 +314,7 @@ public class LaunchOptions implements Serializable {
|
||||
return options.javaAgents;
|
||||
}
|
||||
|
||||
public Builder setGameDir(File gameDir) {
|
||||
public Builder setGameDir(Path gameDir) {
|
||||
options.gameDir = gameDir;
|
||||
return this;
|
||||
}
|
||||
|
||||
@@ -25,12 +25,14 @@ import org.jackhuang.hmcl.game.LibrariesDownloadInfo;
|
||||
import org.jackhuang.hmcl.game.Library;
|
||||
import org.jackhuang.hmcl.game.LibraryDownloadInfo;
|
||||
import org.jackhuang.hmcl.util.Immutable;
|
||||
import org.jackhuang.hmcl.util.gson.JsonSerializable;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@Immutable
|
||||
public class TLauncherLibrary {
|
||||
@JsonSerializable
|
||||
public final class TLauncherLibrary {
|
||||
|
||||
@SerializedName("name")
|
||||
private final Artifact name;
|
||||
|
||||
@@ -28,7 +28,7 @@ import java.time.Instant;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class TLauncherVersion implements Validation {
|
||||
public final class TLauncherVersion implements Validation {
|
||||
|
||||
private final String id;
|
||||
private final String minecraftArguments;
|
||||
|
||||
@@ -36,6 +36,7 @@ import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.nio.file.StandardCopyOption;
|
||||
import java.util.*;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
@@ -64,7 +65,7 @@ public class DefaultLauncher extends Launcher {
|
||||
this.analyzer = LibraryAnalyzer.analyze(version, repository.getGameVersion(version).orElse(null));
|
||||
}
|
||||
|
||||
private Command generateCommandLine(File nativeFolder) throws IOException {
|
||||
private Command generateCommandLine(Path nativeFolder) throws IOException {
|
||||
CommandBuilder res = new CommandBuilder();
|
||||
|
||||
switch (options.getProcessPriority()) {
|
||||
@@ -149,25 +150,25 @@ public class DefaultLauncher extends Launcher {
|
||||
|
||||
String formatMsgNoLookups = res.addDefault("-Dlog4j2.formatMsgNoLookups=", "true");
|
||||
if (!"-Dlog4j2.formatMsgNoLookups=false".equals(formatMsgNoLookups) && isUsingLog4j()) {
|
||||
res.addDefault("-Dlog4j.configurationFile=", getLog4jConfigurationFile().getAbsolutePath());
|
||||
res.addDefault("-Dlog4j.configurationFile=", FileUtils.getAbsolutePath(getLog4jConfigurationFile()));
|
||||
}
|
||||
|
||||
// Default JVM Args
|
||||
if (!options.isNoGeneratedJVMArgs()) {
|
||||
appendJvmArgs(res);
|
||||
|
||||
res.addDefault("-Dminecraft.client.jar=", repository.getVersionJar(version).toString());
|
||||
res.addDefault("-Dminecraft.client.jar=", FileUtils.getAbsolutePath(repository.getVersionJar(version)));
|
||||
|
||||
if (OperatingSystem.CURRENT_OS == OperatingSystem.MACOS) {
|
||||
res.addDefault("-Xdock:name=", "Minecraft " + version.getId());
|
||||
repository.getAssetObject(version.getId(), version.getAssetIndex().getId(), "icons/minecraft.icns")
|
||||
.ifPresent(minecraftIcns -> {
|
||||
res.addDefault("-Xdock:icon=", minecraftIcns.toAbsolutePath().toString());
|
||||
res.addDefault("-Xdock:icon=", FileUtils.getAbsolutePath(minecraftIcns));
|
||||
});
|
||||
}
|
||||
|
||||
if (OperatingSystem.CURRENT_OS != OperatingSystem.WINDOWS)
|
||||
res.addDefault("-Duser.home=", options.getGameDir().getAbsoluteFile().getParent());
|
||||
res.addDefault("-Duser.home=", options.getGameDir().toAbsolutePath().getParent().toString());
|
||||
|
||||
Proxy.Type proxyType = options.getProxyType();
|
||||
if (proxyType == null) {
|
||||
@@ -250,23 +251,23 @@ public class DefaultLauncher extends Launcher {
|
||||
classpath.removeIf(c -> c.contains("2.9.4-nightly-20150209"));
|
||||
}
|
||||
|
||||
File jar = repository.getVersionJar(version);
|
||||
if (!jar.exists() || !jar.isFile())
|
||||
Path jar = repository.getVersionJar(version);
|
||||
if (!Files.isRegularFile(jar))
|
||||
throw new IOException("Minecraft jar does not exist");
|
||||
classpath.add(jar.getAbsolutePath());
|
||||
classpath.add(FileUtils.getAbsolutePath(jar.toAbsolutePath()));
|
||||
|
||||
// Provided Minecraft arguments
|
||||
Path gameAssets = repository.getActualAssetDirectory(version.getId(), version.getAssetIndex().getId());
|
||||
Map<String, String> configuration = getConfigurations();
|
||||
configuration.put("${classpath}", String.join(File.pathSeparator, classpath));
|
||||
configuration.put("${game_assets}", gameAssets.toAbsolutePath().toString());
|
||||
configuration.put("${assets_root}", gameAssets.toAbsolutePath().toString());
|
||||
configuration.put("${game_assets}", FileUtils.getAbsolutePath(gameAssets));
|
||||
configuration.put("${assets_root}", FileUtils.getAbsolutePath(gameAssets));
|
||||
|
||||
Optional<String> gameVersion = repository.getGameVersion(version);
|
||||
|
||||
// lwjgl assumes path to native libraries encoded by ASCII.
|
||||
// Here is a workaround for this issue: https://github.com/HMCL-dev/HMCL/issues/1141.
|
||||
String nativeFolderPath = nativeFolder.getAbsolutePath();
|
||||
String nativeFolderPath = FileUtils.getAbsolutePath(nativeFolder);
|
||||
Path tempNativeFolder = null;
|
||||
if ((OperatingSystem.CURRENT_OS == OperatingSystem.LINUX || OperatingSystem.CURRENT_OS == OperatingSystem.MACOS)
|
||||
&& !StringUtils.isASCII(nativeFolderPath)
|
||||
@@ -376,9 +377,9 @@ public class DefaultLauncher extends Launcher {
|
||||
protected void appendJvmArgs(CommandBuilder result) {
|
||||
}
|
||||
|
||||
public void decompressNatives(File destination) throws NotDecompressingNativesException {
|
||||
public void decompressNatives(Path destination) throws NotDecompressingNativesException {
|
||||
try {
|
||||
FileUtils.cleanDirectoryQuietly(destination.toPath());
|
||||
FileUtils.cleanDirectoryQuietly(destination);
|
||||
for (Library library : version.getLibraries())
|
||||
if (library.isNative())
|
||||
new Unzipper(repository.getLibraryFile(version, library), destination)
|
||||
@@ -408,12 +409,12 @@ public class DefaultLauncher extends Launcher {
|
||||
return GameVersionNumber.compare(repository.getGameVersion(version).orElse("1.7"), "1.7") >= 0;
|
||||
}
|
||||
|
||||
public File getLog4jConfigurationFile() {
|
||||
return new File(repository.getVersionRoot(version.getId()), "log4j2.xml");
|
||||
public Path getLog4jConfigurationFile() {
|
||||
return repository.getVersionRoot(version.getId()).resolve("log4j2.xml");
|
||||
}
|
||||
|
||||
public void extractLog4jConfigurationFile() throws IOException {
|
||||
File targetFile = getLog4jConfigurationFile();
|
||||
Path targetFile = getLog4jConfigurationFile();
|
||||
InputStream source;
|
||||
if (GameVersionNumber.asGameVersion(repository.getGameVersion(version)).compareTo("1.12") < 0) {
|
||||
source = DefaultLauncher.class.getResourceAsStream("/assets/game/log4j2-1.7.xml");
|
||||
@@ -421,8 +422,8 @@ public class DefaultLauncher extends Launcher {
|
||||
source = DefaultLauncher.class.getResourceAsStream("/assets/game/log4j2-1.12.xml");
|
||||
}
|
||||
|
||||
try (InputStream input = source; OutputStream output = new FileOutputStream(targetFile)) {
|
||||
input.transferTo(output);
|
||||
try (InputStream input = source) {
|
||||
Files.copy(input, targetFile, StandardCopyOption.REPLACE_EXISTING);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -436,35 +437,35 @@ public class DefaultLauncher extends Launcher {
|
||||
pair("${version_name}", Optional.ofNullable(options.getVersionName()).orElse(version.getId())),
|
||||
pair("${profile_name}", Optional.ofNullable(options.getProfileName()).orElse("Minecraft")),
|
||||
pair("${version_type}", Optional.ofNullable(options.getVersionType()).orElse(version.getType().getId())),
|
||||
pair("${game_directory}", repository.getRunDirectory(version.getId()).getAbsolutePath()),
|
||||
pair("${game_directory}", FileUtils.getAbsolutePath(repository.getRunDirectory(version.getId()))),
|
||||
pair("${user_type}", authInfo.getUserType()),
|
||||
pair("${assets_index_name}", version.getAssetIndex().getId()),
|
||||
pair("${user_properties}", authInfo.getUserProperties()),
|
||||
pair("${resolution_width}", options.getWidth().toString()),
|
||||
pair("${resolution_height}", options.getHeight().toString()),
|
||||
pair("${library_directory}", repository.getLibrariesDirectory(version).getAbsolutePath()),
|
||||
pair("${library_directory}", FileUtils.getAbsolutePath(repository.getLibrariesDirectory(version))),
|
||||
pair("${classpath_separator}", File.pathSeparator),
|
||||
pair("${primary_jar}", repository.getVersionJar(version).getAbsolutePath()),
|
||||
pair("${primary_jar}", FileUtils.getAbsolutePath(repository.getVersionJar(version))),
|
||||
pair("${language}", Locale.getDefault().toLanguageTag()),
|
||||
|
||||
// defined by HMCL
|
||||
// libraries_directory stands for historical reasons here. We don't know the official launcher
|
||||
// had already defined "library_directory" as the placeholder for path to ".minecraft/libraries"
|
||||
// when we propose this placeholder.
|
||||
pair("${libraries_directory}", repository.getLibrariesDirectory(version).getAbsolutePath()),
|
||||
pair("${libraries_directory}", FileUtils.getAbsolutePath(repository.getLibrariesDirectory(version))),
|
||||
// file_separator is used in -DignoreList
|
||||
pair("${file_separator}", File.separator),
|
||||
pair("${primary_jar_name}", FileUtils.getName(repository.getVersionJar(version).toPath()))
|
||||
pair("${primary_jar_name}", FileUtils.getName(repository.getVersionJar(version)))
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ManagedProcess launch() throws IOException, InterruptedException {
|
||||
File nativeFolder;
|
||||
Path nativeFolder;
|
||||
if (options.getNativesDirType() == NativesDirectoryType.VERSION_FOLDER) {
|
||||
nativeFolder = repository.getNativeDirectory(version.getId(), options.getJava().getPlatform());
|
||||
} else {
|
||||
nativeFolder = new File(options.getNativesDir());
|
||||
nativeFolder = Path.of(options.getNativesDir());
|
||||
}
|
||||
|
||||
final Command command = generateCommandLine(nativeFolder);
|
||||
@@ -474,7 +475,7 @@ public class DefaultLauncher extends Launcher {
|
||||
|
||||
if (command.tempNativeFolder != null) {
|
||||
Files.deleteIfExists(command.tempNativeFolder);
|
||||
Files.createSymbolicLink(command.tempNativeFolder, nativeFolder.toPath().toAbsolutePath());
|
||||
Files.createSymbolicLink(command.tempNativeFolder, nativeFolder.toAbsolutePath());
|
||||
}
|
||||
|
||||
if (rawCommandLine.stream().anyMatch(StringUtils::isBlank)) {
|
||||
@@ -488,22 +489,22 @@ public class DefaultLauncher extends Launcher {
|
||||
if (isUsingLog4j())
|
||||
extractLog4jConfigurationFile();
|
||||
|
||||
File runDirectory = repository.getRunDirectory(version.getId());
|
||||
Path runDirectory = repository.getRunDirectory(version.getId());
|
||||
|
||||
if (StringUtils.isNotBlank(options.getPreLaunchCommand())) {
|
||||
ProcessBuilder builder = new ProcessBuilder(StringUtils.tokenize(options.getPreLaunchCommand(), getEnvVars())).directory(runDirectory);
|
||||
ProcessBuilder builder = new ProcessBuilder(StringUtils.tokenize(options.getPreLaunchCommand(), getEnvVars())).directory(runDirectory.toFile());
|
||||
builder.environment().putAll(getEnvVars());
|
||||
SystemUtils.callExternalProcess(builder);
|
||||
}
|
||||
|
||||
Process process;
|
||||
try {
|
||||
ProcessBuilder builder = new ProcessBuilder(rawCommandLine).directory(runDirectory);
|
||||
ProcessBuilder builder = new ProcessBuilder(rawCommandLine).directory(runDirectory.toFile());
|
||||
if (listener == null) {
|
||||
builder.inheritIO();
|
||||
}
|
||||
String appdata = options.getGameDir().getAbsoluteFile().getParent();
|
||||
if (appdata != null) builder.environment().put("APPDATA", appdata);
|
||||
Path appdata = options.getGameDir().toAbsolutePath().getParent();
|
||||
if (appdata != null) builder.environment().put("APPDATA", appdata.toString());
|
||||
|
||||
builder.environment().putAll(getEnvVars());
|
||||
process = builder.start();
|
||||
@@ -522,8 +523,8 @@ public class DefaultLauncher extends Launcher {
|
||||
Map<String, String> env = new LinkedHashMap<>();
|
||||
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_DIR", FileUtils.getAbsolutePath(repository.getVersionRoot(version.getId())));
|
||||
env.put("INST_MC_DIR", FileUtils.getAbsolutePath(repository.getRunDirectory(version.getId())));
|
||||
env.put("INST_JAVA", options.getJava().getBinary().toString());
|
||||
|
||||
Renderer renderer = options.getRenderer();
|
||||
@@ -582,11 +583,11 @@ public class DefaultLauncher extends Launcher {
|
||||
public void makeLaunchScript(File scriptFile) throws IOException {
|
||||
boolean isWindows = OperatingSystem.WINDOWS == OperatingSystem.CURRENT_OS;
|
||||
|
||||
File nativeFolder;
|
||||
Path nativeFolder;
|
||||
if (options.getNativesDirType() == NativesDirectoryType.VERSION_FOLDER) {
|
||||
nativeFolder = repository.getNativeDirectory(version.getId(), options.getJava().getPlatform());
|
||||
} else {
|
||||
nativeFolder = new File(options.getNativesDir());
|
||||
nativeFolder = Path.of(options.getNativesDir());
|
||||
}
|
||||
|
||||
if (options.getNativesDirType() == NativesDirectoryType.VERSION_FOLDER) {
|
||||
@@ -640,9 +641,12 @@ public class DefaultLauncher extends Launcher {
|
||||
try (BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(outputStream, charset))) {
|
||||
if (usePowerShell) {
|
||||
if (isWindows) {
|
||||
writer.write("$Env:APPDATA=");
|
||||
writer.write(CommandBuilder.pwshString(options.getGameDir().getAbsoluteFile().getParent()));
|
||||
writer.newLine();
|
||||
Path appdata = options.getGameDir().toAbsolutePath().getParent();
|
||||
if (appdata != null) {
|
||||
writer.write("$Env:APPDATA=");
|
||||
writer.write(CommandBuilder.pwshString(appdata.toString()));
|
||||
writer.newLine();
|
||||
}
|
||||
}
|
||||
for (Map.Entry<String, String> entry : envVars.entrySet()) {
|
||||
writer.write("$Env:" + entry.getKey() + "=");
|
||||
@@ -650,7 +654,7 @@ public class DefaultLauncher extends Launcher {
|
||||
writer.newLine();
|
||||
}
|
||||
writer.write("Set-Location -Path ");
|
||||
writer.write(CommandBuilder.pwshString(repository.getRunDirectory(version.getId()).getAbsolutePath()));
|
||||
writer.write(CommandBuilder.pwshString(FileUtils.getAbsolutePath(repository.getRunDirectory(version.getId()))));
|
||||
writer.newLine();
|
||||
|
||||
|
||||
@@ -682,14 +686,19 @@ public class DefaultLauncher extends Launcher {
|
||||
if (isWindows) {
|
||||
writer.write("@echo off");
|
||||
writer.newLine();
|
||||
writer.write("set APPDATA=" + options.getGameDir().getAbsoluteFile().getParent());
|
||||
writer.newLine();
|
||||
|
||||
Path appdata = options.getGameDir().toAbsolutePath().getParent();
|
||||
if (appdata != null) {
|
||||
writer.write("set APPDATA=" + appdata);
|
||||
writer.newLine();
|
||||
}
|
||||
|
||||
for (Map.Entry<String, String> entry : envVars.entrySet()) {
|
||||
writer.write("set " + entry.getKey() + "=" + CommandBuilder.toBatchStringLiteral(entry.getValue()));
|
||||
writer.newLine();
|
||||
}
|
||||
writer.newLine();
|
||||
writer.write(new CommandBuilder().add("cd", "/D", repository.getRunDirectory(version.getId()).getAbsolutePath()).toString());
|
||||
writer.write(new CommandBuilder().add("cd", "/D", FileUtils.getAbsolutePath(repository.getRunDirectory(version.getId()))).toString());
|
||||
} else {
|
||||
writer.write("#!/usr/bin/env bash");
|
||||
writer.newLine();
|
||||
@@ -698,10 +707,10 @@ public class DefaultLauncher extends Launcher {
|
||||
writer.newLine();
|
||||
}
|
||||
if (commandLine.tempNativeFolder != null) {
|
||||
writer.write(new CommandBuilder().add("ln", "-s", nativeFolder.getAbsolutePath(), commandLine.tempNativeFolder.toString()).toString());
|
||||
writer.write(new CommandBuilder().add("ln", "-s", FileUtils.getAbsolutePath(nativeFolder), commandLine.tempNativeFolder.toString()).toString());
|
||||
writer.newLine();
|
||||
}
|
||||
writer.write(new CommandBuilder().add("cd", repository.getRunDirectory(version.getId()).getAbsolutePath()).toString());
|
||||
writer.write(new CommandBuilder().add("cd", FileUtils.getAbsolutePath(repository.getRunDirectory(version.getId()))).toString());
|
||||
}
|
||||
writer.newLine();
|
||||
if (StringUtils.isNotBlank(options.getPreLaunchCommand())) {
|
||||
@@ -751,7 +760,7 @@ public class DefaultLauncher extends Launcher {
|
||||
|
||||
if (StringUtils.isNotBlank(options.getPostExitCommand())) {
|
||||
try {
|
||||
ProcessBuilder builder = new ProcessBuilder(StringUtils.tokenize(options.getPostExitCommand(), getEnvVars())).directory(options.getGameDir());
|
||||
ProcessBuilder builder = new ProcessBuilder(StringUtils.tokenize(options.getPostExitCommand(), getEnvVars())).directory(options.getGameDir().toFile());
|
||||
builder.environment().putAll(getEnvVars());
|
||||
SystemUtils.callExternalProcess(builder);
|
||||
} catch (Throwable e) {
|
||||
|
||||
@@ -75,7 +75,7 @@ public final class ModManager {
|
||||
}
|
||||
|
||||
public Path getModsDirectory() {
|
||||
return repository.getRunDirectory(id).toPath().resolve("mods");
|
||||
return repository.getRunDirectory(id).resolve("mods");
|
||||
}
|
||||
|
||||
public LocalMod getLocalMod(String id, ModLoaderType modLoaderType) {
|
||||
|
||||
@@ -38,7 +38,7 @@ public class ModpackUpdateTask extends Task<Void> {
|
||||
this.id = id;
|
||||
this.updateTask = updateTask;
|
||||
|
||||
Path backup = repository.getBaseDirectory().toPath().resolve("backup");
|
||||
Path backup = repository.getBaseDirectory().resolve("backup");
|
||||
while (true) {
|
||||
int num = (int)(Math.random() * 10000000);
|
||||
if (!Files.exists(backup.resolve(id + "-" + num))) {
|
||||
@@ -55,7 +55,7 @@ public class ModpackUpdateTask extends Task<Void> {
|
||||
|
||||
@Override
|
||||
public void execute() throws Exception {
|
||||
FileUtils.copyDirectory(repository.getVersionRoot(id).toPath(), backupFolder);
|
||||
FileUtils.copyDirectory(repository.getVersionRoot(id), backupFolder);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -71,7 +71,7 @@ public class ModpackUpdateTask extends Task<Void> {
|
||||
// Restore backup
|
||||
repository.removeVersionFromDisk(id);
|
||||
|
||||
FileUtils.copyDirectory(backupFolder, repository.getVersionRoot(id).toPath());
|
||||
FileUtils.copyDirectory(backupFolder, repository.getVersionRoot(id));
|
||||
|
||||
repository.refreshVersionsAsync().start();
|
||||
}
|
||||
|
||||
@@ -31,6 +31,8 @@ import org.jackhuang.hmcl.util.gson.JsonUtils;
|
||||
import java.io.File;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
@@ -84,9 +86,9 @@ public final class CurseCompletionTask extends Task<Void> {
|
||||
|
||||
if (manifest == null)
|
||||
try {
|
||||
File manifestFile = new File(repository.getVersionRoot(version), "manifest.json");
|
||||
if (manifestFile.exists())
|
||||
this.manifest = JsonUtils.fromJsonFile(manifestFile.toPath(), CurseManifest.class);
|
||||
Path manifestFile = repository.getVersionRoot(version).resolve("manifest.json");
|
||||
if (Files.exists(manifestFile))
|
||||
this.manifest = JsonUtils.fromJsonFile(manifestFile, CurseManifest.class);
|
||||
} catch (Exception e) {
|
||||
LOG.warning("Unable to read CurseForge modpack manifest.json", e);
|
||||
}
|
||||
@@ -109,7 +111,7 @@ public final class CurseCompletionTask extends Task<Void> {
|
||||
if (manifest == null)
|
||||
return;
|
||||
|
||||
File root = repository.getVersionRoot(version);
|
||||
Path root = repository.getVersionRoot(version);
|
||||
|
||||
// Because in China, Curse is too difficult to visit,
|
||||
// if failed, ignore it and retry next time.
|
||||
@@ -135,17 +137,18 @@ public final class CurseCompletionTask extends Task<Void> {
|
||||
}
|
||||
})
|
||||
.collect(Collectors.toList()));
|
||||
JsonUtils.writeToJsonFile(root.toPath().resolve("manifest.json"), newManifest);
|
||||
JsonUtils.writeToJsonFile(root.resolve("manifest.json"), newManifest);
|
||||
|
||||
File versionRoot = repository.getVersionRoot(modManager.getVersion());
|
||||
File resourcePacksRoot = new File(versionRoot, "resourcepacks"), shaderPacksRoot = new File(versionRoot, "shaderpacks");
|
||||
Path versionRoot = repository.getVersionRoot(modManager.getVersion());
|
||||
Path resourcePacksRoot = versionRoot.resolve("resourcepacks");
|
||||
Path shaderPacksRoot = versionRoot.resolve("shaderpacks");
|
||||
finished.set(0);
|
||||
dependencies = newManifest.getFiles()
|
||||
.stream().parallel()
|
||||
.filter(f -> f.getFileName() != null)
|
||||
.flatMap(f -> {
|
||||
try {
|
||||
File path = guessFilePath(f, resourcePacksRoot, shaderPacksRoot);
|
||||
File path = guessFilePath(f, resourcePacksRoot.toFile(), shaderPacksRoot.toFile());
|
||||
if (path == null) {
|
||||
return Stream.empty();
|
||||
}
|
||||
@@ -171,9 +174,10 @@ public final class CurseCompletionTask extends Task<Void> {
|
||||
|
||||
/**
|
||||
* Guess where to store the file.
|
||||
* @param file The file.
|
||||
*
|
||||
* @param file The file.
|
||||
* @param resourcePacksRoot ./resourcepacks.
|
||||
* @param shaderPacksRoot ./shaderpacks.
|
||||
* @param shaderPacksRoot ./shaderpacks.
|
||||
* @return ./resourcepacks/$filename or ./shaderpacks/$filename or ./mods/$filename if the file doesn't exist. null if the file existed.
|
||||
* @throws IOException If IOException was encountered during getting data from CurseForge.
|
||||
*/
|
||||
|
||||
@@ -68,10 +68,10 @@ public final class CurseInstallTask extends Task<Void> {
|
||||
this.manifest = manifest;
|
||||
this.name = name;
|
||||
this.repository = dependencyManager.getGameRepository();
|
||||
this.run = repository.getRunDirectory(name);
|
||||
this.run = repository.getRunDirectory(name).toFile();
|
||||
|
||||
File json = repository.getModpackConfiguration(name);
|
||||
if (repository.hasVersion(name) && !json.exists())
|
||||
Path json = repository.getModpackConfiguration(name);
|
||||
if (repository.hasVersion(name) && Files.notExists(json))
|
||||
throw new IllegalArgumentException("Version " + name + " already exists.");
|
||||
|
||||
GameBuilder builder = dependencyManager.gameBuilder().name(name).gameVersion(manifest.getMinecraft().getGameVersion());
|
||||
@@ -97,8 +97,8 @@ public final class CurseInstallTask extends Task<Void> {
|
||||
|
||||
ModpackConfiguration<CurseManifest> config = null;
|
||||
try {
|
||||
if (json.exists()) {
|
||||
config = JsonUtils.fromJsonFile(json.toPath(), ModpackConfiguration.typeOf(CurseManifest.class));
|
||||
if (Files.exists(json)) {
|
||||
config = JsonUtils.fromJsonFile(json, ModpackConfiguration.typeOf(CurseManifest.class));
|
||||
|
||||
if (!CurseModpackProvider.INSTANCE.getName().equals(config.getType()))
|
||||
throw new IllegalArgumentException("Version " + name + " is not a Curse modpack. Cannot update this version.");
|
||||
@@ -107,7 +107,7 @@ public final class CurseInstallTask extends Task<Void> {
|
||||
}
|
||||
this.config = config;
|
||||
dependents.add(new ModpackInstallTask<>(zipFile, run, modpack.getEncoding(), Collections.singletonList(manifest.getOverrides()), any -> true, config).withStage("hmcl.modpack"));
|
||||
dependents.add(new MinecraftInstanceTask<>(zipFile, modpack.getEncoding(), Collections.singletonList(manifest.getOverrides()), manifest, CurseModpackProvider.INSTANCE, manifest.getName(), manifest.getVersion(), repository.getModpackConfiguration(name)).withStage("hmcl.modpack"));
|
||||
dependents.add(new MinecraftInstanceTask<>(zipFile, modpack.getEncoding(), Collections.singletonList(manifest.getOverrides()), manifest, CurseModpackProvider.INSTANCE, manifest.getName(), manifest.getVersion(), repository.getModpackConfiguration(name).toFile()).withStage("hmcl.modpack"));
|
||||
|
||||
dependencies.add(new CurseCompletionTask(dependencyManager, name, manifest));
|
||||
}
|
||||
@@ -136,7 +136,7 @@ public final class CurseInstallTask extends Task<Void> {
|
||||
}
|
||||
}
|
||||
|
||||
Path root = repository.getVersionRoot(name).toPath();
|
||||
Path root = repository.getVersionRoot(name);
|
||||
Files.createDirectories(root);
|
||||
JsonUtils.writeToJsonFile(root.resolve("manifest.json"), manifest);
|
||||
}
|
||||
|
||||
@@ -73,7 +73,7 @@ public class McbbsModpackCompletionTask extends CompletableFutureTask<Void> {
|
||||
this.repository = dependencyManager.getGameRepository();
|
||||
this.modManager = repository.getModManager(version);
|
||||
this.version = version;
|
||||
this.configurationFile = repository.getModpackConfiguration(version);
|
||||
this.configurationFile = repository.getModpackConfiguration(version).toFile();
|
||||
this.configuration = configuration;
|
||||
|
||||
setStage("hmcl.modpack.download");
|
||||
@@ -110,7 +110,7 @@ public class McbbsModpackCompletionTask extends CompletableFutureTask<Void> {
|
||||
throw new IOException("Unable to parse server manifest.json from " + manifest.getFileApi(), e);
|
||||
}
|
||||
|
||||
Path rootPath = repository.getVersionRoot(version).toPath();
|
||||
Path rootPath = repository.getVersionRoot(version);
|
||||
Files.createDirectories(rootPath);
|
||||
|
||||
Map<McbbsModpackManifest.File, McbbsModpackManifest.File> localFiles = manifest.getFiles().stream().collect(Collectors.toMap(Function.identity(), Function.identity()));
|
||||
@@ -172,7 +172,7 @@ public class McbbsModpackCompletionTask extends CompletableFutureTask<Void> {
|
||||
manifest = remoteManifest.setFiles(newFiles);
|
||||
return executor.all(tasks.stream().filter(Objects::nonNull).collect(Collectors.toList()));
|
||||
})).thenAcceptAsync(wrapConsumer(unused1 -> {
|
||||
Path manifestFile = repository.getModpackConfiguration(version).toPath();
|
||||
Path manifestFile = repository.getModpackConfiguration(version);
|
||||
JsonUtils.writeToJsonFile(manifestFile,
|
||||
new ModpackConfiguration<>(manifest, this.configuration.getType(), this.manifest.getName(), this.manifest.getVersion(),
|
||||
this.manifest.getFiles().stream()
|
||||
@@ -274,7 +274,7 @@ public class McbbsModpackCompletionTask extends CompletableFutureTask<Void> {
|
||||
@Nullable
|
||||
private Path getFilePath(McbbsModpackManifest.File file) {
|
||||
if (file instanceof McbbsModpackManifest.AddonFile) {
|
||||
return modManager.getRepository().getRunDirectory(modManager.getVersion()).toPath().resolve(((McbbsModpackManifest.AddonFile) file).getPath());
|
||||
return modManager.getRepository().getRunDirectory(modManager.getVersion()).resolve(((McbbsModpackManifest.AddonFile) file).getPath());
|
||||
} else if (file instanceof McbbsModpackManifest.CurseFile) {
|
||||
String fileName = ((McbbsModpackManifest.CurseFile) file).getFileName();
|
||||
if (fileName == null) return null;
|
||||
|
||||
@@ -67,7 +67,7 @@ public class McbbsModpackExportTask extends Task<Void> {
|
||||
blackList.add(version + ".json");
|
||||
LOG.info("Compressing game files without some files in blacklist, including files or directories: usernamecache.json, asm, logs, backups, versions, assets, usercache.json, libraries, crash-reports, launcher_profiles.json, NVIDIA, TCNodeTracker");
|
||||
try (Zipper zip = new Zipper(modpackFile.toPath())) {
|
||||
Path runDirectory = repository.getRunDirectory(version).toPath();
|
||||
Path runDirectory = repository.getRunDirectory(version);
|
||||
List<McbbsModpackManifest.File> files = new ArrayList<>();
|
||||
zip.putDirectory(runDirectory, "overrides", path -> {
|
||||
if (Modpack.acceptFile(path, blackList, info.getWhitelist())) {
|
||||
|
||||
@@ -31,6 +31,8 @@ import org.jackhuang.hmcl.util.gson.JsonUtils;
|
||||
|
||||
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.Collections;
|
||||
import java.util.List;
|
||||
@@ -56,10 +58,10 @@ public class McbbsModpackLocalInstallTask extends Task<Void> {
|
||||
this.manifest = manifest;
|
||||
this.name = name;
|
||||
this.repository = dependencyManager.getGameRepository();
|
||||
File run = repository.getRunDirectory(name);
|
||||
Path run = repository.getRunDirectory(name);
|
||||
|
||||
File json = repository.getModpackConfiguration(name);
|
||||
if (repository.hasVersion(name) && !json.exists())
|
||||
Path json = repository.getModpackConfiguration(name);
|
||||
if (repository.hasVersion(name) && Files.notExists(json))
|
||||
throw new IllegalArgumentException("Version " + name + " already exists.");
|
||||
this.update = repository.hasVersion(name);
|
||||
|
||||
@@ -77,16 +79,16 @@ public class McbbsModpackLocalInstallTask extends Task<Void> {
|
||||
|
||||
ModpackConfiguration<McbbsModpackManifest> config = null;
|
||||
try {
|
||||
if (json.exists()) {
|
||||
config = JsonUtils.fromJsonFile(json.toPath(), ModpackConfiguration.typeOf(McbbsModpackManifest.class));
|
||||
if (Files.exists(json)) {
|
||||
config = JsonUtils.fromJsonFile(json, ModpackConfiguration.typeOf(McbbsModpackManifest.class));
|
||||
|
||||
if (!McbbsModpackProvider.INSTANCE.getName().equals(config.getType()))
|
||||
throw new IllegalArgumentException("Version " + name + " is not a Mcbbs modpack. Cannot update this version.");
|
||||
}
|
||||
} catch (JsonParseException | IOException ignore) {
|
||||
}
|
||||
dependents.add(new ModpackInstallTask<>(zipFile, run, modpack.getEncoding(), Collections.singletonList("/overrides"), any -> true, config).withStage("hmcl.modpack"));
|
||||
instanceTask = new MinecraftInstanceTask<>(zipFile, modpack.getEncoding(), Collections.singletonList("/overrides"), manifest, McbbsModpackProvider.INSTANCE, modpack.getName(), modpack.getVersion(), repository.getModpackConfiguration(name));
|
||||
dependents.add(new ModpackInstallTask<>(zipFile, run.toFile(), modpack.getEncoding(), Collections.singletonList("/overrides"), any -> true, config).withStage("hmcl.modpack"));
|
||||
instanceTask = new MinecraftInstanceTask<>(zipFile, modpack.getEncoding(), Collections.singletonList("/overrides"), manifest, McbbsModpackProvider.INSTANCE, modpack.getName(), modpack.getVersion(), repository.getModpackConfiguration(name).toFile());
|
||||
dependents.add(instanceTask.withStage("hmcl.modpack"));
|
||||
}
|
||||
|
||||
|
||||
@@ -25,8 +25,9 @@ import org.jackhuang.hmcl.mod.ModpackConfiguration;
|
||||
import org.jackhuang.hmcl.task.Task;
|
||||
import org.jackhuang.hmcl.util.gson.JsonUtils;
|
||||
|
||||
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.Collections;
|
||||
import java.util.List;
|
||||
@@ -46,8 +47,8 @@ public class McbbsModpackRemoteInstallTask extends Task<Void> {
|
||||
this.repository = dependencyManager.getGameRepository();
|
||||
this.manifest = manifest;
|
||||
|
||||
File json = repository.getModpackConfiguration(name);
|
||||
if (repository.hasVersion(name) && !json.exists())
|
||||
Path json = repository.getModpackConfiguration(name);
|
||||
if (repository.hasVersion(name) && Files.notExists(json))
|
||||
throw new IllegalArgumentException("Version " + name + " already exists.");
|
||||
|
||||
GameBuilder builder = dependencyManager.gameBuilder().name(name);
|
||||
@@ -63,8 +64,8 @@ public class McbbsModpackRemoteInstallTask extends Task<Void> {
|
||||
|
||||
ModpackConfiguration<McbbsModpackManifest> config;
|
||||
try {
|
||||
if (json.exists()) {
|
||||
config = JsonUtils.fromJsonFile(json.toPath(), ModpackConfiguration.typeOf(McbbsModpackManifest.class));
|
||||
if (Files.exists(json)) {
|
||||
config = JsonUtils.fromJsonFile(json, ModpackConfiguration.typeOf(McbbsModpackManifest.class));
|
||||
|
||||
if (!MODPACK_TYPE.equals(config.getType()))
|
||||
throw new IllegalArgumentException("Version " + name + " is not a Mcbbs modpack. Cannot update this version.");
|
||||
|
||||
@@ -79,7 +79,7 @@ public class ModrinthCompletionTask extends Task<Void> {
|
||||
|
||||
if (manifest == null)
|
||||
try {
|
||||
Path manifestFile = repository.getVersionRoot(version).toPath().resolve("modrinth.index.json");
|
||||
Path manifestFile = repository.getVersionRoot(version).resolve("modrinth.index.json");
|
||||
if (Files.exists(manifestFile))
|
||||
this.manifest = JsonUtils.fromJsonFile(manifestFile, ModrinthManifest.class);
|
||||
} catch (Exception e) {
|
||||
@@ -104,7 +104,7 @@ public class ModrinthCompletionTask extends Task<Void> {
|
||||
if (manifest == null)
|
||||
return;
|
||||
|
||||
Path runDirectory = repository.getRunDirectory(version).toPath().toAbsolutePath().normalize();
|
||||
Path runDirectory = FileUtils.toAbsolute(repository.getRunDirectory(version));
|
||||
Path modsDirectory = runDirectory.resolve("mods");
|
||||
|
||||
for (ModrinthManifest.File file : manifest.getFiles()) {
|
||||
|
||||
@@ -51,10 +51,10 @@ public class ModrinthInstallTask extends Task<Void> {
|
||||
this.manifest = manifest;
|
||||
this.name = name;
|
||||
this.repository = dependencyManager.getGameRepository();
|
||||
this.run = repository.getRunDirectory(name);
|
||||
this.run = repository.getRunDirectory(name).toFile();
|
||||
|
||||
File json = repository.getModpackConfiguration(name);
|
||||
if (repository.hasVersion(name) && !json.exists())
|
||||
Path json = repository.getModpackConfiguration(name);
|
||||
if (repository.hasVersion(name) && Files.notExists(json))
|
||||
throw new IllegalArgumentException("Version " + name + " already exists.");
|
||||
|
||||
GameBuilder builder = dependencyManager.gameBuilder().name(name).gameVersion(manifest.getGameVersion());
|
||||
@@ -91,8 +91,8 @@ public class ModrinthInstallTask extends Task<Void> {
|
||||
|
||||
ModpackConfiguration<ModrinthManifest> config = null;
|
||||
try {
|
||||
if (json.exists()) {
|
||||
config = JsonUtils.fromJsonFile(json.toPath(), ModpackConfiguration.typeOf(ModrinthManifest.class));
|
||||
if (Files.exists(json)) {
|
||||
config = JsonUtils.fromJsonFile(json, ModpackConfiguration.typeOf(ModrinthManifest.class));
|
||||
|
||||
if (!ModrinthModpackProvider.INSTANCE.getName().equals(config.getType()))
|
||||
throw new IllegalArgumentException("Version " + name + " is not a Modrinth modpack. Cannot update this version.");
|
||||
@@ -103,7 +103,7 @@ public class ModrinthInstallTask extends Task<Void> {
|
||||
this.config = config;
|
||||
List<String> subDirectories = Arrays.asList("/client-overrides", "/overrides");
|
||||
dependents.add(new ModpackInstallTask<>(zipFile, run, modpack.getEncoding(), subDirectories, any -> true, config).withStage("hmcl.modpack"));
|
||||
dependents.add(new MinecraftInstanceTask<>(zipFile, modpack.getEncoding(), subDirectories, manifest, ModrinthModpackProvider.INSTANCE, manifest.getName(), manifest.getVersionId(), repository.getModpackConfiguration(name)).withStage("hmcl.modpack"));
|
||||
dependents.add(new MinecraftInstanceTask<>(zipFile, modpack.getEncoding(), subDirectories, manifest, ModrinthModpackProvider.INSTANCE, manifest.getName(), manifest.getVersionId(), repository.getModpackConfiguration(name).toFile()).withStage("hmcl.modpack"));
|
||||
|
||||
dependencies.add(new ModrinthCompletionTask(dependencyManager, name, manifest));
|
||||
}
|
||||
@@ -131,7 +131,7 @@ public class ModrinthInstallTask extends Task<Void> {
|
||||
}
|
||||
}
|
||||
|
||||
Path root = repository.getVersionRoot(name).toPath();
|
||||
Path root = repository.getVersionRoot(name);
|
||||
Files.createDirectories(root);
|
||||
JsonUtils.writeToJsonFile(root.resolve("modrinth.index.json"), manifest);
|
||||
}
|
||||
|
||||
@@ -108,7 +108,7 @@ public class ModrinthModpackExportTask extends Task<Void> {
|
||||
blackList.add(version + ".json");
|
||||
LOG.info("Compressing game files without some files in blacklist, including files or directories: usernamecache.json, asm, logs, backups, versions, assets, usercache.json, libraries, crash-reports, launcher_profiles.json, NVIDIA, TCNodeTracker");
|
||||
try (Zipper zip = new Zipper(modpackFile.toPath())) {
|
||||
Path runDirectory = repository.getRunDirectory(version).toPath();
|
||||
Path runDirectory = repository.getRunDirectory(version);
|
||||
List<ModrinthManifest.File> files = new ArrayList<>();
|
||||
Set<String> filesInManifest = new HashSet<>();
|
||||
|
||||
|
||||
@@ -69,7 +69,7 @@ public class MultiMCModpackExportTask extends Task<Void> {
|
||||
blackList.add(versionId + ".json");
|
||||
LOG.info("Compressing game files without some files in blacklist, including files or directories: usernamecache.json, asm, logs, backups, versions, assets, usercache.json, libraries, crash-reports, launcher_profiles.json, NVIDIA, TCNodeTracker");
|
||||
try (Zipper zip = new Zipper(output.toPath())) {
|
||||
zip.putDirectory(repository.getRunDirectory(versionId).toPath(), ".minecraft", path -> Modpack.acceptFile(path, blackList, whitelist));
|
||||
zip.putDirectory(repository.getRunDirectory(versionId), ".minecraft", path -> Modpack.acceptFile(path, blackList, whitelist));
|
||||
|
||||
String gameVersion = repository.getGameVersion(versionId)
|
||||
.orElseThrow(() -> new IOException("Cannot parse the version of " + versionId));
|
||||
|
||||
@@ -94,8 +94,8 @@ public final class MultiMCModpackInstallTask extends Task<MultiMCInstancePatch.R
|
||||
this.dependencyManager = dependencyManager;
|
||||
this.repository = dependencyManager.getGameRepository();
|
||||
|
||||
File json = repository.getModpackConfiguration(name);
|
||||
if (repository.hasVersion(name) && !json.exists())
|
||||
Path json = repository.getModpackConfiguration(name);
|
||||
if (repository.hasVersion(name) && Files.notExists(json))
|
||||
throw new IllegalArgumentException("Version " + name + " already exists.");
|
||||
|
||||
onDone().register(event -> {
|
||||
@@ -113,13 +113,13 @@ public final class MultiMCModpackInstallTask extends Task<MultiMCInstancePatch.R
|
||||
public void preExecute() throws Exception {
|
||||
// Stage #0: General Setup
|
||||
{
|
||||
File run = repository.getRunDirectory(name);
|
||||
File json = repository.getModpackConfiguration(name);
|
||||
Path run = repository.getRunDirectory(name);
|
||||
Path json = repository.getModpackConfiguration(name);
|
||||
|
||||
ModpackConfiguration<MultiMCInstanceConfiguration> config = null;
|
||||
try {
|
||||
if (json.exists()) {
|
||||
config = JsonUtils.fromJsonFile(json.toPath(), ModpackConfiguration.typeOf(MultiMCInstanceConfiguration.class));
|
||||
if (Files.exists(json)) {
|
||||
config = JsonUtils.fromJsonFile(json, ModpackConfiguration.typeOf(MultiMCInstanceConfiguration.class));
|
||||
|
||||
if (!MultiMCModpackProvider.INSTANCE.getName().equals(config.getType()))
|
||||
throw new IllegalArgumentException("Version " + name + " is not a MultiMC modpack. Cannot update this version.");
|
||||
@@ -133,8 +133,8 @@ public final class MultiMCModpackInstallTask extends Task<MultiMCInstancePatch.R
|
||||
}
|
||||
|
||||
// TODO: Optimize unbearably slow ModpackInstallTask
|
||||
dependents.add(new ModpackInstallTask<>(zipFile, run, modpack.getEncoding(), Collections.singletonList(mcDirectory), any -> true, config).withStage("hmcl.modpack"));
|
||||
dependents.add(new MinecraftInstanceTask<>(zipFile, modpack.getEncoding(), Collections.singletonList(mcDirectory), manifest, MultiMCModpackProvider.INSTANCE, manifest.getName(), null, repository.getModpackConfiguration(name)).withStage("hmcl.modpack"));
|
||||
dependents.add(new ModpackInstallTask<>(zipFile, run.toFile(), modpack.getEncoding(), Collections.singletonList(mcDirectory), any -> true, config).withStage("hmcl.modpack"));
|
||||
dependents.add(new MinecraftInstanceTask<>(zipFile, modpack.getEncoding(), Collections.singletonList(mcDirectory), manifest, MultiMCModpackProvider.INSTANCE, manifest.getName(), null, repository.getModpackConfiguration(name).toFile()).withStage("hmcl.modpack"));
|
||||
}
|
||||
|
||||
// Stage #1: Load all related Json-Patch from meta maven or local mod pack.
|
||||
@@ -253,7 +253,7 @@ public final class MultiMCModpackInstallTask extends Task<MultiMCInstancePatch.R
|
||||
|
||||
Path libraries = root.resolve("libraries");
|
||||
if (Files.exists(libraries))
|
||||
FileUtils.copyDirectory(libraries, repository.getVersionRoot(name).toPath().resolve("libraries"));
|
||||
FileUtils.copyDirectory(libraries, repository.getVersionRoot(name).resolve("libraries"));
|
||||
|
||||
for (Library library : artifact.getVersion().getLibraries()) {
|
||||
if ("local".equals(library.getHint())) {
|
||||
@@ -261,15 +261,15 @@ public final class MultiMCModpackInstallTask extends Task<MultiMCInstancePatch.R
|
||||
Retain them will facilitate compatibility, as some embedded libraries may check where their JAR is.
|
||||
Meanwhile, potential compatibility issue with other launcher which never supports these fields might occur.
|
||||
Here, we make the file stored twice, to keep maximum compatibility. */
|
||||
Path from = repository.getLibraryFile(artifact.getVersion(), library).toPath();
|
||||
Path target = repository.getLibraryFile(artifact.getVersion(), library.withoutCommunityFields()).toPath();
|
||||
Path from = repository.getLibraryFile(artifact.getVersion(), library);
|
||||
Path target = repository.getLibraryFile(artifact.getVersion(), library.withoutCommunityFields());
|
||||
Files.createDirectories(target.getParent());
|
||||
Files.copy(from, target, StandardCopyOption.REPLACE_EXISTING);
|
||||
}
|
||||
}
|
||||
|
||||
try (InputStream input = MaintainTask.class.getResourceAsStream("/assets/game/HMCLMultiMCBootstrap-1.0.jar")) {
|
||||
Path libraryPath = repository.getLibraryFile(artifact.getVersion(), MultiMCInstancePatch.BOOTSTRAP_LIBRARY).toPath();
|
||||
Path libraryPath = repository.getLibraryFile(artifact.getVersion(), MultiMCInstancePatch.BOOTSTRAP_LIBRARY);
|
||||
|
||||
Files.createDirectories(libraryPath.getParent());
|
||||
Files.copy(Objects.requireNonNull(input, "Bundled HMCLMultiMCBootstrap is missing."), libraryPath, StandardCopyOption.REPLACE_EXISTING);
|
||||
@@ -279,7 +279,7 @@ public final class MultiMCModpackInstallTask extends Task<MultiMCInstancePatch.R
|
||||
if (iconKey != null) {
|
||||
Path iconFile = root.resolve(iconKey + ".png");
|
||||
if (Files.exists(iconFile)) {
|
||||
FileUtils.copyFile(iconFile, repository.getVersionRoot(name).toPath().resolve("icon.png"));
|
||||
FileUtils.copyFile(iconFile, repository.getVersionRoot(name).resolve("icon.png"));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -339,7 +339,7 @@ public final class MultiMCModpackInstallTask extends Task<MultiMCInstancePatch.R
|
||||
Path root = getRootPath(fs).resolve("jarmods");
|
||||
|
||||
try (FileSystem mc = CompressingUtils.writable(
|
||||
repository.getVersionRoot(name).toPath().resolve(name + ".jar")
|
||||
repository.getVersionRoot(name).resolve(name + ".jar")
|
||||
).setAutoDetectEncoding(true).build()) {
|
||||
for (String fileName : files) {
|
||||
try (FileSystem jm = CompressingUtils.readonly(root.resolve(fileName)).setAutoDetectEncoding(true).build()) {
|
||||
|
||||
@@ -30,7 +30,6 @@ import org.jackhuang.hmcl.util.DigestUtils;
|
||||
import org.jackhuang.hmcl.util.StringUtils;
|
||||
import org.jackhuang.hmcl.util.gson.JsonUtils;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
@@ -61,9 +60,9 @@ public class ServerModpackCompletionTask extends Task<Void> {
|
||||
|
||||
if (manifest == null) {
|
||||
try {
|
||||
File manifestFile = repository.getModpackConfiguration(version);
|
||||
if (manifestFile.exists()) {
|
||||
this.manifest = JsonUtils.fromJsonFile(manifestFile.toPath(), ModpackConfiguration.typeOf(ServerModpackManifest.class));
|
||||
Path manifestFile = repository.getModpackConfiguration(version);
|
||||
if (Files.exists(manifestFile)) {
|
||||
this.manifest = JsonUtils.fromJsonFile(manifestFile, ModpackConfiguration.typeOf(ServerModpackManifest.class));
|
||||
}
|
||||
} catch (Exception e) {
|
||||
LOG.warning("Unable to read Server modpack manifest.json", e);
|
||||
@@ -121,7 +120,7 @@ public class ServerModpackCompletionTask extends Task<Void> {
|
||||
dependencies.add(builder.buildAsync());
|
||||
}
|
||||
|
||||
Path rootPath = repository.getVersionRoot(version).toPath().toAbsolutePath().normalize();
|
||||
Path rootPath = repository.getVersionRoot(version).toAbsolutePath().normalize();
|
||||
Map<String, ModpackConfiguration.FileInformation> files = manifest.getManifest().getFiles().stream()
|
||||
.collect(Collectors.toMap(ModpackConfiguration.FileInformation::getPath,
|
||||
Function.identity()));
|
||||
@@ -129,7 +128,7 @@ public class ServerModpackCompletionTask extends Task<Void> {
|
||||
Set<String> remoteFiles = remoteManifest.getFiles().stream().map(ModpackConfiguration.FileInformation::getPath)
|
||||
.collect(Collectors.toSet());
|
||||
|
||||
Path runDirectory = repository.getRunDirectory(version).toPath().toAbsolutePath().normalize();
|
||||
Path runDirectory = repository.getRunDirectory(version).toAbsolutePath().normalize();
|
||||
Path modsDirectory = runDirectory.resolve("mods");
|
||||
|
||||
int total = 0;
|
||||
@@ -189,7 +188,7 @@ public class ServerModpackCompletionTask extends Task<Void> {
|
||||
@Override
|
||||
public void postExecute() throws Exception {
|
||||
if (manifest == null || StringUtils.isBlank(manifest.getManifest().getFileApi())) return;
|
||||
Path manifestFile = repository.getModpackConfiguration(version).toPath();
|
||||
Path manifestFile = repository.getModpackConfiguration(version);
|
||||
Files.createDirectories(manifestFile.getParent());
|
||||
JsonUtils.writeToJsonFile(manifestFile, new ModpackConfiguration<>(remoteManifest, this.manifest.getType(), this.manifest.getName(), this.manifest.getVersion(), remoteManifest.getFiles()));
|
||||
}
|
||||
|
||||
@@ -63,7 +63,7 @@ public class ServerModpackExportTask extends Task<Void> {
|
||||
blackList.add(versionId + ".json");
|
||||
LOG.info("Compressing game files without some files in blacklist, including files or directories: usernamecache.json, asm, logs, backups, versions, assets, usercache.json, libraries, crash-reports, launcher_profiles.json, NVIDIA, TCNodeTracker");
|
||||
try (Zipper zip = new Zipper(modpackFile.toPath())) {
|
||||
Path runDirectory = repository.getRunDirectory(versionId).toPath();
|
||||
Path runDirectory = repository.getRunDirectory(versionId);
|
||||
List<ModpackConfiguration.FileInformation> files = new ArrayList<>();
|
||||
zip.putDirectory(runDirectory, "overrides", path -> {
|
||||
if (Modpack.acceptFile(path, blackList, exportInfo.getWhitelist())) {
|
||||
|
||||
@@ -30,6 +30,8 @@ import org.jackhuang.hmcl.util.gson.JsonUtils;
|
||||
|
||||
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.Collections;
|
||||
import java.util.List;
|
||||
@@ -50,10 +52,10 @@ public class ServerModpackLocalInstallTask extends Task<Void> {
|
||||
this.manifest = manifest;
|
||||
this.name = name;
|
||||
this.repository = dependencyManager.getGameRepository();
|
||||
File run = repository.getRunDirectory(name);
|
||||
Path run = repository.getRunDirectory(name);
|
||||
|
||||
File json = repository.getModpackConfiguration(name);
|
||||
if (repository.hasVersion(name) && !json.exists())
|
||||
Path json = repository.getModpackConfiguration(name);
|
||||
if (repository.hasVersion(name) && Files.notExists(json))
|
||||
throw new IllegalArgumentException("Version " + name + " already exists.");
|
||||
|
||||
GameBuilder builder = dependencyManager.gameBuilder().name(name);
|
||||
@@ -69,16 +71,16 @@ public class ServerModpackLocalInstallTask extends Task<Void> {
|
||||
|
||||
ModpackConfiguration<ServerModpackManifest> config = null;
|
||||
try {
|
||||
if (json.exists()) {
|
||||
config = JsonUtils.fromJsonFile(json.toPath(), ModpackConfiguration.typeOf(ServerModpackManifest.class));
|
||||
if (Files.exists(json)) {
|
||||
config = JsonUtils.fromJsonFile(json, ModpackConfiguration.typeOf(ServerModpackManifest.class));
|
||||
|
||||
if (!ServerModpackProvider.INSTANCE.getName().equals(config.getType()))
|
||||
throw new IllegalArgumentException("Version " + name + " is not a Server modpack. Cannot update this version.");
|
||||
}
|
||||
} catch (JsonParseException | IOException ignore) {
|
||||
}
|
||||
dependents.add(new ModpackInstallTask<>(zipFile, run, modpack.getEncoding(), Collections.singletonList("/overrides"), any -> true, config).withStage("hmcl.modpack"));
|
||||
dependents.add(new MinecraftInstanceTask<>(zipFile, modpack.getEncoding(), Collections.singletonList("/overrides"), manifest, ServerModpackProvider.INSTANCE, modpack.getName(), modpack.getVersion(), repository.getModpackConfiguration(name)).withStage("hmcl.modpack"));
|
||||
dependents.add(new ModpackInstallTask<>(zipFile, run.toFile(), modpack.getEncoding(), Collections.singletonList("/overrides"), any -> true, config).withStage("hmcl.modpack"));
|
||||
dependents.add(new MinecraftInstanceTask<>(zipFile, modpack.getEncoding(), Collections.singletonList("/overrides"), manifest, ServerModpackProvider.INSTANCE, modpack.getName(), modpack.getVersion(), repository.getModpackConfiguration(name).toFile()).withStage("hmcl.modpack"));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -25,8 +25,9 @@ import org.jackhuang.hmcl.mod.ModpackConfiguration;
|
||||
import org.jackhuang.hmcl.task.Task;
|
||||
import org.jackhuang.hmcl.util.gson.JsonUtils;
|
||||
|
||||
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.Collections;
|
||||
import java.util.List;
|
||||
@@ -46,8 +47,8 @@ public class ServerModpackRemoteInstallTask extends Task<Void> {
|
||||
this.repository = dependencyManager.getGameRepository();
|
||||
this.manifest = manifest;
|
||||
|
||||
File json = repository.getModpackConfiguration(name);
|
||||
if (repository.hasVersion(name) && !json.exists())
|
||||
Path json = repository.getModpackConfiguration(name);
|
||||
if (repository.hasVersion(name) && Files.notExists(json))
|
||||
throw new IllegalArgumentException("Version " + name + " already exists.");
|
||||
|
||||
GameBuilder builder = dependencyManager.gameBuilder().name(name);
|
||||
@@ -63,8 +64,8 @@ public class ServerModpackRemoteInstallTask extends Task<Void> {
|
||||
|
||||
ModpackConfiguration<ServerModpackManifest> config;
|
||||
try {
|
||||
if (json.exists()) {
|
||||
config = JsonUtils.fromJsonFile(json.toPath(), ModpackConfiguration.typeOf(ServerModpackManifest.class));
|
||||
if (Files.exists(json)) {
|
||||
config = JsonUtils.fromJsonFile(json, ModpackConfiguration.typeOf(ServerModpackManifest.class));
|
||||
|
||||
if (!MODPACK_TYPE.equals(config.getType()))
|
||||
throw new IllegalArgumentException("Version " + name + " is not a Server modpack. Cannot update this version.");
|
||||
|
||||
@@ -109,6 +109,14 @@ public final class FileUtils {
|
||||
return fileName != null ? fileName.toString() : "";
|
||||
}
|
||||
|
||||
public static Path toAbsolute(Path path) {
|
||||
return path.toAbsolutePath().normalize();
|
||||
}
|
||||
|
||||
public static String getAbsolutePath(Path path) {
|
||||
return path.toAbsolutePath().normalize().toString();
|
||||
}
|
||||
|
||||
// https://learn.microsoft.com/biztalk/core/restrictions-when-configuring-the-file-adapter
|
||||
private static final Set<String> INVALID_WINDOWS_RESOURCE_BASE_NAMES = Set.of(
|
||||
"aux", "con", "nul", "prn", "clock$",
|
||||
@@ -187,6 +195,18 @@ public final class FileUtils {
|
||||
return true;
|
||||
}
|
||||
|
||||
/// Safely get the file size. Returns `0` if the file does not exist or the size cannot be obtained.
|
||||
public static long size(Path file) {
|
||||
try {
|
||||
return Files.size(file);
|
||||
} catch (NoSuchFileException ignored) {
|
||||
return 0L;
|
||||
} catch (IOException e) {
|
||||
LOG.warning("Failed to get file size of " + file, e);
|
||||
return 0L;
|
||||
}
|
||||
}
|
||||
|
||||
public static String readTextMaybeNativeEncoding(Path file) throws IOException {
|
||||
byte[] bytes = Files.readAllBytes(file);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user