fix(multiplayer): reconcile authoritative initial connections

This commit is contained in:
Josh Creek
2026-09-03 00:02:04 +01:00
parent 781cbc35aa
commit aa446cfbfe
40 changed files with 809 additions and 87 deletions
+14 -6
View File
@@ -3,16 +3,18 @@ package store
import (
"context"
"database/sql"
"errors"
"fmt"
"time"
"github.com/cosmic-clash/cosmic-clash/server/domain"
)
const initialConnectCandidatesSQL = `SELECT match_id, playlist, created_at
const initialConnectCandidatesSQL = `SELECT match_id, playlist, initial_connect_ready_at
FROM matches
WHERE state IN ('ASSIGNMENT_READY', 'ASSIGNED', 'CONNECTING')
ORDER BY created_at, match_id
AND initial_connect_ready_at IS NOT NULL
ORDER BY initial_connect_ready_at, match_id
LIMIT $1`
const initialConnectHistorySQL = `SELECT starts_at
@@ -71,6 +73,12 @@ func ReconcileInitialConnect(ctx context.Context, db *sql.DB, now time.Time, lim
continue
}
if err := ApplyInitialConnectPlan(ctx, db, candidate.matchID, "initial-connect:"+candidate.matchID, plan, now); err != nil {
// A connection receipt or another maintenance replica may have
// changed the locked roster/state after our snapshot. Re-evaluate on
// the next bounded pass instead of killing the maintenance process.
if errors.Is(err, domain.ErrConflict) {
continue
}
return count, err
}
count++
@@ -79,7 +87,7 @@ func ReconcileInitialConnect(ctx context.Context, db *sql.DB, now time.Time, lim
}
func loadInitialConnectSnapshot(ctx context.Context, db *sql.DB, matchID string) ([]domain.ConnectParticipant, error) {
rows, err := db.QueryContext(ctx, `SELECT player_id, team, connected_at
rows, err := db.QueryContext(ctx, `SELECT player_id, team, slot, connected_at
FROM match_participants WHERE match_id = $1 AND participation_active ORDER BY player_id`, matchID)
if err != nil {
return nil, err
@@ -88,12 +96,12 @@ FROM match_participants WHERE match_id = $1 AND participation_active ORDER BY pl
var participants []domain.ConnectParticipant
for rows.Next() {
var playerID string
var team int
var team, slot int
var connectedAt sql.NullTime
if err := rows.Scan(&playerID, &team, &connectedAt); err != nil {
if err := rows.Scan(&playerID, &team, &slot, &connectedAt); err != nil {
return nil, err
}
participants = append(participants, domain.ConnectParticipant{PlayerID: playerID, Team: team, Connected: connectedAt.Valid})
participants = append(participants, domain.ConnectParticipant{PlayerID: playerID, Team: team, Slot: slot, Connected: connectedAt.Valid})
}
return participants, rows.Err()
}