mirror of https://github.com/Minestom/Minestom.git
151 lines
4.8 KiB
Java
151 lines
4.8 KiB
Java
package net.minestom.server.network.socket;
|
|
|
|
import net.minestom.server.MinecraftServer;
|
|
import net.minestom.server.ServerFlag;
|
|
import net.minestom.server.network.PacketProcessor;
|
|
import org.jetbrains.annotations.ApiStatus;
|
|
import org.jetbrains.annotations.NotNull;
|
|
|
|
import java.io.IOException;
|
|
import java.net.*;
|
|
import java.nio.channels.SelectionKey;
|
|
import java.nio.channels.Selector;
|
|
import java.nio.channels.ServerSocketChannel;
|
|
import java.nio.channels.SocketChannel;
|
|
import java.nio.file.Files;
|
|
import java.util.Arrays;
|
|
import java.util.List;
|
|
import org.slf4j.Logger;
|
|
import org.slf4j.LoggerFactory;
|
|
|
|
public final class Server {
|
|
|
|
private static final Logger LOGGER = LoggerFactory.getLogger(Server.class);
|
|
|
|
public static final boolean NO_DELAY = true;
|
|
|
|
private volatile boolean stop;
|
|
|
|
private final Selector selector = Selector.open();
|
|
private final PacketProcessor packetProcessor;
|
|
private final List<Worker> workers;
|
|
private int index;
|
|
|
|
private ServerSocketChannel serverSocket;
|
|
private SocketAddress socketAddress;
|
|
private String address;
|
|
private int port;
|
|
|
|
public Server(PacketProcessor packetProcessor) throws IOException {
|
|
this.packetProcessor = packetProcessor;
|
|
Worker[] workers = new Worker[ServerFlag.WORKER_COUNT];
|
|
Arrays.setAll(workers, value -> new Worker(this));
|
|
this.workers = List.of(workers);
|
|
}
|
|
|
|
@ApiStatus.Internal
|
|
public void init(SocketAddress address) throws IOException {
|
|
ProtocolFamily family;
|
|
if (address instanceof InetSocketAddress inetSocketAddress) {
|
|
this.address = inetSocketAddress.getHostString();
|
|
this.port = inetSocketAddress.getPort();
|
|
family = inetSocketAddress.getAddress().getAddress().length == 4 ? StandardProtocolFamily.INET : StandardProtocolFamily.INET6;
|
|
} else if (address instanceof UnixDomainSocketAddress unixDomainSocketAddress) {
|
|
this.address = "unix://" + unixDomainSocketAddress.getPath();
|
|
this.port = 0;
|
|
family = StandardProtocolFamily.UNIX;
|
|
} else {
|
|
throw new IllegalArgumentException("Address must be an InetSocketAddress or a UnixDomainSocketAddress");
|
|
}
|
|
|
|
ServerSocketChannel server = ServerSocketChannel.open(family);
|
|
server.bind(address);
|
|
server.configureBlocking(false);
|
|
server.register(selector, SelectionKey.OP_ACCEPT);
|
|
this.serverSocket = server;
|
|
this.socketAddress = address;
|
|
|
|
if (address instanceof InetSocketAddress && port == 0) {
|
|
port = server.socket().getLocalPort();
|
|
}
|
|
}
|
|
|
|
@ApiStatus.Internal
|
|
public void start() {
|
|
this.workers.forEach(Thread::start);
|
|
new Thread(() -> {
|
|
while (!stop) {
|
|
// Busy wait for connections
|
|
try {
|
|
this.selector.select(key -> {
|
|
if (!key.isAcceptable()) return;
|
|
try {
|
|
// Register socket and forward to thread
|
|
Worker worker = findWorker();
|
|
final SocketChannel client = serverSocket.accept();
|
|
worker.receiveConnection(client);
|
|
} catch (IOException e) {
|
|
e.printStackTrace();
|
|
}
|
|
});
|
|
} catch (IOException e) {
|
|
MinecraftServer.getExceptionManager().handleException(e);
|
|
}
|
|
}
|
|
}, "Ms-entrypoint").start();
|
|
}
|
|
|
|
public void tick() {
|
|
this.workers.forEach(Worker::tick);
|
|
}
|
|
|
|
public boolean isOpen() {
|
|
return !stop;
|
|
}
|
|
|
|
public void stop() {
|
|
this.stop = true;
|
|
try {
|
|
if(serverSocket != null) {
|
|
this.serverSocket.close();
|
|
}
|
|
|
|
if (socketAddress instanceof UnixDomainSocketAddress unixDomainSocketAddress) {
|
|
Files.deleteIfExists(unixDomainSocketAddress.getPath());
|
|
}
|
|
} catch (IOException e) {
|
|
MinecraftServer.getExceptionManager().handleException(e);
|
|
}
|
|
try {
|
|
this.selector.wakeup();
|
|
this.selector.close();
|
|
} catch (IOException e) {
|
|
LOGGER.error("Server socket selector could not be closed", e);
|
|
System.exit(-1);
|
|
}
|
|
this.workers.forEach(Worker::close);
|
|
}
|
|
|
|
@ApiStatus.Internal
|
|
public @NotNull PacketProcessor packetProcessor() {
|
|
return packetProcessor;
|
|
}
|
|
|
|
public SocketAddress socketAddress() {
|
|
return socketAddress;
|
|
}
|
|
|
|
public String getAddress() {
|
|
return address;
|
|
}
|
|
|
|
public int getPort() {
|
|
return port;
|
|
}
|
|
|
|
private Worker findWorker() {
|
|
this.index = ++index % ServerFlag.WORKER_COUNT;
|
|
return workers.get(index);
|
|
}
|
|
}
|