mirror of
https://github.com/Pumpkin-MC/Pumpkin.git
synced 2026-08-31 08:22:33 +00:00
chore: batch some things together
also added DrownedEntity
This commit is contained in:
@@ -226,21 +226,28 @@ impl BlockPos {
|
||||
}
|
||||
|
||||
pub fn chunk_and_chunk_relative_position(&self) -> (Vector2<i32>, Vector3<i32>) {
|
||||
let (z_chunk, z_rem) = self.0.z.div_rem_euclid(&16);
|
||||
let (x_chunk, x_rem) = self.0.x.div_rem_euclid(&16);
|
||||
(
|
||||
Vector2 {
|
||||
x: x_chunk,
|
||||
y: z_chunk,
|
||||
},
|
||||
// Since we divide by 16, remnant can never exceed u8
|
||||
Vector3 {
|
||||
x: x_rem,
|
||||
z: z_rem,
|
||||
y: self.0.y,
|
||||
},
|
||||
)
|
||||
(self.chunk_position(), self.chunk_relative_position())
|
||||
}
|
||||
|
||||
pub fn chunk_position(&self) -> Vector2<i32> {
|
||||
let z_chunk = self.0.z.div_euclid(16);
|
||||
let x_chunk = self.0.x.div_euclid(16);
|
||||
Vector2 {
|
||||
x: x_chunk,
|
||||
y: z_chunk,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn chunk_relative_position(&self) -> Vector3<i32> {
|
||||
let z_chunk = self.0.z.rem_euclid(16);
|
||||
let x_chunk = self.0.x.rem_euclid(16);
|
||||
Vector3 {
|
||||
x: x_chunk,
|
||||
y: self.0.y,
|
||||
z: z_chunk,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn section_relative_position(&self) -> Vector3<i32> {
|
||||
let (_z_chunk, z_rem) = self.0.z.div_rem_euclid(&16);
|
||||
let (_x_chunk, x_rem) = self.0.x.div_rem_euclid(&16);
|
||||
|
||||
@@ -844,9 +844,7 @@ impl Level {
|
||||
delay: u8,
|
||||
priority: TickPriority,
|
||||
) {
|
||||
let chunk = self
|
||||
.get_chunk(block_pos.chunk_and_chunk_relative_position().0)
|
||||
.await;
|
||||
let chunk = self.get_chunk(block_pos.chunk_position()).await;
|
||||
let mut chunk = chunk.write().await;
|
||||
chunk.block_ticks.schedule_tick(
|
||||
&ScheduledTick {
|
||||
@@ -867,9 +865,7 @@ impl Level {
|
||||
delay: u8,
|
||||
priority: TickPriority,
|
||||
) {
|
||||
let chunk = self
|
||||
.get_chunk(block_pos.chunk_and_chunk_relative_position().0)
|
||||
.await;
|
||||
let chunk = self.get_chunk(block_pos.chunk_position()).await;
|
||||
let mut chunk = chunk.write().await;
|
||||
chunk.fluid_ticks.schedule_tick(
|
||||
&ScheduledTick {
|
||||
@@ -888,9 +884,7 @@ impl Level {
|
||||
block_pos: &BlockPos,
|
||||
block: &Block,
|
||||
) -> bool {
|
||||
let chunk = self
|
||||
.get_chunk(block_pos.chunk_and_chunk_relative_position().0)
|
||||
.await;
|
||||
let chunk = self.get_chunk(block_pos.chunk_position()).await;
|
||||
let chunk = chunk.read().await;
|
||||
chunk.block_ticks.is_scheduled(*block_pos, block)
|
||||
}
|
||||
@@ -900,9 +894,7 @@ impl Level {
|
||||
block_pos: &BlockPos,
|
||||
fluid: &Fluid,
|
||||
) -> bool {
|
||||
let chunk = self
|
||||
.get_chunk(block_pos.chunk_and_chunk_relative_position().0)
|
||||
.await;
|
||||
let chunk = self.get_chunk(block_pos.chunk_position()).await;
|
||||
let chunk = chunk.read().await;
|
||||
chunk.fluid_ticks.is_scheduled(*block_pos, fluid)
|
||||
}
|
||||
|
||||
@@ -93,7 +93,6 @@ impl LivingEntity {
|
||||
} else {
|
||||
0.8
|
||||
};
|
||||
|
||||
// TODO: Extract default MOVEMENT_SPEED Entity Attribute
|
||||
let default_movement_speed = 0.25;
|
||||
let health = entity.entity_type.max_health.unwrap_or(20.0);
|
||||
@@ -361,8 +360,8 @@ impl LivingEntity {
|
||||
.slipperiness,
|
||||
);
|
||||
|
||||
let speed =
|
||||
self.movement_speed.load() * 0.216 / (slipperiness * slipperiness * slipperiness);
|
||||
let speed = self.movement_speed.load() * 0.216_000_02
|
||||
/ (slipperiness * slipperiness * slipperiness);
|
||||
|
||||
(speed, slipperiness * 0.91)
|
||||
} else {
|
||||
|
||||
26
pumpkin/src/entity/mob/drowned.rs
Normal file
26
pumpkin/src/entity/mob/drowned.rs
Normal file
@@ -0,0 +1,26 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use crate::entity::{
|
||||
Entity, NBTStorage,
|
||||
mob::{Mob, MobEntity, zombie::ZombieEntity},
|
||||
};
|
||||
|
||||
pub struct DrownedEntity {
|
||||
entity: Arc<ZombieEntity>,
|
||||
}
|
||||
|
||||
impl DrownedEntity {
|
||||
pub async fn make(entity: Entity) -> Arc<Self> {
|
||||
Arc::new(Self {
|
||||
entity: ZombieEntity::make(entity).await,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl NBTStorage for DrownedEntity {}
|
||||
|
||||
impl Mob for DrownedEntity {
|
||||
fn get_mob_entity(&self) -> &MobEntity {
|
||||
&self.entity.mob_entity
|
||||
}
|
||||
}
|
||||
@@ -14,6 +14,8 @@ use std::sync::atomic::AtomicI32;
|
||||
use std::sync::atomic::Ordering::Relaxed;
|
||||
use tokio::sync::Mutex;
|
||||
|
||||
pub mod drowned;
|
||||
pub mod skeleton;
|
||||
pub mod zombie;
|
||||
|
||||
pub struct MobEntity {
|
||||
|
||||
55
pumpkin/src/entity/mob/skeleton/mod.rs
Normal file
55
pumpkin/src/entity/mob/skeleton/mod.rs
Normal file
@@ -0,0 +1,55 @@
|
||||
use std::sync::{Arc, Weak};
|
||||
|
||||
use pumpkin_data::entity::EntityType;
|
||||
|
||||
use crate::entity::{
|
||||
Entity, NBTStorage,
|
||||
ai::goal::{
|
||||
active_target_goal::ActiveTargetGoal, look_around_goal::LookAroundGoal,
|
||||
look_at_entity::LookAtEntityGoal,
|
||||
},
|
||||
mob::{Mob, MobEntity},
|
||||
};
|
||||
|
||||
//pub mod skeleton;
|
||||
|
||||
pub struct SkeletonEntityBase {
|
||||
pub mob_entity: MobEntity,
|
||||
}
|
||||
|
||||
impl SkeletonEntityBase {
|
||||
pub async fn make(entity: Entity) -> Arc<Self> {
|
||||
let mob_entity = MobEntity::new(entity);
|
||||
let mob = Self { mob_entity };
|
||||
let mob_arc = Arc::new(mob);
|
||||
let mob_weak: Weak<dyn Mob> = {
|
||||
let mob_arc: Arc<dyn Mob> = mob_arc.clone();
|
||||
Arc::downgrade(&mob_arc)
|
||||
};
|
||||
{
|
||||
let mut goal_selector = mob_arc.mob_entity.goals_selector.lock().await;
|
||||
let mut target_selector = mob_arc.mob_entity.target_selector.lock().await;
|
||||
|
||||
goal_selector.add_goal(
|
||||
8,
|
||||
LookAtEntityGoal::with_default(mob_weak, &EntityType::PLAYER, 8.0),
|
||||
);
|
||||
goal_selector.add_goal(8, Box::new(LookAroundGoal::default()));
|
||||
|
||||
target_selector.add_goal(
|
||||
2,
|
||||
ActiveTargetGoal::with_default(&mob_arc.mob_entity, &EntityType::PLAYER, true),
|
||||
);
|
||||
};
|
||||
|
||||
mob_arc
|
||||
}
|
||||
}
|
||||
|
||||
impl NBTStorage for SkeletonEntityBase {}
|
||||
|
||||
impl Mob for SkeletonEntityBase {
|
||||
fn get_mob_entity(&self) -> &MobEntity {
|
||||
&self.mob_entity
|
||||
}
|
||||
}
|
||||
0
pumpkin/src/entity/mob/skeleton/skeleton.rs
Normal file
0
pumpkin/src/entity/mob/skeleton/skeleton.rs
Normal file
@@ -19,11 +19,11 @@ use rand::{Rng, rng};
|
||||
use std::pin::Pin;
|
||||
use std::sync::{Arc, Weak};
|
||||
|
||||
pub struct Zombie {
|
||||
mob_entity: MobEntity,
|
||||
pub struct ZombieEntity {
|
||||
pub mob_entity: MobEntity,
|
||||
}
|
||||
|
||||
impl Zombie {
|
||||
impl ZombieEntity {
|
||||
pub async fn make(entity: Entity) -> Arc<Self> {
|
||||
let mob_entity = MobEntity::new(entity);
|
||||
let zombie = Self { mob_entity };
|
||||
@@ -55,9 +55,9 @@ impl Zombie {
|
||||
}
|
||||
}
|
||||
|
||||
impl NBTStorage for Zombie {}
|
||||
impl NBTStorage for ZombieEntity {}
|
||||
|
||||
impl Mob for Zombie {
|
||||
impl Mob for ZombieEntity {
|
||||
fn get_mob_entity(&self) -> &MobEntity {
|
||||
&self.mob_entity
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@ use crate::{
|
||||
Entity, EntityBase,
|
||||
decoration::{end_crystal::EndCrystalEntity, painting::PaintingEntity},
|
||||
living::LivingEntity,
|
||||
mob::zombie::Zombie,
|
||||
mob::{drowned::DrownedEntity, zombie::ZombieEntity},
|
||||
},
|
||||
world::World,
|
||||
};
|
||||
@@ -23,7 +23,8 @@ pub async fn from_type(
|
||||
let entity = Entity::new(uuid, world.clone(), position, entity_type, false);
|
||||
|
||||
let mob: Arc<dyn EntityBase> = match entity_type.id {
|
||||
id if id == EntityType::ZOMBIE.id => Zombie::make(entity).await,
|
||||
id if id == EntityType::ZOMBIE.id => ZombieEntity::make(entity).await,
|
||||
id if id == EntityType::DROWNED.id => DrownedEntity::make(entity).await,
|
||||
id if id == EntityType::PAINTING.id => Arc::new(PaintingEntity::new(entity)),
|
||||
id if id == EntityType::END_CRYSTAL.id => Arc::new(EndCrystalEntity::new(entity)),
|
||||
// Fallback Entity
|
||||
|
||||
@@ -236,10 +236,7 @@ impl World {
|
||||
// First lets see if the entity was saved on an other chunk, and if the current chunk does not match we remove it
|
||||
// Otherwise we just update the nbt data
|
||||
let base_entity = entity.get_entity();
|
||||
let (current_chunk_coordinate, _) = base_entity
|
||||
.block_pos
|
||||
.load()
|
||||
.chunk_and_chunk_relative_position();
|
||||
let current_chunk_coordinate = base_entity.block_pos.load().chunk_position();
|
||||
let mut nbt = NbtCompound::new();
|
||||
entity.write_nbt(&mut nbt).await;
|
||||
if let Some(old_chunk) = base_entity.first_loaded_chunk_position.load() {
|
||||
@@ -263,8 +260,7 @@ impl World {
|
||||
}
|
||||
|
||||
async fn remove_entity_data(&self, entity: &Entity) {
|
||||
let (current_chunk_coordinate, _) =
|
||||
entity.block_pos.load().chunk_and_chunk_relative_position();
|
||||
let current_chunk_coordinate = entity.block_pos.load().chunk_position();
|
||||
if let Some(old_chunk) = entity.first_loaded_chunk_position.load() {
|
||||
let old_chunk = old_chunk.to_vec2_i32();
|
||||
let chunk = self.level.get_entity_chunk(old_chunk).await;
|
||||
@@ -351,7 +347,7 @@ impl World {
|
||||
pub async fn broadcast_packet_all<P: ClientPacket>(&self, packet: &P) {
|
||||
let current_players = self.players.read().await;
|
||||
|
||||
for (_, player) in current_players.iter() {
|
||||
for player in current_players.values() {
|
||||
player.client.enqueue_packet(packet).await;
|
||||
}
|
||||
}
|
||||
@@ -378,7 +374,7 @@ impl World {
|
||||
) {
|
||||
let current_players = self.players.read().await;
|
||||
|
||||
for (_, player) in current_players.iter() {
|
||||
for player in current_players.values() {
|
||||
match &player.client {
|
||||
ClientPlatform::Java(client) => client.enqueue_packet(je_packet).await,
|
||||
ClientPlatform::Bedrock(client) => client.send_game_packet(be_packet).await,
|
||||
@@ -563,7 +559,6 @@ impl World {
|
||||
self.flush_block_updates().await;
|
||||
self.flush_synced_block_events().await;
|
||||
self.tick_environment().await;
|
||||
let env_done = start.elapsed();
|
||||
|
||||
// 2. Chunks
|
||||
let chunk_start = tokio::time::Instant::now();
|
||||
@@ -610,14 +605,13 @@ impl World {
|
||||
let total_elapsed = start.elapsed();
|
||||
if total_elapsed.as_millis() > 50 {
|
||||
log::warn!(
|
||||
"Slow Tick [{}ms]: Chunks: {:?} | Players({}): {:?} | Entities({}): {:?} | Env: {:?}",
|
||||
"Slow Tick [{}ms]: Chunks: {:?} | Players({}): {:?} | Entities({}): {:?}",
|
||||
total_elapsed.as_millis(),
|
||||
chunk_elapsed,
|
||||
player_count,
|
||||
player_elapsed,
|
||||
entity_count,
|
||||
entity_elapsed,
|
||||
env_done
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1843,6 +1837,7 @@ impl World {
|
||||
|
||||
// NOTE: This function doesn't actually await on anything, it just spawns two tokio tasks
|
||||
/// IMPORTANT: Chunks have to be non-empty
|
||||
#[expect(clippy::too_many_lines)]
|
||||
fn spawn_world_entity_chunks(
|
||||
self: &Arc<Self>,
|
||||
player: Arc<Player>,
|
||||
@@ -1896,6 +1891,7 @@ impl World {
|
||||
let mut ids = Vec::new();
|
||||
// Remove all the entities from the world
|
||||
let mut entities = world.entities.write().await;
|
||||
|
||||
for (uuid, entity_nbt) in &chunk.read().await.data {
|
||||
let Some(id) = entity_nbt.get_string("id") else {
|
||||
log::warn!("Entity has no ID");
|
||||
@@ -1917,8 +1913,30 @@ impl World {
|
||||
entities.remove(&base_entity.entity_uuid);
|
||||
ids.push(VarInt(base_entity.entity_id));
|
||||
|
||||
world.save_entity(uuid, &entity).await;
|
||||
let mut nbt = NbtCompound::new();
|
||||
entity.write_nbt(&mut nbt).await;
|
||||
if let Some(old_chunk) = base_entity.first_loaded_chunk_position.load() {
|
||||
let old_chunk = old_chunk.to_vec2_i32();
|
||||
let chunk = world.level.get_entity_chunk(old_chunk).await;
|
||||
let mut chunk = chunk.write().await;
|
||||
chunk.mark_dirty(true);
|
||||
let base_entity = entity.get_entity();
|
||||
let current_chunk_coordinate =
|
||||
base_entity.block_pos.load().chunk_position();
|
||||
|
||||
if old_chunk == current_chunk_coordinate {
|
||||
chunk.data.insert(*uuid, nbt);
|
||||
return;
|
||||
}
|
||||
|
||||
// The chunk has changed, lets remove the entity from the old chunk
|
||||
chunk.data.remove(uuid);
|
||||
}
|
||||
let mut chunk = chunk.write().await;
|
||||
chunk.data.insert(*uuid, nbt);
|
||||
chunk.mark_dirty(true);
|
||||
}
|
||||
|
||||
if !ids.is_empty() {
|
||||
player
|
||||
.client
|
||||
@@ -2268,17 +2286,15 @@ impl World {
|
||||
.await;
|
||||
entity.init_data_tracker().await;
|
||||
|
||||
let (chunk_coordinate, _) = base_entity
|
||||
.block_pos
|
||||
.load()
|
||||
.chunk_and_chunk_relative_position();
|
||||
let chunk_coordinate = base_entity.block_pos.load().chunk_position();
|
||||
let chunk = self.level.get_entity_chunk(chunk_coordinate).await;
|
||||
let mut chunk = chunk.write().await;
|
||||
let mut nbt = NbtCompound::new();
|
||||
entity.write_nbt(&mut nbt).await;
|
||||
chunk.data.insert(base_entity.entity_uuid, nbt);
|
||||
chunk.mark_dirty(true);
|
||||
drop(chunk);
|
||||
{
|
||||
let mut chunk = chunk.write().await;
|
||||
let mut nbt = NbtCompound::new();
|
||||
entity.write_nbt(&mut nbt).await;
|
||||
chunk.data.insert(base_entity.entity_uuid, nbt);
|
||||
chunk.mark_dirty(true);
|
||||
};
|
||||
|
||||
let mut current_entities = self.entities.write().await;
|
||||
current_entities.insert(base_entity.entity_uuid, entity);
|
||||
@@ -3016,10 +3032,7 @@ impl World {
|
||||
}
|
||||
|
||||
pub async fn get_block_entity(&self, block_pos: &BlockPos) -> Option<Arc<dyn BlockEntity>> {
|
||||
let chunk = self
|
||||
.level
|
||||
.get_chunk(block_pos.chunk_and_chunk_relative_position().0)
|
||||
.await;
|
||||
let chunk = self.level.get_chunk(block_pos.chunk_position()).await;
|
||||
let chunk: tokio::sync::RwLockReadGuard<ChunkData> = chunk.read().await;
|
||||
|
||||
chunk.block_entities.get(block_pos).cloned()
|
||||
@@ -3027,10 +3040,7 @@ impl World {
|
||||
|
||||
pub async fn add_block_entity(&self, block_entity: Arc<dyn BlockEntity>) {
|
||||
let block_pos = block_entity.get_position();
|
||||
let chunk = self
|
||||
.level
|
||||
.get_chunk(block_pos.chunk_and_chunk_relative_position().0)
|
||||
.await;
|
||||
let chunk = self.level.get_chunk(block_pos.chunk_position()).await;
|
||||
let mut chunk: tokio::sync::RwLockWriteGuard<ChunkData> = chunk.write().await;
|
||||
let block_entity_nbt = block_entity.chunk_data_nbt();
|
||||
|
||||
@@ -3050,10 +3060,7 @@ impl World {
|
||||
}
|
||||
|
||||
pub async fn remove_block_entity(&self, block_pos: &BlockPos) {
|
||||
let chunk = self
|
||||
.level
|
||||
.get_chunk(block_pos.chunk_and_chunk_relative_position().0)
|
||||
.await;
|
||||
let chunk = self.level.get_chunk(block_pos.chunk_position()).await;
|
||||
let mut chunk: tokio::sync::RwLockWriteGuard<ChunkData> = chunk.write().await;
|
||||
chunk.block_entities.remove(block_pos);
|
||||
chunk.mark_dirty(true);
|
||||
@@ -3061,10 +3068,7 @@ impl World {
|
||||
|
||||
pub async fn update_block_entity(&self, block_entity: &Arc<dyn BlockEntity>) {
|
||||
let block_pos = block_entity.get_position();
|
||||
let chunk = self
|
||||
.level
|
||||
.get_chunk(block_pos.chunk_and_chunk_relative_position().0)
|
||||
.await;
|
||||
let chunk = self.level.get_chunk(block_pos.chunk_position()).await;
|
||||
let mut chunk: tokio::sync::RwLockWriteGuard<ChunkData> = chunk.write().await;
|
||||
let block_entity_nbt = block_entity.chunk_data_nbt();
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@ use pumpkin_data::tag::Fluid::{MINECRAFT_LAVA, MINECRAFT_WATER};
|
||||
use pumpkin_data::tag::Taggable;
|
||||
use pumpkin_data::tag::WorldgenBiome::MINECRAFT_REDUCE_WATER_AMBIENT_SPAWNS;
|
||||
use pumpkin_data::{Block, BlockDirection, BlockState};
|
||||
use pumpkin_nbt::compound::NbtCompound;
|
||||
use pumpkin_util::GameMode;
|
||||
use pumpkin_util::math::boundingbox::{BoundingBox, EntityDimensions};
|
||||
use pumpkin_util::math::get_section_cord;
|
||||
@@ -16,6 +17,7 @@ use pumpkin_util::math::vector2::Vector2;
|
||||
use pumpkin_util::math::vector3::Vector3;
|
||||
use pumpkin_util::random::xoroshiro128::Xoroshiro;
|
||||
use pumpkin_util::random::{RandomImpl, get_seed};
|
||||
use pumpkin_world::chunk::io::Dirtiable;
|
||||
use pumpkin_world::chunk::{ChunkData, ChunkHeightmapType};
|
||||
use rand::seq::IndexedRandom;
|
||||
use rand::{Rng, rng};
|
||||
@@ -42,8 +44,8 @@ impl MobCounts {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct LocalMobCapCalculator {
|
||||
world: Arc<World>,
|
||||
player_mob_counts: HashMap<i32, MobCounts>,
|
||||
players_near_chunk: HashMap<Vector2<i32>, Vec<i32>>,
|
||||
}
|
||||
@@ -59,14 +61,6 @@ impl fmt::Debug for LocalMobCapCalculator {
|
||||
}
|
||||
|
||||
impl LocalMobCapCalculator {
|
||||
pub fn new(world: &Arc<World>) -> Self {
|
||||
Self {
|
||||
world: world.clone(), // can anybody get rid of this clone?
|
||||
player_mob_counts: HashMap::new(),
|
||||
players_near_chunk: HashMap::new(),
|
||||
}
|
||||
}
|
||||
|
||||
const fn calc_distance(chunk_pos: Vector2<i32>, player_pos: &Vector3<f64>) -> f64 {
|
||||
let dx = ((chunk_pos.x << 4) + 8) as f64 - player_pos.x;
|
||||
let dy = ((chunk_pos.y << 4) + 8) as f64 - player_pos.z;
|
||||
@@ -79,10 +73,7 @@ impl LocalMobCapCalculator {
|
||||
chunk_pos: &Vector2<i32>,
|
||||
) -> &'b Vec<i32> {
|
||||
match players_near_chunk.entry(*chunk_pos) {
|
||||
Entry::Occupied(value) => {
|
||||
// debug!("chunk {chunk_pos:?} near player {:?}", value.get());
|
||||
value.into_mut()
|
||||
}
|
||||
Entry::Occupied(value) => value.into_mut(),
|
||||
Entry::Vacant(entry) => {
|
||||
let mut players = Vec::new();
|
||||
for (_uuid, player) in world.players.read().await.iter() {
|
||||
@@ -93,14 +84,17 @@ impl LocalMobCapCalculator {
|
||||
players.push(player.entity_id());
|
||||
}
|
||||
}
|
||||
// debug!("chunk {chunk_pos:?} near player {:?}", players);
|
||||
entry.insert(players)
|
||||
}
|
||||
}
|
||||
}
|
||||
pub async fn add_mob(&mut self, chunk_pos: &Vector2<i32>, category: &'static MobCategory) {
|
||||
let players =
|
||||
Self::get_players_near(&mut self.players_near_chunk, &self.world, chunk_pos).await;
|
||||
pub async fn add_mob(
|
||||
&mut self,
|
||||
chunk_pos: &Vector2<i32>,
|
||||
world: &Arc<World>,
|
||||
category: &'static MobCategory,
|
||||
) {
|
||||
let players = Self::get_players_near(&mut self.players_near_chunk, world, chunk_pos).await;
|
||||
for player in players {
|
||||
self.player_mob_counts
|
||||
.entry(*player)
|
||||
@@ -115,10 +109,10 @@ impl LocalMobCapCalculator {
|
||||
pub async fn can_spawn(
|
||||
&mut self,
|
||||
category: &'static MobCategory,
|
||||
world: &Arc<World>,
|
||||
chunk_pos: &Vector2<i32>,
|
||||
) -> bool {
|
||||
let players =
|
||||
Self::get_players_near(&mut self.players_near_chunk, &self.world, chunk_pos).await;
|
||||
let players = Self::get_players_near(&mut self.players_near_chunk, world, chunk_pos).await;
|
||||
for player in players {
|
||||
if let Some(count) = self.player_mob_counts.get(player) {
|
||||
if count.can_spawn(category) {
|
||||
@@ -195,7 +189,7 @@ impl SpawnState {
|
||||
world: &Arc<World>,
|
||||
) -> Self {
|
||||
let mut potential = PotentialCalculator::default();
|
||||
let mut local_mob_cap = LocalMobCapCalculator::new(world);
|
||||
let mut local_mob_cap = LocalMobCapCalculator::default();
|
||||
let mut counter = MobCounts::default();
|
||||
for entity in entities.read().await.values() {
|
||||
let entity = entity.get_entity();
|
||||
@@ -211,7 +205,7 @@ impl SpawnState {
|
||||
}
|
||||
if entity_type.mob {
|
||||
local_mob_cap
|
||||
.add_mob(&entity.chunk_pos.load(), entity_type.category)
|
||||
.add_mob(&entity.chunk_pos.load(), world, entity_type.category)
|
||||
.await;
|
||||
}
|
||||
counter.add(entity_type.category);
|
||||
@@ -234,11 +228,12 @@ impl SpawnState {
|
||||
}
|
||||
async fn can_spawn_for_category_local(
|
||||
&mut self,
|
||||
world: &Arc<World>,
|
||||
category: &'static MobCategory,
|
||||
chunk_pos: &Vector2<i32>,
|
||||
) -> bool {
|
||||
self.local_mob_cap_calculator
|
||||
.can_spawn(category, chunk_pos)
|
||||
.can_spawn(category, world, chunk_pos)
|
||||
.await
|
||||
}
|
||||
async fn can_spawn(
|
||||
@@ -285,6 +280,7 @@ impl SpawnState {
|
||||
self.local_mob_cap_calculator
|
||||
.add_mob(
|
||||
&Vector2::<i32>::new(get_section_cord(pos.0.x), get_section_cord(pos.0.z)),
|
||||
world,
|
||||
entity_type.category,
|
||||
)
|
||||
.await;
|
||||
@@ -331,7 +327,7 @@ pub async fn spawn_for_chunk(
|
||||
// debug!("spawn for chunk {:?}", chunk_pos);
|
||||
for category in spawn_list {
|
||||
if spawn_state
|
||||
.can_spawn_for_category_local(category, chunk_pos)
|
||||
.can_spawn_for_category_local(world, category, chunk_pos)
|
||||
.await
|
||||
{
|
||||
let random_pos = get_random_pos_within(world.min_y, chunk_pos, chunk).await;
|
||||
@@ -371,25 +367,30 @@ pub async fn spawn_category_for_position(
|
||||
) {
|
||||
// TODO StructureManager structureManager = level.structureManager();
|
||||
// TODO blockState.isRedstoneConductor(chunk, pos) is true then return
|
||||
let mut batch_buffer = vec![];
|
||||
let mut spawn_cluster_size = 0;
|
||||
let mut new_pos = pos;
|
||||
let player_positions: Vec<_> = world
|
||||
.players
|
||||
.read()
|
||||
.await
|
||||
.values()
|
||||
.map(|p| p.position())
|
||||
.collect();
|
||||
for _ in 0..3 {
|
||||
let mut new_x = new_pos.0.x;
|
||||
let mut new_z = new_pos.0.z;
|
||||
|
||||
let mut random_group_size = (rng().random::<f32>() * 4.).ceil() as i32;
|
||||
let mut inc = 0;
|
||||
|
||||
'outer: while inc < random_group_size {
|
||||
new_x += rng().random_range(0..6) - rng().random_range(0..6);
|
||||
new_z += rng().random_range(0..6) - rng().random_range(0..6);
|
||||
new_pos = BlockPos::new(new_x, new_pos.0.y, new_z);
|
||||
let new_pos_center = new_pos.to_centered_f64();
|
||||
let player_distance = get_nearest_player(&new_pos_center, world).await;
|
||||
if !is_right_distance_to_player_and_spawn_point(
|
||||
&new_pos,
|
||||
player_distance,
|
||||
world,
|
||||
chunk_pos,
|
||||
) {
|
||||
let player_distance = get_nearest_player(&new_pos_center, &player_positions);
|
||||
if !is_right_distance_to_player_and_spawn_point(&new_pos, player_distance, chunk_pos) {
|
||||
inc += 1;
|
||||
continue;
|
||||
}
|
||||
@@ -423,7 +424,7 @@ pub async fn spawn_category_for_position(
|
||||
// TODO spawnGroupData = mob.finalizeSpawn(level, level.getCurrentDifficultyAt(mob.blockPosition()), EntitySpawnReason.NATURAL, spawnGroupData);
|
||||
spawn_cluster_size += 1;
|
||||
//group_size += 1;
|
||||
world.spawn_entity(entity).await;
|
||||
batch_buffer.push(entity);
|
||||
spawn_state.after_spawn(entity_type, &new_pos, world).await;
|
||||
if spawn_cluster_size >= entity_type.limit_per_chunk {
|
||||
return;
|
||||
@@ -433,25 +434,59 @@ pub async fn spawn_category_for_position(
|
||||
inc += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn get_nearest_player(pos: &Vector3<f64>, world: &Arc<World>) -> f64 {
|
||||
let mut dst = f64::MAX;
|
||||
for (_uuid, player) in world.players.read().await.iter() {
|
||||
if player.gamemode.load() == GameMode::Spectator {
|
||||
continue;
|
||||
// Spawn in batch
|
||||
if !batch_buffer.is_empty() {
|
||||
let mut prepared_data = Vec::with_capacity(batch_buffer.len());
|
||||
|
||||
for entity in &batch_buffer {
|
||||
entity.init_data_tracker().await;
|
||||
let base_entity = entity.get_entity();
|
||||
let packet = base_entity.create_spawn_packet();
|
||||
let mut nbt = NbtCompound::new();
|
||||
entity.write_nbt(&mut nbt).await;
|
||||
prepared_data.push((base_entity.entity_uuid, nbt, packet));
|
||||
}
|
||||
let cur_dst = player.position().squared_distance_to_vec(*pos);
|
||||
if cur_dst < dst {
|
||||
dst = cur_dst;
|
||||
{
|
||||
let chunk_handle = world.level.get_entity_chunk(*chunk_pos).await;
|
||||
let mut chunk_lock = chunk_handle.write().await;
|
||||
let mut entities_lock = world.entities.write().await;
|
||||
|
||||
for (uuid, nbt, _) in &prepared_data {
|
||||
let entity_ref = batch_buffer
|
||||
.iter()
|
||||
.find(|e| e.get_entity().entity_uuid == *uuid)
|
||||
.unwrap();
|
||||
|
||||
entities_lock.insert(*uuid, entity_ref.clone());
|
||||
chunk_lock.data.insert(*uuid, nbt.clone());
|
||||
}
|
||||
chunk_lock.mark_dirty(true);
|
||||
};
|
||||
|
||||
for (_, _, packet) in prepared_data {
|
||||
world.broadcast_packet_all(&packet).await;
|
||||
}
|
||||
}
|
||||
dst
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn get_nearest_player(pos: &Vector3<f64>, player_positions: &[Vector3<f64>]) -> f64 {
|
||||
let mut min_dst_sq = f64::MAX;
|
||||
|
||||
for player_pos in player_positions {
|
||||
let cur_dst_sq = player_pos.squared_distance_to_vec(*pos);
|
||||
if cur_dst_sq < min_dst_sq {
|
||||
min_dst_sq = cur_dst_sq;
|
||||
}
|
||||
}
|
||||
min_dst_sq
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn is_right_distance_to_player_and_spawn_point(
|
||||
pos: &BlockPos,
|
||||
distance: f64,
|
||||
_world: &Arc<World>,
|
||||
chunk_pos: &Vector2<i32>,
|
||||
) -> bool {
|
||||
if distance <= 24. * 24. {
|
||||
|
||||
Reference in New Issue
Block a user