Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ chrono = "0.4.43"
rayon = "1.10.0"
clap = { version = "4.5.54", features = ["derive"] }
toml = "0.9.11"
fastrand = "2.3.0"

[patch.crates-io]
# TODO: Remove patch once egui requirement is more flexible.
Expand Down
2 changes: 1 addition & 1 deletion src/client/player/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ impl Plugin for PlayerPlugin {
player_systems::handle_controller_movement_system,
player_systems::handle_player_collider_events_system,
)
.run_if(terrain_resources::SpawnRegionLoaded::is_loaded) // TODO: doublecheck
.run_if(terrain_resources::SpawnRegionLoaded::is_loaded)
.run_if(player_resources::PlayerSpawned::is_spawned),
);
app.add_systems(
Expand Down
3 changes: 1 addition & 2 deletions src/server/terrain/persistence.rs
Original file line number Diff line number Diff line change
Expand Up @@ -179,7 +179,6 @@ mod tests {
let mut actual_new_chunk = Chunk::new(IVec3::new(20, 0, 20));
generator.generate_chunk(&mut actual_new_chunk);

// FIXME: Make Terrain Generation fully deterministic
// assert_eq!(possible_new_chunk.data, actual_new_chunk.data);
assert_eq!(possible_new_chunk.data, actual_new_chunk.data);
}
}
60 changes: 28 additions & 32 deletions src/server/terrain/util/generator.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
use fastrand::Rng;

