Repository navigation
Developer Architecture
WandererZ v2.3.0+ ships as a single jar (wandererz-<version>+<mc>.jar) under Fabric mod id wandererz, but internally it is composed of six previously-separate codebases that still live in their own Java packages and resource namespaces. This page documents how the jar is actually laid out.
net.wandererz.* – the original WandererZ mod (player levels, skills, stats)
net.jobsaddon.* – the former JobsAddon mod (job system), merged in v1.5.0
net.rpgdifficulty.* – the former RpgDifficulty mod (mob difficulty scaling), merged in v1.6.0
draylar.tiered.* – the former Tiered mod (item rarity tiers + anvil reforging), merged in v2.1.0
net.partyaddon.* – the former PartyAddon mod (player parties/groups, shared XP), merged in v2.2.0
net.spoiledz.* – the former SpoiledZ mod (seasons-based food spoilage), merged in v2.3.0
These are fully disjoint namespaces — including several intentionally-duplicated class names (ConfigInit, EventInit, RenderInit, TagInit, CriteriaInit, CommandInit, JsonReaderInit, MobEntityMixin, LivingEntityMixin, ServerWorldMixin exist once under each relevant package). This is legal in Java and was a deliberate choice during every merge: each mod's own classes stay self-contained rather than being combined into one, which kept the merges mechanical/low-risk (see JobsAddon Integration, RpgDifficulty Integration, Tiered Integration, PartyAddon Integration, and SpoiledZ Integration). Note Tiered kept its upstream draylar package root (matching its own maven_group) rather than moving under net — the only merged addon not rooted there. Note also SpoiledZ is the only merged addon with zero LevelZ/WandererZ coupling — it's a general-purpose mod by the same author, not a LevelZ-specific addon.
| Package | Responsibility |
|---|---|
access |
Mixin-implemented interfaces exposed as a public API (PlayerSyncAccess, PlayerStatsManagerAccess, PlayerListAccess, PlayerBreakBlockAccess, PlayerDropAccess, MobEntityAccess) |
compat |
REI/EMI recipe plugin integration |
config |
WandererzConfig (Cloth Config @Config) + the combined ModMenuIntegration
|
criteria |
Custom advancement criteria/predicates for skill-based advancements |
data |
LevelLoader/LevelLists — data-driven XP tables |
entity / entity.render
|
LevelExperienceOrbEntity and its renderer |
init |
Registration classes (commands, compat hooks, config, criteria, entities, events, items, JSON readers, keybinds, rendering, tags) |
item |
Custom items (RareCandyItem, StrangePotionItem) |
mixin.* |
~90 mixins across block, compat, entity, item, misc, player — see below |
network |
PlayerStatsClientPacket / PlayerStatsServerPacket custom payloads |
screen |
Skill list/info screens + widgets |
stats |
PlayerStatsManager, Skill
|
util |
TooltipUtil |
waila |
Jade/WAILA plugin integration |
| Package | Responsibility |
|---|---|
access |
JobsManagerAccess (fetches the per-player JobsManager), PlayerAccess (block-break/recipe tracking helpers used to prevent XP farming exploits) |
config |
JobsAddonConfig (Cloth Config @Config) |
criteria |
JobUpCriterion / JobPredicate — advancement triggers for job level-ups |
data |
JobLoader / JobLists — data-driven per-job XP tables loaded from data/jobsaddon/<job>/*.json
|
init |
Registration classes (commands /jobmanager, config, criteria, events, JSON readers, rendering, tags) |
jobs |
JobsManager (core per-player job state machine) and JobHelper
|
mixin.block / mixin.compat / mixin.entity / mixin.misc / mixin.player
|
18 mixins, several gated behind optional-mod checks (see below) |
network |
JobsClientPacket / JobsServerPacket custom payloads |
screen |
JobScreen + JobTab inventory tab widget |
| Package | Responsibility |
|---|---|
access |
EntityAccess / ZombieEntityAccess (mob-level NBT state), ScreenAccess (lets RpgDifficultyConfigScreen add the clock widget to the generic Screen) |
api |
MobStrengthener — the core static methods that scale a mob's attributes, XP drop, and loot, invoked from mixins |
config |
RpgDifficultyConfig (Cloth Config @Config), RpgDifficultyConfigScreen (factored out of the original ModMenuApi entrypoint so WandererZ's combined chooser can reuse it), ConfigClock (in-game-time widget) |
data |
DifficultyLoader — data-driven per-dimension difficulty overrides |
mixin.* |
20 mixins across the root package, access, client, compat, and entity — see below |
| Package | Responsibility |
|---|---|
api |
ModifierUtils — core tier-roll/reforge-odds math, including the Smithing-skill/Luck weighted-reroll bonus |
block / item
|
Tiered-specific block/item classes |
config |
TieredConfig (Cloth Config @Config, including wandererzReforgeModifier) |
mixin.* |
~15 mixins across the root package, client, and compat — see below |
network |
TieredServerPacket custom payload |
screen |
Reforging tab screen + widgets |
util |
CommandInit (/tiered tier / /tiered untier), TieredItemTags, CustomEntityAttributes
|
| Package | Responsibility |
|---|---|
access |
GroupLeaderAccess / GroupManagerAccess (per-player group state), WorldMapAccess (Xaero's World Map compat hook) |
config |
PartyAddonConfig (Cloth Config @Config) |
group |
GroupManager — core group/invite/membership state machine |
init |
Registration classes (/party join//party leave commands, compat, config, events, rendering) |
mixin.compat |
LevelExperienceOrbEntityMixin (WandererZ XP-orb share hook), GuiMapMixin/ControlsHandlerMixin (Xaero's World Map), LevelZServerPlayerEntityMixin (name kept unchanged, see PartyAddon Integration) |
mixin.misc / mixin.player
|
ExperienceOrbEntityMixin, PlayerEntityMixin, PlayerManagerMixin, ServerPlayerEntityMixin, ServerWorldMixin
|
network |
PartyAddonClientPacket / PartyAddonServerPacket custom payloads |
screen |
PartyScreen, PartyTab inventory tab widget |
util |
NameHelper, GroupHelper
|
| Package | Responsibility |
|---|---|
access |
ServerWorldAccess (season-tracking state hook) |
compat |
AppleSkinPlugin (fully commented-out dead code, not wired to any entrypoint, kept for upstream fidelity) |
config |
SpoiledZConfig (Cloth Config @Config) |
init |
Registration classes (config, food-eaten/food-properties event hooks via Capsaicin, model providers, tags) |
mixin.* |
~20 mixins across the root package, access, client, and compat — see below |
util |
SpoiledUtil — core spoilage-time calculation and item-stack spoilage state helpers |
Four separate Mixin config JSONs ship in the jar and are all declared in fabric.mod.json's "mixins" array:
-
wandererz.mixins.json— packagenet.wandererz.mixin, pluginnet.wandererz.mixin.WandererzMixinPlugin -
jobsaddon.mixins.json— packagenet.jobsaddon.mixin, pluginnet.jobsaddon.mixin.compat.JobsAddonMixinPlugin -
rpgdifficulty.mixins.json— packagenet.rpgdifficulty.mixin, pluginnet.rpgdifficulty.mixin.compat.RpgDifficultyMixinPlugin -
tiered.mixins.json— packagedraylar.tiered.mixin, plugindraylar.tiered.mixin.compat.TieredMixinPlugin -
partyaddon.mixins.json— packagenet.partyaddon.mixin, pluginnet.partyaddon.mixin.PartyAddonMixinPlugin -
spoiledz.mixins.json— packagenet.spoiledz.mixin, pluginnet.spoiledz.mixin.SpoiledzMixinPlugin
All three plugins implement IMixinConfigPlugin.shouldApplyMixin(...) to conditionally skip mixins targeting classes from mods that may not be installed. For JobsAddon, four mixins are gated this way:
| Mixin | Only applied if loaded |
|---|---|
TomatoBushCropBlockMixin, CookingPotResultSlotMixin
|
farmersdelight |
BlockWrapperMixin |
fallingtree |
ScytheItemMixin, MacheteItemMixin
|
harvest_scythes |
For RpgDifficulty, two mixins are gated:
| Mixin | Only applied if loaded |
|---|---|
compat.ChangeAttributeMixin |
wandererz — always true post-merge, see RpgDifficulty Integration
|
compat.PlayerExCompatMixin |
playerex |
For Tiered, two mixins are gated:
| Mixin | Only applied if loaded |
|---|---|
mixin.compat.SkillInfoScreenMixin |
wandererz — always true post-merge, see Tiered Integration
|
mixin.compat.ModAnvilScreenMixin |
easyanvils |
For PartyAddon, three mixins are gated:
| Mixin | Only applied if loaded |
|---|---|
mixin.compat.LevelExperienceOrbEntityMixin, mixin.compat.LevelZServerPlayerEntityMixin
|
wandererz — always true post-merge, see PartyAddon Integration
|
mixin.compat.GuiMapMixin, mixin.compat.ControlsHandlerMixin
|
xaeroworldmap |
For SpoiledZ, several mixins are gated:
| Mixin | Only applied if loaded |
|---|---|
mixin.compat.CookingPotBlockEntityMixin, mixin.compat.CookingPotEntityMixin, mixin.compat.CuttingBoardBlockEntityMixin (farmersdelight variants) |
farmersdelight |
mixin.compat.ApplePressBlockEntityMixin, mixin.compat.JuicerBlockEntityMixin
|
vinery |
mixin.compat.SkilletBlockEntityMixin, mixin.compat.SkilletItemMixin, mixin.compat.StoveBlockEntityMixin, mixin.compat.WoodFiredOvenBlockEntityMixin
|
expandeddelight |
All other Jobs mixins (PlayerManagerMixin, PlayerEntityMixin, ServerPlayerEntityMixin, FishingBobberEntityMixin, the screen-handler mixins, BlockMixin, TntBlockMixin, SweetBerryBushBlockMixin, ComposterBlockMixin), all other RpgDifficulty mixins (the entity-attribute, zombie/creeper/guardian/wither/ender-dragon/projectile mixins, ServerWorldMixin, ChunkRegionMixin, ScreenMixin, ZombieEntityRendererMixin, SpawnHelperMixin), all other Tiered mixins (item/anvil/tooltip rendering mixins), all other PartyAddon mixins (ExperienceOrbEntityMixin, PlayerEntityMixin, PlayerManagerMixin, ServerPlayerEntityMixin, ServerWorldMixin), and all other SpoiledZ mixins (the block/item/inventory/furnace/campfire/cake/fishing-bobber/loot-table/item-scatterer mixins, ServerWorldMixin, the client-side ItemRendererMixin/ItemStackMixin) always apply.
Resource/data pack namespaces are independent of the Fabric mod id — they key off the assets/<namespace> / data/<namespace> folder name, not the owning mod. So assets/jobsaddon/*, data/jobsaddon/*, assets/rpgdifficulty/*, assets/partyaddon/*, and assets/spoiledz/*/data/spoiledz/* continue to work unmodified inside the wandererz-id jar; nothing needed renaming here. Lang files merge globally by key across every assets/*/lang/<locale>.json. RpgDifficulty's data/c/tags/entity_types/bosses.json uses Fabric's shared common-tag convention (c namespace, not rpgdifficulty) with "replace": false, so it merges additively with any other mod/datapack contributing to the same tag. PartyAddon ships no data/ folder at all — it has no datapack-configurable content. SpoiledZ is the first merged addon to ship vanilla resource overrides directly under assets/minecraft/* (a cake blockstate + 41 item-model predicate overrides adding "spoiled" variants) rather than only its own namespace — verified to have zero collision with any prior merge's resources before copying.
The jar also bundles resourcepacks/*_compat/ — eight small built-in resource packs (bakery, betterend, betternether, candlelight, earlystage, snuffles, supplementaries, vinery) that add job-restriction tag data for those mods' blocks/items when detected at runtime.
"entrypoints": {
"main": ["net.wandererz.WandererzMain", "net.jobsaddon.JobsAddonMain", "net.rpgdifficulty.RpgDifficultyMain", "draylar.tiered.Tiered", "net.partyaddon.PartyAddonMain", "net.spoiledz.SpoiledZMain"],
"client": ["net.wandererz.WandererzClient", "net.jobsaddon.JobsAddonClient", "net.rpgdifficulty.RpgDifficultyClient", "draylar.tiered.TieredClient", "net.partyaddon.PartyAddonClient", "net.spoiledz.SpoiledZClient"],
"modmenu": ["net.wandererz.config.ModMenuIntegration"]
}main/client are additive arrays — Fabric Loader calls onInitialize()/onInitializeClient() on every listed class, so JobsAddon's, RpgDifficulty's, Tiered's, PartyAddon's, and SpoiledZ's entrypoints were all simply appended without touching their initializer logic. modmenu is not additive (Mod Menu keys its config-screen map by mod id, one slot per id), which is why only one combined class is registered there, now with six buttons — see JobsAddon Integration, RpgDifficulty Integration, Tiered Integration, PartyAddon Integration, and SpoiledZ Integration for the reasoning.
Tiered introduced the jar's first example of both patterns side by side, and SpoiledZ added a third dependency category on top:
-
Bundled (shaded into the jar via Loom's
include(...)):reach-entity-attributes(from Tiered) andcapsaicin-1.20(from SpoiledZ) — end users don't need to install them themselves; they show up as auto-generatedMETA-INF/jars/*.jarentries in the built jar. -
Required, separately-installed:
AutoTag(Tiered) andFabric Seasons(SpoiledZ) — declared only independs("autotag-convention": ">=2.0.2+1.20","seasons": "*"), not bundled, matching each addon's own upstream non-includeGradle dependency declaration for that dependency.
Unlike resource namespaces, FabricLoader.getModContainer(id) is id-sensitive — it only resolves real registered Fabric mod ids. net.jobsaddon.init.EventInit uses this to register the 8 built-in compat resource packs via ResourceManagerHelper.registerBuiltinResourcePack(...). Because the merged mod's id is wandererz (not jobsaddon), all 8 call sites use FabricLoader.getInstance().getModContainer("wandererz") — using the old "jobsaddon" id here would throw NoSuchElementException via .orElseThrow(). RpgDifficulty's source has no equivalent getModContainer calls, so no analogous fix was needed there.
Player Guide
- Overview
- Getting Started
- Levels & Skills
- Jobs
- RPG Difficulty
- Tiered
- Party
- Food Spoilage
- Configuration
- Compatibility
- FAQ
Developer Guide