diff --git a/README.md b/README.md index 0d89971..173df59 100644 --- a/README.md +++ b/README.md @@ -1,22 +1,28 @@ # SaturnClient API + The api for AuraClient which is a Minecraft mod Client +# Handshake +In order to communicate with the server, you will have to make a handshake, here is how +``` + +``` +The session token is stored on your minecraft client when it communicates with Mojang servers, as you might understand, this token is a private token used for authentication with Mojang servers, Why do we need it? Because our server needs to understand that whoever logs in with SaturnClient is you and when you make changes in your client, the changes get synced with others, Example: Changing Cloak. + # Protocol base -All whitespace is used +Every request should be only one line + ``` -method -@key=value -``` -For private requests (requires microsoft session tokens) -``` -@SessionToken= +method@key1=value1@key2=value2 ``` -# Example method +# Error handling +request ``` -ping +invalid-method ``` -- Response +response ``` -pong -``` \ No newline at end of file +!Invalid method +``` +On a response `!` means it's an error. diff --git a/src/assets.rs b/src/assets.rs deleted file mode 100644 index f13b444..0000000 --- a/src/assets.rs +++ /dev/null @@ -1 +0,0 @@ -pub const CLOAKS: &[&str] = &["glitch"]; diff --git a/src/database.rs b/src/database.rs new file mode 100644 index 0000000..b9456ba --- /dev/null +++ b/src/database.rs @@ -0,0 +1,18 @@ +pub struct Player { + pub uuid: String, + pub cloaks: Vec, + pub cloak: String, +} + +pub struct Database { + pub players: mongodb::sync::Collection, +} + +impl Database { + pub fn new(client: &mongodb::sync::Client) -> Self { + let db = client.database("saturn"); + Self { + players: db.collection("players"), + } + } +} diff --git a/src/io.rs b/src/io.rs deleted file mode 100644 index 498032c..0000000 --- a/src/io.rs +++ /dev/null @@ -1,33 +0,0 @@ -use mongodb::sync::{Client, Collection}; -use serde::{Deserialize, Serialize}; - -pub enum Response { - Pong, -} - -impl Response { - pub fn to_string(&self) -> String { - match self { - Response::Pong => "pong".to_string(), - } - } -} - -#[derive(Debug, Serialize, Deserialize)] -pub struct Player { - pub uuid: String, - pub cloaks: Vec, -} - -pub struct Database { - pub players: Collection, -} - -impl Database { - pub fn new(client: &Client) -> Self { - let db = client.database("saturn"); - Self { - players: db.collection("players"), - } - } -} diff --git a/src/main.rs b/src/main.rs index d533c8b..f4adb78 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,68 +1,44 @@ -pub mod assets; -pub mod io; +use std::{ + io::{BufRead, BufReader, Write}, + net::TcpListener, + sync::Arc, +}; + +pub mod database; pub mod methods; pub mod parser; - -use std::io::{Read, Write}; -use std::net::{TcpListener, TcpStream}; -use std::sync::Arc; -use std::thread; - -use io::Database; -use methods::handle_request; -use mongodb::sync::Client; - -const MONGO_URI: &str = "mongodb://admin:admin@localhost/"; +pub mod response; fn main() -> std::io::Result<()> { - let client = Client::with_uri_str(MONGO_URI).unwrap(); - let database = Arc::new(Database::new(&client)); - let listener = TcpListener::bind("127.0.0.1:8080")?; + let client = mongodb::sync::Client::with_uri_str("mongodb://admin:admin@localhost/").unwrap(); + let database = Arc::new(database::Database::new(&client)); - println!("Server listening on port 8080"); + let listener = TcpListener::bind("127.0.0.1:8080").unwrap(); for stream in listener.incoming() { - match stream { - Ok(stream) => { - println!("New connection: {}", stream.peer_addr().unwrap()); - thread::spawn({ - let database = Arc::clone(&database); - move || handle_client(stream, database) - }); + println!("New connection"); + let database = Arc::clone(&database); + + std::thread::spawn({ + move || { + let mut stream = stream.unwrap(); + let mut reader = BufReader::new(stream.try_clone().unwrap()); + let session = methods::Session::new(&mut reader, database).unwrap(); + + loop { + let mut request_string = String::new(); + match reader.read_line(&mut request_string).unwrap() { + 0 => break, + _ => { + let (method, params) = parser::parse(&request_string).unwrap(); + let response = session.handle_request(&method, ¶ms); + stream.write_all(response.to_string().as_bytes()).unwrap(); + } + }; + } } - Err(e) => eprintln!("Connection failed: {}", e), - } + }); } Ok(()) } - -pub fn handle_client(mut stream: TcpStream, database: Arc) { - let mut buffer = [0; 512]; - - loop { - match stream.read(&mut buffer) { - Ok(0) => break, - Ok(n) => { - let recv = String::from_utf8_lossy(&buffer[..n]).to_string(); - match parser::parse(&recv) { - Ok((method, params)) => { - match handle_request(Arc::clone(&database), method, params) { - Ok(response) => { - stream.write_all(response.to_string().as_bytes()).unwrap() - } - Err(e) => eprintln!("Error handling request: {}", e), - } - } - Err(e) => eprintln!("Error parsing request: {}", e), - }; - } - Err(e) => { - eprintln!("Error reading from client: {}", e); - break; - } - } - } - - println!("Client disconnected"); -} diff --git a/src/methods/mod.rs b/src/methods/mod.rs index 944631b..9b49ef5 100644 --- a/src/methods/mod.rs +++ b/src/methods/mod.rs @@ -1,14 +1,38 @@ -use std::{collections::HashMap, sync::Arc}; +use std::{ + collections::HashMap, + io::{self, BufRead, BufReader}, + net::TcpStream, + sync::Arc, +}; -use crate::io::{Database, Response}; +pub struct Session { + pub session_id: String, + pub database: Arc, +} -pub fn handle_request( - database: Arc, - method: String, - params: HashMap, -) -> Result { - match method.as_str() { - "ping" => Ok(Response::Pong), - _ => Err("Invalid request: ".to_string()), +impl Session { + pub fn new( + reader: &mut BufReader, + database: Arc, + ) -> io::Result { + let mut session_id = String::new(); + reader.read_line(&mut session_id)?; + + Ok(Self { + session_id, + database, + }) + } + + pub fn handle_request( + &self, + method: &str, + params: &HashMap, + ) -> crate::response::Response { + match method { + "ping" => crate::response::Response::Pong, + + _ => crate::response::Response::Error(crate::response::Error::InvalidRequest), + } } } diff --git a/src/parser.rs b/src/parser.rs index 0a5a4e8..2c08df6 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -13,7 +13,7 @@ impl ParamMap for HashMap { } } -pub fn parse(s: &str) -> Result<(String, HashMap), String> { +pub fn parse(s: &str) -> std::io::Result<(String, HashMap)> { let mut parts = s.split("@"); let method = parts.next().unwrap().trim().to_lowercase(); diff --git a/src/response.rs b/src/response.rs new file mode 100644 index 0000000..06392c9 --- /dev/null +++ b/src/response.rs @@ -0,0 +1,35 @@ +#[derive(Debug, Clone)] +pub enum Response { + Pong, + Error(Error), +} + +impl std::fmt::Display for Response { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Response::Pong => write!(f, "Pong"), + Response::Error(error) => write!(f, "!{}", error), + } + } +} + +#[derive(Debug, Clone)] +pub enum Error { + InvalidRequest, + InvalidMethod, + InvalidParameter, + ParameterNotFound, + InvalidSessionToken, +} + +impl std::fmt::Display for Error { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Error::InvalidRequest => write!(f, "Invalid request"), + Error::InvalidMethod => write!(f, "Invalid method"), + Error::InvalidParameter => write!(f, "Invalid parameter"), + Error::ParameterNotFound => write!(f, "Parameter not found"), + Error::InvalidSessionToken => write!(f, "Invalid session token"), + } + } +}