chore: some fixes
This commit is contained in:
parent
141b49f973
commit
98d4451fc9
42
pumpkin-codegen/src/chunk_view_lut.rs
Normal file
42
pumpkin-codegen/src/chunk_view_lut.rs
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
use proc_macro2::TokenStream;
|
||||||
|
use quote::quote;
|
||||||
|
|
||||||
|
const MAX_VIEW_DISTANCE: u8 = 32;
|
||||||
|
|
||||||
|
pub fn build() -> TokenStream {
|
||||||
|
let entries = (0..=MAX_VIEW_DISTANCE).map(|dist| {
|
||||||
|
if dist < 2 {
|
||||||
|
return quote! { &[] };
|
||||||
|
}
|
||||||
|
let mut positions = vec![];
|
||||||
|
let d = i64::from(dist);
|
||||||
|
|
||||||
|
for z in -(d + 2)..=(d + 2) {
|
||||||
|
for x in -(d + 2)..=(d + 2) {
|
||||||
|
let rel_x = (x.abs() - 2).max(0);
|
||||||
|
let rel_z = (z.abs() - 2).max(0);
|
||||||
|
if rel_x * rel_x + rel_z * rel_z < d * d {
|
||||||
|
positions.push((x as i8, z as i8));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
positions.sort_by_key(|&(x, z)| i32::from(x).pow(2) + i32::from(z).pow(2));
|
||||||
|
|
||||||
|
let array_elems = positions.into_iter().map(|(x, z)| quote!((#x, #z)));
|
||||||
|
|
||||||
|
quote! {
|
||||||
|
&[ #(#array_elems),* ]
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
let array_len = MAX_VIEW_DISTANCE as usize + 1;
|
||||||
|
|
||||||
|
quote! {
|
||||||
|
/// The maximum supported view distance
|
||||||
|
pub const MAX_VIEW_DISTANCE: u8 = #MAX_VIEW_DISTANCE;
|
||||||
|
|
||||||
|
/// Static precomputed lookup table for relative chunk offsets by view distance (0..=32).
|
||||||
|
pub static CHUNK_VIEW_LUT: [&[(i8, i8)]; #array_len] = [ #(#entries),* ];
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -18,6 +18,7 @@ mod block;
|
|||||||
mod carver;
|
mod carver;
|
||||||
pub mod chest_loot;
|
pub mod chest_loot;
|
||||||
mod chunk_gen_settings;
|
mod chunk_gen_settings;
|
||||||
|
mod chunk_view_lut;
|
||||||
mod chunk_status;
|
mod chunk_status;
|
||||||
mod composter_increase_chance;
|
mod composter_increase_chance;
|
||||||
mod configured_feature;
|
mod configured_feature;
|
||||||
@ -85,6 +86,7 @@ pub fn main() {
|
|||||||
(meta_data_type::build, "meta_data_type.rs"),
|
(meta_data_type::build, "meta_data_type.rs"),
|
||||||
(tracked_data::build, "tracked_data.rs"),
|
(tracked_data::build, "tracked_data.rs"),
|
||||||
(chunk_status::build, "chunk_status.rs"),
|
(chunk_status::build, "chunk_status.rs"),
|
||||||
|
(chunk_view_lut::build, "chunk_view_lut.rs"),
|
||||||
(game_event::build, "game_event.rs"),
|
(game_event::build, "game_event.rs"),
|
||||||
(game_rules::build, "game_rules.rs"),
|
(game_rules::build, "game_rules.rs"),
|
||||||
(registry::build, "registry.rs"),
|
(registry::build, "registry.rs"),
|
||||||
|
|||||||
44321
pumpkin-data/src/generated/chunk_view_lut.rs
Normal file
44321
pumpkin-data/src/generated/chunk_view_lut.rs
Normal file
File diff suppressed because it is too large
Load Diff
@ -1,5 +1,9 @@
|
|||||||
#![allow(unused_imports)]
|
#![allow(unused_imports)]
|
||||||
|
|
||||||
|
#[rustfmt::skip]
|
||||||
|
#[path = "generated/chunk_view_lut.rs"]
|
||||||
|
pub mod chunk_view_lut;
|
||||||
|
|
||||||
#[rustfmt::skip]
|
#[rustfmt::skip]
|
||||||
#[path = "generated/chest_loot.rs"]
|
#[path = "generated/chest_loot.rs"]
|
||||||
pub mod chest_loot_table;
|
pub mod chest_loot_table;
|
||||||
|
|||||||
@ -978,7 +978,6 @@ mod tests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
#[ignore]
|
|
||||||
fn no_fade_chunk() {
|
fn no_fade_chunk() {
|
||||||
let expected_data: Vec<(i32, i32, i32, f64)> =
|
let expected_data: Vec<(i32, i32, i32, f64)> =
|
||||||
read_data_from_file!("../../assets/tests/perlin_7_4.json");
|
read_data_from_file!("../../assets/tests/perlin_7_4.json");
|
||||||
@ -1013,7 +1012,6 @@ mod tests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
#[ignore]
|
|
||||||
fn map() {
|
fn map() {
|
||||||
let expected_data: Vec<i32> = read_data_from_file!("../../assets/tests/perlin_map.json");
|
let expected_data: Vec<i32> = read_data_from_file!("../../assets/tests/perlin_map.json");
|
||||||
let mut expected_iter = expected_data.iter();
|
let mut expected_iter = expected_data.iter();
|
||||||
|
|||||||
@ -93,7 +93,6 @@ mod test {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
#[ignore]
|
|
||||||
fn sample_multinoise_biome() {
|
fn sample_multinoise_biome() {
|
||||||
use crate::generation::generator::{GeneratorInit, VanillaGenerator};
|
use crate::generation::generator::{GeneratorInit, VanillaGenerator};
|
||||||
use pumpkin_util::world_seed::Seed;
|
use pumpkin_util::world_seed::Seed;
|
||||||
|
|||||||
@ -1,42 +1,14 @@
|
|||||||
use super::{ChunkLevel, ChunkPos, HashMapType, LevelChannel};
|
use super::{ChunkLevel, ChunkPos, HashMapType, LevelChannel};
|
||||||
use crate::chunk_system::chunk_state::StagedChunkEnum; // Fixed path
|
use crate::chunk_system::chunk_state::StagedChunkEnum; // Fixed path
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
use std::cmp::{Ordering, PartialEq, min};
|
use std::cmp::min;
|
||||||
use std::collections::BinaryHeap;
|
use std::collections::VecDeque;
|
||||||
use std::collections::hash_map::Entry;
|
use std::collections::hash_map::Entry;
|
||||||
use std::fmt::Write;
|
use std::fmt::Write;
|
||||||
use std::mem::swap;
|
use std::mem::swap;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use tracing::debug;
|
use tracing::debug;
|
||||||
|
|
||||||
pub struct HeapNode(i8, ChunkPos);
|
|
||||||
impl PartialEq for HeapNode {
|
|
||||||
fn eq(&self, other: &Self) -> bool {
|
|
||||||
self.0 == other.0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl Eq for HeapNode {}
|
|
||||||
impl PartialOrd for HeapNode {
|
|
||||||
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
|
|
||||||
Some(self.cmp(other))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl Ord for HeapNode {
|
|
||||||
fn cmp(&self, other: &Self) -> Ordering {
|
|
||||||
self.0.cmp(&other.0).reverse()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl From<(ChunkPos, i8)> for HeapNode {
|
|
||||||
fn from(value: (ChunkPos, i8)) -> Self {
|
|
||||||
Self(value.1, value.0)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl From<HeapNode> for (ChunkPos, i8) {
|
|
||||||
fn from(val: HeapNode) -> Self {
|
|
||||||
(val.1, val.0)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
struct LevelCache(i32, i32, usize, [(i8, i8); 256 * 256]);
|
struct LevelCache(i32, i32, usize, [(i8, i8); 256 * 256]);
|
||||||
|
|
||||||
#[expect(clippy::large_stack_arrays)]
|
#[expect(clippy::large_stack_arrays)]
|
||||||
@ -123,8 +95,8 @@ pub struct ChunkLoading {
|
|||||||
pub ticket: HashMapType<ChunkPos, Vec<i8>>, // TODO lifetime & id
|
pub ticket: HashMapType<ChunkPos, Vec<i8>>, // TODO lifetime & id
|
||||||
pub high_priority: Vec<ChunkPos>,
|
pub high_priority: Vec<ChunkPos>,
|
||||||
pub sender: Arc<LevelChannel>,
|
pub sender: Arc<LevelChannel>,
|
||||||
pub increase_update: BinaryHeap<HeapNode>,
|
pub increase_update: VecDeque<(ChunkPos, i8)>,
|
||||||
pub decrease_update: BinaryHeap<HeapNode>,
|
pub decrease_update: VecDeque<(ChunkPos, i8)>,
|
||||||
cache: LevelCache,
|
cache: LevelCache,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -207,6 +179,12 @@ impl ChunkLoading {
|
|||||||
Self::FULL_CHUNK_LEVEL - (view_distance as i8)
|
Self::FULL_CHUNK_LEVEL - (view_distance as i8)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
#[must_use]
|
||||||
|
pub const fn get_level_from_simulation_distance(simulation_distance: u8) -> i8 {
|
||||||
|
Self::FULL_CHUNK_LEVEL - (simulation_distance as i8)
|
||||||
|
}
|
||||||
|
|
||||||
pub fn new(sender: Arc<LevelChannel>) -> Self {
|
pub fn new(sender: Arc<LevelChannel>) -> Self {
|
||||||
Self {
|
Self {
|
||||||
is_priority_dirty: true,
|
is_priority_dirty: true,
|
||||||
@ -215,8 +193,8 @@ impl ChunkLoading {
|
|||||||
ticket: HashMapType::default(),
|
ticket: HashMapType::default(),
|
||||||
high_priority: Vec::new(),
|
high_priority: Vec::new(),
|
||||||
sender,
|
sender,
|
||||||
increase_update: BinaryHeap::default(),
|
increase_update: VecDeque::default(),
|
||||||
decrease_update: BinaryHeap::default(),
|
decrease_update: VecDeque::default(),
|
||||||
cache: LevelCache::new(),
|
cache: LevelCache::new(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -241,8 +219,7 @@ impl ChunkLoading {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn run_increase_update(&mut self) {
|
fn run_increase_update(&mut self) {
|
||||||
while let Some(node) = self.increase_update.pop() {
|
while let Some((pos, level)) = self.increase_update.pop_front() {
|
||||||
let (pos, level) = node.into();
|
|
||||||
debug_assert!(level < Self::MAX_LEVEL);
|
debug_assert!(level < Self::MAX_LEVEL);
|
||||||
if level > self.cache.get(&self.pos_level, pos) {
|
if level > self.cache.get(&self.pos_level, pos) {
|
||||||
continue;
|
continue;
|
||||||
@ -270,12 +247,11 @@ impl ChunkLoading {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
self.cache.set(&self.pos_level, pos, level);
|
self.cache.set(&self.pos_level, pos, level);
|
||||||
self.increase_update.push((pos, level).into());
|
self.increase_update.push_back((pos, level));
|
||||||
}
|
}
|
||||||
|
|
||||||
fn run_decrease_update(&mut self, pos: ChunkPos, range: i32) {
|
fn run_decrease_update(&mut self, pos: ChunkPos, range: i32) {
|
||||||
while let Some(node) = self.decrease_update.pop() {
|
while let Some((pos, level)) = self.decrease_update.pop_front() {
|
||||||
let (pos, level) = node.into();
|
|
||||||
debug_assert!(level < Self::MAX_LEVEL);
|
debug_assert!(level < Self::MAX_LEVEL);
|
||||||
let spread_level = level + 1;
|
let spread_level = level + 1;
|
||||||
for dx in -1..2 {
|
for dx in -1..2 {
|
||||||
@ -292,10 +268,10 @@ impl ChunkLoading {
|
|||||||
if new_pos_level == spread_level {
|
if new_pos_level == spread_level {
|
||||||
self.cache.set(&self.pos_level, new_pos, Self::MAX_LEVEL);
|
self.cache.set(&self.pos_level, new_pos, Self::MAX_LEVEL);
|
||||||
if spread_level < Self::MAX_LEVEL {
|
if spread_level < Self::MAX_LEVEL {
|
||||||
self.decrease_update.push((new_pos, spread_level).into());
|
self.decrease_update.push_back((new_pos, spread_level));
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
self.increase_update.push((new_pos, new_pos_level).into());
|
self.increase_update.push_back((new_pos, new_pos_level));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -310,7 +286,7 @@ impl ChunkLoading {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
self.cache.set(&self.pos_level, *ticket_pos, level);
|
self.cache.set(&self.pos_level, *ticket_pos, level);
|
||||||
self.increase_update.push((*ticket_pos, level).into());
|
self.increase_update.push_back((*ticket_pos, level));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
self.run_increase_update();
|
self.run_increase_update();
|
||||||
@ -354,7 +330,7 @@ impl ChunkLoading {
|
|||||||
self.cache.set(&self.pos_level, pos, level);
|
self.cache.set(&self.pos_level, pos, level);
|
||||||
|
|
||||||
debug_assert!(self.increase_update.is_empty());
|
debug_assert!(self.increase_update.is_empty());
|
||||||
self.increase_update.push((pos, level).into());
|
self.increase_update.push_back((pos, level));
|
||||||
self.run_increase_update();
|
self.run_increase_update();
|
||||||
self.cache.write(&mut self.pos_level, &mut self.change);
|
self.cache.write(&mut self.pos_level, &mut self.change);
|
||||||
debug_assert!(self.debug_check_error());
|
debug_assert!(self.debug_check_error());
|
||||||
@ -382,7 +358,7 @@ impl ChunkLoading {
|
|||||||
self.cache.clean(pos, old_level);
|
self.cache.clean(pos, old_level);
|
||||||
self.cache.set(&self.pos_level, pos, Self::MAX_LEVEL);
|
self.cache.set(&self.pos_level, pos, Self::MAX_LEVEL);
|
||||||
debug_assert!(self.decrease_update.is_empty());
|
debug_assert!(self.decrease_update.is_empty());
|
||||||
self.decrease_update.push((pos, level).into());
|
self.decrease_update.push_back((pos, level));
|
||||||
self.run_decrease_update(pos, (Self::MAX_LEVEL - level - 1) as i32);
|
self.run_decrease_update(pos, (Self::MAX_LEVEL - level - 1) as i32);
|
||||||
self.cache.write(&mut self.pos_level, &mut self.change);
|
self.cache.write(&mut self.pos_level, &mut self.change);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -38,18 +38,22 @@ impl Cylindrical {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
const fn left(&self) -> i32 {
|
const fn left(&self) -> i32 {
|
||||||
self.center.x - self.view_distance.get() as i32 - 1
|
self.center.x - self.view_distance.get() as i32 - 1
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
const fn bottom(&self) -> i32 {
|
const fn bottom(&self) -> i32 {
|
||||||
self.center.y - self.view_distance.get() as i32 - 1
|
self.center.y - self.view_distance.get() as i32 - 1
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
const fn right(&self) -> i32 {
|
const fn right(&self) -> i32 {
|
||||||
self.center.x + self.view_distance.get() as i32 + 1
|
self.center.x + self.view_distance.get() as i32 + 1
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
const fn top(&self) -> i32 {
|
const fn top(&self) -> i32 {
|
||||||
self.center.y + self.view_distance.get() as i32 + 1
|
self.center.y + self.view_distance.get() as i32 + 1
|
||||||
}
|
}
|
||||||
@ -67,32 +71,22 @@ impl Cylindrical {
|
|||||||
hyp_sqr < (self.view_distance.get() as i64).pow(2)
|
hyp_sqr < (self.view_distance.get() as i64).pow(2)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns an iterator of all chunks within this cylinder
|
/// Returns a precomputed list of relative chunk offset pairs `(dx, dy)` for a given view distance.
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn all_chunks_within(&self) -> Vec<Vector2<i32>> {
|
#[inline]
|
||||||
if self.view_distance.get() == 1 {
|
pub fn get_offsets(view_distance: u8) -> &'static [(i8, i8)] {
|
||||||
return Vec::new();
|
let idx =
|
||||||
}
|
(view_distance as usize).min(pumpkin_data::chunk_view_lut::MAX_VIEW_DISTANCE as usize);
|
||||||
// I came up with this values by testing
|
pumpkin_data::chunk_view_lut::CHUNK_VIEW_LUT[idx]
|
||||||
// for view distances 2-32 it usually gives 5 - 20 chunks more than needed if the player is on ground
|
|
||||||
// this looks scary but this few calculations are definitely faster than ~5 reallocations
|
|
||||||
// this part "3167) >> 10" is a replacement for flointing point multiplication
|
|
||||||
let estimated_capacity = ((self.view_distance.get() as usize + 3).pow(2) * 3167) >> 10;
|
|
||||||
let mut all_chunks = Vec::with_capacity(estimated_capacity);
|
|
||||||
|
|
||||||
for x in self.left()..=self.right() {
|
|
||||||
let mut was_in = false;
|
|
||||||
'inner: for z in self.bottom()..=self.top() {
|
|
||||||
if self.is_within_distance(x, z) {
|
|
||||||
all_chunks.push(Vector2::new(x, z));
|
|
||||||
was_in = true;
|
|
||||||
} else if was_in {
|
|
||||||
break 'inner;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
all_chunks
|
/// Returns an iterator of all chunks within this cylinder using the precomputed LUT
|
||||||
|
#[must_use]
|
||||||
|
pub fn all_chunks_within(self) -> impl ExactSizeIterator<Item = Vector2<i32>> {
|
||||||
|
let offsets = Self::get_offsets(self.view_distance.get());
|
||||||
|
offsets.iter().map(move |&(dx, dy)| {
|
||||||
|
Vector2::new(self.center.x + i32::from(dx), self.center.y + i32::from(dy))
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -114,6 +114,7 @@ macro_rules! sample_surface_router_function {
|
|||||||
//
|
//
|
||||||
// This is equivalent to a Java `NoiseRouter` after being passed into `NoiseConfig` but before being
|
// This is equivalent to a Java `NoiseRouter` after being passed into `NoiseConfig` but before being
|
||||||
// passed into `ChunkNoiseGenerator`
|
// passed into `ChunkNoiseGenerator`
|
||||||
|
#[expect(clippy::too_many_lines)]
|
||||||
fn normal_surface_noisified() {
|
fn normal_surface_noisified() {
|
||||||
let pos = Vector3 { x: 0, y: 0, z: 0 };
|
let pos = Vector3 { x: 0, y: 0, z: 0 };
|
||||||
// TODO: Move these values to a file and create an extractor for them
|
// TODO: Move these values to a file and create an extractor for them
|
||||||
|
|||||||
@ -172,6 +172,13 @@ impl StructurePieceBase for EndCityPiece {
|
|||||||
&[],
|
&[],
|
||||||
Some(chunk_box),
|
Some(chunk_box),
|
||||||
);
|
);
|
||||||
|
crate::generation::structure::template::place_template_entities(
|
||||||
|
chunk,
|
||||||
|
&self.ship,
|
||||||
|
ship_pos,
|
||||||
|
self.rotation,
|
||||||
|
chunk_box,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -182,6 +182,13 @@ impl StructurePieceBase for IglooPiece {
|
|||||||
&[],
|
&[],
|
||||||
Some(chunk_box),
|
Some(chunk_box),
|
||||||
);
|
);
|
||||||
|
crate::generation::structure::template::place_template_entities(
|
||||||
|
chunk,
|
||||||
|
&self.top_template,
|
||||||
|
origin,
|
||||||
|
self.rotation,
|
||||||
|
chunk_box,
|
||||||
|
);
|
||||||
|
|
||||||
// Place basement components if present
|
// Place basement components if present
|
||||||
if self.has_basement {
|
if self.has_basement {
|
||||||
@ -233,6 +240,21 @@ impl StructurePieceBase for IglooPiece {
|
|||||||
&[],
|
&[],
|
||||||
Some(chunk_box),
|
Some(chunk_box),
|
||||||
);
|
);
|
||||||
|
let (rotated_ox, rotated_oz) = self
|
||||||
|
.rotation
|
||||||
|
.rotate_offset(BASEMENT_OFFSET_X, BASEMENT_OFFSET_Z);
|
||||||
|
let placement_origin = Vector3::new(
|
||||||
|
basement_origin.x + rotated_ox,
|
||||||
|
basement_origin.y,
|
||||||
|
basement_origin.z + rotated_oz,
|
||||||
|
);
|
||||||
|
crate::generation::structure::template::place_template_entities(
|
||||||
|
chunk,
|
||||||
|
bottom,
|
||||||
|
placement_origin,
|
||||||
|
self.rotation,
|
||||||
|
chunk_box,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -497,7 +497,7 @@ impl StructurePieceBase for PoolElementStructurePiece {
|
|||||||
pumpkin_util::math::vector3::Vector3::new(self.pos.0.x, self.pos.0.y, self.pos.0.z);
|
pumpkin_util::math::vector3::Vector3::new(self.pos.0.x, self.pos.0.y, self.pos.0.z);
|
||||||
|
|
||||||
self.element
|
self.element
|
||||||
.for_each_template(|name, processor_list, legacy, template| {
|
.for_each_template(|_name, processor_list, legacy, template| {
|
||||||
let corner = self.rotation.rotate_offset(
|
let corner = self.rotation.rotate_offset(
|
||||||
template.size.x.saturating_sub(1),
|
template.size.x.saturating_sub(1),
|
||||||
template.size.z.saturating_sub(1),
|
template.size.z.saturating_sub(1),
|
||||||
@ -524,7 +524,6 @@ impl StructurePieceBase for PoolElementStructurePiece {
|
|||||||
processors.as_ref(),
|
processors.as_ref(),
|
||||||
Some(chunk_box),
|
Some(chunk_box),
|
||||||
);
|
);
|
||||||
if name.starts_with("minecraft:pillager_outpost/") {
|
|
||||||
crate::generation::structure::template::place_template_entities(
|
crate::generation::structure::template::place_template_entities(
|
||||||
chunk,
|
chunk,
|
||||||
&template,
|
&template,
|
||||||
@ -532,7 +531,6 @@ impl StructurePieceBase for PoolElementStructurePiece {
|
|||||||
self.rotation,
|
self.rotation,
|
||||||
chunk_box,
|
chunk_box,
|
||||||
);
|
);
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
if let Some(feature) = self.element.feature()
|
if let Some(feature) = self.element.feature()
|
||||||
@ -683,7 +681,7 @@ impl StructureGenerator for JigsawGenerator {
|
|||||||
&MaxDistance::new(max_distance),
|
&MaxDistance::new(max_distance),
|
||||||
&dimension_padding,
|
&dimension_padding,
|
||||||
liquid_settings,
|
liquid_settings,
|
||||||
&PoolAliasLookup,
|
&PoolAliasLookup::default(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -52,16 +52,57 @@ pub const MAX_TOTAL_STRUCTURE_RANGE: i32 = 128;
|
|||||||
pub const MIN_DEPTH: i32 = 0;
|
pub const MIN_DEPTH: i32 = 0;
|
||||||
pub const MAX_DEPTH: i32 = 20;
|
pub const MAX_DEPTH: i32 = 20;
|
||||||
|
|
||||||
/// Simple lookup for Pool Aliases introduced in 1.20+
|
/// Dynamic lookup for Pool Aliases introduced in 1.20+ (e.g. Trial Chambers spawner/contents aliases).
|
||||||
pub struct PoolAliasLookup;
|
#[derive(Debug, Clone, Default)]
|
||||||
|
pub struct PoolAliasLookup {
|
||||||
|
aliases: std::collections::HashMap<String, Vec<(String, u32)>>,
|
||||||
|
}
|
||||||
|
|
||||||
impl PoolAliasLookup {
|
impl PoolAliasLookup {
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub const fn lookup<'a>(&self, id: &'a str) -> &'a str {
|
pub fn new() -> Self {
|
||||||
// In a complete implementation, this would look up the alias in the context/registry.
|
Self {
|
||||||
// Returning the ID directly acts as a fallback/default behavior.
|
aliases: std::collections::HashMap::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn add_direct(&mut self, alias: String, target: String) {
|
||||||
|
self.aliases.insert(alias, vec![(target, 1)]);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn add_random(&mut self, alias: String, targets: Vec<(String, u32)>) {
|
||||||
|
self.aliases.insert(alias, targets);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
|
pub fn lookup<'a>(
|
||||||
|
&'a self,
|
||||||
|
id: &'a str,
|
||||||
|
random: &mut pumpkin_util::random::RandomGenerator,
|
||||||
|
) -> &'a str {
|
||||||
|
if let Some(targets) = self.aliases.get(id) {
|
||||||
|
if targets.is_empty() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
if targets.len() == 1 {
|
||||||
|
return &targets[0].0;
|
||||||
|
}
|
||||||
|
let total_weight: u32 = targets.iter().map(|(_, w)| *w).sum();
|
||||||
|
if total_weight == 0 {
|
||||||
|
return &targets[0].0;
|
||||||
|
}
|
||||||
|
let mut r = random.next_bounded_i32(total_weight as i32) as u32;
|
||||||
|
for (target, weight) in targets {
|
||||||
|
if r < *weight {
|
||||||
|
return target.as_str();
|
||||||
|
}
|
||||||
|
r -= *weight;
|
||||||
|
}
|
||||||
|
&targets[0].0
|
||||||
|
} else {
|
||||||
id
|
id
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl JigsawPlacement {
|
impl JigsawPlacement {
|
||||||
@ -86,7 +127,7 @@ impl JigsawPlacement {
|
|||||||
|
|
||||||
let max_depth = max_depth.clamp(MIN_DEPTH, MAX_DEPTH);
|
let max_depth = max_depth.clamp(MIN_DEPTH, MAX_DEPTH);
|
||||||
|
|
||||||
let actual_start_pool_id = pool_alias_lookup.lookup(start_pool_id);
|
let actual_start_pool_id = pool_alias_lookup.lookup(start_pool_id, &mut context.random);
|
||||||
let pool = TemplatePool::discover(actual_start_pool_id)?;
|
let pool = TemplatePool::discover(actual_start_pool_id)?;
|
||||||
let rotation = Rotation::from_index(context.random.next_bounded_i32(4) as u8);
|
let rotation = Rotation::from_index(context.random.next_bounded_i32(4) as u8);
|
||||||
let element = pool.get_random_element(&mut context.random).clone();
|
let element = pool.get_random_element(&mut context.random).clone();
|
||||||
@ -235,7 +276,7 @@ impl JigsawPlacement {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
let target_pool_id = pool_alias_lookup.lookup(raw_pool_id);
|
let target_pool_id = pool_alias_lookup.lookup(raw_pool_id, &mut context.random);
|
||||||
let Some(target_pool) = TemplatePool::discover(target_pool_id) else {
|
let Some(target_pool) = TemplatePool::discover(target_pool_id) else {
|
||||||
continue;
|
continue;
|
||||||
};
|
};
|
||||||
@ -246,7 +287,8 @@ impl JigsawPlacement {
|
|||||||
.extend(target_pool.get_shuffled_elements(&mut context.random));
|
.extend(target_pool.get_shuffled_elements(&mut context.random));
|
||||||
}
|
}
|
||||||
|
|
||||||
let fallback_pool_id = pool_alias_lookup.lookup(&target_pool.fallback);
|
let fallback_pool_id =
|
||||||
|
pool_alias_lookup.lookup(&target_pool.fallback, &mut context.random);
|
||||||
if let Some(fallback_pool) = TemplatePool::discover(fallback_pool_id) {
|
if let Some(fallback_pool) = TemplatePool::discover(fallback_pool_id) {
|
||||||
target_elements
|
target_elements
|
||||||
.extend(fallback_pool.get_shuffled_elements(&mut context.random));
|
.extend(fallback_pool.get_shuffled_elements(&mut context.random));
|
||||||
@ -361,7 +403,8 @@ impl JigsawPlacement {
|
|||||||
rotated_tj_target_pos.y,
|
rotated_tj_target_pos.y,
|
||||||
rotated_tj_target_pos.z,
|
rotated_tj_target_pos.z,
|
||||||
) {
|
) {
|
||||||
let child_pool_id = pool_alias_lookup.lookup(&tj.pool);
|
let child_pool_id = pool_alias_lookup
|
||||||
|
.lookup(&tj.pool, &mut context.random);
|
||||||
let child_pool_max_y =
|
let child_pool_max_y =
|
||||||
get_pool_max_y_size(child_pool_id);
|
get_pool_max_y_size(child_pool_id);
|
||||||
|
|
||||||
@ -369,7 +412,8 @@ impl JigsawPlacement {
|
|||||||
TemplatePool::discover(child_pool_id)
|
TemplatePool::discover(child_pool_id)
|
||||||
{
|
{
|
||||||
get_pool_max_y_size(
|
get_pool_max_y_size(
|
||||||
pool_alias_lookup.lookup(&cp.fallback),
|
pool_alias_lookup
|
||||||
|
.lookup(&cp.fallback, &mut context.random),
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
0
|
0
|
||||||
|
|||||||
@ -444,10 +444,14 @@ impl Level {
|
|||||||
|
|
||||||
/// Marks chunks no longer "watched" by a unique player. When no players are watching a chunk,
|
/// Marks chunks no longer "watched" by a unique player. When no players are watching a chunk,
|
||||||
/// it is removed from memory. Should only be called on chunks the player was watching before
|
/// it is removed from memory. Should only be called on chunks the player was watching before
|
||||||
pub async fn mark_chunks_as_not_watched(&self, chunks: &[Vector2<i32>]) -> Vec<Vector2<i32>> {
|
pub async fn mark_chunks_as_not_watched(
|
||||||
|
&self,
|
||||||
|
chunks: impl IntoIterator<Item = impl std::borrow::Borrow<Vector2<i32>>>,
|
||||||
|
) -> Vec<Vector2<i32>> {
|
||||||
let mut chunks_to_clean = Vec::new();
|
let mut chunks_to_clean = Vec::new();
|
||||||
|
let chunks_vec: Vec<Vector2<i32>> = chunks.into_iter().map(|c| *c.borrow()).collect();
|
||||||
|
|
||||||
for chunk in chunks {
|
for chunk in &chunks_vec {
|
||||||
if let Entry::Occupied(mut entry) = self.chunk_watchers.entry(*chunk) {
|
if let Entry::Occupied(mut entry) = self.chunk_watchers.entry(*chunk) {
|
||||||
*entry.get_mut() = entry.get().saturating_sub(1);
|
*entry.get_mut() = entry.get().saturating_sub(1);
|
||||||
if *entry.get() == 0 {
|
if *entry.get() == 0 {
|
||||||
@ -458,7 +462,7 @@ impl Level {
|
|||||||
}
|
}
|
||||||
|
|
||||||
self.entity_saver
|
self.entity_saver
|
||||||
.unwatch_chunks(&self.level_folder, chunks)
|
.unwatch_chunks(&self.level_folder, &chunks_vec)
|
||||||
.await;
|
.await;
|
||||||
chunks_to_clean
|
chunks_to_clean
|
||||||
}
|
}
|
||||||
@ -466,14 +470,18 @@ impl Level {
|
|||||||
/// Returns whether the chunk should be removed from memory
|
/// Returns whether the chunk should be removed from memory
|
||||||
#[inline]
|
#[inline]
|
||||||
pub async fn mark_chunk_as_not_watched(&self, chunk: Vector2<i32>) -> bool {
|
pub async fn mark_chunk_as_not_watched(&self, chunk: Vector2<i32>) -> bool {
|
||||||
!self.mark_chunks_as_not_watched(&[chunk]).await.is_empty()
|
!self.mark_chunks_as_not_watched([chunk]).await.is_empty()
|
||||||
}
|
}
|
||||||
|
|
||||||
// In Level::clean_entity_chunks()
|
// In Level::clean_entity_chunks()
|
||||||
pub fn clean_entity_chunks(self: &Arc<Self>, chunks: &[Vector2<i32>]) {
|
pub fn clean_entity_chunks(
|
||||||
|
self: &Arc<Self>,
|
||||||
|
chunks: impl IntoIterator<Item = impl std::borrow::Borrow<Vector2<i32>>>,
|
||||||
|
) {
|
||||||
let chunks_to_process: Vec<_> = chunks
|
let chunks_to_process: Vec<_> = chunks
|
||||||
.iter()
|
.into_iter()
|
||||||
.filter_map(|pos| {
|
.filter_map(|pos_borrow| {
|
||||||
|
let pos = pos_borrow.borrow();
|
||||||
// Only include chunks with no watchers
|
// Only include chunks with no watchers
|
||||||
let has_watchers = self
|
let has_watchers = self
|
||||||
.chunk_watchers
|
.chunk_watchers
|
||||||
@ -581,7 +589,7 @@ impl Level {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn clean_entity_chunk(self: &Arc<Self>, chunk: &Vector2<i32>) {
|
pub fn clean_entity_chunk(self: &Arc<Self>, chunk: &Vector2<i32>) {
|
||||||
self.clean_entity_chunks(&[*chunk]);
|
self.clean_entity_chunks([*chunk]);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn is_chunk_watched(&self, chunk: &Vector2<i32>) -> bool {
|
pub fn is_chunk_watched(&self, chunk: &Vector2<i32>) -> bool {
|
||||||
|
|||||||
@ -230,23 +230,15 @@ pub struct LevelDat {
|
|||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {
|
||||||
|
|
||||||
use flate2::read::GzDecoder;
|
|
||||||
use pumpkin_data::game_rules::GameRuleRegistry;
|
use pumpkin_data::game_rules::GameRuleRegistry;
|
||||||
use pumpkin_nbt::{deserializer::from_bytes, serializer::to_bytes};
|
use pumpkin_nbt::{deserializer::from_bytes, serializer::to_bytes};
|
||||||
use pumpkin_util::{Difficulty, world_seed::Seed};
|
use pumpkin_util::{Difficulty, world_seed::Seed};
|
||||||
use std::{
|
use std::{io::Cursor, sync::LazyLock};
|
||||||
fs,
|
|
||||||
io::{Cursor, Read},
|
|
||||||
sync::LazyLock,
|
|
||||||
};
|
|
||||||
use tempfile::TempDir;
|
use tempfile::TempDir;
|
||||||
|
|
||||||
use crate::{
|
use crate::world_info::{DataPacks, LevelData, WorldGenSettings, WorldVersion};
|
||||||
global_path,
|
|
||||||
world_info::{DataPacks, LevelData, WorldGenSettings, WorldInfoError, WorldVersion},
|
|
||||||
};
|
|
||||||
|
|
||||||
use super::{AnvilLevelInfo, LEVEL_DAT_FILE_NAME, LevelDat, WorldInfoReader, WorldInfoWriter};
|
use super::{AnvilLevelInfo, LevelDat, WorldInfoReader, WorldInfoWriter};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn preserve_level_dat_seed() {
|
fn preserve_level_dat_seed() {
|
||||||
|
|||||||
@ -93,6 +93,11 @@ impl<'a> PistonHandler<'a> {
|
|||||||
Self::is_block_sticky(state) || Self::is_block_sticky(adjacent_state)
|
Self::is_block_sticky(state) || Self::is_block_sticky(adjacent_state)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn is_piston_head_or_base(&self, pos: BlockPos) -> bool {
|
||||||
|
pos == self.pos_from
|
||||||
|
|| (!self.retracted && pos == self.pos_from.offset(self.piston_direction.to_offset()))
|
||||||
|
}
|
||||||
|
|
||||||
async fn try_move(&mut self, pos: BlockPos, dir: BlockDirection) -> bool {
|
async fn try_move(&mut self, pos: BlockPos, dir: BlockDirection) -> bool {
|
||||||
let (mut block, block_state) = self.world.get_block_and_state(&pos);
|
let (mut block, block_state) = self.world.get_block_and_state(&pos);
|
||||||
if block_state.is_air() {
|
if block_state.is_air() {
|
||||||
@ -101,7 +106,7 @@ impl<'a> PistonHandler<'a> {
|
|||||||
if !PistonBlock::is_movable(block, block_state, self.motion_direction, false, dir) {
|
if !PistonBlock::is_movable(block, block_state, self.motion_direction, false, dir) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if pos == self.pos_from {
|
if self.is_piston_head_or_base(pos) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if self.moved_blocks.contains(&pos) {
|
if self.moved_blocks.contains(&pos) {
|
||||||
@ -124,7 +129,7 @@ impl<'a> PistonHandler<'a> {
|
|||||||
false,
|
false,
|
||||||
self.motion_direction.opposite(),
|
self.motion_direction.opposite(),
|
||||||
)
|
)
|
||||||
|| block_pos == self.pos_from
|
|| self.is_piston_head_or_base(block_pos)
|
||||||
{
|
{
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -157,7 +162,10 @@ impl<'a> PistonHandler<'a> {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
let (block, block_state) = self.world.get_block_and_state(&block_pos2);
|
let (block, block_state) = self.world.get_block_and_state(&block_pos2);
|
||||||
if block_state.is_air() {
|
if block_state.is_air()
|
||||||
|
|| (!self.retracted
|
||||||
|
&& block_pos2 == self.pos_from.offset(self.piston_direction.to_offset()))
|
||||||
|
{
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if !PistonBlock::is_movable(
|
if !PistonBlock::is_movable(
|
||||||
@ -166,7 +174,7 @@ impl<'a> PistonHandler<'a> {
|
|||||||
self.motion_direction,
|
self.motion_direction,
|
||||||
true,
|
true,
|
||||||
self.motion_direction,
|
self.motion_direction,
|
||||||
) || block_pos2 == self.pos_from
|
) || self.is_piston_head_or_base(block_pos2)
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -65,7 +65,12 @@ impl BlockBehaviour for ObserverBlock {
|
|||||||
Box::pin(async move {
|
Box::pin(async move {
|
||||||
let props = ObserverLikeProperties::from_state_id(args.state_id, args.block);
|
let props = ObserverLikeProperties::from_state_id(args.state_id, args.block);
|
||||||
|
|
||||||
if props.facing.to_block_direction() == args.direction && !props.powered {
|
if props.facing.to_block_direction() == args.direction
|
||||||
|
&& !props.powered
|
||||||
|
&& !args
|
||||||
|
.world
|
||||||
|
.is_block_tick_scheduled(args.position, &Block::OBSERVER)
|
||||||
|
{
|
||||||
Self::schedule_tick(args.world, args.position);
|
Self::schedule_tick(args.world, args.position);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -75,12 +80,9 @@ impl BlockBehaviour for ObserverBlock {
|
|||||||
|
|
||||||
fn emits_redstone_power<'a>(
|
fn emits_redstone_power<'a>(
|
||||||
&'a self,
|
&'a self,
|
||||||
args: EmitsRedstonePowerArgs<'a>,
|
_args: EmitsRedstonePowerArgs<'a>,
|
||||||
) -> BlockFuture<'a, bool> {
|
) -> BlockFuture<'a, bool> {
|
||||||
Box::pin(async move {
|
Box::pin(async move { true })
|
||||||
let props = ObserverLikeProperties::from_state_id(args.state.id, args.block);
|
|
||||||
props.facing.to_block_direction() == args.direction
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_weak_redstone_power<'a>(
|
fn get_weak_redstone_power<'a>(
|
||||||
@ -127,12 +129,12 @@ impl ObserverBlock {
|
|||||||
block_pos: &BlockPos,
|
block_pos: &BlockPos,
|
||||||
props: &ObserverLikeProperties,
|
props: &ObserverLikeProperties,
|
||||||
) {
|
) {
|
||||||
let facing = props.facing;
|
let facing = props.facing.to_block_direction();
|
||||||
let opposite_facing_pos =
|
let opposite_facing_pos = block_pos.offset(facing.opposite().to_offset());
|
||||||
block_pos.offset(facing.to_block_direction().opposite().to_offset());
|
|
||||||
world.update_neighbor(&opposite_facing_pos, block).await;
|
world.update_neighbor(&opposite_facing_pos, block).await;
|
||||||
world
|
world
|
||||||
.update_neighbors(&opposite_facing_pos, Some(facing.to_block_direction()))
|
.update_neighbors(&opposite_facing_pos, Some(facing))
|
||||||
.await;
|
.await;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -95,7 +95,7 @@ impl JigsawBlockEntity {
|
|||||||
&MaxDistance::new(80),
|
&MaxDistance::new(80),
|
||||||
&DimensionPadding::ZERO,
|
&DimensionPadding::ZERO,
|
||||||
LiquidSettings::ApplyWaterlog,
|
LiquidSettings::ApplyWaterlog,
|
||||||
&PoolAliasLookup,
|
&PoolAliasLookup::default(),
|
||||||
)
|
)
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -208,9 +208,7 @@ impl PistonBlockEntity {
|
|||||||
.clone()
|
.clone()
|
||||||
.set_block_state(&pos, state, BlockFlags::NOTIFY_ALL)
|
.set_block_state(&pos, state, BlockFlags::NOTIFY_ALL)
|
||||||
.await;
|
.await;
|
||||||
world
|
world.update_neighbors(&pos, None).await;
|
||||||
.update_neighbor(&pos, Block::from_state_id(state))
|
|
||||||
.await;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -260,10 +258,7 @@ impl BlockEntity for PistonBlockEntity {
|
|||||||
BlockFlags::NOTIFY_ALL | BlockFlags::MOVED,
|
BlockFlags::NOTIFY_ALL | BlockFlags::MOVED,
|
||||||
)
|
)
|
||||||
.await;
|
.await;
|
||||||
world
|
world.clone().update_neighbors(&pos, None).await;
|
||||||
.clone()
|
|
||||||
.update_neighbor(&pos, Block::from_state_id(updated_state))
|
|
||||||
.await;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
|
|||||||
@ -225,7 +225,7 @@ impl CommandExecutor for PlaceJigsawExecutor {
|
|||||||
&MaxDistance::new(128),
|
&MaxDistance::new(128),
|
||||||
&DimensionPadding::ZERO,
|
&DimensionPadding::ZERO,
|
||||||
LiquidSettings::ApplyWaterlog,
|
LiquidSettings::ApplyWaterlog,
|
||||||
&PoolAliasLookup,
|
&PoolAliasLookup::default(),
|
||||||
)
|
)
|
||||||
.ok_or_else(|| {
|
.ok_or_else(|| {
|
||||||
JIGSAW_FAILED.create_without_context(TextComponent::text(pool.clone()))
|
JIGSAW_FAILED.create_without_context(TextComponent::text(pool.clone()))
|
||||||
@ -334,7 +334,7 @@ impl CommandExecutor for PlaceStructureExecutor {
|
|||||||
&MaxDistance::new(structure.max_distance_from_center.unwrap_or(128)),
|
&MaxDistance::new(structure.max_distance_from_center.unwrap_or(128)),
|
||||||
&DimensionPadding::ZERO,
|
&DimensionPadding::ZERO,
|
||||||
LiquidSettings::ApplyWaterlog,
|
LiquidSettings::ApplyWaterlog,
|
||||||
&PoolAliasLookup,
|
&PoolAliasLookup::default(),
|
||||||
)
|
)
|
||||||
.ok_or_else(|| {
|
.ok_or_else(|| {
|
||||||
JIGSAW_FAILED
|
JIGSAW_FAILED
|
||||||
|
|||||||
@ -254,10 +254,17 @@ impl ChunkManager {
|
|||||||
let new_level = ChunkLoading::get_level_from_view_distance(view_distance);
|
let new_level = ChunkLoading::get_level_from_view_distance(view_distance);
|
||||||
lock.add_ticket(center, new_level);
|
lock.add_ticket(center, new_level);
|
||||||
|
|
||||||
|
let sim_dist = self.world.server.upgrade().map_or(10, |s| {
|
||||||
|
s.advanced_config.networking.java.simulation_distance.get()
|
||||||
|
});
|
||||||
|
let sim_level = ChunkLoading::get_level_from_simulation_distance(sim_dist);
|
||||||
|
lock.add_ticket(center, sim_level);
|
||||||
|
|
||||||
if (old_center != center || old_view_distance != view_distance) && old_view_distance > 0
|
if (old_center != center || old_view_distance != view_distance) && old_view_distance > 0
|
||||||
{
|
{
|
||||||
let old_level = ChunkLoading::get_level_from_view_distance(old_view_distance);
|
let old_level = ChunkLoading::get_level_from_view_distance(old_view_distance);
|
||||||
lock.remove_ticket(old_center, old_level);
|
lock.remove_ticket(old_center, old_level);
|
||||||
|
lock.remove_ticket(old_center, sim_level);
|
||||||
}
|
}
|
||||||
lock.send_change();
|
lock.send_change();
|
||||||
};
|
};
|
||||||
@ -891,7 +898,7 @@ impl Player {
|
|||||||
let level = &world.level;
|
let level = &world.level;
|
||||||
|
|
||||||
// Decrement the value of watched chunks
|
// Decrement the value of watched chunks
|
||||||
let chunks_to_clean = level.mark_chunks_as_not_watched(&radial_chunks).await;
|
let chunks_to_clean = level.mark_chunks_as_not_watched(radial_chunks).await;
|
||||||
// Remove chunks with no watchers from the cache
|
// Remove chunks with no watchers from the cache
|
||||||
if !chunks_to_clean.is_empty() {
|
if !chunks_to_clean.is_empty() {
|
||||||
world.remove_entities_in_chunks(&chunks_to_clean).await;
|
world.remove_entities_in_chunks(&chunks_to_clean).await;
|
||||||
@ -2474,7 +2481,7 @@ impl Player {
|
|||||||
pub async fn unload_watched_chunks(&self, world: &World) {
|
pub async fn unload_watched_chunks(&self, world: &World) {
|
||||||
let radial_chunks = self.watched_section.load().all_chunks_within();
|
let radial_chunks = self.watched_section.load().all_chunks_within();
|
||||||
let level = &world.level;
|
let level = &world.level;
|
||||||
let chunks_to_clean = level.mark_chunks_as_not_watched(&radial_chunks).await;
|
let chunks_to_clean = level.mark_chunks_as_not_watched(radial_chunks).await;
|
||||||
if !chunks_to_clean.is_empty() {
|
if !chunks_to_clean.is_empty() {
|
||||||
world.remove_entities_in_chunks(&chunks_to_clean).await;
|
world.remove_entities_in_chunks(&chunks_to_clean).await;
|
||||||
level.clean_entity_chunks(&chunks_to_clean);
|
level.clean_entity_chunks(&chunks_to_clean);
|
||||||
|
|||||||
@ -319,11 +319,13 @@ impl World {
|
|||||||
|
|
||||||
pub fn update_active_chunks(self: &Arc<Self>) {
|
pub fn update_active_chunks(self: &Arc<Self>) {
|
||||||
let mut active_chunks = FxHashSet::default();
|
let mut active_chunks = FxHashSet::default();
|
||||||
|
let sim_dist = self.server.upgrade().map_or(10, |s| {
|
||||||
|
s.advanced_config.networking.java.simulation_distance.get()
|
||||||
|
}) as i32;
|
||||||
for player in self.players.load().iter() {
|
for player in self.players.load().iter() {
|
||||||
let center = player.get_entity().chunk_pos.load();
|
let center = player.get_entity().chunk_pos.load();
|
||||||
// TODO: gamerule for view distance/ticking distance
|
for dx in -sim_dist..=sim_dist {
|
||||||
for dx in -8..=8 {
|
for dy in -sim_dist..=sim_dist {
|
||||||
for dy in -8..=8 {
|
|
||||||
active_chunks.insert(center.add_raw(dx, dy));
|
active_chunks.insert(center.add_raw(dx, dy));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -4213,8 +4215,14 @@ impl World {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn remove_entities_in_chunks(&self, chunks: &[Vector2<i32>]) {
|
pub async fn remove_entities_in_chunks(
|
||||||
let chunks_set: FxHashSet<_> = chunks.iter().copied().collect();
|
&self,
|
||||||
|
chunks: impl IntoIterator<Item = impl std::borrow::Borrow<Vector2<i32>>>,
|
||||||
|
) {
|
||||||
|
let chunks_set: FxHashSet<_> = chunks.into_iter().map(|c| *c.borrow()).collect();
|
||||||
|
if chunks_set.is_empty() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
let mut entities_to_remove = Vec::new();
|
let mut entities_to_remove = Vec::new();
|
||||||
|
|
||||||
self.entities.rcu(|current_entities| {
|
self.entities.rcu(|current_entities| {
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user