diff --git a/doc/ru/scripting/extensions.md b/doc/ru/scripting/extensions.md index 0de8e4a7..7ef835f4 100644 --- a/doc/ru/scripting/extensions.md +++ b/doc/ru/scripting/extensions.md @@ -259,8 +259,15 @@ function sleep(timesec: number) Вызывает остановку корутины до тех пор, пока не пройдёт количество секунд, указанное в **timesec**. Функция может быть использована только внутри корутины. +```lua +function await(co: coroutine) -> result, error +``` + +Ожидает завершение переданной корутины, возвращая поток управления. Функция может быть использована только внутри корутины. +Возвращает значения аналогичные возвращаемым значениям *pcall*. + ```lua os.pid -> number ``` -Константа, в которой хранится PID текущего инстанса движка \ No newline at end of file +Константа, в которой хранится PID текущего инстанса движка diff --git a/res/modules/schedule.lua b/res/modules/schedule.lua new file mode 100644 index 00000000..24d216f0 --- /dev/null +++ b/res/modules/schedule.lua @@ -0,0 +1,46 @@ +local Schedule = { + __index = { + set_interval = function(self, ms, callback, repetions) + local id = self._next_interval + self._intervals[id] = { + last_called = 0.0, + delay = ms / 1000.0, + callback = callback, + repetions = repetions, + } + self._next_interval = id + 1 + return id + end, + tick = function(self, dt) + local timer = self._timer + dt + for id, interval in pairs(self._intervals) do + if timer - interval.last_called >= interval.delay then + xpcall(interval.callback, function(s) + debug.error(s..'\n'..debug.traceback()) + end) + interval.last_called = timer + local repetions = interval.repetions + if repetions then + if repetions <= 1 then + self:remove_interval(id) + else + interval.repetions = repetions - 1 + end + end + end + end + self._timer = timer + end, + remove_interval = function (self, id) + self._intervals[id] = nil + end + } +} + +return function () + return setmetatable({ + _next_interval = 1, + _timer = 0.0, + _intervals = {}, + }, Schedule) +end diff --git a/res/scripts/stdlib.lua b/res/scripts/stdlib.lua index a5142fdf..b0fb86ea 100644 --- a/res/scripts/stdlib.lua +++ b/res/scripts/stdlib.lua @@ -470,8 +470,37 @@ function __vc_on_hud_open() hud.open_permanent("core:ingame_chat") end +local ScheduleGroup_mt = { + __index = { + publish = function(self, schedule) + local id = self._next_schedule + self._schedules[id] = schedule + self._next_schedule = id + 1 + end, + tick = function(self, dt) + for id, schedule in pairs(self._schedules) do + schedule:tick(dt) + end + end, + remove = function(self, id) + self._schedules[id] = nil + end + } +} + +local function ScheduleGroup() + return setmetatable({ + _next_schedule = 1, + _schedules = {}, + }, ScheduleGroup_mt) +end + +time.schedules = {} + local RULES_FILE = "world:rules.toml" function __vc_on_world_open() + time.schedules.world = ScheduleGroup() + if not file.exists(RULES_FILE) then return end @@ -481,6 +510,10 @@ function __vc_on_world_open() end end +function __vc_on_world_tick(tps) + time.schedules.world:tick(1.0 / tps) +end + function __vc_on_world_save() local rule_values = {} for name, rule in pairs(rules.rules) do @@ -650,4 +683,4 @@ function dofile(path) end end return _dofile(path) -end \ No newline at end of file +end diff --git a/res/scripts/stdmin.lua b/res/scripts/stdmin.lua index 9f8c48c2..38fdbfdc 100644 --- a/res/scripts/stdmin.lua +++ b/res/scripts/stdmin.lua @@ -20,6 +20,18 @@ if not ipairs_mt_supported then end end +function await(co) + local res, err + while coroutine.status(co) ~= 'dead' do + coroutine.yield() + res, err = coroutine.resume(co) + if err then + return res, err + end + end + return res, err +end + local _ffi = ffi function __vc_Canvas_set_data(self, data) if type(data) == "cdata" then diff --git a/src/logic/BlocksController.cpp b/src/logic/BlocksController.cpp index cdc25b48..ee74bb95 100644 --- a/src/logic/BlocksController.cpp +++ b/src/logic/BlocksController.cpp @@ -127,7 +127,7 @@ void BlocksController::update(float delta, uint padding) { onBlocksTick(blocksTickClock.getPart(), blocksTickClock.getParts()); } if (worldTickClock.update(delta)) { - scripting::on_world_tick(); + scripting::on_world_tick(worldTickClock.getTickRate()); } } diff --git a/src/logic/scripting/scripting.cpp b/src/logic/scripting/scripting.cpp index f766f29d..39168843 100644 --- a/src/logic/scripting/scripting.cpp +++ b/src/logic/scripting/scripting.cpp @@ -297,8 +297,12 @@ void scripting::on_world_load(LevelController* controller) { } } -void scripting::on_world_tick() { +void scripting::on_world_tick(int tps) { auto L = lua::get_main_state(); + if (lua::getglobal(L, "__vc_on_world_tick")) { + lua::pushinteger(L, tps); + lua::call_nothrow(L, 1, 0); + } for (auto& pack : content_control->getAllContentPacks()) { lua::emit_event(L, pack.id + ":.worldtick"); } diff --git a/src/logic/scripting/scripting.hpp b/src/logic/scripting/scripting.hpp index 0f375746..bae5aac6 100644 --- a/src/logic/scripting/scripting.hpp +++ b/src/logic/scripting/scripting.hpp @@ -70,7 +70,7 @@ namespace scripting { ); void on_world_load(LevelController* controller); - void on_world_tick(); + void on_world_tick(int tps); void on_world_save(); void on_world_quit(); void cleanup();