This page gets you from a fresh machine to a green build of an MCME plugin. MCME's game servers run Paper 26.2 behind a Velocity 4 proxy, and every current plugin is built with JDK 25. Most plugins build with Maven, a few with Gradle; all of them resolve MCME's own libraries from the Maven repository. Read How it fits together first if you want the big picture.
What you need
| Tool | Version | Notes |
|---|---|---|
| JDK | 25 | Paper 26.2 and Velocity 4 need Java 25 to run, and paper-api 26.2 is Java 25 bytecode. Any Java 25 JDK works (CI uses Temurin 25) |
| Maven | 3.9 | For the Maven plugins: MCME-Architect, MCME-Connect, TheGaffer, MCME-Introduction, MCME-PvP, CommonerVote, MCME-Base. They have no Maven wrapper |
| Gradle | the repository's wrapper | For PluginUtils, Guidebook and MCME-Warps. Run ./gradlew; you do not need Gradle installed |
| Git | any recent version | Several jar names carry the commit id (MCME-Architect, MCME-Connect, MCME-PvP, CommonerVote), so build from a git checkout |
| An IDE | your choice | Let it import the project from pom.xml or the Gradle build. See the note on filtered plugin.yml under Testing |
Then clone a repository from github.com/MCME and run its build:
mvn -B -ntp clean verify # Maven plugins: build, run the tests, make the jar in target/
./gradlew build # Gradle projects: the same, jar in build/libs/
The first build downloads everything it needs from the public repositories in the build file. Nothing needs to be installed by hand.
Why JDK 25, and what goes wrong with an older one
paper-api 26.2 is compiled for Java 25 (class file version 69). A compiler older than Java 25 cannot read it, and the error does not say so: you get cannot access org.bukkit... on every import, which looks like a broken class path. Gradle builds that target older bytecode fail differently, with "only compatible with JVM runtime version 25 or newer". In both cases the fix is a Java 25 JDK (and in Gradle, a Java 25 toolchain).
JDK 25 can still produce older bytecode when a project needs it. Most MCME plugins compile with --release 25. MCME-Connect and MCME-PvP emit Java 21 bytecode (source/target 21) because their shade plugin cannot read Java 25 class files; the result runs fine on the Java 25 server. When you bump paper-api in a project, check that its GitHub Actions workflow also installs JDK 25.
The platform APIs
Paper
- Artifact:
io.papermc.paper:paper-api, fromhttps://repo.papermc.io/repository/maven-public/(or MCME's mirror, see below). - Pin an exact build, for example
26.2.build.126-stable, which most MCME plugins use. Do not use a version range: MCME-Connect's open range once started resolving to Minecraft 26.3 pre-release builds, and the CI of MCME-Connect and TheGaffer now rejects ranges outright. - Scope
provided(Maven) orcompileOnly(Gradle): the server supplies the API. - Do not pin Adventure. paper-api brings the Adventure version that matches the server (Adventure 5.2.0 on 26.2). An explicit Adventure dependency can pull in an older version and break the build.
- If you need server internals (NMS, the
net.minecraftclasses), use paperweight-userdev, as PluginUtils does. It downloads PaperMC's published dev bundle, so no local server jar is needed and the build works in CI.
Velocity
- Artifact:
com.velocitypowered:velocity-api4.x from the PaperMC repository. MCME-Connect pins4.2.0. - Inject with
com.google.inject.Inject, notjavax.inject.Inject. Velocity 4 moved to Guice 7, which no longer understandsjavax.inject; a main class that uses it fails to load with aGuice/MissingConstructorerror. - If your plugin's
velocity-plugin.jsonis generated from the@Pluginannotation, putvelocity-apion the compiler'sannotationProcessorPathstoo (TheGaffer'sthegaffer-velocity/pom.xmlshows how). Declaring it only as a dependency does not run the processor, and Velocity silently ignores a jar without a descriptor.
MCME libraries
Shared code lives in two libraries, PluginUtils (Paper helpers) and MCME-Base (an API that works on Paper and Velocity). Both are installed on the servers as plugins of their own, so you compile against them and declare them in plugin.yml; you never bundle them into your jar.
Add MCME's Maven repository to your build. The short version:
<repository>
<id>mcme-releases</id>
<url>https://repo.mcmiddleearth.com/releases</url>
</repository>
Maven repository has the full snippets for Maven and Gradle, the caching mirror, and what is published there.
Testing with MockBukkit
MCME's Paper plugins test with MockBukkit, a fake Paper server that runs inside JUnit.
| Test dependency | Version used in MCME plugins |
|---|---|
org.mockbukkit.mockbukkit:mockbukkit-v26.2 |
4.116.1 |
io.papermc.paper:paper-api |
a 26.2 build on the test class path (MockBukkit needs the same Minecraft version) |
org.junit.jupiter:junit-jupiter |
JUnit 6 (6.1.x) |
What trips people up:
- The plugin's main class must not be
final. MockBukkit subclasses it when it loads the plugin. - Read the Skipped count. When a test reaches something MockBukkit has not implemented, it throws
UnimplementedOperationException, and JUnit reports the test as skipped, not failed. A green build can hide tests that never ran. - Exceptions surface where Paper would only log them. A listener that throws during
callEvent, a task that throws duringperformTicks, or an error inonDisablecomes back out into your test. - MockBukkit is more forgiving than Paper in places. It lets a plugin schedule tasks from
onDisable, which Paper refuses, and it does not checkdepend:. Test those paths with care. - Brigadier commands register late. Commands registered through Paper's lifecycle API exist only after the first
server.dispatchCommand(...). - Paper Dialogs cannot be built in a test. Keep dialog building behind your own interface so the logic around it can be tested.
plugin.ymlmust be filtered. Many plugins writename: ${project.artifactId}in the source copy and let the build fill it in. Bukkit rejects the unfiltered name, so run tests through Maven or Gradle, or set your IDE up to use the build's resource filtering.- One plugin load per test class. MCME-Architect's suite loads the plugin once per class (
@BeforeAll) and runs each test class in a fresh JVM. Its Developer Manual has a longer list of MockBukkit lessons and test patterns worth copying.
Next steps
- Contributing: branches, pull requests and releases.
- The page of the plugin you want to work on, under Developers.