From 0a826ee366f0a267e1eb15c2d3eca8badfa4edea Mon Sep 17 00:00:00 2001 From: wtclaude Date: Fri, 9 Oct 2026 01:25:57 -0500 Subject: [PATCH] =?UTF-8?q?docs(runicnpc):=209e=20step=201=20=E2=80=94=20d?= =?UTF-8?q?ome=20stack=203=20and=20the=20PopupNotifications=20banner=20(D3?= =?UTF-8?q?26-D328)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit D326: a blank dome stack is 3 (module only; the wire still defaults to 1). D327: the recommended PopupNotifications banner ships as a paste block (rust-link/INTEGRATION.md §2.4), from the installer (install and update) and from the egg's install. D328: "at the plugin's defaults" is all or nothing. PLAN_REDESIGNS §3's "until it is chosen by eye" closes; rust-link/INSTALL.md says what the installer and the egg write, and that uninstall leaves it. Code: Module-Rust#36, installer#39, Rust-Link#22. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01E14m6SuuY6i1vASFeGDBeY --- modules/rust/PLAN_REDESIGNS.md | 8 +++++-- runicnpc/PLAN.md | 20 ++++++++++++++++ rust-link/INSTALL.md | 13 ++++++++-- rust-link/INTEGRATION.md | 44 ++++++++++++++++++++++++++++++++++ 4 files changed, 81 insertions(+), 4 deletions(-) diff --git a/modules/rust/PLAN_REDESIGNS.md b/modules/rust/PLAN_REDESIGNS.md index 11bb4c8..fb338a9 100644 --- a/modules/rust/PLAN_REDESIGNS.md +++ b/modules/rust/PLAN_REDESIGNS.md @@ -470,7 +470,10 @@ Built on `edge` as Rust-Plugins `feat/zones-domes`, Module-Rust `feat/zones-dome Green and Purple are the Twitch battle-royale spheres, which ZoneDomes itself says show only where they meet terrain or an object, so they read as a ring at the zone's edge. **The org lead chose the full dome as the default:** the step lists Standard first as "Full dome (shaded)", labels each colour for what it shows, and -a `dome` sent without a `type` is Standard. The default stack is still 1, until it is chosen by eye. +a `dome` sent without a `type` is Standard. **The default stack is 3** (D326, `runicnpc/PLAN.md`): the org +lead chose it by eye at noon in RunicNPC's 9d player session, and the module sends it when the step's stack +is blank. Steps saved with a blank stack draw 3 from their next run. The wire is unchanged: a `dome` sent +without a `stack` still gets 1 from the bridge, and the module always sends one. **Compiled on both rigs, and the rig rows walked, 2026-09-29** (no player; the "file API outage" that morning was a Git Bash path-rewriting bug in the walk's own helper, not the rigs): @@ -540,7 +543,8 @@ so the site refuses it until protocol 13 ships: - **The in-game rows, deferred by the org lead (2026-09-29) to the later in-game walk, with §5.7's:** - The flags hold for a player without the exemption and not for one with it. - The enter and leave messages arrive as chat and as a popup. - - The default stack is chosen by eye, in daylight and at night. + - ~~The default stack is chosen by eye, in daylight and at night.~~ Chosen at noon in RunicNPC's 9d + player session: 3 (D326). --- diff --git a/runicnpc/PLAN.md b/runicnpc/PLAN.md index 78a17f5..dfd313b 100644 --- a/runicnpc/PLAN.md +++ b/runicnpc/PLAN.md @@ -148,6 +148,9 @@ architectural or design decision is implemented. | **D323** | **Each example profile wears its own outfit; the weapons of D289 stay.** The raider wears scrap armour (coffee-can helmet, roadsign jacket and kilt, hoodie, pants, boots, leather gloves); the camp guard metal (metal facemask and chest plate, long-sleeve shirt, pants, boots, roadsign gloves); the sniper hide (boonie hat, balaclava, hide poncho, pants and boots); the Juggernaut heavy plate, as before. Three shared one hoodie, pants and boots, so they read alike in game. Only a first load writes them (D284): no live server has RunicNPC yet, and the test rigs were given them by resetting `examplesWritten` (9d player walk, 2026-10-09). | One outfit for all; replacing a server's kits that still match the old ones. | | **D324** | **An example kit's items are written at full durability, read from this Rust's item definitions when the kits are written.** Kits sets an item's condition and maximum straight from its file. The writer put 0 on armour (a metal facemask 0 of 320, heavy plate 0 of 1,000, so it barely protected) and 100 on every weapon whatever its real maximum (an M249's is 500). Now an item with a condition gets the definition's maximum and one without gets 0. The harness checks it (`s6.examples.fullCondition`). | A fixed table of maximums, which a Rust update would make stale. | | **D325** | **`/rnpc here` stands the NPC 1 m in front of the admin, facing them, not at their feet.** At their feet it spawned inside the admin, looking away (9d player walk). The spot must fit: nothing at chest height in the way, ground no more than 2 m below, and navmesh for one that walks. Otherwise it falls back to the admin's own spot and the reply says so. The harness checks the distance (`cmd.chat.here`). | 1.5 m or 3 m in front; refusing when the spot does not fit. | +| **D326** | **An event zone's dome stacks 3 spheres when its step leaves the stack blank, not 1.** The org lead chose it by eye at noon in the 9d player session. It changes in `module-rust` only, so steps saved with a blank stack draw 3 from their next run. The wire is unchanged: the bridge still gives a `dome` sent without a `stack` 1, and the module always sends one (2026-10-09). | The bridge's default too, which changes a message's meaning and needs a plugin release. | +| **D327** | **A recommended PopupNotifications look ships three ways:** a banner across the top of the screen (left 0.15, bottom 0.87, width 0.7, height 0.07, spacing 0.005), no close button, Roboto Condensed Bold at 18, panel transparency 0.8. It is a block to paste in `rust-link/INTEGRATION.md` §2.4, and both the installer (`install` and `update`) and the Pterodactyl egg's install write it. Each writes it with PopupNotifications' `Version`, because the plugin resets a config without one. PopupNotifications stays optional and is the operator's to install (D141). The org lead: whether `update` also writes it "does not matter", since no host outside the test rigs is installed yet. | Writing it on a first install only; writing it once per host and recording that. | +| **D328** | **"Still at the plugin's defaults" means every setting, all or nothing.** The installer and the egg write the banner only where `PopupNotifications.json` is missing, or where every setting equals PopupNotifications 0.2.1's own defaults (its `Version` aside; numbers compared as numbers). A config with any one setting changed, or one that does not parse, is kept whole and never merged into. | Field by field: our values only where that one field is still at its default, which mixes two looks nobody chose. | **Borrowing, not copying.** NpcSpawn states no licence at all, so its source grants us nothing and is read only as a description of *what* can be done in Rust. HumanNPC is MIT on uMod, which is GPL-compatible, but §1.2 rules out its @@ -1825,6 +1828,23 @@ put three steps in 9e, in this order (2026-10-09), each ending in its PRs: `module-rust` `edge` → `main` with RunicNPC required. Each release is checked (checksums, the bundle composing) before the next merge. The row the walk left for it: the installer's RunicNPC path from a real bundle. +**9e step 1, as built (2026-10-09; D326–D328).** Before building, the org lead answered three questions. The dome change +is in the module only. "At defaults" means all settings or none. Whether `update` also writes the banner "does not matter", +since no host outside the test rigs is installed yet, so both verbs apply the rule. + +- **Dome stack 3:** Module-Rust#36. A blank stack sends 3, and the field's hint says so. 505/505 tests pass. +- **The banner, three ways:** the paste block in `rust-link/INTEGRATION.md` §2.4; installer#39 (`popup.rs`; a + `popup look` row in the plan; `install` and `update`; 188 tests pass, 5 of them new); Rust-Link#22 (`rg_popup` in + `egg/install.sh`; a failure there is said in the console and does not fail the install). Both write PopupNotifications' + `Version`, because the plugin resets a config without one, and keep a later version they find. +- **How it was checked:** + - **The installer:** run against two scratch roots. No published bundle carries RunicNPC yet, so the run bypassed its + check in the working tree only. Oxide with no config got the banner. Carbon at the rig's defaults got the walk's + banner file exactly. On `update`, a hand-changed config and an existing banner were both kept. + - **The egg's function:** run in `ghcr.io/ptero-eggs/installers:debian` over the same cases, plus a config that is + not JSON and a directory that cannot be made. +- **Left for step 3:** both paths from a real bundle (the installer on a rig, the egg by a panel reinstall). + **Then the Rust plan resumes** at PLAN_REDESIGNS §9 item 6 (the step editor and the kit weekend) and §11. ### Stage 10 — Custom navigation (only if needed) diff --git a/rust-link/INSTALL.md b/rust-link/INSTALL.md index d5ab07f..fc6d7a6 100644 --- a/rust-link/INSTALL.md +++ b/rust-link/INSTALL.md @@ -77,6 +77,12 @@ What a run does: The plugin fills in every other key on its first load. An existing config is never rewritten, and one that names a different server stops the run and says so — the website locks a server's id once it has seen it. + It also writes the **recommended PopupNotifications banner** (`PopupNotifications.json` beside it; + [`INTEGRATION.md` §2.4](INTEGRATION.md#24-the-recommended-popupnotifications-look-optional)), but + only where that file is missing or still exactly the plugin's own defaults. A config with any + setting changed is kept whole. `install` and `update` both apply this rule, and the plan names + which case it found (`popup look … banner written` or `kept`). PopupNotifications itself stays + optional and yours to install. One that is already running shows the banner after its next reload. 4. **Installs the sidecar**, writes its config with this server's ports, and registers its service. 5. **Places the plugin** in `oxide/plugins/` or `carbon/plugins/`. Both frameworks load a plugin the moment it lands, so on a running server the bridge comes up at once; on a stopped one, at @@ -125,7 +131,10 @@ When you create a server from it: against the bundle's checksum, and only then places them: the sidecar in `rust-link/`, the plugin in `oxide/plugins/` or `carbon/plugins/`. `RunicNPC.cs` goes beside the plugin (its data directory is left for RunicNPC to make); a bundle without RunicNPC is refused. Any mismatch fails the install with the reason, before -anything is placed. +anything is placed. It then writes the recommended PopupNotifications banner by the installer's +rule: only where `PopupNotifications.json` is missing or still the plugin's defaults, so a look +you chose survives a reinstall. If that write fails, the console says so and the install still +succeeds, because it only affects how popups look. **The first boot** prints, in the console, the token the sidecar generated — **once** — and a line with what the website needs: @@ -230,7 +239,7 @@ With the installer: |---|---| | `doctor --game rust [--server-id ]` | Per server: the framework; whether the plugin file is still the one deployed; each helper deployed beside it, as a **warning** when missing or edited (the bridge runs without one, and the row says what that costs); RunicNPC as a **failure** when it is missing or was never installed (it is required: without it every NPC an event places is refused), and a warning when edited by hand; that the plugin's config names this server; the required uMod plugins; the service; and `/health` through to **plugin connected**. A stopped server is a warning; a running one whose plugin never connected is a failure, printed with the framework versions the plugin is known good on | | `update --game rust` | Moves the sidecar and every server's plugin to the current bundle, and restarts the sidecars. Always all servers together — they share one binary | -| `uninstall --game rust [--server-id ] [--purge]` | Removes the service, the plugin file, its helpers and RunicNPC. **Keeps the plugin's config** and RunicNPC's `data/RunicNPC/` (an admin's placements and routes) — it is the website's, and it names the server. `--purge` also removes the sidecar config (the token) and the database. Removing the last server removes the shared binary too | +| `uninstall --game rust [--server-id ] [--purge]` | Removes the service, the plugin file, its helpers and RunicNPC. **Keeps the plugin's config** and RunicNPC's `data/RunicNPC/` (an admin's placements and routes) — it is the website's, and it names the server. `--purge` also removes the sidecar config (the token) and the database. Removing the last server removes the shared binary too. `PopupNotifications.json` is never touched: it belongs to PopupNotifications | With the egg: reinstall to update (above); the console is the diagnosis. diff --git a/rust-link/INTEGRATION.md b/rust-link/INTEGRATION.md index 9511c6f..589c3e3 100644 --- a/rust-link/INTEGRATION.md +++ b/rust-link/INTEGRATION.md @@ -137,6 +137,50 @@ Then press **Test**, which probes the sidecar and reports what came back: Within a poll interval the server appears at `/rust/servers`. +### 2.4 The recommended PopupNotifications look (optional) + +PopupNotifications is optional (D141). Where it is installed, news, events and zone messages can +arrive as popups, and its stock look is a small grey box at the right of the screen, sized for one +short line. The look we recommend is a **banner across the top of the screen**: no close button, +Roboto Condensed Bold at 18 (chosen in RunicNPC's 9d player session, D327). Paste this over +`oxide/config/PopupNotifications.json` (Carbon: `carbon/configs/PopupNotifications.json`), then +`oxide.reload PopupNotifications` (Carbon: `c.reload PopupNotifications`): + +```json +{ + "Notification duration (in seconds)": 8, + "Maximum notifications shown at any time": 6, + "UI Positioning": { + "Position of the left side of notification (0.0 - 1.0)": 0.15, + "Position of the bottom of noticiation (0.0 - 1.0)": 0.87, + "Width (0.0 - 1.0)": 0.7, + "Height (0.0 - 1.0)": 0.07, + "Space between notification (0.0 - 1.0)": 0.005 + }, + "UI Options": { + "Show close button": false, + "Panel color (hex)": "#2b2b2b", + "Panel transparency (0.0 - 1.0)": 0.8, + "Close button color (hex)": "#d85540", + "Close button transparency (0.0 - 1.0)": 0.5, + "Font": "robotocondensed-bold.ttf", + "Font size": 18 + }, + "Version": { + "Major": 0, + "Minor": 2, + "Patch": 1 + } +} +``` + +- **Keep the `Version` block.** PopupNotifications resets a config with no version (or one below + 0.2.0) to its defaults when it loads, which would undo the banner. +- The key `noticiation` is PopupNotifications' own spelling. Leave it as it is. +- **The installer and the Pterodactyl egg write this for you**, but only where the file is missing + or is still exactly the plugin's own defaults (D328). A config with any setting changed is kept + whole, so a look you chose is never replaced. See [`INSTALL.md`](INSTALL.md). + --- ## 3. When it does not work