Configuration
ChatColor uses multiple YAML files to keep settings, colors, patterns, and messages organized and easy to customize.
config.yml
This file controls general settings and core behavior of the plugin.
settings:
apply-to-message: true # Whether to apply colors to chat messages
apply-to-name: false # Whether to apply colors to the player's display name
default-color: "NONE" # The default color for new players (Key from colors.yml/patterns.yml)
group-defaults: # Group-based default colors
admin: ""
vip: ""
event-priority: "DEFAULT" # The listener priority for the chat event (LOWEST to MONITOR)
chat-hook: "AUTO" # Which chat event to hook (AUTO / MODERN / LEGACY)
late-bind: false # Use if you use %chatcolor_message% in other plugin formats
papi-output: "AUTO" # Form our placeholders return (AUTO / LEGACY / MINIMESSAGE)
clean-console: true # Strips color codes from console output
show-standard-colors: true # Toggle standard colors in GUI
show-standard-gradients: true # Toggle standard gradients in GUI
show-standard-patterns: true # Toggle standard patterns in GUI
Settings Detailed
| Setting | Description |
|---|---|
apply-to-message |
Whether to apply the player's selected color to their chat messages. |
apply-to-name |
Whether to apply the color to the player's display name in chat. |
default-color |
A MiniMessage tag applied to players who haven't picked one, e.g. "<gray>". Set to NONE to disable. Overridden by group-defaults when the player matches one. |
group-defaults |
Map of permission-node-suffix: color-tag. Players with chatcolor.group.<suffix> get that color. Evaluated top to bottom — the first match wins, so list staff first. |
event-priority |
Set to DEFAULT to auto-detect, or set one manually (e.g. HIGHEST). See below — you should rarely need to change this. |
chat-hook |
Which chat event to hook. AUTO (recommended), MODERN, or LEGACY. Paper only — Spigot always uses LEGACY. See below. |
message-mode |
How the color is applied to the message. AUTO (recommended) picks for you, RENDERER colors it as chat is rendered, DIRECT colors the event message itself — use DIRECT when a formatter (e.g. EssentialsChat) ignores what the renderer hands it. Invalid values fall back to AUTO with a warning. |
late-bind |
Stops ChatColor coloring the message itself, so another plugin can place it via %chatcolor_message%. Enable this only when a chat plugin places the message from %chatcolor_message% in its own format (e.g. LPC set up that way) — with LPC's normal {message} format, leave it false. Otherwise chat comes out uncolored. |
papi-output |
What our PlaceholderAPI placeholders return. AUTO (recommended) uses MiniMessage for LPC 4.x or newer on Paper and § legacy codes otherwise. Force MINIMESSAGE if the console shows "Legacy formatting codes have been detected in a MiniMessage string" and chat stops sending, or LEGACY for formatters that use setFormat(). |
clean-console |
If true, the plugin strips color codes from its own console output to keep logs readable. /color debug output is escaped so this cannot hide it. |
show-standard-* |
Set to false to skip loading the bundled colors, gradients, or patterns entirely — they disappear from the GUI and from /color set and the API. Custom colors are unaffected. |
Chat Hook (chat-hook)
ChatColor registers exactly one chat listener. This matters more than it sounds.
Paper has two chat pipelines: the modern one built on AsyncChatEvent, and a legacy one built on
the deprecated AsyncPlayerChatEvent. A plugin that registers on both would color the same
message twice on the way through, so ChatColor picks one.
| Value | Behaviour |
|---|---|
AUTO |
Recommended. MODERN on Paper (and Folia), LEGACY on Spigot. |
MODERN |
Always use AsyncChatEvent. ChatColor colors from a chat renderer, which runs after every listener has finished, so nothing downstream can strip it. |
LEGACY |
Always use AsyncPlayerChatEvent. Only needed if a plugin cancels chat on the legacy event and you need ChatColor to run before it. See the warning below. |
The active choice is logged on startup:
[ChatColor] Chat hook: MODERN (AsyncChatEvent), priority HIGHEST
/color reload re-runs this resolution and rebinds the listener, so you do not need a full
restart after changing chat-hook or event-priority.
Why MODERN is safe even when other chat plugins are installed
On Paper, AsyncChatEvent fires for every chat message regardless of what else is installed:
- With no legacy listeners, Paper fires it directly.
- With legacy listeners present, Paper fires
AsyncPlayerChatEventfirst and then feeds the result straight intoAsyncChatEvent.
So the modern event is always last in the chain, which is exactly where a colorizer wants to
be. On top of that, MODERN does not write color codes into the message at all — it installs a
renderer that applies the color after every listener has had its say. A plugin that strips color
codes out of chat therefore has nothing of ours left to strip.
Earlier versions got this wrong.
AUTOused to join whichever event already had listeners on it. That sounds like "go where the chat plugins are", butAsyncPlayerChatEventis also where every plugin that merely captures chat input lives — WorldGuard, mcMMO, HeadDatabase, GUI prompts. On a populated server one of those is always present, soAUTOpickedLEGACYevery time and left ChatColor running before a modern formatter such as EssentialsChat, which then stripped the codes. If you previously worked around this by forcingchat-hook: MODERN, you can safely set it back toAUTO.
When you would still want LEGACY
Only if a plugin cancels or consumes chat on AsyncPlayerChatEvent and you need ChatColor to
act before that happens. Be aware of two costs:
- Colors written on the legacy event can still be stripped by anything reading the modern event
afterwards — EssentialsChat included. This is the exact failure
MODERNwas introduced to fix. - Paper renders legacy-pipeline messages through a shared legacy serializer. On most builds that
serializer has no hex support, so
#FF7F00arrives at the client as the nearest of the 16 named colors (gold). Solid colors are unaffected; only gradients and custom hex are approximated. ChatColor detects this at startup and warns you.
Listener Priority (event-priority)
DEFAULT auto-detects, and what it picks depends on which hook is active:
| Hook | DEFAULT resolves to |
|---|---|
MODERN |
Always HIGHEST. |
LEGACY |
HIGHEST when EssentialsChat, LPC, or DeluxeChat is running, otherwise NORMAL. |
The two rules differ because the hooks work differently. On MODERN, ChatColor installs a chat
renderer and the last plugin to install one wins, so being late is correct no matter what else
is installed — there is nothing to detect. On LEGACY, ChatColor writes color codes into the
message, so it genuinely matters whether a known formatter is going to run before it.
Earlier versions used the plugin-name check on both hooks. That only ever worked for the three plugins named above and missed everything else — including every chat plugin on Folia, since none of those three run there.
ChatColor never takes MONITOR automatically. That priority is reserved for plugins that only
observe an event, and leaving it free means you can still configure something to run after
ChatColor if you need to.
When to override. Only when another plugin is replacing ChatColor's renderer instead of
wrapping it. Run /color debug, say something, and look at ourRendererStillInstalled — if it
reports false, set event-priority: "MONITOR" to get the last word.
gui/gui.yml
This file allows for deep customization of the GUI, including all titles, items, layouts, and status messages.
See the GUI page for more details.
| Section | Description |
|---|---|
titles |
MiniMessage formatted titles for each menu page. |
layouts |
Define inventory size and slot positions for items. |
status |
Text shown in item lore based on player access/state. |
items |
Material, name, and lore for navigational/button items. |
colors/colors.yml
This file defines all your solid colors and gradients.
Solid Colors
Solid colors use a single MiniMessage tag or a hex code.
colors:
red:
display-name: "Red"
tag: ""
permission: "chatcolor.color.red"
icon: "RED_WOOL"
hot-pink:
display-name: "Hot Pink"
tag: "<#FF69B4>"
permission: "chatcolor.color.hot_pink"
icon: "PINK_WOOL"
Gradients
Gradients use the <gradient:color1:color2:...> tag.
gradients:
sunset:
display-name: "Sunset"
tag: ""
permission: "chatcolor.gradient.sunset"
icon: "ORANGE_WOOL"
Custom Colors
The custom-colors section holds anything created with /color create. It starts empty ({})
and you can also hand-edit it. Entries here appear in the GUI alongside the standard colors and
are listed by /color list custom.
custom-colors:
pastel-pink:
display-name: "Pastel Pink"
tag: "<#FCB6E1>"
permission: "chatcolor.custom.pastel-pink"
icon: "PINK_WOOL"
default: "false"
| Key | Required | Notes |
|---|---|---|
display-name |
No | Shown in the GUI. Falls back to the key. |
tag |
Yes | Any MiniMessage tag, including gradients. |
permission |
No | Omit or leave blank to make the color public. You may also write none/public/everyone/all. |
icon |
No | Any placeable Bukkit material. Defaults to WHITE_WOOL. |
default |
No | Bukkit permission default: true, op, or false. Defaults to false. |
The same permission / default rules apply to the colors, gradients, and patterns
sections.
What happens when a permission is revoked
Color selections are resolved fresh on every message rather than trusting the tag stored when the player picked it. So if you revoke someone's access to a color, or delete the entry from YAML, they immediately fall back to their group default (or to no color) without needing to re-pick.
colors/patterns.yml
Patterns cycle colors character-by-character throughout the message.
patterns:
rainbow:
display-name: "Rainbow"
permission: "chatcolor.pattern.rainbow"
icon: "YELLOW_WOOL"
colors:
- ""
- ""
- ""
- ""
- ""
- ""
- ""
lang/messages.yml
All plugin messages can be fully customized with MiniMessage formatting. Use <prefix> to include the defined prefix.
prefix: "[ChatColor] "
color-applied: "Color has been applied!"
color-created: "Custom color '' has been created!"
color-reset: "Your chat color has been reset."
no-permission: "You don't have permission to use that color."
config-reloaded: "Configuration reloaded successfully."
Automatic Configuration Updates
You should never have to delete a config file to update ChatColor. Dropping in a new jar and restarting is enough: new settings, new colours and rewritten documentation arrive on their own, and everything you typed stays where you put it.
What is kept, and what is refreshed
| Behaviour on update | |
|---|---|
| Values you changed | Kept. Always, even when the shipped default changes. |
| Entries you added yourself | Kept. Your own colours under colors:, your own GUI items, anything the bundled file has never heard of. |
custom-colors |
Kept. This is the section ChatColor writes on your behalf from /color. |
| Keys you deleted | Stay deleted — see below. |
| New settings and colours | Added, with their documentation. |
| New fields on entries you already have | Added (e.g. the per-entry default: field). |
| Reworded comments | Refreshed from the jar, so the guidance in your file matches the version you are running. |
| Settings ChatColor no longer uses | Removed if you never touched them, kept and reported if you did. |
| Key order and section layout | Reset to the bundled order, so your file reads like the documentation. |
A backup is written to plugins/ChatColor/backups/ before any change, and the ten most recent
per file are kept. The write itself goes through a temporary file, so an interrupted restart
cannot leave a half-written config behind.
Nothing is written at all when there is nothing to change — a restart that changes no settings leaves the file byte-for-byte identical and creates no backup.
Deleted keys, and the one exception
If you delete a standard colour you do not want, it should not silently reappear on the next
update. To manage that, ChatColor records what it has already shipped you in
plugins/ChatColor/data/.config-state.yml. A key missing from your file is then either
something new, which gets added, or something you removed, which does not.
The exception is the first update after this feature shipped. There is no record yet on that run, so ChatColor cannot tell the two apart and back-fills everything missing — including any standard colour you had deleted. Delete it once more and it will stay gone from then on.
To hide all the standard colours at once instead of deleting them one by one, use the toggles in
config.yml:
settings:
show-standard-colors: false
show-standard-gradients: false
show-standard-patterns: false
The ledger is bookkeeping, not configuration. Deleting it is safe: the install simply looks new again and back-fills once.
Reading the startup log
A migration that changed something prints a summary:
[ChatColor] === colors.yml Migration Summary ===
[ChatColor] Your existing settings were kept. Previous copy: backups/
[ChatColor] Added 3 new key(s):
[ChatColor] + colors.red.default
[ChatColor] + colors.pink.tag
[ChatColor] Removed 1 obsolete key(s):
[ChatColor] - settings.legacy-mode
[ChatColor] === Migration Complete ===
| Marker | Meaning |
|---|---|
+ |
New key delivered from the jar |
- |
Key ChatColor no longer uses, and which still held the value we shipped |
~ |
A renamed setting; your value was moved to the new name |
! |
Needs your attention — see below |
Lines marked ! are the only ones worth acting on:
- "…is no longer used, but you have customised it - left in place" — a setting that has been retired. Your value is untouched but nothing reads it any more; delete the line when convenient.
- "…changed from a value to a section - your old value is in the backup" — a setting that grew
into a group of settings. The new structure is in place; recover your old value from
backups/if you need it.
Forcing a clean regenerate
If you genuinely want the bundled file back:
- Stop the server.
- Delete the file (for example
colors/colors.yml) and its entry will be re-seeded automatically — no need to touchdata/.config-state.yml. - Start the server.
Copy anything you want to keep out first. If you only realise afterwards, your previous file is
still in backups/.
For a corrupted file
Invalid YAML — usually a stray tab, or an unclosed quote — cannot be merged. ChatColor moves the
file to backups/<name>.corrupted-<timestamp> and writes a fresh copy from the jar, so the
server still starts. Your broken file is intact in backups/ if you want to recover values from
it by hand.
Troubleshooting
If colors aren't appearing in chat, see Common Issues for a
step-by-step checklist, or run /color debug and check
Chat Compatibility for how to interpret the output.