diff --git a/pumpkin-data/build/block.rs b/pumpkin-data/build/block.rs index 92ff02503..77eb518f2 100644 --- a/pumpkin-data/build/block.rs +++ b/pumpkin-data/build/block.rs @@ -1043,6 +1043,16 @@ pub(crate) fn build() -> TokenStream { } } + #[doc = r" Get a raw ID from an State ID."] + #[inline] + pub const fn get_raw_id_from_state_id(state_id: u16) -> u16 { + if state_id as usize >= Self::RAW_ID_FROM_STATE_ID.len() { + 0 + } else { + Self::RAW_ID_FROM_STATE_ID[state_id as usize] + } + } + #[doc = r" Get a block from a state id."] #[inline] pub const fn from_state_id(id: u16) -> &'static Self { diff --git a/pumpkin/src/block/blocks/anvil.rs b/pumpkin/src/block/blocks/anvil.rs index e4ecffb31..b317fc73c 100644 --- a/pumpkin/src/block/blocks/anvil.rs +++ b/pumpkin/src/block/blocks/anvil.rs @@ -1,4 +1,7 @@ -use crate::block::{BlockBehaviour, OnPlaceArgs}; +use crate::block::blocks::falling::FallingBlock; +use crate::block::{ + BlockBehaviour, GetStateForNeighborUpdateArgs, OnPlaceArgs, OnScheduledTickArgs, PlacedArgs, +}; use async_trait::async_trait; use pumpkin_data::block_properties::{BlockProperties, WallTorchLikeProperties}; use pumpkin_data::tag::{RegistryKey, get_tag_values}; @@ -23,4 +26,18 @@ impl BlockBehaviour for AnvilBlock { props.facing = dir; props.to_state_id(args.block) } + + async fn placed(&self, args: PlacedArgs<'_>) { + FallingBlock::placed(&FallingBlock, args).await; + } + + async fn get_state_for_neighbor_update( + &self, + args: GetStateForNeighborUpdateArgs<'_>, + ) -> BlockStateId { + FallingBlock::get_state_for_neighbor_update(&FallingBlock, args).await + } + async fn on_scheduled_tick(&self, args: OnScheduledTickArgs<'_>) { + FallingBlock::on_scheduled_tick(&FallingBlock, args).await; + } } diff --git a/pumpkin/src/block/blocks/falling.rs b/pumpkin/src/block/blocks/falling.rs new file mode 100644 index 000000000..e08ed3400 --- /dev/null +++ b/pumpkin/src/block/blocks/falling.rs @@ -0,0 +1,62 @@ +use crate::{ + block::{ + BlockBehaviour, BlockMetadata, GetStateForNeighborUpdateArgs, OnScheduledTickArgs, + PlacedArgs, + }, + entity::falling::FallingEntity, +}; +use async_trait::async_trait; +use pumpkin_data::{ + Block, BlockState, + tag::{self, Taggable}, +}; +use pumpkin_world::{BlockStateId, tick::TickPriority}; +pub struct FallingBlock; + +impl FallingBlock { + #[must_use] + pub fn can_fall_through(state: &BlockState, block: &Block) -> bool { + state.is_air() + || block.is_tagged_with_by_tag(&tag::Block::MINECRAFT_FIRE) + || state.is_liquid() + || state.replaceable() + } +} + +impl BlockMetadata for FallingBlock { + fn namespace(&self) -> &'static str { + "minecraft" + } + + fn ids(&self) -> &'static [&'static str] { + &[Block::GRAVEL.name, Block::SAND.name, Block::RED_SAND.name] + } +} + +#[async_trait] +impl BlockBehaviour for FallingBlock { + async fn placed(&self, args: PlacedArgs<'_>) { + // TODO: make delay configurable + args.world + .schedule_block_tick(args.block, *args.position, 2, TickPriority::Normal) + .await; + } + async fn get_state_for_neighbor_update( + &self, + args: GetStateForNeighborUpdateArgs<'_>, + ) -> BlockStateId { + // TODO: make delay configurable + args.world + .schedule_block_tick(args.block, *args.position, 2, TickPriority::Normal) + .await; + args.state_id + } + + async fn on_scheduled_tick(&self, args: OnScheduledTickArgs<'_>) { + let (block, state) = args.world.get_block_and_state(&args.position.down()).await; + if !Self::can_fall_through(state, block) || args.position.0.y < args.world.min_y { + return; + } + FallingEntity::replace_spawn(args.world, *args.position, args.block.default_state.id).await; + } +} diff --git a/pumpkin/src/block/blocks/mod.rs b/pumpkin/src/block/blocks/mod.rs index b66e0b870..44102c030 100644 --- a/pumpkin/src/block/blocks/mod.rs +++ b/pumpkin/src/block/blocks/mod.rs @@ -17,6 +17,7 @@ pub mod dirt_path; pub mod doors; pub mod end_portal; pub mod end_portal_frame; +pub mod falling; pub mod farmland; pub mod fence_gates; pub mod fences; diff --git a/pumpkin/src/block/registry.rs b/pumpkin/src/block/registry.rs index aba473123..d0b89b900 100644 --- a/pumpkin/src/block/registry.rs +++ b/pumpkin/src/block/registry.rs @@ -11,6 +11,7 @@ use crate::block::blocks::dirt_path::DirtPathBlock; use crate::block::blocks::doors::DoorBlock; use crate::block::blocks::end_portal::EndPortalBlock; use crate::block::blocks::end_portal_frame::EndPortalFrameBlock; +use crate::block::blocks::falling::FallingBlock; use crate::block::blocks::farmland::FarmlandBlock; use crate::block::blocks::fence_gates::FenceGateBlock; use crate::block::blocks::fences::FenceBlock; @@ -205,6 +206,7 @@ pub fn default_registry() -> Arc { manager.register(LecternBlock); manager.register(StemBlock); manager.register(AttachedStemBlock); + manager.register(FallingBlock); // Fire manager.register(SoulFireBlock); diff --git a/pumpkin/src/entity/falling.rs b/pumpkin/src/entity/falling.rs new file mode 100644 index 000000000..4b8d9b67a --- /dev/null +++ b/pumpkin/src/entity/falling.rs @@ -0,0 +1,128 @@ +use async_trait::async_trait; +use pumpkin_data::Block; +use pumpkin_data::{damage::DamageType, entity::EntityType}; +use pumpkin_protocol::java::client::play::{MetaDataType, Metadata}; +use pumpkin_util::math::{position::BlockPos, vector3::Vector3}; +use pumpkin_world::{BlockStateId, world::BlockFlags}; +use std::sync::{Arc, atomic::Ordering}; +use uuid::Uuid; + +use crate::{ + entity::{Entity, EntityBase, NBTStorage, living::LivingEntity}, + server::Server, + world::World, +}; + +pub struct FallingEntity { + entity: Entity, + block_state_id: BlockStateId, +} + +impl FallingEntity { + pub fn new(entity: Entity, block_state_id: BlockStateId) -> Self { + Self { + entity, + block_state_id, + } + } + + /// Replaced the current Block and Spawns a new Falling one + pub async fn replace_spawn(world: &Arc, position: BlockPos, block_state: BlockStateId) { + // Replace the original block, TODO: use fluid state + world + .set_block_state( + &position, + Block::AIR.default_state.id, + BlockFlags::NOTIFY_ALL, + ) + .await; + + let position = position.0.to_f64().add_raw(0.5, 0.0, 0.5); + let entity = Entity::new( + Uuid::new_v4(), + world.clone(), + position, + &EntityType::FALLING_BLOCK, + false, + ); + entity.data.store(i32::from(block_state), Ordering::Relaxed); + let entity = Arc::new(Self::new(entity, block_state)); + world.spawn_entity(entity).await; + } +} + +impl NBTStorage for FallingEntity {} + +#[async_trait] +impl EntityBase for FallingEntity { + async fn tick(&self, caller: Arc, server: &Server) { + let entity = &self.entity; + entity.tick(caller.clone(), server).await; + + let original_velo = entity.velocity.load(); + let mut velo = original_velo; + velo.y -= self.get_gravity(); + + entity.velocity.store(velo); + + entity.move_entity(caller.clone(), velo).await; + entity.tick_block_collisions(&caller, server).await; + if entity.on_ground.load(Ordering::Relaxed) { + entity.velocity.store(velo.multiply(0.7, -0.5, 0.7)); + entity + .world + .read() + .await + .set_block_state( + &self.entity.block_pos.load(), + self.block_state_id, + BlockFlags::NOTIFY_ALL, + ) + .await; + entity.remove().await; + } + + entity.velocity.store(velo.multiply(0.98, 0.98, 0.98)); + + entity.send_pos_rot().await; + + entity.send_velocity().await; + } + + async fn init_data_tracker(&self) { + self.entity + .send_meta_data(&[Metadata::new( + 8, + MetaDataType::BlockPos, + self.entity.block_pos.load(), + )]) + .await; + } + + async fn damage_with_context( + &self, + _amount: f32, + _damage_type: DamageType, + _position: Option>, + _source: Option<&dyn EntityBase>, + _cause: Option<&dyn EntityBase>, + ) -> bool { + false + } + + fn get_entity(&self) -> &Entity { + &self.entity + } + + fn get_living_entity(&self) -> Option<&LivingEntity> { + None + } + + fn as_nbt_storage(&self) -> &dyn NBTStorage { + self + } + + fn get_gravity(&self) -> f64 { + 0.04 + } +} diff --git a/pumpkin/src/entity/mod.rs b/pumpkin/src/entity/mod.rs index 1327b6a34..7bed99a88 100644 --- a/pumpkin/src/entity/mod.rs +++ b/pumpkin/src/entity/mod.rs @@ -53,6 +53,7 @@ pub mod ai; pub mod decoration; pub mod effect; pub mod experience_orb; +pub mod falling; pub mod hunger; pub mod item; pub mod living;