One timer is cool and all

Nestharus

o-o
Reaction score
84
Ok.... apparently I've been shooting too high on this, rofl... I tried out my tests on KT2 o-o... so... I'm doing good I guess ; ).

KT2 Vs Streams
I bet you would love to see this now wouldn't you : ), but I respect jesus4lyf so I'm not going to say the results =).

Streams (Timers) Vs Streams (Task Manager)
So what's the difference between these two? One uses Blizzard's Timers for Tasking timers and firing things off. The other uses its own custom task manager (runs on one timer ^_^).

Both are pretty much same speed... they lagged at the same rates ^_^; however, the Task Manager version still needs work (a bit complicated).

Both of them are hella faster than plain old timers when using the same periods. They also use precision to merge streams (timers) of the same period that are close to each other. The lower the precision, the less it merges. 0 is 0 merger unless they on exact same time =). The higher the period, the more the merges, and the more the performance, but the less accurate your first tick.

Both of them use data blocks as units. The data block size is that of the STREAM_RATE variable.

Stream Timers Version is a later version, so it uses definition overloading on the functions to make it nicer =), but this'll get introduced to Task Manager version as well.

The Stream Timers version is not that big... not even 200 lines with comments o-o. The Task Manager version is double size : p.

Uh... yea... so you can try them both out I suppose..

Oh yes.. and both of them beat out everything on data attachment : D. SetupStream must be called on both of them (to set your stream up), or the stream dies : (.

So... without further ado, v4.3 of Task Manager of v1.0 of Timers : D.

Timers Demo Code
Task Manager Demo Code isn't out because the Task Manager syntax is going to change to match Timers ^^
JASS:
include "cj_types.j"
include "cj_typesEx.j"
include "cj_print.j"
include "cj_types_priv.j"  
include "cj_typesEx_priv.j"
include "cj_order.j"  
include "cj_antibj_base.j"


scope Demo initializer Initialization {
    int loops
    int i = 1
    int size = 0
    trigger t = CreateTrigger()
    timer ti = CreateTimer()
    
    bool Test() {
        Stream.setup
        printf(I2S(Stream.get))
        return false
    }
    
    bool Setup() {
        loops = 1
        do {
            //TimerStart(CreateTimer(), ++size*.0125, true, function Test)
            size += 32
            Stream.add(size, function Test, 0)
            //KT_Add(function Test, 5, ++size*.0125)
        } whilenot --loops == 0
        return false
    }
    
    void Initialization() {
        TriggerAddCondition(t, Condition(function Setup))
        do {
            TriggerEvaluate(t)
        } whilenot --i == 0
    }
}


The Task Manager
JASS:
library Stream initializer Initialization {
/*System Information
//===================================================================
Name: Streams (Task Manager)
Version: 4.3
Author: Nestharus

Settings:
*///===================================================================
globals
    private constant real STREAM_RATE = .03125
    private constant int PRECISION = 1
endglobals
/*//===================================================================

Description:
This is a very fast and efficient timer system that runs on two timers, or known as streams.
One timer deals with whole numbers and the other timer deals with smaller numbers.

Requirements: None

Installation: NA

Variables:
-STREAM_RATE
    How many often the stream runs. The bigger the bit rate,
    the more accurate your timers can be. .03125 is a good value.

-PRECISION
    How precise streamed code is. The smaller the value, the more precise, but the more
    streams. The less precise, the less stream and higher performance. 0 is maximum precision.
    Each precision is 1 stream block (STREAM_RATE).

Functions:
------------------------------------------------------------------
-public int AddCode(int period, code c, int data)
    Will put code into the system. Returns a unique stream.
    
-public void RemoveCode()
-public void RemoveStreamCode(Stream theStream)
    Will remove a specific or current running stream from the system
    
-public constant int GetAge()
-public constant int GetStreamAge(Stream theStream)
    Will get the age of a specified or current running strema.
    
-public constant int GetTime()
-public constant int GetStreamTime(Stream theStream)
    Will get timeout of a specified or current running stream
    
-public constant int GetStream()
    Will get the current running stream
    
-public void SetData(int value)
-public void SetStreamData(Stream theStream, int value)
    Set data on a specified or current running stream
    
-public int GetData()
-public int GetStreamData(Stream theStream)
    Get data for a specified or current running stream
    
-public constant int GetInterval()
-public constant int GetStreamInterval(Stream theStream)
    Returns a master stream's interval. Will automatically retrieve the master stream
    that a stream is on.

-public void SetStreamInterval(Stream theStream, int value)
-public void SetInterval(int value)
    Will forcefully set a master stream's interval and will take effect on next tick.
    This will change all streams on the master stream. To change a time out, you should
    remove the stream and re-add it into the system! This can also cause two master streams
    to have the same value. Be careful with this function.
    
-public constant int GetRemainingTime(Stream theStream)
    Will return the remaining time of a given stream
    
-public int R2B(real r)
    Will convert a real time into a stream block for use in the system
    
------------------------------------------------------------------*/
//===================================================================    
    private int time = 0
    
    public keyword RunStack
    
    private struct Stream extends array {
        public Stream last
        public int birth
        public int interval
        public Stream master
        public Stream next
        public Stream previous
        public int data
        public triggercondition conditionCode
        public trigger runStream
        public RunStack stack
        public int size
    }
    
    public struct RunStack extends array {
        public static int index = 0
        public static int array stack
        public static int stackIndex = 0
        public static RunStack current = 0
        public static RunStack last = 0
        public int time
        public RunStack previous
        public RunStack next
        public Stream data
    }
    
    private int array streamStack
    private int streamStackIndex = 0
    private int streamIndex = 0
    
    public Stream stream
    
    private Stream masterStream = 0
    public RunStack runStack = 0
    public int i
    private int i2
    private int i3
    private bool fin = false
    
    private hashtable streams = InitHashtable()
    
    private timer streamProcess = CreateTimer()
    
    define SetupStream = {
        if Stream_stream == -1 {Stream_stream = Stream_RunStack(Stream_i).data.next}
        else {Stream_stream = Stream_stream.next}
    }
    
    public int GetData() {
        return stream.data
    }
    
    public void SetData(int value) {
        stream.data = value
    }
    
    public constant int GetStream() {
        return stream
    }
    
    public constant int GetTime() {
        return time
    }
    
    public constant int GetRemainingTime(Stream theStream) {
        return theStream.master.stack.time - time
    }
    
    public constant int GetAge() {
        return time - stream.birth
    }
    
    public constant int GetInterval() {
        return stream.master.interval
    }
    
    public void SetInterval(int value) {
        stream.master.interval = value
    }
    
    public void RemoveCode() {
        RELEASE_STREAM(stream)
        TriggerRemoveCondition(stream.runStream, stream.conditionCode)
        stream.data = 0
        REMOVE_STREAM(stream)
    }
    
    public int GetStreamData(Stream theStream) {
        return theStream.data
    }
    
    public void SetStreamData(Stream theStream, int value) {
        theStream.data = value
    }
    
    public int R2B(real r) {
        return R2I((r/STREAM_RATE)+.5)
    }
    
    public constant int GetStreamTime(Stream theStream) {
        return theStream.master.stack.time
    }
    
    public constant int GetStreamAge(Stream theStream) {
        return time - theStream.birth
    }
    
    public void RemoveStreamCode(Stream theStream) {
        RELEASE_STREAM(theStream)
        TriggerRemoveCondition(theStream.runStream, theStream.conditionCode)
        theStream.data = 0
        REMOVE_STREAM(theStream)
    }
    
    public constant int GetStreamInterval(Stream theStream) {
        return theStream.master.interval
    }
    
    public void SetStreamInterval(Stream theStream, int value) {
        theStream.master.interval = value
    }
    
    define private NEW_STREAM_STACK(streamName) = {
        if RunStack.stackIndex != 0 {
            streamName = RunStack.stack[RunStack.stackIndex--]
        }
        else {
            streamName = ++RunStack.index
        }
    }
    
    define private NEW_STREAM(streamName) = {
        if streamStackIndex != 0 {
            streamName = streamStack[streamStackIndex--]
        }
        else {
            streamName = ++streamIndex
        }
    }
    
    define private RELEASE_STREAM(streamName) = {
        streamStack[++streamStackIndex] = streamName
    }
    
    define private REMOVE_STREAM(streamName) = {
        if streamName.last == streamName {
            streamName.master.last = streamName
        }
        streamName.previous.next = streamName.next
        streamName.next.previous = streamName.previous
        streamName.master.size--
        if streamName.master.size == 0 {
            RemoveSavedInteger(streams, masterStream.interval, masterStream.birth - (masterStream.birth - masterStream.interval))
            masterStream = streamName.master
            RELEASE_STREAM(masterStream)
            DestroyTrigger(masterStream.runStream)
            masterStream.stack.next.previous = masterStream.stack.previous
            masterStream.stack.previous.next = masterStream.stack.next
            if RunStack.current == masterStream.stack {
                RunStack.current = RunStack.current.next
                RunStack.current.previous = 0
            }
            if RunStack.last == masterStream.stack {
                RunStack.last = RunStack.last.previous
                RunStack.last.next = 0
            }
            RunStack.stack[++RunStack.stackIndex] = masterStream.stack
        }
    }
    
    define private RUN_STREAM_STACK_NEW(runStackNew) = {
        if RunStack.current == 0 {
            RunStack.current = runStackNew
            RunStack.last = runStackNew
            runStackNew = runStackNew
            runStackNew = runStackNew
            runStack.time = masterStream.interval + time
            fin = true
        }
    }
    
    define private RUN_STREAM_STACK_DO_GREATER_THAN(runStackOld, runStackNew) = {
        if runStackOld.data.interval+time >= masterStream.interval + time {
            if runStackOld.previous != 0 {
                runStackNew.previous = runStackOld.previous
                runStackOld.previous.next = runStackNew
            }
            else {
                RunStack.current = runStackNew
            }
            runStackOld.previous = runStackNew
            runStackNew.next = runStackOld
        }
    }
    
    define private RUN_STREAM_STACK_DO_LESS_THAN(runStackOld, runStackNew) = {
        if runStackOld.data.interval+time <= masterStream.interval + time {
            if runStackOld.next != 0 {
                runStackNew.next = runStackOld.next
                runStackOld.next.previous = runStackNew
            }
            else {
                RunStack.last = runStackNew
            }
            runStackNew.previous = runStackOld
            runStackOld.next = runStackNew
        }
    }
    
    define private RUN_STREAM_STACK_GREATER_THAN(runStackOld, runStackNew) = {
        if runStackOld.data.interval+time > masterStream.interval + time {
            i = runStackOld
            whilenot RunStack(i).previous.data == 0 || RunStack(i).data.interval+time >= masterStream.interval + time {
                i = RunStack(i).previous
            }
            RUN_STREAM_STACK_DO_GREATER_THAN(RunStack(i), runStackNew)
            RUN_STREAM_STACK_DO_LESS_THAN(RunStack(i), runStackNew)
            runStackNew = runStackNew
            runStackNew = runStackNew
            runStack.time = masterStream.interval + time
            fin = true
        }
    }
    
    define private RUN_STREAM_STACK_LESS_THAN(runStackOld, runStackNew) = {
        if runStackOld.data.interval+time < masterStream.interval + time {
            i = runStackOld
            whilenot RunStack(i).next == 0 || RunStack(i).data.interval+time >= masterStream.interval + time {
                i = RunStack(i).next
            }
            i = RunStack(i)
            RUN_STREAM_STACK_DO_GREATER_THAN(RunStack(i), runStackNew)
            RUN_STREAM_STACK_DO_LESS_THAN(RunStack(i), runStackNew)
            runStackNew = runStackNew
            runStackNew = runStackNew
            runStack.time = masterStream.interval + time
            fin = true
        }
    }
    
    public int AddCode(int period, code c, int data) {
        fin = false
        i2 = PRECISION*-1
        do {
            i = LoadInteger(streams, period, time-(time-period)+i2)
            i2++
        } whilenot i != 0 || i2 == PRECISION
        if i == 0 {
            fin = false
            NEW_STREAM(masterStream)
            masterStream.master = masterStream
            masterStream.last = masterStream
            masterStream.interval = period
            masterStream.birth = time
            masterStream.runStream = CreateTrigger()
            SaveInteger(streams, period, time-(time-period), masterStream)
            NEW_STREAM_STACK(runStack)
            RUN_STREAM_STACK_NEW(runStack)
            if !fin {
                RUN_STREAM_STACK_GREATER_THAN(RunStack.current, runStack)
            }
            if !fin {
                RUN_STREAM_STACK_LESS_THAN(RunStack.current, runStack)
            }
            masterStream.stack = runStack
            runStack.data = masterStream
        }
        else {
            masterStream = i
        }
        NEW_STREAM(stream)
        masterStream.last.next = stream
        stream.previous = masterStream.last
        masterStream.last = stream
        stream.master = masterStream
        stream.birth = time
        stream.conditionCode = TriggerAddCondition(masterStream.runStream, Condition(c))
        masterStream.size++
        stream.data = data
        return stream
    }
    
    private void StreamProcess() {
        time++
        if RunStack.current.time == time {
            if RunStack.current.next != 0 {
                i = RunStack.current
                runStack = RunStack(i).next
                do {
                    stream = -1
                    TriggerEvaluate(RunStack(i).data.runStream)
                    RunStack(i).time = RunStack(i).data.interval + time
                    i3 = RunStack(i).next
                    runStack = i2
                    if (RunStack(i).time < runStack.time && RunStack(i).time < runStack.previous.time) {
                        runStack = RunStack(i).next
                    }
                    whilenot RunStack(i).time < runStack.time || runStack.next == 0 {
                        runStack = runStack.next
                    }
                    if RunStack(i).next != runStack {
                        if RunStack(i).time <= runStack.time {
                            runStack.previous.next = i
                            RunStack(i).previous.next = RunStack(i).next
                            RunStack(i).next.previous = RunStack(i).previous
                            RunStack(i).previous = runStack.previous
                            runStack.previous = i
                            RunStack(i).next = runStack
                        }
                        else {
                            RunStack(i).previous.next = RunStack(i).next
                            RunStack(i).next.previous = RunStack(i).previous
                            RunStack(i).next = runStack.next
                            RunStack(i).previous = runStack
                            runStack.next = i
                        }
                        if RunStack(i) == RunStack.current {
                            RunStack.current = i3
                        }
                        if runStack == RunStack.last {
                            RunStack.last = i
                        }
                    }
                    i2 = i
                    i = i3
                } whilenot RunStack(i).time != time
            }
            else {
                stream = -1
                i = RunStack.current
                TriggerEvaluate(RunStack.current.data.runStream)
                RunStack.current.time = RunStack.current.data.interval + time
            }
        }
    }
    
    private void Initialization() {
        stream = -1
        TimerStart(streamProcess, STREAM_RATE, true, function StreamProcess)
    }
}



Timers version requires C Style Recycling-
JASS:
/*Utility Information
//===================================================================
Name: Recycle
Version: 1.0
Author: Nestharus
-Help from Jesus4Lyf for initial designs

Description:
    What does it do-
        A stack of a type. When you are done using something, 
        you throw it into the stack and it is added to the stack. 
        When you need something, it'll either get something from the 
        stack, or it'll create new if the stack is empty. This is very 
        simple, but I keep having to rewrite this over and over again 
        so I figured this'd be cooler : ).
        
Requirements: NA

Installation: NA

Functions:
------------------------------------------------------------------
-get returns type
    will return the type
    
    Timer.get()
    Group.get()
    

-release(type h)
    Will release the type and stop it.
    
    Timer.release(h)
    Group.release(h)
------------------------------------------------------------------*/
library Recycle {    
    define private varName(varNameType) = {
        public varNameType handles
        public static integer index = 0
    }
    
    struct Timer extends array {
        varName(timer)
        define {
            <Timer.get>(varName) = {
                if Timer.index == 0 {
                    varName = CreateTimer()
                    //probs?
                }
                else {
                    varName = Timer[--Timer.index].handles
                }
            }
            <Timer.release>(varName) = {
                PauseTimer(varName)
                Timer[Timer.index++].handles = varName
            }
        }
    }
    
    struct Group extends array {
        varName(group)
        define {
            <Group.get>(varName) = {
                if Group.index == 0 {
                    varName = CreateTimer()
                }
                else {
                    varName = Group[--Group.index].handles
                }
            }
            <Group.release>(varName) = {
                PauseTimer(varName)
                Group[Group.index++].handles = varName
            }
        }
    }
}


And requires C Game Time
JASS:
/*Utility Information
//===================================================================
Name: GameTime
Version: 1.0
Author: Nestharus

Description: This will track the game time.

Requirements: None

Installation: NA

Usage:
------------------------------------------------------------------
-GetElapsedGameTime takes nothing returns real
 will return the current elapsed game time
 
 local real currentGameTime = GetElapsedGameTime
------------------------------------------------------------------*/
//===================================================================

library GameTime initializer Initialization {
    timer gameTime = CreateTimer()
    define {GetElapsedGameTime() = TimerGetElapsed(gameTime)}
    private void Initialization() {TimerStart(gameTime, 1000000, false, null)}
}


Timers
JASS:
library Stream initializer Initialization uses GameTime, Recycle {
/*System Information
//===================================================================
Name: Streams (Timers)
Version: 1.1
Author: Nestharus

Settings:
*///===================================================================
globals
    public constant real STREAM_RATE = .03125
endglobals
/*//===================================================================

Description:
A timer system that merges timers into master streams.

Requirements: None

Installation: NA

Variables:
-STREAM_RATE
    Size of a block in a stream

Functions:
------------------------------------------------------------------
Methods/Operators
-Stream.add(int period, code c, int data, int precision)
    Will put code into the system. Returns a unique stream.
    
    Precision-
        How precise streamed code is. The smaller the value, the more precise, but the more
        streams. The less precise, the less stream and higher performance. 0 is maximum precision.
    
-Stream.remove()
-Stream.removeStream(Stream theStream)
    Will remove a specific or current running stream from the system
    
-Stream.getAge
-Stream.getAge(theStream)
    Will get the age of a specified or current running strema.
    
-Stream.getTime
-Stream.getTime(Stream theStream)
    Will get timeout of a specified or current running stream
    
-Stream.getRemaining
-Stream.getRemaining(Stream theStream)
    Will get remaining time of a specified or current running stream
    
-Stream.get()
    Will get the current running stream
    
-Stream.setData(value)
-Stream.setData(theStream, value)
    Set data on a specified or current running stream
    
-Stream.getData
-Stream.getData(theStream)
    Get data for a specified or current running stream
    
-Stream.getInterval()
-Stream.getInterval(Stream theStream)
    Returns a master stream's interval. Will automatically retrieve the master stream
    that a stream is on.
    
-Stream.R2B(real r)
    Will convert a real time into a stream block for use in the system
    
-Stream.Setup
    Sets the stream up. Must be called on all timer functions
------------------------------------------------------------------*/
//===================================================================
    private int time = 0
    
    public Stream stream = -1
    public Stream masterStream = 0
    public int i = 0
    private int timeout
    
    private boolexpr streamSetup
    
    private hashtable streams = InitHashtable()
    
    struct Stream extends array {
        public static int array stack
        public static int index = 0
        public static int stackIndex = 0
        public Stream master
        public int interval
        public Stream previous
        public Stream next
        public Stream last
        public triggercondition conditionCode
        public trigger runStream
        public timer runStreamTimer
        public int birth
        public int data
        
        private static timer array timerStack
        private static trigger array triggerStack
        private static int timerTriggerStack = 0
        
        public static void remove() {
            ReleaseStream(stream)
            TriggerRemoveCondition(stream.runStream, stream.conditionCode)
            stream.data = 0
            RemoveStream(stream)
        }
        
        public static void removeStream(Stream theStream) {
            ReleaseStream(theStream)
            theStream.data = 0
            RemoveStream(theStream)
        }
        
        define private NewStream(streamName) = {
            if Stream.stackIndex != 0 {
                streamName = Stream.stack[Stream.stackIndex--]
            }
            else {
                streamName = ++Stream.index
            }
        }
        
        define private <NewTimerTrigger> = {
            if Stream.timerTriggerStack != 0 {
                masterStream.runStream = Stream.triggerStack[--Stream.timerTriggerStack]
                masterStream.runStreamTimer = Stream.timerStack[Stream.timerTriggerStack]
            }
            else {
                masterStream.runStream = CreateTrigger()
                Timer.get(masterStream.runStreamTimer)
                TriggerRegisterTimerExpireEvent(masterStream.runStream, masterStream.runStreamTimer)
            }
        }
        
        define private <ReleaseTimerTrigger> = {
            TriggerClearConditions(masterStream.runStream)
            PauseTimer(masterStream.runStreamTimer)
            Stream.triggerStack[Stream.timerTriggerStack] = masterStream.runStream
            Stream.timerStack[Stream.timerTriggerStack++] = masterStream.runStreamTimer
        }
        
        public static bool StreamSetup() {
            time = R2I(GetElapsedGameTime*STREAM_RATE)
            timeout = R2I(TimerGetTimeout(GetExpiredTimer())/STREAM_RATE)
            stream = LoadInteger(streams, timeout , time-(time-timeout))
            return false
        }
        
        public static int add(int period, code c, int data, int precision) {
            i = precision*-1
            time = R2I(GetElapsedGameTime*STREAM_RATE)
            do {
                masterStream = LoadInteger(streams, period, time-(time-period)+i)
                i++
            } whilenot integer(masterStream) != 0 || i == precision
            if integer(masterStream) == 0 {
                NewStream(masterStream)
                masterStream.last = masterStream
                masterStream.interval = period
                masterStream.birth = time
                NewTimerTrigger
                TriggerAddCondition(masterStream.runStream, streamSetup)
                TimerStart(masterStream.runStreamTimer, I2R(period)*STREAM_RATE, true, null)
                SaveInteger(streams, period, time-(time-period), masterStream)
            }
            NewStream(stream)
            masterStream.last.next = stream
            stream.previous = masterStream.last
            masterStream.last = stream
            stream.master = masterStream
            stream.birth = time
            stream.conditionCode = TriggerAddCondition(masterStream.runStream, Condition(c))
            stream.data = data
            return stream
        }
    }
    
    define {
        <Stream.getData> = Stream_stream.data
        <Stream.getData>(theStream) = Stream_Stream(theStream).data
        <Stream.setData>(value) = Stream_stream.data
        <Stream.setData>(theStream, value) = Stream_Stream(theStream) = value
        <Stream.get> = Stream_stream
        <Stream.getAge> = GetElapsedGameTime - I2R(Stream_stream.birth)/STREAM_RATE
        <Stream.getAge>(theStream) = GetElapsedGameTime - I2R(Stream_Stream(theStream).birth)/STREAM_RATE
        <Stream.R2B>(realVal) = R2I((realVal/Stream_STREAM_RATE)+.5)
        <Stream.getTime> = TimerGetElapsed(Stream_stream.master.runStreamTimer)
        <Stream.getTime>(theStream) = TimerGetElapsed(Stream_Stream(theStream).master.runStreamTimer)
        <Stream.getInterval> = Stream_stream.master.interval
        <Stream.getInterval>(theStream) = Stream_Stream(theStream).master.interval
        <Stream.getRemaining> = TimerGetRemaining(Stream_stream.master.runStreamTimer)
        <Stream.getRemaining>(theStream) = TimerGetRemaining(Stream_stream.master.runStreamTimer)
        <Stream.setup> = Stream_stream = Stream_stream.next
    }
    
    define private ReleaseStream(streamName) = Stream.stack[++Stream.stackIndex] = streamName
    
    define private RemoveStream(streamName) = {
        if streamName.master.last == streamName {
            streamName.master.last = streamName.previous
        }
        streamName.previous.next = streamName.next
        streamName.next.previous = streamName.previous
        if streamName.master.last == streamName.master {
            masterStream = streamName.master
            RemoveSavedInteger(streams, masterStream.interval, masterStream.birth - (masterStream.birth - masterStream.interval))
            ReleaseStream(masterStream)
            ReleaseTimerTrigger
        }
    }
    
    private void Initialization() {
        streamSetup = Condition(function Stream.StreamSetup)
    }
}
 

Viikuna

No Marlo no game.
Reaction score
265
IMO systems like KT2 serve no purpose. ( sry, Jesus4Lyf )

One timer method works perfectly when you want to do something that requires smooth transition or fast tracking or whatever you want. ( moving units, changing their colour over time or whatever you want to do ) T32 does this all with one timer, and its simple and covers all that stuff. It does only that, but is good in what it does.

TimerUtils covers all the rest. Its fast enough, if you are not being stupid, and its really flexible. I guess I could use KT2 for most of stuff, but since Id still need TimerUtils for few things that KT2 cant handle, I might as well use TimerUtils for it all.

Also, one timer per timed effect is really cool when doing slow time stuff or other wicked shit.
 

Romek

Super Moderator
Reaction score
963
Submitting your cJass resources here instead of in T&R simply isn't the way foreward.
 

Nestharus

o-o
Reaction score
84
It wasn't a submission =).

I've been trying to tackle this problem for a long while, so I figured letting other people see what I've been trying to tackle and what my current solution is would be productive =).

