Some more Item dropping work

And also some fixes
This commit is contained in:
Alexander Medvedev 2025-01-31 23:21:24 +01:00
parent ef497fb435
commit 819e0c62ac
11 changed files with 72 additions and 34 deletions

View File

@ -1,6 +1,6 @@
use super::{position::BlockPos, vector3::Vector3}; use super::{position::BlockPos, vector3::Vector3};
#[derive(Clone, Copy)] #[derive(Clone, Copy, Debug)]
pub struct BoundingBox { pub struct BoundingBox {
pub min: Vector3<f64>, pub min: Vector3<f64>,
pub max: Vector3<f64>, pub max: Vector3<f64>,
@ -74,7 +74,7 @@ impl BoundingBox {
} }
} }
#[derive(Clone, Copy)] #[derive(Clone, Copy, Debug)]
pub struct BoundingBoxSize { pub struct BoundingBoxSize {
pub width: f64, pub width: f64,
pub height: f64, pub height: f64,

View File

@ -58,7 +58,7 @@ pub enum Direction {
West, West,
} }
// TODO: We can automaticly parse them ig // TODO: We can automatically parse them ig
#[must_use] #[must_use]
pub fn get_property_key(property_name: &str) -> Option<BlockProperty> { pub fn get_property_key(property_name: &str) -> Option<BlockProperty> {
match property_name { match property_name {
@ -93,7 +93,6 @@ pub fn evaluate_layers(
} }
if block.id == clicked_block.id { if block.id == clicked_block.id {
dbg!(property);
// bro its is so hacky :crying: // bro its is so hacky :crying:
let mut layer: u8 = property.replace("layers", "").parse().unwrap(); let mut layer: u8 = property.replace("layers", "").parse().unwrap();
// lets add a new layer // lets add a new layer

View File

@ -32,7 +32,6 @@ impl ArgumentConsumer for TextComponentArgConsumer {
let text_component = parse_text_component(s); let text_component = parse_text_component(s);
let Some(text_component) = text_component else { let Some(text_component) = text_component else {
dbg!(text_component);
if s.starts_with('"') && s.ends_with('"') { if s.starts_with('"') && s.ends_with('"') {
let s = s.replace('"', ""); let s = s.replace('"', "");
return Some(Arg::TextComponent(TextComponent::text(s))); return Some(Arg::TextComponent(TextComponent::text(s)));

View File

@ -37,8 +37,8 @@ impl CommandExecutor for SummonExecutor {
// TODO: Make this work in console // TODO: Make this work in console
if let Some(player) = sender.as_player() { if let Some(player) = sender.as_player() {
let pos = pos.unwrap_or(player.living_entity.entity.pos.load()); let pos = pos.unwrap_or(player.living_entity.entity.pos.load());
let (mob, uuid) = mob::from_type(entity, server, pos, player.world()).await; let mob = mob::from_type(entity, server, pos, player.world()).await;
player.world().spawn_entity(uuid, mob).await; player.world().spawn_entity(mob).await;
sender sender
.send_message(TextComponent::translate( .send_message(TextComponent::translate(
"commands.summon.success", "commands.summon.success",

View File

@ -1,3 +1,5 @@
use std::sync::atomic::AtomicI8;
use async_trait::async_trait; use async_trait::async_trait;
use pumpkin_protocol::{ use pumpkin_protocol::{
client::play::{MetaDataType, Metadata}, client::play::{MetaDataType, Metadata},
@ -10,12 +12,17 @@ use super::{living::LivingEntity, Entity, EntityBase};
pub struct ItemEntity { pub struct ItemEntity {
entity: Entity, entity: Entity,
item: Slot, item: Slot,
pickup_delay: AtomicI8,
} }
impl ItemEntity { impl ItemEntity {
pub fn new(entity: Entity, stack: &ItemStack) -> Self { pub fn new(entity: Entity, stack: &ItemStack) -> Self {
let slot = Slot::from(stack); let slot = Slot::from(stack);
Self { entity, item: slot } Self {
entity,
item: slot,
pickup_delay: AtomicI8::new(10), // Vanilla
}
} }
pub async fn send_meta_packet(&self) { pub async fn send_meta_packet(&self) {
self.entity self.entity
@ -26,7 +33,18 @@ impl ItemEntity {
#[async_trait] #[async_trait]
impl EntityBase for ItemEntity { impl EntityBase for ItemEntity {
async fn tick(&self) {} async fn tick(&self) {
if self.pickup_delay.load(std::sync::atomic::Ordering::Relaxed) >= 0 {
self.pickup_delay
.fetch_sub(1, std::sync::atomic::Ordering::Relaxed);
}
}
async fn on_player_collision(&self) {
if self.pickup_delay.load(std::sync::atomic::Ordering::Relaxed) == 0 {
// check if inventory is full
self.entity.remove().await;
}
}
fn get_entity(&self) -> &Entity { fn get_entity(&self) -> &Entity {
&self.entity &self.entity

View File

@ -4,7 +4,6 @@ use async_trait::async_trait;
use pumpkin_data::entity::EntityType; use pumpkin_data::entity::EntityType;
use pumpkin_util::math::vector3::Vector3; use pumpkin_util::math::vector3::Vector3;
use tokio::sync::Mutex; use tokio::sync::Mutex;
use uuid::Uuid;
use zombie::Zombie; use zombie::Zombie;
use crate::{server::Server, world::World}; use crate::{server::Server, world::World};
@ -56,8 +55,8 @@ pub async fn from_type(
server: &Server, server: &Server,
position: Vector3<f64>, position: Vector3<f64>,
world: &Arc<World>, world: &Arc<World>,
) -> (Arc<dyn EntityBase>, Uuid) { ) -> Arc<dyn EntityBase> {
let (entity, uuid) = server.add_entity(position, entity_type, world); let entity = server.add_entity(position, entity_type, world);
let mob = MobEntity { let mob = MobEntity {
living_entity: LivingEntity::new(entity), living_entity: LivingEntity::new(entity),
goals: Mutex::new(vec![]), goals: Mutex::new(vec![]),
@ -69,7 +68,7 @@ pub async fn from_type(
// TODO // TODO
_ => (), _ => (),
} }
(Arc::new(mob), uuid) Arc::new(mob)
} }
impl MobEntity { impl MobEntity {

View File

@ -25,7 +25,6 @@ use pumpkin_util::math::{
wrap_degrees, wrap_degrees,
}; };
use serde::Serialize; use serde::Serialize;
use uuid::Uuid;
use crate::world::World; use crate::world::World;
@ -39,7 +38,10 @@ pub type EntityId = i32;
#[async_trait] #[async_trait]
pub trait EntityBase: Send + Sync { pub trait EntityBase: Send + Sync {
async fn tick(&self); /// Gets Called every tick
async fn tick(&self) {}
/// Called when a player collides with the entity
async fn on_player_collision(&self) {}
fn get_entity(&self) -> &Entity; fn get_entity(&self) -> &Entity;
fn get_living_entity(&self) -> Option<&LivingEntity>; fn get_living_entity(&self) -> Option<&LivingEntity>;
} }
@ -222,12 +224,12 @@ impl Entity {
self.world.remove_entity(self).await; self.world.remove_entity(self).await;
} }
pub fn create_spawn_packet(&self, uuid: Uuid) -> CSpawnEntity { pub fn create_spawn_packet(&self) -> CSpawnEntity {
let entity_loc = self.pos.load(); let entity_loc = self.pos.load();
let entity_vel = self.velocity.load(); let entity_vel = self.velocity.load();
CSpawnEntity::new( CSpawnEntity::new(
VarInt(self.entity_id), VarInt(self.entity_id),
uuid, self.entity_uuid,
VarInt((self.entity_type) as i32), VarInt((self.entity_type) as i32),
entity_loc.x, entity_loc.x,
entity_loc.y, entity_loc.y,
@ -328,7 +330,7 @@ impl Entity {
pub async fn set_pose(&self, pose: EntityPose) { pub async fn set_pose(&self, pose: EntityPose) {
self.pose.store(pose); self.pose.store(pose);
let pose = pose as i32; let pose = pose as i32;
self.send_meta_data(Metadata::new(6, MetaDataType::EntityPose, pose)) self.send_meta_data(Metadata::new(6, MetaDataType::EntityPose, VarInt(pose)))
.await; .await;
} }
} }

View File

@ -537,9 +537,9 @@ impl Player {
// TODO: This should be optimized for larger servers based on current player chunk // TODO: This should be optimized for larger servers based on current player chunk
pub async fn send_mobs(&self, world: &World) { pub async fn send_mobs(&self, world: &World) {
let entities = world.entities.lock().await.clone(); let entities = world.entities.lock().await.clone();
for (uuid, entity) in entities { for (_, entity) in entities {
self.client self.client
.send_packet(&entity.get_entity().create_spawn_packet(uuid)) .send_packet(&entity.get_entity().create_spawn_packet())
.await; .await;
} }
} }
@ -718,13 +718,13 @@ impl Player {
pub async fn drop_item(&self, server: &Server) { pub async fn drop_item(&self, server: &Server) {
let inv = self.inventory.lock().await; let inv = self.inventory.lock().await;
if let Some(item) = inv.held_item() { if let Some(item) = inv.held_item() {
let (entity, uuid) = server.add_entity( let entity = server.add_entity(
self.living_entity.entity.pos.load(), self.living_entity.entity.pos.load(),
EntityType::Item, EntityType::Item,
self.world(), self.world(),
); );
let item_entity = Arc::new(ItemEntity::new(entity, item)); let item_entity = Arc::new(ItemEntity::new(entity, item));
self.world().spawn_entity(uuid, item_entity.clone()).await; self.world().spawn_entity(item_entity.clone()).await;
item_entity.send_meta_packet().await; item_entity.send_meta_packet().await;
} }
} }

View File

@ -1125,7 +1125,7 @@ impl Player {
let world = self.world(); let world = self.world();
// create new mob and uuid based on spawn egg id // create new mob and uuid based on spawn egg id
let (mob, uuid) = mob::from_type( let mob = mob::from_type(
EntityType::from_raw(*spawn_item_id).unwrap(), EntityType::from_raw(*spawn_item_id).unwrap(),
server, server,
pos, pos,
@ -1137,7 +1137,7 @@ impl Player {
mob.get_entity().set_rotation(yaw, 0.0); mob.get_entity().set_rotation(yaw, 0.0);
// broadcast new mob to all players // broadcast new mob to all players
world.spawn_entity(uuid, mob).await; world.spawn_entity(mob).await;
// TODO: send/configure additional commands/data based on type of entity (horse, slime, etc) // TODO: send/configure additional commands/data based on type of entity (horse, slime, etc)
} else { } else {

View File

@ -29,7 +29,6 @@ use std::{
time::Duration, time::Duration,
}; };
use tokio::sync::{Mutex, RwLock}; use tokio::sync::{Mutex, RwLock};
use uuid::Uuid;
use crate::block::block_manager::BlockManager; use crate::block::block_manager::BlockManager;
use crate::block::properties::BlockPropertiesManager; use crate::block::properties::BlockPropertiesManager;
@ -218,7 +217,7 @@ impl Server {
position: Vector3<f64>, position: Vector3<f64>,
entity_type: EntityType, entity_type: EntityType,
world: &Arc<World>, world: &Arc<World>,
) -> (Entity, Uuid) { ) -> Entity {
let entity_id = self.new_entity_id(); let entity_id = self.new_entity_id();
// TODO: this should be resolved to a integer using a macro when calling this function // TODO: this should be resolved to a integer using a macro when calling this function
@ -235,18 +234,21 @@ impl Server {
// TODO: standing eye height should be per mob // TODO: standing eye height should be per mob
let new_uuid = uuid::Uuid::new_v4(); let new_uuid = uuid::Uuid::new_v4();
let entity = Entity::new( Entity::new(
entity_id, entity_id,
new_uuid, new_uuid,
world.clone(), world.clone(),
position, position,
entity_type, entity_type,
1.62, 1.62,
AtomicCell::new(BoundingBox::new_default(&bounding_box_size)), AtomicCell::new(BoundingBox::new_from_pos(
position.x,
position.y,
position.z,
&bounding_box_size,
)),
AtomicCell::new(bounding_box_size), AtomicCell::new(bounding_box_size),
); )
(entity, new_uuid)
} }
pub async fn try_get_container( pub async fn try_get_container(

View File

@ -234,9 +234,27 @@ impl World {
for player in self.players.lock().await.values() { for player in self.players.lock().await.values() {
player.tick().await; player.tick().await;
} }
// entities tick // entities tick
for entity in self.entities.lock().await.values() { for entity in self.entities.lock().await.values() {
entity.tick().await; entity.tick().await;
// this boolean thing prevents deadlocks, since we lock players we can't broadcast packets
let mut collied = false;
for player in self.players.lock().await.values() {
if player
.living_entity
.entity
.bounding_box
.load()
.intersects(&entity.get_entity().bounding_box.load())
{
collied = true;
break;
}
}
if collied {
entity.on_player_collision().await;
}
} }
} }
@ -838,11 +856,12 @@ impl World {
/// ///
/// * `uuid`: The unique UUID of the living entity to add. /// * `uuid`: The unique UUID of the living entity to add.
/// * `living_entity`: A `Arc<LivingEntity>` reference to the living entity object. /// * `living_entity`: A `Arc<LivingEntity>` reference to the living entity object.
pub async fn spawn_entity(&self, uuid: uuid::Uuid, entity: Arc<dyn EntityBase>) { pub async fn spawn_entity(&self, entity: Arc<dyn EntityBase>) {
self.broadcast_packet_all(&entity.get_entity().create_spawn_packet(uuid)) let base_entity = entity.get_entity();
self.broadcast_packet_all(&base_entity.create_spawn_packet())
.await; .await;
let mut current_living_entities = self.entities.lock().await; let mut current_living_entities = self.entities.lock().await;
current_living_entities.insert(uuid, entity); current_living_entities.insert(base_entity.entity_uuid, entity);
} }
pub async fn remove_entity(&self, entity: &Entity) { pub async fn remove_entity(&self, entity: &Entity) {