PGRData/Script/matrix/xentity/XSignalData.lua
2024-09-01 22:49:41 +02:00

200 lines
6.8 KiB
Lua
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

--######################## 独立事件系统,用于提供信号机制拥有独立处理事件的功能 ########################
local XEventSystem = XClass(nil, "XEventSystem")
function XEventSystem:Ctor( )
self._handlers = {}
end
-- 注册事件
-- @params keepEvent : bool 常驻事件,不会被清除
function XEventSystem:Push( eventType, caller, callback, keepEvent, returnArgKey)
assert(eventType and callback, "XEventSystem:Push fail. eventType or callback cannot be empty")
local handler = self._handlers[eventType]
if not handler then
handler = {}
self._handlers[eventType] = handler
end
table.insert(
handler,
1,
{
eventType = eventType,
caller = caller,
callback = callback,
keepEvent = keepEvent,
returnArgKey = returnArgKey
}
)
end
function XEventSystem:_PopWithHandler( handler, ignoreCaller, caller )
if not handler then
return
end
for i = #handler, 1, -1 do
if ignoreCaller or handler[i].caller == caller then
-- 判断是否常驻事件
if not handler[i].keepEvent then
table.remove(handler, i)
end
end
end
end
function XEventSystem:PopWithEventType( eventType )
self:_PopWithHandler(self._handlers[eventType], true)
end
function XEventSystem:PopWithCaller( caller )
for _, handler in pairs(self._handlers) do
self:_PopWithHandler(handler, false, caller)
end
end
function XEventSystem:Call( eventType, ... )
local callResult = nil
local handler = self._handlers[eventType]
if nil == handler then
return callResult
end
local msgData
for i=#handler, 1, -1 do
msgData = handler[i]
if msgData then
local tmpResult = msgData.callback(msgData.caller, ...)
-- 如果被通知的事件方法有返回值并注册时显示传入返回结果的key丢进callResult里返回调用者进行下一步处理
if tmpResult and msgData.returnArgKey then
if callResult == nil then callResult = {} end
-- 小优化:可判断是否存在相同key进行报错或警告处理
callResult[msgData.returnArgKey] = tmpResult
end
end
end
return callResult
end
function XEventSystem:ClearAll()
for _, handler in pairs(self._handlers) do
self:_PopWithHandler(handler, true)
end
end
--######################## 信号数据 ########################
XSignalCode = {
SUCCESS = 0,
EMPTY_FROM_OBJ = 1, -- 等待信号的对象已被置为null
RELEASE = 2, -- 信号拥有者已被释放如果是XLuaUi被关闭时也会触发此code因为有部分Ui代码直接判断RELEASE来获取Ui是否关闭
EMPTY_UI = 3, -- 针对UiUi信号拥有者为null。PS:这块可以统一成EMPTY_FROM_OBJ
}
---@class XSignalData
XSignalData = XClass(nil, "XSignalData")
function XSignalData:Ctor()
self.SignalMap = {}
self.EventSystem = XEventSystem.New()
end
function XSignalData:ConnectSignal(signalName, caller, callback, returnArgKey)
if self.EventSystem == nil then return end
self.EventSystem:Push(signalName, caller, callback, nil, returnArgKey)
end
function XSignalData:RemoveConnectSignalWithName(signalName)
if self.EventSystem == nil then return end
self.EventSystem:PopWithEventType(signalName)
end
-- 等待信号必选放在RunAsyn方法内才会起作用
-- signalName: 等待的信号名
-- fromObj: 在等待该信号的对象
function XSignalData:AwaitSignal(signalName, fromObj)
self.SignalMap[signalName] = self.SignalMap[signalName] or {}
-- 获取正在运行的协程
local running = coroutine.running()
-- 注册正在挂起的协程
table.insert(self.SignalMap[signalName], {
running = running,
fromObj = fromObj
})
-- 挂起协程,直到等到信号回来
return coroutine.yield()
end
-- 检查是否有signalName信号如果不传fromObj则不判断fromObj
function XSignalData:CheckHasSignal(signalName, fromObj)
local signalData = self.SignalMap[signalName]
if not signalData then return false end
if fromObj == nil then return true end
for _, v in ipairs(signalData) do
if v.fromObj == fromObj then
return true
end
end
return false
end
-- 发射信号通知已注册的事件ConnectSignal调用回调或解除已挂起的协程AwaitSignal继续往下运行
function XSignalData:EmitSignal(signalName, ...)
local result = nil
-- 发送事件
result = self.EventSystem:Call(signalName, ...)
-- 处理信号
local signalData = self.SignalMap[signalName]
-- 如果没有对应的信号处理直接返回
if XTool.IsTableEmpty(signalData) then
return result
end
local tmpData, code
for i = #signalData, 1, -1 do
tmpData = signalData[i]
-- 如果状态没有运行完主体函数或因错误停止的话,恢复协程
if coroutine.status(tmpData.running) ~= "dead" then
-- 可优化的点如果fromObj为nil的时候直接不处理即可但这里需要测试一下不处理后积攒了大量挂起的协程会有什么影响目前这个写法判空是为了尽量让所有活着的协程都尽量恢复。
code = tmpData.fromObj ~= nil and XSignalCode.SUCCESS or XSignalCode.EMPTY_FROM_OBJ
coroutine.resume(tmpData.running, code, ...)
end
table.remove(signalData, i)
end
signalData = nil
self.SignalMap[signalName] = nil
return result
end
-- function XSignalData:EmitSingleSignal(signalName, ...)
-- local signalData = self.SignalMap[signalName]
-- if XTool.IsTableEmpty(signalData) then
-- return
-- end
-- local code
-- local tmpData = signalData[#signalData]
-- if coroutine.status(tmpData.running) ~= "dead" then
-- code = tmpData.fromObj ~= nil and XSignalCode.SUCCESS or XSignalCode.EMPTY_FROM_OBJ
-- coroutine.resume(tmpData.running, code, ...)
-- end
-- table.remove(signalData, #signalData)
-- if #signalData <= 0 then
-- signalData = nil
-- self.SignalMap[signalName] = nil
-- end
-- end
function XSignalData:Release()
if self.EventSystem then
self.EventSystem:ClearAll()
self.EventSystem = nil
end
if self.SignalMap == nil then
return
end
-- 释放时,全部发送出去,避免挂着无效的程序,错误由收信号方处理
for name, signalData in pairs(self.SignalMap) do
for i = #signalData, 1, -1 do
if coroutine.status(signalData[i].running) ~= "dead" then
coroutine.resume(signalData[i].running, XSignalCode.RELEASE)
end
table.remove(signalData, i)
end
signalData = nil
end
self.SignalMap = nil
end