Trade overrides
Profession catalogues, offer selection, stacks and transactional reload validation.
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
| Field | Type | Default | What it does |
|---|---|---|---|
schema_version | integer | required | Must be 3; any other value rejects the catalogue update. |
profession | identifier | required | Catalogue key for a registered villager profession or a custom NPC job. Optional provider mods must be gated explicitly with requires_mods. |
priority | integer | 0 | Within the same namespace authority, higher wins. Range −1000000…1000000. |
requires_mods | array<string> | [] | All listed mod IDs must be loaded; empty imposes no requirement. |
excludes_mods | array<string> | [] | Any loaded listed mod disables this definition; empty excludes none. |
selection | object | {"mode":"all"} | Missing or null selects all eligible offers. See selection fields. |
selection.mode | enum(all, weighted) | required in object | all selects every candidate; weighted samples without replacement after guaranteed offers. |
selection.count | integer | required for weighted | 1–64 offers per tier. Forbidden in all mode. |
tiers | object | required | At least one catalogue tier key, 1–5. Each value is an offer array with at most 1024 entries; an empty array is accepted. |
courtesies | array<object> | [] | Up to 3 profession services. See courtesy fields; explicit null is rejected. |
Offer fields
| Field | Type | Default | What it does |
|---|---|---|---|
id | string | required | Unique across every tier in this file. Matches [a-z0-9][a-z0-9_.-]*. |
costs | array<object> | required | 1–2 stacks, at most one money stack. Currency is sorted into the first payment slot. |
result | object | required | Output stack, using the same stack schema as costs. |
weight | integer | 1 | Weighted selection weight, 1–100000. |
guaranteed | boolean | false | Eligible offer is selected before random candidates. Guaranteed entries per tier must not exceed weighted count. |
max_uses | integer | 12 | Uses before restocking, 1–1000000. |
price_multiplier | number | 0.0 | Finite value 0–100. A money cost forces the constructed offer’s multiplier to zero. |
demand | integer | 0 | Initial demand, −1000000…1000000. A money cost forces initial demand to zero. |
conditions | object | {} | Missing or null imposes no conditions. Tested when offers are generated. |
Stack fields
| Field | Type | Default | What it does |
|---|---|---|---|
money | integer | required for currency | 1–9223372036854775807 bronze units. This must be the only key in a currency stack. |
item | item id | required for items | One ID or a nonempty array of unique IDs. A registered candidate is chosen uniformly; missing candidates are removed. |
count | integer | 1 | Fixed count or [minimum, maximum], inclusive. Bounds 1–64, maximum at least minimum. Random count is independent of the item’s native stack limit. |
nbt | object | null | Compound JSON or an SNBT string; parsed as a compound. Applied before other stack options; invalid syntax rejects the update. |
custom_name | string | null | Nonblank literal name, not a translation key; explicit null is invalid. |
damage | integer | 0 | 0–2147483647. Applied only to damageable items and capped at the item’s maximum damage. |
potion | identifier | null | Potion ID; unregistered potions are ignored when constructing the stack. Explicit null is invalid. |
enchantments | array<object> | [] | Missing or null means none. Unknown registered-name lookups are skipped; does not validate item/enchantment compatibility. |
enchantments[].id | identifier | required | Syntactically valid enchantment ID. |
enchantments[].level | integer | required | 1–255; does not clamp to the enchantment’s ordinary maximum. |
Condition fields
| Field | Type | Default | What it does |
|---|---|---|---|
conditions.requires_mods | array<string> | [] | All listed mods must be loaded. |
conditions.excludes_mods | array<string> | [] | None of these mods may be loaded. |
conditions.villager_types | array<identifier> | absent: unrestricted | One ID or a nonempty unique ID array. Matches the villager’s type; an explicit empty array is rejected. |
conditions.dimensions | array<identifier> | absent: unrestricted | One ID or a nonempty unique ID array. Matches the world ID; an explicit empty array is rejected. |
Courtesy fields
| Field | Type | Default | What it does |
|---|---|---|---|
courtesies[].id | string | required | Unique within courtesies; same syntax as offer IDs. |
courtesies[].label | string | required | Nonblank translation key, authored in both languages. |
courtesies[].rank | integer | required | One service at each chosen rank: 2, 3 or 5. Duplicate ranks are rejected. |
courtesies[].action | object | required | Registered action payload; missing or invalid payload rejects the catalogue. |
courtesies[].action.type | identifier | required | Namespaced registered codec. Unknown IDs reject reload. |
courtesies[].action.item | object | isekaiexpansion:gift | One fixed item stack of 1–16; no money. Uses the stack fields above. |
courtesies[].action.item_tag | identifier | isekaiexpansion:repair | Tag without #; damaged main-hand item must match. |
courtesies[].action.percent | integer | isekaiexpansion:repair | 1–10% maximum durability; round down, minimum 1 point. |
courtesies[].action.cap | integer | isekaiexpansion:repair | 1–100 durability points; existing damage is the final ceiling. |
courtesies[].action.hunger | integer | isekaiexpansion:meal | 1–8 hunger points; adds saturation at factor 0.25. Requires hunger below full. |
courtesies[].action.effect | identifier | isekaiexpansion:cleanse, isekaiexpansion:effect | Existing status effect. Cleanse removes a present non-beneficial effect. Effect grants a non-instant beneficial effect at amplifier 0 only when absent. |
courtesies[].action.seconds | integer | isekaiexpansion:effect | 1–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.
Related
Trade prices can also interact with Reputation.