Content Types & Merging
What packs can extend, and how pack content combines with the defaults and the server owner's files
How merging works
- By id, additive. Each content entry has an id. A pack entry with a new id adds content; an entry whose id already exists overrides it. If two packs define the same id, the last-loaded pack wins.
- The server owner always wins. Content the owner authored locally (their own files under
mods/mmoskilltree/ormods/ziggfreedcommon/) is never overridden or dropped by a pack. - Defaults < pack < owner. Packs override the plugin's built-in defaults, but not the owner's explicit customizations.
Shared library types merge on their own
Everything under Server/ZiggfreedCommon/ - quests, achievements, categories, milestones, conversations, NPC placements, wallets, storefronts, shelves, offers, boards, contracts, reward kinds, loot tables, feedback moments - is a keyed asset whose store the engine merges natively by id. There is no Control key for any of them and none is needed:
- To add, ship a file with an id nobody else used.
- To override, ship a file with the same id and the body you want.
- To re-tune one leaf, ship a file carrying
"Parent": "<id>"and author only what changes. - To switch one off, author its id with
"Enabled": falseand nothing else.
That is per file, which is finer-grained than a whole-type switch, and it is why the Control file below covers only the older mod-side types.
Every folder on this page is authored inside your pack directory and shipped as one zip - see Building the zip for the packaging step and the manifest-at-root rule. Every type here also carries a full editor schema, so a pack's files can be authored and validated in the game's own Asset Editor - closed vocabularies as dropdowns, defaults declared - rather than by hand.
Add vs. replace (the Control file)
By default a pack adds to existing content. To make a pack replace the built-in defaults for one of the older mod-side types (a total-conversion mastery pack, say), include a control file at Server/MMOSkillTree/Control/<YourPackName>.json:
{
"Name": "MyMmoPack",
"Quests": "replace",
"XpMaps": "add"
}replace drops the plugin's default content for that type and uses the pack's instead, but the server owner's own authored content is still kept and still wins. The Name must match your manifest Name.
The file accepts exactly these keys (PascalCase, like all asset JSON keys), and nothing else: Quests, QuestTemplates, Achievements, AchievementTemplates, XpMaps, MobKillXp, ItemRequirements, ActionRequirements, BoostTemplates, CommandRewards, CommandRewardTemplates, UIThemes. The Quests and Achievements keys steer the older compat layer only; shared-schema quests and achievements merge natively as described above.
Control keys retired in 1.6.0
LuckLoot, MobDrops, Currencies, BountyBoards, Shops, ShopPools, ShopEntries, ShopTemplates, QuestGivers, DialogueTemplates, Masteries, MasteryTemplates, Classes and ClassTemplates are gone. Every one of those content types is now a natively-merged asset (a bonus-drop row, a wallet, a board, a storefront, a shelf, an offer, a placement, a conversation, a mastery track), so a pack overrides an entry by re-authoring its id and turns one off with "Enabled": false. A control file still naming one is simply an unused key.Abilities, ability improvements, gear-triggered ability grants, custom skills, bonus drops, power-level tuning, world rules and mastery tracks have no Control key either - they merge by id like the shared types.
Feature gating
Quests and achievements can declare a dependency on one or more server features. When any listed feature is disabled, the entry is hidden everywhere - it does not appear in the UI, accrues no progress, and cannot be claimed. Re-enable the feature (config flip + server restart) and the entry returns.
There are three spellings of one gate, and they mean the same thing:
// Shared shape - a flat requirement
{ "Meta": { "mmoskilltree": { "Feature": ["mastery"] } } }
// Shared shape - one arm of an either-or
{ "Requires": { "AnyOf": [
{ "Factors": [ { "Factor": "mmoskilltree:feature", "Param": "mastery", "Min": 1 } ] },
{ "Factors": [ { "Factor": "mmoskilltree:feature", "Param": "taming", "Min": 1 } ] } ] } }
// The older shape
{ "Name": "mastery_tithe", "Payload": { "id": "mastery_tithe", "requiresFeatures": ["mastery"] } }| Feature id | Gates on |
|---|---|
mastery | Mastery system enabled |
abilities (alias active_abilities) | Active abilities enabled |
currency (alias currencies) | At least one wallet enabled |
xp_tokens (alias tokens) | XP tokens enabled |
command_rewards | Command rewards enabled |
bounty (alias bounties) | At least one board loaded |
shop (aliases tokenshop, token_shop) | At least one storefront loaded |
npc_placements (aliases spawn_hub, hubnpc, hub_npc, questgivers, quest_givers) | Whether NPC placements are running. To gate on ONE character, gate on that character's quest or conversation instead |
item_requirements | Item requirements enabled |
action_requirements | Action requirements enabled |
taming (alias tamework) | Taming integration active (Alec's Animal Husbandry) |
stations (aliases station, workstations) | RPG Stations present with at least one station loaded |
No elite_mobs feature id
elite_mobs/elites is not a registered feature id - authoring it fails closed (unknown id, entry hidden forever, a one-time startup warning). Open-world mob difficulty comes from the optional companion mod MMO Mob Scaling, which contributes readings a Requires block can bound instead.- Lenient parse. Either a bare string (
"mastery") or an array is accepted. - AND-combined. When multiple ids are listed, the entry is hidden if any one is off.
- Inherited. A
Parent(or an older template) carrying the gate applies it to every child; a child can author its own list, or an empty one to ungate. - Unknown ids fail closed. An unrecognized feature id hides the entry and logs a one-time
Unknown feature idwarning at server start.
Shared library types (Server/ZiggfreedCommon/)
One file per entry, no Payload envelope and no Id field: the file is the asset. Folders under a type root are yours to arrange and normally change nothing about the id, so give yourself a namespace folder (Quests/MyStudio/...). Every one of these resolves
"Parent": "<id>".| Folder | What it is | Id | Docs |
|---|---|---|---|
Quests/ | One quest | Filename, lower-cased; a folder whose name starts with _ prefixes the id | Quests |
QuestGenerators/ | One file writes a family of quests from a table | Each child's id comes from the generator's IdPattern | Quest generators |
Achievements/ | One achievement | As quests | Achievements |
AchievementCategories/ | What a category is called, its icon and its order | The filename IS the category word, lower-cased | Categories |
AchievementMilestones/ | One rung of the achievement-points ladder | Threshold is the identity, not the filename | Milestones |
Dialogues/ | One branching conversation | Filename, lower-cased | Dialogues |
NpcPlacements/ | Where a character stands, in which worlds, and what press-F opens | Filename (write it PascalCase; lookups ignore case) | NPC placements |
NpcIdentities/ | An identity overlay for a role or a native NPC group | Filename | Identity overlays |
Currencies/ | One wallet | Filename | Currencies |
Shops/ | One storefront | Filename | Storefronts |
ShopPools/ | One rotating shelf | Filename | Shelves |
ShopEntries/ | One purchasable offer | Filename | Offers |
ShopEntryGenerators/ | One file writes a family of offers; loads after offers | Each child's id comes from IdPattern | Offer generators |
Boards/ | One board of contracts: cadence, slots, bands | Filename | Boards |
Bounties/ | One contract (a quest with board membership) | Filename | Contracts |
RewardKinds/ | A payout kind of your own: parameters plus one command line | The filename IS the kind id | Reward kinds |
Lootables/ | A named, reusable loot table anything can point at | Filename, lower-cased | Shared loot |
RollPools/ | A named, reusable table of stat-roll outcomes a stamp draws from when it enhances an item a player already owns | Filename, lower-cased | Roll pools |
FeedbackMoments/ | What a moment says: toast, broadcast, sound, command, with Variants branching on the moment's own values. Eight fire today (Quest_Objective_Progressed, Quest_Completed, Quest_Parked, Quest_Claimed, Achievement_Unlocked, Achievement_Claimed, Achievement_Server_First_Lost, Rare_Find); the library ships a neutral file for the first seven and the MMO jar ships its own file for all eight, so a pack overrides one by shipping the same name (later pack wins) - and a loot roll's Cue can name a brand-new moment id a pack ships beside it | The filename IS the moment id | Feedback moments |
Factors/ | A naming overlay for one requirement factor: how a locked line is worded (never what the gate checks), via Text.TitleKey and the ParamNames grammar. Overlays compose, so a pack can word its own custom skill, mastery nodes, or a factor somebody else registered | Filename | Naming overlays |
A basename must be unique across its whole store
_-marked folder above it) starts that content over for everyone mid-way through it. Pick both as carefully as a database key.MMO content types (Server/MMOSkillTree/)
Files may be nested in subfolders freely. Three shapes are in use.
Structured types
The codec is the schema: PascalCase keys, no Payload wrapper, native Parent reuse.
| Type | Folder | What a file holds | Docs |
|---|---|---|---|
| Abilities (1.6.0) | Abilities/<Skill>/ | Seven envelope groups (Identity, Cooldown, Cost, XpRouting, Casting, Presentation, PassiveTrigger) plus Body, the interaction chain the ability runs - eight in all | Ability authoring |
| Ability Mods (1.6.0) | AbilityMods/ | A grantable improvement to an existing ability | Ability Mods |
| Item ability grants (1.6.0) | ItemAbilityGrants/ | Which ability a held or worn item grants | Ability authoring |
| Custom skills (1.6.0) | CustomSkills/ | A skill of your own: Display / Placement groups, Triggers, RequiresFeatures, MaxLevel | Custom Skills |
| Bonus drops (1.6.0) | BonusDrops/ | One row: When (BreakBlock or KillMob plus a name pattern), an optional Chance, the Loot it hands over, Notify, Enabled | Bonus Drops |
| Power Level (1.5.0) | PowerLevel/ | Formula tuning for the power-level pillars, clamp and modes read by MMO Mob Scaling | Power Level |
| Modifier clamps | ModifierClamps/ | A project-wide Min/Max bound for one modifier key, so a stacked pack cannot push a number past what the server intends | Ability numbers |
| World rules | WorldRules/ | Per-world rules: a Where selector picking the worlds, a nested Rules group of gameplay knobs | World Rules |
| Masteries (1.6.0) | Masteries/ | One mastery track: Target, a Nodes map keyed by node id, Requires, with an Abstract base reusable as Parent | Masteries |
| Mastery generators (1.6.0) | MasteryGenerators/ | One file writes a family of tracks from a table; each child's id comes from IdPattern | Mastery generators |
| Classes (Future) | Classes/ | One class definition: Requires, SwitchPolicy, Grants, an Advancements map keyed by advancement id | below |
Per-entry "payload" types
The older mod-side shape: one file per entry, the asset key derived from the filename, and Payload holding the entry as a nested JSON object.
| Type | Folder | Payload | Docs |
|---|---|---|---|
| Quests | Quests/ | A single quest object. Older shape (still read); the current path is Server/ZiggfreedCommon/Quests/ | Quests |
| Achievements | Achievements/ | A single achievement object. Older shape (still read); the current path is Server/ZiggfreedCommon/Achievements/ | Achievements |
| Dialogues | Dialogues/ | A single conversation. Compat store; the current path is Server/ZiggfreedCommon/Dialogues/ | Dialogues |
| UI Themes | UIThemes/ | A menu theme: a palette of colours and texture directory, a displayNameKey, an order, plus optional per-class accents and .ui template overrides. Ship as many as you like - the Molten theme pack ships two - and the server picks the active one with /mmotheme or the in-game theme picker | - |
Masteries and Classes left the Payload shape in 1.6.0
Name/Payload envelope, native Parent reuse. A pack file still speaking the old envelope grammar, or the retired MasteryTemplates//ClassTemplates/ stores, is named in the start-up log and loads nothing; /mmomigrate and the Migration Converter both report exactly which files and what replaces them.Ability precedence (unique to this type)
Abilities are the one content type with a full three-layer fold, all through the SAME codec:
- Defaults - the jar-shipped abilities under
Server/MMOSkillTree/Abilities/<Skill>/*.json. - Pack - a pack file at the same path with a matching id overrides the default entirely, unless it uses
Parentto overlay just a few fields. - Owner -
mods/mmoskilltree/ability-overrides.jsonfolds last, per-leaf (the same overlay semantics asParent: overriding one field never nulls out the rest).
abilities.json is retired
mods/mmoskilltree/abilities.json was a hard break in 1.6.0. Any file found there at startup is moved to mods/mmoskilltree/backup/ with a warning and never read again. Re-author ability tuning in ability-overrides.json instead - see Ability authoring.
Per-pack "map" types
One file per pack; Name is your pack name and Payload is the config's natural map.
| Type | Folder | Payload shape | Docs |
|---|---|---|---|
| XP maps | XpMaps/ | { "SKILL": { "pattern": value } } | XP Values |
| Mob-kill XP | MobKillXp/ | { "MobPattern": value } | Mob Kill XP |
| Item requirements | ItemRequirements/ | { "ItemPattern": { …override… } } | Item Requirements |
| Action requirements | ActionRequirements/ | { "blockTool": {…}, "mobWeapon": {…} } | Item Requirements |
| Boost templates | BoostTemplates/ | { "templateId": { "multiplier": … } } | XP Boosts |
| Command rewards | CommandRewards/ | { "<SKILL|TOTAL|GLOBAL_SKILL>": { "<level>": [ … ] } } | Command Rewards |
// Server/MMOSkillTree/XpMaps/MyMmoPack.json
{
"Name": "MyMmoPack",
"Payload": { "MINING": { "Ore_Adamantite": 25.0 } }
}Masteries
A mastery track targets a skill (skill:MINING), an ability (ability:fireball) or every ability (global) and holds a Nodes map of purchasable nodes, each with a Tier, a Cost, and Modifiers. Repeatable Eternal nodes set MaxPurchases: -1 with CostScaling. Full field reference, Parent reuse and generators: Authoring Mastery Tracks; see the player-facing Mastery guide for how this looks in-game.
// Server/MMOSkillTree/Masteries/Mining_Mastery.json
{
"Target": "skill:MINING",
"RefundPercent": 50,
"Requires": {
"Factors": [ { "Factor": "hytale:stat", "Param": "MMO_Level_MINING", "Min": 5 } ]
},
"Nodes": {
"min_t1": {
"Tier": 1,
"Cost": {
"Currencies": { "mastery_point": 2, "life_essence": 500 },
"Items": [ { "Item": "Ingredient_Crystal_Blue", "Count": 1 } ]
},
"Modifiers": [ { "Shape": "PERCENT", "ParamKey": "lootMultiplier", "Value": 0.03, "TargetSkill": "MINING" } ]
},
"min_eternal": {
"Tier": 9,
"MaxPurchases": -1,
"CostScaling": { "Curve": "EXPONENTIAL", "Multiplier": 1.1 },
"Cost": { "Currencies": { "mastery_point": 1, "life_essence": 200 } },
"Modifiers": [ { "Shape": "PERCENT", "ParamKey": "lootMultiplier", "Value": 0.005, "TargetSkill": "MINING" } ]
}
}
}Currencies, shops and boards moved
Wallets, storefronts, shelves, offers, boards and contracts are shared library content and live under Server/ZiggfreedCommon/. The six old mod-side stores (Currencies, Shops, ShopPools, ShopEntries, ShopTemplates, BountyBoards) are read at boot only so a pack still shipping them can be named in the log and pointed at the new folder; nothing in them loads. See Authoring Shops & Currencies and Boards and contracts.
Classes (Future)
The Class System is a future, unreleased feature; the schema below documents the planned content shape. The plugin ships no built-in classes, and without a class pack the class system stays dormant. A class definition carries base grants applied while the class is selected, plus an additive Advancements map (ranks, keyed by advancement id) unlocked by meeting requirements. It is a structured type: PascalCase fields, no Payload envelope, and a shared skeleton is an Abstract file named as Parent, the same paradigm as mastery tracks.
| Field | Type | Default | Description |
|---|---|---|---|
Icon / Color | string | optional | UI surface; Icon is a Hytale item id. Display text is key-only (class.<id>.name and friends). |
Requires | Requires block | optional | Gate selection. Empty = freely selectable. See The one Requires block. |
SwitchPolicy | SwitchPolicy | optional | Per-class override of the global switch policy; its price is one Cost group (Currency/Amount/Items) beside FirstSwitchFree, CooldownMs, EscalationMultiplier, PermanentLock. |
Grants | ClassGrants | optional | Applied while the class is selected. |
Advancements | map | optional | Additive ranks keyed by advancement id, each with its own Requires and Grants; grants deep-merge over base. |
ClassGrants (used by base and each advancement):
| Field | Type | Default | Description |
|---|---|---|---|
XpMultipliers | - | optional | map of skill → multiplier (0.0 = no XP, 1.25 = +25%). |
Abilities | - | optional | {"Allow": [...], "Deny": [...]} - gates which abilities are usable. A Deny always wins; an empty Allow means no gate. |
Mastery | - | optional | The same Allow/Deny pair over mastery purchases (by trackId or trackId:nodeId). |
SkillRewards | - | optional | The same Allow/Deny pair over skill-tree reward claims. |
PassiveRewards | - | optional | SkillRewards applied while the class is active. |
StartingItems / StartingMasteryNodes / ClassQuests | - | optional | Granted/unlocked on selection. |
Exporting your server's content as a pack
/mmopacks --action=export --name=<MyPack> writes your server's custom quests and achievements out as a loadable pack zip under packs/ - a working starting point with valid filenames and shapes already in place. /mmopacks (or /mmopacks --action=list) lists loaded packs and how many entries each contributes per content type. Both arguments bind by name only, never positionally. The exporter writes the shared Server/ZiggfreedCommon/ layout described above, so the result is ready to author further without conversion.