Developer RpgDifficulty Integration - Atlasroar/WandererZ GitHub Wiki

RpgDifficulty Integration — Methodology

This page documents why and how Globox1997/RpgDifficulty was merged directly into WandererZ in v1.6.0, following the same playbook as the JobsAddon merge in v1.5.0.

Implementing PR: #3 — Integrate RpgDifficulty directly into WandererZ.

Why this was safe to do mechanically

Unlike JobsAddon, RpgDifficulty does not declare a hard dependency on WandererZ in its own fabric.mod.json — it is a standalone mob-difficulty-scaling mod. However, it already shipped an optional, conditionally-applied WandererZ compat mixin (ChangeAttributeMixin, gated by FabricLoader.isModLoaded("wandererz")), so the two mods were already designed to interoperate. Both mods also:

  • target the same Minecraft version (1.20.1) and Yarn mappings (1.20.1+build.10)
  • are authored by the same person (Globox_Z)
  • use fully disjoint Java packages (net.wandererz.* / net.jobsaddon.* vs net.rpgdifficulty.*)
  • use disjoint resource namespaces (assets/rpgdifficulty), aside from one shared Fabric common-tag contribution (see below)

Step-by-step methodology

  1. Inventory the tree. Cloned RpgDifficulty's 1.20 branch and reviewed every mixin, access interface, and config class. Confirmed no Java class-name or @Mixin(TargetClass.class) collisions with WandererZ/JobsAddon's existing mixins (several mixins do target the same vanilla classes, e.g. MobEntity, LivingEntity, ServerWorld — this is supported by Mixin as long as field/method names added by each mixin class don't collide, which was verified field-by-field).
  2. Copy source wholesale, keeping net.rpgdifficulty as the package name.
  3. Extract the Mod Menu screen into a reusable helper. Same single-entrypoint-per-id problem as JobsAddon. RpgDifficulty's ModMenuIntegration implements ModMenuApi was rewritten as a plain static helper (RpgDifficultyConfigScreen.create(Screen parent)) that still wires up the custom in-game-time clock widget (ConfigClock via ScreenAccess), and WandererZ's combined chooser screen gained a third button that calls it.
  4. Merge the manifest. fabric.mod.json: appended RpgDifficulty's main/client entrypoints, registered rpgdifficulty.mixins.json alongside the existing two mixin configs.
  5. Add the optional compat dependencies. RpgDifficulty compile-depends on NamePlate (to read a mob's displayed RPG level for bonus loot chance) and on PlayerEx's attribute API (data-attributes-directors-cut, playerex-directors-cut) for its alternate non-WandererZ scaling mixin. Both were added as modCompileOnly so the classes resolve at compile time; all three usages are runtime-gated behind FabricLoader.isModLoaded(...) checks, so nothing breaks if a player doesn't have them installed.
  6. Reconcile build configuration.
    • gradle.properties: took the newer of loader_version (0.15.5 → 0.15.11) and fabric_version (0.91.0 → 0.92.2). WandererZ's cloth_config_version was already newer than RpgDifficulty's, so it was left unchanged.
    • build.gradle/Gradle wrapper: RpgDifficulty's NamePlate/PlayerEx dependency jars were built with a newer Loom and ship a mixin-config remap format that Loom 1.3 cannot parse (This version of loom does not support the mixin remap type value). Fixed by bumping fabric-loom from 1.3-SNAPSHOT to 1.7-SNAPSHOT (resolved 1.7.4), which in turn required bumping the Gradle wrapper from 8.1.1 to 8.8 (Loom 1.7 needs Gradle's plugin API ≥ 8.8).
  7. Handle the shared data/c tag namespace. RpgDifficulty ships data/c/tags/entity_types/bosses.json (Fabric's shared common-tag convention, not its own rpgdifficulty namespace). WandererZ had no pre-existing file at that path, and the tag file already declares "replace": false, so it merges additively with any other mod/datapack contributing to the same tag — no manual JSON merge was needed.
  8. Validate. gradlew compileJava then gradlew build on JDK 17 — both succeeded, producing levelz-1.6.0.jar (the project's name at the time). Verified the jar contains the expected net/rpgdifficulty/** classes and assets/rpgdifficulty/** resources.
  9. Version bump. mod_version went from 1.5.0 → 1.6.0 — a MINOR semver bump (additive functionality, no breaking changes to existing worlds/configs/API).

Behavioral change worth calling out

ChangeAttributeMixin was previously only active for players who had both RpgDifficulty and WandererZ installed separately. Since the merged jar's mod id is wandererz, FabricLoader.isModLoaded("wandererz") is now always true, so this mixin is always active — RPG Difficulty's mob scaling now always factors in nearby players' WandererZ overall level (levelFactor/playerRadius config), on top of the default distance/time/height scaling, for every user of the merged mod. This is treated as intended behavior (consistent with how the JobsAddon merge activated previously-optional compat paths), not a regression, since levelFactor defaults to 0.0 (disabled) unless a server opts in. The separate PlayerExCompatMixin remains correctly gated behind FabricLoader.isModLoaded("playerex"), since PlayerEx is still a genuinely optional third-party mod.

Things deliberately not changed

  • RpgDifficulty's own difficulty-curve formulas, NBT keys (MobHealthMultiplier, SpawnerMob), and config field names/defaults were left 100% intact.
  • Non-English RpgDifficulty lang files (pt_br) were copied as-is; only one new Mod Menu chooser-screen string was added to WandererZ's en_us.json.

Risk register (resolved)

Risk Resolution
Mod Menu only showing one config per button slot Added a third button to the existing combined chooser Screen
Loom 1.3 failing to resolve NamePlate/PlayerEx mixin configs Bumped Loom to 1.7.4 and Gradle wrapper to 8.8
ChangeAttributeMixin becoming unconditionally active Documented as intended; gated at the config level via levelFactor = 0.0 default
Hidden package/mixin-target/resource collisions Verified none exist via full file-tree and @Mixin target review before copying
data/c/tags/entity_types/bosses.json namespace collision Confirmed "replace": false makes it additive; no existing WandererZ file at that path