• 🏆 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!

[System] ChatCommand

Level 4
Joined
Jun 9, 2011
Messages
91
This system allows fast commands development.

JASS:
library ChatCommand /* v1.3.0 by Bills
***********************************************************************************************
*
*   This system allows fast commands development.
*
************************************************************************************************
*
*   */ uses /*
*   
*       New*/ Table    /*  hiveworkshop.com/forums/jass-resources-412/snippet-new-table-188084/
*
************************************************************************************************
*
*   SETTINGS
*
*/
globals
    // prefix of all commands
    public constant string PREFIX = "-"

    // alert/error message
    private constant string ALERT_MSG_INAVLID = "|cFFF00000Command Invalid|r"
endglobals
/*
********************************************************************************************
*
*   struct ChatCommand
*
*
*       Constructor
*       -----------------------
*
*           static method create takes string whichCommand, code onCommand returns thistype
*
*       Operators
*       -----------------------
*
*           static method operator [] takes string whichCommand returns ChatCommand
*               -   this operator returns (if exists) the instance of a ChatCommand
*               -   exemple: ChatCommand["ar"]
*
*       Members
*       -----------------------
*
*           readonly boolean enabled
*               -   indicates whether the command is enabled
*
*       Methods
*       -----------------------
*
*           method enable takes boolean flag returns nothing
*               -   Enable or disable a command
*
*           method fire takes player whichPlayer, string commandData returns boolean
*               -   Executes a command for a determined player
*               -   Returns true on success
*
*       Event Responses
*       -----------------------
*
*           readonly static ChatCommand eventCommand
*               -    struct of typed command
*
*           readonly static string eventString
*               -    the command in string
*
*           readonly static player eventPlayer
*               -    player that triggered the command
*
*           readonly static integer eventPlayerId
*               -    id of above player
*
*           readonly static string eventData
*               -    data of command (everything after of the first space)
*
****************************************************************************************
*
*   module ChatCommandModule
*
*           this module creates a command using 1 static member and 1 static method:
*               -   static constant string CHAT_COMMAND
*               -   static method onCommand takes nothing returns nothing
*
*****************************************************************************************
*
*    Exemples
*
*        - using struct
*
*            struct CmdExemple extends array
*                static constant string CHAT_COMMAND = "hello"
*
*                static method onCommand takes nothing returns nothing
*                    call BJDebugMsg("Hello Player "+ I2S(ChatCommand.eventPlayerId+1))
*                endmethod
*
*                implement ChatCommandModule
*            endstruct
*
*        - now using a single function
*
*            library Cmd initializer init
*                function CommandResponse takes nothing returns nothing
*                    call BJDebugMsg("Hello Player "+ I2S(ChatCommand.eventPlayerId+1))
*                endfunction
*
*                private function init takes nothing returns nothing
*                    call ChatCommand.create("hello", function CommandResponse)
*                endfunction
*            endlibrary
*
*****************************************************************************************/
    struct ChatCommand extends array

        readonly static thistype eventCommand
        readonly static string eventString
        readonly static player eventPlayer
        readonly static integer eventPlayerId
        readonly static string eventData

        private static Table instances
        private static trigger trig
        private static integer ic = 1 // instance count
        private static integer PREFIX_LEN

        readonly boolean enabled
        private boolexpr callback
        private string command

        static method operator [] takes string command returns thistype
            return instances[StringHash(command)]
        endmethod

        static method create takes string command, code callback returns thistype
            local thistype this = ic
			static if DEBUG_MODE then
				if ChatCommand[command] != 0 then
					call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,60,"[ChatCommand] Warning: Attempted to overwrite \"-"+command+"\" command")
					return 0
				endif
			endif
            set ic = ic + 1
            set instances[StringHash(command)] = this
            set this.command = command
            set this.enabled = true
            set this.callback = Filter(callback)
            return this
        endmethod

        method fire takes player which, string data returns boolean
            if enabled then
                set eventCommand = this
                set eventString = command
                set eventPlayer = which
                set eventPlayerId = GetPlayerId(which)
                set eventData = data
                call TriggerAddCondition(trig,callback)
                call TriggerEvaluate(trig)
                call TriggerClearConditions(trig)
                return true
            endif
            return false
        endmethod
        
        method enable takes boolean flag returns nothing
            set enabled = flag
        endmethod
        
        private static method eventListener takes nothing returns boolean
            local string chat = GetEventPlayerChatString()
            local string char
            local string str = ""
            local string data = ""
            local integer i = PREFIX_LEN-1
            local integer strlen = StringLength(chat)
            local thistype cmd
            if SubString(chat,0,PREFIX_LEN) != PREFIX or strlen <= PREFIX_LEN then
                return false
            endif
            loop
                set i = i+1
                set char = SubString(chat,i,i+1)
                exitwhen char == " " or i == strlen
                set str = str + char
            endloop
            set cmd = ChatCommand[str] 
            if cmd != 0 then
                loop
                    set i = i+1
                    set char = SubString(chat,i,i+1)
                    exitwhen i >= strlen
                    set data = data + char
                endloop
                call cmd.fire(GetTriggerPlayer(),data)
            endif
            return false
        endmethod
        
        static method onInit takes nothing returns nothing
            local trigger t = CreateTrigger()
            local integer i = 0
            loop
                exitwhen i>12
                call TriggerRegisterPlayerChatEvent(t, Player(i), PREFIX, false )
                set i=i+1
            endloop
            call TriggerAddCondition(t, Filter(function thistype.eventListener))
            set t = null
            set instances = Table.create()
            set trig = CreateTrigger()
            set PREFIX_LEN = StringLength(PREFIX)
        endmethod
    endstruct
    
    module ChatCommandModule
        // static constant string CHAT_COMMAND = "command"
        // static method onCommand takes nothing returns nothing
        // endmethod
        private static method onInit takes nothing returns nothing
            call ChatCommand.create(CHAT_COMMAND, function thistype.onCommand)
        endmethod
    endmodule
