fix(multiplayer): restore LocalServerBroadcaster and impl port forwarding.

This commit is contained in:
huanghongxun
2022-09-17 18:36:12 +08:00
parent aa7f9b91c7
commit 2028b0ef9c
2 changed files with 128 additions and 0 deletions

View File

@@ -0,0 +1,127 @@
/*
* Hello Minecraft! Launcher
* Copyright (C) 2022 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 java.io.IOException;
import java.net.*;
import java.nio.ByteBuffer;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.nio.charset.StandardCharsets;
import java.util.logging.Level;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import static org.jackhuang.hmcl.util.Logging.LOG;
import static org.jackhuang.hmcl.util.i18n.I18n.i18n;
public class LocalServerBroadcaster implements AutoCloseable {
private final String address;
private final ThreadGroup threadGroup = new ThreadGroup("JoinSession");
private boolean running = true;
public LocalServerBroadcaster(String address) {
this.address = address;
this.threadGroup.setDaemon(true);
}
@Override
public void close() {
running = false;
threadGroup.interrupt();
}
public static final Pattern ADDRESS_PATTERN = Pattern.compile("^\\s*(\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}):(\\d{1,5})\\s*$");
public void start() {
Thread forwardPortThread = new Thread(threadGroup, this::forwardPort, "ForwardPort");
forwardPortThread.start();
}
private void forwardPort() {
try {
Matcher matcher = ADDRESS_PATTERN.matcher(address);
if (!matcher.find()) {
throw new MalformedURLException();
}
try (SocketChannel forwardingSocket = SocketChannel.open(new InetSocketAddress(matcher.group(0), Lang.parseInt(matcher.group(1), 0)));
ServerSocketChannel serverSocket = ServerSocketChannel.open()) {
serverSocket.socket().bind(null);
Thread broadcastMOTDThread = new Thread(threadGroup, () -> broadcastMOTD(serverSocket.socket().getLocalPort()), "BroadcastMOTD");
broadcastMOTDThread.start();
while (running) {
SocketChannel forwardedSocket = serverSocket.accept();
new Thread(threadGroup, () -> forwardTraffic(forwardingSocket, forwardedSocket), "Forward S->D").start();
new Thread(threadGroup, () -> forwardTraffic(forwardedSocket, forwardingSocket), "Forward D->S").start();
}
}
} catch (IOException e) {
LOG.log(Level.WARNING, "Error in forwarding port", e);
threadGroup.interrupt();
}
}
private void forwardTraffic(SocketChannel src, SocketChannel dest) {
try {
ByteBuffer buf = ByteBuffer.allocate(1024);
while (true) {
int len = src.read(buf);
if (len < 0) break;
dest.write(buf);
}
} catch (IOException e) {
LOG.log(Level.WARNING, "Disconnected", e);
}
}
private void broadcastMOTD(int port) {
DatagramSocket socket;
InetAddress broadcastAddress;
try {
socket = new DatagramSocket();
broadcastAddress = InetAddress.getByName("224.0.2.60");
} catch (IOException e) {
LOG.log(Level.WARNING, "Failed to create datagram socket", e);
return;
}
while (running) {
try {
byte[] data = String.format("[MOTD]%s[/MOTD][AD]%d[/AD]", i18n("multiplayer.session.name.motd"), port).getBytes(StandardCharsets.UTF_8);
DatagramPacket packet = new DatagramPacket(data, 0, data.length, broadcastAddress, 4445);
socket.send(packet);
LOG.finest("Broadcast server 0.0.0.0:" + port);
} catch (IOException e) {
LOG.log(Level.WARNING, "Failed to send motd packet", e);
}
try {
Thread.sleep(1500);
} catch (InterruptedException ignored) {
return;
}
}
socket.close();
}
}

View File

@@ -852,6 +852,7 @@ multiplayer.exit=HiPer exited unexpectedly with exit code %d
multiplayer.hint=The multiplayer online function is in the experimental stage, if you have any questions, please go to mcer.cn to give feedback
multiplayer.powered_by=This service is provided under the license of (<a href="https://mcer.cn">Matrix Lab</a>)<a href\="https\://hmcl.huangyuhui.net/api/redirect/multiplayer-agreement" >License Agreement</a>
multiplayer.report=Illegal and Violation Report
multiplayer.session.name.motd=HMCL Multiplayer Session
multiplayer.token=Token
multiplayer.token.apply=Apply for a token
multiplayer.token.expired=HiPer certificate expired, please restart HMCL and try again.