fix(multiplayer): exchange "from" and "to".

This commit is contained in:
huanghongxun 2021-09-19 00:42:51 +08:00
parent ea9f547ada
commit 6ac2274c29
7 changed files with 66 additions and 32 deletions

View File

@ -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.

View File

@ -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<PosixFilePermission> 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<String> 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));
}
}

View File

@ -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> state = new ReadOnlyObjectWrapper<>(State.fromTitle(i18n("multiplayer"), -1));
private final ObjectProperty<MultiplayerManager.State> multiplayerState = new SimpleObjectProperty<>();
private final ObjectProperty<MultiplayerManager.State> multiplayerState = new SimpleObjectProperty<>(MultiplayerManager.State.DISCONNECTED);
private final ReadOnlyObjectWrapper<DiscoveryInfo> natState = new ReadOnlyObjectWrapper<>();
private final ReadOnlyIntegerWrapper port = new ReadOnlyIntegerWrapper(-1);
private final ReadOnlyObjectWrapper<MultiplayerManager.CatoSession> 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;
}

View File

@ -45,7 +45,6 @@ public class MultiplayerPageSkin extends SkinBase<MultiplayerPage> {
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<MultiplayerPage> {
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<MultiplayerPage> {
{
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<MultiplayerPage> {
{
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<MultiplayerPage> {
{
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<MultiplayerPage> {
}
content.getChildren().setAll(
ComponentList.createComponentListTitle(i18n("multiplayer.room")),
ComponentList.createComponentListTitle(i18n("multiplayer.session")),
roomPane,
ComponentList.createComponentListTitle(i18n("multiplayer.nat")),
natDetectionPane

View File

@ -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

View File

@ -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=延遲

View File

@ -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=延迟