refactor: Refactor json loading mechanism & more
- Move json loading into separate crate `common` - Add new http route for handling SRTools API - Listen to `freesr-data.json` file change, and sync with client immediately - Move json loading into `PlayerSession`, instead of load it everytime - Implement global buff for Castorice - Implement `GetBigDataAllRecommendCsReq`
This commit is contained in:
@@ -12,7 +12,8 @@ hex.workspace = true
|
||||
lazy_static.workspace = true
|
||||
paste.workspace = true
|
||||
rbase64.workspace = true
|
||||
sysinfo.workspace = true
|
||||
notify.workspace = true
|
||||
notify-debouncer-mini.workspace = true
|
||||
|
||||
serde.workspace = true
|
||||
serde_json.workspace = true
|
||||
@@ -32,3 +33,4 @@ proto-derive.workspace = true
|
||||
|
||||
rand.workspace = true
|
||||
mhy-kcp.workspace = true
|
||||
common.workspace = true
|
||||
|
||||
@@ -3,7 +3,6 @@ use anyhow::Result;
|
||||
|
||||
mod logging;
|
||||
mod net;
|
||||
mod tools;
|
||||
mod util;
|
||||
|
||||
use logging::init_tracing;
|
||||
|
||||
@@ -1,20 +1,24 @@
|
||||
use std::{
|
||||
collections::HashMap,
|
||||
net::SocketAddr,
|
||||
path::Path,
|
||||
sync::{
|
||||
atomic::{AtomicU32, Ordering},
|
||||
Arc,
|
||||
atomic::{AtomicU32, Ordering},
|
||||
},
|
||||
time::Duration,
|
||||
};
|
||||
|
||||
use anyhow::Result;
|
||||
|
||||
use common::sr_tools::FreesrData;
|
||||
use rand::RngCore;
|
||||
|
||||
use crate::net::PlayerSession;
|
||||
|
||||
use tokio::{
|
||||
net::UdpSocket,
|
||||
sync::{Mutex, RwLock},
|
||||
sync::{Mutex, RwLock, mpsc},
|
||||
};
|
||||
|
||||
use crate::net::packet::NetOperation;
|
||||
@@ -74,15 +78,79 @@ impl Gateway {
|
||||
let (conv_id, session_token) = self.next_conv_pair();
|
||||
tracing::info!("New connection from addr: {addr} with conv_id: {conv_id}");
|
||||
|
||||
self.sessions.lock().await.insert(
|
||||
let session = Arc::new(RwLock::new(PlayerSession::new(
|
||||
self.socket.clone(),
|
||||
addr,
|
||||
conv_id,
|
||||
Arc::new(RwLock::new(PlayerSession::new(
|
||||
self.socket.clone(),
|
||||
addr,
|
||||
conv_id,
|
||||
session_token,
|
||||
))),
|
||||
);
|
||||
session_token,
|
||||
)));
|
||||
|
||||
// Init the json to session
|
||||
let _ = session
|
||||
.write()
|
||||
.await
|
||||
.json_data
|
||||
.set(FreesrData::load().await);
|
||||
|
||||
let session_ref = session.clone();
|
||||
|
||||
// FS watcher
|
||||
tokio::spawn(async move {
|
||||
let (tx, mut rx) = mpsc::channel(100);
|
||||
let mut debouncer =
|
||||
notify_debouncer_mini::new_debouncer(Duration::from_millis(1000), move |ev| {
|
||||
let _ = tx.blocking_send(ev);
|
||||
})
|
||||
.unwrap();
|
||||
|
||||
let path = Path::new("freesr-data.json");
|
||||
|
||||
debouncer
|
||||
.watcher()
|
||||
.watch(path, notify::RecursiveMode::NonRecursive)
|
||||
.unwrap();
|
||||
|
||||
tracing::info!("watching freesr-data.json changes");
|
||||
|
||||
let mut shutdown_rx = session.read().await.shutdown_rx.clone();
|
||||
loop {
|
||||
tokio::select! {
|
||||
res = rx.recv() => {
|
||||
let Some(res) = res else {
|
||||
break;
|
||||
};
|
||||
match res {
|
||||
Ok(events) => {
|
||||
if events
|
||||
.iter()
|
||||
.any(|p| p.path.file_name() == path.file_name())
|
||||
{
|
||||
let mut session = session.write().await;
|
||||
if let Some(json) = session.json_data.get_mut() {
|
||||
let _ = json.update().await;
|
||||
session.sync_player().await;
|
||||
tracing::info!("json updated")
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(e) => eprintln!("json watcher error: {:?}", e),
|
||||
}
|
||||
}
|
||||
_ = shutdown_rx.changed() => {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
tracing::info!("unwatch freesr-data.json");
|
||||
});
|
||||
|
||||
let mut sessions = self.sessions.lock().await;
|
||||
for session in sessions.values_mut() {
|
||||
let _ = session.write().await.shutdown_tx.send(());
|
||||
}
|
||||
sessions.clear();
|
||||
sessions.insert(conv_id, session_ref);
|
||||
|
||||
self.socket
|
||||
.send_to(
|
||||
@@ -102,38 +170,29 @@ impl Gateway {
|
||||
|
||||
async fn drop_kcp_session(&mut self, conv_id: u32, token: u32, addr: SocketAddr) {
|
||||
tracing::info!("drop_kcp_session {conv_id} {token}");
|
||||
let Some(session) = self.sessions.lock().await.get(&conv_id).cloned() else {
|
||||
let mut sessions = self.sessions.lock().await;
|
||||
let Some(session) = sessions.get(&conv_id) else {
|
||||
tracing::warn!("drop_kcp_session failed, no session with conv_id {conv_id} was found");
|
||||
return;
|
||||
};
|
||||
|
||||
if session.read().await.token == token {
|
||||
self.sessions.lock().await.remove(&conv_id);
|
||||
let session = session.write().await;
|
||||
if session.token == token {
|
||||
let _ = session.shutdown_tx.send(());
|
||||
drop(session);
|
||||
sessions.remove(&conv_id);
|
||||
tracing::info!("Client from {addr} disconnected");
|
||||
}
|
||||
}
|
||||
|
||||
async fn process_kcp_payload(&mut self, data: Box<[u8]>, addr: SocketAddr) {
|
||||
let conv_id = mhy_kcp::get_conv(&data);
|
||||
let mut sessions = self.sessions.lock().await;
|
||||
|
||||
let Some(session) = self
|
||||
.sessions
|
||||
.lock()
|
||||
.await
|
||||
.get_mut(&conv_id)
|
||||
.map(|s| s.clone())
|
||||
else {
|
||||
let Some(session) = sessions.get_mut(&conv_id).map(|s| s.clone()) else {
|
||||
tracing::warn!("Session with conv_id {conv_id} not found!");
|
||||
return;
|
||||
};
|
||||
|
||||
// TODO: Temporary fix
|
||||
if session.read().await.is_destroyed {
|
||||
drop(session);
|
||||
self.sessions.lock().await.remove(&conv_id);
|
||||
return;
|
||||
}
|
||||
|
||||
tokio::spawn(async move {
|
||||
if let Err(err) = Box::pin(session.write().await.consume(&data)).await {
|
||||
tracing::error!("An error occurred while processing session ({addr}): {err}");
|
||||
|
||||
@@ -1,20 +1,21 @@
|
||||
use crate::net::tools::FreesrData;
|
||||
|
||||
use super::*;
|
||||
|
||||
static UNLOCKED_AVATARS: [u32; 63] = [
|
||||
pub static UNLOCKED_AVATARS: [u32; 63] = [
|
||||
1002, 1003, 1004, 1005, 1006, 1008, 1009, 1013, 1101, 1102, 1103, 1104, 1105, 1106, 1107, 1108,
|
||||
1109, 1110, 1111, 1112, 1201, 1202, 1203, 1204, 1205, 1206, 1207, 1208, 1209, 1210, 1211, 1212,
|
||||
1213, 1214, 1215, 1217, 1301, 1302, 1303, 1304, 1305, 1306, 1307, 1308, 1309, 1312, 1315, 1310,
|
||||
1314, 1218, 1221, 1220, 1222, 1223, 1317, 1313, 1225, 1402, 1401, 1404, 1403, 1405, 1407
|
||||
1314, 1218, 1221, 1220, 1222, 1223, 1317, 1313, 1225, 1402, 1401, 1404, 1403, 1405, 1407,
|
||||
];
|
||||
|
||||
pub async fn on_get_avatar_data_cs_req(
|
||||
_session: &mut PlayerSession,
|
||||
session: &mut PlayerSession,
|
||||
body: &GetAvatarDataCsReq,
|
||||
res: &mut GetAvatarDataScRsp,
|
||||
) {
|
||||
let json = FreesrData::load().await;
|
||||
let Some(json) = session.json_data.get() else {
|
||||
tracing::error!("data is not set!");
|
||||
return;
|
||||
};
|
||||
|
||||
// TODO: HARDCODED
|
||||
let mc_ids = if json.main_character.get_gender() == Gender::Man {
|
||||
|
||||
@@ -3,19 +3,19 @@ use std::collections::HashMap;
|
||||
use rand::Rng;
|
||||
use rogue_magic_battle_unit_info::Item;
|
||||
|
||||
use crate::{
|
||||
net::tools::{self, BattleType, Monster},
|
||||
tools::resources::GAME_RES,
|
||||
use common::{
|
||||
resources::GAME_RES,
|
||||
sr_tools::{BattleType, Monster, RogueMagicComponentType},
|
||||
};
|
||||
|
||||
use super::*;
|
||||
|
||||
pub async fn on_start_cocoon_stage_cs_req(
|
||||
_session: &mut PlayerSession,
|
||||
session: &mut PlayerSession,
|
||||
req: &StartCocoonStageCsReq,
|
||||
res: &mut StartCocoonStageScRsp,
|
||||
) {
|
||||
let battle_info = create_battle_info(0, 0).await;
|
||||
let battle_info = create_battle_info(session, 0, 0).await;
|
||||
|
||||
res.prop_entity_id = req.prop_entity_id;
|
||||
res.cocoon_id = req.cocoon_id;
|
||||
@@ -24,11 +24,11 @@ pub async fn on_start_cocoon_stage_cs_req(
|
||||
}
|
||||
|
||||
pub async fn on_quick_start_cocoon_stage_cs_req(
|
||||
_session: &mut PlayerSession,
|
||||
session: &mut PlayerSession,
|
||||
req: &QuickStartCocoonStageCsReq,
|
||||
res: &mut QuickStartCocoonStageScRsp,
|
||||
) {
|
||||
let mut battle_info = create_battle_info(0, 0).await;
|
||||
let mut battle_info = create_battle_info(session, 0, 0).await;
|
||||
|
||||
battle_info.world_level = req.world_level;
|
||||
res.cocoon_id = req.cocoon_id;
|
||||
@@ -46,7 +46,7 @@ pub async fn on_pve_battle_result_cs_req(
|
||||
}
|
||||
|
||||
pub async fn on_scene_cast_skill_cs_req(
|
||||
_session: &mut PlayerSession,
|
||||
session: &mut PlayerSession,
|
||||
req: &SceneCastSkillCsReq,
|
||||
res: &mut SceneCastSkillScRsp,
|
||||
) {
|
||||
@@ -55,20 +55,30 @@ pub async fn on_scene_cast_skill_cs_req(
|
||||
let targets = req
|
||||
.hit_target_entity_id_list
|
||||
.iter()
|
||||
.chain(&req.assist_monster_entity_id_list)
|
||||
.filter(|id| **id > 30_000 || **id < 1_000)
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
if targets.is_empty() {
|
||||
tracing::warn!("scene cast skill target is empty!");
|
||||
return;
|
||||
}
|
||||
|
||||
let battle_info = create_battle_info(req.caster_id, req.skill_index).await;
|
||||
let battle_info = create_battle_info(session, req.caster_id, req.skill_index).await;
|
||||
|
||||
res.attacked_group_id = req.attacked_group_id;
|
||||
res.battle_info = Some(battle_info);
|
||||
}
|
||||
|
||||
async fn create_battle_info(caster_id: u32, skill_index: u32) -> SceneBattleInfo {
|
||||
let player = tools::FreesrData::load().await;
|
||||
async fn create_battle_info(
|
||||
session: &mut PlayerSession,
|
||||
caster_id: u32,
|
||||
skill_index: u32,
|
||||
) -> SceneBattleInfo {
|
||||
let Some(player) = session.json_data.get() else {
|
||||
tracing::error!("data is not set!");
|
||||
return SceneBattleInfo::default();
|
||||
};
|
||||
|
||||
let mut battle_info = SceneBattleInfo {
|
||||
stage_id: player.battle_config.stage_id,
|
||||
@@ -207,27 +217,21 @@ async fn create_battle_info(caster_id: u32, skill_index: u32) -> SceneBattleInfo
|
||||
// pf score object
|
||||
if player.battle_config.battle_type == BattleType::PF {
|
||||
if battle_info.stage_id >= 30309011 {
|
||||
battle_info.battle_target_info.insert(
|
||||
1,
|
||||
BattleTargetList {
|
||||
battle_target_list: vec![BattleTarget {
|
||||
id: 10003,
|
||||
progress: 0,
|
||||
..Default::default()
|
||||
}],
|
||||
},
|
||||
);
|
||||
battle_info.battle_target_info.insert(1, BattleTargetList {
|
||||
battle_target_list: vec![BattleTarget {
|
||||
id: 10003,
|
||||
progress: 0,
|
||||
..Default::default()
|
||||
}],
|
||||
});
|
||||
} else {
|
||||
battle_info.battle_target_info.insert(
|
||||
1,
|
||||
BattleTargetList {
|
||||
battle_target_list: vec![BattleTarget {
|
||||
id: 10002,
|
||||
progress: 0,
|
||||
..Default::default()
|
||||
}],
|
||||
},
|
||||
);
|
||||
battle_info.battle_target_info.insert(1, BattleTargetList {
|
||||
battle_target_list: vec![BattleTarget {
|
||||
id: 10002,
|
||||
progress: 0,
|
||||
..Default::default()
|
||||
}],
|
||||
});
|
||||
}
|
||||
|
||||
for i in 2..=4 {
|
||||
@@ -236,37 +240,31 @@ async fn create_battle_info(caster_id: u32, skill_index: u32) -> SceneBattleInfo
|
||||
.insert(i, BattleTargetList::default());
|
||||
}
|
||||
|
||||
battle_info.battle_target_info.insert(
|
||||
5,
|
||||
BattleTargetList {
|
||||
battle_target_list: vec![
|
||||
BattleTarget {
|
||||
id: 2001,
|
||||
progress: 0,
|
||||
..Default::default()
|
||||
},
|
||||
BattleTarget {
|
||||
id: 2002,
|
||||
progress: 0,
|
||||
..Default::default()
|
||||
},
|
||||
],
|
||||
},
|
||||
);
|
||||
battle_info.battle_target_info.insert(5, BattleTargetList {
|
||||
battle_target_list: vec![
|
||||
BattleTarget {
|
||||
id: 2001,
|
||||
progress: 0,
|
||||
..Default::default()
|
||||
},
|
||||
BattleTarget {
|
||||
id: 2002,
|
||||
progress: 0,
|
||||
..Default::default()
|
||||
},
|
||||
],
|
||||
});
|
||||
}
|
||||
|
||||
// Apocalyptic Shadow
|
||||
if player.battle_config.battle_type == BattleType::AS {
|
||||
battle_info.battle_target_info.insert(
|
||||
1,
|
||||
BattleTargetList {
|
||||
battle_target_list: vec![BattleTarget {
|
||||
id: 90005,
|
||||
progress: 0,
|
||||
..Default::default()
|
||||
}],
|
||||
},
|
||||
);
|
||||
battle_info.battle_target_info.insert(1, BattleTargetList {
|
||||
battle_target_list: vec![BattleTarget {
|
||||
id: 90005,
|
||||
progress: 0,
|
||||
..Default::default()
|
||||
}],
|
||||
});
|
||||
}
|
||||
|
||||
// SU
|
||||
@@ -313,9 +311,9 @@ async fn create_battle_info(caster_id: u32, skill_index: u32) -> SceneBattleInfo
|
||||
|
||||
for component in &scepter.components {
|
||||
let (slot_type, locked) = match component.component_type {
|
||||
tools::RogueMagicComponentType::Passive => (3u32, false),
|
||||
tools::RogueMagicComponentType::Active => (4, true),
|
||||
tools::RogueMagicComponentType::Attach => (5, false),
|
||||
RogueMagicComponentType::Passive => (3u32, false),
|
||||
RogueMagicComponentType::Active => (4, true),
|
||||
RogueMagicComponentType::Attach => (5, false),
|
||||
};
|
||||
|
||||
let slot_index = &mut index[slot_type as usize - 3];
|
||||
@@ -339,5 +337,17 @@ async fn create_battle_info(caster_id: u32, skill_index: u32) -> SceneBattleInfo
|
||||
});
|
||||
}
|
||||
|
||||
// Global Buff
|
||||
if !battle_info.buff_list.iter().any(|b| b.id == 140703) {
|
||||
battle_info.buff_list.push(BattleBuff {
|
||||
id: 140703,
|
||||
level: 1,
|
||||
owner_id: u32::MAX,
|
||||
wave_flag: u32::MAX,
|
||||
target_index_list: Vec::with_capacity(0),
|
||||
dynamic_values: HashMap::with_capacity(0),
|
||||
});
|
||||
}
|
||||
|
||||
battle_info
|
||||
}
|
||||
|
||||
@@ -1,10 +1,6 @@
|
||||
use crate::{
|
||||
net::{
|
||||
tools::{FreesrData, MultiPathAvatar},
|
||||
PlayerSession,
|
||||
},
|
||||
util::cur_timestamp_ms,
|
||||
};
|
||||
use common::sr_tools::MultiPathAvatar;
|
||||
|
||||
use crate::{net::PlayerSession, util::cur_timestamp_ms};
|
||||
|
||||
use super::*;
|
||||
|
||||
@@ -16,7 +12,7 @@ const SERVER_CHAT_HISTORY: [&str; 5] = [
|
||||
"'mc {mc_id}' mc_id can be set from 8001 to 8008",
|
||||
"'march {march_id}' march_id can be set 1001 or 1224",
|
||||
"available commands:",
|
||||
"visit srtools.pages.dev to configure the PS! (you configure relics, equipment, monsters from there)"
|
||||
"visit srtools.pages.dev to configure the PS! (you configure relics, equipment, monsters from there)",
|
||||
];
|
||||
|
||||
pub async fn on_get_friend_login_info_cs_req(
|
||||
@@ -76,12 +72,15 @@ pub async fn on_send_msg_cs_req(
|
||||
body: &SendMsgCsReq,
|
||||
_res: &mut SendMsgScRsp,
|
||||
) {
|
||||
let mut json = FreesrData::load().await;
|
||||
let Some(json) = session.json_data.get_mut() else {
|
||||
tracing::error!("data is not set!");
|
||||
return;
|
||||
};
|
||||
|
||||
if let Some((cmd, args)) = parse_command(&body.text) {
|
||||
match cmd {
|
||||
"sync" => {
|
||||
sync_player(session, json).await;
|
||||
session.sync_player().await;
|
||||
session
|
||||
.send(RevcMsgScNotify {
|
||||
msg_type: body.msg_type,
|
||||
@@ -90,7 +89,7 @@ pub async fn on_send_msg_cs_req(
|
||||
from_uid: SERVER_UID,
|
||||
to_uid: 25,
|
||||
chat_type: body.chat_type,
|
||||
hnbepabnbng: body.hnbepabnbng.clone(),
|
||||
hnbepabnbng: body.hnbepabnbng,
|
||||
})
|
||||
.await
|
||||
.unwrap();
|
||||
@@ -104,7 +103,7 @@ pub async fn on_send_msg_cs_req(
|
||||
);
|
||||
|
||||
json.main_character = mc;
|
||||
json.save().await;
|
||||
json.save_persistent().await;
|
||||
|
||||
session
|
||||
.send(AvatarPathChangedNotify {
|
||||
@@ -114,7 +113,7 @@ pub async fn on_send_msg_cs_req(
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
sync_player(session, json).await;
|
||||
session.sync_player().await;
|
||||
|
||||
session
|
||||
.send(RevcMsgScNotify {
|
||||
@@ -124,7 +123,7 @@ pub async fn on_send_msg_cs_req(
|
||||
from_uid: SERVER_UID,
|
||||
to_uid: 25,
|
||||
chat_type: body.chat_type,
|
||||
hnbepabnbng: body.hnbepabnbng.clone(),
|
||||
hnbepabnbng: body.hnbepabnbng,
|
||||
})
|
||||
.await
|
||||
.unwrap();
|
||||
@@ -144,7 +143,7 @@ pub async fn on_send_msg_cs_req(
|
||||
}
|
||||
|
||||
json.march_type = march_type;
|
||||
json.save().await;
|
||||
json.save_persistent().await;
|
||||
|
||||
session
|
||||
.send(AvatarPathChangedNotify {
|
||||
@@ -162,7 +161,7 @@ pub async fn on_send_msg_cs_req(
|
||||
from_uid: SERVER_UID,
|
||||
to_uid: 25,
|
||||
chat_type: body.chat_type,
|
||||
hnbepabnbng: body.hnbepabnbng.clone(),
|
||||
hnbepabnbng: body.hnbepabnbng,
|
||||
})
|
||||
.await
|
||||
.unwrap();
|
||||
@@ -181,70 +180,3 @@ fn parse_command(command: &str) -> Option<(&str, Vec<&str>)> {
|
||||
|
||||
Some((parts[0], parts[1..].to_vec()))
|
||||
}
|
||||
|
||||
async fn sync_player(session: &mut PlayerSession, json: FreesrData) {
|
||||
// clear relics & lightcones
|
||||
session
|
||||
.send(PlayerSyncScNotify {
|
||||
del_equipment_list: (2000..3500).collect(),
|
||||
del_relic_list: (1..2000).collect(),
|
||||
..Default::default()
|
||||
})
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
// Sync avatars
|
||||
session
|
||||
.send(PlayerSyncScNotify {
|
||||
avatar_sync: Some(AvatarSync {
|
||||
avatar_list: json
|
||||
.avatars
|
||||
.values()
|
||||
.map(|avatar| avatar.to_avatar_proto(Option::None, vec![]))
|
||||
.collect::<Vec<_>>(),
|
||||
}),
|
||||
multi_path_avatar_type_info_list: json.get_multi_path_info(),
|
||||
..Default::default()
|
||||
})
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
// Sync new relics
|
||||
session
|
||||
.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(),
|
||||
..Default::default()
|
||||
})
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
// Sync new lightcones
|
||||
session
|
||||
.send(PlayerSyncScNotify {
|
||||
avatar_sync: Some(AvatarSync {
|
||||
avatar_list: json
|
||||
.avatars
|
||||
.values()
|
||||
.map(|avatar| {
|
||||
avatar.to_avatar_proto(
|
||||
json.lightcones
|
||||
.iter()
|
||||
.find(|v| v.equip_avatar == avatar.avatar_id),
|
||||
json.relics
|
||||
.iter()
|
||||
.filter(|v| v.equip_avatar == avatar.avatar_id)
|
||||
.collect(),
|
||||
)
|
||||
})
|
||||
.collect(),
|
||||
}),
|
||||
..Default::default()
|
||||
})
|
||||
.await
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
@@ -1,13 +1,18 @@
|
||||
use proto::*;
|
||||
use proto::{get_big_data_all_recommend_sc_rsp::RecommendType, *};
|
||||
|
||||
use crate::net::{tools::FreesrData, PlayerSession};
|
||||
use crate::net::PlayerSession;
|
||||
|
||||
use super::UNLOCKED_AVATARS;
|
||||
|
||||
pub async fn on_get_bag_cs_req(
|
||||
_session: &mut PlayerSession,
|
||||
session: &mut PlayerSession,
|
||||
_req: &GetBagCsReq,
|
||||
res: &mut GetBagScRsp,
|
||||
) {
|
||||
let player = FreesrData::load().await;
|
||||
let Some(player) = session.json_data.get() else {
|
||||
tracing::error!("data is not set!");
|
||||
return;
|
||||
};
|
||||
|
||||
res.equipment_list = player
|
||||
.lightcones
|
||||
@@ -65,10 +70,37 @@ pub async fn on_take_off_equipment_cs_req(
|
||||
) {
|
||||
}
|
||||
|
||||
// pub async fn on_relic_recommend_cs_req(
|
||||
// _: &mut PlayerSession,
|
||||
// req: &RelicRecommendCsReq,
|
||||
// res: &mut RelicRecommendScRsp,
|
||||
// ) {
|
||||
// res.avatar_id = req.avatar_id
|
||||
// }
|
||||
pub async fn on_get_big_data_all_recommend_cs_req(
|
||||
_: &mut PlayerSession,
|
||||
req: &GetBigDataAllRecommendCsReq,
|
||||
res: &mut GetBigDataAllRecommendScRsp,
|
||||
) {
|
||||
res.big_data_recommend_type = req.big_data_recommend_type;
|
||||
|
||||
match req.big_data_recommend_type() {
|
||||
BigDataRecommendType::RelicAvatar => {
|
||||
res.recommend_type = Some(RecommendType::RelicAvatar(BigDataRecommendRelicAvatar {
|
||||
recommended_avatar_info_list: UNLOCKED_AVATARS
|
||||
.into_iter()
|
||||
.map(|recommend_avatar_id| RecomendedAvatarInfo {
|
||||
avatar_id_list: vec![],
|
||||
recommend_avatar_id,
|
||||
relic_set_id: 0,
|
||||
})
|
||||
.collect(),
|
||||
}))
|
||||
}
|
||||
BigDataRecommendType::AvatarRelic => {
|
||||
res.recommend_type = Some(RecommendType::AvatarRelic(BigDataRecommendAvatarRelic {
|
||||
recomended_relic_info_list: UNLOCKED_AVATARS
|
||||
.into_iter()
|
||||
.map(|avatar_id| RecommendedRelicInfo {
|
||||
avatar_id,
|
||||
..Default::default()
|
||||
})
|
||||
.collect(),
|
||||
}))
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,38 +1,45 @@
|
||||
use common::sr_tools::AvatarJson;
|
||||
use scene_entity_info::Entity;
|
||||
use scene_entity_refresh_info::RefreshType;
|
||||
|
||||
use crate::net::tools::{self, AvatarJson, FreesrData};
|
||||
|
||||
use super::*;
|
||||
|
||||
pub async fn on_get_all_lineup_data_cs_req(
|
||||
_session: &mut PlayerSession,
|
||||
session: &mut PlayerSession,
|
||||
_body: &GetAllLineupDataCsReq,
|
||||
res: &mut GetAllLineupDataScRsp,
|
||||
) {
|
||||
let player = tools::FreesrData::load().await;
|
||||
let Some(player) = session.json_data.get() else {
|
||||
tracing::error!("data is not set!");
|
||||
return;
|
||||
};
|
||||
|
||||
res.lineup_list = vec![LineupInfo {
|
||||
extra_lineup_type: ExtraLineupType::LineupNone.into(),
|
||||
name: "Squad 1".to_string(),
|
||||
mp: 5,
|
||||
max_mp: 5,
|
||||
avatar_list: AvatarJson::to_lineup_avatars(&player),
|
||||
avatar_list: AvatarJson::to_lineup_avatars(player),
|
||||
..Default::default()
|
||||
}];
|
||||
}
|
||||
|
||||
pub async fn on_get_cur_lineup_data_cs_req(
|
||||
_session: &mut PlayerSession,
|
||||
session: &mut PlayerSession,
|
||||
_body: &GetCurLineupDataCsReq,
|
||||
res: &mut GetCurLineupDataScRsp,
|
||||
) {
|
||||
let player = tools::FreesrData::load().await;
|
||||
let Some(player) = session.json_data.get() else {
|
||||
tracing::error!("data is not set!");
|
||||
return;
|
||||
};
|
||||
|
||||
let lineup = LineupInfo {
|
||||
extra_lineup_type: ExtraLineupType::LineupNone.into(),
|
||||
name: "Squad 1".to_string(),
|
||||
mp: 5,
|
||||
max_mp: 5,
|
||||
avatar_list: AvatarJson::to_lineup_avatars(&player),
|
||||
avatar_list: AvatarJson::to_lineup_avatars(player),
|
||||
is_virtual: false,
|
||||
plane_id: 0,
|
||||
..Default::default()
|
||||
@@ -46,19 +53,26 @@ pub async fn on_join_lineup_cs_req(
|
||||
body: &JoinLineupCsReq,
|
||||
_res: &mut JoinLineupScRsp,
|
||||
) {
|
||||
let mut player = tools::FreesrData::load().await;
|
||||
let Some(player) = session.json_data.get_mut() else {
|
||||
tracing::error!("data is not set!");
|
||||
return;
|
||||
};
|
||||
|
||||
let lineups = &mut player.lineups;
|
||||
lineups.insert(body.slot, body.base_avatar_id);
|
||||
player.save_lineup().await;
|
||||
refresh_lineup(session, &player).await;
|
||||
player.save_persistent().await;
|
||||
refresh_lineup(session).await;
|
||||
}
|
||||
|
||||
pub async fn on_replace_lineup_cs_req(
|
||||
_session: &mut PlayerSession,
|
||||
session: &mut PlayerSession,
|
||||
req: &ReplaceLineupCsReq,
|
||||
_res: &mut ReplaceLineupScRsp,
|
||||
) {
|
||||
let mut player = tools::FreesrData::load().await;
|
||||
let Some(player) = session.json_data.get_mut() else {
|
||||
tracing::error!("data is not set!");
|
||||
return;
|
||||
};
|
||||
|
||||
let lineups = &mut player.lineups;
|
||||
for (slot, avatar_id) in &mut *lineups {
|
||||
@@ -68,8 +82,8 @@ pub async fn on_replace_lineup_cs_req(
|
||||
*avatar_id = 0;
|
||||
}
|
||||
}
|
||||
player.save_lineup().await;
|
||||
refresh_lineup(_session, &player).await;
|
||||
player.save_persistent().await;
|
||||
refresh_lineup(session).await;
|
||||
}
|
||||
|
||||
pub async fn on_quit_lineup_cs_req(
|
||||
@@ -79,7 +93,12 @@ pub async fn on_quit_lineup_cs_req(
|
||||
) {
|
||||
}
|
||||
|
||||
async fn refresh_lineup(session: &mut PlayerSession, player: &FreesrData) {
|
||||
async fn refresh_lineup(session: &mut PlayerSession) {
|
||||
let Some(player) = session.json_data.get_mut() else {
|
||||
tracing::error!("data is not set!");
|
||||
return;
|
||||
};
|
||||
|
||||
let lineup = LineupInfo {
|
||||
extra_lineup_type: ExtraLineupType::LineupNone.into(),
|
||||
name: "Squad 1".to_string(),
|
||||
@@ -89,33 +108,37 @@ async fn refresh_lineup(session: &mut PlayerSession, player: &FreesrData) {
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
let new_entities = player
|
||||
.lineups
|
||||
.iter()
|
||||
.map(|(idx, v)| SceneEntityRefreshInfo {
|
||||
refresh_type: Some(RefreshType::AddEntity(SceneEntityInfo {
|
||||
entity: Some(Entity::Actor(SceneActorInfo {
|
||||
avatar_type: AvatarType::AvatarFormalType.into(),
|
||||
base_avatar_id: *v,
|
||||
map_layer: 0,
|
||||
uid: 25,
|
||||
})),
|
||||
entity_id: idx + 1,
|
||||
group_id: 0,
|
||||
inst_id: 0,
|
||||
..Default::default()
|
||||
})),
|
||||
})
|
||||
.collect();
|
||||
|
||||
let floor_id = player.scene.floor_id;
|
||||
|
||||
session
|
||||
.send(SceneGroupRefreshScNotify {
|
||||
group_refresh_info: vec![SceneGroupRefreshInfo {
|
||||
group_id: 0,
|
||||
state: 0,
|
||||
group_refresh_type: 0,
|
||||
refresh_entity: player
|
||||
.lineups
|
||||
.iter()
|
||||
.map(|(idx, v)| SceneEntityRefreshInfo {
|
||||
refresh_type: Some(RefreshType::AddEntity(SceneEntityInfo {
|
||||
entity: Some(Entity::Actor(SceneActorInfo {
|
||||
avatar_type: AvatarType::AvatarFormalType.into(),
|
||||
base_avatar_id: *v,
|
||||
map_layer: 0,
|
||||
uid: 25,
|
||||
})),
|
||||
entity_id: idx + 1,
|
||||
group_id: 0,
|
||||
inst_id: 0,
|
||||
..Default::default()
|
||||
})),
|
||||
})
|
||||
.collect(),
|
||||
group_refresh_type: SceneGroupRefreshType::Loaded.into(),
|
||||
refresh_entity: new_entities,
|
||||
bccgjihncdn: Vec::with_capacity(0),
|
||||
}],
|
||||
floor_id: 0, // TODO!
|
||||
floor_id,
|
||||
gfhglffhfbd: 0,
|
||||
})
|
||||
.await
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
use std::collections::HashMap;
|
||||
|
||||
use crate::net::tools::FreesrData;
|
||||
|
||||
use super::*;
|
||||
|
||||
pub async fn on_get_basic_info_cs_req(
|
||||
@@ -56,11 +54,14 @@ pub async fn on_player_login_finish_cs_req(
|
||||
}
|
||||
|
||||
pub async fn on_get_multi_path_avatar_info_cs_req(
|
||||
_session: &mut PlayerSession,
|
||||
session: &mut PlayerSession,
|
||||
_req: &GetMultiPathAvatarInfoCsReq,
|
||||
res: &mut GetMultiPathAvatarInfoScRsp,
|
||||
) {
|
||||
let json = FreesrData::load().await;
|
||||
let Some(json) = session.json_data.get() else {
|
||||
tracing::error!("data is not set!");
|
||||
return;
|
||||
};
|
||||
|
||||
res.current_multi_path_avatar_id = HashMap::from([
|
||||
(8001, json.main_character.get_type().into()),
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
use std::collections::HashMap;
|
||||
|
||||
use common::{
|
||||
resources::GAME_RES,
|
||||
sr_tools::{AvatarJson, Position},
|
||||
};
|
||||
use lazy_static::lazy_static;
|
||||
use scene_entity_info::Entity;
|
||||
use tokio::sync::Mutex;
|
||||
|
||||
use crate::{
|
||||
net::tools::{AvatarJson, FreesrData, Position},
|
||||
tools::resources::GAME_RES,
|
||||
util::{self},
|
||||
};
|
||||
use crate::util::{self};
|
||||
|
||||
use super::*;
|
||||
|
||||
@@ -17,21 +17,25 @@ pub async fn on_get_cur_scene_info_cs_req(
|
||||
_body: &GetCurSceneInfoCsReq,
|
||||
res: &mut GetCurSceneInfoScRsp,
|
||||
) {
|
||||
let mut player = FreesrData::load().await;
|
||||
let entry = player.scene.entry_id;
|
||||
let Some(player) = session.json_data.get() else {
|
||||
tracing::error!("data is not set!");
|
||||
return;
|
||||
};
|
||||
|
||||
let scene = load_scene(session, &mut player, entry, false, Option::<u32>::None).await;
|
||||
let default_scene = SceneInfo {
|
||||
game_mode_type: 3,
|
||||
entry_id: player.scene.entry_id,
|
||||
plane_id: player.scene.plane_id,
|
||||
floor_id: player.scene.floor_id,
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
let scene = load_scene(session, default_scene.entry_id, false, Option::<u32>::None).await;
|
||||
|
||||
res.scene = if let Ok(scene) = scene {
|
||||
Some(scene)
|
||||
} else {
|
||||
Some(SceneInfo {
|
||||
game_mode_type: 3,
|
||||
entry_id: player.scene.entry_id,
|
||||
plane_id: player.scene.plane_id,
|
||||
floor_id: player.scene.floor_id,
|
||||
..Default::default()
|
||||
})
|
||||
Some(default_scene)
|
||||
};
|
||||
}
|
||||
|
||||
@@ -40,17 +44,9 @@ pub async fn on_enter_scene_cs_req(
|
||||
req: &EnterSceneCsReq,
|
||||
res: &mut EnterSceneScRsp,
|
||||
) {
|
||||
let mut player = FreesrData::load().await;
|
||||
|
||||
if load_scene(
|
||||
session,
|
||||
&mut player,
|
||||
req.entry_id,
|
||||
true,
|
||||
Some(req.teleport_id),
|
||||
)
|
||||
.await
|
||||
.is_err()
|
||||
if load_scene(session, req.entry_id, true, Some(req.teleport_id))
|
||||
.await
|
||||
.is_err()
|
||||
{
|
||||
res.retcode = Retcode::RetSceneEntryIdNotMatch as u32;
|
||||
};
|
||||
@@ -126,11 +122,15 @@ lazy_static! {
|
||||
}
|
||||
|
||||
pub async fn on_scene_entity_move_cs_req(
|
||||
_session: &mut PlayerSession,
|
||||
session: &mut PlayerSession,
|
||||
req: &SceneEntityMoveCsReq,
|
||||
_res: &mut SceneEntityMoveScRsp,
|
||||
) {
|
||||
let mut player = FreesrData::load().await;
|
||||
let Some(player) = session.json_data.get_mut() else {
|
||||
tracing::error!("data is not set!");
|
||||
return;
|
||||
};
|
||||
|
||||
let mut timestamp = NEXT_SCENE_SAVE.lock().await;
|
||||
|
||||
if util::cur_timestamp_ms() <= *timestamp {
|
||||
@@ -157,7 +157,7 @@ pub async fn on_scene_entity_move_cs_req(
|
||||
}
|
||||
}
|
||||
|
||||
player.save().await;
|
||||
player.save_persistent().await;
|
||||
}
|
||||
|
||||
pub async fn on_get_entered_scene_cs_req(
|
||||
@@ -187,11 +187,15 @@ pub async fn on_get_entered_scene_cs_req(
|
||||
|
||||
async fn load_scene(
|
||||
session: &mut PlayerSession,
|
||||
json: &mut FreesrData,
|
||||
entry_id: u32,
|
||||
is_enter_scene: bool,
|
||||
teleport_id: Option<u32>,
|
||||
) -> Result<SceneInfo> {
|
||||
let Some(json) = session.json_data.get_mut() else {
|
||||
tracing::error!("data is not set!");
|
||||
return Err(anyhow::format_err!("data is not set!"));
|
||||
};
|
||||
|
||||
let (name, scene) = GAME_RES
|
||||
.level_output_configs
|
||||
.get(&entry_id)
|
||||
@@ -375,7 +379,7 @@ async fn load_scene(
|
||||
.map(|(slot, avatar_id)| SceneEntityInfo {
|
||||
inst_id: 0,
|
||||
entity_id: (*slot) + 1,
|
||||
motion: Some(player_pos.clone()),
|
||||
motion: Some(player_pos),
|
||||
entity: Some(Entity::Actor(SceneActorInfo {
|
||||
avatar_type: AvatarType::AvatarFormalType.into(),
|
||||
base_avatar_id: *avatar_id,
|
||||
@@ -388,14 +392,6 @@ async fn load_scene(
|
||||
});
|
||||
|
||||
if is_enter_scene {
|
||||
session
|
||||
.send(EnterSceneByServerScNotify {
|
||||
scene: Some(scene_info.clone()),
|
||||
lineup: Some(lineup_info),
|
||||
..Default::default()
|
||||
})
|
||||
.await?;
|
||||
|
||||
json.scene.entry_id = entry_id;
|
||||
json.scene.floor_id = floor_id;
|
||||
json.scene.plane_id = plane_id;
|
||||
@@ -403,7 +399,16 @@ async fn load_scene(
|
||||
json.position.y = json_pos.y;
|
||||
json.position.z = json_pos.z;
|
||||
json.position.rot_y = json_pos.rot_y;
|
||||
json.save().await;
|
||||
|
||||
json.save_persistent().await;
|
||||
|
||||
session
|
||||
.send(EnterSceneByServerScNotify {
|
||||
scene: Some(scene_info.clone()),
|
||||
lineup: Some(lineup_info),
|
||||
..Default::default()
|
||||
})
|
||||
.await?;
|
||||
}
|
||||
|
||||
Ok(scene_info)
|
||||
|
||||
@@ -3,7 +3,6 @@ pub mod gateway;
|
||||
mod handlers;
|
||||
mod packet;
|
||||
mod session;
|
||||
mod tools;
|
||||
|
||||
pub use packet::NetPacket;
|
||||
pub use session::PlayerSession;
|
||||
|
||||
@@ -27,8 +27,8 @@ use proto::{
|
||||
CmdWaypointType::*, CmdWolfBroType::*,
|
||||
};
|
||||
|
||||
use super::handlers::*;
|
||||
use super::PlayerSession;
|
||||
use super::handlers::*;
|
||||
|
||||
const HEAD_MAGIC: u32 = 0x9D74C714;
|
||||
const TAIL_MAGIC: u32 = 0xD7A152C8;
|
||||
@@ -219,6 +219,6 @@ trait_handler! {
|
||||
GetGachaInfo;
|
||||
DoGacha;
|
||||
PlayerLoginFinish;
|
||||
// RelicRecommend;
|
||||
GetBigDataAllRecommend;
|
||||
// SetClientPaused;
|
||||
}
|
||||
|
||||
@@ -2,66 +2,73 @@ use std::{
|
||||
io::Error,
|
||||
net::SocketAddr,
|
||||
pin::Pin,
|
||||
sync::Arc,
|
||||
sync::{Arc, OnceLock},
|
||||
task::{Context, Poll},
|
||||
};
|
||||
|
||||
use anyhow::Result;
|
||||
use common::sr_tools::FreesrData;
|
||||
use mhy_kcp::Kcp;
|
||||
use prost::Message;
|
||||
use proto::{CmdID, CmdPlayerType};
|
||||
use tokio::{io::AsyncWrite, net::UdpSocket, sync::Mutex};
|
||||
use proto::{AvatarSync, CmdID, CmdPlayerType, PlayerSyncScNotify};
|
||||
use tokio::{
|
||||
io::AsyncWrite,
|
||||
net::UdpSocket,
|
||||
sync::{Mutex, watch},
|
||||
};
|
||||
|
||||
use crate::util;
|
||||
|
||||
use super::{packet::CommandHandler, NetPacket};
|
||||
use super::{NetPacket, packet::CommandHandler};
|
||||
|
||||
struct RemoteEndPoint {
|
||||
socket: Arc<UdpSocket>,
|
||||
addr: SocketAddr,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct PlayerSession {
|
||||
pub token: u32,
|
||||
kcp: Arc<Mutex<Kcp<RemoteEndPoint>>>,
|
||||
start_time: u64,
|
||||
pub is_destroyed: bool,
|
||||
pub shutdown_tx: watch::Sender<()>,
|
||||
pub shutdown_rx: watch::Receiver<()>,
|
||||
pub json_data: OnceLock<FreesrData>,
|
||||
}
|
||||
|
||||
impl PlayerSession {
|
||||
pub fn new(socket: Arc<UdpSocket>, addr: SocketAddr, conv: u32, token: u32) -> Self {
|
||||
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(),
|
||||
is_destroyed: false,
|
||||
json_data: OnceLock::new(),
|
||||
shutdown_rx,
|
||||
shutdown_tx,
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn consume(&mut self, buffer: &[u8]) -> Result<()> {
|
||||
{
|
||||
let mut kcp = self.kcp.lock().await;
|
||||
kcp.input(buffer)?;
|
||||
kcp.async_update(self.session_time() as u32).await?;
|
||||
kcp.async_flush().await?;
|
||||
}
|
||||
let mut kcp = self.kcp.lock().await;
|
||||
kcp.input(buffer)?;
|
||||
kcp.async_update(self.session_time() as u32).await?;
|
||||
kcp.async_flush().await?;
|
||||
|
||||
let mut packets = Vec::new();
|
||||
let mut buf = [0; 24756];
|
||||
while let Ok(length) = self.kcp.lock().await.recv(&mut buf) {
|
||||
while let Ok(length) = kcp.recv(&mut buf) {
|
||||
packets.push(NetPacket::from(&buf[..length]));
|
||||
}
|
||||
|
||||
drop(kcp);
|
||||
|
||||
for packet in packets {
|
||||
// TODO: Temporary fix
|
||||
if packet.cmd_type == CmdPlayerType::CmdPlayerLogoutCsReq as u16 {
|
||||
self.is_destroyed = true;
|
||||
tracing::info!("Player logged out");
|
||||
let _ = self.shutdown_tx.send(());
|
||||
return Ok(());
|
||||
};
|
||||
Self::on_message(self, packet.cmd_type, packet.body).await?;
|
||||
@@ -78,6 +85,7 @@ impl PlayerSession {
|
||||
pub async fn send(&self, body: impl Message + CmdID) -> Result<()> {
|
||||
let mut buf = Vec::new();
|
||||
body.encode(&mut buf)?;
|
||||
tracing::info!("sent packet with CmdID: {}", body.get_cmd_id());
|
||||
|
||||
let payload: Vec<u8> = NetPacket {
|
||||
cmd_type: body.get_cmd_id(),
|
||||
@@ -104,6 +112,74 @@ impl PlayerSession {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn sync_player(&self) {
|
||||
let Some(json) = self.json_data.get() else {
|
||||
tracing::error!("data is not init!");
|
||||
return;
|
||||
};
|
||||
|
||||
// clear relics & lightcones
|
||||
self.send(PlayerSyncScNotify {
|
||||
del_equipment_list: (2000..3500).collect(),
|
||||
del_relic_list: (1..2000).collect(),
|
||||
..Default::default()
|
||||
})
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
// Sync avatars
|
||||
self.send(PlayerSyncScNotify {
|
||||
avatar_sync: Some(AvatarSync {
|
||||
avatar_list: json
|
||||
.avatars
|
||||
.values()
|
||||
.map(|avatar| avatar.to_avatar_proto(Option::None, vec![]))
|
||||
.collect::<Vec<_>>(),
|
||||
}),
|
||||
multi_path_avatar_type_info_list: json.get_multi_path_info(),
|
||||
..Default::default()
|
||||
})
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
// 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(),
|
||||
..Default::default()
|
||||
})
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
// Sync new lightcones
|
||||
self.send(PlayerSyncScNotify {
|
||||
avatar_sync: Some(AvatarSync {
|
||||
avatar_list: json
|
||||
.avatars
|
||||
.values()
|
||||
.map(|avatar| {
|
||||
avatar.to_avatar_proto(
|
||||
json.lightcones
|
||||
.iter()
|
||||
.find(|v| v.equip_avatar == avatar.avatar_id),
|
||||
json.relics
|
||||
.iter()
|
||||
.filter(|v| v.equip_avatar == avatar.avatar_id)
|
||||
.collect(),
|
||||
)
|
||||
})
|
||||
.collect(),
|
||||
}),
|
||||
..Default::default()
|
||||
})
|
||||
.await
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
fn session_time(&self) -> u64 {
|
||||
util::cur_timestamp_secs() - self.start_time
|
||||
}
|
||||
|
||||
@@ -1,773 +0,0 @@
|
||||
use proto::*;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::collections::{BTreeMap, HashMap};
|
||||
|
||||
// AVATAR
|
||||
#[derive(Serialize, Deserialize, Debug, Clone)]
|
||||
pub struct AvatarJson {
|
||||
#[serde(alias = "ownerUid")]
|
||||
pub owner_uid: u32,
|
||||
#[serde(alias = "avatarId")]
|
||||
pub avatar_id: u32,
|
||||
pub data: AvatarData,
|
||||
pub level: u32,
|
||||
pub promotion: u32,
|
||||
#[serde(alias = "use_technique")]
|
||||
#[serde(alias = "useTechnique")]
|
||||
pub techniques: Vec<u32>,
|
||||
#[serde(alias = "spValue")]
|
||||
pub sp_value: Option<u32>,
|
||||
#[serde(alias = "spMax")]
|
||||
pub sp_max: Option<u32>,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Clone)]
|
||||
pub struct AvatarData {
|
||||
pub rank: u32,
|
||||
pub skills: HashMap<u32, u32>,
|
||||
}
|
||||
|
||||
impl AvatarJson {
|
||||
pub fn to_avatar_proto(&self, lightcone: Option<&Lightcone>, relics: Vec<&Relic>) -> Avatar {
|
||||
// TODO: HARDCODED
|
||||
let base_avatar_id = if self.avatar_id > 8000 {
|
||||
8001
|
||||
} else if self.avatar_id == 1001 || self.avatar_id == 1224 {
|
||||
1001
|
||||
} else {
|
||||
self.avatar_id
|
||||
};
|
||||
|
||||
Avatar {
|
||||
base_avatar_id,
|
||||
level: self.level,
|
||||
promotion: self.promotion,
|
||||
rank: self.data.rank,
|
||||
skilltree_list: self
|
||||
.data
|
||||
.skills
|
||||
.iter()
|
||||
.map(|v| AvatarSkillTree {
|
||||
point_id: *v.0,
|
||||
level: *v.1,
|
||||
})
|
||||
.collect::<Vec<_>>(),
|
||||
equipment_unique_id: if let Some(lc) = lightcone {
|
||||
// TODO: HARDCODED LIGHTCONE ID
|
||||
2000 + lc.internal_uid
|
||||
} else {
|
||||
0
|
||||
},
|
||||
first_met_timestamp: 1712924677,
|
||||
equip_relic_list: relics
|
||||
.iter()
|
||||
.map(|v| v.to_equipment_relic_proto())
|
||||
.collect::<Vec<_>>(),
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn to_battle_avatar_proto(
|
||||
&self,
|
||||
index: u32,
|
||||
lightcone: Option<&Lightcone>,
|
||||
relics: Vec<&Relic>,
|
||||
) -> (BattleAvatar, Vec<BattleBuff>) {
|
||||
let battle_avatar = BattleAvatar {
|
||||
index,
|
||||
avatar_type: AvatarType::AvatarUpgradeAvailableType.into(),
|
||||
id: self.avatar_id,
|
||||
level: self.level,
|
||||
rank: self.data.rank,
|
||||
skilltree_list: self
|
||||
.data
|
||||
.skills
|
||||
.iter()
|
||||
.map(|v| AvatarSkillTree {
|
||||
point_id: *v.0,
|
||||
level: *v.1,
|
||||
})
|
||||
.collect::<Vec<_>>(),
|
||||
equipment_list: if let Some(lc) = lightcone {
|
||||
vec![lc.to_battle_equipment_proto()]
|
||||
} else {
|
||||
vec![]
|
||||
},
|
||||
hp: 10_000,
|
||||
promotion: self.promotion,
|
||||
relic_list: relics
|
||||
.iter()
|
||||
.map(|v| v.to_battle_relic_proto())
|
||||
.collect::<Vec<_>>(),
|
||||
world_level: 6,
|
||||
sp_bar: Some(SpBarInfo {
|
||||
cur_sp: self.sp_value.unwrap_or(10_000),
|
||||
max_sp: self.sp_max.unwrap_or(10_000),
|
||||
}),
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
let mut battle_buff = Vec::<BattleBuff>::new();
|
||||
for buff_id in &self.techniques {
|
||||
battle_buff.push(BattleBuff {
|
||||
wave_flag: 0xffffffff,
|
||||
owner_id: index,
|
||||
level: 1,
|
||||
id: *buff_id,
|
||||
dynamic_values: HashMap::from([(String::from("SkillIndex"), 2.0)]),
|
||||
..Default::default()
|
||||
});
|
||||
}
|
||||
|
||||
(battle_avatar, battle_buff)
|
||||
}
|
||||
|
||||
pub fn to_lineup_avatar_proto(&self, slot: u32) -> LineupAvatar {
|
||||
LineupAvatar {
|
||||
id: self.avatar_id,
|
||||
hp: 10_000,
|
||||
satiety: 100,
|
||||
avatar_type: AvatarType::AvatarFormalType.into(),
|
||||
sp_bar: Some(SpBarInfo {
|
||||
cur_sp: self.sp_value.unwrap_or(10_000),
|
||||
max_sp: self.sp_max.unwrap_or(10_000),
|
||||
}),
|
||||
slot,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn to_lineup_avatars(player: &FreesrData) -> Vec<LineupAvatar> {
|
||||
let avatar_ids = player
|
||||
.avatars
|
||||
.values()
|
||||
.map(|v| &v.avatar_id)
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
player
|
||||
.lineups
|
||||
.iter()
|
||||
.filter(|(slot, v)| **slot < 4 && v > &&0 && avatar_ids.contains(v))
|
||||
.map(|(slot, avatar_id)| {
|
||||
player
|
||||
.avatars
|
||||
.get(avatar_id)
|
||||
.unwrap()
|
||||
.to_lineup_avatar_proto(*slot)
|
||||
})
|
||||
.collect::<Vec<LineupAvatar>>()
|
||||
}
|
||||
|
||||
pub fn to_lineup_info(lineups: &BTreeMap<u32, u32>) -> LineupInfo {
|
||||
let mut lineup_info = LineupInfo {
|
||||
extra_lineup_type: ExtraLineupType::LineupNone.into(),
|
||||
name: "Squad 1".to_string(),
|
||||
mp: 5,
|
||||
max_mp: 5,
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
for id in lineups.values() {
|
||||
if *id == 0 {
|
||||
continue;
|
||||
}
|
||||
lineup_info.avatar_list.push(LineupAvatar {
|
||||
id: *id,
|
||||
hp: 10_000,
|
||||
satiety: 100,
|
||||
avatar_type: AvatarType::AvatarFormalType.into(),
|
||||
sp_bar: Some(SpBarInfo {
|
||||
cur_sp: 10_000,
|
||||
max_sp: 10_000,
|
||||
}),
|
||||
slot: lineup_info.avatar_list.len() as u32,
|
||||
});
|
||||
}
|
||||
|
||||
lineup_info
|
||||
}
|
||||
}
|
||||
|
||||
// LIGHTCONE
|
||||
#[derive(Serialize, Deserialize, Debug, Clone)]
|
||||
pub struct Lightcone {
|
||||
pub level: u32,
|
||||
#[serde(alias = "itemId")]
|
||||
pub item_id: u32,
|
||||
#[serde(alias = "equipAvatar")]
|
||||
pub equip_avatar: u32,
|
||||
pub rank: u32,
|
||||
pub promotion: u32,
|
||||
#[serde(alias = "internalUid")]
|
||||
pub internal_uid: u32,
|
||||
}
|
||||
|
||||
impl Lightcone {
|
||||
pub fn to_equipment_proto(&self) -> Equipment {
|
||||
Equipment {
|
||||
equip_avatar_id: self.equip_avatar,
|
||||
exp: 0,
|
||||
is_protected: false,
|
||||
level: self.level,
|
||||
promotion: self.promotion,
|
||||
rank: self.rank,
|
||||
tid: self.item_id,
|
||||
// ?
|
||||
unique_id: 2000 + self.internal_uid,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn to_battle_equipment_proto(&self) -> BattleEquipment {
|
||||
BattleEquipment {
|
||||
id: self.item_id,
|
||||
level: self.level,
|
||||
promotion: self.promotion,
|
||||
rank: self.rank,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// RELIC
|
||||
#[derive(Debug, Serialize, Deserialize, Clone)]
|
||||
pub struct Relic {
|
||||
pub level: u32,
|
||||
#[serde(alias = "relicId")]
|
||||
pub relic_id: u32,
|
||||
#[serde(alias = "relicSetId")]
|
||||
pub relic_set_id: u32,
|
||||
#[serde(alias = "mainAffixId")]
|
||||
pub main_affix_id: u32,
|
||||
#[serde(alias = "subAffixes")]
|
||||
pub sub_affixes: Vec<SubAffix>,
|
||||
#[serde(alias = "internalUid")]
|
||||
pub internal_uid: u32,
|
||||
#[serde(alias = "equipAvatar")]
|
||||
pub equip_avatar: u32,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, Default, Clone)]
|
||||
pub struct SubAffix {
|
||||
#[serde(alias = "subAffixId")]
|
||||
pub sub_affix_id: u32,
|
||||
pub count: u32,
|
||||
pub step: u32,
|
||||
}
|
||||
|
||||
impl Relic {
|
||||
pub fn to_relic_proto(&self) -> proto::Relic {
|
||||
proto::Relic {
|
||||
equip_avatar_id: self.equip_avatar,
|
||||
exp: 0,
|
||||
is_protected: false,
|
||||
level: self.level,
|
||||
main_affix_id: self.main_affix_id,
|
||||
tid: self.relic_id,
|
||||
// ?
|
||||
unique_id: 1 + self.internal_uid,
|
||||
sub_affix_list: self
|
||||
.sub_affixes
|
||||
.iter()
|
||||
.map(|v| RelicAffix {
|
||||
affix_id: v.sub_affix_id,
|
||||
cnt: v.count,
|
||||
step: v.step,
|
||||
})
|
||||
.collect::<Vec<_>>(),
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn to_battle_relic_proto(&self) -> BattleRelic {
|
||||
BattleRelic {
|
||||
id: self.relic_id,
|
||||
level: self.level,
|
||||
main_affix_id: self.main_affix_id,
|
||||
unique_id: self.internal_uid,
|
||||
sub_affix_list: self
|
||||
.sub_affixes
|
||||
.iter()
|
||||
.map(|v| RelicAffix {
|
||||
affix_id: v.sub_affix_id,
|
||||
cnt: v.count,
|
||||
step: v.step,
|
||||
})
|
||||
.collect::<Vec<_>>(),
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn to_equipment_relic_proto(&self) -> EquipRelic {
|
||||
EquipRelic {
|
||||
slot: self.relic_id % 10,
|
||||
// ?
|
||||
relic_unique_id: 1 + self.internal_uid,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MONSTER
|
||||
#[derive(Debug, Serialize, Deserialize, Clone, Default)]
|
||||
pub struct Monster {
|
||||
pub level: u32,
|
||||
#[serde(alias = "monsterId")]
|
||||
pub monster_id: u32,
|
||||
#[serde(default)]
|
||||
pub max_hp: u32,
|
||||
}
|
||||
|
||||
impl Monster {
|
||||
fn to_scene_monster_info(&self) -> SceneMonster {
|
||||
SceneMonster {
|
||||
monster_id: self.monster_id,
|
||||
max_hp: self.max_hp,
|
||||
cur_hp: self.max_hp,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn to_scene_monster_wave(wave_index: u32, monsters: &[Self]) -> SceneMonsterWave {
|
||||
let mut wave_index = wave_index;
|
||||
if wave_index < 1 {
|
||||
wave_index += 1;
|
||||
}
|
||||
|
||||
SceneMonsterWave {
|
||||
wave_id: wave_index, // wave indexx??
|
||||
|
||||
wave_param: Some(SceneMonsterWaveParam {
|
||||
// monster param
|
||||
level: monsters.iter().map(|v| v.level).max().unwrap_or(95),
|
||||
..Default::default()
|
||||
}),
|
||||
|
||||
monster_list: monsters
|
||||
.iter()
|
||||
.map(|v| v.to_scene_monster_info())
|
||||
.collect::<Vec<_>>(),
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn to_scene_monster_waves(monsters: &[Vec<Self>]) -> Vec<SceneMonsterWave> {
|
||||
monsters
|
||||
.iter()
|
||||
.enumerate()
|
||||
.map(|(i, v)| Self::to_scene_monster_wave(i as u32, v))
|
||||
.collect::<_>()
|
||||
}
|
||||
}
|
||||
|
||||
// BATTLE CONFIG
|
||||
#[derive(Debug, Serialize, Deserialize, Clone)]
|
||||
pub struct BattleConfig {
|
||||
pub battle_type: BattleType,
|
||||
pub monsters: Vec<Vec<Monster>>,
|
||||
pub blessings: Vec<BattleBuffJson>,
|
||||
pub stage_id: u32,
|
||||
pub cycle_count: u32,
|
||||
pub path_resonance_id: u32,
|
||||
pub custom_stats: Vec<SubAffix>,
|
||||
#[serde(default)]
|
||||
pub scepters: Vec<RogueMagicScepter>,
|
||||
}
|
||||
|
||||
impl Default for BattleConfig {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
battle_type: Default::default(),
|
||||
monsters: vec![vec![Monster {
|
||||
level: 60,
|
||||
monster_id: 3014022,
|
||||
max_hp: 0,
|
||||
}]],
|
||||
stage_id: 201012311,
|
||||
blessings: Default::default(),
|
||||
cycle_count: Default::default(),
|
||||
path_resonance_id: Default::default(),
|
||||
custom_stats: Default::default(),
|
||||
scepters: Default::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
|
||||
pub enum BattleType {
|
||||
#[serde(alias = "DEFAULT")]
|
||||
Default = 0,
|
||||
#[serde(alias = "MOC")]
|
||||
Moc = 1,
|
||||
PF = 2,
|
||||
SU = 3,
|
||||
AS = 4,
|
||||
}
|
||||
|
||||
impl Default for BattleType {
|
||||
fn default() -> Self {
|
||||
Self::Default
|
||||
}
|
||||
}
|
||||
|
||||
// BATTLE BUFFS
|
||||
#[derive(Debug, Serialize, Deserialize, Clone, Default)]
|
||||
pub struct BattleBuffJson {
|
||||
pub level: u32,
|
||||
pub id: u32,
|
||||
pub dynamic_key: Option<DynamicKey>,
|
||||
#[serde(default)]
|
||||
pub dynamic_values: Vec<DynamicKey>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, Clone, Default)]
|
||||
pub struct DynamicKey {
|
||||
pub key: String,
|
||||
pub value: u32,
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
impl BattleBuffJson {
|
||||
pub fn to_battle_buff_proto(&self) -> proto::BattleBuff {
|
||||
proto::BattleBuff {
|
||||
id: self.id,
|
||||
level: self.level,
|
||||
wave_flag: 0xffffffff,
|
||||
owner_id: 0xffffffff,
|
||||
dynamic_values: if let Some(dyn_key) = &self.dynamic_key {
|
||||
HashMap::from([(dyn_key.key.clone(), dyn_key.value as f32)])
|
||||
} else {
|
||||
Default::default()
|
||||
},
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, Clone, Eq, PartialEq, Default)]
|
||||
#[repr(u32)]
|
||||
pub enum RogueMagicComponentType {
|
||||
Passive = 3,
|
||||
#[default]
|
||||
Active = 4,
|
||||
Attach = 5,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, Clone, Default)]
|
||||
pub struct RogueMagicScepter {
|
||||
pub level: u32,
|
||||
pub id: u32,
|
||||
pub components: Vec<RogueMagicComponent>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, Clone, Default)]
|
||||
pub struct RogueMagicComponent {
|
||||
pub id: u32,
|
||||
pub level: u32,
|
||||
pub component_type: RogueMagicComponentType,
|
||||
}
|
||||
|
||||
// SCENE
|
||||
#[derive(Serialize, Deserialize, Debug, Clone)]
|
||||
pub struct Scene {
|
||||
pub plane_id: u32,
|
||||
pub floor_id: u32,
|
||||
pub entry_id: u32,
|
||||
}
|
||||
|
||||
impl Default for Scene {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
entry_id: 2032101,
|
||||
plane_id: 20321,
|
||||
floor_id: 20321001,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Position
|
||||
#[derive(Debug, Serialize, Deserialize, Clone, Default)]
|
||||
pub struct Position {
|
||||
pub x: i32,
|
||||
pub y: i32,
|
||||
pub z: i32,
|
||||
pub rot_y: i32,
|
||||
}
|
||||
|
||||
impl Position {
|
||||
#[allow(unused)]
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.x == 0 && self.y == 0 && self.z == 0
|
||||
}
|
||||
|
||||
pub fn to_motion(&self) -> MotionInfo {
|
||||
MotionInfo {
|
||||
rot: Some(Vector {
|
||||
x: 0,
|
||||
y: self.rot_y,
|
||||
z: 0,
|
||||
}),
|
||||
pos: Some(Vector {
|
||||
x: self.x,
|
||||
y: self.y,
|
||||
z: self.z,
|
||||
}),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// FREESR-DATA.json
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
pub struct FreesrData {
|
||||
pub lightcones: Vec<Lightcone>,
|
||||
pub relics: Vec<Relic>,
|
||||
pub avatars: HashMap<u32, AvatarJson>,
|
||||
#[serde(default)]
|
||||
pub battle_config: BattleConfig,
|
||||
|
||||
#[serde(default, skip_serializing)]
|
||||
pub lineups: BTreeMap<u32, u32>,
|
||||
#[serde(default, skip_serializing)]
|
||||
pub position: Position,
|
||||
#[serde(default, skip_serializing)]
|
||||
pub scene: Scene,
|
||||
|
||||
#[serde(skip_serializing, skip_deserializing)]
|
||||
pub main_character: MultiPathAvatar,
|
||||
#[serde(skip_serializing, skip_deserializing)]
|
||||
pub march_type: MultiPathAvatar,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
pub struct Persistent {
|
||||
#[serde(default)]
|
||||
pub lineups: BTreeMap<u32, u32>,
|
||||
#[serde(default)]
|
||||
pub position: Position,
|
||||
#[serde(default)]
|
||||
pub scene: Scene,
|
||||
pub main_character: MultiPathAvatar,
|
||||
pub march_type: MultiPathAvatar,
|
||||
}
|
||||
|
||||
impl Default for Persistent {
|
||||
fn default() -> Self {
|
||||
let mut lineups = BTreeMap::<u32, u32>::new();
|
||||
lineups.insert(0, 8001);
|
||||
lineups.insert(1, 0);
|
||||
lineups.insert(2, 0);
|
||||
lineups.insert(3, 0);
|
||||
|
||||
Self {
|
||||
lineups,
|
||||
position: Default::default(),
|
||||
main_character: MultiPathAvatar::FemaleRememberance,
|
||||
scene: Default::default(),
|
||||
march_type: MultiPathAvatar::MarchHunt,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone, Debug, Copy, PartialEq, Eq, Default)]
|
||||
#[repr(u32)]
|
||||
pub enum MultiPathAvatar {
|
||||
MalePyhsical = 8001,
|
||||
FemalePhysical = 8002,
|
||||
MalePreservation = 8003,
|
||||
FemalePreservation = 8004,
|
||||
MaleHarmony = 8005,
|
||||
FemaleHarmony = 8006,
|
||||
MaleRememberance = 8007,
|
||||
FemaleRememberance = 8008,
|
||||
MarchHunt = 1224,
|
||||
MarchPreservation = 1001,
|
||||
#[default]
|
||||
Unk = 0,
|
||||
}
|
||||
|
||||
impl From<u32> for MultiPathAvatar {
|
||||
fn from(value: u32) -> Self {
|
||||
match value {
|
||||
8001 => Self::MalePyhsical,
|
||||
8002 => Self::FemalePhysical,
|
||||
8003 => Self::MalePreservation,
|
||||
8004 => Self::FemalePreservation,
|
||||
8005 => Self::MaleHarmony,
|
||||
8006 => Self::FemaleHarmony,
|
||||
8007 => Self::MaleRememberance,
|
||||
8008 => Self::FemaleRememberance,
|
||||
1224 => Self::MarchHunt,
|
||||
1001 => Self::MarchPreservation,
|
||||
_ => Self::Unk,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<MultiPathAvatar> for u32 {
|
||||
fn from(value: MultiPathAvatar) -> Self {
|
||||
match value {
|
||||
MultiPathAvatar::MalePyhsical => 8001,
|
||||
MultiPathAvatar::FemalePhysical => 8002,
|
||||
MultiPathAvatar::MalePreservation => 8003,
|
||||
MultiPathAvatar::FemalePreservation => 8004,
|
||||
MultiPathAvatar::MaleHarmony => 8005,
|
||||
MultiPathAvatar::FemaleHarmony => 8006,
|
||||
MultiPathAvatar::MaleRememberance => 8007,
|
||||
MultiPathAvatar::FemaleRememberance => 8008,
|
||||
MultiPathAvatar::MarchHunt => 1224,
|
||||
MultiPathAvatar::MarchPreservation => 1001,
|
||||
_ => 8006,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl MultiPathAvatar {
|
||||
#[allow(unused)]
|
||||
pub fn get_gender(&self) -> Gender {
|
||||
if (*self as u32) < 8000 {
|
||||
Gender::None
|
||||
} else if *self as u32 % 2 == 1 {
|
||||
Gender::Man
|
||||
} else {
|
||||
Gender::Woman
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_type(&self) -> MultiPathAvatarType {
|
||||
match *self {
|
||||
MultiPathAvatar::MalePyhsical => MultiPathAvatarType::BoyWarriorType,
|
||||
MultiPathAvatar::FemalePhysical => MultiPathAvatarType::GirlWarriorType,
|
||||
MultiPathAvatar::MalePreservation => MultiPathAvatarType::BoyKnightType,
|
||||
MultiPathAvatar::FemalePreservation => MultiPathAvatarType::GirlKnightType,
|
||||
MultiPathAvatar::MaleHarmony => MultiPathAvatarType::BoyShamanType,
|
||||
MultiPathAvatar::FemaleHarmony => MultiPathAvatarType::GirlShamanType,
|
||||
MultiPathAvatar::MarchHunt => MultiPathAvatarType::Mar7thRogueType,
|
||||
MultiPathAvatar::MarchPreservation => MultiPathAvatarType::Mar7thKnightType,
|
||||
MultiPathAvatar::Unk => MultiPathAvatarType::None,
|
||||
MultiPathAvatar::MaleRememberance => MultiPathAvatarType::BoyMemoryType,
|
||||
MultiPathAvatar::FemaleRememberance => MultiPathAvatarType::GirlMemoryType,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_mc(&self) -> bool {
|
||||
(*self as u32) > 8000
|
||||
}
|
||||
|
||||
pub fn to_vec() -> Vec<MultiPathAvatar> {
|
||||
vec![
|
||||
Self::MalePyhsical,
|
||||
Self::FemalePhysical,
|
||||
Self::MalePreservation,
|
||||
Self::FemalePreservation,
|
||||
Self::MaleHarmony,
|
||||
Self::FemaleHarmony,
|
||||
Self::MaleRememberance,
|
||||
Self::FemaleRememberance,
|
||||
Self::MarchHunt,
|
||||
Self::MarchPreservation,
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
impl FreesrData {
|
||||
pub async fn load() -> Self {
|
||||
let mut json: FreesrData = tokio::fs::read_to_string("freesr-data.json")
|
||||
.await
|
||||
.map(|v| {
|
||||
serde_json::from_str::<FreesrData>(&v)
|
||||
.expect("freesr-data.json is broken, pls redownload")
|
||||
})
|
||||
.expect("failed to read freesr-data.json");
|
||||
|
||||
let json2: Persistent = serde_json::from_str(
|
||||
&tokio::fs::read_to_string("persistent")
|
||||
.await
|
||||
.unwrap_or_default(),
|
||||
)
|
||||
.unwrap_or_default();
|
||||
|
||||
json.lineups = json2.lineups;
|
||||
json.position = json2.position;
|
||||
json.scene = json2.scene;
|
||||
json.main_character = json2.main_character;
|
||||
json.march_type = json2.march_type;
|
||||
|
||||
json.verify_lineup().await;
|
||||
|
||||
if json.march_type as u32 > 8000 {
|
||||
json.march_type = MultiPathAvatar::MarchHunt;
|
||||
}
|
||||
|
||||
json
|
||||
}
|
||||
|
||||
async fn verify_lineup(&mut self) {
|
||||
if self.lineups.is_empty() {
|
||||
self.lineups = BTreeMap::<u32, u32>::from([(0, 8001), (1, 0), (2, 0), (3, 0)])
|
||||
} else if self.lineups.len() < 4 {
|
||||
for i in self.lineups.len()..4 {
|
||||
self.lineups.insert(i as u32, 0);
|
||||
}
|
||||
}
|
||||
self.save().await;
|
||||
}
|
||||
|
||||
pub async fn save_lineup(&self) {
|
||||
self.save().await;
|
||||
}
|
||||
|
||||
pub async fn save(&self) {
|
||||
let json = serde_json::to_string_pretty(&self).unwrap();
|
||||
let _ = tokio::fs::write("freesr-data.json", json.as_bytes()).await;
|
||||
let _ = tokio::fs::write(
|
||||
"persistent",
|
||||
serde_json::to_string_pretty(&Persistent {
|
||||
lineups: self.lineups.clone(),
|
||||
main_character: self.main_character,
|
||||
position: self.position.clone(),
|
||||
scene: self.scene.clone(),
|
||||
march_type: self.march_type,
|
||||
})
|
||||
.unwrap()
|
||||
.as_bytes(),
|
||||
)
|
||||
.await;
|
||||
}
|
||||
|
||||
pub fn get_multi_path_info(&self) -> Vec<MultiPathAvatarTypeInfo> {
|
||||
MultiPathAvatar::to_vec()
|
||||
.iter()
|
||||
.filter_map(|mp_type| {
|
||||
if mp_type.is_mc() && mp_type.get_gender() != self.main_character.get_gender() {
|
||||
return Option::None;
|
||||
}
|
||||
|
||||
let avatar_info = self.avatars.get(&((*mp_type) as u32))?;
|
||||
Some(MultiPathAvatarTypeInfo {
|
||||
avatar_id: *mp_type as i32,
|
||||
rank: avatar_info.data.rank,
|
||||
equip_relic_list: self
|
||||
.relics
|
||||
.iter()
|
||||
.filter(|relic| relic.equip_avatar == *mp_type as u32)
|
||||
.map(|relic| relic.to_equipment_relic_proto())
|
||||
.collect(),
|
||||
skilltree_list: avatar_info
|
||||
.data
|
||||
.skills
|
||||
.iter()
|
||||
.map(|(point_id, level)| AvatarSkillTree {
|
||||
point_id: *point_id,
|
||||
level: *level,
|
||||
})
|
||||
.collect(),
|
||||
path_equipment_id: self
|
||||
.lightcones
|
||||
.iter()
|
||||
.find(|v| v.equip_avatar == *mp_type as u32)
|
||||
.map(|v| {
|
||||
// TODO: HARDCODED LIGHTCONE ID
|
||||
2000 + v.internal_uid
|
||||
})
|
||||
.unwrap_or_default(),
|
||||
dressed_skin_id: 0,
|
||||
})
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
}
|
||||
@@ -1 +0,0 @@
|
||||
pub mod resources;
|
||||
@@ -1,146 +0,0 @@
|
||||
use serde::Deserialize;
|
||||
use std::{collections::HashMap, fs, sync::LazyLock};
|
||||
|
||||
#[derive(Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct Vector {
|
||||
pub x: i32,
|
||||
pub y: i32,
|
||||
pub z: i32,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Copy, PartialEq, Clone)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
#[repr(u32)]
|
||||
pub enum PropState {
|
||||
Closed = 0,
|
||||
Open = 1,
|
||||
Locked = 2,
|
||||
BridgeState1 = 3,
|
||||
BridgeState2 = 4,
|
||||
BridgeState3 = 5,
|
||||
BridgeState4 = 6,
|
||||
CheckPointDisable = 7,
|
||||
CheckPointEnable = 8,
|
||||
TriggerDisable = 9,
|
||||
TriggerEnable = 10,
|
||||
ChestLocked = 11,
|
||||
ChestClosed = 12,
|
||||
ChestUsed = 13,
|
||||
Elevator1 = 14,
|
||||
Elevator2 = 15,
|
||||
Elevator3 = 16,
|
||||
WaitActive = 17,
|
||||
EventClose = 18,
|
||||
EventOpen = 19,
|
||||
Hidden = 20,
|
||||
TeleportGate0 = 21,
|
||||
TeleportGate1 = 22,
|
||||
TeleportGate2 = 23,
|
||||
TeleportGate3 = 24,
|
||||
Destructed = 25,
|
||||
CustomState01 = 101,
|
||||
CustomState02 = 102,
|
||||
CustomState03 = 103,
|
||||
CustomState04 = 104,
|
||||
CustomState05 = 105,
|
||||
CustomState06 = 106,
|
||||
CustomState07 = 107,
|
||||
CustomState08 = 108,
|
||||
CustomState09 = 109,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Copy, Clone, PartialEq)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
#[repr(u32)]
|
||||
pub enum PlaneType {
|
||||
Unknown = 0,
|
||||
Maze = 2,
|
||||
Train = 3,
|
||||
Challenge = 4,
|
||||
Rogue = 5,
|
||||
Raid = 6,
|
||||
AetherDivide = 7,
|
||||
TrialActivity = 8,
|
||||
Town = 1,
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct SceneMonsterInfo {
|
||||
pub pos: Vector,
|
||||
pub rot: Vector,
|
||||
pub group_id: u32,
|
||||
pub inst_id: u32,
|
||||
pub monster_id: u32,
|
||||
pub event_id: u32,
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct SceneNpcInfo {
|
||||
pub pos: Vector,
|
||||
pub rot: Vector,
|
||||
pub group_id: u32,
|
||||
pub inst_id: u32,
|
||||
pub npc_id: u32,
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct ScenePropInfo {
|
||||
pub pos: Vector,
|
||||
pub rot: Vector,
|
||||
pub group_id: u32,
|
||||
pub inst_id: u32,
|
||||
pub prop_state: u32,
|
||||
pub prop_id: u32,
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct TeleportInfo {
|
||||
pub pos: Vector,
|
||||
pub rot: Vector,
|
||||
// pub group_id: u32,
|
||||
// pub inst_id: u32,
|
||||
// pub anchor_id: u32,
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct SceneData {
|
||||
pub npcs: Vec<SceneNpcInfo>,
|
||||
pub props: Vec<ScenePropInfo>,
|
||||
pub monsters: Vec<SceneMonsterInfo>,
|
||||
pub teleports: HashMap<u32, TeleportInfo>,
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct LevelOutputConfig {
|
||||
pub is_entered_scene_info: bool,
|
||||
pub scenes: HashMap<u32, SceneData>,
|
||||
pub plane_type: u32,
|
||||
pub world_id: u32,
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct AvatarConfig {
|
||||
pub weakness_buff_id: u32,
|
||||
// pub technique_buff_ids: Vec<u32>,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Default)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct JsonConfig {
|
||||
/// `entryid` -> `P[planeId]_F[floorId]` -> `groupId`
|
||||
pub level_output_configs: HashMap<u32, HashMap<String, LevelOutputConfig>>,
|
||||
pub avatar_configs: HashMap<u32, AvatarConfig>,
|
||||
pub map_default_entrance_map: HashMap<u32, u32>
|
||||
}
|
||||
|
||||
pub static GAME_RES: LazyLock<JsonConfig> = LazyLock::new(|| {
|
||||
serde_json::from_str::<JsonConfig>(&fs::read_to_string("res.json").unwrap()).unwrap()
|
||||
});
|
||||
Reference in New Issue
Block a user