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/ or mods/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": false and 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 idGates on
masteryMastery 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_rewardsCommand 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_requirementsItem requirements enabled
action_requirementsAction 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

The built-in Elite Mobs system was removed in 1.5.0, and 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 id warning 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

defaults<pack<owner
and supports "Parent": "<id>".

FolderWhat it isIdDocs
Quests/One questFilename, lower-cased; a folder whose name starts with _ prefixes the idQuests
QuestGenerators/One file writes a family of quests from a tableEach child's id comes from the generator's IdPatternQuest generators
Achievements/One achievementAs questsAchievements
AchievementCategories/What a category is called, its icon and its orderThe filename IS the category word, lower-casedCategories
AchievementMilestones/One rung of the achievement-points ladderThreshold is the identity, not the filenameMilestones
Dialogues/One branching conversationFilename, lower-casedDialogues
NpcPlacements/Where a character stands, in which worlds, and what press-F opensFilename (write it PascalCase; lookups ignore case)NPC placements
NpcIdentities/An identity overlay for a role or a native NPC groupFilenameIdentity overlays
Currencies/One walletFilenameCurrencies
Shops/One storefrontFilenameStorefronts
ShopPools/One rotating shelfFilenameShelves
ShopEntries/One purchasable offerFilenameOffers
ShopEntryGenerators/One file writes a family of offers; loads after offersEach child's id comes from IdPatternOffer generators
Boards/One board of contracts: cadence, slots, bandsFilenameBoards
Bounties/One contract (a quest with board membership)FilenameContracts
RewardKinds/A payout kind of your own: parameters plus one command lineThe filename IS the kind idReward kinds
Lootables/A named, reusable loot table anything can point atFilename, lower-casedShared loot
RollPools/A named, reusable table of stat-roll outcomes a stamp draws from when it enhances an item a player already ownsFilename, lower-casedRoll 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 itThe filename IS the moment idFeedback 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 registeredFilenameNaming overlays

A basename must be unique across its whole store

Two files resolving to one id are reported together, naming both, and one of them loses. An id is also what a player's saved progress is filed under, so renaming a file (or an _-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.

TypeFolderWhat a file holdsDocs
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 allAbility authoring
Ability Mods (1.6.0)AbilityMods/A grantable improvement to an existing abilityAbility Mods
Item ability grants (1.6.0)ItemAbilityGrants/Which ability a held or worn item grantsAbility authoring
Custom skills (1.6.0)CustomSkills/A skill of your own: Display / Placement groups, Triggers, RequiresFeatures, MaxLevelCustom 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, EnabledBonus Drops
Power Level (1.5.0)PowerLevel/Formula tuning for the power-level pillars, clamp and modes read by MMO Mob ScalingPower Level
Modifier clampsModifierClamps/A project-wide Min/Max bound for one modifier key, so a stacked pack cannot push a number past what the server intendsAbility numbers
World rulesWorldRules/Per-world rules: a Where selector picking the worlds, a nested Rules group of gameplay knobsWorld Rules
Masteries (1.6.0)Masteries/One mastery track: Target, a Nodes map keyed by node id, Requires, with an Abstract base reusable as ParentMasteries
Mastery generators (1.6.0)MasteryGenerators/One file writes a family of tracks from a table; each child's id comes from IdPatternMastery generators
Classes (Future)Classes/One class definition: Requires, SwitchPolicy, Grants, an Advancements map keyed by advancement idbelow

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.

TypeFolderPayloadDocs
QuestsQuests/A single quest object. Older shape (still read); the current path is Server/ZiggfreedCommon/Quests/Quests
AchievementsAchievements/A single achievement object. Older shape (still read); the current path is Server/ZiggfreedCommon/Achievements/Achievements
DialoguesDialogues/A single conversation. Compat store; the current path is Server/ZiggfreedCommon/Dialogues/Dialogues
UI ThemesUIThemes/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

A mastery track (and, when it ships, a class) is a structured type now: PascalCase fields at the top level, no 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<pack<owner
  1. Defaults - the jar-shipped abilities under Server/MMOSkillTree/Abilities/<Skill>/*.json.
  2. Pack - a pack file at the same path with a matching id overrides the default entirely, unless it uses Parent to overlay just a few fields.
  3. Owner - mods/mmoskilltree/ability-overrides.json folds last, per-leaf (the same overlay semantics as Parent: overriding one field never nulls out the rest).

abilities.json is retired

The legacy JSON-blob override format at 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.
The in-game pack-management view, showing loaded packs and how many entries each contributes per content type.
The in-game pack-management view, showing loaded packs and how many entries each contributes per content type.

Per-pack "map" types

One file per pack; Name is your pack name and Payload is the config's natural map.

TypeFolderPayload shapeDocs
XP mapsXpMaps/{ "SKILL": { "pattern": value } }XP Values
Mob-kill XPMobKillXp/{ "MobPattern": value }Mob Kill XP
Item requirementsItemRequirements/{ "ItemPattern": { …override… } }Item Requirements
Action requirementsActionRequirements/{ "blockTool": {…}, "mobWeapon": {…} }Item Requirements
Boost templatesBoostTemplates/{ "templateId": { "multiplier": … } }XP Boosts
Command rewardsCommandRewards/{ "<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.

FieldTypeDefaultDescription
Icon / ColorstringoptionalUI surface; Icon is a Hytale item id. Display text is key-only (class.<id>.name and friends).
RequiresRequires blockoptionalGate selection. Empty = freely selectable. See The one Requires block.
SwitchPolicySwitchPolicyoptionalPer-class override of the global switch policy; its price is one Cost group (Currency/Amount/Items) beside FirstSwitchFree, CooldownMs, EscalationMultiplier, PermanentLock.
GrantsClassGrantsoptionalApplied while the class is selected.
AdvancementsmapoptionalAdditive ranks keyed by advancement id, each with its own Requires and Grants; grants deep-merge over base.
* required

ClassGrants (used by base and each advancement):

FieldTypeDefaultDescription
XpMultipliers-optionalmap 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-optionalThe same Allow/Deny pair over mastery purchases (by trackId or trackId:nodeId).
SkillRewards-optionalThe same Allow/Deny pair over skill-tree reward claims.
PassiveRewards-optionalSkillRewards applied while the class is active.
StartingItems / StartingMasteryNodes / ClassQuests-optionalGranted/unlocked on selection.
* required

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.