Kzeroko KTM2

Humanoid and NPC presets

The complete shared appearance format, player gallery presets and NPC outfit rules.

Updated Sep 30, 2026 13 min read

Player presets store a reusable appearance. NPC presets store a base character and selectable outfit overlays. They share appearance fields but have different application and unlock rules.

Where the files live

PathMeaning
data/<namespace>/humanoid_presets/<path>.jsonPlayer gallery preset <namespace>:<path>.
data/<namespace>/npc_presets/<path>.jsonNPC preset <namespace>:<path>; the NPC must use this preset ID.

These are server data-pack resources. Models and textures are separate client resources. A file is a complete definition, not a patch to another preset.

Player titles use humanoid_preset.<namespace>.<path>, descriptions add .desc, and outfit titles use npc_outfit.<namespace>.<preset_path>.<outfit_id>. Slashes in preset paths become dots.

Minimal example

A player gallery entry at data/example/humanoid_presets/simple.json, using bundled parts:

{
  "gender": "female",
  "parts": {
    "body": "isekaiexpansion:body/feminine",
    "eyes": "isekaiexpansion:eyes/default"
  },
  "sort": 1000
}

For the bundled citizen wardrobe, save this at data/isekaiexpansion/npc_presets/citizen.json. It leaves citizen appearance generation in place and adds an explicitly rewarded overlay:

{
  "outfits": [
    { "id": "default", "default": true },
    {
      "id": "without_hair",
      "category": "hair",
      "unlock": { "type": "granted" },
      "parts": { "hair": "none" }
    }
  ]
}

The reward record is per player and keyed by preset ID plus outfit ID, so it applies to every NPC sharing that preset.

Fields

Shared root and preset metadata

FieldTypeDefaultWhat it does
genderstringfemalefemale or male, trimmed and case-insensitive; unknown uses the default.
ageinteger18Clamped to 12–400; identity metadata, independent of body height.
nicknamestring""Removes formatting/control characters, keeps at most 48 UTF-16 units without splitting code points, then trims. Unicode control/format characters and section signs are removed. A nonempty result enables nickname use.
preset_nameidentifierabsentTranslated full-name ID from name pools, at most 256 characters. Mutually exclusive with nickname; invalid syntax rejects the preset. Enables name display.
originidentifierabsentOrigin hint; invalid syntax clears it. Applying a player preset preserves the player’s actual origin.
buildobjectneutralBody values below; omitted members keep their defaults.
faceobjectneutralFace values below; omitted members keep their defaults.
paletteobjectchannel defaultsRGB values for the channels below. Unknown channels are ignored.
partsobject{}Slot-to-part map. Empty does not create models; player validation can supply required parts.
parts.<slot>string / object / nullabsentPart ID, {part, style}, or no part. Slots and overlay semantics are listed below.
parts.<slot>.partidentifierrequired in objectInvalid or missing ID removes this reference; existence is checked later.
parts.<slot>.stylestringdefaultTrimmed lowercase a-z, 0-9, _, -, at most 64 characters. Invalid becomes default.
sortinteger1000Gallery order; player presets sort by this value, then full identifier.
tagsarray<string>[]Trimmed lowercase, empty entries removed and duplicates collapsed. Keep player-preset tags within 48 characters for network sync.
unlockobjectalwaysPlayer-preset rule metadata; currently not enforced by the preset gallery. NPC presets gate individual outfits.
fixed_genderbooleanfalseNPC preset only; forces the preset gender when an NPC profile is assigned. Unique NPCs also impose their own fixed gender.
trade_professionidentifierabsentNPC catalogue key; custom jobs and vanilla professions share Trade overrides. Invalid identifier syntax rejects the preset.
trade_accessenum(public, trusted)trustedNPC shop access: requires Trust unless explicitly public. Invalid values reject the preset; export preserves this policy.
outfitsarray<object>fallback default outfitNPC preset only. Missing or empty gives an always-available default outfit with no overlay or bonuses.

A player preset can load as an empty object, but it then contains the default profile with no explicit parts. Applying the whole preset replaces build, face, palette and parts, changes gender, and preserves the player’s age, character name and origin. Applying selected part kinds overlays only those slots; body selection also copies build, face selection copies face, and every partial application copies the entire palette.

For NPCs, the presence of any profile field marks the preset authored, even an empty parts object. Base NPCs use that authored profile. Citizens override initial profile resolution with generated appearances; their presets chiefly supply wardrobes. See NPC assets for generation and entity wiring.

Body shape

