Fixed: now after a forfeit win, when the player is disconnected can be created a new game without any bug. Added: comment in some functions like doFirstEvent, RMIServer start and TCPServer start.

This commit is contained in:
rubenpirreram
2026-05-23 20:37:29 +02:00
parent 84a1f8de0f
commit aa8c3b0034
3 changed files with 122 additions and 147 deletions
@@ -30,7 +30,7 @@ import java.rmi.*;
*/
public class RMIServer extends UnicastRemoteObject implements IGameServer {
private String host;
private GameController controller;
private final GameController controller;
private Registry registry;
private int nPort;
@@ -105,23 +105,13 @@ public class RMIServer extends UnicastRemoteObject implements IGameServer {
System.out.println("(After crash)Reconnected player: " + username);
return true;
}
if (playerList.containsKey(username) && !playerList.get(username)) {
playerList.put(username, true);
clients.put(username, callback);
System.out.println("Reconnected player: " + username);
startWatchdog(username);
Game game = controller.getModel();
callback.onGameInit(new MiniModel(game.getSlotMap(), game.orderLogicCard,game.getCurrentState(),game.getPlayers(),game.getAvailableTotems(),game.getUpperListTribeCards(),game.getLowerListTribeCards(),game.getUpperListBuilding(),game.getLowerListBuilding()));
System.out.println("Model sent: " + username);
actionQueue.add(new ReconnectPlayer(username));
return true;
}
}
else
{
if (playerList.isEmpty()) {
controller.setModel(new Game(preferredInt));
playerList.setLimit(preferredInt);
System.out.println("Game Created With :"+preferredInt+" Players");
}
if (controller.addPlayer(username)) {
clients.put(username, callback);
@@ -130,18 +120,17 @@ public class RMIServer extends UnicastRemoteObject implements IGameServer {
System.out.println("Accepted player: " + username);
return true;
}
// Reconnection: player was offline
if (playerList.containsKey(username) && !playerList.get(username)) {
playerList.put(username, true);
clients.put(username, callback);
System.out.println("Reconnected player: " + username);
startWatchdog(username);
Game game = controller.getModel();
callback.onGameInit(new MiniModel(game.getSlotMap(), game.orderLogicCard,game.getCurrentState(),game.getPlayers(),game.getAvailableTotems(),game.getUpperListTribeCards(),game.getLowerListTribeCards(),game.getUpperListBuilding(),game.getLowerListBuilding()));
System.out.println("Model sent: " + username);
actionQueue.add(new ReconnectPlayer(username));
return true;
}
}
if (playerList.containsKey(username) && !playerList.get(username)) {
playerList.put(username, true);
clients.put(username, callback);
System.out.println("Reconnected player: " + username);
startWatchdog(username);
Game game = controller.getModel();
callback.onGameInit(new MiniModel(game.getSlotMap(), game.orderLogicCard,game.getCurrentState(),game.getPlayers(),game.getAvailableTotems(),game.getUpperListTribeCards(),game.getLowerListTribeCards(),game.getUpperListBuilding(),game.getLowerListBuilding()));
System.out.println("Model sent: " + username);
actionQueue.add(new ReconnectPlayer(username));
return true;
}
return false;
}
@@ -179,12 +179,11 @@ public class TCPServer {
synchronized (controller) {
String username = eventAddPlayer.getUsername();
//reconnect players after a server crash
if (serverCrashed) {
if (controller.getModel().getPlayers().stream()
.anyMatch(p -> p.getUserName().equals(username))
&& !playerList.containsKey(username)) {
playerList.put(username, true);
System.out.println("(After crash)Reconnected player: " + username);
@@ -206,54 +205,18 @@ public class TCPServer {
clientHandlers.add(handler);
connectedPlayers++;
return;
} else if (playerList.containsKey(username)
&& !playerList.get(username)) {
playerList.put(username, true);
System.out.println("Reconnected player: " + username);
ClientHandler handler = new ClientHandler(
username,
clientSocket,
clientSend,
clientReceive,
clientHandlers,
playerList,
actionQueue
);
clientSocket.getOutputStream().write(1);
pendingHeartbeat.put(username, handler);
Game game = controller.getModel();
handler.notifyMiniModel(new MiniModel(
game.getSlotMap(),
game.orderLogicCard,
game.getCurrentState(),
game.getPlayers(),
game.getAvailableTotems(),
game.getUpperListTribeCards(),game.getLowerListTribeCards(),game.getUpperListBuilding(),game.getLowerListBuilding()
));
Thread thread = new Thread(handler);
thread.start();
clientHandlers.add(handler);
connectedPlayers++;
actionQueue.add(new ReconnectPlayer(username));
} else {
clientSocket.getOutputStream().write(-1);
clientSocket.close();
System.out.println("Player could not be added. Connection terminated.");
}
} else {
}
//manages a new player adding
else {
if (playerList.isEmpty()) {
Game model = new Game(eventAddPlayer.getProposedNPlayer());
controller.setModel(model);
playerList.setLimit(eventAddPlayer.getProposedNPlayer());
System.out.println("Game Created With :"+eventAddPlayer.getProposedNPlayer()+" Players");
}
if (controller.addPlayer(username)) {
@@ -278,52 +241,55 @@ public class TCPServer {
clientHandlers.add(handler);
connectedPlayers++;
return;
} else if (playerList.containsKey(username)
&& !playerList.get(username)) {
playerList.put(username, true);
System.out.println("Reconnected player: " + username);
ClientHandler handler = new ClientHandler(
username,
clientSocket,
clientSend,
clientReceive,
clientHandlers,
playerList,
actionQueue
);
clientSocket.getOutputStream().write(1);
pendingHeartbeat.put(username, handler);
Game game = controller.getModel();
handler.notifyMiniModel(new MiniModel(
game.getSlotMap(),
game.orderLogicCard,
game.getCurrentState(),
game.getPlayers(),
game.getAvailableTotems(),
game.getUpperListTribeCards(),game.getLowerListTribeCards(),game.getUpperListBuilding(),game.getLowerListBuilding()
));
Thread thread = new Thread(handler);
thread.start();
clientHandlers.add(handler);
connectedPlayers++;
actionQueue.add(new ReconnectPlayer(username));
} else {
clientSocket.getOutputStream().write(-1);
clientSocket.close();
System.out.println("Player could not be added. Connection terminated.");
}
}
//reconnect a previously disconnected player
if (playerList.containsKey(username)
&& !playerList.get(username)) {
playerList.put(username, true);
System.out.println("Reconnected player: " + username);
ClientHandler handler = new ClientHandler(
username,
clientSocket,
clientSend,
clientReceive,
clientHandlers,
playerList,
actionQueue
);
clientSocket.getOutputStream().write(1);
pendingHeartbeat.put(username, handler);
Game game = controller.getModel();
handler.notifyMiniModel(new MiniModel(
game.getSlotMap(),
game.orderLogicCard,
game.getCurrentState(),
game.getPlayers(),
game.getAvailableTotems(),
game.getUpperListTribeCards(),game.getLowerListTribeCards(),game.getUpperListBuilding(),game.getLowerListBuilding()
));
Thread thread = new Thread(handler);
thread.start();
clientHandlers.add(handler);
connectedPlayers++;
actionQueue.add(new ReconnectPlayer(username));
} else {
clientSocket.getOutputStream().write(-1);
clientSocket.close();
System.out.println("Player could not be added. Connection terminated.");
}
}
} catch (IOException | ClassNotFoundException e) {
e.printStackTrace();
}
@@ -52,7 +52,7 @@ public class ServerLauncher {
BlockingQueue<NetworkEvent> actionQueue;
/** Game controller. Used to apply events */
GameController gameController;
final GameController gameController;
/** Server RMI. Handles RMI clients */
RMIServer serverRMI;
@@ -106,8 +106,10 @@ public class ServerLauncher {
*/
public boolean doFirstEvent() throws InterruptedException {
NetworkEvent event = actionQueue.take();
if(gameController.getModel()!=null && !gameController.getModel().getCurrentState().equals(GameStages.ENDED))
//Verify that the model exist and if it exists that's not ended
if(gameController.getModel()!=null && gameController.getModel().getCurrentState().getGameStage()!=GameStages.ENDED)
{
//if there is only one player ignore every event different by reconnection
if(disconnectionTimer!=null && event.getEventType() != EventType.RECONNECT_PLAYER)
{
event.setIsError(true);
@@ -115,16 +117,24 @@ public class ServerLauncher {
serverTCP.notifyAll(event);
return false;
}
//Cancel the timer if another player is reconnected , so there are more than one player
if (event.getEventType() == EventType.RECONNECT_PLAYER && disconnectionTimer != null && !disconnectionTimer.isDone()) {
disconnectionTimer.cancel(false);
disconnectionTimer = null;
}
int roundPrev=gameController.getModel().getCurrentState().getRound();
synchronized(gameController){
//applies the event and set if is an error
event.setIsError(!event.apply(gameController));
Game game=gameController.getModel();
//if the event wasn't an error , it will be sent to players
if(!event.getIsError())
{
if(!this.gameSave() ){
System.out.println("\n!!! Save failed !!!\n");
}
if(event.getEventType().equals(EventType.DISCONNECTED_PLAYER)&& game.getCurrentState().getGameStage().equals(GameStages.WAITING))
{
playerList.remove(event.getUsername());
@@ -132,11 +142,43 @@ public class ServerLauncher {
event.setData(game.getSlotMap(), game.orderLogicCard, game.getCurrentState(), game.getPlayers());
if (event.getEventType().equals(EventType.TOTEM_CHOICE)) {
((TotemChoice) event).setAvailableTotems(gameController.getModel().getAvailableTotems());
} else if (event.getEventType().equals(EventType.DISCONNECTED_PLAYER) && game.getCurrentState().equals(GameStages.WAITING)) {
} else if (event.getEventType().equals(EventType.DISCONNECTED_PLAYER) && game.getCurrentState().getGameStage().equals(GameStages.WAITING)) {
playerList.remove(event.getUsername());
}
}
if(game.getCurrentState().getGameStage() == GameStages.ENDED){
//if there is only one player the game will be suspended and starts the forfeit timer
if (event.getEventType().equals(EventType.DISCONNECTED_PLAYER) && playerList.values().stream().filter(x -> x).count() == 1&& game.getCurrentState().getGameStage() != GameStages.ENDED) {
if (disconnectionTimer != null && !disconnectionTimer.isDone()) {
disconnectionTimer.cancel(false);
}
//schedule endgame for forfeit
disconnectionTimer = timerExecutor.schedule(() -> {
game.EndGameForFeit();
serverRMI.notifyAll(new EndedGame(game.getSlotMap(), game.orderLogicCard, game.getCurrentState(),game.getPlayerStanding()));
serverTCP.notifyAll(new EndedGame(game.getSlotMap(), game.orderLogicCard, game.getCurrentState(),game.getPlayerStanding()));
System.out.println("Timer expired: no player reconnected in 60s.");
for(Map.Entry<String,Boolean> entry:playerList.entrySet()){
if(!entry.getValue())
playerList.remove(entry.getKey());
}
if(!this.deleteSave()){
System.out.println("\n!!! Couldn't delete save !!!\n");
}
disconnectionTimer = null;
}, 1, TimeUnit.MINUTES);
}
//if the round is changed send a new next round event , the previous event is ignored and directly sent the next round(also upper and lower lists updated)
if(game.getCurrentState().getRound()!=roundPrev)
{
ApplyNextRound nextRound=new ApplyNextRound(game.getSlotMap(), game.orderLogicCard, game.getCurrentState(),game.getPlayers(),game.getUpperListTribeCards(),game.getLowerListTribeCards(),game.getUpperListBuilding(),game.getLowerListBuilding());
serverRMI.notifyAll(nextRound);
serverTCP.notifyAll(nextRound);
}
// if the game ends after the event, delete the game. await that all players are disconnected and then create a new game
else if(game.getCurrentState().getGameStage().equals(GameStages.ENDED))
{
serverRMI.notifyAll(new EndedGame(game.getSlotMap(), game.orderLogicCard, game.getCurrentState(),game.getPlayerStanding()));
serverTCP.notifyAll(new EndedGame(game.getSlotMap(), game.orderLogicCard, game.getCurrentState(),game.getPlayerStanding()));
if(!this.deleteSave()){
System.out.println("\n!!! Couldn't delete save !!!\n");
}
@@ -147,48 +189,26 @@ public class ServerLauncher {
serverRMI.setServerCrashed(false);
serverTCP.setServerCrashed(false);
}
else if(!this.gameSave() ){
System.out.println("\n!!! Save failed !!!\n");
//notify the event
else {
serverRMI.notifyAll(event);
serverTCP.notifyAll(event);
}
}
serverRMI.notifyAll(event);
serverTCP.notifyAll(event);
if(game.getCurrentState().getRound()!=roundPrev)
{
ApplyNextRound nextRound=new ApplyNextRound(game.getSlotMap(), game.orderLogicCard, game.getCurrentState(),game.getPlayers(),game.getUpperListTribeCards(),game.getLowerListTribeCards(),game.getUpperListBuilding(),game.getLowerListBuilding());
serverRMI.notifyAll(nextRound);
serverTCP.notifyAll(nextRound);
}
else if(game.getCurrentState().getGameStage().equals(GameStages.ENDED))
{
serverRMI.notifyAll(new EndedGame(game.getSlotMap(), game.orderLogicCard, game.getCurrentState(),game.getPlayerStanding()));
serverTCP.notifyAll(new EndedGame(game.getSlotMap(), game.orderLogicCard, game.getCurrentState(),game.getPlayerStanding()));
}
if (event.getEventType().equals(EventType.DISCONNECTED_PLAYER) && playerList.values().stream().filter(x -> x).count() == 1&& game.getCurrentState().getGameStage() != GameStages.ENDED) {
if (disconnectionTimer != null && !disconnectionTimer.isDone()) {
disconnectionTimer.cancel(false);
}
disconnectionTimer = timerExecutor.schedule(() -> {
game.EndGameForFeit();
serverRMI.notifyAll(new EndedGame(game.getSlotMap(), game.orderLogicCard, game.getCurrentState(),game.getPlayerStanding()));
serverTCP.notifyAll(new EndedGame(game.getSlotMap(), game.orderLogicCard, game.getCurrentState(),game.getPlayerStanding()));
System.out.println("Timer expired: no player reconnected in 60s.");
if(!this.deleteSave()){
System.out.println("\n!!! Couldn't delete save !!!\n");
}
}, 1, TimeUnit.MINUTES);
}
return !event.getIsError();
}
}
else{
// removes disconneted players when the game is ended
if(event.getEventType().equals(EventType.DISCONNECTED_PLAYER))
{
playerList.remove(event.getUsername());
if(playerList.isEmpty())
{
gameController.setModel(null);
System.out.println("\n!!! Player list is now empty, ready for a new game init !!!\n");
}
return true;
}
return false;
}
@@ -253,12 +273,12 @@ public class ServerLauncher {
MiniModel miniModel;
synchronized (gameController) {
Game game = gameController.getModel();
miniModel= new MiniModel(game.getSlotMap(), game.orderLogicCard,game.getCurrentState(),game.getPlayers(), new ArrayList<>(List.of(Totems.values())),game.getUpperListTribeCards(),game.getLowerListTribeCards(),game.getUpperListBuilding(),game.getLowerListBuilding());
miniModel= new MiniModel(game.getSlotMap(), game.orderLogicCard,game.getCurrentState(),game.getPlayers(), game.getAvailableTotems(),game.getUpperListTribeCards(),game.getLowerListTribeCards(),game.getUpperListBuilding(),game.getLowerListBuilding());
}
serverRMI.notifyAll(miniModel);
serverTCP.notifyAll(miniModel);
view = new TUI(miniModel);
view.fullRender();
view.render();
}).start();
});
new Thread(()-> {
@@ -290,10 +310,10 @@ public class ServerLauncher {
while (true) {
try{
this.doFirstEvent();
if(view!=null)
{
view.fullRender();
}
// if(view!=null)
// {
// view.fullRender();
// }
}
catch(InterruptedException e){
Thread.currentThread().interrupt();