Snippet Event

Jesus4Lyf

Good Idea™
Reaction score
397
Event​
Version 1.04​

Requirements:
- Jass NewGen

JASS:
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//  ~~    Event     ~~    By Jesus4Lyf    ~~    Version 1.04    ~~
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
//  What is Event?
//         - Event simulates Warcraft III events. They can be created,
//           registered for, fired and also destroyed.
//         - Event, therefore, can also be used like a trigger "group".
//         - This was created when there was an influx of event style systems 
//           emerging that could really benefit from a standardised custom
//           events snippet. Many users were trying to achieve the same thing
//           and making the same kind of errors. This snippet aims to solve that.
//
//  Functions:
//         - Event.create()       --> Creates a new Event.
//         - .destroy()           --> Destroys an Event.
//         - .fire()              --> Fires all triggers which have been
//                                    registered on this Event.
//         - .register(trigger)   --> Registers another trigger on this Event.
//         - .unregister(trigger) --> Unregisters a trigger from this Event.
//
//  Details:
//         - Event is extremely efficient and lightweight.
//         - It is safe to use with dynamic triggers.
//         - Internally, it is just a linked list. Very simple.
//
//  How to import:
//         - Create a trigger named Event.
//         - Convert it to custom text and replace the whole trigger text with this.
//
//  Thanks:
//         - Builder Bob for the trigger destroy detection method.
//         - Azlier for inspiring this by ripping off my dodgier code.
//
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
library Event
    ///////////////
    // EventRegs //
    ////////////////////////////////////////////////////////////////////////////
    // For reading this far, you can learn one thing more.
    // Unlike normal Warcraft III events, you can attach to Event registries.
    // 
    // Event Registries are registrations of one trigger on one event.
    // These cannot be created or destroyed, just attached to.
    //
    // It is VERY efficient for loading and saving data.
    // 
    //  Functions:
    //         - set eventReg.data = someStruct --> Store data.
    //         - eventReg.data                  --> Retreive data.
    //         - Event.getTriggeringEventReg()  --> Get the triggering EventReg.
    //         - eventReg.destroy()             --> Undo this registration.
    // 
    private keyword destroyNode
    struct EventReg extends array
        integer data
        method clear takes nothing returns nothing
            set this.data=0
        endmethod
        method destroy takes nothing returns nothing
            call Event(this).destroyNode()
        endmethod
    endstruct
    
    private module Stack
        static thistype top=0
        static method increment takes nothing returns nothing
            set thistype.top=thistype(thistype.top+1)
        endmethod
        static method decrement takes nothing returns nothing
            set thistype.top=thistype(thistype.top-1)
        endmethod
    endmodule
    
    private struct EventStack extends array
        implement Stack
        Event current
    endstruct
    
    struct Event
        private trigger trig
        private thistype next
        private thistype prev
        
        static method getTriggeringEventReg takes nothing returns EventReg
            return EventStack.top.current
        endmethod
        
        static method create takes nothing returns Event
            local Event this=Event.allocate()
            set this.next=this
            set this.prev=this
            return this
        endmethod
        
        private static trigger currentTrigger
        method fire takes nothing returns nothing
            local thistype curr=this.next
            call EventStack.increment()
            loop
                exitwhen curr==this
                set thistype.currentTrigger=curr.trig
                if IsTriggerEnabled(thistype.currentTrigger) then
                    set EventStack.top.current=curr
                    if TriggerEvaluate(thistype.currentTrigger) then
                        call TriggerExecute(thistype.currentTrigger)
                    endif
                else
                    call EnableTrigger(thistype.currentTrigger) // Was trigger destroyed?
                    if IsTriggerEnabled(thistype.currentTrigger) then
                        call DisableTrigger(thistype.currentTrigger)
                    else // If trigger destroyed...
                        set curr.next.prev=curr.prev
                        set curr.prev.next=curr.next
                        call curr.deallocate()
                    endif
                endif
                set curr=curr.next
            endloop
            call EventStack.decrement()
        endmethod
        method register takes trigger t returns EventReg
            local Event new=Event.allocate()
            set new.prev=this.prev
            set this.prev.next=new
            set this.prev=new
            set new.next=this
            
            set new.trig=t
            
            call EventReg(new).clear()
            return new
        endmethod
        method destroyNode takes nothing returns nothing // called on EventReg
            set this.prev.next=this.next
            set this.next.prev=this.prev
            call this.deallocate()
        endmethod
        method unregister takes trigger t returns nothing
            local thistype curr=this.next
            loop
                exitwhen curr==this
                if curr.trig==t then
                    set curr.next.prev=curr.prev
                    set curr.prev.next=curr.next
                    call curr.deallocate()
                    return
                endif
                set curr=curr.next
            endloop
        endmethod
        
        method destroy takes nothing returns nothing
            local thistype curr=this.next
            loop
                call curr.deallocate()
                exitwhen curr==this
                set curr=curr.next
            endloop
        endmethod
        method chainDestroy takes nothing returns nothing
            call this.destroy() // backwards compatability.
        endmethod
    endstruct
    
    /////////////////////////////////////////////////////
    // Demonstration Functions & Alternative Interface //
    ////////////////////////////////////////////////////////////////////////////
    // What this would look like in normal WC3 style JASS (should all inline).
    // 
    function CreateEvent takes nothing returns Event
        return Event.create()
    endfunction
    function DestroyEvent takes Event whichEvent returns nothing
        call whichEvent.chainDestroy()
    endfunction
    function FireEvent takes Event whichEvent returns nothing
        call whichEvent.fire()
    endfunction
    function TriggerRegisterEvent takes trigger whichTrigger, Event whichEvent returns EventReg
        return whichEvent.register(whichTrigger)
    endfunction
    
    // And for EventRegs...
    function SetEventRegData takes EventReg whichEventReg, integer data returns nothing
        set whichEventReg.data=data
    endfunction
    function GetEventRegData takes EventReg whichEventReg returns integer
        return whichEventReg.data
    endfunction
    function GetTriggeringEventReg takes nothing returns integer
        return Event.getTriggeringEventReg()
    endfunction
