Support install Forge/OptiFine from local file
This commit is contained in:
@@ -21,6 +21,7 @@ import javafx.scene.Node;
|
|||||||
import org.jackhuang.hmcl.download.DownloadProvider;
|
import org.jackhuang.hmcl.download.DownloadProvider;
|
||||||
import org.jackhuang.hmcl.download.LibraryAnalyzer;
|
import org.jackhuang.hmcl.download.LibraryAnalyzer;
|
||||||
import org.jackhuang.hmcl.download.RemoteVersion;
|
import org.jackhuang.hmcl.download.RemoteVersion;
|
||||||
|
import org.jackhuang.hmcl.download.VersionMismatchException;
|
||||||
import org.jackhuang.hmcl.download.game.LibraryDownloadException;
|
import org.jackhuang.hmcl.download.game.LibraryDownloadException;
|
||||||
import org.jackhuang.hmcl.download.optifine.OptiFineInstallTask;
|
import org.jackhuang.hmcl.download.optifine.OptiFineInstallTask;
|
||||||
import org.jackhuang.hmcl.game.Library;
|
import org.jackhuang.hmcl.game.Library;
|
||||||
@@ -143,6 +144,11 @@ public final class InstallerWizardProvider implements WizardProvider {
|
|||||||
}
|
}
|
||||||
} else if (exception instanceof OptiFineInstallTask.UnsupportedOptiFineInstallationException) {
|
} else if (exception instanceof OptiFineInstallTask.UnsupportedOptiFineInstallationException) {
|
||||||
Controllers.dialog(i18n("install.failed.optifine_conflict"), i18n("install.failed"), MessageType.ERROR, next);
|
Controllers.dialog(i18n("install.failed.optifine_conflict"), i18n("install.failed"), MessageType.ERROR, next);
|
||||||
|
} else if (exception instanceof UnsupportedOperationException) {
|
||||||
|
Controllers.dialog(i18n("install.failed.install_online"), i18n("install.failed"), MessageType.ERROR, next);
|
||||||
|
} else if (exception instanceof VersionMismatchException) {
|
||||||
|
VersionMismatchException e = ((VersionMismatchException) exception);
|
||||||
|
Controllers.dialog(i18n("install.failed.version_mismatch", e.getExpect(), e.getActual()), i18n("install.failed"), MessageType.ERROR, next);
|
||||||
} else {
|
} else {
|
||||||
Controllers.dialog(StringUtils.getStackTrace(exception), i18n("install.failed"), MessageType.ERROR, next);
|
Controllers.dialog(StringUtils.getStackTrace(exception), i18n("install.failed"), MessageType.ERROR, next);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,6 +17,9 @@
|
|||||||
*/
|
*/
|
||||||
package org.jackhuang.hmcl.ui.versions;
|
package org.jackhuang.hmcl.ui.versions;
|
||||||
|
|
||||||
|
import javafx.scene.Node;
|
||||||
|
import javafx.scene.control.Skin;
|
||||||
|
import javafx.stage.FileChooser;
|
||||||
import org.jackhuang.hmcl.download.LibraryAnalyzer;
|
import org.jackhuang.hmcl.download.LibraryAnalyzer;
|
||||||
import org.jackhuang.hmcl.download.MaintainTask;
|
import org.jackhuang.hmcl.download.MaintainTask;
|
||||||
import org.jackhuang.hmcl.download.game.VersionJsonSaveTask;
|
import org.jackhuang.hmcl.download.game.VersionJsonSaveTask;
|
||||||
@@ -26,25 +29,42 @@ import org.jackhuang.hmcl.game.Version;
|
|||||||
import org.jackhuang.hmcl.setting.Profile;
|
import org.jackhuang.hmcl.setting.Profile;
|
||||||
import org.jackhuang.hmcl.task.Schedulers;
|
import org.jackhuang.hmcl.task.Schedulers;
|
||||||
import org.jackhuang.hmcl.task.Task;
|
import org.jackhuang.hmcl.task.Task;
|
||||||
import org.jackhuang.hmcl.ui.Controllers;
|
import org.jackhuang.hmcl.task.TaskExecutor;
|
||||||
import org.jackhuang.hmcl.ui.InstallerItem;
|
import org.jackhuang.hmcl.task.TaskListener;
|
||||||
import org.jackhuang.hmcl.ui.ListPage;
|
import org.jackhuang.hmcl.ui.*;
|
||||||
import org.jackhuang.hmcl.ui.download.InstallerWizardProvider;
|
import org.jackhuang.hmcl.ui.download.InstallerWizardProvider;
|
||||||
import org.jackhuang.hmcl.ui.download.UpdateInstallerWizardProvider;
|
import org.jackhuang.hmcl.ui.download.UpdateInstallerWizardProvider;
|
||||||
|
import org.jackhuang.hmcl.util.io.FileUtils;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.util.Arrays;
|
||||||
import java.util.LinkedList;
|
import java.util.LinkedList;
|
||||||
|
import java.util.List;
|
||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
|
|
||||||
import static org.jackhuang.hmcl.download.LibraryAnalyzer.LibraryType.*;
|
import static org.jackhuang.hmcl.download.LibraryAnalyzer.LibraryType.*;
|
||||||
|
import static org.jackhuang.hmcl.ui.FXUtils.runInFX;
|
||||||
import static org.jackhuang.hmcl.util.i18n.I18n.i18n;
|
import static org.jackhuang.hmcl.util.i18n.I18n.i18n;
|
||||||
|
|
||||||
public class InstallerListPage extends ListPage<InstallerItem> {
|
public class InstallerListPage extends ListPageBase<InstallerItem> {
|
||||||
private Profile profile;
|
private Profile profile;
|
||||||
private String versionId;
|
private String versionId;
|
||||||
private Version version;
|
private Version version;
|
||||||
private String gameVersion;
|
private String gameVersion;
|
||||||
|
|
||||||
|
{
|
||||||
|
FXUtils.applyDragListener(this, it -> Arrays.asList("jar", "exe").contains(FileUtils.getExtension(it)), mods -> {
|
||||||
|
if (!mods.isEmpty())
|
||||||
|
doInstallOffline(mods.get(0));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Skin<?> createDefaultSkin() {
|
||||||
|
return new InstallerListPageSkin();
|
||||||
|
}
|
||||||
|
|
||||||
public void loadVersion(Profile profile, String versionId) {
|
public void loadVersion(Profile profile, String versionId) {
|
||||||
this.profile = profile;
|
this.profile = profile;
|
||||||
this.versionId = versionId;
|
this.versionId = versionId;
|
||||||
@@ -83,11 +103,54 @@ public class InstallerListPage extends ListPage<InstallerItem> {
|
|||||||
}).start();
|
}).start();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
public void installOnline() {
|
||||||
public void add() {
|
|
||||||
if (gameVersion == null)
|
if (gameVersion == null)
|
||||||
Controllers.dialog(i18n("version.cannot_read"));
|
Controllers.dialog(i18n("version.cannot_read"));
|
||||||
else
|
else
|
||||||
Controllers.getDecorator().startWizard(new InstallerWizardProvider(profile, gameVersion, version));
|
Controllers.getDecorator().startWizard(new InstallerWizardProvider(profile, gameVersion, version));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void installOffline() {
|
||||||
|
FileChooser chooser = new FileChooser();
|
||||||
|
chooser.getExtensionFilters().add(new FileChooser.ExtensionFilter(i18n("install.installer.install_offline.extension"), "*.jar", "*.exe"));
|
||||||
|
File file = chooser.showOpenDialog(Controllers.getStage());
|
||||||
|
if (file != null) doInstallOffline(file);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void doInstallOffline(File file) {
|
||||||
|
Task task = profile.getDependency().installLibraryAsync(version, file.toPath())
|
||||||
|
.then(profile.getRepository().refreshVersionsAsync());
|
||||||
|
task.setName(i18n("install.installer.install_offline"));
|
||||||
|
TaskExecutor executor = task.executor(new TaskListener() {
|
||||||
|
@Override
|
||||||
|
public void onStop(boolean success, TaskExecutor executor) {
|
||||||
|
runInFX(() -> {
|
||||||
|
if (success) {
|
||||||
|
loadVersion(profile, versionId);
|
||||||
|
Controllers.dialog(i18n("install.success"));
|
||||||
|
} else {
|
||||||
|
if (executor.getLastException() == null)
|
||||||
|
return;
|
||||||
|
InstallerWizardProvider.alertFailureMessage(executor.getLastException(), null);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
Controllers.taskDialog(executor, i18n("install.installer.install_offline"));
|
||||||
|
executor.start();
|
||||||
|
}
|
||||||
|
|
||||||
|
private class InstallerListPageSkin extends ToolbarListPageSkin<InstallerListPage> {
|
||||||
|
|
||||||
|
InstallerListPageSkin() {
|
||||||
|
super(InstallerListPage.this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected List<Node> initializeToolbar(InstallerListPage skinnable) {
|
||||||
|
return Arrays.asList(
|
||||||
|
createToolbarButton(i18n("install.installer.install_online"), SVG::plus, skinnable::installOnline),
|
||||||
|
createToolbarButton(i18n("install.installer.install_offline"), SVG::plus, skinnable::installOffline));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -64,7 +64,6 @@ public interface TaskExecutorDialogWizardDisplayer extends AbstractWizardDisplay
|
|||||||
@Override
|
@Override
|
||||||
public void onStop(boolean success, TaskExecutor executor) {
|
public void onStop(boolean success, TaskExecutor executor) {
|
||||||
runInFX(() -> {
|
runInFX(() -> {
|
||||||
pane.fireEvent(new DialogCloseEvent());
|
|
||||||
if (success) {
|
if (success) {
|
||||||
if (settings.containsKey("success_message") && settings.get("success_message") instanceof String)
|
if (settings.containsKey("success_message") && settings.get("success_message") instanceof String)
|
||||||
Controllers.dialog((String) settings.get("success_message"), null, MessageType.FINE, () -> onEnd());
|
Controllers.dialog((String) settings.get("success_message"), null, MessageType.FINE, () -> onEnd());
|
||||||
|
|||||||
@@ -131,11 +131,18 @@ install.failed=Failed to install
|
|||||||
install.failed.downloading=Failed to install due to some files not downloaded successfully
|
install.failed.downloading=Failed to install due to some files not downloaded successfully
|
||||||
install.failed.downloading.detail=Failed to download file: %s
|
install.failed.downloading.detail=Failed to download file: %s
|
||||||
install.failed.downloading.timeout=Download timed out: %s
|
install.failed.downloading.timeout=Download timed out: %s
|
||||||
|
install.failed.install_online=Unable to recognize what you provided installer file is
|
||||||
install.failed.optifine_conflict=OptiFine and Forge are both installed simultaneously on Minecraft 1.13
|
install.failed.optifine_conflict=OptiFine and Forge are both installed simultaneously on Minecraft 1.13
|
||||||
|
install.failed.version_mismatch=The library requires game version %s, but actual version is %s.
|
||||||
install.installer.choose=Choose a %s version
|
install.installer.choose=Choose a %s version
|
||||||
install.installer.forge=Forge
|
install.installer.forge=Forge
|
||||||
install.installer.game=Game
|
install.installer.game=Game
|
||||||
install.installer.install=
|
install.installer.install=Install %s
|
||||||
|
install.installer.install_offline=Install/Upgrade from local file
|
||||||
|
install.installer.install_offline.extension=Forge/OptiFine installer
|
||||||
|
install.installer.install_offline.tooltip=Support importing Forge/OptiFine installer jar file
|
||||||
|
install.installer.install_online=Install Online
|
||||||
|
install.installer.install_online.tooltip=Support Forge, OptiFine, LiteLoader installation.
|
||||||
install.installer.liteloader=LiteLoader
|
install.installer.liteloader=LiteLoader
|
||||||
install.installer.not_installed=%s not Installed
|
install.installer.not_installed=%s not Installed
|
||||||
install.installer.optifine=OptiFine
|
install.installer.optifine=OptiFine
|
||||||
|
|||||||
@@ -130,11 +130,18 @@ install.failed=安裝失敗
|
|||||||
install.failed.downloading=安裝失敗,部分文件未能完成下載
|
install.failed.downloading=安裝失敗,部分文件未能完成下載
|
||||||
install.failed.downloading.detail=未能下載檔案:%s
|
install.failed.downloading.detail=未能下載檔案:%s
|
||||||
install.failed.downloading.timeout=下載超時:%s
|
install.failed.downloading.timeout=下載超時:%s
|
||||||
install.failed.optifine_conflict=暫不支持 OptiFine 與 Forge 同時安裝於 Minecraft 1.13
|
install.failed.install_online=無法識別要安裝的軟體
|
||||||
|
install.failed.optifine_conflict=暫不支持 OptiFine 與 Forge 同時安裝在 Minecraft 1.13 上
|
||||||
|
install.failed.version_mismatch=該軟體需要的遊戲版本為 %s,但實際的遊戲版本為 %s。
|
||||||
install.installer.choose=選擇 %s 版本
|
install.installer.choose=選擇 %s 版本
|
||||||
install.installer.forge=Forge
|
install.installer.forge=Forge
|
||||||
install.installer.game=遊戲
|
install.installer.game=遊戲
|
||||||
install.installer.install=安裝 %s
|
install.installer.install=安裝 %s
|
||||||
|
install.installer.install_offline=從本地檔案安裝/升級
|
||||||
|
install.installer.install_offline.extension=Forge/OptiFine 安裝器
|
||||||
|
install.installer.install_offline.tooltip=支持導入已經下載好的 Forge/OptiFine 安裝器
|
||||||
|
install.installer.install_online=在線安裝
|
||||||
|
install.installer.install_online.tooltip=支持安裝 Forge、OptiFine、LiteLoader
|
||||||
install.installer.liteloader=LiteLoader
|
install.installer.liteloader=LiteLoader
|
||||||
install.installer.not_installed=暫時不安裝 %s,可以點擊此處安裝
|
install.installer.not_installed=暫時不安裝 %s,可以點擊此處安裝
|
||||||
install.installer.optifine=OptiFine
|
install.installer.optifine=OptiFine
|
||||||
|
|||||||
@@ -130,11 +130,18 @@ install.failed=安装失败
|
|||||||
install.failed.downloading=安装失败,部分文件未能完成下载
|
install.failed.downloading=安装失败,部分文件未能完成下载
|
||||||
install.failed.downloading.detail=未能下载文件:%s
|
install.failed.downloading.detail=未能下载文件:%s
|
||||||
install.failed.downloading.timeout=下载超时:%s
|
install.failed.downloading.timeout=下载超时:%s
|
||||||
install.failed.optifine_conflict=暂不支持 OptiFine 与 Forge 同时安装于 Minecraft 1.13
|
install.failed.install_online=无法识别要安装的软件
|
||||||
|
install.failed.optifine_conflict=暂不支持 OptiFine 与 Forge 同时安装在 Minecraft 1.13 上
|
||||||
|
install.failed.version_mismatch=该软件需要的游戏版本为 %s,但实际的游戏版本为 %s。
|
||||||
install.installer.choose=选择 %s 版本
|
install.installer.choose=选择 %s 版本
|
||||||
install.installer.forge=Forge
|
install.installer.forge=Forge
|
||||||
install.installer.game=游戏
|
install.installer.game=游戏
|
||||||
install.installer.install=安装 %s
|
install.installer.install=安装 %s
|
||||||
|
install.installer.install_offline=从本地文件安装/升级
|
||||||
|
install.installer.install_offline.extension=Forge/OptiFine 安装器
|
||||||
|
install.installer.install_offline.tooltip=支持导入已经下载好的 Forge/OptiFine 安装器
|
||||||
|
install.installer.install_online=在线安装
|
||||||
|
install.installer.install_online.tooltip=支持安装 Forge、OptiFine、LiteLoader
|
||||||
install.installer.liteloader=LiteLoader
|
install.installer.liteloader=LiteLoader
|
||||||
install.installer.not_installed=暂不安装 %s,可以点击此处安装
|
install.installer.not_installed=暂不安装 %s,可以点击此处安装
|
||||||
install.installer.optifine=OptiFine
|
install.installer.optifine=OptiFine
|
||||||
|
|||||||
@@ -31,6 +31,9 @@ import org.jackhuang.hmcl.task.Task;
|
|||||||
import org.jackhuang.hmcl.task.TaskResult;
|
import org.jackhuang.hmcl.task.TaskResult;
|
||||||
import org.jackhuang.hmcl.util.function.ExceptionalFunction;
|
import org.jackhuang.hmcl.util.function.ExceptionalFunction;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Note: This class has no state.
|
* Note: This class has no state.
|
||||||
*
|
*
|
||||||
@@ -112,8 +115,29 @@ public class DefaultDependencyManager extends AbstractDependencyManager {
|
|||||||
.thenCompose(newVersion -> new VersionJsonSaveTask(repository, newVersion));
|
.thenCompose(newVersion -> new VersionJsonSaveTask(repository, newVersion));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public ExceptionalFunction<Version, TaskResult<Version>, ?> installLibraryAsync(RemoteVersion libraryVersion) {
|
public ExceptionalFunction<Version, TaskResult<Version>, ?> installLibraryAsync(RemoteVersion libraryVersion) {
|
||||||
return version -> installLibraryAsync(version, libraryVersion);
|
return version -> installLibraryAsync(version, libraryVersion);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Task installLibraryAsync(Version oldVersion, Path installer) {
|
||||||
|
return Task
|
||||||
|
.of(() -> {
|
||||||
|
})
|
||||||
|
.thenCompose(() -> {
|
||||||
|
try {
|
||||||
|
return ForgeInstallTask.install(this, oldVersion, installer);
|
||||||
|
} catch (IOException ignore) {
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
return OptiFineInstallTask.install(this, oldVersion, installer);
|
||||||
|
} catch (IOException ignore) {
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new UnsupportedOperationException("Library cannot be recognized");
|
||||||
|
})
|
||||||
|
.thenCompose(LibrariesUniqueTask::new)
|
||||||
|
.thenCompose(MaintainTask::new)
|
||||||
|
.thenCompose(newVersion -> new VersionJsonSaveTask(repository, newVersion));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,37 @@
|
|||||||
|
/*
|
||||||
|
* Hello Minecraft! Launcher
|
||||||
|
* Copyright (C) 2019 huangyuhui <huanghongxun2008@126.com> and contributors
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
package org.jackhuang.hmcl.download;
|
||||||
|
|
||||||
|
public class VersionMismatchException extends Exception {
|
||||||
|
|
||||||
|
private final String expect, actual;
|
||||||
|
|
||||||
|
public VersionMismatchException(String expect, String actual) {
|
||||||
|
super("Mismatched game version requirement, library requires game to be " + expect + ", but actual is " + actual);
|
||||||
|
this.expect = expect;
|
||||||
|
this.actual = actual;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getExpect() {
|
||||||
|
return expect;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getActual() {
|
||||||
|
return actual;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -18,17 +18,26 @@
|
|||||||
package org.jackhuang.hmcl.download.forge;
|
package org.jackhuang.hmcl.download.forge;
|
||||||
|
|
||||||
import org.jackhuang.hmcl.download.DefaultDependencyManager;
|
import org.jackhuang.hmcl.download.DefaultDependencyManager;
|
||||||
|
import org.jackhuang.hmcl.download.VersionMismatchException;
|
||||||
|
import org.jackhuang.hmcl.game.GameVersion;
|
||||||
import org.jackhuang.hmcl.game.Version;
|
import org.jackhuang.hmcl.game.Version;
|
||||||
import org.jackhuang.hmcl.task.FileDownloadTask;
|
import org.jackhuang.hmcl.task.FileDownloadTask;
|
||||||
import org.jackhuang.hmcl.task.Task;
|
import org.jackhuang.hmcl.task.Task;
|
||||||
import org.jackhuang.hmcl.task.TaskResult;
|
import org.jackhuang.hmcl.task.TaskResult;
|
||||||
|
import org.jackhuang.hmcl.util.gson.JsonUtils;
|
||||||
|
import org.jackhuang.hmcl.util.io.CompressingUtils;
|
||||||
|
import org.jackhuang.hmcl.util.io.FileUtils;
|
||||||
import org.jackhuang.hmcl.util.io.NetworkUtils;
|
import org.jackhuang.hmcl.util.io.NetworkUtils;
|
||||||
import org.jackhuang.hmcl.util.versioning.VersionNumber;
|
import org.jackhuang.hmcl.util.versioning.VersionNumber;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.nio.file.FileSystem;
|
||||||
import java.nio.file.Files;
|
import java.nio.file.Files;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
@@ -90,4 +99,36 @@ public final class ForgeInstallTask extends TaskResult<Version> {
|
|||||||
else
|
else
|
||||||
dependency = new ForgeOldInstallTask(dependencyManager, version, installer);
|
dependency = new ForgeOldInstallTask(dependencyManager, version, installer);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Install Forge library from existing local file.
|
||||||
|
*
|
||||||
|
* @param dependencyManager game repository
|
||||||
|
* @param version version.json
|
||||||
|
* @param installer the Forge installer, either the new or old one.
|
||||||
|
* @return the task to install library
|
||||||
|
* @throws IOException if unable to read compressed content of installer file, or installer file is corrupted, or the installer is not the one we want.
|
||||||
|
* @throws VersionMismatchException if required game version of installer does not match the actual one.
|
||||||
|
*/
|
||||||
|
public static TaskResult<Version> install(DefaultDependencyManager dependencyManager, Version version, Path installer) throws IOException, VersionMismatchException {
|
||||||
|
Optional<String> gameVersion = GameVersion.minecraftVersion(dependencyManager.getGameRepository().getVersionJar(version));
|
||||||
|
if (!gameVersion.isPresent()) throw new IOException();
|
||||||
|
try (FileSystem fs = CompressingUtils.createReadOnlyZipFileSystem(installer)) {
|
||||||
|
String installProfileText = FileUtils.readText(fs.getPath("install_profile.json"));
|
||||||
|
Map installProfile = JsonUtils.fromNonNullJson(installProfileText, Map.class);
|
||||||
|
if (installProfile.containsKey("spec")) {
|
||||||
|
ForgeNewInstallProfile profile = JsonUtils.fromNonNullJson(installProfileText, ForgeNewInstallProfile.class);
|
||||||
|
if (!gameVersion.get().equals(profile.getMinecraft()))
|
||||||
|
throw new VersionMismatchException(profile.getMinecraft(), gameVersion.get());
|
||||||
|
return new ForgeNewInstallTask(dependencyManager, version, installer);
|
||||||
|
} else if (installProfile.containsKey("install") && installProfile.containsKey("versionInfo")) {
|
||||||
|
ForgeInstallProfile profile = JsonUtils.fromNonNullJson(installProfileText, ForgeInstallProfile.class);
|
||||||
|
if (!gameVersion.get().equals(profile.getInstall().getMinecraft()))
|
||||||
|
throw new VersionMismatchException(profile.getInstall().getMinecraft(), gameVersion.get());
|
||||||
|
return new ForgeOldInstallTask(dependencyManager, version, installer);
|
||||||
|
} else {
|
||||||
|
throw new IOException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,10 +17,13 @@
|
|||||||
*/
|
*/
|
||||||
package org.jackhuang.hmcl.download.forge;
|
package org.jackhuang.hmcl.download.forge;
|
||||||
|
|
||||||
|
import com.google.gson.JsonParseException;
|
||||||
import org.jackhuang.hmcl.game.Artifact;
|
import org.jackhuang.hmcl.game.Artifact;
|
||||||
import org.jackhuang.hmcl.game.Library;
|
import org.jackhuang.hmcl.game.Library;
|
||||||
import org.jackhuang.hmcl.util.Immutable;
|
import org.jackhuang.hmcl.util.Immutable;
|
||||||
import org.jackhuang.hmcl.util.function.ExceptionalFunction;
|
import org.jackhuang.hmcl.util.function.ExceptionalFunction;
|
||||||
|
import org.jackhuang.hmcl.util.gson.TolerableValidationException;
|
||||||
|
import org.jackhuang.hmcl.util.gson.Validation;
|
||||||
|
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
@@ -29,7 +32,7 @@ import java.util.Map;
|
|||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
@Immutable
|
@Immutable
|
||||||
public class ForgeNewInstallProfile {
|
public class ForgeNewInstallProfile implements Validation {
|
||||||
|
|
||||||
private final int spec;
|
private final int spec;
|
||||||
private final String minecraft;
|
private final String minecraft;
|
||||||
@@ -97,6 +100,8 @@ public class ForgeNewInstallProfile {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Data for processors.
|
* Data for processors.
|
||||||
|
*
|
||||||
|
* @return a mutable data map for processors.
|
||||||
*/
|
*/
|
||||||
public Map<String, String> getData() {
|
public Map<String, String> getData() {
|
||||||
if (data == null)
|
if (data == null)
|
||||||
@@ -105,7 +110,13 @@ public class ForgeNewInstallProfile {
|
|||||||
return data.entrySet().stream().collect(Collectors.toMap(Map.Entry::getKey, e -> e.getValue().getClient()));
|
return data.entrySet().stream().collect(Collectors.toMap(Map.Entry::getKey, e -> e.getValue().getClient()));
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class Processor {
|
@Override
|
||||||
|
public void validate() throws JsonParseException, TolerableValidationException {
|
||||||
|
if (minecraft == null || json == null || path == null)
|
||||||
|
throw new JsonParseException("ForgeNewInstallProfile is malformed");
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class Processor implements Validation {
|
||||||
private final List<String> sides;
|
private final List<String> sides;
|
||||||
private final Artifact jar;
|
private final Artifact jar;
|
||||||
private final List<Artifact> classpath;
|
private final List<Artifact> classpath;
|
||||||
@@ -170,6 +181,12 @@ public class ForgeNewInstallProfile {
|
|||||||
public Map<String, String> getOutputs() {
|
public Map<String, String> getOutputs() {
|
||||||
return outputs == null ? Collections.emptyMap() : outputs;
|
return outputs == null ? Collections.emptyMap() : outputs;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void validate() throws JsonParseException, TolerableValidationException {
|
||||||
|
if (jar == null)
|
||||||
|
throw new JsonParseException("Processor::jar cannot be null");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class Datum {
|
public static class Datum {
|
||||||
|
|||||||
@@ -18,19 +18,26 @@
|
|||||||
package org.jackhuang.hmcl.download.optifine;
|
package org.jackhuang.hmcl.download.optifine;
|
||||||
|
|
||||||
import org.jackhuang.hmcl.download.DefaultDependencyManager;
|
import org.jackhuang.hmcl.download.DefaultDependencyManager;
|
||||||
import org.jackhuang.hmcl.game.LibrariesDownloadInfo;
|
import org.jackhuang.hmcl.download.VersionMismatchException;
|
||||||
import org.jackhuang.hmcl.game.Library;
|
import org.jackhuang.hmcl.game.*;
|
||||||
import org.jackhuang.hmcl.game.LibraryDownloadInfo;
|
|
||||||
import org.jackhuang.hmcl.game.Version;
|
|
||||||
import org.jackhuang.hmcl.task.Task;
|
import org.jackhuang.hmcl.task.Task;
|
||||||
import org.jackhuang.hmcl.task.TaskResult;
|
import org.jackhuang.hmcl.task.TaskResult;
|
||||||
import org.jackhuang.hmcl.util.Lang;
|
import org.jackhuang.hmcl.util.Lang;
|
||||||
import org.jackhuang.hmcl.util.StringUtils;
|
import org.jackhuang.hmcl.util.io.CompressingUtils;
|
||||||
|
import org.jackhuang.hmcl.util.io.FileUtils;
|
||||||
|
import org.jenkinsci.constant_pool_scanner.ConstantPool;
|
||||||
|
import org.jenkinsci.constant_pool_scanner.ConstantPoolScanner;
|
||||||
|
import org.jenkinsci.constant_pool_scanner.ConstantType;
|
||||||
|
import org.jenkinsci.constant_pool_scanner.Utf8Constant;
|
||||||
|
|
||||||
import java.util.Arrays;
|
import java.io.File;
|
||||||
import java.util.Collection;
|
import java.io.IOException;
|
||||||
import java.util.LinkedList;
|
import java.nio.file.FileSystem;
|
||||||
import java.util.List;
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
|
import static org.jackhuang.hmcl.util.Lang.getOrDefault;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* <b>Note</b>: OptiFine should be installed in the end.
|
* <b>Note</b>: OptiFine should be installed in the end.
|
||||||
@@ -42,13 +49,19 @@ public final class OptiFineInstallTask extends TaskResult<Version> {
|
|||||||
private final DefaultDependencyManager dependencyManager;
|
private final DefaultDependencyManager dependencyManager;
|
||||||
private final Version version;
|
private final Version version;
|
||||||
private final OptiFineRemoteVersion remote;
|
private final OptiFineRemoteVersion remote;
|
||||||
|
private final Path installer;
|
||||||
private final List<Task> dependents = new LinkedList<>();
|
private final List<Task> dependents = new LinkedList<>();
|
||||||
private final List<Task> dependencies = new LinkedList<>();
|
private final List<Task> dependencies = new LinkedList<>();
|
||||||
|
|
||||||
public OptiFineInstallTask(DefaultDependencyManager dependencyManager, Version version, OptiFineRemoteVersion remoteVersion) {
|
public OptiFineInstallTask(DefaultDependencyManager dependencyManager, Version version, OptiFineRemoteVersion remoteVersion) {
|
||||||
|
this(dependencyManager, version, remoteVersion, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public OptiFineInstallTask(DefaultDependencyManager dependencyManager, Version version, OptiFineRemoteVersion remoteVersion, Path installer) {
|
||||||
this.dependencyManager = dependencyManager;
|
this.dependencyManager = dependencyManager;
|
||||||
this.version = version;
|
this.version = version;
|
||||||
this.remote = remoteVersion;
|
this.remote = remoteVersion;
|
||||||
|
this.installer = installer;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -67,7 +80,7 @@ public final class OptiFineInstallTask extends TaskResult<Version> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void execute() {
|
public void execute() throws IOException {
|
||||||
if (!Arrays.asList("net.minecraft.client.main.Main",
|
if (!Arrays.asList("net.minecraft.client.main.Main",
|
||||||
"net.minecraft.launchwrapper.Launch")
|
"net.minecraft.launchwrapper.Launch")
|
||||||
.contains(version.getMainClass()))
|
.contains(version.getMainClass()))
|
||||||
@@ -82,6 +95,10 @@ public final class OptiFineInstallTask extends TaskResult<Version> {
|
|||||||
remote.getUrl()))
|
remote.getUrl()))
|
||||||
);
|
);
|
||||||
|
|
||||||
|
if (installer != null) {
|
||||||
|
FileUtils.copyFile(installer, dependencyManager.getGameRepository().getLibraryFile(version, library).toPath());
|
||||||
|
}
|
||||||
|
|
||||||
List<Library> libraries = new LinkedList<>();
|
List<Library> libraries = new LinkedList<>();
|
||||||
libraries.add(library);
|
libraries.add(library);
|
||||||
|
|
||||||
@@ -99,4 +116,37 @@ public final class OptiFineInstallTask extends TaskResult<Version> {
|
|||||||
|
|
||||||
public static class UnsupportedOptiFineInstallationException extends UnsupportedOperationException {
|
public static class UnsupportedOptiFineInstallationException extends UnsupportedOperationException {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Install OptiFine library from existing local file.
|
||||||
|
*
|
||||||
|
* @param dependencyManager game repository
|
||||||
|
* @param version version.json
|
||||||
|
* @param installer the OptiFine installer
|
||||||
|
* @return the task to install library
|
||||||
|
* @throws IOException if unable to read compressed content of installer file, or installer file is corrupted, or the installer is not the one we want.
|
||||||
|
* @throws VersionMismatchException if required game version of installer does not match the actual one.
|
||||||
|
*/
|
||||||
|
public static TaskResult<Version> install(DefaultDependencyManager dependencyManager, Version version, Path installer) throws IOException, VersionMismatchException {
|
||||||
|
File jar = dependencyManager.getGameRepository().getVersionJar(version);
|
||||||
|
Optional<String> gameVersion = GameVersion.minecraftVersion(jar);
|
||||||
|
if (!gameVersion.isPresent()) throw new IOException();
|
||||||
|
try (FileSystem fs = CompressingUtils.createReadOnlyZipFileSystem(installer)) {
|
||||||
|
ConstantPool pool = ConstantPoolScanner.parse(Files.readAllBytes(fs.getPath("Config.class")), ConstantType.UTF8);
|
||||||
|
List<String> constants = new ArrayList<>();
|
||||||
|
pool.list(Utf8Constant.class).forEach(utf8 -> constants.add(utf8.get()));
|
||||||
|
String mcVersion = getOrDefault(constants, constants.indexOf("MC_VERSION") + 1, null);
|
||||||
|
String ofEdition = getOrDefault(constants, constants.indexOf("OF_EDITION") + 1, null);
|
||||||
|
String ofRelease = getOrDefault(constants, constants.indexOf("OF_RELEASE") + 1, null);
|
||||||
|
|
||||||
|
if (mcVersion == null || ofEdition == null || ofRelease == null)
|
||||||
|
throw new IOException("Unrecognized OptiFine installer");
|
||||||
|
|
||||||
|
if (!mcVersion.equals(gameVersion.get()))
|
||||||
|
throw new VersionMismatchException(mcVersion, gameVersion.get());
|
||||||
|
|
||||||
|
return new OptiFineInstallTask(dependencyManager, version,
|
||||||
|
new OptiFineRemoteVersion(mcVersion, ofEdition + "_" + ofRelease, () -> null, false), installer);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -91,6 +91,10 @@ public final class Lang {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static <T> T getOrDefault(List<T> a, int index, T defaultValue) {
|
||||||
|
return index < 0 || index >= a.size() ? defaultValue : a.get(index);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Join two collections into one list.
|
* Join two collections into one list.
|
||||||
*
|
*
|
||||||
|
|||||||
Reference in New Issue
Block a user