feat!: Refactor, Update to version 2.5.x, Add Battle Scepter

This commit is contained in:
amizing25
2024-09-16 13:10:28 +07:00
parent 88f3025333
commit 6e97b516f4
38 changed files with 722000 additions and 15463 deletions
+2
View File
@@ -54,6 +54,8 @@ tracing-subscriber = { version = "0.3.18", features = [
tracing-bunyan-formatter = "0.3.9"
proto = { path = "proto/" }
proto-derive = { path = "proto/proto-derive" }
[profile.release]
strip = true # Automatically strip symbols from the binary.
+7578 -507
View File
File diff suppressed because it is too large Load Diff
+1
View File
@@ -28,5 +28,6 @@ tracing-bunyan-formatter.workspace = true
prost.workspace = true
proto.workspace = true
proto-derive.workspace = true
rand.workspace = true
+1
View File
@@ -2,6 +2,7 @@ use anyhow::Result;
mod logging;
mod net;
mod tools;
mod util;
use logging::init_tracing;
+13 -29
View File
@@ -1,45 +1,29 @@
use anyhow::Result;
use proto::*;
use crate::{net::PlayerSession, util};
pub async fn on_player_get_token_cs_req(
session: &mut PlayerSession,
_session: &mut PlayerSession,
_body: &PlayerGetTokenCsReq,
) -> Result<()> {
session
.send(
CMD_PLAYER_GET_TOKEN_SC_RSP,
PlayerGetTokenScRsp {
retcode: 0,
msg: String::from("OK"),
uid: 25,
..Default::default()
},
)
.await
res: &mut PlayerGetTokenScRsp,
) {
res.msg = String::from("OK");
res.uid = 25;
}
pub async fn on_player_login_cs_req(
session: &mut PlayerSession,
_session: &mut PlayerSession,
body: &PlayerLoginCsReq,
) -> Result<()> {
session
.send(
CMD_PLAYER_LOGIN_SC_RSP,
PlayerLoginScRsp {
login_random: body.login_random,
server_timestamp_ms: util::cur_timestamp_ms(),
stamina: 240,
basic_info: Some(PlayerBasicInfo {
res: &mut PlayerLoginScRsp,
) {
res.login_random = body.login_random;
res.server_timestamp_ms = util::cur_timestamp_ms();
res.stamina = 240;
res.basic_info = Some(PlayerBasicInfo {
nickname: String::from("RobinSR"),
level: 70,
world_level: 6,
stamina: 240,
..Default::default()
}),
..Default::default()
},
)
.await
});
}
+9 -16
View File
@@ -2,25 +2,21 @@ use crate::net::tools::FreesrData;
use super::*;
static UNLOCKED_AVATARS: [u32; 56] = [
static UNLOCKED_AVATARS: [u32; 57] = [
8001, 1001, 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,
1315, 1310, 1314, 1218, 1221, 1220, 1222, 1223, 1317,
];
pub async fn on_get_avatar_data_cs_req(
session: &mut PlayerSession,
_session: &mut PlayerSession,
body: &GetAvatarDataCsReq,
) -> Result<()> {
res: &mut GetAvatarDataScRsp,
) {
let json = FreesrData::load().await;
session
.send(
CMD_GET_AVATAR_DATA_SC_RSP,
GetAvatarDataScRsp {
retcode: 0,
is_get_all: body.is_get_all,
avatar_list: UNLOCKED_AVATARS
res.is_get_all = body.is_get_all;
res.avatar_list = UNLOCKED_AVATARS
.iter()
.map(|id| {
json.avatars
@@ -45,12 +41,9 @@ pub async fn on_get_avatar_data_cs_req(
level: 1,
})
.collect(),
first_met_timestamp: 1712924677,
..Default::default()
})
})
.collect(),
..Default::default()
},
)
.await
.collect();
}
+72 -38
View File
@@ -1,70 +1,53 @@
use std::collections::HashMap;
use rand::Rng;
use rogue_magic_battle_unit_info::Item;
use crate::net::tools::{self, BattleType, Monster};
use super::*;
pub async fn on_start_cocoon_stage_cs_req(
session: &mut PlayerSession,
_session: &mut PlayerSession,
body: &StartCocoonStageCsReq,
) -> Result<()> {
res: &mut StartCocoonStageScRsp,
) {
let battle_info = create_battle_info().await;
let rsp = StartCocoonStageScRsp {
retcode: 0,
prop_entity_id: body.prop_entity_id,
cocoon_id: body.cocoon_id,
wave: body.wave,
battle_info: Some(battle_info),
};
session.send(CMD_START_COCOON_STAGE_SC_RSP, rsp).await
res.prop_entity_id = body.prop_entity_id;
res.cocoon_id = body.cocoon_id;
res.wave = body.wave;
res.battle_info = Some(battle_info);
}
pub async fn on_pve_battle_result_cs_req(
session: &mut PlayerSession,
_session: &mut PlayerSession,
body: &PveBattleResultCsReq,
) -> Result<()> {
session
.send(
CMD_PVE_BATTLE_RESULT_SC_RSP,
PveBattleResultScRsp {
retcode: 0,
end_status: body.end_status,
battle_id: body.battle_id,
..Default::default()
},
)
.await
res: &mut PveBattleResultScRsp,
) {
res.end_status = body.end_status;
res.battle_id = body.battle_id;
}
pub async fn on_scene_cast_skill_cs_req(
session: &mut PlayerSession,
_session: &mut PlayerSession,
request: &SceneCastSkillCsReq,
) -> Result<()> {
res: &mut SceneCastSkillScRsp,
) {
res.attacked_group_id = request.attacked_group_id;
let battle_info = create_battle_info().await;
let mut resp = SceneCastSkillScRsp {
attacked_group_id: request.attacked_group_id,
retcode: 0,
..Default::default()
};
let targets = request
.assist_monster_entity_id_list
.hit_target_entity_id_list
.iter()
.filter(|id| **id > 30_000 || **id < 1_000)
.collect::<Vec<_>>();
if targets.is_empty() {
return session.send(CMD_SCENE_CAST_SKILL_SC_RSP, resp).await;
return;
}
resp.battle_info = Some(battle_info);
session.send(CMD_SCENE_CAST_SKILL_SC_RSP, resp).await
res.battle_info = Some(battle_info);
}
async fn create_battle_info() -> SceneBattleInfo {
@@ -237,11 +220,62 @@ async fn create_battle_info() -> SceneBattleInfo {
max_sp: 10_000,
}),
}),
skill_info: vec![],
})
}
// monsters
battle_info.monster_wave_list = Monster::to_scene_monster_waves(&player.battle_config.monsters);
if !player.battle_config.scepters.is_empty() {
battle_info.rogue_magic_battle_info = Some(RogueMagicBattleInfo {
player_detail_info: Some(RogueMagicBattleUnitInfo {
item: Some(Item::BattleRogueMagicData(BattleRogueMagicData {
round_cnt: Some(BattleRogueMagicRoundCount {
hhfjaibgama: 3,
mmkolnbikjh: 0,
}),
battle_scepter_list: player
.battle_config
.scepters
.iter()
.map(|scepter| {
let mut battle_scepter = BattleRogueMagicScepter {
level: scepter.level,
scepter_id: scepter.id,
magic_unit_list: Vec::new(),
slot_count_map: HashMap::from([(3, 0), (4, 0), (5, 0)]),
};
let mut index = [0u32; 3];
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),
};
let slot_index = &mut index[slot_type as usize - 3];
battle_scepter.magic_unit_list.push(BattleRogueMagicUnit {
level: component.level,
unit_id: component.id,
slot_id: *slot_index,
locked,
counter_map: Default::default(),
});
*slot_index += 1;
*battle_scepter.slot_count_map.get_mut(&slot_type).unwrap() += 1;
}
battle_scepter
})
.collect(),
})),
}),
scepter: Some(Igefngnckog { ojibobnaikh: 5 }),
});
}
battle_info
}
+92 -139
View File
@@ -8,37 +8,37 @@ use crate::{
use super::*;
const SERVER_UID: u32 = 727;
const SERVER_HEAD_ICON: u32 = 201008;
const SERVER_CHAT_BUBBLE_ID: u32 = 220005;
const SERVER_CHAT_HISTORY: [&str; 4] = [
"'sync'",
"'mc {mc_id}' mc_id can be set from 8001 to 8006",
"'march {march_id}' march_id can be set 1001 or 1224",
"available command:",
];
pub async fn on_get_friend_login_info_cs_req(
session: &mut PlayerSession,
_: &GetFriendLoginInfoCsReq,
) -> Result<()> {
session
.send(
CMD_GET_FRIEND_LOGIN_INFO_SC_RSP,
GetFriendLoginInfoScRsp {
mnacbpjeche: vec![727],
..Default::default()
},
)
.await
_session: &mut PlayerSession,
_req: &GetFriendLoginInfoCsReq,
res: &mut GetFriendLoginInfoScRsp,
) {
res.friend_uid_list = vec![SERVER_UID];
}
pub async fn on_get_friend_list_info_cs_req(
session: &mut PlayerSession,
_: &GetFriendListInfoCsReq,
) -> Result<()> {
session
.send(
CMD_GET_FRIEND_LIST_INFO_SC_RSP,
GetFriendListInfoScRsp {
friend_list: vec![FriendListInfo {
_session: &mut PlayerSession,
_req: &GetFriendListInfoCsReq,
res: &mut GetFriendListInfoScRsp,
) {
res.friend_list = vec![FriendListInfo {
friend_name: String::from("RobinSR"),
simple_info: Some(SimpleInfo {
uid: 727,
platform_type: 3,
online_status: 1,
head_icon: 201008,
chat_bubble_id: 220005,
uid: SERVER_UID,
platform_type: PlatformType::Pc.into(),
online_status: FriendOnlineStatus::Online.into(),
head_icon: SERVER_HEAD_ICON,
chat_bubble_id: SERVER_CHAT_BUBBLE_ID,
level: 70,
nickname: String::from("Server"),
signature: String::from("omg"),
@@ -47,79 +47,52 @@ pub async fn on_get_friend_list_info_cs_req(
is_marked: true,
sent_time: 0,
..Default::default()
}],
..Default::default()
},
)
.await
}];
}
pub async fn on_get_private_chat_history_cs_req(
session: &mut PlayerSession,
_session: &mut PlayerSession,
req: &GetPrivateChatHistoryCsReq,
) -> Result<()> {
session
.send(
CMD_GET_PRIVATE_CHAT_HISTORY_SC_RSP,
GetPrivateChatHistoryScRsp {
chat_list: vec![
Chat {
res: &mut GetPrivateChatHistoryScRsp,
) {
let cur_time = cur_timestamp_ms();
res.chat_list = SERVER_CHAT_HISTORY
.iter()
.map(|text| Chat {
msg_type: MsgType::CustomText.into(),
sent_time: cur_timestamp_ms(),
text: "'sync'".to_string(),
sender_uid: 727,
sent_time: cur_time,
text: String::from(*text),
sender_uid: SERVER_UID,
..Default::default()
},
Chat {
msg_type: MsgType::CustomText.into(),
sent_time: cur_timestamp_ms(),
text: "'mc {mc_id}' mc_id can be set from 8001 to 8006".to_string(),
sender_uid: 727,
..Default::default()
},
Chat {
msg_type: MsgType::CustomText.into(),
sent_time: cur_timestamp_ms(),
text: "'march {march_id}' march_id can be set 1001 or 1224".to_string(),
sender_uid: 727,
..Default::default()
},
Chat {
msg_type: MsgType::CustomText.into(),
sent_time: cur_timestamp_ms(),
text: "available command:".to_string(),
sender_uid: 727,
..Default::default()
},
],
sender_id: 0, // from
to_uid: req.to_uid, // to
..Default::default()
},
)
.await
})
.collect();
res.to_uid = req.to_uid;
res.from_uid = SERVER_UID;
}
pub async fn on_send_msg_cs_req(session: &mut PlayerSession, body: &SendMsgCsReq) -> Result<()> {
pub async fn on_send_msg_cs_req(
session: &mut PlayerSession,
body: &SendMsgCsReq,
_res: &mut SendMsgScRsp,
) {
let mut json = FreesrData::load().await;
if let Some((cmd, args)) = parse_command(&body.text) {
match cmd {
"sync" => {
sync_player(session, json).await?;
sync_player(session, json).await;
session
.send(
CMD_REVC_MSG_SC_NOTIFY,
RevcMsgScNotify {
.send(RevcMsgScNotify {
msg_type: body.msg_type,
text: String::from("Inventory Synced"),
emote: body.emote,
from_uid: 727, // from
from_uid: SERVER_UID, // from
to_uid: 25, // to
chat_type: body.chat_type,
},
)
.await?;
..Default::default()
})
.await
.unwrap();
}
"mc" => {
let mc = MultiPathAvatar::from(
@@ -133,28 +106,25 @@ pub async fn on_send_msg_cs_req(session: &mut PlayerSession, body: &SendMsgCsReq
json.save().await;
session
.send(
CMD_AVATAR_PATH_CHANGED_NOTIFY,
AvatarPathChangedNotify {
.send(AvatarPathChangedNotify {
base_avatar_id: 8001,
cur_multi_path_avatar_type: mc as i32,
},
)
.await?;
})
.await
.unwrap();
session
.send(
CMD_REVC_MSG_SC_NOTIFY,
RevcMsgScNotify {
.send(RevcMsgScNotify {
msg_type: body.msg_type,
text: format!("Success change mc to {:#?}", mc),
emote: body.emote,
from_uid: 727, // from
from_uid: SERVER_UID, // from
to_uid: 25, // to
chat_type: body.chat_type,
},
)
.await?;
..Default::default()
})
.await
.unwrap();
}
"march" => {
let march_type = MultiPathAvatar::from(
@@ -168,42 +138,29 @@ pub async fn on_send_msg_cs_req(session: &mut PlayerSession, body: &SendMsgCsReq
json.save().await;
session
.send(
CMD_AVATAR_PATH_CHANGED_NOTIFY,
AvatarPathChangedNotify {
.send(AvatarPathChangedNotify {
base_avatar_id: 1001,
cur_multi_path_avatar_type: march_type as i32,
},
)
.await?;
})
.await
.unwrap();
session
.send(
CMD_REVC_MSG_SC_NOTIFY,
RevcMsgScNotify {
.send(RevcMsgScNotify {
msg_type: body.msg_type,
text: format!("Success change march to {:#?}", march_type),
emote: body.emote,
from_uid: 727, // from
from_uid: SERVER_UID, // from
to_uid: 25, // to
chat_type: body.chat_type,
},
)
.await?;
..Default::default()
})
.await
.unwrap();
}
_ => {}
}
}
session
.send(
CMD_SEND_MSG_SC_RSP,
SendMsgScRsp {
retcode: 0,
end_time: 0,
},
)
.await
}
fn parse_command(command: &str) -> Option<(&str, Vec<&str>)> {
@@ -216,22 +173,20 @@ 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) -> Result<()> {
async fn sync_player(session: &mut PlayerSession, json: FreesrData) {
// clear relics & lightcones
session
.send(
CMD_PLAYER_SYNC_SC_NOTIFY,
PlayerSyncScNotify {
.send(PlayerSyncScNotify {
del_equipment_list: (2000..3500).collect(),
del_relic_list: (1..2000).collect(),
..Default::default()
},
)
.await?;
})
.await
.unwrap();
// Sync avatars
session
.send(
CMD_PLAYER_SYNC_SC_NOTIFY,
PlayerSyncScNotify {
.send(PlayerSyncScNotify {
avatar_sync: Some(AvatarSync {
avatar_list: json
.avatars
@@ -240,14 +195,13 @@ async fn sync_player(session: &mut PlayerSession, json: FreesrData) -> Result<()
.collect::<Vec<_>>(),
}),
..Default::default()
},
)
.await?;
})
.await
.unwrap();
// Sync new relics
session
.send(
CMD_PLAYER_SYNC_SC_NOTIFY,
PlayerSyncScNotify {
.send(PlayerSyncScNotify {
relic_list: json.relics.iter().map(|v| v.to_relic_proto()).collect(),
equipment_list: json
.lightcones
@@ -255,14 +209,13 @@ async fn sync_player(session: &mut PlayerSession, json: FreesrData) -> Result<()
.map(|v| v.to_equipment_proto())
.collect(),
..Default::default()
},
)
.await?;
})
.await
.unwrap();
// Sync new lightcones
session
.send(
CMD_PLAYER_SYNC_SC_NOTIFY,
PlayerSyncScNotify {
.send(PlayerSyncScNotify {
avatar_sync: Some(AvatarSync {
avatar_list: json
.avatars
@@ -281,7 +234,7 @@ async fn sync_player(session: &mut PlayerSession, json: FreesrData) -> Result<()
.collect(),
}),
..Default::default()
},
)
})
.await
.unwrap()
}
+13 -28
View File
@@ -1,10 +1,11 @@
pub use super::*;
pub async fn on_get_gacha_info_cs_req(
session: &mut PlayerSession,
_: &GetGachaInfoCsReq,
) -> anyhow::Result<()> {
let gacha = GachaInfo {
_session: &mut PlayerSession,
_req: &GetGachaInfoCsReq,
res: &mut GetGachaInfoScRsp,
) {
res.gacha_info_list = vec![GachaInfo {
end_time: 1924992000,
begin_time: 0,
gacha_ceiling: Some(GachaCeiling::default()),
@@ -17,29 +18,17 @@ pub async fn on_get_gacha_info_cs_req(
],
gacha_id: 1001,
..Default::default()
};
session
.send(
CMD_GET_GACHA_INFO_SC_RSP,
GetGachaInfoScRsp {
gacha_info_list: vec![gacha],
..Default::default()
},
)
.await
}];
}
pub async fn on_do_gacha_cs_req(
session: &mut PlayerSession,
_session: &mut PlayerSession,
req: &DoGachaCsReq,
) -> anyhow::Result<()> {
session
.send(
CMD_DO_GACHA_SC_RSP,
DoGachaScRsp {
gacha_id: req.gacha_id,
gacha_num: req.gacha_num,
gacha_item_list: (0..req.gacha_num)
res: &mut DoGachaScRsp,
) {
res.gacha_id = req.gacha_id;
res.gacha_num = req.gacha_num;
res.gacha_item_list = (0..req.gacha_num)
.map(|v| GachaItem {
is_new: false,
gacha_item: Some(Item {
@@ -55,9 +44,5 @@ pub async fn on_do_gacha_cs_req(
}),
transfer_item_list: Some(ItemList { item_list: vec![] }),
})
.collect(),
..Default::default()
},
)
.await
.collect();
}
+30 -60
View File
@@ -1,22 +1,21 @@
use anyhow::Result;
use proto::*;
use crate::net::{tools::FreesrData, PlayerSession};
pub async fn on_get_bag_cs_req(session: &mut PlayerSession, _: &GetBagCsReq) -> Result<()> {
pub async fn on_get_bag_cs_req(
_session: &mut PlayerSession,
_req: &GetBagCsReq,
res: &mut GetBagScRsp,
) {
let player = FreesrData::load().await;
session
.send(
CMD_GET_BAG_SC_RSP,
GetBagScRsp {
equipment_list: player
res.equipment_list = player
.lightcones
.iter()
.map(|v| v.to_equipment_proto())
.collect(),
relic_list: player.relics.iter().map(|v| v.to_relic_proto()).collect(),
material_list: vec![
.collect();
res.relic_list = player.relics.iter().map(|v| v.to_relic_proto()).collect();
res.material_list = vec![
Material {
tid: 101, // Normal Pass
num: 999999,
@@ -27,70 +26,41 @@ pub async fn on_get_bag_cs_req(session: &mut PlayerSession, _: &GetBagCsReq) ->
num: 999999,
..Default::default()
},
],
..Default::default()
},
)
.await
];
}
pub async fn on_get_archive_data_cs_req(
session: &mut PlayerSession,
_session: &mut PlayerSession,
_: &GetArchiveDataCsReq,
) -> Result<()> {
session
.send(
CMD_GET_ARCHIVE_DATA_SC_RSP,
GetArchiveDataScRsp {
archive_data: Some(ArchiveData::default()),
retcode: 0,
},
)
.await
res: &mut GetArchiveDataScRsp,
) {
res.archive_data = Some(ArchiveData::default());
}
pub async fn on_dress_relic_avatar_cs_req(
session: &mut PlayerSession,
_: &DressRelicAvatarCsReq,
) -> Result<()> {
// ?
session
.send(
CMD_DRESS_RELIC_AVATAR_SC_RSP,
DressRelicAvatarScRsp::default(),
)
.await
_session: &mut PlayerSession,
_req: &DressRelicAvatarCsReq,
_res: &mut DressRelicAvatarScRsp,
) {
}
pub async fn on_take_off_relic_cs_req(
session: &mut PlayerSession,
_: &TakeOffRelicCsReq,
) -> Result<()> {
// ?
session
.send(CMD_TAKE_OFF_RELIC_SC_RSP, TakeOffRelicScRsp::default())
.await
_session: &mut PlayerSession,
_req: &TakeOffRelicCsReq,
_res: &mut TakeOffRelicScRsp,
) {
}
pub async fn on_dress_avatar_cs_req(
session: &mut PlayerSession,
_: &DressAvatarCsReq,
) -> Result<()> {
// ?
session
.send(CMD_DRESS_AVATAR_SC_RSP, DressAvatarScRsp::default())
.await
_session: &mut PlayerSession,
_req: &DressAvatarCsReq,
_res: &mut DressAvatarScRsp,
) {
}
pub async fn on_take_off_equipment_cs_req(
session: &mut PlayerSession,
_: &TakeOffEquipmentCsReq,
) -> Result<()> {
// ?
session
.send(
CMD_TAKE_OFF_EQUIPMENT_SC_RSP,
TakeOffEquipmentScRsp::default(),
)
.await
_session: &mut PlayerSession,
_req: &TakeOffEquipmentCsReq,
_res: &mut TakeOffEquipmentScRsp,
) {
}
+31 -80
View File
@@ -6,34 +6,26 @@ 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,
) -> Result<()> {
res: &mut GetAllLineupDataScRsp,
) {
let player = tools::FreesrData::load().await;
let lineup = LineupInfo {
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),
..Default::default()
};
session
.send(
CMD_GET_ALL_LINEUP_DATA_SC_RSP,
GetAllLineupDataScRsp {
lineup_list: vec![lineup],
..Default::default()
},
)
.await
}];
}
pub async fn on_get_cur_lineup_data_cs_req(
session: &mut PlayerSession,
_session: &mut PlayerSession,
_body: &GetCurLineupDataCsReq,
) -> Result<()> {
res: &mut GetCurLineupDataScRsp,
) {
let player = tools::FreesrData::load().await;
let lineup = LineupInfo {
extra_lineup_type: ExtraLineupType::LineupNone.into(),
@@ -46,24 +38,14 @@ pub async fn on_get_cur_lineup_data_cs_req(
..Default::default()
};
session
.send(
CMD_GET_CUR_LINEUP_DATA_SC_RSP,
GetCurLineupDataScRsp {
lineup: Some(lineup),
..Default::default()
},
)
.await
res.lineup = Some(lineup)
}
pub async fn on_join_lineup_cs_req(
session: &mut PlayerSession,
body: &JoinLineupCsReq,
) -> Result<()> {
// update lineups
// TODO: FIX THESE SHIT
{
_res: &mut JoinLineupScRsp,
) {
let mut player = tools::FreesrData::load().await;
let lineups = &mut player.lineups;
lineups.insert(
@@ -77,24 +59,14 @@ pub async fn on_join_lineup_cs_req(
},
);
player.save_lineup().await;
}
{
let player = tools::FreesrData::load().await;
refresh_lineup(session, &player).await?;
}
session
.send(CMD_JOIN_LINEUP_SC_RSP, JoinLineupScRsp::default())
.await
refresh_lineup(session, &player).await;
}
pub async fn on_replace_lineup_cs_req(
_session: &mut PlayerSession,
req: &ReplaceLineupCsReq,
) -> Result<()> {
{
_res: &mut ReplaceLineupScRsp,
) {
let mut player = tools::FreesrData::load().await;
let lineups = &mut player.lineups;
@@ -112,29 +84,17 @@ pub async fn on_replace_lineup_cs_req(
}
}
player.save_lineup().await;
}
{
let player = tools::FreesrData::load().await;
refresh_lineup(_session, &player).await?;
}
_session
.send(CMD_JOIN_LINEUP_SC_RSP, JoinLineupScRsp::default())
.await
refresh_lineup(_session, &player).await;
}
pub async fn on_quit_lineup_cs_req(
_session: &mut PlayerSession,
_: &QuitLineupCsReq,
) -> Result<()> {
_session
.send(CMD_JOIN_LINEUP_SC_RSP, JoinLineupScRsp::default())
.await
_res: &mut QuitLineupScRsp,
) {
}
async fn refresh_lineup(sess: &mut PlayerSession, player: &FreesrData) -> Result<()> {
async fn refresh_lineup(session: &mut PlayerSession, player: &FreesrData) {
let lineup = LineupInfo {
extra_lineup_type: ExtraLineupType::LineupNone.into(),
name: "Squad 1".to_string(),
@@ -144,9 +104,8 @@ async fn refresh_lineup(sess: &mut PlayerSession, player: &FreesrData) -> Result
..Default::default()
};
sess.send(
CMD_SCENE_GROUP_REFRESH_SC_NOTIFY,
SceneGroupRefreshScNotify {
session
.send(SceneGroupRefreshScNotify {
group_refresh_info: vec![SceneGroupRefreshInfo {
group_id: 0,
state: 0,
@@ -170,31 +129,23 @@ async fn refresh_lineup(sess: &mut PlayerSession, player: &FreesrData) -> Result
})
.collect(),
}],
},
)
.await?;
})
.await
.unwrap();
sess.send(
CMD_SYNC_LINEUP_NOTIFY,
SyncLineupNotify {
session
.send(SyncLineupNotify {
lineup: Some(lineup),
reason_list: vec![],
},
)
})
.await
.unwrap();
}
pub async fn on_change_lineup_leader_cs_req(
session: &mut PlayerSession,
_session: &mut PlayerSession,
body: &ChangeLineupLeaderCsReq,
) -> Result<()> {
session
.send(
CMD_CHANGE_LINEUP_LEADER_SC_RSP,
ChangeLineupLeaderScRsp {
slot: body.slot,
retcode: 0,
},
)
.await
res: &mut ChangeLineupLeaderScRsp,
) {
res.slot = body.slot;
}
+8 -16
View File
@@ -1,13 +1,12 @@
use super::*;
use anyhow::Result;
pub async fn on_get_mail_cs_req(session: &mut PlayerSession, _: &GetMailCsReq) -> Result<()> {
session
.send(
CMD_GET_MAIL_SC_RSP,
GetMailScRsp {
is_end: true,
mail_list: vec![ClientMail {
pub async fn on_get_mail_cs_req(
_session: &mut PlayerSession,
_req: &GetMailCsReq,
res: &mut GetMailScRsp,
) {
res.is_end = true;
res.mail_list = vec![ClientMail {
title: String::from("Welcome!"),
sender: String::from("Server"),
content: String::from("Welcome!"),
@@ -26,12 +25,5 @@ pub async fn on_get_mail_cs_req(session: &mut PlayerSession, _: &GetMailCsReq) -
}),
mail_type: MailType::Normal.into(),
template_id: 0,
}],
notice_mail_list: vec![],
start: 0,
retcode: 0,
total_num: 1,
},
)
.await
}];
}
+6 -11
View File
@@ -1,13 +1,12 @@
use super::*;
pub async fn on_get_mission_status_cs_req(
session: &mut PlayerSession,
_session: &mut PlayerSession,
body: &GetMissionStatusCsReq,
) -> Result<()> {
let rsp = GetMissionStatusScRsp {
retcode: 0,
finished_main_mission_id_list: body.main_mission_id_list.clone(),
sub_mission_status_list: body
res: &mut GetMissionStatusScRsp,
) {
res.finished_main_mission_id_list = body.main_mission_id_list.clone();
res.sub_mission_status_list = body
.sub_mission_id_list
.iter()
.map(|id| Mission {
@@ -15,9 +14,5 @@ pub async fn on_get_mission_status_cs_req(
progress: 1,
status: MissionStatus::MissionFinish.into(),
})
.collect(),
..Default::default()
};
session.send(CMD_GET_MISSION_STATUS_SC_RSP, rsp).await
.collect();
}
-2
View File
@@ -9,7 +9,6 @@ mod mail;
mod mission;
mod player;
mod scene;
mod tutorial;
use anyhow::Result;
use paste::paste;
@@ -30,7 +29,6 @@ pub use mail::*;
pub use mission::*;
pub use player::*;
pub use scene::*;
// pub use tutorial::*;
#[allow(unused_imports)]
use proto::{
+39 -83
View File
@@ -1,106 +1,70 @@
use std::collections::HashMap;
use crate::{net::tools::FreesrData, util};
use crate::{net::tools::FreesrData, util::cur_timestamp_ms};
use super::*;
pub async fn on_get_basic_info_cs_req(
session: &mut PlayerSession,
_session: &mut PlayerSession,
_body: &GetBasicInfoCsReq,
) -> Result<()> {
session
.send(
CMD_GET_BASIC_INFO_SC_RSP,
GetBasicInfoScRsp {
retcode: 0,
player_setting_info: Some(PlayerSettingInfo::default()),
gender: Gender::Woman as u32,
is_gender_set: true,
..Default::default()
},
)
.await
res: &mut GetBasicInfoScRsp,
) {
res.player_setting_info = Some(PlayerSettingInfo::default());
res.gender = Gender::Woman as u32;
res.is_gender_set = true;
}
pub async fn on_player_heart_beat_cs_req(
session: &mut PlayerSession,
_session: &mut PlayerSession,
body: &PlayerHeartBeatCsReq,
) -> Result<()> {
session
.send(
CMD_PLAYER_HEART_BEAT_SC_RSP,
PlayerHeartBeatScRsp {
retcode: 0,
client_time_ms: body.client_time_ms,
server_time_ms: util::cur_timestamp_ms(),
download_data: Some(ClientDownloadData {
res: &mut PlayerHeartBeatScRsp,
) {
res.client_time_ms = body.client_time_ms;
res.server_time_ms = cur_timestamp_ms();
res.download_data = Some(ClientDownloadData {
version: 51,
time: util::cur_timestamp_ms() as i64,
time: res.server_time_ms as i64,
data: rbase64::decode("bG9jYWwgZnVuY3Rpb24gYmV0YV90ZXh0KG9iaikKICAgIGxvY2FsIGdhbWVPYmplY3QgPSBDUy5Vbml0eUVuZ2luZS5HYW1lT2JqZWN0LkZpbmQoIlVJUm9vdC9BYm92ZURpYWxvZy9CZXRhSGludERpYWxvZyhDbG9uZSkiKQoKICAgIGlmIGdhbWVPYmplY3QgdGhlbgogICAgICAgIGxvY2FsIHRleHRDb21wb25lbnQgPSBnYW1lT2JqZWN0OkdldENvbXBvbmVudEluQ2hpbGRyZW4odHlwZW9mKENTLlJQRy5DbGllbnQuTG9jYWxpemVkVGV4dCkpCgogICAgICAgIGlmIHRleHRDb21wb25lbnQgdGhlbgogICAgICAgICAgICB0ZXh0Q29tcG9uZW50LnRleHQgPSAiUm9iaW5TUiBpcyBhIGZyZWUgYW5kIG9wZW4gc291cmNlIHNvZnR3YXJlLiBkaXNjb3JkLmdnL3JldmVyc2Vkcm9vbXMiCiAgICAgICAgZW5kCiAgICBlbHNlCiAgICBlbmQKZW5kCgpiZXRhX3RleHQoKQ==").unwrap()
}),
},
)
.await
});
}
pub type PlayerLoginFinishCsReq = Dummy;
pub async fn on_player_login_finish_cs_req(
session: &mut PlayerSession,
_: &PlayerLoginFinishCsReq,
_req: &PlayerLoginFinishCsReq,
_res: &mut PlayerLoginFinishScRsp,
) -> Result<()> {
session
.send(CMD_PLAYER_LOGIN_FINISH_SC_RSP, Dummy::default())
.await?;
session
.send(
CMD_CONTENT_PACKAGE_SYNC_DATA_SC_NOTIFY,
ContentPackageSyncDataScNotify {
.send(ContentPackageSyncDataScNotify {
data: Some(PackageData {
info_list: vec![
ContentInfo {
200001, 200002, 200003, 200004, 150017, 150015, 150021, 150018, 130011, 130012,
130013,
]
.iter()
.map(|v| ContentInfo {
status: ContentPackageStatus::Finished.into(),
content_id: 200001,
},
ContentInfo {
status: ContentPackageStatus::Finished.into(),
content_id: 200002,
},
ContentInfo {
status: ContentPackageStatus::Finished.into(),
content_id: 200003,
},
ContentInfo {
status: ContentPackageStatus::Finished.into(),
content_id: 150017,
},
ContentInfo {
status: ContentPackageStatus::Finished.into(),
content_id: 150015,
},
],
content_id: *v,
})
.collect(),
..Default::default()
}),
},
)
.await?;
Ok(())
})
.await
}
pub async fn on_get_multi_path_avatar_info_cs_req(
session: &mut PlayerSession,
_: &GetMultiPathAvatarInfoCsReq,
) -> Result<()> {
_session: &mut PlayerSession,
_req: &GetMultiPathAvatarInfoCsReq,
res: &mut GetMultiPathAvatarInfoScRsp,
) {
let json = FreesrData::load().await;
session
.send(
CMD_GET_MULTI_PATH_AVATAR_INFO_SC_RSP,
GetMultiPathAvatarInfoScRsp {
retcode: 0,
multi_path_avatar_type_info_list: vec![
res.current_multi_path_avatar_id = HashMap::from([
(8001, json.main_character.get_type().into()),
(1001, json.march_type.get_type().into()),
]);
res.multi_path_avatar_type_info_list = vec![
MultiPathAvatarTypeInfo {
avatar_id: json.main_character as i32,
rank: 6,
@@ -145,13 +109,5 @@ pub async fn on_get_multi_path_avatar_info_cs_req(
.map(|v| v.internal_uid)
.unwrap_or_default(),
},
],
current_multi_path_avatar_id: HashMap::from([
(8001, json.main_character.get_type().into()),
(1001, json.march_type.get_type().into()),
]),
..Default::default()
},
)
.await
];
}
+147 -214
View File
@@ -1,95 +1,65 @@
use lazy_static::lazy_static;
use prost::Message;
use scene_entity_info::Entity;
use tokio::sync::Mutex;
use crate::{
net::{
tools::{AvatarJson, FreesrData, Position},
tools_res::{PropState, GAME_RESOURCES},
},
net::tools::{AvatarJson, FreesrData, Position},
tools::resources::GAME_RES,
util::{self},
};
use super::*;
#[derive(Message)]
pub struct Dummy {}
pub async fn on_get_cur_scene_info_cs_req(
session: &mut PlayerSession,
_body: &GetCurSceneInfoCsReq,
) -> Result<()> {
res: &mut GetCurSceneInfoScRsp,
) {
let mut player = FreesrData::load().await;
let entry = player.scene.entry_id;
let scene = load_scene(session, &mut player, entry, false, Option::<u32>::None).await;
let resp = GetCurSceneInfoScRsp {
retcode: 0,
scene: if let Ok(scene) = scene {
res.scene = if let Ok(scene) = scene {
Some(scene)
} else {
Some(SceneInfo {
game_mode_type: 1,
game_mode_type: 3,
entry_id: player.scene.entry_id,
plane_id: player.scene.plane_id,
floor_id: player.scene.floor_id,
..Default::default()
})
},
};
session.send(CMD_GET_CUR_SCENE_INFO_SC_RSP, resp).await?;
if !player.position.is_empty() {
session
.send(
CMD_SCENE_ENTITY_MOVE_SC_NOTIFY,
SceneEntityMoveScNotify {
entity_id: 0,
entry_id: player.scene.entry_id,
motion: Some(player.position.to_motion()),
..Default::default()
},
)
.await?;
};
Ok(())
}
// enterscene
pub async fn on_enter_scene_cs_req(
session: &mut PlayerSession,
request: &EnterSceneCsReq,
) -> Result<()> {
req: &EnterSceneCsReq,
res: &mut EnterSceneScRsp,
) {
let mut player = FreesrData::load().await;
// send packet first
session
.send(CMD_ENTER_SCENE_SC_RSP, Dummy::default())
.await?;
load_scene(
if load_scene(
session,
&mut player,
request.entry_id,
req.entry_id,
true,
Some(request.teleport_id),
Some(req.teleport_id),
)
.await?;
Ok(())
.await
.is_err()
{
res.retcode = Nbbhhpnhond::RetSceneEntryIdNotMatch as u32;
};
}
// getscenemapinfocsreq
pub async fn on_get_scene_map_info_cs_req(
sesison: &mut PlayerSession,
request: &GetSceneMapInfoCsReq,
) -> Result<()> {
let mut map_infos = Vec::<MazeMapData>::new();
for entry_id in &request.entry_id_list {
_sesison: &mut PlayerSession,
req: &GetSceneMapInfoCsReq,
res: &mut GetSceneMapInfoScRsp,
) {
for entry_id in &req.entry_id_list {
let mut map_info = MazeMapData {
retcode: 0,
unlocked_chest_list: vec![
@@ -114,51 +84,37 @@ pub async fn on_get_scene_map_info_cs_req(
map_info.lighten_section_list.push(i)
}
let group_config = GAME_RESOURCES.map_entrance.get(entry_id).and_then(|v| {
GAME_RESOURCES
.level_group
.get(&format!("P{}_F{}", v.plane_id, v.floor_id))
});
if let Some(level) = group_config {
// add teleports
for teleport in &level.teleports {
map_info.unlocked_teleport_list.push(*teleport.0)
}
let group_config = GAME_RES
.level_output_configs
.get(&entry_id)
.and_then(|v| v.iter().next());
for (group_id, group) in &level.group_items {
if let Some((_, group_config)) = group_config {
for (_, (group_id, group)) in group_config.scenes.iter().enumerate() {
map_info.maze_group_list.push(MazeGroup {
group_id: *group_id,
..Default::default()
});
// prop
for (teleport, _) in &group.teleports {
map_info.unlocked_teleport_list.push(*teleport)
}
for prop in &group.props {
if prop.prop_state != 8 {
continue;
}
map_info.maze_prop_list.push(MazeProp {
group_id: prop.group_id,
state: if prop.prop_state_list.contains(&PropState::CheckPointEnable) {
PropState::CheckPointEnable as u32
} else {
prop.state.clone() as u32
},
config_id: prop.id,
state: prop.prop_state,
config_id: prop.inst_id,
});
}
}
}
map_infos.push(map_info)
res.map_list.push(map_info)
}
sesison
.send(
CMD_GET_SCENE_MAP_INFO_SC_RSP,
GetSceneMapInfoScRsp {
retcode: 0,
map_list: map_infos,
..Default::default()
},
)
.await
}
lazy_static! {
@@ -166,24 +122,21 @@ lazy_static! {
}
pub async fn on_scene_entity_move_cs_req(
session: &mut PlayerSession,
request: &SceneEntityMoveCsReq,
) -> Result<()> {
_session: &mut PlayerSession,
req: &SceneEntityMoveCsReq,
_res: &mut SceneEntityMoveScRsp,
) {
let mut player = FreesrData::load().await;
let mut timestamp = NEXT_SCENE_SAVE.lock().await;
if util::cur_timestamp_ms() <= *timestamp {
session
.send(CMD_SCENE_ENTITY_MOVE_SC_RSP, Dummy::default())
.await?;
return Ok(());
return;
}
// save every 5 sec
*timestamp = util::cur_timestamp_ms() + (5 * 1000);
for entity in &request.entity_motion_list {
for entity in &req.entity_motion_list {
if entity.entity_id != 0 {
continue;
}
@@ -201,99 +154,101 @@ pub async fn on_scene_entity_move_cs_req(
}
player.save().await;
session
.send(CMD_SCENE_ENTITY_MOVE_SC_RSP, Dummy::default())
.await
}
pub async fn on_get_entered_scene_cs_req(
session: &mut PlayerSession,
_: &GetEnteredSceneCsReq,
) -> Result<()> {
let scenes = GAME_RESOURCES
.map_entrance
_session: &mut PlayerSession,
_req: &GetEnteredSceneCsReq,
res: &mut GetEnteredSceneScRsp,
) {
res.entered_scene_info = GAME_RES
.level_output_configs
.iter()
.filter(|(_, v)| {
!v.finish_main_mission_list.is_empty() || !v.finish_sub_mission_list.is_empty()
.map(|(_, v)| {
v.iter()
.filter(|(_, v)| v.is_entered_scene_info)
.map(|(k, _)| {
let split: Vec<_> = k.split("_").collect();
let plane_id = &split[0][1..];
let floor_id = &split[1][1..];
EnteredSceneInfo {
floor_id: floor_id.parse().unwrap(),
plane_id: plane_id.parse().unwrap(),
}
})
.map(|(_, v)| EnteredSceneInfo {
floor_id: v.floor_id,
plane_id: v.plane_id,
.collect::<Vec<_>>()
})
.flatten()
.collect::<Vec<_>>();
session
.send(
CMD_GET_ENTERED_SCENE_SC_RSP,
GetEnteredSceneScRsp {
entered_scene_info: scenes,
retcode: 0,
},
)
.await
}
async fn load_scene(
session: &mut PlayerSession,
json: &mut FreesrData,
entry_id: u32,
_save: bool,
save_scene: bool,
teleport_id: Option<u32>,
) -> Result<SceneInfo> {
let enterance = GAME_RESOURCES
.map_entrance
let (name, scene) = GAME_RES
.level_output_configs
.get(&entry_id)
.ok_or_else(|| anyhow::format_err!("Map Entrance Not Found"))?;
.map(|v| v.iter().next())
.flatten()
.ok_or_else(|| {
tracing::error!("Map Entrance Not Found {}", entry_id);
anyhow::format_err!("Map Entrance Not Found {}", entry_id)
})?;
let plane = GAME_RESOURCES
.maze_plane
.get(&enterance.plane_id)
.ok_or_else(|| anyhow::format_err!("Map Plane Not Found"))?;
let split: Vec<_> = name.split("_").collect();
let plane_id = *&split[0][1..].parse::<u32>()?;
let floor_id = *&split[1][1..].parse::<u32>()?;
let group_config = GAME_RESOURCES
.level_group
.get(&format!("P{}_F{}", enterance.plane_id, enterance.floor_id))
.ok_or_else(|| anyhow::format_err!("Group Config Not Found"))?;
let mut position = json.position.clone();
let mut json_pos = json.position.clone();
if let Some(teleport_id) = teleport_id {
if let Some(teleport) = group_config.teleports.get(&teleport_id) {
let anchor = group_config
.group_items
.get(&teleport.anchor_group_id.unwrap_or_default())
.and_then(|v| v.anchors.get(&teleport.anchor_id.unwrap_or_default()));
if let Some(anchor) = anchor {
position.x = (anchor.pos_x * 1000f64) as i32;
position.y = (anchor.pos_y * 1000f64) as i32;
position.z = (anchor.pos_z * 1000f64) as i32;
position.rot_y = (anchor.rot_y * 1000f64) as i32;
if let Some(teleport) = scene
.scenes
.iter()
.find_map(|(_, v)| v.teleports.get(&teleport_id))
{
json_pos.x = teleport.pos.x;
json_pos.y = teleport.pos.y;
json_pos.z = teleport.pos.z;
json_pos.rot_y = teleport.rot.y;
} else {
if let Some((_, teleport)) = scene
.scenes
.iter()
.find_map(|v| v.1.teleports.iter().next())
{
json_pos.x = teleport.pos.x;
json_pos.y = teleport.pos.y;
json_pos.z = teleport.pos.z;
json_pos.rot_y = teleport.rot.y;
}
}
}
let mut scene_info = SceneInfo {
floor_id: enterance.floor_id,
plane_id: enterance.plane_id,
floor_id,
plane_id,
entry_id,
game_mode_type: plane.plane_type as u32,
game_mode_type: *(&scene.plane_type) as u32,
leader_entity_id: 1,
world_id: plane.world_id,
world_id: scene.world_id,
..Default::default()
};
let lineup_info = AvatarJson::to_lineup_info(&json.lineups);
let player_pos = MotionInfo {
// rot
rot: Some(Vector {
x: 0,
y: position.rot_y,
y: json_pos.rot_y,
z: 0,
}),
// pos
pos: Some(Vector {
x: position.x,
y: position.y,
z: position.z,
x: json_pos.x,
y: json_pos.y,
z: json_pos.z,
}),
};
@@ -302,36 +257,29 @@ async fn load_scene(
let mut npc_entity_id = 20_000;
let mut monster_entity_id = 30_000;
for (group_id, group) in &group_config.group_items {
for (group_id, group) in &scene.scenes {
let mut group_info = SceneEntityGroupInfo {
state: 0,
group_id: *group_id,
..Default::default()
};
// Load Props
for prop in &group.props {
let prop_state = if prop.prop_state_list.contains(&PropState::CheckPointEnable) {
PropState::CheckPointEnable
} else {
prop.state.clone()
};
prop_entity_id += 1;
let prop_position = Position {
x: (prop.pos_x * 1000f64) as i32,
y: (prop.pos_y * 1000f64) as i32,
z: (prop.pos_z * 1000f64) as i32,
rot_y: (prop.rot_y * 1000f64) as i32,
x: (prop.pos.x),
y: (prop.pos.y),
z: (prop.pos.z),
rot_y: (prop.rot.y),
};
let entity_info = SceneEntityInfo {
inst_id: prop.id,
inst_id: prop.inst_id,
group_id: prop.group_id,
motion: Some(prop_position.to_motion()),
entity: Some(Entity::Prop(ScenePropInfo {
prop_state: prop_state as u32,
prop_state: prop.prop_state,
prop_id: prop.prop_id,
..Default::default()
})),
@@ -343,26 +291,26 @@ async fn load_scene(
// Load NPCs
for npc in &group.npcs {
if loaded_npc.contains(&(npc.npcid)) || json.avatars.contains_key(&(npc.npcid)) {
if loaded_npc.contains(&(npc.npc_id)) || json.avatars.contains_key(&(npc.npc_id)) {
continue;
}
npc_entity_id += 1;
loaded_npc.push(npc.npcid);
loaded_npc.push(npc.npc_id);
let npc_position = Position {
x: (npc.pos_x * 1000f64) as i32,
y: (npc.pos_y * 1000f64) as i32,
z: (npc.pos_z * 1000f64) as i32,
rot_y: (npc.rot_y * 1000f64) as i32,
x: (npc.pos.x) as i32,
y: (npc.pos.y) as i32,
z: (npc.pos.z) as i32,
rot_y: (npc.rot.y) as i32,
};
let info = SceneEntityInfo {
inst_id: npc.id,
inst_id: npc.inst_id,
group_id: npc.group_id,
entity_id: npc_entity_id,
motion: Some(npc_position.to_motion()),
entity: Some(Entity::Npc(SceneNpcInfo {
npc_id: npc.npcid,
npc_id: npc.npc_id,
..Default::default()
})),
};
@@ -374,21 +322,21 @@ async fn load_scene(
for monster in &group.monsters {
monster_entity_id += 1;
let monster_position = Position {
x: (monster.pos_x * 1000f64) as i32,
y: (monster.pos_y * 1000f64) as i32,
z: (monster.pos_z * 1000f64) as i32,
rot_y: (monster.rot_y * 1000f64) as i32,
x: (monster.pos.x) as i32,
y: (monster.pos.y) as i32,
z: (monster.pos.z) as i32,
rot_y: (monster.rot.y) as i32,
};
let npc_monster = SceneNpcMonsterInfo {
monster_id: monster.npcmonster_id,
monster_id: monster.monster_id,
event_id: monster.event_id,
world_level: 6,
..Default::default()
};
let info = SceneEntityInfo {
inst_id: monster.id,
inst_id: monster.inst_id,
group_id: monster.group_id,
entity_id: monster_entity_id,
motion: Some(monster_position.to_motion()),
@@ -398,7 +346,20 @@ async fn load_scene(
group_info.entity_list.push(info);
}
// TODO: for now don't load group that have nothing in it
if group.props.is_empty() && group.npcs.is_empty() && group.monsters.is_empty() {
continue;
}
scene_info.entity_group_list.push(group_info);
scene_info.group_id_list.push(*group_id);
scene_info.group_state_list.push(SceneGroupState {
group_id: *group_id,
is_default: true,
state: 0,
});
}
// load player entity
@@ -411,20 +372,7 @@ async fn load_scene(
player_group.entity_list.push(SceneEntityInfo {
inst_id: 0,
entity_id: (*slot) + 1,
motion: Some(MotionInfo {
// pos
pos: Some(Vector {
x: json.position.x,
y: json.position.y,
z: json.position.z,
}),
// rot
rot: Some(Vector {
x: 0,
y: json.position.rot_y,
z: 0,
}),
}),
motion: Some(player_pos.clone()),
entity: Some(Entity::Actor(SceneActorInfo {
avatar_type: AvatarType::AvatarFormalType.into(),
base_avatar_id: *avatar_id,
@@ -436,37 +384,22 @@ async fn load_scene(
}
scene_info.entity_group_list.push(player_group);
if _save {
if save_scene {
session
.send(
CMD_ENTER_SCENE_BY_SERVER_SC_NOTIFY,
EnterSceneByServerScNotify {
.send(EnterSceneByServerScNotify {
scene: Some(scene_info.clone()),
lineup: Some(lineup_info),
..Default::default()
},
)
.await?;
session
.send(
CMD_SCENE_ENTITY_MOVE_SC_NOTIFY,
SceneEntityMoveScNotify {
entity_id: 0,
motion: Some(player_pos),
entry_id,
..Default::default()
},
)
})
.await?;
json.scene.entry_id = entry_id;
json.scene.floor_id = enterance.floor_id;
json.scene.plane_id = enterance.plane_id;
json.position.x = position.x;
json.position.y = position.y;
json.position.z = position.z;
json.position.rot_y = position.rot_y;
json.scene.floor_id = floor_id;
json.scene.plane_id = plane_id;
json.position.x = json_pos.x;
json.position.y = json_pos.y;
json.position.z = json_pos.z;
json.position.rot_y = json_pos.rot_y;
json.save().await;
}
-117
View File
@@ -1,117 +0,0 @@
// use super::*;
// static TUTORIAL_IDS: [u32; 463] = [
// 1001, 1002, 1003, 1004, 1005, 1007, 1008, 1010, 2000, 2001, 2002, 2003, 2004, 2008, 2010, 2011,
// 2012, 2013, 3001, 3002, 3003, 3004, 3005, 3006, 3008, 3009, 3010, 3011, 3012, 3202, 4002, 4003,
// 4004, 4005, 4006, 4007, 4008, 4009, 4010, 4011, 4012, 4013, 4014, 4015, 4016, 4017, 4018, 4019,
// 4020, 4021, 4022, 4023, 4024, 4025, 4026, 4027, 4028, 4029, 4030, 4031, 4032, 4033, 4034, 4036,
// 4037, 4038, 4039, 4040, 5001, 5002, 5003, 5004, 5005, 5006, 5007, 5008, 5009, 5010, 5011, 5012,
// 5013, 5014, 5015, 5016, 5017, 5018, 5019, 5021, 5022, 5023, 5024, 5025, 5026, 5027, 5028, 5029,
// 5030, 5031, 5032, 5033, 5034, 5035, 5036, 5038, 5039, 5041, 5044, 5045, 5046, 5047, 5048, 5049,
// 5050, 5051, 5052, 5053, 5054, 5055, 5056, 5057, 5058, 5059, 5060, 5061, 5062, 5063, 5064, 5065,
// 5070, 5071, 5072, 5073, 5074, 5075, 5077, 5078, 5079, 5081, 5082, 5083, 5084, 5085, 5086, 5087,
// 5088, 5089, 5090, 5091, 5092, 5093, 5094, 5095, 5096, 5097, 5098, 5099, 5100, 5101, 5102, 5103,
// 5104, 5105, 5106, 5107, 5108, 5109, 5110, 5111, 5112, 5113, 5114, 5115, 5116, 5130, 5131, 5132,
// 5133, 5134, 5135, 5140, 5141, 5142, 5143, 5144, 5145, 5146, 5150, 5151, 5152, 5153, 5154, 5155,
// 5156, 5157, 5158, 5159, 5160, 5161, 5162, 5172, 5173, 5174, 5175, 5176, 5177, 5178, 5179, 5180,
// 5181, 5182, 5183, 5184, 5185, 5186, 5187, 5188, 5189, 5190, 5191, 5192, 5193, 5194, 5195, 5196,
// 5197, 5198, 5199, 5200, 5201, 5202, 5301, 5302, 5303, 5304, 5305, 5306, 5308, 5309, 5310, 5322,
// 5326, 5312, 5311, 5313, 5307, 5314, 5315, 5316, 5317, 5318, 5319, 5320, 5321, 5323, 5324, 5325,
// 5350, 5387, 5352, 5388, 5355, 5360, 5361, 5356, 5357, 5358, 5359, 5351, 5365, 5366, 5367, 5368,
// 5375, 5376, 5377, 5389, 5374, 5369, 5370, 5371, 5372, 5373, 5390, 5380, 5381, 5400, 5382, 5383,
// 5392, 5378, 5384, 5393, 5408, 5394, 5395, 5391, 5385, 5397, 5398, 5399, 5386, 5401, 5402, 5403,
// 5404, 5405, 5406, 5407, 5409, 5410, 5510, 5511, 7001, 7002, 7003, 7004, 7005, 7007, 7008, 7009,
// 9001, 9002, 9003, 9004, 9005, 9006, 9007, 9008, 9009, 9010, 9011, 9012, 9013, 9014, 9015, 9016,
// 9017, 9018, 9019, 9020, 9021, 9022, 9023, 9024, 9025, 9026, 9027, 9028, 9029, 9030, 9031, 9032,
// 9033, 9034, 9035, 9036, 9037, 9038, 9039, 9040, 9041, 9042, 9043, 9044, 9045, 9046, 9047, 9048,
// 9049, 9050, 9051, 9052, 9053, 9201, 9202, 9203, 9204, 9205, 9206, 9207, 9208, 9209, 9210, 9211,
// 9212, 9213, 9214, 9301, 9302, 9303, 9304, 9305, 9306, 9307, 9308, 9309, 9310, 9311, 9313, 9314,
// 9315, 9316, 9317, 9318, 9319, 9321, 9322, 9323, 9324, 9325, 9801, 9802, 9803, 9804, 9805, 9806,
// 9807, 9808, 9809, 9810, 9811, 9812, 9813, 9814, 9816, 9817, 9818, 9819, 9815, 9820, 9901, 9903,
// 9904, 9905, 9906, 9907, 9908, 9610, 9611, 9612, 9613, 9614, 9615, 9616, 9617, 9618, 9619, 9620,
// 9621, 9622, 9623, 9624, 9625, 9626, 9627, 9628, 9629, 9630, 9631, 9701, 9702, 9703, 9704,
// ];
// static GUIDE_IDS: [u32; 326] = [
// 1101, 1102, 1103, 1104, 1105, 1108, 1109, 1116, 1117, 1118, 2006, 2007, 2008, 2105, 2106, 2107,
// 2200, 2201, 2202, 2203, 2204, 2205, 2206, 2207, 2208, 2209, 2210, 2211, 2212, 2213, 2214, 2215,
// 2216, 2217, 2218, 2219, 2220, 2221, 2222, 3007, 3105, 3106, 3107, 3108, 3109, 3201, 3202, 3203,
// 3204, 3205, 3206, 3207, 3208, 3209, 3210, 3211, 3212, 3213, 3214, 3215, 4001, 4101, 4102, 4103,
// 4104, 4105, 4106, 4107, 4109, 4110, 5101, 5102, 5103, 5104, 5105, 5106, 6001, 6002, 6003, 6004,
// 6008, 6009, 6010, 6011, 6012, 6014, 6015, 6018, 6020, 6021, 6023, 6024, 6025, 6027, 6028, 6029,
// 6030, 6031, 6032, 6033, 6034, 6035, 6036, 6037, 6038, 6039, 6040, 6041, 6042, 6043, 6044, 6045,
// 6046, 6047, 6048, 6049, 6050, 6051, 6052, 6053, 6054, 6055, 6056, 6057, 6058, 6059, 6060, 6061,
// 6062, 6063, 6064, 6065, 6066, 6067, 6068, 6069, 6070, 6071, 6072, 6073, 6074, 6075, 6076, 6077,
// 6078, 6079, 6080, 6081, 6082, 6083, 6085, 6086, 6087, 6088, 6089, 6090, 6091, 6092, 6093, 6094,
// 6095, 6096, 6097, 6098, 6099, 6100, 6101, 6102, 6103, 6104, 6105, 6106, 6107, 6108, 6109, 6110,
// 6111, 7090, 7091, 7092, 7501, 7502, 7503, 7504, 7506, 7507, 7508, 7509, 7511, 7514, 7515, 8001,
// 8002, 8003, 8004, 8006, 8007, 8008, 8010, 8011, 8012, 8013, 8014, 8015, 8016, 8017, 8018, 8019,
// 8020, 8021, 8022, 8023, 8024, 8025, 8026, 8027, 8028, 8038, 8039, 8047, 8050, 8051, 8052, 8055,
// 8056, 8057, 8058, 8059, 8061, 8062, 8063, 8064, 8065, 8066, 8067, 8069, 8070, 8072, 8073, 8074,
// 8075, 8076, 8078, 8079, 8080, 8090, 8091, 8092, 8093, 8094, 8095, 8096, 8100, 8101, 8102, 8103,
// 8104, 8105, 8106, 8107, 8108, 8109, 8110, 8111, 8112, 8113, 8122, 8123, 8124, 8140, 8141, 8142,
// 8143, 8144, 8145, 8146, 9101, 9102, 9103, 9104, 9105, 9107, 9108, 9109, 9110, 9111, 9112, 9113,
// 9114, 9115, 9116, 9117, 9118, 9119, 9120, 9201, 9202, 9203, 9204, 9205, 9206, 9207, 9208, 9209,
// 9210, 9211, 9212, 9301, 9303, 9304, 9305, 9601, 9602, 9603, 9604, 9605, 9701, 9702, 31001,
// 31102, 31103, 31105, 31106, 31109, 31204, 31206,
// ];
// pub async fn on_get_tutorial_cs_req(
// session: &mut PlayerSession,
// _body: &GetTutorialCsReq,
// ) -> Result<()> {
// session
// .send(
// CMD_GET_TUTORIAL_SC_RSP,
// GetTutorialScRsp {
// retcode: 0,
// tutorial_list: TUTORIAL_IDS
// .iter()
// .map(|id| Tutorial {
// id: *id,
// status: TutorialStatus::TutorialFinish.into(),
// })
// .collect(),
// },
// )
// .await
// }
// pub async fn on_get_tutorial_guide_cs_req(
// session: &mut PlayerSession,
// _body: &GetTutorialGuideCsReq,
// ) -> Result<()> {
// session
// .send(
// CMD_GET_TUTORIAL_GUIDE_SC_RSP,
// GetTutorialGuideScRsp {
// retcode: 0,
// tutorial_guide_list: GUIDE_IDS
// .iter()
// .map(|id| TutorialGuide {
// id: *id,
// status: TutorialStatus::TutorialFinish.into(),
// })
// .collect(),
// },
// )
// .await
// }
// pub async fn on_unlock_tutorial_guide_cs_req(
// session: &mut PlayerSession,
// body: &UnlockTutorialGuideCsReq,
// ) -> Result<()> {
// session
// .send(
// CMD_UNLOCK_TUTORIAL_GUIDE_SC_RSP,
// UnlockTutorialGuideScRsp {
// retcode: 0,
// tutorial_guide: Some(TutorialGuide {
// id: body.group_id,
// status: TutorialStatus::TutorialUnlock.into(),
// }),
// },
// )
// .await
// }
-2
View File
@@ -4,8 +4,6 @@ mod handlers;
mod packet;
mod session;
mod tools;
mod tools_res;
pub use packet::NetPacket;
pub use session::PlayerSession;
+69 -46
View File
@@ -5,6 +5,28 @@ use tokio::net::TcpStream;
use tracing::Instrument;
use proto::*;
#[allow(unused_imports)]
use proto::{
CmdActivityType::*, CmdAdventureType::*, CmdAetherDivideType::*, CmdAlleyType::*,
CmdArchiveType::*, CmdAvatarType::*, CmdBattleCollegeType::*, CmdBattlePassType::*,
CmdBattleType::*, CmdBoxingClubType::*, CmdChallengeType::*, CmdChatType::*,
CmdChessRogueType::*, CmdClockParkType::*, CmdContentPackageType::*, CmdDailyActiveType::*,
CmdDrinkMakerType::*, CmdExpeditionType::*, CmdFantasticStoryActivityType::*,
CmdFeverTimeActivityType::*, CmdFightActivityType::*, CmdFightMathc3Type::*, CmdFightType::*,
CmdFriendType::*, CmdGachaType::*, CmdHeartdialType::*, CmdHeliobusType::*, CmdItemType::*,
CmdJukeboxType::*, CmdLineupType::*, CmdLobbyType::*, CmdMailType::*, CmdMapRotationType::*,
CmdMatchThreeModuleType::*, CmdMatchType::*, CmdMessageType::*, CmdMiscModuleType::*,
CmdMissionType::*, CmdMonopolyType::*, CmdMultiplayerType::*, CmdMultipleDropType::*,
CmdMuseumType::*, CmdOfferingType::*, CmdPamMissionType::*, CmdPhoneType::*,
CmdPlayerBoardType::*, CmdPlayerReturnType::*, CmdPlayerSync::*, CmdPlayerType::*,
CmdPlotType::*, CmdPunkLordType::*, CmdQuestType::*, CmdRaidCollectionType::*, CmdRaidType::*,
CmdRedDotType::*, CmdReplayType::*, CmdRndOptionType::*, CmdRogueCommonType::*,
CmdRogueEndless::*, CmdRogueModifierType::*, CmdRogueTournType::*, CmdRogueType::*,
CmdRollShopType::*, CmdSceneType::*, CmdServerPrefsType::*, CmdShopType::*, CmdSpaceZooType::*,
CmdStarFightType::*, CmdStoryLineType::*, CmdStrongChallengeActivityType::*,
CmdTalkRewardType::*, CmdTelevisionActivityType::*, CmdTextJoinType::*, CmdTrainVisitorType::*,
CmdTreasureDungeonType::*, CmdTutorialType::*, CmdWaypointType::*, CmdWolfBroType::*,
};
use super::handlers::*;
use super::PlayerSession;
@@ -58,12 +80,16 @@ impl NetPacket {
}
macro_rules! trait_handler {
($($name:ident $cmd_type:expr;)*) => {
($($name:tt;)*) => {
pub trait CommandHandler {
$(
paste! {
async fn [<on_$name:snake>](session: &mut PlayerSession, body: &$name) -> Result<()> {
[<on_$name:snake>](session, body).await
async fn [<on_$name:snake _cs_req>](session: &mut PlayerSession, request: &[<$name CsReq>]) -> Result<()> {
let mut response = proto::[<$name ScRsp>]::default();
let _ = [<on_$name:snake _cs_req>](session, request, &mut response).await;
session.send(response).await?;
Ok(())
}
}
)*
@@ -74,12 +100,14 @@ macro_rules! trait_handler {
session.send_dummy_response(cmd_type).await?;
return Ok(());
}
match cmd_type {
$(
$cmd_type => {
let body = $name::decode(&mut &payload[..])?;
cmd_type if cmd_type == paste! { [<Cmd$name CsReq>] as u16 } => {
let body = paste! { proto::[<$name CsReq>]::decode(&mut &payload[..])? };
paste! {
Self::[<on_$name:snake>](session, &body)
Self::[<on_$name:snake _cs_req>](session, &body)
.instrument(tracing::info_span!(stringify!([<on_$name:snake>]), cmd_type = cmd_type))
.await
}
@@ -96,59 +124,54 @@ macro_rules! trait_handler {
}
trait_handler! {
PlayerGetTokenCsReq 56; // PlayerGetTokenScRsp
PlayerLoginCsReq 68; // PlayerLoginScRsp, PlayerBasicInfo
GetMissionStatusCsReq 1224; // GetMissionStatusScRsp, Mission
GetBasicInfoCsReq 40; // GetBasicInfoScRsp, PlayerSettingInfo
GetMultiPathAvatarInfoCsReq 27;
GetAvatarDataCsReq 368; // GetAvatarDataScRsp, Avatar
GetAllLineupDataCsReq 724; // GetAllLineupDataScRsp, LineupInfo, ExtraLineupType, LineupAvatar, AmountInfo
GetCurLineupDataCsReq 711; // GetCurLineupDataScRsp
GetCurSceneInfoCsReq 1439; // GetCurSceneInfoScRsp, SceneInfo
PlayerHeartBeatCsReq 31; // PlayerHeartBeatScRsp
PlayerGetToken;
PlayerLogin;
GetMissionStatus;
GetBasicInfo;
GetMultiPathAvatarInfo;
GetAvatarData;
GetAllLineupData;
GetCurLineupData;
GetCurSceneInfo;
PlayerHeartBeat;
// // Tutorial (dummy!)
// GetTutorialGuideCsReq 1691;
// UnlockTutorialGuideCsReq 1630;
// GetTutorialCsReq 1661;
// Entity move (dummy!)
SceneEntityMoveCsReq 1468;
SceneEntityMove;
// Inventory (dummy!)
GetBagCsReq 568;
GetArchiveDataCsReq 2368;
DressAvatarCsReq 351;
TakeOffEquipmentCsReq 399;
DressRelicAvatarCsReq 334;
TakeOffRelicCsReq 398;
GetBag;
GetArchiveData;
DressAvatar;
TakeOffEquipment;
DressRelicAvatar;
TakeOffRelic;
// Chat (dummy!)
SendMsgCsReq 3968;
GetPrivateChatHistoryCsReq 3956;
GetFriendListInfoCsReq 2968;
GetFriendLoginInfoCsReq 2969;
SendMsg;
GetPrivateChatHistory;
GetFriendListInfo;
GetFriendLoginInfo;
// In-game lineup
JoinLineupCsReq 756;
ChangeLineupLeaderCsReq 748;
ReplaceLineupCsReq 790;
QuitLineupCsReq 739;
JoinLineup;
ChangeLineupLeader;
ReplaceLineup;
QuitLineup;
// Battle
StartCocoonStageCsReq 1445;
PveBattleResultCsReq 168;
SceneCastSkillCsReq 1456;
StartCocoonStage;
PveBattleResult;
SceneCastSkill;
// Teleport
GetEnteredSceneCsReq 1427;
GetSceneMapInfoCsReq 1436;
EnterSceneCsReq 1486;
GetEnteredScene;
GetSceneMapInfo;
EnterScene;
// Optional
GetMailCsReq 868;
GetGachaInfoCsReq 1968;
DoGachaCsReq 1911;
// GetQuestDataCsReq 961;
PlayerLoginFinishCsReq 73;
GetMail;
GetGachaInfo;
DoGacha;
PlayerLoginFinish;
}
+3 -2
View File
@@ -1,5 +1,6 @@
use anyhow::Result;
use prost::Message;
use proto::CmdID;
use tokio::{io::AsyncWriteExt, net::TcpStream};
use super::{packet::CommandHandler, NetPacket};
@@ -20,12 +21,12 @@ impl PlayerSession {
}
}
pub async fn send(&mut self, cmd_type: u16, body: impl Message) -> Result<()> {
pub async fn send(&mut self, body: impl Message + CmdID) -> Result<()> {
let mut buf = Vec::new();
body.encode(&mut buf)?;
let payload: Vec<u8> = NetPacket {
cmd_type,
cmd_type: body.get_cmd_id(),
head: Vec::new(),
body: buf,
}
+34 -12
View File
@@ -12,7 +12,6 @@ pub struct AvatarJson {
pub data: AvatarData,
pub level: u32,
pub promotion: u32,
// pub rank: u32,
#[serde(alias = "use_technique")]
#[serde(alias = "useTechnique")]
pub techniques: Vec<u32>,
@@ -32,7 +31,6 @@ impl AvatarJson {
pub fn to_avatar_proto(&self, lightcone: Option<&Lightcone>, relics: Vec<&Relic>) -> Avatar {
Avatar {
base_avatar_id: self.avatar_id,
exp: 0,
level: self.level,
promotion: self.promotion,
rank: self.data.rank,
@@ -68,7 +66,7 @@ impl AvatarJson {
) -> (BattleAvatar, Vec<BattleBuff>) {
let battle_avatar = BattleAvatar {
index,
avatar_type: AvatarType::AvatarFormalType.into(),
avatar_type: AvatarType::AvatarUpgradeAvailableType.into(),
id: self.avatar_id,
level: self.level,
rank: self.data.rank,
@@ -358,6 +356,8 @@ pub struct BattleConfig {
pub cycle_count: u32,
pub path_resonance_id: u32,
pub custom_stats: Vec<SubAffix>,
#[serde(default)]
pub scepters: Vec<RogueMagicScepter>,
}
impl Default for BattleConfig {
@@ -374,6 +374,7 @@ impl Default for BattleConfig {
cycle_count: Default::default(),
path_resonance_id: Default::default(),
custom_stats: Default::default(),
scepters: Default::default(),
}
}
}
@@ -429,6 +430,29 @@ impl BattleBuffJson {
}
}
#[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 {
@@ -463,13 +487,11 @@ impl Position {
pub fn to_motion(&self) -> MotionInfo {
MotionInfo {
// rot
rot: Some(Vector {
x: 0,
y: self.rot_y,
z: 0,
}),
// pos
pos: Some(Vector {
x: self.x,
y: self.y,
@@ -488,15 +510,15 @@ pub struct FreesrData {
#[serde(default)]
pub battle_config: BattleConfig,
#[serde(default)]
#[serde(default, skip_serializing)]
pub lineups: BTreeMap<u32, u32>,
#[serde(default)]
#[serde(default, skip_serializing)]
pub position: Position,
#[serde(default)]
#[serde(default, skip_serializing)]
pub scene: Scene,
#[serde(default)]
#[serde(default, skip_serializing)]
pub main_character: MultiPathAvatar,
#[serde(default)]
#[serde(default, skip_serializing)]
pub march_type: MultiPathAvatar,
}
@@ -517,7 +539,7 @@ pub struct Persistent {
impl Default for Persistent {
fn default() -> Self {
let mut lineups = BTreeMap::<u32, u32>::new();
lineups.insert(0, 8006);
lineups.insert(0, 8001);
lineups.insert(1, 0);
lineups.insert(2, 0);
lineups.insert(3, 0);
@@ -624,7 +646,7 @@ impl FreesrData {
async fn verify_lineup(&mut self) {
if self.lineups.is_empty() {
self.lineups = BTreeMap::<u32, u32>::from([(0, 8006), (1, 0), (2, 0), (3, 0)])
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);
-345
View File
@@ -1,345 +0,0 @@
use lazy_static::lazy_static;
use serde::{Deserialize, Serialize};
use serde_json::Value;
use std::collections::HashMap;
#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct LevelGroup {
#[serde(rename = "GroupGUID")]
pub group_guid: String,
#[serde(default)]
#[serde(rename = "LoadSide")]
pub load_side: LoadSide,
#[serde(default)]
#[serde(rename = "LoadOnInitial")]
pub load_on_initial: bool,
#[serde(default)]
#[serde(rename = "AnchorList")]
pub anchor_list: Vec<LevelAnchor>,
#[serde(default)]
#[serde(rename = "MonsterList")]
pub monster_list: Vec<LevelMonster>,
#[serde(default)]
#[serde(rename = "PropList")]
pub prop_list: Vec<LevelProp>,
#[serde(default)]
#[serde(rename = "NPCList")]
pub npc_list: Vec<LevelNPC>,
}
#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct LevelProp {
#[serde(rename = "ID")]
pub id: u32, // need
#[serde(default)]
#[serde(rename = "Category")]
pub category: String,
#[serde(default)]
#[serde(rename = "GroupName")]
pub group_name: String,
#[serde(default)]
#[serde(rename = "LoadSide")]
pub load_side: Option<LoadSide>, // need
#[serde(default)]
#[serde(rename = "PosX")]
pub pos_x: f64, // n
#[serde(default)]
#[serde(rename = "PosY")]
pub pos_y: f64, // n
#[serde(default)]
#[serde(rename = "PosZ")]
pub pos_z: f64, // n
#[serde(default)]
#[serde(rename = "RotY")]
pub rot_y: f64, // n
#[serde(rename = "PropID")]
pub prop_id: u32, // n
#[serde(rename = "AnchorID")]
pub anchor_id: Option<u32>, // n
#[serde(rename = "AnchorGroupID")]
pub anchor_group_id: Option<u32>, // n
#[serde(rename = "MappingInfoID")]
pub mapping_info_id: Option<u32>, // n
#[serde(rename = "InitLevelGraph")]
pub init_level_graph: Option<String>,
#[serde(default)]
#[serde(rename = "State")]
pub state: PropState,
#[serde(default)]
pub prop_state_list: Vec<PropState>,
#[serde(default)]
pub group_id: u32,
#[serde(rename = "IsDelete")]
#[serde(default)]
pub is_delete: bool, // n
#[serde(rename = "IsClientOnly")]
#[serde(default)]
pub client_only: bool, // n
// #[serde(default)]
// pub is_door: bool,
// #[serde(default)]
// pub is_chest: bool,
#[serde(default)]
pub __test_field: String,
}
#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct LevelAnchor {
#[serde(rename = "ID")]
pub id: u32,
#[serde(default)]
#[serde(rename = "PosX")]
pub pos_x: f64,
#[serde(default)]
#[serde(rename = "PosY")]
pub pos_y: f64,
#[serde(default)]
#[serde(rename = "PosZ")]
pub pos_z: f64,
#[serde(default)]
#[serde(rename = "RotY")]
pub rot_y: f64,
#[serde(default)]
pub group_id: u32,
}
#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct LevelNPC {
#[serde(rename = "ID")]
pub id: u32,
#[serde(default)]
#[serde(rename = "PosX")]
pub pos_x: f64,
#[serde(default)]
#[serde(rename = "PosY")]
pub pos_y: f64,
#[serde(default)]
#[serde(rename = "PosZ")]
pub pos_z: f64,
#[serde(rename = "Name")]
pub name: String,
#[serde(default)]
#[serde(rename = "RotY")]
pub rot_y: f64,
#[serde(rename = "NPCID")]
pub npcid: u32,
#[serde(default)]
pub group_id: u32,
#[serde(rename = "IsDelete")]
#[serde(default)]
pub is_delete: bool,
#[serde(rename = "IsClientOnly")]
#[serde(default)]
pub client_only: bool,
}
#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct LevelMonster {
#[serde(rename = "ID")]
pub id: u32,
#[serde(default)]
#[serde(rename = "RotY")]
pub rot_y: f64,
#[serde(default)]
#[serde(rename = "PosX")]
pub pos_x: f64,
#[serde(default)]
#[serde(rename = "PosY")]
pub pos_y: f64,
#[serde(default)]
#[serde(rename = "PosZ")]
pub pos_z: f64,
#[serde(rename = "NPCMonsterID")]
pub npcmonster_id: u32,
#[serde(default)]
#[serde(rename = "EventID")]
pub event_id: u32,
#[serde(default)]
pub group_id: u32,
#[serde(rename = "IsDelete")]
#[serde(default)]
pub is_delete: bool,
#[serde(rename = "IsClientOnly")]
#[serde(default)]
pub client_only: bool,
}
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Copy)]
pub enum LoadSide {
Client = 0,
Server = 1,
Unk = 2,
}
impl Default for LoadSide {
fn default() -> Self {
Self::Client
}
}
#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct LevelFloor {
pub floor_id: u32,
pub floor_name: String,
pub start_group_index: u32,
pub start_anchor_id: u32,
}
#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct MapEntrance {
#[serde(rename = "ID")]
pub id: u32,
#[serde(default)]
#[serde(rename = "EntranceType")]
pub entrance_type: PlaneType,
#[serde(rename = "PlaneID")]
pub plane_id: u32,
#[serde(rename = "FloorID")]
pub floor_id: u32,
#[serde(rename = "BeginMainMissionList")]
pub begin_main_mission_list: Vec<Value>,
#[serde(rename = "FinishMainMissionList")]
pub finish_main_mission_list: Vec<Value>,
#[serde(rename = "FinishSubMissionList")]
pub finish_sub_mission_list: Vec<Value>,
}
#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct MazePlane {
#[serde(rename = "PlaneID")]
pub plane_id: u32,
#[serde(rename = "PlaneType")]
pub plane_type: PlaneType,
#[serde(rename = "SubType")]
pub sub_type: u32,
#[serde(rename = "MazePoolType")]
pub maze_pool_type: u32,
#[serde(rename = "WorldID")]
pub world_id: u32,
#[serde(rename = "StartFloorID")]
pub start_floor_id: u32,
#[serde(rename = "FloorIDList")]
pub floor_idlist: Vec<u32>,
}
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Copy)]
pub enum PlaneType {
Unknown = 0,
Maze = 2,
Train = 3,
Challenge = 4,
Rogue = 5,
Raid = 6,
AetherDivide = 7,
TrialActivity = 8,
#[serde(other)]
Town = 1,
}
impl Default for PlaneType {
fn default() -> Self {
Self::Maze
}
}
#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct MazeProp {
#[serde(rename = "ID")]
pub id: u32,
#[serde(rename = "PropType")]
pub prop_type: String,
#[serde(rename = "PropStateList")]
pub prop_state_list: Vec<PropState>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Default)]
pub enum PropState {
#[default] 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,
}
pub type IntMap<T> = HashMap<u32, T>;
pub type StringMap<T> = HashMap<String, T>;
#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct SimpleLevelGroup {
pub teleports: IntMap<LevelProp>,
pub group_items: IntMap<LevelGroupItem>,
}
#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct LevelGroupItem {
pub props: Vec<LevelProp>,
pub npcs: Vec<LevelNPC>,
pub monsters: Vec<LevelMonster>,
pub anchors: HashMap<u32, LevelAnchor>,
}
#[derive(Debug, Serialize, Deserialize)]
pub struct GameResources {
pub map_entrance: IntMap<MapEntrance>,
/// Key is P{PLANE_ID}_F{FLOOR_ID}
pub level_group: StringMap<SimpleLevelGroup>,
pub maze_prop: IntMap<MazeProp>,
pub maze_plane: IntMap<MazePlane>,
}
impl GameResources {
pub fn new() -> Self {
let str = std::fs::read_to_string("./resources.json")
.expect("resources.json is broken, pls redownload");
let res: Self =
serde_json::from_str(&str).expect("resources.json is broken, pls redownload");
res
}
}
lazy_static! {
pub static ref GAME_RESOURCES: GameResources = GameResources::new();
}
+1
View File
@@ -0,0 +1 @@
pub mod resources;
+145
View File
@@ -0,0 +1,145 @@
use serde::Deserialize;
use std::{collections::HashMap, fs, sync::LazyLock};
#[derive(Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct Vector {
pub x: i32, // or f32 depending on your needs
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 static GAME_RES: LazyLock<JsonConfig> = LazyLock::new(|| {
serde_json::from_str::<JsonConfig>(&fs::read_to_string("res.json").unwrap()).unwrap()
});
+12 -12
View File
@@ -1,21 +1,21 @@
{
"lineups": {
"0": 1222,
"1": 1223,
"2": 1220,
"3": 1308
"0": 1220,
"1": 1222,
"2": 1304,
"3": 1309
},
"position": {
"x": -3722,
"y": 56256,
"z": -87464,
"rot_y": 318488
"x": 64574,
"y": 1699,
"z": 258502,
"rot_y": 243401
},
"scene": {
"plane_id": 20321,
"floor_id": 20321001,
"entry_id": 2032101
"plane_id": 20332,
"floor_id": 20332001,
"entry_id": 2033201
},
"main_character": "FemaleHarmony",
"march_type": "MarchHunt"
"march_type": "FemaleHarmony"
}
+1
View File
@@ -6,6 +6,7 @@ version.workspace = true
[dependencies]
prost.workspace = true
prost-types.workspace = true
proto-derive.workspace = true
[build-dependencies]
prost-build.workspace = true
+37
View File
@@ -1,3 +1,9 @@
use std::{
fs,
io::{self, BufRead},
path::Path,
};
pub fn main() {
let proto_file = "StarRail.proto";
if std::path::Path::new(proto_file).exists() {
@@ -5,7 +11,38 @@ pub fn main() {
prost_build::Config::new()
.out_dir("out/")
.type_attribute(".", "#[derive(proto_derive::CmdID)]")
.compile_protos(&[proto_file], &["."])
.unwrap();
impl_message_id(Path::new("out/_.rs")).unwrap();
}
}
pub fn impl_message_id(path: &Path) -> io::Result<()> {
let file = fs::File::open(path)?;
let reader = io::BufReader::new(file);
let mut output = Vec::new();
let mut attr = None;
for line in reader.lines() {
let line = line?;
if line.contains("CmdID:") {
attr = Some(make_message_id_attr(&line).unwrap());
} else {
output.push(line);
if let Some(attr) = attr.take() {
output.push(attr);
}
}
}
fs::write(path, output.join("\n").as_bytes())?;
Ok(())
}
fn make_message_id_attr(line: &str) -> Option<String> {
let id = line.trim_start().split(' ').nth(2)?.parse::<u16>().ok()?;
Some(format!("#[cmdid({id})]"))
}
+50965 -11843
View File
File diff suppressed because it is too large Load Diff
+12
View File
@@ -0,0 +1,12 @@
[package]
name = "proto-derive"
version = "0.1.0"
edition = "2021"
[dependencies]
syn = "2.0.53"
quote = "1.0.35"
proc-macro2 = "1.0.79"
[lib]
proc-macro = true
+27
View File
@@ -0,0 +1,27 @@
use proc_macro::TokenStream;
use quote::{quote, ToTokens};
use syn::{parse_macro_input, DeriveInput, Meta, MetaList};
#[proc_macro_derive(CmdID, attributes(cmdid))]
pub fn message_id_derive(input: TokenStream) -> TokenStream {
let input = parse_macro_input!(input as DeriveInput);
let struct_name = input.ident;
let id = match input
.attrs
.iter()
.find(|attr| attr.path().is_ident("cmdid"))
{
Some(attr) => match attr.meta {
Meta::List(MetaList { ref tokens, .. }) => tokens.into_token_stream(),
_ => panic!("Invalid cmdid attribute value"),
},
_ => 0u16.into_token_stream(),
};
TokenStream::from(quote! {
impl crate::CmdID for #struct_name {
const CMD_ID: u16 = #id;
}
})
}
File diff suppressed because it is too large Load Diff
+11 -3
View File
@@ -1,4 +1,12 @@
mod cmd_types;
pub use cmd_types::*;
include!("../out/_.rs");
pub use prost::DecodeError as ProtobufDecodeError;
pub use prost::Message as Protobuf;
pub trait CmdID {
const CMD_ID: u16;
fn get_cmd_id(&self) -> u16 {
Self::CMD_ID
}
}
View File
+662361
View File
File diff suppressed because it is too large Load Diff
-1
View File
File diff suppressed because one or more lines are too long
+2 -9
View File
@@ -42,7 +42,6 @@ pub async fn query_gateway(parameters: Query<QueryGatewayParameters>) -> String
asset_bundle_url: config.asset_bundle_url.clone(),
ex_resource_url: config.ex_resource_url.clone(),
lua_url: config.lua_url.clone(),
// lua_version: config.lua_version.clone(),
ifix_version: String::from("0"),
unk1: true,
unk2: true,
@@ -51,14 +50,8 @@ pub async fn query_gateway(parameters: Query<QueryGatewayParameters>) -> String
unk5: true,
unk6: true,
unk7: true,
mmcachlfjaa: true,
ncikdciigof: true,
efknbdlnakj: true,
gpjcfcjdmol: true,
jidbdekohdh: true,
pphbpgbnoem: true,
dhppkkgjldl: true,
jipjkleanbd: true,
jgciiljehhe: true,
..Default::default()
}
} else {
+10 -10
View File
@@ -1,14 +1,14 @@
{
"CNBETAWin2.2.51": {
"asset_bundle_url": "https://autopatchcn.bhsr.com/asb/BetaLive/output_7037158_b67f5a6a68fb",
"ex_resource_url": "https://autopatchcn.bhsr.com/design_data/BetaLive/output_7033392_aaca9c1b456b",
"lua_url": "https://autopatchcn.bhsr.com/lua/BetaLive/output_7050564_f05a0f949b10",
"lua_version": "7050564"
"CNBETAWin2.5.51": {
"asset_bundle_url": "",
"ex_resource_url": "",
"lua_url": "",
"ifix_url": ""
},
"OSBETAWin2.2.51": {
"asset_bundle_url": "https://autopatchos.starrails.com/asb/BetaLive/output_7037158_b67f5a6a68fb",
"ex_resource_url": "https://autopatchos.starrails.com/design_data/BetaLive/output_7033392_aaca9c1b456b",
"lua_url": "https://autopatchos.starrails.com/lua/BetaLive/output_7050564_f05a0f949b10",
"lua_version": "7050564"
"CNBETAWin2.5.52": {
"asset_bundle_url": "",
"ex_resource_url": "",
"lua_url": "",
"ifix_url": ""
}
}
+11 -5
View File
@@ -1,8 +1,14 @@
{
"CNBETAWin2.4.51": {
"asset_bundle_url": "https://autopatchcn-ipv6.bhsr.com/asb/BetaLive/output_7663997_cd086af3f307",
"ex_resource_url": "https://autopatchcn-ipv6.bhsr.com/design_data/BetaLive/output_7689868_456662b48277",
"lua_url": "https://autopatchcn-ipv6.bhsr.com/lua/BetaLive/output_7668875_0231727458ad",
"ifix_url": "https://autopatchcn-ipv6.bhsr.com/ifix/BetaLive/output_0_40d2ce0253"
"CNBETAWin2.5.51": {
"asset_bundle_url": "",
"ex_resource_url": "",
"lua_url": "",
"ifix_url": ""
},
"CNBETAWin2.5.52": {
"asset_bundle_url": "",
"ex_resource_url": "",
"lua_url": "",
"ifix_url": ""
}
}