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.
9.2 KiB
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.jarexists. If missing, runstaging/chatchat/build/build.sh(uses podman + temurin 21). - Configs staged:
staging/chatchat/configs/{channels,formats,settings,extensions}.ymlreviewed by operator. - LP migration script reviewed:
staging/chatchat/scripts/lp-migration.sh— verify perm grants match operator intent (esp.chatchat.tag.color/hover/itemfor default group). - Rollback ready:
staging/chatchat/scripts/{rollback,lp-rollback}.shpresent 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: falsematches. - 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:
- Broadcast 60s warning via RCON (
say [racked.ru] chat plugin swap in 60s …). save-allto flush worlds.- Backup Carbon JAR +
/data/plugins/CarbonChat/directory into/data/backups/chat-swap-YYYY-MM-DD-HHMMSS/(in-container, on the named docker volume). 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.- Remove
carbonchat-paper-3.0.0-beta.36.jarand rename/data/plugins/CarbonChat→CarbonChat.disabled-<ts>(kept for forensics). - Drop
ChatChat-1.0.0-SNAPSHOT-racked-1.jarinto/data/plugins/. - Drop translated configs into
/data/plugins/ChatChat/{channels,formats,settings,extensions}.yml. docker compose start mc— server boots, ChatChat reads configs, generatesusers.json/messages.yml/placeholders.ymldefaults.- Run
lp-migration.sh— dropscarbon.channel.*and grantschatchat.channel.{see,use}.global, pluschatchat.pm,chatchat.ignore*,chatchat.mention.personal*, and safe MM tag grants. - 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 listreturns the player list (server alive).- s8n logs in. Sends
helloin 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 tovalue=falsein 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 pongfrom YOU500's session via test client (or socially: ask YOU500 to confirm/msg s8nworks). /ignore <test-name>succeeds and/ignorelistshows the entry.- LP perm check:
lp group default permission check chatchat.channel.use.globalreturnstrue. - 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:
- Locate latest
chat-swap-<ts>backup dir on the data volume. docker compose stop mc.- Remove ChatChat JAR + dir.
- Restore Carbon JAR +
CarbonChat/from backup. - Run
lp-rollback.shto restorecarbon.channel.*perms and dropchatchat.*. docker compose start mc.- 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
<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.mdto swap CarbonChat → ChatChat in the plugin table. - Update
docs/PERMISSIONS.mdwith the newchatchat.*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