fix nether

This commit is contained in:
Alexander Medvedev 2026-01-04 20:28:51 +01:00
parent 01d3be963d
commit ce652860e0
31 changed files with 566 additions and 352 deletions

186
assets/dimension.json Normal file
View File

@ -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"
}
}

View File

@ -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"),

View File

@ -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<i64>,
}
// #[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<i32> 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<String, Dimension> = 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<i64>,
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 {}
}
}

View File

@ -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;

View File

@ -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<i64>,
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<i32> for MonsterSpawnLightLevel {
fn from(value: i32) -> Self {
Self::Int(value)
}
}

View File

@ -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<Self> {
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<Self> {
let mut synced_registries = Vec::new();

View File

@ -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,
},

View File

@ -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);

View File

@ -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,

View File

@ -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<Vec<(ChunkPos, Chunk)>>, level: Arc<Level>, 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));
}

View File

@ -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<dyn BlockRegistryExt>,
seed: i64,
) -> Arc<Level> {
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<dyn BlockRegistryExt>,
seed: i64,
) -> Arc<Level> {
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,
)
}

View File

@ -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);

View File

@ -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<VanillaGenerator> {
// TODO decide which WorldGenerator to pick based on config.
Box::new(VanillaGenerator::new(seed, dimension))

View File

@ -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,

View File

@ -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<HashMap<GeneratorSetting, GenerationSet
);
pub fn gen_settings_from_dimension(dimension: &Dimension) -> &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)]

View File

@ -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;

View File

@ -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,

View File

@ -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(),
)

View File

@ -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

View File

@ -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
{

View File

@ -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;

View File

@ -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
{

View File

@ -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
};

View File

@ -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
{

View File

@ -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

View File

@ -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;

View File

@ -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)

View File

@ -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(&current_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,

View File

@ -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,

View File

@ -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<Vec<Arc<World>>>,
/// All the dimensions that exist on the server.
pub dimensions: Vec<VanillaDimensionType>,
pub dimensions: Vec<Dimension>,
/// Caches game registries for efficient access.
pub cached_registry: Vec<Registry>,
/// 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<World> {
pub async fn get_world_from_dimension(&self, dimension: &Dimension) -> Arc<World> {
// 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 {

View File

@ -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<LevelTime>,
/// 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>,
level_info: Arc<RwLock<LevelData>>,
dimension_type: VanillaDimensionType,
dimension: Dimension,
block_registry: Arc<BlockRegistry>,
server: Weak<Server>,
) -> 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>) -> 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<ResourceLocation> = 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<Player>, 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,