endlibrary

JASS:
library Demo initializer init uses ChatCommand
    private struct HelloCmd extends array
        // with struct
        private static constant string CHAT_COMMAND = "hello"
        
        private static method onCommand takes nothing returns nothing
            if ChatCommand.eventData == "" then
                call DisplayTimedTextToPlayer(ChatCommand.eventPlayer,0,0,60,"Hello Player "+ I2S(ChatCommand.eventPlayerId+1))
            else
                call DisplayTimedTextToPlayer(ChatCommand.eventPlayer,0,0,60,"Hello "+ ChatCommand.eventData)
            endif
            // disables this command
            call ChatCommand.eventCommand.enable(false)
        endmethod
        // implementation of ChatCommand
        implement ChatCommandModule
        // command "-hello" created
    endstruct
    
    private function CommandResponse takes nothing returns nothing
        //with function 
        if ChatCommand.eventData == "" then
            call DisplayTimedTextToPlayer(ChatCommand.eventPlayer,0,0,60,"Hi Player "+ I2S(ChatCommand.eventPlayerId+1))
        else
            call DisplayTimedTextToPlayer(ChatCommand.eventPlayer,0,0,60,"Hi "+ ChatCommand.eventData)
        endif
    endfunction
    
    private function init takes nothing returns nothing
        call ChatCommand.create("hi",function CommandResponse)
        // command "-hi" created
    endfunction
endlibrary


v1.3.0
- code improvement (thanks LeP)
v1.2.0
- method trigger renamed to fire (thanks Bribe)
- method fire now returns a boolean, indicating success or failure
- disabled command message removed
- Minor fix
- Minor changes on API
v1.1.1
- Minor improvement
- Minor fix
v1.1.0
- Minor improvement
v1.0.1
- Minor fix with prefix length (thanks Mag)
v1.0.0
- Release

