Skip to content

Developer Setup

Brennan Hatton edited this page Jun 7, 2026 · 1 revision

Developer Setup

How to build PlayerMob from source and how to depend on it from your own mod.

Prerequisites: JDK 21. The build pins the Java toolchain to 21 (Gradle will auto-provision a JDK 21 if you don't have one). The Fabric mixin transformer on 1.21.1 cannot read class files from JDK 23+, so 21 is mandatory.


Clone & build

git clone https://github.com/bh679/playermob-mc.git
cd playermob-mc
./gradlew build
Task What it does
./gradlew build Compile + package all three loader jars
./gradlew :common:test Run the JUnit tests in common/
./gradlew fabric:runClient Launch a dev Fabric client with the mod loaded
./gradlew neoforge:runClient Launch a dev NeoForge client
./gradlew forge:runClient Launch a dev Forge client — currently blocked, see below
./gradlew --stop Stop the Gradle daemon if a dev client hangs

Built jars land in:

fabric/build/libs/playermob-fabric-<version>.jar
forge/build/libs/playermob-forge-<version>.jar
neoforge/build/libs/playermob-neoforge-<version>.jar

Forge runClient caveat. forge:runClient is blocked by an upstream Architectury Loom 1.13 + Forge 1.21.1 JPMS conflict (architectury-loom#284). The Forge production jar still builds and is verified by dropping it into a real Forge 1.21.1 install. Develop against Fabric and/or NeoForge dev clients.


Project layout

PlayerMob is an Architectury multi-loader project. Almost all logic lives in common/; each loader module is a thin entrypoint.

common/      Shared code — entity, AI goals, personality, skins, menus.
             References only vanilla + the Fabric loader's mixin/annotation deps.
fabric/      Fabric entrypoint + client.
forge/       Forge entrypoint + client.
neoforge/    NeoForge entrypoint + client + the optional Dungeon Train integration.

Package root: games.brennan.playermob. See Architecture Overview for why the split looks the way it does (short version: the Architectury API runtime dropped Forge for 1.21, so registration is done per-loader with native APIs).


Depending on PlayerMob from your mod

PlayerMob does not publish to a public Maven repository today. There are three realistic ways to get its API on your compile classpath. In every case the dependency is compileOnly / modCompileOnly at compile time plus a declared mod dependency at runtime — you never bundle PlayerMob into your jar.

Option A — Modrinth Maven (recommended once released)

PlayerMob's releases publish per-loader to Modrinth. You can pull the published jar straight off the Modrinth Maven, exactly the way this repo consumes Dungeon Train (see Soft-Dependency Integrations):

repositories {
    maven {
        name = "Modrinth"
        url  = "https://api.modrinth.com/maven"
        content { includeGroup "maven.modrinth" }
    }
}

dependencies {
    // <slug> = PlayerMob's Modrinth project slug (from its Modrinth page);
    // <version> = the Modrinth version_number for your loader + MC 1.21.1.
    modCompileOnly("maven.modrinth:<slug>:<version>") { transitive = false }
}

Replace <slug> and <version> with the real values from PlayerMob's Modrinth page — they aren't hard-coded in this repo, so this wiki won't guess them.

Option B — Build locally, consume the file Maven

Each module publishes to a local file Maven at repo/ under the project root. From a clone:

./gradlew publish        # writes artifacts into ./repo

This produces games.brennan.playermob:playermob-common, :playermob-fabric, :playermob-forge, and :playermob-neoforge (all at the current mod_version). Point your build at that repo/ directory:

repositories {
    maven { url "file:///absolute/path/to/playermob-mc/repo" }
}
dependencies {
    compileOnly "games.brennan.playermob:playermob-common:0.15.0"
}

Option C — Drop the jar in as a local dependency

For a quick spike, drop a built playermob-<loader>-<version>.jar into your project and reference it with compileOnly files("libs/playermob-fabric-0.15.0.jar") (or your loader's modCompileOnly/compileOnly equivalent).


Declaring the runtime dependency

So your mod loads after PlayerMob (and optionally requires it), declare it in your loader metadata. PlayerMob's mod id is playermob.

Fabricfabric.mod.json:

"depends":    { "playermob": "*" }
// ...or, for an optional integration:
"suggests":   { "playermob": "*" }

NeoForgeneoforge.mods.toml:

[[dependencies.yourmodid]]
    modId = "playermob"
    type = "optional"          # or "required"
    versionRange = "[0.15.0,)"
    ordering = "AFTER"
    side = "BOTH"

Forgemods.toml:

[[dependencies.yourmodid]]
    modId = "playermob"
    mandatory = false          # true = hard requirement
    versionRange = "[0.15.0,)"
    ordering = "AFTER"
    side = "BOTH"

For an optional integration (your mod works with or without PlayerMob), keep the dependency compileOnly and gate your PlayerMob-touching code behind a loaded-check — see the Soft-Dependency Integrations recipe.


A note on contributing

PlayerMob ships through a strict review workflow (plan → test → merge gates). Any change touching entity registration, the goal selector, the renderer, the spawn egg, or the shared mixin config must be verified on Fabric and NeoForge dev clients (Forge gets a production-jar smoke test). See CLAUDE.md for the full contributor process.

Clone this wiki locally