System Timer Utils

Nestharus

o-o
Reaction score
84
JASS:

library Tt /* v1.1.0.1
	*************************************************************************************
	*
	*	Timer Tools, or Tt for short, provides two types of timers.
	*
	*	Timer tools timers use a special merging algorithm. When two timers merge, they
	*	both expiring on the same timer. If two timers expire close to the same time, they
	*	merge. The first tick accuracy is based on the size of the timer's timeout. The larger
	*	the timeout, the more inaccurate the first tick can be. The tick only becomes inaccurate
	*	if it merges with another timer.
	*
	*	Timer timeouts are changed to a very slightly degree so that timers have a higher chance
	*	of merging. For example, 1.001 and 1 would have absolutely no chance of merging because
	*	they both have different timeouts. Timer Tools timers attempt to remedy this situation
	*	by making the timeout a little inaccurate (still highly accurate). This means that if
	*	2 timers have timeouts that are very close to each other, like 1.001 and 1, they'd both
	*	have a timeout of 1 and have a chance to merge. 1.001 would also be able to merge with
	*	other future 1 second timeout timers if any are added. The timeout inaccuracy is based
	*	on the size of the timeout. For example, 1.1's actual timeout would be
	*	1.09999 (just about 1.1) while 3600's timeout would be 3555.56, or about 44.44 seconds off. 
	*	Keep in mind that 3600 seconds is 1 full hour, so 44.44 seconds relative to that entire hour 
	*	really isn't that much. It's accuracy is 98.7654% at that point. The accuracy setting
	*	can be modified. Increasing it to a value like 10000000 will pretty much remove all inaccuracy.
	*	3600 with 10000000 accuracy turns into 3599.712.
	*
	************************************************************************************
	*
	*	*/uses/*
	*
	*		*/ Table /*		hiveworkshop.com/forums/jass-functions-413/snippet-new-table-188084/
	*
	************************************************************************************
	*
	*	SETTINGS
	*/
	globals
		/*************************************************************************************
		*
		*					RELATIVE_MERGE
		*
		*	Effects the accuracy of first tick. The smaller the merge value, the less chance two
		*	timers have of sharing the same first tick, which leads to worse performance.
		*	However, a larger merge value decreases the accuracy of the first tick.
		*
		*	Formula: Log(RELATIVE_MERGE/timeout)/100*timeout
		*		Log(64000/3600)/100*3600=44.995589035797596625536391805959 seconds max off
		*
		*************************************************************************************/
		private constant real RELATIVE_MERGE=64000

		/*************************************************************************************
		*
		*					CONSTANT_MERGE
		*
		*	Effects the accuracy of the first tick. This is a constant merge. It just adds
		*	this value to the relative merge.
		*
		*	The max 3600's first tick can be off is 44.995589035797596625536391805959+.03
		*	or 45.295589035797596625536391805959 seconds.
		*
		*************************************************************************************/
		private constant real CONSTANT_MERGE=.3

		/*************************************************************************************
		*
		*					RELATIVE_ACCURACY
		*
		*	Effects the timeout of a timer. A larger number will give a more accurate timeout.
		*	This allows two timers of relatively close timeouts to have a chance to merge and
		*	lowers the total number of timeouts within the system. More timeouts means less
		*	performance.
		*
		*	Formula: RELATIVE_ACCURACY/Round(RELATIVE_ACCURACY/timeout)
		*		64000/Round(64000/3600)=3555.5555555555555555555555555556
		*
		*		3600's timeout will be 3555.5555555555555555555555555556 instead of 3600
		*
		*************************************************************************************/
		private constant real RELATIVE_ACCURACY=64000

		/*************************************************************************************
		*
		*					CONSTANT_ACCURACY
		*
		*	This essentially just removes small decimals. An accuracy of 800 would allow for
		*	decimals in units of .00125. Removing small decimals helps increase the performance
		*	of very small timeouts by a drastic amount.
		*
		*	Formula: Round(timeout*CONSTANT_ACCURACY)/CONSTANT_ACCURACY
		*		Round(3555.5555555555555555555555555556*800)/800=3555.555
		*
		*	If a timeout is less than the minimum possible timeout, then it is changed to the
		*	minimum possible timeout.
		*
		*************************************************************************************/
		private constant real CONSTANT_ACCURACY=800
	endglobals
	/*
	************************************************************************************
	*
	*	Constant Timer Queue Module
	*		-	Constant timeouts
	*	Constant Merged Timer Module
	*		-	Dynamic timeouts
	*	Constant Light Timer Queue 1
	*		-	Specializes in 1 shot timers with constant 2+ timeouts. Useful for expirations on spell effects.
	*
	*		private static constant real TIMEOUT						Timer Queue
	*			-	Timer Queue expects this value declare at the top of the struct
	*			-	This value represents the constant timeout of the queue
	*
	*		real timeout												Merged Timers
	*			-	How often the timer expires
	*		readonly real elapsed										Merged Timers and Timer Queue
	*			-	How long the timer has run since its last expiration
	*		readonly real remaining										Merged Timers and Timer Queue
	*			-	How long the timer has left before it next expires
	*
	*		static method create takes real to returns thistype			Merged Timers
	*			-	Creates a new timer given a timeout
	*		static method create takes nothing returns thistype			Timer Queue, Light Timer Queue 1
	*			-	Creates a new timer of a constant timeout
	*
	*		method destroy takes nothing returns nothing				Timer Queue, Light Timer Queue 1, Merged Timers
	*																	Note: Light Timer Queue auto destroys in expiration method
	*			-	Destroys created timer
	*
	*		Module
	*			-	Implement these three modules into the struct in order to do a Constant Merged Timer. Code
	*			-	written in between the modules is run whenever a timer expires.
	*			-
	*			-	The first section allows local declarations and ini. It runs before a set of expiring timers
	*			-	are about to run, meaning that there is no access to a timer in this section.
	*			-
	*			-	The second section runs for each timer. Access the expiring timer via the this keyword.
	*
	*			module CTM							Merged Timers
	*			module CTQ							Timer Queue
	*			module CLTQ1						Light Timer Queue
	*				-	Declare locals in here
	*				-	Run ini code
	*			module CTMExpire					Merged Timers
	*			module CTQExpire					Timer Queue
	*				-	Run timer code
	*				-
	*				-	thistype this		refers to current expiring timer\
	*			module CTMNull						Merged Timers
	*			module CTQNull						Timer Queue
	*				-	Null locals here
	*			module CTMEnd						Merged Timers
	*			module CTQEnd						Timer Queue
	*			module CLTQ1End						Light Timer Queue
	*
	*			Example of Merged Timers
	*				struct MyTimer extends array
	*					integer myValue
	*					implement CTM
	*						local string s="My value is "
	*					implement CTMExpire
	*						call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,60,s+I2S(myValue))
	*						call destroy()
	*					implement CTMNull
	*						set s=null			//pointless, but shows how to use null block
	*					implement CTMEnd
	*				endstruct
	*
	*				set MyTimer.create(5).myValue=16 //will display "My value is 16" in 5 seconds
	*
	************************************************************************************/
	globals
		private timer array st		//segment timers
		private integer ic=0		//instance count

		private integer array sn	//segment next
		private integer array sp	//segment previous
		private boolean array sr	//segment running
		private integer array sf	//segment first
		private integer array tn	//timer next, list recycler
		private integer array tp	//timer previous
		private integer array tf	//timer first
		private integer array nh	//node head

		private real array nt		//node timeout

		private integer array qt	//queue->timeout id, segment->handle id

		private integer array an	//after next
		private integer array ap	//after prev
		private integer array as	//after segment

		private Table array tl
		private hashtable tt=InitHashtable()
	endglobals
	//hiveworkshop.com/forums/jass-functions-413/snippet-natural-logarithm-108059/
	//credits to BlinkBoy
	private function Ln takes real a returns real
		local real s=0
		loop
		exitwhen a<2.71828
		set a=a/2.71828
		set s=s+1
		endloop
		return s+(a-1)*(1+8/(1+a)+1/a)/6
	endfunction
	//! textmacro TIMER_TOOLS_ALLOCATE_TIMER takes TIMER_REF, TIMEOUT_REF, TABLE_REF
		if (0==tn[0]) then
			set $TIMER_REF$=ic+1
			set ic=$TIMER_REF$
		else
			set $TIMER_REF$=tn[0]
			set tn[0]=tn[$TIMER_REF$]
			set nh[$TIMER_REF$]=0
		endif
		set nt[$TIMER_REF$]=$TIMEOUT_REF$
		set tl[$TIMER_REF$]=$TABLE_REF$
	//! endtextmacro
	//! textmacro TIMER_TOOLS_GET_TIMER_QUEUE takes THIS_REF, QUEUE_REF, ID_REF
		set $QUEUE_REF$=$THIS_REF$[$ID_REF$]
		if (0==$QUEUE_REF$) then
			if (0==tn[0]) then
				set $QUEUE_REF$=ic+1
				set ic=$QUEUE_REF$
			else
				set $QUEUE_REF$=tn[0]
				set tn[0]=tn[$QUEUE_REF$]
			endif
			set sf[$QUEUE_REF$]=0
			set nt[$QUEUE_REF$]=to
			set $THIS_REF$[$ID_REF$]=$QUEUE_REF$
			set qt[$QUEUE_REF$]=$ID_REF$
			set tl[$QUEUE_REF$]=$THIS_REF$
		endif
	//! endtextmacro
	//! textmacro TIMER_TOOLS_PREPARE_TIMEOUT takes REF_TIMEOUT, ID_REF
		set $REF_TIMEOUT$=RELATIVE_ACCURACY/R2I(RELATIVE_ACCURACY/$REF_TIMEOUT$+.5)
		set $ID_REF$=R2I($REF_TIMEOUT$*CONSTANT_ACCURACY+.5)
		if (0==$ID_REF$) then
			set $ID_REF$=1
		endif
		set $REF_TIMEOUT$=$ID_REF$/CONSTANT_ACCURACY
	//! endtextmacro
	//! textmacro TIMER_TOOLS_PREPARE_TICK takes TIMEOUT_REF, TICK_LENIANCY_REF
		set $TICK_LENIANCY_REF$=Ln(RELATIVE_MERGE/$TIMEOUT_REF$)/230.258509*$TIMEOUT_REF$+CONSTANT_MERGE
	//! endtextmacro
	//! textmacro TIMER_TOOLS_CANT_MERGE_RIGHT takes QUEUE_REF, SEGMENT_REF, TICK_LENIANCY_REF, REMAINING_TIME_REF, TIMEOUT_REF
		set $SEGMENT_REF$=sp[sf[$QUEUE_REF$]]
		set $REMAINING_TIME_REF$=TimerGetRemaining(st[$SEGMENT_REF$])
		if (not sr[$SEGMENT_REF$] or 0<$TIMEOUT_REF$-$REMAINING_TIME_REF$-$TICK_LENIANCY_REF$) then
	//! endtextmacro
	//! textmacro TIMER_TOOLS_CAN_MERGE_LEFT takes QUEUE_REF, SEGMENT_REF, TICK_LENIANCY_REF, REMAINING_TIME_REF, TIMEOUT_REF
		set $SEGMENT_REF$=sf[$QUEUE_REF$]
		set $REMAINING_TIME_REF$=TimerGetRemaining(st[$SEGMENT_REF$])
		if (0!=$SEGMENT_REF$ and 0>=$REMAINING_TIME_REF$-$TICK_LENIANCY_REF$) then
	//! endtextmacro
	//! textmacro TIMER_TOOLS_ALLOCATE_SEGMENT takes QUEUE_REF, SEGMENT_REF, TIMEOUT_REF, CODE_REF, SEGMENT_FIRST
		if (0==tn[0]) then
			set $SEGMENT_REF$=ic+1
			set ic=$SEGMENT_REF$
		else
			set $SEGMENT_REF$=tn[0]
			set tn[0]=tn[$SEGMENT_REF$]
		endif
		set $SEGMENT_FIRST$=sf[$QUEUE_REF$]
		if (0==$SEGMENT_FIRST$) then
			set sn[$SEGMENT_REF$]=$SEGMENT_REF$
			set sp[$SEGMENT_REF$]=$SEGMENT_REF$
			set sf[$QUEUE_REF$]=$SEGMENT_REF$
		else
			set sp[$SEGMENT_REF$]=sp[$SEGMENT_FIRST$]
			set sn[$SEGMENT_REF$]=$SEGMENT_FIRST$
			set sn[sp[$SEGMENT_FIRST$]]=$SEGMENT_REF$
			set sp[$SEGMENT_FIRST$]=$SEGMENT_REF$
		endif
		set tf[$SEGMENT_REF$]=0
		set st[$SEGMENT_REF$]=CreateTimer()
		set nt[$SEGMENT_REF$]=$TIMEOUT_REF$
		call TimerStart(st[$SEGMENT_REF$],$TIMEOUT_REF$,true,$CODE_REF$)
		set sr[$SEGMENT_REF$]=true
		set an[$SEGMENT_REF$]=$SEGMENT_REF$
		set ap[$SEGMENT_REF$]=$SEGMENT_REF$
	//! endtextmacro
	//! textmacro TIMER_TOOLS_ATTACH_SEGMENT takes QUEUE_REF, SEGMENT_REF
		set nh[$SEGMENT_REF$]=$QUEUE_REF$
		set qt[$SEGMENT_REF$]=-GetHandleId(st[$SEGMENT_REF$])
		call SaveInteger(tt,qt[$SEGMENT_REF$],0,$QUEUE_REF$)
	//! endtextmacro
	//! textmacro TIMER_TOOLS_PUSH_AFTER takes SEGMENT_REF, SEGMENT_REF_TARGET, TIMER_REF
		if ($SEGMENT_REF_TARGET$!=nh[$TIMER_REF$]) then
			set an[$TIMER_REF$]=an[$SEGMENT_REF$]
			set ap[$TIMER_REF$]=$SEGMENT_REF$
			set ap[an[$SEGMENT_REF$]]=$TIMER_REF$
			set an[$SEGMENT_REF$]=$TIMER_REF$
			set as[$TIMER_REF$]=$SEGMENT_REF_TARGET$
		endif
	//! endtextmacro
	//! textmacro TIMER_TOOLS_POP_AFTER takes SEGMENT_REF, TIMER_REF
		set an[ap[$TIMER_REF$]]=an[$TIMER_REF$]
		set ap[an[$TIMER_REF$]]=ap[$TIMER_REF$]
		set $SEGMENT_REF$=as[$TIMER_REF$]
		set as[$TIMER_REF$]=0
	//! endtextmacro
	//! textmacro TIMER_TOOLS_IS_AFTER takes TIMER_REF
		if (0!=as[$TIMER_REF$]) then
	//! endtextmacro
	//! textmacro TIMER_TOOLS_ADD_TIMER_TO_SEGMENT takes SEGMENT_REF, TIMER_REF
		set tn[$TIMER_REF$]=tf[$SEGMENT_REF$]
		set tp[tf[$SEGMENT_REF$]]=$TIMER_REF$
		set tp[$TIMER_REF$]=0
		set tf[$SEGMENT_REF$]=$TIMER_REF$
		set nh[$TIMER_REF$]=$SEGMENT_REF$
	//! endtextmacro
	//! textmacro TIMER_TOOLS_IS_ALLOCATED takes TIMER_REF, ERROR_MESSAGE
		if (0==nt[$TIMER_REF$]) then
			call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,60,"$ERROR_MESSAGE$")
		else
	//! endtextmacro
	//! textmacro TIMER_TOOLS_IS_NOT_PAUSED takes SEGMENT_REF
		if (0!=$SEGMENT_REF$) then
	//! endtextmacro
	//! textmacro TIMER_TOOLS_IS_PAUSED takes SEGMENT_REF
		if (0==$SEGMENT_REF$) then
	//! endtextmacro
	//! textmacro TIMER_TOOLS_DEL takes THIS
		set tn[$THIS$]=tn[0]
		set tn[0]=$THIS$
	//! endtextmacro
	//! textmacro TIMER_TOOLS_IS_SEGMENT_RUNNING takes SEGMENT_REF, TIMER_REF
		if (sr[$SEGMENT_REF$]) then
	//! endtextmacro
	//! textmacro TIMER_TOOLS_REMOVE_FROM_SEGMENT takes SEGMENT_REF, TIMER_REF
		if (0==tp[$TIMER_REF$]) then
			set tf[$SEGMENT_REF$]=tn[$TIMER_REF$]
			set tp[tn[$TIMER_REF$]]=0
		else
			set tp[tn[$TIMER_REF$]]=tp[$TIMER_REF$]
			set tn[tp[$TIMER_REF$]]=tn[$TIMER_REF$]
		endif
		set nh[$TIMER_REF$]=0
	//! endtextmacro
	//! textmacro TIMER_TOOLS_IS_SEGMENT_EMPTY takes SEGMENT_REF
		if (sr[$SEGMENT_REF$] and 0==tf[$SEGMENT_REF$] and $SEGMENT_REF$==an[$SEGMENT_REF$]) then
	//! endtextmacro
	//! textmacro TIMER_TOOLS_DEL_SEGMENT takes QUEUE_REF, SEGMENT_REF
		if ($SEGMENT_REF$==sf[$QUEUE_REF$]) then
			if ($SEGMENT_REF$==sn[$SEGMENT_REF$]) then
				set sf[$QUEUE_REF$]=0
			else
				set sf[$QUEUE_REF$]=sn[$SEGMENT_REF$]
				set sn[sp[$SEGMENT_REF$]]=sn[$SEGMENT_REF$]
				set sp[sn[$SEGMENT_REF$]]=sp[$SEGMENT_REF$]
			endif
		else
			set sn[sp[$SEGMENT_REF$]]=sn[$SEGMENT_REF$]
			set sp[sn[$SEGMENT_REF$]]=sp[$SEGMENT_REF$]
		endif
		set tn[$SEGMENT_REF$]=tn[0]
		set tn[0]=$SEGMENT_REF$
		call PauseTimer(st[$SEGMENT_REF$])
		call DestroyTimer(st[$SEGMENT_REF$])
		set st[$SEGMENT_REF$]=null
	//! endtextmacro
	//! textmacro TIMER_TOOLS_IS_QUEUE_EMPTY takes QUEUE_REF
		if (0==sf[$QUEUE_REF$]) then
	//! endtextmacro
	//! textmacro TIMER_TOOLS_UNATTACH takes THIS, REF
		call $THIS$.remove(qt[$REF$])
	//! endtextmacro
	//! textmacro TIMER_TOOLS_UNATTACH2 takes REF
		call RemoveSavedInteger(tt,qt[$REF$],0)
	//! endtextmacro
	//! textmacro TIMER_TOOLS_IS_NEW_TIMEOUT takes TIMEOUT_REF, NEW_TIMEOUT
		if ($NEW_TIMEOUT$!=$TIMEOUT_REF$ and $NEW_TIMEOUT$>0) then
	//! endtextmacro
	//! textmacro TIMER_TOOLS_GET_NODE_HEAD takes HEAD_REF, NODE_REF
		set $HEAD_REF$=nh[$NODE_REF$]
	//! endtextmacro
	//! textmacro TIMER_TOOLS_SET_TIMER_RUNNING_FLAG takes SEGMENT, FLAG
		set sr[$SEGMENT$]=$FLAG$
	//! endtextmacro
	//! textmacro TIMER_TOOLS_PREPARE_TIMER takes QUEUE, SEGMENT
		set sf[$QUEUE$]=sn[$SEGMENT$]
	//! endtextmacro
	//! textmacro TIMER_TOOLS_RUN_AFTER_QUEUE takes SEGMENT2, TIMER, TIMER2
		set $TIMER$=an[$SEGMENT2$]
		set an[$SEGMENT2$]=$SEGMENT2$
		loop
			set $TIMER2$=an[$TIMER$]
	//! endtextmacro
	//! textmacro TIMER_TOOLS_IS_DESTROYING takes TIMER
			if (0==nt[$TIMER$]) then
	//! endtextmacro
	//! textmacro TIMER_TOOLS_IS_PAUSING takes TIMER
			if (0==as[$TIMER$]) then
	//! endtextmacro
	//! textmacro TIMER_TOOLS_IS_ADDING takes SEGMENT2, TIMER
			elseif ($SEGMENT2$==as[$TIMER$]) then
				set as[$TIMER$]=0
	//! endtextmacro
	//! textmacro TIMER_TOOLS_IS_MOVING takes TIMER
			else
				set as[$TIMER$]=0
	//! endtextmacro
	//! textmacro TIMER_TOOLS_GET_NEXT_AFTER takes SEGMENT, SEGMENT2, TIMER
			endif
			set $TIMER$=$TIMER$2
			exitwhen $SEGMENT2$==$TIMER$
		endloop
	//! endtextmacro
	private function CQ takes integer f, real to, code c returns integer
		local integer t
		local real l=Ln(RELATIVE_MERGE/to)/230.258509*to+CONSTANT_MERGE
		local integer s=sp[f]
		local real x
		if (0==tn[0]) then
			set t=ic+1
			set ic=t
		else
			set t=tn[0]
			set tn[0]=tn[t]
			set nh[t]=0
		endif
		set nt[t]=to
		set x=TimerGetRemaining(st<s>)
		if (not sr<s> or 0&lt;to-x-l) then
			set x=TimerGetRemaining(st[f])
			if (0!=f and 0&gt;=x-l) then
				//! runtextmacro TIMER_TOOLS_PUSH_AFTER(&quot;f&quot;,&quot;f&quot;,&quot;t&quot;)
			else
				if (0==tn[0]) then
					set s=ic+1
					set ic=s
				else
					set s=tn[0]
					set tn[0]=tn<s>
				endif
				if (0==f) then
					set sn<s>=s
					set sp<s>=s
				else
					set sp<s>=sp[f]
					set sn<s>=f
					set sn[sp[f]]=s
					set sp[f]=s
				endif
				set tn[t]=0
				set tp[t]=0
				set tf<s>=t
				set nh[t]=s
				set st<s>=CreateTimer()
				call TimerStart(st<s>,to,true,c)
				set sr<s>=true
				set an<s>=s
				set ap<s>=s
			endif
		else
			//! runtextmacro TIMER_TOOLS_ADD_TIMER_TO_SEGMENT(&quot;s&quot;,&quot;t&quot;)
		endif
		return t
	endfunction
	private function DT takes integer t, integer f returns integer
		local integer s
		//! runtextmacro TIMER_TOOLS_IS_ALLOCATED(&quot;t&quot;,&quot;TIMER ERROR: ATTEMPTED TO DESTROY NULL TIMER&quot;)
			set nt[t]=0
			//! runtextmacro TIMER_TOOLS_IS_AFTER(&quot;t&quot;)
				//! runtextmacro TIMER_TOOLS_POP_AFTER(&quot;s&quot;,&quot;t&quot;)
				//! runtextmacro TIMER_TOOLS_IS_SEGMENT_EMPTY(&quot;s&quot;)
					if (s==f) then
						if (s==sn<s>) then
							set f=0
						else
							set f=sn<s>
							set sn[sp<s>]=sn<s>
							set sp[sn<s>]=sp<s>
						endif
					else
						set sn[sp<s>]=sn<s>
						set sp[sn<s>]=sp<s>
					endif
					set tn<s>=tn[0]
					set tn[0]=s
					call PauseTimer(st<s>)
					call DestroyTimer(st<s>)
					set st<s>=null
				endif
			endif
			//! runtextmacro TIMER_TOOLS_GET_NODE_HEAD(&quot;s&quot;,&quot;t&quot;)
			//! runtextmacro TIMER_TOOLS_IS_PAUSED(&quot;s&quot;)
				//! runtextmacro TIMER_TOOLS_DEL(&quot;t&quot;)
			else
				//! runtextmacro TIMER_TOOLS_IS_SEGMENT_RUNNING(&quot;s&quot;,&quot;t&quot;)
					//! runtextmacro TIMER_TOOLS_REMOVE_FROM_SEGMENT(&quot;s&quot;,&quot;t&quot;)
					//! runtextmacro TIMER_TOOLS_DEL(&quot;t&quot;)
					//! runtextmacro TIMER_TOOLS_IS_SEGMENT_EMPTY(&quot;s&quot;)
						if (s==f) then
							if (s==sn<s>) then
								set f=0
							else
								set f=sn<s>
								set sn[sp<s>]=sn<s>
								set sp[sn<s>]=sp<s>
							endif
						else
							set sn[sp<s>]=sn<s>
							set sp[sn<s>]=sp<s>
						endif
						set tn<s>=tn[0]
						set tn[0]=s
						call PauseTimer(st<s>)
						call DestroyTimer(st<s>)
						set st<s>=null
					endif
				else
					//! runtextmacro TIMER_TOOLS_PUSH_AFTER(&quot;s&quot;,&quot;0&quot;,&quot;t&quot;)
				endif
			endif
		endif
		return f
	endfunction
	private keyword f
	private keyword c
	private keyword e
	private keyword s
	module CTQ
		static integer f=0
		static code c
		static method create takes nothing returns thistype
			local integer t=CQ(f,TIMEOUT,c)
			if (0==f) then
				set f=nh[t]
			endif
			return t
		endmethod
		method destroy takes nothing returns nothing
			set f=DT(this,f)
		endmethod
		method operator elapsed takes nothing returns real
			return TimerGetElapsed(st[nh[this]])
		endmethod
		method operator remaining takes nothing returns real
			return TimerGetRemaining(st[nh[this]])
		endmethod
		static method e takes nothing returns nothing
			local integer s=f
			local thistype this=tf<s>
	endmodule
	module CTQExpire
			set sr<s>=false
			set f=sn<s>
			loop
				exitwhen 0==this
	endmodule
	module CTQNull
				set this=tn[this]
			endloop
	endmodule
	module CTQEnd
			set this=an<s>
			set an<s>=s
			loop
				exitwhen s==this
				//! runtextmacro TIMER_TOOLS_IS_PAUSING(&quot;this&quot;)
					//! runtextmacro TIMER_TOOLS_REMOVE_FROM_SEGMENT(&quot;s&quot;,&quot;this&quot;)
					//! runtextmacro TIMER_TOOLS_IS_DESTROYING(&quot;this&quot;)
						//! runtextmacro TIMER_TOOLS_DEL(&quot;this&quot;)
					endif
				else
					set as[this]=0
					//! runtextmacro TIMER_TOOLS_ADD_TIMER_TO_SEGMENT(&quot;s&quot;,&quot;this&quot;)
				endif
				set this=an[this]
			endloop
			if (0==tf<s>) then
				if (s==f) then
					if (s==sn<s>) then
						set f=0
					else
						set f=sn<s>
						set sn[sp<s>]=sn<s>
						set sp[sn<s>]=sp<s>
					endif
				else
					set sn[sp<s>]=sn<s>
					set sp[sn<s>]=sp<s>
				endif
				set tn<s>=tn[0]
				set tn[0]=s
				call PauseTimer(st<s>)
				call DestroyTimer(st<s>)
				set st<s>=null
			else
				set sr<s>=true
			endif
		endmethod
		private static method onInit takes nothing returns nothing
			set c=function thistype.e
		endmethod
	endmodule
	private function LCQ takes integer f, real to, code c returns integer
		local integer t
		if (0==tn[0]) then
			set t=ic+1
			set ic=t
		else
			set t=tn[0]
			set tn[0]=tn[t]
		endif
		if (0==f) then
			set f=t
			set tn[t]=0
			set tp[t]=t
		else
			set tn[t]=0
			set tp[t]=tp[f]
			set tn[tp[f]]=t
			set tp[f]=t
		endif
		set st[t]=CreateTimer()
		call TimerStart(st[t],to,false,c)
		return t
	endfunction
	module CLTQ1
		static integer f=0
		static code c
		static method create takes nothing returns thistype
			local integer t=LCQ(f,TIMEOUT,c)
			if (0==f) then
				set f=t
			endif
			return t
		endmethod
		method destroy takes nothing returns nothing
			set tn[tp[this]]=tn[this]
			set tp[tn[this]]=tp[this]
			if (this==f) then
				set f=tn[this]
			endif
			set tn[this]=tn[0]
			set tn[0]=this
			call PauseTimer(st[this])
			call DestroyTimer(st[this])
			set st[this]=null
		endmethod
		static method e takes nothing returns nothing
			local thistype this=f
	endmodule
	module CLTQ1End
			set f=tn[this]
			set tn[tp[this]]=tn[this]
			set tp[tn[this]]=tp[this]
			set tn[this]=tn[0]
			set tn[0]=this
			call DestroyTimer(st[this])
			set st[this]=null
		endmethod
		private static method onInit takes nothing returns nothing
			set c=function thistype.e
		endmethod
	endmodule
	private struct Mt extends array
		private method operator [] takes integer i returns thistype
			return Table(this)<i>
		endmethod
		private method operator []= takes integer i, integer v returns nothing
			set Table(this)<i>=v
		endmethod
		private method remove takes integer i returns nothing
			call Table(this).remove(i)
		endmethod
		method operator timeout takes nothing returns real
			return nt[this]
		endmethod
		method operator elapsed takes nothing returns real
			return TimerGetElapsed(st[nh[this]])
		endmethod
		method operator remaining takes nothing returns real
			return TimerGetRemaining(st[nh[this]])
		endmethod
		method setTimeout takes real to, code c returns nothing
			local integer t=this
			local integer s
			local integer h
			local integer i
			local real x
			local real l
			local boolean b=true
			set this=tl[t]
			//! runtextmacro TIMER_TOOLS_IS_ALLOCATED(&quot;t&quot;,&quot;TIMER ERROR: ATTEMPTED TO MANIPULATE NULL TIMER&quot;)
				//! runtextmacro TIMER_TOOLS_PREPARE_TIMEOUT(&quot;to&quot;,&quot;i&quot;)
				//! runtextmacro TIMER_TOOLS_IS_NEW_TIMEOUT(&quot;nt[t]&quot;,&quot;to&quot;)
					set nt[t]=to
					//! runtextmacro TIMER_TOOLS_IS_AFTER(&quot;t&quot;)
						//! runtextmacro TIMER_TOOLS_POP_AFTER(&quot;s&quot;,&quot;t&quot;)
						//! runtextmacro TIMER_TOOLS_IS_SEGMENT_EMPTY(&quot;s&quot;)
						//! runtextmacro TIMER_TOOLS_GET_NODE_HEAD(&quot;h&quot;,&quot;s&quot;)
							//! runtextmacro TIMER_TOOLS_DEL_SEGMENT(&quot;h&quot;,&quot;s&quot;)
							//! runtextmacro TIMER_TOOLS_UNATTACH2(&quot;s&quot;)
							//! runtextmacro TIMER_TOOLS_IS_QUEUE_EMPTY(&quot;h&quot;)
								//! runtextmacro TIMER_TOOLS_DEL(&quot;h&quot;)
								//! runtextmacro TIMER_TOOLS_UNATTACH(&quot;this&quot;,&quot;h&quot;)
							endif
						endif
					endif
					//! runtextmacro TIMER_TOOLS_GET_NODE_HEAD(&quot;s&quot;,&quot;t&quot;)
					//! runtextmacro TIMER_TOOLS_IS_NOT_PAUSED(&quot;s&quot;)
						//! runtextmacro TIMER_TOOLS_IS_SEGMENT_RUNNING(&quot;s&quot;,&quot;t&quot;)
							//! runtextmacro TIMER_TOOLS_REMOVE_FROM_SEGMENT(&quot;s&quot;,&quot;t&quot;)
							//! runtextmacro TIMER_TOOLS_IS_SEGMENT_EMPTY(&quot;s&quot;)
								//! runtextmacro TIMER_TOOLS_GET_NODE_HEAD(&quot;h&quot;,&quot;s&quot;)
								//! runtextmacro TIMER_TOOLS_DEL_SEGMENT(&quot;h&quot;,&quot;s&quot;)
								//! runtextmacro TIMER_TOOLS_UNATTACH2(&quot;s&quot;)
								//! runtextmacro TIMER_TOOLS_IS_QUEUE_EMPTY(&quot;h&quot;)
									//! runtextmacro TIMER_TOOLS_DEL(&quot;h&quot;)
									//! runtextmacro TIMER_TOOLS_UNATTACH(&quot;this&quot;,&quot;h&quot;)
								endif
							endif
						else
							//! runtextmacro TIMER_TOOLS_PUSH_AFTER(&quot;s&quot;,&quot;-1&quot;,&quot;t&quot;)
							set b=false
						endif
					endif
					if (b) then
						//! runtextmacro TIMER_TOOLS_PREPARE_TICK(&quot;to&quot;,&quot;l&quot;)
						//! runtextmacro TIMER_TOOLS_GET_TIMER_QUEUE(&quot;this&quot;,&quot;h&quot;,&quot;i&quot;)
						//! runtextmacro TIMER_TOOLS_CANT_MERGE_RIGHT(&quot;h&quot;,&quot;s&quot;,&quot;l&quot;,&quot;x&quot;,&quot;to&quot;)
							//! runtextmacro TIMER_TOOLS_CAN_MERGE_LEFT(&quot;h&quot;,&quot;s&quot;,&quot;l&quot;,&quot;x&quot;,&quot;to&quot;)
								//! runtextmacro TIMER_TOOLS_PUSH_AFTER(&quot;s&quot;,&quot;s&quot;,&quot;t&quot;)
							else
								//! runtextmacro TIMER_TOOLS_ALLOCATE_SEGMENT(&quot;h&quot;,&quot;s&quot;,&quot;to&quot;,&quot;c&quot;,&quot;i&quot;)
								//! runtextmacro TIMER_TOOLS_ADD_TIMER_TO_SEGMENT(&quot;s&quot;,&quot;t&quot;)
								//! runtextmacro TIMER_TOOLS_ATTACH_SEGMENT(&quot;h&quot;,&quot;s&quot;)
							endif
						else
							//! runtextmacro TIMER_TOOLS_ADD_TIMER_TO_SEGMENT(&quot;s&quot;,&quot;t&quot;)
						endif
					endif
				endif
			endif
		endmethod
		method new takes real to, code c returns thistype
			local integer t
			local integer i
			local integer h
			local integer s
			local real x
			local real l
			//! runtextmacro TIMER_TOOLS_PREPARE_TIMEOUT(&quot;to&quot;,&quot;i&quot;)
			//! runtextmacro TIMER_TOOLS_PREPARE_TICK(&quot;to&quot;,&quot;l&quot;)
			//! runtextmacro TIMER_TOOLS_ALLOCATE_TIMER(&quot;t&quot;,&quot;to&quot;,&quot;this&quot;)
			//! runtextmacro TIMER_TOOLS_GET_TIMER_QUEUE(&quot;this&quot;,&quot;h&quot;,&quot;i&quot;)
			//! runtextmacro TIMER_TOOLS_CANT_MERGE_RIGHT(&quot;h&quot;,&quot;s&quot;,&quot;l&quot;,&quot;x&quot;,&quot;to&quot;)
				//! runtextmacro TIMER_TOOLS_CAN_MERGE_LEFT(&quot;h&quot;,&quot;s&quot;,&quot;l&quot;,&quot;x&quot;,&quot;to&quot;)
					//! runtextmacro TIMER_TOOLS_PUSH_AFTER(&quot;s&quot;,&quot;s&quot;,&quot;t&quot;)
				else
					//! runtextmacro TIMER_TOOLS_ALLOCATE_SEGMENT(&quot;h&quot;,&quot;s&quot;,&quot;to&quot;,&quot;c&quot;,&quot;i&quot;)
					//! runtextmacro TIMER_TOOLS_ADD_TIMER_TO_SEGMENT(&quot;s&quot;,&quot;t&quot;)
					//! runtextmacro TIMER_TOOLS_ATTACH_SEGMENT(&quot;h&quot;,&quot;s&quot;)
				endif
			else
				//! runtextmacro TIMER_TOOLS_ADD_TIMER_TO_SEGMENT(&quot;s&quot;,&quot;t&quot;)
			endif
			return t
		endmethod
		method destroy takes nothing returns nothing
			local integer t=this
			local integer s
			local integer h
			set this=tl[t]
			//! runtextmacro TIMER_TOOLS_IS_ALLOCATED(&quot;t&quot;,&quot;TIMER ERROR: ATTEMPTED TO DESTROY NULL TIMER&quot;)
				set nt[t]=0
				//! runtextmacro TIMER_TOOLS_IS_AFTER(&quot;t&quot;)
					//! runtextmacro TIMER_TOOLS_POP_AFTER(&quot;s&quot;,&quot;t&quot;)
					//! runtextmacro TIMER_TOOLS_IS_SEGMENT_EMPTY(&quot;s&quot;)
						//! runtextmacro TIMER_TOOLS_GET_NODE_HEAD(&quot;h&quot;,&quot;s&quot;)
						//! runtextmacro TIMER_TOOLS_DEL_SEGMENT(&quot;h&quot;,&quot;s&quot;)
						//! runtextmacro TIMER_TOOLS_UNATTACH2(&quot;s&quot;)
						//! runtextmacro TIMER_TOOLS_IS_QUEUE_EMPTY(&quot;h&quot;)
							//! runtextmacro TIMER_TOOLS_DEL(&quot;h&quot;)
							//! runtextmacro TIMER_TOOLS_UNATTACH(&quot;this&quot;,&quot;h&quot;)
						endif
					endif
				endif
				//! runtextmacro TIMER_TOOLS_GET_NODE_HEAD(&quot;s&quot;,&quot;t&quot;)
				//! runtextmacro TIMER_TOOLS_IS_PAUSED(&quot;s&quot;)
					//! runtextmacro TIMER_TOOLS_DEL(&quot;t&quot;)
				else
					//! runtextmacro TIMER_TOOLS_IS_SEGMENT_RUNNING(&quot;s&quot;,&quot;t&quot;)
						//! runtextmacro TIMER_TOOLS_REMOVE_FROM_SEGMENT(&quot;s&quot;,&quot;t&quot;)
						//! runtextmacro TIMER_TOOLS_DEL(&quot;t&quot;)
						//! runtextmacro TIMER_TOOLS_IS_SEGMENT_EMPTY(&quot;s&quot;)
							//! runtextmacro TIMER_TOOLS_GET_NODE_HEAD(&quot;h&quot;,&quot;s&quot;)
							//! runtextmacro TIMER_TOOLS_DEL_SEGMENT(&quot;h&quot;,&quot;s&quot;)
							//! runtextmacro TIMER_TOOLS_UNATTACH2(&quot;s&quot;)
							//! runtextmacro TIMER_TOOLS_IS_QUEUE_EMPTY(&quot;h&quot;)
								//! runtextmacro TIMER_TOOLS_DEL(&quot;h&quot;)
								//! runtextmacro TIMER_TOOLS_UNATTACH(&quot;this&quot;,&quot;h&quot;)
							endif
						endif
					else
						//! runtextmacro TIMER_TOOLS_PUSH_AFTER(&quot;s&quot;,&quot;0&quot;,&quot;t&quot;)
					endif
				endif
			endif
		endmethod
	endstruct
	private function MTM takes integer h, integer s, integer t, code c returns nothing
		local integer s2=s
		local integer t2
		local Table this=tl[t]
		local real l
		local real x
		local real to
		local integer i
		//! runtextmacro TIMER_TOOLS_RUN_AFTER_QUEUE(&quot;s2&quot;,&quot;t&quot;,&quot;t2&quot;)
			//! runtextmacro TIMER_TOOLS_IS_PAUSING(&quot;t&quot;)
				//! runtextmacro TIMER_TOOLS_REMOVE_FROM_SEGMENT(&quot;s2&quot;,&quot;t&quot;)
				//! runtextmacro TIMER_TOOLS_IS_DESTROYING(&quot;t&quot;)
					//! runtextmacro TIMER_TOOLS_DEL(&quot;t&quot;)
				endif
			//! runtextmacro TIMER_TOOLS_IS_ADDING(&quot;s2&quot;,&quot;t&quot;)
				//! runtextmacro TIMER_TOOLS_ADD_TIMER_TO_SEGMENT(&quot;s2&quot;,&quot;t&quot;)
			//! runtextmacro TIMER_TOOLS_IS_MOVING(&quot;t&quot;)
				set to=nt[t]
				set i=R2I(to/CONSTANT_ACCURACY+.5)
				//! runtextmacro TIMER_TOOLS_REMOVE_FROM_SEGMENT(&quot;s2&quot;,&quot;t&quot;)
				//! runtextmacro TIMER_TOOLS_PREPARE_TICK(&quot;to&quot;,&quot;l&quot;)
				//! runtextmacro TIMER_TOOLS_GET_TIMER_QUEUE(&quot;this&quot;,&quot;h&quot;,&quot;i&quot;)
				//! runtextmacro TIMER_TOOLS_CANT_MERGE_RIGHT(&quot;h&quot;,&quot;s&quot;,&quot;l&quot;,&quot;x&quot;,&quot;to&quot;)
					//! runtextmacro TIMER_TOOLS_CAN_MERGE_LEFT(&quot;h&quot;,&quot;s&quot;,&quot;l&quot;,&quot;x&quot;,&quot;to&quot;)
						//! runtextmacro TIMER_TOOLS_PUSH_AFTER(&quot;s&quot;,&quot;s&quot;,&quot;t&quot;)
					else
						//! runtextmacro TIMER_TOOLS_ALLOCATE_SEGMENT(&quot;h&quot;,&quot;s&quot;,&quot;to&quot;,&quot;c&quot;,&quot;i&quot;)
						//! runtextmacro TIMER_TOOLS_ADD_TIMER_TO_SEGMENT(&quot;s&quot;,&quot;t&quot;)
						//! runtextmacro TIMER_TOOLS_ATTACH_SEGMENT(&quot;h&quot;,&quot;s&quot;)
					endif
				else
					//! runtextmacro TIMER_TOOLS_ADD_TIMER_TO_SEGMENT(&quot;s&quot;,&quot;t&quot;)
				endif
			//! runtextmacro TIMER_TOOLS_GET_NEXT_AFTER(&quot;s&quot;,&quot;s2&quot;,&quot;t&quot;)
	endfunction
	module CTM
		static Mt f
		static code c
		static method create takes real to returns thistype
			return f.new(to,c)
		endmethod
		method destroy takes nothing returns nothing
			call Mt(this).destroy()
		endmethod
		method operator timeout takes nothing returns real
			return Mt(this).timeout
		endmethod
		method operator timeout= takes real to returns nothing
			call Mt(this).setTimeout(to,c)
		endmethod
		static method e takes nothing returns nothing
			local integer s=LoadInteger(tt,-GetHandleId(GetExpiredTimer()),0)
			local thistype this=sf<s>
	endmodule
	module CTMExpire
			//! runtextmacro TIMER_TOOLS_SET_TIMER_RUNNING_FLAG(&quot;this&quot;,&quot;false&quot;)
			//! runtextmacro TIMER_TOOLS_PREPARE_TIMER(&quot;s&quot;,&quot;this&quot;)
			set s=this
			set this=tf[this]
			loop
				exitwhen 0==this
	endmodule
	module CTMNull
				set this=tn[this]
			endloop
	endmodule
	module CTMEnd
			if (s!=an<s>) then
				call MTM(nh<s>,s,tf<s>,function thistype.e)
			endif
			if (0==tf<s>) then
				//! runtextmacro TIMER_TOOLS_GET_NODE_HEAD(&quot;this&quot;,&quot;s&quot;)
				//! runtextmacro TIMER_TOOLS_DEL_SEGMENT(&quot;this&quot;,&quot;s&quot;)
				//! runtextmacro TIMER_TOOLS_UNATTACH2(&quot;s&quot;)
				//! runtextmacro TIMER_TOOLS_IS_QUEUE_EMPTY(&quot;this&quot;)
					//! runtextmacro TIMER_TOOLS_DEL(&quot;this&quot;)
					//! runtextmacro TIMER_TOOLS_UNATTACH(&quot;tl[this]&quot;,&quot;this&quot;)
				endif
			else
				//! runtextmacro TIMER_TOOLS_SET_TIMER_RUNNING_FLAG(&quot;s&quot;,&quot;true&quot;)
			endif
		endmethod
		private static method onInit takes nothing returns nothing
			set f=Table.create()
			set c=function thistype.e
		endmethod
	endmodule
