Achievements
Points, meta-chains, server-first claims, and lifetime statistics
Overview
Achievements reward players for long-running play patterns - first kill of a monster type, a thousand mined blocks, reaching level 100, or claiming a server-first bragging right. The system tracks progress automatically from combat, gathering, crafting, questing, bounties, NPC dialogue, and level events, awards points on completion, and can trigger skill XP, boosts, currency, or console commands as rewards.
Players open the Achievements page with /mmoachievements (aliases: /mmoach, /achievements). The jar ships its achievements as 167 readable files across combat, gathering, crafting, leveling, quests, and misc categories (each split into subcategories), plus 5 point milestones. 6 of those files are per-skill ladders that stamp themselves out once per skill your server actually runs, so the earned total on a default install is well above the file count, and a server with extra skills gets their ladders for free.
Achievements can also ship in a content pack, and can be feature-gated so they only appear when a system like mastery is enabled.
Achievements land the moment you earn them. Progress is worked out inline as the event happens, so the unlock, its points and its rewards arrive on the same swing that finished it. There is no background pass to wait on and nothing to flush, which also means a crash or a restart can never lose the last few seconds of progress.
An achievement's requirements are honored. An achievement carries the same requirement block quests and shop offers do, and it is answered the same way, so a pack that gates its taming or mastery achievements behind those features has them stay hidden - not merely unearnable - on a server where the feature is off, with no content edits.
The Achievements UI
The page uses the standard three-column menu layout. The left nav mirrors the other menu pages; the middle panel shows the achievement list filtered by category and subcategory; the side panel shows your current points, milestone progress, and a summary of your lifetime statistics.
- Categories & subcategories tab along the top of the list
- Search and sort controls to narrow down the list
- Pinning - up to 5 achievements can be pinned. Pinned achievements appear in the side panel for quick progress checking
- Progress bars for single- and multi-criteria achievements; multi-criteria show one bar per criterion
Achievement Types
One achievement, as a file
A built-in achievement is one file, and this is the shape it is written in. The file name is the id, the folders under it are plain organization, and a file naming another as its Parent starts from that one and changes only what it mentions.
// Server/ZiggfreedCommon/Achievements/MMOSkillTree/Combat/Melee/Trork_Hunter.json
{
"Name": "Trork_Hunter",
"Text": { "FlavorKey": "achievement.kills_trork.desc",
"TextArgs": { "Flavor": ["@amount"] } },
"Listing": { "Category": "combat", "Subcategory": "melee", "SortOrder": 6,
"Chains": [ { "Id": "kills_trork", "Tier": 2 } ] },
"Scoring": { "Points": 35 },
"Criteria": {
"kill-entity-trork": { "Kind": "KILL_ENTITY", "Target": "Trork",
"MatchMode": "CONTAINS", "Amount": 250 }
},
"Rewards": {
"Claim": [
{ "Kind": "Mmo_Xp", "Params": { "Skill": "DEFENSE", "Amount": "2500" } }
]
}
}| Group | What it holds |
|---|---|
Text | The title and flavor localization keys, and the values that fill their blanks. |
Listing | Where it reads on the page: category, subcategory, sort order, icon, its place in a chain, and whether it stays hidden until it is earned or until its requirements pass (Hidden, RequirePrerequisites). |
Scoring | Points (10 unless you say otherwise) and CountsTowardTotal, the one switch that keeps an achievement out of the points total: write it false for something nobody can earn any more, so an old player's total stays comparable with a newcomer's. |
Requires | The one shared requirement block, as on quests and shop offers. |
Criteria | A map of steps keyed by criterion id, all of which must be done. |
MetaChildren | Other achievement ids that must all be earned, for a capstone over a set. |
Rewards | One group with two buckets: Auto lands the instant it is earned, Claim waits in the menu to be collected. |
Meta.mmoskilltree | This mod's own flags, including ServerFirst and Feat. |
Each criterion is filed under its own key, and that key is what a player's progress is stored under. Adding, removing or reordering criteria therefore leaves every other tally exactly where it was, and re-authoring the file can never re-point somebody's progress onto a different step. A file naming a Parent merges Criteria per key, so a child can retune one step by naming its key and inherit the rest untouched. Pick keys that read as what the step is (kill-entity-trork) and treat a key, once players have progress under it, as permanent.
Single-criterion
One criterion is enough for most achievements: a trigger, a target, and how many.
// Server/ZiggfreedCommon/Achievements/MyPack/Combat/Trork_Slayer.json
{
"Text": { "TitleKey": "achievement.trork_slayer.title",
"FlavorKey": "achievement.trork_slayer.desc" },
"Listing": { "Category": "combat", "Subcategory": "melee" },
"Scoring": { "Points": 10 },
"Criteria": {
"kill-trork": { "Kind": "KILL_ENTITY", "Target": "Trork",
"MatchMode": "CONTAINS", "Amount": 50 }
},
"Rewards": {
"Claim": [
{ "Kind": "Mmo_Xp", "Params": { "Skill": "DEFENSE", "Amount": "500" } }
]
}
}Multi-criteria
Key several criteria under Criteria and every one has to finish before the achievement unlocks. Progress reads as one bar per criterion on the page.
{
"Text": { "TitleKey": "achievement.well_rounded_hunter.title" },
"Listing": { "Category": "combat", "Subcategory": "melee" },
"Scoring": { "Points": 25 },
"Criteria": {
"skeletons": { "Kind": "KILL_ENTITY", "Target": "Skeleton", "Amount": 50 },
"trorks": { "Kind": "KILL_ENTITY", "Target": "Trork", "Amount": 50 },
"wraiths": { "Kind": "KILL_ENTITY", "Target": "Wraith", "Amount": 50 }
}
}Because every tally is filed under its criterion's key, a fourth quarry can be added, or one of these three dropped, without disturbing the progress players already have on the others.
Meta
A meta-achievement unlocks the moment every child on its list is unlocked. Use it to gate a mastery-style capstone behind a group of smaller achievements.
{
"Text": { "TitleKey": "achievement.combat_master.title",
"FlavorKey": "achievement.combat_master.desc" },
"Listing": { "Category": "combat" },
"Scoring": { "Points": 150 },
"MetaChildren": ["first_blood", "trork_slayer", "skeleton_hunter", "wraith_bane", "goblin_cleaver"],
"Rewards": {
"Claim": [
{ "Kind": "Mmo_Boost_Token", "Params": { "Skill": "SWORDS", "Multiplier": "2.5", "DurationMinutes": "30" } }
]
}
}Meta completions cascade - unlocking the last child triggers the meta unlock automatically.
The older flat shape
Every owner file and every pack authored before this format keeps working untouched: a flat file with id / displayName / triggerType / requiredAmount / rewards at the top level is still read in full, so nothing already written has to change.
{
"id": "trork_slayer",
"displayName": "Trork Slayer",
"description": "Defeat 50 Trorks",
"triggerType": "KILL_ENTITY",
"target": "Trork",
"matchMode": "CONTAINS",
"requiredAmount": 50,
"category": "combat",
"subcategory": "melee",
"points": 10,
"rewards": [
{ "type": "XP", "skill": "DEFENSE", "amount": 500 }
]
}Server-First
Mark an achievement server-first (Meta.mmoskilltree.ServerFirst, or "serverFirst": true on the older shape) to lock it to the first player on the server who clears it. The claim is recorded in mods/mmoskilltree/achievements/server-first-claims.json and all other players see the achievement as locked once someone has claimed it.
A player who finishes one after somebody else gets a quiet grey note rather than a fanfare, and their criteria stay met, so if the winner's claim is ever released the achievement is theirs on the next check with nothing to redo. That note is an authored file you can reword, recolour, silence or announce - see feedback moments.
Feat of Strength
Mark an achievement a feat (Meta.mmoskilltree.Feat) to give it a feat's listing treatment: its own earned-only shelf on the page, no pinning, and nothing shown until somebody earns it. Useful for retired event rewards or admin-granted commemorative unlocks.
Whether it counts toward the points total is a separate, explicit switch: "Scoring": { "CountsTowardTotal": false }, written beside the feat flag. Every feat shipped in the jar authors it, and so should a feat nobody can earn any more; leave it off and the feat scores like any other achievement, which is the right answer for one that is merely rare. On the older flat shape, "featOfStrength": truestill forces the exclusion by itself.
Trigger Types
Any objective trigger can drive an achievement, the same roster quests and bounties draw from.
| Trigger | Fires on | Qualifier |
|---|---|---|
BREAK_BLOCK | Break blocks | - |
PLACE_BLOCK | Place blocks | - |
CRAFT_ITEM | Craft items | - |
KILL_ENTITY | Kill entities | - |
DEAL_DAMAGE | Deal damage | - |
PICKUP_ITEM | Pick up items | - |
REACH_LEVEL | Reach a skill level | - |
TALK_TO_NPC | Talk to an NPC | - |
CATCH_FISH | Catch fish | - |
TURN_IN | Turn in / deliver items | - |
COMPLETE_QUEST | Complete quests | - |
TAKE_FALL_DAMAGE | Take fall damage | - |
PLAYER_DEATH | Die | - |
GAIN_XP | Gain XP | - |
SPRINT_DISTANCE | Sprint a distance | - |
SWIM_DISTANCE | Swim a distance | - |
ABILITY_CAST | Cast abilities | - |
EARN_CURRENCY | Earn currency | - |
BREED_ANIMAL | Breed animals | - |
FEED_ANIMAL | Feed animals | - |
HARVEST_ANIMAL | Harvest from animals | - |
COMPANION_COMBAT | Fight with a companion | - |
COMPLETE_BOUNTY | Complete bounties | Board id (target) and difficulty |
BOUNTY_STREAK | Reach a bounty streak | - |
REACH_LOCATION | Reach a location | - |
CONSUME_ITEM | Consume items | - |
SPEND_CURRENCY | Spend currency | - |
WORK_STATION | Work a station | - |
STATION_OUTPUT | Collect station output | - |
INSTANCE_ROUND_WON | Win a minigame round | - |
INSTANCE_ROUND_ENDED | Finish a minigame round | - |
ENCOUNTER_DEFEATED | Bring down a boss | - |
ENCOUNTER_ATTEMPT | Stand in a boss fight to the end | - |
ENCOUNTER_PHASE | Reach a boss phase | - |
STAT_THRESHOLD | Reach a tracked value | - |
The matchMode field controls how target is matched: EXACT, CONTAINS, or PREFIX. Leave it out and CONTAINS is used; write it anyway, so the file says what it means.
DEAL_DAMAGE counts damage, not hits. Each hit contributes the damage that actually landed, after the target's defenses, so a criterion authored "Amount": 50000 asks for fifty thousand damage rather than fifty thousand swings. Size these the way you would size a health bar, not a kill count.
A KILL_ENTITY qualifier matches when a companion mod attributes a kill rarity
KILL_ENTITY and DEAL_DAMAGE accept a qualifier field. On KILL_ENTITY it is live whenever a companion mod attributes a rarity to the kill: the optional MMO Mob Scaling mod does, so a criterion authored with a tier word (Rare, Epic, Legendary, Boss) progresses on scaled kills - that is how the shipped tier achievements (First_Legend_Kill, Legendary_Slayer, Rare_Hunter, Elite_Slayer) work. Without such a mod every kill fires unqualified and a tier-authored criterion never fires; a criterion with no qualifier always counts every kill exactly once either way. The scaling mod also publishes what it knows about a mob as readings - its rarity, its affix, its difficulty, the power of its region - which a requirement block or a bonus-drop chance can weigh directly. DEAL_DAMAGE's qualifier is still filled by nothing; leave it unset there.Rewards
Achievements pay out through the same open reward vocabulary as quests, shop offers and bounty contracts. A reward names a kind and gives that kind its parameters, and the list of kinds is open: anything the mod, the shared library or a companion mod registers can be authored, and a kind nothing on this server provides is reported by /mmoconfig validate rather than silently dropped, because the mod that pays it out may simply not be installed yet.
| Kind | Pays out |
|---|---|
Mmo_Xp | Skill XP: a skill and an amount. |
Mmo_Boost_Token | A timed XP-multiplier boost token. |
Currency | An amount into a wallet. |
Mmo_Ability_Mod | A standalone ability improvement. |
Item | Items straight into the bag, checked for room first. |
Lootable | A roll on a shared loot table by id. |
Stamped_Item | Gear with its stat bonuses already rolled and stamped on. |
Droplist | A roll on a native drop list, spilled at the player's feet. |
Effect | A native effect applied for a while. |
Command | An authored console command line. |
The historical spellings (XP, BOOST_TOKEN, CURRENCY, COMMAND, ABILITY_MOD, and the flat { "type": ..., ... } form) are translated on the way in, so nothing already written has to be touched. Prefer Item over a Command running a give line: it is checked against the bag before the claim is allowed, and it waits for the player's next connect instead of being lost when there is no room.
Rewards sit in one Rewards group with two buckets, the same group quests and point milestones use. Auto rewards land the instant the achievement is earned, wherever the player is. Claim rewards wait on the achievement's detail page behind the Claim button, which is where anything needing bag room belongs: one room check runs before the claim charges, and a claim with no room stays waiting instead of being lost. Claim is the default: put a reward there unless you specifically want it to arrive unannounced in the field. Each bucket is one leaf, so a file naming a Parent that re-authors Claim replaces the inherited claim list whole and keeps the inherited Auto untouched.
An achievement may also carry its own server-wide announcement (an Announce block naming a title key and a body key), because its words are per-achievement rather than one template; how that banner is drawn, and the unlock toast and jingle every achievement shares, are one authored file you can reword, recolour or silence - see feedback moments.
Points, Milestones & Categories
Every achievement is worth its points value unless its Scoring block says CountsTowardTotal: false, and crossing a cumulative total pays a milestone. Five milestones ship, handing out a Defense XP token at 500 and escalating to a 3x All Skills boost at 24000.
A milestone is a file at Server/ZiggfreedCommon/AchievementMilestones/<Namespace>/<Name>.json, and its threshold is its identity, not its file name - so a file re-authoring 3000 replaces the shipped 3000 rung outright rather than adding a second one beside it. A milestone pays through the same two-bucket Rewards group an achievement does: Auto lands the instant the total is crossed, Claim waits on the achievements page.
// Server/ZiggfreedCommon/AchievementMilestones/MyPack/Points_3000.json
{
"Threshold": 3000,
"TitleKey": "achievement.milestone.3000.title",
"Rewards": {
"Auto": [ { "Kind": "Mmo_Xp", "Params": { "Skill": "ALL", "Amount": "1500" } } ],
"Claim": [ { "Kind": "Item", "Params": { "Item": "Ingredient_Bar_Iron", "Count": "5" } } ]
}
}The categories the page groups by are content too, at Server/ZiggfreedCommon/AchievementCategories/<Namespace>/<Category>.json. The file name is the category word content writes, lower-cased, and the file decides its sort order, its heading key, its fallback icon and the order its subcategories read in. Declare one and your own achievements have a home on the page.
A server owner still has the last word on the points ladder through mods/mmoskilltree/achievements/point-milestones.json, which stays exactly as it was:
{
"schemaVersion": 1,
"milestones": [
{ "threshold": 500, "displayName": "Bronze Achiever", "rewards": [ ... ] },
{ "threshold": 3000, "displayName": "Silver Achiever", "rewards": [ ... ] },
{ "threshold": 7500, "displayName": "Gold Achiever", "rewards": [ ... ] },
{ "threshold": 14000, "displayName": "Platinum Achiever", "rewards": [ ... ] },
{ "threshold": 24000, "displayName": "Diamond Achiever", "rewards": [ ... ] }
]
}Statistics
Alongside achievements, the plugin maintains per-player lifetime statistics. The counters update on the world thread as the same moments quests and achievements react to reach the mod (a block broken, a mob killed, an item crafted or picked up, a block placed), and show on the Achievements side panel with top-3 breakdowns per category. Because XP, statistics and progress all hang off those same moments, what quests do not see, statistics do not count either: a self-placed block broken again produces no moment, so it counts nothing anywhere, and a kill made by a player's turret or summoned ally is credited to that player.
| Key | What it counts |
|---|---|
Mob_Kills | Total mob kills (plus per-mob breakdown) |
Blocks_Broken | Total blocks broken (plus per-block breakdown) |
Blocks_Placed | Total blocks placed (plus per-block breakdown) |
Items_Crafted | Total items crafted (plus per-item breakdown) |
Items_Pickedup | Total items picked up (plus per-item breakdown) |
Damage_Dealt | Total damage dealt to mobs and players, after the target's defenses |
Damage_Taken | Total damage taken, after the player's own defenses |
Deaths | Player deaths |
Fish_Caught | Fish caught (with a supported fishing mod) |
Xp_Earned | Total skill XP earned (plus per-skill breakdown) |
Playtime_Seconds | Time online |
Encounters_Defeated | Boss fights won (plus a per-fight breakdown, keyed by the encounter script id) |
File Layout
The built-in achievements are readable files, shipped in the jar at Server/ZiggfreedCommon/Achievements/MMOSkillTree/<Area>/<Name>.json - 167 of them, with the 6 per-skill ladders stamped out once per skill your server runs. Open any of them to see exactly how a shipped achievement is written, and copy one as the starting point for your own.
Prefer a content pack: author one file per achievement under Server/ZiggfreedCommon/Achievements/<YourPack>/<Area>/ inside a pack folder, zip it, and drop the .zip into your server Mods/ folder. Building the zip.
mods/mmoskilltree/achievements/<id>.json stays the quick per-server tweak that always wins.
For a quick single-achievement tweak with no pack, drop a file with the same id into mods/mmoskilltree/achievements/. Yours wins for that id and nothing else moves: creating an owner file does not hide the built-in set, so extending the shipped achievements and replacing one of them are the same simple move.
| Path | What it is |
|---|---|
Server/ZiggfreedCommon/Achievements/<ns>/** | The achievements themselves, from the jar or from any pack. The file name is the id. |
Server/ZiggfreedCommon/AchievementCategories/<ns>/*.json | The categories the page groups by, with their order, heading and fallback icon. |
Server/ZiggfreedCommon/AchievementMilestones/<ns>/*.json | The rungs of the points ladder. A milestone's threshold is its identity. |
mods/mmoskilltree/achievements/*.json | Your own achievements and your overrides, loaded recursively. Same id wins. |
mods/mmoskilltree/achievements/point-milestones.json | The owner layer over the points ladder. Unchanged, and still the last word. |
mods/mmoskilltree/achievements/server-first-claims.json | Auto-managed; records who claimed each server-first. Unchanged. |
The _defaults/ folder retires itself
_defaults/ copies were regenerated on every startup from achievements that lived in code. Now that the achievements themselves are readable files, there is nothing left to echo: on the first startup after upgrading, each of those files is renamed to .legacy in place, with one line in the log saying so. Nothing is deleted, and nothing you wrote outside that folder is touched.Admin Commands
The MMO's own achievement admin subcommands run under /mmoconfig achievement. The | delimiter separates fields inside --args.
| Command | Effect |
|---|---|
/mmoconfig achievement --args=award|<player>|<id> | Grant an achievement. Add --force to override a server-first lock. |
/mmoconfig achievement --args=revoke|<player>|<id> | Remove an achievement and its progress; cascades to parent metas. |
/mmoconfig achievement --args=reset|<player>|<id|all> | Clear one achievement or all achievements for a player. |
/mmoconfig achievement --args=progress|<player>|<id>|<amount>[|<criterionIndex>] | Set progress on a criterion (default index 0). The index is a shorthand for the criterion's own key in authored order, and the command reports back the key it wrote; an index no criterion sits at is refused rather than guessed at. |
/mmoconfig achievement --args=reload | Reload achievement config from disk; re-hydrate online players. |
/mmoconfig achievement --args=list|[category|all] | List achievements (40 per page). Flags: [meta], [server-first], [feat], [disabled]. |
/mmoconfig achievement --args=pin|<player>|<id> | Pin an achievement for a player (max 5). |
/mmoconfig achievement --args=unpin|<player>|<id> | Remove a pinned achievement. |
The admin dashboard also has an Achievements panel that wraps these operations in a UI.
The shared library's achievement verbs
Achievements run on the shared library's one achievement engine, and the same verbs are reachable as /zigprogress achievement list|status|give|claim|reset - give hands one over outright, claim is the peer of quest claim and takes --achievement=<id|all>, and reset takes the same argument and clears the record, meta cascade included. The MMO's /mmoachadmin reset --player=<name> is an alias over /zigprogress achievement reset that additionally releases the server-first claims that player won, which only this mod can do; the shared spelling wipes the record but leaves the claim recorded. See ZiggfreedCommon commands for the full verb list and permission nodes.