use std::{ cell::{Cell, RefCell}, rc::{Rc, Weak}, sync::{self, mpsc::Receiver, Arc}, }; use common::{net::tools::Packet, tools::time_utils}; use data::{ get_default_world_main_scene_id, math_def::Vector3, prop_type::{ PROP_CUR_PERSIST_STAMINA, PROP_IS_FLYABLE, PROP_IS_GAME_TIME_LOCKED, PROP_IS_SPRING_AUTO_USE, PROP_IS_TRANSFERABLE, PROP_IS_WEATHER_LOCKED, PROP_MAX_STAMINA, PROP_PLAYER_EXP, PROP_PLAYER_HCOIN, PROP_PLAYER_LEVEL, PROP_PLAYER_SCOIN, PROP_SPRING_AUTO_USE_PERCENT, }, AvatarUseType, }; use prost::Message; use proto::{ AbilitySyncStateInfo, AvatarEnterSceneInfo, CmdID, DoSetPlayerBornDataNotify, EnterSceneDoneReq, EnterSceneDoneRsp, EnterScenePeerNotify, EnterSceneReadyReq, EnterType, MpLevelEntityInfo, PacketHead, PingReq, PingRsp, PlayerData, PlayerDataBin, PlayerDataNotify, PlayerEnterSceneInfoNotify, PlayerEnterSceneNotify, PlayerLoginRsp, PostEnterSceneReq, PostEnterSceneRsp, Retcode, SavePlayerDataReq, SceneInitFinishReq, SetPlayerBornDataReq, TeamEnterSceneInfo, VisionType, YSMessage, }; use rand::RngCore; use crate::{ creature::{property::append_int_prop_value, Creature, LifeState}, entity::{Entity, VisionContext}, gadget::WeaponGadget, gameserver_service, handler, network::PlayerSessionProxy, scene::{EnterSceneState, PlayerSceneComp, Scene}, }; use super::{ avatar::PlayerAvatarComp, basic::PlayerBasicComp, item::PlayerItemComp, PlayerCoroExec, PlayerLoginComp, }; pub struct Player { pub uid: u32, session: RefCell>, pub player_basic_comp: Rc>, pub player_login_comp: Rc>, pub player_avatar_comp: Rc>, pub player_scene_comp: Rc>, pub player_item_comp: Rc>, pub peer_id: Cell, last_recv_ping_time: Cell, last_save_time: Cell, is_coro_stopping: Cell, } impl Player { pub fn construct_player(uid: u32) -> Self { Self { uid, session: RefCell::new(sync::Weak::new()), player_basic_comp: Rc::new(RefCell::new(PlayerBasicComp::default())), player_login_comp: Rc::new(RefCell::new(PlayerLoginComp::default())), player_avatar_comp: Rc::new(RefCell::new(PlayerAvatarComp::default())), player_scene_comp: Rc::new(RefCell::new(PlayerSceneComp::default())), player_item_comp: Rc::new(RefCell::new(PlayerItemComp::default())), peer_id: Cell::new(0), last_recv_ping_time: Cell::new(0), last_save_time: Cell::new(0), is_coro_stopping: Cell::new(false), } } pub fn login( &self, is_new_player: bool, is_relogin: bool, target_uid: u32, target_home_owner_uid: u32, is_client_relogin: bool, ) { tracing::info!("Player::login uid:{} is_new_player:{} is_relogin:{} target_uid:{} is_client_relogin:{}", self.uid, is_new_player, is_relogin, target_uid, is_client_relogin); self.update_last_ping_time(); self.player_scene_comp.borrow_mut().pre_login(is_relogin); if is_new_player { self.first_login(); } // TODO: checkLuaShellOnLogin self.player_basic_comp.borrow_mut().on_login(is_new_player); self.player_login_comp.borrow_mut().on_login(is_new_player); self.player_item_comp.borrow_mut().on_login(is_new_player); self.player_avatar_comp.borrow_mut().on_login(is_new_player); self.player_scene_comp.borrow_mut().on_login(is_new_player); // TODO: PlayerSceneComp::afterLogin if self.player_avatar_comp.borrow().get_avatar_count() == 0 { self.send_proto(DoSetPlayerBornDataNotify::default()); } else { self.init_and_begin_enter_scene( false, target_uid, target_home_owner_uid, is_client_relogin, ); } } fn first_login(&self) { self.player_basic_comp.borrow_mut().on_first_login(); self.player_avatar_comp.borrow_mut().on_first_login(); self.player_scene_comp.borrow_mut().on_first_login(); } pub fn block_stop_coroutine(&self) -> Result<(), ()> { if self.is_coro_stopping.get() { tracing::error!("block_stop_coroutine called repeated, uid:{}", self.uid); return Err(()); } self.is_coro_stopping.set(true); Ok(()) } fn coroutine_loop(uid: u32, rx: Receiver, session: Arc) { let player = Rc::new(Self::construct_player(uid)); player.set_session(session, false); loop { if player.is_coro_stopping.get() { break; } let packet = rx.recv().unwrap(); match packet.cmd_id { PlayerLoginRsp::CMD_ID => { let rsp: PlayerLoginRsp = packet.get_proto().unwrap(); let player_data = PlayerData::decode(rsp.player_data.as_ref()).unwrap(); player.from_bin(player_data).unwrap(); player.init(); player.login(rsp.is_new_player, false, 0, 0, false); let rsp = PlayerLoginRsp { player_data: Vec::with_capacity(0), ..rsp }; player.send_proto(rsp); // First post-login save player.save_to_db(); player.last_save_time.set(time_utils::get_now()); } _ => { let _ = handler::process_packet(&player, packet); } } // TODO: configurable save time? if time_utils::get_now() - player.last_save_time.get() > 30 { player.save_to_db(); player.last_save_time.set(time_utils::get_now()); } } } pub fn start(session: Arc, uid: u32) -> PlayerCoroExec { PlayerCoroExec::start(move |rx| Self::coroutine_loop(uid, rx, session.clone())) } pub fn set_session(&self, session: Arc, _is_relogin: bool) { *self.session.borrow_mut() = Arc::downgrade(&session); } pub fn send_proto(&self, proto: impl YSMessage) { self.session .borrow() .upgrade() .unwrap() .send_packet(Packet { cmd_id: proto.get_cmd_id(), head: PacketHead::default(), recv_vec: proto.encode_to_vec(), }); } pub fn on_ping_req(&self, req: PingReq, rsp: &mut PingRsp) { tracing::info!("[PING] uid:{}, seq:{}", self.uid, req.seq); rsp.retcode = Retcode::RetSucc.into(); rsp.seq = req.seq; rsp.client_time = req.client_time; self.update_last_ping_time(); } pub fn set_player_born_data(&self, req: SetPlayerBornDataReq) -> Result<(), Retcode> { if self.player_avatar_comp.borrow().get_avatar_count() != 0 { return Err(Retcode::RetRepeatSetPlayerBornData); } self.player_basic_comp .borrow() .check_nick_name(&req.nick_name)?; if self .player_avatar_comp .borrow_mut() .set_born_avatar(req.avatar_id) .is_err() { return Err(Retcode::RetAvatarIdError); } self.player_basic_comp.borrow_mut().nickname = req.nick_name; self.init_and_begin_enter_scene(false, 0, 0, false); Ok(()) } pub fn check_is_in_safe_state_for_change_team(&self) -> Result<(), Retcode> { Ok(()) } pub fn init_and_begin_enter_scene( &self, _is_relogin: bool, _target_uid: u32, _target_home_owner_uid: u32, _is_client_relogin: bool, ) { // Unlock all stuff here // TODO: MUIP/GM for this self.player_avatar_comp.borrow_mut().is_flyable = true; self.player_avatar_comp.borrow_mut().is_transferable = true; self.player_avatar_comp.borrow_mut().is_spring_auto_use = true; self.player_avatar_comp.borrow_mut().spring_auto_use_percent = 50; for open_state in data::iter_open_state_data() { self.player_basic_comp .borrow_mut() .open_state_map .insert(open_state.id, 1); } for avatar in data::iter_avatar_data() { if avatar.use_type == AvatarUseType::AvatarFormal { if !self.player_avatar_comp.borrow().has_avatar(avatar.id, 1) { self.player_avatar_comp .borrow_mut() .add_formal_avatar(avatar.id, false, 1, 0) .unwrap(); } } } self.notify_all_data(); let cur_scene_id = get_default_world_main_scene_id(); let cur_scene = { let mut scene_comp = self.player_scene_comp.borrow_mut(); scene_comp.dest_world_ptr = Some(scene_comp.own_world.clone()); let dest_world = scene_comp.own_world.borrow(); let cur_scene = dest_world.scene_map.get(&cur_scene_id).unwrap().clone(); cur_scene.borrow_mut().base_mut().cur_world_wtr = Rc::downgrade(&scene_comp.own_world); cur_scene }; let (pos, rot) = { let cur_scene = cur_scene.borrow_mut(); cur_scene.base().get_last_owner_location() }; self.begin_enter_scene( cur_scene, pos, rot, EnterType::EnterSelf, VisionType::VisionMeet, ); } fn begin_enter_scene( &self, dest_scene_ptr: Rc>, pos: Vector3, rot: Vector3, enter_type: EnterType, vision_type: VisionType, ) { let mut scene_comp = self.player_scene_comp.borrow_mut(); if scene_comp.enter_scene_state != EnterSceneState::ENTER_SCENE_NONE && scene_comp.enter_scene_state != EnterSceneState::ENTER_SCENE_POST { tracing::warn!( "begin_enter_scene with last one not finish, uid:{}", self.uid ); } self.mark_destination( &dest_scene_ptr, pos.clone(), rot, enter_type, vision_type, &mut scene_comp, ); let enter_scene_token = match scene_comp.enter_scene_token { 0 => rand::thread_rng().next_u32() & 0xFFFF, // OG: gettid lmfao _ => scene_comp.enter_scene_token.wrapping_add(100), }; scene_comp.enter_scene_token = enter_scene_token; let scene = dest_scene_ptr.borrow(); let scene_id = scene.base().scene_id; let notify = PlayerEnterSceneNotify { scene_id, pos: Some(pos.to_client()), scene_transaction: scene.base().scene_transaction.clone(), enter_scene_token, scene_begin_time: scene.base().begin_time_ms, r#type: enter_type.into(), target_uid: self.uid, ..Default::default() }; scene_comp.enter_scene_state = EnterSceneState::ENTER_SCENE_NOTIFY; tracing::info!("[EnterScene] BeginEnterScene uid:{} enter_type:{:?} token:{} dest_scene_id:{} pos:{:?} target_uid:{}", self.uid, enter_type, notify.enter_scene_token, scene_id, pos, 0); self.send_proto(notify); } pub fn enter_scene_ready(self: &Rc, req: &EnterSceneReadyReq) -> Result<(), Retcode> { { let scene_comp = self.player_scene_comp.borrow(); if scene_comp.enter_scene_token != req.enter_scene_token { tracing::info!( "[EnterScene] token not match, client_token:{} server_token:{}", req.enter_scene_token, scene_comp.enter_scene_token ); return Err(Retcode::RetEnterSceneTokenInvalid); } if scene_comp.enter_scene_state != EnterSceneState::ENTER_SCENE_NOTIFY { tracing::warn!("EnterSceneState is not EnterSceneNotify, uid:{}", self.uid); return Err(Retcode::RetFail); } }; self.pre_enter_scene()?; let mut scene_comp = self.player_scene_comp.borrow_mut(); let dest_scene = scene_comp.get_dest_scene().unwrap(); let dest_scene = dest_scene.borrow(); let scene = dest_scene.base(); let notify = EnterScenePeerNotify { enter_scene_token: scene_comp.enter_scene_token, dest_scene_id: scene_comp.dest_scene_id, host_peer_id: scene.get_host_peer_id().unwrap_or_default(), peer_id: scene.get_peer_id(self.uid).unwrap_or_default(), }; tracing::info!( "[EnterScene] EnterSceneReady uid:{}, dest_scene_id:{}, peer_id:{}, host_peer_id:{}", self.uid, notify.dest_scene_id, notify.peer_id, notify.host_peer_id ); scene_comp.enter_scene_state = EnterSceneState::ENTER_SCENE_READY; self.send_proto(notify); Ok(()) } pub fn scene_init_finish(self: &Rc, req: &SceneInitFinishReq) -> Result<(), Retcode> { let scene_comp = self.player_scene_comp.borrow(); if scene_comp.enter_scene_token != req.enter_scene_token { tracing::warn!( "[ENTER_SCENE] token not match, client_token:{} server_token:{}", req.enter_scene_token, scene_comp.enter_scene_token ); return Err(Retcode::RetEnterSceneTokenInvalid); } if scene_comp.enter_scene_state != EnterSceneState::ENTER_SCENE_READY { tracing::warn!("EnterSceneState is not EnterSceneReady, uid:{}", self.uid); return Err(Retcode::RetFail); } let dest_pos = scene_comp.dest_pos.clone(); let dest_rot = scene_comp.dest_rot.clone(); let enter_type = scene_comp.dest_enter_type; let vision_type = scene_comp.dest_vision_type; let is_need_enter_world = { let Some(dest_scene) = scene_comp.get_dest_scene() else { tracing::error!("get_dest_scene failed, uid:{}", self.uid); return Err(Retcode::RetFail); }; let is_reenter_dungeon = match scene_comp.get_cur_scene() { Some(_) => false, None => matches!(*dest_scene.borrow(), Scene::Dungeon(_)), }; !is_reenter_dungeon }; drop(scene_comp); if is_need_enter_world { let (world_owner_uid, dest_world_ptr) = { let scene_comp = self.player_scene_comp.borrow(); let owner_uid = scene_comp .dest_world_ptr .as_ref() .unwrap() .borrow() .get_owner_uid(); let dest_world_ptr = scene_comp.dest_world_ptr.clone().unwrap(); (owner_uid, dest_world_ptr) }; let mut dest_world = dest_world_ptr.borrow_mut(); if let Err(ret) = dest_world.player_enter(self) { tracing::info!( "uid: {} player_enter world fails, owner_uid: {}", self.uid, world_owner_uid ); return Err(ret); } } let dest_scene = self.player_scene_comp.borrow().get_dest_scene(); self.player_enter_scene(dest_scene, dest_pos, dest_rot, enter_type, vision_type)?; self.player_scene_comp.borrow_mut().enter_scene_state = EnterSceneState::ENTER_SCENE_INIT; Ok(()) } pub fn enter_scene_done(self: &Rc, req: &EnterSceneDoneReq) -> Result<(), Retcode> { let mut scene_comp = self.player_scene_comp.borrow_mut(); if scene_comp.enter_scene_token != req.enter_scene_token { tracing::warn!( "[ENTER_SCENE] token not match, client_token:{} server_token:{}", req.enter_scene_token, scene_comp.enter_scene_token ); return Err(Retcode::RetEnterSceneTokenInvalid); } if scene_comp.enter_scene_state != EnterSceneState::ENTER_SCENE_INIT { tracing::warn!("EnterSceneState is not EnterSceneInit, uid:{}", self.uid); return Err(Retcode::RetFail); } let Some(cur_scene) = scene_comp.get_cur_scene() else { tracing::warn!("get_cur_scene is None, uid:{}", self.uid); return Err(Retcode::RetFail); }; let vision_type = scene_comp.dest_vision_type; let dest_pos = scene_comp.dest_pos.clone(); let dest_rot = scene_comp.dest_rot.clone(); let Some(cur_avatar_ptr) = ({ let avatar_comp = self.player_avatar_comp.borrow(); avatar_comp.get_cur_avatar() }) else { tracing::warn!("[EnterScene] get_cur_avatar failed, uid:{}", self.uid); return Err(Retcode::RetFail); }; { let mut cur_avatar = cur_avatar_ptr.borrow_mut(); if cur_avatar.get_life_state() == LifeState::LifeDead { cur_avatar.set_life_state(LifeState::LifeAlive); if cur_avatar.get_cur_hp() == 0.0 { cur_avatar.set_cur_hp(1.0); } } cur_avatar.set_position(dest_pos.clone()); cur_avatar.set_rotation(dest_rot.clone()); cur_avatar.set_last_valid_position(dest_pos); cur_avatar.set_last_valid_rotation(dest_rot); } let vision_context = VisionContext::new(vision_type); if cur_scene .borrow_mut() .entity_appear(&(cur_avatar_ptr as Rc>), vision_context) .is_err() { tracing::warn!("[EnterScene] entity_appear failed, uid:{}", self.uid); return Err(Retcode::RetFail); } scene_comp.enter_scene_state = EnterSceneState::ENTER_SCENE_DONE; // TODO: scene_comp.notify_current_weather(); // Why here but ok.. self.send_proto(EnterSceneDoneRsp { retcode: Retcode::RetSucc.into(), enter_scene_token: req.enter_scene_token, }); tracing::info!("[EnterScene] enter_scene_done succ, uid:{}", self.uid); Ok(()) } pub fn post_enter_scene(self: &Rc, req: &PostEnterSceneReq) -> Result<(), Retcode> { { let scene_comp = self.player_scene_comp.borrow(); if scene_comp.enter_scene_token != req.enter_scene_token { tracing::warn!( "[ENTER_SCENE] token not match, client_token:{} server_token:{}", req.enter_scene_token, scene_comp.enter_scene_token ); return Err(Retcode::RetEnterSceneTokenInvalid); } if scene_comp.enter_scene_state != EnterSceneState::ENTER_SCENE_DONE { tracing::warn!("EnterSceneState is not EnterSceneDone, uid:{}", self.uid); return Err(Retcode::RetFail); } if scene_comp.get_cur_scene().is_none() { tracing::warn!("get_cur_scene is None, uid:{}", self.uid); return Err(Retcode::RetFail); }; } self.clear_destination(); self.player_scene_comp.borrow_mut().enter_scene_state = EnterSceneState::ENTER_SCENE_POST; // TODO: self.player_basic_comp.borrow_mut().is_in_transfer = false; tracing::info!("[EnterScene] post_enter_scene succ, uid:{}", self.uid); self.send_proto(PostEnterSceneRsp { retcode: Retcode::RetSucc.into(), enter_scene_token: req.enter_scene_token, }); Ok(()) } fn player_enter_scene( self: &Rc, dest_scene_ptr: Option>>, pos: Vector3, rot: Vector3, _enter_type: EnterType, _vision_type: VisionType, ) -> Result<(), Retcode> { let Some(dest_scene_ptr) = dest_scene_ptr else { tracing::error!("dest_scene_ptr is None, uid: {}", self.uid); return Err(Retcode::RetFail); }; let cur_avatar_team = self.init_enter_scene_avatar(&mut dest_scene_ptr.borrow_mut())?; let (cur_avatar_team, appear_avatar) = { let avatar_comp = self.player_avatar_comp.borrow(); let avatar_team = avatar_comp .avatar_team_map .get(&avatar_comp.cur_avatar_team_id) .unwrap(); let cur_avatar_team = cur_avatar_team .iter() .map(|guid| avatar_comp.avatar_map.get(guid).unwrap().clone()) .collect(); let appear_avatar = avatar_comp .avatar_map .get(&avatar_team.borrow().last_cur_avatar_guid) .unwrap() .clone(); (cur_avatar_team, appear_avatar) }; if dest_scene_ptr .borrow_mut() .player_enter(self, pos, rot, false, cur_avatar_team, &appear_avatar) .is_err() { tracing::error!("player_enter fails, uid: {}", self.uid); return Err(Retcode::RetEnterSceneFail); } self.player_scene_comp.borrow_mut().cur_scene_wtr = Rc::downgrade(&dest_scene_ptr); Ok(()) } pub fn notify_player_enter_scene_info(&self, scene: &mut Scene) { use crate::entity::Entity; // TODO: OG server gets scene from PlayerSceneComp here let cur_avatar_entity_id = { let Some(cur_avatar) = self.player_avatar_comp.borrow().get_cur_avatar() else { tracing::error!("cur_avatar_ptr is None"); return; }; let cur_avatar = cur_avatar.borrow(); cur_avatar.entity_id() }; let mut notify = PlayerEnterSceneInfoNotify::default(); notify.cur_avatar_entity_id = cur_avatar_entity_id; notify.enter_scene_token = self.player_scene_comp.borrow().enter_scene_token; { let avatar_comp = self.player_avatar_comp.borrow(); for avatar in avatar_comp.get_cur_avatar_team_vec() { let avatar = avatar.borrow(); let weapon_gadget = avatar .base_avatar() .equip_comp .weapon_gadget .as_ref() .unwrap(); let weapon_gadget = weapon_gadget.borrow(); notify.avatar_enter_info.push(AvatarEnterSceneInfo { avatar_guid: avatar.base_avatar().guid, weapon_guid: match &*weapon_gadget { WeaponGadget::Avatar(gadget) => gadget.weapon_guid, //_ => 0, }, avatar_entity_id: avatar.entity_id(), weapon_entity_id: weapon_gadget.entity_id(), weapon_ability_info: Some(AbilitySyncStateInfo::default()), avatar_ability_info: Some(AbilitySyncStateInfo::default()), buff_id_list: Vec::with_capacity(0), server_buff_list: Vec::with_capacity(0), }); } let Some(avatar_team_entity) = avatar_comp.team_entity.upgrade() else { tracing::error!("team_entity is None"); return; }; let mut team_enter_info = TeamEnterSceneInfo::default(); avatar_team_entity.borrow().to_client(&mut team_enter_info); notify.team_enter_info = Some(team_enter_info); } let mp_level_entity = scene.get_or_create_mp_level_entity(); let mut mp_level_entity_info = MpLevelEntityInfo::default(); mp_level_entity .borrow() .to_client(&mut mp_level_entity_info); notify.mp_level_entity_info = Some(mp_level_entity_info); self.send_proto(notify); } fn init_enter_scene_avatar(&self, dest_scene_ptr: &mut Scene) -> Result, Retcode> { // TODO: special avatars for dungeons/challenges let avatar_comp = self.player_avatar_comp.borrow(); let cur_avatar_team = avatar_comp.get_cur_avatar_team_with_temp_avatar(); let cur_world_ptr = dest_scene_ptr.base_mut().cur_world_wtr.upgrade().unwrap(); let mut cur_world = cur_world_ptr.borrow_mut(); let _team_avatar_vec = cur_world .scene_team .team_avatar_map .get_mut(&self.uid) .unwrap(); /* TODO: check if this actually should be done here, I doubt tbh * cur_avatar_team.iter().for_each(|guid| { team_avatar_vec.push(crate::scene::SceneTeamAvatar { uid: self.uid, avatar_guid: *guid, }) });*/ // TODO: avatar_comp.is_all_avatar_dead() -> revive Ok(cur_avatar_team) } fn pre_enter_scene(self: &Rc) -> Result<(), Retcode> { let scene_comp = self.player_scene_comp.borrow(); let Some(mut dest_world_ptr) = scene_comp.dest_world_ptr.as_ref().map(|d| d.borrow_mut()) else { tracing::error!("dest_world_ptr is None, uid: {}", self.uid); return Err(Retcode::RetFail); }; dest_world_ptr.player_pre_enter(self).map_err(|retcode| { tracing::warn!( "uid: {} pre-enter world fails, owner: {}", self.uid, dest_world_ptr.get_owner_uid() ); retcode })?; scene_comp .get_dest_scene() .unwrap() .borrow_mut() .base_mut() .player_pre_enter(self.clone()) .map_err(|retcode| { tracing::warn!("[EnterScene] playerPreEnter failed, uid:{}", self.uid); retcode })?; Ok(()) } fn mark_destination( &self, dest_scene_ptr: &Rc>, pos: Vector3, rot: Vector3, enter_type: EnterType, vision_type: VisionType, comp: &mut PlayerSceneComp, ) { comp.dest_pos = pos; comp.dest_rot = rot; comp.set_dest_scene(Rc::downgrade(dest_scene_ptr)); comp.dest_enter_type = enter_type; comp.dest_vision_type = vision_type; } fn clear_destination(&self) { let mut comp = self.player_scene_comp.borrow_mut(); comp.set_dest_scene(Weak::new()); comp.dest_enter_type = EnterType::EnterNone; comp.dest_vision_type = VisionType::VisionNone; } fn notify_all_data(&self) { self.notify_player_data(); self.player_basic_comp.borrow().notify_all_data(); self.player_item_comp.borrow().notify_all_data(); self.player_avatar_comp.borrow().notify_all_data(); } fn notify_player_data(&self) { let mut notify = PlayerDataNotify::default(); let basic_comp = self.player_basic_comp.borrow(); let avatar_comp = self.player_avatar_comp.borrow(); let item_comp = self.player_item_comp.borrow(); notify.nick_name = basic_comp.nickname.clone(); notify.server_time = time_utils::get_now(); let map = &mut notify.prop_map; append_int_prop_value( map, PROP_IS_SPRING_AUTO_USE, avatar_comp.is_spring_auto_use.into(), ); append_int_prop_value( map, PROP_SPRING_AUTO_USE_PERCENT, avatar_comp.spring_auto_use_percent.into(), ); append_int_prop_value(map, PROP_IS_FLYABLE, avatar_comp.is_flyable.into()); append_int_prop_value( map, PROP_IS_TRANSFERABLE, avatar_comp.is_transferable.into(), ); append_int_prop_value( map, PROP_IS_WEATHER_LOCKED, basic_comp.is_weather_locked.into(), ); append_int_prop_value( map, PROP_IS_GAME_TIME_LOCKED, basic_comp.is_game_time_locked.into(), ); append_int_prop_value(map, PROP_PLAYER_LEVEL, basic_comp.level.into()); append_int_prop_value(map, PROP_PLAYER_EXP, basic_comp.exp.into()); append_int_prop_value(map, PROP_PLAYER_HCOIN, item_comp.hcoin.into()); append_int_prop_value(map, PROP_PLAYER_SCOIN, item_comp.scoin.into()); append_int_prop_value(map, PROP_MAX_STAMINA, 10000); append_int_prop_value(map, PROP_CUR_PERSIST_STAMINA, 10000); self.send_proto(notify); } fn update_last_ping_time(&self) { self.last_recv_ping_time.set(time_utils::get_now()); } pub fn save_to_db(&self) { use common::{net::ServiceType, ServiceQualifier}; let player_data = self.to_bin(); if let Err(err) = gameserver_service() .service_box .push_packet_to_service_blocking( ServiceQualifier::Type(ServiceType::Dbgate), Packet::new( SavePlayerDataReq { player_data: player_data.encode_to_vec(), save_stat_id: 0, // TODO }, PacketHead { user_id: self.uid, user_session_id: self .session .borrow() .upgrade() .map(|s| s.get_gateserver_session_id()) .unwrap_or_default(), ..Default::default() }, ), ) { tracing::warn!("save_to_db failed, uid: {}, err: {err}", self.uid); } } pub fn from_bin(&self, data: PlayerData) -> Result<(), prost::DecodeError> { // TODO: PlayerData::bin zlib let bin = PlayerDataBin::decode(&*data.bin)?; *self.player_basic_comp.borrow_mut() = PlayerBasicComp::from_bin(bin.basic_bin.unwrap_or_default()); *self.player_login_comp.borrow_mut() = PlayerLoginComp::from_bin(bin.login_bin.unwrap_or_default()); *self.player_avatar_comp.borrow_mut() = PlayerAvatarComp::from_bin(bin.avatar_bin.unwrap_or_default()); *self.player_item_comp.borrow_mut() = PlayerItemComp::from_bin(bin.item_bin.unwrap_or_default()); *self.player_scene_comp.borrow_mut() = PlayerSceneComp::from_bin(bin.scene_bin.unwrap_or_default()); Ok(()) } pub fn to_bin(&self) -> PlayerData { let mut bin = PlayerDataBin::default(); bin.basic_bin = Some(self.player_basic_comp.borrow().to_bin()); bin.login_bin = Some(self.player_login_comp.borrow().to_bin()); bin.avatar_bin = Some(self.player_avatar_comp.borrow().to_bin()); bin.item_bin = Some(self.player_item_comp.borrow().to_bin()); bin.scene_bin = Some(self.player_scene_comp.borrow().to_bin()); let basic_comp = self.player_basic_comp.borrow(); PlayerData { uid: self.uid, nickname: basic_comp.nickname.clone(), level: basic_comp.level, exp: basic_comp.exp, bin: bin.encode_to_vec(), ..Default::default() } } pub fn init(self: &Rc) { self.player_basic_comp.borrow_mut().set_player(self.clone()); self.player_login_comp.borrow_mut().set_player(self.clone()); self.player_avatar_comp .borrow_mut() .set_player(self.clone()); self.player_item_comp.borrow_mut().set_player(self.clone()); self.player_scene_comp.borrow_mut().set_player(self.clone()); self.player_basic_comp.borrow_mut().init(); self.player_login_comp.borrow_mut().init(); self.player_avatar_comp.borrow_mut().init(); self.player_item_comp.borrow_mut().init(); self.player_scene_comp.borrow_mut().init(); } }