FieldTypeDefaultWhat it does
build.heightnumber1.81.20–2.05. Height in blocks.
build.torso_widthnumber10.85–1.20. Torso width multiplier.
build.arm_lengthnumber10.90–1.10. Arm length multiplier.
build.arm_thicknessnumber10.85–1.15. Arm thickness multiplier.
build.leg_lengthnumber10.90–1.10. Leg length multiplier.
build.leg_thicknessnumber10.85–1.15. Leg thickness multiplier.
build.head_scalenumber10.90–1.10. Head scale multiplier.
build.chest_sizenumber10–2. Chest scale; zero is valid.
build.chest_liftnumber0-1–1. Signed chest adjustment.
build.chest_spreadnumber0-1–1. Signed chest spread adjustment.

Height and all multipliers except chest_size require a positive finite value: zero, negative and nonfinite values reset to the default; other values clamp to the stated range. chest_size, chest_lift and chest_spread clamp finite values, with nonfinite values resetting to their defaults. Wrong JSON types can fail the whole file.

Face shape

FieldTypeDefaultWhat it does
face.eye_spacingnumber0-1–1. Horizontal eye spacing.
face.eye_heightnumber0-1–1. Vertical eye offset.
face.eye_depthnumber0-1–1. Eye depth offset.
face.eye_scalenumber10.82–1.24. Eye scale multiplier.
face.eye_tiltnumber0-1–1. Signed eye tilt.

Signed face values clamp to -1–1 and nonfinite values become 0. Eye scale requires a positive finite value; otherwise it becomes 1. These values adjust the shared rig, so a part must use the matching bones to show the adjustment.

Palette

FieldTypeDefaultWhat it does
palette.skinRGB#FFFFFFTint for skin.
palette.eye_leftRGB#4F8FC0Tint for eye_left.
palette.eye_rightRGB#4F8FC0Tint for eye_right.
palette.eye_scleraRGB#F6F3EETint for eye_sclera.
palette.hair_baseRGB#2B2B33Tint for hair_base.
palette.hair_tipRGB#3A3A46Tint for hair_tip.
palette.eyebrowRGB#2B2B33Tint for eyebrow.
palette.lipRGB#C97A78Tint for lip.
palette.blushRGB#E39C96Tint for blush.
palette.trait_primaryRGB#FFFFFFTint for trait_primary.
palette.trait_secondaryRGB#CCCCCCTint for trait_secondary.
palette.outfit_primaryRGB#FFFFFFTint for outfit_primary.
palette.outfit_secondaryRGB#FFFFFFTint for outfit_secondary.
palette.outfit_accentRGB#FFFFFFTint for outfit_accent.
palette.underwear_primaryRGB#FFFFFFTint for underwear_primary.
palette.underwear_secondaryRGB#FFFFFFTint for underwear_secondary.
palette.accessory_primaryRGB#FFFFFFTint for accessory_primary.
palette.accessory_secondaryRGB#CCCCCCTint for accessory_secondary.
palette.accessory_accentRGB#FFFFFFTint for accessory_accent.

Colours accept integers or hexadecimal strings with optional # or 0x; even an unprefixed string is hexadecimal. Only the low 24 bits are kept; alpha is discarded. A missing channel keeps the table default, while explicit null or invalid hexadecimal text becomes #FFFFFF. Arrays or objects in a colour field can fail parsing.

Part slots and overlays

KindSlots
bodybody
faceface, eyes, eyebrows, facial_hair
traittrait_ear, trait_horn, trait_tail, trait_wing, trait_other
hairhair
underwearunderwear_top, underwear_bottom
outfitsocks, outfit_top, outfit_bottom, gloves, shoes
accessoryear_accessory, face_accessory, neck_accessory, head_accessory, back_accessory

Slot names are trimmed and case-insensitive. Unknown slots are ignored. A reference can be an identifier string or an object with part and optional style. Preset JSON does not import custom paint data.

In the base profile, null, a blank string, none, or an invalid part ID leaves that slot empty. In an outfit overlay, an absent slot, null or an invalid ID inherits the base; the explicit string none clears the slot. This applies to every slot, including body. Changing outfits restores whatever the underlying profile supplies.

Some parts are chained: one garment spread across several slots, such as the default male briefs isekaiexpansion:underwear_top/default_male and isekaiexpansion:underwear_bottom/default_male. An outfit that names one member wears the whole chain, and none on one member clears all of it. A player preset that names only one member receives the rest when the profile is validated, provided their slots are empty; otherwise the chain is dropped. See part chains.

A valid identifier does not prove a part, model or texture exists. An unavailable style falls back only to default; if that is unavailable too, the renderer skips the part.

NPC outfits

