mirror of
https://github.com/horizon-rust/horizon.git
synced 2026-11-07 11:06:11 -10:00
docs/world-height.md, a 2096-high fixture pack, and the tall parity run
tools/fixtures/tall_datapacks/horizon_tall_test is Horizon's own pack (MIT): an Overworld dimension type of min_y -64, height 2096 (To The Max's, the highest top vanilla allows) and noise settings that raise terrain to about y 1900. VanillaChunkGenerationOracle builds each level at its dimension type's range instead of a fixed -64..320, so HORIZON_PARITY_DATAPACKS=tools/fixtures/tall_datapacks tools/test_datapack_worldgen_parity.py compares Horizon's tall generation with vanilla's: 16 chunks over two seeds, terrain and decorated, the same. docs/world-height.md describes the model (one LevelShape per level, from its dimension type, read everywhere), the network path, the checks and what is left; world-generation.md and the README say packs' dimension types now apply. Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
1 parent
148d592567
commit
83b958a824
12 files changed
+200
-5
No files matched your search
@@ -46,7 +46,7 @@ The [documentation](https://playhorizon.dev/docs/) covers each of these.
|
||||
|
||||
- **The game:** singleplayer with vanilla's world generation, mobs, villagers, raids, redstone, every command, the HUD and the recipe book, on saves in vanilla's format. Worlds from 1.8 to 26.2 are upgraded as vanilla does. [Status table](#project-status).
|
||||
- **Horizon's own extras:** new menus (vanilla's are one setting away), graphics presets, distant terrain up to 1024 chunks, controller support, optional zoom, minimap and more, all off by default where they would change the vanilla look. See [Settings](https://playhorizon.dev/docs/settings).
|
||||
- **Resource packs and data packs:** resource packs work, and data packs load functions, predicates, item modifiers and world generation (vanilla's heights and biomes for now).
|
||||
- **Resource packs and data packs:** resource packs work, and data packs load functions, predicates, item modifiers and world generation, taller levels included (vanilla's biomes for now).
|
||||
- **Playing with others:** invite friends from the pause menu's Friends button with a code, Open to LAN, and joining servers, online-mode ones with the Microsoft account the launcher signs in with. See [Multiplayer](https://playhorizon.dev/docs/multiplayer).
|
||||
- **Mods:** Horizon has its own mod system (Rust compiled to WebAssembly). Java mod jars do not run. See [Mods and data packs](https://playhorizon.dev/docs/mods).
|
||||
|
||||
|
||||
@@ -35,6 +35,7 @@ To play, use [the launcher](../launcher/README.md); the project site is [playhor
|
||||
## Architecture: world generation
|
||||
|
||||
- [World generation](world-generation.md): the pipeline from noise to features.
|
||||
- [World height](world-height.md): each level's shape from its dimension type, data packs' taller levels.
|
||||
- [Pregeneration](pregeneration.md): `/pregen`, generating, lighting and saving an area ahead of the players.
|
||||
- [Noise generation](noise-generation.md), [compiled density functions](density-functions.md), [structure density](structure-density.md), [terrain filling](terrain-filling.md).
|
||||
- Biomes: [lookup](biome-lookup.md), [temperatures](biome-temperatures.md).
|
||||
|
||||
@@ -209,8 +209,8 @@ conditions, carvers, configured and placed features, structures, structure sets,
|
||||
higher pack's file for an id replacing a lower one's, and its block, fluid and biome tag files to the tag bundles in pack order.
|
||||
`GeneratorData::load_with` reads the bundles with them; the client caches the result per set of pack entries
|
||||
(`vanillagen::shared_data_with`). What Horizon leaves out is logged: a pack's new biomes (the game has no id, colours or sounds for them;
|
||||
their tag entries and climate points go with them), dimension types (every dimension keeps its vanilla build height, and noise settings
|
||||
taller than it generate only inside it, as `clampToHeightAccessor`), and folders the generator does not read. Sapling growth and bone
|
||||
their tag entries and climate points go with them) and folders the generator does not read. A pack's dimension types set each level's
|
||||
height ([world-height.md](world-height.md)); noise settings taller than the level generate only inside it, as `clampToHeightAccessor`. Sapling growth and bone
|
||||
meal still place vanilla's features.
|
||||
|
||||
`tools/test_datapack_worldgen_parity.py` runs the whole-chunk harness with `HORIZON_DATAPACKS=tools/fixtures/datapacks` on both sides
|
||||
|
||||
@@ -0,0 +1,75 @@
|
||||
# World height: each level's shape from its dimension type
|
||||
|
||||
Vanilla takes each level's build range from its dimension type: `min_y` and `height`, multiples of 16,
|
||||
with `height >= 16` and `min_y + height <= 2032` (`DimensionType.MIN_Y = -2032`, `Y_SIZE = 4064`: `BlockPos`
|
||||
packs Y in 12 bits), plus `logical_height` (how high portals and chorus fruit reach) and `has_skylight`.
|
||||
`LevelHeightAccessor` derives everything else from them. Data packs that reshape the Overworld make it taller:
|
||||
JJThunder's To The Max sets `min_y -64, height 2096`, the largest top vanilla allows, which is 131 sections
|
||||
against vanilla's 24.
|
||||
|
||||
## The model
|
||||
|
||||
**`chunk::LevelShape`** is one level's shape: `min_y`, `height`, `logical_height`, `sky_light`, checked as the
|
||||
dimension type codec checks them (`LevelShape::new`). **`LevelShapes`** holds a world's three.
|
||||
|
||||
- **Where it comes from.** A local world's shapes are its dimension types, its enabled data packs' over
|
||||
vanilla's, through the level stems that name them (`worldgen::overrides::WorldgenOverrides::level_height`,
|
||||
read by `game/world_loading.rs`'s `level_shapes` when the world opens; the game keeps them in
|
||||
`Game.level_shapes`). A remote client takes a level's shape from the server's `minecraft:dimension_type`
|
||||
registry (`protocol::registry::DimensionType`, with its logical height). A type the codec refuses keeps the
|
||||
vanilla shape, with a warning.
|
||||
- **Where it lives.** Each level's `World` holds its `shape`, `SaveDir::new(root, shape)` takes it, and the
|
||||
generator reads the same dimension type for the level's build range (`OverworldGenerator::level`).
|
||||
- **The column.** `LevelShape::column()` is the span of sections the level's chunks hold (`ColumnLayout`):
|
||||
the level's own range, widened to the vanilla Overworld's `-64..320`. So vanilla's three levels keep the
|
||||
24 sections they have always had, and a taller Overworld gets its own column while its Nether and End stay
|
||||
at 24. Every `Chunk` records its own `min_y` and section count, and the hot accessors read the chunk.
|
||||
- **No global.** The shape travels with the data (`World`, `SaveDir`, `Chunk`, the light and mesh jobs, the
|
||||
renderer's `clear_sections(layout)`, the LAN encoder's `set_dimension(shape)`, the remote `ChunkFormat`).
|
||||
Test binaries run tests in parallel in one process, so a process-wide height would leak between them.
|
||||
|
||||
| Subsystem | Reads |
|
||||
| --- | --- |
|
||||
| Chunk storage (`chunk.rs`) | the chunk's own `min_y` and sections |
|
||||
| Light (`light.rs`) | the chunk's sections; the relighter the world's column (`LightWorld::layout`) |
|
||||
| World, streaming, meshing, renderer | `World::layout()`, the chunk's `min_y`, the renderer's layout |
|
||||
| Saves (`save.rs`) | `SaveDir.shape` (sections, `yPos`, heightmaps, light layers, far tiles) |
|
||||
| Network (`lan/`, `game/remote_chunks.rs`) | the level's shape on the host, the server's dimension type on the client |
|
||||
| Section caches, generation glue, far terrain, minimap, flat worlds | the chunk's column, the generator's level range |
|
||||
| Gameplay (bounds, searches, commands, mobs) | `World::bottom_y`/`top_y` (the column), `World.shape`, `Game.level_shapes` |
|
||||
|
||||
Section sets are `SectionMask`s (256 bits: vanilla's tallest level has 254 sections, light data one more
|
||||
below and above). Uniform sections (one block, one biome, one light value) are one shared allocation per kind
|
||||
(`chunk::share_uniform`), and the sky-source scan passes over a uniform section light goes through: a tall
|
||||
column is mostly such air.
|
||||
|
||||
## A world of data packs
|
||||
|
||||
The game, single player and LAN alike, runs through a host: the integrated server thread of single player
|
||||
reads the world's shapes, encodes each level at its shape (`HubConfig.levels`) and sends the data of a
|
||||
dimension type the packs changed even to a client that knows the vanilla pack (`NetConfig.levels`,
|
||||
`SyncData::registry_packets`), and its client takes the heights from that registry data, as a vanilla client
|
||||
of a vanilla server does.
|
||||
|
||||
## Checks
|
||||
|
||||
- Unit tests: a 131-section chunk stores, lights, relights, saves, loads, encodes and random-ticks its whole
|
||||
height; `tests/tall_column.rs` streams, lights and meshes a 131-section level to its top and remeshes after an
|
||||
edit at y 1800.
|
||||
- `HORIZON_PARITY_DATAPACKS=tools/fixtures/tall_datapacks python3 tools/test_datapack_worldgen_parity.py`
|
||||
generates the fixture's 2096-high Overworld (`horizon_tall_test`, Horizon's own MIT pack, terrain from about
|
||||
y 475 to 1900) with the vanilla oracle, which takes each level's range from its dimension type, and with
|
||||
Horizon, and compares every block, biome and block entity.
|
||||
- Vanilla worlds are unchanged at every step: `chunkgen_bench --snapshot`, `test_light_parity.py` and the save
|
||||
and LAN tests give the same results as before.
|
||||
|
||||
## What is left
|
||||
|
||||
- **Nether and End at their own height.** They keep the vanilla column (`-64..320`). Holding exactly their 16
|
||||
sections would save memory and match vanilla's `LevelChunk`, but Horizon's light only spreads inside a
|
||||
chunk's column while vanilla's spreads one section past the level, so it needs that border in the flood fill
|
||||
and the relighter first, checked with `test_light_parity.py`.
|
||||
- The vanilla constants `MIN_Y`, `HEIGHT` and `SECTIONS` remain as the default column and in tests.
|
||||
- Far terrain's detail for a camera far above the world uses vanilla's top (`lod.rs`).
|
||||
- Looking down from far above the terrain shows fog: vanilla's render-distance fog uses `max(xz, |y|)`, so
|
||||
terrain more than the render distance below the camera is fogged in vanilla too.
|
||||
@@ -262,7 +262,8 @@ public final class VanillaChunkGenerationOracle {
|
||||
case "the_end" -> net.minecraft.world.level.dimension.BuiltinDimensionTypes.END;
|
||||
default -> net.minecraft.world.level.dimension.BuiltinDimensionTypes.OVERWORLD;
|
||||
}).value();
|
||||
LevelHeightAccessor height = dimension.equals("overworld") ? LevelHeightAccessor.create(-64, 384) : LevelHeightAccessor.create(0, 256);
|
||||
// The level's build range is its dimension type's: a data pack's when one changes it.
|
||||
LevelHeightAccessor height = LevelHeightAccessor.create(dimensionType.minY(), dimensionType.height());
|
||||
var factory = PalettedContainerFactory.create(access);
|
||||
var unsafeField = Class.forName("sun.misc.Unsafe").getDeclaredField("theUnsafe");
|
||||
unsafeField.setAccessible(true);
|
||||
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"type": "minecraft:max",
|
||||
"left": "minecraft:overworld/final_density",
|
||||
"right": "horizon_tall:peaks"
|
||||
}
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"type": "minecraft:add",
|
||||
"left": {
|
||||
"type": "minecraft:noise",
|
||||
"noise": "horizon_tall:peaks",
|
||||
"xz_scale": 1.0,
|
||||
"y_scale": 0.0
|
||||
},
|
||||
"right": {
|
||||
"type": "minecraft:gradient",
|
||||
"axis": "y",
|
||||
"from_coordinate": 0,
|
||||
"from_value": 1.0,
|
||||
"to_coordinate": 1900,
|
||||
"to_value": -1.0
|
||||
}
|
||||
}
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"base_amplitude": 1.0,
|
||||
"base_octave": -9,
|
||||
"octave_count": 3
|
||||
}
|
||||
+63
@@ -0,0 +1,63 @@
|
||||
{
|
||||
"ambient_light": 0.0,
|
||||
"attributes": {
|
||||
"minecraft:audio/ambient_sounds": {
|
||||
"mood": {
|
||||
"block_search_extent": 8,
|
||||
"offset": 2.0,
|
||||
"sound": "minecraft:ambient.cave",
|
||||
"tick_delay": 6000
|
||||
}
|
||||
},
|
||||
"minecraft:audio/background_music": {
|
||||
"creative": {
|
||||
"max_delay": 24000,
|
||||
"min_delay": 12000,
|
||||
"sound": "minecraft:music.creative"
|
||||
},
|
||||
"default": {
|
||||
"max_delay": 24000,
|
||||
"min_delay": 12000,
|
||||
"sound": "minecraft:music.game"
|
||||
}
|
||||
},
|
||||
"minecraft:gameplay/bed_rule": {
|
||||
"can_set_spawn": "always",
|
||||
"can_sleep": "when_dark",
|
||||
"error_message": {
|
||||
"translate": "block.minecraft.bed.no_sleep"
|
||||
}
|
||||
},
|
||||
"minecraft:gameplay/nether_portal_spawns_piglin": true,
|
||||
"minecraft:gameplay/respawn_anchor_works": false,
|
||||
"minecraft:gameplay/straw_bed_rule": {
|
||||
"can_set_spawn": "never",
|
||||
"can_sleep": "when_dark",
|
||||
"destroy_on_leave": true,
|
||||
"error_message": {
|
||||
"translate": "block.minecraft.bed.no_sleep"
|
||||
}
|
||||
},
|
||||
"minecraft:visual/ambient_light_color": "#0a0a0a",
|
||||
"minecraft:visual/cloud_color": "#ccffffff",
|
||||
"minecraft:visual/cloud_height": 192.33,
|
||||
"minecraft:visual/fog_color": "#c0d8ff",
|
||||
"minecraft:visual/sky_color": "#78a7ff"
|
||||
},
|
||||
"coordinate_scale": 1.0,
|
||||
"default_clock": "minecraft:overworld",
|
||||
"has_ceiling": false,
|
||||
"has_ender_dragon_fight": false,
|
||||
"has_skylight": true,
|
||||
"height": 2096,
|
||||
"infiniburn": "#minecraft:infiniburn_overworld",
|
||||
"logical_height": 384,
|
||||
"min_y": -64,
|
||||
"monster_spawn_block_light_limit": 0,
|
||||
"monster_spawn_light_level": {
|
||||
"type": "minecraft:uniform",
|
||||
"max_inclusive": 7,
|
||||
"min_inclusive": 0
|
||||
},
|
||||
"timelines": "#minecraft:in_overworld"
|
||||
}
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
{
|
||||
"noise": {"min_y": -64, "height": 2096},
|
||||
"default_block": "minecraft:stone",
|
||||
"default_fluid": "minecraft:water",
|
||||
"noise_router": {
|
||||
"chunk_surface_level": "minecraft:overworld/chunk_surface_level",
|
||||
"continents": "minecraft:overworld/continents",
|
||||
"depth": "minecraft:overworld/depth",
|
||||
"erosion": "minecraft:overworld/erosion",
|
||||
"final_density": "horizon_tall:final_density",
|
||||
"ridges": "minecraft:overworld/ridges",
|
||||
"temperature": "minecraft:overworld/temperature",
|
||||
"vegetation": "minecraft:overworld/vegetation"
|
||||
},
|
||||
"material_rule": "minecraft:overworld",
|
||||
"spawn_target": [],
|
||||
"sea_level": 63,
|
||||
"disable_mob_generation": false,
|
||||
"legacy_random_source": false
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"pack": {
|
||||
"description": "Horizon's tall-world fixture: a 2096-high Overworld with peaks past y 1000",
|
||||
"min_format": 121,
|
||||
"max_format": 121
|
||||
}
|
||||
}
|
||||
@@ -26,7 +26,8 @@ except ImportError:
|
||||
import lab
|
||||
import test_chunk_generation_parity as chunks
|
||||
|
||||
# HORIZON_PARITY_DATAPACKS: another folder of packs to compare.
|
||||
# HORIZON_PARITY_DATAPACKS: another folder of packs (tools/fixtures/tall_datapacks: a 2096-high
|
||||
# Overworld).
|
||||
DATAPACKS = Path(os.environ.get('HORIZON_PARITY_DATAPACKS', lab.ROOT / 'tools/fixtures/datapacks'))
|
||||
SEEDS = [12345, 4242]
|
||||
|
||||
|
||||
Reference in new issue
Block a user