Compare commits

...

2 Commits

Author SHA1 Message Date
e8f0ff2407 docs(rust): §5 compiled on both rigs, and what the item-list probe corrected
PLAN_REDESIGNS 5.8 records the probe's lists and the three members it corrected; PROTOCOL 19.10 names the crafter, the mission assignee and the heal target as built.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01E14m6SuuY6i1vASFeGDBeY
2026-09-29 02:07:30 -05:00
e3c5d7128c docs(rust): the chat-title conditions as built — PROTOCOL 19.10, PLAN_REDESIGNS 5.8 and D209
PROTOCOL.md §19.10 documents player.tally's new fields and the ten hooks
behind them. PLAN_REDESIGNS §5.8 records what the build settled (the
category ids, where each is read from, the empty-text rule, the admin
API, the plugin's counting choices) and what is left before the walk.
D209: a title rule's "NPC kills" ranks human NPCs only.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01E14m6SuuY6i1vASFeGDBeY
2026-09-28 23:32:59 -05:00
2 changed files with 101 additions and 2 deletions

View File

@@ -4,7 +4,8 @@
On 2026-09-28 the org lead added the first-class optional plugins (§11, D199–D202), and after the RaidableBases spikes, D203–D208 (§11.4).
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.
they can be read together. **Built so far:** §1, walked 2026-09-28 (§1.9); §5, built 2026-09-28 with its
rig walk still to come (§5.8, D209).
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. Its decisions continue PLAN_FIXES' numbering at **D188**.
@@ -526,6 +527,62 @@ shirt and a weapon; build and repair a wall; bandage and revive another player;
destroy an APC; shoot down a helicopter; finish a mission and abandon one. Each count is checked in the tally
frame and on the titles screen.
### 5.8 As built (2026-09-28/29; compiled on both rigs, not yet walked)
Rust-Plugins `feat/title-conditions`, Module-Rust `feat/title-conditions`. The wire is
[`PROTOCOL.md`](../../rust-link/PROTOCOL.md) §19.10, and the admin API is the module's swagger fragment
(`/admin/rust/title-categories…`).
**One question the plan left open, answered (D209):** a rule's `npckills` now ranks **human NPCs only**,
every NPC kill less the animals. Titles had never shipped, so no live rule changes meaning; the rigs'
walk database held one such rule.
**What the build settled:**
- **The categories are ids a rule stores**, in `model/titles/titles.js`: `kills`, `npckills`,
`animalkills`, `bowkills`, `meleekills`, `bladekills`, `revolverkills`, `headshots`, `pvpdistance`,
`pvedistance`, `apckills`, `helikills`, `wood`, `ore`, `plants`, `attire`, `weapons`, `built`, `repaired`,
`healed`, `rockets`, `explosives`, `missions`, plus `playtime`, which predates them. **Playtime has no
default title**, so it cannot be renamed and a playtime rule always carries its own text.
- **Each category names where it is read from:** a summed column, a column's `MAX` (the two distances), a
difference (`npc_kills − animal_kills`, in signed arithmetic, since a row counted before `animal_kills`
existed would otherwise wrap), or a named list over `rust_weapon_kills` or `rust_gather_totals`. Every
standing is ordered by value, then by who was seen last, the leaderboard's own tie-break.
- **The lists are one file**, `model/titles/lists.js`: bow, melee, blade, revolver, wood, ore and plants.
Every blade is also melee. Plants include cloth, which is what hemp picks up as, and leave out seeds.
- **A rule's text may be empty**, meaning its category's title. **A text typed as only markup is still
refused** (§33.4 reading 3): brackets are a mistake, not a request for the default. The form shows the
category's current title as a placeholder and never saves it into the rule.
- **The category titles** are `GET /admin/rust/title-categories` and
`PUT /admin/rust/title-categories/:stat` (an empty `text` is the Reset). A change forgets every server's
cached answer, so the next push carries the new name. The servers list also returns them, so each
server's summary line names what its rules show.
- **The plugin's choices, each to be checked by the walk:** a repair counts only when the entity's health
rose by the next tick (the hook fires before the game decides there is anything to repair); a syringe
revive raises both the revive and the heal hook and counts once; an explosive is a `TimedExplosive` that
deals damage, which leaves out supply signals and smoke; the helicopter's last-hitter credit (§5.4) is
built rather than waiting for the walk, because proving it unnecessary needs a helicopter shot down
first.
**Read off the rig, 2026-09-29.** A read-only probe walked the live server's `ItemManager.itemList` and named
each held entity's prefab and class. The lists in `lists.js` are that output, not memory: the revolvers are
`pistol_revolver`, `python` and `hc_revolver` (there is no `revolver.entity`); the bows include the legacy
bow, the mini crossbow and the bowless crossbow, and leave out the speargun, a crossbow underneath; melee is
every `BaseMelee` except the pies (`Food` is a `BaseMelee` too). C4, the satchel and the beancan are all
`TimedExplosive` subclasses, so §5.1's explosive test holds. The same probe corrected three members the
plan had assumed, each of which would not have compiled or would never have been called:
- `ItemCraftTask` has no owner; the crafter is `ItemCrafter.owner`.
- A mission's assignee is the hook's third argument (`MissionSuccess(instance, assignee)`); the instance
has no accessor for it.
- `MedicalTool.GiveEffectsTo` now takes an `IMedicalToolTarget`, so `OnHealingItemUse` takes its target as
`object` and counts only a player.
**Compiled on both rigs, 2026-09-29.** `rust-oxide` (a fresh install on the published egg) and
`rust-carbon`: all 45 hooks bound, and `OnEntityTakeDamage` fires on world damage with nobody on (53 and
109 in the first half-minute), which is the cost the helicopter credit was expected to have. The other nine
new hooks need a player: that is §5.7's walk.
---
## 6. NPCs (§4.7)
@@ -639,6 +696,12 @@ The org lead added §11 on 2026-09-28 and answered its questions the same day:
| **D207** | **A step places its base either where RaidableBases picks (the default, D203) or at an admin-given spot:** x and z, plus either a height adjustment or an exact height. The spot needs the raids helper; without it the step offers only the plugin's pick. | Only an admin-given spot; keeping D203 alone. | 11.4.5 |
| **D208** | **A blocked spot is refused, with the reason.** The step does not spawn when the plugin's area check fails or the helper's water and terrain test fails, and the run log says why ("player building", "raid base", "in water"). The editor runs the same check when the step is saved. | Falling back to the plugin's pick; spawning anyway. | 11.4.5 |
The §5 build raised one more, answered on 2026-09-28:
| # | Decision | Rejected | § |
|---|---|---|---|
| **D209** | **A title rule's "NPC kills" ranks human NPCs only** (scientists and bandits): every NPC kill less the animals, which Beastbane ranks. The public leaderboard's NPC column still counts both (§5.2). | Keeping the old all-NPC count as a 25th category beside Exterminator. | 5.8 |
## 11. First-class optional plugins (D199–D202)
The org lead named four plugins the module supports as first-class integrations. Kits already is one. The other

View File

@@ -461,7 +461,7 @@ Every kind protocol 2 defines, and the hook behind it. **`class` is not a field
| `player.respawned` | `OnPlayerRespawned` | **presence** | steamId |
| `player.death` | `OnPlayerDeath` | **presence** | victim, attacker, attackerType, weapon, distance, grid |
| `player.chat` | `OnPlayerChat` | **presence** | steamId, name, channel, message |
| `player.tally` | *aggregate* — see §8.6 | **presence** | steamId, gathered{}, npcKills, structures |
| `player.tally` | *aggregate* — see §8.6 | **presence** | steamId, gathered{}, npcKills, structures; from protocol 13 the chat-title counts (§19.10) |
| `entity.destroyed` | `OnEntityDeath` on owned building blocks — **and doors, external walls and the cupboard from protocol 7 (§13)** | **staff** | ownerId, prefab, grid, attacker; from protocol 7 also `structure`, `buildingId`, `authorized` |
| `player.reported` | `OnPlayerReported` | **staff** | reporter, target, subject, message, type |
| `player.banned` / `player.unbanned` | `OnUserBanned` / `OnUserUnbanned` | **staff** | id, name, **ip**, reason |
@@ -2245,3 +2245,39 @@ logs its summary instead of replying.
Walked on both rigs, 2026-09-27/28. Oxide gave 85 permissions, every one owned (`RustCore` owns
`oxide.*`), in 148 ms over four ticks. Carbon gave 104, the 30 `adminmodule.*` unowned, in 204 ms. On
both, **`zonemanager.ignoreflag.nokits` belongs to ZoneManager**.
### 19.10 What the tally adds for chat titles
The twenty-three chat-title conditions (`docs/modules/rust/PLAN_REDESIGNS.md` §5, D172–D174) ride on
`player.tally` (rule 2: aggregates, never a frame per shot, craft or swing). Every new field is
**absent when nothing moved**, as the existing three are, so a site on an older build ignores them and a
frame from an older plugin has none:
```json
{"kind":"player.tally","type":"event","steamId":"76561198038695917","name":"whitlocktech",
"npcKills":3,"animalKills":2,"weaponKills":{"bow_hunting.entity":2,"machete.weapon":1},
"headshots":1,"bestPvpM":187.5,"bestPveM":42.1,"apcKills":1,"heliKills":1,
"crafted":{"attire":3,"weapon":1},"built":12,"repaired":4,"healed":2,"rockets":6,
"explosives":3,"missions":1}
```
| Field | Counted from | Notes |
|---|---|---|
| `animalKills` | `OnEntityDeath`, victim a `BaseNpc` | **Also** counted in `npcKills`, which keeps meaning every NPC and animal so the public column does not change mid-wipe. Human NPCs are the difference (D209) |
| `weaponKills` | every credited kill, any victim | By the **same weapon short prefab name the killfeed carries** (`WeaponPrefab`, else `Weapon`). The site classifies them into bow, melee, blade and revolver lists it keeps (§5.3), so a new Rust weapon needs no plugin release |
| `headshots` | the killing blow's `HitInfo.isHeadshot`, any victim | |
| `bestPvpM`, `bestPveM` | the killfeed's distance | **This interval's maximum, not a sum.** The site keeps the running best with `GREATEST`. PvE is NPCs and animals |
| `apcKills`, `heliKills` | `OnEntityDeath`, victim `BradleyAPC`; `PatrolHelicopter` or `CH47Helicopter` | The killing blow's player (D174). A patrol helicopter usually dies by crashing and its blow names nobody, so **the last player who hurt it within 60 s** is credited instead, recorded by `OnEntityTakeDamage` for those three types only |
| `crafted` | `OnItemCraftFinished`, by `item.info.category` lower-cased | Every category is sent; the site reads `attire` and `weapon`. Counts `item.amount`. The crafter is `ItemCrafter.owner` (the task carries none) |
| `built` | `OnEntityBuilt` placing a `BuildingBlock` | A deployable is not a structure |
| `repaired` | `OnStructureRepair` | The hook fires on every hammer hit **before** the game decides there is anything to repair, the resources for it, or no recent attack. So the health is read then and again next tick, and only a hit that raised it counts |
| `healed` | `OnHealingItemUse` on **another** player; `OnPlayerRevive` | Yourself never counts (D174). A syringe on a downed player raises both hooks for one use; it counts once. The heal's target is an `IMedicalToolTarget` in current Rust, so the bridge takes it as `object` and counts only a player |
| `rockets` | `OnRocketLaunched` | |
| `explosives` | `OnExplosiveThrown`, `OnExplosiveDropped` | A `TimedExplosive` that deals damage: a supply signal and a smoke grenade go through the same hooks and do not count |
| `missions` | `OnMissionSucceeded(mission, instance, assignee)` | Rust's own NPC missions (D173), credited to the hook's `assignee`. A failed or abandoned one raises a different hook and never counts |
**All ten hooks are `void`** (§8.7). Four of them can veto — `OnStructureRepair`, `OnHealingItemUse`,
`OnPlayerRevive`, and `OnEntityTakeDamage`, which cancels the damage — and none returns anything. All ten
are in `ExpectedHooks`, so a framework that stops raising one shows as zero in `rg.hooks`.
No sidecar change: the sidecar stores and forwards `player.tally` whole, as it does every frame.