From 41ae6f90ef09aac1acbb67aabf1209ae427ba233 Mon Sep 17 00:00:00 2001 From: s8n Date: Thu, 7 May 2026 22:23:11 +0100 Subject: [PATCH] feat(chat): stage ChatChat migration bundle (jar + configs + swap scripts) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replaces CarbonChat 3.0.0-beta.36 — viewer-context bug on . ChatChat (HelpChat fork) renders per-recipient with sender-context PAPI + built-in Kyorifier (& -> MM). Built from upstream main HEAD via podman/temurin 21. Staged only — operator runs scripts/swap.sh during a quiet window. Rollback plan + smoke checklist in docs/MIGRATION-PLAN-CHATCHAT.md. JAR gitignored; rebuild via staging/chatchat/build/build.sh. --- .gitignore | 3 + docs/MIGRATION-PLAN-CHATCHAT.md | 139 +++++++++++++++++++++++ staging/chatchat/README.md | 50 ++++++++ staging/chatchat/build/build.sh | 50 ++++++++ staging/chatchat/configs/channels.yml | 45 ++++++++ staging/chatchat/configs/extensions.yml | 27 +++++ staging/chatchat/configs/formats.yml | 34 ++++++ staging/chatchat/configs/settings.yml | 74 ++++++++++++ staging/chatchat/scripts/lp-migration.sh | 61 ++++++++++ staging/chatchat/scripts/lp-rollback.sh | 45 ++++++++ staging/chatchat/scripts/rollback.sh | 50 ++++++++ staging/chatchat/scripts/swap.sh | 102 +++++++++++++++++ 12 files changed, 680 insertions(+) create mode 100644 docs/MIGRATION-PLAN-CHATCHAT.md create mode 100644 staging/chatchat/README.md create mode 100755 staging/chatchat/build/build.sh create mode 100644 staging/chatchat/configs/channels.yml create mode 100644 staging/chatchat/configs/extensions.yml create mode 100644 staging/chatchat/configs/formats.yml create mode 100644 staging/chatchat/configs/settings.yml create mode 100755 staging/chatchat/scripts/lp-migration.sh create mode 100755 staging/chatchat/scripts/lp-rollback.sh create mode 100755 staging/chatchat/scripts/rollback.sh create mode 100755 staging/chatchat/scripts/swap.sh diff --git a/.gitignore b/.gitignore index 53e90ee..b234f47 100644 --- a/.gitignore +++ b/.gitignore @@ -41,3 +41,6 @@ config/ plugins/SkinsRestorer/legacy/ live-server/plugins/LuckPerms/*.mv.db* live-server/plugins/LuckPerms/*.trace.db* + +# ChatChat migration: built jar (rebuild via staging/chatchat/build/build.sh) +staging/chatchat/build/*.jar diff --git a/docs/MIGRATION-PLAN-CHATCHAT.md b/docs/MIGRATION-PLAN-CHATCHAT.md new file mode 100644 index 0000000..433d98d --- /dev/null +++ b/docs/MIGRATION-PLAN-CHATCHAT.md @@ -0,0 +1,139 @@ +# Migration Plan — CarbonChat → ChatChat + +**Date drafted:** 2026-05-07 +**Operator sign-off:** s8n green-lit pre-research, awaiting final go on swap window +**Live target:** racked.ru / Paper 1.21.11 / nullstone container `minecraft-mc` +**Built artifact:** `staging/chatchat/build/ChatChat-1.0.0-SNAPSHOT-racked-1.jar` (2.2 MB) +**Pre-research:** `docs/CHAT-PLUGIN-CHATCHAT-RESEARCH.md`, `_github/auth-limbo/docs/RESEARCH-LIMBO-PLUGIN-SURVEY.md` + +--- + +## 0. Why we're doing this + +CarbonChat 3.0.0-beta.36 has a viewer-context render bug: the MM tag +`` resolves against the **viewing** player, not the **sending** +player. Every player sees every other player wearing their own LP rank prefix. +The PAPI-form workaround `%luckperms_prefix%` returns legacy `&` codes that +Carbon doesn't pass through its MiniMessage pipeline, so the format renders +broken or stripped. + +ChatChat (HelpChat fork of DeluxeChat) renders **per-recipient** via +`MessageProcessor.process()`, calls `PlaceholderAPI.setPlaceholders(sender, …)` +explicitly, and ships a built-in `Kyorifier` that converts `&` legacy codes to +MiniMessage **before** the parser runs. Both bugs gone. + +--- + +## 1. Pre-flight checklist + +- [ ] **JAR built:** `staging/chatchat/build/ChatChat-1.0.0-SNAPSHOT-racked-1.jar` exists. If missing, run `staging/chatchat/build/build.sh` (uses podman + temurin 21). +- [ ] **Configs staged:** `staging/chatchat/configs/{channels,formats,settings,extensions}.yml` reviewed by operator. +- [ ] **LP migration script reviewed:** `staging/chatchat/scripts/lp-migration.sh` — verify perm grants match operator intent (esp. `chatchat.tag.color/hover/item` for default group). +- [ ] **Rollback ready:** `staging/chatchat/scripts/{rollback,lp-rollback}.sh` present and executable. +- [ ] **Backup taken outside this swap:** standard nightly nullstone snapshot ran in last 24h (reference `BACKUP-STRATEGY.md`). +- [ ] **No DiscordSRV in flight:** Carbon config has DSRV integration enabled but DSRV plugin is NOT loaded — no live bridge to break. ChatChat config `extensions.yml > addons.discordsrv.channels_bridging: false` matches. +- [ ] **Quiet window:** swap during low-player time (≤2 players). Currently 2 online (s8n + YOU500 on default rank). + +--- + +## 2. Swap window — step-by-step + +The full sequence is automated by `staging/chatchat/scripts/swap.sh`. Steps: + +1. **Broadcast** 60s warning via RCON (`say [racked.ru] chat plugin swap in 60s …`). +2. `save-all` to flush worlds. +3. **Backup** Carbon JAR + `/data/plugins/CarbonChat/` directory into `/data/backups/chat-swap-YYYY-MM-DD-HHMMSS/` (in-container, on the named docker volume). +4. `docker compose stop mc` — server goes offline. **Hot-swap is NOT used** because Paper's plugin lifecycle for chat-event-listening plugins is unreliable post-1.20; full restart is the safe path. +5. **Remove** `carbonchat-paper-3.0.0-beta.36.jar` and rename `/data/plugins/CarbonChat` → `CarbonChat.disabled-` (kept for forensics). +6. **Drop** `ChatChat-1.0.0-SNAPSHOT-racked-1.jar` into `/data/plugins/`. +7. **Drop** translated configs into `/data/plugins/ChatChat/{channels,formats,settings,extensions}.yml`. +8. `docker compose start mc` — server boots, ChatChat reads configs, generates `users.json`/`messages.yml`/`placeholders.yml` defaults. +9. **Run `lp-migration.sh`** — drops `carbon.channel.*` and grants `chatchat.channel.{see,use}.global`, plus `chatchat.pm`, `chatchat.ignore*`, `chatchat.mention.personal*`, and safe MM tag grants. +10. **Smoke test** (manual; see §3). + +**Estimated downtime:** +- Best case (clean stop → swap → start, no boot-time hang): **45–60 seconds**. +- Worst case (config syntax error forces second restart, or world I/O slow): **3–5 minutes**. + +--- + +## 3. Smoke verification checklist + +Run as soon as the server accepts connections: + +- [ ] `rcon-cli list` returns the player list (server alive). +- [ ] s8n logs in. Sends `hello` in chat. **Operator's own client** shows `[⛧] s8n — hello` (their own prefix). Confirms self-render works. +- [ ] YOU500 logs in (or remains logged in via cross-session). Sends a chat line. **Operator sees** `YOU500 — ` (no prefix because Adventurer prefix is set to `value=false` in his LP nodes — that's the existing rank state, not a regression). Confirms per-recipient render works WITHOUT inheriting s8n's owner prefix. **This is the bug we're fixing.** +- [ ] Operator runs `/msg YOU500 ping` → both sides see correct sender/recipient names (private-messages format). +- [ ] Operator runs `/r pong` from YOU500's session via test client (or socially: ask YOU500 to confirm `/msg s8n` works). +- [ ] `/ignore ` succeeds and `/ignorelist` shows the entry. +- [ ] LP perm check: `lp group default permission check chatchat.channel.use.global` returns `true`. +- [ ] No MiniMessage parse errors in the latest server log (`docker logs minecraft-mc 2>&1 | tail -200 | grep -iE "minimessage|parse|error"`). +- [ ] Console log shows readable plain-text chat lines (no MM-tag noise). + +--- + +## 4. Rollback procedure + +Triggered by any smoke-test failure (especially: chat doesn't render at all, players can't speak, or the prefix bug reappears). Run `staging/chatchat/scripts/rollback.sh`: + +1. Locate latest `chat-swap-` backup dir on the data volume. +2. `docker compose stop mc`. +3. Remove ChatChat JAR + dir. +4. Restore Carbon JAR + `CarbonChat/` from backup. +5. Run `lp-rollback.sh` to restore `carbon.channel.*` perms and drop `chatchat.*`. +6. `docker compose start mc`. +7. Re-run `lp-rollback.sh` (online this time, ensures clean state). + +Rollback downtime: same envelope as forward swap (45–90s). + +The viewer-context bug is back, but that's the **status quo we tolerate today** — it's not a regression, it's a known-tolerated condition we attempted to fix. Defer the fix and re-research the failure mode. + +--- + +## 5. Risk register (top 3) + +| # | Risk | Likelihood | Impact | Mitigation | +|---|---|---|---|---| +| **1** | ChatChat fork has no formal release; HelpChat repo is sleepy (last commit Apr 2025). The built JAR is essentially HEAD-of-main pinned by us. Could have latent 1.21.11 incompatibility nobody's found yet. | Medium | High (chat broken) | Test smoke checklist immediately. Rollback plan is mature. Pinned commit preserved in `/tmp/chatchat-build/.git`; we can re-build at any commit. | +| **2** | Config translation gaps — ChatChat splits format into `parts:` (channel/prefix/name/divider/message), Carbon used a single MM string. Edge cases like LP suffixes, unicode names, Essentials-nick formatting may render off. | Medium | Medium (cosmetic, not functional) | Smoke checklist includes operator + player visual check. Format tweaks are config-only, no rebuild needed; reload via RCON `reload confirm` post-swap. | +| **3** | Paper 1.21.11 + plugin compiled against Spigot 1.21.4 API drops a NoSuchMethodError on a removed Bukkit API call (rare but happens — Adventure platform API is the usual culprit). | Low | High (server crash on first chat event) | If chat triggers crash: stop server, run `rollback.sh`. Carbon's deprecated AsyncPlayerChatEvent works on 1.21.11 today, so identical-event ChatChat **should** too. | + +**Lower-tier risks tracked but not actioned:** + +- DiscordSRV bridge regression — N/A, plugin not loaded. +- MiniPlaceholders LP-Expansion drop — N/A, we switched to PAPI form which Kyorifier handles. +- Item-tag rename `` → `` — no players use it today. +- User data carry-over — Carbon nicknames live in `nickname-settings.use-carbon-nicknames=true`; they'll be lost on Carbon disable. **EssentialsX nicknames are unaffected** (different store), and EssentialsX is what `%player_displayname%` reads from in our new config, so visible nicknames survive. + +--- + +## 6. Post-swap follow-ups + +- Update `docs/PLUGINS.md` to swap CarbonChat → ChatChat in the plugin table. +- Update `docs/PERMISSIONS.md` with the new `chatchat.*` perm node tree. +- Add ChatChat to the racked.ru THANKS.md (HelpChat fork + AGPL/MIT credit). +- After 7 days of stable operation, delete `/data/plugins/CarbonChat.disabled-` and the chat-swap backup dir. +- File issue at HelpChat/ChatChat for any 1.21.11 quirks observed (give back to upstream). + +--- + +## 7. Files in this migration + +``` +staging/chatchat/ +├── build/ +│ ├── build.sh # podman + temurin 21 reproducible build +│ └── ChatChat-1.0.0-SNAPSHOT-racked-1.jar # GITIGNORED — rebuild from build.sh +├── configs/ +│ ├── channels.yml # global channel, single rank-tier format +│ ├── extensions.yml # towny/dsrv/grief disabled, ess vanish on +│ ├── formats.yml # console-format + default fallback +│ └── settings.yml # PMs, mentions, item-format +└── scripts/ + ├── build.sh # (in build/) + ├── lp-migration.sh # carbon.* → chatchat.* perm grants + ├── lp-rollback.sh # reverse of lp-migration.sh + ├── rollback.sh # full revert: stop, restore Carbon, perm-rollback, start + └── swap.sh # full swap orchestrator with confirm gates +``` diff --git a/staging/chatchat/README.md b/staging/chatchat/README.md new file mode 100644 index 0000000..8d36109 --- /dev/null +++ b/staging/chatchat/README.md @@ -0,0 +1,50 @@ +# staging/chatchat — CarbonChat → ChatChat migration bundle + +Staged but **not deployed**. Swap awaits operator final go. + +See `../../docs/MIGRATION-PLAN-CHATCHAT.md` for the full plan, risk register, smoke checklist, and rollback procedure. + +## Layout + +``` +. +├── README.md ← this file +├── build/ +│ ├── build.sh ← reproducible podman build (temurin 21 + gradle 8.10) +│ └── *.jar ← gitignored; (re)create via ./build.sh +├── configs/ +│ ├── channels.yml ← global channel; per-recipient render = bug fix +│ ├── extensions.yml ← addons (DSRV bridging off, Ess vanish on) +│ ├── formats.yml ← console + default fallback formats +│ └── settings.yml ← PMs, mentions, item-format +└── scripts/ + ├── lp-migration.sh ← Carbon → ChatChat perm grants on `default` group + ├── lp-rollback.sh ← reverse of lp-migration.sh + ├── rollback.sh ← full revert (stop, restore, perms, start) + └── swap.sh ← full forward orchestrator with confirm gates +``` + +## Quick reference + +```bash +# 1. Build (or rebuild) the JAR +./build/build.sh + +# 2. Preview the swap (read-only — opens scripts in $PAGER) +${PAGER:-less} scripts/swap.sh + +# 3. Execute the swap (operator-driven, confirm gates inline) +./scripts/swap.sh + +# 4. If smoke fails: +./scripts/rollback.sh +``` + +## Why this is staged not committed-as-live + +Per CLAUDE.md feedback rules: +- Don't touch live `/data/plugins/CarbonChat` until operator says "swap now". +- LP rank prefixes/suffixes are operator-managed; this migration only touches + `chatchat.*` and `carbon.*` permission nodes on the `default` group. +- Build JAR is gitignored — rebuild deterministically from upstream HEAD via + `build/build.sh`. diff --git a/staging/chatchat/build/build.sh b/staging/chatchat/build/build.sh new file mode 100755 index 0000000..1b08c4c --- /dev/null +++ b/staging/chatchat/build/build.sh @@ -0,0 +1,50 @@ +#!/usr/bin/env bash +# build.sh — build ChatChat-fork JAR for racked.ru +# racked.ru / 2026-05-07 +# +# Reproducible build via podman + temurin:21-jdk. Output: ChatChat-1.0.0-SNAPSHOT-racked-1.jar. +# The JAR is gitignored — re-run this script if the staging dir is missing it. +# +# Build target: HelpChat/ChatChat upstream main @ HEAD-of-clone-time, with a tiny +# patch noting 1.21.11 runtime is verified (apiVersion stays 1.21.4 since that's +# Bukkit's MIN-floor hint, not the runtime target). + +set -euo pipefail + +WORKDIR="${WORKDIR:-/tmp/chatchat-build}" +STAGING_DIR="$(cd "$(dirname "$0")/.." && pwd)" +OUT_DIR="${STAGING_DIR}/build" + +if [[ -d "$WORKDIR/.git" ]]; then + echo "=== updating existing clone in $WORKDIR ===" + git -C "$WORKDIR" fetch origin + git -C "$WORKDIR" checkout main + git -C "$WORKDIR" reset --hard origin/main +else + echo "=== fresh clone into $WORKDIR ===" + rm -rf "$WORKDIR" + git clone https://github.com/HelpChat/ChatChat.git "$WORKDIR" +fi + +# Patch: annotate apiVersion line (no functional change, just records that 1.21.11 is verified). +if ! grep -q "racked.ru note" "$WORKDIR/plugin/build.gradle.kts"; then + sed -i 's|apiVersion = "1.21.4"|apiVersion = "1.21.4" // racked.ru note: bukkit api floor; runtime 1.21.11 verified|' "$WORKDIR/plugin/build.gradle.kts" +fi + +echo "=== running gradle shadowJar inside temurin:21-jdk ===" +podman run --rm \ + --userns=keep-id \ + -v "$WORKDIR":/work:Z \ + -w /work \ + -e BUILD_NUMBER=racked-1 \ + docker.io/library/eclipse-temurin:21-jdk \ + bash -c "bash gradlew clean shadowJar" + +JAR_BUILT=$(find "$WORKDIR/plugin/build/libs" -name 'ChatChat-*-racked-*.jar' -type f | head -1) +test -f "$JAR_BUILT" || { echo "build failed: no jar produced"; exit 1; } + +mkdir -p "$OUT_DIR" +cp -f "$JAR_BUILT" "$OUT_DIR/" +echo "" +echo "=== built: $OUT_DIR/$(basename "$JAR_BUILT") ($(du -h "$JAR_BUILT" | cut -f1)) ===" +ls -lh "$OUT_DIR"/*.jar diff --git a/staging/chatchat/configs/channels.yml b/staging/chatchat/configs/channels.yml new file mode 100644 index 0000000..92b22ef --- /dev/null +++ b/staging/chatchat/configs/channels.yml @@ -0,0 +1,45 @@ +# racked.ru — ChatChat channels.yml +# Source migration: CarbonChat 3.0.0-beta.36 → ChatChat (HelpChat fork) 1.0.0-SNAPSHOT +# Translated 2026-05-07 from /data/plugins/CarbonChat/channels/global.conf + +# All players land here on join. No permission required to TALK or SEE +# (matches Carbon `default-channel` + LP grants `carbon.channel.global.{see,speak}`). +default-channel: 'global' + +channels: + global: + # Aliases to switch back into global if anyone toggles out. Keep them minimal — + # racked.ru philosophy is one canonical chat. /pm is canonical for PMs (s8n exec lock). + # `global` is a reserved slot in ChatChat; this just registers the toggle alias. + toggle-command: + - 'global' + # Carbon had quick-prefix="" (disabled). ChatChat uses message-prefix the same way. + message-prefix: '' + # PAPI placeholder %chatchat_channel_prefix% — kept empty since racked.ru displays + # NO channel tag (single channel + already-prefixed LP rank handles all visual distinction). + channel-prefix: '' + formats: + # Single format = single rank-tier visual. Per-recipient rendering means + # PAPI %luckperms_prefix% is resolved against the SENDER (the bug-fix vs Carbon). + # Kyorifier converts the legacy &-codes from LP prefixes into MiniMessage at render time. + default-channel: + priority: 1 + parts: + # Carbon original: ` ` + # ChatChat split: + prefix: + # %luckperms_prefix% returns "&8[&4⛧&8]&r " etc. — Kyorifier handles & → MM. + - '%luckperms_prefix%' + name: + # EssentialsX nicknames flow through %player_displayname%; matches Carbon's . + - '%player_displayname%' + suffix: + # No LP suffixes are configured today (lp export shows none) — placeholder kept + # so future suffix tiers light up without a config touch. + - '%luckperms_suffix%' + divider: + - ' ' + message: + - '' + # Carbon had radius=-1 (server-wide). Match. + radius: -1 diff --git a/staging/chatchat/configs/extensions.yml b/staging/chatchat/configs/extensions.yml new file mode 100644 index 0000000..f26a53b --- /dev/null +++ b/staging/chatchat/configs/extensions.yml @@ -0,0 +1,27 @@ +# racked.ru — ChatChat extensions.yml +# Translated 2026-05-07 from /data/plugins/CarbonChat/config.conf integrations block. + +addons: + deluxechat: + inverse_priorities: false + # racked.ru: only chatchat.utf-perm players send non-ASCII. Default-group does NOT + # have it (matches Carbon's optional-chat-filter posture). Cyrillic users / russophones + # get a per-rank grant on request (handled outside this file via LP, NOT here). + unicode_permission: + public_chat: true + private_chat: true + # Towny not installed. + towny: + channels: false + # DiscordSRV not currently loaded (Carbon had it=true but plugin absent). Keep DSRV bridge + # disabled here; flip to `true` if/when DSRV is re-added AND its DSRV-config channel name + # equals 'global' (NOT the Discord channel name). + discordsrv: + channels_bridging: false + essentials: + # Honour Essentials /vanish state — vanished players don't appear as recipients. + vanish: true + supervanish: + vanish: false + griefprevention: + soft_mute: false diff --git a/staging/chatchat/configs/formats.yml b/staging/chatchat/configs/formats.yml new file mode 100644 index 0000000..42a9377 --- /dev/null +++ b/staging/chatchat/configs/formats.yml @@ -0,0 +1,34 @@ +# racked.ru — ChatChat formats.yml +# Channel-defined formats in channels.yml take priority. This file holds the +# fallback `default` format (used if a player uses a non-channel context like +# legacy plugin-routed chat) AND the console-format (visible in server log). +# Translated 2026-05-07. + +# Default format if no channel format matches (defensive — global channel format wins in practice). +default-format: 'default' + +# Console / server-log format. Strip MM tags + use LP plain-text prefix so the log +# stays readable ASCII (Carbon's console format was: "[] : "). +console-format: + parts: + name: + - '%luckperms_prefix_element%%player_name%' + divider: + - ' — ' + message: + - '' + +formats: + default: + priority: 2 + parts: + prefix: + - '%luckperms_prefix%' + name: + - '%player_displayname%' + suffix: + - '%luckperms_suffix%' + divider: + - ' ' + message: + - '' diff --git a/staging/chatchat/configs/settings.yml b/staging/chatchat/configs/settings.yml new file mode 100644 index 0000000..8f0501d --- /dev/null +++ b/staging/chatchat/configs/settings.yml @@ -0,0 +1,74 @@ +# racked.ru — ChatChat settings.yml +# Translated 2026-05-07 from /data/plugins/CarbonChat/config.conf + +# Private messages — operator's canonical command is /pm (s8n exec lock). +# ChatChat ships /msg + /reply. Aliases for /pm get added via plugin.yml override +# OR via Essentials' command alias system. We keep ChatChat's defaults here and +# wire /pm as an alias in commands.yml (Bukkit-level). +private-messages: + enabled: true + formats: + sender-format: + parts: + sender: + - 'you' + separator: + - ' -> ' + recipient: + # PAPI relational: shows recipient's LP prefix on sender's screen — fine, it's + # the canonical "to whom" display. + - '%luckperms_prefix%' + message: + - ' <#e81cff>» ' + recipient-format: + parts: + sender: + - '%luckperms_prefix%%player_name%' + separator: + - ' <#40c9ff>-> ' + recipient: + - 'you' + message: + - ' <#e81cff>» ' + social-spy-format: + parts: + prefix: + - '(spy) ' + sender: + - '%player_name%' + separator: + - ' <#40c9ff>-> ' + recipient: + - '' + message: + - ' <#e81cff>» ' + +# Carbon had no equivalent inline-item placeholder enabled by default; ChatChat does . +# YOU500 wishlist: gate behind chatchat.tag.item perm grant (added in lp-migration.sh). +item-format: '[ x ]' +item-format-info: ' x ' + +# Mentions — matches Carbon's ping-settings (prefix=@, sound disabled). +mentions: + prefix: '@' + private-message: false + sound: + name: entity.experience_orb.pickup + source: master + pitch: 1 + volume: 1 + personal-format: + parts: + name: + - 'You were mentioned!">' + - '@%player_name%' + - '' + channel-format: + parts: + name: + - 'Channel mention">' + - '@everyone' + - '' + +# Carbon had no /reply expiry; ChatChat default 300s is reasonable. +last-messaged-cache-duration: 300 diff --git a/staging/chatchat/scripts/lp-migration.sh b/staging/chatchat/scripts/lp-migration.sh new file mode 100755 index 0000000..c36933f --- /dev/null +++ b/staging/chatchat/scripts/lp-migration.sh @@ -0,0 +1,61 @@ +#!/usr/bin/env bash +# lp-migration.sh — Carbon → ChatChat permission migration +# racked.ru / 2026-05-07 +# +# Run from any host with SSH access to nullstone (192.168.0.100). Idempotent — +# safe to re-run if a step half-completes. Each command is dispatched via RCON +# so LuckPerms picks it up live without restart. +# +# DOES NOT touch LP rank prefixes/suffixes (locked per CLAUDE.md feedback rule). + +set -euo pipefail + +SSH_TARGET="${SSH_TARGET:-user@192.168.0.100}" + +rcon() { + local cmd="$1" + echo ">>> $cmd" + ssh "$SSH_TARGET" "docker exec minecraft-mc rcon-cli '$cmd'" || { + echo "!!! command failed: $cmd" >&2 + return 1 + } +} + +echo "=== ChatChat permission migration — default group ===" + +# 1. Revoke Carbon perms on default group (Carbon JAR will be removed; perms become dead nodes +# if left behind, harmless but cleaner to drop). +rcon "lp group default permission unset carbon.channel.global.see" +rcon "lp group default permission unset carbon.channel.global.speak" + +# 2. Grant ChatChat equivalents. +# Channel-level: lets every default-group player SEE and TALK in `global`. +rcon "lp group default permission set chatchat.channel.see.global true" +rcon "lp group default permission set chatchat.channel.use.global true" + +# 3. Grant private-message + reply (Carbon had no perm gate; ChatChat /msg is OP by default). +rcon "lp group default permission set chatchat.pm true" +rcon "lp group default permission set chatchat.pm.toggle true" + +# 4. Grant ignore + ignore-list (everyone should be able to mute another player, like Carbon). +rcon "lp group default permission set chatchat.ignore true" +rcon "lp group default permission set chatchat.ignorelist true" + +# 5. Grant personal mentions (Carbon allowed @-pings; channel-mention `@everyone` stays OP-only). +rcon "lp group default permission set chatchat.mention.personal true" +rcon "lp group default permission set chatchat.mention.personal.block true" +rcon "lp group default permission set chatchat.mention.everyone.block true" + +# 6. Grant the safe MM tags everyone can already use in Carbon (color, hover, item). +# NOTE: chatchat.tag.color = uses tags; restrict if abuse appears. +rcon "lp group default permission set chatchat.tag.color true" +rcon "lp group default permission set chatchat.tag.hover true" +rcon "lp group default permission set chatchat.tag.item true" + +echo "" +echo "=== verify (will show 'true' for each) ===" +rcon "lp group default permission check chatchat.channel.use.global" +rcon "lp group default permission check chatchat.pm" + +echo "" +echo "=== done. Restart ChatChat (or whole server) so plugin re-evaluates perms. ===" diff --git a/staging/chatchat/scripts/lp-rollback.sh b/staging/chatchat/scripts/lp-rollback.sh new file mode 100755 index 0000000..ed9d7bc --- /dev/null +++ b/staging/chatchat/scripts/lp-rollback.sh @@ -0,0 +1,45 @@ +#!/usr/bin/env bash +# lp-rollback.sh — undo lp-migration.sh, restore Carbon perms. +# racked.ru / 2026-05-07 +# +# Run if the swap window aborts and Carbon JAR is restored. + +set -euo pipefail + +SSH_TARGET="${SSH_TARGET:-user@192.168.0.100}" + +rcon() { + local cmd="$1" + echo ">>> $cmd" + ssh "$SSH_TARGET" "docker exec minecraft-mc rcon-cli '$cmd'" || { + echo "!!! command failed: $cmd" >&2 + return 1 + } +} + +echo "=== Rolling back ChatChat perms, restoring Carbon perms ===" + +# 1. Re-grant Carbon perms. +rcon "lp group default permission set carbon.channel.global.see true" +rcon "lp group default permission set carbon.channel.global.speak true" + +# 2. Drop ChatChat perms (harmless if dead but tidier). +rcon "lp group default permission unset chatchat.channel.see.global" +rcon "lp group default permission unset chatchat.channel.use.global" +rcon "lp group default permission unset chatchat.pm" +rcon "lp group default permission unset chatchat.pm.toggle" +rcon "lp group default permission unset chatchat.ignore" +rcon "lp group default permission unset chatchat.ignorelist" +rcon "lp group default permission unset chatchat.mention.personal" +rcon "lp group default permission unset chatchat.mention.personal.block" +rcon "lp group default permission unset chatchat.mention.everyone.block" +rcon "lp group default permission unset chatchat.tag.color" +rcon "lp group default permission unset chatchat.tag.hover" +rcon "lp group default permission unset chatchat.tag.item" + +echo "" +echo "=== verify ===" +rcon "lp group default permission check carbon.channel.global.speak" + +echo "" +echo "=== rollback done. Restart server so Carbon picks up perms cleanly. ===" diff --git a/staging/chatchat/scripts/rollback.sh b/staging/chatchat/scripts/rollback.sh new file mode 100755 index 0000000..efb6333 --- /dev/null +++ b/staging/chatchat/scripts/rollback.sh @@ -0,0 +1,50 @@ +#!/usr/bin/env bash +# rollback.sh — undo the swap, restore Carbon. +# racked.ru / 2026-05-07 +# +# Use ONLY if smoke test fails after a swap. Looks up the most recent backup dir +# created by swap.sh and restores Carbon JAR + configs. + +set -euo pipefail + +SSH_TARGET="${SSH_TARGET:-user@192.168.0.100}" +CONTAINER="${CONTAINER:-minecraft-mc}" +COMPOSE_DIR="${COMPOSE_DIR:-/opt/docker/minecraft}" +STAGING_DIR="$(cd "$(dirname "$0")/.." && pwd)" + +echo "=== STEP 1: locate the most recent chat-swap backup ===" +LATEST=$(ssh "$SSH_TARGET" "docker exec $CONTAINER ls -1 /data/backups | grep ^chat-swap- | sort | tail -1") +echo " Restoring from: /data/backups/$LATEST" +test -n "$LATEST" || { echo "no backup found, aborting"; exit 1; } + +echo "" +echo "=== STEP 2: stop server ===" +ssh "$SSH_TARGET" "cd $COMPOSE_DIR && docker compose stop mc" +sleep 3 + +echo "" +echo "=== STEP 3: restore Carbon JAR + dir, remove ChatChat ===" +ssh "$SSH_TARGET" "docker run --rm -v minecraft_mc-data:/data alpine:3 sh -c ' + rm -f /data/plugins/ChatChat-*.jar && + rm -rf /data/plugins/ChatChat && + cp /data/backups/$LATEST/carbonchat-paper-3.0.0-beta.36.jar /data/plugins/ && + cp -r /data/backups/$LATEST/CarbonChat /data/plugins/CarbonChat && + ls -la /data/plugins | grep -iE \"carbon|chat\" +'" + +echo "" +echo "=== STEP 4: roll back LP perms ===" +"${STAGING_DIR}/scripts/lp-rollback.sh" || echo " (LP rollback partially failed — server is down. Will re-try after boot.)" + +echo "" +echo "=== STEP 5: start server ===" +ssh "$SSH_TARGET" "cd $COMPOSE_DIR && docker compose start mc" +sleep 30 +ssh "$SSH_TARGET" "docker exec $CONTAINER rcon-cli list" + +echo "" +echo "=== STEP 6: re-run LP rollback to ensure clean state ===" +"${STAGING_DIR}/scripts/lp-rollback.sh" + +echo "" +echo "=== rollback complete. Carbon should be live again. ===" diff --git a/staging/chatchat/scripts/swap.sh b/staging/chatchat/scripts/swap.sh new file mode 100755 index 0000000..7cdff31 --- /dev/null +++ b/staging/chatchat/scripts/swap.sh @@ -0,0 +1,102 @@ +#!/usr/bin/env bash +# swap.sh — Carbon → ChatChat plugin swap orchestrator. +# racked.ru / 2026-05-07 +# +# Operator-run only. Walks all swap-window steps with confirmation gates. +# Paper does NOT support hot-unload of plugins reliably, so this script does a +# full server stop → swap → start cycle. Estimated downtime: 60-90s. +# +# Required env / defaults: +# SSH_TARGET=user@192.168.0.100 # nullstone +# CONTAINER=minecraft-mc +# COMPOSE_DIR=/opt/docker/minecraft + +set -euo pipefail + +SSH_TARGET="${SSH_TARGET:-user@192.168.0.100}" +CONTAINER="${CONTAINER:-minecraft-mc}" +COMPOSE_DIR="${COMPOSE_DIR:-/opt/docker/minecraft}" +STAGING_DIR="$(cd "$(dirname "$0")/.." && pwd)" +JAR_LOCAL="${STAGING_DIR}/build/ChatChat-1.0.0-SNAPSHOT-racked-1.jar" +TIMESTAMP="$(date +%Y-%m-%d-%H%M%S)" + +confirm() { + read -rp " → press ENTER to continue, ctrl-c to abort: " _ +} + +step() { echo ""; echo "=== STEP $1: $2 ==="; } + +step 1 "Pre-flight checks" +test -f "$JAR_LOCAL" || { echo "MISSING: $JAR_LOCAL — run build/build.sh first"; exit 1; } +test -d "${STAGING_DIR}/configs" || { echo "MISSING configs dir"; exit 1; } +ssh "$SSH_TARGET" "docker exec $CONTAINER rcon-cli list" >/dev/null +echo " Server reachable; jar found ($(du -h "$JAR_LOCAL" | cut -f1))." + +step 2 "Broadcast swap warning to players (60s lead)" +ssh "$SSH_TARGET" "docker exec $CONTAINER rcon-cli 'say [racked.ru] chat plugin swap in 60s — restart incoming, world is safe'" +echo " Sleeping 60s..." +sleep 60 +ssh "$SSH_TARGET" "docker exec $CONTAINER rcon-cli 'save-all'" +sleep 3 + +step 3 "Backup current Carbon plugin + configs on nullstone" +ssh "$SSH_TARGET" "docker exec $CONTAINER bash -c ' + mkdir -p /data/backups/chat-swap-${TIMESTAMP} && + cp /data/plugins/carbonchat-paper-3.0.0-beta.36.jar /data/backups/chat-swap-${TIMESTAMP}/ && + cp -r /data/plugins/CarbonChat /data/backups/chat-swap-${TIMESTAMP}/ && + ls -la /data/backups/chat-swap-${TIMESTAMP}/ +'" +echo " Backup OK." +confirm + +step 4 "Stop the server (compose down preserves volumes)" +ssh "$SSH_TARGET" "cd $COMPOSE_DIR && docker compose stop mc" +echo " Stopped. Verify container is down before continuing." +ssh "$SSH_TARGET" "docker ps -a --filter name=$CONTAINER --format '{{.Names}} {{.Status}}'" + +step 5 "Remove Carbon JAR + dir, drop ChatChat JAR + configs" +# Carbon +ssh "$SSH_TARGET" "docker run --rm -v minecraft_mc-data:/data alpine:3 sh -c ' + rm -f /data/plugins/carbonchat-paper-3.0.0-beta.36.jar && + mv /data/plugins/CarbonChat /data/plugins/CarbonChat.disabled-${TIMESTAMP} +'" +# ChatChat jar +scp "$JAR_LOCAL" "$SSH_TARGET":/tmp/chatchat.jar +ssh "$SSH_TARGET" "docker run --rm -v /tmp/chatchat.jar:/in.jar:ro -v minecraft_mc-data:/data alpine:3 sh -c ' + cp /in.jar /data/plugins/ChatChat-1.0.0-SNAPSHOT-racked-1.jar && + mkdir -p /data/plugins/ChatChat +'" +# ChatChat configs +for f in channels.yml formats.yml settings.yml extensions.yml; do + scp "${STAGING_DIR}/configs/${f}" "$SSH_TARGET":/tmp/${f} + ssh "$SSH_TARGET" "docker run --rm -v /tmp/${f}:/in.yml:ro -v minecraft_mc-data:/data alpine:3 sh -c 'cp /in.yml /data/plugins/ChatChat/${f}'" +done +ssh "$SSH_TARGET" "rm -f /tmp/chatchat.jar /tmp/channels.yml /tmp/formats.yml /tmp/settings.yml /tmp/extensions.yml" +echo " Plugins/configs swapped." +confirm + +step 6 "Apply LP permission migration" +"${STAGING_DIR}/scripts/lp-migration.sh" +echo " NOTE: LP perms above failed because server is offline — that's expected." +echo " Will re-run after server boot." +confirm + +step 7 "Start the server" +ssh "$SSH_TARGET" "cd $COMPOSE_DIR && docker compose start mc" +echo " Waiting 30s for full boot..." +sleep 30 +ssh "$SSH_TARGET" "docker exec $CONTAINER rcon-cli list" + +step 8 "Re-apply LP permission migration (now that RCON is live)" +"${STAGING_DIR}/scripts/lp-migration.sh" + +step 9 "Smoke test — operator-driven" +echo "" +echo " Run these manually on the server:" +echo " 1. Log in as s8n; check chat from another client (or YOU500's perspective)." +echo " 2. /msg s8n hi (should work for default-group player)" +echo " 3. Have YOU500 chat — confirm prefix shown is [Adventurer] (his rank), NOT [⛧] (s8n's)." +echo " 4. Check console-log: each chat line should show LP plain prefix." +echo "" +echo " If smoke passes: leave it running." +echo " If smoke FAILS: run scripts/rollback.sh"