BaseController = BaseController or BaseClass() local BaseController = BaseController local UserMsgAdapter = UserMsgAdapter BaseController.request_protocals_list = Array.New() BaseController.handle_protocals = {} --处理的指令列表 BaseController.is_delay_send_protocal = false function BaseController:__init() --销毁引用 local function onViewDestoryHandler(view) self:clearViewQuoted(view) end self.clearViewQuoted_BindId = self:Bind(BaseView.DestroyEvent,onViewDestoryHandler) end function BaseController:RemoveCheckOutEvent() end function BaseController:EnableCheckoutClear(bool) bool = bool == nil and true or bool self.checkout_clear_view = bool if bool then self.view_list = {} --依附于该控制器的界面 setmetatable(self.view_list, {__mode = "v"}) --弱引用 --创建窗口 local function onViewCreateHandler(list) self:addView(list) end self.CreateView_BindId = self:Bind(BaseView.CreateView,onViewCreateHandler) local function close_view() for k,v in pairs(self.view_list) do v:Close() end self:RemoveCheckOutEvent() end self:Bind(EventName.CHANGE_ACCOUNT, close_view) self:Bind(EventName.CHANGE_ROLE, close_view) end end function BaseController:__delete() if GlobalEventSystem then if self.clearViewQuoted_BindId then GlobalEventSystem:UnBind(self.clearViewQuoted_BindId) self.clearViewQuoted_BindId = nil end if self.CreateView_BindId then GlobalEventSystem:UnBind(self.CreateView_BindId) self.CreateView_BindId = nil end end if self.checkout_timer then for k, timer_id in pairs(self.checkout_timer) do GlobalTimerQuest:CancelQuest(timer_id) end self.checkout_timer = nil end end function BaseController:addView(list) if list and list[1] then for k,v in pairs(self) do if list[1] == v then table.insert(self.view_list,v) return end end end end --清除view的引用 function BaseController:clearViewQuoted(view) -- if self._class_type._source == view._source then --只检测baseitem local function checkBaseItem(table) --判断纯队列 或者单个baseitem if type(table) == "table" and (not table._class_type or table._class_type.Class_Type == "BaseItem" or table._is_uitabwindow) then if (table._class_type and table._class_type.Class_Type == "BaseItem") or table._is_uitabwindow then if not table._use_delete_method then if table._is_uitabwindow then GlobalEventSystem:Fire(LuaErrorModel.SEND_LUAERROR_MESSAGE,"BaseView没清除UITabWindow") else GlobalEventSystem:Fire(LuaErrorModel.SEND_LUAERROR_MESSAGE,view._class_type._source.."有baseitem没deleteMe " .. table._class_type._source) end end else for k, value in pairs(table) do checkBaseItem(value) end end end return end for k,v in pairs(self) do if view == v then -- for p,value in pairs(view) do -- if type(value) == "number" then -- if EventSystem.checkHasUnBind(value) then -- Alert.show(view._class_type._source.."有事件没解绑 "..p) -- end -- end -- end if not Application.isMobilePlatform then for p,value in pairs(view) do if type(value) == "table" and (not value._class_type or value._class_type.Class_Type == "BaseItem" or value._is_uitabwindow) then checkBaseItem(value) end end end self[k] = nil break end end -- end end function BaseController:AddSendFmtCheckout(cmd, time, callback) self.checkout_timer = self.checkout_timer or {} local timer_id = self.checkout_timer[cmd] if timer_id then GlobalTimerQuest:CancelQuest(timer_id) timer_id = nil end timer_id = GlobalTimerQuest:AddDelayQuest(callback,time) self.checkout_timer[cmd] = timer_id end function BaseController:RemoveSendFmtCheckout(cmd) if self.checkout_timer then local timer_id = self.checkout_timer[cmd] if timer_id then GlobalTimerQuest:CancelQuest(timer_id) self.checkout_timer[cmd] = nil end end end --[[@ 功能: 注册一条协议的响应函数 参数: id 协议号 func_name 继承ProtocalBase类的成员函数名称 返回值: 无 其它: 无 作者: raowei ]] function BaseController:RegisterProtocal(id, func_name) local register_func = nil register_func = function(data_list) local oper_func = self[func_name] if oper_func then oper_func(self, data_list) end if self.checkout_timer then local timer_id = self.checkout_timer[id] if timer_id then GlobalTimerQuest:CancelQuest(timer_id) self.checkout_timer[id] = nil end end end UserMsgAdapter.RegisterMsgOperate(id, register_func) end --[[@ 功能: 根据格式化字符串发送协议内容到Game服 参数: cmd 指令号 int fmt_str 格式化字符串(说明请参照文件头部) string ... 发送到协议中的字段 返回值: 无 其它: 无 作者: raowei ]] function BaseController.SendFmtFromDelayList(vo) if vo == nil then return end local cmd = vo.cmd local fmt_str = vo.fmt_str local arg_list = vo.args UserMsgAdapter.WriteBegin(cmd) if fmt_str ~= nil then UserMsgAdapter.WriteFMT(fmt_str, unpack(vo.args)) end UserMsgAdapter.SendToGame() end function BaseController:RegisterProtocal2(id, func) UserMsgAdapter.RegisterMsgOperate(id, func) end function BaseController:SendFmtToGame(cmd, fmt_str, ...) -- if cmd == 32007 or cmd == 12005 or cmd == 12002 or cmd == 61105 then -- if not LoginModel:getInstance().show_loading_state or cmd == 32007 or cmd == 12005 or cmd == 12002 or cmd == 61105 then -- BaseController.SendFmtFromDelayList(vo) -- else if ... and cmd ~= 12001 then--12001走路发的协议 print("send cmd log: ",cmd,fmt_str,...) end if BaseController.is_delay_send_protocal or BaseController.request_protocals_list:GetSize() > 0 then local vo = {cmd = cmd, fmt_str = fmt_str, args = {...}} BaseController.request_protocals_list:PushBack(vo) else UserMsgAdapter.SendAllFmtToGame(cmd, fmt_str, ...) end -- end end function BaseController:SendAllFmtToGame2(cmd, fmt_str, ...) UserMsgAdapter.SendAllFmtToGame2(cmd, fmt_str, ...) end function BaseController.DelaySendFmtToGame() if BaseController.request_protocals_list:GetSize() > 0 then local vo = BaseController.request_protocals_list:PopFront() UserMsgAdapter.SendAllFmtToGame(vo.cmd, vo.fmt_str, unpack(vo.args)) end end function BaseController:WriteBegin(cmd) UserMsgAdapter.WriteBegin(cmd) end function BaseController:WriteFMT(fmt_str, ...) UserMsgAdapter.WriteFMT(fmt_str, ...) end function BaseController:SendToGame() UserMsgAdapter.SendToGame() end --[[@ 功能: 从协议缓冲区按格式化字符串读取内容 参数: fmt_str 格式化字符串(参见文件头部说明) string 返回值: 根据格式化字符串返回多个值 其它: 无 作者: raowei ]] function BaseController:ReadFmt(fmt_str) return UserMsgAdapter.ReadFmt(fmt_str) end --[[@ 功能: 绑定事件 参数: 绑定的id,绑定的函数 返回值: 一个事件的handler 其它: 无 作者: raowei ]] function BaseController:Bind(event_id, event_func,class_name) return GlobalEventSystem:Bind(event_id,event_func,class_name) end --[[@ 功能: 解除绑定事件 参数: 事件的handler 返回值: 无 其它: 无 作者: raowei ]] function BaseController:UnBind( obj ) GlobalEventSystem:UnBind( obj ) end --[[@ 功能: 立即触发事件 参数: 绑定的id,绑定的函数 返回值: 绑定的id,传递的参数 其它: 无 作者: raowei ]] function BaseController:Fire(event_id,...) GlobalEventSystem:Fire( event_id ,...) end --[[@ 功能: 下一帧触发事件 参数: 绑定的id,绑定的函数 返回值: 绑定的id,传递的参数 其它: 无 作者: raowei ]] function BaseController:FireNextFrame(event_id,...) GlobalEventSystem:FireNextFrame( event_id ,...) end --生成model的绑定请求协议的事件 local function BindReqEvent(self, id, req_event_data, register_func) local req_event_failed_str = "req net proto " .. id .. " failed with fire event " .. req_event_data[1] local function on_req( ... ) req_event_data[2] = req_event_data[2] or "" local need_print = self.proto_info[id].show_print local arge = {req_event_data[2], ...} if need_print then local arge_str = "" if #arge > 0 then for i,v in ipairs(arge) do arge_str = arge_str.."[No."..(i)..":"..tostring(arge[i]).."] " end else arge_str = "none arge" end print('Cat:BaseController.lua req net proto : '..id.." arges:", arge_str) --网络协议的参数数量不对!比如"cii"就只需要传入3个,具体传了哪些见上条打印 local has_proto_field_desc = (arge[1] and type(arge[1])=="string") local need_assert = has_proto_field_desc and (#arge[1] ~= #arge - 1) if need_assert then local error_str = req_event_failed_str..":proto fields num not match!" assert(false, error_str) end end if not self.proto_info[id].is_debug then if self.proto_info[id].req_func then table.remove(arge,1) self.proto_info[id].req_func( self, unpack(arge)) else self:SendFmtToGame(id, unpack(arge)) end else self.proto_info[id].req_arge = arge register_func() end end self.model:Bind(req_event_data[1], on_req) end --自动生成发送和收到网络协议的处理代码,注:之所以是local function就是不想外部调用 local function RegisterProtocalAndReqEvent(self, id, req_event_data) local register_func = function() local proto = self.proto_info[id] if proto.handler_manual then proto.handler_manual(self) return end if proto.show_print then assert(_G["SCMD"..id]~=nil, "cannot find SCMD"..id..".lua, have you generated this proto file yet?") end --Cat_Todo : 这里可以考虑用个协议对象池,这样就不需要频繁new协议对象了 local scmd = _G["SCMD"..id].New(not proto.is_debug) if proto.is_debug and self.CreateDebugSCMD then --如果该协议的is_debug属性为true,调用handler前将进入CreateDebugSCMD函数,在这里返回一个调试用的结构体模仿收到网络信息,之所以放在一个函数里处理是因为想删掉时可以方便点 scmd = self:CreateDebugSCMD(id, scmd, self.proto_info[id].req_arge) end if proto.show_print then print("Cat:BaseController [start:handle"..id..(proto.is_debug and " Debug Model!" or "").."] scmd:", scmd) PrintTable(scmd) print("Cat:BaseController [end]") end -- local handler = proto.handler or self["Handle"..id] if proto.handler then proto.handler(self, scmd, id) end end UserMsgAdapter.RegisterMsgOperate(id, register_func) if req_event_data and req_event_data[1] then BindReqEvent(self, id, req_event_data, register_func) end end --[[说明: 本套做法好处:不再需要手写代码绑定Req请求协议的代码,且发送Req请求协议事件时可以传入收到回复协议的回调函数,所以不再需要触发和监听Ack收到协议的事件;调试代码集中在CreateDebugSCMD函数里方便管理;可以针对某协议开关调试和打印信息,show_print为true时除了打印请求和收到的协议内容,还会判定请求的协议内容是否有问题信息,建议前期开启方便定位bug 具体用法:配置proto_info: )key值为协议id )req_event_data会生成self.model的绑定事件,具体生成什么代码见BaseController,为nil就不生成;想请求该协议时可: req_event_data第一个参数是事件名,后续参数可选,第二个参数必须为 协议内容的格式字符串 )handler就是收到后端回复时的处理函数,触发时机在CreateDebugSCMD之后 )handler_manual就是收到后端回复时的手写处理函数,会绕过部分功能比如CreateDebugSCMD )req_func就是适应部分自己处理发协议的事件响应,没有的话就用默认方式发,有的话会返回{self, 内容} )is_debug为true的话将不发送协议,直接回调收到协议的处理函数; )show_print控制是否在请求和收到协议时打印相关信息 ]]-- function BaseController:RegisterProtocalByCFG(proto_info) self.proto_info = proto_info self.ack_call_back = self.ack_call_back or {} for k,v in pairs(self.proto_info) do RegisterProtocalAndReqEvent(self, k, v.req_event_data) end end --打开界面 function BaseController:OpenView( class_name, show, ... ) if show then if not self[class_name] then self[class_name] = _G[class_name].New() end if not self[class_name]:HasOpen() then self[class_name]:Open(...) end else if self[class_name] then self[class_name]:Close(...) end end end