Match effect attribute templates and permanent modifier callback ordering
This commit is contained in:
@@ -0,0 +1,304 @@
|
||||
{
|
||||
"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": 3317,
|
||||
"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/VanillaEffectAttributeModifiersOracle.java": "a62ebf408d4a0b2f38a561d39f2c325b226b86516cae3ae4605e1f9a050b3747",
|
||||
"tools/test_effect_attribute_modifiers_parity.py": "e4bc6d1eee3b3a240a447836e8cdaa18c6777160a7b4e9cc163443057dbee787",
|
||||
"tools/test_rust_parity.py": "29d3db08005873528894b45ca10dcd6278753650473b3eb574e53e06ede40ec6",
|
||||
"tools/lab.py": "d00bf107fa01b6c579734a082c3ca4ade3018f0d6851d46844acf17574f66e64",
|
||||
"Cargo.toml": "3f93319c1eb70dbd7ba25cd74ef343fbf35c52c7c4752f955605c58699cbff9d",
|
||||
"Cargo.lock": "6884c7209f007ab9ee742b9c3c73828cfa1c6753d35b5fe25a87cab5955b33f8",
|
||||
"crates/horizon-core/Cargo.toml": "e21b502c949da93cf67826592459b687a8cbf737ef92fee514e3ed2cc7cd27ad",
|
||||
"crates/horizon-world/Cargo.toml": "18bbad377a8047e4d4ecd0fc87d83d46d1c0f1ce2b8c47bbe64633e7b4420b57",
|
||||
"crates/horizon-core/src/bin/density_parity.rs": "e5b1dd33b7d01306c73cf072629b4fb186e9ab87b672a00108999d1a72263fd0",
|
||||
"crates/horizon-core/src/bin/float_log_parity.rs": "4a655fcf8822ee7668c4bd126bc84d0b118864a6fc7f5a6aeee667d8bf2590b1",
|
||||
"crates/horizon-core/src/bin/geometry_parity.rs": "27f6c09fe5018ac3de8e70e0f0bbf78f9abb14f5e4783bcaf46e8ca87b9c6f8a",
|
||||
"crates/horizon-core/src/bin/noise_parity.rs": "c135bfc708d853e7061e81487d2cfe10de64b2f65677d3b4837cbfe0b8341010",
|
||||
"crates/horizon-core/src/bin/parity.rs": "f35d928c66f958de39fac5e7e836a657eed95f83599e84899f7ea7a5802ba481",
|
||||
"crates/horizon-core/src/bin/tick_parity.rs": "a54c164fe4814034d369947a887f5273966f5a878ba849204b80d1c729526a8f",
|
||||
"crates/horizon-core/src/biome/seed_hash.rs": "5afbb4921c750826b395a08bf06f382fc7f01b5759a93de02a2749c523e2a317",
|
||||
"crates/horizon-core/src/biome.rs": "908efa8cd4aa0a7adf1cb5e1a222e3dfb640eed5e9172e0fe7f0d1287afae7e8",
|
||||
"crates/horizon-core/src/carving/ellipsoid.rs": "09232a7588610e8a9176fd9c62d40afb1fb89f2ecab6d555536a1d847b142d86",
|
||||
"crates/horizon-core/src/carving.rs": "0aa3093caa0437868a8c0ed52a6c6e1993bd51c03583c670a2837e749eae5426",
|
||||
"crates/horizon-core/src/density/arithmetic.rs": "372faec96c923e2cf4aa6c66c1ac398e676853274557823850dbc8933dd15ab1",
|
||||
"crates/horizon-core/src/density/beardifier.rs": "bb1052e27dae4a01954ca78d7d5babafef85f3b2c704ccaf1920cb85a098cb50",
|
||||
"crates/horizon-core/src/density/cache.rs": "38d0b6c9dd04ffef979fdad3f29cfebf98d331c8c9d430be5f39aa34f6c30975",
|
||||
"crates/horizon-core/src/density/end_islands.rs": "168b9384237bd2e41d0ae5a74e944da4dc74e493bdec31fb73184c9a49e3de55",
|
||||
"crates/horizon-core/src/density/graph/compiler.rs": "ac67deeb16f16d0859aa20e5a165811a29aca0b910037ee6bf5098211f3a6ede",
|
||||
"crates/horizon-core/src/density/graph/mod.rs": "b776e7da934cb03051a3053d56d461c71e9978e084922fe4194147aa580c07bf",
|
||||
"crates/horizon-core/src/density/graph/seeded.rs": "6bcdcaa3dffcfa9df673bbbea35120057a16ba3d2b506c52709c36cf8976b74a",
|
||||
"crates/horizon-core/src/density/graph/tests.rs": "6bfb8cbff462587dd147b0775ea01d370abbd97544b9bd596c792df1b5f7e634",
|
||||
"crates/horizon-core/src/density/interpolation.rs": "8c890f52b6467f38a94c654986a6a484b8e0b296ab98c398e60a6ce465b5b010",
|
||||
"crates/horizon-core/src/density/interval.rs": "b42629163baa6b599a233afe8596d1c370cd81d015cc6fc36a9663aa47b98424",
|
||||
"crates/horizon-core/src/density/mod.rs": "ca0d77d3282e3746ddcbc92bee97dbf640b9a56214e6ac1537f51f9a5b63d6e7",
|
||||
"crates/horizon-core/src/density/power.rs": "d5b7270a128d4264268cd772109ad16d71a8ec3ff2d3b63b84cb985296d54a91",
|
||||
"crates/horizon-core/src/density/rounding.rs": "71840c5206ff8d218ac29bf6f66915d5212764ba270c675711a866ba5b55a7a6",
|
||||
"crates/horizon-core/src/density/selection.rs": "0dd26af2e85004ec5e053039ad11c384c17c56ace9ab45eb3f5cb3503c0b0330",
|
||||
"crates/horizon-core/src/density/slice.rs": "5d77605573c0deda47778f1f78f7fa3fbf1944d8f5efbcc65cf589181cadf80b",
|
||||
"crates/horizon-core/src/density/spline.rs": "6bb2e40f940fc6544d6fb09cb0b8cac722be03bc663dbdfb0f61faae207400bc",
|
||||
"crates/horizon-core/src/density/surface.rs": "159e0e69cda74f870e80ea64fe523c4397e64af4ccb861bb9fd07b2c3cb91b00",
|
||||
"crates/horizon-core/src/density/terrain_noise.rs": "8d0059d25eb5d248cb97a4c5a2e670d61528928ea151495c004f25adb723fe3e",
|
||||
"crates/horizon-core/src/density/tests.rs": "7817796ee40d0ae241bfbebec6bca190e0a8e7d5a1caa8ddf858c3eaa58b8858",
|
||||
"crates/horizon-core/src/density/world_fields.rs": "c1ce305cb8046b384d90c564cafb24af7365d580389637910f3d8b0cc18eb583",
|
||||
"crates/horizon-core/src/geometry/border.rs": "1386f8080521231d05ba3e84d94a3e1d5218d35ccb3d5e9fe2bbb05a8739e573",
|
||||
"crates/horizon-core/src/geometry/mod.rs": "c650386f9860e44a96c55f18c8f1428ca715fcb0bc5c2434db5b1caa43df27fe",
|
||||
"crates/horizon-core/src/geometry/movement.rs": "bcfb452752940ce53ae6dc267a3c4eb29459046a063c1f884684ab4f4bd0068f",
|
||||
"crates/horizon-core/src/geometry/shape/create.rs": "189812b91d5e0e3ad963afcd1a6e2831b31b8188df91a904ef27061e644806ad",
|
||||
"crates/horizon-core/src/geometry/shape/face.rs": "f243bce81384040a95ce7984b02b5699206c596c537b2713097e86005b4d3a5c",
|
||||
"crates/horizon-core/src/geometry/shape/join.rs": "f1b51887b7bf9482557e3ffa99e136f2e5db07ceeb05fdb116adf45e87129075",
|
||||
"crates/horizon-core/src/geometry/shape.rs": "e8d88a763de1525f77a30b99591641e8a8b80946263acbde035a9bcc356df960",
|
||||
"crates/horizon-core/src/geometry/sweep.rs": "abd6009c61555161ef2f18b431b2aa384b126c0871cae8b96a62358498a56a1d",
|
||||
"crates/horizon-core/src/geometry/traversal.rs": "77a3da570a0b85582b669cf4d841cba4b06327006dda26d4a26df4e8ce05bae4",
|
||||
"crates/horizon-core/src/geometry/trig.rs": "05cef9341a298a474221a0556a11ddf47650393ea7a97b4b2022a4da6628fe37",
|
||||
"crates/horizon-core/src/lib.rs": "4255e16837fde42a52ba118f60581d197c571bb530b60d635e506184ebf4db0b",
|
||||
"crates/horizon-core/src/noise/mod.rs": "1dab5a713abf702402e69ace1bf9e476bba544b0ede5a61c3c3afc60af0a03f3",
|
||||
"crates/horizon-core/src/noise/normal.rs": "e9e7a46472a54a02c98c46da819842090567153b32fe0c84249009ef19af7aad",
|
||||
"crates/horizon-core/src/noise/perlin.rs": "e73815447432eab7542f67366cb3f1b368d164e33f6c9cdbd045ac8e4db0932a",
|
||||
"crates/horizon-core/src/noise/simplex.rs": "6c0dbc15b2b7510195dd28fceca01dcaac1847ccc4a84d299c351589c642fe7a",
|
||||
"crates/horizon-core/src/noise/stack.rs": "7f6ef0e7d920139ef1a6ad3ec5640f92765715f86dcc5ec29f1a149984f45fa3",
|
||||
"crates/horizon-core/src/noise/volume.rs": "3f05d2b1780acfdfee60037677d309937a5033b45ef428fc2d540942d4b56b43",
|
||||
"crates/horizon-core/src/position/iter.rs": "32d2d8e74821cc4e49e3354202d35d3120e4415bfdda9375a73f2137ef651129",
|
||||
"crates/horizon-core/src/position.rs": "e10101088e6902386b9b6d27773d44312f1ff72bebb19e57967a54945dde9dcd",
|
||||
"crates/horizon-core/src/random/hash.rs": "0f88a127a1ba2705d3d6c0ebd009324d6ba86ffa2d27edf2d2a84765e690e4a3",
|
||||
"crates/horizon-core/src/random/positional.rs": "35494745e5d4cf89e282c3ae0ee245e9f2640ddbbc03bc0055df742006a9c862",
|
||||
"crates/horizon-core/src/random/worldgen.rs": "9f07ff33bcc038d34e27aa5465753bb21df18f2975e43cae201f709bc186945a",
|
||||
"crates/horizon-core/src/random.rs": "ddb95d3a83e28ad605ff1f49f5df270cc543ca5ef1130e37d989dc74c8d861e6",
|
||||
"crates/horizon-core/src/storage.rs": "f6c32201c64aefde06e57a2c767be46918df613548a322801546b133799cc33d",
|
||||
"crates/horizon-core/src/text.rs": "e6553ddb5db5039e8dc4614b6a6dc9f35a26dc3d1673b68a04e72e7983b67abb",
|
||||
"crates/horizon-core/src/ticks/generation.rs": "be1237e790693f633a61714ddae68c6506a777588a8a3ab5efbe53a35774dbd2",
|
||||
"crates/horizon-core/src/ticks/heap.rs": "bf28afab8af77c3d243aa96cd8a1fb8d934140a4dbd07dbfdbf4e7908bb4b845",
|
||||
"crates/horizon-core/src/ticks/index.rs": "1f1a6a568c2a30eafecb020ca4d5f5fc414860de85b8b381fc419a2e3e65c24a",
|
||||
"crates/horizon-core/src/ticks/mod.rs": "01634630668172516b8b79d0907bad47f6e2c543a6d499c2fa4b201823b375cb",
|
||||
"crates/horizon-world/src/active_effect_tick.rs": "88f19cfc3532c47a58c646bb3e9d99ff239a900752302b6a444dda3013e95cbf",
|
||||
"crates/horizon-world/src/active_effects.rs": "daaf352a69511a7d69fb329997f783b86585e486e0379c84ab0a55641771646c",
|
||||
"crates/horizon-world/src/attribute_health.rs": "6135beb1baf9a55745ab95955bcd05e585e4042a6cffda0a14c13f05593e7186",
|
||||
"crates/horizon-world/src/attribute_updates.rs": "4cb322d75a50d222298994607097ba7f7e6674021128fa02a98a74c057ad05f1",
|
||||
"crates/horizon-world/src/attributes/entity.rs": "56fb96b1beb9caaa769369bf7cddf522d2f2e84e241c213b51ae8d39d31a9f13",
|
||||
"crates/horizon-world/src/attributes/mod.rs": "3d05ce2ff10c1b68540e3c87bbd2ac65c56a2e7b7127a7865b60890a2e9a690d",
|
||||
"crates/horizon-world/src/attributes/operation_map.rs": "fc5bc8921d78ea6721a2eaa915e67483e2a33683927467bdba24f0492356ad97",
|
||||
"crates/horizon-world/src/bin/worldgen_parity.rs": "3143ceb9b56eecfd7667e5feb5f9f0cd13cfb8754b38d4ea338dbba97f2819da",
|
||||
"crates/horizon-world/src/block_collision.rs": "39207247f2f6dd3ddc77640229044246f91b32054563f88a435f1bd92ad2092c",
|
||||
"crates/horizon-world/src/block_collision_scan.rs": "20802862364c110ff85eeaf48606596a8468b83768dda46b412d337958790b41",
|
||||
"crates/horizon-world/src/block_entities.rs": "a9deae92e6e5a1fcf4f0476c33ea4d170ddad6fd4b3303e1bb66d933973d5e06",
|
||||
"crates/horizon-world/src/block_entity_push.rs": "12ee91bf604946234b24a0da4333f07d1830ab9743a422d41c8d0afac7d49cfd",
|
||||
"crates/horizon-world/src/block_landing.rs": "33083fe39266fbefec1b3ea1a19f2afc609791d189da247c895e116ad6eb266a",
|
||||
"crates/horizon-world/src/block_placement.rs": "524f4892d50bab77b7d89d19f7b048ffe746996f2ed7f376db2c6ab00ed75c24",
|
||||
"crates/horizon-world/src/block_properties.rs": "b0bd8a983336c48e488a20caa77dd5c39b1a74bbd14ebc03b3b64556a1c864fc",
|
||||
"crates/horizon-world/src/block_render.rs": "8df52ee11aef406a1571198a64048062a0ef6864e0058d9293a57f64b73c916a",
|
||||
"crates/horizon-world/src/block_support.rs": "ade4fbd47284b450dabdd682dc86138b2ac05a0fd379525d7110648908245476",
|
||||
"crates/horizon-world/src/block_survival.rs": "28ce64375e09819a4f13da9cfbe37c08b5ad0daf01b289c1c360b4567e50e811",
|
||||
"crates/horizon-world/src/blocking_item_damage.rs": "afea3cb5761e8b82ee4d9bb4ef3c6fd10182a324dbaec99e6c9ebfac255dda09",
|
||||
"crates/horizon-world/src/blocking_rules.rs": "83c5e2105cec56a90182ce582e1c67ade2b189be6de1a34d08035000cc07c492",
|
||||
"crates/horizon-world/src/bundle_weight.rs": "a0e7b0587ab5d46cd55588f25d55a088b7a4725099c13ba6df146957be36014c",
|
||||
"crates/horizon-world/src/chunk.rs": "795895284d6e615bcb2082fceacdfe7025c152fb2934bb43f598f17a00ab46f1",
|
||||
"crates/horizon-world/src/chunk_status.rs": "dbb8583cfa775203c544d24d69e03720385428f15a267a561356c21f5025e9c7",
|
||||
"crates/horizon-world/src/climbing.rs": "6cd2f919872ff0b726effddaa57eccb805b8eae43f9d01144716010853891879",
|
||||
"crates/horizon-world/src/collision.rs": "a749794d5c27ccd6c0d964ad02f9dc31d49d9f4fe4b7366cd4bd8155e681b00b",
|
||||
"crates/horizon-world/src/collision_context.rs": "2176c7034e07f547510a8327cdad9d3b63f26e4754032b76b7d5fe13166c333c",
|
||||
"crates/horizon-world/src/combat_rules.rs": "01131f70d1acdd96a910e7f96deaa12e51a1e3bb9208085f04e3ec7b657d3cd4",
|
||||
"crates/horizon-world/src/component_durability.rs": "37a46e184f95bc5027e2b9af72ff52f729ad03601d08e1827f181fca383a35af",
|
||||
"crates/horizon-world/src/component_patch.rs": "f6f8992f241dfb25e012a06a7ba3fecab8d958686dddd096595a4806e56f0ab3",
|
||||
"crates/horizon-world/src/compression.rs": "4db73e6ca9a0d552d415ce60e7e88b89c9cf2b7d060a669ff8328f518316e101",
|
||||
"crates/horizon-world/src/consumable.rs": "ba3575cb85866cb940f33a1ac86455d71cf0cc6395b848588a8ee1fe33f5c326",
|
||||
"crates/horizon-world/src/consume_effect.rs": "03f2aac6879ce133347d42f6f6667d62c06d956ff9faac5dc14182a788b6a236",
|
||||
"crates/horizon-world/src/damage_immunity.rs": "fd39c41124d694e755cf7faadcf93c636df4042c78ec345092a856b5f952dbcf",
|
||||
"crates/horizon-world/src/damage_reduction.rs": "f5a599ab11e077f526237018aeda803ea3c420c5b35c78ae9d85e32267e23bf3",
|
||||
"crates/horizon-world/src/damage_source.rs": "24945e397a926d34695652980923609e4723e02fe6b6e56d4aaf673e45d759f7",
|
||||
"crates/horizon-world/src/dynamic_support.rs": "5996bc1c436fb6215d8fc3d0bb4de5dd9b966d842d432580d624195771697fb8",
|
||||
"crates/horizon-world/src/edge_backoff.rs": "5259d7aa44e64f88351d1556c44dd0edcad3b400a099c659755792efba7b5773",
|
||||
"crates/horizon-world/src/effect_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": "0c0f3338bcabc7fb568c4588736c02dd6d3c61fe7f871a427986830b625a3c90",
|
||||
"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_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/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_attribute_modifiers_fixture.rs": "b5dd3fa298ee8241415ae08ef3068decf448101d8e02672c67d1e61fbc73a2c5"
|
||||
},
|
||||
"fixtures_sha256": {
|
||||
".local/rust-effect-attribute-modifiers-parity/inputs.txt": "af718dd8e32c708bfe4c8ccdba882f174b0f30a90a9074fd715253ab73a28f06"
|
||||
},
|
||||
"rust_output_sha256": "ae77cf7c813f7be5e9e7d544b0cb4b34eb39bfa79bb185e9b2473ab9fcbb4a82",
|
||||
"references": [
|
||||
{
|
||||
"side": "client",
|
||||
"jar_sha256": "4508d006323f24fa02876310c192d739af56516eb259000ac50f0909a68c9a2d",
|
||||
"output_sha256": "ae77cf7c813f7be5e9e7d544b0cb4b34eb39bfa79bb185e9b2473ab9fcbb4a82",
|
||||
"matched_output_lines": 1421730
|
||||
},
|
||||
{
|
||||
"side": "server",
|
||||
"jar_sha256": "a362163eec5d1612d520772bc16e5b39c09e3b234fdc045f56bf544284ee8ae6",
|
||||
"output_sha256": "ae77cf7c813f7be5e9e7d544b0cb4b34eb39bfa79bb185e9b2473ab9fcbb4a82",
|
||||
"matched_output_lines": 1421730
|
||||
}
|
||||
],
|
||||
"scope": "Actual MobEffect addAttributeModifier/createModifiers/addAttributeModifiers/removeAttributeModifiers with native fastutil definition map and AttributeInstance permanent modifier storage, cached values and dirty observers. Tests wrapping amplifier addition, double scaling, fresh modifier identity, map entry rereads after virtual getInstance/remove callbacks, null/missing instance skipping, map growth/shrink/replacement and partial failures. AttributeMap instance lookup and dirty receiver are controlled; callback mutation touches only definitions/routes. Registry effect definitions, pending attribute set ownership, entity notifications, full gameplay and playable platforms remain pending."
|
||||
}
|
||||
@@ -0,0 +1,348 @@
|
||||
use horizon_world::{
|
||||
attributes::{AttributeError, Definition, Instance, Modifier, Observer, Operation},
|
||||
effect_attribute_modifiers::{self as effects, Definitions, Error, ModifierWorld, Template},
|
||||
object_open_set::{CursorError, ObjectKey},
|
||||
};
|
||||
use serde_json::Value;
|
||||
use std::{
|
||||
fs,
|
||||
io::{self, Write},
|
||||
path::Path,
|
||||
};
|
||||
#[derive(Clone, PartialEq, Eq)]
|
||||
struct Key {
|
||||
id: usize,
|
||||
hash: i32,
|
||||
}
|
||||
impl ObjectKey for Key {
|
||||
fn object_hash(&self) -> i32 {
|
||||
self.hash
|
||||
}
|
||||
}
|
||||
#[derive(Debug)]
|
||||
enum Stop {
|
||||
Callback,
|
||||
Duplicate,
|
||||
}
|
||||
fn bits(v: f64) -> String {
|
||||
format!(
|
||||
"{:016x}",
|
||||
if v.is_nan() {
|
||||
f64::NAN.to_bits()
|
||||
} else {
|
||||
v.to_bits()
|
||||
}
|
||||
)
|
||||
}
|
||||
fn number(v: &Value) -> f64 {
|
||||
f64::from_bits(u64::from_str_radix(v.as_str().unwrap(), 16).unwrap())
|
||||
}
|
||||
fn desc(m: &Modifier) -> String {
|
||||
format!("{}:{}:{}", m.id(), bits(m.amount()), m.operation() as u8)
|
||||
}
|
||||
fn key_desc(k: Option<&Key>) -> String {
|
||||
k.map_or("null".into(), |k| k.id.to_string())
|
||||
}
|
||||
struct Shared {
|
||||
keys: Vec<Key>,
|
||||
templates: Vec<Template>,
|
||||
routes: Vec<i32>,
|
||||
pool: Vec<Modifier>,
|
||||
calls: i64,
|
||||
changed: bool,
|
||||
op: Value,
|
||||
logs: Vec<String>,
|
||||
}
|
||||
impl Shared {
|
||||
fn key(&self, op: &Value) -> Option<Key> {
|
||||
let k = op["key"].as_i64().unwrap();
|
||||
(k >= 0).then(|| self.keys[k as usize].clone())
|
||||
}
|
||||
fn definitions(&mut self, defs: &Definitions<Key>) {
|
||||
self.logs.push(format!(
|
||||
"definitions {} | {}",
|
||||
defs.len(),
|
||||
defs.entries()
|
||||
.iter()
|
||||
.map(|(k, v)| {
|
||||
let v = v.as_ref().map_or("null".into(), |t| {
|
||||
format!("{}:{}:{}", t.id(), bits(t.amount()), t.operation() as u8)
|
||||
});
|
||||
format!("{}={v}", key_desc(k.as_ref()))
|
||||
})
|
||||
.collect::<Vec<_>>()
|
||||
.join(",")
|
||||
));
|
||||
}
|
||||
fn raw(&mut self, defs: &mut Definitions<Key>, op: &Value) {
|
||||
match op["op"].as_str().unwrap() {
|
||||
"define" => effects::add_definition(
|
||||
defs,
|
||||
self.key(op),
|
||||
self.templates[op["template"].as_u64().unwrap() as usize].clone(),
|
||||
),
|
||||
"null" => {
|
||||
defs.put(self.key(op), None);
|
||||
}
|
||||
"drop" => {
|
||||
defs.remove(self.key(op).as_ref());
|
||||
}
|
||||
"clear" => defs.clear(),
|
||||
"route" => {
|
||||
let k = self.key(op).map_or(self.keys.len(), |k| k.id);
|
||||
self.routes[k] = op["target"].as_i64().unwrap() as i32;
|
||||
}
|
||||
_ => panic!("raw operation"),
|
||||
}
|
||||
}
|
||||
fn event(&mut self, defs: &mut Definitions<Key>, name: String) -> Result<(), Stop> {
|
||||
let call = self.calls;
|
||||
self.calls += 1;
|
||||
self.logs.push(format!("event {call} {name}"));
|
||||
self.definitions(defs);
|
||||
if call == self.op["fail"].as_i64().unwrap() {
|
||||
return Err(Stop::Callback);
|
||||
}
|
||||
if call == self.op["mutate"].as_i64().unwrap() {
|
||||
self.changed = true;
|
||||
for action in self.op["mutations"].as_array().unwrap().clone() {
|
||||
self.raw(defs, &action);
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
fn refs(&self, mods: &[Modifier]) -> String {
|
||||
mods.iter()
|
||||
.map(|m| {
|
||||
self.pool
|
||||
.iter()
|
||||
.position(|p| p.same_identity(m))
|
||||
.unwrap()
|
||||
.to_string()
|
||||
})
|
||||
.collect::<Vec<_>>()
|
||||
.join(",")
|
||||
}
|
||||
fn state(&mut self, id: usize, a: &Instance) {
|
||||
self.logs.push(format!(
|
||||
"instance {id} {} {} {} | {} | {}",
|
||||
bits(a.base_value()),
|
||||
a.is_dirty(),
|
||||
bits(a.cached_value()),
|
||||
self.refs(a.modifiers()),
|
||||
self.refs(a.permanent_modifiers())
|
||||
));
|
||||
}
|
||||
fn modifier(&mut self, m: Modifier) -> usize {
|
||||
let id = self.pool.len();
|
||||
self.pool.push(m);
|
||||
id
|
||||
}
|
||||
}
|
||||
struct Dirty<'a> {
|
||||
shared: &'a mut Shared,
|
||||
definitions: &'a mut Definitions<Key>,
|
||||
id: usize,
|
||||
}
|
||||
impl Observer for Dirty<'_> {
|
||||
type Error = Stop;
|
||||
fn dirty(&mut self, a: &mut Instance) -> Result<(), Stop> {
|
||||
self.shared.state(self.id, a);
|
||||
self.shared
|
||||
.event(self.definitions, format!("dirty {}", self.id))?;
|
||||
if self.shared.op["read"].as_bool().unwrap() {
|
||||
self.shared
|
||||
.logs
|
||||
.push(format!("observed {}", bits(a.value())));
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
fn attribute_error(e: AttributeError<Stop>) -> Stop {
|
||||
match e {
|
||||
AttributeError::DuplicateModifier(_) => Stop::Duplicate,
|
||||
AttributeError::Observer(e) => e,
|
||||
}
|
||||
}
|
||||
struct World {
|
||||
shared: Shared,
|
||||
instances: Vec<Instance>,
|
||||
}
|
||||
impl ModifierWorld<Key> for World {
|
||||
type Instance = usize;
|
||||
type Error = Stop;
|
||||
fn get_instance(
|
||||
&mut self,
|
||||
defs: &mut Definitions<Key>,
|
||||
key: Option<Key>,
|
||||
) -> Result<Option<usize>, Stop> {
|
||||
self.shared
|
||||
.event(defs, format!("get {}", key_desc(key.as_ref())))?;
|
||||
let i = key.as_ref().map_or(self.shared.keys.len(), |k| k.id);
|
||||
let id = self.shared.routes[i];
|
||||
Ok((id >= 0).then_some(id as usize))
|
||||
}
|
||||
fn remove_modifier(
|
||||
&mut self,
|
||||
defs: &mut Definitions<Key>,
|
||||
id: &usize,
|
||||
modifier: &str,
|
||||
) -> Result<(), Stop> {
|
||||
self.shared.event(defs, format!("remove {id} {modifier}"))?;
|
||||
let mut cb = Dirty {
|
||||
shared: &mut self.shared,
|
||||
definitions: defs,
|
||||
id: *id,
|
||||
};
|
||||
self.instances[*id]
|
||||
.remove(modifier, &mut cb)
|
||||
.map_err(attribute_error)?;
|
||||
Ok(())
|
||||
}
|
||||
fn add_permanent(
|
||||
&mut self,
|
||||
defs: &mut Definitions<Key>,
|
||||
id: &usize,
|
||||
modifier: Modifier,
|
||||
) -> Result<(), Stop> {
|
||||
let m = self.shared.modifier(modifier.clone());
|
||||
self.shared
|
||||
.event(defs, format!("permanent {id} {m} {}", desc(&modifier)))?;
|
||||
let mut cb = Dirty {
|
||||
shared: &mut self.shared,
|
||||
definitions: defs,
|
||||
id: *id,
|
||||
};
|
||||
self.instances[*id]
|
||||
.add_permanent(modifier, &mut cb)
|
||||
.map_err(attribute_error)
|
||||
}
|
||||
}
|
||||
fn error(e: Error<Stop>) -> &'static str {
|
||||
match e {
|
||||
Error::MissingTemplate | Error::Cursor(CursorError::MissingWrapped) => "null",
|
||||
Error::Cursor(CursorError::IndexOutOfBounds) => "bounds",
|
||||
Error::Cursor(CursorError::NoSuchElement) => "empty",
|
||||
Error::Cursor(CursorError::NoCurrentEntry) => "current",
|
||||
Error::World(Stop::Callback) => "callback",
|
||||
Error::World(Stop::Duplicate) => "duplicate",
|
||||
}
|
||||
}
|
||||
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
let arg = std::env::args().nth(1).unwrap();
|
||||
let dir = Path::new(&arg);
|
||||
let mut out = io::BufWriter::new(fs::File::create(dir.join("rust.txt"))?);
|
||||
for line in fs::read_to_string(dir.join("inputs.txt"))?.lines() {
|
||||
let f: Value = serde_json::from_str(line)?;
|
||||
let keys = f["hashes"]
|
||||
.as_array()
|
||||
.unwrap()
|
||||
.iter()
|
||||
.enumerate()
|
||||
.map(|(id, h)| Key {
|
||||
id,
|
||||
hash: h.as_i64().unwrap() as i32,
|
||||
})
|
||||
.collect();
|
||||
let templates = f["templates"]
|
||||
.as_array()
|
||||
.unwrap()
|
||||
.iter()
|
||||
.map(|v| {
|
||||
Template::new(
|
||||
v["id"].as_str().unwrap(),
|
||||
number(&v["amount"]),
|
||||
Operation::ALL[v["operation"].as_u64().unwrap() as usize],
|
||||
)
|
||||
})
|
||||
.collect::<io::Result<Vec<_>>>()?;
|
||||
let routes = f["routes"]
|
||||
.as_array()
|
||||
.unwrap()
|
||||
.iter()
|
||||
.map(|i| i.as_i64().unwrap() as i32)
|
||||
.collect();
|
||||
let instances = f["defaults"]
|
||||
.as_array()
|
||||
.unwrap()
|
||||
.iter()
|
||||
.map(|v| Instance::new(Definition::basic(number(v))))
|
||||
.collect();
|
||||
let shared = Shared {
|
||||
keys,
|
||||
templates,
|
||||
routes,
|
||||
pool: Vec::new(),
|
||||
calls: 0,
|
||||
changed: false,
|
||||
op: Value::Null,
|
||||
logs: Vec::new(),
|
||||
};
|
||||
let mut world = World { shared, instances };
|
||||
let mut defs = Definitions::default();
|
||||
for op in f["setup"].as_array().unwrap() {
|
||||
world.shared.raw(&mut defs, op);
|
||||
}
|
||||
for op in f["ops"].as_array().unwrap() {
|
||||
world.shared.op = op.clone();
|
||||
world.shared.calls = 0;
|
||||
world.shared.changed = false;
|
||||
let amp = op["amplifier"].as_i64().unwrap() as i32;
|
||||
let result = match op["op"].as_str().unwrap() {
|
||||
"add" => effects::add_modifiers(&mut defs, &mut world, amp),
|
||||
"remove" => effects::remove_modifiers(&mut defs, &mut world),
|
||||
"create" => effects::create_modifiers(&mut defs, amp, |defs, key, modifier| {
|
||||
let id = world.shared.modifier(modifier.clone());
|
||||
world.shared.event(
|
||||
defs,
|
||||
format!(
|
||||
"consume {} {id} {}",
|
||||
key_desc(key.as_ref()),
|
||||
desc(&modifier)
|
||||
),
|
||||
)
|
||||
}),
|
||||
"seed" => {
|
||||
let template =
|
||||
&world.shared.templates[op["template"].as_u64().unwrap() as usize];
|
||||
let modifier = template.create(amp);
|
||||
world
|
||||
.add_permanent(
|
||||
&mut defs,
|
||||
&(op["target"].as_u64().unwrap() as usize),
|
||||
modifier,
|
||||
)
|
||||
.map_err(Error::World)
|
||||
}
|
||||
"value" => {
|
||||
let id = op["target"].as_u64().unwrap() as usize;
|
||||
world
|
||||
.shared
|
||||
.logs
|
||||
.push(format!("value {id} {}", bits(world.instances[id].value())));
|
||||
Ok(())
|
||||
}
|
||||
_ => {
|
||||
world.shared.raw(&mut defs, op);
|
||||
Ok(())
|
||||
}
|
||||
};
|
||||
world
|
||||
.shared
|
||||
.logs
|
||||
.push(format!("result {}", result.map_or_else(error, |()| "ok")));
|
||||
world.shared.logs.push(format!(
|
||||
"calls {} {}",
|
||||
world.shared.calls, world.shared.changed
|
||||
));
|
||||
world.shared.definitions(&defs);
|
||||
for (id, a) in world.instances.iter().enumerate() {
|
||||
world.shared.state(id, a);
|
||||
}
|
||||
for log in world.shared.logs.drain(..) {
|
||||
writeln!(out, "{log}")?;
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
@@ -0,0 +1,139 @@
|
||||
//! MobEffect attribute templates and native modifier traversal/evaluation order.
|
||||
use crate::{
|
||||
attributes::{Modifier, Operation},
|
||||
object_open_map::{ObjectOpenMap, TraversalError},
|
||||
object_open_set::{CursorError, ObjectKey},
|
||||
};
|
||||
use std::io;
|
||||
|
||||
/// A validated modifier definition. Each creation produces a fresh identity.
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct Template(Modifier);
|
||||
impl Template {
|
||||
pub fn new(id: &str, amount: f64, operation: Operation) -> io::Result<Self> {
|
||||
Modifier::new(id, amount, operation).map(Self)
|
||||
}
|
||||
pub fn id(&self) -> &str {
|
||||
self.0.id()
|
||||
}
|
||||
pub fn amount(&self) -> f64 {
|
||||
self.0.amount()
|
||||
}
|
||||
pub fn operation(&self) -> Operation {
|
||||
self.0.operation()
|
||||
}
|
||||
pub fn create(&self, amplifier: i32) -> Modifier {
|
||||
Modifier::new(
|
||||
self.id(),
|
||||
self.amount() * f64::from(amplifier.wrapping_add(1)),
|
||||
self.operation(),
|
||||
)
|
||||
.expect("template identifier already validated")
|
||||
}
|
||||
}
|
||||
pub type Definitions<H> = ObjectOpenMap<H, Template>;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum Error<E> {
|
||||
Cursor(CursorError),
|
||||
MissingTemplate,
|
||||
World(E),
|
||||
}
|
||||
impl<E> From<CursorError> for Error<E> {
|
||||
fn from(value: CursorError) -> Self {
|
||||
Self::Cursor(value)
|
||||
}
|
||||
}
|
||||
/// Virtual attribute methods may mutate the effect's definitions. Their
|
||||
/// captured attribute-map receiver belongs to this world for the whole call.
|
||||
pub trait ModifierWorld<H> {
|
||||
type Instance;
|
||||
type Error;
|
||||
fn get_instance(
|
||||
&mut self,
|
||||
definitions: &mut Definitions<H>,
|
||||
attribute: Option<H>,
|
||||
) -> Result<Option<Self::Instance>, Self::Error>;
|
||||
fn remove_modifier(
|
||||
&mut self,
|
||||
definitions: &mut Definitions<H>,
|
||||
instance: &Self::Instance,
|
||||
id: &str,
|
||||
) -> Result<(), Self::Error>;
|
||||
fn add_permanent(
|
||||
&mut self,
|
||||
definitions: &mut Definitions<H>,
|
||||
instance: &Self::Instance,
|
||||
modifier: Modifier,
|
||||
) -> Result<(), Self::Error>;
|
||||
}
|
||||
pub fn add_definition<H: ObjectKey + Clone>(
|
||||
definitions: &mut Definitions<H>,
|
||||
attribute: Option<H>,
|
||||
template: Template,
|
||||
) {
|
||||
definitions.put(attribute, Some(template));
|
||||
}
|
||||
pub fn remove_modifiers<H: ObjectKey + Clone, W: ModifierWorld<H>>(
|
||||
definitions: &mut Definitions<H>,
|
||||
world: &mut W,
|
||||
) -> Result<(), Error<W::Error>> {
|
||||
let mut cursor = definitions.cursor();
|
||||
while cursor.has_next() {
|
||||
let entry = definitions.next_entry(&mut cursor)?;
|
||||
let key = definitions.entry_key(&entry)?;
|
||||
if let Some(instance) = world.get_instance(definitions, key).map_err(Error::World)? {
|
||||
let template = definitions
|
||||
.entry_value(&entry)?
|
||||
.ok_or(Error::MissingTemplate)?;
|
||||
world
|
||||
.remove_modifier(definitions, &instance, template.id())
|
||||
.map_err(Error::World)?;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
pub fn add_modifiers<H: ObjectKey + Clone, W: ModifierWorld<H>>(
|
||||
definitions: &mut Definitions<H>,
|
||||
world: &mut W,
|
||||
amplifier: i32,
|
||||
) -> Result<(), Error<W::Error>> {
|
||||
let mut cursor = definitions.cursor();
|
||||
while cursor.has_next() {
|
||||
let entry = definitions.next_entry(&mut cursor)?;
|
||||
let key = definitions.entry_key(&entry)?;
|
||||
if let Some(instance) = world.get_instance(definitions, key).map_err(Error::World)? {
|
||||
let template = definitions
|
||||
.entry_value(&entry)?
|
||||
.ok_or(Error::MissingTemplate)?;
|
||||
world
|
||||
.remove_modifier(definitions, &instance, template.id())
|
||||
.map_err(Error::World)?;
|
||||
// removeModifier can invoke dirty callbacks that replace/rehash the
|
||||
// definitions. The indexed entry is read again afterwards.
|
||||
let template = definitions
|
||||
.entry_value(&entry)?
|
||||
.ok_or(Error::MissingTemplate)?;
|
||||
let modifier = template.create(amplifier);
|
||||
world
|
||||
.add_permanent(definitions, &instance, modifier)
|
||||
.map_err(Error::World)?;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
pub fn create_modifiers<H: ObjectKey + Clone, E>(
|
||||
definitions: &mut Definitions<H>,
|
||||
amplifier: i32,
|
||||
mut consumer: impl FnMut(&mut Definitions<H>, Option<H>, Modifier) -> Result<(), E>,
|
||||
) -> Result<(), Error<E>> {
|
||||
definitions
|
||||
.for_each(|definitions, key, template| {
|
||||
let modifier = template.ok_or(Error::MissingTemplate)?.create(amplifier);
|
||||
consumer(definitions, key, modifier).map_err(Error::World)
|
||||
})
|
||||
.map_err(|error| match error {
|
||||
TraversalError::Cursor(e) => Error::Cursor(e),
|
||||
TraversalError::Callback(e) => e,
|
||||
})
|
||||
}
|
||||
@@ -2,6 +2,7 @@
|
||||
#![forbid(unsafe_code)]
|
||||
|
||||
pub mod object_open_map;
|
||||
pub mod effect_attribute_modifiers;
|
||||
pub mod object_open_set;
|
||||
pub mod effect_notifications;
|
||||
pub mod active_effects;
|
||||
|
||||
@@ -1114,3 +1114,23 @@ failure prefixes. Alternate constructors, custom default return values,
|
||||
compute/merge/bulk APIs and external structural mutation combined with iterator
|
||||
removal remain outside this scope. Effect-modifier and full gameplay integration
|
||||
remain unfinished.
|
||||
|
||||
## Effect attribute templates and permanent modifiers
|
||||
|
||||
Effect modifier definitions now scale their amount with vanilla's wrapping
|
||||
amplifier addition and create a fresh modifier identity on each application.
|
||||
Application looks up the attribute instance, removes the current definition's
|
||||
modifier, then reads the indexed definition again before creating and adding a
|
||||
permanent modifier. This preserves callback changes between those operations.
|
||||
Missing attribute instances skip template access, and failures retain preceding
|
||||
map and attribute writes.
|
||||
|
||||
The [modifier comparison](../benchmarks/rust-effect-attribute-modifiers-2026-10-07/scoped-parity.json)
|
||||
matches **1,421,730 lines per reference JAR** across **3,317 fixtures**. It executes
|
||||
native effect modifier methods with real attribute instances, including cached
|
||||
values, permanent membership, dirty observers, nonfinite arithmetic, identity,
|
||||
map rehashes and callback failures. Library and fixture Clippy pass. The fixture
|
||||
asserts actual holder hashes when constructing collision cases. Attribute-map
|
||||
lookup and dirty callback receivers remain controlled. Registered effect
|
||||
definitions, pending queue ownership, entity bindings and complete gameplay/
|
||||
platform builds remain unfinished.
|
||||
|
||||
+165
@@ -0,0 +1,165 @@
|
||||
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.resources.Identifier;
|
||||
import net.minecraft.world.effect.*;
|
||||
import net.minecraft.world.entity.ai.attributes.*;
|
||||
|
||||
/** Native MobEffect modifier methods and AttributeInstance storage/callbacks. */
|
||||
public final class VanillaEffectAttributeModifiersOracle {
|
||||
static Field definitions, dirty, cached, templateId, templateAmount, templateOperation;
|
||||
static Field field(Class<?> c, String name) throws Exception {
|
||||
var f = c.getDeclaredField(name); f.setAccessible(true); return f;
|
||||
}
|
||||
static double number(JsonElement v) { return Double.longBitsToDouble(Long.parseUnsignedLong(v.getAsString(), 16)); }
|
||||
static String bits(double v) { return String.format("%016x", Double.doubleToLongBits(v)); }
|
||||
static String desc(AttributeModifier m) { return m.id() + ":" + bits(m.amount()) + ":" + m.operation().ordinal(); }
|
||||
static final class Stop extends RuntimeException {}
|
||||
static final class Basic extends Attribute {
|
||||
int hash;
|
||||
Basic(double value, int hash) { super("horizon.fixture", value); this.hash = hash; }
|
||||
@Override public int hashCode() { return hash; }
|
||||
}
|
||||
static final class Effect extends MobEffect { Effect() { super(MobEffectCategory.BENEFICIAL, 0); } }
|
||||
record Template(Identifier id, double amount, AttributeModifier.Operation operation) {
|
||||
AttributeModifier create(int amplifier) { return new AttributeModifier(id, amount * (double)(amplifier + 1), operation); }
|
||||
}
|
||||
static final class Data {
|
||||
List<Holder<Attribute>> keys = new ArrayList<>();
|
||||
List<Template> templates = new ArrayList<>();
|
||||
List<AttributeModifier> pool = new ArrayList<>();
|
||||
int[] routes; Attr[] instances; Attrs attributes; Effect effect = new Effect();
|
||||
Map<Holder<Attribute>, Object> defs; JsonObject op; int calls; boolean changed; PrintWriter out;
|
||||
Holder<Attribute> key(JsonObject op) { int k = op.get("key").getAsInt(); return k < 0 ? null : keys.get(k); }
|
||||
String keyDesc(Holder<Attribute> key) { return key == null ? "null" : Integer.toString(keys.indexOf(key)); }
|
||||
int modifier(AttributeModifier m) { pool.add(m); return pool.size() - 1; }
|
||||
String refs(Collection<AttributeModifier> mods) {
|
||||
var out = new StringJoiner(",");
|
||||
for (var m : mods) {
|
||||
int id = -1; for (int i = 0; i < pool.size(); i++) if (pool.get(i) == m) { id = i; break; }
|
||||
if (id < 0) throw new AssertionError("unknown modifier");
|
||||
out.add(Integer.toString(id));
|
||||
}
|
||||
return out.toString();
|
||||
}
|
||||
void definitions() {
|
||||
var result = new StringJoiner(",");
|
||||
defs.forEach((key, t) -> {
|
||||
try { result.add(keyDesc(key) + "=" + (t == null ? "null" : templateId.get(t) + ":" + bits(templateAmount.getDouble(t)) + ":" + ((AttributeModifier.Operation)templateOperation.get(t)).ordinal())); }
|
||||
catch (IllegalAccessException e) { throw new AssertionError(e); }
|
||||
});
|
||||
out.println("definitions " + defs.size() + " | " + result);
|
||||
}
|
||||
void state(int id, AttributeInstance a) {
|
||||
try { out.println("instance " + id + " " + bits(a.getBaseValue()) + " " + dirty.getBoolean(a) + " " + bits(cached.getDouble(a)) + " | " + refs(a.getModifiers()) + " | " + refs(a.getPermanentModifiers())); }
|
||||
catch (IllegalAccessException e) { throw new AssertionError(e); }
|
||||
}
|
||||
void raw(JsonObject op) {
|
||||
switch (op.get("op").getAsString()) {
|
||||
case "define" -> {
|
||||
var t = templates.get(op.get("template").getAsInt());
|
||||
if (effect.addAttributeModifier(key(op), t.id(), t.amount(), t.operation()) != effect) throw new AssertionError();
|
||||
}
|
||||
case "null" -> defs.put(key(op), null);
|
||||
case "drop" -> defs.remove(key(op));
|
||||
case "clear" -> defs.clear();
|
||||
case "route" -> { var k = key(op); routes[k == null ? keys.size() : keys.indexOf(k)] = op.get("target").getAsInt(); }
|
||||
default -> throw new AssertionError();
|
||||
}
|
||||
}
|
||||
void event(String name) {
|
||||
int call = calls++;
|
||||
out.println("event " + call + " " + name); definitions();
|
||||
if (call == op.get("fail").getAsInt()) throw new Stop();
|
||||
if (call == op.get("mutate").getAsInt()) {
|
||||
changed = true;
|
||||
for (var a : op.getAsJsonArray("mutations")) raw(a.getAsJsonObject());
|
||||
}
|
||||
}
|
||||
void changed(AttributeInstance a) {
|
||||
int id = ((Attr)a).id; state(id, a); event("dirty " + id);
|
||||
if (op.get("read").getAsBoolean()) out.println("observed " + bits(a.getValue()));
|
||||
}
|
||||
}
|
||||
static final class Attrs extends AttributeMap {
|
||||
Data d;
|
||||
Attrs(Data d) { super(null); this.d = d; }
|
||||
@Override public AttributeInstance getInstance(Holder<Attribute> h) {
|
||||
d.event("get " + d.keyDesc(h));
|
||||
int route = d.routes[h == null ? d.keys.size() : d.keys.indexOf(h)];
|
||||
return route < 0 ? null : d.instances[route];
|
||||
}
|
||||
}
|
||||
static final class Attr extends AttributeInstance {
|
||||
Data d; int id;
|
||||
Attr(Data d, int id, double value) { super(Holder.direct(new Basic(value, id)), d::changed); this.d = d; this.id = id; }
|
||||
@Override public boolean removeModifier(Identifier id) { d.event("remove " + this.id + " " + id); return super.removeModifier(id); }
|
||||
@Override public void addPermanentModifier(AttributeModifier modifier) {
|
||||
int m = d.modifier(modifier); d.event("permanent " + id + " " + m + " " + desc(modifier));
|
||||
super.addPermanentModifier(modifier);
|
||||
}
|
||||
}
|
||||
@SuppressWarnings("unchecked")
|
||||
public static void main(String[] args) throws Exception {
|
||||
net.minecraft.SharedConstants.tryDetectVersion(); net.minecraft.server.Bootstrap.bootStrap();
|
||||
definitions = field(MobEffect.class, "attributeModifiers");
|
||||
dirty = field(AttributeInstance.class, "dirty"); cached = field(AttributeInstance.class, "cachedValue");
|
||||
var template = Class.forName("net.minecraft.world.effect.MobEffect$AttributeTemplate");
|
||||
templateId = field(template, "id"); templateAmount = field(template, "amount"); templateOperation = field(template, "operation");
|
||||
Path dir = Path.of(args[1]);
|
||||
try (var out = new PrintWriter(Files.newBufferedWriter(dir.resolve(args[0] + ".txt")));
|
||||
var lines = Files.lines(dir.resolve("inputs.txt"))) {
|
||||
for (var line : (Iterable<String>)lines::iterator) {
|
||||
var f = JsonParser.parseString(line).getAsJsonObject(); var d = new Data(); d.out = out;
|
||||
d.defs = (Map<Holder<Attribute>, Object>)definitions.get(d.effect);
|
||||
// Direct is a two-component record (value, components). Solve
|
||||
// its record hash for the requested holder hash so collision
|
||||
// fixtures target the actual keys, not only their values.
|
||||
int componentsHash = Holder.direct(new Basic(0, 0)).hashCode();
|
||||
for (var h : f.getAsJsonArray("hashes")) {
|
||||
int requested = h.getAsInt();
|
||||
var key = Holder.<Attribute>direct(new Basic(0, (requested - componentsHash) * 0xbdef7bdf));
|
||||
if (key.hashCode() != requested) throw new AssertionError("holder hash fixture");
|
||||
d.keys.add(key);
|
||||
}
|
||||
for (var v : f.getAsJsonArray("templates")) {
|
||||
var t = v.getAsJsonObject();
|
||||
d.templates.add(new Template(Identifier.parse(t.get("id").getAsString()), number(t.get("amount")), AttributeModifier.Operation.values()[t.get("operation").getAsInt()]));
|
||||
}
|
||||
d.routes = new int[d.keys.size() + 1];
|
||||
for (int i = 0; i < d.routes.length; i++) d.routes[i] = f.getAsJsonArray("routes").get(i).getAsInt();
|
||||
d.instances = new Attr[f.getAsJsonArray("defaults").size()];
|
||||
for (int i = 0; i < d.instances.length; i++) d.instances[i] = new Attr(d, i, number(f.getAsJsonArray("defaults").get(i)));
|
||||
d.attributes = new Attrs(d);
|
||||
for (var v : f.getAsJsonArray("setup")) d.raw(v.getAsJsonObject());
|
||||
for (var v : f.getAsJsonArray("ops")) {
|
||||
d.op = v.getAsJsonObject(); d.calls = 0; d.changed = false; String result = "ok";
|
||||
int amp = d.op.get("amplifier").getAsInt();
|
||||
try {
|
||||
switch (d.op.get("op").getAsString()) {
|
||||
case "add" -> d.effect.addAttributeModifiers(d.attributes, amp);
|
||||
case "remove" -> d.effect.removeAttributeModifiers(d.attributes);
|
||||
case "create" -> d.effect.createModifiers(amp, (key, modifier) -> {
|
||||
int id = d.modifier(modifier); d.event("consume " + d.keyDesc(key) + " " + id + " " + desc(modifier));
|
||||
});
|
||||
case "seed" -> d.instances[d.op.get("target").getAsInt()].addPermanentModifier(d.templates.get(d.op.get("template").getAsInt()).create(amp));
|
||||
case "value" -> { int id = d.op.get("target").getAsInt(); out.println("value " + id + " " + bits(d.instances[id].getValue())); }
|
||||
default -> d.raw(d.op);
|
||||
}
|
||||
} catch (Stop e) { result = "callback";
|
||||
} catch (NullPointerException e) { result = "null";
|
||||
} catch (IndexOutOfBoundsException e) { result = "bounds";
|
||||
} catch (NoSuchElementException e) { result = "empty";
|
||||
} catch (IllegalArgumentException e) { result = "duplicate"; }
|
||||
out.println("result " + result); out.println("calls " + d.calls + " " + d.changed); d.definitions();
|
||||
for (int i = 0; i < d.instances.length; i++) d.state(i, d.instances[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,139 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Compare effect modifier templates and native AttributeInstance integration."""
|
||||
import argparse
|
||||
import json
|
||||
import os
|
||||
from pathlib import Path
|
||||
import platform
|
||||
import subprocess
|
||||
import random
|
||||
import struct
|
||||
try:
|
||||
from . import lab, test_rust_parity as base
|
||||
except ImportError:
|
||||
import lab
|
||||
import test_rust_parity as base
|
||||
|
||||
|
||||
def corpus():
|
||||
rng = random.Random(0x45464641545452)
|
||||
def bits(v): return struct.pack('>d', float(v)).hex()
|
||||
values = [-float('inf'), -1e30, -1, -0., 0., 1e-40, .1, 1, 2, 1e16, 1e30, float('inf'), float('nan')]
|
||||
amplifiers = [-2**31, -2**31 + 1, -2, -1, 0, 1, 2, 127, 255, 256, 2**31 - 2, 2**31 - 1]
|
||||
names = ['a', 'minecraft:a', ':a', '', 'minecraft:', ':', 'horizon:', 'horizon:/a', 'a:b.c-_/0']
|
||||
def template(name='horizon:a', amount=.1, operation=0):
|
||||
return dict(id=name, amount=bits(amount), operation=operation)
|
||||
def op(kind, **kw):
|
||||
row = dict(op=kind, key=0, template=0, target=0, amplifier=0, fail=-1, mutate=-1, mutations=[], read=False)
|
||||
row.update(kw); return row
|
||||
def hashes(mode, n):
|
||||
def unmix(x):
|
||||
h = ((x ^ (x >> 16)) * pow(0x9e3779b9, -1, 2**32)) & 0xffffffff
|
||||
return h if h < 0x80000000 else h - 2**32
|
||||
if mode == 0: return [0] * n
|
||||
if mode == 1: return [unmix(31)] * n
|
||||
if mode == 2: return [unmix(127 + i % 4) for i in range(n)]
|
||||
if mode == 3: return list(range(n))
|
||||
return [rng.randrange(-2**31, 2**31) for _ in range(n)]
|
||||
def fixture(h, templates, setup, ops, routes=None):
|
||||
return dict(hashes=h, templates=templates, defaults=[bits(1), bits(20), bits(-0.)],
|
||||
routes=routes if routes is not None else [i % 3 for i in range(len(h) + 1)], setup=setup, ops=ops)
|
||||
for amount in values:
|
||||
for operation in range(3):
|
||||
for amp in amplifiers:
|
||||
yield fixture([0], [template(amount=amount, operation=operation)], [op('define')],
|
||||
[op('create', amplifier=amp), op('add', amplifier=amp), op('value'),
|
||||
op('add', amplifier=amp, read=True), op('value'), op('remove'), op('value')])
|
||||
pool = [template(name, values[i % len(values)], i % 3) for i, name in enumerate(names)]
|
||||
yield fixture([0] * len(pool), pool, [op('define', key=i, template=i) for i in range(len(pool))],
|
||||
[op('create'), op('add'), op('add', amplifier=2), op('remove'), op('remove')])
|
||||
for mode in range(5):
|
||||
for n in [0, 1, 8, 24, 25, 49, 64]:
|
||||
h = hashes(mode, n)
|
||||
pool = [template('horizon:m' + str(i), values[i % len(values)], i % 3) for i in range(max(n, 1))]
|
||||
for null in [False, True]:
|
||||
setup = [op('define', key=i, template=i) for i in range(n)]
|
||||
if null: setup.append(op('define', key=-1))
|
||||
ops = [op('create'), op('add'), op('value', target=0), op('value', target=1), op('value', target=2),
|
||||
op('add', amplifier=2, read=True), op('remove'), op('remove')]
|
||||
yield fixture(h, pool, setup, ops)
|
||||
# Native methods defer template reads until getInstance returns; add reads
|
||||
# the template again after removeModifier (including its dirty observer).
|
||||
for mode in range(5):
|
||||
h = hashes(mode, 64)
|
||||
pool = [template('horizon:m' + str(i % 5), values[i % len(values)], i % 3) for i in range(64)]
|
||||
for n in [1, 24, 25, 49]:
|
||||
setup = [op('define', key=i, template=i) for i in range(n)]
|
||||
mutations = [[op('define', key=i, template=(i + 1) % 64) for i in range(n)],
|
||||
[op('null', key=i) for i in range(n)], [op('clear')],
|
||||
[op('drop', key=i) for i in range(n - 1)],
|
||||
[op('define', key=i, template=i) for i in range(n, 64)],
|
||||
[op('route', key=i, target=-1) for i in range(n)],
|
||||
[op('route', key=i, target=2) for i in range(n)],
|
||||
[op('define', key=-1, template=7)]]
|
||||
for kind in ['add', 'remove', 'create']:
|
||||
for actions in mutations:
|
||||
for at in [0, 1, 2, 3, 5]:
|
||||
yield fixture(h, pool, setup, [op('add'), op(kind, amplifier=-1, mutate=at, mutations=actions, read=True),
|
||||
op('create', amplifier=2**31 - 1), op('remove')])
|
||||
for null in [False, True]:
|
||||
key = -1 if null else 0
|
||||
for route in [-1, 0, 1]:
|
||||
yield fixture([0], [template()], [op('null', key=key), op('route', key=key, target=route)],
|
||||
[op('add'), op('remove'), op('create')])
|
||||
for fail in range(12):
|
||||
for read in [False, True]:
|
||||
for kind in ['add', 'remove', 'create']:
|
||||
pool = [template(), template('horizon:b', .3, 1), template('horizon:c', -.7, 2)]
|
||||
yield fixture([0, 0, 0], pool, [op('define', key=i, template=i) for i in range(3)],
|
||||
[op('add'), op(kind, amplifier=3, fail=fail, read=read), op('value'), op('add'), op('remove')])
|
||||
for _ in range(300):
|
||||
n = rng.choice([1, 8, 24, 25, 49])
|
||||
pool = [template(rng.choice(names + ['horizon:m' + str(i) for i in range(12)]), rng.choice(values), rng.randrange(3)) for __ in range(20)]
|
||||
h = hashes(rng.randrange(5), n)
|
||||
setup = [op('define', key=i, template=rng.randrange(len(pool))) for i in range(n)]
|
||||
def raw():
|
||||
return op(rng.choice(['define', 'define', 'null', 'drop', 'clear', 'route']), key=rng.randrange(-1, n),
|
||||
template=rng.randrange(len(pool)), target=rng.randrange(-1, 3))
|
||||
ops = []
|
||||
for __ in range(30):
|
||||
if rng.randrange(5) == 0: ops.append(raw()); continue
|
||||
ops.append(op(rng.choice(['add', 'remove', 'create', 'seed', 'value']),
|
||||
target=rng.randrange(3), template=rng.randrange(len(pool)), amplifier=rng.choice(amplifiers),
|
||||
fail=rng.choice([-1, -1, -1, 0, 1, 2, 3]), mutate=rng.choice([-1, -1, 0, 1, 2, 3]),
|
||||
read=bool(rng.randrange(2)), mutations=[raw() for ___ in range(rng.randrange(5))]))
|
||||
yield fixture(h, pool, setup, ops)
|
||||
|
||||
|
||||
def main():
|
||||
parser=argparse.ArgumentParser(description=__doc__);parser.add_argument('--prepare',action='store_true');args=parser.parse_args()
|
||||
build=lab.LOCAL/'rust-effect-attribute-modifiers-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/VanillaEffectAttributeModifiersOracle.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_attribute_modifiers_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.VanillaEffectAttributeModifiersOracle',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_attribute_modifiers_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_attribute_modifiers_fixture.exe' if os.name=='nt' else 'effect_attribute_modifiers_fixture')
|
||||
run([binary,build],'rust');references=[]
|
||||
for side,jar in jars.items():
|
||||
java(side,side,build);reference=build/f'{side}.txt';matched=base.compare(reference,build/'rust.txt')
|
||||
references.append({'side':side,'jar_sha256':lab.digest(jar,'sha256'),'output_sha256':lab.digest(reference,'sha256'),'matched_output_lines':matched})
|
||||
print(f'{side}: {matched:,} effect attribute modifier 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),'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,'rust_output_sha256':lab.digest(build/'rust.txt','sha256'),'references':references,'scope':'Actual MobEffect addAttributeModifier/createModifiers/addAttributeModifiers/removeAttributeModifiers with native fastutil definition map and AttributeInstance permanent modifier storage, cached values and dirty observers. Tests wrapping amplifier addition, double scaling, fresh modifier identity, map entry rereads after virtual getInstance/remove callbacks, null/missing instance skipping, map growth/shrink/replacement and partial failures. AttributeMap instance lookup and dirty receiver are controlled; callback mutation touches only definitions/routes. Registry effect definitions, pending attribute set ownership, entity notifications, full gameplay and playable platforms remain pending.'}
|
||||
lab.write_json(report_path,report);print(f'Scoped effect attribute modifier parity passed: {report_path}')
|
||||
|
||||
|
||||
if __name__=='__main__':main()
|
||||
Reference in New Issue
Block a user