feat(scum): add local game player intelligence

This commit is contained in:
npc0-hue
2026-07-28 15:23:09 +08:00
parent c5806cc6b7
commit 73947edcee
26 changed files with 1209 additions and 65 deletions
+109 -29
View File
@@ -3,6 +3,7 @@ package repo
import (
"errors"
"sort"
"strings"
"sync"
"browser.local/platform/domain"
@@ -224,6 +225,41 @@ type GameClientBridgeSnapshotStreamRepository interface {
Delete(id string) error
}
type GamePlayerRepository interface {
Create(domain.GamePlayer) error
Get(string) (domain.GamePlayer, error)
List(domain.GamePlayerFilter) ([]domain.GamePlayer, error)
Update(domain.GamePlayer) error
}
type GamePlayerAliasRepository interface {
Create(domain.GamePlayerAlias) error
Get(string) (domain.GamePlayerAlias, error)
List(domain.GamePlayerAliasFilter) ([]domain.GamePlayerAlias, error)
Update(domain.GamePlayerAlias) error
Delete(string) error
}
type GamePlayerSessionRepository interface {
Create(domain.GamePlayerSession) error
Get(string) (domain.GamePlayerSession, error)
List(domain.GamePlayerSessionFilter) ([]domain.GamePlayerSession, error)
Update(domain.GamePlayerSession) error
Delete(string) error
}
type GameAccessAttemptRepository interface {
Create(domain.GameAccessAttempt) error
Get(string) (domain.GameAccessAttempt, error)
List(domain.GameAccessAttemptFilter) ([]domain.GameAccessAttempt, error)
Update(domain.GameAccessAttempt) error
Delete(string) error
}
type GameSecuritySignalRepository interface {
Create(domain.GameSecuritySignal) error
Get(string) (domain.GameSecuritySignal, error)
List(domain.GameSecuritySignalFilter) ([]domain.GameSecuritySignal, error)
Update(domain.GameSecuritySignal) error
Delete(string) error
}
type Store interface {
Users() UserRepository
AuthSessions() AuthSessionRepository
@@ -254,38 +290,48 @@ type Store interface {
GameClientBridgeCommands() GameClientBridgeCommandRepository
GameClientBridgeSnapshots() GameClientBridgeSnapshotRepository
GameClientBridgeSnapshotStreams() GameClientBridgeSnapshotStreamRepository
GamePlayers() GamePlayerRepository
GamePlayerAliases() GamePlayerAliasRepository
GamePlayerSessions() GamePlayerSessionRepository
GameAccessAttempts() GameAccessAttemptRepository
GameSecuritySignals() GameSecuritySignalRepository
}
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]
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]
}
func NewMemoryStore() *MemoryStore {
@@ -423,6 +469,11 @@ 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),
}
}
@@ -475,6 +526,19 @@ func (store *MemoryStore) GameClientBridgeSnapshots() GameClientBridgeSnapshotRe
func (store *MemoryStore) GameClientBridgeSnapshotStreams() GameClientBridgeSnapshotStreamRepository {
return store.bridgeStreams
}
func (store *MemoryStore) GamePlayers() GamePlayerRepository { return store.gamePlayers }
func (store *MemoryStore) GamePlayerAliases() GamePlayerAliasRepository {
return store.gamePlayerAliases
}
func (store *MemoryStore) GamePlayerSessions() GamePlayerSessionRepository {
return store.gamePlayerSessions
}
func (store *MemoryStore) GameAccessAttempts() GameAccessAttemptRepository {
return store.gameAccessAttempts
}
func (store *MemoryStore) GameSecuritySignals() GameSecuritySignalRepository {
return store.gameSecuritySignals
}
type memoryRepository[T any, F any] struct {
mu sync.RWMutex
@@ -775,3 +839,19 @@ func matchGameClientBridgeSnapshotStream(stream domain.GameClientBridgeSnapshotS
(filter.Type == "" || stream.Type == filter.Type) &&
(filter.StreamKey == "" || stream.StreamKey == filter.StreamKey)
}
func matchGamePlayer(v domain.GamePlayer, f domain.GamePlayerFilter) bool {
return (f.ServerInstanceID == "" || v.ServerInstanceID == f.ServerInstanceID) && (f.Search == "" || strings.Contains(strings.ToLower(v.DisplayName), strings.ToLower(f.Search)) || strings.Contains(strings.ToLower(v.GamePlayerID), strings.ToLower(f.Search)))
}
func matchGamePlayerAlias(v domain.GamePlayerAlias, f domain.GamePlayerAliasFilter) bool {
return (f.GamePlayerRecordID == "" || v.GamePlayerRecordID == f.GamePlayerRecordID) && (f.ServerInstanceID == "" || v.ServerInstanceID == f.ServerInstanceID)
}
func matchGamePlayerSession(v domain.GamePlayerSession, f domain.GamePlayerSessionFilter) bool {
return (f.GamePlayerRecordID == "" || v.GamePlayerRecordID == f.GamePlayerRecordID) && (f.ServerInstanceID == "" || v.ServerInstanceID == f.ServerInstanceID) && (!f.OpenOnly || v.EndedAt.IsZero())
}
func matchGameAccessAttempt(v domain.GameAccessAttempt, f domain.GameAccessAttemptFilter) bool {
return (f.GamePlayerRecordID == "" || v.GamePlayerRecordID == f.GamePlayerRecordID) && (f.ServerInstanceID == "" || v.ServerInstanceID == f.ServerInstanceID)
}
func matchGameSecuritySignal(v domain.GameSecuritySignal, f domain.GameSecuritySignalFilter) bool {
return (f.GamePlayerRecordID == "" || v.GamePlayerRecordID == f.GamePlayerRecordID) && (f.ServerInstanceID == "" || v.ServerInstanceID == f.ServerInstanceID)
}