feat(rust): RunicNPC profiles, their push and adoption, per-profile kills (runicnpc stage 4, WIP)

Schema for site NPC profiles (per server, shared or fleet), the per-server
push record, and kills by profile. The push adopts a server's own profiles
before its first push (D244), keeping one whose name a site profile already
has as replaced (D251). The tally's npcProfileKills are stored per profile
and credited to the site profile pushed under that name (D247).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01E14m6SuuY6i1vASFeGDBeY
This commit is contained in:
2026-09-30 04:21:46 -05:00
parent b0143801a2
commit 564a234890
12 changed files with 1200 additions and 0 deletions

View File

@@ -1259,3 +1259,81 @@ CREATE TABLE IF NOT EXISTS rust_zone_preset_servers (
CONSTRAINT fk_rust_zone_preset_servers_server
FOREIGN KEY (server_id) REFERENCES rust_servers (id) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
-- ── RunicNPC (docs/runicnpc/PLAN.md stage 4, D243–D252) ────────────────────
--
-- NPC profiles are authored here and pushed to each server's RunicNPC through
-- the bridge, which then marks that server managed (D221). The same shape as a
-- zone preset: one server, several, or every server (`all_servers`), and two
-- profiles of one name may not share a server. `body` is the profile as RunicNPC
-- reads it (D238), everything but the name, as JSON in a TEXT column.
--
-- `adopted_from` names the server a profile was read from on the site's first
-- push there (D244), and `replaced` marks one whose name a site profile already
-- had on that server (D251): kept for an admin to restore, and pushed nowhere.
--
-- `kills_scope` is D247's setting, how the profile's kills are counted: `server`
-- (the default, kills of that name on the server being looked at), `name`
-- (every server's kills of that name) or `profile` (this site profile's own,
-- wherever it was pushed).
CREATE TABLE IF NOT EXISTS rust_npc_profiles (
id INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(40) NOT NULL,
body TEXT NOT NULL,
all_servers TINYINT(1) NOT NULL DEFAULT 0,
kills_scope VARCHAR(16) NOT NULL DEFAULT 'server',
adopted_from VARCHAR(64) NULL,
replaced TINYINT(1) NOT NULL DEFAULT 0,
created_by INT NULL,
created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
KEY idx_rust_npc_profiles_name (name)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
CREATE TABLE IF NOT EXISTS rust_npc_profile_servers (
profile_id INT UNSIGNED NOT NULL,
server_id VARCHAR(64) NOT NULL,
PRIMARY KEY (profile_id, server_id),
KEY idx_rust_npc_profile_servers_server (server_id),
CONSTRAINT fk_rust_npc_profile_servers_profile
FOREIGN KEY (profile_id) REFERENCES rust_npc_profiles (id) ON DELETE CASCADE,
CONSTRAINT fk_rust_npc_profile_servers_server
FOREIGN KEY (server_id) REFERENCES rust_servers (id) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
-- What the push last did on each server. `adopted_at` is set once, when the
-- server's own profiles were read and imported (D244); until then nothing is
-- pushed there. `pushed` is the name → site profile id map of the last push,
-- which is how a kill is credited to "this profile only" (D247). `state` is
-- `ok`, `failed`, or `absent` (no RunicNPC, or one older than API 3).
CREATE TABLE IF NOT EXISTS rust_npc_sync (
server_id VARCHAR(64) NOT NULL PRIMARY KEY,
adopted_at DATETIME NULL,
state VARCHAR(16) NOT NULL DEFAULT 'pending',
synced_hash CHAR(64) NULL,
boot_id VARCHAR(64) NULL,
pushed TEXT NULL,
refused TEXT NULL,
error VARCHAR(191) NULL,
last_attempt_at DATETIME NULL,
synced_at DATETIME NULL,
CONSTRAINT fk_rust_npc_sync_server
FOREIGN KEY (server_id) REFERENCES rust_servers (id) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
-- Kills of RunicNPC's NPCs, per player, profile name and wipe (D247), from the
-- tally's `npcProfileKills`. `site_profile_id` is the site profile that name was
-- pushed as on that server when the kill arrived, 0 for one the site did not
-- push. The ranking reads it by the profile's `kills_scope`. No foreign keys, as
-- the other stats: a deleted profile keeps its history.
CREATE TABLE IF NOT EXISTS rust_npc_kills (
server_id VARCHAR(64) NOT NULL,
wipe_id VARCHAR(48) NOT NULL,
steam_id VARCHAR(32) NOT NULL,
profile VARCHAR(40) NOT NULL,
site_profile_id INT UNSIGNED NOT NULL DEFAULT 0,
kills INT UNSIGNED NOT NULL DEFAULT 0,
PRIMARY KEY (server_id, wipe_id, steam_id, profile, site_profile_id),
KEY idx_rust_npc_kills_profile (profile, server_id, wipe_id),
KEY idx_rust_npc_kills_site (site_profile_id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;