Implement bamboo stalks, leafy caps and podzol patches

This commit is contained in:
Ludovic Marie
2026-10-06 13:17:56 +02:00
parent 4cc0eaf6da
commit 11acaa1617
9 changed files with 462 additions and 2 deletions
+4
View File
@@ -151,6 +151,10 @@ jobs:
env:
RUSTUP_TOOLCHAIN: stable
run: python tools/test_replace_blobs_parity.py
- name: Compare bamboo stalks, podzol patches and leafy caps
env:
RUSTUP_TOOLCHAIN: stable
run: python tools/test_bamboo_parity.py
- name: Compare ordered single-block replacement features
env:
RUSTUP_TOOLCHAIN: stable
@@ -0,0 +1,214 @@
{
"schema": 1,
"minecraft": "26.3",
"manifest_sha1": "4fe1aa1ef8da1cb95c5bad1fb98890ca56dd8ca3",
"platform": "Linux-7.0.0-31-generic-x86_64-with-glibc2.43",
"java": "openjdk 25.0.4.1 2026-08-18",
"rustc": "rustc 1.99.0 (b940084d7 2026-09-28)",
"fixtures": 144452,
"candidate_states": 35723,
"double_plant_blocks": 10,
"mossy_carpet_blocks": 1,
"sources_sha256": {
"mods/horizon/src/test/java/dev/horizonlab/parity/VanillaSimpleBlockOracle.java": "311c073113f67aaa85e492ea0ec61cd57e40e5f035ec45331759318ada35892e",
"mods/horizon/src/test/java/dev/horizonlab/parity/VanillaPlacementOracle.java": "12b1c46cc397755ef6827da777faef3e71c008cafa48403669c03ca2bdc3c7cb",
"mods/horizon/src/test/java/dev/horizonlab/parity/VanillaBlockSurvivalOracle.java": "4e7cc151617c1b02e872f9ca3e9fe4632379f225f4f07718ff6492df4ee551e7",
"mods/horizon/src/test/java/dev/horizonlab/parity/VanillaBlockPropertiesOracle.java": "e871d3a01f15616a98ca9227c27a81e4d17e2d57ac4e68a5650a70fa29811497",
"mods/horizon/src/test/java/dev/horizonlab/parity/VanillaBlockSupportOracle.java": "04350901a2d708fb8fa672de61d058f73f332d2e90df1b93a67781862f89c1ad",
"mods/horizon/src/test/java/dev/horizonlab/parity/VanillaBlockStateCodecOracle.java": "5c217a8cb72094020082bd4846ed675a6399b9fa371be95a221bd92d227b2e09",
"mods/horizon/src/test/java/dev/horizonlab/parity/VanillaCollisionOracle.java": "60ff9ef7c1d5e23c080f9aec95ab2e7d3b7c5b5bcd67b9efa9518776c400629a",
"mods/horizon/src/test/java/dev/horizonlab/parity/VanillaHeightmapOracle.java": "145c943637ed177cc4ef8a85f2484da8afd164868e84a8463882feec1653a860",
"tools/test_simple_block_parity.py": "50b783323051fe8cd369bb733448b0314f1e6e7994a950e4cb6cd2fe4f7f8e11",
"tools/test_bamboo_parity.py": "9fb99189bb3264cbd28c94f3a7dd5430acbccafb9b49e9ecf1d712d86b82d005",
"tools/test_block_survival_parity.py": "9e8dff3bd4b95dbfd29f8adc6a2f6d66abf19a6b1cd91423b794a345d4ec244a",
"tools/test_worldgen_parity.py": "b40972ab2ad222aa6d77537cc36b434a217baa1f1b56bbaad945d6e12fe776af",
"tools/test_rust_parity.py": "29d3db08005873528894b45ca10dcd6278753650473b3eb574e53e06ede40ec6",
"tools/lab.py": "d00bf107fa01b6c579734a082c3ca4ade3018f0d6851d46844acf17574f66e64",
"Cargo.toml": "dc370d5fdde37238c3e4820046f11c3201020406a4edfa1308257c13edbdcda4",
"Cargo.lock": "7307d0c887364db36d327685ecf0671b75305e31e5ba5fbdcdbb15eaf156d20e",
"crates/horizon-core/Cargo.toml": "e21b502c949da93cf67826592459b687a8cbf737ef92fee514e3ed2cc7cd27ad",
"crates/horizon-world/Cargo.toml": "977338bf3e654193685b015d383493b9f59ff6f5d6210bec212cef8ac4a50bed",
"crates/horizon-core/src/bin/density_parity.rs": "e5b1dd33b7d01306c73cf072629b4fb186e9ab87b672a00108999d1a72263fd0",
"crates/horizon-core/src/bin/float_log_parity.rs": "4a655fcf8822ee7668c4bd126bc84d0b118864a6fc7f5a6aeee667d8bf2590b1",
"crates/horizon-core/src/bin/geometry_parity.rs": "27f6c09fe5018ac3de8e70e0f0bbf78f9abb14f5e4783bcaf46e8ca87b9c6f8a",
"crates/horizon-core/src/bin/noise_parity.rs": "c135bfc708d853e7061e81487d2cfe10de64b2f65677d3b4837cbfe0b8341010",
"crates/horizon-core/src/bin/parity.rs": "f35d928c66f958de39fac5e7e836a657eed95f83599e84899f7ea7a5802ba481",
"crates/horizon-core/src/bin/tick_parity.rs": "a54c164fe4814034d369947a887f5273966f5a878ba849204b80d1c729526a8f",
"crates/horizon-core/src/biome/seed_hash.rs": "5afbb4921c750826b395a08bf06f382fc7f01b5759a93de02a2749c523e2a317",
"crates/horizon-core/src/biome.rs": "908efa8cd4aa0a7adf1cb5e1a222e3dfb640eed5e9172e0fe7f0d1287afae7e8",
"crates/horizon-core/src/carving/ellipsoid.rs": "09232a7588610e8a9176fd9c62d40afb1fb89f2ecab6d555536a1d847b142d86",
"crates/horizon-core/src/carving.rs": "0aa3093caa0437868a8c0ed52a6c6e1993bd51c03583c670a2837e749eae5426",
"crates/horizon-core/src/density/arithmetic.rs": "372faec96c923e2cf4aa6c66c1ac398e676853274557823850dbc8933dd15ab1",
"crates/horizon-core/src/density/beardifier.rs": "bb1052e27dae4a01954ca78d7d5babafef85f3b2c704ccaf1920cb85a098cb50",
"crates/horizon-core/src/density/cache.rs": "38d0b6c9dd04ffef979fdad3f29cfebf98d331c8c9d430be5f39aa34f6c30975",
"crates/horizon-core/src/density/end_islands.rs": "168b9384237bd2e41d0ae5a74e944da4dc74e493bdec31fb73184c9a49e3de55",
"crates/horizon-core/src/density/graph/compiler.rs": "ac67deeb16f16d0859aa20e5a165811a29aca0b910037ee6bf5098211f3a6ede",
"crates/horizon-core/src/density/graph/mod.rs": "b776e7da934cb03051a3053d56d461c71e9978e084922fe4194147aa580c07bf",
"crates/horizon-core/src/density/graph/seeded.rs": "6bcdcaa3dffcfa9df673bbbea35120057a16ba3d2b506c52709c36cf8976b74a",
"crates/horizon-core/src/density/graph/tests.rs": "6bfb8cbff462587dd147b0775ea01d370abbd97544b9bd596c792df1b5f7e634",
"crates/horizon-core/src/density/interpolation.rs": "8c890f52b6467f38a94c654986a6a484b8e0b296ab98c398e60a6ce465b5b010",
"crates/horizon-core/src/density/interval.rs": "b42629163baa6b599a233afe8596d1c370cd81d015cc6fc36a9663aa47b98424",
"crates/horizon-core/src/density/mod.rs": "ca0d77d3282e3746ddcbc92bee97dbf640b9a56214e6ac1537f51f9a5b63d6e7",
"crates/horizon-core/src/density/power.rs": "d5b7270a128d4264268cd772109ad16d71a8ec3ff2d3b63b84cb985296d54a91",
"crates/horizon-core/src/density/rounding.rs": "71840c5206ff8d218ac29bf6f66915d5212764ba270c675711a866ba5b55a7a6",
"crates/horizon-core/src/density/selection.rs": "0dd26af2e85004ec5e053039ad11c384c17c56ace9ab45eb3f5cb3503c0b0330",
"crates/horizon-core/src/density/slice.rs": "5d77605573c0deda47778f1f78f7fa3fbf1944d8f5efbcc65cf589181cadf80b",
"crates/horizon-core/src/density/spline.rs": "6bb2e40f940fc6544d6fb09cb0b8cac722be03bc663dbdfb0f61faae207400bc",
"crates/horizon-core/src/density/surface.rs": "159e0e69cda74f870e80ea64fe523c4397e64af4ccb861bb9fd07b2c3cb91b00",
"crates/horizon-core/src/density/terrain_noise.rs": "8d0059d25eb5d248cb97a4c5a2e670d61528928ea151495c004f25adb723fe3e",
"crates/horizon-core/src/density/tests.rs": "7817796ee40d0ae241bfbebec6bca190e0a8e7d5a1caa8ddf858c3eaa58b8858",
"crates/horizon-core/src/density/world_fields.rs": "c1ce305cb8046b384d90c564cafb24af7365d580389637910f3d8b0cc18eb583",
"crates/horizon-core/src/geometry/mod.rs": "dc92f73885dd0a7760743bf74f1ab8afa839d61cef2dfef890f8a23b67b4742e",
"crates/horizon-core/src/geometry/movement.rs": "930e34abef6f6eeec1ee7a6c920b5ba01bfe37b90678884490f4be53241f6b96",
"crates/horizon-core/src/geometry/shape/create.rs": "189812b91d5e0e3ad963afcd1a6e2831b31b8188df91a904ef27061e644806ad",
"crates/horizon-core/src/geometry/shape/face.rs": "f243bce81384040a95ce7984b02b5699206c596c537b2713097e86005b4d3a5c",
"crates/horizon-core/src/geometry/shape/join.rs": "f1b51887b7bf9482557e3ffa99e136f2e5db07ceeb05fdb116adf45e87129075",
"crates/horizon-core/src/geometry/shape.rs": "e8d88a763de1525f77a30b99591641e8a8b80946263acbde035a9bcc356df960",
"crates/horizon-core/src/geometry/sweep.rs": "abd6009c61555161ef2f18b431b2aa384b126c0871cae8b96a62358498a56a1d",
"crates/horizon-core/src/geometry/traversal.rs": "77a3da570a0b85582b669cf4d841cba4b06327006dda26d4a26df4e8ce05bae4",
"crates/horizon-core/src/geometry/trig.rs": "05cef9341a298a474221a0556a11ddf47650393ea7a97b4b2022a4da6628fe37",
"crates/horizon-core/src/lib.rs": "47350edab5791f5074a54563ba98108818c5c6b1c5818a591388eab2ac9f5490",
"crates/horizon-core/src/noise/mod.rs": "1dab5a713abf702402e69ace1bf9e476bba544b0ede5a61c3c3afc60af0a03f3",
"crates/horizon-core/src/noise/normal.rs": "e9e7a46472a54a02c98c46da819842090567153b32fe0c84249009ef19af7aad",
"crates/horizon-core/src/noise/perlin.rs": "e73815447432eab7542f67366cb3f1b368d164e33f6c9cdbd045ac8e4db0932a",
"crates/horizon-core/src/noise/simplex.rs": "6c0dbc15b2b7510195dd28fceca01dcaac1847ccc4a84d299c351589c642fe7a",
"crates/horizon-core/src/noise/stack.rs": "7f6ef0e7d920139ef1a6ad3ec5640f92765715f86dcc5ec29f1a149984f45fa3",
"crates/horizon-core/src/noise/volume.rs": "3f05d2b1780acfdfee60037677d309937a5033b45ef428fc2d540942d4b56b43",
"crates/horizon-core/src/position/iter.rs": "32d2d8e74821cc4e49e3354202d35d3120e4415bfdda9375a73f2137ef651129",
"crates/horizon-core/src/position.rs": "e10101088e6902386b9b6d27773d44312f1ff72bebb19e57967a54945dde9dcd",
"crates/horizon-core/src/random/hash.rs": "0f88a127a1ba2705d3d6c0ebd009324d6ba86ffa2d27edf2d2a84765e690e4a3",
"crates/horizon-core/src/random/positional.rs": "35494745e5d4cf89e282c3ae0ee245e9f2640ddbbc03bc0055df742006a9c862",
"crates/horizon-core/src/random/worldgen.rs": "9f07ff33bcc038d34e27aa5465753bb21df18f2975e43cae201f709bc186945a",
"crates/horizon-core/src/random.rs": "ddb95d3a83e28ad605ff1f49f5df270cc543ca5ef1130e37d989dc74c8d861e6",
"crates/horizon-core/src/storage.rs": "f6c32201c64aefde06e57a2c767be46918df613548a322801546b133799cc33d",
"crates/horizon-core/src/ticks/heap.rs": "bf28afab8af77c3d243aa96cd8a1fb8d934140a4dbd07dbfdbf4e7908bb4b845",
"crates/horizon-core/src/ticks/index.rs": "1f1a6a568c2a30eafecb020ca4d5f5fc414860de85b8b381fc419a2e3e65c24a",
"crates/horizon-core/src/ticks/mod.rs": "84852599dde78687d782f115a5b4b3ef6856c1f9cc92917255167e89c72ab01e",
"crates/horizon-world/src/bin/worldgen_parity.rs": "3143ceb9b56eecfd7667e5feb5f9f0cd13cfb8754b38d4ea338dbba97f2819da",
"crates/horizon-world/src/block_collision.rs": "cec2af81d212d5eed7a2196e4cf0be2daf4cecf53248f78cc68f2190e4d83f9a",
"crates/horizon-world/src/block_placement.rs": "524f4892d50bab77b7d89d19f7b048ffe746996f2ed7f376db2c6ab00ed75c24",
"crates/horizon-world/src/block_properties.rs": "b0bd8a983336c48e488a20caa77dd5c39b1a74bbd14ebc03b3b64556a1c864fc",
"crates/horizon-world/src/block_support.rs": "ade4fbd47284b450dabdd682dc86138b2ac05a0fd379525d7110648908245476",
"crates/horizon-world/src/block_survival.rs": "28ce64375e09819a4f13da9cfbe37c08b5ad0daf01b289c1c360b4567e50e811",
"crates/horizon-world/src/chunk.rs": "8843e6f48dbbc4245ce4e95564dfba3a4d7a051ba0b1577fb038c07adb0ff0f9",
"crates/horizon-world/src/collision.rs": "05313e0c1f3089524fe029f9aac3e439cd47f1c9584c8c7e154c9adcfa88c335",
"crates/horizon-world/src/compression.rs": "4db73e6ca9a0d552d415ce60e7e88b89c9cf2b7d060a669ff8328f518316e101",
"crates/horizon-world/src/dynamic_support.rs": "e799b31e5120cfdb04e0b342f2097d610ed98ef8c4be031620ee00368d82df59",
"crates/horizon-world/src/entity_obstruction.rs": "a4b28a19bf83b23611c59d94535c488db37e4aee24b22c973c545910894a809f",
"crates/horizon-world/src/fluid.rs": "a0a1f34ba94d8b59dc975becee8952960764d67ae08cf2d6a8bcd40a614b15c4",
"crates/horizon-world/src/heightmap.rs": "a5edef082b5af40d814421ea17e129b3b06d70abee6434ed263e4bba58571698",
"crates/horizon-world/src/lib.rs": "a8cd85f6223668c1446ff103276d80937b9707d9a7dc619a176b1b1ceed36a4a",
"crates/horizon-world/src/main.rs": "fce57418b8c22a6a0a9044baa89ed3d9c243bc2e8413fea3ff0f32985daba021",
"crates/horizon-world/src/nbt.rs": "60262c3de8c8f68bc2da0370ccb17393d32b85f5f5022c6358599a7d32119ea6",
"crates/horizon-world/src/piston_shape.rs": "399c2337103b8e7e0eb5667227c5aa14f9549d6a467b7be7da3d0717f0263500",
"crates/horizon-world/src/region.rs": "72b642e87fed37ed17f54d4c30f96c3b64d26c14bc0589c31ef40ff1616188fc",
"crates/horizon-world/src/registry/json.rs": "1eae82604d2b3377fd35aaa3a914a67e5af28f93923815a49e895b4d437433cd",
"crates/horizon-world/src/registry.rs": "f2e58b2b4d944ac8fc6226bd3352cefc3d2052d8473e02ccf7dc402b176df435",
"crates/horizon-world/src/tick_data.rs": "f8d04a69ea263f4168bce33f92e5b4ee2fe07d7caf50a1621c6b3ef6eb2479f3",
"crates/horizon-world/src/worldgen/anchor.rs": "d156b8a0b147a274cdd143409b441f4f43edecde6e8ee14ef9cf0309f743143a",
"crates/horizon-world/src/worldgen/aquifer/config.rs": "fcd2cc3a520c1527c88f095a3b5d38487e7ebb1ccecc5f757f93d04a069add4f",
"crates/horizon-world/src/worldgen/aquifer/noise_based.rs": "74daaa389b5c3dfdc99214dfff256a9e172f83548d2326cbc941d9637cc53fb7",
"crates/horizon-world/src/worldgen/aquifer.rs": "cb4b66d87de3f9f79ae6ffe4badd6f0f44886cd42ad9de2771b8b56c4f1e28c7",
"crates/horizon-world/src/worldgen/bamboo.rs": "d9b47113c99be34256e58308bc6f40072ff25c579d6bf995182aa862c0006102",
"crates/horizon-world/src/worldgen/biome.rs": "3fe316b875aa32683759c337dc71f624910faf0812a2d6d83b8098d6d0bac7a0",
"crates/horizon-world/src/worldgen/biome_chunk.rs": "d6389c8d1adf900eb8f0e6c8833feafe43dc92d74e5c7ffa6f3950975529d624",
"crates/horizon-world/src/worldgen/biome_predicate.rs": "bfc581bc40077cc74ffda43e990a55fe997adc04c7130f98e039c3305ca79aac",
"crates/horizon-world/src/worldgen/biome_source/codec.rs": "2b9e4f5d679eb444cb73174f511e3b9f32e9eb138248ab12cffc6cfd76adf3a8",
"crates/horizon-world/src/worldgen/biome_source/runtime.rs": "f0b02c1521178e4d0377c6ca62694604b6a773f1f234f070ff0cde4685911d45",
"crates/horizon-world/src/worldgen/biome_source.rs": "e181e1736f3e0b75f7091a24c88de3fd116e01b98424d02382aea7a503d1eb54",
"crates/horizon-world/src/worldgen/biome_temperature.rs": "cde4bbab5e260c4065b113376c423b8511ade93c5aa5fd976e245ee8a58b5ad1",
"crates/horizon-world/src/worldgen/block_blob.rs": "c608c57b3d77279646ecb89e50677dd8e452b0422ffd357ba755536c5589eb89",
"crates/horizon-world/src/worldgen/block_column.rs": "f72c21a9066ee0aa68c2fb79fba436d05afb14c6ef5a2cb012f1ed35fc17cd22",
"crates/horizon-world/src/worldgen/block_feature.rs": "5ce5c6eaa5f8a2557a620e533788217b1eb430654801dcf6135d6c92d7a012ac",
"crates/horizon-world/src/worldgen/block_pile.rs": "623e369b77e0526ed2756485a6c0bb09683cd28ad8876dea907360ca6665b39d",
"crates/horizon-world/src/worldgen/block_predicate.rs": "074c692367e3f2191a1bb28d87b23402430a843c246bd30a4ea838ee3500280e",
"crates/horizon-world/src/worldgen/blue_ice.rs": "eedd77bdaff808330b864166d303378096daa5c7d235e925770b171c05109247",
"crates/horizon-world/src/worldgen/bulk_section.rs": "eecf61532d7793f853884c5a18a7f436a59835c7b13d3ec9075804511553c9ba",
"crates/horizon-world/src/worldgen/carver/canyon.rs": "509c4d5a9d689849e98e894ab14a9fbf82d10707d38e9cc688ded3c4778df3e2",
"crates/horizon-world/src/worldgen/carver/generation.rs": "a4498d0d1457536de3922a08e9c40ccce8d2ef9cc263801c15a412327513c098",
"crates/horizon-world/src/worldgen/carver.rs": "46b251e28225fe1250de38f49dec5dacda4e2c4ba1e556e21e6d1e14e2651fc3",
"crates/horizon-world/src/worldgen/carving.rs": "4e77d4e5c4a0ca55ca3563b9bdf6f379342e0bdf9d019d60f7bc57ce4afb5fbd",
"crates/horizon-world/src/worldgen/climate/codec.rs": "d567adeb5d837816fca69102db51d790f71aec1b51c09298230d24612e400a14",
"crates/horizon-world/src/worldgen/climate/index.rs": "c5ce61679a5f0e0f2f0af2a373424effa124d0cce8250609eff38b7034650756",
"crates/horizon-world/src/worldgen/climate.rs": "1b9ad44407f40aee4c1cd5357a5c9c443c1b105728f01cf5c644072f99f8357e",
"crates/horizon-world/src/worldgen/decoration.rs": "75a79b20b21e133816e729f16e758be6165d0db18a64bfbd5fd3618325a2adf3",
"crates/horizon-world/src/worldgen/density.rs": "65a98c156a290a6b374ef670d28cd5dfbe0aa93171824d1bbd96422536273f84",
"crates/horizon-world/src/worldgen/disk.rs": "6f8f9e059755af44c9bfa1b91eba54a7d13fad811dce31717c5502d7f6df33d7",
"crates/horizon-world/src/worldgen/end_podium.rs": "8f6d0d6d60ff0fde63de5781e867239db321f82cf12639f0537d410a3731ee1d",
"crates/horizon-world/src/worldgen/feature.rs": "40500af71ee6628dfb27518262d99f08d0383ae6c7b9397452feef0c213eadd9",
"crates/horizon-world/src/worldgen/feature_catalog.rs": "efc7b17b227e71622ef34108be9abd010f5675b68ea0409fcab82581d9aab88a",
"crates/horizon-world/src/worldgen/feature_graph.rs": "bfd3c21cddf130ebafc7041cadb8a6db3180cf9cd9c74457def8cdb8931ac484",
"crates/horizon-world/src/worldgen/feature_region.rs": "79e8ce343b0dc80dceb8b214c818b383d9b110540bb8c3b0ac32863a7800e288",
"crates/horizon-world/src/worldgen/feature_sort.rs": "3488a093dfe1321aaa29d8a30cfb115d8e666a81bf195514e87b170de626835c",
"crates/horizon-world/src/worldgen/fluid_predicate.rs": "e682d13a21f90605215b79f5d254bf2e524d35e89eab2e3b46384709af80b847",
"crates/horizon-world/src/worldgen/height_provider.rs": "368d4dc119854742a045c4a717679c921456da681fc5f71aa9b37102d6f33b47",
"crates/horizon-world/src/worldgen/material_condition/codec.rs": "09b909bc384472f76d163c8069ec6b507c7d5c6a028a36d5e0e3df20deff7b3b",
"crates/horizon-world/src/worldgen/material_condition/density.rs": "df9d0761e978e5010663ae8b44f2d6c1ecb4907331e333bdb6a2b487c1e107de",
"crates/horizon-world/src/worldgen/material_condition/library.rs": "a55534c5da97575a819af5a7dc0dbec9069993fa8b2619c6c9d5576946e683fa",
"crates/horizon-world/src/worldgen/material_condition/noise.rs": "ffb59cc6be2275d7a5621bf7d768527e74a8326ed7ad78618746711540921675",
"crates/horizon-world/src/worldgen/material_condition/rule.rs": "fed643352efbcb1f68f29b6241896617db889fc7c577a6247564e01922fec1fe",
"crates/horizon-world/src/worldgen/material_condition.rs": "d638dcb8cbcb5da20b3cb29602b0ae6a86c4fdf9d8c0fb725de8ec5fc108717e",
"crates/horizon-world/src/worldgen/material_extensions.rs": "ab806eb679d5116e1aebb64fca70e1499ae71f83c9507a8cb4c3201c2b1bf037",
"crates/horizon-world/src/worldgen/material_stage.rs": "698cbc786eff13c246e2bc2ea062401918cd4265899fbc3d53abc87e45a8e7a1",
"crates/horizon-world/src/worldgen/material_surface.rs": "5d37b90099df3d6da0845dab164e71469fb287019c2ee74bc6714f3b9d6d6d5b",
"crates/horizon-world/src/worldgen/ore_feature.rs": "83b89fd3be691c5412c0215b6abefdcaa44119a05aa41217e061970eb7e12ebd",
"crates/horizon-world/src/worldgen/placement/chain.rs": "9218399b4d3757f7bbc550d7729a7201f32a1c83a7030865e0c12aab8a80d116",
"crates/horizon-world/src/worldgen/placement/context.rs": "dc3f07719dac53c5ceab6635598c010ccc93db0b149970894407bf9b713946e7",
"crates/horizon-world/src/worldgen/placement/layer.rs": "a0f5ca776f33f9d4f72b8fdcd163426152e5e43cb4a7572fc1d4fc57bf965de1",
"crates/horizon-world/src/worldgen/placement.rs": "b0397e5818fae84358546193a95f84bcb0b5bffafce909e6fcb3386179ee8023",
"crates/horizon-world/src/worldgen/replace_blobs.rs": "bf3377d86d2570cd15288ba69dde64a2cf67c5bcbb9a31d0c23421964af74afa",
"crates/horizon-world/src/worldgen/router.rs": "2cee7cf15a0d2ada12be6b39d83e78177fd3033922e4f4cce87a64073cec2847",
"crates/horizon-world/src/worldgen/rule_test.rs": "d1662ec492489ebaa4b87e20f9a8c809dcf3a223a151211bd65ec241a63692fc",
"crates/horizon-world/src/worldgen/settings.rs": "a8fe87752e1982ef11cb67f8d80d8e3428b2c27db178044cb425ba803e689e59",
"crates/horizon-world/src/worldgen/simple_block.rs": "9ba1a88d5db4925f8c37366557887f776c805f9a6444781615b5dbaff52e92a4",
"crates/horizon-world/src/worldgen/spike.rs": "eacf0d9d3d2bc3d4bcf62bc87555f90031138236a5bfef0eb5270204f7c2a4fc",
"crates/horizon-world/src/worldgen/spring.rs": "830849e9b89fcdf09e9b06fbefdcdebe3c8c3f82804f60b8bd5de04ba303cb62",
"crates/horizon-world/src/worldgen/state_provider/catalog.rs": "c8687e15a4887a9200c8d365857d801efaa8f90e669eee3e600a1db4905119eb",
"crates/horizon-world/src/worldgen/state_provider/noise.rs": "7ee369c2a032e5620cb9bdf9ee7adad1f70870fb0a27efb7b7c514d92f83491f",
"crates/horizon-world/src/worldgen/state_provider/properties.rs": "9909139aaca1dc4f743957257fadaa9d423d21fa401fa9abc050505f79365139",
"crates/horizon-world/src/worldgen/state_provider.rs": "be29ada818826a90afe01ab60916360fdfc4cbde5af15c9359cd56b1f1f7b156",
"crates/horizon-world/src/worldgen/tags.rs": "d35ded93fe6e95ea045cadcf4925e656dc34ef832b742179b9ad0cc49f902f31",
"crates/horizon-world/src/worldgen/terrain.rs": "7fb7130111654563f7217f7b84563adde0b11c3ebd6a499a514dc98ebef7c733",
"crates/horizon-world/src/worldgen/underwater_magma.rs": "7317e40af5ee5a9780f90ce358fa5d3e7f5b3d513b731df6ff97754d9c2a0154",
"crates/horizon-world/src/worldgen/value_provider.rs": "3358b563db8cb0bc8a9cfbbe2b8b3e70941cd2d37a0b519fbb6545f8d0dcbb69",
"crates/horizon-world/src/worldgen/vines.rs": "ee9b79b65012bd86a838241d2ef708680f7fe8eafc8aa656dae9cf2abf3c6418",
"crates/horizon-world/src/worldgen/weighted.rs": "839840aae25302933bbd97a81a2336330e2d03a0ad1684e1e16ac769ed408e6b",
"crates/horizon-world/src/worldgen.rs": "6e1d10aeee1776013db06e0768b51591064484decefeabf8f4721a7512cb1808",
"crates/horizon-world/examples/simple_block_fixture.rs": "2943f7a5b9af96eb2a63b7dfd857051faeffd7cea15df30711094cb281a32a67"
},
"fixtures_sha256": {
".local/rust-bamboo-parity/inputs.txt": "70a4b16ad5f5c2c9a59e940e43ffd90d799e38c620c0da3a91b1b94018d905c3",
".local/rust-bamboo-parity/block-properties.nbt": "1aba22092f70d16b8092014c058ddc210e4aaa77bac32f6ec62e4d0e91d92a99",
".local/rust-bamboo-parity/block-registry.nbt": "5042073c80753f9548cb8fdb5b1000f0e5a1220127a0cc3c6a75737e180bf9bb",
".local/rust-bamboo-parity/cached-collision.nbt": "f3a5c6a5b2d51dad6c52e486e2b9989e79fd55db1a9bda6196db399550b04078",
".local/rust-bamboo-parity/cached-support.nbt": "b6fdc9471b0b61e904206d3f099f4dc235aad6d723536c210be0cb7c3560d82c",
".local/rust-bamboo-parity/catalog.json": "93581107e3564e60261044fdca61e3b7f237b9e6724c14b86e335ccc01198ba9",
".local/rust-bamboo-parity/facts.json": "5e89a805514cabfe46e082d651db406b9479781e1d97b65faa2ab9f6f871309b",
".local/rust-bamboo-parity/feature-catalog.json": "c048c761c5084a40560731955cc999a364d14055bdd86fa50989a92e4a771286",
".local/rust-bamboo-parity/fluid-tags.json": "f1e5eb6d22ea7effbb118a2282709cfdeef1bfae4516b775a55ad7a4df29d7d0",
".local/rust-bamboo-parity/placement-definitions.json": "f8a972ec87c2352500af6481158a3192bdbf699951025639bea744e4127b97c4",
".local/rust-bamboo-parity/support-cached-states.json": "353945a58fe69b352925473f1693f9b14a9ad19f13c15159c4184f2c29258622",
".local/rust-bamboo-parity/survival-classes.json": "361832008eea0aa9151d367a125a19c6353d99a0b88b27dd0682f55b7954553e",
".local/rust-bamboo-parity/survival-extra-definitions.json": "170f2db5bc97a49fa25cc249540aa7f04beea455cdbaf43742ed9415f5090a02",
".local/rust-bamboo-parity/survival-placement-classes.json": "7fdbf48ab18c87098bbe1b6176cb81f4842b6c5d9a4d6bbaf7037ff61cb1848c",
".local/rust-bamboo-parity/survival-rules.json": "8805c18b3195bb1eac1f1e5281333df56e2892505f64023e5403c58ce2ec4368",
".local/rust-bamboo-parity/survival-support-tags.json": "15d88d5bb5ad10d65124d5abb2cc055d251b06d129bca7be06f55b43dc13f6fd",
".local/rust-bamboo-parity/tags.json": "36975358262568f32bf84778d63d9989a36e7394ed4796d70a34809a8f4a64f0"
},
"rust_output_sha256": "34664ee037a753f00002f68ac8fa6199d5e9540806a34614be11973de4c0fb13",
"references": [
{
"side": "client",
"jar_sha256": "4508d006323f24fa02876310c192d739af56516eb259000ac50f0909a68c9a2d",
"output_sha256": "34664ee037a753f00002f68ac8fa6199d5e9540806a34614be11973de4c0fb13",
"matched_output_lines": 3485027
},
{
"side": "server",
"jar_sha256": "a362163eec5d1612d520772bc16e5b39c09e3b234fdc045f56bf544284ee8ae6",
"output_sha256": "34664ee037a753f00002f68ac8fa6199d5e9540806a34614be11973de4c0fb13",
"matched_output_lines": 3485027
}
],
"scope": "Native BambooFeature bounded float codec, air-origin gate, actual bamboo survival, attempted-success semantics, height and optional radius draws, circular podzol patch with ordered WORLD_SURFACE requests, replacement tag, stalk air checks and three leafy caps including the stopping-position overwrite. All origin/support/podzol states have failure-terminated prefixes; all obstacle states have full no-podzol runs. Both RNGs, many seeds, signed zero and float rounding, obstruction heights, rejected writes, repeated mutation, partial failures, wrapped coordinates and composition are covered. Fixture height values are controlled and do not update after writes. Growth, world updates and complete generation remain pending; pathological wrapped patch loops are bounded to 2^20 visits."
}
@@ -50,6 +50,7 @@ struct World<'a, 'r, W, R> {
reject: i32,
brightness: i32,
sea_level: i32,
surface_height: i32,
air: BlockStateId,
destroy_success: bool,
}
@@ -213,8 +214,10 @@ impl<W: Write, R: RandomSource> PlacementContext for World<'_, '_, W, R> {
fn max_y(&mut self) -> Result<i32, PlacementQueryError> {
Ok(319)
}
fn height(&mut self, _: HeightmapType, _: i32, _: i32) -> Result<i32, PlacementQueryError> {
Err(PlacementQueryError::Unavailable)
fn height(&mut self, kind: HeightmapType, x: i32, z: i32) -> Result<i32, PlacementQueryError> {
self.event(format_args!("height {} {x} {z}", kind.name()))
.map_err(query)?;
Ok(self.surface_height)
}
fn block_state(&mut self, p: [i32; 3]) -> Result<BlockStateId, PlacementQueryError> {
self.get(p).map_err(query)
@@ -308,6 +311,7 @@ fn fixture<R: RandomSource>(
reject: f["reject"].as_i64().unwrap() as i32,
brightness: f["brightness"].as_i64().unwrap() as i32,
sea_level: f.get("sea").and_then(Value::as_i64).unwrap_or(63) as i32,
surface_height: f.get("height").and_then(Value::as_i64).unwrap_or(64) as i32,
air: blocks.air,
destroy_success: f
.get("destroy_success")
+1
View File
@@ -2,6 +2,7 @@
//! Complete terrain generation remains pending.
pub mod anchor;
pub mod aquifer;
pub mod bamboo;
pub mod biome;
pub mod biome_chunk;
pub mod biome_predicate;
+156
View File
@@ -0,0 +1,156 @@
//! Bamboo stalks, podzol patches and ordered cap overwrites.
use super::{
DecodeError, Object,
block_feature::FeatureBlockBindings,
feature::FeatureWorld,
number_text,
placement::{PlacementContext, PlacementQueryError},
required,
};
use crate::{heightmap::HeightmapType, registry::BlockStateId};
use horizon_core::random::{InvalidBound, RandomSource};
use std::{collections::BTreeSet, sync::Arc};
#[derive(Clone, Debug)]
pub struct BambooFeature {
probability: f32,
air: [BlockStateId; 3],
bamboo: BlockStateId,
trunk: BlockStateId,
caps: [BlockStateId; 3],
podzol: BlockStateId,
replaceable: Arc<BTreeSet<BlockStateId>>,
}
#[derive(Debug)]
pub enum BambooError<E> {
World(E),
Query(PlacementQueryError),
Random(InvalidBound),
WorkLimit,
}
impl<E: std::fmt::Display> std::fmt::Display for BambooError<E> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::World(e) => e.fmt(f),
Self::Query(e) => e.fmt(f),
Self::Random(e) => e.fmt(f),
Self::WorkLimit => f.write_str("bamboo work limit exceeded"),
}
}
}
impl<E: std::error::Error + 'static> std::error::Error for BambooError<E> {}
impl BambooFeature {
pub fn from_json(json: &str, bindings: &FeatureBlockBindings<'_>) -> Result<Self, DecodeError> {
let object: Object = serde_json::from_str(json)?;
let probability = number_text(required(&object, "probability")?)?
.parse::<f64>()
.map_err(|_| DecodeError::Field("bamboo probability"))?
as f32;
if !(0.0..=1.0).contains(&probability) || probability.is_sign_negative() {
return Err(DecodeError::Field("bamboo probability"));
}
let state = |leaves, stage| {
bindings.codec.decode(&format!(r#"{{"id":"bamboo","properties":{{"age":"1","leaves":"{leaves}","stage":"{stage}"}}}}"#),false).map_err(|e|DecodeError::Registry(e.to_string()))
};
let get = |name| {
bindings
.registry()
.default_state(name)
.ok_or_else(|| DecodeError::Registry(format!("unknown bamboo block {name}")))
};
Ok(Self {
probability,
air: bindings.air,
bamboo: get("minecraft:bamboo")?,
trunk: state("none", 0)?,
caps: [state("large", 1)?, state("large", 0)?, state("small", 0)?],
podzol: get("minecraft:podzol")?,
replaceable: bindings
.tags
.get("minecraft:beneath_bamboo_podzol_replaceable")
.cloned()
.unwrap_or_default(),
})
}
pub fn place<W: FeatureWorld + PlacementContext>(
&self,
world: &mut W,
random: &mut impl RandomSource,
origin: [i32; 3],
) -> Result<bool, BambooError<W::Error>> {
if !self
.air
.contains(&world.block(origin).map_err(BambooError::World)?)
{
return Ok(false);
}
// Native success records the air-origin attempt, even if survival fails.
if !world
.block_survives(self.bamboo, origin)
.map_err(BambooError::Query)?
{
return Ok(true);
}
let height = random.next_int_bound(12).map_err(BambooError::Random)? + 5;
if random.next_float() < self.probability {
let radius = random.next_int_bound(4).map_err(BambooError::Random)? + 1;
let mut x = origin[0].wrapping_sub(radius);
let max_x = origin[0].wrapping_add(radius);
let mut remaining = 1_048_576;
while x <= max_x {
let mut z = origin[2].wrapping_sub(radius);
let max_z = origin[2].wrapping_add(radius);
while z <= max_z {
if remaining == 0 {
return Err(BambooError::WorkLimit);
}
remaining -= 1;
let dx = x.wrapping_sub(origin[0]);
let dz = z.wrapping_sub(origin[2]);
if dx.wrapping_mul(dx).wrapping_add(dz.wrapping_mul(dz)) <= radius * radius {
let y = world
.height(HeightmapType::WorldSurface, x, z)
.map_err(BambooError::Query)?
.wrapping_sub(1);
let p = [x, y, z];
if self
.replaceable
.contains(&world.block(p).map_err(BambooError::World)?)
{
world
.set_block(p, self.podzol, 2)
.map_err(BambooError::World)?;
}
}
z = z.wrapping_add(1);
}
// Also bound overflow loops when the inner range is empty.
if remaining == 0 {
return Err(BambooError::WorkLimit);
}
remaining -= 1;
x = x.wrapping_add(1);
}
}
let mut p = origin;
for _ in 0..height {
if !self
.air
.contains(&world.block(p).map_err(BambooError::World)?)
{
break;
}
world
.set_block(p, self.trunk, 2)
.map_err(BambooError::World)?;
p[1] = p[1].wrapping_add(1);
}
if p[1].wrapping_sub(origin[1]) >= 3 {
// The first cap occupies the stopping position, including an obstacle.
for state in self.caps {
world.set_block(p, state, 2).map_err(BambooError::World)?;
p[1] = p[1].wrapping_sub(1);
}
}
Ok(true)
}
}
@@ -49,6 +49,7 @@ pub enum FeatureDefinition {
Disk(super::disk::DiskFeature),
Spring(super::spring::SpringFeature),
Vines(super::vines::VinesFeature),
Bamboo(super::bamboo::BambooFeature),
ReplaceBlobs(super::replace_blobs::ReplaceBlobsFeature),
EndPodium(super::end_podium::EndPodiumFeature),
Spike(super::spike::SpikeFeature),
@@ -78,6 +79,7 @@ pub enum FeatureExecutionError<E> {
BlockPile(super::block_pile::BlockPileError<E>),
Disk(super::disk::DiskError<E>),
BlockBlob(super::block_blob::BlockBlobError<E>),
Bamboo(super::bamboo::BambooError<E>),
ReplaceBlobs(super::replace_blobs::ReplaceBlobsError<E>),
EndPodium(super::end_podium::EndPodiumError<E>),
Spike(super::spike::SpikeError<E>),
@@ -98,6 +100,7 @@ impl<E: std::fmt::Display> std::fmt::Display for FeatureExecutionError<E> {
Self::BlockPile(e) => e.fmt(f),
Self::Disk(e) => e.fmt(f),
Self::BlockBlob(e) => e.fmt(f),
Self::Bamboo(e) => e.fmt(f),
Self::ReplaceBlobs(e) => e.fmt(f),
Self::EndPodium(e) => e.fmt(f),
Self::Spike(e) => e.fmt(f),
@@ -269,6 +272,11 @@ impl FeatureDefinition {
)?)?;
match kind.as_str() {
"minecraft:no_op" => Ok(Self::NoOp),
"minecraft:bamboo" => super::bamboo::BambooFeature::from_json(
json,
blocks.ok_or(DecodeError::Field("bamboo bindings"))?,
)
.map(Self::Bamboo),
"minecraft:netherrack_replace_blobs" => {
super::replace_blobs::ReplaceBlobsFeature::decode(
json,
@@ -629,6 +637,9 @@ impl FeatureDefinition {
.place(world, random, origin, run.blocks)
.map_err(FeatureExecutionError::Basic),
Self::NoOp => Ok(true),
Self::Bamboo(feature) => feature
.place(world, random, origin)
.map_err(FeatureExecutionError::Bamboo),
Self::ReplaceBlobs(feature) => feature
.place(world, random, origin)
.map_err(FeatureExecutionError::ReplaceBlobs),
+17
View File
@@ -592,3 +592,20 @@ prefixes, every constant radius, six provider families, both RNGs, height
boundaries, wrapped X/Z coordinates, partial failures and composition. Workspace
tests and all-target Clippy pass. World updates and full generation remain
pending; pathological descent is bounded to 2^20 visits.
## Bamboo and podzol patches
Bamboo features preserve the initial air gate, real survival checks and native
attempt-success behavior when survival fails. Podzol patches use ordered surface
height requests and the native replacement tag. Stalks preserve random draws and
leaf states, including the first cap overwriting the stopping position when at
least three stalk positions were attempted.
The [bamboo comparison](../benchmarks/rust-bamboo-2026-10-06/scoped-parity.json)
matches **3,485,027 lines per reference JAR** over **144,452 fixtures**. Every
origin/support/podzol state has a failure-terminated prefix; every obstacle state
has a complete no-podzol run. Tests include both RNGs, signed-zero decoding,
obstruction heights, rejected writes, repeated mutations, wrapped coordinates
and composition. Workspace tests and all-target Clippy pass. Fixture surface
heights are controlled; dynamic height updates, bamboo growth and full gameplay
remain pending. Pathological wrapped patch loops have a 2^20 work limit.
@@ -63,6 +63,7 @@ public final class VanillaSimpleBlockOracle {
if(name.equals("getChunk")&&args.length==1&&args[0] instanceof BlockPos)return chunk((BlockPos)args[0]);
if(name.equals("getMinY"))return -64;
if(name.equals("getHeight")&&(args==null||args.length==0))return 384;
if(name.equals("getHeight")&&args.length==3){event("height "+args[0]+" "+args[1]+" "+args[2]);return f.has("height")?f.get("height").getAsInt():64;}
if(name.equals("getSeaLevel"))return f.has("sea")?f.get("sea").getAsInt():63;
if(name.equals("getBlockState")){var p=(BlockPos)args[0];event("get "+xyz(p));var state=at(p);out.println("state "+Block.getId(state));return state;}
if(name.equals("getFluidState")){var p=(BlockPos)args[0];event("fluid "+xyz(p));return (f.has("fluid")?Block.stateById(f.get("fluid").getAsInt()):at(p)).getFluidState();}
+52
View File
@@ -0,0 +1,52 @@
#!/usr/bin/env python3
"""Compare bamboo survival, podzol spread and ordered leafy caps."""
import itertools
import json
try:
from . import test_simple_block_parity as harness
except ImportError:
import test_simple_block_parity as harness
def corpus(facts, placement):
states = facts['states']; get = lambda name: facts['defaults']['minecraft:' + name]
wrap = lambda n: (n + 2**31) % 2**32 - 2**31
def bamboo(probability=1): return {'type': 'bamboo', 'probability': probability}
def case(definition=None, **extra):
origin = extra.pop('origin', [0, 65, 0])
value = {'definition': json.dumps(definition if definition is not None else bamboo()),
'mode': 'feature', 'origin': origin, 'legacy': False, 'seed': 0, 'world_seed': 1,
'fail': -1, 'reject': 0, 'brightness': 15, 'height': origin[1], 'fallback': get('air'),
'states': [[*origin, get('air')], [origin[0], wrap(origin[1]-1), origin[2], get('dirt')]], 'repeats': 1}
value.update(extra); return value
for state in range(len(states)):
yield case(states=[[0, 65, 0, state], [0, 64, 0, get('dirt')]], fail=25)
yield case(bamboo(0), states=[[0, 65, 0, get('air')], [0, 64, 0, state]], fail=25)
yield case(fallback=state, fail=25)
yield case(bamboo(0), states=[[0, 64, 0, get('dirt')], [0, 68, 0, state]])
for probability, legacy, seed in itertools.product([0, 0.01, 0.5, 1], [False, True], range(64)):
yield case(bamboo(probability), legacy=legacy, seed=seed, repeats=2)
for legacy, seed, fail, reject in itertools.product([False, True], range(16), [-1, 0, 1, 2, 4, 11, 25, 60, 100], [0, 1, 3]):
yield case(legacy=legacy, seed=seed, fail=fail, reject=reject, fallback=get('dirt'),
states=[[0, y, 0, get('air')] for y in range(65, 84)], repeats=2)
for stop, legacy, reject in itertools.product(range(1, 19), [False, True], [0, 1, 3]):
yield case(bamboo(0), legacy=legacy, reject=reject, states=[[0, 64, 0, get('dirt')], [0, 65+stop, 0, get('bedrock')]])
for height, legacy in itertools.product([-2147483648, -64, 0, 65, 320, 2147483647], [False, True]):
yield case(height=height, legacy=legacy)
for origin, legacy in itertools.product(
[[2147483647, 65, 2147483647], [-2147483648, 65, -2147483648], [0, 2147483647, 0], [0, -2147483648, 0],
[2147483644, 65, 2147483644]], [False, True]):
yield case(origin=origin, legacy=legacy, fail=20)
yield case(bamboo(0), origin=origin, legacy=legacy)
for value in [None, False, -1, -0.0, -1e-100, 1e-100, 1.0000000001, 1.01, '0.5', [], {}]:
yield case(bamboo(value))
yield case({'type': 'bamboo'})
for legacy, fail, reject in itertools.product([False, True], [-1, 0, 9, 31], [0, 3]):
placed = {'feature': bamboo(), 'placement': [{'type': 'count', 'count': 2}]}
yield case(placed, mode='placed', legacy=legacy, fail=fail, reject=reject, repeats=2)
yield case({'type': 'overlay', 'features': [placed, {'feature': 'horizon:moss', 'placement': []}]}, legacy=legacy, fail=fail, reject=reject)
if __name__ == '__main__':
harness.run(corpus, 'rust-bamboo-parity', __file__,
'Native BambooFeature bounded float codec, air-origin gate, actual bamboo survival, attempted-success semantics, height and optional radius draws, circular podzol patch with ordered WORLD_SURFACE requests, replacement tag, stalk air checks and three leafy caps including the stopping-position overwrite. All origin/support/podzol states have failure-terminated prefixes; all obstacle states have full no-podzol runs. Both RNGs, many seeds, signed zero and float rounding, obstruction heights, rejected writes, repeated mutation, partial failures, wrapped coordinates and composition are covered. Fixture height values are controlled and do not update after writes. Growth, world updates and complete generation remain pending; pathological wrapped patch loops are bounded to 2^20 visits.')