Bind registered attribute refresh to native entity update consequences

This commit is contained in:
Ludovic Marie committed 2026-10-07 13:51:15 +02:00
1 parent 794b447b0c
commit 4a2fcedbd3
7 files changed
+1418

No files matched your search

@@ -0,0 +1,319 @@
{
"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": 9284,
"attributes": 40,
"entity_suppliers": 93,
"fastutil_sha256": {
".local/minecraft/26.3/libraries/server/it/unimi/dsi/fastutil/8.5.18/fastutil-8.5.18.jar": "9094ae67d01d0ad246f886f11ad557fc2e79c72cbf3feed83e1512a8ae90a74a"
},
"sources_sha256": {
"mods/horizon/src/test/java/dev/horizonlab/parity/VanillaLiveAttributeUpdatesOracle.java": "23ef1bc52c59018258126197f22ef98cdcd4dd4db852be0bb60eeac3ab9f9b1f",
"mods/horizon/src/test/java/dev/horizonlab/parity/VanillaEntityAttributeOracle.java": "2ad3190a9798ab54bd12e88573028d0ca6a204e2f8a92018aae98b22335213bf",
"mods/horizon/src/test/java/dev/horizonlab/parity/VanillaLiveAttributesOracle.java": "e6a6ab15c6ac2594e41e7830de480a01844d54d171913dd7bfe07881d10226c1",
"tools/test_live_attribute_updates_parity.py": "ea80fc255e14e25fe0e35dfa8037675e3e561e3e72b597b6c548d7ddc6416292",
"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": "1420a80787d10dd2e2954a3ee6193ff313289d00c7c78ca26ece8bafa21f4380",
"crates/horizon-world/src/attribute_map.rs": "3d871cd530f5c035e0753f3ba065c2b9c5b49cdee75d4119f7dd584b22af67e6",
"crates/horizon-world/src/attribute_updates.rs": "4cb322d75a50d222298994607097ba7f7e6674021128fa02a98a74c057ad05f1",
"crates/horizon-world/src/attributes/entity.rs": "64ac99682a3aa06b1fb94b776b47794234c76b1cf70f1f54933d51e545e69279",
"crates/horizon-world/src/attributes/live.rs": "8198c45134f69135d48ef203e71b6e702529568fd1ed9174d0aa843570bbf9e7",
"crates/horizon-world/src/attributes/mod.rs": "5c2fcbbde58c19261874d227ea98332f00336c9fa53446da2e9adb6aa64a5e5a",
"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_attribute_modifiers.rs": "0bc7ab63d0f3f34a7133fe20364cae0ec1fdbd3773cba90faa1f08e0d63a5646",
"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": "00243ac1a80934d54546452a98bc5b37b64a2cafa3a3dfadb82cd5997b46d7cb",
"crates/horizon-world/src/liquid_collision.rs": "0f0a83b04e5a5bc9d98ed05fde4d6584ba69642e93522e726e4cfc325d6fcb1d",
"crates/horizon-world/src/live_attribute_updates.rs": "778171faaaf7077deb0b62c4cc1db16351eba41adb8bb07656ba65e827056199",
"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_map.rs": "b4934a9cc38df93ed206ab0bc3bbc4499acd8e40b41b7def7d47067b7170065b",
"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/stored_effect_attributes.rs": "c79f6a52d3cfee1a5288225bf8fa91a7bf3ea8cd21faea9f6dc748ecefc6309a",
"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/live_attribute_updates_fixture.rs": "657f86f79582073ac23d7958cec5464fdc1071b9c50e19b1aa5f5c5033cbe3af",
"crates/horizon-world/examples/live_attributes_fixture.rs": "dc4136118c6b42f700cb5d5474aefc8bb136ca5f1a8ca02f7766c1412ad7e665"
},
"fixtures_sha256": {
".local/rust-live-attribute-updates-parity/inputs.txt": "1de9e5ca84a2dbd2c04b42c17548dc3a00ad38d006cae7906cfad8558cf90954",
".local/rust-live-attribute-updates-parity/catalog.json": "ea8811c786c28dd2a6e3dee78dd36ee9655f7c415b4febd0c2218159e4dbc1eb",
".local/rust-live-attribute-updates-parity/catalog-client.json": "d1e488e4f432888beb9f643904c8bca242d1e2093f53e23e07da305d70cf7106",
".local/rust-live-attribute-updates-parity/catalog-server.json": "d1e488e4f432888beb9f643904c8bca242d1e2093f53e23e07da305d70cf7106",
".local/rust-live-attribute-updates-parity/inputs-client.txt": "5c2d384d141240f79132399e384dcb73c8f25ec2f4d550cd3759f7e9dba2b747",
".local/rust-live-attribute-updates-parity/inputs-server.txt": "be6a31b6e0b3da73ac012c081ae073d57fc5954bceba59770b9a1e01b22aaa1a"
},
"references": [
{
"side": "client",
"jar_sha256": "4508d006323f24fa02876310c192d739af56516eb259000ac50f0909a68c9a2d",
"output_sha256": "6d25f14bb8fe0e8e8bad48dfcea0795c7508515b8ba6892cc5beeeb1aa25d0c2",
"matched_output_lines": 2836190,
"rust_output_sha256": "6d25f14bb8fe0e8e8bad48dfcea0795c7508515b8ba6892cc5beeeb1aa25d0c2"
},
{
"side": "server",
"jar_sha256": "a362163eec5d1612d520772bc16e5b39c09e3b234fdc045f56bf544284ee8ae6",
"output_sha256": "e124ba567070fd090bc152408f73fd6742e8da8e14280b62f798b4a1e02d5e22",
"matched_output_lines": 2836234,
"rust_output_sha256": "e124ba567070fd090bc152408f73fd6742e8da8e14280b62f798b4a1e02d5e22"
}
],
"scope": "Native LivingEntity attribute queries, notification and private refresh methods with registered AttributeMaps, real effect modifiers and pending queues, onAttributeUpdated health/absorption consequences and SynchedEntityData Float storage. Exercises all registered attributes/default suppliers, separate captured/selected maps and health stores, callbacks changing limits and routes, null pending members, client guards, failure prefixes and modifier/cache/queue identity. Replays JVM holder/instance hashes per reference. Scale dimension rebuild and waypoint-manager internals remain controlled; passenger lists are empty. Full effect lifecycle/dirty-particle synchronization, player absorption, complete gameplay and playable platform builds remain pending."
}
@@ -0,0 +1,598 @@
#[allow(dead_code)]
mod integration {
include!("live_attributes_fixture.rs");
use horizon_world::{
active_effects::{ActiveEffects, EffectHooks},
attribute_updates::{self, UpdateWorld, UpdatedAttribute},
attributes::live::Key,
effect_notifications::{self as notify, EffectPacket, NotificationWorld},
java_hash_map::JavaKey,
live_attribute_updates::{self as live_updates, LiveAttributeWorld},
living_health::{self, HealthWorld, SyncedFloat},
object_open_set::{CursorError, ObjectOpenSet},
status_effect::{Details, EffectInstance},
};
type Live = LiveAttributes<Box<dyn FnMut() -> i32>>;
type Result<T, E = &'static str> = std::result::Result<T, E>;
#[derive(Clone, PartialEq, Eq)]
struct EffectKey;
impl JavaKey for EffectKey {
fn java_hash(&self) -> i32 {
0
}
fn tie_break(&self, _: &Self) -> std::cmp::Ordering {
std::cmp::Ordering::Less
}
}
type Storage = ActiveEffects<EffectKey>;
type Effect = EffectInstance<EffectKey>;
fn single(v: &Value) -> f32 {
f32::from_bits(v.as_u64().unwrap() as u32)
}
fn float_bits(v: f32) -> u32 {
if v.is_nan() { 0x7fc00000 } else { v.to_bits() }
}
fn stop(e: Error) -> &'static str {
match e {
Error::NullAttribute | Error::MissingInstanceModifier => "null",
Error::MissingAttribute | Error::MissingModifier | Error::DuplicateModifier(_) => {
"argument"
}
_ => panic!("invalid fixture: {e:?}"),
}
}
fn cursor_stop(e: CursorError) -> &'static str {
match e {
CursorError::IndexOutOfBounds => "bounds",
CursorError::NoSuchElement => "empty",
_ => "null",
}
}
struct World<'a> {
maps: [Live; 2],
health: [SyncedFloat; 2],
absorption: f32,
selection: usize,
direct: usize,
data: usize,
manager: usize,
server: bool,
client: bool,
calls: i64,
op: Value,
definitions: Definitions<Key>,
pools: [Vec<Modifier>; 2],
out: &'a mut dyn Write,
}
impl World<'_> {
fn key(&self, op: &Value) -> Option<Key> {
let index = op["attribute"].as_i64().unwrap();
(index >= 0).then(|| self.maps[0].keys()[index as usize].clone())
}
fn controls(&mut self, c: &Value) {
if let Some(v) = c["selection"].as_u64() {
self.selection = v as usize;
}
if let Some(v) = c["direct"].as_u64() {
self.direct = v as usize;
}
if let Some(v) = c["data"].as_u64() {
self.data = v as usize;
}
if let Some(v) = c["manager"].as_u64() {
self.manager = v as usize;
}
if let Some(v) = c["server"].as_bool() {
self.server = v;
}
if let Some(v) = c["client"].as_bool() {
self.client = v;
}
if !c["absorption"].is_null() {
self.absorption = single(&c["absorption"]);
}
}
fn event(&mut self, name: impl AsRef<str>) -> Result<()> {
let call = self.calls;
self.calls += 1;
writeln!(self.out, "event {call} {}", name.as_ref()).unwrap();
if call == self.op["fail"].as_i64().unwrap() {
return Err("callback");
}
if call == self.op["mutate"].as_i64().unwrap() {
let changes = self.op["changes"].clone();
self.controls(&changes);
for action in self.op["actions"].as_array().unwrap().clone() {
self.raw(&action)?;
}
}
Ok(())
}
fn attribute_value(&mut self, key: Option<Key>) -> Result<f64> {
self.event(format!("value {}", key.as_ref().map_or("null", Key::name)))?;
live_updates::value(self, key, ValueKind::Computed)
}
fn raw(&mut self, op: &Value) -> Result<String> {
let key = self.key(op);
let target = op["target"].as_u64().unwrap() as usize;
let m = Modifier::new(
op["id"].as_str().unwrap(),
number(&op["amount"]),
Operation::ALL[op["operation"].as_u64().unwrap() as usize],
)
.unwrap();
match op["mode"].as_str().unwrap() {
"controls" => {
self.controls(&op["changes"]);
Ok("ok".into())
}
"ensure" => self.maps[target]
.get_instance(key)
.map(|h| handle(h.as_ref()))
.map_err(stop),
"reset" => self.maps[target]
.reset_base(key)
.map(|v| v.to_string())
.map_err(stop),
"define" => {
effects::add_definition(
&mut self.definitions,
key,
Template::new(m.id(), m.amount(), m.operation()).unwrap(),
);
Ok("ok".into())
}
"effect-add" => self.maps[target]
.add_effect_modifiers(
&mut self.definitions,
op["amplifier"].as_i64().unwrap() as i32,
)
.map(|()| "ok".into())
.map_err(|e| stop(effect_error(e))),
"effect-remove" => self.maps[target]
.remove_effect_modifiers(&mut self.definitions)
.map(|()| "ok".into())
.map_err(|e| stop(effect_error(e))),
"clear-updates" => {
self.maps[target].updates_mut().clear();
Ok("ok".into())
}
"clear-sync" => {
self.maps[target].sync_mut().clear();
Ok("ok".into())
}
"null-update" => {
self.maps[target].updates_mut().add(None);
Ok("ok".into())
}
mode => {
let Some(h) = self.maps[target].get_instance(key).map_err(stop)? else {
return Ok("absent".into());
};
self.maps[target]
.edit(&h, |a, o| match mode {
"set" => a
.set_base_value(number(&op["base"]), o)
.map(|()| "ok".into()),
"update" => a.add_or_update_transient(m, o).map(|()| "ok".into()),
"permanent" => a.add_permanent(m, o).map(|()| "ok".into()),
"remove" => a.remove(m.id(), o).map(|v| v.to_string()),
"clear" => a.remove_all(o).map(|()| "ok".into()),
_ => panic!("raw mode: {mode}"),
})
.map_err(stop)
}
}
}
fn state(&mut self, storage: &Storage) {
writeln!(
self.out,
"controls {} {} {} {} {} {} {} {}",
self.selection,
self.direct,
self.data,
self.manager,
self.server,
self.client,
self.calls,
storage.dirty
)
.unwrap();
for (i, h) in self.health.iter().enumerate() {
writeln!(
self.out,
"health {i} {} {} {} {}",
float_bits(h.value()),
h.is_dirty(),
h.item_dirty(),
h.is_default()
)
.unwrap();
}
writeln!(self.out, "absorption {}", float_bits(self.absorption)).unwrap();
for (i, map) in self.maps.iter().enumerate() {
writeln!(self.out, "attributes {i}").unwrap();
writeln!(
self.out,
"map {}",
map.entries()
.iter()
.map(|(k, h)| format!(
"{}={}",
k.as_ref().map_or("null", Key::name),
handle(h.as_ref())
))
.collect::<Vec<_>>()
.join(",")
)
.unwrap();
writeln!(
self.out,
"updates {}",
map.updates()
.values()
.map(handle)
.collect::<Vec<_>>()
.join(",")
)
.unwrap();
writeln!(
self.out,
"sync {}",
map.sync()
.values()
.map(handle)
.collect::<Vec<_>>()
.join(",")
)
.unwrap();
for (h, key, a) in map.instances() {
writeln!(
self.out,
"instance {} {} {} {} {} | {} | {}",
h.index(),
key.name(),
bits(a.base_value()),
a.is_dirty(),
bits(a.cached_value()),
refs(a.modifiers(), &mut self.pools[i]),
refs(a.permanent_modifiers(), &mut self.pools[i])
)
.unwrap();
}
}
}
}
impl HealthWorld for World<'_> {
type Error = &'static str;
type Data = usize;
fn health(&mut self) -> Result<f32> {
self.event("health")?;
Ok(self.health[self.data].value())
}
fn health_data(&self) -> usize {
self.data
}
fn max_health_attribute(&mut self) -> Result<f64> {
self.attribute_value(self.maps[0].key("minecraft:max_health"))
}
fn max_absorption_attribute(&mut self) -> Result<f64> {
self.attribute_value(self.maps[0].key("minecraft:max_absorption"))
}
fn store_health(&mut self, data: usize, value: f32) -> Result<()> {
let mut h = self.health[data];
let result = h.set(value, false, |current| {
self.health[data] = *current;
let result = self.event("health-updated");
*current = self.health[data];
result
});
self.health[data] = h;
result
}
fn store_absorption(&mut self, value: f32) -> Result<()> {
self.event(format!("store-absorption {}", float_bits(value)))?;
self.absorption = value;
Ok(())
}
fn dead_flag(&self) -> bool {
false
}
fn dispatch_set_health(&mut self, value: f32) -> Result<()> {
self.event(format!("set-health {}", float_bits(value)))?;
living_health::set_health(self, value)
}
}
impl UpdateWorld<Key> for World<'_> {
type Attributes = usize;
type Level = bool;
type Manager = usize;
fn attribute_kind(&self, key: &Key) -> UpdatedAttribute {
live_updates::kind(key)
}
fn absorption(&mut self) -> Result<f32> {
self.event("absorption")?;
Ok(self.absorption)
}
fn refresh_dimensions(&mut self) -> Result<()> {
self.event("dimensions")
}
fn level(&mut self) -> Result<bool> {
self.event("level")?;
Ok(self.server)
}
fn is_server_level(&self, level: bool) -> bool {
level
}
fn waypoint_manager(&mut self, _: bool) -> Result<usize> {
self.event("manager")?;
Ok(self.manager)
}
fn attributes_field(&self) -> usize {
self.direct
}
fn attribute_value_in(&mut self, map: usize, key: Key) -> Result<f64> {
live_updates::value_in(self, map, Some(key), ValueKind::Computed)
}
fn track_waypoint(&mut self, manager: usize, track: bool) -> Result<()> {
self.event(format!("waypoint {manager} {track}"))
}
}
impl LiveAttributeWorld for World<'_> {
type Identity = Box<dyn FnMut() -> i32>;
fn get_attributes(&mut self) -> Result<usize> {
self.event("attributes")?;
Ok(self.selection)
}
fn live_attributes(&self, map: usize) -> &Live {
&self.maps[map]
}
fn live_attributes_mut(&mut self, map: usize) -> &mut Live {
&mut self.maps[map]
}
fn instance_attribute(&self, instance: Option<Handle>) -> Result<Key> {
let h = instance.ok_or("null")?;
self.maps
.iter()
.find_map(|m| m.attribute(&h).ok().cloned())
.ok_or("null")
}
fn attribute_error(error: Error) -> &'static str {
stop(error)
}
fn cursor_error(error: CursorError) -> &'static str {
cursor_stop(error)
}
fn dispatch_attribute_updated(&mut self, key: Key) -> Result<()> {
self.event(format!("updated {}", key.name()))?;
attribute_updates::updated(self, key)
}
}
impl EffectHooks<EffectKey> for World<'_> {
type Error = &'static str;
type Source = ();
type Level = bool;
fn can_be_affected(&mut self, _: &mut Storage, _: Effect) -> Result<bool> {
Ok(true)
}
fn on_added(&mut self, s: &mut Storage, e: Effect, _: Option<()>) -> Result<()> {
notify::on_added(s, &e, self)
}
fn on_updated(
&mut self,
s: &mut Storage,
e: Effect,
reapply: bool,
_: Option<()>,
) -> Result<()> {
notify::on_updated(s, &e, reapply, self)
}
fn on_removed(&mut self, s: &mut Storage, es: Vec<Effect>) -> Result<()> {
notify::on_removed(s, &es, self)
}
fn effect_added(&mut self, _: &mut Storage, _: EffectKey, _: i32) -> Result<()> {
Ok(())
}
fn effect_started(&mut self, _: &mut Storage, _: EffectKey, _: i32) -> Result<()> {
Ok(())
}
fn level(&mut self, _: &mut Storage) -> Result<bool> {
UpdateWorld::level(self)
}
fn is_client(&mut self, _: &mut Storage, level: bool) -> Result<bool> {
if !level {
return Err("null");
}
self.event("client")?;
Ok(self.client)
}
fn actor_removed(&self) -> bool {
false
}
}
impl NotificationWorld<EffectKey> for World<'_> {
type Attributes = usize;
type UpdateSet = usize;
type AttributeInstance = Handle;
type Attribute = Key;
type Passenger = ();
type Connection = ();
fn attributes(&mut self, _: &mut Storage) -> Result<usize> {
self.get_attributes()
}
fn add_modifiers(
&mut self,
_: &mut Storage,
_: EffectKey,
map: usize,
amp: i32,
) -> Result<()> {
self.maps[map]
.add_effect_modifiers(&mut self.definitions, amp)
.map_err(|e| stop(effect_error(e)))
}
fn remove_modifiers(&mut self, _: &mut Storage, _: EffectKey, map: usize) -> Result<()> {
self.maps[map]
.remove_effect_modifiers(&mut self.definitions)
.map_err(|e| stop(effect_error(e)))
}
fn pending_updates(&mut self, _: &mut Storage, map: usize) -> Result<usize> {
Ok(map)
}
fn update_set(&self, set: usize) -> &ObjectOpenSet<Handle> {
self.maps[set].updates()
}
fn update_set_mut(&mut self, set: usize) -> &mut ObjectOpenSet<Handle> {
self.maps[set].updates_mut()
}
fn iterator_error(e: CursorError) -> &'static str {
cursor_stop(e)
}
fn attribute_of(&mut self, _: &mut Storage, instance: Option<Handle>) -> Result<Key> {
self.instance_attribute(instance)
}
fn attribute_updated(&mut self, _: &mut Storage, key: Key) -> Result<()> {
self.dispatch_attribute_updated(key)
}
fn passengers(&self) -> Vec<()> {
vec![]
}
fn is_server_player(&self, _: ()) -> bool {
false
}
fn connection(&self, _: ()) {}
fn entity_id(&mut self, _: &mut Storage) -> Result<i32> {
Ok(0)
}
fn send_packet(
&mut self,
_: &mut Storage,
_: (),
_: EffectPacket<EffectKey>,
) -> Result<()> {
Ok(())
}
}
pub fn run() -> std::result::Result<(), Box<dyn std::error::Error>> {
let args: Vec<_> = std::env::args().collect();
let dir = Path::new(&args[1]);
let catalog = Arc::new(Catalog::from_json(&serde_json::from_str(
&fs::read_to_string(dir.join("catalog.json"))?,
)?)?);
let mut out =
io::BufWriter::new(fs::File::create(dir.join(format!("rust-{}.txt", args[2])))?);
for line in fs::read_to_string(dir.join(format!("inputs-{}.txt", args[2])))?.lines() {
let f: Value = serde_json::from_str(line)?;
let hashes: Vec<_> = f["holder_hashes"]
.as_array()
.unwrap()
.iter()
.map(|v| v.as_i64().unwrap() as i32)
.collect();
let maps = std::array::from_fn(|i| {
let mut ids = f["instance_hashes"][i]
.as_array()
.unwrap()
.iter()
.map(|v| v.as_i64().unwrap() as i32)
.collect::<Vec<_>>()
.into_iter();
Live::new(
catalog.clone(),
f["entity"].as_str().unwrap(),
&hashes,
Box::new(move || ids.next().unwrap()),
)
.unwrap()
});
let mut w = World {
maps,
health: std::array::from_fn(|i| SyncedFloat::new(single(&f["health"][i]))),
absorption: single(&f["absorption"]),
selection: 0,
direct: 0,
data: 0,
manager: 0,
server: true,
client: false,
calls: 0,
op: Value::Null,
definitions: Definitions::default(),
pools: [vec![], vec![]],
out: &mut out,
};
let mut storage = Storage::default();
for op in f["setup"].as_array().unwrap() {
w.raw(op).unwrap();
}
for op in f["ops"].as_array().unwrap() {
w.op = op.clone();
w.calls = 0;
let key = w.key(op);
let effect = EffectInstance::new(
EffectKey,
Details {
duration: 200,
amplifier: op["amplifier"].as_i64().unwrap() as i32,
ambient: false,
visible: true,
show_icon: true,
},
None,
);
let result = match op["mode"].as_str().unwrap() {
"value" => w.attribute_value(key).map(bits),
"base" => live_updates::value(&mut w, key, ValueKind::Base).map(bits),
"set-health" => w
.dispatch_set_health(single(&op["value"]))
.map(|()| "ok".into()),
"set-absorption" => living_health::set_absorption(&mut w, single(&op["value"]))
.map(|()| "ok".into()),
"heal" => {
living_health::heal(&mut w, single(&op["value"])).map(|()| "ok".into())
}
"attribute-update" => match key {
Some(key) => w.dispatch_attribute_updated(key),
None => w.event("updated null").and(Err("null")),
}
.map(|()| "ok".into()),
"refresh" => live_updates::refresh(&mut w).map(|()| "ok".into()),
"notify-add" => {
notify::on_added(&mut storage, &effect, &mut w).map(|()| "ok".into())
}
"notify-update" => notify::on_updated(
&mut storage,
&effect,
op["reapply"].as_bool().unwrap(),
&mut w,
)
.map(|()| "ok".into()),
"notify-remove" => notify::on_removed(
&mut storage,
&vec![effect; op["count"].as_u64().unwrap() as usize],
&mut w,
)
.map(|()| "ok".into()),
"pack" => Ok(w.health[op["target"].as_u64().unwrap() as usize]
.take_dirty()
.map_or("none".into(), |v| float_bits(v).to_string())),
"dirty" => {
storage.dirty = op["flag"].as_bool().unwrap();
Ok("ok".into())
}
_ => w.raw(op),
};
writeln!(
w.out,
"result {}",
result.unwrap_or_else(|e| format!("error {e}"))
)?;
w.state(&storage);
}
}
Ok(())
}
}
fn main() -> Result<(), Box<dyn std::error::Error>> {
integration::run()
}
+1
View File
@@ -12,6 +12,7 @@ pub mod active_effect_tick;
pub mod stored_consume;
pub mod attribute_health;
pub mod attribute_updates;
pub mod live_attribute_updates;
pub mod attributes;
pub mod block_collision;
pub mod block_collision_scan;
@@ -0,0 +1,85 @@
//! LivingEntity query/refresh dispatch over registered live attribute storage.
use crate::{
attribute_map::ValueKind,
attribute_updates::{self, UpdateWorld, UpdatedAttribute},
attributes::live::{Error, Handle, Key, LiveAttributes},
object_open_set::CursorError,
};
/// Entity handles keep a captured map alive even if a callback changes the
/// entity's current attributes. Map and instance accessors are direct storage
/// reads; virtual entity methods remain explicit dispatch points.
pub trait LiveAttributeWorld: UpdateWorld<Key> {
type Identity: FnMut() -> i32;
fn get_attributes(&mut self) -> Result<Self::Attributes, Self::Error>;
fn live_attributes(&self, map: Self::Attributes) -> &LiveAttributes<Self::Identity>;
fn live_attributes_mut(&mut self, map: Self::Attributes)
-> &mut LiveAttributes<Self::Identity>;
/// Resolve the queued instance itself, including an instance from another
/// map if a caller has explicitly placed it in this map's pending set.
fn instance_attribute(&self, instance: Option<Handle>) -> Result<Key, Self::Error>;
fn attribute_error(error: Error) -> Self::Error;
fn cursor_error(error: CursorError) -> Self::Error;
fn dispatch_attribute_updated(&mut self, attribute: Key) -> Result<(), Self::Error>
where
Self: Sized,
{
attribute_updates::updated(self, attribute)
}
}
/// Registered holder names are unique in a catalog. This classifier is for
/// registered attributes; custom holder dispatch can implement UpdateWorld.
pub fn kind(attribute: &Key) -> UpdatedAttribute {
match attribute.name() {
"minecraft:max_health" => UpdatedAttribute::MaxHealth,
"minecraft:max_absorption" => UpdatedAttribute::MaxAbsorption,
"minecraft:scale" => UpdatedAttribute::Scale,
"minecraft:waypoint_transmit_range" => UpdatedAttribute::WaypointRange,
_ => UpdatedAttribute::Other,
}
}
/// Base body of getAttributeValue/getAttributeBaseValue, including the virtual
/// getAttributes call. Querying defaults does not create instances.
pub fn value<W: LiveAttributeWorld>(
world: &mut W,
attribute: Option<Key>,
kind: ValueKind,
) -> Result<f64, W::Error> {
let map = world.get_attributes()?;
value_in(world, map, attribute, kind)
}
/// Query an already captured map, as used by waypoint updates after their
/// direct attributes-field read. This does not call virtual getAttributes.
pub fn value_in<W: LiveAttributeWorld>(
world: &mut W,
map: W::Attributes,
attribute: Option<Key>,
kind: ValueKind,
) -> Result<f64, W::Error> {
world
.live_attributes_mut(map)
.value(attribute, kind)
.map_err(W::attribute_error)
}
/// Base private refresh body for concrete registered AttributeMaps. Callbacks
/// may change map selection or mutate the captured pending set. Clear that set
/// only after the live cursor and every attribute callback finish successfully.
pub fn refresh<W: LiveAttributeWorld>(world: &mut W) -> Result<(), W::Error> {
let map = world.get_attributes()?;
let mut cursor = world.live_attributes(map).updates().cursor();
while cursor.has_next() {
let instance = world
.live_attributes(map)
.updates()
.next(&mut cursor)
.map_err(W::cursor_error)?;
let attribute = world.instance_attribute(instance)?;
world.dispatch_attribute_updated(attribute)?;
}
world.live_attributes_mut(map).updates_mut().clear();
Ok(())
}
+24
View File
@@ -1230,3 +1230,27 @@ targets compile, and library/affected fixture Clippy checks pass. This borrowed
binding keeps its selected attribute map fixed; entity virtual dispatch,
attribute refresh, player absorption synchronization and starvation damage
delivery still require their complete runtime integration.
## Entity refresh over live registered attributes
The live attribute bridge now connects virtual entity queries and pending
attribute refresh to registered storage and attribute-update dispatch. Refresh
retains its selected map through callbacks and clears that map's pending set
only on success. Health and absorption consequences read actual computed limits;
waypoint updates retain their manager before reading the direct attribute-map
field. Scale changes dispatch dimension refresh.
The [combined live update comparison](../benchmarks/rust-live-attribute-updates-2026-10-07/scoped-parity.json)
matches **2,836,190 client-reference lines and 2,836,234 server-reference lines**.
It executes actual native notifications, modifier loops, registered attribute
queries, private refresh, health/absorption update bodies and synchronized Float
storage. Cases cover all registered attributes/default suppliers, selected and
captured map/data handles, callbacks changing limits or queues, null pending
members, client guards, empty removals and failures late in large refreshes.
Each reference uses its own JVM identity hashes, so callback-mutation paths can
produce different trace lengths between runs. Library and fixture Clippy pass.
Dimension rebuilding and waypoint-manager internals remain controlled, and
passenger lists are empty in this combined fixture. Full active-effect lifecycle
and particle/invisibility synchronization, player absorption storage and complete
gameplay/platform integration remain unfinished.
@@ -0,0 +1,197 @@
package dev.horizonlab.parity;
import static dev.horizonlab.parity.VanillaEntityAttributeOracle.*;
import com.google.gson.*;
import com.google.common.collect.ImmutableList;
import java.io.*;
import java.lang.reflect.*;
import java.nio.file.*;
import java.util.*;
import net.minecraft.core.Holder;
import net.minecraft.core.registries.BuiltInRegistries;
import net.minecraft.resources.Identifier;
import net.minecraft.world.effect.*;
import net.minecraft.world.entity.*;
import net.minecraft.world.entity.ai.attributes.*;
import net.minecraft.world.level.Level;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.server.waypoints.ServerWaypointManager;
import net.minecraft.world.waypoints.WaypointTransmitter;
import net.minecraft.network.syncher.*;
/** Native entity updates and notifications with actual registered attribute maps. */
public final class VanillaLiveAttributeUpdatesOracle {
static Object unsafe;
static Method allocate, refresh;
static Field healthData, absorption, attributesField, effectDirty;
static EntityDataAccessor<Float> healthKey;
static Constructor<SynchedEntityData> dataCtor;
static <T> T make(Class<T> type) throws Exception { return type.cast(allocate.invoke(unsafe, type)); }
static float single(JsonElement v) { return Float.intBitsToFloat((int)v.getAsLong()); }
static long floatBits(float value) { return Integer.toUnsignedLong(Float.floatToIntBits(value)); }
static final class Stop extends RuntimeException {}
static final class Data {
VanillaLiveAttributesOracle.Data[] maps = new VanillaLiveAttributesOracle.Data[2];
VanillaLiveAttributesOracle.Effect effect = new VanillaLiveAttributesOracle.Effect();
SynchedEntityData[] stores = new SynchedEntityData[2];
List<SynchedEntityData.DataItem<Float>> items = new ArrayList<>();
Manager[] managers = new Manager[2];
Actor actor; EffectLevel level; PrintWriter out; JsonObject op;
int selection, direct, data, manager, calls;
boolean server = true, client;
Holder<Attribute> key(JsonObject op) {
int index = op.get("attribute").getAsInt();
return index < 0 ? null : attributes.get(index);
}
void controls(JsonObject c) throws Exception {
if (c.has("selection")) selection = c.get("selection").getAsInt();
if (c.has("direct")) direct = c.get("direct").getAsInt();
if (c.has("data")) data = c.get("data").getAsInt();
if (c.has("manager")) manager = c.get("manager").getAsInt();
if (c.has("server")) server = c.get("server").getAsBoolean();
if (c.has("client")) client = c.get("client").getAsBoolean();
if (c.has("absorption")) absorption.setFloat(actor, single(c.get("absorption")));
healthData.set(actor, stores[data]);
attributesField.set(actor, maps[direct].attributes);
}
void event(String name) {
int call = calls++;
out.println("event " + call + " " + name);
if (call == op.get("fail").getAsInt()) throw new Stop();
if (call == op.get("mutate").getAsInt()) {
try {
controls(op.getAsJsonObject("changes"));
for (var action : op.getAsJsonArray("actions")) raw(action.getAsJsonObject());
} catch (RuntimeException e) { throw e; }
catch (Exception e) { throw new AssertionError(e); }
}
}
String raw(JsonObject op) throws Exception {
var map = maps[op.get("target").getAsInt()];
var key = key(op);
String mode = op.get("mode").getAsString();
var modifier = new AttributeModifier(Identifier.parse(op.get("id").getAsString()), number(op.get("amount")), AttributeModifier.Operation.values()[op.get("operation").getAsInt()]);
switch (mode) {
case "controls": controls(op.getAsJsonObject("changes")); return "ok";
case "ensure": return map.reference(map.attributes.getInstance(key));
case "reset": return Boolean.toString(map.attributes.resetBaseValue(key));
case "define": effect.addAttributeModifier(key, modifier.id(), modifier.amount(), modifier.operation()); return "ok";
case "effect-add": effect.addAttributeModifiers(map.attributes, op.get("amplifier").getAsInt()); return "ok";
case "effect-remove": effect.removeAttributeModifiers(map.attributes); return "ok";
case "clear-updates": map.attributes.getAttributesToUpdate().clear(); return "ok";
case "clear-sync": map.attributes.getAttributesToSync().clear(); return "ok";
case "null-update": map.attributes.getAttributesToUpdate().add(null); return "ok";
case "dirty": effectDirty.setBoolean(actor, op.get("flag").getAsBoolean()); return "ok";
default:
var instance = map.attributes.getInstance(key);
if (instance == null) return "absent";
switch (mode) {
case "set": instance.setBaseValue(number(op.get("base"))); break;
case "update": instance.addOrUpdateTransientModifier(modifier); break;
case "permanent": instance.addPermanentModifier(modifier); break;
case "remove": return Boolean.toString(instance.removeModifier(modifier.id()));
case "clear": instance.removeModifiers(); break;
default: throw new AssertionError(mode);
}
return "ok";
}
}
void state() throws Exception {
out.println("controls " + selection + " " + direct + " " + data + " " + manager + " " + server + " " + client + " " + calls + " " + effectDirty.getBoolean(actor));
for (int i = 0; i < 2; i++) out.println("health " + i + " " + floatBits(items.get(i).getValue()) + " " + stores[i].isDirty() + " " + items.get(i).isDirty() + " " + items.get(i).isSetToDefault());
out.println("absorption " + floatBits(absorption.getFloat(actor)));
for (int i = 0; i < 2; i++) { out.println("attributes " + i); maps[i].state(out); }
}
}
static final class Actor extends net.minecraft.world.entity.decoration.ArmorStand {
Data d; Actor() { super(null, null); }
@Override public AttributeMap getAttributes() { d.event("attributes"); return d.maps[d.selection].attributes; }
@Override public double getAttributeValue(Holder<Attribute> h) { d.event("value " + (h == null ? "null" : h.getRegisteredName())); return super.getAttributeValue(h); }
@Override public float getHealth() { d.event("health"); return super.getHealth(); }
@Override public void setHealth(float v) { d.event("set-health " + floatBits(v)); super.setHealth(v); }
@Override public float getAbsorptionAmount() { d.event("absorption"); return super.getAbsorptionAmount(); }
@Override protected void internalSetAbsorptionAmount(float v) { d.event("store-absorption " + floatBits(v)); super.internalSetAbsorptionAmount(v); }
@Override public void onSyncedDataUpdated(EntityDataAccessor<?> key) { if (key != healthKey) throw new AssertionError(); d.event("health-updated"); }
@Override public void refreshDimensions() { d.event("dimensions"); }
@Override public Level level() { d.event("level"); return d.server ? d.level : null; }
@Override protected void onAttributeUpdated(Holder<Attribute> h) { d.event("updated " + (h == null ? "null" : h.getRegisteredName())); super.onAttributeUpdated(h); }
void added(MobEffectInstance e) { super.onEffectAdded(e, null); }
void updated(MobEffectInstance e, boolean reapply) { super.onEffectUpdated(e, reapply, null); }
void removed(List<MobEffectInstance> es) { super.onEffectsRemoved(es); }
}
static final class EffectLevel extends ServerLevel {
Data d; EffectLevel() { super(null,null,null,null,null,null,false,0L,List.of(),false); }
@Override public boolean isClientSide() { d.event("client"); return d.client; }
@Override public ServerWaypointManager getWaypointManager() { d.event("manager"); return d.managers[d.manager]; }
}
static final class Manager extends ServerWaypointManager {
Data d; int id;
@Override public void trackWaypoint(WaypointTransmitter actor) { if (actor != d.actor) throw new AssertionError(); d.event("waypoint " + id + " true"); }
@Override public void untrackWaypoint(WaypointTransmitter actor) { if (actor != d.actor) throw new AssertionError(); d.event("waypoint " + id + " false"); }
}
@SuppressWarnings("unchecked")
public static void main(String[] args) throws Exception {
net.minecraft.SharedConstants.tryDetectVersion(); net.minecraft.server.Bootstrap.bootStrap();
for (var a : BuiltInRegistries.ATTRIBUTE) attributes.add(BuiltInRegistries.ATTRIBUTE.wrapAsHolder(a));
Path dir = Path.of(args[1]);
if (args[0].startsWith("export-")) { Files.writeString(dir.resolve("catalog-" + args[0].substring(7) + ".json"), export().toString()); return; }
var u = Class.forName("sun.misc.Unsafe"); unsafe = field(u, "theUnsafe").get(null); allocate = u.getMethod("allocateInstance", Class.class);
healthData = field(Entity.class, "entityData"); absorption = field(LivingEntity.class, "absorptionAmount"); attributesField = field(LivingEntity.class, "attributes"); effectDirty = field(LivingEntity.class, "effectsDirty");
var passengers = field(Entity.class, "passengers");
refresh = LivingEntity.class.getDeclaredMethod("refreshDirtyAttributes"); refresh.setAccessible(true);
healthKey = (EntityDataAccessor<Float>)field(LivingEntity.class, "DATA_HEALTH_ID").get(null);
dataCtor = SynchedEntityData.class.getDeclaredConstructor(SyncedDataHolder.class, SynchedEntityData.DataItem[].class); dataCtor.setAccessible(true);
VanillaLiveAttributesOracle.storage = field(AttributeMap.class, "attributes"); VanillaLiveAttributesOracle.dirty = field(AttributeInstance.class, "dirty"); VanillaLiveAttributesOracle.cached = field(AttributeInstance.class, "cachedValue");
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 f = JsonParser.parseString(line).getAsJsonObject(); var d = new Data(); d.out = out;
d.actor = make(Actor.class); d.actor.d = d; d.level = make(EffectLevel.class); d.level.d = d;
var entity = BuiltInRegistries.ENTITY_TYPE.getValue(Identifier.parse(f.get("entity").getAsString()));
for (int i = 0; i < 2; i++) {
var map = new VanillaLiveAttributesOracle.Data(); d.maps[i] = map;
map.attributes = new VanillaLiveAttributesOracle.Attrs(map, supplier(entity));
map.map = (Map<Holder<Attribute>,AttributeInstance>)VanillaLiveAttributesOracle.storage.get(map.attributes);
d.managers[i] = make(Manager.class); d.managers[i].d = d; d.managers[i].id = i;
var items = new SynchedEntityData.DataItem<?>[healthKey.id() + 1];
for (int j = 0; j < items.length; j++) items[j] = new SynchedEntityData.DataItem<Float>(new EntityDataAccessor<Float>(j, EntityDataSerializers.FLOAT), 0f);
var health = new SynchedEntityData.DataItem<Float>(healthKey, single(f.getAsJsonArray("health").get(i)));
d.items.add(health); items[healthKey.id()] = health; d.stores[i] = dataCtor.newInstance(d.actor, items);
}
effectDirty.setBoolean(d.actor, true); passengers.set(d.actor, ImmutableList.of()); absorption.setFloat(d.actor, single(f.get("absorption"))); d.controls(new JsonObject());
for (var op : f.getAsJsonArray("setup")) d.raw(op.getAsJsonObject());
var effect = Holder.<MobEffect>direct(d.effect);
for (var ov : f.getAsJsonArray("ops")) {
d.op = ov.getAsJsonObject(); d.calls = 0; String result = "ok";
var op = d.op; String mode = op.get("mode").getAsString();
try {
switch (mode) {
case "value": result = bits(d.actor.getAttributeValue(d.key(op))); break;
case "base": result = bits(d.actor.getAttributeBaseValue(d.key(op))); break;
case "set-health": d.actor.setHealth(single(op.get("value"))); break;
case "set-absorption": d.actor.setAbsorptionAmount(single(op.get("value"))); break;
case "heal": d.actor.heal(single(op.get("value"))); break;
case "attribute-update": d.actor.onAttributeUpdated(d.key(op)); break;
case "refresh": try { refresh.invoke(d.actor); } catch (InvocationTargetException e) { if (e.getCause() instanceof RuntimeException x) throw x; throw new AssertionError(e.getCause()); } break;
case "notify-add": d.actor.added(new MobEffectInstance(effect,200,op.get("amplifier").getAsInt())); break;
case "notify-update": d.actor.updated(new MobEffectInstance(effect,200,op.get("amplifier").getAsInt()),op.get("reapply").getAsBoolean()); break;
case "notify-remove": d.actor.removed(Collections.nCopies(op.get("count").getAsInt(),new MobEffectInstance(effect,200,op.get("amplifier").getAsInt()))); break;
case "pack": var packed = d.stores[op.get("target").getAsInt()].packDirty(); result = packed == null ? "none" : Long.toString(floatBits((Float)packed.getFirst().value())); break;
default: result = d.raw(op);
}
} catch (Stop e) { result = "error callback"; }
catch (NullPointerException e) { result = "error null"; }
catch (IndexOutOfBoundsException e) { result = "error bounds"; }
catch (NoSuchElementException e) { result = "error empty"; }
catch (IllegalArgumentException e) { result = "error argument"; }
out.println("result " + result); d.state();
}
var hashes = new JsonArray(); for (var key : attributes) hashes.add(key.hashCode()); f.add("holder_hashes", hashes);
var instances = new JsonArray(); for (var map : d.maps) { hashes = new JsonArray(); for (var a : map.instances) hashes.add(System.identityHashCode(a)); instances.add(hashes); } f.add("instance_hashes", instances);
enriched.println(f);
}
if (out.checkError() || enriched.checkError()) throw new IOException("cannot write native trace");
}
}
}
+194
View File
@@ -0,0 +1,194 @@
#!/usr/bin/env python3
"""Compare live registered attributes with native entity notifications and update consequences."""
import argparse
import json
import math
import os
from pathlib import Path
import platform
import random
import struct
import subprocess
try:
from . import lab, test_rust_parity as base
except ImportError:
import lab
import test_rust_parity as base
def corpus(catalog):
def bits(value):
return struct.pack('>d', float(value)).hex()
def fl(value):
return int.from_bytes(struct.pack('>f', float(value)), 'big')
def op(mode, attribute=0, **kw):
result = dict(mode=mode, target=0, attribute=attribute, id='horizon:a',
amount=bits(.25), base=bits(7), operation=0, value=fl(100),
amplifier=0, fail=-1, mutate=-1, changes={}, actions=[],
reapply=True, count=1, flag=False)
result.update(kw)
return result
def row(setup, ops, entity='minecraft:player', **kw):
result = dict(entity=entity, health=[fl(100), fl(200)], absorption=fl(100), setup=setup, ops=ops)
result.update(kw)
return result
attrs = catalog['attributes']
count = len(attrs)
ids = [next(i for i, a in enumerate(attrs) if a['id'] == 'minecraft:' + name)
for name in ['max_health', 'max_absorption', 'scale', 'waypoint_transmit_range', 'attack_damage']]
values = [-math.inf, -1e30, -1, -0., 0., 1e-40, .1, 1, 2, 20, 1e30, math.inf, math.nan]
rng = random.Random(0x4c495645555044)
for entity in catalog['entities']:
for reverse in [False, True]:
order = list(range(count))
if reverse:
order.reverse()
ops = [op(mode, i) for i in [-1, *order] for mode in ['value', 'base']]
for target in range(2):
ops.extend(op('ensure', i, target=target) for i in order)
ops += [op('refresh'), op('pack'), op('pack', target=1), op('controls', changes={'selection': 1}), op('refresh')]
for i in order:
ops.append(op('define', i, operation=i % 3, amount=bits(-2)))
ops += [op('dirty'), op('notify-add', amplifier=1), op('refresh'), op('notify-update', amplifier=2),
op('notify-remove'), op('notify-remove', count=0), op('controls', changes={'client': True}),
op('dirty'), op('notify-add'), op('notify-update'), op('notify-remove'),
op('controls', changes={'client': False, 'server': False}), op('attribute-update', ids[3])]
yield row([], ops, entity)
setup = [op('set', key, target=target, base=bits(10 if target == 0 else 30))
for target in range(2) for key in ids]
setup += [op('define', key, amount=bits(-3)) for key in ids]
for key in ids[:4]:
for value in values:
for operation in range(3):
ops = [op('set', key, base=bits(value)), op('attribute-update', key), op('pack'),
op('define', key, amount=bits(value), operation=operation), op('notify-add'),
op('refresh'), op('set-health', value=fl(value)), op('set-absorption', value=fl(value)),
op('notify-update', amplifier=255), op('notify-remove'), op('pack'), op('refresh')]
yield row(setup, ops, health=[fl(value), fl(-value)], absorption=fl(value))
changes = {'selection': 1, 'direct': 1, 'data': 1, 'manager': 1, 'absorption': fl(50)}
actions = [[], [op('set', ids[0], base=bits(2)), op('set', ids[0], target=1, base=bits(3))],
[op('set', ids[1], base=bits(2)), op('set', ids[1], target=1, base=bits(3))],
[op('clear-updates')], [op('null-update')], [op('clear-updates', target=1)],
[op('set', ids[3], target=1, base=bits(0))], [op('clear-sync')]]
# Inject failures and routing/limit changes at successive callback positions
# so captures and writes preceding an exception remain visible in state.
for key in ids[:4]:
for mode in ['attribute-update', 'refresh', 'set-health', 'set-absorption', 'notify-add', 'notify-update', 'notify-remove']:
for at in range(-1, 32):
yield row(setup, [op(mode, key, fail=at), op('pack'), op('refresh')])
for action in actions:
yield row(setup, [op(mode, key, mutate=at, changes=changes, actions=action), op('pack'), op('refresh')])
large_setup = [op('ensure', key, target=target) for target in range(2) for key in range(count)]
large_setup += [op('define', key, amount=bits(-3)) for key in range(count)]
for mode in ['refresh', 'notify-update', 'notify-remove']:
for at in range(64):
yield row(large_setup, [op(mode, fail=at), op('pack'), op('refresh')])
yield row(large_setup, [op(mode, mutate=at, changes=changes, actions=actions[at % len(actions)]), op('pack'), op('refresh')])
for amp in [-2**31, -1, 0, 1, 255, 256, 2**31-1]:
for reapply in [False, True]:
for n in [0, 1, 3]:
yield row(setup, [op('notify-add', amplifier=amp), op('notify-update', amplifier=amp, reapply=reapply),
op('notify-remove', count=n), op('refresh'), op('pack')])
for _ in range(200):
ops = []
for _ in range(40):
ops.append(op(rng.choice(['set', 'update', 'permanent', 'remove', 'reset', 'value', 'base',
'refresh', 'attribute-update', 'define', 'notify-add', 'notify-update',
'notify-remove', 'effect-add', 'effect-remove', 'pack', 'set-health',
'set-absorption', 'heal', 'clear-updates', 'clear-sync']),
rng.choice([-1, *ids]), target=rng.randrange(2), operation=rng.randrange(3),
amount=bits(rng.choice(values)), base=bits(rng.choice(values)), value=fl(rng.choice(values)),
amplifier=rng.choice([-2**31, -1, 0, 1, 255, 256, 2**31-1]), count=rng.randrange(3),
reapply=bool(rng.randrange(2)), fail=rng.choice([-1, -1, -1, 0, 1, 2]),
mutate=rng.choice([-1, -1, 0, 1, 2]), changes=changes, actions=rng.choice(actions)))
yield row(setup, ops)
def main():
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument('--prepare', action='store_true')
args = parser.parse_args()
build = lab.LOCAL / 'rust-live-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/VanillaLiveAttributeUpdatesOracle.java'
helper = oracle.with_name('VanillaEntityAttributeOracle.java')
live_helper = oracle.with_name('VanillaLiveAttributesOracle.java')
sources = [oracle, helper, live_helper, 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/live_attribute_updates_fixture.rs',
lab.ROOT / 'crates/horizon-world/examples/live_attributes_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, helper, live_helper], 'compile-java')
def java(side, mode):
run(['java', '-Xmx512M', '-cp', os.pathsep.join([str(classes), str(jars[side]), dependency_path]),
'dev.horizonlab.parity.VanillaLiveAttributeUpdatesOracle', mode, build], mode)
for side in jars:
java(side, 'export-' + side)
catalog = json.loads((build / 'catalog-server.json').read_text())
if catalog != json.loads((build / 'catalog-client.json').read_text()):
raise AssertionError('Client/server attribute catalogs differ')
lab.write_json(build / 'catalog.json', catalog)
fixtures = list(corpus(catalog))
inputs = build / 'inputs.txt'
inputs.write_text(''.join(json.dumps(row, separators=(',', ':')) + '\n' for row in fixtures), encoding='utf-8')
fixture_paths = [inputs, build / 'catalog.json', build / 'catalog-client.json', build / 'catalog-server.json']
tested_fixtures = base.source_hashes(fixture_paths)
run(['cargo', 'build', '--release', '-p', 'horizon-world', '--example', 'live_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' / ('live_attribute_updates_fixture.exe' if os.name == 'nt' else 'live_attribute_updates_fixture')
references = []
for side, jar in jars.items():
java(side, side)
enriched = build / f'inputs-{side}.txt'
fixture_paths.append(enriched)
tested_fixtures.update(base.source_hashes([enriched]))
run([binary, build, side], 'rust-' + side)
reference = build / f'{side}.txt'
candidate = build / f'rust-{side}.txt'
matched = base.compare(reference, candidate)
references.append(dict(side=side, jar_sha256=lab.digest(jar, 'sha256'),
output_sha256=lab.digest(reference, 'sha256'), matched_output_lines=matched,
rust_output_sha256=lab.digest(candidate, 'sha256')))
print(f'{side}: {matched:,} live attribute update output lines match Rust', flush=True)
base.ensure_sources_unchanged(sources, tested_sources)
base.ensure_sources_unchanged(fixture_paths, tested_fixtures)
report = dict(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),
attributes=len(catalog['attributes']), entity_suppliers=len(catalog['entities']),
fastutil_sha256={str(p.relative_to(lab.ROOT)): lab.digest(p, 'sha256') for p in libraries if 'fastutil' in str(p)},
sources_sha256=tested_sources, fixtures_sha256=tested_fixtures, references=references,
scope='Native LivingEntity attribute queries, notification and private refresh methods with registered AttributeMaps, real effect modifiers and pending queues, onAttributeUpdated health/absorption consequences and SynchedEntityData Float storage. Exercises all registered attributes/default suppliers, separate captured/selected maps and health stores, callbacks changing limits and routes, null pending members, client guards, failure prefixes and modifier/cache/queue identity. Replays JVM holder/instance hashes per reference. Scale dimension rebuild and waypoint-manager internals remain controlled; passenger lists are empty. Full effect lifecycle/dirty-particle synchronization, player absorption, complete gameplay and playable platform builds remain pending.')
lab.write_json(report_path, report)
print(f'Scoped live attribute update parity passed: {report_path}')
if __name__ == '__main__':
main()