diff --git a/MinecraftClient/ChatBot.cs b/MinecraftClient/ChatBot.cs index 8fc29e62..9ed626ae 100644 --- a/MinecraftClient/ChatBot.cs +++ b/MinecraftClient/ChatBot.cs @@ -8,7 +8,8 @@ namespace MinecraftClient /// /// Welcome to the Bot API file ! /// The virtual class "ChatBot" contains anything you need for creating chat bots - /// Inherit from this class while adding your bot class to the folder "Bots". + /// Inherit from this class while adding your bot class to the folder "ChatBots". + /// Override the methods you want for handling events: Initialize, Update, GetText. /// Once your bot is created, read the explanations below to start using it in the MinecraftClient app. /// /// Pieces of code to add in other parts of the program for your bot. Line numbers are approximative. diff --git a/MinecraftClient/Command.cs b/MinecraftClient/Command.cs new file mode 100644 index 00000000..ea0d2902 --- /dev/null +++ b/MinecraftClient/Command.cs @@ -0,0 +1,78 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace MinecraftClient +{ + /// + /// Represents an internal MCC command: Command name, source code and usage message + /// To add a new command, inherit from this class while adding the command class to the folder "Commands". + /// If inheriting from the 'Command' class and placed in the 'Commands' namespace, the command will be + /// automatically loaded and available in main chat prompt, scripts, remote control and command help. + /// + + public abstract class Command + { + /// + /// The command name + /// + + public abstract string CMDName { get; } + + /// + /// Usage message, eg: 'name [args]: do something' + /// + + public abstract string CMDDesc { get; } + + /// + /// Perform the command + /// + /// The full command, eg: 'mycommand arg1 arg2' + /// A confirmation/error message, or "" if no message + + public abstract string Run(McTcpClient handler, string command); + + /// + /// Return a list of aliases for this command. + /// Override this method if you wish to put aliases to the command + /// + + public virtual IEnumerable getCMDAliases() { return new string[0]; } + + /// + /// Check if at least one argument has been passed to the command + /// + + public static bool hasArg(string command) + { + int first_space = command.IndexOf(' '); + return (first_space > 0 && first_space < command.Length - 1); + } + + /// + /// Extract the argument string from the command + /// + /// Argument or "" if no argument + + public static string getArg(string command) + { + if (hasArg(command)) + { + return command.Substring(command.IndexOf(' ') + 1); + } + else return ""; + } + + /// + /// Extract the arguments as a string array from the command + /// + /// Argument array or empty array if no arguments + + public static string[] getArgs(string command) + { + return getArg(command).Split(' '); + } + } +} diff --git a/MinecraftClient/Commands/Connect.cs b/MinecraftClient/Commands/Connect.cs new file mode 100644 index 00000000..d36890fd --- /dev/null +++ b/MinecraftClient/Commands/Connect.cs @@ -0,0 +1,24 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace MinecraftClient.Commands +{ + public class Connect : Command + { + public override string CMDName { get { return "connect"; } } + public override string CMDDesc { get { return "connect : connect to the specified server."; } } + + public override string Run(McTcpClient handler, string command) + { + if (hasArg(command)) + { + Settings.setServerIP(getArgs(command)[0]); + Program.Restart(); + return ""; + } + else return CMDDesc; + } + } +} diff --git a/MinecraftClient/Commands/Exit.cs b/MinecraftClient/Commands/Exit.cs new file mode 100644 index 00000000..db26410d --- /dev/null +++ b/MinecraftClient/Commands/Exit.cs @@ -0,0 +1,24 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace MinecraftClient.Commands +{ + public class Exit : Command + { + public override string CMDName { get { return "exit"; } } + public override string CMDDesc { get { return "exit: disconnect from the server."; } } + + public override string Run(McTcpClient handler, string command) + { + Program.Exit(); + return ""; + } + + public override IEnumerable getCMDAliases() + { + return new string[] { "quit" }; + } + } +} diff --git a/MinecraftClient/Commands/Reco.cs b/MinecraftClient/Commands/Reco.cs new file mode 100644 index 00000000..a691ae66 --- /dev/null +++ b/MinecraftClient/Commands/Reco.cs @@ -0,0 +1,19 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace MinecraftClient.Commands +{ + public class Reco : Command + { + public override string CMDName { get { return "reco"; } } + public override string CMDDesc { get { return "reco: restart and reconnect to the server."; } } + + public override string Run(McTcpClient handler, string command) + { + Program.Restart(); + return ""; + } + } +} diff --git a/MinecraftClient/Commands/Respawn.cs b/MinecraftClient/Commands/Respawn.cs new file mode 100644 index 00000000..ce894e52 --- /dev/null +++ b/MinecraftClient/Commands/Respawn.cs @@ -0,0 +1,19 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace MinecraftClient.Commands +{ + public class Respawn : Command + { + public override string CMDName { get { return "respawn"; } } + public override string CMDDesc { get { return "respawn: respawn after death."; } } + + public override string Run(McTcpClient handler, string command) + { + handler.SendRespawnPacket(); + return "You have respawned."; + } + } +} diff --git a/MinecraftClient/Commands/Script.cs b/MinecraftClient/Commands/Script.cs new file mode 100644 index 00000000..3273b60c --- /dev/null +++ b/MinecraftClient/Commands/Script.cs @@ -0,0 +1,23 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace MinecraftClient.Commands +{ + public class Script : Command + { + public override string CMDName { get { return "script"; } } + public override string CMDDesc { get { return "script : run a script file."; } } + + public override string Run(McTcpClient handler, string command) + { + if (hasArg(command)) + { + handler.BotLoad(new ChatBots.Script(getArg(command))); + return ""; + } + else return CMDDesc; + } + } +} diff --git a/MinecraftClient/Commands/Send.cs b/MinecraftClient/Commands/Send.cs new file mode 100644 index 00000000..f47e6450 --- /dev/null +++ b/MinecraftClient/Commands/Send.cs @@ -0,0 +1,23 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace MinecraftClient.Commands +{ + public class Send : Command + { + public override string CMDName { get { return "send"; } } + public override string CMDDesc { get { return "send : send a chat message or command."; } } + + public override string Run(McTcpClient handler, string command) + { + if (hasArg(command)) + { + handler.SendChatMessage(getArg(command)); + return ""; + } + else return CMDDesc; + } + } +} diff --git a/MinecraftClient/Commands/Set.cs b/MinecraftClient/Commands/Set.cs new file mode 100644 index 00000000..9316077a --- /dev/null +++ b/MinecraftClient/Commands/Set.cs @@ -0,0 +1,31 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace MinecraftClient.Commands +{ + public class Set : Command + { + public override string CMDName { get { return "set"; } } + public override string CMDDesc { get { return "set varname=value: set a custom %variable%."; } } + + public override string Run(McTcpClient handler, string command) + { + if (hasArg(command)) + { + string[] temp = getArg(command).Split('='); + if (temp.Length > 1) + { + if (Settings.setVar(temp[0], getArg(command).Substring(temp[0].Length + 1))) + { + return ""; //Success + } + else return "variable name must be A-Za-z0-9."; + } + else return CMDDesc; + } + else return CMDDesc; + } + } +} diff --git a/MinecraftClient/McTcpClient.cs b/MinecraftClient/McTcpClient.cs index 743632ad..590e19af 100644 --- a/MinecraftClient/McTcpClient.cs +++ b/MinecraftClient/McTcpClient.cs @@ -17,6 +17,8 @@ namespace MinecraftClient public class McTcpClient : IMinecraftComHandler { + private static List cmd_names = new List(); + private static Dictionary cmds = new Dictionary(); private List bots = new List(); private static List scripts_on_hold = new List(); public void BotLoad(ChatBot b) { b.SetHandler(this); bots.Add(b); b.Initialize(); Settings.SingleCommand = ""; } @@ -206,91 +208,59 @@ namespace MinecraftClient public bool performInternalCommand(string command, ref string response_msg) { - response_msg = ""; - string[] command_args = command.Split(' '); - string command_name = command_args[0].ToLower(); - switch (command_name) + /* Load commands from the 'Commands' namespace */ + + if (cmds.Count == 0) { - case "exit": - case "quit": - Program.Exit(); - break; - - case "reco": - Program.Restart(); - break; - - case "respawn": - handler.SendRespawnPacket(); - response_msg = "You have respawned."; - break; - - case "send": - if (command.Length > 5) + Type[] cmds_classes = Program.GetTypesInNamespace("MinecraftClient.Commands"); + foreach (Type type in cmds_classes) + { + if (type.IsSubclassOf(typeof(Command))) { - string text = command.Substring(5); - SendChatMessage(text); - } - else response_msg = "send : send a chat message or command."; - break; - - case "set": - if (command.Length > 3) - { - string[] temp = command.Substring(4).Split('='); - if (temp.Length > 1) + try { - if (!Settings.setVar(temp[0], command.Substring(temp[0].Length + 5))) - { - response_msg = "variable name must be A-Za-z0-9."; - } + Command cmd = (Command)Activator.CreateInstance(type); + cmds[cmd.CMDName.ToLower()] = cmd; + cmd_names.Add(cmd.CMDName.ToLower()); + foreach (string alias in cmd.getCMDAliases()) + cmds[alias.ToLower()] = cmd; } - else response_msg = "set varname=value: set a custom %variable%."; - } - else response_msg = "set varname=value: set a custom %variable%."; - break; - - case "script": - if (command.Length > 8) - { - BotLoad(new ChatBots.Script(command.Substring(8))); - } - else response_msg = "script : run a script file."; - break; - - case "connect": - if (command_args.Length > 1) - { - Settings.setServerIP(command_args[1]); - Program.Restart(); - } - else response_msg = "connect : connect to the specified server."; - break; - - case "help": - if (command.Length >= 6) - { - string help_cmd_name = command.Substring(5).ToLower(); - switch (help_cmd_name) + catch (Exception e) { - case "quit": response_msg = "quit: disconnect from the server."; break; - case "exit": response_msg = "exit: disconnect from the server."; break; - case "reco": response_msg = "reco: restart and reconnct to the server."; break; - case "respawn": response_msg = "respawn: respawn after death."; break; - case "send": response_msg = "send : send a chat message or command."; break; - case "set": response_msg = "set varname=value: set a custom %variable%."; break; - case "script": response_msg = "script : run a script file."; break; - case "connect": response_msg = "connect : connect to the specified server."; break; - case "help": response_msg = "help : show brief help about a command."; break; - default: response_msg = "help: unknown command '" + help_cmd_name + "'."; break; + ConsoleIO.WriteLine(e.Message); } } - else response_msg = "help . Available commands: exit, reco, script, send, connect."; - break; + } + } - default: - response_msg = "Unknown command '" + command_name + "'. Use 'help' for help."; - return false; + /* Process the provided command */ + + string command_name = command.Split(' ')[0].ToLower(); + if (command_name == "help") + { + if (Command.hasArg(command)) + { + string help_cmdname = Command.getArgs(command)[0].ToLower(); + if (help_cmdname == "help") + { + response_msg = "help : show brief help about a command."; + } + else if (cmds.ContainsKey(help_cmdname)) + { + response_msg = cmds[help_cmdname].CMDDesc; + } + else response_msg = "Unknown command '" + command_name + "'. Use 'help' for command list."; + } + else response_msg = "help . Available commands: " + String.Join(", ", cmd_names.ToArray()); + } + else if (cmds.ContainsKey(command_name)) + { + response_msg = cmds[command_name].Run(this, command); + } + else + { + response_msg = "Unknown command '" + command_name + "'. Use 'help' for help."; + return false; } return true; } @@ -404,5 +374,15 @@ namespace MinecraftClient } else return handler.SendChatMessage(text); } + + /// + /// Allow to respawn after death + /// + /// True if packet successfully sent + + public bool SendRespawnPacket() + { + return handler.SendRespawnPacket(); + } } } diff --git a/MinecraftClient/MinecraftClient.csproj b/MinecraftClient/MinecraftClient.csproj index 8add0d36..47f9a771 100644 --- a/MinecraftClient/MinecraftClient.csproj +++ b/MinecraftClient/MinecraftClient.csproj @@ -81,6 +81,14 @@ + + + + + + + + diff --git a/MinecraftClient/Program.cs b/MinecraftClient/Program.cs index 629520bf..a0265c1e 100644 --- a/MinecraftClient/Program.cs +++ b/MinecraftClient/Program.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.Linq; using System.Text; using MinecraftClient.Protocol; +using System.Reflection; namespace MinecraftClient { @@ -285,5 +286,18 @@ namespace MinecraftClient if (Client != null) { Client.Disconnect(); ConsoleIO.Reset(); } Environment.Exit(0); } + + /// + /// Enumerate types in namespace through reflection + /// + /// Namespace to process + /// Assembly to use. Default is Assembly.GetExecutingAssembly() + /// + + public static Type[] GetTypesInNamespace(string nameSpace, Assembly assembly = null) + { + if (assembly == null) { assembly = Assembly.GetExecutingAssembly(); } + return assembly.GetTypes().Where(t => String.Equals(t.Namespace, nameSpace, StringComparison.Ordinal)).ToArray(); + } } }