Building a mod on Conduit
This page is the map. If you are about to write a minigame on Conduit and you do not yet know the codebase, read this before anything else, then follow the links into the module docs you actually need.
The one-paragraph model
Section titled “The one-paragraph model”Conduit is a Fabric engine that hands you the things every minigame re-solves: an isolated world per match, a lobby, phases, spectators, reconnects, in-world UI. Your mod does not build a server; it registers a game with the engine and implements the rules. The engine spins up a runtime dimension per match, drives the lobby and phase machine, and hands your code control at the points where your game is actually different from every other game.
Which module does what
Section titled “Which module does what”Start from the job, not the module list.
| You need to… | Go to |
|---|---|
| Register a game, run a lobby, drive phases, manage instances | conduit-arena |
| Player sessions, state, permissions, safe teleports, tick budget, config | conduit-core |
| In-world screens, labels, menus, block displays, custom UI | conduit-render |
| A fresh dimension per match, void worlds, runtime dimension specs | conduit-instance |
| Per-instance world rules, void generation | conduit-world |
| Build scenes and prefabs, zones, bindings | conduit-prefab |
| Countdowns, boss bars, effects | conduit-fx |
| Spectator flow | conduit-spectator |
| Custom entities, elite mobs, spawn waves, forces, flying props | conduit-mob |
| Pause, scrub back, resume a live match | conduit-rewind |
The shape of a real game
Section titled “The shape of a real game”Every shipped Conduit game converges on roughly the same layout. Taken from Block Shuffle:
me/zlex/<game>/ <Game>Mod.java entry point, registers with the engine <Game>Game.java the game definition the engine drives <Game>Config.java tunables <Game>Rules.java the actual rules RoundDriver.java per-round logic <Game>Command.java admin/debug commands lobby/ <Game>Lobby.java lobby state LobbyManager.java GamePhase.java the phase enum game/ LobbyController.java LobbyRoundDriver.java <Game>ConfigMenu.java in-world config UI ScreenIds.javaThe split that matters: lobby/ is pre-match, game/ is in-match. Keeping them apart is what stops a round driver from quietly reaching into lobby state that no longer means anything once the match has started.
Consuming the engine
Section titled “Consuming the engine”Games depend on the engine by version, and bundle it Jar-in-Jar so the person receiving the mod gets one file:
engine_version=0.22.0+mc26.1.2
// build.gradleimplementation "me.zlex:conduit:${project.engine_version}"
include "me.zlex:conduit-core:${project.engine_version}"include "me.zlex:conduit-instance:${project.engine_version}"// …and every other module, see the trap belowThe engine is published Mojang-mapped, so a plain implementation resolves correctly even though Loom 1.16.2 does not expose modImplementation.
The traps
Section titled “The traps”These are the ones that have actually cost time. They are listed here because none of them announce themselves.
The dependency graph, read from each module’s fabric.mod.json (Loader resolves these by mod id):
conduit-core (nothing)conduit-render coreconduit-world coreconduit-spectator coreconduit-instance core, worldconduit-fx core, renderconduit-prefab core, render, worldconduit-mob core, prefabconduit-rewind core, render, world, fxconduit-arena core, render, world, instance, fx, spectator, prefab, rewindconduit-arena pulls in eight of the others, which is why “I only use arena” is never true.
Bundle every engine module, not the ones you think you use. conduit-arena hard-depends on conduit-rewind as of engine 0.17.0, and Fabric Loader resolves that by mod id, not by class. A jar missing the nested conduit-rewind compiles perfectly, builds perfectly, and then refuses to load conduit-arena at runtime. Verify what actually shipped:
unzip -l build/libs/<game>-*.jar | grep META-INF/jarsDo not double-bundle in an umbrella build. When several games ship inside one bundle, the umbrella JiJs the engine exactly once for all of them. A game that also nests it produces duplicate engine jars and fails mod loading. This is why the include block sits behind a if (!project.hasProperty('padPartyBundle')) guard.
Gate your tick loop on rewind’s freeze. The engine cannot stop your round loop. See cooperative pause, one line, and without it a paused match keeps counting down.
Register a snapshot slice for your own state. Scores and phase do not live in the world, so they do not rewind unless you say how. See snapshot slices.
Measure spawn cost before trusting a wave. /conduit mob spawnprobe reports what a scatter spawn costs on the tick it runs.
Where the truth lives
Section titled “Where the truth lives”When this site and the code disagree, the code wins, and the site is the thing that needs fixing.
- Engine source and per-module
README.mdfiles: theconduitrepo undermodules/. - Release history and the authoritative version:
CHANGELOG.mdon the engine’smainbranch. - The version games are actually built against:
engine_versionin each game’sgradle.properties.
Versioning
Section titled “Versioning”SemVer with a +mcXX.X.X build-metadata suffix recording the Minecraft version the artifacts were compiled against. While the engine is 0.x, MINOR is the breaking-change signal (a 0.4 → 0.5 bump may require consumer changes) and PATCH carries features and fixes. That relaxes at 1.0.0.