Implement proto-chunk block-entity storage and coordinate resolution

This commit is contained in:
Ludovic Marie
2026-10-06 14:59:17 +02:00
parent e9a082517b
commit e755a5920a
9 changed files with 731 additions and 0 deletions
+4
View File
@@ -187,6 +187,10 @@ jobs:
env:
RUSTUP_TOOLCHAIN: stable
run: python tools/test_generation_tick_parity.py
- name: Compare proto-chunk block-entity storage and coordinates
env:
RUSTUP_TOOLCHAIN: stable
run: python tools/test_block_entity_storage_parity.py
- name: Compare ordered single-block replacement features
env:
RUSTUP_TOOLCHAIN: stable
@@ -0,0 +1,205 @@
{
"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)",
"commands": {
"reset": 23,
"resolve": 20825,
"pending": 2533,
"dump": 773,
"get": 864,
"save": 914,
"mutate": 869,
"active": 889,
"remove": 821
},
"sources_sha256": {
"mods/horizon/src/test/java/dev/horizonlab/parity/VanillaBlockEntityStorageOracle.java": "7d9c7b9f9b82e3b543a062424d4b59374fccc2068010d5523c6c0020c22c2e69",
"tools/test_block_entity_storage_parity.py": "0a772a8db25e46db18d9a1962209c3199e6daea34140452e64cd73747805f198",
"tools/test_rust_parity.py": "29d3db08005873528894b45ca10dcd6278753650473b3eb574e53e06ede40ec6",
"tools/lab.py": "d00bf107fa01b6c579734a082c3ca4ade3018f0d6851d46844acf17574f66e64",
"Cargo.toml": "dc370d5fdde37238c3e4820046f11c3201020406a4edfa1308257c13edbdcda4",
"Cargo.lock": "7307d0c887364db36d327685ecf0671b75305e31e5ba5fbdcdbb15eaf156d20e",
"crates/horizon-core/Cargo.toml": "e21b502c949da93cf67826592459b687a8cbf737ef92fee514e3ed2cc7cd27ad",
"crates/horizon-world/Cargo.toml": "977338bf3e654193685b015d383493b9f59ff6f5d6210bec212cef8ac4a50bed",
"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/mod.rs": "dc92f73885dd0a7760743bf74f1ab8afa839d61cef2dfef890f8a23b67b4742e",
"crates/horizon-core/src/geometry/movement.rs": "930e34abef6f6eeec1ee7a6c920b5ba01bfe37b90678884490f4be53241f6b96",
"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": "47350edab5791f5074a54563ba98108818c5c6b1c5818a591388eab2ac9f5490",
"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/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/bin/worldgen_parity.rs": "3143ceb9b56eecfd7667e5feb5f9f0cd13cfb8754b38d4ea338dbba97f2819da",
"crates/horizon-world/src/block_collision.rs": "cec2af81d212d5eed7a2196e4cf0be2daf4cecf53248f78cc68f2190e4d83f9a",
"crates/horizon-world/src/block_entities.rs": "a9deae92e6e5a1fcf4f0476c33ea4d170ddad6fd4b3303e1bb66d933973d5e06",
"crates/horizon-world/src/block_placement.rs": "524f4892d50bab77b7d89d19f7b048ffe746996f2ed7f376db2c6ab00ed75c24",
"crates/horizon-world/src/block_properties.rs": "b0bd8a983336c48e488a20caa77dd5c39b1a74bbd14ebc03b3b64556a1c864fc",
"crates/horizon-world/src/block_support.rs": "ade4fbd47284b450dabdd682dc86138b2ac05a0fd379525d7110648908245476",
"crates/horizon-world/src/block_survival.rs": "28ce64375e09819a4f13da9cfbe37c08b5ad0daf01b289c1c360b4567e50e811",
"crates/horizon-world/src/chunk.rs": "8843e6f48dbbc4245ce4e95564dfba3a4d7a051ba0b1577fb038c07adb0ff0f9",
"crates/horizon-world/src/collision.rs": "05313e0c1f3089524fe029f9aac3e439cd47f1c9584c8c7e154c9adcfa88c335",
"crates/horizon-world/src/compression.rs": "4db73e6ca9a0d552d415ce60e7e88b89c9cf2b7d060a669ff8328f518316e101",
"crates/horizon-world/src/dynamic_support.rs": "e799b31e5120cfdb04e0b342f2097d610ed98ef8c4be031620ee00368d82df59",
"crates/horizon-world/src/entity_obstruction.rs": "a4b28a19bf83b23611c59d94535c488db37e4aee24b22c973c545910894a809f",
"crates/horizon-world/src/fluid.rs": "a0a1f34ba94d8b59dc975becee8952960764d67ae08cf2d6a8bcd40a614b15c4",
"crates/horizon-world/src/heightmap.rs": "a5edef082b5af40d814421ea17e129b3b06d70abee6434ed263e4bba58571698",
"crates/horizon-world/src/lib.rs": "4e556a5896b893fbb73bf2e2b8e70601a8374f58b9f4f5f3730e99916a846e3a",
"crates/horizon-world/src/main.rs": "fce57418b8c22a6a0a9044baa89ed3d9c243bc2e8413fea3ff0f32985daba021",
"crates/horizon-world/src/nbt.rs": "60262c3de8c8f68bc2da0370ccb17393d32b85f5f5022c6358599a7d32119ea6",
"crates/horizon-world/src/piston_shape.rs": "399c2337103b8e7e0eb5667227c5aa14f9549d6a467b7be7da3d0717f0263500",
"crates/horizon-world/src/region.rs": "72b642e87fed37ed17f54d4c30f96c3b64d26c14bc0589c31ef40ff1616188fc",
"crates/horizon-world/src/registry/json.rs": "1eae82604d2b3377fd35aaa3a914a67e5af28f93923815a49e895b4d437433cd",
"crates/horizon-world/src/registry.rs": "f2e58b2b4d944ac8fc6226bd3352cefc3d2052d8473e02ccf7dc402b176df435",
"crates/horizon-world/src/tick_data.rs": "f8d04a69ea263f4168bce33f92e5b4ee2fe07d7caf50a1621c6b3ef6eb2479f3",
"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": "d6389c8d1adf900eb8f0e6c8833feafe43dc92d74e5c7ffa6f3950975529d624",
"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": "7677027594ed3df5e538402d5a3ba9276b67b56490463ca2686bfa418d02bd24",
"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": "d35ded93fe6e95ea045cadcf4925e656dc34ef832b742179b9ad0cc49f902f31",
"crates/horizon-world/src/worldgen/terrain.rs": "b8fb9fb80a66a851d916f07f15df1a40b6cc95b49e71aaec4d4b4107f13d9f9f",
"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": "1d79a7fdb5dd18b0b59079cb2dc74682ba7af2a650c8efff027cfa530eee7bf9",
"crates/horizon-world/src/worldgen.rs": "e8e9f099ee1fcec8eedce31805d676d3f89c18e84f494a147ffcbfee23d3d54f",
"crates/horizon-world/examples/block_entity_storage_fixture.rs": "feefacc51bcf2b5af62869130512ac150664470c07cc2c2746d9258a6709b181"
},
"fixtures_sha256": {
".local/rust-block-entity-storage-parity/inputs.txt": "9b9242240b36a6b65fbc0a3c707685946cbbbe16cfcafa59c5b16bd5aa33718f"
},
"rust_output_sha256": "81304a08cb574ec6b4c46ea76ffd4bdd8bfc331d5b07bd4bc4de3d7564340dc9",
"references": [
{
"side": "client",
"jar_sha256": "4508d006323f24fa02876310c192d739af56516eb259000ac50f0909a68c9a2d",
"output_sha256": "81304a08cb574ec6b4c46ea76ffd4bdd8bfc331d5b07bd4bc4de3d7564340dc9",
"matched_output_lines": 78251
},
{
"side": "server",
"jar_sha256": "a362163eec5d1612d520772bc16e5b39c09e3b234fdc045f56bf544284ee8ae6",
"output_sha256": "81304a08cb574ec6b4c46ea76ffd4bdd8bfc331d5b07bd4bc4de3d7564340dc9",
"matched_output_lines": 78251
}
],
"scope": "Native BlockEntity.getPosFromTag and actual ProtoChunk pending/active insertion, lookup, removal, position union and pending-save fallback. NumericTag coordinate reads cover integer narrowing, float/double floor overflow, infinities/NaNs, missing/nonnumeric fields, wrong-chunk relocation and wrapped chunk coordinates. Active entries replace pending data; later pending records cannot overwrite active entries. In-place pending coordinate mutation retains the original map key. Native chest entities carry loot seeds for active identity and basic saving-metadata comparisons; Rust supplies caller-owned payload serialization. Iteration order is normalized, and pending reads are borrowed/owned values rather than unrestricted Java aliases. Entity creation from NBT, complete entity serializers, active behavior and integration with chunk lifecycle remain pending."
}
@@ -0,0 +1,169 @@
use horizon_core::position::{BlockPos, ChunkPos};
use horizon_world::{
block_entities::{ProtoBlockEntities, position_from_tag},
nbt::Tag,
};
use std::{
fs,
io::{self, BufRead, Write},
str::SplitWhitespace,
};
struct Input<'a>(SplitWhitespace<'a>);
impl Input<'_> {
fn int(&mut self) -> i32 {
self.0.next().unwrap().parse().unwrap()
}
fn long(&mut self) -> i64 {
self.0.next().unwrap().parse().unwrap()
}
fn pos(&mut self) -> BlockPos {
BlockPos::new(self.int(), self.int(), self.int())
}
fn tag(&mut self) -> Option<Tag> {
let kind = self.0.next().unwrap();
let text = self.0.next().unwrap();
Some(match kind {
"none" => return None,
"byte" => Tag::Byte(text.parse().unwrap()),
"short" => Tag::Short(text.parse().unwrap()),
"int" => Tag::Int(text.parse().unwrap()),
"long" => Tag::Long(text.parse().unwrap()),
"float" => {
let bits = u32::from_str_radix(text, 16).unwrap();
Tag::Float(if f32::from_bits(bits) == 0.0 { 0 } else { bits })
}
"double" => {
let bits = u64::from_str_radix(text, 16).unwrap();
Tag::Double(if f64::from_bits(bits) == 0.0 { 0 } else { bits })
}
"string" => Tag::String(text.into()),
_ => panic!("unknown atom"),
})
}
}
fn xyz(p: BlockPos) -> String {
format!("{} {} {}", p.x, p.y, p.z)
}
fn atom(tag: Option<&Tag>) -> String {
match tag {
None => "none".into(),
Some(Tag::Float(v)) => format!("float:{v:x}"),
Some(Tag::Double(v)) => format!("double:{v:x}"),
Some(Tag::Byte(v)) => format!("1:{v}"),
Some(Tag::Short(v)) => format!("2:{v}"),
Some(Tag::Int(v)) => format!("3:{v}"),
Some(Tag::Long(v)) => format!("4:{v}"),
Some(v) => format!("{}:invalid", v.id()),
}
}
fn pending(tag: Option<&Tag>) -> String {
match tag {
None => "none".into(),
Some(tag) => {
let Some(Tag::Long(mark)) = tag.get("mark") else {
panic!("missing marker")
};
format!(
"{mark} {} {} {}",
atom(tag.get("x")),
atom(tag.get("y")),
atom(tag.get("z"))
)
}
}
}
fn main() -> Result<(), Box<dyn std::error::Error>> {
let args: Vec<_> = std::env::args().collect();
let mut out = io::BufWriter::new(fs::File::create(&args[2])?);
let mut position = ChunkPos::default();
let mut chunk = ProtoBlockEntities::<i64>::new(position);
for (index, line) in io::BufReader::new(fs::File::open(&args[1])?)
.lines()
.enumerate()
{
let line = line?;
let mut input = Input(line.split_whitespace());
let op = input.0.next().unwrap();
writeln!(out, "command {index} {op}")?;
match op {
"reset" => {
position = ChunkPos::new(input.int(), input.int());
chunk = ProtoBlockEntities::new(position);
}
"pending" | "resolve" => {
let mut entries = std::collections::BTreeMap::new();
entries.insert("mark".into(), Tag::Long(input.long()));
for name in ["x", "y", "z"] {
if let Some(tag) = input.tag() {
entries.insert(name.into(), tag);
}
}
let tag = Tag::Compound(entries);
writeln!(out, "position {}", xyz(position_from_tag(position, &tag)?))?;
if op == "pending" {
chunk.set_pending(tag)?;
}
}
"active" => {
let p = input.pos();
chunk.set_active(p, input.long());
}
"remove" => {
chunk.remove(input.pos());
}
"mutate" => {
let p = input.pos();
let x = input.int();
if let Some(Tag::Compound(map)) = chunk.pending_mut(p) {
map.insert("x".into(), Tag::Int(x));
}
}
"get" => {
let p = input.pos();
writeln!(
out,
"active {}",
chunk.active(p).map_or("none".into(), ToString::to_string)
)?;
writeln!(out, "pending {}", pending(chunk.pending(p)))?;
}
"save" => {
let p = input.pos();
let saved = chunk.for_saving(p, |mark| {
Ok::<_, io::Error>(Tag::compound([("LootTableSeed", Tag::Long(*mark))]))
})?;
if chunk.active(p).is_some() {
let Some(Tag::Long(seed)) = saved.as_ref().unwrap().get("LootTableSeed") else {
unreachable!()
};
writeln!(out, "saved active minecraft:chest {} {seed}", xyz(p))?;
} else {
writeln!(out, "saved pending {}", pending(saved.as_ref()))?;
}
}
"dump" => {
let mut positions: Vec<_> = chunk.positions().into_iter().collect();
positions.sort_by_key(|p| (p.x, p.y, p.z));
for p in &positions {
writeln!(
out,
"entry {} {} {}",
xyz(*p),
chunk.active(*p).map_or("none".into(), ToString::to_string),
pending(chunk.pending(*p))
)?;
}
writeln!(
out,
"size {} {}",
positions.len(),
chunk.pending_entries().len()
)?;
}
_ => panic!("unknown command"),
}
assert!(input.0.next().is_none());
}
out.flush()?;
Ok(())
}
+102
View File
@@ -0,0 +1,102 @@
//! Proto-chunk block-entity storage. Active payloads are caller-owned runtime
//! types; this module does not create entities or execute their behavior.
use crate::{compression::invalid, nbt::Tag};
use horizon_core::position::{BlockPos, ChunkPos};
use std::{
collections::{HashMap, HashSet},
io,
};
/// Resolve native block-entity coordinates, relocating mismatched X/Z into the
/// owning chunk while retaining local offsets and the full Y coordinate. The
/// supplied tag is not rewritten. NumericTag float/double reads use Mth.floor.
pub fn position_from_tag(chunk: ChunkPos, tag: &Tag) -> io::Result<BlockPos> {
if !matches!(tag, Tag::Compound(_)) {
return Err(invalid("block entity is not a compound"));
}
let read = |name| match tag.get(name) {
Some(Tag::Byte(v)) => i32::from(*v),
Some(Tag::Short(v)) => i32::from(*v),
Some(Tag::Int(v)) => *v,
Some(Tag::Long(v)) => *v as i32,
Some(Tag::Float(bits)) => f64::from(f32::from_bits(*bits)).floor() as i32,
Some(Tag::Double(bits)) => f64::from_bits(*bits).floor() as i32,
_ => 0,
};
let (mut x, y, mut z) = (read("x"), read("y"), read("z"));
if x >> 4 != chunk.x || z >> 4 != chunk.z {
x = chunk.x.wrapping_shl(4).wrapping_add(x & 15);
z = chunk.z.wrapping_shl(4).wrapping_add(z & 15);
}
Ok(BlockPos::new(x, y, z))
}
pub struct ProtoBlockEntities<E> {
position: ChunkPos,
pending: HashMap<BlockPos, Tag>,
active: HashMap<BlockPos, E>,
}
impl<E> ProtoBlockEntities<E> {
pub fn new(position: ChunkPos) -> Self {
Self {
position,
pending: HashMap::new(),
active: HashMap::new(),
}
}
/// Ignore a pending record when an active entity already owns its resolved
/// position. Inserting an active entity removes any earlier pending record.
pub fn set_pending(&mut self, tag: Tag) -> io::Result<()> {
let position = position_from_tag(self.position, &tag)?;
if !self.active.contains_key(&position) {
self.pending.insert(position, tag);
}
Ok(())
}
/// Position must be the payload's immutable world position, as supplied by
/// the runtime's block-entity constructor. No chunk/build-height gate applies.
pub fn set_active(&mut self, position: BlockPos, entity: E) -> Option<E> {
self.pending.remove(&position);
self.active.insert(position, entity)
}
pub fn active(&self, position: BlockPos) -> Option<&E> {
self.active.get(&position)
}
pub fn active_mut(&mut self, position: BlockPos) -> Option<&mut E> {
self.active.get_mut(&position)
}
pub fn pending(&self, position: BlockPos) -> Option<&Tag> {
self.pending.get(&position)
}
/// Mutating coordinate fields does not re-key an existing pending entry.
pub fn pending_mut(&mut self, position: BlockPos) -> Option<&mut Tag> {
self.pending.get_mut(&position)
}
pub fn pending_entries(&self) -> &HashMap<BlockPos, Tag> {
&self.pending
}
pub fn positions(&self) -> HashSet<BlockPos> {
self.pending
.keys()
.chain(self.active.keys())
.copied()
.collect()
}
pub fn remove(&mut self, position: BlockPos) -> Option<E> {
self.pending.remove(&position);
self.active.remove(&position)
}
/// Serialize an active entity when present, otherwise clone its pending tag.
/// The caller supplies runtime serialization; this does not instantiate NBT.
pub fn for_saving<T>(
&self,
position: BlockPos,
serialize: impl FnOnce(&E) -> Result<Tag, T>,
) -> Result<Option<Tag>, T> {
if let Some(entity) = self.active.get(&position) {
serialize(entity).map(Some)
} else {
Ok(self.pending.get(&position).cloned())
}
}
}
+1
View File
@@ -2,6 +2,7 @@
#![forbid(unsafe_code)]
pub mod block_collision;
pub mod block_entities;
pub mod block_placement;
pub mod block_properties;
pub mod block_support;
@@ -0,0 +1,73 @@
use horizon_core::position::{BlockPos, ChunkPos};
use horizon_world::{block_entities::ProtoBlockEntities, nbt::Tag};
#[test]
fn invalid_pending_data_is_rejected_without_replacing_active_or_pending_entries() {
let mut store = ProtoBlockEntities::new(ChunkPos::new(0, 0));
let active = BlockPos::new(0, 64, 0);
store.set_active(active, "active");
let pending = Tag::compound([("x", Tag::Int(1)), ("y", Tag::Int(64)), ("z", Tag::Int(0))]);
store.set_pending(pending.clone()).unwrap();
assert!(store.set_pending(Tag::String("invalid".into())).is_err());
assert_eq!(store.active(active), Some(&"active"));
assert_eq!(store.pending(BlockPos::new(1, 64, 0)), Some(&pending));
}
#[test]
fn failed_active_serialization_never_falls_back_to_stale_pending_data() {
let p = BlockPos::new(-1, 64, -1);
let mut store = ProtoBlockEntities::new(ChunkPos::new(-1, -1));
store
.set_pending(Tag::compound([
("x", Tag::Int(15)),
("y", Tag::Int(64)),
("z", Tag::Int(15)),
]))
.unwrap();
store.set_active(p, 42);
assert!(store.pending(p).is_none());
let result = store.for_saving(p, |_| Err::<Tag, _>("serialization failed"));
assert_eq!(result, Err("serialization failed"));
assert_eq!(store.remove(p), Some(42));
assert_eq!(
store.for_saving(p, |_| Err::<Tag, _>("must not run")),
Ok(None)
);
}
#[test]
fn pending_save_snapshot_is_independent_and_mutating_coordinates_keeps_the_key() {
let mut store = ProtoBlockEntities::<()>::new(ChunkPos::new(0, 0));
let p = BlockPos::new(0, 0, 0);
store
.set_pending(Tag::compound([("payload", Tag::Long(42))]))
.unwrap();
let snapshot = store.for_saving(p, |_| Err::<Tag, _>(())).unwrap().unwrap();
let Some(Tag::Compound(tag)) = store.pending_mut(p) else {
panic!("pending")
};
tag.insert("x".into(), Tag::Int(123));
assert_eq!(snapshot.get("x"), None);
assert_eq!(store.pending(p).unwrap().get("x"), Some(&Tag::Int(123)));
assert!(store.pending(BlockPos::new(123, 0, 0)).is_none());
}
#[test]
fn floating_coordinates_floor_before_saturating_to_java_int() {
use horizon_world::block_entities::position_from_tag;
for (value, expected) in [
(Tag::Float(0xcf000001), i32::MIN),
(Tag::Double(0xc1e0000000000001), i32::MIN),
(Tag::Float(f32::NEG_INFINITY.to_bits()), i32::MIN),
(Tag::Double(f64::INFINITY.to_bits()), i32::MAX),
(Tag::Double(f64::NAN.to_bits()), 0),
(Tag::Float((-1.5_f32).to_bits()), -2),
(Tag::Double((-1.5_f64).to_bits()), -2),
] {
let tag = Tag::compound([("y", value)]);
assert_eq!(
position_from_tag(ChunkPos::new(0, 0), &tag).unwrap().y,
expected
);
}
}
+20
View File
@@ -813,3 +813,23 @@ Vanilla's actual public/private write-zone methods provide the reference. Worksp
tests, all-target Clippy and both focused fixture tests pass. The owning world
still supplies generation-step metadata and upgrading-world vertical restrictions;
full WorldGenRegion effects remain pending.
## Proto-chunk block-entity storage
Rust now stores pending NBT and caller-owned active block-entity payloads by full
block position. Active insertion removes pending data, later pending insertion
cannot overwrite an active entry, and removal clears both maps. Saving uses the
active serializer when present and otherwise returns a pending snapshot. Changing
pending coordinate fields retains the existing map key. Coordinate resolution
matches NumericTag integer narrowing and floating-point flooring before saturating
conversion, then relocates mismatched X/Z into the owning chunk.
The [block-entity storage comparison](../benchmarks/rust-block-entity-storage-2026-10-06/scoped-parity.json)
matches **78,251 lines per reference JAR** across **28,511 commands**, using actual
native ProtoChunks and chest entities. Cases cover replacement, removal, position
unions, save selection, missing/nonnumeric coordinates, infinities/NaNs, integer
extremes and positions that alias when packed. Four focused Rust tests and
all-target Clippy pass. Collection order is normalized; Rust exposes borrowed or
owned data instead of unrestricted Java aliases. Entity construction from NBT,
complete serializers, active entity behavior and resident chunk lifecycle
integration remain pending.
@@ -0,0 +1,62 @@
package dev.horizonlab.parity;
import java.io.*;
import java.nio.file.*;
import java.util.*;
import net.minecraft.core.*;
import net.minecraft.core.registries.*;
import net.minecraft.nbt.*;
import net.minecraft.resources.*;
import net.minecraft.world.level.*;
import net.minecraft.world.level.biome.*;
import net.minecraft.world.level.block.*;
import net.minecraft.world.level.block.entity.*;
import net.minecraft.world.level.chunk.*;
import net.minecraft.world.level.storage.loot.LootTable;
/** Native proto-chunk entity storage and numeric position transport. */
public final class VanillaBlockEntityStorageOracle {
private static final Comparator<BlockPos> ORDER=Comparator.<BlockPos>comparingInt(BlockPos::getX).thenComparingInt(BlockPos::getY).thenComparingInt(BlockPos::getZ);
private static PalettedContainerFactory factory;
private static String xyz(BlockPos p){return p.getX()+" "+p.getY()+" "+p.getZ();}
private static final class Input {
String[] words;int cursor=1;Input(String line){words=line.split(" ");}
int integer(){return Integer.parseInt(words[cursor++]);}long number(){return Long.parseLong(words[cursor++]);}
BlockPos pos(){return new BlockPos(integer(),integer(),integer());}
Tag tag(){String type=words[cursor++],value=words[cursor++];return switch(type){
case "none"->null;case "byte"->ByteTag.valueOf(Byte.parseByte(value));case "short"->ShortTag.valueOf(Short.parseShort(value));case "int"->IntTag.valueOf(Integer.parseInt(value));case "long"->LongTag.valueOf(Long.parseLong(value));
case "float"->FloatTag.valueOf(Float.intBitsToFloat(Integer.parseUnsignedInt(value,16)));case "double"->DoubleTag.valueOf(Double.longBitsToDouble(Long.parseUnsignedLong(value,16)));case "string"->StringTag.valueOf(value);default->throw new AssertionError(type);
};}
}
private static String atom(Tag tag){
if(tag==null)return "none";
if(tag instanceof FloatTag value)return "float:"+Integer.toUnsignedString(Float.floatToRawIntBits(value.floatValue()),16);
if(tag instanceof DoubleTag value)return "double:"+Long.toUnsignedString(Double.doubleToRawLongBits(value.doubleValue()),16);
if(tag instanceof NumericTag value)return tag.getId()+":"+value.longValue();
return tag.getId()+":invalid";
}
private static String pending(CompoundTag tag){return tag==null?"none":tag.getLongOr("mark",0)+" "+atom(tag.get("x"))+" "+atom(tag.get("y"))+" "+atom(tag.get("z"));}
public static void main(String[] args)throws Exception {
net.minecraft.SharedConstants.tryDetectVersion();net.minecraft.server.Bootstrap.bootStrap();
Holder<Biome> biome=Holder.Reference.createStandAlone(new HolderOwner<Biome>(){},Biomes.PLAINS);var ids=new IdMapper<Holder<Biome>>();ids.add(biome);
factory=new PalettedContainerFactory(Strategy.createForBlockStates(Block.BLOCK_STATE_REGISTRY),Blocks.AIR.defaultBlockState(),null,Strategy.createForBiomes(ids),biome,null);
var registries=RegistryAccess.fromRegistryOfRegistries(BuiltInRegistries.REGISTRY);
var loot=ResourceKey.create(Registries.LOOT_TABLE,Identifier.parse("horizon:storage_probe"));
ProtoChunk chunk=null;
try(var input=Files.newBufferedReader(Path.of(args[0]));var out=new PrintWriter(Files.newBufferedWriter(Path.of(args[1])))){
String line;int index=0;
while((line=input.readLine())!=null){var in=new Input(line);String op=in.words[0];out.println("command "+index+++" "+op);
switch(op){
case "reset"->chunk=new ProtoChunk(new ChunkPos(in.integer(),in.integer()),UpgradeData.EMPTY,LevelHeightAccessor.create(-64,384),factory,null);
case "pending","resolve"->{var tag=new CompoundTag();tag.putLong("mark",in.number());for(String axis:new String[]{"x","y","z"}){var value=in.tag();if(value!=null)tag.put(axis,value);}out.println("position "+xyz(BlockEntity.getPosFromTag(chunk.getPos(),tag)));if(op.equals("pending"))chunk.setBlockEntityNbt(tag);}
case "active"->{var p=in.pos();var entity=new ChestBlockEntity(p,Blocks.CHEST.defaultBlockState());entity.setLootTable(loot,in.number());chunk.setBlockEntity(entity);}
case "remove"->chunk.removeBlockEntity(in.pos());
case "mutate"->{var p=in.pos();int x=in.integer();var tag=chunk.getBlockEntityNbt(p);if(tag!=null)tag.putInt("x",x);}
case "get","save"->{var p=in.pos();if(op.equals("get")){var active=chunk.getBlockEntity(p);out.println("active "+(active==null?"none":((ChestBlockEntity)active).getLootTableSeed()));out.println("pending "+pending(chunk.getBlockEntityNbt(p)));}else{var tag=chunk.getBlockEntityNbtForSaving(p,registries);if(chunk.getBlockEntity(p)!=null)out.println("saved active "+tag.getStringOr("id","")+" "+tag.getIntOr("x",0)+" "+tag.getIntOr("y",0)+" "+tag.getIntOr("z",0)+" "+tag.getLongOr("LootTableSeed",0));else out.println("saved pending "+pending(tag));}}
case "dump"->{var positions=new TreeSet<BlockPos>(ORDER);positions.addAll(chunk.getBlockEntitiesPos());for(var p:positions){var entity=chunk.getBlockEntity(p);out.println("entry "+xyz(p)+" "+(entity==null?"none":((ChestBlockEntity)entity).getLootTableSeed())+" "+pending(chunk.getBlockEntityNbt(p)));}out.println("size "+positions.size()+" "+chunk.getBlockEntityNbts().size());}
default->throw new AssertionError(op);
}
if(in.cursor!=in.words.length)throw new AssertionError("extra input");
}
}
}
}
+95
View File
@@ -0,0 +1,95 @@
#!/usr/bin/env python3
"""Compare native proto-chunk block-entity storage and NBT coordinate coercion."""
import argparse
from collections import Counter
import json
import os
from pathlib import Path
import platform
import random
import subprocess
try:
from . import lab, test_rust_parity as base
except ImportError:
import lab, test_rust_parity as base
def corpus():
atoms=['none 0','string invalid']
for kind,values in [('byte',[-128,-1,0,1,127]),('short',[-32768,-1,0,1,32767]),('int',[-2147483648,-17,-16,-1,0,1,15,16,2147483647]),('long',[-9223372036854775808,-4294967297,-2147483649,-1,0,2147483648,4294967297,9223372036854775807])]:
atoms += [f'{kind} {v}' for v in values]
atoms += ['float '+bits for bits in ['00000000','80000000','00000001','80000001','3fc00000','bfc00000','4f000000','cf000000','cf000001','7f800000','ff800000','7fc00000','7f800001']]
atoms += ['double '+bits for bits in ['0000000000000000','8000000000000000','0000000000000001','8000000000000001','3ff8000000000000','bff8000000000000','41e0000000000000','c1e0000000000000','c1e0000000000001','7ff0000000000000','fff0000000000000','7ff8000000000000','7ff0000000000001']]
for cx,cz in [(0,0),(-1,-1),(1,-2),(-134217728,134217727),(-2147483648,2147483647)]:
yield f'reset {cx} {cz}'
for axis in range(3):
for i,atom in enumerate(atoms):
xyz=['int -1','int 64','int 31'];xyz[axis]=atom
yield 'resolve 0 '+' '.join(xyz)
yield f'pending {i} '+' '.join(xyz)
yield 'dump'
for marker in [-9223372036854775808,-1,0,1,9223372036854775807]:
yield 'reset -1 -1'
yield f'pending {marker} int 15 int 64 int 15'
yield 'get -1 64 -1';yield 'save -1 64 -1'
yield 'mutate -1 64 -1 2147483647'
yield 'get -1 64 -1';yield 'save -1 64 -1'
yield f'active -1 64 -1 {marker}'
yield 'pending 99 int 15 int 64 int 15'
yield 'get -1 64 -1';yield 'save -1 64 -1';yield 'dump'
yield 'remove -1 64 -1';yield 'get -1 64 -1';yield 'save -1 64 -1'
rng=random.Random(0x424c4f434b454e54)
for trial in range(12):
cx,cz=trial%3-1,trial%5-2
yield f'reset {cx} {cz}'
positions=[(cx*16+x,y,cz*16+z) for x in [0,1,15] for y in [-2147483648,-64,0,64,2147483647] for z in [0,1,15]]
positions += [(2147483647,-2147483648,-2147483648),(0,4096,0),(67108864,4096,-67108864)]
for _ in range(500):
p=rng.choice(positions);xyz=' '.join(map(str,p));op=rng.randrange(7)
if op<2:yield f'pending {rng.randrange(-2**63,2**63)} int {p[0]} int {p[1]} int {p[2]}'
elif op==2:yield f'active {xyz} {rng.randrange(-2**63,2**63)}'
elif op==3:yield f'remove {xyz}'
elif op==4:yield f'mutate {xyz} {rng.randrange(-2**31,2**31)}'
elif op==5:yield f'get {xyz}'
else:yield f'save {xyz}'
if rng.randrange(8)==0:yield 'dump'
yield 'dump'
yield 'reset 0 0'
for _ in range(20000):
xyz=[rng.choice([f'float {rng.getrandbits(32):08x}',f'double {rng.getrandbits(64):016x}',rng.choice(atoms)]) for _ in range(3)]
yield 'resolve 0 '+' '.join(xyz)
def main():
parser=argparse.ArgumentParser(description=__doc__);parser.add_argument('--prepare',action='store_true');args=parser.parse_args()
build=lab.LOCAL/'rust-block-entity-storage-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/VanillaBlockEntityStorageOracle.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/block_entity_storage_fixture.rs']
tested_sources=base.source_hashes(sources)
commands=list(corpus());inputs=build/'inputs.txt';inputs.write_text('\n'.join(commands)+'\n')
tested_fixtures=base.source_hashes([inputs]);classes=build/'classes';classes.mkdir(exist_ok=True)
def run(command,name):
with (build/(name+'.log')).open('w') as log:subprocess.run(list(map(str,command)),cwd=lab.ROOT,check=True,timeout=3600,stdout=log,stderr=subprocess.STDOUT)
dependencies=os.pathsep.join(map(str,libraries))
run(['javac','--release',lock['minecraft']['java_major'],'-proc:none','-cp',str(jars['server'])+os.pathsep+dependencies,'-d',classes,oracle],'compile-java')
run(['cargo','build','--release','-p','horizon-world','--example','block_entity_storage_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'/('block_entity_storage_fixture.exe' if os.name=='nt' else 'block_entity_storage_fixture')
run([binary,inputs,build/'rust.txt'],'rust')
references=[]
for side,jar in jars.items():
run(['java','-Xmx512M','-cp',os.pathsep.join([str(classes),str(jar),dependencies]),'dev.horizonlab.parity.VanillaBlockEntityStorageOracle',inputs,build/(side+'.txt')],side)
matched=base.compare(build/(side+'.txt'),build/'rust.txt')
references.append({'side':side,'jar_sha256':lab.digest(jar,'sha256'),'output_sha256':lab.digest(build/(side+'.txt'),'sha256'),'matched_output_lines':matched})
print(f'{side}: {matched:,} block-entity storage lines match Rust',flush=True)
base.ensure_sources_unchanged(sources,tested_sources);base.ensure_sources_unchanged([inputs],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(),
'commands':dict(Counter(c.split()[0] for c in commands)),'sources_sha256':tested_sources,'fixtures_sha256':tested_fixtures,'rust_output_sha256':lab.digest(build/'rust.txt','sha256'),'references':references,
'scope':'Native BlockEntity.getPosFromTag and actual ProtoChunk pending/active insertion, lookup, removal, position union and pending-save fallback. NumericTag coordinate reads cover integer narrowing, float/double floor overflow, infinities/NaNs, missing/nonnumeric fields, wrong-chunk relocation and wrapped chunk coordinates. Active entries replace pending data; later pending records cannot overwrite active entries. In-place pending coordinate mutation retains the original map key. Native chest entities carry loot seeds for active identity and basic saving-metadata comparisons; Rust supplies caller-owned payload serialization. Iteration order is normalized, and pending reads are borrowed/owned values rather than unrestricted Java aliases. Entity creation from NBT, complete entity serializers, active behavior and integration with chunk lifecycle remain pending.'}
lab.write_json(report_path,report);print(f'Scoped block-entity storage parity passed: {report_path}')
if __name__=='__main__':main()