Skip to content

Variable Engine

Belongs to: Server Owner Guide · Related: Quality & Display · Fragments & Templates

Variables are item-level dynamic values: random values, quality words, star levels, dynamic text, etc. Similar to MMOItems' placeholders / RNG attributes.


1. What Variables Can Do

  • Roll a random damage value per weapon instance.
  • Quality prefix / suffix text that changes with the item.
  • Insert dynamic numbers, quality names, and descriptions into the Lore.
  • Display alongside star levels, enhancement levels, etc.

2. Declaring Variables

Declare in the item's (or fragment's) variables: section. The key is the variable name, the value is a range or static string:

yaml
random_blade:
  material: iron_sword
  display_name: "<gold>{tier_name} 之刃</gold>"
  lore:
    - "基础伤害:{min_damage} - {max_damage}"
    - "暴击几率:{crit_pct}%"
  variables:
    min_damage: "10 - 15"      # Range → rolled randomly per instance
    max_damage: "25 - 35"      # Range
    crit_pct: "5 - 15"         # Range
    tier_name: "传说"          # Static string

Range Syntax

min - max (space-hyphen-space, regex ^-?\d+(\.\d+)?\s*-\s*-?\d+(\.\d+)?$):

SyntaxBehavior
15 - 25Rolls a value in [15,25); if integer, outputs 20
1.5 - 2.5Rolls in [1.5,2.5), outputs a float with .0
-10 - 10Negative numbers allowed

If min >= max, returns min directly (no roll).


3. Placeholder Syntax

Use {variableName} to reference a variable in text. Allowed characters: alphanumerics, underscores, hyphens ([a-zA-Z0-9_\-], no spaces).

yaml
display_name: "<gold>{name}</gold>"
lore:
  - "伤害:<red>{damage}</red>"
  - "类型:{type}"
  - "{description}"
  • Variables that aren't found are kept verbatim ({missing}{missing}), without raising an error.
  • Usable in: lore, display_name, an action's lore, and action_lore values.

4. Roll → Resolve → Render → Conflict Pipeline

Variables pass through four stages:

StageClassWhat it does
RollVariableRollerAt item creation, rolls ranges into concrete values and stores them in the instance data values
ResolveVariableResolver + ConflictResolverMerges multiple sources into the final value
RenderVariableRendererSubstitutes {variable} into text
TraceVariableTraceRecords the source history of each variable, for debugging

The Four Resolution Sources and Priorities

SourcePriorityContent
TEMPLATE10definition.variables (template declaration)
INSTANCE20Instance values (values rolled at creation)
LAYER30Variables from layer patches
RUNTIME_OVERRIDE40Instance overrides (set at runtime via API)

Higher priority wins (RUNTIME > LAYER > INSTANCE > TEMPLATE). When different owners conflict, a VariableConflict is recorded.


5. Variable Merging (Fragments / Overrides)

Variables stack in the following order (later overrides earlier):

  1. Fragments (in fragments: list order)
  2. The override.variables section
  3. The root-level variables section (highest priority)
  4. Auto-injection: if tier is set, then variables["tier"] = <lowercase tier>
yaml
dragon_plate:
  fragments:
    - armor_base                 # armor_type=Light in the fragment
  override:
    variables:
      armor_type: "Heavy"        # Overrides the fragment
  variables:
    base_defense: "15"           # Highest priority
  lore:
    - "类型:{armor_type}"        # → Heavy
    - "防御:{base_defense}"      # → 15

6. Reserved / Automatic Variables

VariableSource
tierAuto-injected from the tier: field (lowercase)
starStar level; rendered as [+N] at the end of the name (some types)
soulbound.ownerSet on soulbinding, see Soulbinding
soulbound.levelBinding level

7. Developer API

Via the facade QinhItemsAPI.variables() (see API Reference):

kotlin
val api = QinhItemsAPI.variables()

api.get(item): Map<String,String>                    // Get resolved variables
api.explain(item): Map<String, VariableTrace.Entry>  // Source tracing (debug)
api.set(item, key, value, owner): Pair<ItemStack?, InstanceWriteResult>
api.setAll(item, values, owner): Pair<ItemStack?, InstanceWriteResult>
api.lock(item, key, owner): Boolean                  // Lock a variable to prevent others changing it
api.unlock(item, key, owner): Boolean
api.refresh(item): ItemStack?                        // Re-render the display

Write result InstanceWriteResult: OK / NOT_QINH_ITEM / LOCKED_BY_OTHER / DOMAIN_VIOLATION.

Tracing Example

kotlin
val trace = QinhItemsAPI.variables().explain(item)
trace["damage"]?.let {
    it.value           // Final value
    it.source          // TEMPLATE / INSTANCE / LAYER / RUNTIME_OVERRIDE
    it.owner           // Who wrote it
    it.formatHistory() // "template=5 → instance=10 → runtime=15"
}

Locking

kotlin
QinhItemsAPI.variables().lock(item, "damage", owner = "my_plugin")
// Afterward, when another owner calls set, it gets InstanceWriteResult.LOCKED_BY_OTHER

8. Storage Format

Variable instance data is stored as a YAML string in the item's NBT (key qinhitems:instance):

yaml
values:
  damage: "20"
  tier_name: "传说"
overrides:
  damage: "30"
override_owners:
  damage: "my_plugin"
locks:
  damage: "my_plugin"
seed: 12345678

The seed guarantees reproducibility: the same seed rolls the same random values.


Next Steps