From 8beeea55ffee6757a4a63a4561dfd35e01f6ee3b Mon Sep 17 00:00:00 2001 From: Ludovic Marie <136255825+ludovic111@users.noreply.github.com> Date: Mon, 19 Oct 2026 08:34:35 +0200 Subject: [PATCH] Carry Formations' structures Its formations:simple becomes the game's horizon:formations (its pool and placement), and its single pool element vanilla's with its ground_level under horizon:ground_level. Its pieced structure (one) stays out; its processors are still dropped. Co-Authored-By: Claude Opus 5.5 --- crates/horizon-port-pack/src/worldgen.rs | 54 ++++++++++++++++++++++-- docs/modpacks.md | 13 +++++- 2 files changed, 62 insertions(+), 5 deletions(-) diff --git a/crates/horizon-port-pack/src/worldgen.rs b/crates/horizon-port-pack/src/worldgen.rs index 7b8f36b65..5544b2047 100644 --- a/crates/horizon-port-pack/src/worldgen.rs +++ b/crates/horizon-port-pack/src/worldgen.rs @@ -507,6 +507,12 @@ fn upgrade_types( v["type"] = "minecraft:jigsaw".into(); true } + // Formations' SimpleStructure: its pool and placement, laid out + // by the game (`horizon_world::worldgen::structure::formations`). + Some("formations:simple") => { + v["type"] = "horizon:formations".into(); + true + } Some(t) if !t.starts_with("minecraft:") => { unsupported(t); false @@ -565,6 +571,15 @@ fn upgrade_types( } else if t == "yungsapi:yung_single_element" { yung_element(element, lost); changed = true; + } else if t == "formations:single_pool_element" { + // Vanilla's element with its own ground level (1 when + // missing, as its codec reads it). + element["element_type"] = "minecraft:single_pool_element".into(); + if let Some(o) = element.as_object_mut() { + let ground = o.remove("ground_level").unwrap_or(1.into()); + o.insert("horizon:ground_level".into(), ground); + } + changed = true; } else if !t.starts_with("minecraft:") { unsupported(t); } @@ -1204,14 +1219,47 @@ mod tests { const NO_TAGS: BTreeMap> = BTreeMap::new(); + #[test] + fn formations_become_the_games() { + let mut report = Upgrade::default(); + let mut s = json!({"type": "formations:simple", "template": "f:campsites", + "placement": "surface", "biomes": "#f:x", "step": "surface_structures", + "terrain_adaptation": "beard_thin", "spawn_overrides": {}}); + assert!(upgrade("structure", &mut s, &mut report, &NO_TAGS)); + assert_eq!(s["type"], "horizon:formations"); + assert_eq!(s["placement"], "surface"); + let mut pool = json!({"elements": [ + {"weight": 1, "element": {"element_type": "formations:single_pool_element", + "location": "f:a", "projection": "rigid", "ground_level": 6, "processors": []}}, + {"weight": 1, "element": {"element_type": "formations:single_pool_element", + "location": "f:b", "projection": "rigid", "processors": []}}]}); + assert!(upgrade("template_pool", &mut pool, &mut report, &NO_TAGS)); + let e = |i: usize| &pool["elements"][i]["element"]; + assert_eq!(e(0)["element_type"], "minecraft:single_pool_element"); + assert_eq!( + ( + e(0)["horizon:ground_level"].clone(), + e(1)["horizon:ground_level"].clone() + ), + (json!(6), json!(1)) + ); + assert!(e(0).get("ground_level").is_none()); + // The pieced structure stays out. + let mut pieced = json!({"type": "formations:pieced", "start_pool": "f:p", "max_depth": 2, + "max_distance_from_center": 40, "placement": "surface", "biomes": "#f:x"}); + assert!(!upgrade("structure", &mut pieced, &mut report, &NO_TAGS)); + assert_eq!(report.unsupported.get("formations:pieced"), Some(&1)); + assert_eq!(report.unsupported.len(), 1); + } + #[test] fn maps_library_types_to_vanillas() { let mut report = Upgrade::default(); let mut s = json!({"type": "lithostitched:jigsaw", "start_pool": "a:b"}); assert!(upgrade("structure", &mut s, &mut report, &NO_TAGS)); assert_eq!(s["type"], "minecraft:jigsaw"); - let mut formation = json!({"type": "formations:simple"}); - assert!(!upgrade("structure", &mut formation, &mut report, &NO_TAGS)); + let mut other = json!({"type": "somelib:structure"}); + assert!(!upgrade("structure", &mut other, &mut report, &NO_TAGS)); let mut set = json!({"placement": {"type": "moogs_structures:advanced_random_spread", "spacing": 10, "separation": 5, "min_distance_from_world_origin": 250}}); assert!(upgrade("structure_set", &mut set, &mut report, &NO_TAGS)); @@ -1255,8 +1303,8 @@ mod tests { assert_eq!( report.unsupported, BTreeMap::from([ - ("formations:simple".into(), 1), ("somelib:element".into(), 1), + ("somelib:structure".into(), 1), ]) ); } diff --git a/docs/modpacks.md b/docs/modpacks.md index 814950597..a11284634 100644 --- a/docs/modpacks.md +++ b/docs/modpacks.md @@ -548,14 +548,23 @@ Two cases the oracle fills in: its mod id `t_and_t`, and a mod's content may only use its own namespace (docs/mods.md), so carry leaves them out and its 60 structures are missing. - Still lost, written in Java: - - Formations' structure and element types (179): their placements read block states in the - noise column (solid, water, lava), which the structure context doesn't give yet; + - Formations' pieced structure (1, its one jigsaw-like type); - Repurposed's mineshafts, mansions and monuments, Better Mineshafts and two Better Dungeons types: Java structure types; - the block processors written in Java (YUNG's banners, legs and decorations, Repurposed's pillars and fluid fixes, Lithostitched's block swaps); - MO Structures (38; its library is only on CurseForge). + - **Formations** (Overworld and Nether: 50 structures, 128 pool elements) places one + template per start at a height found in the noise columns under its corners and centre: on + the surface, on water or lava, or in a cave (on its floor, under its ceiling, buried in + stone, on a lava lake). Carry writes its `formations:simple` as the game's + `horizon:formations` (`horizon-world`'s `structure/formations.rs`, which reads the blocks + of ChunkGenerator.getBaseColumn through the structure context) and its pool element as + vanilla's with its `ground_level` under `horizon:ground_level`. In chunks -64..63 that adds + 39 Overworld and 672 Nether starts at seed 1234 (51 and 643 at seed 2), and leaves every + other start as it was. Its processors (biome replacements, aged blocks, bed colours, + plants) are still dropped, so its structures keep their templates' blocks. With every structure PR above, the same square (seed 1234, chunks -64..63) holds 362 overworld starts with 22,026 pieces. CTOV has 7, Better Dungeons 72 and Better Ocean