Skip to content

Developer Architecture

Atlasroar edited this page Oct 3, 2026 · 6 revisions

Architecture & Internal Jar Breakdown

WandererZ v2.2.0+ ships as a single jar (wandererz-<version>+<mc>.jar) under Fabric mod id wandererz, but internally it is composed of five previously-separate codebases that still live in their own Java packages and resource namespaces. This page documents how the jar is actually laid out.

Top-level package split

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

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, and PartyAddon 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.

net.wandererz subpackages

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

net.jobsaddon subpackages

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

net.rpgdifficulty subpackages

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

draylar.tiered subpackages

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

net.partyaddon subpackages

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

Mixin configuration files

Four separate Mixin config JSONs ship in the jar and are all declared in fabric.mod.json's "mixins" array:

  • wandererz.mixins.json — package net.wandererz.mixin, plugin net.wandererz.mixin.WandererzMixinPlugin
  • jobsaddon.mixins.json — package net.jobsaddon.mixin, plugin net.jobsaddon.mixin.compat.JobsAddonMixinPlugin
  • rpgdifficulty.mixins.json — package net.rpgdifficulty.mixin, plugin net.rpgdifficulty.mixin.compat.RpgDifficultyMixinPlugin
  • tiered.mixins.json — package draylar.tiered.mixin, plugin draylar.tiered.mixin.compat.TieredMixinPlugin
  • partyaddon.mixins.json — package net.partyaddon.mixin, plugin net.partyaddon.mixin.PartyAddonMixinPlugin

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

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), and all other PartyAddon mixins (ExperienceOrbEntityMixin, PlayerEntityMixin, PlayerManagerMixin, ServerPlayerEntityMixin, ServerWorldMixin) always apply.

Resource namespaces

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/*, and assets/partyaddon/* 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.

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 (fabric.mod.json)

"entrypoints": {
  "main":   ["net.wandererz.WandererzMain", "net.jobsaddon.JobsAddonMain", "net.rpgdifficulty.RpgDifficultyMain", "draylar.tiered.Tiered", "net.partyaddon.PartyAddonMain"],
  "client": ["net.wandererz.WandererzClient", "net.jobsaddon.JobsAddonClient", "net.rpgdifficulty.RpgDifficultyClient", "draylar.tiered.TieredClient", "net.partyaddon.PartyAddonClient"],
  "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, and PartyAddon'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 five buttons — see JobsAddon Integration, RpgDifficulty Integration, Tiered Integration, and PartyAddon Integration for the reasoning.

Bundled vs. required-separate dependencies

Tiered introduced the jar's first example of both patterns side by side:

  • Bundled (shaded into the jar via Loom's include(...)): reach-entity-attributes — end users don't need to install it themselves; it shows up as an auto-generated META-INF/jars/reach-entity-attributes-<version>.jar entry in the built jar's fabric.mod.json.
  • Required, separately-installed: AutoTag — declared only in depends ("autotag-convention": ">=2.0.2+1.20"), not bundled, matching upstream Tiered's own non-include Gradle dependency declaration for it.

One important runtime detail: FabricLoader.getModContainer(id)

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.

Clone this wiki locally