Better doc
This commit is contained in:
parent
ada0d82a72
commit
0bebc6fc31
7
pumpkin-config/README.md
Normal file
7
pumpkin-config/README.md
Normal 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
0
pumpkin-entity/README.md
Normal 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`
|
||||||
@ -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
|
||||||
|
|||||||
@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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
17
pumpkin-world/README.md
Normal 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
|
||||||
@ -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
|
||||||
///
|
///
|
||||||
|
|||||||
@ -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),
|
||||||
|
}
|
||||||
|
|||||||
@ -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
|
||||||
|
|||||||
@ -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(
|
||||||
|
|||||||
@ -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,
|
||||||
|
}
|
||||||
|
|||||||
@ -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,
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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())?;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -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,
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user