Match living entity landing particles and fall callback ordering

This commit is contained in:
Ludovic Marie committed 2026-10-07 02:19:25 +02:00
1 parent 73fe41f531
commit 921f092c0f
7 files changed
+698

No files matched your search

@@ -0,0 +1,225 @@
{
"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": 26460,
"sources_sha256": {
"mods/horizon/src/test/java/dev/horizonlab/parity/VanillaLivingLandingOracle.java": "6c0f8109aa685f04e94ee642459fe27573667e0076ec181b0371b88356ac9f7a",
"tools/test_living_landing_parity.py": "039d18708631d1c139f2590bd0cc8cf0dae95439ba56fb338e6b9c5e8769a7b4",
"tools/test_rust_parity.py": "29d3db08005873528894b45ca10dcd6278753650473b3eb574e53e06ede40ec6",
"tools/lab.py": "d00bf107fa01b6c579734a082c3ca4ade3018f0d6851d46844acf17574f66e64",
"Cargo.toml": "3f93319c1eb70dbd7ba25cd74ef343fbf35c52c7c4752f955605c58699cbff9d",
"Cargo.lock": "cb1e87d449114a63edd904c0559d631e8fa9feeb27efd6539aacdcfeae178876",
"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/border.rs": "1386f8080521231d05ba3e84d94a3e1d5218d35ccb3d5e9fe2bbb05a8739e573",
"crates/horizon-core/src/geometry/mod.rs": "c650386f9860e44a96c55f18c8f1428ca715fcb0bc5c2434db5b1caa43df27fe",
"crates/horizon-core/src/geometry/movement.rs": "bcfb452752940ce53ae6dc267a3c4eb29459046a063c1f884684ab4f4bd0068f",
"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": "4255e16837fde42a52ba118f60581d197c571bb530b60d635e506184ebf4db0b",
"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/text.rs": "e6553ddb5db5039e8dc4614b6a6dc9f35a26dc3d1673b68a04e72e7983b67abb",
"crates/horizon-core/src/ticks/generation.rs": "be1237e790693f633a61714ddae68c6506a777588a8a3ab5efbe53a35774dbd2",
"crates/horizon-core/src/ticks/heap.rs": "bf28afab8af77c3d243aa96cd8a1fb8d934140a4dbd07dbfdbf4e7908bb4b845",
"crates/horizon-core/src/ticks/index.rs": "1f1a6a568c2a30eafecb020ca4d5f5fc414860de85b8b381fc419a2e3e65c24a",
"crates/horizon-core/src/ticks/mod.rs": "01634630668172516b8b79d0907bad47f6e2c543a6d499c2fa4b201823b375cb",
"crates/horizon-world/src/bin/worldgen_parity.rs": "3143ceb9b56eecfd7667e5feb5f9f0cd13cfb8754b38d4ea338dbba97f2819da",
"crates/horizon-world/src/block_collision.rs": "39207247f2f6dd3ddc77640229044246f91b32054563f88a435f1bd92ad2092c",
"crates/horizon-world/src/block_collision_scan.rs": "20802862364c110ff85eeaf48606596a8468b83768dda46b412d337958790b41",
"crates/horizon-world/src/block_entities.rs": "a9deae92e6e5a1fcf4f0476c33ea4d170ddad6fd4b3303e1bb66d933973d5e06",
"crates/horizon-world/src/block_entity_push.rs": "12ee91bf604946234b24a0da4333f07d1830ab9743a422d41c8d0afac7d49cfd",
"crates/horizon-world/src/block_landing.rs": "33083fe39266fbefec1b3ea1a19f2afc609791d189da247c895e116ad6eb266a",
"crates/horizon-world/src/block_placement.rs": "524f4892d50bab77b7d89d19f7b048ffe746996f2ed7f376db2c6ab00ed75c24",
"crates/horizon-world/src/block_properties.rs": "b0bd8a983336c48e488a20caa77dd5c39b1a74bbd14ebc03b3b64556a1c864fc",
"crates/horizon-world/src/block_render.rs": "8df52ee11aef406a1571198a64048062a0ef6864e0058d9293a57f64b73c916a",
"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/chunk_status.rs": "dbb8583cfa775203c544d24d69e03720385428f15a267a561356c21f5025e9c7",
"crates/horizon-world/src/collision.rs": "a749794d5c27ccd6c0d964ad02f9dc31d49d9f4fe4b7366cd4bd8155e681b00b",
"crates/horizon-world/src/collision_context.rs": "2176c7034e07f547510a8327cdad9d3b63f26e4754032b76b7d5fe13166c333c",
"crates/horizon-world/src/compression.rs": "4db73e6ca9a0d552d415ce60e7e88b89c9cf2b7d060a669ff8328f518316e101",
"crates/horizon-world/src/dynamic_support.rs": "5996bc1c436fb6215d8fc3d0bb4de5dd9b966d842d432580d624195771697fb8",
"crates/horizon-world/src/edge_backoff.rs": "5259d7aa44e64f88351d1556c44dd0edcad3b400a099c659755792efba7b5773",
"crates/horizon-world/src/entity_collision.rs": "09f2fd7f6cd56fc282f468ecfa765a94506929e9684bd3a5c90123e5b51cd627",
"crates/horizon-world/src/entity_move.rs": "a40a50d3e841625a75d4cfbb7c2630ab5a62a574456567e7234f401ae8441b32",
"crates/horizon-world/src/entity_obstruction.rs": "a4b28a19bf83b23611c59d94535c488db37e4aee24b22c973c545910894a809f",
"crates/horizon-world/src/fall_damage.rs": "8c28e2933493569fe05dea22332024287d28b0746b514dd465681040b003296b",
"crates/horizon-world/src/fluid.rs": "a0a1f34ba94d8b59dc975becee8952960764d67ae08cf2d6a8bcd40a614b15c4",
"crates/horizon-world/src/fluid_shape.rs": "28bb65a72f5bd074a354ba38a1b1707fcb0947537977a11540bba0adb85995c2",
"crates/horizon-world/src/ground_support.rs": "3e371d74ecede4d30beef0a7e8a2ee33463584a5b1ed3d852b394efcce1ffbe0",
"crates/horizon-world/src/heightmap.rs": "a5edef082b5af40d814421ea17e129b3b06d70abee6434ed263e4bba58571698",
"crates/horizon-world/src/interaction_shapes.rs": "f321284ddab440a394372b6dedc8b5cc29a653c0745251a9e96c650f21c33dea",
"crates/horizon-world/src/lib.rs": "5aa09a6af45a1cbba716343b1ae9cdb565e32086bf837bdbc449140268c82595",
"crates/horizon-world/src/liquid_collision.rs": "0f0a83b04e5a5bc9d98ed05fde4d6584ba69642e93522e726e4cfc325d6fcb1d",
"crates/horizon-world/src/living_fall.rs": "da0f176f4731e1c86e08bfeee8167742fd1b86380ff0e9304c13c1ce3f1c67ab",
"crates/horizon-world/src/living_landing.rs": "8bca4eb7977abc2b14fb7174be4ea355352106ea1e76ff5f3ad8696969d9eefd",
"crates/horizon-world/src/main.rs": "fce57418b8c22a6a0a9044baa89ed3d9c243bc2e8413fea3ff0f32985daba021",
"crates/horizon-world/src/movement_emission.rs": "76e7d6f5b19c94676190f54389f9c4ce12cf89945ccfcb9c750de473223e7753",
"crates/horizon-world/src/movement_properties.rs": "6f01ab920b376a13f5ae12c3718f4dd067e1dff2b829585af00a8b2100f3775d",
"crates/horizon-world/src/movement_restitution.rs": "04b4fe0947e7e52576145bd8ed15920278e8a538afcc74d125eaca513dffa0ef",
"crates/horizon-world/src/movement_support.rs": "02d85cf572847f2e60b5a7f9d60b856888a67b3c1f4df03e81998c66fdf874be",
"crates/horizon-world/src/nbt.rs": "60262c3de8c8f68bc2da0370ccb17393d32b85f5f5022c6358599a7d32119ea6",
"crates/horizon-world/src/piston_movement.rs": "7a3fd2549a582b5f3a5c21aa05d67d944d62ca1105856679e100ac5b44e9a52f",
"crates/horizon-world/src/piston_shape.rs": "399c2337103b8e7e0eb5667227c5aa14f9549d6a467b7be7da3d0717f0263500",
"crates/horizon-world/src/powder_snow_collision.rs": "7289946a21478d11abfd2bdcdddff3d7f98e25bf6a68564fed45c3cc01ac2a36",
"crates/horizon-world/src/region.rs": "72b642e87fed37ed17f54d4c30f96c3b64d26c14bc0589c31ef40ff1616188fc",
"crates/horizon-world/src/registry/json.rs": "7a683afcd1742c5307756927d7f0aa779c7d7992af87d8d371dd98108728e235",
"crates/horizon-world/src/registry.rs": "f2e58b2b4d944ac8fc6226bd3352cefc3d2052d8473e02ccf7dc402b176df435",
"crates/horizon-world/src/registry_collision_scan.rs": "a5021a168adcedc37d77f533a8921c02f09c59f46a01737586aaa33c5908b8d0",
"crates/horizon-world/src/registry_fall_ray.rs": "52935436132df505691144840dcbd858b15d00d8305997284e3a542ecf6bc0dc",
"crates/horizon-world/src/retrogen.rs": "6efd36a6293756688c1990a7965bd65609d7c5e2bf2a5f4f01d54932d2b9386b",
"crates/horizon-world/src/suffocation.rs": "bed219dd5bbab6263b98fbe3d0eb512cb96c52ae847210efe49514d05f22f11e",
"crates/horizon-world/src/tick_data.rs": "f8d04a69ea263f4168bce33f92e5b4ee2fe07d7caf50a1621c6b3ef6eb2479f3",
"crates/horizon-world/src/world_movement.rs": "f8fe9e34f2395d2a04c839d1c8ba78e09ce493c26de6f6e4e237b9c19c061da5",
"crates/horizon-world/src/world_ray.rs": "879bdbbe12f56deb705cc183933431489e840b71eade40c33391eed5949c2e61",
"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": "8fdfe6ebe3ffeca97611fd5991a5bc6b2783ea1c62f4d8ef3b2dd53c04a5dc1d",
"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/biome_weather.rs": "96b1f9494559ed31d06257b83d604d00cc069ccc8c7b68094117e034b4a71bd7",
"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": "36b4e46ee7f7f73bcb94d8bce8d99013e4b16234c9a03542eeb3186e88dd1c21",
"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/bonus_chest.rs": "05d0f63fbc7412f13ba1fa37a7238114b8f70bfff318beb2dece590482d49cc0",
"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/chorus_plant.rs": "29224c164ede919e280a65a68f155c873e2c8fd0aa7d7e244d8fa9e468760250",
"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/delta.rs": "c2bf49ddf3231d28c109e68871429bc13d76a7bebe14eb33e84aa856bdd66145",
"crates/horizon-world/src/worldgen/density.rs": "65a98c156a290a6b374ef670d28cd5dfbe0aa93171824d1bbd96422536273f84",
"crates/horizon-world/src/worldgen/disk.rs": "6f8f9e059755af44c9bfa1b91eba54a7d13fad811dce31717c5502d7f6df33d7",
"crates/horizon-world/src/worldgen/end_gateway.rs": "f53fde77d6a36a525cd15d890003b2c129d0689a4cbfa2cf443a0c2e3acae040",
"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": "da132fca9fdafb0d89a917d5f4b68c60b70fb33cadf948a107da0e35d83dcd91",
"crates/horizon-world/src/worldgen/feature_region.rs": "df0ff142a461cafa9a3b42b949ad3376887f2be8d4ea5cc794a3ed2e977bb499",
"crates/horizon-world/src/worldgen/feature_sort.rs": "3488a093dfe1321aaa29d8a30cfb115d8e666a81bf195514e87b170de626835c",
"crates/horizon-world/src/worldgen/fluid_predicate.rs": "64197c9e63bebf1ce04bc280bfe980bb6c1b9aa887ca5cded904aeb0d9ae6c46",
"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": "a278304795136677ad4b4933ea50c972b9729a1c671c03fa309c193e224b1fd6",
"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/snow_freeze.rs": "9040be3d5c7f8f0fd812d2df45e154bf8a9eeef038530b83fa4159d246eac35f",
"crates/horizon-world/src/worldgen/speleothem.rs": "c71651c1c97818a8a85863e14414d6139ea3bdeb897fa422043bcc1499d99aae",
"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": "fd1f2e0afc15daf66f484ea40a1f2b7013650bda0941b23c64c81ee9f79cb673",
"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/write_zone.rs": "8e17a179764d3f68c05882aef35f6810c28a0000f9a83e58bd6030660b76e64e",
"crates/horizon-world/src/worldgen.rs": "e8e9f099ee1fcec8eedce31805d676d3f89c18e84f494a147ffcbfee23d3d54f",
"crates/horizon-world/examples/living_landing_fixture.rs": "929d423377667e5f1b7145150dabfc4706c8f73e509c37c0a91173816870d1c7"
},
"fixtures_sha256": {
".local/rust-living-landing-parity/inputs.txt": "b01850d7cb4a55b36f59b0848a5445f1f1bd78cea2a68593a4f693cce74601b8"
},
"rust_output_sha256": "83eba3422733039e35adac26da5020d02b0df640746b5cb5c80fd82c57a09120",
"references": [
{
"side": "client",
"jar_sha256": "4508d006323f24fa02876310c192d739af56516eb259000ac50f0909a68c9a2d",
"output_sha256": "83eba3422733039e35adac26da5020d02b0df640746b5cb5c80fd82c57a09120",
"matched_output_lines": 244844
},
{
"side": "server",
"jar_sha256": "a362163eec5d1612d520772bc16e5b39c09e3b234fdc045f56bf544284ee8ae6",
"output_sha256": "83eba3422733039e35adac26da5020d02b0df640746b5cb5c80fd82c57a09120",
"matched_output_lines": 244844
}
],
"scope": "Actual LivingEntity.checkFallDamage including its base Entity fall handling. Controlled fluid update, onChangedBlock, attribute, state, particle and block landing providers exercise callback order, mutations, server class identity, particle positions/counts, nonfinite arithmetic, climbing cleanup and partial state after failures. Full fluid immersion, enchantment hooks, particle rendering and player/world integration remain pending."
}
@@ -0,0 +1,215 @@
use horizon_core::{geometry::Vec3, position::BlockPos};
use horizon_world::fall_damage::FallDamageWorld;
use horizon_world::living_landing::{LivingLandingWorld, check_living_fall_damage};
use serde_json::Value;
use std::{
fs,
io::{self, Write},
path::Path,
};
fn number(v: &Value) -> f64 {
f64::from_bits(u64::from_str_radix(v.as_str().unwrap(), 16).unwrap())
}
fn bits(v: f64) -> u64 {
if v.is_nan() {
0x7ff8000000000000
} else {
v.to_bits()
}
}
fn support(v: &Value) -> Option<BlockPos> {
if v.is_null() {
None
} else {
Some(BlockPos::new(
v[0].as_i64().unwrap() as i32,
v[1].as_i64().unwrap() as i32,
v[2].as_i64().unwrap() as i32,
))
}
}
struct World<'a> {
input: &'a Value,
out: &'a mut dyn Write,
calls: i64,
fall: f64,
position: Vec3,
support: Option<BlockPos>,
water: bool,
climbable: bool,
}
impl World<'_> {
fn query(&mut self, text: String) -> io::Result<()> {
writeln!(self.out, "{text}")?;
let call = self.calls;
self.calls += 1;
if call == self.input["fail"].as_i64().unwrap() {
Err(io::Error::other("injected fall query failure"))
} else {
Ok(())
}
}
}
impl FallDamageWorld for World<'_> {
type Error = io::Error;
type State = u8;
fn is_in_water(&mut self) -> io::Result<bool> {
self.query("water".into())?;
Ok(self.water)
}
fn fall_distance(&self) -> f64 {
self.fall
}
fn set_fall_distance(&mut self, v: f64) {
self.fall = v;
}
fn fall_on(&mut self, _: u8, p: BlockPos, distance: f64) -> io::Result<()> {
self.query(format!(
"fall-on {} {} {} {:016x}",
p.x,
p.y,
p.z,
bits(distance)
))?;
if self.input["mutate"].as_bool().unwrap() {
self.fall = 17.25;
self.position = Vec3::new(17.0, 18.0, 19.0);
self.support = support(&self.input["after_support"]);
}
Ok(())
}
fn position(&self) -> Vec3 {
self.position
}
fn supporting_block(&self) -> Option<BlockPos> {
self.support
}
fn block_state(&mut self, p: BlockPos) -> io::Result<u8> {
self.query(format!("state {} {} {}", p.x, p.y, p.z))?;
Ok(1)
}
fn hit_ground(&mut self, p: Vec3, s: u8) -> io::Result<()> {
self.query(format!(
"hit-ground {:016x} {:016x} {:016x} {}",
bits(p.x),
bits(p.y),
bits(p.z),
if s == 1 { "queried" } else { "input" }
))
}
fn reset_fall_distance(&mut self) -> io::Result<()> {
self.query("reset".into())?;
self.fall = 0.0;
Ok(())
}
}
impl LivingLandingWorld for World<'_> {
fn update_fluid_interaction(&mut self) -> io::Result<bool> {
self.query("fluid-update".into())?;
self.water = self.input["after_water"].as_bool().unwrap();
if self.input["fluid_mutate"].as_bool().unwrap() {
self.fall = number(&self.input["after_fall"]);
}
Ok(self.input["fluid_result"].as_bool().unwrap())
}
fn is_server_level(&self) -> bool {
self.input["server"].as_bool().unwrap()
}
fn on_changed_block(&mut self, p: BlockPos) -> io::Result<()> {
self.query(format!("changed {} {} {}", p.x, p.y, p.z))?;
if self.input["changed_mutate"].as_bool().unwrap() {
self.fall = number(&self.input["after_fall"]);
self.position = Vec3::new(17.0, 18.0, 19.0);
}
Ok(())
}
fn safe_fall_distance(&mut self) -> io::Result<f64> {
self.query("safe".into())?;
Ok(number(&self.input["safe"]))
}
fn state_is_air(&mut self, _: u8) -> io::Result<bool> {
self.query("air".into())?;
Ok(self.input["air"].as_bool().unwrap())
}
fn block_position(&mut self) -> io::Result<BlockPos> {
self.query("block-position".into())?;
if self.input["block_mutate"].as_bool().unwrap() {
self.position = Vec3::new(31.0, 32.0, 33.0);
}
Ok(support(&self.input["block_position"]).unwrap())
}
fn landing_particles(&mut self, s: u8, p: Vec3, count: i32, speed: f64) -> io::Result<()> {
assert_eq!(s, 0);
self.query(format!(
"particles {:016x} {:016x} {:016x} {} {:016x}",
bits(p.x),
bits(p.y),
bits(p.z),
count,
bits(speed)
))?;
if self.input["particle_mutate"].as_bool().unwrap() {
self.fall = number(&self.input["after_fall"]);
self.water = !self.water;
}
Ok(())
}
fn clear_last_climbable_position(&mut self) {
self.climbable = false;
}
}
fn main() -> Result<(), Box<dyn std::error::Error>> {
let arg = std::env::args().nth(1).expect("private fixture directory");
let dir = Path::new(&arg);
let mut out = io::BufWriter::new(fs::File::create(dir.join("rust.txt"))?);
for line in fs::read_to_string(dir.join("inputs.txt"))?.lines() {
let f: Value = serde_json::from_str(line)?;
let mut world = World {
input: &f,
out: &mut out,
calls: 0,
fall: number(&f["fall"]),
position: Vec3::new(
number(&f["position"][0]),
number(&f["position"][1]),
number(&f["position"][2]),
),
water: f["water"].as_bool().unwrap(),
climbable: true,
support: support(&f["support"]),
};
let result = check_living_fall_damage(
&mut world,
number(&f["dy"]),
f["ground"].as_bool().unwrap(),
0,
support(&f["landing"]).unwrap(),
);
writeln!(
world.out,
"result {}",
if result.is_ok() { "ok" } else { "error" }
)?;
let p = world.position;
writeln!(
world.out,
"final {:016x} {:016x} {:016x} {:016x}",
bits(world.fall),
bits(p.x),
bits(p.y),
bits(p.z)
)?;
if let Some(p) = world.support {
writeln!(world.out, "support {} {} {}", p.x, p.y, p.z)?;
} else {
writeln!(world.out, "support none")?;
}
writeln!(
world.out,
"climbable {} water {}",
world.climbable, world.water
)?;
writeln!(world.out, "queries {}", world.calls)?;
}
Ok(())
}
+1
View File
@@ -29,6 +29,7 @@ pub mod heightmap;
pub mod interaction_shapes;
pub mod liquid_collision;
pub mod living_fall;
pub mod living_landing;
pub mod movement_emission;
pub mod movement_properties;
pub mod movement_restitution;
@@ -0,0 +1,69 @@
//! LivingEntity's fluid refresh and landing effects around base fall handling.
use crate::fall_damage::{FallDamageWorld, check_fall_damage};
use horizon_core::{geometry::Vec3, position::BlockPos};
pub trait LivingLandingWorld: FallDamageWorld {
fn update_fluid_interaction(&mut self) -> Result<bool, Self::Error>;
/// Native level type identity, independent of its client-side flag.
fn is_server_level(&self) -> bool;
fn on_changed_block(&mut self, position: BlockPos) -> Result<(), Self::Error>;
fn safe_fall_distance(&mut self) -> Result<f64, Self::Error>;
fn state_is_air(&mut self, state: Self::State) -> Result<bool, Self::Error>;
fn block_position(&mut self) -> Result<BlockPos, Self::Error>;
/// Dispatch BLOCK particles with zero axis spread and the supplied speed.
fn landing_particles(
&mut self,
state: Self::State,
position: Vec3,
count: i32,
speed: f64,
) -> Result<(), Self::Error>;
fn clear_last_climbable_position(&mut self);
}
/// Preserve callbacks' field mutations and partial state on failure. The
/// landing position and state are the original movement phase's arguments.
pub fn check_living_fall_damage<W: LivingLandingWorld>(
world: &mut W,
dy: f64,
on_ground: bool,
state: W::State,
landing: BlockPos,
) -> Result<(), W::Error> {
if !world.is_in_water()? {
world.update_fluid_interaction()?;
}
if world.is_server_level() && on_ground && world.fall_distance() > 0.0 {
world.on_changed_block(landing)?;
let power = world.fall_distance() + 1.0e-6 - world.safe_fall_distance()?;
let power = f64::from((power.floor() as i32).max(0));
if power > 0.0 && !world.state_is_air(state)? {
let mut position = world.position();
let current = world.block_position()?;
if landing.x != current.x || landing.z != current.z {
let dx = position.x - f64::from(landing.x) - 0.5;
let dz = position.z - f64::from(landing.z) - 0.5;
// Math.max propagates NaN; Rust's max selects the other operand.
let scale = if dx.is_nan() || dz.is_nan() {
f64::NAN
} else {
dx.abs().max(dz.abs())
};
position.x = f64::from(landing.x) + 0.5 + dx / scale * 0.5;
position.z = f64::from(landing.z) + 0.5 + dz / scale * 0.5;
}
let magnitude = (f64::from(0.2f32) + power / 15.0).min(2.5);
world.landing_particles(
state,
position,
(150.0 * magnitude) as i32,
f64::from(0.15f32),
)?;
}
}
check_fall_damage(world, dy, on_ground, state, landing)?;
if on_ground {
world.clear_last_climbable_position();
}
Ok(())
}
+16
View File
@@ -820,3 +820,19 @@ all **35,723 states**, tag reloads, server/client distinctions, player type chec
portal delays, changing above-fluid states, shared cache reuse and failures.
Targeted Clippy passes. World storage is controlled; live entity movement and
complete player/world gameplay integration remain pending.
`living_landing` wraps base fall handling with fluid refresh, server-only changed
block notification and landing particles, followed by climbing-position cleanup.
Particle positions preserve the native support-block edge projection and capture
entity coordinates before later callbacks. Attribute, fluid, particle and block
callbacks can mutate fields; failures retain preceding writes.
`python3 tools/test_living_landing_parity.py` invokes actual
`LivingEntity.checkFallDamage` and its base implementation. The
[living landing report](../benchmarks/rust-living-landing-2026-10-07/scoped-parity.json)
records **244,844 matching lines per JAR**, including server/nonserver levels,
water changes, numeric boundaries, particle locations/counts, callback failures,
field mutations and climbing cleanup. Targeted Clippy passes. Fluid immersion,
changed-block enchantments, particle rendering and live gameplay binding remain
pending; those providers are controlled by this gate.
@@ -0,0 +1,90 @@
package dev.horizonlab.parity;
import com.google.gson.*;
import java.io.*;
import java.lang.reflect.*;
import java.nio.file.*;
import java.util.*;
import net.minecraft.core.*;
import net.minecraft.world.entity.*;
import net.minecraft.world.level.*;
import net.minecraft.world.level.block.*;
import net.minecraft.world.level.block.state.*;
import net.minecraft.world.level.gameevent.*;
import net.minecraft.world.phys.Vec3;
/** Actual LivingEntity landing wrapper with native base fall handling. */
public final class VanillaLivingLandingOracle {
static final class Stop extends RuntimeException {}
static double number(JsonElement v){return Double.longBitsToDouble(Long.parseUnsignedLong(v.getAsString(),16));}
static Optional<BlockPos> support(JsonElement value){if(value.isJsonNull())return Optional.empty();var a=value.getAsJsonArray();return Optional.of(new BlockPos(a.get(0).getAsInt(),a.get(1).getAsInt(),a.get(2).getAsInt()));}
static Field positionField,supportField,climbableField;
static final class Data {
JsonObject input;PrintWriter out;int calls;Actor actor;boolean water;BlockState inputState;
void setPosition(Vec3 p){try{positionField.set(actor,p);}catch(ReflectiveOperationException e){throw new AssertionError(e);}}
void query(String text){out.println(text);if(calls++==input.get("fail").getAsInt())throw new Stop();}
void mutate() {if(input.get("mutate").getAsBoolean()){try{actor.fallDistance=17.25;positionField.set(actor,new Vec3(17,18,19));supportField.set(actor,support(input.get("after_support")));}catch(ReflectiveOperationException e){throw new AssertionError(e);}}}
}
static final class FallBlock extends Block {
Data d;FallBlock(){super(BlockBehaviour.Properties.of());}
@Override public void fallOn(net.minecraft.world.level.Level level,BlockState state,BlockPos p,Entity entity,double distance){d.query(String.format("fall-on %d %d %d %016x",p.getX(),p.getY(),p.getZ(),Double.doubleToLongBits(distance)));d.mutate();}
}
static final class State extends BlockState {Block block;Data d;@Override public boolean isAir(){d.query("air");return d.input.get("air").getAsBoolean();}State(){super(null,null,null);}@Override public Block getBlock(){return block;}}
static final class Level extends net.minecraft.server.level.ServerLevel {
Data d;BlockState queried;
private Level(){super(null,null,null,null,null,null,false,0L,List.of(),false);}
@Override public <T extends net.minecraft.core.particles.ParticleOptions> int sendParticles(T particle,double x,double y,double z,int count,double dx,double dy,double dz,double speed){if(!(particle instanceof net.minecraft.core.particles.BlockParticleOption b)||b.getState()!=d.inputState||b.getType()!=net.minecraft.core.particles.ParticleTypes.BLOCK||dx!=0||dy!=0||dz!=0)throw new AssertionError(particle);d.query(String.format("particles %016x %016x %016x %d %016x",Double.doubleToLongBits(x),Double.doubleToLongBits(y),Double.doubleToLongBits(z),count,Double.doubleToLongBits(speed)));if(d.input.get("particle_mutate").getAsBoolean()){d.actor.fallDistance=number(d.input.get("after_fall"));d.water=!d.water;}return -1;}
@Override public BlockState getBlockState(BlockPos p){d.query("state "+p.getX()+" "+p.getY()+" "+p.getZ());return queried;}
@Override public void gameEvent(Holder<GameEvent> event,Vec3 p,GameEvent.Context context){if(event!=GameEvent.HIT_GROUND)throw new AssertionError(event);d.query(String.format("hit-ground %016x %016x %016x %s",Double.doubleToLongBits(p.x),Double.doubleToLongBits(p.y),Double.doubleToLongBits(p.z),context.affectedState()==queried?"queried":"input"));}
}
static final class ClientLevel extends net.minecraft.world.level.Level {
Data d;BlockState queried;ClientLevel(){super(null,null,null,null,false,false,0L,0);}
@Override public BlockState getBlockState(BlockPos p){d.query("state "+p.getX()+" "+p.getY()+" "+p.getZ());return queried;}
@Override public void gameEvent(Holder<GameEvent> event,Vec3 p,GameEvent.Context context){if(event!=GameEvent.HIT_GROUND)throw new AssertionError(event);d.query(String.format("hit-ground %016x %016x %016x %s",Double.doubleToLongBits(p.x),Double.doubleToLongBits(p.y),Double.doubleToLongBits(p.z),context.affectedState()==queried?"queried":"input"));}
@Override public net.minecraft.world.clock.ClockManager clockManager(){throw new UnsupportedOperationException();}
@Override public void destroyBlockProgress(int p0,net.minecraft.core.BlockPos p1,int p2){throw new UnsupportedOperationException();}
@Override public java.util.Collection dragonParts(){throw new UnsupportedOperationException();}
@Override public net.minecraft.world.flag.FeatureFlagSet enabledFeatures(){throw new UnsupportedOperationException();}
@Override public net.minecraft.world.attribute.EnvironmentAttributeSystem environmentAttributes(){throw new UnsupportedOperationException();}
@Override public void explode(net.minecraft.world.entity.Entity p0,net.minecraft.world.damagesource.DamageSource p1,net.minecraft.world.level.ExplosionDamageCalculator p2,double p3,double p4,double p5,float p6,boolean p7,net.minecraft.world.level.Level.ExplosionInteraction p8,net.minecraft.core.particles.ParticleOptions p9,net.minecraft.core.particles.ParticleOptions p10,net.minecraft.util.random.WeightedList p11,net.minecraft.core.Holder p12){throw new UnsupportedOperationException();}
@Override public java.lang.String gatherChunkSourceStats(){throw new UnsupportedOperationException();}
@Override public net.minecraft.world.ticks.LevelTickAccess getBlockTicks(){throw new UnsupportedOperationException();}
@Override public net.minecraft.world.level.chunk.ChunkSource getChunkSource(){throw new UnsupportedOperationException();}
@Override public net.minecraft.world.level.entity.LevelEntityGetter getEntities(){throw new UnsupportedOperationException();}
@Override public net.minecraft.world.entity.Entity getEntity(int p0){throw new UnsupportedOperationException();}
@Override public net.minecraft.world.ticks.LevelTickAccess getFluidTicks(){throw new UnsupportedOperationException();}
@Override public net.minecraft.world.level.saveddata.maps.MapItemSavedData getMapData(net.minecraft.world.level.saveddata.maps.MapId p0){throw new UnsupportedOperationException();}
@Override public net.minecraft.world.level.storage.LevelData.RespawnData getRespawnData(){throw new UnsupportedOperationException();}
@Override public net.minecraft.world.scores.Scoreboard getScoreboard(){throw new UnsupportedOperationException();}
@Override public int getSeaLevel(){throw new UnsupportedOperationException();}
@Override public net.minecraft.core.Holder getUncachedNoiseBiome(int p0,int p1,int p2){throw new UnsupportedOperationException();}
@Override public net.minecraft.world.level.border.WorldBorder getWorldBorder(){throw new UnsupportedOperationException();}
@Override public void levelEvent(net.minecraft.world.entity.Entity p0,int p1,net.minecraft.core.BlockPos p2,int p3){throw new UnsupportedOperationException();}
@Override public void playSeededSound(net.minecraft.world.entity.Entity p0,net.minecraft.world.entity.Entity p1,net.minecraft.core.Holder p2,net.minecraft.sounds.SoundSource p3,float p4,float p5,long p6){throw new UnsupportedOperationException();}
@Override public void playSeededSound(net.minecraft.world.entity.Entity p0,double p1,double p2,double p3,net.minecraft.core.Holder p4,net.minecraft.sounds.SoundSource p5,float p6,float p7,long p8){throw new UnsupportedOperationException();}
@Override public java.util.List players(){throw new UnsupportedOperationException();}
@Override public net.minecraft.world.item.crafting.RecipeAccess recipeAccess(){throw new UnsupportedOperationException();}
@Override public void sendBlockUpdated(net.minecraft.core.BlockPos p0,net.minecraft.world.level.block.state.BlockState p1,net.minecraft.world.level.block.state.BlockState p2,int p3){throw new UnsupportedOperationException();}
@Override public void setRespawnData(net.minecraft.world.level.storage.LevelData.RespawnData p0){throw new UnsupportedOperationException();}
@Override public net.minecraft.world.TickRateManager tickRateManager(){throw new UnsupportedOperationException();}
}
static final class Actor extends net.minecraft.world.entity.decoration.ArmorStand {
Data d;Actor(){super(null,null);}void configure(net.minecraft.world.level.Level level){setLevel(level);}
@Override public boolean isInWater(){d.query("water");return d.water;}
@Override public void resetFallDistance(){d.query("reset");super.resetFallDistance();}
@Override public boolean updateFluidInteraction(){d.query("fluid-update");d.water=d.input.get("after_water").getAsBoolean();if(d.input.get("fluid_mutate").getAsBoolean())fallDistance=number(d.input.get("after_fall"));return d.input.get("fluid_result").getAsBoolean();}
@Override protected void onChangedBlock(net.minecraft.server.level.ServerLevel level,BlockPos p){d.query("changed "+p.getX()+" "+p.getY()+" "+p.getZ());if(d.input.get("changed_mutate").getAsBoolean()){fallDistance=number(d.input.get("after_fall"));d.setPosition(new Vec3(17,18,19));}}
@Override public double getAttributeValue(Holder<net.minecraft.world.entity.ai.attributes.Attribute> attribute){if(attribute!=net.minecraft.world.entity.ai.attributes.Attributes.SAFE_FALL_DISTANCE)throw new AssertionError(attribute);d.query("safe");return number(d.input.get("safe"));}
@Override public BlockPos blockPosition(){d.query("block-position");if(d.input.get("block_mutate").getAsBoolean())d.setPosition(new Vec3(31,32,33));return support(d.input.get("block_position")).orElseThrow();}
void check(double dy,boolean ground,BlockState state,BlockPos p){checkFallDamage(dy,ground,state,p);}
}
public static void main(String[] args)throws Exception {
net.minecraft.SharedConstants.tryDetectVersion();net.minecraft.server.Bootstrap.bootStrap();
var allocator=Class.forName("sun.misc.Unsafe");var field=allocator.getDeclaredField("theUnsafe");field.setAccessible(true);var unsafe=field.get(null);var allocate=allocator.getMethod("allocateInstance",Class.class);positionField=Entity.class.getDeclaredField("position");positionField.setAccessible(true);supportField=Entity.class.getDeclaredField("mainSupportingBlockPos");supportField.setAccessible(true);climbableField=LivingEntity.class.getDeclaredField("lastClimbablePos");climbableField.setAccessible(true);Path dir=Path.of(args[1]);
try(var out=new PrintWriter(Files.newBufferedWriter(dir.resolve(args[0]+".txt")));var lines=Files.lines(dir.resolve("inputs.txt"))){
for(var line:(Iterable<String>)lines::iterator){
var f=JsonParser.parseString(line).getAsJsonObject();var d=new Data();d.input=f;d.out=out;d.water=f.get("water").getAsBoolean();net.minecraft.world.level.Level level;if(f.get("server").getAsBoolean()){var l=(Level)allocate.invoke(unsafe,Level.class);l.d=d;l.queried=Blocks.STONE.defaultBlockState();level=l;}else{var l=(ClientLevel)allocate.invoke(unsafe,ClientLevel.class);l.d=d;l.queried=Blocks.STONE.defaultBlockState();level=l;}var entity=(Actor)allocate.invoke(unsafe,Actor.class);entity.d=d;d.actor=entity;entity.configure(level);entity.fallDistance=number(f.get("fall"));var xyz=f.getAsJsonArray("position");positionField.set(entity,new Vec3(number(xyz.get(0)),number(xyz.get(1)),number(xyz.get(2))));climbableField.set(entity,Optional.of(new BlockPos(7,8,9)));supportField.set(entity,support(f.get("support")));var block=(FallBlock)allocate.invoke(unsafe,FallBlock.class);block.d=d;var state=(State)allocate.invoke(unsafe,State.class);state.block=block;state.d=d;d.inputState=state;
try{entity.check(number(f.get("dy")),f.get("ground").getAsBoolean(),state,support(f.get("landing")).orElseThrow());out.println("result ok");}catch(Stop error){out.println("result error");}
var p=(Vec3)positionField.get(entity);out.printf("final %016x %016x %016x %016x%n",Double.doubleToLongBits(entity.fallDistance),Double.doubleToLongBits(p.x),Double.doubleToLongBits(p.y),Double.doubleToLongBits(p.z));var selected=(Optional<?>)supportField.get(entity);if(selected.isEmpty())out.println("support none");else{var pos=(BlockPos)selected.get();out.println("support "+pos.getX()+" "+pos.getY()+" "+pos.getZ());}out.println("climbable "+((Optional<?>)climbableField.get(entity)).isPresent()+" water "+d.water);out.println("queries "+d.calls);
}
}
}
}
+82
View File
@@ -0,0 +1,82 @@
#!/usr/bin/env python3
"""Compare LivingEntity landing wrapper and base fall callbacks with vanilla."""
import argparse
import json
import os
from pathlib import Path
import platform
import subprocess
import random
import struct
import math
try:
from . import lab, test_rust_parity as base
except ImportError:
import lab
import test_rust_parity as base
def corpus():
def bits(v): return struct.pack('>d',float(v)).hex()
def row(**kw):
f=dict(fall=6,dy=-.08,ground=True,water=False,after_water=False,support=None,after_support=None,mutate=False,fail=-1,server=True,fluid_mutate=False,fluid_result=False,changed_mutate=False,particle_mutate=False,block_mutate=False,after_fall=12,safe=3,air=False,position=[.3,1.5,-.3],block_position=[0,1,-1],landing=[3,4,5])
f.update(kw)
for k in ['fall','dy','after_fall','safe']:f[k]=bits(f[k])
f['position']=[bits(v) for v in f['position']]
return f
values=[-float('inf'),-1e40,-1,-1e-46,-0.0,0,1e-46,2.9999989,2.999999,3,3.999999,4,4.000001,6,7,37.5,100,1e40,float('inf'),float('nan')]
for fall in values:
for safe in values:
for server in [False,True]:
for ground in [False,True]:
for water in [False,True]:
for after_water in [False,True]:
for air in [False,True]:yield row(fall=fall,safe=safe,server=server,ground=ground,water=water,after_water=after_water,air=air)
positions=[[.3,1.5,-.3],[3.5,0,5.5],[3.5,0,10],[8,0,5.5],[4.5,0,6.5],[float('nan'),1,5.5],[3.5,float('inf'),float('nan')],[float('inf'),0,-float('inf')],[30000000.5,-64,-30000000.5],[-0.0,0,-0.0]]
for position in positions:
for landing in [[3,4,5],[0,1,-1],[30000000,-64,-30000000],[-2147483648,0,2147483647]]:
for block_position in [landing,[0,0,0]]:
for block_mutate in [False,True]:yield row(position=position,landing=landing,block_position=block_position,block_mutate=block_mutate)
for field in ['fluid_mutate','changed_mutate','particle_mutate','block_mutate','mutate']:
for after in values:
for fail in range(-1,14):yield row(**{field:True},after_fall=after,fail=fail,support=[17,-64,-33],after_support=[31,15,-1])
rng=random.Random(0x4c414e44)
flags=['ground','water','after_water','mutate','server','fluid_mutate','fluid_result','changed_mutate','particle_mutate','block_mutate','air']
for _ in range(12000):
f={k:bool(rng.randrange(2)) for k in flags}
for k in ['fall','dy','after_fall','safe']:f[k]=rng.choice(values) if rng.randrange(3)==0 else rng.uniform(-5,80)
yield row(**f,position=rng.choice(positions),support=rng.choice([None,[17,-64,-33]]),after_support=rng.choice([None,[31,15,-1]]),fail=rng.choice([-1,-1,*range(14)]))
def main():
parser=argparse.ArgumentParser(description=__doc__);parser.add_argument('--prepare',action='store_true');args=parser.parse_args()
build=lab.LOCAL/'rust-living-landing-parity';build.mkdir(parents=True,exist_ok=True)
report_path=build/'report.json';report_path.unlink(missing_ok=True)
if args.prepare:lab.prepare()
lock,jars,libraries=base.verify_artifacts()
oracle=lab.ROOT/'mods/horizon/src/test/java/dev/horizonlab/parity/VanillaLivingLandingOracle.java'
sources=[oracle,Path(__file__),lab.ROOT/'tools/test_rust_parity.py',lab.ROOT/'tools/lab.py',lab.ROOT/'Cargo.toml',lab.ROOT/'Cargo.lock',lab.ROOT/'crates/horizon-core/Cargo.toml',lab.ROOT/'crates/horizon-world/Cargo.toml',*sorted((lab.ROOT/'crates/horizon-core/src').rglob('*.rs')),*sorted((lab.ROOT/'crates/horizon-world/src').rglob('*.rs')),lab.ROOT/'crates/horizon-world/examples/living_landing_fixture.rs']
tested_sources=base.source_hashes(sources)
classes=build/'classes';classes.mkdir(exist_ok=True)
def run(command,name):
with (build/(name+'.log')).open('w',encoding='utf-8') as log:base.run(list(map(str,command)),stdout=log,stderr=subprocess.STDOUT)
dependency_path=os.pathsep.join(map(str,libraries))
run(['javac','--release',lock['minecraft']['java_major'],'-proc:none','-cp',str(jars['server'])+os.pathsep+dependency_path,'-d',classes,oracle],'compile-java')
def java(side,mode,path):
run(['java','-Xmx512M','-cp',os.pathsep.join([str(classes),str(jars[side]),dependency_path]),'dev.horizonlab.parity.VanillaLivingLandingOracle',mode,path],side+'-'+mode)
fixtures=list(corpus());inputs=build/'inputs.txt';inputs.write_text(''.join(json.dumps(value,separators=(',',':'))+'\n' for value in fixtures),encoding='utf-8')
fixture_paths=[inputs];tested_fixtures=base.source_hashes(fixture_paths)
run(['cargo','build','--release','-p','horizon-world','--example','living_landing_fixture'],'compile-rust')
metadata=json.loads(subprocess.check_output(['cargo','metadata','--no-deps','--format-version','1'],cwd=lab.ROOT,text=True))
binary=Path(metadata['target_directory'])/'release/examples'/('living_landing_fixture.exe' if os.name=='nt' else 'living_landing_fixture')
run([binary,build],'rust');references=[]
for side,jar in jars.items():
java(side,side,build);reference=build/f'{side}.txt';matched=base.compare(reference,build/'rust.txt')
references.append({'side':side,'jar_sha256':lab.digest(jar,'sha256'),'output_sha256':lab.digest(reference,'sha256'),'matched_output_lines':matched})
print(f'{side}: {matched:,} living-landing output lines match Rust',flush=True)
base.ensure_sources_unchanged(sources,tested_sources);base.ensure_sources_unchanged(fixture_paths,tested_fixtures)
report={'schema':1,'minecraft':lock['minecraft']['version'],'manifest_sha1':lock['minecraft']['manifest_sha1'],'platform':platform.platform(),'java':subprocess.check_output(['java','--version'],text=True).splitlines()[0],'rustc':subprocess.check_output(['rustc','--version'],text=True).strip(),'fixtures':len(fixtures),'sources_sha256':tested_sources,'fixtures_sha256':tested_fixtures,'rust_output_sha256':lab.digest(build/'rust.txt','sha256'),'references':references,'scope':'Actual LivingEntity.checkFallDamage including its base Entity fall handling. Controlled fluid update, onChangedBlock, attribute, state, particle and block landing providers exercise callback order, mutations, server class identity, particle positions/counts, nonfinite arithmetic, climbing cleanup and partial state after failures. Full fluid immersion, enchantment hooks, particle rendering and player/world integration remain pending.'}
lab.write_json(report_path,report);print(f'Scoped living landing parity passed: {report_path}')
if __name__=='__main__':main()