Kzeroko KTM2

Trade overrides

Profession catalogues, offer selection, stacks and transactional reload validation.

Updated Sep 30, 2026 9 min read

A trade override owns a merchant profession’s complete catalogue. Every tier contributes its selected stock on the first visit. The same definition can serve villagers and NPCs; personal Trade Rapport controls courtesies separately.

Where the files live

Use data/<namespace>/trade_overrides/<path>.json. One file targets one profession. For different resource IDs targeting the same profession, the isekaiexpansion namespace wins over every other namespace, then higher priority wins, then the lexicographically greater resource-ID string wins. Selection is for the whole definition, not a tier-by-tier merge. To replace bundled content, override its exact resource path or use an isekaiexpansion resource with suitable priority.

Bundled files are organised into vanilla/, compat/<provider>_<profession>.json, npc/unique/ and npc/common/. An NPC preset selects a catalogue with trade_profession. A unique character such as Evelina uses isekaiexpansion:mahoutsukai_evelina; a common NPC may use minecraft:farmer or another existing catalogue. A per-entity TradeProfession assignment overrides the preset for common NPCs and survives saving. Unique NPCs always use the preset binding.

Minimal example

This complete catalogue uses the same item-stack syntax as the bundled farmer file and uses Numismatic currency:

{
  "schema_version": 3,
  "profession": "minecraft:farmer",
  "tiers": {
    "1": [
      {
        "id": "bread",
        "costs": [
          {
            "money": 48
          }
        ],
        "result": {
          "item": "minecraft:bread"
        }
      }
    ]
  }
}

Fields

Root fields

FieldTypeDefaultWhat it does
schema_versionintegerrequiredMust be 3; any other value rejects the catalogue update.
professionidentifierrequiredCatalogue key for a registered villager profession or a custom NPC job. Optional provider mods must be gated explicitly with requires_mods.
priorityinteger0Within the same namespace authority, higher wins. Range −1000000…1000000.
requires_modsarray<string>[]All listed mod IDs must be loaded; empty imposes no requirement.
excludes_modsarray<string>[]Any loaded listed mod disables this definition; empty excludes none.
selectionobject{"mode":"all"}Missing or null selects all eligible offers. See selection fields.
selection.modeenum(all, weighted)required in objectall selects every candidate; weighted samples without replacement after guaranteed offers.
selection.countintegerrequired for weighted1–64 offers per tier. Forbidden in all mode.
tiersobjectrequiredAt least one catalogue tier key, 1–5. Each value is an offer array with at most 1024 entries; an empty array is accepted.
courtesiesarray<object>[]Up to 3 profession services. See courtesy fields; explicit null is rejected.

Offer fields

FieldTypeDefaultWhat it does
idstringrequiredUnique across every tier in this file. Matches [a-z0-9][a-z0-9_.-]*.
costsarray<object>required1–2 stacks, at most one money stack. Currency is sorted into the first payment slot.
resultobjectrequiredOutput stack, using the same stack schema as costs.
weightinteger1Weighted selection weight, 1–100000.
guaranteedbooleanfalseEligible offer is selected before random candidates. Guaranteed entries per tier must not exceed weighted count.
max_usesinteger12Uses before restocking, 1–1000000.
price_multipliernumber0.0Finite value 0–100. A money cost forces the constructed offer’s multiplier to zero.
demandinteger0Initial demand, −1000000…1000000. A money cost forces initial demand to zero.
conditionsobject{}Missing or null imposes no conditions. Tested when offers are generated.

Stack fields

FieldTypeDefaultWhat it does
moneyintegerrequired for currency1–9223372036854775807 bronze units. This must be the only key in a currency stack.
itemitem idrequired for itemsOne ID or a nonempty array of unique IDs. A registered candidate is chosen uniformly; missing candidates are removed.
countinteger1Fixed count or [minimum, maximum], inclusive. Bounds 1–64, maximum at least minimum. Random count is independent of the item’s native stack limit.
nbtobjectnullCompound JSON or an SNBT string; parsed as a compound. Applied before other stack options; invalid syntax rejects the update.
custom_namestringnullNonblank literal name, not a translation key; explicit null is invalid.
damageinteger00–2147483647. Applied only to damageable items and capped at the item’s maximum damage.
potionidentifiernullPotion ID; unregistered potions are ignored when constructing the stack. Explicit null is invalid.
enchantmentsarray<object>[]Missing or null means none. Unknown registered-name lookups are skipped; does not validate item/enchantment compatibility.
enchantments[].ididentifierrequiredSyntactically valid enchantment ID.
enchantments[].levelintegerrequired1–255; does not clamp to the enchantment’s ordinary maximum.

Condition fields

FieldTypeDefaultWhat it does
conditions.requires_modsarray<string>[]All listed mods must be loaded.
conditions.excludes_modsarray<string>[]None of these mods may be loaded.
conditions.villager_typesarray<identifier>absent: unrestrictedOne ID or a nonempty unique ID array. Matches the villager’s type; an explicit empty array is rejected.
conditions.dimensionsarray<identifier>absent: unrestrictedOne ID or a nonempty unique ID array. Matches the world ID; an explicit empty array is rejected.

Courtesy fields

