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

View File

@ -1,5 +1,5 @@
[workspace]
members = [ "gameserver", "proto", "sdkserver"]
members = ["gameserver", "proto", "sdkserver"]
resolver = "2"
[workspace.package]
@ -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.

File diff suppressed because it is too large Load Diff

View File

@ -28,5 +28,6 @@ tracing-bunyan-formatter.workspace = true
prost.workspace = true
proto.workspace = true
proto-derive.workspace = true
rand.workspace = true

View File

@ -2,6 +2,7 @@ use anyhow::Result;
mod logging;
mod net;
mod tools;
mod util;
use logging::init_tracing;

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 {
nickname: String::from("RobinSR"),
level: 70,
world_level: 6,
stamina: 240,
..Default::default()
}),
..Default::default()
},
)
.await
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()
});
}

View File

@ -2,55 +2,48 @@ 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
.iter()
.map(|id| {
json.avatars
.get(id)
.map(|v| {
v.to_avatar_proto(
json.lightcones.iter().find(|v| v.equip_avatar == *id),
json.relics
.iter()
.filter(|v| v.equip_avatar == *id)
.collect(),
)
})
.unwrap_or(Avatar {
base_avatar_id: *id,
level: 80,
promotion: 6,
rank: 6,
skilltree_list: (1..=4)
.map(|m| AvatarSkillTree {
point_id: (*id) * 1000 + m,
level: 1,
})
.collect(),
..Default::default()
})
})
.collect(),
..Default::default()
},
)
.await
res.is_get_all = body.is_get_all;
res.avatar_list = UNLOCKED_AVATARS
.iter()
.map(|id| {
json.avatars
.get(id)
.map(|v| {
v.to_avatar_proto(
json.lightcones.iter().find(|v| v.equip_avatar == *id),
json.relics
.iter()
.filter(|v| v.equip_avatar == *id)
.collect(),
)
})
.unwrap_or(Avatar {
base_avatar_id: *id,
level: 80,
promotion: 6,
rank: 6,
skilltree_list: (1..=4)
.map(|m| AvatarSkillTree {
point_id: (*id) * 1000 + m,
level: 1,
})
.collect(),
first_met_timestamp: 1712924677,
..Default::default()
})
})
.collect();
}

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
}

View File

