Skip to content

Placement Profiles (generators/*.yml)

Previous: Trap System (Triggers × Actions) · Next: Natural Generation and Pre-loading (Generation Director / Density / Regen) Related: Ruin Template · Procedural Generation · Built-in Content List

A Placement Profile is a reusable rule pack that answers "where does this ruin generate": dimension / world / biome filtering, generation layer, flatness, minimum spacing, landing-spot checks, probability… One profile is a single file at plugins/QinhRuins/generators/<name>.yml. Multiple templates can share the same profile (generation.profile: surface_overworld), and each template can also locally override any field. The plugin ships 17 built-in profiles, covering all common landscapes across the three dimensions.

Tip

Division of labor in one line: the profile handles "site-selection rules"; the template handles "identity and gameplay." A template references a profile via generation.profile to inherit a full set of defaults, then only writes the fields it wants to change.

🖼️ [Image placeholder] A diagram of "template generation.profile → profile generators/*.yml → landing-spot validation chain" · suggested assets/profile-flow.png


1. Profile vs Template Override: the Inheritance Relationship (key)

A placement profile and a template's generation section use the same set of fields. The load order is:

built-in fallback (DEFAULT) → referenced profile → fields written in the template's generation section
        each layer only overrides "the keys it explicitly writes"; unwritten keys inherit from the layer above
  • The template writes generation.profile: surface_overworld → first the whole surface_overworld profile is taken as the base;
  • any field the template then writes in its generation section (biomes / y / flatness / min-distance-same…) overrides the same-named item in the profile key by key;
  • unwritten keys keep using the profile's values.

Key-by-key override, not whole-section replacement: writing only biomes: [PLAINS] in a template will not wipe out the profile's layer / min-distance-others along with it—it only overrides the single biomes key, the rest stay as is. So "reference a profile + write only two or three override lines" is the recommended style.

Caution

Referencing a non-existent profile: it automatically falls back to the built-in default profile and warns in the console; an unrecognizable y (generation layer) value → falls back to surface and warns.

For the full template-side generation section syntax, see Ruin Template §5. This page focuses solely on all the fields of the profile file itself.


2. Full Field Table

The table below lists all fields of generators/*.yml (the template generation section can write the same ones). Bold ones are the most commonly configured.

YAML KeyTypeDefaultMeaning
enabledBoolfalseWhether the profile is usable (generally write true). The template-side generation.enabled is the real switch for "does this ruin take part in natural generation."
layer (or y)StringsurfaceGeneration layer: surface / underground / sky / sea surface / seabed / fixed Y / relative to surface. See §4.
environmentsListemptyDimension environment: NORMAL (Overworld) / NETHER (Nether) / THE_END (End); empty = no restriction.
worldsListemptyFurther narrow by world name (empty = all worlds in that environment).
biomesListemptyRestrict biomes; empty = no restriction. See §3.
probabilitysection1/1000{ numerator, denominator }, used for each profile's own roll only when the generation director is off. See §5.
priorityInt0Priority: the smaller the number, the higher the priority; at a single candidate spot, only the batch with the highest priority takes part in the weighted draw.
spawn-chanceDouble1.0Absolute rarity: after being drawn, an independent dice roll is made again; only <1.0 is truly rare.
min-distance-othersInt0Minimum spacing (blocks) from any ruin.
min-distance-sameInt0Minimum spacing (blocks) from the same kind of ruin, to prevent clustering of the same type.
flatnesssectionSite flatness check { radius, max-variance, max-errors }. See §6.
whitelist-groundListemptyOnly allow landing on these ground blocks.
blacklist-groundListemptyForbid landing on these ground blocks.
spawn-in-waterBooltruefalse = not allowed to land on water-surface blocks.
spawn-in-lavaBoolfalsetrue = allow landing on lava surfaces (Nether lava-sea themes).
spawn-in-voidBooltruefalse = force landing on solid ground (reject overhanging the feet over air).
heightmapString?noneHeightmap algorithm for the surface layer. See §4.
y-bandsectionRandom depth / height range { min, max } for underground / sky.
y-offsetInt0Vertical fine-tune (the final Y is shifted ±N again; the sea surface auto-aligns to the waterline, so generally leave at 0).
spawn-regionsectionnoneCoordinate-box restriction { min-x, min-z, max-x, max-z, exclude }. See §7.

Note

layer and y are two names for the same field (layer takes precedence). In profile files it's conventional to write layer; in the template generation section it's conventional to write y. The effect is exactly the same.


3. Dimension / World / Biome Filtering

yaml
environments: [NORMAL]     # dimension: NORMAL(Overworld)/NETHER(Nether)/THE_END(End); empty = no restriction
worlds: []                 # further narrow by world name (empty = all worlds in that environment)
biomes:                    # restrict biomes (empty = no restriction)
  - PLAINS
  - FOREST
  • environments recognizes NORMAL / OVERWORLD (both equal the Overworld), NETHER / THE_NETHER, and END / THE_END. To support custom Overworld dimensions, you can also add CUSTOM.
  • worlds narrows further, on top of environments, to specific world names (case-insensitive). Empty = all worlds in that environment are fine.
  • biomes takes the vanilla biome enum names (e.g. PLAINS / DESERT / LUSH_CAVES). The match rule recognizes both the "namespaced key" and the "enum name"; writing or omitting the minecraft: prefix both work; empty = no biome restriction.

The biome decision reads the actual biome of the block at the landing spot. Seabed / underground profiles land underwater / underground, and the biome is still read by that coordinate, so for OCEAN / DRIPSTONE_CAVES and the like you must write the corresponding cave / ocean biomes.


4. Generation Layer and Height (layer / y-band / y-offset / heightmap)

layer (= y) decides which layer the structure lands on. Values:

layer / y valueMeaning
surface (ground / top)Surface. Intelligently skips tree canopy / leaves to land on the real ground.
underground (cave / buried)Underground, buried downward. Pair with y-band to set the depth range (default 8–40).
sky (floating / air)Sky, floating upward. Pair with y-band to set the height range (default 30–70).
ocean-surface (sea-surface / water-surface)Sea surface, auto-aligned to the waterline; if that column has no water, pick another spot.
seabed (ocean-floor / underwater)Seabed (solid sea-floor surface); if there's no water, pick another spot.
Fixed number (e.g. 64)Fixed Y coordinate.
+8 / -58 blocks above / 5 blocks below the surface.
+[3;12] / -[2;6]A random 3–12 blocks above / random 2–6 blocks below the surface.
yaml
layer: underground
y-band: { min: 10, max: 35 }   # random burial depth 10~35 blocks below the surface
# y-offset: 0                  # the final Y is fine-tuned ±N again
# heightmap: world-surface     # surface layer only: where "the surface" is computed
  • y-band: only takes effect for underground / skyunderground is "a random [min,max] blocks below the surface," sky is "a random [min,max] blocks above the surface."
  • y-offset: adds a fixed offset on top of the computed final Y, for an overall fine-tune.
  • heightmap (surface only): controls which heightmap "the surface" is computed against—world-surface / motion-blocking / motion-blocking-no-leaves (= no-leaves) / ocean-floor. Leave empty = intelligently skip tree canopy / leaves / snow / vines and land on the real ground (recommended).

The Nether's "surface" is a special algorithm: the Nether's top is a bedrock ceiling, so QR scans downward from Y≈120, finding the first block where "the block below is solid (non-lava) + the current and above blocks are both air" as the landing spot, automatically avoiding the bedrock ceiling. So a Nether profile can just write layer: surface to land on the terrain's top surface.

Caution

Sea-surface / seabed profiles rely on water to decide the landing spot: if that column has no water below sea level, the spot is abandoned and re-picked. For underwater structures, make sure the schematic comes with a sealed outer shell, otherwise the interior floods.


5. Probability (probability) —— Only When the Generation Director Is Off

yaml
probability: { numerator: 1, denominator: 700 }   # 1/700: each profile rolls by this only when the director is off
  • When the generation director is on: probability is ignored, and all ruins switch to "weighted competitive draw + global density." Each template's generation.weight (or, when left blank, the weight auto-converted from probability) takes part in the draw.
  • When the generation director is off (backward-compatible old logic): it falls back to "each template rolls independently by its own probability"—every candidate spot rolls dice by numerator/denominator to decide whether to generate.

Note

Built-in profiles generally have a denominator of 500–900 (underground caves denser, sky / ocean sparser), a rarity baseline of "on average one per this many candidate spots." Once the generation director is on, these values no longer take effect directly, but when director.weights is left blank they get converted into draw weights, so they still carry relative-rarity meaning. For the generation director see Natural Generation and Pre-loading.


6. Flatness and Landing-Spot Checks

yaml
flatness: { radius: 4, max-variance: 4, max-errors: 0 }
whitelist-ground: [GRASS_BLOCK, DIRT]   # only allow landing on these ground blocks
blacklist-ground: [SAND, WATER]         # forbid landing on these ground blocks
spawn-in-water: true
spawn-in-lava: false
spawn-in-void: true

Flatness flatness: samples a ring of ground heights centered on the landing spot to judge whether the ground is flat enough.

FieldDefaultDescription
radiusDetection radius (blocks). 0 or max-variance:0 = no check (sky / ocean / underground profiles all turn it off).
max-varianceMaximum allowed height difference. A sample point rising more than this above the lowest point counts as one "bump."
max-errors0How many bump sample points are allowed. 0 = none at all (strictest, the ground must be very flat).

Sampling implementation: when radius<=4 it samples every block, larger than that it samples every 2 blocks; it takes the minimum of all sampled heights as the baseline, counts the points "rising above max-variance," and if that exceeds max-errors it judges the ground uneven and abandons the spot. The flatter the ground the stricter; the more bumps the more likely to be rejected.

Landing-spot block checks (check the block directly below the landing spot, y-1):

FieldBehavior
whitelist-groundWhen non-empty, the ground block must be in the whitelist (e.g. the desert profile's [SAND, SANDSTONE, RED_SAND]).
blacklist-groundReject if the ground block is in the blacklist.
spawn-in-waterfalse = reject if the block directly below is water.
spawn-in-lavafalse (default) = reject if the block directly below is lava; true is used for Nether lava-sea themes.
spawn-in-voidfalse = reject if the block directly below is air (overhanging over the void), forcing a solid landing spot.

7. Coordinate-Box Restriction (spawn-region)

Restrict / exclude generation within a rectangular XZ box (e.g. "no ruins in the spawn protection area").

yaml
spawn-region:
  min-x: -2000
  min-z: -2000
  max-x: 2000
  max-z: 2000
  exclude: true        # true = only spawn outside the box (none near spawn); false = only spawn inside the box
FieldDescription
min-x / min-z / max-x / max-zThe rectangular XZ range.
excludefalse (default) = generate only inside the box; true = generate only outside the box.

Tip

Spawn protection: exclude: true + a box enclosing the spawn point, and the newbie zone will never sprout a ruin.


8. The 17 Built-in Profiles List

The plugin ships 17 profiles under generators/, divided by dimension × landscape. A template just references one via generation.profile: <name>.

Overworld (NORMAL)

Profile NameLayerBiome RangeNotes
surface_overworldsurfaceno biome restrictiongeneral surface fallback, the most used
surface_grasslandsurfaceplains / forest / taiga / swamp / jungle / meadow and other vegetated zonesflatness is stricter
surface_desertsurfacedesertwhitelist-ground: [SAND, SANDSTONE, RED_SAND]
surface_snowysurfacesnowy plains / snowy taiga / ice spikes / frozen peaks / grove and other snow-ice zones
surface_barrensurfacebadlands / savanna / mushroom island / stony peaks and other wastelands
underground_overworldundergroundno biome restrictiongeneral crypt / tomb, y-band 10~35
underground_lushundergroundlush cavesy-band 6~24
underground_dripstoneundergrounddripstone cavesy-band 8~30
sky_overworldskyno biome restrictionfloating sky island, y-band 35~75
ocean_overworldocean-surfacevarious ocean biomesshipwreck / floating platform, floats on the sea surface
seabed_overworldseabedvarious ocean biomesunderwater ruin, needs its own sealed outer shell

Nether (NETHER)

Profile NameLayerNotes
surface_nethersurfacesits on the terrain's top surface, already avoids the bedrock ceiling
underground_netherundergroundburied in the netherrack layer, y-band 8~24
sky_netherskyfloats above the lava sea, y-band 18~38 (the top is bedrock, don't set it too high)

End (THE_END)

Profile NameLayerNotes
surface_endsurfacesits on the surface of End islands
underground_endundergroundburied inside End-stone islands, y-band 6~18
sky_endskyfloats above End islands, y-band 25~60

For the complete field values of the built-in profiles, see Built-in Content List.


9. Full Annotated Example

A custom "vegetated-zone surface" profile (can be dropped straight into generators/my_surface.yml):

yaml
# placement profile · custom vegetated-zone surface
enabled: true
layer: surface                 # generation layer: surface
environments: [NORMAL]         # Overworld only
worlds: []                     # no world restriction
biomes: [PLAINS, FOREST, TAIGA]  # restrict biomes
probability: { numerator: 1, denominator: 600 }  # each profile rolls by this only when the director is off
min-distance-others: 80        # ≥80 blocks from any ruin
min-distance-same: 320         # ≥320 blocks from the same kind of ruin
flatness: { radius: 5, max-variance: 3, max-errors: 1 }  # require fairly flat ground
whitelist-ground: []           # no ground-block restriction
blacklist-ground: [WATER, LAVA]
spawn-in-water: false          # don't land on water surface
spawn-in-void: false           # don't overhang the void
y-offset: 0
# spawn-region:                # optional: spawn protection
#   min-x: -1500
#   min-z: -1500
#   max-x: 1500
#   max-z: 1500
#   exclude: true              # only spawn outside this box

Reference it in a template, overriding only two lines:

yaml
# template.yml fragment
generation:
  profile: my_surface          # reference the profile above
  enabled: true
  weight: 20                   # draw weight
  biomes: [PLAINS]             # override only biomes: narrow to plains only (other fields inherit from the profile)

🖼️ [Image placeholder] A comparison of the same profile referenced by three different ruin templates, each overriding only a few fields · suggested assets/profile-shared.png


10. After You're Done

  • Changed generators/*.yml or a template's generation section → /qr reload.
  • Want to confirm "can a template generate under my feet, and where does it get stuck" → /qr why <template> (lists the pass / fail of each check one by one).
  • Want to see the generation director's actual hit rate nearby → /qr gentest.
  • Want to immediately blanket an area within a radius to see the effect → /qr scatter <radius> <amount>.

For details see Natural Generation and Pre-loading and Diagnostics and Troubleshooting.


Next Steps