UnityFS5.x.x2022.3.38f1 A[CQ `@ TCAB-3f1f8f4d805b080506d56e62e209bde0Y T2022.3.38f11Hk]jd0X O7H11@QjH11@Qj_FZWȴ-OBIr:7H11@Qj 11@yj  .$ - 11  jH11@Qj9 CPy\ .$9bCPy\ .$nH 11!@"Qj#$11%&Hj'11(@)Qj*L+@,-.11/0j1H2113@4Qj5H 6117@8Qj9AssetBundlem_PreloadTablem_FileIDm_PathIDm_ContainerAssetInfopreloadIndexpreloadSizeassetm_MainAssetm_RuntimeCompatibilitym_AssetBundleNamem_Dependenciesm_IsStreamedSceneAssetBundlem_ExplicitDataLayoutm_PathFlagsm_SceneHashes't `c ,OWdMAPd ):KBtׄp4޾$P8WNNT]טCSY`[?M ٞ|i5ڎy, 52M`wEg}K-c(H<1뽘Jh :k\0hUlviZ~iaFĞlC1՚XuDV(Kw8P+h-b*| {.Ǥa"b-pcH0 6AǨxO 8٨8o\Zϯhr`q2PgUaAgZk߳gೈrt^0; ʠ&DEԗ43eF8`꽘f"2  fh>0, yP6h5tw<06dSm<øD lNMHİJ*6t4DqvX gvX utl C 6Xh\m<5SwNwsژ yTx<z)ܚ>܀+BGN$xX<D*`s'ߠAt) W=HELw=hE;+0Hl  ͕RHКAW+L4 Y 6jV)(Z &ÒSpf$43K>LwTHl3~` 3%T/lT]58vX`JWT>g(L?}5~.(4KTs_m`<d^Q 4/޽h ?GI נ>|@dX'b)Lz '8鄠> * a  05Tұx*@ 5X0 PRyB4,Z|-H27ʎAP<`RD ͓GU,/F@58B#|#<۝L0 XJdZHh6)Z'F5H@o۹ < *Gt3IFA`HX$n.sl,<a lACX dɄO'#xD|Ca$v&@%B)l &b점=*pчK* k+$V+ ",8GyDI-2T9w'.X6rq5/EP.l p0`s4!؎@p0s0(Ȍsd1ț$N0\cD3 sIO3<Tu#3_k30#g2~b'5\06 x`#E7(̓i7[]L9T aiN94 t -:PC H(!V`;C 4gp_d ¦J#cd ~PIe xjfc 0 then local last for i = len, 1, -1 do local layer = stack[i] if layer.__maskClickCb then last = layer break end end if last then local cb = last.__maskClickCb if cb then if last.__maskClickOnce then last.__maskClickCb = nil end cb() return true end end end end util.ugui.addClickEvent(mask, function() if not checkClick(globalUIRoot) then -- 先处理global的click,再处理local的click checkClick(localUIRoot) end end) mask.__listenerAdded = true end self.__maskClickCb = clickCb self.__maskClickOnce = once return self end function UILayer:hideMask() local mask = self[UI_MASK_NAME] if mask then UnityEngine.GameObject.Destroy(mask) self[UI_MASK_NAME] = nil end UILayerUtil:refreshMask() end function UILayer:enableCloseWhenClickMask(cb) self:setMaskClickCb(function() if cb then cb() end self:close() end, true) return self end ---@private function UILayer:__saveEnabledRaycastTargets() if self.__uiContainer then self.__enabledRaycastTargets__ = {} local coms = self.__uiContainer:GetComponentsInChildren(typeof(UGUI.MaskableGraphic)) for _, com in cs_ipairs(coms) do table.insert(self.__enabledRaycastTargets__, com) end end end function UILayer:disableAllUITouch() if self.__uiContainer then self:__saveEnabledRaycastTargets() local coms = self.__uiContainer:GetComponentsInChildren(typeof(UGUI.MaskableGraphic)) for _, com in cs_ipairs(coms) do if com.gameObject ~= self[UI_MASK_NAME] then if com.raycastTarget then com.raycastTarget = false end end end end end function UILayer:enableAllUITouch() if self.__uiContainer then -- 只enable默认打开touch的 for _, com in ipairs(self.__enabledRaycastTargets__ or {}) do com.raycastTarget = true end end end function UILayer:close(dontDestroy) self.dontDestroyOnExit = dontDestroy self:exit() return self end function UILayer:onExit() printInfo("UILayer", "onExit of %s", self.__cls_name) local dontDestroy = self.dontDestroyOnExit if not self.__rootUI then super.onExit(self) return end self.__uiContainerUpateId.remove() -- 关闭所有ui事件 self:disableAllUITouch() local bNeedDestroy = true for i = #(self.__closeCbs or {}), 1, -1 do if self.__closeCbs[i](self) then bNeedDestroy = false break end end if self.__tweenOnClose then self:__runTweenFade(1, 0, function() UnityEngine.GameObject.Destroy(self.__uiContainer) end) elseif not dontDestroy and bNeedDestroy then UnityEngine.GameObject.Destroy(self.__uiContainer) end self.__maskClickCb = nil local rootUI = self.__rootUI self.__rootUI = nil rootUI:removeUILayer(self) UILayerUtil:refreshMask() Msg.send(Msg.UI_LAYER_ON_EXIT, self) super.onExit(self) end -- 在onLoad中设置层级,会先调用onShow的setPriority,导致onLoad排序的时候后打开的ui层级index偏小 -- 需在ctor中设置 function UILayer:setPriority(priority) self.__uiContainer.__priority = priority self.__priority = priority if not self.__rootUI then return self end local parent = self.__uiContainer.transform.parent if not parent then return self end local list = {} local childCount = parent.childCount -- 遍历所有子节点 for i = 0, childCount - 1 do -- child 是 transform local child = parent:GetChild(i) table.insert(list, {transform = child, index = i, priority = child.gameObject.__priority}) end table.stableSort(list, function(l, r) if l.priority == r.priority then return l.index < r.index else return l.priority < r.priority end end) for i, content in ipairs(list) do content.transform:SetSiblingIndex(i-1) end return self end function UILayer:setVisible(b) if self.__uiContainer then self.__uiContainer:SetActive(b) end return self end function UILayer:setTag(tag) self.__tag = tag Msg.send(Msg.UI_LAYER_SET_TAG, self) return self end function UILayer:setStatTag(tag) self.__statTag = tag return self end function UILayer:setStatResource(resource) self.__resource = resource return self end function UILayer:getResource() return self.__resource end function UILayer:addCloseCallback(cb) self.__closeCbs = self.__closeCbs or {} table.insert(self.__closeCbs, cb) return self end function UILayer:removeCloseCalback(cb) if self.__closeCbs then table.removeByValue(self.__closeCbs, cb, true) end end ---@private ---更新 ExtendedRectTransform DeviceSafeArea=true 的子节点,适配 真正的安全框(留海屏) function UILayer:_refreshDeviceSafeArea() local rect,offsetMin, offsetMax = util.ugui.getSafeAreaRectInCanvasSpace(nil, UILayerUtil:getCanvas(), UILayerUtil:getCamera()) local coms = self.__uiContainer:GetComponentsInChildren(typeof(CS.ExtendedRectTransform)) for _, com in cs_ipairs(coms) do local child = com.gameObject local rectTransform = child[RectTransform] rectTransform.anchorMin = Vector2(0, 0) -- 设置左下角的 Anchor rectTransform.anchorMax = Vector2(1, 1) -- 设置右上角的 Anchor rectTransform.offsetMin = offsetMin rectTransform.offsetMax = offsetMax end end -- 手动设置,一般不需要,会根据priority排序,除非有特殊需求 function UILayer:setSiblingIndexMaunally(index) self.__uiContainer.transform:SetSiblingIndex(index) return self end function UILayer:getSceneArgs() return UILayerUtil:getCurScene():getSceneArgs() end ---@private ---响应屏幕尺寸变化 function UILayer:__resize() local __uinode = self.__uinode local canvas = __uinode:Seek("UICanvas") if canvas then UILayerUtil:calcCanvasScalerFactor(canvas) end self:_refreshDeviceSafeArea() end ---@private function UILayer:__update() if self.__uiPreScreenSize == nil then self.__uiPreScreenSize = UnityEngine.Vector2(UnityEngine.Screen.width, UnityEngine.Screen.height) else if self.__uiPreScreenSize.x ~= UnityEngine.Screen.width or self.__uiPreScreenSize.y ~= UnityEngine.Screen.height then self.__uiPreScreenSize = UnityEngine.Vector2(UnityEngine.Screen.width, UnityEngine.Screen.height) self:__resize() end end self:__tryUpdateKeyboardAdapt() end ---@private function UILayer:__tryUpdateKeyboardAdapt() if not self.isKeyboardAdaptEnable then return end local eventSystem = UILayerUtil:_getEventSystem() local go = eventSystem.currentSelectedGameObject if CS.LuaHelper.IsNull(go) or (not go) or (not go:GetComponent(typeof(UGUI.InputField))) or (not go:GetComponent(typeof(UGUI.InputField)).touchScreenKeyboard) then self.curInputFieldGo = nil self.__uiContainer.transform.localPosition = self.originalPos return end if go ~= self.curInputFieldGo then self.curInputFieldGo = go local trans = go.transform self.curInputFieldGoOriginalH = util.ugui.localSpaceToScreenSpace(UnityEngine.Vector2(0, -trans.rect.height / 2), trans, UILayerUtil:getCamera()).y end if not UnityEngine.TouchScreenKeyboard.area then return end local h = UnityEngine.TouchScreenKeyboard.area.height local dh = h - self.curInputFieldGoOriginalH if dh <= 0 then self.__uiContainer.transform.localPosition = self.originalPos return end local p = util.ugui.screenSpaceToLocalSpace(UnityEngine.Vector2(0, dh), self.__uiContainer:GetParent().transform, UILayerUtil:getCamera()) self.__uiContainer.transform.localPosition = self.originalPos + UnityEngine.Vector3(0, p.y, 0) end ---@private function UILayer:__runTweenScale(srcScale, dstScale) if self._pauseList then return end self.__tweenOnOpenGo:SetScalef(srcScale) self:disableAllUITouch() self.__tweenOnOpenGo:RunAction(ua.Sequence({ua.ease.BackOut(ua.ScaleTo(0.3, dstScale)), ua.cb(function() self:enableAllUITouch() end)})) end ---@private function UILayer:__runTweenFade(srcAlpha, dstAlpha, cb) local coms = self.__tweenOnOpenGo:GetComponentsInChildren(typeof(UGUI.Graphic)) local function setAlpha(alpha) for _, com in cs_ipairs(coms) do local color = com.color color.a = alpha com.color = color end end self:disableAllUITouch() setAlpha(srcAlpha) self.__tweenOnCloseGo:RunAction(ua.Sequence({ua.Tween(0.2, function(r) local alpha = srcAlpha * (1 - r) + dstAlpha * r setAlpha(alpha) return true end), ua.cb(function() setAlpha(dstAlpha) if cb then cb() end end)})) end NetworkStateUtil--[[ author:{zhangpeng} time:2023-08-17 14:24:45 ]] local NetworkStateUtil = defClassStatic("NetworkStateUtil") local Application = CS.UnityEngine.Application local NetworkReachability = CS.UnityEngine.NetworkReachability function NetworkStateUtil:init() self.mobileEnableFlag = false end function NetworkStateUtil:isMobileEnable() return self.mobileEnableFlag end function NetworkStateUtil:setMobildEnable(bool) self.mobileEnableFlag = bool end function NetworkStateUtil:isReachable() if CS.LocalDataStorage.Get("DEVICE_PRETEND_NET_NOT_OPEN") == "true" then return false end return Application.internetReachability ~= NetworkReachability.NotReachable end function NetworkStateUtil:isWifiReachable() if CS.LocalDataStorage.Get("DEVICE_PRETEND_WIFI_NET_CLOSE") == "true" then return false end return Application.internetReachability == NetworkReachability.ReachableViaLocalAreaNetwork end function NetworkStateUtil:isMobileReachable() if CS.LocalDataStorage.Get("DEVICE_PRETEND_USE_CELLUAR_NET") == "true" then return true end return Application.internetReachability == NetworkReachability.ReachableViaCarrierDataNetwork end NetworkStateUtil:init() ExtendLuaI[function checkNumber(value, base) return tonumber(value, base) or 0 end function checkInt(value) return math.round(checkNumber(value)) end function checkBool(value) return (value ~= nil and value ~= false) end function checkTable(value) if type(value) ~= "table" then value = {} end return value end function clone(object) local lookup_table = {} local function _copy(object) if type(object) ~= "table" then return object elseif lookup_table[object] then return lookup_table[object] end local newObject = {} lookup_table[object] = newObject for key, value in pairs(object) do newObject[_copy(key)] = _copy(value) end return setmetatable(newObject, getmetatable(object)) end return _copy(object) end local isNull = CS.LuaHelper.IsNull function handlerBind(obj, method) return function(...) if obj == nil or isNull(obj) then return end return method(...) end end function handler(obj, method) return function(...) return method(obj, ...) end end function math.newRandomSeed() local ok, socket = pcall(function() return require("socket") end) if ok then math.randomseed(socket.gettime() * 1000) else math.randomseed(os.time()) end math.random() math.random() math.random() math.random() end function math.round(value) value = checkNumber(value) if value >= 0 then return math.floor(value + 0.5) else return math.ceil(value - 0.5) end end local piDiv180 = math.pi / 180 function math.angle2radian(angle) return angle * piDiv180 end local piMul180 = math.pi * 180 function math.radian2angle(radian) return radian / piMul180 end --#region table ---获取表最后的元素 ---@param t table ---@param index integer 小于0的索引,-1表示最后一个,默认 -1 ---@return any function table.last(t, index) index = index or -1 local count = #t local index = count + 1 + index return t[index] end ---表格是否为空 ---@param t table ---@return boolean function table.isEmpty(t) return next(t) == nil end ---表格的个数 ---@param t table ---@return integer function table.nums(t) local count = 0 for k, v in pairs(t) do count = count + 1 end return count end ---返回指定表格中的所有键 ---@param hashTable table 要检查的表格 ---@return table function table.keys(hashTable) local keys = {} for k, v in pairs(hashTable) do keys[#keys + 1] = k end return keys end ---返回指定表格中的所有值 ---@param hashTable table 要检查的表格 ---@return table function table.values(hashTable) local values = {} for k, v in pairs(hashTable) do values[#values + 1] = v end return values end ---将来源表格中所有键及其值复制到目标表格对象中,如果存在同名键,则覆盖其值 ---@param des table 目标表格 ---@param src table 来源表格 function table.merge(des, src) if src and des then for k, v in pairs(src) do des[k] = v end return des end end ---在目标表格的指定位置插入来源表格,如果没有指定位置则连接两个表格 ---@param des table 目标表格 ---@param src table 来源表格 ---@param begin integer 插入位置,默认最后 function table.insertTo(des, src, begin) begin = checkInt(begin) if begin <= 0 then begin = #des + 1 end local len = #src for i = 0, len - 1 do des[i + begin] = src[i + 1] end end ---合并列表 ---@param list1 table 列表1 ---@param list2 table 列表2 ---@param removeDuplicate boolean 是否删除重复的值 ---@return table function table.mergeList(list1, list2, removeDuplicate) local unique_values = {} -- 创建一个空表用于存储唯一值 local result = {} -- 创建一个空表用于存储合并后的列表 -- 遍历第一个列表并添加到唯一值表中 for _, value in ipairs(list1) do if not unique_values[value] then unique_values[value] = true table.insert(result, value) end end -- 遍历第二个列表并添加到唯一值表中,同时检查是否已经存在于第一个列表中 for _, value in ipairs(list2) do if not removeDuplicate or not unique_values[value] then unique_values[value] = true table.insert(result, value) end end return result end ---从表格中查找指定值,返回其索引,如果没找到返回 false ---@param array table 表格 ---@param value any 要查找的值 ---@param begin integer|nil 起始索引值 ---@return integer|boolean function table.indexOf(array, value, begin) for i = begin or 1, #array do if array[i] == value then return i end end return false end ---从表格中查找指定值,返回其 key,如果没找到返回 nil ---@param hashTable table 表格 ---@param value any 要查找的值 ---@return string|nil 该值对应的 key function table.keyOf(hashTable, value) for k, v in pairs(hashTable) do if v == value then return k end end return nil end ---反转表格 ---@param array table function table.reverse(array) local i, j = 1, #array local tmp while i < j do tmp = array[i] array[i] = array[j] array[j] = tmp i = i + 1 j = j - 1 end end ---根据条件函数,从列表中删除指定值 ---@param array table 列表 ---@param cond fun(array: table, i: integer) ---@param removeAll boolean 是否删除所有相同的值 ---@return integer 返回删除的值的个数 function table.removeByCond(array, cond, removeAll) local c, i, max = 0, 1, #array while i <= max do if cond(array[i], i) then table.remove(array, i) c = c + 1 i = i - 1 max = max - 1 if not removeAll then break end end i = i + 1 end return c end ---从列表中删除指定值,返回删除的值的个数 ---@param array table 列表 ---@param value any 要删除的值 ---@param removeAll boolean 是否删除所有相同的值 ---@return integer 返回删除的值的个数 function table.removeByValue(array, value, removeAll) local c, i, max = 0, 1, #array while i <= max do if array[i] == value then table.remove(array, i) c = c + 1 i = i - 1 max = max - 1 if not removeAll then break end end i = i + 1 end return c end ---对表格中每一个值执行一次指定的函数,并用函数返回值更新表格内容 ---@param t table 表格 ---@param fn fun(v,k):any 函数, 返回值会被赋值给 t[k] function table.map(t, fn) for k, v in pairs(t) do t[k] = fn(v, k) end end ---对表格中每一个值执行一次指定的函数,但不改变表格内容 ---@param t table 表格 ---@param fn fun(v:any, k:any) 函数 function table.walk(t, fn) for k, v in pairs(t) do fn(v, k) end end ---对表格中每一个值执行一次指定的函数,如果该函数返回 false,则对应的值会从表格中删除 ---@param t table 表格 ---@param fn fun(v:any, k:any) 函数 function table.filter(t, fn) for k, v in pairs(t) do if not fn(v, k) then t[k] = nil end end end ---遍历表格,确保其中的值唯一 ---@param t table 表格 ---@param bArray boolean t是否是数组,是数组,t中重复的项被移除后,后续的项会前移 ---@return table 包含所有唯一值的新表格 function table.unique(t, bArray) local check = {} local n = {} local idx = 1 for k, v in pairs(t) do if not check[v] then if bArray then n[idx] = v idx = idx + 1 else n[k] = v end check[v] = true end end return n end ---是否包含 ---@param t table 表格 ---@param d any 要查找的值 ---@return boolean function table.contain(t, d) for k, v in pairs(t) do if v == d then return true end end return false end ---找到表格中所有符合条件的值 ---@param t table 表格 ---@param cond fun(v:any):boolean 判断函数 ---@return table function table.subByCond(t, cond) local newt = {} for _, v in pairs(t) do if cond(v) then table.insert(newt, v) end end return newt end ---找到表格中符合条件的值 ---@param t table 表格 ---@param cond fun(v:any):boolean 判断函数 ---@return any function table.getByCond(t, cond) for _, v in pairs(t) do if cond(v) then return v end end end ---获取子表 ---@param t table 原始表格 ---@param iStart integer 起始索引值 ---@param iEnd integer 结束索引值 ---@return table function table.sub(t, iStart, iEnd) local sub = {} for i = iStart, iEnd do local v = t[i] if v then table.insert(sub, v) else break end end return sub end ---洗牌 ---@param t table 原始表格 function table.shuffle(t) assert(t, "table.shuffle() expected a table, got nil") local rand = math.random local n = #t for i = n, 2, -1 do local j = rand(i) t[i], t[j] = t[j], t[i] end end ---表格拷贝 ---@param t table 原始表格 ---@return table function table.copy(t) local result = {} for k, v in pairs(t or {}) do result[k] = v end return result end local function lessThanComp(a, b) return a < b end ---表格排序 ---@param list table 排序列表 ---@param comp fun(a:any, b:any):boolean 排序方法 function table.stableSort(list, comp) comp = comp or lessThanComp local num = #list if num <= 1 then return end local sorted = false local n = num while not sorted do sorted = true for i = 1, n - 1 do if comp(list[i + 1], list[i]) then local tmp = list[i] list[i] = list[i + 1] list[i + 1] = tmp sorted = false end end n = n - 1 end end function table.toString(value) if value == nil then return "nil" end if type(value) == "string" then return value end -- 判断 myTable 是否存在 __tostring 方法 if getmetatable(value) and type(getmetatable(value).__tostring) == "function" then -- 调用 __tostring 方法并输出结果 return (tostring(value)) end local str = "{" for i, v in pairs(value) do local keyType = type(i) if keyType == "string" then str = str .. "[" .. string.format("%q", i) .. "]=" elseif keyType == "number" then str = str .. "[" .. i .. "]=" end local valueType = type(v) if valueType == "table" then str = str .. table.toString(v) .. "," elseif valueType == "string" then str = str .. string.format("%q", v) .. "," elseif valueType == "boolean" then if v then str = str .. "true," else str = str .. "false," end else str = str .. tostring(v) .. "," end end str = str .. "}" return str end function table.equal(l,r) local lL = #l local rL = #r if lL == rL then for i = 1,rL do if l[i] ~= r[i] then return false end end return true else return false end end function table.max(t,cmp_greater) local max = t[1] if max then for i = 2,#t do local v = t[i] if cmp_greater(v,max) then max = v end end return max end end function table.min(t,cmp_less) local min = t[1] if min then for i = 2,#t do local v = t[i] if cmp_less(v,min) then min = v end end return min end end function table.random(t) local l = #t if l > 0 then local i = math.random(1,l) return t[i] end end function table.reverseKV(t) local new = {} for k, v in pairs(t) do new[v] = k end return new end function table.initList(count, initValue) local t = {} for i = 1, count do t[i] = initValue end return t end --#endregion string._htmlSpecialCharsSet = {} string._htmlSpecialCharsSet["&"] = "&" string._htmlSpecialCharsSet['"'] = """ string._htmlSpecialCharsSet["'"] = "'" string._htmlSpecialCharsSet["<"] = "<" string._htmlSpecialCharsSet[">"] = ">" function string.encodeHtmlSpecialChars(input) for k, v in pairs(string._htmlSpecialCharsSet) do input = string.gsub(input, k, v) end return input end function string.decodeHtmlSpecialChars(input) for k, v in pairs(string._htmlSpecialCharsSet) do input = string.gsub(input, v, k) end return input end function string.lastIndexOf(input, pattern) local i = string.match(input, ".*" .. pattern .. "()") if i == nil then return -1 else return i - 1 end end function string.nl2br(input) return string.gsub(input, "\n", "
") end function string.text2html(input) input = string.gsub(input, "\t", " ") input = string.htmlspecialchars(input) input = string.gsub(input, " ", " ") input = string.nl2br(input) return input end ---@param input string ---@param delimiter string ---@param num integer 分割次数,默认是不限制 ---@return boolean|string[] function string.split(input, delimiter, num) input = tostring(input) delimiter = tostring(delimiter) if (delimiter == "") then return false end local pos, arr, splitCount = 0, {}, 0 -- for each divider found for st, sp in function() return string.find(input, delimiter, pos, true) end do if (num ~= nil and splitCount == num) then break end table.insert(arr, string.sub(input, pos, st - 1)) pos = sp + 1 splitCount = splitCount + 1 end table.insert(arr, string.sub(input, pos)) return arr end ---从右边开始分割字符串 ---@param input string ---@param delimiter string ---@param num integer 分割次数,默认是不限制 ---@return boolean|string[] function string.rsplit(input, delimiter, num) local arr = string.split(string.reverse(input), string.reverse(delimiter), num) if not arr then return false end table.reverse(arr) for index, value in ipairs(arr) do arr[index] = string.reverse(value) end return arr end function string.ltrim(input) return string.gsub(input, "^[ \t\n\r]+", "") end function string.rtrim(input) return string.gsub(input, "[ \t\n\r]+$", "") end function string.trim(input) input = string.gsub(input, "^[ \t\n\r]+", "") return string.gsub(input, "[ \t\n\r]+$", "") end function string.upperFirstChar(input) return string.upper(string.sub(input, 1, 1)) .. string.sub(input, 2) end local function urlEncodeChar(char) return "%" .. string.format("%02X", string.byte(char)) end function string.urlEncode(input) -- convert line endings input = string.gsub(tostring(input), "\n", "\r\n") -- escape all characters but alphanumeric, '.' and '-' input = string.gsub(input, "([^%w%.%- ])", urlEncodeChar) -- convert spaces to "+" symbols return string.gsub(input, " ", "+") end function string.urlDecode(input) input = string.gsub(input, "+", " ") input = string.gsub(input, "%%(%x%x)", function(h) return string.char(checkNumber(h, 16)) end) input = string.gsub(input, "\r\n", "\n") return input end function string.utf8Len(input) local len = string.len(input) local left = len local cnt = 0 local arr = { 0, 0xc0, 0xe0, 0xf0, 0xf8, 0xfc } while left ~= 0 do local tmp = string.byte(input, -left) local i = #arr while arr[i] do if tmp >= arr[i] then left = left - i break end i = i - 1 end cnt = cnt + 1 end return cnt end ---用于将一个数字 num 转换为带有千位分隔符(逗号)的字符串形式 ---@param num number ---@return string function string.formatNumberThousands(num) local formatted = tostring(checkNumber(num)) local k while true do formatted, k = string.gsub(formatted, "^(-?%d+)(%d%d%d)", "%1,%2") if k == 0 then break end end return formatted end -- 是否是中文字符 function string.isChineseChar(str) local c = string.byte(str, 1) local c1 = string.byte(str, 2) local c2 = string.byte(str, 3) -- local c = string.byte(s,k) if c >= 228 and c <= 233 then if c1 and c2 then local a1, a2, a3, a4 = 128, 191, 128, 191 if c == 228 then a1 = 184 elseif c == 233 then a2 = 190 if c1 == 190 then a4 = 165 end end if c1 >= a1 and c1 <= a2 and c2 >= a3 and c2 <= a4 then return true end end end return false end local CHINESE_SYMBOLS = { "·", "×", "—", "‘", "’", "“", "”", "…", "、", "。", "《", "》", "『", "』", "【", "】", "!", "(", ")", ",", ":", ";", "?" } ---是否中文标点 ---@param str string ---@return boolean|integer function string.isChineseSymbol(str) return table.indexOf(CHINESE_SYMBOLS, str) end -- 分割utf8字符串 function string.splitUTF8(s) local ss = {} local len = string.len(s) local index = 1 while index <= len do local curByte = string.byte(s, index) if curByte == nil then break elseif curByte > 0 and curByte <= 127 then table.insert(ss, string.char(curByte)) index = index + 1 elseif curByte >= 192 and curByte <= 223 then local c1 = string.byte(s, index + 1) table.insert(ss, string.char(curByte, c1)) index = index + 2 elseif curByte >= 224 and curByte <= 239 then local c1 = string.byte(s, index + 1) local c2 = string.byte(s, index + 2) table.insert(ss, string.char(curByte, c1, c2)) index = index + 3 elseif curByte >= 240 and curByte <= 247 then local c1 = string.byte(s, index + 1) local c2 = string.byte(s, index + 2) local c3 = string.byte(s, index + 3) table.insert(ss, string.char(curByte, c1, c2, c3)) index = index + 4 end end return ss end function string.startsWith(str, prefix) return string.sub(str, 1, string.len(prefix)) == prefix end function string.endsWith(str, suffix) local L = string.len(str) local Lsuffix = string.len(suffix) if L >= Lsuffix then return string.sub(str, L - Lsuffix + 1, L) == suffix else return false end end -- 截取中英混合的UTF8字符串,endIndex可缺省 function string.subUTF8(str, startIndex, endIndex) -- 返回当前字符实际占用的字符数 local function SubStringGetByteCount(str, index) local curByte = string.byte(str, index) local byteCount = 1 if curByte == nil then byteCount = 0 elseif curByte > 0 and curByte <= 127 then byteCount = 1 elseif curByte >= 192 and curByte <= 223 then byteCount = 2 elseif curByte >= 224 and curByte <= 239 then byteCount = 3 elseif curByte >= 240 and curByte <= 247 then byteCount = 4 end return byteCount end -- 获取中英混合UTF8字符串的真实字符数量 local function SubStringGetTotalIndex(str) local curIndex = 0 local i = 1 local lastCount = 1 repeat lastCount = SubStringGetByteCount(str, i) i = i + lastCount curIndex = curIndex + 1 until (lastCount == 0) return curIndex - 1 end local function SubStringGetTrueIndex(str, index) local curIndex = 0 local i = 1 local lastCount = 1 repeat lastCount = SubStringGetByteCount(str, i) i = i + lastCount curIndex = curIndex + 1 until (curIndex >= index) return i - lastCount end if startIndex < 0 then startIndex = SubStringGetTotalIndex(str) + startIndex + 1 end if endIndex ~= nil and endIndex < 0 then endIndex = SubStringGetTotalIndex(str) + endIndex + 1 end if endIndex == nil then return string.sub(str, SubStringGetTrueIndex(str, startIndex)) else return string.sub(str, SubStringGetTrueIndex(str, startIndex), SubStringGetTrueIndex(str, endIndex + 1) - 1) end end function string.isEmpty(str) return str == nil or str == "" end ---返回处理后的字符串,其中所有的Lua模式匹配中的特殊字符都被转义了 ---@param text string ---@return string function string.escapePattern(text) local magic_chars = { "%", "^", "$", "(", ")", ".", "[", "]", "*", "+", "-", "?" } for i, char in ipairs(magic_chars) do text = text:gsub("%" .. char, "%%" .. char) end return text end function string.f(format, vars) return (format:gsub("{(.-)}", function(key) return tostring(vars[key]) end)) end ---打乱字符串 ---@param inputStr string ---@return string function string.shuffle(inputStr) local charArray = {} for char in inputStr:gmatch(".") do table.insert(charArray, char) end for i = #charArray, 2, -1 do local j = math.random(1, i) charArray[i], charArray[j] = charArray[j], charArray[i] end return table.concat(charArray) end -- linkedlist双向链表 linkedlist = {} function linkedlist.create() return { len = 0, head = nil, tail = nil } end function linkedlist.push_back(list, v) local d = { data = v, next = nil } if list.head then d.prev = list.tail list.tail.next = d list.tail = d else list.head = d list.tail = d end list.len = list.len + 1 end function linkedlist.pop_back(list) if list.tail then list.tail = list.tail.prev list.len = list.len - 1 end end function linkedlist.pop_front(list) if list.head then list.head = list.head.next list.len = list.len - 1 end end function linkedlist.len(list) return list.len end function linkedlist.foreach(list, cb) local curr = list.head while curr ~= nil do cb(curr.data) curr = curr.next end end EMPTY_FUNC = function()end CosLuaEnum-- defGlobal("CosLuaServiceType") -- ---@enum CosLuaServiceType -- CosLuaServiceType = { -- UGC = 2, -- AVATAR = 6666, -- } mainorequire("framework/core/app/App") require("framework/core/app/Msg") require("framework/core/app/scene/main") HttpCmdMgr--[[ 使用http发送和接收json协议 author:{zhangpeng} time:2023-08-17 11:57:50 ]] local HttpCmdMgr = defClassStatic("HttpCmdMgr") local HttpGlobalUtil = CS.HttpGlobalUtil local GameObject = CS.UnityEngine.GameObject local LOG_TAG = "HttpCmdMgr" local CMD = HttpCmdDef.CMD function HttpCmdMgr:init() end function HttpCmdMgr:getJsonHeader() -- 请求头 self.header = { ['x-deviceid'] = "", ['x-os'] = Device.getPlatformStr(), ['x-version'] = "1.0", ['x-channel'] = "appstore", ['x-language'] = "en", } if self.tokenEnable then self.header['x-token'] = self.token end return self.header end function HttpCmdMgr:setHeaderToken(token) if not token then printWarn(LOG_TAG,"token is nil ! ! !") return end self.tokenEnable = true self.token = token CS.AESCTR.SetToken(token) end function HttpCmdMgr:getHeaderToken() return self.token end function HttpCmdMgr:setUserId(userid) self.userId = userid end function HttpCmdMgr:getUserId() return self.userId end function HttpCmdMgr:setHeaderDeviceId(deviceId) self.deviceId = deviceId end function HttpCmdMgr:postCmdSync(cmd,data,callback) callback = callback or function() end if not self.httpGo then self.httpGo = CS.HttpGlobalUtil.GetHttpGoInScene() end local start_time = os.time() if not NetworkStateUtil.isReachable() then printInfo(LOG_TAG,"--close loading anim-- net not reachable") UIComsTool:showDailog("网络不可用,请检查网络连接", "ok", function (ui) ui:close() return true end ) callback(false, nil) return end self.curCmd = cmd -- 组装url local url = HttpCmdDef.getUrlByCmd(cmd) -- 加密post body self.deviceId = self.deviceId or User:getDeviceId() printInfo(LOG_TAG, "协议名: %s", cmd) local base64Str = CS.AESCTR.EnctryHttpCmd(self.deviceId,data) -- 加密后重新组装json local body = {["s"] = base64Str} local jsonStr = json.encode(body) local header = HttpCmdMgr:getJsonHeader() header['x-deviceid'] = self.deviceId local ret = CS.HttpGlobalUtil.httpPostJsonSyncInScene(url,jsonStr,header, function(errorCode, ret) ret = tostring(ret) if errorCode ~= 200 then local net_error = self:rpsNetErrorCode(errorCode) if not net_error then -- 处理逻辑错误信息 callback(false, {isHttpError = true, errorCode = errorCode, errorMsg = ret}) end return end local resultData = json.decode(ret) if self:rpsCode(resultData) then -- 解密 if resultData.data then local decryData = CS.AESCTR.DecryptHttpCmd(resultData.data) printInfo(LOG_TAG,"decryData:%s", decryData) local rspData = json.decode(decryData) callback(true, rspData) else callback(true, resultData) end end end ) end function HttpCmdMgr:rpsNetErrorCode(code) local is_net_error = false for k,v in pairs(HttpCmdDef.ErrorCodeNet) do if code == v.code then UIComsTool:showToast(v.text,2) is_net_error = true end end return is_net_error end function HttpCmdMgr:rpsCode(resultData) local code = resultData.code local msg = resultData.message or "" if code == 0 then -- no error code return true end printInfo(LOG_TAG, "处理错误信息 code:%s msg:%s", code, msg) if code == HttpCmdDef.ErrorCode.TOKEN_ERROR.ID then UIComsTool:showToast(HttpCmdDef.ErrorCode.TOKEN_ERROR.TEXT) -- todo::处理token错误的逻辑 -- User:clearInvalidToken() elseif code == HttpCmdDef.ErrorCode.EMAIL_BINDED.ID then UIComsTool:showToast(HttpCmdDef.ErrorCode.EMAIL_BINDED.TEXT) elseif code == HttpCmdDef.ErrorCode.EMAIL_PW_ERROR.ID then UIComsTool:showToast(HttpCmdDef.ErrorCode.EMAIL_PW_ERROR.TEXT) elseif code == HttpCmdDef.ErrorCode.EMAIL_BE_USED.ID then UIComsTool:showToast(HttpCmdDef.ErrorCode.EMAIL_BE_USED.TEXT) elseif code == HttpCmdDef.ErrorCode.COIN_NOT_ENOUGH.ID then UIComsTool:showToast(HttpCmdDef.ErrorCode.COIN_NOT_ENOUGH.TEXT) elseif code == HttpCmdDef.ErrorCode.GEM_NOT_ENOUGH.ID then UIComsTool:showToast(HttpCmdDef.ErrorCode.GEM_NOT_ENOUGH.TEXT) elseif code == HttpCmdDef.ErrorCode.APPLE_ACCOUNT_BIND_EXIST.ID then UIComsTool:showToast(HttpCmdDef.ErrorCode.APPLE_ACCOUNT_BIND_EXIST.TEXT, 2) else UIComsTool:showToast(string.format("post cmd %s 未知错误代码:%s\n message:%s",self.curCmd,code,msg),3) end return false end function HttpCmdMgr:getToken() return self.token end HttpCmdMgr:init() SqliteQuery5 ---@class SqliteQuery:LuaClass local SqliteQuery = defClass("SqliteQuery") local LOGTAG = SqliteQuery.__cls_name function SqliteQuery:ctor(table) self.table = table end --------------------------------------------------------------------------------------------- -- 构造sql语句 --------------------------------------------------------------------------------------------- function SqliteQuery:select(...) self.selectColumnList = {...} return self end function SqliteQuery:selectDistinct(...) return self:select(...):distinct() end function SqliteQuery:distinct() local sqlFunc = self.aggregateFunList[#self.aggregateFunList] if not sqlFunc then self.isSelectDistinct = true return self end sqlFunc:distinct() return self end function SqliteQuery:count(column, asName) self.aggregateFunList = self.aggregateFunList or {} local sqlFunc = self.table:genFunc(SqliteFunc.Type.count):column(column):as(asName) table.insert(self.aggregateFunList, sqlFunc) return self end function SqliteQuery:countDistinct(column, asName) return self:count(column, asName):distinct() end function SqliteQuery:sum(column, asName) self.aggregateFunList = self.aggregateFunList or {} local sqlFunc = self.table:genFunc(SqliteFunc.Type.sum):column(column):as(asName) table.insert(self.aggregateFunList, sqlFunc) return self end function SqliteQuery:max(column, asName) self.aggregateFunList = self.aggregateFunList or {} local sqlFunc = self.table:genFunc(SqliteFunc.Type.max):column(column):as(asName) table.insert(self.aggregateFunList, sqlFunc) return self end function SqliteQuery:min(column, asName) self.aggregateFunList = self.aggregateFunList or {} local sqlFunc = self.table:genFunc(SqliteFunc.Type.min):column(column):as(asName) table.insert(self.aggregateFunList, sqlFunc) return self end function SqliteQuery:avg(column, asName) self.aggregateFunList = self.aggregateFunList or {} local sqlFunc = self.table:genFunc(SqliteFunc.Type.avg):column(column):as(asName) table.insert(self.aggregateFunList, sqlFunc) return self end function SqliteQuery:as(asName) local func = self.aggregateFunList[#self.aggregateFunList] if not func then printWarn(LOGTAG, "as, 应该在聚合函数后") return self end func:as(asName) return self end function SqliteQuery:join(sqlTable) return self:innerJoin(sqlTable) end function SqliteQuery:innerJoin(sqlTable) self.joinList = self.joinList or {} table.insert(self.joinList, {op = "INNER JOIN", table = sqlTable}) return self end function SqliteQuery:leftJoin(sqlTable) self.joinList = self.joinList or {} table.insert(self.joinList, {op = "LEFT JOIN", table = sqlTable}) return self end function SqliteQuery:rightJoin(sqlTable) self.joinList = self.joinList or {} table.insert(self.joinList, {op = "RIGHT JOIN", table = sqlTable}) return self end function SqliteQuery:fullJoin(sqlTable) self.joinList = self.joinList or {} table.insert(self.joinList, {op = "FULL JOIN", table = sqlTable}) return self end function SqliteQuery:on(column1, column2) local t = self.joinList[#self.joinList] if not t then printWarn(LOGTAG, "on, 应该在join后调用") return self end t.column1 = column1 t.column2 = column2 return self end function SqliteQuery:union(query) self.unionQuery = query return self end function SqliteQuery:unionAll(query) self.unionQuery = query self.isUnionAll = true return self end function SqliteQuery:where(condition) self.whereCondition = condition return self end function SqliteQuery:groupBy(...) self.groupColumnList = {...} return self end function SqliteQuery:haveing(condition) self.havingCondition = condition return self end function SqliteQuery:orderBy(...) self.orderColumnList = {...} return self end function SqliteQuery:asc() if self:isColumnNil(self.orderColumnList) then printWarn(LOGTAG, "asc, 应该在orderBy后调用") return end self.isDesc = false return self end function SqliteQuery:desc() if self:isColumnNil(self.orderColumnList) then printWarn(LOGTAG, "desc, 应该在orderBy后调用") return end self.isDesc = true return self end function SqliteQuery:reverse() if self:isColumnNil(self.orderColumnList) then printWarn(LOGTAG, "reverse, 应该在orderBy后调用") return end if self.isDesc == nil then self.isDesc = false end self.isDesc = not self.isDesc return self end function SqliteQuery:limit(count) self.limitCount = count return self end function SqliteQuery:offset(count) self.offsetCount = count return self end --------------------------------------------------------------------------------------------- -- 构造sql语句 where的便捷函数 --------------------------------------------------------------------------------------------- function SqliteQuery:filter(column, op, value) local oldCondition = self.whereCondition local condition = self.table:genCondition(column, op, value) if oldCondition then condition = oldCondition:xand(condition) end self:where(condition) return self end function SqliteQuery:filterEqual(column, value) return self:filter(column, SqliteCondition.CompareOp.E, value) end SqliteQuery.filterE = SqliteQuery.filterEqual function SqliteQuery:filterNotEqual(column, value) return self:filter(column, SqliteCondition.CompareOp.NE, value) end SqliteQuery.filterNE = SqliteQuery.filterNotEqual function SqliteQuery:filterLessThan(column, number) return self:filter(column, SqliteCondition.CompareOp.L, number) end SqliteQuery.filterL = SqliteQuery.filterLessThan function SqliteQuery:filterLessThanOrEqual(column, number) return self:filter(column, SqliteCondition.CompareOp.LE, number) end SqliteQuery.filterLE = SqliteQuery.filterLessThanOrEqual function SqliteQuery:filterGreaterThan(column, number) return self:filter(column, SqliteCondition.CompareOp.G, number) end SqliteQuery.filterG = SqliteQuery.filterGreaterThan function SqliteQuery:filterGreaterThanOrEqual(column, number) return self:filter(column, SqliteCondition.CompareOp.GE, number) end SqliteQuery.filterGE = SqliteQuery.filterGreaterThanOrEqual function SqliteQuery:mingle(column, op, value) local oldCondition = self.whereCondition local condition = self.table:genCondition(column, op, value) if oldCondition then condition = oldCondition:xor(condition) end self:where(condition) return self end function SqliteQuery:mingleEqual(column, value) return self:mingle(column, SqliteCondition.CompareOp.E, value) end SqliteQuery.mingleE = SqliteQuery.mingleEqual function SqliteQuery:mingleNotEqual(column, value) return self:mingle(column, SqliteCondition.CompareOp.NE, value) end SqliteQuery.mingleNE = SqliteQuery.mingleNotEqual function SqliteQuery:mingleLessThan(column, number) return self:mingle(column, SqliteCondition.CompareOp.L, number) end SqliteQuery.mingleL = SqliteQuery.mingleLessThan function SqliteQuery:mingleLessThanOrEqual(column, number) return self:mingle(column, SqliteCondition.CompareOp.LE, number) end SqliteQuery.mingleLE = SqliteQuery.mingleLessThanOrEqual function SqliteQuery:mingleGreaterThan(column, number) return self:mingle(column, SqliteCondition.CompareOp.G, number) end SqliteQuery.mingleG = SqliteQuery.mingleGreaterThan function SqliteQuery:mingleGreaterThanOrEqual(column, number) return self:mingle(column, SqliteCondition.CompareOp.GE, number) end SqliteQuery.mingleGE = SqliteQuery.mingleGreaterThanOrEqual --------------------------------------------------------------------------------------------- -- 执行sql语句 --------------------------------------------------------------------------------------------- function SqliteQuery:get(funcOrKey) local str = self:toSelectSqlStr() local list = {} -- printVerbose(LOGTAG, "get, sqlstr:%s", str) if not funcOrKey then for row in self.table.databaseApis.nrows(str) do table.insert(list, row) end elseif type(funcOrKey) == "string" then for row in self.table.databaseApis.nrows(str) do table.insert(list, row[funcOrKey]) end else for row in self.table.databaseApis.nrows(str) do table.insert(list, funcOrKey(row)) end end return list end function SqliteQuery:getFirst(funcOrKey) local list = self:get(funcOrKey) return list[1] end function SqliteQuery:getRecordList() if not self:isAggregateFunNil() or not self:isColumnNil(self.selectColumnList) then printWarn(LOGTAG, "getRecordList, 请勿使用select以及聚合函数") end local list = self:get( function(row) local record = self.table.recordCls.new(self.table) record:setSqliteData(row) self.table:setCache(record) return record end ) return list end function SqliteQuery:getFirstRecord() local list = self:getRecordList() return list[1] end function SqliteQuery:update(data) local str = self:toUpdateSqlStr(data) printVerbose(LOGTAG, "update, sqlstr:%s", str) self.table.databaseApis.exec(str) end function SqliteQuery:updateRecord(record) local data = {} for i, col in ipairs(self.table.columnList) do local key = col.name local value = record[key] if value ~= nil then data[key] = value end end self:update(data) end function SqliteQuery:delete() local str = self:toDeleteSqlStr() printVerbose(LOGTAG, "update, delete:%s", str) self.table.databaseApis.exec(str) end --------------------------------------------------------------------------------------------- -- 工具函数 --------------------------------------------------------------------------------------------- function SqliteQuery:isColumnNil(columnList) return columnList == nil or #columnList == 0 end function SqliteQuery:getColumnStr(columnList) if self:isColumnNil(columnList) then return "*" end local str = table.concat(columnList, ",") return str end function SqliteQuery:isAggregateFunNil() return self.aggregateFunList == nil or #self.aggregateFunList == 0 end function SqliteQuery:getAggregateFunStr() if self:isAggregateFunNil() then return "" end local list = {} for i, sqlFunc in ipairs(self.aggregateFunList) do table.insert(list, sqlFunc:toSqlStr()) end local str = table.concat(list, ",") return str end function SqliteQuery:canUnion() return self:isColumnNil(self.orderColumnList) end function SqliteQuery:toSelectSqlStr() local str = "SELECT" if self.isSelectDistinct then str = str .. " DISTINCT" end if not self:isAggregateFunNil() then str = string.format("%s %s\n", str, self:getAggregateFunStr()) if self:isColumnNil(self.selectColumnList) then str = string.format("%s,%s", str, self:getColumnStr(self.selectColumnList)) end else str = string.format("%s %s\n", str, self:getColumnStr(self.selectColumnList)) end str = str .. string.format("FROM %s\n", self.table:getName()) for i, v in ipairs(self.joinList or {}) do local op = v.op local name = v.table:getName() local column1 = v.column1 local colmun2 = v.column2 str = str .. string.format("%s %s\n", op, name) str = str .. string.format("ON %s = %s\n", column1, colmun2) end if self.unionQuery then if not self.unionQuery:canUnion() then printWarn(LOGTAG, "toSelectSqlStr, can not Union") end if self.isUnionAll then str = str .. "UNION ALL\n" else str = str .. "UNION\n" end str = str .. self.unionQuery:toSelectSqlStr() .. "\n" end if self.whereCondition then str = str .. string.format("WHERE %s\n", self.whereCondition:toSqlStr()) end if not self:isColumnNil(self.groupColumnList) then str = str .. string.format("GROUP BY %s\n", self:getColumnStr(self.groupColumnList)) if self.havingCondition then str = str .. string.format("HAVING %s\n", self.havingCondition:toSqlStr()) end end if not self:isColumnNil(self.orderColumnList) then local subStr = "ASC" if self.isDesc then subStr = "DESC" end str = str .. string.format("ORDER BY %s %s\n", self:getColumnStr(self.orderColumnList), subStr) end if self.limitCount and self.limitCount > 0 then str = str .. " LIMIT " .. self.limitCount end if self.offsetCount and self.offsetCount > 0 then str = str .. " OFFSET " .. self.offsetCount end return str end function SqliteQuery:toUpdateSqlStr(data) local str = string.format("UPDATE %s\nSET ", self.table:getName()) local list = {} for k, v in pairs(data) do table.insert(list, string.format("%s = %s", k, SqliteUtil:luaValueToStr(v))) end str = str .. table.concat(list, ",") .. "\n" if self.whereCondition then str = str .. string.format("WHERE %s\n", self.whereCondition:toSqlStr()) end return str end function SqliteQuery:toDeleteSqlStr() local str = string.format("DELETE FROM %s\n", self.table:getName()) if self.whereCondition then str = str .. string.format("WHERE %s\n", self.whereCondition:toSqlStr()) end return str end return SqliteQuery UIDialogr--[[ 尺寸:1240*640,根据文字长度按照此比例等比缩 author:{zhangpeng} time:2023-09-17 10:40:10 ]] local UIDialog, super = defClass("UIDialog", UILayer) local TMPUGUI = CS.TMPro.TextMeshProUGUI UIDialog.isPopup = true local prefab_path = "Assets/AssetsPackage/Res/framework/ui/dailog/prefab/common_dailog.prefab" local LOG_TAG = "UIDialog" function UIDialog:ctor(style) super.ctor(self) self.uiStyle = style end function UIDialog:onLoad() do return end self:setPriority(UIOrderDef.UI_ORDER.SYS_PANEL) self.uiStyle = self.uiStyle or UITypeEnums.DialogType.Common local prefab = UITypeEnums.DialogPrefabs[self.uiStyle] local ui = CS.UnityEngine.GameObject.Instantiate(Res.loadAsset(prefab)) self:addChild(ui) -- close btn util.ugui.addClickEvent( ui:Seek("closebtn"), function() print("dialog click close") self:close() end ) self.ui = ui self:storeBtnsPosX() -- self:useTweenOnOpen(ui) local btnOK = self.ui:Seek("BtnOK") local btnCancel = self.ui:Seek("BtnCancel") btnOK:SetActive(false) btnCancel:SetActive(false) self:showMask() -- self:hideEar() end -- 单按钮弹窗 function UIDialog:showSingleBtn(text, cb) do return end self:storeBtnsPosX() local btnOK = self.ui:Seek("BtnOK") local btnCancel = self.ui:Seek("BtnCancel") self.btnOK = btnOK self.btnCancel = btnCancel self.btnOK.cb = cb local text_ui if self.btnOK:Seek("Text"):GetComponent(typeof(UGUI.Text)) then text_ui = self.btnOK:Seek("Text"):GetComponent(typeof(UGUI.Text)) elseif self.btnOK:Seek("Text")[TMPUGUI] then text_ui = self.btnOK:Seek("Text")[TMPUGUI] end text_ui.text = text util.ugui.addButtonClickEvent( self.btnOK, function() printInfo(LOG_TAG, "点击单按钮弹窗的确定") if self.btnOK.cb then self.btnOK.cb(self) end end ) btnCancel:SetActive(false) btnOK:SetActive(true) btnOK:SetPositionX((self.btnOKPosX + self.btnCancelPosX) * 0.5) return self end function UIDialog:showDoubleBtns(text1, text2, cb1, cb2) self:storeBtnsPosX() local btnOK = self.ui:Seek("BtnOK") btnOK:SetActive(true) btnOK:SetPositionX(self.btnOKPosX) btnOK:Seek("Text"):GetComponent(typeof(UGUI.Text)).text = text1 util.ugui.addButtonClickEvent( btnOK, function() if cb1 then cb1(self) end end ) local btnCancel = self.ui:Seek("BtnCancel") btnCancel:SetActive(true) btnCancel:SetPositionX(self.btnCancelPosX) btnCancel:Seek("Text"):GetComponent(typeof(UGUI.Text)).text = text2 util.ugui.addButtonClickEvent( btnCancel, function() if cb2 then cb2(self) end end ) return self end -- 设置确认按钮文本 function UIDialog:setOkBtnText(text) local text_ui if self.btnOK:Seek("Text"):GetComponent(typeof(UGUI.Text)) then text_ui = self.btnOK:Seek("Text"):GetComponent(typeof(UGUI.Text)) elseif self.btnOK:Seek("Text")[TMPUGUI] then text_ui = self.btnOK:Seek("Text")[TMPUGUI] end text_ui.text = text end -- 设置弹窗文本 function UIDialog:setContentText(text) do return end self.ui:Seek("ContentText"):GetComponent(typeof(UGUI.Text)).text = text self.content = text return self end -- 隐藏关闭按钮 function UIDialog:hideCloseBtn() do return end self.ui:Seek("closebtn"):SetActive(false) return self end -- 隐藏确认和取消按钮 function UIDialog:hideDoubleBtn() do return end self.ui:Seek("BtnOK"):SetActive(false) self.ui:Seek("BtnCancel"):SetActive(false) return self end function UIDialog:storeBtnsPosX() if self.ui then self.btnOKPosX = self.btnOKPosX or self.ui:Seek("BtnOK"):GetPositionX() self.btnCancelPosX = self.btnCancelPosX or self.ui:Seek("BtnCancel"):GetPositionX() end end function UIDialog:playSound(sound, cb) self.ui:PlaySound(sound, cb) return self end function UIDialog:hideEar() local ears = self.ui:SearchPattern("ear_\\d") for k,v in pairs(ears) do v:SetActive(false) end end -- 设置弹窗类型 function UIDialog:setStyle(style) self.uiStylePrefab = UITypeEnums.DialogPrefabs[style] -- if style == UITypeEnums.DialogType.Common then -- elseif style == UITypeEnums.DialogType.SkinPartAdUI then -- elseif style == UITypeEnums.DialogType.SkinPartUnlock then -- end end return UIDialogXSdkBase--[[ author:{zhangpeng} time:2023-09-24 17:17:27 ]] local XSdkBase = defClassStatic("XSdkBase") local LOG_TAG = "XSdkBase" function XSdkBase:showWebView(url, title, sceneName, orientation, callback) CS.UnityEngine.Application.OpenURL(url) end --@region 支付 function XSdkBase:purchaseProduct(productOrder) printError(LOG_TAG, "purchaseProduct not implement") end function XSdkBase:purchaseProductWithChannel(productOrder, paymentChannel) printError(LOG_TAG, "purchaseProductWithChannel not implement") end function XSdkBase:restorePurchase() printError(LOG_TAG, "restorePurchase not implement") end function XSdkBase:clearIAPCache() printError(LOG_TAG, "clearIAPCache not implement") end function XSdkBase:cancelPromotedPayment(purchaseId, count) printError(LOG_TAG, "cancelPromotedPayment not implement") end function XSdkBase:getPromoteProduct() printError(LOG_TAG, "getPromoteProduct not implement") end function XSdkBase:clearPromoteProduct() printError(LOG_TAG, "clearPromoteProduct not implement") end function XSdkBase:getPurchaseState(purchaseId) printError(LOG_TAG, "getPurchaseState not implement") end function XSdkBase:getLocalProductInfoListAsyn(productInfoList, callback) printError(LOG_TAG, "getLocalProductInfoListAsyn not implement") end function XSdkBase:suggestedRegion() printError(LOG_TAG, "suggestedRegion not implement") end --@endregionloginscenereslink|return { --BASIC --ASSET SCENE = {"Assets/AssetsPackage/Res/modules/common/login/scene/LoginScene.unity", 0, 1}, } YooAssetAdapter$1--[[ YooAssetLoader适配层 提供统一的资源加载接口,封装YooAssetLoader的具体实现 支持智能类型推断、缓存管理、错误处理等功能 author: zhangheng time: 2025-07-19 ]] ---@class YooAssetAdapter local YooAssetAdapter = defClassStatic("YooAssetAdapter") local LOGTAG = "YooAssetAdapter" -- ============================================================================ -- 私有函数和配置 -- ============================================================================ -- 获取YooAssetLoader实例 local function getYooAssetLoader() return CS.YooAssetLoader.Instance end -- 资源类型映射表 local ASSET_TYPE_MAP = { [".prefab"] = "GameObject", [".png"] = "Sprite", [".jpg"] = "Sprite", [".jpeg"] = "Sprite", [".wav"] = "AudioClip", [".mp3"] = "AudioClip", [".ogg"] = "AudioClip", [".bytes"] = "TextAsset", [".txt"] = "TextAsset", [".json"] = "TextAsset", [".mat"] = "Material", [".shader"] = "Shader", [".anim"] = "AnimationClip", [".controller"] = "RuntimeAnimatorController", [".asset"] = "SkeletonDataAsset" -- Spine资源 } -- 根据路径推断资源类型 local function inferAssetType(path) if not path then return nil end local ext = string.match(path:lower(), "%.([^%.]+)$") if ext then return ASSET_TYPE_MAP["." .. ext] end return nil end -- ============================================================================ -- 公共接口 -- ============================================================================ -- 检查YooAssetLoader是否可用 function YooAssetAdapter.isAvailable() local loader = getYooAssetLoader() return loader ~= nil and CS.YooAssetLoaderExtension.IsReady(loader) end -- 同步加载资源适配器 ---@param path string 资源路径 ---@param assetType string|nil 资源类型 ---@return any|nil 加载的资源对象 function YooAssetAdapter.loadAssetSync(path, assetType) printDebug(LOGTAG, "同步加载资源:%s, 类型:%s", path, tostring(assetType)) -- 根据UseLocalRes判断加载方式 if CS.LuaHelper.UseLocalRes() then printDebug(LOGTAG, "使用本地资源加载:%s", path) return YooAssetAdapter.loadAssetInEditor(path, assetType) else printDebug(LOGTAG, "使用YooAssetLoader加载:%s", path) local loader = getYooAssetLoader() if not loader then printError(LOGTAG, "YooAssetLoader未初始化") return nil end -- 根据资源类型选择合适的加载方法 if assetType == "GameObject" or inferAssetType(path) == "GameObject" then return loader:LoadPrefab(path) elseif assetType == "Sprite" or inferAssetType(path) == "Sprite" then return loader:LoadSprite(path) elseif assetType == "AudioClip" or inferAssetType(path) == "AudioClip" then return loader:LoadAudioClip(path) elseif assetType == "TextAsset" or inferAssetType(path) == "TextAsset" then return loader:LoadTextAsset(path) elseif assetType == "Material" or inferAssetType(path) == "Material" then return loader:LoadMaterial(path) else -- 使用通用加载方法和扩展 if assetType then return CS.YooAssetLoaderExtension.LoadAssetByTypeName(loader, path, assetType) else -- 尝试推断类型 local inferredType = inferAssetType(path) if inferredType then return YooAssetAdapter.loadAssetSync(path, inferredType) else return CS.YooAssetLoaderExtension.LoadAssetByTypeName(loader, path, nil) end end end end end -- 异步加载资源适配器 ---@param path string 资源路径 ---@param assetType string|nil 资源类型 ---@param progressCallback function|nil 进度回调 ---@param finishCallback function|nil 完成回调 function YooAssetAdapter.loadAssetAsync(path, assetType, progressCallback, finishCallback) printDebug(LOGTAG, "异步加载资源:%s, 类型:%s", path, tostring(assetType)) -- 根据UseLocalRes判断加载方式 if CS.LuaHelper.UseLocalRes() then printDebug(LOGTAG, "使用本地资源加载:%s", path) local asset = YooAssetAdapter.loadAssetInEditor(path, assetType) -- 模拟异步完成 if progressCallback then progressCallback(1) end if finishCallback then finishCallback(asset) end return else printDebug(LOGTAG, "使用YooAssetLoader异步加载:%s", path) local loader = getYooAssetLoader() if not loader then printError(LOGTAG, "YooAssetLoader未初始化") if finishCallback then finishCallback(nil) end return end -- 进度回调包装 local wrappedCallback = function(asset) if finishCallback then finishCallback(asset) end end -- 根据资源类型选择合适的加载方法 if assetType == "GameObject" or inferAssetType(path) == "GameObject" then loader:LoadPrefabAsyncLua(path, wrappedCallback) elseif assetType == "Sprite" or inferAssetType(path) == "Sprite" then loader:LoadSpriteAsyncLua(path, wrappedCallback) elseif assetType == "AudioClip" or inferAssetType(path) == "AudioClip" then loader:LoadAudioClipAsyncLua(path, wrappedCallback) elseif assetType == "TextAsset" or inferAssetType(path) == "TextAsset" then loader:LoadTextAsyncLua(path, wrappedCallback) else -- 使用通用异步加载方法和扩展 if assetType then CS.YooAssetLoaderExtension.LoadAssetByTypeNameAsyncLua(loader, path, assetType, wrappedCallback) else -- 尝试推断类型 local inferredType = inferAssetType(path) if inferredType then YooAssetAdapter.loadAssetAsync(path, inferredType, progressCallback, finishCallback) else CS.YooAssetLoaderExtension.LoadAssetByTypeNameAsyncLua(loader, path, nil, wrappedCallback) end end end end end -- 检查资源是否存在 ---@param path string 资源路径 ---@return boolean 是否存在 function YooAssetAdapter.hasAsset(path) -- 根据UseLocalRes判断检查方式 if CS.LuaHelper.UseLocalRes() then printDebug(LOGTAG, "使用本地资源检查:%s", path) return YooAssetAdapter.hasAssetInEditor(path) else printDebug(LOGTAG, "使用YooAssetLoader检查:%s", path) local loader = getYooAssetLoader() if not loader then return false end return loader:CheckAssetExist(path) end end -- 释放资源 ---@param path string 资源路径 function YooAssetAdapter.releaseAsset(path) local loader = getYooAssetLoader() if loader then loader:ReleaseAsset(path) end end -- 预加载资源列表 ---@param assetList string[] 资源路径列表 ---@param callback function|nil 完成回调 function YooAssetAdapter.preloadAssets(assetList, callback) if not assetList or #assetList == 0 then if callback then callback(true) end return end printInfo(LOGTAG, "预加载资源列表,数量:%d", #assetList) local loader = getYooAssetLoader() if loader then -- 转换为C#数组 local csArray = CS.System.Array.CreateInstance(CS.System.String, #assetList) for i = 1, #assetList do csArray[i-1] = assetList[i] end loader:PreloadAssets(csArray, function(success) printInfo(LOGTAG, "批量预加载完成,成功:%s", tostring(success)) if callback then callback(success) end end) else printError(LOGTAG, "YooAssetLoader未初始化,无法预加载") if callback then callback(false) end end end -- 清理所有YooAssetLoader资源 function YooAssetAdapter.clearAll() local loader = getYooAssetLoader() if loader then loader:ReleaseAllAssets() printInfo(LOGTAG, "YooAssetLoader资源清理完成") end end -- 获取YooAssetLoader统计信息 ---@return table 统计信息 function YooAssetAdapter.getStats() local loader = getYooAssetLoader() if not loader then return { assetCount = 0, sceneCount = 0, isReady = false } end return { assetCount = loader:GetLoadedAssetCount(), sceneCount = loader:GetLoadedSceneCount(), isReady = CS.YooAssetLoaderExtension.IsReady(loader), details = CS.YooAssetLoaderExtension.GetDetailedStats(loader) } end -- 获取支持的资源类型列表 ---@return string[] 支持的文件扩展名列表 function YooAssetAdapter.getSupportedTypes() local types = {} for ext, typeName in pairs(ASSET_TYPE_MAP) do table.insert(types, ext .. " -> " .. typeName) end return types end -- 根据路径推断资源类型(公共接口) ---@param path string 资源路径 ---@return string|nil 推断的资源类型 function YooAssetAdapter.inferAssetType(path) return inferAssetType(path) end -- 打印适配器状态信息 function YooAssetAdapter.printStatus() printInfo(LOGTAG, "=== YooAssetAdapter 状态 ===") local loader = getYooAssetLoader() if not loader then printError(LOGTAG, "YooAssetLoader未初始化") return end local stats = YooAssetAdapter.getStats() printInfo(LOGTAG, "就绪状态: %s", tostring(stats.isReady)) printInfo(LOGTAG, "已加载资源数量: %d", stats.assetCount) printInfo(LOGTAG, "已加载场景数量: %d", stats.sceneCount) printInfo(LOGTAG, "详细信息: %s", stats.details) printInfo(LOGTAG, "支持的资源类型:") local supportedTypes = YooAssetAdapter.getSupportedTypes() for i, typeInfo in ipairs(supportedTypes) do printInfo(LOGTAG, " %s", typeInfo) end printInfo(LOGTAG, "========================") end -- ============================================================================ -- 编辑器兼容性支持 -- ============================================================================ -- 编辑器模式下加载资源 ---@param path string 资源路径 ---@param assetType string|nil 资源类型 ---@return any|nil 加载的资源对象 function YooAssetAdapter.loadAssetInEditor(path, assetType) printWarn(LOGTAG, "使用编辑器模式加载资源:%s", path) local assetPath = path -- string.format("Assets/AssetsPackage/%s", path) -- 使用AssetDatabase加载资源 if assetType then local csType = CS.System.Type.GetType("UnityEngine." .. assetType .. ", UnityEngine") if csType then return CS.UnityEditor.AssetDatabase.LoadAssetAtPath(assetPath, csType) else return CS.UnityEditor.AssetDatabase.LoadAssetAtPath(assetPath, CS.System.Type.GetType(assetType)) end else return CS.UnityEditor.AssetDatabase.LoadMainAssetAtPath(assetPath) end end -- 检查编辑器模式下资源是否存在 ---@param path string 资源路径 ---@return boolean 是否存在 function YooAssetAdapter.hasAssetInEditor(path) -- 处理资源路径格式转换(与loadAssetInEditor保持一致) local assetPath = path -- 如果路径不以Assets/开头,尝试添加Assets/前缀 if not string.match(assetPath, "^[Aa]ssets/") then -- 处理AssetsPackage路径 if string.match(assetPath, "^[Aa]ssetsPackage/") then assetPath = "Assets/" .. assetPath else -- 尝试在常见目录中查找 local possiblePaths = { "Assets/AssetsPackage/" .. assetPath, "Assets/Resources/" .. assetPath, "Assets/" .. assetPath } for _, testPath in ipairs(possiblePaths) do local testAsset = CS.UnityEditor.AssetDatabase.LoadMainAssetAtPath(testPath) if testAsset then return true end end return false end end local asset = CS.UnityEditor.AssetDatabase.LoadMainAssetAtPath(assetPath) return asset ~= nil end return YooAssetAdapterPaymentErrorCodeM --[[ ios/google play支付返回的错误代码 author:{zhangpeng} time:2024-08-20 15:08:55 ]] local PaymentErrorCode,_ = defClassStatic("PaymentErrorCode") -- ios payment PaymentErrorCode.iOS = { SKErrorClientInvalid = 0, -- 当前设备或用户不被允许进行App内购买操作 SKErrorPaymentCancelled = 2, -- 用户取消了支付请求 SKErrorPaymentInvalid = 3, -- 请求支付的产品标识符无效 SKErrorPaymentNotAllowed = 4, -- 设备不允许进行支付 SKErrorStoreProductNotAvailable = 5, -- 请求的产品在当前店面不可用 SKErrorCloudServicePermissionDenied=6, -- 用户没有授权使用云服务 SKErrorCloudServiceNetworkConnectionFailed = 7, -- 无法连接到网络 SKErrorCloudServiceRevoked = 8, -- 用户的云服务权限已被撤销 SKErrorPrivacyAcknowledgementRequired = 9, -- 用户需要先同意Apple的隐私政策 SKErrorUnauthorizedRequestData = 10, -- 应用试图使用未经授权的数据 SKErrorInvalidOfferIdentifier = 11, -- 提供的优惠标识符无效 SKErrorInvalidSignature = 12, -- 提供的签名无效 SKErrorMissingOfferParams = 13, -- 提供的优惠参数缺失 SKErrorInvalidOfferPrice = 14, -- 提供的优惠价格无效 } -- google play payment PaymentErrorCode.GP = { SERVICE_TIMEOUT = -3, -- 服务超时 FEATURE_NOT_SUPPORTED = -2, -- 不支持功能 SERVICE_DISCONNECTED = -1, -- 服务单元已断开 OK = 0, -- 成功 USER_CANCELED = 1, -- 用户按上一步或取消对话框 SERVICE_UNAVAILABLE = 2, -- 网络连接断开 BILLING_UNAVAILABLE = 3, -- 所请求的类型不支持 Google Play 结算服务 AIDL 版本 ITEM_UNAVAILABLE = 4, -- 请求的商品已不再出售。 DEVELOPER_ERROR = 5, -- 提供给 API 的参数无效。此错误也可能说明应用未针对结算服务正确签名或设置,或者在其清单中缺少必要的权限。 ERROR = 6, -- 操作期间出现严重错误 ITEM_ALREADY_OWNED = 7, -- 未能购买,因为已经拥有此商品 ITEM_NOT_OWNED = 8, -- 未能消费,因为尚未拥有此商品 NETWORK_ERROR = 12, -- 网络故障 } function PaymentErrorCode:init() end PaymentErrorCode:init() PauseUtilW--[[ 暂停工具 author:{zhangpeng} time:2023-04-17 17:23:20 ]] local PauseUtil = {} function PauseUtil.pauseAudio(goOrScene) local result = {} local coms = goOrScene:SeekType(typeof(CS.AudiosComponent)) for i =0,coms.Count - 1 do local com = coms[i] for _,clip in cs_ipairs(com.clips)do if clip.state == CS.AudiosComponent.State.Playing then table.insert(result,clip) clip:Pause() -- printInfo("PauseUtil", "pauseAudio, %s", clip.useSource.source.clip.name) end end end return result end function PauseUtil.resumeAudio(result) for _,clip in ipairs(result or {})do if not CS.LuaHelper.IsNull(clip) then clip:Resume() end end end function PauseUtil.pauseTimeline(goOrScene) local timelines = goOrScene:SeekType(typeof(CS.UnityEngine.Playables.PlayableDirector)) local playstate = CS.UnityEngine.Playables.PlayState.Playing local result = {} for i =0,timelines.Count - 1 do local t = timelines[i] if t.isActiveAndEnabled and t.state == playstate then t.enabled = false table.insert(result, t) end end return result end function PauseUtil.resumeTimeline(result) for i,t in ipairs(result or {}) do if not CS.LuaHelper.IsNull(t) then t.enabled = true end end end function PauseUtil.pauseVideoPlayer(goOrScene) local coms = goOrScene:SeekType(typeof(CS.UnityEngine.Video.VideoPlayer)) local result = {} for i =0,coms.Count - 1 do local com = coms[i] if com.isActiveAndEnabled and not com.isPaused then -- com.gameObject.__videoPlayerOnAppBackgroud = com.time com:Pause() printDebug("PauseUtil.pauseVideoPlayer:" .. tostring(com.gameObject.name)) table.insert(result , com) end end return result end function PauseUtil.resumeVideoPlayer(result) for _,videoPlayer in ipairs(result or {}) do if not CS.LuaHelper.IsNull(videoPlayer) then videoPlayer:Play() for _,f in ipairs(videoPlayer.gameObject.__videoPlayerNotCalledPreparedCallbacks or {})do f() end videoPlayer.gameObject.__videoPlayerNotCalledPreparedCallbacks = {} end end end function PauseUtil.stopVideoPlayer(goOrScene) local coms = goOrScene:SeekType(typeof(CS.UnityEngine.Video.VideoPlayer)) for i =0,coms.Count - 1 do local com = coms[i] com:Stop() printDebug("PauseUtil.stopVideoPlayer:" .. tostring(com.gameObject.name)) end end function PauseUtil.stopAction(goOrScene) local coms = goOrScene:SeekType(typeof(CS.wtween.ActionUpdater)) local result = {} local Destroy = CS.UnityEngine.Object.Destroy for i =0,coms.Count - 1 do local com = coms[i] com.actionList:Clear() com.isDestoried = true Destroy(com) end return result end function PauseUtil.pauseAction(goOrScene) local coms = goOrScene:SeekType(typeof(CS.wtween.ActionUpdater)) local result = {} for i =0,coms.Count - 1 do local com = coms[i] if not com.isPaused then com:Pause() printDebug("PauseUtil.pauseAction:" .. tostring(com.gameObject.name)) table.insert(result , com) end end return result end function PauseUtil.resumeAction(result) for _,action in ipairs(result or {}) do if not CS.LuaHelper.IsNull(action) then action:Resume() end end end function PauseUtil.pauseSpine(goOrScene) local coms = goOrScene:SeekType(typeof(CS.Spine.Unity.SkeletonAnimation)) local result = {} for i =0,coms.Count - 1 do local com = coms[i] com:Initialize(false) if com.AnimationState and com.AnimationState.TimeScale ~= 0 then util.spine.pause(com.gameObject) printDebug("PauseUtil.pauseSpine:" .. tostring(com.gameObject.name)) table.insert(result , com.gameObject) end end return result end function PauseUtil.resumeSpine(result) for _,spineObj in ipairs(result or {}) do if not CS.LuaHelper.IsNull(spineObj) then util.spine.resume(spineObj) end end end function PauseUtil.pauseAll(goOrScene) local result = { audio = PauseUtil.pauseAudio(goOrScene), timeline = PauseUtil.pauseTimeline(goOrScene), video = PauseUtil.pauseVideoPlayer(goOrScene), action = PauseUtil.pauseAction(goOrScene), spine = PauseUtil.pauseSpine(goOrScene), } return result end function PauseUtil.resumeAll(result) PauseUtil.resumeAudio(result.audio) PauseUtil.resumeTimeline(result.timeline) PauseUtil.resumeVideoPlayer(result.video) PauseUtil.resumeAction(result.action) PauseUtil.resumeSpine(result.spine) end function PauseUtil.stopAll(goOrScene) PauseUtil.pauseAudio(goOrScene) PauseUtil.pauseTimeline(goOrScene) PauseUtil.stopVideoPlayer(goOrScene) PauseUtil.stopAction(goOrScene) PauseUtil.pauseSpine(goOrScene) end return PauseUtilTouchCustomListener---@class TouchCustomListener:TouchListener local TouchCustomListener = defClass("TouchCustomListener", TouchListener) function TouchCustomListener:onInit() end ---@param point CS.UnityEngine.Vector3 function TouchCustomListener:onBegan(point) if self._beganCb then self._actived = self._beganCb(point) end end ---@param point CS.UnityEngine.Vector3 function TouchCustomListener:onMoved(point) if self._movedCb then self._movedCb(point) end end ---@param point CS.UnityEngine.Vector3 function TouchCustomListener:onEnd(point) if self._endCb then self._endCb(point) end self._actived = false end LoginScene--[[ 登录场景 author:zhangpeng time:2025-07-20 21:13:57 ]] local LoginScene, super = defClass("LoginScene", Scene) local LOGTAG = "LoginScene" function LoginScene:ctor(sceneInfo) super.ctor(self) self.sceneInfo = sceneInfo self.args = sceneInfo.args or {} self.R = ResLoader.loadResLink("modules/common/login/loginscenereslink") end function LoginScene:info() return { trans = nil, asset = self.R, } end function LoginScene:onLoad() printInfo(LOGTAG,"LoginScene:onLoad updateType:%s", self.updateType) self:initUI() self:testDtween() end function LoginScene:initUI() LoginUI.new(self):show() end -- 测试dtween function LoginScene:testDtween() local role = self.rootNode:Seek("test_anim") -- -- 测试移动 -- util.dtween.MoveTo(role, Vector3(-3, 2, 0), 3, function() -- printInfo(LOGTAG,"LoginScene:testDtween MoveTo") -- end) -- 测试缩放 -- util.dtween.ScaleTo(role, 4, 3, function() -- printInfo(LOGTAG,"LoginScene:testDtween ScaleTo") -- end) -- -- 测试旋转 -- util.dtween.RotateTo(role, Vector3(0, 180, 0), 3, function() -- printInfo(LOGTAG,"LoginScene:testDtween RotateTo") -- end) -- 左右翻转 -- util.dtween.FlipX(role, 0, function() -- printInfo(LOGTAG,"LoginScene:testDtween FlipToLeft") -- end) -- 上下翻转 -- util.dtween.FlipY(role, 3, function() -- printInfo(LOGTAG,"LoginScene:testDtween FlipToLeft") -- end) -- 一边移动一边缩小 util.dtween.DoSpawn({ util.dtween.MoveTo(role, Vector3(-3, 2, 0), 3), util.dtween.ScaleTo(role, 0.5, 3), }, function() printInfo(LOGTAG,"LoginScene:testDtween DoSpawn") end) self.starTime = os.time() -- TimerMgr:addTimerLoop(handler(self, self.updateTimer),1) -- 延时调用 util.dtween.DelayedCall(3, function() printInfo(LOGTAG,"LoginScene:testDtween DelayedCall") end) end function LoginScene:updateTimer() local curTime = os.time() printInfo(LOGTAG,"LoginScene:updateTimer %d", curTime - self.starTime) end function LoginScene:onExit() super.onExit(self) end return LoginScene CSharpUtil_G.cs_ipairs = function(cs_array) local enumerator = cs_array:GetEnumerator() local i = 0 return function() if enumerator:MoveNext() then i = i + 1 return i,enumerator.Current end end end _G.cs_pairs = function(cs_dict) local enumerator = cs_dict:GetEnumerator() return function() if enumerator:MoveNext() then local Current = enumerator.Current return Current.Key,Current.Value end end endFileUtil' local FileUtil = {} local Path = CS.System.IO.Path local File = CS.System.IO.File local Directory = CS.System.IO.Directory -- 拷贝文件 function FileUtil.copyFolder(sourceFolder,destFolder) -- 如果目标路径不存在,则创建目标路径 if not Directory.Exists(destFolder) then Directory.CreateDirectory(destFolder) end -- 得到原文件根目录下的所有文件 local files = Directory.GetFiles(sourceFolder); for i = 0,files.Length-1 do local file = files[i] local name = Path.GetFileName(file); local dest = Path.Combine(destFolder, name); File.Copy(file, dest); --复制文件 end --得到原文件根目录下的所有文件夹 local folders = Directory.GetDirectories(sourceFolder); for i=0,folders.Length-1 do local folder = folders[i] local name = Path.GetFileName(folder); local dest = Path.Combine(destFolder, name); FileUtil.copyFolder(folder, dest); --构建目标路径,递归复制文件 end end -- 路径是否存在 function FileUtil:isDirExist(destFolder) if Directory.Exists(destFolder) then return true end return false end -- 文件是否存在 function FileUtil:isFileExist(filePath) return File.Exists(filePath) end -- 递归获取指定目录下指定文件名后缀的文件路径 function FileUtil.getAllFilesWithExtension(directory, extension) local allFiles = {} local function ends(str, ending) return ending == "" or str:sub(-#ending) == ending end local function getFilesRecursively(dir) local files = Directory.GetFiles(dir) for i = 0, files.Length - 1 do local filePath = files[i] if ends(filePath, extension) then table.insert(allFiles, filePath) end end local subDirs = Directory.GetDirectories(dir) for i = 0, subDirs.Length - 1 do getFilesRecursively(subDirs[i]) end end getFilesRecursively(directory) return allFiles end return FileUtil UITweenDeflocal UITweenDef,_ = defClassStatic("UITweenDef") UITweenDef.TWEEN_TYPE = { SCALE = 0, FADE = 1 } function UITweenDef:init() end UITweenDef:init()mainrequire("framework/core/app/scene/Scene") require("framework/core/app/scene/SceneComponent") require("framework/core/app/scene/SceneCfg") require("framework/core/app/scene/SceneInfo") require("framework/core/app/scene/SceneMgr")UIToasti  ---@class UIToast:UILayer local UIToast, super = defClass("UIToast", UILayer) local LOGTAG = "UIToast" local TMPUGUI = CS.TMPro.TextMeshProUGUI local MAX_WIDTH = 600 --最大宽度 local MIN_WIDTH = 242 --最大宽度 local MIN_HEIGHT = 100 --最小高度 local MIN_HEIGHT_DET = 54 --文本和高度之间的delta local SHOW_TIME = 3 --显示时间 function UIToast:ctor(content, time) super.ctor(self) self.content = content or "默认toast" self.show_time = time or SHOW_TIME self.R = Res.loadResLink("framework/ui/uicoms/reslink/uitoastreslink") end function UIToast:onLoad() if self.__closed then return end self:setPriority(UILayer.UI_ORDER.TOAST) local ui = UnityEngine.GameObject.Instantiate(self.R.toast) self:addChild(ui) self.ui = ui -- self.show_time = SHOW_TIME self:addCloseCallback( function() if UIToast.current == self then UIToast.current = nil end end ) if UIToast.current then UIToast.current:close() end UIToast.current = self self:setContentText(self.content) end function UIToast:setContentText(text) local ui_text = self.ui:Seek("text")[TMPUGUI] ui_text.text = text self.ui_text = ui_text self:autoSize() self:delayHide() return self end function UIToast:delayHide() self.ui:Delay( self.show_time, function() self:close() end ) end function UIToast:autoSize() printInfo(LOGTAG, "UIToast:autoSize") local preferredHeight = self.ui_text.preferredHeight -- if preferredHeight < (MIN_HEIGHT - MIN_HEIGHT_DET) then -- return -- end local preferredWidth = self.ui_text.preferredWidth if preferredWidth > MAX_WIDTH then preferredWidth = MAX_WIDTH end if preferredWidth < MIN_WIDTH then preferredWidth = MIN_WIDTH end local transform = self.ui:Seek("bg"):GetComponent(typeof(UnityEngine.RectTransform)) transform.sizeDelta = UnityEngine.Vector2(preferredWidth + 100, preferredHeight + MIN_HEIGHT_DET) end ---@param show_time number? ---@return UIToast function UIToast:setShowTime(show_time) if self.__closed then return self end if show_time == nil or type(show_time) ~= "number" then return self end self.show_time = show_time return self end return UIToast StringUtil local StringUtil = {} local LOG_TAG = "StringUtil" local unpack = unpack or table.unpack -- 字符串连接 StringUtil.join = function (join_table, joiner) if #join_table == 0 then return "" end local fmt = "%s" for i = 2, #join_table do fmt = fmt .. joiner .. "%s" end return string.format(fmt, unpack(join_table)) end -- 是否包含 -- 注意:plain为true时,关闭模式匹配机制,此时函数仅做直接的 “查找子串”的操作 StringUtil.contains = function (target_string, pattern, plain) plain = plain or true local find_pos_begin, find_pos_end = string.find(target_string, pattern, 1, plain) return find_pos_begin ~= nil end -- 以某个字符串开始 StringUtil.startswith = function (target_string, start_pattern, plain) plain = plain or true local find_pos_begin, find_pos_end = string.find(target_string, start_pattern, 1, plain) return find_pos_begin == 1 end -- 以某个字符串结尾 StringUtil.endswith = function (target_string, start_pattern, plain) plain = plain or true local find_pos_begin, find_pos_end = string.find(target_string, start_pattern, -#start_pattern, plain) return find_pos_end == #target_string end StringUtil.isVersion = function(version) if string.isEmpty(version) then return false end local list = string.split(version, ".") if #list ~= 3 then return false end for i, v in ipairs(list) do local num = tonumber(v) if not num then return false end if num < 0 then return false end end return true end StringUtil.compareVersion = function(versionA, versionB) if not util.string.isVersion(versionA) then printWarn(LOG_TAG, "compareVersion, versionA:%s is not a version", versionA) return 0 end if not util.string.isVersion(versionB) then printWarn(LOG_TAG, "compareVersion, versionB:%s is not a version", versionB) return 0 end local tableA = string.split(versionA, ".") local tableB = string.split(versionB, ".") local maxLength = math.max(#tableA, #tableB) for i = 1, maxLength do local numA = tonumber(tableA[i]) local numB = tonumber(tableB[i]) if numA == nil then return -1 elseif numB == nil then return 1 elseif numA > numB then return 1 elseif numA < numB then return -1 end end return 0 end StringUtil.formatMemorrySize = function(size, unit) if size == nil then return "" end local unitList = {"B", "KB", "MB", "GB", "TB"} local index = 1 if not unit then while size > 1024 and (unitList[index + 1]) do size = size / 1024 index = index + 1 end else for i, v in ipairs(unitList) do if v == unit then index = i break end size = size / 1024 end end return string.format("%.2f%s", size, unitList[index]) end -- 提取其中的数字和字母字符 StringUtil.processNumberAndCharacterText = function(text) local newText = "" local len = #text for i = 1, len do local v = string.sub(text, i, i) if v ~= nil and v ~= "" then local regex = "%w" local re = string.find(v, regex) if re then newText = newText .. tostring(v) end end end return newText end -- 是否CN手机号 StringUtil.isMobile = function(mobile) if not mobile then return false end local len = string.len(mobile) if len ~= 11 then return false end local pattern = "^[1]%d%d%d%d%d%d%d%d%d%d$" return string.match(mobile, pattern) end -- 是否密码 StringUtil.isPwd = function(pwd) if not pwd then return false end local len = string.len(pwd) if len < 8 or len > 12 then return false end for i = 1, len do local v = string.sub(pwd, i, i) if v ~= nil and v ~= "" then local regex = "%w" local re = string.find(v, regex) if not re then return false end end end return true end -- 是否短信验证码 StringUtil.isSmsCode = function(smsCode) if not smsCode then return false end local len = string.len(smsCode) if len ~= 4 then return false end local pattern = "^%d%d%d%d$" return string.match(smsCode, pattern) end -- int转byte StringUtil.formatNumberInBits = function(num, min, gap) min = min or 8 gap = gap or 4 local t = {} -- will contain the bits while num > 0 do local rest = math.fmod(num, 2) t[#t + 1] = rest num = num >> 1 end local t2 = {} for i = 1, math.max(min, #t) do t[i] = t[i] or 0 table.insert(t2, 1, string.format("%d", t[i])) if i % gap == 0 then table.insert(t2, 1, " ") end end local str = table.concat(t2) return str end StringUtil.formatTime = function(time) local day = math.floor(time / 86400) local hour = math.floor(time / 3600) local minute = math.floor((time - hour * 3600) / 60) local second = math.floor(time - hour * 3600 - minute * 60) local str = "" if day > 0 then str = str .. day .. "天" elseif hour > 0 then str = string.format("%02d:%02d:%02d", hour, minute, second) else str = string.format("%02d:%02d", minute, second) end return str end function StringUtil.appendUrlParam(url,key,value) if string.find(url,"?") then return string.format("%s&%s=%s",url,key,value) else return string.format("%s?%s=%s",url,key,value) end end return StringUtil main_editor--[[ 用于editor模式下的boot文件 author:zhangpeng time:2025-07-20 19:24:04 ]] local _ENV = _G --FORCE CLEAN ENV local LOGTAG = "[boot/main_editor]" print(LOGTAG.."start editor mode 777") local json = require("rapidjson") print(LOGTAG.."launch luaengine from here") local luaengine = require("luaengine") local UnityEngine = CS.UnityEngine local AET = CS.AET local isEditor = CS.UnityEngine.Application.isEditor local YooAssetLoader = CS.YooAssetLoader.Instance _G.BOOT_MAIN_FILE = "boot/main" _G.GAME_MAIN_FILE = "main/main" local debug_flag = true if CS.LocalDataStorage.Get("PRINT_EVERY_LUA_CALL") == "true" then debug.sethook(function(event,line) local info = debug.getinfo(2) if info.currentline > 0 then print(string.format("%s:%s:%s:%s:%s",info.short_src,tostring(info.currentline),tostring(info.linedefined),tostring(info.name),tostring(info.namewhat))) end end, "c" ) end local cached_lua_ret_map = {} local CLEAR_ALL_LUA_CACHES = function() print("[boot.main] 清空lua缓存") for k,_ in pairs(cached_lua_ret_map) do cached_lua_ret_map[k] = nil end end local _loadlua = function (bytes, file, opts, env) if bytes == nil or bytes == "" then error("lua文件不存在->"..file..":"..tostring(bytes).. "\n" .. debug.traceback()) end if opts == "b" then print(LOGTAG .. "loadlua:bytes file") bytes = AET.Dec(bytes) end local f,err = load(bytes, file, opts, env) if f then local ok,ret = xpcall(f,function(err) CS.UnityEngine.Debug.LogError(string.format("加载lua失败[%s]%s\n%s",file,tostring(err),debug.traceback())) end) if not string.lower(file):find("reslink") and not isEditor then cached_lua_ret_map[file] = {ret = ret} end return ret, env else CS.UnityEngine.Debug.LogError("加载lua失败" .. file) error(tostring(err) .. "\n" .. debug.traceback()) end end -- 热更结束后加载lua文件(编辑器专用版本) -- @ filename:要加载的lua文件名 -- @ env:lua环境,用于加载 Lua 文件的执行环境 -- _require函数会根据传入的参数直接从文件系统加载指定的 Lua 文件,然后执行它,最终返回加载结果 print(LOGTAG .. "使用编辑器本地文件加载模式") local dataPath = CS.UnityEngine.Application.dataPath local _require = function(env, filename) local ret = cached_lua_ret_map[filename] if ret then return ret.ret end -- print("[require local]", filename) local filepath = dataPath.."/LuaScripts/"..filename..".lua" local src = CS.LuaHelper.ReadFileText(filepath) return _loadlua(src, filename, "bt", env) end local _newenv = function() -- local _G = _G local _E = _G local rawset = _G.rawset local env = { _G = _G, _print = print, CLEAR_ALL_LUA_CACHES = CLEAR_ALL_LUA_CACHES, ENV_REQUIRE = _require } _G.setmetatable( env, { __index = function(t, k) local v = _E[k] rawset(t, k, v) return v end } ) return env end --Run Main Code do print(LOGTAG .. "start run main code") local _ENV = _newenv() _ENV.CLEAR_ENV = function() for k, _ in pairs(_ENV) do _ENV[k] = nil end end _ENV.raw_require = raw_require or require _ENV._require = _require _ENV.require = function(filename, _env) _env = _env or _ENV return _require(_env, filename) end require("boot/build_config") -- 执行main/main.lua print(LOGTAG.." -------- RUN GAME_MAIN_FILE -------- ") require(_G.GAME_MAIN_FILE) end AnimatorUtil --[[ author:{author} time:2022-05-17 17:58:33 ]] local AnimatorUtil = {} local Animator = CS.UnityEngine.Animator local WrapMode = CS.UnityEngine.WrapMode function AnimatorUtil._play(animator,clip,animName,loop,cb) animator:SetEndCb(clip,function() if not loop then animator.enabled = false end if cb then cb() end end) -- animator:StopPlayback() -- animator:Play(animName) animator:SetTrigger(animName) animator.enabled = true end function AnimatorUtil.play(go,animName,loop,cb) local logtag = "AnimatorUtil.play" local animator = go:GetComponent(typeof(Animator)) if animator then local clip = animator:GetClip(animName) if clip then AnimatorUtil._play(animator,clip,animName,loop,cb) else printInfo(logtag,"anim clip not found:%s",animName) end end end function AnimatorUtil.playSeq(go,...) local logtag = "AnimatorUtil.playSeq" local animator = go:GetComponent(typeof(Animator)) if animator then local args = {...} local tasks = {} local function doTask() local task = table.remove(tasks,1) if task then printInfo(logtag,task.animName) local clip = animator:GetClip(task.animName) if clip then AnimatorUtil._play(animator,clip,task.animName,task.loop,function() if task.cb then task.cb() task.cb = nil end if #tasks > 0 then doTask() end end) else printInfo(logtag,"anim clip not found:%s",task.animName) end end end local function parseNext() local arg1 = table.remove(args,1) if not arg1 then return end local t1 = type(arg1) local task = {} if t1 == "string" then task.animName = arg1 local arg2 = args[1] local t2 = type(arg2) if t2 == "boolean" then table.remove(args,1) task.loop = arg2 local arg3 = args[1] local t3 = type(arg3) if t3 == "function" then table.remove(args,1) task.cb = arg3 end elseif t2 == "function" then local f = table.remove(args,1) task.cb = f end table.insert(tasks,task) else return end parseNext() end parseNext() doTask() end end function AnimatorUtil.getCurrentClip(go) local animator = go[Animator] if animator then local clip = animator:GetCurrentAnimatorClipInfo(0); if clip.Length > 0 then return clip[0].clip end end end function AnimatorUtil.isPlaying(go,animName) local animator = go[Animator] if animator then local state = animator:GetCurrentAnimatorStateInfo(0); if state then return state:IsName(animName) end end end return AnimatorUtil PaymentMgr--[[ 支付 ]] local PaymentMgr, super = defClassStatic("PaymentMgr") local LOG_TAG = "PaymentMgr" local IOC_FB_UTIL_CLASS_NAME = "PaymentUtil" local JavaClass = "com/fy/xgame/tilelink/billing/BillingManager" function PaymentMgr:init() if Device.isIOS() then ApplePaymentMgr:init() elseif Device.isAndroid() then GooglePaymentMgr:init() end end function PaymentMgr:check() end function PaymentMgr:buyProduct(shopKey) UIComsTool:showLoading() if Device.isIOS() then ApplePaymentMgr:payByProductId(shopKey) elseif Device.isAndroid() then GooglePaymentMgr:payByProductId(shopKey) end end function PaymentMgr:shareLink(url) if Device.isIOS() then local param = {} luaoc.callStaticMethod(IOC_FB_UTIL_CLASS_NAME, "loginWithFacebook") elseif Device.isAndroid() then local function cb(code, msg) if code == 0 then printInfo(LOG_TAG, "分享成功回调") elseif code == 1 then -- Msg.send(Msg.USER_LOGIN_FB_FAILED) elseif code == 2 then -- Msg.send(Msg.USER_LOGIN_FB_CANCLE) end end luaj.callStaticMethod(JavaClass, "shareLink", { url, cb }) end end PaymentMgr:init()main+ require("framework/cos/CosLuaEnum") require("framework/cos/CosLuaCredentialBean") require("framework/cos/CosLuaTemporaryCredential") require("framework/cos/CosLuaUploadTask") require("framework/cos/CosLuaTagTask") require("framework/cos/CosLuaBatchUploadTask") require("framework/cos/CosLuaMgr")Log--[[ author:wanghuai time:2022-08-01 10:41:23 ]] Log = {} local json = raw_require("rapidjson") local isEditor = CS.UnityEngine.Application.isEditor local useColor = isEditor local raw_print = raw_print local LOG_LEVEL = { VERBOSE = 2, DEBUG = 3, INFO = 4, WARN = 5, ERROR = 6, ASSERT = 7 } local TRACEBACK_LEVEL = { NONE = 0, SIMPLE = 1, FULL = 2 } local LOG_LEVEL_NAME_DICT = { [LOG_LEVEL.VERBOSE] = "VERB", [LOG_LEVEL.DEBUG] = "DEBUG", [LOG_LEVEL.INFO] = "INFO", [LOG_LEVEL.WARN] = "WARN", [LOG_LEVEL.ERROR] = "ERROR", [LOG_LEVEL.ASSERT] = "FATAL" } local LOG_LEVEL_COLOR_DICT = { [LOG_LEVEL.VERBOSE] = "silver", [LOG_LEVEL.DEBUG] = "lightblue", [LOG_LEVEL.INFO] = "green", [LOG_LEVEL.WARN] = "orange", [LOG_LEVEL.ERROR] = "red", [LOG_LEVEL.ASSERT] = "red" } ---@type table local SENSITIVE_STR_DICT = {} local isEnableSensitive = false local function getLogLevelStr(logLevel) local str = LOG_LEVEL_NAME_DICT[logLevel] if not useColor then return str end local color = LOG_LEVEL_COLOR_DICT[logLevel] str = string.format("%s", color, str) return str end function Log.init() Log.LOG_LEVEL = LOG_LEVEL Log.TRACEBACK_LEVEL = TRACEBACK_LEVEL Log.logTagDict = {} Log.logLevelReverseMap = {} for k, v in pairs(LOG_LEVEL) do Log.logLevelReverseMap[v] = k end if isEditor then Log.curLogLevel = LOG_LEVEL.VERBOSE Log.curTracebackLevel = TRACEBACK_LEVEL.FULL else Log.curLogLevel = LOG_LEVEL.INFO Log.curTracebackLevel = TRACEBACK_LEVEL.NONE end end -- # todo next 需要在app内调用 function Log.setLogTagDict(initLogTagDict) Log.logTagDict = initLogTagDict or {} end function Log.log(logLevel, tag, fmt, ...) if logLevel < Log.curLogLevel then return end tag = tostring(tag or "") if Log.logTagDict[tag] == nil then Log.logTagDict[tag] = (logLevel >= Log.curLogLevel) end if Log.logTagDict[tag] == false then return end local logLevelStr = getLogLevelStr(logLevel or LOG_LEVEL.VERBOSE) fmt = "[%s][%s]" .. tostring(fmt) local status, str = pcall(string.format, fmt, logLevelStr, tag, ...) if not status then str = table.concat({...}, " ") end if Log.curTracebackLevel == TRACEBACK_LEVEL.SIMPLE then local info = debug.getinfo(3) or {} local source = info.source if not isEditor then local index = string.find(source, "/Assets/") if index then source = string.sub(source, index + 8) end end str = str .. string.format('\n["%s"]: %d: %s', source, info.currentline, info.name) elseif Log.curTracebackLevel == TRACEBACK_LEVEL.FULL then str = str .. "\n" .. debug.traceback(nil, 3) end -- 日期和 [Lua] 前缀 local timeStr = CS.System.DateTime.Now:ToString("G") str = timeStr .. " [LUA] "..str if isEnableSensitive then for sensitiveStr, value in pairs(SENSITIVE_STR_DICT) do str = string.gsub(str, sensitiveStr, string.rep("*", #sensitiveStr)) end end if logLevel >= LOG_LEVEL.ERROR then CS.UnityEngine.Debug.LogError(str) elseif logLevel == LOG_LEVEL.WARN then CS.UnityEngine.Debug.LogWarning(str) else CS.UnityEngine.Debug.Log(str) end return str end function Log.setLogLevel(level) Log.curLogLevel = level end function Log.setTracebackLevel(level) Log.curTracebackLevel = level end function Log.getLogLevel() return Log.curLogLevel end function Log.getTracebackLevel() return Log.curTracebackLevel end function Log.setSensetiveEnable(enable) isEnableSensitive = enable end -- # todo next 在app内调用 -- 屏蔽print方法,提示开发者用 printInfo ... function Log.disableRawPrint() function print(...) local t = {...} local str = "" for i, v in ipairs(t) do str = str .. tostring(v) .. " " end Log.log(LOG_LEVEL.WARN, "请替换为printXXXX", str) end raw_print = print end function printAssert(LOGTAG, fmt, ...) local tmp = Log.getTracebackLevel() Log.setTracebackLevel(TRACEBACK_LEVEL.FULL) Log.log(LOG_LEVEL.ASSERT, LOGTAG, fmt, ...) Log.setTracebackLevel(tmp) end function printError(LOGTAG, fmt, ...) local tmp = Log.getTracebackLevel() Log.setTracebackLevel(TRACEBACK_LEVEL.FULL) Log.log(LOG_LEVEL.ERROR, LOGTAG, fmt, ...) Log.setTracebackLevel(tmp) end function printWarn(LOGTAG, fmt, ...) Log.log(LOG_LEVEL.WARN, LOGTAG, fmt, ...) end function printInfo(LOGTAG, fmt, ...) Log.log(LOG_LEVEL.INFO, LOGTAG, fmt, ...) end function printDebug(LOGTAG, fmt, ...) Log.log(LOG_LEVEL.DEBUG, LOGTAG, fmt, ...) end function printVerbose(LOGTAG, fmt, ...) Log.log(LOG_LEVEL.VERBOSE, LOGTAG, fmt, ...) end function dump(t, LOGTAG, logLevel) LOGTAG = LOGTAG or "dump" Log.log(logLevel or LOG_LEVEL.DEBUG, LOGTAG, util.serpent.block(t)) end function setSensitiveStr(str, enable) if isEditor then return end if enable then SENSITIVE_STR_DICT[str] = true else SENSITIVE_STR_DICT[str] = nil end end Log.init() -- printAssert("test", "printAssert") -- printError("test", "printError") -- printWarn("test", "printWarn") -- printInfo("test", "printInfo") -- printDebug("test", "printDebug") -- printVerbose("test", "printVerbose") loginuireslinkreturn { --BASIC --ASSET login_ui = {"Assets/AssetsPackage/Res/modules/common/login/ui/prefabs/login_ui.prefab", 0, 0}, } ShortForUnityu--[[ author:{zhangpeng} time:2022-08-14 13:09:31 ]] UnityEngine = CS.UnityEngine UGUI = CS.UnityEngine.UImainrequire("framework/core/db/DBMgr") -- sqlite -- webgl不支持sqlite,不加载相关文件 if not Device.isWebGL() then require("framework/core/db/dbsql/BaseModel") require("framework/core/db/dbsql/SqliteModel") require("framework/core/db/dbsql/SqliteUtil") require("framework/core/db/dbsql/SqliteColumn") require("framework/core/db/dbsql/SqliteFunc") require("framework/core/db/dbsql/SqliteCondition") require("framework/core/db/dbsql/SqliteQuery") require("framework/core/db/dbsql/SqliteDatabase") require("framework/core/db/dbsql/SqliteMgr") require("framework/core/db/dbsql/SqliteTable") require("framework/core/db/dbsql/SingleSqliteTable") require("framework/core/db/dbsql/SyncMgr") end -- key:value require("framework/core/db/dbkv/KVTable") require("framework/core/db/dbkv/KVDatabase") require("framework/core/db/dbkv/KVMgr") -- playerprefs require("framework/core/db/playerprefs/PlayerPrefsMgr") require("framework/core/db/LocalStorageMgr") SqliteFunc ---@class SqliteFunc:LuaClass local SqliteFunc = defClass("SqliteFunc") local LOGTAG = SqliteFunc.__cls_name SqliteFunc.Type = { count = "COUNT", avg = "AVG", min = "MIN", max = "MAX", sum = "SUM" } function SqliteFunc:ctor(table, type) self.table = table self.type = type self.col = nil self.asName = nil self.isDistinct = false end function SqliteFunc:column(col) self.col = col return self end function SqliteFunc:as(asName) self.asName = asName return self end function SqliteFunc:distinct() if not self:canDistinct() then printWarn(LOGTAG, "distinct, %s can not distinct", self.type) end self.isDistinct = true return self end --------------------------------------------------------------------------------------------- -- 工具函数 --------------------------------------------------------------------------------------------- function SqliteFunc:isAggregateFun() local t = { SqliteFunc.Type.count, SqliteFunc.Type.avg, SqliteFunc.Type.min, SqliteFunc.Type.max, SqliteFunc.Type.sum } return table.contain(t, self.type) end function SqliteFunc:canDistinct() return self.type == SqliteFunc.Type.count end function SqliteFunc:toSqlStr() local distinctStr = "" if self.isDistinct and self:canDistinct() then distinctStr = "DISTINCT " end local asStr = "" if not string.isEmpty(self.asName) then asStr = string.format(" AS %s", self.asName) end local col = self.col or "*" local str = string.format("%s(%s%s)%s", self.type, distinctStr, col, asStr) return str end return SqliteFunc main require("framework/network/NetworkStateUtil") if not Device.isWebGL() then -- socket -- require("framework/network/socket/CmdDef") -- require("framework/network/socket/SocketCmdMgr") -- require("framework/network/socket/PBSocketPack") -- require("framework/network/socket/SocketMgr") end -- http require("framework/network/http/HttpCmdDef") require("framework/network/http/HttpCmdMgr")ScreenRecordUtilo--[[ 录屏 author:{zhangpeng} time:2024-11-25 10:32:42 ]] local ScreenRecordUtil, super = defClassStatic("ScreenRecordUtil") local LOG_TAG = "ScreenRecordUtil" local IOS_CLASS_NAME = "ScreenRecorder" local JavaClass = "com/fy/xgame/tilelink/screenRecord/ScreenRecorderUtil" local max_record_time = 120 -- 最大录屏时间120秒 function ScreenRecordUtil:init() self:registLuaCallback() end -- 录屏开始倒计时动画 function ScreenRecordUtil:showCountdown(cb) local spine_prefab = "Assets/AssetsPackage/Res/Modules/common_spines/countdown/prefabs/countdown.prefab" local anim = CS.UnityEngine.GameObject.Instantiate(Res.loadAsset(spine_prefab)) anim:SetParent(util.ScreenUtil:getRootNode()) local render = anim:GetComponent(typeof(CS.UnityEngine.Renderer)) render.sortingOrder = 30 util.spine.play(anim, "idle",false,function () if cb then cb() end end) end -- 开始录屏倒计时,最长2分钟 function ScreenRecordUtil:startTimerCount(updateCallback, finalCallback) if self._countDownAct then return end -- timer ui local prefab = "Assets/AssetsPackage/Res/framework/ui/timerui/daojishi/daojishi.prefab" if not self.timer_ui then self.timer_ui = CS.UnityEngine.GameObject.Instantiate(Res.loadAsset(prefab)) self.timer_ui:SetParent(util.ScreenUtil:getRootNode()) util.ScreenUtil:adaptScreen(self.timer_ui,util.ScreenUtil.layout_type.top_center, CS.UnityEngine.Vector3(0,0,0)) end self._countDownTime = max_record_time self._curTime = 0 self._countDownPause = false self._isCountDownOver = false self._finalCallback = function () self:stopTimerCount() if finalCallback then finalCallback() end end self._updateCallback = function (curTime, totalTime) if updateCallback then updateCallback() end local time = self._countDownTime - curTime if self.timer_ui then self.timer_ui:Seek("text")[CS.TMPro.TextMeshPro].text = math.floor(time) end end self._countDownAct = true self.timerId = Timer:add(function (dt) if self._countDownPause or self._isCountDownOver then return end self._curTime = self._curTime + dt self._updateCallback(self._curTime, self._countDownTime) if self._curTime >= self._countDownTime then self._isCountDownOver = true self._finalCallback() end end, nil, 0) end function ScreenRecordUtil:stopTimerCount() if self.timerId then printInfo(LOG_TAG, "停掉录屏计时器") Timer:rem(self.timerId) self.timer_ui:SetActive(false) end end function ScreenRecordUtil:registLuaCallback() local onScreenRecordStartSuc = function(message) printInfo(LOG_TAG,"ios启动录屏成功,开始录屏倒计时") self:startTimerCount() end local onScreenRecordStartError = function(error) printInfo(LOG_TAG,"ios启动录屏失败") end local onSaveVideoSuc = function () printInfo("保存录屏成功") end local onCancleSaveVideo = function () end local param = { onScreenRecordStartSuc = onScreenRecordStartSuc, onScreenRecordStartError = onScreenRecordStartError, onSaveVideoSuc = onSaveVideoSuc, onCancleSaveVideo = onCancleSaveVideo } if Device.isIOS() then luaoc.callStaticMethod(IOS_CLASS_NAME, "registLuaCallback", param) elseif Device.isAndroid() then luaj.callStaticMethod(JavaClass, "registLuaCallback", {onScreenRecordStartSuc, onScreenRecordStartError}) end end function ScreenRecordUtil:startScreenRecord() self:showCountdown(function () if Device.isIOS() then luaoc.callStaticMethod(IOS_CLASS_NAME, "StartRecording") elseif Device.isAndroid() then luaj.callStaticMethod(JavaClass, "startScreenCapture", {}) -- luaj.callStaticMethod(JavaClass, "startRecording", {}) end end) end function ScreenRecordUtil:pauseScreenRecord() if Device.isIOS() then luaoc.callStaticMethod(IOS_CLASS_NAME, "pauseRecordingWithCompletion",{}) elseif Device.isAndroid() then luaj.callStaticMethod(JavaClass, "pauseRecording", {}) end end function ScreenRecordUtil:stopScreenRecord() self:stopTimerCount() if Device.isIOS() then luaoc.callStaticMethod(IOS_CLASS_NAME, "StopRecording") elseif Device.isAndroid() then luaj.callStaticMethod(JavaClass, "stopRecording", {}) end end ScreenRecordUtil:init()SqliteDatabase ---@class SqliteDatabase:LuaClass local SqliteDatabase = defClass("SqliteDatabase") local sqlite3 = raw_require("lsqlite") local Path = CS.System.IO.Path local Directory = CS.System.IO.Directory local File = CS.System.IO.File local LOGTAG = SqliteDatabase.__cls_name function SqliteDatabase:ctor(filePath) self.filePath = filePath self.db = nil self.key = nil self.transactionList = nil self.tableNameList = {} self.tableNameDict = {} end function SqliteDatabase:open(key) if not self.filePath then return false end local str = string.format(self.filePath) local path = Path.GetDirectoryName(str) .. "/" if Directory.Exists(path) == false then Directory.CreateDirectory(path) end self.db = sqlite3.open(str) self.db:busy_handler( function(...) printWarn(LOGTAG, "open, sqlite is locked, try again") return 1 end, 1 ) self.key = key if self.key then sqlite3.key(self.db, self.key) end self:_updateTableNameList() return true end function SqliteDatabase:close() if self.db == nil then return end printVerbose(LOGTAG, "db close. %s", self) self.db:close() self.db = nil self.key = nil self.transactionList = nil self.tableNameList = {} self.tableNameDict = {} return true end function SqliteDatabase:onExit() self:close() SqliteDatabase.super.onExit(self) end function SqliteDatabase:remove() if File.Exists(self.filePath) then File.Delete(self.filePath) end end function SqliteDatabase:_updateTableNameList() self.tableNameList = self:_getTableNameListFromSql() for i, name in ipairs(self.tableNameList) do self.tableNameDict[name] = name end end function SqliteDatabase:getTable(tableCls, tableName, mgrApis) tableName = tableName or tableCls.__cls_name local apis = { exec = function (...) return self:_exec(...) end, beginTransaction = function (...) return self:_beginTransaction(...) end, finishTransaction = function (...) return self:_finishTransaction(...) end, nrows = function (...) return self:_nrows(...) end, getTimeFunc = mgrApis.getTimeFunc, getVersionFunc = mgrApis.getVersionFunc, } local sqlTable = tableCls.new(apis, tableName) local list = {} for i, col in ipairs(sqlTable:getColumnList()) do local colName = col.name local typeName = col:getSqlType() local str = colName .. " " .. typeName table.insert(list, str) end local str = string.format("CREATE TABLE IF NOT EXISTS %s (%s)", sqlTable:getName(), table.concat(list, ",")) self:_exec(str) self:_alterTable(sqlTable) return sqlTable end function SqliteDatabase:_alterTable(sqlTable) local oldColList = sqlTable:getColumnListFromSql() local newColList = sqlTable:getColumnList() local oldColDict = {} local newColDict = {} for i, col in ipairs(oldColList) do oldColDict[col.name] = col end for i, col in ipairs(newColList) do newColDict[col.name] = col end local addColList = {} local delColList = {} for k, col in pairs(oldColDict) do if not newColDict[k] then table.insert(delColList, col) end end for k, col in pairs(newColDict) do if not oldColDict[k] then table.insert(addColList, col) end end for i, col in ipairs(addColList) do sqlTable:addColumn(col) end end function SqliteDatabase:_beginTransaction() if self.transactionList then printError(LOGTAG, "beginTransaction, 当前正在执行事务") return end self.transactionList = {} printInfo(LOGTAG, "beginTransaction <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<") table.insert(self.transactionList, "begin") end function SqliteDatabase:_finishTransaction() if not self.transactionList then printError(LOGTAG, "finishTransaction, 当前未在执行事务") return end table.insert(self.transactionList, "commit") if #self.transactionList <= 2 then self.transactionList = nil printWarn(LOGTAG, "finishTransaction, 当前事务为空") return end local str = "" for i, transaction in ipairs(self.transactionList) do str = str .. transaction .. ";\n" end self.transactionList = nil local ret = self:_exec(str) if ret ~= 0 then printError(LOGTAG, "数据库事务执行失败,尝试回滚失败") ret = self:_exec("rollback;") end if ret ~= 0 then printError(LOGTAG, "数据库回滚执行失败,数据可能已丢失") end printInfo(LOGTAG, "finishTransaction >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>") return ret end function SqliteDatabase:_exec(str, func) if (not self.db) then printError(LOGTAG, "_exec, db is nil") return end if self.transactionList then table.insert(self.transactionList, str) return end local bm if util.perf then bm = util.perf.BenchMark() end local ret = self.db:exec(str, func) if bm then bm:dump("sqlite exec", 5, str) end if ret ~= 0 then printInfo(LOGTAG, "exec, sql:" .. str .. ", ret:" .. ret) if ret == 13 then printError(LOGTAG, string.format("SQL ERROR SQLITE_CANTOPEN 13: %s", tostring(self.dataBasePath))) elseif ret == 14 then printError(LOGTAG, string.format("SQL ERROR SQLITE_CANTOPEN 14: %s", tostring(self.dataBasePath))) elseif ret == 10 then printError(LOGTAG, string.format("SQL ERROR SQLITE_IOERR 10: %s", tostring(self.dataBasePath))) end local msg = self.db:errmsg() printError(LOGTAG, string.format("SQL ERROR %s, error msg: %s, sql:%s", tostring(ret), tostring(msg), tostring(str))) end return ret end function SqliteDatabase:_nrows(str) if (not self.db) then printError(LOGTAG, "_nrows, db is nil") return end return self.db:nrows(str) end function SqliteDatabase:_getTableNameListFromSql() local str = "select name from sqlite_master WHERE type = 'table'" local tableNameList = {} for row in self.db:nrows(str) do table.insert(tableNameList, row.name) end return tableNameList end function SqliteDatabase:_changeTableName(oldName, newName) if not self.tableNameDict[oldName] then return end if self.tableNameDict[newName] then return end printInfo(LOGTAG, "_changeTableName, from %s to %s", oldName, newName) local str = "ALTER TABLE " .. oldName .. " RENAME TO " .. newName return self:_exec(str) end return SqliteDatabase ParticleUtilm--[[ author:{粒子播放工具} time:2025-04-01 10:45:41 ]] local ParticleUtil = {} function ParticleUtil:play(node) local particles = node.transform:GetComponentsInChildren(typeof(CS.UnityEngine.ParticleSystem)) for _, particle in cs_ipairs(particles) do particle:Stop() particle:Play() end end -- 循环播放粒子 function ParticleUtil:playLoop(node) local particles = node.transform:GetComponentsInChildren(typeof(CS.UnityEngine.ParticleSystem)) for _, particle in cs_ipairs(particles) do particle.main.loop = true particle:Stop() particle:Play() end end function ParticleUtil:stop(node) local particles = node.transform:GetComponentsInChildren(typeof(CS.UnityEngine.ParticleSystem)) for _, particle in cs_ipairs(particles) do particle:Stop() end end return ParticleUtilmainS--[[ luaide 模板位置位于 Template/FunTemplate/NewFileTemplate.lua 其中 Template 为配置路径 与luaide.luaTemplatesDir luaide.luaTemplatesDir 配置 https://www.showdoc.cc/web/#/luaide?page_id=713062580213505 author:{zhangpeng} time:2022-05-17 17:29:25 ]] require("framework/core/base/utils/CSharpUtil") require("framework/core/base/utils/ShortForUnity") require("framework/core/base/utils/uAction") util = { spine = require("framework/core/base/utils/SpineUtil"), animator = require("framework/core/base/utils/AnimatorUtil"), async = require("framework/core/base/utils/async"), string = require("framework/core/base/utils/StringUtil"), time = require("framework/core/base/utils/TimeUtil"), file = require("framework/core/base/utils/FileUtil"), lua = require("framework/core/base/utils/LuaUtil"), perf = require("framework/core/base/utils/PerfUtil"), TableUtil = require("framework/core/base/utils/TableUtil"), ugui = require("framework/core/base/utils/UGuiUtil"), coord = require("framework/core/base/utils/CoordUtil"), fsm = require("framework/core/base/utils/luafsm"), pause = require("framework/core/base/utils/PauseUtil"), serpent = require("framework/core/base/utils/serpent"), common = require("framework/core/base/utils/CommonUtils"), PositionConvert = require("framework/core/base/utils/PositionConvert"), RichTextUtil = require("framework/core/base/utils/RichTextUtil"), TakePhoto = require("framework/core/base/utils/ScreenShotUtil"), BoundUtil = require("framework/core/base/utils/BoundUtil"), GameNodeAdapt = require("framework/core/base/utils/GameNodeAdapt"), particle = require("framework/core/base/utils/ParticleUtil"), dtween = require("framework/core/base/utils/DoTweenAction"), }ExtendPlayableDirector--[[ author:wanglong time:2022-08-29 15:02:41 ]] local PlayableDirector = CS.UnityEngine.Playables.PlayableDirector local PlayableDirectorCls, PlayableDirector_index = HackCSharpClass(PlayableDirector) local origin_Play = PlayableDirectorCls.Play PlayableDirectorCls.__index = function(ud, k) if k == "Play" then -- print("PlayableDirector.Play") -- rawset(_G,"__last_PlayableDirector_Play_traceback__",debug.traceback()) local result = "" for i = 2, 6 do local info = debug.getinfo(i, "Snl") if info then result = result .. string.format("%s:%s:%d;", info.source, info.name, info.currentline) end end if Msg then -- # todo next Msg.send("PlayableDirector_Play", result) end -- BuglySDK:setUserValue("PlayableDirector_Play", result) end return PlayableDirector_index(ud, k) -- c#的方法或者属性 end GooglePaymentMgr --[[ Google支付 ]] local GooglePaymentMgr, super = defClassStatic("GooglePaymentMgr") local LOG_TAG = "GooglePaymentMgr" local IOC_FB_UTIL_CLASS_NAME = "PaymentUtil" local JavaClass = "com/fy/xgame/tilelink/billing/BillingManager" function GooglePaymentMgr:init() self:registIAPLuaCallback() self:registMsgListener() end function GooglePaymentMgr:registMsgListener() Msg.add( { Msg.SHOP_GOOGLE_PURCHASE_SUC, Msg.SHOP_GOOGLE_PURCHASE_FAILED }, function(...) self:listener(...) end ) end function GooglePaymentMgr:listener(msgId, data) if msgId == Msg.SHOP_GOOGLE_PURCHASE_SUC then printInfo(LOG_TAG, "google 支付成功") self:parseProductInfo(data) elseif msgId == Msg.SHOP_GOOGLE_PURCHASE_FAILED then printInfo(LOG_TAG, "google 支付失败 error code:%s", data.errorcode) self:handleErrorCode(data.errorcode) end end -- 根据id购买 function GooglePaymentMgr:payByProductId(productId) luaj.callStaticMethod(JavaClass, "buyProduct", { productId,productId }) end function GooglePaymentMgr:parseProductInfo(rspData) printInfo(LOG_TAG, "解析购买信息") for k,v in pairs(rspData) do printInfo(LOG_TAG, "parseProductInfo key:%s, value:%s", k, v) end UIComsTool:showToast(TextCfgParse:getTextStr("payment_parse_item"),2) Msg.send(Msg.GEM_UPDATE_COUNT, {}) UIComsTool:hideLoading() end function GooglePaymentMgr:registIAPLuaCallback() local purchaseSucCallback = function(param) printInfo(LOG_TAG, "购买成功") Msg.send(Msg.SHOP_GOOGLE_PURCHASE_SUC, {param = param}) end local purchaseFaileCallback = function (errorcode) printInfo(LOG_TAG,"购买失败回调到lua %s",errorcode) Msg.send(Msg.SHOP_GOOGLE_PURCHASE_FAILED, {errorcode = errorcode}) end local purchaseVerifyCallback = function (param) -- 交易验证(把票据信息receipt发给服务器,验签成功后,赋予用户购买的商品) printInfo(LOG_TAG, "购买成功服务器发送服务器 " .. param) luaj.callStaticMethod(JavaClass, "verifyOver", {""}) end local printLog = function(log) printInfo(LOG_TAG, log) end luaj.callStaticMethod(JavaClass, "registSucLuaCallback", { purchaseSucCallback }) luaj.callStaticMethod(JavaClass, "registFailLuaCallback", { purchaseFaileCallback }) luaj.callStaticMethod(JavaClass, "registVerifyLuaCallback", { purchaseVerifyCallback }) luaj.callStaticMethod(JavaClass, "registLuaLogCallback", { printLog }) end function GooglePaymentMgr:handleErrorCode(code) if code == PaymentErrorCode.GP.USER_CANCELED then UIComsTool:showToast(TextCfgParse:getTextStr("payment_cancle"), 1) -- gp 购买取消 else UIComsTool:showToast(TextCfgParse:getTextStr("payment_failed"), 1) -- gp购买失败 end UIComsTool:hideLoading() end ApplovinConstj --[[ author:{zhangpeng} time:2024-04-25 18:00:11 ]] local ApplovinConst,_ = defClassStatic("ApplovinConst") -- 激励视频播放位置标识符 -- ApplovinConst.RewardPlacement = { -- SkinPart = "SkinPartItem", -- 换装界面的皮肤部件 -- SkinSuit = "SkinSuit", -- 换装界面的套装 -- CatItem = "CatItem", -- 猫咪道具 -- Test = "TestPlacement" -- 测试位置 -- } -- 广告加载超时时间 ApplovinConst.adLoadTimeMax = 3 -- 广告位 ApplovinConst.placement = { FreeCoins = "free_coins", -- 看5次广告免费得金币 Victory = "victory", -- 胜利界面 Fail = "fail", -- 失败看广告 ItemGetAd = "item_get_ad" -- 获得道具看广告 } -- 广告类型 ApplovinConst.AdType = { EAdTypeRewardedAd = 100, -- 激励视频 EAdTypeInterstitialAd = 200 -- 插屏广告 }; -- sdk的回调结果类型 ApplovinConst.AdCode = { ESAdCodeLoadSucceeded = 0, -- 加载成功 ESAdCodeLoadFailed = 1, -- 加载失败 ESAdCodeShowSucceeded = 10, -- 显示成功,此时应该暂停游戏音频 ESAdCodeShowFailed = 11, -- 显示失败 ESAdCodeHide = 12, -- 显示完毕,关闭全屏广告,恢复背景音乐等,自动预加载下一个广告 ESAdCodeDidReward = 20, -- 关闭视频,给用户奖励 ESAdCodeClick = 30, -- 点击了广告 ESAdCodeImpression = 40, }; -- 加载的广告数据详情 ApplovinConst.DataEnum = { ad_unit_identifier = "ad_unit_identifier", -- 广告单元标识符,用于标识特定的广告单元 country = "country", -- 国家代码,表示广告请求的国家 creative_identifier = "creative_identifier",-- 创意标识符,用于标识特定的广告创意 network_name = "network_name", -- 网络名称,表示提供广告的广告网络 network_placement = "network_placement", -- 网络放置位置,表示广告在网络上的具体位置或类型 platform = "platform", -- 平台,表示广告平台 revenue = "revenue", -- 收入,表示广告的收入金额 revenue_precision = "revenue_precision" -- 收入精度,表示收入金额的精确度 } function ApplovinConst:init() end ApplovinConst:init()mainhrequire("framework/core/base/resmgr/YooAssetAdapter") require("framework/core/base/resmgr/ResLoader") PBSocketPackp--[[ author:{zhangpeng} time:2023-06-13 20:02:04 ]] local PBSocketPack, super = defClass("PBSocketPack") PBSocketPack.seq = 0 PBSocketPack.headProtoName = "handshake.Request" function PBSocketPack:ctor(head, body, reqBodyProtoName, rspBodyProtoName, callback) self.reqHead = head self.reqBody = body self.rspHead = nil self.rspBody = nil self.rspMsgType = nil self.reqHeadProtoName = PBSocketPack.headProtoName self.reqBodyProtoName = reqBodyProtoName self.rspHeadProtoName = nil--PBSocketPack.headProtoName self.rspBodyProtoName = rspBodyProtoName self.callback = callback self.connection = nil -- seq是数据包的客户端序列号,用于唯一标识数据包 PBSocketPack.seq = PBSocketPack.seq + 1 self.client_seq = PBSocketPack.seq end function PBSocketPack:setConnection(connection) self.connection = connection end function PBSocketPack:getConnection() return self.connection end function PBSocketPack:getSeq() return self.client_seq end function PBSocketPack:getRpcName() if type(self.reqHead) == "table" then if self.reqHead.request_name then return self.reqHead.request_name end else return "no rpc name" end end function PBSocketPack:setReqTime(time) self.reqTime = time or os.time() end function PBSocketPack:getReqTime() return self.reqTime or 0 end function PBSocketPack:setRspTime(time) self.rspTime = time or os.time() end function PBSocketPack:getRspTime() return self.rspTime or 0 end function PBSocketPack:setReqLength(len) self.reqLength = len end function PBSocketPack:getReqLength() return self.reqLength or 0 end function PBSocketPack:setRspLength(len) self.rspLength = len end function PBSocketPack:getRspLength() return self.rspLength or 0 end -- 检查数据包是否完整,是否有请求头和请求体 function PBSocketPack:isComplete() return self.rspHead ~= nil and self.rspBody ~= nil end function PBSocketPack:getError() if not self:isComplete() then return "pack is not complete" end if self:getSeq() ~= self.client_seq then return string.format("pack seq is wrong (%s, %s)", self:getSeq(), self.client_seq) end return nil end local PBTAG = "ProtoBuf" if CS.UnityEngine.Application.isEditor then PBTAG = "" .. PBTAG .. "" end function PBSocketPack:printReq() printInfo(PBTAG, "<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<") printInfo(PBTAG, "REQ, seq:%s, rpcName:%s", self:getSeq(), self:getRpcName()) printInfo(PBTAG, "REQ, reqHead:%s", util.serpent.block(self.reqHead)) printInfo(PBTAG, "REQ, reqBody:%s", util.serpent.block(self.reqBody)) printInfo(PBTAG, "<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<") end function PBSocketPack:printRsp() printInfo(PBTAG, ">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>") printInfo(PBTAG, "RSP, seq:%s, rpcName:%s", self:getSeq(), self:getRpcName()) printInfo(PBTAG, "RSP, rspHead:%s", util.serpent.block(self.rspHead)) printInfo(PBTAG, "RSP, rspBody:%s", util.serpent.block(self.rspBody)) printInfo(PBTAG, ">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>") end function PBSocketPack:printError(errorCode, errorMsg) printInfo(PBTAG, ">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>") printWarn(PBTAG, "ERR, seq:%s, rpcName:%s", self:getSeq(), self:getRpcName()) printWarn(PBTAG, "ERR, error code:%s", errorCode) printWarn(PBTAG, "ERR, error msg:%s", errorMsg) printInfo(PBTAG, ">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>") end return PBSocketPack mainlocal platform = CS.UnityEngine.Application.platform local platformFolderName = "pc/" if platform == CS.UnityEngine.RuntimePlatform.IPhonePlayer then platformFolderName = "ios/" elseif platform == CS.UnityEngine.RuntimePlatform.Android then platformFolderName = "android/" elseif platform == CS.UnityEngine.RuntimePlatform.OSXEditor or platform == CS.UnityEngine.RuntimePlatform.WindowsEditor then platformFolderName = "pc/" end -- 是否加载广告,支付,登录相关(调试阶段先不加载) local debug_use_sdk = false if debug_use_sdk then require("framework/platform/base/XSdkConstants") require("framework/platform/base/XSdkBase") require("framework/platform/" .. platformFolderName .. "XSdk") require("framework/platform/base/framework/PlatformUtil") require("framework/platform/base/applovin/ApplovinConst") require("framework/platform/base/applovin/ApplovinMgr") require("framework/platform/base/firebase/FirebaseEventEnum") require("framework/platform/base/firebase/FirebaseAnalyticsUtil") require("framework/platform/base/facebook/login/FacebookLoginUtil") require("framework/platform/base/apple/login/AppleLoginMgr") require("framework/platform/base/payment/PaymentErrorCode") require("framework/platform/base/payment/ApplePaymentMgr") require("framework/platform/base/payment/GooglePaymentMgr") require("framework/platform/base/payment/PaymentMgr") require("framework/platform/base/device/DeviceVibration") end SpineUtil*--[[ author:{zhangpeng} time:2022-05-17 17:44:42 ]] local SpineUtil = {} local SkeletonAnimation = typeof(CS.Spine.Unity.SkeletonAnimation) local SkeletonGraphic = typeof(CS.Spine.Unity.SkeletonGraphic) local LOG_TAG = "SpineUtil" local function getSpine(gameObject) if gameObject:GetType() ~= typeof(CS.UnityEngine.GameObject) then CS.UnityEngine.Debug.LogError("SpineUtil:第一个参数不是GameObject"); return nil end return gameObject:GetComponent(SkeletonAnimation) or gameObject:GetComponent(SkeletonGraphic) end function SpineUtil.play(gameObject,animName,loop,cb,timescale) if loop == nil then loop = false end if type(loop) ~= "boolean" then CS.UnityEngine.Debug.LogError("SpineUtil.play loop is not boolean") return end local spine = getSpine(gameObject) if spine then timescale = timescale or 1 local trackEntry = spine.AnimationState:SetAnimation(0, animName, loop); spine.AnimationState.TimeScale = timescale if cb then local cb_func cb_func = function() gameObject.__spineFinishCB__ = nil trackEntry:Complete("-",cb_func) cb_func = nil cb() end if gameObject.__spineFinishCB__ then trackEntry:Complete("-", gameObject.__spineFinishCB__) gameObject.__spineFinishCB__ = nil end trackEntry:Complete("+",cb_func) gameObject.__spineFinishCB__ = cb_func Event.add(gameObject,Event.OnDestroy,function() if cb_func then trackEntry:Complete("-",cb_func) cb_func = nil end end) end else error("SpineUtil.play Spine Component not Exist on " .. gameObject.name) end end --[[ 功能: 播放完本次播放的动画之后,开始播放animName cb1 播放完本次动画之后回调 cb2 播放完animName之后回调 timescale1 默认是之前的1倍速,快速播放完本次播放的动画 timescale2 默认是1倍速播放animName ]] function SpineUtil.add(gameObject,animName,cb1,cb2,timescale1, timescale2) local spine = getSpine(gameObject) if spine then timescale1 = timescale1 or 1 local curTrackEntry = spine.AnimationState:GetCurrent(0) curTrackEntry.TimeScale = timescale1 if cb1 then local cb_func cb_func = function() gameObject.__spineFinishCB__ = nil curTrackEntry:Complete("-",cb_func) cb_func = nil cb1() end if gameObject.__spineFinishCB__ then curTrackEntry:Complete("-", gameObject.__spineFinishCB__) gameObject.__spineFinishCB__ = nil end curTrackEntry:Complete("+",cb_func) gameObject.__spineFinishCB__ = cb_func Event.add(gameObject,Event.OnDestroy,function() if cb_func then curTrackEntry:Complete("-",cb_func) cb_func = nil end end) end timescale2 = timescale2 or 1 -- spine.AnimationState:ClearTracks(); local trackEntry = spine.AnimationState:AddAnimation(0, animName, false, 0); trackEntry.TimeScale = timescale2 spine.AnimationState.TimeScale = timescale2 if cb2 then local cb_func cb_func = function() gameObject.__spineFinishCB__ = nil trackEntry:Complete("-",cb_func) cb_func = nil cb2() end if gameObject.__spineFinishCB__ then trackEntry:Complete("-", gameObject.__spineFinishCB__) gameObject.__spineFinishCB__ = nil end trackEntry:Complete("+",cb_func) gameObject.__spineFinishCB__ = cb_func Event.add(gameObject,Event.OnDestroy,function() if cb_func then trackEntry:Complete("-",cb_func) cb_func = nil end end) end else error("SpineUtil.play Spine Component not Exist on " .. gameObject.name) end end function SpineUtil.pause(gameObject) local sp = getSpine(gameObject) gameObject.__spineTimeScaleWhenPause = sp.AnimationState.TimeScale sp.AnimationState.TimeScale = 0 end function SpineUtil.resume(gameObject) local sp = getSpine(gameObject) if gameObject.__spineTimeScaleWhenPause then sp.AnimationState.TimeScale = gameObject.__spineTimeScaleWhenPause end end local tryPlay,parseFunction tryPlay = function(ctx,spine,args,name,loop,delay) local trackEntry if not ctx.playedFirst then ctx.current_info.playedFirst = not (#ctx.tasks == 0) ctx.current_info.loop = loop ctx.playedFirst = true else ctx.current_info.playedFirst = not (#ctx.tasks == 0) local nextArg = args[1] local tNext = type(nextArg) if tNext == "number" then local delay = table.remove(args,1) table.insert(ctx.parsed_args,(delay)) ctx.current_info.loop = loop ctx.current_info.delay = delay elseif tNext == "function" then parseFunction(ctx,spine,args,name,loop,0) else ctx.current_info.loop = loop ctx.current_info.delay = delay end end return trackEntry end parseFunction = function(ctx,spine,args,name,loop,delay) local cb = table.remove(args,1) table.insert(ctx.parsed_args,(cb)) local trackEntry = tryPlay(ctx,spine,args,name,loop,delay) ctx.current_info.callback = cb ctx.hasCallback = true local argNext = args[1] local tNext = type(argNext) if tNext == "number" then local delay = table.remove(args,1) table.insert(ctx.parsed_args,(delay)) tryPlay(ctx,spine,args,name,false,delay) end return trackEntry end local function parseNextSeqArg(spine,args,ctx) local first = args[1] if not first then return end local t1 = type(first) if t1 == "string" then local trackEntry local name = table.remove(args,1) table.insert(ctx.parsed_args,(name)) ctx.current_info = { name = name, loop = false, delay = 0, callback = nil, } local second = args[1] local t2 = type(second) if t2 == "boolean" then local loop = table.remove(args,1) table.insert(ctx.parsed_args,(loop)) parseFunction(ctx,spine,args,name,loop,0) elseif t2 == "function" then parseFunction(ctx,spine,args,name,false,0) else trackEntry = tryPlay(ctx,spine,args,name,false,0) end table.insert(ctx.tasks,ctx.current_info) else local iWrong = #ctx.parsed_args + 1 local argWrong = ctx.orig_args[iWrong] local argPrev = ctx.orig_args[iWrong - 1] local argNext = ctx.orig_args[iWrong + 1] if type(argWrong) == "number" and type(argNext) == "function" then error("SpineUtil.playSeq 不可以先延时后回调,不支持该语义。只能先回调后延时") end for k,v in ipairs(ctx.orig_args) do ctx.orig_args[k] = tostring(v) end for k,v in ipairs(ctx.parsed_args) do ctx.parsed_args[k] = tostring(v) end local orig = table.concat(ctx.orig_args,",") local parsed = table.concat(ctx.parsed_args,",") error(string.format("SpineUtil.playSeq 参数错误\n原始的参数:%s\n成功的参数:%s",orig,parsed)) return end parseNextSeqArg(spine,args,ctx) end function SpineUtil.playSeq(gameObject,...) local args = {...} local spine = getSpine(gameObject) if spine then local ctx = { orig_args = {}, parsed_args = {}, current_info = {}, tasks = {}, playedFirst = false, hasCallback = false, } for _,v in ipairs(args) do table.insert(ctx.orig_args,v) end parseNextSeqArg(spine,args,ctx) local prevTask for _,task in ipairs(ctx.tasks) do if task.playedFirst then task.trackEntry = spine.AnimationState:AddAnimation(0, task.name, task.loop, prevTask.delay); printInfo(LOG_TAG, string.format("[AddAnimation]name:%s,loop:%s,delay:%s",task.name,task.loop,prevTask.delay)) else -- spine.AnimationState:ClearTracks(); task.trackEntry = spine.AnimationState:SetAnimation(0, task.name, task.loop); -- printInfo(LOG_TAG, string.format("[SetAnimation]name:%s,loop:%s",task.name,task.loop)) end if task.callback then local cb_func cb_func = function() gameObject.__spineFinishCB__ = nil task.trackEntry:Complete("-",cb_func) task.callback() cb_func = nil end if gameObject.__spineFinishCB__ then task.trackEntry:Complete("-", gameObject.__spineFinishCB__) gameObject.__spineFinishCB__ = nil end task.trackEntry:Complete("+",cb_func) gameObject.__spineFinishCB__ = cb_func Event.add(gameObject,Event.OnDestroy,function() if cb_func then task.trackEntry:Complete("-",cb_func) cb_func = nil end end) end prevTask = task end return spine else error("SpineUtil.play Spine Component not Exist on " .. gameObject.name) end end function SpineUtil.hideSlot(go,name) local skeleton = getSpine(go) local slot = skeleton.Skeleton:FindSlot(name) slot.A = 0 end function SpineUtil.showSlot(go,name) local skeleton = getSpine(go) local slot = skeleton.Skeleton:FindSlot(name) slot.A = 1 end -- 替换slot图片 function SpineUtil.changeSlotTexture(go, name, sprite) if not go or not name or not sprite then return end local skeleton = getSpine(go) if skeleton then local skeletonDataAsset = skeleton.SkeletonDataAsset local sourceMaterial = skeletonDataAsset.atlasAssets[0].PrimaryMaterial local slot = skeleton.Skeleton:FindSlot(name) local oldAtt = slot.Attachment local newAtt = CS.Spine.Unity.AttachmentTools.AttachmentCloneExtensions.GetRemappedClone(oldAtt, sprite, sourceMaterial, true, true, false, false) slot.Attachment = newAtt end end -- 获取骨骼的世界坐标 function SpineUtil.getBoneWorldPos( go, boneName ) -- 获取骨骼位置 local skeleton = getSpine(go) local bone = skeleton.skeleton:FindBone(boneName) local tmpEndPos = CS.UnityEngine.Vector3(bone.WorldX, bone.WorldY, 0) return tmpEndPos end -- 创建骨骼跟随 默认全部跟随 function SpineUtil.createBoneFollow(go, followObj, boneName, followArgs) followArgs = followArgs or {} if followArgs.followXYPosition == nil then followArgs.followXYPosition = true end if followArgs.followZPosition == nil then followArgs.followZPosition = true end if followArgs.followBoneRotation == nil then followArgs.followBoneRotation = true end local BoneFollower = typeof(CS.Spine.Unity.BoneFollower) local boneFollower = followObj:AddComponent(BoneFollower) local skeleton = getSpine(go) boneFollower.skeletonRenderer = skeleton boneFollower.followXYPosition = followArgs.followXYPosition boneFollower.followZPosition = followArgs.followZPosition boneFollower.followBoneRotation = followArgs.followBoneRotation boneFollower:SetBone(boneName) return boneFollower end function SpineUtil.reset(go) local skeleton = getSpine(go) skeleton.Skeleton:SetToSetupPose() skeleton.AnimationState:ClearTracks() end function SpineUtil.setSkin( go, skinName ) local skeleton = getSpine(go) skeleton.Skeleton:SetSkin(skinName) end function SpineUtil.getCurrentAnim(gameObj) return getSpine(gameObj).AnimationState:GetCurrent(0).Animation.Name end function SpineUtil.getAnimTime(gameObj, name) return getSpine(gameObj).SkeletonDataAsset:GetAnimationStateData().SkeletonData:FindAnimation(name).Duration end return SpineUtilmain& require("framework/ui/UITypeEnums") require("framework/ui/uibase/main") -- coms require("framework/ui/uicoms/UIToast") require("framework/ui/uicoms/UIDialog") require("framework/ui/uicoms/UILoading") require("framework/ui/uicoms/UIComsTool") require("framework/ui/uicoms/UIDialogSimple") PositionConvert?--[[ 世界坐标,屏幕坐标,UGUI坐标相互转换 可转换路径: UI 坐标 -> 屏幕坐标 UI 坐标 -> ViewPort 屏幕坐标 -> 世界坐标 屏幕坐标 -> UI 坐标 世界坐标 -> 屏幕坐标 1:屏幕坐标: 相对于屏幕坐标系 从屏幕左下角开始 坐标为 Vector2 (0, 0),右上角结束坐标为 Vector2(Screen.width, Screen.height) 2:ViewPort坐标: 相对于摄像机视口坐标系 从视口左下角开始 坐标为 Vector2 (0, 0),右上角结束坐标为 Vector2(1, 1) 3:UGUI坐标: 相对于Canvas坐标系,是一种基于屏幕坐标系的特殊坐标系,原点位于Canvas的中心点 从Canvas的中心点开始 x 轴向右延伸,y 轴向上延伸 Canvas的中心有两种情况: Screen Space - Overlay(此项目默认全都使用Overlay) 以屏幕为参考,覆盖在屏幕上方,此时,Canvas中心位于屏幕中心 Screen Space - Camera 以相机为参考,覆盖在相机渲染的区域上方,此时,Canvas中心位于相机视口(ViewPort)的中心 因此,Canvas的中心在屏幕的位置取决于Canvas的渲染模式和Canvas的大小设置 4:世界坐标: 相对于世界坐标系,以米为单位,使用Transform类的position属性获得 注意: UGUI坐标和世界坐标之间不能直接转换,需要通过屏幕坐标中转 author:{zhangpeng} time:2024-03-11 11:30:57 ]] local Screen = CS.UnityEngine.Screen local Vector2 = CS.UnityEngine.Vector2 local Vector3 = CS.UnityEngine.Vector3 local Rect = CS.UnityEngine.Rect local Bounds = CS.UnityEngine.Bounds local PositionConvert = {} -- 通用:将任意UI节点的世界坐标转换为UIRoot为根的世界坐标(适配UI节点不在Canvas根节点的情况) -- @param uiNode 需要转换的UI节点(GameObject或RectTransform) -- @return uirootWorldPos 以UIRoot为根的世界坐标 function PositionConvert.UIPosToWorldPos(uiNode) local uiRoot = CS.UnityEngine.GameObject.Find("UIRoot") if not uiRoot then printWarn("PositionConvert", "找不到UIRoot") return uiNode.transform.position end local uiRootRectTransform = uiRoot:GetComponent(typeof(CS.UnityEngine.RectTransform)) local nodeRectTransform = uiNode:GetComponent(typeof(CS.UnityEngine.RectTransform)) local uiCamera = UILayerUtil:getCamera() local worldPos = nil -- 先把uiNode的位置转换到UIRoot下的局部坐标 local localPos = uiRootRectTransform:InverseTransformPoint(uiNode.transform.position) -- 再转成UIRoot的世界坐标 local uiRootWorldPos = uiRootRectTransform:TransformPoint(localPos) -- 转到屏幕坐标 local screenPos = uiCamera:WorldToScreenPoint(uiRootWorldPos) -- 屏幕坐标转主摄像机世界坐标 local mainCamera = CS.UnityEngine.GameObject.Find("MainCamera") if mainCamera then worldPos = mainCamera:GetComponent("Camera"):ScreenToWorldPoint(screenPos) end return worldPos end -----------------------------------UI------------------------------------- --[[ @desc: UI--->屏幕坐标 time:2024-03-11 11:32:19 ]] function PositionConvert.UIToScreen(uipos) local uiCamera = UILayerUtil:getCamera() local screenPoint = CS.UnityEngine.RectTransformUtility.WorldToScreenPoint(uiCamera, uipos); return screenPoint end --[[ @desc: UI-->ViewPort坐标 author:{author} time:2024-03-11 11:52:53 @return: ]] function PositionConvert.UIToViewpoint() end ----------------------------------屏幕-------------------------------------- --[[ @desc: 屏幕 -> 世界 time:2024-03-11 12:11:55 ]] function PositionConvert.ScreenToWorld(screenPos) local screen_pos = CS.UnityEngine.Vector3(screenPos.x, screenPos.y, 0) local worldPostion = UILayerUtil:getCamera():ScreenToWorldPoint(screen_pos) return worldPostion end --[[ @desc: 屏幕 -> ViewPort time:2024-03-11 12:12:55 ]] function PositionConvert.ScreenToViewPort(screenPos) local screen_pos = CS.UnityEngine.Vector3(screenPos.x, screenPos.y, 0) local viewPortPostion = UILayerUtil:getCamera():ScreenToViewportPoint(screen_pos) return viewPortPostion end --[[ @desc: 屏幕 -> UI @canvas: 为UI所在的Canvas。 time:2024-03-11 11:35:29 ]] function PositionConvert.ScreenToUI(screenPos, canvas) -- local worldPos = PositionConvert.ScreenToWorld(screenPos) -- local uiPos = Vector2.zero -- RectTransformUtility.ScreenPointToLocalPointInRectangle(canvas.transform, screenPos, nil, uiPos); -- -- img的锚点需要在屏幕中间,此时就可正确的设置其坐标了。 -- img.rectTransform.anchoredPosition = uiPos; end --------------------------------------ViewPort---------------------------------- function PositionConvert.ViewportToWorld(screenPos) local screen_pos = CS.UnityEngine.Vector3(screenPos.x, screenPos.y, 0) local viewPortPostion = UILayerUtil:getCamera():ViewportToWorldPoint(screen_pos) return viewPortPostion end --------------------------------------世界---------------------------------- --[[ @desc: 世界 -> 屏幕 time:2024-03-11 12:00:42 ]] function PositionConvert.WorldToScreen(worldPosition) local cam = UILayerUtil:getCamera() local worldpos = CS.UnityEngine.Vector3(worldPosition.x, worldPosition.y, 0) local screenpos = cam:GetComponent("Camera"):WorldToScreenPoint(worldpos) return screenpos end --[[ @desc: 世界 -> ViewPort time:2024-03-11 12:07:19 ]] function PositionConvert.WorldToViewPort(worldPosition) local cam = UILayerUtil:getCamera() local worldpos = CS.UnityEngine.Vector3(worldPosition.x, worldPosition.y, 0) local viewport = cam:GetComponent("Camera"):WorldToViewportPoint(worldpos) return viewport end --[[ @desc: 世界坐标转换为RectTransform 对象的本地坐标 time:2024-03-11 14:43:42 ]] function PositionConvert.WorldToRectTrancLocalPos(gameObject, rectTransform) local localPos = rectTransform.transform:InverseTransformPoint(gameObject.transform.position) return localPos end --[[ @desc: 世界坐标转UGUI坐标(角色头上顶UI,取的角色头上坐标之后,转换为UGUI坐标,把UI设置到该位置) author:{author} time:2024-03-11 22:34:37 ]] function PositionConvert.WorldToUI(position, canvasRectTransform) local scene = SceneMgr:getCurScene() local mainCamera = scene.cams.scene local screenPoint = mainCamera.WorldToScreenPoint(position) local screenSize = Vector2(Screen.width, Screen.height); screenPoint = screenPoint - screenSize/2 -- 将屏幕坐标变换为以屏幕中心为原点 local anchorPos = screenPoint / screenSize * canvasRectTransform.sizeDelta -- 缩放得到UGUI坐标 return anchorPos; end --[[ @desc: author:{author} time:2024-03-11 15:08:26 ]] function PositionConvert.getSafeScreenRectInCoord(camera,coord,isIgnoreBottom) local safeArea = CS.UnityEngine.Screen.safeArea if isIgnoreBottom then safeArea.yMin = 0 end local bl = safeArea.min local tr = safeArea.max bl = CS.UnityEngine.Vector3(bl.x, bl.y, 0) tr = CS.UnityEngine.Vector3(tr.x, tr.y, 0) bl = camera:ScreenToWorldPoint(bl) tr = camera:ScreenToWorldPoint(tr) bl = coord.transform:InverseTransformPoint(bl) tr = coord.transform:InverseTransformPoint(tr) return CS.UnityEngine.Rect(bl.x, bl.y, tr.x - bl.x, tr.y - bl.y) end return PositionConvert UIComsToolw --[[ UI组件工具类 author:{zhangpeng} time:2023-08-01 17:37:41 ]] local UIComsTool = defClassStatic("UIComsTool") local LOG_TAG = "UIComsTool" local TMPUGUI = CS.TMPro.TextMeshProUGUI function UIComsTool:init() self:clearLoadingState() end --@region loading show and hide function UIComsTool:clearLoadingState() self.loadingCount = 0 self.loadingPanel = nil end -- mask alpha value:0-1 function UIComsTool:showLoading(maskalpha) maskalpha = maskalpha or 0.5 self.loadingCount = self.loadingCount + 1 if self.loadingCount <= 0 then return end if self.loadingPanel then return end self.loadingPanel = UILoading.new(true):show():showMask(maskalpha):addCloseCallback( function() self:clearLoadingState() end ) end function UIComsTool:hideLoading(isAll) if isAll then self.loadingCount = 0 else self.loadingCount = self.loadingCount - 1 end printInfo(LOG_TAG, "loading count: %s", self.loadingCount) if self.loadingCount > 0 then return end if self.loadingPanel then self.loadingPanel:close() end self:clearLoadingState() end --@endregion --#region show toast function UIComsTool:showToast(text, showtime, closecb, isGlobal) showtime = showtime or 1 if isGlobal == nil then isGlobal = true end return UIToast.new():show(isGlobal):setShowTime(showtime):setContentText(text):setPriority(UILayer.UI_ORDER.TOP_DEFAULT) end --#endregion function UIComsTool:showSimplyDialogBox(text) return UIDialogSimple.new():show():showMask(0.5):setContentText(text):setPriority(UILayer.UI_ORDER.TOP_DEFAULT) end -- 显示特殊外观的弹框 function UIComsTool:showDailogByStyle(style, content_text, btn_left_text, cb_left, price) content_text = content_text or "no content!" local dialog = self:createDialog(style) -- dialog:enableCloseWhenClickMask() dialog:setContentText(content_text) dialog.ui:Seek("price")[TMPUGUI].text = string.format("x%s", price) if btn_left_text then local btn_text = btn_left_text dialog:showSingleBtn(btn_text, cb_left) end return dialog end function UIComsTool:createDialog(style) local dialog = UIDialog.new(style) dialog:show(false) return dialog end --#region show dialog function UIComsTool:showDailog(content_text, btn_left_text, cb_left, btn_right_text, cb_right,tag) content_text = content_text or "no content!" local dialog = self:createDialog(UITypeEnums.DialogType.Common) dialog:setContentText(content_text) dialog:hideCloseBtn() dialog:setTagName(tag or "uidialog") if btn_left_text and btn_right_text then dialog:showDoubleBtns(btn_left_text, btn_right_text, function (ui) if cb_left then cb_left(ui) end end, function (ui) if cb_right then cb_right(ui) end end ) elseif btn_left_text or btn_right_text then local btn_text = btn_left_text or btn_right_text dialog:showSingleBtn(btn_text, function (ui) local cb = cb_left or cb_right if cb then cb(ui) end end ) end return dialog end --#endregion UIComsTool:init() TimelineUtilX  util.timeline = {} local GameObject = CS.UnityEngine.GameObject local PlayableDirector = CS.UnityEngine.Playables.PlayableDirector local function getTimeline(timelineOrGo) local t = timelineOrGo:GetType() if t == typeof(GameObject) then return timelineOrGo:GetComponent(typeof(PlayableDirector)) elseif t == typeof(PlayableDirector) then return timelineOrGo else CS.UnityEngine.Debug.LogError("TimelineUtil:第一参数不是GameObject或者PlayableDirector,而是" .. tostring(t)); end end function util.timeline.getTimeline( timelineOrGo ) return getTimeline(timelineOrGo) end function util.timeline.play(timelineOrGo,finishCb,isLoop) local timeline = getTimeline(timelineOrGo) local func func = function() if not timeline.gameObject.scene.isLoaded then return--场景销毁时同样会触发stopped事件,时机早于OnDisable和OnDestroy,所以只能靠此判断 end timeline.gameObject.__tlFinishCB__ = nil timeline:stopped("-", func) if finishCb then finishCb() end func = nil end if timeline.gameObject.__tlFinishCB__ then timeline:stopped("-", timeline.gameObject.__tlFinishCB__) timeline.gameObject.__tlFinishCB__ = nil end timeline:stopped("+", func) timeline.gameObject.__tlFinishCB__ = func Event.add(timeline.gameObject,Event.OnDestroy,function() if func then timeline:stopped("-", func) func = nil end end) if isLoop then timeline:Play(timeline.playableAsset, CS.UnityEngine.Playables.DirectorWrapMode.Loop) else timeline:Play() end return timeline end function util.timeline.stop(timelineOrGo) local function resetTl( timeline ) timeline:ClearAllSignalCb() timeline.time = timeline.duration timeline:RebuildGraph() timeline:Evaluate() timeline:Stop() end local timeline = getTimeline(timelineOrGo) -- stop之后,必须清掉回调,要不然还是会调用 if timeline.gameObject.__tlFinishCB__ then timeline:stopped("-", timeline.gameObject.__tlFinishCB__) timeline.gameObject.__tlFinishCB__ = nil end resetTl(timeline) end function util.timeline.reset(timelineOrGo) local function resetTl( timeline ) timeline:ClearAllSignalCb() timeline.time = 0 timeline:RebuildGraph() timeline:Evaluate() timeline:Stop() end local timeline = getTimeline(timelineOrGo) if timeline.gameObject.__tlFinishCB__ then timeline:stopped("-", timeline.gameObject.__tlFinishCB__) timeline.gameObject.__tlFinishCB__ = nil end resetTl(timeline) end function util.timeline.pause(timelineOrGo) local timeline = getTimeline(timelineOrGo) timeline:Pause() end StrogeKeyDefQ--[[ 本地数据的key author:{zhangpeng} time:2023-06-16 15:21:03 ]] local StrogeKeyDef = defClassStatic("StrogeKeyDef") -- 用户相关(使用LocalStorageMgr:set/:get操作) StrogeKeyDef.DEVICE_ID = "DEVICE_ID" --登录用 StrogeKeyDef.USER_ID_KEY_TEST = "USER_ID_KEY_TEST" StrogeKeyDef.USER_NAME_KEY_TEST = "USER_NAME_KEY_TEST" -- 用户登录信息 StrogeKeyDef.USER_LAST_USER_ID = "USER_LAST_USER_ID" -- 上次登录的userid StrogeKeyDef.USER_LAST_USER_NAME = "USER_LAST_USER_NAME" -- 上次登录的user name StrogeKeyDef.USER_LAST_TOKEN = "USER_LAST_TOKEN" -- 上次登录的token StrogeKeyDef.USER_LAST_LOGIN_TYPE = "USER_LAST_LOGIN_TYPE" -- 上次登录类型 -- 用户货币数据 StrogeKeyDef.USER_COIN_COUNT = "USER_COIN_COUNT" -- 用户金币数量 --设备相关(使用CS.LocalDataStorage.Get/.Set操作) StrogeKeyDef.DEVICE_PRETEND_NET_NOT_OPEN = "DEVICE_PRETEND_NET_NOT_OPEN" -- 假装网络未开 StrogeKeyDef.DEVICE_PRETEND_WIFI_NET_CLOSE = "DEVICE_PRETEND_WIFI_NET_CLOSE" -- 假装wifi关闭 StrogeKeyDef.DEVICE_PRETEND_USE_CELLUAR_NET = "DEVICE_PRETEND_USE_CELLUAR_NET" -- 假装使用移动网络 StrogeKeyDef.DEVICE_ALWAYS_SHOW_ERR_DIALOG = "ALWAYS_SHOW_ERR_DIALOG" -- 允许显示lua报错弹框 -- 新手相关 StrogeKeyDef.GUIDE_WORLD_MAP = "GUIDE_WORLD_MAP" -- 是否执行大地图新手 return StrogeKeyDef UILayerUtil&5 ---@class UILayerUtil:LuaStaticClass local UILayerUtil = defClassStatic("UILayerUtil") local LOGTAG = "UILayerUtil" local UnityEngine = CS.UnityEngine local UGUI = CS.UnityEngine.UI UILayerUtil.SORTING_ORDER = { LOCAL_UI = 10000, GLOBAL_UI = 30000 -- 32767是允许的最大值 } local UIConfig = { safeWidth = 1098, safeHeight = 2048, } local LOCAL_UI_ROOT_NAME = "__ROOT_UI__" local GLOBAL_UIROOT_NAME = "UILayerUtil.uiroot" local UI_MASK_NAME = "__UIMASK__" function UILayerUtil:init() if self:isInited() then return end local needCreate = false self.layerIndex = 0 self.topMaskLayer = nil -- 最上层带遮罩的uilayer local goname = "UINodeGameObject" local uinode = GameObject.Find(goname) if not uinode then needCreate = true uinode = GameObject.Instantiate(Resources.Load("ui/uinode")) uinode:SetName(goname) GameObject.DontDestroyOnLoad(uinode) end ---@type UIRoot[] self._uirootList = {} self.uinode = uinode self.canvas = uinode:Seek("UICanvas") self.prefab = uinode:Seek("UIPrefab") self.mask = uinode:Seek("UIMask") self.camera = uinode:Seek("UICamera") self.eventSystem = uinode:Seek("UIEventSystem") self:calcCanvasScalerFactor(self.canvas) self.mask:SetActive(false) if needCreate then self.uiroot = UnityEngine.GameObject.Instantiate(self.prefab) self.uiroot:SetParent(self.prefab.transform.parent.gameObject, false) self.uiroot:SetName(GLOBAL_UIROOT_NAME) else self.uiroot = self.prefab.transform.parent.gameObject:Child(GLOBAL_UIROOT_NAME) end -- 相机渲染类型改成Overlay local cam = self.camera:GetComponent(typeof(UnityEngine.Camera)) local data = CS.UnityEngine.Rendering.Universal.CameraExtensions.GetUniversalAdditionalCameraData(cam) data.renderType = CS.UnityEngine.Rendering.Universal.CameraRenderType.Overlay self.cameraCom = cam local canvasCom = self.canvas:GetComponent(typeof(UnityEngine.Canvas)) canvasCom.sortingOrder = self.SORTING_ORDER.GLOBAL_UI -- 局部ui在全局ui下方 if needCreate then -- clear all children local trans = self.canvas.transform local Destroy = UnityEngine.GameObject.Destroy for i = 0, trans.childCount - 1 do local child = trans:GetChild(i).gameObject if child ~= self.prefab and child ~= self.uiroot and child ~= self.mask then Destroy(child) end end end end function UILayerUtil:tryAddAppExitMsg() if rawget(Msg, "APP_EXIT") then Msg.add(Msg.APP_EXIT, function () self:CloseAllGlobal() self.camera:SetActive(false) end) end self.tryAddAppExitMsg = function () end end function UILayerUtil:getLocalUI() local uinode = UnityEngine.GameObject.Find(LOCAL_UI_ROOT_NAME) local uiroot,uirootObj if not uinode then uinode = UnityEngine.GameObject.Instantiate(UnityEngine.Resources.Load("ui/uinodelocal")) uinode:SetName(LOCAL_UI_ROOT_NAME) uiroot = uinode:Seek("UIPrefab") uinode:Seek("UIMask"):SetActive(false) local canvas = uinode:Seek("UICanvas") self:calcCanvasScalerFactor(canvas) local canvasCom = canvas:GetComponent(typeof(UnityEngine.Canvas)) canvasCom.sortingOrder = self.SORTING_ORDER.LOCAL_UI canvasCom.worldCamera = self.cameraCom canvas.transform.sizeDelta = self.canvas.transform.sizeDelta -- fix issue,sizeDelta 的值不正确 local uiMask = uinode:Seek("UIMask") uiMask:SetActive(true) uiMask:SetScalef(0) local img = uiMask[UGUI.Image] if img then local color = img.color color.a = 0 img.color = color end uirootObj = UILayerUtil:_getOrCreateUIRoot(uiroot, false, uiMask) else uiroot = uinode:Seek("UIPrefab") uirootObj = UILayerUtil:_getOrCreateUIRoot(uiroot) end return uirootObj, uinode end ---@private function UILayerUtil:refreshMask() local globalStack = self:getGlobalUI():getUILayerStack() or {} local localStack = self:getLocalUI():getUILayerStack() or {} local found = false self.topMaskLayer = nil for i = #globalStack, 1, -1 do local ui = globalStack[i] local mask = ui[UI_MASK_NAME] if mask then if found then mask:SetActive(false) else mask:SetActive(true) found = true self.topMaskLayer = ui end end end for i = #localStack, 1, -1 do local ui = localStack[i] local mask = ui[UI_MASK_NAME] if mask then if found then mask:SetActive(false) else mask:SetActive(true) found = true self.topMaskLayer = ui end end end local localMask = self:getLocalUI():getUIMask() if not localMask then return end if found then localMask:SetScalef(1) else localMask:SetScalef(0) end end function UILayerUtil:setCurScene(scene) self.scene = scene end function UILayerUtil:getCurScene() return self.scene end function UILayerUtil:canShowPopup() return self._canShowPopup ~= false end function UILayerUtil:setCanShowPopup(b) self._canShowPopup = b end function UILayerUtil:genEmptyUILayerProxy(uiLayer) local proxy = { __isProxy = true, } setmetatable(proxy,{ __index = function(t,k) local emptyFunc = function(...) print("proxy",uiLayer.__cls_name,k,...) return proxy end return emptyFunc end, }) return proxy end function UILayerUtil:CloseAllLocal(dontDestroy) local stack = self:getLocalUI():getUILayerStack() for i = #stack, 1, -1 do local layer = stack[i] if layer then layer:close(dontDestroy) end end end function UILayerUtil:PrintLocalUIList() local stack = self:getGlobalUI():getUILayerStack() for _, layer in ipairs(stack) do printInfo(LOGTAG, "UILayer inst tag:%d SiblingIndex:%d", layer.tag, layer.__uiContainer.transform:GetSiblingIndex()) end end function UILayerUtil:getLocalByTag(tag) local stack = self:getLocalUI():getUILayerStack() for _, ui in ipairs(stack) do if ui.__tag == tag then return ui end end end function UILayerUtil:getGlobalByTag(tag) local stack = self:getGlobalUI():getUILayerStack() for _, ui in ipairs(stack) do if ui.__tag == tag then return ui end end end --#region pause resume function UILayerUtil:onAppPause() local rootUIObj = self:getLocalUI() if rootUIObj:getPauseList() then return end local pauseList = {audio = {}, timeline = {}, videoplayer = {}, action = {} } pauseList.audio = util.pause.pauseAudio(rootUIObj:getGameObject()) pauseList.timeline = util.pause.pauseTimeline(rootUIObj:getGameObject()) pauseList.videoplayer = util.pause.pauseVideoPlayer(rootUIObj:getGameObject()) pauseList.action = util.pause.pauseAction(rootUIObj:getGameObject()) rootUIObj:setPauseList(pauseList) end function UILayerUtil:onAppResume() local rootUIObj = self:getLocalUI() if not rootUIObj:getPauseList() then return end local pauseList = rootUIObj:getPauseList() rootUIObj:setPauseList(nil) util.pause.resumeAudio(pauseList.audio) util.pause.resumeTimeline(pauseList.timeline) util.pause.resumeVideoPlayer(pauseList.videoplayer) util.pause.resumeAction(pauseList.action) end function UILayerUtil:stopLocalUI() local rootGo = self:getLocalUI():getGameObject() util.pause.stopAll(rootGo) end --#endregion function UILayerUtil:calcCanvasScalerFactor(canvasGo) local BEST_SCREEN_RATIO = UIConfig.safeWidth / UIConfig.safeHeight local screenWidth = UnityEngine.Screen.width local screenHeight = UnityEngine.Screen.height -- 如果配置了四个方向都支持,启动的时候可能是竖屏,导致算出来的缩放比例不对 -- if screenWidth < screenHeight then -- screenWidth,screenHeight = screenHeight,screenWidth -- end local CURRENT_SCREEN_RATIO = screenWidth / screenHeight local canvas = canvasGo:GetComponent(typeof(UnityEngine.Canvas)) if not self.scaleFactor then if CURRENT_SCREEN_RATIO > BEST_SCREEN_RATIO then self.scaleFactor = screenHeight / UIConfig.safeHeight else self.scaleFactor = screenWidth / UIConfig.safeWidth end end canvas.scaleFactor = self.scaleFactor end ---获取全局的 uiroot 和 uinode ---@return UIRoot ---@return CS.UnityEngine.GameObject|CS.UnityEngine.Object function UILayerUtil:getGlobalUI() if not self.uirootObj then self.uirootObj = self:_getOrCreateUIRoot(self.uiroot, true, nil) end return self.uirootObj, self.uinode end function UILayerUtil:_getOrCreateUIRoot(go, isGlobalUI, uiMask) for index, value in ipairs(self._uirootList) do if value.go == go then return value end end local uirootObj = UIRoot.new(go, isGlobalUI, uiMask) table.insert(self._uirootList, uirootObj) return uirootObj end function UILayerUtil:_removeUIRoot(uirootObj) table.removeByValue(self._uirootList, uirootObj, true) end function UILayerUtil:CloseAllGlobal() printInfo(LOGTAG, "CloseAllGlobal") local stack = self:getGlobalUI():getUILayerStack() or {} for i = #stack, 1, -1 do local layer = stack[i] layer:close() end for index, uirootObj in ipairs(self._uirootList) do uirootObj:exit() end end function UILayerUtil:isInited() return not CS.LuaHelper.IsNull(self.uiroot) end --#region UILayer api function UILayerUtil:_addLayerIndex() self.layerIndex = self.layerIndex + 1 return self.layerIndex end function UILayerUtil:_getPrefabGameObject() return UnityEngine.GameObject.Instantiate(self.prefab) end function UILayerUtil:_getMaskGameObject() return UnityEngine.GameObject.Instantiate(self.mask) end function UILayerUtil:_getEventSystem() return self.eventSystem[UnityEngine.EventSystems.EventSystem] end --#endregion function UILayerUtil:getCamera() return self.cameraCom end function UILayerUtil:getCanvas() return self.canvas end function UILayerUtil:isGlobalUIGo(go) return go:SeekInParentHierarchy(GLOBAL_UIROOT_NAME) end function UILayerUtil:isUIGo(go) return go:SeekInParentHierarchy("UICanvas") end -- 获取屏幕ui大小 function UILayerUtil:getCanvasSize() if not self.uinode then return CS.UnityEngine.Vector2(CS.UnityEngine.Screen.width, CS.UnityEngine.Screen.height) end local canvas = self.uinode:Seek("UICanvas") return canvas.transform.sizeDelta end function UILayerUtil:findUILayerOfGo(go) local function findUILayerIsGo(_go) for index, value in ipairs(self._uirootList) do local stack = value:getUILayerStack() for _, layer in ipairs(stack) do if layer:getUIContainer() == _go then return layer end end end return nil end local curr = go while curr do local layer = findUILayerIsGo(curr) if layer then return layer end curr = curr:GetParent() end end function UILayerUtil:setMaskAlpha(alpha) self._maskAlpha = alpha end function UILayerUtil:getMaskAlpha() return self._maskAlpha or 0.63 end --- 根据UI界面名称关闭对应的UI ---@param uiName string UI界面的类名 ---@return boolean 是否成功关闭UI function UILayerUtil:closeUIByName(uiName) if not uiName then printInfo(LOGTAG, "closeUIByName: uiName is nil") return false end -- 先检查全局UI栈 local globalStack = self:getGlobalUI():getUILayerStack() or {} for i = #globalStack, 1, -1 do local layer = globalStack[i] if layer.__cls_name == uiName then layer:close() return true end end -- 再检查局部UI栈 local localStack = self:getLocalUI():getUILayerStack() or {} for i = #localStack, 1, -1 do local layer = localStack[i] if layer.__cls_name == uiName then layer:close() return true end end printInfo(LOGTAG, "closeUIByName: UI '%s' not found", uiName) return false end -- 根据名字查找UI function UILayerUtil:findUIByName(uiName) local globalStack = self:getGlobalUI():getUILayerStack() or {} local localStack = self:getLocalUI():getUILayerStack() or {} for _, layer in ipairs(globalStack) do if layer.__cls_name == uiName then return layer end end for _, layer in ipairs(localStack) do if layer.__cls_name == uiName then return layer end end return nil end -- 判断某一个界面是否打开 function UILayerUtil:isUIOpen(uiName) local globalStack = self:getGlobalUI():getUILayerStack() or {} local localStack = self:getLocalUI():getUILayerStack() or {} for _, layer in ipairs(globalStack) do if layer.__cls_name == uiName then return true end end for _, layer in ipairs(localStack) do if layer.__cls_name == uiName then return true end end return false end UILayerUtil:init() CurrencyData--[[ 货币数据 author:{zhangpeng} time:2025-08-20 10:23:16 ]] local CurrencyData = defClassStatic("CurrencyData") local LOGTAG = CurrencyData.__cls_name function CurrencyData:ctor() self:init() end -- 初始化并赋默认值 function CurrencyData:init() self.currencyData = {} self:reset() end -- 重置货币数据 function CurrencyData:reset() self.currencyData.coin = 0 self.currencyData.diamond = 0 end -- 从本地playerprefs加载 function CurrencyData:loadFromPlayerPrefs() self.currencyData.coin = PlayerPrefsMgr:getInt("coin") self.currencyData.diamond = PlayerPrefsMgr:getInt("diamond") end -- 从服务器返回数据加载 function CurrencyData:loadFromServer(serverData) self.currencyData.coin = serverData.coin self.currencyData.diamond = serverData.diamond end -- 保存到本地playerprefs function CurrencyData:saveToPlayerPrefs() PlayerPrefsMgr:setInt("coin", self.currencyData.coin) PlayerPrefsMgr:setInt("diamond", self.currencyData.diamond) end -- 保存到服务器 function CurrencyData:syncToServer() -- TODO: 保存到服务器 end return CurrencyData XSdk--[[ luaide 模板位置位于 Template/FunTemplate/NewFileTemplate.lua 其中 Template 为配置路径 与luaide.luaTemplatesDir luaide.luaTemplatesDir 配置 https://www.showdoc.cc/web/#/luaide?page_id=713062580213505 author:{author} time:2023-09-25 23:13:54 ]] local XSdk, super = defClassStatic("XSdk", XSdkBase) function XSdk:init() self.appVersion = "1.1.1" self.groupId = 46 self.showUserId = "" self.deviceId = "" end function XSdk:getDeviceId() if not string.isEmpty(self.deviceId) then return self.deviceId end local deviceId = "1" --SystemInfo.deviceUniqueIdentifier--"616fda05b7e5e8417c000001"-- deviceId = string.gsub(deviceId, "-", "") deviceId = string.lower(deviceId) local totalDeviceIdLength = 24 deviceId = string.sub(deviceId, 1, totalDeviceIdLength) for i = 1, totalDeviceIdLength - #deviceId do deviceId = "1" .. deviceId end return deviceId end XSdk:init() LuaDebugjit;local debugger_reLoadFile =nil local debugger_xpcall = nil local sethook = debug.sethook local debugger_stackInfo = nil local coro_debugger = nil local require = rawget(_G,"require") local debugger_require = require local debugger_exeLuaString = nil local checkSetVar = nil local loadstring_ = nil local debugger_sendMsg = nil if (loadstring) then loadstring_ = loadstring else loadstring_ = load end local ZZBase64 = {} local LuaDebugTool_ = nil if (LuaDebugTool) then LuaDebugTool_ = LuaDebugTool elseif (CS and CS.LuaDebugTool) then LuaDebugTool_ = CS.LuaDebugTool end local LuaDebugTool = LuaDebugTool_ local loadstring = loadstring_ local getinfo = debug.getinfo local function createSocket() local base = _G local string = require("string") local math = require("math") local socket = require("socket.core") local _M = socket ----------------------------------------------------------------------------- -- Exported auxiliar functions ----------------------------------------------------------------------------- function _M.connect4(address, port, laddress, lport) return socket.connect(address, port, laddress, lport, "inet") end function _M.connect6(address, port, laddress, lport) return socket.connect(address, port, laddress, lport, "inet6") end if (not _M.connect) then function _M.connect(address, port, laddress, lport) local sock, err = socket.tcp() if not sock then return nil, err end if laddress then local res, err = sock:bind(laddress, lport, -1) if not res then return nil, err end end local res, err = sock:connect(address, port) if not res then return nil, err end return sock end end function _M.bind(host, port, backlog) if host == "*" then host = "0.0.0.0" end local addrinfo, err = socket.dns.getaddrinfo(host) if not addrinfo then return nil, err end local sock, res err = "no info on address" for i, alt in base.ipairs(addrinfo) do if alt.family == "inet" then sock, err = socket.tcp4() else sock, err = socket.tcp6() end if not sock then return nil, err end sock:setoption("reuseaddr", true) res, err = sock:bind(alt.addr, port) if not res then sock:close() else res, err = sock:listen(backlog) if not res then sock:close() else return sock end end end return nil, err end _M.try = _M.newtry() function _M.choose(table) return function(name, opt1, opt2) if base.type(name) ~= "string" then name, opt1, opt2 = "default", name, opt1 end local f = table[name or "nil"] if not f then base.error("unknown key (" .. base.tostring(name) .. ")", 3) else return f(opt1, opt2) end end end ----------------------------------------------------------------------------- -- Socket sources and sinks, conforming to LTN12 ----------------------------------------------------------------------------- -- create namespaces inside LuaSocket namespace local sourcet, sinkt = {}, {} _M.sourcet = sourcet _M.sinkt = sinkt _M.BLOCKSIZE = 2048 sinkt["close-when-done"] = function(sock) return base.setmetatable( { getfd = function() return sock:getfd() end, dirty = function() return sock:dirty() end }, { __call = function(self, chunk, err) if not chunk then sock:close() return 1 else return sock:send(chunk) end end } ) end sinkt["keep-open"] = function(sock) return base.setmetatable( { getfd = function() return sock:getfd() end, dirty = function() return sock:dirty() end }, { __call = function(self, chunk, err) if chunk then return sock:send(chunk) else return 1 end end } ) end sinkt["default"] = sinkt["keep-open"] _M.sink = _M.choose(sinkt) sourcet["by-length"] = function(sock, length) return base.setmetatable( { getfd = function() return sock:getfd() end, dirty = function() return sock:dirty() end }, { __call = function() if length <= 0 then return nil end local size = math.min(socket.BLOCKSIZE, length) local chunk, err = sock:receive(size) if err then return nil, err end length = length - string.len(chunk) return chunk end } ) end sourcet["until-closed"] = function(sock) local done return base.setmetatable( { getfd = function() return sock:getfd() end, dirty = function() return sock:dirty() end }, { __call = function() if done then return nil end local chunk, err, partial = sock:receive(socket.BLOCKSIZE) if not err then return chunk elseif err == "closed" then sock:close() done = 1 return partial else return nil, err end end } ) end sourcet["default"] = sourcet["until-closed"] _M.source = _M.choose(sourcet) return _M end local function createJson() local math = require("math") local string = require("string") local table = require("table") local object = nil ----------------------------------------------------------------------------- -- Module declaration ----------------------------------------------------------------------------- local json = {} -- Public namespace local json_private = {} -- Private namespace -- Public constants json.EMPTY_ARRAY = {} json.EMPTY_OBJECT = {} -- Public functions -- Private functions local decode_scanArray local decode_scanComment local decode_scanConstant local decode_scanNumber local decode_scanObject local decode_scanString local decode_scanWhitespace local encodeString local isArray local isEncodable ----------------------------------------------------------------------------- -- PUBLIC FUNCTIONS ----------------------------------------------------------------------------- --- Encodes an arbitrary Lua object / variable. -- @param v The Lua object / variable to be JSON encoded. -- @return String containing the JSON encoding in internal Lua string format (i.e. not unicode) function json.encode(v) -- Handle nil values if v == nil then return "null" end local vtype = type(v) -- Handle strings if vtype == "string" then return '"' .. json_private.encodeString(v) .. '"' -- Need to handle encoding in string end -- Handle booleans if vtype == "number" or vtype == "boolean" then return tostring(v) end -- Handle tables if vtype == "table" then local rval = {} -- Consider arrays separately local bArray, maxCount = isArray(v) if bArray then for i = 1, maxCount do table.insert(rval, json.encode(v[i])) end else -- An object, not an array for i, j in pairs(v) do if isEncodable(i) and isEncodable(j) then table.insert(rval, '"' .. json_private.encodeString(i) .. '":' .. json.encode(j)) end end end if bArray then return "[" .. table.concat(rval, ",") .. "]" else return "{" .. table.concat(rval, ",") .. "}" end end -- Handle null values if vtype == "function" and v == json.null then return "null" end assert(false, "encode attempt to encode unsupported type " .. vtype .. ":" .. tostring(v)) end --- Decodes a JSON string and returns the decoded value as a Lua data structure / value. -- @param s The string to scan. -- @param [startPos] Optional starting position where the JSON string is located. Defaults to 1. -- @param Lua object, number The object that was scanned, as a Lua table / string / number / boolean or nil, -- and the position of the first character after -- the scanned JSON object. function json.decode(s, startPos) startPos = startPos and startPos or 1 startPos = decode_scanWhitespace(s, startPos) assert(startPos <= string.len(s), "Unterminated JSON encoded object found at position in [" .. s .. "]") local curChar = string.sub(s, startPos, startPos) -- Object if curChar == "{" then return decode_scanObject(s, startPos) end -- Array if curChar == "[" then return decode_scanArray(s, startPos) end -- Number if string.find("+-0123456789.e", curChar, 1, true) then return decode_scanNumber(s, startPos) end -- String if curChar == '"' or curChar == [[']] then return decode_scanString(s, startPos) end if string.sub(s, startPos, startPos + 1) == "/*" then return json.decode(s, decode_scanComment(s, startPos)) end -- Otherwise, it must be a constant return decode_scanConstant(s, startPos) end --- The null function allows one to specify a null value in an associative array (which is otherwise -- discarded if you set the value with 'nil' in Lua. Simply set t = { first=json.null } function json.null() return json.null -- so json.null() will also return null ;-) end ----------------------------------------------------------------------------- -- Internal, PRIVATE functions. -- Following a Python-like convention, I have prefixed all these 'PRIVATE' -- functions with an underscore. ----------------------------------------------------------------------------- --- Scans an array from JSON into a Lua object -- startPos begins at the start of the array. -- Returns the array and the next starting position -- @param s The string being scanned. -- @param startPos The starting position for the scan. -- @return table, int The scanned array as a table, and the position of the next character to scan. function decode_scanArray(s, startPos) local array = {} -- The return value local stringLen = string.len(s) assert( string.sub(s, startPos, startPos) == "[", "decode_scanArray called but array does not start at position " .. startPos .. " in string:\n" .. s ) startPos = startPos + 1 -- Infinite loop for array elements repeat startPos = decode_scanWhitespace(s, startPos) assert(startPos <= stringLen, "JSON String ended unexpectedly scanning array.") local curChar = string.sub(s, startPos, startPos) if (curChar == "]") then return array, startPos + 1 end if (curChar == ",") then startPos = decode_scanWhitespace(s, startPos + 1) end assert(startPos <= stringLen, "JSON String ended unexpectedly scanning array.") object, startPos = json.decode(s, startPos) table.insert(array, object) until false end --- Scans a comment and discards the comment. -- Returns the position of the next character following the comment. -- @param string s The JSON string to scan. -- @param int startPos The starting position of the comment function decode_scanComment(s, startPos) assert( string.sub(s, startPos, startPos + 1) == "/*", "decode_scanComment called but comment does not start at position " .. startPos ) local endPos = string.find(s, "*/", startPos + 2) assert(endPos ~= nil, "Unterminated comment in string at " .. startPos) return endPos + 2 end --- Scans for given constants: true, false or null -- Returns the appropriate Lua type, and the position of the next character to read. -- @param s The string being scanned. -- @param startPos The position in the string at which to start scanning. -- @return object, int The object (true, false or nil) and the position at which the next character should be -- scanned. function decode_scanConstant(s, startPos) local consts = {["true"] = true, ["false"] = false, ["null"] = nil} local constNames = {"true", "false", "null"} for i, k in pairs(constNames) do if string.sub(s, startPos, startPos + string.len(k) - 1) == k then return consts[k], startPos + string.len(k) end end assert(nil, "Failed to scan constant from string " .. s .. " at starting position " .. startPos) end --- Scans a number from the JSON encoded string. -- (in fact, also is able to scan numeric +- eqns, which is not -- in the JSON spec.) -- Returns the number, and the position of the next character -- after the number. -- @param s The string being scanned. -- @param startPos The position at which to start scanning. -- @return number, int The extracted number and the position of the next character to scan. function decode_scanNumber(s, startPos) local endPos = startPos + 1 local stringLen = string.len(s) local acceptableChars = "+-0123456789.e" while (string.find(acceptableChars, string.sub(s, endPos, endPos), 1, true) and endPos <= stringLen) do endPos = endPos + 1 end local stringValue = "return " .. string.sub(s, startPos, endPos - 1) local stringEval = loadstring(stringValue) assert( stringEval, "Failed to scan number [ " .. stringValue .. "] in JSON string at position " .. startPos .. " : " .. endPos ) return stringEval(), endPos end --- Scans a JSON object into a Lua object. -- startPos begins at the start of the object. -- Returns the object and the next starting position. -- @param s The string being scanned. -- @param startPos The starting position of the scan. -- @return table, int The scanned object as a table and the position of the next character to scan. function decode_scanObject(s, startPos) local object = {} local stringLen = string.len(s) local key, value assert( string.sub(s, startPos, startPos) == "{", "decode_scanObject called but object does not start at position " .. startPos .. " in string:\n" .. s ) startPos = startPos + 1 repeat startPos = decode_scanWhitespace(s, startPos) assert(startPos <= stringLen, "JSON string ended unexpectedly while scanning object.") local curChar = string.sub(s, startPos, startPos) if (curChar == "}") then return object, startPos + 1 end if (curChar == ",") then startPos = decode_scanWhitespace(s, startPos + 1) end assert(startPos <= stringLen, "JSON string ended unexpectedly scanning object.") -- Scan the key key, startPos = json.decode(s, startPos) assert(startPos <= stringLen, "JSON string ended unexpectedly searching for value of key " .. key) startPos = decode_scanWhitespace(s, startPos) assert(startPos <= stringLen, "JSON string ended unexpectedly searching for value of key " .. key) assert( string.sub(s, startPos, startPos) == ":", "JSON object key-value assignment mal-formed at " .. startPos ) startPos = decode_scanWhitespace(s, startPos + 1) assert(startPos <= stringLen, "JSON string ended unexpectedly searching for value of key " .. key) value, startPos = json.decode(s, startPos) object[key] = value until false -- infinite loop while key-value pairs are found end -- START SoniEx2 -- Initialize some things used by decode_scanString -- You know, for efficiency local escapeSequences = { ["\\t"] = "\t", ["\\f"] = "\f", ["\\r"] = "\r", ["\\n"] = "\n", ["\\b"] = "" } setmetatable( escapeSequences, { __index = function(t, k) -- skip "\" aka strip escape return string.sub(k, 2) end } ) -- END SoniEx2 --- Scans a JSON string from the opening inverted comma or single quote to the -- end of the string. -- Returns the string extracted as a Lua string, -- and the position of the next non-string character -- (after the closing inverted comma or single quote). -- @param s The string being scanned. -- @param startPos The starting position of the scan. -- @return string, int The extracted string as a Lua string, and the next character to parse. function decode_scanString(s, startPos) assert(startPos, "decode_scanString(..) called without start position") local startChar = string.sub(s, startPos, startPos) -- START SoniEx2 -- PS: I don't think single quotes are valid JSON assert(startChar == '"' or startChar == [[']], "decode_scanString called for a non-string") --assert(startPos, "String decoding failed: missing closing " .. startChar .. " for string at position " .. oldStart) local t = {} local i, j = startPos, startPos while string.find(s, startChar, j + 1) ~= j + 1 do local oldj = j i, j = string.find(s, "\\.", j + 1) local x, y = string.find(s, startChar, oldj + 1) if not i or x < i then i, j = x, y - 1 end table.insert(t, string.sub(s, oldj + 1, i - 1)) if string.sub(s, i, j) == "\\u" then local a = string.sub(s, j + 1, j + 4) j = j + 4 local n = tonumber(a, 16) assert(n, "String decoding failed: bad Unicode escape " .. a .. " at position " .. i .. " : " .. j) -- math.floor(x/2^y) == lazy right shift -- a % 2^b == bitwise_and(a, (2^b)-1) -- 64 = 2^6 -- 4096 = 2^12 (or 2^6 * 2^6) local x if n < 128 then x = string.char(n % 128) elseif n < 2048 then -- [110x xxxx] [10xx xxxx] x = string.char(192 + (math.floor(n / 64) % 32), 128 + (n % 64)) else -- [1110 xxxx] [10xx xxxx] [10xx xxxx] x = string.char(224 + (math.floor(n / 4096) % 16), 128 + (math.floor(n / 64) % 64), 128 + (n % 64)) end table.insert(t, x) else table.insert(t, escapeSequences[string.sub(s, i, j)]) end end table.insert(t, string.sub(j, j + 1)) assert( string.find(s, startChar, j + 1), "String decoding failed: missing closing " .. startChar .. " at position " .. j .. "(for string at position " .. startPos .. ")" ) return table.concat(t, ""), j + 2 -- END SoniEx2 end --- Scans a JSON string skipping all whitespace from the current start position. -- Returns the position of the first non-whitespace character, or nil if the whole end of string is reached. -- @param s The string being scanned -- @param startPos The starting position where we should begin removing whitespace. -- @return int The first position where non-whitespace was encountered, or string.len(s)+1 if the end of string -- was reached. function decode_scanWhitespace(s, startPos) local whitespace = " \n\r\t" local stringLen = string.len(s) while (string.find(whitespace, string.sub(s, startPos, startPos), 1, true) and startPos <= stringLen) do startPos = startPos + 1 end return startPos end --- Encodes a string to be JSON-compatible. -- This just involves back-quoting inverted commas, back-quotes and newlines, I think ;-) -- @param s The string to return as a JSON encoded (i.e. backquoted string) -- @return The string appropriately escaped. local escapeList = { ['"'] = '\\"', ["\\"] = "\\\\", ["/"] = "\\/", [""] = "\\b", ["\f"] = "\\f", ["\n"] = "\\n", ["\r"] = "\\r", ["\t"] = "\\t" } function json_private.encodeString(s) local s = tostring(s) return s:gsub( ".", function(c) return escapeList[c] end ) -- SoniEx2: 5.0 compat end -- Determines whether the given Lua type is an array or a table / dictionary. -- We consider any table an array if it has indexes 1..n for its n items, and no -- other data in the table. -- I think this method is currently a little 'flaky', but can't think of a good way around it yet... -- @param t The table to evaluate as an array -- @return boolean, number True if the table can be represented as an array, false otherwise. If true, -- the second returned value is the maximum -- number of indexed elements in the array. function isArray(t) -- Next we count all the elements, ensuring that any non-indexed elements are not-encodable -- (with the possible exception of 'n') if (t == json.EMPTY_ARRAY) then return true, 0 end if (t == json.EMPTY_OBJECT) then return false end local maxIndex = 0 for k, v in pairs(t) do if (type(k) == "number" and math.floor(k) == k and 1 <= k) then -- k,v is an indexed pair if (not isEncodable(v)) then return false end -- All array elements must be encodable maxIndex = math.max(maxIndex, k) else if (k == "n") then if v ~= (t.n or #t) then return false end -- False if n does not hold the number of elements else -- Else of (k=='n') if isEncodable(v) then return false end end -- End of (k~='n') end -- End of k,v not an indexed pair end -- End of loop across all pairs return true, maxIndex end --- Determines whether the given Lua object / table / variable can be JSON encoded. The only -- types that are JSON encodable are: string, boolean, number, nil, table and json.null. -- In this implementation, all other types are ignored. -- @param o The object to examine. -- @return boolean True if the object should be JSON encoded, false if it should be ignored. function isEncodable(o) local t = type(o) return (t == "string" or t == "boolean" or t == "number" or t == "nil" or t == "table") or (t == "function" and o == json.null) end return json end local debugger_print = print local debug_server = nil local breakInfoSocket = nil local json = createJson() local LuaDebugger = { fileMaps = {}, Run = true, --表示正常运行只检测断点 StepIn = false, StepNext = false, StepOut = false, breakInfos = {}, runTimeType = nil, isHook = true, pathCachePaths = {}, isProntToConsole = 1, isDebugPrint = true, hookType = "lrc", stepNextFun = nil, DebugLuaFie = "", runLineCount = 0, --分割字符串缓存 splitFilePaths = {}, version="0.9.3", serVarLevel = 4 } local debug_hook = nil local _resume = coroutine.resume coroutine.resume = function(co, ...) if (LuaDebugger.isHook) then if coroutine.status(co) ~= "dead" then debug.sethook(co, debug_hook, "lrc") end end return _resume(co, ...) end local _wrap = coroutine.wrap coroutine.wrap = function(fun,dd) local newFun =_wrap(function() debug.sethook(debug_hook, "lrc") return fun(); end) return newFun end LuaDebugger.event = { S2C_SetBreakPoints = 1, C2S_SetBreakPoints = 2, S2C_RUN = 3, C2S_HITBreakPoint = 4, S2C_ReqVar = 5, C2S_ReqVar = 6, --单步跳过请求 S2C_NextRequest = 7, --单步跳过反馈 C2S_NextResponse = 8, -- 单步跳过 结束 没有下一步 C2S_NextResponseOver = 9, --单步跳入 S2C_StepInRequest = 10, C2S_StepInResponse = 11, --单步跳出 S2C_StepOutRequest = 12, --单步跳出返回 C2S_StepOutResponse = 13, --打印 C2S_LuaPrint = 14, S2C_LoadLuaScript = 16, C2S_SetSocketName = 17, C2S_LoadLuaScript = 18, C2S_DebugXpCall = 20, S2C_DebugClose = 21, S2C_SerVar = 24, C2S_SerVar = 25, S2C_ReLoadFile = 26, C2S_ReLoadFile = 27, } --@region print function print(...) if (LuaDebugger.isProntToConsole == 1 or LuaDebugger.isProntToConsole == 3) then debugger_print(...) end if (LuaDebugger.isProntToConsole == 1 or LuaDebugger.isProntToConsole == 2) then if (debug_server) then local arg = {...} --这里的...和{}符号中间需要有空格号,否则会出错 local str = "" if (#arg == 0) then arg = {"nil"} end for k, v in pairs(arg) do str = str .. tostring(v) .. "\t" end local sendMsg = { event = LuaDebugger.event.C2S_LuaPrint, data = {msg = ZZBase64.encode(str), type = 1} } local sendStr = json.encode(sendMsg) debug_server:send(sendStr .. "__debugger_k0204__") end end end function luaIdePrintWarn(...) if (LuaDebugger.isProntToConsole == 1 or LuaDebugger.isProntToConsole == 3) then debugger_print(...) end if (LuaDebugger.isProntToConsole == 1 or LuaDebugger.isProntToConsole == 2) then if (debug_server) then local arg = {...} --这里的...和{}符号中间需要有空格号,否则会出错 local str = "" if (#arg == 0) then arg = {"nil"} end for k, v in pairs(arg) do str = str .. tostring(v) .. "\t" end local sendMsg = { event = LuaDebugger.event.C2S_LuaPrint, data = {msg = ZZBase64.encode(str), type = 2} } local sendStr = json.encode(sendMsg) debug_server:send(sendStr .. "__debugger_k0204__") end end end function luaIdePrintErr(...) if (LuaDebugger.isProntToConsole == 1 or LuaDebugger.isProntToConsole == 3) then debugger_print(...) end if (LuaDebugger.isProntToConsole == 1 or LuaDebugger.isProntToConsole == 2) then if (debug_server) then local arg = {...} --这里的...和{}符号中间需要有空格号,否则会出错 local str = "" if (#arg == 0) then arg = {"nil"} end for k, v in pairs(arg) do str = str .. tostring(v) .. "\t" end local sendMsg = { event = LuaDebugger.event.C2S_LuaPrint, data = {msg = ZZBase64.encode(str), type = 3} } local sendStr = json.encode(sendMsg) debug_server:send(sendStr .. "__debugger_k0204__") end end end --@endregion --@region 辅助方法 local function debugger_lastIndex(str, p) local startIndex = string.find(str, p, 1) while startIndex do local findstartIndex = string.find(str, p, startIndex + 1) if (not findstartIndex) then break else startIndex = findstartIndex end end return startIndex end local function debugger_convertParentDir(dir) local index, endindex = string.find(dir, "/%.%./") if (index) then local file1 = string.sub(dir, 1, index - 1) local startIndex = debugger_lastIndex(file1, "/") file1 = string.sub(file1, 1, startIndex - 1) local file2 = string.sub(dir, endindex) dir = file1 .. file2 dir = debugger_convertParentDir(dir) return dir else return dir end end local function debugger_getFilePathInfo(file) local fileName = nil local dir = nil file = file:gsub("/.\\", "/") file = file:gsub("\\", "/") file = file:gsub("//", "/") if file:find("@") == 1 then file = file:sub(2) end local findex = file:find("%./") if (findex == 1) then file = file:sub(3) end file = debugger_convertParentDir(file) local fileLength = string.len(file) local suffixNames = { ".lua", ".lua.txt", ".txt", ".bytes" } table.sort( suffixNames, function(name1, name2) return string.len(name1) > string.len(name2) end ) local suffixLengs = {} for i, suffixName in ipairs(suffixNames) do table.insert(suffixLengs, string.len(suffixName)) end local fileLength = string.len(file) for i, suffix in ipairs(suffixNames) do local suffixName = string.sub(file, fileLength - suffixLengs[i] + 1) if (suffixName == suffix) then file = string.sub(file, 1, fileLength - suffixLengs[i]) break end end local fileNameStartIndex = debugger_lastIndex(file, "/") if (fileNameStartIndex) then fileName = string.sub(file, fileNameStartIndex + 1) dir = string.sub(file, 1, fileNameStartIndex) file = dir .. fileName else fileNameStartIndex = debugger_lastIndex(file, "%.") if (not fileNameStartIndex) then fileName = file dir = "" else dir = string.sub(file, 1, fileNameStartIndex) dir = dir:gsub("%.", "/") fileName = string.sub(file, fileNameStartIndex + 1) file = dir .. fileName end end return file, dir, fileName end --@endregion ----=============================工具方法============================================= --@region 工具方法 local function debugger_strSplit(input, delimiter) input = tostring(input) delimiter = tostring(delimiter) if (delimiter == "") then return false end local pos, arr = 0, {} -- for each divider found for st, sp in function() return string.find(input, delimiter, pos, true) end do table.insert(arr, string.sub(input, pos, st - 1)) pos = sp + 1 end table.insert(arr, string.sub(input, pos)) return arr end local function debugger_strTrim(input) input = string.gsub(input, "^[ \t\n\r]+", "") return string.gsub(input, "[ \t\n\r]+$", "") end local function debugger_dump(value, desciption, nesting) if type(nesting) ~= "number" then nesting = 3 end local lookupTable = {} local result = {} local function _v(v) if type(v) == "string" then v = '"' .. v .. '"' end return tostring(v) end local traceback = debugger_strSplit(debug.traceback("", 2), "\n") print("dump from: " .. debugger_strTrim(traceback[3])) local function _dump(value, desciption, indent, nest, keylen) desciption = desciption or "" local spc = "" if type(keylen) == "number" then spc = string.rep(" ", keylen - string.len(_v(desciption))) end if type(value) ~= "table" then result[#result + 1] = string.format("%s%s%s = %s", indent, _v(desciption), spc, _v(value)) elseif lookupTable[value] then result[#result + 1] = string.format("%s%s%s = *REF*", indent, desciption, spc) else lookupTable[value] = true if nest > nesting then result[#result + 1] = string.format("%s%s = *MAX NESTING*", indent, desciption) else result[#result + 1] = string.format("%s%s = {", indent, _v(desciption)) local indent2 = indent .. " " local keys = {} local keylen = 0 local values = {} for k, v in pairs(value) do keys[#keys + 1] = k local vk = _v(k) local vkl = string.len(vk) if vkl > keylen then keylen = vkl end values[k] = v end table.sort( keys, function(a, b) if type(a) == "number" and type(b) == "number" then return a < b else return tostring(a) < tostring(b) end end ) for i, k in ipairs(keys) do _dump(values[k], k, indent2, nest + 1, keylen) end result[#result + 1] = string.format("%s}", indent) end end end _dump(value, desciption, "- ", 1) for i, line in ipairs(result) do print(line) end end --@endregion local function debugger_valueToString(v) local vtype = type(v) local vstr = nil if (vtype == "userdata") then if (LuaDebugger.isFoxGloryProject ) then return "userdata",vtype else return tostring(v), vtype end elseif (vtype == "table" or vtype == "function" or vtype == "boolean") then local value = vtype xpcall(function() if(LuaDebugger.isFoxGloryProject) then value = vtype else value = tostring(v) end end,function() value = vtype end) return value, vtype elseif (vtype == "number" or vtype == "string" ) then return v, vtype else return tostring(v), vtype end end local function debugger_setVarInfo(name, value) local valueStr, valueType = debugger_valueToString(value) local nameStr,nameType = debugger_valueToString(name) if(valueStr == nil) then valueStr = valueType end local valueInfo = { name =nameStr, valueType = valueType, valueStr = ZZBase64.encode(valueStr) } return valueInfo end local function debugger_getvalue(f) local i = 1 local locals = {} -- get locals while true do local name, value = debug.getlocal(f, i) if not name then break end if (name ~= "(*temporary)") then locals[name] = value end i = i + 1 end local func = getinfo(f, "f").func i = 1 local ups = {} while func do -- check for func as it may be nil for tail calls local name, value = debug.getupvalue(func, i) if not name then break end if (name == "_ENV") then ups["_ENV_"] = value else ups[name] = value end i = i + 1 end return {locals = locals, ups = ups} end --获取堆栈 debugger_stackInfo = function(ignoreCount, event) local datas = {} local stack = {} local varInfos = {} local funcs = {} local index = 0 for i = ignoreCount, 100 do local source = getinfo(i) local isadd = true if (i == ignoreCount) then local file = source.source if (file:find(LuaDebugger.DebugLuaFie)) then return end if (file == "=[C]") then isadd = false end end if not source then break end if (isadd) then local fullName, dir, fileName = debugger_getFilePathInfo(source.source) local info = { src = fullName, scoreName = source.name, currentline = source.currentline, linedefined = source.linedefined, what = source.what, nameWhat = source.namewhat } index = i local vars = debugger_getvalue(i + 1) table.insert(stack, info) table.insert(varInfos, vars) table.insert(funcs, source.func) end if source.what == "main" then break end end local stackInfo = {stack = stack, vars = varInfos, funcs = funcs} local data = { stack = stackInfo.stack, vars = stackInfo.vars, funcs = stackInfo.funcs, event = event, funcsLength = #stackInfo.funcs, upFunc = getinfo(ignoreCount - 3, "f").func } return data end --==============================工具方法 end====================================================== --===========================点断信息================================================== --根据不同的游戏引擎进行定时获取断点信息 --CCDirector:sharedDirector():getScheduler() local debugger_setBreak = nil local function debugger_receiveDebugBreakInfo() if(not jit) then if(_VERSION)then print("当前lua版本为: ".._VERSION.." 请使用 -----LuaDebug.lua----- 进行调试!") else print("当前为lua版本,请使用-----LuaDebug.lua-----进行调试!") end end if (breakInfoSocket) then local msg, status = breakInfoSocket:receive() if(LuaDebugger.isLaunch == true and status == "closed") then os.exit() end if (msg) then local netData = json.decode(msg) if netData.event == LuaDebugger.event.S2C_SetBreakPoints then debugger_setBreak(netData.data) elseif netData.event == LuaDebugger.event.S2C_LoadLuaScript then LuaDebugger.loadScriptBody = netData.data debugger_exeLuaString() debugger_sendMsg(breakInfoSocket,LuaDebugger.event.C2S_LoadLuaScript,LuaDebugger.loadScriptBody) elseif netData.event == LuaDebugger.event.S2C_ReLoadFile then LuaDebugger.reLoadFileBody = netData.data LuaDebugger.isReLoadFile = false LuaDebugger.reLoadFileBody.isReLoad = debugger_reLoadFile(LuaDebugger.reLoadFileBody) LuaDebugger.reLoadFileBody.script = nil debugger_sendMsg( breakInfoSocket, LuaDebugger.event.C2S_ReLoadFile, { stack = LuaDebugger.reLoadFileBody } ) end end end end local function splitFilePath(path) if (LuaDebugger.splitFilePaths[path]) then return LuaDebugger.splitFilePaths[path] end local pos, arr = 0, {} -- for each divider found for st, sp in function() return string.find(path, "/", pos, true) end do local pathStr = string.sub(path, pos, st - 1) table.insert(arr, pathStr) pos = sp + 1 end local pathStr = string.sub(path, pos) table.insert(arr, pathStr) LuaDebugger.splitFilePaths[path] = arr return arr end debugger_setBreak = function(datas) local breakInfos = LuaDebugger.breakInfos for i, data in ipairs(datas) do data.fileName = string.lower(data.fileName) data.serverPath = string.lower(data.serverPath) local breakInfo = breakInfos[data.fileName] if (not breakInfo) then breakInfos[data.fileName] = {} breakInfo = breakInfos[data.fileName] end if (not data.breakDatas or #data.breakDatas == 0) then breakInfo[data.serverPath] = nil else local fileBreakInfo = breakInfo[data.serverPath] if (not fileBreakInfo) then fileBreakInfo = { pathNames = splitFilePath(data.serverPath), --命中次數判斷計數器 hitCounts = {} } breakInfo[data.serverPath] = fileBreakInfo end local lineInfos = {} for li, breakData in ipairs(data.breakDatas) do lineInfos[breakData.line] = breakData if (breakData.hitCondition and breakData.hitCondition ~= "") then breakData.hitCondition = tonumber(breakData.hitCondition) else breakData.hitCondition = 0 end if (not fileBreakInfo.hitCounts[breakData.line]) then fileBreakInfo.hitCounts[breakData.line] = 0 end end fileBreakInfo.lines = lineInfos --這裡添加命中次數判斷 for line, count in pairs(fileBreakInfo.hitCounts) do if (not lineInfos[line]) then fileBreakInfo.hitCounts[line] = nil end end end local count = 0 for i, linesInfo in pairs(breakInfo) do count = count + 1 end if (count == 0) then breakInfos[data.fileName] = nil end end --debugger_dump(breakInfos, "breakInfos", 6) --检查是否需要断点 local isHook = false for k, v in pairs(breakInfos) do isHook = true break end --这样做的原因是为了最大限度的使手机调试更加流畅 注意这里会连续的进行n次 if (isHook) then if (not LuaDebugger.isHook) then debug.sethook(debug_hook, "lrc") end LuaDebugger.isHook = true else if (LuaDebugger.isHook) then debug.sethook() end LuaDebugger.isHook = false end end local function debugger_checkFileIsBreak(fileName) return LuaDebugger.breakInfos[fileName] end --=====================================断点信息 end ---------------------------------------------- local controller_host = "192.168.1.102" local controller_port = 7003 debugger_sendMsg = function(serverSocket, eventName, data) local sendMsg = { event = eventName, data = data } local sendStr = json.encode(sendMsg) serverSocket:send(sendStr .. "__debugger_k0204__") end function debugger_conditionStr(condition, vars, callBack) local function loadScript() local currentTabble = {} local locals = vars[1].locals local ups = vars[1].ups if (ups) then for k, v in pairs(ups) do currentTabble[k] = v end end if (locals) then for k, v in pairs(locals) do currentTabble[k] = v end end setmetatable(currentTabble, {__index = _G}) local fun = loadstring("return " .. condition) setfenv(fun, currentTabble) return fun() end local status, msg = xpcall( loadScript, function(error) print(error) end ) if (status and msg) then callBack() end end --执行lua字符串 debugger_exeLuaString = function() local function loadScript() local script = LuaDebugger.loadScriptBody.script if (LuaDebugger.loadScriptBody.isBreak) then local currentTabble = {_G = _G} local frameId = LuaDebugger.loadScriptBody.frameId frameId = frameId local func = LuaDebugger.currentDebuggerData.funcs[frameId] local vars = LuaDebugger.currentDebuggerData.vars[frameId] local locals = vars.locals local ups = vars.ups for k, v in pairs(ups) do currentTabble[k] = v end for k, v in pairs(locals) do currentTabble[k] = v end setmetatable(currentTabble, {__index = _G}) local fun = loadstring(script) setfenv(fun, currentTabble) fun() else local fun = loadstring(script) fun() end end local status, msg = xpcall( loadScript, function(error) -- debugger_sendMsg(debug_server, LuaDebugger.event.C2S_LoadLuaScript, LuaDebugger.loadScriptBody) end ) LuaDebugger.loadScriptBody.script = nil if (LuaDebugger.loadScriptBody.isBreak) then LuaDebugger.serVarLevel = LuaDebugger.serVarLevel+1 LuaDebugger.currentDebuggerData = debugger_stackInfo(LuaDebugger.serVarLevel, LuaDebugger.event.C2S_HITBreakPoint) LuaDebugger.loadScriptBody.stack = LuaDebugger.currentDebuggerData.stack end LuaDebugger.loadScriptBody.complete = true end --@region 调试中修改变量值 --根据key 值在 value 查找 local function debugger_getTablekey(key,keyType,value) if(keyType == -1) then return key elseif(keyType == 1) then return tonumber(key) elseif(keyType == 2) then local valueKey = nil for k,v in pairs(value) do local nameType = type(k) if(nameType == "userdata" or nameType == "table") then if (not LuaDebugger.isFoxGloryProject) then valueKey = tostring(k) if(key == valueKey) then return k end break end end end end end local function debugger_setVarValue(server, data) local newValue = nil local level = LuaDebugger.serVarLevel+LuaDebugger.setVarBody.frameId local firstKeyName = data.keys[1] --@region vars check local localValueChangeIndex = -1 local upValueChangeIndex = -1 local upValueFun = nil local oldValue = nil local i = 1 local locals = {} -- get locals while true do local name, value = debug.getlocal(level, i) if not name then break end if(firstKeyName == name) then localValueChangeIndex = i oldValue = value end if (name ~= "(*temporary)") then locals[name] = value end i = i + 1 end local func = getinfo(level, "f").func i = 1 local ups = {} while func do -- check for func as it may be nil for tail calls local name, value = debug.getupvalue(func, i) if not name then break end if(localValueChangeIndex == -1 and firstKeyName == name) then upValueFun = func oldValue = value upValueChangeIndex = i end if (name == "_ENV") then ups["_ENV_"] = value else ups[name] = value end i = i + 1 end --@endregion local vars = {locals = locals, ups = ups} local function loadScript() local currentTabble = {} local locals = vars.locals local ups = vars.ups if (ups) then for k, v in pairs(ups) do currentTabble[k] = v end end if (locals) then for k, v in pairs(locals) do currentTabble[k] = v end end setmetatable(currentTabble, {__index = _G}) local fun = loadstring("return " .. data.value) setfenv(fun, currentTabble) newValue = fun() end local status, msg = xpcall( loadScript, function(error) print(error, "============================") end ) local i = 1 -- local 查找并替换 local keyLength = #data.keys if(keyLength == 1) then if(localValueChangeIndex ~= -1) then debug.setlocal(level, localValueChangeIndex, newValue) elseif(upValueFun ~= nil) then debug.setupvalue( upValueFun, upValueChangeIndex, newValue ) else --全局变量查找 if(_G[firstKeyName]) then _G[firstKeyName] = newValue end end else if(not oldValue) then if(_G[firstKeyName]) then oldValue = _G[firstKeyName] end end local tempValue = oldValue for i=2,keyLength-1 do if(tempValue) then oldValue = oldValue[debugger_getTablekey(data.keys[i],data.numberTypes[i],oldValue)] end end if(tempValue) then oldValue[debugger_getTablekey(data.keys[keyLength],data.numberTypes[keyLength],oldValue)] = newValue end end local varInfo = debugger_setVarInfo(data.varName, newValue) data.varInfo = varInfo LuaDebugger.serVarLevel = LuaDebugger.serVarLevel+1 LuaDebugger.currentDebuggerData = debugger_stackInfo(LuaDebugger.serVarLevel, LuaDebugger.event.C2S_HITBreakPoint) end --@endregion --调试修改变量值统一的 _resume checkSetVar = function() if (LuaDebugger.isSetVar) then LuaDebugger.isSetVar = false debugger_setVarValue(debug_server,LuaDebugger.setVarBody) LuaDebugger.serVarLevel = LuaDebugger.serVarLevel+1 _resume(coro_debugger, LuaDebugger.setVarBody) xpcall( checkSetVar, function(error) print("设置变量", error) end ) elseif(LuaDebugger.isLoadLuaScript) then LuaDebugger.isLoadLuaScript = false debugger_exeLuaString() LuaDebugger.serVarLevel = LuaDebugger.serVarLevel+1 _resume(coro_debugger, LuaDebugger.reLoadFileBody) xpcall( checkSetVar, function(error) print("执行代码", error) end ) elseif(LuaDebugger.isReLoadFile) then LuaDebugger.isReLoadFile = false LuaDebugger.reLoadFileBody.isReLoad = debugger_reLoadFile(LuaDebugger.reLoadFileBody) print("重载结果:",LuaDebugger.reLoadFileBody.isReLoad) LuaDebugger.reLoadFileBody.script = nil LuaDebugger.serVarLevel = LuaDebugger.serVarLevel+1 _resume(coro_debugger, LuaDebugger.reLoadFileBody) xpcall( checkSetVar, function(error) print("重新加载文件", error) end ) end end local function getSource(source) source = string.lower(source) if (LuaDebugger.pathCachePaths[source]) then LuaDebugger.currentLineFile = LuaDebugger.pathCachePaths[source] return LuaDebugger.pathCachePaths[source] end local fullName, dir, fileName = debugger_getFilePathInfo(source) LuaDebugger.currentLineFile = fullName LuaDebugger.pathCachePaths[source] = fileName return fileName end local function debugger_GeVarInfoBytUserData(server, var) local fileds = LuaDebugTool.getUserDataInfo(var) local varInfos = {} --c# vars for i = 1, fileds.Count do local filed = fileds[i - 1] local valueInfo = { name = filed.name, valueType = filed.valueType, valueStr = ZZBase64.encode(filed.valueStr), isValue = filed.isValue, csharp = true } table.insert(varInfos, valueInfo) end return varInfos end local function debugger_getValueByScript(value, script) local val = nil local status, msg = xpcall( function() local fun = loadstring("return " .. script) setfenv(fun, value) val = fun() end, function(error) print(error, "====>") val = nil end ) return val end local function debugger_getVarByKeys(value, keys, index) local str = "" local keyLength = #keys for i = index, keyLength do local key = keys[i] if (key == "[metatable]") then else if (i == index) then if (string.find(key, "%.")) then if (str == "") then i = index + 1 value = value[key] end if (i >= #keys) then return index, value end return debugger_getVarByKeys(value, keys, i) else str = key end else if (string.find(key, "%[")) then str = str .. key elseif (type(key) == "string") then if (string.find(key, "table:") or string.find(key, "userdata:") or string.find(key, "function:")) then if (str ~= "") then local vl = debugger_getValueByScript(value, str) value = vl if (value) then for k, v in pairs(value) do local ktype = type(k) if (ktype == "userdata" or ktype == "table" or ktype == "function") then local keyName = debugger_valueToString(k) if (keyName == key) then value = v break end end end end str = "" if (i == keyLength) then return #keys, value else return debugger_getVarByKeys(value, keys, i + 1) end else str = str .. '["' .. key .. '"]' end else str = str .. '["' .. key .. '"]' end else str = str .. "[" .. key .. "]" end end end end local v = debugger_getValueByScript(value, str) return #keys, v end --[[ @desc: 查找c# 值 author:k0204 time:2018-04-07 21:32:31 return ]] local function debugger_getCSharpValue(value, searchIndex, keys) local key = keys[searchIndex] local val = LuaDebugTool.getCSharpValue(value, key) if (val) then --1最后一个 直接返回 if (searchIndex == #keys) then return #keys, val else --2再次获得 如果没有找到那么 进行lua 层面查找 local vindex, val1 = debugger_getCSharpValue(val, searchIndex + 1, keys) if (not val1) then --组建新的keys local tempKeys = {} for i = vindex, #keys do table.insert(tempKeys, keys[i]) end local vindx, val1 = debugger_searchVarByKeys(value, searckKeys, 1) return vindx, val1 else return vindex, val1 end end else --3最终这里返回 所以2 中 没有当val1 不为空的处理 return searchIndex, val end end local function debugger_searchVarByKeys(value, keys, searckKeys) local index, val = debugger_getVarByKeys(value, searckKeys, 1) if (not LuaDebugTool or not LuaDebugTool.getCSharpValue or type(LuaDebugTool.getCSharpValue) ~= "function") then return index, val end if (val) then if (index == #keys) then return index, val else local searchStr = "" --进行c# 值查找 local keysLength = #keys local searchIndex = index + 1 local sindex, val = debugger_getCSharpValue(val, searchIndex, keys) return sindex, val end else --进行递减 local tempKeys = {} for i = 1, #searckKeys - 1 do table.insert(tempKeys, keys[i]) end if (#tempKeys == 0) then return #keys, nil end return debugger_searchVarByKeys(value, keys, tempKeys) end end --[[ @desc: 获取metatable 信息 author:k0204 time:2018-04-06 20:27:12 return ]] local function debugger_getmetatable(value, metatable, vinfos, server, variablesReference, debugSpeedIndex, metatables) for i, mtable in ipairs(metatables) do if (metatable == mtable) then return vinfos end end table.insert(metatables, metatable) for k, v in pairs(metatable) do local val = nil if (type(k) == "string") then xpcall( function() val = value[k] end, function(error) val = nil end ) if (val == nil) then xpcall( function() if (string.find(k, "__")) then val = v end end, function(error) val = nil end ) end end if (val) then local vinfo = debugger_setVarInfo(k, val) table.insert(vinfos, vinfo) if (#vinfos > 10) then debugger_sendMsg( server, LuaDebugger.event.C2S_ReqVar, { variablesReference = variablesReference, debugSpeedIndex = debugSpeedIndex, vars = vinfos, isComplete = 0 } ) vinfos = {} end end end local m = getmetatable(metatable) if (m) then return debugger_getmetatable(value, m, vinfos, server, variablesReference, debugSpeedIndex, metatables) else return vinfos end end local function debugger_sendTableField(luatable, vinfos, server, variablesReference, debugSpeedIndex, valueType) if (valueType == "userdata") then if (tolua and tolua.getpeer) then luatable = tolua.getpeer(luatable) else return vinfos end end if (luatable == nil) then return vinfos end for k, v in pairs(luatable) do local vinfo = debugger_setVarInfo(k, v) table.insert(vinfos, vinfo) if (#vinfos > 10) then debugger_sendMsg( server, LuaDebugger.event.C2S_ReqVar, { variablesReference = variablesReference, debugSpeedIndex = debugSpeedIndex, vars = vinfos, isComplete = 0 } ) vinfos = {} end end return vinfos end local function debugger_sendTableValues(value, server, variablesReference, debugSpeedIndex) local vinfos = {} local luatable = {} local valueType = type(value) local userDataInfos = {} local m = nil if (valueType == "userdata") then m = getmetatable(value) vinfos = debugger_sendTableField(value, vinfos, server, variablesReference, debugSpeedIndex, valueType) if (LuaDebugTool) then local varInfos = debugger_GeVarInfoBytUserData(server, value, variablesReference, debugSpeedIndex) for i, v in ipairs(varInfos) do if (v.valueType == "System.Byte[]" and value[v.name] and type(value[v.name]) == "string") then local valueInfo = { name = v.name, valueType = "string", valueStr = ZZBase64.encode(value[v.name]) } table.insert(vinfos, valueInfo) else table.insert(vinfos, v) end if (#vinfos > 10) then debugger_sendMsg( server, LuaDebugger.event.C2S_ReqVar, { variablesReference = variablesReference, debugSpeedIndex = debugSpeedIndex, vars = vinfos, isComplete = 0 } ) vinfos = {} end end end else m = getmetatable(value) vinfos = debugger_sendTableField(value, vinfos, server, variablesReference, debugSpeedIndex, valueType) end if (m) then vinfos = debugger_getmetatable(value, m, vinfos, server, variablesReference, debugSpeedIndex, {}) end debugger_sendMsg( server, LuaDebugger.event.C2S_ReqVar, { variablesReference = variablesReference, debugSpeedIndex = debugSpeedIndex, vars = vinfos, isComplete = 1 } ) end --获取lua 变量的方法 local function debugger_getBreakVar(body, server) local variablesReference = body.variablesReference local debugSpeedIndex = body.debugSpeedIndex local vinfos = {} local function exe() local frameId = body.frameId local type_ = body.type local keys = body.keys --找到对应的var local vars = nil if (type_ == 1) then vars = LuaDebugger.currentDebuggerData.vars[frameId + 1] vars = vars.locals elseif (type_ == 2) then vars = LuaDebugger.currentDebuggerData.vars[frameId + 1] vars = vars.ups elseif (type_ == 3) then vars = _G end if (#keys == 0) then debugger_sendTableValues(vars, server, variablesReference, debugSpeedIndex) return end local index, value = debugger_searchVarByKeys(vars, keys, keys) if (value) then local valueType = type(value) if (valueType == "table" or valueType == "userdata") then debugger_sendTableValues(value, server, variablesReference, debugSpeedIndex) else if (valueType == "function") then if(LuaDebugger.isFoxGloryProject) then value = "function" else value = tostring(value) end end debugger_sendMsg( server, LuaDebugger.event.C2S_ReqVar, { variablesReference = variablesReference, debugSpeedIndex = debugSpeedIndex, vars = ZZBase64.encode(value), isComplete = 1, varType = valueType } ) end else debugger_sendMsg( server, LuaDebugger.event.C2S_ReqVar, { variablesReference = variablesReference, debugSpeedIndex = debugSpeedIndex, vars = {}, isComplete = 1, varType = "nil" } ) end end xpcall( exe, function(error) -- print("获取变量错误 错误消息-----------------") -- print(error) -- print(debug.traceback("", 2)) debugger_sendMsg( server, LuaDebugger.event.C2S_ReqVar, { variablesReference = variablesReference, debugSpeedIndex = debugSpeedIndex, vars = { { name = "error", valueType = "string", valueStr = ZZBase64.encode("无法获取属性值:" .. error .. "->" .. debug.traceback("", 2)), isValue = false } }, isComplete = 1 } ) end ) end local function ResetDebugInfo() LuaDebugger.Run = false LuaDebugger.StepIn = false LuaDebugger.StepNext = false LuaDebugger.StepOut = false end local function debugger_loop(server) server = debug_server --命令 local command local eval_env = {} local arg while true do local line, status = server:receive() if (status == "closed") then if(LuaDebugger.isLaunch) then os.exit() else debug.sethook() coroutine.yield() end end if (line) then local netData = json.decode(line) local event = netData.event local body = netData.data if (event == LuaDebugger.event.S2C_DebugClose) then if(LuaDebugger.isLaunch) then os.exit() else debug.sethook() coroutine.yield() end elseif event == LuaDebugger.event.S2C_SetBreakPoints then --设置断点信息 local function setB() debugger_setBreak(body) end xpcall( setB, function(error) print(error) end ) elseif event == LuaDebugger.event.S2C_RUN then --开始运行 LuaDebugger.runTimeType = body.runTimeType LuaDebugger.isProntToConsole = body.isProntToConsole LuaDebugger.isFoxGloryProject = body.isFoxGloryProject LuaDebugger.isLaunch = body.isLaunch ResetDebugInfo() LuaDebugger.currentDebuggerData = nil LuaDebugger.Run = true LuaDebugger.tempRunFlag = true LuaDebugger.currentLine= nil local data = coroutine.yield() LuaDebugger.serVarLevel = 4 LuaDebugger.currentDebuggerData = data debugger_sendMsg( server, data.event, { stack = data.stack } ) elseif event == LuaDebugger.event.S2C_ReqVar then -- 获取变量信息 --请求数据信息 debugger_getBreakVar(body, server) elseif event == LuaDebugger.event.S2C_NextRequest then -- 设置单步跳过 ResetDebugInfo() LuaDebugger.StepNext = true --设置当前文件名和当前行数 local data = coroutine.yield() LuaDebugger.serVarLevel = 4 --重置调试信息 LuaDebugger.currentDebuggerData = data debugger_sendMsg( server, data.event, { stack = data.stack } ) elseif (event == LuaDebugger.event.S2C_StepInRequest) then --单步跳入 --单步跳入 ResetDebugInfo() LuaDebugger.StepIn = true local data = coroutine.yield() LuaDebugger.serVarLevel = 4 --重置调试信息 LuaDebugger.currentDebuggerData = data debugger_sendMsg( server, data.event, { stack = data.stack, eventType = data.eventType } ) elseif (event == LuaDebugger.event.S2C_StepOutRequest) then --单步跳出 ResetDebugInfo() LuaDebugger.StepOut = true local data = coroutine.yield() LuaDebugger.serVarLevel = 4 --重置调试信息 LuaDebugger.currentDebuggerData = data debugger_sendMsg( server, data.event, { stack = data.stack, eventType = data.eventType } ) elseif event == LuaDebugger.event.S2C_LoadLuaScript then LuaDebugger.loadScriptBody = body LuaDebugger.isLoadLuaScript = true local data = coroutine.yield() debugger_sendMsg( server, LuaDebugger.event.C2S_LoadLuaScript, LuaDebugger.loadScriptBody ) elseif event == LuaDebugger.event.S2C_SerVar then LuaDebugger.isSetVar = true LuaDebugger.setVarBody = body local data = coroutine.yield() debugger_sendMsg( server, LuaDebugger.event.C2S_SerVar, { stack = data, eventType = data.eventType } ) elseif event == LuaDebugger.event.S2C_ReLoadFile then LuaDebugger.isReLoadFile = true LuaDebugger.reLoadFileBody = body local data = coroutine.yield() debugger_sendMsg( server, LuaDebugger.event.C2S_ReLoadFile, { stack = data, eventType = data.eventType } ) end end end end coro_debugger = coroutine.create(debugger_loop) debug_hook = function(event, line) -- local stepInfo = getinfo(2) -- print(stepInfo.source,stepInfo.currentline) if(not LuaDebugger.isHook) then return end if(LuaDebugger.Run) then if(event == "line") then local isCheck = false for k, breakInfo in pairs(LuaDebugger.breakInfos) do for bk, linesInfo in pairs(breakInfo) do if(linesInfo.lines and linesInfo.lines[line]) then isCheck = true break end end if(isCheck) then break end end if(not isCheck) then return end end end local file = nil if(event == "line") then local funs = nil local funlength =0 if(LuaDebugger.currentDebuggerData) then funs = LuaDebugger.currentDebuggerData.funcs funlength = #funs end local stepInfo = getinfo(2) local tempFunc = stepInfo.func local source = stepInfo.source file = getSource(source); if(source == "=[C]" or source:find(LuaDebugger.DebugLuaFie)) then return end if(funlength > 0 and funs[1] == tempFunc and LuaDebugger.currentLine ~= line) then LuaDebugger.runLineCount = LuaDebugger.runLineCount+1 end local breakInfo = LuaDebugger.breakInfos[file] local breakData = nil local ischeck = false if(breakInfo) then for k, lineInfo in pairs(breakInfo) do local lines = lineInfo.lines if(lines and lines[line]) then ischeck = true break end end end local isHit = false if(ischeck) then --并且在断点中 local info = stepInfo local source = string.lower( info.source ) local fullName,dir,fileName = debugger_getFilePathInfo(source) local hitPathNames = splitFilePath(fullName) local hitCounts = {} local debugHitCounts = nil for k, lineInfo in pairs(breakInfo) do local lines = lineInfo.lines local pathNames = lineInfo.pathNames debugHitCounts = lineInfo.hitCounts if(lines and lines[line]) then breakData = lines[line] --判断路径 hitCounts[k] = 0 local hitPathNamesCount = #hitPathNames local pathNamesCount = #pathNames local checkCount = 0; while(true) do if (pathNames[pathNamesCount] ~= hitPathNames[hitPathNamesCount]) then break else hitCounts[k] = hitCounts[k] + 1 end pathNamesCount = pathNamesCount - 1 hitPathNamesCount = hitPathNamesCount - 1 checkCount = checkCount+1 if(pathNamesCount <= 0 or hitPathNamesCount <= 0) then break end end if(checkCount>0) then break; end if(checkCount==0) then breakData = nil -- break; end else breakData = nil end end if(breakData) then local hitFieName = "" local maxCount = 0 for k, v in pairs(hitCounts) do if(v > maxCount) then maxCount = v hitFieName = k; end end local hitPathNamesLength = #hitPathNames if (hitPathNamesLength == 1 or (hitPathNamesLength > 1 and maxCount > 1)) then if(hitFieName ~= "") then local hitCount = breakData.hitCondition local clientHitCount = debugHitCounts[breakData.line] clientHitCount = clientHitCount + 1 debugHitCounts[breakData.line] = clientHitCount if(funs and funs[1] == tempFunc and LuaDebugger.runLineCount == 0) then LuaDebugger.runLineCount = 0 elseif(LuaDebugger.tempRunFlag and LuaDebugger.currentLine == line) then LuaDebugger.runLineCount = 0 LuaDebugger.tempRunFlag = nil elseif(clientHitCount >= hitCount) then isHit = true end end end end end if(LuaDebugger.StepOut) then if(funlength == 1) then ResetDebugInfo(); LuaDebugger.Run = true return else if(funs[2] == tempFunc) then local data = debugger_stackInfo(3, LuaDebugger.event.C2S_StepInResponse) -- print("StepIn 挂起") --挂起等待调试器作出反应 _resume(coro_debugger, data) checkSetVar() return end end end if(LuaDebugger.StepIn) then if(funs[1] == tempFunc and LuaDebugger.runLineCount == 0) then return end local data = debugger_stackInfo(3, LuaDebugger.event.C2S_StepInResponse) -- print("StepIn 挂起") --挂起等待调试器作出反应 _resume(coro_debugger, data) checkSetVar() return end if(LuaDebugger.StepNext ) then local isNext = false if(funs) then for i,f in ipairs(funs) do if(tempFunc == f) then if(LuaDebugger.currentLine == line) then return end isNext =true break; end end else isNext =true end if(isNext) then local data = debugger_stackInfo(3, LuaDebugger.event.C2S_NextResponse) LuaDebugger.runLineCount = 0 LuaDebugger.currentLine = line --挂起等待调试器作出反应 _resume(coro_debugger, data) checkSetVar() return end end local sevent = nil --断点判断 if(isHit) then LuaDebugger.runLineCount = 0 LuaDebugger.currentLine = line sevent = LuaDebugger.event.C2S_HITBreakPoint --调用 coro_debugger 并传入 参数 local data = debugger_stackInfo(3, sevent) --挂起等待调试器作出反应 if(breakData and breakData.condition) then debugger_conditionStr(breakData.condition,data.vars,function() _resume(coro_debugger, data) checkSetVar() end) else --挂起等待调试器作出反应 _resume(coro_debugger, data) checkSetVar() end end end end debugger_xpcall = function() --调用 coro_debugger 并传入 参数 local data = debugger_stackInfo(4, LuaDebugger.event.C2S_HITBreakPoint) if(data.stack and data.stack[1]) then data.stack[1].isXpCall = true end --挂起等待调试器作出反应 _resume(coro_debugger, data) checkSetVar() end --调试开始 local function start() local socket = createSocket() print(controller_host) print(controller_port) local fullName,dirName,fileName = debugger_getFilePathInfo(getinfo(1).source) LuaDebugger.DebugLuaFie = fileName local server = socket.connect(controller_host, controller_port) debug_server = server; if server then --创建breakInfo socket socket = createSocket() breakInfoSocket = socket.connect(controller_host, controller_port) if(breakInfoSocket) then breakInfoSocket:settimeout(0) debugger_sendMsg(breakInfoSocket, LuaDebugger.event.C2S_SetSocketName, { name = "breakPointSocket" }) debugger_sendMsg(server, LuaDebugger.event.C2S_SetSocketName, { name = "mainSocket", version = LuaDebugger.version }) xpcall(function() sethook(debug_hook, "lrc") end, function(error) print("error:", error) end) if(not jit) then if(_VERSION)then print("当前lua版本为: ".._VERSION.." 请使用LuaDebug 进行调试!") else print("当前为lua版本,请使用LuaDebug 进行调试!") end end _resume(coro_debugger, server) end end end function StartDebug(host, port,isReLoad) if(not host) then print("error host nil") end if(not port) then print("error prot nil") end if(type(host) ~= "string") then print("error host not string") end if(type(port) ~= "number") then print("error host not number") end controller_host = host controller_port = port xpcall(start, function(error) -- body print(error) end) --代码重载 if(isReLoad) then xpcall(function() debugger_reLoadFile = require("luaideReLoadFile") end,function() print("左侧luaide按钮->打开luaIde最新调试文件所在文件夹->luaideReLoadFile.lua->拷贝到项目中") print("具体使用方式请看luaideReLoadFile中文件注释") debugger_reLoadFile = function() print("未实现代码重载") end end) end return debugger_receiveDebugBreakInfo, debugger_xpcall end --base64 local string = string ZZBase64.__code = { 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/', }; ZZBase64.__decode = {} for k,v in pairs(ZZBase64.__code) do ZZBase64.__decode[string.byte(v,1)] = k - 1 end function ZZBase64.encode(text) local len = string.len(text) local left = len % 3 len = len - left local res = {} local index = 1 for i = 1, len, 3 do local a = string.byte(text, i ) local b = string.byte(text, i + 1) local c = string.byte(text, i + 2) -- num = a<<16 + b<<8 + c local num = a * 65536 + b * 256 + c for j = 1, 4 do --tmp = num >> ((4 -j) * 6) local tmp = math.floor(num / (2 ^ ((4-j) * 6))) --curPos = tmp&0x3f local curPos = tmp % 64 + 1 res[index] = ZZBase64.__code[curPos] index = index + 1 end end if left == 1 then ZZBase64.__left1(res, index, text, len) elseif left == 2 then ZZBase64.__left2(res, index, text, len) end return table.concat(res) end function ZZBase64.__left2(res, index, text, len) local num1 = string.byte(text, len + 1) num1 = num1 * 1024 --lshift 10 local num2 = string.byte(text, len + 2) num2 = num2 * 4 --lshift 2 local num = num1 + num2 local tmp1 = math.floor(num / 4096) --rShift 12 local curPos = tmp1 % 64 + 1 res[index] = ZZBase64.__code[curPos] local tmp2 = math.floor(num / 64) curPos = tmp2 % 64 + 1 res[index + 1] = ZZBase64.__code[curPos] curPos = num % 64 + 1 res[index + 2] = ZZBase64.__code[curPos] res[index + 3] = "=" end function ZZBase64.__left1(res, index,text, len) local num = string.byte(text, len + 1) num = num * 16 local tmp = math.floor(num / 64) local curPos = tmp % 64 + 1 res[index ] = ZZBase64.__code[curPos] curPos = num % 64 + 1 res[index + 1] = ZZBase64.__code[curPos] res[index + 2] = "=" res[index + 3] = "=" end function ZZBase64.decode(text) local len = string.len(text) local left = 0 if string.sub(text, len - 1) == "==" then left = 2 len = len - 4 elseif string.sub(text, len) == "=" then left = 1 len = len - 4 end local res = {} local index = 1 local decode = ZZBase64.__decode for i =1, len, 4 do local a = decode[string.byte(text,i )] local b = decode[string.byte(text,i + 1)] local c = decode[string.byte(text,i + 2)] local d = decode[string.byte(text,i + 3)] --num = a<<18 + b<<12 + c<<6 + d local num = a * 262144 + b * 4096 + c * 64 + d local e = string.char(num % 256) num = math.floor(num / 256) local f = string.char(num % 256) num = math.floor(num / 256) res[index ] = string.char(num % 256) res[index + 1] = f res[index + 2] = e index = index + 3 end if left == 1 then ZZBase64.__decodeLeft1(res, index, text, len) elseif left == 2 then ZZBase64.__decodeLeft2(res, index, text, len) end return table.concat(res) end function ZZBase64.__decodeLeft1(res, index, text, len) local decode = ZZBase64.__decode local a = decode[string.byte(text, len + 1)] local b = decode[string.byte(text, len + 2)] local c = decode[string.byte(text, len + 3)] local num = a * 4096 + b * 64 + c local num1 = math.floor(num / 1024) % 256 local num2 = math.floor(num / 4) % 256 res[index] = string.char(num1) res[index + 1] = string.char(num2) end function ZZBase64.__decodeLeft2(res, index, text, len) local decode = ZZBase64.__decode local a = decode[string.byte(text, len + 1)] local b = decode[string.byte(text, len + 2)] local num = a * 64 + b num = math.floor(num / 16) res[index] = string.char(num) end return StartDebug Msg--Msg.lua--------------------------- --@tianye112197 --@2016-09-22 ------------------------------------ ------------------------------------ ---@class Msg:LuaStaticClass local Msg = defClassStatic("Msg") Msg._msgmap = {} Msg._idMap = {} Msg.init = function() -- _MSG.init(Msg) end local i = 0 Msg.def = function(k) if Msg.isDef(k) then error("MsgDefine k redefined!" .. k) end i = i + 1 Msg._idMap[i] = k rawset(Msg, k, i) return i end Msg.getMsgName = function(i) return Msg._idMap[i] end Msg.isDef = function(k) if rawget(Msg, k) then return true else return false end end --[[ luaobj可以是BaseScene,BaseComponent,UILayer ]] ---@param idOrList string|string[] ---@param func fun(msgid:string, ...)|fun(object:LuaClass, msgid:string, ...) ---@param luaobj LuaClass ---@param priority? integer 默认是0, 优先级越大越先执行 Msg.add = function(idOrList, func, luaobj, priority) priority = priority or 0 if type(idOrList) == "table" and idOrList.__cls_type == nil then for i, v in ipairs(idOrList) do Msg._add(v, func, luaobj, priority) end else Msg._add(idOrList, func, luaobj, priority) end end Msg._add = function(msgid, func, luaobj, priority) if luaobj then local isValid = false local t = type(luaobj) if t == "table" then if luaobj.__cls_inst then isValid = true else printError("Msg.add只能用于luaoobject,不能用于" .. tostring(luaobj.__cls_name)) end else printError("Msg.add只能用于luaoobject,不能用于" .. t) end end local list = Msg._msgmap[msgid] if not list then list = {} Msg._msgmap[msgid] = list end local f,_luaobj for i, v in ipairs(list) do f = v[1] _luaobj = v[2] if f == func and _luaobj == luaobj then return end end table.insert(list, {func, luaobj, priority}) end Msg.del = function(idOrList, func, luaobj) if type(idOrList) == "table" and idOrList.__cls_type == nil then for i, v in ipairs(idOrList) do Msg._del(v, func, luaobj) end else Msg._del(idOrList, func, luaobj) end end Msg._del = function(msgid, func, luaobj) if msgid then local list = Msg._msgmap[msgid] if list then local f,_luaobj,v for i = #list, 1, -1 do v = list[i] f = v[1] _luaobj = v[2] if (func == nil or func == f) and (luaobj == nil or luaobj == _luaobj) then table.remove(list, i) end end end else for msgid, v in pairs(Msg._msgmap) do Msg._del(msgid, func, luaobj) end end end Msg.send = function(msgid, ...) local list = Msg._msgmap[msgid] if list then local temp = {} for i = #list, 1, -1 do temp[i] = list[i] end table.stableSort(temp, function (a, b) return a[3] > b[3] end) local luaobj,f for i, v in ipairs(temp) do luaobj = v[2] f = v[1] if luaobj then f(luaobj, msgid, ...) else f(msgid, ...) end end end end Msg.cast = Msg.send --for compat Msg.call = function(msgid, ...) local list = Msg._msgmap[msgid] if list then local temp = {} for i = #list, 1, -1 do temp[i] = list[i] end table.stableSort(temp, function (a, b) return a[3] > b[3] end) for i,v in ipairs(temp) do local ret = nil if v[2] then ret = {v[1](v[2], msgid, ...)} else ret = {v[1](msgid, ...)} end if ret[1] ~= nil then return table.unpack(ret) end end end end Msg.exit = function() Msg._msgmap = {} end --protect Msg Var setmetatable( Msg, { __newindex = function(t, k, v) error(string.format("MsgDefine readonly %s,%s,%s", tostring(t), tostring(k), tostring(v))) end, __index = function(t, k) error(string.format("MsgDefine notfound %s,%s", tostring(t), tostring(k))) end } ) FirebaseAnalyticsUtil#--[[ 埋点工具 author:{zhangpeng} time:2023-12-07 15:44:24 ]] local FirebaseAnalyticsUtil, super = defClassStatic("FirebaseAnalyticsUtil") local IOC_FA_UTIL_CLASS_NAME = "FirebaseUtil" local JavaFireBaseClass = "com/fy/xgame/tilelink/util/FireBaseUtil" function FirebaseAnalyticsUtil:init() end function FirebaseAnalyticsUtil:sendAnalyticsEvent(eventName, params) local param = {} for k,v in pairs(params) do local key = string.format("eventParam_%s",k) param[key] = v end if Device.isIOS() then param.eventName = eventName luaoc.callStaticMethod(IOC_FA_UTIL_CLASS_NAME, "logEventWithName", param) elseif Device.isAndroid() then local paramStr = json.encode(param) luaj.callStaticMethod(JavaFireBaseClass, "logEventWithName", { eventName, paramStr }) end end function FirebaseAnalyticsUtil:setUserId(userId) if Device.isIOS() then local param = {} param.userId = userId luaoc.callStaticMethod(IOC_FA_UTIL_CLASS_NAME, "setUserId", param) elseif Device.isAndroid() then luaj.callStaticMethod(JavaFireBaseClass, "setUserId", { tostring(userId) }) end end -- 上传错误信息 function FirebaseAnalyticsUtil:sendErrorInfo(errorStr) end FirebaseAnalyticsUtil:init() UITweenAction\--[[ author:{zhangpeng} time:2023-09-17 12:26:17 ]] local UITweenAction,_ = defClassStatic("UITweenAction") function UITweenAction:init() end function UITweenAction:runTweenScale(layerObj, srcScale, dstScale, cb) if App.isPaused() then return end layerObj.__tweenOnOpenGo:SetScalef(srcScale) layerObj:disableAllUITouch() layerObj.__tweenOnOpenGo:RunAction(ua.Sequence({ua.ease.BackOut(ua.ScaleTo(0.3, dstScale)), ua.cb(function() layerObj:enableAllUITouch() if cb then cb() end end) })) end function UITweenAction:runTweenScaleUINode(node, srcScale, dstScale, cb) node:SetScalef(srcScale) node:RunAction(ua.Sequence({ua.ease.BackOut(ua.ScaleTo(0.3, dstScale)), ua.cb(function() if cb then cb() end end) })) end UITweenAction:init() main>-- 全局通用的常量 require("data/const/StrogeKeyDef") UIOrderDef--[[ UI层级定义 author:{zhangpeng} time:2022-05-25 17:34:07 ]] local UIOrderDef,_ = defClassStatic("UIOrderDef") UIOrderDef.SORTING_ORDER = { LOCAL_UI = 10000, GLOBAL_UI = 30000 --32767是允许的最大值 } --UI类型定义 UIOrderDef.UI_ORDER = { GAME = 1,--非UGUI的ui界面 PANEL = 10000, -- 默认panle,全屏 CURRENCY_BAR = 10000, -- 货币栏 GUIDE = 20001, -- 新手引导 DIALOG = 20002, --对话框 SYS_PANEL = 20005, -- 系统弹窗 层级高 LOADING = 20006, -- TOAST = 20007, -- 纯文字提示条 TOP_TOUCH_MASK = 30001-- 最顶层点击遮罩 } function UIOrderDef:init() end UIOrderDef:init() uActionZ --[[ author:{zhangpeng} time:2022-08-15 17:40:45 ]] local api = CS.wtween.wtweenArgAPI local dynamic_api = CS.wtween.MoveToObjectArgAPI local Vector3 = CS.UnityEngine.Vector3 local SpriteFadeToAPI = CS.wtween.SpriteFadeToAPI local easeType = { Linear = CS.wtween.TweenEase.EaseType.Linear, BounceOut = CS.wtween.TweenEase.EaseType.BounceOut, BounceIn = CS.wtween.TweenEase.EaseType.BounceIn, BounceInOut = CS.wtween.TweenEase.EaseType.BounceInOut, BackIn = CS.wtween.TweenEase.EaseType.BackIn, BackOut = CS.wtween.TweenEase.EaseType.BackOut, BackInOut = CS.wtween.TweenEase.EaseType.BackInOut, Bezier2 = CS.wtween.TweenEase.EaseType.Bezier2, } local function exScaleTo(t,arg1,arg2) local t1 = type(arg1) if t1 == "number" then if arg2 and type(arg2) == "number" then return api.ScaleTo(t,Vector3(arg1,arg2,1)) else return api.ScaleTo(t,Vector3(arg1,arg1,1)) end else return api.ScaleTo(t,arg1) end end local ua = { Delay = api.Delay, MoveTo = api.MoveTo, MoveToObject = dynamic_api.MoveToObject, ScaleTo = exScaleTo, RotateTo = api.RotateTo, BezierTo = CS.wtween.CurveAPI.BezierTo, BezierToNode = CS.wtween.CurveAPI.BezierToNode, cb = api.CallFunc, Destroy = api.Destroy, SetParent = api.SetParent, Sequence = api.Sequence, Spawn = api.Spawn, Step = api.Step, Repeat = api.Repeat, FadeTo = api.FadoTo, RepeatForever = api.RepeatForever, SpriteFadeTo = SpriteFadeToAPI.SpriteFadeTo, Tween = api.Tween,--自定义 ease = { Linear = function(action) return api.Ease(easeType.Linear,action) end, BounceOut = function(action) return api.Ease(easeType.BounceOut,action) end, BounceIn = function(action) return api.Ease(easeType.BounceIn,action) end, BounceInOut = function(action) return api.Ease(easeType.BounceInOut,action) end, BackOut = function(action,s) api.Ease(easeType.BackOut,action) if s then local easeFunc = action.tween.easeFunc easeFunc.s = s end return action end, BackIn = function(action) return api.Ease(easeType.BackIn,action) end, BackInOut = function(action) return api.Ease(easeType.BackInOut,action) end, Bezier2 = function(action,ctrl1,ctrl2) local action = api.Ease(easeType.Bezier2,action) local easeFunc = action.tween.easeFunc return action end, }, Ease = api.Ease, } rawset(_ENV,"ua",ua) UITypeEnums3--[[ UI组件枚举 author:{zhangpeng} time:2024-04-29 21:01:39 ]] local UITypeEnums = defClassStatic("UITypeEnums") local LOG_TAG = "UITypeEnums" -- 弹窗类型 UITypeEnums.DialogType = { Common = 1, SkinPartAdUI = 2, -- 点击未解锁皮肤时的看广告弹窗 SkinPartUnlock =3, -- 金币充足,弹窗确认兑换皮肤 SuitUnlock = 4, -- 钻石充足,弹窗确认兑换套装 WorldAdBtn = 5, WorldMapUnlockUI = 6, -- 场景解锁弹窗 } UITypeEnums.DialogPrefabs = { [UITypeEnums.DialogType.Common] = "Assets/AssetsPackage/Res/framework/ui/dailog/prefab/common_dailog.prefab", [UITypeEnums.DialogType.SkinPartAdUI] = "Assets/AssetsPackage/Res/framework/ui/dailog/prefab/dailog_skin_ad.prefab", [UITypeEnums.DialogType.SkinPartUnlock] = "Assets/AssetsPackage/Res/framework/ui/dailog/prefab/dailog_skin_unlock.prefab", [UITypeEnums.DialogType.SuitUnlock] = "Assets/AssetsPackage/Res/framework/ui/dailog/prefab/dailog_suit_unlock.prefab", [UITypeEnums.DialogType.WorldAdBtn] = "Assets/AssetsPackage/Res/framework/ui/dailog/prefab/dailog_worldmap_ui_ad.prefab", [UITypeEnums.DialogType.WorldMapUnlockUI] = "Assets/AssetsPackage/Res/Modules/holoworldmap/prefab/ui/unlock_scene_pop_ui.prefab", } function UITypeEnums:init() end UITypeEnums:init() debug_mainlocal LOG_TAG = "debug_main" -- luaide 调试 local debugXpCall local breakSocketHandle local platform = CS.UnityEngine.Application.platform local can_debug = (platform == CS.UnityEngine.RuntimePlatform.WindowsEditor or platform == CS.UnityEngine.RuntimePlatform.WindowsPlayer or false) local DEBUG_LUA = true--false and can_debug if DEBUG_LUA then breakSocketHandle,debugXpCall = require("framework/debug/LuaDebug")("localhost",7003) printInfo(LOG_TAG,"init lua debug :",platform) end XSdk--[[ author:{zhangpeng} time:2023-09-24 17:18:28 ]] local XSdk, super = defClassStatic("XSdk", XSdkBase) function XSdk:init() end XSdk:init()PlayerPrefsMgr --[[ PlayPrefsMgr存档管理 author:{zhangpeng} time:2025-08-20 10:05:35 ]] local PlayerPrefsMgr = defClassStatic("PlayerPrefsMgr") local PlayerPrefs = CS.WXPlayerPref -- 微信的PlayerPrefs类,用于存储用户数据 -- local PlayerPrefs = CS.UnityEngine.PlayerPrefs -- 微信的PlayerPrefs类,用于存储用户数据 local GetString = PlayerPrefs.GetString -- 获取指定key的值 fun(key, defValue):string local SetString = PlayerPrefs.SetString -- 设置指定key的值 fun(key, value):void local GetInt = PlayerPrefs.GetInt -- 获取指定key的值 fun(key, defValue):int local SetInt = PlayerPrefs.SetInt -- 设置指定key的值 fun(key, value):void local GetFloat = PlayerPrefs.GetFloat -- 获取指定key的值 fun(key, defValue):float local SetFloat = PlayerPrefs.SetFloat -- 设置指定key的值 fun(key, value):void local DeleteKey = PlayerPrefs.DeleteKey -- 删除指定key fun(key):void local DeleteAll = PlayerPrefs.DeleteAll -- 删除所有key fun():void local Save = PlayerPrefs.Save -- 保存所有修改 fun():void -- 获取字符串值 function PlayerPrefsMgr:getString(key, defaultValue) if not defaultValue then defaultValue = "" -- 如果没有提供默认值,使用空字符串 end local value = GetString(key, defaultValue) if value == "null" then return defaultValue end return value end -- 设置字符串值 function PlayerPrefsMgr:setString(key, value) if value == nil or value == "" then return else SetString(key, value) end end -- 获取整数值 function PlayerPrefsMgr:getInt(key, defaultValue) if not defaultValue then defaultValue = 0 -- 如果没有提供默认值,使用0 end return GetInt(key, defaultValue) end -- 设置整数值 function PlayerPrefsMgr:setInt(key, value) if value == nil then return else SetInt(key, value) end end -- 获取浮点数值 function PlayerPrefsMgr:getFloat(key, defaultValue) if not defaultValue then defaultValue = 0.0 -- 如果没有提供默认值,使用0.0 end return GetFloat(key, defaultValue) end -- 设置浮点数值 function PlayerPrefsMgr:setFloat(key, value) if value == nil then return else SetFloat(key, value) end end -- 删除指定key function PlayerPrefsMgr:deleteKey(key) DeleteKey(key) end -- 删除所有key function PlayerPrefsMgr:deleteAll() DeleteAll() end -- 保存所有修改 function PlayerPrefsMgr:save() Save() end -- 将字典的键从数字转换为字符串 function PlayerPrefsMgr:idToString(dic) local put = {} for i, v in pairs(dic) do put[tostring(i)] = v end return put end -- 将字典的键从字符串转换为数字 function PlayerPrefsMgr:idToNumber(dic) local put = {} for i, v in pairs(dic) do put[tonumber(i)] = v end return put end return PlayerPrefsMgr ApplePaymentMgr --[[ 苹果支付 author:{zhangpeng} time:2024-08-13 14:33:01 ]] local ApplePaymentMgr, super = defClassStatic("ApplePaymentMgr") local LOG_TAG = "ApplePaymentMgr" local IOC_CLASS_NAME = "XIAPManager" function ApplePaymentMgr:init() self:registIAPLuaCallback() self:registMsgListener() end function ApplePaymentMgr:registMsgListener() Msg.add( { Msg.SHOP_iOS_PURCHASE_SUC, Msg.SHOP_iOS_PURCHASE_FAILED }, function(...) self:listener(...) end ) end function ApplePaymentMgr:listener(msgId,data) if msgId == Msg.SHOP_iOS_PURCHASE_SUC then printInfo(LOG_TAG, "Apple支付成功, 向服务器验签") UIComsTool:showToast(TextCfgParse:getTextStr("payment_verify_server"),2) ApplePaymentMgr:verifyTransaction(data.transactionReceipt,function (suc, rspData) if suc then self:parseProductInfo(rspData) else printError("验签失败!") end end) elseif msgId == Msg.SHOP_iOS_PURCHASE_FAILED then printInfo(LOG_TAG,"ios 支付失败 error code:%s", data.errorcode) self:handleErrorCode(data.errorcode) end end function ApplePaymentMgr:parseProductInfo(rspData) table.print_r(rspData,"支付验签返回") UIComsTool:showToast(TextCfgParse:getTextStr("payment_verify_suc"),2) local _rspData = rspData.shopItem local id = _rspData.id local count = _rspData.count local price = _rspData.price local key = _rspData.key local name = _rspData.name local platform = _rspData.platform printInfo(LOG_TAG, "解析验签返回数据: id:%s count:%s price:%s key:%s name:%s platform:%s", id, count, price, key, name, platform) Msg.send(Msg.GEM_UPDATE_COUNT, { count = count, awardType = "gem", opt = "add" }) UIComsTool:hideLoading() end -- 根据id购买 function ApplePaymentMgr:payByProductId(productId) if Device.isIOS() then luaoc.callStaticMethod( "XIAPManager", "doPayment", { productId = productId, userName = User:getUserName() } ) end end -- 交易验证(把票据信息receipt发给服务器,验签成功后,赋予用户购买的商品) function ApplePaymentMgr:verifyTransaction(originalTransactionId,cb) local param = {} param.originalTransactionId = originalTransactionId local jsonStr = json.encode(param) printInfo(LOG_TAG,"发送购买验签请求,订单id:%s", originalTransactionId) HttpCmdMgr:postCmdSync(HttpCmdDef.CMD.VERIFY_TRANS,jsonStr, function (...) if cb then cb(...) end end ) end function ApplePaymentMgr:registIAPLuaCallback() local param = { purchaseSucCallback = function(state,transactionReceipt) if state == XSdkConstants.TransactionState.PaymentSuc then printInfo(LOG_TAG,"购买成功回调到 lua call: %s transactionReceipt:%s",state,transactionReceipt) Msg.send(Msg.SHOP_iOS_PURCHASE_SUC,{state = state, transactionReceipt = transactionReceipt}) end end, purchaseFaileCallback = function (state, errorcode) if state == XSdkConstants.TransactionState.PaymentFailed then printInfo(LOG_TAG,"购买失败回调到lua %s",errorcode) Msg.send(Msg.SHOP_iOS_PURCHASE_FAILED, {errorcode = errorcode}) end end, canNotMakePaymentsCallback = function () UIComsTool:showToast(TextCfgParse:getTextStr("payment_disable") ,2) -- 购买功能不可用 end, invalidProductIdentifiersCallback = function (productId) UIComsTool:showToast(TextCfgParse:getTextStr("payment_disable_item"..productId),2)-- 无效的购买商品 end } luaoc.callStaticMethod(IOC_CLASS_NAME, "registLuaCallback", param) end function ApplePaymentMgr:handleErrorCode(code) if code == PaymentErrorCode.iOS.SKErrorPaymentCancelled then UIComsTool:showToast(TextCfgParse:getTextStr("payment_cancle"),1) -- 购买取消 else UIComsTool:showToast(TextCfgParse:getTextStr("payment_failed"),1) -- 购买失败 end UIComsTool:hideLoading() end SqliteUtil> ---@class SqliteUtil:LuaStaticClass local SqliteUtil = defClassStatic("SqliteUtil") local LOGTAG = SqliteUtil.__cls_name SqliteUtil.null = { str = "NULL" } function SqliteUtil:luaTypeNameToSqlTypeName(luaTypeName) local sqlTypeName = "" if luaTypeName == "string" then sqlTypeName = "TEXT" elseif luaTypeName == "number" then sqlTypeName = "INTEGER" else printError(LOGTAG, "luaTypeNameToSqlTypeName, unsupport lua type %s", luaTypeName) end return sqlTypeName end function SqliteUtil:sqlTypeNameToLuaTypeName(sqlTypeName) local luaTypeName = "" if sqlTypeName == "TEXT" then luaTypeName = "string" elseif sqlTypeName == "INTEGER" then luaTypeName = "number" else printError(LOGTAG, "sqlTypeNameToLuaTypeName, unsupport sql type %s", sqlTypeName) end return luaTypeName end function SqliteUtil:luaValueToStr(luaValue, luaTypeName) local str = nil luaTypeName = luaTypeName or type(luaValue) if luaTypeName == "string" then str = string.format("'%s'", luaValue) elseif math.type(luaValue) == "integer" then str = string.format("%d", luaValue) elseif luaValue == SqliteUtil.null then str = SqliteUtil.null.str else str = luaValue end return str end function SqliteUtil:connectCondionWithAnd(conditionList) local ret = nil for i, condition in ipairs(conditionList) do if not ret then ret = condition else ret = ret:xand(condition) end end return ret end function SqliteUtil:connectCondionWithOr(conditionList) local ret = nil for i, condition in ipairs(conditionList) do if not ret then ret = condition else ret = ret:xor(condition) end end return ret end RichTextUtil--[[ 富文本工具 author:{zhangpeng} time:2024-03-27 23:36:07 ]] local RichTextUtil = {} --[[ @desc: time:2024-03-27 23:38:24 --@text: 原始文本 --@borderColor:描边颜色 --@borderWidth: 描边粗细(单位:像素) @return: ]] function RichTextUtil:addBorder(text, borderColor, borderWidth) local richText = string.format("%s", borderColor, text) local borderText = string.format("%s", text) for i = 1, borderWidth do richText = string.format("%s", richText) borderText = string.format("%s", borderText) richText = borderText .. richText end return richText end return RichTextUtil BaseModel  ---@class BaseModel:LuaClass local BaseModel = defClass("BaseModel") function BaseModel:ctor() self.bindInfoList = {} self:init() end --重写 function BaseModel:init() end --[[ 四种绑定方式 self:b("name", "pbName") self:b("name", "pbRole.name") self:b("role", "pbRole", roleModel) --未测试 self:b("roleList", "pbRoleList", {roleModel}) --未测试 ]] function BaseModel:bindKey(keyName, pbName, t) local moCls = t local isArr = false if t and t[1] then moCls = t[1] isArr = true end local bindInfo = {keyName = keyName, pbName = pbName, moCls = moCls, isArr = isArr} table.insert(self.bindInfoList, bindInfo) return bindInfo end BaseModel.b = BaseModel.bindKey function BaseModel:setByPB(pbData) for i, bindInfo in ipairs(self.bindInfoList) do local value = nil local list = string.split(bindInfo.pbName, ".") local count = #list for j, name in ipairs(list) do name = tonumber(name) or name if j == 1 then value = pbData[name] elseif j < count then value = value[name] or {} else value = value or {} value = value[name] end end if bindInfo.moCls then if bindInfo.isArr then self[bindInfo.keyName] = {} for k, pbData in ipairs(value) do table.insert(self[bindInfo.keyName], bindInfo.moCls.new():setByPB(pbData)) end else self[bindInfo.keyName] = bindInfo.moCls.new():setByPB(value) end else self[bindInfo.keyName] = value end end return self end function BaseModel:getToPB() local pbData = {} for i, bindInfo in ipairs(self.bindInfoList) do local value = nil if bindInfo.moCls then if bindInfo.isArr then value = {} for k, mo in ipairs(self[bindInfo.keyName]) do table.insert(value, mo:getToPB()) end else value = self[bindInfo.keyName]:getToPB() end else value = self[bindInfo.keyName] end local list = string.split(bindInfo.pbName, ".") local t = nil local count = #list for j, name in ipairs(list) do name = tonumber(name) or name if 1 == count then pbData[name] = value else if j == 1 then if pbData[name] == nil then pbData[name] = {} end t = pbData[name] elseif j < count then if t[name] == nil then t[name] = {} end t = t[name] else t[name] = value end end end end return pbData end return BaseModel ExtendScenelocal SceneCls = HackCSharpClass(CS.UnityEngine.SceneManagement.Scene) SceneCls.GetGameRoot = function(self) local rootGos = self:GetRootGameObjects() for i = 0,rootGos.Length - 1,1 do local go = rootGos[i] if go.name == "GameRoot" then return go end end return rootGos[0] end SceneCls.SeekByXPath = function(self,xpath) local rootGos = self:GetRootGameObjects() local splits = string.split(xpath,"/") for i = 0,rootGos.Length - 1,1 do local go = rootGos[i] if go.name == splits[1] then table.remove(splits,1) return go:SeekByXPath(table.concat(splits,"/")) end end endCosLuaCredentialBean7---@class CosLuaCredentialBean:LuaClass local CosLuaCredentialBean = defClass("CosLuaCredentialBean") ---comment ---@param storage_type string 存储类型(默认Standard,表示标准存储) ---@param service_type string 业务类型(1-homework 作业业务,99-extension 扩展业务) ---@param allow_prefix string 允许访问的文件前缀 ---@param max_file_size integer 最大文件大小(100M) ---@param left string 临时密钥id ---@param right string 临时密钥key ---@param session_token string 临时密钥sessionToken,用于向腾讯云发起请求时携带 ---@param start_time integer 密钥起始时间(单位:s) ---@param expired_time integer 密钥过期时间(单位:s) ---@param bucket_name string 存储桶名称 ---@param protocol string http协议 ---@param region string 存储桶地区 ---@param domain string 域名 ---@param base_url string 基础 url ---@param server_time_zone string 服务器时区 ---@param cos_appid string 对象存储的appid,用于与腾讯云通讯 function CosLuaCredentialBean:ctor(storage_type, service_type, allow_prefix, max_file_size, left, right, session_token, start_time, expired_time, bucket_name, protocol, region, domain, base_url, server_time_zone, cos_appid) self.storage_type = storage_type self.service_type = service_type self.allow_prefix = allow_prefix self.max_file_size = max_file_size self.left = left self.right = right self.session_token = session_token self.start_time = start_time self.expired_time = expired_time self.bucket_name = bucket_name self.protocol = protocol self.region = region self.domain = domain self.base_url = base_url self.server_time_zone = server_time_zone self.cos_appid = cos_appid end function CosLuaCredentialBean:initWithConfig(config) for key, value in pairs(config) do self[key] = value end self.max_file_size = tonumber(self.max_file_size) self.start_time = tonumber(self.start_time) self.expired_time = tonumber(self.expired_time) end return CosLuaCredentialBean SqliteColumn ---@class SqliteColumn:LuaClass local SqliteColumn = defClass("SqliteColumn") function SqliteColumn:ctor(table, name, type, defaultValue, isPrimary, isMetadata) self.table = table self.name = name self.type = type -- lua type self.defaultValue = defaultValue self.isPrimary = isPrimary self.isMetadata = isMetadata self.asName = nil end function SqliteColumn:as(asName) self.asName = asName return self end function SqliteColumn:getSqlType() return SqliteUtil:luaTypeNameToSqlTypeName(self.type) end --------------------------------------------------------------------------------------------- -- 工具函数 --------------------------------------------------------------------------------------------- function SqliteColumn:toSqlStr(isFullName) if not isFullName then return self.name end return string.format("%s.%s", self.table:getName(), self.asName) end return SqliteColumn SqliteMgrD ---@class SqliteMgr:DBMgr local SqliteMgr = defClassStatic("SqliteMgr", DBMgr) ---@param isEncrypt boolean 是否加密 ---@param getDeviceIdFunc fun(): string 获取deviceid的方法 ---@param getTimeFunc fun(): number 获取时间戳的方法, 默认 os.time() function SqliteMgr:init(isEncrypt, getDeviceIdFunc, getTimeFunc) SqliteMgr.super.init(self, isEncrypt, getDeviceIdFunc, getTimeFunc) self.logTag = "SqliteMgr" self.userDBName = "user.db" self.roleDBName = "role.db" self.deviceDBName = "device.db" self.dbCls = SqliteDatabase end .bundles_assets_assetspackage_luascripts.bundlert^DqbC{+zAC̓i7d^QɯF@k,Z|-43K>LcdɄO'# -:-b*oHviZ~?jBtXghr¦J#cdGyDI-fh>W;Xxrq5/&ÒSzntD6^ aiN9Uϩ|060 ݜF; ʠ&nX0nC 6dSm<y, КA 6jV)(#g2~b'5c ,OWx`#E7s7TC`N0\cD3Osgw=k}y) W=HRyBXW|oo^boZ+L4 H"g9HO kN k+yP6hg lNMHGxcX0Za `q2{.ǤL?}5~.M ٞ[]L9]x F4~ۯClKMAb)Lz zS4[]gf"2`O_rבvkVwRyv&נ>|@dSw(!V`;чK*z)ܚ>? )((i58B#]5W}x?'LEMKTs_maFĞXJdZ6AǨ~PIea"b-7ʎAP<*`s'/޽ha vd ):KO 8٨WT>ggp_<}3IFA*Lw<]טCSsIO3fAΩrn.s43eFUaAgZk߳DV(K`:k\0a  =E'AϹjNws3%T۹ < ұx*@ ?GIZ'F57S2| gvH0 +hȌsd1Xa͓GUC1՚6)!؎@p0i5ڎ鄠> q;FjfcdV+,Tu#3_k3DEԗ\myDJDKOBtׄL/Fg޾$P7Assets/AssetsPackage/LuaScripts/boot/build_config.bytes@L?}5~.5Assets/AssetsPackage/LuaScripts/boot/debug/main.bytes2o/Assets/AssetsPackage/LuaScripts/boot/main.bytes aiN96Assets/AssetsPackage/LuaScripts/boot/main_editor.bytes_a"b-5Assets/AssetsPackage/LuaScripts/boot/main_webgl.bytes9w'.=Assets/AssetsPackage/LuaScripts/data/const/StrogeKeyDef.bytes"C 65Assets/AssetsPackage/LuaScripts/data/const/main.bytes/) W=HBAssets/AssetsPackage/LuaScripts/data/game_datas/CurrencyData.bytesPSw?Assets/AssetsPackage/LuaScripts/data/game_datas/LoginData.bytesGyDI-:Assets/AssetsPackage/LuaScripts/data/game_datas/main.bytesDۯClK/Assets/AssetsPackage/LuaScripts/data/main.bytesDAssets/AssetsPackage/LuaScripts/data/scene_config/SceneCfgList.bytes,Osg<Assets/AssetsPackage/LuaScripts/data/scene_config/main.bytes5H"g9HGAssets/AssetsPackage/LuaScripts/data/static_config/config/TextCfg.bytesY'LEMKAssets/AssetsPackage/LuaScripts/data/static_config/parse/TextCfgParse.bytesdd ):KCAssets/AssetsPackage/LuaScripts/data/static_config/parse/main.bytes/F<Assets/AssetsPackage/LuaScripts/framework/core/app/App.bytes3o^boZ<Assets/AssetsPackage/LuaScripts/framework/core/app/Msg.bytes͓GU=Assets/AssetsPackage/LuaScripts/framework/core/app/main.bytesAM ٞDAssets/AssetsPackage/LuaScripts/framework/core/app/scene/Scene.bytesV58B#GAssets/AssetsPackage/LuaScripts/framework/core/app/scene/SceneCfg.bytesɯF@kMAssets/AssetsPackage/LuaScripts/framework/core/app/scene/SceneComponent.bytes}7S2|HAssets/AssetsPackage/LuaScripts/framework/core/app/scene/SceneInfo.bytesfWT>gGAssets/AssetsPackage/LuaScripts/framework/core/app/scene/SceneMgr.bytes{?GICAssets/AssetsPackage/LuaScripts/framework/core/app/scene/main.bytes+h=Assets/AssetsPackage/LuaScripts/framework/core/base/Def.bytesbC{+z>Assets/AssetsPackage/LuaScripts/framework/core/base/Enum.bytes'#g2~b'5=Assets/AssetsPackage/LuaScripts/framework/core/base/Env.bytes.k}y?Assets/AssetsPackage/LuaScripts/framework/core/base/Event.bytes,CAssets/AssetsPackage/LuaScripts/framework/core/base/ExtendLua.bytesl]טCS=Assets/AssetsPackage/LuaScripts/framework/core/base/Log.byteso\Zϯ=Assets/AssetsPackage/LuaScripts/framework/core/base/Msg.bytesSz)ܚ>EAssets/AssetsPackage/LuaScripts/framework/core/base/ReslinkLoad.bytes06BAssets/AssetsPackage/LuaScripts/framework/core/base/TimerMgr.bytesZKTs_m>Assets/AssetsPackage/LuaScripts/framework/core/base/Util.bytes¦J#cdDAssets/AssetsPackage/LuaScripts/framework/core/base/extend/Ext.bytesT?MAssets/AssetsPackage/LuaScripts/framework/core/base/extend/ExtendBounds.bytesd^QQAssets/AssetsPackage/LuaScripts/framework/core/base/extend/ExtendGameObject.byteson.sWAssets/AssetsPackage/LuaScripts/framework/core/base/extend/ExtendPlayableDirector.bytesKAssets/AssetsPackage/LuaScripts/framework/core/base/extend/ExtendRect.bytesBl>dLAssets/AssetsPackage/LuaScripts/framework/core/base/extend/ExtendScene.bytesx3%TEAssets/AssetsPackage/LuaScripts/framework/core/base/extend/main.bytes dɄO'#>Assets/AssetsPackage/LuaScripts/framework/core/base/main.bytesXaJAssets/AssetsPackage/LuaScripts/framework/core/base/resmgr/ResLoader.bytesjBtXgPAssets/AssetsPackage/LuaScripts/framework/core/base/resmgr/YooAssetAdapter.bytesHEAssets/AssetsPackage/LuaScripts/framework/core/base/resmgr/main.bytes8yP6hRAssets/AssetsPackage/LuaScripts/framework/core/base/touch/TouchClickListener.bytes<۝L0HAssets/AssetsPackage/LuaScripts/framework/core/base/touch/TouchCom.bytesjfc`q2IAssets/AssetsPackage/LuaScripts/framework/core/base/utils/SpineUtil.bytes: lNMHJAssets/AssetsPackage/LuaScripts/framework/core/base/utils/StringUtil.bytes?{.ǤIAssets/AssetsPackage/LuaScripts/framework/core/base/utils/TableUtil.bytes&b점=*HAssets/AssetsPackage/LuaScripts/framework/core/base/utils/TimeUtil.bytesnfAΩrLAssets/AssetsPackage/LuaScripts/framework/core/base/utils/TimelineUtil.bytesutHAssets/AssetsPackage/LuaScripts/framework/core/base/utils/UGuiUtil.bytesW;XxEAssets/AssetsPackage/LuaScripts/framework/core/base/utils/async.bytesL/FgFAssets/AssetsPackage/LuaScripts/framework/core/base/utils/luafsm.bytesU )((iDAssets/AssetsPackage/LuaScripts/framework/core/base/utils/main.bytesp43eFGAssets/AssetsPackage/LuaScripts/framework/core/base/utils/serpent.bytesy۹ < GAssets/AssetsPackage/LuaScripts/framework/core/base/utils/uAction.bytes;+=Assets/AssetsPackage/LuaScripts/framework/core/db/DBMgr.bytesi3IFAGAssets/AssetsPackage/LuaScripts/framework/core/db/LocalStorageMgr.bytes=a GAssets/AssetsPackage/LuaScripts/framework/core/db/dbkv/KVDatabase.bytesyDJDKOBAssets/AssetsPackage/LuaScripts/framework/core/db/dbkv/KVMgr.bytes7 k+DAssets/AssetsPackage/LuaScripts/framework/core/db/dbkv/KVTable.bytes1XW|GAssets/AssetsPackage/LuaScripts/framework/core/db/dbsql/BaseModel.bytess`OAssets/AssetsPackage/LuaScripts/framework/core/db/dbsql/SingleSqliteTable.bytes`7ʎAP<JAssets/AssetsPackage/LuaScripts/framework/core/db/dbsql/SqliteColumn.bytesW]5MAssets/AssetsPackage/LuaScripts/framework/core/db/dbsql/SqliteCondition.bytes't `LAssets/AssetsPackage/LuaScripts/framework/core/db/dbsql/SqliteDatabase.bytes ; ʠ&HAssets/AssetsPackage/LuaScripts/framework/core/db/dbsql/SqliteFunc.bytesqUaAgZk߳GAssets/AssetsPackage/LuaScripts/framework/core/db/dbsql/SqliteMgr.bytesvIAssets/AssetsPackage/LuaScripts/framework/core/db/dbsql/SqliteModel.bytesv=E'AϹjIAssets/AssetsPackage/LuaScripts/framework/core/db/dbsql/SqliteQuery.bytes$y, IAssets/AssetsPackage/LuaScripts/framework/core/db/dbsql/SqliteTable.bytesB[]L9HAssets/AssetsPackage/LuaScripts/framework/core/db/dbsql/SqliteUtil.bytes43K>LEAssets/AssetsPackage/LuaScripts/framework/core/db/dbsql/SyncMgr.bytesq;F<Assets/AssetsPackage/LuaScripts/framework/core/db/main.bytesHgSAssets/AssetsPackage/LuaScripts/framework/core/db/playerprefs/PlayerPrefsKeys.bytesQ(!V`;RAssets/AssetsPackage/LuaScripts/framework/core/db/playerprefs/PlayerPrefsMgr.bytes& 6jV)(HAssets/AssetsPackage/LuaScripts/framework/core/db/playerprefs/main.bytes  -:9Assets/AssetsPackage/LuaScripts/framework/core/main.bytes^~PIeIAssets/AssetsPackage/LuaScripts/framework/cos/CosLuaBatchUploadTask.bytes鄠> HAssets/AssetsPackage/LuaScripts/framework/cos/CosLuaCredentialBean.bytes/l>Assets/AssetsPackage/LuaScripts/framework/cos/CosLuaEnum.bytes?=Assets/AssetsPackage/LuaScripts/framework/cos/CosLuaMgr.bytesrq5/AAssets/AssetsPackage/LuaScripts/framework/cos/CosLuaTagTask.bytesJ`O_MAssets/AssetsPackage/LuaScripts/framework/cos/CosLuaTemporaryCredential.bytes!؎@p0DAssets/AssetsPackage/LuaScripts/framework/cos/CosLuaUploadTask.byteszntD8Assets/AssetsPackage/LuaScripts/framework/cos/main.byteseO 8٨JAssets/AssetsPackage/LuaScripts/framework/debug/luaidedebug/LuaDebug.byteszұx*@ MAssets/AssetsPackage/LuaScripts/framework/debug/luaidedebug/LuaDebugjit.bytes yTQAssets/AssetsPackage/LuaScripts/framework/debug/luaidedebug/boot_debug_main.bytesȌsd1LAssets/AssetsPackage/LuaScripts/framework/debug/luaidedebug/debug_main.bytes%КAFAssets/AssetsPackage/LuaScripts/framework/debug/luaidedebug/main.bytesl p0=Assets/AssetsPackage/LuaScripts/framework/device/Device.bytes*s7TC`;Assets/AssetsPackage/LuaScripts/framework/device/main.bytesj*L4Assets/AssetsPackage/LuaScripts/framework/main.bytes|Ca$HAssets/AssetsPackage/LuaScripts/framework/network/NetworkStateUtil.bytesWNNGAssets/AssetsPackage/LuaScripts/framework/network/http/HttpCmdDef.bytesRDGAssets/AssetsPackage/LuaScripts/framework/network/http/HttpCmdMgr.bytesi5ڎ<Assets/AssetsPackage/LuaScripts/framework/network/main.bytes9gEAssets/AssetsPackage/LuaScripts/framework/network/socket/CmdDef.bytesTu#3_k3KAssets/AssetsPackage/LuaScripts/framework/network/socket/PBSocketPack.byteskw<KAssets/AssetsPackage/LuaScripts/framework/network/socket/SocketCmdMgr.bytesUϩ|HAssets/AssetsPackage/LuaScripts/framework/network/socket/SocketMgr.bytesV+HAssets/AssetsPackage/LuaScripts/framework/network/socket/pbreslink.bytesggp_RAssets/AssetsPackage/LuaScripts/framework/platform/base/applovin/ApplovinMgr.bytesACQAssets/AssetsPackage/LuaScripts/framework/platform/base/common/PlatformUtil.bytes0RyBUAssets/AssetsPackage/LuaScripts/framework/platform/base/common/ScreenRecordUtil.bytesrt^TAssets/AssetsPackage/LuaScripts/framework/platform/base/device/DeviceVibration.bytes]b^Assets/AssetsPackage/LuaScripts/framework/platform/base/facebook/login/FacebookLoginUtil.bytesca v^Assets/AssetsPackage/LuaScripts/framework/platform/base/facebook/share/FacebookShareUtil.bytes\XJdZ\Assets/AssetsPackage/LuaScripts/framework/platform/base/firebase/FirebaseAnalyticsUtil.bytesBGN$xXAssets/AssetsPackage/LuaScripts/framework/platform/base/firebase/FirebaseEventEnum.bytes;GxcX0ZVAssets/AssetsPackage/LuaScripts/framework/platform/base/ironsource/IronSourceMgr.bytes6O kNBAssets/AssetsPackage/LuaScripts/framework/platform/base/main.bytes#dSmAssets/AssetsPackage/LuaScripts/framework/ui/UITypeEnums.bytes ͕7Assets/AssetsPackage/LuaScripts/framework/ui/main.bytes6AAssets/AssetsPackage/LuaScripts/framework/ui/uibase/UILayer.bytes޾$PEAssets/AssetsPackage/LuaScripts/framework/ui/uibase/UILayerUtil.bytes\mDAssets/AssetsPackage/LuaScripts/framework/ui/uibase/UIOrderDef.bytes-w=@Assets/AssetsPackage/LuaScripts/framework/ui/uibase/UIRoot.bytes|Z'F5>Assets/AssetsPackage/LuaScripts/framework/ui/uibase/main.bytesEMADAssets/AssetsPackage/LuaScripts/framework/ui/uicoms/UIComsTool.bytes~ gvBAssets/AssetsPackage/LuaScripts/framework/ui/uicoms/UIDialog.bytes2M`wHAssets/AssetsPackage/LuaScripts/framework/ui/uicoms/UIDialogSimple.bytesNv&CAssets/AssetsPackage/LuaScripts/framework/ui/uicoms/UILoading.bytesFb)Lz AAssets/AssetsPackage/LuaScripts/framework/ui/uicoms/UIToast.bytes -b*XAssets/AssetsPackage/LuaScripts/framework/ui/uicoms/reslink/uicircleloadingreslink.bytesb/޽hQAssets/AssetsPackage/LuaScripts/framework/ui/uicoms/reslink/uidialogreslink.bytes oHPAssets/AssetsPackage/LuaScripts/framework/ui/uicoms/reslink/uitoastreslink.bytesOנ>|@dHAssets/AssetsPackage/LuaScripts/framework/ui/uitween/UITweenAction.bytesa*`s'EAssets/AssetsPackage/LuaScripts/framework/ui/uitween/UITweenDef.bytes</Assets/AssetsPackage/LuaScripts/main/main.bytesXW}x?EAssets/AssetsPackage/LuaScripts/modules/common/login/LoginScene.bytes[aFĞBAssets/AssetsPackage/LuaScripts/modules/common/login/LoginUI.bytesGzS4[]LAssets/AssetsPackage/LuaScripts/modules/common/login/loginscenereslink.bytesK-cIAssets/AssetsPackage/LuaScripts/modules/common/login/loginuireslink.byteshr?Assets/AssetsPackage/LuaScripts/modules/common/login/main.bytes(c ,OW2Assets/AssetsPackage/LuaScripts/modules/main.bytesua  :Assets/AssetsPackage/LuaScripts/modules/setting/main.bytes!nX0n=Assets/AssetsPackage/LuaScripts/modules/setting/setting.bytesBtׄ.bundles_assets_assetspackage_luascripts.bundle SceneInfo--[[ author:{zhangpeng} time:2023-03-21 20:26:17 ]] local SceneInfo = defClass("SceneInfo") function SceneInfo:ctor(cfg, args) self:setArgs(args) self:setSceneCfg(cfg) local metatable = getmetatable(self) metatable.__tostring = function (t) return string.format("SceneInfo. path:%s, name:%s", t.path, t.name) end end function SceneInfo:setSceneCfg(cfg) self.cfg = DeepCopy(cfg) self.path = cfg[1] -- 场景代码入口 self.name = cfg[2] or "" -- 场景名字 self.enterTransCls = cfg.enterTransCls end function SceneInfo:getCfg() return self.cfg end function SceneInfo:setArgs(args) self.args = args end function SceneInfo:getArgs() return self.args end function SceneInfo:getPath() return self.path end function SceneInfo:getFileName() local list = string.split(self:getPath(), "/") return list[#list] end function SceneInfo:getName() return self.name end function SceneInfo:getTag() return self.tag end function SceneInfo:setSkipHotUpdate(bool) self.isSkipHotUpdate = bool end function SceneInfo:getEnterTransCls() return self.enterTransCls end function SceneInfo:setEnterTransCls(transCls) self.enterTransCls = transCls end return SceneInfo build_config  local ue = CS.UnityEngine APP_NAME = "Tile Link" local LOGTAG = "[build_config] " print(LOGTAG.."BUILD_ENV:", BUILD_ENV) DEBUG = ue.Debug.isDebugBuild print(LOGTAG.."DEBUG:", DEBUG) SKIP_INPKG_HOTUPDATE = true print(LOGTAG.."跳过热更新:", SKIP_INPKG_HOTUPDATE) -- sqlite是否需要加密 电脑上不加密 SQLITE_ENCRYPT = true if ue.Application.platform == ue.RuntimePlatform.WindowsEditor or ue.Application.platform == ue.RuntimePlatform.OSXEditor then SQLITE_ENCRYPT = false end print(LOGTAG.."SQLITE_ENCRYPT:", SQLITE_ENCRYPT) ENV_DEVELOPMENT = "dev" ENV_PRODUCTION = "production" if BUILD_ENV == ENV_DEVELOPMENT then RES_URL_LAN = "http://192.144.239.125:55068/dev/"--开发用文件服务器地址 RES_URL = "http://192.144.239.125:55068/" -- cdn 资源服务器 WEAK_RES_URL = "" PACKAGE_NAME = "com.fy.xgame.tilelink.dev" elseif BUILD_ENV == ENV_PRODUCTION then RES_URL_LAN = "http://192.144.239.125:55068/production/"--开发用文件服务器地址 RES_URL = "http://192.144.239.125:55068/" -- cdn 资源服务器 WEAK_RES_URL = "" PACKAGE_NAME = "com.fy.xgame.tilelink" else error("BUILD_ENV:" .. BUILD_ENV) end local branchFile = "svnBranch.txt" SVN_BRANCH_NAME = "linkmatchClient"-- CS.LuaHelper.ReadTextFileInPackage(branchFile) or "trunk"TimerMgr,---@class TimerMgr:LuaStaticClass local TimerMgr = defClassStatic("TimerMgr") local Time = UnityEngine.Time local LOGTAG = "TimerMgr" function TimerMgr:init() local name = "Timer_" .. tostring(self) if self._gameObject or GameObject.Find(name) then return printWarn(LOGTAG, "Timer init only once") end local go = GameObject(name) GameObject.DontDestroyOnLoad(go) Event.add( go, Event.Update, function(...) self:update(...) end ) self._gameObject = go self.curId = 0 self.taskDict = {} self.taskPriorityList = {} self.activeFlag = true self.pauseLevel = 0 self.isDirty = true end function TimerMgr:exit() local go = self._gameObject if go then Event.remove(go) GameObject.Destroy(go) end self._gameObject = nil end function TimerMgr:setActive(bool) self.activeFlag = bool end function TimerMgr:isActive() return self.activeFlag end -- 延迟一帧执行 function TimerMgr:runAtNextFrame(handler) CS.LuaGlobal.instance:runAtNextFrame(handler) end -- 延迟一帧执行 function TimerMgr:runAtEndOfFrame(handler) CS.LuaGlobal.instance:runAtNextFrame(handler) end -- 快速添加一个一直循环的计时器 -- @param handler 回调函数 -- @param interval 执行间隔时间(可选,不传入的话就是每帧都执行) function TimerMgr:addTimerLoop(handler, interval) return self:add(handler, interval, 0, 0, false, nil) end ---增加一个timer, 尽量不要调用,使用 self:timer() ---@param handler fun(dt:number) ---@param interval number|nil nil 的话表示下一帧执行 ---@param loop integer|nil nil的话只执行一次, 0的话表示一直执行 ---@param priority integer 默认是0 ---@param isTimingOnBackground boolean ---@param tailHandler fun() 最后一次的回调 ---@return integer id function TimerMgr:add(handler, interval, loop, priority, isTimingOnBackground, tailHandler) self.isDirty = true self.curId = self.curId + 1 if loop == nil then loop = 1 elseif loop <= 0 then loop = nil end self.taskDict[self.curId] = { id = self.curId, handler = handler, time = interval, curTime = 0, count = loop, curCount = 0, priority = priority or 0, isTimingOnBackground = isTimingOnBackground, tailHandler = tailHandler, } return self.curId end --删除 function TimerMgr:rem(id) if id ~= nil then self.isDirty = true self.taskDict[id] = nil end end function TimerMgr:pause(isForce) if isForce then self.pauseLevel = 1 return end if self.pauseLevel < 0 then self.pauseLevel = 0 end self.pauseLevel = self.pauseLevel + 1 end function TimerMgr:resume(isForce) if isForce then self.pauseLevel = 0 return end self.pauseLevel = self.pauseLevel - 1 if self.pauseLevel < 0 then self.pauseLevel = 0 end end function TimerMgr:update() if not self:isActive() then return end if self.pauseLevel > 0 then return end self.dt = Time.deltaTime self:tryUpdateTaskPriorityList() for i, taskList in ipairs(self.taskPriorityList) do for j, task in ipairs(taskList.list) do self:tryDoTask(task) end end end function TimerMgr:tryUpdateTaskPriorityList() if not self.isDirty then return end self.isDirty = false local taskPriorityDict = {} for i, task in pairs(self.taskDict) do local taskList = taskPriorityDict[task.priority] if not taskList then taskList = {priority = task.priority, list = {}} taskPriorityDict[task.priority] = taskList end table.insert(taskList.list, task) end local taskPriorityList = {} for i, v in pairs(taskPriorityDict) do table.insert(taskPriorityList, v) end table.sort( taskPriorityList, function(a, b) return a.priority > b.priority end ) self.taskPriorityList = taskPriorityList end function TimerMgr:tryDoTask(task) if not task.time then self:tryDoTaskCount(task) return end task.curTime = task.curTime + self.dt local count = math.floor(task.curTime / task.time) if count < 1 then return end for i = 1, count do self:tryDoTaskCount(task) end task.curTime = task.curTime - (task.time * count) end function TimerMgr:tryDoTaskCount(task) local dt = task.time or self.dt if not task.count then task.handler(dt) return end task.curCount = task.curCount + 1 if task.curCount <= task.count then task.handler(dt) end if task.curCount == task.count then self:rem(task.id) if task.tailHandler then task.tailHandler() end end end function TimerMgr:onAppPause() self.enterBackgroundTime = Time.realtimeSinceStartup end function TimerMgr:onAppResume() self.enterBackgroundTime = self.enterBackgroundTime or Time.realtimeSinceStartup local dt = Time.realtimeSinceStartup - self.enterBackgroundTime if dt < 0 then dt = 0 end for _, task in pairs(self.taskDict) do if task.isTimingOnBackground then task.curTime = task.curTime + dt end end end ExtendBoundslocal Bounds = HackCSharpClass(CS.UnityEngine.Bounds) local Vector3 = CS.UnityEngine.Vector3 function Bounds:IntersectsOrContainsX(other) return not((self.min.x > other.max.x) or (self.max.x < other.min.x)) -- return (self.min.x <= other.max.x) and (self.max.x >= other.min.x) enduicircleloadingreslinkreturn { --BASIC --ASSET circle_loading_ui = {"Assets/AssetsPackage/Res/modules/common/ui/circle_loading/circle_loading_ui.prefab", 0, 0}, } SceneMgr--[[ @desc: 场景管理 切换,场景栈的管理,场景事件的管理,场景资源的下载和加载 author:{zhangpeng} time:2023-03-21 20:33:25 ]] local SceneMgr = defClassStatic("SceneMgr") local LOGTAG = SceneMgr.__cls_name SceneMgr.State = { idle = 0, dowload = 1, load = 2 } function SceneMgr:init() self.state = SceneMgr.State.idle self.curScene = nil --在场景资源加载完才赋值 self.curSceneInfo = nil --开始进入场景就赋值 self.lastSceneInfo = nil self.sceneInfoStack = {} Msg.add(Msg.SCENE_PREPARE_LOAD, handler(self, self.onScenePrepareLoad)) Msg.add(Msg.SCENE_BEFORE_LOAD, handler(self, self.onSceneBeforeLoad)) Msg.add(Msg.SCENE_AFTER_LOAD_SCENE, handler(self, self.onSceneAfterLoadScene)) end function SceneMgr:isBusy() return self.state ~= SceneMgr.State.idle end function SceneMgr:setCurScene(scene) self.curScene = scene UILayerUtil:setCurScene(scene) end function SceneMgr:getCurScene() return self.curScene end function SceneMgr:getCurSceneName() local scene = self:getCurScene() if not scene then return end return scene:getName() end function SceneMgr:getCurSceneObj() local scene = self:getCurScene() if not scene then return end return scene:getSceneObj() end function SceneMgr:_setCurSceneInfo(sceneInfo) self.lastSceneInfo = self.curSceneInfo self.curSceneInfo = sceneInfo end function SceneMgr:getCurSceneInfo() return self.curSceneInfo end ---进入下一个场景以后,不把自己压入栈,这样就无法返回到自己 ---@param sceneCfg any ---@param args any ---@param callback fun(suc: boolean) function SceneMgr:enter(sceneCfg, args, callback) local sceneInfo = SceneInfo.new(sceneCfg, args) self:_enterWithInfo(sceneInfo, callback) end -- callback 接受一个bool参数 表示是否成功进入场景 function SceneMgr:_enterWithInfo(sceneInfo, callback) self.afterEnterCallback = callback if self:isBusy() then self:_enterFinish(false) return end self:_doEnter(sceneInfo) end ---@private function SceneMgr:_doEnter(sceneInfo) printInfo(LOGTAG,"SceneMgr:_doEnter") self.state = SceneMgr.State.load self:_setCurSceneInfo(sceneInfo) local path = sceneInfo:getPath() local env = Env.new() local cls = env:require(path) local scene = cls.new(sceneInfo) scene:startLoad() end function SceneMgr:afterEnter(scene) self:setCurScene(scene) if self.needPush then self:pushStack(self.lastSceneInfo) self.needPush = false end if self.needPop then self:popStack() self.needPop = false end self.state = SceneMgr.State.idle self:_enterFinish(true) end function SceneMgr:_enterFinish(result) local callback = self.afterEnterCallback or function() end self.afterEnterCallback = nil self.needPop = false self.needPush = false callback(result) end ---不建议使用 ---@param callback fun(suc: boolean) function SceneMgr:reEnter(enterTransCls, callback) -- 有可能重进场景的时候在暂停状态 if App.isPaused() then App.resume() end local sceneInfo = self:getCurSceneInfo() local initEnterTransCls = sceneInfo:getEnterTransCls() sceneInfo:setEnterTransCls(enterTransCls) self:_enterWithInfo( sceneInfo, function(...) sceneInfo:setEnterTransCls(initEnterTransCls) if callback then callback(...) end end ) end ---进入下一个场景以后,把自己压入栈 ---@param callback fun(suc: boolean) function SceneMgr:pushAndEnter(sceneCfg, args, callback) callback = callback or function() end local sceneInfo = self:getCurSceneInfo() if not sceneInfo then callback(false) return end self.needPush = true self:enter(sceneCfg, args, callback) end ---@private ---@param callback fun(suc: boolean) function SceneMgr:_popAndEnter(callback) callback = callback or function() end local sceneInfo = self:getStackTop() if not sceneInfo then callback(false) return end self.needPop = true self:_enterWithInfo(sceneInfo, callback) end function SceneMgr:getStack() return self.sceneInfoStack end function SceneMgr:pushStack(sceneInfo) table.insert(self.sceneInfoStack, sceneInfo) end function SceneMgr:popStack() return table.remove(self.sceneInfoStack, self:getStackCount()) end function SceneMgr:clearStack() self.sceneInfoStack = {} end function SceneMgr:getStackTop() return self.sceneInfoStack[self:getStackCount()] end function SceneMgr:getStackCount() return #self.sceneInfoStack end function SceneMgr:getStackTopSceneName() local sceneInfo = SceneMgr:getStackTop() if not sceneInfo then return nil end return sceneInfo:getName() end function SceneMgr:getStackTopSceneClassName() local sceneInfo = SceneMgr:getStackTop() if not sceneInfo then return nil end return sceneInfo:getSceneClassName() end function SceneMgr:replaceStackTop(sceneInfo) SceneMgr:popStack() SceneMgr:pushStack(sceneInfo) end ---@param sceneInfo SceneInfo ---@param cond fun(sceneInfo: SceneInfo): boolean function SceneMgr:replaceStackByCondition(sceneInfo, cond) for i = #self.sceneInfoStack, 1, -1 do local scene = self.sceneInfoStack[i] if cond(scene) then table.remove(self.sceneInfoStack, i) table.insert(self.sceneInfoStack, i, sceneInfo) break end end end ---@param scenePath string ---@return boolean function SceneMgr:isStackTopScenePathOf(scenePath) local top = SceneMgr:getStackTop() if not top then return false end return scenePath == top.path end function SceneMgr:hasSceneInStack(sceneInfo) for _, _sceneInfo in ipairs(self.sceneInfoStack) do if _sceneInfo:getPath() == sceneInfo[1] then return true end end return false end function SceneMgr:back() if self:getStackCount() <= 0 then self:enterMainScene() return end self:_popAndEnter() end function SceneMgr:enterMainScene() self:clearStack() App.main() end function SceneMgr:isInScene(sceneCls) local scene = self:getCurScene() if scene and IsInstanceOf(scene, sceneCls) then return true end end function SceneMgr:isInSceneByName(sceneName) local scene = self:getCurScene() if not scene then return false end return scene:getName() == sceneName end function SceneMgr:onScenePrepareLoad() UILayerUtil:onAppPause() UILayerUtil:CloseAllLocal(true) --不销毁ui显示对象,因为场景下一帧才出现,会闪一下 App.disableAllTouches() end function SceneMgr:onSceneBeforeLoad() local lastScene = self:getCurScene() if lastScene then lastScene:exit() self:setCurScene() end end function SceneMgr:onSceneAfterLoadScene(msgid, scene) self:afterEnter(scene) end uitoastreslinkureturn { --BASIC --ASSET toast = {"Assets/AssetsPackage/Res/modules/common/ui/toast/toast.prefab", 0, 0}, } UILoading"--[[ author:{zhangpeng} time:2023-08-01 16:33:43 ]] local UILoading, super = defClass("UILoading", UILayer) local GameObject = CS.UnityEngine.GameObject function UILoading:ctor() super.ctor(self) self.R = Res.loadResLink("framework/ui/uicoms/reslink/uicircleloadingreslink") end function UILoading:onLoad() self.ui = GameObject.Instantiate(self.R.circle_loading_ui) self:addChild(self.ui) self:setPriority(UILayer.UI_ORDER.LOADING) end function UILoading:onExit() super.onExit(self) end return UILoadingCosLuaBatchUploadTask ---@class CosLuaBatchUploadTask:LuaClass local CosLuaBatchUploadTask = defClass("CosLuaBatchUploadTask") local LOGTAG = "CosLuaBatchUploadTask" ---@param files {srcPath:string, dstPath:string}[] ---@param serviceType CosLuaServiceType ---@param progressCallback fun(progress:number, batchUploadTask:CosLuaBatchUploadTask) ---@param oneCompleteCallback fun(result:boolean, data:{errorCode:integer, errorMsg:string, srcPath:string}, batchUploadTask:CosLuaBatchUploadTask, uploadTask:CosLuaUploadTask) ---@param completeCallback fun(result:boolean, data:{sucCount:integer, failCount:integer}, batchUploadTask:CosLuaBatchUploadTask) ---@param prepareUrlCallback fun(originFilePath:string, prepareFileUrl:string) function CosLuaBatchUploadTask:ctor(files, serviceType, progressCallback, oneCompleteCallback, completeCallback, prepareUrlCallback) ---@type CosLuaUploadTask[] self.tasks = {} self.progressCallback = progressCallback self.oneCompleteCallback = oneCompleteCallback self.completeCallback = completeCallback for _, value in ipairs(files) do local task = CosLuaUploadTask.new(value.srcPath, value.dstPath, serviceType, function (progress, srcPath, uploadTask) self:onOneProgress(progress, srcPath, uploadTask) end, function (result, data, uploadTask) self:onOneComplete(result, data, uploadTask) end, prepareUrlCallback) table.insert(self.tasks, task) end end ---comment ---@param uploadTask CosLuaUploadTask ---@param progress number ---@param srcPath string function CosLuaBatchUploadTask:onOneProgress(progress, srcPath, uploadTask) local all = #self.tasks local allProgress = 0 for _, value in ipairs(self.tasks) do allProgress = allProgress + value.progress end local p = allProgress/all if self.progressCallback then self.progressCallback(p, self) end end ---@param result boolean ---@param data {errorCode:integer, errorMsg:string, srcPath:string} ---@param uploadTask CosLuaUploadTask function CosLuaBatchUploadTask:onOneComplete(result, data, uploadTask) if self.oneCompleteCallback then self.oneCompleteCallback(result, data, self, uploadTask) end local sucCount = 0 for _, value in ipairs(self.tasks) do if not value:isFinished() then return end if value:isSuc() then sucCount = sucCount + 1 end end local failCount = #self.tasks - sucCount if self.completeCallback then self.completeCallback(result, {sucCount = sucCount, failCount = failCount}, self) end end function CosLuaBatchUploadTask:cancel() printInfo(LOGTAG, "cancel") for _, value in ipairs(self.tasks) do value:cancel() end end return CosLuaBatchUploadTask mainGrequire("modules/common/login/main") require("modules/setting/main")LuaDebugWlocal debugger_reLoadFile =nil local debugger_xpcall = nil local debugger_stackInfo = nil local coro_debugger = nil local require = rawget(_G,"require") local debugger_require = require local debugger_exeLuaString = nil local checkSetVar = nil local loadstring_ = nil local debugger_sendMsg = nil local _ENV = _G if (loadstring) then loadstring_ = loadstring else loadstring_ = load end --只针对 luadebug 调试 jit版本不存在这个问题 local setfenv = setfenv if (not setfenv) then setfenv = function(fn, env) local i = 1 while true do local name = debug.getupvalue(fn, i) if name == "_ENV" then debug.upvaluejoin( fn, i, (function() return env end), 1 ) break elseif not name then break end i = i + 1 end return fn end end local ZZBase64 = {} local LuaDebugTool_ = nil if (LuaDebugTool) then LuaDebugTool_ = LuaDebugTool elseif (CS and CS.LuaDebugTool) then LuaDebugTool_ = CS.LuaDebugTool end local LuaDebugTool = LuaDebugTool_ local loadstring = loadstring_ local getinfo = debug.getinfo local function createSocket() local base = _G local string = require("string") local math = require("math") local socket = require("socket.core") local _M = socket ----------------------------------------------------------------------------- -- Exported auxiliar functions ----------------------------------------------------------------------------- function _M.connect4(address, port, laddress, lport) return socket.connect(address, port, laddress, lport, "inet") end function _M.connect6(address, port, laddress, lport) return socket.connect(address, port, laddress, lport, "inet6") end if (not _M.connect) then function _M.connect(address, port, laddress, lport) local sock, err = socket.tcp() if not sock then return nil, err end if laddress then local res, err = sock:bind(laddress, lport, -1) if not res then return nil, err end end local res, err = sock:connect(address, port) if not res then return nil, err end return sock end end function _M.bind(host, port, backlog) if host == "*" then host = "0.0.0.0" end local addrinfo, err = socket.dns.getaddrinfo(host) if not addrinfo then return nil, err end local sock, res err = "no info on address" for i, alt in base.ipairs(addrinfo) do if alt.family == "inet" then sock, err = socket.tcp4() else sock, err = socket.tcp6() end if not sock then return nil, err end sock:setoption("reuseaddr", true) res, err = sock:bind(alt.addr, port) if not res then sock:close() else res, err = sock:listen(backlog) if not res then sock:close() else return sock end end end return nil, err end _M.try = _M.newtry() function _M.choose(table) return function(name, opt1, opt2) if base.type(name) ~= "string" then name, opt1, opt2 = "default", name, opt1 end local f = table[name or "nil"] if not f then base.error("unknown key (" .. base.tostring(name) .. ")", 3) else return f(opt1, opt2) end end end ----------------------------------------------------------------------------- -- Socket sources and sinks, conforming to LTN12 ----------------------------------------------------------------------------- -- create namespaces inside LuaSocket namespace local sourcet, sinkt = {}, {} _M.sourcet = sourcet _M.sinkt = sinkt _M.BLOCKSIZE = 2048 sinkt["close-when-done"] = function(sock) return base.setmetatable( { getfd = function() return sock:getfd() end, dirty = function() return sock:dirty() end }, { __call = function(self, chunk, err) if not chunk then sock:close() return 1 else return sock:send(chunk) end end } ) end sinkt["keep-open"] = function(sock) return base.setmetatable( { getfd = function() return sock:getfd() end, dirty = function() return sock:dirty() end }, { __call = function(self, chunk, err) if chunk then return sock:send(chunk) else return 1 end end } ) end sinkt["default"] = sinkt["keep-open"] _M.sink = _M.choose(sinkt) sourcet["by-length"] = function(sock, length) return base.setmetatable( { getfd = function() return sock:getfd() end, dirty = function() return sock:dirty() end }, { __call = function() if length <= 0 then return nil end local size = math.min(socket.BLOCKSIZE, length) local chunk, err = sock:receive(size) if err then return nil, err end length = length - string.len(chunk) return chunk end } ) end sourcet["until-closed"] = function(sock) local done return base.setmetatable( { getfd = function() return sock:getfd() end, dirty = function() return sock:dirty() end }, { __call = function() if done then return nil end local chunk, err, partial = sock:receive(socket.BLOCKSIZE) if not err then return chunk elseif err == "closed" then sock:close() done = 1 return partial else return nil, err end end } ) end sourcet["default"] = sourcet["until-closed"] _M.source = _M.choose(sourcet) return _M end local function createJson() local math = require("math") local string = require("string") local table = require("table") local object = nil ----------------------------------------------------------------------------- -- Module declaration ----------------------------------------------------------------------------- local json = {} -- Public namespace local json_private = {} -- Private namespace -- Public constants json.EMPTY_ARRAY = {} json.EMPTY_OBJECT = {} -- Public functions -- Private functions local decode_scanArray local decode_scanComment local decode_scanConstant local decode_scanNumber local decode_scanObject local decode_scanString local decode_scanWhitespace local encodeString local isArray local isEncodable ----------------------------------------------------------------------------- -- PUBLIC FUNCTIONS ----------------------------------------------------------------------------- --- Encodes an arbitrary Lua object / variable. -- @param v The Lua object / variable to be JSON encoded. -- @return String containing the JSON encoding in internal Lua string format (i.e. not unicode) function json.encode(v) -- Handle nil values if v == nil then return "null" end local vtype = type(v) -- Handle strings if vtype == "string" then return '"' .. json_private.encodeString(v) .. '"' -- Need to handle encoding in string end -- Handle booleans if vtype == "number" or vtype == "boolean" then return tostring(v) end -- Handle tables if vtype == "table" then local rval = {} -- Consider arrays separately local bArray, maxCount = isArray(v) if bArray then for i = 1, maxCount do table.insert(rval, json.encode(v[i])) end else -- An object, not an array for i, j in pairs(v) do if isEncodable(i) and isEncodable(j) then table.insert(rval, '"' .. json_private.encodeString(i) .. '":' .. json.encode(j)) end end end if bArray then return "[" .. table.concat(rval, ",") .. "]" else return "{" .. table.concat(rval, ",") .. "}" end end -- Handle null values if vtype == "function" and v == json.null then return "null" end assert(false, "encode attempt to encode unsupported type " .. vtype .. ":" .. tostring(v)) end --- Decodes a JSON string and returns the decoded value as a Lua data structure / value. -- @param s The string to scan. -- @param [startPos] Optional starting position where the JSON string is located. Defaults to 1. -- @param Lua object, number The object that was scanned, as a Lua table / string / number / boolean or nil, -- and the position of the first character after -- the scanned JSON object. function json.decode(s, startPos) startPos = startPos and startPos or 1 startPos = decode_scanWhitespace(s, startPos) assert(startPos <= string.len(s), "Unterminated JSON encoded object found at position in [" .. s .. "]") local curChar = string.sub(s, startPos, startPos) -- Object if curChar == "{" then return decode_scanObject(s, startPos) end -- Array if curChar == "[" then return decode_scanArray(s, startPos) end -- Number if string.find("+-0123456789.e", curChar, 1, true) then return decode_scanNumber(s, startPos) end -- String if curChar == '"' or curChar == [[']] then return decode_scanString(s, startPos) end if string.sub(s, startPos, startPos + 1) == "/*" then return json.decode(s, decode_scanComment(s, startPos)) end -- Otherwise, it must be a constant return decode_scanConstant(s, startPos) end --- The null function allows one to specify a null value in an associative array (which is otherwise -- discarded if you set the value with 'nil' in Lua. Simply set t = { first=json.null } function json.null() return json.null -- so json.null() will also return null ;-) end ----------------------------------------------------------------------------- -- Internal, PRIVATE functions. -- Following a Python-like convention, I have prefixed all these 'PRIVATE' -- functions with an underscore. ----------------------------------------------------------------------------- --- Scans an array from JSON into a Lua object -- startPos begins at the start of the array. -- Returns the array and the next starting position -- @param s The string being scanned. -- @param startPos The starting position for the scan. -- @return table, int The scanned array as a table, and the position of the next character to scan. function decode_scanArray(s, startPos) local array = {} -- The return value local stringLen = string.len(s) assert( string.sub(s, startPos, startPos) == "[", "decode_scanArray called but array does not start at position " .. startPos .. " in string:\n" .. s ) startPos = startPos + 1 -- Infinite loop for array elements repeat startPos = decode_scanWhitespace(s, startPos) assert(startPos <= stringLen, "JSON String ended unexpectedly scanning array.") local curChar = string.sub(s, startPos, startPos) if (curChar == "]") then return array, startPos + 1 end if (curChar == ",") then startPos = decode_scanWhitespace(s, startPos + 1) end assert(startPos <= stringLen, "JSON String ended unexpectedly scanning array.") object, startPos = json.decode(s, startPos) table.insert(array, object) until false end --- Scans a comment and discards the comment. -- Returns the position of the next character following the comment. -- @param string s The JSON string to scan. -- @param int startPos The starting position of the comment function decode_scanComment(s, startPos) assert( string.sub(s, startPos, startPos + 1) == "/*", "decode_scanComment called but comment does not start at position " .. startPos ) local endPos = string.find(s, "*/", startPos + 2) assert(endPos ~= nil, "Unterminated comment in string at " .. startPos) return endPos + 2 end --- Scans for given constants: true, false or null -- Returns the appropriate Lua type, and the position of the next character to read. -- @param s The string being scanned. -- @param startPos The position in the string at which to start scanning. -- @return object, int The object (true, false or nil) and the position at which the next character should be -- scanned. function decode_scanConstant(s, startPos) local consts = {["true"] = true, ["false"] = false, ["null"] = nil} local constNames = {"true", "false", "null"} for i, k in pairs(constNames) do if string.sub(s, startPos, startPos + string.len(k) - 1) == k then return consts[k], startPos + string.len(k) end end assert(nil, "Failed to scan constant from string " .. s .. " at starting position " .. startPos) end --- Scans a number from the JSON encoded string. -- (in fact, also is able to scan numeric +- eqns, which is not -- in the JSON spec.) -- Returns the number, and the position of the next character -- after the number. -- @param s The string being scanned. -- @param startPos The position at which to start scanning. -- @return number, int The extracted number and the position of the next character to scan. function decode_scanNumber(s, startPos) local endPos = startPos + 1 local stringLen = string.len(s) local acceptableChars = "+-0123456789.e" while (string.find(acceptableChars, string.sub(s, endPos, endPos), 1, true) and endPos <= stringLen) do endPos = endPos + 1 end local stringValue = "return " .. string.sub(s, startPos, endPos - 1) local stringEval = loadstring(stringValue) assert( stringEval, "Failed to scan number [ " .. stringValue .. "] in JSON string at position " .. startPos .. " : " .. endPos ) return stringEval(), endPos end --- Scans a JSON object into a Lua object. -- startPos begins at the start of the object. -- Returns the object and the next starting position. -- @param s The string being scanned. -- @param startPos The starting position of the scan. -- @return table, int The scanned object as a table and the position of the next character to scan. function decode_scanObject(s, startPos) local object = {} local stringLen = string.len(s) local key, value assert( string.sub(s, startPos, startPos) == "{", "decode_scanObject called but object does not start at position " .. startPos .. " in string:\n" .. s ) startPos = startPos + 1 repeat startPos = decode_scanWhitespace(s, startPos) assert(startPos <= stringLen, "JSON string ended unexpectedly while scanning object.") local curChar = string.sub(s, startPos, startPos) if (curChar == "}") then return object, startPos + 1 end if (curChar == ",") then startPos = decode_scanWhitespace(s, startPos + 1) end assert(startPos <= stringLen, "JSON string ended unexpectedly scanning object.") -- Scan the key key, startPos = json.decode(s, startPos) assert(startPos <= stringLen, "JSON string ended unexpectedly searching for value of key " .. key) startPos = decode_scanWhitespace(s, startPos) assert(startPos <= stringLen, "JSON string ended unexpectedly searching for value of key " .. key) assert( string.sub(s, startPos, startPos) == ":", "JSON object key-value assignment mal-formed at " .. startPos ) startPos = decode_scanWhitespace(s, startPos + 1) assert(startPos <= stringLen, "JSON string ended unexpectedly searching for value of key " .. key) value, startPos = json.decode(s, startPos) object[key] = value until false -- infinite loop while key-value pairs are found end -- START SoniEx2 -- Initialize some things used by decode_scanString -- You know, for efficiency local escapeSequences = { ["\\t"] = "\t", ["\\f"] = "\f", ["\\r"] = "\r", ["\\n"] = "\n", ["\\b"] = "" } setmetatable( escapeSequences, { __index = function(t, k) -- skip "\" aka strip escape return string.sub(k, 2) end } ) -- END SoniEx2 --- Scans a JSON string from the opening inverted comma or single quote to the -- end of the string. -- Returns the string extracted as a Lua string, -- and the position of the next non-string character -- (after the closing inverted comma or single quote). -- @param s The string being scanned. -- @param startPos The starting position of the scan. -- @return string, int The extracted string as a Lua string, and the next character to parse. function decode_scanString(s, startPos) assert(startPos, "decode_scanString(..) called without start position") local startChar = string.sub(s, startPos, startPos) -- START SoniEx2 -- PS: I don't think single quotes are valid JSON assert(startChar == '"' or startChar == [[']], "decode_scanString called for a non-string") --assert(startPos, "String decoding failed: missing closing " .. startChar .. " for string at position " .. oldStart) local t = {} local i, j = startPos, startPos while string.find(s, startChar, j + 1) ~= j + 1 do local oldj = j i, j = string.find(s, "\\.", j + 1) local x, y = string.find(s, startChar, oldj + 1) if not i or x < i then i, j = x, y - 1 end table.insert(t, string.sub(s, oldj + 1, i - 1)) if string.sub(s, i, j) == "\\u" then local a = string.sub(s, j + 1, j + 4) j = j + 4 local n = tonumber(a, 16) assert(n, "String decoding failed: bad Unicode escape " .. a .. " at position " .. i .. " : " .. j) -- math.floor(x/2^y) == lazy right shift -- a % 2^b == bitwise_and(a, (2^b)-1) -- 64 = 2^6 -- 4096 = 2^12 (or 2^6 * 2^6) local x if n < 128 then x = string.char(n % 128) elseif n < 2048 then -- [110x xxxx] [10xx xxxx] x = string.char(192 + (math.floor(n / 64) % 32), 128 + (n % 64)) else -- [1110 xxxx] [10xx xxxx] [10xx xxxx] x = string.char(224 + (math.floor(n / 4096) % 16), 128 + (math.floor(n / 64) % 64), 128 + (n % 64)) end table.insert(t, x) else table.insert(t, escapeSequences[string.sub(s, i, j)]) end end table.insert(t, string.sub(j, j + 1)) assert( string.find(s, startChar, j + 1), "String decoding failed: missing closing " .. startChar .. " at position " .. j .. "(for string at position " .. startPos .. ")" ) return table.concat(t, ""), j + 2 -- END SoniEx2 end --- Scans a JSON string skipping all whitespace from the current start position. -- Returns the position of the first non-whitespace character, or nil if the whole end of string is reached. -- @param s The string being scanned -- @param startPos The starting position where we should begin removing whitespace. -- @return int The first position where non-whitespace was encountered, or string.len(s)+1 if the end of string -- was reached. function decode_scanWhitespace(s, startPos) local whitespace = " \n\r\t" local stringLen = string.len(s) while (string.find(whitespace, string.sub(s, startPos, startPos), 1, true) and startPos <= stringLen) do startPos = startPos + 1 end return startPos end --- Encodes a string to be JSON-compatible. -- This just involves back-quoting inverted commas, back-quotes and newlines, I think ;-) -- @param s The string to return as a JSON encoded (i.e. backquoted string) -- @return The string appropriately escaped. local escapeList = { ['"'] = '\\"', ["\\"] = "\\\\", ["/"] = "\\/", [""] = "\\b", ["\f"] = "\\f", ["\n"] = "\\n", ["\r"] = "\\r", ["\t"] = "\\t" } function json_private.encodeString(s) local s = tostring(s) return s:gsub( ".", function(c) return escapeList[c] end ) -- SoniEx2: 5.0 compat end -- Determines whether the given Lua type is an array or a table / dictionary. -- We consider any table an array if it has indexes 1..n for its n items, and no -- other data in the table. -- I think this method is currently a little 'flaky', but can't think of a good way around it yet... -- @param t The table to evaluate as an array -- @return boolean, number True if the table can be represented as an array, false otherwise. If true, -- the second returned value is the maximum -- number of indexed elements in the array. function isArray(t) -- Next we count all the elements, ensuring that any non-indexed elements are not-encodable -- (with the possible exception of 'n') if (t == json.EMPTY_ARRAY) then return true, 0 end if (t == json.EMPTY_OBJECT) then return false end local maxIndex = 0 for k, v in pairs(t) do if (type(k) == "number" and math.floor(k) == k and 1 <= k) then -- k,v is an indexed pair if (not isEncodable(v)) then return false end -- All array elements must be encodable maxIndex = math.max(maxIndex, k) else if (k == "n") then if v ~= (t.n or #t) then return false end -- False if n does not hold the number of elements else -- Else of (k=='n') if isEncodable(v) then return false end end -- End of (k~='n') end -- End of k,v not an indexed pair end -- End of loop across all pairs return true, maxIndex end --- Determines whether the given Lua object / table / variable can be JSON encoded. The only -- types that are JSON encodable are: string, boolean, number, nil, table and json.null. -- In this implementation, all other types are ignored. -- @param o The object to examine. -- @return boolean True if the object should be JSON encoded, false if it should be ignored. function isEncodable(o) local t = type(o) return (t == "string" or t == "boolean" or t == "number" or t == "nil" or t == "table") or (t == "function" and o == json.null) end return json end local debugger_print = print local debug_server = nil local breakInfoSocket = nil local json = createJson() local LuaDebugger = { fileMaps = {}, Run = true, --表示正常运行只检测断点 StepIn = false, StepInLevel = 0, StepNext = false, StepNextLevel = 0, StepOut = false, breakInfos = {}, runTimeType = nil, isHook = true, pathCachePaths = {}, isProntToConsole = 1, isFoxGloryProject = false, isDebugPrint = true, hookType = "lrc", currentFileName = "", currentTempFunc = nil, --分割字符串缓存 splitFilePaths = {}, DebugLuaFie = "", version = "0.9.3", serVarLevel = 4 } local debug_hook = nil local _resume = coroutine.resume coroutine.resume = function(co, ...) if (LuaDebugger.isHook) then if coroutine.status(co) ~= "dead" then debug.sethook(co, debug_hook, "lrc") end end return _resume(co, ...) end local _wrap = coroutine.wrap coroutine.wrap = function(fun,dd) local newFun =_wrap(function() debug.sethook(debug_hook, "lrc") return fun(); end) return newFun end LuaDebugger.event = { S2C_SetBreakPoints = 1, C2S_SetBreakPoints = 2, S2C_RUN = 3, C2S_HITBreakPoint = 4, S2C_ReqVar = 5, C2S_ReqVar = 6, --单步跳过请求 S2C_NextRequest = 7, --单步跳过反馈 C2S_NextResponse = 8, -- 单步跳过 结束 没有下一步 C2S_NextResponseOver = 9, --单步跳入 S2C_StepInRequest = 10, C2S_StepInResponse = 11, --单步跳出 S2C_StepOutRequest = 12, --单步跳出返回 C2S_StepOutResponse = 13, --打印 C2S_LuaPrint = 14, S2C_LoadLuaScript = 16, C2S_SetSocketName = 17, C2S_LoadLuaScript = 18, C2S_DebugXpCall = 20, S2C_DebugClose = 21, S2C_SerVar = 24, C2S_SerVar = 25, S2C_ReLoadFile = 26, C2S_ReLoadFile = 27, } --@region print function print(...) if (LuaDebugger.isProntToConsole == 1 or LuaDebugger.isProntToConsole == 3) then debugger_print(...) end if (LuaDebugger.isProntToConsole == 1 or LuaDebugger.isProntToConsole == 2) then if (debug_server) then local arg = {...} --这里的...和{}符号中间需要有空格号,否则会出错 local str = "" if (#arg == 0) then arg = {"nil"} end for k, v in pairs(arg) do str = str .. tostring(v) .. "\t" end local sendMsg = { event = LuaDebugger.event.C2S_LuaPrint, data = {msg = ZZBase64.encode(str), type = 1} } local sendStr = json.encode(sendMsg) debug_server:send(sendStr .. "__debugger_k0204__") end end end function luaIdePrintWarn(...) if (LuaDebugger.isProntToConsole == 1 or LuaDebugger.isProntToConsole == 3) then debugger_print(...) end if (LuaDebugger.isProntToConsole == 1 or LuaDebugger.isProntToConsole == 2) then if (debug_server) then local arg = {...} --这里的...和{}符号中间需要有空格号,否则会出错 local str = "" if (#arg == 0) then arg = {"nil"} end for k, v in pairs(arg) do str = str .. tostring(v) .. "\t" end local sendMsg = { event = LuaDebugger.event.C2S_LuaPrint, data = {msg = ZZBase64.encode(str), type = 2} } local sendStr = json.encode(sendMsg) debug_server:send(sendStr .. "__debugger_k0204__") end end end function luaIdePrintErr(...) if (LuaDebugger.isProntToConsole == 1 or LuaDebugger.isProntToConsole == 3) then debugger_print(...) end if (LuaDebugger.isProntToConsole == 1 or LuaDebugger.isProntToConsole == 2) then if (debug_server) then local arg = {...} --这里的...和{}符号中间需要有空格号,否则会出错 local str = "" if (#arg == 0) then arg = {"nil"} end for k, v in pairs(arg) do str = str .. tostring(v) .. "\t" end local sendMsg = { event = LuaDebugger.event.C2S_LuaPrint, data = {msg = ZZBase64.encode(str), type = 3} } local sendStr = json.encode(sendMsg) debug_server:send(sendStr .. "__debugger_k0204__") end end end --@endregion --@region 辅助方法 local function debugger_lastIndex(str, p) local startIndex = string.find(str, p, 1) while startIndex do local findstartIndex = string.find(str, p, startIndex + 1) if (not findstartIndex) then break else startIndex = findstartIndex end end return startIndex end local function debugger_convertParentDir(dir) local index, endindex = string.find(dir, "/%.%./") if (index) then local file1 = string.sub(dir, 1, index - 1) local startIndex = debugger_lastIndex(file1, "/") file1 = string.sub(file1, 1, startIndex - 1) local file2 = string.sub(dir, endindex) dir = file1 .. file2 dir = debugger_convertParentDir(dir) return dir else return dir end end local function debugger_getFilePathInfo(file) local fileName = nil local dir = nil file = file:gsub("/.\\", "/") file = file:gsub("\\", "/") file = file:gsub("//", "/") if file:find("@") == 1 then file = file:sub(2) end local findex = file:find("%./") if (findex == 1) then file = file:sub(3) end file = debugger_convertParentDir(file) local fileLength = string.len(file) local suffixNames = { ".lua", ".lua.txt", ".txt", ".bytes" } table.sort( suffixNames, function(name1, name2) return string.len(name1) > string.len(name2) end ) local suffixLengs = {} for i, suffixName in ipairs(suffixNames) do table.insert(suffixLengs, string.len(suffixName)) end local fileLength = string.len(file) for i, suffix in ipairs(suffixNames) do local suffixName = string.sub(file, fileLength - suffixLengs[i] + 1) if (suffixName == suffix) then file = string.sub(file, 1, fileLength - suffixLengs[i]) break end end local fileNameStartIndex = debugger_lastIndex(file, "/") if (fileNameStartIndex) then fileName = string.sub(file, fileNameStartIndex + 1) dir = string.sub(file, 1, fileNameStartIndex) file = dir .. fileName else fileNameStartIndex = debugger_lastIndex(file, "%.") if (not fileNameStartIndex) then fileName = file dir = "" else dir = string.sub(file, 1, fileNameStartIndex) dir = dir:gsub("%.", "/") fileName = string.sub(file, fileNameStartIndex + 1) file = dir .. fileName end end return file, dir, fileName end --@endregion ----=============================工具方法============================================= --@region 工具方法 local function debugger_strSplit(input, delimiter) input = tostring(input) delimiter = tostring(delimiter) if (delimiter == "") then return false end local pos, arr = 0, {} -- for each divider found for st, sp in function() return string.find(input, delimiter, pos, true) end do table.insert(arr, string.sub(input, pos, st - 1)) pos = sp + 1 end table.insert(arr, string.sub(input, pos)) return arr end local function debugger_strTrim(input) input = string.gsub(input, "^[ \t\n\r]+", "") return string.gsub(input, "[ \t\n\r]+$", "") end local function debugger_dump(value, desciption, nesting) if type(nesting) ~= "number" then nesting = 3 end local lookupTable = {} local result = {} local function _v(v) if type(v) == "string" then v = '"' .. v .. '"' end return tostring(v) end local traceback = debugger_strSplit(debug.traceback("", 2), "\n") print("dump from: " .. debugger_strTrim(traceback[3])) local function _dump(value, desciption, indent, nest, keylen) desciption = desciption or "" local spc = "" if type(keylen) == "number" then spc = string.rep(" ", keylen - string.len(_v(desciption))) end if type(value) ~= "table" then result[#result + 1] = string.format("%s%s%s = %s", indent, _v(desciption), spc, _v(value)) elseif lookupTable[value] then result[#result + 1] = string.format("%s%s%s = *REF*", indent, desciption, spc) else lookupTable[value] = true if nest > nesting then result[#result + 1] = string.format("%s%s = *MAX NESTING*", indent, desciption) else result[#result + 1] = string.format("%s%s = {", indent, _v(desciption)) local indent2 = indent .. " " local keys = {} local keylen = 0 local values = {} for k, v in pairs(value) do keys[#keys + 1] = k local vk = _v(k) local vkl = string.len(vk) if vkl > keylen then keylen = vkl end values[k] = v end table.sort( keys, function(a, b) if type(a) == "number" and type(b) == "number" then return a < b else return tostring(a) < tostring(b) end end ) for i, k in ipairs(keys) do _dump(values[k], k, indent2, nest + 1, keylen) end result[#result + 1] = string.format("%s}", indent) end end end _dump(value, desciption, "- ", 1) for i, line in ipairs(result) do print(line) end end --@endregion local function debugger_valueToString(v) local vtype = type(v) local vstr = nil if (vtype == "userdata") then if (LuaDebugger.isFoxGloryProject) then return "userdata",vtype else return tostring(v), vtype end elseif (vtype == "table" or vtype == "function" or vtype == "boolean") then local value = vtype xpcall(function() value = tostring(v) end,function() value = vtype end) return value, vtype elseif (vtype == "number" or vtype == "string" ) then return v, vtype else return tostring(v), vtype end end local function debugger_setVarInfo(name, value) local valueStr, valueType = debugger_valueToString(value) local nameStr,nameType = debugger_valueToString(name) if(valueStr == nil) then valueStr = valueType end local valueInfo = { name =nameStr, valueType = valueType, valueStr = ZZBase64.encode(valueStr) } return valueInfo end local function debugger_getvalue(f) local i = 1 local locals = {} -- get locals while true do local name, value = debug.getlocal(f, i) if not name then break end if (name ~= "(*temporary)") then locals[name] = value end i = i + 1 end local func = getinfo(f, "f").func i = 1 local ups = {} while func do -- check for func as it may be nil for tail calls local name, value = debug.getupvalue(func, i) if not name then break end if (name == "_ENV") then ups["_ENV_"] = value else ups[name] = value end i = i + 1 end return {locals = locals, ups = ups} end --获取堆栈 debugger_stackInfo = function(ignoreCount, event) local datas = {} local stack = {} local varInfos = {} local funcs = {} local index = 0 for i = ignoreCount, 100 do local source = getinfo(i) local isadd = true if (i == ignoreCount) then local file = source.source if (file:find(LuaDebugger.DebugLuaFie)) then return end if (file == "=[C]") then isadd = false end end if not source then break end if (isadd) then local fullName, dir, fileName = debugger_getFilePathInfo(source.source) local info = { src = fullName, scoreName = source.name, currentline = source.currentline, linedefined = source.linedefined, what = source.what, nameWhat = source.namewhat } index = i local vars = debugger_getvalue(i + 1) table.insert(stack, info) table.insert(varInfos, vars) table.insert(funcs, source.func) end if source.what == "main" then break end end local stackInfo = {stack = stack, vars = varInfos, funcs = funcs} local data = { stack = stackInfo.stack, vars = stackInfo.vars, funcs = stackInfo.funcs, event = event, funcsLength = #stackInfo.funcs, upFunc = getinfo(ignoreCount - 3, "f").func } LuaDebugger.currentTempFunc = data.funcs[1] return data end --===========================点断信息================================================== --根据不同的游戏引擎进行定时获取断点信息 --CCDirector:sharedDirector():getScheduler() local debugger_setBreak = nil local function debugger_receiveDebugBreakInfo() if (jit) then if (LuaDebugger.debugLuaType ~= "jit") then local msg = "当前luajit版本为: " .. jit.version .. " 请使用LuaDebugjit 进行调试!" print(msg) end end if (breakInfoSocket) then local msg, status = breakInfoSocket:receive() if(LuaDebugger.isLaunch and status == "closed") then os.exit() end if (msg) then local netData = json.decode(msg) if netData.event == LuaDebugger.event.S2C_SetBreakPoints then debugger_setBreak(netData.data) elseif netData.event == LuaDebugger.event.S2C_LoadLuaScript then LuaDebugger.loadScriptBody = netData.data debugger_exeLuaString() debugger_sendMsg(breakInfoSocket,LuaDebugger.event.C2S_LoadLuaScript,LuaDebugger.loadScriptBody) elseif netData.event == LuaDebugger.event.S2C_ReLoadFile then LuaDebugger.reLoadFileBody = netData.data LuaDebugger.isReLoadFile = false LuaDebugger.reLoadFileBody.isReLoad = debugger_reLoadFile(LuaDebugger.reLoadFileBody) print("重载结果:",LuaDebugger.reLoadFileBody.isReLoad) LuaDebugger.reLoadFileBody.script = nil debugger_sendMsg( breakInfoSocket, LuaDebugger.event.C2S_ReLoadFile, { stack = LuaDebugger.reLoadFileBody } ) end end end end local function splitFilePath(path) if (LuaDebugger.splitFilePaths[path]) then return LuaDebugger.splitFilePaths[path] end local pos, arr = 0, {} -- for each divider found for st, sp in function() return string.find(path, "/", pos, true) end do local pathStr = string.sub(path, pos, st - 1) table.insert(arr, pathStr) pos = sp + 1 end local pathStr = string.sub(path, pos) table.insert(arr, pathStr) LuaDebugger.splitFilePaths[path] = arr return arr end debugger_setBreak = function(datas) local breakInfos = LuaDebugger.breakInfos for i, data in ipairs(datas) do data.fileName = string.lower(data.fileName) data.serverPath = string.lower(data.serverPath) local breakInfo = breakInfos[data.fileName] if (not breakInfo) then breakInfos[data.fileName] = {} breakInfo = breakInfos[data.fileName] end if (not data.breakDatas or #data.breakDatas == 0) then breakInfo[data.serverPath] = nil else local fileBreakInfo = breakInfo[data.serverPath] if (not fileBreakInfo) then fileBreakInfo = { pathNames = splitFilePath(data.serverPath), --命中次數判斷計數器 hitCounts = {} } breakInfo[data.serverPath] = fileBreakInfo end local lineInfos = {} for li, breakData in ipairs(data.breakDatas) do lineInfos[breakData.line] = breakData if (breakData.hitCondition and breakData.hitCondition ~= "") then breakData.hitCondition = tonumber(breakData.hitCondition) else breakData.hitCondition = 0 end if (not fileBreakInfo.hitCounts[breakData.line]) then fileBreakInfo.hitCounts[breakData.line] = 0 end end fileBreakInfo.lines = lineInfos --這裡添加命中次數判斷 for line, count in pairs(fileBreakInfo.hitCounts) do if (not lineInfos[line]) then fileBreakInfo.hitCounts[line] = nil end end end local count = 0 for i, linesInfo in pairs(breakInfo) do count = count + 1 end if (count == 0) then breakInfos[data.fileName] = nil end end --debugger_dump(breakInfos, "breakInfos", 6) --检查是否需要断点 local isHook = false for k, v in pairs(breakInfos) do isHook = true break end --这样做的原因是为了最大限度的使手机调试更加流畅 注意这里会连续的进行n次 if (isHook) then if (not LuaDebugger.isHook) then debug.sethook(debug_hook, "lrc") end LuaDebugger.isHook = true else if (LuaDebugger.isHook) then debug.sethook() end LuaDebugger.isHook = false end end local function debugger_checkFileIsBreak(fileName) return LuaDebugger.breakInfos[fileName] end --=====================================断点信息 end ---------------------------------------------- local controller_host = "192.168.1.102" local controller_port = 7003 debugger_sendMsg = function(serverSocket, eventName, data) local sendMsg = { event = eventName, data = data } local sendStr = json.encode(sendMsg) serverSocket:send(sendStr .. "__debugger_k0204__") end function debugger_conditionStr(condition, vars, callBack) local function loadScript() local currentTabble = {} local locals = vars[1].locals local ups = vars[1].ups if (ups) then for k, v in pairs(ups) do currentTabble[k] = v end end if (locals) then for k, v in pairs(locals) do currentTabble[k] = v end end setmetatable(currentTabble, {__index = _G}) local fun = loadstring("return " .. condition) setfenv(fun, currentTabble) return fun() end local status, msg = xpcall( loadScript, function(error) print(error) end ) if (status and msg) then callBack() end end --执行lua字符串 debugger_exeLuaString = function() local function loadScript() local script = LuaDebugger.loadScriptBody.script if (LuaDebugger.loadScriptBody.isBreak) then local currentTabble = {_G = _G} local frameId = LuaDebugger.loadScriptBody.frameId frameId = frameId local func = LuaDebugger.currentDebuggerData.funcs[frameId] local vars = LuaDebugger.currentDebuggerData.vars[frameId] local locals = vars.locals local ups = vars.ups for k, v in pairs(ups) do currentTabble[k] = v end for k, v in pairs(locals) do currentTabble[k] = v end setmetatable(currentTabble, {__index = _G}) local fun = loadstring(script) setfenv(fun, currentTabble) fun() else local fun = loadstring(script) fun() end end local status, msg = xpcall( loadScript, function(error) -- debugger_sendMsg(debug_server, LuaDebugger.event.C2S_LoadLuaScript, LuaDebugger.loadScriptBody) end ) LuaDebugger.loadScriptBody.script = nil if (LuaDebugger.loadScriptBody.isBreak) then LuaDebugger.serVarLevel = LuaDebugger.serVarLevel+1 LuaDebugger.currentDebuggerData = debugger_stackInfo(LuaDebugger.serVarLevel, LuaDebugger.event.C2S_HITBreakPoint) LuaDebugger.loadScriptBody.stack = LuaDebugger.currentDebuggerData.stack end LuaDebugger.loadScriptBody.complete = true end --@region 调试中修改变量值 --根据key 值在 value 查找 local function debugger_getTablekey(key,keyType,value) if(keyType == -1) then return key elseif(keyType == 1) then return tonumber(key) elseif(keyType == 2) then local valueKey = nil for k,v in pairs(value) do local nameType = type(k) if(nameType == "userdata" or nameType == "table") then if (not LuaDebugger.isFoxGloryProject) then valueKey = tostring(k) if(key == valueKey) then return k end break end end end end end local function debugger_setVarValue(server, data) local newValue = nil local level = LuaDebugger.serVarLevel+LuaDebugger.setVarBody.frameId local firstKeyName = data.keys[1] --@region vars check local localValueChangeIndex = -1 local upValueChangeIndex = -1 local upValueFun = nil local oldValue = nil local i = 1 local locals = {} -- get locals while true do local name, value = debug.getlocal(level, i) if not name then break end if(firstKeyName == name) then localValueChangeIndex = i oldValue = value end if (name ~= "(*temporary)") then locals[name] = value end i = i + 1 end local func = getinfo(level, "f").func i = 1 local ups = {} while func do -- check for func as it may be nil for tail calls local name, value = debug.getupvalue(func, i) if not name then break end if(localValueChangeIndex == -1 and firstKeyName == name) then upValueFun = func oldValue = value upValueChangeIndex = i end if (name == "_ENV") then ups["_ENV_"] = value else ups[name] = value end i = i + 1 end --@endregion local vars = {locals = locals, ups = ups} local function loadScript() local currentTabble = {} local locals = vars.locals local ups = vars.ups if (ups) then for k, v in pairs(ups) do currentTabble[k] = v end end if (locals) then for k, v in pairs(locals) do currentTabble[k] = v end end setmetatable(currentTabble, {__index = _G}) local fun = loadstring("return " .. data.value) setfenv(fun, currentTabble) newValue = fun() end local status, msg = xpcall( loadScript, function(error) print(error, "============================") end ) local i = 1 -- local 查找并替换 local keyLength = #data.keys if(keyLength == 1) then if(localValueChangeIndex ~= -1) then debug.setlocal(level, localValueChangeIndex, newValue) elseif(upValueFun ~= nil) then debug.setupvalue( upValueFun, upValueChangeIndex, newValue ) else --全局变量查找 if(_G[firstKeyName]) then _G[firstKeyName] = newValue end end else if(not oldValue) then if(_G[firstKeyName]) then oldValue = _G[firstKeyName] end end local tempValue = oldValue for i=2,keyLength-1 do if(tempValue) then oldValue = oldValue[debugger_getTablekey(data.keys[i],data.numberTypes[i],oldValue)] end end if(tempValue) then oldValue[debugger_getTablekey(data.keys[keyLength],data.numberTypes[keyLength],oldValue)] = newValue end end local varInfo = debugger_setVarInfo(data.varName, newValue) data.varInfo = varInfo LuaDebugger.serVarLevel = LuaDebugger.serVarLevel+1 LuaDebugger.currentDebuggerData = debugger_stackInfo(LuaDebugger.serVarLevel, LuaDebugger.event.C2S_HITBreakPoint) end --@endregion --调试修改变量值统一的 _resume checkSetVar = function() if (LuaDebugger.isSetVar) then LuaDebugger.isSetVar = false debugger_setVarValue(debug_server,LuaDebugger.setVarBody) LuaDebugger.serVarLevel = LuaDebugger.serVarLevel+1 _resume(coro_debugger, LuaDebugger.setVarBody) xpcall( checkSetVar, function(error) print("设置变量", error) end ) elseif(LuaDebugger.isLoadLuaScript) then LuaDebugger.isLoadLuaScript = false debugger_exeLuaString() LuaDebugger.serVarLevel = LuaDebugger.serVarLevel+1 _resume(coro_debugger, LuaDebugger.reLoadFileBody) xpcall( checkSetVar, function(error) print("执行代码", error) end ) elseif(LuaDebugger.isReLoadFile) then LuaDebugger.isReLoadFile = false LuaDebugger.reLoadFileBody.isReLoad = debugger_reLoadFile(LuaDebugger.reLoadFileBody) print("重载结果:",LuaDebugger.reLoadFileBody.isReLoad) LuaDebugger.reLoadFileBody.script = nil LuaDebugger.serVarLevel = LuaDebugger.serVarLevel+1 _resume(coro_debugger, LuaDebugger.reLoadFileBody) xpcall( checkSetVar, function(error) print("重新加载文件", error) end ) end end local function getSource(source) source = string.lower(source) if (LuaDebugger.pathCachePaths[source]) then LuaDebugger.currentLineFile = LuaDebugger.pathCachePaths[source] return LuaDebugger.pathCachePaths[source] end local fullName, dir, fileName = debugger_getFilePathInfo(source) LuaDebugger.currentLineFile = fullName LuaDebugger.pathCachePaths[source] = fileName return fileName end local function debugger_GeVarInfoBytUserData(server, var) local fileds = LuaDebugTool.getUserDataInfo(var) local varInfos = {} --c# vars for i = 1, fileds.Count do local filed = fileds[i - 1] local valueInfo = { name = filed.name, valueType = filed.valueType, valueStr = ZZBase64.encode(filed.valueStr), isValue = filed.isValue, csharp = true } table.insert(varInfos, valueInfo) end return varInfos end local function debugger_getValueByScript(value, script) local val = nil local status, msg = xpcall( function() local fun = loadstring("return " .. script) setfenv(fun, value) val = fun() end, function(error) print(error, "====>") val = nil end ) return val end local function debugger_getVarByKeys(value, keys, index) local str = "" local keyLength = #keys for i = index, keyLength do local key = keys[i] if (key == "[metatable]") then else if (i == index) then if (string.find(key, "%.")) then if (str == "") then i = index + 1 value = value[key] end if (i >= #keys) then return index, value end return debugger_getVarByKeys(value, keys, i) else str = key end else if (string.find(key, "%[")) then str = str .. key elseif (type(key) == "string") then if (string.find(key, "table:") or string.find(key, "userdata:") or string.find(key, "function:")) then if (str ~= "") then local vl = debugger_getValueByScript(value, str) value = vl if (value) then for k, v in pairs(value) do local ktype = type(k) if (ktype == "userdata" or ktype == "table" or ktype == "function") then local keyName = debugger_valueToString(k) if (keyName == key) then value = v break end end end end str = "" if (i == keyLength) then return #keys, value else return debugger_getVarByKeys(value, keys, i + 1) end else str = str .. '["' .. key .. '"]' end else str = str .. '["' .. key .. '"]' end else str = str .. "[" .. key .. "]" end end end end local v = debugger_getValueByScript(value, str) return #keys, v end --[[ @desc: 查找c# 值 author:k0204 time:2018-04-07 21:32:31 return ]] local function debugger_getCSharpValue(value, searchIndex, keys) local key = keys[searchIndex] local val = LuaDebugTool.getCSharpValue(value, key) if (val) then --1最后一个 直接返回 if (searchIndex == #keys) then return #keys, val else --2再次获得 如果没有找到那么 进行lua 层面查找 local vindex, val1 = debugger_getCSharpValue(val, searchIndex + 1, keys) if (not val1) then --组建新的keys local tempKeys = {} for i = vindex, #keys do table.insert(tempKeys, keys[i]) end local vindx, val1 = debugger_searchVarByKeys(value, searckKeys, 1) return vindx, val1 else return vindex, val1 end end else --3最终这里返回 所以2 中 没有当val1 不为空的处理 return searchIndex, val end end local function debugger_searchVarByKeys(value, keys, searckKeys) local index, val = debugger_getVarByKeys(value, searckKeys, 1) if (not LuaDebugTool or not LuaDebugTool.getCSharpValue or type(LuaDebugTool.getCSharpValue) ~= "function") then return index, val end if (val) then if (index == #keys) then return index, val else local searchStr = "" --进行c# 值查找 local keysLength = #keys local searchIndex = index + 1 local sindex, val = debugger_getCSharpValue(val, searchIndex, keys) return sindex, val end else --进行递减 local tempKeys = {} for i = 1, #searckKeys - 1 do table.insert(tempKeys, keys[i]) end if (#tempKeys == 0) then return #keys, nil end return debugger_searchVarByKeys(value, keys, tempKeys) end end --[[ @desc: 获取metatable 信息 author:k0204 time:2018-04-06 20:27:12 return ]] local function debugger_getmetatable(value, metatable, vinfos, server, variablesReference, debugSpeedIndex, metatables) for i, mtable in ipairs(metatables) do if (metatable == mtable) then return vinfos end end table.insert(metatables, metatable) for k, v in pairs(metatable) do local val = nil if (type(k) == "string") then xpcall( function() val = value[k] end, function(error) val = nil end ) if (val == nil) then xpcall( function() if (string.find(k, "__")) then val = v end end, function(error) val = nil end ) end end if (val) then local vinfo = debugger_setVarInfo(k, val) table.insert(vinfos, vinfo) if (#vinfos > 10) then debugger_sendMsg( server, LuaDebugger.event.C2S_ReqVar, { variablesReference = variablesReference, debugSpeedIndex = debugSpeedIndex, vars = vinfos, isComplete = 0 } ) vinfos = {} end end end local m = getmetatable(metatable) if (m) then return debugger_getmetatable(value, m, vinfos, server, variablesReference, debugSpeedIndex, metatables) else return vinfos end end local function debugger_sendTableField(luatable, vinfos, server, variablesReference, debugSpeedIndex, valueType) if (valueType == "userdata") then if (tolua and tolua.getpeer) then luatable = tolua.getpeer(luatable) else return vinfos end end if (luatable == nil) then return vinfos end for k, v in pairs(luatable) do local vinfo = debugger_setVarInfo(k, v) table.insert(vinfos, vinfo) if (#vinfos > 10) then debugger_sendMsg( server, LuaDebugger.event.C2S_ReqVar, { variablesReference = variablesReference, debugSpeedIndex = debugSpeedIndex, vars = vinfos, isComplete = 0 } ) vinfos = {} end end return vinfos end local function debugger_sendTableValues(value, server, variablesReference, debugSpeedIndex) local vinfos = {} local luatable = {} local valueType = type(value) local userDataInfos = {} local m = nil if (valueType == "userdata") then m = getmetatable(value) vinfos = debugger_sendTableField(value, vinfos, server, variablesReference, debugSpeedIndex, valueType) if (LuaDebugTool) then local varInfos = debugger_GeVarInfoBytUserData(server, value, variablesReference, debugSpeedIndex) for i, v in ipairs(varInfos) do if (v.valueType == "System.Byte[]" and value[v.name] and type(value[v.name]) == "string") then local valueInfo = { name = v.name, valueType = "string", valueStr = ZZBase64.encode(value[v.name]) } table.insert(vinfos, valueInfo) else table.insert(vinfos, v) end if (#vinfos > 10) then debugger_sendMsg( server, LuaDebugger.event.C2S_ReqVar, { variablesReference = variablesReference, debugSpeedIndex = debugSpeedIndex, vars = vinfos, isComplete = 0 } ) vinfos = {} end end end else m = getmetatable(value) vinfos = debugger_sendTableField(value, vinfos, server, variablesReference, debugSpeedIndex, valueType) end if (m) then vinfos = debugger_getmetatable(value, m, vinfos, server, variablesReference, debugSpeedIndex, {}) end debugger_sendMsg( server, LuaDebugger.event.C2S_ReqVar, { variablesReference = variablesReference, debugSpeedIndex = debugSpeedIndex, vars = vinfos, isComplete = 1 } ) end --获取lua 变量的方法 local function debugger_getBreakVar(body, server) local variablesReference = body.variablesReference local debugSpeedIndex = body.debugSpeedIndex local vinfos = {} local function exe() local frameId = body.frameId local type_ = body.type local keys = body.keys --找到对应的var local vars = nil if (type_ == 1) then vars = LuaDebugger.currentDebuggerData.vars[frameId + 1] vars = vars.locals elseif (type_ == 2) then vars = LuaDebugger.currentDebuggerData.vars[frameId + 1] vars = vars.ups elseif (type_ == 3) then vars = _G end if (#keys == 0) then debugger_sendTableValues(vars, server, variablesReference, debugSpeedIndex) return end local index, value = debugger_searchVarByKeys(vars, keys, keys) if (value) then local valueType = type(value) if (valueType == "table" or valueType == "userdata") then debugger_sendTableValues(value, server, variablesReference, debugSpeedIndex) else if (valueType == "function") then value = tostring(value) end debugger_sendMsg( server, LuaDebugger.event.C2S_ReqVar, { variablesReference = variablesReference, debugSpeedIndex = debugSpeedIndex, vars = ZZBase64.encode(value), isComplete = 1, varType = valueType } ) end else debugger_sendMsg( server, LuaDebugger.event.C2S_ReqVar, { variablesReference = variablesReference, debugSpeedIndex = debugSpeedIndex, vars = {}, isComplete = 1, varType = "nil" } ) end end xpcall( exe, function(error) -- print("获取变量错误 错误消息-----------------") -- print(error) -- print(debug.traceback("", 2)) debugger_sendMsg( server, LuaDebugger.event.C2S_ReqVar, { variablesReference = variablesReference, debugSpeedIndex = debugSpeedIndex, vars = { { name = "error", valueType = "string", valueStr = ZZBase64.encode("无法获取属性值:" .. error .. "->" .. debug.traceback("", 2)), isValue = false } }, isComplete = 1 } ) end ) end local function ResetDebugInfo() LuaDebugger.Run = false LuaDebugger.StepIn = false LuaDebugger.StepNext = false LuaDebugger.StepOut = false LuaDebugger.StepNextLevel = 0 end local function debugger_loop(server) server = debug_server --命令 local command local eval_env = {} local arg while true do local line, status = server:receive() if (status == "closed") then if(LuaDebugger.isLaunch) then os.exit() else debug.sethook() coroutine.yield() end end if (line) then local netData = json.decode(line) local event = netData.event local body = netData.data if (event == LuaDebugger.event.S2C_DebugClose) then if(LuaDebugger.isLaunch) then os.exit() else debug.sethook() coroutine.yield() end elseif event == LuaDebugger.event.S2C_SetBreakPoints then --设置断点信息 local function setB() debugger_setBreak(body) end xpcall( setB, function(error) print(error) end ) elseif event == LuaDebugger.event.S2C_RUN then --开始运行 LuaDebugger.runTimeType = body.runTimeType LuaDebugger.isProntToConsole = body.isProntToConsole LuaDebugger.isFoxGloryProject = body.isFoxGloryProject LuaDebugger.isLaunch = body.isLaunch ResetDebugInfo() LuaDebugger.Run = true local data = coroutine.yield() LuaDebugger.serVarLevel = 4 LuaDebugger.currentDebuggerData = data debugger_sendMsg( server, data.event, { stack = data.stack } ) elseif event == LuaDebugger.event.S2C_ReqVar then -- 获取变量信息 --请求数据信息 debugger_getBreakVar(body, server) elseif event == LuaDebugger.event.S2C_NextRequest then -- 设置单步跳过 ResetDebugInfo() LuaDebugger.StepNext = true LuaDebugger.StepNextLevel = 0 --设置当前文件名和当前行数 local data = coroutine.yield() LuaDebugger.serVarLevel = 4 --重置调试信息 LuaDebugger.currentDebuggerData = data debugger_sendMsg( server, data.event, { stack = data.stack } ) elseif (event == LuaDebugger.event.S2C_StepInRequest) then --单步跳入 --单步跳入 ResetDebugInfo() LuaDebugger.StepIn = true local data = coroutine.yield() LuaDebugger.serVarLevel = 4 --重置调试信息 LuaDebugger.currentDebuggerData = data debugger_sendMsg( server, data.event, { stack = data.stack, eventType = data.eventType } ) elseif (event == LuaDebugger.event.S2C_StepOutRequest) then --单步跳出 ResetDebugInfo() LuaDebugger.StepOut = true local data = coroutine.yield() LuaDebugger.serVarLevel = 4 --重置调试信息 LuaDebugger.currentDebuggerData = data debugger_sendMsg( server, data.event, { stack = data.stack, eventType = data.eventType } ) elseif event == LuaDebugger.event.S2C_LoadLuaScript then LuaDebugger.loadScriptBody = body LuaDebugger.isLoadLuaScript = true local data = coroutine.yield() debugger_sendMsg( server, LuaDebugger.event.C2S_LoadLuaScript, LuaDebugger.loadScriptBody ) elseif event == LuaDebugger.event.S2C_SerVar then LuaDebugger.isSetVar = true LuaDebugger.setVarBody = body local data = coroutine.yield() debugger_sendMsg( server, LuaDebugger.event.C2S_SerVar, { stack = data, eventType = data.eventType } ) elseif event == LuaDebugger.event.S2C_ReLoadFile then LuaDebugger.isReLoadFile = true LuaDebugger.reLoadFileBody = body local data = coroutine.yield() debugger_sendMsg( server, LuaDebugger.event.C2S_ReLoadFile, { stack = data, eventType = data.eventType } ) end end end end coro_debugger = coroutine.create(debugger_loop) debug_hook = function(event, line) if(not LuaDebugger.isHook) then return end if(LuaDebugger.Run) then if(event == "line") then local isCheck = false for k, breakInfo in pairs(LuaDebugger.breakInfos) do for bk, linesInfo in pairs(breakInfo) do if(linesInfo.lines and linesInfo.lines[line]) then isCheck = true break end end if(isCheck) then break end end if(not isCheck) then return end else LuaDebugger.currentFileName = nil LuaDebugger.currentTempFunc = nil return end end --跳出 if (LuaDebugger.StepOut) then if (event == "line" or event == "call") then return end local tempFun = getinfo(2, "f").func if (LuaDebugger.currentDebuggerData.funcsLength == 1) then ResetDebugInfo() LuaDebugger.Run = true else if (LuaDebugger.currentDebuggerData.funcs[2] == tempFun) then local data = debugger_stackInfo(3, LuaDebugger.event.C2S_StepInResponse) --挂起等待调试器作出反应 _resume(coro_debugger, data) checkSetVar() end end return end -- debugger_dump(LuaDebugger,"LuaDebugger") -- print(LuaDebugger.StepNextLevel,"LuaDebugger.StepNextLevel") local file = nil if (event == "call") then -- end -- if(not LuaDebugger.StepOut) then if (not LuaDebugger.Run) then LuaDebugger.StepNextLevel = LuaDebugger.StepNextLevel + 1 end -- print("stepIn",LuaDebugger.StepNextLevel) local stepInfo = getinfo(2, "S") local source = stepInfo.source if (source:find(LuaDebugger.DebugLuaFie) or source == "=[C]") then return end file = getSource(source) LuaDebugger.currentFileName = file elseif (event == "return" or event == "tail return") then -- end -- if(not LuaDebugger.StepOut) then if (not LuaDebugger.Run) then LuaDebugger.StepNextLevel = LuaDebugger.StepNextLevel - 1 end LuaDebugger.currentFileName = nil elseif (event == "line") then --@region 判断命中断点 --判断命中断点 --判断命中断点 --判断命中断点 --判断命中断点 local isHit = false local stepInfo = nil if (not LuaDebugger.currentFileName) then stepInfo = getinfo(2, "S") local source = stepInfo.source if (source == "=[C]" or source:find(LuaDebugger.DebugLuaFie)) then return end file = getSource(source) LuaDebugger.currentFileName = file end file = LuaDebugger.currentFileName --判断断点 local breakInfo = LuaDebugger.breakInfos[file] local breakData = nil if (breakInfo) then local ischeck = false for k, lineInfo in pairs(breakInfo) do local lines = lineInfo.lines if (lines and lines[line]) then ischeck = true break end end if (ischeck) then --并且在断点中 -- local info = stepInfo -- if (not info) then -- print("info ---------------") -- info = getinfo(2) -- end local hitPathNames = splitFilePath(LuaDebugger.currentLineFile) local hitCounts = {} local debugHitCounts = nil for k, lineInfo in pairs(breakInfo) do local lines = lineInfo.lines local pathNames = lineInfo.pathNames debugHitCounts = lineInfo.hitCounts if (lines and lines[line]) then breakData = lines[line] --判断路径 hitCounts[k] = 0 local hitPathNamesCount = #hitPathNames local pathNamesCount = #pathNames local checkCount = 0; while (true) do if (pathNames[pathNamesCount] ~= hitPathNames[hitPathNamesCount]) then break else hitCounts[k] = hitCounts[k] + 1 end pathNamesCount = pathNamesCount - 1 hitPathNamesCount = hitPathNamesCount - 1 checkCount = checkCount+1 if (pathNamesCount <= 0 or hitPathNamesCount <= 0) then break end end if(checkCount>0) then break; end if(checkCount==0) then breakData = nil -- break; end else breakData = nil end end if (breakData) then local hitFieName = "" local maxCount = 0 for k, v in pairs(hitCounts) do if (v > maxCount) then maxCount = v hitFieName = k end end local hitPathNamesLength = #hitPathNames if (hitPathNamesLength == 1 or (hitPathNamesLength > 1 and maxCount > 1)) then if (hitFieName ~= "") then local hitCount = breakData.hitCondition local clientHitCount = debugHitCounts[breakData.line] clientHitCount = clientHitCount + 1 debugHitCounts[breakData.line] = clientHitCount if (clientHitCount >= hitCount) then isHit = true end end end end end end --@endregion if (LuaDebugger.StepIn) then local data = debugger_stackInfo(3, LuaDebugger.event.C2S_NextResponse) --挂起等待调试器作出反应 if (data) then LuaDebugger.currentTempFunc = data.funcs[1] _resume(coro_debugger, data) checkSetVar() return end end if (LuaDebugger.StepNext) then if (LuaDebugger.StepNextLevel <= 0) then local data = debugger_stackInfo(3, LuaDebugger.event.C2S_NextResponse) -- 挂起等待调试器作出反应 if (data) then LuaDebugger.currentTempFunc = data.funcs[1] _resume(coro_debugger, data) checkSetVar() return end end end if (isHit) then local data = debugger_stackInfo(3, LuaDebugger.event.C2S_HITBreakPoint) if (breakData and breakData.condition) then debugger_conditionStr( breakData.condition, data.vars, function() _resume(coro_debugger, data) checkSetVar() end ) else --挂起等待调试器作出反应 _resume(coro_debugger, data) checkSetVar() end end end end debugger_xpcall = function() --调用 coro_debugger 并传入 参数 local data = debugger_stackInfo(4, LuaDebugger.event.C2S_HITBreakPoint) if(data.stack and data.stack[1]) then data.stack[1].isXpCall = true end --挂起等待调试器作出反应 _resume(coro_debugger, data) checkSetVar() end --调试开始 local function start() local fullName, dirName, fileName = debugger_getFilePathInfo(getinfo(1).source) LuaDebugger.DebugLuaFie = fileName local socket = createSocket() print(controller_host) print(controller_port) local server = socket.connect(controller_host, controller_port) debug_server = server if server then --创建breakInfo socket socket = createSocket() breakInfoSocket = socket.connect(controller_host, controller_port) if (breakInfoSocket) then breakInfoSocket:settimeout(0) debugger_sendMsg( breakInfoSocket, LuaDebugger.event.C2S_SetSocketName, { name = "breakPointSocket" } ) debugger_sendMsg( server, LuaDebugger.event.C2S_SetSocketName, { name = "mainSocket", version = LuaDebugger.version } ) xpcall( function() debug.sethook(debug_hook, "lrc") end, function(error) print("error:", error) end ) if (jit) then if (LuaDebugger.debugLuaType ~= "jit") then print("error======================================================") local msg = "当前luajit版本为: " .. jit.version .. " 请使用LuaDebugjit 进行调试!" print(msg) end end _resume(coro_debugger, server) end end end function StartDebug(host, port,reLoad) if (not host) then print("error host nil") end if (not port) then print("error prot nil") end if (type(host) ~= "string") then print("error host not string") end if (type(port) ~= "number") then print("error host not number") end controller_host = host controller_port = port xpcall( start, function(error) -- body print(error) end ) --代码重载 if(isReLoad) then xpcall(function() debugger_reLoadFile = require("luaideReLoadFile") end,function() print("左侧luaide按钮->打开luaIde最新调试文件所在文件夹->luaideReLoadFile.lua->拷贝到项目中") print("具体使用方式请看luaideReLoadFile中文件注释") debugger_reLoadFile = function() print("未实现代码重载") end end) end return debugger_receiveDebugBreakInfo, debugger_xpcall end --base64 local string = string ZZBase64.__code = { 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/', }; ZZBase64.__decode = {} for k,v in pairs(ZZBase64.__code) do ZZBase64.__decode[string.byte(v,1)] = k - 1 end function ZZBase64.encode(text) local len = string.len(text) local left = len % 3 len = len - left local res = {} local index = 1 for i = 1, len, 3 do local a = string.byte(text, i ) local b = string.byte(text, i + 1) local c = string.byte(text, i + 2) -- num = a<<16 + b<<8 + c local num = a * 65536 + b * 256 + c for j = 1, 4 do --tmp = num >> ((4 -j) * 6) local tmp = math.floor(num / (2 ^ ((4-j) * 6))) --curPos = tmp&0x3f local curPos = tmp % 64 + 1 res[index] = ZZBase64.__code[curPos] index = index + 1 end end if left == 1 then ZZBase64.__left1(res, index, text, len) elseif left == 2 then ZZBase64.__left2(res, index, text, len) end return table.concat(res) end function ZZBase64.__left2(res, index, text, len) local num1 = string.byte(text, len + 1) num1 = num1 * 1024 --lshift 10 local num2 = string.byte(text, len + 2) num2 = num2 * 4 --lshift 2 local num = num1 + num2 local tmp1 = math.floor(num / 4096) --rShift 12 local curPos = tmp1 % 64 + 1 res[index] = ZZBase64.__code[curPos] local tmp2 = math.floor(num / 64) curPos = tmp2 % 64 + 1 res[index + 1] = ZZBase64.__code[curPos] curPos = num % 64 + 1 res[index + 2] = ZZBase64.__code[curPos] res[index + 3] = "=" end function ZZBase64.__left1(res, index,text, len) local num = string.byte(text, len + 1) num = num * 16 local tmp = math.floor(num / 64) local curPos = tmp % 64 + 1 res[index ] = ZZBase64.__code[curPos] curPos = num % 64 + 1 res[index + 1] = ZZBase64.__code[curPos] res[index + 2] = "=" res[index + 3] = "=" end function ZZBase64.decode(text) local len = string.len(text) local left = 0 if string.sub(text, len - 1) == "==" then left = 2 len = len - 4 elseif string.sub(text, len) == "=" then left = 1 len = len - 4 end local res = {} local index = 1 local decode = ZZBase64.__decode for i =1, len, 4 do local a = decode[string.byte(text,i )] local b = decode[string.byte(text,i + 1)] local c = decode[string.byte(text,i + 2)] local d = decode[string.byte(text,i + 3)] --num = a<<18 + b<<12 + c<<6 + d local num = a * 262144 + b * 4096 + c * 64 + d local e = string.char(num % 256) num = math.floor(num / 256) local f = string.char(num % 256) num = math.floor(num / 256) res[index ] = string.char(num % 256) res[index + 1] = f res[index + 2] = e index = index + 3 end if left == 1 then ZZBase64.__decodeLeft1(res, index, text, len) elseif left == 2 then ZZBase64.__decodeLeft2(res, index, text, len) end return table.concat(res) end function ZZBase64.__decodeLeft1(res, index, text, len) local decode = ZZBase64.__decode local a = decode[string.byte(text, len + 1)] local b = decode[string.byte(text, len + 2)] local c = decode[string.byte(text, len + 3)] local num = a * 4096 + b * 64 + c local num1 = math.floor(num / 1024) % 256 local num2 = math.floor(num / 4) % 256 res[index] = string.char(num1) res[index + 1] = string.char(num2) end function ZZBase64.__decodeLeft2(res, index, text, len) local decode = ZZBase64.__decode local a = decode[string.byte(text, len + 1)] local b = decode[string.byte(text, len + 2)] local num = a * 64 + b num = math.floor(num / 16) res[index] = string.char(num) end return StartDebug AppleLoginMgr5--[[ appleid登录 author:{zhangpeng} time:2024-08-05 21:47:17 ]] local AppleLoginMgr, super = defClassStatic("AppleLoginMgr") local LOG_TAG = "AppleLoginMgr" local IOC_CLASS_NAME = "AppleLoginUtil" function AppleLoginMgr:init() AppleLoginMgr:registLuaCallBack() end function AppleLoginMgr:login() if Device.isIOS() then local param = {} printInfo(LOG_TAG,"ios login...") luaoc.callStaticMethod(IOC_CLASS_NAME, "login") end end function AppleLoginMgr:registLuaCallBack() if Device.isIOS() then local param = { loginErrorCb = function() printInfo(LOG_TAG,"登录失败回调") Msg.send(Msg.USER_LOGIN_APPLE_FAILED) end, loginAuthFailedCb = function (desc) local data = json.decode(desc) printInfo(LOG_TAG,"授权失败回调") Msg.send(Msg.USER_LOGIN_APPLE_AUTH_FILED, data) end, loginSucCb = function (jsonStr) local data = json.decode(jsonStr) table.print_r(data,"Apple ID 登录成功回调数据") if User:getLoginOrigin() == LoginConst.Origin.login then Msg.send(Msg.USER_LOGIN_APPLE_SUC, data) elseif User:getLoginOrigin() == LoginConst.Origin.bind then Msg.send(Msg.USER_LOGIN_APPLE_BIND_SUC, data) end end } luaoc.callStaticMethod(IOC_CLASS_NAME, "registLuaCallback", param) end end AppleLoginMgr:init() PlatformUtil --[[ author:{zhangpeng} time:2024-08-22 11:44:58 ]] local PlatformUtil, super = defClassStatic("PlatformUtil") local LOG_TAG = "PlatformUtil" local IOC_CLASS_NAME = "IHappy" local JavaUtilClass = "com/fy/xgame/tilelink/util/Util" function PlatformUtil:init() end -- 获取版本号 function PlatformUtil:getAppVersion() local version if Device.isIOS() then printInfo(LOG_TAG,"get ios version") local ok, _version = luaoc.callStaticMethod(IOC_CLASS_NAME, "getAppVersion") printInfo(LOG_TAG,"ios version:%s",version) version = _version elseif Device.isAndroid() then -- todo:: end version = version or CS.UnityEngine.Application.version printInfo(LOG_TAG,"version:%s",version) return version end -- 跳转到应用商店 function PlatformUtil:openAppStore() if Device.isIOS() then luaoc.callStaticMethod(IOC_CLASS_NAME, "openAppStore") elseif Device.isAndroid() then luaj.callStaticMethod(JavaUtilClass, "goToMarket", { "com.fy.xgame.tilelink", "com.android.vending"} ) end end -- 打开评分弹窗 function PlatformUtil:requestAppReview() if Device.isIOS() then luaoc.callStaticMethod(IOC_CLASS_NAME, "requestAppReview") end end PlatformUtil:init()ScreenShotUtil.2--[[ 屏幕截图工具 author:{zhangpeng} time:2024-04-02 19:19:27 ]] local ScreenShotUtil = {} local LOGTAG = "ScreenShotUtil" local p_max = CS.UnityEngine.Vector3.zero local p_min = CS.UnityEngine.Vector3.zero local center = CS.UnityEngine.Vector3.zero local layer = "TopLayer" local coefficientScale = 100 local orthographicSize = nil local designSizeOne = CS.UnityEngine.Vector2(2560, 1538) local designSizeTwo = CS.UnityEngine.Vector2(2048, 1538) function ScreenShotUtil:SetOrthographicSize(size) orthographicSize = size end -- 截全屏 function ScreenShotUtil:TakeAllScreenImg(obj) if obj == nil then return nil end local oldLayer = obj.layer self:ChangeObjLayer(obj, CS.UnityEngine.LayerMask.NameToLayer(layer)) local b = self:ClacBounds(obj) local cameraPhoto = CS.UnityEngine.GameObject("CameraPhoto") cameraPhoto.hideFlags = CS.UnityEngine.HideFlags.HideAndDontSave cameraPhoto.transform.position = b.center - CS.UnityEngine.Vector3(0, 0, b.extents.z + 10) cameraPhoto.transform:LookAt(b.center) local c = cameraPhoto:AddComponent(typeof(CS.UnityEngine.Camera)) c.cullingMask = 1 << CS.UnityEngine.LayerMask.NameToLayer(layer) c.clearFlags = CS.UnityEngine.CameraClearFlags.SolidColor c.orthographic = true c.orthographicSize = orthographicSize local tex = self:CaptureCamera(c, CS.UnityEngine.Rect(0, 0, b.size.x * coefficientScale, b.size.y * coefficientScale)) CS.UnityEngine.GameObject.Destroy(cameraPhoto) self:ChangeObjLayer(obj, oldLayer) return tex end -- 拍照 返回Texture2d function ScreenShotUtil:TakePhotoTexture2D(obj) if obj == nil then return nil end local oldLayer = obj.layer self:ChangeObjLayer(obj, CS.UnityEngine.LayerMask.NameToLayer(layer)) local b = self:ClacBounds(obj) local cameraPhoto = CS.UnityEngine.GameObject("CameraPhoto") cameraPhoto.hideFlags = CS.UnityEngine.HideFlags.HideAndDontSave cameraPhoto.transform.position = b.center - CS.UnityEngine.Vector3(0, 0, b.extents.z + 10) cameraPhoto.transform:LookAt(b.center) local c = cameraPhoto:AddComponent(typeof(CS.UnityEngine.Camera)) c.cullingMask = 1 << CS.UnityEngine.LayerMask.NameToLayer(layer) c.clearFlags = CS.UnityEngine.CameraClearFlags.SolidColor c.orthographic = true local size = math.max(b.extents.x, b.extents.y) c.orthographicSize = size local tex = self:CaptureCamera(c, CS.UnityEngine.Rect(0, 0, b.size.x * coefficientScale, b.size.y * coefficientScale)) CS.UnityEngine.GameObject.Destroy(cameraPhoto) self:ChangeObjLayer(obj, oldLayer) return tex end function ScreenShotUtil:TakePhotoTexture2DWithBounds(obj, bounds) if obj == nil then return nil end local oldLayer = obj.layer self:ChangeObjLayer(obj, CS.UnityEngine.LayerMask.NameToLayer(layer)) local b = bounds local cameraPhoto = CS.UnityEngine.GameObject("CameraPhoto") cameraPhoto.hideFlags = CS.UnityEngine.HideFlags.HideAndDontSave cameraPhoto.transform.position = b.center - CS.UnityEngine.Vector3(0, 0, b.extents.z + 10) cameraPhoto.transform:LookAt(b.center) local c = cameraPhoto:AddComponent(typeof(CS.UnityEngine.Camera)) c.cullingMask = 1 << CS.UnityEngine.LayerMask.NameToLayer(layer) c.clearFlags = CS.UnityEngine.CameraClearFlags.SolidColor c.orthographic = true local size = math.max(b.extents.x, b.extents.y) c.orthographicSize = size local tex = self:CaptureCamera(c, CS.UnityEngine.Rect(0, 0, b.size.x * coefficientScale, b.size.y * coefficientScale)) CS.UnityEngine.GameObject.Destroy(cameraPhoto) self:ChangeObjLayer(obj, oldLayer) return tex end -- obj: 要截屏的节点 -- width height: 图片大小 function ScreenShotUtil:takeCameraPhoto(obj, width, height, offset) if obj == nil then return nil end local _offset = offset or CS.UnityEngine.Vector3.zero local list = {} local oldLayerList = self:ChangeObjLayer(obj, CS.UnityEngine.LayerMask.NameToLayer(layer)) local b = self:ClacBounds(obj) -- 相机中心世界坐标 local cameraObject = CS.UnityEngine.GameObject.Find("MainCamera") local cameraComponent = CS.UnityEngine.GameObject.Find("MainCamera")[CS.UnityEngine.Camera] -- 相机焦点 local worldPos = cameraComponent:ViewportToWorldPoint(CS.UnityEngine.Vector3(0.5, 0.5, 0)) local cameraPhoto = CS.UnityEngine.GameObject("CameraPhoto") cameraPhoto.hideFlags = CS.UnityEngine.HideFlags.HideAndDontSave cameraPhoto.transform.position = cameraObject.transform.position + _offset cameraPhoto.transform:LookAt(CS.UnityEngine.Vector3(worldPos.x, worldPos.y, 0) + _offset) local c = cameraPhoto:AddComponent(typeof(CS.UnityEngine.Camera)) c.cullingMask = 1 << CS.UnityEngine.LayerMask.NameToLayer(layer) c.clearFlags = CS.UnityEngine.CameraClearFlags.SolidColor c.orthographic = true c.orthographicSize = cameraComponent.orthographicSize local tex = self:CaptureCamera(c, CS.UnityEngine.Rect(0, 0, width or CS.UnityEngine.Screen.width, height or CS.UnityEngine.Screen.height)) CS.UnityEngine.GameObject.Destroy(cameraPhoto) self:ChangeObjLayer(obj, oldLayerList) return tex end -- 更改目标所在层 function ScreenShotUtil:ChangeObjLayer(obj, layer) local s = type(layer) if type(layer) == "table" then for _, value in ipairs(layer) do if value["NODE"] and value["LAYER"] then value["NODE"]:SetLayer(value["LAYER"]) end end else local oldLayerList = {} local recursion recursion = function(gameObject, layer) table.insert( oldLayerList, { NODE = gameObject, LAYER = gameObject.layer } ) gameObject:SetLayer(layer) local transform = gameObject.transform for i = 1, gameObject.transform.childCount do local item = gameObject.transform:GetChild(i - 1) recursion(item.gameObject, layer) end end recursion(obj, layer) return oldLayerList end end -- 拍照 返回Sprite function ScreenShotUtil:TakePhotoSprite(obj) local tex = self:TakePhotoTexture2D(obj) local sprite = CS.UnityEngine.Sprite.Create(tex, CS.UnityEngine.Rect(0, 0, tex.width, tex.height), CS.UnityEngine.Vector2(0.5, 0.5), 100) return sprite end -- 获取整个屏幕的sprite function ScreenShotUtil:TakeScreenSprite(obj) local tex = self:TakeAllScreenImg(obj) local sprite = CS.UnityEngine.Sprite.Create(tex, CS.UnityEngine.Rect(0, 0, tex.width, tex.height), CS.UnityEngine.Vector2(0.5, 0.5), 100) return sprite end -- 对相机截图 function ScreenShotUtil:CaptureCamera(camera, rect) rect.width = math.round(rect.width) rect.height = math.round(rect.height) local rt = CS.UnityEngine.RenderTexture(rect.width, rect.height, 0) camera.targetTexture = rt camera:Render() CS.UnityEngine.RenderTexture.active = rt local screenShot = CS.UnityEngine.Texture2D(rect.width, rect.height, CS.UnityEngine.TextureFormat.RGBA32, false) screenShot:ReadPixels(rect, 0, 0) screenShot:Apply() camera.targetTexture = nil CS.UnityEngine.RenderTexture.active = nil rt:Release() CS.UnityEngine.Object.Destroy(rt) return screenShot end -- 计算目标包围盒 function ScreenShotUtil:ClacBounds(obj) local mesh = obj:GetComponent(typeof(CS.UnityEngine.Renderer)) if mesh ~= nil then local b = mesh.bounds p_max = b.max p_min = b.min center = b.center else end self:RecursionClacBounds(obj.transform) if mesh == nil then self:ClacCenter(p_max, p_min, center) end local size = CS.UnityEngine.Vector3(p_max.x - p_min.x, p_max.y - p_min.y, p_max.z - p_min.z) local bound = CS.UnityEngine.Bounds(center, size) bound.size = size bound.extents = size / 2 return bound end -- 计算包围盒中心坐标 function ScreenShotUtil:ClacCenter(max, min, center) local xc = (p_max.x + p_min.x) / 2 local yc = (p_max.y + p_min.z) / 2 local zc = (p_max.z + p_min.z) / 2 center = CS.UnityEngine.Vector3(xc, yc, zc) end -- 计算包围盒顶点 function ScreenShotUtil:RecursionClacBounds(obj) if obj.transform.childCount <= 0 then return end for i = 1, obj.childCount do local item = obj:GetChild(i - 1) local m = item:GetComponent(typeof(CS.UnityEngine.Renderer)) if m ~= nil then local b = m.bounds if p_max:Equals(CS.UnityEngine.Vector3.zero) and p_min:Equals(CS.UnityEngine.Vector3.zero) then p_max = b.max p_min = b.min end if b.max.x > p_max.x then p_max.x = b.max.x end if b.max.y > p_max.y then p_max.y = b.max.yz end if b.max.z > p_max.z then p_max.z = b.max.z end if b.min.x < p_min.x then p_min.x = b.min.x end if b.min.y < p_min.y then p_min.y = b.min.y end if b.min.z < p_min.z then p_min.z = b.min.z end end self:RecursionClacBounds(item) end end -- ui截图 function ScreenShotUtil:TakeUICameraPhoto(obj, width, height, offset, delayCb) local isDelay = false if delayCb then isDelay = true end -- 创建相机 local camera = CS.UnityEngine.GameObject.Instantiate(UITemplate.camera) local com = camera:GetComponent(typeof(CS.UnityEngine.Camera)) local worldPos = com:ViewportToWorldPoint(CS.UnityEngine.Vector3(0.5, 0.5, 0)) camera.transform:LookAt(CS.UnityEngine.Vector3(worldPos.x, worldPos.y, 0)) local data = CS.UnityEngine.Rendering.Universal.CameraExtensions.GetUniversalAdditionalCameraData(com) data.renderType = CS.UnityEngine.Rendering.Universal.CameraRenderType.Base com.cullingMask = 1 << CS.UnityEngine.LayerMask.NameToLayer(layer) -- 创建画布 local uinode = CS.UnityEngine.GameObject.Instantiate(CS.UnityEngine.Resources.Load("ui/uinode")) local canvas = CS.UnityEngine.GameObject.Instantiate(uinode:Seek("UICanvas")) -- local canvas = CS.UnityEngine.GameObject.Instantiate(UITemplate.canvas) local canvasCom = canvas:GetComponent(typeof(CS.UnityEngine.Canvas)) local oldLayerList = self:ChangeObjLayer(canvas, CS.UnityEngine.LayerMask.NameToLayer(layer)) canvasCom.worldCamera = com canvasCom.scaleFactor = UITemplate.canvas:GetComponent(typeof(CS.UnityEngine.Canvas)).scaleFactor if width then width = width * canvasCom.scaleFactor end if height then height = height * canvasCom.scaleFactor end -- 复制obj local parent = obj:GetParent() local newObj = CS.UnityEngine.GameObject.Instantiate(obj, parent.transform) newObj:SetParent(canvas) if offset then newObj:SetPosition(offset) end -- 截屏 local function capCam() local tex = self:CaptureCamera(com, CS.UnityEngine.Rect(0, 0, width or CS.UnityEngine.Screen.width, height or CS.UnityEngine.Screen.height)) CS.UnityEngine.GameObject.Destroy(camera) CS.UnityEngine.GameObject.Destroy(canvas) CS.UnityEngine.GameObject.Destroy(uinode) return tex end if not isDelay then return capCam() else CS.LuaGlobal.instance:runAtNextFrame( function() local tex = capCam() delayCb(tex) end ) end end --[[ 默认为 1 png类型 2 为 jpg ]] function ScreenShotUtil:saveShareTexture(tex, fileName, fileType) if not tex then printWarn(LOGTAG, "saveShareTexture, 存储图片 tex 不能为空") return end fileName = fileName or tostring(util.time.getTimeStamp()) local File = CS.System.IO.File local Directory = CS.System.IO.Directory local Path = CS.System.IO.Path if not fileType then fileType = 1 end local fileDir = Path.Combine(CS.UnityEngine.Application.persistentDataPath, "capturedata") local bytes local picPath if fileType == 1 then bytes = tex:EncodeToPNG() picPath = Path.Combine(fileDir, string.format("%s.png", fileName)) elseif fileType == 2 then bytes = tex:EncodeToJPG() picPath = Path.Combine(fileDir, string.format("%s.jpg", fileName)) end if not Directory.Exists(fileDir) then Directory.CreateDirectory(fileDir) end File.WriteAllBytes(picPath, bytes) return picPath end return ScreenShotUtil SingleSqliteTablen --[[ 仅有单条model的sqlite数据库表 不需要指定和使用主键key ]] ---@class SingleSqliteTable:SqliteTable local SingleSqliteTable, super = defClass("SingleSqliteTable", SqliteTable) local LOGTAG = SingleSqliteTable.__cls_name SingleSqliteTable.KEY_NAME = "Singlekey" SingleSqliteTable.KEY_VALUE = 1 function SingleSqliteTable:beforeInit() self:createKeyColumn() end function SingleSqliteTable:createKeyColumn() return self:c(SingleSqliteTable.KEY_NAME, SingleSqliteTable.KEY_VALUE, true) end function SingleSqliteTable:create() return super.create(self, SingleSqliteTable.KEY_VALUE) end function SingleSqliteTable:get() return super.get(self, SingleSqliteTable.KEY_VALUE) end function SingleSqliteTable:getOrCreate() return super.getOrCreate(self, SingleSqliteTable.KEY_VALUE) end function SingleSqliteTable:add(model) if model[SingleSqliteTable.KEY_NAME] ~= SingleSqliteTable.KEY_VALUE then printError(LOGTAG,"add, key错误:%s", model[SingleSqliteTable.KEY_NAME]) return end return super.add(self, model) end function SingleSqliteTable:upd(model) if model[SingleSqliteTable.KEY_NAME] ~= SingleSqliteTable.KEY_VALUE then printError(LOGTAG,"upd, key错误:%s", model[SingleSqliteTable.KEY_NAME]) return end return super.upd(self, model) end function SingleSqliteTable:set(model) if model[SingleSqliteTable.KEY_NAME] ~= SingleSqliteTable.KEY_VALUE then printError(LOGTAG,"set, key错误:%s", model[SingleSqliteTable.KEY_NAME]) return end return super.set(self, model) end return SingleSqliteTable HttpCmdDef --[[ author:{zhangpeng} time:2023-08-17 12:01:47 ]] local HttpCmdDef,_ = defClassStatic("HttpCmdDef") local baseUrl = "" HttpCmdDef.CMD = { -- 登录 LOGIN_BY_DEVICE = "/account/loginByDeviceId",--设备id登录 token required: false BIND_EMAIL = "/account/bindEmail",--绑定email token required: true BIND_APPLE = "/account/bindAppleId", -- 游客绑定苹果账号 BIND_FACEBOOK = "/account/bindFacebookId", -- 游客绑定facebook账号 LOGIN_BY_EMAIL_PW = "/account/loginByEmailPassword",--邮箱账号密码登录 token required: false LOGIN_BY_FACEBOOK = "/account/loginByFacebookId",-- facebook 登录,会发送数据(id) LOGIN_BY_APPLEID = "/account/loginByAppleIdToken", -- AppleId 登录 SAVE_USER_DATA = "/user/saveData", -- 保存数据 GET_USER_DATA = "/user/getData",-- 获取数据 -- 支付 VERIFY_TRANS = "/payment/apple/verifyTransaction", -- 支付验签 GET_PRODUCTS_LIST = "/payment/product/getList", -- 支付后请求商品列表 VERIFY_TRANS_GOOGLE = "/payment/google/verifyTransaction", -- 支付验签 SHOP_ITEM_LIST = "/payment/shop/getItemList", -- 商城内商品列表 -- 使用金币/钻石购买物品 BUY_RESOURCES = "/shop/purchase", -- 购买资源 -- 角色列表相关 ROLE_CREATE = "/role/create", -- 创建角色 ROLE_UPDATE = "/role/update", --更新角色 ROLE_GET_LIST = "/role/getList", -- 拉取角色列表 ROLE_DEL = "/role/delete", -- 删除角色 -- 货币相关 -- 宠物猫互动系统 PET_UNLOCK = "/pet/unlock", -- 解锁宠物 PET_GET_PET_LIST = "/pet/getList", -- 获取宠物列表 PET_BUY_ITEM = "/pet/buyItem", -- 购买宠物道具 PET_GET_ITEM_LIST = "/pet/getItems",-- 获取已有道具列表 PET_USE_ITEM = "/pet/useItem", -- 使用道具 -- 广告 AD_GET_REWARD = "/ad/getRewards", -- 获取广告奖励 -- 获取服务器时间 GET_TIME_SERVER = "/global/getServerTime", -- 获取服务器时间 -- 更新 GET_APP_VERSION = "/global/getAppVersionInfo" -- 获取版本号 } HttpCmdDef.ErrorCode = { TOKEN_ERROR = { ID = 10002, TEXT = "token 错误", }, EMAIL_BINDED = { ID = 20001, TEXT = "邮箱已经绑定过", }, EMAIL_PW_ERROR = { ID = 20002, TEXT = "邮箱登录密码错误", } , EMAIL_BE_USED = { ID = 20003, TEXT = "邮箱已经被占用", } , APPLE_ACCOUNT_BIND_EXIST = { ID = 20005, TEXT = "该账号已经被绑定过,不能重复绑定", } , COIN_NOT_ENOUGH = { ID = 30001, TEXT = "金币不足", }, GEM_NOT_ENOUGH = { ID = 30002, TEXT = "钻石不足", }, NET_BAD_GATEWAY = { ID = 502, TEXT = "网络异常(Bad Gateway)", } } HttpCmdDef.ErrorCodeNet ={ { code = 502, text = "Bad Gateway" }, { code = 0, text = "网络异常,请重试" } } function HttpCmdDef:init() if BUILD_ENV == ENV_DEVELOPMENT then baseUrl = "http://192.144.239.125" -- 开发环境 elseif BUILD_ENV == ENV_PRODUCTION then baseUrl = "http://192.144.239.125" -- 生产环境 end baseUrl = "http://192.144.239.125" end function HttpCmdDef.getUrlByCmd(cmd) local url = string.format("%s:3000%s",baseUrl,cmd) return url end HttpCmdDef:init()Msg--[[ 消息定义 author:{zhangpeng} time:2022-05-12 18:10:34 ]] Msg.def("APP_INIT") Msg.def("APP_INIT_FINISH") Msg.def("APP_MAIN") Msg.def("APP_EXIT") Msg.def("APP_PAUSED") Msg.def("APP_RESUME") Msg.def("APP_NATIVE") Msg.def("APP_LUAMSG") Msg.def("APP_BACKGROUND") -- 切到后台 Msg.def("APP_FOREGROUND") -- 切到前台 Msg.def("SCENE_PREPARE_LOAD") Msg.def("SCENE_BEFORE_LOAD") Msg.def("SCENE_AFTER_LOAD_SCENE") Msg.def("SCENE_ON_LOAD") Msg.def("SCENE_EXIT") Msg.def("ROLE_UPDATE") --热更begin Msg.def("HOTUPDATE_DOWNLOAD_PROGRESS") Msg.def("HOTUPDATE_DELETE_PROGRESS") Msg.def("HOTUPDATE_DOWNLOAD_QUENCE_FINISHED") -- 登录 -- Msg.def("USER_TOKEN_EXPIRED") -- 用户token失效 -- facebook Msg.def("USER_LOGIN_FB_SUC") -- facebook 登录成功 Msg.def("USER_LOGIN_FB_FAILED") -- 登录失败 Msg.def("USER_LOGIN_FB_CANCLE") -- 取消登录 -- AppleId Msg.def("USER_LOGIN_APPLE_SUC") -- appleid 登录成功 Msg.def("USER_LOGIN_APPLE_FAILED") -- 登录失败 Msg.def("USER_LOGIN_APPLE_AUTH_FILED") -- 授权失败 -- 绑定 -- Msg.def("USER_LOGIN_APPLE_BIND_SUC") -- 绑定发起Apple的登录成功 Msg.def("USER_LOGIN_FB_BIND_SUC") -- 绑定发起facebook的登录成功 Msg.def("LOGIN_SUCCESS") -- 登录成功 Msg.def("LOGIN_FAIL") Msg.def("LOGIN_CANCEL") -- 在用户取消情况下,关闭登录相关界面 -- socket msg-- Msg.def("SOCKET_MSG_ACK_SUC") --握手成功 -- 商店 Msg.def("SHOP_iOS_PURCHASE_SUC") -- 苹果服务器的支付成功回调 Msg.def("SHOP_iOS_PURCHASE_FAILED") -- 购买失败 Msg.def("SHOP_GOOGLE_PURCHASE_SUC") -- google服务器的支付成功回调 Msg.def("SHOP_GOOGLE_PURCHASE_FAILED") -- 购买失败 Msg.def("SHOP_PRODUCT_LIST_UPDATE") -- 广告 Msg.def("AD_REWARD_VIEWO_WATCH_SUC") -- 激励视频观看成功,领奖 -- 货币刷新 Msg.def("COIN_UPDATE_COUNT") -- 刷新金币数量 Msg.def("GEM_UPDATE_COUNT") -- 刷新钻石数量 -- 道具数量刷新 Msg.def("ITEM_CHANGED") -- 道具数量刷新 -- 红点 Msg.def("RED_DOT_UPDATE") -- 刷新红点main3require("data/static_config/parse/TextCfgParse")Scenej# ---@class Scene:LuaClass local Scene = defClass("Scene") local LOGTAG = "Scene" local LogicNodeName = "LogicNode" function Scene:ctor(sceneInfo) printInfo(LOGTAG, "ctor, name:%s", self.__cls_name) self.sceneInfo = sceneInfo self:onmsg(Msg.PAUSE, function (msgid, ...) self:onAppPause() end) self:onmsg(Msg.RESUME, function (msgid, ...) self:onAppResume() end) self:__initCom() end function Scene:startLoad() ---@type ReslinkLoad local sceneLoad = ReslinkLoad.new() local info = self:info() sceneLoad:addResLink(info.asset) self._sceneLoad = sceneLoad local sceneInfo = self.sceneInfo if sceneInfo:getEnterTransCls() then self:loadSceneAsync(sceneInfo:getEnterTransCls()) else self:loadSceneSync() end end function Scene:getSceneArgs() return self.sceneInfo:getArgs() end ---comment ---@return SceneInfoValue function Scene:info() printError(LOGTAG,"info未重写") return { asset = nil } end function Scene:getSceneObj() if not self._scene then return end if CS.LuaHelper.IsNull(self._scene) then return end return self._scene end -- 子类自己的加载 function Scene:getCustomSceneLoad() return nil end function Scene:_doLoadScene(finishCb, progressCb, isAsync) self:prepareLoad() TimerMgr:runAtEndOfFrame( function() self:beforeLoad() local customSceneLoad = self:getCustomSceneLoad() if customSceneLoad ~= nil then -- 如果有自定义的load, 进度和完成以自定义的load为准 self._sceneLoad:load(customSceneLoad, nil, isAsync) else self._sceneLoad:load(finishCb, progressCb, isAsync) end end ) end function Scene:loadSceneSync() self:_doLoadScene(function (sceneList) self:onLoadComplete(sceneList[1]) end, nil, false) end function Scene:loadSceneAsync(transCls) local transUI = transCls.new() self._transUI = transUI self:doTransIn( transUI, function() self:_doLoadScene(function (sceneList) self:onLoadComplete(sceneList[1]) end, function (progress) self:onLoadProgress(progress) end, true) end ) end ---场景加载的进度 function Scene:onLoadProgress(progress) if not self._transUI then return end self._transUI:setProgress(progress) end ---场景加载完成 ---@param scene CS.UnityEngine.SceneManagement.Scene function Scene:onLoadComplete(scene) if not self._transUI then self:afterLoad(scene) self:afterTrans() return end self:doTransOut( self._transUI, function() self:afterLoad(scene) self:afterTrans() end ) end function Scene:doTransIn(transUI, callback) transUI:transIn( function() if self.__exited then return end callback() end ):show(true) end function Scene:doTransOut(transUI, callback) transUI:transOut( function() if self.__exited then return end callback() end ) end --#region 加载时的生命周期函数 function Scene:prepareLoad() printInfo(LOGTAG, "prepareLoad, name:%s", self.__cls_name) -- 暂停上一个场景的 self:onAppPause() local go = self:getSceneObj() if go then local rootGo = go:GetGameRoot() util.pause.stopAll(rootGo) end Msg.send(Msg.SCENE_PREPARE_LOAD, self) end function Scene:beforeLoad() printInfo(LOGTAG, "beforeLoad, name:%s", self.__cls_name) Msg.send(Msg.SCENE_BEFORE_LOAD, self) end ---@param scene CS.UnityEngine.SceneManagement.Scene function Scene:afterLoad(scene) printInfo(LOGTAG, "afterLoad, name:%s", self.__cls_name) self._scene = scene Msg.send(Msg.SCENE_AFTER_LOAD_SCENE, self) self:beforeOnLoad(scene) self:onLoad() util.ugui.enableAllTouches() ResLoader.gcAfterLoadScene() self:afterOnLoad() self:initDebug() end --[[ ---@param scene CS.UnityEngine.SceneManagement.Scene function Scene:afterLoadDep(scene) printInfo(LOGTAG, "afterLoadDep, name:%s", self.__cls_name) self:beforeOnLoad(scene) self:onLoad() util.ugui.enableAllTouches() ResLoader.gcAfterLoadScene() self:afterOnLoad() end ]] function Scene:afterTrans() printInfo(LOGTAG, "afterTrans, name:%s", self.__cls_name) self:onSceneTransitionOver() end --#endregion function Scene:beforeOnLoad(scene) --#region 初始化 rootNode 和 cams local rootGameObjects = scene:GetRootGameObjects() ---@type CS.UnityEngine.GameObject, CS.UnityEngine.Camera local rootNode, camera = rootGameObjects[0], nil for _, go in cs_ipairs(rootGameObjects) do if go.name == "GameRoot" then rootNode = go elseif go.name == "MainCamera" then camera = go:GetComponent("Camera") end end if camera == nil then local camera = UnityEngine.GameObject.FindFirstObjectByType(typeof(UnityEngine.Camera)) end ---@type CS.UnityEngine.GameObject self.rootNode = rootNode self.cams = { scene = camera } self:initLogicNode() local data = CS.UnityEngine.Rendering.Universal.CameraExtensions.GetUniversalAdditionalCameraData(self.cams.scene) data.cameraStack:Add(UILayerUtil:getCamera()) self:defineComs() for _, com in pairs(self.__coms__) do com:onLoad() end end function Scene:afterOnLoad() for _, com in pairs(self.__coms__) do com:afterOnLoad() end end function Scene:initLogicNode() local go = CS.UnityEngine.GameObject.Find(LogicNodeName) if not go then go = CS.UnityEngine.GameObject(LogicNodeName) CS.UnityEngine.GameObject.DontDestroyOnLoad(go) end self.logicNode = go end --#region 子类重写 function Scene:onLoad() printInfo(LOGTAG, "onLoad, name:%s", self.__cls_name) Msg.send(Msg.SCENE_ON_LOAD, self.__cls_name) end function Scene:onSceneTransitionOver() for _, com in pairs(self.__coms__) do com:onSceneTransitionOver() end end function Scene:onExit() self.sceneInfo = nil Scene.super.onExit(self) printInfo(LOGTAG, "onExit, name:%s", self.__cls_name) Msg.send(Msg.SCENE_EXIT, self.__cls_name) end --#endregion --#region 组件相关 --初始化组件 -- 防止self.coms被重写 -- fake的作用是为了能够直接访问组件,而不用判断 hasCom function Scene:__initCom() local emptyFunc = function() end local fake = {} setmetatable(fake, { __index = function(t, k) return emptyFunc end }) self.coms = {} self.__coms__ = {} local __coms__ = self.__coms__ setmetatable(self.coms, { __index = function(t, k) if k == "__cls_inst" then return false end local com = __coms__[k] if com then return com else printWarn(LOGTAG, "index, 组件不存在:%s", k) return fake end end, __newindex = function(table, key, value) __coms__[key] = value end }) end -- 废弃 -- function Scene:hasCom(comName) -- return self.__coms__[comName] -- end --声明组件 function Scene:defineComs() end function Scene:addCom(comCls, ...) local com = comCls.new(self, ...) self.coms[com.__cls_name] = com end --#endregion --#region pause resume function Scene:onAppPause() printVerbose(LOGTAG, "onAppPause %s", tostring(self)) local go = self:getSceneObj() if go == nil then return end local rootGo = go:GetGameRoot() if rootGo.__pausedList__ then return end local pauseList = {audio = {}, timeline = {}, videoplayer = {}, action = {} } pauseList.audio = util.pause.pauseAudio(rootGo) pauseList.timeline = util.pause.pauseTimeline(rootGo) pauseList.videoplayer = util.pause.pauseVideoPlayer(rootGo) pauseList.action = util.pause.pauseAction(rootGo) rootGo.__pausedList__ = pauseList end function Scene:onAppResume() local go = self:getSceneObj() local rootGo = go and go:GetGameRoot() or {} if not rootGo.__pausedList__ then return end local pauseList = rootGo.__pausedList__ rootGo.__pausedList__ = nil util.pause.resumeAudio(pauseList.audio) util.pause.resumeTimeline(pauseList.timeline) util.pause.resumeVideoPlayer(pauseList.videoplayer) util.pause.resumeAction(pauseList.action) end function Scene:getName() return self.__cls_name end function Scene:initDebug() if Application.isEditor and self.rootNode then self.rootNode:Step(function() if Input.GetKeyDown("space") then SceneMgr:reEnter() end end) end end --#endregionTouchClickListener-- -- TouchClickListener.lua -- @author zhangxiaojun -- @description -- @created 2023-07-11T15:51:31.773Z+08:00 -- @last-modified 2023-07-12T09:25:36.564Z+08:00 -- ---@class TouchClickListener:TouchListener local TouchClickListener = defClass("TouchClickListener", TouchListener) function TouchClickListener:onInit() self._triggerOnce = false end function TouchClickListener:triggerOnce() self._triggerOnce = true end ---@param point CS.UnityEngine.Vector3 function TouchClickListener:onBegan(point) if TouchCom.intersect(self.gameObject, point) then self._actived = true self._downPt = point else self._actived = false end end ---@param point CS.UnityEngine.Vector3 function TouchClickListener:onEnd(point) local isIntersect = false if self._actived then if (self._downPt - point).magnitude < 0.1 then if TouchCom.intersect(self.gameObject, point) then self._endCb(point) if self._triggerOnce then self:destroy() end isIntersect = true end end self._actived = false end return isIntersect end FacebookShareUtilK--[[ fb分享 author:{zhangpeng} time:2023-12-26 11:10:34 ]] TouchListener ---@class TouchListener:LuaClass local TouchListener = defClass("TouchListener") local globalIndex = 0 function TouchListener:ctor(gameObject, touchCom) self.gameObject = gameObject self.priority = 0 self._dead = false self._bSwallow = true -- 吞没事件 self._enabled = true self._actived = false self.touchCom = touchCom self._globalIndex = globalIndex globalIndex = globalIndex + 1 self:onInit() end function TouchListener:disable() self._enabled = false return self end function TouchListener:enable() self._enabled = true return self end function TouchListener:isEnable() return self._enabled and self.gameObject.activeInHierarchy end function TouchListener:setPriority(priority) self.priority = priority self.touchCom.sortDirtry = true return self end --#region begin:override me function TouchListener:onInit() end ---@param point CS.UnityEngine.Vector3 function TouchListener:onBegan(point) end ---@param point CS.UnityEngine.Vector3 function TouchListener:onMoved(point) end ---@param point CS.UnityEngine.Vector3 function TouchListener:onEnd(point) end --#endregion function TouchListener:setBeganCb(cb) self._beganCb = cb return self end function TouchListener:setMovedCb(cb) self._movedCb = cb return self end function TouchListener:setEndCb(cb) self._endCb = cb return self end function TouchListener:destroy() self._dead = true end function TouchListener:isDead() return self._dead or CS.LuaHelper.IsNull(self.gameObject) end function TouchListener:setSwallow(isSwallow) self._bSwallow = isSwallow return self end UIRoot. ---@class UIRoot:LuaClass local UIRoot, super = defClass("UIRoot") local UnityEngine = CS.UnityEngine local UGUI = CS.UnityEngine.UI local LOGTAG = "UIRoot" ---@param go CS.UnityEngine.GameObject ---@param isGlobal boolean ---@param uiMask CS.UnityEngine.GameObject function UIRoot:ctor(go, isGlobal, uiMask) Msg.del(Msg.EXIT, nil, self) self.go = go self.isGlobalUI = isGlobal ---@type UILayer[] self.uiLayerStack = {} self.uiMask = uiMask ---@type {audio:table, timeline:table, videoplayer:table, action:table}|nil self.pauseList = nil end function UIRoot:getUILayerStack() return self.uiLayerStack or {} end function UIRoot:getUIMask() return self.uiMask end function UIRoot:addUILayer(uilayer) table.insert(self.uiLayerStack, uilayer) end function UIRoot:removeUILayer(uilayer) printInfo(LOGTAG, "removeUILayer") table.removeByValue(self.uiLayerStack, uilayer, true) if not next(self.uiLayerStack) and not self.isGlobalUI then self:exit() end end function UIRoot:getGameObject() return self.go end function UIRoot:getPauseList() return self.pauseList end function UIRoot:setPauseList(pauseList) self.pauseList = pauseList end function UIRoot:onExit() UILayerUtil:_removeUIRoot(self) super.onExit(self) end return UIRootmainyrequire("Assets.LuaScripts.framework.debug.luaidedebug.boot_debug_main") require("Assets.LuaScripts.boot.main_editor")serpent* local n, v = "serpent", "0.302" -- (C) 2012-18 Paul Kulchenko; MIT License local c, d = "Paul Kulchenko", "Lua serializer and pretty printer" local snum = {[tostring(1 / 0)] = "1/0 --[[math.huge]]", [tostring(-1 / 0)] = "-1/0 --[[-math.huge]]", [tostring(0 / 0)] = "0/0"} local badtype = {thread = true, userdata = true, cdata = true} local getmetatable = debug and debug.getmetatable or getmetatable local pairs = function(t) return next, t end -- avoid using __pairs in Lua 5.2+ local keyword, globals, G = {}, {}, (_G or _ENV) for _, k in ipairs( { "and", "break", "do", "else", "elseif", "end", "false", "for", "function", "goto", "if", "in", "local", "nil", "not", "or", "repeat", "return", "then", "true", "until", "while" } ) do keyword[k] = true end for k, v in pairs(G) do globals[v] = k end -- build func to name mapping for _, g in ipairs({"coroutine", "debug", "io", "math", "string", "table", "os"}) do for k, v in pairs(type(G[g]) == "table" and G[g] or {}) do globals[v] = g .. "." .. k end end local function s(t, opts) local name, indent, fatal, maxnum = opts.name, opts.indent, opts.fatal, opts.maxnum local sparse, custom, huge = opts.sparse, opts.custom, not opts.nohuge local space, maxl = (opts.compact and "" or " "), (opts.maxlevel or math.huge) local maxlen, metatostring = tonumber(opts.maxlength), opts.metatostring local iname, comm = "_" .. (name or ""), opts.comment and (tonumber(opts.comment) or math.huge) local numformat = opts.numformat or "%.17g" local seen, sref, syms, symn = {}, {"local " .. iname .. "={}"}, {}, 0 local function gensym(val) return "_" .. (tostring(tostring(val)):gsub("[^%w]", ""):gsub( "(%d%w+)", -- tostring(val) is needed because __tostring may return a non-string value function(s) if not syms[s] then symn = symn + 1 syms[s] = symn end return tostring(syms[s]) end )) end local function safestr(s) return type(s) == "number" and tostring(huge and snum[tostring(s)] or numformat:format(s)) or type(s) ~= "string" and tostring(s) or -- escape NEWLINE/010 and EOF/026 ("%q"):format(s):gsub("\010", "n"):gsub("\026", "\\026") end local function comment(s, l) return comm and (l or 0) < comm and " --[[" .. select(2, pcall(tostring, s)) .. "]]" or "" end local function globerr(s, l) return globals[s] and globals[s] .. comment(s, l) or not fatal and safestr(select(2, pcall(tostring, s))) or error("Can't serialize " .. tostring(s)) end local function safename(path, name) -- generates foo.bar, foo[3], or foo['b a r'] local n = name == nil and "" or name local plain = type(n) == "string" and n:match("^[%l%u_][%w_]*$") and not keyword[n] local safe = plain and n or "[" .. safestr(n) .. "]" return (path or "") .. (plain and path and "." or "") .. safe, safe end local alphanumsort = type(opts.sortkeys) == "function" and opts.sortkeys or function(k, o, n) -- k=keys, o=originaltable, n=padding local maxn, to = tonumber(n) or 12, {number = "a", string = "b"} local function padnum(d) return ("%0" .. tostring(maxn) .. "d"):format(tonumber(d)) end table.sort( k, function(a, b) -- sort numeric keys first: k[key] is not nil for numerical keys return (k[a] ~= nil and 0 or to[type(a)] or "z") .. (tostring(a):gsub("%d+", padnum)) < (k[b] ~= nil and 0 or to[type(b)] or "z") .. (tostring(b):gsub("%d+", padnum)) end ) end local function val2str(t, name, indent, insref, path, plainindex, level) local ttype, level, mt = type(t), (level or 0), getmetatable(t) local spath, sname = safename(path, name) local tag = plainindex and ((type(name) == "number") and "" or name .. space .. "=" .. space) or (name ~= nil and sname .. space .. "=" .. space or "") if seen[t] then -- already seen this element sref[#sref + 1] = spath .. space .. "=" .. space .. seen[t] return tag .. "nil" .. comment("ref", level) end -- protect from those cases where __tostring may fail if type(mt) == "table" and metatostring ~= false then local to, tr = pcall( function() return mt.__tostring(t) end ) local so, sr = pcall( function() return mt.__serialize(t) end ) if (to or so) then -- knows how to serialize itself seen[t] = insref or spath t = so and sr or tr ttype = type(t) end -- new value falls through to be serialized end if ttype == "table" then if level >= maxl then return tag .. "{}" .. comment("maxlvl", level) end seen[t] = insref or spath if next(t) == nil then return tag .. "{}" .. comment(t, level) end -- table empty if maxlen and maxlen < 0 then return tag .. "{}" .. comment("maxlen", level) end local maxn, o, out = math.min(#t, maxnum or #t), {}, {} for key = 1, maxn do o[key] = key end if not maxnum or #o < maxnum then local n = #o -- n = n + 1; o[n] is much faster than o[#o+1] on large tables for key,_ in pairs(t) do if o[key] ~= key then n = n + 1 o[n] = key end end end if maxnum and #o > maxnum then o[maxnum + 1] = nil end if opts.sortkeys and #o > maxn then alphanumsort(o, t, opts.sortkeys) end local sparse = sparse and #o > maxn -- disable sparsness if only numeric keys (shorter output) for n, key in ipairs(o) do local value, ktype, plainindex = t[key], type(key), n <= maxn and not sparse if opts.valignore and opts.valignore[value] or -- skip ignored values; do nothing opts.keyallow and not opts.keyallow[key] or opts.keyignore and opts.keyignore[key] or opts.valtypeignore and opts.valtypeignore[type(value)] or -- skipping ignored value types sparse and value == nil then -- skipping nils; do nothing elseif ktype == "table" or ktype == "function" or badtype[ktype] then if not seen[key] and not globals[key] then sref[#sref + 1] = "placeholder" local sname = safename(iname, gensym(key)) -- iname is table for local variables sref[#sref] = val2str(key, sname, indent, sname, iname, true) end sref[#sref + 1] = "placeholder" local path = seen[t] .. "[" .. tostring(seen[key] or globals[key] or gensym(key)) .. "]" sref[#sref] = path .. space .. "=" .. space .. tostring(seen[value] or val2str(value, nil, indent, path)) else out[#out + 1] = val2str(value, key, indent, nil, seen[t], plainindex, level + 1) if maxlen then maxlen = maxlen - #out[#out] if maxlen < 0 then break end end end end local prefix = string.rep(indent or "", level) local head = indent and "{\n" .. prefix .. indent or "{" local body = table.concat(out, "," .. (indent and "\n" .. prefix .. indent or space)) local tail = indent and "\n" .. prefix .. "}" or "}" return (custom and custom(tag, head, body, tail, level) or tag .. head .. body .. tail) .. comment(t, level) elseif badtype[ttype] then seen[t] = insref or spath return tag .. globerr(t, level) elseif ttype == "function" then seen[t] = insref or spath if opts.nocode then return tag .. "function() --[[..skipped..]] end" .. comment(t, level) end local ok, res = pcall(string.dump, t) local func = ok and "((loadstring or load)(" .. safestr(res) .. ",'@serialized'))" .. comment(t, level) return tag .. (func or globerr(t, level)) else return tag .. safestr(t) end -- handle all other types end local sepr = indent and "\n" or ";" .. space local body = val2str(t, name, indent) -- this call also populates sref local tail = #sref > 1 and table.concat(sref, sepr) .. sepr or "" local warn = opts.comment and #sref > 1 and space .. "--[[incomplete output with shared/self-references skipped]]" or "" return not name and body .. warn or "do local " .. body .. sepr .. tail .. "return " .. name .. sepr .. "end" end local function deserialize(data, opts) local env = (opts and opts.safe == false) and G or setmetatable( {}, { __index = function(t, k) return t end, __call = function(t, ...) error("cannot call functions") end } ) local f, res = (loadstring or load)("return " .. data, nil, nil, env) if not f then f, res = (loadstring or load)(data, nil, nil, env) end if not f then return f, res end if setfenv then setfenv(f, env) end return pcall(f) end local function merge(a, b) if b then for k, v in pairs(b) do a[k] = v end end return a end return { _NAME = n, _COPYRIGHT = c, _DESCRIPTION = d, _VERSION = v, serialize = s, load = deserialize, dump = function(a, opts) return s(a, merge({name = "_", compact = true, sparse = true}, opts)) end, line = function(a, opts) return s(a, merge({sortkeys = true, comment = true}, opts)) end, block = function(a, opts) return s(a, merge({indent = " ", sortkeys = true, comment = true}, opts)) end } mainhrequire("data/const/main") require("data/scene_config/main") require("data/static_config/parse/main")DBMgrE$ local DBMgr = defClassStatic("DBMgr") local Convert = CS.System.Convert local Md5Util = CS.Md5Util function DBMgr:init(isEncrypt, getDeviceIdFunc, getTimeFunc) if self.inited then return end self.inited = true self.isEncrypt = isEncrypt self.dbTableList = {} self.dbTableListDirty = false self.dbTableClsList = {} self.getDeviceIdFunc = getDeviceIdFunc or function () return "111111" end self.getTimeFunc = getTimeFunc or function () return os.time() end self.getVersionFunc = function () return PlatformUtil.getAppVersion() end Msg.add(Msg.EXIT, function() self:closeCurUserDB() end) end ---增加 table 类 ---@param tableCls SqliteTable ---@param isUser boolean 是否是用户表 ---@param isRole boolean 是否是角色表, 都false是设备表 ---@param tableName string 可以用 DBMgr[tableName] 访问 table function DBMgr:addDBTableCls(tableCls, isUser, isRole, tableName) printVerbose(self.logTag,"addDBTableCls") table.insert(self.dbTableClsList, { tableCls = tableCls, isUser = isUser, isRole = isRole, tableName = tableName, }) end ---初始化表。用户表 角色表 设备表 ---@param isUser boolean 是否用户 ---@param isRole boolean 是否角色 function DBMgr:_initTables(isUser, isRole) local db, dbTableList, tableCls if isUser and isRole then db = self.roleDB dbTableList = self.roleDBTableList elseif isUser then db = self.userDB dbTableList = self.userDBTableList else db = self.deviceDB dbTableList = self.deviceDBTableList end if not db then return end local apis = { getTimeFunc = self.getTimeFunc, getVersionFunc = self.getVersionFunc, } for i,v in ipairs(self.dbTableClsList) do if v.isUser == isUser and v.isRole == isRole then local tableCls = v.tableCls local tableVarName = v.tableName or tableCls.__cls_name:gsub("^%l", string.lower) printVerbose(self.logTag,"_initTables %s", tableVarName) self[tableVarName] = self:_addDBTable(db, dbTableList, tableCls, tableVarName, apis) end end end ---获取数据库的路径 function DBMgr:_getDBPath(userId, roleId) local path = CS.UnityEngine.Application.persistentDataPath .. "/db/" if not roleId and not userId then path = path .. "local" .. "/" elseif not roleId then path = path .. userId .. "/" else path = path .. userId .. "/" .. roleId .. "/" end printInfo(self.logTag, "_getDBPath, path:%s", path) return path end ---获取所有的userid function DBMgr:getAllLocalDBUserIdList() local path = CS.UnityEngine.Application.persistentDataPath .. "/db" local list = {} local dirPathList = Directory.GetDirectories(path) for _, dirPath in cs_ipairs(dirPathList) do if CS.LuaHelper.IsFileExists(Path.Combine(dirPath, USER_DB_NAME)) then local userId = string.sub(dirPath, -7) table.insert(list, userId) end end return list end function DBMgr:getDeviceId() local filePath = CS.UnityEngine.Application.persistentDataPath .. "/udid.dat" if CS.LuaHelper.IsFileExists(filePath) then printInfo(self.logTag, "getDeviceId, udid.data exist") local deviceId = CS.LuaHelper.ReadFileText(filePath) if deviceId then return deviceId end end printInfo(self.logTag, "getDeviceId, udid.data not exist") local deviceId = self.getDeviceIdFunc() -- IHumanSDK:getDeviceId() CS.LuaHelper.SaveFileWithText(filePath, deviceId) return deviceId end function DBMgr:getKey() local key = nil if not self.isEncrypt then return key end key = self:getDeviceId() key = Convert.ToBase64String(key) key = Md5Util.GetMd5OfString(key) return key end function DBMgr:_addDBTable(db, dbTableList, tableCls, tableName, apis) local dbTable = db:getTable(tableCls, tableName, apis) table.insert(dbTableList, dbTable) self.dbTableListDirty = true return dbTable end ---打开当前 user db function DBMgr:openCurUserDB(userId) self:closeCurUserDB() self.userDBTableList = {} self.dbTableListDirty = true self.userDB = self:openUserDB(userId) self:_initTables(true, false) end ---打开当前 role db function DBMgr:openCurRoleDB(userId, roleId) self:closeCurRoleDB() self.roleDBTableList = {} self.dbTableListDirty = true self.roleDB = self:openRoleDB(userId, roleId) self:_initTables(true, true) end ---打开当前 device db function DBMgr:openCurDeviceDB() printVerbose(self.logTag, "openCurDeviceDB") self.deviceDBTableList = {} self.dbTableListDirty = true self.deviceDB = self:openDeviceDB() self:_initTables(false, false) end ---关闭当前 user db function DBMgr:closeCurUserDB() if self.userDB then self.userDB:close() end self.userDB = nil self.userDBTableList = {} self.dbTableListDirty = true self:closeCurRoleDB() end ---关闭当前 role db function DBMgr:closeCurRoleDB() if self.roleDB then self.roleDB:close() end self.roleDB = nil self.roleDBTableList = {} self.dbTableListDirty = true end ---打开 user db function DBMgr:openUserDB(userId) local path = self:_getDBPath(userId) .. self.userDBName local db = self.dbCls.new(path) db:open(self:getKey(userId)) return db end ---打开 role db function DBMgr:openRoleDB(userId, roleId) local path = self:_getDBPath(userId, roleId) .. self.roleDBName local db = self.dbCls.new(path) db:open(self:getKey(userId)) return db end ---打开 device db function DBMgr:openDeviceDB() local path = self:_getDBPath(nil, nil) .. self.deviceDBName local db = self.dbCls.new(path) db:open(self:getKey()) return db end ---拷贝 user db. 会把 role 的也拷贝过去,然后删掉原来的文件 function DBMgr:copyUserDB(srcUserId, srcRoleId, desUserId, desRoleId) printInfo(self.logTag, "copyUserDB, from %s %s to %s %s", srcUserId, srcRoleId, desUserId, desRoleId) local srcUserDBPath = self:_getDBPath(srcUserId) local desUserDBPath = self:_getDBPath(desUserId) local srcRoleDBPath = self:_getDBPath(srcUserId, srcRoleId) local desRoleDBPath = self:_getDBPath(desUserId, desRoleId) local srcUserDBFilePath = srcUserDBPath .. self.userDBName local desUserDBFilePath = desUserDBPath .. self.userDBName local srcRoleDBFilePath = srcRoleDBPath .. self.roleDBName local desRoleDBFilePath = desRoleDBPath .. self.roleDBName if not CS.LuaHelper.IsFileExists(srcUserDBFilePath) then printInfo(self.logTag, "copyUserDB, srcUserDBPath is not exist %s", srcUserDBFilePath) return end if not CS.LuaHelper.IsFileExists(desUserDBFilePath) then printInfo(self.logTag, "copyUserDB, desUserDBPath is not exist %s", desUserDBFilePath) return end if not CS.LuaHelper.IsFileExists(srcRoleDBFilePath) then printInfo(self.logTag, "copyRoleDB, srcRoleDBPath is not exist %s", srcRoleDBFilePath) return end if CS.LuaHelper.IsFileExists(desRoleDBFilePath) then printError(self.logTag, "copyRoleDB, desRoleDBPath is exist %s", desRoleDBFilePath) return end local ret, errorMsg = xpcall( function() if not Directory.Exists(desRoleDBPath) then Directory.CreateDirectory(desRoleDBPath) end CS.LuaHelper.CopyFile(srcUserDBFilePath, desUserDBFilePath) CS.LuaHelper.CopyFile(srcRoleDBFilePath, desRoleDBFilePath) Directory.Delete(srcUserDBPath, true) end, function() printInfo(self.logTag, debug.traceback()) end ) if not ret then printError(self.logTag, "copyRoleDB, 数据库拷贝失败 ret:%s errorMsg:%s", ret, errorMsg) return end printInfo(self.logTag, "copyRoleDB, 数据库拷贝成功") end ---获取所有的 table function DBMgr:getDBTableList() if not self.dbTableListDirty then return self.dbTableList end self.dbTableList = {} for i, dbTable in ipairs(self.userDBTableList or {}) do table.insert(self.dbTableList, dbTable) end for i, dbTable in ipairs(self.roleDBTableList or {}) do table.insert(self.dbTableList, dbTable) end for i, dbTable in ipairs(self.deviceDBTableList or {}) do table.insert(self.dbTableList, dbTable) end self.dbTableListDirty = false return self.dbTableList end ---根据 table name 获取 table function DBMgr:getDBTableByName(name) local dbTableList = self:getDBTableList() for _, db in ipairs(dbTableList) do if name == db.name then return db end end end ---删除数据库 function DBMgr:removeCurDB(cb) if self.userDB then self:closeCurUserDB() self.userDB:remove() end if self.roleDB then self:closeCurRoleDB() self.roleDB:remove() end if self.deviceDB then self.deviceDB:remove() end if cb then cb() end end ExtendGameObject<local gameObjetsAttrsMap = {} local SpriteRenderer = CS.UnityEngine.SpriteRenderer local LuaHelper = CS.LuaHelper local RectTransform = CS.UnityEngine.RectTransform local GameObject = CS.UnityEngine.GameObject local Object = CS.UnityEngine.Object local GameObjectCls,GameObjectCls__index = HackCSharpClass(CS.UnityEngine.GameObject) ---@diagnostic disable-next-line: duplicate-set-field GameObjectCls.__index = function(ud, k) local v = rawget(GameObjectCls, k) -- lua扩展的方法或者属性 if v ~= nil then return v end local v = GameObjectCls__index(ud, k) -- c#的方法或者属性 if v ~= nil then return v end local ktype = type(k) if ktype == "table" then local meta = getmetatable(k) if meta and meta.__call then --Find Component 传入c#类的时候获取应对组件 local comp = ud:GetComponent(typeof(k)) if comp then return comp end end end --GameObject的lua userdata对象上挂的lua属性 local attrs = gameObjetsAttrsMap[ud] if not attrs then attrs = {} gameObjetsAttrsMap[ud] = attrs -- local name = ud.name -- print("luaGC GameObject的lua关联添加"..name) if Event then Event.add( ud, Event.OnDestroy, function() -- print("luaGC GameObject的lua关联析构"..name) gameObjetsAttrsMap[ud] = nil end ) end end return attrs[k] end local GameObjectCls__newindex = GameObjectCls.__newindex GameObjectCls.__newindex = function(ud, k, v) -- GameObjectCls__newindex(ud,k,v) -- 如果k是c#的属性,会失效,get不影响,set影响。相当于GameObject的属性setter都不可用,GameObject的setter在GameObjectExtend.cs里都有等价函数(GameObject能挂属性的代价)。 local attrs = gameObjetsAttrsMap[ud] if not attrs then attrs = {} gameObjetsAttrsMap[ud] = attrs -- local name = ud.name -- print("luaGC GameObject的lua关联添加"..name) if Event then --Event不一定存在 Event.add( ud, Event.OnDestroy, function() -- print("luaGC GameObject的lua关联析构"..name) gameObjetsAttrsMap[ud] = nil end ) end end attrs[k] = v end local function recur(go, cb) cb(go) for i = 0, go.transform.childCount - 1 do local child = go.transform:GetChild(i).gameObject recur(child, cb) end end GameObjectCls.TraverseChildrenRecusivly = function(self, cb) recur(self, cb) end --Unity的坑,如果节点A.activeSelf == false,删除父节点,节点A的OnDestroy可能不触发 local originDestroy = rawget(GameObject, "__originDestroy") or CS.UnityEngine.GameObject.Destroy rawset(GameObject, "__originDestroy", originDestroy) rawset( GameObject, "Destroy", function(go, time) if not CS.LuaHelper.IsNull(go) then local TraverseChildrenRecusivly = GameObjectCls.TraverseChildrenRecusivly if TraverseChildrenRecusivly then TraverseChildrenRecusivly( go, function(child) if not child.activeSelf and child.SetActive then child:SetActive(true) end end ) if time then originDestroy(go, time) else originDestroy(go) end else print("Failed To Destroy!") print(typeof(go), debug.traceback()) print(go.name) end end end ) GameObjectCls.SetVisible = function(self, visible, recursively) local coms if not recursively then coms = self:GetComponents(typeof(CS.UnityEngine.Renderer)) else coms = self:GetComponentsInChildren(typeof(CS.UnityEngine.Renderer)) end for _,com in cs_ipairs(coms)do com.enabled = visible end end GameObjectCls.AddEvent = function(self, ev, fn, obj) Event.add(self, ev, fn, obj) end GameObjectCls.RmEvent = function(self, ev, fn, obj) Event.rm(self, ev, fn, obj) end -- GameObjectCls.Clone = function(self, transform, meshlist) -- local copy = transform and GameObject.Instantiate(self, transform) or GameObject.Instantiate(self) -- local list1 = meshlist or self:GetComponentsInChildren(typeof(MeshRenderer)) -- local list2 = copy:GetComponentsInChildren(typeof(MeshRenderer)) -- for i = 0, list2.Length - 1 do -- local m1, m2 = list1[i], list2[i] -- if m1.name == m2.name then -- m2.lightmapIndex = m1.lightmapIndex -- m2.lightmapScaleOffset = m1.lightmapScaleOffset -- else -- print("[GameObject][InstantiateWithLightmap]lightmap dismatch", debug.traceback()) -- end -- end -- return copy -- end GameObjectCls.SearchPattern = function(self,name, recurse) recurse = recurse or false local result = self:_SearchPattern(name, recurse) local tbl = {} for i = 0,result.Count - 1 do table.insert(tbl,result[i]) end return tbl end GameObjectCls.ChildPattern = function(self,name) local result = self:_ChildPattern(name) local tbl = {} for i = 0,result.Count - 1 do table.insert(tbl,result[i]) end return tbl end -- 返回子节点,非递归 GameObjectCls.getChildrens = function (self) local childrens = {} for i = 1, self.transform.childCount do local item = self.transform:GetChild(i - 1).gameObject table.insert(childrens,item) end return childrens end GameObjectCls.Delay = function(self,time,cb) return self:RunAction(ua.Sequence({ ua.Delay(time), ua.cb(cb), })) end GameObjectCls.Step = function(self,cb,interval) if interval then return self:RunAction(ua.RepeatForever(ua.Sequence({ ua.Delay(interval), ua.cb(cb) }))) else return self:RunAction(ua.Step(cb)) end end GameObjectCls.__Sound__ = function(self) local audiosCom = self:GetComponent(typeof(CS.AudiosComponent)) if not audiosCom then audiosCom = self:AddComponent(typeof(CS.AudiosComponent)) end return audiosCom end GameObjectCls.PlaySound = function(self,assetInfo,cb) local com = self:__Sound__() local audioClip local t = type(assetInfo) if t == "table" then audioClip = Res.loadAsset(assetInfo[1]) elseif t == "string" then audioClip = Res.loadAsset(assetInfo) else-- is AudioClip audioClip = assetInfo end if audioClip then return com:Play(audioClip,cb) end end GameObjectCls.StopSound = function (self, audioClip) if audioClip then audioClip:Stop() end end GameObjectCls.PlayVoice = function(self,assetInfo,cb) if self.__playVoiceRet then self.__playVoiceRet:Stop() end self.__playVoiceRet = GameObjectCls.PlaySound(self,assetInfo,cb) end GameObjectCls.PlaySoundLoop = function(self,assetInfo,isBgMusic) local com = self:__Sound__() local audioClip local t = type(assetInfo) if t == "table" then audioClip = Res.loadAsset(assetInfo[1]) elseif t == "string" then audioClip = Res.loadAsset(assetInfo) else-- is AudioClip audioClip = assetInfo end if audioClip then if isBgMusic == nil then isBgMusic = false end return com:PlayLoop(audioClip,isBgMusic) end end GameObjectCls.PlayBgMusic = function(self,assetInfo) return self:PlaySoundLoop(assetInfo, true) end GameObjectCls.PlaySoundSeq = function(self,...) local args = {...} local cur local audios = {} local proxy = { audios = audios, stop = function(self) if self.current then self.current:Stop() self.current = nil end if self.action then self.gameObject:StopAction(self.action) self.action = nil end end, pause = function(self) if self.current then self.current:Pause() end end, resume = function(self) if self.current then self.current:Resume() end end, current = nil, action = nil, } local com = self:__Sound__() for _,arg in ipairs(args)do local t = type(arg) if t == "table" then cur = {clip = Res.loadAsset(arg[1])} table.insert(audios,cur) elseif t == "function" then if cur then if not cur.cb then cur.cb = arg else table.insert(audios,{cb = arg}) end else table.insert(audios,{cb = arg}) end elseif t == "number" then cur.delay = arg elseif t == "string" then cur = {clip = Res.loadAsset(arg)} table.insert(audios,cur) else error("GameObject.PlaySoundSeq arg type error:" .. t) end end local com = self:__Sound__() local function playNext() local audio = table.remove(audios,1) if audio then if audio.clip then proxy.current = com:Play(audio.clip,function() proxy.current = nil if audio.delay then proxy.action = self.gameObject:Delay(audio.delay,function() proxy.action = nil if audio.cb then audio.cb() end playNext() end) else if audio.cb then audio.cb() end playNext() end end) else if audio.cb then audio.cb() end playNext() end end end playNext() return proxy end GameObjectCls.StopAllSounds = function(self,ignoreLoop) if ignoreLoop == nil then ignoreLoop = false end local com = self:__Sound__() com:StopAll(ignoreLoop) end GameObjectCls.RemoveAllChildren = function(self) local trans = self.transform local Destroy = CS.UnityEngine.GameObject.Destroy for i = 0,trans.childCount - 1 do local child = trans:GetChild(i) Destroy(child.gameObject) end end GameObjectCls.WaitUntil = function(self,condition,cb) if condition() then--保证当前帧也判断一次 if cb then cb() end else self:RunAction(ua.Step(function() if condition() then if cb then cb() end return true else return false end end)) end end -- 设置到摄像机中心位置 GameObjectCls.SetPosInCameraCenter = function(self) local camera = CS.UnityEngine.GameObject.Find("MainCamera") if camera then local pos local parent = self:GetParent() if parent then pos = parent.transform:InverseTransformPoint(camera:GetWorldPosition()) else pos = camera:GetWorldPosition() end pos.z = 0 self:SetPosition(pos) end end GameObjectCls.GetOpacity = function(self) local spr = self:GetComponent(typeof(SpriteRenderer)) --这里兼容下ugui local maskableGraphic = self:GetComponent(typeof(CS.UnityEngine.UI.MaskableGraphic)) if spr then local c = spr.color return c.a elseif maskableGraphic then local c = maskableGraphic.color return c.a end end -- alpha range: [0,1] GameObjectCls.SetOpacity = function(self,alpha) local spr = self:GetComponent(typeof(SpriteRenderer)) --这里兼容下ugui local maskableGraphic = self:GetComponent(typeof(CS.UnityEngine.UI.MaskableGraphic)) -- 兼容spine local spineAnimation = self:GetComponent(typeof(CS.Spine.Unity.SkeletonAnimation)); if spr then local c = spr.color c.a = alpha spr.color = c elseif maskableGraphic then local c = maskableGraphic.color c.a = alpha maskableGraphic.color = c elseif spineAnimation then -- local spineMaterial = spineAnimation.material -- local spineMater = spineAnimation:GetMaterial(CS.Spine.Unity.MaterialType.Default) -- local material = spineAnimation.skeletonDataAsset.atlasAssets[0].materials[0].material -- local c = spineMaterial.color -- c.a = alpha -- spineMaterial.color = c end end local IsNull = CS.LuaHelper.IsNull GameObjectCls.RunAtNextFrame = function(self,cb) CS.LuaGlobal.instance:runAtNextFrame(function() if not IsNull(self) and self.activeSelf then cb() end end) end GameObjectCls.RunAtNextNFrame = function(self,N,cb) local n = 0 local function run() n = n + 1 if n == N then cb() else self:RunAtNextFrame(run) end end run() end GameObjectCls.RunTimer = function(self,dt,func) return self:RunAction(ua.RepeatForever(ua.Sequence({ ua.Delay(dt), ua.cb(func) }))) end GameObjectCls.GetAnchoredPositionX = function(self,x) local rectTrans = self:GetComponent(typeof(RectTransform)) if rectTrans then return rectTrans.anchoredPosition3D.x end end GameObjectCls.GetAnchoredPositionY = function(self,x) local rectTrans = self:GetComponent(typeof(RectTransform)) if rectTrans then return rectTrans.anchoredPosition3D.y end end GameObjectCls.SetAnchoredPositionX = function(self,x) local rectTrans = self:GetComponent(typeof(RectTransform)) if rectTrans then local p = rectTrans.anchoredPosition3D p.x = x rectTrans.anchoredPosition3D = p end end GameObjectCls.SetAnchoredPositionY = function(self,y) local rectTrans = self:GetComponent(typeof(RectTransform)) if rectTrans then local p = rectTrans.anchoredPosition3D p.y = y rectTrans.anchoredPosition3D = p end end GameObjectCls.GetXPath = function(self) local curr = self.transform local xpath = nil while curr ~= nil do if xpath then xpath = curr.gameObject.name .. "/" .. xpath else xpath = curr.gameObject.name end curr = curr.parent end return xpath end GameObjectCls.SeekByXPath = function(self, xpath) local splits = string.split(xpath, "/") local curr = self ---@diagnostic disable-next-line: param-type-mismatch for _, split in ipairs(splits) do curr = curr:Child(split) if not curr then break end end return curr end GameObjectCls.RemoveAllComponentsByType = function(self, comType) local coms = self:GetComponents(comType) for _, com in cs_ipairs(coms) do Object.Destroy(com) end end GameObjectCls.SeekInParentHierarchy = function(self, name) local function func(go) local parent = go.transform.parent if not parent then return false end local parentGo = go.transform.parent.gameObject if parentGo.name == name then return true end return func(parentGo) end return func(self) endLocalStorageMgrQ ---@class LocalStorageMgr:LuaStaticClass local LocalStorageMgr = defClassStatic("LocalStorageMgr") function LocalStorageMgr:init() -- KVMgr KVMgr:addDBTableCls(KVTable, false, false, "LocalStorageTable") KVMgr:addDBTableCls(KVTable, false, false, "LocalDebugTable") KVMgr:addDBTableCls(KVTable, true, false, "LocalStorageTableForUser") end function LocalStorageMgr:get(key, defaultValue) return KVMgr.LocalStorageTable:get(key, defaultValue) end function LocalStorageMgr:set(key, value) return KVMgr.LocalStorageTable:set(key, value) end function LocalStorageMgr:getKeys() return KVMgr.LocalStorageTable:getKeys() end function LocalStorageMgr:getDebug(key, defaultValue) return KVMgr.LocalDebugTable:get(key, defaultValue) end function LocalStorageMgr:setDebug(key, value) return KVMgr.LocalDebugTable:set(key, value) end function LocalStorageMgr:getDebugKeys() return KVMgr.LocalDebugTable:getKeys() end function LocalStorageMgr:getUserKeys() if not KVMgr.LocalStorageTableForUser then return {} end return KVMgr.LocalStorageTableForUser:getKeys() end function LocalStorageMgr:getForUser(key, defaultValue) return KVMgr.LocalStorageTableForUser:get(key, defaultValue) end function LocalStorageMgr:setForUser(key, value) return KVMgr.LocalStorageTableForUser:set(key, value) end ApplovinMgr --[[ author:{zhangpeng} time:2024-04-25 14:52:46 ]] local ApplovinMgr, super = defClassStatic("ApplovinMgr") local LOG_TAG = "ApplovinMgr" local AdConst = ApplovinConst local IOS_NATIVE_CLASS_NAME = "AppLovinMgr" local JavaADClass = "com/fy/xgame/tilelink/ads/AdManager" function ApplovinMgr:init() self:registLuaCallBack() end function ApplovinMgr:registLuaCallBack() local callback = function(value) if value == "initServer" then return end local decoded_data = json.decode(value) local type = decoded_data.type local code = decoded_data.code local placement = decoded_data.placement printInfo(LOG_TAG, string.format("Ad Type: %s Code: %s", type, code)) if type == AdConst.AdType.EAdTypeRewardedAd then -- 激励视频 elseif type == AdConst.AdType.EAdTypeInterstitialAd then -- 插屏广告 end self:onAdStateCodeChange(code, decoded_data) printInfo(LOG_TAG, "onAdStateCodeCb to lua suc") end if Device.isIOS() then local param = { onAdStateCodeCb = callback, } luaoc.callStaticMethod(IOS_NATIVE_CLASS_NAME, "registLuaCallback", param) elseif Device.isAndroid() then luaj.callStaticMethod(JavaADClass, "registLuaCallback", { callback }) end end function ApplovinMgr:adDidLaunch() if Device.isIOS() then luaoc.callStaticMethod(IOS_NATIVE_CLASS_NAME, "adDidLaunch") elseif Device.isAndroid() then luaj.callStaticMethod(JavaADClass, "adDidLaunch", { }) end end -- 显示一个广告 function ApplovinMgr:showRewardAdByPlacement(placement, showcb) if not Device:isIOS() and not Device:isAndroid() then UIComsTool:showToast(TextCfgParse:getTextStr("ad_not_mobile_device",TextCfgParse.Lan.en), 2) return end local actionNode = SceneMgr:getCurScene().logicNode self.startLoadTime = os.time() actionNode.gameObject:WaitUntil( function () -- condition local ready = self:isRewardedAdReady() self.readyOnce = ready if ready then printInfo(LOG_TAG, string.format("广告就绪:%s",ready)) else local useTime = os.time() - self.startLoadTime if useTime > ApplovinConst.adLoadTimeMax then return true end end UIComsTool:showLoading() return ready end, function () if self.readyOnce then -- show ad if showcb then showcb() end UIComsTool:hideLoading(true) printInfo(LOG_TAG, "开始展示广告") ApplovinMgr:showRewardedAd(placement) else UIComsTool:hideLoading(true) UIComsTool:showToast("no ads available") end end ) end -- 根据广告标识符向服务器请求奖励 function ApplovinMgr:requestAwardByPlacementType(placement) end -- ad状态码发生变化时的回调 function ApplovinMgr:onAdStateCodeChange(code, decodeData) if code == AdConst.AdCode.ESAdCodeLoadSucceeded then local data = json.decode(decodeData.data) if data then printInfo(LOG_TAG,"code::" .. code) printInfo(LOG_TAG,"Ad 单元标识符: " .. data[AdConst.DataEnum.ad_unit_identifier]) printInfo(LOG_TAG,"Ad 国家代码: " .. data[AdConst.DataEnum.country]) printInfo(LOG_TAG,"Ad 创意标识符: " .. data[AdConst.DataEnum.creative_identifier]) printInfo(LOG_TAG,"Ad 网络名称: " .. data[AdConst.DataEnum.network_name]) printInfo(LOG_TAG,"Ad 网络放置位置: " .. data[AdConst.DataEnum.network_placement]) printInfo(LOG_TAG,"Ad 平台: " .. data[AdConst.DataEnum.platform]) printInfo(LOG_TAG,"Ad 收入: " .. data[AdConst.DataEnum.revenue]) printInfo(LOG_TAG,"Ad 收入精度: " .. data[AdConst.DataEnum.revenue_precision]) end UIComsTool:showToast(TextCfgParse:getTextStr("ad_load_suc"),1) -- 通知红点刷新 elseif code == AdConst.AdCode.ESAdCodeDidReward then -- self:updateAdTodayWatchTimes() -- self:updateAllAdWatchTimes() local placement = decodeData.placement -- 发领奖消息 Msg.send(Msg.AD_REWARD_VIEWO_WATCH_SUC, {placeId = placement}) elseif code == AdConst.AdCode.ESAdCodeHide then -- todo:: 恢复之前被暂停的游戏音乐等 end end -- 广告测试模式 function ApplovinMgr:showMediationDebugger() if Device.isIOS() then luaoc.callStaticMethod(IOS_NATIVE_CLASS_NAME, "showMediationDebugger") elseif Device.isAndroid() then luaj.callStaticMethod(JavaADClass, "showMediationDebugger", { }) end end -----------------------------------banner------------------------------------- function ApplovinMgr:loadBannerAd() if Device.isIOS() then elseif Device.isAndroid() then luaj.callStaticMethod(JavaADClass, "loadBannerAd", { }) end end -----------------------------------激励视频------------------------------------- function ApplovinMgr:setNeedLoadRewardedAd(need) local _need if need == true then _need = "true" elseif need == false then _need = "false" end if Device.isIOS() then local param = {need = _need} luaoc.callStaticMethod(IOS_NATIVE_CLASS_NAME, "setNeedLoadRewardedAdWithDictionary", param) elseif Device.isAndroid() then luaj.callStaticMethod(JavaADClass, "setNeedLoadRewardedAd", { need }) end end function ApplovinMgr:loadRewardedAd() if Device.isIOS() then luaoc.callStaticMethod(IOS_NATIVE_CLASS_NAME, "loadRewardedAd") elseif Device.isAndroid() then luaj.callStaticMethod(JavaADClass, "loadRewardedAd", { }) end end -- 主动检测是否有可用的激励视频 function ApplovinMgr:isRewardedAdReady() if Device.isIOS() then local ok,ret = luaoc.callStaticMethod(IOS_NATIVE_CLASS_NAME, "isRewardedAdReady") return ret elseif Device.isAndroid() then local ok, re = luaj.callStaticMethod(JavaADClass, "isRewardedAdReady", {}, "()Z") return re else return false end end function ApplovinMgr:showRewardedAd(placementName) if Device.isIOS() then local param = {placement = placementName} luaoc.callStaticMethod(IOS_NATIVE_CLASS_NAME, "showRewardedAdWithDictionary", param) elseif Device.isAndroid() then luaj.callStaticMethod(JavaADClass, "showRewardedAd", {placementName}) end end ----------------------------------- 广告观看次数统计 ------------------------------------- -- 刷新每日观看次数 function ApplovinMgr:updateAdTodayWatchTimes() local userData = User:getUserTableData() local curtimes = userData:getTodayAdWatchTimes() curtimes = curtimes + 1 userData:setTodayAdWatchTimes(curtimes) userData:save() end -- 刷新总观看次数 function ApplovinMgr:updateAllAdWatchTimes() local userData = User:getUserTableData() local cur_all_times = userData:getAllAdWatchTimes() cur_all_times = cur_all_times + 1 userData:setAllAdWatchTimes(cur_all_times) userData:save() end -- 返回今日观看次数 function ApplovinMgr:getTodayAdWatchTimes() local userData = User:getUserTableData() local todaytimes = userData:getTodayAdWatchTimes() return todaytimes end -- 返回总观看次数 function ApplovinMgr:getAllAdWatchTimes() local userData = User:getUserTableData() local cur_all_times = userData:getAllAdWatchTimes() return cur_all_times end -- 根据广告位名字取奖励内容 function ApplovinMgr:getAwardByPlacementId(placement) end ApplovinMgr:init()main6require("framework/core/base/extend/Ext") require("framework/core/base/extend/ExtendGameObject") require("framework/core/base/extend/ExtendRect") require("framework/core/base/extend/ExtendBounds") require("framework/core/base/extend/ExtendScene") require("framework/core/base/extend/ExtendPlayableDirector")mainrequire("framework/device/main") require("framework/core/main") require("framework/cos/main") require("framework/ui/main") require("framework/network/main") require("framework/platform/ext/main") require("framework/platform/base/main") UIDialogSimple(--[[ 只包含一个文字和一个按钮的对话框 author:{zhangpeng} time:2025-04-07 12:26:16 ]] local UIDialogSimple,super= defClass("UIDialogSimple",UILayer) local TMPUGUI = CS.TMPro.TextMeshProUGUI local UnityEngine = CS.UnityEngine local GameObject = UnityEngine.GameObject local LOGTAG = "UIDialogSimple" function UIDialogSimple:ctor() super.ctor(self) self.R = Res.loadResLink("framework/ui/uicoms/reslink/uidialogreslink") end function UIDialogSimple:onLoad() self:initView() end function UIDialogSimple:initView() self.ui = GameObject.Instantiate(self.R.dialog_box_simple) self:addChild(self.ui) self:useTweenOnOpen(self.ui) -- ok local close = self.ui:Seek("btn") util.ugui.addButtonClickEvent(close, function() self:close() end) end -- 设置对话框内容 function UIDialogSimple:setContentText(text) self.ui:Seek("content")[TMPUGUI].text = text self.content = text return self end function UIDialogSimple:onExit() super.onExit(self) end return UIDialogSimpleluaj_  local luaj = {} local callStaticMethod = LuaJavaBridge.callStaticMethod local queue_action = require("framework/platform/ext/queue_action") luaj.S = "Ljava/lang/String;" function luaj.getArgsSig(args, retSig) if not Device.isAndroid() then return end local sig = {"("} for i, v in ipairs(args) do local t = type(v) if t == "number" then if v == math.floor(v) then sig[#sig + 1] = "I" else sig[#sig + 1] = "F" end elseif t == "boolean" then sig[#sig + 1] = "Z" elseif t == "function" then sig[#sig + 1] = "I" else sig[#sig + 1] = luaj.S end end sig[#sig + 1] = ")" sig[#sig + 1] = retSig or "V" return table.concat(sig) end local LUAJ_ERR_CODE = { LUAJ_ERR_OK = 0, LUAJ_ERR_TYPE_NOT_SUPPORT = -1, LUAJ_ERR_INVALID_SIGNATURES = -2, LUAJ_ERR_METHOD_NOT_FOUND = -3, LUAJ_ERR_EXCEPTION_OCCURRED = -4, LUAJ_ERR_VM_THREAD_DETACHED = -5, LUAJ_ERR_VM_FAILURE = -6, LUAJ_ERR_CLASS_NOT_FOUND = -7 } local LUAJ_ERR_NAME = {} for k, v in pairs(LUAJ_ERR_CODE) do LUAJ_ERR_NAME[v] = k end -- 调用java类的接口。 -- @function [parent=#luaj] callStaticMethod -- @param string className java类名 -- @param string methodName java类静态方法名 -- @param table args java类静态方法所需要的各种参数 数组 -- @param string sig java类方法的签名 -- @return boolean#boolean ret (return value: bool) ok, mixed ret ok为是否调用成功, ok为true时,ret为java方法的返回值,ok为false时,ret为出错原因 function luaj.callStaticMethod(className, methodName, args, sig) args = args or {} sig = sig or luaj.getArgsSig(args) --需要在java层做QueueAction处理 --这里做QueueAction时机晚了,已经回到了主线程 -- queue_action.table_process(args) -- printInfo("luaj", 'callStaticMethod("%s",\t"%s",\targs,\t"%s"', className, methodName, sig) local ok, ret, javaException = callStaticMethod(className, methodName, args, sig) if not ok then error( string.format("luaj error:%s,class:%s,method:%s,javaException:%s,sig:%s", tostring(LUAJ_ERR_NAME[ret]), tostring(className), tostring(methodName), tostring(javaException), tostring(sig)) ) end return ok, ret end return luaj TableUtil-- -- table扩展工具类,对table不支持的功能执行扩展 -- 注意: -- 1、所有参数带hashtable的函数,将把table当做哈希表对待 -- 2、所有参数带array的函数,将把table当做可空值数组对待 -- 3、所有参数带tb的函数,对表通用,不管是哈希表还是数组 -- -- 计算哈希表长度 local function count(hashtable) local count = 0 for _,_ in pairs(hashtable) do count = count + 1 end return count end -- 计算数据长度 local function length(array) if array.n ~= nil then return array.n end local count = 0 for i,_ in pairs(array) do if count < i then count = i end end return count end -- 设置数组长度 local function setlen(array, n) array.n = n end -- 获取哈希表所有键 local function keys(hashtable) local keys = {} for k, v in pairs(hashtable) do keys[#keys + 1] = k end return keys end -- 获取哈希表所有值 local function values(hashtable) local values = {} for k, v in pairs(hashtable) do values[#values + 1] = v end return values end -- 合并哈希表:将src_hashtable表合并到dest_hashtable表,相同键值执行覆盖 local function merge(dest_hashtable, src_hashtable) for k, v in pairs(src_hashtable) do dest_hashtable[k] = v end end -- 合并数组:将src_array数组从begin位置开始插入到dest_array数组 -- 注意:begin <= 0被认为没有指定起始位置,则将两个数组执行拼接 local function insertto(dest_array, src_array, begin) assert(begin == nil or type(begin) == "number") if begin == nil or begin <= 0 then begin = #dest_array + 1 end local src_len = #src_array for i = 0, src_len - 1 do dest_array[i + begin] = src_array[i + 1] end end -- 从数组中查找指定值,返回其索引,没找到返回false local function indexof(array, value, begin) for i = begin or 1, #array do if array[i] == value then return i end end return false end -- 从哈希表查找指定值,返回其键,没找到返回nil -- 注意: -- 1、containskey用hashtable[key] ~= nil快速判断 -- 2、containsvalue由本函数返回结果是否为nil判断 local function keyof(hashtable, value) for k, v in pairs(hashtable) do if v == value then return k end end return nil end -- 从数组中删除指定值,返回删除的值的个数 function table.removebyvalue(array, value, removeall) local remove_count = 0 for i = #array, 1, -1 do if array[i] == value then table.remove(array, i) remove_count = remove_count + 1 if not removeall then break end end end return remove_count end -- 遍历写:用函数返回值更新表格内容 local function map(tb, func) for k, v in pairs(tb) do tb[k] = func(k, v) end end -- 遍历读:不修改表格 local function walk(tb, func) for k,v in pairs(tb) do func(k, v) end end -- 按指定的排序方式遍历:不修改表格 local function walksort(tb, sort_func, walk_func) local keys = table.keys(tb) table.sort(keys, function(lkey, rkey) return sort_func(lkey, rkey) end) for i = 1, table.length(keys) do walk_func(keys[i], tb[keys[i]]) end end -- 过滤掉不符合条件的项:不对原表执行操作 local function filter(tb, func) local filter = {} for k, v in pairs(tb) do if not func(k, v) then filter[k] = v end end return filter end -- 筛选出符合条件的项:不对原表执行操作 local function choose(tb, func) local choose = {} for k, v in pairs(tb) do if func(k, v) then choose[k] = v end end return choose end -- 获取数据循环器:用于循环数组遍历,每次调用走一步,到数组末尾从新从头开始 local function circulator(array) local i = 1 local iter = function() i = i >= #array and 1 or i + 1 return array[i] end return iter end local function dump(tb, dump_metatable, max_level) local lookup_table = {} local level = 0 local rep = string.rep local dump_metatable = dump_metatable local max_level = max_level or 1 local function _dump(tb, level) local str = "\n" .. rep("\t", level) .. "{\n" for k,v in pairs(tb) do local k_is_str = type(k) == "string" and 1 or 0 local v_is_str = type(v) == "string" and 1 or 0 str = str..rep("\t", level + 1).."["..rep("\"", k_is_str)..(tostring(k) or type(k))..rep("\"", k_is_str).."]".." = " if type(v) == "table" then if not lookup_table[v] and ((not max_level) or level < max_level) then lookup_table[v] = true str = str.._dump(v, level + 1, dump_metatable).."\n" else str = str..(tostring(v) or type(v))..",\n" end else str = str..rep("\"", v_is_str)..(tostring(v) or type(v))..rep("\"", v_is_str)..",\n" end end if dump_metatable then local mt = getmetatable(tb) if mt ~= nil and type(mt) == "table" then str = str..rep("\t", level + 1).."[\"__metatable\"]".." = " if not lookup_table[mt] and ((not max_level) or level < max_level) then lookup_table[mt] = true str = str.._dump(mt, level + 1, dump_metatable).."\n" else str = str..(tostring(mt) or type(mt))..",\n" end end end str = str..rep("\t", level) .. "}," return str end return _dump(tb, level) end table.count = count table.length = length table.setlen = setlen table.keys = keys table.values = values table.merge = merge table.insertto = insertto table.indexof = indexof table.keyof = keyof table.map = map table.walk = walk table.walksort = walksort table.filter = filter table.choose = choose table.circulator = circulator table.dump = dump main_G.json = raw_require("rapidjson") if Device.isIOS() then _G.luaoc = require("framework/platform/ext/luaoc") elseif Device.isAndroid() then _G.luaj = require("framework/platform/ext/luaj") end KVMgr ---@class KVMgr:DBMgr local KVMgr = defClassStatic("KVMgr", DBMgr) function KVMgr:init(aes_key) KVMgr.super.init(self, false) self.aes_key = aes_key self.logTag = "KVMgr" self.userDBName = "user.txt" self.roleDBName = "role.txt" self.deviceDBName = "device.txt" self.dbCls = KVDatabase Msg.add(Msg.APP_INIT_FINISH, function (id) printVerbose(self.logTag, "onmsg APP_INIT_FINISH id:%s", id) self:openCurDeviceDB() end) end function KVMgr:getKey() return self.aes_key end SocketMgr!--[[ author:{zhangpeng} time:2023-08-24 11:28:07 todo: 所有消息头用table,消息类型+pb 加msg名字,打印使用 ]] local SocketMgr = defClassStatic("SocketMgr") local LOG_TAG = SocketMgr.__cls_name local pb = raw_require("pb") local SocketConnection = CS.SocketConnection -- 心跳时间3秒 local HEART_DELTA = 5 local NEED_SOCKET = false -- 是否需要心跳 local NEED_HEART = false function SocketMgr:init() printInfo(LOG_TAG, "init") self.useLongConnection = true self.historyPackList = {} self.host = "" self.port = 4000 self.maxCount = 5 self.heartDelta = 0 self.handAckSuc = false -- 握手成功 self:clear() Timer:add( function(dt) self:updatePackSend(dt) end ) if NEED_SOCKET then Timer:nextTick( function() self:initServerInfo( function(serverInfo) self:setServerInfo(serverInfo) self:connect() printInfo(LOG_TAG, "init, host:%s, port:%s", self.host, self.port) end ) end ) end end function SocketMgr:initServerInfo(cb) local serverInfo = { serverIp = "192.144.239.125", serverPort = 4000, } self:setServerInfo(serverInfo) cb(serverInfo) end function SocketMgr:setServerInfo(serverInfo) self.host = serverInfo.serverIp self.port = serverInfo.serverPort self.serverInfo = serverInfo end function SocketMgr:connect() -- 建立连接 if not self.sock_con then self.sock_con = SocketConnection(self.host, self.port,self.useLongConnection) printInfo(LOG_TAG," >>>lua socket connect suc:%s:%d<<< ",self.host,self.port) -- 建立连接之后立即握手 self:sendHandShakeRequest() end end function SocketMgr:startHeartTimer() printInfo(LOG_TAG,"启动心跳...") self.startHeart = true self.heartCount = 0 if self.heartTimerId then Timer:rem(self.heartTimerId) self.heartTimerId = nil end self.heartTimerId = Timer:add( function (dt) self:doHeartUpdate() end,HEART_DELTA ) end function SocketMgr:doHeartUpdate() if not self.startHeart then return end self.heartCount = self.heartCount + 1 if self:isSocketEnable() then self:sendHeart() end -- printInfo(LOG_TAG,"heart ping pong:%s",self.heartCount) end function SocketMgr:clear() self.curCount = 0 self.isSendPackDict = {} self.toSendPackList = {} end -- 发送待发队列里的数据包 function SocketMgr:updatePackSend() if not self:isHostAndPortReady() then return end if #self.toSendPackList <= 0 then -- printInfo(LOG_TAG,"toSendPackList:%s",#self.toSendPackList) return end if self.curCount >= self.maxCount then -- printWarn(LOG_TAG,"curCount:%s maxCount:%s",self.curCount,self.maxCount) return end local pack = table.remove(self.toSendPackList, 1) self:sendByPack(pack) end -- 统一的发送接口 function SocketMgr:send(head, body, reqProtoName, rspProtoName, callback) local pack = PBSocketPack.new(head, body, reqProtoName, rspProtoName, callback) self:sendByPack(pack) end -- 发送握手请求 function SocketMgr:sendHandShakeRequest() local head = CmdDef.MsgType.REQ local body = { ["userId"] = HttpCmdMgr:getUserId(), ["token"] = HttpCmdMgr:getHeaderToken(), ["deviceId"] = "zpios", ["version"] = "1.0.0", } local pack = PBSocketPack.new(head, body,"handshake.Request", "handshake.Response", function (suc,rspData) if suc then self:setHandShakeRspSalt(rspData.rspBody.salt) self:sendHandShakeAck() else end end ) self:sendByPack(pack) end function SocketMgr:sendHandShakeAck() local head = CmdDef.MsgType.ACK PBSocketPack.headProtoName = nil local pack = PBSocketPack.new(head, nil,nil, nil, function (suc,rspData) if suc then self.handAckSuc = true printInfo(LOG_TAG,"握手ACK返回Suc") Msg.send(Msg.SOCKET_MSG_ACK_SUC, rspData) if NEED_HEART then self:startHeartTimer() end else end end ) printInfo(LOG_TAG,"发送握手ACK...") self:sendByPack(pack) end function SocketMgr:sendHeart() local head = CmdDef.MsgType.HEART PBSocketPack.headProtoName = nil local pack = PBSocketPack.new(head, nil,nil, nil, function (suc,rspData) if suc then printInfo(LOG_TAG,"rev heart pong...") end end ) printInfo(LOG_TAG,"send heart ping...") self:sendByPack(pack) end function SocketMgr:stopHeart() self.startHeart = false end function SocketMgr:sendByPack(pack) if not NetworkStateUtil:isReachable() then local errorCode = -999999 local errorMsg = "网络不可用" pack.callback(false, {errorCode = errorCode, errorMsg = errorMsg}) return end if self.curCount >= self.maxCount or (not self:isHostAndPortReady()) then table.insert(self.toSendPackList, pack) return end self.curCount = self.curCount + 1 local seq = pack:getSeq() if self.isSendPackDict[seq] then printWarn(LOG_TAG, "sendByPack, pack is send, seq:%s", seq) return end self.isSendPackDict[seq] = pack local head = nil if type(pack.reqHead) == "table" then local encode_data = self:encodePB(pack.reqHead.headData, pack.reqHeadProtoName) local headBytes = {} table.insert(headBytes, string.char(pack.reqHead.msg_type)) for i = 1, #encode_data do table.insert(headBytes, string.char(string.byte(encode_data, i))) end head = table.concat(headBytes) elseif type(pack.reqHead) == "number" then head = string.char(pack.reqHead) end local body = nil if pack.reqBody and pack.reqBodyProtoName then body = self:encodePB(pack.reqBody, pack.reqBodyProtoName) end -- pack:printReq() pack:setReqTime() -- pack:setReqLength(#head + #(body or "")) if not self:isSocketEnable() then self:connect() end self.sock_con:Send( head, body, function(bool, _head, _body) self:afterSend(bool, _head, _body, seq) end ) pack:setConnection(self.sock_con) table.insert(self.historyPackList, pack) end function SocketMgr:encodePB(data, schemeName) if data == nil or schemeName == nil then return end return assert(pb.encode(schemeName, data)) end function SocketMgr:decodePB(data, schemeName) if data == nil or schemeName == nil then return end return assert(pb.decode(schemeName, data)) end function SocketMgr:afterSend(bool, head, body, seq) if self.curCount == 0 then printWarn(LOG_TAG, "afterSend, no pask is send") return end local pack = self.isSendPackDict[seq] if not pack then printWarn(LOG_TAG, "afterSend, pack not found, seq:%s", seq) return end self.curCount = self.curCount - 1 self.isSendPackDict[seq] = nil if not bool then local errorCode = head local errorMsg = body if errorCode == -1 then -- self:addFailHostAndPort(self.host, self.port) -- self:updateHostAndPort() end pack:printError(errorCode, errorMsg) pack.callback(false, {errorCode = errorCode, errorMsg = errorMsg}) return end pack.rspMsgType = self:getMsgTypeByHead(head) pack.rspBody = self:decodePB(body, pack.rspBodyProtoName) pack:printRsp() pack:setRspTime() pack:setRspLength(#head + #(body or "")) pack.callback(true, pack) end -- 消息类型取返回包头的前4个字节 function SocketMgr:getMsgTypeByHead(head) return string.byte(head, 1) end function SocketMgr:isSocketEnable() if self.sock_con then if self.sock_con.isConnected then return true end end return false end function SocketMgr:isHostAndPortReady() if self.host == nil or self.port == nil then return false end return true end function SocketMgr:setHandShakeRspSalt(salt) CS.AESCTR.SetAckResponseSlat(salt) self.handShakeRspSalt = salt end function SocketMgr:getHandShakeRspSalt() return self.handShakeRspSalt end Event--@func Event.add(go, ev, fn, obj=nil) --@desc 给go绑定一个事件回调,同GameObject.AddEvent --@arg ev:int/string,事件名称或事件id(两种都支持),见Event --@arg fn:function,回调函数,参数见unity定义 --@func Event.remove(go, ev=nil, fn=nil, obj=nil) --@desc 删除给go绑定的事件回调,条件判断 (ev && fn && obj),同GameObject.RmEvent --@arg ev:int/string,事件名称或事件id(两种都支持),见Event,ev==nil则表示所有事件ev==* --@arg fn:function,回调函数,参数见unity定义,fn==nil则表示所有函数fn==* ------------------------------------ --Event = nil--ignore UnityEngine.Event -- local GameObject = CS.UnityEngine.GameObject local Object = CS.UnityEngine.Object local LOGTAT = "Event" ---@class Event:LuaStaticClass local Event = defClassStatic("Event") Event.Awake = 1 Event.Start = 2 Event.Reset = 3 Event.Update = 4 Event.LateUpdate = 5 Event.FixedUpdate = 6 Event.OnDisable = 7 Event.OnEnable = 8 Event.OnDestroy = 9 Event.OnApplicationFocus = 10 Event.OnApplicationPause = 11 Event.OnApplicationQuit = 12 Event.OnPreCull = 13 Event.OnPreRender = 14 Event.OnWillRenderObject = 15 Event.OnRenderObject = 16 Event.OnPostRender = 17 Event.OnRenderImage = 18 Event.OnBecameInvisible = 19 Event.OnBecameVisible = 20 Event.OnMouseDown = 21 Event.OnMouseDrag = 22 Event.OnMouseEnter = 23 Event.OnMouseExit = 24 Event.OnMouseOver = 25 Event.OnMouseUp = 26 Event.OnMouseUpAsButton = 27 Event.OnCollisionEnter = 28 Event.OnCollisionExit = 29 Event.OnCollisionStay = 30 Event.OnCollisionEnter2D = 31 Event.OnCollisionExit2D = 32 Event.OnCollisionStay2D = 33 Event.OnTriggerEnter = 34 Event.OnTriggerExit = 35 Event.OnTriggerStay = 36 Event.OnTriggerEnter2D = 37 Event.OnTriggerExit2D = 38 Event.OnTriggerStay2D = 39 Event.OnJointBreak = 40 Event.OnJointBreak2D = 41 Event.OnParticleCollision = 42 Event.OnParticleTrigger = 43 Event.OnTransformChildrenChanged = 44 Event.OnTransformParentChanged = 45 Event.OnControllerColliderHit = 46 Event.OnAnimationEvent = 47 Event.OnGUI = 48 Event.OnAnimatorMove = 49 Event.OnAnimatorIK = 50 Event.OnAudioFilterRead = 51 Event.OnBeginDrag = 52 Event.OnEndDrag = 53 Event.OnDrag = 54 Event.OnApplicationUnload = 55 Event.cslist = { CS.LuaEventAwake, CS.LuaEventStart, CS.LuaEventReset, CS.LuaEventUpdate, CS.LuaEventLateUpdate, CS.LuaEventFixedUpdate, CS.LuaEventOnDisable, CS.LuaEventOnEnable, CS.LuaEventOnDestroy, CS.LuaEventOnApplicationFocus, CS.LuaEventOnApplicationPause, CS.LuaEventOnApplicationQuit, CS.LuaEventOnPreCull, CS.LuaEventOnPreRender, CS.LuaEventOnWillRenderObject, CS.LuaEventOnRenderObject, CS.LuaEventOnPostRender, CS.LuaEventOnRenderImage, CS.LuaEventOnBecameInvisible, CS.LuaEventOnBecameVisible, CS.LuaEventOnMouseDown, CS.LuaEventOnMouseDrag, CS.LuaEventOnMouseEnter, CS.LuaEventOnMouseExit, CS.LuaEventOnMouseOver, CS.LuaEventOnMouseUp, CS.LuaEventOnMouseUpAsButton, CS.LuaEventOnCollisionEnter, CS.LuaEventOnCollisionExit, CS.LuaEventOnCollisionStay, CS.LuaEventOnCollisionEnter2D, CS.LuaEventOnCollisionExit2D, CS.LuaEventOnCollisionStay2D, CS.LuaEventOnTriggerEnter, CS.LuaEventOnTriggerExit, CS.LuaEventOnTriggerStay, CS.LuaEventOnTriggerEnter2D, CS.LuaEventOnTriggerExit2D, CS.LuaEventOnTriggerStay2D, CS.LuaEventOnJointBreak, CS.LuaEventOnJointBreak2D, CS.LuaEventOnParticleCollision, CS.LuaEventOnParticleTrigger, CS.LuaEventOnTransformChildrenChanged, CS.LuaEventOnTransformParentChanged, CS.LuaEventOnControllerColliderHit, CS.LuaEventOnAnimationEvent, CS.LuaEventOnGUI, CS.LuaEventOnAnimatorMove, CS.LuaEventOnAnimatorIK, CS.LuaEventOnAudioFilterRead, CS.LuaEventOnBeginDrag, CS.LuaEventOnEndDrag, CS.LuaEventOnDrag, CS.LuaEventOnApplicationUnload, } Event.goEvents = {} Event.__exited = false Event.add = function(go, eventId, cb) if Event.__exited then return end local events = Event.goEvents[go] if events == nil then events = {} Event.goEvents[go] = events end local cb_list = events[eventId] if not cb_list then cb_list = {} events[eventId] = cb_list local comp = go:AddComponent(typeof(Event.cslist[eventId])) comp:Bind(function(...) for _,cb in ipairs(cb_list) do cb(...) end end) end table.insert(cb_list,cb) return { remove = function() for i = #cb_list,1,-1 do if cb_list[i] == cb then table.remove(cb_list,i) end end end } end Event.remove = function(go, eventId) local events = Event.goEvents[go] or {} if not events[eventId] then return end if not CS.LuaHelper.IsNull(go) then local comp = go:GetComponent(typeof(Event.cslist[eventId])) if comp then if eventId == Event.OnDestroy then comp:UnBind() else comp:Bind(nil) end Object.Destroy(comp) end end events[eventId] = nil end Event.exit = function () print("Event exit") --把所有绑定了 Event.OnDestroy 的go都触发一下回调,并移除 Event.OnDestroy 事件. 不然在 gameObject 真正 OnDestroy 的时候,拿不到 luaenv for go, events in pairs(Event.goEvents or {}) do for eventId, cb_list in pairs(events) do if eventId == Event.OnDestroy then for _,cb in ipairs(cb_list or {}) do cb(false) end end Event.remove(go, eventId) end end Event.goEvents = {} Event.__exited = true end --protect Event setmetatable(Event, { __newindex = function(t, k, v) error("Event modify limited"..t..k..v) end, __index = function(t, k) error("Event define missing"..t..k) end }) LoginData?--[[ 用户登录数据 author:{zhangpeng} time:2025-08-20 10:16:55 ]] local LoginData = defClassStatic("LoginData") local LOGTAG = LoginData.__cls_name function LoginData:ctor() self:init() end -- 初始化并赋默认值 function LoginData:init() self.loginData = {} self:reset() end -- 重置登录数据 function LoginData:reset() self.loginData.uid = 0 self.loginData.token = "" end -- 从本地playerprefs加载 function LoginData:loadFromPlayerPrefs() self.loginData.uid = PlayerPrefsMgr:getInt("uid") self.loginData.token = PlayerPrefsMgr:getString("token") end -- 从服务器返回数据加载 function LoginData:loadFromServer(serverData) self.loginData.uid = serverData.uid self.loginData.token = serverData.token end -- 保存到本地playerprefs function LoginData:saveToPlayerPrefs() PlayerPrefsMgr:setInt("uid", self.loginData.uid) PlayerPrefsMgr:setString("token", self.loginData.token) end -- 保存到服务器 function LoginData:syncToServer() -- TODO: 保存到服务器 end return LoginData main_webgl--[[ 用于webgl的boot文件 author:zhangpeng time:2025-07-18 17:58:16 ]] local _ENV = _G --FORCE CLEAN ENV local LOGTAG = "[boot/main_webgl]" print(LOGTAG.."start 999") local json = require("rapidjson") print(LOGTAG.."launch luaengine from here") local luaengine = require("luaengine") local UnityEngine = CS.UnityEngine local AET = CS.AET local isEditor = CS.UnityEngine.Application.isEditor local YooAssetLoader = CS.YooAssetLoader.Instance _G.BOOT_MAIN_FILE = "boot/main" _G.GAME_MAIN_FILE = "main/main" local debug_flag = true if CS.LocalDataStorage.Get("PRINT_EVERY_LUA_CALL") == "true" then debug.sethook(function(event,line) local info = debug.getinfo(2) if info.currentline > 0 then print(string.format("%s:%s:%s:%s:%s",info.short_src,tostring(info.currentline),tostring(info.linedefined),tostring(info.name),tostring(info.namewhat))) end end, "c" ) end local cached_lua_ret_map = {} local CLEAR_ALL_LUA_CACHES = function() print("[boot.main] 清空lua缓存") for k,_ in pairs(cached_lua_ret_map) do cached_lua_ret_map[k] = nil end end local _loadlua = function (bytes, file, opts, env) if bytes == nil or bytes == "" then error("lua文件不存在->"..file..":"..tostring(bytes).. "\n" .. debug.traceback()) end if opts == "b" then print(LOGTAG .. "loadlua:bytes file") bytes = AET.Dec(bytes) end local f,err = load(bytes, file, opts, env) if f then local ok,ret = xpcall(f,function(err) CS.UnityEngine.Debug.LogError(string.format("加载lua失败[%s]%s\n%s",file,tostring(err),debug.traceback())) end) if not string.lower(file):find("reslink") and not isEditor then cached_lua_ret_map[file] = {ret = ret} end return ret, env else CS.UnityEngine.Debug.LogError("加载lua失败" .. file) error(tostring(err) .. "\n" .. debug.traceback()) end end -- 热更结束后加载lua文件 -- @ filename:要加载的lua文件名 -- @ env:lua环境,用于加载 Lua 文件的执行环境 -- _require函数会根据传入的参数加载指定的 Lua 文件,然后执行它,最终返回加载结果 print(LOGTAG .. "run in hotupdate") local _require = function(env, filename) local ret = cached_lua_ret_map[filename] if ret then return ret.ret end print("[require]", filename) -- local filepath = string.lower(filename) local filepath = filename -- 使用YooAssetLoader同步加载资源,启动时候已经把ab加载到内存了 local src = YooAssetLoader:LoadText(filepath) return _loadlua(src, filename, "bt", env) end local _newenv = function() -- local _G = _G local _E = _G local rawset = _G.rawset local env = { _G = _G, _print = print, CLEAR_ALL_LUA_CACHES = CLEAR_ALL_LUA_CACHES, ENV_REQUIRE = _require } _G.setmetatable( env, { __index = function(t, k) local v = _E[k] rawset(t, k, v) return v end } ) return env end --Run Main Code do print(LOGTAG .. "start run main code") local _ENV = _newenv() _ENV.CLEAR_ENV = function() for k, _ in pairs(_ENV) do _ENV[k] = nil end end _ENV.raw_require = raw_require or require _ENV._require = _require _ENV.require = function(filename, _env) _env = _env or _ENV return _require(_env, filename) end require("boot/build_config") -- 执行main/main.lua print(LOGTAG.." -------- RUN GAME_MAIN_FILE -------- ") require(_G.GAME_MAIN_FILE) end CosLuaMgr<. ---@class CosLuaMgr:LuaStaticClass local CosLuaMgr = defClassStatic("CosLuaMgr") local LOGTAG = "CosLuaMgr" function CosLuaMgr:init() end ---初始化 ---@param appid string ---@param deviceid string ---@param buildEnv string ---@param oversea boolean ---@param getTimeFunc fun():integer ---@param serviceTypes CosLuaServiceType[] 指定用到的serviceTypes, 只在 tryUpateCredential 方法中用到 function CosLuaMgr:active(appid, buildEnv, oversea, deviceid, getTimeFunc, serviceTypes) self.taskList = {} self.tagTaskList = {} self.appid = appid self.deviceid = deviceid self.getTimeFunc = getTimeFunc self.serviceTypes = serviceTypes CosLuaTemporaryCredential:init(appid, buildEnv, oversea, deviceid, getTimeFunc, function (result, data) self:onCredentialFetched(result, data) end) end ---尝试更新密钥。可以不使用,在上传时会自动获取密钥。 function CosLuaMgr:tryUpateCredential() for _, value in ipairs(self.serviceTypes) do if value == CosLuaServiceType.AVATAR then goto continue end if not CosLuaTemporaryCredential:hasValidCredential(value) then CosLuaTemporaryCredential:fetchCredential(value) end ::continue:: end end ---设置用户信息,需要在app登录完成的时候设置,cos模块内不会保存用户信息,完全依赖app的设置。 function CosLuaMgr:setUserInfo(uid, utoken, showUid) printInfo(LOGTAG, "setUserInfo uid:%s, utoken:%s, showUid:%s", uid, utoken, showUid) self.uid = uid self.utoken = utoken self.showUid = showUid CosLuaTemporaryCredential:setUserInfo(uid, utoken, showUid) ---@type CosLuaUploadTask[] self.taskList = {} ---@type CosLuaTagTask[] self.tagTaskList = {} end ---清理用户信息,当用户登录的时候调用 function CosLuaMgr:clearUserInfo() printInfo(LOGTAG, "clearUserInfo") self.uid = nil self.utoken = nil self.showUid = nil self:cancelAllUploadFiles() end ---上传文件,可以多次调用, cossdk 支持批量上传 ---@param srcPath string 文件路径 ---@param dstPath string 自定义url的文件名,不能为 nil ---@param serviceType CosLuaServiceType 服务类型 ---@param progressCallback fun(progress:number, srcPath:string, uploadTask:CosLuaUploadTask) ---@param completeCallback fun(result:boolean, data:{errorCode:integer, errorMsg:string, srcPath:string}, uploadTask:CosLuaUploadTask) ---@param prepareUrlCallback fun(originFilePath:string, prepareFileUrl:string, prepareFileRelativeUrl:string) ---@return CosLuaUploadTask function CosLuaMgr:uploadFile(srcPath, dstPath, serviceType, progressCallback, completeCallback, prepareUrlCallback) local cosUploadTask = CosLuaUploadTask.new(srcPath, dstPath, serviceType, progressCallback, function (result, data, uploadTask) self:onOneTaskComplete(result, data, uploadTask) if completeCallback then completeCallback(result, data, uploadTask) end end, prepareUrlCallback) table.insert(self.taskList, cosUploadTask) self:_tryStartTask(true) return cosUploadTask end ---批量上传 ---@param files {srcPath:string, dstPath:string}[] ---@param serviceType CosLuaServiceType ---@param progressCallback fun(progress:number, batchUploadTask:CosLuaBatchUploadTask) ---@param oneCompleteCallback fun(result:boolean, data:{errorCode:integer, errorMsg:string, srcPath:string}, batchUploadTask:CosLuaBatchUploadTask, uploadTask:CosLuaUploadTask) ---@param completeCallback fun(result:boolean, data:{sucCount:integer, failCount:integer}, batchUploadTask:CosLuaBatchUploadTask) ---@param prepareUrlCallback fun(originFilePath:string, prepareFileUrl:string, prepareFileRelativeUrl:string) ---@return CosLuaBatchUploadTask function CosLuaMgr:batchUploadFile(files, serviceType, progressCallback, oneCompleteCallback, completeCallback, prepareUrlCallback) local batchUploadTask = CosLuaBatchUploadTask.new(files, serviceType, progressCallback, function (result, data, batchUploadTask, uploadTask) self:onOneTaskComplete(result, data, uploadTask) if oneCompleteCallback then oneCompleteCallback(result, data, batchUploadTask, uploadTask) end end, completeCallback, prepareUrlCallback) table.insertTo(self.taskList, batchUploadTask.tasks) self:_tryStartTask(true) return batchUploadTask end ---@private ---@param isFetchCredential boolean 是否去获取 credential function CosLuaMgr:_tryStartTask(isFetchCredential) printInfo(LOGTAG, "_tryStartTask") ---@type table local serviceTypes = {} for _, value in ipairs(self.taskList) do if value:canStart() then serviceTypes[value.serviceType] = serviceTypes[value.serviceType] or {} table.insert(serviceTypes[value.serviceType], value) end end for _, value in ipairs(self.tagTaskList) do if value:canStart() then serviceTypes[value.serviceType] = serviceTypes[value.serviceType] or {} table.insert(serviceTypes[value.serviceType], value) end end for serviceType, serviceTypeTasks in pairs(serviceTypes) do if not CosLuaTemporaryCredential:hasValidCredential(serviceType) then if isFetchCredential then CosLuaTemporaryCredential:fetchCredential(serviceType) end else for _, task in ipairs(serviceTypeTasks) do task:start() end end end end ---@private ---@param result boolean ---@param data {errorCode:integer, errorMsg:string, service_type:CosLuaServiceType} function CosLuaMgr:onCredentialFetched(result, data) if result then self:_tryStartTask(false) else for _, task in ipairs(self.taskList or {}) do task:failWithErrorCredential(data.service_type) end for _, task in ipairs(self.tagTaskList or {}) do task:failWithErrorCredential(data.service_type) end end end ---@private ---@param result boolean ---@param data {errorCode:integer, errorMsg:string, srcPath:string} ---@param uploadTask CosLuaUploadTask|CosLuaTagTask function CosLuaMgr:onOneTaskComplete(result, data, uploadTask) self:_updateTaskList() self:_tryStartTask(true) end ---@param cosUploadTask CosLuaUploadTask function CosLuaMgr:cancelUploadFile(cosUploadTask) cosUploadTask:cancel() self:_updateTaskList() end function CosLuaMgr:cancelAllUploadFiles() for _, task in ipairs(self.taskList or {}) do task:cancel() end self.taskList = {} end ---@private function CosLuaMgr:_updateTaskList() local leftList = {} for _, task in ipairs(self.taskList) do if not task:isFinished() then table.insert(leftList, task) end end self.taskList = leftList local tagLeftList = {} for _, task in ipairs(self.tagTaskList) do if not task:isFinished() then table.insert(tagLeftList, task) end end self.tagTaskList = tagLeftList end ---希望 srcPath 是已经经过压缩的图片 ---@param srcPath string 文件路径 ---@param completeCallback fun(result:boolean, data:{errorCode:integer, errorMsg:string, srcPath:string}) ---@param prepareUrlCallback fun(originFilePath:string, prepareFileUrl:string, prepareFileRelativeUrl:string) function CosLuaMgr:uploadAvatar(srcPath, completeCallback, prepareUrlCallback) if not CS.LuaHelper.IsFileExists(srcPath) then completeCallback(false, {-5, string.format("srcPath:%s not exists", srcPath)}) return end local md5 = CS.Md5Util.GetHashCodeOfFile(srcPath) local suffix = CS.System.IO.Path.GetExtension(srcPath) local dstPath = md5..suffix self:uploadFile(srcPath, dstPath, CosLuaServiceType.AVATAR, function() end, completeCallback, prepareUrlCallback) end ---@param quality integer 1-100 ---@param completeCallback fun(result:boolean, data:{errorCode:integer, errorMsg:string, srcPath:string}) ---@param prepareUrlCallback fun(originFilePath:string, prepareFileUrl:string, prepareFileRelativeUrl:string) ---@param width? integer 指定target的宽度 ---@param height? integer 指定 target 的高度 function CosLuaMgr:uploadAvatarWithTexture2D(texture, savePath, quality, completeCallback, prepareUrlCallback, width, height) local smallTexture = CS.iHuman.UnitySz.Framework.COS.CosImageUtil.ResizeTexture(texture, width or texture.width, height or texture.height) local useTexture = smallTexture and smallTexture or texture local saveResult = CS.LuaHelper.SaveImage(savePath, useTexture, quality or 50) if smallTexture then Texture2D.DestroyImmediate(smallTexture) end if not saveResult then if completeCallback then completeCallback(false, {errorCode = -4, errorMsg = string.format("savePath:%s has not right suffix", savePath)}) end return end self:uploadAvatar(savePath, completeCallback, prepareUrlCallback) end --#region tag 操作 ---设置tag ---@param cosUrl string ---@param serviceType CosLuaServiceType ---@param tags table ---@param callback fun(result:boolean, data:{errorCode:integer, errorMsg:string, tags:table}) function CosLuaMgr:setTagsForCosUrl(cosUrl, serviceType, tags, callback) local cosTagTask = CosLuaTagTask.new(cosUrl, CosLuaTagTask.OPT.SET, tags, serviceType, function (result, data, tagTask) self:onOneTaskComplete(result, data, tagTask) if callback then callback(result, data) end end) table.insert(self.tagTaskList, cosTagTask) self:_tryStartTask(true) end ---删除tag ---@param cosUrl string ---@param serviceType CosLuaServiceType ---@param callback fun(result:boolean, data:{errorCode:integer, errorMsg:string, tags:table}) function CosLuaMgr:deleteTagsForCosUrl(cosUrl, serviceType, callback) local cosTagTask = CosLuaTagTask.new(cosUrl, CosLuaTagTask.OPT.DELETE, nil, serviceType, function (result, data, tagTask) self:onOneTaskComplete(result, data, tagTask) if callback then callback(result, data) end end) table.insert(self.tagTaskList, cosTagTask) self:_tryStartTask(true) end ---查询tag ---@param cosUrl string ---@param serviceType CosLuaServiceType ---@param callback fun(result:boolean, data:{errorCode:integer, errorMsg:string, tags:table}) function CosLuaMgr:getTagsForCosUrl(cosUrl, serviceType, callback) local cosTagTask = CosLuaTagTask.new(cosUrl, CosLuaTagTask.OPT.GET, nil, serviceType, function (result, data, tagTask) self:onOneTaskComplete(result, data, tagTask) if callback then callback(result, data) end end) table.insert(self.tagTaskList, cosTagTask) self:_tryStartTask(true) end --#endregion ---查询 function CosLuaMgr:putLog(isPre, dev) local region = "na-siliconvalley" local appId = "1304062922" local secretId = "AKIDnRr1vZy70f55qKYWFivsZ0Ti1A3gCTUV" local secretKey = "0lGWXJxRgfVDhy0mIsKWTMBsuiGZdR1M" local cosXmlServer = CS.CosSDKAPI.GetCosXml(region, secretId, secretKey, true) local userId = "log"-- User:getUserId() local bucket = string.format("%s-%s", "package", appId) local filePath = isPre and CS.LogHelper.prevLuaLogFile or CS.LogHelper.luaLogFile local destPath = string.format("linkmatch/lua/dev/%s/%s.txt", dev, userId) CS.LogHelper.CloseLuaLogFile() CS.CosSDKAPI.TransferUploadFile(cosXmlServer, bucket, filePath, destPath, function() end, function(result, a, b, c) CS.LogHelper.OpenLuaLogFile() local baseUrl = "https://resource.bjfytech.com/" local toPath = string.format("%s%s", baseUrl,destPath) printInfo(LOGTAG, "日志完整cos地址: %s", toPath) end) end --#endregion return CosLuaMgrmain(require("framework/debug/debug_main")CosLuaTemporaryCredential ( ---@class CosLuaTemporaryCredential:LuaStaticClass local CosLuaTemporaryCredential = defClassStatic("CosLuaTemporaryCredential") local LOGTAG = "CosLuaTemporaryCredential" local CosSDKAPI = CS.iHuman.UnitySz.Framework.COS.CosSDKAPI local function generateParameterKey(sourceKey) if sourceKey == nil or sourceKey == "0" or sourceKey == "-1" then return "" end local key = sourceKey while #key < 32 do key = key .. sourceKey end return string.sub(key, 1, 32) end ---@param appid string ---@param deviceid string ---@param buildEnv string ---@param oversea boolean ---@param getTimeFunc fun():integer ---@param onCredentialFetched fun(result:boolean, data:{errorCode:integer, errorMsg:string, service_type:CosLuaServiceType}) function CosLuaTemporaryCredential:init(appid, buildEnv, oversea, deviceid, getTimeFunc, onCredentialFetched) self.appid = appid self.key = generateParameterKey(appid) printVerbose(LOGTAG, "self.key:%s", self.key) self.buildEnv = buildEnv self.deviceid = deviceid self.overea = oversea self.getTimeFunc = getTimeFunc self.onCredentialFetched = onCredentialFetched or function () end self:clear() end function CosLuaTemporaryCredential:setUserInfo(uid, utoken, showUid) printInfo(LOGTAG, "setUserInfo uid:%s, utoken:%s, showUid:%s", uid, utoken, showUid) self.uid = uid self.utoken = utoken self.showUid = showUid end function CosLuaTemporaryCredential:clearUserInfo() printInfo(LOGTAG, "clearUserInfo") self:clear() end ---comment ---@param service_type CosLuaServiceType ---@return boolean function CosLuaTemporaryCredential:hasValidCredential(service_type) local cosCredential = self.cosCredentialDict[service_type] if cosCredential == nil then return false end local curTime = self.getTimeFunc() if curTime > cosCredential.expired_time then return false end return true end ---@param service_type CosLuaServiceType ---@return CosLuaCredentialBean|nil function CosLuaTemporaryCredential:getCredential(service_type) if not self:hasValidCredential(service_type) then return nil end return self.cosCredentialDict[service_type] end function CosLuaTemporaryCredential:_getApiUrl(service_type) local avatarConfig = { path = "avatar/open/v2/get_credential", host = { staging = "https://staging-api.ihumand.com/", production = "https://avatars.ihuman.com/", production_oversea = "https://avatars.bekids.com/" } } local normalConfig = { path = "cos/gateway/open/v5/get_credential", host = { staging = "https://staging-api.ihumand.com/", production = "https://cos.ihuman.com/", production_oversea = "https://cos.bekids.com/" } } local configs = { [CosLuaServiceType.UGC] = normalConfig, [CosLuaServiceType.AVATAR] = avatarConfig, } local config = service_type and configs[service_type] if config == nil then return end local key = (self.buildEnv ~= "production") and "staging" or (self.overea and "production_oversea" or "production") local host = config.host[key] return string.format("%s%s", host, config.path) end ---@param service_type CosLuaServiceType function CosLuaTemporaryCredential:fetchCredential(service_type) if not self.uid then self.onCredentialFetched(false, {errorCode = -1, errorMsg = "uid is empty", service_type = service_type}) return end local api = self:_getApiUrl(service_type) if not api then self.onCredentialFetched(false, {errorCode = -1, errorMsg = string.format("service_type:%s not right", tostring(service_type)), service_type = service_type}) return end local service_type_str = json.encode({tostring(service_type)}) local data = { appid = tostring(self.appid), service_type = service_type ~= CosLuaServiceType.AVATAR and service_type_str or nil, uid = tostring(self.uid), show_uid = self.showUid, timestamp = tostring(self.getTimeFunc()), deviceid = self.deviceid, utoken = self.utoken, } local function onFetchFinish(result, data, rawStr) if not result then self.onCredentialFetched(false, {errorCode = -2, errorMsg = string.format("fetchCredential code:%d, error:%s", data.errorCode, data.errorMsg)}) return end local cosCredential = CosLuaCredentialBean.new() cosCredential:initWithConfig(data) self.cosCredentialDict[service_type] = cosCredential -- 创建 CosLuaXmlServer local secretId = CS.AES256.Decrypt(cosCredential.left, self.key) local secretKey = CS.AES256.Decrypt(cosCredential.right, self.key) local cosLuaXmlServer = CosSDKAPI.GetCosXml(cosCredential.region, secretId, secretKey, cosCredential.session_token, cosCredential.start_time, cosCredential.expired_time, cosCredential.protocol == "https") self.cosXmlServerDict[service_type] = cosLuaXmlServer self.onCredentialFetched(true, {service_type = service_type}) end local mock = false local mock_data = { [CosLuaServiceType.AVATAR] = { allow_prefix = "31/U5809619974/", base_url = "https://avatar-1253822818.cos.ap-beijing.myqcloud.com", bucket_name = "avatar-1253822818", cos_appid = "1253822818", domain = "myqcloud.com", expired_time = "1705903790", left = "g2zCMncoaOJDuPOptuf9CaFBaQhJpxVRApsf95JqypA077MkXWEwzI4xHIKQqtiM4A8aOshIoqI0hkgl5P9IWoF5iVn6nu95kFGc+MaJ/+E=", max_file_size = "5", protocol = "https", region = "ap-beijing", right = "aOCwq5PFZBAPW130BgZkRnE/yzvInUFBqV+xioh7tsQy8Adfx0LuSJ3uMtK7gVxp", server_time_zone = "GMT+08:00", session_token = "msxTNe5pkeT5TeoiNI7g6UDFplgRkgba76413bacb69e093617eac421be173fb4CS9BSiBG_qcFL3OMXnzEWQKA1XfBjt4x743e3gcd0KJkKRXW8eZdKeS2BAEQWR_B16msAH4Nnz-dIBlQC5g8gG57hTvv5J1RHGNZlL3oUoAysw34T-8wWbkODwE7WWgysx-uhDj5g2PH3_sqV4a_Rr48_4rz2ILXb_mHvdd3cXkot44hVHwLUwPSo6fiNFWvHfUr82PXoq-kqLy7qTHP3Lg3YmYWPUeolWLbxdttDbdujlDp3CJi0clzEabuj3tR_iHib-y6ux5vCpLXvj0UrkoljN2veGtiP8Ia7rVzie_3cx-myU8g_MNQPKMDHsIdkoVRUo3w65Ru77g2mM6-Rlrbdj8U89LUZKwbUB8YrGrymHvoAcLsNsxPvcM8nAaaQk7V6joFM4RGE_7LCfXNGGWC5j5q1mNhMMswgKgRnlvn9JZiFrkNI_Y5GF2uBpOVm7eSShAd7ANLIBkDOLLKoHHml9WETJRIcbcVgGBbtpkbKbARV7o9bdnbTHv5RTDAa4sTdwjtftoCbr_cLnxnDaK5McFLwIPTaxUO00OTjT2_902D83wGe_tdAY20hZCNzI53OvEQ7TNMEYqJ9rnjja6Vm_yKUXIjmVMswXXntnOtKJhPWOMxFMhqyjW5aWg1Ic7yJE6hJnYLl0VNGWtvZmIUAZ2H_tlsnUXUnecnLfMlBx16Ll2tT-aTPAhxG79zm1ew_kdvbmcOmXn5LAzQorAzRclJMY2pz1M1_ORzf8CWNuaCZ6TxR0cy_jnSwQbz", start_time = "1705896590", storage_type = "0" }, [CosLuaServiceType.UGC] = { allow_prefix = "U5809619974/", base_url = "https://31-ugc-1253822818.cos.ap-beijing.myqcloud.com", bucket_name = "31-ugc-1253822818", cos_appid = "1253822818", domain = "myqcloud.com", expired_time = "1706001131", left = "S6sRa3cvJk6JBPLeN5L+W9wqNhSuClTeDFsUNw+bMDcH5xpLlKDxq4h9kKrJ0DFmt+0qdcNH8DACKPADcvOrHGFPvyKFhPETrMBKZnQfSJg=", max_file_size = "100", protocol = "https", region = "ap-beijing", right = "Nng4d/FBAjq71Xkh64xdHuK5f7s0rj5YNCbHWgkx0f+CgQ7z+aa1Bdca2uKznzxN", server_time_zone = "GMT+08:00", service_type = "2", session_token = "msxTNe5pkeT5TeoiNI7g6UDFplgRkgbaa1718188bec43e8c5adb351d9981db18CS9BSiBG_qcFL3OMXnzEWR1tzDu3LTIRcDQMBnNmEGKWDrUixsiVYkJaepGPnXKNB-nNfNvNBRsnaoqi-arqK7CKNGL2YDb1TDHmLQGcgzjaURDgNhKZuG8kNVqVKXyyEmSNlRojYbG7PmofWRdLIzWdZyCnALFAMucf-z0bhwhXr-a2kXidiYDGBkrPuAbWSiQLusZ5f1vLTz4yUlWRBpB3_KwF2kuSGwKJf0dPinHafmlF7RP2zWQU9AVHWQFM4U71lZYGxmb3LPgYWaGkHqHwhN5Bu9Z0DYs1eT-rgCZq9D-WoZUarQK6gwAxELNEZU48N8V73wdrBcM8rDkNT_7vFwVa_vaSW9SWrtJBsdr6wpFdMEcbrifT1u6a8jJpmRj_HuXGqVT8zUffSUJwb8ygH8s3d-4tT7bv66USZK-JE2jIfma0LHtAY_pwdWSp_cF0LNeC21uLlypJq89UwInkEecqwQSHWjoakF7pXaMJC0cufvZVVFqtrZAWt1CM3ZtuGtUIrfRrVP32x3cm3TZOCJmmUX6onXcklrZecNuxIheSumKzxJ31UI1jFg-Ci54DDKxvfI4mL_N8WG8wAgDD4h4E1QEcY6xRIwdaZ1vP-EQkYLPca7GLLFon_h940zZzdckBB7Qw2V254UnFISKFKE1tFcKF2qJdf2m3_fyMoRr_x4naxU2YcpQgnDF5BwLKJaHWzinM7AkEX_HEWgzIqbtWLgNR2TTroVFRXhC7Ukhl7zT73qBZGRKq792_GCE7C0sKeU1OTJdUt1yvWxEi4zXTnef7dgNh7LM8f3V9M7nXxftCuGbKXEH_Fsab2CoDTr7Xz_PMyNSJsG8bO7yjCXb5YHurNZc1CAUrKbI", start_time = "1705893131", storage_type = "0" } } if mock then onFetchFinish(true, mock_data[service_type]) return end HttpUtil:fetchApiWithSign(api, "post", data, function (result, data, rawStr) onFetchFinish(result, result and data[1] or data, rawStr) end, nil, "sdk") end function CosLuaTemporaryCredential:clear() self.uid = nil self.utoken = nil self.showUid = nil self.cosXmlServerDict = {} --key:service_type, value CosLuaXmlServer ---@type table self.cosCredentialDict = {} --key:service_type, value CosLuaCredentialBean end ---comment ---@param service_type CosLuaServiceType function CosLuaTemporaryCredential:getCosXmlServer(service_type) return self.cosXmlServerDict[service_type] end function CosLuaTemporaryCredential:getPrepareUrl(service_type) local cosCredential = self.cosCredentialDict[service_type] local base_url = cosCredential.base_url local prefix = cosCredential.allow_prefix return string.format("%s/%s", base_url, prefix), "/"..prefix end ---comment ---@param cosUrl string ---@param service_type any ---@return string|nil function CosLuaTemporaryCredential:getRelativePath(cosUrl, service_type) local cosCredential = self.cosCredentialDict[service_type] local base_url = cosCredential.base_url local base_url_with_slash = base_url .. "/" if cosUrl:sub(1, #base_url_with_slash) == base_url_with_slash then return cosUrl:sub(#base_url_with_slash + 1) else return nil end end function CosLuaTemporaryCredential:exit() self:clear() endboot_debug_main local LOG_TAG = "debug_main" -- luaide 调试 local debugXpCall local breakSocketHandle local platform = CS.UnityEngine.Application.platform local can_debug = (platform == CS.UnityEngine.RuntimePlatform.WindowsEditor or platform == CS.UnityEngine.RuntimePlatform.WindowsPlayer or false) local DEBUG_LUA = true--false and can_debug if DEBUG_LUA then breakSocketHandle,debugXpCall = require("Assets.LuaScripts.framework.debug.luaidedebug.LuaDebug")("localhost",7003) print(LOG_TAG,"init lua debug :",platform) end GameNodeAdapt --- 非UI节点屏幕适配 local GameNodeAdapt = {} GameNodeAdapt.layout_type = { top_left = 1, top_center = 2, top_right = 3, center_left = 4, center_center = 5, center_right = 6, bottom_left = 7, bottom_center = 8, bottom_right = 9 } local layout_type = GameNodeAdapt.layout_type -- obj的每条边 靠每个边的 offset function GameNodeAdapt:adaptScreen(obj, layoutType, offset) local lbPoint = GameUtil:getCamera():ScreenToWorldPoint(Vector3(0, 0, 0)) local rtPoint = GameUtil:getCamera():ScreenToWorldPoint(Vector3(Screen.width, Screen.height, 0)) local b = util.BoundUtil:getObjBounds(obj) local x, y = 0, 0 if layoutType == layout_type.top_left then x = lbPoint.x + b.size.x / 2 + offset.x y = rtPoint.y - b.size.y / 2 - offset.y elseif layoutType == layout_type.top_center then x = 0 + offset.x y = rtPoint.y - b.size.y / 2 - offset.y elseif layoutType == layout_type.top_right then x = rtPoint.x - b.size.x / 2 - offset.x y = rtPoint.y - b.size.y / 2 - offset.y elseif layoutType == layout_type.center_left then x = lbPoint.x + b.size.x / 2 + offset.x y = 0 + offset.y elseif layoutType == layout_type.center_center then x = 0 + offset.x y = 0 + offset.y elseif layoutType == layout_type.center_right then x = rtPoint.x - b.size.x / 2 - offset.x y = 0 + offset.y elseif layoutType == layout_type.bottom_left then x = lbPoint.x + b.size.x / 2 + offset.x y = lbPoint.y + b.size.y / 2 + offset.y elseif layoutType == layout_type.bottom_center then x = 0 + offset.x y = lbPoint.y + b.size.y / 2 + offset.y elseif layoutType == layout_type.bottom_right then x = rtPoint.x - b.size.x / 2 - offset.x y = lbPoint.y + b.size.y / 2 + offset.y end local p = Vector3(x, y, 0) obj.transform.position = p end -- 判断 obj 的 bounds 是否在屏幕内 -- isOnlyPos 是否只判断坐标 function GameNodeAdapt:isInScreen(obj, isOnlyPos) if isOnlyPos == nil then isOnlyPos = true end local lbPoint = GameUtil:getCamera():ScreenToWorldPoint(Vector3(0, 0, 0)) local rtPoint = GameUtil:getCamera():ScreenToWorldPoint(Vector3(Screen.width, Screen.height, 0)) local x = obj.transform.position.x local y = obj.transform.position.y local minX, minY, maxX, maxY = x, y, x, y if not isOnlyPos then local b = util.BoundUtil:getObjBounds(obj) minX = minX - b.size.x / 2 maxX = maxX + b.size.x / 2 minY = minY - b.size.y / 2 maxY = maxY + b.size.y / 2 end return x <= rtPoint.x and x >= lbPoint.x and y >= lbPoint.y and y <= rtPoint.y end return GameNodeAdapt CoordUtil'local CoordUtil = {} local Screen = CS.UnityEngine.Screen local Vector3 = CS.UnityEngine.Vector3 local Rect = CS.UnityEngine.Rect local Bounds = CS.UnityEngine.Bounds -- gameObject的坐标转换成坐标系coord下的坐标 function CoordUtil.getPosInCoord(gameObject,coord) local lp = coord.transform:InverseTransformPoint(gameObject.transform.position) return lp end function CoordUtil.getScreenRectInCoord(cam,coord) local bl = cam:ViewportToWorldPoint(Vector3(0, 0, 0)) bl = coord.transform:InverseTransformPoint(bl) local tr = cam:ViewportToWorldPoint(Vector3(1, 1, 0)) tr = coord.transform:InverseTransformPoint(tr) return CS.UnityEngine.Rect(bl.x, bl.y, tr.x - bl.x, tr.y - bl.y) end function CoordUtil.getSafeScreenRectInCoord(cam,coord,isIgnoreBottom) local safeArea = CS.UnityEngine.Screen.safeArea if isIgnoreBottom then safeArea.yMin = 0 end local bl = safeArea.min local tr = safeArea.max bl = CS.UnityEngine.Vector3(bl.x, bl.y, 0) tr = CS.UnityEngine.Vector3(tr.x, tr.y, 0) bl = cam:ScreenToWorldPoint(bl) tr = cam:ScreenToWorldPoint(tr) bl = coord.transform:InverseTransformPoint(bl) tr = coord.transform:InverseTransformPoint(tr) return CS.UnityEngine.Rect(bl.x, bl.y, tr.x - bl.x, tr.y - bl.y) end function CoordUtil.getBoundsInCoord(bounds,coord) local min = coord.transform:InverseTransformPoint(bounds.min) local max = coord.transform:InverseTransformPoint(bounds.max) local lbounds = Bounds(Vector3.zero,Vector3.zero) lbounds:SetMinMax(min,max) return lbounds end -- 计算一个节点的包围盒 function CoordUtil.getNodeBounds(obj) local p_max = Vector3.zero local p_min = Vector3.zero local center = Vector3.zero local mesh = obj:GetComponent(typeof(CS.UnityEngine.Renderer)) if mesh ~= nil then local b = mesh.bounds p_max = b.max p_min = b.min center = b.center end CoordUtil.recursionCalculateBounds(p_max, p_min, obj) if mesh == nil then local xc = (p_max.x + p_min.x) / 2 local yc = (p_max.y + p_min.z) / 2 local zc = (p_max.z + p_min.z) / 2 center = Vector3(xc, yc, zc) end local size = Vector3(p_max.x - p_min.x, p_max.y - p_min.y, p_max.z - p_min.z) local bound = CS.UnityEngine.Bounds(center, size) bound.size = size bound.extents = size / 2 return bound end -- 计算包围盒顶点 function CoordUtil.recursionCalculateBounds(p_max, p_min, obj) if obj.transform.childCount <= 0 then return end for i = 1, obj.transform.childCount do local item = obj.transform:GetChild(i - 1).gameObject local m = item:GetComponent(typeof(CS.UnityEngine.Renderer)) if m ~= nil and item.activeSelf then local b = m.bounds if p_max:Equals(Vector3.zero) and p_min:Equals(Vector3.zero) then p_max = b.max p_min = b.min end if b.max.x > p_max.x then p_max.x = b.max.x end if b.max.y > p_max.y then p_max.y = b.max.y end if b.max.z > p_max.z then p_max.z = b.max.z end if b.min.x < p_min.x then p_min.x = b.min.x end if b.min.y < p_min.y then p_min.y = b.min.y end if b.min.z < p_min.z then p_min.z = b.min.z end end CoordUtil.recursionCalculateBounds(item) end return p_max, p_min end return CoordUtilCmdDef--[[ 定义指令的tag author:{author} time:2023-08-23 18:02:59 ]] local CmdDef,_ = defClassStatic("CmdDef") -- 消息类型 CmdDef.MsgType = { REQ = 0x0, --客户端到服务器的握手请求/服务器到客户端的握手响应 ACK = 0x1, --客户端到服务器的握手应答/服务器到客户端的握手应答 SERVER_DIS_CONN = 0x2,--服务器主动断连通知 HEART = 0x3,--心跳包 DATA = 0x4,-- 指令数据包 } -- 业务指令编号 CmdDef.SendDef = { SingleChat = 0x1 } -- 接收的指令 CmdDef.ReceiveDef = { SingleChat = 0x2 } function CmdDef:init() end CmdDef:init()EnumMEnum = { ENV_DEVELOPMENT = "dev", ENV_PRODUCTION = "production", } ReslinkLoad---@alias AssetItem {[1]:string, [2]:number} ---@class ReslinkLoad:LuaClass local ReslinkLoad,super = defClass("ReslinkLoad") local LOGTAG = "ReslinkLoad" ReslinkLoad.ST_WAIT = 1 ReslinkLoad.ST_LOAD = 2 ReslinkLoad.ST_STOP = 3 function ReslinkLoad.inTrans() return (ReslinkLoad._cur ~= nil) end function ReslinkLoad:ctor() self.state = ReslinkLoad.ST_WAIT self.progress = 0 self.isDone = false self._cur = 0 self._max = 0 self._time = nil self._func = nil self._loadSceneFlag = false ---@type AssetItem[] self._loadSceneList = {} self._loadAssetList = {} self._loadedSceneList = {} end ---comment ---@param set ResLink ---@param p1 number ---@param p2 number function ReslinkLoad:addResLink(set, p1, p2) printInfo(LOGTAG, "addResLink") if self.state == ReslinkLoad.ST_WAIT and set then local scene = set:getAssetInfo("SCENE") if scene then self:addSceneFile(scene[1], p1) end local list = set:getAssetList() for i,v in ipairs(list) do -- preload 预加载 if v[2] == 1 then if v[3] == "Prefab" then self:addAssetFile(v[1], p2) elseif v[3] == "Scene" then self:addSceneFile(v[1], p1) elseif v[3] == "ResLink" then self:addResLink(ResLoader.loadResLink(v[1]), p1, p2) elseif v[3] == typeof(AudioClip) then self:addAssetFile(v[1], p2) end end end end end ---comment ---@param scene string 路径 ---@param p any function ReslinkLoad:addSceneFile(scene, p) if self.state == ReslinkLoad.ST_WAIT and scene then for i,v in ipairs(self._loadSceneList) do if v[1] == scene then return end end table.insert(self._loadSceneList, {scene, p or 10}) end end function ReslinkLoad:addAssetFile(asset, p) if self.state == ReslinkLoad.ST_WAIT and asset then if type(asset) == "table" then p = p and (p / (#asset)) or 1 for i,v in ipairs(asset) do table.insert(self._loadAssetList, {v, p}) end else table.insert(self._loadAssetList, {asset, p or 1}) end end end ---开始加载 ---@param func fun(sceneList:CS.UnityEngine.SceneManagement.Scene[]) 完成回调 ---@param progressCb fun(progress:number) 加载进度回调 ---@param isAsync boolean 是否异步加载 function ReslinkLoad:load(func, progressCb, isAsync) printInfo(LOGTAG, "load") if self.state ~= ReslinkLoad.ST_WAIT then return end self.state = ReslinkLoad.ST_LOAD ReslinkLoad._cur = self self._time = Time.time self._func = func self._progressCb = progressCb self._isAsync = isAsync self._cur = 0 self._max = 0 for i,v in ipairs(self._loadSceneList) do self._max = self._max + v[2] end for i,v in ipairs(self._loadAssetList) do self._max = self._max + v[2] end self:_loadStart() end function ReslinkLoad:stop() self.state = ReslinkLoad.ST_STOP self._func = nil self._progressCb = nil end function ReslinkLoad:onExit() ReslinkLoad._cur = nil self:stop() super.onExit(self) end -- 加载开始 function ReslinkLoad:_loadStart() printInfo(LOGTAG, "_loadStart") self:_unloadLastScene() self:_loadScene() end function ReslinkLoad:_unloadLastScene() printInfo(LOGTAG, "_unloadLastScene") ResLoader.unloadAssets() end function ReslinkLoad:_loadScene() printInfo(LOGTAG, "_loadScene") if self.state ~= ReslinkLoad.ST_LOAD then return end ---@type AssetItem local scene = table.remove(self._loadSceneList or {}, 1) if scene then local flag = self._loadSceneFlag self._loadSceneFlag = true self:_loadSceneIMPL(scene[1], scene[2], flag) else if self._loadSceneList then self._loadSceneList = nil -- self:onLoadSceneFinish() end if self._loadAssetList then self:_loadAsset() else self:_loadComplete() end end end ---comment ---@param res any ---@param p any ---@param addtive boolean unity加载场景是否 Additive 模式(不卸载之前的场景) 目前不支持 function ReslinkLoad:_loadSceneIMPL(res, p, addtive) printInfo(LOGTAG, "_loadSceneIMPL res:%s", res) local function onFinish(scene) if self.state == ReslinkLoad.ST_LOAD then table.insert(self._loadedSceneList, scene) self:onProgress(res, p, true) self:_loadScene() else self:stop() end end if self._isAsync then ResLoader.loadSceneAsync(res, function (progress) self:onProgress(res, p, progress) end, onFinish) else ResLoader.loadSceneSync(res, onFinish) end end function ReslinkLoad:_loadAsset() if self.state ~= ReslinkLoad.ST_LOAD then return end ---@type AssetItem local asset = table.remove(self._loadAssetList or {}, 1) if asset then self:_loadAssetIMPL(asset[1], asset[2]) else self._loadAssetList = nil if self._loadSceneList then self:_loadScene() else self:_loadComplete() end end end function ReslinkLoad:_loadAssetIMPL(res, p) if self._isAsync then ResLoader.loadAssetAsync(res, nil, function (progress) self:onProgress(res, p, progress) end, function () self:onProgress(res, p, true) self:_loadAsset() end) else ResLoader.loadAsset(res, nil) self:onProgress(res, p, true) self:_loadAsset() end end function ReslinkLoad:_loadComplete() if self.state ~= ReslinkLoad.ST_LOAD then return end if self.state == ReslinkLoad.ST_LOAD then self:onComplete() self:stop() end end function ReslinkLoad:onProgress(res, p, progress) if self.state == ReslinkLoad.ST_LOAD then local cur = 0 if progress == true then self._cur = self._cur + p cur = self._cur else cur = self._cur + p * progress end if self._max > 0 then self._progress = cur/self._max end if self._progressCb then self._progressCb(self._progress) end end end function ReslinkLoad:onComplete() self.isDone = true if self._func then self._func(self._loadedSceneList) self._func = nil end end -- function ReslinkLoad:onLoadAsset(res, obj) -- end -- function ReslinkLoad:onLoadScene(res, addtive) -- end -- function ReslinkLoad:onLoadScenePaused() -- end -- function ReslinkLoad:onLoadSceneFinish() -- end return ReslinkLoad queue_action--@desc: 将table里的lua function, 封装成闭包, 用于异步回调 --@desc: 用法: queue_action.table_process(args) local queueAction = CS.LuaGlobal.QueueAction local queue_action = {} function queue_action.func_wrapper(func) return function(...) local args = {...} -- print("func_wrapper before run --------------") queueAction(function() -- print("func_wrapper run --------------") func(table.unpack(args)) end) end end --将table里的lua function, 封装成闭包 function queue_action.table_process(args) if args == nil then return end local need_wrapper = {} for k,v in pairs(args) do if type(v) == "function" then need_wrapper[k] = v end end for k_n,v_n in pairs(need_wrapper) do args[k_n] = queue_action.func_wrapper(v_n) end end return queue_action XSdkConstants--[[ author:{zhangpeng} time:2023-09-25 17:34:49 ]] local XSdkConstants,_ = defClassStatic("XSdkConstants") function XSdkConstants:init() end XSdkConstants.EnvironmentType = { Development = 1, Test = 2, Staging = 3, Production = 4 } XSdkConstants.TransactionState ={ PaymentSuc = 1; -- 支付成功 PaymentFailed = 2; -- 支付失败 } XSdkConstants.PurchasedStatus = { Purchased = 1, --只对非消耗型产品有效 InTrial = 10, --只对订阅型产品有效 InSubscription = 11, --只对订阅型产品有效 Expired = 12, --只对订阅型产品有效 } XSdkConstants.ReceiptDeliverStatus = { OK = 0, ServerError = 301, NoPreparedOrder = 407, --也是成功 AlreadyBound = 408, GuestPurchaseSucceed = 409, GuestPurchaseConsumableForbidden = 410 } XSdkConstants.ExpireIntentType = { Canceled = 1, --用户取消订阅 BillingError = 2, --扣款失败 DisagreePriceIncrease = 3, --用户拒绝接受订阅商品价格上涨 ProductNotAvailable = 4, --此商品已不可购买 Unknown = 5 --未知 } XSdkConstants.IAPProductType = { Consumable = 1, --消耗型 NonConsumable = 2, --非消耗型 AutoRenewableSubscription = 3, --自动续费的订阅 NonRenewingSubscription = 4 --非自动续费的订阅 } XSdkConstants.IAPProductDiscountType = { Unsupported = -99, --优惠类型不支持,针对 iOS 11.2 以下的设备 None = -1, --无优惠,有两种情况 1. 非订阅商品 2. 订阅商品,确实无优惠 PayAsYouGo = 0, --前x个付费周期享受更低的优惠价格 PayUpFront = 1, --前x个付费周期打包购买享受折扣价格 FreeTrial = 2 --前x个付费周期免费试用 } XSdkConstants.ProductPeriodUnit = { Unsupported = -99, --订阅周期类型不支持,针对 iOS 11.2 以下的设备 None = -1, --无订阅周期,针对 非订阅商品 Day = 0, --订阅周期:天 Week = 1, --订阅周期:周 Month = 2, --订阅周期:月 Year = 3 --订阅周期:年 } XSdkConstants:init() SqliteTableT ---@class SqliteTable:LuaClass local SqliteTable = defClass("SqliteTable") local LOGTAG = SqliteTable.__cls_name SqliteTable.SYNC_STATE = { HAVE_SYNCED = 0, NEED_SYNC = 1, IS_SYNCING = 2 } --[[ add/del/upd/get --对应数据库的增删改查 set会根据数据库中否已有该数据自动调用add或者upd create会创建一个默认值的record,但不会对数据库做任何操作 *ByList 批量操作,使用事务, 需要调用者保证list里不包含重复主键的数据 ]] function SqliteTable:ctor(databaseApis, name) self.recordCls = SqliteModel self.databaseApis = databaseApis self.name = name self.useCache = true self.useSync = false self.recordDict = {} self.col = {} self.tcol = {} self.columnList = {} self.columnDict = {} self.primaryList = {} self.primaryDict = {} self.bindInfoList = {} -- self.changeColNameDict = {} --需要改名的字段, self.changeColNameDict[oldName] = newName if self.beforeInit then self:beforeInit() end self:init() if self.afterInit then self.afterInit() end if self.useSync then self:c("syncState", SqliteTable.SYNC_STATE.HAVE_SYNCED, false, true) end self:c("lastUpdateTime", 0, false, true) self:c("lastUpdateVersion", "", false, true) for i, column in ipairs(self.columnList) do self.columnDict[column.name] = column if column.isPrimary then self.primaryDict[column.name] = column table.insert(self.primaryList, column) end self.col[column.name] = column:toSqlStr() self.tcol[column.name] = column:toSqlStr(true) end end function SqliteTable:genColumn(name, value, isPrimary, isMetadata) local column = SqliteColumn.new(self, name, type(value), value, isPrimary, isMetadata) table.insert(self.columnList, column) return column end SqliteTable.c = SqliteTable.genColumn function SqliteTable:bindKeyToProtoBuf(keyName, pbName) local bindInfo = { keyName = keyName, pbName = pbName } table.insert(self.bindInfoList, bindInfo) return bindInfo end SqliteTable.b = SqliteTable.bindKeyToProtoBuf function SqliteTable:init() self.columnList = {} end function SqliteTable:getName() return self.name end function SqliteTable:getColumnList() return self.columnList end function SqliteTable:getColumnDict() return self.columnDict end function SqliteTable:haveColumn(colName) return self:getColumnDict()[colName] ~= nil end --------------------------------------------------------------------------------------------- --数据库 增删改查 相关接口 --------------------------------------------------------------------------------------------- function SqliteTable:create(key) local record = self.recordCls.new(self) record:setKey(key) return record end function SqliteTable:createByPB(pbData) if not pbData then return end local record = self.recordCls.new(self) record:setByPB(pbData) return record end function SqliteTable:get(key) local record = self:getCache(key) or self:doSelectKey(key) return record end function SqliteTable:getOrCreate(key) local record = self:get(key) if not record then record = self:create(key) end return record end function SqliteTable:add(record) if (not record) or record.table ~= self then return end if self.useSync then record:setSyncState(SqliteTable.SYNC_STATE.NEED_SYNC) SyncMgr:addSyncTable(self) end return self:doInsert(record) end function SqliteTable:upd(record) if (not record) or record.table ~= self then return end if self.useSync then record:setSyncState(SqliteTable.SYNC_STATE.NEED_SYNC) SyncMgr:addSyncTable(self) end return self:doUpdate(record) end function SqliteTable:del(record) if (not record) or record.table ~= self then return end local key = record:getKey() if not self:get(key) then return end self:doDelete(record) end function SqliteTable:set(record) if (not record) or record.table ~= self then return end local key = record:getKey() if (not self:get(key)) then return self:add(record) else return self:upd(record) end end function SqliteTable:getAll() self:clearCache() return self:getByCondition() end function SqliteTable:getByCondition(condition) local list = self:doSelectCondition(condition) return list end function SqliteTable:getCount() return self:getCountByCondition() end function SqliteTable:getCountByCondition(condition) local name = "count" local count = self:genQuery():count():as(name):where(condition):getFirst(name) return math.tointeger(count) or math.round(count) end function SqliteTable:addByList(list) if not self:checkRecordListPrimaryKey(list) then printError(LOGTAG, "addByList, %s has duplicate key", self:getName()) return end self.databaseApis.beginTransaction() for i, record in pairs(list) do if self.useSync then record:setSyncState(SqliteTable.SYNC_STATE.NEED_SYNC) end self:doInsert(record) end self.databaseApis.finishTransaction() if self.useSync then SyncMgr:addSyncTable(self) end end function SqliteTable:updByList(list) if not self:checkRecordListPrimaryKey(list) then printError(LOGTAG, "updByList, %s has duplicate key", self:getName()) return end self.databaseApis.beginTransaction() for i, record in pairs(list) do if self.useSync then record:setSyncState(SqliteTable.SYNC_STATE.NEED_SYNC) end self:doUpdate(record) end self.databaseApis.finishTransaction() if self.useSync then SyncMgr:addSyncTable(self) end end function SqliteTable:delByList(list) if not self:checkRecordListPrimaryKey(list) then printError(LOGTAG, "delByList, %s has duplicate key", self:getName()) return end self.databaseApis.beginTransaction() for i, record in pairs(list) do self:doDelete(record) end self.databaseApis.finishTransaction() end --会自动insert不存在的数据 function SqliteTable:setByList(list) if not self:checkRecordListPrimaryKey(list) then printError(LOGTAG, "setByList, %s has duplicate key", self:getName()) return end local insertList = {} local updateList = {} for i, record in ipairs(list) do local key = record:getKey() if (not self:get(key)) then table.insert(insertList, record) else table.insert(updateList, record) end end self.databaseApis.beginTransaction() for i, record in pairs(insertList) do if self.useSync then record:setSyncState(SqliteTable.SYNC_STATE.NEED_SYNC) end self:doInsert(record) end for i, record in pairs(updateList) do if self.useSync then record:setSyncState(SqliteTable.SYNC_STATE.NEED_SYNC) end self:doUpdate(record) end self.databaseApis.finishTransaction() if self.useSync then SyncMgr:addSyncTable(self) end end function SqliteTable:checkRecordListPrimaryKey(list) local dict = {} for i, record in ipairs(list) do local key = record:getKeyStr() if dict[key] then return false end dict[key] = record end return true end --------------------------------------------------------------------------------------------- -- orm相关接口 --------------------------------------------------------------------------------------------- function SqliteTable:genQuery() local query = SqliteQuery.new(self) return query end function SqliteTable:genCondition(column, op, value) if value then value = SqliteUtil:luaValueToStr(value) end local condition = SqliteCondition.new(self):column(column):operate(op, value) return condition end function SqliteTable:genFunc(type, column, asName) local func = SqliteFunc.new(self, type):column(column):as(asName) return func end --------------------------------------------------------------------------------------------- --网络同步相关接口 --------------------------------------------------------------------------------------------- -- sync必定save -- function SqliteTable:sync(record) -- if not self.useSync then -- self:set(record) -- printError(LOGTAG, "sync, self.useSync is false, name:%s", self:getName()) -- return -- end -- record:setSyncState(SqliteTable.SYNC_STATE.NEED_SYNC) -- self:set(record) -- SyncMgr:addSyncTable(self) -- end -- function SqliteTable:syncByList(list) -- if not self.useSync then -- self:setByList(list) -- printError(LOGTAG, "syncByList, self.useSync is false, name:%s", self:getName()) -- return -- end -- for i, record in pairs(list) do -- record:setSyncState(SqliteTable.SYNC_STATE.NEED_SYNC) -- end -- self:setByList(list) -- SyncMgr:addSyncTable(self) -- end function SqliteTable:getAllNeedSync() if not self.useSync then printError(LOGTAG, "getAllNeedSync, self.useSync is false, name:%s", self:getName()) return end local condition = self:genCondition(self.col.syncState):isEqual(SqliteTable.SYNC_STATE.NEED_SYNC) local list = self:getByCondition(condition) return list end function SqliteTable:getAllIsSync() if not self.useSync then printError(LOGTAG, "getAllIsSync, self.useSync is false, name:%s", self:getName()) return end local condition = self:genCondition(self.col.syncState):isEqual(SqliteTable.SYNC_STATE.IS_SYNCING) local list = self:getByCondition(condition) return list end function SqliteTable:push(recordList, handler) handler = handler or function() end self:doPush( recordList, function(result) handler(result) self:afterPush(result) end ) end function SqliteTable:doPush(recordList, handler) printError(LOGTAG, "doPush, must be override, name:%s", self:getName()) end function SqliteTable:afterPush(result) printError(LOGTAG, "afterPush, must be override, name:%s", self:getName()) end function SqliteTable:pull(handler) handler = handler or function() end self:doPull( function(result) local recordList = self:afterPull(result) handler(result, recordList) end ) end function SqliteTable:doPull(handler) printError(LOGTAG, "doPull, must be override, name:%s", self:getName()) end function SqliteTable:afterPull(result) printError(LOGTAG, "afterPull, must be override, name:%s", self:getName()) end function SqliteTable:merge(oldRecordList, newRecordList, mergeFunction) local keyDict = {} local oldRecordDict = {} for i, oldRecord in ipairs(oldRecordList) do local key = oldRecord:getKey() local str = self:keyToStr(key) oldRecordDict[str] = oldRecord keyDict[str] = str end local newRecordDict = {} for i, newRecord in ipairs(newRecordList) do local key = newRecord:getKey() local str = self:keyToStr(key) newRecordDict[str] = newRecord keyDict[str] = str end local recordList = {} for i, str in pairs(keyDict) do local oldRecord = oldRecordDict[str] local newRecord = newRecordDict[str] local record = mergeFunction(oldRecord, newRecord) if record then table.insert(recordList, record) end end return recordList end --以远端为主,添加远端(newRecordList)存在而本地(oldRecordList)缺失的 function SqliteTable:mergeBaseServer(oldRecordList, newRecordList, mergeFunction) local oldRecordDict = {} for i, oldRecord in ipairs(oldRecordList) do local key = oldRecord:getKey() local str = self:keyToStr(key) oldRecordDict[str] = oldRecord end local recordList = {} for i, newRecord in ipairs(newRecordList) do local key = newRecord:getKey() local str = self:keyToStr(key) local oldRecord = oldRecordDict[str] if not oldRecord then table.insert(recordList, newRecord) else local record = mergeFunction(oldRecord, newRecord) if record then table.insert(recordList, record) end end end return recordList end --以本地为主,添加本地(oldRecordList)存在而远端(newRecordList)缺失的 function SqliteTable:mergeBaseLocal(oldRecordList, newRecordList, mergeFunction) local newRecordDict = {} for i, newRecord in ipairs(newRecordList) do local key = newRecord:getKey() local str = self:keyToStr(key) newRecordDict[str] = newRecord end local recordList = {} for i, oldRecord in ipairs(oldRecordList) do local key = oldRecord:getKey() local str = self:keyToStr(key) local newRecord = newRecordDict[str] if not newRecord then table.insert(recordList, oldRecord) else local record = mergeFunction(oldRecord, newRecord) if record then table.insert(recordList, record) end end end return recordList end --------------------------------------------------------------------------------------------- -- 数据库操作底层私有函数 --------------------------------------------------------------------------------------------- function SqliteTable:doInsert(record) local time = self.databaseApis.getTimeFunc() local version = self.databaseApis.getVersionFunc() record:setLastUpdateTime(time) record:setLastUpdateVersion(version) local keyList = {} local valueList = {} local columnList = self:getColumnList() for i, col in ipairs(columnList) do local key = col.name local value = record[key] or col.defaultValue if type(value) ~= col.type then printWarn(LOGTAG, "doInsert, 类型错误 tableName:%s, key:%s, value:%s, valueType:%s, tableType:%s", self:getName(), key, value, type(value), col.type) if col.type == "string" then value = tostring(value) elseif col.type == "number" then value = tonumber(value) end if value == nil then printError(LOGTAG, "doInsert, 无法转换类型") return end end table.insert(keyList, key) table.insert(valueList, SqliteUtil:luaValueToStr(value, col.type)) end local str = string.format("INSERT INTO %s (%s) VALUES (%s)", self:getName(), table.concat(keyList, ","), table.concat(valueList, ",")) record:clearMetadata() self:setCache(record) return self.databaseApis.exec(str) end function SqliteTable:doDelete(record) self:genQuery():where(self:getKeyCondition(record:getKey())):delete() self:delCache(record) end function SqliteTable:doUpdate(record) local time = self.databaseApis.getTimeFunc() local version = self.databaseApis.getVersionFunc() record:setLastUpdateTime(time) record:setLastUpdateVersion(version) self:genQuery():where(self:getKeyCondition(record:getKey())):updateRecord(record) record:clearMetadata() self:setCache(record) end function SqliteTable:doSelectKey(key) local list = self:doSelectCondition(self:getKeyCondition(key)) if #list == 0 then return nil elseif #list > 1 then printError(LOGTAG, "doSelect, key 重复") end self:setCache(list[1]) return list[1] end function SqliteTable:doSelectCondition(condition) local list = self:genQuery():where(condition):get( function(row) local record = self.recordCls.new(self) record:setSqliteData(row) self:setCache(record) return record end ) return list end function SqliteTable:getKeyCondition(key) if key == nil then printError(LOGTAG, "getKeyCondition, key 不能为nil") return end if type(key) ~= "table" then key = { key } end local conditionList = {} for i, col in ipairs(self.primaryList) do local colName = col.name local value = key[i] if value == nil then printError(LOGTAG, "getKeyCondition, 表%s中key不合法, key:%s", self:getName(), table.concat(key, ",")) end local condition = self:genCondition(self.col[colName]):isEqual(value) table.insert(conditionList, condition) end return SqliteUtil:connectCondionWithAnd(conditionList) end --------------------------------------------------------------------------------------------- --缓存相关私有函数 --------------------------------------------------------------------------------------------- function SqliteTable:getCache(key) if not self.useCache then return end local str = self:keyToStr(key) return self.recordDict[str] end function SqliteTable:setCache(record) if not self.useCache then return end if not record then return end local key = record:getKey() local str = self:keyToStr(key) self.recordDict[str] = record end function SqliteTable:delCache(record) if not self.useCache then return end if not record then return end local key = record:getKey() local str = self:keyToStr(key) self.recordDict[str] = nil end function SqliteTable:clearCache() if not self.useCache then return end self.recordDict = {} end --------------------------------------------------------------------------------------------- --------------------------------------------------------------------------------------------- function SqliteTable:keyToStr(key) if type(key) ~= "table" then key = { key } end for i, v in ipairs(key) do if math.type(v) == "integer" then v = string.format("%d", v) end key[i] = v end return table.concat(key, "__") end function SqliteTable:strToKey(str) local keys = string.split(tostring(str), "__") if #keys == 1 then return keys[1] end return keys end --------------------------------------------------------------------------------------------- --------------------------------------------------------------------------------------------- function SqliteTable:getColumnListFromSql() local str = "PRAGMA table_info(" .. self:getName() .. ")" local list = {} self.databaseApis.exec( str, function(udata, colCount, values, names) local colName = nil local colType = nil for i = 1, colCount do if names[i] == "name" then colName = values[i] elseif names[i] == "type" then colType = SqliteUtil:sqlTypeNameToLuaTypeName(values[i]) end end local column = SqliteColumn.new(self, colName, colType) table.insert(list, column) return 0 end ) return list end function SqliteTable:haveColumnListFromSql(colNameList) local colList = self:getColumnListFromSql() local colNameDict = {} for i, col in ipairs(colList) do colNameDict[col.name] = col.name end for i, name in ipairs(colNameList) do if not colNameDict[name] then return false end end return true end function SqliteTable:haveColumnFromSql(colName) return self:haveColumnListFromSql({ colName }) end function SqliteTable:addColumn(column) printInfo(LOGTAG, "addColumn, tableName:%s, colName:%s, colType:%s", self.name, column.name, column:getSqlType()) local str = "ALTER TABLE " .. self.name .. " ADD COLUMN " .. column.name .. " " .. column:getSqlType() return self.databaseApis.exec(str) end -- function SqliteTable:delColumn(column) -- printInfo(LOGTAG, "delColumn, tableName:%s, colName:%s", self.name, column.name) -- local str = "ALTER TABLE " .. self.name .. " DROP COLUMN " .. column.name -- return self.databaseApis.exec(str) -- end -- function SqliteTable:changeColumn(oldColumn, newColumn) -- printInfo( -- LOGTAG, -- "changeColumn, tableName:%s, oldColName:%s, oldColType:%s, newColName:%s, newColType:%s", -- self.name, -- oldColumn.name, -- oldColumn:getSqlType(), -- newColumn.name, -- newColumn:getSqlType() -- ) -- local str = "ALTER TABLE " .. self.name .. " CHANGE COLUMN " .. oldColumn.name .. " " .. newColumn.name .. " " .. newColumn:getSqlType() -- return self.databaseApis.exec(str) -- end function SqliteTable:getLastUpdateVersion() local list = self:genQuery() :select(self.col.lastUpdateVersion) :distinct() :get("lastUpdateVersion") if #list <= 0 then return "0.0.0" end list = util.linq(list) :order(function(a, b) return util.string.compareVersion(a, b) <= 0 end) :array() return list[#list] end return SqliteTable maine--[[ 用于android/ios的boot文件 author:zhangpeng time:2025-07-18 17:58:16 ]] local _ENV = _G --FORCE CLEAN ENV local LOGTAG = "[boot/main]" print(LOGTAG.."start 000") local json = require("rapidjson") print(LOGTAG.."launch luaengine from here") local luaengine = require("luaengine") local UnityEngine = CS.UnityEngine local AET = CS.AET local isEditor = CS.UnityEngine.Application.isEditor local YooAssetLoader = CS.YooAssetLoader.Instance _G.BOOT_MAIN_FILE = "boot/main" _G.GAME_MAIN_FILE = "main/main" local debug_flag = true if CS.LocalDataStorage.Get("PRINT_EVERY_LUA_CALL") == "true" then debug.sethook(function(event,line) local info = debug.getinfo(2) if info.currentline > 0 then print(string.format("%s:%s:%s:%s:%s",info.short_src,tostring(info.currentline),tostring(info.linedefined),tostring(info.name),tostring(info.namewhat))) end end, "c" ) end local cached_lua_ret_map = {} local CLEAR_ALL_LUA_CACHES = function() print("[boot.main] 清空lua缓存") for k,_ in pairs(cached_lua_ret_map) do cached_lua_ret_map[k] = nil end end local _loadlua = function (bytes, file, opts, env) if bytes == nil or bytes == "" then error("lua文件不存在->"..file..":"..tostring(bytes).. "\n" .. debug.traceback()) end if opts == "b" then print(LOGTAG .. "loadlua:bytes file") bytes = AET.Dec(bytes) end local f,err = load(bytes, file, opts, env) if f then local ok,ret = xpcall(f,function(err) CS.UnityEngine.Debug.LogError(string.format("加载lua失败[%s]%s\n%s",file,tostring(err),debug.traceback())) end) if not string.lower(file):find("reslink") and not isEditor then cached_lua_ret_map[file] = {ret = ret} end return ret, env else CS.UnityEngine.Debug.LogError("加载lua失败" .. file) error(tostring(err) .. "\n" .. debug.traceback()) end end -- 热更结束后加载lua文件 -- @ filename:要加载的lua文件名 -- @ env:lua环境,用于加载 Lua 文件的执行环境 -- _require函数会根据传入的参数加载指定的 Lua 文件,然后执行它,最终返回加载结果 local _require = function(env, filename) local ret = cached_lua_ret_map[filename] if ret then return ret.ret end print("[require]", filename) -- local filepath = string.lower(filename) local filepath = filename -- 使用YooAssetLoader同步加载资源,启动时候已经把ab加载到内存了 local src = YooAssetLoader:LoadText(filepath) return _loadlua(src, filename, "bt", env) end local _newenv = function() -- local _G = _G local _E = _G local rawset = _G.rawset local env = { _G = _G, _print = print, CLEAR_ALL_LUA_CACHES = CLEAR_ALL_LUA_CACHES, ENV_REQUIRE = _require } _G.setmetatable( env, { __index = function(t, k) local v = _E[k] rawset(t, k, v) return v end } ) return env end --Run Main Code do print(LOGTAG .. "start run main code") local _ENV = _newenv() _ENV.CLEAR_ENV = function() for k, _ in pairs(_ENV) do _ENV[k] = nil end end _ENV.raw_require = raw_require or require _ENV._require = _require _ENV.require = function(filename, _env) _env = _env or _ENV return _require(_env, filename) end require("boot/build_config") -- 执行main/main.lua print(LOGTAG.." -------- RUN GAME_MAIN_FILE -------- ") require(_G.GAME_MAIN_FILE) endmain:require("framework/core/db/playerprefs/PlayerPrefsMgr")PlayerPrefsKeys--[[ luaide 模板位置位于 Template/FunTemplate/NewFileTemplate.lua 其中 Template 为配置路径 与luaide.luaTemplatesDir luaide.luaTemplatesDir 配置 https://www.showdoc.cc/web/#/luaide?page_id=713062580213505 author:{author} time:2025-08-20 10:50:14 ]] pbreslinkreturn { --BASIC --ASSET chat = {"Assets/AssetsPackage/Res/framework/proto/chat.pb.bytes", 0, 9}, echo = {"Assets/AssetsPackage/Res/framework/proto/echo.pb.bytes", 0, 9}, head = {"Assets/AssetsPackage/Res/framework/proto/head.pb.bytes", 0, 9}, user = {"Assets/AssetsPackage/Res/framework/proto/user.pb.bytes", 0, 9}, handshake = {"Assets/AssetsPackage/Res/framework/proto/handshake.pb.bytes", 0, 9}, } mainlocal LOGTAG = "[main/main]" require("framework/main") require("data/main") require("modules/main") printInfo(LOGTAG,"main/main.lua 进入游戏逻辑 end 999 ") App.init() Ext@ local UnityEngine = CS.UnityEngine local GameObject = UnityEngine.GameObject local Quaternion = UnityEngine.Quaternion local LuaHelperReadFile = CS.LuaHelper.ReadFile local LuaHelperDeleteFile = CS.LuaHelper.DeleteFile local type = type local rawget = rawget local rawset = rawset --Ext UnityClass Quaternion local QuaternionCls = xlua.metatable_operation(typeof(Quaternion)) local QuaternionClsMul = HackFunc(QuaternionCls, "__mul") QuaternionCls.__mul = function(q, v) if type(v) == "number" then return Quaternion(q.x*v, q.y*v, q.z*v, q.w*v) else return QuaternionClsMul(q, v) end end QuaternionCls.__add = function(q, v) return Quaternion(q.x+v.x, q.y+v.y, q.z+v.z, q.w+v.w) end QuaternionCls.__sub = function(q, v) return Quaternion(q.x-v.x, q.y-v.y, q.z-v.z, q.w-v.w) end local ButtonClickedEventCls = xlua.metatable_operation(typeof(UnityEngine.UI.Button.ButtonClickedEvent)) rawset(ButtonClickedEventCls, "AddListener", ButtonClickedEventCls.__index(nil, "AddListener")) local ButtonClickedEventClsIndex = HackFunc(ButtonClickedEventCls, "__index") ButtonClickedEventCls.__index = function(ud, k) local v = rawget(ButtonClickedEventCls, k) if v then return v end v = ButtonClickedEventClsIndex(ud, k) if v then return v end end CosLuaUploadTask---@class CosLuaUploadTask:LuaClass local CosLuaUploadTask = defClass("CosLuaUploadTask") local LOGTAG = "CosLuaUploadTask" local CosSDKAPI = CS.iHuman.UnitySz.Framework.COS.CosSDKAPI CosLuaUploadTask.STATUS = { NONE = 0, RUNNING = 1, SUC = 2, FAIL = 3, CANCEL = 4, } ---@param srcPath string 文件路径 ---@param dstPath string 自定义url的文件名,不能为 nil ---@param serviceType CosLuaServiceType 服务类型 ---@param progressCallback fun(progress:number, srcPath:string, uploadTask:CosLuaUploadTask) ---@param completeCallback fun(result:boolean, data:{errorCode:integer, errorMsg:string, srcPath:string}, uploadTask:CosLuaUploadTask) ---@param prepareUrlCallback fun(originFilePath:string, prepareFileUrl:string, prepareFileRelativeUrl:string) function CosLuaUploadTask:ctor(srcPath, dstPath, serviceType, progressCallback, completeCallback, prepareUrlCallback) self.srcPath = srcPath self.dstPath = dstPath self.progress = 0 self.serviceType = serviceType self.progressCallback = progressCallback self.completeCallback = completeCallback self.prepareUrlCallback = prepareUrlCallback self.status = self.STATUS.NONE self.sdkUploadTask = nil end function CosLuaUploadTask:start() printInfo(LOGTAG, "start srcPath:%s", self.srcPath) if self.status ~= self.STATUS.NONE then printWarn(LOGTAG, "CosLuaUploadTask start error. status(%s) is not none.", tostring(self.status)) return end self.status = self.STATUS.RUNNING local cosXmlServer = CosLuaTemporaryCredential:getCosXmlServer(self.serviceType) if not cosXmlServer then self:onComplete(false, {errorCode = -4, errorMsg = "cosXmlServer is nil"}) return end local cosLuaCredentialBean = CosLuaTemporaryCredential:getCredential(self.serviceType) if not cosLuaCredentialBean then self:onComplete(false, {errorCode = -5, errorMsg = "cosLuaCredentialBean is nil"}) return end local prepareUrl,prepareUrlPrefix = CosLuaTemporaryCredential:getPrepareUrl(self.serviceType) printInfo(LOGTAG, "srcPath:%s, prepareUrl:%s", self.srcPath, prepareUrl..self.dstPath) if self.prepareUrlCallback then self.prepareUrlCallback(self.srcPath, prepareUrl..self.dstPath, prepareUrlPrefix..self.dstPath) end self.sdkUploadTask = CosSDKAPI.TransferUploadFile(cosXmlServer, cosLuaCredentialBean.bucket_name, self.srcPath, cosLuaCredentialBean.allow_prefix..self.dstPath, function (progress) if self.sdkUploadTask == nil then return end self:onProgress(progress) end, function (result, code, message) if self.sdkUploadTask == nil then return end self:onComplete(result, {errorCode = code, errorMsg = message or ""}) end) end function CosLuaUploadTask:cancel() if self.status ~= self.STATUS.RUNNING then printWarn(LOGTAG, "CosLuaUploadTask cancel error. status(%s) is not RUNNING.", tostring(self.status)) return end self.status = self.STATUS.CANCEL local sdkUploadTask = self.sdkUploadTask self.sdkUploadTask = nil if sdkUploadTask then if sdkUploadTask.Status == 3 then local cosXMLUploadTask = sdkUploadTask.Result cosXMLUploadTask:Cancel() end end self:onComplete(false, {errorCode = -6, errorMsg = "user cancelled"}) end function CosLuaUploadTask:isFinished() return (self.status == self.STATUS.SUC or self.status == self.STATUS.FAIL or self.status == self.STATUS.CANCEL) end function CosLuaUploadTask:isSuc() return self.status == self.STATUS.SUC end function CosLuaUploadTask:canStart() return self.status == self.STATUS.NONE end ---获取失败,上层调用 ---@param service_type CosLuaServiceType function CosLuaUploadTask:failWithErrorCredential(service_type) if self.status == self.STATUS.NONE and self.service_type == service_type then self:onComplete(false, {errorCode = -3, errorMsg = "fetch credential error"}) end end function CosLuaUploadTask:getStatus() return self.status end ---@private ---@param result boolean ---@param data {errorCode:integer, errorMsg:string} function CosLuaUploadTask:onComplete(result, data) printInfo(LOGTAG, "onComplete result:%s, data:%s", result, table.toString(data)) self.status = result and self.STATUS.SUC or self.STATUS.FAIL if self.completeCallback then self.completeCallback(result, data, self) end end ---@private ---@param progress number function CosLuaUploadTask:onProgress(progress) self.progress = progress if self.progressCallback then self.progressCallback(progress, self.srcPath, self) end end return CosLuaUploadTask SyncMgr ---@class SyncMgr:LuaStaticClass local SyncMgr = defClassStatic("SyncMgr") local LOGTAG = SyncMgr.__cls_name function SyncMgr:init(isGuestFunc) self.isGuestFunc = isGuestFunc self.historySyncTableList = {} self:clear() Msg.add( {Msg.LOGOUT, Msg.LOGIN_USER_SUCCESS}, function(...) self:msgHandler(...) end ) TimerMgr:add( function(dt) self:update(dt) end , nil, 0) end function SyncMgr:msgHandler(msgId, ...) if msgId == Msg.LOGIN_USER_SUCCESS then self:initSyncTable() self:start() elseif msgId == Msg.LOGOUT then self:stop() self:clear() end end function SyncMgr:clear() self.isStart = false self.dbTableDict = {} self:clearForOneSync() end function SyncMgr:clearForOneSync() self.time = 0 self.isSync = false self.syncDBTable = nil end function SyncMgr:initSyncTable() self:clear() local list = SqliteMgr:getDBTableList() for i, v in ipairs(list) do if v.useSync then local list = v:getAllNeedSync() if #list > 0 then self:addSyncTable(v) end --将上次正在上传状态的数据,重置成需要上传状态 local list = v:getAllIsSync() if #list > 0 then for j, record in ipairs(list) do record:setSyncState(SqliteTable.SYNC_STATE.NEED_SYNC) end v:setByList(list) self:addSyncTable(v) end end end end function SyncMgr:addSyncTable(dbTable) table.insert(self.historySyncTableList, dbTable) self.dbTableDict[dbTable:getName()] = dbTable end function SyncMgr:remSyncTable(dbTable) self.dbTableDict[dbTable:getName()] = nil end function SyncMgr:start() if self.isStart then return end if self.isGuestFunc() then return end self.isStart = true end function SyncMgr:stop() if not self.isStart then return end self.isStart = false end function SyncMgr:update(dt) if not self.isStart then return end if self.isSync then self.time = self.time + dt self:checkSyncTimeout() return end for i, dbTable in pairs(self.dbTableDict) do self:doSync(dbTable) break end end function SyncMgr:checkSyncTimeout() if self.time < 15 then return false end self.time = 0 local result = RpcResult.new() result:setError(-1, 0, "timeout, may not call handler in doSync") self:afterSync(result) printWarn(LOGTAG, "update, timeout") return true end function SyncMgr:doSync(dbTable) self:remSyncTable(dbTable) self.isSync = true self.syncDBTable = dbTable local recordList = self.syncDBTable:getAllNeedSync() if #recordList == 0 then printInfo(LOGTAG, "doSync, no data to sync, dbTable:%s", tostring(self.syncDBTable:getName())) self:clearForOneSync() return end for i, record in ipairs(recordList) do record:setSyncState(SqliteTable.SYNC_STATE.IS_SYNCING) end self.syncDBTable:setByList(recordList) self.syncDBTable:push( recordList, function(result) self:afterSync(result) end ) end function SyncMgr:afterSync(result) if (not result) or (not self.isSync) then printInfo(LOGTAG, "afterSync, no data to handle, isSync:%s", tostring(self.isSync)) self:clearForOneSync() return end local recordList = self.syncDBTable:getAllIsSync() if #recordList == 0 then printInfo(LOGTAG, "afterSync, no data after sync, dbTable:%s", tostring(self.syncDBTable:getName())) self:clearForOneSync() return end if not result:getResult() then printInfo(LOGTAG, "afterSync, sync fail %s count:%s", tostring(self.syncDBTable:getName()), #recordList) printInfo(LOGTAG, "afterSync, %s", result:getErrorString()) for i, record in ipairs(recordList) do record:setSyncState(SqliteTable.SYNC_STATE.NEED_SYNC) end else printInfo(LOGTAG, "afterSync, sync success %s count:%s", tostring(self.syncDBTable:getName()), #recordList) for i, record in ipairs(recordList) do record:setSyncState(SqliteTable.SYNC_STATE.HAVE_SYNCED) end end self.syncDBTable:setByList(recordList) self:clearForOneSync() end -- 全量同步 function SyncMgr:syncAll() printInfo(LOGTAG, "syncAll") local list = SqliteMgr:getDBTableList() for i, dbTable in ipairs(list) do if dbTable.useSync then local list = dbTable:getAll() if #list > 0 then dbTable:syncByList(list) end end end end function SyncMgr:printHistorySyncTable() local dict = {} -- printInfo(LOGTAG, "print sync table history beging") for i, dbTable in ipairs(self.historySyncTableList) do local name = dbTable.name -- printInfo(LOGTAG, "seq:%s, name:%s", i, name) dict[name] = dict[name] or 0 dict[name] = dict[name] + 1 end -- printInfo(LOGTAG, "print sync table history finish") printInfo(LOGTAG, "print sync table statistics begin") local totalLength = 0 for name, count in pairs(dict) do printInfo(LOGTAG, "name:%s, count:%s", name, count) end printInfo(LOGTAG, "print sync table statistics finish") end BoundUtil  local BoundUtil = {} function BoundUtil:getObjBounds(obj, onlySelf) local p_max = Vector3.zero local p_min = Vector3.zero local center = Vector3.zero local p = obj.transform.position local mesh = obj:GetComponent(typeof(CS.UnityEngine.Renderer)) if mesh ~= nil then local b = mesh.bounds p_max = b.max p_min = b.min center = b.center end if not onlySelf then p_min, p_max = BoundUtil:_recursionCalculateBounds(obj, p_min, p_max) end if mesh == nil then center = BoundUtil:_calculateCenter(p_max, p_min) end local size = Vector3(p_max.x - p_min.x, p_max.y - p_min.y, p_max.z - p_min.z) local bound = UnityEngine.Bounds(center, size) bound.size = size bound.extents = size / 2 obj.offset_min = Vector2(p_min.x - p.x, p_min.y - p.y) obj.offset_max = Vector2(p_max.x - p.x, p_max.y - p.y) return bound end -- 计算包围盒顶点 function BoundUtil:_recursionCalculateBounds(node, p_min, p_max) local calculateF calculateF = function (obj) if obj.transform.childCount <= 0 then return end for i = 1, obj.transform.childCount do local item = obj.transform:GetChild(i - 1).gameObject local m = item:GetComponent(typeof(CS.UnityEngine.Renderer)) if m ~= nil and item.activeSelf then local b = m.bounds if p_max:Equals(Vector3.zero) and p_min:Equals(Vector3.zero) then p_max = b.max p_min = b.min end if b.max.x > p_max.x then p_max.x = b.max.x end if b.max.y > p_max.y then p_max.y = b.max.y end if b.max.z > p_max.z then p_max.z = b.max.z end if b.min.x < p_min.x then p_min.x = b.min.x end if b.min.y < p_min.y then p_min.y = b.min.y end if b.min.z < p_min.z then p_min.z = b.min.z end end if item.activeSelf then calculateF(item) end end end calculateF(node) return p_min, p_max end function BoundUtil:_calculateCenter(p_max, p_min) local xc = (p_max.x + p_min.x) / 2 local yc = (p_max.y + p_min.y) / 2 local zc = (p_max.z + p_min.z) / 2 return Vector3(xc, yc, zc) end function BoundUtil:isObjIntersect(obj1, obj2) local b1 = obj1.__boundutil_bounds if not b1 then b1 = BoundUtil:getObjBounds(obj1) obj1.__boundutil_bounds = b1 end local p1 = obj1.transform.position local b2 = obj2.__boundutil_bounds if not b2 then b2 = BoundUtil:getObjBounds(obj2) obj2.__boundutil_bounds = b2 end local p2 = obj2.transform.position local minx1, miny1 = p1.x - b1.size.x / 2, p1.y - b1.size.y / 2 local maxx1, maxy1 = p1.x + b1.size.x / 2, p1.y + b1.size.y / 2 local minx2, miny2 = p2.x - b2.size.x / 2, p2.y - b2.size.y / 2 local maxx2, maxy2 = p2.x + b2.size.x / 2, p2.y + b2.size.y / 2 local minx = math.max(minx1, minx2) local miny = math.max(miny1, miny2) local maxx = math.min(maxx1, maxx2) local maxy = math.min(maxy1, maxy2) if minx > maxx or miny > maxy then return false end return (maxx - minx) * (maxy - miny) end return BoundUtiluidialogreslinkreturn { --BASIC --ASSET dialog_box_simple = {"Assets/AssetsPackage/Res/modules/common/ui/dialog_box/dialog_box_simple_ui.prefab", 0, 0}, } main,require("data/scene_config/SceneCfgList")mainOrequire("data/game_datas/LoginData") require("data/game_datas/CurrencyData")main%require("framework/device/Device")TextCfg--[[ from file:TextCfg.xlsx --]] local TextCfg = { [1] = { textId = "buy_suc", cn = "购买成功", en = "Purchase successful!", jp = '', desc = '', }, } return TextCfg IronSourceMgrG --[[ author:{zhangpeng} time:2023-10-19 22:16:13 ]] local IronSourceMgr, super = defClassStatic("IronSourceMgr") local LOG_TAG = "IronSourceMgr" local IOC_IS_UTIL_CLASS_NAME = "IronSourceUtil" function IronSourceMgr:init() printInfo(LOG_TAG,"是否IOS设备:%s",Device.isIOS()) if Device.isIOS() then self:registIosLuaCallBack() end end -- 注册ios IS lua回调 function IronSourceMgr:registIosLuaCallBack() printInfo(LOG_TAG,"注册ios广告回调 registIosLuaCallBack") local param = { didReceiveRewardForPlacementCb = function() printInfo(LOG_TAG,"观看完一个激励视频 lua") end, hasAvailableAdWithAdInfoCb = function () -- UIComsTool:showToast("有可用激励视频",1) printInfo(LOG_TAG,"[激励视频] call lua hasAvailableAdWithAdInfoCb") end } luaoc.callStaticMethod(IOC_IS_UTIL_CLASS_NAME, "registLuaCallback", param) end function IronSourceMgr:loadBannerView() if Device.isIOS() then printInfo(LOG_TAG,"lua侧请求banner广告") local ok, result = luaoc.callStaticMethod(IOC_IS_UTIL_CLASS_NAME, "loadBannerView") return result elseif Device.isAndroid() then -- todo:: end end function IronSourceMgr:showBannerView() if Device.isIOS() then luaoc.callStaticMethod(IOC_IS_UTIL_CLASS_NAME, "showBannerView") elseif Device.isAndroid() then -- todo:: end end function IronSourceMgr:hideBannerView() end function IronSourceMgr:loadReviewVideo() if Device.isIOS() then luaoc.callStaticMethod(IOC_IS_UTIL_CLASS_NAME, "loadReviewVideo") end end -- 主动检测是否有可用的激励视频 function IronSourceMgr:haveRewardVideo() if Device.isIOS() then local ok,ret = luaoc.callStaticMethod(IOC_IS_UTIL_CLASS_NAME, "haveRewardVideo") return ret elseif Device.isAndroid() then -- todo: return true else return true end end function IronSourceMgr:showReviewVideo(placmentName,itemId) if Device.isIOS() then local param = { placementName = "RV_UNLOCK_SKIN" -- 这个参数根据实际情况配置 } luaoc.callStaticMethod(IOC_IS_UTIL_CLASS_NAME, "showReviewVideo", param) elseif Device.isAndroid() then end end IronSourceMgr:init() KVDatabase< ---@class KVDatabase:LuaClass local KVDatabase = defClass("KVDatabase") local Path = CS.System.IO.Path local Directory = CS.System.IO.Directory local File = CS.System.IO.File local LOGTAG = KVDatabase.__cls_name local AES_KEY = "a1732436c18c7ac80c29080c9aef1aff" function KVDatabase:ctor(filePath) self.filePath = filePath self.backFilePath = self.filePath .. ".back" end ----------------------------------------- public ------------------------------------------- ---打开数据库。加载文件到内存 ---@return boolean 是否成功 function KVDatabase:open(key) if not self.filePath then return false end self.key = key local str = string.format(self.filePath) local path = Path.GetDirectoryName(str) .. "/" if Directory.Exists(path) == false then Directory.CreateDirectory(path) end self:_load() return true end ---关闭数据库 ---@return boolean function KVDatabase:close() return true end ---删除数据库 function KVDatabase:remove() if File.Exists(self.filePath) then File.Delete(self.filePath) end if File.Exists(self.backFilePath) then File.Delete(self.backFilePath) end end ---获取数据库的表obj ---@param tableCls any 表的类 ---@return SqliteTable 表的 obj function KVDatabase:getTable(tableCls, tableName) tableName = tableName or tableCls.__cls_name local apis = { get = function (...) return self:_get(tableName,...) end, set = function (...) return self:_set(tableName,...) end, getKeys = function (...) return self:_getKeys(tableName) end } local table = tableCls.new(apis, tableName) return table end ----------------------------------------- private ------------------------------------------- ---获取数据库某个表的值,给 table 用 ---@param tableName string 表名 function KVDatabase:_get(tableName, key, defaultValue) local data = self.data local keys = {tableName} if type(key) == "table" and key.__cls_type == nil then table.insertTo(keys, key) else table.insert(keys, key) end for _, key in ipairs(keys) do data = data[key] if data == nil then return defaultValue end end return data end ---设置数据库某个表的值,给 table 用 ---@param tableName string 表名 function KVDatabase:_set(tableName, key, value) local data = self.data local keys = {tableName} if type(key) == "table" and key.__cls_type == nil then table.insertTo(keys, key) else table.insert(keys, key) end for i = 1, #keys - 1 do local key = keys[i] if not data[key] then data[key] = {} end data = data[key] end data[keys[#keys]] = value self:_save() end function KVDatabase:_getKeys(tableName) local data = self.data local tableData = data[tableName] or {} local keys = {} for key, value in pairs(tableData) do table.insert(keys, key) end return keys end ---保存数据到文件 function KVDatabase:_save() printInfo(LOGTAG, "_save") local data = self.data local text = json.encode(data) if self.key then text = CS.AES256.Encrypt(text, self.key) end if string.isEmpty(text) then return end local writeOk = false if CS.System.IO and CS.System.IO.File and CS.System.IO.File.WriteAllText then printInfo(LOGTAG, "filepath:%s", self.filePath) CS.System.IO.File.WriteAllText(self.filePath, text) CS.System.IO.File.Copy(self.filePath, self.backFilePath, true) writeOk = true else printInfo(LOGTAG, "WriteAllText is nil! 请检查导出/裁剪配置,或平台API注入。尝试用Lua io写入。") -- 兼容性写法:用Lua io库写入 local f, err = io.open(self.filePath, "w+b") if f then f:write(text) f:close() writeOk = true -- 备份 local fb, err2 = io.open(self.backFilePath, "w+b") if fb then fb:write(text) fb:close() end else printError(LOGTAG, "Lua io.open 写入失败: " .. tostring(err)) end end return writeOk end local function try(cb,onError) local ok,ret = xpcall(cb,function(err,a,b,c) local tb = debug.traceback() local errstr = tostring(err) .. "\n" .. tb CS.UnityEngine.Debug.LogError(errstr) if onError then onError() end end) end ---加载文件到内存 function KVDatabase:_load() self.data = {} if not File.Exists(self.filePath) then return end local data = {} try( function() data = self:_getDataFromFile(self.filePath) end, function() printInfo(LOGTAG, "_load, 读取存档文件错误:%s", self.filePath) try( function() printInfo(LOGTAG, "_load, 尝试加载备份存档:%s", self.backFilePath) File.Copy(self.backFilePath, self.filePath, true) data = self:_getDataFromFile(self.filePath) dump(data, LOGTAG) end ) end ) self.data = data end function KVDatabase:_getDataFromFile(path) local text = File.ReadAllText(path) if string.isEmpty(text) then return {} end printVerbose(LOGTAG, "getDataFromFile, text: %s", text) if self.key then text = CS.AES256.Decrypt(text, self.key) end local data = json.decode(text) or {} return data end DoTweenAction<--[[ author:{zhangpeng} time:2025-08-27 19:45:31 ]] local api = CS.DoTweenUtil local DG = CS.DG.Tweening local Vector3 = CS.UnityEngine.Vector3 local dtween = {} -- 移动到 function dtween.MoveTo(obj, target, duration, onComplete, ease) return api.MoveTo(obj, target, duration, onComplete, ease or DG.Ease.Linear) end function dtween.MoveLocalTo(obj, target, duration, onComplete, ease) return api.MoveLocalTo(obj, target, duration, onComplete, ease or DG.Ease.Linear) end function dtween.ScaleTo(obj, target, duration, onComplete, ease) -- 如果target是数字,转换为Vector3统一缩放 if type(target) == "number" then target = Vector3(target, target, target) end return api.ScaleTo(obj, target, duration, onComplete, ease or DG.Ease.Linear) end -- 水平翻转 function dtween.FlipX(obj, duration, onComplete, ease) -- 获取当前缩放 local scale = obj:GetComponent(typeof(CS.UnityEngine.Transform)).localScale -- 设置缩放为负数,并保持y和z不变 return api.ScaleTo(obj, Vector3(-scale.x, scale.y, scale.z), duration, onComplete, ease or DG.Ease.Linear) end -- 垂直翻转 function dtween.FlipY(obj, duration, onComplete, ease) -- 获取当前缩放 local scale = obj:GetComponent(typeof(CS.UnityEngine.Transform)).localScale -- 设置缩放为负数,并保持x和z不变 return api.ScaleTo(obj, Vector3(scale.x, -scale.y, scale.z), duration, onComplete, ease or DG.Ease.Linear) end function dtween.RotateTo(obj, target, duration, onComplete, ease) return api.RotateTo(obj, target, duration, onComplete, ease or DG.Ease.Linear) end function dtween.RotateLocalTo(obj, target, duration, onComplete, ease) return api.RotateLocalTo(obj, target, duration, onComplete, ease or DG.Ease.Linear) end -- UI动作 function dtween.FadeTo(target, alpha, duration, onComplete, ease) return api.FadeTo(target, alpha, duration, onComplete, ease or DG.Ease.Linear) end function dtween.ColorTo(graphic, color, duration, onComplete, ease) return api.ColorTo(graphic, color, duration, onComplete, ease or DG.Ease.Linear) end function dtween.SizeDeltaTo(rectTransform, size, duration, onComplete, ease) return api.SizeDeltaTo(rectTransform, size, duration, onComplete, ease or DG.Ease.Linear) end function dtween.AnchorPosTo(rectTransform, anchorPos, duration, onComplete, ease) return api.AnchorPosTo(rectTransform, anchorPos, duration, onComplete, ease or DG.Ease.Linear) end -- 序列动作 function dtween.CreateSequence() return api.CreateSequence() end function dtween.DoSequence(tweens, onComplete, onStart, onUpdate) return api.DoSequence(tweens, onComplete, onStart, onUpdate) end function dtween.DoSpawn(tweens, onComplete, onStart, onUpdate) return api.DoSpawn(tweens, onComplete, onStart, onUpdate) end -- 重复动作 function dtween.Repeat(tween, times, onComplete, onStepComplete) return api.Repeat(tween, times, onComplete, onStepComplete) end function dtween.RepeatForever(tween, onStepComplete) return api.RepeatForever(tween, onStepComplete) end function dtween.CreateRepeatSequence(tweens, times, onComplete, onStepComplete) return api.CreateRepeatSequence(tweens, times or 1, onComplete, onStepComplete) end function dtween.CreateYoyoSequence(tweens, times, onComplete, onStepComplete) return api.CreateYoyoSequence(tweens, times or 1, onComplete, onStepComplete) end -- 延迟调用 function dtween.DelayedCall(delay, onComplete, ignoreTimeScale) return api.DelayedCall(delay, onComplete, ignoreTimeScale or false) end function dtween.DelayedCallWithTag(delay, onComplete, tag, ignoreTimeScale) return api.DelayedCallWithTag(delay, onComplete, tag, ignoreTimeScale or false) end function dtween.DelayedCallWithProgress(delay, onComplete, onUpdate, ignoreTimeScale) return api.DelayedCallWithProgress(delay, onComplete, onUpdate, ignoreTimeScale or false) end function dtween.CancelDelayedCalls(tag) api.CancelDelayedCalls(tag) end -- 预设动画效果 function dtween.PopIn(obj, duration, onComplete, ease) return api.PopIn(obj, duration or 0.3, onComplete, ease or DG.Ease.OutBack) end function dtween.PopOut(obj, duration, onComplete, ease) return api.PopOut(obj, duration or 0.3, onComplete, ease or DG.Ease.InBack) end function dtween.PunchScale(obj, duration, strength, onComplete, ease) return api.PunchScale(obj, duration or 0.3, strength or 0.3, onComplete, ease or DG.Ease.Linear) end function dtween.ShakePosition(obj, duration, strength, onComplete, ease) return api.ShakePosition(obj, duration or 0.3, strength or 0.3, onComplete, ease or DG.Ease.Linear) end -- 值动画 function dtween.ValueTo(from, to, duration, onUpdate, onComplete, ease) return api.ValueTo(from, to, duration, onUpdate, onComplete, ease or DG.Ease.Linear) end -- 常用缓动类型 dtween.Ease = { Linear = DG.Ease.Linear, InSine = DG.Ease.InSine, OutSine = DG.Ease.OutSine, InOutSine = DG.Ease.InOutSine, InQuad = DG.Ease.InQuad, OutQuad = DG.Ease.OutQuad, InOutQuad = DG.Ease.InOutQuad, InCubic = DG.Ease.InCubic, OutCubic = DG.Ease.OutCubic, InOutCubic = DG.Ease.InOutCubic, InQuart = DG.Ease.InQuart, OutQuart = DG.Ease.OutQuart, InOutQuart = DG.Ease.InOutQuart, InQuint = DG.Ease.InQuint, OutQuint = DG.Ease.OutQuint, InOutQuint = DG.Ease.InOutQuint, InExpo = DG.Ease.InExpo, OutExpo = DG.Ease.OutExpo, InOutExpo = DG.Ease.InOutExpo, InCirc = DG.Ease.InCirc, OutCirc = DG.Ease.OutCirc, InOutCirc = DG.Ease.InOutCirc, InElastic = DG.Ease.InElastic, OutElastic = DG.Ease.OutElastic, InOutElastic = DG.Ease.InOutElastic, InBack = DG.Ease.InBack, OutBack = DG.Ease.OutBack, InOutBack = DG.Ease.InOutBack, InBounce = DG.Ease.InBounce, OutBounce = DG.Ease.OutBounce, InOutBounce = DG.Ease.InOutBounce, } return dtweenApp--[[ author:{zhangpeng} time:2022-05-11 17:07:11 ]] local App, super = defClassStatic("App") local SceneManagement = CS.UnityEngine.SceneManagement local GameObject = CS.UnityEngine.GameObject local _config = nil local Path = CS.System.IO.Path local File = CS.System.IO.File local LOGTAG = "App" -- 不销毁的节点 local LuaAppMsgGameObjectName = "LuaAppMsgGameObject" function App.init(config) _config = config or {} math.randomseed(os.time()) CS.UnityEngine.Time.timeScale = 1 CS.UnityEngine.Application.targetFrameRate = 60 CS.UnityEngine.Screen.sleepTimeout = CS.UnityEngine.SleepTimeout.NeverSleep App._paused = 0 App._background = false App._exited = false App._initAppMsg() App.onInit() end function App.onInit() printInfo(LOGTAG,"App.onInit()") -- 初始化一些东西 ResLoader.init() SceneMgr:init() App.main() end function App.main(...) if not CS.LuaHelper.UseLocalRes() then print("加载urp_shaders....") -- todo: 加载urp_shaders end if CS.UnityEngine.Application.isEditor then SceneMgr:enter(SceneCfgList.SCENES.LoginScene) else SceneMgr:enter(SceneCfgList.SCENES.LoginScene) end end function App.enableAllTouches() App.isAllTouchesEnabled = true App._lastEnableAllTouchesLoc = util.lua.getLoc(2) util.ugui.enableAllTouches() printInfo(LOGTAG,"App.enableAllTouches()") end function App.disableAllTouches() App.isAllTouchesEnabled = false App._lastDisbleAllTouchesLoc = util.lua.getLoc(2) util.ugui.disableAllTouches() printInfo(LOGTAG,"App.disableAllTouches()") end function App.exit() Msg.send(Msg.APP_EXIT) --必须清空之前的,尤其可能注册会回调的,闭包会不释放 --这里要求了LuaGlobal上注册的Event.Update只能用来做全局Timer local luaGlobal = CS.LuaGlobal.instance local luaGlobalGo = luaGlobal.gameObject luaGlobal:clearActionQueue() -- --timer必须清除,因为app可能重启,需要卸载干净上次注册的,否则上个_ENV的timer还在跑。 Event.remove(luaGlobalGo,Event.Update) Event.remove(luaGlobalGo,Event.LateUpdate) Event.remove(luaGlobalGo,Event.FixedUpdate) luaGlobalGo:StopAllSounds() luaGlobalGo:StopAllActions() luaGlobalGo:RemoveAllComponentsByType(typeof(CS.AudiosComponent)) luaGlobalGo:RemoveAllComponentsByType(typeof(CS.HttpDownloadCom)) CS.UnityEngine.Input.multiTouchEnabled = false App.enableAllTouches() CS.LuaGlobal.instance:StopAllCoroutines() local SceneManager = SceneManagement.SceneManager for i = 0, SceneManager.sceneCount - 1 do local scene = SceneManager.GetSceneAt(i) local items = scene:GetRootGameObjects() for j = 0, items.Length - 1 do GameObject.Destroy(items[j]) end end end function App.pauseScene() App.pauseAudio() end function App.pause() App._paused = App._paused + 1 CS.UnityEngine.Time.timeScale = 0 App.pauseScene() if App._paused == 1 then Msg.send(Msg.APP_PAUSED) return true else return false end end function App.resumeScene() App.resumeAudio() end function App.resume() --print("[App.resume]", timer, audio, timeline, debug.traceback()) if App._paused == 0 then return false end App._paused = App._paused - 1 if App._paused == 0 then CS.UnityEngine.Time.timeScale = 1 App.resumeScene() Msg.send(Msg.APP_RESUME) return true else return false end end function App.isPaused() return App._paused > 0 end function App.isBackground() return App._background end function App.pauseAudio() local scene = SceneMgr:getCurSceneObj() if scene then local rootGo = scene:GetGameRoot() local result = util.pause.pauseAudio(scene) if rootGo.__pausedAudioList__ then table.insertTo(rootGo.__pausedAudioList__,result) else rootGo.__pausedAudioList__ = result end end end function App.resumeAudio() local scene = SceneMgr:getCurSceneObj() if scene then local rootGo = scene:GetGameRoot() util.pause.resumeAudio(rootGo.__pausedAudioList__) rootGo.__pausedAudioList__ = {} end end function App._onBackground() App._background = true Msg.send(Msg.APP_BACKGROUND) App.pause() printInfo(">App._onBackground") -- print("[APP]_onBackground", Time.realtimeSinceStartup) end function App._onForeground() App._background = false App.resume() Msg.send(Msg.APP_FOREGROUND) printInfo(">App._onForeground") end function App._setLogLvl() if BUILD_ENV == ENV_DEVELOPMENT or BUILD_ENV == ENV_TESTING then else print = function(...) end end local LogType = CS.UnityEngine.LogType local logType = {[LogType.Error] = "LogError", [LogType.Exception] = "LogException", [LogType.Assert] = "LogAssert",} local logHash = {} --上报 Debug.LogError Debug.LogException Debug.LogAssert的日志 local logFunc = function(msg, stb, tt) if logType[tt] then if logHash[msg] then return end local handler = CS.LogHelper.luaLogFileWriteHandler local cs_err = "C# ERR,errorKey = " .. tostring(msg) local cs_stack = "C# ERR,c# traceback = " .. tostring(stb) local lua_stack = "C# ERR,lua traceback = " .. debug.traceback() if handler then handler:WriteLine(cs_err) handler:WriteLine(cs_stack) handler:WriteLine(lua_stack) end logHash[msg] = true if BUILD_ENV ~= ENV_PRODUCTION or CS.LocalDataStorage.Get(StrogeKeyDef.DEVICE_ALWAYS_SHOW_ERR_DIALOG) == "true" then UIComsTool:showDailog(tostring(msg),"ok", function (ui) FirebaseAnalyticsUtil:sendErrorInfo(msg) ui:close() end) end if Device.isIOS() then local param = { name = tostring(msg), reason = tostring(msg), callStack = tostring(stb), } luaoc.callStaticMethod("CustomAppController", "reportLuaErrorToFirebase", param) end end end CS.UnityEngine.Application.logMessageReceived("+", logFunc) Msg.add(Msg.APP_EXIT, function() CS.UnityEngine.Application.logMessageReceived("-", logFunc) end) end function App._initAppMsg() local go = CS.UnityEngine.GameObject.Find(LuaAppMsgGameObjectName) if go then CS.UnityEngine.GameObject.Destroy(go) --必须销毁上一次 end go = CS.UnityEngine.GameObject(LuaAppMsgGameObjectName) CS.UnityEngine.GameObject.DontDestroyOnLoad(go) go:AddEvent(Event.OnApplicationQuit, function() printInfo(LOGTAG, "Event.OnApplicationQuit") App.exit() end) go:AddEvent(Event.OnApplicationUnload, function() printInfo(LOGTAG, "Event.OnApplicationUnload") App.exit() end) if not _config.ignoreApplicationFocus then go:AddEvent(Event.OnApplicationFocus, function(focus) if focus then App._onForeground() else App._onBackground() end end) end Msg.add(Msg.APP_EXIT, function() local go = CS.UnityEngine.GameObject.Find(LuaAppMsgGameObjectName) if go then CS.UnityEngine.GameObject.Destroy(go) end end) endFirebaseEventEnum--[[ 埋点事件名字定义,按功能模块分类 author:{zhangpeng} time:2023-12-07 15:59:46 ]] local FirebaseEventEnum,_ = defClassStatic("FirebaseEventEnum") FirebaseEventEnum.currency = { earn_virtual_currency = "earn_virtual_currency", -- 获得虚拟货币 参数:{virtual_currency_name:string(Gem/GoldCoin), value:number} spend_virtual_currency = " spend_virtual_currency" -- 消耗虚拟货币 } -- 登录注册埋点 FirebaseEventEnum.login = { login_by_email = "login_by_email" } -- 大地图埋点 FirebaseEventEnum.worldmap = { click_building = "click_building" -- 点击大地图建筑 参数:大地图id, 建筑名字 } -- 互动场景埋点 FirebaseEventEnum.interactivescene = { } -- 换装埋点 FirebaseEventEnum.skinswitch = { } -- 形象列表场景埋点 FirebaseEventEnum.roleshowscene = { } -- 任务系统 FirebaseEventEnum.tasksystem = { } function FirebaseEventEnum:init() end FirebaseEventEnum:init()LoginUI--[[ author: zhangheng time: 2025-07-19 ]] local LoginUI, super = defClass("LoginUI", UILayer) local TMProUGUI = CS.TMPro.TextMeshProUGUI function LoginUI:ctor() super.ctor(self) self.R = ResLoader.loadResLink("modules/common/login/loginuireslink") end function LoginUI:onLoad() printInfo("LoginUI:onLoad") self.ui = GameObject.Instantiate(self.R.login_ui) self:addChild(self.ui) self:initData() self:initUI() end -- init ui function LoginUI:initUI() self.coin_count = self.ui:Seek("coin_count")[TMProUGUI] self.coin_count.text = self.data_coin_count end -- init data function LoginUI:initData() -- PlayerPrefsMgr:setInt(StrogeKeyDef.USER_COIN_COUNT, 200) self.data_coin_count = PlayerPrefsMgr:getInt(StrogeKeyDef.USER_COIN_COUNT) printInfo("LoginUI:initData coin_count: %s", self.data_coin_count) end return LoginUI LuaUtilZ --[[ luaide 模板位置位于 Template/FunTemplate/NewFileTemplate.lua 其中 Template 为配置路径 与luaide.luaTemplatesDir luaide.luaTemplatesDir 配置 https://www.showdoc.cc/web/#/luaide?page_id=713062580213505 author:{author} time:2022-05-15 16:57:36 ]] local LuaUtil = {} function LuaUtil.try(func, onError) return xpcall( func, function(err, a, b, c) local traceback = debug.traceback() local errStr = tostring(err) .. "\n" .. traceback CS.UnityEngine.Debug.LogError(errStr) CS.LogHelper.invokeLuaError(errStr) if onError then onError(err) end end ) end function LuaUtil.forEachCall(funcs, ...) for _, func in ipairs(funcs) do func(...) end end function LuaUtil.getLoc(depth) depth = depth or 1 local i = debug.getinfo(depth + 1) -- return string.format("%s:%d",i.short_src,i.currentline) return string.format("%s:%d",i.source,i.currentline) end local unpack = unpack or table.unpack -- 解决原生pack的nil截断问题,SafePack与SafeUnpack要成对使用 function LuaUtil.SafePack(...) local params = {...} params.n = select('#', ...) return params end -- 解决原生unpack的nil截断问题,SafePack与SafeUnpack要成对使用 function LuaUtil.SafeUnpack(safe_pack_tb) return unpack(safe_pack_tb, 1, safe_pack_tb.n) end -- 对两个SafePack的表执行连接 function LuaUtil.ConcatSafePack(safe_pack_l, safe_pack_r) local concat = {} for i = 1,safe_pack_l.n do concat[i] = safe_pack_l[i] end for i = 1,safe_pack_r.n do concat[safe_pack_l.n + i] = safe_pack_r[i] end concat.n = safe_pack_l.n + safe_pack_r.n return concat end -- 将字符串转换为boolean值 function LuaUtil.ToBoolean(s) local transform_map = { ["true"] = true, ["false"] = false, } return transform_map[s] end -- 深拷贝对象 function LuaUtil.DeepCopy(object) local lookup_table = {} local function _copy(object) if type(object) ~= "table" then return object elseif lookup_table[object] then return lookup_table[object] end local new_table = {} lookup_table[object] = new_table for index, value in pairs(object) do new_table[_copy(index)] = _copy(value) end return setmetatable(new_table, getmetatable(object)) end return _copy(object) end return LuaUtil CosLuaTagTask---@class CosLuaTagTask:LuaClass local CosLuaTagTask = defClass("CosLuaTagTask") local LOGTAG = "CosLuaTagTask" local CosSDKAPI = CS.iHuman.UnitySz.Framework.COS.CosSDKAPI CosLuaTagTask.STATUS = { NONE = 0, RUNNING = 1, SUC = 2, FAIL = 3, } ---@enum CosLuaTagTaskOpt CosLuaTagTask.OPT = { SET = 0, GET = 1, DELETE = 2, } ---@param cosUrl string cosurl ---@param opt CosLuaTagTaskOpt 操作类型 ---@param serviceType CosLuaServiceType 服务类型 ---@param completeCallback fun(result:boolean, data:{errorCode:integer, errorMsg:string, tags:table}, tagTask:CosLuaTagTask) function CosLuaTagTask:ctor(cosUrl, opt, tags, serviceType, completeCallback) self.cosUrl = cosUrl self.opt = opt self.tags = tags self.serviceType = serviceType self.completeCallback = completeCallback self.status = self.STATUS.NONE end function CosLuaTagTask:start() printInfo(LOGTAG, "start srcPath:%s", self.srcPath) if self.status ~= self.STATUS.NONE then printWarn(LOGTAG, "CosLuaTagTask start error. status(%s) is not none.", tostring(self.status)) return end self.status = self.STATUS.RUNNING local cosXmlServer = CosLuaTemporaryCredential:getCosXmlServer(self.serviceType) if not cosXmlServer then self:onComplete(false, {errorCode = -4, errorMsg = "cosXmlServer is nil"}) return end local cosLuaCredentialBean = CosLuaTemporaryCredential:getCredential(self.serviceType) if not cosLuaCredentialBean then self:onComplete(false, {errorCode = -5, errorMsg = "cosLuaCredentialBean is nil"}) return end local rPath = CosLuaTemporaryCredential:getRelativePath(self.cosUrl, self.serviceType) printInfo(LOGTAG, "cosUrl:%s, rPath:%s", self.cosUrl, rPath) if self.opt == self.OPT.GET then CosSDKAPI.GetObjectTagging(cosXmlServer, cosLuaCredentialBean.bucket_name, rPath, handler(self, self.onComplete)) elseif self.opt == self.OPT.SET then CosSDKAPI.PutObjectTagging(cosXmlServer, cosLuaCredentialBean.bucket_name, rPath, self.tags, handler(self, self.onComplete)) elseif self.opt == self.OPT.DELETE then CosSDKAPI.DeleteObjectTagging(cosXmlServer, cosLuaCredentialBean.bucket_name, rPath, handler(self, self.onComplete)) end end function CosLuaTagTask:isFinished() return (self.status == self.STATUS.SUC or self.status == self.STATUS.FAIL) end function CosLuaTagTask:isSuc() return self.status == self.STATUS.SUC end function CosLuaTagTask:canStart() return self.status == self.STATUS.NONE end ---获取失败,上层调用 ---@param service_type CosLuaServiceType function CosLuaTagTask:failWithErrorCredential(service_type) if self.status == self.STATUS.NONE and self.service_type == service_type then self:onComplete(false, -3, "fetch credential error") end end function CosLuaTagTask:getStatus() return self.status end ---@private ---@param result boolean ---@param code integer ---@param message string ---@param tags table|nil function CosLuaTagTask:onComplete(result, code, message, tags) local luaTags = {} for key, value in pairs(tags or {}) do luaTags[key] = value end tags = nil local data = {errorCode = code, errorMsg = message, tags = luaTags} printInfo(LOGTAG, "onComplete result:%s, data:%s", result, table.toString(data)) self.status = result and self.STATUS.SUC or self.STATUS.FAIL if self.completeCallback then self.completeCallback(result, data, self) end end return CosLuaTagTask Device  Device ={} local platform = CS.UnityEngine.Application.platform local RuntimePlatform = CS.UnityEngine.RuntimePlatform local Screen = CS.UnityEngine.Screen local ScreenOrientation = CS.UnityEngine.ScreenOrientation function Device.isIOS() return platform == RuntimePlatform.IPhonePlayer end function Device.isApple() return platform == RuntimePlatform.IPhonePlayer or platform == RuntimePlatform.OSXEditor end function Device.isAndroid() return platform == RuntimePlatform.Android end function Device.isHarmonyOS() return platform == RuntimePlatform.OpenHarmony end function Device.isWindows() return platform == RuntimePlatform.WindowsPlayer or platform == RuntimePlatform.WindowsEditor end function Device.isMac() return platform == RuntimePlatform.OSXEditor or platform == RuntimePlatform.OSXPlayer end function Device.isEditor() return platform == RuntimePlatform.OSXEditor or platform == RuntimePlatform.WindowsEditor end function Device.isPlayer() return platform == RuntimePlatform.WindowsPlayer or platform == RuntimePlatform.IPhonePlayer or platform == RuntimePlatform.OSXPlayer end function Device.isWebGL() return platform == RuntimePlatform.WebGLPlayer end function Device.isScreenLandscape() local orientation = Screen.orientation return orientation == ScreenOrientation.LandscapeLeft or orientation == ScreenOrientation.LandscapeRight end function Device.getPlatformStr() if platform == RuntimePlatform.IPhonePlayer then return "ios" elseif platform == RuntimePlatform.Android then return "android" elseif platform == RuntimePlatform.OSXEditor then return "macos" elseif platform == RuntimePlatform.WindowsEditor then return "windows" elseif platform == RuntimePlatform.WebGLPlayer then return "webgl" end return "unknow" end function Device.getPlatformLowerStr(ignoreEditor) if Device.isIOS() then return "ios" elseif Device.isAndroid() then return "android" elseif Device.isWindows() then return ignoreEditor and "ios" or "windows" elseif Device.isMac() then return ignoreEditor and "ios" or "mac" end return "ios" end ---是否桌面端 function Device.isDesktop() return not Device.isIOS() and not Device.isAndroid() and not Device.isHarmonyOS() end ---是否桌面端非编辑器 function Device.isDesktopPlayer() return platform == RuntimePlatform.WindowsPlayer or platform == RuntimePlatform.OSXPlayer end print("当前平台:" .. Device.getPlatformStr())mainrequire("framework/core/base/Log") require("framework/core/base/Enum") require("framework/core/base/ExtendLua") require("framework/core/base/Util") require("framework/core/base/utils/main") require("framework/core/base/Def") EnableGlobalCheck() require("framework/core/base/Env") require("framework/core/base/Msg") require("framework/core/base/resmgr/main") require("framework/core/base/ReslinkLoad") require("framework/core/base/Event") require("framework/core/base/TimerMgr") require("framework/core/base/extend/main") require("framework/core/base/touch/main") Msg.def("PAUSE") Msg.def("RESUME") Msg.def("EXIT") Msg.init() TimerMgr:init() Msg.add(Msg.PAUSE, function() TimerMgr:onAppPause() end) Msg.add(Msg.RESUME, function() TimerMgr:onAppResume() end) Msg.add(Msg.EXIT, function() -- Msg.send(Msg.LUA_EXIT) Event.exit() TimerMgr:exit() ResLoader.exit() Msg.exit() util.ugui.removeAllListeners() end) DeviceVibrationc--[[ 设备振动 author:{zhangpeng} time:2025-04-18 20:59:16 ]] local DeviceVibration, super = defClassStatic("DeviceVibration") local IOS_NATIVE_CLASS_NAME = "DeviceVibrationUtil" local JavaADClass = "com/fy/xgame/tilelink/util/VibrationUtil" function DeviceVibration:init() end function DeviceVibration:vibrate(milliseconds) if Device.isIOS() then luaoc.callStaticMethod(IOS_NATIVE_CLASS_NAME, "vibrate", {milliseconds}) elseif Device.isAndroid() then luaj.callStaticMethod(JavaADClass, "vibrate", {tostring(milliseconds)}) end end DeviceVibration:init() ExtendRect local RectCls = HackCSharpClass(CS.UnityEngine.Rect) local Vector3 = CS.UnityEngine.Vector3 RectCls.GetTopMid = function(self) return Vector3(self.center.x,self.max.y,0) end RectCls.GetBottomMid = function(self) return Vector3(self.center.x,self.min.y,0) end RectCls.GetLeftMid = function(self) return Vector3(self.min.x,self.center.y,0) end RectCls.GetRightMid = function(self) return Vector3(self.max.x,self.center.y,0) end RectCls.GetByPercent = function(self,rx,ry) return Vector3(self.min.x + (self.max.x - self.min.x) * rx,self.min.y + (self.max.y - self.min.y) * ry,0) end RectCls.Add = function (self, b) return Rect(self.x + b.x, self.y + b.y, self.width + b.width, self.height + b.height) end RectCls.Sub = function (self, b) return Rect(self.x - b.x, self.y - b.y, self.width - b.width, self.height - b.height) end RectCls.Multiply = function (self, b) return Rect(self.x * b, self.y * b, self.width * b, self.height * b) end Util --全局变量定义 UnityEngine = CS.UnityEngine GameObject = UnityEngine.GameObject Resources = UnityEngine.Resources json = raw_require("rapidjson") IsNull = _G._IsNull IsType = _G._IsType YieldK = _G._YieldK TickGC = _G._TickGC xlua = _G.xlua cast = xlua.cast typeof = xlua.typeof Yield = function(x, co, traceback) if co == nil then local ismain = nil co, ismain = coroutine.running() if ismain or not coroutine.isyieldable() then error("[Api.Yield]is not yieldable" .. debug.traceback()) end end if type(x) == 'thread' and coroutine.status(x) ~= 'dead' then repeat Yield(nil, co) until coroutine.status(x) == 'dead' else local tb = debug.traceback() YieldK(x, function() local result,errMsg = coroutine.resume(co) if not result then local arr = {"Yield error:",errMsg,"\n",debug.traceback(co),"\n",tb,"\n",traceback,} error(table.concat(arr)) end end) coroutine.yield() end end local type = type local rawget = rawget local rawset = rawset local getmetatable = getmetatable local setmetatable = setmetatable function IsInstanceOf(inst, class) local mti = inst.class local mt for i = 1,20 do mt = getmetatable(mti) if not mt then break else mti = mt.__index end if mti == class then return true end end return false end function YieldCall(fn, a, b, c) assert(coroutine.resume(coroutine.create(function() Yield(nil) if fn then fn(a, b, c) end end))) return function() fn = nil end end function FastCopy(v, append) if type(v) == "table" then local t = append or {} for k,v in pairs(v) do t[k] = v end return t else return v end end function DeepCopy(v, hash) hash = hash or {} if type(v) == "table" then local t = {} hash[v] = t for kk,vv in pairs(v) do t[kk] = hash[vv] or DeepCopy(vv, hash) end return t else return v end end -- Util.lua 的加载在 Def 之前,可以声明全局变量 function HackFunc(cls, funcname) local f = rawget(cls, funcname .. "_origin_") if not f then f = cls[funcname] rawset(cls, funcname .. "_origin_", f) end return f end function HackCSharpClass(cls) local meta = xlua.metatable_operation(typeof(cls)) local origin_index = meta.__origin_index or meta.__index local origin_newindex = meta.__origin_newindex or meta.__newindex meta.__index = function(ud, k) local v = rawget(meta, k) if v then return v end v = origin_index(ud, k) if v then return v end end meta.__origin_index = origin_index meta.__origin_newindex = origin_newindex return meta,origin_index,origin_newindex end function HackObjectSetter(object, key, metafunction) local meta = xlua.metatable_operation(typeof(object)) local _, setters = debug.getupvalue(meta.__newindex, 1) if setters then local origin = setters[key] setters[key] = metafunction(origin) else print("setter is nil: ", typeof(object)) end end function cs_ipairs(cs_array) local enumerator = cs_array:GetEnumerator() local i = 0 return function() if enumerator:MoveNext() then i = i + 1 return i,enumerator.Current end end end function cs_pairs(cs_dict) local enumerator = cs_dict:GetEnumerator() return function() if enumerator:MoveNext() then local Current = enumerator.Current return Current.Key,Current.Value end end end mainkrequire("framework/core/base/main") require("framework/core/app/main") require("framework/core/db/main")TouchComw---@class TouchCom local TouchCom = defClass("TouchCom") ---@enum TouchCom.LISTENER_TYPE TouchCom.LISTENER_TYPE = { CLICK = 0, CUSTOM = 10, } local LOGTAG = "TouchCom" local Input = CS.UnityEngine.Input function TouchCom:ctor(rootNode, camera) self:setCamera(camera) Input.multiTouchEnabled = false local GetMouseButtonDown = Input.GetMouseButtonDown local GetMouseButton = Input.GetMouseButton local GetMouseButtonUp = Input.GetMouseButtonUp self._listeners = {} self._enabled = true self.sortDirty = false local isDown = false CS.LuaGlobal.instance:AddInput(rootNode, function() if not self._enabled then return end local camera = self:getCamera() if GetMouseButtonDown(0) then if not CS.InputMgr.IsPointerOverUIObject() then --防止ui穿透 isDown = true local p = self:convertPos(camera, Input.mousePosition) self:onBegan(p) end end if not isDown then return end if GetMouseButton(0) then local p = self:convertPos(camera, Input.mousePosition) self:onMoved(p) end if GetMouseButtonUp(0) then isDown = false local p = self:convertPos(camera, Input.mousePosition) self:onEnd(p) end end) end ---@private function TouchCom:getCamera() return self.camera end function TouchCom:setCamera(camera) self.camera = camera end --#region touch events begin ---@private function TouchCom:onBegan(point) Msg.send(Msg.TouchComOnBegan, point) self:sort() local gc = {} for i, listener in ipairs(self._listeners) do if not listener:isDead() then if listener:isEnable() then listener:onBegan(point) if listener._actived and listener._bSwallow then break else end end else table.insert(gc, i) end end if #gc > 0 then self:_deleteListeners(gc) end end ---@private function TouchCom:onMoved(point) Msg.send(Msg.TouchComonMoved, point) self:sort() local gc = {} for i, listener in ipairs(self._listeners) do if not listener:isDead() then if listener:isEnable() and listener._actived then listener:onMoved(point) if listener._bSwallow then break end end else table.insert(gc, i) end end if #gc > 0 then self:_deleteListeners(gc) end end ---@private function TouchCom:onEnd(point) Msg.send(Msg.TouchComOnEnd, point) self:sort() local gc = {} for i, listener in ipairs(self._listeners) do if not listener:isDead() then if listener._enabled and listener._actived then listener:onEnd(point) if listener._bSwallow then break end end else table.insert(gc, i) end end if #gc > 0 then self:_deleteListeners(gc) end end --#endregion ---@private function TouchCom:_deleteListeners(gc) for i = #gc, 1, -1 do if self._listeners then table.remove(self._listeners, gc[i]) end end end ---@param gameObject CS.UnityEngine.GameObject ---@param touchType TouchCom.LISTENER_TYPE ---@param cb1 fun(point:CS.UnityEngine.Vector3) | fun(point:CS.UnityEngine.Vector3): boolean ---@param cb2 fun(point:CS.UnityEngine.Vector3) ---@param cb3 fun(point:CS.UnityEngine.Vector3) ---@return TouchListener|nil function TouchCom:addListener(gameObject, touchType, cb1, cb2, cb3) local listener if touchType == self.LISTENER_TYPE.CLICK then listener = TouchClickListener.new(gameObject, self) listener:setEndCb(cb1) elseif touchType == self.LISTENER_TYPE.CUSTOM then listener = TouchCustomListener.new(gameObject, self) listener:setBeganCb(cb1) listener:setMovedCb(cb2) listener:setEndCb(cb3) end table.insert(self._listeners, 1, listener) self.sortDirty = true return listener end function TouchCom:disable() self._enabled = false end function TouchCom:enable() self._enabled = true end ---@private function TouchCom:sort() if self.sortDirty then table.stableSort(self._listeners, function(l, r) return l.priority > r.priority end) self.sortDirty = false end end function TouchCom:removeListenerByGameObject(go) for i = #self._listeners, 1, -1 do if self._listeners[i].gameObject == go then table.remove(self._listeners, i) break end end end function TouchCom:removeListener(listener) for i = #self._listeners, 1, -1 do if self._listeners[i] == listener then table.remove(self._listeners, i) break end end end -- 鼠标点击坐标转世界坐标 透视相机修改触摸点z坐标 ---@private function TouchCom:convertPos(camera, pos) pos.z = camera.transform.position.z local wordPos = camera:ScreenToWorldPoint(pos) wordPos.z = 0 return wordPos end ---point是否在go内 ---@param go CS.UnityEngine.GameObject ---@param point CS.UnityEngine.Vector3 ---@return boolean function TouchCom.intersect(go, point) local renderer = go:GetComponent(typeof(CS.UnityEngine.Renderer)) if renderer then local bounds = renderer.bounds local p = UnityEngine.Vector3(point.x, point.y, point.z) p.z = (bounds.min.z + bounds.max.z) * 0.5 return bounds:Contains(p) else return false end end ResLoaderD--[[ 功能: 1. 加载资源 2. 缓存资源 3. 释放资源 4. 清理缓存 5. 卸载资源 6. 统计和调试 author: zhangheng time:2025-07-19 15:50:00 ]] local ResLoader = defClassStatic("ResLoader") local LOGTAG = "ResLoader" ResLoader._assetfileCache = {} -- 资源文件缓存 {assetName -> asset} ResLoader._reslinkCache = {} -- ResLink配置缓存 {path -> ResLink} -- ============================================================================ -- ResLink配置层 -- ============================================================================ -- 资源类型映射 local _load_func_map_by_type = { [0] = {"Prefab", function(c) return ResLoader.loadAsset(c[1]) end}, [1] = {"Scene", function(c) return c[1] end}, [2] = {"ResLink", function(c) return ResLoader.loadResLink(c[1]) end}, [3] = {"RawTxt", function(c) local f,err = load("return " .. c[1]) return f and f() or c[1] end}, [4] = {"LuaFile", function(c) return c[1] end}, [5] = {"ResPath", function(c) return c[1] end}, [6] = {"Sprite", function(c) return ResLoader.loadAsset(c[1], "Sprite") end}, [7] = {"AudioClip", function(c) return c[1] -- 音频通常是路径引用 end}, [8] = {"VideoClip", function(c) return ResLoader.loadAsset(c[1]) end}, [9] = {"TextAsset", function(c) local asset = ResLoader.loadAsset(c[1], "TextAsset") return asset and asset.bytes or nil end}, [10] = {"AnimationClip", function(c) return ResLoader.loadAsset(c[1], "AnimationClip") end}, [11] = {"RuntimeAnimatorController", function(c) return ResLoader.loadAsset(c[1], "RuntimeAnimatorController") end}, [12] = {"JsonFile", function(c) local asset = ResLoader.loadAsset(c[1], "TextAsset") if asset and asset.text then return json.decode(asset.text) end return nil end}, [13] = {"SkeletonDataAsset", function(c) return ResLoader.loadAsset(c[1], "SkeletonDataAsset") end}, } -- ResLink元表 local _reslink_meta = { __newindex = function(t, k, v) printError(LOGTAG, "[ResLink]只读,不能修改:%s", k) end, __index = function(t, k) local c = t.__asset[k] if c then return c[4] and c[4](c) else printWarn(LOGTAG,"[ResLink]缺失资源:%s", k) end end } -- ResLink实例数据 local _reslink_data = { __cls_inst = false, ---获取所有资源的键名 ---@param self ResLink ---@return string[] getAssetNames = function (self) local t = {} for k,_ in pairs(self.__asset) do table.insert(t, k) end return t end, ---获取所有资源配置 ---@param self ResLink ---@return AssetCfg[] getAssetList = function(self) local t = {} for k,v in pairs(self.__asset) do table.insert(t, v) end return t end, ---获取指定资源配置 ---@param self ResLink ---@param k string ---@return AssetCfg getAssetInfo = function(self, k) return self.__asset[k] end, ---获取指定资源路径 ---@param self ResLink ---@param k string ---@return string|nil getAssetPath = function(self, k) local info = self.__asset[k] return info and info[1] end, } -- 初始化 function ResLoader.init() -- 清理缓存 collectgarbage() CS.UnityEngine.Resources.UnloadUnusedAssets() printInfo(LOGTAG, "ResLoader系统初始化完成") return true end -- 检查资源是否存在 function ResLoader.hasAsset(path) if not path then return false end -- 直接使用YooAssetAdapter检查(已经内部处理了本地资源判断) return YooAssetAdapter.hasAsset(path) end -- 同步加载资源 ---@param path string 资源路径 ---@param assetType string|nil 资源类型(可选) ---@return any|nil 加载的资源对象 function ResLoader.loadAsset(path, assetType) if not path then printError(LOGTAG, "资源路径为空") return nil end local assetName = assetType and string.format("%s:%s", path, assetType) or path printInfo(LOGTAG, "[loadAsset]同步加载资源:%s", assetName) -- 检查缓存 local asset = ResLoader._assetfileCache[assetName] if asset then printDebug(LOGTAG, "使用缓存资源:%s", assetName) return asset end -- 使用YooAssetAdapter加载(已经内部处理了本地资源判断) asset = YooAssetAdapter.loadAssetSync(path, assetType) if asset then -- 缓存资源 ResLoader._assetfileCache[assetName] = asset printDebug(LOGTAG, "资源加载成功并缓存:%s", assetName) else printError(LOGTAG, "资源加载失败:%s", assetName) end return asset end -- 异步加载资源 ---@param path string 资源路径 ---@param assetType string|nil 资源类型(可选) ---@param progressCallback function|nil 进度回调 ---@param finishCallback function|nil 完成回调 function ResLoader.loadAssetAsync(path, assetType, progressCallback, finishCallback) if not path then printError(LOGTAG, "资源路径为空") if finishCallback then finishCallback(nil) end return end local assetName = assetType and string.format("%s:%s", path, assetType) or path printInfo(LOGTAG, "[loadAssetAsync]异步加载资源:%s", assetName) -- 检查缓存 local asset = ResLoader._assetfileCache[assetName] if asset then printDebug(LOGTAG, "使用缓存资源:%s", assetName) if progressCallback then progressCallback(1) end if finishCallback then finishCallback(asset) end return end -- 使用YooAssetAdapter异步加载(已经内部处理了本地资源判断) YooAssetAdapter.loadAssetAsync(path, assetType, progressCallback, function(asset) if asset then -- 缓存资源 ResLoader._assetfileCache[assetName] = asset printDebug(LOGTAG, "异步资源加载成功并缓存:%s", assetName) else printError(LOGTAG, "异步资源加载失败:%s", assetName) end if finishCallback then finishCallback(asset) end end) end -- 加载ResLink配置 ---@param path string ResLink配置路径 ---@param cache table|nil 缓存表(用于避免循环引用) ---@return ResLink|nil ResLink实例 function ResLoader.loadResLink(path, cache) printInfo(LOGTAG, "[loadResLink]加载配置:%s", path) -- 检查缓存 if ResLoader._reslinkCache[path] then printDebug(LOGTAG, "使用缓存ResLink:%s", path) return ResLoader._reslinkCache[path] end -- 加载配置文件 local success, assetData if CS.LuaHelper.UseLocalSrc() then -- 编辑器本地模式:直接从文件系统加载 printDebug(LOGTAG, "编辑器本地模式加载ResLink配置:%s", path) local dataPath = CS.UnityEngine.Application.dataPath local filepath = dataPath.."/LuaScripts/"..path..".lua" printDebug(LOGTAG, "加载ResLink文件路径:%s", filepath) local src = CS.LuaHelper.ReadFileText(filepath) if src then local func, err = load(src, path, "bt") if func then success, assetData = pcall(func) else printError(LOGTAG, "ResLink配置编译失败:%s, 错误:%s", path, tostring(err)) return nil end else printError(LOGTAG, "ResLink配置文件读取失败:%s", filepath) return nil end else -- 标准模式:使用require success, assetData = pcall(require, path) end if not success or not assetData then printError(LOGTAG, "ResLink配置加载失败:%s, 错误:%s", path, tostring(assetData)) return nil end -- 处理资源类型映射 for k, v in pairs(assetData) do if type(v) == "table" and #v >= 3 then local typeInfo = _load_func_map_by_type[v[3]] or {} v[3], v[4] = typeInfo[1], typeInfo[2] end end -- 处理继承机制 local baseLink = assetData.BASE if baseLink then assetData.BASE = nil cache = cache or {} cache[path] = true if cache[baseLink[1]] ~= true then local parentLink = ResLoader.loadResLink(baseLink[1], cache) if parentLink then -- 合并父配置 for k, v in pairs(parentLink.__asset) do if assetData[k] == nil then assetData[k] = v end end end end end -- 创建ResLink实例 local resLink = {__asset = assetData} for k, v in pairs(_reslink_data) do resLink[k] = v end local instance = setmetatable(resLink, _reslink_meta) -- 缓存ResLink ResLoader._reslinkCache[path] = instance printDebug(LOGTAG, "ResLink加载成功并缓存:%s", path) return instance end -- 预加载资源列表 ---@param assetList string[] 资源路径列表 ---@param callback function|nil 完成回调 function ResLoader.preloadAssets(assetList, callback) if not assetList or #assetList == 0 then if callback then callback(true) end return end printInfo(LOGTAG, "[preloadAssets]预加载资源列表,数量:%d", #assetList) -- 委托给YooAssetAdapter处理 YooAssetAdapter.preloadAssets(assetList, callback) end -- ============================================================================ -- 资源管理 -- ============================================================================ -- 释放指定资源 function ResLoader.releaseAsset(path, assetType) local assetName = assetType and string.format("%s:%s", path, assetType) or path -- 从缓存中移除 if ResLoader._assetfileCache[assetName] then ResLoader._assetfileCache[assetName] = nil printDebug(LOGTAG, "从缓存中移除资源:%s", assetName) end -- 释放YooAssetAdapter中的资源 YooAssetAdapter.releaseAsset(path) end -- 清理所有缓存 function ResLoader.clearCache() printInfo(LOGTAG, "清理所有资源缓存") -- 清理资源缓存 for k, v in pairs(ResLoader._assetfileCache) do if type(v) == "userdata" then xlua.release(v) end end ResLoader._assetfileCache = {} -- 清理ResLink缓存 ResLoader._reslinkCache = {} -- 释放YooAssetAdapter资源 YooAssetAdapter.clearAll() -- Unity资源清理 CS.UnityEngine.Resources.UnloadUnusedAssets() collectgarbage() printInfo(LOGTAG, "资源缓存清理完成") end -- 卸载资源(兼容性接口) function ResLoader.unloadAssets() ResLoader.clearCache() end -- 退出清理 function ResLoader.exit() ResLoader.clearCache() printInfo(LOGTAG, "ResLoader系统已退出") end -- 场景加载后的垃圾回收 function ResLoader.gcAfterLoadScene() printInfo(LOGTAG, "[gcAfterLoadScene]场景加载后执行垃圾回收") -- 执行Lua垃圾回收 collectgarbage("collect") -- 卸载未使用的Unity资源 CS.UnityEngine.Resources.UnloadUnusedAssets() printDebug(LOGTAG, "场景加载后垃圾回收完成") end -- ============================================================================ -- 便捷API -- ============================================================================ -- 加载预制体 function ResLoader.loadPrefab(path) return ResLoader.loadAsset(path, "GameObject") end -- 异步加载预制体 function ResLoader.loadPrefabAsync(path, callback) ResLoader.loadAssetAsync(path, "GameObject", nil, callback) end -- 加载贴图 function ResLoader.loadSprite(path) return ResLoader.loadAsset(path, "Sprite") end -- 异步加载贴图 function ResLoader.loadSpriteAsync(path, callback) ResLoader.loadAssetAsync(path, "Sprite", nil, callback) end -- 加载音频 ---@param path string 音频路径 ---@return AudioClip|nil 音频对象 function ResLoader.loadAudioClip(path) return ResLoader.loadAsset(path, "AudioClip") end function ResLoader.loadAudioClipAsync(path, callback) ResLoader.loadAssetAsync(path, "AudioClip", nil, callback) end -- 加载文本资源 ---@param path string 文本资源路径 ---@return TextAsset|nil 文本资源对象 function ResLoader.loadTextAsset(path) return ResLoader.loadAsset(path, "TextAsset") end function ResLoader.loadTextAssetAsync(path, callback) ResLoader.loadAssetAsync(path, "TextAsset", nil, callback) end -- 场景加载 ---@param path string 场景路径 ---@param callback function|nil 完成回调 function ResLoader.loadSceneSync(path, callback) printInfo(LOGTAG, "[loadSceneSync]同步加载场景:%s", path) -- 注意:Unity没有真正的同步场景加载,这里使用异步但立即等待 ResLoader.loadSceneAsync(path, nil, callback) end ---@param path string 场景路径 ---@param progressCallback function|nil 进度回调 ---@param finishCallback function|nil 完成回调 function ResLoader.loadSceneAsync(path, progressCallback, finishCallback) printInfo(LOGTAG, "[loadSceneAsync]异步加载场景:%s", path) if not path then printError(LOGTAG, "场景路径为空") if finishCallback then finishCallback(nil) end return end -- 从路径中提取场景名称,处理各种路径格式 local sceneName = path -- 去除.unity扩展名 sceneName = string.gsub(sceneName, "%.unity$", "") -- 提取最后一个路径分量作为场景名 sceneName = string.match(sceneName, "([^/\\]+)$") or sceneName printDebug(LOGTAG, "提取场景名称:%s", sceneName) -- 直接使用YooAssetLoader加载场景 local loader = CS.YooAssetLoader.Instance if loader then loader:LoadSceneAsyncLua(path, function(success) if finishCallback then if success then -- 通过遍历所有场景来找到匹配的场景对象 local sceneManager = CS.UnityEngine.SceneManagement.SceneManager local sceneCount = sceneManager.sceneCount local targetScene = nil for i = 0, sceneCount - 1 do local loadedScene = sceneManager:GetSceneAt(i) if loadedScene and loadedScene.isLoaded and loadedScene.name == sceneName then targetScene = loadedScene break end end if targetScene then printDebug(LOGTAG, "场景加载成功,场景名称:%s", sceneName) finishCallback(targetScene) else printError(LOGTAG, "场景加载失败,无法找到匹配的场景:%s", sceneName) finishCallback(nil) end else printError(LOGTAG, "场景加载失败:%s", path) finishCallback(nil) end end end) else printError(LOGTAG, "YooAssetLoader未初始化") if finishCallback then finishCallback(nil) end end end -- ============================================================================ -- 统计和调试 -- ============================================================================ -- 获取缓存统计 function ResLoader.getCacheStats() local assetCount = 0 local reslinkCount = 0 for _ in pairs(ResLoader._assetfileCache) do assetCount = assetCount + 1 end for _ in pairs(ResLoader._reslinkCache) do reslinkCount = reslinkCount + 1 end local yooStats = YooAssetAdapter.getStats() return { assetCount = assetCount, reslinkCount = reslinkCount, yooAssetCount = yooStats.assetCount, yooSceneCount = yooStats.sceneCount, yooReady = yooStats.isReady } end -- 打印缓存统计 function ResLoader.printCacheStats() local stats = ResLoader.getCacheStats() printInfo(LOGTAG, "=== ResLoader缓存统计 ===") printInfo(LOGTAG, "资源缓存数量: %d", stats.assetCount) printInfo(LOGTAG, "ResLink缓存数量: %d", stats.reslinkCount) printInfo(LOGTAG, "YooAsset资源数量: %d", stats.yooAssetCount) printInfo(LOGTAG, "YooAsset场景数量: %d", stats.yooSceneCount) printInfo(LOGTAG, "YooAsset就绪状态: %s", tostring(stats.yooReady)) printInfo(LOGTAG, "====================") end -- 打印系统状态 function ResLoader.printStatus() printInfo(LOGTAG, "=== ResLoader系统状态 ===") ResLoader.printCacheStats() print("") YooAssetAdapter.printStatus() end -- 获取YooAssetAdapter实例(用于高级操作) function ResLoader.getYooAssetAdapter() return YooAssetAdapter end return ResLoader SceneCfgList--[[ 场景配置 author:{zhangpeng} time:2022-05-14 18:05:42 ]] local SceneCfgList = defClassStatic("SceneCfgList") -- 场景id SceneCfgList.SceneIndexs = { Start = 1, Shop = 2, Login = 3, WorldMap = 4, --测试 Test = 99 } -- 场景配置 SceneCfgList.SCENES = { LoginScene = {"modules/common/login/LoginScene", "登录场景"}, LobbyScene = {"modules/lobby/LobbyScene", "主场景"}, } -- 当前加载的场景信息 SceneCfgList.currLoadedSceneInfo = nil async--[[ luaide 模板位置位于 Template/FunTemplate/NewFileTemplate.lua 其中 Template 为配置路径 与luaide.luaTemplatesDir luaide.luaTemplatesDir 配置 https://www.showdoc.cc/web/#/luaide?page_id=713062580213505 author:{author} time:2022-05-17 17:31:23 ]] local async = {} function async.wait_all(names,finishCb) local cloned = {} for k,v in ipairs(names)do cloned[k] = v end local proxy = { notify = function(name) for i = #cloned,1,-1 do if cloned[i] == name then table.remove(cloned,i) end end if #cloned == 0 then finishCb() end end, } return proxy end function async.foreach(list,iterator,finishCb) local i = 0 local resolve local isAborted = false resolve = function() i = i + 1 local item = list[i] if item and not isAborted then iterator(item,resolve,i) else if finishCb then finishCb() end end end resolve() -- 返回一个函数,用于在外部函数内终止迭代 return function() isAborted = true end end function async.fori(total,iterator,finishCb) local i = 0 local resolve resolve = function() i = i + 1 if i <= total then iterator(i,resolve) else if finishCb then finishCb() end end end resolve() end function async.forij(from,total,iterator,finishCb) local i = from - 1 local resolve resolve = function() i = i + 1 if i <= total then iterator(i,resolve) else if finishCb then finishCb() end end end resolve() end function async.seq(steps) local i = 0 local resolve resolve = function() i = i + 1 local step = steps[i] local resolved = false if step then step(function() if not resolved then resolved = true resolve() end end) end end resolve() end return asyncluafsm--[[================================================= Lua State Machine Library ----=================================================]] VERSION = "2.3.2" SUCCEEDED = 1 -- the event transitioned successfully from one state to another NOTRANSITION = 2 -- the event was successfull but no state transition was necessary CANCELLED = 3 -- the event was cancelled by the caller in a beforeEvent callback PENDING = 4 -- the event is asynchronous and the caller is in control of when the transition occurs INVALID_TRANSITION_ERROR = 'INVALID_TRANSITION_ERROR' -- caller tried to fire an event that was innapropriate in the current state PENDING_TRANSITION_ERROR = 'PENDING_TRANSITION_ERROR' -- caller tried to fire an event while an async transition was still pending INVALID_CALLBACK_ERROR = 'INVALID_CALLBACK_ERROR' -- caller provided callback function threw an exception WILDCARD = '*' ASYNC = 'async' local function do_callback(fsm, func, event, params) if type(func) == 'function' then local success, ret = pcall(func, unpack(params)) if not success then local err = ret fsm:error(event, INVALID_CALLBACK_ERROR, err) end return ret end end local function before_any_event(fsm, event, params) return do_callback(fsm, fsm.onbeforeevent, event, params) end local function after_any_event(fsm, event, params) return do_callback(fsm, fsm.onafterevent or fsm.onevent, event, params) end local function leave_any_state(fsm, event, params) return do_callback(fsm, fsm.onleavestate, event, params) end local function enter_any_state(fsm, event, params) return do_callback(fsm, fsm.onenterstate or fsm.onstate, event, params) end local function change_state(fsm, event, params) return do_callback(fsm, fsm.onchangestate, event, params) end local function before_this_event(fsm, event, params) return do_callback(fsm, fsm['onbefore' .. event.name], event, params) end local function after_this_event(fsm, event, params) return do_callback(fsm, fsm['onafter' .. event.name] or fsm['on' .. event.name], event, params) end local function leave_this_state(fsm, event, params) return do_callback(fsm, fsm['onleave' .. event.from], event, params) end local function enter_this_state(fsm, event, params) return do_callback(fsm, fsm['onenter' .. event.to] or fsm['on' .. event.to], event, params) end local function before_event(fsm, event, params) if before_this_event(fsm, event, params) == false or before_any_event(fsm, event, params) == false then return false end end local function after_event(fsm, event, params) after_this_event(fsm, event, params) after_any_event(fsm, event, params) end local function leave_state(fsm, event, params) local specific = leave_this_state(fsm, event, params) local general = leave_any_state(fsm, event, params) if specific == false or general == false then return false elseif specific == ASYNC or general == ASYNC then return ASYNC end end local function enter_state(fsm, event, params) enter_this_state(fsm, event, params) enter_any_state(fsm, event, params) end local function build_event(name, entry) return function(self, ...) local from = self.current local to = entry[from] or entry[WILDCARD] or from local event = { name = name, from = from, to = to, } local params = {self, event, ...} if self.transition then return self:error(event, PENDING_TRANSITION_ERROR, ('event %s inappropriate because previous transition did not complete'):format(name)) end if self:cannot(name) then return self:error(event, INVALID_TRANSITION_ERROR, ('event %s inappropriate in current state %s'):format(name, self.current)) end if before_event(self, event, params) == false then return CANCELLED end if from == to then after_event(self, event, params) return NOTRANSITION end -- prepare a transition method for use EITHER lower down, -- or by caller if they want an async transition (indicated by an ASYNC return value from leaveState) local fsm = self self.transition = { -- provide a way for caller to cancel async transition if desired cancel = function() fsm.transition = nil after_event(fsm, event, params) end } setmetatable(self.transition, { __call = function() fsm.transition = nil -- this method should only ever be called once fsm.current = to enter_state(fsm, event, params) change_state(fsm, event, params) after_event(fsm, event, params) return SUCCEEDED end }) local leave = leave_state(fsm, event, params) if leave == false then self.transition = nil return CANCELLED elseif leave == ASYNC then return PENDING else if self.transition then -- need to check in case user manually called transition() but forgot to return ASYNC return self.transition() end end end end function create(cfg, target) assert(type(cfg) == 'table', 'cfg must be a table') -- allow for a simple string, or an object with { state: = 'foo', event = 'setup', defer = true|false } local initial = type(cfg.initial) == 'string' and { state = cfg.initial } or cfg.initial local terminal = cfg.terminal or cfg.final local fsm = target or cfg.target or {} local events = cfg.events or {} local callbacks = cfg.callbacks or {} local map = {} local function add(e) -- allow 'wildcard' transition if 'from' is not specified local from = type(e.from) == 'table' and e.from or (e.from and {e.from} or {WILDCARD}) local entry = map[e.name] or {} map[e.name] = entry for _, v in ipairs(from) do entry[v] = e.to or v -- allow no-op transition if 'to' is not specified end end if initial then initial.event = initial.event or 'startup' add { name = initial.event, from = 'none', to = initial.state } end for _, e in ipairs(events) do add(e) end for k, v in pairs(map) do fsm[k] = build_event(k, v) end for k, v in pairs(callbacks) do fsm[k] = v end fsm.current = 'none' fsm.is = function(self, state) if type(state) == 'table' then for _, s in ipairs(state) do if s == self.current then return true end end return false else return self.current == state end end fsm.can = function(self, event) if (not self.transition) and map[event] and (map[event][self.current] or map[event][WILDCARD]) then return true else return false end end fsm.cannot = function(self, event) return not self:can(event) end -- default behavior when something unexpected happens is to throw an exception, but caller can override this behavior if desired fsm.error = cfg.error or function(self, event, error_code, err) error(error_code .. " " .. err) end fsm.is_finished = function(self) return self:is(terminal) end if initial and not initial.defer then fsm[initial.event](fsm) end return fsm end return _M SqliteModel  ---@class SqliteModel:BaseModel local SqliteModel = defClass("SqliteModel", BaseModel) function SqliteModel:ctor(table) self.table = table self.bindInfoList = self.table.bindInfoList or {} local list = self.table:getColumnList() for i, col in ipairs(list) do if not col.isMetadata then self[col.name] = col.defaultValue else self[col.name] = nil end end self:init() end function SqliteModel:setSqliteData(data) local list = self.table:getColumnList() for i, col in ipairs(list) do if not col.isMetadata then if col.type == "string" then self[col.name] = data[col.name] or self[col.name] else self[col.name] = math.tointeger(data[col.name]) or data[col.name] or self[col.name] end else self[col.name] = nil end end end function SqliteModel:setSqliteDataWithKeysAndValues(keyList, valueList) local dict = self.table:getColumnDict() for i, key in pairs(keyList) do if dict[key] and not dict[key].isMetadata then if dict[key].type == "string" then self[key] = valueList[i] or self[key] else self[key] = math.tointeger(valueList[i]) or valueList[i] or self[key] end else self[key] = nil end end end function SqliteModel:setKey(key) if type(key) ~= "table" then key = {key} end local list = self.table.primaryList for i, col in ipairs(list) do self[col.name] = key[i] end end function SqliteModel:getKey() local key = {} local list = self.table.primaryList for i, col in ipairs(list) do table.insert(key, self[col.name]) end if #key == 1 then key = key[1] end return key end function SqliteModel:getKeyStr() local key = self:getKey() return self.table:keyToStr(key) end function SqliteModel:save() self.table:set(self) end -- sync必定save function SqliteModel:sync() if not self.table.useSync then self:save() return end self.table:sync(self) end function SqliteModel:refresh() local key = self:getKey() local data = self.table:get(key) if not data then return end self:setSqliteData(data) end --#region 设置metadata,既业务无关的数据,不可读,设置后在写入数据库后会置为空,防止业务模块中的缓存数据污染metadata function SqliteModel:setLastUpdateTime(time) self.lastUpdateTime = time end function SqliteModel:getLastUpdateTime() local key = self:getKey() local data = self.table:genQuery():where(self.table:getKeyCondition(key)):getFirst() return data.lastUpdateTime end function SqliteModel:setLastUpdateVersion(version) self.lastUpdateVersion = version end function SqliteModel:getLastUpdateVersion() local key = self:getKey() local data = self.table:genQuery():where(self.table:getKeyCondition(key)):getFirst() return data.lastUpdateVersion end function SqliteModel:setSyncState(syncState) self.syncState = syncState end function SqliteModel:getSyncState() local key = self:getKey() local data = self.table:genQuery():where(self.table:getKeyCondition(key)):getFirst() return data.syncState end function SqliteModel:clearMetadata() local list = self.table:getColumnList() for i, col in ipairs(list) do if col.isMetadata then self[col.name] = nil end end end --#endregion return SqliteModel SceneCfg--[[ 管理场景配置 ]] local SceneCfg = defClassStatic("SceneCfg") function SceneCfg.init(SCENES) SceneCfg.SCENES = SCENES end function SceneCfg.addScene(k,v) SceneCfg.SCENES[k] = v end function SceneCfg.addSubScene(k, kk, v) if SceneCfg.SCENES[k] == nil then SceneCfg.SCENES[k] = {} end SceneCfg.SCENES[k][kk] = v end function SceneCfg.isSubSceneExist(k, kk) if SceneCfg.SCENES[k] and SceneCfg.SCENES[k][kk] then return true else return false end end function SceneCfg.initModuleId(cfg, args) local path = cfg[1] local name = cfg[2] or "" local moduleId = cfg[3] if moduleId == nil then return nil end local t = type(moduleId) if t == "function" then local func = moduleId moduleId = func(args) elseif t == "string" then if moduleId == "_dir" then local tmp = string.split(path,"/") moduleId = tmp[#tmp-1] end end return moduleId endmain%require("modules/setting/setting")TimeUtil%$--[[ author:{author} time:2022-05-17 17:34:08 ]] local TimeUtil = {} local TimeSpan = CS.System.TimeSpan local DateTime = CS.System.DateTime local Convert = CS.System.Convert local ts_start = DateTime(1970, 1, 1, 0, 0, 0, 0) local LOG_TAG = "TimeUtil" function TimeUtil.getTimeStamp() local ts = DateTime.UtcNow - ts_start return math.floor(ts.TotalMilliseconds) end function TimeUtil.getTimeStampInSeconds() local ts = DateTime.UtcNow - ts_start return math.floor(ts.TotalSeconds) end function TimeUtil.getHourMinSec(ostime) local formattedTime = os.date("%H:%M:%S", ostime) return formattedTime end --获取某时刻上一个自然周的date列表 (周一到周日) 参数传空为当先时间 function TimeUtil.getLastWeekDateList(timestamp) local date = os.date("*t", timestamp) printInfo("[getLastWeekDateList] %s %s %s %s",date.year, date.month, date.day, date.wday) --将周日为1 转化为周一为1 local week = (date.wday - 2) % 7 + 1 local dateTable = {} dateTable.year = date.year dateTable.month = date.month dateTable.day = date.day dateTable.hour = 0 dateTable.min = 0 dateTable.sec = 0 local timeSecond = os.time(dateTable) local list = {} for i = 1, 7 do local second = timeSecond - (week + 6 - i + 1) * 60 * 60 * 24 local table = os.date("*t", second) list[#list + 1] = table end return list end function TimeUtil.getCurWeekDateList(timestamp) local list = TimeUtil.getLastWeekDateList(timestamp) local dt = 7 * 24 * 60 * 60 for i, v in ipairs(list) do local second = os.time(v) second = second + dt list[i] = os.date("*t", second) end return list end -- 将一个时间数转换成"00:00:00"格式(天:小时:分:秒) function TimeUtil.getTimeString1(timeInt) if tonumber(timeInt) <= 0 then return "00:00:00:00" else local days = math.floor(timeInt / (24 * 60 * 60)) local hours = math.floor((timeInt % (24 * 60 * 60)) / (60 * 60)) local minutes = math.floor((timeInt % (60 * 60)) / 60) local seconds = timeInt % 60 return string.format("%02d:%02d:%02d:%02d", days, hours, minutes, seconds) end end -- 将一个时间数转换成"00:00"格式 function TimeUtil.getTimeString(timeInt) if (tonumber(timeInt) <= 0) then return "00:00" else return string.format("%02d:%02d", math.floor((timeInt/60)%60), timeInt%60) end end -- 将一个时间数转换成"00"分格式 function TimeUtil.getTimeMinuteString(timeInt) if (tonumber(timeInt) <= 0) then return "00" else return string.format("%02d", math.floor((timeInt/60)%60)) end end -- 将一个时间数转换成"00“秒格式 function TimeUtil.getTimeSecondString(timeInt) if (tonumber(timeInt) <= 0) then return "00" else return string.format("%02d", timeInt%60) end end -- 将一个时间戳转换 function TimeUtil.getTimeStampString(time,splitStr,haveSec) if not time then return "" end time = tonumber(time) if time<0 then return "" end if not splitStr then splitStr="_" end local date = os.date("*t",time) local year = date.year local month = date.month if tonumber(month)<10 then month = "0"..month end local day = date.day if tonumber(day)<10 then day = "0"..day end local hour = date.hour if tonumber(hour)<10 then hour = "0"..hour end local min = date.min if tonumber(min)<10 then min = "0"..min end if haveSec==true then local sec = date.sec if tonumber(sec)<10 then sec = "0"..sec end return date.year..splitStr..month..splitStr..day.." "..hour..":"..min.." "..sec end return date.year..splitStr..month..splitStr..day.." "..hour..":"..min end function TimeUtil.getTimeSimpleString(time,splitStr,ishaveYear, isnohaveTime) if not time then return "" end time = tonumber(time) if time<0 then return "" end if not splitStr then splitStr="_" end local date = os.date("*t",time) local year = date.year local month = date.month if tonumber(month)<10 then month = "0"..month end local day = date.day if tonumber(day)<10 then day = "0"..day end local hour = date.hour if tonumber(hour)<10 then hour = "0"..hour end local min = date.min if tonumber(min)<10 then min = "0"..min end -- if ishaveYear then if isnohaveTime then return year..splitStr..month..splitStr..day else return year..splitStr..month..splitStr..day.." "..hour..":"..min end else return month..splitStr..day.." "..hour..":"..min end end ----------------------------------服务器时间-------------------------------------- function TimeUtil.transServerTime(_timeInt) -- 将毫秒转换为秒 local totalSeconds = math.floor(_timeInt / 1000) if tonumber(totalSeconds) <= 0 then return "00:00:00:00:00:00" else local data = {} data.year = TimeUtil.getYear(totalSeconds) data.month = TimeUtil.getMonth(totalSeconds) data.day = TimeUtil.getDay(totalSeconds) data.hours = TimeUtil.getHour(totalSeconds) data.minutes = TimeUtil.getMinutes(totalSeconds) data.sec = TimeUtil.getSeconds(totalSeconds) return string.format("%02d:%02d:%02d:%02d:%02d:%02d", data.year, data.month, data.day, data.hours, data.minutes, data.sec) end end -- 服务器毫秒时间转换为秒 function TimeUtil.transServerToSec(_time) return math.floor(_time / 1000) end -- 获取服务器时间 -- useage: --[[ util.TimeUtil.getServerTime(function (time) local server_time = time printInfo(LOG_TAG, "server time:%s", server_time) end) ]] function TimeUtil.getServerTime(cb) UserCmdMgr:requestCurTime(function (ret, time) local time_1 = util.TimeUtil.transServerTime(time) printInfo(LOG_TAG, "服务器时间: %s 当前时间:%s", time, time_1) if cb then cb(util.TimeUtil.transServerToSec(time) or os.time()) end end) end -- Author: KevinYu -- Date: 2015-11-12 11:44:29 -- 扩展功能 --[[ os.date("*t", time) 返回的table time = { "day" = 12 日 "hour" = 15 时 "isdst" = false 是否夏令时 "min" = 7 分 "month" = 11 月 "sec" = 12 秒 "wday" = 5 星期几(星期天为1) "yday" = 316 一年中的第几天 "year" = 2015 年 }]] --获取本月相关数据 function TimeUtil.getMonthData(time) local data = {} data.firstDayWeek = TimeUtil.getWeekOfMonthFirstDay(time) data.year = TimeUtil.getYear(time) data.month = TimeUtil.getMonth(time) data.day = TimeUtil.getDay(time) data.monthDays = TimeUtil.getMonthDays_(data.year, data.month) return data end --获取本月一号是星期几 function TimeUtil.getWeekOfMonthFirstDay(time) time = tonumber(time) local tab = os.date("*t", time) local year, month, day, wday = tab.year, tab.month, tab.day, tab.wday local f_wday = 1 day = day % 7 --转换到1-7号对应的第几天 星期天为第1天 if day == 0 then f_wday = wday + 1 else if day > wday then f_wday = wday - day + 8 else f_wday = wday - day + 1 end end return f_wday - 1 --返回0 - 6 对应星期天-星期六 end --判断是否为闰年 function TimeUtil.isLeapYear(year) if (year % 4 == 0 and year % 100 ~= 0) or (year % 400 == 0) then return true end return false end --每个月对应的天数 local months = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31} function TimeUtil.getMonthDays_(year, month) if month == 2 then if TimeUtil.isLeapYear(year) then return 29 else return 28 end else return months[month] end end function TimeUtil.getMonthDays(time) local tab = os.date("*t", time) return TimeUtil.getMonthDays_(tab.year, tab.month) end --年 function TimeUtil.getYear(time) return tonumber(os.date("%Y", time)) end --月 function TimeUtil.getMonth(time) return tonumber(os.date("%m", time)) end --日 function TimeUtil.getDay(time) return tonumber(os.date("%d", time)) end --时 function TimeUtil.getHour(time) return tonumber(os.date("%H", time)) end --分 function TimeUtil.getMinutes(time) return tonumber(os.date("%M", time)) end --秒 function TimeUtil.getSeconds(time) return tonumber(os.date("%S", time)) end --星期中的第几天,星期天为 0 与wday属性不一样 function TimeUtil.getWeekDay(time) return tonumber(os.date("%w", time)) end --足球比赛时间格式 function TimeUtil.getFootballMathTime(time, separator) separator = separator or " " local date = os.date("*t", time) local dayStr = string.format("%02d/%02d", date.month, date.day) local timeStr = string.format("%02d:%02d", date.hour, date.min) return dayStr .. separator .. timeStr end return TimeUtilFacebookLoginUtil --[[ fb登录管理 author:{zhangpeng} time:2023-12-26 10:47:05 ]] local FacebookLoginUtil, super = defClassStatic("FacebookLoginUtil") local LOG_TAG = "FacebookLoginUtil" local IOC_FB_UTIL_CLASS_NAME = "FBLoginUtil" local JavaFacebookClass = "com/fy/xgame/tilelink/util/FacebookUtil" function FacebookLoginUtil:init() FacebookLoginUtil:registLuaCallBack() end function FacebookLoginUtil:login() if Device.isIOS() then local param = {} luaoc.callStaticMethod(IOC_FB_UTIL_CLASS_NAME, "loginWithFacebook") elseif Device.isAndroid() then local function cb(code, msg) if code == 0 then printInfo(LOG_TAG, msg) -- 根据不同发起原因,发送不同消息 if User:getLoginOrigin() == LoginConst.Origin.login then printInfo(LOG_TAG,"fb登录成功回调") Msg.send(Msg.USER_LOGIN_FB_SUC, json.decode(msg)) elseif User:getLoginOrigin() == LoginConst.Origin.bind then printInfo(LOG_TAG,"fb绑定登录成功回调") Msg.send(Msg.USER_LOGIN_FB_BIND_SUC, json.decode(msg)) end elseif code == 1 then Msg.send(Msg.USER_LOGIN_FB_FAILED) elseif code == 2 then Msg.send(Msg.USER_LOGIN_FB_CANCLE) end end luaj.callStaticMethod(JavaFacebookClass, "loginWithFacebook", {cb}) end end function FacebookLoginUtil:registLuaCallBack() if Device.isIOS() then local param = { loginErrorCb = function() printInfo(LOG_TAG,"FB 登录失败回调") Msg.send(Msg.USER_LOGIN_FB_FAILED) end, loginCancleCb = function () printInfo(LOG_TAG,"FB 取消登录回调") Msg.send(Msg.USER_LOGIN_FB_CANCLE) end, loginSucCb = function (name, email, fid) local data = {id = fid, name = name, email = email} table.print_r(data,"Facebook ID 登录成功回调数据") Msg.send(Msg.USER_LOGIN_FB_SUC,data) end } luaoc.callStaticMethod(IOC_FB_UTIL_CLASS_NAME, "registLuaCallback", param) end end function FacebookLoginUtil:shareLink(url) if Device.isIOS() then local param = {} luaoc.callStaticMethod(IOC_FB_UTIL_CLASS_NAME, "loginWithFacebook") elseif Device.isAndroid() then local function cb(code, msg) if code == 0 then printInfo(LOG_TAG,"分享成功回调") elseif code == 1 then -- Msg.send(Msg.USER_LOGIN_FB_FAILED) elseif code == 2 then -- Msg.send(Msg.USER_LOGIN_FB_CANCLE) end end luaj.callStaticMethod(JavaFacebookClass, "shareLink", {url, cb}) end end FacebookLoginUtil:init()luaoc local luaoc = {} local callStaticMethod = LuaObjcBridge.callStaticMethod local queue_action = require("main/ext/queue_action") function luaoc.callStaticMethod(className, methodName, args) if not Device.isIOS() then return end queue_action.table_process(args) local ok, ret, errMsg = callStaticMethod(className, methodName, args) if not ok then local msg = string.format('luaoc.callStaticMethod("%s", "%s", "%s") - error: [%s] %s', className, methodName, tostring(args), tostring(ret), tostring(errMsg)) if ret == -1 then error(msg .. "INVALID PARAMETERS") elseif ret == -2 then error(msg .. "CLASS NOT FOUND") elseif ret == -3 then error(msg .. "METHOD NOT FOUND") elseif ret == -4 then error(msg .. "EXCEPTION OCCURRED") elseif ret == -5 then error(msg .. "INVALID METHOD SIGNATURE") else error(msg .. "UNKNOWN") end end return ok, ret end return luaoc main$require("framework/core/base/touch/TouchCom") require("framework/core/base/touch/TouchListener") require("framework/core/base/touch/TouchClickListener") require("framework/core/base/touch/TouchCustomListener") Msg.def("TouchComOnBegan") Msg.def("TouchComonMoved") Msg.def("TouchComOnEnd")UGuiUtil?-- -- UGUI 的lua侧工具,给UI添加各种触摸事件 -- local UGuiUtil = {} local LOGTAG = "UGuiUtil" local EventTrigger = CS.UnityEngine.EventSystems.EventTrigger local EventTriggerType = CS.UnityEngine.EventSystems.EventTriggerType local Button = CS.UnityEngine.UI.Button local UnityEngine = CS.UnityEngine local UI = CS.UnityEngine.UI local Screen = CS.UnityEngine.Screen local RectTransform = CS.UnityEngine.RectTransform function UGuiUtil._addEvent(go,eventID,cb) local trigger = go:AddComponent(typeof(EventTrigger)) local entry = EventTrigger.Entry() entry.eventID = eventID entry.callback = EventTrigger.TriggerEvent() entry.callback:AddListener( function (data) -- print("click ugui:"..go.name) if cb then cb(data) end end) trigger.triggers:Add(entry) end function UGuiUtil.addClickEvent(go, cb) if not go then return end go._clickCallbackList = go._clickCallbackList or {} table.insert(go._clickCallbackList, cb) UGuiUtil._addEvent(go,EventTriggerType.PointerClick,cb) end function UGuiUtil.addBeginDragEvent(go,cb) if go then UGuiUtil._addEvent(go,EventTriggerType.BeginDrag,cb) end end function UGuiUtil.addDragEvent(go,cb) if go then UGuiUtil._addEvent(go,EventTriggerType.Drag,cb) end end function UGuiUtil.addEndDragEvent(go,cb) if go then UGuiUtil._addEvent(go,EventTriggerType.EndDrag,cb) end end function UGuiUtil.addPointerDownEvent(go,cb) if go then UGuiUtil._addEvent(go,EventTriggerType.PointerDown,cb) end end function UGuiUtil.addPointerUpEvent(go,cb) if go then UGuiUtil._addEvent(go,EventTriggerType.PointerUp,cb) end end function UGuiUtil.addPointerEnterEvent(go,cb) if go then UGuiUtil._addEvent(go,EventTriggerType.PointerEnter,cb) end end function UGuiUtil.addPointerExitEvent(go,cb) if go then UGuiUtil._addEvent(go,EventTriggerType.PointerExit,cb) end end function UGuiUtil.addMoveEvent(go,cb) if not go then return end UGuiUtil._addEvent(go,EventTriggerType.Move,cb) end function UGuiUtil.addButtonClickEvent(go,cb) if not go then return end local button = go:GetComponent(typeof(Button)) if not button then return end go._clickCallbackList = go._clickCallbackList or {} table.insert(go._clickCallbackList, cb) button.onClick:AddListener(cb) end -- 输入框内容变化事件 function UGuiUtil.addInputFieldValueChangeEvent(go, cb) if not go then return end local componet = go:GetComponent(typeof(UI.InputField)) if not componet then return end componet.onValueChanged:AddListener(cb) end -- 输入框输入结束 function UGuiUtil.addInputFieldEndEditEvent(go, cb) if not go then return end local componet = go:GetComponent(typeof(UI.InputField)) if not componet then return end componet.onEndEdit:AddListener(cb) end -- 复选框切换 function UGuiUtil.addToggleValueChangeEvent(go, cb) if not go then return end local componet = go:GetComponent(typeof(UI.Toggle)) if not componet then return end componet.onValueChanged:AddListener(cb) end function UGuiUtil.isLongScreen() local screenWidth = CS.UnityEngine.Screen.width local screenHeight = CS.UnityEngine.Screen.height local whRatio = screenWidth / screenHeight if whRatio >= 1.78 then --2436*1125 是iphoneX的分辨率 return true else return false end end function UGuiUtil.worldSpaceToLocalSpace(p3, rectTrans) p3 = rectTrans:InverseTransformPoint(p3) local p2 = CS.UnityEngine.Vector2(p3.x, p3.y) return p2 end ---@param p2 CS.UnityEngine.Vector2 ---@param rectTrans CS.UnityEngine.RectTransform ---@param cameraCom CS.UnityEngine.Camera ---@return CS.UnityEngine.Vector2 function UGuiUtil.screenSpaceToLocalSpace(p2, rectTrans, cameraCom) local p3 = CS.UnityEngine.Vector3(p2.x, p2.y, 0) p3 = cameraCom:ScreenToWorldPoint(p3) p2 = util.ugui.worldSpaceToLocalSpace(p3, rectTrans) return p2 end ---屏幕空间转canvas空间 ---@param p2 CS.UnityEngine.Vector2 ---@param canvas CS.UnityEngine.GameObject ---@param cameraCom CS.UnityEngine.Camera function UGuiUtil.screenSpaceToCanvasSpace(p2, canvas, cameraCom) p2 = util.ugui.screenSpaceToLocalSpace(p2, canvas[CS.UnityEngine.RectTransform], cameraCom) return p2 end function UGuiUtil.getCutoutAreaRectInCanvasSpace(isFourceSymmetry, padding) padding = padding or 0 local orientation = CS.UnityEngine.Screen.orientation local w, h = CS.UnityEngine.Screen.width, CS.UnityEngine.Screen.height local rect = CS.UnityEngine.Rect(0, 0, w, h) local isCutout = util.ugui.isLongScreen() --and Device.isIOS() if isCutout then if orientation == CS.UnityEngine.ScreenOrientation.LandscapeLeft then rect.xMin = rect.xMin + 90 if isFourceSymmetry then rect.xMax = rect.xMax - 90 end else rect.xMax = rect.xMax - 90 if isFourceSymmetry then rect.xMin = rect.xMin + 90 end end end local p1 = util.ugui.screenSpaceToCanvasSpace(rect.min) local p2 = util.ugui.screenSpaceToCanvasSpace(rect.max) if isCutout then if p1 and p2 then p1.x = p1.x + padding p2.x = p2.x - padding end end return CS.UnityEngine.Rect(p1, p2 - p1) end -- 获取屏幕尺寸 function UGuiUtil:getScreenSize() if self.screenSize then return self.screenSize end local canvas = CS.UnityEngine.GameObject.Find("UIRoot") local size = {x = 0, y = 0} if canvas then local rectTrans = canvas:GetComponent(typeof(RectTransform)) if rectTrans then local sizeDelta = rectTrans.sizeDelta size.width = sizeDelta.x size.height = sizeDelta.y self.screenSize = size return size end end self.screenSize = size return size end -- 某一个物体是否在屏幕内 function UGuiUtil:isInScreen(node) local cam = UnityEngine.GameObject.Find("UICamera") local screenSize = self:getScreenSize() local worldpos = UnityEngine.Vector3(node:GetWorldPosition().x, node:GetWorldPosition().y, 0) local screenpos = cam:GetComponent("Camera"):WorldToScreenPoint(worldpos) return screenpos.x >0 and screenpos.x < screenSize.width end -- 某一个物体是否在某个物体的右侧 function UGuiUtil:isWayPointInScreen(rightNode,node) local cam = UnityEngine.GameObject.Find("UICamera") local rightNode_worldpos = UnityEngine.Vector3(rightNode:GetWorldPosition().x, rightNode:GetWorldPosition().y, 0) local rightNodescreenpos = cam:GetComponent("Camera"):WorldToScreenPoint(rightNode_worldpos) local screenSize = self:getScreenSize() local worldpos = UnityEngine.Vector3(node:GetWorldPosition().x, node:GetWorldPosition().y, 0) local screenpos = cam:GetComponent("Camera"):WorldToScreenPoint(worldpos) return screenpos.x - rightNodescreenpos.x > 0 and screenpos.x < screenSize.width end ---获取真正的安全区,去掉留海等等 ---@param padding {left:number, bottom:number, right:number, top:number} ---@param canvas CS.UnityEngine.GameObject ---@param cameraCom CS.UnityEngine.Camera function UGuiUtil.getSafeAreaRectInCanvasSpace(padding, canvas, cameraCom) padding = padding or CS.UnityEngine.RectOffset(-25, -25, 0, -25) local safeArea = Screen.safeArea safeArea.xMin = math.max(safeArea.xMin + padding.left, 0) safeArea.yMin = math.max(safeArea.yMin + padding.bottom, 0) safeArea.xMax = math.min(safeArea.xMax - padding.right, Screen.width) safeArea.yMax = math.min(safeArea.yMax - padding.top, Screen.height) local p1 = util.ugui.screenSpaceToCanvasSpace(safeArea.min, canvas, cameraCom) local p2 = util.ugui.screenSpaceToCanvasSpace(safeArea.max, canvas, cameraCom) local screenMin = util.ugui.screenSpaceToCanvasSpace(Vector2(0, 0), canvas, cameraCom) local screenMax = util.ugui.screenSpaceToCanvasSpace(Vector2(Screen.width, Screen.height), canvas, cameraCom) return Rect(p1, p2 - p1),p1-screenMin,p2-screenMax end function UGuiUtil.isUIGameObject(go) -- local isui = go:GetComponent(typeof(RectTransform)) ~=nil local isui = go:SeekInParentHierarchy("UICanvas") return isui end function UGuiUtil.isGlobalUIGameObject(go) return go:SeekInParentHierarchy("UIGlobal.uiroot") end -- 世界空间转canvas空间 function UGuiUtil.worldSpaceToCanvasSpace(p3, canvas) local p2 = util.ugui.worldSpaceToLocalSpace(p3, canvas[CS.UnityEngine.RectTransform]) return p2 end function UGuiUtil.rmClickEvent(go) if not go then return end go._clickCallbackList = nil UGuiUtil._rmEvent(go, EventTriggerType.PointerClick) end function UGuiUtil._rmEvent(go, eventID) local triggerList = go:GetComponents(typeof(EventTrigger)) if not triggerList then return end for i = 0, triggerList.Length - 1 do local trigger = triggerList[i] local entryList = trigger.triggers for j = entryList.Count - 1, 0 do local entry = entryList[j] if entry.eventID == eventID then entryList:Remove(entry) end end end end function UGuiUtil.rebuildLayout(go) if not go then printWarn(LOGTAG, "rebuildLayout, go is nil") return end local rectTrans = go[RectTransform] if not rectTrans then printWarn(LOGTAG, "rebuildLayout, rectTrans is nil") return end CS.UnityEngine.UI.LayoutRebuilder.ForceRebuildLayoutImmediate(rectTrans) end function UGuiUtil.disableAllTouches() CS.LuaGlobal.instance:SetTouchEnable(false) end function UGuiUtil.enableAllTouches() CS.LuaGlobal.instance:SetTouchEnable(true) end --#region 坐标转换 ---本地空间转世界空间 未测试 ---@param p2 CS.UnityEngine.Vector2 ---@param rectTrans CS.UnityEngine.RectTransform ---@return CS.UnityEngine.Vector3 function UGuiUtil.localSpaceToWorldSpace(p2, rectTrans) local p3 = CS.UnityEngine.Vector3(p2.x, p2.y, 0) p3 = rectTrans:TransformPoint(p3) return p3 end ---本地空间转屏幕空间 未测试 ---@param p2 CS.UnityEngine.Vector2 ---@param rectTrans CS.UnityEngine.RectTransform ---@param cameraCom CS.UnityEngine.Camera ---@return CS.UnityEngine.Vector2 function UGuiUtil.localSpaceToScreenSpace(p2, rectTrans, cameraCom) local p3 = UGuiUtil.localSpaceToWorldSpace(p2, rectTrans) p3 = cameraCom:WorldToScreenPoint(p3) local p2 = CS.UnityEngine.Vector2(p3.x, p3.y) return p2 end ---把 listener 移除,以防止 csharp 持有 lua 的方法,无法销毁 luaenv function UGuiUtil.removeAllListeners() printInfo(LOGTAG, "removeAllListeners") for index, value in ipairs(events) do value:RemoveAllListeners() end end --- 获取B物体世界坐标,讲B物体wp坐标转换到A物体父节点下的局部坐标 -- 让a物体(fromNode)飞到b物体(toNode),支持自定义飞行时间和动画结束回调。 -- @param fromNode 源UGUI节点 -- @param toNode 目标UGUI节点 -- @param duration 动画时长,单位秒 -- @param needClone 是否需要克隆 -- @param onComplete 回调函数,动画结束后调用 function UGuiUtil.flyUIToUI(fromNode, toNode, duration, needClone, onComplete) -- 1. 克隆fromNode到其父节点下 local parent = fromNode.transform.parent if not parent then printWarn(LOGTAG, "fromNode has no parent!") return end local parentRect = parent:GetComponent(typeof(CS.UnityEngine.RectTransform)) local actFromNode if needClone then actFromNode = GameObject.Instantiate(fromNode) actFromNode:SetName("fly_clone") actFromNode.transform:SetParent(parent, false) actFromNode.transform:SetAsLastSibling() else actFromNode = fromNode end -- 2. 获取toNode的世界坐标 local toWorldPos = toNode.transform:TransformPoint(Vector3.zero) -- 3. 获取fromNode父节点 local fromParent = fromNode:GetParent() -- 4. 将toNode的世界坐标转换到fromNode父节点下的局部坐标 local localTo = fromParent.transform:InverseTransformPoint(toWorldPos) actFromNode:RunAction(ua.Sequence({ ua.MoveTo(duration or 1.0, Vector3(localTo.x, localTo.y, 0)), ua.cb(function () if onComplete then onComplete(actFromNode) end end) })) end --- -- 让UI节点(fromNode)飞向世界物体(targetWorldObj),支持自定义飞行时间和动画结束回调。 -- @param fromNode 源UGUI节点(RectTransform) -- @param targetWorldObj 目标世界物体(如3D物体、场景物体等,需有GetWorldPosition方法或transform.position) -- @param duration 动画时长,单位秒 -- @param needClone 是否需要克隆UI节点 -- @param onComplete 回调函数,动画结束后调用 -- @param canvas UI画布对象(可选,默认查找"UIRoot") function UGuiUtil.flyUIToWorld(fromNode, targetWorldObj, duration, needClone, onComplete, canvas) canvas = canvas or CS.UnityEngine.GameObject.Find("UIRoot") if not canvas then printWarn(LOGTAG, "找不到UIRoot画布") return end local uiCamera = UILayerUtil:getCamera() local mainCamera = nil -- 兼容目标物体是3D物体或带有GetWorldPosition方法 local worldPos = nil if targetWorldObj.GetWorldPosition then worldPos = targetWorldObj:GetWorldPosition() elseif targetWorldObj.transform and targetWorldObj.transform.position then worldPos = targetWorldObj.transform.position else printWarn(LOGTAG, "目标物体没有世界坐标") return end -- 1. 获取目标世界物体的世界坐标转屏幕坐标 mainCamera = CS.UnityEngine.GameObject.Find("MainCamera") if not mainCamera then printWarn(LOGTAG, "找不到主摄像机 MainCamera") return end local screenPos = mainCamera:GetComponent("Camera"):WorldToScreenPoint(worldPos) -- 2. 屏幕坐标转UI画布下的局部坐标 local canvasRect = canvas:GetComponent(typeof(CS.UnityEngine.RectTransform)) local localTo = util.ugui.screenSpaceToLocalSpace(screenPos, canvasRect, uiCamera) -- 3. 获取fromNode在画布下的局部坐标 local fromParent = fromNode:GetParent() local actFromNode if needClone then actFromNode = GameObject.Instantiate(fromNode) actFromNode:SetName("fly_clone") actFromNode.transform:SetParent(fromParent, false) actFromNode.transform:SetAsLastSibling() else actFromNode = fromNode end -- 4. 执行动画 actFromNode:RunAction(ua.Sequence({ ua.MoveTo(duration or 1.0, Vector3(localTo.x, localTo.y, 0)), ua.cb(function () if onComplete then onComplete(actFromNode) end end) })) end -- 获取设备分辨率 function UGuiUtil.getDeviceResolution() local width = CS.UnityEngine.Screen.width local height = CS.UnityEngine.Screen.height printInfo(LOGTAG, string.format("设备分辨率: %dx%d", width, height)) return {width = width, height = height} end -- 按指定大小正方形框限制设置图片的平铺尺寸 function UGuiUtil:setImageTileSize(image, size) self:setImageTileSizeToRectangles(image, size, size) end -- 按指定大小长方形框限制设置图片的平铺尺寸 function UGuiUtil:setImageTileSizeToRectangles(image, maxWidth, maxHeight) if (not image) or (not image.sprite) then printWarn(LOGTAG, "图片平铺设置失败") return end local originalWidth = image.sprite.bounds.size.x local originalHeight = image.sprite.bounds.size.y local widthRatio = maxWidth / originalWidth; local heightRatio = maxHeight / originalHeight; -- 取最小比例,保证不会超出限制 local scaleRatio = Mathf.Min(widthRatio, heightRatio); -- 设置图片,保持图片的宽高比 image.rectTransform.sizeDelta = Vector2(originalWidth * scaleRatio, originalHeight * scaleRatio); end return UGuiUtilEnv--[[ author:{zhangpeng} time:2022-05-11 18:15:12 ]] ---@class Env:LuaClass local Env,super = defClass("Env") local LOGTAG = "Env" function Env:ctor(cfg) local env = cfg or {} env._G = env env.require = function(filename) return _require(self.env, filename) end env.import = function(filename) return _require(self.env, self.root .. filename) end env.defClass = function(name, super, _env) return defClass(name, super, _env or self.env) end env.defClassStatic = function(name, super, _env) return defClassStatic(name, super, _env or self.env) end env.defGlobal = function (name, _env) return defGlobal(name, _env or self.env) end env.setGlobal = function (t) for k, v in pairs(t) do printInfo(LOGTAG, "setGlobal k:%s, v:%s", k, v) rawset(self.env, k, v) end env.setGlobal = function () printError(LOGTAG, "setGlobal Cannot call twice") end end self.env = setmetatable(env, { __newindex = function(t, k, v) printError(LOGTAG, "Cannot define global variant in localenv") end, __index = function(t, k) local v = _ENV[k] if v == nil then v = _G[k] end rawset(t, k, v) return v end }) end function Env:require(filename, ...) local t = filename if type(filename) == "string" then t = {filename} end local class for _, f in ipairs(t) do local root, file = string.match(f, "(.*/)(.-)$") self.root = root or self.root class = self.env.require(f, ...) end return class end function Env:onExit() self.env = nil super.onExit(self) end CommonUtils--[[ luaide 模板位置位于 Template/FunTemplate/NewFileTemplate.lua 其中 Template 为配置路径 与luaide.luaTemplatesDir luaide.luaTemplatesDir 配置 https://www.showdoc.cc/web/#/luaide?page_id=713062580213505 author:{author} time:2024-01-31 15:13:59 ]] local CommonUtils = {} local Color = CS.UnityEngine.Color local inv255 = 1/255 function CommonUtils.hex2color(n) local r = ((n>>16)&0xff) local g = ((n>>8)&0xff) local b = ((n)&0xff) return Color(r*inv255,g*inv255,b*inv255) end function CommonUtils.hex2color4(n) local r = ((n>>24)&0xff) local g = ((n>>16)&0xff) local b = ((n>>8)&0xff) local a = ((n)&0xff) return Color(r*inv255,g*inv255,b*inv255,a*inv255) end function CommonUtils.color2hex(color) local floor = math.floor local r,g,b = floor(color.r*255),floor(color.g*255),floor(color.b*255) local n = 0 n = (r<<16)|n n = (g<<8)|n n = (b)|n return n end function CommonUtils.color2hex4(color) local floor = math.floor local r,g,b,a = floor(color.r*255),floor(color.g*255),floor(color.b*255),floor(color.a*255) local n = 0 n = (r<<24)|n n = (g<<16)|n n = (b<<8)|n n = (a)|n return n end return CommonUtilsDef$------------------------------------ --@func defClass(name, super=nil) --@desc 定义类 --@ret class:定义的类 --@ret super:定义类的父类,同输入super --@arg name:string,类名称 --@arg super:table,已经定义的其他类 --@func defClassStatic(name, super=nil) --@desc 定义静态类,生成的类不含new方法,直接通过类名调用 --@ret class:定义的类 --@ret super:定义类的父类,同输入super --@arg name:string,类名称 --@arg super:table,已经定义的其他类 ------------------------------------ local type = type local rawget = rawget local rawset = rawset local getmetatable = getmetatable local setmetatable = setmetatable local UnityEngine = CS.UnityEngine local LOGTAG = "Def" ---@class LuaClassBase local _cls_base = { ---需要退出之时,会遍历自己保持的所有子object和table中的object, 调用他们的 exit. ---如果保持了生命周期长于自己的object, 需要在 onExit 内改为nil, 例如 self.scene=nil ---最后要调用 super.onExit(self) ---@param self LuaClassBase onExit = function(self) if not self.__cls_inst then printError(LOGTAG, "[cls.onExit]invalid self", self.__cls_name) end self.__cls_call_onExit = true local selfStr = string.format("%s", self) for k, v in pairs(self) do if type(v) == "table" then if v.__cls_inst then if not v.__exited then v:exit(string.format("%s %s", selfStr, k)) end else for k, t in pairs(v) do if type(t) == "table" and t.__cls_inst then if not t.__exited then t:exit(string.format("%s %s", selfStr, k)) end end end end end end end, onmsg = function(self, ids, func, obj, priority) if obj ~= self then self._onmsgRefList = self._onmsgRefList or {} table.insert(self._onmsgRefList, { ids, func, obj }) end Msg.add(ids, func, obj, priority) end, timer = function(self, handler, interval, loop, priority, isTimingOnBackground, tailHandler) self._timerRefList = self._timerRefList or {} local ref = TimerMgr:add(handler, interval, loop, priority, isTimingOnBackground, tailHandler) table.insert(self._timerRefList, ref) return ref end, clear = function(self, onmsg, timer) self.__cls_call_clear = true if self._onmsgRefList and onmsg ~= false then for i, v in ipairs(self._onmsgRefList) do Msg.del(v[1], v[2], v[3]) end self._onmsgRefList = nil end if onmsg ~= false then Msg.del(nil, nil, self) end if self._timerRefList and timer ~= false then for i, v in ipairs(self._timerRefList) do TimerMgr:rem(v) end self._timerRefList = nil end end, valid = function(self) return (not self.__exited) end, ---退出,需要手动调用。尽量不要重构,除非需要hook exit, 比如第一次exit不退出,第二次exit才退出 ---@param self LuaClassBase exit = function(self, srcStr) if self.__exited then return end self.__exited = true printVerbose("Def", "exit of %s srcStr:%s", self, srcStr) self:clear() if not self.__cls_call_clear then printError("Def", "missing super.clear %s", self.__cls_name) end self:onExit() if not self.__cls_call_onExit then printError("Def", "[cls.exit]missing super.onExit. %s", self.__cls_name) end for k, _v in pairs(self) do self[k] = nil end self.__exited = true end } local _cls_mark = function(name, cls, env) local p = env or _ENV for ns in string.gmatch(name, "(.-)%.") do local t = rawget(p, ns) if not t then t = {} rawset(p, ns, t) end p = t end local mod = string.match(name, "([^%.]+)$") rawset(p, mod, cls) end function isClassOf(obj, name) local curr = obj local i = 0 while true do i = i + 1 if i > 1000 then error("死循环了") break end if not curr then break end if curr == _cls_base then return false end if curr.__cls_name == name then return true end local mt = getmetatable(curr) if mt then curr = mt.__index else return false end end end function defClass(name, super, env) env = env or _ENV if rawget(env, name) then local str = "defClass:redefined" .. name UnityEngine.Debug.LogError(str) error(str) end ---@class LuaClass:LuaClassBase local cls = {} cls.__cls_type = cls cls.__cls_name = name cls.__cls_source = debug.getinfo(2) cls.__ori_str = tostring(cls) local __tostring = function(t) return "LuaObject of " .. name .. " " .. t.__ori_str end local cls_meta = { __index = cls, __tostring = __tostring } cls.new = function(...) local obj = {} obj.__cls_inst = true obj.__ori_str = tostring(obj) obj.class = cls setmetatable(obj, cls_meta) -- 不添加,如果是从原生启动 unity 退出的时候需要清理的话,obj 自己添加消息 -- Msg.add(Msg.EXIT, function() -- if obj.exit then obj:exit("Msg.EXIT") end -- end, obj) if obj.ctor then obj:ctor(...) end return obj end super = super or _cls_base setmetatable(cls, { __index = super, __tostring = function(t) return "LuaClass of " .. name .. " " .. t.__ori_str end }) cls.super = super _cls_mark(name, cls, env) return cls, super end function defClassStatic(name, super, env) env = env or _ENV if rawget(env, name) then local str = "defClassStatic:redefined" .. name UnityEngine.Debug.LogError(str) error(str) end ---@class LuaStaticClass local cls = {} cls.__cls_type = cls cls.__cls_name = name cls.__cls_static = true cls.__ori_str = tostring(cls) cls.__cls_source = debug.getinfo(2) local __tostring = function(t) return "LuaStaticObject of " .. name .. " " .. t.__ori_str end if super then setmetatable(cls, { __index = super, __tostring = __tostring }) cls.super = super else setmetatable(cls, { __tostring = __tostring }) end _cls_mark(name, cls, env) return cls, super end function defGlobal(name, env) env = env or _ENV if rawget(env, name) then local str = "defGlobal:redefined" .. name UnityEngine.Debug.LogError(str) error(str) end local t = {} _cls_mark(name, t, env) return t end local _Lua_G = _G or _ENV local rawget = _Lua_G.rawget local rawset = _Lua_G.rawset local UnityEngineUI = UnityEngine.UI local _global_find = function(k) local v = rawget(_Lua_G, k) if v ~= nil then return v end local v = rawget(UnityEngineUI, k) if v then return v end xlua.import_type("UnityEngine.UI." .. k) local v = rawget(UnityEngineUI, k) if v then return v end local v = rawget(UnityEngine, k) if v then return v end xlua.import_type("UnityEngine." .. k) local v = rawget(UnityEngine, k) if v then return v end return nil end local _meta_newindex = function(t, k, v) local str = string.format("不允许修改全局变量:k:%s,v:%s\n%s", tostring(k), tostring(v), debug.traceback()) UnityEngine.Debug.Log(str) end local _meta_newindex_error = function(t, k, v) local str = string.format("不允许修改全局变量:k:%s,v:%s\n%s", tostring(k), tostring(v), debug.traceback()) UnityEngine.Debug.LogError(str) error(str) end local _meta_index = function(t, k) local v = _global_find(k) if v ~= nil then rawset(t, k, v) return v else UnityEngine.Debug.Log(string.format("全局变量不存在:k:%s,v:%s\n%s", tostring(k), tostring(v), debug.traceback())) return nil end end local _meta_index_error = function(t, k) local v = _global_find(k) if v ~= nil then rawset(t, k, v) return v else UnityEngine.Debug.LogError(string.format("全局变量不存在:k:%s,v:%s\n%s", tostring(k), tostring(v), debug.traceback())) return nil end end function isGlobalExists(k) local v = _global_find(k) return v ~= nil end function EnableGlobalCheck(env) setmetatable(env or _ENV, { __index = _meta_index_error, __newindex = _meta_newindex_error, }) end function DisableGlobalCheck(env) setmetatable(env or _ENV, { __index = _meta_index, __newindex = _meta_newindex, }) end DisableGlobalCheck() SceneComponentw---@class SceneComponent:LuaClass local SceneComponent = defClass("SceneComponent") ---@param scene Scene function SceneComponent:ctor(scene) self.scene = scene self.coms = scene.coms end function SceneComponent:onLoad() end function SceneComponent:afterOnLoad() end function SceneComponent:onSceneTransitionOver() end function SceneComponent:DrawIMGUI() end function SceneComponent:getSceneArgs() return self.scene:getSceneArgs() end function SceneComponent:getRootNode() return self.scene.rootNode end function SceneComponent:getSceneCamera() return self.scene.cams.scene end return SceneComponent SocketCmdMgr--[[ 业务指令收发 author:{zhangpeng} time:2023-08-23 18:01:16 ]] local SocketCmdMgr,_ = defClassStatic("SocketCmdMgr") local Guid = CS.System.Guid local pb = raw_require("pb") local PROTO_PATH = "Assets/AssetsPackage/Res/framework/proto/" local LOG_TAG = "SocketCmdMgr" function SocketCmdMgr:init() self.protoNameList = { "handshake.pb", "chat.pb", "head.pb" } self.R = Res.loadResLink("framework/network/socket/pbreslink") printInfo(LOG_TAG, "init, url:%s, port:%s", self.url, self.port) end function SocketCmdMgr:send(cmdId,callback) PBSocketPack.headProtoName = "head.Head" -- 聊天指令 if cmdId == CmdDef.SendDef.SingleChat then local head = { msg_type = CmdDef.MsgType.DATA, headData = { ["tag"] = cmdId, ["messageId"] = Guid.NewGuid():ToString("N") }, request_name = "SingleChat" } local body = { ["toUserId"] = "64e41f7735a9d490710fd4b0", ["content"] = "测试聊天", } local reqest_proto = "chat.SendSingleChat" local response_proto = "chat.ReceiveSingleChat" SocketMgr:send(head,body ,reqest_proto,response_proto,function (suc,rspData) printInfo(LOG_TAG,string.format("收到聊天信息:%s",rspData.reqBody.content)) if callback then callback(suc,rspData) end end) end end function SocketCmdMgr:receive() end SocketCmdMgr:init()KVTable ---@class KVTable:LuaClass local KVTable = defClass("KVTable") local LOGTAG = KVTable.__cls_name --[[ ]] function KVTable:ctor(databaseApis, name) self.databaseApis = databaseApis self.name = name self:init() end function KVTable:init() self.columnList = {} end function KVTable:getName() return self.name end --------------------------------------------------------------------------------------------- --数据库 增删改查 相关接口 --------------------------------------------------------------------------------------------- ---获取, key 可以是列表 ---@param key string|string[] ---@param defaultValue any ---@return any function KVTable:get(key, defaultValue) return self.databaseApis.get(key, defaultValue) end ---设置 ---@param key string|string[] ---@param value any function KVTable:set(key, value) return self.databaseApis.set(key, value) end -- 获取所有的key ---@return any[] function KVTable:getKeys() return self.databaseApis.getKeys() end return KVTable XSdk---@diagnostic disable: duplicate-set-field --[[ author:{zhangpeng} time:2023-09-24 17:18:10 ]] local XSdk, super = defClassStatic("XSdk", XSdkBase) function XSdk:init() end XSdk:init()PerfUtil--[[ performance benchmark 性能分析工具 author:{zhangpeng} time:2022-05-21 18:04:32 ]] local PerfUtil = {} local LOG_TAG = "PerfUtil" local Time = CS.UnityEngine.Time local Stopwatch = CS.System.Diagnostics.Stopwatch local ms_inv = (1000*1000) / Stopwatch.Frequency local BenchMarkMeta = { init = function(self) self.stopWatch = Stopwatch() self.stopWatch:Start() self.lastTime = self.stopWatch.ElapsedMilliseconds -- 所用时间 毫秒 end, dump = function(self,msg) local time = self.stopWatch.ElapsedMilliseconds if msg then self.stopWatch:Stop() local dt = (time - self.lastTime) printInfo(LOG_TAG,"[性能]<%.3fms>-%s<当前帧:%d>",dt,msg,Time.frameCount) self.stopWatch:Start() else local info = debug.getinfo(2) local src = info.short_src local line = info.currentline self.stopWatch:Stop() local dt = (time - self.lastTime) printInfo(LOG_TAG,"[性能]<%.3fms,frm:%d>-%s:%d",dt,Time.frameCount,src,line) self.stopWatch:Start() end self.lastTime = time end, stop = function(self) self.stopWatch:Stop() end } function PerfUtil.BenchMark() local inst = {} setmetatable(inst,{ __index = BenchMarkMeta, }) inst:init() return inst end return PerfUtil