endlibrary

I couldn't upload a test map due to running out of attachment space, but here's the script I used to test it:
JASS:
scope EventTest initializer DoTest
    private function ExampleCond takes nothing returns boolean
        call BJDebugMsg("Trigger "+I2S(GetTriggerExecCount(GetTriggeringTrigger()))+" fired.")
        return false
    endfunction
    private function DoTest takes nothing returns nothing
        local trigger t1=CreateTrigger()
        local trigger t2=CreateTrigger()
        local trigger t3=CreateTrigger()
        
        local Event e=CreateEvent()
        
        call TriggerExecute(t1)
        call TriggerExecute(t2)
        call TriggerExecute(t2)
        call TriggerExecute(t3)
        call TriggerExecute(t3)
        call TriggerExecute(t3)
        
        call TriggerAddCondition(t1,Condition(function ExampleCond))
        call TriggerAddCondition(t2,Condition(function ExampleCond))
        call TriggerAddCondition(t3,Condition(function ExampleCond))
        
        call TriggerRegisterEvent(t1,e)
        
        call BJDebugMsg("Fire event with Trigger 1 registered...")
        call FireEvent(e)
        
        call TriggerRegisterEvent(t2,e)
        call TriggerRegisterEvent(t3,e)
        
        call BJDebugMsg("Fire event with Trigger 1, 2 and 3 registered...")
        call FireEvent(e)
        
        call TriggerRegisterEvent(t1,e)
        call DestroyTrigger(t2)
        
        call BJDebugMsg("Fire event with Trigger 1 (twice) and 3 registered...")
        call FireEvent(e)
        
        call DestroyEvent(e)
    endfunction
