Background:
UPDATES:
Changed this over to a full fledged Trigger Utility system. Changed the ways in which errors were displayed. Added in trigger attachment functionality. Updated the user configurable constants greatly. Added in a much better readme to the script. Removed the need for multiple timers. Added in a trigger queue status update message.
Check it out!
People, more recently, have shunned the usage of dynamic triggers, or more accurately, destroying triggers (but one without the other is unrealistic). I like dynamic triggers. Sometimes its just plain easier and more convenient to use them, and I am all about ease of use.
What is the problem with them? - Well, supposedly, destroying a trigger before every thread started by it ends will cause bugs with handles, and possible handle stack corruption. Personally, I have never had such issues; however, perhaps I never did something silly like that.
In any case, this is a little system I have used since hearing about such incidents. Its also similar to the one IceFrog uses in DotA: Allstars to try and avoid these bugs, with much success mind you.
Usage:What is the problem with them? - Well, supposedly, destroying a trigger before every thread started by it ends will cause bugs with handles, and possible handle stack corruption. Personally, I have never had such issues; however, perhaps I never did something silly like that.
In any case, this is a little system I have used since hearing about such incidents. Its also similar to the one IceFrog uses in DotA: Allstars to try and avoid these bugs, with much success mind you.
JASS:
call DestroyTriggerEx(trigger to destroy)
When ever you are completely done with your dynamic trigger, or any trigger for that matter. Its beneficial to use Condition functions as opposed to trigger actions with dynamic triggers since they are reused and thus do not leak or need to be cleaned, and because they tend to run faster.
Theory:If we wait a decent amount of time after a trigger is last used, the chances that all threads it created are finished running will be much higher then if it is destroyed immediately. Therefore, we wait, and destroy the triggers after a set duration.
The less triggers you create dynamically, the longer you can wait (RecycleTimeLimit) before needing to destroy triggers or experiencing any slowdown, and the higher the chance of success for this system.
Code:The less triggers you create dynamically, the longer you can wait (RecycleTimeLimit) before needing to destroy triggers or experiencing any slowdown, and the higher the chance of success for this system.
JASS:
library TriggerUtils initializer Init
//******************************************************************************
//* By: emjlr3 Version 1.00
//*
//* This librarys functionality is two fold.
//* It allows users to safely use dynamic triggers without leaking, by
//* waiting a duration after which the chance that all threads started through
//* said trigger are no longer active.
//* It also gives users a quick and safe method for attaching data to triggers
//* by using the H2I bug and affording many user error checks.
//*
//* The trigger destruction method removes the need to destroy/disable triggers
//* at the users end. Users simply call:
//* function DestroyTriggerEx takes trigger t returns nothing
//*
//* When they wish to destroy any trigger.
//*
//* The trigger data storage method uses the time tested H2I()-Offset method:
//* function SetTriggerData takes trigger t, integer i returns nothing
//* function GetTriggerData takes trigger t returns integer
//*
//* SetTriggerData attachs the integer i to the specified trigger.
//* GetTriggerData retrieves the integer previously stored to a trigger.
//*
globals
// Duration after a trigger is disabled before it is checked for destruction
private constant real RecycleTimeLimit = 60.
// Periodic interval to check for triggers to destroy
private constant real Timeout = 15.
// Maximum number of destroyable triggers allowed before the system reports an error
// This is a fairly inflated size. If you need more then this you are doing it wrong.
private constant integer MaxTriggers = 2500
// Trigger data array size, change this to a larger value if your map has an over abundance of handles
private constant integer ArraySize = 8191
// Smallest handle value in your map
private constant integer Offset = 0x100000
// If in debug mode, this will display the total number of queued triggers every Timeout interval
private boolean DisplayStatus = true
// -- Don't touch past this point
private trigger array Trigs[MaxTriggers]
private integer Count=0
private integer Time=0
private integer RealArraySize
private integer array TriggerData[ArraySize]
private real array ExpirationTime[MaxTriggers]
private timer Tim = CreateTimer()
endglobals
private function t2i takes trigger t returns integer
return t
return 0
endfunction
private function Recycle takes integer i returns nothing
if i!=Count then
set Trigs<i>=Trigs[Count]
set ExpirationTime<i>=ExpirationTime[Count]
endif
set Trigs[Count]=null
set ExpirationTime[Count]=0.
set Count=Count-1
endfunction
private function Destroy takes nothing returns nothing
local integer i=1
local real r
set Time=Time+1
set r = TimerGetElapsed(Tim)+Time*Timeout
loop
exitwhen i>Count
if ExpirationTime<i><=r then
debug if Trigs<i>==null or IsTriggerEnabled(Trigs<i>) then
debug call BJDebugMsg("|c00ff0303Trigger Destruction error: trigger is null or still enabled, destruction is unwise.")
debug endif
call DestroyTrigger(Trigs<i>)
call Recycle(i)
else
set i=i+1
endif
endloop
debug if DisplayStatus then
debug call DisplayTimedTextToPlayer(GetLocalPlayer(),0.,0.,Timeout/1.5,"Total number of triggers in the destroy queue: "+I2S(Count))
debug endif
endfunction
// User functions
function GetTriggerData takes trigger t returns integer
local integer id = t2i(t)-Offset
debug if t==null then
debug call BJDebugMsg("|c00ff0303GetTriggerData error: null triggers cannot have retrievable data.")
debug elseif id<0 then
debug call BJDebugMsg("|c00ff0303GetTriggerData error: trigger id is too small, reduce Offset value.")
debug elseif id>RealArraySize then
debug call BJDebugMsg("|c00ff0303GetTriggerData error: trigger id is too large, increase ArraySize value.")
debug elseif TriggerData[id]<=0 then
debug call BJDebugMsg("|c00ff0303GetTriggerData error: trigger has no attached data.")
debug endif
return TriggerData[id]
endfunction
function SetTriggerData takes trigger t, integer i returns nothing
local integer id = t2i(t)-Offset
debug if t==null then
debug call BJDebugMsg("|c00ff0303SetTriggerData error: null triggers cannot be attached to.")
debug elseif i<=0 then
debug call BJDebugMsg("|c00ff0303SetTriggerData error: no attachable data specified.")
debug elseif id<0 then
debug call BJDebugMsg("|c00ff0303SetTriggerData error: trigger id is too small, reduce Offset value.")
debug elseif id>RealArraySize then
debug call BJDebugMsg("|c00ff0303SetTriggerData error: trigger id is too large, increase ArraySize value.")
debug endif
set TriggerData[id] = i
endfunction
function DestroyTriggerEx takes trigger t returns nothing
call DisableTrigger(t)
set Count=Count+1
set Trigs[Count]=t
set ExpirationTime[Count]=(TimerGetElapsed(Tim)+Time*Timeout)+RecycleTimeLimit
debug if Count==MaxTriggers then
debug call BJDebugMsg("|c00ff0303DestroyTriggerEx error: maximum number of destroyable triggers reached, increase MaxTriggers value.")
debug endif
endfunction
// Initialization
private function Init takes nothing returns nothing
call TimerStart(Tim,Timeout,true,function Destroy)
set RealArraySize = ArraySize-1
endfunction
endlibrary</i></i></i></i></i></i>
UPDATES:
Changed this over to a full fledged Trigger Utility system. Changed the ways in which errors were displayed. Added in trigger attachment functionality. Updated the user configurable constants greatly. Added in a much better readme to the script. Removed the need for multiple timers. Added in a trigger queue status update message.
JASS:
Check it out!
Thoughts, suggestions, etc....?? Enjoy :shades: