From 813ef02d7afad2e905b0d1ec90d4f8ad79d481ac Mon Sep 17 00:00:00 2001 From: "neelesh.poli2006@gmail.com" <72574589+neeleshpoli@users.noreply.github.com> Date: Wed, 30 Oct 2024 20:59:42 -0500 Subject: [PATCH 1/3] Add query options to config Added packet data types Correctly parse a handshake packet Add tokio to pumpkin protocol for `AsyncReadExt` convenience functions --- pumpkin-config/src/lib.rs | 3 ++ pumpkin-config/src/query.rs | 21 +++++++++ pumpkin-protocol/Cargo.toml | 2 +- pumpkin-protocol/src/lib.rs | 1 + pumpkin-protocol/src/query.rs | 89 +++++++++++++++++++++++++++++++++++ pumpkin/src/main.rs | 8 ++++ pumpkin/src/query.rs | 25 ++++++++++ 7 files changed, 148 insertions(+), 1 deletion(-) create mode 100644 pumpkin-config/src/query.rs create mode 100644 pumpkin-protocol/src/query.rs create mode 100644 pumpkin/src/query.rs diff --git a/pumpkin-config/src/lib.rs b/pumpkin-config/src/lib.rs index 3295b186..c4bae06f 100644 --- a/pumpkin-config/src/lib.rs +++ b/pumpkin-config/src/lib.rs @@ -1,6 +1,7 @@ use log::warn; use logging::LoggingConfig; use pumpkin_core::{Difficulty, GameMode}; +use query::QueryConfig; use serde::{de::DeserializeOwned, Deserialize, Serialize}; // TODO: when https://github.com/rust-lang/rfcs/pull/3681 gets merged, replace serde-inline-default with native syntax @@ -17,6 +18,7 @@ pub mod auth; pub mod logging; pub mod proxy; pub mod resource_pack; +pub mod query; pub use auth::AuthenticationConfig; pub use commands::CommandsConfig; @@ -53,6 +55,7 @@ pub struct AdvancedConfiguration { pub rcon: RCONConfig, pub pvp: PVPConfig, pub logging: LoggingConfig, + pub query: QueryConfig, } #[serde_inline_default] diff --git a/pumpkin-config/src/query.rs b/pumpkin-config/src/query.rs new file mode 100644 index 00000000..65593173 --- /dev/null +++ b/pumpkin-config/src/query.rs @@ -0,0 +1,21 @@ +use serde::{Deserialize, Serialize}; +use serde_inline_default::serde_inline_default; + +#[serde_inline_default] +#[derive(Deserialize, Serialize)] +pub struct QueryConfig { + #[serde_inline_default(false)] + pub enabled: bool, + // Optional so if not specified the port server is running on will be used + #[serde_inline_default(None)] + pub port: Option, +} + +impl Default for QueryConfig { + fn default() -> Self { + Self { + enabled: false, + port: None, + } + } +} \ No newline at end of file diff --git a/pumpkin-protocol/Cargo.toml b/pumpkin-protocol/Cargo.toml index abb54fc8..0f0ca1da 100644 --- a/pumpkin-protocol/Cargo.toml +++ b/pumpkin-protocol/Cargo.toml @@ -14,7 +14,7 @@ serde.workspace = true thiserror.workspace = true itertools.workspace = true log.workspace = true - +tokio.workspace = true num-traits.workspace = true num-derive.workspace = true diff --git a/pumpkin-protocol/src/lib.rs b/pumpkin-protocol/src/lib.rs index 0ef2bc25..7b42e96d 100644 --- a/pumpkin-protocol/src/lib.rs +++ b/pumpkin-protocol/src/lib.rs @@ -9,6 +9,7 @@ pub mod packet_decoder; pub mod packet_encoder; pub mod server; pub mod slot; +pub mod query; mod var_int; pub use var_int::*; diff --git a/pumpkin-protocol/src/query.rs b/pumpkin-protocol/src/query.rs new file mode 100644 index 00000000..581e168b --- /dev/null +++ b/pumpkin-protocol/src/query.rs @@ -0,0 +1,89 @@ +use tokio::io::AsyncReadExt; + +#[derive(Debug)] +pub enum PacketType { + Handshake = 9, + Stat = 0, +} + +#[derive(Debug)] +pub struct SBasePacket { + pub magic: u16, + pub packet_type: PacketType, + pub session_id: i32, + pub payload: SBasePayload, +} + +#[derive(Debug)] +pub enum SBasePayload { + Handshake, + BasicInfo(i32), + FullInfo(i32), +} + +impl SBasePacket { + pub async fn decode(mut reader: impl AsyncReadExt + Unpin) -> Self { + let magic = reader.read_u16().await.unwrap(); + match reader.read_u8().await.unwrap() { + 0 => todo!(), + // Handshake + 9 => Self { + magic, + packet_type: PacketType::Handshake, + session_id: reader.read_i32().await.unwrap(), + payload: SBasePayload::Handshake, + }, + _ => todo!(), + } + } +} + +pub struct CBasePacket { + pub packet_type: PacketType, + pub session_id: i32, + pub payload: CBasePayload, +} + +pub enum CBasePayload { + Handshake { + // For simplicity use a number type + // Should be encoded as string here + // Will be converted in encoding + challange_token: i32 + }, + BasicInfo { + // Use CString as protocol requires nul terminated strings + motd: String, + gametype: String, + map: String, + num_players: String, + max_players: String, + host_port: u16, + host_ip: String, + }, + FullInfo { + hostname: String, + // Game type and game id are hardcoded into protocol + // They are not here as they cannot be changed + version: String, + plugins: String, + map: String, + num_players: u16, + max_players: u16, + host_port: u16, + host_ip: String, + players: Vec, + } +} + +impl CBasePacket { + fn encode(&self) -> Vec { + // let buf = Vec::new(); + + match &self.payload { + CBasePayload::Handshake { challange_token } => todo!(), + CBasePayload::BasicInfo { motd, gametype, map, num_players, max_players, host_port, host_ip } => todo!(), + CBasePayload::FullInfo { hostname, version, plugins, map, num_players, max_players, host_port, host_ip, players } => todo!(), + } + } +} diff --git a/pumpkin/src/main.rs b/pumpkin/src/main.rs index 8b7ca607..c6be951a 100644 --- a/pumpkin/src/main.rs +++ b/pumpkin/src/main.rs @@ -47,6 +47,7 @@ pub mod proxy; pub mod rcon; pub mod server; pub mod world; +pub mod query; fn scrub_address(ip: &str) -> String { use pumpkin_config::BASIC_CONFIG; @@ -140,6 +141,13 @@ async fn main() -> io::Result<()> { RCONServer::new(&rcon, server).await.unwrap(); }); } + + if ADVANCED_CONFIG.query.enabled { + let server = server.clone(); + log::info!("Query protocol enabled. Starting..."); + tokio::spawn(query::start_query_handler()); + } + { let server = server.clone(); tokio::spawn(async move { diff --git a/pumpkin/src/query.rs b/pumpkin/src/query.rs new file mode 100644 index 00000000..174a91ad --- /dev/null +++ b/pumpkin/src/query.rs @@ -0,0 +1,25 @@ +// Query protocol + +use std::io::Cursor; + +use pumpkin_protocol::query::SBasePacket; +use tokio::net::UdpSocket; + +pub async fn start_query_handler() { + let socket = UdpSocket::bind("0.0.0.0:25565").await.expect("Unable to bind to address"); + log::info!("Query socket created"); + + loop { + let mut buf= vec![0; 1024]; + log::info!("Waiting for requests"); + let (len, addr) = socket.recv_from(&mut buf).await.unwrap(); + + tokio::spawn(async move { + let cursor = Cursor::new(buf); + let packet = SBasePacket::decode(cursor).await; + + println!("{:#?}", packet); + + }); + } +} From 363e06b7aaf95387504adf04edab4ec78ba6cb17 Mon Sep 17 00:00:00 2001 From: "neelesh.poli2006@gmail.com" <72574589+neeleshpoli@users.noreply.github.com> Date: Thu, 31 Oct 2024 07:40:36 -0500 Subject: [PATCH 2/3] Run cargo fmt --- pumpkin-config/src/lib.rs | 2 +- pumpkin-config/src/query.rs | 2 +- pumpkin-protocol/src/lib.rs | 2 +- pumpkin-protocol/src/query.rs | 30 ++++++++++++++++++++++++------ pumpkin/src/main.rs | 2 +- pumpkin/src/query.rs | 7 ++++--- 6 files changed, 32 insertions(+), 13 deletions(-) diff --git a/pumpkin-config/src/lib.rs b/pumpkin-config/src/lib.rs index c4bae06f..e7f4f4ee 100644 --- a/pumpkin-config/src/lib.rs +++ b/pumpkin-config/src/lib.rs @@ -17,8 +17,8 @@ use std::{ pub mod auth; pub mod logging; pub mod proxy; -pub mod resource_pack; pub mod query; +pub mod resource_pack; pub use auth::AuthenticationConfig; pub use commands::CommandsConfig; diff --git a/pumpkin-config/src/query.rs b/pumpkin-config/src/query.rs index 65593173..6a735149 100644 --- a/pumpkin-config/src/query.rs +++ b/pumpkin-config/src/query.rs @@ -18,4 +18,4 @@ impl Default for QueryConfig { port: None, } } -} \ No newline at end of file +} diff --git a/pumpkin-protocol/src/lib.rs b/pumpkin-protocol/src/lib.rs index 7b42e96d..d30ae3ba 100644 --- a/pumpkin-protocol/src/lib.rs +++ b/pumpkin-protocol/src/lib.rs @@ -7,9 +7,9 @@ pub mod bytebuf; pub mod client; pub mod packet_decoder; pub mod packet_encoder; +pub mod query; pub mod server; pub mod slot; -pub mod query; mod var_int; pub use var_int::*; diff --git a/pumpkin-protocol/src/query.rs b/pumpkin-protocol/src/query.rs index 581e168b..cd4dcc7a 100644 --- a/pumpkin-protocol/src/query.rs +++ b/pumpkin-protocol/src/query.rs @@ -31,7 +31,7 @@ impl SBasePacket { magic, packet_type: PacketType::Handshake, session_id: reader.read_i32().await.unwrap(), - payload: SBasePayload::Handshake, + payload: SBasePayload::Handshake, }, _ => todo!(), } @@ -39,7 +39,7 @@ impl SBasePacket { } pub struct CBasePacket { - pub packet_type: PacketType, + pub packet_type: PacketType, pub session_id: i32, pub payload: CBasePayload, } @@ -49,7 +49,7 @@ pub enum CBasePayload { // For simplicity use a number type // Should be encoded as string here // Will be converted in encoding - challange_token: i32 + challange_token: i32, }, BasicInfo { // Use CString as protocol requires nul terminated strings @@ -73,7 +73,7 @@ pub enum CBasePayload { host_port: u16, host_ip: String, players: Vec, - } + }, } impl CBasePacket { @@ -82,8 +82,26 @@ impl CBasePacket { match &self.payload { CBasePayload::Handshake { challange_token } => todo!(), - CBasePayload::BasicInfo { motd, gametype, map, num_players, max_players, host_port, host_ip } => todo!(), - CBasePayload::FullInfo { hostname, version, plugins, map, num_players, max_players, host_port, host_ip, players } => todo!(), + CBasePayload::BasicInfo { + motd, + gametype, + map, + num_players, + max_players, + host_port, + host_ip, + } => todo!(), + CBasePayload::FullInfo { + hostname, + version, + plugins, + map, + num_players, + max_players, + host_port, + host_ip, + players, + } => todo!(), } } } diff --git a/pumpkin/src/main.rs b/pumpkin/src/main.rs index c6be951a..41317cd8 100644 --- a/pumpkin/src/main.rs +++ b/pumpkin/src/main.rs @@ -44,10 +44,10 @@ pub mod command; pub mod entity; pub mod error; pub mod proxy; +pub mod query; pub mod rcon; pub mod server; pub mod world; -pub mod query; fn scrub_address(ip: &str) -> String { use pumpkin_config::BASIC_CONFIG; diff --git a/pumpkin/src/query.rs b/pumpkin/src/query.rs index 174a91ad..f9f9604b 100644 --- a/pumpkin/src/query.rs +++ b/pumpkin/src/query.rs @@ -6,11 +6,13 @@ use pumpkin_protocol::query::SBasePacket; use tokio::net::UdpSocket; pub async fn start_query_handler() { - let socket = UdpSocket::bind("0.0.0.0:25565").await.expect("Unable to bind to address"); + let socket = UdpSocket::bind("0.0.0.0:25565") + .await + .expect("Unable to bind to address"); log::info!("Query socket created"); loop { - let mut buf= vec![0; 1024]; + let mut buf = vec![0; 1024]; log::info!("Waiting for requests"); let (len, addr) = socket.recv_from(&mut buf).await.unwrap(); @@ -19,7 +21,6 @@ pub async fn start_query_handler() { let packet = SBasePacket::decode(cursor).await; println!("{:#?}", packet); - }); } } From 52853618e7d99d83218e1d6e851509ce9d5cf337 Mon Sep 17 00:00:00 2001 From: "neelesh.poli2006@gmail.com" <72574589+neeleshpoli@users.noreply.github.com> Date: Thu, 31 Oct 2024 07:50:41 -0500 Subject: [PATCH 3/3] Add repr(u8) so that PacketType is a byte --- pumpkin-protocol/src/query.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/pumpkin-protocol/src/query.rs b/pumpkin-protocol/src/query.rs index cd4dcc7a..a15dae57 100644 --- a/pumpkin-protocol/src/query.rs +++ b/pumpkin-protocol/src/query.rs @@ -1,6 +1,7 @@ use tokio::io::AsyncReadExt; #[derive(Debug)] +#[repr(u8)] pub enum PacketType { Handshake = 9, Stat = 0,