endlibrary
</s></s></s></s></s></i></i></s></s></s></s></s></s></s></s></s></s></s></s></s></s></s></s></s></s></s></s></s></s></s></s></s></s></s></s></s></s></s></s></s></s></s></s></s></s></s></s></s></s></s></s></s></s></s></s></s></s></s></s></s></s></s></s></s></s></s></s></s></s>


Use all of the below for data attachment purposes only

Constant Light Timer Queue 1 Demo
Use when dealing with constant timeout 1 shot timers with constant methods of timeouts that are 2+. Usually used for expiring effects. Essentially use this when there is a low chance for merges between timers (perhaps highly infrequent timers).
JASS:

struct MyLightTimerQueue1 extends array
    private static constant real TIMEOUT=2
    readonly integer value
    implement CLTQ1
        local string s=&quot;My Value: &quot;
        call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,60,s+I2S(value))
    implement CLTQ1End
    private static method onInit takes nothing returns nothing
        set create().value=1
    endmethod
endstruct


Constant Timer Queue Demo
Use when dealing with constant repeating timeouts.
JASS:

struct MyTimerQueue extends array
    private static constant real TIMEOUT=2
    readonly integer value
    implement CTQ
        local string s=&quot;My Value: &quot;
    implement CTQExpire
        call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,60,s+I2S(value))
    implement CTQNull
        set s=null //pointless, but shows how to use null block
    implement CTQEnd
    private static method onInit takes nothing returns nothing
        set create().value=1
    endmethod
