- Joined
- Sep 26, 2009
- Messages
- 9,534
Timeout is a new vJass library for handling timer callbacks with as little nonsense as possible. Using Table version 6, common WarCraft 3 timer API is handled behind-the-scenes, and you can just focus on getting your code from A to B.
When running in TEST mode, it can provide instructive debugging messages.
Basic Example (shows how short the code can be):
Core API:
Creates a new Table for you to pack some data into, rather than you passing data to it.
All keys but the handle ID of the timer (integer), as well as the integer and boolean on index -1 are available to use. The Table will be garbage collected when the timer is finished.
You'd access this static property from a callback function, and it points to the Table that holds your data. Accessing this property will automatically cleanup the expired timer for a non-repeating timer.
This is a function similar to getData, except that it will clean up the expired timer for intervals as well.
The static method operator [] on the Timeout struct converts a timer into the Table stored on it. It is similar to Timeout.getData()/Timeout, and is only meant to be used in cases where you intend to resume an expired one-shot timer.
This is like Timeout.start, but the first argument is the timer we're trying to restart. This method is intend to be used when the data on the timer needs to be preserved, and you'd rather not go through the trouble of copying data from one Table over to the fresh Table that would otherwise be provided by Timeout.start.
If you want access to the timer stored inside of the Table.
The Code:
When running in TEST mode, it can provide instructive debugging messages.
Basic Example (shows how short the code can be):
JASS:
function DestroyFX takes nothing returns nothing
call DestroyEffect(Timeout.getData().effect[1])
endfunction
function AddFXTimed takes effect whichEffect, real duration returns Table
return Timeout.start(duration, false, function DestroyFX).effect.save(1, whichEffect)
endfunction
Core API:
JASS:
Timeout.start(
real duration // how long the timeout should last for
boolean isInterval // whether to repeat indefinitely (true) or expire only once (false)
code callbackFn // what function to run after the timeout expires
) -> Table
All keys but the handle ID of the timer (integer), as well as the integer and boolean on index -1 are available to use. The Table will be garbage collected when the timer is finished.
JASS:
local Table data = Timeout.getData()
JASS:
local Table data = Timeout.complete()
JASS:
local Table data = Timeout[GetExpiredTimer()]
JASS:
local Table data = Timeout.restart(timer, duration, isInterval, callbackFn)
JASS:
local timer t = Timeout.getTimer(data)
The Code:
JASS:
// Timeout version 1.0 by Bribe
// Hosted at: https://www.hiveworkshop.com/threads/timeout.351618/
// Requires Table version 6: https://www.hiveworkshop.com/threads/188084/
library Timeout requires Table
globals
private constant boolean TEST = true
endglobals
struct Timeout extends array // "extends" Table
static if TEST then
private static boolean disableUnknownError = false
endif
static method operator [] takes timer obj returns Table
static if TEST then
local Table data = Table(thistype.typeid).get(obj)
if obj == null then
call BJDebugMsg("Timeout[null timer] Error!")
elseif data == 0 then
if thistype.disableUnknownError then
set thistype.disableUnknownError = false
else
call BJDebugMsg("Timeout[unknown timer] Error!")
endif
endif
return data
endif
return Table(thistype.typeid).get(obj)
endmethod
static method getTimer takes Table data returns timer
static if TEST then
local timer obj = data.timer[-1]
if obj == null then
call BJDebugMsg("Timeout.getTimer(Table(" + I2S(data) + ")) -> null Error!")
endif
return obj
else
return data.timer[-1]
endif
endmethod
static method restart takes timer obj, real duration, boolean isInterval, code callbackFn returns Table
local Table data = Timeout[obj]
static if TEST then
local string recommendation
endif
if obj == null then
return 0
elseif data == 0 then
set data = Table(thistype.typeid).bind(obj)
else
static if TEST then
if not data.handle.has(-1) then
if data.boolean[-1] then
set recommendation = "Use Timeout.stop(Table, false) rather than Timeout.complete() when you don't want to destroy a repeating timer."
else
set recommendation = "Use Timeout[GetExpiredTimer()] rather than Timeout.getData() when you don't want to destroy a one-shot timer."
endif
call BJDebugMsg("Timer.restart(zombie timer) -> Table(" + I2S(data) + ") Warning: " + recommendation)
endif
endif
endif
set data.timer[-1] = obj
set data.boolean[-1] = isInterval
call TimerStart(obj, duration, isInterval, callbackFn)
return data
endmethod
static method start takes real duration, boolean isInterval, code callbackFn returns Table
static if TEST then
set thistype.disableUnknownError = true
endif
return Timeout.restart(CreateTimer(), duration, isInterval, callbackFn)
endmethod
private static method asyncSelfDestruct takes nothing returns nothing
local timer obj = GetExpiredTimer()
call Table(thistype.typeid).forget(obj)
call DestroyTimer(obj)
set obj = null
endmethod
static method stop takes Table data, boolean destroyTimer returns Table
local timer obj = Timeout.getTimer(data)
if obj == null then
return 0
endif
call PauseTimer(obj)
if destroyTimer then
call data.handle.remove(-1)
call TimerStart(obj, 0, false, function thistype.asyncSelfDestruct)
endif
set obj = null
return data
endmethod
static method getData takes nothing returns Table
local Table data = Timeout[GetExpiredTimer()]
local boolean isInterval = data.boolean[-1]
if not isInterval then
call Timeout.stop(data, true)
endif
return data
endmethod
static method complete takes nothing returns Table
return Timeout.stop(Timeout[GetExpiredTimer()], true)
endmethod
endstruct
endlibrary
JASS:
library TimeoutTests initializer Init requires Timeout
globals
private constant integer BAD_INTERVAL_CALL_EXAMPLE = 1
private constant integer GOOD_INTERVAL_CALL_EXAMPLE = 2
private integer testPhase = BAD_INTERVAL_CALL_EXAMPLE
endglobals
private function Callback takes nothing returns nothing
local Table data = Timeout.getData()
call BJDebugMsg("Table " + I2S(data) + data.string[1])
endfunction
private function StandardInterval takes nothing returns nothing
local Table data = Timeout.getData()
local integer count = data[1]
local boolean badExample = testPhase == BAD_INTERVAL_CALL_EXAMPLE
call BJDebugMsg("Table " + I2S(data) + " has " + I2S(count) + " seconds remaining.")
if badExample then
if count < -2 then
call BJDebugMsg("Forgetting to call `complete` or `stop` will cause the interval to continue on forever. Let's try again.")
set testPhase = GOOD_INTERVAL_CALL_EXAMPLE
set data[1] = 3
return
endif
elseif count == 0 then
call Timeout.stop(data, true)
call BJDebugMsg("Tests have concluded.")
endif
set count = count - 1
set data[1] = count
endfunction
private function IrregularInterval takes nothing returns nothing
local Table data
local integer count
local boolean badExample = testPhase == BAD_INTERVAL_CALL_EXAMPLE
if badExample then
set data = Timeout.getData()
else
set data = Timeout[GetExpiredTimer()]
endif
set count = data[1]
call BJDebugMsg("Table " + I2S(data) + " has " + I2S(count) + " seconds remaining.")
if count == 0 then
if badExample then
call BJDebugMsg("Timeout should no longer show warnings in TEST mode. Let's try again with the non-destructive Timeout[GetExpiredTimer()] call.")
set testPhase = GOOD_INTERVAL_CALL_EXAMPLE
call Timeout.start(1, false, function IrregularInterval).save(1, 3)
else
call Timeout.complete()
set testPhase = BAD_INTERVAL_CALL_EXAMPLE
call BJDebugMsg("Now show an example where we forget to complete a repeating timer.")
call Timeout.start(1, true, function StandardInterval).save(1, 3)
endif
return
elseif count == 3 and badExample then
call BJDebugMsg("Timeout should show warnings in TEST mode because we are going to test cases where we restart an expired one-shot timer.")
endif
call Timeout.restart(GetExpiredTimer(), 1, false, function IrregularInterval)
set count = count - 1
set data[1] = count
endfunction
private function Init takes nothing returns nothing
call Timeout.start(1, false, function Callback).string.save(1, " has been called after 1 second")
call Timeout.start(2, false, function Callback).string.save(1, " has been called after 2 seconds")
call Timeout.start(3, false, function IrregularInterval).save(1, 3)
endfunction
endlibrary
Last edited: