Overview
AstralJobs is the professions plugin for AstralRealms. Each player levels up in a set of per-job professions — Farmer, Fisherman, Hunter, Lumberjack, Miner — by performing tracked world actions (breaking a block, killing a mob, catching a fish, smelting an ingot, …). Every tracked action grants experience and money, XP feeds a per-job Crunch equation that drives leveling, and each level unlocks a claimable reward. Timed boosters and permission-/API-driven modifiers can multiply or add to what a player earns on top of the base rates.
Jobs
A job is one YAML file under jobs/ (Job, loaded by JobService#load, one file per job, indexed by its id). AstralJobs ships five:
Job | id | Display name | Actions used |
|---|---|---|---|
Farmer |
| Fermier |
|
Fisherman |
| Pêcheur |
|
Hunter |
| Chasseur |
|
Lumberjack |
| Bûcheron |
|
Miner |
| Mineur |
|
Each job carries its own menu (menu-title, menu-icon, menu-secondary-icon), its own three leveling equations, its own actions map (material/entity key → action type → reward), and its own rewards map (level → description + action list). Nothing is shared between jobs at the config level — in the shipped configs all five happen to use the same leveling-equation constants, but each job is free to define its own. See Jobs for the full per-job YAML shape.
How earning works
ActionService registers one JobActionType per tracked Bukkit event and groups them by event class so only one listener per event is installed. Ten action types are registered, each under a fixed key that also appears as a sub-key in a job's actions map:
Key | Event | Fires on |
|---|---|---|
|
| Breaking a block not placed by a player; ageable blocks (crops) must be fully grown. |
|
| Placing a block, keyed by the block placed. |
|
| Harvesting a block (e.g. berry bushes) without breaking it. |
|
| Shearing a block (e.g. a pumpkin). |
|
| A player strips a log/wood block (target block tagged |
|
| Right-clicking a full beehive/bee nest with a glass bottle in hand. |
|
| A player lands the killing blow on an entity. |
|
| Shearing an entity (e.g. a sheep). |
|
| Reeling in a caught fish/item. |
|
| Taking a cooked/smelted item out of a furnace, blast furnace, or smoker. |
When a tracked event fires, the handler resolves the block/item/entity's Key, looks up cached actions for that action type + key across all jobs (JobService#findActionsByType), and — if any match — hands the rest of the work to a 2-thread worker pool so equation math and the level-up loop never run on the main thread. For every block-based action type other than block-place itself, a block that was placed by a player is skipped (BlockService#wasPlacedByPlayer, checked on the main thread since placement state can only be queried there) — so, for example, breaking a player-placed stone block doesn't grant Miner XP.
Each matched JobActionValue in a job's actions map defines a base reward (money) and experience value for that material/entity, multiplied by the item-stack amount where the action carries one (e.g. cooked item count). These base values are run through the job's experience-progress-equation/experience-income-equation, then through any active modifiers, before being applied. A cancellable JobExperienceGainEvent and JobMoneyGainEvent fire for each gain, letting other plugins veto or rewrite the amount before it lands.
Leveling model
Each job defines three Crunch expressions, compiled once per job and evaluated per action:
Equation | Variables | Purpose |
|---|---|---|
|
| Total XP required to reach |
|
| Turns a raw action XP value into the XP actually granted — shipped as |
|
| Turns a raw action money value into the money actually granted — shipped as |
On every matched action, ActionService snapshots the player's (level, experience) for that job, computes the XP/money gain through the equations above, applies experience, then loops job.computeLevelProgress(level + 1) — leveling up (firing a cancellable JobLevelUpEvent each time) while accumulated experience covers the next threshold. The loop is bounded by max-level in config.yml (default 200); a max-level <= 0 is treated as uncapped. Once computeLevelProgress returns a non-positive value the equation's domain is exhausted and the player is treated as maxed, so an uncapped config can't spin forever. Players past the cap keep accumulating XP but stop gaining levels.
Rewards
Each job's rewards map keys a level to a JobReward: a description (component list, shown in the job menu) and an actions list run through the AstralCore action framework — typically a [message] line per item/currency plus a [console] line that actually grants it (eco give, give, permission grants, …). Rewards are not granted automatically on level-up; a player claims them via:
/jobs claim— claims every reward across every job whose level requirement is met and not yet claimed.The
claim-job-rewardAstralCore action, registered by the plugin on enable — takes a job and a level as parameters and claims that one job/level reward, for use from a menu button.
Claim state is tracked per player as JobsPlayerData (a Map<String jobId, Set<Integer> claimedLevels>), registered with AstralSync as the astraljobs:data snapshot adapter (JobsSnapshotAdapter) — it round-trips through Sync's binary snapshot format rather than a local table, so claimed rewards follow the player across servers. See Jobs for the reward YAML shape.
Boosters and modifiers
Two independent systems multiply or add to a player's XP/money gain, both consumed through ModifierService:
Boosters — timed, consumable items. A
BoosterBlueprint(one entry per file underboosters/,BoosterBlueprintService) defines atype(EXPERIENCE,MONEY, orBOTH), ascope(INDIVIDUALorGLOBAL), aduration, amultiplier, and theitemit's represented by. Activating one (BoosterService#use) converts the multiplier into aRELATIVEJobModifierapplied for the booster's duration;GLOBALboosters apply to every online player and queue back-to-back if one is already running. ABoosterWatchdogTaskpolls for activation/expiry, firingBoosterActivateEvent/BoosterExpiredEvent. See Boosters.Modifiers —
JobModifiers stacked per player inModifierService, each scoped toEXPERIENCE,MONEY, orGLOBALand typedRELATIVE(percentage, combined multiplicatively) orABSOLUTE(flat, summed). Two sources feed modifiers automatically:permissions-boostersinconfig.yml— maps a permission node to anEXPERIENCE/MONEYmultiplier, applied on join (BoosterService#applyPermissionModifiers) for every permission the player holds.Boosters themselves, as described above. Other plugins can add/remove/query modifiers directly through
JobsAPI.
Integrations
AstralHud (soft-dependency) —
AstralHudHookwraps the XP-gain action-bar component with AstralHud's background renderer when present.NotificationServicebatches XP/money gains per player+job (350 ms cooldown, 5 s stale timeout) and sends them as a rawWrapperPlayServerActionBarpacket via packetevents, independent of the standard Bukkit action-bar API.AstralAnalytics —
AnalyticsListenerlogs ajob_level_upevent on every level-up;NotificationService#flushlogs ajob_experience_notificationevent (accumulated XP/income/levels) each time a batched notification is persisted.Leaderboards —
LeaderboardListenerpushes a player's new level intoLeaderboardServiceon level-up, using theleaderboardsmap inconfig.yml(job id → leaderboard id; shipped with all five jobs mapped tojobs-<job>).Item stack supplier — booster items are exposed to AstralCore's item-stack-supplier registry under namespace
jobs(JobsBoostersItemSupplier, registered asCommonRegistries.itemStackSuppliers()), so other plugins/menus can reference a booster item asjobs:<blueprint-id>.
Dependencies
Dependency | Required | Notes |
|---|---|---|
AstralCore | Yes |
|
AstralSync | Yes | Persists |
packetevents | Yes | Sends the batched XP/money action-bar notification as a raw packet. |
AstralHud | Soft | When present, adds a background to the action-bar notification. |
Where to go next
Configuration —
config.yml:max-level,permissions-boosters,leaderboards, action displays.Commands —
/jobs,/jobs level,/jobs experience.Jobs — the per-job YAML shape: menu, leveling equations, actions, rewards.
Boosters — booster blueprint YAML and activation flow.
Placeholders — the
%job_*%,%jobs_*%,%booster_*%namespaces.Messages — the message-key catalogue.
Developer API —
JobsAPI, events, and theclaim-job-rewardaction.