In essence, I hit a dead end with single timer systems and this is the best I think they can get o-o.
 

Jesus4Lyf

Good Idea™
Reaction score
397
>IMO systems like KT2 serve no purpose. ( sry, Jesus4Lyf )
Nah, I agree. T32 and TU are fine.

KT2 is brilliant for ease of use + efficiency, learn one system and let it figure out the way forward. For high class JASSers I recommend T32 for all things periodic and TU for all waits. :thup: S'more efficient.

Of course, it makes no real difference.

PS. Haven't read that code yet, Nestharus.
 

kingkingyyk3

Visitor (Welcome to the Jungle, Baby!)
Reaction score
216
For high class JASSers I recommend T32 for all things periodic and TU for all waits.
=_= I does not use TU-like systems more, because of my laziness to type NewTimer(), ReleaseTimer(), nulling variables, SetTimerData(). KT2-like systems is my cup of tea. :)
 

Viikuna

No Marlo no game.
Reaction score
265
For high class JASSers I recommend T32 for all things periodic and TU for all waits.
=_= I does not use TU-like systems more, because of my laziness to type NewTimer(), ReleaseTimer(), nulling variables, SetTimerData(). KT2-like systems is my cup of tea. :)

Nulling which variables? You need to same variables with KT2 and TimerUtils.