endscope
And the script to test the new data attachment on EventRegs:
JASS:
scope EventTest initializer DoTest
    private function ExampleCond takes nothing returns boolean
        call BJDebugMsg("Trigger "+I2S(GetEventRegData(GetTriggeringEventReg()))+" fired.")
        return false
    endfunction
    private function DoTest takes nothing returns nothing
        local trigger t1=CreateTrigger()
        local trigger t2=CreateTrigger()
        local trigger t3=CreateTrigger()
        
        local Event e=CreateEvent()
        
        call TriggerAddCondition(t1,Condition(function ExampleCond))
        call TriggerAddCondition(t2,Condition(function ExampleCond))
        call TriggerAddCondition(t3,Condition(function ExampleCond))
        
        call SetEventRegData(TriggerRegisterEvent(t1,e),1)
        
        call BJDebugMsg("Fire event with Trigger 1 registered...")
        call FireEvent(e)
        
        call SetEventRegData(TriggerRegisterEvent(t2,e),2)
        call SetEventRegData(TriggerRegisterEvent(t3,e),3)
        
        call BJDebugMsg("Fire event with Trigger 1, 2 and 3 registered...")
        call FireEvent(e)
        
        call SetEventRegData(TriggerRegisterEvent(t1,e),1)
        call DestroyTrigger(t2)
        
        call BJDebugMsg("Fire event with Trigger 1 (twice) and 3 registered...")
        call FireEvent(e)
        
        call DestroyEvent(e)
    endfunction
endscope
It's a pretty low-level snippet. It can be applied in a whole range of this (and already has been at the time of this update).

Updates:
- Version 1.04: Better supported recursive Events, reversed the firing order to match Warcraft III's, added .unregister(trigger), implemented .destroy() for events (deprecating .chainDestroy()) and added .destroy() for EventRegs (to unregister a given registration). Changed internal implementation to a doubly linked circular list.
- Version 1.03: Fixed a bug to do with recursive Events.
- Version 1.02: Updated documentation (finally).
- Version 1.01: Added EventRegs.
- Version 1.00: Release.
 

Viikuna

No Marlo no game.
Reaction score
265
Awesome.

You should maybe add IsTriggerEnabled check there. ( Im not sure, but I think you can evaluate and execute disabled trigers too )
 

Romek

Super Moderator
Reaction score
963
method chainDestroy would make more sense as onDestroy.
I don't see why anybody would want to destroy it without doing what chainDestroy does anyway.

> if GetTriggerEvalCount(current.trig)==0 then // If trigger destroyed...
Isn't that set to 0 when the trigger is 'reset' too? (call ResetTrigger(..))
I've never messed around with triggers as such, but I've certainly read about that.

Also, 'next' could be given a default value of 0, and the 'create' method could be removed.
 

Azlier

Old World Ghost
Reaction score
461
Not too sure how to use this, and not so sure I'll get any efficiency gain. Besides, I like DoubleClick not requiring any other systems. ;)

Edit: Ooh, now I gots it. I'll release an Event version now. And do what Romek said about the 0, I made TrigChains do that.
 

Jesus4Lyf

Good Idea™
Reaction score
397
Hmmm. I think you're right there, Viikuna. I'll fix that when I release data attachment probably.

Ahh, Romek, everything happens for a reason!

>method chainDestroy would make more sense as onDestroy.
I would need to make onDestroy recursive then, which is less efficient. (.destroy() is called inside .chainDestroy().)
vJass has this horrible "feature" of not being able to write your own .destroy() method with a .deallocate() inside it, although you can write your own .create() method with a .allocate(). :(
It's one of the things that limits the usefulness of structs.
Afterthought: No, I couldn't even make this recursive, otherwise when a trigger is destroyed it would destroy its node and all the nodes after it. I could write a way around it but this still simplifies code and makes it more efficient. If you still think I should use the .destroy() function, let me know. o.o

> if GetTriggerEvalCount(current.trig)==0 then // If trigger destroyed...
>Isn't that set to 0 when the trigger is 'reset' too? (call ResetTrigger(..))
I thought about that. Call it a feature for detaching the triggering event. :)
I might even write a function wrapper for it.

