Match entity move lifecycle ordering and retained movement records

This commit is contained in:
Ludovic Marie committed 2026-10-07 01:52:30 +02:00
1 parent 4c12bb22cc
commit 7e69372517
7 files changed
+1038

No files matched your search

@@ -0,0 +1,220 @@
{
"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": 21656,
"sources_sha256": {
"mods/horizon/src/test/java/dev/horizonlab/parity/VanillaEntityMoveOracle.java": "8e7abd89ab8fe5c64d5608f1e9218127c546a1cfcbe08d784492c9b021cd8e70",
"tools/test_entity_move_parity.py": "3fc4497b90559a59ec8d74f0ebb413585499516dd698d534c2e7db0ad6109cb8",
"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/ground_support.rs": "3e371d74ecede4d30beef0a7e8a2ee33463584a5b1ed3d852b394efcce1ffbe0",
"crates/horizon-world/src/heightmap.rs": "a5edef082b5af40d814421ea17e129b3b06d70abee6434ed263e4bba58571698",
"crates/horizon-world/src/lib.rs": "c27984bbc003c31f60ff60f7bf01d928f3eaf8ba977c3bf0b3102b2cb0faeced",
"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/entity_move_fixture.rs": "dc26dffa594a9c6d7fcc39ae03b254a2a45398db513b34f6b30867818539ff53"
},
"fixtures_sha256": {
".local/rust-entity-move-parity/inputs.txt": "b7dd8870db34cf8a5c178ee0fa709e8a6eff7196b25cc95f931072283bdc69a7"
},
"rust_output_sha256": "23db36cb04444bef7e7bcab15c976a698b247a6de7a0192acf43a592b095b70c",
"references": [
{
"side": "client",
"jar_sha256": "4508d006323f24fa02876310c192d739af56516eb259000ac50f0909a68c9a2d",
"output_sha256": "23db36cb04444bef7e7bcab15c976a698b247a6de7a0192acf43a592b095b70c",
"matched_output_lines": 543653
},
{
"side": "server",
"jar_sha256": "a362163eec5d1612d520772bc16e5b39c09e3b234fdc045f56bf544284ee8ae6",
"output_sha256": "23db36cb04444bef7e7bcab15c976a698b247a6de7a0192acf43a592b095b70c",
"matched_output_lines": 543653
}
],
"scope": "Actual Entity.move with native shape collision resolution (zero step height), native zero-restitution handling and final authority checks. Compares no-physics and piston early exits including signed zero, stuck multiplier reset, edge callback, fall-reset clip endpoints, movement recording cap, collision flag writes, ground/fall callback ordering, removal, passenger emission guard, speed multiplication and failures after partial state changes. Providers control piston/edge decisions, ground support, ray hits, landing effects and sound policy; live world integration and non-passenger movement emission remain separate work."
}
@@ -0,0 +1,418 @@
use horizon_core::{
geometry::{Aabb, Axis, Vec3, VoxelShape, resolve_shape_movement},
position::BlockPos,
};
use horizon_world::{
entity_move::*,
movement_emission::MovementEmission,
movement_restitution::{RestitutionInput, RestitutionWorld, restitute_movement},
registry::BlockStateId,
};
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 bits(v: f64) -> u64 {
if v.is_nan() {
0x7ff8000000000000
} else {
v.to_bits()
}
}
fn vec(label: &str, v: Vec3) -> String {
format!(
"{label} {:016x} {:016x} {:016x}",
bits(v.x),
bits(v.y),
bits(v.z)
)
}
fn bounds(label: &str, b: Aabb) -> String {
format!(
"{label} {:016x} {:016x} {:016x} {:016x} {:016x} {:016x}",
bits(b.min(Axis::X)),
bits(b.min(Axis::Y)),
bits(b.min(Axis::Z)),
bits(b.max(Axis::X)),
bits(b.max(Axis::Y)),
bits(b.max(Axis::Z))
)
}
fn pos(p: BlockPos) -> String {
format!("{} {} {}", p.x, p.y, p.z)
}
struct World<'a> {
f: &'a Value,
out: &'a mut dyn Write,
calls: i64,
position: Vec3,
velocity: Vec3,
stuck: Vec3,
bounds: Aabb,
flags: CollisionFlags,
grounded: bool,
removed: bool,
fall: f64,
history: MovementHistory,
shapes: Vec<VoxelShape>,
}
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 move failure"))
} else {
Ok(())
}
}
}
impl EntityMoveWorld for World<'_> {
type Error = io::Error;
fn no_physics(&self) -> bool {
self.flag("no_physics")
}
fn position(&self) -> Vec3 {
self.position
}
fn set_position(&mut self, p: Vec3) -> io::Result<()> {
self.query(vec("set-position", p))?;
self.bounds = self.bounds.move_by(p.subtract(self.position));
self.position = p;
Ok(())
}
fn record_movement(&mut self, t: MoverType, v: Vec3) -> io::Result<()> {
self.query(vec(&format!("record {}", t as u32), v))
}
fn collision_flags(&self) -> CollisionFlags {
self.flags
}
fn set_collision_flags(&mut self, f: CollisionFlags) {
self.flags = f;
}
fn limit_piston_movement(&mut self, v: Vec3) -> io::Result<Vec3> {
self.query(vec("piston", v))?;
Ok(vector(&self.f["piston_result"]))
}
fn stuck_multiplier(&self) -> Vec3 {
self.stuck
}
fn set_stuck_multiplier(&mut self, v: Vec3) {
self.stuck = v;
}
fn velocity(&mut self) -> io::Result<Vec3> {
self.query("velocity".into())?;
Ok(self.velocity)
}
fn set_velocity(&mut self, v: Vec3) -> io::Result<()> {
self.query(vec("set-velocity", v))?;
self.velocity = v;
Ok(())
}
fn back_off_edge(&mut self, v: Vec3, t: MoverType) -> io::Result<Vec3> {
self.query(vec(&format!("edge {}", t as u32), v))?;
Ok(if self.flag("edge_override") {
vector(&self.f["edge_result"])
} else {
v
})
}
fn collide(&mut self, v: Vec3) -> io::Result<Vec3> {
let b = self.bounds.expand_towards(v);
self.query(bounds("entities", b))?;
if v.length_squared() == 0.0 {
return Ok(v);
}
self.query("border".into())?;
self.query(bounds("border-test", b))?;
self.query(bounds("blocks", b))?;
Ok(resolve_shape_movement(
v,
self.bounds,
&self.shapes.iter().collect::<Vec<_>>(),
))
}
fn fall_distance(&self) -> f64 {
self.fall
}
fn fall_reset_clip(&mut self, from: Vec3, to: Vec3) -> io::Result<bool> {
self.query(vec("clip-from", from))?;
self.query(vec("clip-to", to))?;
Ok(self.flag("clip_hit"))
}
fn reset_fall_distance(&mut self) -> io::Result<()> {
self.query("reset-fall".into())?;
self.fall = 0.0;
Ok(())
}
fn movement_history(&mut self) -> &mut MovementHistory {
&mut self.history
}
fn locally_authoritative(&mut self) -> io::Result<bool> {
let client = self.client_side()?;
self.query(
if client {
"local-client-authority"
} else {
"client-authority"
}
.into(),
)?;
Ok(if client {
self.flag("local_client")
} else {
!self.flag("client_authority")
})
}
fn set_on_ground_with_movement(
&mut self,
below: bool,
horizontal: bool,
v: Vec3,
) -> io::Result<()> {
self.query(vec(&format!("ground-set {below} {horizontal}"), v))?;
self.grounded = below;
self.flags.horizontal = horizontal;
Ok(())
}
fn minor_horizontal_collision(&mut self, v: Vec3) -> io::Result<bool> {
self.query(vec("minor", v))?;
Ok(self.flag("minor_result"))
}
fn legacy_on_position(&mut self) -> io::Result<BlockPos> {
self.query("legacy".into())?;
Ok(BlockPos::new(17, -64, -33))
}
fn block_state(&mut self, p: BlockPos) -> io::Result<BlockStateId> {
self.query(format!("block {}", pos(p)))?;
Ok(BlockStateId(0))
}
fn on_ground(&mut self) -> io::Result<bool> {
self.query("ground".into())?;
Ok(self.grounded)
}
fn check_fall_damage(
&mut self,
dy: f64,
ground: bool,
_: BlockStateId,
p: BlockPos,
) -> io::Result<()> {
self.query(format!("fall {:016x} {ground} {}", bits(dy), pos(p)))?;
if self.flag("fall_mutates") {
self.fall = 3.0;
self.velocity = Vec3::new(17.0, 18.0, 19.0);
self.flags.horizontal = self.flag("mutated_horizontal");
self.flags.vertical = self.flag("mutated_vertical");
self.flags.below = self.flag("mutated_below");
}
if self.flag("fall_removes") {
self.removed = true;
}
Ok(())
}
fn removed(&mut self) -> io::Result<bool> {
Ok(self.removed)
}
fn can_simulate(&mut self) -> io::Result<bool> {
self.query("simulation".into())?;
Ok(true)
}
fn restitute(
&mut self,
state: BlockStateId,
x: bool,
z: bool,
resolved: Vec3,
) -> io::Result<()> {
let input = RestitutionInput {
block: state,
collided_x: x,
collided_z: z,
vertical_collision: self.flags.vertical,
collision_below: self.flags.below,
living: false,
resolved_movement: resolved,
fall_distance: self.fall,
};
restitute_movement(&mut Bounce(self), input)
}
fn client_side(&mut self) -> io::Result<bool> {
self.query("client".into())?;
Ok(self.flag("client"))
}
fn movement_emission(&mut self) -> io::Result<MovementEmission> {
self.query("emission".into())?;
Ok(if self.flag("emits") {
MovementEmission::All
} else {
MovementEmission::None
})
}
fn passenger(&mut self) -> io::Result<bool> {
self.query("passenger".into())?;
Ok(true)
}
fn apply_movement_emission(
&mut self,
_: MovementEmission,
_: Vec3,
_: BlockPos,
_: BlockStateId,
) -> io::Result<()> {
panic!("passenger guard must skip emission in this fixture")
}
fn block_speed_factor(&mut self) -> io::Result<f32> {
self.query("speed".into())?;
Ok(single(&self.f["speed"]))
}
}
struct Bounce<'a, 'b>(&'a mut World<'b>);
impl RestitutionWorld for Bounce<'_, '_> {
type Error = io::Error;
fn suppressing_bounce(&mut self) -> io::Result<bool> {
self.0.query("suppress".into())?;
Ok(false)
}
fn entity_bounciness(&mut self) -> io::Result<f64> {
self.0.query("entity-bounce".into())?;
Ok(0.0)
}
fn velocity(&mut self) -> io::Result<Vec3> {
self.0.velocity()
}
fn effective_gravity(&mut self) -> io::Result<f64> {
self.0.query("gravity".into())?;
Ok(0.08)
}
fn block_suppresses_bounce(&mut self, _: BlockStateId) -> io::Result<bool> {
self.0.query("block-suppresses".into())?;
Ok(false)
}
fn block_bounce_restitution(&mut self, _: BlockStateId) -> io::Result<f32> {
self.0.query("block-bounce".into())?;
Ok(0.0)
}
fn air_drag(&mut self) -> io::Result<f32> {
panic!("no positive restitution")
}
fn movement_support_position(&mut self) -> io::Result<BlockPos> {
panic!("no bounce event")
}
fn bounce_on(&mut self, _: BlockStateId, _: BlockPos, _: f64) -> io::Result<()> {
panic!("no bounce event")
}
fn bounce_event(&mut self) -> io::Result<()> {
panic!("no bounce event")
}
fn mark_position_for_sync(&mut self) {
panic!("no bounce event")
}
fn set_velocity(&mut self, v: Vec3) -> io::Result<()> {
self.0.set_velocity(v)
}
}
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 p = vector(&f["position"]);
let flag = |n: &str| f[n].as_bool().unwrap();
let shapes = f["shapes"]
.as_array()
.unwrap()
.iter()
.map(|v| {
VoxelShape::from_box(Aabb::new(
Vec3::new(number(&v[0]), number(&v[1]), number(&v[2])),
Vec3::new(number(&v[3]), number(&v[4]), number(&v[5])),
))
})
.collect::<Result<Vec<_>, _>>()?;
let mut history = MovementHistory::default();
for i in 0..f["history"].as_u64().unwrap() {
history.add(MovementRecord {
from: Vec3::new(i as f64, 0.0, 0.0),
to: Vec3::new((i + 1) as f64, 0.0, 0.0),
original: Some(Vec3::new(1.0, 0.0, 0.0)),
});
}
let mut w = World {
f: &f,
out: &mut out,
calls: 0,
position: p,
velocity: vector(&f["velocity"]),
stuck: vector(&f["stuck"]),
bounds: Aabb::new(
Vec3::new(p.x - 0.3, p.y, p.z - 0.3),
Vec3::new(p.x + 0.3, p.y + 1.8, p.z + 0.3),
),
flags: CollisionFlags {
horizontal: flag("horizontal"),
vertical: flag("vertical"),
below: flag("below"),
minor: flag("minor"),
},
grounded: flag("ground"),
removed: flag("removed"),
fall: number(&f["distance"]),
history,
shapes,
};
let mover = [
MoverType::SelfMovement,
MoverType::Player,
MoverType::Piston,
MoverType::ShulkerBox,
MoverType::Shulker,
][f["mover"].as_u64().unwrap() as usize];
let result = move_entity(&mut w, mover, vector(&f["movement"]));
writeln!(w.out, "{}", if result.is_ok() { "ok" } else { "error" })?;
writeln!(w.out, "{}", vec("position", w.position))?;
writeln!(w.out, "{}", vec("velocity-final", w.velocity))?;
writeln!(w.out, "{}", vec("stuck", w.stuck))?;
writeln!(w.out, "{}", bounds("bounds", w.bounds))?;
writeln!(
w.out,
"flags {} {} {} {} {} {} {:016x}",
w.flags.horizontal,
w.flags.vertical,
w.flags.below,
w.flags.minor,
w.grounded,
w.removed,
bits(w.fall)
)?;
writeln!(w.out, "history {}", w.history.records().len())?;
for r in w.history.records() {
writeln!(w.out, "{}", vec("from", r.from))?;
writeln!(w.out, "{}", vec("to", r.to))?;
writeln!(
w.out,
"{}",
r.original
.map_or_else(|| "original none".to_string(), |v| vec("original", v))
)?;
}
writeln!(w.out, "queries {}", w.calls)?;
}
Ok(())
}
+217
View File
@@ -0,0 +1,217 @@
//! Entity.move ordering around collision, support, landing and movement effects.
use crate::{movement_emission::MovementEmission, registry::BlockStateId};
use horizon_core::{geometry::Vec3, position::BlockPos};
use std::collections::VecDeque;
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum MoverType {
SelfMovement,
Player,
Piston,
ShulkerBox,
Shulker,
}
#[derive(Clone, Copy, Debug, Default)]
pub struct CollisionFlags {
pub horizontal: bool,
pub vertical: bool,
pub below: bool,
pub minor: bool,
}
#[derive(Clone, Copy, Debug)]
pub struct MovementRecord {
pub from: Vec3,
pub to: Vec3,
pub original: Option<Vec3>,
}
#[derive(Clone, Debug, Default)]
pub struct MovementHistory {
records: VecDeque<MovementRecord>,
}
impl MovementHistory {
pub fn records(&self) -> &VecDeque<MovementRecord> {
&self.records
}
pub fn add(&mut self, record: MovementRecord) {
if self.records.len() >= 100 {
let first = self.records.pop_front().unwrap();
let second = self.records.pop_front().unwrap();
self.records.push_front(MovementRecord {
from: first.from,
to: second.to,
original: None,
});
}
self.records.push_back(record);
}
pub fn remove_latest(&mut self) {
self.records.pop_back();
}
pub fn clear(&mut self) {
self.records.clear();
}
}
/// The provider retains entity fields so callbacks can mutate them. Hooks below
/// are native phase boundaries; bind collision, support, landing, restitution
/// and emission hooks to their corresponding world modules.
pub trait EntityMoveWorld {
type Error;
fn no_physics(&self) -> bool;
fn position(&self) -> Vec3;
fn set_position(&mut self, position: Vec3) -> Result<(), Self::Error>;
fn record_movement(&mut self, mover: MoverType, movement: Vec3) -> Result<(), Self::Error>;
fn collision_flags(&self) -> CollisionFlags;
fn set_collision_flags(&mut self, flags: CollisionFlags);
fn limit_piston_movement(&mut self, movement: Vec3) -> Result<Vec3, Self::Error>;
fn stuck_multiplier(&self) -> Vec3;
fn set_stuck_multiplier(&mut self, value: Vec3);
fn velocity(&mut self) -> Result<Vec3, Self::Error>;
fn set_velocity(&mut self, value: Vec3) -> Result<(), Self::Error>;
fn back_off_edge(&mut self, movement: Vec3, mover: MoverType) -> Result<Vec3, Self::Error>;
fn collide(&mut self, movement: Vec3) -> Result<Vec3, Self::Error>;
fn fall_distance(&self) -> f64;
/// FALLDAMAGE_RESETTING blocks and WATER fluids, entity collision context.
/// Return true for a non-MISS hit.
fn fall_reset_clip(&mut self, from: Vec3, to: Vec3) -> Result<bool, Self::Error>;
fn reset_fall_distance(&mut self) -> Result<(), Self::Error>;
fn movement_history(&mut self) -> &mut MovementHistory;
fn locally_authoritative(&mut self) -> Result<bool, Self::Error>;
fn set_on_ground_with_movement(
&mut self,
below: bool,
horizontal: bool,
movement: Vec3,
) -> Result<(), Self::Error>;
fn minor_horizontal_collision(&mut self, movement: Vec3) -> Result<bool, Self::Error>;
fn legacy_on_position(&mut self) -> Result<BlockPos, Self::Error>;
fn block_state(&mut self, p: BlockPos) -> Result<BlockStateId, Self::Error>;
fn on_ground(&mut self) -> Result<bool, Self::Error>;
fn check_fall_damage(
&mut self,
dy: f64,
on_ground: bool,
state: BlockStateId,
p: BlockPos,
) -> Result<(), Self::Error>;
fn removed(&mut self) -> Result<bool, Self::Error>;
fn can_simulate(&mut self) -> Result<bool, Self::Error>;
fn restitute(
&mut self,
state: BlockStateId,
collided_x: bool,
collided_z: bool,
resolved: Vec3,
) -> Result<(), Self::Error>;
fn client_side(&mut self) -> Result<bool, Self::Error>;
fn movement_emission(&mut self) -> Result<MovementEmission, Self::Error>;
fn passenger(&mut self) -> Result<bool, Self::Error>;
fn apply_movement_emission(
&mut self,
emission: MovementEmission,
resolved: Vec3,
p: BlockPos,
state: BlockStateId,
) -> Result<(), Self::Error>;
fn block_speed_factor(&mut self) -> Result<f32, Self::Error>;
}
fn java_zero(v: Vec3) -> bool {
v.x.to_bits() == 0 && v.y.to_bits() == 0 && v.z.to_bits() == 0
}
fn different(a: f64, b: f64) -> bool {
let delta = (b - a).abs();
delta >= f64::from(1.0e-5f32) || delta.is_nan()
}
/// Release behavior; native IDE-only invalid-simulation assertions and profiler
/// callbacks do not form part of this gameplay transition.
pub fn move_entity<W: EntityMoveWorld>(
world: &mut W,
mover: MoverType,
mut movement: Vec3,
) -> Result<(), W::Error> {
if world.no_physics() {
world.set_position(world.position().add_vector(movement))?;
world.record_movement(mover, movement)?;
world.set_collision_flags(CollisionFlags::default());
return Ok(());
}
if mover == MoverType::Piston {
movement = world.limit_piston_movement(movement)?;
if java_zero(movement) {
return Ok(());
}
}
let stuck = world.stuck_multiplier();
if stuck.length_squared() > 1.0e-7 {
if mover != MoverType::Piston {
movement = movement.multiply(stuck);
}
world.set_stuck_multiplier(Vec3::ZERO);
world.set_velocity(Vec3::ZERO)?;
}
movement = world.back_off_edge(movement, mover)?;
let resolved = world.collide(movement)?;
let length = resolved.length_squared();
if length > 1.0e-7 || movement.length_squared() - length < 1.0e-7 {
if world.fall_distance() != 0.0 && length >= 1.0 {
let distance = resolved.length().min(8.0);
let from = world.position();
let to = from.add_vector(resolved.normalize().scale(distance));
if world.fall_reset_clip(from, to)? {
world.reset_fall_distance()?;
}
}
let from = world.position();
let to = from.add_vector(resolved);
world.movement_history().add(MovementRecord {
from,
to,
original: Some(movement),
});
world.set_position(to)?;
world.record_movement(mover, resolved)?;
}
let x = different(movement.x, resolved.x);
let z = different(movement.z, resolved.z);
let mut flags = world.collision_flags();
flags.horizontal = x || z;
world.set_collision_flags(flags);
let nonzero_y = movement.y.abs() > 0.0;
if nonzero_y || world.locally_authoritative()? {
flags = world.collision_flags();
flags.vertical = movement.y != resolved.y;
flags.below = flags.vertical && movement.y < 0.0;
world.set_collision_flags(flags);
world.set_on_ground_with_movement(flags.below, flags.horizontal, resolved)?;
}
let minor = if world.collision_flags().horizontal {
world.minor_horizontal_collision(resolved)?
} else {
false
};
flags = world.collision_flags();
flags.minor = minor;
world.set_collision_flags(flags);
let p = world.legacy_on_position()?;
let state = world.block_state(p)?;
if world.locally_authoritative()? {
let grounded = world.on_ground()?;
world.check_fall_damage(resolved.y, grounded, state, p)?;
}
if world.removed()? {
return Ok(());
}
if world.can_simulate()?
&& (nonzero_y && world.collision_flags().vertical || world.collision_flags().horizontal)
{
world.restitute(state, x, z, resolved)?;
}
if !world.client_side()? || world.locally_authoritative()? {
let emission = world.movement_emission()?;
if emission.anything() && !world.passenger()? {
world.apply_movement_emission(emission, resolved, p, state)?;
}
}
let factor = f64::from(world.block_speed_factor()?);
let velocity = world.velocity()?.multiply(Vec3::new(factor, 1.0, factor));
world.set_velocity(velocity)
}
+1
View File
@@ -19,6 +19,7 @@ pub mod compression;
pub mod dynamic_support;
pub mod edge_backoff;
pub mod entity_collision;
pub mod entity_move;
pub mod entity_obstruction;
pub mod fall_damage;
pub mod fluid;
+18
View File
@@ -737,3 +737,21 @@ 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.
`entity_move` sequences native movement phases: no-physics/piston early exits,
stuck multiplier reset, edge backoff, collision resolution, fall-reset clipping,
position/recording writes, collision/ground flags, landing, removal, restitution,
movement emission and block speed multiplication. `MovementHistory` retains at
most 100 records by merging the oldest pair and dropping their original-axis
movement metadata. Callbacks can change fields read by later phases.
`python3 tools/test_entity_move_parity.py` invokes actual `Entity.move` with native
shape resolution and zero restitution. The
[movement lifecycle report](../benchmarks/rust-entity-move-2026-10-07/scoped-parity.json)
records **543,653 matching lines per JAR**, covering signed zero, tiny/nonfinite
movement, collisions, early exits, recording cap, authority guards, landing
mutations/removal and failures after partial writes. Targeted Clippy passes.
This gate controls piston/edge decisions, ground support, ray hits and landing
callbacks; step height is zero and non-passenger emission is tested separately.
Binding all phases to a live entity/world and player travel remains pending.
@@ -0,0 +1,92 @@
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.*;
import net.minecraft.world.level.block.*;
import net.minecraft.world.level.block.state.*;
import net.minecraft.world.level.border.WorldBorder;
import net.minecraft.world.level.gameevent.GameEvent;
import net.minecraft.world.phys.*;
import net.minecraft.world.phys.shapes.*;
/** Actual Entity.move with controlled lifecycle providers and native collision resolution. */
public final class VanillaEntityMoveOracle {
static final class Stop extends RuntimeException {}
static Field position,stuck,history,removal;
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 String vec(String label,Vec3 v){return String.format("%s %016x %016x %016x",label,Double.doubleToLongBits(v.x),Double.doubleToLongBits(v.y),Double.doubleToLongBits(v.z));}
static String box(String label,AABB b){return String.format("%s %016x %016x %016x %016x %016x %016x",label,Double.doubleToLongBits(b.minX),Double.doubleToLongBits(b.minY),Double.doubleToLongBits(b.minZ),Double.doubleToLongBits(b.maxX),Double.doubleToLongBits(b.maxY),Double.doubleToLongBits(b.maxZ));}
static String pos(BlockPos p){return p.getX()+" "+p.getY()+" "+p.getZ();}
static void write(Field f,Object o,Object v){try{f.set(o,v);}catch(Exception e){throw new AssertionError(e);}}
static final class Data {
JsonObject f;PrintWriter out;int calls;Vec3 velocity;boolean grounded;Actor actor;State state;List<VoxelShape> shapes;
boolean flag(String n){return f.get(n).getAsBoolean();}
void query(String s){out.println(s);if(calls++==f.get("fail").getAsInt())throw new Stop();}
}
static final class BounceBlock extends Block {
Data d;BounceBlock(){super(BlockBehaviour.Properties.of());}
@Override public float getBounceRestitution(){d.query("block-bounce");return 0;}
}
static final class State extends BlockState {
Data d;BounceBlock block;State(){super(null,null,null);}
@Override public Block getBlock(){return block;}
@Override public boolean is(TagKey<Block> tag){if(tag!=BlockTags.SUPPRESSES_BOUNCE)throw new AssertionError();d.query("block-suppresses");return false;}
}
static final class Border extends WorldBorder {
Data d;@Override public boolean isInsideCloseToBorder(Entity e,AABB b){d.query(box("border-test",b));return false;}
}
static final class Level extends net.minecraft.server.level.ServerLevel {
Data d;Border border;Level(){super(null,null,null,null,null,null,false,0L,List.of(),false);}
@Override public boolean isClientSide(){d.query("client");return d.flag("client");}
@Override public BlockState getBlockState(BlockPos p){d.query("block "+pos(p));return d.state;}
@Override public WorldBorder getWorldBorder(){d.query("border");return border;}
@Override public List<VoxelShape> getEntityCollisions(Entity e,AABB b){d.query(box("entities",b));return List.of();}
@Override public Iterable<VoxelShape> getBlockCollisions(Entity e,AABB b){d.query(box("blocks",b));return d.shapes;}
@Override public BlockHitResult clip(ClipContext c){d.query(vec("clip-from",c.getFrom()));d.query(vec("clip-to",c.getTo()));return d.flag("clip_hit")?new BlockHitResult(c.getTo(),Direction.UP,BlockPos.ZERO,false):BlockHitResult.miss(c.getTo(),Direction.UP,BlockPos.ZERO);}
}
static final class Actor extends Marker {
Data d;Actor(){super(null,null);}void configure(Level level){setLevel(level);}
@Override public void setPos(double x,double y,double z){var next=new Vec3(x,y,z);d.query(vec("set-position",next));setBoundingBox(getBoundingBox().move(next.subtract(position())));write(position,this,next);}
@Override public void recordMovement(MoverType mover,Vec3 v){d.query(vec("record "+mover.ordinal(),v));}
@Override protected Vec3 limitPistonMovement(Vec3 v){d.query(vec("piston",v));return vector(d.f.get("piston_result"));}
@Override public Vec3 getDeltaMovement(){d.query("velocity");return d.velocity;}
@Override public void setDeltaMovement(Vec3 v){d.query(vec("set-velocity",v));d.velocity=v;}
@Override protected Vec3 maybeBackOffFromEdge(Vec3 v,MoverType mover){d.query(vec("edge "+mover.ordinal(),v));return d.flag("edge_override")?vector(d.f.get("edge_result")):v;}
@Override public float maxUpStep(){return 0;}
@Override public boolean isDescending(){return false;}
@Override protected boolean isLocalClientAuthoritative(){d.query("local-client-authority");return d.flag("local_client");}
@Override public boolean isClientAuthoritative(){d.query("client-authority");return d.flag("client_authority");}
@Override public MoveSimulationType getMoveSimulationType(){d.query("simulation");return MoveSimulationType.SERVER_AND_CLIENT;}
@Override public boolean onGround(){d.query("ground");return d.grounded;}
@Override public void setOnGroundWithMovement(boolean below,boolean horizontal,Vec3 v){d.query(vec("ground-set "+below+" "+horizontal,v));d.grounded=below;horizontalCollision=horizontal;}
@Override protected boolean isHorizontalCollisionMinor(Vec3 v){d.query(vec("minor",v));return d.flag("minor_result");}
@Override public BlockPos getOnPosLegacy(){d.query("legacy");return new BlockPos(17,-64,-33);}
@Override protected void checkFallDamage(double dy,boolean ground,BlockState s,BlockPos p){d.query(String.format("fall %016x %s %s",Double.doubleToLongBits(dy),ground,pos(p)));if(d.flag("fall_mutates")){fallDistance=3;d.velocity=new Vec3(17,18,19);horizontalCollision=d.flag("mutated_horizontal");verticalCollision=d.flag("mutated_vertical");verticalCollisionBelow=d.flag("mutated_below");}if(d.flag("fall_removes"))write(removal,this,Entity.RemovalReason.DISCARDED);}
@Override public void resetFallDistance(){d.query("reset-fall");fallDistance=0;}
@Override public boolean isSuppressingBounce(){d.query("suppress");return false;}
@Override protected double getEntityBounciness(){d.query("entity-bounce");return 0;}
@Override protected double getEffectiveGravity(){d.query("gravity");return .08;}
@Override protected MovementEmission getMovementEmission(){d.query("emission");return d.flag("emits")?MovementEmission.ALL:MovementEmission.NONE;}
@Override public boolean isPassenger(){d.query("passenger");return true;}
@Override protected float getBlockSpeedFactor(){d.query("speed");return single(d.f.get("speed"));}
}
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);
position=Entity.class.getDeclaredField("position");position.setAccessible(true);stuck=Entity.class.getDeclaredField("stuckSpeedMultiplier");stuck.setAccessible(true);history=Entity.class.getDeclaredField("movementThisTick");history.setAccessible(true);removal=Entity.class.getDeclaredField("removalReason");removal.setAccessible(true);
var record=Class.forName("net.minecraft.world.entity.Entity$Movement");var constructor=record.getDeclaredConstructor(Vec3.class,Vec3.class,Vec3.class);constructor.setAccessible(true);var from=record.getDeclaredMethod("from");from.setAccessible(true);var to=record.getDeclaredMethod("to");to.setAccessible(true);var original=record.getDeclaredMethod("axisDependentOriginalMovement");original.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;d.velocity=vector(f.get("velocity"));d.grounded=d.flag("ground");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);var border=(Border)allocate.invoke(unsafe,Border.class);border.d=d;level.border=border;var state=(State)allocate.invoke(unsafe,State.class);state.d=d;var block=(BounceBlock)allocate.invoke(unsafe,BounceBlock.class);block.d=d;state.block=block;d.state=state;
var p=vector(f.get("position"));position.set(actor,p);actor.setBoundingBox(new AABB(p.x-.3,p.y,p.z-.3,p.x+.3,p.y+1.8,p.z+.3));stuck.set(actor,vector(f.get("stuck")));actor.noPhysics=d.flag("no_physics");actor.fallDistance=number(f.get("distance"));actor.horizontalCollision=d.flag("horizontal");actor.verticalCollision=d.flag("vertical");actor.verticalCollisionBelow=d.flag("below");actor.minorHorizontalCollision=d.flag("minor");removal.set(actor,d.flag("removed")?Entity.RemovalReason.DISCARDED:null);
var movements=new ArrayDeque<Object>();for(int i=0;i<f.get("history").getAsInt();i++)movements.add(constructor.newInstance(new Vec3(i,0,0),new Vec3(i+1,0,0),new Vec3(1,0,0)));history.set(actor,movements);d.shapes=new ArrayList<>();for(var v:f.getAsJsonArray("shapes")){var a=v.getAsJsonArray();d.shapes.add(Shapes.box(number(a.get(0)),number(a.get(1)),number(a.get(2)),number(a.get(3)),number(a.get(4)),number(a.get(5))));}
try{actor.move(MoverType.values()[f.get("mover").getAsInt()],vector(f.get("movement")));out.println("ok");}catch(Stop e){out.println("error");}
out.println(vec("position",actor.position()));out.println(vec("velocity-final",d.velocity));out.println(vec("stuck",(Vec3)stuck.get(actor)));out.println(box("bounds",actor.getBoundingBox()));out.printf("flags %s %s %s %s %s %s %016x%n",actor.horizontalCollision,actor.verticalCollision,actor.verticalCollisionBelow,actor.minorHorizontalCollision,d.grounded,actor.isRemoved(),Double.doubleToLongBits(actor.fallDistance));out.println("history "+movements.size());for(var r:movements){out.println(vec("from",(Vec3)from.invoke(r)));out.println(vec("to",(Vec3)to.invoke(r)));var v=(Optional<?>)original.invoke(r);out.println(v.isEmpty()?"original none":vec("original",(Vec3)v.get()));}out.println("queries "+d.calls);
}}
}
}
+72
View File
@@ -0,0 +1,72 @@
#!/usr/bin/env python3
"""Compare Entity.move lifecycle ordering, state writes and recording 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 row(**kw):
f=dict(movement=list(map(bits,[.4,-.2,.3])),position=list(map(bits,[.5,1.,.5])),velocity=list(map(bits,[.1,-.8,.2])),stuck=list(map(bits,[0,0,0])),piston_result=list(map(bits,[.25,0,0])),edge_result=list(map(bits,[.1,0,.2])),edge_override=False,no_physics=False,mover=0,client=False,client_authority=False,local_client=False,ground=True,horizontal=False,vertical=False,below=False,minor=False,minor_result=True,removed=False,distance=bits(5),clip_hit=False,fall_mutates=False,fall_removes=False,mutated_horizontal=False,mutated_vertical=False,mutated_below=False,emits=False,speed=single(.4),history=0,shapes=[],fail=-1)
f.update(kw);return f
scenes=[[],[[0,0,0,1,1,1]],[[1,0,0,2,3,1]],[[0,3,0,1,4,1]],[[0,0,1,1,3,2]],[[0,0,0,1,.9375,1]],[[0,0,0,1,1,1],[1,0,0,2,3,1],[0,0,1,1,3,2]]]
motions=[[0,0,0],[-0.,0,0],[1e-200,-1e-200,1e-200],[1e-4,-1e-4,1e-4],[.4,-.2,.3],[2,-1,2],[-2,-5,-2],[0,9,0],[1e-5,0,-1e-5],[0,0,math.nan],[0,math.nan,0],[math.inf,0,0]]
for shapes,motion,mover,client,authority,local in itertools.product(scenes,motions,range(5),[False,True],[False,True],[False,True]):
yield row(shapes=[[bits(v) for v in b] for b in shapes],movement=list(map(bits,motion)),mover=mover,client=client,client_authority=authority,local_client=local,emits=True)
for mover,no_physics,stuck,piston,fail in itertools.product(range(5),[False,True],[[0,0,0],[1e-200,0,0],[.1,.2,.3],[math.nan,1,1],[math.inf,0,0]],[[0,0,0],[-0.,0,0],[0,-0.,0],[0,0,-0.],[.25,0,0]],range(-1,12)):
yield row(mover=mover,no_physics=no_physics,stuck=list(map(bits,stuck)),piston_result=list(map(bits,piston)),fail=fail,horizontal=True,vertical=True,below=True,minor=True)
for shapes,flags,fail in itertools.product(scenes,range(32),range(-1,38)):
yield row(shapes=[[bits(v) for v in b] for b in shapes],fall_mutates=True,fall_removes=bool(flags&1),mutated_horizontal=bool(flags&2),mutated_vertical=bool(flags&4),mutated_below=bool(flags&8),clip_hit=bool(flags&16),movement=list(map(bits,[2,-2,2])),fail=fail)
for count,fail,no_physics in itertools.product([0,1,98,99,100],range(-1,30),[False,True]):yield row(history=count,fail=fail,no_physics=no_physics)
rng=random.Random(0x4d4f56454d454e54)
values=[-math.inf,-1e30,-2.,-0.,0.,1e-200,1e-7,1.,1e30,math.inf,math.nan]
for _ in range(6000):
yield row(movement=list(map(bits,rng.choice(motions))),shapes=[[bits(v) for v in b] for b in rng.choice(scenes)],mover=rng.randrange(5),stuck=[bits(rng.choice([0,.5,1,math.nan])) for _ in range(3)],piston_result=[bits(rng.choice(values)) for _ in range(3)],edge_override=bool(rng.randrange(2)),edge_result=[bits(rng.choice(values)) for _ in range(3)],velocity=[bits(rng.choice(values)) for _ in range(3)],no_physics=bool(rng.randrange(2)),client=bool(rng.randrange(2)),client_authority=bool(rng.randrange(2)),local_client=bool(rng.randrange(2)),ground=bool(rng.randrange(2)),horizontal=bool(rng.randrange(2)),vertical=bool(rng.randrange(2)),below=bool(rng.randrange(2)),minor=bool(rng.randrange(2)),minor_result=bool(rng.randrange(2)),removed=bool(rng.randrange(2)),distance=bits(rng.choice(values)),clip_hit=bool(rng.randrange(2)),fall_mutates=bool(rng.randrange(2)),fall_removes=bool(rng.randrange(2)),mutated_horizontal=bool(rng.randrange(2)),mutated_vertical=bool(rng.randrange(2)),mutated_below=bool(rng.randrange(2)),emits=bool(rng.randrange(2)),speed=single(rng.choice(values)),fail=rng.randrange(-1,34))
def main():
parser=argparse.ArgumentParser(description=__doc__);parser.add_argument('--prepare',action='store_true');args=parser.parse_args()
build=lab.LOCAL/'rust-entity-move-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/VanillaEntityMoveOracle.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/entity_move_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.VanillaEntityMoveOracle',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','entity_move_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'/('entity_move_fixture.exe' if os.name=='nt' else 'entity_move_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:,} entity-move 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.move with native shape collision resolution (zero step height), native zero-restitution handling and final authority checks. Compares no-physics and piston early exits including signed zero, stuck multiplier reset, edge callback, fall-reset clip endpoints, movement recording cap, collision flag writes, ground/fall callback ordering, removal, passenger emission guard, speed multiplication and failures after partial state changes. Providers control piston/edge decisions, ground support, ray hits, landing effects and sound policy; live world integration and non-passenger movement emission remain separate work.'}
lab.write_json(report_path,report);print(f'Scoped entity move parity passed: {report_path}')
if __name__=='__main__':main()