mirror of
https://github.com/HMCL-dev/HMCL.git
synced 2025-04-18 18:40:34 +08:00
自动开始下载 JavaFX (#1063)
* Automatically start downloading JavaFX * Reimplement ProgressFrame with JDialog
This commit is contained in:
parent
2cb70a41fb
commit
3fad76aae1
@ -99,6 +99,9 @@ public final class Main {
|
||||
} catch (SelfDependencyPatcher.IncompatibleVersionException e) {
|
||||
LOG.log(Level.SEVERE, "unable to patch JVM", e);
|
||||
showErrorAndExit(i18n("fatal.javafx.incompatible"));
|
||||
} catch (SelfDependencyPatcher.CanceledException e) {
|
||||
LOG.log(Level.SEVERE, "User cancels downloading JavaFX", e);
|
||||
System.exit(0);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -49,18 +49,18 @@ import static org.jackhuang.hmcl.util.Logging.LOG;
|
||||
import static org.jackhuang.hmcl.util.i18n.I18n.i18n;
|
||||
import static org.jackhuang.hmcl.util.platform.JavaVersion.CURRENT_JAVA;
|
||||
|
||||
import java.awt.Dialog;
|
||||
import java.awt.GridBagConstraints;
|
||||
import java.awt.GridBagLayout;
|
||||
import java.awt.Insets;
|
||||
import java.awt.*;
|
||||
import java.awt.event.WindowAdapter;
|
||||
import java.awt.event.WindowEvent;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.io.UncheckedIOException;
|
||||
import java.net.URL;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.StandardCopyOption;
|
||||
import java.util.*;
|
||||
import java.util.List;
|
||||
|
||||
import javax.swing.*;
|
||||
|
||||
@ -202,7 +202,7 @@ public final class SelfDependencyPatcher {
|
||||
/**
|
||||
* Patch in any missing dependencies, if any.
|
||||
*/
|
||||
public static void patch() throws PatchException, IncompatibleVersionException {
|
||||
public static void patch() throws PatchException, IncompatibleVersionException, CanceledException {
|
||||
// Do nothing if JavaFX is detected
|
||||
try {
|
||||
try {
|
||||
@ -233,10 +233,8 @@ public final class SelfDependencyPatcher {
|
||||
// Download missing dependencies
|
||||
List<DependencyDescriptor> missingDependencies = checkMissingDependencies();
|
||||
if (!missingDependencies.isEmpty()) {
|
||||
final Repository repository = showChooseRepositoryDialog();
|
||||
|
||||
try {
|
||||
fetchDependencies(repository, missingDependencies);
|
||||
fetchDependencies(missingDependencies);
|
||||
} catch (IOException e) {
|
||||
throw new PatchException("Failed to download dependencies", e);
|
||||
}
|
||||
@ -273,9 +271,9 @@ public final class SelfDependencyPatcher {
|
||||
}
|
||||
}
|
||||
|
||||
int res = JOptionPane.showConfirmDialog(null, panel, i18n("repositories.chooser.title"), JOptionPane.YES_NO_OPTION);
|
||||
int res = JOptionPane.showConfirmDialog(null, panel, i18n("repositories.chooser.title"), JOptionPane.OK_CANCEL_OPTION);
|
||||
|
||||
if (res == JOptionPane.YES_OPTION) {
|
||||
if (res == JOptionPane.OK_OPTION) {
|
||||
final Enumeration<AbstractButton> buttons = buttonGroup.getElements();
|
||||
while (buttons.hasMoreElements()) {
|
||||
final AbstractButton button = buttons.nextElement();
|
||||
@ -317,28 +315,86 @@ public final class SelfDependencyPatcher {
|
||||
*
|
||||
* @throws IOException When the files cannot be fetched or saved.
|
||||
*/
|
||||
private static void fetchDependencies(Repository repository, List<DependencyDescriptor> dependencies) throws IOException {
|
||||
ProgressFrame dialog = new ProgressFrame(i18n("download.javafx"));
|
||||
dialog.setVisible(true);
|
||||
|
||||
int progress = 0;
|
||||
for (DependencyDescriptor dependency : dependencies) {
|
||||
int currentProgress = ++progress;
|
||||
final String url = repository.resolveDependencyURL(dependency);
|
||||
SwingUtilities.invokeLater(() -> {
|
||||
dialog.setStatus(url);
|
||||
dialog.setProgress(currentProgress, dependencies.size() + 1);
|
||||
});
|
||||
|
||||
LOG.info("Downloading " + url);
|
||||
Files.createDirectories(dependency.localPath().getParent());
|
||||
try (InputStream is = new URL(url).openStream()) {
|
||||
Files.copy(is, dependency.localPath(), StandardCopyOption.REPLACE_EXISTING);
|
||||
}
|
||||
verifyChecksum(dependency);
|
||||
private static void fetchDependencies(List<DependencyDescriptor> dependencies) throws IOException {
|
||||
class BooleanHole {
|
||||
volatile boolean value = false;
|
||||
}
|
||||
|
||||
dialog.dispose();
|
||||
boolean isFirstTime = true;
|
||||
|
||||
byte[] buffer = new byte[IOUtils.DEFAULT_BUFFER_SIZE];
|
||||
Repository repository = Repository.DEFAULT;
|
||||
|
||||
int count = 0;
|
||||
while (true) {
|
||||
BooleanHole isCancelled = new BooleanHole();
|
||||
BooleanHole showDetails = new BooleanHole();
|
||||
|
||||
ProgressFrame dialog = new ProgressFrame(i18n("download.javafx"));
|
||||
dialog.setProgressMaximum(dependencies.size() + 1);
|
||||
dialog.setProgress(count);
|
||||
dialog.setOnCancel(() -> isCancelled.value = true);
|
||||
dialog.setOnChangeSource(() -> {
|
||||
isCancelled.value = true;
|
||||
showDetails.value = true;
|
||||
});
|
||||
dialog.setVisible(true);
|
||||
try {
|
||||
if (isFirstTime) {
|
||||
isFirstTime = false;
|
||||
try {
|
||||
//noinspection BusyWait
|
||||
Thread.sleep(1000);
|
||||
} catch (InterruptedException ignored) {
|
||||
}
|
||||
}
|
||||
Files.createDirectories(DependencyDescriptor.DEPENDENCIES_DIR_PATH);
|
||||
for (int i = count; i < dependencies.size(); i++) {
|
||||
if (isCancelled.value) {
|
||||
throw new CanceledException();
|
||||
}
|
||||
|
||||
DependencyDescriptor dependency = dependencies.get(i);
|
||||
|
||||
final String url = repository.resolveDependencyURL(dependency);
|
||||
SwingUtilities.invokeLater(() -> {
|
||||
dialog.setCurrent(dependency.module);
|
||||
dialog.incrementProgress();
|
||||
});
|
||||
|
||||
LOG.info("Downloading " + url);
|
||||
|
||||
try (InputStream is = new URL(url).openStream();
|
||||
OutputStream os = Files.newOutputStream(dependency.localPath())) {
|
||||
|
||||
int read;
|
||||
while ((read = is.read(buffer, 0, IOUtils.DEFAULT_BUFFER_SIZE)) >= 0) {
|
||||
if (isCancelled.value) {
|
||||
try {
|
||||
os.close();
|
||||
} finally {
|
||||
Files.deleteIfExists(dependency.localPath());
|
||||
}
|
||||
throw new CanceledException();
|
||||
}
|
||||
os.write(buffer, 0, read);
|
||||
}
|
||||
}
|
||||
verifyChecksum(dependency);
|
||||
count++;
|
||||
}
|
||||
} catch (CanceledException e) {
|
||||
dialog.dispose();
|
||||
if (showDetails.value) {
|
||||
repository = showChooseRepositoryDialog();
|
||||
continue;
|
||||
} else {
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
dialog.dispose();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
private static List<DependencyDescriptor> checkMissingDependencies() {
|
||||
@ -383,64 +439,78 @@ public final class SelfDependencyPatcher {
|
||||
public static class IncompatibleVersionException extends Exception {
|
||||
}
|
||||
|
||||
public static class CanceledException extends RuntimeException {
|
||||
}
|
||||
|
||||
public static class ProgressFrame extends JDialog {
|
||||
|
||||
private final JProgressBar progressBar;
|
||||
private final JLabel progressText;
|
||||
private final JButton btnChangeSource;
|
||||
private final JButton btnCancel;
|
||||
|
||||
public ProgressFrame(String title) {
|
||||
super((Dialog) null);
|
||||
|
||||
JPanel panel = new JPanel();
|
||||
|
||||
setResizable(false);
|
||||
setTitle(title);
|
||||
setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
|
||||
setBounds(100, 100, 600, 150);
|
||||
setBounds(100, 100, 500, 200);
|
||||
setContentPane(panel);
|
||||
setLocationRelativeTo(null);
|
||||
|
||||
GridBagLayout gridBagLayout = new GridBagLayout();
|
||||
gridBagLayout.columnWidths = new int[]{600, 0};
|
||||
gridBagLayout.rowHeights = new int[]{0, 0, 0, 200};
|
||||
gridBagLayout.columnWeights = new double[]{1.0, Double.MIN_VALUE};
|
||||
gridBagLayout.rowWeights = new double[]{0.0, 0.0, 0.0, 1.0};
|
||||
panel.setLayout(gridBagLayout);
|
||||
|
||||
progressText = new JLabel("");
|
||||
GridBagConstraints gbc_lblProgressText = new GridBagConstraints();
|
||||
gbc_lblProgressText.insets = new Insets(10, 0, 5, 0);
|
||||
gbc_lblProgressText.gridx = 0;
|
||||
gbc_lblProgressText.gridy = 0;
|
||||
panel.add(progressText, gbc_lblProgressText);
|
||||
JPanel content = new JPanel();
|
||||
content.setLayout(new BoxLayout(content, BoxLayout.Y_AXIS));
|
||||
|
||||
for (String note : i18n("download.javafx.notes").split("\n")) {
|
||||
content.add(new JLabel(note));
|
||||
}
|
||||
content.add(new JLabel("<html><br/></html>"));
|
||||
progressText = new JLabel(i18n("download.javafx.prepare"));
|
||||
content.add(progressText);
|
||||
progressBar = new JProgressBar();
|
||||
GridBagConstraints gbc_progressBar = new GridBagConstraints();
|
||||
gbc_progressBar.insets = new Insets(0, 25, 5, 25);
|
||||
gbc_progressBar.fill = GridBagConstraints.HORIZONTAL;
|
||||
gbc_progressBar.gridx = 0;
|
||||
gbc_progressBar.gridy = 1;
|
||||
panel.add(progressBar, gbc_progressBar);
|
||||
content.add(progressBar);
|
||||
|
||||
JButton btnCancel = new JButton(i18n("button.cancel"));
|
||||
btnCancel.addActionListener(e -> {
|
||||
System.exit(-1);
|
||||
});
|
||||
GridBagConstraints gbc_btnCancel = new GridBagConstraints();
|
||||
gbc_btnCancel.insets = new Insets(0, 25, 5, 25);
|
||||
gbc_btnCancel.fill = GridBagConstraints.HORIZONTAL;
|
||||
gbc_btnCancel.gridx = 0;
|
||||
gbc_btnCancel.gridy = 2;
|
||||
panel.add(btnCancel, gbc_btnCancel);
|
||||
final JPanel buttonBar = new JPanel();
|
||||
btnChangeSource = new JButton(i18n("button.change_source"));
|
||||
btnCancel = new JButton(i18n("button.cancel"));
|
||||
buttonBar.add(btnChangeSource);
|
||||
buttonBar.add(btnCancel);
|
||||
|
||||
panel.setLayout(new BorderLayout());
|
||||
panel.setBorder(BorderFactory.createEmptyBorder(10, 5, 0, 5));
|
||||
panel.add(content, BorderLayout.CENTER);
|
||||
panel.add(buttonBar, BorderLayout.SOUTH);
|
||||
}
|
||||
|
||||
public void setStatus(String status) {
|
||||
progressText.setText(status);
|
||||
public void setCurrent(String component) {
|
||||
progressText.setText(i18n("download.javafx.component", component));
|
||||
}
|
||||
|
||||
public void setProgress(int current, int total) {
|
||||
progressBar.setValue(current);
|
||||
public void setProgressMaximum(int total) {
|
||||
progressBar.setMaximum(total);
|
||||
}
|
||||
|
||||
public void setProgress(int n) {
|
||||
progressBar.setValue(n);
|
||||
}
|
||||
|
||||
public void incrementProgress() {
|
||||
progressBar.setValue(progressBar.getValue() + 1);
|
||||
}
|
||||
|
||||
public void setOnCancel(Runnable action) {
|
||||
btnCancel.addActionListener(e -> action.run());
|
||||
addWindowListener(new WindowAdapter() {
|
||||
@Override
|
||||
public void windowClosing(WindowEvent e) {
|
||||
action.run();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void setOnChangeSource(Runnable action) {
|
||||
btnChangeSource.addActionListener(e -> action.run());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -140,6 +140,7 @@ assets.download_all=Asset Integrity Check
|
||||
assets.index.malformed=Asset index malformed, you can retry by "Update Game Asset Files" in version settings.
|
||||
|
||||
button.cancel=Cancel
|
||||
button.change_source=Change Download Source
|
||||
button.clear=Clear
|
||||
button.delete=Delete
|
||||
button.edit=Edit
|
||||
@ -283,7 +284,10 @@ download.provider.mojang=Mojang (OptiFine download service is provided by BMCLAP
|
||||
download.provider.official=Try to load from official source
|
||||
download.provider.balanced=Load from fastest source
|
||||
download.provider.mirror=Try to load from mirror source
|
||||
download.javafx=Downloading the JavaFX runtime components
|
||||
download.javafx=Downloading necessary runtime components
|
||||
download.javafx.notes=Downloading necessary components for HMCL through network.\nClick the "Change Download Source" button to see more details and select the download source, or click the "Cancel" button to stop and exit.\nNote: If the download speed is too slow, please try to change the download source.
|
||||
download.javafx.component=Downloading the module %s
|
||||
download.javafx.prepare=Ready to download
|
||||
|
||||
extension.bat=Windows Bat file
|
||||
extension.mod=Mod file
|
||||
@ -747,8 +751,8 @@ profile.use_relative_path=Use relative path for game directory if possible
|
||||
repositories.custom=Custom Maven Repository (%s)
|
||||
repositories.maven_central=Universal (Maven Central)
|
||||
repositories.aliyun_mirror=Chinese mainland (Aliyun Maven Repository)
|
||||
repositories.chooser=JavaFX is missing. Do you want to automatically download and load JavaFX runtime components from web?\nSelect 'Yes' to download the JavaFX runtime components from the specified download source and start the HMCL, or select 'No' to exit the program.\nDownload Source:
|
||||
repositories.chooser.title=Do you want to download JavaFX?
|
||||
repositories.chooser=JavaFX is missing, HMCL needs JavaFX to work.\nClick the 'Ok' to download the JavaFX runtime components from specified download source and start HMCL, or click the 'Cancel' to exit.\nSelect Download Sources:
|
||||
repositories.chooser.title=Select a download source to download JavaFX
|
||||
|
||||
resourcepack=Resource Pack
|
||||
|
||||
|
@ -140,6 +140,7 @@ assets.download_all=驗證資源檔案完整性
|
||||
assets.index.malformed=資源檔案的索引檔案損壞,您可以在遊戲 [設定] 頁面右上角的設定按鈕中選擇 [更新遊戲資源檔案],以修復該問題
|
||||
|
||||
button.cancel=取消
|
||||
button.change_source=切換下載源
|
||||
button.clear=清除
|
||||
button.delete=刪除
|
||||
button.edit=編輯
|
||||
@ -283,7 +284,10 @@ download.provider.mojang=官方伺服器 (OptiFine 自動安裝的下載來源
|
||||
download.provider.official=儘量使用官方源(最新,但可能加載慢)
|
||||
download.provider.balanced=選擇加載速度快的下載源(平衡,但可能不是最新)
|
||||
download.provider.mirror=儘量使用鏡像源(加載快,但可能不是最新)
|
||||
download.javafx=正在下載 JavaFX 運行時組件
|
||||
download.javafx=正在下載必要的運行時組件
|
||||
download.javafx.notes=正在通過網絡下載 HMCL 必要的運行時組件。\n點擊“切換下載源”按鈕查看詳情以及選擇下載源,點擊“取消”按鈕停止並退出。\n注意:如果下載速度過慢,請嘗試切换下載源。
|
||||
download.javafx.component=正在下載模塊 %s
|
||||
download.javafx.prepare=準備開始下載
|
||||
|
||||
extension.bat=Windows 指令碼
|
||||
extension.mod=模組檔案
|
||||
@ -746,8 +750,8 @@ profile.use_relative_path=如可行,則在遊戲目錄使用相對路徑
|
||||
repositories.custom=自定義 Maven 倉庫(%s)
|
||||
repositories.maven_central=全球(Maven Central)
|
||||
repositories.aliyun_mirror=中國大陸(阿里雲 Maven 倉庫)
|
||||
repositories.chooser=缺少 JavaFX 運行環境。是否需要從網路下載並加載 JavaFX 運行時組件?\n選擇“是”從指定下載源下載 JavaFX 運行時組件並啟動HMCL,選擇“否”退出程式。\n下載源:
|
||||
repositories.chooser.title=是否下載 JavaFX?
|
||||
repositories.chooser=缺少 JavaFX 運行環境,HMCL 需要 JavaFX 才能正常運行。\n點擊“確認”從指定下載源下載 JavaFX 運行時組件並啟動 HMCL,點擊“取消”退出程式。\n選擇下載源:
|
||||
repositories.chooser.title=選擇下載源下載JavaFX
|
||||
|
||||
resourcepack=資源包
|
||||
|
||||
|
@ -140,6 +140,7 @@ assets.download_all=检查资源文件完整性
|
||||
assets.index.malformed=资源文件的索引文件损坏,您可以在游戏设置页面右上角的设置按钮中选择更新游戏资源文件以修复该问题
|
||||
|
||||
button.cancel=取消
|
||||
button.change_source=切换下载源
|
||||
button.clear=清除
|
||||
button.delete=删除
|
||||
button.edit=修改
|
||||
@ -283,7 +284,10 @@ download.provider.mojang=官方(OptiFine 自动安装使用 BMCLAPI 下载源
|
||||
download.provider.official=尽量使用官方源(最新,但可能加载慢)
|
||||
download.provider.balanced=选择加载速度快的下载源(平衡,但可能不是最新)
|
||||
download.provider.mirror=尽量使用镜像源(加载快,但可能不是最新)
|
||||
download.javafx=正在下载 JavaFX 运行时组件
|
||||
download.javafx=正在下载必要的运行时组件
|
||||
download.javafx.notes=正在通过网络下载 HMCL 必要的运行时组件。\n点击“切换下载源”按钮查看详情以及选择下载源,点击“取消”按钮停止并退出。\n注意:如果下载速度过慢,请尝试切换下载源。
|
||||
download.javafx.component=正在下载模块 %s
|
||||
download.javafx.prepare=准备开始下载
|
||||
|
||||
extension.bat=Windows 脚本
|
||||
extension.mod=模组文件
|
||||
@ -746,8 +750,8 @@ profile.use_relative_path=若可能,游戏目录使用相对路径
|
||||
repositories.custom=自定义 Maven 仓库(%s)
|
||||
repositories.maven_central=全球(Maven Central)
|
||||
repositories.aliyun_mirror=中国大陆(阿里云 Maven 仓库)
|
||||
repositories.chooser=缺少 JavaFX 运行环境,是否需要从网络下载并加载 JavaFX 运行时组件?\n选择“是”从指定下载源下载 JavaFX 运行时组件并启动 HMCL,选择“否”退出程序。\n下载源:
|
||||
repositories.chooser.title=是否下载 JavaFX?
|
||||
repositories.chooser=缺少 JavaFX 运行环境,HMCL 需要 JavaFX 才能正常运行。\n点击“确认”从指定下载源下载 JavaFX 运行时组件并启动 HMCL,点击“取消”退出程序。\n选择下载源:
|
||||
repositories.chooser.title=选择下载源下载 JavaFX
|
||||
|
||||
resourcepack=资源包
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user