Skip to content

Developer PartyAddon Integration

Atlasroar edited this page Oct 3, 2026 · 1 revision

PartyAddon Integration — Methodology

This page documents why and how Globox1997/PartyAddon was merged directly into WandererZ in v2.2.0, following the same playbook as the JobsAddon, RpgDifficulty, and Tiered merges.

Why this merge was different

PartyAddon is a small player-party/group mod (invite, join, leave, kick, shared XP), but unlike Tiered — whose LevelZ coupling was a single optional compat mixin — PartyAddon shipped a hard modImplementation 'maven.modrinth:levelz:1.4.11+1.20.1' dependency. Its core feature (sharing XP among group members when any member picks up a LevelZ XP orb) directly imports net.levelz.access.PlayerSyncAccess, net.levelz.entity.LevelExperienceOrbEntity, and net.levelz.init.KeyInit, and one of those import points is a @Redirect mixin targeting a literal bytecode descriptor string, not just a plain Java import.

Both mods also:

  • target the same Minecraft version (1.20.1) and Yarn mappings
  • use a fully disjoint Java package (net.partyaddon.*), a clean sibling to net.wandererz.* / net.jobsaddon.* / net.rpgdifficulty.* (and distinct from Tiered's non-net-rooted draylar.tiered)
  • use a disjoint resource namespace (assets/partyaddon) — PartyAddon ships no data/ folder, so it has no datapack-configurable content and needed no new "Datapack Creation" wiki section

Step-by-step methodology

  1. Inventory the tree. Cloned PartyAddon's 1.20 branch — a small addon (29 Java files: group manager, party screen/HUD, networking, Clumps/Xaero's World Map compat). Grepped every levelz-related reference (~25 matches across 8 files) to scope the compat rewrite.
  2. Copy source wholesale, keeping net.partyaddon as the package name, and copy all resources (assets/partyaddon, partyaddon.mixins.json).
  3. Rewrite the LevelZ compat points:
    • PartyAddonMain.isJobsAddonLoaded + .isLevelZLoaded → collapsed into a single isWandererzLoaded field checking FabricLoader.isModLoaded("wandererz"). Both upstream checks collapse to the same post-merge condition, so keeping two separate always-identical booleans added nothing.
    • PartyAddonMixinPlugin — the FabricLoader.isModLoaded("levelz") guard controlling whether LevelExperienceOrbEntityMixin/LevelZServerPlayerEntityMixin apply was updated to check "wandererz". The unrelated isModLoaded("xaeroworldmap") guard for GuiMapMixin/ControlsHandlerMixin was left untouched (genuinely optional third-party compat).
    • LevelExperienceOrbEntityMixin (in mixin/compat/) — net.levelz.access.PlayerSyncAccess / net.levelz.entity.LevelExperienceOrbEntity imports repointed to net.wandererz.*, and the @Redirect's literal bytecode descriptor target string Lnet/levelz/access/PlayerSyncAccess;addLevelExperience(I)V repointed to Lnet/wandererz/access/PlayerSyncAccess;addLevelExperience(I)V (same descriptor-string-fix pattern as the Tiered SkillInfoScreenMixin merge).
    • PartyScreen — net.levelz.init.KeyInit import repointed to net.wandererz.init.KeyInit.
    • LevelZServerPlayerEntityMixin — class name and its addLeaderLevelZExperience/addLevelZExperience interface method names (on GroupLeaderAccess) were left unchanged: these are PartyAddon's own API surface describing what the method does (distribute LevelZ-style XP), not a reference to a LevelZ class that moved — same reasoning as JobsAddon's levelZXPMultiplicator field being left unrenamed.
    • PartyAddonConfig.distributeLevelZXP — this is a previously-shipped PartyAddon config field (unlike Tiered's brand-new levelzReforgeModifier), so the field name was left as-is to avoid breaking existing PartyAddon configs on upgrade, following the JobsAddon precedent rather than the Tiered one. Only the @Comment doc-string and the lang-file display label were rebranded to "WandererZ".
    • Dropped the now-unnecessary modImplementation 'maven.modrinth:levelz:1.4.11+1.20.1' Gradle dependency and the "levelz": "*" fabric.mod.json depends entry entirely — the code it used to compile against is now in the same source tree.
  4. Extract the Mod Menu screen. Same single-entrypoint-per-id constraint as the previous three merges. PartyAddon's standalone ModMenuIntegration implements ModMenuApi class was deleted, and WandererZ's combined chooser screen gained a fifth button that opens AutoConfig.getConfigScreen(PartyAddonConfig.class, this).
  5. Merge the manifest. fabric.mod.json: appended PartyAddon's main/client entrypoints, registered partyaddon.mixins.json alongside the existing four mixin configs. No new depends entry was needed (LibZ was already a shared dependency from the WandererZ base).
  6. Add Gradle dependencies. Clumps and Xaero's World Map carried over as optional modCompileOnly soft-compat, unrelated to the LevelZ merge, gated by their own isModLoaded checks exactly as upstream.
  7. Validate the fragile @Redirect target before building. LevelExperienceOrbEntityMixin's @Redirect(method = "lambda$onPlayerCollision$4", ...) targets a synthetic lambda method name, which only exists if net.wandererz.entity.LevelExperienceOrbEntity's compiled lambda numbering matches what PartyAddon was built against. Verified this with javap -p on the compiled LevelExperienceOrbEntity.class before trusting the hardcoded string — confirmed lambda$onPlayerCollision$4(PlayerEntity, Integer, Integer) exists with the expected 3-parameter signature, since WandererZ's orb class is structurally unchanged from the LevelZ version PartyAddon targeted.
  8. Validate. gradlew compileJava then gradlew build on JDK 17 — both succeeded, producing wandererz-2.2.0.jar. Verified the jar contains the expected net/partyaddon/** classes (29) and the merged fabric.mod.json.
  9. Version bump. mod_version went from 2.1.1 → 2.2.0 — a MINOR semver bump (additive functionality, no breaking changes).

Things deliberately not changed

  • distributeLevelZXP config field name, and GroupLeaderAccess.addLeaderLevelZExperience/addLevelZExperience method names — previously-shipped/own-API, not LevelZ class references.
  • LevelZServerPlayerEntityMixin's class name — internal implementation detail, not exposed to players, kept for minimal diff and to avoid touching the mixin-registration string matching in PartyAddonMixinPlugin and partyaddon.mixins.json.
  • Clumps/Xaero's World Map compat mixins (GuiMapMixin, ControlsHandlerMixin, the EventInit Clumps hook) — unrelated third-party integrations, left untouched.
  • Non-English PartyAddon lang file — copied as-is, aside from the mechanical "LevelZ" → "WandererZ" word swap in the one display-label string referencing the brand.

Risk register (resolved)

Risk Resolution
Mod Menu only showing one config per button slot Added a fifth button to the existing combined chooser Screen
net.levelz.* imports no longer resolving Repointed to the equivalent net.wandererz.* classes at their actual current paths
@Redirect targeting a synthetic lambda method name by string, silently breaking at runtime if the index didn't match Verified the exact lambda signature via javap -p against the actual compiled class before trusting the hardcoded index
isModLoaded("levelz") becoming a dead/always-false check Repointed to "wandererz", now a tautological always-true check (consistent with JobsAddon/RpgDifficulty/Tiered)
Previously-shipped config field getting renamed and silently resetting existing PartyAddon user configs Left distributeLevelZXP unrenamed, following the JobsAddon precedent
Hidden package/mixin-target/resource collisions Verified none exist via full file-tree review before copying — net.partyaddon is fully disjoint from all other merged packages

Clone this wiki locally