Merge branch 'main' into 'feat/143-leave-lobby-command'
This commit is contained in:
@@ -30,6 +30,8 @@ public class GameController {
|
||||
|
||||
private int dealerIndex = 0;
|
||||
|
||||
private volatile Runnable onGameEndedCallback;
|
||||
|
||||
private static final int DEALER_OFFSET = 1;
|
||||
private static final int SMALL_BLIND_OFFSET = 1;
|
||||
private static final int BIG_BLIND_OFFSET = 2;
|
||||
@@ -45,6 +47,11 @@ public class GameController {
|
||||
this.engine = engine;
|
||||
}
|
||||
|
||||
/** Set a callback to be invoked when the game ends. */
|
||||
public void setOnGameEndedCallback(Runnable callback) {
|
||||
this.onGameEndedCallback = callback;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a player to the game with the specified name and initial chip count.
|
||||
*
|
||||
@@ -304,5 +311,12 @@ public class GameController {
|
||||
*/
|
||||
public void endGame() {
|
||||
engine.getState().setPhase(GamePhase.FINISHED);
|
||||
if (onGameEndedCallback != null) {
|
||||
try {
|
||||
onGameEndedCallback.run();
|
||||
} catch (RuntimeException e) {
|
||||
// Log or handle callback errors
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,6 +9,8 @@ import ch.unibas.dmi.dbis.cs108.casono.server.domain.game.rules.RuleEngine;
|
||||
import ch.unibas.dmi.dbis.cs108.casono.server.domain.game.state.GameState;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.CopyOnWriteArrayList;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
@@ -26,7 +28,9 @@ public class Lobby {
|
||||
private final LobbyId id;
|
||||
private final String name;
|
||||
private final List<String> playerNames = new CopyOnWriteArrayList<>();
|
||||
private final Set<String> absentPlayers = ConcurrentHashMap.newKeySet();
|
||||
private volatile GameController gameController;
|
||||
private volatile Runnable onGameEndedCallback;
|
||||
|
||||
public Lobby(LobbyId id, String name) {
|
||||
this.id = id;
|
||||
@@ -96,12 +100,91 @@ public class Lobby {
|
||||
|
||||
public void initGame(GameController controller) {
|
||||
this.gameController = controller;
|
||||
if (controller != null && onGameEndedCallback != null) {
|
||||
controller.setOnGameEndedCallback(onGameEndedCallback);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a callback to be invoked when the game in this lobby ends. This will be propagated to the
|
||||
* game controller when a game is started.
|
||||
*/
|
||||
public void setOnGameEndedCallback(Runnable callback) {
|
||||
this.onGameEndedCallback = callback;
|
||||
if (gameController != null) {
|
||||
gameController.setOnGameEndedCallback(callback);
|
||||
}
|
||||
}
|
||||
|
||||
public GameController getGameController() {
|
||||
return gameController;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a player is currently active (not absent) in the lobby.
|
||||
*
|
||||
* @param playerName the player to check
|
||||
* @return true if player is in the active player list
|
||||
*/
|
||||
public boolean isPlayerActive(String playerName) {
|
||||
return playerNames.contains(playerName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Mark a player as absent (left the lobby) while the game is still running. The player remains
|
||||
* in the lobby's mappings but is moved to the absent set.
|
||||
*
|
||||
* @param playerName the player to mark as absent
|
||||
* @return true if the player was active and is now absent
|
||||
*/
|
||||
public boolean leavePlayer(String playerName) {
|
||||
if (playerName == null) {
|
||||
return false;
|
||||
}
|
||||
boolean removed = playerNames.remove(playerName);
|
||||
if (removed) {
|
||||
absentPlayers.add(playerName);
|
||||
}
|
||||
return removed;
|
||||
}
|
||||
|
||||
/**
|
||||
* Try to restore an absent player back to active. Returns true if player was absent and is now
|
||||
* active again.
|
||||
*
|
||||
* @param playerName the player to restore
|
||||
* @return true if player was restored from absent
|
||||
*/
|
||||
public boolean restoreAbsentPlayer(String playerName) {
|
||||
if (playerName == null) {
|
||||
return false;
|
||||
}
|
||||
boolean wasAbsent = absentPlayers.remove(playerName);
|
||||
if (wasAbsent && !playerNames.contains(playerName)) {
|
||||
playerNames.add(playerName);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the set of absent (gone but not removed) players in this lobby.
|
||||
*
|
||||
* @return copy of absent players set
|
||||
*/
|
||||
public Set<String> getAbsentPlayers() {
|
||||
return Set.copyOf(absentPlayers);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if this lobby has any players (active or absent).
|
||||
*
|
||||
* @return true if playerNames or absentPlayers is non-empty
|
||||
*/
|
||||
public boolean hasAnyPlayers() {
|
||||
return !playerNames.isEmpty() || !absentPlayers.isEmpty();
|
||||
}
|
||||
|
||||
/**
|
||||
* Atomically add a player and, if the lobby reached {@code autoStartPlayers} and no game exists
|
||||
* yet, create and start a game. The operation is synchronized on the internal player list to
|
||||
@@ -139,6 +222,11 @@ public class Lobby {
|
||||
game.addPlayer(PlayerId.of(p), defaultStartChips);
|
||||
}
|
||||
|
||||
// Set the callback before starting the game
|
||||
if (onGameEndedCallback != null) {
|
||||
game.setOnGameEndedCallback(onGameEndedCallback);
|
||||
}
|
||||
|
||||
game.startGame();
|
||||
this.gameController = game;
|
||||
LOGGER.info(() -> "Auto-started game in lobby " + id.value());
|
||||
|
||||
+3
@@ -4,4 +4,7 @@ package ch.unibas.dmi.dbis.cs108.casono.server.domain.lobby;
|
||||
public interface LobbyEventListener {
|
||||
/** Called when a game is automatically started in the given lobby. */
|
||||
void onGameStarted(LobbyId lobbyId);
|
||||
|
||||
/** Called when a game in the given lobby ends (phase set to FINISHED). */
|
||||
void onGameEnded(LobbyId lobbyId);
|
||||
}
|
||||
|
||||
@@ -71,6 +71,22 @@ public class LobbyManager {
|
||||
if (lobby == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check if player was absent and try to restore
|
||||
if (lobby.getAbsentPlayers().contains(username)) {
|
||||
boolean restored = lobby.restoreAbsentPlayer(username);
|
||||
if (restored) {
|
||||
LOGGER.info(
|
||||
() ->
|
||||
"User '"
|
||||
+ username
|
||||
+ "' rejoined absent slot in lobby "
|
||||
+ lobbyId.value());
|
||||
playerToLobby.put(username, lobbyId);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
AddResult result =
|
||||
lobby.addPlayerAndMaybeStart(
|
||||
username, maxPlayersPerLobby, AUTO_START_PLAYERS, DEFAULT_START_CHIPS);
|
||||
@@ -94,6 +110,8 @@ public class LobbyManager {
|
||||
+ AUTO_START_PLAYERS
|
||||
+ " players; game started.");
|
||||
notifyGameStarted(lobbyId);
|
||||
// Set the game-ended callback
|
||||
notifySetGameEndedCallback(lobbyId);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@@ -121,6 +139,29 @@ public class LobbyManager {
|
||||
}
|
||||
}
|
||||
|
||||
private void notifySetGameEndedCallback(LobbyId lobbyId) {
|
||||
Lobby lobby = activeLobbies.get(lobbyId);
|
||||
if (lobby != null) {
|
||||
lobby.setOnGameEndedCallback(
|
||||
() -> {
|
||||
notifyGameEnded(lobbyId);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private void notifyGameEnded(LobbyId lobbyId) {
|
||||
for (LobbyEventListener l : listeners) {
|
||||
try {
|
||||
l.onGameEnded(lobbyId);
|
||||
} catch (RuntimeException e) {
|
||||
LOGGER.warning(
|
||||
() ->
|
||||
"Listener threw while handling game-end for lobby "
|
||||
+ lobbyId.value());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public boolean removePlayer(String username) {
|
||||
LobbyId id = playerToLobby.remove(username);
|
||||
if (id == null) {
|
||||
@@ -138,6 +179,33 @@ public class LobbyManager {
|
||||
return removed;
|
||||
}
|
||||
|
||||
/**
|
||||
* Mark a player as absent (left the lobby during a running game). The player is not removed but
|
||||
* moved to the absent set, allowing them to rejoin later without losing their slot.
|
||||
*
|
||||
* @param username the player to mark as absent
|
||||
* @param lobbyId the lobby they're leaving
|
||||
* @return true if the player was marked absent successfully
|
||||
*/
|
||||
public boolean leavePlayerFromLobby(String username, LobbyId lobbyId) {
|
||||
Lobby lobby = getLobby(lobbyId);
|
||||
if (lobby == null) {
|
||||
return false;
|
||||
}
|
||||
boolean left = lobby.leavePlayer(username);
|
||||
if (left) {
|
||||
LOGGER.info(
|
||||
() ->
|
||||
"User '"
|
||||
+ username
|
||||
+ "' left lobby "
|
||||
+ lobbyId.value()
|
||||
+ " (marked absent)");
|
||||
// Keep the playerToLobby mapping intact for rejoin
|
||||
}
|
||||
return left;
|
||||
}
|
||||
|
||||
public Collection<Lobby> getAllLobbies() {
|
||||
return activeLobbies.values();
|
||||
}
|
||||
@@ -169,6 +237,9 @@ public class LobbyManager {
|
||||
for (String username : removed.getPlayerNames()) {
|
||||
playerToLobby.remove(username);
|
||||
}
|
||||
for (String username : removed.getAbsentPlayers()) {
|
||||
playerToLobby.remove(username);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+172
@@ -0,0 +1,172 @@
|
||||
package ch.unibas.dmi.dbis.cs108.casono.server.domain.lobby;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
/** Unit tests for LobbyManager leave/rejoin functionality. */
|
||||
public class LobbyManagerTest {
|
||||
private LobbyManager lobbyManager;
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
lobbyManager = new LobbyManager(4);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testLeavePlayerFromLobbyMarkingAbsent() {
|
||||
LobbyId lobbyId = lobbyManager.createNewLobby("Test Lobby");
|
||||
assertNotNull(lobbyId);
|
||||
|
||||
// Add a player
|
||||
boolean joined = lobbyManager.addPlayerToLobby("Player1", lobbyId);
|
||||
assertTrue(joined);
|
||||
|
||||
Lobby lobby = lobbyManager.getLobby(lobbyId);
|
||||
assertTrue(lobby.getPlayerNames().contains("Player1"));
|
||||
assertTrue(lobby.getAbsentPlayers().isEmpty());
|
||||
|
||||
// Mark player as absent
|
||||
boolean left = lobbyManager.leavePlayerFromLobby("Player1", lobbyId);
|
||||
assertTrue(left);
|
||||
|
||||
// Player should be in absent set
|
||||
assertFalse(lobby.getPlayerNames().contains("Player1"));
|
||||
assertTrue(lobby.getAbsentPlayers().contains("Player1"));
|
||||
|
||||
// Player should still be in the playerToLobby mapping
|
||||
Lobby retrievedLobby = lobbyManager.getLobbyByUsername("Player1");
|
||||
assertEquals(lobbyId, retrievedLobby.getId());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testAbsentPlayerCanRejoin() {
|
||||
LobbyId lobbyId = lobbyManager.createNewLobby("Test Lobby");
|
||||
|
||||
// Add players to fill the lobby almost
|
||||
lobbyManager.addPlayerToLobby("Player1", lobbyId);
|
||||
lobbyManager.addPlayerToLobby("Player2", lobbyId);
|
||||
lobbyManager.addPlayerToLobby("Player3", lobbyId);
|
||||
|
||||
Lobby lobby = lobbyManager.getLobby(lobbyId);
|
||||
assertEquals(3, lobby.getPlayerNames().size());
|
||||
|
||||
// Player1 leaves
|
||||
lobbyManager.leavePlayerFromLobby("Player1", lobbyId);
|
||||
assertEquals(2, lobby.getPlayerNames().size());
|
||||
assertEquals(1, lobby.getAbsentPlayers().size());
|
||||
|
||||
// Player1 tries to rejoin - should succeed without error
|
||||
boolean rejoined = lobbyManager.addPlayerToLobby("Player1", lobbyId);
|
||||
assertTrue(rejoined);
|
||||
|
||||
assertEquals(3, lobby.getPlayerNames().size());
|
||||
assertEquals(0, lobby.getAbsentPlayers().size());
|
||||
assertTrue(lobby.getPlayerNames().contains("Player1"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testAbsentPlayerDoesNotCountAsFullLobby() {
|
||||
LobbyId lobbyId = lobbyManager.createNewLobby("Test Lobby");
|
||||
|
||||
// Fill lobby completely
|
||||
lobbyManager.addPlayerToLobby("P1", lobbyId);
|
||||
lobbyManager.addPlayerToLobby("P2", lobbyId);
|
||||
lobbyManager.addPlayerToLobby("P3", lobbyId);
|
||||
lobbyManager.addPlayerToLobby("P4", lobbyId);
|
||||
|
||||
Lobby lobby = lobbyManager.getLobby(lobbyId);
|
||||
assertEquals(4, lobby.getPlayerNames().size());
|
||||
|
||||
// P1 leaves
|
||||
lobbyManager.leavePlayerFromLobby("P1", lobbyId);
|
||||
assertEquals(3, lobby.getPlayerNames().size());
|
||||
assertEquals(1, lobby.getAbsentPlayers().size());
|
||||
|
||||
// P1 can rejoin without "LOBBY_FULL" error
|
||||
boolean rejoined = lobbyManager.addPlayerToLobby("P1", lobbyId);
|
||||
assertTrue(rejoined);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testPlayerToLobbyMappingPreservedAfterLeave() {
|
||||
LobbyId lobbyId = lobbyManager.createNewLobby("Test Lobby");
|
||||
lobbyManager.addPlayerToLobby("Player1", lobbyId);
|
||||
|
||||
// Verify mapping exists
|
||||
Lobby beforeLeave = lobbyManager.getLobbyByUsername("Player1");
|
||||
assertEquals(lobbyId, beforeLeave.getId());
|
||||
|
||||
// Player leaves
|
||||
lobbyManager.leavePlayerFromLobby("Player1", lobbyId);
|
||||
|
||||
// Mapping should still exist for rejoin
|
||||
Lobby afterLeave = lobbyManager.getLobbyByUsername("Player1");
|
||||
assertEquals(lobbyId, afterLeave.getId());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testRemoveLobbyCleanupsBothActiveAndAbsentPlayers() {
|
||||
LobbyId lobbyId = lobbyManager.createNewLobby("Test Lobby");
|
||||
|
||||
lobbyManager.addPlayerToLobby("P1", lobbyId);
|
||||
lobbyManager.addPlayerToLobby("P2", lobbyId);
|
||||
lobbyManager.leavePlayerFromLobby("P1", lobbyId);
|
||||
|
||||
// Verify both are in lobby
|
||||
Lobby lobby = lobbyManager.getLobby(lobbyId);
|
||||
assertTrue(lobby.getPlayerNames().contains("P2"));
|
||||
assertTrue(lobby.getAbsentPlayers().contains("P1"));
|
||||
|
||||
// Remove lobby
|
||||
lobbyManager.removeLobby(lobbyId);
|
||||
|
||||
// Both players should be removed from playerToLobby
|
||||
assertNull(lobbyManager.getLobbyByUsername("P1"));
|
||||
assertNull(lobbyManager.getLobbyByUsername("P2"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testLeaveNonExistentPlayerFromLobby() {
|
||||
LobbyId lobbyId = lobbyManager.createNewLobby("Test Lobby");
|
||||
|
||||
// Try to remove a player that was never added
|
||||
boolean left = lobbyManager.leavePlayerFromLobby("NonExistent", lobbyId);
|
||||
assertFalse(left);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testLeavePlayerFromNonExistentLobby() {
|
||||
LobbyId nonExistentId = LobbyId.of(999);
|
||||
|
||||
// Try to remove from non-existent lobby
|
||||
boolean left = lobbyManager.leavePlayerFromLobby("Player1", nonExistentId);
|
||||
assertFalse(left);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testGameStartedWithAbsentPlayerTracking() {
|
||||
// This test verifies that when a game starts with 4 players,
|
||||
// the absent players tracking is initialized and works
|
||||
LobbyId lobbyId = lobbyManager.createNewLobby("Test Lobby");
|
||||
|
||||
lobbyManager.addPlayerToLobby("P1", lobbyId);
|
||||
lobbyManager.addPlayerToLobby("P2", lobbyId);
|
||||
lobbyManager.addPlayerToLobby("P3", lobbyId);
|
||||
// Game should start when 4th player joins
|
||||
lobbyManager.addPlayerToLobby("P4", lobbyId);
|
||||
|
||||
Lobby lobby = lobbyManager.getLobby(lobbyId);
|
||||
assertNotNull(lobby.getGameController(), "Game should be started");
|
||||
assertEquals(4, lobby.getPlayerNames().size());
|
||||
assertEquals(0, lobby.getAbsentPlayers().size());
|
||||
|
||||
// P1 leaves during game
|
||||
boolean left = lobbyManager.leavePlayerFromLobby("P1", lobbyId);
|
||||
assertTrue(left);
|
||||
|
||||
assertEquals(3, lobby.getPlayerNames().size());
|
||||
assertEquals(1, lobby.getAbsentPlayers().size());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,195 @@
|
||||
package ch.unibas.dmi.dbis.cs108.casono.server.domain.lobby;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
import ch.unibas.dmi.dbis.cs108.casono.server.domain.game.GameController;
|
||||
import ch.unibas.dmi.dbis.cs108.casono.server.domain.game.engine.GameEngine;
|
||||
import ch.unibas.dmi.dbis.cs108.casono.server.domain.game.engine.RoundManager;
|
||||
import ch.unibas.dmi.dbis.cs108.casono.server.domain.game.engine.TurnManager;
|
||||
import ch.unibas.dmi.dbis.cs108.casono.server.domain.game.rules.RuleEngine;
|
||||
import ch.unibas.dmi.dbis.cs108.casono.server.domain.game.state.GameState;
|
||||
import java.util.ArrayList;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
/** Unit tests for Lobby absent players tracking functionality. */
|
||||
public class LobbyTest {
|
||||
private Lobby lobby;
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
lobby = new Lobby(LobbyId.of(1), "Test Lobby");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testAddPlayerSuccessfully() {
|
||||
boolean added = lobby.addPlayer("Player1", 4);
|
||||
assertTrue(added);
|
||||
assertEquals(1, lobby.getPlayerNames().size());
|
||||
assertTrue(lobby.getPlayerNames().contains("Player1"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testAddPlayerToFullLobby() {
|
||||
lobby.addPlayer("Player1", 4);
|
||||
lobby.addPlayer("Player2", 4);
|
||||
lobby.addPlayer("Player3", 4);
|
||||
lobby.addPlayer("Player4", 4);
|
||||
|
||||
// Try to add 5th player to a 4-player lobby
|
||||
boolean added = lobby.addPlayer("Player5", 4);
|
||||
assertFalse(added);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testLeavePlayerMovesToAbsent() {
|
||||
lobby.addPlayer("Player1", 4);
|
||||
assertTrue(lobby.getPlayerNames().contains("Player1"));
|
||||
assertTrue(lobby.getAbsentPlayers().isEmpty());
|
||||
|
||||
// Player leaves
|
||||
boolean left = lobby.leavePlayer("Player1");
|
||||
assertTrue(left);
|
||||
|
||||
// Player should be in absent set
|
||||
assertFalse(lobby.getPlayerNames().contains("Player1"));
|
||||
assertTrue(lobby.getAbsentPlayers().contains("Player1"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testRestoreAbsentPlayerSuccessfully() {
|
||||
lobby.addPlayer("Player1", 4);
|
||||
lobby.leavePlayer("Player1");
|
||||
|
||||
// Restore the absent player
|
||||
boolean restored = lobby.restoreAbsentPlayer("Player1");
|
||||
assertTrue(restored);
|
||||
|
||||
// Player should be back in active list
|
||||
assertTrue(lobby.getPlayerNames().contains("Player1"));
|
||||
assertFalse(lobby.getAbsentPlayers().contains("Player1"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testRestoreAbsentPlayerCannotRestoreTwice() {
|
||||
lobby.addPlayer("Player1", 4);
|
||||
lobby.leavePlayer("Player1");
|
||||
|
||||
boolean restored1 = lobby.restoreAbsentPlayer("Player1");
|
||||
assertTrue(restored1);
|
||||
|
||||
// Try to restore again - should fail
|
||||
boolean restored2 = lobby.restoreAbsentPlayer("Player1");
|
||||
assertFalse(restored2);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testHasAnyPlayersWithActiveOnly() {
|
||||
lobby.addPlayer("Player1", 4);
|
||||
assertTrue(lobby.hasAnyPlayers());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testHasAnyPlayersWithAbsentOnly() {
|
||||
lobby.addPlayer("Player1", 4);
|
||||
lobby.leavePlayer("Player1");
|
||||
assertTrue(lobby.hasAnyPlayers());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testHasAnyPlayersEmpty() {
|
||||
assertFalse(lobby.hasAnyPlayers());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testIsPlayerActive() {
|
||||
lobby.addPlayer("Player1", 4);
|
||||
assertTrue(lobby.isPlayerActive("Player1"));
|
||||
|
||||
lobby.leavePlayer("Player1");
|
||||
assertFalse(lobby.isPlayerActive("Player1"));
|
||||
|
||||
lobby.restoreAbsentPlayer("Player1");
|
||||
assertTrue(lobby.isPlayerActive("Player1"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testMultiplePlayersLeaveAndRejoin() {
|
||||
// Add 4 players
|
||||
lobby.addPlayer("P1", 4);
|
||||
lobby.addPlayer("P2", 4);
|
||||
lobby.addPlayer("P3", 4);
|
||||
lobby.addPlayer("P4", 4);
|
||||
|
||||
// All active
|
||||
assertEquals(4, lobby.getPlayerNames().size());
|
||||
assertEquals(0, lobby.getAbsentPlayers().size());
|
||||
|
||||
// P1 and P2 leave
|
||||
lobby.leavePlayer("P1");
|
||||
lobby.leavePlayer("P2");
|
||||
|
||||
assertEquals(2, lobby.getPlayerNames().size());
|
||||
assertEquals(2, lobby.getAbsentPlayers().size());
|
||||
|
||||
// P1 comes back
|
||||
lobby.restoreAbsentPlayer("P1");
|
||||
|
||||
assertEquals(3, lobby.getPlayerNames().size());
|
||||
assertEquals(1, lobby.getAbsentPlayers().size());
|
||||
assertTrue(lobby.getPlayerNames().contains("P1"));
|
||||
assertTrue(lobby.getAbsentPlayers().contains("P2"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testGameEndedCallbackInvoked() {
|
||||
// Create a simple game setup
|
||||
GameState state = new GameState();
|
||||
GameEngine engine =
|
||||
new GameEngine(
|
||||
state,
|
||||
new RuleEngine(new ArrayList<>()),
|
||||
new RoundManager(),
|
||||
new TurnManager());
|
||||
GameController gameController = new GameController(engine);
|
||||
|
||||
// Track if callback was invoked
|
||||
boolean[] callbackInvoked = {false};
|
||||
|
||||
// Set callback
|
||||
lobby.setOnGameEndedCallback(() -> callbackInvoked[0] = true);
|
||||
|
||||
// Simulate game ending
|
||||
gameController.endGame();
|
||||
// Callback should NOT be invoked yet because it's not registered in the
|
||||
// controller
|
||||
|
||||
// Now register the callback in the controller
|
||||
gameController.setOnGameEndedCallback(() -> callbackInvoked[0] = true);
|
||||
gameController.endGame();
|
||||
|
||||
assertTrue(callbackInvoked[0], "Game ended callback should have been invoked");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testRenamePlayerInActiveList() {
|
||||
lobby.addPlayer("OldName", 4);
|
||||
assertTrue(lobby.getPlayerNames().contains("OldName"));
|
||||
|
||||
boolean renamed = lobby.renamePlayer("OldName", "NewName");
|
||||
assertTrue(renamed);
|
||||
|
||||
assertFalse(lobby.getPlayerNames().contains("OldName"));
|
||||
assertTrue(lobby.getPlayerNames().contains("NewName"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testRenamePlayerCannotRenameIfAbsent() {
|
||||
lobby.addPlayer("Player1", 4);
|
||||
lobby.leavePlayer("Player1");
|
||||
|
||||
// Should NOT be able to rename an absent player directly
|
||||
boolean renamed = lobby.renamePlayer("Player1", "Player2");
|
||||
assertFalse(renamed);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user