Compare commits

...
Author SHA1 Message Date
Glavo d3d46f6a29 Fix #5039: 修复陶瓦联机页面部分文本在深色模式下颜色异常的问题 (#5040) 2025-12-21 15:13:58 +08:00
Glavo 6f1db67e9a 优化反馈页面 (#5008)
1. 将主页左下角“官方群组”按钮调整为跳转到“反馈”页面;
2. 将反馈页面分为“官方群组”和“提交反馈”两部分
2025-12-20 20:35:41 +08:00
mineDiamond 0d7ece819d feat: 添加快速进入世界功能 (#4872)
close #2563
2025-12-20 20:33:52 +08:00
Damon Lu 7f852080cd 在 macOS 上添加启动脚本的后缀 .command (#4927) 2025-12-20 20:32:34 +08:00
neveler e0360c2b38 使用 subtitle 显示预览更新提示 (#5019) 2025-12-20 16:04:20 +08:00
Glavo 3bf0eb2a16 Fix #5014: 修复 GameCrashWindow 中 feedbackTextFlow 样式错误的问题 (#5020) 2025-12-20 16:00:42 +08:00
Glavo 84b428b57c 在 JFXCustomColorPickerDialog 中使用 HEX 作为默认 Tab (#5011) 2025-12-20 15:33:03 +08:00
neveler 41895e97e1 修复 subtitle 颜色丢失的问题 (#5018)
fix #5017
2025-12-20 15:32:34 +08:00
zimzaza4 f839dc192c 修改AccountAdvancedListItem 控件, 在 RootPage 中添加快速选择账户 (#4932) 2025-12-19 22:18:10 +08:00
Glavo 24250a6239 更新 CNB Issue 模板 (#5010) 2025-12-19 22:03:43 +08:00
Glavo a3ab2b52b6 添加 CNB Issue 模板 (#5009) 2025-12-19 21:39:00 +08:00
Glavo c4d4c6fcf3 优化镜像仓库同步功能 (#5006) 2025-12-19 16:47:12 +08:00
Glavo f77f005f88 mirror.yml 中直接传递用户名 (#5005) 2025-12-19 16:10:02 +08:00
Glavo 9ec494664c 简化镜像仓库同步 workflow (#5004) 2025-12-19 16:02:02 +08:00
38 changed files with 405 additions and 226 deletions

No files matched your search

+8
View File
@@ -0,0 +1,8 @@
blank_issues_enabled: false
contact_links:
- name: QQ 群
url: https://docs.hmcl.net/groups.html
about: Hello Minecraft! Launcher 的官方 QQ 交流群。
- name: Discord 服务器
url: https://discord.gg/jVvC7HfM6U
about: Hello Minecraft! Launcher 的官方 Discord 服务器。
-18
View File
@@ -1,18 +0,0 @@
name: Sync to CNB
on:
push
jobs:
run:
if: ${{ github.repository == 'HMCL-dev/HMCL' }}
runs-on: ubuntu-latest
steps:
- name: Mirror GitHub to CNB
env:
SOURCE_REPO: HMCL-dev/HMCL
TARGET_REPO: HMCL-dev/HMCL
run: |
git clone --mirror "https://github.com/$SOURCE_REPO.git" -- repo
cd repo
git push -f --prune "https://${{ secrets.CNB_SYNC_USERNAME }}:${{ secrets.CNB_SYNC_TOKEN }}@cnb.cool/$TARGET_REPO.git" "refs/heads/*:refs/heads/*" "refs/tags/*:refs/tags/*"
-21
View File
@@ -1,21 +0,0 @@
name: Sync to Gitee
on:
push
jobs:
run:
if: ${{ github.repository_owner == 'HMCL-dev' }}
runs-on: ubuntu-latest
steps:
- name: Mirror GitHub to Gitee
uses: Yikun/hub-mirror-action@v1.4
with:
src: github/HMCL-dev
dst: gitee/huanghongxun
static_list: 'HMCL'
force_update: true
debug: true
dst_key: ${{ secrets.GITEE_SYNC_BOT_PRIVATE_KEY }}
dst_token: ${{ secrets.GITEE_SYNC_BOT_TOKEN }}
cache_path: /github/workspace/hub-mirror-cache
+32
View File
@@ -0,0 +1,32 @@
name: Mirror Repository
on:
workflow_dispatch:
push:
concurrency:
group: mirror-repository
cancel-in-progress: true
jobs:
mirror:
strategy:
matrix:
target:
- name: Gitee
repo: gitee.com/huanghongxun/HMCL
user: 'hmcl-sync'
token: 'GITEE_SYNC_TOKEN'
- name: CNB
repo: cnb.cool/HMCL-dev/HMCL
user: 'cnb'
token: 'CNB_SYNC_TOKEN'
name: Mirror to ${{ matrix.target.name }}
if: ${{ github.repository == 'HMCL-dev/HMCL' }}
runs-on: ubuntu-latest
steps:
- name: Mirror GitHub to ${{ matrix.target.name }}
run: |
git clone --mirror "https://github.com/$GITHUB_REPOSITORY.git" -- repo
cd repo
git push -f --prune "https://${{ matrix.target.user }}:${{ secrets[matrix.target.token] }}@${{ matrix.target.repo }}.git" "refs/heads/*:refs/heads/*" "refs/tags/*:refs/tags/*"
+9 -4
View File
@@ -100,21 +100,26 @@ jobs:
- name: Create CNB release
if: ${{ env.continue == 'true' }}
run: |
echo "Uploading tags to CNB"
git fetch --tags
git push "https://cnb:${{ secrets.CNB_SYNC_TOKEN }}@cnb.cool/$CNB_REPO.git" "$HMCL_TAG_NAME"
echo "Creating CNB release"
~/go/bin/git-cnb release create \
--repo "$CNB_REPO" \
--tag "v$HMCL_VERSION" \
--tag "$HMCL_TAG_NAME" \
--name "HMCL $HMCL_VERSION" \
--body "$(cat CNB_RELEASE_NOTE)" \
--prerelease true
echo "Uploading HMCL-$HMCL_VERSION.jar"
~/go/bin/git-cnb release asset-upload --repo="$CNB_REPO" --tag-name "v$HMCL_VERSION" --file-name "HMCL-$HMCL_VERSION.jar"
~/go/bin/git-cnb release asset-upload --repo="$CNB_REPO" --tag-name "$HMCL_TAG_NAME" --file-name "HMCL-$HMCL_VERSION.jar"
echo "Uploading HMCL-$HMCL_VERSION.exe"
~/go/bin/git-cnb release asset-upload --repo="$CNB_REPO" --tag-name "v$HMCL_VERSION" --file-name "HMCL-$HMCL_VERSION.exe"
~/go/bin/git-cnb release asset-upload --repo="$CNB_REPO" --tag-name "$HMCL_TAG_NAME" --file-name "HMCL-$HMCL_VERSION.exe"
echo "Uploading HMCL-$HMCL_VERSION.sh"
~/go/bin/git-cnb release asset-upload --repo="$CNB_REPO" --tag-name "v$HMCL_VERSION" --file-name "HMCL-$HMCL_VERSION.sh"
~/go/bin/git-cnb release asset-upload --repo="$CNB_REPO" --tag-name "$HMCL_TAG_NAME" --file-name "HMCL-$HMCL_VERSION.sh"
env:
CNB_TOKEN: ${{ secrets.CNB_SYNC_TOKEN }}
CNB_REPO: HMCL-dev/HMCL
@@ -116,9 +116,9 @@ public class JFXCustomColorPickerDialog extends StackPane {
Tab hexTab = new Tab("HEX");
hexTab.setContent(hexField);
tabs.getTabs().add(hexTab);
tabs.getTabs().add(rgbTab);
tabs.getTabs().add(hsbTab);
tabs.getTabs().add(hexTab);
curvedColorPicker.selectedPath.addListener((o, oldVal, newVal) -> {
if (paraTransition != null) {
@@ -25,20 +25,20 @@ import org.jackhuang.hmcl.Metadata;
import org.jackhuang.hmcl.download.LibraryAnalyzer;
import org.jackhuang.hmcl.event.Event;
import org.jackhuang.hmcl.event.EventManager;
import org.jackhuang.hmcl.java.JavaRuntime;
import org.jackhuang.hmcl.mod.ModAdviser;
import org.jackhuang.hmcl.mod.Modpack;
import org.jackhuang.hmcl.mod.ModpackConfiguration;
import org.jackhuang.hmcl.mod.ModpackProvider;
import org.jackhuang.hmcl.setting.Profile;
import org.jackhuang.hmcl.util.FileSaver;
import org.jackhuang.hmcl.setting.VersionIconType;
import org.jackhuang.hmcl.setting.VersionSetting;
import org.jackhuang.hmcl.ui.FXUtils;
import org.jackhuang.hmcl.util.FileSaver;
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;
import org.jackhuang.hmcl.java.JavaRuntime;
import org.jackhuang.hmcl.util.platform.OperatingSystem;
import org.jackhuang.hmcl.util.platform.SystemInfo;
import org.jackhuang.hmcl.util.versioning.GameVersionNumber;
@@ -55,8 +55,8 @@ import java.util.stream.Collectors;
import java.util.stream.Stream;
import static org.jackhuang.hmcl.setting.ConfigHolder.config;
import static org.jackhuang.hmcl.util.logging.Logger.LOG;
import static org.jackhuang.hmcl.util.Pair.pair;
import static org.jackhuang.hmcl.util.logging.Logger.LOG;
public final class HMCLGameRepository extends DefaultGameRepository {
private final Profile profile;
@@ -418,7 +418,6 @@ public final class HMCLGameRepository extends DefaultGameRepository {
.setWidth(vs.getWidth())
.setHeight(vs.getHeight())
.setFullscreen(vs.isFullscreen())
.setServerIp(vs.getServerIp())
.setWrapper(vs.getWrapper())
.setPreLaunchCommand(vs.getPreLaunchCommand())
.setPostExitCommand(vs.getPostExitCommand())
@@ -435,6 +434,10 @@ public final class HMCLGameRepository extends DefaultGameRepository {
.setJavaAgents(javaAgents)
.setJavaArguments(javaArguments);
if (StringUtils.isNotBlank(vs.getServerIp())) {
builder.setQuickPlayOption(new QuickPlayOption.MultiPlayer(vs.getServerIp()));
}
if (config().hasProxy()) {
builder.setProxyType(config().getProxyType());
builder.setProxyHost(config().getProxyHost());
@@ -52,6 +52,7 @@ import org.jackhuang.hmcl.util.versioning.VersionNumber;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.lang.ref.WeakReference;
import java.net.SocketTimeoutException;
import java.net.URI;
import java.nio.file.AccessDeniedException;
@@ -61,7 +62,6 @@ import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicReference;
import java.util.concurrent.locks.ReentrantLock;
import java.util.stream.Collectors;
import java.lang.ref.WeakReference;
import static javafx.application.Platform.runLater;
import static javafx.application.Platform.setImplicitExit;
@@ -82,6 +82,7 @@ public final class LauncherHelper {
private final VersionSetting setting;
private LauncherVisibility launcherVisibility;
private boolean showLogs;
private QuickPlayOption quickPlayOption;
private boolean disableOfflineSkin = false;
public LauncherHelper(Profile profile, Account account, String selectedVersion) {
@@ -113,6 +114,10 @@ public final class LauncherHelper {
launcherVisibility = LauncherVisibility.KEEP;
}
public void setQuickPlayOption(QuickPlayOption quickPlayOption) {
this.quickPlayOption = quickPlayOption;
}
public void setDisableOfflineSkin() {
disableOfflineSkin = true;
}
@@ -199,6 +204,9 @@ public final class LauncherHelper {
if (disableOfflineSkin) {
launchOptionsBuilder.setDaemon(false);
}
if (quickPlayOption != null) {
launchOptionsBuilder.setQuickPlayOption(quickPlayOption);
}
LaunchOptions launchOptions = launchOptionsBuilder.create();
LOG.info("Here's the structure of game mod directory:\n" + FileUtils.printFileStructure(repository.getModsDirectory(selectedVersion), 10));
@@ -400,6 +400,8 @@ public class GameCrashWindow extends Stage {
reasonPane.setHbarPolicy(ScrollPane.ScrollBarPolicy.NEVER);
reasonPane.setVbarPolicy(ScrollPane.ScrollBarPolicy.AS_NEEDED);
feedbackTextFlow.getStyleClass().add("crash-reason-text-flow");
gameDirPane.setPadding(new Insets(8));
VBox.setVgrow(gameDirPane, Priority.ALWAYS);
FXUtils.onChangeAndOperate(feedbackTextFlow.visibleProperty(), visible -> {
@@ -68,6 +68,10 @@ public class AccountAdvancedListItem extends AdvancedListItem {
};
public AccountAdvancedListItem() {
this(null);
}
public AccountAdvancedListItem(Account account) {
tooltip = new Tooltip();
FXUtils.installFastTooltip(this, tooltip);
@@ -76,9 +80,13 @@ public class AccountAdvancedListItem extends AdvancedListItem {
setActionButtonVisible(false);
FXUtils.onScroll(this, Accounts.getAccounts(),
accounts -> accounts.indexOf(account.get()),
Accounts::setSelectedAccount);
if (account != null) {
this.accountProperty().set(account);
} else {
FXUtils.onScroll(this, Accounts.getAccounts(),
accounts -> accounts.indexOf(accountProperty().get()),
Accounts::setSelectedAccount);
}
}
public ObjectProperty<Account> accountProperty() {
@@ -51,8 +51,9 @@ public class OptionToggleButton extends StackPane {
Label titleLabel = new Label();
titleLabel.textProperty().bind(title);
Label subtitleLabel = new Label();
subtitleLabel.setMouseTransparent(true);
subtitleLabel.setWrapText(true);
subtitleLabel.setMouseTransparent(true);
subtitleLabel.getStyleClass().add("subtitle");
subtitleLabel.textProperty().bind(subtitle);
pane.setCenter(left);
left.setAlignment(Pos.CENTER_LEFT);
@@ -373,9 +373,8 @@ public class DecoratorController {
if (navigator.getCurrentPage() instanceof DecoratorPage) {
DecoratorPage page = (DecoratorPage) navigator.getCurrentPage();
// FIXME: Get WorldPage working first, and revisit this later
page.closePage();
if (page.isPageCloseable()) {
page.closePage();
return;
}
}
@@ -43,34 +43,41 @@ public class FeedbackPage extends SpinnerPane {
FXUtils.smoothScrolling(scrollPane);
setContent(scrollPane);
ComponentList community = new ComponentList();
ComponentList groups = new ComponentList();
{
IconedTwoLineListItem users = new IconedTwoLineListItem();
users.setImage(FXUtils.newBuiltinImage("/assets/img/icon.png"));
users.setTitle(i18n("feedback.qq_group"));
users.setSubtitle(i18n("feedback.qq_group.statement"));
users.setTitle(i18n("contact.chat.qq_group"));
users.setSubtitle(i18n("contact.chat.qq_group.statement"));
users.setExternalLink(Metadata.GROUPS_URL);
IconedTwoLineListItem discord = new IconedTwoLineListItem();
discord.setImage(FXUtils.newBuiltinImage("/assets/img/discord.png"));
discord.setTitle(i18n("contact.chat.discord"));
discord.setSubtitle(i18n("contact.chat.discord.statement"));
discord.setExternalLink("https://discord.gg/jVvC7HfM6U");
groups.getContent().setAll(users, discord);
}
ComponentList feedback = new ComponentList();
{
IconedTwoLineListItem github = new IconedTwoLineListItem();
github.imageProperty().bind(Bindings.when(Themes.darkModeProperty())
.then(FXUtils.newBuiltinImage("/assets/img/github-white.png"))
.otherwise(FXUtils.newBuiltinImage("/assets/img/github.png")));
github.setTitle(i18n("feedback.github"));
github.setSubtitle(i18n("feedback.github.statement"));
github.setTitle(i18n("contact.feedback.github"));
github.setSubtitle(i18n("contact.feedback.github.statement"));
github.setExternalLink("https://github.com/HMCL-dev/HMCL/issues/new/choose");
IconedTwoLineListItem discord = new IconedTwoLineListItem();
discord.setImage(FXUtils.newBuiltinImage("/assets/img/discord.png"));
discord.setTitle(i18n("feedback.discord"));
discord.setSubtitle(i18n("feedback.discord.statement"));
discord.setExternalLink("https://discord.gg/jVvC7HfM6U");
community.getContent().setAll(users, github, discord);
feedback.getContent().setAll(github);
}
content.getChildren().addAll(
ComponentList.createComponentListTitle(i18n("feedback.channel")),
community
ComponentList.createComponentListTitle(i18n("contact.chat")),
groups,
ComponentList.createComponentListTitle(i18n("contact.feedback")),
feedback
);
this.setContent(content);
@@ -69,7 +69,7 @@ public class LauncherSettingsPage extends DecoratorAnimatedPage implements Decor
.addNavigationDrawerTab(tab, downloadTab, i18n("download"), SVG.DOWNLOAD)
.startCategory(i18n("help").toUpperCase(Locale.ROOT))
.addNavigationDrawerTab(tab, helpTab, i18n("help"), SVG.HELP, SVG.HELP_FILL)
.addNavigationDrawerTab(tab, feedbackTab, i18n("feedback"), SVG.FEEDBACK, SVG.FEEDBACK_FILL)
.addNavigationDrawerTab(tab, feedbackTab, i18n("contact"), SVG.FEEDBACK, SVG.FEEDBACK_FILL)
.addNavigationDrawerTab(tab, aboutTab, i18n("about"), SVG.INFO, SVG.INFO_FILL);
FXUtils.setLimitWidth(sideBar, 200);
setLeft(sideBar);
@@ -346,7 +346,7 @@ public final class MainPage extends StackPane implements DecoratorPage {
private void launch() {
Profile profile = Profiles.getSelectedProfile();
Versions.launch(profile, profile.getSelectedVersion(), null);
Versions.launch(profile, profile.getSelectedVersion());
}
private void launchNoGame() {
@@ -17,9 +17,11 @@
*/
package org.jackhuang.hmcl.ui.main;
import com.jfoenix.controls.JFXPopup;
import javafx.beans.property.ReadOnlyObjectProperty;
import javafx.scene.input.MouseButton;
import org.jackhuang.hmcl.Metadata;
import org.jackhuang.hmcl.auth.Account;
import org.jackhuang.hmcl.event.EventBus;
import org.jackhuang.hmcl.event.RefreshedVersionsEvent;
import org.jackhuang.hmcl.game.HMCLGameRepository;
@@ -39,6 +41,7 @@ import org.jackhuang.hmcl.ui.animation.AnimationUtils;
import org.jackhuang.hmcl.ui.construct.AdvancedListBox;
import org.jackhuang.hmcl.ui.construct.AdvancedListItem;
import org.jackhuang.hmcl.ui.construct.MessageDialogPane;
import org.jackhuang.hmcl.ui.construct.PopupMenu;
import org.jackhuang.hmcl.ui.decorator.DecoratorAnimatedPage;
import org.jackhuang.hmcl.ui.decorator.DecoratorPage;
import org.jackhuang.hmcl.ui.download.ModpackInstallWizardProvider;
@@ -64,8 +67,8 @@ import java.util.stream.Collectors;
import static org.jackhuang.hmcl.ui.FXUtils.runInFX;
import static org.jackhuang.hmcl.ui.versions.VersionPage.wrap;
import static org.jackhuang.hmcl.util.logging.Logger.LOG;
import static org.jackhuang.hmcl.util.i18n.I18n.i18n;
import static org.jackhuang.hmcl.util.logging.Logger.LOG;
public class RootPage extends DecoratorAnimatedPage implements DecoratorPage {
private MainPage mainPage = null;
@@ -144,6 +147,13 @@ public class RootPage extends DecoratorAnimatedPage implements DecoratorPage {
// first item in left sidebar
AccountAdvancedListItem accountListItem = new AccountAdvancedListItem();
accountListItem.setOnAction(e -> Controllers.navigate(Controllers.getAccountListPage()));
accountListItem.setOnMouseClicked(e -> {
if (e.getButton() == MouseButton.SECONDARY) {
if (!Accounts.getAccounts().isEmpty())
showAccountListPopupMenu(accountListItem);
e.consume();
}
});
accountListItem.accountProperty().bind(Accounts.selectedAccountProperty());
// second item in left sidebar
@@ -234,13 +244,39 @@ public class RootPage extends DecoratorAnimatedPage implements DecoratorPage {
.startCategory(i18n("settings.launcher.general").toUpperCase(Locale.ROOT))
.add(launcherSettingsItem)
.add(terracottaItem)
.addNavigationDrawerItem(i18n("chat"), SVG.CHAT, () -> FXUtils.openLink(Metadata.GROUPS_URL));
.addNavigationDrawerItem(i18n("contact.chat"), SVG.CHAT, () -> {
Controllers.getSettingsPage().showFeedback();
Controllers.navigate(Controllers.getSettingsPage());
});
// the root page, with the sidebar in left, navigator in center.
setLeft(sideBar);
setCenter(getSkinnable().getMainPage());
}
public void showAccountListPopupMenu(
AccountAdvancedListItem accountListItem
) {
PopupMenu popupMenu = new PopupMenu();
JFXPopup popup = new JFXPopup(popupMenu);
AdvancedListBox scrollPane = new AdvancedListBox();
scrollPane.getStyleClass().add("no-padding");
scrollPane.setPrefWidth(220);
scrollPane.setPrefHeight(-1);
scrollPane.setMaxHeight(260);
for (Account account : Accounts.getAccounts()) {
AccountAdvancedListItem item = new AccountAdvancedListItem(account);
item.setOnAction(e -> {
Accounts.setSelectedAccount(account);
popup.hide();
});
scrollPane.add(item);
}
popupMenu.getContent().add(scrollPane);
popup.show(accountListItem, JFXPopup.PopupVPosition.TOP, JFXPopup.PopupHPosition.LEFT, accountListItem.getWidth(), 0);
}
}
private boolean checkedModpack = false;
@@ -210,8 +210,8 @@ public final class SettingsPage extends ScrollPane {
{
OptionToggleButton previewPane = new OptionToggleButton();
previewPane.setTitle(i18n("update.preview"));
previewPane.setSubtitle(i18n("update.preview.subtitle"));
previewPane.selectedProperty().bindBidirectional(config().acceptPreviewUpdateProperty());
FXUtils.installFastTooltip(previewPane, i18n("update.preview.tooltip"));
ObjectProperty<UpdateChannel> updateChannel = selectedItemPropertyFor(updateChannelGroup, UpdateChannel.class);
updateChannel.set(UpdateChannel.getChannel());
@@ -167,7 +167,7 @@ public class TerracottaControllerPage extends StackPane {
progressProperty.set(0);
TextFlow body = FXUtils.segmentToTextFlow(i18n("terracotta.confirm.desc"), Controllers::onHyperlinkAction);
body.getStyleClass().add("terracotta-license");
body.getStyleClass().add("terracotta-hint");
body.setLineSpacing(4);
LineButton download = LineButton.of();
@@ -211,7 +211,7 @@ public class TerracottaControllerPage extends StackPane {
progressProperty.set(1);
TextFlow flow = FXUtils.segmentToTextFlow(i18n("terracotta.confirm.desc"), Controllers::onHyperlinkAction);
flow.getStyleClass().add("terracotta-license");
flow.getStyleClass().add("terracotta-hint");
flow.setLineSpacing(4);
LineButton host = LineButton.of();
@@ -286,6 +286,7 @@ public class TerracottaControllerPage extends StackPane {
progressProperty.set(-1);
TextFlow body = FXUtils.segmentToTextFlow(i18n("terracotta.status.scanning.desc"), Controllers::onHyperlinkAction);
body.getStyleClass().add("terracotta-hint");
body.setLineSpacing(4);
LineButton room = LineButton.of();
@@ -85,10 +85,14 @@ public class GameListItem extends Control {
Versions.openFolder(profile, version);
}
public void launch() {
public void testGame() {
Versions.testGame(profile, version);
}
public void launch() {
Versions.launch(profile, version);
}
public void modifyGameSettings() {
Versions.modifyGameSettings(profile, version);
}
@@ -44,7 +44,7 @@ public class GameListItemSkin extends SkinBase<GameListItem> {
JFXPopup popup = new JFXPopup(menu);
menu.getContent().setAll(
new IconedMenuItem(SVG.ROCKET_LAUNCH, i18n("version.launch.test"), () -> currentSkinnable.launch(), popup),
new IconedMenuItem(SVG.ROCKET_LAUNCH, i18n("version.launch.test"), () -> currentSkinnable.testGame(), popup),
new IconedMenuItem(SVG.SCRIPT, i18n("version.launch_script"), () -> currentSkinnable.generateLaunchScript(), popup),
new MenuSeparator(),
new IconedMenuItem(SVG.SETTINGS, i18n("version.manage.manage"), () -> currentSkinnable.modifyGameSettings(), popup),
@@ -87,7 +87,7 @@ public class GameListItemSkin extends SkinBase<GameListItem> {
{
JFXButton btnLaunch = new JFXButton();
btnLaunch.setOnAction(e -> skinnable.launch());
btnLaunch.setOnAction(e -> skinnable.testGame());
btnLaunch.getStyleClass().add("toggle-icon4");
BorderPane.setAlignment(btnLaunch, Pos.CENTER);
btnLaunch.setGraphic(FXUtils.limitingSize(SVG.ROCKET_LAUNCH.createIcon(24), 24, 24));
@@ -23,10 +23,7 @@ import javafx.stage.FileChooser;
import org.jackhuang.hmcl.auth.Account;
import org.jackhuang.hmcl.auth.authlibinjector.AuthlibInjectorAccount;
import org.jackhuang.hmcl.download.game.GameAssetDownloadTask;
import org.jackhuang.hmcl.game.GameDirectoryType;
import org.jackhuang.hmcl.game.GameRepository;
import org.jackhuang.hmcl.game.HMCLGameRepository;
import org.jackhuang.hmcl.game.LauncherHelper;
import org.jackhuang.hmcl.game.*;
import org.jackhuang.hmcl.mod.RemoteMod;
import org.jackhuang.hmcl.setting.*;
import org.jackhuang.hmcl.task.*;
@@ -200,7 +197,8 @@ public final class Versions {
}
}
public static void generateLaunchScript(Profile profile, String id) {
@SafeVarargs
public static void generateLaunchScript(Profile profile, String id, Consumer<LauncherHelper>... injecters) {
if (!checkVersionForLaunching(profile, id))
return;
ensureSelectedAccount(account -> {
@@ -209,23 +207,35 @@ public final class Versions {
if (Files.isDirectory(repository.getRunDirectory(id)))
chooser.setInitialDirectory(repository.getRunDirectory(id).toFile());
chooser.setTitle(i18n("version.launch_script.save"));
if (OperatingSystem.CURRENT_OS == OperatingSystem.MACOS) {
chooser.getExtensionFilters().add(
new FileChooser.ExtensionFilter(i18n("extension.command"), "*.command")
);
}
chooser.getExtensionFilters().add(OperatingSystem.CURRENT_OS == OperatingSystem.WINDOWS
? new FileChooser.ExtensionFilter(i18n("extension.bat"), "*.bat")
: new FileChooser.ExtensionFilter(i18n("extension.sh"), "*.sh"));
chooser.getExtensionFilters().add(new FileChooser.ExtensionFilter(i18n("extension.ps1"), "*.ps1"));
Path file = FileUtils.toPath(chooser.showSaveDialog(Controllers.getStage()));
if (file != null)
new LauncherHelper(profile, account, id).makeLaunchScript(file);
if (file != null) {
LauncherHelper launcherHelper = new LauncherHelper(profile, account, id);
for (Consumer<LauncherHelper> injecter : injecters) {
injecter.accept(launcherHelper);
}
launcherHelper.makeLaunchScript(file);
}
});
}
public static void launch(Profile profile, String id, Consumer<LauncherHelper> injecter) {
@SafeVarargs
public static void launch(Profile profile, String id, Consumer<LauncherHelper>... injecters) {
if (!checkVersionForLaunching(profile, id))
return;
ensureSelectedAccount(account -> {
LauncherHelper launcherHelper = new LauncherHelper(profile, account, id);
if (injecter != null)
for (Consumer<LauncherHelper> injecter : injecters) {
injecter.accept(launcherHelper);
}
launcherHelper.launch();
});
}
@@ -234,6 +244,16 @@ public final class Versions {
launch(profile, id, LauncherHelper::setTestMode);
}
public static void launchAndEnterWorld(Profile profile, String id, String worldFolderName) {
launch(profile, id, launcherHelper ->
launcherHelper.setQuickPlayOption(new QuickPlayOption.SinglePlayer(worldFolderName)));
}
public static void generateLaunchScriptForQuickEnterWorld(Profile profile, String id, String worldFolderName) {
generateLaunchScript(profile, id, launcherHelper ->
launcherHelper.setQuickPlayOption(new QuickPlayOption.SinglePlayer(worldFolderName)));
}
private static boolean checkVersionForLaunching(Profile profile, String id) {
if (id == null || !profile.getRepository().isLoaded() || !profile.getRepository().hasVersion(id)) {
JFXButton gotoDownload = new JFXButton(i18n("version.empty.launch.goto_download"));
@@ -22,6 +22,7 @@ import javafx.scene.control.Skin;
import javafx.stage.FileChooser;
import org.jackhuang.hmcl.game.World;
import org.jackhuang.hmcl.game.WorldLockedException;
import org.jackhuang.hmcl.setting.Profile;
import org.jackhuang.hmcl.task.Schedulers;
import org.jackhuang.hmcl.task.Task;
import org.jackhuang.hmcl.ui.Controllers;
@@ -39,11 +40,15 @@ public final class WorldListItem extends Control {
private final World world;
private final Path backupsDir;
private final WorldListPage parent;
private final Profile profile;
private final String id;
public WorldListItem(WorldListPage parent, World world, Path backupsDir) {
public WorldListItem(WorldListPage parent, World world, Path backupsDir, Profile profile, String id) {
this.world = world;
this.backupsDir = backupsDir;
this.parent = parent;
this.profile = profile;
this.id = id;
}
@Override
@@ -91,6 +96,14 @@ public final class WorldListItem extends Control {
}
public void showManagePage() {
Controllers.navigate(new WorldManagePage(world, backupsDir));
Controllers.navigate(new WorldManagePage(world, backupsDir, profile, id));
}
public void launch() {
Versions.launchAndEnterWorld(profile, id, world.getFileName());
}
public void generateLaunchScript() {
Versions.generateLaunchScriptForQuickEnterWorld(profile, id, world.getFileName());
}
}
@@ -115,8 +115,19 @@ public final class WorldListItemSkin extends SkinBase<WorldListItem> {
WorldListItem item = getSkinnable();
World world = item.getWorld();
popupMenu.getContent().addAll(
new IconedMenuItem(SVG.SETTINGS, i18n("world.manage.button"), item::showManagePage, popup));
if (world.getGameVersion() != null && world.getGameVersion().isAtLeast("1.20", "23w14a")) {
IconedMenuItem launchItem = new IconedMenuItem(SVG.ROCKET_LAUNCH, i18n("version.launch_and_enter_world"), item::launch, popup);
launchItem.setDisable(world.isLocked());
popupMenu.getContent().add(launchItem);
popupMenu.getContent().addAll(
new IconedMenuItem(SVG.SCRIPT, i18n("version.launch_script"), item::generateLaunchScript, popup),
new MenuSeparator()
);
}
popupMenu.getContent().add(new IconedMenuItem(SVG.SETTINGS, i18n("world.manage.button"), item::showManagePage, popup));
if (ChunkBaseApp.isSupported(world)) {
popupMenu.getContent().addAll(
@@ -61,7 +61,7 @@ public final class WorldListPage extends ListPageBase<WorldListItem> implements
if (worlds != null)
itemsProperty().setAll(worlds.stream()
.filter(world -> isShowAll() || world.getGameVersion() == null || world.getGameVersion().equals(gameVersion))
.map(world -> new WorldListItem(this, world, backupsDir)).toList());
.map(world -> new WorldListItem(this, world, backupsDir, profile, id)).toList());
});
}
@@ -100,7 +100,7 @@ public final class WorldListPage extends ListPageBase<WorldListItem> implements
if (exception == null) {
itemsProperty().setAll(result.stream()
.filter(world -> isShowAll() || world.getGameVersion() == null || world.getGameVersion().equals(gameVersion))
.map(world -> new WorldListItem(this, world, backupsDir))
.map(world -> new WorldListItem(this, world, backupsDir, profile, id))
.collect(Collectors.toList()));
} else {
LOG.warning("Failed to load world list page", exception);
@@ -131,7 +131,7 @@ public final class WorldListPage extends ListPageBase<WorldListItem> implements
Controllers.prompt(i18n("world.name.enter"), (name, resolve, reject) -> {
Task.runAsync(() -> world.install(savesDir, name))
.whenComplete(Schedulers.javafx(), () -> {
itemsProperty().add(new WorldListItem(this, new World(savesDir.resolve(name)), backupsDir));
itemsProperty().add(new WorldListItem(this, new World(savesDir.resolve(name)), backupsDir, profile, id));
resolve.run();
}, e -> {
if (e instanceof FileAlreadyExistsException)
@@ -26,6 +26,7 @@ import javafx.scene.layout.BorderPane;
import javafx.scene.layout.Priority;
import javafx.scene.layout.VBox;
import org.jackhuang.hmcl.game.World;
import org.jackhuang.hmcl.setting.Profile;
import org.jackhuang.hmcl.ui.FXUtils;
import org.jackhuang.hmcl.ui.SVG;
import org.jackhuang.hmcl.ui.animation.TransitionPane;
@@ -49,6 +50,8 @@ public final class WorldManagePage extends DecoratorAnimatedPage implements Deco
private final ObjectProperty<State> state;
private final World world;
private final Path backupsDir;
private final Profile profile;
private final String id;
private final TabHeader header;
private final TabHeader.Tab<WorldInfoPage> worldInfoTab = new TabHeader.Tab<>("worldInfoPage");
@@ -59,10 +62,13 @@ public final class WorldManagePage extends DecoratorAnimatedPage implements Deco
private FileChannel sessionLockChannel;
public WorldManagePage(World world, Path backupsDir) {
public WorldManagePage(World world, Path backupsDir, Profile profile, String id) {
this.world = world;
this.backupsDir = backupsDir;
this.profile = profile;
this.id = id;
this.worldInfoTab.setNodeSupplier(() -> new WorldInfoPage(this));
this.worldBackupsTab.setNodeSupplier(() -> new WorldBackupsPage(this));
this.datapackTab.setNodeSupplier(() -> new DatapackListPage(this));
@@ -71,6 +77,13 @@ public final class WorldManagePage extends DecoratorAnimatedPage implements Deco
this.header = new TabHeader(transitionPane, worldInfoTab, worldBackupsTab);
header.select(worldInfoTab);
// Does it need to be done in the background?
try {
sessionLockChannel = world.lock();
LOG.info("Acquired lock on world " + world.getFileName());
} catch (IOException ignored) {
}
setCenter(transitionPane);
BorderPane left = new BorderPane();
@@ -91,6 +104,12 @@ public final class WorldManagePage extends DecoratorAnimatedPage implements Deco
left.setTop(sideBar);
AdvancedListBox toolbar = new AdvancedListBox();
if (world.getGameVersion() != null && world.getGameVersion().isAtLeast("1.20", "23w14a")) {
toolbar.addNavigationDrawerItem(i18n("version.launch"), SVG.ROCKET_LAUNCH, this::launch, null);
toolbar.addNavigationDrawerItem(i18n("version.launch_script"), SVG.SCRIPT, this::generateLaunchScript, null);
}
if (ChunkBaseApp.isSupported(world)) {
PopupMenu popupMenu = new PopupMenu();
JFXPopup popup = new JFXPopup(popupMenu);
@@ -117,12 +136,7 @@ public final class WorldManagePage extends DecoratorAnimatedPage implements Deco
BorderPane.setMargin(toolbar, new Insets(0, 0, 12, 0));
left.setBottom(toolbar);
// Does it need to be done in the background?
try {
sessionLockChannel = world.lock();
LOG.info("Acquired lock on world " + world.getFileName());
} catch (IOException ignored) {
}
this.addEventHandler(Navigator.NavigationEvent.EXITED, this::onExited);
}
@Override
@@ -142,14 +156,7 @@ public final class WorldManagePage extends DecoratorAnimatedPage implements Deco
return sessionLockChannel == null;
}
@Override
public boolean back() {
closePage();
return true;
}
@Override
public void closePage() {
public void onExited(Navigator.NavigationEvent event) {
if (sessionLockChannel != null) {
try {
sessionLockChannel.close();
@@ -161,4 +168,13 @@ public final class WorldManagePage extends DecoratorAnimatedPage implements Deco
sessionLockChannel = null;
}
}
public void launch() {
fireEvent(new PageCloseEvent());
Versions.launchAndEnterWorld(profile, id, world.getFileName());
}
public void generateLaunchScript() {
Versions.generateLaunchScriptForQuickEnterWorld(profile, id, world.getFileName());
}
}
+9 -1
View File
@@ -265,6 +265,10 @@
-fx-spacing: 0;
}
.no-padding .advanced-list-box-content {
-fx-padding: 0 0 0 0;
}
.iconed-item {
-jfx-rippler-fill: -monet-secondary-container;
}
@@ -346,7 +350,7 @@
-fx-font-size: 15px;
}
.terracotta-license Text {
.terracotta-hint Text {
-fx-fill: -monet-on-surface;
}
@@ -1005,6 +1009,10 @@
-fx-text-fill: -monet-on-surface;
}
.options-list-item .label.subtitle {
-fx-text-fill: -monet-on-surface-variant;
}
.options-list-item.no-padding {
-fx-padding: 0;
}
@@ -197,7 +197,15 @@ button.select_all=Select All
button.view=View
button.yes=Yes
chat=Join Group Chat
contact=Feedback
contact.chat=Join Group Chat
contact.chat.discord=Discord
contact.chat.discord.statement=Welcome to join our Discord server.
contact.chat.qq_group=HMCL User QQ Group
contact.chat.qq_group.statement=Welcome to join our user QQ group.
contact.feedback=Feedback Channel
contact.feedback.github=GitHub Issues
contact.feedback.github.statement=Submit an issue on GitHub.
color.recent=Recommended
color.custom=Custom Color
@@ -376,6 +384,7 @@ extension.mod=Mod File
extension.png=Image File
extension.ps1=Windows PowerShell Script
extension.sh=Shell Script
extension.command=macOS Shell Script
fatal.create_hmcl_current_directory_failure=Hello Minecraft! Launcher cannot create the HMCL directory (%s). Please move HMCL to another location and reopen it.
fatal.javafx.incompatible=Missing JavaFX environment.\n\
@@ -432,15 +441,6 @@ fatal.unsupported_platform.windows_arm64=Hello Minecraft! Launcher has provided
If you are using the <b>Qualcomm</b> platform, you may need to install the <a href="ms-windows-store://pdp/?productid=9NQPSL29BFFF">OpenGL Compatibility Pack</a> before playing games.\n\
Click the link to navigate to the Microsoft Store and install the compatibility pack.
feedback=Feedback
feedback.channel=Feedback Channel
feedback.discord=Discord
feedback.discord.statement=Welcome to join our Discord server.
feedback.github=GitHub Issues
feedback.github.statement=Submit an issue on GitHub.
feedback.qq_group=HMCL User QQ Group
feedback.qq_group.statement=Welcome to join our user QQ group.
file=File
folder.config=Configs
@@ -1538,7 +1538,7 @@ update.latest=This is the latest version
update.no_browser=Cannot open in system browser. But we copied the link to your clipboard, and you can open it manually.
update.tooltip=Update
update.preview=Preview HMCL releases early
update.preview.tooltip=Enable this option to receive new versions of HMCL early for testing before their official release.
update.preview.subtitle=Enable this option to receive new versions of HMCL early for testing before their official release.
version=Games
version.name=Instance Name
@@ -1559,6 +1559,7 @@ version.game.support_status.unsupported=Unsupported
version.game.support_status.untested=Untested
version.game.type=Type
version.launch=Launch Game
version.launch_and_enter_world=Play World
version.launch.empty=Start Game
version.launch.empty.installing=Installing Game
version.launch.empty.tooltip=Install and launch the latest official release
@@ -197,7 +197,15 @@ button.select_all=تحديد الكل
button.view=عرض
button.yes=نعم
chat=الانضمام إلى دردشة جماعية
contact=الملاحظات
contact.chat=الانضمام إلى دردشة جماعية
contact.chat.discord=Discord
contact.chat.discord.statement=مرحباً بك للانضمام إلى خادم Discord الخاص بنا.
contact.chat.qq_group=مجموعة QQ لمستخدمي HMCL
contact.chat.qq_group.statement=مرحباً بك للانضمام إلى مجموعة QQ الخاصة بمستخدمينا.
contact.feedback=قناة الملاحظات
contact.feedback.github=GitHub Issues
contact.feedback.github.statement=إرسال مشكلة على GitHub.
color.recent=موصى به
color.custom=لون مخصص
@@ -432,15 +440,6 @@ fatal.unsupported_platform.windows_arm64=Hello Minecraft! Launcher وفر دعم
إذا كنت تستخدم منصة <b>Qualcomm</b>، فقد تحتاج إلى تثبيت <a href="ms-windows-store://pdp/?productid=9NQPSL29BFFF">حزمة توافق OpenGL</a> قبل لعب الألعاب.\n\
انقر على الرابط للانتقال إلى متجر Microsoft وتثبيت حزمة التوافق.
feedback=الملاحظات
feedback.channel=قناة الملاحظات
feedback.discord=Discord
feedback.discord.statement=مرحباً بك للانضمام إلى خادم Discord الخاص بنا.
feedback.github=GitHub Issues
feedback.github.statement=إرسال مشكلة على GitHub.
feedback.qq_group=مجموعة QQ لمستخدمي HMCL
feedback.qq_group.statement=مرحباً بك للانضمام إلى مجموعة QQ الخاصة بمستخدمينا.
file=ملف
folder.config=التكوينات
@@ -1526,7 +1525,7 @@ update.latest=هذا هو أحدث إصدار
update.no_browser=لا يمكن الفتح في متصفح النظام. ولكننا نسخنا الرابط إلى الحافظة الخاصة بك، ويمكنك فتحه يدويًا.
update.tooltip=تحديث
update.preview=معاينة إصدارات HMCL مبكرًا
update.preview.tooltip=قم بتفعيل هذا الخيار لاستلام إصدارات جديدة من HMCL مبكرًا للاختبار قبل إصدارها الرسمي.
update.preview.subtitle=قم بتفعيل هذا الخيار لاستلام إصدارات جديدة من HMCL مبكرًا للاختبار قبل إصدارها الرسمي.
version=الألعاب
version.name=اسم النسخة
@@ -198,7 +198,15 @@ button.select_all=Seleccionar todo
button.view=Vista
button.yes=Sí
chat=Chat de grupo
contact=Comentarios
contact.chat=Chat de grupo
contact.chat.discord=Discord
contact.chat.discord.statement=¡Únase a nuestro servidor Discord!
contact.chat.qq_group=Grupo QQ de usuarios de HMCL
contact.chat.qq_group.statement=¡Únase a nuestro grupo QQ de usuarios!
contact.feedback=Canal de comentarios
contact.feedback.github=GitHub Issues
contact.feedback.github.statement=Envíe una propuesta en GitHub.
color.recent=Recomendado
color.custom=Color personalizado
@@ -433,15 +441,6 @@ fatal.unsupported_platform.windows_arm64=Hello Minecraft! Launcher ha proporcion
Si utilizas la plataforma <b>Qualcomm</b>, es posible que tengas que instalar el <a href="ms-windows-store://pdp/?productid=9NQPSL29BFFF">paquete de compatibilidad OpenGL</a> antes de jugar.\n\
Haz clic en el enlace para ir a Microsoft Store e instalar el paquete de compatibilidad.
feedback=Comentarios
feedback.channel=Canal de comentarios
feedback.discord=Discord
feedback.discord.statement=¡Únase a nuestro servidor Discord!
feedback.github=GitHub Issues
feedback.github.statement=Envíe una propuesta en GitHub.
feedback.qq_group=Grupo QQ de usuarios de HMCL
feedback.qq_group.statement=¡Únase a nuestro grupo QQ de usuarios!
file=Archivo
folder.config=Configuraciones de mod
@@ -1447,7 +1446,7 @@ update.latest=Esta es la última versión.
update.no_browser=No se puede abrir en el navegador del sistema. Pero, hemos copiado el enlace a su portapapeles y puede abrirlo manualmente.
update.tooltip=Actualización
update.preview=Vista previa de actualizaciones anticipadas
update.preview.tooltip=Activa esta opción para recibir nuevas versiones del lanzador antes de su lanzamiento oficial para probarlas
update.preview.subtitle=Activa esta opción para recibir nuevas versiones del lanzador antes de su lanzamiento oficial para probarlas
version=Juegos
version.name=Nombre de instancia
@@ -157,7 +157,15 @@ button.select_all=すべて選択
button.view=読む
button.yes=はい
chat=グループチャット
contact=フィードバック
contact.chat=グループチャット
contact.chat.discord=Discord
contact.chat.discord.statement=Discordサーバーに参加してください!
contact.chat.qq_group=HMCLユーザーQQグループ
contact.chat.qq_group.statement=ユーザーQQグループに参加してください!
contact.feedback=フィードバックチャンネル
contact.feedback.github=GitHub Issues
contact.feedback.github.statement=GitHubで問題を送信します。
color.recent=推奨
color.custom=カスタムカラー
@@ -326,16 +334,6 @@ fatal.apply_update_failure=ごめんなさい、Hello Minecraft! Launcher 何か
fatal.samba=If you are trying to run HMCL in a shared folder by Samba, HMCL may not working, please try updating your Java or running HMCL in a local folder.
fatal.illegal_char=ユーザーフォルダーのパスに不正な文字'='が含まれています, ログインアカウントやオフラインログインではスキンの変更ができなくなり。
feedback=フィードバック
feedback.channel=フィードバックチャンネル
feedback.discord=Discord
feedback.discord.statement=Discordサーバーに参加してください!
feedback.github=GitHub Issues
feedback.github.statement=GitHubで問題を送信します。
feedback.qq_group=HMCLユーザーQQグループ
feedback.qq_group.statement=ユーザーQQグループに参加してください!
file=ファイル
folder.config=Config
@@ -206,11 +206,19 @@ button.select_all=悉擇之
button.view=覽
button.yes=然
chat=會集
color.recent=薦
color.custom=自定色
contact=建言
contact.chat=會集
contact.chat.discord=齟齬
contact.chat.discord.statement=恭迎至齟齬伺服器,且循論議之規
contact.chat.qq_group=HMCL 群組
contact.chat.qq_group.statement=恭迎至 HMCL 群組,且循論議之規
contact.feedback=建言之徑
contact.feedback.github=Github 議題
contact.feedback.github.statement=舉一 Github 議題
crash.NoClassDefFound=宜驗 HMCL 之案全否,抑更迭爪哇。\n君可求助於 https://docs.hmcl.net/help.html。
crash.user_fault=君之械網與爪哇或有謬,是以崩。宜驗爪哇與算機。\n君可求助於 https://docs.hmcl.net/help.html。
@@ -401,15 +409,6 @@ fatal.unsupported_platform.loongarch=HMCL 既適龍芯。\n凡有謬,遽求助
fatal.unsupported_platform.macos_arm64=HMCL 既適蘋矽。宜啟以 ARM 之爪哇,以益君之戲事。\n誠有謬,宜啟以 x86-64 之爪哇,以益其兼。\n凡有謬,遽求助於右上之鈕。
fatal.unsupported_platform.windows_arm64=HMCL 既適 ARM 之視窗。誠有謬,宜啟以 x86 之爪哇。\n誠用<b>栝柑</b>,或須置<a href="ms-windows-store://pdp/?productid=9NQPSL29BFFF">開圖庫兼囊</a>,而後可戯。擊鏈以置之於微軟貨舍。\n君可求助於右上之鈕。
feedback=建言
feedback.channel=建言之徑
feedback.discord=齟齬
feedback.discord.statement=恭迎至齟齬伺服器,且循論議之規
feedback.github=Github 議題
feedback.github.statement=舉一 Github 議題
feedback.qq_group=HMCL 群組
feedback.qq_group.statement=恭迎至 HMCL 群組,且循論議之規
file=案
folder.config=置設案夾
@@ -197,7 +197,15 @@ button.select_all=Выбрать все
button.view=Просмотреть
button.yes=Да
chat=Групповой чат
contact=Обратная связь
contact.chat=Групповой чат
contact.chat.discord=Discord
contact.chat.discord.statement=Добро пожаловать в наш Discord.
contact.chat.qq_group=Группа QQ пользователя HMCL
contact.chat.qq_group.statement=Добро пожаловать в нашу группу QQ.
contact.feedback=Канал обратной связи
contact.feedback.github=Проблемы GitHub
contact.feedback.github.statement=Отправить проблему на GitHub.
color.recent=Рекомендуемые
color.custom=Пользовательский цвет
@@ -429,15 +437,6 @@ fatal.unsupported_platform.windows_arm64=Лаунчер обеспечил на
Если вы используете платформу <b>Qualcomm</b>, вам может потребоваться установить <a href="ms-windows-store://pdp/?productid=9NQPSL29BFFF">пакет совместимости OpenGL</a> перед началом игры.\n\
Щелкните ссылку, чтобы перейти в Microsoft Store и установить пакет совместимости.
feedback=Обратная связь
feedback.channel=Канал обратной связи
feedback.discord=Discord
feedback.discord.statement=Добро пожаловать в наш Discord.
feedback.github=Проблемы GitHub
feedback.github.statement=Отправить проблему на GitHub.
feedback.qq_group=Группа QQ пользователя HMCL
feedback.qq_group.statement=Добро пожаловать в нашу группу QQ.
file=Файл
folder.config=Конфигурация мод
@@ -194,7 +194,15 @@ button.select_all=Вибрати все
button.view=Переглянути
button.yes=Так
chat=Приєднатися до групового чату
contact=Зворотний зв'язок
contact.chat=Приєднатися до групового чату
contact.chat.discord=Discord
contact.chat.discord.statement=Ласкаво просимо приєднатися до нашого сервера Discord.
contact.chat.qq_group=Група користувачів QQ HMCL
contact.chat.qq_group.statement=Ласкаво просимо приєднатися до нашої групи користувачів QQ.
contact.feedback=Канал зворотного зв'язку
contact.feedback.github=GitHub Issues
contact.feedback.github.statement=Надіслати проблему на GitHub.
color.recent=Рекомендовані
color.custom=Власний колір
@@ -411,15 +419,6 @@ fatal.unsupported_platform.windows_arm64=Hello Minecraft! Лаунчер над
Якщо ви використовуєте платформу <b>Qualcomm</b>, вам може знадобитися встановити <a href="ms-windows-store://pdp/?productid=9NQPSL29BFFF">Пакет сумісності OpenGL</a> перед грою.\n\
Натисніть посилання, щоб перейти до Microsoft Store та встановити пакет сумісності.
feedback=Зворотний зв'язок
feedback.channel=Канал зворотного зв'язку
feedback.discord=Discord
feedback.discord.statement=Ласкаво просимо приєднатися до нашого сервера Discord.
feedback.github=GitHub Issues
feedback.github.statement=Надіслати проблему на GitHub.
feedback.qq_group=Група користувачів QQ HMCL
feedback.qq_group.statement=Ласкаво просимо приєднатися до нашої групи користувачів QQ.
file=Файл
folder.config=Конфігурації
@@ -200,7 +200,15 @@ button.select_all=全選
button.view=查看
button.yes=是
chat=官方群組
contact=回報
contact.chat=官方群組
contact.chat.discord=Discord 伺服器
contact.chat.discord.statement=歡迎加入 Discord 伺服器,加入後請遵守討論區規則
contact.chat.qq_group=使用者 QQ 群組
contact.chat.qq_group.statement=歡迎加入 HMCL 使用者 QQ 群組,加入後請遵守群組規則
contact.feedback=回報管道
contact.feedback.github=GitHub Issues
contact.feedback.github.statement=提交一個 GitHub Issue
color.recent=建議
color.custom=自訂顏色
@@ -374,6 +382,7 @@ extension.mod=模組檔案
extension.png=圖片檔案
extension.ps1=PowerShell 指令碼
extension.sh=Bash 指令碼
extension.command=macOS Shell 指令碼
fatal.create_hmcl_current_directory_failure=Hello Minecraft! Launcher 無法建立 HMCL 資料夾 (%s),請將 HMCL 移動至其他位置再開啟。
fatal.javafx.incompatible=缺少 JavaFX 執行環境。\nHMCL 無法在低於 Java 11 的 Java 環境上自行補全 JavaFX 執行環境。請更新到 Java 11 或更高版本。
@@ -398,15 +407,6 @@ fatal.unsupported_platform.loongarch=Hello Minecraft! Launcher 已為龍芯提
fatal.unsupported_platform.macos_arm64=Hello Minecraft! Launcher 已為 Apple Silicon 平臺提供支援。使用 ARM 原生 Java 啟動遊戲以獲得更流暢的遊戲體驗。\n如果你在遊戲中遭遇問題,使用 x86-64 架構的 Java 啟動遊戲可能有更好的相容性。
fatal.unsupported_platform.windows_arm64=Hello Minecraft! Launcher 已為 Windows on Arm 平臺提供原生支援。如果你在遊戲中遭遇問題,請嘗試使用 x86 架構的 Java 啟動遊戲。\n\n如果你正在使用<b>高通</b>平臺,你可能需要安裝 <a href="ms-windows-store://pdp/?productid=9NQPSL29BFFF">OpenGL 相容包</a>後才能進行遊戲。點擊連結前往 Microsoft Store 安裝相容包。
feedback=回報
feedback.channel=回報管道
feedback.discord=Discord 伺服器
feedback.discord.statement=歡迎加入 Discord 伺服器,加入後請遵守討論區規則
feedback.github=GitHub Issues
feedback.github.statement=提交一個 GitHub Issue
feedback.qq_group=HMCL 使用者 QQ 群組
feedback.qq_group.statement=歡迎加入 HMCL 使用者 QQ 群組,加入後請遵守群組規則
file=檔案
folder.config=模組設定目錄
@@ -1322,7 +1322,7 @@ update.latest=目前版本為最新版本
update.no_browser=無法開啟瀏覽器。網址已經複製到剪貼簿了,你可以手動複製網址開啟頁面。
update.tooltip=更新
update.preview=提前測試 HMCL 預覽版本
update.preview.tooltip=啟用此選項,你將可以提前取得 HMCL 的新版本,以便在正式發布前進行測試。
update.preview.subtitle=啟用此選項,你將可以提前取得 HMCL 的新版本,以便在正式發布前進行測試。
version=遊戲
version.name=遊戲實例名稱
@@ -1343,6 +1343,7 @@ version.game.support_status.unsupported=不支援
version.game.support_status.untested=未經測試
version.game.type=版本類型
version.launch=啟動遊戲
version.launch_and_enter_world=進入世界
version.launch.empty=開始遊戲
version.launch.empty.installing=安裝遊戲
version.launch.empty.tooltip=安裝並啟動最新正式版遊戲
@@ -208,11 +208,19 @@ button.select_all=全选
button.view=查看
button.yes=是
chat=官方群组
color.recent=推荐
color.custom=自定义颜色
contact=反馈
contact.feedback=提交反馈
contact.feedback.github=GitHub Issue
contact.feedback.github.statement=提交一个 GitHub Issue
contact.chat=官方群组
contact.chat.qq_group=用户 QQ 群
contact.chat.qq_group.statement=欢迎加入 HMCL 官方 QQ 群,加入后请遵守群规
contact.chat.discord=Discord
contact.chat.discord.statement=欢迎加入 Discord 服务器,加入后请遵守讨论区规定
crash.NoClassDefFound=请确认 Hello Minecraft! Launcher 本体是否完整,或更新你的 Java。\n你可以访问 https://docs.hmcl.net/help.html 页面寻求帮助。
crash.user_fault=你的系统或 Java 环境可能安装不当导致本软件崩溃,请检查你的 Java 环境或你的电脑。\n你可以访问 https://docs.hmcl.net/help.html 页面寻求帮助。
@@ -383,6 +391,7 @@ extension.mod=模组文件
extension.png=图片文件
extension.ps1=PowerShell 脚本
extension.sh=Bash 脚本
extension.command=macOS Shell 脚本
fatal.create_hmcl_current_directory_failure=Hello Minecraft! Launcher 无法创建 HMCL 文件夹 (%s),请将 HMCL 移动至其他位置再启动。\n如遇到问题,你可以访问 https://docs.hmcl.net/help.html 页面寻求帮助。
fatal.javafx.incompatible=缺少 JavaFX 运行环境。\nHello Minecraft! Launcher 无法在低于 Java 11 的 Java 环境上自行补全 JavaFX 运行环境。请更新到 Java 11 或更高版本。\n你可以访问 https://docs.hmcl.net/help.html 页面寻求帮助。
@@ -407,15 +416,6 @@ fatal.unsupported_platform.loongarch=Hello Minecraft! Launcher 已为龙芯提
fatal.unsupported_platform.macos_arm64=Hello Minecraft! Launcher 已为 Apple Silicon 平台提供支持。使用 ARM 原生 Java 启动游戏以获得更流畅的游戏体验。\n如果你在游戏中遇到问题,使用 x86-64 架构的 Java 启动游戏可能有更好的兼容性。\n如遇到问题,你可以点击右上角帮助按钮进行求助。
fatal.unsupported_platform.windows_arm64=Hello Minecraft! Launcher 已为 Windows on Arm 平台提供原生支持。如果你在游戏中遇到问题,请尝试使用 x86 架构的 Java 启动游戏。\n如果你正在使用 <b>高通</b> 平台,你可能需要安装 <a href="ms-windows-store://pdp/?productid=9NQPSL29BFFF">OpenGL 兼容包</a> 后才能进行游戏。点击链接前往 Microsoft Store 安装兼容包。\n如遇到问题,你可以点击右上角帮助按钮进行求助。
feedback=反馈
feedback.channel=反馈渠道
feedback.discord=Discord
feedback.discord.statement=欢迎加入 Discord 服务器,加入后请遵守讨论区规定
feedback.github=GitHub Issue
feedback.github.statement=提交一个 GitHub Issue
feedback.qq_group=HMCL 用户群
feedback.qq_group.statement=欢迎加入 HMCL 用户群,加入后请遵守群规
file=文件
folder.config=配置文件夹
@@ -1332,7 +1332,7 @@ update.latest=当前版本为最新版本
update.no_browser=无法打开浏览器。网址已经复制到剪贴板,你可以手动粘贴网址打开页面。
update.tooltip=更新
update.preview=提前预览 HMCL 版本
update.preview.tooltip=启用此选项,你将可以提前获取 HMCL 的新版本,以便在正式发布前进行测试。
update.preview.subtitle=启用此选项,你将可以提前获取 HMCL 的新版本,以便在正式发布前进行测试。
version=游戏
version.name=游戏实例名称
@@ -1353,6 +1353,7 @@ version.game.support_status.unsupported=不支持
version.game.support_status.untested=未经测试
version.game.type=版本类型
version.launch=启动游戏
version.launch_and_enter_world=进入世界
version.launch.empty=开始游戏
version.launch.empty.installing=安装游戏
version.launch.empty.tooltip=安装并启动最新正式版游戏
@@ -47,7 +47,7 @@ public class LaunchOptions implements Serializable {
private Integer width;
private Integer height;
private boolean fullscreen;
private String serverIp;
private QuickPlayOption quickPlayOption;
private String wrapper;
private Proxy.Type proxyType;
private String proxyHost;
@@ -181,11 +181,11 @@ public class LaunchOptions implements Serializable {
return fullscreen;
}
/**
* The server ip that will connect to when enter game main menu.
*/
public String getServerIp() {
return serverIp;
/// The quick play option.
///
/// @see <a href="https://minecraft.wiki/w/Quick_Play">Quick Play - Minecraft Wiki</a>
public QuickPlayOption getQuickPlayOption() {
return quickPlayOption;
}
/**
@@ -412,8 +412,8 @@ public class LaunchOptions implements Serializable {
return this;
}
public Builder setServerIp(String serverIp) {
options.serverIp = serverIp;
public Builder setQuickPlayOption(QuickPlayOption quickPlayOption) {
options.quickPlayOption = quickPlayOption;
return this;
}
@@ -0,0 +1,32 @@
/*
* 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.game;
/// The quick play option.
///
/// @see <a href="https://minecraft.wiki/w/Quick_Play">Quick Play - Minecraft Wiki</a>
public sealed interface QuickPlayOption {
record SinglePlayer(String worldFolderName) implements QuickPlayOption {
}
record MultiPlayer(String serverIP) implements QuickPlayOption {
}
record Realm(String realmID) implements QuickPlayOption {
}
}
@@ -304,23 +304,31 @@ public class DefaultLauncher extends Launcher {
if (argumentsFromAuthInfo != null && argumentsFromAuthInfo.getGame() != null && !argumentsFromAuthInfo.getGame().isEmpty())
res.addAll(Arguments.parseArguments(argumentsFromAuthInfo.getGame(), configuration, features));
if (StringUtils.isNotBlank(options.getServerIp())) {
String address = options.getServerIp();
if (options.getQuickPlayOption() instanceof QuickPlayOption.MultiPlayer multiPlayer) {
String address = multiPlayer.serverIP();
try {
ServerAddress parsed = ServerAddress.parse(address);
if (GameVersionNumber.asGameVersion(gameVersion).compareTo("1.20") < 0) {
if (GameVersionNumber.asGameVersion(gameVersion).isAtLeast("1.20", "23w14a")) {
res.add("--quickPlayMultiplayer");
res.add(parsed.getPort() >= 0 ? address : parsed.getHost() + ":25565");
} else {
res.add("--server");
res.add(parsed.getHost());
res.add("--port");
res.add(parsed.getPort() >= 0 ? String.valueOf(parsed.getPort()) : "25565");
} else {
res.add("--quickPlayMultiplayer");
res.add(parsed.getPort() < 0 ? address + ":25565" : address);
}
} catch (IllegalArgumentException e) {
LOG.warning("Invalid server address: " + address, e);
}
} else if (options.getQuickPlayOption() instanceof QuickPlayOption.SinglePlayer singlePlayer
&& GameVersionNumber.asGameVersion(gameVersion).isAtLeast("1.20", "23w14a")) {
res.add("--quickPlaySingleplayer");
res.add(singlePlayer.worldFolderName());
} else if (options.getQuickPlayOption() instanceof QuickPlayOption.Realm realm
&& GameVersionNumber.asGameVersion(gameVersion).isAtLeast("1.20", "23w14a")) {
res.add("--quickPlayRealms");
res.add(realm.realmID());
}
if (options.isFullscreen())
@@ -619,8 +627,8 @@ public class DefaultLauncher extends Launcher {
if (!usePowerShell) {
if (isWindows && !scriptExtension.equals("bat"))
throw new IllegalArgumentException("The extension of " + scriptFile + " is not 'bat' or 'ps1' in Windows");
else if (!isWindows && !scriptExtension.equals("sh"))
throw new IllegalArgumentException("The extension of " + scriptFile + " is not 'sh' or 'ps1' in macOS/Linux");
else if (!isWindows && !(scriptExtension.equals("sh") || scriptExtension.equals("command")))
throw new IllegalArgumentException("The extension of " + scriptFile + " is not 'sh', 'ps1' or 'command' in macOS/Linux");
}
final Command commandLine = generateCommandLine(nativeFolder);