You dont have to null local timer variables, if those refer to recycled timers.
 

Troll-Brain

You can change this now in User CP.
Reaction score
85
But you don't have it, if you use TU, since timers are never destroyed.
That's what Viikuna want mean.
 

Romek

Super Moderator
Reaction score
963
Coincidentally, I've recently started using TU and T32.
Then again, recently I've started a project (again - Though this'll be on standby for a while). I never used any timer systems before that, because I never needed to. :p
 

Nestharus

o-o
Reaction score
84
Well, KT2 is a lot better than TU : p.

3000 timers vrs 3000 timers in KT2, KT2 doesn't lag but the 3000 reg empty timers do : O.

But still, this little piece of code does deserve some merit. It has the same speed as regular timers (TimerStart()) but does it all thru one timer and has data attachment. 3000 regular timers and this lag at the same rate, lol.

I do think that's a pretty sound achievement, to do it all in one timer : O. It means the task list in it uses a sound algorithm for doing it ^_^.

Now, if I didn't have to worry about data attachment, I could merge all the triggers together... so maybe I'm looking at this wrong. Maybe overloading the methods in some sort of definition, one version with attachment and one version without ; ).

The non-attachment version would blow away everything, lol. Why? Merger of all of the evaluations into single trigger evaluations ^_^.

