diff options
Diffstat (limited to 'script-beta/pub/pub.lua')
-rw-r--r-- | script-beta/pub/pub.lua | 236 |
1 files changed, 236 insertions, 0 deletions
diff --git a/script-beta/pub/pub.lua b/script-beta/pub/pub.lua new file mode 100644 index 00000000..2cb1b4e8 --- /dev/null +++ b/script-beta/pub/pub.lua @@ -0,0 +1,236 @@ +local thread = require 'bee.thread' +local utility = require 'utility' +local await = require 'await' +local timer = require 'timer' + +local errLog = thread.channel 'errlog' +local type = type +local counter = utility.counter() + +local braveTemplate = [[ +package.path = %q +package.cpath = %q + +collectgarbage 'generational' + +log = require 'brave.log' + +dofile(%q) +local brave = require 'brave' +brave.register(%d) +]] + +---@class pub +local m = {} +m.type = 'pub' +m.braves = {} +m.ability = {} +m.taskQueue = {} + +--- 注册酒馆的功能 +function m.on(name, callback) + m.ability[name] = callback +end + +--- 招募勇者,勇者会从公告板上领取任务,完成任务后到看板娘处交付任务 +---@param num integer +function m.recruitBraves(num) + for _ = 1, num do + local id = #m.braves + 1 + log.info('Create brave:', id) + thread.newchannel('taskpad' .. id) + thread.newchannel('waiter' .. id) + m.braves[id] = { + id = id, + taskpad = thread.channel('taskpad' .. id), + waiter = thread.channel('waiter' .. id), + thread = thread.thread(braveTemplate:format( + package.path, + package.cpath, + (ROOT / 'debugger.lua'):string(), + id + )), + taskMap = {}, + currentTask = nil, + memory = 0, + } + end +end + +--- 勇者是否有空 +function m.isIdle(brave) + return next(brave.taskMap) == nil +end + +--- 给勇者推送任务 +function m.pushTask(brave, info) + if info.removed then + return false + end + brave.taskpad:push(info.name, info.id, info.params) + brave.taskMap[info.id] = info + --log.info(('Push task %q(%d) to # %d, queue length %d'):format(info.name, info.id, brave.id, #m.taskQueue)) + return true +end + +--- 从勇者处接收任务反馈 +function m.popTask(brave, id, result) + local info = brave.taskMap[id] + if not info then + log.warn(('Brave pushed unknown task result: # %d => [%d]'):format(brave.id, id)) + return + end + brave.taskMap[id] = nil + --log.info(('Pop task %q(%d) from # %d'):format(info.name, info.id, brave.id)) + m.checkWaitingTask(brave) + if not info.removed then + info.removed = true + if info.callback then + xpcall(info.callback, log.error, result) + end + end +end + +--- 从勇者处接收报告 +function m.popReport(brave, name, params) + local abil = m.ability[name] + if not abil then + log.warn(('Brave pushed unknown report: # %d => %q'):format(brave.id, name)) + return + end + xpcall(abil, log.error, params, brave) +end + +--- 发布任务 +---@parma name string +---@param params any +function m.awaitTask(name, params) + local info = { + id = counter(), + name = name, + params = params, + } + for _, brave in ipairs(m.braves) do + if m.isIdle(brave) then + if m.pushTask(brave, info) then + return await.wait(function (waker) + info.callback = waker + end) + else + return nil + end + end + end + -- 如果所有勇者都在战斗,那么把任务缓存到队列里 + -- 当有勇者提交任务反馈后,尝试把按顺序将堆积任务 + -- 交给该勇者 + m.taskQueue[#m.taskQueue+1] = info + --log.info(('Add task %q(%d) in queue, length %d.'):format(name, info.id, #m.taskQueue)) + return await.wait(function (waker) + info.callback = waker + end) +end + +--- 发布同步任务,如果任务进入了队列,会返回执行器 +--- 通过 jumpQueue 可以插队 +---@parma name string +---@param params any +---@param callback function +function m.task(name, params, callback) + local info = { + id = counter(), + name = name, + params = params, + callback = callback, + } + for _, brave in ipairs(m.braves) do + if m.isIdle(brave) then + m.pushTask(brave, info) + return nil + end + end + -- 如果所有勇者都在战斗,那么把任务缓存到队列里 + -- 当有勇者提交任务反馈后,尝试把按顺序将堆积任务 + -- 交给该勇者 + m.taskQueue[#m.taskQueue+1] = info + --log.info(('Add task %q(%d) in queue, length %d.'):format(name, info.id, #m.taskQueue)) + return info +end + +--- 插队 +function m.jumpQueue(info) + for i = 2, #m.taskQueue do + if m.taskQueue[i] == info then + m.taskQueue[i] = nil + table.move(m.taskQueue, 1, i - 1, 2) + m.taskQueue[1] = info + return + end + end +end + +--- 移除任务 +function m.remove(info) + info.removed = true + for i = 1, #m.taskQueue do + if m.taskQueue[i] == info then + table.remove(m.taskQueue[i], i) + return + end + end +end + +--- 检查堆积任务 +function m.checkWaitingTask(brave) + if #m.taskQueue == 0 then + return + end + -- 如果勇者还有其他活要忙,那么让他继续忙去吧 + if next(brave.taskMap) then + return + end + while #m.taskQueue > 0 do + local info = table.remove(m.taskQueue, 1) + if m.pushTask(brave, info) then + break + end + end +end + +--- 接收反馈 +---|返回接收到的反馈数量 +---@return integer +function m.recieve() + for _, brave in ipairs(m.braves) do + while true do + local suc, id, result = brave.waiter:pop() + if not suc then + goto CONTINUE + end + if type(id) == 'string' then + m.popReport(brave, id, result) + else + m.popTask(brave, id, result) + end + end + ::CONTINUE:: + end +end + +--- 检查伤亡情况 +function m.checkDead() + while true do + local suc, err = errLog:pop() + if not suc then + break + end + log.error('Brave is dead!: ' .. err) + end +end + +function m.step() + m.checkDead() + m.recieve() +end + +return m |