Match world border extent geometry and tick-driven interpolation
This commit is contained in:
@@ -0,0 +1,209 @@
|
||||
{
|
||||
"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": 1072,
|
||||
"sources_sha256": {
|
||||
"mods/horizon/src/test/java/dev/horizonlab/parity/VanillaWorldBorderOracle.java": "d3366aacd9e6ea415046007dc1047ad44d15722cca5db2a685a0649642b91d50",
|
||||
"mods/horizon/src/test/java/dev/horizonlab/parity/VanillaFaceSupportOracle.java": "9ce939a435842867786fed648951bbe77f6ab843f6df09af15f3deb1948c4877",
|
||||
"tools/test_world_border_parity.py": "29d3935d6c9fa77f4ddad8ebd9ce611421dbbc1a4eb80fd145bb24b3c0bb3313",
|
||||
"tools/test_rust_parity.py": "29d3db08005873528894b45ca10dcd6278753650473b3eb574e53e06ede40ec6",
|
||||
"tools/lab.py": "d00bf107fa01b6c579734a082c3ca4ade3018f0d6851d46844acf17574f66e64",
|
||||
"Cargo.toml": "3f93319c1eb70dbd7ba25cd74ef343fbf35c52c7c4752f955605c58699cbff9d",
|
||||
"Cargo.lock": "cb1e87d449114a63edd904c0559d631e8fa9feeb27efd6539aacdcfeae178876",
|
||||
"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/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/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_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/chunk.rs": "8843e6f48dbbc4245ce4e95564dfba3a4d7a051ba0b1577fb038c07adb0ff0f9",
|
||||
"crates/horizon-world/src/chunk_status.rs": "dbb8583cfa775203c544d24d69e03720385428f15a267a561356c21f5025e9c7",
|
||||
"crates/horizon-world/src/collision.rs": "a749794d5c27ccd6c0d964ad02f9dc31d49d9f4fe4b7366cd4bd8155e681b00b",
|
||||
"crates/horizon-world/src/collision_context.rs": "2176c7034e07f547510a8327cdad9d3b63f26e4754032b76b7d5fe13166c333c",
|
||||
"crates/horizon-world/src/compression.rs": "4db73e6ca9a0d552d415ce60e7e88b89c9cf2b7d060a669ff8328f518316e101",
|
||||
"crates/horizon-world/src/dynamic_support.rs": "5996bc1c436fb6215d8fc3d0bb4de5dd9b966d842d432580d624195771697fb8",
|
||||
"crates/horizon-world/src/entity_collision.rs": "09f2fd7f6cd56fc282f468ecfa765a94506929e9684bd3a5c90123e5b51cd627",
|
||||
"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": "434f48e310beb66490f6d861d326a5382a04dfb30b253676710b3f9fed6ca0c8",
|
||||
"crates/horizon-world/src/liquid_collision.rs": "0f0a83b04e5a5bc9d98ed05fde4d6584ba69642e93522e726e4cfc325d6fcb1d",
|
||||
"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/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": "a5021a168adcedc37d77f533a8921c02f09c59f46a01737586aaa33c5908b8d0",
|
||||
"crates/horizon-world/src/retrogen.rs": "6efd36a6293756688c1990a7965bd65609d7c5e2bf2a5f4f01d54932d2b9386b",
|
||||
"crates/horizon-world/src/suffocation.rs": "bed219dd5bbab6263b98fbe3d0eb512cb96c52ae847210efe49514d05f22f11e",
|
||||
"crates/horizon-world/src/tick_data.rs": "f8d04a69ea263f4168bce33f92e5b4ee2fe07d7caf50a1621c6b3ef6eb2479f3",
|
||||
"crates/horizon-world/src/world_movement.rs": "8ac9e90fe899855036658afd862ff20c4dcd15d1bd3f92a763c4795fecf0549f",
|
||||
"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": "d35ded93fe6e95ea045cadcf4925e656dc34ef832b742179b9ad0cc49f902f31",
|
||||
"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/world_border_fixture.rs": "2ee668ec9b6a81167eeeac16990914ded015057d02f35e9f1885b5ba3130b74e"
|
||||
},
|
||||
"fixtures_sha256": {
|
||||
".local/rust-world-border-parity/inputs.txt": "17e5d1494da670d8b9856a212486e433f4e449f54b018ed807dff656554d9deb"
|
||||
},
|
||||
"rust_output_sha256": "5216f1c366de17cdc9a9de1bf1ede19ac83c14363a57729de755e27faa18a446",
|
||||
"references": [
|
||||
{
|
||||
"side": "client",
|
||||
"jar_sha256": "4508d006323f24fa02876310c192d739af56516eb259000ac50f0909a68c9a2d",
|
||||
"output_sha256": "5216f1c366de17cdc9a9de1bf1ede19ac83c14363a57729de755e27faa18a446",
|
||||
"matched_output_lines": 1138830
|
||||
},
|
||||
{
|
||||
"side": "server",
|
||||
"jar_sha256": "a362163eec5d1612d520772bc16e5b39c09e3b234fdc045f56bf544284ee8ae6",
|
||||
"output_sha256": "5216f1c366de17cdc9a9de1bf1ede19ac83c14363a57729de755e27faa18a446",
|
||||
"matched_output_lines": 1138830
|
||||
}
|
||||
],
|
||||
"scope": "Actual WorldBorder stationary/moving extents, center/absolute-limit updates, tick progression, size/target/time/speed/status, float partial-tick bounds, full collision shape grids and proximity predicates. Covers finite nonnegative sizes, finite centers, nonnegative absolute limits and durations, signed zero, zero and very large durations, wrapping start timestamps, tick completion, retargeting, fractional boundaries and nonfinite predicate/partial-tick inputs. Rust returns typed errors for parameters outside this supported domain. Border saved-data codecs, listeners, dirty-state events, damage, networking and live movement integration remain pending."
|
||||
}
|
||||
@@ -0,0 +1,266 @@
|
||||
//! Pinned world-border extent geometry and tick-driven size interpolation.
|
||||
//! Saved-data codecs, listeners, damage and synchronization are separate layers.
|
||||
use super::{Aabb, ShapeError, ShapeOp, Vec3, VoxelShape, clamp, java_max, java_min};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum BorderError {
|
||||
InvalidParameters,
|
||||
Shape(ShapeError),
|
||||
}
|
||||
impl std::fmt::Display for BorderError {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {Self::InvalidParameters=>f.write_str("border requires finite nonnegative sizes, finite centers and nonnegative limits/duration"),Self::Shape(e)=>e.fmt(f)}
|
||||
}
|
||||
}
|
||||
impl std::error::Error for BorderError {}
|
||||
impl From<ShapeError> for BorderError {
|
||||
fn from(e: ShapeError) -> Self {
|
||||
Self::Shape(e)
|
||||
}
|
||||
}
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
pub enum BorderStatus {
|
||||
Stationary,
|
||||
Growing,
|
||||
Shrinking,
|
||||
}
|
||||
#[derive(Clone)]
|
||||
enum Extent {
|
||||
Static {
|
||||
size: f64,
|
||||
bounds: [f64; 4],
|
||||
shape: VoxelShape,
|
||||
},
|
||||
Moving {
|
||||
from: f64,
|
||||
to: f64,
|
||||
duration: i64,
|
||||
remaining: i64,
|
||||
size: f64,
|
||||
previous: f64,
|
||||
},
|
||||
}
|
||||
pub struct BorderGeometry {
|
||||
center: [f64; 2],
|
||||
absolute_max: i32,
|
||||
extent: Extent,
|
||||
}
|
||||
impl BorderGeometry {
|
||||
pub fn new(center: [f64; 2], size: f64, absolute_max: i32) -> Result<Self, BorderError> {
|
||||
if !center.into_iter().all(f64::is_finite)
|
||||
|| !size.is_finite()
|
||||
|| size < 0.0
|
||||
|| absolute_max < 0
|
||||
{
|
||||
return Err(BorderError::InvalidParameters);
|
||||
}
|
||||
Ok(Self {
|
||||
center,
|
||||
absolute_max,
|
||||
extent: Self::stationary(center, size, absolute_max)?,
|
||||
})
|
||||
}
|
||||
fn stationary(center: [f64; 2], size: f64, absolute_max: i32) -> Result<Extent, BorderError> {
|
||||
let bounds = Self::extent_bounds(center, size, absolute_max);
|
||||
Ok(Extent::Static {
|
||||
size,
|
||||
bounds,
|
||||
shape: Self::shape_for(bounds)?,
|
||||
})
|
||||
}
|
||||
fn extent_bounds(center: [f64; 2], size: f64, absolute_max: i32) -> [f64; 4] {
|
||||
let limit = f64::from(absolute_max);
|
||||
let lower = f64::from(-absolute_max);
|
||||
[
|
||||
clamp(center[0] - size / 2.0, lower, limit),
|
||||
clamp(center[1] - size / 2.0, lower, limit),
|
||||
clamp(center[0] + size / 2.0, lower, limit),
|
||||
clamp(center[1] + size / 2.0, lower, limit),
|
||||
]
|
||||
}
|
||||
fn shape_for([min_x, min_z, max_x, max_z]: [f64; 4]) -> Result<VoxelShape, ShapeError> {
|
||||
let infinity = VoxelShape::from_box(Aabb::new(
|
||||
Vec3::new(f64::NEG_INFINITY, f64::NEG_INFINITY, f64::NEG_INFINITY),
|
||||
Vec3::new(f64::INFINITY, f64::INFINITY, f64::INFINITY),
|
||||
))?;
|
||||
let inside = VoxelShape::from_box(Aabb::new(
|
||||
Vec3::new(min_x.floor(), f64::NEG_INFINITY, min_z.floor()),
|
||||
Vec3::new(max_x.ceil(), f64::INFINITY, max_z.ceil()),
|
||||
))?;
|
||||
infinity.join(&inside, ShapeOp::ONLY_FIRST)
|
||||
}
|
||||
pub fn center(&self) -> [f64; 2] {
|
||||
self.center
|
||||
}
|
||||
pub fn set_center(&mut self, center: [f64; 2]) -> Result<(), BorderError> {
|
||||
if !center.into_iter().all(f64::is_finite) {
|
||||
return Err(BorderError::InvalidParameters);
|
||||
}
|
||||
if let Extent::Static { size, .. } = self.extent {
|
||||
self.extent = Self::stationary(center, size, self.absolute_max)?;
|
||||
}
|
||||
self.center = center;
|
||||
Ok(())
|
||||
}
|
||||
pub fn set_absolute_max(&mut self, limit: i32) -> Result<(), BorderError> {
|
||||
if limit < 0 {
|
||||
return Err(BorderError::InvalidParameters);
|
||||
}
|
||||
if let Extent::Static { size, .. } = self.extent {
|
||||
self.extent = Self::stationary(self.center, size, limit)?;
|
||||
}
|
||||
self.absolute_max = limit;
|
||||
Ok(())
|
||||
}
|
||||
pub fn set_size(&mut self, size: f64) -> Result<(), BorderError> {
|
||||
if !size.is_finite() || size < 0.0 {
|
||||
return Err(BorderError::InvalidParameters);
|
||||
}
|
||||
self.extent = Self::stationary(self.center, size, self.absolute_max)?;
|
||||
Ok(())
|
||||
}
|
||||
/// Native start/end tick timestamps cancel in the exposed speed calculation;
|
||||
/// progress itself advances by one per tick, independently of wall time.
|
||||
pub fn lerp_size_between(
|
||||
&mut self,
|
||||
from: f64,
|
||||
to: f64,
|
||||
duration: i64,
|
||||
) -> Result<(), BorderError> {
|
||||
if !from.is_finite() || !to.is_finite() || from < 0.0 || to < 0.0 || duration < 0 {
|
||||
return Err(BorderError::InvalidParameters);
|
||||
}
|
||||
if from == to {
|
||||
return self.set_size(to);
|
||||
}
|
||||
let size = Self::moving_size(from, to, duration, duration);
|
||||
self.extent = Extent::Moving {
|
||||
from,
|
||||
to,
|
||||
duration,
|
||||
remaining: duration,
|
||||
size,
|
||||
previous: size,
|
||||
};
|
||||
Ok(())
|
||||
}
|
||||
fn moving_size(from: f64, to: f64, duration: i64, remaining: i64) -> f64 {
|
||||
let fraction = (duration as f64 - remaining as f64) / duration as f64;
|
||||
if fraction < 1.0 {
|
||||
from + fraction * (to - from)
|
||||
} else {
|
||||
to
|
||||
}
|
||||
}
|
||||
pub fn tick(&mut self) -> Result<(), BorderError> {
|
||||
if let Extent::Moving {
|
||||
from,
|
||||
to,
|
||||
duration,
|
||||
remaining,
|
||||
size,
|
||||
previous,
|
||||
} = &mut self.extent
|
||||
{
|
||||
*remaining = remaining.wrapping_sub(1);
|
||||
*previous = *size;
|
||||
*size = Self::moving_size(*from, *to, *duration, *remaining);
|
||||
if *remaining <= 0 {
|
||||
self.extent = Self::stationary(self.center, *to, self.absolute_max)?;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
pub fn size(&self) -> f64 {
|
||||
match self.extent {
|
||||
Extent::Static { size, .. } | Extent::Moving { size, .. } => size,
|
||||
}
|
||||
}
|
||||
pub fn lerp_target(&self) -> f64 {
|
||||
match self.extent {
|
||||
Extent::Static { size, .. } => size,
|
||||
Extent::Moving { to, .. } => to,
|
||||
}
|
||||
}
|
||||
pub fn lerp_time(&self) -> i64 {
|
||||
match self.extent {
|
||||
Extent::Static { .. } => 0,
|
||||
Extent::Moving { remaining, .. } => remaining,
|
||||
}
|
||||
}
|
||||
pub fn lerp_speed(&self) -> f64 {
|
||||
match self.extent {
|
||||
Extent::Static { .. } => 0.0,
|
||||
Extent::Moving {
|
||||
from, to, duration, ..
|
||||
} => (from - to).abs() / duration as f64,
|
||||
}
|
||||
}
|
||||
pub fn status(&self) -> BorderStatus {
|
||||
match self.extent {
|
||||
Extent::Static { .. } => BorderStatus::Stationary,
|
||||
Extent::Moving { from, to, .. } => {
|
||||
if to < from {
|
||||
BorderStatus::Shrinking
|
||||
} else {
|
||||
BorderStatus::Growing
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
/// [minX, minZ, maxX, maxZ]. Ordinary collision bounds use partial_tick=0,
|
||||
/// hence the previous size while moving. Static extents ignore partial_tick.
|
||||
pub fn bounds(&self, partial_tick: f32) -> [f64; 4] {
|
||||
match self.extent {
|
||||
Extent::Static { bounds, .. } => bounds,
|
||||
Extent::Moving { size, previous, .. } => Self::extent_bounds(
|
||||
self.center,
|
||||
previous + f64::from(partial_tick) * (size - previous),
|
||||
self.absolute_max,
|
||||
),
|
||||
}
|
||||
}
|
||||
pub fn collision_shape(&self) -> Result<VoxelShape, ShapeError> {
|
||||
match &self.extent {
|
||||
Extent::Static { shape, .. } => Ok(shape.clone()),
|
||||
Extent::Moving { .. } => Self::shape_for(self.bounds(0.0)),
|
||||
}
|
||||
}
|
||||
pub fn distance_to_border(&self, x: f64, z: f64) -> f64 {
|
||||
let [min_x, min_z, max_x, max_z] = self.bounds(0.0);
|
||||
java_min(
|
||||
java_min(java_min(x - min_x, max_x - x), z - min_z),
|
||||
max_z - z,
|
||||
)
|
||||
}
|
||||
pub fn is_within_bounds(&self, x: f64, z: f64, margin: f64) -> bool {
|
||||
let [min_x, min_z, max_x, max_z] = self.bounds(0.0);
|
||||
x >= min_x - margin && x < max_x + margin && z >= min_z - margin && z < max_z + margin
|
||||
}
|
||||
pub fn is_inside_close_to_border(&self, entity_x: f64, entity_z: f64, bounds: Aabb) -> bool {
|
||||
let size = bounds.size();
|
||||
let margin = java_max(java_max(size.x.abs(), size.z.abs()), 1.0);
|
||||
self.distance_to_border(entity_x, entity_z) < margin * 2.0
|
||||
&& self.is_within_bounds(entity_x, entity_z, margin)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
#[test]
|
||||
fn invalid_parameters_leave_existing_extent_unchanged() {
|
||||
let mut border = BorderGeometry::new([1.0, 2.0], 10.0, 100).unwrap();
|
||||
let bounds = border.bounds(0.0);
|
||||
assert!(border.set_center([f64::NAN, 0.0]).is_err());
|
||||
assert!(border.set_size(-1.0).is_err());
|
||||
assert!(border.set_size(f64::INFINITY).is_err());
|
||||
assert!(border.set_absolute_max(-1).is_err());
|
||||
assert!(border.lerp_size_between(10.0, 20.0, -1).is_err());
|
||||
assert!(border.lerp_size_between(10.0, f64::NAN, 1).is_err());
|
||||
assert_eq!(border.bounds(0.0), bounds);
|
||||
assert_eq!(border.center(), [1.0, 2.0]);
|
||||
assert_eq!(border.size(), 10.0);
|
||||
assert_eq!(border.status(), BorderStatus::Stationary);
|
||||
}
|
||||
}
|
||||
@@ -3,6 +3,7 @@
|
||||
//! Operation order and Java floating-point edge cases are deliberate. These are
|
||||
//! simulation coordinates; rendering may use a separate camera-relative format.
|
||||
|
||||
pub mod border;
|
||||
mod movement;
|
||||
mod shape;
|
||||
mod sweep;
|
||||
|
||||
@@ -0,0 +1,154 @@
|
||||
use horizon_core::geometry::{
|
||||
Aabb, Axis, Vec3, VoxelShape,
|
||||
border::{BorderGeometry, BorderStatus},
|
||||
};
|
||||
use serde_json::Value;
|
||||
use std::{
|
||||
fs,
|
||||
io::{self, Write},
|
||||
path::Path,
|
||||
};
|
||||
fn number(v: &Value) -> f64 {
|
||||
f64::from_bits(u64::from_str_radix(v.as_str().unwrap(), 16).unwrap())
|
||||
}
|
||||
fn values(
|
||||
out: &mut impl Write,
|
||||
label: &str,
|
||||
values: impl IntoIterator<Item = f64>,
|
||||
) -> io::Result<()> {
|
||||
write!(out, "{label}")?;
|
||||
for v in values {
|
||||
let bits = if v.is_nan() {
|
||||
0x7ff8_0000_0000_0000
|
||||
} else {
|
||||
v.to_bits()
|
||||
};
|
||||
write!(out, " {bits:016x}")?;
|
||||
}
|
||||
writeln!(out)
|
||||
}
|
||||
fn box_values(out: &mut impl Write, label: &str, b: Aabb) -> io::Result<()> {
|
||||
values(
|
||||
out,
|
||||
label,
|
||||
[
|
||||
b.min(Axis::X),
|
||||
b.min(Axis::Y),
|
||||
b.min(Axis::Z),
|
||||
b.max(Axis::X),
|
||||
b.max(Axis::Y),
|
||||
b.max(Axis::Z),
|
||||
],
|
||||
)
|
||||
}
|
||||
fn emit(out: &mut impl Write, shape: &VoxelShape) -> io::Result<()> {
|
||||
writeln!(
|
||||
out,
|
||||
"shape {} {} {}",
|
||||
shape.is_empty(),
|
||||
shape.boxes().len(),
|
||||
shape.is_cube()
|
||||
)?;
|
||||
for (axis, name) in Axis::ALL.into_iter().zip(["x", "y", "z"]) {
|
||||
values(out, name, shape.coordinates(axis).iter().copied())?;
|
||||
}
|
||||
let axes = shape.cube_coordinate_axes();
|
||||
writeln!(
|
||||
out,
|
||||
"axes {}",
|
||||
u8::from(axes[0]) + 2 * u8::from(axes[1]) + 4 * u8::from(axes[2])
|
||||
)?;
|
||||
values(
|
||||
out,
|
||||
"extrema",
|
||||
Axis::ALL
|
||||
.map(|a| shape.min(a))
|
||||
.into_iter()
|
||||
.chain(Axis::ALL.map(|a| shape.max(a))),
|
||||
)?;
|
||||
match shape.bounds() {
|
||||
Some(b) => box_values(out, "bounds", b)?,
|
||||
None => writeln!(out, "bounds none")?,
|
||||
}
|
||||
for b in shape.boxes() {
|
||||
box_values(out, "box", *b)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
let arg = std::env::args().nth(1).expect("private fixture directory");
|
||||
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 mut border = BorderGeometry::new([0.0, 0.0], 59999968.0, 29999984)?;
|
||||
let mut limit = 29999984;
|
||||
for op in f["ops"].as_array().unwrap() {
|
||||
match op[0].as_str().unwrap() {
|
||||
"center" => border.set_center([number(&op[1]), number(&op[2])])?,
|
||||
"size" => border.set_size(number(&op[1]))?,
|
||||
"limit" => {
|
||||
limit = op[1].as_i64().unwrap() as i32;
|
||||
border.set_absolute_max(limit)?;
|
||||
}
|
||||
"lerp" => border.lerp_size_between(
|
||||
number(&op[1]),
|
||||
number(&op[2]),
|
||||
op[3].as_i64().unwrap(),
|
||||
)?,
|
||||
"tick" => {
|
||||
for _ in 0..op[1].as_u64().unwrap() {
|
||||
border.tick()?;
|
||||
}
|
||||
}
|
||||
_ => panic!("unknown border operation"),
|
||||
}
|
||||
let status = match border.status() {
|
||||
BorderStatus::Stationary => "STATIONARY",
|
||||
BorderStatus::Growing => "GROWING",
|
||||
BorderStatus::Shrinking => "SHRINKING",
|
||||
};
|
||||
writeln!(out, "status {status} {} {limit}", border.lerp_time())?;
|
||||
let [x, z] = border.center();
|
||||
values(
|
||||
&mut out,
|
||||
"state",
|
||||
[
|
||||
x,
|
||||
z,
|
||||
border.size(),
|
||||
border.lerp_target(),
|
||||
border.lerp_speed(),
|
||||
],
|
||||
)?;
|
||||
for partial in f["partials"].as_array().unwrap() {
|
||||
values(
|
||||
&mut out,
|
||||
"bounds",
|
||||
border.bounds(f32::from_bits(u32::from_str_radix(
|
||||
partial.as_str().unwrap(),
|
||||
16,
|
||||
)?)),
|
||||
)?;
|
||||
}
|
||||
emit(&mut out, &border.collision_shape()?)?;
|
||||
for q in f["queries"].as_array().unwrap() {
|
||||
let x = number(&q[0]);
|
||||
let z = number(&q[1]);
|
||||
let margin = number(&q[2]);
|
||||
let b = Aabb::new(
|
||||
Vec3::new(number(&q[3]), number(&q[4]), number(&q[5])),
|
||||
Vec3::new(number(&q[6]), number(&q[7]), number(&q[8])),
|
||||
);
|
||||
values(&mut out, "distance", [border.distance_to_border(x, z)])?;
|
||||
writeln!(
|
||||
out,
|
||||
"inside {} {}",
|
||||
border.is_within_bounds(x, z, margin),
|
||||
border.is_inside_close_to_border(x, z, b)
|
||||
)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
@@ -497,3 +497,25 @@ outputs are covered. The [entity collision report](../benchmarks/rust-entity-col
|
||||
records **158,558 matching lines per JAR**. Workspace Clippy passes. This adds the
|
||||
selection helper needed by movement; live entity indexing, lifecycle and
|
||||
entity-type policy implementations remain pending.
|
||||
|
||||
|
||||
`BorderGeometry` now models stationary and moving world-border extents, cached
|
||||
stationary collision geometry, center/absolute-limit changes, tick progression,
|
||||
partial-tick bounds and movement proximity. The pinned version decrements size
|
||||
transition progress once per tick. Ordinary bounds and collision shapes use the
|
||||
previous size while moving; completing the transition installs a stationary
|
||||
extent at the target. Collision geometry subtracts the floor/ceil-rounded inner
|
||||
column from the infinite outer shape. Boundary predicates retain their asymmetric
|
||||
inclusive minimum/exclusive maximum tests and native floating-point order.
|
||||
|
||||
`python3 tools/test_world_border_parity.py` compares actual `WorldBorder` mutation
|
||||
sequences, extent metadata, complete shape geometry and proximity predicates.
|
||||
The [border report](../benchmarks/rust-world-border-2026-10-07/scoped-parity.json)
|
||||
records **1,138,830 matching lines per JAR**, including zero/large durations,
|
||||
wrapping start timestamps, completion, retargeting, signed zero, extreme finite
|
||||
centers/sizes and nonfinite query/interpolation inputs. The supported construction
|
||||
and mutation domain requires finite nonnegative sizes, finite centers and
|
||||
nonnegative absolute limits/durations. A unit test verifies invalid parameters
|
||||
leave the existing extent unchanged; workspace Clippy passes. Saved-data codecs,
|
||||
listeners, dirty-state events, damage, synchronization and live movement binding
|
||||
remain separate work.
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
package dev.horizonlab.parity;
|
||||
import com.google.gson.*;
|
||||
import java.io.*;
|
||||
import java.lang.reflect.*;
|
||||
import java.nio.file.*;
|
||||
import net.minecraft.world.entity.*;
|
||||
import net.minecraft.world.level.border.WorldBorder;
|
||||
import net.minecraft.world.phys.*;
|
||||
import net.minecraft.world.phys.shapes.*;
|
||||
/** Native border extents, interpolation ticks, collision shape and proximity. */
|
||||
public final class VanillaWorldBorderOracle {
|
||||
static double number(JsonElement v){return Double.longBitsToDouble(Long.parseUnsignedLong(v.getAsString(),16));}
|
||||
static void values(PrintWriter out,String label,double... values){out.print(label);for(double v:values)out.printf(" %016x",Double.doubleToLongBits(v));out.println();}
|
||||
public static void main(String[] args)throws Exception {
|
||||
net.minecraft.SharedConstants.tryDetectVersion();net.minecraft.server.Bootstrap.bootStrap();
|
||||
var allocator=Class.forName("sun.misc.Unsafe");var access=allocator.getDeclaredField("theUnsafe");access.setAccessible(true);var unsafe=access.get(null);var allocate=allocator.getMethod("allocateInstance",Class.class);
|
||||
var entity=(Marker)allocate.invoke(unsafe,Marker.class);var position=Entity.class.getDeclaredField("position");position.setAccessible(true);
|
||||
var emit=VanillaFaceSupportOracle.class.getDeclaredMethod("emit",PrintWriter.class,VoxelShape.class);emit.setAccessible(true);Path dir=Path.of(args[1]);
|
||||
try(var out=new PrintWriter(Files.newBufferedWriter(dir.resolve(args[0]+".txt")));var lines=Files.lines(dir.resolve("inputs.txt"))){
|
||||
for(var line:(Iterable<String>)lines::iterator){
|
||||
var f=JsonParser.parseString(line).getAsJsonObject();var border=new WorldBorder();
|
||||
for(var value:f.getAsJsonArray("ops")){
|
||||
var op=value.getAsJsonArray();switch(op.get(0).getAsString()){
|
||||
case "center"->border.setCenter(number(op.get(1)),number(op.get(2)));
|
||||
case "size"->border.setSize(number(op.get(1)));
|
||||
case "limit"->border.setAbsoluteMaxSize(op.get(1).getAsInt());
|
||||
case "lerp"->border.lerpSizeBetween(number(op.get(1)),number(op.get(2)),op.get(3).getAsLong(),op.get(4).getAsLong());
|
||||
case "tick"->{for(int i=0;i<op.get(1).getAsInt();i++)border.tick();}
|
||||
default->throw new AssertionError(op);
|
||||
}
|
||||
out.println("status "+border.getStatus().name()+" "+border.getLerpTime()+" "+border.getAbsoluteMaxSize());values(out,"state",border.getCenterX(),border.getCenterZ(),border.getSize(),border.getLerpTarget(),border.getLerpSpeed());
|
||||
for(var partial:f.getAsJsonArray("partials")){float t=Float.intBitsToFloat(Integer.parseUnsignedInt(partial.getAsString(),16));values(out,"bounds",border.getMinX(t),border.getMinZ(t),border.getMaxX(t),border.getMaxZ(t));}
|
||||
emit.invoke(null,out,border.getCollisionShape());
|
||||
for(var item:f.getAsJsonArray("queries")){
|
||||
var q=item.getAsJsonArray();double x=number(q.get(0)),z=number(q.get(1)),margin=number(q.get(2));position.set(entity,new Vec3(x,0,z));
|
||||
var b=new AABB(number(q.get(3)),number(q.get(4)),number(q.get(5)),number(q.get(6)),number(q.get(7)),number(q.get(8)));
|
||||
values(out,"distance",border.getDistanceToBorder(x,z));out.println("inside "+border.isWithinBounds(x,z,margin)+" "+border.isInsideCloseToBorder(entity,b));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,86 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Compare world-border extents, tick interpolation and collision geometry with pinned vanilla."""
|
||||
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():
|
||||
def bits(v):return struct.pack('>d',float(v)).hex()
|
||||
partials=[struct.pack('>f',v).hex() for v in [0,-0.0,0.25,0.5,1,-1,2,float('nan'),float('inf')]]
|
||||
def row(ops,center=0,size=10):
|
||||
points=[0,-0.0,-1,1,-5,5,-6,6,center,center-size/2,center+size/2,math.nextafter(center+size/2,-float('inf')),float('nan'),float('inf')]
|
||||
queries=[]
|
||||
for i,x in enumerate(points):
|
||||
z=0 if i%2 else x
|
||||
for margin in [0,1,-0.5]:
|
||||
width=[0,0.6,1,3][i%4]
|
||||
queries.append(list(map(bits,[x,z,margin,0,0,0,width,1.8,width])))
|
||||
return dict(ops=ops,partials=partials,queries=queries)
|
||||
for center in [0,-0.0,0.25,-0.25,29999984,-29999984,1.7e308]:
|
||||
for size in [0,-0.0,1e-8,0.125,1,5.5,59999968,1.7e308]:
|
||||
for limit in [0,1,8,29999984,2147483647]:
|
||||
yield row([['center',bits(center),bits(-center)],['size',bits(size)],['limit',limit]],center,size)
|
||||
for start,target in [(0,10),(10,0),(3.25,17.75),(59999968,0),(0,59999968),(1.7e308,0),(0,1.7e308),(0,-0.0)]:
|
||||
for duration in [0,1,2,7,20,(1<<53)-1,1<<53,(1<<63)-1]:
|
||||
for begin in [0,(1<<63)-1,-(1<<63)]:
|
||||
ops=[['lerp',bits(start),bits(target),duration,begin],['tick',1],['center',bits(0.25),bits(-0.75)],['tick',1],['limit',8],['tick',2],['tick',4],['tick',12]]
|
||||
yield row(ops,0,start)
|
||||
rng=random.Random(0x424f52444552)
|
||||
for index in range(600):
|
||||
center=rng.uniform(-100,100);size=rng.uniform(0,200)
|
||||
ops=[['center',bits(center),bits(rng.uniform(-100,100))],['size',bits(size)]]
|
||||
for _ in range(12):
|
||||
action=rng.randrange(5)
|
||||
if action==0:ops.append(['center',bits(rng.uniform(-100,100)),bits(rng.uniform(-100,100))])
|
||||
elif action==1:ops.append(['size',bits(rng.uniform(0,200))])
|
||||
elif action==2:ops.append(['limit',rng.randrange(0,201)])
|
||||
elif action==3:ops.append(['lerp',bits(rng.uniform(0,200)),bits(rng.uniform(0,200)),rng.randrange(0,30),rng.randrange(-(1<<63),(1<<63))])
|
||||
else:ops.append(['tick',rng.randrange(0,10)])
|
||||
yield row(ops,center,size)
|
||||
|
||||
|
||||
def main():
|
||||
parser=argparse.ArgumentParser(description=__doc__);parser.add_argument('--prepare',action='store_true');args=parser.parse_args()
|
||||
build=lab.LOCAL/'rust-world-border-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/VanillaWorldBorderOracle.java'
|
||||
helper=oracle.with_name('VanillaFaceSupportOracle.java')
|
||||
sources=[oracle,helper,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/world_border_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,helper],'compile-java')
|
||||
def java(side,mode,path):
|
||||
run(['java','-Xmx512M','-cp',os.pathsep.join([str(classes),str(jars[side]),dependency_path]),'dev.horizonlab.parity.VanillaWorldBorderOracle',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','world_border_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'/('world_border_fixture.exe' if os.name=='nt' else 'world_border_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:,} world-border 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 WorldBorder stationary/moving extents, center/absolute-limit updates, tick progression, size/target/time/speed/status, float partial-tick bounds, full collision shape grids and proximity predicates. Covers finite nonnegative sizes, finite centers, nonnegative absolute limits and durations, signed zero, zero and very large durations, wrapping start timestamps, tick completion, retargeting, fractional boundaries and nonfinite predicate/partial-tick inputs. Rust returns typed errors for parameters outside this supported domain. Border saved-data codecs, listeners, dirty-state events, damage, networking and live movement integration remain pending.'}
|
||||
lab.write_json(report_path,report);print(f'Scoped world-border parity passed: {report_path}')
|
||||
|
||||
|
||||
if __name__=='__main__':main()
|
||||
Reference in New Issue
Block a user