From ce652860e0066c869b1e9acc873b9ce5165d826f Mon Sep 17 00:00:00 2001 From: Alexander Medvedev Date: Sun, 4 Jan 2026 20:28:51 +0100 Subject: [PATCH] fix nether --- assets/dimension.json | 186 ++++++++++++++++++ pumpkin-data/build/build.rs | 2 + pumpkin-data/build/dimension.rs | 137 +++++++++++++ pumpkin-data/src/lib.rs | 4 + pumpkin-registry/src/dimension.rs | 55 ------ pumpkin-registry/src/lib.rs | 29 --- pumpkin-world/src/biome/end.rs | 3 +- pumpkin-world/src/biome/mod.rs | 28 +-- pumpkin-world/src/biome/multi_noise.rs | 9 +- pumpkin-world/src/chunk_system.rs | 77 +++++--- pumpkin-world/src/dimension.rs | 45 ++--- pumpkin-world/src/generation/generator/mod.rs | 14 +- pumpkin-world/src/generation/mod.rs | 3 +- pumpkin-world/src/generation/proto_chunk.rs | 38 ++-- pumpkin-world/src/generation/settings.rs | 18 +- pumpkin-world/src/level.rs | 2 +- pumpkin-world/src/lib.rs | 13 +- pumpkin/src/block/blocks/bed.rs | 6 +- pumpkin/src/block/blocks/end_portal.rs | 8 +- pumpkin/src/block/blocks/fire/fire.rs | 7 +- pumpkin/src/block/blocks/fire/mod.rs | 8 +- pumpkin/src/block/blocks/flower_pots.rs | 12 +- pumpkin/src/block/blocks/nether_portal.rs | 8 +- pumpkin/src/block/blocks/plant/flower.rs | 12 +- pumpkin/src/block/fluid/lava.rs | 6 +- pumpkin/src/command/commands/setworldspawn.rs | 22 +-- pumpkin/src/entity/mod.rs | 24 +-- pumpkin/src/entity/player.rs | 18 +- pumpkin/src/item/items/bucket.rs | 6 +- pumpkin/src/server/mod.rs | 51 ++--- pumpkin/src/world/mod.rs | 67 ++----- 31 files changed, 566 insertions(+), 352 deletions(-) create mode 100644 assets/dimension.json create mode 100644 pumpkin-data/build/dimension.rs delete mode 100644 pumpkin-registry/src/dimension.rs diff --git a/assets/dimension.json b/assets/dimension.json new file mode 100644 index 000000000..e7256db39 --- /dev/null +++ b/assets/dimension.json @@ -0,0 +1,186 @@ +{ + "minecraft:overworld": { + "has_skylight": true, + "has_ceiling": false, + "coordinate_scale": 1.0, + "min_y": -64, + "height": 384, + "logical_height": 384, + "infiniburn": "#minecraft:infiniburn_overworld", + "ambient_light": 0.0, + "monster_spawn_light_level": { + "min_inclusive": 0, + "max_inclusive": 7, + "type": "minecraft:uniform" + }, + "monster_spawn_block_light_limit": 0, + "attributes": { + "minecraft:audio/ambient_sounds": { + "mood": { + "sound": "minecraft:ambient.cave", + "tick_delay": 6000, + "block_search_extent": 8, + "offset": 2.0 + } + }, + "minecraft:audio/background_music": { + "default": { + "sound": "minecraft:music.game", + "min_delay": 12000, + "max_delay": 24000 + }, + "creative": { + "sound": "minecraft:music.creative", + "min_delay": 12000, + "max_delay": 24000 + } + }, + "minecraft:gameplay/bed_rule": { + "can_sleep": "when_dark", + "can_set_spawn": "always", + "error_message": { + "translate": "block.minecraft.bed.no_sleep" + } + }, + "minecraft:gameplay/nether_portal_spawns_piglin": true, + "minecraft:gameplay/respawn_anchor_works": false, + "minecraft:visual/cloud_color": "#ccffffff", + "minecraft:visual/cloud_height": 192.33, + "minecraft:visual/fog_color": "#c0d8ff", + "minecraft:visual/sky_color": "#78a7ff" + }, + "timelines": "#minecraft:in_overworld" + }, + "minecraft:overworld_caves": { + "has_skylight": true, + "has_ceiling": true, + "coordinate_scale": 1.0, + "min_y": -64, + "height": 384, + "logical_height": 384, + "infiniburn": "#minecraft:infiniburn_overworld", + "ambient_light": 0.0, + "monster_spawn_light_level": { + "min_inclusive": 0, + "max_inclusive": 7, + "type": "minecraft:uniform" + }, + "monster_spawn_block_light_limit": 0, + "attributes": { + "minecraft:audio/ambient_sounds": { + "mood": { + "sound": "minecraft:ambient.cave", + "tick_delay": 6000, + "block_search_extent": 8, + "offset": 2.0 + } + }, + "minecraft:audio/background_music": { + "default": { + "sound": "minecraft:music.game", + "min_delay": 12000, + "max_delay": 24000 + }, + "creative": { + "sound": "minecraft:music.creative", + "min_delay": 12000, + "max_delay": 24000 + } + }, + "minecraft:gameplay/bed_rule": { + "can_sleep": "when_dark", + "can_set_spawn": "always", + "error_message": { + "translate": "block.minecraft.bed.no_sleep" + } + }, + "minecraft:gameplay/nether_portal_spawns_piglin": true, + "minecraft:gameplay/respawn_anchor_works": false, + "minecraft:visual/cloud_color": "#ccffffff", + "minecraft:visual/cloud_height": 192.33, + "minecraft:visual/fog_color": "#c0d8ff", + "minecraft:visual/sky_color": "#78a7ff" + }, + "timelines": "#minecraft:in_overworld" + }, + "minecraft:the_end": { + "has_fixed_time": true, + "has_skylight": true, + "has_ceiling": false, + "coordinate_scale": 1.0, + "min_y": 0, + "height": 256, + "logical_height": 256, + "infiniburn": "#minecraft:infiniburn_end", + "ambient_light": 0.25, + "monster_spawn_light_level": 15, + "monster_spawn_block_light_limit": 0, + "skybox": "end", + "attributes": { + "minecraft:audio/ambient_sounds": { + "mood": { + "sound": "minecraft:ambient.cave", + "tick_delay": 6000, + "block_search_extent": 8, + "offset": 2.0 + } + }, + "minecraft:audio/background_music": { + "default": { + "sound": "minecraft:music.end", + "min_delay": 6000, + "max_delay": 24000, + "replace_current_music": true + } + }, + "minecraft:gameplay/bed_rule": { + "can_sleep": "never", + "can_set_spawn": "never", + "explodes": true + }, + "minecraft:gameplay/respawn_anchor_works": false, + "minecraft:visual/fog_color": "#181318", + "minecraft:visual/sky_color": "#000000", + "minecraft:visual/sky_light_color": "#e580ff", + "minecraft:visual/sky_light_factor": 0.0 + }, + "timelines": "#minecraft:in_end" + }, + "minecraft:the_nether": { + "has_fixed_time": true, + "has_skylight": false, + "has_ceiling": true, + "coordinate_scale": 8.0, + "min_y": 0, + "height": 256, + "logical_height": 128, + "infiniburn": "#minecraft:infiniburn_nether", + "ambient_light": 0.1, + "monster_spawn_light_level": 7, + "monster_spawn_block_light_limit": 15, + "skybox": "none", + "cardinal_light": "nether", + "attributes": { + "minecraft:gameplay/bed_rule": { + "can_sleep": "never", + "can_set_spawn": "never", + "explodes": true + }, + "minecraft:gameplay/can_start_raid": false, + "minecraft:gameplay/fast_lava": true, + "minecraft:gameplay/piglins_zombify": false, + "minecraft:gameplay/respawn_anchor_works": true, + "minecraft:gameplay/sky_light_level": 4.0, + "minecraft:gameplay/snow_golem_melts": true, + "minecraft:gameplay/water_evaporates": true, + "minecraft:visual/default_dripstone_particle": { + "type": "minecraft:dripping_dripstone_lava" + }, + "minecraft:visual/fog_end_distance": 96.0, + "minecraft:visual/fog_start_distance": 10.0, + "minecraft:visual/sky_light_color": "#7a7aff", + "minecraft:visual/sky_light_factor": 0.0 + }, + "timelines": "#minecraft:in_nether" + } +} \ No newline at end of file diff --git a/pumpkin-data/build/build.rs b/pumpkin-data/build/build.rs index 7b0342de0..04e2cc65f 100644 --- a/pumpkin-data/build/build.rs +++ b/pumpkin-data/build/build.rs @@ -16,6 +16,7 @@ mod chunk_status; mod composter_increase_chance; mod damage_type; mod data_component; +mod dimension; mod effect; mod enchantments; mod entity_pose; @@ -62,6 +63,7 @@ pub fn main() { (chunk_status::build, "chunk_status.rs"), (game_event::build, "game_event.rs"), (game_rules::build, "game_rules.rs"), + (dimension::build, "dimension.rs"), (sound_category::build, "sound_category.rs"), (entity_pose::build, "entity_pose.rs"), (scoreboard_slot::build, "scoreboard_slot.rs"), diff --git a/pumpkin-data/build/dimension.rs b/pumpkin-data/build/dimension.rs new file mode 100644 index 000000000..6a1c778fe --- /dev/null +++ b/pumpkin-data/build/dimension.rs @@ -0,0 +1,137 @@ +use std::{collections::BTreeMap, fs}; + +use heck::ToShoutySnakeCase; +use proc_macro2::TokenStream; +use quote::{format_ident, quote}; +use serde::Deserialize; + +#[derive(Deserialize)] +pub struct Dimension { + pub has_skylight: bool, + pub has_ceiling: bool, + pub ambient_light: f32, + pub coordinate_scale: f64, + pub min_y: i32, + pub height: i32, + pub logical_height: i32, + pub infiniburn: String, + #[serde(rename = "fixed_time")] + pub fixed_time: Option, +} + +// #[derive(Clone, PartialEq, Deserialize)] +// #[serde(untagged)] +// pub enum MonsterSpawnLightLevel { +// Int(i32), +// Tagged(MonsterSpawnLightLevelTagged), +// } + +// #[derive(Clone, PartialEq, Deserialize)] +// pub struct MonsterSpawnLightLevelTagged { +// min_inclusive: i32, +// max_inclusive: i32, +// r#type: String, +// } + +// impl From for MonsterSpawnLightLevel { +// fn from(value: i32) -> Self { +// Self::Int(value) +// } +// } + +pub(crate) fn build() -> TokenStream { + println!("cargo:rerun-if-changed=../assets/dimension.json"); + + let dimensions: BTreeMap = serde_json::from_str( + &fs::read_to_string("../assets/dimension.json").expect("Missing dimension.json"), + ) + .expect("Failed to parse dimension.json"); + + let mut variants = TokenStream::new(); + let mut name_to_type = TokenStream::new(); + + // Iterate with index to generate a unique numeric ID + for (id, (name, dim)) in dimensions.into_iter().enumerate() { + let id = id as u8; // Overworld=0, Nether=1, End=2 (usually) + let format_name = format_ident!( + "{}", + name.strip_prefix("minecraft:") + .unwrap_or(&name) + .to_shouty_snake_case() + ); + + let fixed_time = match dim.fixed_time { + Some(t) => quote! { Some(#t) }, + None => quote! { None }, + }; + + let ambient_light = dim.ambient_light; + let coordinate_scale = dim.coordinate_scale; + let height = dim.height; + let min_y = dim.min_y; + let logical_height = dim.logical_height; + let has_skylight = dim.has_skylight; + let has_ceiling = dim.has_ceiling; + let infiniburn = &dim.infiniburn; + + let minecraft_name = if name.contains(':') { + name.clone() + } else { + format!("minecraft:{}", name) + }; + + variants.extend(quote! { + pub const #format_name: Self = Self { + id: #id, + minecraft_name: #minecraft_name, + fixed_time: #fixed_time, + has_skylight: #has_skylight, + has_ceiling: #has_ceiling, + coordinate_scale: #coordinate_scale, + min_y: #min_y, + height: #height, + logical_height: #logical_height, + infiniburn: #infiniburn, + ambient_light: #ambient_light, + }; + }); + + name_to_type.extend(quote! { + #minecraft_name => Some(&Self::#format_name), + }); + } + + quote! { + #[derive(Debug, Clone, Copy)] + pub struct Dimension { + pub id: u8, + pub minecraft_name: &'static str, + pub fixed_time: Option, + pub has_skylight: bool, + pub has_ceiling: bool, + pub coordinate_scale: f64, + pub min_y: i32, + pub height: i32, + pub logical_height: i32, + pub infiniburn: &'static str, + pub ambient_light: f32, + } + + impl Dimension { + #variants + + pub fn from_name(name: &str) -> Option<&'static Self> { + match name { + #name_to_type + _ => None + } + } + } + impl PartialEq for Dimension { + fn eq(&self, other: &Self) -> bool { + self.id == other.id + } + } + impl Eq for Dimension {} + } +} diff --git a/pumpkin-data/src/lib.rs b/pumpkin-data/src/lib.rs index 10a5a6924..033957df2 100644 --- a/pumpkin-data/src/lib.rs +++ b/pumpkin-data/src/lib.rs @@ -84,6 +84,10 @@ mod entity_type; #[path = "generated/spawn_egg.rs"] mod spawn_egg; +#[rustfmt::skip] +#[path = "generated/dimension.rs"] +pub mod dimension; + #[rustfmt::skip] #[path = "generated/enchantment.rs"] mod enchantment; diff --git a/pumpkin-registry/src/dimension.rs b/pumpkin-registry/src/dimension.rs deleted file mode 100644 index 36d0ac6b9..000000000 --- a/pumpkin-registry/src/dimension.rs +++ /dev/null @@ -1,55 +0,0 @@ -use serde::{Deserialize, Serialize}; - -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct Dimension { - ambient_light: f32, - bed_works: bool, - coordinate_scale: f64, - effects: DimensionEffects, - #[serde(skip_serializing_if = "Option::is_none")] - fixed_time: Option, - has_ceiling: bool, - has_raids: bool, - has_skylight: bool, - height: i32, - infiniburn: String, - logical_height: i32, - min_y: i32, - monster_spawn_block_light_limit: i32, - monster_spawn_light_level: MonsterSpawnLightLevel, - natural: bool, - piglin_safe: bool, - respawn_anchor_works: bool, - ultrawarm: bool, -} - -#[derive(Serialize, Deserialize, Clone, PartialEq, Eq, Default, Debug)] -pub enum DimensionEffects { - #[serde(rename = "minecraft:overworld")] - #[default] - Overworld, - #[serde(rename = "minecraft:the_nether")] - TheNether, - #[serde(rename = "minecraft:the_end")] - TheEnd, -} - -#[derive(Clone, PartialEq, Debug, Serialize, Deserialize)] -#[serde(untagged)] -pub enum MonsterSpawnLightLevel { - Int(i32), - Tagged(MonsterSpawnLightLevelTagged), -} - -#[derive(Clone, PartialEq, Debug, Serialize, Deserialize)] -pub struct MonsterSpawnLightLevelTagged { - min_inclusive: i32, - max_inclusive: i32, - r#type: String, -} - -impl From for MonsterSpawnLightLevel { - fn from(value: i32) -> Self { - Self::Int(value) - } -} diff --git a/pumpkin-registry/src/lib.rs b/pumpkin-registry/src/lib.rs index 27492bada..08de4b6dd 100644 --- a/pumpkin-registry/src/lib.rs +++ b/pumpkin-registry/src/lib.rs @@ -46,35 +46,6 @@ impl SyncedRegistry { } } -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub enum VanillaDimensionType { - Overworld, - OverworldCaves, - TheEnd, - TheNether, -} - -impl VanillaDimensionType { - pub fn resource_location(&self) -> ResourceLocation { - match self { - Self::Overworld => ResourceLocation::vanilla("overworld"), - Self::OverworldCaves => ResourceLocation::vanilla("overworld_caves"), - Self::TheEnd => ResourceLocation::vanilla("the_end"), - Self::TheNether => ResourceLocation::vanilla("the_nether"), - } - } - - pub fn from_resource_location_string(resource_location: &str) -> Option { - match resource_location { - "minecraft:overworld" => Some(Self::Overworld), - "minecraft:overworld_caves" => Some(Self::OverworldCaves), - "minecraft:the_end" => Some(Self::TheEnd), - "minecraft:the_nether" => Some(Self::TheNether), - _ => None, - } - } -} - impl Registry { pub fn get_synced() -> Vec { let mut synced_registries = Vec::new(); diff --git a/pumpkin-world/src/biome/end.rs b/pumpkin-world/src/biome/end.rs index b8e8376f6..cc35fe9a7 100644 --- a/pumpkin-world/src/biome/end.rs +++ b/pumpkin-world/src/biome/end.rs @@ -1,8 +1,7 @@ -use pumpkin_data::chunk::Biome; +use pumpkin_data::{chunk::Biome, dimension::Dimension}; use crate::{ biome::BiomeSupplier, - dimension::Dimension, generation::{ biome_coords, noise::router::multi_noise_sampler::MultiNoiseSampler, section_coords, }, diff --git a/pumpkin-world/src/biome/mod.rs b/pumpkin-world/src/biome/mod.rs index d05d82606..6fcda0da5 100644 --- a/pumpkin-world/src/biome/mod.rs +++ b/pumpkin-world/src/biome/mod.rs @@ -2,11 +2,12 @@ use sha2::{Digest, Sha256}; use std::cell::RefCell; use enum_dispatch::enum_dispatch; -use pumpkin_data::chunk::{Biome, BiomeTree, NETHER_BIOME_SOURCE, OVERWORLD_BIOME_SOURCE}; - -use crate::{ - dimension::Dimension, generation::noise::router::multi_noise_sampler::MultiNoiseSampler, +use pumpkin_data::{ + chunk::{Biome, BiomeTree, NETHER_BIOME_SOURCE, OVERWORLD_BIOME_SOURCE}, + dimension::Dimension, }; + +use crate::generation::noise::router::multi_noise_sampler::MultiNoiseSampler; pub mod end; pub mod multi_noise; @@ -36,10 +37,10 @@ impl BiomeSupplier for MultiNoiseBiomeSupplier { noise: &mut MultiNoiseSampler<'_>, dimension: Dimension, ) -> &'static Biome { - let source: &'static BiomeTree = match dimension { - Dimension::Overworld => &OVERWORLD_BIOME_SOURCE, - Dimension::Nether => &NETHER_BIOME_SOURCE, - Dimension::End => unreachable!(), // Use TheEndBiomeSupplier + let source: &'static BiomeTree = if dimension == Dimension::OVERWORLD { + &OVERWORLD_BIOME_SOURCE + } else { + &NETHER_BIOME_SOURCE }; let point = noise.sample(x, y, z); let point_list = point.convert_to_list(); @@ -56,14 +57,15 @@ pub fn hash_seed(seed: u64) -> i64 { #[cfg(test)] mod test { - use pumpkin_data::{chunk::Biome, noise_router::OVERWORLD_BASE_NOISE_ROUTER}; + use pumpkin_data::{ + chunk::Biome, dimension::Dimension, noise_router::OVERWORLD_BASE_NOISE_ROUTER, + }; use pumpkin_util::read_data_from_file; use serde::Deserialize; use crate::{ GENERATION_SETTINGS, GeneratorSetting, GlobalRandomConfig, ProtoChunk, chunk::palette::BIOME_NETWORK_MAX_BITS, - dimension::Dimension, generation::{ noise::router::{ multi_noise_sampler::{MultiNoiseSampler, MultiNoiseSamplerBuilderOptions}, @@ -84,7 +86,7 @@ mod test { let multi_noise_config = MultiNoiseSamplerBuilderOptions::new(1, 1, 1); let mut sampler = MultiNoiseSampler::generate(&noise_router.multi_noise, &multi_noise_config); - let biome = MultiNoiseBiomeSupplier::biome(-24, 1, 8, &mut sampler, Dimension::Overworld); + let biome = MultiNoiseBiomeSupplier::biome(-24, 1, 8, &mut sampler, Dimension::OVERWORLD); assert_eq!(biome, &Biome::DESERT) } @@ -121,7 +123,7 @@ mod test { let mut chunk = ProtoChunk::new( chunk_x, chunk_z, - surface_settings, + &Dimension::OVERWORLD, default_block, biome_mixer_seed, ); @@ -144,7 +146,7 @@ mod test { let mut multi_noise_sampler = MultiNoiseSampler::generate(&noise_router.multi_noise, &multi_noise_config); - chunk.populate_biomes(Dimension::Overworld, &mut multi_noise_sampler); + chunk.populate_biomes(Dimension::OVERWORLD, &mut multi_noise_sampler); for (biome_x, biome_y, biome_z, biome_id) in data.data { let calculated_biome = chunk.get_biome(biome_x, biome_y, biome_z); diff --git a/pumpkin-world/src/biome/multi_noise.rs b/pumpkin-world/src/biome/multi_noise.rs index 69c9620d4..c80417898 100644 --- a/pumpkin-world/src/biome/multi_noise.rs +++ b/pumpkin-world/src/biome/multi_noise.rs @@ -30,13 +30,14 @@ impl NoiseValuePoint { #[cfg(test)] mod test { - use pumpkin_data::{chunk::Biome, noise_router::OVERWORLD_BASE_NOISE_ROUTER}; + use pumpkin_data::{ + chunk::Biome, dimension::Dimension, noise_router::OVERWORLD_BASE_NOISE_ROUTER, + }; use pumpkin_util::read_data_from_file; use crate::{ GENERATION_SETTINGS, GeneratorSetting, GlobalRandomConfig, ProtoChunk, biome::{BiomeSupplier, MultiNoiseBiomeSupplier}, - dimension::Dimension, generation::{ noise::router::{ multi_noise_sampler::{MultiNoiseSampler, MultiNoiseSamplerBuilderOptions}, @@ -71,7 +72,7 @@ mod test { let _chunk = ProtoChunk::new( chunk_x, chunk_z, - surface_config, + &Dimension::OVERWORLD, surface_config.default_block.get_state(), biome_mixer_seed, ); @@ -121,7 +122,7 @@ mod test { for (x, y, z, biome_id) in expected_data { let calculated_biome = - MultiNoiseBiomeSupplier::biome(x, y, z, &mut sampler, Dimension::Overworld); + MultiNoiseBiomeSupplier::biome(x, y, z, &mut sampler, Dimension::OVERWORLD); assert_eq!( biome_id, diff --git a/pumpkin-world/src/chunk_system.rs b/pumpkin-world/src/chunk_system.rs index db1f8ae11..92a5a5c28 100644 --- a/pumpkin-world/src/chunk_system.rs +++ b/pumpkin-world/src/chunk_system.rs @@ -9,7 +9,7 @@ TODO use crate::block::RawBlockState; use crate::chunk::io::LoadedData::Loaded; use crate::chunk::{ChunkData, ChunkHeightmapType, ChunkLight, ChunkSections, SubChunk}; -use crate::dimension::Dimension; +use pumpkin_data::dimension::Dimension; use std::default::Default; use std::pin::Pin; @@ -43,10 +43,9 @@ use std::sync::{Arc, Condvar, Mutex}; use crate::chunk::format::LightContainer; use crate::chunk::io::LoadedData; -use crate::chunk::palette::{BiomePalette, BlockPalette}; use crate::chunk_system::Chunk::Proto; use crate::chunk_system::StagedChunkEnum::{Biomes, Empty, Features, Full, Noise, Surface}; -use crate::generation::{biome_coords, section_coords}; +use crate::generation::biome_coords; use crossfire::AsyncRx; use pumpkin_data::chunk::ChunkStatus; use rustc_hash::{FxHashMap, FxHashSet}; @@ -775,36 +774,45 @@ impl Chunk { Proto(chunk) => chunk, } } - fn upgrade_to_level_chunk(&mut self, generation_settings: &GenerationSettings) { + fn upgrade_to_level_chunk(&mut self, dimension: &Dimension) { let proto_chunk = self.get_proto_chunk_mut(); - let sub_chunks = generation_settings.shape.height as usize / BlockPalette::SIZE; - let sections = (0..sub_chunks).map(|_| SubChunk::default()).collect(); - let mut sections = ChunkSections::new(sections, generation_settings.shape.min_y as i32); - for y in 0..biome_coords::from_block(generation_settings.shape.height) { - let relative_y = y as usize; - let section_index = relative_y / BiomePalette::SIZE; - let relative_y = relative_y % BiomePalette::SIZE; + let total_sections = dimension.height as usize / 16; + let mut sections = ChunkSections::new( + (0..total_sections).map(|_| SubChunk::default()).collect(), + dimension.min_y, + ); + + let proto_biome_height = biome_coords::from_block(proto_chunk.height()); + let biome_min_y = biome_coords::from_block(dimension.min_y); + + for y_offset in 0..proto_biome_height { + let section_index = y_offset as usize / 4; + let relative_y = y_offset as usize % 4; + if let Some(section) = sections.sections.get_mut(section_index) { - for z in 0..BiomePalette::SIZE { - for x in 0..BiomePalette::SIZE { - let absolute_y = - biome_coords::from_block(generation_settings.shape.min_y as i32) - + y as i32; - let biome = proto_chunk.get_biome(x as i32, absolute_y, z as i32); + let absolute_biome_y = biome_min_y + y_offset as i32; + + for z in 0..4 { + for x in 0..4 { + let biome = proto_chunk.get_biome(x as i32, absolute_biome_y, z as i32); section.biomes.set(x, relative_y, z, biome.id); } } } } - for y in 0..generation_settings.shape.height { - let relative_y = y as usize; - let section_index = section_coords::block_to_section(relative_y); - let relative_y = relative_y % BlockPalette::SIZE; + + let proto_block_height = proto_chunk.height(); + + for y_offset in 0..proto_block_height { + let section_index = (y_offset as usize) / 16; + let relative_y = (y_offset as usize) % 16; + if let Some(section) = sections.sections.get_mut(section_index) { - for z in 0..BlockPalette::SIZE { - for x in 0..BlockPalette::SIZE { - let block = proto_chunk.get_block_state_raw(x as i32, y as i32, z as i32); + for z in 0..16 { + for x in 0..16 { + let block = + proto_chunk.get_block_state_raw(x as i32, y_offset as i32, z as i32); section.block_states.set(x, relative_y, z, block); } } @@ -813,7 +821,15 @@ impl Chunk { let mut chunk = ChunkData { light_engine: ChunkLight { sky_light: (0..sections.sections.len()) - .map(|_| LightContainer::new_filled(15)) + .map(|_| { + if dimension.has_skylight { + // Overworld: Start with full sky light before occlusion + LightContainer::new_filled(15) + } else { + // Nether/End: No sky light permitted + LightContainer::new_empty(0) + } + }) .collect(), block_light: (0..sections.sections.len()) .map(|_| LightContainer::new_empty(0)) @@ -1141,7 +1157,7 @@ impl Cache { Full => { debug_assert_eq!(self.chunks[mid].get_proto_chunk_mut().stage, Features); self.chunks[mid].get_proto_chunk_mut().stage = Full; - self.chunks[mid].upgrade_to_level_chunk(settings); + self.chunks[mid].upgrade_to_level_chunk(&dimension); } StagedChunkEnum::None => {} } @@ -1581,7 +1597,7 @@ impl GenerationSchedule { log::info!("io read thread start"); use crate::biome::hash_seed; let biome_mixer_seed = hash_seed(level.world_gen.random_config.seed); - let generation_setting = gen_settings_from_dimension(&level.world_gen.dimension); + let dimension = &level.world_gen.dimension; let (t_send, mut t_recv) = tokio::sync::mpsc::channel(2); while let Ok(pos) = recv.recv().await { // debug!("io read thread receive chunk pos {pos:?}"); @@ -1610,7 +1626,7 @@ impl GenerationSchedule { let val = RecvChunk::IO(Chunk::Proto(Box::new(ProtoChunk::from_chunk_data( chunk.read().await.deref(), - generation_setting, + dimension, level.world_gen.default_block, biome_mixer_seed, )))); @@ -1631,7 +1647,7 @@ impl GenerationSchedule { RecvChunk::IO(Proto(Box::new(ProtoChunk::new( pos.x, pos.y, - generation_setting, + dimension, level.world_gen.default_block, biome_mixer_seed, )))), @@ -1646,7 +1662,6 @@ impl GenerationSchedule { async fn io_write_work(recv: AsyncRx>, level: Arc, lock: IOLock) { log::info!("io write thread start",); - let generation_setting = gen_settings_from_dimension(&level.world_gen.dimension); while let Ok(data) = recv.recv().await { // debug!("io write thread receive chunks size {}", data.len()); let mut vec = Vec::with_capacity(data.len()); @@ -1655,7 +1670,7 @@ impl GenerationSchedule { Chunk::Level(chunk) => vec.push((pos, chunk)), Proto(chunk) => { let mut temp = Proto(chunk); - temp.upgrade_to_level_chunk(generation_setting); + temp.upgrade_to_level_chunk(&level.world_gen.dimension); let Chunk::Level(chunk) = temp else { panic!() }; vec.push((pos, chunk)); } diff --git a/pumpkin-world/src/dimension.rs b/pumpkin-world/src/dimension.rs index 0562fb4f9..d6c3af4f0 100644 --- a/pumpkin-world/src/dimension.rs +++ b/pumpkin-world/src/dimension.rs @@ -1,31 +1,28 @@ use std::{path::PathBuf, sync::Arc}; use pumpkin_config::world::LevelConfig; -use serde::Deserialize; +use pumpkin_data::dimension::Dimension; use crate::{level::Level, world::BlockRegistryExt}; -#[derive(Clone, Copy, PartialEq, Eq, Hash, Deserialize, Debug)] -#[serde(rename_all = "snake_case")] -pub enum Dimension { - Overworld, - Nether, - End, -} - -impl Dimension { - pub fn into_level( - &self, - level_config: &LevelConfig, - mut base_directory: PathBuf, - block_registry: Arc, - seed: i64, - ) -> Arc { - match self { - Dimension::Overworld => {} - Dimension::Nether => base_directory.push("DIM-1"), - Dimension::End => base_directory.push("DIM1"), - } - Level::from_root_folder(level_config, base_directory, block_registry, seed, *self) - } +pub fn into_level( + dimension: Dimension, + level_config: &LevelConfig, + mut base_directory: PathBuf, + block_registry: Arc, + seed: i64, +) -> Arc { + if dimension == Dimension::OVERWORLD { + } else if dimension == Dimension::THE_NETHER { + base_directory.push("DIM-1") + } else if dimension == Dimension::THE_END { + base_directory.push("DIM1") + }; + Level::from_root_folder( + level_config, + base_directory, + block_registry, + seed, + dimension, + ) } diff --git a/pumpkin-world/src/generation/generator/mod.rs b/pumpkin-world/src/generation/generator/mod.rs index f863a146c..fbc2db733 100644 --- a/pumpkin-world/src/generation/generator/mod.rs +++ b/pumpkin-world/src/generation/generator/mod.rs @@ -1,4 +1,5 @@ use pumpkin_data::BlockState; +use pumpkin_data::dimension::Dimension; use pumpkin_data::noise_router::{ END_BASE_NOISE_ROUTER, NETHER_BASE_NOISE_ROUTER, OVERWORLD_BASE_NOISE_ROUTER, }; @@ -6,7 +7,6 @@ use pumpkin_data::noise_router::{ use super::{ noise::router::proto_noise_router::ProtoNoiseRouters, settings::gen_settings_from_dimension, }; -use crate::dimension::Dimension; use crate::generation::proto_chunk::TerrainCache; use crate::generation::{GlobalRandomConfig, Seed}; @@ -30,10 +30,14 @@ impl GeneratorInit for VanillaGenerator { // TODO: The generation settings contains (part of?) the noise routers too; do we keep the separate or // use only the generation settings? - let base = match dimension { - Dimension::Overworld => OVERWORLD_BASE_NOISE_ROUTER, - Dimension::Nether => NETHER_BASE_NOISE_ROUTER, - Dimension::End => END_BASE_NOISE_ROUTER, + let base = if dimension == Dimension::OVERWORLD { + OVERWORLD_BASE_NOISE_ROUTER + } else if dimension == Dimension::THE_NETHER { + NETHER_BASE_NOISE_ROUTER + } else if dimension == Dimension::THE_END { + END_BASE_NOISE_ROUTER + } else { + unreachable!() }; let terrain_cache = TerrainCache::from_random(&random_config); let generation_settings = gen_settings_from_dimension(&dimension); diff --git a/pumpkin-world/src/generation/mod.rs b/pumpkin-world/src/generation/mod.rs index 980a11678..7a5a38780 100644 --- a/pumpkin-world/src/generation/mod.rs +++ b/pumpkin-world/src/generation/mod.rs @@ -23,6 +23,7 @@ mod surface; pub mod y_offset; use generator::{GeneratorInit, VanillaGenerator}; +use pumpkin_data::dimension::Dimension; use pumpkin_util::{ random::{ RandomDeriver, RandomDeriverImpl, RandomImpl, legacy_rand::LegacyRand, @@ -31,8 +32,6 @@ use pumpkin_util::{ world_seed::Seed, }; -use crate::dimension::Dimension; - pub fn get_world_gen(seed: Seed, dimension: Dimension) -> Box { // TODO decide which WorldGenerator to pick based on config. Box::new(VanillaGenerator::new(seed, dimension)) diff --git a/pumpkin-world/src/generation/proto_chunk.rs b/pumpkin-world/src/generation/proto_chunk.rs index a587fb827..9d1aa9592 100644 --- a/pumpkin-world/src/generation/proto_chunk.rs +++ b/pumpkin-world/src/generation/proto_chunk.rs @@ -1,6 +1,7 @@ use std::collections::HashMap; use std::pin::Pin; +use pumpkin_data::dimension::Dimension; use pumpkin_data::fluid::{Fluid, FluidState}; use pumpkin_data::tag; use pumpkin_data::{ @@ -42,7 +43,6 @@ use crate::{ biome::{BiomeSupplier, MultiNoiseBiomeSupplier, end::TheEndBiomeSupplier}, block::RawBlockState, chunk::CHUNK_AREA, - dimension::Dimension, generation::{biome, positions::chunk_pos}, world::{BlockAccessor, BlockRegistryExt}, }; @@ -126,7 +126,7 @@ pub struct ProtoChunk { biome_mixer_seed: i64, // These are local positions flat_block_map: Box<[BlockStateId]>, - flat_biome_map: Box<[&'static Biome]>, + pub flat_biome_map: Box<[&'static Biome]>, /// HEIGHTMAPS /// /// Top block that is not air @@ -168,12 +168,11 @@ impl ProtoChunk { pub fn new( x: i32, z: i32, - settings: &GenerationSettings, + dimension: &Dimension, default_block: &'static BlockState, biome_mixer_seed: i64, ) -> Self { - let generation_shape = &settings.shape; - let height = generation_shape.height; + let height = dimension.logical_height as u16; let default_heightmap = vec![i16::MIN; CHUNK_AREA].into_boxed_slice(); Self { @@ -195,34 +194,40 @@ impl ProtoChunk { flat_motion_blocking_no_leaves_height_map: default_heightmap, structure_starts: HashMap::new(), height, - bottom_y: generation_shape.min_y, + bottom_y: dimension.min_y as i8, stage: StagedChunkEnum::Empty, } } pub fn from_chunk_data( chunk_data: &ChunkData, - settings: &GenerationSettings, + dimension: &Dimension, default_block: &'static BlockState, biome_mixer_seed: i64, ) -> Self { let mut proto_chunk = ProtoChunk::new( chunk_data.x, chunk_data.z, - settings, + dimension, default_block, biome_mixer_seed, ); for (section_y, section) in chunk_data.section.sections.iter().enumerate() { + // 1. Calculate the base Y for this section + let section_base_y = section_y as i32 * 16; + + if section_base_y >= proto_chunk.height() as i32 { + continue; + } + for x in 0..16 { for y in 0..16 { for z in 0..16 { let block_state_id = section.block_states.get(x, y, z); let block_state = BlockState::from_id(block_state_id); - let absolute_y = - (section_y << 4) as i32 + y as i32 + chunk_data.section.min_y; + let absolute_y = section_base_y + y as i32 + chunk_data.section.min_y; proto_chunk.set_block_state( &Vector3::new(x as i32, absolute_y, z as i32), @@ -231,18 +236,23 @@ impl ProtoChunk { } } } + for x in 0..4 { for y in 0..4 { for z in 0..4 { let biome_id = section.biomes.get(x, y, z); let biome = Biome::from_id(biome_id).unwrap(); - let relative_y_block = (section_y as i32 * 16) + (y as i32 * 4); + let relative_y_block = section_base_y + (y as i32 * 4); + + let biome_y_idx = biome_coords::from_block(relative_y_block); + let index = proto_chunk.local_biome_pos_to_biome_index( x as i32, - biome_coords::from_block(relative_y_block), + biome_y_idx, z as i32, ); + proto_chunk.flat_biome_map[index] = biome; } } @@ -396,7 +406,7 @@ impl ProtoChunk { } #[inline] - fn local_biome_pos_to_biome_index(&self, x: i32, y: i32, z: i32) -> usize { + pub fn local_biome_pos_to_biome_index(&self, x: i32, y: i32, z: i32) -> usize { #[cfg(debug_assertions)] { assert!((0..=3).contains(&x)); @@ -607,7 +617,7 @@ impl ProtoChunk { for x in 0..biomes_per_section { for y in 0..biomes_per_section { for z in 0..biomes_per_section { - let biome = if dimension == Dimension::End { + let biome = if dimension == Dimension::THE_END { TheEndBiomeSupplier::biome( start_biome_x + x, start_biome_y + y, diff --git a/pumpkin-world/src/generation/settings.rs b/pumpkin-world/src/generation/settings.rs index 3b34480cb..f5789b59f 100644 --- a/pumpkin-world/src/generation/settings.rs +++ b/pumpkin-world/src/generation/settings.rs @@ -1,9 +1,10 @@ use std::{collections::HashMap, sync::LazyLock}; +use pumpkin_data::dimension::Dimension; use pumpkin_util::include_json_static; use serde::Deserialize; -use crate::{block::BlockStateCodec, dimension::Dimension}; +use crate::block::BlockStateCodec; use super::{biome_coords::to_block, height_limit::HeightLimitView, surface::rule::MaterialRule}; @@ -13,13 +14,14 @@ pub static GENERATION_SETTINGS: LazyLock &GenerationSettings { - match dimension { - Dimension::Overworld => GENERATION_SETTINGS - .get(&GeneratorSetting::Overworld) - .unwrap(), - Dimension::Nether => GENERATION_SETTINGS.get(&GeneratorSetting::Nether).unwrap(), - Dimension::End => GENERATION_SETTINGS.get(&GeneratorSetting::End).unwrap(), - } + let setting = if dimension == &Dimension::OVERWORLD { + GeneratorSetting::Overworld + } else if dimension == &Dimension::THE_NETHER { + GeneratorSetting::Nether + } else { + GeneratorSetting::End + }; + GENERATION_SETTINGS.get(&setting).unwrap() } #[derive(Deserialize, Hash, PartialEq, Eq)] diff --git a/pumpkin-world/src/level.rs b/pumpkin-world/src/level.rs index b976fcd92..a56c4b805 100644 --- a/pumpkin-world/src/level.rs +++ b/pumpkin-world/src/level.rs @@ -8,7 +8,6 @@ use crate::{ format::{anvil::AnvilChunkFile, linear::LinearFile}, io::{Dirtiable, FileIO, LoadedData, file_manager::ChunkFileManager}, }, - dimension::Dimension, generation::get_world_gen, tick::{OrderedTick, ScheduledTick, TickPriority}, world::BlockRegistryExt, @@ -19,6 +18,7 @@ use log::trace; use num_traits::Zero; use pumpkin_config::{chunk::ChunkConfig, world::LevelConfig}; use pumpkin_data::biome::Biome; +use pumpkin_data::dimension::Dimension; use pumpkin_data::{Block, block_properties::has_random_ticks, fluid::Fluid}; use pumpkin_util::math::{position::BlockPos, vector2::Vector2}; use pumpkin_util::world_seed::Seed; diff --git a/pumpkin-world/src/lib.rs b/pumpkin-world/src/lib.rs index 7bcf3eb7d..a55be2566 100644 --- a/pumpkin-world/src/lib.rs +++ b/pumpkin-world/src/lib.rs @@ -1,6 +1,5 @@ -use dimension::Dimension; use generation::settings::GenerationSettings; -use pumpkin_data::BlockState; +use pumpkin_data::{BlockState, dimension::Dimension}; use pumpkin_util::math::vector2::Vector2; pub mod biome; @@ -67,7 +66,7 @@ pub fn bench_create_and_populate_noise( }; let biome_mixer_seed = hash_seed(random_config.seed); - let mut chunk = ProtoChunk::new(0, 0, settings, default_block, biome_mixer_seed); + let mut chunk = ProtoChunk::new(0, 0, &Dimension::OVERWORLD, default_block, biome_mixer_seed); // Create noise sampler and other required components let generation_shape = &settings.shape; @@ -128,7 +127,7 @@ pub fn bench_create_and_populate_biome( use crate::generation::{biome_coords, positions::chunk_pos}; let biome_mixer_seed = hash_seed(random_config.seed); - let mut chunk = ProtoChunk::new(0, 0, settings, default_block, biome_mixer_seed); + let mut chunk = ProtoChunk::new(0, 0, &Dimension::OVERWORLD, default_block, biome_mixer_seed); // Create multi-noise sampler let generation_shape = &settings.shape; @@ -150,7 +149,7 @@ pub fn bench_create_and_populate_biome( let mut multi_noise_sampler = MultiNoiseSampler::generate(&base_router.multi_noise, &multi_noise_config); - chunk.populate_biomes(Dimension::Overworld, &mut multi_noise_sampler); + chunk.populate_biomes(Dimension::OVERWORLD, &mut multi_noise_sampler); } pub fn bench_create_and_populate_noise_with_surface( @@ -176,7 +175,7 @@ pub fn bench_create_and_populate_noise_with_surface( }; let biome_mixer_seed = hash_seed(random_config.seed); - let mut chunk = ProtoChunk::new(0, 0, settings, default_block, biome_mixer_seed); + let mut chunk = ProtoChunk::new(0, 0, &Dimension::OVERWORLD, default_block, biome_mixer_seed); // Create all required components let generation_shape = &settings.shape; @@ -230,7 +229,7 @@ pub fn bench_create_and_populate_noise_with_surface( let mut surface_height_estimate_sampler = SurfaceHeightEstimateSampler::generate(&base_router.surface_estimator, &surface_config); - chunk.populate_biomes(Dimension::Overworld, &mut multi_noise_sampler); + chunk.populate_biomes(Dimension::OVERWORLD, &mut multi_noise_sampler); chunk.populate_noise(&mut noise_sampler, &mut surface_height_estimate_sampler); chunk.build_surface( settings, diff --git a/pumpkin/src/block/blocks/bed.rs b/pumpkin/src/block/blocks/bed.rs index ca181c643..d0f51eb28 100644 --- a/pumpkin/src/block/blocks/bed.rs +++ b/pumpkin/src/block/blocks/bed.rs @@ -3,10 +3,10 @@ use std::sync::Arc; use pumpkin_data::Block; use pumpkin_data::block_properties::BedPart; use pumpkin_data::block_properties::BlockProperties; +use pumpkin_data::dimension::Dimension; use pumpkin_data::entity::EntityType; use pumpkin_data::tag::{RegistryKey, get_tag_values}; use pumpkin_macros::pumpkin_block_from_tag; -use pumpkin_registry::VanillaDimensionType; use pumpkin_util::GameMode; use pumpkin_util::math::position::BlockPos; use pumpkin_util::text::TextComponent; @@ -163,7 +163,7 @@ impl BlockBehaviour for BedBlock { }; // Explode if not in the overworld - if args.world.dimension_type != VanillaDimensionType::Overworld { + if args.world.dimension != Dimension::OVERWORLD { args.world .break_block(&bed_head_pos, None, BlockFlags::SKIP_DROPS) .await; @@ -235,7 +235,7 @@ impl BlockBehaviour for BedBlock { if args .player .set_respawn_point( - args.world.dimension_type, + args.world.dimension, bed_head_pos, args.player.get_entity().yaw.load(), ) diff --git a/pumpkin/src/block/blocks/end_portal.rs b/pumpkin/src/block/blocks/end_portal.rs index 82b08b5e2..e8a0b5f3f 100644 --- a/pumpkin/src/block/blocks/end_portal.rs +++ b/pumpkin/src/block/blocks/end_portal.rs @@ -4,8 +4,8 @@ use crate::block::BlockBehaviour; use crate::block::BlockFuture; use crate::block::OnEntityCollisionArgs; use crate::block::PlacedArgs; +use pumpkin_data::dimension::Dimension; use pumpkin_macros::pumpkin_block; -use pumpkin_registry::VanillaDimensionType; use pumpkin_world::block::entities::end_portal::EndPortalBlockEntity; #[pumpkin_block("minecraft:end_portal")] @@ -14,13 +14,13 @@ pub struct EndPortalBlock; impl BlockBehaviour for EndPortalBlock { fn on_entity_collision<'a>(&'a self, args: OnEntityCollisionArgs<'a>) -> BlockFuture<'a, ()> { Box::pin(async move { - let world = if args.world.dimension_type == VanillaDimensionType::TheEnd { + let world = if args.world.dimension == Dimension::THE_END { args.server - .get_world_from_dimension(VanillaDimensionType::Overworld) + .get_world_from_dimension(&Dimension::OVERWORLD) .await } else { args.server - .get_world_from_dimension(VanillaDimensionType::TheEnd) + .get_world_from_dimension(&Dimension::THE_END) .await }; args.entity diff --git a/pumpkin/src/block/blocks/fire/fire.rs b/pumpkin/src/block/blocks/fire/fire.rs index 3280a2e3d..13e7c32d4 100644 --- a/pumpkin/src/block/blocks/fire/fire.rs +++ b/pumpkin/src/block/blocks/fire/fire.rs @@ -1,7 +1,7 @@ use pumpkin_data::block_properties::{BlockProperties, EnumVariants, HorizontalAxis}; +use pumpkin_data::dimension::Dimension; use pumpkin_data::entity::EntityType; use pumpkin_data::fluid::Fluid; -use pumpkin_registry::VanillaDimensionType; use pumpkin_util::math::vector3::Vector3; use pumpkin_world::world::{BlockAccessor, BlockFlags}; use rand::Rng; @@ -172,10 +172,9 @@ impl BlockBehaviour for FireBlock { return; } - let dimension = args.world.dimension_type; + let dimension = args.world.dimension; // First lets check if we are in OverWorld or Nether, its not possible to place an Nether portal in other dimensions in Vanilla - if (dimension == VanillaDimensionType::Overworld - || dimension == VanillaDimensionType::TheNether) + if (dimension == Dimension::OVERWORLD || dimension == Dimension::THE_NETHER) && let Some(portal) = NetherPortal::get_new_portal(args.world, args.position, HorizontalAxis::X).await { diff --git a/pumpkin/src/block/blocks/fire/mod.rs b/pumpkin/src/block/blocks/fire/mod.rs index 2f534d474..6e19f72b3 100644 --- a/pumpkin/src/block/blocks/fire/mod.rs +++ b/pumpkin/src/block/blocks/fire/mod.rs @@ -1,9 +1,9 @@ use std::sync::Arc; +use pumpkin_data::dimension::Dimension; use pumpkin_data::tag::Taggable; use pumpkin_data::world::WorldEvent; use pumpkin_data::{Block, BlockDirection, tag}; -use pumpkin_registry::VanillaDimensionType; use pumpkin_util::math::position::BlockPos; use pumpkin_util::random::RandomGenerator; use pumpkin_util::random::xoroshiro128::Xoroshiro; @@ -84,10 +84,8 @@ impl FireBlockBase { block_pos: &BlockPos, direction: BlockDirection, ) -> bool { - let dimension = world.dimension_type; - if dimension != VanillaDimensionType::Overworld - && dimension != VanillaDimensionType::TheNether - { + let dimension = world.dimension; + if dimension != Dimension::OVERWORLD && dimension != Dimension::THE_NETHER { return false; } let mut found = false; diff --git a/pumpkin/src/block/blocks/flower_pots.rs b/pumpkin/src/block/blocks/flower_pots.rs index 6b999c7f2..b481f3f8d 100644 --- a/pumpkin/src/block/blocks/flower_pots.rs +++ b/pumpkin/src/block/blocks/flower_pots.rs @@ -1,10 +1,10 @@ use crate::block::registry::BlockActionResult; use crate::block::{BlockBehaviour, BlockFuture, RandomTickArgs, UseWithItemArgs}; use pumpkin_data::Block; +use pumpkin_data::dimension::Dimension; use pumpkin_data::flower_pot_transformations::get_potted_item; use pumpkin_data::tag::{RegistryKey, get_tag_values}; use pumpkin_macros::pumpkin_block_from_tag; -use pumpkin_registry::VanillaDimensionType; use pumpkin_world::world::BlockFlags; #[pumpkin_block_from_tag("minecraft:flower_pots")] @@ -49,14 +49,8 @@ impl BlockBehaviour for FlowerPotBlock { fn random_tick<'a>(&'a self, args: RandomTickArgs<'a>) -> BlockFuture<'a, ()> { Box::pin(async move { - if (args - .world - .dimension_type - .eq(&VanillaDimensionType::Overworld) - || args - .world - .dimension_type - .eq(&VanillaDimensionType::OverworldCaves)) + if (args.world.dimension.eq(&Dimension::OVERWORLD) + || args.world.dimension.eq(&Dimension::OVERWORLD_CAVES)) && args.block.eq(&Block::POTTED_CLOSED_EYEBLOSSOM) && args.world.level_time.lock().await.time_of_day % 24000 > 14500 { diff --git a/pumpkin/src/block/blocks/nether_portal.rs b/pumpkin/src/block/blocks/nether_portal.rs index 9f2fc76af..df2363abe 100644 --- a/pumpkin/src/block/blocks/nether_portal.rs +++ b/pumpkin/src/block/blocks/nether_portal.rs @@ -8,9 +8,9 @@ use crate::world::World; use crate::world::portal::nether::NetherPortal; use pumpkin_data::Block; use pumpkin_data::block_properties::{Axis, BlockProperties, NetherPortalLikeProperties}; +use pumpkin_data::dimension::Dimension; use pumpkin_data::entity::EntityType; use pumpkin_macros::pumpkin_block; -use pumpkin_registry::VanillaDimensionType; use pumpkin_util::GameMode; use pumpkin_world::BlockStateId; @@ -62,13 +62,13 @@ impl BlockBehaviour for NetherPortalBlock { fn on_entity_collision<'a>(&'a self, args: OnEntityCollisionArgs<'a>) -> BlockFuture<'a, ()> { Box::pin(async move { - let target_world = if args.world.dimension_type == VanillaDimensionType::TheNether { + let target_world = if args.world.dimension == Dimension::THE_NETHER { args.server - .get_world_from_dimension(VanillaDimensionType::Overworld) + .get_world_from_dimension(&Dimension::OVERWORLD) .await } else { args.server - .get_world_from_dimension(VanillaDimensionType::TheNether) + .get_world_from_dimension(&Dimension::THE_NETHER) .await }; diff --git a/pumpkin/src/block/blocks/plant/flower.rs b/pumpkin/src/block/blocks/plant/flower.rs index 9802178df..384b86ae1 100644 --- a/pumpkin/src/block/blocks/plant/flower.rs +++ b/pumpkin/src/block/blocks/plant/flower.rs @@ -1,6 +1,6 @@ use pumpkin_data::Block; +use pumpkin_data::dimension::Dimension; use pumpkin_data::tag::{RegistryKey, get_tag_values}; -use pumpkin_registry::VanillaDimensionType; use pumpkin_world::BlockStateId; use pumpkin_world::world::BlockFlags; @@ -48,14 +48,8 @@ impl BlockBehaviour for FlowerBlock { fn random_tick<'a>(&'a self, args: RandomTickArgs<'a>) -> BlockFuture<'a, ()> { Box::pin(async move { //TODO add trail particule - if (args - .world - .dimension_type - .eq(&VanillaDimensionType::Overworld) - || args - .world - .dimension_type - .eq(&VanillaDimensionType::OverworldCaves)) + if (args.world.dimension.eq(&Dimension::OVERWORLD) + || args.world.dimension.eq(&Dimension::OVERWORLD_CAVES)) && args.block.eq(&Block::CLOSED_EYEBLOSSOM) && args.world.level_time.lock().await.time_of_day % 24000 > 14500 { diff --git a/pumpkin/src/block/fluid/lava.rs b/pumpkin/src/block/fluid/lava.rs index 1eea05e87..e66c64014 100644 --- a/pumpkin/src/block/fluid/lava.rs +++ b/pumpkin/src/block/fluid/lava.rs @@ -2,11 +2,11 @@ use std::sync::Arc; use pumpkin_data::{ Block, BlockDirection, + dimension::Dimension, fluid::{Falling, Fluid, FluidProperties, Level}, world::WorldEvent, }; use pumpkin_macros::pumpkin_block; -use pumpkin_registry::VanillaDimensionType; use pumpkin_util::math::position::BlockPos; use pumpkin_world::{BlockStateId, tick::TickPriority, world::BlockFlags}; @@ -141,7 +141,7 @@ impl FluidBehaviour for FlowingLava { impl FlowingFluid for FlowingLava { fn get_level_decrease_per_block(&self, world: &World) -> i32 { // ultrawarm logic - if world.dimension_type == VanillaDimensionType::TheNether { + if world.dimension == Dimension::THE_NETHER { 1 } else { 2 @@ -150,7 +150,7 @@ impl FlowingFluid for FlowingLava { fn get_max_flow_distance(&self, world: &World) -> i32 { // ultrawarm logic - if world.dimension_type == VanillaDimensionType::TheNether { + if world.dimension == Dimension::THE_NETHER { 4 } else { 2 diff --git a/pumpkin/src/command/commands/setworldspawn.rs b/pumpkin/src/command/commands/setworldspawn.rs index 71ba7c765..7805fa549 100644 --- a/pumpkin/src/command/commands/setworldspawn.rs +++ b/pumpkin/src/command/commands/setworldspawn.rs @@ -10,7 +10,7 @@ use crate::command::{ tree::{CommandTree, builder::argument}, }; use crate::server::Server; -use pumpkin_registry::VanillaDimensionType; +use pumpkin_data::dimension::Dimension; use pumpkin_util::{math::position::BlockPos, text::TextComponent}; const NAMES: [&str; 1] = ["setworldspawn"]; @@ -106,18 +106,14 @@ async fn setworldspawn( "Failed to get world.", ))); }; - - match world.dimension_type { - VanillaDimensionType::Overworld | VanillaDimensionType::OverworldCaves => {} - _ => { - sender - .send_message(TextComponent::translate( - "commands.setworldspawn.failure.not_overworld", - [], - )) - .await; - return Ok(()); - } + if world.dimension != Dimension::OVERWORLD && world.dimension != Dimension::OVERWORLD_CAVES { + sender + .send_message(TextComponent::translate( + "commands.setworldspawn.failure.not_overworld", + [], + )) + .await; + return Ok(()); } let mut level_info_guard = server.level_info.write().await; diff --git a/pumpkin/src/entity/mod.rs b/pumpkin/src/entity/mod.rs index 1e44528f0..a0155ef5b 100644 --- a/pumpkin/src/entity/mod.rs +++ b/pumpkin/src/entity/mod.rs @@ -7,6 +7,7 @@ use living::LivingEntity; use player::Player; use pumpkin_data::BlockState; use pumpkin_data::block_properties::{EnumVariants, Integer0To15}; +use pumpkin_data::dimension::Dimension; use pumpkin_data::fluid::Fluid; use pumpkin_data::{Block, BlockDirection}; use pumpkin_data::{ @@ -25,7 +26,6 @@ use pumpkin_protocol::{ }, ser::serializer::Serializer, }; -use pumpkin_registry::VanillaDimensionType; use pumpkin_util::math::vector3::Axis; use pumpkin_util::math::{ boundingbox::{BoundingBox, EntityDimensions}, @@ -993,7 +993,7 @@ impl Entity { .await; } - let lava_speed = if self.world.dimension_type == VanillaDimensionType::TheNether { + let lava_speed = if self.world.dimension == Dimension::THE_NETHER { 0.007 } else { 0.002_333_333 @@ -1305,21 +1305,9 @@ impl Entity { self.portal_cooldown .store(self.default_portal_cooldown(), Ordering::Relaxed); let pos = self.pos.load(); - // TODO: this is bad - let scale_factor_new = if portal_manager.portal_world.dimension_type - == VanillaDimensionType::TheNether - { - 8.0 - } else { - 1.0 - }; - // TODO: this is bad - let scale_factor_current = - if self.world.dimension_type == VanillaDimensionType::TheNether { - 8.0 - } else { - 1.0 - }; + let scale_factor_new = portal_manager.portal_world.dimension.coordinate_scale; + let scale_factor_current = self.world.dimension.coordinate_scale; + let scale_factor = scale_factor_current / scale_factor_new; // TODO let pos = BlockPos::floored(pos.x * scale_factor, pos.y, pos.z * scale_factor); @@ -1762,7 +1750,7 @@ impl Entity { } pub async fn check_out_of_world(&self, dyn_self: &dyn EntityBase) { - if self.pos.load().y < f64::from(self.world.generation_settings().shape.min_y) - 64.0 { + if self.pos.load().y < f64::from(self.world.dimension.min_y) - 64.0 { // Tick out of world damage dyn_self .damage(dyn_self, 4.0, DamageType::OUT_OF_WORLD) diff --git a/pumpkin/src/entity/player.rs b/pumpkin/src/entity/player.rs index 56c8e888a..0f7965721 100644 --- a/pumpkin/src/entity/player.rs +++ b/pumpkin/src/entity/player.rs @@ -10,6 +10,7 @@ use std::time::{Duration, Instant}; use crossbeam::atomic::AtomicCell; use crossbeam::channel::Receiver; use log::warn; +use pumpkin_data::dimension::Dimension; use pumpkin_inventory::player::ender_chest_inventory::EnderChestInventory; use pumpkin_protocol::bedrock::client::level_chunk::CLevelChunk; use pumpkin_protocol::bedrock::client::set_time::CSetTime; @@ -56,7 +57,6 @@ use pumpkin_protocol::java::client::play::{ CUpdateTime, GameEvent, MetaDataType, Metadata, PlayerAction, PlayerInfoFlags, PreviousMessage, }; use pumpkin_protocol::java::server::play::SClickSlot; -use pumpkin_registry::VanillaDimensionType; use pumpkin_util::math::{ boundingbox::BoundingBox, experience, position::BlockPos, vector2::Vector2, vector3::Vector3, }; @@ -661,7 +661,7 @@ impl Player { pub async fn set_respawn_point( &self, - dimension: VanillaDimensionType, + dimension: Dimension, block_pos: BlockPos, yaw: f32, ) -> bool { @@ -690,12 +690,12 @@ impl Player { let block = self.world().get_block(&respawn_point.position).await; - if respawn_point.dimension == VanillaDimensionType::Overworld + if respawn_point.dimension == Dimension::OVERWORLD && block.has_tag(&tag::Block::MINECRAFT_BEDS) { // TODO: calculate respawn position Some((respawn_point.position.to_f64(), respawn_point.yaw)) - } else if respawn_point.dimension == VanillaDimensionType::TheNether + } else if respawn_point.dimension == Dimension::THE_NETHER && block == &Block::RESPAWN_ANCHOR { // TODO: calculate respawn position @@ -1292,7 +1292,7 @@ impl Player { self.chunk_manager.lock().await.change_world(¤t_world.level, &new_world.level); let last_pos = self.living_entity.entity.last_pos.load(); - let death_dimension = self.world().dimension_type.resource_location(); + let death_dimension = ResourceLocation::from(self.world().dimension.minecraft_name); let death_location = BlockPos(Vector3::new( last_pos.x.round() as i32, last_pos.y.round() as i32, @@ -1300,8 +1300,8 @@ impl Player { )); self.client .send_packet_now(&CRespawn::new( - (new_world.dimension_type as u8).into(), - new_world.dimension_type.resource_location(), + (new_world.dimension.id).into(), + ResourceLocation::from(new_world.dimension.minecraft_name), biome::hash_seed(new_world.level.seed.0), // seed self.gamemode.load() as u8, self.gamemode.load() as i8, @@ -2117,7 +2117,7 @@ impl NBTStorage for Player { nbt.put_string( "Dimension", - self.world().dimension_type.resource_location().to_string(), + ResourceLocation::from(self.world().dimension.minecraft_name).to_string(), ); }) } @@ -2531,7 +2531,7 @@ impl Abilities { /// Represents the player's respawn point. #[derive(Copy, Debug, Clone, PartialEq)] pub struct RespawnPoint { - pub dimension: VanillaDimensionType, + pub dimension: Dimension, pub position: BlockPos, pub yaw: f32, pub force: bool, diff --git a/pumpkin/src/item/items/bucket.rs b/pumpkin/src/item/items/bucket.rs index c39a77500..ef9c3e685 100644 --- a/pumpkin/src/item/items/bucket.rs +++ b/pumpkin/src/item/items/bucket.rs @@ -6,11 +6,11 @@ use crate::{ }; use pumpkin_data::{ Block, + dimension::Dimension, fluid::Fluid, item::Item, sound::{Sound, SoundCategory}, }; -use pumpkin_registry::VanillaDimensionType; use pumpkin_util::{ GameMode, math::{position::BlockPos, vector3::Vector3}, @@ -236,9 +236,7 @@ impl ItemBehaviour for FilledBucketItem { return; }; - if item.id != Item::LAVA_BUCKET.id - && world.dimension_type == VanillaDimensionType::TheNether - { + if item.id != Item::LAVA_BUCKET.id && world.dimension == Dimension::THE_NETHER { world .play_sound_raw( Sound::BlockFireExtinguish as u16, diff --git a/pumpkin/src/server/mod.rs b/pumpkin/src/server/mod.rs index cf61226e9..2600587d3 100644 --- a/pumpkin/src/server/mod.rs +++ b/pumpkin/src/server/mod.rs @@ -13,17 +13,18 @@ use crate::{command::dispatcher::CommandDispatcher, entity::player::Player, worl use connection_cache::{CachedBranding, CachedStatus}; use key_store::KeyStore; use pumpkin_config::{AdvancedConfiguration, BasicConfiguration}; +use pumpkin_data::dimension::Dimension; +use pumpkin_world::dimension::into_level; use crate::command::CommandSender; use pumpkin_macros::send_cancellable; use pumpkin_protocol::java::client::login::CEncryptionRequest; use pumpkin_protocol::java::client::play::CChangeDifficulty; use pumpkin_protocol::{ClientPacket, java::client::config::CPluginMessage}; -use pumpkin_registry::{Registry, VanillaDimensionType}; +use pumpkin_registry::Registry; use pumpkin_util::Difficulty; use pumpkin_util::math::vector3::Vector3; use pumpkin_util::text::TextComponent; -use pumpkin_world::dimension::Dimension; use pumpkin_world::lock::LevelLocker; use pumpkin_world::lock::anvil::AnvilLevelLocker; use pumpkin_world::world_info::anvil::{ @@ -72,7 +73,7 @@ pub struct Server { /// Manages multiple worlds within the server. pub worlds: RwLock>>, /// All the dimensions that exist on the server. - pub dimensions: Vec, + pub dimensions: Vec, /// Caches game registries for efficient access. pub cached_registry: Vec, /// Assigns unique IDs to containers. @@ -182,10 +183,9 @@ impl Server { container_id: 0.into(), worlds: RwLock::new(vec![]), dimensions: vec![ - VanillaDimensionType::Overworld, - VanillaDimensionType::OverworldCaves, - VanillaDimensionType::TheNether, - VanillaDimensionType::TheEnd, + Dimension::OVERWORLD, + Dimension::THE_NETHER, + Dimension::THE_END, ], command_dispatcher, block_registry: block_registry.clone(), @@ -215,35 +215,43 @@ impl Server { log::info!("Loading Overworld: {seed}"); let overworld = World::load( - Dimension::Overworld.into_level( + into_level( + Dimension::OVERWORLD, level_config, world_path.clone(), block_registry.clone(), seed, ), level_info.clone(), - VanillaDimensionType::Overworld, + Dimension::OVERWORLD, block_registry.clone(), weak.clone(), ); log::info!("Loading Nether: {seed}"); let nether = World::load( - Dimension::Nether.into_level( + into_level( + Dimension::THE_NETHER, level_config, world_path.clone(), block_registry.clone(), seed, ), level_info.clone(), - VanillaDimensionType::TheNether, + Dimension::THE_NETHER, block_registry.clone(), weak.clone(), ); log::info!("Loading End: {seed}"); let end = World::load( - Dimension::End.into_level(level_config, world_path, block_registry.clone(), seed), + into_level( + Dimension::THE_END, + level_config, + world_path, + block_registry.clone(), + seed, + ), level_info, - VanillaDimensionType::TheEnd, + Dimension::THE_END, block_registry, weak, ); @@ -266,14 +274,15 @@ impl Server { self.tasks.spawn(task) } - pub async fn get_world_from_dimension(&self, dimension: VanillaDimensionType) -> Arc { + pub async fn get_world_from_dimension(&self, dimension: &Dimension) -> Arc { // TODO: this is really bad let world_guard = self.worlds.read().await; - match dimension { - VanillaDimensionType::Overworld => world_guard.first(), - VanillaDimensionType::OverworldCaves => todo!(), - VanillaDimensionType::TheEnd => world_guard.get(2), - VanillaDimensionType::TheNether => world_guard.get(1), + if dimension == &Dimension::OVERWORLD { + world_guard.first() + } else if dimension == &Dimension::THE_NETHER { + world_guard.get(1) + } else { + world_guard.get(2) } .cloned() .unwrap() @@ -315,9 +324,7 @@ impl Server { let (world, nbt) = if let Ok(Some(data)) = self.player_data_storage.load_data(&profile.id) { if let Some(dimension_key) = data.get_string("Dimension") { - if let Some(dimension) = - VanillaDimensionType::from_resource_location_string(dimension_key) - { + if let Some(dimension) = Dimension::from_name(dimension_key) { let world = self.get_world_from_dimension(dimension).await; (world, Some(data)) } else { diff --git a/pumpkin/src/world/mod.rs b/pumpkin/src/world/mod.rs index 7dadb5cc0..ef018039f 100644 --- a/pumpkin/src/world/mod.rs +++ b/pumpkin/src/world/mod.rs @@ -39,6 +39,7 @@ use crossbeam::queue::SegQueue; use explosion::Explosion; use pumpkin_config::BasicConfiguration; use pumpkin_data::data_component_impl::EquipmentSlot; +use pumpkin_data::dimension::Dimension; use pumpkin_data::entity::MobCategory; use pumpkin_data::fluid::{Falling, FluidProperties, FluidState}; use pumpkin_data::{ @@ -94,7 +95,6 @@ use pumpkin_protocol::{ CWorldEvent, }, }; -use pumpkin_registry::VanillaDimensionType; use pumpkin_util::resource_location::ResourceLocation; use pumpkin_util::text::{TextComponent, color::NamedColor}; use pumpkin_util::{ @@ -108,9 +108,8 @@ use pumpkin_util::{ use pumpkin_world::chunk::palette::BlockPalette; use pumpkin_world::world::{GetBlockError, WorldFuture}; use pumpkin_world::{ - BlockStateId, CURRENT_BEDROCK_MC_VERSION, GENERATION_SETTINGS, GeneratorSetting, biome, - block::entities::BlockEntity, chunk::io::Dirtiable, inventory::Inventory, item::ItemStack, - world::SimpleWorld, + BlockStateId, CURRENT_BEDROCK_MC_VERSION, biome, block::entities::BlockEntity, + chunk::io::Dirtiable, inventory::Inventory, item::ItemStack, world::SimpleWorld, }; use pumpkin_world::{chunk::ChunkData, world::BlockAccessor}; use pumpkin_world::{level::Level, tick::TickPriority}; @@ -132,7 +131,7 @@ pub mod weather; use crate::world::natural_spawner::{SpawnState, spawn_for_chunk}; use pumpkin_data::effect::StatusEffect; use pumpkin_world::chunk::ChunkHeightmapType::MotionBlocking; -use pumpkin_world::generation::settings::GenerationSettings; +use pumpkin_world::generation::settings::gen_settings_from_dimension; use uuid::Uuid; use weather::Weather; @@ -177,7 +176,7 @@ pub struct World { /// The world's time, including counting ticks for weather, time cycles, and statistics. pub level_time: Mutex, /// The type of dimension the world is in. - pub dimension_type: VanillaDimensionType, + pub dimension: Dimension, pub sea_level: i32, pub min_y: i32, /// The world's weather, including rain and thunder levels. @@ -197,33 +196,21 @@ impl World { pub fn load( level: Arc, level_info: Arc>, - dimension_type: VanillaDimensionType, + dimension: Dimension, block_registry: Arc, server: Weak, ) -> Self { // TODO - let generation_settings = match dimension_type { - VanillaDimensionType::Overworld => GENERATION_SETTINGS - .get(&GeneratorSetting::Overworld) - .unwrap(), - VanillaDimensionType::OverworldCaves => todo!(), - VanillaDimensionType::TheEnd => { - GENERATION_SETTINGS.get(&GeneratorSetting::End).unwrap() - } - VanillaDimensionType::TheNether => { - GENERATION_SETTINGS.get(&GeneratorSetting::Nether).unwrap() - } - }; - + let generation_settings = gen_settings_from_dimension(&dimension); Self { level, level_info, players: Arc::new(RwLock::new(HashMap::new())), entities: Arc::new(RwLock::new(HashMap::new())), scoreboard: Mutex::new(Scoreboard::default()), - worldborder: Mutex::new(Worldborder::new(0.0, 0.0, 30_000_000.0, 0, 0, 0)), + worldborder: Mutex::new(Worldborder::new(0.0, 0.0, 5.999_996_8E7, 0, 5, 300)), level_time: Mutex::new(LevelTime::new()), - dimension_type, + dimension, weather: Mutex::new(Weather::new()), block_registry, sea_level: generation_settings.sea_level, @@ -1154,29 +1141,9 @@ impl World { spawn_for_chunk(self, chunk_pos, chunk, spawn_state, spawn_list).await; } - pub fn generation_settings(&self) -> &GenerationSettings { - // TODO: this is bad - match self.dimension_type { - VanillaDimensionType::Overworld => GENERATION_SETTINGS - .get(&GeneratorSetting::Overworld) - .unwrap(), - VanillaDimensionType::OverworldCaves => todo!(), - VanillaDimensionType::TheEnd => { - GENERATION_SETTINGS.get(&GeneratorSetting::End).unwrap() - } - VanillaDimensionType::TheNether => { - GENERATION_SETTINGS.get(&GeneratorSetting::Nether).unwrap() - } - } - } - /// Gets the y position of the first non air block from the top down pub async fn get_top_block(&self, position: Vector2) -> i32 { - let generation_settings = self.generation_settings(); - for y in (i32::from(generation_settings.shape.min_y) - ..i32::from(generation_settings.shape.max_y())) - .rev() - { + for y in (self.dimension.min_y..self.dimension.height).rev() { let pos = BlockPos::new(position.x, y, position.y); let block = self.get_block_state(&pos).await; if block.is_air() { @@ -1184,7 +1151,7 @@ impl World { } return y; } - i32::from(generation_settings.shape.min_y) + self.dimension.min_y } #[expect(clippy::too_many_lines)] @@ -1379,7 +1346,7 @@ impl World { let dimensions: Vec = server .dimensions .iter() - .map(VanillaDimensionType::resource_location) + .map(|d| ResourceLocation::from(d.minecraft_name)) .collect(); // This code follows the vanilla packet order @@ -1404,8 +1371,8 @@ impl World { false, true, false, - (self.dimension_type as u8).into(), - self.dimension_type.resource_location(), + (self.dimension.id).into(), + ResourceLocation::from(self.dimension.minecraft_name), biome::hash_seed(self.level.seed.0), // seed gamemode as u8, player @@ -1805,7 +1772,7 @@ impl World { pub async fn respawn_player(&self, player: &Arc, alive: bool) { let last_pos = player.living_entity.entity.last_pos.load(); - let death_dimension = player.world().dimension_type.resource_location(); + let death_dimension = ResourceLocation::from(player.world().dimension.minecraft_name); let death_location = BlockPos(Vector3::new( last_pos.x.round() as i32, last_pos.y.round() as i32, @@ -1819,8 +1786,8 @@ impl World { player .client .enqueue_packet(&CRespawn::new( - (self.dimension_type as u8).into(), - self.dimension_type.resource_location(), + (self.dimension.id).into(), + ResourceLocation::from(self.dimension.minecraft_name), biome::hash_seed(self.level.seed.0), // seed player.gamemode.load() as u8, player.gamemode.load() as i8,