Skip to content

Fix/47 #51

Merged
merged 6 commits into from
Apr 16, 2026
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,11 @@ public void removePlayerFromSession(
sessionRepository.removePlayerFromSession(sessionId, playerId, callback);
}

@Override
public void cancelSession(String sessionId, String endReason, SimpleCallback callback) {
sessionRepository.cancelSession(sessionId, endReason, callback);
}

@Override
public void deleteSession(String sessionId, DeleteSessionCallback callback) {
sessionRepository.deleteSession(sessionId, callback);
Expand All @@ -98,6 +103,11 @@ public void listenToSessionState(String sessionId, SessionStateListener listener
sessionRepository.listenToSessionState(sessionId, listener);
}

@Override
public void stopListeningToSessionState(String sessionId) {
sessionRepository.stopListeningToSessionState();
}

@Override
public void updatePlayerScore(String sessionId, String playerId, int score, SimpleCallback callback) {
sessionRepository.updatePlayerScore(sessionId, playerId, score, callback);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ public class FirestoreSessionRepository {
private static final String QUESTIONS = "Questions";

private final FirebaseFirestore firestore;
private ListenerRegistration sessionStateRegistration;

public FirestoreSessionRepository(FirebaseFirestore firestore) {
this.firestore = firestore;
Expand All @@ -32,9 +33,7 @@ public void gamePinExists(String gamePin, FirebaseGateway.GamePinExistsCallback
firestore.collection(SESSIONS)
.whereEqualTo("gamePin", gamePin)
.get()
.addOnSuccessListener(snapshot -> {
callback.onResult(!snapshot.isEmpty());
})
.addOnSuccessListener(snapshot -> callback.onResult(!snapshot.isEmpty()))
.addOnFailureListener(callback::onFailure);
}

Expand All @@ -52,6 +51,7 @@ public void createSession(
session.put("state", state);
session.put("currentRound", currentRound);
session.put("totalRounds", totalRounds);
session.put("endReason", FirebaseGateway.END_REASON_NONE);
session.put("createdAt", Timestamp.now());

firestore.collection(SESSIONS)
Expand Down Expand Up @@ -208,6 +208,22 @@ public void removePlayerFromSession(
.addOnFailureListener(callback::onFailure);
}

public void cancelSession(
String sessionId,
String endReason,
FirebaseGateway.SimpleCallback callback
) {
Map<String, Object> update = new HashMap<>();
update.put("state", FirebaseGateway.STATE_CANCELLED);
update.put("endReason", endReason);

firestore.collection(SESSIONS)
.document(sessionId)
.update(update)
.addOnSuccessListener(unused -> callback.onSuccess())
.addOnFailureListener(callback::onFailure);
}

public void deleteSession(
String sessionId,
FirebaseGateway.DeleteSessionCallback callback
Expand Down Expand Up @@ -307,7 +323,16 @@ public void getQuestions(
String songArtist = doc.getString("songArtist");
String previewUrl = doc.getString("previewUrl");
String albumArtUrl = doc.getString("albumArtUrl");
List<String> options = (List<String>) doc.get("options");
Object rawOptions = doc.get("options");
List<String> options = new ArrayList<>();

if (rawOptions instanceof List<?>) {
for (Object item : (List<?>) rawOptions) {
if (item instanceof String) {
options.add((String) item);
}
}
}
Long roundIndexLong = doc.getLong("roundIndex");
int roundIndex = roundIndexLong != null ? roundIndexLong.intValue() : 0;

Expand Down Expand Up @@ -335,23 +360,42 @@ public void updateSessionState(
.addOnFailureListener(callback::onFailure);
}

public void stopListeningToSessionState() {
if (sessionStateRegistration != null) {
sessionStateRegistration.remove();
sessionStateRegistration = null;
}
}

public void listenToSessionState(
String sessionId,
FirebaseGateway.SessionStateListener listener
) {
firestore.collection(SESSIONS)
sessionStateRegistration = firestore.collection(SESSIONS)
.document(sessionId)
.addSnapshotListener((snapshot, error) -> {
if (error != null) {
listener.onFailure(error);
return;
}
if (snapshot == null || !snapshot.exists()) return;

if (snapshot == null || !snapshot.exists()) {
listener.onFailure(new IllegalStateException("Session not found"));
return;
}

String state = snapshot.getString("state");
Long roundLong = snapshot.getLong("currentRound");
String endReason = snapshot.getString("endReason");

int currentRound = roundLong != null ? roundLong.intValue() : 0;

if (state != null) {
listener.onStateChanged(state, currentRound);
listener.onStateChanged(
state,
currentRound,
endReason != null ? endReason : ""
);
}
});
}
Expand Down
5 changes: 5 additions & 0 deletions core/src/main/java/group07/beatbattle/BeatBattle.java
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,11 @@ public void setLocalSession(String sessionId, boolean isHost) {
this.localIsHost = isHost;
}

public void clearLocalSession() {
this.localSessionId = null;
this.localIsHost = false;
}

public String getLocalSessionId() { return localSessionId; }
public boolean isLocalHost() { return localIsHost; }

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,14 @@

import com.badlogic.gdx.Gdx;

import java.util.List;

import group07.beatbattle.BeatBattle;
import group07.beatbattle.firebase.FirebaseGateway;
import group07.beatbattle.model.GameSession;
import group07.beatbattle.model.Leaderboard;
import group07.beatbattle.model.LeaderboardEntry;
import group07.beatbattle.model.Player;
import group07.beatbattle.states.GameOverState;
import group07.beatbattle.states.InRoundState;
import group07.beatbattle.states.StartState;
Expand All @@ -16,16 +19,51 @@ public class LeaderboardController {

private final BeatBattle game;
private final GameSession session;
private final Leaderboard leaderboard;

public LeaderboardController(BeatBattle game, GameSession session, Leaderboard leaderboard) {
private boolean playerListenerActive = false;
private Runnable onPlayersChangedCallback;

public LeaderboardController(BeatBattle game, GameSession session) {
this.game = game;
this.session = session;
this.leaderboard = leaderboard;
}

public Leaderboard getLeaderboard() {
return leaderboard;
return new Leaderboard(session.getPlayers());
}

public void startPlayerListener(Runnable onChanged) {
this.playerListenerActive = true;
this.onPlayersChangedCallback = onChanged;
String sessionId = game.getLocalSessionId();
if (sessionId == null || sessionId.isBlank()) return;

game.getFirebaseGateway().listenToPlayers(
sessionId,
new FirebaseGateway.PlayersListenerCallback() {
@Override
public void onPlayersChanged(List<Player> updatedPlayers) {
if (!playerListenerActive) return;
Gdx.app.postRunnable(() -> {
if (!playerListenerActive) return;
session.syncPlayers(updatedPlayers);
if (onPlayersChangedCallback != null) {
onPlayersChangedCallback.run();
}
});
}

@Override
public void onFailure(Exception e) {
Gdx.app.error("LeaderboardController", "Player listener failed: " + e.getMessage());
}
}
);
}

public void stopPlayerListener() {
playerListenerActive = false;
onPlayersChangedCallback = null;
}

/** Returns the leaderboard entry for the local player, or null if not found. */
Expand All @@ -35,7 +73,7 @@ public LeaderboardEntry getLocalPlayerEntry() {
localId = session.getPlayers().get(0).getId();
}
if (localId == null) return null;
for (LeaderboardEntry entry : leaderboard.getEntries()) {
for (LeaderboardEntry entry : getLeaderboard().getEntries()) {
if (entry.getPlayerId().equals(localId)) return entry;
}
return null;
Expand Down Expand Up @@ -65,7 +103,7 @@ public void onNextRound() {
final int newRound = session.getCurrentRoundIndex();
game.getFirebaseGateway().updateRoundState(
sessionId,
"in_round",
FirebaseGateway.STATE_IN_ROUND,
newRound,
new FirebaseGateway.SimpleCallback() {
@Override
Expand All @@ -89,16 +127,81 @@ private void launchNextRound() {
StateManager.getInstance().setState(new InRoundState(game, roundController));
}

public void onBackToMenu() {
public void onSessionCancelled() {
StateManager.getInstance().setState(new StartState(game, new LobbyController(game)));
}

public String getSessionId() {
return game.getLocalSessionId();
}

public void onBackToMenu() {
String sessionId = game.getLocalSessionId();
String playerId = game.getLocalPlayerId();

if (sessionId == null || sessionId.isBlank()) {
StateManager.getInstance().setState(new StartState(game, new LobbyController(game)));
return;
}

if (game.isLocalHost()) {
game.getFirebaseGateway().cancelSession(
sessionId,
FirebaseGateway.END_REASON_HOST_LEFT,
new FirebaseGateway.SimpleCallback() {
@Override
public void onSuccess() {
Gdx.app.postRunnable(() ->
StateManager.getInstance().setState(new StartState(game, new LobbyController(game)))
);
}

@Override
public void onFailure(Exception exception) {
Gdx.app.postRunnable(() ->
StateManager.getInstance().setState(new StartState(game, new LobbyController(game)))
);
}
}
);
} else {
if (playerId == null || playerId.isBlank()) {
game.clearLocalSession();
game.getFirebaseGateway().stopListeningToSessionState(sessionId);
StateManager.getInstance().setState(new StartState(game, new LobbyController(game)));
return;
}

game.clearLocalSession();
game.getFirebaseGateway().stopListeningToSessionState(sessionId);
game.getFirebaseGateway().removePlayerFromSession(
sessionId,
playerId,
new FirebaseGateway.RemovePlayerCallback() {
@Override
public void onSuccess() {
Gdx.app.postRunnable(() ->
StateManager.getInstance().setState(new StartState(game, new LobbyController(game)))
);
}

@Override
public void onFailure(Exception exception) {
Gdx.app.postRunnable(() ->
StateManager.getInstance().setState(new StartState(game, new LobbyController(game)))
);
}
}
);
}
}

public void onGameOver() {
String sessionId = game.getLocalSessionId();
if (game.isLocalHost() && sessionId != null && !sessionId.isBlank()) {
game.getFirebaseGateway().updateSessionState(
sessionId,
"game_over",
FirebaseGateway.STATE_GAME_OVER,
new FirebaseGateway.SimpleCallback() {
@Override
public void onSuccess() {
Expand Down
Loading
Loading