Implement player inventory storage and shared equipment selection

This commit is contained in:
Ludovic Marie committed 2026-10-07 07:47:32 +02:00
1 parent 202dad8192
commit 6f3d124f70
7 files changed
+1055

No files matched your search

@@ -0,0 +1,273 @@
{
"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": 3960,
"sources_sha256": {
"mods/horizon/src/test/java/dev/horizonlab/parity/VanillaPlayerInventoryOracle.java": "dc30eda6ef4b858b8cd43e7d486895b4b3be3e6454b013ac1b86ef231ca36294",
"tools/test_player_inventory_parity.py": "e9330270104479af8d7b7ba360d2b66f64f0960a6b13351c113d00967e7f3fc0",
"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/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": "73067d8cbb5b1b6a6e460abc0a36ba4ea79de2d3c6d664374fb06f7c256aa0d1",
"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": "e1d08747516ba9a8b3c8f7627ba9b6caa14677a53efacfe863ab782d13c6110f",
"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/player_inventory_fixture.rs": "5e8f93f623b225ad65721058b61269763b8d49179166fe8dcb45b83e5de794fd"
},
"fixtures_sha256": {
".local/rust-player-inventory-parity/inputs.txt": "01e7711fd5f12f3b3c4e47b263f5a98751a146aec217255ef0182b5e76ba1cc3"
},
"rust_output_sha256": "003e761e441fad8b82b169f8ac04d228237236fb492e74690d85ded629278297",
"references": [
{
"side": "client",
"jar_sha256": "4508d006323f24fa02876310c192d739af56516eb259000ac50f0909a68c9a2d",
"output_sha256": "003e761e441fad8b82b169f8ac04d228237236fb492e74690d85ded629278297",
"matched_output_lines": 790440
},
{
"side": "server",
"jar_sha256": "a362163eec5d1612d520772bc16e5b39c09e3b234fdc045f56bf544284ee8ae6",
"output_sha256": "003e761e441fad8b82b169f8ac04d228237236fb492e74690d85ded629278297",
"matched_output_lines": 790440
}
],
"scope": "Actual player Inventory storage, equipment, selected slots, hotbar picking, matching/crafting eligibility, reference removals, clear/replacement, counts and change counter. Actual ItemStack components cover damage, unbreakable, one enchantment and custom names; fixture-controlled equipment-map iteration order isolates native JVM Map.of random ordering. Shared stack and equipment references are traced across two inventories, including self-replacement and failures after selection changes. Pickup, item/equipment ticks, menus, recipes, persistence, arbitrary virtual overrides, full components and complete gameplay integration remain pending."
}
@@ -0,0 +1,303 @@
use horizon_world::{
component_patch::{Patch, Prototype},
item_stack_refs::{ItemStacks, StackRef},
player_inventory::{EntityEquipment, EquipmentSlot, InventoryRules, PlayerInventory},
};
use serde_json::Value;
use std::{
fs,
io::{self, Write},
path::Path,
};
type Stack = StackRef<u8, usize, i32>;
type Inventory = PlayerInventory<u8, usize, i32>;
struct Rules;
impl InventoryRules<u8, usize, i32> for Rules {
fn is_enchanted(&self, s: &Stack) -> bool {
s.read().component(&4).is_some_and(|n| *n != 0)
}
fn is_damaged(&self, s: &Stack) -> bool {
let s = s.read();
s.component(&2).is_some()
&& s.component(&3).is_none()
&& s.component(&1).is_some()
&& s.component(&1)
.copied()
.unwrap_or(0)
.max(0)
.min(s.component(&2).copied().unwrap_or(0))
> 0
}
fn has_custom_name(&self, s: &Stack) -> bool {
s.read().component(&5).is_some()
}
}
fn n(v: &Value, k: &str) -> i32 {
v[k].as_i64().unwrap() as i32
}
fn opt(v: Option<i32>) -> String {
v.map_or("none".into(), |n| n.to_string())
}
struct Output {
out: io::BufWriter<fs::File>,
ids: Vec<Stack>,
}
impl Output {
fn snapshot(&mut self, s: &Stack) -> String {
let id = self
.ids
.iter()
.position(|v| v.same_reference(s))
.unwrap_or_else(|| {
self.ids.push(s.clone());
self.ids.len() - 1
});
let damaged = Rules.is_damaged(s);
let enchanted = Rules.is_enchanted(s);
let s = s.read();
format!(
"{}:{}:{}:{}:{}:{}:{}:{}:{}:{}",
id,
s.item(),
s.count,
s.count(),
s.pop_time,
s.is_empty(),
damaged,
enchanted,
(0..6)
.map(|k| opt(s.component(&k).copied()))
.collect::<Vec<_>>()
.join(","),
s.components
.as_patch()
.entries()
.iter()
.map(|(k, v)| format!("{k}={}", opt(*v)))
.collect::<Vec<_>>()
.join(",")
)
}
fn all(&mut self, refs: &[Stack], inv: &[Inventory; 2], empty: &Stack) {
for (i, s) in refs.iter().enumerate() {
let s = self.snapshot(s);
writeln!(self.out, "ref {i} {s}").unwrap();
}
for (i, c) in inv.iter().enumerate() {
writeln!(
self.out,
"inventory {} {} {} {} {} {} {} {} {}",
i,
c.selected(),
c.times_changed,
c.free_slot(),
c.suitable_hotbar(&Rules),
c.is_empty(),
c.count_item(0),
c.count_item(1),
c.count_item(2)
)
.unwrap();
let slots = (0..43)
.map(|j| self.snapshot(&c.get(j).unwrap()))
.collect::<Vec<_>>()
.join(" | ");
writeln!(self.out, "slots {slots}").unwrap();
let eq = c
.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 {eq}").unwrap();
for (j, s) in refs.iter().enumerate() {
writeln!(
self.out,
"query {} {} {} {}",
j,
c.contains(s),
c.find_matching(s),
c.find_crafting(1, s, &Rules)
)
.unwrap();
}
}
let e = self.snapshot(empty);
writeln!(self.out, "empty {e}").unwrap();
}
}
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 prototypes: Vec<_> = f["prototypes"]
.as_array()
.unwrap()
.iter()
.map(|p| {
Prototype::new(p.as_array().unwrap().iter().map(|v| {
(
v[0].as_u64().unwrap() as usize,
v[1].as_i64().unwrap() as i32,
)
}))
})
.collect();
let mut 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"), prototypes[item as usize].clone());
s.write().pop_time = n(v, "pop");
s.write().components.restore_patch(&Patch::new(
v["patch"].as_array().unwrap().iter().map(|v| {
(
v[0].as_u64().unwrap() as usize,
v[1].as_i64().map(|n| n as i32),
)
}),
));
s
}
})
.collect();
let equipment = EntityEquipment::new(empty.clone());
let other = if f["shared_equipment"].as_bool().unwrap() {
equipment.clone()
} else {
EntityEquipment::new(empty.clone())
};
let mut inv = [
Inventory::new(stacks.clone(), equipment),
Inventory::new(stacks.clone(), other),
];
let order: [EquipmentSlot; 7] = std::array::from_fn(|i| {
EquipmentSlot::from_inventory_slot(
36 + f["equipment_order"][i].as_i64().unwrap() as i32,
)
.unwrap()
});
for (i, c) in inv.iter_mut().enumerate() {
let initial = &f["inventories"][i];
c.set_selected(n(initial, "selected")).unwrap();
c.times_changed = n(initial, "changed");
for p in initial["slots"].as_array().unwrap() {
c.set(
p[0].as_i64().unwrap() as i32,
refs[p[1].as_u64().unwrap() as usize].clone(),
)
.unwrap();
}
if n(initial, "mainhand") >= 0 {
c.equipment().set(
EquipmentSlot::MainHand,
refs[n(initial, "mainhand") as usize].clone(),
);
}
}
let mut w = Output {
out,
ids: vec![empty.clone()],
};
w.all(&refs, &inv, &empty);
for op in f["ops"].as_array().unwrap() {
let target = n(op, "inventory") as usize;
let a = n(op, "a") as usize;
let b = n(op, "b") as usize;
let slot = n(op, "slot");
let amount = n(op, "amount");
let result = (|| -> Result<(), ()> {
match op["kind"].as_str().unwrap() {
"get" => refs[b] = inv[target].get(slot).map_err(|_| ())?,
"set" => inv[target].set(slot, refs[a].clone()).map_err(|_| ())?,
"remove" => refs[b] = inv[target].remove(slot, amount),
"no-update" => refs[b] = inv[target].remove_no_update(slot).map_err(|_| ())?,
"remove-ref" => inv[target].remove_reference(&refs[a], &order),
"select" => inv[target].set_selected(slot).map_err(|_| ())?,
"selected" => refs[b] = inv[target].selected_item(),
"set-selected" => refs[b] = inv[target].set_selected_item(refs[a].clone()),
"remove-selected" => {
refs[b] = inv[target].remove_selected(op["all"].as_bool().unwrap())
}
"pick" => inv[target].pick_slot(slot, &Rules).map_err(|_| ())?,
"add-pick" => inv[target].add_and_pick(refs[a].clone(), &Rules),
"clear" => inv[target].clear(),
"changed" => inv[target].changed(),
"replace" => {
let other = n(op, "other") as usize;
if other == target {
inv[target].replace_self();
} else {
let (first, second) = inv.split_at_mut(1);
if target == 0 {
first[0].replace_with(&second[0]);
} else {
second[0].replace_with(&first[0]);
}
}
}
"equip" => {
refs[b] = inv[target].equipment().set(
EquipmentSlot::ALL[n(op, "equipment") as usize],
refs[a].clone(),
)
}
"get-equip" => {
refs[b] = inv[target]
.equipment()
.get(EquipmentSlot::ALL[n(op, "equipment") as usize])
}
"alias" => refs[b] = refs[a].clone(),
"copy" => refs[b] = stacks.copy(&refs[a]),
"count" => refs[a].write().count = amount,
"component" => {
refs[a]
.write()
.components
.set(
n(op, "key") as usize,
op["value"].as_i64().map(|n| n as i32),
)
.map_err(|_| ())?;
}
_ => panic!("operation"),
}
Ok(())
})();
writeln!(
w.out,
"result {}",
if result.is_ok() { "ok" } else { "error" }
)?;
w.all(&refs, &inv, &empty);
}
out = w.out;
}
Ok(())
}
+2
View File
@@ -96,3 +96,5 @@ pub const DATA_VERSION: i32 = 5023;
pub mod item_stack_refs;
pub mod simple_container;
pub mod player_inventory;
@@ -0,0 +1,336 @@
//! Player inventory storage and selection. Equipment is shared with its entity;
//! stack references preserve aliases across inventories and external callers.
//! Pickup, ticking, menus and persistence are implemented separately.
use crate::item_stack_refs::{ItemStacks, StackRef};
use std::{cell::RefCell, rc::Rc};
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
#[repr(usize)]
pub enum EquipmentSlot {
MainHand,
OffHand,
Feet,
Legs,
Chest,
Head,
Body,
Saddle,
}
impl EquipmentSlot {
pub const ALL: [Self; 8] = [
Self::MainHand,
Self::OffHand,
Self::Feet,
Self::Legs,
Self::Chest,
Self::Head,
Self::Body,
Self::Saddle,
];
pub fn inventory_slot(self) -> Option<usize> {
match self {
Self::MainHand => None,
Self::OffHand => Some(40),
Self::Feet => Some(36),
Self::Legs => Some(37),
Self::Chest => Some(38),
Self::Head => Some(39),
Self::Body => Some(41),
Self::Saddle => Some(42),
}
}
pub fn from_inventory_slot(index: i32) -> Option<Self> {
match index {
36 => Some(Self::Feet),
37 => Some(Self::Legs),
38 => Some(Self::Chest),
39 => Some(Self::Head),
40 => Some(Self::OffHand),
41 => Some(Self::Body),
42 => Some(Self::Saddle),
_ => None,
}
}
}
type EquipmentItems<I, K, V> = [Option<StackRef<I, K, V>>; 8];
#[derive(Debug)]
pub struct EntityEquipment<I, K, V> {
items: Rc<RefCell<EquipmentItems<I, K, V>>>,
empty: StackRef<I, K, V>,
}
impl<I, K, V> Clone for EntityEquipment<I, K, V> {
fn clone(&self) -> Self {
Self {
items: self.items.clone(),
empty: self.empty.clone(),
}
}
}
impl<I, K, V> EntityEquipment<I, K, V> {
pub fn new(empty: StackRef<I, K, V>) -> Self {
Self {
items: Rc::new(RefCell::new(std::array::from_fn(|_| None))),
empty,
}
}
pub fn get(&self, slot: EquipmentSlot) -> StackRef<I, K, V> {
self.items.borrow()[slot as usize]
.clone()
.unwrap_or_else(|| self.empty.clone())
}
pub fn set(&self, slot: EquipmentSlot, stack: StackRef<I, K, V>) -> StackRef<I, K, V> {
self.items.borrow_mut()[slot as usize]
.replace(stack)
.unwrap_or_else(|| self.empty.clone())
}
/// Native clear preserves which enum-map entries have been populated.
pub fn clear(&self) {
for s in self.items.borrow_mut().iter_mut().flatten() {
*s = self.empty.clone();
}
}
pub fn entries(&self) -> Vec<(EquipmentSlot, StackRef<I, K, V>)> {
let items = self.items.borrow();
EquipmentSlot::ALL
.into_iter()
.filter_map(|s| items[s as usize].clone().map(|v| (s, v)))
.collect()
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum InventoryError {
Index,
Selection,
}
pub trait InventoryRules<I, K, V> {
fn is_enchanted(&self, stack: &StackRef<I, K, V>) -> bool;
fn is_damaged(&self, stack: &StackRef<I, K, V>) -> bool;
fn has_custom_name(&self, stack: &StackRef<I, K, V>) -> bool;
}
#[derive(Debug)]
pub struct PlayerInventory<I, K, V> {
items: [StackRef<I, K, V>; 36],
equipment: EntityEquipment<I, K, V>,
stacks: ItemStacks<I, K, V>,
selected: usize,
pub times_changed: i32,
}
impl<I: Copy + Eq, K: Clone + Eq, V: Clone + PartialEq> PlayerInventory<I, K, V> {
pub fn new(stacks: ItemStacks<I, K, V>, equipment: EntityEquipment<I, K, V>) -> Self {
Self {
items: std::array::from_fn(|_| stacks.empty()),
equipment,
stacks,
selected: 0,
times_changed: 0,
}
}
pub fn items(&self) -> &[StackRef<I, K, V>; 36] {
&self.items
}
pub fn equipment(&self) -> &EntityEquipment<I, K, V> {
&self.equipment
}
pub fn selected(&self) -> usize {
self.selected
}
pub fn set_selected(&mut self, selected: i32) -> Result<(), InventoryError> {
if !(0..9).contains(&selected) {
return Err(InventoryError::Selection);
}
self.selected = selected as usize;
Ok(())
}
pub fn selected_item(&self) -> StackRef<I, K, V> {
self.items[self.selected].clone()
}
pub fn set_selected_item(&mut self, stack: StackRef<I, K, V>) -> StackRef<I, K, V> {
std::mem::replace(&mut self.items[self.selected], stack)
}
pub fn get(&self, index: i32) -> Result<StackRef<I, K, V>, InventoryError> {
if index < 0 {
return Err(InventoryError::Index);
}
Ok(if index < 36 {
self.items[index as usize].clone()
} else {
EquipmentSlot::from_inventory_slot(index)
.map_or_else(|| self.stacks.empty(), |s| self.equipment.get(s))
})
}
pub fn set(&mut self, index: i32, stack: StackRef<I, K, V>) -> Result<(), InventoryError> {
if index < 0 {
return Err(InventoryError::Index);
}
if index < 36 {
self.items[index as usize] = stack;
} else if let Some(s) = EquipmentSlot::from_inventory_slot(index) {
self.equipment.set(s, stack);
}
Ok(())
}
pub fn remove(&mut self, index: i32, amount: i32) -> StackRef<I, K, V> {
if index < 0 || (index < 36 && amount <= 0) {
return self.stacks.empty();
}
let s = self.get(index).expect("nonnegative index");
if s.read().is_empty() {
self.stacks.empty()
} else {
self.stacks.split(&s, amount)
}
}
pub fn remove_no_update(&mut self, index: i32) -> Result<StackRef<I, K, V>, InventoryError> {
if index < 0 {
return Err(InventoryError::Index);
}
Ok(if index < 36 {
std::mem::replace(&mut self.items[index as usize], self.stacks.empty())
} else {
EquipmentSlot::from_inventory_slot(index).map_or_else(
|| self.stacks.empty(),
|s| self.equipment.set(s, self.stacks.empty()),
)
})
}
/// The native Map.of equipment iteration order varies by JVM. The caller
/// supplies its pinned runtime order when reference aliases span equipment.
pub fn remove_reference(
&mut self,
stack: &StackRef<I, K, V>,
equipment_order: &[EquipmentSlot; 7],
) {
for s in &mut self.items {
if s.same_reference(stack) {
*s = self.stacks.empty();
return;
}
}
for slot in equipment_order {
if self.equipment.get(*slot).same_reference(stack) {
self.equipment.set(*slot, self.stacks.empty());
return;
}
}
}
pub fn free_slot(&self) -> i32 {
self.items
.iter()
.position(|s| s.read().is_empty())
.map_or(-1, |i| i as i32)
}
pub fn find_matching(&self, stack: &StackRef<I, K, V>) -> i32 {
self.items
.iter()
.position(|s| !s.read().is_empty() && stack.read().same_item_components(&s.read()))
.map_or(-1, |i| i as i32)
}
pub fn contains(&self, stack: &StackRef<I, K, V>) -> bool {
(0..43).any(|i| {
let s = self.get(i).expect("valid slot");
!s.read().is_empty() && s.read().same_item_components(&stack.read())
})
}
pub fn count_item(&self, item: I) -> i32 {
(0..43).fold(0_i32, |n, i| {
let s = self.get(i).expect("valid slot");
let s = s.read();
if s.item() == item {
n.wrapping_add(s.count())
} else {
n
}
})
}
pub fn suitable_hotbar<R: InventoryRules<I, K, V>>(&self, rules: &R) -> usize {
for delta in 0..9 {
let i = (self.selected + delta) % 9;
if self.items[i].read().is_empty() {
return i;
}
}
for delta in 0..9 {
let i = (self.selected + delta) % 9;
if !rules.is_enchanted(&self.items[i]) {
return i;
}
}
self.selected
}
pub fn find_crafting<R: InventoryRules<I, K, V>>(
&self,
item: I,
stack: &StackRef<I, K, V>,
rules: &R,
) -> i32 {
self.items
.iter()
.position(|s| {
!s.read().is_empty()
&& s.read().item() == item
&& !rules.is_damaged(s)
&& !rules.is_enchanted(s)
&& !rules.has_custom_name(s)
&& (stack.read().is_empty() || stack.read().same_item_components(&s.read()))
})
.map_or(-1, |i| i as i32)
}
pub fn add_and_pick<R: InventoryRules<I, K, V>>(
&mut self,
stack: StackRef<I, K, V>,
rules: &R,
) {
self.selected = self.suitable_hotbar(rules);
if !self.items[self.selected].read().is_empty() {
let free = self.free_slot();
if free >= 0 {
self.items[free as usize] = self.items[self.selected].clone();
}
}
self.items[self.selected] = stack;
}
pub fn pick_slot<R: InventoryRules<I, K, V>>(
&mut self,
index: i32,
rules: &R,
) -> Result<(), InventoryError> {
self.selected = self.suitable_hotbar(rules);
// Selection changes even when the following list access fails.
let index = usize::try_from(index)
.ok()
.filter(|i| *i < 36)
.ok_or(InventoryError::Index)?;
self.items.swap(self.selected, index);
Ok(())
}
pub fn remove_selected(&mut self, all: bool) -> StackRef<I, K, V> {
let s = self.selected_item();
if s.read().is_empty() {
return self.stacks.empty();
}
let amount = if all { s.read().count() } else { 1 };
self.remove(self.selected as i32, amount)
}
pub fn changed(&mut self) {
self.times_changed = self.times_changed.wrapping_add(1);
}
pub fn clear(&mut self) {
self.items.fill(self.stacks.empty());
self.equipment.clear();
}
pub fn is_empty(&self) -> bool {
(0..43).all(|i| self.get(i).expect("valid slot").read().is_empty())
}
pub fn replace_self(&mut self) {
for i in 0..43 {
self.set(i, self.get(i).expect("valid slot"))
.expect("valid slot");
}
}
pub fn replace_with(&mut self, other: &Self) {
for i in 0..43 {
self.set(i, other.get(i).expect("valid slot"))
.expect("valid slot");
}
self.selected = other.selected;
}
}
+20
View File
@@ -1664,3 +1664,23 @@ the first empty slot clears the remainder before limiting the inserted copy.
Targeted Clippy passes. Player inventory, menus, crafting accounting, persistence,
arbitrary getItem/setItem overrides and complete gameplay integration remain
pending. Reference counting here has not been benchmarked as a performance gain.
`player_inventory` implements the 36 ordinary slots, seven mapped equipment
slots, shared entity equipment, selected hotbar slot, picking, matching/crafting
eligibility, removals, replacement, clearing and the change counter. Equipment
retains populated entries when cleared. Stack and equipment references can be
shared across inventories; replacing an inventory retains stack aliases.
`python3 tools/test_player_inventory_parity.py` compares actual native Inventory,
EntityEquipment and ItemStack methods. The
[inventory report](../benchmarks/rust-player-inventory-2026-10-07/scoped-parity.json)
records **790,440 matching lines per JAR**, including invalid-index failure
prefixes, selected-slot changes, negative equipment removal amounts, ordinary
versus equipment identity, self-replacement and shared equipment clearing.
Components cover damage, unbreakable presence, a fixed enchantment and literal
custom names. Native Map.of iteration order varies by JVM; fixtures explicitly
supply equipment-map order to both sides when removing an aliased reference.
Targeted Clippy passes. Pickup, inventory/equipment ticks, menus, recipe
accounting, persistence, full component values and complete gameplay integration
remain pending.
@@ -0,0 +1,37 @@
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.component.*;import net.minecraft.world.item.*;import net.minecraft.world.entity.*;import net.minecraft.world.entity.player.Inventory;import net.minecraft.world.item.enchantment.*;
/** Native Inventory storage/selection with real stack/component and equipment aliases. */
@SuppressWarnings({"unchecked","rawtypes"})
public final class VanillaPlayerInventoryOracle {
static Item[] items;static DataComponentType[] keys;static Field components,count,patchMap,equipmentItems,changed;static Object removed;static ItemEnchantments enchanted;
static EquipmentSlot[] mapped={EquipmentSlot.FEET,EquipmentSlot.LEGS,EquipmentSlot.CHEST,EquipmentSlot.HEAD,EquipmentSlot.OFFHAND,EquipmentSlot.BODY,EquipmentSlot.SADDLE};
static Field field(Class<?> c,String n)throws Exception{var f=c.getDeclaredField(n);f.setAccessible(true);return f;}
static int n(JsonObject o,String k){return o.get(k).getAsInt();}
static int key(Object k){for(int i=0;i<keys.length;i++)if(keys[i]==k)return i;throw new AssertionError();}
static int item(Item i){for(int k=0;k<items.length;k++)if(items[k]==i)return k;throw new AssertionError();}
static Object value(int k,JsonElement v){if(v.isJsonNull())return null;int n=v.getAsInt();return switch(k){case 3->net.minecraft.util.Unit.INSTANCE;case 4->n==0?ItemEnchantments.EMPTY:enchanted;case 5->net.minecraft.network.chat.Component.literal(Integer.toString(n));default->n;};}
static String text(int k,Object v){if(v==null||v==removed)return "none";return switch(k){case 3->"0";case 4->((ItemEnchantments)v).isEmpty()?"0":"1";case 5->((net.minecraft.network.chat.Component)v).getString();default->v.toString();};}
static DataComponentMap prototype(JsonArray a){var b=DataComponentMap.builder();for(var e:a){var p=e.getAsJsonArray();int k=p.get(0).getAsInt();b.set(keys[k],value(k,p.get(1)));}return b.build();}
static DataComponentPatch patch(JsonArray a){var b=DataComponentPatch.builder();for(var e:a){var p=e.getAsJsonArray();int k=p.get(0).getAsInt();if(p.get(1).isJsonNull())b.remove(keys[k]);else b.set(keys[k],value(k,p.get(1)));}return b.build();}
static final class Data {
PrintWriter out;ItemStack[] refs;Inventory[] inv;EntityEquipment[] equipment;IdentityHashMap<ItemStack,Integer> ids=new IdentityHashMap<>();
int id(ItemStack s){return ids.computeIfAbsent(s,k->ids.size());}
String snapshot(ItemStack s){try{var raw=(PatchedDataComponentMap)components.get(s);var p=new StringJoiner(",");for(var e:((Map<?,?>)patchMap.get(raw.asPatch())).entrySet())p.add(key(e.getKey())+"="+text(key(e.getKey()),e.getValue()));var v=new StringJoiner(",");for(int k=0;k<keys.length;k++)v.add(text(k,s.get(keys[k])));return id(s)+":"+item(s.getItem())+":"+count.getInt(s)+":"+s.getCount()+":"+s.getPopTime()+":"+s.isEmpty()+":"+s.isDamaged()+":"+s.isEnchanted()+":"+v+":"+p;}catch(Exception e){throw new AssertionError(e);}}
void all()throws Exception {
for(int i=0;i<refs.length;i++)out.println("ref "+i+" "+snapshot(refs[i]));
for(int i=0;i<2;i++){var c=inv[i];out.println("inventory "+i+" "+c.getSelectedSlot()+" "+c.getTimesChanged()+" "+c.getFreeSlot()+" "+c.getSuitableHotbarSlot()+" "+c.isEmpty()+" "+c.countItem(items[0])+" "+c.countItem(items[1])+" "+c.countItem(items[2]));var slots=new StringJoiner(" | ");for(int j=0;j<43;j++)slots.add(snapshot(c.getItem(j)));out.println("slots "+slots);var eq=new StringJoiner(" | ");for(var e:((EnumMap<EquipmentSlot,ItemStack>)equipmentItems.get(equipment[i])).entrySet())eq.add(e.getKey().name()+"="+snapshot(e.getValue()));out.println("equipment "+eq);for(int j=0;j<refs.length;j++)out.println("query "+j+" "+c.contains(refs[j])+" "+c.findSlotMatchingItem(refs[j])+" "+c.findSlotMatchingCraftingIngredient(items[1].builtInRegistryHolder(),refs[j]));}
out.println("empty "+snapshot(ItemStack.EMPTY));
}
}
public static void main(String[] args)throws Exception {
net.minecraft.SharedConstants.tryDetectVersion();net.minecraft.server.Bootstrap.bootStrap();var lookup=net.minecraft.data.registries.VanillaRegistries.createWorldLookup();var m=new ItemEnchantments.Mutable(ItemEnchantments.EMPTY);m.set(lookup.lookupOrThrow(net.minecraft.core.registries.Registries.ENCHANTMENT).getOrThrow(Enchantments.UNBREAKING),1);enchanted=m.toImmutable();
items=new Item[]{Items.AIR,Items.STICK,Items.SHIELD};keys=new DataComponentType[]{DataComponents.MAX_STACK_SIZE,DataComponents.DAMAGE,DataComponents.MAX_DAMAGE,DataComponents.UNBREAKABLE,DataComponents.ENCHANTMENTS,DataComponents.CUSTOM_NAME};components=field(ItemStack.class,"components");count=field(ItemStack.class,"count");patchMap=field(DataComponentPatch.class,"map");removed=field(Class.forName("net.minecraft.core.component.Removed"),"INSTANCE").get(null);equipmentItems=field(EntityEquipment.class,"items");changed=field(Inventory.class,"timesChanged");Path dir=Path.of(args[1]);
try(var out=new PrintWriter(Files.newBufferedWriter(dir.resolve(args[0]+".txt")));var lines=Files.lines(dir.resolve("inputs.txt"))){for(var line:(Iterable<String>)lines::iterator){var f=JsonParser.parseString(line).getAsJsonObject();for(int i=0;i<3;i++)items[i].builtInRegistryHolder().bindComponents(prototype(f.getAsJsonArray("prototypes").get(i).getAsJsonArray()));Inventory.EQUIPMENT_SLOT_MAPPING.clear();for(var e:f.getAsJsonArray("equipment_order")){int i=e.getAsInt();Inventory.EQUIPMENT_SLOT_MAPPING.put(36+i,mapped[i]);}ItemStack.EMPTY.setCount(0);ItemStack.EMPTY.setPopTime(0);((PatchedDataComponentMap)components.get(ItemStack.EMPTY)).clearPatch();var d=new Data();d.out=out;d.ids.put(ItemStack.EMPTY,0);d.refs=new ItemStack[4];for(int i=0;i<4;i++){var v=f.getAsJsonArray("stacks").get(i).getAsJsonObject();int type=n(v,"item");d.refs[i]=type<0?ItemStack.EMPTY:new ItemStack(items[type],n(v,"count"));if(type>=0){d.refs[i].setPopTime(n(v,"pop"));((PatchedDataComponentMap)components.get(d.refs[i])).restorePatch(patch(v.getAsJsonArray("patch")));}}
d.equipment=new EntityEquipment[]{new EntityEquipment(),new EntityEquipment()};if(f.get("shared_equipment").getAsBoolean())d.equipment[1]=d.equipment[0];d.inv=new Inventory[]{new Inventory(null,d.equipment[0]),new Inventory(null,d.equipment[1])};
for(int i=0;i<2;i++){var initial=f.getAsJsonArray("inventories").get(i).getAsJsonObject();d.inv[i].setSelectedSlot(n(initial,"selected"));changed.setInt(d.inv[i],n(initial,"changed"));for(var entry:initial.getAsJsonArray("slots")){var pair=entry.getAsJsonArray();d.inv[i].setItem(pair.get(0).getAsInt(),d.refs[pair.get(1).getAsInt()]);}if(n(initial,"mainhand")>=0)d.equipment[i].set(EquipmentSlot.MAINHAND,d.refs[n(initial,"mainhand")]);}
d.all();for(var ov:f.getAsJsonArray("ops")){var op=ov.getAsJsonObject();var c=d.inv[n(op,"inventory")];int a=n(op,"a"),b=n(op,"b"),slot=n(op,"slot"),amount=n(op,"amount");try{switch(op.get("kind").getAsString()){
case "get":d.refs[b]=c.getItem(slot);break;case "set":c.setItem(slot,d.refs[a]);break;case "remove":d.refs[b]=c.removeItem(slot,amount);break;case "no-update":d.refs[b]=c.removeItemNoUpdate(slot);break;case "remove-ref":c.removeItem(d.refs[a]);break;case "select":c.setSelectedSlot(slot);break;case "selected":d.refs[b]=c.getSelectedItem();break;case "set-selected":d.refs[b]=c.setSelectedItem(d.refs[a]);break;case "remove-selected":d.refs[b]=c.removeFromSelected(op.get("all").getAsBoolean());break;case "pick":c.pickSlot(slot);break;case "add-pick":c.addAndPickItem(d.refs[a]);break;case "clear":c.clearContent();break;case "changed":c.setChanged();break;case "replace":c.replaceWith(d.inv[n(op,"other")]);break;case "equip":d.refs[b]=d.equipment[n(op,"inventory")].set(EquipmentSlot.values()[n(op,"equipment")],d.refs[a]);break;case "get-equip":d.refs[b]=d.equipment[n(op,"inventory")].get(EquipmentSlot.values()[n(op,"equipment")]);break;case "alias":d.refs[b]=d.refs[a];break;case "copy":d.refs[b]=d.refs[a].copy();break;case "count":d.refs[a].setCount(amount);break;case "component":int k=n(op,"key");d.refs[a].set(keys[k],value(k,op.get("value")));break;default:throw new AssertionError();}out.println("result ok");}catch(IndexOutOfBoundsException|IllegalArgumentException|NullPointerException e){out.println("result error");}d.all();}
}}
}
}
+84
View File
@@ -0,0 +1,84 @@
#!/usr/bin/env python3
"""Compare native PlayerInventory 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():
kinds=['get','set','remove','no-update','remove-ref','select','selected','set-selected','remove-selected','pick','add-pick','clear','changed','replace','equip','get-equip','alias','copy','count','component']
edges=[-2147483648,-1,0,1,2,8,9,35,36,39,40,41,42,43,64,2147483647]
def op(kind,**kw):
v=dict(kind=kind,inventory=0,other=1,a=0,b=3,slot=0,amount=1,all=False,equipment=0,key=0,value=64);v.update(kw);return v
def stack(item,count,patch=None):return dict(item=item,count=count,pop=7,patch=patch or [])
def row(ops,count=5,slots=None,selected=0):return dict(prototypes=[[],[[0,64]],[[0,1],[1,0],[2,100]]],stacks=[stack(1,count),stack(1,60),stack(2,1),stack(-1,0)],equipment_order=list(range(7)),shared_equipment=False,inventories=[dict(selected=selected,changed=2147483647,mainhand=0,slots=slots if slots is not None else [[0,0],[8,1],[35,2],[36,2],[40,0],[42,1]]),dict(selected=8,changed=0,mainhand=-1,slots=[[2,1],[39,0]])],ops=ops)
for kind in kinds:
for slot in edges:
for amount in [-2147483648,-1,0,1,64,2147483647]:
yield row([op(kind,slot=slot,amount=amount),op('changed'),op('copy'),op('count',a=3,amount=3)])
for enchanted in [False,True]:
for selected in range(9):
for free in [-1,0,1,8,9,35]:
for kind in ['pick','add-pick','replace','remove-ref','remove-selected']:
slots=[[i,1] for i in range(43) if i!=free]
v=row([op(kind,slot=35),op('replace',other=0)],slots=slots,selected=selected)
if enchanted:v['stacks'][1]['patch']=[[4,1]]
yield v
rng=random.Random(0x504c41594552494e)
def integer():return rng.choice(edges) if rng.randrange(2) else rng.randrange(-2147483648,2147483648)
def value(k):return 0 if k==3 else rng.randrange(2) if k==4 else integer()
def patch():return [[k,rng.choice([None,value(k)])] for k in rng.sample(range(6),rng.randrange(7))]
for _ in range(1500):
prototypes=[[[k,value(k)] for k in rng.sample(range(6),rng.randrange(7))] for __ in range(3)]
stacks=[dict(item=rng.randrange(-1,3),count=integer(),pop=integer(),patch=patch()) for __ in range(4)]
inventories=[dict(selected=rng.randrange(9),changed=integer(),mainhand=rng.randrange(-1,4),slots=[[i,rng.randrange(4)] for i in rng.sample(range(43),rng.randrange(44))]) for __ in range(2)]
ops=[]
for __ in range(18):
k=rng.randrange(6)
ops.append(op(rng.choice(kinds),inventory=rng.randrange(2),other=rng.randrange(2),a=rng.randrange(4),b=rng.randrange(4),slot=rng.choice(edges) if rng.randrange(2) else rng.randrange(43),amount=integer(),all=bool(rng.randrange(2)),equipment=rng.randrange(8),key=k,value=rng.choice([None,value(k)])))
yield dict(prototypes=prototypes,stacks=stacks,inventories=inventories,equipment_order=rng.sample(range(7),7),shared_equipment=bool(rng.randrange(2)),ops=ops)
def main():
parser=argparse.ArgumentParser(description=__doc__);parser.add_argument('--prepare',action='store_true');args=parser.parse_args()
build=lab.LOCAL/'rust-player-inventory-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/VanillaPlayerInventoryOracle.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/player_inventory_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.VanillaPlayerInventoryOracle',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','player_inventory_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'/('player_inventory_fixture.exe' if os.name=='nt' else 'player_inventory_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:,} player inventory 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 player Inventory storage, equipment, selected slots, hotbar picking, matching/crafting eligibility, reference removals, clear/replacement, counts and change counter. Actual ItemStack components cover damage, unbreakable, one enchantment and custom names; fixture-controlled equipment-map iteration order isolates native JVM Map.of random ordering. Shared stack and equipment references are traced across two inventories, including self-replacement and failures after selection changes. Pickup, item/equipment ticks, menus, recipes, persistence, arbitrary virtual overrides, full components and complete gameplay integration remain pending.'}
lab.write_json(report_path,report);print(f'Scoped player inventory parity passed: {report_path}')
if __name__=='__main__':main()