From 339d9dde5b5f5fa25bf728ce41dcba2a36c54a3c Mon Sep 17 00:00:00 2001 From: Alexander Medvedev Date: Wed, 8 Jul 2026 17:30:55 +0200 Subject: [PATCH] feat: add bedrock biome mappings --- assets/bedrock/README.md | 3 + assets/bedrock/biomes.json | 197 ++++++++++++++++++ pumpkin-codegen/src/biome.rs | 16 ++ pumpkin-data/src/generated/biome.rs | 67 ++++++ .../src/bedrock/client/level_chunk.rs | 32 ++- pumpkin-world/src/chunk/palette.rs | 72 +++++++ 6 files changed, 383 insertions(+), 4 deletions(-) create mode 100644 assets/bedrock/biomes.json diff --git a/assets/bedrock/README.md b/assets/bedrock/README.md index f1536232a..68b953463 100644 --- a/assets/bedrock/README.md +++ b/assets/bedrock/README.md @@ -16,3 +16,6 @@ This directory contains a number of different data files used to help support co - adapted from `GeyserMC/mappings` `items.json`. - Strips everything except the `bedrock_data` field (making it the value of each corresponding top-level key), omitting any `0` values. - Most of `items.json` is automatically generated, but that value appears to be manually maintained by the Geyser team. We separate that out for our own use, while keeping the rest generated. +- `biomes.json` + - downloaded from [GeyserMC/mappings](https://github.com/GeyserMC/mappings) + - Maps Java Edition biome identifiers to their corresponding Bedrock Edition biome ID. Used in code generation to translate Java biomes to Bedrock counterparts. diff --git a/assets/bedrock/biomes.json b/assets/bedrock/biomes.json new file mode 100644 index 000000000..5377e0e4b --- /dev/null +++ b/assets/bedrock/biomes.json @@ -0,0 +1,197 @@ +{ + "minecraft:badlands": { + "bedrock_id": 37 + }, + "minecraft:bamboo_jungle": { + "bedrock_id": 48 + }, + "minecraft:basalt_deltas": { + "bedrock_id": 181 + }, + "minecraft:beach": { + "bedrock_id": 16 + }, + "minecraft:birch_forest": { + "bedrock_id": 27 + }, + "minecraft:cherry_grove": { + "bedrock_id": 192 + }, + "minecraft:cold_ocean": { + "bedrock_id": 44 + }, + "minecraft:crimson_forest": { + "bedrock_id": 179 + }, + "minecraft:dark_forest": { + "bedrock_id": 29 + }, + "minecraft:deep_cold_ocean": { + "bedrock_id": 45 + }, + "minecraft:deep_dark": { + "bedrock_id": 190 + }, + "minecraft:deep_frozen_ocean": { + "bedrock_id": 47 + }, + "minecraft:deep_lukewarm_ocean": { + "bedrock_id": 43 + }, + "minecraft:deep_ocean": { + "bedrock_id": 24 + }, + "minecraft:desert": { + "bedrock_id": 2 + }, + "minecraft:dripstone_caves": { + "bedrock_id": 188 + }, + "minecraft:end_barrens": { + "bedrock_id": 9 + }, + "minecraft:end_highlands": { + "bedrock_id": 9 + }, + "minecraft:end_midlands": { + "bedrock_id": 9 + }, + "minecraft:eroded_badlands": { + "bedrock_id": 165 + }, + "minecraft:flower_forest": { + "bedrock_id": 132 + }, + "minecraft:forest": { + "bedrock_id": 4 + }, + "minecraft:frozen_ocean": { + "bedrock_id": 46 + }, + "minecraft:frozen_peaks": { + "bedrock_id": 183 + }, + "minecraft:frozen_river": { + "bedrock_id": 11 + }, + "minecraft:grove": { + "bedrock_id": 185 + }, + "minecraft:ice_spikes": { + "bedrock_id": 140 + }, + "minecraft:jagged_peaks": { + "bedrock_id": 182 + }, + "minecraft:jungle": { + "bedrock_id": 21 + }, + "minecraft:lukewarm_ocean": { + "bedrock_id": 42 + }, + "minecraft:lush_caves": { + "bedrock_id": 187 + }, + "minecraft:mangrove_swamp": { + "bedrock_id": 191 + }, + "minecraft:meadow": { + "bedrock_id": 186 + }, + "minecraft:mushroom_fields": { + "bedrock_id": 14 + }, + "minecraft:nether_wastes": { + "bedrock_id": 8 + }, + "minecraft:ocean": { + "bedrock_id": 0 + }, + "minecraft:old_growth_birch_forest": { + "bedrock_id": 155 + }, + "minecraft:old_growth_pine_taiga": { + "bedrock_id": 32 + }, + "minecraft:old_growth_spruce_taiga": { + "bedrock_id": 160 + }, + "minecraft:pale_garden": { + "bedrock_id": 193 + }, + "minecraft:plains": { + "bedrock_id": 1 + }, + "minecraft:river": { + "bedrock_id": 7 + }, + "minecraft:savanna": { + "bedrock_id": 35 + }, + "minecraft:savanna_plateau": { + "bedrock_id": 36 + }, + "minecraft:small_end_islands": { + "bedrock_id": 9 + }, + "minecraft:snowy_beach": { + "bedrock_id": 26 + }, + "minecraft:snowy_plains": { + "bedrock_id": 12 + }, + "minecraft:snowy_slopes": { + "bedrock_id": 184 + }, + "minecraft:snowy_taiga": { + "bedrock_id": 30 + }, + "minecraft:soul_sand_valley": { + "bedrock_id": 178 + }, + "minecraft:sparse_jungle": { + "bedrock_id": 23 + }, + "minecraft:stony_peaks": { + "bedrock_id": 189 + }, + "minecraft:stony_shore": { + "bedrock_id": 25 + }, + "minecraft:sunflower_plains": { + "bedrock_id": 129 + }, + "minecraft:swamp": { + "bedrock_id": 6 + }, + "minecraft:taiga": { + "bedrock_id": 5 + }, + "minecraft:the_end": { + "bedrock_id": 9 + }, + "minecraft:the_void": { + "bedrock_id": 7 + }, + "minecraft:warm_ocean": { + "bedrock_id": 40 + }, + "minecraft:warped_forest": { + "bedrock_id": 180 + }, + "minecraft:windswept_forest": { + "bedrock_id": 34 + }, + "minecraft:windswept_gravelly_hills": { + "bedrock_id": 131 + }, + "minecraft:windswept_hills": { + "bedrock_id": 3 + }, + "minecraft:windswept_savanna": { + "bedrock_id": 163 + }, + "minecraft:wooded_badlands": { + "bedrock_id": 38 + } +} \ No newline at end of file diff --git a/pumpkin-codegen/src/biome.rs b/pumpkin-codegen/src/biome.rs index ca2ecea18..24458ba2c 100644 --- a/pumpkin-codegen/src/biome.rs +++ b/pumpkin-codegen/src/biome.rs @@ -7,6 +7,11 @@ use quote::{format_ident, quote}; use serde::Deserialize; use syn::LitInt; +#[derive(Deserialize)] +struct GeyserBiomeMapping { + bedrock_id: u8, +} + /// Raw deserialization shape for a single biome entry from `biome.json`. #[derive(Deserialize)] pub struct Biome { @@ -214,12 +219,21 @@ pub fn build() -> TokenStream { let biome_trees: MultiNoiseBiomeSuppliers = serde_json::from_str(&fs::read_to_string("../assets/multi_noise_biome_tree.json").unwrap()) .expect("Failed to parse multi_noise_biome_tree.json"); + let bedrock_biomes: BTreeMap = + serde_json::from_str(&fs::read_to_string("../assets/bedrock/biomes.json").unwrap()) + .expect("Failed to parse biomes.json"); let mut variants = TokenStream::new(); let mut name_to_type = TokenStream::new(); let mut id_to_type = TokenStream::new(); for (name, biome) in biomes { + let full_name = format!("minecraft:{name}"); + let be_network_id = bedrock_biomes + .get(&full_name) + .map(|mapping| mapping.bedrock_id) + .unwrap_or(1); + // let full_name = format!("minecraft:{name}"); let format_name = format_ident!("{}", name.to_shouty_snake_case()); let has_precipitation = biome.has_precipitation; @@ -327,6 +341,7 @@ pub fn build() -> TokenStream { pub const #format_name: Biome = Biome { id: #index, registry_id: #name, + be_network_id: #be_network_id, weather: Weather::new( #has_precipitation, #temperature, @@ -361,6 +376,7 @@ pub fn build() -> TokenStream { pub struct Biome { pub id: u8, pub registry_id: &'static str, + pub be_network_id: u8, pub weather: Weather, // carvers: &'static [&str], pub features: &'static [&'static [crate::placed_feature::PlacedFeature]], diff --git a/pumpkin-data/src/generated/biome.rs b/pumpkin-data/src/generated/biome.rs index 5bad1a043..f83a2865f 100644 --- a/pumpkin-data/src/generated/biome.rs +++ b/pumpkin-data/src/generated/biome.rs @@ -13,6 +13,7 @@ use std::{ pub struct Biome { pub id: u8, pub registry_id: &'static str, + pub be_network_id: u8, pub weather: Weather, pub features: &'static [&'static [crate::placed_feature::PlacedFeature]], pub creature_spawn_probability: f32, @@ -85,6 +86,7 @@ impl Biome { pub const BADLANDS: Biome = Biome { id: 0, registry_id: "badlands", + be_network_id: 37u8, weather: Weather::new(false, 2f32, TemperatureModifier::None, 0f32), features: &[ &[], @@ -241,6 +243,7 @@ impl Biome { pub const BAMBOO_JUNGLE: Biome = Biome { id: 1, registry_id: "bamboo_jungle", + be_network_id: 48u8, weather: Weather::new(true, 0.95f32, TemperatureModifier::None, 0.9f32), features: &[ &[], @@ -413,6 +416,7 @@ impl Biome { pub const BASALT_DELTAS: Biome = Biome { id: 2, registry_id: "basalt_deltas", + be_network_id: 181u8, weather: Weather::new(false, 2f32, TemperatureModifier::None, 0f32), features: &[ &[], @@ -475,6 +479,7 @@ impl Biome { pub const BEACH: Biome = Biome { id: 3, registry_id: "beach", + be_network_id: 16u8, weather: Weather::new(true, 0.8f32, TemperatureModifier::None, 0.4f32), features: &[ &[], @@ -606,6 +611,7 @@ impl Biome { pub const BIRCH_FOREST: Biome = Biome { id: 4, registry_id: "birch_forest", + be_network_id: 27u8, weather: Weather::new(true, 0.6f32, TemperatureModifier::None, 0.6f32), features: &[ &[], @@ -758,6 +764,7 @@ impl Biome { pub const CHERRY_GROVE: Biome = Biome { id: 5, registry_id: "cherry_grove", + be_network_id: 192u8, weather: Weather::new(true, 0.5f32, TemperatureModifier::None, 0.8f32), features: &[ &[], @@ -899,6 +906,7 @@ impl Biome { pub const COLD_OCEAN: Biome = Biome { id: 6, registry_id: "cold_ocean", + be_network_id: 44u8, weather: Weather::new(true, 0.5f32, TemperatureModifier::None, 0.5f32), features: &[ &[], @@ -1056,6 +1064,7 @@ impl Biome { pub const CRIMSON_FOREST: Biome = Biome { id: 7, registry_id: "crimson_forest", + be_network_id: 179u8, weather: Weather::new(false, 2f32, TemperatureModifier::None, 0f32), features: &[ &[], @@ -1125,6 +1134,7 @@ impl Biome { pub const DARK_FOREST: Biome = Biome { id: 8, registry_id: "dark_forest", + be_network_id: 29u8, weather: Weather::new(true, 0.7f32, TemperatureModifier::None, 0.8f32), features: &[ &[], @@ -1276,6 +1286,7 @@ impl Biome { pub const DEEP_COLD_OCEAN: Biome = Biome { id: 9, registry_id: "deep_cold_ocean", + be_network_id: 45u8, weather: Weather::new(true, 0.5f32, TemperatureModifier::None, 0.5f32), features: &[ &[], @@ -1433,6 +1444,7 @@ impl Biome { pub const DEEP_DARK: Biome = Biome { id: 10, registry_id: "deep_dark", + be_network_id: 190u8, weather: Weather::new(true, 0.8f32, TemperatureModifier::None, 0.4f32), features: &[ &[], @@ -1508,6 +1520,7 @@ impl Biome { pub const DEEP_FROZEN_OCEAN: Biome = Biome { id: 11, registry_id: "deep_frozen_ocean", + be_network_id: 47u8, weather: Weather::new(true, 0.5f32, TemperatureModifier::Frozen, 0.5f32), features: &[ &[], @@ -1664,6 +1677,7 @@ impl Biome { pub const DEEP_LUKEWARM_OCEAN: Biome = Biome { id: 12, registry_id: "deep_lukewarm_ocean", + be_network_id: 43u8, weather: Weather::new(true, 0.5f32, TemperatureModifier::None, 0.5f32), features: &[ &[], @@ -1831,6 +1845,7 @@ impl Biome { pub const DEEP_OCEAN: Biome = Biome { id: 13, registry_id: "deep_ocean", + be_network_id: 24u8, weather: Weather::new(true, 0.5f32, TemperatureModifier::None, 0.5f32), features: &[ &[], @@ -1986,6 +2001,7 @@ impl Biome { pub const DESERT: Biome = Biome { id: 14, registry_id: "desert", + be_network_id: 2u8, weather: Weather::new(false, 2f32, TemperatureModifier::None, 0f32), features: &[ &[], @@ -2138,6 +2154,7 @@ impl Biome { pub const DRIPSTONE_CAVES: Biome = Biome { id: 15, registry_id: "dripstone_caves", + be_network_id: 188u8, weather: Weather::new(true, 0.8f32, TemperatureModifier::None, 0.4f32), features: &[ &[], @@ -2276,6 +2293,7 @@ impl Biome { pub const END_BARRENS: Biome = Biome { id: 16, registry_id: "end_barrens", + be_network_id: 9u8, weather: Weather::new(false, 0.5f32, TemperatureModifier::None, 0.5f32), features: &[], creature_spawn_probability: 0.1f32, @@ -2298,6 +2316,7 @@ impl Biome { pub const END_HIGHLANDS: Biome = Biome { id: 17, registry_id: "end_highlands", + be_network_id: 9u8, weather: Weather::new(false, 0.5f32, TemperatureModifier::None, 0.5f32), features: &[ &[], @@ -2331,6 +2350,7 @@ impl Biome { pub const END_MIDLANDS: Biome = Biome { id: 18, registry_id: "end_midlands", + be_network_id: 9u8, weather: Weather::new(false, 0.5f32, TemperatureModifier::None, 0.5f32), features: &[], creature_spawn_probability: 0.1f32, @@ -2353,6 +2373,7 @@ impl Biome { pub const ERODED_BADLANDS: Biome = Biome { id: 19, registry_id: "eroded_badlands", + be_network_id: 165u8, weather: Weather::new(false, 2f32, TemperatureModifier::None, 0f32), features: &[ &[], @@ -2509,6 +2530,7 @@ impl Biome { pub const FLOWER_FOREST: Biome = Biome { id: 20, registry_id: "flower_forest", + be_network_id: 132u8, weather: Weather::new(true, 0.7f32, TemperatureModifier::None, 0.8f32), features: &[ &[], @@ -2664,6 +2686,7 @@ impl Biome { pub const FOREST: Biome = Biome { id: 21, registry_id: "forest", + be_network_id: 4u8, weather: Weather::new(true, 0.7f32, TemperatureModifier::None, 0.8f32), features: &[ &[], @@ -2820,6 +2843,7 @@ impl Biome { pub const FROZEN_OCEAN: Biome = Biome { id: 22, registry_id: "frozen_ocean", + be_network_id: 46u8, weather: Weather::new(true, 0f32, TemperatureModifier::Frozen, 0.5f32), features: &[ &[], @@ -2976,6 +3000,7 @@ impl Biome { pub const FROZEN_PEAKS: Biome = Biome { id: 23, registry_id: "frozen_peaks", + be_network_id: 183u8, weather: Weather::new(true, -0.7f32, TemperatureModifier::None, 0.9f32), features: &[ &[], @@ -3100,6 +3125,7 @@ impl Biome { pub const FROZEN_RIVER: Biome = Biome { id: 24, registry_id: "frozen_river", + be_network_id: 11u8, weather: Weather::new(true, 0f32, TemperatureModifier::None, 0.5f32), features: &[ &[], @@ -3242,6 +3268,7 @@ impl Biome { pub const GROVE: Biome = Biome { id: 25, registry_id: "grove", + be_network_id: 185u8, weather: Weather::new(true, -0.2f32, TemperatureModifier::None, 0.8f32), features: &[ &[], @@ -3382,6 +3409,7 @@ impl Biome { pub const ICE_SPIKES: Biome = Biome { id: 26, registry_id: "ice_spikes", + be_network_id: 140u8, weather: Weather::new(true, 0f32, TemperatureModifier::None, 0.5f32), features: &[ &[], @@ -3529,6 +3557,7 @@ impl Biome { pub const JAGGED_PEAKS: Biome = Biome { id: 27, registry_id: "jagged_peaks", + be_network_id: 182u8, weather: Weather::new(true, -0.7f32, TemperatureModifier::None, 0.9f32), features: &[ &[], @@ -3653,6 +3682,7 @@ impl Biome { pub const JUNGLE: Biome = Biome { id: 28, registry_id: "jungle", + be_network_id: 21u8, weather: Weather::new(true, 0.95f32, TemperatureModifier::None, 0.9f32), features: &[ &[], @@ -3825,6 +3855,7 @@ impl Biome { pub const LUKEWARM_OCEAN: Biome = Biome { id: 29, registry_id: "lukewarm_ocean", + be_network_id: 42u8, weather: Weather::new(true, 0.5f32, TemperatureModifier::None, 0.5f32), features: &[ &[], @@ -3992,6 +4023,7 @@ impl Biome { pub const LUSH_CAVES: Biome = Biome { id: 30, registry_id: "lush_caves", + be_network_id: 187u8, weather: Weather::new(true, 0.5f32, TemperatureModifier::None, 0.5f32), features: &[ &[], @@ -4129,6 +4161,7 @@ impl Biome { pub const MANGROVE_SWAMP: Biome = Biome { id: 31, registry_id: "mangrove_swamp", + be_network_id: 191u8, weather: Weather::new(true, 0.8f32, TemperatureModifier::None, 0.9f32), features: &[ &[], @@ -4274,6 +4307,7 @@ impl Biome { pub const MEADOW: Biome = Biome { id: 32, registry_id: "meadow", + be_network_id: 186u8, weather: Weather::new(true, 0.5f32, TemperatureModifier::None, 0.8f32), features: &[ &[], @@ -4416,6 +4450,7 @@ impl Biome { pub const MUSHROOM_FIELDS: Biome = Biome { id: 33, registry_id: "mushroom_fields", + be_network_id: 14u8, weather: Weather::new(true, 0.9f32, TemperatureModifier::None, 1f32), features: &[ &[], @@ -4504,6 +4539,7 @@ impl Biome { pub const NETHER_WASTES: Biome = Biome { id: 34, registry_id: "nether_wastes", + be_network_id: 8u8, weather: Weather::new(false, 2f32, TemperatureModifier::None, 0f32), features: &[ &[], @@ -4583,6 +4619,7 @@ impl Biome { pub const OCEAN: Biome = Biome { id: 35, registry_id: "ocean", + be_network_id: 0u8, weather: Weather::new(true, 0.5f32, TemperatureModifier::None, 0.5f32), features: &[ &[], @@ -4738,6 +4775,7 @@ impl Biome { pub const OLD_GROWTH_BIRCH_FOREST: Biome = Biome { id: 36, registry_id: "old_growth_birch_forest", + be_network_id: 155u8, weather: Weather::new(true, 0.6f32, TemperatureModifier::None, 0.6f32), features: &[ &[], @@ -4890,6 +4928,7 @@ impl Biome { pub const OLD_GROWTH_PINE_TAIGA: Biome = Biome { id: 37, registry_id: "old_growth_pine_taiga", + be_network_id: 32u8, weather: Weather::new(true, 0.3f32, TemperatureModifier::None, 0.8f32), features: &[ &[], @@ -5062,6 +5101,7 @@ impl Biome { pub const OLD_GROWTH_SPRUCE_TAIGA: Biome = Biome { id: 38, registry_id: "old_growth_spruce_taiga", + be_network_id: 160u8, weather: Weather::new(true, 0.25f32, TemperatureModifier::None, 0.8f32), features: &[ &[], @@ -5234,6 +5274,7 @@ impl Biome { pub const PALE_GARDEN: Biome = Biome { id: 39, registry_id: "pale_garden", + be_network_id: 193u8, weather: Weather::new(true, 0.7f32, TemperatureModifier::None, 0.8f32), features: &[ &[], @@ -5362,6 +5403,7 @@ impl Biome { pub const PLAINS: Biome = Biome { id: 40, registry_id: "plains", + be_network_id: 1u8, weather: Weather::new(true, 0.8f32, TemperatureModifier::None, 0.4f32), features: &[ &[], @@ -5528,6 +5570,7 @@ impl Biome { pub const RIVER: Biome = Biome { id: 41, registry_id: "river", + be_network_id: 7u8, weather: Weather::new(true, 0.5f32, TemperatureModifier::None, 0.5f32), features: &[ &[], @@ -5671,6 +5714,7 @@ impl Biome { pub const SAVANNA: Biome = Biome { id: 42, registry_id: "savanna", + be_network_id: 35u8, weather: Weather::new(false, 2f32, TemperatureModifier::None, 0f32), features: &[ &[], @@ -5841,6 +5885,7 @@ impl Biome { pub const SAVANNA_PLATEAU: Biome = Biome { id: 43, registry_id: "savanna_plateau", + be_network_id: 36u8, weather: Weather::new(false, 2f32, TemperatureModifier::None, 0f32), features: &[ &[], @@ -6021,6 +6066,7 @@ impl Biome { pub const SMALL_END_ISLANDS: Biome = Biome { id: 44, registry_id: "small_end_islands", + be_network_id: 9u8, weather: Weather::new(false, 0.5f32, TemperatureModifier::None, 0.5f32), features: &[&[crate::placed_feature::PlacedFeature::EndIslandDecorated]], creature_spawn_probability: 0.1f32, @@ -6043,6 +6089,7 @@ impl Biome { pub const SNOWY_BEACH: Biome = Biome { id: 45, registry_id: "snowy_beach", + be_network_id: 26u8, weather: Weather::new(true, 0.05f32, TemperatureModifier::None, 0.3f32), features: &[ &[], @@ -6170,6 +6217,7 @@ impl Biome { pub const SNOWY_PLAINS: Biome = Biome { id: 46, registry_id: "snowy_plains", + be_network_id: 12u8, weather: Weather::new(true, 0f32, TemperatureModifier::None, 0.5f32), features: &[ &[], @@ -6319,6 +6367,7 @@ impl Biome { pub const SNOWY_SLOPES: Biome = Biome { id: 47, registry_id: "snowy_slopes", + be_network_id: 184u8, weather: Weather::new(true, -0.3f32, TemperatureModifier::None, 0.9f32), features: &[ &[], @@ -6453,6 +6502,7 @@ impl Biome { pub const SNOWY_TAIGA: Biome = Biome { id: 48, registry_id: "snowy_taiga", + be_network_id: 30u8, weather: Weather::new(true, -0.5f32, TemperatureModifier::None, 0.4f32), features: &[ &[], @@ -6619,6 +6669,7 @@ impl Biome { pub const SOUL_SAND_VALLEY: Biome = Biome { id: 49, registry_id: "soul_sand_valley", + be_network_id: 178u8, weather: Weather::new(false, 2f32, TemperatureModifier::None, 0f32), features: &[ &[], @@ -6684,6 +6735,7 @@ impl Biome { pub const SPARSE_JUNGLE: Biome = Biome { id: 50, registry_id: "sparse_jungle", + be_network_id: 23u8, weather: Weather::new(true, 0.95f32, TemperatureModifier::None, 0.8f32), features: &[ &[], @@ -6845,6 +6897,7 @@ impl Biome { pub const STONY_PEAKS: Biome = Biome { id: 51, registry_id: "stony_peaks", + be_network_id: 189u8, weather: Weather::new(true, 1f32, TemperatureModifier::None, 0.3f32), features: &[ &[], @@ -6964,6 +7017,7 @@ impl Biome { pub const STONY_SHORE: Biome = Biome { id: 52, registry_id: "stony_shore", + be_network_id: 25u8, weather: Weather::new(true, 0.2f32, TemperatureModifier::None, 0.3f32), features: &[ &[], @@ -7091,6 +7145,7 @@ impl Biome { pub const SULFUR_CAVES: Biome = Biome { id: 53, registry_id: "sulfur_caves", + be_network_id: 1u8, weather: Weather::new(true, 0.8f32, TemperatureModifier::None, 0.4f32), features: &[ &[], @@ -7218,6 +7273,7 @@ impl Biome { pub const SUNFLOWER_PLAINS: Biome = Biome { id: 54, registry_id: "sunflower_plains", + be_network_id: 129u8, weather: Weather::new(true, 0.8f32, TemperatureModifier::None, 0.4f32), features: &[ &[], @@ -7384,6 +7440,7 @@ impl Biome { pub const SWAMP: Biome = Biome { id: 55, registry_id: "swamp", + be_network_id: 6u8, weather: Weather::new(true, 0.8f32, TemperatureModifier::None, 0.9f32), features: &[ &[], @@ -7554,6 +7611,7 @@ impl Biome { pub const TAIGA: Biome = Biome { id: 56, registry_id: "taiga", + be_network_id: 5u8, weather: Weather::new(true, 0.25f32, TemperatureModifier::None, 0.8f32), features: &[ &[], @@ -7720,6 +7778,7 @@ impl Biome { pub const THE_END: Biome = Biome { id: 57, registry_id: "the_end", + be_network_id: 9u8, weather: Weather::new(false, 0.5f32, TemperatureModifier::None, 0.5f32), features: &[ &[], @@ -7754,6 +7813,7 @@ impl Biome { pub const THE_VOID: Biome = Biome { id: 58, registry_id: "the_void", + be_network_id: 7u8, weather: Weather::new(false, 0.5f32, TemperatureModifier::None, 0.5f32), features: &[ &[], @@ -7784,6 +7844,7 @@ impl Biome { pub const WARM_OCEAN: Biome = Biome { id: 59, registry_id: "warm_ocean", + be_network_id: 40u8, weather: Weather::new(true, 0.5f32, TemperatureModifier::None, 0.5f32), features: &[ &[], @@ -7947,6 +8008,7 @@ impl Biome { pub const WARPED_FOREST: Biome = Biome { id: 60, registry_id: "warped_forest", + be_network_id: 180u8, weather: Weather::new(false, 2f32, TemperatureModifier::None, 0f32), features: &[ &[], @@ -8006,6 +8068,7 @@ impl Biome { pub const WINDSWEPT_FOREST: Biome = Biome { id: 61, registry_id: "windswept_forest", + be_network_id: 34u8, weather: Weather::new(true, 0.2f32, TemperatureModifier::None, 0.3f32), features: &[ &[], @@ -8162,6 +8225,7 @@ impl Biome { pub const WINDSWEPT_GRAVELLY_HILLS: Biome = Biome { id: 62, registry_id: "windswept_gravelly_hills", + be_network_id: 131u8, weather: Weather::new(true, 0.2f32, TemperatureModifier::None, 0.3f32), features: &[ &[], @@ -8318,6 +8382,7 @@ impl Biome { pub const WINDSWEPT_HILLS: Biome = Biome { id: 63, registry_id: "windswept_hills", + be_network_id: 3u8, weather: Weather::new(true, 0.2f32, TemperatureModifier::None, 0.3f32), features: &[ &[], @@ -8474,6 +8539,7 @@ impl Biome { pub const WINDSWEPT_SAVANNA: Biome = Biome { id: 64, registry_id: "windswept_savanna", + be_network_id: 163u8, weather: Weather::new(false, 2f32, TemperatureModifier::None, 0f32), features: &[ &[], @@ -8643,6 +8709,7 @@ impl Biome { pub const WOODED_BADLANDS: Biome = Biome { id: 65, registry_id: "wooded_badlands", + be_network_id: 38u8, weather: Weather::new(false, 2f32, TemperatureModifier::None, 0f32), features: &[ &[], diff --git a/pumpkin-protocol/src/bedrock/client/level_chunk.rs b/pumpkin-protocol/src/bedrock/client/level_chunk.rs index ebaee3913..cfef510f8 100644 --- a/pumpkin-protocol/src/bedrock/client/level_chunk.rs +++ b/pumpkin-protocol/src/bedrock/client/level_chunk.rs @@ -70,15 +70,39 @@ impl PacketWrite for CLevelChunk<'_> { } } - for i in 0..self.chunk.section.count { + let biome_sections = self + .chunk + .section + .biome_sections + .read() + .map_err(|_| Error::other("biome_sections read lock poisoned"))?; + + for (i, biome_palette) in biome_sections.iter().enumerate() { let num_storages = 1; let y = (i as i8) + min_y_section; data_write.write_all(&[VERSION, num_storages, y as u8])?; for _ in 0..num_storages { - 1u8.write(data_write)?; - // TODO - VarInt(0).write(data_write)?; + let network_repr = biome_palette.convert_be_network(); + + (network_repr.bits_per_entry << 1 | 1).write(data_write)?; + + for data in network_repr.packed_data { + data.write(data_write)?; + } + + match network_repr.palette { + NetworkPalette::Single(id) => { + VarInt(i32::from(id)).write(data_write)?; + } + NetworkPalette::Indirect(palette) => { + VarInt(palette.len() as i32).write(data_write)?; + for id in palette { + VarInt(i32::from(id)).write(data_write)?; + } + } + NetworkPalette::Direct => (), + } } } diff --git a/pumpkin-world/src/chunk/palette.rs b/pumpkin-world/src/chunk/palette.rs index c4d86bc82..a2a32947b 100644 --- a/pumpkin-world/src/chunk/palette.rs +++ b/pumpkin-world/src/chunk/palette.rs @@ -493,6 +493,78 @@ impl BiomePalette { } } + #[must_use] + pub fn convert_be_network(&self) -> BeNetworkSerialization { + match self { + Self::Homogeneous(registry_id) => BeNetworkSerialization { + bits_per_entry: 0, + palette: NetworkPalette::Single( + pumpkin_data::biome::Biome::from_id(*registry_id) + .map_or(1, |b| b.be_network_id), + ), + packed_data: Box::new([]), + }, + Self::Heterogeneous(data) => { + let bits_per_entry = encompassing_bits(data.palette.len()); + + let key_to_index_map: HashMap<_, usize> = data + .palette + .iter() + .enumerate() + .map(|(index, key)| (*key, index)) + .collect(); + + let biomes_per_word = 32 / bits_per_entry; + let volume: usize = 4096; + let expected_word_count = volume.div_ceil(biomes_per_word as usize); + let mut packed_data = Vec::with_capacity(expected_word_count); + + let mut current_word: u32 = 0; + let mut current_index_in_word = 0; + + for x in 0..16 { + for y in 0..16 { + for z in 0..16 { + let key = self.get(x / 4, z / 4, y / 4); + let key_index = key_to_index_map.get(&key).unwrap(); + debug_assert!((1 << bits_per_entry) > *key_index); + + current_word |= (*key_index as u32) + << (bits_per_entry as u32 * current_index_in_word); + current_index_in_word += 1; + + if current_index_in_word == biomes_per_word as u32 { + packed_data.push(current_word); + current_word = 0; + current_index_in_word = 0; + } + } + } + } + + // Push any remaining bits if the volume isn't a multiple of biomes_per_word + if current_index_in_word > 0 { + packed_data.push(current_word); + } + + BeNetworkSerialization { + bits_per_entry, + palette: NetworkPalette::Indirect( + data.palette + .iter() + .map(|&id| { + pumpkin_data::biome::Biome::from_id(id) + .map_or(1, |b| b.be_network_id) + }) + .collect::>() + .into_boxed_slice(), + ), + packed_data: packed_data.into_boxed_slice(), + } + } + } + } + #[must_use] pub fn from_disk_nbt(nbt: ChunkSectionBiomes) -> Self { let palette = nbt.palette;