minecraft-server/docs/CHAT-PLUGIN-CHATCHAT-RESEARCH.md
s8n d25b3d2e0d docs: chat plugin research (ChatChat + VentureChat)
Carbon viewer-context bug: <luckperms_prefix> resolves against viewer
not sender. Researched two open-source alternatives.

Both fix the bug. ChatChat (HelpChat fork) renders per-recipient with
PlaceholderAPI.setPlaceholders(sender, ...) + Kyorifier converts legacy
& codes to MM. VentureChat resolves PAPI once vs sender, splits packets
via ProtocolLib.

Concerns:
- ChatChat: 0 GitHub releases, last commit 2025-04-02, apiVersion 1.21.4,
  uses deprecated AsyncPlayerChatEvent
- VentureChat: built for 1.21.8, open issues #154/#156/#157 report
  1.21.10+ breakage with no maintainer response 4+ months

Both verdicts: cautious recommend. Operator decision pending player
input + migration plan synthesis.
2026-05-07 18:53:13 +01:00

7.9 KiB

ChatChat Migration Research — racked.ru

Date: 2026-05-07 Target: Replace CarbonChat 3.0.0-beta.36 (viewer-context bug on <luckperms_prefix>) Source: HelpChat/ChatChat — repo URL github.com/HelpChat/ChatChat (the HibiscusMC/ChatChat URL 404s; HelpChat is the actual maintainer org — DeluxeChat / extendedclip family).


1. Project Health — RISKY

