diff --git a/pumpkin-data/build/build.rs b/pumpkin-data/build/build.rs index 5f30a2c07..6173c8ff1 100644 --- a/pumpkin-data/build/build.rs +++ b/pumpkin-data/build/build.rs @@ -1,9 +1,8 @@ -use quote::quote; +use quote::{format_ident, quote}; use std::{env, fs, path::Path, process::Command}; use heck::ToPascalCase; -use proc_macro2::{Span, TokenStream}; -use syn::Ident; +use proc_macro2::TokenStream; mod biome; mod chunk_status; @@ -43,7 +42,7 @@ pub fn array_to_tokenstream(array: Vec) -> TokenStream { let mut variants = TokenStream::new(); for item in array.iter() { - let name = ident(item.to_pascal_case()); + let name = format_ident!("{}", item.to_pascal_case()); variants.extend([quote! { #name, }]); @@ -62,12 +61,3 @@ pub fn write_generated_file(content: TokenStream, out_file: &str) { // Doesn't matter if rustfmt is unavailable. let _ = Command::new("rustfmt").arg(path).output(); } - -pub fn ident>(s: I) -> Ident { - let s = s.as_ref().trim(); - - // Parse the ident from a str. If the string is a Rust keyword, stick an - // underscore in front. - syn::parse_str::(s) - .unwrap_or_else(|_| Ident::new(format!("_{s}").as_str(), Span::call_site())) -} diff --git a/pumpkin-data/build/chunk_status.rs b/pumpkin-data/build/chunk_status.rs index 9ba3e72ab..7cae0edaa 100644 --- a/pumpkin-data/build/chunk_status.rs +++ b/pumpkin-data/build/chunk_status.rs @@ -1,8 +1,6 @@ use heck::ToPascalCase; use proc_macro2::TokenStream; -use quote::quote; - -use crate::ident; +use quote::{format_ident, quote}; pub(crate) fn build() -> TokenStream { println!("cargo:rerun-if-changed=../assets/chunk_status.json"); @@ -14,7 +12,7 @@ pub(crate) fn build() -> TokenStream { for status in chunk_status.iter() { let full_name = format!("minecraft:{status}"); - let name = ident(status.to_pascal_case()); + let name = format_ident!("{}", status.to_pascal_case()); variants.extend([quote! { #[serde(rename = #full_name)] #name, diff --git a/pumpkin-data/build/damage_type.rs b/pumpkin-data/build/damage_type.rs index 18526e90d..e7d681181 100644 --- a/pumpkin-data/build/damage_type.rs +++ b/pumpkin-data/build/damage_type.rs @@ -1,6 +1,6 @@ use heck::{ToPascalCase, ToShoutySnakeCase}; use proc_macro2::TokenStream; -use quote::quote; +use quote::{format_ident, quote}; use serde::Deserialize; use std::collections::HashMap; @@ -29,8 +29,8 @@ pub(crate) fn build() -> TokenStream { let mut enum_variants = Vec::new(); for (name, entry) in damage_types { - let const_ident = crate::ident(name.to_shouty_snake_case()); - let enum_ident = crate::ident(name.to_pascal_case()); + let const_ident = format_ident!("{}", name.to_shouty_snake_case()); + let enum_ident = format_ident!("{}", name.to_pascal_case()); enum_variants.push(enum_ident.clone()); @@ -58,7 +58,7 @@ pub(crate) fn build() -> TokenStream { let enum_arms = enum_variants.iter().map(|variant| { let const_name = variant.to_string().to_shouty_snake_case(); - let const_ident = crate::ident(&const_name); + let const_ident = format_ident!("{}", &const_name); quote! { DamageType::#variant => &#const_ident, } diff --git a/pumpkin-data/build/entity_type.rs b/pumpkin-data/build/entity_type.rs index 56c33e0fe..3043c5116 100644 --- a/pumpkin-data/build/entity_type.rs +++ b/pumpkin-data/build/entity_type.rs @@ -1,66 +1,101 @@ use std::collections::HashMap; -use heck::ToPascalCase; use proc_macro2::TokenStream; -use quote::quote; +use quote::{format_ident, quote, ToTokens}; use serde::Deserialize; use syn::LitInt; -use crate::ident; - #[derive(Deserialize)] -pub struct JSONStruct { - id: u16, +pub struct EntityType { + pub id: u16, + pub max_health: Option, + pub attackable: Option, + pub summonable: bool, + pub fire_immune: bool, + pub dimension: [f32; 2], + pub eye_height: f32, +} + +impl ToTokens for EntityType { + fn to_tokens(&self, tokens: &mut TokenStream) { + let id = LitInt::new(&self.id.to_string(), proc_macro2::Span::call_site()); + + let max_health = match self.max_health { + Some(mh) => quote! { Some(#mh) }, + None => quote! { None }, + }; + + let attackable = match self.attackable { + Some(a) => quote! { Some(#a) }, + None => quote! { None }, + }; + + let summonable = self.summonable; + let fire_immune = self.fire_immune; + let eye_height = self.eye_height; + + let dimension0 = self.dimension[0]; + let dimension1 = self.dimension[1]; + + tokens.extend(quote! { + EntityType { + id: #id, + max_health: #max_health, + attackable: #attackable, + summonable: #summonable, + fire_immune: #fire_immune, + dimension: [#dimension0, #dimension1], // Correctly construct the array + eye_height: #eye_height, + } + }); + } } pub(crate) fn build() -> TokenStream { println!("cargo:rerun-if-changed=../assets/entities.json"); - let json: HashMap = + let json: HashMap = serde_json::from_str(include_str!("../../assets/entities.json")) - .expect("Failed to parse sound_category.json"); - let mut variants = TokenStream::new(); + .expect("Failed to parse entities.json"); - for (item, id) in json.iter() { - let id = id.id as u8; + let mut consts = TokenStream::new(); + let mut type_from_raw_id_arms = TokenStream::new(); + let mut type_from_name = TokenStream::new(); + + for (name, entity) in json.iter() { + let id = entity.id as u8; let id_lit = LitInt::new(&id.to_string(), proc_macro2::Span::call_site()); - let name = ident(item.to_pascal_case()); - variants.extend([quote! { - #name = #id_lit, - }]); + let upper_name = format_ident!("{}", name.to_uppercase()); + + let entity_tokens = entity.to_token_stream(); + + consts.extend(quote! { + pub const #upper_name: EntityType = #entity_tokens; + }); + + type_from_raw_id_arms.extend(quote! { + #id_lit => Some(Self::#upper_name), + }); + + type_from_name.extend(quote! { + #name => Some(Self::#upper_name), + }); } - - let type_from_raw_id_arms = json - .iter() - .map(|sound| { - let id = &sound.1.id; - let name = ident(sound.0.to_pascal_case()); - - quote! { - #id => Some(Self::#name), - } - }) - .collect::(); - - let type_from_name = json - .iter() - .map(|sound| { - let id = &sound.0; - let name = ident(sound.0.to_pascal_case()); - - quote! { - #id => Some(Self::#name), - } - }) - .collect::(); - quote! { - #[derive(Clone, Copy, PartialEq, Eq, Debug)] - pub enum EntityType { - #variants + #[derive(Clone, Copy, Debug, PartialEq)] + pub struct EntityType { + pub id: u16, + pub max_health: Option, + pub attackable: Option, + pub summonable: bool, + pub fire_immune: bool, + pub dimension: [f32; 2], + pub eye_height: f32, } impl EntityType { + #consts + pub const fn from_raw(id: u16) -> Option { match id { #type_from_raw_id_arms diff --git a/pumpkin-data/build/message_type.rs b/pumpkin-data/build/message_type.rs index 71086bc5c..f86a3d036 100644 --- a/pumpkin-data/build/message_type.rs +++ b/pumpkin-data/build/message_type.rs @@ -2,11 +2,9 @@ use std::collections::HashMap; use proc_macro2::TokenStream; use pumpkin_util::text::style::Style; -use quote::quote; +use quote::{format_ident, quote}; use serde::{Deserialize, Serialize}; -use crate::ident; - #[derive(Debug, Clone, Serialize, Deserialize)] pub struct RawChatType { id: u32, @@ -37,7 +35,7 @@ pub(crate) fn build() -> TokenStream { for (name, typee) in json.iter() { let i = typee.id; - let name = ident(name.to_uppercase()); + let name = format_ident!("{}", name.to_uppercase()); variants.extend([quote! { pub const #name: u32 = #i; }]); diff --git a/pumpkin-data/build/noise_parameter.rs b/pumpkin-data/build/noise_parameter.rs index 1a113cfa2..830b4bc30 100644 --- a/pumpkin-data/build/noise_parameter.rs +++ b/pumpkin-data/build/noise_parameter.rs @@ -1,11 +1,9 @@ use std::collections::HashMap; use proc_macro2::TokenStream; -use quote::quote; +use quote::{format_ident, quote}; use serde::Deserialize; -use crate::ident; - #[derive(Deserialize)] pub struct DoublePerlinNoiseParameters { #[serde(rename = "firstOctave")] @@ -23,7 +21,7 @@ pub(crate) fn build() -> TokenStream { for (name, parameter) in json.iter() { let raw_name = format!("minecraft:{name}"); - let name = ident(name.to_uppercase()); + let name = format_ident!("{}", name.to_uppercase()); let first_octave = parameter.first_octave; let amplitudes = ¶meter.amplitudes; variants.extend([quote! { diff --git a/pumpkin-data/build/packet.rs b/pumpkin-data/build/packet.rs index 4b5d7413c..f481bd412 100644 --- a/pumpkin-data/build/packet.rs +++ b/pumpkin-data/build/packet.rs @@ -1,11 +1,9 @@ use std::collections::HashMap; use proc_macro2::TokenStream; -use quote::quote; +use quote::{format_ident, quote}; use serde::Deserialize; -use crate::ident; - #[derive(Deserialize)] pub struct Packets { serverbound: HashMap>, @@ -40,7 +38,7 @@ pub(crate) fn parse_packets(packets: HashMap>) -> proc_macro for (id, packet_name) in packet.1.iter().enumerate() { let packet_id = id as i32; let name = format!("{phase}_{packet_name}").to_uppercase(); - let name = ident(name); + let name = format_ident!("{}", name); consts.extend([quote! { pub const #name: i32 = #packet_id; }]); diff --git a/pumpkin-data/build/sound.rs b/pumpkin-data/build/sound.rs index 6ae0f8d3c..dfb74001f 100644 --- a/pumpkin-data/build/sound.rs +++ b/pumpkin-data/build/sound.rs @@ -1,8 +1,8 @@ use heck::ToPascalCase; use proc_macro2::TokenStream; -use quote::quote; +use quote::{format_ident, quote}; -use crate::{array_to_tokenstream, ident}; +use crate::array_to_tokenstream; pub(crate) fn build() -> TokenStream { println!("cargo:rerun-if-changed=../assets/sounds.json"); @@ -15,7 +15,7 @@ pub(crate) fn build() -> TokenStream { .iter() .map(|sound| { let id = &sound; - let name = ident(sound.to_pascal_case()); + let name = format_ident!("{}", sound.to_pascal_case()); quote! { #id => Some(Self::#name), @@ -27,7 +27,7 @@ pub(crate) fn build() -> TokenStream { .iter() .map(|sound| { let id = &sound; - let name = ident(sound.to_pascal_case()); + let name = format_ident!("{}", sound.to_pascal_case()); quote! { Self::#name => #id, diff --git a/pumpkin-data/build/world_event.rs b/pumpkin-data/build/world_event.rs index 5382567c8..7b547d6f1 100644 --- a/pumpkin-data/build/world_event.rs +++ b/pumpkin-data/build/world_event.rs @@ -2,9 +2,7 @@ use std::collections::HashMap; use heck::ToPascalCase; use proc_macro2::TokenStream; -use quote::quote; - -use crate::ident; +use quote::{format_ident, quote}; pub(crate) fn build() -> TokenStream { println!("cargo:rerun-if-changed=../assets/world_event.json"); @@ -15,7 +13,7 @@ pub(crate) fn build() -> TokenStream { let mut variants = TokenStream::new(); for (event, id) in events.iter() { - let name = ident(event.to_pascal_case()); + let name = format_ident!("{}", event.to_pascal_case()); variants.extend([quote! { #name = #id, }]); diff --git a/pumpkin-protocol/src/client/play/entity_velocity.rs b/pumpkin-protocol/src/client/play/entity_velocity.rs index 7228a2c63..617eeac0b 100644 --- a/pumpkin-protocol/src/client/play/entity_velocity.rs +++ b/pumpkin-protocol/src/client/play/entity_velocity.rs @@ -1,5 +1,6 @@ use pumpkin_data::packet::clientbound::PLAY_SET_ENTITY_MOTION; use pumpkin_macros::client_packet; +use pumpkin_util::math::vector3::Vector3; use serde::Serialize; use crate::VarInt; @@ -8,18 +9,18 @@ use crate::VarInt; #[client_packet(PLAY_SET_ENTITY_MOTION)] pub struct CEntityVelocity<'a> { entity_id: &'a VarInt, - velocity_x: i16, - velocity_y: i16, - velocity_z: i16, + velocity: Vector3, } impl<'a> CEntityVelocity<'a> { pub fn new(entity_id: &'a VarInt, velocity_x: f64, velocity_y: f64, velocity_z: f64) -> Self { Self { entity_id, - velocity_x: (velocity_x.clamp(-3.9, 3.9) * 8000.0) as i16, - velocity_y: (velocity_y.clamp(-3.9, 3.9) * 8000.0) as i16, - velocity_z: (velocity_z.clamp(-3.9, 3.9) * 8000.0) as i16, + velocity: Vector3::new( + (velocity_x.clamp(-3.9, 3.9) * 8000.0) as i16, + (velocity_y.clamp(-3.9, 3.9) * 8000.0) as i16, + (velocity_z.clamp(-3.9, 3.9) * 8000.0) as i16, + ), } } } diff --git a/pumpkin-protocol/src/client/play/sound_effect.rs b/pumpkin-protocol/src/client/play/sound_effect.rs index 46249bd0e..35bb02418 100644 --- a/pumpkin-protocol/src/client/play/sound_effect.rs +++ b/pumpkin-protocol/src/client/play/sound_effect.rs @@ -1,6 +1,7 @@ use bytes::BufMut; use pumpkin_data::{packet::clientbound::PLAY_SOUND, sound::SoundCategory}; use pumpkin_macros::client_packet; +use pumpkin_util::math::vector3::Vector3; use crate::{bytebuf::ByteBufMut, ClientPacket, IDOrSoundEvent, SoundEvent, VarInt}; @@ -8,23 +9,18 @@ use crate::{bytebuf::ByteBufMut, ClientPacket, IDOrSoundEvent, SoundEvent, VarIn pub struct CSoundEffect { sound_event: IDOrSoundEvent, sound_category: VarInt, - effect_position_x: i32, - effect_position_y: i32, - effect_position_z: i32, + position: Vector3, volume: f32, pitch: f32, seed: f64, } impl CSoundEffect { - #[allow(clippy::too_many_arguments)] pub fn new( sound_id: VarInt, sound_event: Option, sound_category: SoundCategory, - effect_position_x: f64, - effect_position_y: f64, - effect_position_z: f64, + position: &Vector3, volume: f32, pitch: f32, seed: f64, @@ -35,9 +31,11 @@ impl CSoundEffect { sound_event, }, sound_category: VarInt(sound_category as i32), - effect_position_x: (effect_position_x * 8.0) as i32, - effect_position_y: (effect_position_y * 8.0) as i32, - effect_position_z: (effect_position_z * 8.0) as i32, + position: Vector3::new( + (position.x * 8.0) as i32, + (position.y * 8.0) as i32, + (position.z * 8.0) as i32, + ), volume, pitch, seed, @@ -58,9 +56,9 @@ impl ClientPacket for CSoundEffect { } } bytebuf.put_var_int(&self.sound_category); - bytebuf.put_i32(self.effect_position_x); - bytebuf.put_i32(self.effect_position_y); - bytebuf.put_i32(self.effect_position_z); + bytebuf.put_i32(self.position.x); + bytebuf.put_i32(self.position.y); + bytebuf.put_i32(self.position.z); bytebuf.put_f32(self.volume); bytebuf.put_f32(self.pitch); bytebuf.put_f64(self.seed); diff --git a/pumpkin-protocol/src/client/play/spawn_entity.rs b/pumpkin-protocol/src/client/play/spawn_entity.rs index 7888e8bfe..8527c734d 100644 --- a/pumpkin-protocol/src/client/play/spawn_entity.rs +++ b/pumpkin-protocol/src/client/play/spawn_entity.rs @@ -1,5 +1,6 @@ use pumpkin_data::packet::clientbound::PLAY_ADD_ENTITY; use pumpkin_macros::client_packet; +use pumpkin_util::math::vector3::Vector3; use serde::Serialize; use crate::VarInt; @@ -11,16 +12,12 @@ pub struct CSpawnEntity { #[serde(with = "uuid::serde::compact")] entity_uuid: uuid::Uuid, typ: VarInt, - x: f64, - y: f64, - z: f64, + position: Vector3, pitch: u8, // angle yaw: u8, // angle head_yaw: u8, // angle data: VarInt, - velocity_x: i16, - velocity_y: i16, - velocity_z: i16, + velocity: Vector3, } impl CSpawnEntity { @@ -29,31 +26,27 @@ impl CSpawnEntity { entity_id: VarInt, entity_uuid: uuid::Uuid, typ: VarInt, - x: f64, - y: f64, - z: f64, + position: Vector3, pitch: f32, // angle yaw: f32, // angle head_yaw: f32, // angle data: VarInt, - velocity_x: f32, - velocity_y: f32, - velocity_z: f32, + velocity: Vector3, ) -> Self { Self { entity_id, entity_uuid, typ, - x, - y, - z, + position, pitch: (pitch * 256.0 / 360.0).floor() as u8, yaw: (yaw * 256.0 / 360.0).floor() as u8, head_yaw: (head_yaw * 256.0 / 360.0).floor() as u8, data, - velocity_x: (velocity_x.clamp(-3.9, 3.9) * 8000.0) as i16, - velocity_y: (velocity_y.clamp(-3.9, 3.9) * 8000.0) as i16, - velocity_z: (velocity_z.clamp(-3.9, 3.9) * 8000.0) as i16, + velocity: Vector3::new( + (velocity.x.clamp(-3.9, 3.9) * 8000.0) as i16, + (velocity.x.clamp(-3.9, 3.9) * 8000.0) as i16, + (velocity.x.clamp(-3.9, 3.9) * 8000.0) as i16, + ), } } } diff --git a/pumpkin-util/src/math/boundingbox.rs b/pumpkin-util/src/math/boundingbox.rs index 415035ce0..cc8a4532f 100644 --- a/pumpkin-util/src/math/boundingbox.rs +++ b/pumpkin-util/src/math/boundingbox.rs @@ -7,15 +7,15 @@ pub struct BoundingBox { } impl BoundingBox { - pub fn new_default(size: &BoundingBoxSize) -> Self { + pub fn new_default(size: &EntityDimensions) -> Self { Self::new_from_pos(0., 0., 0., size) } - pub fn new_from_pos(x: f64, y: f64, z: f64, size: &BoundingBoxSize) -> Self { - let f = size.width / 2.; + pub fn new_from_pos(x: f64, y: f64, z: f64, size: &EntityDimensions) -> Self { + let f = size.width as f64 / 2.; Self { min: Vector3::new(x - f, y, z - f), - max: Vector3::new(x + f, y + size.height, z + f), + max: Vector3::new(x + f, y + size.height as f64, z + f), } } @@ -75,7 +75,7 @@ impl BoundingBox { } #[derive(Clone, Copy, Debug)] -pub struct BoundingBoxSize { - pub width: f64, - pub height: f64, +pub struct EntityDimensions { + pub width: f32, + pub height: f32, } diff --git a/pumpkin-world/src/entity/mod.rs b/pumpkin-world/src/entity/mod.rs deleted file mode 100644 index d10899055..000000000 --- a/pumpkin-world/src/entity/mod.rs +++ /dev/null @@ -1 +0,0 @@ -pub mod registry; diff --git a/pumpkin-world/src/entity/registry.rs b/pumpkin-world/src/entity/registry.rs deleted file mode 100644 index bbed23a77..000000000 --- a/pumpkin-world/src/entity/registry.rs +++ /dev/null @@ -1,35 +0,0 @@ -use std::{collections::HashMap, sync::LazyLock}; - -use serde::Deserialize; - -const ENTITIES_JSON: &str = include_str!("../../../assets/entities.json"); - -pub static ENTITIES: LazyLock> = LazyLock::new(|| { - serde_json::from_str(ENTITIES_JSON).expect("Could not parse entity.json registry.") -}); - -pub static ENTITIES_BY_ID: LazyLock> = LazyLock::new(|| { - let mut map = HashMap::new(); - for (entity_name, entity) in ENTITIES.iter() { - map.insert(entity_name.clone(), entity.id); - } - map -}); - -pub fn get_entity_id(name: &str) -> Option<&u16> { - ENTITIES_BY_ID.get(&name.replace("minecraft:", "")) -} - -pub fn get_entity_by_id<'a>(entity_id: u16) -> Option<&'a Entity> { - ENTITIES.values().find(|&entity| entity.id == entity_id) -} - -#[derive(Deserialize, Clone, Debug)] -pub struct Entity { - pub id: u16, - pub max_health: Option, - pub attackable: Option, - pub summonable: bool, - pub fire_immune: bool, - pub dimension: [f32; 2], -} diff --git a/pumpkin-world/src/lib.rs b/pumpkin-world/src/lib.rs index 1ff8ea913..0a479b8bb 100644 --- a/pumpkin-world/src/lib.rs +++ b/pumpkin-world/src/lib.rs @@ -13,7 +13,6 @@ pub mod chunk; pub mod coordinates; pub mod cylindrical_chunk_iterator; pub mod dimension; -pub mod entity; mod generation; pub mod item; pub mod level; diff --git a/pumpkin/src/block/mod.rs b/pumpkin/src/block/mod.rs index bf5ccb35b..bedc8430c 100644 --- a/pumpkin/src/block/mod.rs +++ b/pumpkin/src/block/mod.rs @@ -1,10 +1,19 @@ use blocks::chest::ChestBlock; use blocks::furnace::FurnaceBlock; use properties::BlockPropertiesManager; +use pumpkin_data::entity::EntityType; +use pumpkin_util::math::position::BlockPos; +use pumpkin_util::math::vector3::Vector3; +use pumpkin_world::block::registry::Block; +use pumpkin_world::item::ItemStack; +use rand::Rng; use crate::block::blocks::crafting_table::CraftingTableBlock; use crate::block::blocks::jukebox::JukeboxBlock; use crate::block::registry::BlockRegistry; +use crate::entity::item::ItemEntity; +use crate::server::Server; +use crate::world::World; use std::sync::Arc; mod blocks; @@ -24,6 +33,21 @@ pub fn default_registry() -> Arc { Arc::new(manager) } +pub async fn drop_loot(server: &Server, world: &Arc, block: &Block, pos: &BlockPos) { + // TODO: Currently only the item block is droped, We should drop the loop table + let height = EntityType::ITEM.dimension[1] / 2.0; + let pos = Vector3::new( + f64::from(pos.0.x) + 0.5 + rand::thread_rng().gen_range(-0.25..0.25), + f64::from(pos.0.y) + 0.5 + rand::thread_rng().gen_range(-0.25..0.25) - f64::from(height), + f64::from(pos.0.z) + 0.5 + rand::thread_rng().gen_range(-0.25..0.25), + ); + + let entity = server.add_entity(pos, EntityType::ITEM, world); + let item_entity = Arc::new(ItemEntity::new(entity, &ItemStack::new(1, block.item_id))); + world.spawn_entity(item_entity.clone()).await; + item_entity.send_meta_packet().await; +} + #[must_use] pub fn default_block_properties_manager() -> Arc { let mut manager = BlockPropertiesManager::default(); diff --git a/pumpkin/src/command/commands/fill.rs b/pumpkin/src/command/commands/fill.rs index 40e6b53f3..34a6df5eb 100644 --- a/pumpkin/src/command/commands/fill.rs +++ b/pumpkin/src/command/commands/fill.rs @@ -41,7 +41,7 @@ impl CommandExecutor for SetblockExecutor { async fn execute<'a>( &self, sender: &mut CommandSender<'a>, - _server: &crate::server::Server, + server: &crate::server::Server, args: &ConsumedArgs<'a>, ) -> Result<(), CommandError> { let block = BlockArgumentConsumer::find_arg(args, ARG_BLOCK)?; @@ -67,7 +67,9 @@ impl CommandExecutor for SetblockExecutor { for y in start_y..=end_y { for z in start_z..=end_z { let block_position = BlockPos(Vector3 { x, y, z }); - world.break_block(&block_position, None).await; + world + .break_block(server, &block_position, None, false) + .await; world.set_block_state(&block_position, block_state_id).await; placed_blocks += 1; } diff --git a/pumpkin/src/command/commands/kill.rs b/pumpkin/src/command/commands/kill.rs index 0cf97d3c8..fd778a15b 100644 --- a/pumpkin/src/command/commands/kill.rs +++ b/pumpkin/src/command/commands/kill.rs @@ -46,7 +46,7 @@ impl CommandExecutor for KillExecutor { Some(TextComponent::text(name.clone())), )); - if entity.entity_type == entity::EntityType::Player { + if entity.entity_type == entity::EntityType::PLAYER { entity_display = entity_display.click_event(ClickEvent::SuggestCommand( format!("/tell {} ", name.clone()).into(), )); diff --git a/pumpkin/src/command/commands/setblock.rs b/pumpkin/src/command/commands/setblock.rs index 376715297..2e55427e6 100644 --- a/pumpkin/src/command/commands/setblock.rs +++ b/pumpkin/src/command/commands/setblock.rs @@ -34,7 +34,7 @@ impl CommandExecutor for SetblockExecutor { async fn execute<'a>( &self, sender: &mut CommandSender<'a>, - _server: &crate::server::Server, + server: &crate::server::Server, args: &ConsumedArgs<'a>, ) -> Result<(), CommandError> { let block = BlockArgumentConsumer::find_arg(args, ARG_BLOCK)?; @@ -46,7 +46,7 @@ impl CommandExecutor for SetblockExecutor { let success = match mode { Mode::Destroy => { - world.break_block(&pos, None).await; + world.clone().break_block(server, &pos, None, false).await; world.set_block_state(&pos, block_state_id).await; true } diff --git a/pumpkin/src/command/commands/teleport.rs b/pumpkin/src/command/commands/teleport.rs index 81cf00e6f..c1d9170a9 100644 --- a/pumpkin/src/command/commands/teleport.rs +++ b/pumpkin/src/command/commands/teleport.rs @@ -43,8 +43,8 @@ fn yaw_pitch_facing_position( let yaw_radians = -direction_vector.x.atan2(direction_vector.z); let pitch_radians = (-direction_vector.y).asin(); - let yaw_degrees = yaw_radians * 180.0 / std::f64::consts::PI; - let pitch_degrees = pitch_radians * 180.0 / std::f64::consts::PI; + let yaw_degrees = yaw_radians.to_degrees(); + let pitch_degrees = pitch_radians.to_degrees(); (yaw_degrees as f32, pitch_degrees as f32) } diff --git a/pumpkin/src/command/mod.rs b/pumpkin/src/command/mod.rs index 5847e04d9..0da5ba1a9 100644 --- a/pumpkin/src/command/mod.rs +++ b/pumpkin/src/command/mod.rs @@ -97,7 +97,7 @@ impl CommandSender<'_> { } #[must_use] - pub fn world(&self) -> Option<&World> { + pub fn world(&self) -> Option<&Arc> { match self { // TODO: maybe return first world when console CommandSender::Console | CommandSender::Rcon(..) => None, diff --git a/pumpkin/src/entity/combat.rs b/pumpkin/src/entity/combat.rs index 8125635a4..a509289e6 100644 --- a/pumpkin/src/entity/combat.rs +++ b/pumpkin/src/entity/combat.rs @@ -1,5 +1,3 @@ -use std::f32::consts::PI; - use pumpkin_data::{ particle::Particle, sound::{Sound, SoundCategory}, @@ -74,8 +72,8 @@ pub async fn handle_knockback( let saved_velo = victim_entity.velocity.load(); victim_entity.knockback( strength * 0.5, - f64::from((yaw * (PI / 180.0)).sin()), - f64::from(-(yaw * (PI / 180.0)).cos()), + f64::from((yaw.to_radians()).sin()), + f64::from(-(yaw.to_radians()).cos()), ); let entity_id = VarInt(victim_entity.entity_id); @@ -98,8 +96,8 @@ pub async fn handle_knockback( pub async fn spawn_sweep_particle(attacker_entity: &Entity, world: &World, pos: &Vector3) { let yaw = attacker_entity.yaw.load(); - let d = -f64::from((yaw * (PI / 180.0)).sin()); - let e = f64::from((yaw * (PI / 180.0)).cos()); + let d = -f64::from((yaw.to_radians()).sin()); + let e = f64::from((yaw.to_radians()).cos()); let scale = 0.5; // TODO: use entity height diff --git a/pumpkin/src/entity/mob/mod.rs b/pumpkin/src/entity/mob/mod.rs index 08e462aff..3b4626f15 100644 --- a/pumpkin/src/entity/mob/mod.rs +++ b/pumpkin/src/entity/mob/mod.rs @@ -64,7 +64,7 @@ pub async fn from_type( }; #[expect(clippy::single_match)] match entity_type { - EntityType::Zombie => Zombie::make(&mob).await, + EntityType::ZOMBIE => Zombie::make(&mob).await, // TODO _ => (), } diff --git a/pumpkin/src/entity/mod.rs b/pumpkin/src/entity/mod.rs index 8b3e0e517..2876eef6f 100644 --- a/pumpkin/src/entity/mod.rs +++ b/pumpkin/src/entity/mod.rs @@ -1,8 +1,5 @@ use core::f32; -use std::{ - f32::consts::PI, - sync::{atomic::AtomicBool, Arc}, -}; +use std::sync::{atomic::AtomicBool, Arc}; use async_trait::async_trait; use crossbeam::atomic::AtomicCell; @@ -22,7 +19,7 @@ use pumpkin_protocol::{ codec::var_int::VarInt, }; use pumpkin_util::math::{ - boundingbox::{BoundingBox, BoundingBoxSize}, + boundingbox::{BoundingBox, EntityDimensions}, get_section_cord, position::BlockPos, vector2::Vector2, @@ -94,7 +91,7 @@ pub struct Entity { /// The bounding box of an entity (hitbox) pub bounding_box: AtomicCell, ///The size (width and height) of the bounding box - pub bounding_box_size: AtomicCell, + pub bounding_box_size: AtomicCell, /// Whether this entity is invulnerable to all damage pub invulnerable: AtomicBool, /// List of damage types this entity is immune to @@ -111,7 +108,7 @@ impl Entity { entity_type: EntityType, standing_eye_height: f32, bounding_box: AtomicCell, - bounding_box_size: AtomicCell, + bounding_box_size: AtomicCell, invulnerable: bool, ) -> Self { let floor_x = position.x.floor() as i32; @@ -187,8 +184,8 @@ impl Entity { /// Returns entity rotation as vector pub fn rotation(&self) -> Vector3 { // Convert degrees to radians if necessary - let yaw_rad = self.yaw.load() * (PI / 180.0); - let pitch_rad = self.pitch.load() * (PI / 180.0); + let yaw_rad = self.yaw.load().to_radians(); + let pitch_rad = self.pitch.load().to_radians(); Vector3::new( yaw_rad.cos() * pitch_rad.cos(), @@ -260,17 +257,13 @@ impl Entity { CSpawnEntity::new( VarInt(self.entity_id), self.entity_uuid, - VarInt((self.entity_type) as i32), - entity_loc.x, - entity_loc.y, - entity_loc.z, + VarInt(i32::from(self.entity_type.id)), + entity_loc, self.pitch.load(), self.yaw.load(), self.head_yaw.load(), // todo: head_yaw and yaw are swapped, find out why 0.into(), - entity_vel.x as f32, - entity_vel.y as f32, - entity_vel.z as f32, + entity_vel, ) } diff --git a/pumpkin/src/entity/player.rs b/pumpkin/src/entity/player.rs index 93310e183..29180d217 100644 --- a/pumpkin/src/entity/player.rs +++ b/pumpkin/src/entity/player.rs @@ -47,7 +47,7 @@ use pumpkin_protocol::{ }; use pumpkin_util::{ math::{ - boundingbox::{BoundingBox, BoundingBoxSize}, + boundingbox::{BoundingBox, EntityDimensions}, experience, position::BlockPos, vector2::Vector2, @@ -169,9 +169,9 @@ impl Player { let gameprofile_clone = gameprofile.clone(); let config = client.config.lock().await.clone().unwrap_or_default(); - let bounding_box_size = BoundingBoxSize { - width: 0.6, - height: 1.8, + let bounding_box_size = EntityDimensions { + width: EntityType::PLAYER.dimension[0], + height: EntityType::PLAYER.dimension[1], }; Self { @@ -180,8 +180,8 @@ impl Player { player_uuid, world, Vector3::new(0.0, 0.0, 0.0), - EntityType::Player, - 1.62, + EntityType::PLAYER, + EntityType::PLAYER.eye_height, AtomicCell::new(BoundingBox::new_default(&bounding_box_size)), AtomicCell::new(bounding_box_size), matches!(gamemode, GameMode::Creative | GameMode::Spectator), @@ -404,9 +404,7 @@ impl Player { VarInt(i32::from(sound_id)), None, category, - position.x, - position.y, - position.z, + position, volume, pitch, seed, @@ -791,7 +789,7 @@ impl Player { if let Some(item) = inv.held_item_mut() { let entity = server.add_entity( self.living_entity.entity.pos.load(), - EntityType::Item, + EntityType::ITEM, self.world(), ); let item_entity = Arc::new(ItemEntity::new(entity, &item.clone())); diff --git a/pumpkin/src/entity/projectile/mod.rs b/pumpkin/src/entity/projectile/mod.rs index 5319e2a5e..00294642b 100644 --- a/pumpkin/src/entity/projectile/mod.rs +++ b/pumpkin/src/entity/projectile/mod.rs @@ -1,4 +1,4 @@ -use std::f32::{self, consts::PI}; +use std::f32::{self}; use pumpkin_util::math::vector3::Vector3; @@ -8,8 +8,6 @@ pub struct ThrownItemEntity { entity: Entity, } -const DEG_PER_RAD_F32: f32 = 180.0 / PI; - impl ThrownItemEntity { pub fn new(entity: Entity, owner: &Entity) -> Self { let mut owner_pos = owner.pos.load(); @@ -69,8 +67,8 @@ impl ThrownItemEntity { self.entity.velocity.store(velocity); let len = velocity.horizontal_length(); self.entity.set_rotation( - velocity.x.atan2(velocity.z) as f32 * DEG_PER_RAD_F32, - velocity.y.atan2(len) as f32 * DEG_PER_RAD_F32, + velocity.x.atan2(velocity.z).to_degrees() as f32, + velocity.y.atan2(len).to_degrees() as f32, ); } } diff --git a/pumpkin/src/item/items/egg.rs b/pumpkin/src/item/items/egg.rs index d11a23551..e040c0c0c 100644 --- a/pumpkin/src/item/items/egg.rs +++ b/pumpkin/src/item/items/egg.rs @@ -27,7 +27,7 @@ impl PumpkinItem for EggItem { ) .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 entity = server.add_entity(position, EntityType::EGG, world); let snowball = ThrownItemEntity::new(entity, &player.living_entity.entity); let yaw = player.living_entity.entity.yaw.load(); let pitch = player.living_entity.entity.pitch.load(); diff --git a/pumpkin/src/item/items/snowball.rs b/pumpkin/src/item/items/snowball.rs index f0cacc687..a944ec39f 100644 --- a/pumpkin/src/item/items/snowball.rs +++ b/pumpkin/src/item/items/snowball.rs @@ -26,7 +26,7 @@ impl PumpkinItem for SnowBallItem { &position, ) .await; - let entity = server.add_entity(position, EntityType::Snowball, world); + let entity = server.add_entity(position, EntityType::SNOWBALL, world); let snowball = ThrownItemEntity::new(entity, &player.living_entity.entity); let yaw = player.living_entity.entity.yaw.load(); let pitch = player.living_entity.entity.pitch.load(); diff --git a/pumpkin/src/net/packet/play.rs b/pumpkin/src/net/packet/play.rs index 3ca631c4a..c36d000d0 100644 --- a/pumpkin/src/net/packet/play.rs +++ b/pumpkin/src/net/packet/play.rs @@ -53,7 +53,6 @@ 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}, - entity::registry::get_entity_id, item::registry::get_spawn_egg, }; @@ -820,8 +819,9 @@ impl Player { let world = &entity.world; let block = world.get_block(&location).await; - world.break_block(&location, Some(self.clone())).await; - + world + .break_block(server, &location, Some(self.clone()), false) + .await; if let Ok(block) = block { server .block_registry @@ -864,7 +864,14 @@ impl Player { let world = &entity.world; let block = world.get_block(&location).await; - world.break_block(&location, Some(self.clone())).await; + world + .break_block( + server, + &location, + Some(self.clone()), + self.gamemode.load() != GameMode::Creative, + ) + .await; if let Ok(block) = block { server @@ -1171,7 +1178,7 @@ impl Player { face: &BlockDirection, ) -> Result> { // checks if spawn egg has a corresponding entity name - if let Some(spawn_item_id) = get_entity_id(&item_t) { + if let Some(entity_type) = EntityType::from_name(&item_t) { let world_pos = BlockPos(location.0 + face.to_offset()); // align position like Vanilla does let pos = Vector3::new( @@ -1185,7 +1192,7 @@ impl Player { let world = self.world(); // create new mob and uuid based on spawn egg id let mob = mob::from_type( - EntityType::from_raw(*spawn_item_id).unwrap(), + EntityType::from_raw(entity_type.id).unwrap(), server, pos, world, diff --git a/pumpkin/src/server/mod.rs b/pumpkin/src/server/mod.rs index 2cb2b3538..cec354fd6 100644 --- a/pumpkin/src/server/mod.rs +++ b/pumpkin/src/server/mod.rs @@ -8,14 +8,13 @@ use pumpkin_inventory::{Container, OpenContainer}; use pumpkin_protocol::client::login::CEncryptionRequest; use pumpkin_protocol::{client::config::CPluginMessage, ClientPacket}; use pumpkin_registry::{DimensionType, Registry}; -use pumpkin_util::math::boundingbox::{BoundingBox, BoundingBoxSize}; +use pumpkin_util::math::boundingbox::{BoundingBox, EntityDimensions}; use pumpkin_util::math::position::BlockPos; use pumpkin_util::math::vector2::Vector2; use pumpkin_util::math::vector3::Vector3; use pumpkin_util::text::TextComponent; use pumpkin_world::block::registry::Block; use pumpkin_world::dimension::Dimension; -use pumpkin_world::entity::registry::get_entity_by_id; use rand::prelude::SliceRandom; use std::collections::HashMap; use std::net::IpAddr; @@ -221,16 +220,10 @@ impl Server { let entity_id = self.new_entity_id(); // TODO: this should be resolved to a integer using a macro when calling this function - let bounding_box_size = get_entity_by_id(entity_type as u16).map_or( - BoundingBoxSize { - width: 0.6, - height: 1.8, - }, - |entity| BoundingBoxSize { - width: f64::from(entity.dimension[0]), - height: f64::from(entity.dimension[1]), - }, - ); + let bounding_box_size = EntityDimensions { + width: entity_type.dimension[0], + height: entity_type.dimension[1], + }; // TODO: standing eye height should be per mob let new_uuid = uuid::Uuid::new_v4(); @@ -240,7 +233,7 @@ impl Server { world.clone(), position, entity_type, - 1.62, + entity_type.eye_height, AtomicCell::new(BoundingBox::new_from_pos( position.x, position.y, diff --git a/pumpkin/src/world/mod.rs b/pumpkin/src/world/mod.rs index 25fe824b2..20c77e706 100644 --- a/pumpkin/src/world/mod.rs +++ b/pumpkin/src/world/mod.rs @@ -7,6 +7,7 @@ pub mod chunker; pub mod time; use crate::{ + block, command::client_suggestions, entity::{player::Player, Entity, EntityBase, EntityId}, error::PumpkinError, @@ -423,17 +424,13 @@ impl World { &CSpawnEntity::new( entity_id.into(), gameprofile.id, - (EntityType::Player as i32).into(), - position.x, - position.y, - position.z, + i32::from(EntityType::PLAYER.id).into(), + position, pitch, yaw, yaw, 0.into(), - 0.0, - 0.0, - 0.0, + Vector3::new(0.0, 0.0, 0.0), ), ) .await; @@ -449,17 +446,13 @@ impl World { .send_packet(&CSpawnEntity::new( existing_player.entity_id().into(), gameprofile.id, - (EntityType::Player as i32).into(), - pos.x, - pos.y, - pos.z, + i32::from(EntityType::PLAYER.id).into(), + pos, entity.yaw.load(), entity.pitch.load(), entity.head_yaw.load(), 0.into(), - 0.0, - 0.0, - 0.0, + Vector3::new(0.0, 0.0, 0.0), )) .await; } @@ -594,17 +587,13 @@ impl World { &CSpawnEntity::new( entity.entity_id.into(), player.gameprofile.id, - (EntityType::Player as i32).into(), - position.x, - position.y, - position.z, + i32::from(EntityType::PLAYER.id).into(), + position, pitch, yaw, yaw, 0.into(), - 0.0, - 0.0, - 0.0, + Vector3::new(0.0, 0.0, 0.0), ), ) .await; @@ -1025,7 +1014,13 @@ impl World { chunk } - pub async fn break_block(&self, position: &BlockPos, cause: Option>) { + pub async fn break_block( + self: &Arc, + server: &Server, + position: &BlockPos, + cause: Option>, + drop: bool, + ) { let block = self.get_block(position).await.unwrap(); let event = BlockBreakEvent::new(cause.clone(), block.clone(), 0, false); @@ -1045,6 +1040,10 @@ impl World { false, ); + if drop { + block::drop_loot(server, self, block, position).await; + } + match cause { Some(player) => { self.broadcast_packet_except(&[player.gameprofile.id], &particles_packet)