feat(backend): per-tier, per-kind active-game limits with a guest funnel
CI / changes (pull_request) Successful in 5s
CI / unit (pull_request) Successful in 11s
CI / integration (pull_request) Successful in 20s
CI / ui (pull_request) Has been skipped
CI / conformance (pull_request) Successful in 11s
CI / gate (pull_request) Successful in 0s
CI / deploy (pull_request) Successful in 2m41s
CI / changes (pull_request) Successful in 5s
CI / unit (pull_request) Successful in 11s
CI / integration (pull_request) Successful in 20s
CI / ui (pull_request) Has been skipped
CI / conformance (pull_request) Successful in 11s
CI / gate (pull_request) Successful in 0s
CI / deploy (pull_request) Successful in 2m41s
Cap a player's simultaneous unfinished games per kind (vs_ai, random, friends) with independent guest and durable-account tiers, held in a new single-row backend.config table (-1 = unlimited) behind an in-memory cache and editable live in the admin console (/_gm/limits). Each game is tagged with games.game_kind on creation. This replaces the earlier flat MaxActiveQuickGames=10 combined cap: the per-tier/kind config is the single mechanism, enforced at the same handler gate (ensureUnderGameLimit by kind on lobby/enqueue) plus the durable friends cap in CreateInvitation. game.Service.AtGameLimit only resolves the tier and counts; the limit policy stays at the request edge. Guests are now refused friend requests, friend-code redemption, befriend-in-game and invitation creation outright (403 guest_forbidden) -- previously only the UI hid these. Admin: a kind column in both game lists and the config editor. Defaults: guest 1 vs_ai / 1 random / 0 friends; durable 10 / 10 / 10.
This commit is contained in:
@@ -65,6 +65,12 @@ func (svc *Service) IssueFriendCode(ctx context.Context, accountID uuid.UUID) (F
|
||||
// ErrRequestBlocked (a block stands between the pair). A redeem bypasses any prior
|
||||
// decline between the two: it clears the old row and writes a fresh friendship.
|
||||
func (svc *Service) RedeemFriendCode(ctx context.Context, redeemerID uuid.UUID, code string) (uuid.UUID, error) {
|
||||
// A guest cannot use friends: a durable-account feature (the UI hides it).
|
||||
if acc, err := svc.accounts.GetByID(ctx, redeemerID); err != nil {
|
||||
return uuid.UUID{}, err
|
||||
} else if acc.IsGuest {
|
||||
return uuid.UUID{}, ErrGuestForbidden
|
||||
}
|
||||
issuerID, codeID, err := svc.store.liveFriendCodeByHash(ctx, hashFriendCode(code), svc.now())
|
||||
if err != nil {
|
||||
return uuid.UUID{}, err
|
||||
|
||||
@@ -51,6 +51,13 @@ func (svc *Service) SendFriendRequest(ctx context.Context, requesterID, addresse
|
||||
if requesterID == addresseeID {
|
||||
return ErrSelfRelation
|
||||
}
|
||||
// A guest cannot use friends — friends are a durable-account feature; the UI hides the
|
||||
// flow, this is the server source of truth.
|
||||
if acc, err := svc.accounts.GetByID(ctx, requesterID); err != nil {
|
||||
return err
|
||||
} else if acc.IsGuest {
|
||||
return ErrGuestForbidden
|
||||
}
|
||||
iBlockThem, err := svc.store.blockExists(ctx, requesterID, addresseeID)
|
||||
if err != nil {
|
||||
return err
|
||||
|
||||
@@ -42,6 +42,12 @@ func (svc *Service) RequestInGame(ctx context.Context, requesterID, addresseeID,
|
||||
if requesterID == addresseeID {
|
||||
return ErrSelfRelation
|
||||
}
|
||||
// A guest cannot use friends: a durable-account feature (the UI hides it).
|
||||
if acc, err := svc.accounts.GetByID(ctx, requesterID); err != nil {
|
||||
return err
|
||||
} else if acc.IsGuest {
|
||||
return ErrGuestForbidden
|
||||
}
|
||||
isRobot, err := svc.accounts.IsRobot(ctx, addresseeID)
|
||||
if err != nil {
|
||||
return err
|
||||
|
||||
@@ -59,6 +59,9 @@ var (
|
||||
// ErrRequestBlocked is returned when the addressee does not accept friend
|
||||
// requests (their global toggle) or a block stands between the two accounts.
|
||||
ErrRequestBlocked = errors.New("social: the addressee is not accepting friend requests")
|
||||
// ErrGuestForbidden is returned when a guest attempts a durable-only action (send a friend
|
||||
// request, redeem a friend code); friends are a durable-account feature.
|
||||
ErrGuestForbidden = errors.New("social: guests cannot use friends")
|
||||
// ErrRequestNotFound is returned when no pending friend request matches.
|
||||
ErrRequestNotFound = errors.New("social: no pending friend request")
|
||||
// ErrNoSharedGame is returned when a friend request targets someone the
|
||||
|
||||
Reference in New Issue
Block a user