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