endstruct


Constant Timer Method Demo
Use when dealing with repeating timers of an unknown timeout with a constant method
JASS:

struct MyTimerMethod extends array
    readonly integer value
    implement CTM
        local string s=&quot;My Value: &quot;
    implement CTMExpire
        call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,60,s+I2S(value))
    implement CTMNull
        set s=null //pointless, but shows how to use null block
    implement CTMEnd
    private static method onInit takes nothing returns nothing
        set create(2).value=1
    endmethod
endstruct


In cases where the method is completely unknown, use regular timer data attachment. 1 Shot timers can run through Constant Timer Queues/Consant Timer Methods if there are many one shot timers of the same timeouts, otherwise the overhead on creation/destruction isn't worth it.

Light Timer Queue 1 will always be worth it for 1 shot timers when Constant Timer Queue isn't (perhaps infrequent timers, high period, essentially low chance of merge).
 

Laiev

Hey Listen!!
Reaction score
188
Finally released :p:p:p

EDIT: The name should be another, Timer Utils is aready be using... also if this will replace TimerUtils, should the api replace too lol
 

Tom_Kazansky

--- wraith it ! ---
Reaction score
157
shouldn't this be "Timer Merge" something? because I saw merge this and merge that :p

and seriously I don't know what does this do
 

Dirac

