From 6ac2274c29ea1b5e14801a1c74e8cb353112d13e Mon Sep 17 00:00:00 2001 From: huanghongxun Date: Sun, 19 Sep 2021 00:42:51 +0800 Subject: [PATCH] fix(multiplayer): exchange "from" and "to". --- .../org/jackhuang/hmcl/ui/main/RootPage.java | 2 +- .../ui/multiplayer/MultiplayerManager.java | 43 ++++++++++++------- .../hmcl/ui/multiplayer/MultiplayerPage.java | 22 ++++++++-- .../ui/multiplayer/MultiplayerPageSkin.java | 18 ++++---- .../resources/assets/lang/I18N.properties | 3 ++ .../resources/assets/lang/I18N_zh.properties | 5 ++- .../assets/lang/I18N_zh_CN.properties | 5 ++- 7 files changed, 66 insertions(+), 32 deletions(-) diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/main/RootPage.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/main/RootPage.java index c0e17e2f2..90a64b732 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/main/RootPage.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/main/RootPage.java @@ -192,7 +192,7 @@ public class RootPage extends DecoratorTabPage { .add(gameItem) .add(downloadItem) .startCategory(i18n("settings.launcher.general").toLowerCase()) -// .add(multiplayerItem) + .add(multiplayerItem) .add(launcherSettingsItem); // the root page, with the sidebar in left, navigator in center. diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/multiplayer/MultiplayerManager.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/multiplayer/MultiplayerManager.java index 45a5ff31b..f81c07c9b 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/multiplayer/MultiplayerManager.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/multiplayer/MultiplayerManager.java @@ -28,7 +28,10 @@ import org.jackhuang.hmcl.task.Task; import org.jackhuang.hmcl.util.Lang; import org.jackhuang.hmcl.util.gson.JsonUtils; import org.jackhuang.hmcl.util.io.NetworkUtils; +import org.jackhuang.hmcl.util.platform.Architecture; +import org.jackhuang.hmcl.util.platform.CommandBuilder; import org.jackhuang.hmcl.util.platform.ManagedProcess; +import org.jackhuang.hmcl.util.platform.OperatingSystem; import org.jetbrains.annotations.Nullable; import java.io.IOException; @@ -36,19 +39,22 @@ import java.net.ServerSocket; import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Path; -import java.util.Arrays; -import java.util.Base64; -import java.util.List; +import java.nio.file.attribute.PosixFilePermission; +import java.util.*; import java.util.regex.Matcher; import java.util.regex.Pattern; +import static org.jackhuang.hmcl.util.Logging.LOG; + /** * Cato Management. */ public final class MultiplayerManager { - private static final String CATO_DOWNLOAD_URL = "https://hmcl.huangyuhui.net/maven/"; + private static final String CATO_DOWNLOAD_URL = "https://files.huangyuhui.net/maven/"; private static final String CATO_VERSION = "2021-09-01"; - private static final Artifact CATO_ARTIFACT = new Artifact("cato", "cato", CATO_VERSION); + private static final Artifact CATO_ARTIFACT = new Artifact("cato", "cato", CATO_VERSION, + OperatingSystem.CURRENT_OS.getCheckedName() + "-" + Architecture.CURRENT.name().toLowerCase(Locale.ROOT), + OperatingSystem.CURRENT_OS == OperatingSystem.WINDOWS ? "exe" : null); private MultiplayerManager() { } @@ -57,11 +63,17 @@ public final class MultiplayerManager { return new FileDownloadTask( NetworkUtils.toURL(CATO_DOWNLOAD_URL + CATO_ARTIFACT.getPath()), getCatoExecutable().toFile() - ); + ).thenRunAsync(() -> { + if (OperatingSystem.CURRENT_OS == OperatingSystem.LINUX || OperatingSystem.CURRENT_OS == OperatingSystem.OSX) { + Set perm = Files.getPosixFilePermissions(getCatoExecutable()); + perm.add(PosixFilePermission.OWNER_EXECUTE); + Files.setPosixFilePermissions(getCatoExecutable(), perm); + } + }); } public static Path getCatoExecutable() { - return CATO_ARTIFACT.getPath(Metadata.HMCL_DIRECTORY); + return CATO_ARTIFACT.getPath(Metadata.HMCL_DIRECTORY.resolve("libraries")); } public static CatoSession joinSession(String sessionName, String peer, int remotePort, int localPort) throws IOException { @@ -69,14 +81,12 @@ public final class MultiplayerManager { if (!Files.isRegularFile(exe)) { throw new IllegalStateException("Cato file not found"); } - String[] commands = new String[]{exe.toString(), "--token", "new", "--id", peer, "--from", String.format("127.0.0.1:%d", remotePort), "--to", String.format("127.0.0.1:%d", localPort)}; + String[] commands = new String[]{exe.toString(), "--token", "new", "--peer", peer, "--from", String.format("127.0.0.1:%d", localPort), "--to", String.format("127.0.0.1:%d", remotePort)}; Process process = new ProcessBuilder() .command(commands) - .inheritIO() .start(); - CatoSession catoSession = new CatoSession(sessionName, process, Arrays.asList(commands)); - return catoSession; + return new CatoSession(sessionName, process, Arrays.asList(commands)); } public static CatoSession createSession(String sessionName) throws IOException { @@ -87,11 +97,9 @@ public final class MultiplayerManager { String[] commands = new String[]{exe.toString(), "--token", "new"}; Process process = new ProcessBuilder() .command(commands) - .inheritIO() .start(); - CatoSession catoSession = new CatoSession(sessionName, process, Arrays.asList(commands)); - return catoSession; + return new CatoSession(sessionName, process, Arrays.asList(commands)); } public static Invitation parseInvitationCode(String invitationCode) throws JsonParseException { @@ -115,13 +123,16 @@ public final class MultiplayerManager { CatoSession(String name, Process process, List commands) { super(process, commands); + LOG.info("Started cato with command: " + new CommandBuilder().addAll(commands).toString()); + this.name = name; addRelatedThread(Lang.thread(this::waitFor, "CatoExitWaiter", true)); - addRelatedThread(Lang.thread(new StreamPump(process.getInputStream(), it -> { + addRelatedThread(Lang.thread(new StreamPump(process.getErrorStream(), it -> { if (id == null) { + LOG.info("Cato: " + it); Matcher matcher = TEMP_TOKEN_PATTERN.matcher(it); if (matcher.find()) { - id = matcher.group("id"); + id = "mix" + matcher.group("id"); onIdGenerated.fireEvent(new CatoIdEvent(this, id)); } } diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/multiplayer/MultiplayerPage.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/multiplayer/MultiplayerPage.java index 4734d0d35..1cdaa367e 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/multiplayer/MultiplayerPage.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/multiplayer/MultiplayerPage.java @@ -22,6 +22,7 @@ import de.javawi.jstun.test.DiscoveryTest; import javafx.beans.property.*; import javafx.scene.control.Control; import javafx.scene.control.Skin; +import org.jackhuang.hmcl.setting.DownloadProviders; import org.jackhuang.hmcl.task.Schedulers; import org.jackhuang.hmcl.task.Task; import org.jackhuang.hmcl.task.TaskExecutor; @@ -33,15 +34,18 @@ import org.jackhuang.hmcl.ui.construct.PromptDialogPane; import org.jackhuang.hmcl.ui.construct.RequiredValidator; import org.jackhuang.hmcl.ui.decorator.DecoratorPage; +import java.util.concurrent.CancellationException; import java.util.function.Consumer; +import java.util.logging.Level; import static org.jackhuang.hmcl.ui.FXUtils.runInFX; +import static org.jackhuang.hmcl.util.Logging.LOG; import static org.jackhuang.hmcl.util.i18n.I18n.i18n; public class MultiplayerPage extends Control implements DecoratorPage { private final ReadOnlyObjectWrapper state = new ReadOnlyObjectWrapper<>(State.fromTitle(i18n("multiplayer"), -1)); - private final ObjectProperty multiplayerState = new SimpleObjectProperty<>(); + private final ObjectProperty multiplayerState = new SimpleObjectProperty<>(MultiplayerManager.State.DISCONNECTED); private final ReadOnlyObjectWrapper natState = new ReadOnlyObjectWrapper<>(); private final ReadOnlyIntegerWrapper port = new ReadOnlyIntegerWrapper(-1); private final ReadOnlyObjectWrapper session = new ReadOnlyObjectWrapper<>(); @@ -111,10 +115,20 @@ public class MultiplayerPage extends Control implements DecoratorPage { if (!MultiplayerManager.getCatoExecutable().toFile().exists()) { setDisabled(true); TaskExecutor executor = MultiplayerManager.downloadCato() - .thenRunAsync(Schedulers.javafx(), () -> { + .whenComplete(Schedulers.javafx(), exception -> { setDisabled(false); - }).executor(false); + if (exception != null) { + if (exception instanceof CancellationException) { + Controllers.showToast(i18n("message.cancelled")); + } else { + Controllers.dialog(DownloadProviders.localizeErrorMessage(exception), i18n("install.failed.downloading"), MessageDialogPane.MessageType.ERROR); + } + } else { + Controllers.showToast(i18n("multiplayer.download.success")); + } + }).executor(); Controllers.taskDialog(executor, i18n("multiplayer.download")); + executor.start(); } else { setDisabled(false); } @@ -137,6 +151,7 @@ public class MultiplayerPage extends Control implements DecoratorPage { try { initCatoSession(MultiplayerManager.createSession(((PromptDialogPane.Builder.StringQuestion) result.get(1)).getValue())); } catch (Exception e) { + LOG.log(Level.WARNING, "Failed to create session", e); reject.accept(i18n("multiplayer.session.create.error")); return; } @@ -161,6 +176,7 @@ public class MultiplayerPage extends Control implements DecoratorPage { try { invitation = MultiplayerManager.parseInvitationCode(invitationCode); } catch (Exception e) { + LOG.log(Level.WARNING, "Failed to join session", e); reject.accept(i18n("multiplayer.session.join.invitation_code.error")); return; } diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/multiplayer/MultiplayerPageSkin.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/multiplayer/MultiplayerPageSkin.java index 202439d7b..5c8ca6976 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/multiplayer/MultiplayerPageSkin.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/multiplayer/MultiplayerPageSkin.java @@ -45,7 +45,6 @@ public class MultiplayerPageSkin extends SkinBase { super(control); BorderPane root = new BorderPane(); - root.setPadding(new Insets(10)); getChildren().setAll(root); { VBox roomPane = new VBox(); @@ -79,19 +78,17 @@ public class MultiplayerPageSkin extends SkinBase { if (state == MultiplayerManager.State.DISCONNECTED) { roomPane.getChildren().setAll(createRoomItem, joinRoomItem); } else if (state == MultiplayerManager.State.MASTER) { - roomPane.getChildren().setAll(copyLinkItem); - roomPane.getChildren().setAll(closeRoomItem); + roomPane.getChildren().setAll(copyLinkItem, closeRoomItem); } else if (state == MultiplayerManager.State.SLAVE) { - roomPane.getChildren().setAll(copyLinkItem); - roomPane.getChildren().setAll(quitItem); + roomPane.getChildren().setAll(copyLinkItem, quitItem); } }); } AdvancedListBox sideBar = new AdvancedListBox() - .startCategory("multiplayer.session") + .startCategory(i18n("multiplayer.session")) .add(roomPane) - .startCategory("help") + .startCategory(i18n("help")) .addNavigationDrawerItem(settingsItem -> { settingsItem.setTitle(i18n("help")); settingsItem.setLeftGraphic(wrap(SVG::gamepad)); @@ -103,6 +100,7 @@ public class MultiplayerPageSkin extends SkinBase { { VBox content = new VBox(16); + content.setPadding(new Insets(10)); content.setFillWidth(true); ScrollPane scrollPane = new ScrollPane(content); scrollPane.setFitToWidth(true); @@ -127,7 +125,7 @@ public class MultiplayerPageSkin extends SkinBase { { Label label = new Label(i18n("multiplayer.state.master")); label.textProperty().bind(Bindings.createStringBinding(() -> - i18n("multiplayer.state.master", control.getSession().getName(), control.getPort()), + i18n("multiplayer.state.master", control.getSession() == null ? "" : control.getSession().getName(), control.getPort()), control.portProperty(), control.sessionProperty())); masterPane.getChildren().setAll(label); } @@ -136,7 +134,7 @@ public class MultiplayerPageSkin extends SkinBase { { Label label = new Label(); label.textProperty().bind(Bindings.createStringBinding(() -> - i18n("multiplayer.state.slave", control.getSession().getName()), + i18n("multiplayer.state.slave", control.getSession() == null ? "" : control.getSession().getName()), control.sessionProperty())); slavePane.getChildren().setAll(label); } @@ -182,7 +180,7 @@ public class MultiplayerPageSkin extends SkinBase { } content.getChildren().setAll( - ComponentList.createComponentListTitle(i18n("multiplayer.room")), + ComponentList.createComponentListTitle(i18n("multiplayer.session")), roomPane, ComponentList.createComponentListTitle(i18n("multiplayer.nat")), natDetectionPane diff --git a/HMCL/src/main/resources/assets/lang/I18N.properties b/HMCL/src/main/resources/assets/lang/I18N.properties index d0964ac36..9e07ef53e 100644 --- a/HMCL/src/main/resources/assets/lang/I18N.properties +++ b/HMCL/src/main/resources/assets/lang/I18N.properties @@ -539,6 +539,9 @@ mods.url=Official Page multiplayer=Multiplayer multiplayer.download=Downloading dependencies for multiplayer +multiplayer.download.success=Dependencies initialization succeeded +multiplayer.download.failed=Failed to initialize multiplayer, some files cannot be downloaded +multiplayer.hint=Multiplayer functionality is experimental. Please give feedback. multiplayer.nat=Network Type Detection multiplayer.nat.hint=Network type detection will make it clear whether your network fulfills our requirement for multiplayer mode. multiplayer.nat.latency=Latency diff --git a/HMCL/src/main/resources/assets/lang/I18N_zh.properties b/HMCL/src/main/resources/assets/lang/I18N_zh.properties index 70dbf5603..37ee6dd11 100644 --- a/HMCL/src/main/resources/assets/lang/I18N_zh.properties +++ b/HMCL/src/main/resources/assets/lang/I18N_zh.properties @@ -537,8 +537,11 @@ mods.name=名稱 mods.not_modded=你需要先在自動安裝頁面安裝 Fabric、Forge 或 LiteLoader 才能進行模組管理。 mods.url=官方頁面 -multiplayer=聯機 +multiplayer=多人聯機 multiplayer.download=正在下載相依元件 +multiplayer.download.success=多人聯機初始化完成 +multiplayer.download.failed=初始化失敗,部分文件未能完成下載 +multiplayer.hint=多人聯機功能處於實驗階段,如果有問題請回饋。 multiplayer.nat=網路檢測 multiplayer.nat.hint=執行網路檢測可以讓你更清楚裡的網路狀況是否符合聯機功能的需求。不符合聯機功能運行條件的網路狀況將可能導致聯機失敗。 multiplayer.nat.latency=延遲 diff --git a/HMCL/src/main/resources/assets/lang/I18N_zh_CN.properties b/HMCL/src/main/resources/assets/lang/I18N_zh_CN.properties index e493450e9..0d772b97b 100644 --- a/HMCL/src/main/resources/assets/lang/I18N_zh_CN.properties +++ b/HMCL/src/main/resources/assets/lang/I18N_zh_CN.properties @@ -537,8 +537,11 @@ mods.name=名称 mods.not_modded=你需要先在自动安装页面安装 Fabric、Forge 或 LiteLoader 才能进行模组管理。 mods.url=官方页面 -multiplayer=联机 +multiplayer=多人联机 multiplayer.download=正在下载依赖 +multiplayer.download.success=多人联机初始化完成 +multiplayer.download.failed=初始化失败,部分文件未能完成下载 +multiplayer.hint=多人联机功能处于实验阶段,如果有问题请反馈。 multiplayer.nat=网络检测 multiplayer.nat.hint=执行网络检测可以让你更清楚里的网络状况是否符合联机功能的需求。不符合联机功能运行条件的网络状况将可能导致联机失败。 multiplayer.nat.latency=延迟