diff --git a/android/src/main/java/com/group14/regicidechess/android/AndroidFirebase.java b/android/src/main/java/com/group14/regicidechess/android/AndroidFirebase.java index b57bf72..4e311f6 100644 --- a/android/src/main/java/com/group14/regicidechess/android/AndroidFirebase.java +++ b/android/src/main/java/com/group14/regicidechess/android/AndroidFirebase.java @@ -22,6 +22,17 @@ public class AndroidFirebase implements FirebaseAPI, API { private final DatabaseReference db = FirebaseDatabase.getInstance().getReference(); + private final List cleanupActions = new ArrayList<>(); + + private ValueEventListener trackValue(DatabaseReference ref, ValueEventListener listener) { + cleanupActions.add(() -> ref.removeEventListener(listener)); + return listener; + } + + private ChildEventListener trackChild(DatabaseReference ref, ChildEventListener listener) { + cleanupActions.add(() -> ref.removeEventListener(listener)); + return listener; + } // ── API (Legacy) ────────────────────────────────────────────────────────── @@ -74,8 +85,8 @@ public void joinLobby(String gameId, Callback onSuccess, Callback @Override public void listenForOpponentReady(String gameId, Runnable onReady) { - db.child("lobbies").child(gameId).child("status") - .addValueEventListener(new ValueEventListener() { + DatabaseReference statusRef = db.child("lobbies").child(gameId).child("status"); + statusRef.addValueEventListener(trackValue(statusRef, new ValueEventListener() { @Override public void onDataChange(DataSnapshot snapshot) { if ("joined".equals(snapshot.getValue(String.class))) { @@ -84,7 +95,7 @@ public void onDataChange(DataSnapshot snapshot) { } } @Override public void onCancelled(DatabaseError e) {} - }); + })); } @Override @@ -94,8 +105,8 @@ public void startGame(String gameId) { @Override public void listenForGameStart(String gameId, Runnable onStart) { - db.child("lobbies").child(gameId).child("status") - .addValueEventListener(new ValueEventListener() { + DatabaseReference statusRef = db.child("lobbies").child(gameId).child("status"); + statusRef.addValueEventListener(trackValue(statusRef, new ValueEventListener() { @Override public void onDataChange(DataSnapshot snapshot) { if ("started".equals(snapshot.getValue(String.class))) { @@ -104,13 +115,13 @@ public void onDataChange(DataSnapshot snapshot) { } } @Override public void onCancelled(DatabaseError e) {} - }); + })); } // ── Setup ───────────────────────────────────────────────────────────────── @Override - public void confirmSetup(String gameId, boolean isWhite, int[][] board, Runnable onSuccess) { + public void confirmSetup(String gameId, boolean isWhite, int[][] board, Runnable onSuccess, Callback onError) { String player = isWhite ? "white" : "black"; DatabaseReference gameRef = db.child("games").child(gameId); @@ -141,11 +152,12 @@ public void onDataChange(DataSnapshot snapshot) { } onSuccess.run(); } - @Override public void onCancelled(DatabaseError e) { onSuccess.run(); } + @Override public void onCancelled(DatabaseError e) { onError.call(e.getMessage()); } }) ) + .addOnFailureListener(e -> onError.call(e.getMessage())) ) - .addOnFailureListener(e -> onSuccess.run()); + .addOnFailureListener(e -> onError.call(e.getMessage())); } @Override @@ -172,8 +184,8 @@ public void getOpponentBoard(String gameId, boolean localIsWhite, Callback onMove) { lastExistingKey[0] = child.getKey(); } - db.child("games").child(gameId).child("moves") - .addChildEventListener(new ChildEventListener() { + DatabaseReference movesRef = db.child("games").child(gameId).child("moves"); + movesRef.addChildEventListener(trackChild(movesRef, new ChildEventListener() { private boolean seenStart = (lastExistingKey[0] == null); @Override @@ -257,12 +269,12 @@ public void onChildAdded(DataSnapshot snapshot, String prev) { @Override public void onChildRemoved(DataSnapshot s) {} @Override public void onChildMoved (DataSnapshot s, String p) {} @Override public void onCancelled (DatabaseError e) {} - }); + })); }) .addOnFailureListener(e -> { // Fallback: attach listener without filtering (original behaviour) - db.child("games").child(gameId).child("moves") - .addChildEventListener(new ChildEventListener() { + DatabaseReference movesRef = db.child("games").child(gameId).child("moves"); + movesRef.addChildEventListener(trackChild(movesRef, new ChildEventListener() { @Override public void onChildAdded(DataSnapshot snapshot, String prev) { int fromCol = getInt(snapshot, "fromCol"); @@ -277,7 +289,7 @@ public void onChildAdded(DataSnapshot snapshot, String prev) { @Override public void onChildRemoved(DataSnapshot s) {} @Override public void onChildMoved (DataSnapshot s, String p) {} @Override public void onCancelled (DatabaseError e) {} - }); + })); }); } @@ -296,12 +308,13 @@ public void listenForHeartbeat(String gameId, boolean listenForWhite, String player = listenForWhite ? "white" : "black"; Handler handler = new Handler(Looper.getMainLooper()); Runnable timeoutRunnable = onTimeout::run; + cleanupActions.add(() -> handler.removeCallbacks(timeoutRunnable)); // Start timeout timer — reset each time a valid heartbeat arrives handler.postDelayed(timeoutRunnable, timeoutMs); - db.child("games").child(gameId).child("heartbeat").child(player) - .addValueEventListener(new ValueEventListener() { + DatabaseReference heartbeatRef = db.child("games").child(gameId).child("heartbeat").child(player); + heartbeatRef.addValueEventListener(trackValue(heartbeatRef, new ValueEventListener() { @Override public void onDataChange(DataSnapshot snapshot) { Long timestamp = snapshot.getValue(Long.class); @@ -319,7 +332,7 @@ public void onDataChange(DataSnapshot snapshot) { onHeartbeat.call(latency); } @Override public void onCancelled(DatabaseError e) {} - }); + })); } // ── Game over ───────────────────────────────────────────────────────────── @@ -331,8 +344,8 @@ public void signalGameOver(String gameId, String reason) { @Override public void listenForGameOver(String gameId, Callback onGameOver) { - db.child("games").child(gameId).child("gameOver") - .addValueEventListener(new ValueEventListener() { + DatabaseReference gameOverRef = db.child("games").child(gameId).child("gameOver"); + gameOverRef.addValueEventListener(trackValue(gameOverRef, new ValueEventListener() { @Override public void onDataChange(DataSnapshot snapshot) { String reason = snapshot.getValue(String.class); @@ -342,7 +355,7 @@ public void onDataChange(DataSnapshot snapshot) { } } @Override public void onCancelled(DatabaseError e) {} - }); + })); } // ───────────────────────────────────────────────────────────────────────── @@ -370,4 +383,10 @@ public void unconfirmSetup(String gameId, boolean isWhite, Runnable onSuccess, C }) .addOnFailureListener(e -> onError.call(e.getMessage())); } -} \ No newline at end of file + + @Override + public void removeAllListeners() { + for (Runnable cleanup : cleanupActions) cleanup.run(); + cleanupActions.clear(); + } +} diff --git a/core/src/main/java/com/group14/regicidechess/database/FirebaseAPI.java b/core/src/main/java/com/group14/regicidechess/database/FirebaseAPI.java index c88bd7b..212ba4d 100644 --- a/core/src/main/java/com/group14/regicidechess/database/FirebaseAPI.java +++ b/core/src/main/java/com/group14/regicidechess/database/FirebaseAPI.java @@ -38,7 +38,7 @@ public interface FirebaseAPI { * Saves this player's board layout and marks them as setup-ready. * Sets games/{gameId}/bothReady = true once BOTH players have called this. */ - void confirmSetup(String gameId, boolean isWhite, int[][] board, Runnable onSuccess); + void confirmSetup(String gameId, boolean isWhite, int[][] board, Runnable onSuccess, Callback onError); /** * Unconfirms the setup, marking this player as not ready. @@ -80,9 +80,11 @@ void listenForHeartbeat(String gameId, boolean listenForWhite, long timeoutMs, void signalGameOver(String gameId, String reason); void listenForGameOver(String gameId, Callback onGameOver); + void removeAllListeners(); + // ───────────────────────────────────────────────────────────────────────── interface Callback { void call(T value); } -} \ No newline at end of file +} diff --git a/core/src/main/java/com/group14/regicidechess/screens/game/GameNetworkHandler.java b/core/src/main/java/com/group14/regicidechess/screens/game/GameNetworkHandler.java index 5839faf..a30b012 100644 --- a/core/src/main/java/com/group14/regicidechess/screens/game/GameNetworkHandler.java +++ b/core/src/main/java/com/group14/regicidechess/screens/game/GameNetworkHandler.java @@ -5,8 +5,8 @@ import com.badlogic.gdx.math.Vector2; import com.badlogic.gdx.scenes.scene2d.ui.Image; import com.badlogic.gdx.scenes.scene2d.ui.Label; -import com.group14.regicidechess.database.DatabaseManager; import com.group14.regicidechess.network.CircuitBreaker; +import com.group14.regicidechess.database.FirebaseAPI; import com.group14.regicidechess.model.Move; import com.group14.regicidechess.model.Player; @@ -45,6 +45,7 @@ public interface Listener { private final String gameId; private final Player localPlayer; private final Listener listener; + private final FirebaseAPI api; // Connection UI refs — updated directly here to keep GameScreen clean private final Image connectionIcon; @@ -52,12 +53,13 @@ public interface Listener { private final CircuitBreaker circuitBreaker = new CircuitBreaker(3, 10_000L); public GameNetworkHandler(String gameId, Player localPlayer, Listener listener, - Image connectionIcon, Label connectionLabel) { + Image connectionIcon, Label connectionLabel, FirebaseAPI api) { this.gameId = gameId; this.localPlayer = localPlayer; this.listener = listener; this.connectionIcon = connectionIcon; this.connectionLabel = connectionLabel; + this.api = api; } // ───────────────────────────────────────────────────────────────────────── @@ -70,6 +72,10 @@ public void start() { startHeartbeat(); } + public void stop() { + DatabaseManager.getInstance().getApi().removeAllListeners(); + } + // ───────────────────────────────────────────────────────────────────────── // Outgoing // ───────────────────────────────────────────────────────────────────────── @@ -79,18 +85,18 @@ public void saveMove(Move move) { Gdx.app.log("GameNetworkHandler", "Circuit breaker OPEN — move send blocked"); return; } - DatabaseManager.getInstance().getApi().saveMove(gameId, move, () -> {}); + api.saveMove(gameId, move, () -> {}); } public void sendHeartbeat() { if (!circuitBreaker.allowRequest()) return; - DatabaseManager.getInstance().getApi().sendHeartbeat(gameId, localPlayer.isWhite()); + api.sendHeartbeat(gameId, localPlayer.isWhite()); } /** Signals that this player has forfeited. */ public void signalForfeit() { String loser = localPlayer.isWhite() ? "white" : "black"; - DatabaseManager.getInstance().getApi().signalGameOver(gameId, "forfeit:" + loser); + api.signalGameOver(gameId, "forfeit:" + loser); } // ───────────────────────────────────────────────────────────────────────── @@ -103,8 +109,7 @@ public void signalForfeit() { * Own echoed moves are filtered out via coords[4]. */ private void startOpponentMoveListener() { - DatabaseManager.getInstance().getApi() - .listenForOpponentMove(gameId, coords -> { + api.listenForOpponentMove(gameId, coords -> { boolean moveIsWhite = coords.length > 4 && coords[4] == 1; if (moveIsWhite == localPlayer.isWhite()) return; // own echo @@ -117,16 +122,13 @@ private void startOpponentMoveListener() { } private void startGameOverListener() { - DatabaseManager.getInstance().getApi() - .listenForGameOver(gameId, reason -> + api.listenForGameOver(gameId, reason -> Gdx.app.postRunnable(() -> listener.onGameOver(reason))); } private void startHeartbeat() { sendHeartbeat(); - - DatabaseManager.getInstance().getApi() - .listenForHeartbeat( + api.listenForHeartbeat( gameId, !localPlayer.isWhite(), HEARTBEAT_TIMEOUT_MS, @@ -165,4 +167,4 @@ private void updateConnectionUI(long latencyMs) { else connectionIcon.setColor(Color.RED); connectionLabel.setText(latencyMs + " ms"); } -} \ No newline at end of file +} diff --git a/core/src/main/java/com/group14/regicidechess/screens/game/GameScreen.java b/core/src/main/java/com/group14/regicidechess/screens/game/GameScreen.java index 14cd5e5..5bce68e 100644 --- a/core/src/main/java/com/group14/regicidechess/screens/game/GameScreen.java +++ b/core/src/main/java/com/group14/regicidechess/screens/game/GameScreen.java @@ -18,6 +18,7 @@ import com.badlogic.gdx.scenes.scene2d.utils.ChangeListener; import com.badlogic.gdx.utils.Align; import com.badlogic.gdx.utils.viewport.FitViewport; +import com.group14.regicidechess.database.FirebaseAPI; import com.group14.regicidechess.input.ScreenInputHandler; import com.group14.regicidechess.model.Board; import com.group14.regicidechess.model.Move; @@ -97,7 +98,8 @@ public class GameScreen implements Screen, // ───────────────────────────────────────────────────────────────────────── public GameScreen(Game game, SpriteBatch batch, - Board board, Player localPlayer, int boardSize, String gameId) { + Board board, Player localPlayer, int boardSize, String gameId, + FirebaseAPI api) { this.game = game; this.batch = batch; this.localPlayer = localPlayer; @@ -129,7 +131,7 @@ public GameScreen(Game game, SpriteBatch batch, overlayManager = new GameOverlayManager(stage, skin, localPlayer, this); networkHandler = new GameNetworkHandler(gameId, localPlayer, this, - connectionIcon, connectionLabel); + connectionIcon, connectionLabel, api); boardRenderer = new GameBoardRenderer(batch, localPlayer, boardSize); boardRenderer.computeGeometry(V_WIDTH, V_HEIGHT, TOP_BAR_HEIGHT, STATUS_BAR_HEIGHT); @@ -438,12 +440,14 @@ public void resize(int width, int height) { @Override public void hide() { inputHandler.clearObservers(); + networkHandler.stop(); inMatchState.exit(); } @Override public void dispose() { + networkHandler.stop(); stage.dispose(); boardRenderer.dispose(); } -} \ No newline at end of file +} diff --git a/core/src/main/java/com/group14/regicidechess/screens/lobby/LobbyFlowController.java b/core/src/main/java/com/group14/regicidechess/screens/lobby/LobbyFlowController.java index 93d7d20..882840a 100644 --- a/core/src/main/java/com/group14/regicidechess/screens/lobby/LobbyFlowController.java +++ b/core/src/main/java/com/group14/regicidechess/screens/lobby/LobbyFlowController.java @@ -2,7 +2,7 @@ package com.group14.regicidechess.screens.lobby; import com.badlogic.gdx.Gdx; -import com.group14.regicidechess.database.DatabaseManager; +import com.group14.regicidechess.states.LobbyState; /** * Manages Firebase listeners and flow between host and joiner. @@ -16,10 +16,12 @@ public interface FlowListener { void onError(String message); } + private final LobbyState lobbyState; private final FlowListener listener; private String activeGameId; - public LobbyFlowController(FlowListener listener) { + public LobbyFlowController(LobbyState lobbyState, FlowListener listener) { + this.lobbyState = lobbyState; this.listener = listener; } @@ -29,8 +31,7 @@ public LobbyFlowController(FlowListener listener) { */ public void listenForJoiner(String gameId) { this.activeGameId = gameId; - DatabaseManager.getInstance().getApi() - .listenForOpponentReady(gameId, + lobbyState.listenForOpponentReady(gameId, () -> Gdx.app.postRunnable(() -> { if (listener != null) { listener.onJoinerArrived(); @@ -43,7 +44,7 @@ public void listenForJoiner(String gameId) { * Writes status = "started" to Firebase. */ public void signalGameStart(String gameId) { - DatabaseManager.getInstance().getApi().startGame(gameId); + lobbyState.startGame(gameId); } /** @@ -52,8 +53,7 @@ public void signalGameStart(String gameId) { */ public void listenForGameStart(String gameId) { this.activeGameId = gameId; - DatabaseManager.getInstance().getApi() - .listenForGameStart(gameId, + lobbyState.listenForGameStart(gameId, () -> Gdx.app.postRunnable(() -> { if (listener != null) { listener.onGameStarted(); diff --git a/core/src/main/java/com/group14/regicidechess/screens/lobby/LobbyScreen.java b/core/src/main/java/com/group14/regicidechess/screens/lobby/LobbyScreen.java index 8259b72..52dfb98 100644 --- a/core/src/main/java/com/group14/regicidechess/screens/lobby/LobbyScreen.java +++ b/core/src/main/java/com/group14/regicidechess/screens/lobby/LobbyScreen.java @@ -64,7 +64,7 @@ public LobbyScreen(Game game, SpriteBatch batch, LobbyMode mode, Lobby lobby) { stateManager.setPrefetchedLobby(lobby); } - this.flowController = new LobbyFlowController(createFlowListener()); + this.flowController = new LobbyFlowController(stateManager.getLobbyState(), createFlowListener()); this.stateManager.setListener(createStateListener()); // LibGDX setup diff --git a/core/src/main/java/com/group14/regicidechess/screens/mainmenu/JoinGamePanel.java b/core/src/main/java/com/group14/regicidechess/screens/mainmenu/JoinGamePanel.java index 959ef26..384526a 100644 --- a/core/src/main/java/com/group14/regicidechess/screens/mainmenu/JoinGamePanel.java +++ b/core/src/main/java/com/group14/regicidechess/screens/mainmenu/JoinGamePanel.java @@ -9,6 +9,7 @@ import com.badlogic.gdx.scenes.scene2d.ui.TextField; import com.badlogic.gdx.scenes.scene2d.utils.ChangeListener; import com.group14.regicidechess.model.Lobby; +import com.group14.regicidechess.states.LobbyState; /** * Join game panel UI component. @@ -33,10 +34,10 @@ public interface JoinPanelListener { private boolean visible = false; - public JoinGamePanel(Skin skin, JoinPanelListener listener) { + public JoinGamePanel(Skin skin, LobbyState lobbyState, JoinPanelListener listener) { this.skin = skin; this.listener = listener; - this.validator = new LobbyValidator(createValidationListener()); + this.validator = new LobbyValidator(lobbyState, createValidationListener()); buildPanel(); } diff --git a/core/src/main/java/com/group14/regicidechess/screens/mainmenu/LobbyValidator.java b/core/src/main/java/com/group14/regicidechess/screens/mainmenu/LobbyValidator.java index 33d1d87..14177d8 100644 --- a/core/src/main/java/com/group14/regicidechess/screens/mainmenu/LobbyValidator.java +++ b/core/src/main/java/com/group14/regicidechess/screens/mainmenu/LobbyValidator.java @@ -2,8 +2,8 @@ package com.group14.regicidechess.screens.mainmenu; import com.badlogic.gdx.Gdx; -import com.group14.regicidechess.database.DatabaseManager; import com.group14.regicidechess.model.Lobby; +import com.group14.regicidechess.states.LobbyState; /** * Validates lobby existence in Firebase before navigating. @@ -17,10 +17,12 @@ public interface ValidationListener { void onValidationError(String message); } + private final LobbyState lobbyState; private final ValidationListener listener; private boolean isValidating = false; - public LobbyValidator(ValidationListener listener) { + public LobbyValidator(LobbyState lobbyState, ValidationListener listener) { + this.lobbyState = lobbyState; this.listener = listener; } @@ -48,7 +50,7 @@ public void validate(String gameId) { isValidating = true; - DatabaseManager.getInstance().getApi().fetchLobby(trimmedId, + lobbyState.validateLobby(trimmedId, // Lobby found lobby -> Gdx.app.postRunnable(() -> { isValidating = false; diff --git a/core/src/main/java/com/group14/regicidechess/screens/mainmenu/MainMenuScreen.java b/core/src/main/java/com/group14/regicidechess/screens/mainmenu/MainMenuScreen.java index dbe5e68..c9ab26c 100644 --- a/core/src/main/java/com/group14/regicidechess/screens/mainmenu/MainMenuScreen.java +++ b/core/src/main/java/com/group14/regicidechess/screens/mainmenu/MainMenuScreen.java @@ -13,6 +13,7 @@ import com.group14.regicidechess.model.Lobby; import com.group14.regicidechess.screens.lobby.LobbyMode; import com.group14.regicidechess.screens.lobby.LobbyScreen; +import com.group14.regicidechess.states.LobbyState; import com.group14.regicidechess.states.MainMenuState; import com.group14.regicidechess.utils.ResourceManager; @@ -36,6 +37,7 @@ public class MainMenuScreen implements Screen, ScreenInputHandler.ScreenInputObs // State and UI private final MainMenuState mainMenuState; + private final LobbyState lobbyState; private final MainMenuUI mainMenuUI; public MainMenuScreen(Game game, SpriteBatch batch) { @@ -44,6 +46,7 @@ public MainMenuScreen(Game game, SpriteBatch batch) { // Initialize state this.mainMenuState = new MainMenuState(); + this.lobbyState = new LobbyState(); mainMenuState.enter(); // LibGDX setup @@ -56,7 +59,7 @@ public MainMenuScreen(Game game, SpriteBatch batch) { inputHandler.addObserver(this); // Build UI with listener - this.mainMenuUI = new MainMenuUI(skin, createUIListener()); + this.mainMenuUI = new MainMenuUI(skin, lobbyState, createUIListener()); // Add UI to stage stage.addActor(mainMenuUI.build()); @@ -97,9 +100,7 @@ public void onBack() { private void fetchLobbyAndNavigate(String gameId) { mainMenuUI.showError("Loading..."); - com.group14.regicidechess.database.DatabaseManager.getInstance() - .getApi() - .fetchLobby(gameId, + lobbyState.validateLobby(gameId, // Success - lobby found lobby -> Gdx.app.postRunnable(() -> { mainMenuUI.clearError(); diff --git a/core/src/main/java/com/group14/regicidechess/screens/mainmenu/MainMenuUI.java b/core/src/main/java/com/group14/regicidechess/screens/mainmenu/MainMenuUI.java index 4222be7..ed1ab45 100644 --- a/core/src/main/java/com/group14/regicidechess/screens/mainmenu/MainMenuUI.java +++ b/core/src/main/java/com/group14/regicidechess/screens/mainmenu/MainMenuUI.java @@ -8,6 +8,7 @@ import com.badlogic.gdx.scenes.scene2d.ui.TextButton; import com.badlogic.gdx.scenes.scene2d.utils.ChangeListener; import com.badlogic.gdx.utils.Align; +import com.group14.regicidechess.states.LobbyState; /** * Builds and manages the main menu UI. @@ -30,10 +31,10 @@ public interface MainMenuUIListener { private TextButton joinBtn; private Label mainErrorLabel; - public MainMenuUI(Skin skin, MainMenuUIListener listener) { + public MainMenuUI(Skin skin, LobbyState lobbyState, MainMenuUIListener listener) { this.skin = skin; this.listener = listener; - this.joinPanel = new JoinGamePanel(skin, createJoinPanelListener()); + this.joinPanel = new JoinGamePanel(skin, lobbyState, createJoinPanelListener()); } public Table build() { diff --git a/core/src/main/java/com/group14/regicidechess/screens/setup/SetupFlowController.java b/core/src/main/java/com/group14/regicidechess/screens/setup/SetupFlowController.java index 99dc3f5..4d9e018 100644 --- a/core/src/main/java/com/group14/regicidechess/screens/setup/SetupFlowController.java +++ b/core/src/main/java/com/group14/regicidechess/screens/setup/SetupFlowController.java @@ -2,7 +2,6 @@ package com.group14.regicidechess.screens.setup; import com.badlogic.gdx.Gdx; -import com.group14.regicidechess.database.DatabaseManager; import com.group14.regicidechess.model.Board; import com.group14.regicidechess.model.Player; import com.group14.regicidechess.states.SetupState; @@ -65,11 +64,12 @@ public void confirm() { listener.onUploadComplete(); int[][] encoded = SetupBoardCodec.encode(setupState.getBoard()); - DatabaseManager.getInstance().getApi().confirmSetup( + setupState.confirmSetup( gameId, localPlayer.isWhite(), encoded, - this::listenForBothReady + this::listenForBothReady, + error -> Gdx.app.postRunnable(() -> listener.onError("Setup failed: " + error)) ); } @@ -83,7 +83,7 @@ public void unconfirm() { setupState.getPlayer().resetReady(); isConfirmed = false; - DatabaseManager.getInstance().getApi().unconfirmSetup( + setupState.unconfirmSetup( gameId, localPlayer.isWhite(), () -> Gdx.app.postRunnable(() -> { @@ -99,7 +99,7 @@ public void unconfirm() { } private void listenForBothReady() { - DatabaseManager.getInstance().getApi().listenForBothSetupReady( + setupState.listenForBothSetupReady( gameId, () -> Gdx.app.postRunnable(() -> { listener.onBothReady(); @@ -113,7 +113,7 @@ private void fetchOpponentBoardWithRetry() { } private void fetchOpponentBoard() { - DatabaseManager.getInstance().getApi().getOpponentBoard( + setupState.getOpponentBoard( gameId, localPlayer.isWhite(), opponentBoard -> Gdx.app.postRunnable(() -> { @@ -144,4 +144,4 @@ private void fetchOpponentBoard() { public boolean isConfirmed() { return isConfirmed; } -} \ No newline at end of file +} diff --git a/core/src/main/java/com/group14/regicidechess/screens/setup/SetupScreen.java b/core/src/main/java/com/group14/regicidechess/screens/setup/SetupScreen.java index d64d1f3..e490d1c 100644 --- a/core/src/main/java/com/group14/regicidechess/screens/setup/SetupScreen.java +++ b/core/src/main/java/com/group14/regicidechess/screens/setup/SetupScreen.java @@ -304,7 +304,8 @@ public void onOpponentBoardFetched(com.group14.regicidechess.model.Board finalBo finalBoard, localPlayer, setupState.getBoardSize(), - gameId)); + gameId, + setupState.getFirebaseApi())); } catch (Exception e) { Gdx.app.error("SetupScreen", "Error navigating to GameScreen: " + e.getMessage(), e); showStatus("Error starting game: " + e.getMessage()); diff --git a/core/src/main/java/com/group14/regicidechess/states/LobbyState.java b/core/src/main/java/com/group14/regicidechess/states/LobbyState.java index 660e7d5..66c884c 100644 --- a/core/src/main/java/com/group14/regicidechess/states/LobbyState.java +++ b/core/src/main/java/com/group14/regicidechess/states/LobbyState.java @@ -1,6 +1,7 @@ package com.group14.regicidechess.states; import com.group14.regicidechess.database.DatabaseManager; +import com.group14.regicidechess.database.FirebaseAPI; import com.group14.regicidechess.model.Lobby; import com.group14.regicidechess.model.Player; @@ -64,6 +65,22 @@ public void setPrefetchedLobby(Lobby prefetched) { } } + public void validateLobby(String gameId, FirebaseAPI.Callback onSuccess, FirebaseAPI.Callback onError) { + DatabaseManager.getInstance().getApi().fetchLobby(gameId, onSuccess, onError); + } + + public void listenForOpponentReady(String gameId, Runnable onReady) { + DatabaseManager.getInstance().getApi().listenForOpponentReady(gameId, onReady); + } + + public void startGame(String gameId) { + DatabaseManager.getInstance().getApi().startGame(gameId); + } + + public void listenForGameStart(String gameId, Runnable onStart) { + DatabaseManager.getInstance().getApi().listenForGameStart(gameId, onStart); + } + public Lobby getLobby() { return lobby; } public Player getPlayerOne() { return playerOne; } public Player getPlayerTwo() { return playerTwo; } diff --git a/core/src/main/java/com/group14/regicidechess/states/SetupState.java b/core/src/main/java/com/group14/regicidechess/states/SetupState.java index 46485ea..c48f641 100644 --- a/core/src/main/java/com/group14/regicidechess/states/SetupState.java +++ b/core/src/main/java/com/group14/regicidechess/states/SetupState.java @@ -1,6 +1,8 @@ // File: core/src/main/java/com/group14/regicidechess/states/SetupState.java package com.group14.regicidechess.states; +import com.group14.regicidechess.database.DatabaseManager; +import com.group14.regicidechess.database.FirebaseAPI; import com.group14.regicidechess.model.Board; import com.group14.regicidechess.model.Player; import com.group14.regicidechess.model.pieces.ChessPiece; @@ -155,6 +157,26 @@ private boolean isInHomeZone(int row) { public int getHomeRowMin() { return homeRowMin; } public int getHomeRowMax() { return homeRowMax; } + public FirebaseAPI getFirebaseApi() { + return DatabaseManager.getInstance().getApi(); + } + + public void confirmSetup(String gameId, boolean isWhite, int[][] board, Runnable onSuccess) { + DatabaseManager.getInstance().getApi().confirmSetup(gameId, isWhite, board, onSuccess); + } + + public void unconfirmSetup(String gameId, boolean isWhite, Runnable onSuccess, FirebaseAPI.Callback onError) { + DatabaseManager.getInstance().getApi().unconfirmSetup(gameId, isWhite, onSuccess, onError); + } + + public void listenForBothSetupReady(String gameId, Runnable onBothReady) { + DatabaseManager.getInstance().getApi().listenForBothSetupReady(gameId, onBothReady); + } + + public void getOpponentBoard(String gameId, boolean localIsWhite, FirebaseAPI.Callback onBoard) { + DatabaseManager.getInstance().getApi().getOpponentBoard(gameId, localIsWhite, onBoard); + } + /** * @deprecated Use isReadyForConfirm() for UI readiness, or isPlayerReady() for Firebase readiness. */