22710180
Reaction score
147
shouldn't this be "Timer Merge" something? because I saw merge this and merge that :p

and seriously I don't know what does this do
It has been proved that what slows the game most isn't the amount of timers running at the same time, it's the amount of timers expiring close to each other. This system avoids timer expiration by "merging" both together. How does it merge timers? It adds conditions to a trigger that's evaluated when the timer ends, if timers are meant to be merged then instead of creating a second timer, it adds another condition to the closer trigger timer.

Think of this as a way to "implement T32" to your whole timer usage

I'm so glad this is out. I would change the implementation (if possible) it's very odd.

Isn't 0.3 constant merge VERY inaccurate?
 

Nestharus

o-o
Reaction score
84
Well, KT2 uses .3 so I figured eh, but you can change it if you want ; P. Change it to the timeout that you want to have 100% inaccuracy on the first tick ^)^. Also, keep in mind that people won't be able to tell the 100% inaccuracy of the first tick of a small timeout like .3, hence why .3 was chosen : P.

Also, this provided various types of timers. This is more like Timer Utilities than vex's Timer Utilities is : P. LTQ1 has 0 merges but extremely fast data retrieval ; ).


Also, I'm planning to add a null block to this at some point today (implement 4 modules rather than 3).


Sadly, this is the only real way to do the implementation to avoid the overhead of calling like an expire method. Your code is actually inlined to the module's expiration method ; P.