Sorry for my bad english
 

Attachments

  • ChatCommand.w3x
    28.3 KB · Views: 182
Last edited:
http://www.hiveworkshop.com/forums/jass-resources-412/system-mode-manager-197956/

As you can see, all you need is one quick iteration.
(So it's O(n) instead of O(2n))

edit
I really like your module, it allows the user to create multiple triggers, each for one command and a struct to wrap it instead of a library or a scope :p (Yeah, I just love structs.)
Dat struct /o/

edit
I think I might incorporate this module idea in my ModeManager and remove Chat command support so they could become two different things.
And I'll link to this :3

edit
Tip: This resource currently assumes that the length of prefix is going to be 1 all the time.
You should add a constant that is equal to StringLength(PREFIX).
 
Level 4
Joined
Jun 9, 2011
Messages
91
And I want to make my system supports multiple commands as "-apsp"
._.

@edit

updated ...

JASS:
local integer hash = StringHash(command)
if commands.has(hash) then
    return commands[hash]
endif
return 0

>>>>>>

JASS:
return commands[StringHash(command)]
 

Bribe

Code Moderator
Level 50
Joined
Sep 26, 2009
Messages
9,464
Inline the local boolean expression in the create method. Also, a non- operator method named "trigger" doesn't make sense from an API point of view. Make that "registerTrigger" perhaps.

I don't know how many similarities this has to mode manager but it couldn't hurt to have more than one resource for it as it's not super common and done to death like timer systems and dds...
 
Level 4
Joined
Jun 9, 2011
Messages
91
I think 'fire' is great too.
Heck, if you want, you can add as many functions for this as you want.
They would all just call .fire().
(Sometimes, there are just too many good names for a function. Some users prefer one, others prefer the second. Hence, it's often better to use both because it won't hurt.)

edit
And I take that comment back about my function having O(n) complexity, it's actually O(2n) as well since I'm rebuilding the string by filtering out spaces :D
 

LeP

LeP

Level 13
Joined
Feb 13, 2008
Messages
539
I don't see any reason for two apis. The function-api is enough.
And why use a table for the member-variables? Why not, uhm, like, use member-variables?

Also, use function interfaces.

[…]
And I take that comment back about my function having O(n) complexity, it's actually O(2n) as well since I'm rebuilding the string by filtering out spaces :D

O(2n) = O(n)
 
Level 4
Joined
Jun 9, 2011
Messages
91
Thanks for all suggestions :)

I did the following modifications:
- method trigger renamed to fire
- method fire now returns a boolean, indicating success or failure
- disabled command message removed

...why use a table for the member-variables? Why not, uhm, like, use member-variables?
StringHash returns a number greater than 8190.
 

LeP

LeP

Level 13
Joined
Feb 13, 2008
Messages
539
Thanks for all suggestions :)

I did the following modifications:
- method trigger renamed to fire
- method fire now returns a boolean, indicating success or failure
- disabled command message removed


StringHash returns a number greater than 8190.
Well correct me if im wrong, but you are using your ic as this. Your only saving this via stringhash. ic should always be under 8190 and therefore this too.

8MB file size limitation, compressed code is the way forward. Saving 2 seconds of typing is not.

Lol, as if it's about typing-speed.
 

LeP

LeP

Level 13
Joined
Feb 13, 2008
Messages
539
ic is my instance count. Before allocate I check if there is already a command with that string, therefore I use hashtable.

Yes you're storing this in the table, but this itself is always (as long as you don't create more than 8190 commands) in the range [1..8190]. Therefore you can use arrays to store enabled and that boolexpr and all that.
 

LeP

LeP

Level 13
Joined
Feb 13, 2008
Messages
539
JASS:
static method operator [] takes string command returns thistype
    return commands[StringHash(command)]
endmethod

This need hashtable.

Would you simply just read my posts? I never said you don't need table. But you can use arrays for the callback, for enabled and for the command.

