146 lines
		
	
	
	
		
			4.9 KiB
		
	
	
	
		
			Java
		
	
	
	
	
	
			
		
		
	
	
			146 lines
		
	
	
	
		
			4.9 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 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.jetbrains.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;
 | |
| 	@Nullable
 | |
| 	private 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.");
 | |
| 		} 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 thread2 = new Thread(this::runSender, "chase-server-sender");
 | |
| 			thread2.setDaemon(true);
 | |
| 			thread2.start();
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	private void runSender() {
 | |
| 		ChaseServer.PlayerPosition playerPosition = null;
 | |
| 
 | |
| 		while (this.wantsToRun) {
 | |
| 			if (!this.clientSockets.isEmpty()) {
 | |
| 				ChaseServer.PlayerPosition playerPosition2 = this.getPlayerPosition();
 | |
| 				if (playerPosition2 != null && !playerPosition2.equals(playerPosition)) {
 | |
| 					playerPosition = playerPosition2;
 | |
| 					byte[] bs = playerPosition2.format().getBytes(StandardCharsets.US_ASCII);
 | |
| 
 | |
| 					for (Socket socket : this.clientSockets) {
 | |
| 						if (!socket.isClosed()) {
 | |
| 							Util.ioPool().execute(() -> {
 | |
| 								try {
 | |
| 									OutputStream outputStream = socket.getOutputStream();
 | |
| 									outputStream.write(bs);
 | |
| 									outputStream.flush();
 | |
| 								} catch (IOException var3x) {
 | |
| 									LOGGER.info("Remote control client socket got an IO exception and will be closed", (Throwable)var3x);
 | |
| 									IOUtils.closeQuietly(socket);
 | |
| 								}
 | |
| 							});
 | |
| 						}
 | |
| 					}
 | |
| 				}
 | |
| 
 | |
| 				List<Socket> list = (List<Socket>)this.clientSockets.stream().filter(Socket::isClosed).collect(Collectors.toList());
 | |
| 				this.clientSockets.removeAll(list);
 | |
| 			}
 | |
| 
 | |
| 			if (this.wantsToRun) {
 | |
| 				try {
 | |
| 					Thread.sleep(this.broadcastIntervalMs);
 | |
| 				} catch (InterruptedException var6) {
 | |
| 				}
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	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 var6) {
 | |
| 			if (this.wantsToRun) {
 | |
| 				LOGGER.info("Remote control server closed by interrupt");
 | |
| 			}
 | |
| 		} catch (IOException var7) {
 | |
| 			if (this.wantsToRun) {
 | |
| 				LOGGER.error("Remote control server closed because of an IO exception", (Throwable)var7);
 | |
| 			}
 | |
| 		} 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 = (ServerPlayer)list.get(0);
 | |
| 			String string = (String)ChaseCommand.DIMENSION_NAMES.inverse().get(serverPlayer.level().dimension());
 | |
| 			return string == null
 | |
| 				? null
 | |
| 				: new ChaseServer.PlayerPosition(string, 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);
 | |
| 		}
 | |
| 	}
 | |
| }
 |