Compose native inventory and equipment ticks with shared stack state

This commit is contained in:
Ludovic Marie
2026-10-07 07:59:07 +02:00
parent 843cabc038
commit a3cecfdead
7 changed files with 716 additions and 0 deletions
@@ -0,0 +1,275 @@
{
"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": 4464,
"sources_sha256": {
"mods/horizon/src/test/java/dev/horizonlab/parity/VanillaInventoryTickOracle.java": "d816bbd310ab1e28c09e8a3952aed7b16363e02a15c46f429591fcb78fd59dad",
"tools/test_inventory_tick_parity.py": "2297ac921c8167255d9b700f246e02ba1a030571d42600e2b78aaa0c7b3d35e3",
"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/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/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": "3622244ccdee46e13956c64ffe9be6234e686adf58dbc3a24cece0f1814301a1",
"crates/horizon-world/src/compression.rs": "4db73e6ca9a0d552d415ce60e7e88b89c9cf2b7d060a669ff8328f518316e101",
"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/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_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/interaction_shapes.rs": "f321284ddab440a394372b6dedc8b5cc29a653c0745251a9e96c650f21c33dea",
"crates/horizon-world/src/inventory_pickup.rs": "ea830c7916e7f2d95c6db7ef48be1e876e6ebafcb438e47fc72f9a5bd7c37d07",
"crates/horizon-world/src/inventory_tick.rs": "293a3d57a1689337cfd58e5a73dcdbed4cb360f94d98ff5458f0df92de387365",
"crates/horizon-world/src/item_blocking.rs": "f6eee0d6d177cc3148960122327ba1b459a2f0e85241c1d9a3e40f0b8c169296",
"crates/horizon-world/src/item_durability.rs": "9fdf835e5c7bd0e1dd63ef2ba0076b770cdd4d6d14cdcd11de27690f0702f94e",
"crates/horizon-world/src/item_stack_refs.rs": "1a83604c256a51898b59b0087c2378282bcebe7883add2c4634dc7f9183d6e49",
"crates/horizon-world/src/item_stack_storage.rs": "b4dcb83dcaa346848c5484161feb3e2cf08e83f7793d7ed83be25b14b9d12884",
"crates/horizon-world/src/item_use.rs": "1d0a3afa61b60d356e81a063da79ec6cd38730315976640a591325fc4c8ab98c",
"crates/horizon-world/src/item_use_catalog.rs": "a9b53d2e816583239a75c3c61e78dacc5e3b27dee595be34b1c7fa169f17a571",
"crates/horizon-world/src/item_use_lifecycle.rs": "f9b29f42a528493d0d2521110d4f6e0bc89af8f579d539648cb7c3a9e68e31cd",
"crates/horizon-world/src/item_use_start.rs": "9996a9b90c313925cd90e78c02880f2a1d2ae65aef53f4ce081578a9dcd10c62",
"crates/horizon-world/src/lib.rs": "e211ca0595ae93b335b19f621b6db600f622191fd92d09c1e7b8588124a973fa",
"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/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_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/suffocation.rs": "bed219dd5bbab6263b98fbe3d0eb512cb96c52ae847210efe49514d05f22f11e",
"crates/horizon-world/src/swimming.rs": "49d2dae13c109b37ff832cde98a857944ff45247b864a527b9c4f03ecca7a7b7",
"crates/horizon-world/src/synced_floats.rs": "f417aab280331ddd6f0ca265a12c0130c78b9924f407c7e07f198140501e6010",
"crates/horizon-world/src/tick_data.rs": "f8d04a69ea263f4168bce33f92e5b4ee2fe07d7caf50a1621c6b3ef6eb2479f3",
"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/inventory_tick_fixture.rs": "0011f15f2015e391feb3c8d027f1a629005d467d4c2a6555618fc343532d6e3b"
},
"fixtures_sha256": {
".local/rust-inventory-tick-parity/inputs.txt": "81d8c628cb7854f5aa206937b5ddc789fb11b0eb8e8d8e8362b21987b4301b2a"
},
"rust_output_sha256": "2f11b5e52df3fb3ef4952e75a3ad3961dbbc3c9c07472c4bd92cbac30a10382f",
"references": [
{
"side": "client",
"jar_sha256": "4508d006323f24fa02876310c192d739af56516eb259000ac50f0909a68c9a2d",
"output_sha256": "2f11b5e52df3fb3ef4952e75a3ad3961dbbc3c9c07472c4bd92cbac30a10382f",
"matched_output_lines": 195957
},
{
"side": "server",
"jar_sha256": "a362163eec5d1612d520772bc16e5b39c09e3b234fdc045f56bf544284ee8ae6",
"output_sha256": "2f11b5e52df3fb3ef4952e75a3ad3961dbbc3c9c07472c4bd92cbac30a10382f",
"matched_output_lines": 195957
}
],
"scope": "Actual Inventory.tick, EntityEquipment.tick and ItemStack.inventoryTick composed over shared stacks. Custom item handlers trace server dispatch; level/item callbacks mutate counts, pop time, selection and future slot/enum-map entries or fail. Client JAR uses an actual allocated ClientLevel for the non-server branch; dedicated server uses null because ClientLevel is absent. Concrete registered item tick effects, arbitrary getItem overrides, whole entity/world ticks and complete gameplay integration remain pending."
}
@@ -0,0 +1,231 @@
use horizon_world::{
component_patch::Prototype,
inventory_tick::{self, InventoryTickWorld},
item_stack_refs::{ItemStacks, StackRef},
player_inventory::{EntityEquipment, EquipmentSlot, PlayerInventory},
};
use serde_json::Value;
use std::{
fs,
io::{self, Write},
path::Path,
};
type Stack = StackRef<u8, u8, i32>;
type Inventory = PlayerInventory<u8, u8, i32>;
fn n(v: &Value, k: &str) -> i32 {
v[k].as_i64().unwrap() as i32
}
struct World {
out: io::BufWriter<fs::File>,
f: Value,
refs: Vec<Stack>,
ids: Vec<Stack>,
empty: Stack,
level: u8,
calls: i32,
}
impl World {
fn id(&mut self, s: &Stack) -> usize {
self.ids
.iter()
.position(|v| v.same_reference(s))
.unwrap_or_else(|| {
self.ids.push(s.clone());
self.ids.len() - 1
})
}
fn snapshot(&mut self, s: &Stack) -> String {
let id = self.id(s);
let s = s.read();
format!(
"{}:{}:{}:{}:{}:{}",
id,
s.item(),
s.count,
s.count(),
s.pop_time,
s.is_empty()
)
}
fn all(&mut self, inv: &Inventory) {
for i in 0..self.refs.len() {
let s = self.snapshot(&self.refs[i].clone());
writeln!(self.out, "ref {i} {s}").unwrap();
}
writeln!(
self.out,
"state {} {} {}",
inv.selected(),
self.level,
self.calls
)
.unwrap();
let slots = (0..43)
.map(|i| self.snapshot(&inv.get(i).unwrap()))
.collect::<Vec<_>>()
.join(" | ");
writeln!(self.out, "slots {slots}").unwrap();
let entries = inv
.equipment()
.entries()
.into_iter()
.map(|(s, v)| {
format!(
"{}={}",
match s {
EquipmentSlot::MainHand => "MAINHAND",
EquipmentSlot::OffHand => "OFFHAND",
EquipmentSlot::Feet => "FEET",
EquipmentSlot::Legs => "LEGS",
EquipmentSlot::Chest => "CHEST",
EquipmentSlot::Head => "HEAD",
EquipmentSlot::Body => "BODY",
EquipmentSlot::Saddle => "SADDLE",
},
self.snapshot(&v)
)
})
.collect::<Vec<_>>()
.join(" | ");
writeln!(self.out, "equipment {entries}").unwrap();
let s = self.snapshot(&self.empty.clone());
writeln!(self.out, "empty {s}").unwrap();
}
fn event(&mut self, inv: &mut Inventory, name: &str) -> Result<(), ()> {
writeln!(self.out, "{name}").unwrap();
self.calls += 1;
self.all(inv);
if self.calls == n(&self.f, "fail_at") {
return Err(());
}
if self.calls == n(&self.f, "mutate_at") {
self.refs[n(&self.f, "mutate_ref") as usize].write().count = n(&self.f, "after_count");
self.refs[n(&self.f, "pop_ref") as usize].write().pop_time = n(&self.f, "after_pop");
if n(&self.f, "replace_slot") >= 0 {
inv.set(
n(&self.f, "replace_slot"),
self.refs[n(&self.f, "replace_ref") as usize].clone(),
)
.unwrap();
}
if n(&self.f, "equipment_slot") >= 0 {
inv.equipment().set(
EquipmentSlot::ALL[n(&self.f, "equipment_slot") as usize],
self.refs[n(&self.f, "equipment_ref") as usize].clone(),
);
}
inv.set_selected(n(&self.f, "after_selected")).unwrap();
self.level = n(&self.f, "after_level") as u8;
}
Ok(())
}
}
impl InventoryTickWorld<u8, u8, i32> for World {
type Error = ();
type Level = u8;
fn level(&mut self, inv: &mut Inventory) -> Result<u8, ()> {
self.event(inv, "level")?;
Ok(self.level)
}
fn is_server_level(&self, level: u8) -> bool {
level == 1
}
fn item_tick(
&mut self,
inv: &mut Inventory,
item: u8,
stack: Stack,
level: u8,
slot: Option<EquipmentSlot>,
) -> Result<(), ()> {
assert_eq!(level, 1);
if item != 0 {
let id = self.id(&stack);
self.event(
inv,
&format!("tick {item} {id} {}", slot.map_or(-1, |s| s as i32)),
)?;
}
Ok(())
}
}
fn main() -> Result<(), Box<dyn std::error::Error>> {
let arg = std::env::args().nth(1).unwrap();
let dir = Path::new(&arg);
let mut out = io::BufWriter::new(fs::File::create(dir.join("rust.txt"))?);
for line in fs::read_to_string(dir.join("inputs.txt"))?.lines() {
let f: Value = serde_json::from_str(line)?;
let stacks = ItemStacks::new(0);
let empty = stacks.empty();
let refs: Vec<_> = f["stacks"]
.as_array()
.unwrap()
.iter()
.map(|v| {
let item = n(v, "item");
if item < 0 {
empty.clone()
} else {
let s = stacks.create(item as u8, n(v, "count"), Prototype::new([]));
s.write().pop_time = n(v, "pop");
s
}
})
.collect();
let equipment = EntityEquipment::new(empty.clone());
let mut inv = Inventory::new(stacks, equipment);
inv.set_selected(n(&f, "selected")).unwrap();
for p in f["slots"].as_array().unwrap() {
inv.set(
p[0].as_i64().unwrap() as i32,
refs[p[1].as_u64().unwrap() as usize].clone(),
)
.unwrap();
}
for p in f["equipment"].as_array().unwrap() {
inv.equipment().set(
EquipmentSlot::ALL[p[0].as_u64().unwrap() as usize],
refs[p[1].as_u64().unwrap() as usize].clone(),
);
}
let mut w = World {
out,
f: f.clone(),
refs,
ids: vec![empty.clone()],
empty,
level: n(&f, "level") as u8,
calls: 0,
};
w.all(&inv);
let result = match f["mode"].as_str().unwrap() {
"inventory" => inventory_tick::tick_inventory(&mut w, &mut inv),
"equipment" => inventory_tick::tick_equipment(&mut w, &mut inv),
"both" => inventory_tick::tick_inventory(&mut w, &mut inv)
.and_then(|()| inventory_tick::tick_equipment(&mut w, &mut inv)),
"stack" => {
let stack = w.refs[n(&f, "argument") as usize].clone();
inventory_tick::tick_stack(
&mut w,
&mut inv,
stack,
n(&f, "argument_level") as u8,
if n(&f, "slot") < 0 {
None
} else {
Some(EquipmentSlot::ALL[n(&f, "slot") as usize])
},
)
}
_ => panic!("mode"),
};
writeln!(
w.out,
"result {}",
if result.is_ok() { "ok" } else { "error" }
)?;
w.all(&inv);
out = w.out;
}
Ok(())
}
@@ -0,0 +1,87 @@
//! Inventory/equipment ticking composed with ItemStack's pop-time update and
//! server-only item dispatch. All references are captured at native boundaries.
use crate::{
item_stack_refs::StackRef,
player_inventory::{EquipmentSlot, PlayerInventory},
};
pub trait InventoryTickWorld<I, K, V> {
type Error;
type Level: Copy;
fn level(
&mut self,
inventory: &mut PlayerInventory<I, K, V>,
) -> Result<Self::Level, Self::Error>;
/// This is a ServerLevel type test, not Level.isClientSide().
fn is_server_level(&self, level: Self::Level) -> bool;
fn item_tick(
&mut self,
inventory: &mut PlayerInventory<I, K, V>,
item: I,
stack: StackRef<I, K, V>,
level: Self::Level,
slot: Option<EquipmentSlot>,
) -> Result<(), Self::Error>;
}
pub fn tick_stack<
I: Copy + Eq,
K: Clone + Eq,
V: Clone + PartialEq,
W: InventoryTickWorld<I, K, V>,
>(
w: &mut W,
inventory: &mut PlayerInventory<I, K, V>,
stack: StackRef<I, K, V>,
level: W::Level,
slot: Option<EquipmentSlot>,
) -> Result<(), W::Error> {
{
let mut data = stack.write();
if data.pop_time > 0 {
data.pop_time = data.pop_time.wrapping_sub(1);
}
}
if w.is_server_level(level) {
let item = stack.read().item();
w.item_tick(inventory, item, stack, level, slot)?;
}
Ok(())
}
pub fn tick_inventory<
I: Copy + Eq,
K: Clone + Eq,
V: Clone + PartialEq,
W: InventoryTickWorld<I, K, V>,
>(
w: &mut W,
inventory: &mut PlayerInventory<I, K, V>,
) -> Result<(), W::Error> {
for index in 0..36 {
let stack = inventory.get(index).expect("ordinary slot");
if !stack.read().is_empty() {
let level = w.level(inventory)?;
let slot = (index as usize == inventory.selected()).then_some(EquipmentSlot::MainHand);
tick_stack(w, inventory, stack, level, slot)?;
}
}
Ok(())
}
pub fn tick_equipment<
I: Copy + Eq,
K: Clone + Eq,
V: Clone + PartialEq,
W: InventoryTickWorld<I, K, V>,
>(
w: &mut W,
inventory: &mut PlayerInventory<I, K, V>,
) -> Result<(), W::Error> {
// EnumMap iteration is weakly consistent. Read each entry when reached so
// writes to later slots during a callback are visible in this same tick.
for slot in EquipmentSlot::ALL {
let stack = inventory.equipment().get(slot);
if !stack.read().is_empty() {
let level = w.level(inventory)?;
tick_stack(w, inventory, stack, level, Some(slot))?;
}
}
Ok(())
}
+2
View File
@@ -100,3 +100,5 @@ pub mod simple_container;
pub mod player_inventory;
pub mod inventory_pickup;
pub mod inventory_tick;
+17
View File
@@ -1700,3 +1700,20 @@ copy-and-clear that happens before slot validation. Targeted Clippy passes.
Native crash-report wrapping is represented as an error; diagnostic text is not
compared. Item-entity pickup events, networking, inventory ticks, menus, full
components and complete gameplay integration remain pending.
`inventory_tick` composes inventory/equipment iteration with ItemStack's pop-time
update and ServerLevel-only item dispatch. It retains stack arguments across
level callbacks, reads hotbar selection at the native point and observes changes
to later equipment entries during the same tick. Aliased stacks can be visited
more than once, with each visit updating their shared pop-time counter.
`python3 tools/test_inventory_tick_parity.py` compares actual Inventory.tick,
EntityEquipment.tick and ItemStack.inventoryTick with custom item handlers. The
[tick report](../benchmarks/rust-inventory-tick-2026-10-07/scoped-parity.json)
records **195,957 matching lines per JAR**, including empty stacks, callbacks
that mutate later slots, weakly consistent equipment iteration and failure
prefixes. The client comparison uses an actual allocated ClientLevel for the
non-server branch; the dedicated JAR uses null because ClientLevel is absent.
Targeted Clippy passes. Registered items' individual tick effects, full entity
and world ticking and complete gameplay integration remain pending.
@@ -0,0 +1,28 @@
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.*;import net.minecraft.core.component.*;import net.minecraft.world.item.*;import net.minecraft.world.entity.*;import net.minecraft.world.entity.player.*;import net.minecraft.world.level.Level;import net.minecraft.server.level.ServerLevel;
/** Native inventory/equipment loops composed with actual ItemStack.inventoryTick. */
public final class VanillaInventoryTickOracle {
static final class Stop extends RuntimeException {}
static Object unsafe;static Method allocate;static Field count,eqItems;static Constructor<ItemStack> ctor;
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 o,String k){return o.get(k).getAsInt();}
static final class Data {
PrintWriter out;JsonObject f;ItemStack[] refs;Item[] types;Inventory inv;EntityEquipment equipment;Actor actor;Level[] levels;int level,calls;IdentityHashMap<ItemStack,Integer> ids=new IdentityHashMap<>();
int type(Item item){for(int i=0;i<types.length;i++)if(types[i]==item)return i;throw new AssertionError();}
int id(ItemStack s){return ids.computeIfAbsent(s,k->ids.size());}
String snapshot(ItemStack s){try{return id(s)+":"+type(s.getItem())+":"+count.getInt(s)+":"+s.getCount()+":"+s.getPopTime()+":"+s.isEmpty();}catch(Exception e){throw new AssertionError(e);}}
void all(){try{for(int i=0;i<refs.length;i++)out.println("ref "+i+" "+snapshot(refs[i]));out.println("state "+inv.getSelectedSlot()+" "+level+" "+calls);var slots=new StringJoiner(" | ");for(int i=0;i<43;i++)slots.add(snapshot(inv.getItem(i)));out.println("slots "+slots);var entries=new StringJoiner(" | ");for(var e:((EnumMap<EquipmentSlot,ItemStack>)eqItems.get(equipment)).entrySet())entries.add(e.getKey().name()+"="+snapshot(e.getValue()));out.println("equipment "+entries);out.println("empty "+snapshot(ItemStack.EMPTY));}catch(Exception e){throw new AssertionError(e);}}
void event(String name){out.println(name);calls++;all();if(calls==n(f,"fail_at"))throw new Stop();if(calls==n(f,"mutate_at")){refs[n(f,"mutate_ref")].setCount(n(f,"after_count"));refs[n(f,"pop_ref")].setPopTime(n(f,"after_pop"));if(n(f,"replace_slot")>=0)inv.setItem(n(f,"replace_slot"),refs[n(f,"replace_ref")]);if(n(f,"equipment_slot")>=0)equipment.set(EquipmentSlot.values()[n(f,"equipment_slot")],refs[n(f,"equipment_ref")]);inv.setSelectedSlot(n(f,"after_selected"));level=n(f,"after_level");}}
}
static final class Actor extends Player {Data d;Actor(){super(null,null);}@Override public net.minecraft.world.level.GameType gameMode(){return net.minecraft.world.level.GameType.SURVIVAL;}@Override public boolean isSpectator(){return false;}@Override public boolean isCreative(){return false;}@Override public Level level(){d.event("level");return d.levels[d.level];}}
static final class TickItem extends Item {Data d;int type;TickItem(){super(null);}@Override public void inventoryTick(ItemStack s,ServerLevel level,Entity owner,EquipmentSlot slot){if(owner!=d.actor||level!=d.levels[1])throw new AssertionError();d.event("tick "+type+" "+d.id(s)+" "+(slot==null?-1:slot.ordinal()));}}
@SuppressWarnings("unchecked")public static void main(String[] args)throws Exception {
net.minecraft.SharedConstants.tryDetectVersion();net.minecraft.server.Bootstrap.bootStrap();var u=Class.forName("sun.misc.Unsafe");unsafe=field(u,"theUnsafe").get(null);allocate=u.getMethod("allocateInstance",Class.class);count=field(ItemStack.class,"count");eqItems=field(EntityEquipment.class,"items");ctor=ItemStack.class.getDeclaredConstructor(Holder.class,int.class,PatchedDataComponentMap.class);ctor.setAccessible(true);
// ClientLevel is absent from the dedicated JAR. Null exercises the same
// non-ServerLevel branch there; client comparisons use an actual ClientLevel.
Level client=null;try{client=(Level)allocate.invoke(unsafe,Class.forName("net.minecraft.client.multiplayer.ClientLevel"));}catch(ClassNotFoundException ignored){}
Path dir=Path.of(args[1]);try(var out=new PrintWriter(Files.newBufferedWriter(dir.resolve(args[0]+".txt")));var lines=Files.lines(dir.resolve("inputs.txt"))){for(var line:(Iterable<String>)lines::iterator){var d=new Data();d.out=out;d.f=JsonParser.parseString(line).getAsJsonObject();d.ids.put(ItemStack.EMPTY,0);ItemStack.EMPTY.setCount(0);ItemStack.EMPTY.setPopTime(0);d.actor=make(Actor.class);d.actor.d=d;d.levels=new Level[]{client,make(ServerLevel.class)};d.level=n(d.f,"level");var first=make(TickItem.class);first.d=d;first.type=1;var second=make(TickItem.class);second.d=d;second.type=2;d.types=new Item[]{Items.AIR,first,second};d.refs=new ItemStack[4];for(int i=0;i<4;i++){var v=d.f.getAsJsonArray("stacks").get(i).getAsJsonObject();int item=n(v,"item");d.refs[i]=item<0?ItemStack.EMPTY:ctor.newInstance(Holder.direct(d.types[item]),n(v,"count"),new PatchedDataComponentMap(DataComponentMap.EMPTY));if(item>=0)d.refs[i].setPopTime(n(v,"pop"));}d.equipment=new EntityEquipment();d.inv=new Inventory(d.actor,d.equipment);d.inv.setSelectedSlot(n(d.f,"selected"));for(var e:d.f.getAsJsonArray("slots")){var p=e.getAsJsonArray();d.inv.setItem(p.get(0).getAsInt(),d.refs[p.get(1).getAsInt()]);}for(var e:d.f.getAsJsonArray("equipment")){var p=e.getAsJsonArray();d.equipment.set(EquipmentSlot.values()[p.get(0).getAsInt()],d.refs[p.get(1).getAsInt()]);}d.all();try{switch(d.f.get("mode").getAsString()){case "inventory":d.inv.tick();break;case "equipment":d.equipment.tick(d.actor);break;case "both":d.inv.tick();d.equipment.tick(d.actor);break;case "stack":int slot=n(d.f,"slot");d.refs[n(d.f,"argument")].inventoryTick(d.levels[n(d.f,"argument_level")],d.actor,slot<0?null:EquipmentSlot.values()[slot]);break;default:throw new AssertionError();}out.println("result ok");}catch(Stop e){out.println("result error");}d.all();}}
}
}
+76
View File
@@ -0,0 +1,76 @@
#!/usr/bin/env python3
"""Compare native InventoryTick transfers, shared stack identity and change callbacks."""
import argparse
import json
import os
from pathlib import Path
import platform
import subprocess
import random
import struct
import math
try:
from . import lab, test_rust_parity as base
except ImportError:
import lab
import test_rust_parity as base
def corpus():
edges=[-2147483648,-1,0,1,2,5,2147483647]
def row(mode='both',**kw):
v=dict(mode=mode,stacks=[dict(item=1,count=2,pop=5),dict(item=2,count=1,pop=2),dict(item=1,count=1,pop=0),dict(item=-1,count=0,pop=0)],slots=[[0,0],[1,0],[8,1],[35,2]],equipment=[[0,0],[1,1],[7,2]],selected=0,level=1,argument=0,argument_level=1,slot=-1,fail_at=0,mutate_at=0,mutate_ref=0,after_count=1,pop_ref=1,after_pop=5,replace_slot=8,replace_ref=2,equipment_slot=7,equipment_ref=0,after_selected=8,after_level=1);v.update(kw);return v
for mode in ['inventory','equipment','both','stack']:
for item in [-1,0,1,2]:
for count in edges:
for pop in edges:
for level in [0,1]:
v=row(mode,level=level,argument_level=level)
v['stacks'][0]=dict(item=item,count=count,pop=pop)
yield v
for mode in ['inventory','equipment','both','stack']:
for fail in range(1,9):
for mutate in range(1,9):
for count in [-1,0,2]:yield row(mode,fail_at=fail,mutate_at=mutate,after_count=count)
# Inserts into an unpopulated EnumMap entry after iteration has begun.
for initial in range(8):
for inserted in range(8):
for mutate in [1,2]:yield row('equipment',equipment=[[initial,0]],equipment_slot=inserted,mutate_at=mutate)
rng=random.Random(0x494e565449434b)
for _ in range(2000):
v=row(rng.choice(['inventory','equipment','both','stack']),stacks=[dict(item=rng.randrange(-1,3),count=rng.choice(edges),pop=rng.choice(edges)) for __ in range(4)],slots=[[i,rng.randrange(4)] for i in rng.sample(range(43),rng.randrange(44))],equipment=[[i,rng.randrange(4)] for i in rng.sample(range(8),rng.randrange(9))],selected=rng.randrange(9),level=rng.randrange(2),argument=rng.randrange(4),argument_level=rng.randrange(2),slot=rng.randrange(-1,8),fail_at=rng.randrange(10),mutate_at=rng.randrange(10),mutate_ref=rng.randrange(4),after_count=rng.choice(edges),pop_ref=rng.randrange(4),after_pop=rng.choice(edges),replace_slot=rng.randrange(-1,43),replace_ref=rng.randrange(4),equipment_slot=rng.randrange(-1,8),equipment_ref=rng.randrange(4),after_selected=rng.randrange(9),after_level=rng.randrange(2))
yield v
def main():
parser=argparse.ArgumentParser(description=__doc__);parser.add_argument('--prepare',action='store_true');args=parser.parse_args()
build=lab.LOCAL/'rust-inventory-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/VanillaInventoryTickOracle.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/inventory_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.VanillaInventoryTickOracle',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','inventory_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'/('inventory_tick_fixture.exe' if os.name=='nt' else 'inventory_tick_fixture')
run([binary,build],'rust');references=[]
for side,jar in jars.items():
java(side,side,build);reference=build/f'{side}.txt';matched=base.compare(reference,build/'rust.txt')
references.append({'side':side,'jar_sha256':lab.digest(jar,'sha256'),'output_sha256':lab.digest(reference,'sha256'),'matched_output_lines':matched})
print(f'{side}: {matched:,} inventory 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,'rust_output_sha256':lab.digest(build/'rust.txt','sha256'),'references':references,'scope':'Actual Inventory.tick, EntityEquipment.tick and ItemStack.inventoryTick composed over shared stacks. Custom item handlers trace server dispatch; level/item callbacks mutate counts, pop time, selection and future slot/enum-map entries or fail. Client JAR uses an actual allocated ClientLevel for the non-server branch; dedicated server uses null because ClientLevel is absent. Concrete registered item tick effects, arbitrary getItem overrides, whole entity/world ticks and complete gameplay integration remain pending.'}
lab.write_json(report_path,report);print(f'Scoped inventory tick parity passed: {report_path}')
if __name__=='__main__':main()