feat: game lobby service

This commit is contained in:
Ilia Denisov
2026-04-25 23:20:55 +02:00
committed by GitHub
parent 32dc29359a
commit 48b0056b49
336 changed files with 57074 additions and 1418 deletions
@@ -15,33 +15,66 @@ import (
"galaxy/user/internal/service/shared"
)
// limitCatalogEntry stores one frozen default quota for free and paid
// entitlement states.
// limitCatalogEntry stores the frozen default quota for every tariff plan
// plus the toggle that decides whether a `free` default is materialized at
// all.
type limitCatalogEntry struct {
code policy.LimitCode
freeValue int
paidValue int
monthlyValue int
yearlyValue int
lifetimeValue int
freeEnabled bool
}
// planValue returns the frozen default quota for plan.
func (entry limitCatalogEntry) planValue(plan entitlement.PlanCode) int {
switch plan {
case entitlement.PlanCodePaidMonthly:
return entry.monthlyValue
case entitlement.PlanCodePaidYearly:
return entry.yearlyValue
case entitlement.PlanCodePaidLifetime:
return entry.lifetimeValue
default:
return entry.freeValue
}
}
// limitCatalog stores the frozen lobby-facing effective limit defaults used
// to materialize numeric quotas from the current entitlement state.
// to materialize numeric quotas from the current entitlement state. Paid
// plans share the same default unless stated otherwise; per-plan values
// diverge only for `max_registered_race_names`.
var limitCatalog = []limitCatalogEntry{
{
code: policy.LimitCodeMaxOwnedPrivateGames,
paidValue: 3,
code: policy.LimitCodeMaxOwnedPrivateGames,
monthlyValue: 3,
yearlyValue: 3,
lifetimeValue: 3,
},
{
code: policy.LimitCodeMaxPendingPublicApplications,
freeValue: 3,
paidValue: 10,
freeEnabled: true,
code: policy.LimitCodeMaxPendingPublicApplications,
freeValue: 3,
monthlyValue: 10,
yearlyValue: 10,
lifetimeValue: 10,
freeEnabled: true,
},
{
code: policy.LimitCodeMaxActiveGameMemberships,
freeValue: 3,
paidValue: 10,
freeEnabled: true,
code: policy.LimitCodeMaxActiveGameMemberships,
freeValue: 3,
monthlyValue: 10,
yearlyValue: 10,
lifetimeValue: 10,
freeEnabled: true,
},
{
code: policy.LimitCodeMaxRegisteredRaceNames,
freeValue: 1,
monthlyValue: 2,
yearlyValue: 6,
lifetimeValue: 0,
freeEnabled: true,
},
}
@@ -268,7 +301,7 @@ func (service *SnapshotReader) Execute(
result.Exists = true
result.Entitlement = entitlementSnapshotView(entitlementSnapshot)
result.ActiveSanctions = lobbyRelevantSanctionViews(activeSanctions)
result.EffectiveLimits = materializeEffectiveLimits(entitlementSnapshot.IsPaid, activeLimits)
result.EffectiveLimits = materializeEffectiveLimits(entitlementSnapshot.PlanCode, activeLimits)
result.Markers = deriveEligibilityMarkers(entitlementSnapshot.IsPaid, activeSanctions)
return result, nil
@@ -308,22 +341,20 @@ func lobbyRelevantSanctionViews(records []policy.SanctionRecord) []ActiveSanctio
return views
}
func materializeEffectiveLimits(isPaid bool, overrides []policy.LimitRecord) []EffectiveLimitView {
func materializeEffectiveLimits(plan entitlement.PlanCode, overrides []policy.LimitRecord) []EffectiveLimitView {
overrideValues := make(map[policy.LimitCode]int, len(overrides))
for _, record := range overrides {
overrideValues[record.LimitCode] = record.Value
}
isPaid := plan.IsPaid()
limits := make([]EffectiveLimitView, 0, len(limitCatalog))
for _, entry := range limitCatalog {
if !isPaid && !entry.freeEnabled {
continue
}
value := entry.freeValue
if isPaid {
value = entry.paidValue
}
value := entry.planValue(plan)
if override, ok := overrideValues[entry.code]; ok {
value = override
}
@@ -341,6 +372,10 @@ func deriveEligibilityMarkers(
isPaid bool,
activeSanctions []policy.SanctionRecord,
) EligibilityMarkersView {
if hasActiveSanction(activeSanctions, policy.SanctionCodePermanentBlock) {
return EligibilityMarkersView{}
}
loginBlocked := hasActiveSanction(activeSanctions, policy.SanctionCodeLoginBlock)
createBlocked := hasActiveSanction(activeSanctions, policy.SanctionCodePrivateGameCreateBlock)
manageBlocked := hasActiveSanction(activeSanctions, policy.SanctionCodePrivateGameManageBlock)
@@ -373,7 +408,8 @@ func isLobbyRelevantSanction(code policy.SanctionCode) bool {
case policy.SanctionCodeLoginBlock,
policy.SanctionCodePrivateGameCreateBlock,
policy.SanctionCodePrivateGameManageBlock,
policy.SanctionCodeGameJoinBlock:
policy.SanctionCodeGameJoinBlock,
policy.SanctionCodePermanentBlock:
return true
default:
return false