use std::collections::HashMap;
use steam_enums::EMsg;
use steamid::SteamID;
use crate::{error::SteamError, SteamClient};
#[derive(Debug, Clone, Default)]
pub struct RichPresenceData {
pub steam_id: SteamID,
pub appid: u32,
pub data: HashMap<String, String>,
}
impl SteamClient {
pub async fn upload_rich_presence(&mut self, appid: u32, data: &HashMap<String, String>) -> Result<(), SteamError> {
if !self.is_logged_in() {
return Err(SteamError::NotLoggedOn);
}
let kv_bytes = encode_rich_presence_kv(data);
self.session_recovery.record_rich_presence(appid, data.clone());
let msg = steam_protos::CMsgClientRichPresenceUpload { rich_presence_kv: Some(kv_bytes), ..Default::default() };
self.send_message_with_routing(EMsg::ClientRichPresenceUpload, appid, &msg).await
}
pub async fn request_rich_presence(&mut self, appid: u32, steam_ids: &[SteamID]) -> Result<(), SteamError> {
if !self.is_logged_in() {
return Err(SteamError::NotLoggedOn);
}
if steam_ids.is_empty() {
return Ok(());
}
let msg = steam_protos::CMsgClientRichPresenceRequest { steamid_request: steam_ids.iter().map(|sid| sid.steam_id64()).collect() };
self.send_message_with_routing(EMsg::ClientRichPresenceRequest, appid, &msg).await
}
pub async fn get_app_rich_presence_localization(&mut self, appid: i32, language: &str) -> Result<(), SteamError> {
if !self.is_logged_in() {
return Err(SteamError::NotLoggedOn);
}
let request = steam_protos::CCommunityGetAppRichPresenceLocalizationRequest { appid: Some(appid), language: Some(language.to_string()) };
self.send_service_method("Community.GetAppRichPresenceLocalization#1", &request).await
}
pub async fn clear_rich_presence(&mut self, appid: u32) -> Result<(), SteamError> {
self.upload_rich_presence(appid, &HashMap::new()).await
}
}
fn encode_rich_presence_kv(data: &HashMap<String, String>) -> Vec<u8> {
let mut buf = Vec::with_capacity(1024);
buf.push(0x00);
buf.extend_from_slice(b"RP\0");
for (key, value) in data {
buf.push(0x01); buf.extend_from_slice(key.as_bytes());
buf.push(0x00); buf.extend_from_slice(value.as_bytes());
buf.push(0x00); }
buf.push(0x08);
buf.push(0x08);
buf
}
pub fn parse_rich_presence_kv(data: &[u8]) -> HashMap<String, String> {
let mut result = HashMap::new();
if data.is_empty() {
return result;
}
let mut i = 0;
if i < data.len() && data[i] == 0x00 {
i += 1;
}
while i < data.len() && data[i] != 0x00 {
i += 1;
}
if i < data.len() {
i += 1; }
while i < data.len() {
let type_byte = data[i];
i += 1;
if type_byte == 0x08 {
break;
}
if type_byte != 0x01 {
continue;
}
let key_start = i;
while i < data.len() && data[i] != 0x00 {
i += 1;
}
let key = String::from_utf8_lossy(&data[key_start..i]).to_string();
if i < data.len() {
i += 1; }
let value_start = i;
while i < data.len() && data[i] != 0x00 {
i += 1;
}
let value = String::from_utf8_lossy(&data[value_start..i]).to_string();
if i < data.len() {
i += 1; }
result.insert(key, value);
}
result
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_encode_decode_rich_presence() {
let mut data = HashMap::new();
data.insert("status".to_string(), "In Menu".to_string());
data.insert("connect".to_string(), "+connect 1.2.3.4:27015".to_string());
let encoded = encode_rich_presence_kv(&data);
let decoded = parse_rich_presence_kv(&encoded);
assert_eq!(decoded.get("status"), Some(&"In Menu".to_string()));
assert_eq!(decoded.get("connect"), Some(&"+connect 1.2.3.4:27015".to_string()));
}
#[test]
fn test_empty_rich_presence() {
let data = HashMap::new();
let encoded = encode_rich_presence_kv(&data);
let decoded = parse_rich_presence_kv(&encoded);
assert!(decoded.is_empty());
}
}