Better doc

This commit is contained in:
Snowiiii 2024-09-12 12:33:33 +02:00
parent ada0d82a72
commit 0bebc6fc31
17 changed files with 300 additions and 94 deletions

7
pumpkin-config/README.md Normal file
View File

@ -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.

0
pumpkin-entity/README.md Normal file
View File

View File

@ -69,4 +69,8 @@ Thats a Serverbound packet
pub struct CPlayDisconnect { pub struct CPlayDisconnect {
reason: TextComponent, reason: TextComponent,
} }
`` ```
### 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`

View File

@ -12,6 +12,8 @@ pub mod server;
pub mod slot; pub mod slot;
pub mod uuid; pub mod uuid;
/// To current Minecraft protocol
/// Don't forget to change this when porting
pub const CURRENT_MC_PROTOCOL: u32 = 767; pub const CURRENT_MC_PROTOCOL: u32 = 767;
pub const MAX_PACKET_SIZE: i32 = 2097152; pub const MAX_PACKET_SIZE: i32 = 2097152;
@ -175,7 +177,6 @@ impl From<VarInt> for ConnectionState {
} }
} }
} }
pub struct RawPacket { pub struct RawPacket {
pub id: VarInt, pub id: VarInt,
pub bytebuf: ByteBuffer, pub bytebuf: ByteBuffer,
@ -191,29 +192,42 @@ pub trait ServerPacket: Packet + Sized {
#[derive(Serialize)] #[derive(Serialize)]
pub struct StatusResponse { pub struct StatusResponse {
pub version: Version, /// The version on which the Server is running. Optional
pub players: Players, pub version: Option<Version>,
/// Informations about currently connected Players. Optional
pub players: Option<Players>,
/// The description displayed also called MOTD (Message of the day). Optional
pub description: String, pub description: String,
pub favicon: Option<String>, // data:image/png;base64,<data> /// The icon displayed, Optional
// Players, favicon ... pub favicon: Option<String>,
/// Players are forced to use Secure chat
pub enforece_secure_chat: bool,
} }
#[derive(Serialize)] #[derive(Serialize)]
pub struct Version { pub struct Version {
/// The current name of the Version (e.g. 1.21.1)
pub name: String, pub name: String,
/// The current Protocol Version (e.g. 767)
pub protocol: u32, pub protocol: u32,
} }
#[derive(Serialize)] #[derive(Serialize)]
pub struct Players { pub struct Players {
/// The maximum Player count the server allows
pub max: u32, pub max: u32,
/// The current online player count
pub online: u32, pub online: u32,
/// Informations about currently connected players.
/// Note player can disable listing here.
pub sample: Vec<Sample>, pub sample: Vec<Sample>,
} }
#[derive(Serialize)] #[derive(Serialize)]
pub struct Sample { pub struct Sample {
/// Players Name
pub name: String, pub name: String,
pub id: String, // uuid /// Players UUID
pub id: String,
} }
// basicly game profile // basicly game profile

View File

@ -13,6 +13,8 @@ use crate::{
type Cipher = cfb8::Decryptor<aes::Aes128>; type Cipher = cfb8::Decryptor<aes::Aes128>;
// Decoder: Client -> Server // Decoder: Client -> Server
// Supports ZLib decoding/decompression
// Supports Aes128 Encyption
#[derive(Default)] #[derive(Default)]
pub struct PacketDecoder { pub struct PacketDecoder {
buf: BytesMut, buf: BytesMut,
@ -105,6 +107,7 @@ impl PacketDecoder {
self.cipher = Some(cipher); self.cipher = Some(cipher);
} }
/// Enables ZLib Deompression
pub fn set_compression(&mut self, compression: Option<u32>) { pub fn set_compression(&mut self, compression: Option<u32>) {
self.compression = compression; self.compression = compression;
} }

View File

@ -13,6 +13,8 @@ use crate::{bytebuf::ByteBuffer, ClientPacket, PacketError, VarInt, MAX_PACKET_S
type Cipher = cfb8::Encryptor<aes::Aes128>; type Cipher = cfb8::Encryptor<aes::Aes128>;
// Encoder: Server -> Client // Encoder: Server -> Client
// Supports ZLib endecoding/compression
// Supports Aes128 Encyption
#[derive(Default)] #[derive(Default)]
pub struct PacketEncoder { pub struct PacketEncoder {
buf: BytesMut, buf: BytesMut,
@ -121,6 +123,7 @@ impl PacketEncoder {
self.cipher = Some(Cipher::new_from_slices(key, key).expect("invalid key")); 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)>) { pub fn set_compression(&mut self, compression: Option<(u32, u32)>) {
self.compression = compression; self.compression = compression;
} }

View File

@ -1,6 +1,8 @@
use serde::Serialize; use serde::Serialize;
#[derive(Clone)] #[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); pub struct UUID(pub uuid::Uuid);
impl Serialize for UUID { impl Serialize for UUID {

17
pumpkin-world/README.md Normal file
View File

@ -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 <minecraft_server>.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

View File

@ -18,7 +18,15 @@ use crate::{
world_gen::{get_world_gen, Seed, WorldGenerator}, 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 { pub struct Level {
save_file: Option<SaveFile>, save_file: Option<SaveFile>,
loaded_chunks: Arc<Mutex<HashMap<Vector2<i32>, Arc<ChunkData>>>>, loaded_chunks: Arc<Mutex<HashMap<Vector2<i32>, Arc<ChunkData>>>>,
@ -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<ChunkData, WorldError> {
// self.read_chunks(vec![chunk])
// .await
// .pop()
// .expect("Read chunks must return a chunk")
// .1
// }
/// Reads/Generates many chunks in a world /// Reads/Generates many chunks in a world
/// MUST be called from a tokio runtime thread /// MUST be called from a tokio runtime thread
/// ///

View File

@ -39,6 +39,19 @@ pub struct GameProfile {
pub profile_actions: Option<Vec<ProfileAction>>, pub profile_actions: Option<Vec<ProfileAction>>,
} }
/// 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( pub async fn authenticate(
username: &str, username: &str,
server_hash: &str, server_hash: &str,
@ -71,29 +84,34 @@ pub async fn authenticate(
Ok(profile) Ok(profile)
} }
pub fn unpack_textures(property: Property, config: &TextureConfig) { pub fn unpack_textures(property: Property, config: &TextureConfig) -> Result<(), TextureError> {
// TODO: no unwrap let from64 = general_purpose::STANDARD
let from64 = general_purpose::STANDARD.decode(property.value).unwrap(); .decode(property.value)
let textures: ProfileTextures = serde_json::from_slice(&from64).unwrap(); .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 { 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 { pub fn auth_digest(bytes: &[u8]) -> String {
BigInt::from_signed_bytes_be(bytes).to_str_radix(16) 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(); let scheme = url.scheme();
if !config.allowed_url_schemes.contains(&scheme.to_string()) { if !config.allowed_url_schemes.contains(&scheme.to_string()) {
return false; return Err(TextureError::DisallowedUrlScheme(scheme.to_string()));
} }
let domain = url.domain().unwrap_or(""); let domain = url.domain().unwrap_or("");
if !config.allowed_url_domains.contains(&domain.to_string()) { if !config.allowed_url_domains.contains(&domain.to_string()) {
return false; return Err(TextureError::DisallowedUrlDomain(domain.to_string()));
} }
true Ok(())
} }
#[derive(Error, Debug)] #[derive(Error, Debug)]
@ -109,3 +127,17 @@ pub enum AuthError {
#[error("Unknown Status Code")] #[error("Unknown Status Code")]
UnknownStatusCode(String), 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),
}

View File

@ -171,9 +171,9 @@ impl Client {
Err(e) => self.kick(&e.to_string()), Err(e) => self.kick(&e.to_string()),
} }
} }
for ele in gameprofile.as_ref().unwrap().properties.clone() { for property in gameprofile.as_ref().unwrap().properties.clone() {
// todo, use this unpack_textures(property, &ADVANCED_CONFIG.authentication.textures)
unpack_textures(ele, &ADVANCED_CONFIG.authentication.textures); .unwrap_or_else(|e| self.kick(&e.to_string()));
} }
// enable compression // enable compression

View File

@ -37,15 +37,30 @@ mod client_packet;
mod container; mod container;
pub mod player_packet; 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)] #[derive(Clone)]
pub struct PlayerConfig { pub struct PlayerConfig {
/// The player's preferred language.
pub locale: String, // 16 pub locale: String, // 16
/// The maximum distance at which chunks are rendered.
pub view_distance: i8, pub view_distance: i8,
/// The player's chat mode settings
pub chat_mode: ChatMode, pub chat_mode: ChatMode,
/// Whether chat colors are enabled.
pub chat_colors: bool, pub chat_colors: bool,
/// The player's skin configuration options.
pub skin_parts: u8, pub skin_parts: u8,
/// The player's dominant hand (left or right).
pub main_hand: Hand, pub main_hand: Hand,
/// Whether text filtering is enabled.
pub text_filtering: bool, pub text_filtering: bool,
/// Whether the player wants to appear in the server list.
pub server_listing: bool, 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 { pub struct Client {
/// The client's game profile information.
pub gameprofile: Mutex<Option<GameProfile>>, pub gameprofile: Mutex<Option<GameProfile>>,
/// The client's configuration settings, Optional
pub config: Mutex<Option<PlayerConfig>>, pub config: Mutex<Option<PlayerConfig>>,
/// The client's brand or modpack information, Optional.
pub brand: Mutex<Option<String>>, pub brand: Mutex<Option<String>>,
/// The minecraft protocol version used by the client.
pub protocol_version: AtomicI32, pub protocol_version: AtomicI32,
/// The current connection state of the client (e.g., Handshaking, Status, Play).
pub connection_state: Mutex<ConnectionState>, pub connection_state: Mutex<ConnectionState>,
/// Whether encryption is enabled for the connection.
pub encryption: AtomicBool, pub encryption: AtomicBool,
/// Indicates if the client connection is closed.
pub closed: AtomicBool, pub closed: AtomicBool,
/// A unique token identifying the client.
pub token: Token, pub token: Token,
/// The underlying TCP connection to the client.
pub connection: Arc<Mutex<TcpStream>>, pub connection: Arc<Mutex<TcpStream>>,
/// The client's IP address.
pub address: Mutex<SocketAddr>, pub address: Mutex<SocketAddr>,
/// The packet encoder for outgoing packets.
enc: Arc<Mutex<PacketEncoder>>, enc: Arc<Mutex<PacketEncoder>>,
/// The packet decoder for incoming packets.
dec: Arc<Mutex<PacketDecoder>>, dec: Arc<Mutex<PacketDecoder>>,
/// A queue of raw packets received from the client, waiting to be processed.
pub client_packets_queue: Arc<Mutex<Vec<RawPacket>>>, pub client_packets_queue: Arc<Mutex<Vec<RawPacket>>>,
/// Indicates whether the client should be converted into a player.
pub make_player: AtomicBool, 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) { pub fn add_packet(&self, packet: RawPacket) {
let mut client_packets_queue = self.client_packets_queue.lock().unwrap(); let mut client_packets_queue = self.client_packets_queue.lock().unwrap();
client_packets_queue.push(packet); client_packets_queue.push(packet);
} }
/// enables encryption /// Enables encryption
pub fn enable_encryption( pub fn enable_encryption(
&self, &self,
shared_secret: &[u8], // decrypted shared_secret: &[u8], // decrypted
@ -125,7 +154,7 @@ impl Client {
Ok(()) Ok(())
} }
// Compression threshold, Compression level /// Compression threshold, Compression level
pub fn set_compression(&self, compression: Option<(u32, u32)>) { pub fn set_compression(&self, compression: Option<(u32, u32)>) {
self.dec self.dec
.lock() .lock()
@ -161,6 +190,7 @@ impl Client {
Ok(()) Ok(())
} }
/// Processes all packets send by the client
pub async fn process_packets(&self, server: &Arc<Server>) { pub async fn process_packets(&self, server: &Arc<Server>) {
while let Some(mut packet) = self.client_packets_queue.lock().unwrap().pop() { while let Some(mut packet) = self.client_packets_queue.lock().unwrap().pop() {
match self.handle_packet(server, &mut packet).await { 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 /// Kicks the Client with a reason depending on the connection state
pub fn kick(&self, reason: &str) { pub fn kick(&self, reason: &str) {
dbg!(reason);
match *self.connection_state.lock().unwrap() { match *self.connection_state.lock().unwrap() {
ConnectionState::Login => { ConnectionState::Login => {
self.try_send_packet(&CLoginDisconnect::new( self.try_send_packet(&CLoginDisconnect::new(

View File

@ -1,5 +1,7 @@
use std::sync::{atomic::AtomicBool, Arc, Mutex}; use std::sync::{atomic::AtomicBool, Arc, Mutex};
use num_derive::{FromPrimitive, ToPrimitive};
use num_traits::ToPrimitive;
use pumpkin_core::math::{ use pumpkin_core::math::{
get_section_cord, position::WorldPosition, vector2::Vector2, vector3::Vector3, get_section_cord, position::WorldPosition, vector2::Vector2, vector3::Vector3,
}; };
@ -14,27 +16,44 @@ use crate::world::World;
pub mod player; pub mod player;
pub struct Entity { pub struct Entity {
/// A unique identifier for the entity
pub entity_id: EntityId, pub entity_id: EntityId,
/// The type of entity (e.g., player, zombie, item)
pub entity_type: EntityType, pub entity_type: EntityType,
/// The world in which the entity exists.
pub world: Arc<World>, pub world: Arc<World>,
/// The entity's current health level.
pub health: Mutex<f32>,
/// The entity's current position in the world
pub pos: Mutex<Vector3<f64>>, pub pos: Mutex<Vector3<f64>>,
/// The entity's position rounded to the nearest block coordinates
pub block_pos: Mutex<WorldPosition>, pub block_pos: Mutex<WorldPosition>,
/// The chunk coordinates of the entity's current position
pub chunk_pos: Mutex<Vector2<i32>>, pub chunk_pos: Mutex<Vector2<i32>>,
/// Indicates whether the entity is sneaking
pub sneaking: AtomicBool, pub sneaking: AtomicBool,
/// Indicates whether the entity is sprinting
pub sprinting: AtomicBool, pub sprinting: AtomicBool,
/// Indicates whether the entity is flying due to a fall
pub fall_flying: AtomicBool, pub fall_flying: AtomicBool,
/// The entity's current velocity vector, aka Knockback
pub velocity: Mutex<Vector3<f64>>, pub velocity: Mutex<Vector3<f64>>,
// Should be not trusted /// Indicates whether the entity is on the ground (may not always be accurate).
pub on_ground: AtomicBool, pub on_ground: AtomicBool,
/// The entity's yaw rotation (horizontal rotation) ← →
pub yaw: Mutex<f32>, pub yaw: Mutex<f32>,
/// The entity's head yaw rotation (horizontal rotation of the head)
pub head_yaw: Mutex<f32>, pub head_yaw: Mutex<f32>,
/// The entity's pitch rotation (vertical rotation) ↑ ↓
pub pitch: Mutex<f32>, pub pitch: Mutex<f32>,
/// The height of the entity's eyes from the ground.
// TODO: Change this in diffrent poses // TODO: Change this in diffrent poses
pub standing_eye_height: f32, pub standing_eye_height: f32,
/// The entity's current pose (e.g., standing, sitting, swimming).
pub pose: Mutex<EntityPose>, pub pose: Mutex<EntityPose>,
} }
@ -54,6 +73,8 @@ impl Entity {
chunk_pos: Mutex::new(Vector2::new(0, 0)), chunk_pos: Mutex::new(Vector2::new(0, 0)),
sneaking: AtomicBool::new(false), sneaking: AtomicBool::new(false),
world, world,
// TODO: Load this from previous instance
health: Mutex::new(20.0),
sprinting: AtomicBool::new(false), sprinting: AtomicBool::new(false),
fall_flying: AtomicBool::new(false), fall_flying: AtomicBool::new(false),
yaw: Mutex::new(0.0), 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) { pub fn set_pos(&self, x: f64, y: f64, z: f64) {
let mut pos = self.pos.lock().unwrap(); let mut pos = self.pos.lock().unwrap();
if pos.x != x || pos.y != y || pos.z != z { 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) { pub fn set_rotation(&self, yaw: f32, pitch: f32) {
// TODO // TODO
*self.yaw.lock().unwrap() = yaw; *self.yaw.lock().unwrap() = yaw;
*self.pitch.lock().unwrap() = pitch *self.pitch.lock().unwrap() = pitch
} }
/// Removes the Entity from their current World
pub async fn remove(&mut self) { pub async fn remove(&mut self) {
self.world.remove_entity(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) { pub fn knockback(&self, strength: f64, x: f64, z: f64) {
// This has some vanilla magic // This has some vanilla magic
let mut x = x; let mut x = x;
@ -125,7 +154,7 @@ impl Entity {
assert!(self.sneaking.load(std::sync::atomic::Ordering::Relaxed) != sneaking); assert!(self.sneaking.load(std::sync::atomic::Ordering::Relaxed) != sneaking);
self.sneaking self.sneaking
.store(sneaking, std::sync::atomic::Ordering::Relaxed); .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 { // if sneaking {
// self.set_pose(EntityPose::Crouching).await; // self.set_pose(EntityPose::Crouching).await;
// } else { // } else {
@ -137,7 +166,7 @@ impl Entity {
assert!(self.sprinting.load(std::sync::atomic::Ordering::Relaxed) != sprinting); assert!(self.sprinting.load(std::sync::atomic::Ordering::Relaxed) != sprinting);
self.sprinting self.sprinting
.store(sprinting, std::sync::atomic::Ordering::Relaxed); .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 { 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); assert!(self.fall_flying.load(std::sync::atomic::Ordering::Relaxed) != fall_flying);
self.fall_flying self.fall_flying
.store(fall_flying, std::sync::atomic::Ordering::Relaxed); .store(fall_flying, std::sync::atomic::Ordering::Relaxed);
self.set_flag(Self::FALL_FLYING_FLAG_INDEX, fall_flying) self.set_flag(Flag::FallFlying, fall_flying).await;
.await;
} }
pub const ON_FIRE_FLAG_INDEX: u32 = 0; async fn set_flag(&self, flag: Flag, value: bool) {
pub const SNEAKING_FLAG_INDEX: u32 = 1; let index = flag.to_u32().unwrap();
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) {
let mut b = 0i8; let mut b = 0i8;
if value { if value {
b |= 1 << index; b |= 1 << index;
@ -180,3 +202,28 @@ impl Entity {
self.world.broadcast_packet_all(&packet) 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,
}

View File

@ -38,55 +38,49 @@ use crate::{
use super::Entity; use super::Entity;
pub struct PlayerAbilities { /// Represents a Minecraft player entity.
pub invulnerable: bool, ///
pub flying: bool, /// A `Player` is a special type of entity that represents a human player connected to the server.
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,
}
}
}
pub struct Player { pub struct Player {
/// The underlying entity object that represents the player.
pub entity: Entity, pub entity: Entity,
/// The player's game profile information, including their username and UUID.
pub gameprofile: GameProfile, pub gameprofile: GameProfile,
/// The client connection associated with the player.
pub client: Client, pub client: Client,
/// The player's configuration settings. Changes when the Player changes their settings.
pub config: Mutex<PlayerConfig>, pub config: Mutex<PlayerConfig>,
/// Current gamemode /// The player's current gamemode (e.g., Survival, Creative, Adventure).
pub gamemode: Mutex<GameMode>, pub gamemode: Mutex<GameMode>,
// TODO: prbly should put this into an Living Entitiy or something /// The player's hunger level.
pub health: Mutex<f32>,
pub food: AtomicI32, pub food: AtomicI32,
/// The player's food saturation level.
pub food_saturation: Mutex<f32>, pub food_saturation: Mutex<f32>,
/// The player's inventory, containing items and equipment.
pub inventory: Mutex<PlayerInventory>, pub inventory: Mutex<PlayerInventory>,
/// The ID of the currently open container (if any).
pub open_container: Mutex<Option<u64>>, pub open_container: Mutex<Option<u64>>,
/// The item currently being held by the player.
pub carried_item: Mutex<Option<ItemStack>>, pub carried_item: Mutex<Option<ItemStack>>,
/// The player's abilities and special powers.
/// send `send_abilties_update` when changed ///
/// 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, 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<Vector3<f64>>, pub last_position: Mutex<Vector3<f64>>,
/// 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 // 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, pub current_block_destroy_stage: AtomicU8,
/// A counter for teleport IDs used to track pending teleports.
pub teleport_id_count: AtomicI32, 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<Option<(VarInt, Vector3<f64>)>>, pub awaiting_teleport: Mutex<Option<(VarInt, Vector3<f64>)>>,
/// The coordinates of the chunk section the player is currently watching.
pub watched_section: Mutex<Vector3<i32>>, pub watched_section: Mutex<Vector3<i32>>,
} }
@ -112,7 +106,6 @@ impl Player {
client, client,
awaiting_teleport: Mutex::new(None), awaiting_teleport: Mutex::new(None),
// TODO: Load this from previous instance // TODO: Load this from previous instance
health: Mutex::new(20.0),
food: AtomicI32::new(20), food: AtomicI32::new(20),
food_saturation: Mutex::new(20.0), food_saturation: Mutex::new(20.0),
current_block_destroy_stage: AtomicU8::new(0), current_block_destroy_stage: AtomicU8::new(0),
@ -136,6 +129,7 @@ impl Player {
self.entity.entity_id self.entity.entity_id
} }
/// Updates the current abilities the Player has
pub fn send_abilties_update(&mut self) { pub fn send_abilties_update(&mut self) {
let mut b = 0i8; let mut b = 0i8;
let abilities = &self.abilities; let abilities = &self.abilities;
@ -225,7 +219,7 @@ impl Player {
} }
pub fn update_health(&self, health: f32, food: i32, food_saturation: f32) { 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.store(food, std::sync::atomic::Ordering::Relaxed);
*self.food_saturation.lock().unwrap() = food_saturation; *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)] #[derive(FromPrimitive, Clone)]
pub enum Hand { pub enum Hand {
/// The player's primary hand (usually the right hand).
Main, Main,
/// The player's off-hand (usually the left hand).
Off, Off,
} }
/// Represents the player's chat mode settings.
#[derive(FromPrimitive, Clone)] #[derive(FromPrimitive, Clone)]
pub enum ChatMode { pub enum ChatMode {
/// Chat is enabled for the player.
Enabled, Enabled,
/// The player should only see chat messages from commands
CommandsOnly, CommandsOnly,
/// All messages should be hidden
Hidden, Hidden,
} }

View File

@ -169,11 +169,8 @@ fn main() -> io::Result<()> {
} }
if closed { if closed {
if let Some(player) = players.remove(&token) { if let Some(player) = players.remove(&token) {
dbg!("a");
player.remove().await; player.remove().await;
dbg!("b");
let connection = &mut player.client.connection.lock().unwrap(); let connection = &mut player.client.connection.lock().unwrap();
dbg!("c");
poll.registry().deregister(connection.by_ref())?; poll.registry().deregister(connection.by_ref())?;
} }

View File

@ -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 { pub fn new_entity_id(&self) -> EntityId {
self.entity_id.fetch_add(1, Ordering::SeqCst) self.entity_id.fetch_add(1, Ordering::SeqCst)
} }
@ -191,20 +192,22 @@ impl Server {
}; };
StatusResponse { StatusResponse {
version: Version { version: Some(Version {
name: CURRENT_MC_VERSION.into(), name: CURRENT_MC_VERSION.into(),
protocol: CURRENT_MC_PROTOCOL, protocol: CURRENT_MC_PROTOCOL,
}, }),
players: Players { players: Some(Players {
max: config.max_players, max: config.max_players,
online: 0, online: 0,
sample: vec![Sample { sample: vec![Sample {
name: "".into(), name: "".into(),
id: "".into(), id: "".into(),
}], }],
}, }),
description: config.motd.clone(), description: config.motd.clone(),
favicon: icon, favicon: icon,
// TODO
enforece_secure_chat: false,
} }
} }

View File

@ -26,10 +26,21 @@ use crate::{
entity::{player::Player, Entity}, 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 { pub struct World {
/// The underlying level, responsible for chunk management and terrain generation.
pub level: Arc<Mutex<Level>>, pub level: Arc<Mutex<Level>>,
/// A map of active players within the world, keyed by their unique token.
pub current_players: Arc<Mutex<HashMap<Token, Arc<Player>>>>, pub current_players: Arc<Mutex<HashMap<Token, Arc<Player>>>>,
// entities, players... // TODO: entities
} }
impl World { 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<P>(&self, packet: &P) pub fn broadcast_packet_all<P>(&self, packet: &P)
where where
P: ClientPacket, 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<P>(&self, except: &[Token], packet: &P) pub fn broadcast_packet_expect<P>(&self, except: &[Token], packet: &P)
where where
P: ClientPacket, P: ClientPacket,