fix(multiplayer): restore LocalServerBroadcaster and impl port forwarding.
This commit is contained in:
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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.
|
||||
|
||||
Reference in New Issue
Block a user