150 lines
6.0 KiB
Java
150 lines
6.0 KiB
Java
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 javax.annotation.Nullable;
|
|
import net.minecraft.Util;
|
|
import net.minecraft.server.commands.ChaseCommand;
|
|
import net.minecraft.server.level.ServerPlayer;
|
|
import net.minecraft.server.players.PlayerList;
|
|
import org.apache.commons.io.IOUtils;
|
|
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;
|
|
@Nullable
|
|
private ServerSocket serverSocket;
|
|
private final CopyOnWriteArrayList<Socket> clientSockets = new CopyOnWriteArrayList<>();
|
|
|
|
public ChaseServer(String p_196032_, int p_196033_, PlayerList p_196034_, int p_196035_) {
|
|
this.serverBindAddress = p_196032_;
|
|
this.serverPort = p_196033_;
|
|
this.playerList = p_196034_;
|
|
this.broadcastIntervalMs = p_196035_;
|
|
}
|
|
|
|
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.");
|
|
} else {
|
|
this.wantsToRun = true;
|
|
this.serverSocket = new ServerSocket(this.serverPort, 50, InetAddress.getByName(this.serverBindAddress));
|
|
Thread thread = new Thread(this::runAcceptor, "chase-server-acceptor");
|
|
thread.setDaemon(true);
|
|
thread.start();
|
|
Thread thread1 = new Thread(this::runSender, "chase-server-sender");
|
|
thread1.setDaemon(true);
|
|
thread1.start();
|
|
}
|
|
}
|
|
|
|
private void runSender() {
|
|
ChaseServer.PlayerPosition chaseserver$playerposition = null;
|
|
|
|
while (this.wantsToRun) {
|
|
if (!this.clientSockets.isEmpty()) {
|
|
ChaseServer.PlayerPosition chaseserver$playerposition1 = this.getPlayerPosition();
|
|
if (chaseserver$playerposition1 != null && !chaseserver$playerposition1.equals(chaseserver$playerposition)) {
|
|
chaseserver$playerposition = chaseserver$playerposition1;
|
|
byte[] abyte = chaseserver$playerposition1.format().getBytes(StandardCharsets.US_ASCII);
|
|
|
|
for (Socket socket : this.clientSockets) {
|
|
if (!socket.isClosed()) {
|
|
Util.ioPool().execute(() -> {
|
|
try {
|
|
OutputStream outputstream = socket.getOutputStream();
|
|
outputstream.write(abyte);
|
|
outputstream.flush();
|
|
} catch (IOException ioexception) {
|
|
LOGGER.info("Remote control client socket got an IO exception and will be closed", (Throwable)ioexception);
|
|
IOUtils.closeQuietly(socket);
|
|
}
|
|
});
|
|
}
|
|
}
|
|
}
|
|
|
|
List<Socket> list = this.clientSockets.stream().filter(Socket::isClosed).collect(Collectors.toList());
|
|
this.clientSockets.removeAll(list);
|
|
}
|
|
|
|
if (this.wantsToRun) {
|
|
try {
|
|
Thread.sleep(this.broadcastIntervalMs);
|
|
} catch (InterruptedException interruptedexception) {
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
public void stop() {
|
|
this.wantsToRun = false;
|
|
IOUtils.closeQuietly(this.serverSocket);
|
|
this.serverSocket = null;
|
|
}
|
|
|
|
private void runAcceptor() {
|
|
try {
|
|
while (this.wantsToRun) {
|
|
if (this.serverSocket != null) {
|
|
LOGGER.info("Remote control server is listening for connections on port {}", this.serverPort);
|
|
Socket socket = this.serverSocket.accept();
|
|
LOGGER.info("Remote control server received client connection on port {}", socket.getPort());
|
|
this.clientSockets.add(socket);
|
|
}
|
|
}
|
|
} catch (ClosedByInterruptException closedbyinterruptexception) {
|
|
if (this.wantsToRun) {
|
|
LOGGER.info("Remote control server closed by interrupt");
|
|
}
|
|
} catch (IOException ioexception) {
|
|
if (this.wantsToRun) {
|
|
LOGGER.error("Remote control server closed because of an IO exception", (Throwable)ioexception);
|
|
}
|
|
} finally {
|
|
IOUtils.closeQuietly(this.serverSocket);
|
|
}
|
|
|
|
LOGGER.info("Remote control server is now stopped");
|
|
this.wantsToRun = false;
|
|
}
|
|
|
|
@Nullable
|
|
private ChaseServer.PlayerPosition getPlayerPosition() {
|
|
List<ServerPlayer> list = this.playerList.getPlayers();
|
|
if (list.isEmpty()) {
|
|
return null;
|
|
} else {
|
|
ServerPlayer serverplayer = list.get(0);
|
|
String s = ChaseCommand.DIMENSION_NAMES.inverse().get(serverplayer.level().dimension());
|
|
return s == null
|
|
? null
|
|
: new ChaseServer.PlayerPosition(
|
|
s, serverplayer.getX(), serverplayer.getY(), serverplayer.getZ(), serverplayer.getYRot(), serverplayer.getXRot()
|
|
);
|
|
}
|
|
}
|
|
|
|
record PlayerPosition(String dimensionName, double x, double y, double z, float yRot, float xRot) {
|
|
String format() {
|
|
return String.format(
|
|
Locale.ROOT, "t %s %.2f %.2f %.2f %.2f %.2f\n", this.dimensionName, this.x, this.y, this.z, this.yRot, this.xRot
|
|
);
|
|
}
|
|
}
|
|
} |