Integrate entity effect ticking, expiration and client particles

This commit is contained in:
Ludovic Marie committed 2026-10-07 11:09:59 +02:00
1 parent 32c53db88a
commit 05cdc7573a
7 files changed
+1260 -2

No files matched your search

@@ -0,0 +1,299 @@
{
"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": 4036,
"sources_sha256": {
"mods/horizon/src/test/java/dev/horizonlab/parity/VanillaActiveEffectTickOracle.java": "34db99bcb0190ad7aebdc2c2b4e26c4853343e43de23febf26f30248f2aceaac",
"tools/test_active_effect_tick_parity.py": "e0871a217b7af371d1eb104d10c0e2e04864890c568d0ebc3f852ea7d09b7c0f",
"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": "1788f48f7f88463ced734af0bb02610cd7a991d5a6bdeaa2f9f198a4b7312c2a",
"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/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": "60e2a3cb655c5e6dacafb0f6355ec8d7c8580b3b0e322615acbf4b5b9c06b657",
"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/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/active_effect_tick_fixture.rs": "243811dae9d40ce062afd26564125ad06c431a799d19b618e0d0db5340ce9153"
},
"fixtures_sha256": {
".local/rust-active-effect-tick-parity/inputs.txt": "9856f0340d403d38a530563f6195ac2a71440564b383238e185fa864985282dd",
".local/rust-active-effect-tick-parity/inputs-client.txt": "92124cc0ae75c2a886acdca055350827bbd539a19c656d6bca5eeae516039ce0",
".local/rust-active-effect-tick-parity/inputs-server.txt": "9d09603803169cafe1985dff5f07494059ba9be7a2ef3ed51bc02d421e7d0532"
},
"references": [
{
"side": "client",
"jar_sha256": "4508d006323f24fa02876310c192d739af56516eb259000ac50f0909a68c9a2d",
"output_sha256": "8a5c57f03b84499e636e13502e88e205b2f88a64052a484ce81ce85b173b78cf",
"rust_output_sha256": "8a5c57f03b84499e636e13502e88e205b2f88a64052a484ce81ce85b173b78cf",
"matched_output_lines": 1732406
},
{
"side": "server",
"jar_sha256": "a362163eec5d1612d520772bc16e5b39c09e3b234fdc045f56bf544284ee8ae6",
"output_sha256": "1b57c55f0645d4d3c74386c06319da11f391ccc8cf549fdd7e7134eb7b013de4",
"rust_output_sha256": "1b57c55f0645d4d3c74386c06319da11f391ccc8cf549fdd7e7134eb7b013de4",
"matched_output_lines": 1732849
}
],
"scope": "Inherited LivingEntity.tickEffects composed with actual MobEffectInstance ticks, active-effect HashMaps, native synced particle/ambience data and both RNG kinds. Includes removal/refresh ordering, shared hidden restoration, nonstructural replacements, structural mutations, concurrent-modification catch boundaries, client blending, particle selection and RNG tails. Selected raw instance fields and callbacks can mutate during execution. Level classes are controlled ServerLevel and non-ServerLevel implementations; random particle positions, registered effect behavior and notification side effects remain controlled. Holder identity hashes are exported per reference run. Complete entity/player lifecycle and gameplay remain pending."
}
@@ -0,0 +1,529 @@
use horizon_core::{
position::BlockPos,
random::{LegacyRandom, RandomSource, XoroshiroRandom},
};
use horizon_world::{
active_effect_tick::{TickError, TickHooks},
active_effects::{ActiveEffects, EffectHooks},
consume_effect::ConsumeEffect,
java_hash_map::CursorError,
java_hash_map::JavaKey,
status_effect::{BlendState, Details, EffectInstance},
stored_consume::{ConsumeHooks, StoredConsume},
};
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 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, R> {
random: [R; 2],
f: Value,
out: &'a mut dyn Write,
keys: Vec<Key>,
pool: Vec<Effect>,
ids: Vec<Effect>,
calls: i32,
changed: bool,
}
impl<R: RandomSource> World<'_, R> {
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) {
let c = self.config().clone();
if c["reseed"].as_bool().unwrap() {
for i in 0..2 {
self.random[i].set_seed(c["seeds"][i].as_i64().unwrap());
}
}
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).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() {
"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, "cme") {
return Err("concurrent");
}
if i == n(&self.f, "mutate") {
self.changed = true;
self.reset();
for op in self.f["mutations"].as_array().unwrap().clone() {
self.raw(s, &op);
}
}
Ok(())
}
}
impl<R: RandomSource> EffectHooks<Key> for World<'_, R> {
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> {
let i = self.id(&e);
self.event(s, format!("eligible {i}"))?;
Ok(self.config()["allow"].as_bool().unwrap())
}
fn on_added(
&mut self,
s: &mut Storage,
e: Effect,
source: Option<i32>,
) -> Result<(), Self::Error> {
let i = self.id(&e);
self.event(s, format!("added {i} {}", source.unwrap_or(-1)))
}
fn on_updated(
&mut self,
s: &mut Storage,
e: Effect,
reapply: bool,
source: Option<i32>,
) -> Result<(), Self::Error> {
let i = self.id(&e);
self.event(s, format!("updated {i} {reapply} {}", source.unwrap_or(-1)))
}
fn effect_added(&mut self, s: &mut Storage, e: Key, amp: i32) -> Result<(), Self::Error> {
self.event(s, format!("effect-added {} {amp}", e.id))
}
fn effect_started(&mut self, s: &mut Storage, e: Key, amp: i32) -> Result<(), Self::Error> {
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> {
let ids = es
.iter()
.map(|e| self.id(e).to_string())
.collect::<Vec<_>>()
.join(",");
self.event(s, format!("removed {ids}"))
}
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<R: RandomSource> ConsumeHooks<Key, u8, Vec<Key>> for World<'_, R> {
type Random = usize;
type Rng = R;
type SoundCategory = i32;
fn get_random(&mut self, s: &mut Storage) -> Result<usize, Self::Error> {
self.event(s, "random".into())?;
Ok(n(self.config(), "random") as usize)
}
fn random_source(&mut self, r: usize) -> &mut R {
&mut self.random[r]
}
fn effect_set(&self, set: &Vec<Key>) -> Vec<Key> {
set.clone()
}
fn block_position(&mut self, s: &mut Storage) -> Result<BlockPos, Self::Error> {
self.event(s, "position".into())?;
let p = &self.config()["position"];
Ok(BlockPos::new(
p[0].as_i64().unwrap() as i32,
p[1].as_i64().unwrap() as i32,
p[2].as_i64().unwrap() as i32,
))
}
fn sound_category(&mut self, s: &mut Storage) -> Result<i32, Self::Error> {
self.event(s, "category".into())?;
Ok(n(self.config(), "category"))
}
fn play_sound(
&mut self,
storage: &mut Storage,
level: i32,
p: BlockPos,
sound: u8,
category: i32,
) -> Result<(), Self::Error> {
self.event(
storage,
format!("sound {level} {} {} {} {sound} {category}", p.x, p.y, p.z),
)
}
}
impl<R: RandomSource> TickHooks<Key> for World<'_, R> {
type Rng = R;
type Particle = i32;
fn is_server_level(&self, level: i32) -> bool {
self.f["servers"][level as usize].as_bool().unwrap()
}
fn is_concurrent_modification(&self, e: &Self::Error) -> bool {
*e == "concurrent"
}
fn actor_tick_count(&self) -> i32 {
n(self.config(), "tick")
}
fn should_apply(
&mut self,
s: &mut Storage,
effect: Key,
time: i32,
amp: i32,
) -> Result<bool, Self::Error> {
self.event(s, format!("should {} {time} {amp}", effect.id))?;
Ok(self.config()["should"].as_bool().unwrap())
}
fn apply_tick(
&mut self,
s: &mut Storage,
effect: Key,
level: i32,
amp: i32,
) -> Result<bool, Self::Error> {
self.event(s, format!("apply {} {level} {amp}", effect.id))?;
Ok(self.config()["apply"].as_bool().unwrap())
}
fn advance_ticks(&mut self, s: &mut Storage, effect: Key) -> Result<i32, Self::Error> {
self.event(s, format!("advance {}", effect.id))?;
Ok(n(self.config(), "advance"))
}
fn duration_ticks(
&mut self,
s: &mut Storage,
effect: Key,
blending_in: bool,
) -> Result<i32, Self::Error> {
self.event(s, format!("blend-duration {} {blending_in}", effect.id))?;
Ok(n(
self.config(),
if blending_in { "blend_in" } else { "blend_out" },
))
}
fn particles(&self) -> Vec<i32> {
self.config()["particles"]
.as_array()
.unwrap()
.iter()
.map(|v| v.as_i64().unwrap() as i32)
.collect()
}
fn ambient(&self) -> bool {
self.config()["ambience"].as_bool().unwrap()
}
fn invisible(&mut self, s: &mut Storage) -> Result<bool, Self::Error> {
self.event(s, "invisible".into())?;
Ok(self.config()["invisible"].as_bool().unwrap())
}
fn random_source(&mut self) -> &mut R {
let r = n(self.config(), "random") as usize;
&mut self.random[r]
}
fn random_x(&mut self, s: &mut Storage, scale: f64) -> Result<f64, Self::Error> {
assert_eq!(scale, 0.5);
self.event(s, "x".into())?;
Ok(f64::from_bits(
self.config()["coordinates"][0].as_u64().unwrap(),
))
}
fn random_y(&mut self, s: &mut Storage) -> Result<f64, Self::Error> {
self.event(s, "y".into())?;
Ok(f64::from_bits(
self.config()["coordinates"][1].as_u64().unwrap(),
))
}
fn random_z(&mut self, s: &mut Storage, scale: f64) -> Result<f64, Self::Error> {
assert_eq!(scale, 0.5);
self.event(s, "z".into())?;
Ok(f64::from_bits(
self.config()["coordinates"][2].as_u64().unwrap(),
))
}
fn add_particle(
&mut self,
s: &mut Storage,
level: i32,
particle: i32,
p: [f64; 3],
) -> Result<(), Self::Error> {
let bits = |v: f64| {
if v.is_nan() {
0x7ff8000000000000
} else {
v.to_bits()
}
};
self.event(
s,
format!(
"particle {level} {particle} {} {} {}",
bits(p[0]),
bits(p[1]),
bits(p[2])
),
)
}
}
fn run<R: RandomSource>(f: Value, out: &mut dyn Write, make: fn(i64) -> R) {
let random = std::array::from_fn(|i| make(f["seeds"][i].as_i64().unwrap()));
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 mut w = World {
random,
f,
out,
keys,
pool,
ids: Vec::new(),
calls: 0,
changed: false,
};
let mut s = Storage::default();
w.reset();
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() {
"tick" => s.tick(&mut w).map(|_| "ok".into()).map_err(|e| match e {
TickError::Callback(e) => e,
TickError::Iterator(CursorError::ConcurrentModification) => "concurrent",
other => panic!("{other:?}"),
}),
"apply-status" | "remove-status" | "clear-status" | "sound" => {
let list = op["list"].as_array().unwrap();
let effect: ConsumeEffect<Key, u8> = match op["op"].as_str().unwrap() {
"apply-status" => ConsumeEffect::ApplyStatus {
effects: list
.iter()
.map(|i| w.pool[i.as_u64().unwrap() as usize].clone())
.collect(),
probability: fl(&op, "probability"),
},
"remove-status" => ConsumeEffect::RemoveStatus(
list.iter()
.map(|i| w.keys[i.as_u64().unwrap() as usize].clone())
.collect(),
),
"clear-status" => ConsumeEffect::ClearStatus,
_ => ConsumeEffect::PlaySound(n(&op, "sound") as u8),
};
effect
.apply(
&mut StoredConsume {
effects: &mut s,
world: &mut w,
},
n(&op, "level"),
)
.map(|v| v.to_string())
}
"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);
}
for (i, r) in w.random.iter_mut().enumerate() {
writeln!(w.out, "random {i} {} {}", r.next_long(), r.next_long()).unwrap();
}
}
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() {
let f: Value = serde_json::from_str(line)?;
if f["xor"].as_bool().unwrap() {
run(f, &mut out, XoroshiroRandom::new);
} else {
run(f, &mut out, LegacyRandom::new);
}
}
Ok(())
}
@@ -0,0 +1,207 @@
//! LivingEntity effect tick loop, including native iterator failure handling.
use crate::{
active_effects::{ActiveEffects, EffectHooks},
java_hash_map::{CursorError, JavaKey},
status_effect::{BlendWorld, EffectInstance, TickWorld},
};
use horizon_core::random::RandomSource;
pub trait TickHooks<H: JavaKey + Clone>: EffectHooks<H> {
type Rng: RandomSource;
type Particle: Clone;
/// Runtime ServerLevel class test, distinct from Level.isClientSide.
fn is_server_level(&self, level: Self::Level) -> bool;
fn is_concurrent_modification(&self, error: &Self::Error) -> bool;
fn actor_tick_count(&self) -> i32;
fn should_apply(
&mut self,
storage: &mut ActiveEffects<H>,
effect: H,
time: i32,
amplifier: i32,
) -> Result<bool, Self::Error>;
fn apply_tick(
&mut self,
storage: &mut ActiveEffects<H>,
effect: H,
level: Self::Level,
amplifier: i32,
) -> Result<bool, Self::Error>;
fn advance_ticks(
&mut self,
storage: &mut ActiveEffects<H>,
effect: H,
) -> Result<i32, Self::Error>;
fn duration_ticks(
&mut self,
storage: &mut ActiveEffects<H>,
effect: H,
blending_in: bool,
) -> Result<i32, Self::Error>;
/// Synced-data reads are direct and retain the selected particle list.
fn particles(&self) -> Vec<Self::Particle>;
fn ambient(&self) -> bool;
fn invisible(&mut self, storage: &mut ActiveEffects<H>) -> Result<bool, Self::Error>;
/// Direct actor random field access, without calling getRandom.
fn random_source(&mut self) -> &mut Self::Rng;
fn random_x(&mut self, storage: &mut ActiveEffects<H>, scale: f64) -> Result<f64, Self::Error>;
fn random_y(&mut self, storage: &mut ActiveEffects<H>) -> Result<f64, Self::Error>;
fn random_z(&mut self, storage: &mut ActiveEffects<H>, scale: f64) -> Result<f64, Self::Error>;
/// Native particle velocity is (1, 1, 1).
fn add_particle(
&mut self,
storage: &mut ActiveEffects<H>,
level: Self::Level,
particle: Self::Particle,
position: [f64; 3],
) -> Result<(), Self::Error>;
}
#[derive(Debug)]
pub enum TickError<E> {
Iterator(CursorError),
Callback(E),
}
struct InstanceTick<'a, H, W> {
storage: &'a mut ActiveEffects<H>,
world: &'a mut W,
}
impl<H: JavaKey + Clone, W: TickHooks<H>> TickWorld<H> for InstanceTick<'_, H, W> {
type Error = W::Error;
type Level = W::Level;
fn actor_tick_count(&self) -> i32 {
self.world.actor_tick_count()
}
fn should_apply(
&mut self,
effect: H,
time: i32,
amplifier: i32,
_: &EffectInstance<H>,
) -> Result<bool, Self::Error> {
self.world
.should_apply(self.storage, effect, time, amplifier)
}
fn apply_tick(
&mut self,
effect: H,
level: Self::Level,
amplifier: i32,
_: &EffectInstance<H>,
) -> Result<bool, Self::Error> {
self.world
.apply_tick(self.storage, effect, level, amplifier)
}
fn downgraded(&mut self, instance: &EffectInstance<H>) -> Result<(), Self::Error> {
self.world
.on_updated(self.storage, instance.clone(), true, None)
}
}
impl<H: JavaKey + Clone, W: TickHooks<H>> BlendWorld<H> for InstanceTick<'_, H, W> {
type Error = W::Error;
fn advance_ticks(&mut self, effect: H, _: &EffectInstance<H>) -> Result<i32, Self::Error> {
self.world.advance_ticks(self.storage, effect)
}
fn duration_ticks(
&mut self,
effect: H,
blending_in: bool,
_: &EffectInstance<H>,
) -> Result<i32, Self::Error> {
self.world.duration_ticks(self.storage, effect, blending_in)
}
}
impl<H: JavaKey + Clone> ActiveEffects<H> {
pub fn tick<W: TickHooks<H>>(&mut self, world: &mut W) -> Result<(), TickError<W::Error>> {
let level = world.level(self).map_err(TickError::Callback)?;
if world.is_server_level(level) {
// The catch begins after the level query and iterator creation.
match self.tick_server(world, level) {
Err(TickError::Iterator(CursorError::ConcurrentModification)) => Ok(()),
Err(TickError::Callback(error)) if world.is_concurrent_modification(&error) => {
Ok(())
}
result => result,
}
} else {
self.tick_client(world)
}
}
fn tick_server<W: TickHooks<H>>(
&mut self,
world: &mut W,
level: W::Level,
) -> Result<(), TickError<W::Error>> {
let mut cursor = self.entries.cursor();
while cursor.has_next() {
let (key, _) = self
.entries
.next_entry(&mut cursor)
.map_err(TickError::Iterator)?
.unwrap();
let instance = self.get(key).unwrap();
let active = instance
.tick_server(
&mut InstanceTick {
storage: self,
world,
},
level,
)
.map_err(TickError::Callback)?;
if !active {
self.entries
.remove_current(&mut cursor)
.map_err(TickError::Iterator)?;
world
.on_removed(self, vec![instance])
.map_err(TickError::Callback)?;
} else if instance.read().details.duration % 600 == 0 {
world
.on_updated(self, instance, false, None)
.map_err(TickError::Callback)?;
}
}
Ok(())
}
fn tick_client<W: TickHooks<H>>(&mut self, world: &mut W) -> Result<(), TickError<W::Error>> {
let mut cursor = self.entries.cursor();
while cursor.has_next() {
let (_, instance) = self
.entries
.next_entry(&mut cursor)
.map_err(TickError::Iterator)?
.unwrap();
let instance = instance.clone();
instance
.tick_client(&mut InstanceTick {
storage: self,
world,
})
.map_err(TickError::Callback)?;
}
let particles = world.particles();
if particles.is_empty() {
return Ok(());
}
let ambient = world.ambient();
let invisible = world.invisible(self).map_err(TickError::Callback)?;
let interval = (if invisible { 15 } else { 4 }) * (if ambient { 5 } else { 1 });
if world.random_source().next_int_bound(interval).unwrap() != 0 {
return Ok(());
}
let level = world.level(self).map_err(TickError::Callback)?;
let index = world
.random_source()
.next_int_bound(particles.len() as i32)
.unwrap() as usize;
let particle = particles[index].clone();
let x = world.random_x(self, 0.5).map_err(TickError::Callback)?;
let y = world.random_y(self).map_err(TickError::Callback)?;
let z = world.random_z(self, 0.5).map_err(TickError::Callback)?;
world
.add_particle(self, level, particle, [x, y, z])
.map_err(TickError::Callback)
}
}
+1
View File
@@ -2,6 +2,7 @@
#![forbid(unsafe_code)]
pub mod active_effects;
pub mod active_effect_tick;
pub mod stored_consume;
pub mod attribute_health;
pub mod attributes;
+30 -2
View File
@@ -21,8 +21,11 @@ composes food storage, native random streams and eating particles, with exact
sound parameters and effect ordering checked against both pinned JARs. Consume
effect bodies now cover status application/removal/clearing, sounds and random
teleport attempts. Shared status instances implement hidden-chain updates,
server/client countdown and client blending with native comparisons. These
kernels still need complete entity/world lifecycle bindings. All pinned block-survival
server/client countdown and client blending with native comparisons. Entity
effect storage now binds consumable status changes and the effect tick loop,
including expiration, periodic refresh and client particle selection. Registered
effect behavior, notification side effects and full entity/world lifecycle
bindings remain unfinished. All pinned block-survival
families now have native differential coverage. Full simulation callbacks,
complete chunk generation, entity behavior, networking, rendering and playable
integration remain unfinished. The linked subsystem documents and hashed reports
@@ -1014,3 +1017,28 @@ and fixture Clippy checks pass. Eligibility policy, registered effect behavior,
attribute/packet notifications and sound delivery remain controlled. Named
holder-set binding, the full player consumption lifecycle and complete gameplay
remain pending.
## Entity effect tick loop and client particles
The Rust entity effect loop now iterates shared active storage, removes expired
effects before notifying observers, reports hidden-effect restoration, and
refreshes surviving effects on vanilla's 600-tick duration boundary. It uses the
actual level class distinction. Server iteration catches concurrent-modification
failures from map iteration or callbacks; the initial level query and client
iteration preserve their propagating failure behavior. Value replacement alone
does not invalidate iteration, and callbacks continue using captured instances.
Client ticks advance each instance's duration and blend, then read synchronized
particle/ambience data, apply the invisibility-dependent random interval, and
select particles from the captured list. Position queries and the destination
level preserve native evaluation order.
The [effect-loop comparison](../benchmarks/rust-active-effect-tick-2026-10-07/scoped-parity.json)
matches **1,732,406 client-JAR lines** and **1,732,849 server-JAR lines** across
**4,036 fixtures**. Each reference exports its own holder identity hashes; those
hashes can change iteration order and subsequent callback paths between JVMs.
The checks execute inherited native entity and instance methods, native maps,
synchronized data and both RNG implementations. Library and fixture Clippy pass.
Registered effect bodies, attribute/packet notification side effects and random
position methods are still controlled. Complete entity ticking, gameplay and
playable platform builds remain pending.
@@ -0,0 +1,101 @@
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.core.BlockPos;import net.minecraft.core.HolderSet;import net.minecraft.world.item.ItemStack;import net.minecraft.world.item.consume_effects.*;import net.minecraft.sounds.*;import net.minecraft.util.RandomSource;import net.minecraft.world.level.levelgen.XoroshiroRandomSource;
import net.minecraft.network.syncher.*;import net.minecraft.core.particles.*;
/** Inherited entity effect ticking with shared status storage, real iterators and RNG. */
public final class VanillaActiveEffectTickOracle {
static Object unsafe;static Method allocate;static Field duration,amplifier,ambient,visible,icon,hidden,blend,factor,previous,removed,activeEffects;static Data active;static List<ParticleOptions> particles;static Constructor<SynchedEntityData> dataCtor;static EntityDataAccessor<List<ParticleOptions>> particleKey;static EntityDataAccessor<Boolean> ambienceKey;static Field randomField,dataField;
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;RandomSource[] random;List<Holder<MobEffect>> keys=new ArrayList<>();MobEffectInstance[] pool;IdentityHashMap<MobEffectInstance,Integer> ids=new IdentityHashMap<>();Actor actor;Entity other;Level[] levels;SynchedEntityData.DataItem<List<ParticleOptions>> particleItem;SynchedEntityData.DataItem<Boolean> ambienceItem;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();actor.tickCount=n(c,"tick");randomField.set(actor,random[n(c,"random")]);var selected=new ArrayList<ParticleOptions>();for(var v:c.getAsJsonArray("particles"))selected.add(particles.get(v.getAsInt()));particleItem.setValue(selected);ambienceItem.setValue(c.get("ambience").getAsBoolean());if(c.get("reseed").getAsBoolean())for(int i=0;i<2;i++)random[i].setSeed(c.getAsJsonArray("seeds").get(i).getAsLong());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);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 "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,"cme"))throw new ConcurrentModificationException();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 boolean shouldApplyEffectTickThisTick(int time,int amp){active.event("should "+id+" "+time+" "+amp);return active.config().get("should").getAsBoolean();}
@Override public boolean applyEffectTick(ServerLevel level,LivingEntity actor,int amp){if(actor!=active.actor)throw new AssertionError();int l=level==active.levels[0]?0:level==active.levels[1]?1:-1;if(l<0)throw new AssertionError();active.event("apply "+id+" "+l+" "+amp);return active.config().get("apply").getAsBoolean();}
@Override public int getBlendOutAdvanceTicks(){active.event("advance "+id);return n(active.config(),"advance");}
@Override public int getBlendInDurationTicks(){active.event("blend-duration "+id+" true");return n(active.config(),"blend_in");}
@Override public int getBlendOutDurationTicks(){active.event("blend-duration "+id+" false");return n(active.config(),"blend_out");}
@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);}void effectTick(){super.tickEffects();}
@Override public boolean isInvisible(){d.event("invisible");return d.config().get("invisible").getAsBoolean();}
@Override public double getRandomX(double scale){if(scale!=0.5)throw new AssertionError();d.event("x");return Double.longBitsToDouble(d.config().getAsJsonArray("coordinates").get(0).getAsLong());}
@Override public double getRandomY(){d.event("y");return Double.longBitsToDouble(d.config().getAsJsonArray("coordinates").get(1).getAsLong());}
@Override public double getRandomZ(double scale){if(scale!=0.5)throw new AssertionError();d.event("z");return Double.longBitsToDouble(d.config().getAsJsonArray("coordinates").get(2).getAsLong());}
@Override public RandomSource getRandom(){d.event("random");return d.random[n(d.config(),"random")];}
@Override public BlockPos blockPosition(){d.event("position");var p=d.config().getAsJsonArray("position");return new BlockPos(p.get(0).getAsInt(),p.get(1).getAsInt(),p.get(2).getAsInt());}
@Override public SoundSource getSoundSource(){d.event("category");return SoundSource.values()[n(d.config(),"category")];}
@Override public boolean canBeAffected(MobEffectInstance e){d.event("eligible "+d.id(e));return d.config().get("allow").getAsBoolean();}
@Override protected void onEffectAdded(MobEffectInstance e,Entity source){d.event("added "+d.id(e)+" "+d.source(source));}
@Override protected void onEffectUpdated(MobEffectInstance e,boolean reapply,Entity source){d.event("updated "+d.id(e)+" "+reapply+" "+d.source(source));}
@Override protected void onEffectsRemoved(Collection<MobEffectInstance> values){var ids=new StringJoiner(",");for(var e:values)ids.add(Integer.toString(d.id(e)));d.event("removed "+ids);}
@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 void addParticle(ParticleOptions p,double x,double y,double z,double dx,double dy,double dz){if(dx!=1||dy!=1||dz!=1)throw new AssertionError();d.event("particle "+id+" "+particles.indexOf(p)+" "+Long.toUnsignedString(Double.doubleToLongBits(x))+" "+Long.toUnsignedString(Double.doubleToLongBits(y))+" "+Long.toUnsignedString(Double.doubleToLongBits(z)));}
@Override public void playSound(Entity excluded,BlockPos p,SoundEvent sound,SoundSource category,float volume,float pitch){if(excluded!=null||volume!=1||pitch!=1)throw new AssertionError();int s=sound==SoundEvents.GENERIC_EAT.value()?0:sound==SoundEvents.PLAYER_BURP?1:-1;if(s<0)throw new AssertionError();d.event("sound "+id+" "+p.getX()+" "+p.getY()+" "+p.getZ()+" "+s+" "+category.ordinal());}
@Override public boolean isClientSide(){d.event("client "+id);return d.config().get("client").getAsBoolean();}}
static final class ClientEffectLevel extends Level {Data d;int id;ClientEffectLevel(){super(null,null,null,null,false,false,0L,0);}
@Override public void addParticle(ParticleOptions p,double x,double y,double z,double dx,double dy,double dz){if(dx!=1||dy!=1||dz!=1)throw new AssertionError();d.event("particle "+id+" "+particles.indexOf(p)+" "+Long.toUnsignedString(Double.doubleToLongBits(x))+" "+Long.toUnsignedString(Double.doubleToLongBits(y))+" "+Long.toUnsignedString(Double.doubleToLongBits(z)));}
@Override public void playSound(Entity excluded,BlockPos p,SoundEvent sound,SoundSource category,float volume,float pitch){if(excluded!=null||volume!=1||pitch!=1)throw new AssertionError();int s=sound==SoundEvents.GENERIC_EAT.value()?0:sound==SoundEvents.PLAYER_BURP?1:-1;if(s<0)throw new AssertionError();d.event("sound "+id+" "+p.getX()+" "+p.getY()+" "+p.getZ()+" "+s+" "+category.ordinal());}
@Override public boolean isClientSide(){d.event("client "+id);return d.config().get("client").getAsBoolean();}
@Override public net.minecraft.world.clock.ClockManager clockManager(){throw new UnsupportedOperationException();}
@Override public void destroyBlockProgress(int p0,net.minecraft.core.BlockPos p1,int p2){throw new UnsupportedOperationException();}
@Override public java.util.Collection dragonParts(){throw new UnsupportedOperationException();}
@Override public net.minecraft.world.flag.FeatureFlagSet enabledFeatures(){throw new UnsupportedOperationException();}
@Override public net.minecraft.world.attribute.EnvironmentAttributeSystem environmentAttributes(){throw new UnsupportedOperationException();}
@Override public void explode(net.minecraft.world.entity.Entity p0,net.minecraft.world.damagesource.DamageSource p1,net.minecraft.world.level.ExplosionDamageCalculator p2,double p3,double p4,double p5,float p6,boolean p7,net.minecraft.world.level.Level.ExplosionInteraction p8,net.minecraft.core.particles.ParticleOptions p9,net.minecraft.core.particles.ParticleOptions p10,net.minecraft.util.random.WeightedList p11,net.minecraft.core.Holder p12){throw new UnsupportedOperationException();}
@Override public void gameEvent(net.minecraft.core.Holder p0,net.minecraft.world.phys.Vec3 p1,net.minecraft.world.level.gameevent.GameEvent.Context p2){throw new UnsupportedOperationException();}
@Override public java.lang.String gatherChunkSourceStats(){throw new UnsupportedOperationException();}
@Override public net.minecraft.world.ticks.LevelTickAccess getBlockTicks(){throw new UnsupportedOperationException();}
@Override public net.minecraft.world.level.chunk.ChunkSource getChunkSource(){throw new UnsupportedOperationException();}
@Override public net.minecraft.world.level.entity.LevelEntityGetter getEntities(){throw new UnsupportedOperationException();}
@Override public net.minecraft.world.entity.Entity getEntity(int p0){throw new UnsupportedOperationException();}
@Override public net.minecraft.world.ticks.LevelTickAccess getFluidTicks(){throw new UnsupportedOperationException();}
@Override public net.minecraft.world.level.saveddata.maps.MapItemSavedData getMapData(net.minecraft.world.level.saveddata.maps.MapId p0){throw new UnsupportedOperationException();}
@Override public net.minecraft.world.level.storage.LevelData.RespawnData getRespawnData(){throw new UnsupportedOperationException();}
@Override public net.minecraft.world.scores.Scoreboard getScoreboard(){throw new UnsupportedOperationException();}
@Override public int getSeaLevel(){throw new UnsupportedOperationException();}
@Override public net.minecraft.core.Holder getUncachedNoiseBiome(int p0,int p1,int p2){throw new UnsupportedOperationException();}
@Override public net.minecraft.world.level.border.WorldBorder getWorldBorder(){throw new UnsupportedOperationException();}
@Override public void levelEvent(net.minecraft.world.entity.Entity p0,int p1,net.minecraft.core.BlockPos p2,int p3){throw new UnsupportedOperationException();}
@Override public void playSeededSound(net.minecraft.world.entity.Entity p0,net.minecraft.world.entity.Entity p1,net.minecraft.core.Holder p2,net.minecraft.sounds.SoundSource p3,float p4,float p5,long p6){throw new UnsupportedOperationException();}
@Override public void playSeededSound(net.minecraft.world.entity.Entity p0,double p1,double p2,double p3,net.minecraft.core.Holder p4,net.minecraft.sounds.SoundSource p5,float p6,float p7,long p8){throw new UnsupportedOperationException();}
@Override public java.util.List players(){throw new UnsupportedOperationException();}
@Override public net.minecraft.world.item.crafting.RecipeAccess recipeAccess(){throw new UnsupportedOperationException();}
@Override public void sendBlockUpdated(net.minecraft.core.BlockPos p0,net.minecraft.world.level.block.state.BlockState p1,net.minecraft.world.level.block.state.BlockState p2,int p3){throw new UnsupportedOperationException();}
@Override public void setRespawnData(net.minecraft.world.level.storage.LevelData.RespawnData p0){throw new UnsupportedOperationException();}
@Override public net.minecraft.world.TickRateManager tickRateManager(){throw new UnsupportedOperationException();}
}
public static void main(String[] args)throws Exception{net.minecraft.SharedConstants.tryDetectVersion();net.minecraft.server.Bootstrap.bootStrap();particles=List.of(ParticleTypes.SMOKE,ParticleTypes.FLAME,ParticleTypes.HAPPY_VILLAGER);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");particleKey=(EntityDataAccessor<List<ParticleOptions>>)field(LivingEntity.class,"DATA_EFFECT_PARTICLES").get(null);ambienceKey=(EntityDataAccessor<Boolean>)field(LivingEntity.class,"DATA_EFFECT_AMBIENCE_ID").get(null);dataField=field(Entity.class,"entityData");randomField=field(Entity.class,"random");dataCtor=SynchedEntityData.class.getDeclaredConstructor(SyncedDataHolder.class,SynchedEntityData.DataItem[].class);dataCtor.setAccessible(true);Path dir=Path.of(args[1]);
try(var out=new PrintWriter(Files.newBufferedWriter(dir.resolve(args[0]+".txt")));var enriched=new PrintWriter(Files.newBufferedWriter(dir.resolve("inputs-"+args[0]+".txt")));var lines=Files.lines(dir.resolve("inputs.txt"))){for(var line:(Iterable<String>)lines::iterator){var d=new Data();active=d;d.f=JsonParser.parseString(line).getAsJsonObject();d.out=out;d.random=new RandomSource[2];for(int i=0;i<2;i++){long seed=d.f.getAsJsonArray("seeds").get(i).getAsLong();d.random[i]=d.f.get("xor").getAsBoolean()?new XoroshiroRandomSource(seed):RandomSource.create(seed);}var hashes=new JsonArray();var identities=new JsonArray();for(var hash:d.f.getAsJsonArray("hashes")){var h=Holder.<MobEffect>direct(new Effect(d.keys.size(),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;d.other=make(net.minecraft.world.entity.decoration.ArmorStand.class);activeEffects.set(d.actor,new HashMap<Holder<MobEffect>,MobEffectInstance>());d.levels=new Level[2];for(int i=0;i<2;i++){if(d.f.getAsJsonArray("servers").get(i).getAsBoolean()){var l=make(EffectLevel.class);l.d=d;l.id=i;d.levels[i]=l;}else{var l=make(ClientEffectLevel.class);l.d=d;l.id=i;d.levels[i]=l;}}var dataItems=new SynchedEntityData.DataItem<?>[Math.max(particleKey.id(),ambienceKey.id())+1];for(int i=0;i<dataItems.length;i++)dataItems[i]=new SynchedEntityData.DataItem<Byte>(new EntityDataAccessor<Byte>(i,EntityDataSerializers.BYTE),(byte)0);d.particleItem=new SynchedEntityData.DataItem<>(particleKey,List.of());d.ambienceItem=new SynchedEntityData.DataItem<>(ambienceKey,false);dataItems[particleKey.id()]=d.particleItem;dataItems[ambienceKey.id()]=d.ambienceItem;dataField.set(d.actor,dataCtor.newInstance(d.actor,dataItems));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.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 "tick"->d.actor.effectTick();
case "apply-status","remove-status","clear-status","sound"->{var list=op.getAsJsonArray("list");ConsumeEffect effect;switch(op.get("op").getAsString()){
case "apply-status"->{var selected=new ArrayList<MobEffectInstance>();for(var v:list)selected.add(d.pool[v.getAsInt()]);effect=new ApplyStatusEffectsConsumeEffect(selected,fl(op,"probability"));}
case "remove-status"->{var selected=new ArrayList<Holder<MobEffect>>();for(var v:list)selected.add(d.keys.get(v.getAsInt()));effect=new RemoveStatusEffectsConsumeEffect(HolderSet.direct(selected));}
case "clear-status"->effect=ClearAllStatusEffectsConsumeEffect.INSTANCE;
default->effect=new PlaySoundConsumeEffect(Holder.direct(n(op,"sound")==0?SoundEvents.GENERIC_EAT.value():SoundEvents.PLAYER_BURP));
}result=Boolean.toString(effect.apply(d.levels[n(op,"level")],ItemStack.EMPTY,d.actor));}
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(ConcurrentModificationException e){result="concurrent";}out.println("result "+result);d.all();}
for(int i=0;i<2;i++)out.println("random "+i+" "+d.random[i].nextLong()+" "+d.random[i].nextLong());
}}
}
}
+93
View File
@@ -0,0 +1,93 @@
#!/usr/bin/env python3
"""Compare inherited entity effect ticks, expirations, callbacks, blend and particles."""
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(0x5449434b4546)
durations=[-2147483648,-2,-1,0,1,2,599,600,601,1201,2147483647]
floats=[0,0x80000000,0x3f000000,0x3f800000,0xbf800000,0x7f800000,0xff800000,0x7fc00000]
doubles=[0,0x8000000000000000,0x3ff0000000000000,0xbff0000000000000,0x7ff0000000000000,0x7ff8000000000000]
def state(**kw):
v=dict(key=0,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=0,duration=1,hidden=-1),state(key=1,duration=601),state(key=2,duration=-1,hidden=0)],allow=True,level=0,client=False,removed=False,random=0,reseed=False,seeds=[9,-13],position=[1,2,3],category=0,tick=81,should=True,apply=True,advance=2,blend_in=4,blend_out=7,particles=[0,1,2],ambience=False,invisible=False,coordinates=[0,0x3ff0000000000000,0xbff0000000000000]);v.update(kw);return v
def op(name,a=0,b=0,**kw):
v=dict(op=name,a=a,b=b,source=-1,partial=0x3f000000,list=[0,1,2,0],probability=0x3f800000,sound=0,level=0);v.update(kw);return v
def row(**kw):
v=dict(xor=False,seeds=[1,-3],servers=[True,False],hashes=[0,1,2],initial=config(),after=config(states=[state(key=0,duration=1,amplifier=9),state(key=1,duration=-1,amplifier=2),state(key=2,duration=0,amplifier=0)],allow=True,level=1,client=True,removed=True,random=1,reseed=True,particles=[2,2,0],ambience=True,invisible=True,coordinates=[0x8000000000000000,0x7ff0000000000000,0x7ff8000000000000]),setup=[op('put',i,i) for i in range(3)],ops=[op('tick'),op('tick')],fail=-1,cme=-1,mutate=-1,mutations=[op('put',0,2)]);v.update(kw);return v
for server in [False,True]:
for should in [False,True]:
for apply in [False,True]:
for duration in durations:
for hidden in [-1,0]:
yield row(servers=[server,not server],initial=config(should=should,apply=apply,states=[state(key=0,duration=1201),state(key=1,duration=duration,hidden=hidden),state(key=2,duration=600)]),ops=[op('tick') for _ in range(4)])
for mutation in [[],[op('drop',0)],[op('clear')],[op('put',2,0)],[op('drop',1),op('put',1,2)],[op('put',0,2)]]:
for mutate in range(-1,10):
for failure in ['fail','cme']:
for fail in range(-1,10):
yield row(servers=[server,not server],mutations=mutation,mutate=mutate,**{failure:fail})
for xor in [False,True]:
for ambient in [False,True]:
for invisible in [False,True]:
for seed in range(40):
yield row(xor=xor,seeds=[seed,-seed],servers=[False,False],setup=[],initial=config(ambience=ambient,invisible=invisible),ops=[op('tick') for _ in range(4)])
for count in [0,1,2,8,9,13,25,49,64]:
for server in [False,True]:
states=[state(key=i,duration=[0,1,2,601,-1][i%5]) for i in range(count)]
yield row(xor=xor,servers=[server,server],hashes=[0 for _ in range(count)],initial=config(states=states),after=config(states=states),setup=[op('put',i,i) for i in reversed(range(count))],ops=[op('tick') for _ in range(4)],mutations=[])
for _ in range(600):
count=rng.choice([3,5,8,16]);hashes=[rng.choice([0,0,64,i*31]) for i in range(count)]
def states():return [state(key=rng.randrange(count),duration=rng.choice(durations),amplifier=rng.choice([-1,0,1,2,255,256,2147483647]),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(),allow=bool(rng.randrange(2)),level=rng.randrange(2),client=bool(rng.randrange(2)),removed=bool(rng.randrange(2)),random=rng.randrange(2),reseed=bool(rng.randrange(2)),seeds=[rng.randrange(-2**63,2**63) for _ in range(2)],position=[rng.randrange(-2**31,2**31) for _ in range(3)],category=rng.randrange(8),tick=rng.randrange(-2**31,2**31),should=bool(rng.randrange(2)),apply=bool(rng.randrange(2)),advance=rng.choice(durations),blend_in=rng.choice(durations),blend_out=rng.choice(durations),particles=[rng.randrange(3) for _ in range(rng.randrange(5))],ambience=bool(rng.randrange(2)),invisible=bool(rng.randrange(2)),coordinates=[rng.choice(doubles) for _ in range(3)])
yield row(xor=bool(rng.randrange(2)),seeds=[rng.randrange(-2**63,2**63) for _ in range(2)],servers=[bool(rng.randrange(2)) for _ in range(2)],hashes=hashes,initial=cfg(),after=cfg(),setup=[op('put',rng.randrange(count),rng.randrange(count)) for _ in range(count)],ops=[op(rng.choice(['tick']*8+['apply-status','remove-status','clear-status','sound','add','force','remove','all','get','blend','put','drop','copy','alias']),rng.randrange(count),rng.randrange(count),source=rng.randrange(-1,2),partial=rng.choice(floats),list=[rng.randrange(count) for _ in range(rng.randrange(5))],probability=rng.choice(floats),level=rng.randrange(2),sound=rng.randrange(2)) for _ in range(20)],fail=rng.randrange(-1,30),cme=rng.randrange(-1,30),mutate=rng.randrange(-1,30),mutations=[op(rng.choice(['put','drop','clear','copy','alias']),rng.randrange(count),rng.randrange(count)) 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-active-effect-tick-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/VanillaActiveEffectTickOracle.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/active_effect_tick_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.VanillaActiveEffectTickOracle',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','active_effect_tick_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'/('active_effect_tick_fixture.exe' if os.name=='nt' else 'active_effect_tick_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:,} active effect tick 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.tickEffects composed with actual MobEffectInstance ticks, active-effect HashMaps, native synced particle/ambience data and both RNG kinds. Includes removal/refresh ordering, shared hidden restoration, nonstructural replacements, structural mutations, concurrent-modification catch boundaries, client blending, particle selection and RNG tails. Selected raw instance fields and callbacks can mutate during execution. Level classes are controlled ServerLevel and non-ServerLevel implementations; random particle positions, registered effect behavior and notification side effects remain controlled. Holder identity hashes are exported per reference run. Complete entity/player lifecycle and gameplay remain pending.'}
lab.write_json(report_path,report);print(f'Scoped active effect tick parity passed: {report_path}')
if __name__=='__main__':main()