shorten compile times
but only by a bit :c
This commit is contained in:
parent
4bfbff7c88
commit
fa07af7308
@ -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"
|
||||
|
||||
|
||||
@ -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"
|
||||
|
||||
@ -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<i8> 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;
|
||||
|
||||
|
||||
@ -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]
|
||||
|
||||
@ -16,5 +16,3 @@ rayon.workspace = true
|
||||
tokio.workspace = true
|
||||
thiserror.workspace = true
|
||||
|
||||
num-traits.workspace = true
|
||||
num-derive.workspace = true
|
||||
|
||||
@ -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
|
||||
}
|
||||
|
||||
@ -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
|
||||
|
||||
@ -1,16 +1,7 @@
|
||||
use num_derive::ToPrimitive;
|
||||
use num_traits::ToPrimitive;
|
||||
|
||||
pub trait WindowPropertyTrait {
|
||||
fn to_id(self) -> i16;
|
||||
}
|
||||
|
||||
impl<T: ToPrimitive> WindowPropertyTrait for T {
|
||||
fn to_id(self) -> i16 {
|
||||
self.to_i16().unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
pub struct WindowProperty<T: WindowPropertyTrait> {
|
||||
window_property: T,
|
||||
value: i16,
|
||||
@ -28,7 +19,7 @@ impl<T: WindowPropertyTrait> WindowProperty<T> {
|
||||
(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,
|
||||
}
|
||||
|
||||
@ -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
|
||||
|
||||
@ -241,10 +241,7 @@ impl<T: Buf> 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<G>(
|
||||
|
||||
@ -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<VarInt> for ConnectionState {
|
||||
fn from(value: VarInt) -> Self {
|
||||
impl TryFrom<VarInt> for ConnectionState {
|
||||
type Error = InvalidConnectionState;
|
||||
|
||||
fn try_from(value: VarInt) -> Result<Self, Self::Error> {
|
||||
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),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -13,6 +13,7 @@ type Cipher = cfb8::Decryptor<aes::Aes128>;
|
||||
// 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<Decompressor>,
|
||||
}
|
||||
|
||||
// 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<Option<RawPacket>, 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)?
|
||||
}
|
||||
|
||||
|
||||
@ -13,6 +13,7 @@ type Cipher = cfb8::Encryptor<aes::Aes128>;
|
||||
/// Encoder: Server -> Client
|
||||
/// Supports ZLib endecoding/compression
|
||||
/// Supports Aes128 Encryption
|
||||
#[derive(Default)]
|
||||
pub struct PacketEncoder {
|
||||
buf: BytesMut,
|
||||
compress_buf: Vec<u8>,
|
||||
@ -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))?
|
||||
}
|
||||
|
||||
|
||||
@ -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<u8> for PacketType {
|
||||
type Error = InvalidPacketType;
|
||||
|
||||
fn try_from(value: u8) -> Result<Self, Self::Error> {
|
||||
match value {
|
||||
9 => Ok(Self::Handshake),
|
||||
0 => Ok(Self::Status),
|
||||
_ => Err(InvalidPacketType),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct RawQueryPacket {
|
||||
pub packet_type: PacketType,
|
||||
reader: Cursor<Vec<u8>>,
|
||||
@ -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(()),
|
||||
|
||||
@ -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()))?,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@ -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<i32> for SlotActionType {
|
||||
type Error = InvalidSlotActionType;
|
||||
|
||||
fn try_from(value: i32) -> Result<Self, Self::Error> {
|
||||
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),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -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<Self, ReadingError> {
|
||||
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<Vector3<f32>> = 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<i32> for ActionType {
|
||||
type Error = InvalidActionType;
|
||||
|
||||
fn try_from(value: i32) -> Result<Self, Self::Error> {
|
||||
match value {
|
||||
0 => Ok(Self::Interact),
|
||||
1 => Ok(Self::Attack),
|
||||
2 => Ok(Self::InteractAt),
|
||||
_ => Err(InvalidActionType),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -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<i32> for Status {
|
||||
type Error = InvalidStatus;
|
||||
|
||||
fn try_from(value: i32) -> Result<Self, Self::Error> {
|
||||
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),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -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<i32> for Action {
|
||||
type Error = InvalidAction;
|
||||
|
||||
fn try_from(value: i32) -> Result<Self, Self::Error> {
|
||||
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<Self, ReadingError> {
|
||||
Ok(Self {
|
||||
|
||||
@ -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"
|
||||
|
||||
@ -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<i32> for BlockFace {
|
||||
type Error = InvalidBlockFace;
|
||||
|
||||
fn try_from(value: i32) -> Result<Self, Self::Error> {
|
||||
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<i32> {
|
||||
match self {
|
||||
|
||||
@ -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];
|
||||
|
||||
@ -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",
|
||||
|
||||
@ -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<Arg<'a>> {
|
||||
let s = args.pop()?;
|
||||
|
||||
if let Ok(id) = s.parse::<u8>() {
|
||||
match GameMode::from_u8(id) {
|
||||
None | Some(GameMode::Undefined) => {}
|
||||
Some(gamemode) => return Some(Arg::GameMode(gamemode)),
|
||||
if let Ok(id) = s.parse::<i8>() {
|
||||
match GameMode::from(id) {
|
||||
GameMode::Undefined => {}
|
||||
gamemode => return Some(Arg::GameMode(gamemode)),
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
@ -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);
|
||||
|
||||
1
pumpkin/src/entity/ai/goal/look_at_entity.rs
Normal file
1
pumpkin/src/entity/ai/goal/look_at_entity.rs
Normal file
@ -0,0 +1 @@
|
||||
|
||||
10
pumpkin/src/entity/ai/goal/mod.rs
Normal file
10
pumpkin/src/entity/ai/goal/mod.rs
Normal file
@ -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);
|
||||
}
|
||||
1
pumpkin/src/entity/ai/mod.rs
Normal file
1
pumpkin/src/entity/ai/mod.rs
Normal file
@ -0,0 +1 @@
|
||||
pub mod goal;
|
||||
1
pumpkin/src/entity/mob/mod.rs
Normal file
1
pumpkin/src/entity/mob/mod.rs
Normal file
@ -0,0 +1 @@
|
||||
pub mod zombie;
|
||||
1
pumpkin/src/entity/mob/zombie.rs
Normal file
1
pumpkin/src/entity/mob/zombie.rs
Normal file
@ -0,0 +1 @@
|
||||
|
||||
@ -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<f64>) {
|
||||
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<f64>, 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.
|
||||
|
||||
@ -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<i32> for Hand {
|
||||
type Error = InvalidHand;
|
||||
|
||||
fn try_from(value: i32) -> Result<Self, Self::Error> {
|
||||
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<i32> for ChatMode {
|
||||
type Error = InvalidChatMode;
|
||||
|
||||
fn try_from(value: i32) -> Result<Self, Self::Error> {
|
||||
match value {
|
||||
0 => Ok(Self::Enabled),
|
||||
1 => Ok(Self::CommandsOnly),
|
||||
2 => Ok(Self::Hidden),
|
||||
_ => Err(InvalidChatMode),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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<Self>,
|
||||
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;
|
||||
|
||||
Loading…
Reference in New Issue
Block a user