feat(egg): write the recommended PopupNotifications banner (runicnpc 9e, D327, D328)
All checks were successful
PR Checks / rust-gates (pull_request) Successful in 4m43s

The install writes PopupNotifications.json into oxide/config or
carbon/configs as a banner across the top of the screen, by the installer's
rule: only where the file is missing or every setting is still the plugin's
own default (the Version aside). Anything else is kept whole. The plugin's
Version is written or kept, because PopupNotifications resets a config
without one. A failed write is said in the console and does not fail the
install: it is a look, not the bridge.

Tested in ghcr.io/ptero-eggs/installers:debian: missing, the rig's defaults,
defaults from a later release (version kept), four kinds of change, not
JSON, an existing banner, and an unmakeable directory. egg/build.sh builds.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01E14m6SuuY6i1vASFeGDBeY
This commit is contained in:
2026-10-09 01:24:27 -05:00
parent 13a6d33dfd
commit 4eee1fbe17

View File

@@ -88,6 +88,69 @@ if [ $WORLD_SEED == "0" ]; then
fi
## ── The Runic Gateway bridge ─────────────────────────────────────────────────
# PopupNotifications' recommended look (docs/runicnpc/PLAN.md D327): a banner
# across the top of the screen. PopupNotifications is optional and the operator
# installs it (D141), so its config is theirs: the banner goes in only where the
# file is missing or is still exactly the plugin's own defaults, every setting
# equal and the Version aside (D328). Anything else is kept whole. The Version
# is written too: PopupNotifications resets a config without one to defaults.
rg_popup() { # <the framework's config directory>
local file="$1/PopupNotifications.json"
local banner='{
"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 }
}'
# PopupNotifications 0.2.1's GetBaseConfig(): a small grey box at the right.
local defaults='{
"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.8,
"Position of the bottom of noticiation (0.0 - 1.0)": 0.78,
"Width (0.0 - 1.0)": 0.19,
"Height (0.0 - 1.0)": 0.1,
"Space between notification (0.0 - 1.0)": 0.01
},
"UI Options": {
"Show close button": true,
"Panel color (hex)": "#2b2b2b",
"Panel transparency (0.0 - 1.0)": 0.5,
"Close button color (hex)": "#d85540",
"Close button transparency (0.0 - 1.0)": 0.5,
"Font": "droidsansmono.ttf",
"Font size": 12
}
}'
if [ ! -e "${file}" ]; then
mkdir -p "$1" || return 1
jq -n "${banner}" > "${file}" || return 1
echo "Runic Gateway: wrote the recommended PopupNotifications banner to ${file}"
elif jq -e --argjson d "${defaults}" 'type == "object" and del(.Version) == $d' "${file}" >/dev/null 2>&1; then
jq --argjson b "${banner}" '$b + { Version: (.Version // $b.Version) }' "${file}" > "${file}.rg" \
&& mv "${file}.rg" "${file}" || { rm -f "${file}.rg"; return 1; }
echo "Runic Gateway: ${file} was PopupNotifications' defaults; wrote the recommended banner"
else
echo "Runic Gateway: kept ${file} (not PopupNotifications' defaults)"
fi
}
# After the wipe, so REMOVE_FILES can never delete the plugin this just placed.
rg_install() {
set -euo pipefail
@@ -95,10 +158,10 @@ rg_install() {
# just the variables an egg declares, and this one is not declared.
local api="${RUNICGATEWAY_BUNDLE_API:-https://gitea.whitlocktech.com/api/v1/repos/RunicGateway/installer/contents/v2/rust}"
local work=/tmp/runicgateway
local plugins
local plugins config
case "${FRAMEWORK:-vanilla}" in
oxide) plugins=/mnt/server/oxide/plugins ;;
carbon) plugins=/mnt/server/carbon/plugins ;;
oxide) plugins=/mnt/server/oxide/plugins; config=/mnt/server/oxide/config ;;
carbon) plugins=/mnt/server/carbon/plugins; config=/mnt/server/carbon/configs ;;
*)
# Not a failure (§34.4): failing would leave the operator without a game
# server over a bridge they may not want yet. The startup skips the
@@ -188,6 +251,10 @@ rg_install() {
for file in $(jq -r '.files | keys[]' "${manifest}"); do
install -m 644 "${work}/runicgateway-rust-plugin/${file}" "${plugins}/${file}"
done
# A look, not the bridge: if it cannot be written, say so and finish the install.
if ! rg_popup "${config}"; then
echo "Runic Gateway: could not write the PopupNotifications banner to ${config}; the bridge is installed regardless"
fi
# What is installed, readable from the panel's file manager.
jq --arg framework "${FRAMEWORK}" --arg installed "$(date -u +%Y-%m-%dT%H:%M:%SZ)" \
'{ bundle, protocol, framework: $framework, installed: $installed,