feat(multiplayer): model initial connect outcomes

This commit is contained in:
Josh Creek
2026-09-01 16:21:40 +01:00
parent 3dbecb0bd5
commit b7642ad2be
3 changed files with 72 additions and 0 deletions
+47
View File
@@ -32,6 +32,53 @@ type InitialConnectDecision struct {
Innocent []string
}
// InitialConnectPlan translates the policy decision into the authoritative
// lifecycle result a store/orchestrator must apply. Keeping this translation
// in domain prevents one caller from requeueing innocents while another leaves
// them stuck in an accepted ticket, and makes the bot branch explicit.
type InitialConnectPlan struct {
Action InitialConnectAction
MatchState State
Connected []string
NoShows []Abandonment
CasualLineup []CasualSlot
}
func PlanInitialConnect(playlist Playlist, readyAt, now time.Time, participants []ConnectParticipant, priorAbandons map[string][]time.Time) (InitialConnectPlan, error) {
decision, err := EvaluateInitialConnect(playlist, readyAt, now, participants, priorAbandons)
if err != nil {
return InitialConnectPlan{}, err
}
plan := InitialConnectPlan{
Action: decision.Action,
Connected: append([]string(nil), decision.Innocent...),
NoShows: append([]Abandonment(nil), decision.NoShows...),
}
switch decision.Action {
case InitialConnectWait:
return plan, nil
case InitialConnectCancel:
plan.MatchState = Cancelled
return plan, nil
case InitialConnectStartWithBot:
connected := make([]ConnectParticipant, 0, len(decision.Innocent))
for _, participant := range participants {
if participant.Connected {
connected = append(connected, participant)
}
}
lineup, err := BuildCasualLineup(connected)
if err != nil {
return InitialConnectPlan{}, err
}
plan.MatchState = Live
plan.CasualLineup = lineup
return plan, nil
default:
return InitialConnectPlan{}, fmt.Errorf("unsupported initial-connect action")
}
}
// EvaluateInitialConnect only decides pre-live admission. It never computes a
// game result or rating update; those remain unavailable until a match is
// genuinely live and produces an authoritative result.