-
Notifications
You must be signed in to change notification settings - Fork 0
Architecture
For contributors and anyone debugging the relay.
FMessage is a Maven multi-module project (fr.florianpal:FMessage:2.4.5).
FMessage/
├── FMessageCommon/ # shared: database, queries, configs, domain objects
├── FMessageBukkit/ # backend server plugin (Paper API)
├── FMessageBungee/ # proxy plugin (BungeeCord API)
└── FMessageVelocity/ # proxy plugin (Velocity API)
| Module | Depends on | Notes |
|---|---|---|
FMessageCommon |
HikariCP, MariaDB driver, BoostedYAML | No Minecraft API at all |
FMessageBukkit |
paper-api, PlaceholderAPI, ACF-Paper |
Does not depend on FMessageCommon — no database access |
FMessageBungee |
bungeecord-api, ACF-Bungee, bStats, FMessageCommon
|
|
FMessageVelocity |
velocity-api, ACF-Velocity, bStats, FMessageCommon
|
FMessageBungee and FMessageVelocity are near-identical ports of the same logic against two different proxy APIs: a change to commands or message handling usually has to be made in both.
-
managers/DatabaseManager— HikariCP pool, table bootstrap -
managers/ConfigurationManager— loadsconfig.ymlanddatabase.ymlvia BoostedYAML -
configurations/ChatConfig,configurations/DatabaseConfig -
queries/—GroupeQueries,GroupeMemberQueries,IgnoreQueries,NickNameQueries; each implementsIDatabaseTable, which exposes theCREATE TABLEdefinition used at startup -
managers/commandManagers/— the caching layer above the queries -
objects/Group,objects/Member
Chat travels over two Minecraft plugin-message channels:
| Channel | Direction | Payload |
|---|---|---|
fmessage:chatbungee |
backend → proxy | a chat message or a staff message written by a player |
fmessage:chatbukkit |
proxy → all backend servers | the message to display |
Both are registered by the proxy module and by the Bukkit module at startup.
| # | Field | Type |
|---|---|---|
| 1 | subchannel — Message or StaffMessage
|
UTF |
| 2 | author UUID | UTF |
| 3 | author display name | UTF |
| 4 | format, PlaceholderAPI already resolved | UTF |
| 5 | message body | UTF |
| 6 | author has fmessage.colors
|
boolean (Message only)
|
| 7 | author has fmessage.nick.colors
|
boolean (Message only)
|
| # | Field | Type |
|---|---|---|
| 1 | subchannel | UTF |
| 2 | author UUID | UTF |
| 3 | author display name | UTF |
| 4 | author nickname, empty when none | UTF |
| 5 | format | UTF |
| 6 | message body | UTF |
| 7 |
;-separated UUIDs of players ignoring the author |
UTF (Message only)
|
| 8 | colors flag | boolean (Message only)
|
| 9 | nick colors flag | boolean (Message only)
|
-
ChatListener(Bukkit) catchesAsyncPlayerChatEventatHIGHpriority. - Anti-flood, then anti-caps, are applied (unless bypassed).
- The event is always cancelled — vanilla broadcast never happens.
-
chatFormat.generalis resolved through PlaceholderAPI for the author, then everything is written tofmessage:chatbungee. - The proxy's
MessageListenerreads it and:- if the author has an active group toggle, the message is delivered to that group's online members and nothing else happens;
- if the subchannel is
StaffMessage, it is relayed to every server; - otherwise the author's nickname and the list of players ignoring them are attached, and the message is broadcast to every registered server over
fmessage:chatbukkit.
- Each backend server substitutes
{displayName}and{message}and sends the component to every online player — except those on the ignore list, who receiveignoreFormatinstead.
This is why the Bukkit module cannot run alone: step 3 cancels chat unconditionally, and only the proxy can put it back.
/msg, /m and /r are handled entirely on the proxy by MessageManager — no plugin message is involved. It checks the ignore list in both directions, renders targetChatFormat, senderChatFormat and spyChatFormat, records the /r pairing in memory, logs the spy line to the proxy console and pushes it to every player in spy mode.
Held in memory on the proxy, lost on restart:
-
playerMessage— the/rpairing, written both ways on each private message -
playerSpy— who has/chatspyenabled -
groups,ignores— caches refreshed after each mutating command
-
Formatting: Adventure's
LegacyComponentSerializer.legacyAmpersand(). Legacy&-codes only — no hex, no MiniMessage. -
Commands: ACF (
co.aikar.commands), relocated tofr.florianpal.fmessage.acfby the shade plugin.unstableAPI("help")is enabled. -
Config: BoostedYAML with
BasicVersioning("version")on the proxy, so user files are auto-upgraded. The Bukkit module uses plain Bukkit configuration. -
Metrics: bStats, plugin id
24047, on both proxy modules. -
Shading: MariaDB driver, ACF, ACF locales and bStats are relocated under
fr.florianpal.fmessage.*to avoid clashing with other plugins.
Configuration
Development
Help