MCME is one Minecraft network. Players connect to a Velocity proxy, which passes them on to several Paper game servers (the "backends"). Around the game sit a live map, the websites, a Maven repository for our own libraries and a Discord bot. This page shows how that code connects: which plugin depends on which, which messages travel between the proxy and the game servers, and what the web pieces read. It does not cover machines, hosts or which plugin runs on which server; that is in Network topology (operators only).
Every plugin named here has its own page under Developers. Start there for build steps, config and commands.
The game network
Several MCME plugins come in two halves: one runs on the Velocity proxy, the other on the Paper servers. The halves talk over plugin channels: named message channels that Minecraft carries over the player's connection between the proxy and a backend. A plugin message needs a player online on that backend to travel.
graph LR
subgraph proxy["Velocity proxy"]
CP["MCME-Connect"]
WV["MCME-Warps (Velocity half)"]
GV["thegaffer-velocity"]
IV["MCME-Introduction (proxy half)"]
TV["WebTeleport (Velocity)"]
AV["AccountLink (Velocity)"]
end
subgraph paper["Paper servers"]
CB["MCME-Connect"]
WP["MCME-Warps (Paper half)"]
GP["TheGaffer"]
IP["MCME-Introduction"]
TP["WebTeleport (Paper)"]
end
CB ---|"mcme:connect"| CP
WP ---|"mcme:warp, mcme:player-location, mcme:misc"| WV
GP ---|"mcme:gaffer"| GV
IP -->|"mcme:intro"| IV
TV -->|"mcme:webteleport"| TP
Not every Paper server runs every plugin. The table lists what each channel carries.
| Channel | Between | What it carries |
|---|---|---|
mcme:connect |
MCME-Connect on the proxy and on each backend | One channel for everything: the first string in a message names the operation (server switching, teleports, cross-server chat, titles, commands, restarts, statistics). Constants in com.mcmiddleearth.connect.Channel |
mcme:warp, mcme:player-location, mcme:misc |
The Velocity and Paper halves of MCME-Warps | Warp requests and where to place the player. The two halves must run the same version |
mcme:gaffer |
TheGaffer and thegaffer-velocity |
The network-wide job list: JOB_CREATED, JOB_DELETED, JOB_SYNC. Each backend restates its jobs when a player joins, because a backend cannot send a plugin message before a player is connected |
mcme:intro |
MCME-Introduction, backend to proxy | enter / exit plus the player's UUID, so the proxy knows who is inside an introduction room |
mcme:webteleport |
WebTeleport, Velocity to Paper | Where to teleport a player who clicked on the map; the Paper side lands them at surface height |
BungeeCord |
A backend plugin and the proxy's built-in forwarding (Velocity understands this BungeeCord-era channel) | Messages to other servers or players without a proxy plugin of your own. TheGaffer uses it for its older network-wide job announcement (with proxyAnnouncesJobs: true the proxy plugin announces jobs instead); CommonerVote uses it to send vote updates to other servers |
Who builds against whom
MCME plugins compile against each other's published jars, which come from the Maven repository. None of them bundles (shades) another MCME plugin or library: the dependency must be installed on the server as its own plugin.
graph TB
BASE["MCME-Base"]
PU["PluginUtils"]
CON["MCME-Connect"]
ARCH["MCME-Architect"]
INTRO["MCME-Introduction"]
PVP["MCME-PvP"]
GB["Guidebook"]
GAF["TheGaffer"]
CON --> BASE
CON --> PU
ARCH --> PU
ARCH --> CON
INTRO --> ARCH
INTRO --> CON
PVP --> PU
GB --> PU
ARCH -.->|"reflection, by method name"| GAF
Solid arrows are Maven or Gradle dependencies; the dotted arrow is a runtime call without a compile-time dependency.
| Plugin | Compiles against (MCME artifacts, release branch) | plugin.yml |
Also talks to |
|---|---|---|---|
| MCME-Architect | PluginUtils 2.0.4, MCME-Connect 3.0.1 | depend: [PluginUtils]; softdepends ProtocolLib, Essentials, dynmap, ViaVersion, MCME-Connect |
TheGaffer (job protection), Dynmap (map layers), ResourcePackScripts (/rp release) |
| MCME-Connect | MCME-Base 2.0.1, PluginUtils 2.0.2 | depend: [MCME-Base]; softdepends PremiumVanish, DiscordSRV, PluginUtils, Multiverse-Core |
LuckPerms, DiscordSRV (join and leave messages), a MySQL/MariaDB database (statistics) |
| MCME-Introduction | MCME-Architect 3.1.0, MCME-Connect 3.0.3 | depend: [MCME-Architect, ViaVersion, ProtocolLib] |
Architect's resource-pack API (RpManager); Connect's PlayerConnectEvent and ConnectUtil.sendMessage (used, but not declared in plugin.yml) |
| TheGaffer | none (its own thegaffer-core module) |
softdepends DiscordSRV, MCME-Connect, dynmap | DiscordSRV (job announcements) |
| MCME-PvP | PluginUtils 2.0.4 | depend: [WorldEdit, PlaceholderAPI, PluginUtils] |
|
| Guidebook | PluginUtils 2.0.4 | depend: [WorldEdit, PluginUtils]; softdepends Multiverse-Core, dynmap |
|
| MCME-Warps | none | softdepends dynmap | Dynmap (warp layer) |
| CommonerVote | none | depend: [LuckPerms] |
Other servers, through the proxy's BungeeCord channel; a MySQL/MariaDB database |
Perks and RankBoard are in private repositories; their pages describe them.
TheGaffer's build-protection API
MCME-Architect asks TheGaffer whether a player may build before its tools act, and tells TheGaffer about blocks it places so they count towards the player's job. There is no compile-time dependency: Architect looks up three static methods on com.mcmiddleearth.thegaffer.TheGaffer by name.
| Method | Returns |
|---|---|
hasBuildPermission(Player, Location) |
boolean: may the player build there |
getBuildProtectionMessage(Player, Location) |
String: the reason to show when the answer is no |
recordExternalBuild(Player, Location, boolean place) |
void: counts one placed (true) or broken block towards the player's job (TheGaffer 3.0.0 and later) |
Because the lookup is by name, renaming or changing one of these methods does not break the build: it silently switches off Architect's job checks. Treat the three signatures as a contract and change both repositories together. TheGaffer's ProtectionApiContractTest pins them.
Around the game
graph LR
DYN["Dynmap on the Paper servers"]
ARCH["MCME-Architect"]
WARP["MCME-Warps"]
MAP["LiveAtlas map"]
WT["WebTeleport (Velocity)"]
AV["AccountLink (Velocity) /mcmeverify"]
BOT["Discord bot (qBot)"]
PERKS["Perks"]
FORUM["Forum and its Pledge system"]
FRONT["Front page"]
RPS["ResourcePackScripts release automation"]
GH["GitHub releases"]
CI["GitHub Actions"]
MVN["Maven repository"]
ARCH -->|"marker layers"| DYN
WARP -->|"warp layer"| DYN
DYN -->|"tiles and JSON files"| MAP
MAP -->|"teleport requests"| WT
AV -->|"link codes"| BOT
PERKS -->|"linked accounts, Patreon data"| BOT
PERKS -->|"Pledge totals"| FORUM
FRONT -->|"reads"| FORUM
ARCH -->|"/rp release runs"| RPS
RPS -->|"pack zips"| GH
CI -->|"publishes jars"| MVN
- The live map. Each mapped Paper server runs Dynmap, which writes map tiles and small JSON files to disk. The LiveAtlas front end reads those files directly; Dynmap's own web server is not used. Architect and Warps draw their layers through Dynmap's marker API, so they appear on the map without any map-side code. A click on the map can teleport the player in game through the WebTeleport plugins.
- Account linking. A player asks the Discord bot for a code and types
/mcmeverify <code>in game. The AccountLink Velocity plugin sends the code to the bot, which stores the link between the Discord account and the Minecraft UUID. Perks later reads the links, and the bot's Patreon data, to grant perks. - The front page. The front page reads the forum (sign-in, the staff list, news), the Pledge system, Patreon, Discord events and a few public JSON files, always through scheduled jobs that cache the results. A page view never waits on an outside service.
- Resource packs. Staff run
/rp releasein game. MCME-Architect starts ResourcePackScripts' release automation, which builds the pack zips and publishes them as a GitHub release. See The release pipeline. - Shared artifacts. GitHub Actions in each library and plugin repository publish release jars to the Maven repository, and every MCME build resolves its MCME dependencies from there.
When you add a connection
- Decide the data contract first: channel name, message layout, or method signatures. Write it down in both repositories.
- Plan for the other side being absent. A plugin on another server may be missing, older, or restarting; a web service may be down. Prefer a soft dependency and a clear log line over a crash.
- Plugin channels use the
mcme:namespace. A single channel per plugin, with a leading string that names the operation, keeps registration simple; MCME-Connect and TheGaffer work that way. - Version both halves of a split plugin together and release them together.
- Add the connection to this page.