feat(multiplayer): publish allocation state events

This commit is contained in:
Josh Creek
2026-09-01 16:30:35 +01:00
parent ec82367c50
commit d15d16d593
8 changed files with 133 additions and 4 deletions
+40 -4
View File
@@ -5,6 +5,7 @@ import (
"context"
"crypto/sha256"
"database/sql"
"encoding/json"
"fmt"
"time"
@@ -45,7 +46,7 @@ const BindAllocatedMatchParticipantsSQL = `WITH bound AS (
SELECT 1 FROM allocations
WHERE allocation_id = $2 AND match_id = $1 AND server_id = $3 AND state = 'ALLOCATED'
)
RETURNING match_id
RETURNING match_id, revision
), participants AS (
SELECT mp.ticket_id, mp.player_id
FROM match_participants mp
@@ -77,7 +78,13 @@ const AdvanceServerRegistrationSQL = `WITH matched AS (
WHERE q.ticket_id = mp.ticket_id AND q.player_id = mp.player_id AND q.state = $3
RETURNING q.ticket_id
)
SELECT (SELECT count(*) FROM matched), (SELECT count(*) FROM match_participants WHERE match_id = $1), (SELECT count(*) FROM advanced)`
SELECT (SELECT count(*) FROM matched), (SELECT count(*) FROM match_participants WHERE match_id = $1), (SELECT count(*) FROM advanced), COALESCE((SELECT revision FROM matched), -1)`
const serverRegistrationParticipantIDsSQL = `SELECT player_id FROM match_participants WHERE match_id = $1 ORDER BY player_id`
const serverRegistrationOutboxSQL = `INSERT INTO outbox
(event_id, aggregate_type, aggregate_id, revision, event_type, payload)
VALUES ($1, 'match', $2, $3, 'state_changed', $4)`
const ServerRegistrationIdempotencyScope = "server.register"
@@ -120,13 +127,42 @@ func AdvanceServerRegistration(ctx context.Context, db *sql.DB, binding domain.W
return nil
}
var matched, participants, advanced int
if err := tx.QueryRowContext(ctx, AdvanceServerRegistrationSQL, binding.MatchID, binding.ServerID, from, to, binding.AllocationID, now, protocol).Scan(&matched, &participants, &advanced); err != nil {
var revision int64
if err := tx.QueryRowContext(ctx, AdvanceServerRegistrationSQL, binding.MatchID, binding.ServerID, from, to, binding.AllocationID, now, protocol).Scan(&matched, &participants, &advanced, &revision); err != nil {
return err
}
if matched != 1 || participants == 0 || advanced != participants {
return domain.ErrConflict
}
return nil
rows, err := tx.QueryContext(ctx, serverRegistrationParticipantIDsSQL, binding.MatchID)
if err != nil {
return err
}
playerIDs := make([]string, 0, participants)
for rows.Next() {
var playerID string
if err := rows.Scan(&playerID); err != nil {
rows.Close()
return err
}
playerIDs = append(playerIDs, playerID)
}
if err := rows.Err(); err != nil {
rows.Close()
return err
}
if err := rows.Close(); err != nil {
return err
}
payload, err := json.Marshal(map[string]any{
"event": "state_changed", "revision": revision, "resource_id": binding.MatchID,
"occurred_at": now, "state": string(to), "match_id": binding.MatchID, "player_ids": playerIDs,
})
if err != nil {
return err
}
_, err = tx.ExecContext(ctx, serverRegistrationOutboxSQL, fmt.Sprintf("match:%s:%d", binding.MatchID, revision), binding.MatchID, revision, payload)
return err
})
}