FieldTypeDefaultWhat it does
courtesies[].idstringrequiredUnique within courtesies; same syntax as offer IDs.
courtesies[].labelstringrequiredNonblank translation key, authored in both languages.
courtesies[].rankintegerrequiredOne service at each chosen rank: 2, 3 or 5. Duplicate ranks are rejected.
courtesies[].actionobjectrequiredRegistered action payload; missing or invalid payload rejects the catalogue.
courtesies[].action.typeidentifierrequiredNamespaced registered codec. Unknown IDs reject reload.
courtesies[].action.itemobjectisekaiexpansion:giftOne fixed item stack of 1–16; no money. Uses the stack fields above.
courtesies[].action.item_tagidentifierisekaiexpansion:repairTag without #; damaged main-hand item must match.
courtesies[].action.percentintegerisekaiexpansion:repair1–10% maximum durability; round down, minimum 1 point.
courtesies[].action.capintegerisekaiexpansion:repair1–100 durability points; existing damage is the final ceiling.
courtesies[].action.hungerintegerisekaiexpansion:meal1–8 hunger points; adds saturation at factor 0.25. Requires hunger below full.
courtesies[].action.effectidentifierisekaiexpansion:cleanse, isekaiexpansion:effectExisting status effect. Cleanse removes a present non-beneficial effect. Effect grants a non-instant beneficial effect at amplifier 0 only when absent.
courtesies[].action.secondsintegerisekaiexpansion:effect1–120 seconds, converted at 20 ticks per second.

Each built-in rejects fields belonging to another action. External action types validate their own payload. Gifts require pack space; repairs change only damage. Every action rechecks session, definition, rank, saved use and the selected main-hand snapshot, and spends a use only after success. Unique NPCs reject this menu and have no bundled courtesies. Schema 3 has no version 2 or flat-action reader.

Full example

This selects 1 weighted offer and resolves its item candidate and count range when generating offers:

{
  "schema_version": 3,
  "profession": "minecraft:farmer",
  "tiers": {
    "1": [
      {
        "id": "bread",
        "costs": [
          {
            "money": 48
          }
        ],
        "result": {
          "item": [
            "minecraft:bread",
            "minecraft:carrot"
          ],
          "count": [
            1,
            3
          ]
        },
        "weight": 5
      }
    ]
  },
  "priority": 10,
  "selection": {
    "mode": "weighted",
    "count": 1
  }
}

Validation

Only schema version 3 is accepted. Removed level and villager-experience fields are rejected. Unknown fields are rejected at every object level. Missing required fields, wrong types, duplicate offer IDs, invalid identifier syntax, out-of-range numbers and invalid NBT log errors and retain the previous catalogue; no parsed definitions from that update are installed. An empty text file is invalid JSON, and an empty object lacks required fields. The tiers object cannot be empty, but its tier arrays can.

Integers must be exact integers; numeric strings and fractions are rejected. Mod lists reject blank or duplicate values. Custom profession IDs are accepted; an offer with no available item candidate is filtered out. Even if filtering leaves no offers, the selected definition still owns that profession and does not fall back to vanilla trades.

Money stacks require numismatic-overhaul and its bronze, silver, gold and money-bag items. Silver is worth 100 and gold 10000 bronze units. Money costs occupy the first slot and force initial demand and price multiplier to zero. These values describe IsekaiExpansion’s currency adapter.

Exchange valuables are reserved for the separate gold exchange. Any cost or result containing a registered exchange valuable or LuxuryVal NBT is unavailable, including item-array candidates and NPC catalogues. Creation and REI previews apply the same rule. The catalogue generator derives its valuable IDs from the item registrations and the validator rejects them in either direction.

Reloading

Files load on the server at startup and on /reload. A successful update advances the catalogue revision. Merchants rebuild on their next offer lookup when the definition, revision or stock rotation changes. Seeds derive from merchant UUID, profession, catalogue tier, offer ID and saved stock rotation; reopening the merchant screen does not reroll offers.

Generation includes all tiers 1–5 on the first visit. Rebuilding matches old offers by the items, counts and NBT of payment/output stacks. Matching offers retain uses capped at the new maximum, demand and special price, with existing currency-reputation data transferred. A profession/offer-ID ledger also retains used stock across rotations until a genuine restock; keep IDs stable. Clients receive the actual merchant offers rather than selecting them locally.

A villager restock grants one stored refresh chance, capped at one. NPCs replenish on the next elapsed server day and gain the same refresh chance; first contact does not grant one. The dialogue service spends it to rotate the current selection. Weighted catalogues keep guaranteed offers and prefer non-guaranteed IDs absent from the previous selection; when there are too few unseen candidates, repeats fill the remaining slots. The prior selection and rotation seed survive entity saves. all catalogues still include every eligible offer. See Trading for bargaining and filters.

REI previews read the bundled catalogue, not the server’s reloaded overrides. They show available candidates after mod conditions, using minimum counts for ranged stacks; they do not represent a particular villager’s weighted selection, type or dimension. Verify data-pack edits with a live merchant.

The bundled catalogue generator and validator exclude Donner, Schlag, bounty contracts and bounty boards, as well as developer tools, test items, internal icons and world-authoring anchors or structure blocks. These items must not become shop stock. The checks cover every item candidate, including NPC catalogues.

Trade prices can also interact with Reputation.