mirror of
https://github.com/Pumpkin-MC/Pumpkin.git
synced 2026-08-30 20:14:23 +00:00
fix: mega pine trees
This commit is contained in:
@@ -69,6 +69,7 @@ impl<R: Read> Read for CompressionRead<R> {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct AnvilChunkData {
|
||||
compression: Option<Compression>,
|
||||
// Length is always the length of this + compression byte (1) so we dont need to save a length
|
||||
@@ -321,11 +322,14 @@ impl AnvilChunkData {
|
||||
.map_err(|err| ChunkWritingError::ChunkSerializingError(err.to_string()))?;
|
||||
|
||||
let compression = compression.unwrap_or_else(|| chunk_config.compression.algorithm.into());
|
||||
let level = chunk_config.compression.level;
|
||||
|
||||
// We need to buffer here anyway so there's no use in making an impl Write for this
|
||||
let compressed_data = compression
|
||||
.compress_data(&raw_bytes, chunk_config.compression.level)
|
||||
.map_err(ChunkWritingError::Compression)?;
|
||||
// Offload CPU-heavy compression to blocking thread pool
|
||||
let compressed_data =
|
||||
tokio::task::spawn_blocking(move || compression.compress_data(&raw_bytes, level))
|
||||
.await
|
||||
.map_err(|err| ChunkWritingError::IoError(std::io::Error::other(err)))?
|
||||
.map_err(ChunkWritingError::Compression)?;
|
||||
|
||||
Ok(Self {
|
||||
compression: Some(compression),
|
||||
@@ -504,7 +508,7 @@ impl<S: SingleChunkDataSerializer> Default for AnvilChunkFile<S> {
|
||||
}
|
||||
}
|
||||
|
||||
pub trait SingleChunkDataSerializer: Send + Sync + Sized + Dirtiable {
|
||||
pub trait SingleChunkDataSerializer: Send + Sync + Sized + Dirtiable + 'static {
|
||||
fn to_bytes(
|
||||
&self,
|
||||
) -> Pin<Box<dyn Future<Output = Result<Bytes, ChunkSerializingError>> + Send + '_>>;
|
||||
@@ -512,7 +516,7 @@ pub trait SingleChunkDataSerializer: Send + Sync + Sized + Dirtiable {
|
||||
fn position(&self) -> (i32, i32);
|
||||
}
|
||||
|
||||
impl<S: SingleChunkDataSerializer> ChunkSerializer for AnvilChunkFile<S> {
|
||||
impl<S: SingleChunkDataSerializer + 'static> ChunkSerializer for AnvilChunkFile<S> {
|
||||
type Data = S;
|
||||
type WriteBackend = PathBuf;
|
||||
|
||||
@@ -796,11 +800,17 @@ impl<S: SingleChunkDataSerializer> ChunkSerializer for AnvilChunkFile<S> {
|
||||
let is_ok = match &self.chunks_data[index] {
|
||||
None => stream.send(LoadedData::Missing(chunk)).await.is_ok(),
|
||||
Some(chunk_metadata) => {
|
||||
let chunk_data = &chunk_metadata.serialized_data;
|
||||
let result = match chunk_data.to_chunk(chunk) {
|
||||
Ok(chunk) => LoadedData::Loaded(chunk),
|
||||
Err(err) => LoadedData::Error((chunk, err)),
|
||||
};
|
||||
let chunk_data = chunk_metadata.serialized_data.clone();
|
||||
let result =
|
||||
match tokio::task::spawn_blocking(move || chunk_data.to_chunk(chunk)).await
|
||||
{
|
||||
Ok(Ok(chunk_res)) => LoadedData::Loaded(chunk_res),
|
||||
Ok(Err(err)) => LoadedData::Error((chunk, err)),
|
||||
Err(err) => LoadedData::Error((
|
||||
chunk,
|
||||
ChunkReadingError::IoError(std::io::Error::other(err)),
|
||||
)),
|
||||
};
|
||||
|
||||
stream.send(result).await.is_ok()
|
||||
}
|
||||
|
||||
@@ -339,8 +339,12 @@ impl<S: SingleChunkDataSerializer> LinearV2File<S> {
|
||||
}
|
||||
|
||||
/// Build the decompressed byte stream for bucket `bucket_idx`.
|
||||
fn serialise_bucket(&self, bucket_idx: usize) -> Vec<u8> {
|
||||
let grid_size = self.grid_size;
|
||||
fn serialise_bucket(
|
||||
chunks_data: &[Option<Bytes>; CHUNK_COUNT],
|
||||
timestamps: &[u64; CHUNK_COUNT],
|
||||
bucket_idx: usize,
|
||||
grid_size: u8,
|
||||
) -> Vec<u8> {
|
||||
let cpb = Self::chunks_per_bucket(grid_size);
|
||||
let mut buf = Vec::new();
|
||||
|
||||
@@ -348,8 +352,8 @@ impl<S: SingleChunkDataSerializer> LinearV2File<S> {
|
||||
// Recover the global chunk index from bucket + local.
|
||||
let chunk_index = Self::global_chunk_index(bucket_idx, local, grid_size);
|
||||
let entry = BucketChunkEntry {
|
||||
timestamp: self.timestamps[chunk_index],
|
||||
data: self.chunks_data[chunk_index].clone(),
|
||||
timestamp: timestamps[chunk_index],
|
||||
data: chunks_data[chunk_index].clone(),
|
||||
};
|
||||
entry.write_into(&mut buf);
|
||||
}
|
||||
@@ -371,13 +375,15 @@ impl<S: SingleChunkDataSerializer> LinearV2File<S> {
|
||||
fn build_bitmap(&self) -> ChunkBitmap {
|
||||
let mut bitmap = ChunkBitmap::new();
|
||||
for (i, chunk) in self.chunks_data.iter().enumerate() {
|
||||
bitmap.set(i, chunk.is_some());
|
||||
if chunk.is_some() {
|
||||
bitmap.set(i, true);
|
||||
}
|
||||
}
|
||||
bitmap
|
||||
}
|
||||
}
|
||||
|
||||
impl<S: SingleChunkDataSerializer> ChunkSerializer for LinearV2File<S> {
|
||||
impl<S: SingleChunkDataSerializer + 'static> ChunkSerializer for LinearV2File<S> {
|
||||
type Data = S;
|
||||
type WriteBackend = PathBuf;
|
||||
type ChunkConfig = ();
|
||||
@@ -398,23 +404,29 @@ impl<S: SingleChunkDataSerializer> ChunkSerializer for LinearV2File<S> {
|
||||
|
||||
let grid_size = self.grid_size;
|
||||
let bucket_count = Self::bucket_count(grid_size);
|
||||
let chunks_data = self.chunks_data.clone();
|
||||
let timestamps = self.timestamps;
|
||||
|
||||
let mut compressed_buckets: Vec<Box<[u8]>> = Vec::with_capacity(bucket_count);
|
||||
let mut bucket_entries: Vec<BucketSizeEntry> = Vec::with_capacity(bucket_count);
|
||||
let (bucket_entries, compressed_buckets) = tokio::task::spawn_blocking(move || {
|
||||
let mut compressed_buckets: Vec<Box<[u8]>> = Vec::with_capacity(bucket_count);
|
||||
let mut bucket_entries: Vec<BucketSizeEntry> = Vec::with_capacity(bucket_count);
|
||||
|
||||
for bucket_idx in 0..bucket_count {
|
||||
let raw = self.serialise_bucket(bucket_idx);
|
||||
// TODO: ruzstd currently only supports Fastest level.
|
||||
let compressed =
|
||||
compress_to_vec(raw.as_slice(), CompressionLevel::Fastest).into_boxed_slice();
|
||||
let hash = xxh64(&compressed, 0);
|
||||
bucket_entries.push(BucketSizeEntry {
|
||||
size: compressed.len() as u32,
|
||||
compression_level: 1,
|
||||
xxhash: hash,
|
||||
});
|
||||
compressed_buckets.push(compressed);
|
||||
}
|
||||
for bucket_idx in 0..bucket_count {
|
||||
let raw = Self::serialise_bucket(&chunks_data, ×tamps, bucket_idx, grid_size);
|
||||
let compressed =
|
||||
compress_to_vec(raw.as_slice(), CompressionLevel::Fastest).into_boxed_slice();
|
||||
let hash = xxh64(&compressed, 0);
|
||||
bucket_entries.push(BucketSizeEntry {
|
||||
size: compressed.len() as u32,
|
||||
compression_level: 1,
|
||||
xxhash: hash,
|
||||
});
|
||||
compressed_buckets.push(compressed);
|
||||
}
|
||||
(bucket_entries, compressed_buckets)
|
||||
})
|
||||
.await
|
||||
.map_err(std::io::Error::other)?;
|
||||
|
||||
let newest_timestamp = self.timestamps.iter().copied().max().unwrap_or(0);
|
||||
|
||||
@@ -583,15 +595,27 @@ impl<S: SingleChunkDataSerializer> ChunkSerializer for LinearV2File<S> {
|
||||
for chunk in chunks {
|
||||
let index = Self::get_chunk_index(chunk.x, chunk.y);
|
||||
|
||||
let result = self.chunks_data[index].as_ref().map_or_else(
|
||||
|| LoadedData::Missing(chunk),
|
||||
|data| match S::from_bytes(data, chunk) {
|
||||
Ok(c) => LoadedData::Loaded(c),
|
||||
Err(err) => LoadedData::Error((chunk, err)),
|
||||
},
|
||||
);
|
||||
let is_ok = match &self.chunks_data[index] {
|
||||
None => stream.send(LoadedData::Missing(chunk)).await.is_ok(),
|
||||
Some(data) => {
|
||||
let data = data.clone();
|
||||
let result = match tokio::task::spawn_blocking(move || {
|
||||
S::from_bytes(&data, chunk)
|
||||
})
|
||||
.await
|
||||
{
|
||||
Ok(Ok(c)) => LoadedData::Loaded(c),
|
||||
Ok(Err(err)) => LoadedData::Error((chunk, err)),
|
||||
Err(err) => LoadedData::Error((
|
||||
chunk,
|
||||
ChunkReadingError::IoError(std::io::Error::other(err)),
|
||||
)),
|
||||
};
|
||||
stream.send(result).await.is_ok()
|
||||
}
|
||||
};
|
||||
|
||||
if stream.send(result).await.is_err() {
|
||||
if !is_ok {
|
||||
// Receiver dropped — stop early to avoid unnecessary work.
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -34,7 +34,7 @@ impl<D> Default for PumpFile<D> {
|
||||
|
||||
impl<D> ChunkSerializer for PumpFile<D>
|
||||
where
|
||||
D: SingleChunkDataSerializer + Send + Sync + Sized,
|
||||
D: SingleChunkDataSerializer + Send + Sync + Sized + 'static,
|
||||
{
|
||||
type Data = D;
|
||||
type WriteBackend = PathBuf;
|
||||
@@ -129,34 +129,30 @@ where
|
||||
let index = (rel_x + rel_z * 32) as usize;
|
||||
|
||||
if let Some(chunk_bytes) = self.data.chunks.get(&index.to_string()) {
|
||||
let mut decoder = match StreamingDecoder::new(&chunk_bytes[..]) {
|
||||
Ok(d) => d,
|
||||
Err(e) => {
|
||||
let _ = stream
|
||||
.send(LoadedData::Error((
|
||||
pos,
|
||||
ChunkReadingError::IoError(std::io::Error::other(e.to_string())),
|
||||
)))
|
||||
.await;
|
||||
continue;
|
||||
}
|
||||
};
|
||||
let mut decompressed = Vec::new();
|
||||
if let Err(e) = std::io::Read::read_to_end(&mut decoder, &mut decompressed) {
|
||||
let _ = stream
|
||||
.send(LoadedData::Error((pos, ChunkReadingError::IoError(e))))
|
||||
.await;
|
||||
continue;
|
||||
}
|
||||
let chunk_bytes = chunk_bytes.clone();
|
||||
let res = tokio::task::spawn_blocking(move || {
|
||||
let mut decoder = StreamingDecoder::new(&chunk_bytes[..]).map_err(|e| {
|
||||
ChunkReadingError::IoError(std::io::Error::other(e.to_string()))
|
||||
})?;
|
||||
let mut decompressed = Vec::new();
|
||||
std::io::Read::read_to_end(&mut decoder, &mut decompressed)
|
||||
.map_err(ChunkReadingError::IoError)?;
|
||||
let bytes = Bytes::from(decompressed);
|
||||
D::from_bytes(&bytes, pos)
|
||||
})
|
||||
.await;
|
||||
|
||||
let bytes = Bytes::from(decompressed);
|
||||
match D::from_bytes(&bytes, pos) {
|
||||
Ok(data) => {
|
||||
let _ = stream.send(LoadedData::Loaded(data)).await;
|
||||
}
|
||||
Err(e) => {
|
||||
let _ = stream.send(LoadedData::Error((pos, e))).await;
|
||||
}
|
||||
let data_res = match res {
|
||||
Ok(Ok(data)) => LoadedData::Loaded(data),
|
||||
Ok(Err(e)) => LoadedData::Error((pos, e)),
|
||||
Err(e) => LoadedData::Error((
|
||||
pos,
|
||||
ChunkReadingError::IoError(std::io::Error::other(e)),
|
||||
)),
|
||||
};
|
||||
|
||||
if stream.send(data_res).await.is_err() {
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
let _ = stream.send(LoadedData::Missing(pos)).await;
|
||||
|
||||
@@ -58,7 +58,7 @@ struct ChunkSerializerLazyLoader<S: ChunkSerializer<WriteBackend = PathBuf>> {
|
||||
internal: OnceCell<Arc<RwLock<S>>>,
|
||||
}
|
||||
|
||||
impl<S: ChunkSerializer<WriteBackend = PathBuf>> ChunkSerializerLazyLoader<S> {
|
||||
impl<S: ChunkSerializer<WriteBackend = PathBuf> + 'static> ChunkSerializerLazyLoader<S> {
|
||||
fn new(path: PathBuf) -> Self {
|
||||
Self {
|
||||
path,
|
||||
@@ -101,7 +101,9 @@ impl<S: ChunkSerializer<WriteBackend = PathBuf>> ChunkSerializerLazyLoader<S> {
|
||||
|
||||
match tokio::fs::read(&self.path).await {
|
||||
Ok(bytes) => {
|
||||
let value = S::read(bytes.into())?;
|
||||
let value = tokio::task::spawn_blocking(move || S::read(bytes.into()))
|
||||
.await
|
||||
.map_err(|e| ChunkReadingError::IoError(std::io::Error::other(e)))??;
|
||||
trace!("Successfully read file from disk: {}", self.path.display());
|
||||
Ok(value)
|
||||
}
|
||||
@@ -428,3 +430,92 @@ where
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub enum LevelFileIO<Linear, Anvil, Pump>
|
||||
where
|
||||
Linear: ChunkSerializer<WriteBackend = PathBuf>,
|
||||
Anvil: ChunkSerializer<WriteBackend = PathBuf>,
|
||||
Pump: ChunkSerializer<WriteBackend = PathBuf>,
|
||||
{
|
||||
Linear(ChunkFileManager<Linear>),
|
||||
Anvil(ChunkFileManager<Anvil>),
|
||||
Pump(ChunkFileManager<Pump>),
|
||||
}
|
||||
|
||||
impl<P, Linear, Anvil, Pump> FileIO for LevelFileIO<Linear, Anvil, Pump>
|
||||
where
|
||||
P: PathFromLevelFolder + Send + Sync + Sized + Dirtiable + 'static,
|
||||
Linear: ChunkSerializer<Data = P, WriteBackend = PathBuf>,
|
||||
Anvil: ChunkSerializer<Data = P, WriteBackend = PathBuf>,
|
||||
Pump: ChunkSerializer<Data = P, WriteBackend = PathBuf>,
|
||||
Linear::ChunkConfig: Send + Sync,
|
||||
Anvil::ChunkConfig: Send + Sync,
|
||||
Pump::ChunkConfig: Send + Sync,
|
||||
{
|
||||
type Data = Arc<P>;
|
||||
|
||||
fn fetch_chunks<'a>(
|
||||
&'a self,
|
||||
folder: &'a LevelFolder,
|
||||
chunk_coords: &'a [Vector2<i32>],
|
||||
stream: tokio::sync::mpsc::Sender<LoadedData<Self::Data, ChunkReadingError>>,
|
||||
) -> BoxFuture<'a, ()> {
|
||||
match self {
|
||||
Self::Linear(io) => io.fetch_chunks(folder, chunk_coords, stream),
|
||||
Self::Anvil(io) => io.fetch_chunks(folder, chunk_coords, stream),
|
||||
Self::Pump(io) => io.fetch_chunks(folder, chunk_coords, stream),
|
||||
}
|
||||
}
|
||||
|
||||
fn save_chunks<'a>(
|
||||
&'a self,
|
||||
folder: &'a LevelFolder,
|
||||
chunks_data: Vec<(Vector2<i32>, Self::Data)>,
|
||||
) -> BoxFuture<'a, Result<(), ChunkWritingError>> {
|
||||
match self {
|
||||
Self::Linear(io) => io.save_chunks(folder, chunks_data),
|
||||
Self::Anvil(io) => io.save_chunks(folder, chunks_data),
|
||||
Self::Pump(io) => io.save_chunks(folder, chunks_data),
|
||||
}
|
||||
}
|
||||
|
||||
fn watch_chunks<'a>(
|
||||
&'a self,
|
||||
folder: &'a LevelFolder,
|
||||
chunks: &'a [Vector2<i32>],
|
||||
) -> BoxFuture<'a, ()> {
|
||||
match self {
|
||||
Self::Linear(io) => io.watch_chunks(folder, chunks),
|
||||
Self::Anvil(io) => io.watch_chunks(folder, chunks),
|
||||
Self::Pump(io) => io.watch_chunks(folder, chunks),
|
||||
}
|
||||
}
|
||||
|
||||
fn unwatch_chunks<'a>(
|
||||
&'a self,
|
||||
folder: &'a LevelFolder,
|
||||
chunks: &'a [Vector2<i32>],
|
||||
) -> BoxFuture<'a, ()> {
|
||||
match self {
|
||||
Self::Linear(io) => io.unwatch_chunks(folder, chunks),
|
||||
Self::Anvil(io) => io.unwatch_chunks(folder, chunks),
|
||||
Self::Pump(io) => io.unwatch_chunks(folder, chunks),
|
||||
}
|
||||
}
|
||||
|
||||
fn clear_watched_chunks(&self) -> BoxFuture<'_, ()> {
|
||||
match self {
|
||||
Self::Linear(io) => io.clear_watched_chunks(),
|
||||
Self::Anvil(io) => io.clear_watched_chunks(),
|
||||
Self::Pump(io) => io.clear_watched_chunks(),
|
||||
}
|
||||
}
|
||||
|
||||
fn block_and_await_ongoing_tasks(&self) -> BoxFuture<'_, ()> {
|
||||
match self {
|
||||
Self::Linear(io) => io.block_and_await_ongoing_tasks(),
|
||||
Self::Anvil(io) => io.block_and_await_ongoing_tasks(),
|
||||
Self::Pump(io) => io.block_and_await_ongoing_tasks(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -93,7 +93,7 @@ where
|
||||
///
|
||||
/// The `Data` type is the type of the data that will be updated or serialized/deserialized
|
||||
/// like `ChunkData` or `EntityData`
|
||||
pub trait ChunkSerializer: Send + Sync + Default {
|
||||
pub trait ChunkSerializer: Send + Sync + Default + 'static {
|
||||
type Data: Send + Sync + Sized + Dirtiable;
|
||||
type WriteBackend;
|
||||
|
||||
|
||||
@@ -211,19 +211,24 @@ impl GenerationSchedule {
|
||||
pos: ChunkPos,
|
||||
stage: StagedChunkEnum,
|
||||
) -> i8 {
|
||||
if last_high_priority.is_empty() {
|
||||
return *last_level.get(&pos).unwrap_or(&ChunkLoading::MAX_LEVEL) + (stage as i8);
|
||||
let base_level = *last_level.get(&pos).unwrap_or(&ChunkLoading::MAX_LEVEL);
|
||||
if base_level == ChunkLoading::MAX_LEVEL {
|
||||
return 127;
|
||||
}
|
||||
if last_high_priority.is_empty() {
|
||||
return base_level + (stage as i8);
|
||||
}
|
||||
let mut min_dst = i32::MAX;
|
||||
for i in last_high_priority {
|
||||
let dst = max((i.x - pos.x).abs(), (i.y - pos.y).abs());
|
||||
min_dst = min_dst.min(dst);
|
||||
if dst <= StagedChunkEnum::FULL_RADIUS
|
||||
&& stage <= StagedChunkEnum::FULL_DEPENDENCIES[dst as usize]
|
||||
{
|
||||
return *last_level.get(&pos).unwrap_or(&ChunkLoading::MAX_LEVEL) + (stage as i8)
|
||||
- 100;
|
||||
return base_level + (stage as i8) - 100 + (dst as i8);
|
||||
}
|
||||
}
|
||||
*last_level.get(&pos).unwrap_or(&ChunkLoading::MAX_LEVEL) + (stage as i8)
|
||||
base_level + (stage as i8) + (min_dst.min(60) as i8)
|
||||
}
|
||||
|
||||
fn sort_queue(&mut self) {
|
||||
@@ -1143,6 +1148,20 @@ impl GenerationSchedule {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Cancel/drop task if chunk is out of range or no longer needed by any target/dependency
|
||||
let effective_target = self
|
||||
.chunk_map
|
||||
.get(&node.pos)
|
||||
.map_or(StagedChunkEnum::None, |h| {
|
||||
h.target_stage.max(h.dependency_stage)
|
||||
});
|
||||
|
||||
if node.stage > effective_target {
|
||||
self.waiting_for_chunks.remove(&task.1);
|
||||
self.drop_node(task.1);
|
||||
continue;
|
||||
}
|
||||
|
||||
if node.stage == StagedChunkEnum::Empty {
|
||||
self.running_task_count += 1;
|
||||
let holder = self.chunk_map.get_mut(&node.pos).unwrap();
|
||||
|
||||
@@ -3,8 +3,8 @@ use super::generation_cache::Cache;
|
||||
use super::{ChunkPos, IOLock};
|
||||
use crate::ProtoChunk;
|
||||
use crate::chunk::format::LightContainer;
|
||||
use crate::chunk::io::LoadedData;
|
||||
use crate::chunk::io::LoadedData::Loaded;
|
||||
use crate::chunk::io::{FileIO, LoadedData};
|
||||
use crate::level::Level;
|
||||
use crossfire::compat::AsyncRx;
|
||||
use pumpkin_config::lighting::LightingEngineConfig;
|
||||
|
||||
@@ -26,16 +26,16 @@ impl MegaPineFoliagePlacer {
|
||||
) -> Vec<BlockPos> {
|
||||
let mut foliage_positions = Vec::new();
|
||||
let pos = node.center;
|
||||
let mut current = 0;
|
||||
for y in pos.0.y - foliage_height + offset..pos.0.y + offset {
|
||||
let mut current_radius = 0;
|
||||
for y in (pos.0.y - foliage_height + offset)..=(pos.0.y + offset) {
|
||||
let delta = pos.0.y - y;
|
||||
let rad = radius
|
||||
let computed_radius = radius
|
||||
+ node.foliage_radius
|
||||
+ (delta as f32 / foliage_height as f32 * 3.5).floor() as i32;
|
||||
let radius = if delta > 0 && rad == current && (y & 1) == 0 {
|
||||
radius + 1
|
||||
+ ((delta as f32 / foliage_height as f32) * 3.5).floor() as i32;
|
||||
let r = if delta > 0 && computed_radius == current_radius && (y & 1) == 0 {
|
||||
computed_radius + 1
|
||||
} else {
|
||||
radius
|
||||
computed_radius
|
||||
};
|
||||
FoliagePlacer::generate_square(
|
||||
&mut foliage_positions,
|
||||
@@ -43,12 +43,12 @@ impl MegaPineFoliagePlacer {
|
||||
chunk,
|
||||
random,
|
||||
BlockPos::new(pos.0.x, y, pos.0.z),
|
||||
radius,
|
||||
r,
|
||||
0,
|
||||
node.giant_trunk,
|
||||
foliage_provider,
|
||||
);
|
||||
current = rad;
|
||||
current_radius = computed_radius;
|
||||
}
|
||||
foliage_positions
|
||||
}
|
||||
|
||||
@@ -26,7 +26,7 @@ impl PineFoliagePlacer {
|
||||
) -> Vec<BlockPos> {
|
||||
let mut foliage_positions = Vec::new();
|
||||
let mut radius = 0;
|
||||
for y in (offset - foliage_height)..offset {
|
||||
for y in (offset - foliage_height..=offset).rev() {
|
||||
FoliagePlacer::generate_square(
|
||||
&mut foliage_positions,
|
||||
self,
|
||||
|
||||
@@ -24,47 +24,51 @@ impl GiantTrunkPlacer {
|
||||
below_trunk_provider: &BlockStateProvider,
|
||||
trunk_block: &BlockState,
|
||||
) -> (Vec<TreeNode>, Vec<BlockPos>) {
|
||||
let pos = start_pos.down();
|
||||
TrunkPlacer::set_dirt(block_registry, chunk, random, &pos, below_trunk_provider);
|
||||
let below = start_pos.down();
|
||||
TrunkPlacer::set_dirt(block_registry, chunk, random, &below, below_trunk_provider);
|
||||
TrunkPlacer::set_dirt(
|
||||
block_registry,
|
||||
chunk,
|
||||
random,
|
||||
&pos.east(),
|
||||
&below.east(),
|
||||
below_trunk_provider,
|
||||
);
|
||||
TrunkPlacer::set_dirt(
|
||||
block_registry,
|
||||
chunk,
|
||||
random,
|
||||
&pos.south(),
|
||||
&below.south(),
|
||||
below_trunk_provider,
|
||||
);
|
||||
TrunkPlacer::set_dirt(
|
||||
block_registry,
|
||||
chunk,
|
||||
random,
|
||||
&pos.south().east(),
|
||||
&below.south().east(),
|
||||
below_trunk_provider,
|
||||
);
|
||||
|
||||
let mut trunk_poses = Vec::new();
|
||||
for y in 0..height {
|
||||
if TrunkPlacer::try_place(chunk, &pos.up_height(y as i32), trunk_block) {
|
||||
trunk_poses.push(pos.up_height(y as i32));
|
||||
let log_pos = start_pos.up_height(y as i32);
|
||||
if TrunkPlacer::try_place(chunk, &log_pos, trunk_block) {
|
||||
trunk_poses.push(log_pos);
|
||||
}
|
||||
if y >= height - 1 {
|
||||
continue;
|
||||
}
|
||||
|
||||
if TrunkPlacer::try_place(chunk, &pos.east().up_height(y as i32), trunk_block) {
|
||||
trunk_poses.push(pos.east().up_height(y as i32));
|
||||
let log_pos_east = start_pos.east().up_height(y as i32);
|
||||
if TrunkPlacer::try_place(chunk, &log_pos_east, trunk_block) {
|
||||
trunk_poses.push(log_pos_east);
|
||||
}
|
||||
if TrunkPlacer::try_place(chunk, &pos.east().south().up_height(y as i32), trunk_block) {
|
||||
trunk_poses.push(pos.east().south().up_height(y as i32));
|
||||
let log_pos_se = start_pos.east().south().up_height(y as i32);
|
||||
if TrunkPlacer::try_place(chunk, &log_pos_se, trunk_block) {
|
||||
trunk_poses.push(log_pos_se);
|
||||
}
|
||||
if TrunkPlacer::try_place(chunk, &pos.south().up_height(y as i32), trunk_block) {
|
||||
trunk_poses.push(pos.south().up_height(y as i32));
|
||||
let log_pos_south = start_pos.south().up_height(y as i32);
|
||||
if TrunkPlacer::try_place(chunk, &log_pos_south, trunk_block) {
|
||||
trunk_poses.push(log_pos_south);
|
||||
}
|
||||
}
|
||||
(
|
||||
|
||||
@@ -7,7 +7,10 @@ use crate::{
|
||||
chunk::{
|
||||
ChunkData, ChunkEntityData, ChunkReadingError,
|
||||
format::anvil::AnvilChunkFile,
|
||||
io::{Dirtiable, FileIO, LoadedData, file_manager::ChunkFileManager},
|
||||
io::{
|
||||
Dirtiable, FileIO, LoadedData,
|
||||
file_manager::{ChunkFileManager, LevelFileIO},
|
||||
},
|
||||
palette::has_random_ticking_fluid,
|
||||
},
|
||||
generation::get_world_gen,
|
||||
@@ -47,6 +50,15 @@ use tokio_util::task::TaskTracker;
|
||||
pub type SyncChunk = Arc<ChunkData>;
|
||||
pub type SyncEntityChunk = Arc<ChunkEntityData>;
|
||||
|
||||
pub type ChunkSaver =
|
||||
LevelFileIO<LinearV2File<ChunkData>, AnvilChunkFile<ChunkData>, PumpFile<ChunkData>>;
|
||||
|
||||
pub type EntitySaver = LevelFileIO<
|
||||
LinearV2File<ChunkEntityData>,
|
||||
AnvilChunkFile<ChunkEntityData>,
|
||||
PumpFile<ChunkEntityData>,
|
||||
>;
|
||||
|
||||
/// The `Level` module provides functionality for working with chunks within or outside a Minecraft world.
|
||||
///
|
||||
/// Key features include:
|
||||
@@ -74,8 +86,8 @@ pub struct Level {
|
||||
|
||||
chunk_watchers: Arc<DashMap<Vector2<i32>, usize>>,
|
||||
|
||||
pub chunk_saver: Arc<dyn FileIO<Data = SyncChunk>>,
|
||||
entity_saver: Arc<dyn FileIO<Data = SyncEntityChunk>>,
|
||||
pub chunk_saver: Arc<ChunkSaver>,
|
||||
entity_saver: Arc<EntitySaver>,
|
||||
|
||||
pub world_gen: Arc<WorldGenerator>,
|
||||
|
||||
@@ -216,21 +228,19 @@ impl Level {
|
||||
flat_biome,
|
||||
));
|
||||
|
||||
let chunk_saver: Arc<dyn FileIO<Data = SyncChunk>> = match &level_config.chunk {
|
||||
ChunkConfig::Linear => Arc::new(ChunkFileManager::<LinearV2File<ChunkData>>::new(())),
|
||||
ChunkConfig::Anvil(config) => Arc::new(
|
||||
ChunkFileManager::<AnvilChunkFile<ChunkData>>::new(config.clone()),
|
||||
),
|
||||
ChunkConfig::Pump => Arc::new(ChunkFileManager::<PumpFile<ChunkData>>::new(())),
|
||||
};
|
||||
let entity_saver: Arc<dyn FileIO<Data = SyncEntityChunk>> = match &level_config.chunk {
|
||||
ChunkConfig::Linear => {
|
||||
Arc::new(ChunkFileManager::<LinearV2File<ChunkEntityData>>::new(()))
|
||||
let chunk_saver = match &level_config.chunk {
|
||||
ChunkConfig::Linear => Arc::new(ChunkSaver::Linear(ChunkFileManager::new(()))),
|
||||
ChunkConfig::Anvil(config) => {
|
||||
Arc::new(ChunkSaver::Anvil(ChunkFileManager::new(config.clone())))
|
||||
}
|
||||
ChunkConfig::Anvil(config) => Arc::new(ChunkFileManager::<
|
||||
AnvilChunkFile<ChunkEntityData>,
|
||||
>::new(config.clone())),
|
||||
ChunkConfig::Pump => Arc::new(ChunkFileManager::<PumpFile<ChunkEntityData>>::new(())),
|
||||
ChunkConfig::Pump => Arc::new(ChunkSaver::Pump(ChunkFileManager::new(()))),
|
||||
};
|
||||
let entity_saver = match &level_config.chunk {
|
||||
ChunkConfig::Linear => Arc::new(EntitySaver::Linear(ChunkFileManager::new(()))),
|
||||
ChunkConfig::Anvil(config) => {
|
||||
Arc::new(EntitySaver::Anvil(ChunkFileManager::new(config.clone())))
|
||||
}
|
||||
ChunkConfig::Pump => Arc::new(EntitySaver::Pump(ChunkFileManager::new(()))),
|
||||
};
|
||||
|
||||
let pending_entity_generations = Arc::new(DashMap::new());
|
||||
|
||||
@@ -182,7 +182,9 @@ impl CommandExecutor for Executor {
|
||||
_args: &'a ConsumedArgs<'a>,
|
||||
) -> CommandResult<'a> {
|
||||
Box::pin(async move {
|
||||
let contributors = fetch_all_contributors();
|
||||
let contributors = tokio::task::spawn_blocking(fetch_all_contributors)
|
||||
.await
|
||||
.unwrap_or_default();
|
||||
let contributor_names = contributors
|
||||
.iter()
|
||||
.map(|c| c.login.as_str())
|
||||
@@ -282,7 +284,9 @@ impl CommandExecutor for Executor {
|
||||
|
||||
msg = msg.add_child(TextComponent::text(" "));
|
||||
|
||||
let donators_hover = fetch_donators_hover();
|
||||
let donators_hover = tokio::task::spawn_blocking(fetch_donators_hover)
|
||||
.await
|
||||
.unwrap_or_else(|_| TextComponent::text("Unable to load donators"));
|
||||
msg = msg.add_child(
|
||||
TextComponent::text("[Donate]")
|
||||
.click_event(ClickEvent::OpenUrl {
|
||||
|
||||
Reference in New Issue
Block a user