feat: add controlled scum player state patches
This commit is contained in:
@@ -47,6 +47,7 @@ type StoreSnapshot struct {
|
||||
GamePlayerSessions []domain.GamePlayerSession `json:"gamePlayerSessions"`
|
||||
GameAccessAttempts []domain.GameAccessAttempt `json:"gameAccessAttempts"`
|
||||
GameSecuritySignals []domain.GameSecuritySignal `json:"gameSecuritySignals"`
|
||||
GamePlayerStatePatches []domain.GamePlayerStatePatch `json:"gamePlayerStatePatches"`
|
||||
}
|
||||
|
||||
type FileStore struct {
|
||||
@@ -210,6 +211,9 @@ func (store *FileStore) GameAccessAttempts() GameAccessAttemptRepository {
|
||||
func (store *FileStore) GameSecuritySignals() GameSecuritySignalRepository {
|
||||
return &persistentRepository[domain.GameSecuritySignal, domain.GameSecuritySignalFilter]{repository: store.MemoryStore.gameSecuritySignals, persist: store.persist}
|
||||
}
|
||||
func (store *FileStore) GamePlayerStatePatches() GamePlayerStatePatchRepository {
|
||||
return &persistentRepository[domain.GamePlayerStatePatch, domain.GamePlayerStatePatchFilter]{repository: store.MemoryStore.gamePlayerStatePatches, persist: store.persist}
|
||||
}
|
||||
|
||||
func (store *FileStore) load() error {
|
||||
data, err := os.ReadFile(store.path)
|
||||
@@ -283,7 +287,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),
|
||||
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),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -322,6 +326,7 @@ func (store *FileStore) loadSnapshot(snapshot StoreSnapshot) {
|
||||
loadRepository(store.MemoryStore.gamePlayerSessions, snapshot.GamePlayerSessions)
|
||||
loadRepository(store.MemoryStore.gameAccessAttempts, snapshot.GameAccessAttempts)
|
||||
loadRepository(store.MemoryStore.gameSecuritySignals, snapshot.GameSecuritySignals)
|
||||
loadRepository(store.MemoryStore.gamePlayerStatePatches, snapshot.GamePlayerStatePatches)
|
||||
}
|
||||
|
||||
type mutableRepository[T any, F any] interface {
|
||||
|
||||
@@ -186,6 +186,9 @@ func (store *MySQLStore) GameAccessAttempts() GameAccessAttemptRepository {
|
||||
func (store *MySQLStore) GameSecuritySignals() GameSecuritySignalRepository {
|
||||
return &persistentRepository[domain.GameSecuritySignal, domain.GameSecuritySignalFilter]{repository: store.MemoryStore.gameSecuritySignals, persist: store.persist}
|
||||
}
|
||||
func (store *MySQLStore) GamePlayerStatePatches() GamePlayerStatePatchRepository {
|
||||
return &persistentRepository[domain.GamePlayerStatePatch, domain.GamePlayerStatePatchFilter]{repository: store.MemoryStore.gamePlayerStatePatches, persist: store.persist}
|
||||
}
|
||||
|
||||
func (store *MySQLStore) initialize() error {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
||||
@@ -276,7 +279,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),
|
||||
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),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -315,4 +318,5 @@ func (store *MySQLStore) loadSnapshot(snapshot StoreSnapshot) {
|
||||
loadRepository(store.MemoryStore.gamePlayerSessions, snapshot.GamePlayerSessions)
|
||||
loadRepository(store.MemoryStore.gameAccessAttempts, snapshot.GameAccessAttempts)
|
||||
loadRepository(store.MemoryStore.gameSecuritySignals, snapshot.GameSecuritySignals)
|
||||
loadRepository(store.MemoryStore.gamePlayerStatePatches, snapshot.GamePlayerStatePatches)
|
||||
}
|
||||
|
||||
+54
-39
@@ -259,6 +259,12 @@ type GameSecuritySignalRepository interface {
|
||||
Update(domain.GameSecuritySignal) error
|
||||
Delete(string) error
|
||||
}
|
||||
type GamePlayerStatePatchRepository interface {
|
||||
Create(domain.GamePlayerStatePatch) error
|
||||
Get(string) (domain.GamePlayerStatePatch, error)
|
||||
List(domain.GamePlayerStatePatchFilter) ([]domain.GamePlayerStatePatch, error)
|
||||
Update(domain.GamePlayerStatePatch) error
|
||||
}
|
||||
|
||||
type Store interface {
|
||||
Users() UserRepository
|
||||
@@ -295,43 +301,45 @@ type Store interface {
|
||||
GamePlayerSessions() GamePlayerSessionRepository
|
||||
GameAccessAttempts() GameAccessAttemptRepository
|
||||
GameSecuritySignals() GameSecuritySignalRepository
|
||||
GamePlayerStatePatches() GamePlayerStatePatchRepository
|
||||
}
|
||||
|
||||
type MemoryStore struct {
|
||||
users *memoryRepository[domain.User, domain.UserFilter]
|
||||
authSessions *memoryRepository[domain.AuthSessionRecord, domain.AuthSessionFilter]
|
||||
runSessions *memoryRepository[domain.RunControlSession, struct{}]
|
||||
aiProviders *memoryRepository[domain.AIProvider, domain.AIProviderFilter]
|
||||
gamePlugins *memoryRepository[domain.GamePlugin, domain.GamePluginFilter]
|
||||
serverInstances *memoryRepository[domain.ServerInstance, domain.ServerInstanceFilter]
|
||||
runEndpoints *memoryRepository[domain.RunEndpoint, domain.RunEndpointFilter]
|
||||
jobs *memoryJobRepository
|
||||
artifacts *memoryRepository[domain.Artifact, domain.ArtifactFilter]
|
||||
runtimeBindings *memoryRepository[domain.RuntimeBinding, domain.RuntimeBindingFilter]
|
||||
componentKeys *memoryRepository[domain.EncryptedComponentKey, domain.EncryptedComponentKeyFilter]
|
||||
runDists *memoryRepository[domain.RunDistribution, domain.RunDistributionFilter]
|
||||
clientDists *memoryRepository[domain.ClientManagerDistribution, domain.ClientManagerDistributionFilter]
|
||||
clientInstalls *memoryRepository[domain.ClientManagerInstallation, domain.ClientManagerInstallationFilter]
|
||||
clientSessions *memoryRepository[domain.ClientManagerSession, domain.ClientManagerSessionFilter]
|
||||
clientNonces *memoryRepository[domain.ClientManagerRegistrationNonce, domain.ClientManagerNonceFilter]
|
||||
dependencies *memoryRepository[domain.DependencyStatus, domain.DependencyStatusFilter]
|
||||
buildJobs *memoryRepository[domain.ClientManagerBuildJob, domain.ClientManagerBuildJobFilter]
|
||||
updateJobs *memoryRepository[domain.RunUpdateJob, domain.RunUpdateJobFilter]
|
||||
logStreams *memoryRepository[domain.LogStream, domain.LogStreamFilter]
|
||||
auditEvents *memoryRepository[domain.AuditEvent, domain.AuditEventFilter]
|
||||
metricSamples *memoryRepository[domain.MetricSample, domain.MetricSampleFilter]
|
||||
backups *memoryRepository[domain.BackupRecord, domain.BackupFilter]
|
||||
alerts *memoryRepository[domain.AlertRecord, domain.AlertFilter]
|
||||
pluginLifecycle *memoryRepository[domain.PluginLifecycleInstallation, domain.PluginLifecycleFilter]
|
||||
aiConfigDiffs *memoryRepository[domain.AIConfigDiffPreview, domain.AIConfigDiffFilter]
|
||||
bridgeCommands *memoryGameClientBridgeCommandRepository
|
||||
bridgeSnapshots *memoryGameClientBridgeSnapshotRepository
|
||||
bridgeStreams *memoryRepository[domain.GameClientBridgeSnapshotStream, domain.GameClientBridgeSnapshotStreamFilter]
|
||||
gamePlayers *memoryRepository[domain.GamePlayer, domain.GamePlayerFilter]
|
||||
gamePlayerAliases *memoryRepository[domain.GamePlayerAlias, domain.GamePlayerAliasFilter]
|
||||
gamePlayerSessions *memoryRepository[domain.GamePlayerSession, domain.GamePlayerSessionFilter]
|
||||
gameAccessAttempts *memoryRepository[domain.GameAccessAttempt, domain.GameAccessAttemptFilter]
|
||||
gameSecuritySignals *memoryRepository[domain.GameSecuritySignal, domain.GameSecuritySignalFilter]
|
||||
users *memoryRepository[domain.User, domain.UserFilter]
|
||||
authSessions *memoryRepository[domain.AuthSessionRecord, domain.AuthSessionFilter]
|
||||
runSessions *memoryRepository[domain.RunControlSession, struct{}]
|
||||
aiProviders *memoryRepository[domain.AIProvider, domain.AIProviderFilter]
|
||||
gamePlugins *memoryRepository[domain.GamePlugin, domain.GamePluginFilter]
|
||||
serverInstances *memoryRepository[domain.ServerInstance, domain.ServerInstanceFilter]
|
||||
runEndpoints *memoryRepository[domain.RunEndpoint, domain.RunEndpointFilter]
|
||||
jobs *memoryJobRepository
|
||||
artifacts *memoryRepository[domain.Artifact, domain.ArtifactFilter]
|
||||
runtimeBindings *memoryRepository[domain.RuntimeBinding, domain.RuntimeBindingFilter]
|
||||
componentKeys *memoryRepository[domain.EncryptedComponentKey, domain.EncryptedComponentKeyFilter]
|
||||
runDists *memoryRepository[domain.RunDistribution, domain.RunDistributionFilter]
|
||||
clientDists *memoryRepository[domain.ClientManagerDistribution, domain.ClientManagerDistributionFilter]
|
||||
clientInstalls *memoryRepository[domain.ClientManagerInstallation, domain.ClientManagerInstallationFilter]
|
||||
clientSessions *memoryRepository[domain.ClientManagerSession, domain.ClientManagerSessionFilter]
|
||||
clientNonces *memoryRepository[domain.ClientManagerRegistrationNonce, domain.ClientManagerNonceFilter]
|
||||
dependencies *memoryRepository[domain.DependencyStatus, domain.DependencyStatusFilter]
|
||||
buildJobs *memoryRepository[domain.ClientManagerBuildJob, domain.ClientManagerBuildJobFilter]
|
||||
updateJobs *memoryRepository[domain.RunUpdateJob, domain.RunUpdateJobFilter]
|
||||
logStreams *memoryRepository[domain.LogStream, domain.LogStreamFilter]
|
||||
auditEvents *memoryRepository[domain.AuditEvent, domain.AuditEventFilter]
|
||||
metricSamples *memoryRepository[domain.MetricSample, domain.MetricSampleFilter]
|
||||
backups *memoryRepository[domain.BackupRecord, domain.BackupFilter]
|
||||
alerts *memoryRepository[domain.AlertRecord, domain.AlertFilter]
|
||||
pluginLifecycle *memoryRepository[domain.PluginLifecycleInstallation, domain.PluginLifecycleFilter]
|
||||
aiConfigDiffs *memoryRepository[domain.AIConfigDiffPreview, domain.AIConfigDiffFilter]
|
||||
bridgeCommands *memoryGameClientBridgeCommandRepository
|
||||
bridgeSnapshots *memoryGameClientBridgeSnapshotRepository
|
||||
bridgeStreams *memoryRepository[domain.GameClientBridgeSnapshotStream, domain.GameClientBridgeSnapshotStreamFilter]
|
||||
gamePlayers *memoryRepository[domain.GamePlayer, domain.GamePlayerFilter]
|
||||
gamePlayerAliases *memoryRepository[domain.GamePlayerAlias, domain.GamePlayerAliasFilter]
|
||||
gamePlayerSessions *memoryRepository[domain.GamePlayerSession, domain.GamePlayerSessionFilter]
|
||||
gameAccessAttempts *memoryRepository[domain.GameAccessAttempt, domain.GameAccessAttemptFilter]
|
||||
gameSecuritySignals *memoryRepository[domain.GameSecuritySignal, domain.GameSecuritySignalFilter]
|
||||
gamePlayerStatePatches *memoryRepository[domain.GamePlayerStatePatch, domain.GamePlayerStatePatchFilter]
|
||||
}
|
||||
|
||||
func NewMemoryStore() *MemoryStore {
|
||||
@@ -469,11 +477,12 @@ func NewMemoryStore() *MemoryStore {
|
||||
domain.CopyGameClientBridgeSnapshotStream,
|
||||
matchGameClientBridgeSnapshotStream,
|
||||
),
|
||||
gamePlayers: newMemoryRepository(func(v domain.GamePlayer) string { return v.ID }, domain.CopyGamePlayer, matchGamePlayer),
|
||||
gamePlayerAliases: newMemoryRepository(func(v domain.GamePlayerAlias) string { return v.ID }, domain.CopyGamePlayerAlias, matchGamePlayerAlias),
|
||||
gamePlayerSessions: newMemoryRepository(func(v domain.GamePlayerSession) string { return v.ID }, domain.CopyGamePlayerSession, matchGamePlayerSession),
|
||||
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),
|
||||
gamePlayers: newMemoryRepository(func(v domain.GamePlayer) string { return v.ID }, domain.CopyGamePlayer, matchGamePlayer),
|
||||
gamePlayerAliases: newMemoryRepository(func(v domain.GamePlayerAlias) string { return v.ID }, domain.CopyGamePlayerAlias, matchGamePlayerAlias),
|
||||
gamePlayerSessions: newMemoryRepository(func(v domain.GamePlayerSession) string { return v.ID }, domain.CopyGamePlayerSession, matchGamePlayerSession),
|
||||
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),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -539,6 +548,9 @@ func (store *MemoryStore) GameAccessAttempts() GameAccessAttemptRepository {
|
||||
func (store *MemoryStore) GameSecuritySignals() GameSecuritySignalRepository {
|
||||
return store.gameSecuritySignals
|
||||
}
|
||||
func (store *MemoryStore) GamePlayerStatePatches() GamePlayerStatePatchRepository {
|
||||
return store.gamePlayerStatePatches
|
||||
}
|
||||
|
||||
type memoryRepository[T any, F any] struct {
|
||||
mu sync.RWMutex
|
||||
@@ -855,3 +867,6 @@ func matchGameAccessAttempt(v domain.GameAccessAttempt, f domain.GameAccessAttem
|
||||
func matchGameSecuritySignal(v domain.GameSecuritySignal, f domain.GameSecuritySignalFilter) bool {
|
||||
return (f.GamePlayerRecordID == "" || v.GamePlayerRecordID == f.GamePlayerRecordID) && (f.ServerInstanceID == "" || v.ServerInstanceID == f.ServerInstanceID)
|
||||
}
|
||||
func matchGamePlayerStatePatch(v domain.GamePlayerStatePatch, f domain.GamePlayerStatePatchFilter) bool {
|
||||
return (f.ServerInstanceID == "" || v.ServerInstanceID == f.ServerInstanceID) && (f.GamePlayerRecordID == "" || v.GamePlayerRecordID == f.GamePlayerRecordID)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user