ios lua 增量更新,更新内部代码
基于Cocos2d-x+Lua的技術(shù)架構(gòu)的游戲的更新,只更新腳本即可。Lua中可以實現(xiàn)對于C++類的繼承,更新邏輯還是比較容易的。
App有兩個路子:一個是基于PhoneGap或者React Native的混合開發(fā)(天貓好像已經(jīng)遷移到了React Native上);一種是使用JSPatch來為程序打補丁。不過就功能來看,還是React Native要強大一點,畢竟定位不一樣。
那游戲是怎這么做的,通過Lua或者js。下載下來的是腳本,只是文本文件,那當(dāng)然可以更新。
比較有名的項目有Wax和JSPatch,就是做了這么一個事情。
參考地址:
知乎討論熱更新帖
http://www.zhihu.com/question/28079874
基于Quick-cocos2dx 2.2.3 的動態(tài)更新實現(xiàn)完整篇。(打包,服務(wù)器接口,模塊自更新)
http://my.oschina.net/u/1785418/blog/283043
?1,如何設(shè)計更新服務(wù)器接口。 2,不改變原框架的代碼的情況下如何實現(xiàn)更新,并且可以實現(xiàn)精確的進度。 3,如何按照版本打包。 4,如何跨n個小版本更新。 5,版本回滾。 6,如何更新你的自動更新模塊和framework_precomplied.zip代碼。
先直接上代碼UpdateScene.lua:
代碼位置為:你的游戲目錄/update/UpdateScene.lua
require("config") require("framework.init") require("framework.shortcodes") require("framework.cc.init")local UpdateScene ?= class("UpdateScene", function()return display.newScene("UpdateScene") end)local NEEDUPDATE ? = true local server ? ? ? = "http://服務(wù)器ip:3001/" local versionFile ?= "version/?fileVersion=" local allFileList ?= "version/?id=" local nowVersion ? = CCUserDefault:sharedUserDefault():getStringForKey("current-version-code") local bigVersion ? = CCUserDefault:sharedUserDefault():getStringForKey("VERSION_BIG")function UpdateScene:ctor( ?)display.newSprite("kfs_background.png", display.cx, display.cy):addTo(self)self.path = device.writablePath.."kdfs/"self:createDownPath(self.path)self:createDownPath(self.path.."res/")self:createDownPath(self.path.."scripts/")if string.len(nowVersion) == 0 thennowVersion = bigVersion..".0"endlocal list ? ? ?= string.split(nowVersion,".")self.nowId ? ? ?= tonumber(list[3])self.versionBig = list[1].."."..list[2]--大版本變化了if self.versionBig ~= bigVersion then?self:delAllFilesInDirectory(self.path)endNEEDUPDATE ? ? ? ? ?= CCUserDefault:sharedUserDefault():getBoolForKey("CanUpdate")--NEEDUPDATE ? ? ? ? ?= trueself.updateProgress = self:newProgressTimer( "kfs_jindutiaobox.png","kfs_jindutiao.png" ):pos(display.cx,120):addTo(self)self.progressLabel ?= ui.newTTFLabel({text = "更新", size = 26, align = ui.TEXT_ALIGN_CENTER, color = display.COLOR_BLACK}):pos(display.cx,160):addTo(self)? endfunction UpdateScene:downIndexedVersion( ?)if not self.nowDownIndex then self.nowDownIndex = 1 endlocal versionUrl = server..versionFile..self.needDownVersions[self.nowDownIndex].version.."."..self.needDownVersions[self.nowDownIndex].idlocal packageUrl = self.needDownVersions[self.nowDownIndex].fileUrlif self.nowDownIndex == 1 then?self.assetsManager = AssetsManager:new(packageUrl,versionUrl,self.path) ?--資源包路徑,代碼號路徑,存儲路徑self.assetsManager:registerScriptHandler(handler(self, self.downHandler))elseself.assetsManager:setVersionFileUrl(versionUrl)self.assetsManager:setPackageUrl(packageUrl)endif self.assetsManager:checkUpdate() thenself.assetsManager:update()end endfunction UpdateScene:getNewestVersion( ?)self.progressLabel:setString("正在獲取版本列表")if not NEEDUPDATE thenprint("當(dāng)前版本為DEBUG版本,不需要更新!")self.progressLabel:setString("當(dāng)前版本為DEBUG版本,不需要更新")self:performWithDelay(function ( ?)self:noUpdateStart()end,2)returnendfunction callback(event)local ok = (event.name == "completed")local request = event.requestif event.name then print("request event.name = " .. event.name) endif not ok thenprint("請求失敗 "..request:getErrorMessage())self.progressLabel:setString("版本更新列表請求網(wǎng)絡(luò)出錯")self:performWithDelay(function ( ?)self:noUpdateStart()end,2)returnendlocal code = request:getResponseStatusCode()if code ~= 200 thenprint("請求錯誤,代碼 "..request:getResponseStatusCode())self.progressLabel:setString("版本更新列表請求網(wǎng)絡(luò)出錯"..request:getResponseStatusCode())self:performWithDelay(function ( ?)self:noUpdateStart()end,2)returnendif json.decode(request:getResponseString()) thenprint(request:getResponseString())local needDownVersions = json.decode(request:getResponseString())if needDownVersions.code == 200 thenself.needDownVersions = needDownVersions.listfor i,v in ipairs(self.needDownVersions) doif v.needRestart > 0 thenself.needRestart = trueendendif #self.needDownVersions > 0 thenself:downIndexedVersion()endelseself.progressLabel:setString("當(dāng)前版本已經(jīng)是最新版本")self.updateProgress.progressTimer:setPercentage(100)self:performWithDelay(function ( ?)self:noUpdateStart()end,2)endendendlocal request = network.createHTTPRequest(callback, server..allFileList..self.nowId.."&versionBig="..self.versionBig, "GET")request:setTimeout(10)request:start() endfunction UpdateScene:onEnter( ?)self:getNewestVersion() endfunction UpdateScene:afterUpdateStart( ?)if self.needRestart thenprint("提示需要重新啟動游戲")require("game")game.exit()returnendprint("更新成功,啟動游戲")package.loaded["config"] = nilCCLuaLoadChunksFromZIP("game.zip")require("game")game.startup() endfunction UpdateScene:noUpdateStart( ?)print("沒有更新或者更新失敗啟動游戲")require("game")CCLuaLoadChunksFromZIP("game.zip")game.startup() endfunction UpdateScene:downHandler( event )if event == "success" then?if self.nowDownIndex < #self.needDownVersions then?self.nowDownIndex = self.nowDownIndex +1self:downIndexedVersion()elseself.progressLabel:setString("更新成功")self:performWithDelay(function ( ?)self:afterUpdateStart()end,2)endelseif string.startWith(event,"error") thenlocal text = ""if event == "errorNetwork" then text = "網(wǎng)絡(luò)出錯!" endif event == "errorNoNewVersion" then self.updateProgress.progressTimer:setPercentage(100) text = "已是最新" endif event == "errorUncompress" then text = "解壓出錯" endif event == "errorUnknown" then text = "未知錯誤" endself.progressLabel:setString(text)self:performWithDelay(function ( ?)self:noUpdateStart()end,2)elselocal alreadyDownPercent = (self.nowDownIndex - 1)*100/(#self.needDownVersions )print("alreadyDownPercent:",alreadyDownPercent)alreadyDownPercent = alreadyDownPercent + event/(#self.needDownVersions )alreadyDownPercent = math.floor(alreadyDownPercent)self.progressLabel:setString("已更新"..alreadyDownPercent.."%")self.updateProgress.progressTimer:setPercentage(alreadyDownPercent)self:progressTimerAction(self.updateProgress.progressTimer,self.updateProgress.progressTimer:getPercentage(),alreadyDownPercent)end endfunction UpdateScene:createDownPath( path )if not self:checkDirOK(path) thenprint("更新目錄創(chuàng)建失敗,直接開始游戲")self:noUpdateStart()returnelse-- print("更新目錄存在或創(chuàng)建成功")end endfunction UpdateScene:checkDirOK( path )require "lfs"local oldpath = lfs.currentdir()if lfs.chdir(path) thenlfs.chdir(oldpath)return trueendif lfs.mkdir(path) thenreturn trueend endfunction UpdateScene:newProgressTimer( bgBarImg,progressBarImg )?local bg = display.newSprite(bgBarImg)local progressTimer = CCProgressTimer:create(display.newSprite(progressBarImg))bg.progressTimer = progressTimerprogressTimer:setType(kCCProgressTimerTypeBar)?progressTimer:setPercentage(0)?progressTimer:setMidpoint(ccp(0,0))?progressTimer:setBarChangeRate(ccp(1, 0))?progressTimer:setPosition(ccp(bg:getContentSize().width/2,bg:getContentSize().height/2))?bg:addChild(progressTimer, 140)return bg endfunction UpdateScene:progressTimerAction( progressTimer,fromPercentage,toPercentage,duration )if not duration then duration = 0.3 endlocal ac = CCProgressFromTo:create(duration,fromPercentage,toPercentage)progressTimer:runAction(ac) endfunction UpdateScene:delAllFilesInDirectory( path )for file in lfs.dir(path) doif file ~= "." and file ~= ".." thenlocal f = path..'/'..filelocal attr = lfs.attributes (f)assert (type(attr) == "table")if attr.mode == "directory" thenself:delAllFilesInDirectory (f)elseos.remove(f)endendend endstring.startWith = function(str,strStart)local a,_ = string.find(str,strStart)return a==1 endstring.split = function(s, p)local rt= {}string.gsub(s, '[^'..p..']+', function(w) table.insert(rt, w) end )return rt endreturn UpdateScene 代碼解釋:NeedUpdate 這個變量代表自動更新開關(guān),平常咱們在debug模式下等等時候,不需要這個這個功能,可以把它關(guān)掉。
server,versionFile,allFileList 這3個變量組成你增量更新所需要的2個服務(wù)器接口。
server..versionFile?接受一個版本號作參數(shù),返回的也是這個版本號,具體原因參見代碼,主要是為了配合AssetsManager的參數(shù)需求。
server..allFileList?接受一個當(dāng)前的版本的小版本號。比如全版本是 1.0.1 的話,那小版本就是1。同理 1.0.5的話,小版本id就是5。
server..allFileList?id=0&bigversion=1.0 則返回以下格式接口:
{"code":200,"list":[{"id":1,"fileUrl":"http://服務(wù)器地址/vf/kdfs1.0.1.zip","version":"1.0","needRestart":0}]} 接口中有一個很重要的變量?needRestart 表示本次更新是否有需要重新啟動游戲加載的更新包。具體原因見后文。?
其它代碼不一一贅述。
以上就是服務(wù)器接口設(shè)計篇。
接下來講講更新自更新模塊和打包。
簡單講講實現(xiàn)方法。
將前文的UpdateScene編譯成zip,然后和?framework_precompiled.zip 一起在放到res目錄下,游戲啟動的時候別加載這兩個文件。main.lua為如下代碼:
function __G__TRACKBACK__(errorMessage)print("----------------------------------------")print("LUA ERROR: " .. tostring(errorMessage) .. "\n")print(debug.traceback("", 2))print("----------------------------------------") endlocal writablePath = CCFileUtils:sharedFileUtils():getWritablePath()CCFileUtils:sharedFileUtils():addSearchPath(writablePath.."kdfs/".."res/") CCFileUtils:sharedFileUtils():addSearchPath(writablePath.."kdfs/".."scripts/") CCFileUtils:sharedFileUtils():addSearchPath("res/") CCFileUtils:sharedFileUtils():addSearchPath("scripts/")CCLuaLoadChunksFromZIP("framework_precompiled.zip") CCLuaLoadChunksFromZIP("update.zip")xpcall(function()CCDirector:sharedDirector():runWithScene(require("UpdateScene").new()) end, __G__TRACKBACK__)并且在你的游戲代碼里排除掉?UpdateScene.lua 。目的是為了保證游戲啟動的時候加載的就是唯一的一份UpdateScene代碼。
sh quick的目錄/bin/compile_scripts.sh -i 游戲的目錄/update -o update.zip
我沒有把UpdateScene.lua 放到 scripts目錄下,而是有個單獨的目錄update。這樣的話可以單獨把這個目錄打包,然后啟動游戲的時候加載。
可以參考下面的腳本完整實現(xiàn)以下功能:
?| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 | #!/bin/sh DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" APP_ROOT="游戲目錄啦" echo "$DIR" echo "$APP_ROOT" rm -rf *.zip echo "開始編譯代碼" sh Quick目錄/bin/compile_scripts.sh -i 游戲目錄/scripts -o game.zip?? #編譯游戲代碼 sh Quick目錄/bin/compile_scripts.sh -i 游戲目錄/update -o update.zip? #編譯自更新代碼 if [ -d "$DIR"/res ]; then ????rm -rf "$DIR"/res/*??? #把當(dāng)前目錄下的res目錄清空 fi if [ -d "$DIR"/scripts ]; then ????rm -rf "$DIR"/scripts/* #把當(dāng)前目錄下的res目錄清空 fi chmod 777 "$DIR"/scripts chmod 777 "$DIR"/res echo "- copy scripts" cp -rf -p game.zip "$DIR"/scripts #把編譯好的game.zip復(fù)制到scripts目錄下 cp -rf -p update.zip "$DIR"/../res/update.zip? #把編譯好的update.zip 放到游戲目錄下的res目錄下 cp -rf -p update.zip "$DIR"/res?? #把編譯好的update.zip放到當(dāng)前目錄的res目錄下 echo "- copy resources" cp -rf -p "$APP_ROOT"/res "$DIR"/? #把游戲目錄下的res目錄所有的內(nèi)容復(fù)制到當(dāng)前目錄下的res目錄 find ./res -type f -mtime +15000 -exec rm -rf {} \;? #實現(xiàn)增量更新的關(guān)鍵,代碼含義為 刪掉當(dāng)前res目錄下 文件修改日期為 多少天或者多少時或多少秒 之前的文件 find ./res -name ".DS_Store" | xargs rm -Rf???? #清除一些冗余文件,可不要 echo "打包res和scripts" zip -r kdfs.zip res/* scripts/*?? #將當(dāng)前版本的增量更新的res資源和代碼game.zip 打包成一個文件 cp kdfs.zip kdfs$1.zip??? #根據(jù)接受的參數(shù)重新命名本次更新文件 echo "開始上傳" scp kdfs$1.zip root@服務(wù)器ip:/root/kdfs_server/web-server/public/versionFile/ #通過scp上傳到服務(wù)器 echo "上傳結(jié)束" |
關(guān)于回滾代碼的需求,打個比方你現(xiàn)在版本為1.0.10,你需要回到1.0.5,其實實現(xiàn)很簡單:
1,編譯你1.0.5的時候的game.zip,原理很簡單,你有你的代碼服務(wù)器svn或git。
2,打包res資源,你可以打包一次從1.0.0-1.0.5的資源。
這樣的話,你的代碼和資源都是1.0.5的時候的了。
總結(jié)
以上是生活随笔為你收集整理的ios lua 增量更新,更新内部代码的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: DB2 JDBC
- 下一篇: Datatable删除行的Delete和