Loot System (loottables)
In: Server Owner Guide · Related: Realm & Keystone · Affix System · Blueprints & Objectives · Placeholders · Command Reference
QR's output is driven by loot tables (loottables/*.yml), delivered to players through three dispense channels:
| Channel | Trigger | Who shares it | Growth scaling | See |
|---|---|---|---|---|
| ① Container loot | Place a vanilla chest/barrel/furnace… in the structure, right-click to open | Per-player / server-shared | Yes (when per-player) | §3 |
| ② Blueprint reward chest | Right-click the chest block after a clear unlocks it | Per-player | Yes | §4 |
| ③ Cleanse slot machine | Spin via /qr reward after a realm is cleansed | Privately per participant | Yes | §5 |
🔑 One table can serve multiple channels at once — it's just a recipe of "what to roll, how many times to roll"; the template/blueprint decides who references it.
🖼️ [Image placeholder] Overview diagram of the three dispense flows (table → container/reward chest/slot machine → player) · suggested
assets/loot-overview.png
1. Where Loot Tables Live and How They Load
Every table is a .yml file under plugins/QinhRuins/loottables/, and the file name (minus .yml) is the table name. For example, loottables/vault.yml's table name is vault; reference it in a template by writing loot.container-table: vault.
QR ships three example tables:
| File | Table name | Typical use |
|---|---|---|
default.yml | default | Fallback table for editor-marked chests (guards against "reward table missing") |
vault.yml | vault | Container loot + conditional group demo |
realm.yml | realm | Realm cleanse exclusive jackpot (referenced by default in config.yml) |
Run /qr reload after editing to apply.
2. The Table Structure in Full
A complete loot table can use the following fields (all optional except entries):
# loottables/vault.yml
rolls: 2 # how many times the main entries are rolled (each roll picks one entry by weight)
containers: [CHEST, BARREL] # only takes effect as a container table: limits which container types use this table (empty=all)
# vanilla: minecraft:chests/simple_dungeon # optional: roll a vanilla loot table first, then layer the entries below on top
entries: # main entry pool
- { item: "vanilla:DIAMOND", weight: 10, amount: "1-3" }
- { item: "vanilla:GOLD_INGOT", weight: 20, amount: "2-5" }
- { item: "vanilla:EXPERIENCE_BOTTLE", weight: 15, amount: "2-6" }
groups: # conditional groups: only join the roll if condition is met
rich:
condition: "%player_level% >= 5" # via PlaceholderAPI; if PAPI isn't installed, this condition is always true
rolls: 1
entries:
- { item: "vanilla:EMERALD", weight: 1, amount: 2 }2.1 rolls — How Many Times to Roll
rolls decides how many times this table "rolls"; each roll picks one entry at random by weight from the entry pool. rolls: 2 = roughly 2 items output (the same entry may be picked more than once). Minimum is 1.
The cleanse slot machine channel ignores rolls — it flattens the whole table (including groups) into one prize pool and spins out only one as the jackpot (see §5).
2.2 entries — The Entry Pool
Each entry is a single { ... } line, with these fields:
| Field | Required | Description |
|---|---|---|
item | ✅ | Item source string, see §2.6 |
weight | Weight (larger = more frequent); defaults to 1 if omitted, minimum 1 | |
amount | Quantity; a fixed value 2 or a range "2-5" (random within the range each time); defaults to 1 if omitted | |
min-growth | Growth threshold: if the player's growth is below this value, the entry doesn't join this roll, see §6.2 | |
unique | When true, weighted by the realm "Hidden Trove" affix (drop rate ×multiplier), see §6.4 |
entries:
- item: "vanilla:NETHERITE_SCRAP"
weight: 8
amount: "1-2"
min-growth: 30 # only droppable at growth ≥30
unique: true # an exclusive goodie weighted by the "Hidden Trove" affixTip
A reversed amount range (e.g. "5-2") is auto-corrected to 2-5, no need to worry.
2.3 groups — Conditional Groups
groups are extra entry pools that "only open if a condition is met". Each group has its own condition, rolls, entries. The main entries always roll; a group rolls an extra round only when its condition is true.
groups:
rich: # group name is arbitrary (identifier only)
condition: "%player_level% >= 5" # see the condition rules below
rolls: 1 # how many times this group rolls on its own
entries:
- { item: "vanilla:EMERALD", weight: 1, amount: 2 }
vip:
condition: "%vault_eco_balance% >= 10000"
rolls: 1
entries:
- { item: "qi-legendary_chest", weight: 1 }condition rules (same line of thinking as realm affix scripts / QS conditions):
- Written as
left-value comparator right-value, with both sides first resolved through PlaceholderAPI before comparison. - Supported comparators:
>=<===!=><. - If both sides can be turned into numbers → numeric comparison; otherwise string comparison (only
==/!=are meaningful, case-insensitive). - Empty or no parsable comparator = always true — so without PlaceholderAPI installed,
conditionis always treated as passing, and the group is dispensed as usual.
condition: "%player_level% >= 5" # level ≥5
condition: "%player_world% == world" # in the main world (string equality)
condition: "" # always true (equivalent to no condition)2.4 vanilla — Layer in a Vanilla Loot Table
Fill in a vanilla loot table ID and QR will roll a batch of vanilla loot first, then layer the entries / groups above on top. Good for "reuse vanilla dungeon/fortress drops, then add custom goodies":
vanilla: minecraft:chests/simple_dungeon # roll a batch of dungeon-chest vanilla drops first
rolls: 2 # then layer 2 extra rolls of custom entries
entries:
- { item: "qi-rune_shard", weight: 5, amount: "1-2" }Common vanilla table IDs:
minecraft:chests/simple_dungeon(dungeon),minecraft:chests/desert_pyramid(desert temple),minecraft:chests/end_city_treasure(End city), etc.
2.5 containers — Limit Container Types (Container Channel Only)
Only takes effect when used as a container table: declares which container types this table applies to. Empty = all containers within the ruin range use this table; if listed, only these types apply.
containers: [CHEST, BARREL] # only chests and barrels apply; dispensers/hoppers in the ruin don't dropType names are vanilla material names (uppercase):
CHESTTRAPPED_CHESTBARRELFURNACEBLAST_FURNACESMOKERBREWING_STANDDISPENSERDROPPERHOPPERSHULKER_BOX, etc. Combined with the template'sloot.container-tables(per-type tables), you can do "chests drop treasure, brewing stands drop potions"; see §3.3.
2.6 item — Item Source (Any Plugin)
item is parsed by QinhCoreLib's ItemManagerAPI and supports any item source that's been integrated — one line can pull an item from another plugin:
| Prefix syntax | Source |
|---|---|
vanilla:DIAMOND / minecraft:diamond / DIAMOND | Vanilla item |
qi-<id> / qinhitems:<id> | QinhItems |
ni-<id> / neigeitems:<id> | NeigeItems |
mi-<type>:<id> / mmoitems:<type>:<id> | MMOItems |
ia-<id> / itemsadder:<id> | ItemsAdder |
ce-<id> / craftengine:<id> | CraftEngine |
nx-<id> / nexo:<id> | Nexo |
mm-<id> / mythicmobs:<id> | MythicMobs items |
entries:
- { item: "qi-flame_saber", weight: 3 } # QinhItems weapon
- { item: "mi-SWORD:cutlass", weight: 2 } # MMOItems
- { item: "vanilla:DIAMOND", weight: 10, amount: "1-3" }An entry that fails to parse (wrong item ID, the corresponding plugin not installed) is silently skipped — that roll just outputs one fewer item, with no error spam.
3. Channel ① Container Loot
Place vanilla containers (chest/barrel/furnace…) in the structure, and the player right-clicking to open automatically yields loot — no separate marking needed. Whether a container belongs to a ruin is judged automatically by the structure's footprint (the bounding box, the smallest cuboid boundary of the structure) — a container that falls within the structure's bounding box belongs to it, no radius needed.
The master switch and interface are in the vessel section of config.yml:
vessel:
enabled: true # container loot master switch
refresh-mode: per-player-once # per-player-once=once per person | timed=can be reopened to re-roll after a cooldown
refresh-cooldown: 30m # the re-roll cooldown for timed mode (30m / 1h / 2h…)
rows: 3 # the row count of the opened container interface (1-6)The template side specifies which table to use and the mode (in templates/<id>/template.yml):
# template.yml snippet
loot:
mode: per-player # see §3.1
container-table: vault # all containers use the vault table
# container-tables: # or per-container-type tables, see §3.3
# CHEST: vault
# BREWING_STAND: potion_table3.1 loot.mode — Per-Player vs Server-Shared
| Mode | Behavior | Growth | Suits |
|---|---|---|---|
per-player (default) | Each person gets their own batch on first open; the open is recorded and the rest stays unchanged in the interface | ✅ Quantity scales by growth | Multiplayer servers, fair output, everyone gets a share |
shared | Stuffs a batch of loot straight into the real container, server-shared, first-come-first-served | ❌ Neutral dispense (ignores growth, ignores the min-growth threshold) | Want a "grab-the-chest" competitive feel, single-player servers |
Note
Key difference: per-player is QR opening an independent virtual interface for each player (mutually unaffected, auto-saving anything not taken when closed); shared actually fills items into that chest in the world, first to grab wins, and for fairness it does no growth scaling and ignores min-growth.
3.2 refresh-mode — Can It Be Reopened
per-player-once(default): each player can open each container only once; right-clicking again afterward shows the contents left from last time.timed: after therefresh-cooldownpasses, it can be reopened and re-rolled, making a "timed-refresh supply point".
3.3 Per-Container-Type Tables
loot.container-tables lets different container types use different tables; types not listed fall back to loot.container-table:
loot:
mode: per-player
container-table: vault # fallback: any container not separately listed uses vault
container-tables:
CHEST: treasure_table # chests drop treasure
BREWING_STAND: potion_table # brewing stands specifically yield potions (furnaces/brewing stands smartly fill fuel/potion slots)In
sharedmode, for furnaces/blast furnaces/smokers/brewing stands, QR smartly fills drops into the corresponding slots (fuel→fuel slot, potions→bottle slots…); whatever doesn't fit drops above the container.
4. Channel ② Blueprint Reward Chest (loot-chests)
The unlock-style chests in a blueprint: only openable after clearing to a specified stage, once per person. Configured in the loot-chests section of blueprint.yml (coordinates relative to the structure origin); see Blueprints & Objectives for details.
# blueprint.yml snippet (coordinates are x/y/z relative to the structure origin, written directly on the entry, not a pos sub-table)
loot-chests:
- id: vault_main
x: 8
y: 3
z: 8
loot-table: vault # which table to reference
unlock-stage: 2 # only unlocks once advanced to stage 2 (default 1)
per-player-once: true # only openable once per person (default true)
growth-scaled: true # quantity scales by growth (default true)| Field | Default | Behavior |
|---|---|---|
unlock-stage | 1 | Only unlocks when the current effective stage ≥ this value; otherwise shows "chest not yet unlocked" |
per-player-once | true | The open is recorded; reopening shows "you have already opened this chest" |
growth-scaled | true | When true, quantity scales by growth |
The reward chest stuffs loot straight into the inventory (drops on the ground if full) and plays an upgrade sound. It does not go through the
vesselinterface — it's standalone unlock-reward logic.
5. Channel ③ Cleanse Slot Machine (reward)
The exclusive jackpot after a realm is cleansed (cleared), dispensed in the form of a slot machine spin. The player types /qr reward to open the wheel, clicks the lever to spin once, and spins out only one jackpot from the prize pool.
Workflow
- When a realm/ruin is cleansed, QR grants one spin eligibility to each player within 48 blocks nearby and prompts "the clear jackpot is ready".
- The player runs
/qr rewardto open the wheel → clicks the lever → the animation rolls → it locks onto one jackpot, which goes straight into the inventory. - The eligibility is consumed once; those who already collected see "this reward has been collected".
Which Table It Uses
The slot machine uses the template's clear prize-pool table — write reward.clear-table in template.yml (also the usage mentioned in the comments of vault.yml):
# template.yml snippet
reward:
clear-table: vault # the prize-pool table for the cleanse slot machineWhereas the exclusive loot from a Realm-tier cleanse goes through a separate set, the
realm.rewardofconfig.yml(default tablerealm, privately delivered per participant, with stackable per-tier scaling and currency); see Realm & Keystone. Both belong to "cleanse output", but they're configured in different places:
template.yml'sreward.clear-table→/qr rewardslot machineconfig.yml'srealm.reward.loot-table→ realm-tier exclusive private delivery
How the Slot Machine Picks a Prize
The slot machine flattens the table's entries + all condition-satisfying group entries into one prize pool (filtered by the min-growth threshold), spins out one by weight, and scales the quantity by growth. When the pool has no entry suited to that player's growth, it shows "no reward suited to your growth right now".
6. Growth Scaling & Affix Synergy
6.1 What Is Growth
"Growth" is QR's unified readout of a player's strength tier, decided by the GrowthProvider:
- With QinhClass installed → reads the class level;
- otherwise with MMOCore installed → reads the MMOCore level;
- neither installed → falls back to the vanilla experience level.
It's used in two places: the loot's min-growth threshold, and quantity scaling.
6.2 min-growth Threshold
An entry's min-growth is the "admission line": if the player's growth is below it, this entry doesn't join this roll (and doesn't appear in the slot machine prize pool either). Used to lock high-tier goodies and force players to grow first.
Exception:
shared-mode containers ignoremin-growth(neutral dispense, see §3.1).
6.3 Quantity Scales by Growth
When a channel enables growth-scaled (per-player containers, reward chests on by default, the slot machine, realm private delivery), the drop's base quantity is scaled up by growth:
Scaling formula:
quantity = base quantity × (1 + growth / 200), with a floor never below the base quantity. Example: base 4, growth 100 →4 × (1 + 0.5) = 6; growth 200 → 8.
min-growth controls "whether it can drop", growth-scaled controls "how much drops"; together they make high-growth players' output fatter.
6.4 Affix Synergy (greed / Hidden Trove)
Realm-tier output is further amplified by affixes (only on the realm cleanse private-delivery path, see Realm & Keystone):
| Affix class | Acts on | Effect |
|---|---|---|
| Greed / Golden Touch (greed) | Whole-table quantity | Raises the roll count / quantity multiplier |
| Hidden Trove (hidden-trove) | unique: true entries | Multiplies the weight of exclusive entries, making rares easier to spin |
These multipliers are multiplied with config.yml's realm.loot-bonus (per-tier multiplier, default 1.1× at tier 1 → 2.6× at tier 16):
# config.yml snippet
realm:
tiers:
loot-bonus: "1 + tier * 0.1" # amplify quantity by realm tier; write "1" to disable per-tier scalingThat is: final quantity ≈ base × growth scaling × per-tier loot-bonus × greed multiplier, with exclusive entries additionally getting the Hidden Trove weight bonus.
7. Placeholders
Loot/progress-related placeholders (require PlaceholderAPI), usable in HUDs, groups.condition, chat, etc.:
| Placeholder | Meaning |
|---|---|
%qinhruins_ruin% | Name of the current ruin |
%qinhruins_stage% / %qinhruins_max_stage% | Current stage / total stages |
%qinhruins_stage_name% | Current stage name |
%qinhruins_completed% | Whether cleared |
%qinhruins_objective% / %qinhruins_progress% | Current objective / progress |
%qinhruins_kills_<mob>% | Kill count of a certain mob |
For the full list see Placeholders.
8. Quick Recipes by Need
| I want… | How |
|---|---|
| Custom items dropping from chests | Place chests + point loot.container-table at a table with qi- etc. entries |
| Everyone gets a share, no grabbing | loot.mode: per-player |
| A grab-the-chest competitive feel | loot.mode: shared |
| High-tier goodies locked behind level | Add min-growth: 30 to the entry |
| Reuse vanilla dungeon drops then add more | Write vanilla: minecraft:chests/simple_dungeon + custom entries in the table |
| Extra drops for VIP/high-level | groups + condition: "%placeholder% >= N" |
| Clear-jackpot spin | template.yml's reward.clear-table + player /qr reward |
| Brewing stands specifically yield potions | loot.container-tables: { BREWING_STAND: potion_table } |
Next Steps
- Realm & Keystone: realm-tier cleanse exclusive private delivery, per-tier scaling, currency
- Affix System: loot affixes like Greed / Hidden Trove
- Guide & Codex: view each ruin's loot strategy card in the codex
- Command Reference:
/qr reward//qr reload