The null block would be for code after all of the timers expire. It'd be useful for like nulling local handles ; P. Nulling handles has a pretty hefty cost.

edit
updated to have null block
 

Dirac

22710180
Reaction score
147
Instead of naming modules CQT name them QueueBlock or something like that, it would be a lot less confusing. Imo 0.25-0.20 would be a lot better, i would always use 0.20.
 

Nestharus

o-o
Reaction score
84
.3 is just default. You can change it to whatever.

Also, CTQ stands for Constant Timer Queue. They are all acronyms ; p.
 

Dirac

22710180
Reaction score
147
It's obviously an acronym, just that it would be alot more user friendly if they weren't. But that's just a suggestion.
I had no idea you could use unfinished methods inside modules, very neat.
 

Narks

Vastly intelligent whale-like being from the stars
Reaction score
90
another unreadable abomination...
 

Solmyr

Ultra Cool Member
Reaction score
30
another unreadable abomination...

QFT.

Let's see:
  1. Slow. I will never think that something this huge would end up having less overhead than native timers. Provide benchmarks (along with the code of them) done on an optimized map (by using Vexorian's map optimizer). Don't bring up your silly stress tests (that can be faked anytime, anyday).
  2. Inaccurate.
  3. It has arguably the worst API I've ever seen.
  4. Unreadable.
Really, dude, I doubt that my opinion has much impact on here, but if you really need such code in your map, I strongly advise you that you keep it for yourself. It's simply inadequate for a public resource to be coded like this and have an API like this.

If you prefer speed that much over functionality, write your own "highly optimized and customized" code, but don't post it publicly just because you think that some imaginary overheads can kill someone's map. Look at DotA's code - no structs, nothing. Just plain old hashtables - slow as shit, right?. Yet look at the game itself. It doesn't lag (it really doesn't), and is played by over 10 million players worldside. Hell, it's also about to get its one standalone game, DotA 2 (developed by Valve, mind you). You're free to post some of your ubers maps that have been played by 20 players in total, then we'll have a word.

Excuse the flaming (if you consider it as such).
 

Nestharus

o-o
Reaction score
84
For benching, how do you propose I do that outside of stress tests? In case you haven't noticed, RtC stopped working a long time ago.

Eh, in the timer design thread, I did provide the test code I was using... lol
JASS:

struct tester extends array
    private static hashtable h=InitHashtable()
    private static integer i=7000
    private static constant integer TIMEOUTS=640
    private static real n=.00125
    private static integer nn=TIMEOUTS
    private static method test takes nothing returns boolean
        //local thistype this=Timer.data
        //call KT_GetData()
        return false
    endmethod
    private static method test2 takes nothing returns nothing
        //local thistype this=LoadInteger(h,GetHandleId(GetExpiredTimer()),0)
    endmethod
    private static method r takes nothing returns nothing
        local integer m=1
        local timer t
        local trigger t2
        local conditionfunc c=Condition(function thistype.test)
        
        loop
            //call KT_Add(function thistype.test,i,n)
            call Timer.test(n,i,c)
            //set t=CreateTimer()
            //call TimerStart(t,n,true,function thistype.test2)
            //call SaveInteger(h,GetHandleId(t),0,i)
            if (1&lt;TIMEOUTS) then
                set n=n-.125/100
                set nn=nn-1
                if (0==nn or 0&gt;=n) then
                    set nn=TIMEOUTS
                    set n=.8
                endif
            endif
            set m=m-1
            exitwhen 0==m
			set i=i-1
			if (0==i) then
				call PauseTimer(GetExpiredTimer())
				call DestroyTimer(GetExpiredTimer())
				call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,60,&quot;Done&quot;)
				return
			endif
        endloop
    endmethod
    private static method onInit takes nothing returns nothing
        local real r=.001/100
        if (1&lt;TIMEOUTS) then
            set n=.125/100
        endif
        call TimerStart(CreateTimer(),r,true,function thistype.r)
    endmethod
endstruct


For stress testing, you need to make sure most everything else is closed on the computer to ensure accurate results. This means that you can't have a browser open.

You need to make all player start locations be in the upper right corner in the map to ensure more accurate results.

You should watch the fps number for like 1-2 minutes or until it becomes stable.

Slow. I will never think that something this huge would end up having less overhead than native timers. Provide benchmarks (along with the code of them) done on an optimized map (by using Vexorian's map optimizer). Don't bring up your silly stress tests (that can be faked anytime, anyday).

It only gets less overhead than native timers when timers merge together. This less overhead is due to less hashtable reads and less expiring timers. I said in the Timer System Design Thread that the minimum ratio for the dynamic one (Constant Timer Method) requires a 2:3 ratio, 2 CTM timers for 3 natives with handle attachment. This ratio is much worse for KT2.

Inaccurate

Can't argue with that. It's inaccuracy is the reason for its performance ; ).

It has arguably the worst API I've ever seen.

Can't argue with that, but that's the only way to do it ^)^.

