Skip to content

Commit 0cf0de8

Browse files
committed
Basic world gen
1 parent 68067ae commit 0cf0de8

File tree

12 files changed

+252
-19
lines changed

12 files changed

+252
-19
lines changed

Cargo.toml

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ members = [
3333
"src/lib/utils/general_purpose",
3434
"src/lib/utils/logging",
3535
"src/lib/utils/profiling",
36-
"src/lib/world",
36+
"src/lib/world", "src/lib/world_gen",
3737
]
3838

3939
#================== Lints ==================#
@@ -81,7 +81,7 @@ ferrumc-anvil = { path = "src/lib/adapters/anvil" }
8181
ferrumc-config = { path = "src/lib/utils/config" }
8282
ferrumc-core = { path = "src/lib/core" }
8383
ferrumc-ecs = { path = "src/lib/ecs" }
84-
ferrumc-events = { path = "src/lib/events"}
84+
ferrumc-events = { path = "src/lib/events" }
8585
ferrumc-general-purpose = { path = "src/lib/utils/general_purpose" }
8686
ferrumc-logging = { path = "src/lib/utils/logging" }
8787
ferrumc-macros = { path = "src/lib/derive_macros" }
@@ -96,6 +96,7 @@ ferrumc-storage = { path = "src/lib/storage" }
9696
ferrumc-text = { path = "src/lib/text" }
9797
ferrumc-utils = { path = "src/lib/utils" }
9898
ferrumc-world = { path = "src/lib/world" }
99+
ferrumc-world-gen = { path = "src/lib/world_gen" }
99100

100101

101102

@@ -156,7 +157,7 @@ maplit = "1.0.2"
156157
macro_rules_attribute = "0.2.0"
157158

158159
# Magic ("life-before-main" initialization, __attribute__((constructor)))
159-
ctor = "0.2.9"
160+
ctor = "0.4.0"
160161

161162
# Compression/Decompression
162163
libflate = "2.1.0"
@@ -174,12 +175,13 @@ moka = "0.12.8"
174175
# CLI
175176
clap = "4.5.20"
176177
indicatif = "0.17.8"
177-
colored = "2.1.0"
178+
colored = "3.0.0"
178179

179180
# Misc
180181
deepsize = "0.2.0"
181182
page_size = "0.6.0"
182183
regex = "1.11.1"
184+
noise = "0.9.0"
183185

184186
# I/O
185187
memmap2 = "0.9.5"

src/bin/Cargo.toml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,9 @@ ferrumc-macros = { workspace = true }
2626
ferrumc-nbt = { workspace = true }
2727
ferrumc-general-purpose = { workspace = true }
2828
ferrumc-state = { workspace = true }
29+
ferrumc-world-gen = { workspace = true }
2930

30-
parking_lot = { workspace = true, features = ["deadlock_detection"] }
31+
rayon = { workspace = true }
3132
tracing = { workspace = true }
3233
tokio = { workspace = true }
3334
futures = { workspace = true }
@@ -36,6 +37,7 @@ clap = { workspace = true, features = ["derive"] }
3637
flate2 = { workspace = true }
3738
ctor = { workspace = true }
3839
rand = { workspace = true }
40+
log = "0.4.22"
3941

4042

4143
[[bin]]

src/bin/src/main.rs

Lines changed: 39 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
#![feature(portable_simd)]
2-
#![forbid(unsafe_code)]
32
#![feature(random)]
43
extern crate core;
54

@@ -11,7 +10,11 @@ use ferrumc_ecs::Universe;
1110
use ferrumc_general_purpose::paths::get_root_path;
1211
use ferrumc_net::server::create_server_listener;
1312
use ferrumc_state::ServerState;
13+
use ferrumc_world::chunk_format::Chunk;
1414
use ferrumc_world::World;
15+
use ferrumc_world_gen::errors::WorldGenError;
16+
use ferrumc_world_gen::WorldGenerator;
17+
use rayon::prelude::*;
1518
use std::sync::Arc;
1619
use systems::definition;
1720
use tokio::runtime::Handle;
@@ -23,8 +26,6 @@ mod cli;
2326
mod packet_handlers;
2427
mod systems;
2528

26-
pub type Result<T> = std::result::Result<T, BinaryError>;
27-
2829
// #[tokio::main(flavor = "current_thread")]
2930
#[tokio::main(flavor = "multi_thread")]
3031
async fn main() {
@@ -64,10 +65,41 @@ async fn main() {
6465
}
6566
}
6667