@ -8,118 +8,91 @@ 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 {
friend_name: String::from("RobinSR"),
simple_info: Some(SimpleInfo {
uid: 727,
platform_type: 3,
online_status: 1,
head_icon: 201008,
chat_bubble_id: 220005,
level: 70,
nickname: String::from("Server"),
signature: String::from("omg"),
..Default::default()
}),
is_marked: true,
sent_time: 0,
..Default::default()
}],
..Default::default()
},
)
.await
_session: &mut PlayerSession,
_req: &GetFriendListInfoCsReq,
res: &mut GetFriendListInfoScRsp,
) {
res.friend_list = vec![FriendListInfo {
friend_name: String::from("RobinSR"),
simple_info: Some(SimpleInfo {
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"),
..Default::default()
}),
is_marked: true,
sent_time: 0,
..Default::default()
}];
}
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 {
msg_type: MsgType::CustomText.into(),
sent_time: cur_timestamp_ms(),
text: "'sync'".to_string(),
sender_uid: 727,
..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
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_time,
text: String::from(*text),
sender_uid: SERVER_UID,
..Default::default()
})
.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 {
msg_type: body.msg_type,
text: String::from("Inventory Synced"),
emote: body.emote,
from_uid: 727, // from
to_uid: 25, // to
chat_type: body.chat_type,
},
)
.await?;
.send(RevcMsgScNotify {
msg_type: body.msg_type,
text: String::from("Inventory Synced"),
emote: body.emote,
from_uid: SERVER_UID, // from
to_uid: 25, // to
chat_type: body.chat_type,
..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 {
base_avatar_id: 8001,
cur_multi_path_avatar_type: mc as i32,
},
)
.await?;
.send(AvatarPathChangedNotify {
base_avatar_id: 8001,
cur_multi_path_avatar_type: mc as i32,
})
.await
.unwrap();
session
.send(
CMD_REVC_MSG_SC_NOTIFY,
RevcMsgScNotify {
msg_type: body.msg_type,
text: format!("Success change mc to {:#?}", mc),
emote: body.emote,
from_uid: 727, // from
to_uid: 25, // to
chat_type: body.chat_type,
},
)
.await?;
.send(RevcMsgScNotify {
msg_type: body.msg_type,
text: format!("Success change mc to {:#?}", mc),
emote: body.emote,
from_uid: SERVER_UID, // from
to_uid: 25, // to
chat_type: body.chat_type,
..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 {
base_avatar_id: 1001,
cur_multi_path_avatar_type: march_type as i32,
},
)
.await?;
.send(AvatarPathChangedNotify {
base_avatar_id: 1001,
cur_multi_path_avatar_type: march_type as i32,
})
.await
.unwrap();
session
.send(
CMD_REVC_MSG_SC_NOTIFY,
RevcMsgScNotify {
msg_type: body.msg_type,
text: format!("Success change march to {:#?}", march_type),
emote: body.emote,
from_uid: 727, // from
to_uid: 25, // to
chat_type: body.chat_type,
},
)
.await?;
.send(RevcMsgScNotify {
msg_type: body.msg_type,
text: format!("Success change march to {:#?}", march_type),
emote: body.emote,
from_uid: SERVER_UID, // from
to_uid: 25, // to
chat_type: body.chat_type,
..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,72 +173,68 @@ 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 {
del_equipment_list: (2000..3500).collect(),
del_relic_list: (1..2000).collect(),
..Default::default()
},
)
.await?;
session
.send(
CMD_PLAYER_SYNC_SC_NOTIFY,
PlayerSyncScNotify {
avatar_sync: Some(AvatarSync {
avatar_list: json
.avatars
.values()
.map(|avatar| avatar.to_avatar_proto(Option::None, vec![]))
.collect::<Vec<_>>(),
}),
..Default::default()
},
)
.await?;
session
.send(
CMD_PLAYER_SYNC_SC_NOTIFY,
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?;
session
.send(
CMD_PLAYER_SYNC_SC_NOTIFY,
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()
},
)
.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<_>>(),
}),
..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()
}

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,47 +18,31 @@ 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)
.map(|v| GachaItem {
is_new: false,
gacha_item: Some(Item {
item_id: if v % 2 == 0 { 1310 } else { 1314 },
..Default::default()
}),
token_item: Some(ItemList {
item_list: vec![Item {
item_id: 251,
num: 100,
..Default::default()
}],
}),
transfer_item_list: Some(ItemList { item_list: vec![] }),
})
.collect(),
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 {
item_id: if v % 2 == 0 { 1310 } else { 1314 },
..Default::default()
},
)
.await
}
}),
token_item: Some(ItemList {
item_list: vec![Item {
item_id: 251,
num: 100,
..Default::default()
}],
}),
transfer_item_list: Some(ItemList { item_list: vec![] }),
})
.collect();
}

View File

@ -1,96 +1,66 @@
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
.lightcones
.iter()
.map(|v| v.to_equipment_proto())
.collect(),
relic_list: player.relics.iter().map(|v| v.to_relic_proto()).collect(),
material_list: vec![
Material {
tid: 101, // Normal Pass
num: 999999,
..Default::default()
},
Material {
tid: 102, // Special Pass
num: 999999,
..Default::default()
},
],
..Default::default()
},
)
.await
res.equipment_list = player
.lightcones
.iter()
.map(|v| v.to_equipment_proto())
.collect();
res.relic_list = player.relics.iter().map(|v| v.to_relic_proto()).collect();
res.material_list = vec![
Material {
tid: 101, // Normal Pass
num: 999999,
..Default::default()
},
Material {
tid: 102, // Special Pass
num: 999999,
..Default::default()
},
];
}
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,
) {
}

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,95 +38,63 @@ 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
{
let mut player = tools::FreesrData::load().await;
let lineups = &mut player.lineups;
lineups.insert(
body.slot,
if body.base_avatar_id == 8001 {
player.main_character as u32
} else if body.base_avatar_id == 1001 {
player.march_type as u32
} else {
body.base_avatar_id
},
);
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
_res: &mut JoinLineupScRsp,
) {
let mut player = tools::FreesrData::load().await;
let lineups = &mut player.lineups;
lineups.insert(
body.slot,
if body.base_avatar_id == 8001 {
player.main_character as u32
} else if body.base_avatar_id == 1001 {
player.march_type as u32
} else {
body.base_avatar_id
},
);
player.save_lineup().await;
refresh_lineup(session, &player).await;
}
pub async fn on_replace_lineup_cs_req(
_session: &mut PlayerSession,
req: &ReplaceLineupCsReq,
) -> Result<()> {
{
let mut player = tools::FreesrData::load().await;
_res: &mut ReplaceLineupScRsp,
) {
let mut player = tools::FreesrData::load().await;
let lineups = &mut player.lineups;
for (slot, avatar_id) in &mut *lineups {
if let Some(lineup) = req.slots.get(*slot as usize) {
*avatar_id = if lineup.id == 8001 {
player.main_character as u32
} else if lineup.id == 1001 {
player.march_type as u32
} else {
lineup.id
};
let lineups = &mut player.lineups;
for (slot, avatar_id) in &mut *lineups {
if let Some(lineup) = req.slots.get(*slot as usize) {
*avatar_id = if lineup.id == 8001 {
player.main_character as u32
} else if lineup.id == 1001 {
player.march_type as u32
} else {
*avatar_id = 0;
}
lineup.id
};
} else {
*avatar_id = 0;
}
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
player.save_lineup().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
})
.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;
}

View File

@ -1,37 +1,29 @@
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 {
title: String::from("Welcome!"),
sender: String::from("Server"),
content: String::from("Welcome!"),
id: 1,
is_read: false,
time: 1716041089,
expire_time: 1718633089,
para_list: vec![],
attachment: Some(ItemList {
item_list: vec![Item {
item_id: 1310,
level: 80,
num: 1,
..Default::default()
}],
}),
mail_type: MailType::Normal.into(),
template_id: 0,
}],
notice_mail_list: vec![],
start: 0,
retcode: 0,
total_num: 1,
},
)
.await
}
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!"),
id: 1,
is_read: false,
time: 1716041089,
expire_time: 1718633089,
para_list: vec![],
attachment: Some(ItemList {
item_list: vec![Item {
item_id: 1310,
level: 80,
num: 1,
..Default::default()
}],
}),
mail_type: MailType::Normal.into(),
template_id: 0,
}];
}

