Support testing and modifying pending timeout callbacks.

develop
Alexander Gavrilov 2012-05-16 17:06:08 +04:00
parent e77c9dc730
commit dbd39af58a
3 changed files with 73 additions and 31 deletions

@ -1087,6 +1087,13 @@ Core context specific functions:
Returns the timer id, or *nil* if unsuccessful due to Returns the timer id, or *nil* if unsuccessful due to
world being unloaded. world being unloaded.
* ``dfhack.timeout_active(id[,new_callback])``
Returns the active callback with the given id, or *nil*
if inactive or nil id. If called with 2 arguments, replaces
the current callback with the given value, if still active.
Using ``timeout_active(id,nil)`` cancels the timer.
* ``dfhack.onStateChange.foo = function(code)`` * ``dfhack.onStateChange.foo = function(code)``
Event. Receives the same codes as plugin_onstatechange in C++. Event. Receives the same codes as plugin_onstatechange in C++.

@ -1265,6 +1265,12 @@ and cannot be queued until it is loaded again.
Returns the timer id, or <em>nil</em> if unsuccessful due to Returns the timer id, or <em>nil</em> if unsuccessful due to
world being unloaded.</p> world being unloaded.</p>
</li> </li>
<li><p class="first"><tt class="docutils literal"><span class="pre">dfhack.timeout_active(id[,new_callback])</span></tt></p>
<p>Returns the active callback with the given id, or <em>nil</em>
if inactive or nil id. If called with 2 arguments, replaces
the current callback with the given value, if still active.
Using <tt class="docutils literal">timeout_active(id,nil)</tt> cancels the timer.</p>
</li>
<li><p class="first"><tt class="docutils literal">dfhack.onStateChange.foo = function(code)</tt></p> <li><p class="first"><tt class="docutils literal">dfhack.onStateChange.foo = function(code)</tt></p>
<p>Event. Receives the same codes as plugin_onstatechange in C++.</p> <p>Event. Receives the same codes as plugin_onstatechange in C++.</p>
</li> </li>