Metric Value
Last commit (main) 2025-04-02 (PR #230 merge) — ~13 months ago
Substantive 1.21.4 work 2025-02-26 (one-shot bump)
Releases on GitHub 0 (CI artifacts only)
Jenkins last build #79, ~13 months ago, marked stable; CI appears idle
Open issues / PRs 23 open non-PR issues (some open since 2022)
License MIT
Stars / forks 51 / 31
README Literally says "Coming Soon ™️"
API target apiVersion: 1.21.4 in plugin.yml; Spigot 1.21.4-R0.1-SNAPSHOT
Adventure / MiniMessage 4.16.0 (somewhat behind 4.20.x current)

Verdict: low velocity, no formal release, niche maintainers (M0diis, BlitzOffline, bridgelol). Functional but not a "thriving" project — expect to fork or pin a build.

2. Architecture — Would It Fix Our Bug? YES (with caveats)

MessageProcessor.process() iterates every recipient and re-renders the format per-recipient via FormatUtils.parseFormat(format, sender, recipient, message, miniPlaceholders). Inside that call:

  • PlaceholderAPI.setPlaceholders(player=sender, formatString)%luckperms_prefix% always resolves against the sender. This is the exact opposite of Carbon's bug.
  • PlaceholderAPI.setRelationalPlaceholders(sender, recipient, ...)%rel_*% get both contexts.
  • A <papi:luckperms_prefix> MM tag also exists, also bound to sender via createPlaceholderAPITag(player=sender).
  • Kyorifier.kyorify() then converts any & legacy codes returned by PAPI (e.g. &8[&4⛧&8]) into MiniMessage tags before parsing. The legacy-code workaround Carbon swallows works natively here.
  • A separate internal MiniPlaceholderManager resolves config-defined MM tags with explicit MiniPlaceholderContext.builder().sender(user).recipient(target) — sender-context guaranteed.

Caveat: ChatChat's bundled MiniPlaceholderManager is its own config-yaml system. The external kyori MiniPlaceholders library + LP-Expansion (which provides the bare <luckperms_prefix> MM tag) is not auto-bridged. Two practical fixes:

  1. Use %luckperms_prefix% PAPI placeholder in the format (Kyorifier converts & codes) — works out of the box.
  2. Or use <papi:luckperms_prefix> MM tag — same effect.

Either way the viewer-context bug disappears.

3. Feature Gap vs Carbon (only what matters here)

Feature Carbon 3.0 ChatChat Notes
Channels (multi, per-channel format) yes yes channels.yml + per-channel formats list
/<channel> toggle / message-prefix yes yes toggle-command, message-prefix
Mute / ignore yes yes /ignore, /ignorelist, GP soft-mute hook
Mentions (with sound) yes yes personal + everyone, perm-gated, sound configurable
Profanity / rules yes basic (RuleManager + unicode rule) likely needs simple regex rules
DiscordSRV bridge yes yes (channels-bridging on, name-matched) uses DSRV ChatHook interface
PlaceholderAPI + Vault yes yes (PAPI hard-dep) Vault implicit via %vault_group% examples
MiniPlaceholders (external lib) yes (native) no auto-bridge — use PAPI tag instead minor format rewrite
<itemlink> / <item> in chat <itemlink> <item> (perm chatchat.tag.item) tag name differs
Hover/click on names/items yes yes full MM standard tags, perm-gated
Chat preview (1.19+) partial no (uses deprecated AsyncPlayerChatEvent) preview gone in 1.19.3+ anyway
Cross-server / Velocity yes no (single-server only) matches our setup
Async chat yes rides Bukkit async event open issue #91 wants deeper async
Database backend H2/MySQL/JSON JSON only (GsonDatabase) fine for our scale

4. Migration Cost — MEDIUM

What breaks / needs work:

  • Permission rename: carbon.channel.global.seechatchat.channel.see.global; same for use. Plus chatchat.tag.* per-tag perms (color/hover/click etc) need granting in LP (Carbon doesn't split these).
  • Format syntax rewrite: Carbon uses a single MM string per format. ChatChat splits formats into named parts: (channel/prefix/name/divider/message). Operator's target string maps cleanly onto one default format with one part.
  • DiscordSRV config: ChatChat-channel name MUST match DSRV-config channel name (not Discord channel name). Re-check DiscordSRV/config.yml > Channels: keys.
  • User data: Carbon JSON/H2/MySQL nicknames don't carry over; ChatChat starts a fresh users.json. EssentialsX nicknames are unaffected (different store).
  • Item-tag rename: any <itemlink> in messages must become <item>.
  • External MiniPlaceholders ignored — operator's <luckperms_prefix> MM tag becomes %luckperms_prefix% (PAPI form) inside the format.
  • partychat removal: trivial — just don't define it in channels.yml.

What's preserved automatically: LP prefixes-as-&-codes (Kyorifier handles them), EssentialsX nickname display via %player_displayname%, Discord routing intent, gray-message rendering (<gray><message>), MiniMOTD/TAB/ProAntiTab/ProtocolLib (no overlap).

5. Verdict — CAUTIOUS RECOMMEND

ChatChat will fix the viewer-context bug because its renderer is per-recipient with explicit sender-context PAPI resolution and a built-in legacy-code Kyorifier — but it's a sleepy single-Jenkins-build project pinned to 1.21.4 with no formal releases, so adopt only if you're comfortable building from source and pinning a known-good commit.


Top 3 unknowns to verify before pulling the trigger

  1. 1.21.11 runtime compatibility. plugin.yml says apiVersion: 1.21.4. Need to test the latest CI jar (or build commit 5b9... on a Purpur 1.21.11 staging instance) and confirm no NMS/AsyncPlayerChatEvent regressions on Purpur. Paper still fires the deprecated event so likely OK, but verify on an actual 1.21.11 Purpur jar before swapping in prod.
  2. DiscordSRV 1.25.1 vs whatever you currently run. ChatChat compiles against DSRV 1.25.1; the ChatHook interface or the shaded Adventure-Gson serializer dance can break across DSRV versions. Test in/out routing on a staging server.
  3. MiniPlaceholders LP-Expansion: do you actually need the MM tag form? If any other plugin (MOTD, TAB) consumes <luckperms_prefix> as a MiniMessage tag from racked formats, it must keep working. Confirm switching to %luckperms_prefix% (PAPI) doesn't break TAB/MiniMOTD elsewhere — those may be unaffected since they have their own resolvers.

Source artefacts inspected (for re-audit)

  • plugin/src/main/java/at/helpch/chatchat/util/MessageProcessor.java — per-recipient render loop
  • plugin/src/main/java/at/helpch/chatchat/util/FormatUtils.java — sender-context PAPI call
  • plugin/src/main/java/at/helpch/chatchat/util/PapiTagUtils.java<papi:...> MM tag bridge
  • plugin/src/main/java/at/helpch/chatchat/util/Kyorifier.java — legacy &/hex → MM converter
  • plugin/src/main/java/at/helpch/chatchat/listener/ChatListener.java — uses AsyncPlayerChatEvent
  • plugin/src/main/java/at/helpch/chatchat/util/ChannelUtils.javachatchat.channel.{see,use}.<name> perms
  • plugin/src/main/java/at/helpch/chatchat/hooks/dsrv/{ChatChatDsrvHook,DsrvListener}.java
  • plugin/src/main/resources/{channels,formats,extensions,settings}.yml
  • plugin/build.gradle.kts — apiVersion 1.21.4, soft-deps Towny/DSRV/SuperVanish/GP, loadBefore Essentials
  • gradle/libs.versions.toml — Spigot 1.21.4, MM 4.16.0, DSRV 1.25.1, EssX 2.19.4, PAPI 2.11.6

Last upstream commit reviewed: Merge pull request #230 from M0diis/resolve-228 @ 2025-04-02T17:18:51Z.