Skip to content

Commit 790adbc

Browse files
committed
Merge branch 'master' of https://github.com/Snowiiii/Pumpkin
2 parents 287f466 + 43116c5 commit 790adbc

File tree

14 files changed

+349
-192
lines changed

14 files changed

+349
-192
lines changed

Cargo.toml

+1
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ tokio = { version = "1.40", features = [
2929
"net",
3030
"rt-multi-thread",
3131
"sync",
32+
"io-std",
3233
] }
3334

3435
thiserror = "1.0"

pumpkin-core/src/math/position.rs

+22-2
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,33 @@
1+
use super::vector3::Vector3;
12
use std::fmt;
23

4+
use crate::math::vector2::Vector2;
5+
use num_traits::Euclid;
36
use serde::{Deserialize, Serialize};
47

5-
use super::vector3::Vector3;
6-
78
#[derive(Clone, Copy)]
89
/// Aka Block Position
910
pub struct WorldPosition(pub Vector3<i32>);
1011

12+
impl WorldPosition {
13+
pub fn chunk_and_chunk_relative_position(&self) -> (Vector2<i32>, Vector3<i32>) {
14+
let (z_chunk, z_rem) = self.0.z.div_rem_euclid(&16);
15+
let (x_chunk, x_rem) = self.0.x.div_rem_euclid(&16);
16+
let chunk_coordinate = Vector2 {
17+
x: x_chunk,
18+
z: z_chunk,
19+
};
20+
21+
// Since we divide by 16 remnant can never exceed u8
22+
let relative = Vector3 {
23+
x: x_rem,
24+
z: z_rem,
25+
26+
y: self.0.y,
27+
};
28+
(chunk_coordinate, relative)
29+
}
30+
}
1131
impl Serialize for WorldPosition {
1232
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
1333
where

pumpkin-world/src/chunk/anvil.rs

+1
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ use crate::level::SaveFile;
1010

1111
use super::{ChunkData, ChunkReader, ChunkReadingError, CompressionError};
1212

13+
#[derive(Clone)]
1314
pub struct AnvilChunkReader {}
1415

1516
impl Default for AnvilChunkReader {

pumpkin-world/src/chunk/mod.rs

-1
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,6 @@ pub struct ChunkData {
5858
pub blocks: ChunkBlocks,
5959
pub position: Vector2<i32>,
6060
}
61-
6261
pub struct ChunkBlocks {
6362
// TODO make this a Vec that doesn't store the upper layers that only contain air
6463

pumpkin-world/src/coordinates.rs

+12-2
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
use std::ops::Deref;
22

3+
use crate::{WORLD_LOWEST_Y, WORLD_MAX_Y};
34
use derive_more::derive::{AsMut, AsRef, Display, Into};
45
use num_traits::{PrimInt, Signed, Unsigned};
56
use pumpkin_core::math::vector2::Vector2;
7+
use pumpkin_core::math::vector3::Vector3;
68
use serde::{Deserialize, Serialize};
79

8-
use crate::{WORLD_LOWEST_Y, WORLD_MAX_Y};
9-
1010
#[derive(
1111
Serialize, Deserialize, Debug, Clone, Copy, PartialEq, Eq, AsRef, AsMut, Into, Display,
1212
)]
@@ -130,3 +130,13 @@ impl ChunkRelativeXZBlockCoordinates {
130130
}
131131
}
132132
}
133+
134+
impl From<Vector3<i32>> for ChunkRelativeBlockCoordinates {
135+
fn from(value: Vector3<i32>) -> Self {
136+
Self {
137+
x: (value.x as u8).into(),
138+
z: (value.z as u8).into(),
139+
y: value.y.into(),
140+
}
141+
}
142+
}

pumpkin-world/src/level.rs

+86-68
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,16 @@
11
use std::{collections::HashMap, path::PathBuf, sync::Arc};
22

3-
use parking_lot::{Mutex, RwLock};
4-
use pumpkin_core::math::vector2::Vector2;
5-
use rayon::prelude::*;
6-
use tokio::sync::mpsc;
7-
83
use crate::{
94
chunk::{
105
anvil::AnvilChunkReader, ChunkData, ChunkParsingError, ChunkReader, ChunkReadingError,
116
},
127
world_gen::{get_world_gen, Seed, WorldGenerator},
138
};
9+
use pumpkin_core::math::vector2::Vector2;
10+
use tokio::sync::mpsc;
11+
use tokio::sync::{Mutex, RwLock};
12+
13+
type RAMChunkStorage = Arc<RwLock<HashMap<Vector2<i32>, Arc<RwLock<ChunkData>>>>>;
1414