And with arrays i mean struct member-variables. As in
JASS:
struct shit extends array
  private boolexpr callback
  private boolean enabled
  private string command
  // the rest of your damn struct
endstruct

This works because your this is *always* (see above) in [1..8190].
 
LeP said:
I don't see any reason for two apis. The function-api is enough.

But there's nothing too bad about something like this:

JASS:
struct Data extends array
    static method insert takes integer i returns nothing
        // inserting the integer over here
    endmethod
    static method add takes integer i returns nothing
        call insert(i)
    endmethod
    static method enqueue takes integer i returns nothing
        call insert(i)
    endmethod
    static method push takes integer i returns nothing
        call insert(i)
    endmethod
endstruct

It's all about the user's taste.
Some users like enqueue, some like insert, some like add, others like push.
There's no reason why you have to make only /one/ of those groups happy, am I right?
 

LeP

LeP

Level 13
Joined
Feb 13, 2008
Messages
539
But there's nothing too bad about something like this:

JASS:
struct Data extends array
    static method insert takes integer i returns nothing
        // inserting the integer over here
    endmethod
    static method add takes integer i returns nothing
        call insert(i)
    endmethod
    static method enqueue takes integer i returns nothing
        call insert(i)
    endmethod
    static method push takes integer i returns nothing
        call insert(i)
    endmethod
endstruct

It's all about the user's taste.
Some users like enqueue, some like insert, some like add, others like push.
There's no reason why you have to make only /one/ of those groups happy, am I right?

You can't compare these two.
You just give your methods different names where this uses another wrapper to archieve, uhm, i don't even see what it wants to archieve. Except misusing structs as namespaces. We all know how and underscore is so much worse than a dot.

So yeah, for all of you who work on some jass-preprocessor get your namespacing (and scoping) right. I mean before your edit to use instance variables, your struct were just a collection of static variables. Not what you'd expect from "OOP".

Also when you have data structure or some other resource there normaly is a simple name which just makes sense. You wouldnt alias the push operation for a stack. Just give one api endpoint. Less documentation. Less confusion. And if someone realy dislikes your name, you probably have chose a very bad name.
Or one could simply replace the name in his local version.

And Bribe can't use your version, because all those repeatet code take up the precious 8MB.
 
You can use structs any way you want.
I use them as namespaces because I choose to.

I don't like underscores.

And that was onlIy an example. You knew that very well.

And Bribe can't use your version, because all those repeatet code take up the precious 8MB.

Wrong.
They inline and get deleted by an optimizer.
We can't say the same thing for function interfaces.

And if someone realy dislikes your name, you probably have chose a very bad name.

People have different tastes.
If 90% of the people like 'insert', and 10% like 'add', why can't I give them both and make everyone happy? Why must I treat this 90% as if they are right?

Majority != Right.

You wouldnt alias the push operation for a stack.

In that case, you wouldn't, because it's a stack.
What I posted is an example of a struct called 'Data', so I can use whatever I want >.>

So yeah, for all of you who work on some jass-preprocessor get your namespacing (and scoping) right. I mean before your edit to use instance variables, your struct were just a collection of static variables. Not what you'd expect from "OOP".

I use vJASS the way I want.
I don't treat structs like Objects because they are not. Period.
C# uses the dot operator for namespaces, classes, enums, and pretty much everything.
It's an incredibly good language and I can actually start something and finish it because of that.

edit
LeP, why is it that you show up every couple of weeks, criticize the way we do things around here as if your opinions are better than ours, then disappear, and repeat?
 
Last edited by a moderator:

LeP

LeP

Level 13
Joined
Feb 13, 2008
Messages
539
In that case, you wouldn't, because it's a stack.
What I posted is an example of a struct called 'Data', so I can use whatever I want >.>

You posted something without any meaning, so ofc. you can't give it any meaningfull name.

People have different tastes.
If 90% of the people like 'insert', and 10% like 'add', why can't I give them both and make everyone happy? Why must I treat this 90% as if they are right?