@ -1429,6 +1429,7 @@ int dfhack_timeout(lua_State *L)
using df::global::world; using df::global::world;
using df::global::enabler; using df::global::enabler;
// Parse arguments
lua_Number time = luaL_checknumber(L, 1); lua_Number time = luaL_checknumber(L, 1);
int mode = luaL_checkoption(L, 2, NULL, timeout_modes); int mode = luaL_checkoption(L, 2, NULL, timeout_modes);
luaL_checktype(L, 3, LUA_TFUNCTION); luaL_checktype(L, 3, LUA_TFUNCTION);
@ -1440,6 +1441,7 @@ int dfhack_timeout(lua_State *L)
return 1; return 1;
} }
// Compute timeout value
switch (mode) switch (mode)
{ {
case 2: case 2:
@ -1454,12 +1456,13 @@ int dfhack_timeout(lua_State *L)
default:; default:;
} }
int id = next_timeout_id++;
int delta = time; int delta = time;
if (delta <= 0) if (delta <= 0)
luaL_error(L, "Invalid timeout: %d", delta); luaL_error(L, "Invalid timeout: %d", delta);
// Queue the timeout
int id = next_timeout_id++;
if (mode) if (mode)
tick_timers.insert(std::pair<int,int>(world->frame_counter+delta, id)); tick_timers.insert(std::pair<int,int>(world->frame_counter+delta, id));
else else
@ -1473,20 +1476,44 @@ int dfhack_timeout(lua_State *L)
return 1; return 1;
} }
static void cancel_tick_timers() int dfhack_timeout_active(lua_State *L)
{
int id = luaL_optint(L, 1, -1);
bool set_cb = (lua_gettop(L) >= 2);
lua_settop(L, 2);
if (!lua_isnil(L, 2))
luaL_checktype(L, 2, LUA_TFUNCTION);
if (id < 0)
{
lua_pushnil(L);
return 1;
}
lua_rawgetp(L, LUA_REGISTRYINDEX, &DFHACK_TIMEOUTS_TOKEN);
lua_rawgeti(L, 3, id);
if (set_cb && !lua_isnil(L, -1))
{
lua_pushvalue(L, 2);
lua_rawseti(L, 3, id);
}
return 1;
}
static void cancel_timers(std::multimap<int,int> &timers)
{ {
using Lua::Core::State; using Lua::Core::State;
Lua::StackUnwinder frame(State); Lua::StackUnwinder frame(State);
lua_rawgetp(State, LUA_REGISTRYINDEX, &DFHACK_TIMEOUTS_TOKEN); lua_rawgetp(State, LUA_REGISTRYINDEX, &DFHACK_TIMEOUTS_TOKEN);
for (auto it = tick_timers.begin(); it != tick_timers.end(); ++it) for (auto it = timers.begin(); it != timers.end(); ++it)
{ {
lua_pushnil(State); lua_pushnil(State);
lua_rawseti(State, frame[1], it->second); lua_rawseti(State, frame[1], it->second);
} }
tick_timers.clear(); timers.clear();
} }
void DFHack::Lua::Core::onStateChange(color_ostream &out, int code) { void DFHack::Lua::Core::onStateChange(color_ostream &out, int code) {
@ -1496,7 +1523,7 @@ void DFHack::Lua::Core::onStateChange(color_ostream &out, int code) {
{ {
case SC_MAP_UNLOADED: case SC_MAP_UNLOADED:
case SC_WORLD_UNLOADED: case SC_WORLD_UNLOADED:
cancel_tick_timers(); cancel_timers(tick_timers);
break; break;
default:; default:;
@ -1506,40 +1533,40 @@ void DFHack::Lua::Core::onStateChange(color_ostream &out, int code) {
Lua::InvokeEvent(out, State, (void*)onStateChange, 1); Lua::InvokeEvent(out, State, (void*)onStateChange, 1);
} }
void DFHack::Lua::Core::onUpdate(color_ostream &out) static void run_timers(color_ostream &out, lua_State *L,
std::multimap<int,int> &timers, int table, int bound)
{ {
using df::global::world; while (!timers.empty() && timers.begin()->first <= bound)
Lua::StackUnwinder frame(State);
lua_rawgetp(State, LUA_REGISTRYINDEX, &DFHACK_TIMEOUTS_TOKEN);
frame_idx++;
while (!frame_timers.empty() &&
frame_timers.begin()->first <= frame_idx)
{ {
int id = frame_timers.begin()->second; int id = timers.begin()->second;
frame_timers.erase(frame_timers.begin()); timers.erase(timers.begin());
lua_rawgeti(State, frame[1], id); lua_rawgeti(L, table, id);
lua_pushnil(State);
lua_rawseti(State, frame[1], id); if (lua_isnil(L, -1))
lua_pop(L, 1);
else
{
lua_pushnil(L);
lua_rawseti(L, table, id);
Lua::SafeCall(out, State, 0, 0); Lua::SafeCall(out, L, 0, 0);
}
} }
}
while (!tick_timers.empty() && void DFHack::Lua::Core::onUpdate(color_ostream &out)
tick_timers.begin()->first <= world->frame_counter) {
{ using df::global::world;
int id = tick_timers.begin()->second;
tick_timers.erase(tick_timers.begin());
lua_rawgeti(State, frame[1], id); if (frame_timers.empty() && tick_timers.empty())
lua_pushnil(State); return;
lua_rawseti(State, frame[1], id);
Lua::SafeCall(out, State, 0, 0); Lua::StackUnwinder frame(State);
} lua_rawgetp(State, LUA_REGISTRYINDEX, &DFHACK_TIMEOUTS_TOKEN);
run_timers(out, State, frame_timers, frame[1], ++frame_idx);
run_timers(out, State, tick_timers, frame[1], world->frame_counter);
} }
void DFHack::Lua::Core::Init(color_ostream &out) void DFHack::Lua::Core::Init(color_ostream &out)
@ -1562,6 +1589,8 @@ void DFHack::Lua::Core::Init(color_ostream &out)
lua_pushcfunction(State, dfhack_timeout); lua_pushcfunction(State, dfhack_timeout);
lua_setfield(State, -2, "timeout"); lua_setfield(State, -2, "timeout");
lua_pushcfunction(State, dfhack_timeout_active);
lua_setfield(State, -2, "timeout_active");
lua_pop(State, 1); lua_pop(State, 1);
} }