• 🏆 Texturing Contest #33 is OPEN! Contestants must re-texture a SD unit model found in-game (Warcraft 3 Classic), recreating the unit into a peaceful NPC version. 🔗Click here to enter!
  • It's time for the first HD Modeling Contest of 2024. Join the theme discussion for Hive's HD Modeling Contest #6! Click here to post your idea!

Command

Status
Not open for further replies.
Level 6
Joined
Jul 30, 2013
Messages
282
A system i use to handle text commands.

features:
  • built in -list command
  • built in -help command
  • does initial argument parsing for commands
  • feeds off of 1 trigger, number of triggers run per command in system is O(1)
  • has been in use in FortressSurvival map for .. ages. been a while since we last got any complaints so it should be pretty solid..
issues:
  • is built for internal use originally, might need tweaking to be generally applicable
posted here for feedback, will move to submissions if found acceptable.

JASS:
library Command initializer init uses DelayedPrinter, StringHashEx /* uses StringParser*/
    /*************************************************************
StringHashEx    http://www.hiveworkshop.com/threads/stringhashex-guaranteed-no-collision-string-hash.291755/
DelayedPrinter    http://www.hiveworkshop.com/threads/delayedprinter.291753/

    -dosometing
    -dosomething else with params
 
    example creation of command:
    call Command.create(CommandHandler.printHelpMessage).name("help").handles("help").help("help <command name>", "Prints the help text for the given command")
    call Command.create(CommandHandler.printCommandList).name("list").handles("list").help("list", "Prints the list of all commands you have access to")
 
    example command handler:
    function printHelpMessage takes Args args returns nothing
        if Command.fromName(args[1]) == 0 then
            return
        endif
        if args.length == 2 then
            call DelayedPrinter.printPlayer(GetTriggerPlayer(), Command.fromName(args[1]).dusage)
            call DelayedPrinter.printPlayer(GetTriggerPlayer(), Command.fromName(args[1]).dhelp)
        else
            call DelayedPrinter.printPlayer(GetTriggerPlayer(), Command.fromName("help").dusage)
            call DelayedPrinter.printPlayer(GetTriggerPlayer(), Command.fromName("help").dhelp)
        endif
    endfunction
 
    *************************************************************/
 
    struct Args
        private string str
        private integer array begins[20]
        private integer array ends[20]
        private integer nargs = 0
     
        /**
        *  tokenise a command string, return an Args instance with at most N indicies
        *  @param commandString: the string to tokenise
        *  @param maxArgs: the maximum number of parameters you expect to take,
        *      allows easy handling of the case where the last argument has spaces in it
        */
        public static method parseN takes string commandString, integer maxArgs returns thistype
            local thistype this = thistype.create()
            local integer commandLength = StringLength(commandString)
            local integer tail = 0
            local integer head = 0
            local boolean quoted = false
            set this.str = commandString
            // split the command string breaking on spaces
            loop
                exitwhen head >= commandLength

                if SubString(commandString, head, head + 1) == "\"" then
                    set quoted = true
                    loop
                        set head = head + 1
                        exitwhen head >= commandLength
                        exitwhen SubString(commandString, head, head + 1) == "\""
                    endloop
                    set head = head + 1
                endif
             
                if SubString(commandString, head, head + 1) == " " or (head == commandLength)  or (this.nargs==maxArgs) then
                    if (this.nargs==maxArgs) then
                        set head = commandLength
                    endif
                    set this.begins[nargs] = tail
                    set this.ends[nargs] = head
                 
                    // if the string is in quotes
                    if quoted then
                        set this.begins[nargs] = tail + 1
                        set this.ends[nargs] = head - 1
                    endif
                 
                    set this.nargs = this.nargs + 1
                    set tail = head + 1
                 
                    // skip repeated spaces
                    //loop
                    //    exitwhen (not (SubString(commandString, head, head + 1) == " "))
                    //    set head = head + 1
                    //    set tail = tail + 1
                    //endloop
                endif
                set quoted = false
                set head = head + 1
            endloop
            set this.begins[nargs] = tail
            set this.ends[nargs] = head
            set this.nargs = this.nargs + 1
            set tail = head
            return this
        endmethod
     
        public static method parse takes string commandString returns thistype
            return thistype.parseN(commandString, 9999999)
        endmethod
     
        public method operator[] takes integer i returns string
            if i < this.nargs then
                //call BJDebugMsg("arg index: " + I2S(i) + ": " + SubString(this.str, this.begins[i], this.ends[i]))
                return SubString(this.str, this.begins[i], this.ends[i])
            endif
            return ""
        endmethod
     
        public method operator length takes nothing returns integer
            return this.nargs
        endmethod
     
    endstruct
 
 
    //function interface CommandHandler takes string commandName, StringStack args returns nothing
    function interface CommandHandler takes Args args returns nothing
 
    function printHelpMessage takes Args args returns nothing
        if Command.fromName(args[1]) == 0 then
        return
        endif
        // this needs to be a method so it has access to private state
        if args.length == 2 then
            call DelayedPrinter.printPlayer(GetTriggerPlayer(), Command.fromName(args[1]).dusage)
            call DelayedPrinter.printPlayer(GetTriggerPlayer(), Command.fromName(args[1]).dhelp)
           //call DisplayTimedTextToPlayer(GetTriggerPlayer(), 0, 0, 30, Command.fromName(args[1]).dusage)
           //call DisplayTimedTextToPlayer(GetTriggerPlayer(), 0, 0, 30, Command.fromName(args[1]).dhelp)
        else
            call DelayedPrinter.printPlayer(GetTriggerPlayer(), Command.fromName("help").dusage)
            call DelayedPrinter.printPlayer(GetTriggerPlayer(), Command.fromName("help").dhelp)
           //call DisplayTimedTextToPlayer(GetTriggerPlayer(), 0, 0, 30, Command.fromName("help").dusage)
           //call DisplayTimedTextToPlayer(GetTriggerPlayer(), 0, 0, 30, Command.fromName("help").dhelp)
        endif
    endfunction
 
    function printCommandList takes Args args returns nothing
        local integer i = 1
        call DelayedPrinter.printPlayer(GetTriggerPlayer(), "The list of commands:")
     
        loop
            exitwhen i > Command.commandCount
            if Command(i).triggerPlayerHasPrivileges() then
                call DelayedPrinter.printPlayer(GetTriggerPlayer(), Command.COMMAND_PREFIX + Command(i).dname)
                //call DisplayTimedTextToPlayer(GetTriggerPlayer(), 0, 0, 30, Command.COMMAND_PREFIX + Command(i).dname)
            endif
            set i = i + 1
        endloop
    endfunction
 
    struct Command
        // Command.create(CommandHandler.handlername).name("commandname").handles("commandname").handles("alternatecommandname").help("commandname <param1>", "it does all theese awesome things\n and other ones too")
        //
        public static constant string COMMAND_PREFIX = "-"
        private static Table commandMapping
        public static integer commandCount = 0

        public string dname
        public CommandHandler dhandler
        public string dhelp
        public string dusage
        private integer dprivileges = 0
     
        // used as a hit for Args.parseN(str,int)
        private integer dnmaxargs = 9999999
     
        public static method create takes CommandHandler ch returns thistype
            local thistype this = thistype.allocate()
            set this.dhandler = ch
            set commandCount = commandCount + 1
            return this
        endmethod
     
        // specify the names that this commancd corresponds to, can have multiple names/aliases
        public method handles takes string commandName returns thistype
            set commandMapping[StringHashEx(commandName)] = this
            return this
        endmethod
     
        // sets the canonical name
        public method name takes string commandName returns thistype
            set this.dname = commandName
            return this
        endmethod
     
        /**
         *  tell the command the maximum number of arguments it will ever receive
         *  and it can handle the whitespace in the last argument better
         */
        public method nargs takes integer nargs returns thistype
            set this.dnmaxargs = nargs
            return this
        endmethod
     
        public method privileges takes integer privs returns thistype
            set this.dprivileges = privs
            return this
        endmethod
     
        // usage = 1 line string, eg "help <command>", command prefix will be automatically prepended
        // verbose = long help text, can be anything and multiline
        public method help takes string usage, string verbose returns thistype
            set this.dusage=usage
            set this.dhelp=verbose
            return this
        endmethod
     
        public static method fromName takes string commandName returns thistype
            return thistype(commandMapping[StringHashEx(commandName)])
        endmethod

        // used in FS to filter output of -list command to only commands the user can actually use
        public method triggerPlayerHasPrivileges takes nothing returns boolean
            return true
        endmethod
     
        public static method doCommand takes string commandName, string eventString returns nothing
            local Command comm = Command.fromName(commandName)
            local Args args = Args.parseN(eventString, comm.dnmaxargs)
            if comm.triggerPlayerHasPrivileges() then
                call CommandHandler(comm.dhandler).evaluate(args)
                call args.destroy()
            else
                call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,20,"You lack permissions to use that command")
            endif
        endmethod

        private static method onInit takes nothing returns nothing
            set commandMapping = Table.create()
         
            call Command.create(CommandHandler.printHelpMessage).name("help").handles("help").help("help <command name>", "Prints the help text for the given command")
            call Command.create(CommandHandler.printCommandList).name("list").handles("list").help("list", "Prints the list of all commands you have access to")
        endmethod
     
     
        // ---- COMPATIBILITY WITH OLD API ----
        public static method registerCommand takes string commandName, CommandHandler func returns nothing
            call Command.create(func).name(commandName).handles(commandName)
        endmethod
        public static method registerHelp takes string commandName, string helpstring returns nothing
            call thistype.fromName(commandName).help(commandName, helpstring)
        endmethod
    endstruct
 
    private function actions takes nothing returns nothing
        local string eventString = GetEventPlayerChatString()
        local player triggerPlayer = GetTriggerPlayer()
        local Args args
        local string commandName
     
        set eventString = SubString(eventString, 1, StringLength(eventString))
        set args = Args.parseN(eventString,2)
        set commandName = args[0]
        call Command.doCommand(commandName, eventString)
        call args.destroy()
    endfunction
 
    private function init takes nothing returns nothing
        local integer i=0
        local trigger t = CreateTrigger()
        loop
            call TriggerRegisterPlayerChatEvent(t,Player(i),Command.COMMAND_PREFIX, false)
            set i = i + 1
            exitwhen i > 12
        endloop
        call TriggerAddAction(t, function actions)
    endfunction
 
endlibrary
 
Last edited:
Status
Not open for further replies.
Top