mirror of
https://github.com/HMCL-dev/HMCL.git
synced 2025-01-30 14:39:56 +08:00
fix(multiplayer): MultiplayerChannel works.
This commit is contained in:
parent
c66cea41bd
commit
fac812f1dd
@ -0,0 +1,120 @@
|
||||
/*
|
||||
* 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.event.Event;
|
||||
import org.jackhuang.hmcl.util.gson.JsonSubtype;
|
||||
import org.jackhuang.hmcl.util.gson.JsonType;
|
||||
|
||||
public class MultiplayerChannel {
|
||||
|
||||
@JsonType(
|
||||
property = "type",
|
||||
subtypes = {
|
||||
@JsonSubtype(clazz = JoinRequest.class, name = "join"),
|
||||
@JsonSubtype(clazz = KeepAliveRequest.class, name = "keepalive")
|
||||
}
|
||||
)
|
||||
public static class Request {
|
||||
}
|
||||
|
||||
public static class JoinRequest extends Request {
|
||||
private final String clientVersion;
|
||||
private final String username;
|
||||
|
||||
public JoinRequest(String clientVersion, String username) {
|
||||
this.clientVersion = clientVersion;
|
||||
this.username = username;
|
||||
}
|
||||
|
||||
public String getClientVersion() {
|
||||
return clientVersion;
|
||||
}
|
||||
|
||||
public String getUsername() {
|
||||
return username;
|
||||
}
|
||||
}
|
||||
|
||||
public static class KeepAliveRequest extends Request {
|
||||
private final long timestamp;
|
||||
|
||||
public KeepAliveRequest(long timestamp) {
|
||||
this.timestamp = timestamp;
|
||||
}
|
||||
|
||||
public long getTimestamp() {
|
||||
return timestamp;
|
||||
}
|
||||
}
|
||||
|
||||
@JsonType(
|
||||
property = "type",
|
||||
subtypes = {
|
||||
@JsonSubtype(clazz = JoinResponse.class, name = "join"),
|
||||
@JsonSubtype(clazz = KeepAliveResponse.class, name = "keepalive")
|
||||
}
|
||||
)
|
||||
public static class Response {
|
||||
|
||||
}
|
||||
|
||||
public static class JoinResponse extends Response {
|
||||
private final int port;
|
||||
|
||||
public JoinResponse(int port) {
|
||||
this.port = port;
|
||||
}
|
||||
|
||||
public int getPort() {
|
||||
return port;
|
||||
}
|
||||
}
|
||||
|
||||
public static class KeepAliveResponse extends Response {
|
||||
private final long timestamp;
|
||||
|
||||
public KeepAliveResponse(long timestamp) {
|
||||
this.timestamp = timestamp;
|
||||
}
|
||||
|
||||
public long getTimestamp() {
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
public static String verifyJson(String jsonString) {
|
||||
if (jsonString.indexOf('\r') >= 0 || jsonString.indexOf('\n') >= 0) {
|
||||
throw new IllegalArgumentException();
|
||||
}
|
||||
return jsonString;
|
||||
}
|
||||
}
|
@ -23,9 +23,11 @@ import org.jackhuang.hmcl.event.EventManager;
|
||||
import org.jackhuang.hmcl.util.gson.JsonUtils;
|
||||
|
||||
import java.io.*;
|
||||
import java.net.ConnectException;
|
||||
import java.net.InetAddress;
|
||||
import java.net.Socket;
|
||||
|
||||
import static org.jackhuang.hmcl.ui.multiplayer.MultiplayerChannel.*;
|
||||
import static org.jackhuang.hmcl.util.Logging.LOG;
|
||||
|
||||
public class MultiplayerClient extends Thread {
|
||||
@ -64,42 +66,56 @@ public class MultiplayerClient extends Thread {
|
||||
@Override
|
||||
public void run() {
|
||||
LOG.info("Connecting to 127.0.0.1:" + port);
|
||||
try (Socket socket = new Socket(InetAddress.getLoopbackAddress(), port);
|
||||
BufferedReader reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
|
||||
BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()))) {
|
||||
for (int i = 0; i < 5; i++) {
|
||||
try (Socket socket = new Socket(InetAddress.getLoopbackAddress(), port);
|
||||
BufferedReader reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
|
||||
BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()))) {
|
||||
LOG.info("Connected to 127.0.0.1:" + port);
|
||||
|
||||
writer.write(JsonUtils.GSON.toJson(new MultiplayerServer.JoinRequest(MultiplayerManager.CATO_VERSION, id)));
|
||||
writer.write("\n");
|
||||
writer.write(JsonUtils.UGLY_GSON.toJson(new JoinRequest(MultiplayerManager.CATO_VERSION, id)));
|
||||
writer.newLine();
|
||||
writer.flush();
|
||||
|
||||
LOG.fine("Send join request with id=" + id);
|
||||
LOG.fine("Sent join request with id=" + id);
|
||||
|
||||
String line = reader.readLine();
|
||||
if (line == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
MultiplayerServer.JoinResponse response = JsonUtils.fromNonNullJson(line, MultiplayerServer.JoinResponse.class);
|
||||
setGamePort(response.getPort());
|
||||
onConnected.fireEvent(new ConnectedEvent(this, response.getPort()));
|
||||
|
||||
LOG.fine("Received join response with port " + response.getPort());
|
||||
|
||||
while (!isInterrupted()) {
|
||||
writer.write(JsonUtils.GSON.toJson(new MultiplayerServer.KeepAliveResponse(System.currentTimeMillis())));
|
||||
writer.write("\n");
|
||||
|
||||
try {
|
||||
Thread.sleep(1500);
|
||||
} catch (InterruptedException ignored) {
|
||||
String line = reader.readLine();
|
||||
if (line == null) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
} catch (IOException | JsonParseException e) {
|
||||
e.printStackTrace();
|
||||
} finally {
|
||||
LOG.info("Lost connection to 127.0.0.1:" + port);
|
||||
onDisconnected.fireEvent(new Event(this));
|
||||
JoinResponse response = JsonUtils.fromNonNullJson(line, JoinResponse.class);
|
||||
setGamePort(response.getPort());
|
||||
onConnected.fireEvent(new ConnectedEvent(this, response.getPort()));
|
||||
|
||||
LOG.fine("Received join response with port " + response.getPort());
|
||||
|
||||
while (!isInterrupted()) {
|
||||
writer.write(verifyJson(JsonUtils.UGLY_GSON.toJson(new KeepAliveResponse(System.currentTimeMillis()))));
|
||||
writer.newLine();
|
||||
writer.flush();
|
||||
|
||||
try {
|
||||
Thread.sleep(1500);
|
||||
} catch (InterruptedException ignored) {
|
||||
LOG.warning("MultiplayerClient interrupted");
|
||||
return;
|
||||
}
|
||||
}
|
||||
} catch (ConnectException e) {
|
||||
LOG.info("Failed to connect to 127.0.0.1:" + port + ", tried " + i + " time(s)");
|
||||
try {
|
||||
Thread.sleep(2000);
|
||||
} catch (InterruptedException ex) {
|
||||
LOG.warning("MultiplayerClient interrupted");
|
||||
return;
|
||||
}
|
||||
continue;
|
||||
} catch (IOException | JsonParseException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
LOG.info("Lost connection to 127.0.0.1:" + port);
|
||||
onDisconnected.fireEvent(new Event(this));
|
||||
}
|
||||
|
||||
public static class ConnectedEvent extends Event {
|
||||
|
@ -65,6 +65,7 @@ public final class MultiplayerManager {
|
||||
|
||||
private static final String REMOTE_ADDRESS = "127.0.0.1";
|
||||
private static final String LOCAL_ADDRESS = "127.0.0.1";
|
||||
private static final String MODE = "p2p";
|
||||
|
||||
private MultiplayerManager() {
|
||||
}
|
||||
@ -101,7 +102,8 @@ public final class MultiplayerManager {
|
||||
"--token", StringUtils.isBlank(token) ? "new" : token,
|
||||
"--id", peer,
|
||||
"--local", String.format("%s:%d", LOCAL_ADDRESS, localPort),
|
||||
"--remote", String.format("%s:%d", REMOTE_ADDRESS, remotePort)};
|
||||
"--remote", String.format("%s:%d", REMOTE_ADDRESS, remotePort),
|
||||
"--mode", MODE};
|
||||
Process process;
|
||||
try {
|
||||
process = new ProcessBuilder()
|
||||
@ -133,7 +135,11 @@ public final class MultiplayerManager {
|
||||
client.onConnected().register(connectedEvent -> {
|
||||
try {
|
||||
int port = findAvailablePort();
|
||||
writer.write(String.format("net add %s %s:%d %s:%d p2p\n", peer, LOCAL_ADDRESS, port, REMOTE_ADDRESS, connectedEvent.getPort()));
|
||||
String command = String.format("net add %s %s:%d %s:%d %s\n", peer, LOCAL_ADDRESS, port, REMOTE_ADDRESS, connectedEvent.getPort(), MODE);
|
||||
LOG.info("Invoking cato: " + command);
|
||||
writer.write(command);
|
||||
writer.newLine();
|
||||
writer.flush();
|
||||
future.complete(session);
|
||||
} catch (IOException e) {
|
||||
future.completeExceptionally(e);
|
||||
@ -157,7 +163,8 @@ public final class MultiplayerManager {
|
||||
|
||||
String[] commands = new String[]{exe.toString(),
|
||||
"--token", StringUtils.isBlank(token) ? "new" : token,
|
||||
"--allows", String.format("%s:%d/%s:%d", REMOTE_ADDRESS, server.getPort(), REMOTE_ADDRESS, gamePort)};
|
||||
"--allows", String.format("%s:%d/%s:%d", REMOTE_ADDRESS, server.getPort(), REMOTE_ADDRESS, gamePort),
|
||||
"--mode", MODE};
|
||||
Process process = new ProcessBuilder()
|
||||
.command(commands)
|
||||
.start();
|
||||
@ -214,6 +221,7 @@ public final class MultiplayerManager {
|
||||
private final String name;
|
||||
private final State type;
|
||||
private String id;
|
||||
private boolean peerConnected = false;
|
||||
private MultiplayerClient client;
|
||||
private MultiplayerServer server;
|
||||
|
||||
@ -254,14 +262,15 @@ public final class MultiplayerManager {
|
||||
if (id == null) {
|
||||
Matcher matcher = TEMP_TOKEN_PATTERN.matcher(log);
|
||||
if (matcher.find()) {
|
||||
id = "mix" + matcher.group("id");
|
||||
id = matcher.group("id");
|
||||
onIdGenerated.fireEvent(new CatoIdEvent(this, id));
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
if (!peerConnected) {
|
||||
Matcher matcher = PEER_CONNECTED_PATTERN.matcher(log);
|
||||
if (matcher.find()) {
|
||||
peerConnected = true;
|
||||
onPeerConnected.fireEvent(new Event(this));
|
||||
}
|
||||
}
|
||||
@ -315,8 +324,8 @@ public final class MultiplayerManager {
|
||||
return onPeerConnected;
|
||||
}
|
||||
|
||||
private static final Pattern TEMP_TOKEN_PATTERN = Pattern.compile("id\\(mix(?<id>\\w+)\\)");
|
||||
private static final Pattern PEER_CONNECTED_PATTERN = Pattern.compile("Connection established");
|
||||
private static final Pattern TEMP_TOKEN_PATTERN = Pattern.compile("id\\((?<id>\\w+)\\)");
|
||||
private static final Pattern PEER_CONNECTED_PATTERN = Pattern.compile("Connected to main net");
|
||||
private static final Pattern LOG_PATTERN = Pattern.compile("(\\[\\d+])\\s+(\\w+)\\s+(\\w+-{0,1}\\w+):\\s(.*)");
|
||||
}
|
||||
|
||||
|
@ -53,7 +53,7 @@ public class MultiplayerPage extends Control implements DecoratorPage, PageAware
|
||||
private final ReadOnlyObjectWrapper<DiscoveryInfo> natState = new ReadOnlyObjectWrapper<>();
|
||||
private final ReadOnlyIntegerWrapper gamePort = new ReadOnlyIntegerWrapper(-1);
|
||||
private final ReadOnlyObjectWrapper<MultiplayerManager.CatoSession> session = new ReadOnlyObjectWrapper<>();
|
||||
private final ObservableList<MultiplayerServer.CatoClient> clients = FXCollections.observableArrayList();
|
||||
private final ObservableList<MultiplayerChannel.CatoClient> clients = FXCollections.observableArrayList();
|
||||
|
||||
private Consumer<MultiplayerManager.CatoExitEvent> onExit;
|
||||
private Consumer<MultiplayerManager.CatoIdEvent> onIdGenerated;
|
||||
@ -73,7 +73,7 @@ public class MultiplayerPage extends Control implements DecoratorPage, PageAware
|
||||
return new MultiplayerPageSkin(this);
|
||||
}
|
||||
|
||||
public ObservableList<MultiplayerServer.CatoClient> getClients() {
|
||||
public ObservableList<MultiplayerChannel.CatoClient> getClients() {
|
||||
return clients;
|
||||
}
|
||||
|
||||
@ -236,7 +236,7 @@ public class MultiplayerPage extends Control implements DecoratorPage, PageAware
|
||||
});
|
||||
|
||||
gamePort.set(session.getClient().getGamePort());
|
||||
setMultiplayerState(MultiplayerManager.State.CONNECTING);
|
||||
setMultiplayerState(MultiplayerManager.State.SLAVE);
|
||||
resolve.run();
|
||||
}, Platform::runLater).exceptionally(throwable -> {
|
||||
LOG.log(Level.WARNING, "Failed to join sessoin");
|
||||
@ -314,6 +314,9 @@ public class MultiplayerPage extends Control implements DecoratorPage, PageAware
|
||||
Controllers.dialog(i18n("multiplayer.exit.timeout"));
|
||||
}
|
||||
break;
|
||||
case -1:
|
||||
// do nothing
|
||||
break;
|
||||
default:
|
||||
if (!((MultiplayerManager.CatoSession) event.getSource()).isReady()) {
|
||||
Controllers.dialog(i18n("multiplayer.exit.before_ready", event.getExitCode()));
|
||||
@ -335,7 +338,7 @@ public class MultiplayerPage extends Control implements DecoratorPage, PageAware
|
||||
private void onCatoIdGenerated(MultiplayerManager.CatoIdEvent event) {
|
||||
runInFX(() -> {
|
||||
token.set(event.getId());
|
||||
multiplayerState.set(((MultiplayerManager.CatoSession) event.getSource()).getType());
|
||||
setMultiplayerState(((MultiplayerManager.CatoSession) event.getSource()).getType());
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -301,7 +301,7 @@ public class MultiplayerPageSkin extends SkinBase<MultiplayerPage> {
|
||||
}
|
||||
|
||||
private static class ClientItem extends StackPane {
|
||||
ClientItem(MultiplayerServer.CatoClient client) {
|
||||
ClientItem(MultiplayerChannel.CatoClient client) {
|
||||
BorderPane pane = new BorderPane();
|
||||
pane.setLeft(new Label(client.getUsername()));
|
||||
|
||||
|
@ -21,21 +21,22 @@ 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.gson.JsonSubtype;
|
||||
import org.jackhuang.hmcl.util.gson.JsonType;
|
||||
import org.jackhuang.hmcl.util.gson.JsonUtils;
|
||||
|
||||
import java.io.*;
|
||||
import java.net.ServerSocket;
|
||||
import java.net.Socket;
|
||||
import java.util.logging.Level;
|
||||
|
||||
import static org.jackhuang.hmcl.ui.multiplayer.MultiplayerChannel.*;
|
||||
import static org.jackhuang.hmcl.util.Logging.LOG;
|
||||
|
||||
public class MultiplayerServer extends Thread {
|
||||
private ServerSocket socket;
|
||||
private final int gamePort;
|
||||
|
||||
private final EventManager<CatoClient> onClientAdded = new EventManager<CatoClient>();
|
||||
private final EventManager<MultiplayerChannel.CatoClient> onClientAdded = new EventManager<>();
|
||||
private final EventManager<Event> onKeepAlive = new EventManager<>();
|
||||
|
||||
public MultiplayerServer(int gamePort) {
|
||||
this.gamePort = gamePort;
|
||||
@ -44,15 +45,23 @@ public class MultiplayerServer extends Thread {
|
||||
setDaemon(true);
|
||||
}
|
||||
|
||||
public EventManager<CatoClient> onClientAdded() {
|
||||
public EventManager<MultiplayerChannel.CatoClient> onClientAdded() {
|
||||
return onClientAdded;
|
||||
}
|
||||
|
||||
public EventManager<Event> onKeepAlive() {
|
||||
return onKeepAlive;
|
||||
}
|
||||
|
||||
public void startServer() throws IOException {
|
||||
startServer(0);
|
||||
}
|
||||
|
||||
public void startServer(int port) throws IOException {
|
||||
if (socket != null) {
|
||||
throw new IllegalStateException("MultiplayerServer already started");
|
||||
}
|
||||
socket = new ServerSocket(0);
|
||||
socket = new ServerSocket(port);
|
||||
|
||||
start();
|
||||
}
|
||||
@ -79,120 +88,42 @@ public class MultiplayerServer extends Thread {
|
||||
}
|
||||
|
||||
private void handleClient(Socket targetSocket) {
|
||||
LOG.info("Accepted client " + targetSocket.getRemoteSocketAddress());
|
||||
try (Socket clientSocket = targetSocket;
|
||||
BufferedReader reader = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));
|
||||
BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(clientSocket.getOutputStream()))) {
|
||||
String line;
|
||||
while ((line = reader.readLine()) != null) {
|
||||
Request request = JsonUtils.fromNonNullJson(line, Request.class);
|
||||
request.process(this, writer);
|
||||
if (isInterrupted()) {
|
||||
return;
|
||||
}
|
||||
|
||||
LOG.fine("Message from client " + targetSocket.getRemoteSocketAddress() + ":" + line);
|
||||
MultiplayerChannel.Request request = JsonUtils.fromNonNullJson(line, MultiplayerChannel.Request.class);
|
||||
|
||||
if (request instanceof JoinRequest) {
|
||||
JoinRequest joinRequest = (JoinRequest) request;
|
||||
LOG.info("Received join request with clientVersion=" + joinRequest.getClientVersion() + ", id=" + joinRequest.getUsername());
|
||||
|
||||
writer.write(verifyJson(JsonUtils.UGLY_GSON.toJson(new JoinResponse(gamePort))));
|
||||
writer.newLine();
|
||||
writer.flush();
|
||||
|
||||
onClientAdded.fireEvent(new CatoClient(this, joinRequest.getUsername()));
|
||||
} else if (request instanceof KeepAliveRequest) {
|
||||
writer.write(JsonUtils.UGLY_GSON.toJson(new KeepAliveResponse(System.currentTimeMillis())));
|
||||
writer.newLine();
|
||||
writer.flush();
|
||||
|
||||
onKeepAlive.fireEvent(new Event(this));
|
||||
} else {
|
||||
LOG.log(Level.WARNING, "Unrecognized packet from client " + targetSocket.getRemoteSocketAddress() + ":" + line);
|
||||
}
|
||||
}
|
||||
} catch (IOException | JsonParseException ignored) {
|
||||
}
|
||||
}
|
||||
|
||||
@JsonType(
|
||||
property = "type",
|
||||
subtypes = {
|
||||
@JsonSubtype(clazz = JoinRequest.class, name = "join"),
|
||||
@JsonSubtype(clazz = KeepAliveRequest.class, name = "keepalive")
|
||||
}
|
||||
)
|
||||
public static class Request {
|
||||
|
||||
public void process(MultiplayerServer server, BufferedWriter writer) throws IOException, JsonParseException {
|
||||
}
|
||||
}
|
||||
|
||||
public static class JoinRequest extends Request {
|
||||
private final String clientVersion;
|
||||
private final String username;
|
||||
|
||||
public JoinRequest(String clientVersion, String username) {
|
||||
this.clientVersion = clientVersion;
|
||||
this.username = username;
|
||||
}
|
||||
|
||||
public String getClientVersion() {
|
||||
return clientVersion;
|
||||
}
|
||||
|
||||
public String getUsername() {
|
||||
return username;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void process(MultiplayerServer server, BufferedWriter writer) throws IOException, JsonParseException {
|
||||
LOG.fine("Received join request with clientVersion=" + clientVersion + ", id=" + username);
|
||||
|
||||
writer.write(JsonUtils.GSON.toJson(new JoinResponse(server.gamePort)));
|
||||
|
||||
server.onClientAdded.fireEvent(new CatoClient(server, username));
|
||||
}
|
||||
}
|
||||
|
||||
public static class KeepAliveRequest extends Request {
|
||||
private final long timestamp;
|
||||
|
||||
public KeepAliveRequest(long timestamp) {
|
||||
this.timestamp = timestamp;
|
||||
}
|
||||
|
||||
public long getTimestamp() {
|
||||
return timestamp;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void process(MultiplayerServer server, BufferedWriter writer) throws IOException, JsonParseException {
|
||||
writer.write(JsonUtils.GSON.toJson(new KeepAliveResponse(System.currentTimeMillis())));
|
||||
}
|
||||
}
|
||||
|
||||
@JsonType(
|
||||
property = "type",
|
||||
subtypes = {
|
||||
@JsonSubtype(clazz = JoinResponse.class, name = "join"),
|
||||
@JsonSubtype(clazz = KeepAliveResponse.class, name = "keepalive")
|
||||
}
|
||||
)
|
||||
public static class Response {
|
||||
|
||||
}
|
||||
|
||||
public static class JoinResponse extends Response {
|
||||
private final int port;
|
||||
|
||||
public JoinResponse(int port) {
|
||||
this.port = port;
|
||||
}
|
||||
|
||||
public int getPort() {
|
||||
return port;
|
||||
}
|
||||
}
|
||||
|
||||
public static class KeepAliveResponse extends Response {
|
||||
private final long timestamp;
|
||||
|
||||
public KeepAliveResponse(long timestamp) {
|
||||
this.timestamp = timestamp;
|
||||
}
|
||||
|
||||
public long getTimestamp() {
|
||||
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;
|
||||
} catch (IOException e) {
|
||||
LOG.log(Level.WARNING, "Failed to handle client socket.", e);
|
||||
} catch (JsonParseException e) {
|
||||
LOG.log(Level.SEVERE, "Failed to parse client request. This should not happen.", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,43 @@
|
||||
/*
|
||||
* 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.Logging;
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Test;
|
||||
|
||||
public class MultiplayerClientServerTest {
|
||||
|
||||
@Test
|
||||
@Ignore
|
||||
public void startServer() throws Exception {
|
||||
Logging.initForTest();
|
||||
MultiplayerServer server = new MultiplayerServer(1000);
|
||||
server.startServer(44444);
|
||||
|
||||
MultiplayerClient client = new MultiplayerClient("username", 44444);
|
||||
client.start();
|
||||
|
||||
server.onKeepAlive().register(event -> {
|
||||
client.interrupt();
|
||||
server.interrupt();
|
||||
});
|
||||
|
||||
server.join();
|
||||
}
|
||||
}
|
@ -35,6 +35,12 @@ public final class JsonUtils {
|
||||
|
||||
public static final Gson GSON = defaultGsonBuilder().create();
|
||||
|
||||
public static final Gson UGLY_GSON = new GsonBuilder()
|
||||
.registerTypeAdapterFactory(JsonTypeAdapterFactory.INSTANCE)
|
||||
.registerTypeAdapterFactory(ValidationTypeAdapterFactory.INSTANCE)
|
||||
.registerTypeAdapterFactory(LowerCaseEnumTypeAdapterFactory.INSTANCE)
|
||||
.create();
|
||||
|
||||
private JsonUtils() {
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user