diff --git a/Cargo.lock b/Cargo.lock index 21ed84150..25d080c6b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -888,6 +888,17 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" +[[package]] +name = "num-derive" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed3955f1a9c7c0c15e092f9c887db08b1fc683305fdf6eb6684f22555355e202" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.72", +] + [[package]] name = "num-integer" version = "0.1.46" @@ -1151,6 +1162,8 @@ dependencies = [ "log", "mio", "num-bigint", + "num-derive", + "num-traits", "pumpkin-protocol", "pumpkin-registry", "pumpkin-world", @@ -1185,6 +1198,8 @@ dependencies = [ "cfb8", "fastnbt 2.5.0 (git+https://github.com/owengage/fastnbt.git)", "log", + "num-derive", + "num-traits", "pumpkin-macros", "serde", "serde_json", diff --git a/pumpkin-protocol/Cargo.toml b/pumpkin-protocol/Cargo.toml index 1432cb3ba..564d8de38 100644 --- a/pumpkin-protocol/Cargo.toml +++ b/pumpkin-protocol/Cargo.toml @@ -17,6 +17,8 @@ serde_json = "1.0" thiserror = "1.0.63" log = "0.4" +num-traits = "0.2" +num-derive = "0.4" # for text component fastnbt = { git = "https://github.com/owengage/fastnbt.git" } diff --git a/pumpkin-protocol/src/server/play/mod.rs b/pumpkin-protocol/src/server/play/mod.rs index 209f46251..e0acfe4e4 100644 --- a/pumpkin-protocol/src/server/play/mod.rs +++ b/pumpkin-protocol/src/server/play/mod.rs @@ -1,3 +1,6 @@ +use num_derive::FromPrimitive; +use num_traits::FromPrimitive; + use crate::{ServerPacket, VarInt}; pub struct SConfirmTeleport { @@ -48,6 +51,35 @@ impl ServerPacket for SPlayerPosition { } } +pub struct SPlayerCommand { + pub entitiy_id: VarInt, + pub action: Action, + pub jump_boost: VarInt, +} +#[derive(FromPrimitive)] +pub enum Action { + StartSneaking = 0, + StopSneaking, + LeaveBed, + StartSprinting, + StopSprinting, + StartHourseJump, + OpenVehicleInventory, + StartFlyingElytra, +} + +impl ServerPacket for SPlayerCommand { + const PACKET_ID: VarInt = 0x25; + + fn read(bytebuf: &mut crate::bytebuf::ByteBuffer) -> Self { + Self { + entitiy_id: bytebuf.get_var_int(), + action: Action::from_i32(bytebuf.get_var_int()).unwrap(), + jump_boost: bytebuf.get_var_int(), + } + } +} + pub struct SPlayerPositionRotation { pub x: f64, pub feet_y: f64, diff --git a/pumpkin/Cargo.toml b/pumpkin/Cargo.toml index 69aaf790e..c5fb4fd64 100644 --- a/pumpkin/Cargo.toml +++ b/pumpkin/Cargo.toml @@ -16,6 +16,8 @@ toml = "0.8.19" rand = "0.8.5" +num-traits = "0.2" +num-derive = "0.4" num-bigint = "0.4.6" # encryption diff --git a/pumpkin/src/client/client_packet.rs b/pumpkin/src/client/client_packet.rs index 77e558dca..351adaae8 100644 --- a/pumpkin/src/client/client_packet.rs +++ b/pumpkin/src/client/client_packet.rs @@ -1,3 +1,4 @@ +use num_traits::FromPrimitive; use pumpkin_protocol::{ client::{ config::{CFinishConfig, CKnownPacks, CRegistryData}, @@ -140,10 +141,10 @@ impl Client { self.config = Some(PlayerConfig { locale: client_information.locale, view_distance: client_information.view_distance, - chat_mode: ChatMode::from(client_information.chat_mode), + chat_mode: ChatMode::from_i32(client_information.chat_mode).unwrap(), chat_colors: client_information.chat_colors, skin_parts: client_information.skin_parts, - main_hand: Hand::from(client_information.main_hand), + main_hand: Hand::from_i32(client_information.main_hand).unwrap(), text_filtering: client_information.text_filtering, server_listing: client_information.server_listing, }); diff --git a/pumpkin/src/client/mod.rs b/pumpkin/src/client/mod.rs index 785663ba9..19b18d3c8 100644 --- a/pumpkin/src/client/mod.rs +++ b/pumpkin/src/client/mod.rs @@ -12,6 +12,7 @@ use crate::{ use authentication::GameProfile; use mio::{event::Event, net::TcpStream, Token}; +use num_traits::ToPrimitive; use pumpkin_protocol::{ client::{ config::CConfigDisconnect, @@ -25,8 +26,8 @@ use pumpkin_protocol::{ handshake::SHandShake, login::{SEncryptionResponse, SLoginAcknowledged, SLoginPluginResponse, SLoginStart}, play::{ - SChatCommand, SConfirmTeleport, SPlayerPosition, SPlayerPositionRotation, - SPlayerRotation, + SChatCommand, SConfirmTeleport, SPlayerCommand, SPlayerPosition, + SPlayerPositionRotation, SPlayerRotation, }, status::{SPingRequest, SStatusRequest}, }, @@ -139,7 +140,7 @@ impl Client { } pub fn set_gamemode(&mut self, gamemode: GameMode) { - self.send_packet(CGameEvent::new(3, gamemode.to_byte() as f32)) + self.send_packet(CGameEvent::new(3, gamemode.to_f32().unwrap())) .unwrap_or_else(|e| self.kick(&e.to_string())); } @@ -241,6 +242,9 @@ impl Client { SPlayerRotation::PACKET_ID => { self.handle_rotation(server, SPlayerRotation::read(bytebuf)) } + SPlayerCommand::PACKET_ID => { + self.handle_player_command(server, SPlayerCommand::read(bytebuf)) + } _ => log::error!("Failed to handle player packet id {}", packet.id), } } diff --git a/pumpkin/src/client/player_packet.rs b/pumpkin/src/client/player_packet.rs index afb60c607..423be04cb 100644 --- a/pumpkin/src/client/player_packet.rs +++ b/pumpkin/src/client/player_packet.rs @@ -1,5 +1,6 @@ use pumpkin_protocol::server::play::{ - SChatCommand, SConfirmTeleport, SPlayerPosition, SPlayerPositionRotation, SPlayerRotation, + SChatCommand, SConfirmTeleport, SPlayerCommand, SPlayerPosition, SPlayerPositionRotation, + SPlayerRotation, }; use crate::{ @@ -72,4 +73,23 @@ impl Client { pub fn handle_chat_command(&mut self, server: &mut Server, command: SChatCommand) { handle_command(&mut CommandSender::Player(self), command.command, server); } + + pub fn handle_player_command(&mut self, _server: &mut Server, command: SPlayerCommand) { + let player = self.player.as_mut().unwrap(); + + if command.entitiy_id != player.entity.entity_id { + return; + } + + match command.action { + pumpkin_protocol::server::play::Action::StartSneaking => player.sneaking = true, + pumpkin_protocol::server::play::Action::StopSneaking => player.sneaking = false, + pumpkin_protocol::server::play::Action::LeaveBed => todo!(), + pumpkin_protocol::server::play::Action::StartSprinting => player.sprinting = true, + pumpkin_protocol::server::play::Action::StopSprinting => player.sprinting = false, + pumpkin_protocol::server::play::Action::StartHourseJump => todo!(), + pumpkin_protocol::server::play::Action::OpenVehicleInventory => todo!(), + pumpkin_protocol::server::play::Action::StartFlyingElytra => todo!(), + } + } } diff --git a/pumpkin/src/configuration.rs b/pumpkin/src/configuration.rs index b543deaa4..37fa53baa 100644 --- a/pumpkin/src/configuration.rs +++ b/pumpkin/src/configuration.rs @@ -14,9 +14,23 @@ const CURRENT_BASE_VERSION: &str = "1.0.0"; pub struct AdvancedConfiguration { /// Requires Online mode /// Should player have skins - use_skins: bool, + pub use_skins: bool, /// Should chat be enabled - enable_chat: bool, + pub enable_chat: bool, + + pub commands: Commands, +} + +#[derive(Deserialize, Serialize)] +pub struct Commands { + // Are commands from the Console accepted ? + pub use_console: bool, +} + +impl Default for Commands { + fn default() -> Self { + Self { use_console: true } + } } /// Important: The Configuration should match Vanilla by default @@ -25,6 +39,7 @@ impl Default for AdvancedConfiguration { Self { use_skins: true, enable_chat: true, + commands: Commands::default(), } } } @@ -67,7 +82,6 @@ pub struct BasicConfiguration { pub default_gamemode: GameMode, } - impl Default for BasicConfiguration { fn default() -> Self { Self { @@ -96,7 +110,7 @@ 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") + 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!"); diff --git a/pumpkin/src/entity/player.rs b/pumpkin/src/entity/player.rs index 0faf05f0a..ccded9cd0 100644 --- a/pumpkin/src/entity/player.rs +++ b/pumpkin/src/entity/player.rs @@ -1,5 +1,6 @@ use std::str::FromStr; +use num_derive::{FromPrimitive, ToPrimitive}; use pumpkin_protocol::VarInt; use serde::{Deserialize, Serialize}; @@ -16,6 +17,9 @@ pub struct Player { // Client side value, Should be not trusted pub on_ground: bool, + pub sneaking: bool, + pub sprinting: bool, + // Current awaiting teleport id, None if did not teleport pub awaiting_teleport: Option, } @@ -31,6 +35,8 @@ impl Player { pitch: 0.0, on_ground: false, awaiting_teleport: None, + sneaking: false, + sprinting: false, } } @@ -39,47 +45,22 @@ impl Player { } } +#[derive(FromPrimitive)] pub enum Hand { Main, Off, } -impl From for Hand { - fn from(value: VarInt) -> Self { - match value { - 0 => Self::Off, - 1 => Self::Main, - _ => { - log::info!("Unexpected Hand {}", value); - Self::Main - } - } - } -} - +#[derive(FromPrimitive)] pub enum ChatMode { Enabled, CommandsOnly, Hidden, } -impl From for ChatMode { - fn from(value: VarInt) -> Self { - match value { - 0 => Self::Enabled, - 1 => Self::CommandsOnly, - 2 => Self::Hidden, - _ => { - log::info!("Unexpected ChatMode {}", value); - Self::Enabled - } - } - } -} - -#[derive(Clone, Copy, PartialEq, Serialize, Deserialize)] +#[derive(Clone, Copy, PartialEq, Serialize, Deserialize, FromPrimitive, ToPrimitive)] pub enum GameMode { - Undefined, + Undefined = -1, Survival, Creative, Adventure, @@ -102,31 +83,3 @@ impl FromStr for GameMode { } } } - -impl From for GameMode { - fn from(value: i8) -> Self { - match value { - -1 => GameMode::Undefined, - 0 => GameMode::Survival, - 1 => GameMode::Creative, - 2 => GameMode::Adventure, - 3 => GameMode::Spectator, - _ => { - log::info!("Unexpected GameMode {}", value); - Self::Survival - } - } - } -} - -impl GameMode { - pub fn to_byte(self) -> i8 { - match self { - Self::Undefined => -1, - Self::Survival => 0, - Self::Creative => 1, - Self::Adventure => 2, - Self::Spectator => 3, - } - } -} diff --git a/pumpkin/src/main.rs b/pumpkin/src/main.rs index 047bdae82..9a8012e22 100644 --- a/pumpkin/src/main.rs +++ b/pumpkin/src/main.rs @@ -29,6 +29,9 @@ pub mod util; #[cfg(not(target_os = "wasi"))] 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"); @@ -58,31 +61,34 @@ fn main() -> io::Result<()> { // Unique token for each incoming connection. let mut unique_token = Token(SERVER.0 + 1); - let mut connections: HashMap = HashMap::new(); + let use_console = advanced_configuration.commands.use_console; - log::info!("You now can connect to the server"); + let mut connections: HashMap = HashMap::new(); let server = Arc::new(Mutex::new(Server::new(( basic_config, advanced_configuration, )))); - let server_clone = server.clone(); - - thread::spawn(move || { - let stdin = std::io::stdin(); - loop { - let mut out = String::new(); - stdin - .read_line(&mut out) - .expect("Failed to read console line"); - handle_command( - &mut commands::CommandSender::Console, - out, - &mut server_clone.lock().unwrap(), - ); - } - }); + log::info!("Started Server took {}ms", time.elapsed().as_millis()); + log::info!("You now can connect to the server"); + if use_console { + let server_clone = server.clone(); + thread::spawn(move || { + let stdin = std::io::stdin(); + loop { + let mut out = String::new(); + stdin + .read_line(&mut out) + .expect("Failed to read console line"); + handle_command( + &mut commands::CommandSender::Console, + out, + &mut server_clone.lock().unwrap(), + ); + } + }); + } loop { if let Err(err) = poll.poll(&mut events, None) { if interrupted(&err) { diff --git a/pumpkin/src/server.rs b/pumpkin/src/server.rs index 27f2da2a3..ecdf9401e 100644 --- a/pumpkin/src/server.rs +++ b/pumpkin/src/server.rs @@ -7,6 +7,7 @@ use std::{ use base64::{engine::general_purpose, Engine}; use mio::{event::Event, Poll, Token}; +use num_traits::ToPrimitive; use pumpkin_protocol::{ client::{ config::CPluginMessage, @@ -62,7 +63,6 @@ impl Server { let status_response = Self::build_response(&config.0); 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(); // todo, only create when needed @@ -137,8 +137,9 @@ impl Server { GameMode::Undefined => GameMode::Survival, game_mode => game_mode, } - .to_byte() as u8, - self.base_config.default_gamemode.to_byte(), + .to_u8() + .unwrap(), + self.base_config.default_gamemode.to_i8().unwrap(), false, false, false, // deth loc