Commands as separate classes

Each command is now in its own class in the 'Commands' namespace, and
loaded through reflection.
This commit is contained in:
ORelio 2014-06-18 13:32:17 +02:00
parent 715bc09872
commit 36690b8b34
12 changed files with 322 additions and 78 deletions

View file

@ -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.

View file

@ -0,0 +1,78 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace MinecraftClient
{
/// <summary>
/// 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.
/// </summary>
public abstract class Command
{
/// <summary>
/// The command name
/// </summary>
public abstract string CMDName { get; }
/// <summary>
/// Usage message, eg: 'name [args]: do something'
/// </summary>
public abstract string CMDDesc { get; }
/// <summary>
/// Perform the command
/// </summary>
/// <param name="command">The full command, eg: 'mycommand arg1 arg2'</param>
/// <returns>A confirmation/error message, or "" if no message</returns>
public abstract string Run(McTcpClient handler, string command);
/// <summary>
/// Return a list of aliases for this command.
/// Override this method if you wish to put aliases to the command
/// </summary>
public virtual IEnumerable<string> getCMDAliases() { return new string[0]; }
/// <summary>
/// Check if at least one argument has been passed to the command
/// </summary>
public static bool hasArg(string command)
{
int first_space = command.IndexOf(' ');
return (first_space > 0 && first_space < command.Length - 1);
}
/// <summary>
/// Extract the argument string from the command
/// </summary>
/// <returns>Argument or "" if no argument</returns>
public static string getArg(string command)
{
if (hasArg(command))
{
return command.Substring(command.IndexOf(' ') + 1);
}
else return "";
}
/// <summary>
/// Extract the arguments as a string array from the command
/// </summary>
/// <returns>Argument array or empty array if no arguments</returns>
public static string[] getArgs(string command)
{
return getArg(command).Split(' ');
}
}
}

View file

@ -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 <serverip>: 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;
}
}
}

View file

@ -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<string> getCMDAliases()
{
return new string[] { "quit" };
}
}
}

View file

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

View file

@ -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.";
}
}
}

View file

@ -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 <scriptname>: 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;
}
}
}

View file

@ -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 <text>: 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;
}
}
}

View file

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

View file

@ -17,6 +17,8 @@ namespace MinecraftClient
public class McTcpClient : IMinecraftComHandler
{
private static List<string> cmd_names = new List<string>();
private static Dictionary<string, Command> cmds = new Dictionary<string, Command>();
private List<ChatBot> bots = new List<ChatBot>();
private static List<ChatBots.Script> scripts_on_hold = new List<ChatBots.Script>();
public void BotLoad(ChatBot b) { b.SetHandler(this); bots.Add(b); b.Initialize(); Settings.SingleCommand = ""; }
@ -206,89 +208,57 @@ 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)
{
string text = command.Substring(5);
SendChatMessage(text);
if (type.IsSubclassOf(typeof(Command)))
{
try
{
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 = "send <text>: send a chat message or command.";
break;
case "set":
if (command.Length > 3)
catch (Exception e)
{
string[] temp = command.Substring(4).Split('=');
if (temp.Length > 1)
{
if (!Settings.setVar(temp[0], command.Substring(temp[0].Length + 5)))
{
response_msg = "variable name must be A-Za-z0-9.";
ConsoleIO.WriteLine(e.Message);
}
}
}
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 <scriptname>: run a script file.";
break;
/* Process the provided command */
case "connect":
if (command_args.Length > 1)
string command_name = command.Split(' ')[0].ToLower();
if (command_name == "help")
{
Settings.setServerIP(command_args[1]);
Program.Restart();
}
else response_msg = "connect <serverip>: connect to the specified server.";
break;
case "help":
if (command.Length >= 6)
if (Command.hasArg(command))
{
string help_cmd_name = command.Substring(5).ToLower();
switch (help_cmd_name)
string help_cmdname = Command.getArgs(command)[0].ToLower();
if (help_cmdname == "help")
{
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 <text>: send a chat message or command."; break;
case "set": response_msg = "set varname=value: set a custom %variable%."; break;
case "script": response_msg = "script <scriptname>: run a script file."; break;
case "connect": response_msg = "connect <serverip>: connect to the specified server."; break;
case "help": response_msg = "help <cmdname>: show brief help about a command."; break;
default: response_msg = "help: unknown command '" + help_cmd_name + "'."; break;
response_msg = "help <cmdname>: show brief help about a command.";
}
else if (cmds.ContainsKey(help_cmdname))
{
response_msg = cmds[help_cmdname].CMDDesc;
}
else response_msg = "help <cmdname>. Available commands: exit, reco, script, send, connect.";
break;
default:
else response_msg = "Unknown command '" + command_name + "'. Use 'help' for command list.";
}
else response_msg = "help <cmdname>. 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;
}
@ -404,5 +374,15 @@ namespace MinecraftClient
}
else return handler.SendChatMessage(text);
}
/// <summary>
/// Allow to respawn after death
/// </summary>
/// <returns>True if packet successfully sent</returns>
public bool SendRespawnPacket()
{
return handler.SendRespawnPacket();
}
}
}

View file

@ -81,6 +81,14 @@
<Compile Include="ChatBots\ScriptScheduler.cs" />
<Compile Include="ChatBots\TestBot.cs" />
<Compile Include="ChatBot.cs" />
<Compile Include="Command.cs" />
<Compile Include="Commands\Connect.cs" />
<Compile Include="Commands\Exit.cs" />
<Compile Include="Commands\Reco.cs" />
<Compile Include="Commands\Respawn.cs" />
<Compile Include="Commands\Script.cs" />
<Compile Include="Commands\Send.cs" />
<Compile Include="Commands\Set.cs" />
<Compile Include="ConsoleIO.cs" />
<Compile Include="Crypto\Streams\MonoAesStream.cs" />
<Compile Include="Crypto\Streams\RegularAesStream.cs" />

View file

@ -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);
}
/// <summary>
/// Enumerate types in namespace through reflection
/// </summary>
/// <param name="nameSpace">Namespace to process</param>
/// <param name="assembly">Assembly to use. Default is Assembly.GetExecutingAssembly()</param>
/// <returns></returns>
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();
}
}
}