package repo import ( "context" "database/sql" "encoding/json" "fmt" "strings" "sync" "time" "browser.local/platform/domain" _ "github.com/go-sql-driver/mysql" ) const mysqlSnapshotID = "current" type MySQLStore struct { *MemoryStore db *sql.DB persistMu sync.Mutex } func NewMySQLStore(dsn string) (*MySQLStore, error) { dsn = strings.TrimSpace(dsn) if dsn == "" { return nil, fmt.Errorf("PLATFORM_MYSQL_DSN is required when PLATFORM_STORAGE_BACKEND=mysql") } db, err := sql.Open("mysql", dsn) if err != nil { return nil, fmt.Errorf("open mysql metadata store: %w", err) } store := &MySQLStore{ MemoryStore: NewMemoryStore(), db: db, } if err := store.initialize(); err != nil { _ = db.Close() return nil, err } if err := store.load(); err != nil { _ = db.Close() return nil, err } return store, nil } func (store *MySQLStore) Close() error { return store.db.Close() } func (store *MySQLStore) Users() UserRepository { return &persistentRepository[domain.User, domain.UserFilter]{repository: store.MemoryStore.users, persist: store.persist} } func (store *MySQLStore) AuthSessions() AuthSessionRepository { return &persistentRepository[domain.AuthSessionRecord, domain.AuthSessionFilter]{repository: store.MemoryStore.authSessions, persist: store.persist} } func (store *MySQLStore) RunControlSessions() RunControlSessionRepository { return &persistentRepository[domain.RunControlSession, struct{}]{repository: store.MemoryStore.runSessions, persist: store.persist} } func (store *MySQLStore) AIProviders() AIProviderRepository { return &persistentRepository[domain.AIProvider, domain.AIProviderFilter]{repository: store.MemoryStore.aiProviders, persist: store.persist} } func (store *MySQLStore) GamePlugins() GamePluginRepository { return &persistentRepository[domain.GamePlugin, domain.GamePluginFilter]{repository: store.MemoryStore.gamePlugins, persist: store.persist} } func (store *MySQLStore) ServerInstances() ServerInstanceRepository { return &persistentRepository[domain.ServerInstance, domain.ServerInstanceFilter]{repository: store.MemoryStore.serverInstances, persist: store.persist} } func (store *MySQLStore) RunEndpoints() RunEndpointRepository { return &persistentRepository[domain.RunEndpoint, domain.RunEndpointFilter]{repository: store.MemoryStore.runEndpoints, persist: store.persist} } func (store *MySQLStore) Jobs() JobRepository { return &persistentJobRepository{ persistentRepository: &persistentRepository[domain.Job, domain.JobFilter]{repository: store.MemoryStore.jobs, persist: store.persist}, repository: store.MemoryStore.jobs, } } func (store *MySQLStore) Artifacts() ArtifactRepository { return &persistentRepository[domain.Artifact, domain.ArtifactFilter]{repository: store.MemoryStore.artifacts, persist: store.persist} } func (store *MySQLStore) RuntimeBindings() RuntimeBindingRepository { return &persistentRepository[domain.RuntimeBinding, domain.RuntimeBindingFilter]{repository: store.MemoryStore.runtimeBindings, persist: store.persist} } func (store *MySQLStore) EncryptedComponentKeys() EncryptedComponentKeyRepository { return &persistentRepository[domain.EncryptedComponentKey, domain.EncryptedComponentKeyFilter]{repository: store.MemoryStore.componentKeys, persist: store.persist} } func (store *MySQLStore) RunDistributions() RunDistributionRepository { return &persistentRepository[domain.RunDistribution, domain.RunDistributionFilter]{repository: store.MemoryStore.runDists, persist: store.persist} } func (store *MySQLStore) ClientManagerDistributions() ClientManagerDistributionRepository { return &persistentRepository[domain.ClientManagerDistribution, domain.ClientManagerDistributionFilter]{repository: store.MemoryStore.clientDists, persist: store.persist} } func (store *MySQLStore) ClientManagerInstallations() ClientManagerInstallationRepository { return &persistentRepository[domain.ClientManagerInstallation, domain.ClientManagerInstallationFilter]{repository: store.MemoryStore.clientInstalls, persist: store.persist} } func (store *MySQLStore) ClientManagerSessions() ClientManagerSessionRepository { return &persistentRepository[domain.ClientManagerSession, domain.ClientManagerSessionFilter]{repository: store.MemoryStore.clientSessions, persist: store.persist} } func (store *MySQLStore) ClientManagerNonces() ClientManagerNonceRepository { return &persistentRepository[domain.ClientManagerRegistrationNonce, domain.ClientManagerNonceFilter]{repository: store.MemoryStore.clientNonces, persist: store.persist} } func (store *MySQLStore) DependencyStatuses() DependencyStatusRepository { return &persistentRepository[domain.DependencyStatus, domain.DependencyStatusFilter]{repository: store.MemoryStore.dependencies, persist: store.persist} } func (store *MySQLStore) ClientManagerBuildJobs() ClientManagerBuildJobRepository { return &persistentRepository[domain.ClientManagerBuildJob, domain.ClientManagerBuildJobFilter]{repository: store.MemoryStore.buildJobs, persist: store.persist} } func (store *MySQLStore) RunUpdateJobs() RunUpdateJobRepository { return &persistentRepository[domain.RunUpdateJob, domain.RunUpdateJobFilter]{repository: store.MemoryStore.updateJobs, persist: store.persist} } func (store *MySQLStore) LogStreams() LogStreamRepository { return &persistentRepository[domain.LogStream, domain.LogStreamFilter]{repository: store.MemoryStore.logStreams, persist: store.persist} } func (store *MySQLStore) AuditEvents() AuditEventRepository { return &persistentRepository[domain.AuditEvent, domain.AuditEventFilter]{repository: store.MemoryStore.auditEvents, persist: store.persist} } func (store *MySQLStore) MetricSamples() MetricSampleRepository { return &persistentRepository[domain.MetricSample, domain.MetricSampleFilter]{repository: store.MemoryStore.metricSamples, persist: store.persist} } func (store *MySQLStore) Backups() BackupRepository { return &persistentRepository[domain.BackupRecord, domain.BackupFilter]{repository: store.MemoryStore.backups, persist: store.persist} } func (store *MySQLStore) Alerts() AlertRepository { return &persistentRepository[domain.AlertRecord, domain.AlertFilter]{repository: store.MemoryStore.alerts, persist: store.persist} } func (store *MySQLStore) PluginLifecycles() PluginLifecycleRepository { return &persistentRepository[domain.PluginLifecycleInstallation, domain.PluginLifecycleFilter]{repository: store.MemoryStore.pluginLifecycle, persist: store.persist} } func (store *MySQLStore) AIConfigDiffs() AIConfigDiffRepository { return &persistentRepository[domain.AIConfigDiffPreview, domain.AIConfigDiffFilter]{repository: store.MemoryStore.aiConfigDiffs, persist: store.persist} } func (store *MySQLStore) GameClientBridgeCommands() GameClientBridgeCommandRepository { return &persistentGameClientBridgeCommandRepository{ persistentRepository: &persistentRepository[domain.GameClientBridgeCommand, domain.GameClientBridgeCommandFilter]{repository: store.MemoryStore.bridgeCommands, persist: store.persist}, repository: store.MemoryStore.bridgeCommands, } } func (store *MySQLStore) GameClientBridgeSnapshots() GameClientBridgeSnapshotRepository { return &persistentRepository[domain.GameClientBridgeSnapshot, domain.GameClientBridgeSnapshotFilter]{repository: store.MemoryStore.bridgeSnapshots, persist: store.persist} } func (store *MySQLStore) GameClientBridgeSnapshotStreams() GameClientBridgeSnapshotStreamRepository { return &persistentRepository[domain.GameClientBridgeSnapshotStream, domain.GameClientBridgeSnapshotStreamFilter]{repository: store.MemoryStore.bridgeStreams, persist: store.persist} } func (store *MySQLStore) GamePlayers() GamePlayerRepository { return &persistentRepository[domain.GamePlayer, domain.GamePlayerFilter]{repository: store.MemoryStore.gamePlayers, persist: store.persist} } func (store *MySQLStore) GamePlayerAliases() GamePlayerAliasRepository { return &persistentRepository[domain.GamePlayerAlias, domain.GamePlayerAliasFilter]{repository: store.MemoryStore.gamePlayerAliases, persist: store.persist} } func (store *MySQLStore) GamePlayerSessions() GamePlayerSessionRepository { return &persistentRepository[domain.GamePlayerSession, domain.GamePlayerSessionFilter]{repository: store.MemoryStore.gamePlayerSessions, persist: store.persist} } func (store *MySQLStore) GameAccessAttempts() GameAccessAttemptRepository { return &persistentRepository[domain.GameAccessAttempt, domain.GameAccessAttemptFilter]{repository: store.MemoryStore.gameAccessAttempts, persist: store.persist} } 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) GameMapTrackPoints() GameMapTrackPointRepository { return &persistentRepository[domain.GameMapTrackPoint, domain.GameMapTrackPointFilter]{repository: store.MemoryStore.gameMapTrackPoints, persist: store.persist} } func (store *MySQLStore) GamePlayerVehicleSegments() GamePlayerVehicleSegmentRepository { return &persistentRepository[domain.GamePlayerVehicleSegment, domain.GamePlayerVehicleSegmentFilter]{repository: store.MemoryStore.gamePlayerVehicleSegments, 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) SCUMDataObservations() SCUMDataObservationRepository { return &mysqlSCUMObservationRepository{repository: store.MemoryStore.scumDataObservations, store: store} } func (store *MySQLStore) SCUMDataRows() SCUMDataRowRepository { return &mysqlSCUMDataRowRepository{repository: store.MemoryStore.scumDataRows, store: store} } func (store *MySQLStore) SCUMPlayerLiveStates() SCUMPlayerLiveStateRepository { return &persistentRepository[domain.SCUMPlayerLiveState, domain.SCUMProjectionFilter]{repository: store.MemoryStore.scumPlayerLiveStates, persist: store.persist} } func (store *MySQLStore) SCUMSquads() SCUMSquadRepository { return &persistentRepository[domain.SCUMSquad, domain.SCUMProjectionFilter]{repository: store.MemoryStore.scumSquads, persist: store.persist} } func (store *MySQLStore) SCUMSquadMembers() SCUMSquadMemberRepository { return &persistentRepository[domain.SCUMSquadMember, domain.SCUMProjectionFilter]{repository: store.MemoryStore.scumSquadMembers, persist: store.persist} } func (store *MySQLStore) SCUMVehicles() SCUMVehicleRepository { return &persistentRepository[domain.SCUMVehicle, domain.SCUMProjectionFilter]{repository: store.MemoryStore.scumVehicles, persist: store.persist} } func (store *MySQLStore) SCUMFlags() SCUMFlagRepository { return &persistentRepository[domain.SCUMFlag, domain.SCUMProjectionFilter]{repository: store.MemoryStore.scumFlags, persist: store.persist} } func (store *MySQLStore) SCUMCurrentPositions() SCUMCurrentPositionRepository { return &persistentRepository[domain.SCUMCurrentPosition, domain.SCUMProjectionFilter]{repository: store.MemoryStore.scumCurrentPositions, persist: store.persist} } func (store *MySQLStore) SCUMOperationRequests() SCUMOperationRequestRepository { return &persistentRepository[domain.SCUMOperationRequest, domain.SCUMOperationRequestFilter]{repository: store.MemoryStore.scumOperationRequests, persist: store.persist} } func (store *MySQLStore) SCUMWorkflowInstances() SCUMWorkflowInstanceRepository { return &persistentRepository[domain.SCUMWorkflowInstance, domain.SCUMWorkflowInstanceFilter]{repository: store.MemoryStore.scumWorkflowInstances, persist: store.persist} } func (store *MySQLStore) SCUMWorkflowSteps() SCUMWorkflowStepRepository { return &persistentRepository[domain.SCUMWorkflowStep, domain.SCUMWorkflowStepFilter]{repository: store.MemoryStore.scumWorkflowSteps, persist: store.persist} } func (store *MySQLStore) initialize() error { ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) defer cancel() if err := store.db.PingContext(ctx); err != nil { return fmt.Errorf("connect mysql metadata store: %w", err) } _, err := store.db.ExecContext(ctx, ` CREATE TABLE IF NOT EXISTS platform_metadata_snapshots ( id VARCHAR(64) PRIMARY KEY, snapshot_json JSON NOT NULL, updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP )`) if err != nil { return fmt.Errorf("create mysql metadata snapshot table: %w", err) } for _, table := range []string{"scum_sync_runs", "scum_users", "scum_squads", "scum_squad_members", "scum_vehicles", "scum_flags", "scum_activity_events", "scum_gift_events", "scum_map_points"} { statement := fmt.Sprintf(`CREATE TABLE IF NOT EXISTS %s ( id VARCHAR(191) PRIMARY KEY, server_instance_id VARCHAR(191) NOT NULL, upsert_key VARCHAR(512) NOT NULL, fields_json JSON NOT NULL, payload_json JSON NOT NULL, plugin_id VARCHAR(191) NOT NULL, query_key VARCHAR(191) NOT NULL, freshness_json JSON NOT NULL, created_at DATETIME(6) NOT NULL, updated_at DATETIME(6) NOT NULL, INDEX %s_server_updated (server_instance_id, updated_at) )`, table, table) if _, err := store.db.ExecContext(ctx, statement); err != nil { return fmt.Errorf("create mysql %s table: %w", table, err) } } return nil } type mysqlSCUMDataRowRepository struct { repository mutableRepository[domain.SCUMDataRow, domain.SCUMProjectionFilter] store *MySQLStore } func (repository *mysqlSCUMDataRowRepository) Create(value domain.SCUMDataRow) error { if err := repository.repository.Create(value); err != nil { return err } if err := repository.store.persistSCUMDataRow(value); err != nil { return err } return repository.store.persist() } func (repository *mysqlSCUMDataRowRepository) Get(id string) (domain.SCUMDataRow, error) { return repository.repository.Get(id) } func (repository *mysqlSCUMDataRowRepository) List(filter domain.SCUMProjectionFilter) ([]domain.SCUMDataRow, error) { return repository.repository.List(filter) } func (repository *mysqlSCUMDataRowRepository) Update(value domain.SCUMDataRow) error { if err := repository.repository.Update(value); err != nil { return err } if err := repository.store.persistSCUMDataRow(value); err != nil { return err } return repository.store.persist() } type mysqlSCUMObservationRepository struct { repository mutableRepository[domain.SCUMDataObservation, domain.SCUMProjectionFilter] store *MySQLStore } func (repository *mysqlSCUMObservationRepository) Create(value domain.SCUMDataObservation) error { if err := repository.repository.Create(value); err != nil { return err } if err := repository.store.persistSCUMSyncRun(value); err != nil { return err } return repository.store.persist() } func (repository *mysqlSCUMObservationRepository) Get(id string) (domain.SCUMDataObservation, error) { return repository.repository.Get(id) } func (repository *mysqlSCUMObservationRepository) List(filter domain.SCUMProjectionFilter) ([]domain.SCUMDataObservation, error) { return repository.repository.List(filter) } func (repository *mysqlSCUMObservationRepository) Update(value domain.SCUMDataObservation) error { if err := repository.repository.Update(value); err != nil { return err } if err := repository.store.persistSCUMSyncRun(value); err != nil { return err } return repository.store.persist() } func (store *MySQLStore) persistSCUMDataRow(value domain.SCUMDataRow) error { table, ok := mysqlSCUMTable(value.TargetTable) if !ok { return fmt.Errorf("unsupported SCUM data target %q", value.TargetTable) } fields, err := json.Marshal(value.Fields) if err != nil { return fmt.Errorf("encode SCUM fields: %w", err) } payload, err := json.Marshal(value.Payload) if err != nil { return fmt.Errorf("encode SCUM payload: %w", err) } freshness, err := json.Marshal(value.Freshness) if err != nil { return fmt.Errorf("encode SCUM freshness: %w", err) } return store.upsertSCUMPhysicalRow(table, value.ID, value.ServerInstanceID, value.UpsertKey, fields, payload, value.PluginID, value.QueryKey, freshness, value.CreatedAt, value.UpdatedAt) } func (store *MySQLStore) persistSCUMSyncRun(value domain.SCUMDataObservation) error { freshness, err := json.Marshal(domain.SCUMProjectionFreshnessState{Status: domain.SCUMProjectionFreshness(value.Status), Source: value.Source, QueryKey: value.QueryKey, Sequence: value.Sequence, Checksum: value.Checksum, ObservedAt: value.ObservedAt, ReceivedAt: value.ReceivedAt}) if err != nil { return fmt.Errorf("encode SCUM sync freshness: %w", err) } fields, _ := json.Marshal(map[string]any{"status": value.Status, "errorCode": value.ErrorCode, "observedAt": value.ObservedAt, "receivedAt": value.ReceivedAt}) payload, _ := json.Marshal(value.SafeSummary) return store.upsertSCUMPhysicalRow("scum_sync_runs", value.ID, value.ServerInstanceID, value.QueryKey, fields, payload, value.PluginID, value.QueryKey, freshness, value.ReceivedAt, value.ReceivedAt) } func (store *MySQLStore) upsertSCUMPhysicalRow(table, id, serverInstanceID, upsertKey string, fields, payload []byte, pluginID, queryKey string, freshness []byte, createdAt, updatedAt time.Time) error { if createdAt.IsZero() { createdAt = time.Now().UTC() } if updatedAt.IsZero() { updatedAt = createdAt } ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) defer cancel() statement := fmt.Sprintf(`INSERT INTO %s (id, server_instance_id, upsert_key, fields_json, payload_json, plugin_id, query_key, freshness_json, created_at, updated_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?) ON DUPLICATE KEY UPDATE server_instance_id=VALUES(server_instance_id), upsert_key=VALUES(upsert_key), fields_json=VALUES(fields_json), payload_json=VALUES(payload_json), plugin_id=VALUES(plugin_id), query_key=VALUES(query_key), freshness_json=VALUES(freshness_json), updated_at=VALUES(updated_at)`, table) if _, err := store.db.ExecContext(ctx, statement, id, serverInstanceID, upsertKey, string(fields), string(payload), pluginID, queryKey, string(freshness), createdAt, updatedAt); err != nil { return fmt.Errorf("write mysql %s row: %w", table, err) } return nil } func mysqlSCUMTable(target domain.SCUMDataSet) (string, bool) { switch target { case domain.SCUMDataSetUsers: return "scum_users", true case domain.SCUMDataSetSquads: return "scum_squads", true case domain.SCUMDataSetMembers: return "scum_squad_members", true case domain.SCUMDataSetVehicles: return "scum_vehicles", true case domain.SCUMDataSetFlags: return "scum_flags", true case domain.SCUMDataSetActivity: return "scum_activity_events", true case domain.SCUMDataSetGiftEvents: return "scum_gift_events", true case domain.SCUMDataSetMapPoints: return "scum_map_points", true default: return "", false } } func (store *MySQLStore) load() error { ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) defer cancel() var payload []byte err := store.db.QueryRowContext(ctx, "SELECT snapshot_json FROM platform_metadata_snapshots WHERE id = ?", mysqlSnapshotID).Scan(&payload) if err != nil { if err == sql.ErrNoRows { return nil } return fmt.Errorf("read mysql metadata snapshot: %w", err) } var snapshot StoreSnapshot if err := json.Unmarshal(payload, &snapshot); err != nil { return fmt.Errorf("decode mysql metadata snapshot: %w", err) } store.loadSnapshot(snapshot) return nil } func (store *MySQLStore) persist() error { store.persistMu.Lock() defer store.persistMu.Unlock() snapshot := store.snapshot() payload, err := json.Marshal(snapshot) if err != nil { return fmt.Errorf("encode mysql metadata snapshot: %w", err) } ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) defer cancel() _, err = store.db.ExecContext(ctx, ` INSERT INTO platform_metadata_snapshots (id, snapshot_json) VALUES (?, ?) ON DUPLICATE KEY UPDATE snapshot_json = ?, updated_at = CURRENT_TIMESTAMP`, mysqlSnapshotID, string(payload), string(payload)) if err != nil { return fmt.Errorf("write mysql metadata snapshot: %w", err) } return nil } func (store *MySQLStore) snapshot() StoreSnapshot { return StoreSnapshot{ Users: snapshotRepository(store.MemoryStore.users), AuthSessions: snapshotRepository(store.MemoryStore.authSessions), RunControlSessions: snapshotRepository(store.MemoryStore.runSessions), AIProviders: snapshotRepository(store.MemoryStore.aiProviders), GamePlugins: snapshotRepository(store.MemoryStore.gamePlugins), ServerInstances: snapshotRepository(store.MemoryStore.serverInstances), RunEndpoints: snapshotRepository(store.MemoryStore.runEndpoints), Jobs: snapshotRepository(store.MemoryStore.jobs.memoryRepository), Artifacts: snapshotRepository(store.MemoryStore.artifacts), RuntimeBindings: snapshotRepository(store.MemoryStore.runtimeBindings), EncryptedComponentKeys: snapshotRepository(store.MemoryStore.componentKeys), RunDistributions: snapshotRepository(store.MemoryStore.runDists), ClientManagerDistributions: snapshotRepository(store.MemoryStore.clientDists), ClientManagerInstallations: snapshotRepository(store.MemoryStore.clientInstalls), ClientManagerSessions: snapshotRepository(store.MemoryStore.clientSessions), ClientManagerNonces: snapshotRepository(store.MemoryStore.clientNonces), DependencyStatuses: snapshotRepository(store.MemoryStore.dependencies), ClientManagerBuildJobs: snapshotRepository(store.MemoryStore.buildJobs), RunUpdateJobs: snapshotRepository(store.MemoryStore.updateJobs), LogStreams: snapshotRepository(store.MemoryStore.logStreams), AuditEvents: snapshotRepository(store.MemoryStore.auditEvents), MetricSamples: snapshotRepository(store.MemoryStore.metricSamples), Backups: snapshotRepository(store.MemoryStore.backups), Alerts: snapshotRepository(store.MemoryStore.alerts), PluginLifecycles: snapshotRepository(store.MemoryStore.pluginLifecycle), AIConfigDiffs: snapshotRepository(store.MemoryStore.aiConfigDiffs), 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), GameMapTrackPoints: snapshotRepository(store.MemoryStore.gameMapTrackPoints), GamePlayerVehicleSegments: snapshotRepository(store.MemoryStore.gamePlayerVehicleSegments), GameGiftCatalogs: snapshotRepository(store.MemoryStore.gameGiftCatalogs), GameGiftRevisions: snapshotRepository(store.MemoryStore.gameGiftRevisions), GameGiftGrants: snapshotRepository(store.MemoryStore.gameGiftGrants), SCUMDataObservations: snapshotRepository(store.MemoryStore.scumDataObservations), SCUMDataRows: snapshotRepository(store.MemoryStore.scumDataRows), SCUMPlayerLiveStates: snapshotRepository(store.MemoryStore.scumPlayerLiveStates), SCUMSquads: snapshotRepository(store.MemoryStore.scumSquads), SCUMSquadMembers: snapshotRepository(store.MemoryStore.scumSquadMembers), SCUMVehicles: snapshotRepository(store.MemoryStore.scumVehicles), SCUMFlags: snapshotRepository(store.MemoryStore.scumFlags), SCUMCurrentPositions: snapshotRepository(store.MemoryStore.scumCurrentPositions), SCUMOperationRequests: snapshotRepository(store.MemoryStore.scumOperationRequests), SCUMWorkflowInstances: snapshotRepository(store.MemoryStore.scumWorkflowInstances), SCUMWorkflowSteps: snapshotRepository(store.MemoryStore.scumWorkflowSteps), } } func (store *MySQLStore) loadSnapshot(snapshot StoreSnapshot) { loadRepository(store.MemoryStore.users, snapshot.Users) loadRepository(store.MemoryStore.authSessions, snapshot.AuthSessions) loadRepository(store.MemoryStore.runSessions, snapshot.RunControlSessions) loadRepository(store.MemoryStore.aiProviders, snapshot.AIProviders) loadRepository(store.MemoryStore.gamePlugins, snapshot.GamePlugins) loadRepository(store.MemoryStore.serverInstances, snapshot.ServerInstances) loadRepository(store.MemoryStore.runEndpoints, snapshot.RunEndpoints) loadRepository(store.MemoryStore.jobs.memoryRepository, snapshot.Jobs) loadRepository(store.MemoryStore.artifacts, snapshot.Artifacts) loadRepository(store.MemoryStore.runtimeBindings, snapshot.RuntimeBindings) loadRepository(store.MemoryStore.componentKeys, snapshot.EncryptedComponentKeys) loadRepository(store.MemoryStore.runDists, snapshot.RunDistributions) loadRepository(store.MemoryStore.clientDists, snapshot.ClientManagerDistributions) loadRepository(store.MemoryStore.clientInstalls, snapshot.ClientManagerInstallations) loadRepository(store.MemoryStore.clientSessions, snapshot.ClientManagerSessions) loadRepository(store.MemoryStore.clientNonces, snapshot.ClientManagerNonces) loadRepository(store.MemoryStore.dependencies, snapshot.DependencyStatuses) loadRepository(store.MemoryStore.buildJobs, snapshot.ClientManagerBuildJobs) loadRepository(store.MemoryStore.updateJobs, snapshot.RunUpdateJobs) loadRepository(store.MemoryStore.logStreams, snapshot.LogStreams) loadRepository(store.MemoryStore.auditEvents, snapshot.AuditEvents) loadRepository(store.MemoryStore.metricSamples, snapshot.MetricSamples) loadRepository(store.MemoryStore.backups, snapshot.Backups) loadRepository(store.MemoryStore.alerts, snapshot.Alerts) loadRepository(store.MemoryStore.pluginLifecycle, snapshot.PluginLifecycles) loadRepository(store.MemoryStore.aiConfigDiffs, snapshot.AIConfigDiffs) loadRepository(store.MemoryStore.bridgeCommands.memoryRepository, snapshot.GameClientBridgeCommands) loadRepository(store.MemoryStore.bridgeSnapshots.memoryRepository, snapshot.GameClientBridgeSnapshots) loadRepository(store.MemoryStore.bridgeStreams, snapshot.GameClientBridgeStreams) loadRepository(store.MemoryStore.gamePlayers, snapshot.GamePlayers) loadRepository(store.MemoryStore.gamePlayerAliases, snapshot.GamePlayerAliases) loadRepository(store.MemoryStore.gamePlayerSessions, snapshot.GamePlayerSessions) loadRepository(store.MemoryStore.gameAccessAttempts, snapshot.GameAccessAttempts) loadRepository(store.MemoryStore.gameSecuritySignals, snapshot.GameSecuritySignals) loadRepository(store.MemoryStore.gamePlayerStatePatches, snapshot.GamePlayerStatePatches) loadRepository(store.MemoryStore.gameMapTrackPoints, snapshot.GameMapTrackPoints) loadRepository(store.MemoryStore.gamePlayerVehicleSegments, snapshot.GamePlayerVehicleSegments) loadRepository(store.MemoryStore.gameGiftCatalogs, snapshot.GameGiftCatalogs) loadRepository(store.MemoryStore.gameGiftRevisions, snapshot.GameGiftRevisions) loadRepository(store.MemoryStore.gameGiftGrants, snapshot.GameGiftGrants) loadRepository(store.MemoryStore.scumDataObservations, snapshot.SCUMDataObservations) loadRepository(store.MemoryStore.scumDataRows, snapshot.SCUMDataRows) loadRepository(store.MemoryStore.scumPlayerLiveStates, snapshot.SCUMPlayerLiveStates) loadRepository(store.MemoryStore.scumSquads, snapshot.SCUMSquads) loadRepository(store.MemoryStore.scumSquadMembers, snapshot.SCUMSquadMembers) loadRepository(store.MemoryStore.scumVehicles, snapshot.SCUMVehicles) loadRepository(store.MemoryStore.scumFlags, snapshot.SCUMFlags) loadRepository(store.MemoryStore.scumCurrentPositions, snapshot.SCUMCurrentPositions) loadRepository(store.MemoryStore.scumOperationRequests, snapshot.SCUMOperationRequests) loadRepository(store.MemoryStore.scumWorkflowInstances, snapshot.SCUMWorkflowInstances) loadRepository(store.MemoryStore.scumWorkflowSteps, snapshot.SCUMWorkflowSteps) }