@@ -25,20 +25,20 @@ import org.jackhuang.hmcl.Metadata;
|
|||||||
import org.jackhuang.hmcl.download.LibraryAnalyzer;
|
import org.jackhuang.hmcl.download.LibraryAnalyzer;
|
||||||
import org.jackhuang.hmcl.event.Event;
|
import org.jackhuang.hmcl.event.Event;
|
||||||
import org.jackhuang.hmcl.event.EventManager;
|
import org.jackhuang.hmcl.event.EventManager;
|
||||||
|
import org.jackhuang.hmcl.java.JavaRuntime;
|
||||||
import org.jackhuang.hmcl.mod.ModAdviser;
|
import org.jackhuang.hmcl.mod.ModAdviser;
|
||||||
import org.jackhuang.hmcl.mod.Modpack;
|
import org.jackhuang.hmcl.mod.Modpack;
|
||||||
import org.jackhuang.hmcl.mod.ModpackConfiguration;
|
import org.jackhuang.hmcl.mod.ModpackConfiguration;
|
||||||
import org.jackhuang.hmcl.mod.ModpackProvider;
|
import org.jackhuang.hmcl.mod.ModpackProvider;
|
||||||
import org.jackhuang.hmcl.setting.Profile;
|
import org.jackhuang.hmcl.setting.Profile;
|
||||||
import org.jackhuang.hmcl.util.FileSaver;
|
|
||||||
import org.jackhuang.hmcl.setting.VersionIconType;
|
import org.jackhuang.hmcl.setting.VersionIconType;
|
||||||
import org.jackhuang.hmcl.setting.VersionSetting;
|
import org.jackhuang.hmcl.setting.VersionSetting;
|
||||||
import org.jackhuang.hmcl.ui.FXUtils;
|
import org.jackhuang.hmcl.ui.FXUtils;
|
||||||
|
import org.jackhuang.hmcl.util.FileSaver;
|
||||||
import org.jackhuang.hmcl.util.Lang;
|
import org.jackhuang.hmcl.util.Lang;
|
||||||
import org.jackhuang.hmcl.util.StringUtils;
|
import org.jackhuang.hmcl.util.StringUtils;
|
||||||
import org.jackhuang.hmcl.util.gson.JsonUtils;
|
import org.jackhuang.hmcl.util.gson.JsonUtils;
|
||||||
import org.jackhuang.hmcl.util.io.FileUtils;
|
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.OperatingSystem;
|
||||||
import org.jackhuang.hmcl.util.platform.SystemInfo;
|
import org.jackhuang.hmcl.util.platform.SystemInfo;
|
||||||
import org.jackhuang.hmcl.util.versioning.GameVersionNumber;
|
import org.jackhuang.hmcl.util.versioning.GameVersionNumber;
|
||||||
@@ -55,8 +55,8 @@ import java.util.stream.Collectors;
|
|||||||
import java.util.stream.Stream;
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
import static org.jackhuang.hmcl.setting.ConfigHolder.config;
|
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.Pair.pair;
|
||||||
|
import static org.jackhuang.hmcl.util.logging.Logger.LOG;
|
||||||
|
|
||||||
public final class HMCLGameRepository extends DefaultGameRepository {
|
public final class HMCLGameRepository extends DefaultGameRepository {
|
||||||
private final Profile profile;
|
private final Profile profile;
|
||||||
@@ -418,7 +418,6 @@ public final class HMCLGameRepository extends DefaultGameRepository {
|
|||||||
.setWidth(vs.getWidth())
|
.setWidth(vs.getWidth())
|
||||||
.setHeight(vs.getHeight())
|
.setHeight(vs.getHeight())
|
||||||
.setFullscreen(vs.isFullscreen())
|
.setFullscreen(vs.isFullscreen())
|
||||||
.setServerIp(vs.getServerIp())
|
|
||||||
.setWrapper(vs.getWrapper())
|
.setWrapper(vs.getWrapper())
|
||||||
.setPreLaunchCommand(vs.getPreLaunchCommand())
|
.setPreLaunchCommand(vs.getPreLaunchCommand())
|
||||||
.setPostExitCommand(vs.getPostExitCommand())
|
.setPostExitCommand(vs.getPostExitCommand())
|
||||||
@@ -435,6 +434,10 @@ public final class HMCLGameRepository extends DefaultGameRepository {
|
|||||||
.setJavaAgents(javaAgents)
|
.setJavaAgents(javaAgents)
|
||||||
.setJavaArguments(javaArguments);
|
.setJavaArguments(javaArguments);
|
||||||
|
|
||||||
|
if (StringUtils.isNotBlank(vs.getServerIp())) {
|
||||||
|
builder.setQuickPlayOption(new QuickPlayOption.MultiPlayer(vs.getServerIp()));
|
||||||
|
}
|
||||||
|
|
||||||
if (config().hasProxy()) {
|
if (config().hasProxy()) {
|
||||||
builder.setProxyType(config().getProxyType());
|
builder.setProxyType(config().getProxyType());
|
||||||
builder.setProxyHost(config().getProxyHost());
|
builder.setProxyHost(config().getProxyHost());
|
||||||
|
|||||||
@@ -52,6 +52,7 @@ import org.jackhuang.hmcl.util.versioning.VersionNumber;
|
|||||||
|
|
||||||
import java.io.FileNotFoundException;
|
import java.io.FileNotFoundException;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.lang.ref.WeakReference;
|
||||||
import java.net.SocketTimeoutException;
|
import java.net.SocketTimeoutException;
|
||||||
import java.net.URI;
|
import java.net.URI;
|
||||||
import java.nio.file.AccessDeniedException;
|
import java.nio.file.AccessDeniedException;
|
||||||
@@ -61,7 +62,6 @@ import java.util.concurrent.*;
|
|||||||
import java.util.concurrent.atomic.AtomicReference;
|
import java.util.concurrent.atomic.AtomicReference;
|
||||||
import java.util.concurrent.locks.ReentrantLock;
|
import java.util.concurrent.locks.ReentrantLock;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
import java.lang.ref.WeakReference;
|
|
||||||
|
|
||||||
import static javafx.application.Platform.runLater;
|
import static javafx.application.Platform.runLater;
|
||||||
import static javafx.application.Platform.setImplicitExit;
|
import static javafx.application.Platform.setImplicitExit;
|
||||||
@@ -82,6 +82,7 @@ public final class LauncherHelper {
|
|||||||
private final VersionSetting setting;
|
private final VersionSetting setting;
|
||||||
private LauncherVisibility launcherVisibility;
|
private LauncherVisibility launcherVisibility;
|
||||||
private boolean showLogs;
|
private boolean showLogs;
|
||||||
|
private QuickPlayOption quickPlayOption;
|
||||||
private boolean disableOfflineSkin = false;
|
private boolean disableOfflineSkin = false;
|
||||||
|
|
||||||
public LauncherHelper(Profile profile, Account account, String selectedVersion) {
|
public LauncherHelper(Profile profile, Account account, String selectedVersion) {
|
||||||
@@ -113,6 +114,10 @@ public final class LauncherHelper {
|
|||||||
launcherVisibility = LauncherVisibility.KEEP;
|
launcherVisibility = LauncherVisibility.KEEP;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setQuickPlayOption(QuickPlayOption quickPlayOption) {
|
||||||
|
this.quickPlayOption = quickPlayOption;
|
||||||
|
}
|
||||||
|
|
||||||
public void setDisableOfflineSkin() {
|
public void setDisableOfflineSkin() {
|
||||||
disableOfflineSkin = true;
|
disableOfflineSkin = true;
|
||||||
}
|
}
|
||||||
@@ -199,6 +204,9 @@ public final class LauncherHelper {
|
|||||||
if (disableOfflineSkin) {
|
if (disableOfflineSkin) {
|
||||||
launchOptionsBuilder.setDaemon(false);
|
launchOptionsBuilder.setDaemon(false);
|
||||||
}
|
}
|
||||||
|
if (quickPlayOption != null) {
|
||||||
|
launchOptionsBuilder.setQuickPlayOption(quickPlayOption);
|
||||||
|
}
|
||||||
LaunchOptions launchOptions = launchOptionsBuilder.create();
|
LaunchOptions launchOptions = launchOptionsBuilder.create();
|
||||||
|
|
||||||
LOG.info("Here's the structure of game mod directory:\n" + FileUtils.printFileStructure(repository.getModsDirectory(selectedVersion), 10));
|
LOG.info("Here's the structure of game mod directory:\n" + FileUtils.printFileStructure(repository.getModsDirectory(selectedVersion), 10));
|
||||||
|
|||||||
@@ -373,9 +373,8 @@ public class DecoratorController {
|
|||||||
if (navigator.getCurrentPage() instanceof DecoratorPage) {
|
if (navigator.getCurrentPage() instanceof DecoratorPage) {
|
||||||
DecoratorPage page = (DecoratorPage) navigator.getCurrentPage();
|
DecoratorPage page = (DecoratorPage) navigator.getCurrentPage();
|
||||||
|
|
||||||
// FIXME: Get WorldPage working first, and revisit this later
|
|
||||||
page.closePage();
|
|
||||||
if (page.isPageCloseable()) {
|
if (page.isPageCloseable()) {
|
||||||
|
page.closePage();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -346,7 +346,7 @@ public final class MainPage extends StackPane implements DecoratorPage {
|
|||||||
|
|
||||||
private void launch() {
|
private void launch() {
|
||||||
Profile profile = Profiles.getSelectedProfile();
|
Profile profile = Profiles.getSelectedProfile();
|
||||||
Versions.launch(profile, profile.getSelectedVersion(), null);
|
Versions.launch(profile, profile.getSelectedVersion());
|
||||||
}
|
}
|
||||||
|
|
||||||
private void launchNoGame() {
|
private void launchNoGame() {
|
||||||
|
|||||||
@@ -85,10 +85,14 @@ public class GameListItem extends Control {
|
|||||||
Versions.openFolder(profile, version);
|
Versions.openFolder(profile, version);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void launch() {
|
public void testGame() {
|
||||||
Versions.testGame(profile, version);
|
Versions.testGame(profile, version);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void launch() {
|
||||||
|
Versions.launch(profile, version);
|
||||||
|
}
|
||||||
|
|
||||||
public void modifyGameSettings() {
|
public void modifyGameSettings() {
|
||||||
Versions.modifyGameSettings(profile, version);
|
Versions.modifyGameSettings(profile, version);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -44,7 +44,7 @@ public class GameListItemSkin extends SkinBase<GameListItem> {
|
|||||||
JFXPopup popup = new JFXPopup(menu);
|
JFXPopup popup = new JFXPopup(menu);
|
||||||
|
|
||||||
menu.getContent().setAll(
|
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 IconedMenuItem(SVG.SCRIPT, i18n("version.launch_script"), () -> currentSkinnable.generateLaunchScript(), popup),
|
||||||
new MenuSeparator(),
|
new MenuSeparator(),
|
||||||
new IconedMenuItem(SVG.SETTINGS, i18n("version.manage.manage"), () -> currentSkinnable.modifyGameSettings(), popup),
|
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();
|
JFXButton btnLaunch = new JFXButton();
|
||||||
btnLaunch.setOnAction(e -> skinnable.launch());
|
btnLaunch.setOnAction(e -> skinnable.testGame());
|
||||||
btnLaunch.getStyleClass().add("toggle-icon4");
|
btnLaunch.getStyleClass().add("toggle-icon4");
|
||||||
BorderPane.setAlignment(btnLaunch, Pos.CENTER);
|
BorderPane.setAlignment(btnLaunch, Pos.CENTER);
|
||||||
btnLaunch.setGraphic(FXUtils.limitingSize(SVG.ROCKET_LAUNCH.createIcon(24), 24, 24));
|
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.Account;
|
||||||
import org.jackhuang.hmcl.auth.authlibinjector.AuthlibInjectorAccount;
|
import org.jackhuang.hmcl.auth.authlibinjector.AuthlibInjectorAccount;
|
||||||
import org.jackhuang.hmcl.download.game.GameAssetDownloadTask;
|
import org.jackhuang.hmcl.download.game.GameAssetDownloadTask;
|
||||||
import org.jackhuang.hmcl.game.GameDirectoryType;
|
import org.jackhuang.hmcl.game.*;
|
||||||
import org.jackhuang.hmcl.game.GameRepository;
|
|
||||||
import org.jackhuang.hmcl.game.HMCLGameRepository;
|
|
||||||
import org.jackhuang.hmcl.game.LauncherHelper;
|
|
||||||
import org.jackhuang.hmcl.mod.RemoteMod;
|
import org.jackhuang.hmcl.mod.RemoteMod;
|
||||||
import org.jackhuang.hmcl.setting.*;
|
import org.jackhuang.hmcl.setting.*;
|
||||||
import org.jackhuang.hmcl.task.*;
|
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))
|
if (!checkVersionForLaunching(profile, id))
|
||||||
return;
|
return;
|
||||||
ensureSelectedAccount(account -> {
|
ensureSelectedAccount(account -> {
|
||||||
@@ -219,18 +217,25 @@ public final class Versions {
|
|||||||
: new FileChooser.ExtensionFilter(i18n("extension.sh"), "*.sh"));
|
: new FileChooser.ExtensionFilter(i18n("extension.sh"), "*.sh"));
|
||||||
chooser.getExtensionFilters().add(new FileChooser.ExtensionFilter(i18n("extension.ps1"), "*.ps1"));
|
chooser.getExtensionFilters().add(new FileChooser.ExtensionFilter(i18n("extension.ps1"), "*.ps1"));
|
||||||
Path file = FileUtils.toPath(chooser.showSaveDialog(Controllers.getStage()));
|
Path file = FileUtils.toPath(chooser.showSaveDialog(Controllers.getStage()));
|
||||||
if (file != null)
|
if (file != null) {
|
||||||
new LauncherHelper(profile, account, id).makeLaunchScript(file);
|
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))
|
if (!checkVersionForLaunching(profile, id))
|
||||||
return;
|
return;
|
||||||
ensureSelectedAccount(account -> {
|
ensureSelectedAccount(account -> {
|
||||||
LauncherHelper launcherHelper = new LauncherHelper(profile, account, id);
|
LauncherHelper launcherHelper = new LauncherHelper(profile, account, id);
|
||||||
if (injecter != null)
|
for (Consumer<LauncherHelper> injecter : injecters) {
|
||||||
injecter.accept(launcherHelper);
|
injecter.accept(launcherHelper);
|
||||||
|
}
|
||||||
launcherHelper.launch();
|
launcherHelper.launch();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -239,6 +244,16 @@ public final class Versions {
|
|||||||
launch(profile, id, LauncherHelper::setTestMode);
|
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) {
|
private static boolean checkVersionForLaunching(Profile profile, String id) {
|
||||||
if (id == null || !profile.getRepository().isLoaded() || !profile.getRepository().hasVersion(id)) {
|
if (id == null || !profile.getRepository().isLoaded() || !profile.getRepository().hasVersion(id)) {
|
||||||
JFXButton gotoDownload = new JFXButton(i18n("version.empty.launch.goto_download"));
|
JFXButton gotoDownload = new JFXButton(i18n("version.empty.launch.goto_download"));
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ import javafx.scene.control.Skin;
|
|||||||
import javafx.stage.FileChooser;
|
import javafx.stage.FileChooser;
|
||||||
import org.jackhuang.hmcl.game.World;
|
import org.jackhuang.hmcl.game.World;
|
||||||
import org.jackhuang.hmcl.game.WorldLockedException;
|
import org.jackhuang.hmcl.game.WorldLockedException;
|
||||||
|
import org.jackhuang.hmcl.setting.Profile;
|
||||||
import org.jackhuang.hmcl.task.Schedulers;
|
import org.jackhuang.hmcl.task.Schedulers;
|
||||||
import org.jackhuang.hmcl.task.Task;
|
import org.jackhuang.hmcl.task.Task;
|
||||||
import org.jackhuang.hmcl.ui.Controllers;
|
import org.jackhuang.hmcl.ui.Controllers;
|
||||||
@@ -39,11 +40,15 @@ public final class WorldListItem extends Control {
|
|||||||
private final World world;
|
private final World world;
|
||||||
private final Path backupsDir;
|
private final Path backupsDir;
|
||||||
private final WorldListPage parent;
|
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.world = world;
|
||||||
this.backupsDir = backupsDir;
|
this.backupsDir = backupsDir;
|
||||||
this.parent = parent;
|
this.parent = parent;
|
||||||
|
this.profile = profile;
|
||||||
|
this.id = id;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -91,6 +96,14 @@ public final class WorldListItem extends Control {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void showManagePage() {
|
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();
|
WorldListItem item = getSkinnable();
|
||||||
World world = item.getWorld();
|
World world = item.getWorld();
|
||||||
|
|
||||||
|
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(
|
popupMenu.getContent().addAll(
|
||||||
new IconedMenuItem(SVG.SETTINGS, i18n("world.manage.button"), item::showManagePage, popup));
|
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)) {
|
if (ChunkBaseApp.isSupported(world)) {
|
||||||
popupMenu.getContent().addAll(
|
popupMenu.getContent().addAll(
|
||||||
|
|||||||
@@ -61,7 +61,7 @@ public final class WorldListPage extends ListPageBase<WorldListItem> implements
|
|||||||
if (worlds != null)
|
if (worlds != null)
|
||||||
itemsProperty().setAll(worlds.stream()
|
itemsProperty().setAll(worlds.stream()
|
||||||
.filter(world -> isShowAll() || world.getGameVersion() == null || world.getGameVersion().equals(gameVersion))
|
.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) {
|
if (exception == null) {
|
||||||
itemsProperty().setAll(result.stream()
|
itemsProperty().setAll(result.stream()
|
||||||
.filter(world -> isShowAll() || world.getGameVersion() == null || world.getGameVersion().equals(gameVersion))
|
.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()));
|
.collect(Collectors.toList()));
|
||||||
} else {
|
} else {
|
||||||
LOG.warning("Failed to load world list page", exception);
|
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) -> {
|
Controllers.prompt(i18n("world.name.enter"), (name, resolve, reject) -> {
|
||||||
Task.runAsync(() -> world.install(savesDir, name))
|
Task.runAsync(() -> world.install(savesDir, name))
|
||||||
.whenComplete(Schedulers.javafx(), () -> {
|
.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();
|
resolve.run();
|
||||||
}, e -> {
|
}, e -> {
|
||||||
if (e instanceof FileAlreadyExistsException)
|
if (e instanceof FileAlreadyExistsException)
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ import javafx.scene.layout.BorderPane;
|
|||||||
import javafx.scene.layout.Priority;
|
import javafx.scene.layout.Priority;
|
||||||
import javafx.scene.layout.VBox;
|
import javafx.scene.layout.VBox;
|
||||||
import org.jackhuang.hmcl.game.World;
|
import org.jackhuang.hmcl.game.World;
|
||||||
|
import org.jackhuang.hmcl.setting.Profile;
|
||||||
import org.jackhuang.hmcl.ui.FXUtils;
|
import org.jackhuang.hmcl.ui.FXUtils;
|
||||||
import org.jackhuang.hmcl.ui.SVG;
|
import org.jackhuang.hmcl.ui.SVG;
|
||||||
import org.jackhuang.hmcl.ui.animation.TransitionPane;
|
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 ObjectProperty<State> state;
|
||||||
private final World world;
|
private final World world;
|
||||||
private final Path backupsDir;
|
private final Path backupsDir;
|
||||||
|
private final Profile profile;
|
||||||
|
private final String id;
|
||||||
|
|
||||||
private final TabHeader header;
|
private final TabHeader header;
|
||||||
private final TabHeader.Tab<WorldInfoPage> worldInfoTab = new TabHeader.Tab<>("worldInfoPage");
|
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;
|
private FileChannel sessionLockChannel;
|
||||||
|
|
||||||
public WorldManagePage(World world, Path backupsDir) {
|
public WorldManagePage(World world, Path backupsDir, Profile profile, String id) {
|
||||||
this.world = world;
|
this.world = world;
|
||||||
this.backupsDir = backupsDir;
|
this.backupsDir = backupsDir;
|
||||||
|
|
||||||
|
this.profile = profile;
|
||||||
|
this.id = id;
|
||||||
|
|
||||||
this.worldInfoTab.setNodeSupplier(() -> new WorldInfoPage(this));
|
this.worldInfoTab.setNodeSupplier(() -> new WorldInfoPage(this));
|
||||||
this.worldBackupsTab.setNodeSupplier(() -> new WorldBackupsPage(this));
|
this.worldBackupsTab.setNodeSupplier(() -> new WorldBackupsPage(this));
|
||||||
this.datapackTab.setNodeSupplier(() -> new DatapackListPage(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);
|
this.header = new TabHeader(transitionPane, worldInfoTab, worldBackupsTab);
|
||||||
header.select(worldInfoTab);
|
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);
|
setCenter(transitionPane);
|
||||||
|
|
||||||
BorderPane left = new BorderPane();
|
BorderPane left = new BorderPane();
|
||||||
@@ -91,6 +104,12 @@ public final class WorldManagePage extends DecoratorAnimatedPage implements Deco
|
|||||||
left.setTop(sideBar);
|
left.setTop(sideBar);
|
||||||
|
|
||||||
AdvancedListBox toolbar = new AdvancedListBox();
|
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)) {
|
if (ChunkBaseApp.isSupported(world)) {
|
||||||
PopupMenu popupMenu = new PopupMenu();
|
PopupMenu popupMenu = new PopupMenu();
|
||||||
JFXPopup popup = new JFXPopup(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));
|
BorderPane.setMargin(toolbar, new Insets(0, 0, 12, 0));
|
||||||
left.setBottom(toolbar);
|
left.setBottom(toolbar);
|
||||||
|
|
||||||
// Does it need to be done in the background?
|
this.addEventHandler(Navigator.NavigationEvent.EXITED, this::onExited);
|
||||||
try {
|
|
||||||
sessionLockChannel = world.lock();
|
|
||||||
LOG.info("Acquired lock on world " + world.getFileName());
|
|
||||||
} catch (IOException ignored) {
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -142,14 +156,7 @@ public final class WorldManagePage extends DecoratorAnimatedPage implements Deco
|
|||||||
return sessionLockChannel == null;
|
return sessionLockChannel == null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
public void onExited(Navigator.NavigationEvent event) {
|
||||||
public boolean back() {
|
|
||||||
closePage();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void closePage() {
|
|
||||||
if (sessionLockChannel != null) {
|
if (sessionLockChannel != null) {
|
||||||
try {
|
try {
|
||||||
sessionLockChannel.close();
|
sessionLockChannel.close();
|
||||||
@@ -161,4 +168,13 @@ public final class WorldManagePage extends DecoratorAnimatedPage implements Deco
|
|||||||
sessionLockChannel = null;
|
sessionLockChannel = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void launch() {
|
||||||
|
fireEvent(new PageCloseEvent());
|
||||||
|
Versions.launchAndEnterWorld(profile, id, world.getFileName());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void generateLaunchScript() {
|
||||||
|
Versions.generateLaunchScriptForQuickEnterWorld(profile, id, world.getFileName());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1560,6 +1560,7 @@ version.game.support_status.unsupported=Unsupported
|
|||||||
version.game.support_status.untested=Untested
|
version.game.support_status.untested=Untested
|
||||||
version.game.type=Type
|
version.game.type=Type
|
||||||
version.launch=Launch Game
|
version.launch=Launch Game
|
||||||
|
version.launch_and_enter_world=Play World
|
||||||
version.launch.empty=Start Game
|
version.launch.empty=Start Game
|
||||||
version.launch.empty.installing=Installing Game
|
version.launch.empty.installing=Installing Game
|
||||||
version.launch.empty.tooltip=Install and launch the latest official release
|
version.launch.empty.tooltip=Install and launch the latest official release
|
||||||
|
|||||||
@@ -1344,6 +1344,7 @@ version.game.support_status.unsupported=不支援
|
|||||||
version.game.support_status.untested=未經測試
|
version.game.support_status.untested=未經測試
|
||||||
version.game.type=版本類型
|
version.game.type=版本類型
|
||||||
version.launch=啟動遊戲
|
version.launch=啟動遊戲
|
||||||
|
version.launch_and_enter_world=進入世界
|
||||||
version.launch.empty=開始遊戲
|
version.launch.empty=開始遊戲
|
||||||
version.launch.empty.installing=安裝遊戲
|
version.launch.empty.installing=安裝遊戲
|
||||||
version.launch.empty.tooltip=安裝並啟動最新正式版遊戲
|
version.launch.empty.tooltip=安裝並啟動最新正式版遊戲
|
||||||
|
|||||||
@@ -1354,6 +1354,7 @@ version.game.support_status.unsupported=不支持
|
|||||||
version.game.support_status.untested=未经测试
|
version.game.support_status.untested=未经测试
|
||||||
version.game.type=版本类型
|
version.game.type=版本类型
|
||||||
version.launch=启动游戏
|
version.launch=启动游戏
|
||||||
|
version.launch_and_enter_world=进入世界
|
||||||
version.launch.empty=开始游戏
|
version.launch.empty=开始游戏
|
||||||
version.launch.empty.installing=安装游戏
|
version.launch.empty.installing=安装游戏
|
||||||
version.launch.empty.tooltip=安装并启动最新正式版游戏
|
version.launch.empty.tooltip=安装并启动最新正式版游戏
|
||||||
|
|||||||
@@ -47,7 +47,7 @@ public class LaunchOptions implements Serializable {
|
|||||||
private Integer width;
|
private Integer width;
|
||||||
private Integer height;
|
private Integer height;
|
||||||
private boolean fullscreen;
|
private boolean fullscreen;
|
||||||
private String serverIp;
|
private QuickPlayOption quickPlayOption;
|
||||||
private String wrapper;
|
private String wrapper;
|
||||||
private Proxy.Type proxyType;
|
private Proxy.Type proxyType;
|
||||||
private String proxyHost;
|
private String proxyHost;
|
||||||
@@ -181,11 +181,11 @@ public class LaunchOptions implements Serializable {
|
|||||||
return fullscreen;
|
return fullscreen;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/// The quick play option.
|
||||||
* The server ip that will connect to when enter game main menu.
|
///
|
||||||
*/
|
/// @see <a href="https://minecraft.wiki/w/Quick_Play">Quick Play - Minecraft Wiki</a>
|
||||||
public String getServerIp() {
|
public QuickPlayOption getQuickPlayOption() {
|
||||||
return serverIp;
|
return quickPlayOption;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -412,8 +412,8 @@ public class LaunchOptions implements Serializable {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Builder setServerIp(String serverIp) {
|
public Builder setQuickPlayOption(QuickPlayOption quickPlayOption) {
|
||||||
options.serverIp = serverIp;
|
options.quickPlayOption = quickPlayOption;
|
||||||
return this;
|
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())
|
if (argumentsFromAuthInfo != null && argumentsFromAuthInfo.getGame() != null && !argumentsFromAuthInfo.getGame().isEmpty())
|
||||||
res.addAll(Arguments.parseArguments(argumentsFromAuthInfo.getGame(), configuration, features));
|
res.addAll(Arguments.parseArguments(argumentsFromAuthInfo.getGame(), configuration, features));
|
||||||
|
|
||||||
if (StringUtils.isNotBlank(options.getServerIp())) {
|
if (options.getQuickPlayOption() instanceof QuickPlayOption.MultiPlayer multiPlayer) {
|
||||||
String address = options.getServerIp();
|
String address = multiPlayer.serverIP();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
ServerAddress parsed = ServerAddress.parse(address);
|
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("--server");
|
||||||
res.add(parsed.getHost());
|
res.add(parsed.getHost());
|
||||||
res.add("--port");
|
res.add("--port");
|
||||||
res.add(parsed.getPort() >= 0 ? String.valueOf(parsed.getPort()) : "25565");
|
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) {
|
} catch (IllegalArgumentException e) {
|
||||||
LOG.warning("Invalid server address: " + address, 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())
|
if (options.isFullscreen())
|
||||||
|
|||||||
Reference in New Issue
Block a user