1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146
//! //! Definitions for CommandSets. Depending on a variety of factors, you have access //! to different sets of commands. Some of the basic conditions ruling this would be: //! //! * Connection State //! * Roles //! use crate::comms::Client; use async_trait::async_trait; use std::fmt::Debug; use std::marker::{Send, Sync}; use tokio::sync::mpsc; use tokio_util::codec::LinesCodecError; #[macro_export] /// an easier way to create a command set from /// valid `impl Command` objects macro_rules! cmdset { ($($cmd: expr),+) => { { let mut cmds: Vec<Box<dyn Command+Send>> = Vec::new(); $( cmds.push(Box::new($cmd)); )* CmdSet::new(cmds) } }; } /// alias for sending channel pub type Tx = mpsc::UnboundedSender<String>; /// alias for recieving channel pub type Rx = mpsc::UnboundedReceiver<String>; /// Result generic resulting with decoding/encoding errors pub type LinesCodecResult<T> = Result<T, LinesCodecError>; /// Custom error type revolving around the success of executing commands pub type CommandResult<T> = Result<T, &'static str>; /// Current listening port of the MuOxi game engine pub static GAME_ADDR: &'static str = "127.0.0.1:4567"; /// Current listening port of the staging proxy server pub static PROXY_ADDR: &'static str = "127.0.0.1:8000"; /// defines a command trait #[async_trait] pub trait Command: Debug + Sync { /// name of command fn name(&self) -> &str; /// a list of aliases that will invoke command fn aliases(&self) -> Vec<&str>; /// execute the actual command but only directs commands to game_server, /// will err if client state is not in playing async fn execute_cmd(&self, game_server: &mut Client) -> CommandResult<()>; /// tests to see if supplied string is a valid command fn is_match<'a>(&self, cmd: &'a str) -> bool { let cmd = cmd.to_lowercase(); // first check to see if there is a match with the name itself if cmd == self.name() { return true; } if self.aliases().len() > 0 { for c in self.aliases().iter() { if *c == cmd { return true; } } } false } } /// Defines a common collection of commands /// /// The current set up for this logic is that CmdSet /// is a vector of mutable references to *trait objects* /// meaning that all commands and their nature must be defined /// within the Command trait, defining other data associated with /// the struct of the Command is futile as the compiler will ambiguous /// to the actual object the trait is attached too. For example: /// /// ### Example /// ```rust,ignore /// struct CmdLook /// impl CmdLook{ /// fn method(){ /// println!("Hello from object specific method"); /// } /// } /// impl Command for CmdLoop{ /// fn name() -> str{ /// "look" /// } /// ... /// } /// /// let cmdlook = CmdLook{other: 1}; /// let cmdset = cmdset![cmdlook]; /// let cmd = cmdset.get("look").unwrap(); /// /// cmd.name(); //valid because this method is defined in trait /// cmd.method() // invalid! Method returns object specific method which is invisible. /// ``` /// /// The main take away from this, is that Commands should run and be defined all /// within the Trait, creating a unit struct is just to give the Command a name. /// #[derive(Debug)] pub struct CmdSet { /// holds a list of valid commands in set pub cmds: Vec<Box<dyn Command + Send>>, } impl CmdSet { /// create a new command set based on appropriate structs that implement Command Trait pub fn new(cmds: Vec<Box<dyn Command + Send>>) -> Self { Self { cmds: cmds } } /// check to see if command exists within CmdSet /// and returns the dyn Command that it matches with /// this is still *fucking confusing* pub fn get(&mut self, cmd_string: String) -> Option<&mut (dyn Command + Send)> { let mut cntr = 0; for cmd in self.cmds.iter_mut() { if cmd.is_match(&cmd_string) { break; } cntr += 1; } if let Some(cmd) = self.cmds.get_mut(cntr) { return Some(&mut **cmd); } else { return None; } } }