fix(multiplayer): wrong port.
This commit is contained in:
@@ -67,7 +67,6 @@ import java.util.function.Consumer;
|
|||||||
import java.util.logging.Level;
|
import java.util.logging.Level;
|
||||||
|
|
||||||
import static org.jackhuang.hmcl.setting.ConfigHolder.config;
|
import static org.jackhuang.hmcl.setting.ConfigHolder.config;
|
||||||
import static org.jackhuang.hmcl.ui.FXUtils.runInFX;
|
|
||||||
import static org.jackhuang.hmcl.util.Lang.mapOf;
|
import static org.jackhuang.hmcl.util.Lang.mapOf;
|
||||||
import static org.jackhuang.hmcl.util.Logging.LOG;
|
import static org.jackhuang.hmcl.util.Logging.LOG;
|
||||||
import static org.jackhuang.hmcl.util.Pair.pair;
|
import static org.jackhuang.hmcl.util.Pair.pair;
|
||||||
@@ -489,6 +488,10 @@ public final class LauncherHelper {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!suggested) {
|
||||||
|
future.complete(javaVersion);
|
||||||
|
}
|
||||||
|
|
||||||
return Task.fromCompletableFuture(future);
|
return Task.fromCompletableFuture(future);
|
||||||
}).thenAcceptAsync(Schedulers.javafx(), javaVersion -> {
|
}).thenAcceptAsync(Schedulers.javafx(), javaVersion -> {
|
||||||
if (javaVersion == null) {
|
if (javaVersion == null) {
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ import com.google.gson.annotations.JsonAdapter;
|
|||||||
import javafx.beans.InvalidationListener;
|
import javafx.beans.InvalidationListener;
|
||||||
import javafx.beans.property.*;
|
import javafx.beans.property.*;
|
||||||
import org.jackhuang.hmcl.game.*;
|
import org.jackhuang.hmcl.game.*;
|
||||||
|
import org.jackhuang.hmcl.task.Schedulers;
|
||||||
import org.jackhuang.hmcl.task.Task;
|
import org.jackhuang.hmcl.task.Task;
|
||||||
import org.jackhuang.hmcl.util.Lang;
|
import org.jackhuang.hmcl.util.Lang;
|
||||||
import org.jackhuang.hmcl.util.StringUtils;
|
import org.jackhuang.hmcl.util.StringUtils;
|
||||||
@@ -39,6 +40,8 @@ import java.util.Optional;
|
|||||||
import java.util.concurrent.CancellationException;
|
import java.util.concurrent.CancellationException;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
import static com.jfoenix.concurrency.JFXUtilities.runInFX;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @author huangyuhui
|
* @author huangyuhui
|
||||||
@@ -590,10 +593,12 @@ public final class VersionSetting implements Cloneable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public Task<JavaVersion> getJavaVersion(VersionNumber gameVersion, Version version, boolean checkJava) {
|
public Task<JavaVersion> getJavaVersion(VersionNumber gameVersion, Version version, boolean checkJava) {
|
||||||
return Task.supplyAsync(() -> {
|
return Task.runAsync(Schedulers.javafx(), () -> {
|
||||||
|
if (StringUtils.isBlank(getJava())) {
|
||||||
|
setJava(StringUtils.isBlank(getJavaDir()) ? "Default" : "Custom");
|
||||||
|
}
|
||||||
|
}).thenSupplyAsync(() -> {
|
||||||
try {
|
try {
|
||||||
if (StringUtils.isBlank(getJava()))
|
|
||||||
setJava(StringUtils.isBlank(getJavaDir()) ? "Default" : "Custom");
|
|
||||||
if ("Default".equals(getJava())) {
|
if ("Default".equals(getJava())) {
|
||||||
return JavaVersion.fromCurrentEnvironment();
|
return JavaVersion.fromCurrentEnvironment();
|
||||||
} else if (isJavaAutoSelected()) {
|
} else if (isJavaAutoSelected()) {
|
||||||
@@ -612,7 +617,9 @@ public final class VersionSetting implements Cloneable {
|
|||||||
.filter(java -> java.getVersion().equals(getJava()))
|
.filter(java -> java.getVersion().equals(getJava()))
|
||||||
.collect(Collectors.toList());
|
.collect(Collectors.toList());
|
||||||
if (matchedJava.isEmpty()) {
|
if (matchedJava.isEmpty()) {
|
||||||
setJava("Default");
|
runInFX(() -> {
|
||||||
|
setJava("Default");
|
||||||
|
});
|
||||||
return JavaVersion.fromCurrentEnvironment();
|
return JavaVersion.fromCurrentEnvironment();
|
||||||
} else {
|
} else {
|
||||||
return matchedJava.stream()
|
return matchedJava.stream()
|
||||||
|
|||||||
@@ -32,6 +32,8 @@ public class MultiplayerClient extends Thread {
|
|||||||
private final String id;
|
private final String id;
|
||||||
private final int port;
|
private final int port;
|
||||||
|
|
||||||
|
private int gamePort;
|
||||||
|
|
||||||
private final EventManager<ConnectedEvent> onConnected = new EventManager<>();
|
private final EventManager<ConnectedEvent> onConnected = new EventManager<>();
|
||||||
private final EventManager<Event> onDisconnected = new EventManager<>();
|
private final EventManager<Event> onDisconnected = new EventManager<>();
|
||||||
|
|
||||||
@@ -43,6 +45,14 @@ public class MultiplayerClient extends Thread {
|
|||||||
setDaemon(true);
|
setDaemon(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setGamePort(int gamePort) {
|
||||||
|
this.gamePort = gamePort;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getGamePort() {
|
||||||
|
return gamePort;
|
||||||
|
}
|
||||||
|
|
||||||
public EventManager<ConnectedEvent> onConnected() {
|
public EventManager<ConnectedEvent> onConnected() {
|
||||||
return onConnected;
|
return onConnected;
|
||||||
}
|
}
|
||||||
@@ -53,6 +63,7 @@ public class MultiplayerClient extends Thread {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
|
LOG.info("Connecting to 127.0.0.1:" + port);
|
||||||
try (Socket socket = new Socket(InetAddress.getLoopbackAddress(), port);
|
try (Socket socket = new Socket(InetAddress.getLoopbackAddress(), port);
|
||||||
BufferedReader reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
|
BufferedReader reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
|
||||||
BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()))) {
|
BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()))) {
|
||||||
@@ -68,6 +79,7 @@ public class MultiplayerClient extends Thread {
|
|||||||
}
|
}
|
||||||
|
|
||||||
MultiplayerServer.JoinResponse response = JsonUtils.fromNonNullJson(line, MultiplayerServer.JoinResponse.class);
|
MultiplayerServer.JoinResponse response = JsonUtils.fromNonNullJson(line, MultiplayerServer.JoinResponse.class);
|
||||||
|
setGamePort(response.getPort());
|
||||||
onConnected.fireEvent(new ConnectedEvent(this, response.getPort()));
|
onConnected.fireEvent(new ConnectedEvent(this, response.getPort()));
|
||||||
|
|
||||||
LOG.fine("Received join response with port " + response.getPort());
|
LOG.fine("Received join response with port " + response.getPort());
|
||||||
@@ -85,6 +97,7 @@ public class MultiplayerClient extends Thread {
|
|||||||
} catch (IOException | JsonParseException e) {
|
} catch (IOException | JsonParseException e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
} finally {
|
} finally {
|
||||||
|
LOG.info("Lost connection to 127.0.0.1:" + port);
|
||||||
onDisconnected.fireEvent(new Event(this));
|
onDisconnected.fireEvent(new Event(this));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,6 +18,7 @@
|
|||||||
package org.jackhuang.hmcl.ui.multiplayer;
|
package org.jackhuang.hmcl.ui.multiplayer;
|
||||||
|
|
||||||
import com.google.gson.JsonParseException;
|
import com.google.gson.JsonParseException;
|
||||||
|
import com.google.gson.annotations.SerializedName;
|
||||||
import org.jackhuang.hmcl.Metadata;
|
import org.jackhuang.hmcl.Metadata;
|
||||||
import org.jackhuang.hmcl.event.Event;
|
import org.jackhuang.hmcl.event.Event;
|
||||||
import org.jackhuang.hmcl.event.EventManager;
|
import org.jackhuang.hmcl.event.EventManager;
|
||||||
@@ -62,6 +63,9 @@ public final class MultiplayerManager {
|
|||||||
static final String CATO_VERSION = "1.0.9";
|
static final String CATO_VERSION = "1.0.9";
|
||||||
private static final String CATO_PATH = getCatoPath();
|
private static final String CATO_PATH = getCatoPath();
|
||||||
|
|
||||||
|
private static final String REMOTE_ADDRESS = "127.0.0.1";
|
||||||
|
private static final String LOCAL_ADDRESS = "127.0.0.1";
|
||||||
|
|
||||||
private MultiplayerManager() {
|
private MultiplayerManager() {
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -96,8 +100,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("0.0.0.0:%d", localPort),
|
"--local", String.format("%s:%d", LOCAL_ADDRESS, localPort),
|
||||||
"--remote", String.format("0.0.0.0:%d", remotePort)};
|
"--remote", String.format("%s:%d", REMOTE_ADDRESS, remotePort)};
|
||||||
Process process;
|
Process process;
|
||||||
try {
|
try {
|
||||||
process = new ProcessBuilder()
|
process = new ProcessBuilder()
|
||||||
@@ -129,7 +133,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 0.0.0.0:%d 0.0.0.0:%d p2p\n", peer, port, connectedEvent.getPort()));
|
writer.write(String.format("net add %s %s:%d %s:%d p2p\n", peer, LOCAL_ADDRESS, port, REMOTE_ADDRESS, connectedEvent.getPort()));
|
||||||
future.complete(session);
|
future.complete(session);
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
future.completeExceptionally(e);
|
future.completeExceptionally(e);
|
||||||
@@ -142,18 +146,18 @@ public final class MultiplayerManager {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public static CatoSession createSession(String token, String sessionName, int port) throws IOException {
|
public static CatoSession createSession(String token, String sessionName, int gamePort) throws IOException {
|
||||||
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");
|
||||||
}
|
}
|
||||||
|
|
||||||
MultiplayerServer server = new MultiplayerServer(port);
|
MultiplayerServer server = new MultiplayerServer(gamePort);
|
||||||
server.startServer();
|
server.startServer();
|
||||||
|
|
||||||
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("0.0.0.0:%d/0.0.0.0:%d", port, server.getPort())};
|
"--allows", String.format("%s:%d/%s:%d", REMOTE_ADDRESS, server.getPort(), REMOTE_ADDRESS, gamePort)};
|
||||||
Process process = new ProcessBuilder()
|
Process process = new ProcessBuilder()
|
||||||
.command(commands)
|
.command(commands)
|
||||||
.start();
|
.start();
|
||||||
@@ -291,11 +295,11 @@ public final class MultiplayerManager {
|
|||||||
return id;
|
return id;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String generateInvitationCode(int gamePort, int serverPort) {
|
public String generateInvitationCode(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(CATO_VERSION, id, name, gamePort, serverPort));
|
String json = JsonUtils.GSON.toJson(new Invitation(CATO_VERSION, id, name, 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);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -312,7 +316,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("Connection established");
|
||||||
private static final Pattern LOG_PATTERN = Pattern.compile("(\\[\\d+])\\s+(\\w+)\\s+(\\w+-{0,1}\\w+):\\s(.*)");
|
private static final Pattern LOG_PATTERN = Pattern.compile("(\\[\\d+])\\s+(\\w+)\\s+(\\w+-{0,1}\\w+):\\s(.*)");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -353,17 +357,18 @@ public final class MultiplayerManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static class Invitation {
|
public static class Invitation {
|
||||||
|
@SerializedName("v")
|
||||||
private final String version;
|
private final String version;
|
||||||
private final String id;
|
private final String id;
|
||||||
|
@SerializedName("n")
|
||||||
private final String sessionName;
|
private final String sessionName;
|
||||||
private final int gamePort;
|
@SerializedName("p")
|
||||||
private final int channelPort;
|
private final int channelPort;
|
||||||
|
|
||||||
public Invitation(String version, String id, String sessionName, int gamePort, int channelPort) {
|
public Invitation(String version, String id, String sessionName, int channelPort) {
|
||||||
this.version = version;
|
this.version = version;
|
||||||
this.id = id;
|
this.id = id;
|
||||||
this.sessionName = sessionName;
|
this.sessionName = sessionName;
|
||||||
this.gamePort = gamePort;
|
|
||||||
this.channelPort = channelPort;
|
this.channelPort = channelPort;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -379,10 +384,6 @@ public final class MultiplayerManager {
|
|||||||
return sessionName;
|
return sessionName;
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getGamePort() {
|
|
||||||
return gamePort;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getChannelPort() {
|
public int getChannelPort() {
|
||||||
return channelPort;
|
return channelPort;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,6 +21,8 @@ import de.javawi.jstun.test.DiscoveryInfo;
|
|||||||
import de.javawi.jstun.test.DiscoveryTest;
|
import de.javawi.jstun.test.DiscoveryTest;
|
||||||
import javafx.application.Platform;
|
import javafx.application.Platform;
|
||||||
import javafx.beans.property.*;
|
import javafx.beans.property.*;
|
||||||
|
import javafx.collections.FXCollections;
|
||||||
|
import javafx.collections.ObservableList;
|
||||||
import javafx.scene.control.Control;
|
import javafx.scene.control.Control;
|
||||||
import javafx.scene.control.Skin;
|
import javafx.scene.control.Skin;
|
||||||
import org.jackhuang.hmcl.event.Event;
|
import org.jackhuang.hmcl.event.Event;
|
||||||
@@ -49,8 +51,9 @@ public class MultiplayerPage extends Control implements DecoratorPage, PageAware
|
|||||||
private final ObjectProperty<MultiplayerManager.State> multiplayerState = new SimpleObjectProperty<>(MultiplayerManager.State.DISCONNECTED);
|
private final ObjectProperty<MultiplayerManager.State> multiplayerState = new SimpleObjectProperty<>(MultiplayerManager.State.DISCONNECTED);
|
||||||
private final ReadOnlyStringWrapper token = new ReadOnlyStringWrapper();
|
private final ReadOnlyStringWrapper token = new ReadOnlyStringWrapper();
|
||||||
private final ReadOnlyObjectWrapper<DiscoveryInfo> natState = new ReadOnlyObjectWrapper<>();
|
private final ReadOnlyObjectWrapper<DiscoveryInfo> natState = new ReadOnlyObjectWrapper<>();
|
||||||
private final ReadOnlyIntegerWrapper port = new ReadOnlyIntegerWrapper(-1);
|
private final ReadOnlyIntegerWrapper gamePort = new ReadOnlyIntegerWrapper(-1);
|
||||||
private final ReadOnlyObjectWrapper<MultiplayerManager.CatoSession> session = new ReadOnlyObjectWrapper<>();
|
private final ReadOnlyObjectWrapper<MultiplayerManager.CatoSession> session = new ReadOnlyObjectWrapper<>();
|
||||||
|
private final ObservableList<MultiplayerServer.CatoClient> clients = FXCollections.observableArrayList();
|
||||||
|
|
||||||
private Consumer<MultiplayerManager.CatoExitEvent> onExit;
|
private Consumer<MultiplayerManager.CatoExitEvent> onExit;
|
||||||
private Consumer<MultiplayerManager.CatoIdEvent> onIdGenerated;
|
private Consumer<MultiplayerManager.CatoIdEvent> onIdGenerated;
|
||||||
@@ -70,6 +73,10 @@ public class MultiplayerPage extends Control implements DecoratorPage, PageAware
|
|||||||
return new MultiplayerPageSkin(this);
|
return new MultiplayerPageSkin(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public ObservableList<MultiplayerServer.CatoClient> getClients() {
|
||||||
|
return clients;
|
||||||
|
}
|
||||||
|
|
||||||
public MultiplayerManager.State getMultiplayerState() {
|
public MultiplayerManager.State getMultiplayerState() {
|
||||||
return multiplayerState.get();
|
return multiplayerState.get();
|
||||||
}
|
}
|
||||||
@@ -98,12 +105,12 @@ public class MultiplayerPage extends Control implements DecoratorPage, PageAware
|
|||||||
return token.getReadOnlyProperty();
|
return token.getReadOnlyProperty();
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getPort() {
|
public int getGamePort() {
|
||||||
return port.get();
|
return gamePort.get();
|
||||||
}
|
}
|
||||||
|
|
||||||
public ReadOnlyIntegerProperty portProperty() {
|
public ReadOnlyIntegerProperty gamePortProperty() {
|
||||||
return port.getReadOnlyProperty();
|
return gamePort.getReadOnlyProperty();
|
||||||
}
|
}
|
||||||
|
|
||||||
public MultiplayerManager.CatoSession getSession() {
|
public MultiplayerManager.CatoSession getSession() {
|
||||||
@@ -158,11 +165,11 @@ public class MultiplayerPage extends Control implements DecoratorPage, PageAware
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void copyInvitationCode() {
|
public void copyInvitationCode() {
|
||||||
if (getSession() == null || !getSession().isReady() || port.get() < 0 || getMultiplayerState() != MultiplayerManager.State.MASTER) {
|
if (getSession() == null || !getSession().isReady() || gamePort.get() < 0 || getMultiplayerState() != MultiplayerManager.State.MASTER) {
|
||||||
throw new IllegalStateException("CatoSession not ready");
|
throw new IllegalStateException("CatoSession not ready");
|
||||||
}
|
}
|
||||||
|
|
||||||
FXUtils.copyText(getSession().generateInvitationCode(port.get(), 0));
|
FXUtils.copyText(getSession().generateInvitationCode(getSession().getServer().getPort()));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void createRoom() {
|
public void createRoom() {
|
||||||
@@ -171,16 +178,22 @@ public class MultiplayerPage extends Control implements DecoratorPage, PageAware
|
|||||||
}
|
}
|
||||||
|
|
||||||
Controllers.dialog(new CreateMultiplayerRoomDialog((result, resolve, reject) -> {
|
Controllers.dialog(new CreateMultiplayerRoomDialog((result, resolve, reject) -> {
|
||||||
int port = result.getAd();
|
int gamePort = result.getAd();
|
||||||
try {
|
try {
|
||||||
initCatoSession(MultiplayerManager.createSession(config().getMultiplayerToken(), result.getMotd(), port));
|
MultiplayerManager.CatoSession session = MultiplayerManager.createSession(config().getMultiplayerToken(), result.getMotd(), gamePort);
|
||||||
|
session.getServer().onClientAdded().register(event -> {
|
||||||
|
runInFX(() -> {
|
||||||
|
clients.add(event);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
initCatoSession(session);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
LOG.log(Level.WARNING, "Failed to create session", e);
|
LOG.log(Level.WARNING, "Failed to create session", e);
|
||||||
reject.accept(i18n("multiplayer.session.create.error"));
|
reject.accept(i18n("multiplayer.session.create.error"));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.port.set(port);
|
this.gamePort.set(gamePort);
|
||||||
setMultiplayerState(MultiplayerManager.State.CONNECTING);
|
setMultiplayerState(MultiplayerManager.State.CONNECTING);
|
||||||
resolve.run();
|
resolve.run();
|
||||||
}));
|
}));
|
||||||
@@ -202,7 +215,7 @@ public class MultiplayerPage extends Control implements DecoratorPage, PageAware
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
int localPort;
|
int localPort; // invitation channel
|
||||||
try {
|
try {
|
||||||
localPort = MultiplayerManager.findAvailablePort();
|
localPort = MultiplayerManager.findAvailablePort();
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
@@ -211,7 +224,7 @@ public class MultiplayerPage extends Control implements DecoratorPage, PageAware
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
MultiplayerManager.joinSession(config().getMultiplayerToken(), invitation.getVersion(), invitation.getSessionName(), invitation.getId(), invitation.getGamePort(), localPort)
|
MultiplayerManager.joinSession(config().getMultiplayerToken(), invitation.getVersion(), invitation.getSessionName(), invitation.getId(), invitation.getChannelPort(), localPort)
|
||||||
.thenAcceptAsync(session -> {
|
.thenAcceptAsync(session -> {
|
||||||
initCatoSession(session);
|
initCatoSession(session);
|
||||||
|
|
||||||
@@ -222,7 +235,7 @@ public class MultiplayerPage extends Control implements DecoratorPage, PageAware
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
port.set(localPort);
|
gamePort.set(session.getClient().getGamePort());
|
||||||
setMultiplayerState(MultiplayerManager.State.CONNECTING);
|
setMultiplayerState(MultiplayerManager.State.CONNECTING);
|
||||||
resolve.run();
|
resolve.run();
|
||||||
}, Platform::runLater).exceptionally(throwable -> {
|
}, Platform::runLater).exceptionally(throwable -> {
|
||||||
@@ -270,6 +283,7 @@ public class MultiplayerPage extends Control implements DecoratorPage, PageAware
|
|||||||
onIdGenerated = session.onIdGenerated().registerWeak(this::onCatoIdGenerated);
|
onIdGenerated = session.onIdGenerated().registerWeak(this::onCatoIdGenerated);
|
||||||
onPeerConnected = session.onPeerConnected().registerWeak(this::onCatoPeerConnected);
|
onPeerConnected = session.onPeerConnected().registerWeak(this::onCatoPeerConnected);
|
||||||
|
|
||||||
|
this.clients.clear();
|
||||||
this.session.set(session);
|
this.session.set(session);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -282,7 +296,7 @@ public class MultiplayerPage extends Control implements DecoratorPage, PageAware
|
|||||||
private void clearCatoSession() {
|
private void clearCatoSession() {
|
||||||
this.session.set(null);
|
this.session.set(null);
|
||||||
this.token.set(null);
|
this.token.set(null);
|
||||||
this.port.set(-1);
|
this.gamePort.set(-1);
|
||||||
this.multiplayerState.set(MultiplayerManager.State.DISCONNECTED);
|
this.multiplayerState.set(MultiplayerManager.State.DISCONNECTED);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -21,8 +21,10 @@ import com.jfoenix.controls.JFXButton;
|
|||||||
import com.jfoenix.controls.JFXTextField;
|
import com.jfoenix.controls.JFXTextField;
|
||||||
import de.javawi.jstun.test.DiscoveryInfo;
|
import de.javawi.jstun.test.DiscoveryInfo;
|
||||||
import javafx.beans.binding.Bindings;
|
import javafx.beans.binding.Bindings;
|
||||||
|
import javafx.collections.ObservableList;
|
||||||
import javafx.geometry.Insets;
|
import javafx.geometry.Insets;
|
||||||
import javafx.geometry.Pos;
|
import javafx.geometry.Pos;
|
||||||
|
import javafx.scene.Node;
|
||||||
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;
|
||||||
@@ -38,6 +40,7 @@ import org.jackhuang.hmcl.ui.animation.TransitionPane;
|
|||||||
import org.jackhuang.hmcl.ui.construct.*;
|
import org.jackhuang.hmcl.ui.construct.*;
|
||||||
import org.jackhuang.hmcl.ui.versions.Versions;
|
import org.jackhuang.hmcl.ui.versions.Versions;
|
||||||
import org.jackhuang.hmcl.util.javafx.BindingMapping;
|
import org.jackhuang.hmcl.util.javafx.BindingMapping;
|
||||||
|
import org.jackhuang.hmcl.util.javafx.MappedObservableList;
|
||||||
|
|
||||||
import static org.jackhuang.hmcl.setting.ConfigHolder.config;
|
import static org.jackhuang.hmcl.setting.ConfigHolder.config;
|
||||||
import static org.jackhuang.hmcl.ui.versions.VersionPage.wrap;
|
import static org.jackhuang.hmcl.ui.versions.VersionPage.wrap;
|
||||||
@@ -45,6 +48,8 @@ import static org.jackhuang.hmcl.util.i18n.I18n.i18n;
|
|||||||
|
|
||||||
public class MultiplayerPageSkin extends SkinBase<MultiplayerPage> {
|
public class MultiplayerPageSkin extends SkinBase<MultiplayerPage> {
|
||||||
|
|
||||||
|
private ObservableList<Node> clients;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructor for all SkinBase instances.
|
* Constructor for all SkinBase instances.
|
||||||
*
|
*
|
||||||
@@ -174,9 +179,14 @@ public class MultiplayerPageSkin extends SkinBase<MultiplayerPage> {
|
|||||||
|
|
||||||
Label label = new Label(i18n("multiplayer.state.master"));
|
Label label = new Label(i18n("multiplayer.state.master"));
|
||||||
label.textProperty().bind(Bindings.createStringBinding(() ->
|
label.textProperty().bind(Bindings.createStringBinding(() ->
|
||||||
i18n("multiplayer.state.master", control.getSession() == null ? "" : control.getSession().getName(), control.getPort()),
|
i18n("multiplayer.state.master", control.getSession() == null ? "" : control.getSession().getName(), control.getGamePort()),
|
||||||
control.portProperty(), control.sessionProperty()));
|
control.gamePortProperty(), control.sessionProperty()));
|
||||||
masterPane.getChildren().setAll(masterHintPane, label);
|
|
||||||
|
VBox clientsPane = new VBox(8);
|
||||||
|
clients = MappedObservableList.create(control.getClients(), client -> new ClientItem(client));
|
||||||
|
Bindings.bindContent(clientsPane.getChildren(), clients);
|
||||||
|
|
||||||
|
masterPane.getChildren().setAll(masterHintPane, label, clientsPane);
|
||||||
}
|
}
|
||||||
|
|
||||||
BorderPane slavePane = new BorderPane();
|
BorderPane slavePane = new BorderPane();
|
||||||
@@ -187,13 +197,13 @@ public class MultiplayerPageSkin extends SkinBase<MultiplayerPage> {
|
|||||||
|
|
||||||
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(), "0.0.0.0:" + control.getPort()),
|
i18n("multiplayer.state.slave", control.getSession() == null ? "" : control.getSession().getName(), "0.0.0.0:" + control.getGamePort()),
|
||||||
control.sessionProperty(), control.portProperty()));
|
control.sessionProperty(), control.gamePortProperty()));
|
||||||
BorderPane.setAlignment(label, Pos.CENTER_LEFT);
|
BorderPane.setAlignment(label, Pos.CENTER_LEFT);
|
||||||
slavePane.setCenter(label);
|
slavePane.setCenter(label);
|
||||||
|
|
||||||
JFXButton copyButton = new JFXButton(i18n("multiplayer.state.slave.copy"));
|
JFXButton copyButton = new JFXButton(i18n("multiplayer.state.slave.copy"));
|
||||||
copyButton.setOnAction(e -> FXUtils.copyText("0.0.0.0:" + control.getPort()));
|
copyButton.setOnAction(e -> FXUtils.copyText("0.0.0.0:" + control.getGamePort()));
|
||||||
slavePane.setRight(copyButton);
|
slavePane.setRight(copyButton);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -289,4 +299,15 @@ public class MultiplayerPageSkin extends SkinBase<MultiplayerPage> {
|
|||||||
return i18n("multiplayer.nat.type.unknown");
|
return i18n("multiplayer.nat.type.unknown");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static class ClientItem extends StackPane {
|
||||||
|
ClientItem(MultiplayerServer.CatoClient client) {
|
||||||
|
BorderPane pane = new BorderPane();
|
||||||
|
pane.setLeft(new Label(client.getUsername()));
|
||||||
|
|
||||||
|
RipplerContainer container = new RipplerContainer(pane);
|
||||||
|
getChildren().setAll(container);
|
||||||
|
getStyleClass().add("md-list-cell");
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,6 +18,8 @@
|
|||||||
package org.jackhuang.hmcl.ui.multiplayer;
|
package org.jackhuang.hmcl.ui.multiplayer;
|
||||||
|
|
||||||
import com.google.gson.JsonParseException;
|
import com.google.gson.JsonParseException;
|
||||||
|
import org.jackhuang.hmcl.event.Event;
|
||||||
|
import org.jackhuang.hmcl.event.EventManager;
|
||||||
import org.jackhuang.hmcl.util.Lang;
|
import org.jackhuang.hmcl.util.Lang;
|
||||||
import org.jackhuang.hmcl.util.gson.JsonSubtype;
|
import org.jackhuang.hmcl.util.gson.JsonSubtype;
|
||||||
import org.jackhuang.hmcl.util.gson.JsonType;
|
import org.jackhuang.hmcl.util.gson.JsonType;
|
||||||
@@ -33,6 +35,8 @@ public class MultiplayerServer extends Thread {
|
|||||||
private ServerSocket socket;
|
private ServerSocket socket;
|
||||||
private final int gamePort;
|
private final int gamePort;
|
||||||
|
|
||||||
|
private final EventManager<CatoClient> onClientAdded = new EventManager<CatoClient>();
|
||||||
|
|
||||||
public MultiplayerServer(int gamePort) {
|
public MultiplayerServer(int gamePort) {
|
||||||
this.gamePort = gamePort;
|
this.gamePort = gamePort;
|
||||||
|
|
||||||
@@ -40,6 +44,10 @@ public class MultiplayerServer extends Thread {
|
|||||||
setDaemon(true);
|
setDaemon(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public EventManager<CatoClient> onClientAdded() {
|
||||||
|
return onClientAdded;
|
||||||
|
}
|
||||||
|
|
||||||
public void startServer() throws IOException {
|
public void startServer() throws IOException {
|
||||||
if (socket != null) {
|
if (socket != null) {
|
||||||
throw new IllegalStateException("MultiplayerServer already started");
|
throw new IllegalStateException("MultiplayerServer already started");
|
||||||
@@ -59,6 +67,7 @@ public class MultiplayerServer extends Thread {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
|
LOG.info("Multiplayer Server listening 127.0.0.1:" + socket.getLocalPort());
|
||||||
try {
|
try {
|
||||||
while (!isInterrupted()) {
|
while (!isInterrupted()) {
|
||||||
Socket clientSocket = socket.accept();
|
Socket clientSocket = socket.accept();
|
||||||
@@ -117,6 +126,8 @@ public class MultiplayerServer extends Thread {
|
|||||||
LOG.fine("Received join request with clientVersion=" + clientVersion + ", id=" + username);
|
LOG.fine("Received join request with clientVersion=" + clientVersion + ", id=" + username);
|
||||||
|
|
||||||
writer.write(JsonUtils.GSON.toJson(new JoinResponse(server.gamePort)));
|
writer.write(JsonUtils.GSON.toJson(new JoinResponse(server.gamePort)));
|
||||||
|
|
||||||
|
server.onClientAdded.fireEvent(new CatoClient(server, username));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -171,4 +182,17 @@ public class MultiplayerServer extends Thread {
|
|||||||
return timestamp;
|
return timestamp;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static class CatoClient extends Event {
|
||||||
|
private final String username;
|
||||||
|
|
||||||
|
public CatoClient(Object source, String username) {
|
||||||
|
super(source);
|
||||||
|
this.username = username;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getUsername() {
|
||||||
|
return username;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -770,7 +770,6 @@ settings.game.java_directory=Java 路径
|
|||||||
settings.game.java_directory.auto=自动选择合适的 Java
|
settings.game.java_directory.auto=自动选择合适的 Java
|
||||||
settings.game.java_directory.bit=,%s 位
|
settings.game.java_directory.bit=,%s 位
|
||||||
settings.game.java_directory.choose=选择 Java 路径
|
settings.game.java_directory.choose=选择 Java 路径
|
||||||
settings.game.
|
|
||||||
settings.game.management=管理
|
settings.game.management=管理
|
||||||
settings.game.working_directory=版本隔离(修改后请自行移动相关游戏文件,如存档模组配置等)
|
settings.game.working_directory=版本隔离(修改后请自行移动相关游戏文件,如存档模组配置等)
|
||||||
settings.game.working_directory.choose=选择运行路径
|
settings.game.working_directory.choose=选择运行路径
|
||||||
|
|||||||
@@ -20,8 +20,7 @@ package org.jackhuang.hmcl.util;
|
|||||||
import java.util.Comparator;
|
import java.util.Comparator;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
|
||||||
public class Range<T> {
|
public final class Range<T> {
|
||||||
|
|
||||||
|
|
||||||
@SuppressWarnings({"rawtypes", "unchecked"})
|
@SuppressWarnings({"rawtypes", "unchecked"})
|
||||||
private enum ComparableComparator implements Comparator {
|
private enum ComparableComparator implements Comparator {
|
||||||
|
|||||||
Reference in New Issue
Block a user