require("preload.PluginPatcher") --[[ 更新流程 1、获取服务器信息 2、更新资源 4.1 如果无需更新,则跳过此步骤 4.2 下载差分包并解压 3、如果是android,先检查APK更新。 2.1 如果无需更新,则跳过此步骤 2.2 下载差分包 2.3 合并新的APK 2.4 安装新的APK 4、检查强更版本号。 3.1 如果无需更新,则跳过此步骤 3.2 如果需要更新,则弹出更新提示 5、检查文件完整性 6、完成更新 6.1 如果本次没有更新资源,则完成更新 6.2 如果本次有更新内容,则提示玩家重启游戏。否则直接进入游戏 --]] local AutoUpdater = {}; AutoUpdater.__index = AutoUpdater; local writablePath = cc.FileUtils:getInstance():getWritablePath() local updateConfirmSize = 5 * 1024 * 1024 local tempDir = os.date("%Y%m%d%H%M", os.time()); local tempFile="tempFile.zip" local zipFileDir = writablePath .. tempDir .. '/' local zipFilePath = zipFileDir .. tempFile; -- 创建新对象 function AutoUpdater:new(...) local instance = {}; setmetatable(instance , AutoUpdater); instance:ctor(...); return instance; end function AutoUpdater:ctor(romSetting, mainScene, onFinsishCallback) -- 保存全局配置 self.romSetting = romSetting self.onFinsishCallback = onFinsishCallback self.Packages={} self.OldFiles={} self.NewFiles={} -- 渲染视图 self.View = require("preload.AutoUpdate.AutoUpdaterView"):new(self, mainScene) -- 是否提示过APK更新 self.isShowApkPatch = false; --是否清理过文件 self.IsCleanFile = false --是否更新过程出现问题 self.IsError=false -- 是否更新了资源 self.isUpdateRes = false; -- 标记本次更新是否已经成功获取到配置信息了 self.isGetServerConfigSuccessed = false end function AutoUpdater:doNext() logD("AutoUpdater:doNext()"); if #self.execQueue > 0 then local execFunc = self.execQueue[1] table.remove(self.execQueue, 1) if execFunc then execFunc() end else self:onUpdateFinished() end end function AutoUpdater:startUpdate() logD("AutoUpdater:startUpdate()"); self.execQueue = {} table.insert(self.execQueue, handler(self, self.getServerConfigs)) table.insert(self.execQueue, handler(self, self.updateRes)) table.insert(self.execQueue, handler(self, self.updateApk)) table.insert(self.execQueue, handler(self, self.updateForce )) table.insert(self.execQueue, handler(self, self.checkFilesExist)) self:doNext() end function AutoUpdater:getServerConfigs() logD("AutoUpdater:getServerConfigs()"); self.isGetServerConfigSuccessed = false local function onEndGetServerConfig(isSuccessed) logD("AutoUpdater:onEndGetServerConfig(), isSuccessed = ", tostring(isSuccessed)); if isSuccessed then if not self.isGetServerConfigSuccessed then self.isGetServerConfigSuccessed = true self:doNext() else logD("AutoUpdater:getServerConfigs(), 重复收到服务器的配置数据,本次不处理"); end else self:dispatchEvent("error", "初始化失败,请更换网络后重试") end end if isIOSPlatform() then checkNetWorkPermission(function() getServerConfigs(onEndGetServerConfig, true) end) else getServerConfigs(onEndGetServerConfig, true) end end -- 更新资源 function AutoUpdater:updateRes() logD("AutoUpdater:updateStart() 检查资源更新") if PhpSetting.update.url=="" or PhpSetting.update.md5=="" then local ver = loadVersion() if tonumber(ver) ~= tonumber(PhpSetting.update.ver) and not self.IsCleanFile then logD("AutoUpdater:updateStart() 找不到版本,开始清理数据") self:ClearDatingCache() self:startUpdate() self.IsCleanFile = true else logD("AutoUpdater:updateStart() 版本一致,无需更新") self:doNext() end return end -- 有文件需要更新 logD("AutoUpdater:updateStart() 有文件需要更新") self.isUpdateRes = true; self.DownloadedFileSize=tonumber(PhpSetting.update.size) local function onFinishDownZip() logD("AutoUpdater:onFinishDownZip(), 下载差分包完成"); local md5=cc.FileUtils:getInstance():md5File(zipFilePath) if md5~=PhpSetting.update.md5 then logD("AutoUpdater:onFinishDownZip(), 校验差分包失败, update.md5 = " .. PhpSetting.update.md5 ); logD("AutoUpdater:onFinishDownZip(), 校验差分包失败, md5 = " .. md5 ); os.remove(zipFilePath) self:dispatchEvent("error",PLN.FILE_CHECKSUM_FAILED) return end self:dispatchEvent("zip",{text=PLN.UPDATING_RES,percent=1}) runDelayWithTime(function() local isZipSuccessed=cc.FileUtils:getInstance():decompress(zipFilePath) os.remove(zipFilePath) logD("AutoUpdater:onFinishDownZip(), 解压差分包完成" ); if isZipSuccessed then local move_result = move_dir(zipFileDir, writablePath); if move_result then logD("AutoUpdater:onFinishDownZip() need restart"); local function OnClickOk() cc.Application:getInstance():restart() end local tipsMessage = "更新完成,点击确认重启游戏" showTipsView(tipsMessage, OnClickOk, nil) else self:dispatchEvent("error",PLN.FILE_MOVE_FAILED) end else self:dispatchEvent("error",PLN.FILE_UNCOMPRESS_FAILED) end remove_dir(zipFileDir) end,0.01) end local function onState( state, msg ) logD("AutoUpdater:updateRes() onState() :", state, msg) if state=="progress" then local percent = msg--self.DownloadedFileSize*msg / self.DownloadedFileSize; self:dispatchEvent("downloadFile" , {text = string.format(PLN.DOWNLOADING_FILE , self.DownloadedFileSize*percent / 1024 / 1024 , self.DownloadedFileSize / 1024 / 1024 , percent * 100) , percent = percent}); elseif state=="successed" then local percent = 1 self:dispatchEvent("downloadFile" , {text = string.format(PLN.DOWNLOADING_FILE , self.DownloadedFileSize*percent / 1024 / 1024 , self.DownloadedFileSize / 1024 / 1024 , percent * 100) , percent = percent}); runDelayWithTime(function() onFinishDownZip() end,0.01) else self:dispatchEvent("error",PLN.DOWNLOAD_FILE_FAILED) end end if not cc.FileUtils:getInstance():isDirectoryExist(zipFileDir) then if not cc.FileUtils:getInstance():createDirectory(zipFileDir) then logD("AutoUpdater:updateStart() createDirectory failed :" .. zipFileDir) end end downloadFileByUrls2(PhpSetting.update.url, onState, zipFilePath) end -- 更新APK function AutoUpdater:updateApk() if self.isShowApkPatch then self:doNext() return end self.isShowApkPatch = true; logD("AutoUpdater:updateApk()") if isAndroidPlatform() and PhpSetting.apkupdate and PhpSetting.apkupdate.url ~= nil and PhpSetting.apkupdate.url ~= "" then local function installNewApk(apk_file) logD("AutoUpdater:installNewApk() apk_file = ", apk_file) patcherInstallApk(apk_file); end local function mergePatchToApk(patchFilePath) logD("AutoUpdater:mergePatchToApk() patchFilePath = ", patchFilePath) self:dispatchEvent("info","正在生成新的安装包") local patch_file = patchFilePath; local patch_md5 = PhpSetting.apkupdate.md5; local apk_file = writablePath .. PhpSetting.apkupdate.ver .. ".apk"; local apk_md5 = PhpSetting.apkupdate.apkmd5 local result = patcherMakeApk(patch_file, patch_md5, apk_file, apk_md5); logD("AutoUpdater:mergePatchToApk() result = ", result) if result then installNewApk(apk_file); else self:doNext() end end local function downloadPatch () local totalSize = tonumber(PhpSetting.apkupdate.size) local currentSize = 0; self:dispatchProgress(currentSize, totalSize); local patchFilePath = writablePath .. PhpSetting.apkupdate.ver .. ".patch"; logD("AutoUpdater:downloadPatch() patchFilePath = ", patchFilePath) local function onState( state, msg ) logD("AutoUpdater:downloadPatch() onState() :", state, msg) if state=="progress" then local percent = msg currentSize = totalSize * percent; self:dispatchProgress(currentSize, totalSize); elseif state=="successed" then local percent = 1 currentSize = totalSize self:dispatchProgress(currentSize, totalSize); runDelayWithTime(function() mergePatchToApk(patchFilePath) end,0.01) else self:dispatchEvent("error",PLN.DOWNLOAD_FILE_FAILED) -- 如果下载失败就直接进入下一步,检测强制更新 self:checkUpdate(); end end logD("AutoUpdater:downloadPatch() url = ", PhpSetting.apkupdate.url) downloadFileByUrls2(PhpSetting.apkupdate.url, onState, patchFilePath) end local function onClickOk() downloadPatch(); end local function onClickCancel() self:doNext() end -- 提示信息 local content = PhpSetting.apkupdate.content; if content == nil or content == "" then content = "发现有新的安装包可以使用,是否下载新的安装包?" end -- 是否可以取消更新 if PhpSetting.apkupdate.update_type == 2 then onClickCancel = nil end -- 点击确定后是否关闭界面 local closeWhenOk = true; require("preload.AutoUpdate.UpdateTipsView"):new(content, onClickOk, onClickCancel, closeWhenOk) return else self:doNext() end end -- 检查强更版本 function AutoUpdater:updateForce() if PhpSetting and PhpSetting.force then -- 检查强制更新 if tonumber(PhpSetting.force.isForce) > 0 then local title = "发现新版本" local content = PhpSetting.force.tips or "" local url = PhpSetting.force.url if not url or url == "" then url = self.romSetting.downloadUrl end if not url or url == "" then url = "https://www.baidu.com" end local function onClick(ret) logD("AutoUpdater:updateForce() onClick, url = ", tostring(url)) if PluginDevice then PluginDevice:callVoid("openUrl", url) else logD("AutoUpdater:updateForce() PluginDevice is nil") end end require("preload.AutoUpdate.UpdateTipsView"):new(content, onClick ) return end -- 检查提示更新 if tonumber(PhpSetting.force.isTips) > 0 then local title = "发现新版本" local content = PhpSetting.force.tips or "" local url = PhpSetting.force.url if not url or url == "" then url = self.romSetting.downloadUrl end if not url or url == "" then url = "https://www.baidu.com" end local function onClickOk(ret) logD("AutoUpdater:updateForce() onClickOk, url = ", tostring(url)) if PluginDevice then PluginDevice:callVoid("openUrl", url) else logD("AutoUpdater:updateForce() PluginDevice is nil") end end local function onClickCancel(ret) logD("AutoUpdater:updateForce() onClickCancel") self:doNext() end require("preload.AutoUpdate.UpdateTipsView"):new(content,onClickOk, onClickCancel) return end end self:doNext() end -- 检查文件是否完整 function AutoUpdater:checkFilesExist() local files_list_file = self.romSetting.Platform .. "/files.txt" local ret, missing_files_list = checkFilesExist(files_list_file) if ret then logD("AutoUpdater:checkFilesExist(), 校验完成,未发现文件缺失"); self:doNext(); else logD("AutoUpdater:checkFilesExist(), 校验完成,发现有文件丢失"); local function OnClickOk() self:ClearDatingCache() cc.Application:getInstance():restart() end local tipsMessage = "发现有文件丢失,点击确定开始修复" showTipsView(tipsMessage, OnClickOk, nil) end end -- 更新完成 function AutoUpdater:onUpdateFinished() logD("AutoUpdater:onUpdateFinished()"); self:dispatchEvent("finish", "更新完成!") if self.isUpdateRes then logD("AutoUpdater:onUpdateFinished() need restart"); local function OnClickOk() cc.Application:getInstance():restart() end local tipsMessage = "更新完成,点击确认重启游戏" showTipsView(tipsMessage, OnClickOk, nil) else logD("AutoUpdater:onUpdateFinished() not need restart"); if self.onFinsishCallback then self.onFinsishCallback() end self.View:onExit(); end end -- 通知界面显示当前进度 function AutoUpdater:dispatchProgress(currentSize, totalSize) local percent = currentSize / totalSize self:dispatchEvent("downloadFile" , {text = string.format(PLN.DOWNLOADING_FILE , currentSize / 1024 / 1024 , totalSize / 1024 / 1024 , percent * 100) , percent = percent}); end -- 派发一个更新事件给外部侦听者 function AutoUpdater:dispatchEvent(eventName , eventValue) logD("AutoUpdater:dispatchEvent()" , eventName , eventValue); --[[ if eventName == "error" then self.IsError=true uploadErrorLogs("AutoUpdaterError"); end --]] self.View:showProgress(eventName , eventValue); if eventName == "error" then local function OnClickOk() cc.Application:getInstance():restart() end -- 上报更新错误 uploadLogs(GAME_ERROR_TYPE.UPDATEFAILED) -- 提示更新失败 local tipsMessage = "出错了,点击确定重新开始更新" showTipsView(tipsMessage, OnClickOk, nil) end end -- 清理大厅的缓存文件 function AutoUpdater:ClearDatingCache() logD("AutoUpdater:ClearDatingCache() 清理大厅的缓存文件") local corePath = writablePath.."core/" local datingRomFiles = writablePath..self.romSetting.Platform.."/romFiles/" local datingPreload = writablePath..self.romSetting.Platform.."/preload/" local datingScript = writablePath..self.romSetting.Platform.."/luaScript/" local datingRes = writablePath..self.romSetting.Platform.."/res/" cc.FileUtils:getInstance():removeDirectory(corePath) cc.FileUtils:getInstance():removeDirectory(datingRomFiles) cc.FileUtils:getInstance():removeDirectory(datingPreload) cc.FileUtils:getInstance():removeDirectory(datingScript) cc.FileUtils:getInstance():removeDirectory(datingRes) local filesListPath = writablePath..self.romSetting.Platform.."/files.txt" os.remove(filesListPath) cc.FileUtils:getInstance():purgeCachedEntries() --logD("删除UserConfig.xml") --os.remove(writablePath .. "UserConfig.xml") end return AutoUpdater;