diff --git a/README.md b/README.md index 3c62697..a8bed3c 100644 --- a/README.md +++ b/README.md @@ -84,3 +84,14 @@ No additional data required |has_player| 1 | If the server has a player, this variable will be `0xFF`. or else, it will be `0x00`| |player_id | 4 | The server's current player ID. If the server has no player, this will always be `0x00000000`| +### `PLAYER_UPDATE_TIMESTAMP` (0x05) +Get the time of the last player update, The time is represented as seconds since the last update + +#### Request +No additional data required + +#### Response + +| Name | Size (bytes) | Description | +|----------|--------------|-------------| +|timestamp | 8 | Seconds since the last player update | diff --git a/src/jobs.rs b/src/jobs.rs index 375c6fa..100a22f 100644 --- a/src/jobs.rs +++ b/src/jobs.rs @@ -1,6 +1,6 @@ use futures::SinkExt; use rquickjs::{async_with, AsyncContext, AsyncRuntime}; -use std::{num::NonZeroUsize, sync::Arc, thread::available_parallelism}; +use std::{num::NonZeroUsize, sync::Arc, thread::available_parallelism, time::SystemTime}; use tokio::{runtime::Handle, sync::Mutex, task::block_in_place}; use tub::Pool; @@ -12,6 +12,7 @@ pub enum JobOpcode { DecryptSignature, GetSignatureTimestamp, PlayerStatus, + PlayerUpdateTimestamp, UnknownOpcode, } @@ -23,6 +24,7 @@ impl std::fmt::Display for JobOpcode { Self::DecryptSignature => write!(f, "DecryptSignature"), Self::GetSignatureTimestamp => write!(f, "GetSignatureTimestamp"), Self::PlayerStatus => write!(f, "PlayerStatus"), + Self::PlayerUpdateTimestamp => write!(f, "PlayerUpdateTimestamp"), Self::UnknownOpcode => write!(f, "UnknownOpcode"), } } @@ -35,6 +37,7 @@ impl From for JobOpcode { 0x02 => Self::DecryptSignature, 0x03 => Self::GetSignatureTimestamp, 0x04 => Self::PlayerStatus, + 0x05 => Self::PlayerUpdateTimestamp, _ => Self::UnknownOpcode, } } @@ -47,6 +50,7 @@ pub struct PlayerInfo { pub signature_timestamp: u64, pub player_id: u32, pub has_player: u8, + pub last_update: SystemTime, } pub struct JavascriptInterpreter { @@ -106,6 +110,7 @@ impl GlobalState { player_id: Default::default(), signature_timestamp: Default::default(), has_player: 0x00, + last_update: SystemTime::now(), }), js_runtime_pool: runtime_pool, } @@ -129,10 +134,7 @@ pub async fn process_fetch_update( opcode: JobOpcode::ForceUpdate, request_id, update_status: status, - signature: Default::default(), - signature_timestamp: Default::default(), - has_player: Default::default(), - player_id: Default::default(), + ..Default::default() }) .await; } @@ -171,11 +173,7 @@ pub async fn process_decrypt_n_signature( let _ = writer.send(OpcodeResponse { opcode: JobOpcode::DecryptNSignature, request_id, - update_status: Ok(Default::default()), - signature: String::new(), - signature_timestamp: Default::default(), - has_player: Default::default(), - player_id: Default::default(), + ..Default::default() }).await; return; } @@ -203,11 +201,7 @@ pub async fn process_decrypt_n_signature( let _ = writer.send(OpcodeResponse { opcode: JobOpcode::DecryptNSignature, request_id, - update_status: Ok(Default::default()), - signature: String::new(), - signature_timestamp: Default::default(), - has_player: Default::default(), - player_id: Default::default(), + ..Default::default() }).await; return; } @@ -218,11 +212,8 @@ pub async fn process_decrypt_n_signature( let _ = writer.send(OpcodeResponse { opcode: JobOpcode::DecryptNSignature, request_id, - update_status: Ok(Default::default()), signature: decrypted_string, - signature_timestamp: Default::default(), - has_player: Default::default(), - player_id: Default::default(), + ..Default::default() }).await; }) .await; @@ -261,11 +252,7 @@ pub async fn process_decrypt_signature( let _ = writer.send(OpcodeResponse { opcode: JobOpcode::DecryptSignature, request_id, - update_status: Ok(Default::default()), - signature: String::new(), - signature_timestamp: Default::default(), - has_player: Default::default(), - player_id: Default::default(), + ..Default::default() }).await; return; } @@ -296,11 +283,7 @@ pub async fn process_decrypt_signature( let _ = writer.send(OpcodeResponse { opcode: JobOpcode::DecryptSignature, request_id, - update_status: Ok(Default::default()), - signature: String::new(), - signature_timestamp: Default::default(), - has_player: Default::default(), - player_id: Default::default(), + ..Default::default() }).await; return; } @@ -311,11 +294,8 @@ pub async fn process_decrypt_signature( let _ = writer.send(OpcodeResponse { opcode: JobOpcode::DecryptSignature, request_id, - update_status: Ok(Default::default()), signature: decrypted_string, - signature_timestamp: Default::default(), - has_player: Default::default(), - player_id: Default::default(), + ..Default::default() }).await; }) .await; @@ -339,11 +319,8 @@ pub async fn process_get_signature_timestamp( .send(OpcodeResponse { opcode: JobOpcode::GetSignatureTimestamp, request_id, - update_status: Ok(Default::default()), - signature: String::new(), signature_timestamp: timestamp, - has_player: Default::default(), - player_id: Default::default(), + ..Default::default() }) .await; } @@ -368,11 +345,38 @@ pub async fn process_player_status( .send(OpcodeResponse { opcode: JobOpcode::PlayerStatus, request_id, - update_status: Ok(Default::default()), - signature: String::new(), - signature_timestamp: Default::default(), has_player, player_id, + ..Default::default() + }) + .await; +} + +pub async fn process_player_update_timestamp( + state: Arc, + stream: Arc>, + request_id: u32, +) where + W: SinkExt + Unpin + Send, +{ + let cloned_writer = stream.clone(); + let global_state = state.clone(); + + let player_info = global_state.player_info.lock().await; + let last_update = player_info.last_update; + + let mut writer = cloned_writer.lock().await; + + let _ = writer + .send(OpcodeResponse { + opcode: JobOpcode::PlayerUpdateTimestamp, + request_id, + last_player_update: SystemTime::now() + .duration_since(last_update) + .unwrap() + .as_secs(), + + ..Default::default() }) .await; } diff --git a/src/main.rs b/src/main.rs index 79da8b0..f6f57b2 100644 --- a/src/main.rs +++ b/src/main.rs @@ -18,6 +18,7 @@ use tokio_util::codec::Framed; use crate::jobs::{ process_decrypt_signature, process_get_signature_timestamp, process_player_status, + process_player_update_timestamp, }; macro_rules! break_fail { @@ -152,6 +153,18 @@ async fn process_socket(state: Arc, socket: UnixStream) { .await; }); } + JobOpcode::PlayerUpdateTimestamp => { + let cloned_state = state.clone(); + let cloned_sink = arc_sink.clone(); + tokio::spawn(async move { + process_player_update_timestamp( + cloned_state, + cloned_sink, + opcode.request_id, + ) + .await; + }); + } _ => { continue; } diff --git a/src/opcode.rs b/src/opcode.rs index 5cdd1ed..f84b28f 100644 --- a/src/opcode.rs +++ b/src/opcode.rs @@ -27,8 +27,23 @@ pub struct OpcodeResponse { pub has_player: u8, pub player_id: u32, + pub last_player_update: u64, } +impl Default for OpcodeResponse { + fn default() -> Self { + OpcodeResponse { + opcode: JobOpcode::ForceUpdate, + request_id: 0, + update_status: Ok(()), + signature: String::new(), + signature_timestamp: 0, + has_player: 0, + player_id: 0, + last_player_update: 0, + } + } +} impl Decoder for OpcodeDecoder { type Item = Opcode; type Error = std::io::Error; @@ -47,7 +62,10 @@ impl Decoder for OpcodeDecoder { let request_id: u32 = u32::from_be_bytes(src[1..5].try_into().unwrap()); match opcode { - JobOpcode::ForceUpdate | JobOpcode::GetSignatureTimestamp | JobOpcode::PlayerStatus => { + JobOpcode::ForceUpdate + | JobOpcode::GetSignatureTimestamp + | JobOpcode::PlayerStatus + | JobOpcode::PlayerUpdateTimestamp => { src.advance(5); Ok(Some(Opcode { opcode, @@ -123,6 +141,10 @@ impl Encoder for OpcodeDecoder { dst.put_u8(item.has_player); dst.put_u32(item.player_id); } + JobOpcode::PlayerUpdateTimestamp => { + dst.put_u32(8); + dst.put_u64(item.last_player_update); + } _ => {} } Ok(()) diff --git a/src/player.rs b/src/player.rs index 92fbb6a..7d54565 100644 --- a/src/player.rs +++ b/src/player.rs @@ -1,4 +1,4 @@ -use std::sync::Arc; +use std::{sync::Arc, time::SystemTime}; use regex::Regex; @@ -180,6 +180,7 @@ pub async fn fetch_update(state: Arc) -> Result<(), FetchUpdateStat current_player_info.sig_function_name = sig_function_name.to_string(); current_player_info.signature_timestamp = signature_timestamp; current_player_info.has_player = 0xFF; + current_player_info.last_update = SystemTime::now(); Ok(()) }