2024-09-01 20:49:41 +00:00
|
|
|
|
--######################## 独立事件系统,用于提供信号机制拥有独立处理事件的功能 ########################
|
2023-07-14 19:35:33 +00:00
|
|
|
|
local XEventSystem = XClass(nil, "XEventSystem")
|
|
|
|
|
|
|
|
|
|
function XEventSystem:Ctor( )
|
|
|
|
|
self._handlers = {}
|
|
|
|
|
end
|
|
|
|
|
|
2024-09-01 20:49:41 +00:00
|
|
|
|
-- 注册事件
|
2023-07-14 19:35:33 +00:00
|
|
|
|
-- @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, ...)
|
2024-09-01 20:49:41 +00:00
|
|
|
|
-- 如果被通知的事件方法有返回值并注册时显示传入返回结果的key,丢进callResult里返回调用者进行下一步处理
|
2023-07-14 19:35:33 +00:00
|
|
|
|
if tmpResult and msgData.returnArgKey then
|
|
|
|
|
if callResult == nil then callResult = {} end
|
2024-09-01 20:49:41 +00:00
|
|
|
|
-- 小优化:可判断是否存在相同key进行报错或警告处理
|
2023-07-14 19:35:33 +00:00
|
|
|
|
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,
|
2024-09-01 20:49:41 +00:00
|
|
|
|
EMPTY_FROM_OBJ = 1, -- 等待信号的对象已被置为null
|
|
|
|
|
RELEASE = 2, -- 信号拥有者已被释放,如果是XLuaUi被关闭时也会触发此code,因为有部分Ui代码直接判断RELEASE来获取Ui是否关闭
|
|
|
|
|
EMPTY_UI = 3, -- 针对Ui,Ui信号拥有者为null。PS:这块可以统一成EMPTY_FROM_OBJ
|
2023-07-14 19:35:33 +00:00
|
|
|
|
}
|
2024-09-01 20:49:41 +00:00
|
|
|
|
---@class XSignalData
|
2023-07-14 19:35:33 +00:00
|
|
|
|
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
|
|
|
|
|
|
2024-09-01 20:49:41 +00:00
|
|
|
|
-- 等待信号,必选放在RunAsyn方法内才会起作用
|
|
|
|
|
-- signalName: 等待的信号名
|
|
|
|
|
-- fromObj: 在等待该信号的对象
|
2023-07-14 19:35:33 +00:00
|
|
|
|
function XSignalData:AwaitSignal(signalName, fromObj)
|
|
|
|
|
self.SignalMap[signalName] = self.SignalMap[signalName] or {}
|
2024-09-01 20:49:41 +00:00
|
|
|
|
-- 获取正在运行的协程
|
2023-07-14 19:35:33 +00:00
|
|
|
|
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
|
|
|
|
|
|
2024-09-01 20:49:41 +00:00
|
|
|
|
-- 发射信号,通知已注册的事件(ConnectSignal)调用回调或解除已挂起的协程(AwaitSignal)继续往下运行
|
2023-07-14 19:35:33 +00:00
|
|
|
|
function XSignalData:EmitSignal(signalName, ...)
|
|
|
|
|
local result = nil
|
|
|
|
|
-- 发送事件
|
|
|
|
|
result = self.EventSystem:Call(signalName, ...)
|
|
|
|
|
-- 处理信号
|
|
|
|
|
local signalData = self.SignalMap[signalName]
|
2024-09-01 20:49:41 +00:00
|
|
|
|
-- 如果没有对应的信号处理直接返回
|
2023-07-14 19:35:33 +00:00
|
|
|
|
if XTool.IsTableEmpty(signalData) then
|
|
|
|
|
return result
|
|
|
|
|
end
|
|
|
|
|
local tmpData, code
|
|
|
|
|
for i = #signalData, 1, -1 do
|
|
|
|
|
tmpData = signalData[i]
|
2024-09-01 20:49:41 +00:00
|
|
|
|
-- 如果状态没有运行完主体函数或因错误停止的话,恢复协程
|
2023-07-14 19:35:33 +00:00
|
|
|
|
if coroutine.status(tmpData.running) ~= "dead" then
|
2024-09-01 20:49:41 +00:00
|
|
|
|
-- 可优化的点:如果fromObj为nil的时候直接不处理即可,但这里需要测试一下不处理后积攒了大量挂起的协程会有什么影响,目前这个写法判空是为了尽量让所有活着的协程都尽量恢复。
|
2023-07-14 19:35:33 +00:00
|
|
|
|
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
|
|
|
|
|
|
2024-09-01 20:49:41 +00:00
|
|
|
|
-- 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
|
2023-07-14 19:35:33 +00:00
|
|
|
|
|
|
|
|
|
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
|