From fa07af7308fceeace578726d9cf587c5d1ac6bc2 Mon Sep 17 00:00:00 2001 From: Alexander Medvedev Date: Thu, 2 Jan 2025 15:47:11 +0100 Subject: [PATCH] shorten compile times but only by a bit :c --- Cargo.toml | 4 -- pumpkin-core/Cargo.toml | 4 +- pumpkin-core/src/gamemode.rs | 16 +++++++- pumpkin-core/src/permission.rs | 3 +- pumpkin-inventory/Cargo.toml | 2 - pumpkin-inventory/src/drag_handler.rs | 10 +++-- pumpkin-inventory/src/lib.rs | 3 +- pumpkin-inventory/src/window_property.rs | 23 ++++-------- pumpkin-protocol/Cargo.toml | 2 - pumpkin-protocol/src/bytebuf/mod.rs | 5 +-- pumpkin-protocol/src/lib.rs | 20 +++++----- pumpkin-protocol/src/packet_decoder.rs | 18 ++------- pumpkin-protocol/src/packet_encoder.rs | 20 ++-------- pumpkin-protocol/src/query.rs | 21 ++++++++--- pumpkin-protocol/src/server/handshake/mod.rs | 5 ++- .../src/server/play/s_click_container.rs | 26 +++++++++++-- .../src/server/play/s_interact.rs | 23 +++++++++--- .../src/server/play/s_player_action.rs | 21 ++++++++++- .../src/server/play/s_player_command.rs | 24 +++++++++++- pumpkin-world/Cargo.toml | 4 +- pumpkin-world/src/block/mod.rs | 21 +++++++++-- pumpkin-world/src/chunk/anvil.rs | 5 +-- pumpkin/Cargo.toml | 7 +--- pumpkin/src/command/args/arg_gamemode.rs | 9 ++--- pumpkin/src/command/commands/cmd_help.rs | 7 +--- pumpkin/src/entity/ai/goal/look_at_entity.rs | 1 + pumpkin/src/entity/ai/goal/mod.rs | 10 +++++ pumpkin/src/entity/ai/mod.rs | 1 + pumpkin/src/entity/mob/mod.rs | 1 + pumpkin/src/entity/mob/zombie.rs | 1 + pumpkin/src/entity/mod.rs | 19 +++++++++- pumpkin/src/entity/player.rs | 37 ++++++++++++++++--- pumpkin/src/net/packet/config.rs | 7 ++-- pumpkin/src/net/packet/play.rs | 31 +++++++--------- 34 files changed, 259 insertions(+), 152 deletions(-) create mode 100644 pumpkin/src/entity/ai/goal/look_at_entity.rs create mode 100644 pumpkin/src/entity/ai/goal/mod.rs create mode 100644 pumpkin/src/entity/ai/mod.rs create mode 100644 pumpkin/src/entity/mob/mod.rs create mode 100644 pumpkin/src/entity/mob/zombie.rs diff --git a/Cargo.toml b/Cargo.toml index 7122a3541..371e6e846 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -34,8 +34,6 @@ debug = true [workspace.dependencies] log = "0.4" tokio = { version = "1.42", features = [ - "fs", - "io-util", "macros", "net", "rt-multi-thread", @@ -45,8 +43,6 @@ tokio = { version = "1.42", features = [ ] } thiserror = "2" -num-traits = "0.2" -num-derive = "0.4" bytes = "1.9" diff --git a/pumpkin-core/Cargo.toml b/pumpkin-core/Cargo.toml index 31fd66e52..71888b00d 100644 --- a/pumpkin-core/Cargo.toml +++ b/pumpkin-core/Cargo.toml @@ -8,8 +8,8 @@ pumpkin-nbt = { path = "../pumpkin-nbt" } serde.workspace = true bytes.workspace = true uuid.workspace = true -num-traits.workspace = true -num-derive.workspace = true + +num-traits = "0.2" colored = "2" md5 = "0.7.0" diff --git a/pumpkin-core/src/gamemode.rs b/pumpkin-core/src/gamemode.rs index 085d0be2d..4ac2e121a 100644 --- a/pumpkin-core/src/gamemode.rs +++ b/pumpkin-core/src/gamemode.rs @@ -1,12 +1,11 @@ use std::str::FromStr; -use num_derive::FromPrimitive; use serde::{Deserialize, Serialize}; #[derive(Debug, PartialEq, Eq)] pub struct ParseGameModeError; -#[derive(Clone, Copy, Debug, PartialEq, Serialize, Deserialize, FromPrimitive)] +#[derive(Clone, Copy, Debug, PartialEq, Serialize, Deserialize)] #[repr(i8)] pub enum GameMode { Undefined = -1, @@ -16,6 +15,19 @@ pub enum GameMode { Spectator, } +impl From for GameMode { + fn from(value: i8) -> Self { + match value { + -1 => Self::Undefined, + 0 => Self::Survival, + 1 => Self::Creative, + 2 => Self::Adventure, + 3 => Self::Spectator, + _ => Self::Undefined, + } + } +} + impl FromStr for GameMode { type Err = ParseGameModeError; diff --git a/pumpkin-core/src/permission.rs b/pumpkin-core/src/permission.rs index e569efd60..e9ef6e36f 100644 --- a/pumpkin-core/src/permission.rs +++ b/pumpkin-core/src/permission.rs @@ -1,4 +1,3 @@ -use num_derive::{FromPrimitive, ToPrimitive}; use serde::{Deserialize, Deserializer, Serialize, Serializer}; /// Represents the player's permission level @@ -10,7 +9,7 @@ use serde::{Deserialize, Deserializer, Serialize, Serializer}; /// - `Two`: `gamemaster`: Player or executor can use more commands and player can use command blocks. /// - `Three`: `admin`: Player or executor can use commands related to multiplayer management. /// - `Four`: `owner`: Player or executor can use all of the commands, including commands related to server management. -#[derive(FromPrimitive, ToPrimitive, Clone, Copy, Default, PartialEq, Eq)] +#[derive(Clone, Copy, Default, PartialEq, Eq)] #[repr(i8)] pub enum PermissionLvl { #[default] diff --git a/pumpkin-inventory/Cargo.toml b/pumpkin-inventory/Cargo.toml index 79ff82f60..3fe57edc0 100644 --- a/pumpkin-inventory/Cargo.toml +++ b/pumpkin-inventory/Cargo.toml @@ -16,5 +16,3 @@ rayon.workspace = true tokio.workspace = true thiserror.workspace = true -num-traits.workspace = true -num-derive.workspace = true diff --git a/pumpkin-inventory/src/drag_handler.rs b/pumpkin-inventory/src/drag_handler.rs index 4a6ea4388..2ac0b6a13 100644 --- a/pumpkin-inventory/src/drag_handler.rs +++ b/pumpkin-inventory/src/drag_handler.rs @@ -1,6 +1,5 @@ use crate::container_click::MouseDragType; use crate::{Container, InventoryError}; -use num_traits::Euclid; use pumpkin_world::item::ItemStack; use std::collections::HashMap; use std::sync::Arc; @@ -124,14 +123,17 @@ impl DragHandler { // TODO: please work lol (1, 0) } else { - (carried_item.item_count as usize).div_rem_euclid(&amount_of_slots) + ( + carried_item.item_count.div_euclid(amount_of_slots as u8), + carried_item.item_count.rem_euclid(amount_of_slots as u8), + ) }; let mut item_in_each_slot = *carried_item; - item_in_each_slot.item_count = amount_per_slot as u8; + item_in_each_slot.item_count = amount_per_slot; changing_slots.for_each(|slot| *slots[slot] = Some(item_in_each_slot)); if remainder > 0 { - carried_item.item_count = remainder as u8; + carried_item.item_count = remainder; } else { *maybe_carried_item = None } diff --git a/pumpkin-inventory/src/lib.rs b/pumpkin-inventory/src/lib.rs index f1273b054..92fc4fc5f 100644 --- a/pumpkin-inventory/src/lib.rs +++ b/pumpkin-inventory/src/lib.rs @@ -1,6 +1,5 @@ use crate::container_click::MouseClick; use crate::player::PlayerInventory; -use num_derive::FromPrimitive; use pumpkin_macros::screen; use pumpkin_world::item::ItemStack; @@ -15,7 +14,7 @@ pub mod window_property; pub use error::InventoryError; pub use open_container::*; -#[derive(Debug, FromPrimitive, Clone, Copy, Eq, PartialEq)] +#[derive(Debug, Clone, Copy, Eq, PartialEq)] #[repr(u8)] pub enum WindowType { // not used diff --git a/pumpkin-inventory/src/window_property.rs b/pumpkin-inventory/src/window_property.rs index 136d68312..9bca2c984 100644 --- a/pumpkin-inventory/src/window_property.rs +++ b/pumpkin-inventory/src/window_property.rs @@ -1,16 +1,7 @@ -use num_derive::ToPrimitive; -use num_traits::ToPrimitive; - pub trait WindowPropertyTrait { fn to_id(self) -> i16; } -impl WindowPropertyTrait for T { - fn to_id(self) -> i16 { - self.to_i16().unwrap() - } -} - pub struct WindowProperty { window_property: T, value: i16, @@ -28,7 +19,7 @@ impl WindowProperty { (self.window_property.to_id(), self.value) } } -#[derive(ToPrimitive)] +#[repr(u8)] pub enum Furnace { FireIcon, MaximumFuelBurnTime, @@ -55,35 +46,35 @@ impl WindowPropertyTrait for EnchantmentTable { }) as i16 } } -#[derive(ToPrimitive)] +#[repr(u8)] pub enum Beacon { PowerLevel, FirstPotionEffect, SecondPotionEffect, } -#[derive(ToPrimitive)] +#[repr(u8)] pub enum Anvil { RepairCost, } -#[derive(ToPrimitive)] +#[repr(u8)] pub enum BrewingStand { BrewTime, FuelTime, } -#[derive(ToPrimitive)] +#[repr(u8)] pub enum Stonecutter { SelectedRecipe, } -#[derive(ToPrimitive)] +#[repr(u8)] pub enum Loom { SelectedPattern, } -#[derive(ToPrimitive)] +#[repr(u8)] pub enum Lectern { PageNumber, } diff --git a/pumpkin-protocol/Cargo.toml b/pumpkin-protocol/Cargo.toml index f04d77311..489e653c5 100644 --- a/pumpkin-protocol/Cargo.toml +++ b/pumpkin-protocol/Cargo.toml @@ -21,8 +21,6 @@ serde.workspace = true thiserror.workspace = true log.workspace = true tokio.workspace = true -num-traits.workspace = true -num-derive.workspace = true bytes.workspace = true # encryption diff --git a/pumpkin-protocol/src/bytebuf/mod.rs b/pumpkin-protocol/src/bytebuf/mod.rs index d089c221b..1993e1541 100644 --- a/pumpkin-protocol/src/bytebuf/mod.rs +++ b/pumpkin-protocol/src/bytebuf/mod.rs @@ -241,10 +241,7 @@ impl ByteBuf for T { if data.len() > max_size { return Err(ReadingError::TooLarge("string".to_string())); } - match str::from_utf8(&data) { - Ok(string_result) => Ok(string_result.to_string()), - Err(e) => Err(ReadingError::Message(e.to_string())), - } + String::from_utf8(data.to_vec()).map_err(|e| ReadingError::Message(e.to_string())) } fn try_get_option( diff --git a/pumpkin-protocol/src/lib.rs b/pumpkin-protocol/src/lib.rs index afb136092..4936cd3bc 100644 --- a/pumpkin-protocol/src/lib.rs +++ b/pumpkin-protocol/src/lib.rs @@ -21,7 +21,7 @@ pub mod server; /// Don't forget to change this when porting pub const CURRENT_MC_PROTOCOL: NonZeroU16 = unsafe { NonZeroU16::new_unchecked(769) }; -pub const MAX_PACKET_SIZE: i32 = 2097152; +pub const MAX_PACKET_SIZE: usize = 2097152; pub type FixedBitSet = bytes::Bytes; @@ -49,18 +49,18 @@ pub enum ConnectionState { Config, Play, } +pub struct InvalidConnectionState; -impl From for ConnectionState { - fn from(value: VarInt) -> Self { +impl TryFrom for ConnectionState { + type Error = InvalidConnectionState; + + fn try_from(value: VarInt) -> Result { let value = value.0; match value { - 1 => Self::Status, - 2 => Self::Login, - 3 => Self::Transfer, - _ => { - log::info!("Unexpected Status {}", value); - Self::Status - } + 1 => Ok(Self::Status), + 2 => Ok(Self::Login), + 3 => Ok(Self::Transfer), + _ => Err(InvalidConnectionState), } } } diff --git a/pumpkin-protocol/src/packet_decoder.rs b/pumpkin-protocol/src/packet_decoder.rs index 89ec4f42b..7c38b538a 100644 --- a/pumpkin-protocol/src/packet_decoder.rs +++ b/pumpkin-protocol/src/packet_decoder.rs @@ -13,6 +13,7 @@ type Cipher = cfb8::Decryptor; // Decoder: Client -> Server // Supports ZLib decoding/decompression // Supports Aes128 Encryption +#[derive(Default)] pub struct PacketDecoder { buf: BytesMut, decompress_buf: BytesMut, @@ -20,19 +21,6 @@ pub struct PacketDecoder { decompressor: Option, } -// Manual implementation of Default trait for PacketDecoder -// Since decompressor does not implement Default -impl Default for PacketDecoder { - fn default() -> Self { - Self { - buf: BytesMut::new(), - decompress_buf: BytesMut::new(), - cipher: None, - decompressor: None, - } - } -} - impl PacketDecoder { pub fn decode(&mut self) -> Result, PacketDecodeError> { let mut r = &self.buf[..]; @@ -44,7 +32,7 @@ impl PacketDecoder { }; let packet_len = packet_len.0; - if !(0..=MAX_PACKET_SIZE).contains(&packet_len) { + if !(0..=MAX_PACKET_SIZE as i32).contains(&packet_len) { Err(PacketDecodeError::OutOfBounds)? } @@ -63,7 +51,7 @@ impl PacketDecoder { .map_err(|_| PacketDecodeError::TooLong)? .0; - if !(0..=MAX_PACKET_SIZE).contains(&data_len) { + if !(0..=MAX_PACKET_SIZE as i32).contains(&data_len) { Err(PacketDecodeError::OutOfBounds)? } diff --git a/pumpkin-protocol/src/packet_encoder.rs b/pumpkin-protocol/src/packet_encoder.rs index 006e1a995..04d1fa5b3 100644 --- a/pumpkin-protocol/src/packet_encoder.rs +++ b/pumpkin-protocol/src/packet_encoder.rs @@ -13,6 +13,7 @@ type Cipher = cfb8::Encryptor; /// Encoder: Server -> Client /// Supports ZLib endecoding/compression /// Supports Aes128 Encryption +#[derive(Default)] pub struct PacketEncoder { buf: BytesMut, compress_buf: Vec, @@ -21,19 +22,6 @@ pub struct PacketEncoder { compression: Option<(Compressor, CompressionThreshold)>, } -// Manual implementation of Default trait for PacketEncoder -// Since compressor does not implement Default -impl Default for PacketEncoder { - fn default() -> Self { - Self { - buf: BytesMut::with_capacity(1024), - compress_buf: Vec::with_capacity(1024), - cipher: None, - compression: None, // init compressor with fastest compression level - } - } -} - impl PacketEncoder { /// Appends a Clientbound `ClientPacket` to the internal buffer and applies compression when needed. /// @@ -99,7 +87,7 @@ impl PacketEncoder { let packet_len = data_len_size + compressed_size; - if packet_len >= MAX_PACKET_SIZE as usize { + if packet_len >= MAX_PACKET_SIZE { return Err(PacketEncodeError::TooLong(packet_len)); } @@ -112,7 +100,7 @@ impl PacketEncoder { let data_len_size = 1; let packet_len = data_len_size + data_len; - if packet_len >= MAX_PACKET_SIZE as usize { + if packet_len >= MAX_PACKET_SIZE { Err(PacketEncodeError::TooLong(packet_len))? } @@ -136,7 +124,7 @@ impl PacketEncoder { let packet_len = data_len; - if packet_len >= MAX_PACKET_SIZE as usize { + if packet_len >= MAX_PACKET_SIZE { Err(PacketEncodeError::TooLong(packet_len))? } diff --git a/pumpkin-protocol/src/query.rs b/pumpkin-protocol/src/query.rs index 9cd0444ce..ef505fd0f 100644 --- a/pumpkin-protocol/src/query.rs +++ b/pumpkin-protocol/src/query.rs @@ -1,10 +1,7 @@ use std::{ffi::CString, io::Cursor}; -use num_derive::FromPrimitive; -use num_traits::FromPrimitive; use tokio::io::{AsyncReadExt, AsyncWriteExt}; -#[derive(FromPrimitive)] #[repr(u8)] pub enum PacketType { // There could be other types but they are not documented @@ -13,6 +10,20 @@ pub enum PacketType { Status = 0, } +pub struct InvalidPacketType; + +impl TryFrom for PacketType { + type Error = InvalidPacketType; + + fn try_from(value: u8) -> Result { + match value { + 9 => Ok(Self::Handshake), + 0 => Ok(Self::Status), + _ => Err(InvalidPacketType), + } + } +} + pub struct RawQueryPacket { pub packet_type: PacketType, reader: Cursor>, @@ -27,8 +38,8 @@ impl RawQueryPacket { // Since it denotes the protocol being used // Should not attempt to decode packets with other magic values 65277 => Ok(Self { - packet_type: PacketType::from_u8(reader.read_u8().await.map_err(|_| ())?) - .ok_or(())?, + packet_type: PacketType::try_from(reader.read_u8().await.map_err(|_| ())?) + .map_err(|_| ())?, reader, }), _ => Err(()), diff --git a/pumpkin-protocol/src/server/handshake/mod.rs b/pumpkin-protocol/src/server/handshake/mod.rs index 9436e5696..71ca0bc7a 100644 --- a/pumpkin-protocol/src/server/handshake/mod.rs +++ b/pumpkin-protocol/src/server/handshake/mod.rs @@ -20,7 +20,10 @@ impl ServerPacket for SHandShake { protocol_version: bytebuf.try_get_var_int()?, server_address: bytebuf.try_get_string_len(255)?, server_port: bytebuf.try_get_u16()?, - next_state: bytebuf.try_get_var_int()?.into(), + next_state: bytebuf + .try_get_var_int()? + .try_into() + .map_err(|_| ReadingError::Message("Invalid Status".to_string()))?, }) } } diff --git a/pumpkin-protocol/src/server/play/s_click_container.rs b/pumpkin-protocol/src/server/play/s_click_container.rs index 6dcf4abdd..954d7b02b 100644 --- a/pumpkin-protocol/src/server/play/s_click_container.rs +++ b/pumpkin-protocol/src/server/play/s_click_container.rs @@ -1,7 +1,5 @@ use crate::codec::slot::Slot; use crate::VarInt; -use num_derive::FromPrimitive; -use num_traits::FromPrimitive; use pumpkin_macros::server_packet; use serde::de::SeqAccess; use serde::{de, Deserialize}; @@ -74,7 +72,7 @@ impl<'de> Deserialize<'de> for SClickContainer { state_id, slot, button, - mode: SlotActionType::from_i32(mode.0) + mode: SlotActionType::try_from(mode.0) .expect("Invalid Slot action, TODO better error handling ;D"), length_of_array, array_of_changed_slots, @@ -87,7 +85,7 @@ impl<'de> Deserialize<'de> for SClickContainer { } } -#[derive(Deserialize, FromPrimitive)] +#[derive(Deserialize)] pub enum SlotActionType { /// Performs a normal slot click. This can pickup or place items in the slot, possibly merging the cursor stack into the slot, or swapping the slot stack with the cursor stack if they can't be merged. Pickup, @@ -107,3 +105,23 @@ pub enum SlotActionType { /// Replenishes the cursor stack with items from the screen handler. This is usually triggered by the player double clicking PickupAll, } + +#[derive(Debug)] +pub struct InvalidSlotActionType; + +impl TryFrom for SlotActionType { + type Error = InvalidSlotActionType; + + fn try_from(value: i32) -> Result { + match value { + 0 => Ok(Self::Pickup), + 1 => Ok(Self::QuickMove), + 2 => Ok(Self::Swap), + 3 => Ok(Self::Clone), + 4 => Ok(Self::Throw), + 5 => Ok(Self::QuickCraft), + 6 => Ok(Self::PickupAll), + _ => Err(InvalidSlotActionType), + } + } +} diff --git a/pumpkin-protocol/src/server/play/s_interact.rs b/pumpkin-protocol/src/server/play/s_interact.rs index a6a3844d9..3251f6edf 100644 --- a/pumpkin-protocol/src/server/play/s_interact.rs +++ b/pumpkin-protocol/src/server/play/s_interact.rs @@ -1,6 +1,4 @@ use bytes::Buf; -use num_derive::FromPrimitive; -use num_traits::FromPrimitive; use pumpkin_core::math::vector3::Vector3; use pumpkin_macros::server_packet; @@ -23,8 +21,8 @@ impl ServerPacket for SInteract { fn read(bytebuf: &mut impl Buf) -> Result { let entity_id = bytebuf.try_get_var_int()?; let typ = bytebuf.try_get_var_int()?; - let action = ActionType::from_i32(typ.0) - .ok_or(ReadingError::Message("invalid action type".to_string()))?; + let action = ActionType::try_from(typ.0) + .map_err(|_| ReadingError::Message("invalid action type".to_string()))?; let target_position: Option> = match action { ActionType::Interact => None, ActionType::Attack => None, @@ -50,9 +48,24 @@ impl ServerPacket for SInteract { } } -#[derive(FromPrimitive, PartialEq, Eq)] +#[derive(PartialEq, Eq, Debug)] pub enum ActionType { Interact, Attack, InteractAt, } + +pub struct InvalidActionType; + +impl TryFrom for ActionType { + type Error = InvalidActionType; + + fn try_from(value: i32) -> Result { + match value { + 0 => Ok(Self::Interact), + 1 => Ok(Self::Attack), + 2 => Ok(Self::InteractAt), + _ => Err(InvalidActionType), + } + } +} diff --git a/pumpkin-protocol/src/server/play/s_player_action.rs b/pumpkin-protocol/src/server/play/s_player_action.rs index b8d774dc2..55ceb4dfd 100644 --- a/pumpkin-protocol/src/server/play/s_player_action.rs +++ b/pumpkin-protocol/src/server/play/s_player_action.rs @@ -1,4 +1,3 @@ -use num_derive::FromPrimitive; use pumpkin_core::math::position::WorldPosition; use pumpkin_macros::server_packet; @@ -13,7 +12,6 @@ pub struct SPlayerAction { pub sequence: VarInt, } -#[derive(FromPrimitive)] pub enum Status { /// Sent when the player starts digging a block. If the block was instamined or the player is in creative mode, the client will not send Status = Finished digging, and will assume the server completed the destruction. To detect this, it is necessary to calculate the block destruction speed server-side. StartedDigging = 0, @@ -31,3 +29,22 @@ pub enum Status { /// Used to swap or assign an item to the second hand. Location is always set to 0/0/0, Face is always set to -Y. Sequence is always set to 0. SwapItem, } + +pub struct InvalidStatus; + +impl TryFrom for Status { + type Error = InvalidStatus; + + fn try_from(value: i32) -> Result { + match value { + 0 => Ok(Self::StartedDigging), + 1 => Ok(Self::CancelledDigging), + 2 => Ok(Self::FinishedDigging), + 3 => Ok(Self::DropItemStack), + 4 => Ok(Self::DropItem), + 5 => Ok(Self::ShootArrowOrFinishEating), + 6 => Ok(Self::SwapItem), + _ => Err(InvalidStatus), + } + } +} diff --git a/pumpkin-protocol/src/server/play/s_player_command.rs b/pumpkin-protocol/src/server/play/s_player_command.rs index 6a9b65a49..02dcf5082 100644 --- a/pumpkin-protocol/src/server/play/s_player_command.rs +++ b/pumpkin-protocol/src/server/play/s_player_command.rs @@ -1,5 +1,4 @@ use bytes::Buf; -use num_derive::FromPrimitive; use pumpkin_macros::server_packet; use crate::{ @@ -13,7 +12,7 @@ pub struct SPlayerCommand { pub action: VarInt, pub jump_boost: VarInt, } -#[derive(FromPrimitive)] + pub enum Action { StartSneaking = 0, StopSneaking, @@ -26,6 +25,27 @@ pub enum Action { StartFlyingElytra, } +pub struct InvalidAction; + +impl TryFrom for Action { + type Error = InvalidAction; + + fn try_from(value: i32) -> Result { + match value { + 0 => Ok(Self::StartSneaking), + 1 => Ok(Self::StopSneaking), + 2 => Ok(Self::LeaveBed), + 3 => Ok(Self::StartSprinting), + 4 => Ok(Self::StopSprinting), + 5 => Ok(Self::StartHorseJump), + 6 => Ok(Self::StopHorseJump), + 7 => Ok(Self::OpenVehicleInventory), + 8 => Ok(Self::StartFlyingElytra), + _ => Err(InvalidAction), + } + } +} + impl ServerPacket for SPlayerCommand { fn read(bytebuf: &mut impl Buf) -> Result { Ok(Self { diff --git a/pumpkin-world/Cargo.toml b/pumpkin-world/Cargo.toml index 1c7eacd87..5a603cefc 100644 --- a/pumpkin-world/Cargo.toml +++ b/pumpkin-world/Cargo.toml @@ -17,10 +17,10 @@ serde.workspace = true serde_json.workspace = true log.workspace = true -num-traits.workspace = true -num-derive.workspace = true dashmap = "6.1.0" +num-traits = "0.2" + # Compression flate2 = "1.0" lz4 = "1.28.0" diff --git a/pumpkin-world/src/block/mod.rs b/pumpkin-world/src/block/mod.rs index e5d10db60..01fbb97c2 100644 --- a/pumpkin-world/src/block/mod.rs +++ b/pumpkin-world/src/block/mod.rs @@ -1,5 +1,3 @@ -use num_derive::FromPrimitive; - pub mod block_registry; pub mod block_state; @@ -7,7 +5,6 @@ use pumpkin_core::math::vector3::Vector3; pub use block_state::BlockState; -#[derive(FromPrimitive)] pub enum BlockFace { Bottom = 0, Top, @@ -17,6 +14,24 @@ pub enum BlockFace { East, } +pub struct InvalidBlockFace; + +impl TryFrom for BlockFace { + type Error = InvalidBlockFace; + + fn try_from(value: i32) -> Result { + match value { + 0 => Ok(Self::Bottom), + 1 => Ok(Self::Top), + 2 => Ok(Self::North), + 3 => Ok(Self::South), + 4 => Ok(Self::West), + 5 => Ok(Self::East), + _ => Err(InvalidBlockFace), + } + } +} + impl BlockFace { pub fn to_offset(&self) -> Vector3 { match self { diff --git a/pumpkin-world/src/chunk/anvil.rs b/pumpkin-world/src/chunk/anvil.rs index 2391537f3..9f41b1c1a 100644 --- a/pumpkin-world/src/chunk/anvil.rs +++ b/pumpkin-world/src/chunk/anvil.rs @@ -115,9 +115,8 @@ impl ChunkReader for AnvilChunkReader { .read_exact(&mut timestamp_table) .map_err(|err| ChunkReadingError::IoError(err.kind()))?; - let modulus = |a: i32, b: i32| ((a % b) + b) % b; - let chunk_x = modulus(at.x, 32) as u32; - let chunk_z = modulus(at.z, 32) as u32; + let chunk_x = at.x.rem_euclid(32) as u32; + let chunk_z = at.z.rem_euclid(32) as u32; let table_entry = (chunk_x + chunk_z * 32) * 4; let mut offset = vec![0u8]; diff --git a/pumpkin/Cargo.toml b/pumpkin/Cargo.toml index 87b030201..54fe88a13 100644 --- a/pumpkin/Cargo.toml +++ b/pumpkin/Cargo.toml @@ -8,7 +8,7 @@ edition = "2021" # FileDescription is handled as the Program name by Windows! FileDescription = "Pumpkin" OriginalFilename = "pumpkin.exe" -LegalCopyright = "Copyright © 2024 Aleksander Medvedev" +LegalCopyright = "Copyright © 2025 Aleksander Medvedev" [dependencies] # pumpkin @@ -28,9 +28,6 @@ tokio.workspace = true rayon.workspace = true thiserror.workspace = true -num-traits.workspace = true -num-derive.workspace = true - # config serde.workspace = true serde_json.workspace = true @@ -46,7 +43,7 @@ rsa = "0.9.7" rsa-der = "0.3.0" # authentication -reqwest = { version = "0.12.10", default-features = false, features = [ +reqwest = { version = "0.12.12", default-features = false, features = [ "http2", "json", "macos-system-configuration", diff --git a/pumpkin/src/command/args/arg_gamemode.rs b/pumpkin/src/command/args/arg_gamemode.rs index 2049ad821..77d5199df 100644 --- a/pumpkin/src/command/args/arg_gamemode.rs +++ b/pumpkin/src/command/args/arg_gamemode.rs @@ -1,7 +1,6 @@ use std::str::FromStr; use async_trait::async_trait; -use num_traits::FromPrimitive; use pumpkin_core::GameMode; use pumpkin_protocol::client::play::{ CommandSuggestion, ProtoCmdArgParser, ProtoCmdArgSuggestionType, @@ -36,10 +35,10 @@ impl ArgumentConsumer for GamemodeArgumentConsumer { ) -> Option> { let s = args.pop()?; - if let Ok(id) = s.parse::() { - match GameMode::from_u8(id) { - None | Some(GameMode::Undefined) => {} - Some(gamemode) => return Some(Arg::GameMode(gamemode)), + if let Ok(id) = s.parse::() { + match GameMode::from(id) { + GameMode::Undefined => {} + gamemode => return Some(Arg::GameMode(gamemode)), }; }; diff --git a/pumpkin/src/command/commands/cmd_help.rs b/pumpkin/src/command/commands/cmd_help.rs index 0cf8c01dd..3808c0acf 100644 --- a/pumpkin/src/command/commands/cmd_help.rs +++ b/pumpkin/src/command/commands/cmd_help.rs @@ -1,5 +1,4 @@ use async_trait::async_trait; -use num_traits::ToPrimitive; use pumpkin_core::text::click::ClickEvent; use pumpkin_core::text::color::{Color, NamedColor}; use pumpkin_core::text::TextComponent; @@ -133,14 +132,12 @@ impl CommandExecutor for BaseHelpExecutor { commands.sort_by(|a, b| a.names[0].cmp(&b.names[0])); - let total_pages = - (commands.len().to_i32().unwrap() + COMMANDS_PER_PAGE - 1) / COMMANDS_PER_PAGE; + let total_pages = (commands.len() as i32 + COMMANDS_PER_PAGE - 1) / COMMANDS_PER_PAGE; let page = page_number.min(total_pages); let start = (page - 1) * COMMANDS_PER_PAGE; let end = start + COMMANDS_PER_PAGE; - let page_commands = - &commands[start as usize..end.min(commands.len().to_i32().unwrap()) as usize]; + let page_commands = &commands[start as usize..end.min(commands.len() as i32) as usize]; let arrow_left = if page > 1 { let cmd = format!("/help {}", page - 1); diff --git a/pumpkin/src/entity/ai/goal/look_at_entity.rs b/pumpkin/src/entity/ai/goal/look_at_entity.rs new file mode 100644 index 000000000..8b1378917 --- /dev/null +++ b/pumpkin/src/entity/ai/goal/look_at_entity.rs @@ -0,0 +1 @@ + diff --git a/pumpkin/src/entity/ai/goal/mod.rs b/pumpkin/src/entity/ai/goal/mod.rs new file mode 100644 index 000000000..e7847ddff --- /dev/null +++ b/pumpkin/src/entity/ai/goal/mod.rs @@ -0,0 +1,10 @@ +pub mod look_at_entity; + +pub trait Goal { + /// How Should the Goal initially start? + fn can_start(&self) -> bool; + /// When its started, How it should Continue to run + fn should_continue() -> bool; + /// If the Goal is running, this gets called every tick + fn tick(&self); +} diff --git a/pumpkin/src/entity/ai/mod.rs b/pumpkin/src/entity/ai/mod.rs new file mode 100644 index 000000000..b3dc9aaa2 --- /dev/null +++ b/pumpkin/src/entity/ai/mod.rs @@ -0,0 +1 @@ +pub mod goal; diff --git a/pumpkin/src/entity/mob/mod.rs b/pumpkin/src/entity/mob/mod.rs new file mode 100644 index 000000000..e95568045 --- /dev/null +++ b/pumpkin/src/entity/mob/mod.rs @@ -0,0 +1 @@ +pub mod zombie; diff --git a/pumpkin/src/entity/mob/zombie.rs b/pumpkin/src/entity/mob/zombie.rs new file mode 100644 index 000000000..8b1378917 --- /dev/null +++ b/pumpkin/src/entity/mob/zombie.rs @@ -0,0 +1 @@ + diff --git a/pumpkin/src/entity/mod.rs b/pumpkin/src/entity/mod.rs index bb4bea4c3..b982adf4c 100644 --- a/pumpkin/src/entity/mod.rs +++ b/pumpkin/src/entity/mod.rs @@ -1,13 +1,14 @@ +use core::f32; use std::sync::{atomic::AtomicBool, Arc}; use crossbeam::atomic::AtomicCell; -use num_derive::FromPrimitive; use pumpkin_core::math::{ boundingbox::{BoundingBox, BoundingBoxSize}, get_section_cord, position::WorldPosition, vector2::Vector2, vector3::Vector3, + wrap_degrees, }; use pumpkin_entity::{entity_type::EntityType, pose::EntityPose, EntityId}; use pumpkin_protocol::{ @@ -17,6 +18,9 @@ use pumpkin_protocol::{ use crate::world::World; +pub mod ai; +pub mod mob; + pub mod living; pub mod player; @@ -136,6 +140,17 @@ impl Entity { } } + /// Changes this entity's pitch and yaw to look at target + pub fn look_at(&self, target: Vector3) { + let position = self.pos.load(); + let delta = target.sub(&position); + let root = delta.x.hypot(delta.z); + let pitch = wrap_degrees(-delta.y.atan2(root) as f32 * 180.0 / f32::consts::PI); + let yaw = wrap_degrees((delta.z.atan2(delta.x) as f32 * 180.0 / f32::consts::PI) - 90.0); + self.pitch.store(pitch); + self.yaw.store(yaw); + } + pub async fn teleport(&self, position: Vector3, yaw: f32, pitch: f32) { self.world .broadcast_packet_all(&CTeleportEntity::new( @@ -243,7 +258,7 @@ impl Entity { } } -#[derive(Clone, Copy, Debug, PartialEq, Eq, FromPrimitive)] +#[derive(Clone, Copy, Debug, PartialEq, Eq)] /// Represents various entity flags that are sent in entity metadata. /// /// These flags are used by the client to modify the rendering of entities based on their current state. diff --git a/pumpkin/src/entity/player.rs b/pumpkin/src/entity/player.rs index f70f161cb..9bd67bb77 100644 --- a/pumpkin/src/entity/player.rs +++ b/pumpkin/src/entity/player.rs @@ -8,8 +8,6 @@ use std::{ }; use crossbeam::atomic::AtomicCell; -use num_derive::FromPrimitive; -use num_traits::Pow; use pumpkin_config::{ADVANCED_CONFIG, BASIC_CONFIG}; use pumpkin_core::{ math::{ @@ -298,7 +296,7 @@ impl Player { // only reduce attack damage if in cooldown // TODO: Enchantments are reduced same way just without the square if attack_cooldown_progress < 1.0 { - damage_multiplier = 0.2 + attack_cooldown_progress.pow(2) * 0.8; + damage_multiplier = 0.2 + attack_cooldown_progress.powi(2) * 0.8; } // modify added damage based on multiplier let mut damage = base_damage + add_damage * damage_multiplier; @@ -823,7 +821,7 @@ impl Default for Abilities { } /// Represents the player's dominant hand. -#[derive(Debug, FromPrimitive, Clone, Copy, PartialEq, Eq)] +#[derive(Debug, Clone, Copy, PartialEq, Eq)] #[repr(u8)] pub enum Hand { /// Usually the player's off-hand. @@ -832,8 +830,22 @@ pub enum Hand { Right, } +pub struct InvalidHand; + +impl TryFrom for Hand { + type Error = InvalidHand; + + fn try_from(value: i32) -> Result { + match value { + 0 => Ok(Self::Left), + 1 => Ok(Self::Right), + _ => Err(InvalidHand), + } + } +} + /// Represents the player's chat mode settings. -#[derive(Debug, FromPrimitive, Clone)] +#[derive(Debug, Clone)] pub enum ChatMode { /// Chat is enabled for the player. Enabled, @@ -842,3 +854,18 @@ pub enum ChatMode { /// All messages should be hidden Hidden, } + +pub struct InvalidChatMode; + +impl TryFrom for ChatMode { + type Error = InvalidChatMode; + + fn try_from(value: i32) -> Result { + match value { + 0 => Ok(Self::Enabled), + 1 => Ok(Self::CommandsOnly), + 2 => Ok(Self::Hidden), + _ => Err(InvalidChatMode), + } + } +} diff --git a/pumpkin/src/net/packet/config.rs b/pumpkin/src/net/packet/config.rs index ed1d8769a..4db8ffc2e 100644 --- a/pumpkin/src/net/packet/config.rs +++ b/pumpkin/src/net/packet/config.rs @@ -6,7 +6,6 @@ use crate::{ server::Server, }; use core::str; -use num_traits::FromPrimitive; use pumpkin_protocol::{ client::config::{CFinishConfig, CRegistryData}, codec::var_int::VarInt, @@ -28,9 +27,9 @@ impl Client { return; } - if let (Some(main_hand), Some(chat_mode)) = ( - Hand::from_i32(client_information.main_hand.into()), - ChatMode::from_i32(client_information.chat_mode.into()), + if let (Ok(main_hand), Ok(chat_mode)) = ( + Hand::try_from(client_information.main_hand.0), + ChatMode::try_from(client_information.chat_mode.0), ) { *self.config.lock().await = Some(PlayerConfig { locale: client_information.locale, diff --git a/pumpkin/src/net/packet/play.rs b/pumpkin/src/net/packet/play.rs index 62dce7507..7f402ce9e 100644 --- a/pumpkin/src/net/packet/play.rs +++ b/pumpkin/src/net/packet/play.rs @@ -11,7 +11,6 @@ use crate::{ server::Server, world::player_chunker, }; -use num_traits::FromPrimitive; use pumpkin_config::ADVANCED_CONFIG; use pumpkin_core::math::{boundingbox::BoundingBox, position::WorldPosition}; use pumpkin_core::{ @@ -52,10 +51,6 @@ use pumpkin_world::{ }; use thiserror::Error; -fn modulus(a: f32, b: f32) -> f32 { - ((a % b) + b) % b -} - #[derive(Debug, Error)] pub enum BlockPlacingError { BlockOutOfReach, @@ -217,8 +212,8 @@ impl Player { let entity_id = entity.entity_id; let Vector3 { x, y, z } = pos; - let yaw = modulus(entity.yaw.load() * 256.0 / 360.0, 256.0); - let pitch = modulus(entity.pitch.load() * 256.0 / 360.0, 256.0); + let yaw = (entity.yaw.load() * 256.0 / 360.0).rem_euclid(256.0); + let pitch = (entity.pitch.load() * 256.0 / 360.0).rem_euclid(256.0); // let head_yaw = (entity.head_yaw * 256.0 / 360.0).floor(); let world = &entity.world; @@ -276,8 +271,8 @@ impl Player { ); // send new position to all other players let entity_id = entity.entity_id; - let yaw = modulus(entity.yaw.load() * 256.0 / 360.0, 256.0); - let pitch = modulus(entity.pitch.load() * 256.0 / 360.0, 256.0); + let yaw = (entity.yaw.load() * 256.0 / 360.0).rem_euclid(256.0); + let pitch = (entity.pitch.load() * 256.0 / 360.0).rem_euclid(256.0); // let head_yaw = modulus(entity.head_yaw * 256.0 / 360.0, 256.0); let world = &entity.world; @@ -421,7 +416,7 @@ impl Player { return; } - if let Some(action) = Action::from_i32(command.action.0) { + if let Ok(action) = Action::try_from(command.action.0) { let entity = &self.living_entity.entity; match action { pumpkin_protocol::server::play::Action::StartSneaking => { @@ -546,9 +541,9 @@ impl Player { self: &Arc, client_information: SClientInformationPlay, ) { - if let (Some(main_hand), Some(chat_mode)) = ( - Hand::from_i32(client_information.main_hand.into()), - ChatMode::from_i32(client_information.chat_mode.into()), + if let (Ok(main_hand), Ok(chat_mode)) = ( + Hand::try_from(client_information.main_hand.0), + ChatMode::try_from(client_information.chat_mode.0), ) { if client_information.view_distance <= 0 { self.kick(TextComponent::text( @@ -640,7 +635,7 @@ impl Player { if entity.sneaking.load(std::sync::atomic::Ordering::Relaxed) != sneaking { entity.set_sneaking(sneaking).await; } - let Some(action) = ActionType::from_i32(interact.typ.0) else { + let Ok(action) = ActionType::try_from(interact.typ.0) else { self.kick(TextComponent::text("Invalid action type")).await; return; }; @@ -695,8 +690,8 @@ impl Player { } pub async fn handle_player_action(&self, player_action: SPlayerAction, server: &Server) { - match Status::from_i32(player_action.status.0) { - Some(status) => match status { + match Status::try_from(player_action.status.0) { + Ok(status) => match status { Status::StartedDigging => { if !self.can_interact_with_block_at(&player_action.location, 1.0) { log::warn!( @@ -769,7 +764,7 @@ impl Player { log::debug!("todo"); } }, - None => self.kick(TextComponent::text("Invalid status")).await, + Err(_) => self.kick(TextComponent::text("Invalid status")).await, } self.client @@ -823,7 +818,7 @@ impl Player { return Err(BlockPlacingError::BlockOutOfReach.into()); } - if let Some(face) = BlockFace::from_i32(use_item_on.face.0) { + if let Ok(face) = BlockFace::try_from(use_item_on.face.0) { let mut inventory = self.inventory().lock().await; let entity = &self.living_entity.entity; let world = &entity.world;