Deliver consumed item remainders through player inventory pickup

This commit is contained in:
Ludovic Marie committed 2026-10-07 09:31:23 +02:00
1 parent 0c85e0f5c2
commit fda79cc3db
7 files changed
+889

No files matched your search

@@ -0,0 +1,287 @@
{
"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": 6708,
"sources_sha256": {
"mods/horizon/src/test/java/dev/horizonlab/parity/VanillaExtraItemsOracle.java": "aa0c08b1f0228255ef93f9162bb18cc8f42c76f42b2dc9a59aef712b738df77f",
"tools/test_extra_items_parity.py": "e955aabafd54130ff0bcd8633fc507e3e1fa9dc40416f53d097fc9392ecc3a90",
"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/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/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_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": "a3ad79b1a4ea171726f07b1fc6e2b78f224157e8f0d53dd2ce96e93ccd2d38dd",
"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_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": "6bd97d87f0e3eaad440162145a1515b9a0b7cfb85cb0ea337874ed9745f8dd45",
"crates/horizon-world/src/lib.rs": "f00ba292455970b272680f073a878e29e1cbe1996ad0dcef17596b3ee99f5e5d",
"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/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/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/extra_items_fixture.rs": "84c269be3c83bc93e72f9c3c9da01474e80d20a8f383ebf426111630701d6fd7"
},
"fixtures_sha256": {
".local/rust-extra-items-parity/inputs.txt": "29dc3f0f6006102f75008b1c73b62d5dffd8c07e40608580619438f215d3fa72"
},
"rust_output_sha256": "16470d61d827eda49a1d589908582e8431ed026f89faa4b57eeac9766c211c13",
"references": [
{
"side": "client",
"jar_sha256": "4508d006323f24fa02876310c192d739af56516eb259000ac50f0909a68c9a2d",
"output_sha256": "16470d61d827eda49a1d589908582e8431ed026f89faa4b57eeac9766c211c13",
"matched_output_lines": 272124
},
{
"side": "server",
"jar_sha256": "a362163eec5d1612d520772bc16e5b39c09e3b234fdc045f56bf544284ee8ae6",
"output_sha256": "16470d61d827eda49a1d589908582e8431ed026f89faa4b57eeac9766c211c13",
"matched_output_lines": 272124
}
],
"scope": "Actual ItemStack.finishUsingItem with validated remainders and cooldowns now invokes inherited ServerPlayer.handleExtraItemsCreatedOnUse and real Inventory/PlayerEquipment insertion. Shared item/component references, occupied/aliased slots, offhand merging, creative material queries, partial insertion and the drop fallback are compared through mutation/failure prefixes. The drop call verifies throwRandomly=false and predicted semantics; its entity-spawn body is controlled. Rust composes item-after-use and inventory pickup. Item handlers, full contained schemas, dropped-entity creation, complete use lifecycle and full gameplay remain pending."
}
@@ -0,0 +1,446 @@
use horizon_world::{
component_patch::{Patch, Prototype},
identifier::Identifier,
item_after_use::{self, AfterUseWorld},
item_cooldowns::{self, CooldownUser, ItemCooldowns, Observer, UseCooldown},
item_stack_refs::{ItemStacks, StackRef},
item_stack_storage::ItemStackData,
item_template::{ItemTemplate, ValidationRules},
};
use horizon_world::{
extra_items::{self, ExtraItemHooks},
inventory_pickup::{PickupError, PickupHooks},
player_inventory::{EntityEquipment, InventoryRules, PlayerInventory},
};
use serde_json::Value;
use std::{
fs,
io::{self, Write},
path::Path,
};
#[derive(Clone, Debug, PartialEq)]
enum Component {
Integer(i32),
Remainder(ItemTemplate<usize, u8, Component>),
Cooldown(UseCooldown<Identifier>),
}
type Stack = StackRef<usize, u8, Component>;
type StackData = ItemStackData<usize, u8, Component>;
type Template = ItemTemplate<usize, u8, Component>;
type Book = ItemCooldowns<Identifier>;
type Inventory = PlayerInventory<usize, u8, Component>;
fn n(v: &Value, k: &str) -> i32 {
v[k].as_i64().unwrap() as i32
}
fn integer(v: Option<&Component>) -> Option<i32> {
match v {
Some(Component::Integer(n)) => Some(*n),
_ => None,
}
}
fn bits(f: f32) -> u32 {
if f.is_nan() { 0x7fc00000 } else { f.to_bits() }
}
fn cooldown(v: &Value) -> Option<UseCooldown<Identifier>> {
v["cooldown"].as_u64().map(|b| UseCooldown {
seconds: f32::from_bits(b as u32),
group: usize::try_from(n(v, "group"))
.ok()
.map(|i| Identifier::parse(&format!("test:group{i}")).unwrap()),
})
}
fn template(v: &Value) -> Option<Template> {
v["remainder"].as_i64().map(|count| {
let mut p = Vec::new();
if let Some(m) = v["maximum"].as_i64() {
p.push((0, Some(Component::Integer(m as i32))));
}
if v["damage"].as_bool().unwrap() {
p.push((1, Some(Component::Integer(0))));
}
Template::new(2, 0, count as i32, Patch::new(p)).unwrap()
})
}
fn components(stack: &Stack, v: &Value) {
let mut s = stack.write();
s.components
.set(2, template(v).map(Component::Remainder))
.unwrap();
s.components
.set(3, cooldown(v).map(Component::Cooldown))
.unwrap();
}
struct Data {
f: Value,
out: io::BufWriter<fs::File>,
refs: [Stack; 3],
extra: Option<Stack>,
original: Option<Stack>,
result: Option<Stack>,
dropped: Option<Stack>,
changed: bool,
calls: i32,
prototypes: [Prototype<u8, Component>; 3],
store: ItemStacks<usize, u8, Component>,
}
impl Data {
fn config(&self) -> &Value {
&self.f[if self.changed { "after" } else { "initial" }]
}
fn snapshot(&self, s: Option<&Stack>) -> String {
let Some(s) = s else {
return "none".into();
};
let d = s.read();
let remainder = match d.component(&2) {
Some(Component::Remainder(t)) => format!(
"{}:{}",
t.count(),
integer(t.component(&self.prototypes[t.item()], &0)).unwrap_or(1)
),
_ => "none".into(),
};
let cooldown = match d.component(&3) {
Some(Component::Cooldown(c)) => format!(
"{}:{}",
bits(c.seconds),
c.group.as_ref().map_or("none".into(), ToString::to_string)
),
_ => "none".into(),
};
format!(
"{} {} {} {} {} {} {}",
s.same_reference(&self.store.empty()),
d.item(),
d.count,
d.count(),
d.pop_time,
remainder,
cooldown
)
}
fn all(&mut self, book: &Book, inventory: &Inventory) {
writeln!(
self.out,
"state {} {} {}",
self.calls, self.changed, book.tick_count
)
.unwrap();
let slots = [
Some(self.refs[0].clone()),
Some(self.refs[1].clone()),
Some(self.refs[2].clone()),
self.extra.clone(),
self.original.clone(),
self.result.clone(),
self.dropped.clone(),
];
for (i, s) in slots.iter().enumerate() {
writeln!(self.out, "ref {i} {}", self.snapshot(s.as_ref())).unwrap();
}
let mut same = Vec::new();
for a in 0..slots.len() {
for b in a + 1..slots.len() {
same.push((slots[a] == slots[b]).to_string());
}
}
writeln!(self.out, "same {}", same.join(",")).unwrap();
let entries = book
.entries()
.map(|(k, v)| format!("{k}={}:{}", v.start, v.end))
.collect::<Vec<_>>()
.join(",");
writeln!(self.out, "cooldowns {entries}").unwrap();
let mut contents = Vec::new();
for i in 0..43 {
let item = inventory.get(i).unwrap();
if !item.same_reference(&self.store.empty()) {
let aliases = slots
.iter()
.map(|v| {
v.as_ref()
.is_some_and(|r| r.same_reference(&item))
.to_string()
})
.collect::<Vec<_>>()
.join(",");
contents.push(format!("{i}={} [{aliases}]", self.snapshot(Some(&item))));
}
}
writeln!(
self.out,
"inventory {} | {}",
inventory.selected(),
contents.join(" | ")
)
.unwrap();
}
fn reset(&mut self, book: &mut Book, inventory: &mut Inventory) {
let c = self.config().clone();
self.prototypes[2] = Prototype::new([(0, Component::Integer(n(&c, "prototype_max")))]);
for i in 0..2 {
let v = &c["items"][i];
self.refs[i].write().count = n(v, "count");
components(&self.refs[i], v);
}
book.tick_count = n(&c, "clock");
inventory.clear();
inventory.set_selected(n(&c, "selected")).unwrap();
for pair in c["slots"].as_array().unwrap() {
inventory
.set(
pair[0].as_i64().unwrap() as i32,
self.refs[pair[1].as_u64().unwrap() as usize].clone(),
)
.unwrap();
}
if let Some(extra) = &self.extra {
extra.write().count = n(&c, "extra_count");
}
if let Some(original) = &self.original {
original.write().count = n(&c, "original_count");
components(original, &c["items"][1]);
}
}
fn event(
&mut self,
book: &mut Book,
inventory: &mut Inventory,
name: String,
) -> Result<(), &'static str> {
writeln!(self.out, "event {name}").unwrap();
let index = self.calls;
self.calls += 1;
self.all(book, inventory);
if index == n(&self.f, "fail") {
return Err("callback");
}
if index == n(&self.f, "mutate") {
self.changed = true;
self.reset(book, inventory);
}
Ok(())
}
}
struct Observe<'a> {
data: &'a mut Data,
inventory: &'a mut Inventory,
}
impl Observer<Identifier> for Observe<'_> {
type Error = &'static str;
fn started(&mut self, b: &mut Book, g: Identifier, d: i32) -> Result<(), Self::Error> {
self.data
.event(b, self.inventory, format!("started {g} {d}"))
}
fn ended(&mut self, _: &mut Book, _: Identifier) -> Result<(), Self::Error> {
unreachable!()
}
}
struct Pickup<'a> {
data: &'a mut Data,
book: &'a mut Book,
}
impl InventoryRules<usize, u8, Component> for Pickup<'_> {
fn is_enchanted(&self, _: &Stack) -> bool {
false
}
fn is_damaged(&self, _: &Stack) -> bool {
false
}
fn has_custom_name(&self, _: &Stack) -> bool {
false
}
}
impl PickupHooks<usize, u8, Component> for Pickup<'_> {
type Error = &'static str;
fn item_max_stack_size(&self, s: &Stack) -> i32 {
integer(s.read().component(&0)).unwrap_or(1)
}
fn max_stack_size(&mut self, _: &mut Inventory) -> Result<i32, Self::Error> {
Ok(99)
}
fn infinite_materials(&mut self, inv: &mut Inventory) -> Result<bool, Self::Error> {
self.data.event(self.book, inv, "infinite".into())?;
Ok(self.data.config()["infinite"].as_bool().unwrap())
}
}
impl ExtraItemHooks<usize, u8, Component> for Pickup<'_> {
fn drop_predicted(&mut self, inv: &mut Inventory, s: Stack) -> Result<(), Self::Error> {
self.data.dropped = Some(s);
self.data.event(self.book, inv, "drop".into())
}
}
struct World {
data: Data,
book: Book,
inventory: Inventory,
}
impl ValidationRules<usize, u8, Component> for World {
type ContentsError = std::convert::Infallible;
fn has_max_damage(&self, s: &StackData) -> bool {
s.component(&1).is_some()
}
fn max_stack_size(&self, s: &StackData) -> i32 {
integer(s.component(&0)).unwrap_or(1)
}
fn validate_contents(&self, _: &StackData) -> Result<(), Self::ContentsError> {
Ok(())
}
}
impl CooldownUser for World {
type Error = &'static str;
type Stack = Stack;
type Manager = ();
fn is_player(&self) -> bool {
self.data.f["player"].as_bool().unwrap()
}
fn cooldowns(&mut self) -> Result<(), Self::Error> {
self.data
.event(&mut self.book, &mut self.inventory, "get-cooldowns".into())
}
fn add_cooldown(&mut self, _: (), stack: Stack, ticks: i32) -> Result<(), Self::Error> {
self.data.original = Some(stack.clone());
self.data.event(
&mut self.book,
&mut self.inventory,
format!("add-cooldown {ticks}"),
)?;
let group = {
let s = stack.read();
let c = match s.component(&3) {
Some(Component::Cooldown(c)) => Some(c),
_ => None,
};
item_cooldowns::group(c, || {
Identifier::parse(if s.item() == 2 {
"minecraft:stick"
} else {
"minecraft:air"
})
.unwrap()
})
};
self.book.add(
group,
ticks,
&mut Observe {
data: &mut self.data,
inventory: &mut self.inventory,
},
)
}
}
impl AfterUseWorld<usize, u8, Component> for World {
type Error = &'static str;
type Level = ();
type Group = Identifier;
fn remainder(&self, s: &StackData) -> Option<Template> {
match s.component(&2) {
Some(Component::Remainder(t)) => Some(t.clone()),
_ => None,
}
}
fn cooldown(&self, s: &StackData) -> Option<UseCooldown<Identifier>> {
match s.component(&3) {
Some(Component::Cooldown(c)) => Some(c.clone()),
_ => None,
}
}
fn prototype(&self, item: usize) -> Prototype<u8, Component> {
self.data.prototypes[item].clone()
}
fn finish_item(&mut self, s: Stack, _: ()) -> Result<Stack, Self::Error> {
let kind = s.read().item();
if kind == 1 {
self.data
.event(&mut self.book, &mut self.inventory, "finish".into())?;
Ok(self.data.refs[n(self.data.config(), "finish") as usize].clone())
} else {
Ok(s)
}
}
fn infinite_materials(&mut self) -> Result<bool, Self::Error> {
self.data
.event(&mut self.book, &mut self.inventory, "infinite".into())?;
Ok(self.data.config()["infinite"].as_bool().unwrap())
}
fn extra_item(&mut self, s: Stack) -> Result<(), Self::Error> {
self.data.extra = Some(s.clone());
self.data
.event(&mut self.book, &mut self.inventory, "extra".into())?;
if self.data.f["player"].as_bool().unwrap() {
extra_items::deliver(
&mut self.inventory,
s,
&mut Pickup {
data: &mut self.data,
book: &mut self.book,
},
)
.map_err(|e| match e {
PickupError::Callback(e) => e,
PickupError::Inventory(_) => "callback",
})?;
}
Ok(())
}
fn apply_cooldown(
&mut self,
c: UseCooldown<Identifier>,
original: Stack,
) -> Result<(), Self::Error> {
c.apply(self, original)
}
}
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 store = ItemStacks::new(0);
let prototypes = [
Prototype::new([]),
Prototype::new([]),
Prototype::new([(0, Component::Integer(n(&f["initial"], "prototype_max")))]),
];
let refs = [
store.create(1, 1, prototypes[1].clone()),
store.create(2, 1, prototypes[2].clone()),
store.empty(),
];
refs[0].write().pop_time = 5;
refs[1].write().pop_time = 7;
let mut w = World {
data: Data {
f,
out,
refs,
extra: None,
original: None,
result: None,
dropped: None,
changed: false,
calls: 0,
prototypes,
store: store.clone(),
},
book: Book::default(),
inventory: Inventory::new(store.clone(), EntityEquipment::new(store.empty())),
};
w.data.reset(&mut w.book, &mut w.inventory);
w.data.all(&w.book, &w.inventory);
let source = w.data.refs[n(&w.data.f, "source") as usize].clone();
let result = item_after_use::finish(&store, &mut w, source, ());
match result {
Ok(s) => {
w.data.result = Some(s);
writeln!(w.data.out, "result ok")?;
}
Err(e) => writeln!(w.data.out, "result {e}")?,
}
w.data.all(&w.book, &w.inventory);
out = w.data.out;
}
Ok(())
}
+26
View File
@@ -0,0 +1,26 @@
//! Server-player delivery of extra items created by consuming an item.
use crate::{
inventory_pickup::{self, PickupError, PickupHooks},
item_stack_refs::StackRef,
player_inventory::PlayerInventory,
};
pub trait ExtraItemHooks<I, K, V>: PickupHooks<I, K, V> {
/// Native drop uses throwRandomly=false and Prediction::PREDICTED.
fn drop_predicted(
&mut self,
inventory: &mut PlayerInventory<I, K, V>,
stack: StackRef<I, K, V>,
) -> Result<(), Self::Error>;
}
pub fn deliver<I: Copy + Eq, K: Clone + Eq, V: Clone + PartialEq, H: ExtraItemHooks<I, K, V>>(
inventory: &mut PlayerInventory<I, K, V>,
stack: StackRef<I, K, V>,
hooks: &mut H,
) -> Result<(), PickupError<H::Error>> {
if !inventory_pickup::add(inventory, -1, &stack, hooks)? {
hooks
.drop_predicted(inventory, stack)
.map_err(PickupError::Callback)?;
}
Ok(())
}
+1
View File
@@ -35,6 +35,7 @@ pub mod entity_fluid;
pub mod entity_fluid_tick;
pub mod entity_move;
pub mod entity_obstruction;
pub mod extra_items;
pub mod fall_damage;
pub mod fluid;
pub mod fluid_box;
+17
View File
@@ -1920,3 +1920,20 @@ copies, empty aliases and mutations/failures at each effect boundary. The
retains **353,264 matching lines per JAR**. Targeted Clippy passes. Item handlers
and actor extra-item delivery remain controlled; concrete consumables, full
inventory-use binding, contained schemas and complete gameplay remain pending.
`extra_items::deliver` connects server-player remainder delivery to actual
inventory pickup. Failed insertion reaches the predicted drop service with the
same remaining stack; successful insertion retains native count consumption and
slot aliases. The drop service represents throwRandomly=false and
Prediction::PREDICTED.
`python3 tools/test_extra_items_parity.py` composes native item completion,
remainders and cooldowns with inherited ServerPlayer extra-item handling and
real Inventory/PlayerEquipment. The
[delivery report](../benchmarks/rust-extra-items-2026-10-07/scoped-parity.json)
records **272,124 matching lines per JAR**, including full/partially filled
inventories, offhand merging, aliased slots, creative material queries, partial
insertion and callback failures. Targeted Clippy passes. Drop arguments are
verified, but entity spawning remains controlled. Concrete item handlers, full
contained schemas, complete use lifecycle and full gameplay remain pending.
@@ -0,0 +1,29 @@
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.resources.Identifier;import net.minecraft.world.item.*;import net.minecraft.world.item.component.*;import net.minecraft.world.entity.*;import net.minecraft.world.entity.player.Player;import net.minecraft.world.level.*;
/** Real ItemStack.finishUsingItem, UseRemainder/template and UseCooldown/ItemCooldowns composition. */
public final class VanillaExtraItemsOracle {
static Object unsafe;static Method allocate;static Field count,tick,map,start,end;static Constructor<ItemStack> stackCtor;
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 final class Stop extends RuntimeException{}
static UseCooldown cooldown(JsonObject v){return v.get("cooldown").isJsonNull()?null:new UseCooldown(Float.intBitsToFloat((int)v.get("cooldown").getAsLong()),n(v,"group")<0?Optional.empty():Optional.of(Identifier.parse("test:group"+n(v,"group"))));}
static ItemStackTemplate template(JsonObject v){if(v.get("remainder").isJsonNull())return null;var p=DataComponentPatch.builder();if(!v.get("maximum").isJsonNull())p.set(DataComponents.MAX_STACK_SIZE,n(v,"maximum"));if(v.get("damage").getAsBoolean())p.set(DataComponents.MAX_DAMAGE,0);return new ItemStackTemplate(Items.STICK.builtInRegistryHolder(),n(v,"remainder"),p.build());}
static void components(ItemStack s,JsonObject v){var t=template(v);s.set(DataComponents.USE_REMAINDER,t==null?null:new UseRemainder(t));s.set(DataComponents.USE_COOLDOWN,cooldown(v));}
static final class Custom extends Item{Data d;Custom(){super(null);}@Override public ItemStack finishUsingItem(ItemStack s,Level l,LivingEntity actor){d.event("finish");return d.refs[n(d.config(),"finish")];}}
static final class Actor extends net.minecraft.server.level.ServerPlayer{Data d;Actor(){super(null,null,null,null);}@Override public GameType gameMode(){return GameType.SURVIVAL;}@Override public boolean isSpectator(){return false;}@Override public boolean isCreative(){return false;}@Override public boolean hasInfiniteMaterials(){d.event("infinite");return d.config().get("infinite").getAsBoolean();}@Override public void handleExtraItemsCreatedOnUse(ItemStack stack){d.extra=stack;d.event("extra");super.handleExtraItemsCreatedOnUse(stack);}@Override public net.minecraft.world.entity.player.Inventory getInventory(){return d.inv;}@Override public net.minecraft.world.entity.item.ItemEntity drop(ItemStack stack,boolean random,net.minecraft.util.Prediction prediction){if(random||prediction!=net.minecraft.util.Prediction.PREDICTED)throw new AssertionError();d.dropped=stack;d.event("drop");return null;}@Override public ItemCooldowns getCooldowns(){d.event("get-cooldowns");return d.book;}}
static final class Other extends net.minecraft.world.entity.decoration.ArmorStand{Data d;Other(){super(null,null);}@Override public boolean hasInfiniteMaterials(){d.event("infinite");return d.config().get("infinite").getAsBoolean();}@Override public void handleExtraItemsCreatedOnUse(ItemStack stack){d.extra=stack;d.event("extra");}}
static final class Book extends ItemCooldowns{Data d;@Override public void addCooldown(ItemStack stack,int ticks){d.original=stack;d.event("add-cooldown "+ticks);super.addCooldown(stack,ticks);}@Override protected void onCooldownStarted(Identifier group,int ticks){d.event("started "+group+" "+ticks);}}
static final class Data{
JsonObject f;PrintWriter out;ItemStack[] refs;ItemStack extra,original,result,dropped;net.minecraft.world.entity.player.Inventory inv;Book book;boolean changed;int calls;
JsonObject config(){return f.getAsJsonObject(changed?"after":"initial");}
String snapshot(ItemStack s){if(s==null)return "none";try{var remainder=s.get(DataComponents.USE_REMAINDER);var c=s.get(DataComponents.USE_COOLDOWN);return (s==ItemStack.EMPTY)+" "+(s.getItem()==Items.AIR?0:s.getItem()==Items.STICK?2:1)+" "+count.getInt(s)+" "+s.getCount()+" "+s.getPopTime()+" "+(remainder==null?"none":remainder.convertInto().count()+":"+remainder.convertInto().getMaxStackSize())+" "+(c==null?"none":Integer.toUnsignedString(Float.floatToIntBits(c.seconds()))+":"+c.cooldownGroup().map(Object::toString).orElse("none"));}catch(Exception e){throw new AssertionError(e);}}
void all(){try{out.println("state "+calls+" "+changed+" "+tick.getInt(book));var slots=new ItemStack[]{refs[0],refs[1],refs[2],extra,original,result,dropped};for(int i=0;i<slots.length;i++)out.println("ref "+i+" "+snapshot(slots[i]));var same=new StringJoiner(",");for(int a=0;a<slots.length;a++)for(int b=a+1;b<slots.length;b++)same.add(Boolean.toString(slots[a]==slots[b]));out.println("same "+same);var entries=new StringJoiner(",");for(var e:((Map<Identifier,?>)map.get(book)).entrySet())entries.add(e.getKey()+"="+start.getInt(e.getValue())+":"+end.getInt(e.getValue()));out.println("cooldowns "+entries);var contents=new StringJoiner(" | ");for(int i=0;i<43;i++){var item=inv.getItem(i);if(item!=ItemStack.EMPTY){var aliases=new StringJoiner(",");for(var ref:slots)aliases.add(Boolean.toString(item==ref));contents.add(i+"="+snapshot(item)+" ["+aliases+"]");}}out.println("inventory "+inv.getSelectedSlot()+" | "+contents);}catch(Exception e){throw new AssertionError(e);}}
void reset(){try{var c=config();Items.STICK.builtInRegistryHolder().bindComponents(DataComponentMap.builder().set(DataComponents.MAX_STACK_SIZE,n(c,"prototype_max")).build());for(int i=0;i<2;i++){var v=c.getAsJsonArray("items").get(i).getAsJsonObject();refs[i].setCount(n(v,"count"));components(refs[i],v);}tick.setInt(book,n(c,"clock"));inv.clearContent();inv.setSelectedSlot(n(c,"selected"));for(var sv:c.getAsJsonArray("slots")){var pair=sv.getAsJsonArray();inv.setItem(pair.get(0).getAsInt(),refs[pair.get(1).getAsInt()]);}if(extra!=null)extra.setCount(n(c,"extra_count"));if(original!=null){original.setCount(n(c,"original_count"));components(original,c.getAsJsonArray("items").get(1).getAsJsonObject());}}catch(Exception e){throw new AssertionError(e);}}
void event(String name){out.println("event "+name);int index=calls++;all();if(index==n(f,"fail"))throw new Stop();if(index==n(f,"mutate")){changed=true;reset();}}
}
public static void main(String[] args)throws Exception{net.minecraft.SharedConstants.tryDetectVersion();net.minecraft.server.Bootstrap.bootStrap();Items.AIR.builtInRegistryHolder().bindComponents(DataComponentMap.EMPTY);var u=Class.forName("sun.misc.Unsafe");unsafe=field(u,"theUnsafe").get(null);allocate=u.getMethod("allocateInstance",Class.class);count=field(ItemStack.class,"count");tick=field(ItemCooldowns.class,"tickCount");map=field(ItemCooldowns.class,"cooldowns");var ci=Class.forName("net.minecraft.world.item.ItemCooldowns$CooldownInstance");start=field(ci,"startTime");end=field(ci,"endTime");stackCtor=ItemStack.class.getDeclaredConstructor(Holder.class,int.class,PatchedDataComponentMap.class);stackCtor.setAccessible(true);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.f=JsonParser.parseString(line).getAsJsonObject();d.out=out;var custom=make(Custom.class);custom.d=d;ItemStack.EMPTY.setCount(0);ItemStack.EMPTY.setPopTime(0);var initial=d.config();Items.STICK.builtInRegistryHolder().bindComponents(DataComponentMap.builder().set(DataComponents.MAX_STACK_SIZE,n(initial,"prototype_max")).build());d.refs=new ItemStack[]{stackCtor.newInstance(Holder.direct(custom),1,new PatchedDataComponentMap(DataComponentMap.EMPTY)),new ItemStack(Items.STICK),ItemStack.EMPTY};d.refs[0].setPopTime(5);d.refs[1].setPopTime(7);d.book=new Book();d.book.d=d;var actor=make(Actor.class);actor.d=d;d.inv=new net.minecraft.world.entity.player.Inventory(actor,new net.minecraft.world.entity.player.PlayerEquipment(actor));d.reset();var other=make(Other.class);other.d=d;d.all();try{d.result=d.refs[n(d.f,"source")].finishUsingItem(null,d.f.get("player").getAsBoolean()?actor:other);out.println("result ok");}catch(Stop|net.minecraft.ReportedException e){out.println("result callback");}d.all();}
}}
}
+83
View File
@@ -0,0 +1,83 @@
#!/usr/bin/env python3
"""Compare native extra-item deliverys and strict scalar stack validation."""
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,64,2147483647]
def item(count=2,remainder=1,maximum=None,damage=False,cooldown=0x3f800000,group=0):return dict(count=count,remainder=remainder,maximum=maximum,damage=damage,cooldown=cooldown,group=group)
def config(items=None,**kw):
v=dict(items=items or [item(),item(1,None,cooldown=None)],finish=0,infinite=False,prototype_max=64,clock=0,extra_count=2,original_count=3,selected=0,slots=[]);v.update(kw);return v
def row(initial=None,after=None,**kw):
v=dict(initial=initial or config(),after=after or config([item(1,None,cooldown=None),item(2,None,cooldown=0x40000000,group=1)]),source=0,player=True,fail=-1,mutate=-1);v.update(kw);return v
for count in edges:
for finish in range(3):
for infinite in [False,True]:
for player in [False,True]:
for mutate in [-1,0,1,2,3,4,5]:
for fail in [-1,0,1,2,3,4,5]:
yield row(config([item(count),item(1,None,cooldown=None)],finish=finish,infinite=infinite),source=0,player=player,mutate=mutate,fail=fail)
for existing in [0,1,63,64]:
for remainder in [1,2,64]:
for occupied in [0,1,35,36]:
for offhand in [False,True]:
for infinite in [False,True]:
initial=config([item(2,remainder),item(existing,None,cooldown=None)],slots=[[i,1] for i in range(occupied)]+([[40,1]] if offhand else []))
after=config([item(1,remainder),item(existing,None,cooldown=None)],slots=initial['slots'],infinite=infinite)
yield row(initial,after,mutate=0)
rng=random.Random(0x4146544552555345)
def random_item():return item(rng.choice(edges),rng.choice([None,-1,1,2,64,2147483647]),rng.choice([None,*edges]),bool(rng.randrange(2)),rng.choice([None,0,0x80000000,0x3d4ccccd,0x7f800000,0xff800000,0x7fc00000,rng.randrange(0x100000000)]),rng.randrange(-1,4))
def random_config():return config([random_item(),random_item()],finish=rng.randrange(3),infinite=bool(rng.randrange(2)),prototype_max=rng.choice(edges),clock=rng.choice(edges),extra_count=rng.choice(edges),original_count=rng.choice(edges))
for _ in range(2400):
initial=random_config();after=random_config()
for c in [initial,after]:
c['selected']=rng.randrange(9)
c['slots']=[[i,rng.randrange(3)] for i in rng.sample(range(43),rng.randrange(44))]
yield row(initial,after,source=rng.randrange(3),player=bool(rng.randrange(2)),mutate=rng.randrange(-1,9),fail=rng.randrange(-1,9))
def main():
parser=argparse.ArgumentParser(description=__doc__);parser.add_argument('--prepare',action='store_true');args=parser.parse_args()
build=lab.LOCAL/'rust-extra-items-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/VanillaExtraItemsOracle.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/extra_items_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.VanillaExtraItemsOracle',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','extra_items_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'/('extra_items_fixture.exe' if os.name=='nt' else 'extra_items_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:,} extra-item delivery 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 ItemStack.finishUsingItem with validated remainders and cooldowns now invokes inherited ServerPlayer.handleExtraItemsCreatedOnUse and real Inventory/PlayerEquipment insertion. Shared item/component references, occupied/aliased slots, offhand merging, creative material queries, partial insertion and the drop fallback are compared through mutation/failure prefixes. The drop call verifies throwRandomly=false and predicted semantics; its entity-spawn body is controlled. Rust composes item-after-use and inventory pickup. Item handlers, full contained schemas, dropped-entity creation, complete use lifecycle and full gameplay remain pending.'}
lab.write_json(report_path,report);print(f'Scoped extra-item delivery parity passed: {report_path}')
if __name__=='__main__':main()