System Timer Utils

Discussion in 'Graveyard' started by Nestharus, Aug 8, 2011.

  1. Nestharus

    Nestharus o-o

    Ratings:
    +83 / 0 / -0
    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<to-x-l) then
    			set x=TimerGetRemaining(st[f])
    			if (0!=f and 0>=x-l) then
    				//! runtextmacro TIMER_TOOLS_PUSH_AFTER("f","f","t")
    			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("s","t")
    		endif
    		return t
    	endfunction
    	private function DT takes integer t, integer f returns integer
    		local integer s
    		//! runtextmacro TIMER_TOOLS_IS_ALLOCATED("t","TIMER ERROR: ATTEMPTED TO DESTROY NULL TIMER")
    			set nt[t]=0
    			//! runtextmacro TIMER_TOOLS_IS_AFTER("t")
    				//! runtextmacro TIMER_TOOLS_POP_AFTER("s","t")
    				//! runtextmacro TIMER_TOOLS_IS_SEGMENT_EMPTY("s")
    					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("s","t")
    			//! runtextmacro TIMER_TOOLS_IS_PAUSED("s")
    				//! runtextmacro TIMER_TOOLS_DEL("t")
    			else
    				//! runtextmacro TIMER_TOOLS_IS_SEGMENT_RUNNING("s","t")
    					//! runtextmacro TIMER_TOOLS_REMOVE_FROM_SEGMENT("s","t")
    					//! runtextmacro TIMER_TOOLS_DEL("t")
    					//! runtextmacro TIMER_TOOLS_IS_SEGMENT_EMPTY("s")
    						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("s","0","t")
    				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("this")
    					//! runtextmacro TIMER_TOOLS_REMOVE_FROM_SEGMENT("s","this")
    					//! runtextmacro TIMER_TOOLS_IS_DESTROYING("this")
    						//! runtextmacro TIMER_TOOLS_DEL("this")
    					endif
    				else
    					set as[this]=0
    					//! runtextmacro TIMER_TOOLS_ADD_TIMER_TO_SEGMENT("s","this")
    				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("t","TIMER ERROR: ATTEMPTED TO MANIPULATE NULL TIMER")
    				//! runtextmacro TIMER_TOOLS_PREPARE_TIMEOUT("to","i")
    				//! runtextmacro TIMER_TOOLS_IS_NEW_TIMEOUT("nt[t]","to")
    					set nt[t]=to
    					//! runtextmacro TIMER_TOOLS_IS_AFTER("t")
    						//! runtextmacro TIMER_TOOLS_POP_AFTER("s","t")
    						//! runtextmacro TIMER_TOOLS_IS_SEGMENT_EMPTY("s")
    						//! runtextmacro TIMER_TOOLS_GET_NODE_HEAD("h","s")
    							//! runtextmacro TIMER_TOOLS_DEL_SEGMENT("h","s")
    							//! runtextmacro TIMER_TOOLS_UNATTACH2("s")
    							//! runtextmacro TIMER_TOOLS_IS_QUEUE_EMPTY("h")
    								//! runtextmacro TIMER_TOOLS_DEL("h")
    								//! runtextmacro TIMER_TOOLS_UNATTACH("this","h")
    							endif
    						endif
    					endif
    					//! runtextmacro TIMER_TOOLS_GET_NODE_HEAD("s","t")
    					//! runtextmacro TIMER_TOOLS_IS_NOT_PAUSED("s")
    						//! runtextmacro TIMER_TOOLS_IS_SEGMENT_RUNNING("s","t")
    							//! runtextmacro TIMER_TOOLS_REMOVE_FROM_SEGMENT("s","t")
    							//! runtextmacro TIMER_TOOLS_IS_SEGMENT_EMPTY("s")
    								//! runtextmacro TIMER_TOOLS_GET_NODE_HEAD("h","s")
    								//! runtextmacro TIMER_TOOLS_DEL_SEGMENT("h","s")
    								//! runtextmacro TIMER_TOOLS_UNATTACH2("s")
    								//! runtextmacro TIMER_TOOLS_IS_QUEUE_EMPTY("h")
    									//! runtextmacro TIMER_TOOLS_DEL("h")
    									//! runtextmacro TIMER_TOOLS_UNATTACH("this","h")
    								endif
    							endif
    						else
    							//! runtextmacro TIMER_TOOLS_PUSH_AFTER("s","-1","t")
    							set b=false
    						endif
    					endif
    					if (b) then
    						//! runtextmacro TIMER_TOOLS_PREPARE_TICK("to","l")
    						//! runtextmacro TIMER_TOOLS_GET_TIMER_QUEUE("this","h","i")
    						//! runtextmacro TIMER_TOOLS_CANT_MERGE_RIGHT("h","s","l","x","to")
    							//! runtextmacro TIMER_TOOLS_CAN_MERGE_LEFT("h","s","l","x","to")
    								//! runtextmacro TIMER_TOOLS_PUSH_AFTER("s","s","t")
    							else
    								//! runtextmacro TIMER_TOOLS_ALLOCATE_SEGMENT("h","s","to","c","i")
    								//! runtextmacro TIMER_TOOLS_ADD_TIMER_TO_SEGMENT("s","t")
    								//! runtextmacro TIMER_TOOLS_ATTACH_SEGMENT("h","s")
    							endif
    						else
    							//! runtextmacro TIMER_TOOLS_ADD_TIMER_TO_SEGMENT("s","t")
    						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("to","i")
    			//! runtextmacro TIMER_TOOLS_PREPARE_TICK("to","l")
    			//! runtextmacro TIMER_TOOLS_ALLOCATE_TIMER("t","to","this")
    			//! runtextmacro TIMER_TOOLS_GET_TIMER_QUEUE("this","h","i")
    			//! runtextmacro TIMER_TOOLS_CANT_MERGE_RIGHT("h","s","l","x","to")
    				//! runtextmacro TIMER_TOOLS_CAN_MERGE_LEFT("h","s","l","x","to")
    					//! runtextmacro TIMER_TOOLS_PUSH_AFTER("s","s","t")
    				else
    					//! runtextmacro TIMER_TOOLS_ALLOCATE_SEGMENT("h","s","to","c","i")
    					//! runtextmacro TIMER_TOOLS_ADD_TIMER_TO_SEGMENT("s","t")
    					//! runtextmacro TIMER_TOOLS_ATTACH_SEGMENT("h","s")
    				endif
    			else
    				//! runtextmacro TIMER_TOOLS_ADD_TIMER_TO_SEGMENT("s","t")
    			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("t","TIMER ERROR: ATTEMPTED TO DESTROY NULL TIMER")
    				set nt[t]=0
    				//! runtextmacro TIMER_TOOLS_IS_AFTER("t")
    					//! runtextmacro TIMER_TOOLS_POP_AFTER("s","t")
    					//! runtextmacro TIMER_TOOLS_IS_SEGMENT_EMPTY("s")
    						//! runtextmacro TIMER_TOOLS_GET_NODE_HEAD("h","s")
    						//! runtextmacro TIMER_TOOLS_DEL_SEGMENT("h","s")
    						//! runtextmacro TIMER_TOOLS_UNATTACH2("s")
    						//! runtextmacro TIMER_TOOLS_IS_QUEUE_EMPTY("h")
    							//! runtextmacro TIMER_TOOLS_DEL("h")
    							//! runtextmacro TIMER_TOOLS_UNATTACH("this","h")
    						endif
    					endif
    				endif
    				//! runtextmacro TIMER_TOOLS_GET_NODE_HEAD("s","t")
    				//! runtextmacro TIMER_TOOLS_IS_PAUSED("s")
    					//! runtextmacro TIMER_TOOLS_DEL("t")
    				else
    					//! runtextmacro TIMER_TOOLS_IS_SEGMENT_RUNNING("s","t")
    						//! runtextmacro TIMER_TOOLS_REMOVE_FROM_SEGMENT("s","t")
    						//! runtextmacro TIMER_TOOLS_DEL("t")
    						//! runtextmacro TIMER_TOOLS_IS_SEGMENT_EMPTY("s")
    							//! runtextmacro TIMER_TOOLS_GET_NODE_HEAD("h","s")
    							//! runtextmacro TIMER_TOOLS_DEL_SEGMENT("h","s")
    							//! runtextmacro TIMER_TOOLS_UNATTACH2("s")
    							//! runtextmacro TIMER_TOOLS_IS_QUEUE_EMPTY("h")
    								//! runtextmacro TIMER_TOOLS_DEL("h")
    								//! runtextmacro TIMER_TOOLS_UNATTACH("this","h")
    							endif
    						endif
    					else
    						//! runtextmacro TIMER_TOOLS_PUSH_AFTER("s","0","t")
    					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("s2","t","t2")
    			//! runtextmacro TIMER_TOOLS_IS_PAUSING("t")
    				//! runtextmacro TIMER_TOOLS_REMOVE_FROM_SEGMENT("s2","t")
    				//! runtextmacro TIMER_TOOLS_IS_DESTROYING("t")
    					//! runtextmacro TIMER_TOOLS_DEL("t")
    				endif
    			//! runtextmacro TIMER_TOOLS_IS_ADDING("s2","t")
    				//! runtextmacro TIMER_TOOLS_ADD_TIMER_TO_SEGMENT("s2","t")
    			//! runtextmacro TIMER_TOOLS_IS_MOVING("t")
    				set to=nt[t]
    				set i=R2I(to/CONSTANT_ACCURACY+.5)
    				//! runtextmacro TIMER_TOOLS_REMOVE_FROM_SEGMENT("s2","t")
    				//! runtextmacro TIMER_TOOLS_PREPARE_TICK("to","l")
    				//! runtextmacro TIMER_TOOLS_GET_TIMER_QUEUE("this","h","i")
    				//! runtextmacro TIMER_TOOLS_CANT_MERGE_RIGHT("h","s","l","x","to")
    					//! runtextmacro TIMER_TOOLS_CAN_MERGE_LEFT("h","s","l","x","to")
    						//! runtextmacro TIMER_TOOLS_PUSH_AFTER("s","s","t")
    					else
    						//! runtextmacro TIMER_TOOLS_ALLOCATE_SEGMENT("h","s","to","c","i")
    						//! runtextmacro TIMER_TOOLS_ADD_TIMER_TO_SEGMENT("s","t")
    						//! runtextmacro TIMER_TOOLS_ATTACH_SEGMENT("h","s")
    					endif
    				else
    					//! runtextmacro TIMER_TOOLS_ADD_TIMER_TO_SEGMENT("s","t")
    				endif
    			//! runtextmacro TIMER_TOOLS_GET_NEXT_AFTER("s","s2","t")
    	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("this","false")
    			//! runtextmacro TIMER_TOOLS_PREPARE_TIMER("s","this")
    			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("this","s")
    				//! runtextmacro TIMER_TOOLS_DEL_SEGMENT("this","s")
    				//! runtextmacro TIMER_TOOLS_UNATTACH2("s")
    				//! runtextmacro TIMER_TOOLS_IS_QUEUE_EMPTY("this")
    					//! runtextmacro TIMER_TOOLS_DEL("this")
    					//! runtextmacro TIMER_TOOLS_UNATTACH("tl[this]","this")
    				endif
    			else
    				//! runtextmacro TIMER_TOOLS_SET_TIMER_RUNNING_FLAG("s","true")
    			endif
    		endmethod
    		private static method onInit takes nothing returns nothing
    			set f=Table.create()
    			set c=function thistype.e
    		endmethod
    	endmodule
    endlibrary


    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="My Value: "
            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="My Value: "
        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="My Value: "
        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).
     
    • Like Like x 1
  2. Laiev

    Laiev Hey Listen!!

    Ratings:
    +187 / 0 / -0
    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
     
  3. Tom_Kazansky

    Tom_Kazansky --- wraith it ! ---

    Ratings:
    +157 / 0 / -0
    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
     
  4. Dirac

    Dirac 22710180

    Ratings:
    +147 / 0 / -0
    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?
     
  5. Nestharus

    Nestharus o-o

    Ratings:
    +83 / 0 / -0
    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
     
  6. Dirac

    Dirac 22710180

    Ratings:
    +147 / 0 / -0
    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.
     
  7. Nestharus

    Nestharus o-o

    Ratings:
    +83 / 0 / -0
    .3 is just default. You can change it to whatever.

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

    Dirac 22710180

    Ratings:
    +147 / 0 / -0
    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.
     
  9. Narks

    Narks Vastly intelligent whale-like being from the stars

    Ratings:
    +92 / 0 / -0
    another unreadable abomination...
     
  10. Solmyr

    Solmyr Active Member

    Ratings:
    +30 / 0 / -0
    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).
     
  11. Nestharus

    Nestharus o-o

    Ratings:
    +83 / 0 / -0
    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<TIMEOUTS) then
                    set n=n-.125/100
                    set nn=nn-1
                    if (0==nn or 0>=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,"Done")
    				return
    			endif
            endloop
        endmethod
        private static method onInit takes nothing returns nothing
            local real r=.001/100
            if (1<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.

    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.

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

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

    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("to","i")
    			//! runtextmacro PREPARE_TICK("to","l")
    			//! runtextmacro ALLOCATE_TIMER("t","to","this")
    			//! runtextmacro GET_TIMER_QUEUE("this","h","i")
    			//! runtextmacro CANT_MERGE_RIGHT("h","s","l","x","to")
    				//! runtextmacro CAN_MERGE_LEFT("h","s","l","x","to")
    					//! runtextmacro PUSH_AFTER("s","s","t")
    				else
    					//! runtextmacro ALLOCATE_SEGMENT("h","s","to","c","i")
    					//! runtextmacro ADD_TIMER_TO_SEGMENT("s","t")
    					//! runtextmacro ATTACH_SEGMENT("h","s")
    				endif
    			else
    				//! runtextmacro ADD_TIMER_TO_SEGMENT("s","t")
    			endif
    			return t
    		endmethod


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

    Sim Forum Administrator Staff Member

    Ratings:
    +531 / 0 / -0
    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.
     
  13. Nestharus

    Nestharus o-o

    Ratings:
    +83 / 0 / -0
    I'll change it to Tt, Timer Tools ; )
     
  14. PurgeandFire

    PurgeandFire zxcvmkgdfg

    Ratings:
    +513 / 0 / -0
    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.
     
  15. Nestharus

    Nestharus o-o

    Ratings:
    +83 / 0 / -0
    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 : ).
     
  16. Dirac

    Dirac 22710180

    Ratings:
    +147 / 0 / -0
    You should really really change the API, add an onExpire method or something.
     
  17. Bribe

    Bribe vJass errors are legion

    Ratings:
    +67 / 0 / -0
    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.
     
  18. Nestharus

    Nestharus o-o

    Ratings:
    +83 / 0 / -0
    It really saves on the overhead when a method might have many locals declared and many handles to null.
     
  19. Bribe

    Bribe vJass errors are legion

    Ratings:
    +67 / 0 / -0
    I'm having trouble figuring out what makes the CTM module faster than TimerUtils. It uses its own hashtable
    lookup...
     
  20. Nestharus

    Nestharus o-o

    Ratings:
    +83 / 0 / -0
    The merges
     

Share This Page