diff --git a/Cargo.lock b/Cargo.lock index 990b46cf0..b204a8850 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1906,6 +1906,7 @@ dependencies = [ "num-bigint", "num-derive", "num-traits", + "pumpkin-config", "pumpkin-core", "pumpkin-entity", "pumpkin-inventory", @@ -1929,6 +1930,16 @@ dependencies = [ "uuid", ] +[[package]] +name = "pumpkin-config" +version = "0.1.0" +dependencies = [ + "log", + "pumpkin-core", + "serde", + "toml 0.8.19", +] + [[package]] name = "pumpkin-core" version = "0.1.0" @@ -1936,6 +1947,8 @@ dependencies = [ "colored", "fastnbt", "md5", + "num-derive", + "num-traits", "serde", "uuid", ] diff --git a/Cargo.toml b/Cargo.toml index 3fbea597f..abca231fe 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [workspace] resolver = "2" -members = [ "pumpkin-core", "pumpkin-entity", "pumpkin-inventory", "pumpkin-macros/", "pumpkin-plugin", "pumpkin-protocol/", "pumpkin-registry/", "pumpkin-world", "pumpkin/"] +members = [ "pumpkin-config", "pumpkin-core", "pumpkin-entity", "pumpkin-inventory", "pumpkin-macros/", "pumpkin-plugin", "pumpkin-protocol/", "pumpkin-registry/", "pumpkin-world", "pumpkin/"] [workspace.package] version = "0.1.0" diff --git a/pumpkin-config/Cargo.toml b/pumpkin-config/Cargo.toml new file mode 100644 index 000000000..782294c9c --- /dev/null +++ b/pumpkin-config/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "pumpkin-config" +version.workspace = true +edition.workspace = true + +[dependencies] +pumpkin-core = { path = "../pumpkin-core" } + +serde = "1.0" +toml = "0.8" + +log.workspace = true diff --git a/pumpkin/src/config/auth_config.rs b/pumpkin-config/src/auth.rs similarity index 97% rename from pumpkin/src/config/auth_config.rs rename to pumpkin-config/src/auth.rs index 09c4188ff..c6606cd86 100644 --- a/pumpkin/src/config/auth_config.rs +++ b/pumpkin-config/src/auth.rs @@ -1,7 +1,6 @@ +use pumpkin_core::ProfileAction; use serde::{Deserialize, Serialize}; -use crate::client::authentication::ProfileAction; - #[derive(Deserialize, Serialize)] pub struct AuthenticationConfig { /// Whether to use Mojang authentication. diff --git a/pumpkin-config/src/commands.rs b/pumpkin-config/src/commands.rs new file mode 100644 index 000000000..94ee4d8c2 --- /dev/null +++ b/pumpkin-config/src/commands.rs @@ -0,0 +1,14 @@ +use serde::{Deserialize, Serialize}; + +#[derive(Deserialize, Serialize)] +pub struct CommandsConfig { + /// Are commands from the Console accepted ? + pub use_console: bool, + // TODO: commands... +} + +impl Default for CommandsConfig { + fn default() -> Self { + Self { use_console: true } + } +} diff --git a/pumpkin-config/src/compression.rs b/pumpkin-config/src/compression.rs new file mode 100644 index 000000000..544c8d31d --- /dev/null +++ b/pumpkin-config/src/compression.rs @@ -0,0 +1,24 @@ +use serde::{Deserialize, Serialize}; + +#[derive(Deserialize, Serialize)] +// Packet compression +pub struct CompressionConfig { + /// Is compression enabled ? + pub enabled: bool, + /// The compression threshold used when compression is enabled + pub compression_threshold: u32, + /// A value between 0..9 + /// 1 = Optimize for the best speed of encoding. + /// 9 = Optimize for the size of data being encoded. + pub compression_level: u32, +} + +impl Default for CompressionConfig { + fn default() -> Self { + Self { + enabled: true, + compression_threshold: 256, + compression_level: 4, + } + } +} diff --git a/pumpkin-config/src/lib.rs b/pumpkin-config/src/lib.rs new file mode 100644 index 000000000..f24ca2810 --- /dev/null +++ b/pumpkin-config/src/lib.rs @@ -0,0 +1,176 @@ +use log::warn; +use pumpkin_core::{Difficulty, GameMode}; +use serde::{de::DeserializeOwned, Deserialize, Serialize}; + +use std::{ + fs, + net::{Ipv4Addr, SocketAddr}, + path::Path, + sync::LazyLock, +}; + +pub mod auth; +pub mod proxy; +pub mod resource_pack; + +pub use auth::AuthenticationConfig; +pub use commands::CommandsConfig; +pub use compression::CompressionConfig; +pub use pvp::PVPConfig; +pub use rcon::RCONConfig; + +mod commands; +mod compression; +mod pvp; +mod rcon; + +use proxy::ProxyConfig; +use resource_pack::ResourcePackConfig; + +/// Current Config version of the Base Config +const CURRENT_BASE_VERSION: &str = "1.0.0"; + +pub static ADVANCED_CONFIG: LazyLock = + LazyLock::new(AdvancedConfiguration::load); + +pub static BASIC_CONFIG: LazyLock = LazyLock::new(BasicConfiguration::load); + +/// The idea is that Pumpkin should very customizable. +/// You can Enable or Disable Features depending on your needs. +/// +/// This also allows you get some Performance or Resource boosts. +/// Important: The Configuration should match Vanilla by default +#[derive(Deserialize, Serialize, Default)] +pub struct AdvancedConfiguration { + pub proxy: ProxyConfig, + pub authentication: AuthenticationConfig, + pub packet_compression: CompressionConfig, + pub resource_pack: ResourcePackConfig, + pub commands: CommandsConfig, + pub rcon: RCONConfig, + pub pvp: PVPConfig, +} + +#[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: SocketAddr, + /// 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 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: SocketAddr::new(Ipv4Addr::new(0, 0, 0, 0).into(), 25565), + seed: "".to_string(), + max_players: 100000, + view_distance: 10, + simulation_distance: 10, + 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, + } + } +} + +trait LoadConfiguration { + fn load() -> Self + where + Self: Sized + Default + Serialize + DeserializeOwned, + { + let path = Self::get_path(); + + let config = if path.exists() { + let file_content = fs::read_to_string(path) + .unwrap_or_else(|_| panic!("Couldn't read configuration file at {:?}", path)); + + toml::from_str(&file_content).unwrap_or_else(|err| { + panic!( + "Couldn't parse config at {:?}. Reason: {}", + path, + err.message() + ) + }) + } else { + let content = Self::default(); + + if let Err(err) = fs::write(path, toml::to_string(&content).unwrap()) { + warn!( + "Couldn't write default config to {:?}. Reason: {}", + path, err + ); + } + + content + }; + + config.validate(); + config + } + + fn get_path() -> &'static Path; + + fn validate(&self); +} + +impl LoadConfiguration for AdvancedConfiguration { + fn get_path() -> &'static Path { + Path::new("features.toml") + } + + fn validate(&self) { + self.resource_pack.validate() + } +} + +impl LoadConfiguration for BasicConfiguration { + fn get_path() -> &'static Path { + Path::new("configuration.toml") + } + + 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 at least 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" + ) + } + } +} diff --git a/pumpkin/src/config/proxy.rs b/pumpkin-config/src/proxy.rs similarity index 100% rename from pumpkin/src/config/proxy.rs rename to pumpkin-config/src/proxy.rs diff --git a/pumpkin-config/src/pvp.rs b/pumpkin-config/src/pvp.rs new file mode 100644 index 000000000..cfbddc6c4 --- /dev/null +++ b/pumpkin-config/src/pvp.rs @@ -0,0 +1,27 @@ +use serde::{Deserialize, Serialize}; + +#[derive(Deserialize, Serialize)] +pub struct PVPConfig { + /// Is PVP enabled ? + pub enabled: bool, + /// Do we want to have the Red hurt animation & fov bobbing + pub hurt_animation: bool, + /// Should players in creative be protected against PVP + pub protect_creative: bool, + /// Has PVP Knockback? + pub knockback: bool, + /// Should player swing when attacking? + pub swing: bool, +} + +impl Default for PVPConfig { + fn default() -> Self { + Self { + enabled: true, + hurt_animation: true, + protect_creative: true, + knockback: true, + swing: true, + } + } +} diff --git a/pumpkin-config/src/rcon.rs b/pumpkin-config/src/rcon.rs new file mode 100644 index 000000000..af5aa6ac0 --- /dev/null +++ b/pumpkin-config/src/rcon.rs @@ -0,0 +1,20 @@ +use std::net::{Ipv4Addr, SocketAddr}; + +use serde::{Deserialize, Serialize}; + +#[derive(Deserialize, Serialize, Clone)] +pub struct RCONConfig { + pub enabled: bool, + pub address: SocketAddr, + pub password: String, +} + +impl Default for RCONConfig { + fn default() -> Self { + Self { + enabled: false, + address: SocketAddr::new(Ipv4Addr::new(0, 0, 0, 0).into(), 25575), + password: "".to_string(), + } + } +} diff --git a/pumpkin/src/config/resource_pack.rs b/pumpkin-config/src/resource_pack.rs similarity index 100% rename from pumpkin/src/config/resource_pack.rs rename to pumpkin-config/src/resource_pack.rs diff --git a/pumpkin-core/Cargo.toml b/pumpkin-core/Cargo.toml index 6bd39248b..8d7ae3900 100644 --- a/pumpkin-core/Cargo.toml +++ b/pumpkin-core/Cargo.toml @@ -9,3 +9,6 @@ fastnbt = { git = "https://github.com/owengage/fastnbt.git" } uuid.workspace = true colored = "2" md5 = "0.7.0" + +num-traits = "0.2.19" +num-derive = { version = "0.4.2" } diff --git a/pumpkin-core/src/gamemode.rs b/pumpkin-core/src/gamemode.rs new file mode 100644 index 000000000..6e996a6f9 --- /dev/null +++ b/pumpkin-core/src/gamemode.rs @@ -0,0 +1,30 @@ +use std::str::FromStr; + +use num_derive::{FromPrimitive, ToPrimitive}; +use serde::{Deserialize, Serialize}; + +#[derive(Debug, PartialEq, Eq)] +pub struct ParseGameModeError; + +#[derive(Clone, Copy, Debug, PartialEq, Serialize, Deserialize, FromPrimitive, ToPrimitive)] +pub enum GameMode { + Undefined = -1, + Survival, + Creative, + Adventure, + Spectator, +} + +impl FromStr for GameMode { + type Err = ParseGameModeError; + + fn from_str(s: &str) -> Result { + match s { + "survival" => Ok(Self::Survival), + "creative" => Ok(Self::Creative), + "adventure" => Ok(Self::Adventure), + "spectator" => Ok(Self::Spectator), + _ => Err(ParseGameModeError), + } + } +} diff --git a/pumpkin-core/src/lib.rs b/pumpkin-core/src/lib.rs index 4cfe1770b..9539acae3 100644 --- a/pumpkin-core/src/lib.rs +++ b/pumpkin-core/src/lib.rs @@ -1,2 +1,22 @@ +pub mod gamemode; pub mod random; pub mod text; + +pub use gamemode::GameMode; + +use serde::{Deserialize, Serialize}; + +#[derive(PartialEq, Serialize, Deserialize)] +pub enum Difficulty { + Peaceful, + Easy, + Normal, + Hard, +} + +#[derive(Deserialize, Serialize, Clone, Debug, PartialEq, Eq)] +#[serde(rename_all = "SCREAMING_SNAKE_CASE")] +pub enum ProfileAction { + ForcedNameChange, + UsingBannedSkin, +} diff --git a/pumpkin/Cargo.toml b/pumpkin/Cargo.toml index 0494fc462..804506fd3 100644 --- a/pumpkin/Cargo.toml +++ b/pumpkin/Cargo.toml @@ -10,6 +10,7 @@ default = [] [dependencies] # pumpkin pumpkin-core = { path = "../pumpkin-core"} +pumpkin-config = { path = "../pumpkin-config" } pumpkin-plugin = { path = "../pumpkin-plugin"} pumpkin-inventory = { path = "../pumpkin-inventory"} pumpkin-world = { path = "../pumpkin-world"} diff --git a/pumpkin/src/client/authentication.rs b/pumpkin/src/client/authentication.rs index d841756a3..692cf266c 100644 --- a/pumpkin/src/client/authentication.rs +++ b/pumpkin/src/client/authentication.rs @@ -2,13 +2,15 @@ use std::{collections::HashMap, net::IpAddr}; use base64::{engine::general_purpose, Engine}; use num_bigint::BigInt; +use pumpkin_config::{auth::TextureConfig, ADVANCED_CONFIG}; +use pumpkin_core::ProfileAction; use pumpkin_protocol::Property; use reqwest::{StatusCode, Url}; -use serde::{Deserialize, Serialize}; +use serde::Deserialize; use thiserror::Error; use uuid::Uuid; -use crate::{config::auth_config::TextureConfig, server::Server}; +use crate::server::Server; #[derive(Deserialize, Clone, Debug)] #[allow(non_snake_case)] @@ -29,14 +31,6 @@ pub struct Texture { 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, @@ -52,13 +46,9 @@ pub async fn authenticate( ip: &IpAddr, server: &mut Server, ) -> Result { - assert!(server.advanced_config.authentication.enabled); + assert!(ADVANCED_CONFIG.authentication.enabled); assert!(server.auth_client.is_some()); - let address = if server - .advanced_config - .authentication - .prevent_proxy_connections - { + let address = if 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}") diff --git a/pumpkin/src/client/client_packet.rs b/pumpkin/src/client/client_packet.rs index 7ac629f5d..1c70b0e7c 100644 --- a/pumpkin/src/client/client_packet.rs +++ b/pumpkin/src/client/client_packet.rs @@ -1,4 +1,5 @@ use num_traits::FromPrimitive; +use pumpkin_config::{ADVANCED_CONFIG, BASIC_CONFIG}; use pumpkin_core::text::TextComponent; use pumpkin_protocol::{ client::{ @@ -81,7 +82,7 @@ impl Client { properties: vec![], profile_actions: None, }); - let proxy = &server.advanced_config.proxy; + let proxy = &ADVANCED_CONFIG.proxy; if proxy.enabled { if proxy.velocity.enabled { velocity_login(self) @@ -96,7 +97,7 @@ impl Client { "", public_key_der, &verify_token, - server.base_config.online_mode, // TODO + BASIC_CONFIG.online_mode, // TODO ); self.send_packet(&packet); } @@ -114,7 +115,7 @@ impl Client { self.enable_encryption(&shared_secret) .unwrap_or_else(|e| self.kick(&e.to_string())); - if server.base_config.online_mode { + if BASIC_CONFIG.online_mode { let hash = Sha1::new() .chain_update(&shared_secret) .chain_update(&server.public_key_der) @@ -132,8 +133,7 @@ impl Client { Ok(p) => { // Check if player should join if let Some(p) = &p.profile_actions { - if !server - .advanced_config + if !ADVANCED_CONFIG .authentication .player_profile .allow_banned_players @@ -142,8 +142,7 @@ impl Client { self.kick("Your account can't join"); } } else { - for allowed in server - .advanced_config + for allowed in ADVANCED_CONFIG .authentication .player_profile .allowed_actions @@ -162,16 +161,13 @@ impl Client { } for ele in self.gameprofile.as_ref().unwrap().properties.clone() { // todo, use this - unpack_textures(ele, &server.advanced_config.authentication.textures); + unpack_textures(ele, &ADVANCED_CONFIG.authentication.textures); } // enable compression - if server.advanced_config.packet_compression.enabled { - let threshold = server - .advanced_config - .packet_compression - .compression_threshold; - let level = server.advanced_config.packet_compression.compression_level; + if ADVANCED_CONFIG.packet_compression.enabled { + let threshold = ADVANCED_CONFIG.packet_compression.compression_threshold; + let level = ADVANCED_CONFIG.packet_compression.compression_level; self.send_packet(&CSetCompression::new(threshold.into())); self.set_compression(Some((threshold, level))); } @@ -199,7 +195,7 @@ impl Client { self.connection_state = ConnectionState::Config; server.send_brand(self); - let resource_config = &server.advanced_config.resource_pack; + let resource_config = &ADVANCED_CONFIG.resource_pack; if resource_config.enabled { let prompt_message = if resource_config.prompt_message.is_empty() { None diff --git a/pumpkin/src/client/player_packet.rs b/pumpkin/src/client/player_packet.rs index be6e08ccc..1602a5d89 100644 --- a/pumpkin/src/client/player_packet.rs +++ b/pumpkin/src/client/player_packet.rs @@ -2,12 +2,13 @@ use std::f32::consts::PI; use crate::{ commands::{handle_command, CommandSender}, - entity::player::{ChatMode, GameMode, Hand, Player}, + entity::player::{ChatMode, Hand, Player}, server::Server, util::math::wrap_degrees, }; use num_traits::FromPrimitive; -use pumpkin_core::text::TextComponent; +use pumpkin_config::ADVANCED_CONFIG; +use pumpkin_core::{text::TextComponent, GameMode}; use pumpkin_entity::EntityId; use pumpkin_inventory::WindowType; use pumpkin_protocol::server::play::{SCloseContainer, SSetPlayerGround, SUseItem}; @@ -313,7 +314,7 @@ impl Player { } } - pub async fn handle_interact(&mut self, server: &mut Server, interact: SInteract) { + pub async fn handle_interact(&mut self, _: &mut Server, interact: SInteract) { let sneaking = interact.sneaking; if self.sneaking != sneaking { self.set_sneaking(sneaking).await; @@ -323,7 +324,7 @@ impl Player { ActionType::Attack => { let entity_id = interact.entity_id; // TODO: do validation and stuff - let config = &server.advanced_config.pvp; + let config = &ADVANCED_CONFIG.pvp; if config.enabled { let world = self.world.clone(); let world = world.lock().await; diff --git a/pumpkin/src/commands/cmd_gamemode.rs b/pumpkin/src/commands/cmd_gamemode.rs index 375172f27..17e47fea4 100644 --- a/pumpkin/src/commands/cmd_gamemode.rs +++ b/pumpkin/src/commands/cmd_gamemode.rs @@ -2,6 +2,7 @@ use std::str::FromStr; use num_traits::FromPrimitive; use pumpkin_core::text::TextComponent; +use pumpkin_core::GameMode; use crate::commands::arg_player::{consume_arg_player, parse_arg_player}; @@ -13,7 +14,6 @@ use crate::commands::tree::{CommandTree, ConsumedArgs, RawArgs}; use crate::commands::tree_builder::{argument, require}; use crate::commands::CommandSender; use crate::commands::CommandSender::Player; -use crate::entity::player::GameMode; const NAMES: [&str; 1] = ["gamemode"]; diff --git a/pumpkin/src/config/mod.rs b/pumpkin/src/config/mod.rs deleted file mode 100644 index cc47d9772..000000000 --- a/pumpkin/src/config/mod.rs +++ /dev/null @@ -1,234 +0,0 @@ -use std::path::Path; - -use auth_config::AuthenticationConfig; -use proxy::ProxyConfig; -use resource_pack::ResourcePackConfig; -use serde::{Deserialize, Serialize}; - -use crate::{entity::player::GameMode, server::Difficulty}; - -pub mod auth_config; -pub mod proxy; -pub mod resource_pack; - -/// 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 depending 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 proxy: ProxyConfig, - pub authentication: AuthenticationConfig, - pub packet_compression: CompressionConfig, - pub resource_pack: ResourcePackConfig, - pub commands: CommandsConfig, - pub rcon: RCONConfig, - pub pvp: PVPConfig, -} - -#[derive(Deserialize, Serialize, Clone)] -pub struct RCONConfig { - pub enabled: bool, - pub ip: String, - pub port: u16, - pub password: String, -} - -impl Default for RCONConfig { - fn default() -> Self { - Self { - enabled: false, - ip: "0.0.0.0".to_string(), - port: 25575, - password: "".to_string(), - } - } -} - -#[derive(Deserialize, Serialize)] -pub struct CommandsConfig { - /// Are commands from the Console accepted ? - pub use_console: bool, - // TODO: commands... -} - -impl Default for CommandsConfig { - fn default() -> Self { - Self { use_console: true } - } -} - -#[derive(Deserialize, Serialize)] -pub struct PVPConfig { - /// Is PVP enabled ? - pub enabled: bool, - /// Do we want to have the Red hurt animation & fov bobbing - pub hurt_animation: bool, - /// Should players in creative be protected against PVP - pub protect_creative: bool, - /// Has PVP Knockback? - pub knockback: bool, - /// Should player swing when attacking? - pub swing: bool, -} - -impl Default for PVPConfig { - fn default() -> Self { - Self { - enabled: true, - hurt_animation: true, - protect_creative: true, - knockback: true, - swing: true, - } - } -} - -#[derive(Deserialize, Serialize)] -// Packet compression -pub struct CompressionConfig { - /// Is compression enabled ? - pub enabled: bool, - /// The compression threshold used when compression is enabled - pub compression_threshold: u32, - /// A value between 0..9 - /// 1 = Optimize for the best speed of encoding. - /// 9 = Optimize for the size of data being encoded. - pub compression_level: u32, -} - -impl Default for CompressionConfig { - fn default() -> Self { - Self { - enabled: true, - compression_threshold: 256, - compression_level: 4, - } - } -} - -/// Important: The Configuration should match Vanilla by default -impl Default for AdvancedConfiguration { - fn default() -> Self { - Self { - proxy: ProxyConfig::default(), - authentication: AuthenticationConfig::default(), - commands: CommandsConfig::default(), - packet_compression: CompressionConfig::default(), - resource_pack: ResourcePackConfig::default(), - rcon: RCONConfig::default(), - pvp: PVPConfig::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 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: "0.0.0.0".to_string(), - server_port: 25565, - seed: "".to_string(), - max_players: 100000, - view_distance: 10, - simulation_distance: 10, - 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"); - let config: AdvancedConfiguration = - toml::from_str(toml.as_str()).expect("Couldn't parse features.toml, Probably old config, Replacing with a new one or just delete it"); - config.validate(); - 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.validate(); - config - } - } - pub fn validate(&self) { - self.resource_pack.validate() - } -} - -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"); - let config: BasicConfiguration = toml::from_str(toml.as_str()).expect("Couldn't parse configuration.toml, Probably old config, Replacing with a new one or just delete it"); - config.validate(); - config - } 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 at least 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" - ) - } - } -} diff --git a/pumpkin/src/entity/player.rs b/pumpkin/src/entity/player.rs index 2ee561f41..1dd05cb50 100644 --- a/pumpkin/src/entity/player.rs +++ b/pumpkin/src/entity/player.rs @@ -1,8 +1,8 @@ -use std::{str::FromStr, sync::Arc}; +use std::sync::Arc; -use num_derive::{FromPrimitive, ToPrimitive}; +use num_derive::FromPrimitive; use num_traits::ToPrimitive; -use pumpkin_core::text::TextComponent; +use pumpkin_core::{text::TextComponent, GameMode}; use pumpkin_entity::{entity_type::EntityType, pose::EntityPose, Entity, EntityId}; use pumpkin_inventory::player::PlayerInventory; use pumpkin_protocol::{ @@ -21,7 +21,6 @@ use pumpkin_protocol::{ ConnectionState, RawPacket, ServerPacket, VarInt, }; use pumpkin_world::vector3::Vector3; -use serde::{Deserialize, Serialize}; use crate::{ client::{authentication::GameProfile, Client}, @@ -454,29 +453,3 @@ pub enum ChatMode { CommandsOnly, Hidden, } - -#[derive(Clone, Copy, Debug, PartialEq, Serialize, Deserialize, FromPrimitive, ToPrimitive)] -pub enum GameMode { - Undefined = -1, - Survival, - Creative, - Adventure, - Spectator, -} - -#[derive(Debug, PartialEq, Eq)] -pub struct ParseGameModeError; - -impl FromStr for GameMode { - type Err = ParseGameModeError; - - fn from_str(s: &str) -> Result { - match s { - "survival" => Ok(Self::Survival), - "creative" => Ok(Self::Creative), - "adventure" => Ok(Self::Adventure), - "spectator" => Ok(Self::Spectator), - _ => Err(ParseGameModeError), - } - } -} diff --git a/pumpkin/src/main.rs b/pumpkin/src/main.rs index 56281f274..79cd54053 100644 --- a/pumpkin/src/main.rs +++ b/pumpkin/src/main.rs @@ -6,23 +6,18 @@ compile_error!("Compiling for WASI targets is not supported!"); use mio::net::TcpListener; use mio::{Events, Interest, Poll, Token}; -use std::io::{self}; - -use client::Client; -use commands::handle_command; -use config::AdvancedConfiguration; use std::collections::HashMap; +use std::io::{self}; -use client::interrupted; -use config::BasicConfiguration; +use client::{interrupted, Client}; +use commands::handle_command; use server::Server; // Setup some tokens to allow us to identify which event is for which socket. pub mod client; pub mod commands; -pub mod config; pub mod entity; pub mod proxy; pub mod rcon; @@ -34,6 +29,7 @@ fn main() -> io::Result<()> { use std::sync::{Arc, Mutex}; use entity::player::Player; + use pumpkin_config::{ADVANCED_CONFIG, BASIC_CONFIG}; use pumpkin_core::text::{color::NamedColor, TextComponent}; use rcon::RCONServer; @@ -64,9 +60,6 @@ fn main() -> io::Result<()> { use std::time::Instant; let time = Instant::now(); - let basic_config = BasicConfiguration::load("configuration.toml"); - - let advanced_configuration = AdvancedConfiguration::load("features.toml"); // Create a poll instance. let mut poll = Poll::new()?; @@ -74,14 +67,7 @@ fn main() -> io::Result<()> { let mut events = Events::with_capacity(128); // Setup the TCP server socket. - - let addr = format!( - "{}:{}", - basic_config.server_address, basic_config.server_port - ) - .parse() - .unwrap(); - + let addr = BASIC_CONFIG.server_address; let mut listener = TcpListener::bind(addr)?; // Register the server with poll we can receive events for it. @@ -91,16 +77,13 @@ fn main() -> io::Result<()> { // Unique token for each incoming connection. let mut unique_token = Token(SERVER.0 + 1); - let use_console = advanced_configuration.commands.use_console; - let rcon = advanced_configuration.rcon.clone(); + let use_console = ADVANCED_CONFIG.commands.use_console; + let rcon = ADVANCED_CONFIG.rcon.clone(); let mut clients: HashMap = HashMap::new(); let mut players: HashMap, Arc>> = HashMap::new(); - let server = Arc::new(tokio::sync::Mutex::new(Server::new(( - basic_config, - advanced_configuration, - )))); + let server = Arc::new(tokio::sync::Mutex::new(Server::new())); log::info!("Started Server took {}ms", time.elapsed().as_millis()); log::info!("You now can connect to the server, Listening on {}", addr); @@ -214,7 +197,7 @@ fn main() -> io::Result<()> { server.add_player(token.clone(), client).await; players.insert(token, player.clone()); let mut world = world.lock().await; - world.spawn_player(&server.base_config, player).await; + world.spawn_player(&BASIC_CONFIG, player).await; } } } diff --git a/pumpkin/src/proxy/velocity.rs b/pumpkin/src/proxy/velocity.rs index 3b06ff97a..b2f753506 100644 --- a/pumpkin/src/proxy/velocity.rs +++ b/pumpkin/src/proxy/velocity.rs @@ -2,12 +2,13 @@ use std::net::SocketAddr; use bytes::{BufMut, BytesMut}; use hmac::{Hmac, Mac}; +use pumpkin_config::proxy::VelocityConfig; use pumpkin_protocol::{ bytebuf::ByteBuffer, client::login::CLoginPluginRequest, server::login::SLoginPluginResponse, }; use sha2::Sha256; -use crate::{client::Client, config::proxy::VelocityConfig}; +use crate::client::Client; type HmacSha256 = Hmac; diff --git a/pumpkin/src/rcon/mod.rs b/pumpkin/src/rcon/mod.rs index e6a94fdf8..19a5b17d6 100644 --- a/pumpkin/src/rcon/mod.rs +++ b/pumpkin/src/rcon/mod.rs @@ -9,9 +9,10 @@ use mio::{ Events, Interest, Poll, Token, }; use packet::{Packet, PacketError, PacketType}; +use pumpkin_config::RCONConfig; use thiserror::Error; -use crate::{commands::handle_command, config::RCONConfig, server::Server}; +use crate::{commands::handle_command, server::Server}; mod packet; @@ -35,11 +36,8 @@ impl RCONServer { server: Arc>, ) -> Result { assert!(config.enabled, "RCON is not enabled"); - let addr = format!("{}:{}", config.ip, config.port) - .parse() - .expect("Failed to parse RCON address"); let mut poll = Poll::new().unwrap(); - let mut listener = TcpListener::bind(addr).unwrap(); + let mut listener = TcpListener::bind(config.address).unwrap(); poll.registry() .register(&mut listener, SERVER, Interest::READABLE) diff --git a/pumpkin/src/server.rs b/pumpkin/src/server.rs index 582d4e992..8082b1daf 100644 --- a/pumpkin/src/server.rs +++ b/pumpkin/src/server.rs @@ -11,6 +11,8 @@ use std::{ use base64::{engine::general_purpose, Engine}; use image::GenericImageView; use mio::Token; +use pumpkin_config::{BasicConfiguration, BASIC_CONFIG}; +use pumpkin_core::GameMode; use pumpkin_entity::EntityId; use pumpkin_plugin::PluginLoader; use pumpkin_protocol::{ @@ -21,14 +23,8 @@ use pumpkin_world::dimension::Dimension; use pumpkin_registry::Registry; use rsa::{traits::PublicKeyParts, RsaPrivateKey, RsaPublicKey}; -use serde::{Deserialize, Serialize}; -use crate::{ - client::Client, - config::{AdvancedConfiguration, BasicConfiguration}, - entity::player::{GameMode, Player}, - world::World, -}; +use crate::{client::Client, entity::player::Player, world::World}; pub const CURRENT_MC_VERSION: &str = "1.21.1"; @@ -52,16 +48,15 @@ pub struct Server { pub cached_registry: Vec, entity_id: AtomicI32, - pub base_config: BasicConfiguration, - pub advanced_config: AdvancedConfiguration, /// Used for Authentication, None is Online mode is disabled pub auth_client: Option, } impl Server { - pub fn new(config: (BasicConfiguration, AdvancedConfiguration)) -> Self { - let status_response = Self::build_response(&config.0); + #[allow(clippy::new_without_default)] + pub fn new() -> Self { + let status_response = Self::build_response(&BASIC_CONFIG); let status_response_json = serde_json::to_string(&status_response) .expect("Failed to parse Status response into JSON"); let cached_server_brand = Self::build_brand(); @@ -75,7 +70,7 @@ impl Server { &private_key.e().to_bytes_be(), ) .into_boxed_slice(); - let auth_client = if config.0.online_mode { + let auth_client = if BASIC_CONFIG.online_mode { Some( reqwest::Client::builder() .timeout(Duration::from_millis(5000)) @@ -106,9 +101,7 @@ impl Server { status_response, status_response_json, public_key_der, - base_config: config.0, auth_client, - advanced_config: config.1, } } @@ -118,7 +111,7 @@ impl Server { client: Client, ) -> (Arc>, Arc>) { let entity_id = self.new_entity_id(); - let gamemode = match self.base_config.default_gamemode { + let gamemode = match BASIC_CONFIG.default_gamemode { GameMode::Undefined => GameMode::Survival, game_mode => game_mode, }; @@ -216,11 +209,3 @@ impl Server { (pub_key, priv_key) } } - -#[derive(PartialEq, Serialize, Deserialize)] -pub enum Difficulty { - Peaceful, - Easy, - Normal, - Hard, -} diff --git a/pumpkin/src/world/mod.rs b/pumpkin/src/world/mod.rs index 337f6332b..77123a0ab 100644 --- a/pumpkin/src/world/mod.rs +++ b/pumpkin/src/world/mod.rs @@ -6,6 +6,7 @@ use std::{ use mio::Token; use num_traits::ToPrimitive; +use pumpkin_config::BasicConfiguration; use pumpkin_entity::{entity_type::EntityType, EntityId}; use pumpkin_protocol::{ client::play::{ @@ -19,7 +20,7 @@ use pumpkin_protocol::{ use pumpkin_world::{level::Level, radial_chunk_iterator::RadialIterator}; use tokio::sync::mpsc; -use crate::{config::BasicConfiguration, entity::player::Player}; +use crate::entity::player::Player; pub struct World { pub level: Arc>,