View File

@ -1,23 +1,18 @@
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
.sub_mission_id_list
.iter()
.map(|id| Mission {
id: *id,
progress: 1,
status: MissionStatus::MissionFinish.into(),
})
.collect(),
..Default::default()
};
session.send(CMD_GET_MISSION_STATUS_SC_RSP, rsp).await
}
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 {
id: *id,
progress: 1,
status: MissionStatus::MissionFinish.into(),
})
.collect();
}

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::{

View File

@ -1,157 +1,113 @@
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 {
version: 51,
time: util::cur_timestamp_ms() as i64,
data: rbase64::decode("bG9jYWwgZnVuY3Rpb24gYmV0YV90ZXh0KG9iaikKICAgIGxvY2FsIGdhbWVPYmplY3QgPSBDUy5Vbml0eUVuZ2luZS5HYW1lT2JqZWN0LkZpbmQoIlVJUm9vdC9BYm92ZURpYWxvZy9CZXRhSGludERpYWxvZyhDbG9uZSkiKQoKICAgIGlmIGdhbWVPYmplY3QgdGhlbgogICAgICAgIGxvY2FsIHRleHRDb21wb25lbnQgPSBnYW1lT2JqZWN0OkdldENvbXBvbmVudEluQ2hpbGRyZW4odHlwZW9mKENTLlJQRy5DbGllbnQuTG9jYWxpemVkVGV4dCkpCgogICAgICAgIGlmIHRleHRDb21wb25lbnQgdGhlbgogICAgICAgICAgICB0ZXh0Q29tcG9uZW50LnRleHQgPSAiUm9iaW5TUiBpcyBhIGZyZWUgYW5kIG9wZW4gc291cmNlIHNvZnR3YXJlLiBkaXNjb3JkLmdnL3JldmVyc2Vkcm9vbXMiCiAgICAgICAgZW5kCiAgICBlbHNlCiAgICBlbmQKZW5kCgpiZXRhX3RleHQoKQ==").unwrap()
}),
},
)
.await
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: res.server_time_ms as i64,
data: rbase64::decode("bG9jYWwgZnVuY3Rpb24gYmV0YV90ZXh0KG9iaikKICAgIGxvY2FsIGdhbWVPYmplY3QgPSBDUy5Vbml0eUVuZ2luZS5HYW1lT2JqZWN0LkZpbmQoIlVJUm9vdC9BYm92ZURpYWxvZy9CZXRhSGludERpYWxvZyhDbG9uZSkiKQoKICAgIGlmIGdhbWVPYmplY3QgdGhlbgogICAgICAgIGxvY2FsIHRleHRDb21wb25lbnQgPSBnYW1lT2JqZWN0OkdldENvbXBvbmVudEluQ2hpbGRyZW4odHlwZW9mKENTLlJQRy5DbGllbnQuTG9jYWxpemVkVGV4dCkpCgogICAgICAgIGlmIHRleHRDb21wb25lbnQgdGhlbgogICAgICAgICAgICB0ZXh0Q29tcG9uZW50LnRleHQgPSAiUm9iaW5TUiBpcyBhIGZyZWUgYW5kIG9wZW4gc291cmNlIHNvZnR3YXJlLiBkaXNjb3JkLmdnL3JldmVyc2Vkcm9vbXMiCiAgICAgICAgZW5kCiAgICBlbHNlCiAgICBlbmQKZW5kCgpiZXRhX3RleHQoKQ==").unwrap()
});
}
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 {
data: Some(PackageData {
info_list: vec![
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,
},
],
..Default::default()
}),
},
)
.await?;
Ok(())
.send(ContentPackageSyncDataScNotify {
data: Some(PackageData {
info_list: vec![
200001, 200002, 200003, 200004, 150017, 150015, 150021, 150018, 130011, 130012,
130013,
]
.iter()
.map(|v| ContentInfo {
status: ContentPackageStatus::Finished.into(),
content_id: *v,
})
.collect(),
..Default::default()
}),
})
.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![
MultiPathAvatarTypeInfo {
avatar_id: json.main_character as i32,
rank: 6,
equip_relic_list: json
.relics
.iter()
.filter(|v| v.equip_avatar == json.main_character as u32)
.map(|v| v.to_equipment_relic_proto())
.collect(),
skilltree_list: (1..=4)
.map(|v| AvatarSkillTree {
level: 1,
point_id: (json.main_character as u32) * 1000 + v,
})
.collect(),
path_equipment_id: json
.lightcones
.iter()
.find(|v| v.equip_avatar == json.main_character as u32)
.map(|v| v.internal_uid)
.unwrap_or_default(),
},
MultiPathAvatarTypeInfo {
avatar_id: json.march_type as i32,
rank: 6,
equip_relic_list: json
.relics
.iter()
.filter(|v| v.equip_avatar == json.march_type as u32)
.map(|v| v.to_equipment_relic_proto())
.collect(),
skilltree_list: (1..=4)
.map(|v| AvatarSkillTree {
level: 1,
point_id: (json.march_type as u32) * 1000 + v,
})
.collect(),
path_equipment_id: json
.lightcones
.iter()
.find(|v| v.equip_avatar == json.march_type as u32)
.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
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,
equip_relic_list: json
.relics
.iter()
.filter(|v| v.equip_avatar == json.main_character as u32)
.map(|v| v.to_equipment_relic_proto())
.collect(),
skilltree_list: (1..=4)
.map(|v| AvatarSkillTree {
level: 1,
point_id: (json.main_character as u32) * 1000 + v,
})
.collect(),
path_equipment_id: json
.lightcones
.iter()
.find(|v| v.equip_avatar == json.main_character as u32)
.map(|v| v.internal_uid)
.unwrap_or_default(),
},
MultiPathAvatarTypeInfo {
avatar_id: json.march_type as i32,
rank: 6,
equip_relic_list: json
.relics
.iter()
.filter(|v| v.equip_avatar == json.march_type as u32)
.map(|v| v.to_equipment_relic_proto())
.collect(),
skilltree_list: (1..=4)
.map(|v| AvatarSkillTree {
level: 1,
point_id: (json.march_type as u32) * 1000 + v,
})
.collect(),
path_equipment_id: json
.lightcones
.iter()
.find(|v| v.equip_avatar == json.march_type as u32)
.map(|v| v.internal_uid)
.unwrap_or_default(),
},
];
}

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 {
Some(scene)
} else {
Some(SceneInfo {
game_mode_type: 1,
entry_id: player.scene.entry_id,
plane_id: player.scene.plane_id,
floor_id: player.scene.floor_id,
..Default::default()
})
},
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()
})
};
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)| EnteredSceneInfo {
floor_id: v.floor_id,
plane_id: v.plane_id,
.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(),
}
})
.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,37 +257,30 @@ 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_id: prop.prop_id,
prop_state: prop.prop_state,
prop_id: prop.prop_id,
..Default::default()
})),
entity_id: prop_entity_id,
@ -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 {
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()
},
)
.send(EnterSceneByServerScNotify {
scene: Some(scene_info.clone()),
lineup: Some(lineup_info),
..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;
}

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
// }

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;

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;
}

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,
}

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);

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();
}

View File

@ -0,0 +1 @@
pub mod resources;

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()
});

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"
}

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

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})]"))
}

File diff suppressed because it is too large Load Diff

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

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

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
}
}

0
proto/src/message.rs Normal file
View File

662361
res.json Normal file

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

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 {

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": ""
}
}
}

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": ""
}
}