1

So on my game server we have server-side commands. I was wondering if it was possible to turn this

public string Command
{
    get { return "g"; }
}

into something like this

public string Command
{
    get { return "g", "guild", "group"; }
}

Here's the command interfaces code

internal interface ICommand
{
    string Command { get; }
    int RequiredRank { get; }
    void Execute(Player player, string[] args);
}

Here's the command handler's code
part 1:

ProcessCmd(x[0].Trim('/'), x.Skip(1).ToArray());

part 2:

private void ProcessCmd(string cmd, string[] args)
{
    if (cmds == null)
    {
        cmds = new Dictionary<string, ICommand>();
        var t = typeof (ICommand);
        foreach (var i in t.Assembly.GetTypes())
            if (t.IsAssignableFrom(i) && i != t)
            {
                var instance = (ICommand) Activator.CreateInstance(i);
                cmds.Add(instance.Command, instance);
            }
    }

    ICommand command;
    if (!cmds.TryGetValue(cmd, out command))
    {
        psr.SendPacket(new TextPacket
        {
            BubbleTime = 0,
            Stars = -1,
            Name = "*Error*",
            Text = "Unknown Command!"
        });
        return;
    }
    try
    {
        ExecCmd(command, args);
    }
    catch (Exception e)
    {
        Console.Out.WriteLine(e);
        psr.SendPacket(new TextPacket
        {
            BubbleTime = 0,
            Stars = -1,
            Name = "*Error*",
            Text = "Error when executing the command!"
        });
    }
}
Benny Hill
  • 6,191
  • 4
  • 39
  • 59
Luciferus
  • 33
  • 1
  • 1
  • 6

3 Answers3

9

You want this:

public string[] Commands
{
    get { return new string[] {"g", "guild", "group"}; }
}

Start with this. It will produce errors you should fix and by advancing in the fixing you will refactor a nice portion of code, like the ICommand interface.

pid
  • 11,472
  • 6
  • 34
  • 63
  • Additionally, `ExecCmd()` and other downstream code will have to change to operate on an array of strings. – Cᴏʀʏ Feb 10 '14 at 14:56
  • Exactly. I also *sense* the necessity of a `foreach()` *somewhere* that will creep in there.... :) – pid Feb 10 '14 at 14:57
  • I don't think that you have to change `ExecCmd`, since it takes an `ICommand` and probably just runs `ICommand.Execute(...)`. – sloth Feb 10 '14 at 15:04
  • I think this method would work the best by adding in an alternate ICommand like... ICommandArray and then just pull the command through that. I'll have to test this method out using that when I get access to our VPS again. – Luciferus Feb 10 '14 at 15:57
0

If you are not allowed to change the interface, use a separator.

public string Command
{
    get { return string.Join("|", "g", "guild", "group"); }
}

Usage example:

foreach ( var command in source.Command.Split('|') )
{
    ExecCmd(command, args);
}
Steven Liekens
  • 13,266
  • 8
  • 59
  • 85
0

If your commands don't maintain any state and just consists of basically a single method (it seems like that), I recommend removing the interface and the classes altogether and just use simple functions with some metadata (attributes) attached.

This will make your code simpler by removing a bunch of classes.

Example:

// simple attribute to mark functions as commands
class CommandAttribute : Attribute
{
    public string Name {get;private set;}
    public string[] Keys {get;private set;}
    public int Rank {get;private set;}

    public CommandAttribute(string name, int rank, params string[] keys)
    {
        Name = name;
        Keys = keys;
        Rank = rank;
    }
}

// example commands
[Command("Guild", 15, "g", "guild", "group")]
public static void Guild(Player player,string[] args)
{
    // do stuff
}

[Command("Something other", 5, "f", "foo", "foobar")]
public static void FooIt(Player player,string[] args)
{
    // do stuff
}

Looking for those functions inside an assembly is also easy:

delegate void CommandHandler(Player player, string[] args);

var commands = from t in Assembly.GetExecutingAssembly().GetTypes()
               from m in t.GetMethods(BindingFlags.Static|BindingFlags.Public)
               let attr = (CommandAttribute)m.GetCustomAttribute(typeof(CommandAttribute))
               where attr != null
               select new 
               {
                    attr,
                    function = Delegate.CreateDelegate(typeof(CommandHandler), m)
                };

// probably use some other data structure if you want to lookup attr.Name and attr.Rank, too
var cmds = new Dictionary<string, CommandHandler>();
foreach(var item in commands)
    foreach(var key in item.attr.Keys)
        cmds.Add(key, (CommandHandler)item.function);
sloth
  • 99,095
  • 21
  • 171
  • 219