FieldTypeDefaultWhat it does
outfits[].idstringrequiredTrimmed lowercase ID, nonblank; keep within 128 characters for the wardrobe packet.
outfits[].categoryenumoutfitoutfit, hair, accessory; unknown becomes outfit. UI grouping only.
outfits[].defaultbooleanfalseForces always unlock; only the first declared default keeps the flag.
outfits[].unlockobjectalwaysSame rule fields below. Declare granted explicitly for a rewarded outfit.
outfits[].sortinteger1000Wardrobe sorts by category, then this value, then ID. Does not select the initial outfit.
outfits[].partsobject{}Slot overlay; missing inherits the base, none clears it. Can replace body too.
outfits[].attributesarray<object>[]Temporary attribute modifiers while equipped.
outfits[].attributes[].attributeidentifierrequiredRegistered attribute; malformed fails the preset, unknown registry entry logs and skips this bonus.
outfits[].attributes[].valuenumber0Modifier amount; no loader range or finite-value check.
outfits[].attributes[].operationenumadditionaddition, multiply_base, multiply_total; trimmed and case-insensitive, unknown becomes addition.

The initial outfit is the first retained default, otherwise the first always outfit, otherwise the first outfit whose rule is not locked, otherwise the first entry. This fallback does not evaluate a particular player’s progress. An empty list supplies the fallback default outfit.

Repeated IDs keep the last definition but retain the first insertion position. Only the first declared default keeps its flag; later declared defaults have already been made always before their flags are removed. Use unique IDs and a single explicit default to avoid these interactions.

Attribute bonuses require the NPC to expose the target attribute. Missing attributes are skipped with a warning. Use each attribute only once in an outfit: the modifier identity includes preset, outfit and attribute, but not operation, so repeated entries are not independent stackable bonuses. Lowering maximum health clamps current health to the new maximum.

Unlock rules

The following keys live inside player unlock or NPC outfits[].unlock:

FieldTypeDefaultWhat it does
typeenumalwaysalways, level, advancement, progression, granted, locked; unknown means always.
ididentifierabsentAdvancement ID or progression graph ID; invalid syntax becomes absent and those gates fail.
nodestring""Progression node; trimmed, must be nonempty for that gate. Network limit 64 characters.
levelinteger0Minimum adventurer level; negative becomes 0. Only used by level.
RuleSelection condition
alwaysAlways available. Also the JSON default when the rule is omitted.
levelPlayer’s adventurer level from IsekaiCore meets level; a failed level lookup uses 0.
advancementThe server knows the advancement and that player has completed it.
progressionThat player has unlocked node in graph id.
grantedNPC outfit: the player’s wardrobe grant exists. Generic player preset/part checks have no grant source and return false.
lockedUnavailable for player selection; does not prevent direct scripted NPC outfit assignment.

Omitting an NPC outfit’s rule means always, not granted. An outfit marked default: true also forces always, regardless of its supplied rule.

The current player preset gallery does not evaluate a preset’s unlock rule: all loaded presets get selectable cards. Submission contains a profile rather than a preset ID, and the server validates the resulting parts individually. A preset rule therefore does not restrict access to that template; part rules and NPC outfit rules have their own checks.

Validation

Unknown keys are ignored. A malformed field that throws during conversion rejects the entire preset, including all of its outfits. The loader logs that file and replaces the catalogue with the successful files; a failed definition does not retain its previous version. Missing optional fields use the defaults above. Identifier syntax is checked separately from whether the referenced content exists.

Player-preset tags longer than 48 characters, unlock nodes longer than 64 characters and outfit IDs longer than 128 characters can load but exceed their network writers. These bounds are not enforced by the JSON loaders.

The current NPC exporter omits explicit granted unlock rules, while the reader treats omission as always. Restore that rule after exporting a rewarded outfit before using the output in a pack. Profile export also omits paint, writes nondefault shape values rounded to 3 decimal places and emits colours as hexadecimal RGB.

Reloading

After a successful server data-pack reload, online players receive the part catalogue and player preset gallery, their profiles are revalidated, and loaded NPCs refresh from their presets. This is separate from reloading client models and textures.

Player presets are templates: changing one does not reapply it to every player who previously selected it. Revalidation checks their saved profiles against the current part catalogue and unlocks. Authored NPCs reread their initial profiles; generated citizens still run their generation override. Removed outfit IDs fall back to the current default selection.

There is a current attribute-cleanup limitation: refresh removes modifiers described by the new outfit catalogue. Deleting an old outfit or attribute declaration can therefore leave its temporary modifier on an already loaded NPC. Verify existing NPC attributes as well as newly spawned NPCs when editing these definitions.

NPC asset authoring, NPC runtime and Identity.