Match movement distance and step swim flap emission dispatch

This commit is contained in:
Ludovic Marie
2026-10-07 01:43:13 +02:00
parent 6cea2835e0
commit 4c12bb22cc
7 changed files with 757 additions and 0 deletions
@@ -0,0 +1,219 @@
{
"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": 53036,
"sources_sha256": {
"mods/horizon/src/test/java/dev/horizonlab/parity/VanillaMovementEmissionOracle.java": "8f19d72b0d7ef53dead2be1b97a36c129182bb9849d75c9eddca5075112f4edf",
"tools/test_movement_emission_parity.py": "eed6f8507308a7c7a785abe4bb017bec0e206f63672834e00957b208066915b9",
"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_obstruction.rs": "a4b28a19bf83b23611c59d94535c488db37e4aee24b22c973c545910894a809f",
"crates/horizon-world/src/fall_damage.rs": "8c28e2933493569fe05dea22332024287d28b0746b514dd465681040b003296b",
"crates/horizon-world/src/fluid.rs": "a0a1f34ba94d8b59dc975becee8952960764d67ae08cf2d6a8bcd40a614b15c4",
"crates/horizon-world/src/ground_support.rs": "3e371d74ecede4d30beef0a7e8a2ee33463584a5b1ed3d852b394efcce1ffbe0",
"crates/horizon-world/src/heightmap.rs": "a5edef082b5af40d814421ea17e129b3b06d70abee6434ed263e4bba58571698",
"crates/horizon-world/src/lib.rs": "c875482eb6dd1783e01b3c223464b08e9b6d52c259ac83b17f960b5c6558068e",
"crates/horizon-world/src/liquid_collision.rs": "0f0a83b04e5a5bc9d98ed05fde4d6584ba69642e93522e726e4cfc325d6fcb1d",
"crates/horizon-world/src/living_fall.rs": "da0f176f4731e1c86e08bfeee8167742fd1b86380ff0e9304c13c1ce3f1c67ab",
"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/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/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/movement_emission_fixture.rs": "d511ef20357c6231b13f85c5bf8d7554d080ea0ce927247a935aca38c65c1eca"
},
"fixtures_sha256": {
".local/rust-movement-emission-parity/inputs.txt": "28788ffb694d89eda22a5c3ef510d2258ffe0827e53e526ae83bc5bd76d8e6e6"
},
"rust_output_sha256": "eb84dc864380d2b7830b71ec8bb7391a86f79d0eb6e9f9872e495b8601cbf544",
"references": [
{
"side": "client",
"jar_sha256": "4508d006323f24fa02876310c192d739af56516eb259000ac50f0909a68c9a2d",
"output_sha256": "eb84dc864380d2b7830b71ec8bb7391a86f79d0eb6e9f9872e495b8601cbf544",
"matched_output_lines": 598611
},
{
"side": "server",
"jar_sha256": "a362163eec5d1612d520772bc16e5b39c09e3b234fdc045f56bf544284ee8ae6",
"output_sha256": "eb84dc864380d2b7830b71ec8bb7391a86f79d0eb6e9f9872e495b8601cbf544",
"matched_output_lines": 598611
}
],
"scope": "Actual Entity movement emission, private block vibration/step dispatch, flapping dispatch and native next-step arithmetic. Compares distance accumulation, climbable/powder predicates, legacy/current support ordering, ground/crouch/rail/swimming guards, sound/event emissions, float boundaries, NaN/infinity, callback mutations and partial state on failures. Block predicates, world reads and sound callbacks are controlled; crystal sound predicate is false in this scope. Registered sound selection, audio playback, full entity movement and gameplay remain separate work."
}
@@ -0,0 +1,222 @@
use horizon_core::{geometry::Vec3, position::BlockPos};
use horizon_world::movement_emission::*;
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 single(v: &Value) -> f32 {
f32::from_bits(u32::from_str_radix(v.as_str().unwrap(), 16).unwrap())
}
fn vector(v: &Value) -> Vec3 {
Vec3::new(number(&v[0]), number(&v[1]), number(&v[2]))
}
fn position(v: &Value) -> BlockPos {
BlockPos::new(
v[0].as_i64().unwrap() as i32,
v[1].as_i64().unwrap() as i32,
v[2].as_i64().unwrap() as i32,
)
}
fn pos(p: BlockPos) -> String {
format!("{} {} {}", p.x, p.y, p.z)
}
fn bits(v: f64) -> u64 {
if v.is_nan() {
0x7ff8000000000000
} else {
v.to_bits()
}
}
fn fbits(v: f32) -> u32 {
if v.is_nan() { 0x7fc00000 } else { v.to_bits() }
}
fn emission(v: &Value) -> MovementEmission {
[
MovementEmission::None,
MovementEmission::Sounds,
MovementEmission::Events,
MovementEmission::All,
][v.as_u64().unwrap() as usize]
}
struct World<'a> {
f: &'a Value,
out: &'a mut dyn Write,
calls: i64,
distances: MovementDistances,
}
impl World<'_> {
fn flag(&self, n: &str) -> bool {
self.f[n].as_bool().unwrap()
}
fn query(&mut self, s: String) -> io::Result<()> {
writeln!(self.out, "{s}")?;
let call = self.calls;
self.calls += 1;
if call == self.f["fail"].as_i64().unwrap() {
Err(io::Error::other("injected emission failure"))
} else {
Ok(())
}
}
fn mutate(&mut self) {
if self.flag("mutate") {
self.distances = MovementDistances {
movement: single(&self.f["mutated_move"]),
flight: single(&self.f["mutated_fly"]),
next_step: single(&self.f["mutated_next"]),
};
}
}
fn predicate(&mut self, n: &str, key: &str, s: usize) -> io::Result<bool> {
self.query(format!("{n} {s}"))?;
Ok(self.f["states"][s][key].as_bool().unwrap())
}
fn boolean(&mut self, n: &str) -> io::Result<bool> {
self.query(n.into())?;
Ok(self.flag(n))
}
}
impl MovementEmissionWorld for World<'_> {
type Error = io::Error;
type State = usize;
fn distances(&self) -> MovementDistances {
self.distances
}
fn set_movement_distance(&mut self, v: f32) {
self.distances.movement = v;
}
fn set_flight_distance(&mut self, v: f32) {
self.distances.flight = v;
}
fn set_next_step(&mut self, v: f32) {
self.distances.next_step = v;
}
fn on_position(&mut self) -> io::Result<BlockPos> {
self.query("on-pos".into())?;
Ok(position(&self.f["on_pos"]))
}
fn block_state(&mut self, p: BlockPos) -> io::Result<usize> {
self.query(format!("state {}", pos(p)))?;
Ok(1)
}
fn is_air(&mut self, s: usize) -> io::Result<bool> {
self.predicate("air", "air", s)
}
fn climbable_tag(&mut self, s: usize) -> io::Result<bool> {
self.predicate("climbable", "climbable", s)
}
fn is_powder_snow(&mut self, s: usize) -> io::Result<bool> {
self.predicate("powder", "powder", s)
}
fn on_ground(&mut self) -> io::Result<bool> {
self.boolean("ground")
}
fn crouching(&mut self) -> io::Result<bool> {
self.boolean("crouch")
}
fn on_rails(&mut self) -> io::Result<bool> {
self.boolean("rails")
}
fn swimming(&mut self) -> io::Result<bool> {
self.boolean("swimming")
}
fn in_water(&mut self) -> io::Result<bool> {
self.boolean("water")
}
fn walking_step_sound(&mut self, p: BlockPos, s: usize) -> io::Result<()> {
self.query(format!("walking {} {s}", pos(p)))?;
self.mutate();
self.query(format!("crystal {s}"))
}
fn step_event(&mut self, s: usize) -> io::Result<()> {
let p = vector(&self.f["position"]);
self.query(format!(
"step {s} {:016x} {:016x} {:016x}",
bits(p.x),
bits(p.y),
bits(p.z)
))?;
self.mutate();
Ok(())
}
fn next_step(&mut self) -> io::Result<f32> {
self.query("next-step".into())?;
Ok(self.distances.next_step())
}
fn water_swim_sound(&mut self) -> io::Result<()> {
self.query("water-sound".into())?;
self.mutate();
Ok(())
}
fn entity_event(&mut self, e: MovementEvent) -> io::Result<()> {
self.query(format!(
"event {}",
match e {
MovementEvent::Step => "step",
MovementEvent::Swim => "swim",
MovementEvent::Flap => "flap",
}
))?;
self.mutate();
Ok(())
}
fn flapping(&mut self) -> io::Result<bool> {
self.boolean("flapping")
}
fn on_flap(&mut self) -> io::Result<()> {
self.query("on-flap".into())?;
self.mutate();
Ok(())
}
fn movement_emission(&mut self) -> io::Result<MovementEmission> {
self.query("emission".into())?;
Ok(emission(&self.f["flap_emission"]))
}
}
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 w = World {
f: &f,
out: &mut out,
calls: 0,
distances: MovementDistances {
movement: single(&f["move"]),
flight: single(&f["fly"]),
next_step: single(&f["next"]),
},
};
let p = position(&f["legacy"]);
let m = vector(&f["movement"]);
if w.flag("block") {
let sounds = w.flag("sounds");
let events = w.flag("events");
match block_movement_effects(&mut w, p, 0, sounds, events, m) {
Ok(v) => writeln!(w.out, "result {v}")?,
Err(_) => writeln!(w.out, "error")?,
}
} else {
match apply_movement_emission(&mut w, emission(&f["emission"]), m, p, 0) {
Ok(()) => writeln!(w.out, "ok")?,
Err(_) => writeln!(w.out, "error")?,
}
}
writeln!(
w.out,
"distances {:08x} {:08x} {:08x}",
fbits(w.distances.movement),
fbits(w.distances.flight),
fbits(w.distances.next_step)
)?;
writeln!(w.out, "queries {}", w.calls)?;
}
Ok(())
}
+1
View File
@@ -26,6 +26,7 @@ pub mod ground_support;
pub mod heightmap;
pub mod liquid_collision;
pub mod living_fall;
pub mod movement_emission;
pub mod movement_properties;
pub mod movement_restitution;
pub mod movement_support;
@@ -0,0 +1,158 @@
//! Movement distances, footsteps, swimming and flapping event dispatch.
use horizon_core::{geometry::Vec3, position::BlockPos};
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum MovementEmission {
None,
Sounds,
Events,
All,
}
impl MovementEmission {
pub fn sounds(self) -> bool {
matches!(self, Self::Sounds | Self::All)
}
pub fn events(self) -> bool {
matches!(self, Self::Events | Self::All)
}
pub fn anything(self) -> bool {
self != Self::None
}
}
#[derive(Clone, Copy, Debug, Default)]
pub struct MovementDistances {
pub movement: f32,
pub flight: f32,
pub next_step: f32,
}
impl MovementDistances {
/// Java narrows to int, adds with wrapping overflow, then widens to float.
pub fn next_step(&self) -> f32 {
(self.movement as i32).wrapping_add(1) as f32
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum MovementEvent {
Step,
Swim,
Flap,
}
pub trait MovementEmissionWorld {
type Error;
type State: Copy;
fn distances(&self) -> MovementDistances;
fn set_movement_distance(&mut self, value: f32);
fn set_flight_distance(&mut self, value: f32);
fn set_next_step(&mut self, value: f32);
fn on_position(&mut self) -> Result<BlockPos, Self::Error>;
fn block_state(&mut self, p: BlockPos) -> Result<Self::State, Self::Error>;
fn is_air(&mut self, s: Self::State) -> Result<bool, Self::Error>;
fn climbable_tag(&mut self, s: Self::State) -> Result<bool, Self::Error>;
fn is_powder_snow(&mut self, s: Self::State) -> Result<bool, Self::Error>;
fn on_ground(&mut self) -> Result<bool, Self::Error>;
fn crouching(&mut self) -> Result<bool, Self::Error>;
fn on_rails(&mut self) -> Result<bool, Self::Error>;
fn swimming(&mut self) -> Result<bool, Self::Error>;
fn in_water(&mut self) -> Result<bool, Self::Error>;
fn walking_step_sound(&mut self, p: BlockPos, s: Self::State) -> Result<(), Self::Error>;
/// STEP uses current entity position and a context including this state.
fn step_event(&mut self, s: Self::State) -> Result<(), Self::Error>;
fn next_step(&mut self) -> Result<f32, Self::Error>;
fn water_swim_sound(&mut self) -> Result<(), Self::Error>;
fn entity_event(&mut self, event: MovementEvent) -> Result<(), Self::Error>;
fn flapping(&mut self) -> Result<bool, Self::Error>;
fn on_flap(&mut self) -> Result<(), Self::Error>;
fn movement_emission(&mut self) -> Result<MovementEmission, Self::Error>;
}
fn climbable<W: MovementEmissionWorld>(world: &mut W, state: W::State) -> Result<bool, W::Error> {
Ok(world.climbable_tag(state)? || world.is_powder_snow(state)?)
}
pub fn block_movement_effects<W: MovementEmissionWorld>(
world: &mut W,
p: BlockPos,
state: W::State,
sounds: bool,
events: bool,
movement: Vec3,
) -> Result<bool, W::Error> {
if world.is_air(state)? {
return Ok(false);
}
let climbable = climbable(world, state)?;
if (world.on_ground()?
|| climbable
|| world.crouching()? && movement.y == 0.0
|| world.on_rails()?)
&& !world.swimming()?
{
if sounds {
world.walking_step_sound(p, state)?;
}
if events {
world.step_event(state)?;
}
Ok(true)
} else {
Ok(false)
}
}
pub fn process_flapping_movement<W: MovementEmissionWorld>(world: &mut W) -> Result<(), W::Error> {
if world.flapping()? {
world.on_flap()?;
if world.movement_emission()?.events() {
world.entity_event(MovementEvent::Flap)?;
}
}
Ok(())
}
/// The caller applies Entity.move's authority, emission and passenger guards.
/// Provider callbacks can change distance fields; subsequent reads see changes.
pub fn apply_movement_emission<W: MovementEmissionWorld>(
world: &mut W,
emission: MovementEmission,
movement: Vec3,
legacy_position: BlockPos,
legacy_state: W::State,
) -> Result<(), W::Error> {
let full = (movement.length() * f64::from(0.6f32)) as f32;
let horizontal =
((movement.x * movement.x + movement.z * movement.z).sqrt() * f64::from(0.6f32)) as f32;
let p = world.on_position()?;
let state = world.block_state(p)?;
let climbable = climbable(world, state)?;
world.set_movement_distance(
world.distances().movement + if climbable { full } else { horizontal },
);
world.set_flight_distance(world.distances().flight + full);
if world.distances().movement > world.distances().next_step && !world.is_air(state)? {
let same = p == legacy_position;
// The native legacy call uses `same` for events even for sound-only emission.
let mut emitted = block_movement_effects(
world,
legacy_position,
legacy_state,
emission.sounds(),
same,
movement,
)?;
if !same {
emitted |= block_movement_effects(world, p, state, false, emission.events(), movement)?;
}
if emitted {
let next = world.next_step()?;
world.set_next_step(next);
} else if world.in_water()? {
let next = world.next_step()?;
world.set_next_step(next);
if emission.sounds() {
world.water_swim_sound()?;
}
if emission.events() {
world.entity_event(MovementEvent::Swim)?;
}
}
} else if world.is_air(state)? {
process_flapping_movement(world)?;
}
Ok(())
}
+16
View File
@@ -721,3 +721,19 @@ NaN/infinity, failed writes, callback failures, egg hatch preservation and
farmland collision pushes. Targeted Clippy passes. Actual health, live entity
selection/storage, teleport/network behavior, audio playback and full movement
lifecycle integration remain separate work.
`movement_emission` accumulates native movement/flight distances, evaluates
climbable and powder-snow states, and dispatches step/swim/flap effects. Legacy
and current support blocks retain their separate sound/event rules. Threshold
updates use Java float-to-int saturation and wrapping integer addition, and
sound/event callbacks can change fields observed by subsequent operations.
`python3 tools/test_movement_emission_parity.py` compares actual native movement
emission, block vibration dispatch, flapping and next-step arithmetic. The
[movement emission report](../benchmarks/rust-movement-emission-2026-10-07/scoped-parity.json)
records **598,611 matching lines per JAR** across **53,036 fixtures**, including
predicate combinations, sound/event modes, differing support blocks, nonfinite
numbers, mutations and provider failures. Targeted Clippy passes. Sound callbacks
are controlled and the crystal-sound predicate is false in this gate; registered
sound selection, audio playback and full movement integration remain pending.
@@ -0,0 +1,68 @@
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.tags.*;
import net.minecraft.world.entity.*;
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;
/** Invokes native movement emission, including its private block effect dispatch. */
public final class VanillaMovementEmissionOracle {
static final class Stop extends RuntimeException {}
static double number(JsonElement v){return Double.longBitsToDouble(Long.parseUnsignedLong(v.getAsString(),16));}
static float single(JsonElement v){return Float.intBitsToFloat(Integer.parseUnsignedInt(v.getAsString(),16));}
static Vec3 vector(JsonElement v){var a=v.getAsJsonArray();return new Vec3(number(a.get(0)),number(a.get(1)),number(a.get(2)));}
static BlockPos position(JsonElement v){var a=v.getAsJsonArray();return new BlockPos(a.get(0).getAsInt(),a.get(1).getAsInt(),a.get(2).getAsInt());}
static String pos(BlockPos p){return p.getX()+" "+p.getY()+" "+p.getZ();}
static Field moveDist,flyDist,nextStep;
static final class Data {
JsonObject f;PrintWriter out;int calls;Actor actor;State[] states;
boolean flag(String n){return f.get(n).getAsBoolean();}
void query(String text){out.println(text);if(calls++==f.get("fail").getAsInt())throw new Stop();}
void mutate(){if(flag("mutate")){try{moveDist.setFloat(actor,single(f.get("mutated_move")));flyDist.setFloat(actor,single(f.get("mutated_fly")));nextStep.setFloat(actor,single(f.get("mutated_next")));}catch(Exception e){throw new AssertionError(e);}}}
}
static final class State extends BlockState {
Data d;int id;State(){super(null,null,null);}
boolean flag(String n){return d.f.getAsJsonArray("states").get(id).getAsJsonObject().get(n).getAsBoolean();}
@Override public boolean isAir(){d.query("air "+id);return flag("air");}
@Override public boolean is(TagKey<Block> tag){if(tag==BlockTags.CRYSTAL_SOUND_BLOCKS){d.query("crystal "+id);return false;}if(tag!=BlockTags.CLIMBABLE)throw new AssertionError(tag);d.query("climbable "+id);return flag("climbable");}
@Override public boolean is(Block block){if(block!=Blocks.POWDER_SNOW)throw new AssertionError(block);d.query("powder "+id);return flag("powder");}
}
static final class Level extends net.minecraft.server.level.ServerLevel {
Data d;Level(){super(null,null,null,null,null,null,false,0L,List.of(),false);}
@Override public BlockState getBlockState(BlockPos p){d.query("state "+pos(p));return d.states[1];}
@Override public void gameEvent(Holder<GameEvent> e,Vec3 p,GameEvent.Context c){if(e!=GameEvent.STEP||c.sourceEntity()!=d.actor)throw new AssertionError();d.query(String.format("step %d %016x %016x %016x",((State)c.affectedState()).id,Double.doubleToLongBits(p.x),Double.doubleToLongBits(p.y),Double.doubleToLongBits(p.z)));d.mutate();}
}
static final class Actor extends Marker {
Data d;Actor(){super(null,null);}void configure(Level w){setLevel(w);}
@Override public BlockPos getOnPos(){d.query("on-pos");return VanillaMovementEmissionOracle.position(d.f.get("on_pos"));}
@Override public boolean onGround(){d.query("ground");return d.flag("ground");}
@Override public boolean isCrouching(){d.query("crouch");return d.flag("crouch");}
@Override public boolean isOnRails(){d.query("rails");return d.flag("rails");}
@Override public boolean isSwimming(){d.query("swimming");return d.flag("swimming");}
@Override public boolean isInWater(){d.query("water");return d.flag("water");}
@Override protected void playStepSound(BlockPos p,BlockState s){d.query("walking "+pos(p)+" "+((State)s).id);d.mutate();}
@Override protected float nextStep(){d.query("next-step");return super.nextStep();}
@Override protected void waterSwimSound(){d.query("water-sound");d.mutate();}
@Override public void gameEvent(Holder<GameEvent> e){d.query("event "+(e==GameEvent.SWIM?"swim":e==GameEvent.FLAP?"flap":"unknown"));d.mutate();}
@Override protected boolean isFlapping(){d.query("flapping");return d.flag("flapping");}
@Override protected void onFlap(){d.query("on-flap");d.mutate();}
@Override protected MovementEmission getMovementEmission(){d.query("emission");return MovementEmission.values()[d.f.get("flap_emission").getAsInt()];}
}
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);
moveDist=Entity.class.getDeclaredField("moveDist");moveDist.setAccessible(true);flyDist=Entity.class.getDeclaredField("flyDist");flyDist.setAccessible(true);nextStep=Entity.class.getDeclaredField("nextStep");nextStep.setAccessible(true);var position=Entity.class.getDeclaredField("position");position.setAccessible(true);
var apply=Entity.class.getDeclaredMethod("applyMovementEmissionAndPlaySound",Entity.MovementEmission.class,Vec3.class,BlockPos.class,BlockState.class);apply.setAccessible(true);var block=Entity.class.getDeclaredMethod("vibrationAndSoundEffectsFromBlock",BlockPos.class,BlockState.class,boolean.class,boolean.class,Vec3.class);block.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.f=f;d.out=out;var actor=(Actor)allocate.invoke(unsafe,Actor.class);actor.d=d;d.actor=actor;var level=(Level)allocate.invoke(unsafe,Level.class);level.d=d;actor.configure(level);d.states=new State[2];for(int i=0;i<2;i++){var s=(State)allocate.invoke(unsafe,State.class);s.d=d;s.id=i;d.states[i]=s;}
moveDist.setFloat(actor,single(f.get("move")));flyDist.setFloat(actor,single(f.get("fly")));nextStep.setFloat(actor,single(f.get("next")));position.set(actor,vector(f.get("position")));
try{if(f.get("block").getAsBoolean())out.println("result "+block.invoke(actor,position(f.get("legacy")),d.states[0],d.flag("sounds"),d.flag("events"),vector(f.get("movement"))));else{apply.invoke(actor,Entity.MovementEmission.values()[f.get("emission").getAsInt()],vector(f.get("movement")),position(f.get("legacy")),d.states[0]);out.println("ok");}}catch(InvocationTargetException e){if(e.getCause() instanceof Stop)out.println("error");else throw e;}
out.printf("distances %08x %08x %08x%n",Float.floatToIntBits(moveDist.getFloat(actor)),Float.floatToIntBits(flyDist.getFloat(actor)),Float.floatToIntBits(nextStep.getFloat(actor)));out.println("queries "+d.calls);
}}
}
}
+73
View File
@@ -0,0 +1,73 @@
#!/usr/bin/env python3
"""Compare movement distance, step, swim and flap event dispatch 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():
import itertools
def bits(v):return struct.pack('>d',float(v)).hex()
def single(v):return struct.pack('>f',float(v)).hex()
def state(mask):return dict(air=bool(mask&1),climbable=bool(mask&2),powder=bool(mask&4))
def row(**kw):
f=dict(block=False,states=[state(0),state(0)],emission=3,flap_emission=3,movement=list(map(bits,[1.,.2,1.])),position=list(map(bits,[17.3,-63.,-32.3])),on_pos=[17,-64,-33],legacy=[17,-64,-33],ground=True,crouch=False,rails=False,swimming=False,water=False,flapping=False,sounds=True,events=True,move=single(1.),fly=single(0.),next=single(1.),mutate=False,mutated_move=single(12.5),mutated_fly=single(-3.),mutated_next=single(9.),fail=-1)
f.update(kw);return f
for old,new,flags,emission,same in itertools.product(range(8),range(8),range(64),range(4),[False,True]):
yield row(states=[state(old),state(new)],emission=emission,flap_emission=emission,legacy=[17,-64,-33] if same else [17,-65,-33],ground=bool(flags&1),crouch=bool(flags&2),rails=bool(flags&4),swimming=bool(flags&8),water=bool(flags&16),flapping=bool(flags&32))
for st,flags,y,sounds,events in itertools.product(range(8),range(16),[-0.,.1,math.nan],[False,True],[False,True]):
yield row(block=True,states=[state(st),state(0)],movement=list(map(bits,[1,y,1])),ground=bool(flags&1),crouch=bool(flags&2),rails=bool(flags&4),swimming=bool(flags&8),sounds=sounds,events=events)
values=[-math.inf,-1e30,-1.,-0.,0.,1e-38,1e-7,.99999994,1.,1.00000012,2147483520.,2147483648.,1e30,math.inf,math.nan]
for move,next_value,y,st in itertools.product(values,values,[-0.,.1,math.nan],range(4)):
yield row(move=single(move),next=single(next_value),fly=single(move),movement=list(map(bits,[1.,y,1.])),states=[state(0),state(st)])
# Failures before and after state writes and sound/event callbacks.
for old,new,flags,fail,mutate in itertools.product([0,1,2],[0,1,2],range(8),range(-1,27),[False,True]):
yield row(states=[state(old),state(new)],ground=bool(flags&1),water=bool(flags&2),flapping=bool(flags&4),legacy=[17,-65,-33],fail=fail,mutate=mutate)
rng=random.Random(0x454d495353494f4e)
for _ in range(12000):
yield row(block=bool(rng.randrange(2)),states=[state(rng.randrange(8)),state(rng.randrange(8))],emission=rng.randrange(4),flap_emission=rng.randrange(4),movement=[bits(rng.choice(values+[1e200,1e-200])) for _ in range(3)],ground=bool(rng.randrange(2)),crouch=bool(rng.randrange(2)),rails=bool(rng.randrange(2)),swimming=bool(rng.randrange(2)),water=bool(rng.randrange(2)),flapping=bool(rng.randrange(2)),sounds=bool(rng.randrange(2)),events=bool(rng.randrange(2)),move=single(rng.choice(values)),fly=single(rng.choice(values)),next=single(rng.choice(values)),mutate=bool(rng.randrange(2)),mutated_move=single(rng.choice(values)),mutated_fly=single(rng.choice(values)),mutated_next=single(rng.choice(values)),fail=rng.randrange(-1,23))
def main():
parser=argparse.ArgumentParser(description=__doc__);parser.add_argument('--prepare',action='store_true');args=parser.parse_args()
build=lab.LOCAL/'rust-movement-emission-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/VanillaMovementEmissionOracle.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/movement_emission_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.VanillaMovementEmissionOracle',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','movement_emission_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'/('movement_emission_fixture.exe' if os.name=='nt' else 'movement_emission_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:,} movement-emission 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 Entity movement emission, private block vibration/step dispatch, flapping dispatch and native next-step arithmetic. Compares distance accumulation, climbable/powder predicates, legacy/current support ordering, ground/crouch/rail/swimming guards, sound/event emissions, float boundaries, NaN/infinity, callback mutations and partial state on failures. Block predicates, world reads and sound callbacks are controlled; crystal sound predicate is false in this scope. Registered sound selection, audio playback, full entity movement and gameplay remain separate work.'}
lab.write_json(report_path,report);print(f'Scoped movement emission parity passed: {report_path}')
if __name__=='__main__':main()