Rebuild SCUM plugin-owned data flow

This commit is contained in:
npc0-hue
2026-08-18 07:01:17 +08:00
parent 302f1f64b7
commit 98bf944f4c
39 changed files with 1832 additions and 223 deletions
@@ -10,13 +10,32 @@ The SCUM plugin owns collection names such as `scum_users`, schemas, upsert keys
The generic record is scoped by `pluginId`, `serverInstanceId`, `collection`, and `key`, with an opaque JSON `value` and timestamps. The platform validates scope and authorization only. A page bridge exposes list/put/delete and atomic put/delete transaction methods to plugin bundles.
SCUM uses stable `scum_*` collection names in the Platform database. Game database versions do not become Platform branches: a new SCUM schema updates the plugin SQL, result schema, mappings, and parser declarations while preserving the normalized collection contract.
## SCUM Data Flow
1. The plugin declares v57 SQLite `sqlRef` assets, opaque collection row targets, config maps, and log parsers in its own versioned data pack.
2. Platform dispatches the selected declared operation to Run; no browser or plugin supplies a machine path or SQL string at request time.
3. Run returns structured rows; Platform applies only the declared collection, upsert keys, and column mapping before storing them in the scoped generic store.
4. The SCUM page reads those collections through the generic bridge and applies all SCUM-specific presentation and gift logic locally.
5. Gift delivery and activity commands use the existing generic Game Client Bridge queue exposed by the plugin-page host.
1. The plugin declares SQLite `sqlRef` assets, automatic cadence, collection row targets, config maps, and log projections in its own data pack and manifest. `PRAGMA user_version` is diagnostic evidence, not a Platform version switch.
2. When Run polls for work, Platform creates every due declared query before selecting the next job. This naturally follows the existing two-second polling channel and works without a UI or a new background daemon.
3. Run executes the declared SQL asset and returns structured rows. Platform applies only the declared collection, key, mapping, merge/replace mode, and fixed values.
4. Full snapshot targets delete records absent from a successful complete result. Merge targets preserve stdout presence fields while SQLite later adds profile, economy, squad, and coordinate facts.
5. Durable stdout batches are evaluated against plugin-declared ordered regex steps. Named captures with the same name must agree across the sequence; the complete sequence emits one stable event value and resets its bounded per-stream state.
6. The SCUM BattlEye declaration correlates `reported as player N` with `Player N SteamID (assumed)` by slot. Steam ID is the `scum_users` key; names are mutable display fields and are never identity keys.
7. The presence policy reads the existing user before mutation. A missing record receives the new-player announcement. A record whose last login is inside ten minutes is updated at most once and receives no duplicate announcement. An older record receives the returning-player announcement.
8. Announcement text and `#announce` command syntax belong to the SCUM plugin declaration. Platform only renders captured placeholders and queues the already-declared command through the existing Run channel.
9. The SCUM page polls Platform collections for display freshness. It never dispatches SQLite queries and exposes no manual synchronization button.
10. Gift delivery and activity commands use the existing generic Game Client Bridge queue exposed by the plugin-page host.
## Declared Cadences
- `scum.player.profile`, `scum.vehicles`, and `scum.positions`: 3 seconds.
- `scum.squads` and `scum.squad-members`: 1800 seconds.
- Flags, native event/task observations, and native timed-gift observations use plugin-owned slower cadences appropriate to those datasets.
Cadence is measured from the latest matching job attempt. A still-active matching query suppresses another job, and idempotency keys include the server, template, and cadence bucket.
## Real Data Evidence
The provided complete database is `/Users/tasia/Downloads/SCUM/SCUM.db`; the similarly named file under `Logs/` is empty. The complete database reports SQLite `user_version=57`, contains 162 tables, and passes `quick_check`. All nine packaged SQL files execute against it. The existing users query starts at `user_profile` and therefore misses one real account; it must start at `user` and left join optional profile/prisoner data so stdout-created Steam identities merge correctly.
## Compatibility
@@ -0,0 +1,32 @@
# SCUM Local Data Baseline - 2026-08-18
## Scope
This is redacted structural evidence from the user-authorized local SCUM download and legacy projects. It records no player names, Steam IDs, IP addresses, coordinates, credentials, or row bodies.
## Database
- `/Users/tasia/Downloads/SCUM/Logs/SCUM.db` is an empty zero-byte file and is not usable evidence.
- `/Users/tasia/Downloads/SCUM/SCUM.db` is the complete database: SQLite `user_version=57`, 162 tables, `quick_check=ok`.
- Aggregate rows: 74 accounts, 73 profiles, 72 prisoners, 7 squads, 18 squad members, 313 vehicles, 5 bases, 5 flags, 0 native event rounds, and 2 native timed-gift completion records.
- All nine packaged v57 SQL assets execute against the complete database.
- The account/profile cardinality proves that user extraction must start from `user` and left join the optional profile chain. Steam ID is the stable collection identity; profile ID, prisoner ID, and display name are attributes.
- The current map query returns player, vehicle, base, and flag points and the observed coordinates fit the declared SCUM island bounds.
## Logs
- The download contains 568 UTF-16LE/LF log files across 19 filename prefixes. Filenames use a server-start timestamp and a file can continue growing for many hours; tailing therefore requires an offset cursor per file rather than a daily filename assumption.
- Each file starts with a blank line and one `Game version:` metadata line that must not become a business event.
- Login file logs contain single-line login/logout records with optional coordinates.
- The user-supplied BattlEye `reported`, `connected`, `SteamID`, and GUID sequence is supervised process stdout and does not occur in the downloaded file logs. It requires a separate ordered stdout projection.
## Configuration
- The configuration directory contains `ServerSettings.ini`, list-based access files, `EconomyOverride.json`, `RaidTimes.json`, `Notifications.json`, and engine input/user settings.
- `ServerSettings.ini` reports settings version 7 and contains hundreds of `scum.*` keys. Config reads and patches must preserve unknown keys rather than reconstructing the file from a short allowlist.
## Legacy Behavior
- The legacy robot continuously updated players and positions in the background and queued welcome text through the server command channel; browser presence was never the acquisition trigger.
- Legacy welcome behavior distinguishes first registration from a returning player and suppresses rapid repeated observations. This change uses the explicitly requested ten-minute window.
- The legacy implementation contains field-order mistakes in its user creation branch, so only its product behavior and stable field intent are reused, not its SQL/value assignments.
@@ -4,11 +4,18 @@
The previous SCUM data implementation placed game-specific projections, gift rules, and browser callbacks in Platform. That couples every SCUM version change to Platform releases and makes the implementation larger than the required relay role.
The first rebuild still left collection acquisition behind a page-owned `同步 SCUM.db` action and did not consume supervised SCUM stdout. As a result, opening no UI means no users, squads, vehicles, or coordinates are collected, and a real BattlEye login cannot create a player or produce the expected welcome announcement.
## What Changes
- Replace the recent SCUM direct-data and game-gift additions with a generic plugin data store and generic plugin-page data bridge.
- Keep SCUM SQLite, configuration, and log access plugin-declared and dispatched through Platform to Run.
- Put SCUM `scum_*` collection names, record shapes, gift catalog/grant behavior, map rendering inputs, and feature UI in the SCUM plugin package.
- Let each plugin query template declare its own background cadence. Platform creates due jobs from the authenticated Run polling loop, so collection acquisition continues with every browser closed.
- Add plugin-declared stdout sequence projections. Platform applies the declaration generically when durable Run log batches arrive; it does not hard-code BattlEye or SCUM line formats.
- Use Steam ID as the canonical SCUM user key, merge stdout-created users with later SQLite enrichment, and replace complete snapshot collections so deleted squads, members, vehicles, flags, and map points do not remain forever.
- Remove manual database synchronization controls. The plugin page periodically rereads Platform records only; it never initiates machine collection.
- On an authentic completed login sequence, create a missing user immediately and queue the plugin-declared global new-player announcement. For an existing user, suppress duplicate logins inside ten minutes and otherwise queue the plugin-declared returning-player announcement.
## Success Criteria
@@ -16,3 +23,6 @@ The previous SCUM data implementation placed game-specific projections, gift rul
- The SCUM plugin can read and write its scoped platform collections for users, squads, activity, gifts, and map points through generic bridge calls.
- Version-specific SQL/config/log declarations remain in the SCUM manifest and assets.
- Existing generic lifecycle and machine-job dispatch behavior remains intact.
- Player and vehicle coordinates are acquired every three seconds, while squad and squad-member snapshots are acquired every thirty minutes, without a browser request.
- A supervised stdout BattlEye login creates or updates exactly one `scum_users` record and exactly one eligible announcement according to the ten-minute presence window.
- The SCUM page contains no `同步 SCUM.db`, refresh-projection, or equivalent machine-collection button and never substitutes sample records.
@@ -33,3 +33,54 @@ SCUM machine SQLite, configuration, and log operations SHALL remain plugin-decla
- **WHEN** Run completes a declared SQLite query with a structured `rows` result
- **THEN** Platform uses only the plugin-declared collection, upsert keys, and column mappings to persist the rows
- **AND** Platform does not branch on the game, query key, collection name, or row fields
### Requirement: Browser-Independent Automatic Collection
Platform SHALL schedule plugin-declared collection queries from the authenticated Run work-poll path without requiring a browser, page load, or manual synchronization action.
#### Scenario: Coordinate templates become due
- **WHEN** Run polls for work at least three seconds after the latest matching player, vehicle, or map-position query
- **THEN** Platform queues the due plugin-declared SQLite query before selecting work
- **AND** a still-active matching query prevents duplicate queued work
#### Scenario: Squad templates become due
- **WHEN** Run polls for work at least thirty minutes after the latest matching squad or squad-member query
- **THEN** Platform queues the due declared query
- **AND** no page action or open browser is involved
#### Scenario: Complete snapshot removes absent records
- **WHEN** a replace-mode query succeeds with a complete row set
- **THEN** Platform upserts the returned mapped rows and deletes prior records in that scoped collection whose keys are absent
- **AND** an empty successful snapshot clears the collection
### Requirement: Plugin-Declared Login Presence Projection
Platform SHALL apply ordered plugin-declared log projections to durable Run log batches and SHALL NOT hard-code SCUM or BattlEye parsing rules in service code.
#### Scenario: New player completes the BattlEye login sequence
- **WHEN** one supervised stdout stream reports a player name and slot and later reports the same slot's Steam ID within the declared line gap
- **THEN** Platform creates the Steam-ID-keyed `scum_users` record immediately
- **AND** records the login activity
- **AND** queues exactly one plugin-declared global new-player announcement
#### Scenario: Existing player reconnects inside ten minutes
- **WHEN** the same Steam ID completes another login sequence within ten minutes of its stored login observation
- **THEN** Platform treats the player as already online
- **AND** queues no additional welcome announcement
#### Scenario: Existing player returns after ten minutes
- **WHEN** the same Steam ID completes a login sequence after the ten-minute window
- **THEN** Platform updates the stored name and login observation
- **AND** queues exactly one plugin-declared returning-player announcement
#### Scenario: Database enrichment follows stdout identity
- **WHEN** a later SQLite user query returns the same Steam ID with profile, economy, squad, or coordinate fields
- **THEN** Platform merges those fields into the stdout-created record
- **AND** does not create a second user keyed by profile ID or display name
### Requirement: Read-Only Collection Page Loading
The SCUM plugin page SHALL read Platform records automatically and SHALL NOT expose a control that dispatches machine collection.
#### Scenario: User opens a SCUM management page
- **WHEN** any users, squads, activity, gifts, or map page is opened
- **THEN** it reads only the relevant `scum_*` Platform collections and existing command/snapshot results
- **AND** it periodically rereads Platform data for display freshness
- **AND** it contains no `同步 SCUM.db`, projection refresh, audit, or sample-data action
@@ -7,3 +7,11 @@
- [x] Rebuild the SCUM plugin page to use only generic collection bridge actions for users, squads, activity, gifts, and map points.
- [x] Remove obsolete SCUM-specific Platform/frontend data and gift surfaces that conflict with plugin ownership.
- [x] Add focused backend, plugin, and frontend tests; run structure and OpenSpec validation.
- [x] Record redacted evidence from the downloaded v57 database, configuration directory, log directory, and legacy SCUM projects; correct the effective database path and canonical user identity.
- [x] Extend the generic plugin query declaration with automatic cadence and merge/replace row-target semantics, and extend the manifest with ordered log projection plus presence/announcement declarations.
- [x] Schedule due collection queries from authenticated Run work polling, suppress overlapping jobs, and make successful replace snapshots delete absent records including on empty results.
- [x] Project supervised stdout login sequences into Steam-ID-keyed users and activity records, apply the ten-minute presence window, and queue plugin-declared global new/returning-player announcements without a UI session.
- [x] Correct the SCUM user SQL to include account-only users, declare 3-second player/vehicle/position and 30-minute squad/member cadences, and preserve plugin-owned slower cadences for other observations.
- [x] Remove page-owned query dispatch and all manual synchronization/reload controls; reread Platform collections automatically and merge users only by stable IDs.
- [x] Remove unverified gift/activity count ceilings that are not imposed by the SCUM data contract, then update focused backend/plugin/frontend tests.
- [x] Run focused and full verification, `scripts/check-structure.sh`, and `openspec validate rebuild-scum-plugin-owned-data --strict` before marking these tasks complete.