mirror of
https://git.neonteam.dev/amizing/robinsr.git
synced 2025-03-12 03:28:30 -04:00
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:
parent
50a05a5cc2
commit
de22105514
3
.gitignore
vendored
3
.gitignore
vendored
@ -1,3 +1,6 @@
|
||||
target/
|
||||
Cargo.lock
|
||||
proto/StarRail.proto
|
||||
/prebuild
|
||||
/assets
|
||||
.vscode
|
||||
@ -1,5 +1,5 @@
|
||||
[workspace]
|
||||
members = ["gameserver", "proto", "sdkserver"]
|
||||
members = ["gameserver", "proto", "sdkserver", "common"]
|
||||
resolver = "3"
|
||||
|
||||
[workspace.package]
|
||||
@ -13,6 +13,7 @@ lazy_static = "1.4.0"
|
||||
|
||||
axum = "0.8.1"
|
||||
axum-server = "0.7.1"
|
||||
tower-http = "0.6.2"
|
||||
|
||||
env_logger = "0.11.3"
|
||||
|
||||
@ -32,6 +33,8 @@ prost-build = "0.13.5"
|
||||
|
||||
paste = "1.0.14"
|
||||
sysinfo = "0.33.1"
|
||||
notify = "8.0.0"
|
||||
notify-debouncer-mini = "0.6.0"
|
||||
|
||||
hex = "0.4.3"
|
||||
|
||||
@ -56,6 +59,7 @@ tracing-bunyan-formatter = "0.3.9"
|
||||
proto = { path = "proto/" }
|
||||
proto-derive = { path = "proto/proto-derive" }
|
||||
mhy-kcp = { path = "kcp/", features = ["tokio"] }
|
||||
common = { path = "common/" }
|
||||
|
||||
|
||||
[profile.release]
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
# Supported Version: 2.6.5x
|
||||
# Supported Version: 3.1.5x
|
||||
Run the game by clicking run.bat file.
|
||||
|
||||
Tool website: [https://srtools.pages.dev](https://srtools.pages.dev)
|
||||
|
||||
11
common/Cargo.toml
Normal file
11
common/Cargo.toml
Normal file
@ -0,0 +1,11 @@
|
||||
[package]
|
||||
name = "common"
|
||||
edition = "2024"
|
||||
version.workspace = true
|
||||
|
||||
[dependencies]
|
||||
proto.workspace = true
|
||||
serde.workspace = true
|
||||
serde_json.workspace = true
|
||||
tokio.workspace = true
|
||||
tracing.workspace = true
|
||||
@ -1 +1,2 @@
|
||||
pub mod resources;
|
||||
pub mod sr_tools;
|
||||
@ -696,6 +696,10 @@ impl FreesrData {
|
||||
json
|
||||
}
|
||||
|
||||
pub async fn update(&mut self) {
|
||||
*self = Self::load().await
|
||||
}
|
||||
|
||||
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)])
|
||||
@ -704,16 +708,10 @@ impl FreesrData {
|
||||
self.lineups.insert(i as u32, 0);
|
||||
}
|
||||
}
|
||||
self.save().await;
|
||||
self.save_persistent().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;
|
||||
pub async fn save_persistent(&self) {
|
||||
let _ = tokio::fs::write(
|
||||
"persistent",
|
||||
serde_json::to_string_pretty(&Persistent {
|
||||
@ -723,10 +721,10 @@ impl FreesrData {
|
||||
scene: self.scene.clone(),
|
||||
march_type: self.march_type,
|
||||
})
|
||||
.unwrap()
|
||||
.as_bytes(),
|
||||
.unwrap(),
|
||||
)
|
||||
.await;
|
||||
tracing::info!("persistent saved");
|
||||
}
|
||||
|
||||
pub fn get_multi_path_info(&self) -> Vec<MultiPathAvatarTypeInfo> {
|
||||
@ -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
|
||||
}
|
||||
|
||||
BIN
mhypbase.dll
BIN
mhypbase.dll
Binary file not shown.
18271
proto/out/_.rs
18271
proto/out/_.rs
File diff suppressed because it is too large
Load Diff
@ -7,6 +7,7 @@ edition = "2024"
|
||||
anyhow.workspace = true
|
||||
env_logger.workspace = true
|
||||
|
||||
tower-http = { workspace = true, features = ["cors"]}
|
||||
axum.workspace = true
|
||||
axum-server.workspace = true
|
||||
|
||||
@ -28,3 +29,4 @@ ansi_term.workspace = true
|
||||
prost.workspace = true
|
||||
rbase64.workspace = true
|
||||
proto.workspace = true
|
||||
common.workspace = true
|
||||
|
||||
@ -1,8 +1,11 @@
|
||||
use anyhow::Result;
|
||||
use axum::routing::{get, post};
|
||||
use axum::Router;
|
||||
use axum::http::Method;
|
||||
use axum::http::header::CONTENT_TYPE;
|
||||
use axum::routing::{get, post};
|
||||
use logging::init_tracing;
|
||||
use services::{auth, dispatch, errors};
|
||||
use services::{auth, dispatch, errors, sr_tools};
|
||||
use tower_http::cors::{Any, CorsLayer};
|
||||
use tracing::Level;
|
||||
|
||||
mod config;
|
||||
@ -40,6 +43,16 @@ async fn main() -> Result<()> {
|
||||
auth::GRANTER_LOGIN_VERIFICATION_ENDPOINT,
|
||||
post(auth::granter_login_verification),
|
||||
)
|
||||
.route(
|
||||
sr_tools::SRTOOLS_UPLOAD_ENDPOINT,
|
||||
post(sr_tools::sr_tool_save),
|
||||
)
|
||||
.layer(
|
||||
CorsLayer::new()
|
||||
.allow_origin(Any)
|
||||
.allow_methods([Method::GET, Method::POST, Method::PATCH, Method::DELETE])
|
||||
.allow_headers([CONTENT_TYPE]),
|
||||
)
|
||||
.fallback(errors::not_found);
|
||||
|
||||
let addr = format!("0.0.0.0:{PORT}");
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
pub mod auth;
|
||||
pub mod dispatch;
|
||||
pub mod errors;
|
||||
pub mod sr_tools;
|
||||
|
||||
50
sdkserver/src/services/sr_tools.rs
Normal file
50
sdkserver/src/services/sr_tools.rs
Normal file
@ -0,0 +1,50 @@
|
||||
use axum::Json;
|
||||
use common::sr_tools::FreesrData;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use tokio::fs;
|
||||
|
||||
pub const SRTOOLS_UPLOAD_ENDPOINT: &str = "/srtools";
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
pub struct SrToolDataReq {
|
||||
#[allow(dead_code)]
|
||||
pub data: Option<FreesrData>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize)]
|
||||
pub struct SrToolDataRsp {
|
||||
pub message: String,
|
||||
pub status: u32,
|
||||
}
|
||||
|
||||
#[tracing::instrument]
|
||||
pub async fn sr_tool_save(Json(json): Json<SrToolDataReq>) -> Json<SrToolDataRsp> {
|
||||
let Some(json) = json.data else {
|
||||
return Json(SrToolDataRsp {
|
||||
message: String::from("OK"),
|
||||
status: 200,
|
||||
});
|
||||
};
|
||||
|
||||
let json = match serde_json::to_string_pretty(&json) {
|
||||
Ok(json) => json,
|
||||
Err(err) => {
|
||||
return Json(SrToolDataRsp {
|
||||
message: format!("malformed json: {}", err),
|
||||
status: 200,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
if let Err(err) = fs::write("freesr-data.json", json).await {
|
||||
return Json(SrToolDataRsp {
|
||||
message: format!("failed to write freesr-data.json: {}", err),
|
||||
status: 200,
|
||||
});
|
||||
};
|
||||
|
||||
Json(SrToolDataRsp {
|
||||
message: String::from("OK"),
|
||||
status: 200,
|
||||
})
|
||||
}
|
||||
@ -1,20 +1,8 @@
|
||||
{
|
||||
"CNBETAWin3.0.51": {
|
||||
"asset_bundle_url": "https://autopatchcn.bhsr.com/asb/BetaLive/output_9191572_33717c67eee7",
|
||||
"ex_resource_url": "https://autopatchcn.bhsr.com/design_data/BetaLive/output_9201681_3b7fa40d696e",
|
||||
"lua_url": "https://autopatchcn.bhsr.com/lua/BetaLive/output_9188077_6eddb96c0602",
|
||||
"ifix_url": "https://autopatchcn.bhsr.com/ifix/BetaLive/output_0_40d2ce0253"
|
||||
},
|
||||
"OSBETAWin3.0.51": {
|
||||
"asset_bundle_url": "https://autopatchcn.bhsr.com/asb/BetaLive/output_9191572_33717c67eee7",
|
||||
"ex_resource_url": "https://autopatchcn.bhsr.com/design_data/BetaLive/output_9194543_a2c963cc027a",
|
||||
"lua_url": "https://autopatchcn.bhsr.com/lua/BetaLive/output_9188077_6eddb96c0602",
|
||||
"ifix_url": "https://autopatchcn.bhsr.com/ifix/BetaLive/output_0_40d2ce0253"
|
||||
},
|
||||
"CNBETAWin3.0.53": {
|
||||
"asset_bundle_url": "https://autopatchcn.bhsr.com/asb/BetaLive/output_9327839_3a7f8c61dd4e",
|
||||
"ex_resource_url": "https://autopatchcn.bhsr.com/design_data/BetaLive/output_9330527_430d02ffd64d",
|
||||
"lua_url": "https://autopatchcn.bhsr.com/lua/BetaLive/output_9327980_89d683a0d346",
|
||||
"ifix_url": "https://autopatchcn.bhsr.com/ifix/BetaLive/output_0_40d2ce0253"
|
||||
"OSBETAWin3.1.51": {
|
||||
"asset_bundle_url": "https://autopatchos.starrails.com/asb/BetaLive/output_9573347_b03981f01966",
|
||||
"ex_resource_url": "https://autopatchos.starrails.com/design_data/BetaLive/output_9589567_9c50629b0369",
|
||||
"lua_url": "https://autopatchos.starrails.com/lua/BetaLive/output_9567078_0e2b6acf6a2f",
|
||||
"ifix_url": "https://autopatchos.starrails.com/ifix/BetaLive/output_0_40d2ce0253"
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
{
|
||||
"OSBETAWin3.1.51": {
|
||||
"asset_bundle_url": "https://autopatchos.starrails.com/asb/BetaLive/output_9573347_b03981f01966",
|
||||
"ex_resource_url": "https://autopatchos.starrails.com/design_data/BetaLive/output_9574749_cf833d944ab2",
|
||||
"ex_resource_url": "https://autopatchos.starrails.com/design_data/BetaLive/output_9589567_9c50629b0369",
|
||||
"lua_url": "https://autopatchos.starrails.com/lua/BetaLive/output_9567078_0e2b6acf6a2f",
|
||||
"ifix_url": "https://autopatchos.starrails.com/ifix/BetaLive/output_0_40d2ce0253"
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user