>Also, 'next' could be given a default value of 0, and the 'create' method could be removed.
But then it will set 0 unnecessarily for registering new triggers! :p
IMAGINE THE LAG!

>I like DoubleClick not requiring any other systems
Precisely why this is a snippet. You can just embed it in your system, just change everything to private (including the struct type).
 

Azlier

Old World Ghost
Reaction score
461
Did some testing, and the '=0' is unneeded. Global integer arrays naturally have 0 in each index, just as global boolean arrays have false. Locals don't do that.
 

Jesus4Lyf

Good Idea™
Reaction score
397
Azlier, beware stability. I hope you test things more thoroughly in future.

If a struct is destroyed and then another created with the same index, old values that are not initialised are retained. In this case, it could be the .next pointers. This could lead (and nearly certain would if used any reasonable amount) to events firing other events or parts of them accidentally.

It's exactly for reasons like this that I produced this snippet. Stability.
Linked lists are prone to unpredictable behavior if not thought through carefully. :)

Edit: Example (for if you remove the set 0):
Create Event E1.
Register Trigger T1 on E1.
Destroy Event E1.
Create Event E2.
Create Event E3.
Register Trigger T2 on E3.
Firing E2 at this point will fire Trigger T2 and everything else in E3. It will also, on the first time it fires, deregister E3 and attach its chain onto its own (at the end) (but its current chain was empty anyway). Therefore...
Fire E2.
Create Event E4.
Register Trigger T3 on E4.
Now when you fire E2, it will fire the old E3. Firing E3 will fire E4. E3 and E4 are in fact exactly the same and would both fire T3. This is the same as doubly allocating a handle.

And that is why you must set to 0. (I didn't test the above. It's based on my knowledge.)
 

Azlier

Old World Ghost
Reaction score
461
And I thought destroying them made it become 0. Apparently not. Perhaps I should've tested with structs. :p
 

Azlier

Old World Ghost
Reaction score
461
Too busy writing a failing random maze generator :(
 

Azlier

Old World Ghost
Reaction score
461
Well. I was wrong, then.

Oh, and I recommend either putting the snippet in a library or removing the "helpful" user functions.
 

Jesus4Lyf

Good Idea™
Reaction score
397
Ok. I've put it in a library for easy referencing (even though it's meant to be a snippet, not a system XD).

I've released the new version with data attachment. It's fully backwards compatible, and
JASS:
GetEventRegData(GetTriggeringEventReg())

should actually inline to
JASS:
Data[GlobalIntOfSomeSort]

:D
 

Azlier

Old World Ghost
Reaction score
461
JASS:
private static Event current
        static method getTriggeringEventReg takes nothing returns EventReg
            return .current
        endmethod

...Why?
JASS:
readonly static Event current


JASS:
function SetEventRegData takes EventReg whichEventReg, integer data returns nothing
    set whichEventReg.data=data
endfunction
function GetEventRegData takes EventReg whichEventReg returns integer
    return whichEventReg.data
endfunction
function GetTriggeringEventReg takes nothing returns integer
    return Event.getTriggeringEventReg()
endfunction

These are returning integers... why?

In fact, what good can possibly come from those functions?
 

Romek

Super Moderator
Reaction score
963
> These are returning integers... why?
Structs = Integers

> In fact, what good can possibly come from those functions?
They make things neater, and are inlined anyway.
One can choose not to use them if they don't want to.
 

Azlier

Old World Ghost
Reaction score
461
Yes, I know structs return integers. But integers don't allow . syntax. I would much prefer the functions returning structs.
 

Cohadar

master of fugue
Reaction score
209
This library needs some serious documentation.
For starters explining more clearly what is it, how to use it and why.

And what the hell is EventReg?
A pointless non-struct used to garbage the code for no apparent reason?

And make those damn functions public, you don't own the exclusive access right to jass namespace.
I do.


EDIT:
Who the hell approved of this resource?
 

Cohadar

master of fugue
Reaction score
209
Ah was looking at the wrong subforum. nwm.

@Jesus4Lyf
JASS:

