mirror of
https://github.com/horizon-rust/horizon.git
synced 2026-11-07 02:56:17 -10:00
Match effect eligibility and attribute/passenger notification ordering
This commit is contained in:
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)
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
@@ -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.
|
||||
+55
@@ -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();}
|
||||
}}
|
||||
}
|
||||
}
|
||||
@@ -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()
|
||||
Reference in new issue
Block a user