Match effect eligibility and attribute/passenger notification ordering

This commit is contained in:
Ludovic Marie committed 2026-10-07 11:31:39 +02:00
1 parent f9d8277b73
commit a772a69498
8 files changed
+1228

No files matched your search

@@ -0,0 +1,301 @@
{
"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": 4476,
"sources_sha256": {
"mods/horizon/src/test/java/dev/horizonlab/parity/VanillaEffectNotificationsOracle.java": "44e9c1e4250a49a7b48c836caea5f9d3b863edf3238420272ee0d8671e1e1673",
"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/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": "f7a851566dbabb96c138c3a364617f3ba84070908772fa44970d8079c2a4e47a",
"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/effect_notifications_fixture.rs": "7fe3750a96c45e6023d931d5615fff35f011b6eb5eae0a0075d58393a48f449e"
},
"fixtures_sha256": {
".local/rust-effect-notifications-parity/inputs.txt": "e7e3cbd8c5d6adafd87afa28919060f429bdf9ef7bd818a0e1dc7ddeb1c4ee4d",
".local/rust-effect-notifications-parity/inputs-client.txt": "b655c7578f2f1acc55188de35f00f997d44437d7fd309dd692c7d8a68d03f177",
".local/rust-effect-notifications-parity/inputs-server.txt": "b655c7578f2f1acc55188de35f00f997d44437d7fd309dd692c7d8a68d03f177"
},
"references": [
{
"side": "client",
"jar_sha256": "4508d006323f24fa02876310c192d739af56516eb259000ac50f0909a68c9a2d",
"output_sha256": "399be32217b9df00f188f9692bc9b5b467e225eefce857a88554cd1b0fe1a9ed",
"rust_output_sha256": "399be32217b9df00f188f9692bc9b5b467e225eefce857a88554cd1b0fe1a9ed",
"matched_output_lines": 1667616
},
{
"side": "server",
"jar_sha256": "a362163eec5d1612d520772bc16e5b39c09e3b234fdc045f56bf544284ee8ae6",
"output_sha256": "399be32217b9df00f188f9692bc9b5b467e225eefce857a88554cd1b0fe1a9ed",
"rust_output_sha256": "399be32217b9df00f188f9692bc9b5b467e225eefce857a88554cd1b0fe1a9ed",
"matched_output_lines": 1667616
}
],
"scope": "Inherited LivingEntity eligibility and add/update/remove notification bodies, private refreshDirtyAttributes and sendEffectToPassengers, composed with native active-effect storage. Actual effect instances, fastutil pending sets, immutable passenger lists and update/remove packet constructors are used. Covers eligibility tag precedence, dirty writes, captured maps/sets/connections, callback mutation, pending iteration/clear prefixes, packet fields and failures. Registered modifier bodies, onAttributeUpdated consequences and packet transport remain controlled; full entity/player lifecycle and gameplay remain pending."
}
@@ -0,0 +1,506 @@
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 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> {
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(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, 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> {
self.event(s, format!("attribute-updated {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)
}
}
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 mut w = World {
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() {
"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(())
}
@@ -5,6 +5,8 @@ use crate::{
};
pub struct ActiveEffects<H> {
/// LivingEntity schedules effect synchronization from construction.
pub dirty: bool,
/// Native activeEffects is exposed by getActiveEffectsMap. Direct mutations
/// bypass lifecycle notifications, but retain the map's iterator semantics.
pub entries: JavaHashMap<H, EffectInstance<H>>,
@@ -13,6 +15,7 @@ impl<H: JavaKey> Default for ActiveEffects<H> {
fn default() -> Self {
Self {
entries: JavaHashMap::default(),
dirty: true,
}
}
}
@@ -0,0 +1,246 @@
//! Base LivingEntity effect eligibility, attribute refresh and passenger updates.
use crate::{
active_effects::{ActiveEffects, EffectHooks},
java_hash_map::JavaKey,
object_open_set::{CursorError, ObjectKey, ObjectOpenSet},
status_effect::EffectInstance,
};
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum ImmunityTag {
Infested,
Oozing,
PoisonAndRegeneration,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum RestrictedEffect {
Infested,
Oozing,
Regeneration,
Poison,
}
pub trait EligibilityWorld<H> {
type Error;
fn has_immunity_tag(
&mut self,
storage: &mut ActiveEffects<H>,
tag: ImmunityTag,
) -> Result<bool, Self::Error>;
fn is_restricted_effect(&self, effect: &H, kind: RestrictedEffect) -> bool;
}
pub fn can_be_affected<H, W: EligibilityWorld<H>>(
storage: &mut ActiveEffects<H>,
instance: &EffectInstance<H>,
world: &mut W,
) -> Result<bool, W::Error> {
// These branches are exclusive, even when the actor belongs to several tags.
if world.has_immunity_tag(storage, ImmunityTag::Infested)? {
Ok(!world.is_restricted_effect(&instance.read().effect, RestrictedEffect::Infested))
} else if world.has_immunity_tag(storage, ImmunityTag::Oozing)? {
Ok(!world.is_restricted_effect(&instance.read().effect, RestrictedEffect::Oozing))
} else if world.has_immunity_tag(storage, ImmunityTag::PoisonAndRegeneration)? {
let data = instance.read();
Ok(
!world.is_restricted_effect(&data.effect, RestrictedEffect::Regeneration)
&& !world.is_restricted_effect(&data.effect, RestrictedEffect::Poison),
)
} else {
Ok(true)
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum EffectPacket<H> {
Update {
entity: i32,
effect: H,
amplifier: i32,
duration: i32,
flags: u8,
},
Remove {
entity: i32,
effect: H,
},
}
impl<H: Clone> EffectPacket<H> {
pub fn update(entity: i32, instance: &EffectInstance<H>, blend: bool) -> Self {
let data = instance.read();
Self::Update {
entity,
effect: data.effect.clone(),
amplifier: data.details.amplifier,
duration: data.details.duration,
flags: u8::from(data.details.ambient)
| (u8::from(data.details.visible) << 1)
| (u8::from(data.details.show_icon) << 2)
| (u8::from(blend) << 3),
}
}
}
/// Attribute maps, pending-update sets and connections are stable handles. A
/// callback may switch the actor's current handle without changing a captured one.
pub trait NotificationWorld<H: JavaKey + Clone>: EffectHooks<H> {
type Attributes: Copy;
type UpdateSet: Copy;
type AttributeInstance: ObjectKey + Clone;
type Attribute;
type Passenger: Copy;
type Connection: Copy;
fn attributes(
&mut self,
storage: &mut ActiveEffects<H>,
) -> Result<Self::Attributes, Self::Error>;
fn add_modifiers(
&mut self,
storage: &mut ActiveEffects<H>,
effect: H,
attributes: Self::Attributes,
amplifier: i32,
) -> Result<(), Self::Error>;
fn remove_modifiers(
&mut self,
storage: &mut ActiveEffects<H>,
effect: H,
attributes: Self::Attributes,
) -> Result<(), Self::Error>;
fn pending_updates(
&mut self,
storage: &mut ActiveEffects<H>,
attributes: Self::Attributes,
) -> Result<Self::UpdateSet, Self::Error>;
fn update_set(&self, set: Self::UpdateSet) -> &ObjectOpenSet<Self::AttributeInstance>;
fn update_set_mut(
&mut self,
set: Self::UpdateSet,
) -> &mut ObjectOpenSet<Self::AttributeInstance>;
fn iterator_error(error: CursorError) -> Self::Error;
/// None preserves a native null member's failure at getAttribute.
fn attribute_of(
&mut self,
storage: &mut ActiveEffects<H>,
instance: Option<Self::AttributeInstance>,
) -> Result<Self::Attribute, Self::Error>;
fn attribute_updated(
&mut self,
storage: &mut ActiveEffects<H>,
attribute: Self::Attribute,
) -> Result<(), Self::Error>;
/// Entity.getPassengers is final and returns its current immutable list.
fn passengers(&self) -> Vec<Self::Passenger>;
fn is_server_player(&self, passenger: Self::Passenger) -> bool;
/// Direct ServerPlayer.connection field access before the virtual getId call.
fn connection(&self, passenger: Self::Passenger) -> Self::Connection;
fn entity_id(&mut self, storage: &mut ActiveEffects<H>) -> Result<i32, Self::Error>;
fn send_packet(
&mut self,
storage: &mut ActiveEffects<H>,
connection: Self::Connection,
packet: EffectPacket<H>,
) -> Result<(), Self::Error>;
fn send_effect_to_passengers(
&mut self,
storage: &mut ActiveEffects<H>,
instance: &EffectInstance<H>,
) -> Result<(), Self::Error>
where
Self: Sized,
{
send_to_passengers(storage, instance, self)
}
}
pub fn send_to_passengers<H: JavaKey + Clone, W: NotificationWorld<H>>(
storage: &mut ActiveEffects<H>,
instance: &EffectInstance<H>,
world: &mut W,
) -> Result<(), W::Error> {
for passenger in world.passengers() {
if world.is_server_player(passenger) {
let connection = world.connection(passenger);
let entity = world.entity_id(storage)?;
let packet = EffectPacket::update(entity, instance, false);
world.send_packet(storage, connection, packet)?;
}
}
Ok(())
}
pub fn refresh_attributes<H: JavaKey + Clone, W: NotificationWorld<H>>(
storage: &mut ActiveEffects<H>,
world: &mut W,
) -> Result<(), W::Error> {
let attributes = world.attributes(storage)?;
let updates = world.pending_updates(storage, attributes)?;
let mut cursor = world.update_set(updates).cursor();
while cursor.has_next() {
let instance = world
.update_set(updates)
.next(&mut cursor)
.map_err(W::iterator_error)?;
let attribute = world.attribute_of(storage, instance)?;
world.attribute_updated(storage, attribute)?;
}
world.update_set_mut(updates).clear();
Ok(())
}
pub fn on_added<H: JavaKey + Clone, W: NotificationWorld<H>>(
storage: &mut ActiveEffects<H>,
instance: &EffectInstance<H>,
world: &mut W,
) -> Result<(), W::Error> {
let level = world.level(storage)?;
if !world.is_client(storage, level)? {
storage.dirty = true;
let effect = instance.read().effect.clone();
let attributes = world.attributes(storage)?;
let amplifier = instance.read().details.amplifier;
world.add_modifiers(storage, effect, attributes, amplifier)?;
world.send_effect_to_passengers(storage, instance)?;
}
Ok(())
}
pub fn on_updated<H: JavaKey + Clone, W: NotificationWorld<H>>(
storage: &mut ActiveEffects<H>,
instance: &EffectInstance<H>,
reapply: bool,
world: &mut W,
) -> Result<(), W::Error> {
let level = world.level(storage)?;
if world.is_client(storage, level)? {
return Ok(());
}
storage.dirty = true;
if reapply {
let effect = instance.read().effect.clone();
let attributes = world.attributes(storage)?;
world.remove_modifiers(storage, effect.clone(), attributes)?;
let attributes = world.attributes(storage)?;
let amplifier = instance.read().details.amplifier;
world.add_modifiers(storage, effect, attributes, amplifier)?;
refresh_attributes(storage, world)?;
}
world.send_effect_to_passengers(storage, instance)
}
pub fn on_removed<H: JavaKey + Clone, W: NotificationWorld<H>>(
storage: &mut ActiveEffects<H>,
instances: &[EffectInstance<H>],
world: &mut W,
) -> Result<(), W::Error> {
let level = world.level(storage)?;
if world.is_client(storage, level)? {
return Ok(());
}
storage.dirty = true;
for instance in instances {
let effect = instance.read().effect.clone();
let attributes = world.attributes(storage)?;
world.remove_modifiers(storage, effect, attributes)?;
for passenger in world.passengers() {
if world.is_server_player(passenger) {
let connection = world.connection(passenger);
let entity = world.entity_id(storage)?;
let effect = instance.read().effect.clone();
world.send_packet(storage, connection, EffectPacket::Remove { entity, effect })?;
}
}
}
refresh_attributes(storage, world)
}
+1
View File
@@ -2,6 +2,7 @@
#![forbid(unsafe_code)]
pub mod object_open_set;
pub mod effect_notifications;
pub mod active_effects;
pub mod active_effect_tick;
pub mod stored_consume;
+19
View File
@@ -1059,3 +1059,22 @@ operations and live iteration; alternate constructors, bulk methods, and externa
structural mutation followed by iterator removal remain outside its scope.
Binding this storage to attribute refresh and the complete entity lifecycle is
the next step; this collection comparison does not establish gameplay parity.
## Effect eligibility and entity notification bodies
Effect eligibility now follows vanilla's ordered immunity-tag branches. Entity
effect callbacks mark synchronization dirty, retain captured effect/map handles,
apply or remove modifiers, refresh pending attributes and construct passenger
update/removal packets in native order. New effect storage starts dirty, matching
the entity constructor. Attribute refresh reads its captured live fastutil set
and clears that same set only after every callback succeeds. Passenger lists
remain captured while later connection fields and packet data are read at their
native evaluation points.
The [notification comparison](../benchmarks/rust-effect-notifications-2026-10-07/scoped-parity.json)
matches **1,667,616 lines per reference JAR**, exercising inherited native
eligibility, notification, refresh and passenger methods together with effect
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.
@@ -0,0 +1,55 @@
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;
/** Inherited eligibility and effect notification bodies with real packets and pending sets. */
public final class VanillaEffectNotificationsOracle {
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 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;EffectLevel[] levels;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();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++){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")];}
@Override protected void onAttributeUpdated(Holder<Attribute> h){d.event("attribute-updated "+attributeHolders.indexOf(h));}
@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 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 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);}}
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);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");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 EffectLevel[2];for(int i=0;i<2;i++){d.levels[i]=make(EffectLevel.class);d.levels[i].d=d;d.levels[i].id=i;}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.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 "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();}
}}
}
}
+97
View File
@@ -0,0 +1,97 @@
#!/usr/bin/env python3
"""Compare native effect eligibility, notification bodies, pending sets and passenger packets."""
import argparse
import json
import os
from pathlib import Path
import platform
import subprocess
import random
try:
from . import lab, test_rust_parity as base
except ImportError:
import lab
import test_rust_parity as base
def corpus():
rng=random.Random(0x4e4f54494659)
durations=[-2147483648,-2,-1,0,1,20,200,2147483647]
amps=[-1,0,1,2,255,256,2147483647]
floats=[0,0x80000000,0x3f000000,0x3f800000,0xbf800000,0x7f800000,0x7fc00000]
def state(**kw):
v=dict(key=4,duration=20,amplifier=1,ambient=False,visible=True,icon=True,hidden=-1,factor=0x3f000000,previous=0);v.update(kw);return v
def config(**kw):
v=dict(states=[state(key=i) for i in range(6)],level=0,client=False,removed=False,dirty=False,tags=[False]*3,attributes=0,routes=[0,1],holders=[0,1,2],passengers=[0,2,1,0],connections=[0,1],entity=42);v.update(kw);return v
def op(name,a=4,b=0,**kw):
v=dict(op=name,a=a,b=b,source=-1,partial=0x3f000000,list=[4,5],reapply=True);v.update(kw);return v
def row(**kw):
v=dict(hashes=[0,1,2,3,0,0],attribute_hashes=[0,31,64],initial=config(),after=config(states=[state(key=i,duration=-1,amplifier=9,ambient=True,visible=False,icon=False) for i in range(6)],attributes=1,routes=[1,0],holders=[2,0,1],passengers=[1,0],connections=[1,0],entity=-13,dirty=None),setup=[op('pending-add',0,i) for i in range(3)]+[op('pending-add',1,2)],ops=[op('add'),op('force',5),op('all')],fail=-1,mutate=-1,mutations=[op('put',4,5)]);v.update(kw);return v
for bits in range(8):
for effect in range(6):
for mutate in range(-1,3):
for fail in range(-1,3):
yield row(initial=config(tags=[bool(bits&(1<<i)) for i in range(3)]),ops=[op('eligible',effect)],mutate=mutate,fail=fail)
for mode in ['notify-add','notify-update','notify-remove','send','refresh','add','force','remove','all']:
for mutate in range(-1,15):
for fail in range(-1,15):
yield row(setup=[op('put',4,4),op('put',5,5),op('pending-add',0,0),op('pending-add',0,1),op('pending-add',1,2)],ops=[op(mode),op('get')],mutate=mutate,fail=fail)
for mode in ['notify-add','notify-update','notify-remove','send','refresh']:
for client in [False,True]:
for reapply in [False,True]:
for passengers in [[],[2],[0],[1,0,1],[0,2,1,0]]:
for connections in [[0,1],[1,0],[-1,1],[0,-1]]:
yield row(initial=config(client=client,passengers=passengers,connections=connections),ops=[op(mode,reapply=reapply)])
for count in [0,1,3,24,25,49,64]:
for collision in [False,True]:
for mutate in range(-1,8):
for changes in [[],[op('pending-clear',0)],[op('pending-remove',0,0)],[op('pending-add',0,-1)]]:
cfg=config(holders=[i%4 for i in range(max(1,count))]);cfg2=config(holders=[(i+1)%4 for i in range(max(1,count))],attributes=1,routes=[1,0],dirty=None)
yield row(attribute_hashes=[0 if collision else i*31 for i in range(max(1,count))],initial=cfg,after=cfg2,setup=[op('pending-add',0,i) for i in reversed(range(count))],ops=[op('refresh')],mutate=mutate,mutations=changes)
for _ in range(500):
count=rng.choice([6,8,16]);nattrs=rng.choice([3,8,24,49])
def states():return [state(key=rng.randrange(count),duration=rng.choice(durations),amplifier=rng.choice(amps),ambient=bool(rng.randrange(2)),visible=bool(rng.randrange(2)),icon=bool(rng.randrange(2)),hidden=rng.randrange(-1,i),factor=rng.choice(floats),previous=rng.choice(floats)) for i in range(count)]
def cfg():return config(states=states(),level=rng.randrange(2),client=bool(rng.randrange(2)),removed=bool(rng.randrange(2)),dirty=rng.choice([False,True,None]),tags=[bool(rng.randrange(2)) for _ in range(3)],attributes=rng.randrange(2),routes=[rng.randrange(2) for _ in range(2)],holders=[rng.randrange(4) for _ in range(nattrs)],passengers=[rng.randrange(3) for _ in range(rng.randrange(6))],connections=[rng.randrange(-1,2) for _ in range(2)],entity=rng.randrange(-2**31,2**31))
def mutation():
k=rng.choice(['put','drop','clear','copy','alias','pending-add','pending-remove','pending-clear'])
return op(k,rng.randrange(2) if k.startswith('pending') else rng.randrange(count),rng.randrange(-1,nattrs) if k.startswith('pending') else rng.randrange(count))
yield row(hashes=[rng.choice([0,0,64,i*31]) for i in range(count)],attribute_hashes=[rng.choice([0,31,64,i*31]) for i in range(nattrs)],initial=cfg(),after=cfg(),setup=[op('put',rng.randrange(count),rng.randrange(count)) for _ in range(count)]+[op('pending-add',rng.randrange(2),rng.randrange(-1,nattrs)) for _ in range(nattrs)],ops=[op(rng.choice(['eligible','notify-add','notify-update','notify-remove','send','refresh','add','force','remove','all','get','blend','copy','alias']),rng.randrange(count),rng.randrange(count),reapply=bool(rng.randrange(2)),list=[rng.randrange(count) for _ in range(rng.randrange(5))],partial=rng.choice(floats)) for _ in range(15)],fail=rng.randrange(-1,30),mutate=rng.randrange(-1,30),mutations=[mutation() for _ in range(3)])
def main():
parser=argparse.ArgumentParser(description=__doc__);parser.add_argument('--prepare',action='store_true');args=parser.parse_args()
build=lab.LOCAL/'rust-effect-notifications-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/VanillaEffectNotificationsOracle.java'
sources=[oracle,Path(__file__),lab.ROOT/'tools/test_rust_parity.py',lab.ROOT/'tools/lab.py',lab.ROOT/'Cargo.toml',lab.ROOT/'Cargo.lock',lab.ROOT/'crates/horizon-core/Cargo.toml',lab.ROOT/'crates/horizon-world/Cargo.toml',*sorted((lab.ROOT/'crates/horizon-core/src').rglob('*.rs')),*sorted((lab.ROOT/'crates/horizon-world/src').rglob('*.rs')),lab.ROOT/'crates/horizon-world/examples/effect_notifications_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.VanillaEffectNotificationsOracle',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','effect_notifications_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'/('effect_notifications_fixture.exe' if os.name=='nt' else 'effect_notifications_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:,} effect notification 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 eligibility and add/update/remove notification bodies, private refreshDirtyAttributes and sendEffectToPassengers, composed with native active-effect storage. Actual effect instances, fastutil pending sets, immutable passenger lists and update/remove packet constructors are used. Covers eligibility tag precedence, dirty writes, captured maps/sets/connections, callback mutation, pending iteration/clear prefixes, packet fields and failures. Registered modifier bodies, onAttributeUpdated consequences and packet transport remain controlled; full entity/player lifecycle and gameplay remain pending.'}
lab.write_json(report_path,report);print(f'Scoped effect notification parity passed: {report_path}')
if __name__=='__main__':main()