// Just because blizzard does not have one does not...
function Event_TriggerUnRegister takes trigger whichTrigger, Event whichEvent returns nothing
 
General chit-chat
Help Users
  • No one is chatting at the moment.
  • Varine Varine:
    How can you tell the difference between real traffic and indexing or AI generation bots?
  • The Helper The Helper:
    The bots will show up as users online in the forum software but they do not show up in my stats tracking. I am sure there are bots in the stats but the way alot of the bots treat the site do not show up on the stats
  • Varine Varine:
    I want to build a filtration system for my 3d printer, and that shit is so much more complicated than I thought it would be
  • Varine Varine:
    Apparently ABS emits styrene particulates which can be like .2 micrometers, which idk if the VOC detectors I have can even catch that
  • Varine Varine:
    Anyway I need to get some of those sensors and two air pressure sensors installed before an after the filters, which I need to figure out how to calculate the necessary pressure for and I have yet to find anything that tells me how to actually do that, just the cfm ratings
  • Varine Varine:
    And then I have to set up an arduino board to read those sensors, which I also don't know very much about but I have a whole bunch of crash course things for that
  • Varine Varine:
    These sensors are also a lot more than I thought they would be. Like 5 to 10 each, idk why but I assumed they would be like 2 dollars
  • Varine Varine:
    Another issue I'm learning is that a lot of the air quality sensors don't work at very high ambient temperatures. I'm planning on heating this enclosure to like 60C or so, and that's the upper limit of their functionality
  • Varine Varine:
    Although I don't know if I need to actually actively heat it or just let the plate and hotend bring the ambient temp to whatever it will, but even then I need to figure out an exfiltration for hot air. I think I kind of know what to do but it's still fucking confusing
  • The Helper The Helper:
    Maybe you could find some of that information from AC tech - like how they detect freon and such
  • Varine Varine:
    That's mostly what I've been looking at
  • Varine Varine:
    I don't think I'm dealing with quite the same pressures though, at the very least its a significantly smaller system. For the time being I'm just going to put together a quick scrubby box though and hope it works good enough to not make my house toxic
  • Varine Varine:
    I mean I don't use this enough to pose any significant danger I don't think, but I would still rather not be throwing styrene all over the air
  • The Helper The Helper:
    New dessert added to recipes Southern Pecan Praline Cake https://www.thehelper.net/threads/recipe-southern-pecan-praline-cake.193555/
  • The Helper The Helper:
    Another bot invasion 493 members online most of them bots that do not show up on stats
  • Varine Varine:
    I'm looking at a solid 378 guests, but 3 members. Of which two are me and VSNES. The third is unlisted, which makes me think its a ghost.
    +1
  • The Helper The Helper:
    Some members choose invisibility mode
    +1
  • The Helper The Helper:
    I bitch about Xenforo sometimes but it really is full featured you just have to really know what you are doing to get the most out of it.
  • The Helper The Helper:
    It is just not easy to fix styles and customize but it definitely can be done
  • The Helper The Helper:
    I do know this - xenforo dropped the ball by not keeping the vbulletin reputation comments as a feature. The loss of the Reputation comments data when we switched to Xenforo really was the death knell for the site when it came to all the users that left. I know I missed it so much and I got way less interested in the site when that feature was gone and I run the site.
  • Blackveiled Blackveiled:
    People love rep, lol
    +1
  • The Helper The Helper:
    The recipe today is Sloppy Joe Casserole - one of my faves LOL https://www.thehelper.net/threads/sloppy-joe-casserole-with-manwich.193585/
  • The Helper The Helper:
    Decided to put up a healthier type recipe to mix it up - Honey Garlic Shrimp Stir-Fry https://www.thehelper.net/threads/recipe-honey-garlic-shrimp-stir-fry.193595/

      The Helper Discord

      Members online

      No members online now.

      Affiliates

      Hive Workshop NUON Dome World Editor Tutorials

      Network Sponsors

      Apex Steel Pipe - Buys and sells Steel Pipe.
      Top