use crate::{
prelude::*,
terrain::{
Expand Down Expand Up @@ -42,24 +44,26 @@ impl Generator {
}

pub fn generate_chunk(&self, chunk: &mut Chunk) {
let mut rng = fastrand::Rng::new();
rng.seed(chunk.rng_seed());

for_each_chunk_coordinate!(chunk, |x, y, z, world_position| {
let block = self.generate_block(world_position);
chunk.set_unpadded(x, y, z, block);
});

for_each_chunk_coordinate!(chunk, |x, y, z, _| {
let pos = IVec3::new(x as i32, y as i32, z as i32);

self.decorate_block(chunk, pos);
self.decorate_block(&mut rng, chunk, pos);
});

for _ in 0..self.params.tree.spawn_attempts_per_chunk {
self.attempt_spawn_tree(chunk);
self.attempt_spawn_tree(&mut rng, chunk);
}
}

fn attempt_spawn_tree(&self, chunk: &mut Chunk) {
let proposal = Self::propose_tree_blocks(self);
fn attempt_spawn_tree(&self, rng: &mut Rng, chunk: &mut Chunk) {
let proposal = self.propose_tree_blocks(rng);

struct Bounds {
min: IVec3,
Expand All @@ -85,15 +89,12 @@ impl Generator {
},
);

let sapling_x: i32 = rand::random_range(
proposal_bounds.min.x.abs()..(CHUNK_SIZE as i32 - proposal_bounds.max.x),
);
let sapling_y: i32 = rand::random_range(
proposal_bounds.min.y.abs()..(CHUNK_SIZE as i32 - proposal_bounds.max.y),
);
let sapling_z: i32 = rand::random_range(
proposal_bounds.min.z.abs()..(CHUNK_SIZE as i32 - proposal_bounds.max.z),
);
let sapling_x =
rng.i32(proposal_bounds.min.x.abs()..(CHUNK_SIZE as i32 - proposal_bounds.max.x));
let sapling_y =
rng.i32(proposal_bounds.min.y.abs()..(CHUNK_SIZE as i32 - proposal_bounds.max.y));
let sapling_z =
rng.i32(proposal_bounds.min.z.abs()..(CHUNK_SIZE as i32 - proposal_bounds.max.z));

if chunk.get(sapling_x, sapling_y, sapling_z) != BlockId::Grass {
return;
Expand All @@ -102,14 +103,11 @@ impl Generator {
let proposal_valid = proposal.iter().all(|(relative_pos, _block)| {
let IVec3 { x, y, z } = relative_pos;
Chunk::is_within_padded_bounds(
sapling_x as i32 + { *x },
sapling_y as i32 + { *y },
sapling_z as i32 + { *z },
) && chunk.get(
sapling_x as i32 + { *x },
sapling_y as i32 + { *y },
sapling_z as i32 + { *z },
) == BlockId::Air
sapling_x + { *x },
sapling_y + { *y },
sapling_z + { *z },
) && chunk.get(sapling_x + { *x }, sapling_y + { *y }, sapling_z + { *z })
== BlockId::Air
});

if !proposal_valid {
Expand All @@ -119,26 +117,24 @@ impl Generator {
proposal.iter().for_each(|(relative_pos, block_id)| {
let IVec3 { x, y, z } = relative_pos;
chunk.set(
sapling_x as i32 + { *x },
sapling_y as i32 + { *y },
sapling_z as i32 + { *z },
sapling_x + { *x },
sapling_y + { *y },
sapling_z + { *z },
*block_id,
);
});
}

fn propose_tree_blocks(&self) -> Vec<(IVec3, BlockId)> {
fn propose_tree_blocks(&self, rng: &mut Rng) -> Vec<(IVec3, BlockId)> {
let mut blocks = Vec::new();

let min_tree_stump_height = self.params.tree.min_stump_height;
let max_tree_stump_height = self.params.tree.max_stump_height;

let tree_stump_height =
rand::random_range(min_tree_stump_height..max_tree_stump_height) as i32;
let tree_stump_height = rng.u32(min_tree_stump_height..max_tree_stump_height) as i32;

let bush_radius: i32 =
rand::random_range(self.params.tree.min_bush_radius..self.params.tree.max_bush_radius)
as i32;
rng.u32(self.params.tree.min_bush_radius..self.params.tree.max_bush_radius) as i32;

for dx in -bush_radius..bush_radius {
for dz in -bush_radius..bush_radius {
Expand All @@ -165,7 +161,7 @@ impl Generator {
blocks
}

fn decorate_block(&self, chunk: &mut Chunk, position: IVec3) {
fn decorate_block(&self, rng: &mut Rng, chunk: &mut Chunk, position: IVec3) {
let x = position.x as usize;
let y = position.y as usize;
let z = position.z as usize;
Expand All @@ -177,7 +173,7 @@ impl Generator {
&& Chunk::valid_unpadded(x, y - 1, z)
&& chunk.get_unpadded(x, y - 1, z) == BlockId::Grass
{
let random_number = rand::random_range(0..=self.params.grass.frequency);
let random_number = rng.u32(0..=self.params.grass.frequency);
if random_number == 0 {
chunk.set_unpadded(x, y, z, BlockId::Tallgrass);
}
Expand Down
8 changes: 8 additions & 0 deletions src/shared/chunk/chunk_data.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
use std::hash::{DefaultHasher, Hash, Hasher};

use bevy::math::IVec3;

use crate::*;
Expand Down Expand Up @@ -79,4 +81,10 @@ impl Chunk {
pub fn key_eq_pos(key: [i32; 3], position: IVec3) -> bool {
position.x == key[0] && position.y == key[1] && position.z == key[2]
}

pub fn rng_seed(&self) -> u64 {
let mut s = DefaultHasher::new();
self.position.hash(&mut s);
s.finish()
}
}
2 changes: 1 addition & 1 deletion src/shared/networking.rs
Original file line number Diff line number Diff line change
Expand Up @@ -149,7 +149,7 @@ impl<'de> Deserialize<'de> for Username {
}
}

pub const DEFAULT_SPAWN_POINT: IVec3 = IVec3::new(0, 43, 0); // TODO: determine spawn point from terain
pub const DEFAULT_SPAWN_POINT: IVec3 = IVec3::new(0, 43, 0);

#[derive(Serialize, Deserialize, Clone, Copy, Debug)]
pub struct PlayerState {
Expand Down