Skip to content

Improve/batch answer submission #63

Merged
merged 4 commits into from
Apr 21, 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 @@ -129,6 +129,11 @@ public void submitAnswer(String sessionId, int roundIndex, String playerId, Simp
sessionRepository.submitAnswer(sessionId, roundIndex, playerId, callback);
}

@Override
public void submitAnswerAndScore(String sessionId, int roundIndex, String playerId, int score, SimpleCallback callback) {
sessionRepository.submitAnswerAndScore(sessionId, roundIndex, playerId, score, callback);
}

@Override
public void listenToRoundAnswerCount(String sessionId, int roundIndex, AnswerCountCallback callback) {
sessionRepository.listenToRoundAnswerCount(sessionId, roundIndex, callback);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -452,6 +452,41 @@ public void submitAnswer(
.addOnFailureListener(callback::onFailure);
}

public void submitAnswerAndScore(
String sessionId,
int roundIndex,
String playerId,
int score,
FirebaseGateway.SimpleCallback callback
) {
Map<String, Object> answerData = new HashMap<>();
answerData.put("roundIndex", roundIndex);
answerData.put("playerId", playerId);

Map<String, Object> scoreData = new HashMap<>();
scoreData.put("score", score);

WriteBatch batch = firestore.batch();
batch.set(
firestore.collection(SESSIONS)
.document(sessionId)
.collection("Answers")
.document(roundIndex + "_" + playerId),
answerData
);
batch.update(
firestore.collection(SESSIONS)
.document(sessionId)
.collection(PLAYERS)
.document(playerId),
scoreData
);

batch.commit()
.addOnSuccessListener(unused -> callback.onSuccess())
.addOnFailureListener(callback::onFailure);
}

public void listenToRoundAnswerCount(
String sessionId,
int roundIndex,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,13 @@ public interface LobbySessionCallback {

public LobbyController(BeatBattle game) {
this.game = game;
// Fire a no-op Firestore query on startup to pre-warm the gRPC connection.
// By the time the user navigates to the join screen and types a pin, the
// connection is fully established, eliminating the cold-start latency penalty.
game.getFirebaseGateway().gamePinExists("____", new FirebaseGateway.GamePinExistsCallback() {
@Override public void onResult(boolean exists) {}
@Override public void onFailure(Exception exception) {}
});
}

public void onCreateGame() {
Expand Down Expand Up @@ -125,13 +132,16 @@ public void onFailure(Exception exception) {

view.setStatusMessage("Joining session...");

final long p2StartMs = System.currentTimeMillis(); // P2 — remove after measuring
lobbyService.joinSession(
playerName,
gamePin,
new LobbyService.JoinSessionCallback() {
@Override
public void onSuccess(SessionJoinResult result) {
Gdx.app.postRunnable(() -> {
// P2 — remove after measuring
Gdx.app.log("P2_LATENCY", "join latency: " + (System.currentTimeMillis() - p2StartMs) + " ms");
game.setLocalPlayer(result.getPlayerId(), result.getPlayerName());
game.setLocalSession(result.getSessionId(), false);
game.setLocalGamePin(result.getGamePin());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,10 @@ public interface SessionCancelledCallback {
private int activePlayerCount;
private int currentAnswerCount = 0;

// P1 latency test — remove after measuring
private static int p1SubmissionCount = 0;
private long p1StartMs = 0;

public RoundController(BeatBattle game, Question question, GameSession session) {
this.game = game;
this.question = question;
Expand Down Expand Up @@ -116,26 +120,28 @@ public void onFailure(Exception exception) {
}

public void onAnswerSubmitted(String answer) {
p1StartMs = System.currentTimeMillis(); // P1 — remove after measuring
float answerTime = getTotalTime() - getTimeRemaining();
Player localPlayer = findLocalPlayer();
int score = 0;
if (localPlayer != null) {
AnswerSubmission submission = AnswerSubmission.create(localPlayer.getId(), answer, answerTime, question);
localPlayer.addScore(submission.getPointsAwarded());
submitScoreToFirebase(localPlayer.getId(), localPlayer.getScore());
score = localPlayer.getScore();
}

playerAnswered = true;
recordAnswerInFirebase();
submitAnswerAndScoreToFirebase(localPlayer != null ? localPlayer.getId() : null, score);
}

/** Called when the round timer runs out. Submits a no-answer if the player never responded, then transitions to the leaderboard. */
public void onRoundExpired() {
if (!playerAnswered) {
Player localPlayer = findLocalPlayer();
if (localPlayer != null) {
submitScoreToFirebase(localPlayer.getId(), localPlayer.getScore());
}
recordAnswerInFirebase(); // count as answered so listener is consistent
submitAnswerAndScoreToFirebase(
localPlayer != null ? localPlayer.getId() : null,
localPlayer != null ? localPlayer.getScore() : 0
);
}
transitionToLeaderboard();
}
Expand Down Expand Up @@ -274,19 +280,24 @@ private void maybeAdvanceToLeaderboard() {
}
}

private void recordAnswerInFirebase() {
private void submitAnswerAndScoreToFirebase(String playerId, int score) {
String sessionId = game.getLocalSessionId();
String playerId = game.getLocalPlayerId();
if (sessionId == null || sessionId.isBlank() || playerId == null) return;

game.getFirebaseGateway().submitAnswer(
game.getFirebaseGateway().submitAnswerAndScore(
sessionId,
question.getRoundIndex(),
playerId,
score,
new FirebaseGateway.SimpleCallback() {
@Override public void onSuccess() {}
@Override public void onSuccess() {
// P1 latency test — remove after measuring
long ms = System.currentTimeMillis() - p1StartMs;
p1SubmissionCount++;
Gdx.app.log("P1_LATENCY", "submission " + p1SubmissionCount + ": " + ms + " ms");
}
@Override public void onFailure(Exception e) {
Gdx.app.error("RoundController", "submitAnswer failed: " + e.getMessage());
Gdx.app.error("RoundController", "submitAnswerAndScore failed: " + e.getMessage());
}
}
);
Expand All @@ -305,24 +316,6 @@ private Player findLocalPlayer() {
return null;
}

private void submitScoreToFirebase(String playerId, int score) {
String sessionId = game.getLocalSessionId();
if (sessionId == null || sessionId.isBlank()) return;

game.getFirebaseGateway().updatePlayerScore(
sessionId,
playerId,
score,
new FirebaseGateway.SimpleCallback() {
@Override public void onSuccess() {}
@Override public void onFailure(Exception exception) {
Gdx.app.error("RoundController",
"Failed to submit score: " + exception.getMessage());
}
}
);
}

private void transitionToLeaderboard() {
if (transitioned) return;
transitioned = true;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,9 @@ void deleteSession(
/** Records that a player has submitted an answer for the given round. */
void submitAnswer(String sessionId, int roundIndex, String playerId, SimpleCallback callback);

/** Atomically records the answer and updates the player score in a single round-trip. */
void submitAnswerAndScore(String sessionId, int roundIndex, String playerId, int score, SimpleCallback callback);

/** Fires with the current answer count whenever a new answer is submitted for a round. */
void listenToRoundAnswerCount(String sessionId, int roundIndex, AnswerCountCallback callback);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,11 @@ public void submitAnswer(String sessionId, int roundIndex, String playerId, Simp
callback.onFailure(new UnsupportedOperationException("Firebase is not available on this platform"));
}

@Override
public void submitAnswerAndScore(String sessionId, int roundIndex, String playerId, int score, SimpleCallback callback) {
callback.onFailure(new UnsupportedOperationException("Firebase is not available on this platform"));
}

@Override
public void listenToRoundAnswerCount(String sessionId, int roundIndex, AnswerCountCallback callback) {
callback.onFailure(new UnsupportedOperationException("Firebase is not available on this platform"));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -222,6 +222,16 @@ public void updatePlayerScore(String sessionId, String playerId, int score,
callback.onSuccess();
}

@Override
public void submitAnswerAndScore(String sessionId, int roundIndex, String playerId,
int score, SimpleCallback callback) {
updatePlayerScore(sessionId, playerId, score, new SimpleCallback() {
@Override public void onSuccess() {}
@Override public void onFailure(Exception e) {}
});
submitAnswer(sessionId, roundIndex, playerId, callback);
}

@Override
public void submitAnswer(String sessionId, int roundIndex, String playerId,
SimpleCallback callback) {
Expand Down
Loading