From 0e831dbe001277c973066471eb84bf58948ed582 Mon Sep 17 00:00:00 2001 From: Snowiiii Date: Wed, 7 Aug 2024 00:01:04 +0200 Subject: [PATCH 1/7] connection: enable tcp nodealy --- pumpkin/src/main.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/pumpkin/src/main.rs b/pumpkin/src/main.rs index 967e13622..92fe79bb8 100644 --- a/pumpkin/src/main.rs +++ b/pumpkin/src/main.rs @@ -111,6 +111,9 @@ fn main() -> io::Result<()> { return Err(e); } }; + if let Err(e) = connection.set_nodelay(true) { + log::warn!("failed to set TCP_NODELAY {e}"); + } log::info!("Accepted connection from: {}", address); From d0a8790d7bd706d4bc31efed6d1e7a8fd6d1e325 Mon Sep 17 00:00:00 2001 From: Snowiiii Date: Wed, 7 Aug 2024 10:47:29 +0200 Subject: [PATCH 2/7] Add Clientbound Player Chat Message --- .../src/client/play/c_player_chat_message.rs | 70 +++++++++++++++++++ pumpkin-protocol/src/client/play/mod.rs | 2 + pumpkin/src/main.rs | 18 ++--- 3 files changed, 77 insertions(+), 13 deletions(-) create mode 100644 pumpkin-protocol/src/client/play/c_player_chat_message.rs diff --git a/pumpkin-protocol/src/client/play/c_player_chat_message.rs b/pumpkin-protocol/src/client/play/c_player_chat_message.rs new file mode 100644 index 000000000..cfbdb63ca --- /dev/null +++ b/pumpkin-protocol/src/client/play/c_player_chat_message.rs @@ -0,0 +1,70 @@ +use num_derive::FromPrimitive; +use serde::Serialize; + +use crate::{text::TextComponent, VarInt}; + +#[derive(Serialize)] +pub struct PlayerChatMessage<'a> { + sender: uuid::Uuid, + index: VarInt, + message_signature: Option<&'a [u8]>, + message: String, + timestamp: i64, + salt: i64, + previous_messages: &'a [PreviousMessage<'a>], // max 20 + unsigned_content: Option, + /// See `FilterType` + filter_type: VarInt, + chat_type: VarInt, + sender_name: TextComponent, + target_name: Option, +} + +impl<'a> PlayerChatMessage<'a> { + #[allow(clippy::too_many_arguments)] + pub fn new( + sender: uuid::Uuid, + index: VarInt, + message_signature: Option<&'a [u8]>, + message: String, + timestamp: i64, + salt: i64, + previous_messages: &'a [PreviousMessage<'a>], + unsigned_content: Option, + filter_type: VarInt, + chat_type: VarInt, + sender_name: TextComponent, + target_name: Option, + ) -> Self { + Self { + sender, + index, + message_signature, + message, + timestamp, + salt, + previous_messages, + unsigned_content, + filter_type, + chat_type, + sender_name, + target_name, + } + } +} + +#[derive(Serialize)] +pub struct PreviousMessage<'a> { + message_id: VarInt, + signature: Option<&'a [u8]>, +} + +#[derive(FromPrimitive)] +pub enum FilterType { + /// Message is not filtered at all + PassThrough, + /// Message is fully filtered + FullyFiltered, + /// Only some characters in the message are filtered + PartiallyFiltered, +} diff --git a/pumpkin-protocol/src/client/play/mod.rs b/pumpkin-protocol/src/client/play/mod.rs index e807663cd..0399b656a 100644 --- a/pumpkin-protocol/src/client/play/mod.rs +++ b/pumpkin-protocol/src/client/play/mod.rs @@ -5,6 +5,7 @@ mod c_head_rot; mod c_login; mod c_play_disconnect; mod c_player_abilities; +mod c_player_chat_message; mod c_player_info_update; mod c_set_held_item; mod c_spawn_player; @@ -22,6 +23,7 @@ pub use c_head_rot::*; pub use c_login::*; pub use c_play_disconnect::*; pub use c_player_abilities::*; +pub use c_player_chat_message::*; pub use c_player_info_update::*; pub use c_set_held_item::*; pub use c_spawn_player::*; diff --git a/pumpkin/src/main.rs b/pumpkin/src/main.rs index 92fe79bb8..13c25ce8f 100644 --- a/pumpkin/src/main.rs +++ b/pumpkin/src/main.rs @@ -6,11 +6,7 @@ use client::Client; use commands::handle_command; use configuration::AdvancedConfiguration; -use std::{ - collections::HashMap, - rc::Rc, - thread, -}; +use std::{collections::HashMap, rc::Rc, thread}; use client::interrupted; use configuration::BasicConfiguration; @@ -64,10 +60,7 @@ fn main() -> io::Result<()> { let mut connections: HashMap>> = HashMap::new(); - let mut server = Server::new(( - basic_config, - advanced_configuration, - )); + let mut server = Server::new((basic_config, advanced_configuration)); log::info!("Started Server took {}ms", time.elapsed().as_millis()); log::info!("You now can connect to the server"); @@ -112,8 +105,8 @@ fn main() -> io::Result<()> { } }; if let Err(e) = connection.set_nodelay(true) { - log::warn!("failed to set TCP_NODELAY {e}"); - } + log::warn!("failed to set TCP_NODELAY {e}"); + } log::info!("Accepted connection from: {}", address); @@ -129,8 +122,7 @@ fn main() -> io::Result<()> { connection, addr, ))); - server - .add_client(rc_token, Rc::clone(&client)); + server.add_client(rc_token, Rc::clone(&client)); connections.insert(token, client); }, From ea0128c50b2a190d38c3f28d29e294066529858b Mon Sep 17 00:00:00 2001 From: Snowiiii Date: Wed, 7 Aug 2024 13:32:48 +0200 Subject: [PATCH 3/7] Improve Authentication --- README.md | 2 + .../src/client/login/c_login_success.rs | 5 +- .../src/client/play/c_player_info_update.rs | 5 +- pumpkin/src/client/authentication.rs | 67 +++++++- pumpkin/src/client/client_packet.rs | 40 ++++- pumpkin/src/client/mod.rs | 2 +- pumpkin/src/config/auth_config.rs | 93 +++++++++++ pumpkin/src/{ => config}/configuration.rs | 18 +- pumpkin/src/config/mod.rs | 154 ++++++++++++++++++ pumpkin/src/main.rs | 6 +- pumpkin/src/server.rs | 2 +- 11 files changed, 362 insertions(+), 32 deletions(-) create mode 100644 pumpkin/src/config/auth_config.rs rename pumpkin/src/{ => config}/configuration.rs (92%) create mode 100644 pumpkin/src/config/mod.rs diff --git a/README.md b/README.md index 9a58292cb..e919bfafd 100644 --- a/README.md +++ b/README.md @@ -14,8 +14,10 @@ Pumpkin is currently under heavy development. ## What Pumpkin aims to be: - A fast, efficient, and scalable Minecraft server +- Use Multi-Threading - Compatible with the latest Minecraft server version - Adherent to Vanilla game mechanics +- Be Secure and prevent all Exploits found - Highly configurable with the ability to disable unnecessary features - A platform for plugin development diff --git a/pumpkin-protocol/src/client/login/c_login_success.rs b/pumpkin-protocol/src/client/login/c_login_success.rs index 265527802..065dc5e10 100644 --- a/pumpkin-protocol/src/client/login/c_login_success.rs +++ b/pumpkin-protocol/src/client/login/c_login_success.rs @@ -33,10 +33,7 @@ impl<'a> ClientPacket for CLoginSuccess<'a> { bytebuf.put_list::(self.properties, |p, v| { p.put_string(&v.name); p.put_string(&v.value); - // has signature ? - // todo: for some reason we get "got too many bytes error when using a signature" - p.put_bool(false); - // p.put_option(&v.signature, |p,v| p.put_string(v)); + p.put_option(&v.signature, |p, v| p.put_string(v)); }); bytebuf.put_bool(self.strict_error_handling); } diff --git a/pumpkin-protocol/src/client/play/c_player_info_update.rs b/pumpkin-protocol/src/client/play/c_player_info_update.rs index 23028c855..163c7ce97 100644 --- a/pumpkin-protocol/src/client/play/c_player_info_update.rs +++ b/pumpkin-protocol/src/client/play/c_player_info_update.rs @@ -34,10 +34,7 @@ impl<'a> ClientPacket for CPlayerInfoUpdate<'a> { p.put_list::(properties, |p, v| { p.put_string(&v.name); p.put_string(&v.value); - // has signature ? - // todo: for some reason we get "got too many bytes error when using a signature" - p.put_bool(false); - // todo signature + p.put_option(&v.signature, |p, v| p.put_string(v)); }); } PlayerAction::InitializeChat(_) => todo!(), diff --git a/pumpkin/src/client/authentication.rs b/pumpkin/src/client/authentication.rs index b8f57c0fc..dcebe09a3 100644 --- a/pumpkin/src/client/authentication.rs +++ b/pumpkin/src/client/authentication.rs @@ -1,19 +1,49 @@ -use std::net::IpAddr; +use std::{collections::HashMap, net::IpAddr}; +use base64::{engine::general_purpose, Engine}; use num_bigint::BigInt; use pumpkin_protocol::Property; -use reqwest::StatusCode; -use serde::Deserialize; +use reqwest::{StatusCode, Url}; +use serde::{Deserialize, Serialize}; use thiserror::Error; use uuid::Uuid; -use crate::server::Server; +use crate::{config::auth_config::TextureConfig, server::Server}; + +#[derive(Deserialize, Clone, Debug)] +#[allow(non_snake_case)] +#[allow(dead_code)] +pub struct ProfileTextures { + timestamp: i64, + profileId: Uuid, + profileName: String, + signatureRequired: bool, + textures: HashMap, +} + +#[derive(Deserialize, Clone, Debug)] +#[allow(non_snake_case)] +#[allow(dead_code)] +pub struct Texture { + url: String, + metadata: Option>, +} + +#[derive(Deserialize, Serialize, Clone, Debug, PartialEq, Eq)] +pub enum ProfileAction { + #[serde(rename = "FORCED_NAME_CHANGE")] + ForcedNameChange, + #[serde(rename = "USING_BANNED_SKIN")] + UsingBannedSkin, +} #[derive(Deserialize, Clone, Debug)] pub struct GameProfile { pub id: Uuid, pub name: String, pub properties: Vec, + #[serde(rename = "profileActions")] + pub profile_actions: Option>, } pub fn authenticate( @@ -22,8 +52,13 @@ pub fn authenticate( ip: &IpAddr, server: &mut Server, ) -> Result { + assert!(server.advanced_config.authentication.use_authentication); assert!(server.auth_client.is_some()); - let address = if server.base_config.prevent_proxy_connections { + let address = if server + .advanced_config + .authentication + .prevent_proxy_connections + { format!("https://sessionserver.mojang.com/session/minecraft/hasJoined?username={username}&serverId={server_hash}&ip={ip}") } else { format!("https://sessionserver.mojang.com/session/minecraft/hasJoined?username={username}&serverId={server_hash}") @@ -44,10 +79,32 @@ pub 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(); + dbg!(&textures); + for texture in textures.textures { + is_texture_url_valid(Url::parse(&texture.1.url).unwrap(), config); + } +} + 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 { + let scheme = url.scheme(); + if !config.allowed_url_schemes.contains(&scheme.to_string()) { + return false; + } + let domain = url.domain().unwrap_or(""); + if !config.allowed_url_domains.contains(&domain.to_string()) { + return false; + } + true +} + #[derive(Error, Debug)] pub enum AuthError { #[error("Authentication servers are down")] diff --git a/pumpkin/src/client/client_packet.rs b/pumpkin/src/client/client_packet.rs index 3e44cbce3..a62286c40 100644 --- a/pumpkin/src/client/client_packet.rs +++ b/pumpkin/src/client/client_packet.rs @@ -23,7 +23,10 @@ use crate::{ server::Server, }; -use super::{authentication::auth_digest, Client, EncryptionError, PlayerConfig}; +use super::{ + authentication::{auth_digest, unpack_textures}, + Client, EncryptionError, PlayerConfig, +}; /// Processes incoming Packets from the Client to the Server /// Implements the `Client` Packets @@ -45,12 +48,15 @@ impl Client { } pub fn handle_login_start(&mut self, server: &mut Server, login_start: SLoginStart) { + // todo: do basic name validation dbg!("login start"); // default game profile, when no online mode + // todo: make offline uuid self.gameprofile = Some(GameProfile { id: login_start.uuid, name: login_start.name, properties: vec![], + profile_actions: None, }); // todo: check config for encryption @@ -91,10 +97,40 @@ impl Client { &ip, server, ) { - Ok(p) => self.gameprofile = Some(p), + Ok(p) => { + // Check if player should join + if let Some(p) = &p.profile_actions { + if !server + .advanced_config + .authentication + .player_profile + .allow_banned_players + { + if !p.is_empty() { + self.kick("Your account can't join"); + } + } else { + for allowed in server + .advanced_config + .authentication + .player_profile + .allowed_actions + .clone() + { + if !p.contains(&allowed) { + self.kick("Your account can't join"); + } + } + } + } + self.gameprofile = Some(p); + } Err(e) => self.kick(&e.to_string()), } } + for ele in self.gameprofile.as_ref().unwrap().properties.clone() { + unpack_textures(ele, &server.advanced_config.authentication.textures); + } if let Some(profile) = self.gameprofile.as_ref().cloned() { let packet = CLoginSuccess::new(profile.id, profile.name, &profile.properties, false); diff --git a/pumpkin/src/client/mod.rs b/pumpkin/src/client/mod.rs index 908f644b6..99fc0c2c4 100644 --- a/pumpkin/src/client/mod.rs +++ b/pumpkin/src/client/mod.rs @@ -38,7 +38,7 @@ use pumpkin_protocol::{ use std::io::Read; use thiserror::Error; -mod authentication; +pub mod authentication; mod client_packet; pub mod player_packet; diff --git a/pumpkin/src/config/auth_config.rs b/pumpkin/src/config/auth_config.rs new file mode 100644 index 000000000..0fe373ca8 --- /dev/null +++ b/pumpkin/src/config/auth_config.rs @@ -0,0 +1,93 @@ +use serde::{Deserialize, Serialize}; + +use crate::client::authentication::ProfileAction; + +#[derive(Deserialize, Serialize)] +pub struct Authentication { + /// Whether to use Mojang authentication. + pub use_authentication: bool, + + /// Prevent proxy connections. + pub prevent_proxy_connections: bool, + + /// Player profile handling. + pub player_profile: PlayerProfileConfig, + + /// Texture handling. + pub textures: TextureConfig, +} + +#[derive(Deserialize, Serialize)] +pub struct PlayerProfileConfig { + /// Allow players flagged by Mojang (banned, forced name change). + pub allow_banned_players: bool, + /// Depends on the value above + pub allowed_actions: Vec, +} + +impl Default for PlayerProfileConfig { + fn default() -> Self { + Self { + allow_banned_players: false, + allowed_actions: vec![ + ProfileAction::ForcedNameChange, + ProfileAction::UsingBannedSkin, + ], + } + } +} + +#[derive(Deserialize, Serialize)] +pub struct TextureConfig { + /// Whether to use player textures. + pub enabled: bool, + + pub allowed_url_schemes: Vec, + pub allowed_url_domains: Vec, + + /// Specific texture types. + pub types: TextureTypes, +} + +impl Default for TextureConfig { + fn default() -> Self { + Self { + enabled: true, + allowed_url_schemes: vec!["http".into(), "https".into()], + allowed_url_domains: vec![".minecraft.net".into(), ".mojang.com".into()], + types: Default::default(), + } + } +} + +#[derive(Deserialize, Serialize)] +pub struct TextureTypes { + /// Use player skins. + pub skin: bool, + /// Use player capes. + pub cape: bool, + /// Use player elytras. + /// (i didn't know myself that there are custom elytras) + pub elytra: bool, +} + +impl Default for TextureTypes { + fn default() -> Self { + Self { + skin: true, + cape: true, + elytra: true, + } + } +} + +impl Default for Authentication { + fn default() -> Self { + Self { + use_authentication: true, + prevent_proxy_connections: true, + player_profile: Default::default(), + textures: Default::default(), + } + } +} diff --git a/pumpkin/src/configuration.rs b/pumpkin/src/config/configuration.rs similarity index 92% rename from pumpkin/src/configuration.rs rename to pumpkin/src/config/configuration.rs index 37fa53baa..9f28a0f72 100644 --- a/pumpkin/src/configuration.rs +++ b/pumpkin/src/config/configuration.rs @@ -4,6 +4,8 @@ use serde::{Deserialize, Serialize}; use crate::{entity::player::GameMode, server::Difficulty}; +use super::auth_config::Authentication; + /// Current Config version of the Base Config const CURRENT_BASE_VERSION: &str = "1.0.0"; @@ -12,19 +14,15 @@ const CURRENT_BASE_VERSION: &str = "1.0.0"; /// This also allows you get some Performance or Resource boosts. /// Important: The Configuration should match Vanilla by default pub struct AdvancedConfiguration { - /// Requires Online mode - /// Should player have skins - pub use_skins: bool, - /// Should chat be enabled - pub enable_chat: bool, - pub commands: Commands, + pub authentication: Authentication, } #[derive(Deserialize, Serialize)] pub struct Commands { - // Are commands from the Console accepted ? + /// Are commands from the Console accepted ? pub use_console: bool, + // todo commands... } impl Default for Commands { @@ -37,8 +35,7 @@ impl Default for Commands { impl Default for AdvancedConfiguration { fn default() -> Self { Self { - use_skins: true, - enable_chat: true, + authentication: Authentication::default(), commands: Commands::default(), } } @@ -74,8 +71,6 @@ pub struct BasicConfiguration { pub online_mode: bool, /// Whether packet encryption is enabled. Required when online mode is enabled. pub encryption: bool, - /// Whether to prevent proxy connections. - pub prevent_proxy_connections: bool, /// The server's description displayed on the status screen. pub motd: String, /// The default game mode for players. @@ -99,7 +94,6 @@ impl Default for BasicConfiguration { hardcore: false, online_mode: true, encryption: true, - prevent_proxy_connections: true, motd: "A Blazing fast Pumpkin Server!".to_string(), default_gamemode: GameMode::Survival, } diff --git a/pumpkin/src/config/mod.rs b/pumpkin/src/config/mod.rs new file mode 100644 index 000000000..3430a2330 --- /dev/null +++ b/pumpkin/src/config/mod.rs @@ -0,0 +1,154 @@ +use std::path::Path; + +use auth_config::Authentication; +use serde::{Deserialize, Serialize}; + +use crate::{entity::player::GameMode, server::Difficulty}; + +pub mod auth_config; + +/// Current Config version of the Base Config +const CURRENT_BASE_VERSION: &str = "1.0.0"; + +#[derive(Deserialize, Serialize)] +/// The idea is that Pumpkin should very customizable, You can Enable or Disable Features depning on your needs. +/// This also allows you get some Performance or Resource boosts. +/// Important: The Configuration should match Vanilla by default +pub struct AdvancedConfiguration { + pub commands: Commands, + pub authentication: Authentication, +} + +#[derive(Deserialize, Serialize)] +pub struct Commands { + /// Are commands from the Console accepted ? + pub use_console: bool, + // todo commands... +} + +impl Default for Commands { + fn default() -> Self { + Self { use_console: true } + } +} + +/// Important: The Configuration should match Vanilla by default +impl Default for AdvancedConfiguration { + fn default() -> Self { + Self { + authentication: Authentication::default(), + commands: Commands::default(), + } + } +} + +#[derive(Serialize, Deserialize)] +pub struct BasicConfiguration { + /// A version identifier for the configuration format. + pub config_version: String, + /// The address to bind the server to. + pub server_address: String, + /// The port to listen on. + pub server_port: u16, + /// The seed for world generation. + pub seed: String, + /// The maximum number of players allowed on the server. + pub max_players: u32, + /// The maximum view distance for players. + pub view_distance: u8, + /// The maximum simulated view distance. + pub simulation_distance: u8, + /// The path to the resource pack. + pub resource_pack: String, + /// The SHA1 hash of the resource pack. + pub resource_pack_sha1: String, + /// The default game difficulty. + pub default_difficulty: Difficulty, + /// Whether the Nether dimension is enabled. + pub allow_nether: bool, + /// Whether the server is in hardcore mode. + pub hardcore: bool, + /// Whether online mode is enabled. Requires valid Minecraft accounts. + pub online_mode: bool, + /// Whether packet encryption is enabled. Required when online mode is enabled. + pub encryption: bool, + /// The server's description displayed on the status screen. + pub motd: String, + /// The default game mode for players. + pub default_gamemode: GameMode, +} + +impl Default for BasicConfiguration { + fn default() -> Self { + Self { + config_version: CURRENT_BASE_VERSION.to_string(), + server_address: "127.0.0.1".to_string(), + server_port: 25565, + seed: "".to_string(), + max_players: 100000, + view_distance: 10, + simulation_distance: 10, + resource_pack: "".to_string(), + resource_pack_sha1: "".to_string(), + default_difficulty: Difficulty::Normal, + allow_nether: true, + hardcore: false, + online_mode: true, + encryption: true, + motd: "A Blazing fast Pumpkin Server!".to_string(), + default_gamemode: GameMode::Survival, + } + } +} + +impl AdvancedConfiguration { + pub fn load>(path: P) -> AdvancedConfiguration { + if path.as_ref().exists() { + let toml = std::fs::read_to_string(path).expect("Couldn't read configuration"); + toml::from_str(toml.as_str()).expect("Couldn't parse, Proberbly old config") + } else { + let config = AdvancedConfiguration::default(); + let toml = toml::to_string(&config).expect("Couldn't create toml!"); + std::fs::write(path, toml).expect("Couldn't save configuration"); + config + } + } +} + +impl BasicConfiguration { + pub fn load>(path: P) -> BasicConfiguration { + if path.as_ref().exists() { + let toml = std::fs::read_to_string(path).expect("Couldn't read configuration"); + toml::from_str(toml.as_str()).expect("Couldn't parse") + } else { + let config = BasicConfiguration::default(); + let toml = toml::to_string(&config).expect("Couldn't create toml!"); + std::fs::write(path, toml).expect("Couldn't save configuration"); + config.validate(); + config + } + } + + pub fn validate(&self) { + assert_eq!( + self.config_version, CURRENT_BASE_VERSION, + "Config version does not match used Config version. Please update your config" + ); + assert!(self.view_distance >= 2, "View distance must be atleast 2"); + assert!( + self.view_distance <= 32, + "View distance must be less than 32" + ); + if self.online_mode { + assert!( + self.encryption, + "When Online Mode is enabled, Encryption must be enabled" + ) + } + assert_eq!( + !self.resource_pack.is_empty(), + !self.resource_pack_sha1.is_empty(), + "Resource Pack path or Sha1 hash is missing" + ); + } +} diff --git a/pumpkin/src/main.rs b/pumpkin/src/main.rs index 13c25ce8f..0b5372ae2 100644 --- a/pumpkin/src/main.rs +++ b/pumpkin/src/main.rs @@ -4,12 +4,12 @@ use std::io::{self}; use client::Client; use commands::handle_command; -use configuration::AdvancedConfiguration; +use config::AdvancedConfiguration; use std::{collections::HashMap, rc::Rc, thread}; use client::interrupted; -use configuration::BasicConfiguration; +use config::BasicConfiguration; use server::Server; // Setup some tokens to allow us to identify which event is for which socket. @@ -17,7 +17,7 @@ const SERVER: Token = Token(0); pub mod client; pub mod commands; -pub mod configuration; +pub mod config; pub mod entity; pub mod server; pub mod util; diff --git a/pumpkin/src/server.rs b/pumpkin/src/server.rs index c60fa6ee2..bc73ee60a 100644 --- a/pumpkin/src/server.rs +++ b/pumpkin/src/server.rs @@ -26,7 +26,7 @@ use serde::{Deserialize, Serialize}; use crate::{ client::Client, - configuration::{AdvancedConfiguration, BasicConfiguration}, + config::{AdvancedConfiguration, BasicConfiguration}, entity::player::{GameMode, Player}, }; From 4fd8d47d7e532a153961262fc4274f7ba8bddf6a Mon Sep 17 00:00:00 2001 From: Snowiiii Date: Wed, 7 Aug 2024 14:18:53 +0200 Subject: [PATCH 4/7] Protocol: Add support for lists in Serializer --- pumpkin-protocol/src/bytebuf/mod.rs | 11 +++++++++++ pumpkin-protocol/src/bytebuf/serializer.rs | 17 ++++++++++++----- 2 files changed, 23 insertions(+), 5 deletions(-) diff --git a/pumpkin-protocol/src/bytebuf/mod.rs b/pumpkin-protocol/src/bytebuf/mod.rs index 86d49df2e..d7046496b 100644 --- a/pumpkin-protocol/src/bytebuf/mod.rs +++ b/pumpkin-protocol/src/bytebuf/mod.rs @@ -297,6 +297,17 @@ impl ByteBuffer { self.buffer.put_slice(src) } + pub fn put(&mut self, mut src: T) + where + Self: Sized, + { + self.buffer.put(src) + } + + pub fn reserve(&mut self, additional: usize) { + self.buffer.reserve(additional) + } + pub fn get_slice(&mut self) -> BytesMut { self.buffer.split() } diff --git a/pumpkin-protocol/src/bytebuf/serializer.rs b/pumpkin-protocol/src/bytebuf/serializer.rs index 025bad91b..8685fb7ba 100644 --- a/pumpkin-protocol/src/bytebuf/serializer.rs +++ b/pumpkin-protocol/src/bytebuf/serializer.rs @@ -1,8 +1,11 @@ use std::fmt::Display; +use bytes::Buf; use serde::{ser, Serialize}; use thiserror::Error; +use crate::VarInt; + use super::ByteBuffer; pub struct Serializer { @@ -123,8 +126,12 @@ impl<'a> ser::Serializer for &'a mut Serializer { fn serialize_none(self) -> Result { unimplemented!() } - fn serialize_seq(self, _len: Option) -> Result { - unimplemented!() + fn serialize_seq(self, len: Option) -> Result { + if let Some(len) = len { + self.output.reserve(len); + self.output.put_var_int(&VarInt(len as i32)); + } + Ok(self) } fn serialize_some(self, _value: &T) -> Result where @@ -213,16 +220,16 @@ impl<'a> ser::SerializeSeq for &'a mut Serializer { type Error = SerializerError; // Serialize a single element of the sequence. - fn serialize_element(&mut self, _value: &T) -> Result<(), Self::Error> + fn serialize_element(&mut self, value: &T) -> Result<(), Self::Error> where T: ?Sized + Serialize, { - todo!() + value.serialize(&mut **self) } // Close the sequence. fn end(self) -> Result<(), Self::Error> { - todo!() + Ok(()) } } From a9bd4e23266fde86c4cd07242c103e9d996113b0 Mon Sep 17 00:00:00 2001 From: Snowiiii Date: Wed, 7 Aug 2024 14:42:52 +0200 Subject: [PATCH 5/7] Add Clientbound Entity Metadata --- .../src/client/play/c_entity_metadata.rs | 33 +++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 pumpkin-protocol/src/client/play/c_entity_metadata.rs diff --git a/pumpkin-protocol/src/client/play/c_entity_metadata.rs b/pumpkin-protocol/src/client/play/c_entity_metadata.rs new file mode 100644 index 000000000..506adcb32 --- /dev/null +++ b/pumpkin-protocol/src/client/play/c_entity_metadata.rs @@ -0,0 +1,33 @@ +use pumpkin_macros::packet; +use serde::Serialize; + +use crate::VarInt; + +#[derive(Serialize, Clone)] +#[packet(0x58)] +pub struct CSetEntityMetadata { + entity_id: VarInt, + metadata: Vec, +} + +impl CSetEntityMetadata { + pub fn new(entity_id: VarInt, metadata: Vec) -> Self { + Self { + entity_id, + metadata, + } + } +} + +#[derive(Serialize, Clone)] +pub struct Metadata { + index: u8, + typ: VarInt, + value: u8, +} + +impl Metadata { + pub fn new(index: u8, typ: VarInt, value: u8) -> Self { + Self { index, typ, value } + } +} From bb6e3d94789b49eef918ecf5e76ba6256f58c409 Mon Sep 17 00:00:00 2001 From: Snowiiii Date: Wed, 7 Aug 2024 15:08:16 +0200 Subject: [PATCH 6/7] Transition more Clientbound packets to new system --- pumpkin-protocol/src/bytebuf/mod.rs | 2 +- pumpkin-protocol/src/bytebuf/serializer.rs | 11 +++-- .../src/client/login/c_encryption_request.rs | 10 ++-- pumpkin-protocol/src/client/play/c_login.rs | 42 ++-------------- .../src/client/play/c_player_abilities.rs | 11 +---- pumpkin-protocol/src/client/play/mod.rs | 2 + .../src/client/status/c_ping_response.rs | 3 +- .../src/client/status/c_status_response.rs | 3 +- pumpkin-protocol/src/lib.rs | 2 +- pumpkin/src/server.rs | 48 +++++++++++-------- 10 files changed, 57 insertions(+), 77 deletions(-) diff --git a/pumpkin-protocol/src/bytebuf/mod.rs b/pumpkin-protocol/src/bytebuf/mod.rs index d7046496b..8f4a5fdb0 100644 --- a/pumpkin-protocol/src/bytebuf/mod.rs +++ b/pumpkin-protocol/src/bytebuf/mod.rs @@ -297,7 +297,7 @@ impl ByteBuffer { self.buffer.put_slice(src) } - pub fn put(&mut self, mut src: T) + pub fn put(&mut self, src: T) where Self: Sized, { diff --git a/pumpkin-protocol/src/bytebuf/serializer.rs b/pumpkin-protocol/src/bytebuf/serializer.rs index 8685fb7ba..f3c81f5d7 100644 --- a/pumpkin-protocol/src/bytebuf/serializer.rs +++ b/pumpkin-protocol/src/bytebuf/serializer.rs @@ -1,6 +1,5 @@ use std::fmt::Display; -use bytes::Buf; use serde::{ser, Serialize}; use thiserror::Error; @@ -124,20 +123,22 @@ impl<'a> ser::Serializer for &'a mut Serializer { unimplemented!() } fn serialize_none(self) -> Result { - unimplemented!() + self.output.put_bool(false); + Ok(()) } fn serialize_seq(self, len: Option) -> Result { if let Some(len) = len { - self.output.reserve(len); self.output.put_var_int(&VarInt(len as i32)); } Ok(self) } - fn serialize_some(self, _value: &T) -> Result + fn serialize_some(self, value: &T) -> Result where T: ?Sized + Serialize, { - unimplemented!() + self.output.put_bool(true); + dbg!("aa"); + value.serialize(self) } fn serialize_str(self, v: &str) -> Result { self.output.put_string(v); diff --git a/pumpkin-protocol/src/client/login/c_encryption_request.rs b/pumpkin-protocol/src/client/login/c_encryption_request.rs index 7636ca377..312b5cd28 100644 --- a/pumpkin-protocol/src/client/login/c_encryption_request.rs +++ b/pumpkin-protocol/src/client/login/c_encryption_request.rs @@ -1,11 +1,13 @@ use pumpkin_macros::packet; -use crate::{bytebuf::ByteBuffer, ClientPacket, VarIntType}; +use crate::{bytebuf::ByteBuffer, ClientPacket, VarInt}; #[packet(0x01)] pub struct CEncryptionRequest<'a> { server_id: &'a str, // 20 + public_key_length: VarInt, public_key: &'a [u8], + verify_token_length: VarInt, verify_token: &'a [u8], should_authenticate: bool, } @@ -19,7 +21,9 @@ impl<'a> CEncryptionRequest<'a> { ) -> Self { Self { server_id, + public_key_length: public_key.len().into(), public_key, + verify_token_length: verify_token.len().into(), verify_token, should_authenticate, } @@ -29,9 +33,9 @@ impl<'a> CEncryptionRequest<'a> { impl<'a> ClientPacket for CEncryptionRequest<'a> { fn write(&self, bytebuf: &mut ByteBuffer) { bytebuf.put_string(self.server_id); - bytebuf.put_var_int(&(self.public_key.len() as VarIntType).into()); + bytebuf.put_var_int(&self.public_key_length); bytebuf.put_slice(self.public_key); - bytebuf.put_var_int(&(self.verify_token.len() as VarIntType).into()); + bytebuf.put_var_int(&self.verify_token_length); bytebuf.put_slice(self.verify_token); bytebuf.put_bool(self.should_authenticate); } diff --git a/pumpkin-protocol/src/client/play/c_login.rs b/pumpkin-protocol/src/client/play/c_login.rs index 0cb8fe79e..ad18d6e74 100644 --- a/pumpkin-protocol/src/client/play/c_login.rs +++ b/pumpkin-protocol/src/client/play/c_login.rs @@ -1,7 +1,9 @@ use pumpkin_macros::packet; +use serde::Serialize; -use crate::{bytebuf::ByteBuffer, ClientPacket, VarInt}; +use crate::VarInt; +#[derive(Serialize)] #[packet(0x2B)] pub struct CLogin { entity_id: i32, @@ -20,9 +22,7 @@ pub struct CLogin { previous_gamemode: i8, debug: bool, is_flat: bool, - has_death_loc: bool, - death_dimension_name: Option, - death_loc: Option, // POSITION NOT STRING + death_dimension_name: Option<(String, i64)>, // POSITION NOT STRING portal_cooldown: VarInt, enforce_secure_chat: bool, } @@ -46,9 +46,7 @@ impl CLogin { previous_gamemode: i8, debug: bool, is_flat: bool, - has_death_loc: bool, - death_dimension_name: Option, - death_loc: Option, // todo add block pos + death_dimension_name: Option<(String, i64)>, portal_cooldown: VarInt, enforce_secure_chat: bool, ) -> Self { @@ -69,39 +67,9 @@ impl CLogin { previous_gamemode, debug, is_flat, - has_death_loc, death_dimension_name, - death_loc, portal_cooldown, enforce_secure_chat, } } } - -impl ClientPacket for CLogin { - fn write(&self, bytebuf: &mut ByteBuffer) { - bytebuf.put_i32(self.entity_id); - bytebuf.put_bool(self.is_hardcore); - bytebuf.put_list(&self.dimension_names, |buf, v| buf.put_string(v)); - bytebuf.put_var_int(&self.max_players); - bytebuf.put_var_int(&self.view_distance); - bytebuf.put_var_int(&self.simulated_distance); - bytebuf.put_bool(self.reduced_debug_info); - bytebuf.put_bool(self.enabled_respawn_screen); - bytebuf.put_bool(self.limited_crafting); - bytebuf.put_var_int(&self.dimension_type); - bytebuf.put_string(&self.dimension_name); - bytebuf.put_i64(self.hashed_seed); - bytebuf.put_u8(self.game_mode); - bytebuf.put_i8(self.previous_gamemode); - bytebuf.put_bool(self.debug); - bytebuf.put_bool(self.is_flat); - bytebuf.put_bool(self.has_death_loc); - if self.has_death_loc { - bytebuf.put_string(self.death_dimension_name.as_ref().unwrap()); - bytebuf.put_i64(self.death_loc.unwrap()); - } - bytebuf.put_var_int(&self.portal_cooldown); - bytebuf.put_bool(self.enforce_secure_chat); - } -} diff --git a/pumpkin-protocol/src/client/play/c_player_abilities.rs b/pumpkin-protocol/src/client/play/c_player_abilities.rs index 65c4cbd08..c7f50cb35 100644 --- a/pumpkin-protocol/src/client/play/c_player_abilities.rs +++ b/pumpkin-protocol/src/client/play/c_player_abilities.rs @@ -1,7 +1,8 @@ use pumpkin_macros::packet; +use serde::Serialize; -use crate::{bytebuf::ByteBuffer, ClientPacket}; +#[derive(Serialize)] #[packet(0x38)] pub struct CPlayerAbilities { flags: i8, @@ -18,11 +19,3 @@ impl CPlayerAbilities { } } } - -impl ClientPacket for CPlayerAbilities { - fn write(&self, bytebuf: &mut ByteBuffer) { - bytebuf.put_i8(self.flags); - bytebuf.put_f32(self.flying_speed); - bytebuf.put_f32(self.field_of_view); - } -} diff --git a/pumpkin-protocol/src/client/play/mod.rs b/pumpkin-protocol/src/client/play/mod.rs index 0399b656a..4d0f2231f 100644 --- a/pumpkin-protocol/src/client/play/mod.rs +++ b/pumpkin-protocol/src/client/play/mod.rs @@ -1,5 +1,6 @@ mod c_change_difficulty; mod c_chunk_data_update_light; +mod c_entity_metadata; mod c_game_event; mod c_head_rot; mod c_login; @@ -18,6 +19,7 @@ mod player_action; pub use c_change_difficulty::*; pub use c_chunk_data_update_light::*; +pub use c_entity_metadata::*; pub use c_game_event::*; pub use c_head_rot::*; pub use c_login::*; diff --git a/pumpkin-protocol/src/client/status/c_ping_response.rs b/pumpkin-protocol/src/client/status/c_ping_response.rs index 333339a9b..a75f8616f 100644 --- a/pumpkin-protocol/src/client/status/c_ping_response.rs +++ b/pumpkin-protocol/src/client/status/c_ping_response.rs @@ -1,6 +1,7 @@ use pumpkin_macros::packet; +use serde::Serialize; -#[derive(serde::Serialize)] +#[derive(Serialize)] #[packet(0x01)] pub struct CPingResponse { payload: i64, // must responde with the same as in `SPingRequest` diff --git a/pumpkin-protocol/src/client/status/c_status_response.rs b/pumpkin-protocol/src/client/status/c_status_response.rs index 5f82c33e1..4589877f1 100644 --- a/pumpkin-protocol/src/client/status/c_status_response.rs +++ b/pumpkin-protocol/src/client/status/c_status_response.rs @@ -1,6 +1,7 @@ use pumpkin_macros::packet; +use serde::Serialize; -#[derive(serde::Serialize)] +#[derive(Serialize)] #[packet(0x00)] pub struct CStatusResponse<'a> { json_response: &'a str, // 32767 diff --git a/pumpkin-protocol/src/lib.rs b/pumpkin-protocol/src/lib.rs index 2cbbe3ce0..733c1c23c 100644 --- a/pumpkin-protocol/src/lib.rs +++ b/pumpkin-protocol/src/lib.rs @@ -215,7 +215,7 @@ pub struct Sample { } // basicly game profile -#[derive(Deserialize, Clone, Debug)] +#[derive(Serialize, Deserialize, Clone, Debug)] pub struct Property { pub name: String, // base 64 diff --git a/pumpkin/src/server.rs b/pumpkin/src/server.rs index bc73ee60a..0fc80f42f 100644 --- a/pumpkin/src/server.rs +++ b/pumpkin/src/server.rs @@ -14,8 +14,7 @@ use pumpkin_protocol::{ client::{ config::CPluginMessage, play::{ - CChunkDataUpdateLight, CGameEvent, CLogin, CPlayerAbilities, CPlayerInfoUpdate, - CSpawnEntity, PlayerAction, + CChunkDataUpdateLight, CGameEvent, CLogin, CPlayerAbilities, CPlayerInfoUpdate, CSpawnEntity, PlayerAction, }, }, BitSet, ClientPacket, Players, Sample, StatusResponse, VarInt, Version, CURRENT_MC_PROTOCOL, @@ -144,8 +143,6 @@ impl Server { self.base_config.default_gamemode.to_i8().unwrap(), false, false, - false, // deth loc - None, None, 0.into(), false, @@ -160,19 +157,7 @@ impl Server { client.teleport(x, y, z, 10.0, 10.0); let gameprofile = client.gameprofile.as_ref().unwrap(); // first send info update to our new player, So he can see his Skin - // TODO: send more actions, (chat. list, ping) - client.send_packet(CPlayerInfoUpdate::new( - 0x01, - &[pumpkin_protocol::client::play::Player { - uuid: gameprofile.id, - actions: vec![PlayerAction::AddPlayer { - name: gameprofile.name.clone(), - properties: gameprofile.properties.clone(), - }], - }], - )); - let gameprofile = client.gameprofile.as_ref().unwrap(); - // send his info to everyone else + // also send his info to everyone else self.broadcast_packet( client, CPlayerInfoUpdate::new( @@ -210,7 +195,7 @@ impl Server { let gameprofile = client.gameprofile.as_ref().unwrap(); // spawn player for every client - self.broadcast_packet( + self.broadcast_packet_expect( client, CSpawnEntity::new( entity_id.into(), @@ -252,12 +237,37 @@ impl Server { )) } } + // entity meta data + /* if let Some(config) = &client.config { + self.broadcast_packet( + client, + CSetEntityMetadata::new(entity_id.into(), vec![Metadata::new(18, VarInt(0), 0)]), + ) + } + */ // Server::spawn_test_chunk(client); } /// Sends a Packet to all Players - pub fn broadcast_packet

