forked from reversedrooms/FireflySR
New command system, some new commands (scene enter, avatar max_traces)
This commit is contained in:
parent
2f66f13f4c
commit
66ad49dcb2
11 changed files with 198 additions and 113 deletions
61
gameserver/src/game/commands/avatar.rs
Normal file
61
gameserver/src/game/commands/avatar.rs
Normal file
|
@ -0,0 +1,61 @@
|
|||
use common::data::EXCEL_COLLECTION;
|
||||
|
||||
use super::*;
|
||||
|
||||
pub async fn max_traces(args: &[&str], session: &PlayerSession) -> Result<()> {
|
||||
let Some(Ok(avatar_id)) = args.get(0).map(|s| s.parse::<u32>()) else {
|
||||
return send_text(session, "Usage: /avatar max_traces [avatar_id]").await;
|
||||
};
|
||||
|
||||
{
|
||||
let mut player_info = session.context.player.borrow_mut();
|
||||
let avatar_comp = player_info.data.avatar_bin.as_mut().unwrap();
|
||||
|
||||
let Some(avatar) = avatar_comp
|
||||
.avatar_list
|
||||
.iter_mut()
|
||||
.find(|a| a.avatar_id == avatar_id)
|
||||
else {
|
||||
return send_text(session, &format!("Avatar {avatar_id} doesn't exist")).await;
|
||||
};
|
||||
|
||||
EXCEL_COLLECTION
|
||||
.avatar_skill_tree_configs
|
||||
.iter()
|
||||
.filter(|c| c.avatar_id == avatar_id)
|
||||
.map(|c| (c.point_id, c.max_level))
|
||||
.for_each(|(pt, lv)| {
|
||||
if let Some(skill_tree) = avatar
|
||||
.skill_tree_list
|
||||
.iter_mut()
|
||||
.find(|st| st.point_id == pt)
|
||||
{
|
||||
skill_tree.level = lv
|
||||
} else {
|
||||
avatar.skill_tree_list.push(AvatarSkillTreeBin {
|
||||
point_id: pt,
|
||||
level: lv,
|
||||
})
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
let avatar_mgr = session.context.avatar_mgr.borrow();
|
||||
session
|
||||
.send(
|
||||
CMD_PLAYER_SYNC_SC_NOTIFY,
|
||||
PlayerSyncScNotify {
|
||||
avatar_sync: Some(AvatarSync {
|
||||
avatar_list: avatar_mgr.avatar_list_proto(),
|
||||
}),
|
||||
..Default::default()
|
||||
},
|
||||
)
|
||||
.await?;
|
||||
|
||||
send_text(
|
||||
session,
|
||||
&format!("Successfully maxed out traces of avatar {avatar_id}"),
|
||||
)
|
||||
.await
|
||||
}
|
63
gameserver/src/game/commands/mod.rs
Normal file
63
gameserver/src/game/commands/mod.rs
Normal file
|
@ -0,0 +1,63 @@
|
|||
use anyhow::Result;
|
||||
use proto::*;
|
||||
|
||||
use crate::{net::PlayerSession, util};
|
||||
|
||||
mod avatar;
|
||||
mod relic;
|
||||
mod scene;
|
||||
|
||||
macro_rules! commands {
|
||||
($($category:ident $action:ident;)*) => {
|
||||
pub async fn execute_command(command: &str, session: &PlayerSession) -> Result<()> {
|
||||
let input = command[1..].split(" ").collect::<Vec<&str>>();
|
||||
|
||||
let (Some(category), Some(action)) = (input.get(0), input.get(1)) else {
|
||||
return send_text(session, "Usage: /[category] [action] [arg1] [arg2] ...").await;
|
||||
};
|
||||
|
||||
let args = &input[2..];
|
||||
if let Err(_) = match (*category, *action) {
|
||||
$(
|
||||
(stringify!($category), stringify!($action)) => {
|
||||
$category::$action(args, session).await
|
||||
}
|
||||
)*,
|
||||
_ => send_text(session, "Unknown command").await,
|
||||
} {
|
||||
return send_text(
|
||||
session,
|
||||
"Command execution failed. Re-check your input and try again.",
|
||||
)
|
||||
.await;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
commands! {
|
||||
avatar max_traces;
|
||||
relic give;
|
||||
scene enter;
|
||||
}
|
||||
|
||||
async fn send_text(session: &PlayerSession, content: &str) -> Result<()> {
|
||||
session
|
||||
.send(
|
||||
CMD_GET_PRIVATE_CHAT_HISTORY_SC_RSP,
|
||||
GetPrivateChatHistoryScRsp {
|
||||
contact_id: 13371337,
|
||||
chat_message_list: vec![ChatMessageData {
|
||||
sender_id: 13371337,
|
||||
message_type: MsgType::CustomText.into(),
|
||||
timestamp: util::cur_timestamp_seconds(),
|
||||
content: content.to_string(),
|
||||
..Default::default()
|
||||
}],
|
||||
..Default::default()
|
||||
},
|
||||
)
|
||||
.await
|
||||
}
|
42
gameserver/src/game/commands/relic.rs
Normal file
42
gameserver/src/game/commands/relic.rs
Normal file
|
@ -0,0 +1,42 @@
|
|||
use super::*;
|
||||
|
||||
const GIVE_RELIC_USAGE: &'static str = "Usage: /relic give [id] [level] [main_affix] [sub_affix_count] [sub_affix1_id] [sub_affix1_cnt] ...";
|
||||
pub async fn give(args: &[&str], session: &PlayerSession) -> Result<()> {
|
||||
if args.len() < 4 {
|
||||
return send_text(session, GIVE_RELIC_USAGE).await;
|
||||
}
|
||||
|
||||
let id = args[0].parse::<u32>()?;
|
||||
let level = args[1].parse::<u32>()?;
|
||||
let main_affix = args[2].parse::<u32>()?;
|
||||
let sub_affix_count = args[3].parse::<usize>()?;
|
||||
|
||||
if args.len() - 4 < (sub_affix_count * 2) as usize {
|
||||
return send_text(session, GIVE_RELIC_USAGE).await;
|
||||
}
|
||||
|
||||
let mut sub_affix_params = Vec::with_capacity(sub_affix_count);
|
||||
|
||||
let args = &args[4..];
|
||||
for i in 0..sub_affix_count {
|
||||
let sub_affix_id = args[i * 2].parse::<u32>()?;
|
||||
let sub_affix_cnt = args[i * 2 + 1].parse::<u32>()?;
|
||||
|
||||
sub_affix_params.push((sub_affix_id, sub_affix_cnt));
|
||||
}
|
||||
|
||||
let item_mgr = session.context.item_mgr.borrow();
|
||||
item_mgr.give_relic(id, level, main_affix, sub_affix_params)?;
|
||||
|
||||
session
|
||||
.send(
|
||||
CMD_PLAYER_SYNC_SC_NOTIFY,
|
||||
PlayerSyncScNotify {
|
||||
relic_list: item_mgr.relic_list_proto(),
|
||||
..Default::default()
|
||||
},
|
||||
)
|
||||
.await?;
|
||||
|
||||
send_text(session, "Relic added successfully").await
|
||||
}
|
25
gameserver/src/game/commands/scene.rs
Normal file
25
gameserver/src/game/commands/scene.rs
Normal file
|
@ -0,0 +1,25 @@
|
|||
use super::*;
|
||||
|
||||
pub async fn enter(args: &[&str], session: &PlayerSession) -> Result<()> {
|
||||
let Some(Ok(entry_id)) = args.get(0).map(|s| s.parse::<u32>()) else {
|
||||
return send_text(session, "Usage: /scene enter [entry_id]").await;
|
||||
};
|
||||
|
||||
let mut scene_mgr = session.context.scene_mgr.borrow_mut();
|
||||
let scene = match scene_mgr.enter_scene(entry_id) {
|
||||
Ok(scene_info) => Some(scene_info),
|
||||
Err(_) => return send_text(session, &format!("Failed to enter scene {entry_id}.")).await,
|
||||
};
|
||||
|
||||
let lineup_mgr = session.context.lineup_mgr.borrow();
|
||||
session
|
||||
.send(
|
||||
CMD_ENTER_SCENE_BY_SERVER_SC_NOTIFY,
|
||||
EnterSceneByServerScNotify {
|
||||
scene,
|
||||
lineup: Some(lineup_mgr.cur_lineup_proto()),
|
||||
reason: EnterSceneReason::None.into(),
|
||||
},
|
||||
)
|
||||
.await
|
||||
}
|
|
@ -16,7 +16,6 @@ pub struct GameContext {
|
|||
logged_in: OnceCell<()>,
|
||||
pub player: Arc<AtomicRefCell<PlayerInfo>>,
|
||||
pub avatar_mgr: Arc<AtomicRefCell<AvatarManager>>,
|
||||
pub command_mgr: Arc<AtomicRefCell<CommandManager>>,
|
||||
pub hero_basic_type_mgr: Arc<AtomicRefCell<HeroBasicTypeManager>>,
|
||||
pub item_mgr: Arc<AtomicRefCell<ItemManager>>,
|
||||
pub lineup_mgr: Arc<AtomicRefCell<LineupManager>>,
|
||||
|
@ -38,8 +37,7 @@ impl GameContext {
|
|||
Self {
|
||||
logged_in: OnceCell::new(),
|
||||
player: player.clone(),
|
||||
avatar_mgr: avatar_mgr.clone(),
|
||||
command_mgr: Arc::new(AtomicRefCell::new(CommandManager::new(item_mgr.clone()))),
|
||||
avatar_mgr,
|
||||
hero_basic_type_mgr: Arc::new(AtomicRefCell::new(HeroBasicTypeManager::new(
|
||||
player.clone(),
|
||||
))),
|
||||
|
|
|
@ -1,100 +0,0 @@
|
|||
use crate::{net::PlayerSession, util};
|
||||
use anyhow::Result;
|
||||
use proto::*;
|
||||
|
||||
use super::*;
|
||||
|
||||
pub struct CommandManager {
|
||||
item_mgr: Arc<AtomicRefCell<ItemManager>>,
|
||||
}
|
||||
|
||||
impl CommandManager {
|
||||
pub fn new(item_mgr: Arc<AtomicRefCell<ItemManager>>) -> Self {
|
||||
Self { item_mgr }
|
||||
}
|
||||
|
||||
pub async fn execute_command(&self, command: &str, session: &PlayerSession) -> Result<()> {
|
||||
let input = command.split(" ").collect::<Vec<&str>>();
|
||||
|
||||
let (Some(category), Some(action)) = (input.get(0), input.get(1)) else {
|
||||
return self
|
||||
.send_text(session, "Usage: /[category] [action] [arg1] [arg2] ...")
|
||||
.await;
|
||||
};
|
||||
|
||||
let args = &input[2..];
|
||||
if let Err(_) = match (*category, *action) {
|
||||
("/relic", "give") => self.give_relic(args, session).await,
|
||||
_ => self.send_text(session, "Unknown command").await,
|
||||
} {
|
||||
return self
|
||||
.send_text(
|
||||
session,
|
||||
"Command execution failed. Re-check your input and try again.",
|
||||
)
|
||||
.await;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
const GIVE_RELIC_USAGE: &'static str = "Usage: /relic give [id] [level] [main_affix] [sub_affix_count] [sub_affix1_id] [sub_affix1_cnt] ...";
|
||||
async fn give_relic(&self, args: &[&str], session: &PlayerSession) -> Result<()> {
|
||||
if args.len() < 4 {
|
||||
return self.send_text(session, Self::GIVE_RELIC_USAGE).await;
|
||||
}
|
||||
|
||||
let id = args[0].parse::<u32>()?;
|
||||
let level = args[1].parse::<u32>()?;
|
||||
let main_affix = args[2].parse::<u32>()?;
|
||||
let sub_affix_count = args[3].parse::<usize>()?;
|
||||
|
||||
if args.len() - 4 < (sub_affix_count * 2) as usize {
|
||||
return self.send_text(session, Self::GIVE_RELIC_USAGE).await;
|
||||
}
|
||||
|
||||
let mut sub_affix_params = Vec::with_capacity(sub_affix_count);
|
||||
|
||||
let args = &args[4..];
|
||||
for i in 0..sub_affix_count {
|
||||
let sub_affix_id = args[i * 2].parse::<u32>()?;
|
||||
let sub_affix_cnt = args[i * 2 + 1].parse::<u32>()?;
|
||||
|
||||
sub_affix_params.push((sub_affix_id, sub_affix_cnt));
|
||||
}
|
||||
|
||||
let item_mgr = self.item_mgr.borrow();
|
||||
item_mgr.give_relic(id, level, main_affix, sub_affix_params)?;
|
||||
|
||||
session
|
||||
.send(
|
||||
CMD_PLAYER_SYNC_SC_NOTIFY,
|
||||
PlayerSyncScNotify {
|
||||
relic_list: item_mgr.relic_list_proto(),
|
||||
..Default::default()
|
||||
},
|
||||
)
|
||||
.await?;
|
||||
|
||||
self.send_text(session, "Relic added successfully").await
|
||||
}
|
||||
|
||||
async fn send_text(&self, session: &PlayerSession, content: &str) -> Result<()> {
|
||||
session
|
||||
.send(
|
||||
CMD_GET_PRIVATE_CHAT_HISTORY_SC_RSP,
|
||||
GetPrivateChatHistoryScRsp {
|
||||
contact_id: 13371337,
|
||||
chat_message_list: vec![ChatMessageData {
|
||||
sender_id: 13371337,
|
||||
message_type: MsgType::CustomText.into(),
|
||||
timestamp: util::cur_timestamp_seconds(),
|
||||
content: content.to_string(),
|
||||
..Default::default()
|
||||
}],
|
||||
..Default::default()
|
||||
},
|
||||
)
|
||||
.await
|
||||
}
|
||||
}
|
|
@ -3,7 +3,6 @@ use atomic_refcell::AtomicRefCell;
|
|||
use std::sync::{Arc, Weak};
|
||||
|
||||
mod avatar;
|
||||
mod command;
|
||||
mod hero_basic_type;
|
||||
mod item;
|
||||
mod lineup;
|
||||
|
@ -12,7 +11,6 @@ mod time;
|
|||
mod tutorial;
|
||||
|
||||
pub use avatar::AvatarManager;
|
||||
pub use command::CommandManager;
|
||||
pub use hero_basic_type::HeroBasicTypeManager;
|
||||
pub use item::ItemManager;
|
||||
pub use lineup::LineupManager;
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
pub mod commands;
|
||||
mod context;
|
||||
mod gameplay_config;
|
||||
pub mod managers;
|
||||
|
|
|
@ -1,13 +1,10 @@
|
|||
use crate::util;
|
||||
use crate::{game::commands, util};
|
||||
|
||||
use super::*;
|
||||
|
||||
pub async fn on_send_msg_cs_req(session: &PlayerSession, body: &SendMsgCsReq) -> Result<()> {
|
||||
let command_mgr = session.context.command_mgr.borrow();
|
||||
if body.message_text.starts_with("/") {
|
||||
command_mgr
|
||||
.execute_command(&body.message_text, session)
|
||||
.await?;
|
||||
commands::execute_command(&body.message_text, session).await?;
|
||||
}
|
||||
|
||||
session
|
||||
|
|
|
@ -59,7 +59,7 @@ impl NetPacket {
|
|||
|
||||
macro_rules! trait_handler {
|
||||
($($name:ident $cmd_type:expr;)*) => {
|
||||
pub trait CommandHandler {
|
||||
pub trait NetCommandHandler {
|
||||
$(
|
||||
paste! {
|
||||
async fn [<on_$name:snake>](session: &PlayerSession, body: &$name) -> Result<()> {
|
||||
|
|
|
@ -10,7 +10,7 @@ use tokio::{
|
|||
|
||||
use crate::game::{GameContext, PlayerInfo};
|
||||
|
||||
use super::{packet::CommandHandler, NetPacket};
|
||||
use super::{packet::NetCommandHandler, NetPacket};
|
||||
|
||||
pub struct PlayerSession {
|
||||
client_socket: Arc<Mutex<TcpStream>>,
|
||||
|
@ -82,4 +82,4 @@ impl PlayerSession {
|
|||
}
|
||||
|
||||
// Auto implemented
|
||||
impl CommandHandler for PlayerSession {}
|
||||
impl NetCommandHandler for PlayerSession {}
|
||||
|
|
Loading…
Reference in a new issue