But again, a timer version would be better than this version : \.
 

Troll-Brain

You can change this now in User CP.
Reaction score
85
Well, KT2 is a lot better than TU : p.

3000 timers vrs 3000 timers in KT2, KT2 doesn't lag but the 3000 reg empty timers do : O.

I guess you used 3000 times the same period, which is not a real world scenario.
(Ofc if we imagine someone really needs 3000 timers at the same time ^^)

So the conclusion should be that a timer callback is less efficient than a native trigger evaluation on timer expire event, interesting.
I love my own timer system so :p

(maybe because a timer callback opens a new thread when an evaluation of a trigger doesn't, so KT2 should be less resistant to the reach of the limit op ?)
 

Troll-Brain

You can change this now in User CP.
Reaction score
85
You don't reach the limit op on evaluations...

JASS:
library Test initializer init

globals
    private integer I = 0
endglobals

private function Handler1 takes nothing returns nothing
    local integer i = 0
    
    loop
    exitwhen i == 23000
    
    set i = i+1
    endloop
    
    set I = I+1
    
    if I == 100 then
        call BJDebugMsg(" ")
        call BJDebugMsg("end")
        set I = 0
    endif

endfunction

private function Handler2 takes nothing returns boolean
    local integer i = 0
    
    loop
    exitwhen i == 23000
    
    set i = i+1
    endloop
    
    set I = I+1
    
    if I == 100 then
        call BJDebugMsg(" ")
        call BJDebugMsg("end")
        set I = 0
    endif
    return false

endfunction

private function Test1 takes nothing returns nothing
    local integer i = 0
    
    loop
    exitwhen i == 100
    
    set i = i+1
    
        call TimerStart(CreateTimer(),1.,true,function Handler1)
        
    endloop
    
    
endfunction

private function Test2 takes nothing returns nothing
    local trigger trig = CreateTrigger()
    local timer tim = CreateTimer()
    local integer i = 0
    
    call TimerStart(tim,1.,true,null)
    call TriggerRegisterTimerExpireEvent(trig,tim)
    
    loop
    exitwhen i == 100
    
    set i = i+1
    
        call TriggerAddCondition(trig,Condition(function Handler2))
    endloop
endfunction

private function init takes nothing returns nothing
    //call Test1()
    call Test2()
endfunction

endlibrary


It appears you are right, since "end" is displayed every 1.0 s for both Test1 and Test2, so the new thread is not a right theory.
Since each condition of a trigger already open a new thread.

Could you post a test code where KT2 beats X timers ?
If you still have it.
 

uberfoop

~=Admiral Stukov=~
Reaction score
177
Well, KT2 is a lot better than TU : p.

Of course KT2 beats TU for many timers of an equal period. That's why we say "TU and T32". TU is what's used for arbitrary length waits, and it's fantastic for that. Pretty much any situation where you have a single instance of a specific period running, TIU/TU Red is the most efficient method and arguably has the most intuitive syntax (if perhaps not quite the shortest.
 

Nestharus

o-o
Reaction score
84
My point is how do you setup a trigger with 3000 conditions and make each condition have a unique id associated with it that the condition can access.

I did it in this code with 3000 different triggers each with one unique id and one condition on it.

How could I change that into one trigger and maintain integrity on an evaluation?

Furthermore, take a look at the task list and think of this

If you have a timer with a period of 1
A timer with a period of 2
and a timer with a period of 4

Whenever they run at the same time, they are all run on the same tick as if they were all part of one timer and they are all moved to their next appropriate times.

1 (not moved)
1, 2 (not moved)
1 (not moved)
1, 2, 4 (not moved)

If you had 2 and 3
2 (moved behind 3)
3 (moved behind 2)

When they are equal, they aren't moved.

Atleast this is how it should work and I think this is how it works right now.



Would a task list be faster than the C++ timers firing? Only one timer fires instead of 5 timers when they run at the same time, even with different periods. Also, the task list exits the loops as soon as it finds the position, so if something does not move the loop isn't even entered.

The only slowdown would be the increasing size of the game duration variable (time). A reset would require blocks, but that would slow everything down. What this means is there is a max value. Also, if time was too big (max value exceeded), then the entire system would fail.

However, time could be increased up to 776.72296260127314814814814814815 days before it would fail, or more than 2 years, so this part is fine =).


