minecraft-server/docs/MIGRATION-PLAN-CHATCHAT.md
s8n 41ae6f90ef feat(chat): stage ChatChat migration bundle (jar + configs + swap scripts)
Replaces CarbonChat 3.0.0-beta.36 — viewer-context bug on <luckperms_prefix>.
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.
2026-05-07 22:23:11 +01:00

139 lines
9.2 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 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
`<luckperms_prefix>` 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-<ts>` (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): **4560 seconds**.
- Worst case (config syntax error forces second restart, or world I/O slow): **35 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 — <message>` (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 <test-name>` 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-<ts>` 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 (4590s).
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 `<itemlink>``<item>` — 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-<ts>` 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
```