From 5731e193c8da44a9ee861f3a2e4b84bc90bc1e7e Mon Sep 17 00:00:00 2001 From: Leo dev Date: Wed, 2 Apr 2025 20:08:14 +0200 Subject: [PATCH] online system --- src/database.rs | 1 + src/main.rs | 5 ++- src/methods/mod.rs | 88 +++++++++++++++++++++++++------------------ src/methods/player.rs | 83 +++++++++++++++++++++++----------------- src/response.rs | 2 + 5 files changed, 107 insertions(+), 72 deletions(-) diff --git a/src/database.rs b/src/database.rs index de804ed..fa17ce9 100644 --- a/src/database.rs +++ b/src/database.rs @@ -7,6 +7,7 @@ pub struct Player { pub cloak: String, pub hats: Vec, pub hat: String, + pub online: bool, } pub struct Database { diff --git a/src/main.rs b/src/main.rs index 47271bb..bd0d6e1 100644 --- a/src/main.rs +++ b/src/main.rs @@ -32,7 +32,10 @@ fn main() -> std::io::Result<()> { loop { let mut request_string = String::new(); match reader.read_line(&mut request_string).unwrap() { - 0 => break, + 0 => { + methods::player::logout(&session).unwrap(); + println!("[MOJANG] {} went offline", session.local_player.name); + } _ => { let (method, params) = parser::parse(&request_string).unwrap(); let response = session.handle_request(&method, ¶ms); diff --git a/src/methods/mod.rs b/src/methods/mod.rs index 927c12d..d18685e 100644 --- a/src/methods/mod.rs +++ b/src/methods/mod.rs @@ -1,4 +1,4 @@ -mod player; +pub mod player; mod set_cloak; mod set_hat; @@ -6,10 +6,9 @@ use std::{ collections::HashMap, io::{BufRead, BufReader}, net::TcpStream, - sync::Arc, + sync::{mpsc, Arc}, }; -use player::create; use serde::{Deserialize, Serialize}; use crate::{ @@ -34,42 +33,57 @@ impl Session { reader: &mut BufReader, database: Arc, ) -> Result<(Self, Response)> { - // Read the session token - let mut session_token = String::new(); - reader - .read_line(&mut session_token) + let (token_send, token_recv) = + mpsc::channel::>(); + + let reader_clone = reader + .get_ref() + .try_clone() .map_err(|_| crate::response::Error::InvalidHandshake)?; - - // Validate session id - let response = minreq::get("https://api.minecraftservices.com/minecraft/profile") - .with_header("Authorization", &format!("Bearer {session_token}")) - .send() - .map_err(|_| crate::response::Error::InvalidSession)?; - - // If the session is invalid, return an error - if response.status_code != 200 { - return Err(crate::response::Error::InvalidSession); - } - - // Parse the player data - let local_player: LocalPlayer = response - .json() - .map_err(|_| crate::response::Error::InvalidSession)?; - - let session = Self { - session_token, - database, - local_player, - }; - - // Capture the player - match player::login(&session) { - Ok(player) => Ok((session, player)), - Err(crate::response::Error::DatabaseError) => { - let player = create(&session)?; - Ok((session, player)) + std::thread::spawn(move || { + let mut reader = BufReader::new(reader_clone); + let mut session_token = String::new(); + if reader.read_line(&mut session_token).is_ok() { + token_send.send(Ok(session_token)) + } else { + token_send.send(Err(crate::response::Error::InvalidHandshake)) } - Err(e) => Err(e), + }); + + match token_recv.recv_timeout(std::time::Duration::from_secs(20)) { + Ok(Ok(session_token)) => { + // Validate session id + let response = minreq::get("https://api.minecraftservices.com/minecraft/profile") + .with_header("Authorization", &format!("Bearer {session_token}")) + .send() + .map_err(|_| crate::response::Error::InvalidSession)?; + + // If the session is invalid, return an error + if response.status_code != 200 { + return Err(crate::response::Error::InvalidSession); + } + + // Parse the player data + let local_player: LocalPlayer = response + .json() + .map_err(|_| crate::response::Error::InvalidSession)?; + + println!("[MOJANG] {} successfully logged on", &local_player.name); + + let session = Self { + session_token, + database, + local_player, + }; + + // Capture the player + match player::login(&session) { + Ok(player) => Ok((session, player)), + Err(e) => Err(e), + } + } + Err(mpsc::RecvTimeoutError::Timeout) => Err(crate::response::Error::Timeout), + _ => Err(crate::response::Error::InvalidHandshake), } } diff --git a/src/methods/player.rs b/src/methods/player.rs index c41dba7..37f287b 100644 --- a/src/methods/player.rs +++ b/src/methods/player.rs @@ -16,22 +16,27 @@ pub fn player(session: &Session, uuid: String) -> Result { .run()?; // Check if there's a document in the cursor - match cursor.next() { + Ok(match cursor.next() { Some(Ok(player)) => { - // Player exists, deserialize and return their data - Ok(Response::Player(crate::response::PlayerResponse::Player { - cloak: player.cloak, - uuid, - cloaks: player.cloaks, - hats: player.hats, - hat: player.hat, - })) + if player.online { + Response::Player(crate::response::PlayerResponse::Player { + cloak: player.cloak, + uuid, + cloaks: player.cloaks, + hats: player.hats, + hat: player.hat, + }) + } else { + crate::response::Response::Player(crate::response::PlayerResponse::NonSaturnPlayer( + uuid, + )) + } } - _ => Ok(crate::response::Response::Player( - crate::response::PlayerResponse::NonSaturnPlayer(uuid), + _ => crate::response::Response::Player(crate::response::PlayerResponse::NonSaturnPlayer( + uuid, )), - } + }) } pub fn create(session: &Session) -> Result { @@ -43,6 +48,7 @@ pub fn create(session: &Session) -> Result { cloak: "".to_string(), hats: Vec::new(), hat: String::new(), + online: true, }; session.database.players.insert_one(player.clone()).run()?; @@ -68,7 +74,18 @@ pub fn login(session: &Session) -> Result { // Check if there's a document in the cursor match cursor.next() { - Some(Ok(player)) => { + Some(Ok(mut player)) => { + // Update player's online status + player.online = true; + session + .database + .players + .update_one( + doc! { "uuid": uuid.clone() }, + doc! { "$set": { "online": true } }, + ) + .run()?; + // Player exists, deserialize and return their data Ok(Response::Player(crate::response::PlayerResponse::Player { cloak: player.cloak, @@ -80,26 +97,24 @@ pub fn login(session: &Session) -> Result { } // No document found or deserialization error - _ => { - let player = Player { - uuid: uuid.clone(), - cloaks: Vec::new(), - cloak: "".to_string(), - hats: Vec::new(), - hat: String::new(), - }; - - // Insert the new player - session.database.players.insert_one(player.clone()).run()?; - - // Return the newly created player's data - Ok(Response::Player(crate::response::PlayerResponse::Player { - cloak: player.cloak, - uuid, - cloaks: player.cloaks, - hats: player.hats, - hat: player.hat, - })) - } + _ => create(session), } } + +pub fn logout(session: &Session) -> Result { + let uuid = session.local_player.id.clone(); + + // Update player's online status to false + session + .database + .players + .update_one( + doc! { "uuid": uuid.clone() }, + doc! { "$set": { "online": false } }, + ) + .run()?; + + Ok(Response::Player( + crate::response::PlayerResponse::NonSaturnPlayer(uuid), + )) +} diff --git a/src/response.rs b/src/response.rs index 5e15bb3..7578bc6 100644 --- a/src/response.rs +++ b/src/response.rs @@ -70,6 +70,7 @@ pub enum Error { InvalidHandshake, DatabaseError, SomethingWentWrong, + Timeout, } impl std::fmt::Display for Error { @@ -83,6 +84,7 @@ impl std::fmt::Display for Error { Error::InvalidHandshake => write!(f, "Invalid handshake"), Error::DatabaseError => write!(f, "Database error"), Error::SomethingWentWrong => write!(f, "Something went wrong"), + Error::Timeout => write!(f, "Timeout"), } } }