151 lines
6.1 KiB
Java
151 lines
6.1 KiB
Java
/*
|
|
* Decompiled with CFR 0.152.
|
|
*
|
|
* Could not load the following classes:
|
|
* com.mojang.logging.LogUtils
|
|
* org.apache.commons.io.IOUtils
|
|
* org.jspecify.annotations.Nullable
|
|
* org.slf4j.Logger
|
|
*/
|
|
package net.minecraft.server.chase;
|
|
|
|
import com.mojang.logging.LogUtils;
|
|
import java.io.IOException;
|
|
import java.io.OutputStream;
|
|
import java.net.InetAddress;
|
|
import java.net.ServerSocket;
|
|
import java.net.Socket;
|
|
import java.nio.channels.ClosedByInterruptException;
|
|
import java.nio.charset.StandardCharsets;
|
|
import java.util.List;
|
|
import java.util.Locale;
|
|
import java.util.concurrent.CopyOnWriteArrayList;
|
|
import java.util.stream.Collectors;
|
|
import net.minecraft.server.commands.ChaseCommand;
|
|
import net.minecraft.server.level.ServerPlayer;
|
|
import net.minecraft.server.players.PlayerList;
|
|
import net.minecraft.util.Util;
|
|
import org.apache.commons.io.IOUtils;
|
|
import org.jspecify.annotations.Nullable;
|
|
import org.slf4j.Logger;
|
|
|
|
public class ChaseServer {
|
|
private static final Logger LOGGER = LogUtils.getLogger();
|
|
private final String serverBindAddress;
|
|
private final int serverPort;
|
|
private final PlayerList playerList;
|
|
private final int broadcastIntervalMs;
|
|
private volatile boolean wantsToRun;
|
|
private @Nullable ServerSocket serverSocket;
|
|
private final CopyOnWriteArrayList<Socket> clientSockets = new CopyOnWriteArrayList();
|
|
|
|
public ChaseServer(String serverBindAddress, int serverPort, PlayerList playerList, int broadcastIntervalMs) {
|
|
this.serverBindAddress = serverBindAddress;
|
|
this.serverPort = serverPort;
|
|
this.playerList = playerList;
|
|
this.broadcastIntervalMs = broadcastIntervalMs;
|
|
}
|
|
|
|
public void start() throws IOException {
|
|
if (this.serverSocket != null && !this.serverSocket.isClosed()) {
|
|
LOGGER.warn("Remote control server was asked to start, but it is already running. Will ignore.");
|
|
return;
|
|
}
|
|
this.wantsToRun = true;
|
|
this.serverSocket = new ServerSocket(this.serverPort, 50, InetAddress.getByName(this.serverBindAddress));
|
|
Thread acceptor = new Thread(this::runAcceptor, "chase-server-acceptor");
|
|
acceptor.setDaemon(true);
|
|
acceptor.start();
|
|
Thread sender = new Thread(this::runSender, "chase-server-sender");
|
|
sender.setDaemon(true);
|
|
sender.start();
|
|
}
|
|
|
|
private void runSender() {
|
|
PlayerPosition oldPlayerPosition = null;
|
|
while (this.wantsToRun) {
|
|
if (!this.clientSockets.isEmpty()) {
|
|
PlayerPosition playerPosition = this.getPlayerPosition();
|
|
if (playerPosition != null && !playerPosition.equals(oldPlayerPosition)) {
|
|
oldPlayerPosition = playerPosition;
|
|
byte[] messageBytes = playerPosition.format().getBytes(StandardCharsets.US_ASCII);
|
|
for (Socket clientSocket : this.clientSockets) {
|
|
if (clientSocket.isClosed()) continue;
|
|
Util.ioPool().execute(() -> {
|
|
try {
|
|
OutputStream output = clientSocket.getOutputStream();
|
|
output.write(messageBytes);
|
|
output.flush();
|
|
}
|
|
catch (IOException e) {
|
|
LOGGER.info("Remote control client socket got an IO exception and will be closed", (Throwable)e);
|
|
IOUtils.closeQuietly((Socket)clientSocket);
|
|
}
|
|
});
|
|
}
|
|
}
|
|
List closed = this.clientSockets.stream().filter(Socket::isClosed).collect(Collectors.toList());
|
|
this.clientSockets.removeAll(closed);
|
|
}
|
|
if (!this.wantsToRun) continue;
|
|
try {
|
|
Thread.sleep(this.broadcastIntervalMs);
|
|
}
|
|
catch (InterruptedException interruptedException) {}
|
|
}
|
|
}
|
|
|
|
public void stop() {
|
|
this.wantsToRun = false;
|
|
IOUtils.closeQuietly((ServerSocket)this.serverSocket);
|
|
this.serverSocket = null;
|
|
}
|
|
|
|
private void runAcceptor() {
|
|
try {
|
|
while (this.wantsToRun) {
|
|
if (this.serverSocket == null) continue;
|
|
LOGGER.info("Remote control server is listening for connections on port {}", (Object)this.serverPort);
|
|
Socket clientSocket = this.serverSocket.accept();
|
|
LOGGER.info("Remote control server received client connection on port {}", (Object)clientSocket.getPort());
|
|
this.clientSockets.add(clientSocket);
|
|
}
|
|
}
|
|
catch (ClosedByInterruptException e) {
|
|
if (this.wantsToRun) {
|
|
LOGGER.info("Remote control server closed by interrupt");
|
|
}
|
|
}
|
|
catch (IOException e) {
|
|
if (this.wantsToRun) {
|
|
LOGGER.error("Remote control server closed because of an IO exception", (Throwable)e);
|
|
}
|
|
}
|
|
finally {
|
|
IOUtils.closeQuietly((ServerSocket)this.serverSocket);
|
|
}
|
|
LOGGER.info("Remote control server is now stopped");
|
|
this.wantsToRun = false;
|
|
}
|
|
|
|
private @Nullable PlayerPosition getPlayerPosition() {
|
|
List<ServerPlayer> players = this.playerList.getPlayers();
|
|
if (players.isEmpty()) {
|
|
return null;
|
|
}
|
|
ServerPlayer player = players.get(0);
|
|
String dimensionName = (String)ChaseCommand.DIMENSION_NAMES.inverse().get(player.level().dimension());
|
|
if (dimensionName == null) {
|
|
return null;
|
|
}
|
|
return new PlayerPosition(dimensionName, player.getX(), player.getY(), player.getZ(), player.getYRot(), player.getXRot());
|
|
}
|
|
|
|
private record PlayerPosition(String dimensionName, double x, double y, double z, float yRot, float xRot) {
|
|
private String format() {
|
|
return String.format(Locale.ROOT, "t %s %.2f %.2f %.2f %.2f %.2f\n", this.dimensionName, this.x, this.y, this.z, Float.valueOf(this.yRot), Float.valueOf(this.xRot));
|
|
}
|
|
}
|
|
}
|
|
|