|  |  | @ -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); |  |  |  |         int id = timers.begin()->second; | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |     lua_rawgetp(State, LUA_REGISTRYINDEX, &DFHACK_TIMEOUTS_TOKEN); |  |  |  |         timers.erase(timers.begin()); | 
			
				
				
			
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  |     frame_idx++; |  |  |  |         lua_rawgeti(L, table, id); | 
			
				
				
			
		
	
		
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  |     while (!frame_timers.empty() && |  |  |  |         if (lua_isnil(L, -1)) | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |            frame_timers.begin()->first <= frame_idx) |  |  |  |             lua_pop(L, 1); | 
			
				
				
			
		
	
		
		
	
		
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |         else | 
			
		
	
		
		
			
				
					
					|  |  |  |         { |  |  |  |         { | 
			
		
	
		
		
			
				
					
					|  |  |  |         int id = frame_timers.begin()->second; |  |  |  |             lua_pushnil(L); | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |         frame_timers.erase(frame_timers.begin()); |  |  |  |             lua_rawseti(L, table, id); | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |         lua_rawgeti(State, frame[1], id); |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |         lua_pushnil(State); |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |         lua_rawseti(State, frame[1], 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); | 
			
		
	
		
		
			
				
					
					|  |  |  | } |  |  |  | } | 
			
		
	
	
		
		
			
				
					|  |  | 
 |