Skip to content

Developer API Usage

Atlasroar edited this page Oct 3, 2026 · 3 revisions

API Usage

This page shows the actual access-interface APIs used to bridge net.wandererz and net.jobsaddon (and that third-party addons can also hook into, since both are implemented on vanilla entity classes via Mixin and are safe to cast against on any PlayerEntity/ServerPlayerEntity).

Granting WandererZ XP / levels from other code

net.wandererz.access.PlayerSyncAccess is implemented on PlayerEntity and lets any code (including Jobs) grant WandererZ progression without depending on WandererZ's internal stats manager:

package net.wandererz.access;

public interface PlayerSyncAccess {
    void syncStats(boolean syncDelay);
    void addLevelExperience(int experience);
    void levelUp(int levels, boolean deductXp, boolean ignoreMaxLevel);
}

Usage from net.jobsaddon.jobs.JobsManager#addJobExperienceLevels — granting WandererZ XP proportional to a job level-up:

if (ConfigInit.CONFIG.levelZXPMultiplicator > 0)
    ((PlayerSyncAccess) playerEntity).addLevelExperience(ConfigInit.CONFIG.levelZXPMultiplicator * jobLevel);

Reading/writing a player's job state

net.jobsaddon.access.JobsManagerAccess is implemented on PlayerEntity and exposes the per-player JobsManager (job levels, job XP, employed jobs list — persisted via NBT):

public interface JobsManagerAccess {
    JobsManager getJobsManager();
}
JobsManager jobsManager = ((JobsManagerAccess) player).getJobsManager();

int minerLevel = jobsManager.getJobLevel("miner");
jobsManager.setJobLevel("miner", minerLevel + 1);

// Grant job XP (handles level-ups, sounds, advancement triggers, and network sync internally)
jobsManager.addJobXP(player, "miner", 25);

boolean isMiner = jobsManager.isEmployedJob("miner");
boolean canSwitch = jobsManager.canEmployJob("farmer");
jobsManager.employJob("farmer");

JobsManager recognizes the 8 job name strings: "brewer", "builder", "farmer", "fisher", "lumberjack", "miner", "smither", "warrior".

The job XP curve (used internally by getNextJobLevelExperience) is:

int experienceCost = (int) (ConfigInit.CONFIG.jobXPBaseCost
        + ConfigInit.CONFIG.jobXPCostMultiplicator * Math.pow(getJobLevel(string), ConfigInit.CONFIG.jobXPExponent));

Preventing XP-farming exploits

net.jobsaddon.access.PlayerAccess is implemented on PlayerEntity and tracks the last block broken / recipe crafted per player, so mixins can avoid granting repeat XP for spamming the same action (e.g. repeatedly breaking/placing the same block):

public interface PlayerAccess {
    boolean setLastBlockId(BlockPos blockPos, boolean building, int id);
    int getLastBlockId();
    Identifier getLastRecipeId();
    void setLastRecipeId(Identifier identifier);
    boolean isQuickCrafted();
    void setQuickCraftedRecipe(boolean quickCrafted);
}

Example from the TreeChop compat hook in net.jobsaddon.init.EventInit:

TreeChopEvents.AFTER_CHOP.register((world, player, pos, state, data, felled) -> {
    if (player != null && !world.isClient() && felled) {
        if (((JobsManagerAccess) player).getJobsManager().isEmployedJob("lumberjack")
                && ((PlayerAccess) player).setLastBlockId(pos, false, 0)) {
            int xpCount = JobLists.lumberjackBlockIdMap.getOrDefault(
                    Registries.BLOCK.getRawId(state.getBlock()), ConfigInit.CONFIG.lumberjackXP) * data.getNumChops();
            if (xpCount > 0) {
                JobsServerPacket.writeS2CJobXPPacket((ServerPlayerEntity) player, "lumberjack", xpCount);
            }
        }
    }
});

Networking

Both mods use Fabric's networking API with their own custom payload classes:

  • net.wandererz.network.PlayerStatsClientPacket / PlayerStatsServerPacket
  • net.jobsaddon.network.JobsClientPacket / JobsServerPacket

Example — pushing a job-sync packet to the client after a server-side change (used throughout JobsManager, PlayerManagerMixin, CommandInit):

JobsServerPacket.writeS2CJobPacket(jobsManager, serverPlayerEntity);

Both packet namespaces are self-contained (jobsaddon:* / wandererz:* channel identifiers) and do not collide.

Advancement criteria

Both mods register custom advancement criteria so datapacks/advancements can react to level-ups:

  • net.wandererz.criteria.WandererZCriterion / SkillCriterion (+ their *Predicates)
  • net.jobsaddon.criteria.JobUpCriterion / JobPredicate

JobUpCriterion is triggered from JobsManager#addJobXP on every job level-up:

CriteriaInit.JOB_UP.trigger((ServerPlayerEntity) playerEntity, string, getJobLevel(string));

Cross-referencing player data on respawn

net.jobsaddon.mixin.player.PlayerManagerMixin#respawnPlayerMixin shows the canonical pattern for migrating per-player job state across the respawn entity swap:

JobsManager jobsManager = ((JobsManagerAccess) player).getJobsManager();
JobsManager newJobsManager = ((JobsManagerAccess) serverPlayerEntity).getJobsManager();

newJobsManager.setJobLevel("miner", jobsManager.getJobLevel("miner"));
newJobsManager.setJobXP("miner", keepInventory ? jobsManager.getJobXP("miner") : 0);

RpgDifficulty reading WandererZ levels (PlayerStatsManagerAccess)

net.rpgdifficulty.mixin.compat.ChangeAttributeMixin is the reverse direction of the bridge: a mixin in the RpgDifficulty package reading state from WandererZ's own net.wandererz.access.PlayerStatsManagerAccess, scaling a mob's health/damage/protection factor by the average overall level of players within playerRadius:

double level = ((PlayerStatsManagerAccess) playerEntity).getPlayerStatsManager().getOverallLevel();

This mixin is runtime-gated by RpgDifficultyMixinPlugin on FabricLoader.isModLoaded("wandererz"), which is always true in the merged jar — see RpgDifficulty Integration for why that's intended.

See Architecture for the full package/mixin map these APIs live in.

Clone this wiki locally