2022-07-05 19:49:04 +00:00
|
|
|
package nl.andrewl.aos2_server;
|
|
|
|
|
2022-07-06 22:55:26 +00:00
|
|
|
import nl.andrewl.aos_core.model.Chunk;
|
2022-07-06 18:20:15 +00:00
|
|
|
import nl.andrewl.aos_core.model.Player;
|
2022-07-06 22:55:26 +00:00
|
|
|
import nl.andrewl.aos_core.model.World;
|
2022-07-06 18:20:15 +00:00
|
|
|
import nl.andrewl.aos_core.net.UdpReceiver;
|
|
|
|
import nl.andrewl.aos_core.net.udp.DatagramInit;
|
|
|
|
import nl.andrewl.record_net.Message;
|
|
|
|
|
2022-07-05 19:49:04 +00:00
|
|
|
import java.io.IOException;
|
2022-07-06 18:20:15 +00:00
|
|
|
import java.net.*;
|
2022-07-06 22:55:26 +00:00
|
|
|
import java.util.Arrays;
|
2022-07-06 18:20:15 +00:00
|
|
|
import java.util.HashMap;
|
|
|
|
import java.util.Map;
|
2022-07-06 22:55:26 +00:00
|
|
|
import java.util.concurrent.ForkJoinPool;
|
2022-07-05 19:49:04 +00:00
|
|
|
|
|
|
|
public class Server implements Runnable {
|
|
|
|
private final ServerSocket serverSocket;
|
|
|
|
private final DatagramSocket datagramSocket;
|
|
|
|
private volatile boolean running;
|
|
|
|
|
2022-07-06 18:20:15 +00:00
|
|
|
private int nextClientId = 1;
|
|
|
|
private final Map<Integer, Player> players;
|
|
|
|
private final Map<Integer, ClientCommunicationHandler> playerClientHandlers;
|
2022-07-06 22:55:26 +00:00
|
|
|
private final World world;
|
2022-07-05 19:49:04 +00:00
|
|
|
|
|
|
|
public Server() throws IOException {
|
|
|
|
this.serverSocket = new ServerSocket(24464, 5);
|
|
|
|
this.serverSocket.setReuseAddress(true);
|
|
|
|
this.datagramSocket = new DatagramSocket(24464);
|
|
|
|
this.datagramSocket.setReuseAddress(true);
|
2022-07-06 18:20:15 +00:00
|
|
|
this.players = new HashMap<>();
|
|
|
|
this.playerClientHandlers = new HashMap<>();
|
2022-07-06 22:55:26 +00:00
|
|
|
|
|
|
|
// Generate world. TODO: do this elsewhere.
|
|
|
|
this.world = new World();
|
|
|
|
for (int x = -5; x <= 5; x++) {
|
|
|
|
for (int y = 0; y <= 3; y++) {
|
|
|
|
for (int z = -3; z <= 3; z++) {
|
|
|
|
Chunk chunk = new Chunk(x, y, z);
|
|
|
|
Arrays.fill(chunk.getBlocks(), (byte) 40);
|
|
|
|
world.addChunk(chunk);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2022-07-05 19:49:04 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void run() {
|
|
|
|
running = true;
|
2022-07-06 18:20:15 +00:00
|
|
|
new Thread(new UdpReceiver(datagramSocket, this::handleUdpMessage)).start();
|
2022-07-05 19:49:04 +00:00
|
|
|
System.out.println("Started AOS2-Server on TCP/UDP port " + serverSocket.getLocalPort() + "; now accepting connections.");
|
|
|
|
while (running) {
|
|
|
|
acceptClientConnection();
|
|
|
|
}
|
2022-07-06 22:55:26 +00:00
|
|
|
for (var player : players.values()) {
|
|
|
|
deregisterPlayer(player);
|
|
|
|
}
|
2022-07-06 18:20:15 +00:00
|
|
|
datagramSocket.close();
|
2022-07-06 22:55:26 +00:00
|
|
|
try {
|
|
|
|
serverSocket.close();
|
|
|
|
} catch (IOException e) {
|
|
|
|
throw new RuntimeException(e);
|
|
|
|
}
|
2022-07-06 18:20:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
public void handleUdpMessage(Message msg, DatagramPacket packet) {
|
|
|
|
// Echo any init message from known clients.
|
|
|
|
if (msg instanceof DatagramInit init) {
|
|
|
|
var handler = getHandler(init.clientId());
|
|
|
|
if (handler != null) {
|
|
|
|
handler.setClientUdpPort(packet.getPort());
|
|
|
|
handler.sendDatagramPacket(msg);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
public synchronized Player registerPlayer(ClientCommunicationHandler handler, String username) {
|
|
|
|
Player player = new Player(nextClientId++, username);
|
|
|
|
players.put(player.getId(), player);
|
|
|
|
playerClientHandlers.put(player.getId(), handler);
|
|
|
|
System.out.println("Registered player " + username + " with id " + player.getId());
|
|
|
|
return player;
|
|
|
|
}
|
|
|
|
|
2022-07-06 22:55:26 +00:00
|
|
|
public synchronized void deregisterPlayer(Player player) {
|
|
|
|
ClientCommunicationHandler handler = playerClientHandlers.get(player.getId());
|
|
|
|
handler.shutdown();
|
|
|
|
players.remove(player.getId());
|
|
|
|
playerClientHandlers.remove(player.getId());
|
|
|
|
System.out.println("Deregistered player " + player.getUsername() + " with id " + player.getId());
|
|
|
|
}
|
|
|
|
|
2022-07-06 18:20:15 +00:00
|
|
|
public ClientCommunicationHandler getHandler(int id) {
|
|
|
|
return playerClientHandlers.get(id);
|
2022-07-05 19:49:04 +00:00
|
|
|
}
|
|
|
|
|
2022-07-06 22:55:26 +00:00
|
|
|
public World getWorld() {
|
|
|
|
return world;
|
|
|
|
}
|
|
|
|
|
2022-07-05 19:49:04 +00:00
|
|
|
private void acceptClientConnection() {
|
|
|
|
try {
|
|
|
|
Socket clientSocket = serverSocket.accept();
|
2022-07-06 22:55:26 +00:00
|
|
|
var handler = new ClientCommunicationHandler(this, clientSocket, datagramSocket);
|
|
|
|
ForkJoinPool.commonPool().submit(() -> {
|
|
|
|
try {
|
|
|
|
handler.establishConnection();
|
|
|
|
} catch (IOException e) {
|
|
|
|
e.printStackTrace();
|
|
|
|
}
|
|
|
|
});
|
2022-07-05 19:49:04 +00:00
|
|
|
} catch (IOException e) {
|
|
|
|
if (e instanceof SocketException && !this.running && e.getMessage().equalsIgnoreCase("Socket closed")) {
|
|
|
|
return; // Ignore this exception, since it is expected on shutdown.
|
|
|
|
}
|
|
|
|
e.printStackTrace();
|
|
|
|
}
|
|
|
|
}
|
2022-07-06 18:20:15 +00:00
|
|
|
|
|
|
|
public static void main(String[] args) throws IOException {
|
|
|
|
new Server().run();
|
|
|
|
}
|
2022-07-05 19:49:04 +00:00
|
|
|
}
|