mirror of
https://github.com/jcreek/CosmicClash.git
synced 2026-09-16 03:42:03 +00:00
feat(multiplayer): persist live reconnect abandonments
This commit is contained in:
@@ -156,6 +156,38 @@ type Abandonment struct {
|
||||
AbandonedAt time.Time
|
||||
}
|
||||
|
||||
// ReconnectParticipant is the durable subset needed to evaluate an expired
|
||||
// live reconnect lease. Connected players are deliberately absent: only a
|
||||
// persisted disconnect can start a player-caused abandon clock.
|
||||
type ReconnectParticipant struct {
|
||||
PlayerID string
|
||||
DisconnectedAt time.Time
|
||||
}
|
||||
|
||||
// PlanRankedAbandonments turns expired durable reconnect leases into the
|
||||
// same rolling cooldown ladder used by pre-live ranked no-shows. Future
|
||||
// disconnect timestamps are ignored rather than penalised: they can only be
|
||||
// an infrastructure clock anomaly, not a player abandonment.
|
||||
func PlanRankedAbandonments(now time.Time, participants []ReconnectParticipant, priorAbandons map[string][]time.Time) ([]Abandonment, error) {
|
||||
if now.IsZero() {
|
||||
return nil, fmt.Errorf("invalid reconnect-abandonment time")
|
||||
}
|
||||
seen := make(map[string]bool, len(participants))
|
||||
result := make([]Abandonment, 0, len(participants))
|
||||
for _, participant := range participants {
|
||||
if participant.PlayerID == "" || participant.DisconnectedAt.IsZero() || seen[participant.PlayerID] {
|
||||
return nil, fmt.Errorf("invalid reconnect participant")
|
||||
}
|
||||
seen[participant.PlayerID] = true
|
||||
if now.Before(participant.DisconnectedAt) || now.Sub(participant.DisconnectedAt) <= RankedReconnectGrace {
|
||||
continue
|
||||
}
|
||||
result = append(result, Abandonment{PlayerID: participant.PlayerID, Cooldown: abandonCooldown(priorAbandons[participant.PlayerID], now), AbandonedAt: now})
|
||||
}
|
||||
sort.Slice(result, func(i, j int) bool { return result[i].PlayerID < result[j].PlayerID })
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// ExpireGrace marks every disconnected player whose 60-second reclaim window
|
||||
// has elapsed. The returned list is lexical for stable audit/event ordering.
|
||||
func (r *RankedConnections) ExpireGrace(now time.Time, priorAbandons map[string][]time.Time) []Abandonment {
|
||||
|
||||
Reference in New Issue
Block a user