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:
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 stringRange Syntax
min - max (space-hyphen-space, regex ^-?\d+(\.\d+)?\s*-\s*-?\d+(\.\d+)?$):
| Syntax | Behavior |
|---|---|
15 - 25 | Rolls a value in [15,25); if integer, outputs 20 |
1.5 - 2.5 | Rolls in [1.5,2.5), outputs a float with .0 |
-10 - 10 | Negative 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).
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'slore, andaction_lorevalues.
4. Roll → Resolve → Render → Conflict Pipeline
Variables pass through four stages:
| Stage | Class | What it does |
|---|---|---|
| Roll | VariableRoller | At item creation, rolls ranges into concrete values and stores them in the instance data values |
| Resolve | VariableResolver + ConflictResolver | Merges multiple sources into the final value |
| Render | VariableRenderer | Substitutes {variable} into text |
| Trace | VariableTrace | Records the source history of each variable, for debugging |
The Four Resolution Sources and Priorities
| Source | Priority | Content |
|---|---|---|
| TEMPLATE | 10 | definition.variables (template declaration) |
| INSTANCE | 20 | Instance values (values rolled at creation) |
| LAYER | 30 | Variables from layer patches |
| RUNTIME_OVERRIDE | 40 | Instance 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):
- Fragments (in
fragments:list order) - The
override.variablessection - The root-level
variablessection (highest priority) - Auto-injection: if
tieris set, thenvariables["tier"] = <lowercase tier>
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}" # → 156. Reserved / Automatic Variables
| Variable | Source |
|---|---|
tier | Auto-injected from the tier: field (lowercase) |
star | Star level; rendered as [+N] at the end of the name (some types) |
soulbound.owner | Set on soulbinding, see Soulbinding |
soulbound.level | Binding level |
7. Developer API
Via the facade QinhItemsAPI.variables() (see API Reference):
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 displayWrite result InstanceWriteResult: OK / NOT_QINH_ITEM / LOCKED_BY_OTHER / DOMAIN_VIOLATION.
Tracing Example
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
QinhItemsAPI.variables().lock(item, "damage", owner = "my_plugin")
// Afterward, when another owner calls set, it gets InstanceWriteResult.LOCKED_BY_OTHER8. Storage Format
Variable instance data is stored as a YAML string in the item's NBT (key qinhitems:instance):
values:
damage: "20"
tier_name: "传说"
overrides:
damage: "30"
override_owners:
damage: "my_plugin"
locks:
damage: "my_plugin"
seed: 12345678The seed guarantees reproducibility: the same seed rolls the same random values.
Next Steps
- Affixes / Sections: use pooled text for prefixes and suffixes
- Fragments & Templates: reuse variable declarations
- API Reference: full signatures of the variables facade