fix(multiplayer): recover assignment handoff

This commit is contained in:
Josh Creek
2026-09-02 18:47:00 +01:00
parent 84d27d82da
commit 91658fbc13
12 changed files with 147 additions and 15 deletions
+12 -7
View File
@@ -21,10 +21,14 @@ ON CONFLICT (scope, idempotency_key) DO NOTHING`
FROM idempotency_keys
WHERE scope = $1 AND idempotency_key = $2
FOR UPDATE`
QueueTicketSelectSQL = `SELECT ticket_id, player_id, playlist, state, client_build,
protocol_version, enqueued_at, expires_at, revision, predicted_rtt
FROM queue_tickets
WHERE ticket_id = $1 AND player_id = $2`
QueueTicketSelectSQL = `SELECT q.ticket_id, q.player_id, q.playlist, q.state, q.client_build,
q.protocol_version, q.enqueued_at, q.expires_at, q.revision, q.predicted_rtt,
COALESCE((SELECT mp.match_id FROM match_participants mp
WHERE mp.ticket_id = q.ticket_id AND mp.player_id = q.player_id
AND mp.participation_active
LIMIT 1), '')
FROM queue_tickets q
WHERE q.ticket_id = $1 AND q.player_id = $2`
QueueTicketHeartbeatSQL = `UPDATE queue_tickets SET revision = revision + 1,
expires_at = $4 + INTERVAL '30 seconds'
WHERE ticket_id = $1 AND player_id = $2 AND revision = $3
@@ -173,6 +177,7 @@ func CreateQueueTicket(ctx context.Context, db *sql.DB, ticketID, playerID, idem
type queueTicketRecord struct {
TicketID string `json:"ticket_id"`
PlayerID string `json:"player_id"`
MatchID string `json:"match_id,omitempty"`
Playlist string `json:"playlist"`
State string `json:"state"`
ClientBuild string `json:"client_build"`
@@ -230,7 +235,7 @@ func GetQueueTicket(ctx context.Context, db *sql.DB, playerID, ticketID string,
}
var record queueTicketRecord
var predictedRTT []byte
if err := db.QueryRowContext(ctx, QueueTicketSelectSQL, ticketID, playerID).Scan(&record.TicketID, &record.PlayerID, &record.Playlist, &record.State, &record.ClientBuild, &record.ProtocolVersion, &record.EnqueuedAt, &record.ExpiresAt, &record.Revision, &predictedRTT); err != nil {
if err := db.QueryRowContext(ctx, QueueTicketSelectSQL, ticketID, playerID).Scan(&record.TicketID, &record.PlayerID, &record.Playlist, &record.State, &record.ClientBuild, &record.ProtocolVersion, &record.EnqueuedAt, &record.ExpiresAt, &record.Revision, &predictedRTT, &record.MatchID); err != nil {
return domain.QueueTicket{}, err
}
if err := json.Unmarshal(predictedRTT, &record.PredictedRTT); err != nil {
@@ -305,9 +310,9 @@ func mutateQueueTicket(ctx context.Context, db *sql.DB, playerID, ticketID, idem
}
func queueTicketRecordFromDomain(ticket domain.QueueTicket) queueTicketRecord {
return queueTicketRecord{TicketID: ticket.TicketID, PlayerID: ticket.PlayerID, Playlist: string(ticket.Playlist), State: string(ticket.State), ClientBuild: ticket.Candidate.ClientBuild, ProtocolVersion: ticket.Candidate.ProtocolVersion, EnqueuedAt: ticket.EnqueuedAt, ExpiresAt: ticket.ExpiresAt, Revision: ticket.Revision, PredictedRTT: ticket.Candidate.PredictedRTT}
return queueTicketRecord{TicketID: ticket.TicketID, PlayerID: ticket.PlayerID, MatchID: ticket.MatchID, Playlist: string(ticket.Playlist), State: string(ticket.State), ClientBuild: ticket.Candidate.ClientBuild, ProtocolVersion: ticket.Candidate.ProtocolVersion, EnqueuedAt: ticket.EnqueuedAt, ExpiresAt: ticket.ExpiresAt, Revision: ticket.Revision, PredictedRTT: ticket.Candidate.PredictedRTT}
}
func queueTicketRecordToDomain(record queueTicketRecord) domain.QueueTicket {
candidate := domain.Candidate{TicketID: record.TicketID, PlayerID: record.PlayerID, Playlist: domain.Playlist(record.Playlist), ClientBuild: record.ClientBuild, ProtocolVersion: record.ProtocolVersion, EnqueuedAt: record.EnqueuedAt, PredictedRTT: record.PredictedRTT}
return domain.QueueTicket{TicketID: record.TicketID, PlayerID: record.PlayerID, Candidate: candidate, Playlist: domain.Playlist(record.Playlist), State: domain.State(record.State), Revision: record.Revision, EnqueuedAt: record.EnqueuedAt, ExpiresAt: record.ExpiresAt}
return domain.QueueTicket{TicketID: record.TicketID, PlayerID: record.PlayerID, MatchID: record.MatchID, Candidate: candidate, Playlist: domain.Playlist(record.Playlist), State: domain.State(record.State), Revision: record.Revision, EnqueuedAt: record.EnqueuedAt, ExpiresAt: record.ExpiresAt}
}