Merge branch 'network'
All checks were successful
Linux arm64 / Build (push) Successful in 23m59s

This commit is contained in:
2025-01-26 21:16:57 +01:00
34 changed files with 1145 additions and 59 deletions

View File

@@ -0,0 +1,49 @@
package network.server;
import java.util.Random;
import network.protocol.packets.KeepAlivePacket;
public class KeepAliveHandler {
private final ServerConnexion serverConnexion;
private static final int KEEP_ALIVE_COOLDOWN = 5 * 1000;
private long lastKeepAlive = 0;
private long lastSend = 0;
private volatile boolean keepAliveRecieved = false;
public KeepAliveHandler(ServerConnexion serverConnexion) {
this.serverConnexion = serverConnexion;
sendKeepAlive();
}
public boolean update() {
var currentTime = System.currentTimeMillis();
if (currentTime - lastSend > KEEP_ALIVE_COOLDOWN) {
if (keepAliveRecieved) {
sendKeepAlive();
} else {
System.out.println("Zombie");
serverConnexion.close();
return false;
}
}
return true;
}
public void recievedKeepAlive(long keepAliveId) {
if (lastKeepAlive == keepAliveId)
this.keepAliveRecieved = true;
}
private void sendKeepAlive() {
Random r = new Random();
lastKeepAlive = r.nextLong();
lastSend = System.currentTimeMillis();
keepAliveRecieved = false;
this.serverConnexion.sendPacket(new KeepAlivePacket(lastKeepAlive));
}
}

View File

@@ -0,0 +1,76 @@
package network.server;
import java.io.IOException;
import java.net.ServerSocket;
import java.util.ArrayList;
import java.util.List;
import game.Game;
import game.Player;
import network.protocol.Packet;
import network.protocol.packets.StartGamePacket;
import sudoku.io.SudokuSerializer;
import sudoku.structure.MultiDoku;
public class Server {
final ServerSocket serverSocket;
final List<ServerConnexion> connexions;
private final ServerAcceptThread acceptThread;
private final ServerLogicThread logicThread;
private final Game game;
private int nextPlayerId = 0;
public Server(short port) throws IOException {
this.serverSocket = new ServerSocket(port);
this.connexions = new ArrayList<>();
this.acceptThread = new ServerAcceptThread(this);
this.acceptThread.start();
this.logicThread = new ServerLogicThread(this);
this.logicThread.start();
this.game = new Game();
}
public void broadcastPacket(Packet packet) {
for (ServerConnexion connexion : this.connexions) {
connexion.sendPacket(packet);
}
}
public void update() {
for (var it = connexions.iterator(); it.hasNext();) {
ServerConnexion connexion = it.next();
if (!connexion.update()) {
connexion.close();
connexion.nukeConnection();
it.remove();
}
}
}
public void stop() {
this.acceptThread.cancel();
this.logicThread.cancel();
for (ServerConnexion connexion : this.connexions) {
connexion.nukeConnection();
connexion.close();
}
}
public Player addPlayer(String pseudo) {
Player p = new Player(nextPlayerId, pseudo);
this.game.addPlayer(p);
nextPlayerId++;
return p;
}
public Game getGame() {
return game;
}
public void startGame(MultiDoku doku) {
this.game.startGame(doku);
broadcastPacket(new StartGamePacket(SudokuSerializer.serializeSudoku(doku)));
}
}

View File

@@ -0,0 +1,36 @@
package network.server;
import java.io.IOException;
import java.net.Socket;
public class ServerAcceptThread extends Thread {
private final Server server;
public ServerAcceptThread(Server server) {
this.server = server;
}
public void cancel() {
try {
this.server.serverSocket.close();
} catch (IOException e) {
e.printStackTrace();
}
interrupt();
}
@Override
public void run() {
try {
while(!interrupted()) {
Socket newConnection = this.server.serverSocket.accept();
ServerConnexion serverConnection = new ServerConnexion(newConnection, this.server);
this.server.connexions.add(serverConnection);
}
} catch(IOException e) {
// e.printStackTrace();
}
}
}

View File

@@ -0,0 +1,105 @@
package network.server;
import java.io.IOException;
import java.net.Socket;
import game.Player;
import game.Game.GameState;
import network.Connexion;
import network.protocol.packets.ConnexionInfoPacket;
import network.protocol.packets.DisconnectPacket;
import network.protocol.packets.KeepAlivePacket;
import network.protocol.packets.LoginPacket;
import network.protocol.packets.PlayerJoinPacket;
import network.protocol.packets.PlayerLeavePacket;
import network.protocol.packets.StartGamePacket;
import sudoku.io.SudokuSerializer;
public class ServerConnexion extends Connexion {
private final Server server;
private final KeepAliveHandler keepAliveHandler;
private boolean shouldClose = false;
private Player player = null;
public ServerConnexion(Socket socket, Server server) throws IOException {
super(socket);
this.server = server;
this.keepAliveHandler = new KeepAliveHandler(this);
}
public boolean update() {
if (shouldClose | isClosed())
return false;
return this.keepAliveHandler.update();
}
public void nukeConnection() {
if (player != null) {
sendPacket(new DisconnectPacket("Le serveur a été fermé !"));
this.server.broadcastPacket(new PlayerLeavePacket(player.getId()));
this.server.getGame().removePlayer(player.getId());
}
}
@Override
public synchronized void close() {
if(shouldClose)
return;
super.close();
shouldClose = true;
System.out.println("[Server] Closing connexion !");
}
private void finishLogin() {
// send players that have already joined (excluding this one)
for (Player p : this.server.getGame().getPlayers().values()) {
if (p.getId() != player.getId())
sendPacket(new PlayerJoinPacket(p));
}
this.server.broadcastPacket(new PlayerJoinPacket(player));
sendPacket(new ConnexionInfoPacket(player.getId()));
if (this.server.getGame().getGameState() == GameState.GameGoing) {
sendPacket(new StartGamePacket(SudokuSerializer.serializeSudoku(this.server.getGame().getDoku())));
}
}
@Override
public void visitPacket(KeepAlivePacket packet) {
this.keepAliveHandler.recievedKeepAlive(packet.getKeepAliveId());
}
@Override
public void visitPacket(DisconnectPacket packet) {
close();
}
@Override
public void visitPacket(LoginPacket packet) {
if (this.player != null)
return;
this.player = this.server.addPlayer(packet.getPseudo());
finishLogin();
}
@Override
public void visitPacket(ConnexionInfoPacket packet) {
throw new UnsupportedOperationException("Unimplemented method 'visitPacketConnexionInfo'");
}
@Override
public void visitPacket(PlayerJoinPacket packet) {
throw new UnsupportedOperationException("Unimplemented method 'visitPacketPlayerJoin'");
}
@Override
public void visitPacket(PlayerLeavePacket packet) {
throw new UnsupportedOperationException("Unimplemented method 'visitPacketPlayerLeave'");
}
@Override
public void visitPacket(StartGamePacket packet) {
throw new UnsupportedOperationException("Unimplemented method 'visitPacketStartGame'");
}
}

View File

@@ -0,0 +1,28 @@
package network.server;
public class ServerLogicThread extends Thread {
private final Server server;
public ServerLogicThread(Server server) {
this.server = server;
}
public void cancel() {
interrupt();
}
@Override
public void run() {
while (!interrupted()) {
server.update();
try {
Thread.sleep(50);
} catch (InterruptedException e) {
// e.printStackTrace();
break;
}
}
}
}