docs(rust): record the org lead's answers to the step-3 plan (D188-D198)
Groups are per server unless an admin shares them (D189); an in-game change affects that server only, as an exception or a split (D190); ZoneDomes stays, made safe by a domes helper (D194); presets are the admins' own (D195); zone and reward messages get the popup choice (D193); the NPC spike tries both routes (D197). The rest confirm the plan: D188, D191, D192, D196, D198. Refs RunicGateway/Module-Rust#21 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01E14m6SuuY6i1vASFeGDBeY
This commit is contained in:
@@ -1,13 +1,12 @@
|
||||
# `module-rust` — the redesigns, planned
|
||||
|
||||
**Status:** plan, written 2026-09-27, **waiting for the org lead's answers to §10**. It is step 3 of
|
||||
[`PLAN_FIXES.md`](PLAN_FIXES.md) §6: the six changes the org lead decided during the first player walk,
|
||||
each "planned in detail before code". The org lead asked for all six in one plan (2026-09-27), so they can
|
||||
be read together. No code has been written for any of them.
|
||||
**Status:** plan, written 2026-09-27. **Its eleven questions were answered the same day: D188–D198 (§10).**
|
||||
It is step 3 of [`PLAN_FIXES.md`](PLAN_FIXES.md) §6: the six changes the org lead decided during the first
|
||||
player walk, each "planned in detail before code". The org lead asked for all six in one plan (2026-09-27), so
|
||||
they can be read together. No code has been written for any of them yet.
|
||||
|
||||
This is a companion to [`PLAN_FIXES.md`](PLAN_FIXES.md) and [`PLAN.md`](PLAN.md). Where they disagree, this
|
||||
document is later and wins. The answers to §10 become decisions of record from **D188** onward, continuing
|
||||
PLAN_FIXES' numbering.
|
||||
document is later and wins. Its decisions continue PLAN_FIXES' numbering at **D188**.
|
||||
|
||||
| § | Redesign | PLAN_FIXES | Where the work lands |
|
||||
|---|---|---|---|
|
||||
@@ -95,12 +94,12 @@ Grants and memberships are keyed by **website user** today (D28), and adopt refu
|
||||
many who never link. So the site gains **Steam-keyed holders**:
|
||||
|
||||
- `rust_perm_steam_grants (steam_id, permission, scope, source, …)` and
|
||||
`rust_perm_steam_members (group_name, steam_id, scope, …)`, beside the existing user-keyed tables.
|
||||
`rust_perm_steam_members (group_id, steam_id, …)`, beside the existing user-keyed tables.
|
||||
`buildDesired` unions both, and they can overlap; the existing `seenGrant` de-duplication already handles
|
||||
that.
|
||||
- **The import writes Steam-keyed rows, even for a linked player.** They are an exact copy of what the game
|
||||
holds. A user-keyed row would reach every Steam account the person has linked (D28), which would widen what
|
||||
the game held. **Q1.**
|
||||
- **The import writes Steam-keyed rows, even for a linked player (D188).** They are an exact copy of what the
|
||||
game holds. A user-keyed row would reach every Steam account the person has linked (D28), which would widen
|
||||
what the game held.
|
||||
- **A toggle on the screen for a linked player writes a user-keyed row** (it reaches all their accounts, as
|
||||
today). A toggle for an unlinked player writes a Steam-keyed row.
|
||||
- Linking or unlinking an account moves nothing. A Steam-keyed row stays about that game account, and the
|
||||
@@ -111,19 +110,27 @@ many who never link. So the site gains **Steam-keyed holders**:
|
||||
A group is one name fleet-wide today (`rust_perm_groups.name` is the primary key), with one scope, and
|
||||
**everything it carries or contains is the same on every server in that scope**. Real servers disagree: each
|
||||
has its own `default` and `admin`, and a `vip` on one server can carry different permissions from a `vip` on
|
||||
another. The import has to keep both. The proposal (**Q2**):
|
||||
another.
|
||||
|
||||
- **The name stays fleet-wide, and what the group carries and contains becomes per-server.**
|
||||
`rust_perm_group_permissions` gains `scope`, like grants have (D29). The member tables are scoped the same
|
||||
way. The import writes rows scoped to the server it read.
|
||||
- **The group's own scope** is the server it was first imported from. When a second server has the same name,
|
||||
the scope widens to `*`. Every server in scope then has the group, which may mean an empty group on a server
|
||||
that never had one. That is harmless, and it is what the site would push today anyway.
|
||||
- `rust_perm_groups` gains **`parent`**. Title, rank and parent stay one value per name. The import takes the
|
||||
first server's values and reports a disagreement between servers on the screen; it does not pick silently.
|
||||
**D189: a group belongs to one server, unless an admin shares it.** The same group on several servers, or on
|
||||
the whole fleet, is allowed, but it is never assumed:
|
||||
|
||||
- **A group is a row with its own id**, `rust_perm_groups (id, name, title, rank, parent, shared)`. What it
|
||||
carries (`rust_perm_group_permissions`) and who is in it (the member tables) belong to that row, so they
|
||||
are the same on every server the group is on.
|
||||
- **Which servers it is on** is `rust_perm_group_servers (group_id, server_id)`, or `shared = '*'` for every
|
||||
server, including servers added later. A server never has two groups of one name; the model refuses it.
|
||||
- **The import makes one group per server**, even when two servers have a group of the same name with the
|
||||
same contents. Nothing is merged by guessing.
|
||||
- **Sharing is an admin's act.** "Use this group on other servers" picks servers, or all of them. When a chosen
|
||||
server already has its own group of that name, the screen shows the difference and the admin picks which
|
||||
version wins; the other is replaced. "Stop sharing" gives each server its own copy.
|
||||
- `parent` names a group on the same server. A shared group's parent must be on every server the group is on.
|
||||
- **Built-in groups** (`default` and `admin` on both frameworks, plus `moderator` on Carbon) are imported and
|
||||
editable, and **never retired**: the site does not send `RetireGroup` for them. `default`'s membership is
|
||||
never modelled (§1.2).
|
||||
- **Existing installs:** a group scoped `*` today becomes a group shared with every server, and one scoped to a
|
||||
server becomes that server's group. What each server receives does not change.
|
||||
|
||||
### 1.5 The sync, rebuilt on three sets
|
||||
|
||||
@@ -147,7 +154,7 @@ settled within one tick (30 s) plus the read.
|
||||
`rust_perm_sync.imported_at`) takes everything present as site-owned, whatever its policy, and records it
|
||||
as pushed. Policies apply only to changes after that. **This includes existing installs.** The first inventory
|
||||
after the upgrade imports every current hand edit, including the drift rows now waiting for an answer, and
|
||||
clears those rows. That follows D160 as written; **Q11** confirms it.
|
||||
clears those rows (D198, confirming D160 as written).
|
||||
|
||||
### 1.6 The policy (D161)
|
||||
|
||||
@@ -156,24 +163,33 @@ screen's server header.
|
||||
|
||||
| Policy | Added in the game | Removed in the game |
|
||||
|---|---|---|
|
||||
| **auto-adopt** | written as the site's own: a Steam-keyed row (or a scoped group row) with `source: 'adopted'`, scoped to that server, and recorded as pushed | the site's row is deleted, **when that row is scoped to that server alone** |
|
||||
| **auto-adopt** | written as the site's own, for that server only: a Steam-keyed grant scoped to it, or a change to that server's group, with `source: 'adopted'`, and recorded as pushed | the site stops giving it **on that server only** (below) |
|
||||
| **adopt** | a row on the "needs a person" list, with Adopt and Revoke (today's drift answers) | a row on the list, with "Accept the removal" and "Put it back" |
|
||||
| **revoke** | queued as a revocation and removed at this sync | pushed back at this sync (nothing new: the desired set re-applies it) |
|
||||
|
||||
**A removal that touches a fleet-wide row is held for a person under every policy** (**Q3**). A user grant
|
||||
scoped `*`, or a group permission scoped `*`, that disappears from one server cannot be adopted without
|
||||
deciding whether it should still hold everywhere else. Auto-adopt deleting it would take it off every server,
|
||||
and splitting it into per-server rows would silently stop it reaching the next server added. So the list says
|
||||
"removed on `rust-carbon`, granted on all servers" and offers "remove everywhere", "keep everywhere except
|
||||
here" (which rewrites the row into per-server rows) and "put it back".
|
||||
**D190: an in-game change touches that server and nothing else, even when the site's row reaches more
|
||||
servers.** Auto-adopt acts on it without asking:
|
||||
|
||||
- **A grant that reaches several servers** (scoped `*`, or a user grant) and is removed on one gains an
|
||||
**exception** for that server: `rust_perm_exceptions (kind, subject, object, server_id)`. It keeps
|
||||
reaching every other server, including servers added later. `buildDesired` skips an excepted row on that
|
||||
server. The screen shows it as "all servers except `rust-carbon`", and removing the exception gives it back.
|
||||
- **A group shared across servers** that changes on one server (a permission or a member added or removed in
|
||||
the game) is **split**: that server gets its own copy of the group with the change applied, and the shared
|
||||
group stops covering that server. The other servers see no change. The "needs a person" list records the
|
||||
split, so an admin who wanted the change everywhere can apply it to the shared group and share it back.
|
||||
- A change to a row that is **that server's alone** is adopted directly: a removal deletes the row, and an
|
||||
addition writes one.
|
||||
|
||||
### 1.7 The screen (D162, D163, U-1)
|
||||
|
||||
Following PermissionsManager 2.1.2, the reference screenshots in the workspace's `perms-screenshots/`:
|
||||
|
||||
- **A server picker first.** PermissionsManager works on one server, and the site holds several. Everything
|
||||
below is for the chosen server. A row scoped `*` shows an "all servers" badge, and changing it asks whether
|
||||
to change it everywhere or on this server alone.
|
||||
below is for the chosen server. A grant that reaches more servers shows where it reaches ("all servers",
|
||||
"all except `rust-carbon`"), and a shared group says which servers share it. Changing either asks whether to
|
||||
change it everywhere or on this server alone. "This server alone" makes an exception or splits the group, as
|
||||
D190 does for an in-game change.
|
||||
- **Players ⇄ Groups**, as two tabs. The players list shows the server's holders from the inventory first,
|
||||
then a search over every player seen on that server and every linked account. Each row
|
||||
shows the **in-game name**, the **linked website account** if there is one, and the Steam id when the
|
||||
@@ -184,13 +200,12 @@ Following PermissionsManager 2.1.2, the reference screenshots in the workspace's
|
||||
own state on this server** (U-1): *granted*; *through group `vip`* (read-only, with a link to the group);
|
||||
*waiting for their first connection* (a member the store cannot place yet); *not registered here* (the
|
||||
plugin is not loaded, formerly "unresolved"); or *did not land* (D85).
|
||||
- **A player's groups** (add, remove, Remove all). **A group's players** (Remove all), and its title, rank and
|
||||
parent.
|
||||
- **A player's groups** (add, remove, Remove all). **A group's players** (Remove all), its title, rank and
|
||||
parent, and the servers it is shared with (§1.4).
|
||||
- **The server header:** sync state, the policy, and the "needs a person" list (§1.6).
|
||||
- **`zonemanager.ignoreflag.*`** (the question PLAN_FIXES §4.1 left open, **Q4**): 69 of ZoneManager's 70
|
||||
permissions exempt a player from one zone flag each. Under ZoneManager's button they are shown as one
|
||||
collapsed section, "Exempt from zone flags", labelled with the flag names (No kits, No build, PvP god…).
|
||||
The seventieth, `zonemanager.zone`, is listed normally.
|
||||
- **`zonemanager.ignoreflag.*`** (the question PLAN_FIXES §4.1 left open): **D191, listed like any other
|
||||
permission.** They are ZoneManager's 69 exemptions, under ZoneManager's button with their own names, the
|
||||
same as its seventieth, `zonemanager.zone`.
|
||||
|
||||
The existing account-first view (what one website account holds across the fleet) stays, on the account's own
|
||||
page, because that is where the events' grants (D84) and the chat styles (phase 17) are read.
|
||||
@@ -198,9 +213,11 @@ page, because that is where the events' grants (D84) and the chat styles (phase
|
||||
### 1.8 The walk
|
||||
|
||||
On both rigs: install on a server with hand-made state (a `vip` group with a parent, two direct grants, one to
|
||||
an unlinked Steam id), and check that the import shows it exactly. Then an `oxide.grant` / `c.grant` under each
|
||||
of the three policies. Then a revoke in the game of a server-scoped grant and of a fleet-wide one (held for a
|
||||
person). Then Grant all and Revoke all. The plugin buttons must be by owner: `nokits` is under ZoneManager. The
|
||||
an unlinked Steam id), and check that the import shows it exactly, as two separate `vip` groups when both rigs
|
||||
have one. Then an `oxide.grant` / `c.grant` under each of the three policies. Then, under auto-adopt, an in-game
|
||||
revoke of a server-only grant (deleted), of a grant reaching all servers (an exception, still on the other
|
||||
rig), and a change to a shared group (split off, the other rig unchanged). Then share a group, stop sharing it,
|
||||
and Grant all / Revoke all. The plugin buttons must be by owner: `nokits` is under ZoneManager. The
|
||||
large-store measurement is in §1.2.
|
||||
|
||||
---
|
||||
@@ -246,14 +263,24 @@ version.
|
||||
### 2.4 Popups (§4.3)
|
||||
|
||||
`rust.announce`'s `delivery` already has two values from an option source. With two or three choices the
|
||||
editor draws them as a segmented control rather than a dropdown. Popups for **other** player-facing messages
|
||||
(a zone entered, a reward earned) are **Q6**. The zone's enter message is ZoneManager's own chat line, so a
|
||||
popup there means the bridge sending it on ZoneManager's `OnEnterZone` instead.
|
||||
editor draws them as a segmented control rather than a dropdown.
|
||||
|
||||
**D193: a zone's enter and leave messages and a player's "reward earned" message get the same Chat / Popup
|
||||
choice.**
|
||||
|
||||
- **Zones.** `rust.zone.open` gains `enterMessage`, `leaveMessage` and `delivery`. The bridge no longer passes
|
||||
the messages to ZoneManager for its own zones. It subscribes to ZoneManager's `OnEnterZone` and `OnExitZone`
|
||||
and sends them itself, as chat or as a popup, only for the zones in its registry. This also gives F12's
|
||||
"row 5 cannot show an enter message" (PLAN_FIXES, the step-2 walk) a message to show.
|
||||
- **Rewards.** The reward actions' message to the player (`rust.kit.entitle` and the other `eventRewards.js`
|
||||
steps that tell a player what they won) gains the same `delivery`.
|
||||
- **Without PopupNotifications** a popup falls back to chat. The step editor says so beside the choice when the
|
||||
server's hello reports PopupNotifications absent, as `rust.options.delivery` already does.
|
||||
|
||||
### 2.5 The kit weekend as a template (D164)
|
||||
|
||||
There is no template concept anywhere today. The proposal (**Q5**) is **`api.registerEventTemplates`**
|
||||
(**MODULE_API 1.12.0**): a module declares a named skeleton (phases, steps, params filled in, and a short list
|
||||
There is no template concept anywhere today. **D192: core gains `api.registerEventTemplates`**
|
||||
(**MODULE_API 1.12.0**), which any module can use: a module declares a named skeleton (phases, steps, params filled in, and a short list
|
||||
of the inputs the author supplies). The editor's "New event" screen offers the skeletons, asks for their
|
||||
inputs, and opens an ordinary draft. Nothing about a template survives into the definition, so it is purely an
|
||||
authoring shortcut and the runner never sees it.
|
||||
@@ -296,9 +323,10 @@ in the same editor, which must be unchanged apart from the controls.
|
||||
a hook, `RgZoneOptions()`, returning ZoneManager's version and `ZoneFlags.NameToIndex`'s names. The bridge
|
||||
calls it and adds **`zoneManager: { version, flags[] }`** to hello. **Without the helper** the site offers
|
||||
only a name, radius and duration, and says why, the same way D182 degrades.
|
||||
- **Settings offered:** the enter and leave messages, radiation, comfort, temperature, safe zone and
|
||||
permission. The bridge keeps control of the id, location, radius and size, and the rest (rotation, parent,
|
||||
eject spawns) stays out, because each one changes what the bridge believes the zone is.
|
||||
- **Settings offered:** radiation, comfort, temperature, safe zone and permission. The enter and leave
|
||||
messages are the bridge's own (D193, §2.4). The bridge keeps control of the id, location, radius and size,
|
||||
and the rest (rotation, parent, eject spawns) stays out, because each one changes what the bridge believes
|
||||
the zone is.
|
||||
- **Wire:** `world.zone` gains `flags` (names) and `settings` (key → value). The plugin checks every flag
|
||||
against the live list and every setting against the allowlist, and refuses an unknown one with a new reason,
|
||||
`bad-option`. It passes them to `CreateOrUpdateTemporaryZone` as ZoneManager's own key/value arguments,
|
||||
@@ -306,40 +334,49 @@ in the same editor, which must be unchanged apart from the controls.
|
||||
ZoneManager reload.
|
||||
- **The form groups the flags** from one module file (Vehicles, Combat, Building, Loot, NPCs, Communication,
|
||||
Survival). A flag the file does not know (a newer ZoneManager) goes under **Other**, so it is never hidden.
|
||||
**Presets** fill the flags in one click. **Q8** chooses them; the proposal is *Arena* (PvP only, no building,
|
||||
no decay, no suicide, vehicles kept out), *Safe zone* (no PvP, no PvE damage, no building) and *No building*.
|
||||
- **Presets are the admins' own (D195).** None ship with the module, because what an "arena" is differs by
|
||||
server. An admin saves a set of flags and settings under a name, for one server, several servers, or the
|
||||
whole fleet: `rust_zone_presets (id, name, flags, settings, shared)` plus `rust_zone_preset_servers`, the same
|
||||
shape as a shared group (§1.4). The zone step offers the presets that cover its server, and **Save as preset**
|
||||
turns the flags on a step into one. A preset fills the form once, when it is picked; the step keeps its own
|
||||
copy, so editing a preset later does not change a published event.
|
||||
- **Exemptions** are `zonemanager.ignoreflag.<flag>` permissions, managed on the permission screen (§1.7). The
|
||||
zone form links there.
|
||||
- **Flying** has no ZoneManager flag (PLAN_FIXES §4.4) and is not in this step.
|
||||
|
||||
### 3.2 The dome (D167) — and why this plan proposes drawing it ourselves
|
||||
### 3.2 The dome (D167, D194)
|
||||
|
||||
§0.5 found two problems. ZoneDomes' API throws without a player, and its boot path drops every dome of a
|
||||
temporary zone. ZoneDomes itself is only this, for each dome: create `SphereEntity` from one of five prefabs,
|
||||
set `currentRadius` and `lerpRadius` to twice the zone's radius, `enableSaving = false`, and `Spawn()`
|
||||
(`stack` copies to make it darker).
|
||||
**D194: ZoneDomes stays the optional dependency D167 chose, because it already has the logic. What it lacks
|
||||
is filled in by a helper (D168).** §0.5 found two gaps:
|
||||
|
||||
**Q7:** the proposal is that **the bridge draws the dome itself**, from the same prefabs, as part of the zone's
|
||||
own registry entry. It is made with the zone, re-made with it in `RecreateZones`, and killed with it at expiry
|
||||
or teardown. There is no data file that outlives the zone, and there is no boot-order race. It **reverses
|
||||
D167's dependency** (ZoneDomes would no longer be needed) and keeps D167's intent: the zone step gains "show a
|
||||
dome". The alternative keeps ZoneDomes and patches its message call in the helper, which is a Harmony patch
|
||||
on a third plugin to work around a one-line bug.
|
||||
- **Its API throws without a player.** A second helper, **`RunicGatewayDomes.cs`**, fixes that for ZoneDomes
|
||||
and nothing else (a helper does one job). It patches ZoneDomes' message method with Harmony so a call with
|
||||
no player sends no message, and so the call returns normally instead of throwing after the dome is made.
|
||||
It ships in the overlay beside the zone helper and is released, installed and checked the way D182's
|
||||
helper is. Hello reports it, like the zone helper's `patched` / `missing` / `unsupported`.
|
||||
- **Its boot drops the domes of temporary zones.** The bridge owns the fix, because it owns the order: after
|
||||
`RecreateZones` puts a zone back (at boot or after a ZoneManager reload), it calls `AddNewDome` again for
|
||||
each zone that had one, and it does the same when ZoneDomes itself reloads. At expiry and at teardown it
|
||||
calls `RemoveExistingDome`, so a dome never outlives its zone in ZoneDomes' data file.
|
||||
|
||||
The zone step gains **"Show a dome"**, with ZoneDomes' own type and stack. It is offered only when the server
|
||||
reports ZoneDomes loaded **and** the domes helper patched. Otherwise the step says which one is missing.
|
||||
|
||||
**The default type and stack** (PLAN_FIXES §9 question 3) are chosen by looking at each on the rig, in daylight
|
||||
and at night, during the build walk. The form offers type and stack, with that default.
|
||||
and at night, during the build walk.
|
||||
|
||||
### 3.3 The walk
|
||||
|
||||
A zone with the *Arena* preset on each rig: the flags hold for a player without the exemption and not for one
|
||||
with it; a restart and a ZoneManager reload keep the flags and the dome; expiry removes the dome; and no
|
||||
ZoneDomes install is needed.
|
||||
On each rig, a zone made from an admin's preset: the flags hold for a player without the exemption and not
|
||||
for one with it; the enter and leave messages arrive as chat and then as popups; a restart, a ZoneManager
|
||||
reload and a ZoneDomes reload all keep the flags and the dome; expiry and teardown remove the dome from the
|
||||
world and from ZoneDomes' data file; and with the domes helper removed, the step stops offering a dome.
|
||||
|
||||
---
|
||||
|
||||
## 4. The live map's marker types (§4.5; D165)
|
||||
|
||||
- **The site filters (Q9).** The map meta already carries each monument's `kind` and `label`. The toggle is by
|
||||
- **The site filters (D196).** The map meta already carries each monument's `kind` and `label`. The toggle is by
|
||||
**label**, because that is what staff read and it groups the variants (31 substations, four prefabs, one
|
||||
"Substation"). No wire change is needed.
|
||||
- **The default** is a module list of the minor labels, **off** unless turned on: Substation, Underground
|
||||
@@ -351,8 +388,8 @@ ZoneDomes install is needed.
|
||||
on that server's current map with a switch each, a fleet default, and a per-server override. That is the
|
||||
same machinery the four layers already use (D114). The filter is applied where the map meta is served, so a
|
||||
hidden marker is not sent at all.
|
||||
- **The alternative** is the game's own `MonumentType` (§0.7), sent by the plugin. It is authoritative, but it
|
||||
is a wire change, and nothing yet shows that substations are a type of their own.
|
||||
- **Not chosen:** the game's own `MonumentType` (§0.7), sent by the plugin. It is authoritative, but it is a
|
||||
wire change, and nothing yet shows that substations are a type of their own.
|
||||
|
||||
---
|
||||
|
||||
@@ -441,18 +478,39 @@ frame and on the titles screen.
|
||||
|
||||
## 6. NPCs (§4.7)
|
||||
|
||||
**Recommendation (Q10): extend `rust.npc.place` rather than depend on an NPC plugin.** HumanNPC needs two more
|
||||
plugins and replaces Rust's AI. NPC Loadouts cannot dress one spawn differently from the next (§0.10). The
|
||||
bridge already spawns Rust's own scientists, and they are players underneath:
|
||||
**D197: the spike tries both routes on both rigs, then the org lead picks one.** The goal is the one the org
|
||||
lead named: NPCs with different kits and names. NPC Loadouts is out, because it cannot dress one spawn
|
||||
differently from the next (§0.10). The spike reports on each route before any site work.
|
||||
|
||||
**Route A: extend `rust.npc.place`.** The bridge already spawns Rust's own scientists, and they are players
|
||||
underneath:
|
||||
|
||||
- **`kit`**: a Kits kit. After spawning, the bridge empties the NPC's inventory and gives it the kit through
|
||||
Kits' API (`GiveKit`). Kits is already a required plugin (PLAN.md R6).
|
||||
- **`name`**: the NPC's `displayName`, which already flows into the killfeed (`attackerName`) and on to F2's
|
||||
labels.
|
||||
- The spike answers: does a scientist given a kit fight with the kit's weapon, or fall back to its prefab
|
||||
loadout? Does the name reach the killfeed? Does Rust re-dress it on a later tick?
|
||||
|
||||
**A spike decides it before any site work:** on each rig, does a scientist given a kit fight with the kit's
|
||||
weapon, or fall back to its prefab loadout? And does the name reach the killfeed? If the AI ignores the kit,
|
||||
this section comes back to the org lead with the HumanNPC route costed.
|
||||
**Route B: HumanNPC.** Its API (`SpawnHumanNPC`, `SetHumanNPCInfo`, `RemoveHumanNPC`) gives a name and a kit
|
||||
directly. The gotchas the spike checks, each of which could rule it out:
|
||||
|
||||
- **Two more dependencies.** It requires Pathfinding and Waypoints. All three become optional dependencies
|
||||
that operators have to install, and the egg and the installer do not ship them.
|
||||
- **Carbon.** Nothing says it runs on Carbon. The spike loads all three on `rust-carbon`.
|
||||
- **Its own persistence.** HumanNPC saves its NPCs to its data file and respawns them on boot, which is the
|
||||
ZoneDomes problem again (§0.5). An event's NPC must be removed at teardown and must not come back after a
|
||||
restart that happened mid-event. The bridge would track them in its registry by HumanNPC's id, not by an
|
||||
entity net id as `world.place` does today.
|
||||
- **Its own AI.** Its NPCs do not use Rust's scientist brain. Hostility, the weapon they use and whether they
|
||||
chase are its settings. The spike checks that a hostile one actually fights.
|
||||
- **Being counted as a player.** A HumanNPC is a `BasePlayer` with a generated `userID`. The bridge's death
|
||||
and kill paths (`OnPlayerDeath`, the tally, the killfeed, a player-kill title) must count it as an NPC, not
|
||||
as a player. The spike kills one and reads the frames.
|
||||
- **Upkeep.** Its last release date and open issues are recorded, because an abandoned dependency is a cost.
|
||||
|
||||
If route A works on both frameworks it needs nothing new installed, and that is weighed against what B offers
|
||||
beyond it.
|
||||
|
||||
---
|
||||
|
||||
@@ -465,9 +523,10 @@ All of these are additions to the unreleased protocol 13. PROTOCOL.md §19 gains
|
||||
| `perm.inventory` / its paged reply (owners, groups with parents, users, leased) | site → game | 1.2 |
|
||||
| `perm.sync`: groups carry `parent`; `managed` and the foreign report removed | site → game | 1.2 |
|
||||
| `kits.uses` / its reply | site → game | 2.6 |
|
||||
| hello: `zoneManager: { version, flags[] }` (from the helper) | game → site | 3.1 |
|
||||
| `world.zone`: `flags`, `settings`, `dome: { type, stack }`; reason `bad-option` | site → game | 3.1, 3.2 |
|
||||
| `world.place` for an NPC (the verb behind `rust.npc.place`): `kit`, `name` | site → game | 6 |
|
||||
| hello: `zoneManager: { version, flags[] }` (from the zone helper); `zoneDomes: { loaded, version }` and the domes helper's state | game → site | 3.1, 3.2 |
|
||||
| `world.zone`: `flags`, `settings`, `enterMessage`, `leaveMessage`, `delivery`, `dome: { type, stack }`; reason `bad-option` | site → game | 2.4, 3.1, 3.2 |
|
||||
| the reward verbs' player message: `delivery` | site → game | 2.4 |
|
||||
| `world.place` for an NPC (the verb behind `rust.npc.place`): `kit`, `name`, if route A is chosen | site → game | 6 |
|
||||
| `player.tally`: `animalKills`, `weaponKills`, `headshots`, `bestPvpM`, `bestPveM`, `apcKills`, `heliKills`, `crafted`, `built`, `repaired`, `healed`, `rockets`, `explosives`, `missions` | game → site | 5.1 |
|
||||
|
||||
## 8. MODULE_API 1.12.0 (core, website `main`)
|
||||
@@ -485,43 +544,28 @@ Each item is built on `edge`, walked on both rigs, and PR'd with its spec, like
|
||||
|
||||
1. **The permission manager** (§1). It is the largest, and §3's exemptions point at it.
|
||||
2. **Chat titles** (§5), including the mission hook.
|
||||
3. **Zones and the dome** (§3).
|
||||
3. **Zones and the dome** (§3), with the zone half of D193's popups and the domes helper.
|
||||
4. **The map's marker types** (§4).
|
||||
5. **NPCs** (§6), spike first.
|
||||
5. **NPCs** (§6): the two-route spike, then the org lead's pick, then the build.
|
||||
6. **The step editor and the kit weekend** (§2). It is core's, so it can be built alongside any of the above.
|
||||
It goes out as its own website PR with the Module-uo proof.
|
||||
|
||||
Then PLAN_FIXES §8's re-walk on Oxide and Carbon, and the cutover.
|
||||
|
||||
## 10. Questions for the org lead
|
||||
## 10. Decisions of record
|
||||
|
||||
Each has a recommendation. The answers become D188 onward.
|
||||
The plan asked eleven questions. The org lead answered them on 2026-09-27, the day it was written.
|
||||
|
||||
1. **Q1: import rows for a linked player.** *Recommended:* the import writes Steam-keyed rows even when the
|
||||
Steam id is linked, so it copies the game exactly; a toggle made on the site for a linked player is a user
|
||||
grant, reaching all their accounts (D28). *Alternative:* the import writes user rows for linked players,
|
||||
which widens a holding to the person's other Steam accounts.
|
||||
2. **Q2: groups across servers.** *Recommended:* one name fleet-wide, with what it carries and who is in it
|
||||
scoped per server (§1.4). *Alternative:* groups become per-server, which would give `vip` on each server
|
||||
its own row and its own screen.
|
||||
3. **Q3: an in-game removal of a fleet-wide row.** *Recommended:* held for a person under every policy, with
|
||||
"remove everywhere / keep everywhere except here / put it back" (§1.6). *Alternative:* auto-adopt narrows
|
||||
it to "every server except this one" by itself.
|
||||
4. **Q4: `zonemanager.ignoreflag.*` on the screen.** *Recommended:* one collapsed "Exempt from zone flags"
|
||||
section under ZoneManager, labelled by flag name (§1.7).
|
||||
5. **Q5: templates.** *Recommended:* a core `registerEventTemplates` (MODULE_API 1.12.0) that any module can
|
||||
use, with the kit weekend as the first (§2.5). *Alternative:* a Rust-only "Kit weekend" button on the Rust
|
||||
admin pages that creates the draft.
|
||||
6. **Q6: popups beyond announcements.** *Recommended:* not in this step; `rust.announce` gets the Chat/Popup
|
||||
control. *Alternative:* the bridge sends a zone's enter message and "reward earned" as popups when
|
||||
PopupNotifications is present.
|
||||
7. **Q7: the dome.** *Recommended:* the bridge draws it itself, which reverses D167's dependency on ZoneDomes
|
||||
and keeps its intent (§3.2). *Alternative:* keep ZoneDomes and patch its message call from the helper.
|
||||
8. **Q8: zone presets.** *Proposed:* Arena, Safe zone, No building (§3.1). Add, drop or rename.
|
||||
9. **Q9: who filters the map markers.** *Recommended:* the site, by label, with a default-off list of minor
|
||||
labels (§4). *Alternative:* the plugin sends Rust's `MonumentType` and the default follows it.
|
||||
10. **Q10: NPCs.** *Recommended:* extend `rust.npc.place` with a kit and a name, after a spike (§6).
|
||||
*Alternative:* depend on HumanNPC, which brings Pathfinding and Waypoints.
|
||||
11. **Q11: the first import on an existing install.** *Recommended (D160 as written):* the first inventory
|
||||
after the upgrade adopts everything present, including hand edits that are waiting for an answer today
|
||||
(§1.5). *Alternative:* existing drift rows stay on the "needs a person" list and the import skips them.
|
||||
| # | Decision | Rejected | § |
|
||||
|---|---|---|---|
|
||||
| **D188** | **The import records a holding by Steam account, exactly as the game has it, even when that account is linked.** A toggle made on the site for a linked player is a grant to their website account and reaches every Steam account they link (D28). | Recording imported holdings against the website account, which would widen them to the person's other Steam accounts. | 1.3 |
|
||||
| **D189** | **A group belongs to one server unless an admin shares it** with chosen servers or the whole fleet. The import makes one group per server, even for identical names. The same group across servers is allowed but never assumed. | One group name fleet-wide with per-server contents (the plan's first proposal). | 1.4 |
|
||||
| **D190** | **An in-game change affects that server only, and auto-adopt applies it without asking.** A grant that reaches more servers gains an exception for that server; a shared group is split, and that server gets its own copy. | Holding a change to a multi-server row for a person under every policy. | 1.6 |
|
||||
| **D191** | **ZoneManager's `ignoreflag` permissions are listed like any other permission.** | One collapsed "exempt from zone flags" section. | 1.7 |
|
||||
| **D192** | **Core gains event templates** (`registerEventTemplates`, MODULE_API 1.12.0), and the kit weekend is the first. | A Rust-only kit-weekend button. | 2.5 |
|
||||
| **D193** | **A zone's enter and leave messages and a player's reward message get the Chat / Popup choice**, as announcements have. The bridge sends zone messages itself for its own zones. | Popups for announcements only in this step. | 2.4 |
|
||||
| **D194** | **ZoneDomes stays the optional dependency (D167 stands).** A domes helper makes its API safe without a player; the bridge re-adds domes after re-creating zones and removes them at expiry and teardown. | The bridge drawing the dome itself (the plan's first proposal). | 3.2 |
|
||||
| **D195** | **Zone presets are the admins' own**, saved for one server, several, or the fleet. None ship. | Shipped presets (Arena, Safe zone, No building). | 3.1 |
|
||||
| **D196** | **The site filters map markers by label**, with a default-off list of minor labels; every other label shows. | The plugin sending Rust's `MonumentType`. | 4 |
|
||||
| **D197** | **The NPC spike tries both routes**, extending `rust.npc.place` and HumanNPC, and reports HumanNPC's gotchas before the org lead picks. | Deciding the route before the spike. | 6 |
|
||||
| **D198** | **The first inventory on an existing install adopts everything present**, including hand edits waiting for an answer today (D160 as written). | Leaving existing drift rows on the "needs a person" list. | 1.5 |
|
||||
|
||||
Reference in New Issue
Block a user