use std::{ ffi::{CStr, c_char}, fs::File, }; use bincode::{Decode, Encode}; #[derive(Decode, Encode, Clone)] #[repr(C)] pub struct PlayerStat { kills: u32, deaths: u32, team_damage: u32, } #[derive(Decode, Encode, Clone, Default)] #[repr(C)] pub struct ItemStat { item_name: String, item_count: u32, } #[derive(Decode, Encode, Clone)] #[repr(C)] pub struct Player { player_id: String, player_stats: PlayerStat, player_items: [ItemStat; 256], } impl Default for Player { fn default() -> Self { Self { player_id: String::new(), player_stats: PlayerStat { kills: 0, deaths: 0, team_damage: 0, }, player_items: std::array::from_fn(|_| ItemStat::default()), } } } #[unsafe(no_mangle)] /// # Safety /// `player_id` must be a valid, null-terminated C string pointer. pub unsafe extern "C" fn get_player_stats(player_id: *const c_char) -> *const Player { let player_id = unsafe { CStr::from_ptr(player_id) } .to_string_lossy() .into_owned(); let db_location = "./stats.data"; let player_stats: Vec = match File::open(db_location) { Ok(mut data) => bincode::decode_from_std_read(&mut data, bincode::config::standard()) .unwrap_or_default(), Err(_) => Vec::new(), }; player_stats .iter() .find(|p| p.player_id == player_id) .unwrap_or(&Player::default()) } #[unsafe(no_mangle)] /// # Safety /// `player` must be a valid pointer to a `Player` struct. pub unsafe extern "C" fn save_player_stats(player: *const Player) -> bool { let player = unsafe { &*player }; let db_location = "./stats.data"; let mut player_stats: Vec = match File::open(db_location) { Ok(mut data) => bincode::decode_from_std_read(&mut data, bincode::config::standard()) .unwrap_or_default(), Err(_) => Vec::new(), }; // Find and update existing player or add new player if let Some(existing_player) = player_stats .iter_mut() .find(|p| p.player_id == player.player_id) { *existing_player = player.clone(); } else { player_stats.push(player.clone()); } // Save updated stats match File::create(db_location) { Ok(mut file) => { bincode::encode_into_std_write(&player_stats, &mut file, bincode::config::standard()) .is_ok() } Err(_) => false, } }