From 0bebc6fc3111c7c5135fee4b7057af225f49f248 Mon Sep 17 00:00:00 2001 From: Snowiiii Date: Thu, 12 Sep 2024 12:33:33 +0200 Subject: [PATCH] Better doc --- pumpkin-config/README.md | 7 ++ pumpkin-entity/README.md | 0 pumpkin-protocol/README.md | 6 +- pumpkin-protocol/src/lib.rs | 26 +++++-- pumpkin-protocol/src/packet_decoder.rs | 3 + pumpkin-protocol/src/packet_encoder.rs | 3 + pumpkin-protocol/src/uuid.rs | 2 + pumpkin-world/README.md | 17 +++++ pumpkin-world/src/level.rs | 21 +++--- pumpkin/src/client/authentication.rs | 50 ++++++++++--- pumpkin/src/client/client_packet.rs | 6 +- pumpkin/src/client/mod.rs | 41 +++++++++-- pumpkin/src/entity/mod.rs | 73 +++++++++++++++---- pumpkin/src/entity/player.rs | 98 +++++++++++++++++--------- pumpkin/src/main.rs | 3 - pumpkin/src/server/mod.rs | 13 ++-- pumpkin/src/world/mod.rs | 25 ++++++- 17 files changed, 300 insertions(+), 94 deletions(-) create mode 100644 pumpkin-config/README.md create mode 100644 pumpkin-entity/README.md create mode 100644 pumpkin-world/README.md diff --git a/pumpkin-config/README.md b/pumpkin-config/README.md new file mode 100644 index 000000000..c8506926b --- /dev/null +++ b/pumpkin-config/README.md @@ -0,0 +1,7 @@ +### Pumpkin Configuration +Pumpkin offers a robust configuration system that allows users to customize various aspects of the server's behavior without relying on external plugins. This provides flexibility and control over the server's operation. + +#### Key Features: + - Extensive Customization: Configure server settings, player behavior, world generation, and more. + - Performance Optimization: Optimize server performance through configuration tweaks. + - Plugin-Free Customization: Achieve desired changes without the need for additional plugins. diff --git a/pumpkin-entity/README.md b/pumpkin-entity/README.md new file mode 100644 index 000000000..e69de29bb diff --git a/pumpkin-protocol/README.md b/pumpkin-protocol/README.md index 4ea5c5c71..9eec8c892 100644 --- a/pumpkin-protocol/README.md +++ b/pumpkin-protocol/README.md @@ -69,4 +69,8 @@ Thats a Serverbound packet pub struct CPlayDisconnect { reason: TextComponent, } -`` \ No newline at end of file +``` + +### Porting +You can compare difference in Protocol on wiki.vg https://wiki.vg/index.php?title=Protocol&action=history +Also change the `CURRENT_MC_PROTOCOL` in `src/lib.rs` \ No newline at end of file diff --git a/pumpkin-protocol/src/lib.rs b/pumpkin-protocol/src/lib.rs index dfabd2e99..426e02f98 100644 --- a/pumpkin-protocol/src/lib.rs +++ b/pumpkin-protocol/src/lib.rs @@ -12,6 +12,8 @@ pub mod server; pub mod slot; pub mod uuid; +/// To current Minecraft protocol +/// Don't forget to change this when porting pub const CURRENT_MC_PROTOCOL: u32 = 767; pub const MAX_PACKET_SIZE: i32 = 2097152; @@ -175,7 +177,6 @@ impl From for ConnectionState { } } } - pub struct RawPacket { pub id: VarInt, pub bytebuf: ByteBuffer, @@ -191,29 +192,42 @@ pub trait ServerPacket: Packet + Sized { #[derive(Serialize)] pub struct StatusResponse { - pub version: Version, - pub players: Players, + /// The version on which the Server is running. Optional + pub version: Option, + /// Informations about currently connected Players. Optional + pub players: Option, + /// The description displayed also called MOTD (Message of the day). Optional pub description: String, - pub favicon: Option, // data:image/png;base64, - // Players, favicon ... + /// The icon displayed, Optional + pub favicon: Option, + /// Players are forced to use Secure chat + pub enforece_secure_chat: bool, } #[derive(Serialize)] pub struct Version { + /// The current name of the Version (e.g. 1.21.1) pub name: String, + /// The current Protocol Version (e.g. 767) pub protocol: u32, } #[derive(Serialize)] pub struct Players { + /// The maximum Player count the server allows pub max: u32, + /// The current online player count pub online: u32, + /// Informations about currently connected players. + /// Note player can disable listing here. pub sample: Vec, } #[derive(Serialize)] pub struct Sample { + /// Players Name pub name: String, - pub id: String, // uuid + /// Players UUID + pub id: String, } // basicly game profile diff --git a/pumpkin-protocol/src/packet_decoder.rs b/pumpkin-protocol/src/packet_decoder.rs index 308716ccb..b001cea15 100644 --- a/pumpkin-protocol/src/packet_decoder.rs +++ b/pumpkin-protocol/src/packet_decoder.rs @@ -13,6 +13,8 @@ use crate::{ type Cipher = cfb8::Decryptor; // Decoder: Client -> Server +// Supports ZLib decoding/decompression +// Supports Aes128 Encyption #[derive(Default)] pub struct PacketDecoder { buf: BytesMut, @@ -105,6 +107,7 @@ impl PacketDecoder { self.cipher = Some(cipher); } + /// Enables ZLib Deompression pub fn set_compression(&mut self, compression: Option) { self.compression = compression; } diff --git a/pumpkin-protocol/src/packet_encoder.rs b/pumpkin-protocol/src/packet_encoder.rs index 1c545804b..9e0d9f525 100644 --- a/pumpkin-protocol/src/packet_encoder.rs +++ b/pumpkin-protocol/src/packet_encoder.rs @@ -13,6 +13,8 @@ use crate::{bytebuf::ByteBuffer, ClientPacket, PacketError, VarInt, MAX_PACKET_S type Cipher = cfb8::Encryptor; // Encoder: Server -> Client +// Supports ZLib endecoding/compression +// Supports Aes128 Encyption #[derive(Default)] pub struct PacketEncoder { buf: BytesMut, @@ -121,6 +123,7 @@ impl PacketEncoder { self.cipher = Some(Cipher::new_from_slices(key, key).expect("invalid key")); } + /// Enables ZLib Compression pub fn set_compression(&mut self, compression: Option<(u32, u32)>) { self.compression = compression; } diff --git a/pumpkin-protocol/src/uuid.rs b/pumpkin-protocol/src/uuid.rs index ab70ef83a..a187a083a 100644 --- a/pumpkin-protocol/src/uuid.rs +++ b/pumpkin-protocol/src/uuid.rs @@ -1,6 +1,8 @@ use serde::Serialize; #[derive(Clone)] +/// Wrapper around uuid::UUID, Please use this in every Packet containing a UUID +/// We use this to we can do own Serializing pub struct UUID(pub uuid::Uuid); impl Serialize for UUID { diff --git a/pumpkin-world/README.md b/pumpkin-world/README.md new file mode 100644 index 000000000..40637233c --- /dev/null +++ b/pumpkin-world/README.md @@ -0,0 +1,17 @@ +### Pumpkin World +Contains everything World related for example + +- Loading Chunks (Anvil Format) +- Generating Chunks +- Loading Blocks/Items + +### Porting +When updating your Minecraft server to a newer version, you typically need to replace the files in the assets directory to ensure compatibility with the new version's resources. +Thankfully, vanilla Minecraft provides a way to extract these updated assets directly from the server JAR file itself. + +1. Download the latest Minecraft server JAR file for the version you want to upgrade to. +2. Run `java -DbundlerMainClass=net.minecraft.data.Main -jar .jar --reports`. +3. This command will create a new folder named `reports` in the same directory as the server JAR. This folder contains the updated "assets" directory for the new version. +4. Copy the assets folder from the reports folder and replace the existing assets directory within your server directory. + +For details see https://wiki.vg/Data_Generators diff --git a/pumpkin-world/src/level.rs b/pumpkin-world/src/level.rs index 8707912be..d86b989c7 100644 --- a/pumpkin-world/src/level.rs +++ b/pumpkin-world/src/level.rs @@ -18,7 +18,15 @@ use crate::{ world_gen::{get_world_gen, Seed, WorldGenerator}, }; -/// The Level represents a single Dimension. +/// The `Level` module provides functionality for working with chunks within or outside a Minecraft world. +/// +/// Key features include: +/// +/// - **Chunk Loading:** Efficiently loads chunks from disk (Anvil format). +/// - **Chunk Caching:** Stores accessed chunks in memory for faster access. +/// - **Chunk Generation:** Generates new chunks on-demand using a specified `WorldGenerator`. +/// +/// For more details on world generation, refer to the `WorldGenerator` module. pub struct Level { save_file: Option, loaded_chunks: Arc, Arc>>>, @@ -126,17 +134,6 @@ impl Level { } } - // /// Read one chunk in the world - // /// - // /// Do not use this function if reading many chunks is required, since in case those two chunks which are read separately using `.read_chunk` are in the same region file, it will need to be opened and closed separately for both of them, leading to a performance loss. - // pub async fn read_chunk(&self, chunk: (i32, i32)) -> Result { - // self.read_chunks(vec![chunk]) - // .await - // .pop() - // .expect("Read chunks must return a chunk") - // .1 - // } - /// Reads/Generates many chunks in a world /// MUST be called from a tokio runtime thread /// diff --git a/pumpkin/src/client/authentication.rs b/pumpkin/src/client/authentication.rs index 60d6c5523..9af1020ce 100644 --- a/pumpkin/src/client/authentication.rs +++ b/pumpkin/src/client/authentication.rs @@ -39,6 +39,19 @@ pub struct GameProfile { pub profile_actions: Option>, } +/// Sends a GET request to Mojang's authentication servers to verify a client's Minecraft account. +/// +/// **Purpose:** +/// +/// This function is used to ensure that a client connecting to the server has a valid, premium Minecraft account. It's a crucial step in preventing unauthorized access and maintaining server security. +/// +/// **How it Works:** +/// +/// 1. A client with a premium account sends a login request to the Mojang session server. +/// 2. Mojang's servers verify the client's credentials and add the player to the their Servers +/// 3. Now our server will send a Request to the Session servers and check if the Player has joined the Session Server . +/// +/// **Note:** This process helps prevent unauthorized access to the server and ensures that only legitimate Minecraft accounts can connect. pub async fn authenticate( username: &str, server_hash: &str, @@ -71,29 +84,34 @@ pub async fn authenticate( Ok(profile) } -pub fn unpack_textures(property: Property, config: &TextureConfig) { - // TODO: no unwrap - let from64 = general_purpose::STANDARD.decode(property.value).unwrap(); - let textures: ProfileTextures = serde_json::from_slice(&from64).unwrap(); +pub fn unpack_textures(property: Property, config: &TextureConfig) -> Result<(), TextureError> { + let from64 = general_purpose::STANDARD + .decode(property.value) + .map_err(|e| TextureError::DecodeError(e.to_string()))?; + let textures: ProfileTextures = + serde_json::from_slice(&from64).map_err(|e| TextureError::JSONError(e.to_string()))?; for texture in textures.textures { - is_texture_url_valid(Url::parse(&texture.1.url).unwrap(), config); + let url = + Url::parse(&texture.1.url).map_err(|e| TextureError::InvalidURL(e.to_string()))?; + is_texture_url_valid(url, config)? } + Ok(()) } pub fn auth_digest(bytes: &[u8]) -> String { BigInt::from_signed_bytes_be(bytes).to_str_radix(16) } -pub fn is_texture_url_valid(url: Url, config: &TextureConfig) -> bool { +pub fn is_texture_url_valid(url: Url, config: &TextureConfig) -> Result<(), TextureError> { let scheme = url.scheme(); if !config.allowed_url_schemes.contains(&scheme.to_string()) { - return false; + return Err(TextureError::DisallowedUrlScheme(scheme.to_string())); } let domain = url.domain().unwrap_or(""); if !config.allowed_url_domains.contains(&domain.to_string()) { - return false; + return Err(TextureError::DisallowedUrlDomain(domain.to_string())); } - true + Ok(()) } #[derive(Error, Debug)] @@ -109,3 +127,17 @@ pub enum AuthError { #[error("Unknown Status Code")] UnknownStatusCode(String), } + +#[derive(Error, Debug)] +pub enum TextureError { + #[error("Invalid URL")] + InvalidURL(String), + #[error("Invalid URL scheme for player texture: {0}")] + DisallowedUrlScheme(String), + #[error("Invalid URL domain for player texture: {0}")] + DisallowedUrlDomain(String), + #[error("Failed to decode base64 player texture: {0}")] + DecodeError(String), + #[error("Failed to parse JSON from player texture: {0}")] + JSONError(String), +} diff --git a/pumpkin/src/client/client_packet.rs b/pumpkin/src/client/client_packet.rs index 7fc6d11d1..3323de115 100644 --- a/pumpkin/src/client/client_packet.rs +++ b/pumpkin/src/client/client_packet.rs @@ -171,9 +171,9 @@ impl Client { Err(e) => self.kick(&e.to_string()), } } - for ele in gameprofile.as_ref().unwrap().properties.clone() { - // todo, use this - unpack_textures(ele, &ADVANCED_CONFIG.authentication.textures); + for property in gameprofile.as_ref().unwrap().properties.clone() { + unpack_textures(property, &ADVANCED_CONFIG.authentication.textures) + .unwrap_or_else(|e| self.kick(&e.to_string())); } // enable compression diff --git a/pumpkin/src/client/mod.rs b/pumpkin/src/client/mod.rs index f4e89a450..4018b410b 100644 --- a/pumpkin/src/client/mod.rs +++ b/pumpkin/src/client/mod.rs @@ -37,15 +37,30 @@ mod client_packet; mod container; pub mod player_packet; +/// Represents a player's configuration settings. +/// +/// This struct contains various options that can be customized by the player, affecting their gameplay experience. +/// +/// **Usage:** +/// +/// This struct is typically used to store and manage a player's preferences. It can be sent to the server when a player joins or when they change their settings. #[derive(Clone)] pub struct PlayerConfig { + /// The player's preferred language. pub locale: String, // 16 + /// The maximum distance at which chunks are rendered. pub view_distance: i8, + /// The player's chat mode settings pub chat_mode: ChatMode, + /// Whether chat colors are enabled. pub chat_colors: bool, + /// The player's skin configuration options. pub skin_parts: u8, + /// The player's dominant hand (left or right). pub main_hand: Hand, + /// Whether text filtering is enabled. pub text_filtering: bool, + /// Whether the player wants to appear in the server list. pub server_listing: bool, } @@ -64,23 +79,37 @@ impl Default for PlayerConfig { } } +/// Everything which makes a Conection with our Server is a `Client`. +/// Client will become Players when they reach the `Play` state pub struct Client { + /// The client's game profile information. pub gameprofile: Mutex>, - + /// The client's configuration settings, Optional pub config: Mutex>, + /// The client's brand or modpack information, Optional. pub brand: Mutex>, - + /// The minecraft protocol version used by the client. pub protocol_version: AtomicI32, + /// The current connection state of the client (e.g., Handshaking, Status, Play). pub connection_state: Mutex, + /// Whether encryption is enabled for the connection. pub encryption: AtomicBool, + /// Indicates if the client connection is closed. pub closed: AtomicBool, + /// A unique token identifying the client. pub token: Token, + /// The underlying TCP connection to the client. pub connection: Arc>, + /// The client's IP address. pub address: Mutex, + /// The packet encoder for outgoing packets. enc: Arc>, + /// The packet decoder for incoming packets. dec: Arc>, + /// A queue of raw packets received from the client, waiting to be processed. pub client_packets_queue: Arc>>, + /// Indicates whether the client should be converted into a player. pub make_player: AtomicBool, } @@ -104,13 +133,13 @@ impl Client { } } - /// adds a Incoming packet to the queue + /// Adds a Incoming packet to the queue pub fn add_packet(&self, packet: RawPacket) { let mut client_packets_queue = self.client_packets_queue.lock().unwrap(); client_packets_queue.push(packet); } - /// enables encryption + /// Enables encryption pub fn enable_encryption( &self, shared_secret: &[u8], // decrypted @@ -125,7 +154,7 @@ impl Client { Ok(()) } - // Compression threshold, Compression level + /// Compression threshold, Compression level pub fn set_compression(&self, compression: Option<(u32, u32)>) { self.dec .lock() @@ -161,6 +190,7 @@ impl Client { Ok(()) } + /// Processes all packets send by the client pub async fn process_packets(&self, server: &Arc) { while let Some(mut packet) = self.client_packets_queue.lock().unwrap().pop() { match self.handle_packet(server, &mut packet).await { @@ -330,7 +360,6 @@ impl Client { /// Kicks the Client with a reason depending on the connection state pub fn kick(&self, reason: &str) { - dbg!(reason); match *self.connection_state.lock().unwrap() { ConnectionState::Login => { self.try_send_packet(&CLoginDisconnect::new( diff --git a/pumpkin/src/entity/mod.rs b/pumpkin/src/entity/mod.rs index 132dfedf0..4d0ffe3be 100644 --- a/pumpkin/src/entity/mod.rs +++ b/pumpkin/src/entity/mod.rs @@ -1,5 +1,7 @@ use std::sync::{atomic::AtomicBool, Arc, Mutex}; +use num_derive::{FromPrimitive, ToPrimitive}; +use num_traits::ToPrimitive; use pumpkin_core::math::{ get_section_cord, position::WorldPosition, vector2::Vector2, vector3::Vector3, }; @@ -14,27 +16,44 @@ use crate::world::World; pub mod player; pub struct Entity { + /// A unique identifier for the entity pub entity_id: EntityId, + /// The type of entity (e.g., player, zombie, item) pub entity_type: EntityType, + /// The world in which the entity exists. pub world: Arc, + /// The entity's current health level. + pub health: Mutex, + /// The entity's current position in the world pub pos: Mutex>, + /// The entity's position rounded to the nearest block coordinates pub block_pos: Mutex, + /// The chunk coordinates of the entity's current position pub chunk_pos: Mutex>, + /// Indicates whether the entity is sneaking pub sneaking: AtomicBool, + /// Indicates whether the entity is sprinting pub sprinting: AtomicBool, + /// Indicates whether the entity is flying due to a fall pub fall_flying: AtomicBool, + /// The entity's current velocity vector, aka Knockback pub velocity: Mutex>, - // Should be not trusted + /// Indicates whether the entity is on the ground (may not always be accurate). pub on_ground: AtomicBool, + /// The entity's yaw rotation (horizontal rotation) ← → pub yaw: Mutex, + /// The entity's head yaw rotation (horizontal rotation of the head) pub head_yaw: Mutex, + /// The entity's pitch rotation (vertical rotation) ↑ ↓ pub pitch: Mutex, + /// The height of the entity's eyes from the ground. // TODO: Change this in diffrent poses pub standing_eye_height: f32, + /// The entity's current pose (e.g., standing, sitting, swimming). pub pose: Mutex, } @@ -54,6 +73,8 @@ impl Entity { chunk_pos: Mutex::new(Vector2::new(0, 0)), sneaking: AtomicBool::new(false), world, + // TODO: Load this from previous instance + health: Mutex::new(20.0), sprinting: AtomicBool::new(false), fall_flying: AtomicBool::new(false), yaw: Mutex::new(0.0), @@ -65,6 +86,9 @@ impl Entity { } } + /// Updates the entity's position, block position, and chunk position. + /// + /// This function calculates the new position, block position, and chunk position based on the provided coordinates. If any of these values change, the corresponding fields are updated. pub fn set_pos(&self, x: f64, y: f64, z: f64) { let mut pos = self.pos.lock().unwrap(); if pos.x != x || pos.y != y || pos.z != z { @@ -89,16 +113,21 @@ impl Entity { } } + /// Sets the Entity yaw & pitch Rotation pub fn set_rotation(&self, yaw: f32, pitch: f32) { // TODO *self.yaw.lock().unwrap() = yaw; *self.pitch.lock().unwrap() = pitch } + /// Removes the Entity from their current World pub async fn remove(&mut self) { self.world.remove_entity(self); } + /// Applies knockback to the entity, following vanilla Minecraft's mechanics. + /// + /// This function calculates the entity's new velocity based on the specified knockback strength and direction. pub fn knockback(&self, strength: f64, x: f64, z: f64) { // This has some vanilla magic let mut x = x; @@ -125,7 +154,7 @@ impl Entity { assert!(self.sneaking.load(std::sync::atomic::Ordering::Relaxed) != sneaking); self.sneaking .store(sneaking, std::sync::atomic::Ordering::Relaxed); - self.set_flag(Self::SNEAKING_FLAG_INDEX, sneaking).await; + self.set_flag(Flag::Sneaking, sneaking).await; // if sneaking { // self.set_pose(EntityPose::Crouching).await; // } else { @@ -137,7 +166,7 @@ impl Entity { assert!(self.sprinting.load(std::sync::atomic::Ordering::Relaxed) != sprinting); self.sprinting .store(sprinting, std::sync::atomic::Ordering::Relaxed); - self.set_flag(Self::SPRINTING_FLAG_INDEX, sprinting).await; + self.set_flag(Flag::Sprinting, sprinting).await; } pub fn check_fall_flying(&self) -> bool { @@ -148,18 +177,11 @@ impl Entity { assert!(self.fall_flying.load(std::sync::atomic::Ordering::Relaxed) != fall_flying); self.fall_flying .store(fall_flying, std::sync::atomic::Ordering::Relaxed); - self.set_flag(Self::FALL_FLYING_FLAG_INDEX, fall_flying) - .await; + self.set_flag(Flag::FallFlying, fall_flying).await; } - pub const ON_FIRE_FLAG_INDEX: u32 = 0; - pub const SNEAKING_FLAG_INDEX: u32 = 1; - pub const SPRINTING_FLAG_INDEX: u32 = 3; - pub const SWIMMING_FLAG_INDEX: u32 = 4; - pub const INVISIBLE_FLAG_INDEX: u32 = 5; - pub const GLOWING_FLAG_INDEX: u32 = 6; - pub const FALL_FLYING_FLAG_INDEX: u32 = 7; - async fn set_flag(&self, index: u32, value: bool) { + async fn set_flag(&self, flag: Flag, value: bool) { + let index = flag.to_u32().unwrap(); let mut b = 0i8; if value { b |= 1 << index; @@ -180,3 +202,28 @@ impl Entity { self.world.broadcast_packet_all(&packet) } } + +#[derive(Clone, Copy, Debug, PartialEq, Eq, FromPrimitive, ToPrimitive)] +/// 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. +/// +/// **Purpose:** +/// +/// This enum provides a more type-safe and readable way to represent entity flags compared to using raw integer values. +pub enum Flag { + /// Indicates if the entity is on fire. + OnFire, + /// Indicates if the entity is sneaking. + Sneaking, + /// Indicates if the entity is sprinting. + Sprinting, + /// Indicates if the entity is swimming. + Swimming, + /// Indicates if the entity is invisible. + Invisible, + /// Indicates if the entity is glowing. + Glowing, + /// Indicates if the entity is flying due to a fall. + FallFlying, +} diff --git a/pumpkin/src/entity/player.rs b/pumpkin/src/entity/player.rs index 67ed05e8f..8fd5c5f21 100644 --- a/pumpkin/src/entity/player.rs +++ b/pumpkin/src/entity/player.rs @@ -38,55 +38,49 @@ use crate::{ use super::Entity; -pub struct PlayerAbilities { - pub invulnerable: bool, - pub flying: bool, - pub allow_flying: bool, - pub creative: bool, - pub fly_speed: f32, - pub walk_speed_fov: f32, -} - -impl Default for PlayerAbilities { - fn default() -> Self { - Self { - invulnerable: false, - flying: false, - allow_flying: false, - creative: false, - fly_speed: 0.5, - walk_speed_fov: 0.1, - } - } -} - +/// Represents a Minecraft player entity. +/// +/// A `Player` is a special type of entity that represents a human player connected to the server. pub struct Player { + /// The underlying entity object that represents the player. pub entity: Entity, + /// The player's game profile information, including their username and UUID. pub gameprofile: GameProfile, + /// The client connection associated with the player. pub client: Client, + /// The player's configuration settings. Changes when the Player changes their settings. pub config: Mutex, - /// Current gamemode + /// The player's current gamemode (e.g., Survival, Creative, Adventure). pub gamemode: Mutex, - // TODO: prbly should put this into an Living Entitiy or something - pub health: Mutex, + /// The player's hunger level. pub food: AtomicI32, + /// The player's food saturation level. pub food_saturation: Mutex, + /// The player's inventory, containing items and equipment. pub inventory: Mutex, + /// The ID of the currently open container (if any). pub open_container: Mutex>, + /// The item currently being held by the player. pub carried_item: Mutex>, - - /// send `send_abilties_update` when changed + /// The player's abilities and special powers. + /// + /// This field represents the various abilities that the player possesses, such as flight, invulnerability, and other special effects. + /// + /// **Note:** When the `abilities` field is updated, the server should send a `send_abilities_update` packet to the client to notify them of the changes. pub abilities: PlayerAbilities, + /// The player's last known position. + /// + /// This field is used to calculate the player's movement delta for network synchronization and other purposes. pub last_position: Mutex>, - + /// The current stage of the block the player is breaking. // TODO: This is currently unused, We have to calculate the block breaking speed our own and then break the block our own if its done pub current_block_destroy_stage: AtomicU8, - + /// A counter for teleport IDs used to track pending teleports. pub teleport_id_count: AtomicI32, - // Current awaiting teleport id and location, None if did not teleport + /// The pending teleport information, including the teleport ID and target location. pub awaiting_teleport: Mutex)>>, - + /// The coordinates of the chunk section the player is currently watching. pub watched_section: Mutex>, } @@ -112,7 +106,6 @@ impl Player { client, awaiting_teleport: Mutex::new(None), // TODO: Load this from previous instance - health: Mutex::new(20.0), food: AtomicI32::new(20), food_saturation: Mutex::new(20.0), current_block_destroy_stage: AtomicU8::new(0), @@ -136,6 +129,7 @@ impl Player { self.entity.entity_id } + /// Updates the current abilities the Player has pub fn send_abilties_update(&mut self) { let mut b = 0i8; let abilities = &self.abilities; @@ -225,7 +219,7 @@ impl Player { } pub fn update_health(&self, health: f32, food: i32, food_saturation: f32) { - *self.health.lock().unwrap() = health; + *self.entity.health.lock().unwrap() = health; self.food.store(food, std::sync::atomic::Ordering::Relaxed); *self.food_saturation.lock().unwrap() = food_saturation; } @@ -378,15 +372,53 @@ impl Player { } } +/// Represents a player's abilities and special powers. +/// +/// This struct contains information about the player's current abilities, such as flight, invulnerability, and creative mode. +pub struct PlayerAbilities { + /// Indicates whether the player is invulnerable to damage. + pub invulnerable: bool, + /// Indicates whether the player is currently flying. + pub flying: bool, + /// Indicates whether the player is allowed to fly (if enabled). + pub allow_flying: bool, + /// Indicates whether the player is in creative mode. + pub creative: bool, + /// The player's flying speed. + pub fly_speed: f32, + /// The field of view adjustment when the player is walking or sprinting. + pub walk_speed_fov: f32, +} + +impl Default for PlayerAbilities { + fn default() -> Self { + Self { + invulnerable: false, + flying: false, + allow_flying: false, + creative: false, + fly_speed: 0.5, + walk_speed_fov: 0.1, + } + } +} + +/// Represents the player's dominant hand. #[derive(FromPrimitive, Clone)] pub enum Hand { + /// The player's primary hand (usually the right hand). Main, + /// The player's off-hand (usually the left hand). Off, } +/// Represents the player's chat mode settings. #[derive(FromPrimitive, Clone)] pub enum ChatMode { + /// Chat is enabled for the player. Enabled, + /// The player should only see chat messages from commands CommandsOnly, + /// All messages should be hidden Hidden, } diff --git a/pumpkin/src/main.rs b/pumpkin/src/main.rs index 8a00ebbb4..734fce13d 100644 --- a/pumpkin/src/main.rs +++ b/pumpkin/src/main.rs @@ -169,11 +169,8 @@ fn main() -> io::Result<()> { } if closed { if let Some(player) = players.remove(&token) { - dbg!("a"); player.remove().await; - dbg!("b"); let connection = &mut player.client.connection.lock().unwrap(); - dbg!("c"); poll.registry().deregister(connection.by_ref())?; } diff --git a/pumpkin/src/server/mod.rs b/pumpkin/src/server/mod.rs index 986842d60..71a0eae9c 100644 --- a/pumpkin/src/server/mod.rs +++ b/pumpkin/src/server/mod.rs @@ -161,7 +161,8 @@ impl Server { } } - // move to world + /// Generates a new entity id + /// This should be global pub fn new_entity_id(&self) -> EntityId { self.entity_id.fetch_add(1, Ordering::SeqCst) } @@ -191,20 +192,22 @@ impl Server { }; StatusResponse { - version: Version { + version: Some(Version { name: CURRENT_MC_VERSION.into(), protocol: CURRENT_MC_PROTOCOL, - }, - players: Players { + }), + players: Some(Players { max: config.max_players, online: 0, sample: vec![Sample { name: "".into(), id: "".into(), }], - }, + }), description: config.motd.clone(), favicon: icon, + // TODO + enforece_secure_chat: false, } } diff --git a/pumpkin/src/world/mod.rs b/pumpkin/src/world/mod.rs index 253fa27b4..4046075b5 100644 --- a/pumpkin/src/world/mod.rs +++ b/pumpkin/src/world/mod.rs @@ -26,10 +26,21 @@ use crate::{ entity::{player::Player, Entity}, }; +/// Represents a Minecraft world, containing entities, players, and the underlying level data. +/// +/// Each dimension (Overworld, Nether, End) typically has its own `World`. +/// +/// **Key Responsibilities:** +/// +/// - Manages the `Level` instance for handling chunk-related operations. +/// - Stores and tracks active `Player` entities within the world. +/// - Provides a central hub for interacting with the world's entities and environment. pub struct World { + /// The underlying level, responsible for chunk management and terrain generation. pub level: Arc>, + /// A map of active players within the world, keyed by their unique token. pub current_players: Arc>>>, - // entities, players... + // TODO: entities } impl World { @@ -40,7 +51,11 @@ impl World { } } - /// Sends a Packet to all Players in the World + /// Broadcasts a packet to all connected players within the world. + /// + /// Sends the specified packet to every player currently logged in to the server. + /// + /// **Note:** This function acquires a lock on the `current_players` map, ensuring thread safety. pub fn broadcast_packet_all

(&self, packet: &P) where P: ClientPacket, @@ -51,7 +66,11 @@ impl World { } } - /// Sends a Packet to all Players in the World, Expect the Players given the the expect parameter + /// Broadcasts a packet to all connected players within the world, excluding the specified players. + /// + /// Sends the specified packet to every player currently logged in to the server, excluding the players listed in the `except` parameter. + /// + /// **Note:** This function acquires a lock on the `current_players` map, ensuring thread safety. pub fn broadcast_packet_expect

(&self, except: &[Token], packet: &P) where P: ClientPacket,