feat(multiplayer): check cato version before establishing connection.
This commit is contained in:
@@ -0,0 +1,25 @@
|
|||||||
|
/*
|
||||||
|
* Hello Minecraft! Launcher
|
||||||
|
* Copyright (C) 2021 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.ui.multiplayer;
|
||||||
|
|
||||||
|
public class MultiplayerClient {
|
||||||
|
|
||||||
|
public MultiplayerClient() {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -51,7 +51,7 @@ import static org.jackhuang.hmcl.util.Logging.LOG;
|
|||||||
*/
|
*/
|
||||||
public final class MultiplayerManager {
|
public final class MultiplayerManager {
|
||||||
private static final String CATO_DOWNLOAD_URL = "https://files.huangyuhui.net/maven/";
|
private static final String CATO_DOWNLOAD_URL = "https://files.huangyuhui.net/maven/";
|
||||||
private static final String CATO_VERSION = "2021-09-20";
|
private static final String CATO_VERSION = "2021-09-21";
|
||||||
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.getCheckedName() + "-" + Architecture.CURRENT.name().toLowerCase(Locale.ROOT),
|
||||||
OperatingSystem.CURRENT_OS == OperatingSystem.WINDOWS ? "exe" : null);
|
OperatingSystem.CURRENT_OS == OperatingSystem.WINDOWS ? "exe" : null);
|
||||||
@@ -76,7 +76,11 @@ public final class MultiplayerManager {
|
|||||||
return CATO_ARTIFACT.getPath(Metadata.HMCL_DIRECTORY.resolve("libraries"));
|
return CATO_ARTIFACT.getPath(Metadata.HMCL_DIRECTORY.resolve("libraries"));
|
||||||
}
|
}
|
||||||
|
|
||||||
public static CatoSession joinSession(String sessionName, String peer, int remotePort, int localPort) throws IOException {
|
public static CatoSession joinSession(String version, String sessionName, String peer, int remotePort, int localPort) throws IOException, IncompatibleCatoVersionException {
|
||||||
|
if (!CATO_VERSION.equals(version)) {
|
||||||
|
throw new IncompatibleCatoVersionException(version, CATO_VERSION);
|
||||||
|
}
|
||||||
|
|
||||||
Path exe = getCatoExecutable();
|
Path exe = getCatoExecutable();
|
||||||
if (!Files.isRegularFile(exe)) {
|
if (!Files.isRegularFile(exe)) {
|
||||||
throw new IllegalStateException("Cato file not found");
|
throw new IllegalStateException("Cato file not found");
|
||||||
@@ -96,6 +100,10 @@ public final class MultiplayerManager {
|
|||||||
if (!Files.isRegularFile(exe)) {
|
if (!Files.isRegularFile(exe)) {
|
||||||
throw new IllegalStateException("Cato file not found");
|
throw new IllegalStateException("Cato file not found");
|
||||||
}
|
}
|
||||||
|
//
|
||||||
|
// MultiplayerServer server = new MultiplayerServer(port);
|
||||||
|
// server.start();
|
||||||
|
|
||||||
String[] commands = new String[]{exe.toString(), "--token", "new", "--allows", String.format("127.0.0.1:%d", port)};
|
String[] commands = new String[]{exe.toString(), "--token", "new", "--allows", String.format("127.0.0.1:%d", port)};
|
||||||
Process process = new ProcessBuilder()
|
Process process = new ProcessBuilder()
|
||||||
.command(commands)
|
.command(commands)
|
||||||
@@ -181,11 +189,11 @@ public final class MultiplayerManager {
|
|||||||
return id;
|
return id;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String generateInvitationCode(int port) {
|
public String generateInvitationCode(int gamePort, int serverPort) {
|
||||||
if (id == null) {
|
if (id == null) {
|
||||||
throw new IllegalStateException("id not generated");
|
throw new IllegalStateException("id not generated");
|
||||||
}
|
}
|
||||||
String json = JsonUtils.GSON.toJson(new Invitation(id, name, port));
|
String json = JsonUtils.GSON.toJson(new Invitation(CATO_VERSION, id, name, gamePort, serverPort));
|
||||||
return new String(Base64.getEncoder().encode(json.getBytes(StandardCharsets.UTF_8)), StandardCharsets.UTF_8);
|
return new String(Base64.getEncoder().encode(json.getBytes(StandardCharsets.UTF_8)), StandardCharsets.UTF_8);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -203,6 +211,7 @@ public final class MultiplayerManager {
|
|||||||
|
|
||||||
private static final Pattern TEMP_TOKEN_PATTERN = Pattern.compile("id\\(mix(?<id>\\w+)\\)");
|
private static final Pattern TEMP_TOKEN_PATTERN = Pattern.compile("id\\(mix(?<id>\\w+)\\)");
|
||||||
private static final Pattern PEER_CONNECTED_PATTERN = Pattern.compile("Peer connected");
|
private static final Pattern PEER_CONNECTED_PATTERN = Pattern.compile("Peer connected");
|
||||||
|
private static final Pattern LOG_PATTERN = Pattern.compile("(\\[\\d+])\\s+(\\w+)\\s+(\\w+-{0,1}\\w+):\\s(.*)");
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class CatoExitEvent extends Event {
|
public static class CatoExitEvent extends Event {
|
||||||
@@ -242,14 +251,22 @@ public final class MultiplayerManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static class Invitation {
|
public static class Invitation {
|
||||||
|
private final String version;
|
||||||
private final String id;
|
private final String id;
|
||||||
private final String sessionName;
|
private final String sessionName;
|
||||||
private final int port;
|
private final int gamePort;
|
||||||
|
private final int channelPort;
|
||||||
|
|
||||||
public Invitation(String id, String sessionName, int port) {
|
public Invitation(String version, String id, String sessionName, int gamePort, int channelPort) {
|
||||||
|
this.version = version;
|
||||||
this.id = id;
|
this.id = id;
|
||||||
this.sessionName = sessionName;
|
this.sessionName = sessionName;
|
||||||
this.port = port;
|
this.gamePort = gamePort;
|
||||||
|
this.channelPort = channelPort;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getVersion() {
|
||||||
|
return version;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getId() {
|
public String getId() {
|
||||||
@@ -260,8 +277,30 @@ public final class MultiplayerManager {
|
|||||||
return sessionName;
|
return sessionName;
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getPort() {
|
public int getGamePort() {
|
||||||
return port;
|
return gamePort;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getChannelPort() {
|
||||||
|
return channelPort;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class IncompatibleCatoVersionException extends Exception {
|
||||||
|
private final String expected;
|
||||||
|
private final String actual;
|
||||||
|
|
||||||
|
public IncompatibleCatoVersionException(String expected, String actual) {
|
||||||
|
this.expected = expected;
|
||||||
|
this.actual = actual;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getExpected() {
|
||||||
|
return expected;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getActual() {
|
||||||
|
return actual;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -151,7 +151,7 @@ public class MultiplayerPage extends Control implements DecoratorPage {
|
|||||||
throw new IllegalStateException("CatoSession not ready");
|
throw new IllegalStateException("CatoSession not ready");
|
||||||
}
|
}
|
||||||
|
|
||||||
FXUtils.copyText(getSession().generateInvitationCode(port.get()));
|
FXUtils.copyText(getSession().generateInvitationCode(port.get(), 0));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void createRoom() {
|
public void createRoom() {
|
||||||
@@ -203,7 +203,10 @@ public class MultiplayerPage extends Control implements DecoratorPage {
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
initCatoSession(MultiplayerManager.joinSession(invitation.getSessionName(), invitation.getId(), invitation.getPort(), localPort));
|
initCatoSession(MultiplayerManager.joinSession(invitation.getVersion(), invitation.getSessionName(), invitation.getId(), invitation.getGamePort(), localPort));
|
||||||
|
} catch (MultiplayerManager.IncompatibleCatoVersionException e) {
|
||||||
|
reject.accept(i18n("multiplayer.session.join.invitation_code.version"));
|
||||||
|
return;
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
reject.accept(i18n("multiplayer.session.error"));
|
reject.accept(i18n("multiplayer.session.error"));
|
||||||
return;
|
return;
|
||||||
@@ -223,10 +226,7 @@ public class MultiplayerPage extends Control implements DecoratorPage {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Controllers.confirm(i18n("multiplayer.session.close.warning"), i18n("message.warning"), MessageDialogPane.MessageType.WARNING,
|
Controllers.confirm(i18n("multiplayer.session.close.warning"), i18n("message.warning"), MessageDialogPane.MessageType.WARNING,
|
||||||
() -> {
|
this::stopCatoSession, null);
|
||||||
getSession().stop();
|
|
||||||
clearCatoSession();
|
|
||||||
}, null);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void quitRoom() {
|
public void quitRoom() {
|
||||||
@@ -234,8 +234,16 @@ public class MultiplayerPage extends Control implements DecoratorPage {
|
|||||||
throw new IllegalStateException("CatoSession not ready");
|
throw new IllegalStateException("CatoSession not ready");
|
||||||
}
|
}
|
||||||
|
|
||||||
getSession().stop();
|
Controllers.confirm(i18n("multiplayer.session.quit.warning"), i18n("message.warning"), MessageDialogPane.MessageType.WARNING,
|
||||||
clearCatoSession();
|
this::stopCatoSession, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void cancelRoom() {
|
||||||
|
if (getSession() == null || getSession().isReady() || getMultiplayerState() != MultiplayerManager.State.CONNECTING) {
|
||||||
|
throw new IllegalStateException("CatoSession not existing or already ready");
|
||||||
|
}
|
||||||
|
|
||||||
|
stopCatoSession();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void initCatoSession(MultiplayerManager.CatoSession session) {
|
private void initCatoSession(MultiplayerManager.CatoSession session) {
|
||||||
@@ -248,6 +256,11 @@ public class MultiplayerPage extends Control implements DecoratorPage {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void stopCatoSession() {
|
||||||
|
getSession().stop();
|
||||||
|
clearCatoSession();
|
||||||
|
}
|
||||||
|
|
||||||
private void clearCatoSession() {
|
private void clearCatoSession() {
|
||||||
this.session.set(null);
|
this.session.set(null);
|
||||||
this.token.set(null);
|
this.token.set(null);
|
||||||
@@ -257,15 +270,27 @@ public class MultiplayerPage extends Control implements DecoratorPage {
|
|||||||
|
|
||||||
private void onCatoExit(MultiplayerManager.CatoExitEvent event) {
|
private void onCatoExit(MultiplayerManager.CatoExitEvent event) {
|
||||||
runInFX(() -> {
|
runInFX(() -> {
|
||||||
if (event.getExitCode() == MultiplayerManager.CatoExitEvent.EXIT_CODE_SESSION_EXPIRED) {
|
boolean ready = ((MultiplayerManager.CatoSession) event.getSource()).isReady();
|
||||||
|
switch (event.getExitCode()) {
|
||||||
|
case 0:
|
||||||
|
break;
|
||||||
|
case MultiplayerManager.CatoExitEvent.EXIT_CODE_SESSION_EXPIRED:
|
||||||
Controllers.dialog(i18n("multiplayer.session.expired"));
|
Controllers.dialog(i18n("multiplayer.session.expired"));
|
||||||
} else if (event.getExitCode() != 0) {
|
break;
|
||||||
|
case 1:
|
||||||
|
if (!ready) {
|
||||||
|
Controllers.dialog(i18n("multiplayer.exit.timeout"));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
if (!((MultiplayerManager.CatoSession) event.getSource()).isReady()) {
|
if (!((MultiplayerManager.CatoSession) event.getSource()).isReady()) {
|
||||||
Controllers.dialog(i18n("multiplayer.exit.before_ready", event.getExitCode()));
|
Controllers.dialog(i18n("multiplayer.exit.before_ready", event.getExitCode()));
|
||||||
} else {
|
} else {
|
||||||
Controllers.dialog(i18n("multiplayer.exit.after_ready", event.getExitCode()));
|
Controllers.dialog(i18n("multiplayer.exit.after_ready", event.getExitCode()));
|
||||||
}
|
}
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
clearCatoSession();
|
clearCatoSession();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -64,6 +64,11 @@ public class MultiplayerPageSkin extends SkinBase<MultiplayerPage> {
|
|||||||
copyLinkItem.setLeftGraphic(wrap(SVG::accountArrowRightOutline));
|
copyLinkItem.setLeftGraphic(wrap(SVG::accountArrowRightOutline));
|
||||||
copyLinkItem.setOnAction(e -> control.copyInvitationCode());
|
copyLinkItem.setOnAction(e -> control.copyInvitationCode());
|
||||||
|
|
||||||
|
AdvancedListItem cancelItem = new AdvancedListItem();
|
||||||
|
cancelItem.setTitle(i18n("button.cancel"));
|
||||||
|
cancelItem.setLeftGraphic(wrap(SVG::closeCircle));
|
||||||
|
cancelItem.setOnAction(e -> control.cancelRoom());
|
||||||
|
|
||||||
AdvancedListItem quitItem = new AdvancedListItem();
|
AdvancedListItem quitItem = new AdvancedListItem();
|
||||||
quitItem.setTitle(i18n("multiplayer.session.quit"));
|
quitItem.setTitle(i18n("multiplayer.session.quit"));
|
||||||
quitItem.setLeftGraphic(wrap(SVG::closeCircle));
|
quitItem.setLeftGraphic(wrap(SVG::closeCircle));
|
||||||
@@ -77,6 +82,8 @@ public class MultiplayerPageSkin extends SkinBase<MultiplayerPage> {
|
|||||||
FXUtils.onChangeAndOperate(getSkinnable().multiplayerStateProperty(), state -> {
|
FXUtils.onChangeAndOperate(getSkinnable().multiplayerStateProperty(), state -> {
|
||||||
if (state == MultiplayerManager.State.DISCONNECTED) {
|
if (state == MultiplayerManager.State.DISCONNECTED) {
|
||||||
roomPane.getChildren().setAll(createRoomItem, joinRoomItem);
|
roomPane.getChildren().setAll(createRoomItem, joinRoomItem);
|
||||||
|
} else if (state == MultiplayerManager.State.CONNECTING) {
|
||||||
|
roomPane.getChildren().setAll(cancelItem);
|
||||||
} else if (state == MultiplayerManager.State.MASTER) {
|
} else if (state == MultiplayerManager.State.MASTER) {
|
||||||
roomPane.getChildren().setAll(copyLinkItem, closeRoomItem);
|
roomPane.getChildren().setAll(copyLinkItem, closeRoomItem);
|
||||||
} else if (state == MultiplayerManager.State.SLAVE) {
|
} else if (state == MultiplayerManager.State.SLAVE) {
|
||||||
|
|||||||
@@ -88,16 +88,16 @@ public class MultiplayerServer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static class JoinRequest extends Request {
|
public static class JoinRequest extends Request {
|
||||||
private final String clientLauncherVersion;
|
private final String clientVersion;
|
||||||
private final String username;
|
private final String username;
|
||||||
|
|
||||||
public JoinRequest(String clientLauncherVersion, String username) {
|
public JoinRequest(String clientVersion, String username) {
|
||||||
this.clientLauncherVersion = clientLauncherVersion;
|
this.clientVersion = clientVersion;
|
||||||
this.username = username;
|
this.username = username;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getClientLauncherVersion() {
|
public String getClientVersion() {
|
||||||
return clientLauncherVersion;
|
return clientVersion;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getUsername() {
|
public String getUsername() {
|
||||||
|
|||||||
@@ -568,8 +568,9 @@ multiplayer=Multiplayer
|
|||||||
multiplayer.download=Downloading dependencies for multiplayer
|
multiplayer.download=Downloading dependencies for multiplayer
|
||||||
multiplayer.download.success=Dependencies initialization succeeded
|
multiplayer.download.success=Dependencies initialization succeeded
|
||||||
multiplayer.download.failed=Failed to initialize multiplayer, some files cannot be downloaded
|
multiplayer.download.failed=Failed to initialize multiplayer, some files cannot be downloaded
|
||||||
multiplayer.exit.before_ready=Multiplayer session failed to create. cato exitcode %d
|
|
||||||
multiplayer.exit.after_ready=Multiplayer session broken. cato exitcode %d
|
multiplayer.exit.after_ready=Multiplayer session broken. cato exitcode %d
|
||||||
|
multiplayer.exit.before_ready=Multiplayer session failed to create. cato exitcode %d
|
||||||
|
multiplayer.exit.timeout=Failed to connect to multiplayer server.
|
||||||
multiplayer.hint=Multiplayer functionality is experimental. Please give feedback.
|
multiplayer.hint=Multiplayer functionality is experimental. Please give feedback.
|
||||||
multiplayer.nat=Network Type Detection
|
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.hint=Network type detection will make it clear whether your network fulfills our requirement for multiplayer mode.
|
||||||
@@ -605,9 +606,11 @@ multiplayer.session.join=Join Room
|
|||||||
multiplayer.session.join.hint=You must obtain the invitation code from the gamer who has already created a multiplayer room.
|
multiplayer.session.join.hint=You must obtain the invitation code from the gamer who has already created a multiplayer room.
|
||||||
multiplayer.session.join.invitation_code=Invitation code
|
multiplayer.session.join.invitation_code=Invitation code
|
||||||
multiplayer.session.join.invitation_code.error=Incorrect invitation code. Please obtain invitation code from the player who creates the multiplayer room.
|
multiplayer.session.join.invitation_code.error=Incorrect invitation code. Please obtain invitation code from the player who creates the multiplayer room.
|
||||||
|
multiplayer.session.join.invitation_code.version=Versions of multiplayer functionalities are not the same among you.
|
||||||
multiplayer.session.join.port.error=Cannot find available local network port for listening. Please ensure that HMCL has the permission to listen on a port.
|
multiplayer.session.join.port.error=Cannot find available local network port for listening. Please ensure that HMCL has the permission to listen on a port.
|
||||||
multiplayer.session.members=Room Members
|
multiplayer.session.members=Room Members
|
||||||
multiplayer.session.quit=Quit Room
|
multiplayer.session.quit=Quit Room
|
||||||
|
multiplayer.session.quit.warning=After quiting room, you will lost the connection with the server. Continue?
|
||||||
multiplayer.session.username=Username
|
multiplayer.session.username=Username
|
||||||
multiplayer.state.connecting=Connecting
|
multiplayer.state.connecting=Connecting
|
||||||
multiplayer.state.disconnected=Not created/entered a multiplayer session
|
multiplayer.state.disconnected=Not created/entered a multiplayer session
|
||||||
|
|||||||
@@ -568,8 +568,9 @@ multiplayer=多人聯機
|
|||||||
multiplayer.download=正在下載相依元件
|
multiplayer.download=正在下載相依元件
|
||||||
multiplayer.download.success=多人聯機初始化完成
|
multiplayer.download.success=多人聯機初始化完成
|
||||||
multiplayer.download.failed=初始化失敗,部分文件未能完成下載
|
multiplayer.download.failed=初始化失敗,部分文件未能完成下載
|
||||||
multiplayer.exit.before_ready=多人聯機房間創建失敗,cato 退出碼 %d
|
|
||||||
multiplayer.exit.after_ready=多人聯機會話意外退出,退出碼 %d
|
multiplayer.exit.after_ready=多人聯機會話意外退出,退出碼 %d
|
||||||
|
multiplayer.exit.before_ready=多人聯機房間創建失敗,cato 退出碼 %d
|
||||||
|
multiplayer.exit.timeout=無法連接多人聯機服務
|
||||||
multiplayer.hint=多人聯機功能處於實驗階段,如果有問題請回饋。
|
multiplayer.hint=多人聯機功能處於實驗階段,如果有問題請回饋。
|
||||||
multiplayer.nat=網路檢測
|
multiplayer.nat=網路檢測
|
||||||
multiplayer.nat.hint=執行網路檢測可以讓你更清楚你的網路狀況是否符合聯機功能的需求。不符合聯機功能運行條件的網路狀況將可能導致聯機失敗。
|
multiplayer.nat.hint=執行網路檢測可以讓你更清楚你的網路狀況是否符合聯機功能的需求。不符合聯機功能運行條件的網路狀況將可能導致聯機失敗。
|
||||||
@@ -604,9 +605,11 @@ multiplayer.session.join=加入房間
|
|||||||
multiplayer.session.join.hint=你需要向已經創建好房間的玩家索要邀請碼以便加入多人聯機房間
|
multiplayer.session.join.hint=你需要向已經創建好房間的玩家索要邀請碼以便加入多人聯機房間
|
||||||
multiplayer.session.join.invitation_code=邀請碼
|
multiplayer.session.join.invitation_code=邀請碼
|
||||||
multiplayer.session.join.invitation_code.error=邀請碼不正確,請向開服玩家獲取邀請碼
|
multiplayer.session.join.invitation_code.error=邀請碼不正確,請向開服玩家獲取邀請碼
|
||||||
|
multiplayer.session.join.invitation_code.version=多人聯機功能版本號不一致,請保證連接多人聯機功能版本號一致。
|
||||||
multiplayer.session.join.port.error=無法找到可用的本地網路埠,請確保 HMCL 擁有綁定本地埠的權限。
|
multiplayer.session.join.port.error=無法找到可用的本地網路埠,請確保 HMCL 擁有綁定本地埠的權限。
|
||||||
multiplayer.session.members=房間成員
|
multiplayer.session.members=房間成員
|
||||||
multiplayer.session.quit=退出房間
|
multiplayer.session.quit=退出房間
|
||||||
|
multiplayer.session.quit.warning=退出房間後,你將會與伺服器斷開連接,是否繼續?
|
||||||
multiplayer.session.username=使用者名稱
|
multiplayer.session.username=使用者名稱
|
||||||
multiplayer.state.connecting=連接中
|
multiplayer.state.connecting=連接中
|
||||||
multiplayer.state.disconnected=未創建/加入房間
|
multiplayer.state.disconnected=未創建/加入房間
|
||||||
|
|||||||
@@ -568,11 +568,12 @@ multiplayer=多人联机
|
|||||||
multiplayer.download=正在下载依赖
|
multiplayer.download=正在下载依赖
|
||||||
multiplayer.download.success=多人联机初始化完成
|
multiplayer.download.success=多人联机初始化完成
|
||||||
multiplayer.download.failed=初始化失败,部分文件未能完成下载
|
multiplayer.download.failed=初始化失败,部分文件未能完成下载
|
||||||
multiplayer.exit.before_ready=多人联机房间创建失败,cato 退出码 %d
|
|
||||||
multiplayer.exit.after_ready=多人联机会话意外退出,退出码 %d
|
multiplayer.exit.after_ready=多人联机会话意外退出,退出码 %d
|
||||||
|
multiplayer.exit.before_ready=多人联机房间创建失败,cato 退出码 %d
|
||||||
|
multiplayer.exit.timeout=无法连接多人联机服务
|
||||||
multiplayer.hint=多人联机功能处于实验阶段,如果有问题请反馈。
|
multiplayer.hint=多人联机功能处于实验阶段,如果有问题请反馈。
|
||||||
multiplayer.nat=网络检测
|
multiplayer.nat=网络检测
|
||||||
multiplayer.nat.hint=执行网络检测可以让你更清楚你的网络状况是否符合联机功能的需求。不符合联机功能运行条件的网络状况将可能导致联机失败。
|
multiplayer.nat.hint=执行网络检测可以让你更清楚你的网络状况是否符合联机功能的需求。检测结果为差的网络可能导致联机失败。
|
||||||
multiplayer.nat.latency=延迟
|
multiplayer.nat.latency=延迟
|
||||||
multiplayer.nat.not_yet_tested=尚未检测
|
multiplayer.nat.not_yet_tested=尚未检测
|
||||||
multiplayer.nat.packet_loss_ratio=丢包率
|
multiplayer.nat.packet_loss_ratio=丢包率
|
||||||
@@ -604,9 +605,11 @@ multiplayer.session.join=加入房间
|
|||||||
multiplayer.session.join.hint=你需要向已经创建好房间的玩家索要邀请码以便加入多人联机房间
|
multiplayer.session.join.hint=你需要向已经创建好房间的玩家索要邀请码以便加入多人联机房间
|
||||||
multiplayer.session.join.invitation_code=邀请码
|
multiplayer.session.join.invitation_code=邀请码
|
||||||
multiplayer.session.join.invitation_code.error=邀请码不正确,请向开服玩家获取邀请码
|
multiplayer.session.join.invitation_code.error=邀请码不正确,请向开服玩家获取邀请码
|
||||||
|
multiplayer.session.join.invitation_code.version=多人联机功能版本号不一致,请保证连接多人联机功能版本号一致。
|
||||||
multiplayer.session.join.port.error=无法找到可用的本地网络端口,请确保 HMCL 拥有绑定本地端口的权限。
|
multiplayer.session.join.port.error=无法找到可用的本地网络端口,请确保 HMCL 拥有绑定本地端口的权限。
|
||||||
multiplayer.session.members=房间成员
|
multiplayer.session.members=房间成员
|
||||||
multiplayer.session.quit=退出房间
|
multiplayer.session.quit=退出房间
|
||||||
|
multiplayer.session.quit.warning=退出房间后,你将会与服务器断开连接,是否继续?
|
||||||
multiplayer.session.username=用户名
|
multiplayer.session.username=用户名
|
||||||
multiplayer.state.connecting=连接中
|
multiplayer.state.connecting=连接中
|
||||||
multiplayer.state.disconnected=未创建/加入房间
|
multiplayer.state.disconnected=未创建/加入房间
|
||||||
|
|||||||
@@ -1,3 +1,20 @@
|
|||||||
|
/*
|
||||||
|
* Hello Minecraft! Launcher
|
||||||
|
* Copyright (C) 2021 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;
|
package org.jackhuang.hmcl.game;
|
||||||
|
|
||||||
import org.jackhuang.hmcl.util.io.FileUtils;
|
import org.jackhuang.hmcl.util.io.FileUtils;
|
||||||
@@ -162,7 +179,7 @@ public final class CrashReportAnalyzer {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static final int getJavaVersionFromMajorVersion(int majorVersion) {
|
public static int getJavaVersionFromMajorVersion(int majorVersion) {
|
||||||
if (majorVersion >= 46) {
|
if (majorVersion >= 46) {
|
||||||
return majorVersion - 44;
|
return majorVersion - 44;
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
Reference in New Issue
Block a user