Majority != Right.

It's not about majority. You give one reasonable name. Even if i personally don't like your naming, i would pretty much still use it as it has a meaningfull and reasonable or just a good name.

When i read some code and see .add and then .insert i have to look it up, see that it is just an alias, try to remember that and go on.
Too much hassle for me.

Or even the other way around. When i try to write smth. i look up the methods and then have to choose which alias i have to take. I've got the feeling that this is unneeded.

Just redundant information.

You can use structs any way you want.
I use them as namespaces because I choose to.

I don't like underscores.

Ahh, and the things with those underscores. It was mostly a joke on my side. Underscores are ofc. horrible. But it's funny people react over one single character change.

Wrong.
They inline and get deleted by an optimizer.
We can't say the same thing for function interfaces.

Also not too serious. You don't seem to get a lot of my jokes.

C# uses the dot operator for namespaces, classes, enums, and pretty much everything.
It's an incredibly good language and I can actually start something and finish it because of that.

That's pretty much what i've said (except that c# might or not might be a good language).
If someone wants to fix vJass' warts, take a look how other languages solve these issues and evaluate them how they would work in jass. etc.

I use vJASS the way I want.

That's cool. I also like languges which give me enough rope to hang myself.

I don't treat structs like Objects because they are not. Period.

I would rly like to hear your definition of object or why structs aren't objects.

LeP, why is it that you show up every couple of weeks, criticize the way we do things around here as if your opinions are better than ours, then disappear, and repeat?

Interesting question. I wont go into full length here.
I post my opinion because i think my opinion is as good as every other ones. And i *try* to reason about why i think that way. Also noone has given me good reaons why my opinions are wrong. Nothing has ever prevented me from using inheritance or function interfaces or normal structs. In fact i feel more productive with them.

Make more interesting posts and i will post more.
 
First of all, I would like to apologize for my aggression.

You posted something without any meaning, so ofc. you can't give it any meaningfull name.

Yes, I know. I was giving an example of where giving multiple names might be useful, because it isn't very clear which one you should use when the struct has a name like Data. (It's only an example)

Nothing has ever prevented me from using inheritance or function interfaces or normal structs. In fact i feel more productive with them.

Okay, I just have to agree with you now.
Let me tell you a story about myself:

When I was 14 years old, I bought this book that taught me the basics of C++, and I began coding immediately. For over a year, I remained a noob in the field of C++. I only knew how to do simple things. I tried making something, but I just couldn't. The best thing that I ever made was this name generator (which I uploaded to the Tools section of this site). I started working on a 2-D platformer game, and I failed, because I started out with SDL, and gave it up after GhostWolf recommended SFML, which was not working at all for me for some very odd reason even after I followed the instructions carefully on some SFML configuration page. When I turned 16 (12 days ago), I started working for this programming company. I'm still working for them and I'm a trainee. The first day, they wanted to test my C++ skills, and they were impressed a bit. They were interested in why I would camelCase most of my functions and methods. The second day, they trashed C++, and moved on to C#. They told me about this change the first day, and on the night of that day, I opened up some random webpage and read on C# syntax. It was cake (Not a lie though :3). I was able to actually make something with C#. C# made things easier for me because I could ignore memory management (well, not completely, I still have to be aware of how much memory I'm using at any given moment.), and I could actually focus on making my actual program. I made this (linked in the attachments). Of course, this is an older version of it because the newer version that actually does some hashing to the highscore file data in order to verify if it is legit is on my laptop, which I carry with me to work.

That's why I agree with you. If it makes things easier for you and allows you to be more productive, then go ahead and do it. Unfortunately, I feel more productive when I use boolexpr arrays, and that's mainly why I use them. This may sound crazy, but I even feel productive when I write custom allocation myself. I know, I'm a crazy person.


Peace.
 

Attachments

  • Hangman 1.2.0.0.zip
    7.3 KB · Views: 44
Top