feat(scum): add versioned gift grants

This commit is contained in:
npc0-hue
2026-07-28 16:40:35 +08:00
parent 4527175a6f
commit e2d0bc0595
26 changed files with 1224 additions and 14 deletions
+16 -1
View File
@@ -48,6 +48,9 @@ type StoreSnapshot struct {
GameAccessAttempts []domain.GameAccessAttempt `json:"gameAccessAttempts"`
GameSecuritySignals []domain.GameSecuritySignal `json:"gameSecuritySignals"`
GamePlayerStatePatches []domain.GamePlayerStatePatch `json:"gamePlayerStatePatches"`
GameGiftCatalogs []domain.GameGiftCatalog `json:"gameGiftCatalogs"`
GameGiftRevisions []domain.GameGiftRevision `json:"gameGiftRevisions"`
GameGiftGrants []domain.GameGiftGrant `json:"gameGiftGrants"`
}
type FileStore struct {
@@ -214,6 +217,15 @@ func (store *FileStore) GameSecuritySignals() GameSecuritySignalRepository {
func (store *FileStore) GamePlayerStatePatches() GamePlayerStatePatchRepository {
return &persistentRepository[domain.GamePlayerStatePatch, domain.GamePlayerStatePatchFilter]{repository: store.MemoryStore.gamePlayerStatePatches, persist: store.persist}
}
func (store *FileStore) GameGiftCatalogs() GameGiftCatalogRepository {
return &persistentRepository[domain.GameGiftCatalog, domain.GameGiftCatalogFilter]{repository: store.MemoryStore.gameGiftCatalogs, persist: store.persist}
}
func (store *FileStore) GameGiftRevisions() GameGiftRevisionRepository {
return &persistentRepository[domain.GameGiftRevision, domain.GameGiftRevisionFilter]{repository: store.MemoryStore.gameGiftRevisions, persist: store.persist}
}
func (store *FileStore) GameGiftGrants() GameGiftGrantRepository {
return &persistentRepository[domain.GameGiftGrant, domain.GameGiftGrantFilter]{repository: store.MemoryStore.gameGiftGrants, persist: store.persist}
}
func (store *FileStore) load() error {
data, err := os.ReadFile(store.path)
@@ -287,7 +299,7 @@ func (store *FileStore) snapshot() StoreSnapshot {
GameClientBridgeCommands: snapshotRepository(store.MemoryStore.bridgeCommands.memoryRepository),
GameClientBridgeSnapshots: snapshotRepository(store.MemoryStore.bridgeSnapshots.memoryRepository),
GameClientBridgeStreams: snapshotRepository(store.MemoryStore.bridgeStreams),
GamePlayers: snapshotRepository(store.MemoryStore.gamePlayers), GamePlayerAliases: snapshotRepository(store.MemoryStore.gamePlayerAliases), GamePlayerSessions: snapshotRepository(store.MemoryStore.gamePlayerSessions), GameAccessAttempts: snapshotRepository(store.MemoryStore.gameAccessAttempts), GameSecuritySignals: snapshotRepository(store.MemoryStore.gameSecuritySignals), GamePlayerStatePatches: snapshotRepository(store.MemoryStore.gamePlayerStatePatches),
GamePlayers: snapshotRepository(store.MemoryStore.gamePlayers), GamePlayerAliases: snapshotRepository(store.MemoryStore.gamePlayerAliases), GamePlayerSessions: snapshotRepository(store.MemoryStore.gamePlayerSessions), GameAccessAttempts: snapshotRepository(store.MemoryStore.gameAccessAttempts), GameSecuritySignals: snapshotRepository(store.MemoryStore.gameSecuritySignals), GamePlayerStatePatches: snapshotRepository(store.MemoryStore.gamePlayerStatePatches), GameGiftCatalogs: snapshotRepository(store.MemoryStore.gameGiftCatalogs), GameGiftRevisions: snapshotRepository(store.MemoryStore.gameGiftRevisions), GameGiftGrants: snapshotRepository(store.MemoryStore.gameGiftGrants),
}
}
@@ -327,6 +339,9 @@ func (store *FileStore) loadSnapshot(snapshot StoreSnapshot) {
loadRepository(store.MemoryStore.gameAccessAttempts, snapshot.GameAccessAttempts)
loadRepository(store.MemoryStore.gameSecuritySignals, snapshot.GameSecuritySignals)
loadRepository(store.MemoryStore.gamePlayerStatePatches, snapshot.GamePlayerStatePatches)
loadRepository(store.MemoryStore.gameGiftCatalogs, snapshot.GameGiftCatalogs)
loadRepository(store.MemoryStore.gameGiftRevisions, snapshot.GameGiftRevisions)
loadRepository(store.MemoryStore.gameGiftGrants, snapshot.GameGiftGrants)
}
type mutableRepository[T any, F any] interface {
+13 -1
View File
@@ -189,6 +189,15 @@ func (store *MySQLStore) GameSecuritySignals() GameSecuritySignalRepository {
func (store *MySQLStore) GamePlayerStatePatches() GamePlayerStatePatchRepository {
return &persistentRepository[domain.GamePlayerStatePatch, domain.GamePlayerStatePatchFilter]{repository: store.MemoryStore.gamePlayerStatePatches, persist: store.persist}
}
func (store *MySQLStore) GameGiftCatalogs() GameGiftCatalogRepository {
return &persistentRepository[domain.GameGiftCatalog, domain.GameGiftCatalogFilter]{repository: store.MemoryStore.gameGiftCatalogs, persist: store.persist}
}
func (store *MySQLStore) GameGiftRevisions() GameGiftRevisionRepository {
return &persistentRepository[domain.GameGiftRevision, domain.GameGiftRevisionFilter]{repository: store.MemoryStore.gameGiftRevisions, persist: store.persist}
}
func (store *MySQLStore) GameGiftGrants() GameGiftGrantRepository {
return &persistentRepository[domain.GameGiftGrant, domain.GameGiftGrantFilter]{repository: store.MemoryStore.gameGiftGrants, persist: store.persist}
}
func (store *MySQLStore) initialize() error {
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
@@ -279,7 +288,7 @@ func (store *MySQLStore) snapshot() StoreSnapshot {
GameClientBridgeCommands: snapshotRepository(store.MemoryStore.bridgeCommands.memoryRepository),
GameClientBridgeSnapshots: snapshotRepository(store.MemoryStore.bridgeSnapshots.memoryRepository),
GameClientBridgeStreams: snapshotRepository(store.MemoryStore.bridgeStreams),
GamePlayers: snapshotRepository(store.MemoryStore.gamePlayers), GamePlayerAliases: snapshotRepository(store.MemoryStore.gamePlayerAliases), GamePlayerSessions: snapshotRepository(store.MemoryStore.gamePlayerSessions), GameAccessAttempts: snapshotRepository(store.MemoryStore.gameAccessAttempts), GameSecuritySignals: snapshotRepository(store.MemoryStore.gameSecuritySignals), GamePlayerStatePatches: snapshotRepository(store.MemoryStore.gamePlayerStatePatches),
GamePlayers: snapshotRepository(store.MemoryStore.gamePlayers), GamePlayerAliases: snapshotRepository(store.MemoryStore.gamePlayerAliases), GamePlayerSessions: snapshotRepository(store.MemoryStore.gamePlayerSessions), GameAccessAttempts: snapshotRepository(store.MemoryStore.gameAccessAttempts), GameSecuritySignals: snapshotRepository(store.MemoryStore.gameSecuritySignals), GamePlayerStatePatches: snapshotRepository(store.MemoryStore.gamePlayerStatePatches), GameGiftCatalogs: snapshotRepository(store.MemoryStore.gameGiftCatalogs), GameGiftRevisions: snapshotRepository(store.MemoryStore.gameGiftRevisions), GameGiftGrants: snapshotRepository(store.MemoryStore.gameGiftGrants),
}
}
@@ -319,4 +328,7 @@ func (store *MySQLStore) loadSnapshot(snapshot StoreSnapshot) {
loadRepository(store.MemoryStore.gameAccessAttempts, snapshot.GameAccessAttempts)
loadRepository(store.MemoryStore.gameSecuritySignals, snapshot.GameSecuritySignals)
loadRepository(store.MemoryStore.gamePlayerStatePatches, snapshot.GamePlayerStatePatches)
loadRepository(store.MemoryStore.gameGiftCatalogs, snapshot.GameGiftCatalogs)
loadRepository(store.MemoryStore.gameGiftRevisions, snapshot.GameGiftRevisions)
loadRepository(store.MemoryStore.gameGiftGrants, snapshot.GameGiftGrants)
}
+40
View File
@@ -265,6 +265,23 @@ type GamePlayerStatePatchRepository interface {
List(domain.GamePlayerStatePatchFilter) ([]domain.GamePlayerStatePatch, error)
Update(domain.GamePlayerStatePatch) error
}
type GameGiftCatalogRepository interface {
Create(domain.GameGiftCatalog) error
Get(string) (domain.GameGiftCatalog, error)
List(domain.GameGiftCatalogFilter) ([]domain.GameGiftCatalog, error)
Update(domain.GameGiftCatalog) error
}
type GameGiftRevisionRepository interface {
Create(domain.GameGiftRevision) error
Get(string) (domain.GameGiftRevision, error)
List(domain.GameGiftRevisionFilter) ([]domain.GameGiftRevision, error)
}
type GameGiftGrantRepository interface {
Create(domain.GameGiftGrant) error
Get(string) (domain.GameGiftGrant, error)
List(domain.GameGiftGrantFilter) ([]domain.GameGiftGrant, error)
Update(domain.GameGiftGrant) error
}
type Store interface {
Users() UserRepository
@@ -302,6 +319,9 @@ type Store interface {
GameAccessAttempts() GameAccessAttemptRepository
GameSecuritySignals() GameSecuritySignalRepository
GamePlayerStatePatches() GamePlayerStatePatchRepository
GameGiftCatalogs() GameGiftCatalogRepository
GameGiftRevisions() GameGiftRevisionRepository
GameGiftGrants() GameGiftGrantRepository
}
type MemoryStore struct {
@@ -340,6 +360,9 @@ type MemoryStore struct {
gameAccessAttempts *memoryRepository[domain.GameAccessAttempt, domain.GameAccessAttemptFilter]
gameSecuritySignals *memoryRepository[domain.GameSecuritySignal, domain.GameSecuritySignalFilter]
gamePlayerStatePatches *memoryRepository[domain.GamePlayerStatePatch, domain.GamePlayerStatePatchFilter]
gameGiftCatalogs *memoryRepository[domain.GameGiftCatalog, domain.GameGiftCatalogFilter]
gameGiftRevisions *memoryRepository[domain.GameGiftRevision, domain.GameGiftRevisionFilter]
gameGiftGrants *memoryRepository[domain.GameGiftGrant, domain.GameGiftGrantFilter]
}
func NewMemoryStore() *MemoryStore {
@@ -483,6 +506,9 @@ func NewMemoryStore() *MemoryStore {
gameAccessAttempts: newMemoryRepository(func(v domain.GameAccessAttempt) string { return v.ID }, domain.CopyGameAccessAttempt, matchGameAccessAttempt),
gameSecuritySignals: newMemoryRepository(func(v domain.GameSecuritySignal) string { return v.ID }, domain.CopyGameSecuritySignal, matchGameSecuritySignal),
gamePlayerStatePatches: newMemoryRepository(func(v domain.GamePlayerStatePatch) string { return v.ID }, domain.CopyGamePlayerStatePatch, matchGamePlayerStatePatch),
gameGiftCatalogs: newMemoryRepository(func(v domain.GameGiftCatalog) string { return v.ID }, domain.CopyGameGiftCatalog, matchGameGiftCatalog),
gameGiftRevisions: newMemoryRepository(func(v domain.GameGiftRevision) string { return v.ID }, domain.CopyGameGiftRevision, matchGameGiftRevision),
gameGiftGrants: newMemoryRepository(func(v domain.GameGiftGrant) string { return v.ID }, domain.CopyGameGiftGrant, matchGameGiftGrant),
}
}
@@ -551,6 +577,11 @@ func (store *MemoryStore) GameSecuritySignals() GameSecuritySignalRepository {
func (store *MemoryStore) GamePlayerStatePatches() GamePlayerStatePatchRepository {
return store.gamePlayerStatePatches
}
func (store *MemoryStore) GameGiftCatalogs() GameGiftCatalogRepository { return store.gameGiftCatalogs }
func (store *MemoryStore) GameGiftRevisions() GameGiftRevisionRepository {
return store.gameGiftRevisions
}
func (store *MemoryStore) GameGiftGrants() GameGiftGrantRepository { return store.gameGiftGrants }
type memoryRepository[T any, F any] struct {
mu sync.RWMutex
@@ -870,3 +901,12 @@ func matchGameSecuritySignal(v domain.GameSecuritySignal, f domain.GameSecurityS
func matchGamePlayerStatePatch(v domain.GamePlayerStatePatch, f domain.GamePlayerStatePatchFilter) bool {
return (f.ServerInstanceID == "" || v.ServerInstanceID == f.ServerInstanceID) && (f.GamePlayerRecordID == "" || v.GamePlayerRecordID == f.GamePlayerRecordID)
}
func matchGameGiftCatalog(v domain.GameGiftCatalog, f domain.GameGiftCatalogFilter) bool {
return f.ServerInstanceID == "" || v.ServerInstanceID == f.ServerInstanceID
}
func matchGameGiftRevision(v domain.GameGiftRevision, f domain.GameGiftRevisionFilter) bool {
return (f.CatalogID == "" || v.CatalogID == f.CatalogID) && (f.ServerInstanceID == "" || v.ServerInstanceID == f.ServerInstanceID)
}
func matchGameGiftGrant(v domain.GameGiftGrant, f domain.GameGiftGrantFilter) bool {
return (f.ServerInstanceID == "" || v.ServerInstanceID == f.ServerInstanceID) && (f.GamePlayerRecordID == "" || v.GamePlayerRecordID == f.GamePlayerRecordID) && (f.IdempotencyKey == "" || v.IdempotencyKey == f.IdempotencyKey)
}