Unreadable.

I think this is pretty readable ; P
JASS:

		method new takes real to, code c returns thistype
			local integer t
			local integer i
			local integer h
			local integer s
			local real x
			local real l
			//! runtextmacro PREPARE_TIMEOUT(&quot;to&quot;,&quot;i&quot;)
			//! runtextmacro PREPARE_TICK(&quot;to&quot;,&quot;l&quot;)
			//! runtextmacro ALLOCATE_TIMER(&quot;t&quot;,&quot;to&quot;,&quot;this&quot;)
			//! runtextmacro GET_TIMER_QUEUE(&quot;this&quot;,&quot;h&quot;,&quot;i&quot;)
			//! runtextmacro CANT_MERGE_RIGHT(&quot;h&quot;,&quot;s&quot;,&quot;l&quot;,&quot;x&quot;,&quot;to&quot;)
				//! runtextmacro CAN_MERGE_LEFT(&quot;h&quot;,&quot;s&quot;,&quot;l&quot;,&quot;x&quot;,&quot;to&quot;)
					//! runtextmacro PUSH_AFTER(&quot;s&quot;,&quot;s&quot;,&quot;t&quot;)
				else
					//! runtextmacro ALLOCATE_SEGMENT(&quot;h&quot;,&quot;s&quot;,&quot;to&quot;,&quot;c&quot;,&quot;i&quot;)
					//! runtextmacro ADD_TIMER_TO_SEGMENT(&quot;s&quot;,&quot;t&quot;)
					//! runtextmacro ATTACH_SEGMENT(&quot;h&quot;,&quot;s&quot;)
				endif
			else
				//! runtextmacro ADD_TIMER_TO_SEGMENT(&quot;s&quot;,&quot;t&quot;)
			endif
			return t
		endmethod