Actually, sames with the same interval at the moment are thrown into a master stream if the interval matches. It should only be thrown if interval matches and interval + time matches within a variance. So if you want a maximum of no error, it'll only go if they are 100% match. 50% error means interval + time must be within 50%. Interval would still have to match =). If they don't match, it goes into a new master stream.

This is a slight design flaw on my part, but I can fix it =).
 

Troll-Brain

You can change this now in User CP.
Reaction score
85
My point is how do you setup a trigger with 3000 conditions and make each condition have a unique id associated with it that the condition can access.

This cJass code blow my eyes and i don't really know what you was trying to do.
But i think i've done what you want.
I still need to do some works to avoid the textmacro and make generic methods Add and GetData, ofc it will have limitations then (or not if u use hashtables).

Here is the code :

JASS:
//! textmacro t_Timers takes NAME , PERIOD

library $NAME$

globals
    public constant real PERIOD = $PERIOD$ // must be a low period or it will be horribly inaccurate
    public integer MAX = 8190
endglobals

struct $NAME$
    private static timer tim = CreateTimer()
    private static hashtable hasht = InitHashtable()
    private static trigger trig = CreateTrigger()
    private static thistype I = 0
    private static boolexpr temp_cond = null
   
    private thistype next
    private thistype prev
    private static thistype last = 0
    private static thistype first = 0
    private static thistype current = 0
   
    private triggercondition cond
    private integer data
    private thistype index_ended
   
    private thistype free_index
    private static thistype S = 0
   
    // Add a function to the timer loop
   
    static method Add takes code func, integer data returns nothing
        set .temp_cond = LoadBooleanExprHandle(.hasht,GetHandleId(Condition(func)),0)
   
        if .temp_cond == null then // this is the first time this function is used with this timer
            set .temp_cond = And(Condition(function thistype.initCond),Condition(func))
            set .temp_cond = And(.temp_cond,Condition(function thistype.end))
            call SaveBooleanExprHandle(.hasht,GetHandleId(Condition(func)),0,.temp_cond)
        endif
       
        if .S == 0 then
   
            set .S = 1
            set .first = thistype(1).free_index
            set .last = .first
            set thistype(0).next = .first
     
       
        elseif .S == 1 then
   
            set .S = 2
            set .last = thistype(2).free_index
            set .last.prev = .first
            set .first.next = .last
       
        else
   
            // get a struct instance
            set .S = .S +1
            set .current = .S.free_index

            // linked list, ...
            set .last.next = .current
            set .current.prev = .last
            set .last = .current
       
        endif       

        set .last.cond = TriggerAddCondition(.trig,.temp_cond)
        set .last.data = data
   
    endmethod
   
    // data maybe ?
    static method GetData takes nothing returns integer
        return .current.data
    endmethod
   
    private static method initTimerCycle takes nothing returns boolean
       
        loop
        exitwhen .I == 0
       
            set .current = .I.index_ended
            call TriggerRemoveCondition(.trig,.current.cond)

            // struct instance recycled
           
            set .S.free_index = .current
            set .S = .S-1
           
           
           
            if .current == .first then
                set .first = .current.next
                set thistype(0).next = .first
               
               
            elseif .current == .last then
                set .last.prev = .last
               
            else
           
                set .current.prev.next = .current.next
                set .current.next.prev = .current.prev
            endif
           
        set .I = .I-1
        endloop
       
        set .current = thistype(0)
       
        return false
    endmethod
   
    private static method end takes nothing returns boolean
        // i can't remove the condition yet, or it will stop the entire trigger evaluation
        set .I = .I+1
        set .I.index_ended = .current
       

        return false
    endmethod
   
    private static method initCond takes nothing returns boolean
        // linked list
        set .current = .current.next
        return true
    endmethod

    private static method onInit takes nothing returns nothing
        local integer i = 1
        call TriggerRegisterTimerExpireEvent(.trig,.tim)
        call TriggerAddCondition(.trig,Condition(function thistype.initTimerCycle))
       
        loop
        exitwhen i > MAX
       
           set thistype(i).free_index = i
       
       
        set i = i+1
        endloop
        call TimerStart(.tim,PERIOD,true,null)
    endmethod
   
endstruct

endlibrary

//! endtextmacro


Sorry if i'm off-topic, anyway any constructive comments would be fine.
 

Nestharus

o-o
Reaction score
84
That ... is def not what I'm doing.

That uses multiple timers.. 1 timer per period.

Mine uses 1 timer for all periods. Mine has 1 timer for an entire map, not 2, not 3, just 1.

Try again =)

As far as your data attachment, it's slower... it would run 2 evaluations instead of 1 for each evaluation, lol. I tried that before on attempt #3 and it failed utterly ^_^.

So yea, for your data attachment, you made the same mistake I made when I did attempt #3 =p

So yea yea, try again u =p

And hashtables should not be used as this is supposed to be fast action ><
 
General chit-chat
Help Users
  • No one is chatting at the moment.

      The Helper Discord

      Staff online

      Members online

      Affiliates

      Hive Workshop NUON Dome World Editor Tutorials

      Network Sponsors

      Apex Steel Pipe - Buys and sells Steel Pipe.
      Top