diff --git a/init.lua b/init.lua index 8774bc9..53fb010 100644 --- a/init.lua +++ b/init.lua @@ -49,4 +49,5 @@ if minetest.get_modpath("mtt") and mtt.enabled then dofile(MP .. "/conditions.spec.lua") dofile(MP .. "/build.spec.lua") dofile(MP .. "/build_over.spec.lua") + dofile(MP .. "/timer.spec.lua") end diff --git a/timer.lua b/timer.lua index dab54ed..2a732e6 100644 --- a/timer.lua +++ b/timer.lua @@ -11,36 +11,40 @@ function building_lib.get_building_timer(mapblock_pos) return setmetatable(self, BuildingTimer_mt) end -local function get_timer_key(mapblock_pos) - return "timer_" .. minetest.pos_to_string(mapblock_pos) -end - function BuildingTimer:get_entry() local data = building_lib.store:get_group_data(self.mapblock_pos) - local key = get_timer_key(self.mapblock_pos) - return data[key] or {} + if not data.timers then + -- create timers table + data.timers = {} + building_lib.store:set_group_data(self.mapblock_pos, data) + end + + local key = minetest.pos_to_string(self.mapblock_pos) + return data.timers[key] or {} +end + +function BuildingTimer:set_entry(entry) + local data = building_lib.store:get_group_data(self.mapblock_pos) + if not data.timers then + -- create timers table + data.timers = {} + end + + local key = minetest.pos_to_string(self.mapblock_pos) + data.timers[key] = entry + building_lib.store:set_group_data(self.mapblock_pos, data) end function BuildingTimer:set(timeout, elapsed) - -- TODO: move timers to own field for better lookup - local data = building_lib.store:get_group_data(self.mapblock_pos) - local key = get_timer_key(self.mapblock_pos) - if timeout > 0 then - -- started, update entry - local entry = data[key] - if not entry then - entry = {} - data[key] = entry - end - - entry.timeout = timeout - entry.elapsed = elapsed + self:set_entry({ + timeout = timeout, + elapsed = elapsed + }) else -- stopped, remove entry - data[key] = nil + self:set_entry(nil) end - building_lib.store:set_group_data(self.mapblock_pos, data) end function BuildingTimer:start(timeout) @@ -66,12 +70,52 @@ function BuildingTimer:is_started() return entry.timeout and entry.timeout > entry.elapsed end -function building_lib.update_timers(pos) - -- TODO - print(dump(pos)) +function building_lib.update_timers(pos, interval) + local rpos = mapblock_lib.get_mapblock(pos) + local data = building_lib.store:get_group_data(rpos) + + if not data.timers then + -- no timers found in the mapblock + return + end + + for mapblock_pos_str, entry in pairs(data.timers) do + -- increment active timers and call `on_timer` on buildings + local mapblock_pos = minetest.string_to_pos(mapblock_pos_str) + entry.elapsed = entry.elapsed + interval + + local remove_timer = false + + if entry.elapsed >= entry.timeout then + -- timer event + local def = building_lib.get_building_def_at(mapblock_pos) + if type(def.on_timer) == "function" then + local result = def.on_timer(mapblock_pos, entry.elapsed) + if result then + -- reschedule + entry.elapsed = 0 + else + -- remove + remove_timer = true + end + else + -- invalid field type + remove_timer = true + end + end + + if remove_timer then + data.timers[mapblock_pos_str] = nil + end + end + + -- store timer data + building_lib.store:set_group_data(rpos, data) end --- TODO: iterate over active areas and operate on `DataStorage:get_group_data(pos)` +local TIMER_INTERVAL = 2 + +-- iterate over active areas and operate on `DataStorage:get_group_data(pos)` local function timer_update_loop() local visited = {} @@ -93,7 +137,7 @@ local function timer_update_loop() -- check if already processed if not visited[key] then - building_lib.update_timers(pos) + building_lib.update_timers(pos, TIMER_INTERVAL) visited[key] = true end end @@ -101,7 +145,7 @@ local function timer_update_loop() end end - minetest.after(2, timer_update_loop) + minetest.after(TIMER_INTERVAL, timer_update_loop) end minetest.after(1, timer_update_loop) diff --git a/timer.spec.lua b/timer.spec.lua new file mode 100644 index 0000000..6712c98 --- /dev/null +++ b/timer.spec.lua @@ -0,0 +1,41 @@ + +local building_mapblock_pos = {x=0, y=0, z=0} +local building_name = "building_lib:dummy_timer" +local rotation = 0 +local playername = "singleplayer" + +local timer_mapblock_pos, timer_elapsed + +building_lib.register_building(building_name, { + placement = "dummy", + on_timer = function(mapblock_pos, elapsed) + timer_mapblock_pos = mapblock_pos + timer_elapsed = elapsed + end +}) + +mtt.register("building_lib.get_building_timer", function() + -- clear store + building_lib.store:clear() + + -- try to build + local success, err = building_lib.can_build(building_mapblock_pos, playername, building_name, rotation) + assert(not err) + assert(success) + + -- build + return building_lib.build(building_mapblock_pos, playername, building_name, rotation) + :next(function() + local timer = building_lib.get_building_timer(building_mapblock_pos) + timer:start(10) + + local pos = { x=0, y=0, z=0 } + building_lib.update_timers(pos, 5) + assert(not timer_mapblock_pos) + assert(not timer_elapsed) + + building_lib.update_timers(pos, 5) + assert(vector.equals(timer_mapblock_pos, building_mapblock_pos)) + assert(timer_elapsed >= 10) + end) +end)