If you prefer speed that much over functionality, write your own "highly optimized and customized" code, but don't post it publicly just because you think that some imaginary overheads can kill someone's map. Look at DotA's code - no structs, nothing. Just plain old hashtables - slow as shit, right?. Yet look at the game itself. It doesn't lag (it really doesn't), and is played by over 10 million players worldside. Hell, it's also about to get its one standalone game, DotA 2 (developed by Valve, mind you). You're free to post some of your ubers maps that have been played by 20 players in total, then we'll have a word.

I share my works to increase quality of maps ; ).
 

Sim

Forum Administrator
Staff member
Reaction score
534
Simply put, you cannot name it Timer Utils.

Well, you can. But I will never approve it if that's its name. The name is already in use. I don't really care to which system it fits better; Vexorian's was made years ago.

It's just too confusing for users.
 

PurgeandFire

zxcvmkgdfg
Reaction score
509
I guess this can work as a replacement KT2 if you need it to be more efficient or whatever, but I don't like relying on modules for systems too much. (They add a lot of code to the map, and imo the file-size for speed tradeoff isn't worth it) I guess T32 does the same thing, but it adds very few lines.

Anyway, I'm just saying that the interface is very odd, lol. Maybe you should lengthen the module names a bit, so that they are more specific. Since you have to use them depending on the situation, it might be kind of annoying to always look in the documentation to see the name of the module.
 

Nestharus

o-o
Reaction score
84
I'd never use this over T32 for 32x timers. I actually wrote CTL lib for that ; P. It's a bit of an improvement over T32 : ).
 

Dirac

22710180
Reaction score
147
You should really really change the API, add an onExpire method or something.
 

Bribe

vJass errors are legion
Reaction score
67
This was made for the most extreme of speed freaks, which means cutting all corners like no unnecessary
function calls. Well there are no unnecessary function calls here - most of the stuff inlines.
 

Nestharus

o-o
Reaction score
84
It really saves on the overhead when a method might have many locals declared and many handles to null.
 

Bribe

vJass errors are legion
Reaction score
67
I'm having trouble figuring out what makes the CTM module faster than TimerUtils. It uses its own hashtable
lookup...
 
General chit-chat
Help Users
  • No one is chatting at the moment.

      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