diff --git a/pumpkin-macros/src/lib.rs b/pumpkin-macros/src/lib.rs index 6c98350f4..3ea4bad3c 100644 --- a/pumpkin-macros/src/lib.rs +++ b/pumpkin-macros/src/lib.rs @@ -231,7 +231,7 @@ pub fn pumpkin_item(input: TokenStream, item: TokenStream) -> TokenStream { let gen = quote! { #item - impl #impl_generics crate::block::pumpkin_block::BlockMetadata for #name #ty_generics { + impl #impl_generics crate::item::pumpkin_item::ItemMetadata for #name #ty_generics { const NAMESPACE: &'static str = #namespace; const ID: &'static str = #id; } diff --git a/pumpkin-world/src/item/registry.rs b/pumpkin-world/src/item/registry.rs index 759dba5e0..99c08472b 100644 --- a/pumpkin-world/src/item/registry.rs +++ b/pumpkin-world/src/item/registry.rs @@ -22,6 +22,10 @@ pub fn get_item(name: &str) -> Option<&Item> { ITEMS.get(&name.replace("minecraft:", "")) } +pub fn get_name_by_id<'a>(item_id: u16) -> Option<&'a String> { + ITEMS_REGISTRY_NAME_BY_ID.get(&item_id) +} + pub fn get_item_by_id<'a>(item_id: u16) -> Option<&'a Item> { ITEMS.values().find(|&item| item.id == item_id) } diff --git a/pumpkin/src/block/blocks/chest.rs b/pumpkin/src/block/blocks/chest.rs index c05178516..b14f4b446 100644 --- a/pumpkin/src/block/blocks/chest.rs +++ b/pumpkin/src/block/blocks/chest.rs @@ -29,7 +29,7 @@ pub struct ChestBlock; #[async_trait] impl PumpkinBlock for ChestBlock { - async fn on_use<'a>( + async fn normal_use( &self, block: &Block, player: &Player, @@ -40,7 +40,7 @@ impl PumpkinBlock for ChestBlock { .await; } - async fn on_use_with_item<'a>( + async fn use_with_item( &self, block: &Block, player: &Player, @@ -53,17 +53,11 @@ impl PumpkinBlock for ChestBlock { BlockActionResult::Consume } - async fn on_broken<'a>( - &self, - block: &Block, - player: &Player, - location: BlockPos, - server: &Server, - ) { + async fn broken(&self, block: &Block, player: &Player, location: BlockPos, server: &Server) { super::standard_on_broken_with_container(block, player, location, server).await; } - async fn on_close<'a>( + async fn close( &self, _block: &Block, player: &Player, diff --git a/pumpkin/src/block/blocks/crafting_table.rs b/pumpkin/src/block/blocks/crafting_table.rs index 8cb907042..2d0162b22 100644 --- a/pumpkin/src/block/blocks/crafting_table.rs +++ b/pumpkin/src/block/blocks/crafting_table.rs @@ -14,7 +14,7 @@ pub struct CraftingTableBlock; #[async_trait] impl PumpkinBlock for CraftingTableBlock { - async fn on_use<'a>( + async fn normal_use( &self, block: &Block, player: &Player, @@ -25,7 +25,7 @@ impl PumpkinBlock for CraftingTableBlock { .await; } - async fn on_use_with_item<'a>( + async fn use_with_item( &self, block: &Block, player: &Player, @@ -38,17 +38,11 @@ impl PumpkinBlock for CraftingTableBlock { BlockActionResult::Consume } - async fn on_broken<'a>( - &self, - block: &Block, - player: &Player, - location: BlockPos, - server: &Server, - ) { + async fn broken(&self, block: &Block, player: &Player, location: BlockPos, server: &Server) { super::standard_on_broken_with_container(block, player, location, server).await; } - async fn on_close<'a>( + async fn close( &self, _block: &Block, player: &Player, diff --git a/pumpkin/src/block/blocks/furnace.rs b/pumpkin/src/block/blocks/furnace.rs index a0e88abab..99b829ceb 100644 --- a/pumpkin/src/block/blocks/furnace.rs +++ b/pumpkin/src/block/blocks/furnace.rs @@ -15,7 +15,7 @@ pub struct FurnaceBlock; #[async_trait] impl PumpkinBlock for FurnaceBlock { - async fn on_use<'a>( + async fn normal_use( &self, block: &Block, player: &Player, @@ -26,7 +26,7 @@ impl PumpkinBlock for FurnaceBlock { .await; } - async fn on_use_with_item<'a>( + async fn use_with_item( &self, block: &Block, player: &Player, @@ -39,13 +39,7 @@ impl PumpkinBlock for FurnaceBlock { BlockActionResult::Consume } - async fn on_broken<'a>( - &self, - block: &Block, - player: &Player, - location: BlockPos, - server: &Server, - ) { + async fn broken(&self, block: &Block, player: &Player, location: BlockPos, server: &Server) { super::standard_on_broken_with_container(block, player, location, server).await; } } diff --git a/pumpkin/src/block/blocks/jukebox.rs b/pumpkin/src/block/blocks/jukebox.rs index 469d3ba60..dc58f22cd 100644 --- a/pumpkin/src/block/blocks/jukebox.rs +++ b/pumpkin/src/block/blocks/jukebox.rs @@ -14,7 +14,7 @@ pub struct JukeboxBlock; #[async_trait] impl PumpkinBlock for JukeboxBlock { - async fn on_use<'a>( + async fn normal_use( &self, _block: &Block, player: &Player, @@ -27,7 +27,7 @@ impl PumpkinBlock for JukeboxBlock { world.stop_record(location).await; } - async fn on_use_with_item<'a>( + async fn use_with_item( &self, _block: &Block, player: &Player, @@ -57,13 +57,7 @@ impl PumpkinBlock for JukeboxBlock { BlockActionResult::Consume } - async fn on_broken<'a>( - &self, - _block: &Block, - player: &Player, - location: BlockPos, - _server: &Server, - ) { + async fn broken(&self, _block: &Block, player: &Player, location: BlockPos, _server: &Server) { // For now just stop the music at this position let world = &player.living_entity.entity.world; diff --git a/pumpkin/src/block/pumpkin_block.rs b/pumpkin/src/block/pumpkin_block.rs index c67338889..3a15237cf 100644 --- a/pumpkin/src/block/pumpkin_block.rs +++ b/pumpkin/src/block/pumpkin_block.rs @@ -17,7 +17,7 @@ pub trait BlockMetadata { #[async_trait] pub trait PumpkinBlock: Send + Sync { - async fn on_use<'a>( + async fn normal_use( &self, _block: &Block, _player: &Player, @@ -25,7 +25,7 @@ pub trait PumpkinBlock: Send + Sync { _server: &Server, ) { } - async fn on_use_with_item<'a>( + async fn use_with_item( &self, _block: &Block, _player: &Player, @@ -36,7 +36,7 @@ pub trait PumpkinBlock: Send + Sync { BlockActionResult::Continue } - async fn on_placed<'a>( + async fn placed( &self, _block: &Block, _player: &Player, @@ -45,7 +45,7 @@ pub trait PumpkinBlock: Send + Sync { ) { } - async fn on_broken<'a>( + async fn broken( &self, _block: &Block, _player: &Player, @@ -54,7 +54,7 @@ pub trait PumpkinBlock: Send + Sync { ) { } - async fn on_close<'a>( + async fn close( &self, _block: &Block, _player: &Player, diff --git a/pumpkin/src/block/registry.rs b/pumpkin/src/block/registry.rs index 40f580d15..46c4ca31c 100644 --- a/pumpkin/src/block/registry.rs +++ b/pumpkin/src/block/registry.rs @@ -34,11 +34,13 @@ impl BlockRegistry { ) { let pumpkin_block = self.get_pumpkin_block(block); if let Some(pumpkin_block) = pumpkin_block { - pumpkin_block.on_use(block, player, location, server).await; + pumpkin_block + .normal_use(block, player, location, server) + .await; } } - pub async fn on_use_with_item( + pub async fn use_with_item( &self, block: &Block, player: &Player, @@ -49,7 +51,7 @@ impl BlockRegistry { let pumpkin_block = self.get_pumpkin_block(block); if let Some(pumpkin_block) = pumpkin_block { return pumpkin_block - .on_use_with_item(block, player, location, item, server) + .use_with_item(block, player, location, item, server) .await; } BlockActionResult::Continue @@ -64,13 +66,11 @@ impl BlockRegistry { ) { let pumpkin_block = self.get_pumpkin_block(block); if let Some(pumpkin_block) = pumpkin_block { - pumpkin_block - .on_placed(block, player, location, server) - .await; + pumpkin_block.placed(block, player, location, server).await; } } - pub async fn on_broken( + pub async fn broken( &self, block: &Block, player: &Player, @@ -79,13 +79,11 @@ impl BlockRegistry { ) { let pumpkin_block = self.get_pumpkin_block(block); if let Some(pumpkin_block) = pumpkin_block { - pumpkin_block - .on_broken(block, player, location, server) - .await; + pumpkin_block.broken(block, player, location, server).await; } } - pub async fn on_close( + pub async fn close( &self, block: &Block, player: &Player, @@ -96,7 +94,7 @@ impl BlockRegistry { let pumpkin_block = self.get_pumpkin_block(block); if let Some(pumpkin_block) = pumpkin_block { pumpkin_block - .on_close(block, player, location, server, container) + .close(block, player, location, server, container) .await; } } diff --git a/pumpkin/src/entity/mod.rs b/pumpkin/src/entity/mod.rs index 55ca6defa..8037f6891 100644 --- a/pumpkin/src/entity/mod.rs +++ b/pumpkin/src/entity/mod.rs @@ -235,7 +235,7 @@ impl Entity { pub fn set_rotation(&self, yaw: f32, pitch: f32) { // TODO self.yaw.store(yaw); - self.pitch.store(pitch); + self.pitch.store(pitch.clamp(-90.0, 90.0) % 360.0); } /// Removes the Entity from their current World diff --git a/pumpkin/src/entity/player.rs b/pumpkin/src/entity/player.rs index 2e9de45e4..438c039c9 100644 --- a/pumpkin/src/entity/player.rs +++ b/pumpkin/src/entity/player.rs @@ -484,6 +484,10 @@ impl Player { &self.living_entity.entity.world } + pub fn position(&self) -> Vector3 { + self.living_entity.entity.pos.load() + } + /// Updates the current abilities the Player has pub async fn send_abilities_update(&self) { let mut b = 0i8; @@ -984,7 +988,10 @@ impl Player { self.handle_use_item_on(SUseItemOn::read(bytebuf)?, server) .await?; } - SUseItem::PACKET_ID => self.handle_use_item(&SUseItem::read(bytebuf)?), + SUseItem::PACKET_ID => { + self.handle_use_item(&SUseItem::read(bytebuf)?, server) + .await; + } SCommandSuggestion::PACKET_ID => { self.handle_command_suggestion(SCommandSuggestion::read(bytebuf)?, server) .await; diff --git a/pumpkin/src/entity/projectile/mod.rs b/pumpkin/src/entity/projectile/mod.rs index 00987b6c1..400d72580 100644 --- a/pumpkin/src/entity/projectile/mod.rs +++ b/pumpkin/src/entity/projectile/mod.rs @@ -2,15 +2,21 @@ use std::f32::{self, consts::PI}; use pumpkin_util::math::vector3::Vector3; -use super::Entity; +use super::{living::LivingEntity, Entity, EntityBase}; -pub struct ProjectileEntity { +pub struct ThrownItem { entity: Entity, } const DEG_PER_RAD_F32: f32 = 180.0 / PI; -impl ProjectileEntity { +impl ThrownItem { + pub fn new(entity: Entity, owner: &Entity) -> Self { + let mut owner_pos = owner.pos.load(); + owner_pos.y = (owner_pos.y + f64::from(owner.standing_eye_height)) - 0.1; + entity.pos.store(owner_pos); + Self { entity } + } pub fn set_velocity_from( &self, shooter: &Entity, @@ -20,9 +26,15 @@ impl ProjectileEntity { speed: f32, divergence: f32, ) { - let x = -(yaw * PI / 180.0).sin() * (pitch * PI / 180.0).cos(); - let y = -((pitch + roll) * PI / 180.0).sin(); - let z = -(yaw * PI / 180.0).cos() * (pitch * PI / 180.0).cos(); + let to_radians = |degrees: f32| degrees * PI / 180.0; + + let yaw_rad = to_radians(yaw); + let pitch_rad = to_radians(pitch); + let roll_rad = to_radians(pitch + roll); + + let x = -yaw_rad.sin() * pitch_rad.cos(); + let y = -roll_rad.sin(); + let z = yaw_rad.cos() * pitch_rad.cos(); self.set_velocity( f64::from(x), f64::from(y), @@ -64,3 +76,13 @@ impl ProjectileEntity { ); } } + +impl EntityBase for ThrownItem { + fn get_entity(&self) -> &Entity { + &self.entity + } + + fn get_living_entity(&self) -> Option<&LivingEntity> { + None + } +} diff --git a/pumpkin/src/item/items/egg.rs b/pumpkin/src/item/items/egg.rs new file mode 100644 index 000000000..106b46ee4 --- /dev/null +++ b/pumpkin/src/item/items/egg.rs @@ -0,0 +1,37 @@ +use std::sync::Arc; + +use crate::entity::player::Player; +use crate::entity::projectile::ThrownItem; +use crate::item::pumpkin_item::PumpkinItem; +use crate::server::Server; +use async_trait::async_trait; +use pumpkin_data::entity::EntityType; +use pumpkin_data::sound::Sound; +use pumpkin_macros::pumpkin_item; +use pumpkin_world::item::registry::Item; +#[pumpkin_item("minecraft:egg")] +pub struct EggItem; + +const POWER: f32 = 1.5; + +#[async_trait] +impl PumpkinItem for EggItem { + async fn normal_use(&self, _block: &Item, player: &Player, server: &Server) { + let position = player.position(); + let world = player.world(); + world + .play_sound( + Sound::EntityEggThrow, + pumpkin_data::sound::SoundCategory::Players, + &position, + ) + .await; + // TODO: Implement eggs the right way, so there is a chance of spawning chickens + let entity = server.add_entity(position, EntityType::Egg, world); + let snowball = ThrownItem::new(entity, &player.living_entity.entity); + let yaw = player.living_entity.entity.yaw.load(); + let pitch = player.living_entity.entity.pitch.load(); + snowball.set_velocity_from(&player.living_entity.entity, pitch, yaw, 0.0, POWER, 1.0); + world.spawn_entity(Arc::new(snowball)).await; + } +} diff --git a/pumpkin/src/item/items/mod.rs b/pumpkin/src/item/items/mod.rs new file mode 100644 index 000000000..cca21c512 --- /dev/null +++ b/pumpkin/src/item/items/mod.rs @@ -0,0 +1,2 @@ +pub mod egg; +pub mod snowball; diff --git a/pumpkin/src/item/items/snowball.rs b/pumpkin/src/item/items/snowball.rs new file mode 100644 index 000000000..12ea9d10c --- /dev/null +++ b/pumpkin/src/item/items/snowball.rs @@ -0,0 +1,36 @@ +use std::sync::Arc; + +use crate::entity::player::Player; +use crate::entity::projectile::ThrownItem; +use crate::item::pumpkin_item::PumpkinItem; +use crate::server::Server; +use async_trait::async_trait; +use pumpkin_data::entity::EntityType; +use pumpkin_data::sound::Sound; +use pumpkin_macros::pumpkin_item; +use pumpkin_world::item::registry::Item; +#[pumpkin_item("minecraft:snowball")] +pub struct SnowBallItem; + +const POWER: f32 = 1.5; + +#[async_trait] +impl PumpkinItem for SnowBallItem { + async fn normal_use(&self, _block: &Item, player: &Player, server: &Server) { + let position = player.position(); + let world = player.world(); + world + .play_sound( + Sound::EntitySnowballThrow, + pumpkin_data::sound::SoundCategory::Neutral, + &position, + ) + .await; + let entity = server.add_entity(position, EntityType::Snowball, world); + let snowball = ThrownItem::new(entity, &player.living_entity.entity); + let yaw = player.living_entity.entity.yaw.load(); + let pitch = player.living_entity.entity.pitch.load(); + snowball.set_velocity_from(&player.living_entity.entity, pitch, yaw, 0.0, POWER, 1.0); + world.spawn_entity(Arc::new(snowball)).await; + } +} diff --git a/pumpkin/src/item/mod.rs b/pumpkin/src/item/mod.rs new file mode 100644 index 000000000..db0f38e01 --- /dev/null +++ b/pumpkin/src/item/mod.rs @@ -0,0 +1,18 @@ +use items::{egg::EggItem, snowball::SnowBallItem}; +use registry::ItemRegistry; + +use std::sync::Arc; + +mod items; +pub mod pumpkin_item; +pub mod registry; + +#[must_use] +pub fn default_registry() -> Arc { + let mut manager = ItemRegistry::default(); + + manager.register(SnowBallItem); + manager.register(EggItem); + + Arc::new(manager) +} diff --git a/pumpkin/src/item/pumpkin_item.rs b/pumpkin/src/item/pumpkin_item.rs new file mode 100644 index 000000000..52ec40408 --- /dev/null +++ b/pumpkin/src/item/pumpkin_item.rs @@ -0,0 +1,28 @@ +use crate::entity::player::Player; +use crate::server::Server; +use async_trait::async_trait; +use pumpkin_util::math::position::BlockPos; +use pumpkin_world::block::registry::Block; +use pumpkin_world::item::registry::Item; + +pub trait ItemMetadata { + const NAMESPACE: &'static str; + const ID: &'static str; + fn name(&self) -> String { + format!("{}:{}", Self::NAMESPACE, Self::ID) + } +} + +#[async_trait] +pub trait PumpkinItem: Send + Sync { + async fn normal_use(&self, _block: &Item, _player: &Player, _server: &Server) {} + async fn use_on_block( + &self, + _item: &Item, + _player: &Player, + _location: BlockPos, + _block: &Block, + _server: &Server, + ) { + } +} diff --git a/pumpkin/src/item/registry.rs b/pumpkin/src/item/registry.rs new file mode 100644 index 000000000..c604b3b6e --- /dev/null +++ b/pumpkin/src/item/registry.rs @@ -0,0 +1,49 @@ +use crate::entity::player::Player; +use crate::server::Server; +use pumpkin_util::math::position::BlockPos; +use pumpkin_world::block::registry::Block; +use pumpkin_world::item::registry::Item; +use std::collections::HashMap; +use std::sync::Arc; + +use super::pumpkin_item::{ItemMetadata, PumpkinItem}; + +#[derive(Default)] +pub struct ItemRegistry { + blocks: HashMap>, +} + +impl ItemRegistry { + pub fn register(&mut self, block: T) { + self.blocks.insert(block.name(), Arc::new(block)); + } + + pub async fn on_use(&self, item_name: &str, item: &Item, player: &Player, server: &Server) { + let pumpkin_block = self.get_pumpkin_item(item_name); + if let Some(pumpkin_block) = pumpkin_block { + pumpkin_block.normal_use(item, player, server).await; + } + } + + pub async fn use_on_block( + &self, + item_name: &str, + item: &Item, + player: &Player, + location: BlockPos, + block: &Block, + server: &Server, + ) { + let pumpkin_block = self.get_pumpkin_item(item_name); + if let Some(pumpkin_block) = pumpkin_block { + return pumpkin_block + .use_on_block(item, player, location, block, server) + .await; + } + } + + #[must_use] + pub fn get_pumpkin_item(&self, item_name: &str) -> Option<&Arc> { + self.blocks.get(format!("minecraft:{item_name}").as_str()) + } +} diff --git a/pumpkin/src/lib.rs b/pumpkin/src/lib.rs index e33758b2d..45e9a23e8 100644 --- a/pumpkin/src/lib.rs +++ b/pumpkin/src/lib.rs @@ -24,6 +24,7 @@ pub mod command; pub mod data; pub mod entity; pub mod error; +pub mod item; pub mod net; pub mod plugin; pub mod server; diff --git a/pumpkin/src/main.rs b/pumpkin/src/main.rs index 5e474bef0..c6d0276b7 100644 --- a/pumpkin/src/main.rs +++ b/pumpkin/src/main.rs @@ -58,6 +58,7 @@ pub mod command; pub mod data; pub mod entity; pub mod error; +pub mod item; pub mod net; pub mod plugin; pub mod server; diff --git a/pumpkin/src/net/packet/play.rs b/pumpkin/src/net/packet/play.rs index 70d600feb..f6878dee6 100644 --- a/pumpkin/src/net/packet/play.rs +++ b/pumpkin/src/net/packet/play.rs @@ -45,7 +45,7 @@ use pumpkin_util::{ }; use pumpkin_world::block::registry::get_block_collision_shapes; use pumpkin_world::block::registry::Block; -use pumpkin_world::item::registry::get_item_by_id; +use pumpkin_world::item::registry::{get_item_by_id, get_name_by_id}; use pumpkin_world::item::ItemStack; use pumpkin_world::{ block::{registry::get_block_by_item, BlockDirection}, @@ -236,10 +236,7 @@ impl Player { .on_ground .store(packet.ground, std::sync::atomic::Ordering::Relaxed); - entity.set_rotation( - wrap_degrees(packet.yaw) % 360.0, - wrap_degrees(packet.pitch).clamp(-90.0, 90.0) % 360.0, - ); + entity.set_rotation(wrap_degrees(packet.yaw) % 360.0, wrap_degrees(packet.pitch)); let entity_id = entity.entity_id; let Vector3 { x, y, z } = position; @@ -316,7 +313,7 @@ impl Player { .store(rotation.ground, std::sync::atomic::Ordering::Relaxed); entity.set_rotation( wrap_degrees(rotation.yaw) % 360.0, - wrap_degrees(rotation.pitch).clamp(-90.0, 90.0) % 360.0, + wrap_degrees(rotation.pitch), ); // send new position to all other players let entity_id = entity.entity_id; @@ -799,7 +796,7 @@ impl Player { if let Ok(block) = block { server .block_registry - .on_broken(block, &self, location, server) + .broken(block, &self, location, server) .await; } } @@ -837,7 +834,7 @@ impl Player { if let Ok(block) = block { server .block_registry - .on_broken(block, &self, location, server) + .broken(block, &self, location, server) .await; } } @@ -950,7 +947,7 @@ impl Player { { let action_result = server .block_registry - .on_use_with_item(block, self, location, item, server) + .use_with_item(block, self, location, item, server) .await; match action_result { BlockActionResult::Continue => {} @@ -995,12 +992,17 @@ impl Player { Ok(()) } - pub fn handle_use_item(&self, _use_item: &SUseItem) { + pub async fn handle_use_item(&self, _use_item: &SUseItem, server: &Server) { if !self.has_client_loaded() { return; } - // TODO: handle packet correctly - log::error!("An item was used(SUseItem), but the packet is not implemented yet"); + if let Some(held) = self.inventory().lock().await.held_item() { + // this is so ugly and slow brooo... + if let Some(item) = get_item_by_id(held.item_id) { + let name = get_name_by_id(item.id).unwrap(); + server.item_registry.on_use(name, item, self, server).await; + } + } } pub async fn handle_set_held_item(&self, held: SSetHeldItem) { @@ -1054,7 +1056,7 @@ impl Player { if let Some(block) = container.get_block() { server .block_registry - .on_close(&block, self, pos, server, container) //block, self, location, server) + .close(&block, self, pos, server, container) //block, self, location, server) .await; } } diff --git a/pumpkin/src/server/mod.rs b/pumpkin/src/server/mod.rs index fb36370fe..223daa96e 100644 --- a/pumpkin/src/server/mod.rs +++ b/pumpkin/src/server/mod.rs @@ -30,10 +30,11 @@ use std::{ }; use tokio::sync::{Mutex, RwLock}; +use crate::block::default_block_properties_manager; use crate::block::properties::BlockPropertiesManager; use crate::block::registry::BlockRegistry; -use crate::block::{self, default_block_properties_manager}; use crate::entity::{Entity, EntityId}; +use crate::item::registry::ItemRegistry; use crate::net::EncryptionError; use crate::world::custom_bossbar::CustomBossbars; use crate::{ @@ -61,6 +62,8 @@ pub struct Server { pub command_dispatcher: RwLock, /// Block Behaviour pub block_registry: Arc, + /// Item Behaviour + pub item_registry: Arc, /// Creates and stores block property registry and managed behaviours. pub block_properties_manager: Arc, /// Manages multiple worlds within the server. @@ -132,7 +135,8 @@ impl Server { DimensionType::TheEnd, ], command_dispatcher, - block_registry: block::default_registry(), + block_registry: super::block::default_registry(), + item_registry: super::item::default_registry(), block_properties_manager: default_block_properties_manager(), auth_client, key_store: KeyStore::new(),