Compare commits

...
32 Commits
Author SHA1 Message Date
Zkitefly a8929560df Update PURCHASE_URL (#3797) 2025-04-05 21:04:55 +08:00
Glavo 22c8c59b5d 优化 CacheRepository (#3794) 2025-04-05 06:08:02 +08:00
ZkiteflyandGlavo 78b043c19c 添加 unlisted-versions-of-minecraft (#3247)
* 添加 unlisted-versions-of-minecraft

* Update BMCLAPIDownloadProvider.java

* Update VersionsPage.java

* Update VersionsPage.java

* Update BMCLAPIDownloadProvider.java

* update name

* update

* update

* update

* update

---------

Co-authored-by: Glavo <zjx001202@gmail.com>
2025-04-04 22:49:31 +08:00
3gf8jv4dv fd51fb7d26 chore(locale): adjust the use of quotation marks in spanish (#3685)
Replace the quotation marks with guillemets.
2025-04-04 22:38:05 +08:00
Burning_TNT 43c59dad9c Fix: There are two progress bars when updating HMCL (#3793) 2025-04-04 20:54:08 +08:00
Zkitefly 81b0547610 更新链接为 Metadata 中的常量,优化代码可维护性 (#3674)
* 更新链接为 Metadata 中的常量,优化代码可维护性

* update
2025-04-04 18:47:42 +08:00
997a16486b 优化下载窗口标题 (#3771)
* update

* Update HMCL/src/main/resources/assets/lang/I18N.properties

Co-authored-by: 3gf8jv4dv <3gf8jv4dv@gmail.com>

* Update HMCL/src/main/resources/assets/lang/I18N.properties

Co-authored-by: 3gf8jv4dv <3gf8jv4dv@gmail.com>

* Update HMCL/src/main/resources/assets/lang/I18N_es.properties

Co-authored-by: 3gf8jv4dv <3gf8jv4dv@gmail.com>

* Update HMCL/src/main/resources/assets/lang/I18N_es.properties

Co-authored-by: 3gf8jv4dv <3gf8jv4dv@gmail.com>

* Apply suggestions from code review

Co-authored-by: 3gf8jv4dv <3gf8jv4dv@gmail.com>

* update

* update

* update

---------

Co-authored-by: 3gf8jv4dv <3gf8jv4dv@gmail.com>
Co-authored-by: Glavo <zjx001202@gmail.com>
2025-04-04 00:47:37 +08:00
Wulian233andGlavo 36ebdab698 折叠高级设置中基于特定系统不可用的选项 (#3734)
* 隐藏高级设置中基于特定系统不可用的选项

* 不支持的系统使用折叠选项

* 完成~待审

* 修改文本描述

* 修正翻译

* update

* update

---------

Co-authored-by: Glavo <zjx001202@gmail.com>
2025-04-03 23:08:34 +08:00
Glavo a6471bca09 Add 25w14craftmine to versions.txt (#3781) 2025-04-02 00:35:05 +08:00
Burning_TNT cb6320ffae Fix #3627: 修复离线账户切换模型时左侧 3D 预览不会即时更新的问题 (#3769) 2025-04-01 15:43:01 +08:00
Burning_TNT 7cf520cb48 Fix #3589: 修复 Windows 平台部分语言编码下无法打开 Minecraft 中文 Wiki 的问题 (#3770) 2025-04-01 15:40:05 +08:00
Minecraft-温迪andGlavo 7dc922d4bd Fix #3777: 修复使用 Rosetta 2 转译运行时无法正确识别系统架构的问题 (#3778)
* update

* update

---------

Co-authored-by: Glavo <zjx001202@gmail.com>
2025-04-01 15:31:09 +08:00
Glavo 8e57729349 Update versions.txt (#3744) 2025-03-26 22:55:44 +08:00
Glavo 66ed87eb1e 修复 cslApi 为 null 时启动器崩溃的问题 (#3762) 2025-03-24 02:52:17 +08:00
ShulkerSakuraandGlavo 27ffc8eec5 Add MacOS Style Icon (#3755)
* Add MacOS Style Icon

* MacOS Style Alt Icon

* Scaled Mac Style Icon

* Delete unused icons

* Rename icon

* update

* Optimize icon file

---------

Co-authored-by: Glavo <zjx001202@gmail.com>
2025-03-22 19:13:32 +08:00
Glavo e2f50c92e9 修复无法解析部分 Java 版本号的问题 (#3759)
* 修复无法解析部分 Java 版本号的问题

* update

* update
2025-03-22 12:17:13 +08:00
Glavo e8ff42c4b2 修复取消导出整合包时弹出报错对话框的问题 (#3752)
* 修复取消导出整合包时弹出报错对话框的问题

* Update
2025-03-21 11:01:37 +08:00
Glavo 44ca2cdb55 更新主页公告 (#3751)
* 更新主页公告

* update

* update
2025-03-20 21:25:30 +08:00
Glavo 77bbee02f9 消除 JEP 498 引入的弃用警告 (#3750)
* 消除 JEP 498 引入的弃用警告

* Fix checkstyle
2025-03-20 16:59:46 +08:00
Glavo e4c7b137f1 Add --enable-native-access for javafx.graphics (#3749)
* Add --enable-native-access for javafx.graphics

* fix checkstyle

* update
2025-03-20 16:21:17 +08:00
Glavo efd088e014 在日志中记录编码信息 (#3747)
* 在日志中记录 Code Page

* update

* update
2025-03-20 10:02:16 +08:00
Glavo 2ae266491c 更新关于页面头像 (#3745) 2025-03-18 20:11:07 +08:00
Glavo 0912613cfc 优化 ScrollUtils (#3432)
* 优化 ScrollUtils

* update
2025-03-18 13:31:47 +08:00
Glavo d9e3816b8e 优化账户列表页面 (#3736) 2025-03-13 22:18:23 +08:00
GlavoandZkitefly bdaf72fe2f 修复 ModrinthRemoteModRepository::getRemoteVersionByLocalFile 未捕获 NoSuchFileException 的问题 (#3735)
---------

Co-authored-by: Zkitefly <2573874409@qq.com>
2025-03-11 21:58:18 +08:00
Glavo 7efbbefdcf HTMLRenderer 应当避免插入过多换行符 (#3733) 2025-03-10 22:14:46 +08:00
Glavo ad71216bfc 为部分页面添加半透明背景遮罩 (#3730) 2025-03-09 13:02:00 +08:00
Wulian233 57903da09b Close #3728: 为更新日志对话框添加平滑滚动 (#3729) 2025-03-08 22:35:51 +08:00
3gf8jv4dv cf56c8035b fix: add scrollbar for GameCrashWindow (#3723)
Shows a scroll bar when needed to prevent user misinterpretation
when there is too much information.
2025-03-08 16:32:54 +08:00
Glavo dbf519b435 Fix #3720: 修复备份文件路径被错误地设置为源世界的问题 (#3722) 2025-03-08 16:18:49 +08:00
Glavo 696b9bd7ef Fix #3694: 显示对话框时隐藏焦点 (#3717) 2025-03-08 08:47:19 +08:00
3gf8jv4dv ec5236368a chore(locale): add new translations of world parts for es and ru (#3714) 2025-03-07 21:39:39 +08:00
56 changed files with 1915 additions and 193 deletions

No files matched your search

+3 -2
View File
@@ -144,8 +144,9 @@ tasks.getByName<com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar>("sha
"javafx.controls/com.sun.javafx.scene.control",
"javafx.controls/com.sun.javafx.scene.control.behavior",
"javafx.controls/javafx.scene.control.skin",
"jdk.attach/sun.tools.attach"
).joinToString(" ")
"jdk.attach/sun.tools.attach",
).joinToString(" "),
"Enable-Native-Access" to "ALL-UNNAMED"
)
System.getenv("GITHUB_SHA")?.also {
Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

@@ -229,6 +229,11 @@ public final class Launcher extends Application {
LOG.info("Processor Identifier: " + System.getenv("PROCESSOR_IDENTIFIER"));
}
LOG.info("System Architecture: " + Architecture.SYSTEM_ARCH.getDisplayName());
LOG.info("Native Encoding: " + OperatingSystem.NATIVE_CHARSET);
LOG.info("JNU Encoding: " + System.getProperty("sun.jnu.encoding"));
if (OperatingSystem.CURRENT_OS == OperatingSystem.WINDOWS) {
LOG.info("Code Page: " + OperatingSystem.CODE_PAGE);
}
LOG.info("Java Architecture: " + Architecture.CURRENT_ARCH.getDisplayName());
LOG.info("Java Version: " + System.getProperty("java.version") + ", " + System.getProperty("java.vendor"));
LOG.info("Java VM Version: " + System.getProperty("java.vm.name") + " (" + System.getProperty("java.vm.info") + "), " + System.getProperty("java.vm.vendor"));
@@ -20,6 +20,7 @@ package org.jackhuang.hmcl;
import javafx.application.Platform;
import javafx.scene.control.Alert;
import org.jackhuang.hmcl.ui.AwtUtils;
import org.jackhuang.hmcl.util.ModuleHelper;
import org.jackhuang.hmcl.util.SelfDependencyPatcher;
import org.jackhuang.hmcl.ui.SwingUtils;
import org.jackhuang.hmcl.java.JavaRuntime;
@@ -31,6 +32,7 @@ import javax.net.ssl.TrustManagerFactory;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Method;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
@@ -69,6 +71,8 @@ public final class Main {
checkJavaFX();
verifyJavaFX();
addEnableNativeAccess();
enableUnsafeMemoryAccess();
Launcher.main(args);
}
@@ -79,7 +83,7 @@ public final class Main {
}
private static void initIcon() {
java.awt.Image image = java.awt.Toolkit.getDefaultToolkit().getImage(Main.class.getResource("/assets/img/icon@8x.png"));
java.awt.Image image = java.awt.Toolkit.getDefaultToolkit().getImage(Main.class.getResource("/assets/img/icon-mac.png"));
AwtUtils.setAppleIcon(image);
}
@@ -116,10 +120,36 @@ public final class Main {
Class.forName("javafx.stage.Stage"); // javafx.graphics
Class.forName("javafx.scene.control.Skin"); // javafx.controls
} catch (Exception e) {
e.printStackTrace(System.err);
showErrorAndExit(i18n("fatal.javafx.incomplete"));
}
}
private static void addEnableNativeAccess() {
if (JavaRuntime.CURRENT_VERSION > 21) {
try {
ModuleHelper.addEnableNativeAccess(Class.forName("javafx.stage.Stage")); // javafx.graphics
} catch (ClassNotFoundException e) {
e.printStackTrace(System.err);
showErrorAndExit(i18n("fatal.javafx.incomplete"));
}
}
}
private static void enableUnsafeMemoryAccess() {
// https://openjdk.org/jeps/498
if (JavaRuntime.CURRENT_VERSION == 24 || JavaRuntime.CURRENT_VERSION == 25) {
try {
Class<?> clazz = Class.forName("sun.misc.Unsafe");
Method trySetMemoryAccessWarned = clazz.getDeclaredMethod("trySetMemoryAccessWarned");
trySetMemoryAccessWarned.setAccessible(true);
trySetMemoryAccessWarned.invoke(null);
} catch (Throwable e) {
e.printStackTrace(System.err);
}
}
}
/**
* Indicates that a fatal error has occurred, and that the application cannot start.
*/
@@ -37,12 +37,16 @@ public final class Metadata {
public static final String TITLE = NAME + " " + VERSION;
public static final String FULL_TITLE = FULL_NAME + " v" + VERSION;
public static final String HMCL_UPDATE_URL = System.getProperty("hmcl.update_source.override", "https://hmcl.huangyuhui.net/api/update_link");
public static final String CONTACT_URL = "https://docs.hmcl.net/help.html";
public static final String HELP_URL = "https://docs.hmcl.net";
public static final String CHANGELOG_URL = "https://docs.hmcl.net/changelog/";
public static final String PUBLISH_URL = "https://hmcl.huangyuhui.net";
public static final String EULA_URL = "https://docs.hmcl.net/eula/hmcl.html";
public static final String ABOUT_URL = PUBLISH_URL + "/about";
public static final String DOWNLOAD_URL = PUBLISH_URL + "/download";
public static final String HMCL_UPDATE_URL = System.getProperty("hmcl.update_source.override", PUBLISH_URL + "/api/update_link");
public static final String DOCS_URL = "https://docs.hmcl.net";
public static final String CONTACT_URL = DOCS_URL + "/help.html";
public static final String CHANGELOG_URL = DOCS_URL + "/changelog/";
public static final String EULA_URL = DOCS_URL + "/eula/hmcl.html";
public static final String GROUPS_URL = DOCS_URL + "/groups.html";
public static final String BUILD_CHANNEL = JarUtils.getManifestAttribute("Build-Channel", "nightly");
public static final String GITHUB_SHA = JarUtils.getManifestAttribute("GitHub-SHA", null);
@@ -776,6 +776,8 @@ public final class FXUtils {
String icon;
if (OperatingSystem.CURRENT_OS == OperatingSystem.WINDOWS) {
icon = "/assets/img/icon.png";
} else if (OperatingSystem.CURRENT_OS == OperatingSystem.OSX) {
icon = "/assets/img/icon-mac.png";
} else {
icon = "/assets/img/icon@4x.png";
}
@@ -400,7 +400,7 @@ public class GameCrashWindow extends Stage {
ScrollPane reasonPane = new ScrollPane(reasonTextFlow);
reasonPane.setFitToWidth(true);
reasonPane.setHbarPolicy(ScrollPane.ScrollBarPolicy.NEVER);
reasonPane.setVbarPolicy(ScrollPane.ScrollBarPolicy.NEVER);
reasonPane.setVbarPolicy(ScrollPane.ScrollBarPolicy.AS_NEEDED);
gameDirPane.setPadding(new Insets(8));
VBox.setVgrow(gameDirPane, Priority.ALWAYS);
@@ -422,7 +422,7 @@ public class GameCrashWindow extends Stage {
logButton.setOnAction(e -> showLogWindow());
JFXButton helpButton = FXUtils.newRaisedButton(i18n("help"));
helpButton.setOnAction(e -> FXUtils.openLink("https://docs.hmcl.net/help.html"));
helpButton.setOnAction(e -> FXUtils.openLink(Metadata.CONTACT_URL));
FXUtils.installFastTooltip(helpButton, i18n("logwindow.help"));
@@ -151,6 +151,12 @@ public final class HTMLRenderer {
children.add(textNode);
}
private void appendAutoLineBreak(String text) {
AutoLineBreak textNode = new AutoLineBreak(text);
applyStyle(textNode);
children.add(textNode);
}
private void appendImage(Node node) {
String src = node.absUrl("src");
URI imageUri = null;
@@ -230,7 +236,7 @@ public final class HTMLRenderer {
case "h6":
case "tr":
if (!children.isEmpty())
appendText("\n\n");
appendAutoLineBreak("\n\n");
break;
}
@@ -252,15 +258,62 @@ public final class HTMLRenderer {
case "h4":
case "h5":
case "h6":
appendText("\n");
appendAutoLineBreak("\n");
break;
}
}
private static boolean isSpacing(String text) {
if (text == null)
return true;
for (int i = 0; i < text.length(); i++) {
char ch = text.charAt(i);
if (ch != ' ' && ch != '\t')
return false;
}
return true;
}
public void mergeLineBreaks() {
for (int i = 0; i < this.children.size(); i++) {
javafx.scene.Node child = this.children.get(i);
if (child instanceof AutoLineBreak) {
int lastAutoLineBreak = -1;
for (int j = i + 1; j < this.children.size(); j++) {
javafx.scene.Node otherChild = this.children.get(j);
if (otherChild instanceof AutoLineBreak) {
lastAutoLineBreak = j;
} else if (otherChild instanceof Text && isSpacing(((Text) otherChild).getText())) {
// do nothing
} else {
break;
}
}
if (lastAutoLineBreak > 0) {
this.children.subList(i + 1, lastAutoLineBreak + 1).clear();
if (((Text) child).getText().length() == 1) {
((Text) child).setText("\n\n");
}
}
}
}
}
public TextFlow render() {
TextFlow textFlow = new TextFlow();
textFlow.getStyleClass().add("html");
textFlow.getChildren().setAll(children);
return textFlow;
}
private static final class AutoLineBreak extends Text {
public AutoLineBreak(String text) {
super(text);
}
}
}
@@ -25,14 +25,11 @@ import javafx.animation.Animation.Status;
import javafx.animation.KeyFrame;
import javafx.animation.Timeline;
import javafx.event.EventHandler;
import javafx.geometry.Bounds;
import javafx.scene.control.ScrollPane;
import javafx.scene.input.MouseEvent;
import javafx.scene.input.ScrollEvent;
import javafx.util.Duration;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.Function;
import org.jackhuang.hmcl.util.Holder;
/**
* Utility class for ScrollPanes.
@@ -142,23 +139,23 @@ final class ScrollUtils {
smoothScroll(scrollPane, speed, trackPadAdjustment);
}
private static final double[] FRICTIONS = {0.99, 0.1, 0.05, 0.04, 0.03, 0.02, 0.01, 0.04, 0.01, 0.008, 0.008, 0.008, 0.008, 0.0006, 0.0005, 0.00003, 0.00001};
private static final Duration DURATION = Duration.millis(3);
private static void smoothScroll(ScrollPane scrollPane, double speed, double trackPadAdjustment) {
final double[] frictions = {0.99, 0.1, 0.05, 0.04, 0.03, 0.02, 0.01, 0.04, 0.01, 0.008, 0.008, 0.008, 0.008, 0.0006, 0.0005, 0.00003, 0.00001};
final double[] derivatives = new double[frictions.length];
AtomicReference<Double> atomicSpeed = new AtomicReference<>(speed);
final double[] derivatives = new double[FRICTIONS.length];
Timeline timeline = new Timeline();
AtomicReference<ScrollDirection> scrollDirection = new AtomicReference<>();
Holder<ScrollDirection> scrollDirectionHolder = new Holder<>();
final EventHandler<MouseEvent> mouseHandler = event -> timeline.stop();
final EventHandler<ScrollEvent> scrollHandler = event -> {
if (event.getEventType() == ScrollEvent.SCROLL) {
scrollDirection.set(determineScrollDirection(event));
if (isTrackPad(event, scrollDirection.get())) {
atomicSpeed.set(speed / trackPadAdjustment);
} else {
atomicSpeed.set(speed);
}
derivatives[0] += scrollDirection.get().intDirection * atomicSpeed.get();
ScrollDirection scrollDirection = determineScrollDirection(event);
scrollDirectionHolder.value = scrollDirection;
double currentSpeed = isTrackPad(event, scrollDirection) ? speed / trackPadAdjustment : speed;
derivatives[0] += scrollDirection.intDirection * currentSpeed;
if (timeline.getStatus() == Status.STOPPED) {
timeline.play();
}
@@ -180,28 +177,26 @@ final class ScrollUtils {
}
});
timeline.getKeyFrames().add(new KeyFrame(Duration.millis(3), event -> {
timeline.getKeyFrames().add(new KeyFrame(DURATION, event -> {
for (int i = 0; i < derivatives.length; i++) {
derivatives[i] *= frictions[i];
derivatives[i] *= FRICTIONS[i];
}
for (int i = 1; i < derivatives.length; i++) {
derivatives[i] += derivatives[i - 1];
}
double dy = derivatives[derivatives.length - 1];
Function<Bounds, Double> sizeFunction = (scrollDirection.get() == ScrollDirection.UP || scrollDirection.get() == ScrollDirection.DOWN) ? Bounds::getHeight : Bounds::getWidth;
double size = sizeFunction.apply(scrollPane.getContent().getLayoutBounds());
double value;
switch (scrollDirection.get()) {
double size;
switch (scrollDirectionHolder.value) {
case LEFT:
case RIGHT:
value = Math.min(Math.max(scrollPane.hvalueProperty().get() + dy / size, 0), 1);
scrollPane.hvalueProperty().set(value);
size = scrollPane.getContent().getLayoutBounds().getWidth();
scrollPane.setHvalue(Math.min(Math.max(scrollPane.getHvalue() + dy / size, 0), 1));
break;
case UP:
case DOWN:
value = Math.min(Math.max(scrollPane.vvalueProperty().get() + dy / size, 0), 1);
scrollPane.vvalueProperty().set(value);
size = scrollPane.getContent().getLayoutBounds().getHeight();
scrollPane.setVvalue(Math.min(Math.max(scrollPane.getVvalue() + dy / size, 0), 1));
break;
}
@@ -64,11 +64,13 @@ public final class UpgradeDialog extends JFXDialogLayout {
node = node.nextSibling();
} while (node != null);
renderer.mergeLineBreaks();
return renderer.render();
}).whenComplete(Schedulers.javafx(), (result, exception) -> {
if (exception == null) {
ScrollPane scrollPane = new ScrollPane(result);
scrollPane.setFitToWidth(true);
FXUtils.smoothScrolling(scrollPane);
setBody(scrollPane);
} else {
LOG.warning("Failed to load update log, trying to open it in browser");
@@ -53,6 +53,7 @@ public final class WebPage extends SpinnerPane implements DecoratorPage {
}, null);
});
renderer.appendNode(document);
renderer.mergeLineBreaks();
return renderer.render();
}).whenComplete(Schedulers.javafx(), ((result, exception) -> {
if (exception == null) {
@@ -20,6 +20,7 @@ package org.jackhuang.hmcl.ui.account;
import com.jfoenix.controls.JFXButton;
import com.jfoenix.controls.JFXRadioButton;
import com.jfoenix.effects.JFXDepthManager;
import javafx.beans.binding.Bindings;
import javafx.geometry.Pos;
import javafx.scene.Cursor;
import javafx.scene.canvas.Canvas;
@@ -159,8 +160,7 @@ public final class AccountListItemSkin extends SkinBase<AccountListItem> {
btnUpload.getStyleClass().add("toggle-icon4");
btnUpload.setGraphic(SVG.CHECKROOM.createIcon(Theme.blackFill(), -1));
FXUtils.installFastTooltip(btnUpload, i18n("account.skin.upload"));
spinnerUpload.managedProperty().bind(spinnerUpload.visibleProperty());
spinnerUpload.visibleProperty().bind(skinnable.canUploadSkin());
btnUpload.disableProperty().bind(Bindings.not(skinnable.canUploadSkin()));
spinnerUpload.setContent(btnUpload);
spinnerUpload.getStyleClass().add("small-spinner-pane");
right.getChildren().add(spinnerUpload);
@@ -35,6 +35,8 @@ import javafx.scene.control.Hyperlink;
import javafx.scene.control.Label;
import javafx.scene.control.TextInputControl;
import javafx.scene.layout.*;
import org.jackhuang.hmcl.Metadata;
import org.jackhuang.hmcl.auth.AccountFactory;
import org.jackhuang.hmcl.auth.CharacterSelector;
import org.jackhuang.hmcl.auth.NoSelectedCharacterException;
@@ -337,7 +339,7 @@ public class CreateAccountPane extends JFXDialogLayout implements DialogAware {
hintPane.setSegment(i18n("account.methods.microsoft.snapshot"));
JFXHyperlink officialWebsite = new JFXHyperlink(i18n("account.methods.microsoft.snapshot.website"));
officialWebsite.setExternalLink("https://hmcl.huangyuhui.net");
officialWebsite.setExternalLink(Metadata.PUBLISH_URL);
vbox.getChildren().setAll(hintPane, officialWebsite);
btnAccept.setDisable(true);
@@ -154,7 +154,7 @@ public class OfflineAccountSkinPane extends StackPane {
result.getCape() != null ? result.getCape().getImage() : null);
}
}).start();
}, skinItem.selectedDataProperty(), cslApiField.textProperty(), skinSelector.valueProperty(), capeSelector.valueProperty());
}, skinItem.selectedDataProperty(), cslApiField.textProperty(), modelCombobox.valueProperty(), skinSelector.valueProperty(), capeSelector.valueProperty());
FXUtils.onChangeAndOperate(skinItem.selectedDataProperty(), selectedData -> {
GridPane gridPane = new GridPane();
@@ -397,21 +397,22 @@ public class DecoratorController {
node.getProperties().put(PROPERTY_DIALOG_CLOSE_HANDLER, handler);
node.addEventHandler(DialogCloseEvent.CLOSE, handler);
if (node instanceof DialogAware) {
DialogAware dialogAware = (DialogAware) node;
if (dialog.isVisible()) {
dialogAware.onDialogShown();
} else {
dialog.visibleProperty().addListener(new ChangeListener<Boolean>() {
@Override
public void changed(ObservableValue<? extends Boolean> observable, Boolean oldValue, Boolean newValue) {
if (newValue) {
dialogAware.onDialogShown();
observable.removeListener(this);
}
if (dialog.isVisible()) {
dialog.requestFocus();
if (node instanceof DialogAware)
((DialogAware) node).onDialogShown();
} else {
dialog.visibleProperty().addListener(new ChangeListener<Boolean>() {
@Override
public void changed(ObservableValue<? extends Boolean> observable, Boolean oldValue, Boolean newValue) {
if (newValue) {
dialog.requestFocus();
if (node instanceof DialogAware)
((DialogAware) node).onDialogShown();
observable.removeListener(this);
}
});
}
}
});
}
}
@@ -34,6 +34,8 @@ import javafx.scene.layout.*;
import javafx.scene.paint.Color;
import javafx.scene.shape.Rectangle;
import javafx.stage.Stage;
import org.jackhuang.hmcl.Metadata;
import org.jackhuang.hmcl.setting.Theme;
import org.jackhuang.hmcl.ui.FXUtils;
import org.jackhuang.hmcl.ui.SVG;
@@ -190,7 +192,7 @@ public class DecoratorSkin extends SkinBase<Decorator> {
btnHelp.setFocusTraversable(false);
btnHelp.setGraphic(SVG.HELP.createIcon(Theme.foregroundFillBinding(), -1));
btnHelp.getStyleClass().add("jfx-decorator-button");
btnHelp.setOnAction(e -> FXUtils.openLink("https://docs.hmcl.net/help.html"));
btnHelp.setOnAction(e -> FXUtils.openLink(Metadata.CONTACT_URL));
JFXButton btnMin = new JFXButton();
btnMin.setFocusTraversable(false);
@@ -292,6 +292,7 @@ public final class VersionsPage extends BorderPane implements WizardPage, Refres
switch (it.getVersionType()) {
case RELEASE:
return chkRelease.isSelected();
case PENDING:
case SNAPSHOT:
return chkSnapshot.isSelected();
case OLD:
@@ -411,11 +412,10 @@ public final class VersionsPage extends BorderPane implements WizardPage, Refres
content.setImage(VersionIconType.GRASS.getIcon());
content.setExternalLink(i18n("wiki.version.game.release", remoteVersion.getGameVersion()));
break;
case PENDING:
case SNAPSHOT:
content.getTags().setAll(i18n("version.game.snapshot"));
content.setImage(VersionIconType.COMMAND.getIcon());
content.setExternalLink(i18n("wiki.version.game.snapshot", remoteVersion.getGameVersion()));
break;
default:
@@ -27,6 +27,8 @@ import javafx.scene.Node;
import javafx.scene.control.*;
import javafx.scene.layout.*;
import javafx.stage.FileChooser;
import org.jackhuang.hmcl.Metadata;
import org.jackhuang.hmcl.auth.Account;
import org.jackhuang.hmcl.auth.authlibinjector.AuthlibInjectorServer;
import org.jackhuang.hmcl.game.HMCLGameRepository;
@@ -173,7 +175,7 @@ public final class ModpackInfoPage extends Control implements WizardPage {
if (skinnable.controller.getSettings().get(MODPACK_TYPE) == MODPACK_TYPE_SERVER) {
Hyperlink hyperlink = new Hyperlink(i18n("modpack.wizard.step.initialization.server"));
hyperlink.setOnAction(e -> FXUtils.openLink("https://docs.hmcl.net/modpack/serverpack.html"));
hyperlink.setOnAction(e -> FXUtils.openLink(Metadata.DOCS_URL + "/modpack/serverpack.html"));
borderPane.setTop(hyperlink);
} else {
HintPane pane = new HintPane(MessageDialogPane.MessageType.INFO);
@@ -50,7 +50,7 @@ public final class AboutPage extends StackPane {
launcher.setImage(FXUtils.newBuiltinImage("/assets/img/icon.png"));
launcher.setTitle("Hello Minecraft! Launcher");
launcher.setSubtitle(Metadata.VERSION);
launcher.setExternalLink("https://hmcl.huangyuhui.net");
launcher.setExternalLink(Metadata.PUBLISH_URL);
IconedTwoLineListItem author = new IconedTwoLineListItem();
author.setImage(FXUtils.newBuiltinImage("/assets/img/yellow_fish.png"));
@@ -70,7 +70,7 @@ public final class AboutPage extends StackPane {
IconedTwoLineListItem copyright = new IconedTwoLineListItem();
copyright.setTitle(i18n("about.copyright"));
copyright.setSubtitle(i18n("about.copyright.statement"));
copyright.setExternalLink("https://hmcl.huangyuhui.net/about/");
copyright.setExternalLink(Metadata.ABOUT_URL);
IconedTwoLineListItem claim = new IconedTwoLineListItem();
claim.setTitle(i18n("about.claim"));
@@ -27,6 +27,8 @@ import org.jackhuang.hmcl.ui.construct.SpinnerPane;
import static org.jackhuang.hmcl.util.i18n.I18n.i18n;
import org.jackhuang.hmcl.Metadata;
public class FeedbackPage extends SpinnerPane {
public FeedbackPage() {
@@ -45,7 +47,7 @@ public class FeedbackPage extends SpinnerPane {
users.setImage(FXUtils.newBuiltinImage("/assets/img/icon.png"));
users.setTitle(i18n("feedback.qq_group"));
users.setSubtitle(i18n("feedback.qq_group.statement"));
users.setExternalLink("https://docs.hmcl.net/groups.html");
users.setExternalLink(Metadata.GROUPS_URL);
IconedTwoLineListItem github = new IconedTwoLineListItem();
github.setImage(FXUtils.newBuiltinImage("/assets/img/github.png"));
@@ -53,7 +53,7 @@ public class HelpPage extends SpinnerPane {
IconedTwoLineListItem docPane = new IconedTwoLineListItem();
docPane.setTitle(i18n("help.doc"));
docPane.setSubtitle(i18n("help.detail"));
docPane.setExternalLink(Metadata.HELP_URL);
docPane.setExternalLink(Metadata.DOCS_URL);
ComponentList doc = new ComponentList();
doc.getContent().setAll(docPane);
content.getChildren().add(doc);
@@ -63,7 +63,7 @@ public class HelpPage extends SpinnerPane {
private void loadHelp() {
showSpinner();
Task.supplyAsync(() -> HttpRequest.GET("https://docs.hmcl.net/index.json").getJson(listTypeOf(HelpCategory.class)))
Task.supplyAsync(() -> HttpRequest.GET(Metadata.DOCS_URL + "/index.json").getJson(listTypeOf(HelpCategory.class)))
.thenAcceptAsync(Schedulers.javafx(), helpCategories -> {
for (HelpCategory category : helpCategories) {
ComponentList categoryPane = new ComponentList();
@@ -18,6 +18,8 @@
package org.jackhuang.hmcl.ui.main;
import javafx.beans.property.ReadOnlyObjectProperty;
import org.jackhuang.hmcl.Metadata;
import org.jackhuang.hmcl.event.EventBus;
import org.jackhuang.hmcl.event.RefreshedVersionsEvent;
import org.jackhuang.hmcl.game.HMCLGameRepository;
@@ -179,7 +181,7 @@ public class RootPage extends DecoratorAnimatedPage implements DecoratorPage {
chatItem.setLeftGraphic(wrap(SVG.CHAT));
chatItem.setActionButtonVisible(false);
chatItem.setTitle(i18n("chat"));
chatItem.setOnAction(e -> FXUtils.openLink("https://docs.hmcl.net/groups.html"));
chatItem.setOnAction(e -> FXUtils.openLink(Metadata.GROUPS_URL));
// the left sidebar
AdvancedListBox sideBar = new AdvancedListBox()
@@ -17,6 +17,7 @@ import org.jackhuang.hmcl.setting.VersionSetting;
import org.jackhuang.hmcl.ui.FXUtils;
import org.jackhuang.hmcl.ui.construct.*;
import org.jackhuang.hmcl.ui.decorator.DecoratorPage;
import org.jackhuang.hmcl.util.platform.OperatingSystem;
import java.nio.file.Paths;
import java.util.Arrays;
@@ -59,6 +60,8 @@ public final class AdvancedVersionSettingPage extends StackPane implements Decor
versionId == null ? i18n("settings.advanced") : i18n("settings.advanced.title", versionId)
));
this.getStyleClass().add("gray-background");
ScrollPane scrollPane = new ScrollPane();
scrollPane.setFitToHeight(true);
scrollPane.setFitToWidth(true);
@@ -195,9 +198,18 @@ public final class AdvancedVersionSettingPage extends StackPane implements Decor
useNativeOpenALPane.setTitle(i18n("settings.advanced.use_native_openal"));
workaroundPane.getContent().setAll(
nativesDirSublist, rendererPane,
noJVMArgsPane, noGameCheckPane, noJVMCheckPane, noNativesPatchPane,
useNativeGLFWPane, useNativeOpenALPane);
nativesDirSublist, rendererPane, noJVMArgsPane, noGameCheckPane,
noJVMCheckPane, noNativesPatchPane
);
if (OperatingSystem.CURRENT_OS.isLinuxOrBSD()) {
workaroundPane.getContent().addAll(useNativeGLFWPane, useNativeOpenALPane);
} else {
ComponentSublist unsupportedOptionsSublist = new ComponentSublist();
unsupportedOptionsSublist.setTitle(i18n("settings.advanced.unsupported_system_options"));
unsupportedOptionsSublist.getContent().addAll(useNativeGLFWPane, useNativeOpenALPane);
workaroundPane.getContent().add(unsupportedOptionsSublist);
}
}
rootPane.getChildren().addAll(
@@ -439,9 +439,25 @@ public class DownloadPage extends Control implements DecoratorPage {
private static final class ModVersion extends JFXDialogLayout {
public ModVersion(RemoteMod.Version version, DownloadPage selfPage) {
boolean isModpack = selfPage.repository.getType() == RemoteModRepository.Type.MODPACK;
RemoteModRepository.Type type = selfPage.repository.getType();
this.setHeading(new HBox(new Label(i18n(isModpack ? "modpack.download.title" : "mods.download.title", version.getName()))));
String title;
switch (type) {
case WORLD:
title = "world.download.title";
break;
case MODPACK:
title = "modpack.download.title";
break;
case RESOURCE_PACK:
title = "resourcepack.download.title";
break;
case MOD:
default:
title = "mods.download.title";
break;
}
this.setHeading(new HBox(new Label(i18n(title, version.getName()))));
VBox box = new VBox(8);
box.setPadding(new Insets(8));
@@ -463,10 +479,10 @@ public class DownloadPage extends Control implements DecoratorPage {
this.setBody(box);
JFXButton downloadButton = new JFXButton(isModpack ? i18n("install.modpack") : i18n("mods.install"));
JFXButton downloadButton = new JFXButton(type == RemoteModRepository.Type.MODPACK ? i18n("install.modpack") : i18n("mods.install"));
downloadButton.getStyleClass().add("dialog-accept");
downloadButton.setOnAction(e -> {
if (isModpack || !spinnerPane.isLoading() && spinnerPane.getFailedReason() == null) {
if (type == RemoteModRepository.Type.MODPACK || !spinnerPane.isLoading() && spinnerPane.getFailedReason() == null) {
fireEvent(new DialogCloseEvent());
}
selfPage.download(version);
@@ -153,7 +153,7 @@ public final class WorldBackupsPage extends ListPageBase<WorldBackupsPage.Backup
count = Integer.parseInt(matcher.group("count"));
}
return Pair.pair(path, new BackupInfo(world.getFile(), new World(path), time, count));
return Pair.pair(path, new BackupInfo(path, new World(path), time, count));
}).whenComplete(Schedulers.javafx(), (result, exception) -> {
if (exception == null) {
WorldBackupsPage.this.getItems().add(result.getValue());
@@ -83,6 +83,8 @@ public final class WorldInfoPage extends SpinnerPane implements DecoratorPage {
this.world = world;
this.stateProperty = new SimpleObjectProperty<>(State.fromTitle(i18n("world.info.title", world.getWorldName())));
this.getStyleClass().add("gray-background");
this.setLoading(true);
Task.supplyAsync(this::loadWorldInfo)
.whenComplete(Schedulers.javafx(), ((result, exception) -> {
@@ -30,6 +30,7 @@ import org.jackhuang.hmcl.util.TaskCancellationAction;
import java.util.Map;
import java.util.Queue;
import java.util.concurrent.CancellationException;
import static org.jackhuang.hmcl.ui.FXUtils.runInFX;
import static org.jackhuang.hmcl.util.i18n.I18n.i18n;
@@ -69,6 +70,12 @@ public abstract class TaskExecutorDialogWizardDisplayer extends AbstractWizardDi
} else {
if (executor.getException() == null)
return;
if (executor.getException() instanceof CancellationException) {
onEnd();
return;
}
String appendix = StringUtils.getStackTrace(executor.getException());
if (settings.get("failure_callback") instanceof WizardProvider.FailureCallback)
((WizardProvider.FailureCallback) settings.get("failure_callback")).onFail(settings, executor.getException(), () -> onEnd());
@@ -113,9 +113,8 @@ public final class UpdateHandler {
Task<?> task = new HMCLDownloadTask(version, downloaded);
TaskExecutor executor = task.executor(false);
TaskExecutor executor = task.executor();
Controllers.taskDialog(executor, i18n("message.downloading"), TaskCancellationAction.NORMAL);
executor.start();
thread(() -> {
boolean success = executor.test();
@@ -121,7 +121,7 @@ public final class CrashReporter implements Thread.UncaughtExceptionHandler {
map.put("version", Metadata.VERSION);
map.put("log", LOG.getLogs());
try {
String response = NetworkUtils.doPost(NetworkUtils.toURL("https://hmcl.huangyuhui.net/hmcl/crash.php"), map);
String response = NetworkUtils.doPost(NetworkUtils.toURL(Metadata.PUBLISH_URL + "/hmcl/crash.php"), map);
if (StringUtils.isNotBlank(response))
LOG.error("Crash server response: " + response);
} catch (IOException ex) {
@@ -0,0 +1,31 @@
/*
* Hello Minecraft! Launcher
* Copyright (C) 2025 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.util;
/**
* @author Glavo
*/
public final class ModuleHelper {
public static void addEnableNativeAccess(Class<?> clazzInModule) {
// do nothing
}
private ModuleHelper() {
}
}
@@ -0,0 +1,46 @@
/*
* Hello Minecraft! Launcher
* Copyright (C) 2025 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.util;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
/**
* @author Glavo
*/
public final class ModuleHelper {
public static void addEnableNativeAccess(Class<?> clazzInModule) {
Module module = clazzInModule.getModule();
if (module.isNamed()) {
try {
MethodHandles.Lookup lookup = MethodHandles.privateLookupIn(Module.class, MethodHandles.lookup());
MethodHandle implAddEnableNativeAccess = lookup.findVirtual(Module.class, "implAddEnableNativeAccess", MethodType.methodType(Module.class));
Module ignored = (Module) implAddEnableNativeAccess.invokeExact(module);
} catch (Throwable e) {
e.printStackTrace(System.err);
}
} else {
System.err.println("TODO: Add enable native access for anonymous modules is not yet supported");
}
}
private ModuleHelper() {
}
}
Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.8 KiB

After

Width:  |  Height:  |  Size: 39 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.8 KiB

After

Width:  |  Height:  |  Size: 46 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 122 KiB

@@ -1058,6 +1058,7 @@ world.datapack=Manage Datapacks
world.datapack.1_13=Only Minecraft 1.13 or later supports datapacks.
world.datetime=Last played on %s
world.download=Download World
world.download.title=Download World - %1s
world.export=Export the World
world.export.title=Choose the directory for this exported world
world.export.location=Save As
@@ -1129,6 +1130,7 @@ repositories.chooser=HMCL requires JavaFX to work.\n\
repositories.chooser.title=Choose download source for JavaFX
resourcepack=Resource Packs
resourcepack.download.title=Download Resource Pack - %1s
search=Search
search.hint.chinese=Search in English and Chinese
@@ -1209,6 +1211,7 @@ settings.advanced.renderer.llvmpipe=Software (Poor performance, best compatibili
settings.advanced.renderer.zink=Vulkan (Best performance, poor compatibility)
settings.advanced.server_ip=Server Address
settings.advanced.server_ip.prompt=Automatically join after launching the game
settings.advanced.unsupported_system_options=Settings not applicable to the current system
settings.advanced.use_native_glfw=[Linux/FreeBSD Only] Use System GLFW
settings.advanced.use_native_openal=[Linux/FreeBSD Only] Use System OpenAL
settings.advanced.workaround=Workaround
@@ -1313,12 +1316,16 @@ update.changelog=Changelog
update.channel.dev=Beta
update.channel.dev.hint=You are currently using a Beta channel build of the launcher. While it may include some extra features, it is also sometimes less stable than the Release channel builds.\n\
\n\
If you encounter any bugs or problems, please submit feedback via the channels provided on the <a href="hmcl://settings/feedback">Feedback</a> page.
If you encounter any bugs or problems, please submit feedback via the channels provided on the <a href="hmcl://settings/feedback">Feedback</a> page.\n\
\n\
Follow <a href="https://space.bilibili.com/1445341">@huanghongxun</a> on Bilibili to stay up to date on important HMCL news, or <a href="https://space.bilibili.com/20314891">@Glavo</a> to learn about HMCL development progress.
update.channel.dev.title=Beta Channel Notice
update.channel.nightly=Nightly
update.channel.nightly.hint=You are currently using a Nightly channel build of the launcher. While it may include some extra features, it is also always less stable than the other channel builds.\n\
\n\
If you encounter any bugs or problems, please submit feedback via the channels provided on the <a href="hmcl://settings/feedback">Feedback</a> page.
If you encounter any bugs or problems, please submit feedback via the channels provided on the <a href="hmcl://settings/feedback">Feedback</a> page.\n\
\n\
Follow <a href="https://space.bilibili.com/1445341">@huanghongxun</a> on Bilibili to stay up to date on important HMCL news, or <a href="https://space.bilibili.com/20314891">@Glavo</a> to learn about HMCL development progress.
update.channel.nightly.title=Nightly Channel Notice
update.channel.stable=Release
update.checking=Checking for Updates
@@ -73,7 +73,7 @@ account.failed.server_disconnected=No se ha podido conectar con el servidor de a
account.failed.server_response_malformed=Respuesta del servidor no válida, el servidor de autenticación puede no estar funcionando.
account.failed.ssl=Se ha producido un error SSL al conectar con el servidor. Por favor, intente actualizar su Java.
account.failed.wrong_account=Ha iniciado sesión en la cuenta equivocada.
account.hmcl.hint=Debe hacer clic en "Iniciar sesión" y completar el proceso en la ventana abierta del navegador.
account.hmcl.hint=Debe hacer clic en «Iniciar sesión» y completar el proceso en la ventana abierta del navegador.
account.injector.add=Nuevo servidor Auth
account.injector.empty=Ninguno (Puedes hacer clic en el botón más de la derecha para añadir uno)
account.injector.http=Atención: Este servidor utiliza el protocolo HTTP inseguro. Cualquiera entre su conexión podrá ver sus credenciales en texto claro.
@@ -102,21 +102,21 @@ account.methods.microsoft.close_page=La autorización de la cuenta de Microsoft
account.methods.microsoft.makegameidsettings=Crear perfil / Editar nombre del perfil
account.methods.microsoft.deauthorize=Desautorizar
account.methods.microsoft.error.add_family=Un adulto debe añadirte a una familia para que puedas jugar a Minecraft porque aún no tienes 18 años.
account.methods.microsoft.error.add_family_probably=Por favor, compruebe si la edad indicada en la configuración de su cuenta es de al menos 18 años. Si no es así y cree que se trata de un error, puede hacer clic en "Cómo cambiar la fecha de nacimiento de su cuenta" para saber cómo cambiarla.
account.methods.microsoft.error.add_family_probably=Por favor, compruebe si la edad indicada en la configuración de su cuenta es de al menos 18 años. Si no es así y cree que se trata de un error, puede hacer clic en «Cómo cambiar la fecha de nacimiento de su cuenta» para saber cómo cambiarla.
account.methods.microsoft.error.country_unavailable=Xbox Live no está disponible en tu país/región actual.
account.methods.microsoft.error.missing_xbox_account=Tu cuenta Microsoft aún no tiene una cuenta Xbox vinculada. Haga clic en "Crear perfil / Editar nombre de perfil" para crear una antes de continuar.
account.methods.microsoft.error.no_character=Por favor, asegúrese de que ha comprado Minecraft: Java Edition. \nSi ya lo has comprado, es posible que no hayas creado un perfil de juego.\nPor favor, haga clic en "Crear perfil / Editar nombre de perfil" para crearlo.
account.methods.microsoft.error.missing_xbox_account=Tu cuenta Microsoft aún no tiene una cuenta Xbox vinculada. Haga clic en «Crear perfil / Editar nombre de perfil» para crear una antes de continuar.
account.methods.microsoft.error.no_character=Por favor, asegúrese de que ha comprado Minecraft: Java Edition. \nSi ya lo has comprado, es posible que no hayas creado un perfil de juego.\nPor favor, haga clic en «Crear perfil / Editar nombre de perfil» para crearlo.
account.methods.microsoft.error.unknown=No se ha podido iniciar sesión, error: %d.
account.methods.microsoft.error.wrong_verify_method=Inicie sesión con su contraseña en la página de inicio de sesión de la cuenta Microsoft y no utilice un código de verificación para iniciar sesión.
account.methods.microsoft.logging_in=Iniciando sesión...
account.methods.microsoft.hint=Por favor, haga clic en "Iniciar sesión" y copie el código que aparece aquí para completar el proceso de inicio de sesión en la ventana del navegador que se abre.\n\
account.methods.microsoft.hint=Por favor, haga clic en «Iniciar sesión» y copie el código que aparece aquí para completar el proceso de inicio de sesión en la ventana del navegador que se abre.\n\
\n\
Si el token utilizado para iniciar sesión en la cuenta de Microsoft se ha filtrado, puedes hacer clic en "Desautorizar" para desautorizarlo.
Si el token utilizado para iniciar sesión en la cuenta de Microsoft se ha filtrado, puedes hacer clic en «Desautorizar» para desautorizarlo.
account.methods.microsoft.manual=El código de su dispositivo es <b>%1$s</b>. Por favor, haga clic aquí para copiarlo.\n\
\n\
Después de hacer clic en "Iniciar sesión", debe completar el proceso de inicio de sesión en la ventana abierta del navegador. Si no se muestra, puede navegar a %2$s manualmente.\n\
Después de hacer clic en «Iniciar sesión», debe completar el proceso de inicio de sesión en la ventana abierta del navegador. Si no se muestra, puede navegar a %2$s manualmente.\n\
\n\
Si el token utilizado para iniciar sesión en la cuenta de Microsoft se ha filtrado, puedes hacer clic en "Desautorizar" para desautorizarlo.
Si el token utilizado para iniciar sesión en la cuenta de Microsoft se ha filtrado, puedes hacer clic en «Desautorizar» para desautorizarlo.
account.methods.microsoft.profile=Perfil de la cuenta
account.methods.microsoft.purchase=Comprar Minecraft
account.methods.microsoft.snapshot=Está utilizando una versión no oficial de HMCL. Por favor, descargue la versión oficial para iniciar sesión.
@@ -168,7 +168,7 @@ archive.version=Versión
assets.download=Descargando assets
assets.download_all=Verificando la integridad de los archivos
assets.index.malformed=Los archivos de índice de los activos descargados estaban dañados. Puede intentar utilizar "Actualizar activos del juego" en la configuración de su instancia del juego para solucionar este problema.
assets.index.malformed=Los archivos de índice de los activos descargados estaban dañados. Puede intentar utilizar «Actualizar activos del juego» en la configuración de su instancia del juego para solucionar este problema.
button.cancel=Cancelar
button.change_source=Cambiar fuente de descarga
@@ -329,7 +329,7 @@ download.hint=Instalar juegos y modpacks o descargar mods, paquetes de recursos
download.code.404=Archivo no encontrado en el servidor remoto: %s
download.content=Complementos
download.curseforge.unavailable=La versión Nightly de HMCL no admite el acceso a CurseForge. Utilice las versiones Release o Beta para descargarlas.
download.existing=El archivo no se puede guardar porque ya existe. Puedes hacer clic en "Guardar como" para guardar el archivo en otro lugar.
download.existing=El archivo no se puede guardar porque ya existe. Puedes hacer clic en «Guardar como» para guardar el archivo en otro lugar.
download.external_link=Abrir sitio web
download.failed=Falló la descarga de %1$s, código de respuesta: %2$d.
download.failed.empty=No hay versiones disponibles, por favor haga clic aquí para volver.
@@ -346,14 +346,14 @@ download.java.override=Esta versión de Java ya existe. ¿Desea desinstalarla y
download.javafx=Descargando dependencias para el launcher...
download.javafx.notes=Estamos descargando dependencias para HMCL desde Internet.\n\
\n\
Puede hacer clic en "Cambiar fuente de descarga" para seleccionar el\nespejo de descarga o hacer clic en "Cancelar" para detener y salir.\n\
Puede hacer clic en «Cambiar fuente de descarga» para seleccionar el\nespejo de descarga o hacer clic en «Cancelar» para detener y salir.\n\
Nota: Si su velocidad de descarga es demasiado lenta, puede intentar cambiar a otro espejo.
download.javafx.component=Descargando módulo %s
download.javafx.prepare=Preparando la descarga
exception.access_denied=HMCL no puede acceder al archivo %s. Puede estar bloqueado por otro proceso.\n\
\n\
Para los usuarios de Windows, puede abrir el "Monitor de Recursos" para comprobar si otro proceso lo está utilizando actualmente. Si es así, puede intentarlo de nuevo después de cerrar ese proceso.\n\
Para los usuarios de Windows, puede abrir el «Monitor de Recursos» para comprobar si otro proceso lo está utilizando actualmente. Si es así, puede intentarlo de nuevo después de cerrar ese proceso.\n\
Si no es así, comprueba si tu cuenta tiene permisos suficientes para acceder a ella.
exception.artifact_malformed=No se puede verificar la integridad de los archivos descargados.
exception.ssl_handshake=No se pudo establecer una conexión SSL porque falta el certificado SSL en la instalación actual de Java. Puede intentar abrir HMCL con otro Java y volver a intentarlo.
@@ -377,9 +377,9 @@ fatal.config_in_temp_dir=Estás abriendo Hello Minecraft! Launcher en un directo
Se recomienda trasladar el HMCL a otro lugar y reabrirlo.\n\
¿Todavía quieres continuar?
fatal.config_loading_failure=No se pueden cargar los archivos de configuración.\n\
Por favor, asegúrese de que Hello Minecraft! Launcher tiene acceso de lectura y escritura a "%s" y a los archivos que contiene.\n\
Para macOS, intente colocar HMCL en un lugar con permiso que no sea "Escritorio", "Descargas" y "Documentos" y vuelva a intentarlo.
fatal.config_loading_failure.unix=No se pudo cargar el archivo de configuración porque fue creado por el usuario "%1$s".\n\
Por favor, asegúrese de que Hello Minecraft! Launcher tiene acceso de lectura y escritura a «%s» y a los archivos que contiene.\n\
Para macOS, intente colocar HMCL en un lugar con permiso que no sea «Escritorio», «Descargas» y «Documentos» y vuelva a intentarlo.
fatal.config_loading_failure.unix=No se pudo cargar el archivo de configuración porque fue creado por el usuario «%1$s».\n\
Por favor, abra HMCL como usuario root (no recomendado), o ejecute el siguiente comando en el terminal para cambiar la propiedad del archivo de configuración al usuario actual:\n%2$s
fatal.mac_app_translocation=El sistema operativo aísla Hello Minecraft! Launcher en un directorio temporal debido a los mecanismos de seguridad de macOS.\n\
Por favor, mueve HMCL a un directorio diferente antes de intentar abrirlo. De lo contrario, tus ajustes y datos de juego podrían perderse tras reiniciar.\n\
@@ -393,11 +393,11 @@ fatal.apply_update_need_win7=Hello Minecraft! Launcher no puede actualizarse aut
\n\
Puedes actualizar manualmente descargando una versión más reciente del launcher desde %s.
fatal.samba=Si ha abierto Hello Minecraft! Launcher desde una unidad de red Samba, es posible que algunas funciones no funcionen. Intenta actualizar tu Java o mover el launcher a otro directorio.
fatal.illegal_char=Su ruta de usuario contiene un carácter ilegal "=", por lo que algunas características podrían no funcionar correctamente.\n\
fatal.illegal_char=Su ruta de usuario contiene un carácter ilegal «=», por lo que algunas características podrían no funcionar correctamente.\n\
Por ejemplo, no podrá utilizar authlib-injector o cambiar el skin de su cuenta offline.
fatal.unsupported_platform=Minecraft aún no es totalmente compatible con tu plataforma, por lo que es posible que te falten funciones o incluso que no puedas iniciar el juego.\n\
\n\
Si no puedes iniciar Minecraft 1.17 y versiones posteriores, puedes probar a cambiar el "Renderizador" a "Software" en "Config. Global/Específica de instancia → Configuración avanzada" para utilizar el renderizado de la CPU y mejorar la compatibilidad.
Si no puedes iniciar Minecraft 1.17 y versiones posteriores, puedes probar a cambiar el «Renderizador» a «Software» en «Config. Global/Específica de instancia → Configuración avanzada» para utilizar el renderizado de la CPU y mejorar la compatibilidad.
fatal.unsupported_platform.loongarch=Hello Minecraft! Launcher ha prestado apoyo a la plataforma Loongson.\n\
Si tienes problemas al jugar, puedes visitar https://docs.hmcl.net/groups.html para obtener ayuda.
fatal.unsupported_platform.osx_arm64=Hello Minecraft! Launcher ha proporcionado soporte para la plataforma de chips de Apple, utilizando Java nativo de ARM para ejecutar juegos y conseguir una experiencia de juego más fluida.\n\
@@ -439,15 +439,15 @@ game.crash.reason.block=El juego se ha colgado debido a un bloqueo en el mundo.\
\n\
Tipo de bloque: %1$s\n\
Ubicación: %2$s
game.crash.reason.bootstrap_failed=El juego se ha colgado debido al mod "%1$s".\n\
game.crash.reason.bootstrap_failed=El juego se ha colgado debido al mod «%1$s».\n\
\n\
Puedes intentar borrarlo o actualizarlo.
game.crash.reason.config=El juego se ha colgado debido a que el mod "%1$s" no ha podido analizar su archivo de configuración "%2$s".
game.crash.reason.config=El juego se ha colgado debido a que el mod «%1$s» no ha podido analizar su archivo de configuración «%2$s».
game.crash.reason.debug_crash=El juego se ha colgado porque lo has activado manualmente. Así que probablemente sepas por qué :)
game.crash.reason.mixin_apply_mod_failed=El juego se ha colgado porque no se ha podido aplicar el mixin al mod "%1$s".\n\
game.crash.reason.mixin_apply_mod_failed=El juego se ha colgado porque no se ha podido aplicar el mixin al mod «%1$s».\n\
\n\
Puedes probar a borrar o actualizar el mod para resolver el problema.
game.crash.reason.duplicated_mod=El juego no puede iniciarse debido a la existencia de mods duplicados: "%1$s".\n\
game.crash.reason.duplicated_mod=El juego no puede iniciarse debido a la existencia de mods duplicados: «%1$s».\n\
\n\
%2$s\n\
\n\
@@ -462,7 +462,7 @@ game.crash.reason.fabric_version_0_12=La versión de Fabric Loader 0.12 o poster
game.crash.reason.fabric_warnings=El cargador de Fabric Loader ha advertido:\n\
\n\
%1$s
game.crash.reason.file_already_exists=El juego se ha colgado porque el archivo "%1$s" ya existe.\n\
game.crash.reason.file_already_exists=El juego se ha colgado porque el archivo «%1$s» ya existe.\n\
\n\
Puedes intentar hacer una copia de seguridad y eliminar ese archivo, y luego volver a ejecutar el juego.
game.crash.reason.file_changed=El juego se ha colgado porque ha fallado la verificación de archivos.\n\
@@ -489,27 +489,27 @@ game.crash.reason.install_mixinbootstrap=El juego se ha colgado debido a la falt
Puedes intentar instalar <a href="https://www.curseforge.com/minecraft/mc-mods/mixinbootstrap">MixinBootstrap</a> para resolver el problema. Si se cuelga después de la instalación, prueba a añadir un signo de exclamación (!) delante del nombre de archivo de este mod para intentar resolver el problema.
game.crash.reason.jdk_9=El juego se ha colgado porque la versión de Java es demasiado nueva para esta instancia.\n\
\n\
Tienes que descargar e instalar Java 8 y elegirlo en "Config. Global/Específica de instancia → Java".
Tienes que descargar e instalar Java 8 y elegirlo en «Config. Global/Específica de instancia → Java».
game.crash.reason.need_jdk11=El juego se ha colgado debido a una versión inadecuada de Java.\n\
\n\
Debes descargar e instalar Java 11 y configurarlo en "Config. Global/Específica de instancia → Java".
Debes descargar e instalar Java 11 y configurarlo en «Config. Global/Específica de instancia → Java».
game.crash.reason.jvm_32bit=El juego se ha colgado porque la asignación de memoria actual excedía el límite de 32 bits JVM.\n\
\n\
Si su sistema operativo es de 64 bits, instale y utilice una versión de Java de 64 bits. De lo contrario, es posible que tenga que volver a instalar un sistema operativo de 64 bits o adquirir un ordenador más moderno.\n\
\n\
O bien, puede desactivar la opción "Asignar automáticamente" en "Config. Global/Específica de instancia → Memoria" y establecer el tamaño máximo de asignación de memoria en 1024 MB o menos.
game.crash.reason.loading_crashed_forge=El juego se ha colgado debido al mod "%1$s" (%2$s).\n\
O bien, puede desactivar la opción «Asignar automáticamente» en «Config. Global/Específica de instancia → Memoria» y establecer el tamaño máximo de asignación de memoria en 1024 MB o menos.
game.crash.reason.loading_crashed_forge=El juego se ha colgado debido al mod «%1$s» (%2$s).\n\
\n\
Puedes intentar borrarlo o actualizarlo.
game.crash.reason.loading_crashed_fabric=El juego se ha colgado debido al mod "%1$s".\n\
game.crash.reason.loading_crashed_fabric=El juego se ha colgado debido al mod «%1$s».\n\
\n\
Puedes intentar borrarlo o actualizarlo.
game.crash.reason.memory_exceeded=El juego se ha colgado debido a que se ha asignado demasiada memoria a un pequeño archivo de paginación.\n\
\n\
Puedes probar a desactivar la opción "Asignar automáticamente" en "Config. Global/Específica de instancia → Memoria" y ajustar el valor hasta que se inicie el juego.\n\
Puedes probar a desactivar la opción «Asignar automáticamente» en «Config. Global/Específica de instancia → Memoria» y ajustar el valor hasta que se inicie el juego.\n\
\n\
También puede tratar de aumentar el tamaño del archivo de paginación en la configuración del sistema.
game.crash.reason.mod=El juego se ha colgado debido al mod "%1$s".\n\
game.crash.reason.mod=El juego se ha colgado debido al mod «%1$s».\n\
\n\
Puedes actualizar o eliminar el mod y volver a intentarlo.
game.crash.reason.mod_resolution=El juego no puede seguir ejecutándose debido a problemas de dependencia de mods.\n\
@@ -532,20 +532,20 @@ game.crash.reason.night_config_fixes=El juego se colgó debido a algunos problem
Para obtener más información, visite el <a href="https://www.github.com/Fuzss/nightconfigfixes">repositorio de GitHub</a> del mod.
game.crash.reason.mod_resolution_collection=El juego se colgó porque la versión del mod no es compatible.\n\
\n\
"%1$s" requiere mod "%2$s".\n\
«%1$s» requiere mod «%2$s».\n\
\n\
Necesitas actualizar o degradar "%3$s" antes de continuar.
Necesitas actualizar o degradar «%3$s» antes de continuar.
game.crash.reason.mod_resolution_conflict=El juego se ha colgado debido a mods conflictivos.\n\
\n\
"%1$s" es incompatible con "%2$s".
«%1$s» es incompatible con «%2$s».
game.crash.reason.mod_resolution_missing=El juego se ha colgado porque algunos mods dependientes no estaban instalados.\n\
\n\
"%1$s" requiere mod "%2$s".\n\
«%1$s» requiere mod «%2$s».\n\
\n\
Esto significa que tienes que descargar e instalar "%2$s" primero para continuar jugando.
Esto significa que tienes que descargar e instalar «%2$s» primero para continuar jugando.
game.crash.reason.mod_resolution_missing_minecraft=El juego se ha colgado porque un mod es incompatible con la versión actual de Minecraft.\n\
\n\
"%1$s" requiere la versión de Minecraft %2$s.\n\
«%1$s» requiere la versión de Minecraft %2$s.\n\
\n\
Si quieres jugar con esta versión del mod instalada, debes cambiar la versión del juego de tu instancia.\n\
\n\
@@ -556,10 +556,10 @@ game.crash.reason.forge_repeat_installation=El juego se ha colgado debido a una
\n\
Se recomienda enviar comentarios en GitHub junto con este registro para que podamos encontrar más pistas y resolver el problema.\n\
\n\
Actualmente puede desinstalar Forge y volver a instalarlo en "Editar Instancia → Cargadores".
Actualmente puede desinstalar Forge y volver a instalarlo en «Editar Instancia → Cargadores».
game.crash.reason.optifine_repeat_installation=El juego se ha colgado debido a una instalación duplicada de OptiFine.\n\
\n\
Elimine OptiFine del directorio "mods" o desinstálelo en "Editar instancia → Cargadores".
Elimine OptiFine del directorio «mods» o desinstálelo en «Editar instancia → Cargadores».
game.crash.reason.modmixin_failure=El juego se ha colgado debido a que algunos mods no se han inyectado.\n\
\n\
Esto generalmente significa que el mod tiene un error o no es compatible con el entorno actual. \n\
@@ -578,21 +578,21 @@ game.crash.reason.forge_error=Forge/NeoForge puede haber proporcionado informaci
game.crash.reason.mod_resolution0=El juego se ha colgado debido a algunos problemas con el mod. Puede consultar el registro para ver si hay un mod incorrecto.
game.crash.reason.java_version_is_too_high=El juego se ha colgado debido a que la versión de Java es demasiado nueva para seguir ejecutándose.\n\
\n\
Cambia la versión principal anterior de Java en "Config. Global/Específica de instancia → Java" y, a continuación, inicia el juego.\n\
Cambia la versión principal anterior de Java en «Config. Global/Específica de instancia → Java» y, a continuación, inicia el juego.\n\
\n\
Si no es así, puede descargarlo desde <a href="https://www.java.com/download/">java.com (Java 8)</a> o <a href="https://bell-sw.com/pages/downloads/#downloads">BellSoft Liberica Full JRE (Java 17)</a> y otras plataformas para descargar e instalar una (reinicie el iniciador después de la instalación).
game.crash.reason.mod_name=El juego se ha colgado debido a problemas con el nombre de archivo del mod.\n\
\n\
Los nombres de los archivos Mod deben usar sólo letras inglesas (A~Z, a~z), números (0~9), líneas horizontales(-), subrayado (_) y puntos (.) a media altura.\n\
\n\
Por favor, vaya al directorio "mods" y cambie todos los nombres de archivo de mods no conformes utilizando los caracteres conformes anteriores.
Por favor, vaya al directorio «mods» y cambie todos los nombres de archivo de mods no conformes utilizando los caracteres conformes anteriores.
game.crash.reason.incomplete_forge_installation=O jogo atual não pode continuar devido a uma instalação incompleta do Forge / NeoForge.\n\
\n\
Reinstale o Forge/NeoForge em "Editar Instância → Carregadores".
Reinstale o Forge/NeoForge em «Editar Instância → Carregadores».
game.crash.reason.modlauncher_8=El juego se ha colgado debido a que la versión actual de Forge no es compatible con tu instalación de Java. Intenta actualizar Forge.
game.crash.reason.no_class_def_found_error=El juego no puede ejecutarse debido a un código incompleto.\n\
\n\
A su instancia de juego le falta "%1$s", esto puede deberse a que falta un mod, a que hay un mod incompatible instalado o a que algunos archivos pueden estar dañados.\n\
A su instancia de juego le falta «%1$s», esto puede deberse a que falta un mod, a que hay un mod incompatible instalado o a que algunos archivos pueden estar dañados.\n\
\n\
Puede que tengas que reinstalar el juego y todos los mods o pedir ayuda a alguien.
game.crash.reason.no_such_method_error=El juego no puede ejecutarse debido a un código incompleto.\n\
@@ -606,10 +606,10 @@ game.crash.reason.opengl_not_supported=El juego se ha colgado porque tu controla
O bien, puede intentar actualizar su controlador a la última versión y volver a intentarlo.\n\
\n\
Si tu ordenador tiene una tarjeta gráfica discreta, por favor, asegúrate de que el juego la está utilizando para el renderizado. Si el problema persiste, considera la posibilidad de adquirir una nueva tarjeta gráfica o un nuevo ordenador.
game.crash.reason.openj9=El juego no puede ejecutarse en una JVM OpenJ9. Por favor, cambia a un Java que utilice la JVM Hotspot en "Config. Global/Específica de instancia → Java" y vuelve a ejecutar el juego. Si no dispones de una, puedes descargarla.
game.crash.reason.openj9=El juego no puede ejecutarse en una JVM OpenJ9. Por favor, cambia a un Java que utilice la JVM Hotspot en «Config. Global/Específica de instancia → Java» y vuelve a ejecutar el juego. Si no dispones de una, puedes descargarla.
game.crash.reason.out_of_memory=El juego se ha colgado porque el ordenador se ha quedado sin memoria.\n\
\n\
Puede que no haya suficiente memoria disponible o que haya demasiados mods instalados. Puedes intentar resolverlo aumentando la memoria asignada en "Config. Global/Específica de instancia → Memoria".\n\
Puede que no haya suficiente memoria disponible o que haya demasiados mods instalados. Puedes intentar resolverlo aumentando la memoria asignada en «Config. Global/Específica de instancia → Memoria».\n\
\n\
Si sigues encontrando estos problemas, es posible que necesites una mejor computadora.
game.crash.reason.resolution_too_high=El juego se ha colgado porque la resolución del paquete de recursos/texturas es demasiado alta.\n\
@@ -621,14 +621,14 @@ game.crash.reason.shaders_mod=El juego se ha colgado porque OptiFine y Shaders m
game.crash.reason.rtss_forest_sodium=El juego se colgó porque el RivaTuner Statistical Server (RTSS) no es compatible con Sodium.\n\
\n\
Haz clic <a href="https://github.com/CaffeineMC/sodium-fabric/wiki/Known-Issues#rtss-incompatible">aquí</a> para obtener más detalles.
game.crash.reason.stacktrace=El motivo del cuelgue es desconocido. Puedes ver los detalles haciendo clic en el botón "Registros".\n\
game.crash.reason.stacktrace=El motivo del cuelgue es desconocido. Puedes ver los detalles haciendo clic en el botón «Registros».\n\
\n\
Hay algunas palabras clave que pueden contener algunos nombres de mods. Puedes buscarlas en Internet para averiguar el problema tú mismo.\n\
\n\
%s
game.crash.reason.too_old_java=El juego se ha colgado porque estás utilizando una versión de Java VM obsoleta.\n\
\n\
Tienes que cambiar a una versión de Java más reciente (%1$s) en "Config. Global/Específica de instancia → Java" y, a continuación, volver a ejecutar el juego. Puede descargar Java desde <a href="https://learn.microsoft.com/java/openjdk/download">aquí</a>.
Tienes que cambiar a una versión de Java más reciente (%1$s) en «Config. Global/Específica de instancia → Java» y, a continuación, volver a ejecutar el juego. Puede descargar Java desde <a href="https://learn.microsoft.com/java/openjdk/download">aquí</a>.
game.crash.reason.unknown=No somos capaces de averiguar por qué se ha colgado el juego. Por favor, refiérase a los registros del juego.
game.crash.reason.unsatisfied_link_error=No se puede iniciar Minecraft porque faltan bibliotecas: %1$s.\n\
\n\
@@ -647,10 +647,10 @@ game.crash.reason.mod_files_are_decompressed=El juego se ha colgado porque se ha
Si la extracción provoca errores en el juego, elimine el mod extraído en el directorio de mods y, a continuación, inicie el juego.
game.crash.reason.optifine_causes_the_world_to_fail_to_load=Es posible que el juego no siga ejecutándose debido al OptiFine.\n\
\n\
Este problema solo se produce en una versión específica de OptiFine. Puede probar a cambiar la versión de OptiFine en "Editar instancia → Cargadores".
Este problema solo se produce en una versión específica de OptiFine. Puede probar a cambiar la versión de OptiFine en «Editar instancia → Cargadores».
game.crash.reason.optifine_is_not_compatible_with_forge=El juego se ha colgado porque OptiFine no es compatible con la instalación actual de Forge.\n\
\n\
Por favor, navegue hasta la <a href="https://optifine.net/downloads">página oficial de OptiFine</a>, compruebe si la versión de Forge es compatible con OptiFine, y reinstale la instancia en estricta conformidad con la versión correspondiente, o cambie la versión de OptiFine en "Editar Instancia → Cargadores".\n\
Por favor, navegue hasta la <a href="https://optifine.net/downloads">página oficial de OptiFine</a>, compruebe si la versión de Forge es compatible con OptiFine, y reinstale la instancia en estricta conformidad con la versión correspondiente, o cambie la versión de OptiFine en «Editar Instancia → Cargadores».\n\
\n\
Tras realizar pruebas, creemos que las versiones de OptiFine demasiado altas o demasiado bajas pueden provocar fallos.
game.crash.reason.too_many_mods_lead_to_exceeding_the_id_limit=El juego se ha colgado porque has instalado demasiados mods y has superado el límite de ID del juego.\n\
@@ -676,10 +676,10 @@ install.failed=Fallo en la instalación
install.failed.downloading=No se han podido descargar algunos archivos necesarios.
install.failed.downloading.detail=No se ha podido descargar el archivo: %s
install.failed.downloading.timeout=Tiempo de espera de la descarga: %s
install.failed.install_online=No se ha podido identificar el archivo proporcionado. Si está instalando un mod, vaya a la página "Mods".
install.failed.malformed=Los archivos descargados están dañados. Puedes intentar resolver este problema cambiando a otra fuente de descarga en "Ajustes → Descarga → Fuente de descarga".
install.failed.install_online=No se ha podido identificar el archivo proporcionado. Si está instalando un mod, vaya a la página «Mods».
install.failed.malformed=Los archivos descargados están dañados. Puedes intentar resolver este problema cambiando a otra fuente de descarga en «Ajustes → Descarga → Fuente de descarga».
install.failed.optifine_conflict=No se puede instalar tanto OptiFine como Fabric en Minecraft 1.13 o posterior.
install.failed.optifine_forge_1.17=Para Minecraft 1.17.1, Forge sólo es compatible con OptiFine H1 pre2 o posterior. Puedes instalarlos marcando "Snapshots" al elegir una versión de OptiFine en HMCL.
install.failed.optifine_forge_1.17=Para Minecraft 1.17.1, Forge sólo es compatible con OptiFine H1 pre2 o posterior. Puedes instalarlos marcando «Snapshots» al elegir una versión de OptiFine en HMCL.
install.failed.version_mismatch=Este cargador requiere la versión del juego %s, pero la instalada es %s.
install.installer.change_version=%s Incompatible
install.installer.choose=Elija su versión %s
@@ -749,10 +749,10 @@ lang.default=Usar idioma del sistema
launch.advice=%s ¿Todavía quieres continuar?
launch.advice.multi=Se han detectado los siguientes problemas:\n\n%s\n\nEstos problemas pueden impedir que inicies el juego o afectar a la experiencia de juego.\n\n¿Todavía quieres continuar?
launch.advice.java.auto=La versión actual de la máquina virtual de Java no cumple los requisitos del juego.\n\nHaga clic en "Sí" para elegir automáticamente la versión de Java VM más compatible. O bien, puede navegar hasta "Config. Global/Específica de instancia → Java" para elegir uno usted mismo.
launch.advice.java.auto=La versión actual de la máquina virtual de Java no cumple los requisitos del juego.\n\nHaga clic en «Sí» para elegir automáticamente la versión de Java VM más compatible. O bien, puede navegar hasta «Config. Global/Específica de instancia → Java» para elegir uno usted mismo.
launch.advice.java.modded_java_7=Minecraft 1.7.2 y versiones anteriores requieren Java 7 o anterior.
launch.advice.corrected=Hemos solucionado el problema de la máquina virtual de Java. Si todavía quieres utilizar la versión de Java que elijas, puedes desactivar la opción "No comprobar la compatibilidad de la JVM" en "Config. Global/Específica de instancia → Configuración avanzada".
launch.advice.uncorrected=Si todavía quieres utilizar la versión de Java que elijas, puedes desactivar la opción "No comprobar la compatibilidad de la JVM" en "Config. Global/Específica de instancia → Configuración avanzada".
launch.advice.corrected=Hemos solucionado el problema de la máquina virtual de Java. Si todavía quieres utilizar la versión de Java que elijas, puedes desactivar la opción «No comprobar la compatibilidad de la JVM» en «Config. Global/Específica de instancia → Configuración avanzada».
launch.advice.uncorrected=Si todavía quieres utilizar la versión de Java que elijas, puedes desactivar la opción «No comprobar la compatibilidad de la JVM» en «Config. Global/Específica de instancia → Configuración avanzada».
launch.advice.different_platform=Se recomienda la versión de 64 bits de Java para tu dispositivo, pero has instalado una de 32 bits.
launch.advice.forge2760_liteloader=La versión 2760 o posterior de Forge no es compatible con LiteLoader, por favor considere actualizar Forge a la versión 2773 o posterior.
launch.advice.forge28_2_2_optifine=La versión 28.2.2 o posterior de Forge no es compatible con OptiFine. Considere la posibilidad de actualizar Forge a la versión 28.2.1 o anterior.
@@ -778,7 +778,7 @@ launch.failed.download_library=No se pudo descargar la biblioteca %s.
launch.failed.executable_permission=No se pudo hacer ejecutable el script de ejecución.
launch.failed.execution_policy=Configuración de la política de ejecución
launch.failed.execution_policy.failed_to_set=No se pudo establecer la política de ejecución
launch.failed.execution_policy.hint=La política de ejecución actual impide la ejecución de scripts de PowerShell.\n\nHaga clic en "Aceptar" para permitir que el usuario actual ejecute scripts de PowerShell, o haga clic en "Cancelar" para mantenerlo como está.
launch.failed.execution_policy.hint=La política de ejecución actual impide la ejecución de scripts de PowerShell.\n\nHaga clic en «Aceptar» para permitir que el usuario actual ejecute scripts de PowerShell, o haga clic en «Cancelar» para mantenerlo como está.
launch.failed.exited_abnormally=El juego se ha bloqueado. Por favor, consulte el registro de errores para más detalles.
launch.failed.java_version_too_low=La versión de Java que ha especificado es demasiado baja. Por favor, reajuste la versión de Java.
launch.failed.no_accepted_java=No se ha podido encontrar una versión de Java compatible. Si crees que has descargado una compatible, puedes establecerla manualmente en los ajustes.
@@ -800,13 +800,13 @@ launcher.background=Imagen de fondo
launcher.background.choose=Elige una imagen de fondo
launcher.background.classic=Clásico
launcher.background.default=Por defecto
launcher.background.default.tooltip=O "background.png/.jpg/.gif/.webp" y las imágenes en el directorio "bg".
launcher.background.default.tooltip=O «background.png/.jpg/.gif/.webp» y las imágenes en el directorio «bg».
launcher.background.network=Desde la URL
launcher.background.translucent=Translúcido
launcher.cache_directory=Directorio de la caché
launcher.cache_directory.clean=Borrar caché
launcher.cache_directory.choose=Elegir el directorio de la caché
launcher.cache_directory.default=Por defecto ("%APPDATA%/.minecraft" o "~/.minecraft")
launcher.cache_directory.default=Por defecto («%APPDATA%/.minecraft» o «~/.minecraft»)
launcher.cache_directory.disabled=Desactivado
launcher.cache_directory.invalid=No se ha podido crear el directorio de la caché, volviendo a los valores por defecto.
launcher.contact=Contacta con nosotros
@@ -1051,10 +1051,18 @@ web.view_in_browser=Ver en navegador
world=Mundos
world.add=Añadir mundo
world.backup=Copia de seguridad
world.backup.create.failed=No se ha podido crear la copia de seguridad.\n%s
world.backup.create.locked=El mundo está actualmente en uso. Por favor, cierra el juego e inténtalo de nuevo.
world.backup.create.success=Creada con éxito una nueva copia de seguridad: %s
world.backup.delete=Eliminar esta copia de seguridad
world.backup.reveal=Abrir el directorio
world.backup.title=Mundo [%s] - Copias de seguridad
world.datapack=Gestionar paquetes de datos
world.datapack.1_13=Sólo Minecraft 1.13 o posterior soporta paquetes de datos.
world.datetime=Jugado por última vez en %s
world.download=Descargar Mundo
world.download.title=Descargar mundo - %1s
world.export=Exportar el mundo
world.export.title=Elija el directorio para este mundo exportado
world.export.location=Guardar como
@@ -1065,6 +1073,7 @@ world.import.already_exists=Este mundo ya existe.
world.import.choose=Elija el archivo de mundo que desea importar
world.import.failed=No se ha podido importar este mundo: %s
world.import.invalid=No se ha podido analizar el mundo.
world.info=Información del mundo
world.info.title=Mundo [%s] - Información
world.info.basic=Información básica
world.info.allow_cheats=Permitir comandos/trucos
@@ -1094,6 +1103,7 @@ world.info.player.xp_level=Nivel de experiencia
world.info.random_seed=Semilla
world.info.time=Tiempo de juego
world.info.time.format=%s días
world.locked=En uso
world.manage=Mundos
world.name=Nombre del mundo
world.name.enter=Introducir el nombre del mundo
@@ -1118,12 +1128,13 @@ repositories.maven_central=Universal (Maven Central)
repositories.tencentcloud_mirror=Espejo de China continental (Repositorio Maven Tencent Cloud)
repositories.chooser=HMCL requiere JavaFX para funcionar.\n\
\n\
Por favor, haga clic en "Aceptar" para descargar JavaFX desde el repositorio especificado, o haga clic en "Cancelar" para salir.\n\
Por favor, haga clic en «Aceptar» para descargar JavaFX desde el repositorio especificado, o haga clic en «Cancelar» para salir.\n\
\n\
Repositorios:
repositories.chooser.title=Elija la fuente de descarga de JavaFX
resourcepack=Paquetes de recursos
resourcepack.download.title=Descargar paquete de recursos - %1s
search=Búsqueda
search.hint.chinese=Buscar en inglés y chino
@@ -1162,15 +1173,15 @@ settings.advanced.dont_check_game_completeness=No chequear integridad del juego
settings.advanced.dont_check_jvm_validity=No comprobar la compatibilidad de la JVM
settings.advanced.dont_patch_natives=No intente sustituir automáticamente las bibliotecas nativas
settings.advanced.environment_variables=Variables de entorno
settings.advanced.game_dir.default=Por defecto (".minecraft/")
settings.advanced.game_dir.independent=Aislar (".minecraft/versions/<nombre de la instancia>/", excepto para los activos y las bibliotecas)
settings.advanced.game_dir.default=Por defecto («.minecraft/»)
settings.advanced.game_dir.independent=Aislar («.minecraft/versions/<nombre de la instancia>/», excepto para los activos y las bibliotecas)
settings.advanced.java_permanent_generation_space=Espacio PermGen
settings.advanced.java_permanent_generation_space.prompt=en MB
settings.advanced.jvm=Opciones de la máquina virtual de Java
settings.advanced.jvm_args=Argumentos de la máquina virtual de Java
settings.advanced.jvm_args.prompt=\ · Si los argumentos introducidos en "Argumentos de la máquina virtual de Java" son los mismos que los argumentos por defecto, no se añadirán.\n\
\ · Introduzca cualquier argumento GC en "Argumentos de la máquina virtual de Java", y se desactivará el argumento G1 de los argumentos por defecto.\n\
\ · Activa "No añadir argumentos por defecto de JVM" para ejecutar el juego sin añadir argumentos por defecto.
settings.advanced.jvm_args.prompt=\ · Si los argumentos introducidos en «Argumentos de la máquina virtual de Java» son los mismos que los argumentos por defecto, no se añadirán.\n\
\ · Introduzca cualquier argumento GC en «Argumentos de la máquina virtual de Java», y se desactivará el argumento G1 de los argumentos por defecto.\n\
\ · Activa «No añadir argumentos por defecto de JVM» para ejecutar el juego sin añadir argumentos por defecto.
settings.advanced.launcher_visibility.close=Cerrar el launcher después de ejecutar el juego.
settings.advanced.launcher_visibility.hide=Ocultar el launcher después de ejecutar el juego.
settings.advanced.launcher_visibility.hide_and_reopen=Ocultar el launcher y volver a abrirlo cuando se cierra el juego.
@@ -1203,6 +1214,7 @@ settings.advanced.renderer.llvmpipe=Software (Bajo rendimiento, máxima compatib
settings.advanced.renderer.zink=Vulkan (Máximo rendimiento, baja compatibilidad)
settings.advanced.server_ip=Dirección del servidor
settings.advanced.server_ip.prompt=Entrar automáticamente después de ejecutar el juego
settings.advanced.unsupported_system_options=Configuración no aplicable al sistema actual
settings.advanced.use_native_glfw=[Sólo Linux/FreeBSD] Utilizar GLFW nativo
settings.advanced.use_native_openal=[Sólo Linux/FreeBSD] Utilizar OpenAL nativo
settings.advanced.workaround=Métodos alternativos
@@ -1231,7 +1243,7 @@ settings.game.java_directory.version=Especifique la versión de Java
settings.game.management=Gestionar
settings.game.working_directory=Directorio de trabajo
settings.game.working_directory.choose=Elija el directorio de trabajo
settings.game.working_directory.hint=Activar la opción "Aislar" en "Directorio de trabajo" para permitir que la instancia actual almacene sus configuraciones, mundos y mods en un directorio separado.\n\
settings.game.working_directory.hint=Activar la opción «Aislar» en «Directorio de trabajo» para permitir que la instancia actual almacene sus configuraciones, mundos y mods en un directorio separado.\n\
\n\
Se recomienda activar esta opción para evitar conflictos con los mods, pero tendrás que mover tus guardados manualmente.
@@ -1253,7 +1265,7 @@ settings.launcher.general=General
settings.launcher.language=Idioma
settings.launcher.launcher_log.export=Exportar registros del launcher
settings.launcher.launcher_log.export.failed=No se han podido exportar los registros
settings.launcher.launcher_log.export.success=Los registros se han exportado a "%s"
settings.launcher.launcher_log.export.success=Los registros se han exportado a «%s»
settings.launcher.launcher_log.reveal=Abrir el directorio
settings.launcher.log=Registro
settings.launcher.log.font=Fuente
@@ -1290,7 +1302,7 @@ settings.type.global.manage=Config. Global
settings.type.global.edit=Editar Config. Global
settings.type.special.enable=Activar Config. Específica de la instancia
settings.type.special.edit=Editar configuración de la instancia actual
settings.type.special.edit.hint=La instancia actual "%s" ha activado la "Configuración específica de la instancia". Todas las configuraciones de esta página NO afectarán a esa instancia. Haga clic aquí para editar la configuración de la instancia.
settings.type.special.edit.hint=La instancia actual «%s» ha activado la «Configuración específica de la instancia». Todas las configuraciones de esta página NO afectarán a esa instancia. Haga clic aquí para editar la configuración de la instancia.
sponsor=Donantes
sponsor.bmclapi=Las descargas de China continental son proporcionadas por BMCLAPI. Haga clic aquí para obtener más información.
@@ -1331,7 +1343,7 @@ version.name=Nombre de instancia
version.cannot_read=No se ha podido analizar la instancia del juego, la instalación no puede continuar.
version.empty=No hay instancias
version.empty.add=Añadir una instancia
version.empty.launch=No hay instancias. Puedes instalar una en "Descargar → Nuevo juego".
version.empty.launch=No hay instancias. Puedes instalar una en «Descargar → Nuevo juego».
version.empty.hint=No hay instancias de Minecraft aquí. Puedes intentar cambiar a otro directorio del juego o hacer clic aquí para descargar una.
version.game.old=Histórico
version.game.release=Lanzamiento
@@ -1347,7 +1359,7 @@ version.launch_script.save=Exportar script de ejecución
version.launch_script.success=Exportado el script de ejecución como %s.
version.manage=Todas las instancias
version.manage.clean=Eliminar archivos de registros
version.manage.clean.tooltip=Eliminar los archivos de los directorios "logs" y "crash-reports".
version.manage.clean.tooltip=Eliminar los archivos de los directorios «logs» y «crash-reports».
version.manage.duplicate=Duplicar instancia
version.manage.duplicate.duplicate_save=Duplicar mundos
version.manage.duplicate.prompt=Introduzca el nombre de la nueva instancia
@@ -1356,8 +1368,8 @@ version.manage.manage=Editar instancia
version.manage.manage.title=Editar instancia - %1s
version.manage.redownload_assets_index=Actualizar activos del juego
version.manage.remove=Eliminar instancia
version.manage.remove.confirm=¿Está seguro de que quiere eliminar permanentemente la instancia "%s"? ¡Esta operación no se puede deshacer!
version.manage.remove.confirm.trash=¿Estás seguro de que quieres eliminar la instancia "%s"? Todavía puedes encontrar sus archivos en tu papelera de reciclaje con el nombre de "%s".
version.manage.remove.confirm=¿Está seguro de que quiere eliminar permanentemente la instancia «%s»? ¡Esta operación no se puede deshacer!
version.manage.remove.confirm.trash=¿Estás seguro de que quieres eliminar la instancia «%s»? Todavía puedes encontrar sus archivos en tu papelera de reciclaje con el nombre de «%s».
version.manage.remove.confirm.independent=Dado que esta instancia está almacenada en un directorio aislado, al eliminarla también se eliminarán sus guardados y otros datos. ¿Aún quieres borrar la instancia %s?
version.manage.remove_assets=Borrar todas las activos del juego
version.manage.remove_libraries=Borrar todas las bibliotecas
@@ -804,6 +804,7 @@ settings.advanced.post_exit_command=終了後のコマンド
settings.advanced.post_exit_command.prompt=ゲーム終了後に実行されます
settings.advanced.server_ip=サーバーアドレス
settings.advanced.server_ip.prompt=ゲームの起動時にサーバーに参加する
settings.advanced.unsupported_system_options=サポートされていないシステムオプション
settings.advanced.use_native_glfw=[Linux/FreeBSDのみ]システムGLFWを使用する
settings.advanced.use_native_openal=[Linux/FreeBSDのみ]システムOpenALを使用する
settings.advanced.workaround=デバッグ用オプション
@@ -1050,10 +1050,18 @@ web.view_in_browser=Смотреть в браузере
world=Миры
world.add=Добавить мир
world.backup=Резервный мир
world.backup.create.failed=Не удалось создать резервную копию.\n%s
world.backup.create.locked=В настоящее время мир находится в эксплуатации. Закройте игру и попробуйте снова.
world.backup.create.success=Успешно создано новое резервное копирование: %s
world.backup.delete=Удалить эту резервную копию
world.backup.reveal=Показать в диспетчере файлов
world.backup.title=Мир [%s] - Резервные копии
world.datapack=Управлять наборами данных
world.datapack.1_13=Только Minecraft 1.13 или новее поддерживает наборы данных.
world.datetime=Последний запуск игры %s
world.download=Скачать мир
world.download.title=Скачать мир - %1s
world.export=Экспорт мира
world.export.title=Выберите папку для экспорта мира
world.export.location=Экспорт в
@@ -1064,6 +1072,7 @@ world.import.already_exists=Мир уже существует.
world.import.choose=Выберите архив мира, который хотите импортировать
world.import.failed=Не удалось импортировать этот мир\: %s
world.import.invalid=Не удалось разобрать мир.
world.info=Сведения о мире
world.info.title=Мир [%s] - Сведения
world.info.basic=Основные сведения
world.info.allow_cheats=Разрешить команды/читы
@@ -1093,6 +1102,7 @@ world.info.player.xp_level=Уровень опыта
world.info.random_seed=Ключ генератора мира
world.info.time=Время игры
world.info.time.format=%s дн.
world.locked=В эксплуатации
world.manage=Миры
world.name=Название мира
world.name.enter=Введите название мира
@@ -1123,6 +1133,7 @@ repositories.chooser=Для работы HMCL требуется JavaFX.\n\
repositories.chooser.title=Выберите зеркало для скачивания JavaFX
resourcepack=Пакеты ресурсов
resourcepack.download.title=Скачать пакет ресурсов - %1s
search=Поиск
search.hint.chinese=Поиск на китайском и английском языках
@@ -1203,6 +1214,7 @@ settings.advanced.renderer.llvmpipe=ПО (Низкая производител
settings.advanced.renderer.zink=Vulkan (Лучшая производительность, низкая совместимость)
settings.advanced.server_ip=Адрес сервера
settings.advanced.server_ip.prompt=Присоединяться к серверу при запуске игры
settings.advanced.unsupported_system_options=Настройки, не применимые к текущей системе
settings.advanced.use_native_glfw=[Только для Linux/FreeBSD] Использовать системный GLFW
settings.advanced.use_native_openal=[Только для Linux/FreeBSD] Использовать системный OpenAL
settings.advanced.workaround=Обходные пути
@@ -96,7 +96,7 @@ account.register=註冊
account.manage=帳戶清單
account.copy_uuid=複製該帳戶的 UUID
account.methods=登入方式
account.methods.authlib_injector=authlib-injector 登入
account.methods.authlib_injector=外置登入
account.methods.microsoft=Microsoft 帳戶
account.methods.microsoft.birth=如何變更帳戶出生日期
account.methods.microsoft.deauthorize=移除應用存取權
@@ -863,6 +863,7 @@ world.datapack=管理資料包
world.datapack.1_13=僅 Minecraft 1.13 及之後的版本支援資料包
world.datetime=上一次遊戲時間: %s
world.download=下載世界
world.download.title=世界下載 - %1s
world.export=匯出此世界
world.export.title=選取該世界的儲存位置
world.export.location=儲存到
@@ -930,6 +931,7 @@ repositories.chooser=缺少 JavaFX 執行環境,HMCL 需要 JavaFX 才能正
repositories.chooser.title=選取 JavaFX 下載源
resourcepack=資源包
resourcepack.download.title=資源包下載 - %1s
search=搜尋
search.hint.chinese=支援中英文搜尋
@@ -1007,6 +1009,7 @@ settings.advanced.renderer.llvmpipe=軟繪製器 (效能較差,相容性最好
settings.advanced.renderer.zink=Vulkan (效能最好,相容性較差)
settings.advanced.server_ip=伺服器位址
settings.advanced.server_ip.prompt=預設,啟動遊戲後直接進入對應伺服器
settings.advanced.unsupported_system_options=不適用於目前系統的選項
settings.advanced.use_native_glfw=[僅限 Linux/FreeBSD] 使用系統 GLFW
settings.advanced.use_native_openal=[僅限 Linux/FreeBSD] 使用系統 OpenAL
settings.advanced.workaround=除錯選項
@@ -1109,12 +1112,12 @@ update.changelog=更新日誌
update.channel.dev=開發版
update.channel.dev.hint=你正在使用 HMCL 開發版。開發版包含一些未在穩定版中包含的測試性功能,僅用於體驗新功能。開發版功能未受充分驗證,使用起來可能不穩定!\n\
\n\
如果你遇到了使用問題,可以透過設定中 <a href="hmcl://settings/feedback">回報頁面</a> 提供的管道進行回報。
如果你遇到了使用問題,可以透過設定中 <a href="hmcl://settings/feedback">回報頁面</a> 提供的管道進行回報。歡迎關注 Bilibili <a href="https://space.bilibili.com/1445341">@huanghongxun</a> 以關注 HMCL 的重要動態,或關注 <a href="https://space.bilibili.com/20314891">@Glavo</a> 以瞭解 HMCL 的開發進展。
update.channel.dev.title=開發版提示
update.channel.nightly=預覽版
update.channel.nightly.hint=你正在使用 HMCL 預覽版。預覽版更新較為頻繁,包含一些未在穩定版和開發版中包含的測試性功能,僅用於體驗新功能。預覽版功能未受充分驗證,使用起來可能不穩定!\n\
\n\
如果你遇到了使用問題,可以透過設定中 <a href="hmcl://settings/feedback">回報頁面</a> 提供的管道進行回報。
如果你遇到了使用問題,可以透過設定中 <a href="hmcl://settings/feedback">回報頁面</a> 提供的管道進行回報。歡迎關注 Bilibili <a href="https://space.bilibili.com/1445341">@huanghongxun</a> 以關注 HMCL 的重要動態,或關注 <a href="https://space.bilibili.com/20314891">@Glavo</a> 以瞭解 HMCL 的開發進展。
update.channel.nightly.title=預覽版提示
update.channel.stable=穩定版
update.checking=正在檢查更新
@@ -1170,7 +1173,7 @@ version.settings=遊戲設定
version.update=更新模組包
wiki.tooltip=Minecraft Wiki 頁面
wiki.version.game.release=https://zh.minecraft.wiki/w/Java版%s?variant=zh-tw
wiki.version.game.release=https://zh.minecraft.wiki/w/Java%%E7%%89%%88%s?variant=zh-tw
wiki.version.game.snapshot=https://zh.minecraft.wiki/w/%s?variant=zh-tw
wizard.prev=< 上一步
@@ -97,7 +97,7 @@ account.register=注册
account.manage=账户列表
account.copy_uuid=复制该账户的 UUID
account.methods=登录方式
account.methods.authlib_injector=外置登录 (authlib-injector)
account.methods.authlib_injector=外置登录
account.methods.microsoft=微软账户
account.methods.microsoft.birth=如何更改账户出生日期
account.methods.microsoft.close_page=已完成微软账户授权,接下来启动器还需要完成其余登录步骤。你现在可以关闭本页面了。
@@ -874,6 +874,7 @@ world.datapack=管理数据包
world.datapack.1_13=仅 Minecraft 1.13 及之后的版本支持数据包
world.datetime=上一次游戏时间: %s
world.download=下载世界
world.download.title=世界下载 - %1s
world.export=导出此世界
world.export.title=选择该世界的存储位置
world.export.location=保存到
@@ -941,6 +942,7 @@ repositories.chooser=缺少 JavaFX 运行环境,HMCL 需要 JavaFX 才能正
repositories.chooser.title=选择 JavaFX 下载源
resourcepack=资源包
resourcepack.download.title=资源包下载 - %1s
search=搜索
search.hint.chinese=支持中英文搜索
@@ -1018,6 +1020,7 @@ settings.advanced.renderer.llvmpipe=软渲染器 (性能较差,兼容性最好
settings.advanced.renderer.zink=Vulkan (性能最好,兼容性较差)
settings.advanced.server_ip=服务器地址
settings.advanced.server_ip.prompt=默认,启动游戏后可以直接进入对应服务器
settings.advanced.unsupported_system_options=不适用于当前系统的选项
settings.advanced.use_native_glfw=[仅 Linux/FreeBSD] 使用系统 GLFW
settings.advanced.use_native_openal=[仅 Linux/FreeBSD] 使用系统 OpenAL
settings.advanced.workaround=调试选项
@@ -1120,12 +1123,12 @@ update.changelog=更新日志
update.channel.dev=开发版
update.channel.dev.hint=你正在使用 HMCL 开发版。开发版包含一些未在稳定版中包含的测试性功能,仅用于体验新功能。开发版功能未受充分验证,使用起来可能不稳定!<a href="https://hmcl.huangyuhui.net/download">下载稳定版</a>\n\
\n\
如果你使用时遇到了问题,可以通过设置中<a href="hmcl://settings/feedback">反馈页面</a>提供的渠道进行反馈。欢迎关注 B 站账号 <a href="https://space.bilibili.com/1445341">@huanghongxun</a> 以关注 HMCL 的开发进展。
如果你使用时遇到了问题,可以通过设置中<a href="hmcl://settings/feedback">反馈页面</a>提供的渠道进行反馈。欢迎关注 B 站账号 <a href="https://space.bilibili.com/1445341">@huanghongxun</a> 以关注 HMCL 的重要动态,或关注 <a href="https://space.bilibili.com/20314891">@Glavo</a> 以了解 HMCL 的开发进展。
update.channel.dev.title=开发版提示
update.channel.nightly=预览版
update.channel.nightly.hint=你正在使用 HMCL 预览版。预览版更新较为频繁,包含一些未在稳定版和开发版中包含的测试性功能,仅用于体验新功能。预览版功能未受充分验证,使用起来可能不稳定!<a href="https://hmcl.huangyuhui.net/download">下载稳定版</a>\n\
\n\
如果你使用时遇到了问题,可以通过设置中<a href="hmcl://settings/feedback">反馈页面</a>提供的渠道进行反馈。欢迎关注 B 站账号 <a href="https://space.bilibili.com/1445341">@huanghongxun</a> 以关注 HMCL 的开发进展。
如果你使用时遇到了问题,可以通过设置中<a href="hmcl://settings/feedback">反馈页面</a>提供的渠道进行反馈。欢迎关注 B 站账号 <a href="https://space.bilibili.com/1445341">@huanghongxun</a> 以关注 HMCL 的重要动态,或关注 <a href="https://space.bilibili.com/20314891">@Glavo</a> 以了解 HMCL 的开发进展。
update.channel.nightly.title=预览版提示
update.channel.stable=稳定版
update.checking=正在检查更新
@@ -1181,7 +1184,7 @@ version.settings=游戏设置
version.update=更新整合包
wiki.tooltip=Minecraft Wiki 页面
wiki.version.game.release=https://zh.minecraft.wiki/w/Java版%s?variant=zh-cn
wiki.version.game.release=https://zh.minecraft.wiki/w/Java%%E7%%89%%88%s?variant=zh-cn
wiki.version.game.snapshot=https://zh.minecraft.wiki/w/%s?variant=zh-cn
wizard.prev=< 上一步
@@ -23,6 +23,7 @@ import org.jackhuang.hmcl.auth.yggdrasil.TextureModel;
import org.jackhuang.hmcl.task.FetchTask;
import org.jackhuang.hmcl.task.GetTask;
import org.jackhuang.hmcl.task.Task;
import org.jackhuang.hmcl.util.Lang;
import org.jackhuang.hmcl.util.StringUtils;
import org.jackhuang.hmcl.util.gson.JsonUtils;
import org.jackhuang.hmcl.util.io.FileUtils;
@@ -165,7 +166,9 @@ public class Skin {
});
case LITTLE_SKIN:
case CUSTOM_SKIN_LOADER_API:
String realCslApi = type == Type.LITTLE_SKIN ? "https://littleskin.cn" : StringUtils.removeSuffix(cslApi, "/");
String realCslApi = type == Type.LITTLE_SKIN
? "https://littleskin.cn"
: StringUtils.removeSuffix(Lang.requireNonNullElse(cslApi, ""), "/");
return Task.composeAsync(() -> new GetTask(new URL(String.format("%s/%s.json", realCslApi, username))))
.thenComposeAsync(json -> {
SkinJson result = JsonUtils.GSON.fromJson(json, SkinJson.class);
@@ -269,5 +269,5 @@ public class YggdrasilService {
.registerTypeAdapterFactory(ValidationTypeAdapterFactory.INSTANCE)
.create();
public static final String PURCHASE_URL = "https://www.microsoft.com/store/productid/9nxp44l49shj";
public static final String PURCHASE_URL = "https://www.xbox.com/games/store/minecraft-java-bedrock-edition-for-pc/9nxp44l49shj";
}
@@ -78,7 +78,8 @@ public final class BMCLAPIDownloadProvider implements DownloadProvider {
pair("https://meta.fabricmc.net", apiRoot + "/fabric-meta"),
pair("https://maven.fabricmc.net", apiRoot + "/maven"),
pair("https://authlib-injector.yushi.moe", apiRoot + "/mirrors/authlib-injector"),
pair("https://repo1.maven.org/maven2", "https://mirrors.cloud.tencent.com/nexus/repository/maven-public")
pair("https://repo1.maven.org/maven2", "https://mirrors.cloud.tencent.com/nexus/repository/maven-public"),
pair("https://zkitefly.github.io/unlisted-versions-of-minecraft", "https://vip.123pan.cn/1821946486/unlisted-versions-of-minecraft")
);
}
@@ -127,6 +127,7 @@ public class RemoteVersion implements Comparable<RemoteVersion> {
UNCATEGORIZED,
RELEASE,
SNAPSHOT,
OLD
OLD,
PENDING
}
}
@@ -68,6 +68,8 @@ public final class GameRemoteVersion extends RemoteVersion {
return Type.SNAPSHOT;
case UNKNOWN:
return Type.UNCATEGORIZED;
case PENDING:
return Type.PENDING;
default:
return Type.OLD;
}
@@ -19,12 +19,17 @@ package org.jackhuang.hmcl.download.game;
import org.jackhuang.hmcl.download.DownloadProvider;
import org.jackhuang.hmcl.download.VersionList;
import org.jackhuang.hmcl.util.gson.JsonUtils;
import org.jackhuang.hmcl.util.io.HttpRequest;
import java.io.InputStreamReader;
import java.io.Reader;
import java.util.Collection;
import java.util.Collections;
import java.util.concurrent.CompletableFuture;
import static org.jackhuang.hmcl.util.logging.Logger.LOG;
/**
*
* @author huangyuhui
@@ -50,10 +55,30 @@ public final class GameVersionList extends VersionList<GameRemoteVersion> {
public CompletableFuture<?> refreshAsync() {
return HttpRequest.GET(downloadProvider.getVersionListURL()).getJsonAsync(GameRemoteVersions.class)
.thenAcceptAsync(root -> {
GameRemoteVersions unlistedVersions = null;
//noinspection DataFlowIssue
try (Reader input = new InputStreamReader(
GameVersionList.class.getResourceAsStream("/assets/game/unlisted-versions.json"))) {
unlistedVersions = JsonUtils.GSON.fromJson(input, GameRemoteVersions.class);
} catch (Throwable e) {
LOG.error("Failed to load unlisted versions", e);
}
lock.writeLock().lock();
try {
versions.clear();
if (unlistedVersions != null) {
for (GameRemoteVersionInfo unlistedVersion : unlistedVersions.getVersions()) {
versions.put(unlistedVersion.getGameVersion(), new GameRemoteVersion(
unlistedVersion.getGameVersion(),
unlistedVersion.getGameVersion(),
Collections.singletonList(unlistedVersion.getUrl()),
unlistedVersion.getType(), unlistedVersion.getReleaseTime()));
}
}
for (GameRemoteVersionInfo remoteVersion : root.getVersions()) {
versions.put(remoteVersion.getGameVersion(), new GameRemoteVersion(
remoteVersion.getGameVersion(),
@@ -27,6 +27,7 @@ public enum ReleaseType {
MODIFIED("modified"),
OLD_BETA("old-beta"),
OLD_ALPHA("old-alpha"),
PENDING("pending"),
UNKNOWN("unknown");
private final String id;
@@ -38,25 +38,23 @@ import java.nio.file.Path;
* @author Glavo
*/
public final class JavaInfo {
public static int parseVersion(String version) {
int startIndex = version.startsWith("1.") ? 2 : 0;
int endIndex = startIndex;
while (endIndex < version.length()) {
char ch = version.charAt(endIndex);
if (ch >= '0' && ch <= '9')
endIndex++;
else
break;
}
try {
int idx = version.indexOf('.');
if (idx < 0) {
idx = version.indexOf('u');
return idx > 0 ? Integer.parseInt(version.substring(0, idx)) : Integer.parseInt(version);
} else {
int major = Integer.parseInt(version.substring(0, idx));
if (major != 1) {
return major;
} else {
int idx2 = version.indexOf('.', idx + 1);
if (idx2 < 0) {
return -1;
}
return Integer.parseInt(version.substring(idx + 1, idx2));
}
}
} catch (NumberFormatException e) {
return endIndex > startIndex ? Integer.parseInt(version.substring(startIndex, endIndex)) : -1;
} catch (Throwable e) {
// The version number is too long
return -1;
}
}
@@ -31,6 +31,7 @@ import org.jackhuang.hmcl.util.io.ResponseCodeException;
import org.jetbrains.annotations.Nullable;
import java.io.IOException;
import java.nio.file.NoSuchFileException;
import java.nio.file.Path;
import java.time.Instant;
import java.util.*;
@@ -113,6 +114,8 @@ public final class ModrinthRemoteModRepository implements RemoteModRepository {
} else {
throw e;
}
} catch (NoSuchFileException e) {
return Optional.empty();
}
}
@@ -288,7 +288,7 @@ public class CacheRepository {
try (FileChannel channel = FileChannel.open(indexFile, StandardOpenOption.CREATE, StandardOpenOption.READ, StandardOpenOption.WRITE)) {
FileLock lock = channel.lock();
try {
ETagIndex indexOnDisk = fromMaybeMalformedJson(new String(IOUtils.readFullyWithoutClosing(Channels.newInputStream(channel)), UTF_8), ETagIndex.class);
ETagIndex indexOnDisk = fromMaybeMalformedJson(IOUtils.readFullyAsStringWithClosing(Channels.newInputStream(channel)), ETagIndex.class);
Map<String, ETagItem> newIndex = joinETagIndexes(indexOnDisk == null ? null : indexOnDisk.eTag, index.values());
channel.truncate(0);
ByteBuffer writeTo = ByteBuffer.wrap(GSON.toJson(new ETagIndex(newIndex.values())).getBytes(UTF_8));
@@ -424,11 +424,17 @@ public class CacheRepository {
try (FileChannel channel = FileChannel.open(indexFile, StandardOpenOption.READ, StandardOpenOption.WRITE)) {
FileLock lock = channel.lock();
try {
Map<String, Object> indexOnDisk = fromMaybeMalformedJson(new String(IOUtils.readFullyWithoutClosing(Channels.newInputStream(channel)), UTF_8), mapTypeOf(String.class, Object.class));
Map<String, Object> indexOnDisk = fromMaybeMalformedJson(IOUtils.readFullyAsStringWithClosing(Channels.newInputStream(channel)), mapTypeOf(String.class, Object.class));
if (indexOnDisk == null) indexOnDisk = new HashMap<>();
indexOnDisk.putAll(storage);
channel.truncate(0);
channel.write(ByteBuffer.wrap(GSON.toJson(storage).getBytes(UTF_8)));
ByteBuffer writeTo = ByteBuffer.wrap(GSON.toJson(storage).getBytes(UTF_8));
while (writeTo.hasRemaining()) {
if (channel.write(writeTo) == 0) {
throw new IOException("No value is written");
}
}
this.storage = indexOnDisk;
} finally {
lock.release();
@@ -18,6 +18,7 @@
package org.jackhuang.hmcl.util.io;
import java.io.*;
import java.nio.charset.Charset;
import java.util.zip.GZIPInputStream;
/**
@@ -74,6 +75,10 @@ public final class IOUtils {
return readFully(stream).toString("UTF-8");
}
public static String readFullyAsString(InputStream stream, Charset charset) throws IOException {
return readFully(stream).toString(charset.name());
}
public static void copyTo(InputStream src, OutputStream dest) throws IOException {
copyTo(src, dest, new byte[DEFAULT_BUFFER_SIZE]);
}
@@ -17,10 +17,10 @@
*/
package org.jackhuang.hmcl.util.platform;
import org.jackhuang.hmcl.util.io.IOUtils;
import org.jackhuang.hmcl.util.versioning.VersionNumber;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.File;
import java.util.Locale;
import java.util.concurrent.TimeUnit;
@@ -192,30 +192,46 @@ public enum Architecture {
static {
CURRENT_ARCH = parseArchName(System.getProperty("os.arch"));
String sysArchName = null;
Architecture sysArch = null;
if (OperatingSystem.CURRENT_OS == OperatingSystem.WINDOWS) {
String processorIdentifier = System.getenv("PROCESSOR_IDENTIFIER");
if (processorIdentifier != null) {
int idx = processorIdentifier.indexOf(' ');
if (idx > 0) {
sysArchName = processorIdentifier.substring(0, idx);
sysArch = parseArchName(processorIdentifier.substring(0, idx));
}
}
} else if (OperatingSystem.CURRENT_OS == OperatingSystem.OSX) {
if (CURRENT_ARCH == X86_64) {
try {
Process process = Runtime.getRuntime().exec(new String[]{"/usr/sbin/sysctl", "-n", "sysctl.proc_translated"});
if (process.waitFor(3, TimeUnit.SECONDS) && process.exitValue() == 0
&& "1".equals(IOUtils.readFullyAsString(process.getInputStream(), OperatingSystem.NATIVE_CHARSET).trim())) {
sysArch = ARM64;
}
} catch (Throwable e) {
e.printStackTrace(System.err);
}
}
} else {
try {
Process process = Runtime.getRuntime().exec(new String[]{"/bin/uname", "-m"});
if (process.waitFor(3, TimeUnit.SECONDS)) {
try (BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream(), OperatingSystem.NATIVE_CHARSET))) {
sysArchName = reader.readLine().trim();
} catch (Exception e) {
e.printStackTrace();
for (String uname : new String[]{
"/bin/uname",
"/usr/bin/uname"
}) {
if (new File(uname).exists()) {
try {
Process process = Runtime.getRuntime().exec(new String[]{uname, "-m"});
if (process.waitFor(3, TimeUnit.SECONDS) && process.exitValue() == 0) {
sysArch = parseArchName(IOUtils.readFullyAsString(process.getInputStream(), OperatingSystem.NATIVE_CHARSET).trim());
}
} catch (Throwable e) {
e.printStackTrace(System.err);
}
break;
}
} catch (Throwable ignored) {
}
}
Architecture sysArch = parseArchName(sysArchName);
SYSTEM_ARCH = sysArch == UNKNOWN ? CURRENT_ARCH : sysArch;
SYSTEM_ARCH = sysArch == null || sysArch == UNKNOWN ? CURRENT_ARCH : sysArch;
}
}
@@ -124,6 +124,8 @@ public enum OperatingSystem {
public static final String OS_RELEASE_NAME;
public static final String OS_RELEASE_PRETTY_NAME;
public static final int CODE_PAGE;
public static final Pattern INVALID_RESOURCE_CHARACTERS;
private static final String[] INVALID_RESOURCE_BASENAMES;
private static final String[] INVALID_RESOURCE_FULLNAMES;
@@ -184,13 +186,30 @@ public enum OperatingSystem {
osName = "Windows 11";
}
int codePage = -1;
try {
Process process = Runtime.getRuntime().exec(new String[]{"chcp.com"});
try (BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream(), NATIVE_CHARSET))) {
Matcher matcher = Pattern.compile("(?<cp>[0-9]+)$")
.matcher(reader.readLine().trim());
if (matcher.find()) {
codePage = Integer.parseInt(matcher.group("cp"));
}
}
process.destroy();
} catch (Throwable ignored) {
}
SYSTEM_NAME = osName;
SYSTEM_VERSION = versionNumber;
SYSTEM_BUILD_NUMBER = buildNumber;
CODE_PAGE = codePage;
} else {
SYSTEM_NAME = System.getProperty("os.name");
SYSTEM_VERSION = System.getProperty("os.version");
SYSTEM_BUILD_NUMBER = -1;
CODE_PAGE = -1;
}
Map<String, String> osRelease = Collections.emptyMap();
File diff suppressed because it is too large. Load diff
@@ -797,3 +797,20 @@
1.21.4-rc2
1.21.4-rc3
1.21.4
25w02a
25w03a
25w04a
25w05a
25w06a
25w07a
25w08a
25w09a
25w09b
25w10a
1.21.5-pre1
1.21.5-pre2
1.21.5-pre3
1.21.5-rc1
1.21.5-rc2
1.21.5
25w14craftmine
@@ -12,7 +12,15 @@ public final class JavaRuntimeTest {
@Test
public void testParseVersion() {
assertEquals(8, parseVersion("1.8.0_302"));
assertEquals(8, parseVersion("1.8-internal"));
assertEquals(11, parseVersion("11"));
assertEquals(11, parseVersion("11.0.12"));
assertEquals(11, parseVersion("11-internal"));
assertEquals(11, parseVersion("11+abc"));
assertEquals(-1, parseVersion("abc"));
assertEquals(-1, parseVersion("1."));
assertEquals(-1, parseVersion("1.-internal"));
assertEquals(-1, parseVersion(""));
}
}