use data::tables::{AvatarBaseID, ProcedureConfigID}; use proto::{DisconnectReason, DisconnectScNotify, PlayerSyncScNotify}; use crate::ServerState; use super::ArgSlice; pub async fn avatar( args: ArgSlice<'_>, state: &ServerState, ) -> Result<String, Box<dyn std::error::Error>> { const USAGE: &str = "Usage: player avatar [player_uid] [avatar_id]"; if args.len() != 2 { return Ok(USAGE.to_string()); } let uid = args[0].parse::<u32>()?; let avatar_id = args[1].parse::<u32>()?; let Some(avatar_id) = AvatarBaseID::new(avatar_id) else { return Ok(format!("avatar with id {avatar_id} doesn't exist")); }; let Some(player_lock) = state.player_mgr.get_player(uid).await else { return Ok(String::from("player not found")); }; let should_save = { let mut player = player_lock.lock().await; player.basic_data_model.frontend_avatar_id = Some(avatar_id); player.current_session_id().is_none() }; if should_save { state.player_mgr.save_and_remove(uid).await; } Ok(format!( "changed frontend_avatar_id, you have to re-enter main city now" )) } pub async fn nickname( args: ArgSlice<'_>, state: &ServerState, ) -> Result<String, Box<dyn std::error::Error>> { const USAGE: &str = "Usage: player nickname [uid] [nickname]"; if args.len() != 2 { return Ok(USAGE.to_string()); } let uid = args[0].parse::<u32>()?; let nickname = args[1].trim(); if !matches!(nickname.len(), (4..=15)) { return Ok(String::from( "nickname should contain at least 4 and not more than 15 characters", )); } let Some(player_lock) = state.player_mgr.get_player(uid).await else { return Ok(String::from("player not found")); }; let (session_id, basic_info) = { let mut player = player_lock.lock().await; player.basic_data_model.nick_name = Some(nickname.to_string()); ( player.current_session_id(), player.basic_data_model.player_basic_info(), ) }; if let Some(session) = session_id.map(|id| state.session_mgr.get(id)).flatten() { session .notify(PlayerSyncScNotify { basic_info: Some(basic_info), ..Default::default() }) .await?; } else { state.player_mgr.save_and_remove(uid).await; } Ok(format!( "successfully changed player {uid} nickname to {nickname}" )) } pub async fn procedure( args: ArgSlice<'_>, state: &ServerState, ) -> Result<String, Box<dyn std::error::Error>> { const USAGE: &str = "Usage: player procedure [uid] [procedure_id]"; if args.len() != 2 { return Ok(USAGE.to_string()); } let uid = args[0].parse::<u32>()?; let procedure_id = args[1].parse::<i32>()?; let procedure_id = match procedure_id { 1.. => ProcedureConfigID::new(procedure_id as u32), _ => None, }; let Some(player_lock) = state.player_mgr.get_player(uid).await else { return Ok(String::from("player not found")); }; let session_id = { let mut player = player_lock.lock().await; player.basic_data_model.beginner_procedure_id = procedure_id; player.current_session_id() }; if session_id.is_none() { state.player_mgr.save_and_remove(uid).await; } Ok(format!( "successfully changed procedure_id to {procedure_id:?}" )) } pub async fn kick( args: ArgSlice<'_>, state: &ServerState, ) -> Result<String, Box<dyn std::error::Error>> { const USAGE: &str = "Usage: player kick [player_uid]"; if args.len() > 2 { return Ok(USAGE.to_string()); } let uid = args[0].parse::<u32>()?; let reason = match args.get(1) { Some(arg) => match arg.parse::<i32>() { Ok(val) => val, Err(_err) => 1, }, None => 1, }; let reason_str = match DisconnectReason::try_from(reason) { Ok(converted_enum) => converted_enum.as_str_name().to_owned(), Err(_err) => reason.to_string(), }; let Some(player_lock) = state.player_mgr.get_player(uid).await else { return Ok(String::from("player not found")); }; let session_id = player_lock.lock().await.current_session_id(); if let Some(session) = session_id.map(|id| state.session_mgr.get(id)).flatten() { session .notify(DisconnectScNotify { reason: reason }) .await?; tokio::time::sleep(tokio::time::Duration::from_millis(50)).await; session.shutdown().await?; Ok(format!("kicked player, uid: {uid}, reason: {reason_str}")) } else { Ok(format!("player uid: {uid} is not online yet.")) } }