refactor: some restructuring

- move structs inside `sr_tools.rs` into individual file
- implement in-game equip handler (relic & lightcone)
This commit is contained in:
amizing25
2025-03-11 17:27:05 +07:00
parent fff8fc803e
commit d3af43fb06
18 changed files with 1170 additions and 737 deletions
+1 -1
View File
@@ -5,7 +5,7 @@ use rogue_magic_battle_unit_info::Item;
use common::{
resources::GAME_RES,
sr_tools::{BattleType, Monster, RogueMagicComponentType},
structs::{BattleType, Monster, RogueMagicComponentType},
};
use super::*;
+1 -1
View File
@@ -1,4 +1,4 @@
use common::sr_tools::MultiPathAvatar;
use common::structs::MultiPathAvatar;
use crate::{net::PlayerSession, util::cur_timestamp_ms};
+254 -18
View File
@@ -1,3 +1,4 @@
use common::sr_tools::FreesrData;
use proto::{get_big_data_all_recommend_sc_rsp::RecommendType, *};
use crate::net::PlayerSession;
@@ -14,12 +15,8 @@ pub async fn on_get_bag_cs_req(
return;
};
res.equipment_list = player
.lightcones
.iter()
.map(|v| v.to_equipment_proto())
.collect();
res.relic_list = player.relics.iter().map(|v| v.to_relic_proto()).collect();
res.equipment_list = player.lightcones.iter().map(|v| v.into()).collect();
res.relic_list = player.relics.iter().map(|v| v.into()).collect();
res.material_list = vec![
Material {
tid: 101, // Normal Pass
@@ -43,31 +40,63 @@ pub async fn on_get_archive_data_cs_req(
}
pub async fn on_dress_relic_avatar_cs_req(
_session: &mut PlayerSession,
_req: &DressRelicAvatarCsReq,
_res: &mut DressRelicAvatarScRsp,
session: &mut PlayerSession,
req: &DressRelicAvatarCsReq,
_: &mut DressRelicAvatarScRsp,
) {
let Some(player) = session.json_data.get_mut() else {
tracing::error!("data is not set!");
return;
};
if let Some(pkt) = equip_relic(player, req) {
let _ = session.send(pkt).await;
};
}
pub async fn on_take_off_relic_cs_req(
_session: &mut PlayerSession,
_req: &TakeOffRelicCsReq,
_res: &mut TakeOffRelicScRsp,
session: &mut PlayerSession,
req: &TakeOffRelicCsReq,
_: &mut TakeOffRelicScRsp,
) {
let Some(player) = session.json_data.get_mut() else {
tracing::error!("data is not set!");
return;
};
if let Some(pkt) = unequip_relic(player, req) {
let _ = session.send(pkt).await;
};
}
pub async fn on_dress_avatar_cs_req(
_session: &mut PlayerSession,
_req: &DressAvatarCsReq,
_res: &mut DressAvatarScRsp,
session: &mut PlayerSession,
req: &DressAvatarCsReq,
_: &mut DressAvatarScRsp,
) {
let Some(player) = session.json_data.get_mut() else {
tracing::error!("data is not set!");
return;
};
if let Some(pkt) = set_lightcone_equipper(player, req.avatar_id, req.equipment_unique_id) {
let _ = session.send(pkt).await;
};
}
pub async fn on_take_off_equipment_cs_req(
_session: &mut PlayerSession,
_req: &TakeOffEquipmentCsReq,
_res: &mut TakeOffEquipmentScRsp,
session: &mut PlayerSession,
req: &TakeOffEquipmentCsReq,
_: &mut TakeOffEquipmentScRsp,
) {
let Some(player) = session.json_data.get_mut() else {
tracing::error!("data is not set!");
return;
};
if let Some(pkt) = set_lightcone_equipper(player, req.avatar_id, 0) {
let _ = session.send(pkt).await;
};
}
pub async fn on_get_big_data_all_recommend_cs_req(
@@ -104,3 +133,210 @@ pub async fn on_get_big_data_all_recommend_cs_req(
_ => {}
}
}
pub async fn on_rank_up_avatar_cs_req(
session: &mut PlayerSession,
req: &RankUpAvatarCsReq,
_: &mut RankUpAvatarScRsp,
) -> Option<()> {
let player = session.json_data.get_mut()?;
let avatar = player.avatars.get_mut(&req.avatar_id)?;
avatar.data.rank = req.rank;
let avatar_id = avatar.avatar_id;
let mut ret = PlayerSyncScNotify::default();
build_sync(
player,
&mut ret,
vec![avatar_id],
Vec::with_capacity(0),
Vec::with_capacity(0),
);
let _ = session.send(ret).await;
Some(())
}
// TODO: move these somewhere else?
fn set_lightcone_equipper(
player: &mut FreesrData,
target_avatar: u32,
target_lightcone_uid: u32,
) -> Option<PlayerSyncScNotify> {
let mut ret = PlayerSyncScNotify::default();
let target_avatar = player.avatars.get(&target_avatar)?;
let cur_avatar_lc_idx = player
.lightcones
.iter()
.position(|l| l.equip_avatar == target_avatar.avatar_id);
// undress
if target_lightcone_uid == 0
&& let Some(cur_avatar_lc_idx) = cur_avatar_lc_idx
{
player.lightcones[cur_avatar_lc_idx].equip_avatar = 0;
build_sync(
player,
&mut ret,
vec![target_avatar.avatar_id],
vec![cur_avatar_lc_idx],
Vec::with_capacity(0),
);
return Some(ret);
}
let target_lightcone_idx = player
.lightcones
.iter()
.position(|l| l.get_unique_id() == target_lightcone_uid)?;
// jika avatar sekarang sedang pakai LC, kita tukar pemiliknya dengan pemilik dari LC target
if let Some(cur_avatar_lc_idx) = cur_avatar_lc_idx {
player.lightcones[cur_avatar_lc_idx].equip_avatar =
player.lightcones[target_lightcone_idx].equip_avatar;
}
let avatars_sync = vec![
player.lightcones[target_lightcone_idx].equip_avatar, // old
target_avatar.avatar_id, // cur
];
// set kepemilikan lightcone barunya ke avatar sekarang
player.lightcones[target_lightcone_idx].equip_avatar = target_avatar.avatar_id;
build_sync(
player,
&mut ret,
avatars_sync,
[Some(target_lightcone_idx), cur_avatar_lc_idx]
.into_iter()
.flatten()
.collect(),
Vec::with_capacity(0),
);
Some(ret)
}
fn equip_relic(player: &mut FreesrData, req: &DressRelicAvatarCsReq) -> Option<PlayerSyncScNotify> {
let mut ret = PlayerSyncScNotify::default();
let target_avatar = player.avatars.get(&req.avatar_id)?;
let mut avatar_ids_to_sy = vec![];
let mut relic_index_to_sync = vec![];
for param in &req.param_list {
let Some(target_relic_idx) = player
.relics
.iter()
.position(|v| v.get_unique_id() == param.relic_unique_id)
else {
continue;
};
let cur_avatar_relic_idx = player
.relics
.iter()
.position(|r| r.equip_avatar == target_avatar.avatar_id && r.get_slot() == param.slot);
// jika avatar sekarang sedang pakai LC, kita tukar pemiliknya dengan pemilik dari LC target
if let Some(cur_avatar_relic_idx) = cur_avatar_relic_idx {
avatar_ids_to_sy.push(player.relics[cur_avatar_relic_idx].equip_avatar);
player.relics[cur_avatar_relic_idx].equip_avatar =
player.relics[target_relic_idx].equip_avatar;
relic_index_to_sync.push(cur_avatar_relic_idx);
}
// old owner
avatar_ids_to_sy.push(player.relics[target_relic_idx].equip_avatar);
// set kepemilikan relic barunya ke avatar sekarang
player.relics[target_relic_idx].equip_avatar = target_avatar.avatar_id;
// new owner
avatar_ids_to_sy.push(player.relics[target_relic_idx].equip_avatar);
relic_index_to_sync.push(target_relic_idx);
}
build_sync(
player,
&mut ret,
avatar_ids_to_sy,
Vec::with_capacity(0),
relic_index_to_sync,
);
Some(ret)
}
fn unequip_relic(player: &mut FreesrData, req: &TakeOffRelicCsReq) -> Option<PlayerSyncScNotify> {
let mut ret = PlayerSyncScNotify::default();
let target_avatar = player.avatars.get(&req.avatar_id)?;
let mut relic_index_to_sync = vec![];
for slot in &req.slot_list {
let relics = player
.relics
.iter()
.enumerate()
.filter(|(_, r)| r.equip_avatar == target_avatar.avatar_id && r.get_slot() == *slot)
.map(|(i, _)| i)
.collect::<Vec<_>>();
for relic_idx in &relics {
player.relics[*relic_idx].equip_avatar = 0;
}
relic_index_to_sync.extend(relics);
}
build_sync(
player,
&mut ret,
vec![req.avatar_id],
Vec::with_capacity(0),
relic_index_to_sync,
);
Some(ret)
}
fn build_sync(
player: &mut FreesrData,
ret: &mut PlayerSyncScNotify,
avatar_ids: Vec<u32>,
lightcone_indexes: Vec<usize>,
relic_indexes: Vec<usize>,
) {
let avatar_list = avatar_ids
.iter()
.filter_map(|id| player.get_avatar_proto(*id))
.collect::<Vec<_>>();
ret.avatar_sync = Some(AvatarSync { avatar_list });
ret.multi_path_avatar_type_info_list = avatar_ids
.into_iter()
.filter_map(|id| player.get_avatar_multipath_proto(id))
.collect();
ret.relic_list = relic_indexes
.into_iter()
.map(|id| (&player.relics[id]).into())
.collect();
ret.equipment_list = lightcone_indexes
.into_iter()
.map(|id| (&player.lightcones[id]).into())
.collect();
}
+1 -1
View File
@@ -1,4 +1,4 @@
use common::sr_tools::AvatarJson;
use common::structs::AvatarJson;
use scene_entity_info::Entity;
use scene_entity_refresh_info::RefreshType;
+1 -1
View File
@@ -22,7 +22,7 @@ pub async fn on_player_heart_beat_cs_req(
res.download_data = Some(ClientDownloadData {
version: 51,
time: res.server_time_ms as i64,
data: rbase64::decode("bG9jYWwgZnVuY3Rpb24gYmV0YV90ZXh0KG9iaikKICAgIGxvY2FsIGdhbWVPYmplY3QgPSBDUy5Vbml0eUVuZ2luZS5HYW1lT2JqZWN0LkZpbmQoIlVJUm9vdC9BYm92ZURpYWxvZy9CZXRhSGludERpYWxvZyhDbG9uZSkiKQogICAgaWYgZ2FtZU9iamVjdCB0aGVuCiAgICAgICAgbG9jYWwgdGV4dENvbXBvbmVudCA9IGdhbWVPYmplY3Q6R2V0Q29tcG9uZW50SW5DaGlsZHJlbih0eXBlb2YoQ1MuUlBHLkNsaWVudC5Mb2NhbGl6ZWRUZXh0KSkKICAgICAgICBpZiB0ZXh0Q29tcG9uZW50IHRoZW4KICAgICAgICAgICAgdGV4dENvbXBvbmVudC50ZXh0ID0gIiIKICAgICAgICBlbmQKICAgIGVuZAplbmQKCmxvY2FsIGZ1bmN0aW9uIHZlcnNpb25fdGV4dChvYmopCiAgICBsb2NhbCBnYW1lT2JqZWN0ID0gQ1MuVW5pdHlFbmdpbmUuR2FtZU9iamVjdC5GaW5kKCJWZXJzaW9uVGV4dCIpCiAgICBpZiBnYW1lT2JqZWN0IHRoZW4KICAgICAgICBsb2NhbCB0ZXh0Q29tcG9uZW50ID0gZ2FtZU9iamVjdDpHZXRDb21wb25lbnRJbkNoaWxkcmVuKHR5cGVvZihDUy5SUEcuQ2xpZW50LkxvY2FsaXplZFRleHQpKQogICAgICAgIGlmIHRleHRDb21wb25lbnQgdGhlbgogICAgICAgICAgICB0ZXh0Q29tcG9uZW50LnRleHQgPSAiPGNvbG9yPSMwMGUxZmY+Um9iaW5TUiE8L2NvbG9yPiIKICAgICAgICBlbmQKICAgIGVuZAplbmQKCnZlcnNpb25fdGV4dCgpCmJldGFfdGV4dCgpCg==").unwrap(),
data: rbase64::decode("BAEwB0dldFR5cGUIR2V0RmllbGQIR2V0VmFsdWUCQ1MGU3lzdGVtC0NvbGxlY3Rpb25zB0dlbmVyaWMETGlzdAZTdHJpbmcCY24DQWRkAmVuAmtyAmpwB1RvQXJyYXkDUlBHBkNsaWVudApHbG9iYWxWYXJzFXNfTG9jYWxpemF0aW9uTWFuYWdlchRfYWxsb3dlZExhbmd1YWdlS2V5cxFfYWxsb3dlZEF1ZGlvS2V5cwhHYW1lQ29yZRlBbGxvd2VkTGFuZ3VhZ2VFeGNlbFRhYmxlCGRhdGFEaWN0BXBhaXJzDExhbmd1YWdlTGlzdAtVbml0eUVuZ2luZQtBcHBsaWNhdGlvbgdNQVhfRlBTD3RhcmdldEZyYW1lUmF0ZQ9RdWFsaXR5U2V0dGluZ3MKdlN5bmNDb3VudApHYW1lT2JqZWN0BEZpbmQoVUlSb290L0Fib3ZlRGlhbG9nL0JldGFIaW50RGlhbG9nKENsb25lKQ1Mb2NhbGl6ZWRUZXh0BnR5cGVvZhZHZXRDb21wb25lbnRJbkNoaWxkcmVuB0JFVEFfV00EdGV4dAtWZXJzaW9uVGV4dCI8Y29sb3I9IzAwZTFmZj5Sb2JpblNSISB8IDwvY29sb3I+Dzxjb2xvcj0jMDBlMWZmPg1zX1ZlcnNpb25EYXRhF0dldFNlcnZlclBha1R5cGVWZXJzaW9uCDwvY29sb3I+BnhwY2FsbApJU19QQVRDSEVECQEBAAAAAAEWAAEAAAACAAAABgIAAAAADRQCABsAAAAAFQICAgYEAQAEBT4AFAICkQEAAAAVAgQCBgQAABQCAvMCAAAAFQIDABYCAAADAwEDAgMDAAQAAAAFAAAAAAAfDAIDAAIEAMAPAQL5BAAAAA8AAesFAAAADAEHAAYEAMAVAAICBgEAABUBAQIFBAgAFAIBXwkAAAAVAgMBBQQKABQCAV8JAAAAFQIDAQUECwAUAgFfCQAAABUCAwEFBAwAFAIBXwkAAAAVAgMBFAIBOQ0AAAAVAgIAFgIAAA4DBAMFAwYEAgQAwAMHAwgDCQQGBADAAwoDCwMMAw0DDgMPAAsAAAAGAAAAAAA/DAIDAAIEAMAPAQKsBAAAAA8AAaAFAAAAFAEAGwYAAAAVAQICBQMHAAQEPgAUAQGRCAAAABUBBAIGAwAAFAEB8wkAAAAVAQMCBQQKABQCAV8LAAAAFQIDAQUEDAAUAgFfCwAAABUCAwEFBA0AFAIBXwsAAAAVAgMBBQQOABQCAV8LAAAAFQIDARQCABsGAAAAFQICAgUEDwAEBT4AFAICkQgAAAAVAgQCBgQAABQCAvMJAAAAFQIDAgUFCgAUAwJfCwAAABUDAwEFBQwAFAMCXwsAAAAVAwMBBQUNABQDAl8LAAAAFQMDAQUFDgAUAwJfCwAAABUDAwEWAAEAEAMEAxADEQQCBADAAxIDEwMBAxQDAgMDAwoDCwMMAw0DDgMVABQAAAAHAAEAAAASDAIDAAIEAMAPAQLwBAAAAA8AAbkFAAAACQEAABUBAQIMAgcAAABgQAYDAAAVAgIEQQICABABBjIIAAAAPgL9/wIAAAAWAAEACQMEAxADFgQCBADAAxcDGAMZBAAAYEADGgAiAAAAAgAAAAAADAwAAwACBADABwEAygQAAAAQAQADBQAAAAwABwAGBADABAEAABABANAIAAAAFgABAAkDBAMbAxwEAgQAwAMdAx4DHwQGBADAAyAALAAAAAkAAAAAADcMAQMAAgQAwA8AAXYEAAAABQEFABUAAgIMBAgABxgAwA8DBBQJAAAATSwDAgwCCwAAAKBAFQICAhQAALEMAAAAFQADAgwBDgAAANBAEAEA1Q8AAAAMAQMAAgQAwA8AAXYEAAAABQEQABUAAgIMBAgABxgAwA8DBBQJAAAATSwDAgwCCwAAAKBAFQICAhQAALEMAAAAFQADAgUCEQAFAxIADAgIAAcYAMAPBwisEwAAAA8GByMUAAAAFAYGkRUAAAAVBgICBgQGAAUFFgA1AQIFEAEA1Q8AAAAWAAEAFwMEAxsDIQQCBADAAyIDIwMQAxEEBxgAwAMkAyUEAACgQAMmAycEAADQQAMoAykDKgMrAxIDLAMtAy4AMQAAAAMABQAAACQHAADKAAAAAFEABAAAAACABABoAQgAAMoAAAAADAACAAAAEEAJAQAACQIBABUAAwEMAAIAAAAQQAkBAgAJAgEAFQADAQcAACsDAAAAUgACAAEAAIAWAAEAAwABAAgAACsDAAAADAACAAAAEEAJAQMACQIBABUAAwEMAAIAAAAQQAkBBAAJAgEAFQADARYAAQAEAx0DLwQAABBAAzAAPwAAAAsAAAEAABVFAAAARAAAAEQBAQBEAgIARAMDAEQEBABKAAIARAUFAEQGBgBEBwcASgAFAEoAAABKAAYASgAEAEoAAwAMCAkAAACAQAYJBwAGCgAAFQgDARYAAQAKBgAGAQYCBgMGBAYFBgYGBwMvBAAAgEAIAAECAwQFBgcBAAAACA==").unwrap(),
haehhcpoapp: 0
});
}
+4 -4
View File
@@ -2,7 +2,7 @@ use std::collections::HashMap;
use common::{
resources::GAME_RES,
sr_tools::{AvatarJson, Position},
structs::{AvatarJson, Position},
};
use scene_entity_info::Entity;
@@ -277,7 +277,7 @@ async fn load_scene(
let entity_info = SceneEntityInfo {
inst_id: prop.inst_id,
group_id: prop.group_id,
motion: Some(prop_position.to_motion()),
motion: Some(prop_position.into()),
entity: Some(Entity::Prop(ScenePropInfo {
prop_state: prop.prop_state,
prop_id: prop.prop_id,
@@ -308,7 +308,7 @@ async fn load_scene(
inst_id: npc.inst_id,
group_id: npc.group_id,
entity_id: npc_entity_id,
motion: Some(npc_position.to_motion()),
motion: Some(npc_position.into()),
entity: Some(Entity::Npc(SceneNpcInfo {
npc_id: npc.npc_id,
..Default::default()
@@ -339,7 +339,7 @@ async fn load_scene(
inst_id: monster.inst_id,
group_id: monster.group_id,
entity_id: monster_entity_id,
motion: Some(monster_position.to_motion()),
motion: Some(monster_position.into()),
entity: Some(Entity::NpcMonster(npc_monster)),
};
+1
View File
@@ -190,6 +190,7 @@ trait_handler! {
TakeOffEquipment;
DressRelicAvatar;
TakeOffRelic;
RankUpAvatar;
// Chat (dummy!)
SendMsg;
+10 -16
View File
@@ -14,12 +14,12 @@ use proto::{AvatarSync, CmdID, CmdPlayerType, PlayerSyncScNotify};
use tokio::{
io::AsyncWrite,
net::UdpSocket,
sync::{watch, Mutex},
sync::{Mutex, watch},
};
use crate::util;
use super::{packet::CommandHandler, NetPacket};
use super::{NetPacket, packet::CommandHandler};
struct RemoteEndPoint {
socket: Arc<UdpSocket>,
@@ -41,12 +41,10 @@ impl PlayerSession {
let (shutdown_tx, shutdown_rx) = watch::channel(());
Self {
token,
kcp: Arc::new(Mutex::new(Kcp::new(
conv,
token,
false,
RemoteEndPoint { socket, addr },
))),
kcp: Arc::new(Mutex::new(Kcp::new(conv, token, false, RemoteEndPoint {
socket,
addr,
}))),
start_time: util::cur_timestamp_secs(),
json_data: OnceLock::new(),
shutdown_rx,
@@ -124,8 +122,8 @@ impl PlayerSession {
// clear relics & lightcones
self.send(PlayerSyncScNotify {
del_equipment_list: (2000..3500).collect(),
del_relic_list: (1..2000).collect(),
del_equipment_list: (2000..=3500).collect(),
del_relic_list: (1..=2000).collect(),
..Default::default()
})
.await
@@ -148,12 +146,8 @@ impl PlayerSession {
// Sync new relics
self.send(PlayerSyncScNotify {
relic_list: json.relics.iter().map(|v| v.to_relic_proto()).collect(),
equipment_list: json
.lightcones
.iter()
.map(|v| v.to_equipment_proto())
.collect(),
relic_list: json.relics.iter().map(|v| v.into()).collect(),
equipment_list: json.lightcones.iter().map(|v| v.into()).collect(),
..Default::default()
})
.await