Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement Query protocol #218

Draft
wants to merge 3 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions pumpkin-config/src/lib.rs
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -16,6 +17,7 @@ use std::{
pub mod auth;
pub mod logging;
pub mod proxy;
pub mod query;
pub mod resource_pack;

pub use auth::AuthenticationConfig;
Expand Down Expand Up @@ -53,6 +55,7 @@ pub struct AdvancedConfiguration {
pub rcon: RCONConfig,
pub pvp: PVPConfig,
pub logging: LoggingConfig,
pub query: QueryConfig,
}

#[serde_inline_default]
Expand Down
21 changes: 21 additions & 0 deletions pumpkin-config/src/query.rs
Original file line number Diff line number Diff line change
@@ -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<u16>,
}

impl Default for QueryConfig {
fn default() -> Self {
Self {
enabled: false,
port: None,
}
}
}
2 changes: 1 addition & 1 deletion pumpkin-protocol/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
1 change: 1 addition & 0 deletions pumpkin-protocol/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ pub mod bytebuf;
pub mod client;
pub mod packet_decoder;
pub mod packet_encoder;
pub mod query;
pub mod server;
pub mod slot;

Expand Down
108 changes: 108 additions & 0 deletions pumpkin-protocol/src/query.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
use tokio::io::AsyncReadExt;

#[derive(Debug)]
#[repr(u8)]
pub enum PacketType {
Handshake = 9,
Stat = 0,
}

#[derive(Debug)]
pub struct SBasePacket {
pub magic: u16,
pub packet_type: PacketType,
neeleshpoli marked this conversation as resolved.
Show resolved Hide resolved
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<String>,
},
}

impl CBasePacket {
fn encode(&self) -> Vec<u8> {
// 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!(),
}
}
}
8 changes: 8 additions & 0 deletions pumpkin/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ pub mod command;
pub mod entity;
pub mod error;
pub mod proxy;
pub mod query;
pub mod rcon;
pub mod server;
pub mod world;
Expand Down Expand Up @@ -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 {
Expand Down
26 changes: 26 additions & 0 deletions pumpkin/src/query.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
// 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);
});
}
}
Loading