(&mut self, from: &Client, packet: P) + pub fn broadcast_packet

(&mut self, from: &mut Client, packet: P) + where + P: ClientPacket, + P: Clone, + { + // we can't borrow twice at same time + from.send_packet(packet.clone()); + for (_, client) in self.current_clients.iter().filter(|c| c.0 != &from.token) { + // Check if client is a player + let mut client = client.borrow_mut(); + if client.is_player() { + // we need to clone, Because we send a new packet to every client + client.send_packet(packet.clone()); + } + } + } + + pub fn broadcast_packet_expect

(&mut self, from: &mut Client, packet: P) where P: ClientPacket, P: Clone, From 0cec9bf2161a7121bbe407114eedee76582ce90a Mon Sep 17 00:00:00 2001 From: Snowiiii Date: Wed, 7 Aug 2024 15:11:35 +0200 Subject: [PATCH 7/7] Add Player swing animation --- README.md | 2 +- .../src/client/play/c_entity_animation.rs | 31 ++++ .../src/client/play/c_player_abilities.rs | 1 - pumpkin-protocol/src/client/play/mod.rs | 2 + pumpkin-protocol/src/server/play/mod.rs | 14 ++ pumpkin/src/client/mod.rs | 3 +- pumpkin/src/client/player_packet.rs | 19 ++- pumpkin/src/config/configuration.rs | 153 ------------------ pumpkin/src/server.rs | 3 +- 9 files changed, 69 insertions(+), 159 deletions(-) create mode 100644 pumpkin-protocol/src/client/play/c_entity_animation.rs delete mode 100644 pumpkin/src/config/configuration.rs diff --git a/README.md b/README.md index e919bfafd..b0608e17e 100644 --- a/README.md +++ b/README.md @@ -53,7 +53,7 @@ Pumpkin is currently under heavy development. - [x] Player Client brand - [x] Player Teleport - [x] Player Movement - - [ ] Player Animation + - [x] Player Animation - [ ] Player Inventory - [ ] Player Attack - Server diff --git a/pumpkin-protocol/src/client/play/c_entity_animation.rs b/pumpkin-protocol/src/client/play/c_entity_animation.rs new file mode 100644 index 000000000..a4c7f36da --- /dev/null +++ b/pumpkin-protocol/src/client/play/c_entity_animation.rs @@ -0,0 +1,31 @@ +use num_derive::ToPrimitive; +use pumpkin_macros::packet; +use serde::Serialize; + +use crate::VarInt; + +#[derive(Serialize, Clone)] +#[packet(0x03)] +pub struct CEntityAnimation { + entity_id: VarInt, + /// See `Animation` + animation: u8, +} + +impl CEntityAnimation { + pub fn new(entity_id: VarInt, animation: u8) -> Self { + Self { + entity_id, + animation, + } + } +} + +#[derive(ToPrimitive)] +pub enum Animation { + SwingMainArm, + LeaveBed, + SwingOffhand, + CriticalEffect, + MagicCriticaleffect, +} diff --git a/pumpkin-protocol/src/client/play/c_player_abilities.rs b/pumpkin-protocol/src/client/play/c_player_abilities.rs index c7f50cb35..a74139fac 100644 --- a/pumpkin-protocol/src/client/play/c_player_abilities.rs +++ b/pumpkin-protocol/src/client/play/c_player_abilities.rs @@ -1,7 +1,6 @@ use pumpkin_macros::packet; use serde::Serialize; - #[derive(Serialize)] #[packet(0x38)] pub struct CPlayerAbilities { diff --git a/pumpkin-protocol/src/client/play/mod.rs b/pumpkin-protocol/src/client/play/mod.rs index 4d0f2231f..e7aa90e77 100644 --- a/pumpkin-protocol/src/client/play/mod.rs +++ b/pumpkin-protocol/src/client/play/mod.rs @@ -1,5 +1,6 @@ mod c_change_difficulty; mod c_chunk_data_update_light; +mod c_entity_animation; mod c_entity_metadata; mod c_game_event; mod c_head_rot; @@ -19,6 +20,7 @@ mod player_action; pub use c_change_difficulty::*; pub use c_chunk_data_update_light::*; +pub use c_entity_animation::*; pub use c_entity_metadata::*; pub use c_game_event::*; pub use c_head_rot::*; diff --git a/pumpkin-protocol/src/server/play/mod.rs b/pumpkin-protocol/src/server/play/mod.rs index 62782c7cd..76193a3c5 100644 --- a/pumpkin-protocol/src/server/play/mod.rs +++ b/pumpkin-protocol/src/server/play/mod.rs @@ -51,6 +51,20 @@ impl ServerPacket for SPlayerPosition { } } +pub struct SSwingArm { + pub hand: VarInt, +} + +impl ServerPacket for SSwingArm { + const PACKET_ID: VarInt = VarInt(0x36); + + fn read(bytebuf: &mut crate::bytebuf::ByteBuffer) -> Self { + Self { + hand: bytebuf.get_var_int(), + } + } +} + pub struct SPlayerCommand { pub entitiy_id: VarInt, pub action: Action, diff --git a/pumpkin/src/client/mod.rs b/pumpkin/src/client/mod.rs index 99fc0c2c4..90da622b2 100644 --- a/pumpkin/src/client/mod.rs +++ b/pumpkin/src/client/mod.rs @@ -27,7 +27,7 @@ use pumpkin_protocol::{ login::{SEncryptionResponse, SLoginAcknowledged, SLoginPluginResponse, SLoginStart}, play::{ SChatCommand, SConfirmTeleport, SPlayerCommand, SPlayerPosition, - SPlayerPositionRotation, SPlayerRotation, + SPlayerPositionRotation, SPlayerRotation, SSwingArm, }, status::{SPingRequest, SStatusRequest}, }, @@ -256,6 +256,7 @@ impl Client { SPlayerCommand::PACKET_ID => { self.handle_player_command(server, SPlayerCommand::read(bytebuf)) } + SSwingArm::PACKET_ID => self.handle_swing_arm(server, SSwingArm::read(bytebuf)), _ => log::error!("Failed to handle player packet id {}", packet.id.0), } } diff --git a/pumpkin/src/client/player_packet.rs b/pumpkin/src/client/player_packet.rs index 7801580f5..1850fd345 100644 --- a/pumpkin/src/client/player_packet.rs +++ b/pumpkin/src/client/player_packet.rs @@ -1,13 +1,18 @@ +use num_traits::FromPrimitive; use pumpkin_protocol::{ - client::play::{CHeadRot, CUpdateEntityPos, CUpdateEntityPosRot, CUpdateEntityRot}, + client::play::{ + Animation, CEntityAnimation, CHeadRot, CUpdateEntityPos, CUpdateEntityPosRot, + CUpdateEntityRot, + }, server::play::{ SChatCommand, SConfirmTeleport, SPlayerCommand, SPlayerPosition, SPlayerPositionRotation, - SPlayerRotation, + SPlayerRotation, SSwingArm, }, }; use crate::{ commands::{handle_command, CommandSender}, + entity::player::Hand, server::Server, }; @@ -156,4 +161,14 @@ impl Client { pumpkin_protocol::server::play::Action::StartFlyingElytra => todo!(), } } + + pub fn handle_swing_arm(&mut self, server: &mut Server, swing_arm: SSwingArm) { + let animation = match Hand::from_i32(swing_arm.hand.0).unwrap() { + Hand::Main => Animation::SwingMainArm, + Hand::Off => Animation::SwingOffhand, + }; + let player = self.player.as_mut().unwrap(); + let id = player.entity_id(); + server.broadcast_packet_expect(self, CEntityAnimation::new(id.into(), animation as u8)) + } } diff --git a/pumpkin/src/config/configuration.rs b/pumpkin/src/config/configuration.rs deleted file mode 100644 index 9f28a0f72..000000000 --- a/pumpkin/src/config/configuration.rs +++ /dev/null @@ -1,153 +0,0 @@ -use std::path::Path; - -use serde::{Deserialize, Serialize}; - -use crate::{entity::player::GameMode, server::Difficulty}; - -use super::auth_config::Authentication; - -/// Current Config version of the Base Config -const CURRENT_BASE_VERSION: &str = "1.0.0"; - -#[derive(Deserialize, Serialize)] -/// The idea is that Pumpkin should very customizable, You can Enable or Disable Features depning on your needs. -/// This also allows you get some Performance or Resource boosts. -/// Important: The Configuration should match Vanilla by default -pub struct AdvancedConfiguration { - pub commands: Commands, - pub authentication: Authentication, -} - -#[derive(Deserialize, Serialize)] -pub struct Commands { - /// Are commands from the Console accepted ? - pub use_console: bool, - // todo commands... -} - -impl Default for Commands { - fn default() -> Self { - Self { use_console: true } - } -} - -/// Important: The Configuration should match Vanilla by default -impl Default for AdvancedConfiguration { - fn default() -> Self { - Self { - authentication: Authentication::default(), - commands: Commands::default(), - } - } -} - -#[derive(Serialize, Deserialize)] -pub struct BasicConfiguration { - /// A version identifier for the configuration format. - pub config_version: String, - /// The address to bind the server to. - pub server_address: String, - /// The port to listen on. - pub server_port: u16, - /// The seed for world generation. - pub seed: String, - /// The maximum number of players allowed on the server. - pub max_players: u32, - /// The maximum view distance for players. - pub view_distance: u8, - /// The maximum simulated view distance. - pub simulation_distance: u8, - /// The path to the resource pack. - pub resource_pack: String, - /// The SHA1 hash of the resource pack. - pub resource_pack_sha1: String, - /// The default game difficulty. - pub default_difficulty: Difficulty, - /// Whether the Nether dimension is enabled. - pub allow_nether: bool, - /// Whether the server is in hardcore mode. - pub hardcore: bool, - /// Whether online mode is enabled. Requires valid Minecraft accounts. - pub online_mode: bool, - /// Whether packet encryption is enabled. Required when online mode is enabled. - pub encryption: bool, - /// The server's description displayed on the status screen. - pub motd: String, - /// The default game mode for players. - pub default_gamemode: GameMode, -} - -impl Default for BasicConfiguration { - fn default() -> Self { - Self { - config_version: CURRENT_BASE_VERSION.to_string(), - server_address: "127.0.0.1".to_string(), - server_port: 25565, - seed: "".to_string(), - max_players: 100000, - view_distance: 10, - simulation_distance: 10, - resource_pack: "".to_string(), - resource_pack_sha1: "".to_string(), - default_difficulty: Difficulty::Normal, - allow_nether: true, - hardcore: false, - online_mode: true, - encryption: true, - motd: "A Blazing fast Pumpkin Server!".to_string(), - default_gamemode: GameMode::Survival, - } - } -} - -impl AdvancedConfiguration { - pub fn load>(path: P) -> AdvancedConfiguration { - if path.as_ref().exists() { - let toml = std::fs::read_to_string(path).expect("Couldn't read configuration"); - toml::from_str(toml.as_str()).expect("Couldn't parse, Proberbly old config") - } else { - let config = AdvancedConfiguration::default(); - let toml = toml::to_string(&config).expect("Couldn't create toml!"); - std::fs::write(path, toml).expect("Couldn't save configuration"); - config - } - } -} - -impl BasicConfiguration { - pub fn load>(path: P) -> BasicConfiguration { - if path.as_ref().exists() { - let toml = std::fs::read_to_string(path).expect("Couldn't read configuration"); - toml::from_str(toml.as_str()).expect("Couldn't parse") - } else { - let config = BasicConfiguration::default(); - let toml = toml::to_string(&config).expect("Couldn't create toml!"); - std::fs::write(path, toml).expect("Couldn't save configuration"); - config.validate(); - config - } - } - - pub fn validate(&self) { - assert_eq!( - self.config_version, CURRENT_BASE_VERSION, - "Config version does not match used Config version. Please update your config" - ); - assert!(self.view_distance >= 2, "View distance must be atleast 2"); - assert!( - self.view_distance <= 32, - "View distance must be less than 32" - ); - if self.online_mode { - assert!( - self.encryption, - "When Online Mode is enabled, Encryption must be enabled" - ) - } - assert_eq!( - !self.resource_pack.is_empty(), - !self.resource_pack_sha1.is_empty(), - "Resource Pack path or Sha1 hash is missing" - ); - } -} diff --git a/pumpkin/src/server.rs b/pumpkin/src/server.rs index 0fc80f42f..dd7b19235 100644 --- a/pumpkin/src/server.rs +++ b/pumpkin/src/server.rs @@ -14,7 +14,8 @@ use pumpkin_protocol::{ client::{ config::CPluginMessage, play::{ - CChunkDataUpdateLight, CGameEvent, CLogin, CPlayerAbilities, CPlayerInfoUpdate, CSpawnEntity, PlayerAction, + CChunkDataUpdateLight, CGameEvent, CLogin, CPlayerAbilities, CPlayerInfoUpdate, + CSpawnEntity, PlayerAction, }, }, BitSet, ClientPacket, Players, Sample, StatusResponse, VarInt, Version, CURRENT_MC_PROTOCOL,