- Joined
- Jul 30, 2012
- Messages
- 156
ExecuteSoon is a library that allows you to queue a function to be executed after a 0-second timer. This allows you to run some code after current fired events finish running, which is useful for many cases (like Damage triggers).
The idea of this snippet comes from thehelper days (Snippet - ExecuteSoon). However this is the first implementation that uses JASS Bytecode to call functions dinamically. This gives much more flexibility as you can use functions that take arguments of any type, and they will be called directly by the bytecode, with the data you provided as the argument.
Also, since all functions are called directly within a single VM, it should be faster than other implementations that use traditional methods for code execution.
Notice that trying to use a function that takes parameters as a callback will throw a syntax error from pjass, so you currently need to annotate your function with the
)
The idea of this snippet comes from thehelper days (Snippet - ExecuteSoon). However this is the first implementation that uses JASS Bytecode to call functions dinamically. This gives much more flexibility as you can use functions that take arguments of any type, and they will be called directly by the bytecode, with the data you provided as the argument.
Also, since all functions are called directly within a single VM, it should be faster than other implementations that use traditional methods for code execution.
Example Usage:
JASS:
function RemoveUnitSoonCallback takes unit u returns nothing
call RemoveUnit(u)
endfunction
//This function removes a unit after a 0-second timer.
//# +nosemanticerror
function RemoveUnitSoon takes unit u returns nothing
call ExecuteSoon(function RemoveUnitSoonCallback, GetHandleId(u))
endfunction
//# +nosemanticerror
flag (@LeP maybe you could finally remove the cant-use-func-as-code check
JASS:
library ExecuteSoon initializer onInit requires Memory
globals
private timer t = CreateTimer()
private integer Count = 2
private integer Id
private integer EndId
private boolean running = false
endglobals
private struct BC extends array
static constant boolean hasTrigger = true
implement Bytecode
endstruct
private function ExecuteSoonBegin takes nothing returns nothing
set running = true
set BC[Count] = 0x16000000
set BC[Count+1] = EndId
set Count = Count+2
endfunction
private function ExecuteSoonEnd takes nothing returns nothing
set running = false
set BC[Count] = 0x27000000
set Count = 2
endfunction
//Runs a function by its id, after a 0-second timer. Useful to use with BJs, as their ids are static.
function ExecuteSoonById takes integer funcid, integer data returns nothing
set BC[Count] = 0x0C010400
set BC[Count+1] = data
if running then
/*calling ExecuteSoon while it's already running should work as expected. The workaround
is to make ExecuteSoon queue itself, so it runs again after all callbacks are called.*/
set BC[Count+2] = 0x0C020400
set BC[Count+3] = funcid
set BC[Count+4] = 0x13020000
set BC[Count+6] = 0x13010000
set BC[Count+8] = 0x16000000
set BC[Count+9] = Id
set BC[Count+10] = 0x0B020000
set Count = Count+12
else
set BC[Count+2] = 0x13010000
set BC[Count+4] = 0x16000000
set BC[Count+5] = funcid
set BC[Count+6] = 0x0B010000
set Count = Count+8
call ResumeTimer(t)
endif
endfunction
//Just an inline-friendly wrapper that gets the function id for you.
function ExecuteSoon takes code func, integer data returns nothing
call ExecuteSoonById(Memory[C2I(func)/4-1], data)
endfunction
//# +nosemanticerror
private function onInit takes nothing returns nothing
set BC[0] = 0x16000000
set BC[1] = Memory[C2I(function ExecuteSoonBegin)/4-1]
set Id = Memory[C2I(function ExecuteSoonById)/4-1]
set EndId = Memory[C2I(function ExecuteSoonEnd)/4-1]
call TimerStart(t, .0, false, null)
call TriggerRegisterTimerExpireEvent(BC.trigger, t)
endfunction
endlibrary