Bind attribute refresh to health limits and waypoint update ordering

This commit is contained in:
Ludovic Marie
2026-10-07 11:39:35 +02:00
parent a772a69498
commit 636f1648f0
7 changed files with 1201 additions and 0 deletions
@@ -0,0 +1,303 @@
{
"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": 6836,
"sources_sha256": {
"mods/horizon/src/test/java/dev/horizonlab/parity/VanillaAttributeUpdatesOracle.java": "7f03516b12b47ae7d65898c01565323dd7085791d8b4ecd1ac746e03435e4538",
"tools/test_attribute_updates_parity.py": "837d8a8bd0b373bae9bfd5bc78cbb27f7100f6cacce749d2c5d79b36fe492aa4",
"tools/test_effect_notifications_parity.py": "ceea3baa62e5e4a5a73e932cb33d9062467d8ddd57558c989a93aa283530bb58",
"tools/test_rust_parity.py": "29d3db08005873528894b45ca10dcd6278753650473b3eb574e53e06ede40ec6",
"tools/lab.py": "d00bf107fa01b6c579734a082c3ca4ade3018f0d6851d46844acf17574f66e64",
"Cargo.toml": "3f93319c1eb70dbd7ba25cd74ef343fbf35c52c7c4752f955605c58699cbff9d",
"Cargo.lock": "6884c7209f007ab9ee742b9c3c73828cfa1c6753d35b5fe25a87cab5955b33f8",
"crates/horizon-core/Cargo.toml": "e21b502c949da93cf67826592459b687a8cbf737ef92fee514e3ed2cc7cd27ad",
"crates/horizon-world/Cargo.toml": "18bbad377a8047e4d4ecd0fc87d83d46d1c0f1ce2b8c47bbe64633e7b4420b57",
"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/active_effect_tick.rs": "88f19cfc3532c47a58c646bb3e9d99ff239a900752302b6a444dda3013e95cbf",
"crates/horizon-world/src/active_effects.rs": "daaf352a69511a7d69fb329997f783b86585e486e0379c84ab0a55641771646c",
"crates/horizon-world/src/attribute_health.rs": "6135beb1baf9a55745ab95955bcd05e585e4042a6cffda0a14c13f05593e7186",
"crates/horizon-world/src/attribute_updates.rs": "4cb322d75a50d222298994607097ba7f7e6674021128fa02a98a74c057ad05f1",
"crates/horizon-world/src/attributes/entity.rs": "56fb96b1beb9caaa769369bf7cddf522d2f2e84e241c213b51ae8d39d31a9f13",
"crates/horizon-world/src/attributes/mod.rs": "3d05ce2ff10c1b68540e3c87bbd2ac65c56a2e7b7127a7865b60890a2e9a690d",
"crates/horizon-world/src/attributes/operation_map.rs": "fc5bc8921d78ea6721a2eaa915e67483e2a33683927467bdba24f0492356ad97",
"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/blocking_item_damage.rs": "afea3cb5761e8b82ee4d9bb4ef3c6fd10182a324dbaec99e6c9ebfac255dda09",
"crates/horizon-world/src/blocking_rules.rs": "83c5e2105cec56a90182ce582e1c67ade2b189be6de1a34d08035000cc07c492",
"crates/horizon-world/src/bundle_weight.rs": "a0e7b0587ab5d46cd55588f25d55a088b7a4725099c13ba6df146957be36014c",
"crates/horizon-world/src/chunk.rs": "795895284d6e615bcb2082fceacdfe7025c152fb2934bb43f598f17a00ab46f1",
"crates/horizon-world/src/chunk_status.rs": "dbb8583cfa775203c544d24d69e03720385428f15a267a561356c21f5025e9c7",
"crates/horizon-world/src/climbing.rs": "6cd2f919872ff0b726effddaa57eccb805b8eae43f9d01144716010853891879",
"crates/horizon-world/src/collision.rs": "a749794d5c27ccd6c0d964ad02f9dc31d49d9f4fe4b7366cd4bd8155e681b00b",
"crates/horizon-world/src/collision_context.rs": "2176c7034e07f547510a8327cdad9d3b63f26e4754032b76b7d5fe13166c333c",
"crates/horizon-world/src/combat_rules.rs": "01131f70d1acdd96a910e7f96deaa12e51a1e3bb9208085f04e3ec7b657d3cd4",
"crates/horizon-world/src/component_durability.rs": "37a46e184f95bc5027e2b9af72ff52f729ad03601d08e1827f181fca383a35af",
"crates/horizon-world/src/component_patch.rs": "f6f8992f241dfb25e012a06a7ba3fecab8d958686dddd096595a4806e56f0ab3",
"crates/horizon-world/src/compression.rs": "4db73e6ca9a0d552d415ce60e7e88b89c9cf2b7d060a669ff8328f518316e101",
"crates/horizon-world/src/consumable.rs": "ba3575cb85866cb940f33a1ac86455d71cf0cc6395b848588a8ee1fe33f5c326",
"crates/horizon-world/src/consume_effect.rs": "03f2aac6879ce133347d42f6f6667d62c06d956ff9faac5dc14182a788b6a236",
"crates/horizon-world/src/damage_immunity.rs": "fd39c41124d694e755cf7faadcf93c636df4042c78ec345092a856b5f952dbcf",
"crates/horizon-world/src/damage_reduction.rs": "f5a599ab11e077f526237018aeda803ea3c420c5b35c78ae9d85e32267e23bf3",
"crates/horizon-world/src/damage_source.rs": "24945e397a926d34695652980923609e4723e02fe6b6e56d4aaf673e45d759f7",
"crates/horizon-world/src/dynamic_support.rs": "5996bc1c436fb6215d8fc3d0bb4de5dd9b966d842d432580d624195771697fb8",
"crates/horizon-world/src/edge_backoff.rs": "5259d7aa44e64f88351d1556c44dd0edcad3b400a099c659755792efba7b5773",
"crates/horizon-world/src/effect_notifications.rs": "ea66bfa010d6d44aee97b9672902f41cd99d0d80239f41c6d4eae5c9a9472d3d",
"crates/horizon-world/src/entity_collision.rs": "09f2fd7f6cd56fc282f468ecfa765a94506929e9684bd3a5c90123e5b51cd627",
"crates/horizon-world/src/entity_fluid.rs": "02991040ae879a8914f09913128a3506e9e7a1ca33ffc0a22b5729a168346600",
"crates/horizon-world/src/entity_fluid_tick.rs": "4f9e070b88a73c98a4e2e659360ddd6edee636bca29377f1ea9011fd5a556f64",
"crates/horizon-world/src/entity_move.rs": "a40a50d3e841625a75d4cfbb7c2630ab5a62a574456567e7234f401ae8441b32",
"crates/horizon-world/src/entity_obstruction.rs": "a4b28a19bf83b23611c59d94535c488db37e4aee24b22c973c545910894a809f",
"crates/horizon-world/src/extra_items.rs": "6eba0a0c795d5fd841d31471708e78324b4852adebb2e6ed700dcc0caab668cd",
"crates/horizon-world/src/fall_damage.rs": "8c28e2933493569fe05dea22332024287d28b0746b514dd465681040b003296b",
"crates/horizon-world/src/fluid.rs": "a0a1f34ba94d8b59dc975becee8952960764d67ae08cf2d6a8bcd40a614b15c4",
"crates/horizon-world/src/fluid_box.rs": "b586af72b1c04a9354fe66205c28dc8b619e205ab9ea19a68232722e4b6e96c1",
"crates/horizon-world/src/fluid_flow.rs": "7c7e486b5c565df3602799648b37b879bcca51414337f607962d6c7951b86d05",
"crates/horizon-world/src/fluid_occupancy.rs": "26037579caeef6c8a5a2bde2c3d142d278558b76d77ebff401cee6e35f2ea961",
"crates/horizon-world/src/fluid_shape.rs": "28bb65a72f5bd074a354ba38a1b1707fcb0947537977a11540bba0adb85995c2",
"crates/horizon-world/src/food.rs": "3cf416fa632a5555a8116bb528a6e80bcb5b6b754b0748b4959ed310102418d3",
"crates/horizon-world/src/food_properties.rs": "0926fa3f3bc3d0608c45d02f564355825ca3c3526a82bf9c1375c7a191fa5e12",
"crates/horizon-world/src/food_storage.rs": "58c433801a285efcc973638c11d694a41f85dc96703a3a7ce8d3673f3eb31d52",
"crates/horizon-world/src/free_space.rs": "913536bf4c85ddeeb378d76d1b5110b4175f5688e1a749f32fe6a6d86fbb478e",
"crates/horizon-world/src/ground_support.rs": "3e371d74ecede4d30beef0a7e8a2ee33463584a5b1ed3d852b394efcce1ffbe0",
"crates/horizon-world/src/heightmap.rs": "a5edef082b5af40d814421ea17e129b3b06d70abee6434ed263e4bba58571698",
"crates/horizon-world/src/identifier.rs": "37c921889c4f5d91f7c09ab2ee68d1a5e5a3c4fb1c49b4b189c987b4615fd2d6",
"crates/horizon-world/src/interaction_shapes.rs": "f321284ddab440a394372b6dedc8b5cc29a653c0745251a9e96c650f21c33dea",
"crates/horizon-world/src/inventory_pickup.rs": "ea830c7916e7f2d95c6db7ef48be1e876e6ebafcb438e47fc72f9a5bd7c37d07",
"crates/horizon-world/src/inventory_tick.rs": "293a3d57a1689337cfd58e5a73dcdbed4cb360f94d98ff5458f0df92de387365",
"crates/horizon-world/src/inventory_use.rs": "6a6c2ecc73c83dda4cd798c0304df12ace6096eb110d899f4f0c7ffd852bf001",
"crates/horizon-world/src/item_after_use.rs": "2b6598ce5b7b304dea2c8057b4a1eb6530264ac4d0546c3da0c76deed9609da5",
"crates/horizon-world/src/item_blocking.rs": "f6eee0d6d177cc3148960122327ba1b459a2f0e85241c1d9a3e40f0b8c169296",
"crates/horizon-world/src/item_cooldowns.rs": "7b625868509ab3c51e72db0ac714bb0e7ec5e2610d37271005a1a737f4d14431",
"crates/horizon-world/src/item_durability.rs": "9fdf835e5c7bd0e1dd63ef2ba0076b770cdd4d6d14cdcd11de27690f0702f94e",
"crates/horizon-world/src/item_particles.rs": "861b14f65adb9b14f8158fe9f0b7128beac3df8107b1337c31b8b9b49434ba3f",
"crates/horizon-world/src/item_stack_refs.rs": "18d3ca85cac0daf59200857513761359e5690f719cb3f7f64ae552f0c4169ea3",
"crates/horizon-world/src/item_stack_storage.rs": "b4dcb83dcaa346848c5484161feb3e2cf08e83f7793d7ed83be25b14b9d12884",
"crates/horizon-world/src/item_template.rs": "dc80025215d1867e656924ea2a9d273aacbcaea21293d986ba42d0004ee93b69",
"crates/horizon-world/src/item_use.rs": "b1c79428986efbf9dd5ede703a39eb387fb6dcc7d9950f0e49243a353a5b8070",
"crates/horizon-world/src/item_use_catalog.rs": "a9b53d2e816583239a75c3c61e78dacc5e3b27dee595be34b1c7fa169f17a571",
"crates/horizon-world/src/item_use_lifecycle.rs": "4639b70440cf811e79de09c48a3e035143fcb6be8a64c2f61b10ae55a2f133b7",
"crates/horizon-world/src/item_use_start.rs": "a84092dfb8b8f0a89d0928bb2f8ac252bd9bfd3f8d74fce1a7f7a5b3c3f4f50b",
"crates/horizon-world/src/java_hash_map.rs": "955502e696d7b0b33bae3ec485c8eb1453a8956116aad68226cf1fc67d60babc",
"crates/horizon-world/src/lib.rs": "ba65ff859b662dbbfa6f3243dd0f31c5f8e0ec2e2677bfead6f17eb50b06d69f",
"crates/horizon-world/src/liquid_collision.rs": "0f0a83b04e5a5bc9d98ed05fde4d6584ba69642e93522e726e4cfc325d6fcb1d",
"crates/horizon-world/src/living_air.rs": "b2de36bb50bed15e2808fe4a874fcef07f9fea0d2c96385b7b5e8ee9560d4442",
"crates/horizon-world/src/living_damage.rs": "2b448209629b9bf2e678d667ad0d6c7fa22146892dd106b169d96b31b0df12bb",
"crates/horizon-world/src/living_fall.rs": "da0f176f4731e1c86e08bfeee8167742fd1b86380ff0e9304c13c1ce3f1c67ab",
"crates/horizon-world/src/living_fluid.rs": "d7cd914cfb2a1f4876ce0ae95e999bf0ff1af732cce3ab4a3aa2d1ab881c7339",
"crates/horizon-world/src/living_health.rs": "221659f85552424e937d503bba6d8003006b106ced042b32d3a6f6cc23e12ed0",
"crates/horizon-world/src/living_hurt.rs": "844239ad7d15c39629bb3cfc06ebae6d1cff20b7627468f26e0ee8dff3abdcb8",
"crates/horizon-world/src/living_landing.rs": "8bca4eb7977abc2b14fb7174be4ea355352106ea1e76ff5f3ad8696969d9eefd",
"crates/horizon-world/src/living_motion.rs": "56f8c84cfbe205a7a69912d5f76b52ef1efca149c1b6801d4684e25fb179aeb9",
"crates/horizon-world/src/living_use_flags.rs": "826f4fdeba7e208368cbbf910fa408db0afbc6442d842d178a70fae53e0d0ea6",
"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/object_open_set.rs": "d3e0373bf1b652dd054345d5a8e25ec828e06dd7cad65aada35163c95df4e637",
"crates/horizon-world/src/piston_movement.rs": "7a3fd2549a582b5f3a5c21aa05d67d944d62ca1105856679e100ac5b44e9a52f",
"crates/horizon-world/src/piston_shape.rs": "399c2337103b8e7e0eb5667227c5aa14f9549d6a467b7be7da3d0717f0263500",
"crates/horizon-world/src/player_equipment.rs": "1fa8c53e67624c4641246249d79a800a28a4858c62482bd880b4e3b866b936ef",
"crates/horizon-world/src/player_food.rs": "f188dc110745bcc7acc15e6c521db943b06b404f69cd251bf74a345516070304",
"crates/horizon-world/src/player_health.rs": "79ecb6b4d7a044dc98e5552c53a5ac2724545b1c1cec4d797eb52576728e3480",
"crates/horizon-world/src/player_hurt.rs": "99c9bbd8587ec2dc287806049897b8034d2f5349ad47fc497b70d54148844a36",
"crates/horizon-world/src/player_inventory.rs": "f4f67d8312793f25930626c4ac06db688fcf2e2292d6675071d105fb1708d952",
"crates/horizon-world/src/player_travel.rs": "6841a677a900495eb6223031ef3ebe8d859bb98908dda0e2209e5f28839dc593",
"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": "97cc950a9a4ba387b78e368f36d595f8664ab7730d132900d220ce23b92826ea",
"crates/horizon-world/src/registry_fall_ray.rs": "52935436132df505691144840dcbd858b15d00d8305997284e3a542ecf6bc0dc",
"crates/horizon-world/src/registry_fluid.rs": "b8959c05850cfeaaad1e4de8ca7c7d0d6ac56da9b2c1de3ebefa5871a11ed35e",
"crates/horizon-world/src/retrogen.rs": "6efd36a6293756688c1990a7965bd65609d7c5e2bf2a5f4f01d54932d2b9386b",
"crates/horizon-world/src/server_player_combat.rs": "a9743971581af86d926eeba42004d932caf83382c470dd98bf613cb9c417db58",
"crates/horizon-world/src/server_player_damage.rs": "828a4649fec3e9bda2dfe20aced8c95922c697321de2be4127bd4e5712ecbb7c",
"crates/horizon-world/src/simple_container.rs": "0fedbc7e974b1db79b88bd6aff977654189a5d553cf42852fe8bf0885cf90b30",
"crates/horizon-world/src/status_effect.rs": "588bfcf4d93f50be86cb89545bd5c431edbc71771f704ff55cb52906b1964c37",
"crates/horizon-world/src/stored_consume.rs": "621a52c9fba447c393e561f881a16cf1f97d336e3a0ce493725ef51e2db12ffd",
"crates/horizon-world/src/suffocation.rs": "bed219dd5bbab6263b98fbe3d0eb512cb96c52ae847210efe49514d05f22f11e",
"crates/horizon-world/src/swimming.rs": "49d2dae13c109b37ff832cde98a857944ff45247b864a527b9c4f03ecca7a7b7",
"crates/horizon-world/src/synced_floats.rs": "0152f2752bafaf68769d56dbc88494a976496a680c6b113d6b48a446158ba072",
"crates/horizon-world/src/synced_values.rs": "adffa15026497d6671ffd12e5e17df9329c93f0e022700cdfebb80ab494382ae",
"crates/horizon-world/src/teleport_consume.rs": "d6a53bbb6ac4dbbd4c0591d0646017898a95beb8ef093dc5d81b9833963be6c3",
"crates/horizon-world/src/tick_data.rs": "f8d04a69ea263f4168bce33f92e5b4ee2fe07d7caf50a1621c6b3ef6eb2479f3",
"crates/horizon-world/src/use_remainder.rs": "f555335afeca7970888131a6da2f5a614a73253b631185d1ba804593a349535e",
"crates/horizon-world/src/view_direction.rs": "e18120ae26dc5cf702b29fc4c8487088bab133c8ce5abce01226833fafb0d5a4",
"crates/horizon-world/src/water_splash.rs": "e0461ae1b2e8c851cca6e3251a188334293292201d7f80dc9e7d60fd1815f4d1",
"crates/horizon-world/src/world_movement.rs": "f8fe9e34f2395d2a04c839d1c8ba78e09ce493c26de6f6e4e237b9c19c061da5",
"crates/horizon-world/src/world_ray.rs": "879bdbbe12f56deb705cc183933431489e840b71eade40c33391eed5949c2e61",
"crates/horizon-world/src/worldgen/anchor.rs": "d156b8a0b147a274cdd143409b441f4f43edecde6e8ee14ef9cf0309f743143a",
"crates/horizon-world/src/worldgen/aquifer/config.rs": "fcd2cc3a520c1527c88f095a3b5d38487e7ebb1ccecc5f757f93d04a069add4f",
"crates/horizon-world/src/worldgen/aquifer/noise_based.rs": "74daaa389b5c3dfdc99214dfff256a9e172f83548d2326cbc941d9637cc53fb7",
"crates/horizon-world/src/worldgen/aquifer.rs": "cb4b66d87de3f9f79ae6ffe4badd6f0f44886cd42ad9de2771b8b56c4f1e28c7",
"crates/horizon-world/src/worldgen/bamboo.rs": "d9b47113c99be34256e58308bc6f40072ff25c579d6bf995182aa862c0006102",
"crates/horizon-world/src/worldgen/biome.rs": "3fe316b875aa32683759c337dc71f624910faf0812a2d6d83b8098d6d0bac7a0",
"crates/horizon-world/src/worldgen/biome_chunk.rs": "8fdfe6ebe3ffeca97611fd5991a5bc6b2783ea1c62f4d8ef3b2dd53c04a5dc1d",
"crates/horizon-world/src/worldgen/biome_predicate.rs": "bfc581bc40077cc74ffda43e990a55fe997adc04c7130f98e039c3305ca79aac",
"crates/horizon-world/src/worldgen/biome_source/codec.rs": "2b9e4f5d679eb444cb73174f511e3b9f32e9eb138248ab12cffc6cfd76adf3a8",
"crates/horizon-world/src/worldgen/biome_source/runtime.rs": "f0b02c1521178e4d0377c6ca62694604b6a773f1f234f070ff0cde4685911d45",
"crates/horizon-world/src/worldgen/biome_source.rs": "e181e1736f3e0b75f7091a24c88de3fd116e01b98424d02382aea7a503d1eb54",
"crates/horizon-world/src/worldgen/biome_temperature.rs": "cde4bbab5e260c4065b113376c423b8511ade93c5aa5fd976e245ee8a58b5ad1",
"crates/horizon-world/src/worldgen/biome_weather.rs": "96b1f9494559ed31d06257b83d604d00cc069ccc8c7b68094117e034b4a71bd7",
"crates/horizon-world/src/worldgen/block_blob.rs": "c608c57b3d77279646ecb89e50677dd8e452b0422ffd357ba755536c5589eb89",
"crates/horizon-world/src/worldgen/block_column.rs": "f72c21a9066ee0aa68c2fb79fba436d05afb14c6ef5a2cb012f1ed35fc17cd22",
"crates/horizon-world/src/worldgen/block_feature.rs": "36b4e46ee7f7f73bcb94d8bce8d99013e4b16234c9a03542eeb3186e88dd1c21",
"crates/horizon-world/src/worldgen/block_pile.rs": "623e369b77e0526ed2756485a6c0bb09683cd28ad8876dea907360ca6665b39d",
"crates/horizon-world/src/worldgen/block_predicate.rs": "074c692367e3f2191a1bb28d87b23402430a843c246bd30a4ea838ee3500280e",
"crates/horizon-world/src/worldgen/blue_ice.rs": "eedd77bdaff808330b864166d303378096daa5c7d235e925770b171c05109247",
"crates/horizon-world/src/worldgen/bonus_chest.rs": "05d0f63fbc7412f13ba1fa37a7238114b8f70bfff318beb2dece590482d49cc0",
"crates/horizon-world/src/worldgen/bulk_section.rs": "eecf61532d7793f853884c5a18a7f436a59835c7b13d3ec9075804511553c9ba",
"crates/horizon-world/src/worldgen/carver/canyon.rs": "509c4d5a9d689849e98e894ab14a9fbf82d10707d38e9cc688ded3c4778df3e2",
"crates/horizon-world/src/worldgen/carver/generation.rs": "a4498d0d1457536de3922a08e9c40ccce8d2ef9cc263801c15a412327513c098",
"crates/horizon-world/src/worldgen/carver.rs": "46b251e28225fe1250de38f49dec5dacda4e2c4ba1e556e21e6d1e14e2651fc3",
"crates/horizon-world/src/worldgen/carving.rs": "4e77d4e5c4a0ca55ca3563b9bdf6f379342e0bdf9d019d60f7bc57ce4afb5fbd",
"crates/horizon-world/src/worldgen/chorus_plant.rs": "29224c164ede919e280a65a68f155c873e2c8fd0aa7d7e244d8fa9e468760250",
"crates/horizon-world/src/worldgen/climate/codec.rs": "d567adeb5d837816fca69102db51d790f71aec1b51c09298230d24612e400a14",
"crates/horizon-world/src/worldgen/climate/index.rs": "c5ce61679a5f0e0f2f0af2a373424effa124d0cce8250609eff38b7034650756",
"crates/horizon-world/src/worldgen/climate.rs": "1b9ad44407f40aee4c1cd5357a5c9c443c1b105728f01cf5c644072f99f8357e",
"crates/horizon-world/src/worldgen/decoration.rs": "75a79b20b21e133816e729f16e758be6165d0db18a64bfbd5fd3618325a2adf3",
"crates/horizon-world/src/worldgen/delta.rs": "c2bf49ddf3231d28c109e68871429bc13d76a7bebe14eb33e84aa856bdd66145",
"crates/horizon-world/src/worldgen/density.rs": "65a98c156a290a6b374ef670d28cd5dfbe0aa93171824d1bbd96422536273f84",
"crates/horizon-world/src/worldgen/disk.rs": "6f8f9e059755af44c9bfa1b91eba54a7d13fad811dce31717c5502d7f6df33d7",
"crates/horizon-world/src/worldgen/end_gateway.rs": "f53fde77d6a36a525cd15d890003b2c129d0689a4cbfa2cf443a0c2e3acae040",
"crates/horizon-world/src/worldgen/end_podium.rs": "8f6d0d6d60ff0fde63de5781e867239db321f82cf12639f0537d410a3731ee1d",
"crates/horizon-world/src/worldgen/feature.rs": "40500af71ee6628dfb27518262d99f08d0383ae6c7b9397452feef0c213eadd9",
"crates/horizon-world/src/worldgen/feature_catalog.rs": "efc7b17b227e71622ef34108be9abd010f5675b68ea0409fcab82581d9aab88a",
"crates/horizon-world/src/worldgen/feature_graph.rs": "da132fca9fdafb0d89a917d5f4b68c60b70fb33cadf948a107da0e35d83dcd91",
"crates/horizon-world/src/worldgen/feature_region.rs": "df0ff142a461cafa9a3b42b949ad3376887f2be8d4ea5cc794a3ed2e977bb499",
"crates/horizon-world/src/worldgen/feature_sort.rs": "3488a093dfe1321aaa29d8a30cfb115d8e666a81bf195514e87b170de626835c",
"crates/horizon-world/src/worldgen/fluid_predicate.rs": "64197c9e63bebf1ce04bc280bfe980bb6c1b9aa887ca5cded904aeb0d9ae6c46",
"crates/horizon-world/src/worldgen/height_provider.rs": "368d4dc119854742a045c4a717679c921456da681fc5f71aa9b37102d6f33b47",
"crates/horizon-world/src/worldgen/material_condition/codec.rs": "09b909bc384472f76d163c8069ec6b507c7d5c6a028a36d5e0e3df20deff7b3b",
"crates/horizon-world/src/worldgen/material_condition/density.rs": "df9d0761e978e5010663ae8b44f2d6c1ecb4907331e333bdb6a2b487c1e107de",
"crates/horizon-world/src/worldgen/material_condition/library.rs": "a55534c5da97575a819af5a7dc0dbec9069993fa8b2619c6c9d5576946e683fa",
"crates/horizon-world/src/worldgen/material_condition/noise.rs": "ffb59cc6be2275d7a5621bf7d768527e74a8326ed7ad78618746711540921675",
"crates/horizon-world/src/worldgen/material_condition/rule.rs": "fed643352efbcb1f68f29b6241896617db889fc7c577a6247564e01922fec1fe",
"crates/horizon-world/src/worldgen/material_condition.rs": "d638dcb8cbcb5da20b3cb29602b0ae6a86c4fdf9d8c0fb725de8ec5fc108717e",
"crates/horizon-world/src/worldgen/material_extensions.rs": "ab806eb679d5116e1aebb64fca70e1499ae71f83c9507a8cb4c3201c2b1bf037",
"crates/horizon-world/src/worldgen/material_stage.rs": "698cbc786eff13c246e2bc2ea062401918cd4265899fbc3d53abc87e45a8e7a1",
"crates/horizon-world/src/worldgen/material_surface.rs": "5d37b90099df3d6da0845dab164e71469fb287019c2ee74bc6714f3b9d6d6d5b",
"crates/horizon-world/src/worldgen/ore_feature.rs": "83b89fd3be691c5412c0215b6abefdcaa44119a05aa41217e061970eb7e12ebd",
"crates/horizon-world/src/worldgen/placement/chain.rs": "9218399b4d3757f7bbc550d7729a7201f32a1c83a7030865e0c12aab8a80d116",
"crates/horizon-world/src/worldgen/placement/context.rs": "a278304795136677ad4b4933ea50c972b9729a1c671c03fa309c193e224b1fd6",
"crates/horizon-world/src/worldgen/placement/layer.rs": "a0f5ca776f33f9d4f72b8fdcd163426152e5e43cb4a7572fc1d4fc57bf965de1",
"crates/horizon-world/src/worldgen/placement.rs": "b0397e5818fae84358546193a95f84bcb0b5bffafce909e6fcb3386179ee8023",
"crates/horizon-world/src/worldgen/replace_blobs.rs": "bf3377d86d2570cd15288ba69dde64a2cf67c5bcbb9a31d0c23421964af74afa",
"crates/horizon-world/src/worldgen/router.rs": "2cee7cf15a0d2ada12be6b39d83e78177fd3033922e4f4cce87a64073cec2847",
"crates/horizon-world/src/worldgen/rule_test.rs": "d1662ec492489ebaa4b87e20f9a8c809dcf3a223a151211bd65ec241a63692fc",
"crates/horizon-world/src/worldgen/settings.rs": "a8fe87752e1982ef11cb67f8d80d8e3428b2c27db178044cb425ba803e689e59",
"crates/horizon-world/src/worldgen/simple_block.rs": "9ba1a88d5db4925f8c37366557887f776c805f9a6444781615b5dbaff52e92a4",
"crates/horizon-world/src/worldgen/snow_freeze.rs": "9040be3d5c7f8f0fd812d2df45e154bf8a9eeef038530b83fa4159d246eac35f",
"crates/horizon-world/src/worldgen/speleothem.rs": "c71651c1c97818a8a85863e14414d6139ea3bdeb897fa422043bcc1499d99aae",
"crates/horizon-world/src/worldgen/spike.rs": "eacf0d9d3d2bc3d4bcf62bc87555f90031138236a5bfef0eb5270204f7c2a4fc",
"crates/horizon-world/src/worldgen/spring.rs": "830849e9b89fcdf09e9b06fbefdcdebe3c8c3f82804f60b8bd5de04ba303cb62",
"crates/horizon-world/src/worldgen/state_provider/catalog.rs": "c8687e15a4887a9200c8d365857d801efaa8f90e669eee3e600a1db4905119eb",
"crates/horizon-world/src/worldgen/state_provider/noise.rs": "7ee369c2a032e5620cb9bdf9ee7adad1f70870fb0a27efb7b7c514d92f83491f",
"crates/horizon-world/src/worldgen/state_provider/properties.rs": "9909139aaca1dc4f743957257fadaa9d423d21fa401fa9abc050505f79365139",
"crates/horizon-world/src/worldgen/state_provider.rs": "be29ada818826a90afe01ab60916360fdfc4cbde5af15c9359cd56b1f1f7b156",
"crates/horizon-world/src/worldgen/tags.rs": "74e4110fb19c4c4047192fd65c38015b8afc7f9fc50404d75380b5063808148c",
"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/attribute_updates_fixture.rs": "51fee5271a77066f3e9a0dc7afc7f3b1b01210deeecc6a28680b991bc20d8543"
},
"fixtures_sha256": {
".local/rust-attribute-updates-parity/inputs.txt": "d58c62334e14f9e8770d551bd1d5fd3ae7583ce20bf4da3844ba475b4ffb56fa",
".local/rust-attribute-updates-parity/inputs-client.txt": "846fcc9ff4a84ddf6d2d0ebf5200771032caa0719e64c26adbd812e8735ef63d",
".local/rust-attribute-updates-parity/inputs-server.txt": "846fcc9ff4a84ddf6d2d0ebf5200771032caa0719e64c26adbd812e8735ef63d"
},
"references": [
{
"side": "client",
"jar_sha256": "4508d006323f24fa02876310c192d739af56516eb259000ac50f0909a68c9a2d",
"output_sha256": "22d74ec64fa146cd806175f8e1c97247e2c053ac64f7c2efc04a74b042882b36",
"rust_output_sha256": "22d74ec64fa146cd806175f8e1c97247e2c053ac64f7c2efc04a74b042882b36",
"matched_output_lines": 3111429
},
{
"side": "server",
"jar_sha256": "a362163eec5d1612d520772bc16e5b39c09e3b234fdc045f56bf544284ee8ae6",
"output_sha256": "22d74ec64fa146cd806175f8e1c97247e2c053ac64f7c2efc04a74b042882b36",
"rust_output_sha256": "22d74ec64fa146cd806175f8e1c97247e2c053ac64f7c2efc04a74b042882b36",
"matched_output_lines": 3111429
}
],
"scope": "Inherited LivingEntity.onAttributeUpdated composed with native effect notifications, pending sets and synchronized health storage. Includes max-health and max-absorption lowering, repeated maximum reads, Float dirty callbacks, dimension refresh dispatch, server waypoint manager selection, direct attribute-map field capture and positive-range tracking. Tests finite/nonfinite numeric values, callback mutation/failure prefixes and packet notifications. Actual attribute values, dimension rebuild and waypoint-manager internals remain controlled; registered modifier bodies and complete player/entity gameplay remain pending."
}
@@ -0,0 +1,637 @@
use horizon_world::{
active_effects::{ActiveEffects, EffectHooks},
effect_notifications::{
self as notify, EffectPacket, EligibilityWorld, ImmunityTag, NotificationWorld,
RestrictedEffect,
},
java_hash_map::JavaKey,
object_open_set::{CursorError, ObjectKey, ObjectOpenSet},
status_effect::{BlendState, Details, EffectInstance},
};
use horizon_world::{
attribute_updates::{self, UpdateWorld, UpdatedAttribute},
living_health::{self, HealthWorld, SyncedFloat},
};
use serde_json::Value;
use std::{
cmp::Ordering,
fs,
io::{self, Write},
path::Path,
};
#[derive(Clone)]
struct Key {
id: usize,
hash: i32,
identity: i32,
}
impl PartialEq for Key {
fn eq(&self, other: &Self) -> bool {
self.id == other.id
}
}
impl Eq for Key {}
impl ObjectKey for Key {
fn object_hash(&self) -> i32 {
self.hash
}
}
impl JavaKey for Key {
fn java_hash(&self) -> i32 {
self.hash
}
fn tie_break(&self, other: &Self) -> Ordering {
if self.identity <= other.identity {
Ordering::Less
} else {
Ordering::Greater
}
}
}
type Effect = EffectInstance<Key>;
type Storage = ActiveEffects<Key>;
fn n(v: &Value, k: &str) -> i32 {
v[k].as_i64().unwrap() as i32
}
fn fl(v: &Value, k: &str) -> f32 {
f32::from_bits(v[k].as_u64().unwrap() as u32)
}
fn bits(v: f32) -> u32 {
if v.is_nan() { 0x7fc00000 } else { v.to_bits() }
}
fn details(v: &Value) -> Details {
Details {
duration: n(v, "duration"),
amplifier: n(v, "amplifier"),
ambient: v["ambient"].as_bool().unwrap(),
visible: v["visible"].as_bool().unwrap(),
show_icon: v["icon"].as_bool().unwrap(),
}
}
struct World<'a> {
health: [SyncedFloat; 2],
absorption: f32,
f: Value,
out: &'a mut dyn Write,
keys: Vec<Key>,
attribute_keys: Vec<Key>,
updates: [ObjectOpenSet<Key>; 2],
pool: Vec<Effect>,
ids: Vec<Effect>,
calls: i32,
changed: bool,
}
impl World<'_> {
fn config(&self) -> &Value {
&self.f[if self.changed { "after" } else { "initial" }]
}
fn id(&mut self, e: &Effect) -> usize {
if let Some(i) = self.ids.iter().position(|v| v.same_reference(e)) {
i
} else {
self.ids.push(e.clone());
self.ids.len() - 1
}
}
fn reset(&mut self, storage: &mut Storage) {
if let Some(bits) = self.config()["absorption"].as_u64() {
self.absorption = f32::from_bits(bits as u32);
}
if let Some(dirty) = self.config()["dirty"].as_bool() {
storage.dirty = dirty;
}
let c = self.config().clone();
for (i, v) in c["states"].as_array().unwrap().iter().enumerate() {
let mut e = self.pool[i].write();
e.details = details(v);
e.blend = BlendState {
factor: fl(v, "factor"),
previous: fl(v, "previous"),
};
}
}
fn all(&mut self, s: &Storage) {
writeln!(
self.out,
"state {} {} {}",
self.calls, self.changed, s.dirty
)
.unwrap();
for (i, h) in self.health.iter().enumerate() {
writeln!(
self.out,
"health-data {i} {} {} {} {}",
bits(h.value()),
h.is_dirty(),
h.item_dirty(),
h.is_default()
)
.unwrap();
}
writeln!(self.out, "absorption-data {}", bits(self.absorption)).unwrap();
for (i, updates) in self.updates.iter().enumerate() {
writeln!(
self.out,
"updates {i} {}",
updates
.values()
.map(|v| v.map_or("null".into(), |k| k.id.to_string()))
.collect::<Vec<_>>()
.join(",")
)
.unwrap();
}
let mut roots = Vec::new();
let entries = s
.entries
.entries()
.map(|(k, e)| {
let id = self.id(e);
roots.push(e.clone());
format!("{}={id}", k.id)
})
.collect::<Vec<_>>()
.join(",");
writeln!(self.out, "map {entries}").unwrap();
for (i, e) in self.pool.clone().iter().enumerate() {
let id = self.id(e);
writeln!(self.out, "pool {i} {id}").unwrap();
roots.push(e.clone());
}
let mut seen = Vec::new();
let mut i = 0;
while i < roots.len() {
let e = roots[i].clone();
i += 1;
if seen.iter().any(|v: &Effect| v.same_reference(&e)) {
continue;
}
seen.push(e.clone());
let id = self.id(&e);
let d = e.read();
let h = d
.hidden
.as_ref()
.map(|h| {
roots.push(h.clone());
self.id(h).to_string()
})
.unwrap_or("none".into());
writeln!(
self.out,
"node {id} {} {} {} {} {} {} {h} {} {}",
d.effect.id,
d.details.duration,
d.details.amplifier,
d.details.ambient,
d.details.visible,
d.details.show_icon,
bits(d.blend.factor),
bits(d.blend.previous)
)
.unwrap();
}
}
fn raw(&mut self, s: &mut Storage, op: &Value) {
let a = n(op, "a") as usize;
let b = n(op, "b") as usize;
match op["op"].as_str().unwrap() {
"pending-add" => {
let key = op["b"].as_i64().unwrap();
self.updates[a].add((key >= 0).then(|| self.attribute_keys[key as usize].clone()));
}
"pending-remove" => {
let key = op["b"].as_i64().unwrap();
self.updates[a].remove((key >= 0).then(|| &self.attribute_keys[key as usize]));
}
"pending-clear" => self.updates[a].clear(),
"put" => {
s.entries.put(self.keys[a].clone(), self.pool[b].clone());
}
"drop" => {
s.entries.remove(&self.keys[a]);
}
"clear" => s.entries.clear(),
"copy" => self.pool[b] = self.pool[a].copy_instance(),
"alias" => self.pool[b] = self.pool[a].clone(),
_ => panic!("raw op"),
}
}
fn event(&mut self, s: &mut Storage, name: String) -> Result<(), &'static str> {
writeln!(self.out, "event {name}").unwrap();
let i = self.calls;
self.calls += 1;
self.all(s);
if i == n(&self.f, "fail") {
return Err("callback");
}
if i == n(&self.f, "mutate") {
self.changed = true;
self.reset(s);
for op in self.f["mutations"].as_array().unwrap().clone() {
self.raw(s, &op);
}
}
Ok(())
}
}
impl EffectHooks<Key> for World<'_> {
type Error = &'static str;
type Source = i32;
type Level = i32;
fn can_be_affected(&mut self, s: &mut Storage, e: Effect) -> Result<bool, Self::Error> {
notify::can_be_affected(s, &e, self)
}
fn on_added(&mut self, s: &mut Storage, e: Effect, _: Option<i32>) -> Result<(), Self::Error> {
notify::on_added(s, &e, self)
}
fn on_updated(
&mut self,
s: &mut Storage,
e: Effect,
reapply: bool,
_: Option<i32>,
) -> Result<(), Self::Error> {
notify::on_updated(s, &e, reapply, self)
}
fn effect_added(&mut self, s: &mut Storage, e: Key, amp: i32) -> Result<(), Self::Error> {
if e.id < 4 {
Ok(())
} else {
self.event(s, format!("effect-added {} {amp}", e.id))
}
}
fn effect_started(&mut self, s: &mut Storage, e: Key, amp: i32) -> Result<(), Self::Error> {
if e.id < 4 {
Ok(())
} else {
self.event(s, format!("effect-started {} {amp}", e.id))
}
}
fn level(&mut self, s: &mut Storage) -> Result<i32, Self::Error> {
self.event(s, "level".into())?;
Ok(n(self.config(), "level"))
}
fn is_client(&mut self, s: &mut Storage, l: i32) -> Result<bool, Self::Error> {
self.event(s, format!("client {l}"))?;
Ok(self.config()["client"].as_bool().unwrap())
}
fn on_removed(&mut self, s: &mut Storage, es: Vec<Effect>) -> Result<(), Self::Error> {
notify::on_removed(s, &es, self)
}
fn get_effect(&mut self, s: &mut Storage, key: Key) -> Result<Option<Effect>, Self::Error> {
self.event(s, format!("get {}", key.id))?;
Ok(s.get(&key))
}
fn actor_removed(&self) -> bool {
self.config()["removed"].as_bool().unwrap()
}
}
impl EligibilityWorld<Key> for World<'_> {
type Error = &'static str;
fn has_immunity_tag(&mut self, s: &mut Storage, tag: ImmunityTag) -> Result<bool, Self::Error> {
let id = tag as usize;
self.event(s, format!("tag {id}"))?;
Ok(self.config()["tags"][id].as_bool().unwrap())
}
fn is_restricted_effect(&self, effect: &Key, kind: RestrictedEffect) -> bool {
effect.id == kind as usize
}
}
impl NotificationWorld<Key> for World<'_> {
type Attributes = usize;
type UpdateSet = usize;
type AttributeInstance = Key;
type Attribute = i32;
type Passenger = usize;
type Connection = i32;
fn attributes(&mut self, s: &mut Storage) -> Result<usize, Self::Error> {
self.event(s, "attributes".into())?;
Ok(n(self.config(), "attributes") as usize)
}
fn add_modifiers(
&mut self,
s: &mut Storage,
e: Key,
a: usize,
amp: i32,
) -> Result<(), Self::Error> {
if e.id < 4 {
return Ok(());
}
self.event(s, format!("add-modifiers {} {a} {amp}", e.id))
}
fn remove_modifiers(&mut self, s: &mut Storage, e: Key, a: usize) -> Result<(), Self::Error> {
if e.id < 4 {
return Ok(());
}
self.event(s, format!("remove-modifiers {} {a}", e.id))
}
fn pending_updates(&mut self, s: &mut Storage, a: usize) -> Result<usize, Self::Error> {
self.event(s, format!("pending {a}"))?;
Ok(self.config()["routes"][a].as_u64().unwrap() as usize)
}
fn update_set(&self, s: usize) -> &ObjectOpenSet<Key> {
&self.updates[s]
}
fn update_set_mut(&mut self, s: usize) -> &mut ObjectOpenSet<Key> {
&mut self.updates[s]
}
fn iterator_error(e: CursorError) -> Self::Error {
match e {
CursorError::MissingWrapped => "null",
CursorError::IndexOutOfBounds => "bounds",
CursorError::NoSuchElement => "empty",
CursorError::NoCurrentEntry => "current",
}
}
fn attribute_of(&mut self, s: &mut Storage, i: Option<Key>) -> Result<i32, Self::Error> {
let i = i.ok_or("null")?;
self.event(s, format!("attribute {}", i.id))?;
Ok(self.config()["holders"][i.id].as_i64().unwrap() as i32)
}
fn attribute_updated(&mut self, s: &mut Storage, a: i32) -> Result<(), Self::Error> {
attribute_updates::updated(&mut Update { w: self, s }, a)
}
fn passengers(&self) -> Vec<usize> {
self.config()["passengers"]
.as_array()
.unwrap()
.iter()
.map(|p| p.as_u64().unwrap() as usize)
.collect()
}
fn is_server_player(&self, p: usize) -> bool {
p < 2
}
fn connection(&self, p: usize) -> i32 {
self.config()["connections"][p].as_i64().unwrap() as i32
}
fn entity_id(&mut self, s: &mut Storage) -> Result<i32, Self::Error> {
self.event(s, "entity-id".into())?;
Ok(n(self.config(), "entity"))
}
fn send_packet(
&mut self,
s: &mut Storage,
c: i32,
p: EffectPacket<Key>,
) -> Result<(), Self::Error> {
if c < 0 {
return Err("null");
}
let message = match p {
EffectPacket::Update {
entity,
effect,
amplifier,
duration,
flags,
} => format!(
"packet {c} update {entity} {} {amplifier} {duration} {flags}",
effect.id
),
EffectPacket::Remove { entity, effect } => {
format!("packet {c} remove {entity} {}", effect.id)
}
};
self.event(s, message)
}
}
struct Update<'w, 'a> {
w: &'w mut World<'a>,
s: &'w mut Storage,
}
impl HealthWorld for Update<'_, '_> {
type Error = &'static str;
type Data = usize;
fn health(&mut self) -> Result<f32, Self::Error> {
self.w.event(self.s, "health".into())?;
Ok(self.w.health[self.health_data()].value())
}
fn health_data(&self) -> usize {
n(self.w.config(), "data") as usize
}
fn max_health_attribute(&mut self) -> Result<f64, Self::Error> {
self.w.event(self.s, "max-health".into())?;
Ok(f64::from_bits(
self.w.config()["maximums"][0].as_u64().unwrap(),
))
}
fn max_absorption_attribute(&mut self) -> Result<f64, Self::Error> {
self.w.event(self.s, "max-absorption".into())?;
Ok(f64::from_bits(
self.w.config()["maximums"][1].as_u64().unwrap(),
))
}
fn store_health(&mut self, data: usize, value: f32) -> Result<(), Self::Error> {
let mut h = self.w.health[data];
let result = h.set(value, false, |h| {
self.w.health[data] = *h;
self.w.event(self.s, "health-updated".into())
});
self.w.health[data] = h;
result
}
fn store_absorption(&mut self, value: f32) -> Result<(), Self::Error> {
self.w
.event(self.s, format!("internal-absorption {}", bits(value)))?;
self.w.absorption = value;
Ok(())
}
fn dead_flag(&self) -> bool {
false
}
fn dispatch_set_health(&mut self, value: f32) -> Result<(), Self::Error> {
self.w
.event(self.s, format!("set-health {}", bits(value)))?;
living_health::set_health(self, value)
}
}
impl UpdateWorld<i32> for Update<'_, '_> {
type Level = i32;
type Manager = usize;
type Attributes = usize;
fn attribute_kind(&self, h: &i32) -> UpdatedAttribute {
match *h {
0 => UpdatedAttribute::MaxHealth,
1 => UpdatedAttribute::MaxAbsorption,
2 => UpdatedAttribute::Scale,
3 => UpdatedAttribute::WaypointRange,
_ => UpdatedAttribute::Other,
}
}
fn absorption(&mut self) -> Result<f32, Self::Error> {
self.w.event(self.s, "absorption".into())?;
Ok(self.w.absorption)
}
fn refresh_dimensions(&mut self) -> Result<(), Self::Error> {
self.w.event(self.s, "dimensions".into())
}
fn level(&mut self) -> Result<i32, Self::Error> {
EffectHooks::level(self.w, self.s)
}
fn is_server_level(&self, l: i32) -> bool {
self.w.f["servers"][l as usize].as_bool().unwrap()
}
fn waypoint_manager(&mut self, l: i32) -> Result<usize, Self::Error> {
self.w.event(self.s, format!("manager {l}"))?;
Ok(self.w.config()["managers"][l as usize].as_u64().unwrap() as usize)
}
fn attributes_field(&self) -> usize {
n(self.w.config(), "direct_attributes") as usize
}
fn attribute_value_in(&mut self, a: usize, h: i32) -> Result<f64, Self::Error> {
self.w.event(self.s, format!("value-in {a} {h}"))?;
Ok(f64::from_bits(
self.w.config()["values"][a][h as usize].as_u64().unwrap(),
))
}
fn track_waypoint(&mut self, m: usize, track: bool) -> Result<(), Self::Error> {
self.w.event(self.s, format!("waypoint {m} {track}"))
}
}
fn run(f: Value, out: &mut dyn Write) {
let keys = f["hashes"]
.as_array()
.unwrap()
.iter()
.enumerate()
.map(|(id, v)| Key {
id,
hash: v.as_i64().unwrap() as i32,
identity: f["identities"][id].as_i64().unwrap() as i32,
})
.collect::<Vec<_>>();
let mut pool: Vec<Effect> = Vec::new();
for v in f["initial"]["states"].as_array().unwrap() {
let h = n(v, "hidden");
pool.push(Effect::new(
keys[n(v, "key") as usize].clone(),
details(v),
(h >= 0).then(|| pool[h as usize].clone()),
));
}
let attribute_keys = f["attribute_hashes"]
.as_array()
.unwrap()
.iter()
.enumerate()
.map(|(id, h)| Key {
id,
hash: h.as_i64().unwrap() as i32,
identity: 0,
})
.collect();
let health = std::array::from_fn(|i| {
SyncedFloat::new(f32::from_bits(f["health"][i].as_u64().unwrap() as u32))
});
let mut w = World {
health,
absorption: 0.0,
attribute_keys,
updates: std::array::from_fn(|_| ObjectOpenSet::default()),
f,
out,
keys,
pool,
ids: Vec::new(),
calls: 0,
changed: false,
};
let mut s = Storage::default();
w.reset(&mut s);
for op in w.f["setup"].as_array().unwrap().clone() {
w.raw(&mut s, &op);
}
w.all(&s);
for op in w.f["ops"].as_array().unwrap().clone() {
let a = n(&op, "a") as usize;
let b = n(&op, "b") as usize;
let source = n(&op, "source");
let source = (source >= 0).then_some(source);
let result: Result<String, &'static str> = match op["op"].as_str().unwrap() {
"attribute-update" => attribute_updates::updated(
&mut Update {
w: &mut w,
s: &mut s,
},
a as i32,
)
.map(|_| "ok".into()),
"pack" => Ok(w.health[a]
.take_dirty()
.map(|v| bits(v).to_string())
.unwrap_or("none".into())),
"eligible" => {
let e = w.pool[a].clone();
notify::can_be_affected(&mut s, &e, &mut w).map(|v| v.to_string())
}
"notify-add" => {
let e = w.pool[a].clone();
notify::on_added(&mut s, &e, &mut w).map(|_| "ok".into())
}
"notify-update" => {
let e = w.pool[a].clone();
notify::on_updated(&mut s, &e, op["reapply"].as_bool().unwrap(), &mut w)
.map(|_| "ok".into())
}
"notify-remove" => {
let es = op["list"]
.as_array()
.unwrap()
.iter()
.map(|i| w.pool[i.as_u64().unwrap() as usize].clone())
.collect::<Vec<_>>();
notify::on_removed(&mut s, &es, &mut w).map(|_| "ok".into())
}
"send" => {
let e = w.pool[a].clone();
notify::send_to_passengers(&mut s, &e, &mut w).map(|_| "ok".into())
}
"refresh" => notify::refresh_attributes(&mut s, &mut w).map(|_| "ok".into()),
"pending-add" | "pending-remove" | "pending-clear" => {
w.raw(&mut s, &op);
Ok("ok".into())
}
"add" => s
.add(w.pool[a].clone(), source, &mut w)
.map(|v| v.to_string()),
"force" => s
.force_add(w.pool[a].clone(), source, &mut w)
.map(|_| "ok".into()),
"remove" => s.remove(&w.keys[a].clone(), &mut w).map(|v| v.to_string()),
"remove-raw" => Ok(s
.remove_no_update(&w.keys[a])
.map(|e| w.id(&e).to_string())
.unwrap_or("none".into())),
"all" => s.remove_all(&mut w).map(|v| v.to_string()),
"has" => Ok(s.has(&w.keys[a]).to_string()),
"get" => {
let key = w.keys[a].clone();
EffectHooks::get_effect(&mut w, &mut s, key)
.map(|e| e.map(|e| w.id(&e).to_string()).unwrap_or("none".into()))
}
"blend" => s
.blend_factor(w.keys[a].clone(), fl(&op, "partial"), &mut w)
.map(|v| bits(v).to_string()),
"put" | "drop" | "clear" | "copy" | "alias" => {
w.raw(&mut s, &op);
Ok("ok".into())
}
_ => panic!("operation {b}"),
};
writeln!(w.out, "result {}", result.unwrap_or_else(str::to_string)).unwrap();
w.all(&s);
}
}
fn main() -> Result<(), Box<dyn std::error::Error>> {
let arg = std::env::args().nth(1).unwrap();
let side = std::env::args().nth(2).unwrap();
let dir = Path::new(&arg);
let mut out = io::BufWriter::new(fs::File::create(dir.join(format!("rust-{side}.txt")))?);
for line in fs::read_to_string(dir.join(format!("inputs-{side}.txt")))?.lines() {
run(serde_json::from_str(line)?, &mut out);
}
Ok(())
}
@@ -0,0 +1,57 @@
//! Consequences of LivingEntity attribute changes, preserving virtual call order.
use crate::living_health::{self, HealthWorld};
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum UpdatedAttribute {
MaxHealth,
MaxAbsorption,
Scale,
WaypointRange,
Other,
}
pub trait UpdateWorld<H>: HealthWorld {
type Level: Copy;
type Manager: Copy;
type Attributes: Copy;
fn attribute_kind(&self, attribute: &H) -> UpdatedAttribute;
fn absorption(&mut self) -> Result<f32, Self::Error>;
fn refresh_dimensions(&mut self) -> Result<(), Self::Error>;
fn level(&mut self) -> Result<Self::Level, Self::Error>;
fn is_server_level(&self, level: Self::Level) -> bool;
fn waypoint_manager(&mut self, level: Self::Level) -> Result<Self::Manager, Self::Error>;
/// Direct attributes field, read after obtaining the waypoint manager.
fn attributes_field(&self) -> Self::Attributes;
fn attribute_value_in(
&mut self,
attributes: Self::Attributes,
attribute: H,
) -> Result<f64, Self::Error>;
fn track_waypoint(&mut self, manager: Self::Manager, track: bool) -> Result<(), Self::Error>;
}
pub fn updated<H, W: UpdateWorld<H>>(world: &mut W, attribute: H) -> Result<(), W::Error> {
match world.attribute_kind(&attribute) {
UpdatedAttribute::MaxHealth => {
let maximum = world.max_health_attribute()? as f32;
if world.health()? > maximum {
world.dispatch_set_health(maximum)?;
}
}
UpdatedAttribute::MaxAbsorption => {
let maximum = world.max_absorption_attribute()? as f32;
if world.absorption()? > maximum {
living_health::set_absorption(world, maximum)?;
}
}
UpdatedAttribute::Scale => world.refresh_dimensions()?,
UpdatedAttribute::WaypointRange => {
let level = world.level()?;
if world.is_server_level(level) {
let manager = world.waypoint_manager(level)?;
let attributes = world.attributes_field();
let range = world.attribute_value_in(attributes, attribute)?;
world.track_waypoint(manager, range > 0.0)?;
}
}
UpdatedAttribute::Other => {}
}
Ok(())
}
+1
View File
@@ -7,6 +7,7 @@ pub mod active_effects;
pub mod active_effect_tick;
pub mod stored_consume;
pub mod attribute_health;
pub mod attribute_updates;
pub mod attributes;
pub mod block_collision;
pub mod block_collision_scan;
+18
View File
@@ -1078,3 +1078,21 @@ storage. The fixtures use actual status instances, pending sets, immutable
passenger lists and packet constructors. Library and fixture Clippy pass.
Modifier bodies, attribute-update consequences and packet transport are still
controlled; their full bindings and complete gameplay remain unfinished.
## Attribute-update consequences and health storage
Pending attribute refresh now dispatches native max-health, max-absorption,
scale and waypoint-range consequences. Lowering a health limit compares against
the first captured maximum, then uses the ordinary setter and its fresh limit
lookup. Health synchronization retains its selected data object through later
callbacks. Waypoint changes capture the server manager before reading the
actor's direct attribute-map field, and track only a strictly positive range.
The [combined attribute-update comparison](../benchmarks/rust-attribute-updates-2026-10-07/scoped-parity.json)
matches **3,111,429 lines per reference JAR**, including the notification corpus,
finite/nonfinite limits, signed zero, dirty health callbacks, map/manager changes
and failure prefixes. Actual native effect notifications, attribute-update
methods, health setters and synchronized Float storage execute together. Library
and fixture Clippy pass. Attribute values, dimension rebuilds and waypoint-manager
internals remain controlled. Registered modifier bodies, full player integration
and complete gameplay/platform builds are still unfinished.
@@ -0,0 +1,94 @@
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.Holder;import net.minecraft.world.effect.*;import net.minecraft.world.entity.*;import net.minecraft.world.level.Level;import net.minecraft.server.level.ServerLevel;
import net.minecraft.world.entity.ai.attributes.*;import net.minecraft.server.level.ServerPlayer;import net.minecraft.server.network.ServerGamePacketListenerImpl;import net.minecraft.network.protocol.Packet;import net.minecraft.network.protocol.game.*;import net.minecraft.tags.*;import com.google.common.collect.ImmutableList;import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet;
import net.minecraft.network.syncher.*;import net.minecraft.server.waypoints.ServerWaypointManager;import net.minecraft.world.waypoints.WaypointTransmitter;
/** Native attribute-update consequences composed with effect notifications and health storage. */
public final class VanillaAttributeUpdatesOracle {
static Object unsafe;static Method allocate;static Field duration,amplifier,ambient,visible,icon,hidden,blend,factor,previous,removed,activeEffects;static Data active;static Field dirty,passengers;static Method refresh;static List<Holder<MobEffect>> known;static List<Holder<Attribute>> attributeHolders;static Field healthData,absorption,attributesField;static EntityDataAccessor<Float> healthKey;static Constructor<SynchedEntityData> dataCtor;
static Field field(Class<?> c,String n)throws Exception{var f=c.getDeclaredField(n);f.setAccessible(true);return f;}
static <T>T make(Class<T> c)throws Exception{return c.cast(allocate.invoke(unsafe,c));}
static int n(JsonObject v,String k){return v.get(k).getAsInt();}
static float fl(JsonObject v,String k){return Float.intBitsToFloat((int)v.get(k).getAsLong());}
static long bits(float v){return Integer.toUnsignedLong(Float.floatToIntBits(v));}
static final class Stop extends RuntimeException{}
static final class Data {JsonObject f;PrintWriter out;List<Holder<MobEffect>> keys=new ArrayList<>();MobEffectInstance[] pool;IdentityHashMap<MobEffectInstance,Integer> ids=new IdentityHashMap<>();Actor actor;Entity other;Level[] levels;SynchedEntityData[] healthStores;SynchedEntityData.DataItem<Float>[] healthItems;Manager[] managers;Attr[] attributeInstances;Attrs[] attributes;List<Set<AttributeInstance>> updates;ServerPlayer[] riders;Connection[] connections;int calls;boolean changed;
JsonObject config(){return f.getAsJsonObject(changed?"after":"initial");}
Map<Holder<MobEffect>,MobEffectInstance> map(){return actor.getActiveEffectsMap();}
int id(MobEffectInstance e){return ids.computeIfAbsent(e,k->ids.size());}
int key(Holder<MobEffect> h){return keys.indexOf(h);}
int source(Entity e){if(e==null)return -1;if(e==actor)return 0;if(e==other)return 1;throw new AssertionError();}
void reset(){try{var c=config();healthData.set(actor,healthStores[n(c,"data")]);attributesField.set(actor,attributes[n(c,"direct_attributes")]);if(!c.get("absorption").isJsonNull())absorption.setFloat(actor,fl(c,"absorption"));if(!c.get("dirty").isJsonNull())dirty.setBoolean(actor,c.get("dirty").getAsBoolean());for(int i=0;i<2;i++){int connection=c.getAsJsonArray("connections").get(i).getAsInt();riders[i].connection=connection<0?null:connections[connection];}var ps=new ArrayList<Entity>();for(var p:c.getAsJsonArray("passengers")){int id=p.getAsInt();ps.add(id<2?riders[id]:other);}passengers.set(actor,ImmutableList.copyOf(ps));removed.set(actor,c.get("removed").getAsBoolean()?Entity.RemovalReason.DISCARDED:null);for(int i=0;i<pool.length;i++){var v=c.getAsJsonArray("states").get(i).getAsJsonObject();var e=pool[i];duration.setInt(e,n(v,"duration"));amplifier.setInt(e,n(v,"amplifier"));ambient.setBoolean(e,v.get("ambient").getAsBoolean());visible.setBoolean(e,v.get("visible").getAsBoolean());icon.setBoolean(e,v.get("icon").getAsBoolean());factor.setFloat(blend.get(e),fl(v,"factor"));previous.setFloat(blend.get(e),fl(v,"previous"));}}catch(Exception e){throw new AssertionError(e);}}
void all(){try{out.println("state "+calls+" "+changed+" "+dirty.getBoolean(actor));for(int i=0;i<2;i++)out.println("health-data "+i+" "+bits(healthItems[i].getValue())+" "+healthStores[i].isDirty()+" "+healthItems[i].isDirty()+" "+healthItems[i].isSetToDefault());out.println("absorption-data "+bits(absorption.getFloat(actor)));for(int i=0;i<2;i++){var set=new StringJoiner(",");updates.get(i).forEach(v->set.add(v==null?"null":Integer.toString(((Attr)v).id)));out.println("updates "+i+" "+set);}var roots=new ArrayList<MobEffectInstance>();var entries=new StringJoiner(",");for(var e:map().entrySet()){entries.add(key(e.getKey())+"="+id(e.getValue()));roots.add(e.getValue());}out.println("map "+entries);for(int i=0;i<pool.length;i++){out.println("pool "+i+" "+id(pool[i]));roots.add(pool[i]);}var seen=new IdentityHashMap<MobEffectInstance,Boolean>();for(int i=0;i<roots.size();i++){var e=roots.get(i);if(seen.put(e,true)!=null)continue;int id=id(e);var h=(MobEffectInstance)hidden.get(e);String hid="none";if(h!=null){roots.add(h);hid=Integer.toString(id(h));}var b=blend.get(e);out.println("node "+id+" "+key(e.getEffect())+" "+e.getDuration()+" "+e.getAmplifier()+" "+e.isAmbient()+" "+e.isVisible()+" "+e.showIcon()+" "+hid+" "+bits(factor.getFloat(b))+" "+bits(previous.getFloat(b)));}}catch(Exception e){throw new AssertionError(e);}}
void raw(JsonObject op){int a=n(op,"a"),b=n(op,"b");switch(op.get("op").getAsString()){case "pending-add"->updates.get(a).add(b<0?null:attributeInstances[b]);case "pending-remove"->updates.get(a).remove(b<0?null:attributeInstances[b]);case "pending-clear"->updates.get(a).clear();case "put"->map().put(keys.get(a),pool[b]);case "drop"->map().remove(keys.get(a));case "clear"->map().clear();case "copy"->pool[b]=new MobEffectInstance(pool[a]);case "alias"->pool[b]=pool[a];default->throw new AssertionError();}}
void event(String name){out.println("event "+name);int i=calls++;all();if(i==n(f,"fail"))throw new Stop();if(i==n(f,"mutate")){changed=true;reset();for(var op:f.getAsJsonArray("mutations"))raw(op.getAsJsonObject());}}
}
static final class Effect extends MobEffect {int id,hash;Effect(int id,int hash){super(MobEffectCategory.BENEFICIAL,0);this.id=id;this.hash=hash;}@Override public int hashCode(){return hash;}
@Override public void addAttributeModifiers(AttributeMap attributes,int amp){active.event("add-modifiers "+id+" "+((Attrs)attributes).id+" "+amp);}
@Override public void removeAttributeModifiers(AttributeMap attributes){active.event("remove-modifiers "+id+" "+((Attrs)attributes).id);}
@Override public void onEffectAdded(LivingEntity actor,int amp){if(actor!=active.actor)throw new AssertionError();active.event("effect-added "+id+" "+amp);}
@Override public void onEffectStarted(LivingEntity actor,int amp){if(actor!=active.actor)throw new AssertionError();active.event("effect-started "+id+" "+amp);}
}
static final class Actor extends net.minecraft.world.entity.decoration.ArmorStand {Data d;Actor(){super(null,null);}
@Override public boolean is(TagKey<EntityType<?>> tag){int i=tag==EntityTypeTags.IMMUNE_TO_INFESTED?0:tag==EntityTypeTags.IMMUNE_TO_OOZING?1:tag==EntityTypeTags.IGNORES_POISON_AND_REGEN?2:-1;if(i<0)throw new AssertionError();d.event("tag "+i);return d.config().getAsJsonArray("tags").get(i).getAsBoolean();}
@Override public AttributeMap getAttributes(){d.event("attributes");return d.attributes[n(d.config(),"attributes")];}
void attributeUpdate(int id){super.onAttributeUpdated(attributeHolders.get(id));}
@Override public double getAttributeValue(Holder<Attribute> h){int i=h==Attributes.MAX_HEALTH?0:h==Attributes.MAX_ABSORPTION?1:-1;if(i<0)throw new AssertionError();d.event(i==0?"max-health":"max-absorption");return Double.longBitsToDouble(d.config().getAsJsonArray("maximums").get(i).getAsLong());}
@Override public float getHealth(){d.event("health");return super.getHealth();}
@Override public void setHealth(float v){d.event("set-health "+bits(v));super.setHealth(v);}
@Override public float getAbsorptionAmount(){d.event("absorption");return super.getAbsorptionAmount();}
@Override protected void internalSetAbsorptionAmount(float v){d.event("internal-absorption "+bits(v));super.internalSetAbsorptionAmount(v);}
@Override public void refreshDimensions(){d.event("dimensions");}
@Override public void onSyncedDataUpdated(EntityDataAccessor<?> key){if(key!=healthKey)throw new AssertionError();d.event("health-updated");}
@Override public int getId(){d.event("entity-id");return n(d.config(),"entity");}
void added(MobEffectInstance e){super.onEffectAdded(e,null);}void updated(MobEffectInstance e,boolean apply){super.onEffectUpdated(e,apply,null);}void removed(List<MobEffectInstance> es){super.onEffectsRemoved(es);}
@Override public Level level(){d.event("level");return d.levels[n(d.config(),"level")];}
@Override public MobEffectInstance getEffect(Holder<MobEffect> e){d.event("get "+d.key(e));return super.getEffect(e);}
}
static final class EffectLevel extends ServerLevel{Data d;int id;EffectLevel(){super(null,null,null,null,null,null,false,0L,List.of(),false);}@Override public ServerWaypointManager getWaypointManager(){d.event("manager "+id);return d.managers[d.config().getAsJsonArray("managers").get(id).getAsInt()];}@Override public boolean isClientSide(){d.event("client "+id);return d.config().get("client").getAsBoolean();}}
static final class Attr extends AttributeInstance {Data d;int id,hash;Attr(){super(null,null);}@Override public int hashCode(){return hash;}@Override public Holder<Attribute> getAttribute(){d.event("attribute "+id);return attributeHolders.get(d.config().getAsJsonArray("holders").get(id).getAsInt());}}
static final class Attrs extends AttributeMap {Data d;int id;Attrs(){super(null);}@Override public double getValue(Holder<Attribute> h){int a=attributeHolders.indexOf(h);d.event("value-in "+id+" "+a);return Double.longBitsToDouble(d.config().getAsJsonArray("values").get(id).getAsJsonArray().get(a).getAsLong());}@Override public Set<AttributeInstance> getAttributesToUpdate(){d.event("pending "+id);return d.updates.get(d.config().getAsJsonArray("routes").get(id).getAsInt());}}
static final class Connection extends ServerGamePacketListenerImpl {Data d;int id;Connection(){super(null,null,null,null);}@Override public void send(Packet<?> packet){String body;if(packet instanceof ClientboundUpdateMobEffectPacket p){int flags=(p.isEffectAmbient()?1:0)|(p.isEffectVisible()?2:0)|(p.effectShowsIcon()?4:0)|(p.shouldBlend()?8:0);body="update "+p.getEntityId()+" "+d.key(p.getEffect())+" "+p.getEffectAmplifier()+" "+p.getEffectDurationTicks()+" "+flags;}else if(packet instanceof ClientboundRemoveMobEffectPacket p){body="remove "+p.entityId()+" "+d.key(p.effect());}else throw new AssertionError();d.event("packet "+id+" "+body);}}
static final class ClientEffectLevel extends Level {Data d;int id;ClientEffectLevel(){super(null,null,null,null,false,false,0L,0);}@Override public boolean isClientSide(){d.event("client "+id);return d.config().get("client").getAsBoolean();}
@Override public net.minecraft.world.clock.ClockManager clockManager(){throw new UnsupportedOperationException();}
@Override public void destroyBlockProgress(int p0,net.minecraft.core.BlockPos p1,int p2){throw new UnsupportedOperationException();}
@Override public java.util.Collection dragonParts(){throw new UnsupportedOperationException();}
@Override public net.minecraft.world.flag.FeatureFlagSet enabledFeatures(){throw new UnsupportedOperationException();}
@Override public net.minecraft.world.attribute.EnvironmentAttributeSystem environmentAttributes(){throw new UnsupportedOperationException();}
@Override public void explode(net.minecraft.world.entity.Entity p0,net.minecraft.world.damagesource.DamageSource p1,net.minecraft.world.level.ExplosionDamageCalculator p2,double p3,double p4,double p5,float p6,boolean p7,net.minecraft.world.level.Level.ExplosionInteraction p8,net.minecraft.core.particles.ParticleOptions p9,net.minecraft.core.particles.ParticleOptions p10,net.minecraft.util.random.WeightedList p11,net.minecraft.core.Holder p12){throw new UnsupportedOperationException();}
@Override public void gameEvent(net.minecraft.core.Holder p0,net.minecraft.world.phys.Vec3 p1,net.minecraft.world.level.gameevent.GameEvent.Context p2){throw new UnsupportedOperationException();}
@Override public java.lang.String gatherChunkSourceStats(){throw new UnsupportedOperationException();}
@Override public net.minecraft.world.ticks.LevelTickAccess getBlockTicks(){throw new UnsupportedOperationException();}
@Override public net.minecraft.world.level.chunk.ChunkSource getChunkSource(){throw new UnsupportedOperationException();}
@Override public net.minecraft.world.level.entity.LevelEntityGetter getEntities(){throw new UnsupportedOperationException();}
@Override public net.minecraft.world.entity.Entity getEntity(int p0){throw new UnsupportedOperationException();}
@Override public net.minecraft.world.ticks.LevelTickAccess getFluidTicks(){throw new UnsupportedOperationException();}
@Override public net.minecraft.world.level.saveddata.maps.MapItemSavedData getMapData(net.minecraft.world.level.saveddata.maps.MapId p0){throw new UnsupportedOperationException();}
@Override public net.minecraft.world.level.storage.LevelData.RespawnData getRespawnData(){throw new UnsupportedOperationException();}
@Override public net.minecraft.world.scores.Scoreboard getScoreboard(){throw new UnsupportedOperationException();}
@Override public int getSeaLevel(){throw new UnsupportedOperationException();}
@Override public net.minecraft.core.Holder getUncachedNoiseBiome(int p0,int p1,int p2){throw new UnsupportedOperationException();}
@Override public net.minecraft.world.level.border.WorldBorder getWorldBorder(){throw new UnsupportedOperationException();}
@Override public void levelEvent(net.minecraft.world.entity.Entity p0,int p1,net.minecraft.core.BlockPos p2,int p3){throw new UnsupportedOperationException();}
@Override public void playSeededSound(net.minecraft.world.entity.Entity p0,net.minecraft.world.entity.Entity p1,net.minecraft.core.Holder p2,net.minecraft.sounds.SoundSource p3,float p4,float p5,long p6){throw new UnsupportedOperationException();}
@Override public void playSeededSound(net.minecraft.world.entity.Entity p0,double p1,double p2,double p3,net.minecraft.core.Holder p4,net.minecraft.sounds.SoundSource p5,float p6,float p7,long p8){throw new UnsupportedOperationException();}
@Override public java.util.List players(){throw new UnsupportedOperationException();}
@Override public net.minecraft.world.item.crafting.RecipeAccess recipeAccess(){throw new UnsupportedOperationException();}
@Override public void sendBlockUpdated(net.minecraft.core.BlockPos p0,net.minecraft.world.level.block.state.BlockState p1,net.minecraft.world.level.block.state.BlockState p2,int p3){throw new UnsupportedOperationException();}
@Override public void setRespawnData(net.minecraft.world.level.storage.LevelData.RespawnData p0){throw new UnsupportedOperationException();}
@Override public net.minecraft.world.TickRateManager tickRateManager(){throw new UnsupportedOperationException();}
}
static final class Manager extends ServerWaypointManager {Data d;int id;@Override public void trackWaypoint(WaypointTransmitter e){if(e!=d.actor)throw new AssertionError();d.event("waypoint "+id+" true");}@Override public void untrackWaypoint(WaypointTransmitter e){if(e!=d.actor)throw new AssertionError();d.event("waypoint "+id+" false");}}
public static void main(String[] args)throws Exception{net.minecraft.SharedConstants.tryDetectVersion();net.minecraft.server.Bootstrap.bootStrap();known=List.of(MobEffects.INFESTED,MobEffects.OOZING,MobEffects.REGENERATION,MobEffects.POISON);attributeHolders=List.of(Attributes.MAX_HEALTH,Attributes.MAX_ABSORPTION,Attributes.SCALE,Attributes.WAYPOINT_TRANSMIT_RANGE,Attributes.ATTACK_DAMAGE);dirty=field(LivingEntity.class,"effectsDirty");passengers=field(Entity.class,"passengers");refresh=LivingEntity.class.getDeclaredMethod("refreshDirtyAttributes");refresh.setAccessible(true);var u=Class.forName("sun.misc.Unsafe");unsafe=field(u,"theUnsafe").get(null);allocate=u.getMethod("allocateInstance",Class.class);activeEffects=field(LivingEntity.class,"activeEffects");removed=field(Entity.class,"removalReason");duration=field(MobEffectInstance.class,"duration");amplifier=field(MobEffectInstance.class,"amplifier");ambient=field(MobEffectInstance.class,"ambient");visible=field(MobEffectInstance.class,"visible");icon=field(MobEffectInstance.class,"showIcon");hidden=field(MobEffectInstance.class,"hiddenEffect");blend=field(MobEffectInstance.class,"blendState");factor=field(blend.getType(),"factor");previous=field(blend.getType(),"factorPreviousFrame");healthData=field(Entity.class,"entityData");absorption=field(LivingEntity.class,"absorptionAmount");attributesField=field(LivingEntity.class,"attributes");healthKey=(EntityDataAccessor<Float>)field(LivingEntity.class,"DATA_HEALTH_ID").get(null);dataCtor=SynchedEntityData.class.getDeclaredConstructor(SyncedDataHolder.class,SynchedEntityData.DataItem[].class);dataCtor.setAccessible(true);Path dir=Path.of(args[1]);
try(var out=new PrintWriter(Files.newBufferedWriter(dir.resolve(args[0]+".txt")));var enriched=new PrintWriter(Files.newBufferedWriter(dir.resolve("inputs-"+args[0]+".txt")));var lines=Files.lines(dir.resolve("inputs.txt"))){for(var line:(Iterable<String>)lines::iterator){var d=new Data();active=d;d.f=JsonParser.parseString(line).getAsJsonObject();d.out=out;var hashes=new JsonArray();var identities=new JsonArray();for(var hash:d.f.getAsJsonArray("hashes")){int id=d.keys.size();var h=id<4?known.get(id):Holder.<MobEffect>direct(new Effect(id,hash.getAsInt()));d.keys.add(h);hashes.add(h.hashCode());identities.add(System.identityHashCode(h));}d.f.add("hashes",hashes);d.f.add("identities",identities);enriched.println(d.f);d.actor=make(Actor.class);d.actor.d=d;dirty.setBoolean(d.actor,true);d.other=make(net.minecraft.world.entity.decoration.ArmorStand.class);activeEffects.set(d.actor,new HashMap<Holder<MobEffect>,MobEffectInstance>());d.levels=new Level[2];for(int i=0;i<2;i++){if(d.f.getAsJsonArray("servers").get(i).getAsBoolean()){var l=make(EffectLevel.class);l.d=d;l.id=i;d.levels[i]=l;}else{var l=make(ClientEffectLevel.class);l.d=d;l.id=i;d.levels[i]=l;}}var states=d.config().getAsJsonArray("states");d.pool=new MobEffectInstance[states.size()];for(int i=0;i<d.pool.length;i++){var v=states.get(i).getAsJsonObject();int h=n(v,"hidden");d.pool[i]=new MobEffectInstance(d.keys.get(n(v,"key")),n(v,"duration"),n(v,"amplifier"),v.get("ambient").getAsBoolean(),v.get("visible").getAsBoolean(),v.get("icon").getAsBoolean(),h<0?null:d.pool[h]);}d.connections=new Connection[2];d.riders=new ServerPlayer[2];d.attributes=new Attrs[2];d.updates=List.of(new ObjectOpenHashSet<>(),new ObjectOpenHashSet<>());for(int i=0;i<2;i++){d.connections[i]=make(Connection.class);d.connections[i].d=d;d.connections[i].id=i;d.riders[i]=make(ServerPlayer.class);d.attributes[i]=make(Attrs.class);d.attributes[i].d=d;d.attributes[i].id=i;}var ah=d.f.getAsJsonArray("attribute_hashes");d.attributeInstances=new Attr[ah.size()];for(int i=0;i<ah.size();i++){var at=make(Attr.class);at.d=d;at.id=i;at.hash=ah.get(i).getAsInt();d.attributeInstances[i]=at;}d.managers=new Manager[2];d.healthStores=new SynchedEntityData[2];d.healthItems=(SynchedEntityData.DataItem<Float>[])new SynchedEntityData.DataItem<?>[2];for(int i=0;i<2;i++){d.managers[i]=make(Manager.class);d.managers[i].d=d;d.managers[i].id=i;var entries=new SynchedEntityData.DataItem<?>[healthKey.id()+1];for(int j=0;j<entries.length;j++)entries[j]=new SynchedEntityData.DataItem<Float>(new EntityDataAccessor<Float>(j,EntityDataSerializers.FLOAT),0f);d.healthItems[i]=new SynchedEntityData.DataItem<Float>(healthKey,Float.intBitsToFloat((int)d.f.getAsJsonArray("health").get(i).getAsLong()));entries[healthKey.id()]=d.healthItems[i];d.healthStores[i]=dataCtor.newInstance(d.actor,entries);}d.reset();for(var op:d.f.getAsJsonArray("setup"))d.raw(op.getAsJsonObject());d.all();
for(var value:d.f.getAsJsonArray("ops")){var op=value.getAsJsonObject();int a=n(op,"a"),b=n(op,"b"),source=n(op,"source");Entity src=source<0?null:source==0?d.actor:d.other;String result="ok";try{switch(op.get("op").getAsString()){
case "attribute-update"->d.actor.attributeUpdate(a);case "pack"->{var packed=d.healthStores[a].packDirty();result=packed==null?"none":Long.toString(bits((Float)packed.getFirst().value()));}
case "eligible"->result=Boolean.toString(d.actor.canBeAffected(d.pool[a]));
case "notify-add"->d.actor.added(d.pool[a]);case "notify-update"->d.actor.updated(d.pool[a],op.get("reapply").getAsBoolean());case "notify-remove"->{var es=new ArrayList<MobEffectInstance>();for(var i:op.getAsJsonArray("list"))es.add(d.pool[i.getAsInt()]);d.actor.removed(es);}
case "send"->d.actor.sendEffectToPassengers(d.pool[a]);case "refresh"->{try{refresh.invoke(d.actor);}catch(InvocationTargetException ex){if(ex.getCause() instanceof RuntimeException e)throw e;throw new AssertionError(ex.getCause());}}
case "pending-add","pending-remove","pending-clear"->d.raw(op);
case "add"->result=Boolean.toString(d.actor.addEffect(d.pool[a],src));case "force"->d.actor.forceAddEffect(d.pool[a],src);case "remove"->result=Boolean.toString(d.actor.removeEffect(d.keys.get(a)));case "remove-raw"->{var e=d.actor.removeEffectNoUpdate(d.keys.get(a));result=e==null?"none":Integer.toString(d.id(e));}case "all"->result=Boolean.toString(d.actor.removeAllEffects());case "has"->result=Boolean.toString(d.actor.hasEffect(d.keys.get(a)));case "get"->{var e=d.actor.getEffect(d.keys.get(a));result=e==null?"none":Integer.toString(d.id(e));}case "blend"->result=Long.toString(bits(d.actor.getEffectBlendFactor(d.keys.get(a),fl(op,"partial"))));case "put","drop","clear","copy","alias"->d.raw(op);default->throw new AssertionError();
}}catch(Stop e){result="callback";}catch(NullPointerException e){result="null";}catch(IndexOutOfBoundsException e){result="bounds";}catch(NoSuchElementException e){result="empty";}out.println("result "+result);d.all();}
}}
}
}
+91
View File
@@ -0,0 +1,91 @@
#!/usr/bin/env python3
"""Compare native attribute-update consequences composed with notifications and health storage."""
import argparse
import json
import os
from pathlib import Path
import platform
import subprocess
import random
import struct
try:
from . import lab, test_rust_parity as base, test_effect_notifications_parity as notifications
except ImportError:
import lab
import test_rust_parity as base
import test_effect_notifications_parity as notifications
def corpus():
def d(v):return int.from_bytes(struct.pack('>d',v),'big')
def f(v):return int.from_bytes(struct.pack('>f',v),'big')
def decorate(row):
row['health']=[f(20),f(30)];row['servers']=[True,False]
for after,c in enumerate([row['initial'],row['after']]):
c.update(data=after,absorption=f(12) if not after else None,maximums=[d(10),d(6)] if not after else [d(.5),d(2)],direct_attributes=after,managers=[0,1] if not after else [1,0],values=[[d(1)]*5,[d(-1)]*5])
return row
for row in notifications.corpus():yield decorate(row)
def op(name,a=0,**kw):
v=dict(op=name,a=a,b=0,source=-1,partial=0x3f000000,list=[4,5],reapply=True);v.update(kw);return v
def fresh():
row=decorate(next(notifications.corpus()));row.update(setup=[],ops=[op('attribute-update'),op('pack',0),op('pack',1)],fail=-1,mutate=-1,mutations=[])
return row
doubles=[-float('inf'),-1.,-0.,0.,1e-50,.5,1.,10.,20.,1e50,float('inf'),float('nan')]
floats=[-float('inf'),-1.,-0.,0.,.5,10.,20.,float('inf'),float('nan')]
for kind in [0,1]:
for value in floats:
for maximum in doubles:
row=fresh();row['health']=[f(value),f(value)];row['initial'].update(absorption=f(value),maximums=[d(maximum),d(maximum)]);row['ops'][0]=op('attribute-update',kind);yield row
for kind in range(5):
for servers in [[False,False],[True,False],[False,True],[True,True]]:
for fail in range(-1,8):
for mutate in range(-1,8):
row=fresh();row.update(servers=servers,fail=fail,mutate=mutate);row['ops'][0]=op('attribute-update',kind);yield row
for value in doubles:
for level in [0,1]:
row=fresh();row.update(servers=[True,True]);row['initial'].update(level=level,values=[[d(value)]*5,[d(value)]*5]);row['ops'][0]=op('attribute-update',3);yield row
rng=random.Random(0x41545452555044)
for _ in range(500):
row=fresh();row.update(health=[f(rng.choice(floats)) for _ in range(2)],servers=[bool(rng.randrange(2)) for _ in range(2)],fail=rng.randrange(-1,20),mutate=rng.randrange(-1,20),ops=[op(rng.choice(['attribute-update','refresh','notify-update','notify-remove','pack']),rng.randrange(2)) for _ in range(8)])
for c in [row['initial'],row['after']]:
c.update(data=rng.randrange(2),absorption=f(rng.choice(floats)),maximums=[d(rng.choice(doubles)) for _ in range(2)],direct_attributes=rng.randrange(2),managers=[rng.randrange(2) for _ in range(2)],values=[[d(rng.choice(doubles)) for _ in range(5)] for _ in range(2)],holders=[rng.randrange(5) for _ in range(3)],level=rng.randrange(2))
row['setup']=[op('pending-add',0,b=i) for i in range(3)];yield row
def main():
parser=argparse.ArgumentParser(description=__doc__);parser.add_argument('--prepare',action='store_true');args=parser.parse_args()
build=lab.LOCAL/'rust-attribute-updates-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/VanillaAttributeUpdatesOracle.java'
sources=[oracle,Path(__file__),lab.ROOT/"tools/test_effect_notifications_parity.py",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/attribute_updates_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.VanillaAttributeUpdatesOracle',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','attribute_updates_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'/('attribute_updates_fixture.exe' if os.name=='nt' else 'attribute_updates_fixture')
references=[]
for side,jar in jars.items():
java(side,side,build)
enriched=build/f'inputs-{side}.txt';enriched_hash=base.source_hashes([enriched])
run([binary,build,side],'rust-'+side)
reference=build/f'{side}.txt';rust_output=build/f'rust-{side}.txt';matched=base.compare(reference,rust_output)
base.ensure_sources_unchanged([enriched],enriched_hash)
tested_fixtures.update(enriched_hash);fixture_paths.append(enriched)
references.append({'side':side,'jar_sha256':lab.digest(jar,'sha256'),'output_sha256':lab.digest(reference,'sha256'),'rust_output_sha256':lab.digest(rust_output,'sha256'),'matched_output_lines':matched})
print(f'{side}: {matched:,} attribute update 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,'references':references,'scope':'Inherited LivingEntity.onAttributeUpdated composed with native effect notifications, pending sets and synchronized health storage. Includes max-health and max-absorption lowering, repeated maximum reads, Float dirty callbacks, dimension refresh dispatch, server waypoint manager selection, direct attribute-map field capture and positive-range tracking. Tests finite/nonfinite numeric values, callback mutation/failure prefixes and packet notifications. Actual attribute values, dimension rebuild and waypoint-manager internals remain controlled; registered modifier bodies and complete player/entity gameplay remain pending.'}
lab.write_json(report_path,report);print(f'Scoped attribute update parity passed: {report_path}')
if __name__=='__main__':main()