fix(multiplayer): bind to 0.0.0.0 instead of 127.0.0.1.
This commit is contained in:
@@ -57,7 +57,7 @@ public class LocalServerBroadcaster implements Runnable {
|
|||||||
byte[] data = String.format("[MOTD]%s[/MOTD][AD]%d[/AD]", i18n("multiplayer.session.name.motd", session.getName()), port).getBytes(StandardCharsets.UTF_8);
|
byte[] data = String.format("[MOTD]%s[/MOTD][AD]%d[/AD]", i18n("multiplayer.session.name.motd", session.getName()), port).getBytes(StandardCharsets.UTF_8);
|
||||||
DatagramPacket packet = new DatagramPacket(data, 0, data.length, broadcastAddress, 4445);
|
DatagramPacket packet = new DatagramPacket(data, 0, data.length, broadcastAddress, 4445);
|
||||||
socket.send(packet);
|
socket.send(packet);
|
||||||
LOG.fine("Broadcast server 127.0.0.1:" + port);
|
LOG.fine("Broadcast server 0.0.0.0:" + port);
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
LOG.log(Level.WARNING, "Failed to send motd packet", e);
|
LOG.log(Level.WARNING, "Failed to send motd packet", e);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -79,7 +79,7 @@ public class LocalServerDetector extends Thread {
|
|||||||
}
|
}
|
||||||
|
|
||||||
String response = new String(packet.getData(), packet.getOffset(), packet.getLength(), StandardCharsets.UTF_8);
|
String response = new String(packet.getData(), packet.getOffset(), packet.getLength(), StandardCharsets.UTF_8);
|
||||||
LOG.fine("Local server broadcast message: " + response);
|
LOG.fine("Local server " + packet.getAddress() + ":" + packet.getPort() + " broadcast message: " + response);
|
||||||
onDetectedLanServer.fireEvent(new DetectedLanServerEvent(this, PingResponse.parsePingResponse(response)));
|
onDetectedLanServer.fireEvent(new DetectedLanServerEvent(this, PingResponse.parsePingResponse(response)));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -59,7 +59,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/";
|
||||||
static final String CATO_VERSION = "1.0.8";
|
static final String CATO_VERSION = "1.0.9";
|
||||||
private static final String CATO_PATH = getCatoPath();
|
private static final String CATO_PATH = getCatoPath();
|
||||||
|
|
||||||
private MultiplayerManager() {
|
private MultiplayerManager() {
|
||||||
@@ -96,8 +96,8 @@ public final class MultiplayerManager {
|
|||||||
String[] commands = new String[]{exe.toString(),
|
String[] commands = new String[]{exe.toString(),
|
||||||
"--token", StringUtils.isBlank(token) ? "new" : token,
|
"--token", StringUtils.isBlank(token) ? "new" : token,
|
||||||
"--id", peer,
|
"--id", peer,
|
||||||
"--local", String.format("127.0.0.1:%d", localPort),
|
"--local", String.format("0.0.0.0:%d", localPort),
|
||||||
"--remote", String.format("127.0.0.1:%d", remotePort),
|
"--remote", String.format("0.0.0.0:%d", remotePort),
|
||||||
"--mode", "relay"};
|
"--mode", "relay"};
|
||||||
Process process;
|
Process process;
|
||||||
try {
|
try {
|
||||||
@@ -130,7 +130,7 @@ public final class MultiplayerManager {
|
|||||||
client.onConnected().register(connectedEvent -> {
|
client.onConnected().register(connectedEvent -> {
|
||||||
try {
|
try {
|
||||||
int port = findAvailablePort();
|
int port = findAvailablePort();
|
||||||
writer.write(String.format("net add %s 127.0.0.1:%d 127.0.0.1:%d p2p\n", peer, port, connectedEvent.getPort()));
|
writer.write(String.format("net add %s 0.0.0.0:%d 0.0.0.0:%d p2p\n", peer, port, connectedEvent.getPort()));
|
||||||
future.complete(session);
|
future.complete(session);
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
future.completeExceptionally(e);
|
future.completeExceptionally(e);
|
||||||
@@ -154,7 +154,7 @@ public final class MultiplayerManager {
|
|||||||
|
|
||||||
String[] commands = new String[]{exe.toString(),
|
String[] commands = new String[]{exe.toString(),
|
||||||
"--token", StringUtils.isBlank(token) ? "new" : token,
|
"--token", StringUtils.isBlank(token) ? "new" : token,
|
||||||
"--allows", String.format("127.0.0.1:%d,127.0.0.1:%d", port, server.getPort()),
|
"--allows", String.format("0.0.0.0:%d/0.0.0.0:%d", port, server.getPort()),
|
||||||
"--mode", "relay"};
|
"--mode", "relay"};
|
||||||
Process process = new ProcessBuilder()
|
Process process = new ProcessBuilder()
|
||||||
.command(commands)
|
.command(commands)
|
||||||
|
|||||||
@@ -17,9 +17,11 @@
|
|||||||
*/
|
*/
|
||||||
package org.jackhuang.hmcl.ui.multiplayer;
|
package org.jackhuang.hmcl.ui.multiplayer;
|
||||||
|
|
||||||
|
import com.jfoenix.controls.JFXButton;
|
||||||
import de.javawi.jstun.test.DiscoveryInfo;
|
import de.javawi.jstun.test.DiscoveryInfo;
|
||||||
import javafx.beans.binding.Bindings;
|
import javafx.beans.binding.Bindings;
|
||||||
import javafx.geometry.Insets;
|
import javafx.geometry.Insets;
|
||||||
|
import javafx.geometry.Pos;
|
||||||
import javafx.scene.control.Label;
|
import javafx.scene.control.Label;
|
||||||
import javafx.scene.control.ScrollPane;
|
import javafx.scene.control.ScrollPane;
|
||||||
import javafx.scene.control.SkinBase;
|
import javafx.scene.control.SkinBase;
|
||||||
@@ -172,16 +174,22 @@ public class MultiplayerPageSkin extends SkinBase<MultiplayerPage> {
|
|||||||
masterPane.getChildren().setAll(label);
|
masterPane.getChildren().setAll(label);
|
||||||
}
|
}
|
||||||
|
|
||||||
VBox slavePane = new VBox(8);
|
BorderPane slavePane = new BorderPane();
|
||||||
{
|
{
|
||||||
HintPane slaveHintPane = new HintPane();
|
HintPane slaveHintPane = new HintPane();
|
||||||
slaveHintPane.setText(i18n("multiplayer.state.slave.hint"));
|
slaveHintPane.setText(i18n("multiplayer.state.slave.hint"));
|
||||||
|
slavePane.setTop(slaveHintPane);
|
||||||
|
|
||||||
Label label = new Label();
|
Label label = new Label();
|
||||||
label.textProperty().bind(Bindings.createStringBinding(() ->
|
label.textProperty().bind(Bindings.createStringBinding(() ->
|
||||||
i18n("multiplayer.state.slave", control.getSession() == null ? "" : control.getSession().getName(), control.getPort()),
|
i18n("multiplayer.state.slave", control.getSession() == null ? "" : control.getSession().getName(), "0.0.0.0:" + control.getPort()),
|
||||||
control.sessionProperty(), control.portProperty()));
|
control.sessionProperty(), control.portProperty()));
|
||||||
slavePane.getChildren().setAll(slaveHintPane, label);
|
BorderPane.setAlignment(label, Pos.CENTER_LEFT);
|
||||||
|
slavePane.setCenter(label);
|
||||||
|
|
||||||
|
JFXButton copyButton = new JFXButton(i18n("multiplayer.state.slave.copy"));
|
||||||
|
copyButton.setOnAction(e -> FXUtils.copyText("0.0.0.0:" + control.getPort()));
|
||||||
|
slavePane.setRight(copyButton);
|
||||||
}
|
}
|
||||||
|
|
||||||
FXUtils.onChangeAndOperate(getSkinnable().multiplayerStateProperty(), state -> {
|
FXUtils.onChangeAndOperate(getSkinnable().multiplayerStateProperty(), state -> {
|
||||||
|
|||||||
@@ -643,6 +643,7 @@ multiplayer.state.disconnected=Not created/entered a multiplayer session
|
|||||||
multiplayer.state.disconnected.hint=Someone should create a multiplayer session, and others join the session to play the game together.
|
multiplayer.state.disconnected.hint=Someone should create a multiplayer session, and others join the session to play the game together.
|
||||||
multiplayer.state.master=Created room: %1$s, port: %2$d
|
multiplayer.state.master=Created room: %1$s, port: %2$d
|
||||||
multiplayer.state.slave=Joined room: %1$s, address: %2$s
|
multiplayer.state.slave=Joined room: %1$s, address: %2$s
|
||||||
|
multiplayer.state.slave.copy=Copy address
|
||||||
multiplayer.state.slave.hint=After joining multiplayer room, you should get to multiplayer page in Minecraft and connect to the "HMCL Multiplayer Session" server, or manually add a server with address shown below.
|
multiplayer.state.slave.hint=After joining multiplayer room, you should get to multiplayer page in Minecraft and connect to the "HMCL Multiplayer Session" server, or manually add a server with address shown below.
|
||||||
|
|
||||||
datapack=Datapacks
|
datapack=Datapacks
|
||||||
|
|||||||
@@ -641,7 +641,8 @@ multiplayer.state.connecting=連接中
|
|||||||
multiplayer.state.disconnected=未創建/加入房間
|
multiplayer.state.disconnected=未創建/加入房間
|
||||||
multiplayer.state.disconnected.hint=多人聯機功能需要先有一位玩家創建房間後,其他玩家加入房間後繼續遊戲。
|
multiplayer.state.disconnected.hint=多人聯機功能需要先有一位玩家創建房間後,其他玩家加入房間後繼續遊戲。
|
||||||
multiplayer.state.master=你已創建房間:%1$s,埠號 %2$d
|
multiplayer.state.master=你已創建房間:%1$s,埠號 %2$d
|
||||||
multiplayer.state.slave=你已加入房間: %1$s,地址為 %2$s
|
multiplayer.state.slave=你已加入房間: %1$s,位址為 %2$s
|
||||||
|
multiplayer.state.slave.copy=拷貝位址
|
||||||
multiplayer.state.slave.hint=加入房間後,你需要在 Minecraft 的多人遊戲頁面選擇 HMCL 多人聯機房間伺服器,或者手動添加下方的地址的伺服器。
|
multiplayer.state.slave.hint=加入房間後,你需要在 Minecraft 的多人遊戲頁面選擇 HMCL 多人聯機房間伺服器,或者手動添加下方的地址的伺服器。
|
||||||
|
|
||||||
datapack=資料包
|
datapack=資料包
|
||||||
|
|||||||
@@ -642,6 +642,7 @@ multiplayer.state.disconnected=未创建/加入房间
|
|||||||
multiplayer.state.disconnected.hint=多人联机功能需要先有一位玩家创建房间后,其他玩家加入房间后继续游戏。
|
multiplayer.state.disconnected.hint=多人联机功能需要先有一位玩家创建房间后,其他玩家加入房间后继续游戏。
|
||||||
multiplayer.state.master=你已创建房间:%1$s,端口号 %2$d
|
multiplayer.state.master=你已创建房间:%1$s,端口号 %2$d
|
||||||
multiplayer.state.slave=你已加入房间: %1$s,地址为 %2$s
|
multiplayer.state.slave=你已加入房间: %1$s,地址为 %2$s
|
||||||
|
multiplayer.state.slave.copy=拷贝地址
|
||||||
multiplayer.state.slave.hint=加入房间后,你需要在 Minecraft 的多人游戏页面选择 HMCL 多人联机房间服务器,或者手动添加下方的地址的服务器。
|
multiplayer.state.slave.hint=加入房间后,你需要在 Minecraft 的多人游戏页面选择 HMCL 多人联机房间服务器,或者手动添加下方的地址的服务器。
|
||||||
|
|
||||||
datapack=数据包
|
datapack=数据包
|
||||||
|
|||||||
@@ -0,0 +1,69 @@
|
|||||||
|
/*
|
||||||
|
* Hello Minecraft! Launcher
|
||||||
|
* Copyright (C) 2020 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;
|
||||||
|
|
||||||
|
import org.junit.Ignore;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.net.*;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
|
||||||
|
import static org.jackhuang.hmcl.util.i18n.I18n.i18n;
|
||||||
|
|
||||||
|
public class LocalServerBroadcastTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@Ignore("for manually testing")
|
||||||
|
public void test() {
|
||||||
|
int port = 12345;
|
||||||
|
DatagramSocket socket;
|
||||||
|
InetAddress broadcastAddress;
|
||||||
|
try {
|
||||||
|
socket = new DatagramSocket();
|
||||||
|
broadcastAddress = InetAddress.getByName("224.0.2.60");
|
||||||
|
} catch (IOException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
try {
|
||||||
|
byte[] data = String.format("[MOTD]%s[/MOTD][AD]%d[/AD]", i18n("multiplayer.session.name.motd", "Test server"), port).getBytes(StandardCharsets.UTF_8);
|
||||||
|
DatagramPacket packet = new DatagramPacket(data, 0, data.length, broadcastAddress, 4445);
|
||||||
|
socket.send(packet);
|
||||||
|
System.out.println("Broadcast server 127.0.0.1:" + port);
|
||||||
|
} catch (IOException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
Thread.sleep(1500);
|
||||||
|
} catch (InterruptedException ignored) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@Ignore
|
||||||
|
public void printLocalAddress() throws IOException {
|
||||||
|
DatagramSocket socket = new DatagramSocket(new InetSocketAddress((InetAddress) null, 4444));
|
||||||
|
System.out.println(socket.getLocalAddress());
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,49 @@
|
|||||||
|
/*
|
||||||
|
* 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;
|
||||||
|
|
||||||
|
import org.jackhuang.hmcl.util.Lang;
|
||||||
|
import org.jackhuang.hmcl.util.Logging;
|
||||||
|
import org.junit.Ignore;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import java.net.InetAddress;
|
||||||
|
import java.net.NetworkInterface;
|
||||||
|
import java.net.SocketException;
|
||||||
|
|
||||||
|
public class LocalServerDetectorTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@Ignore("for manually testing")
|
||||||
|
public void test() {
|
||||||
|
try {
|
||||||
|
for (NetworkInterface networkInterface : Lang.toIterable(NetworkInterface.getNetworkInterfaces())) {
|
||||||
|
System.out.println(networkInterface.getName());
|
||||||
|
for (InetAddress address : Lang.toIterable(networkInterface.getInetAddresses())) {
|
||||||
|
System.out.println(address);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (SocketException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
|
||||||
|
Logging.initForTest();
|
||||||
|
LocalServerDetector detector = new LocalServerDetector(3);
|
||||||
|
detector.run();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -330,6 +330,25 @@ public final class Lang {
|
|||||||
return optional.map(Stream::of).orElseGet(Stream::empty);
|
return optional.map(Stream::of).orElseGet(Stream::empty);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static <T> Iterable<T> toIterable(Enumeration<T> enumeration) {
|
||||||
|
if (enumeration == null) {
|
||||||
|
throw new NullPointerException();
|
||||||
|
}
|
||||||
|
return () -> new Iterator<T>() {
|
||||||
|
public boolean hasNext() {
|
||||||
|
return enumeration.hasMoreElements();
|
||||||
|
}
|
||||||
|
|
||||||
|
public T next() {
|
||||||
|
return enumeration.nextElement();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void remove() {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
public static <T> Iterable<T> toIterable(Stream<T> stream) {
|
public static <T> Iterable<T> toIterable(Stream<T> stream) {
|
||||||
return stream::iterator;
|
return stream::iterator;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
* Hello Minecraft! Launcher
|
* Hello Minecraft! Launcher
|
||||||
* Copyright (C) 2020 huangyuhui <huanghongxun2008@126.com> and contributors
|
* Copyright (C) 2021 huangyuhui <huanghongxun2008@126.com> and contributors
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* 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
|
* it under the terms of the GNU General Public License as published by
|
||||||
@@ -72,6 +72,16 @@ public final class Logging {
|
|||||||
LOG.addHandler(streamHandler);
|
LOG.addHandler(streamHandler);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void initForTest() {
|
||||||
|
LOG.setLevel(Level.ALL);
|
||||||
|
LOG.setUseParentHandlers(false);
|
||||||
|
|
||||||
|
ConsoleHandler consoleHandler = new ConsoleHandler();
|
||||||
|
consoleHandler.setFormatter(DefaultFormatter.INSTANCE);
|
||||||
|
consoleHandler.setLevel(Level.FINER);
|
||||||
|
LOG.addHandler(consoleHandler);
|
||||||
|
}
|
||||||
|
|
||||||
public static byte[] getRawLogs() {
|
public static byte[] getRawLogs() {
|
||||||
return storedLogs.toByteArray();
|
return storedLogs.toByteArray();
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user