67-
async fn entry() -> Result<()> {
68+
async fn entry() -> Result<(), BinaryError> {
6869
let state = create_state().await?;
6970
let global_state = Arc::new(state);
7071
create_whitelist().await;
72+
if !global_state.world.chunk_exists(0, 0, "overworld").await? {
73+
info!("No overworld spawn chunk found, generating spawn chunks...");
74+
// Generate a 12x12 chunk area around the spawn point
75+
let mut chunks = Vec::new();
76+
for x in -12..12 {
77+
for z in -12..12 {
78+
chunks.push((x, z));
79+
}
80+
}
81+
let generated_chunks: Vec<Result<Chunk, WorldGenError>> = chunks
82+
.chunks(72)
83+
.par_bridge()
84+
.map(|chunk_coord_arr| {
85+
let mut generated_chunks = Vec::new();
86+
for (x, z) in chunk_coord_arr {
87+
let state = global_state.clone();
88+
generated_chunks.push(state.terrain_generator.generate_chunk(*x, *z));
89+
}
90+
generated_chunks
91+
})
92+
.flatten()
93+
.collect();
94+
for chunk in generated_chunks {
95+
let chunk = chunk.map_err(|e| {
96+
error!("Error generating chunk: {:?}", e);
97+
BinaryError::Custom("Error generating chunk".to_string())
98+
})?;
99+
global_state.world.save_chunk(chunk).await?;
100+
}
101+
info!("Finished generating spawn chunks...");
102+
}
71103

72104
let all_system_handles = tokio::spawn(definition::start_all_systems(global_state.clone()));
73105

@@ -80,7 +112,7 @@ async fn entry() -> Result<()> {
80112
Ok(())
81113
}
82114

83-
async fn handle_import(import_args: ImportArgs) -> Result<()> {
115+
async fn handle_import(import_args: ImportArgs) -> Result<(), BinaryError> {
84116
//! Handles the import of the world.
85117
info!("Importing world...");
86118

@@ -107,12 +139,13 @@ async fn handle_import(import_args: ImportArgs) -> Result<()> {
107139
Ok(())
108140
}
109141

110-
async fn create_state() -> Result<ServerState> {
142+
async fn create_state() -> Result<ServerState, BinaryError> {
111143
let listener = create_server_listener().await?;
112144

113145
Ok(ServerState {
114146
universe: Universe::new(),
115147
tcp_listener: listener,
116148
world: World::new().await,
149+
terrain_generator: WorldGenerator::new(0),
117150
})
118151
}

src/bin/src/systems/tcp_listener_system.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1+
use crate::errors::BinaryError;
12
use crate::systems::definition::System;
2-
use crate::Result;
33
use async_trait::async_trait;
44
use ferrumc_net::connection::handle_connection;
55
use ferrumc_state::GlobalState;
@@ -26,7 +26,7 @@ impl System for TcpListenerSystem {
2626
}
2727

2828
impl TcpListenerSystem {
29-
async fn initiate_loop(state: GlobalState) -> Result<()> {
29+
async fn initiate_loop(state: GlobalState) -> Result<(), BinaryError> {
3030
let tcp_listener = &state.tcp_listener;
3131
info!("Server is listening on [{}]", tcp_listener.local_addr()?);
3232

src/lib/core/state/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,3 +7,4 @@ edition = "2021"
77
tokio = { workspace = true }
88
ferrumc-ecs = { workspace = true }
99
ferrumc-world = { workspace = true }
10+
ferrumc-world-gen = { workspace = true }

src/lib/core/state/src/lib.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,14 @@
11
use ferrumc_ecs::Universe;
22
use ferrumc_world::World;
3+
use ferrumc_world_gen::WorldGenerator;
34
use std::sync::Arc;
45
use tokio::net::TcpListener;
6+
57
pub struct ServerState {
68
pub universe: Universe,
79
pub tcp_listener: TcpListener,
810
pub world: World,
11+
pub terrain_generator: WorldGenerator,
912
}
1013

1114
pub type GlobalState = Arc<ServerState>;

src/lib/world/src/chunk_format.rs

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ use std::cmp::max;
1212
use std::collections::hash_map::Entry;
1313
use std::collections::HashMap;
1414
use std::io::Read;
15-
use tracing::{debug, error, warn};
15+
use tracing::{error, warn};
1616
use vanilla_chunk_format::BlockData;
1717

1818
#[cfg(test)]
@@ -384,7 +384,7 @@ impl Chunk {
384384
// Get old block
385385
let old_block = self.get_block(x, y, z)?;
386386
if old_block == block {
387-
debug!("Block is the same as the old block");
387+
// debug!("Block is the same as the old block");
388388
return Ok(());
389389
}
390390
// Get section
@@ -533,7 +533,6 @@ impl Chunk {
533533
.ok_or(WorldError::SectionOutOfBounds(y >> 4))?;
534534
match &section.block_states.block_data {
535535
PaletteType::Single(val) => {
536-
debug!(x, y, z, "Single palette type");
537536
let block_id = val.val;
538537
ID2BLOCK
539538
.get(&block_id)
@@ -616,11 +615,15 @@ impl Chunk {
616615
///
617616
/// * `Ok(())` - If the section was successfully set.
618617
/// * `Err(WorldError)` - If an error occurs while setting the section.
619-
pub fn set_section(&mut self, section: u8, block: BlockData) -> Result<(), WorldError> {
620-
if let Some(section) = self.sections.get_mut(section as usize) {
618+
pub fn set_section(&mut self, section_y: i8, block: BlockData) -> Result<(), WorldError> {
619+
if let Some(section) = self
620+
.sections
621+
.iter_mut()
622+
.find(|section| section.y == section_y)
623+
{
621624
section.fill(block.clone())
622625
} else {
623-
Err(WorldError::SectionOutOfBounds(section as i32))
626+
Err(WorldError::SectionOutOfBounds(section_y as i32))
624627
}
625628
}
626629

src/lib/world_gen/Cargo.toml

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
[package]
2+
name = "ferrumc-world-gen"
3+
version = "0.1.0"
4+
edition = "2024"
5+
6+
[dependencies]
7+
ferrumc-world = { workspace = true }
8+
thiserror = { workspace = true }
9+
noise = { workspace = true }
10+
11+
[lints]
12+
workspace = true

src/lib/world_gen/src/biomes/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
pub(crate) mod plains;
Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
use crate::errors::WorldGenError;
2+
use crate::{BiomeGenerator, NoiseGenerator};
3+
use ferrumc_world::chunk_format::Chunk;
4+
use ferrumc_world::vanilla_chunk_format::BlockData;
5+
use std::collections::BTreeMap;
6+
7+
pub(crate) struct PlainsBiome;
8+
9+
impl BiomeGenerator for PlainsBiome {
10+
fn _biome_id(&self) -> u8 {
11+
0
12+
}
13+
14+
fn _biome_name(&self) -> String {
15+
"plains".to_string()
16+
}
17+
18+
fn generate_chunk(
19+
&self,
20+
x: i32,
21+
z: i32,
22+
noise: &NoiseGenerator,
23+
) -> Result<Chunk, WorldGenError> {
24+
let mut chunk = Chunk::new(x, z, "overworld".to_string());
25+
let mut heights = vec![];
26+
let stone = BlockData {
27+
name: "minecraft:stone".to_string(),
28+
properties: None,
29+
};
30+
31+
// Fill with water first
32+
for section_y in -4..4 {
33+
chunk.set_section(
34+
section_y as i8,
35+
BlockData {
36+
name: "minecraft:water".to_string(),
37+
properties: Some(BTreeMap::from([("level".to_string(), "0".to_string())])),
38+
},
39+
)?;
40+
}
41+
42+
// Then generate some heights
43+
for chunk_x in 0..16 {
44+
for chunk_z in 0..16 {
45+
let global_x = x * 16 + chunk_x;
46+
let global_z = z * 16 + chunk_z;
47+
let height = noise.get_noise(f64::from(global_x), f64::from(global_z));
48+
let height = (height * 64.0) as i32 + 64;
49+
heights.push((global_x, global_z, height));
50+
}
51+
}
52+
53+
// Fill in the sections that consist of only stone first with the set_section method since
54+
// it's faster than set_block
55+
let y_min = heights.iter().min_by(|a, b| a.2.cmp(&b.2)).unwrap().2;
56+
let heighst_full_section = y_min / 16;
57+
for section_y in -4..heighst_full_section {
58+
chunk.set_section(section_y as i8, stone.clone())?;
59+
}
60+
let above_filled_sections = (heighst_full_section * 16) - 1;
61+
for (global_x, global_z, height) in heights {
62+
if height > above_filled_sections {
63+
let height = height - above_filled_sections;
64+
for y in 0..height {
65+
if y + above_filled_sections <= 64 {
66+
chunk.set_block(
67+
global_x & 0xF,
68+
y + above_filled_sections,
69+
global_z & 0xF,
70+
BlockData {
71+
name: "minecraft:sand".to_string(),
72+
properties: None,
73+
},
74+
)?;
75+
} else {
76+
chunk.set_block(
77+
global_x & 0xF,
78+
y + above_filled_sections,
79+
global_z & 0xF,
80+
BlockData {
81+
name: "minecraft:grass_block".to_string(),
82+
properties: Some(BTreeMap::from([(
83+
"snowy".to_string(),
84+
"false".to_string(),
85+
)])),
86+
},
87+
)?;
88+
}
89+
}
90+
}
91+
}
92+
93+
Ok(chunk)
94+
}
95+
}

0 commit comments

Comments
 (0)