1515
/// The `Level` module provides functionality for working with chunks within or outside a Minecraft world.
1616
///
@@ -23,12 +23,12 @@ use crate::{
2323
/// For more details on world generation, refer to the `WorldGenerator` module.
2424
pub struct Level {
2525
save_file: Option<SaveFile>,
26-
loaded_chunks: Arc<RwLock<HashMap<Vector2<i32>, Arc<ChunkData>>>>,
26+
loaded_chunks: RAMChunkStorage,
2727
chunk_watchers: Arc<Mutex<HashMap<Vector2<i32>, usize>>>,
28-
chunk_reader: Box<dyn ChunkReader>,
29-
world_gen: Box<dyn WorldGenerator>,
28+
chunk_reader: Arc<Box<dyn ChunkReader>>,
29+
world_gen: Arc<Box<dyn WorldGenerator>>,
3030
}
31-
31+
#[derive(Clone)]
3232
pub struct SaveFile {
3333
#[expect(dead_code)]
3434
root_folder: PathBuf,
@@ -37,7 +37,7 @@ pub struct SaveFile {
3737

3838
impl Level {
3939
pub fn from_root_folder(root_folder: PathBuf) -> Self {
40-
let world_gen = get_world_gen(Seed(0)); // TODO Read Seed from config.
40+
let world_gen = get_world_gen(Seed(0)).into(); // TODO Read Seed from config.
4141

4242
if root_folder.exists() {
4343
let region_folder = root_folder.join("region");
@@ -52,7 +52,7 @@ impl Level {
5252
root_folder,
5353
region_folder,
5454
}),
55-
chunk_reader: Box::new(AnvilChunkReader::new()),
55+
chunk_reader: Arc::new(Box::new(AnvilChunkReader::new())),
5656
loaded_chunks: Arc::new(RwLock::new(HashMap::new())),
5757
chunk_watchers: Arc::new(Mutex::new(HashMap::new())),
5858
}
@@ -64,7 +64,7 @@ impl Level {
6464
Self {
6565
world_gen,
6666
save_file: None,
67-
chunk_reader: Box::new(AnvilChunkReader::new()),
67+
chunk_reader: Arc::new(Box::new(AnvilChunkReader::new())),
6868
loaded_chunks: Arc::new(RwLock::new(HashMap::new())),
6969
chunk_watchers: Arc::new(Mutex::new(HashMap::new())),
7070
}
@@ -76,8 +76,8 @@ impl Level {
7676
/// Marks chunks as "watched" by a unique player. When no players are watching a chunk,
7777
/// it is removed from memory. Should only be called on chunks the player was not watching
7878
/// before
79-
pub fn mark_chunk_as_newly_watched(&self, chunks: &[Vector2<i32>]) {
80-
let mut watchers = self.chunk_watchers.lock();
79+
pub async fn mark_chunk_as_newly_watched(&self, chunks: &[Vector2<i32>]) {
80+
let mut watchers = self.chunk_watchers.lock().await;
8181
for chunk in chunks {
8282
match watchers.entry(*chunk) {
8383
std::collections::hash_map::Entry::Occupied(mut occupied) => {
@@ -93,9 +93,9 @@ impl Level {
9393

9494
/// Marks chunks no longer "watched" by a unique player. When no players are watching a chunk,
9595
/// it is removed from memory. Should only be called on chunks the player was watching before
96-
pub fn mark_chunk_as_not_watched_and_clean(&self, chunks: &[Vector2<i32>]) {
96+
pub async fn mark_chunk_as_not_watched_and_clean(&self, chunks: &[Vector2<i32>]) {
9797
let dropped_chunks = {
98-
let mut watchers = self.chunk_watchers.lock();
98+
let mut watchers = self.chunk_watchers.lock().await;
9999
chunks
100100
.iter()
101101
.filter(|chunk| match watchers.entry(**chunk) {
@@ -119,77 +119,95 @@ impl Level {
119119
})
120120
.collect::<Vec<_>>()
121121
};
122-
let mut loaded_chunks = self.loaded_chunks.write();
122+
let mut loaded_chunks = self.loaded_chunks.write().await;
123123
let dropped_chunk_data = dropped_chunks
124124
.iter()
125125
.filter_map(|chunk| {
126-
log::debug!("Unloading chunk {:?}", chunk);
126+
//log::debug!("Unloading chunk {:?}", chunk);
127127
loaded_chunks.remove_entry(*chunk)
128128
})
129129
.collect();
130130
self.write_chunks(dropped_chunk_data);
131131
}
132132

133-
pub fn write_chunks(&self, _chunks_to_write: Vec<(Vector2<i32>, Arc<ChunkData>)>) {
133+
pub fn write_chunks(&self, _chunks_to_write: Vec<(Vector2<i32>, Arc<RwLock<ChunkData>>)>) {
134134
//TODO
135135
}
136136

137137
/// Reads/Generates many chunks in a world
138138
/// MUST be called from a tokio runtime thread
139139
///
140140
/// Note: The order of the output chunks will almost never be in the same order as the order of input chunks
141+
pub fn fetch_chunks(
142+
&self,
143+
chunks: &[Vector2<i32>],
144+
channel: mpsc::Sender<Arc<RwLock<ChunkData>>>,
145+
) {
146+
for chunk in chunks {
147+
{
148+
let chunk_location = *chunk;
149+
let channel = channel.clone();
150+
let loaded_chunks = self.loaded_chunks.clone();
151+
let chunk_reader = self.chunk_reader.clone();
152+
let save_file = self.save_file.clone();
153+
let world_gen = self.world_gen.clone();
154+
tokio::spawn(async move {
155+
let loaded_chunks_read = loaded_chunks.read().await;
156+
let possibly_loaded_chunk = loaded_chunks_read.get(&chunk_location).cloned();
157+
drop(loaded_chunks_read);
158+
match possibly_loaded_chunk {
159+
Some(chunk) => {
160+
let chunk = chunk.clone();
161+
channel.send(chunk).await.unwrap();
162+
}
163+
None => {
164+
let chunk_data = match save_file {
165+
Some(save_file) => {
166+
match chunk_reader.read_chunk(&save_file, &chunk_location) {
167+
Ok(data) => Ok(Arc::new(RwLock::new(data))),
168+
Err(
169+
ChunkReadingError::ChunkNotExist
170+
| ChunkReadingError::ParsingError(
171+
ChunkParsingError::ChunkNotGenerated,
172+
),
173+
) => {
174+
// This chunk was not generated yet.
175+
let chunk = Arc::new(RwLock::new(
176+
world_gen.generate_chunk(chunk_location),
177+
));
178+
let mut loaded_chunks = loaded_chunks.write().await;
179+
loaded_chunks.insert(chunk_location, chunk.clone());
180+
drop(loaded_chunks);
181+
Ok(chunk)
182+
}
183+
Err(err) => Err(err),
184+
}
185+
}
186+
None => {
187+
// There is no savefile yet -> generate the chunks
188+
let chunk = Arc::new(RwLock::new(
189+
world_gen.generate_chunk(chunk_location),
190+
));
141191

142-
pub fn fetch_chunks(&self, chunks: &[Vector2<i32>], channel: mpsc::Sender<Arc<ChunkData>>) {
143-
chunks.into_par_iter().for_each(|at| {
144-
let channel = channel.clone();
145-
146-
let maybe_chunk = {
147-
let loaded_chunks = self.loaded_chunks.read();
148-
loaded_chunks.get(at).cloned()
149-
}
150-
.or_else(|| {
151-
let chunk_data = match &self.save_file {
152-
Some(save_file) => {
153-
match self.chunk_reader.read_chunk(save_file, at) {
154-
Ok(data) => Ok(Arc::new(data)),
155-
Err(
156-
ChunkReadingError::ChunkNotExist
157-
| ChunkReadingError::ParsingError(
158-
ChunkParsingError::ChunkNotGenerated,
159-
),
160-
) => {
161-
// This chunk was not generated yet.
162-
let chunk = Arc::new(self.world_gen.generate_chunk(*at));
163-
Ok(chunk)
192+
let mut loaded_chunks = loaded_chunks.write().await;
193+
loaded_chunks.insert(chunk_location, chunk.clone());
194+
Ok(chunk)
195+
}
196+
};
197+
match chunk_data {
198+
Ok(data) => channel.send(data).await.unwrap(),
199+
Err(err) => {
200+
log::warn!(
201+
"Failed to read chunk {:?}: {:?}",
202+
chunk_location,
203+
err
204+
);
205+
}
164206
}
165-
Err(err) => Err(err),
166207
}
167208
}
168-
None => {
169-
// There is no savefile yet -> generate the chunks
170-
let chunk = Arc::new(self.world_gen.generate_chunk(*at));
171-
Ok(chunk)
172-
}
173-
};
174-
match chunk_data {
175-
Ok(data) => Some(data),
176-
Err(err) => {
177-
// TODO: Panic here?
178-
log::warn!("Failed to read chunk {:?}: {:?}", at, err);
179-
None
180-
}
181-
}
182-
});
183-
match maybe_chunk {
184-
Some(chunk) => {
185-
channel
186-
.blocking_send(chunk.clone())
187-
.expect("Failed sending ChunkData.");
188-
}
189-
None => {
190-
log::error!("Unable to send chunk {:?}!", at);
191-
}
192-
};
193-
})
209+
});
210+
}
211+
}
194212
}
195213
}

0 commit comments

Comments
 (0)