├── res
├── HelloWorld.png
├── GetFilesMD5.exe
├── UpdateTotal.ver
├── Scenes
│ ├── NoticeMsg.csb
│ └── UpdateScene.csb
├── Default
│ ├── Button_Disable.png
│ └── LoadingBarFile.png
└── UpdateFileList.ver
├── frameworks
└── readme.txt
├── src
├── cocos
│ └── readme.txt
├── packages
│ └── mvc
│ │ ├── init.lua
│ │ ├── AppBase.lua
│ │ └── ViewBase.lua
├── app
│ ├── MyApp.lua
│ ├── views
│ │ └── MainScene.lua
│ ├── md5.lua
│ └── UpdateSystem.lua
├── main.lua
└── config.lua
├── .cocos-project.json
├── .buildpath
├── .project
├── config.json
└── README.md
/res/HelloWorld.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bailufeiba/luaFilesUpdate/HEAD/res/HelloWorld.png
--------------------------------------------------------------------------------
/frameworks/readme.txt:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bailufeiba/luaFilesUpdate/HEAD/frameworks/readme.txt
--------------------------------------------------------------------------------
/res/GetFilesMD5.exe:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bailufeiba/luaFilesUpdate/HEAD/res/GetFilesMD5.exe
--------------------------------------------------------------------------------
/res/UpdateTotal.ver:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bailufeiba/luaFilesUpdate/HEAD/res/UpdateTotal.ver
--------------------------------------------------------------------------------
/src/cocos/readme.txt:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bailufeiba/luaFilesUpdate/HEAD/src/cocos/readme.txt
--------------------------------------------------------------------------------
/res/Scenes/NoticeMsg.csb:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bailufeiba/luaFilesUpdate/HEAD/res/Scenes/NoticeMsg.csb
--------------------------------------------------------------------------------
/res/Scenes/UpdateScene.csb:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bailufeiba/luaFilesUpdate/HEAD/res/Scenes/UpdateScene.csb
--------------------------------------------------------------------------------
/res/Default/Button_Disable.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bailufeiba/luaFilesUpdate/HEAD/res/Default/Button_Disable.png
--------------------------------------------------------------------------------
/res/Default/LoadingBarFile.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bailufeiba/luaFilesUpdate/HEAD/res/Default/LoadingBarFile.png
--------------------------------------------------------------------------------
/.cocos-project.json:
--------------------------------------------------------------------------------
1 | {
2 | "engine_version": "cocos2d-x-3.15",
3 | "has_native": true,
4 | "project_type": "lua"
5 | }
--------------------------------------------------------------------------------
/src/packages/mvc/init.lua:
--------------------------------------------------------------------------------
1 |
2 | local _M = {}
3 |
4 | _M.AppBase = import(".AppBase")
5 | _M.ViewBase = import(".ViewBase")
6 |
7 | return _M
8 |
--------------------------------------------------------------------------------
/src/app/MyApp.lua:
--------------------------------------------------------------------------------
1 |
2 | local MyApp = class("MyApp", cc.load("mvc").AppBase)
3 |
4 | function MyApp:onCreate()
5 | math.randomseed(os.time())
6 | end
7 |
8 | return MyApp
9 |
--------------------------------------------------------------------------------
/.buildpath:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/src/main.lua:
--------------------------------------------------------------------------------
1 | cc.FileUtils:getInstance():setPopupNotify(false)
2 |
3 | require "config"
4 | require "cocos.init"
5 |
6 | local function main()
7 | require("app.MyApp"):create():run()
8 | end
9 |
10 | local status, msg = xpcall(main, __G__TRACKBACK__)
11 | if not status then
12 | print(msg)
13 | end
14 |
--------------------------------------------------------------------------------
/.project:
--------------------------------------------------------------------------------
1 |
2 |
3 | luaFilesUpdate
4 |
5 |
6 |
7 |
8 |
9 |
10 | org.ccdt.cocosproject
11 | org.eclipse.koneki.ldt.nature
12 |
13 |
14 |
--------------------------------------------------------------------------------
/res/UpdateFileList.ver:
--------------------------------------------------------------------------------
1 | {
2 | "Default": {
3 | "Button_Disable.png": {
4 | "MD5": "FA79821349DD686F516187283BC2FB8C",
5 | "size": 1111
6 | },
7 | "LoadingBarFile.png": {
8 | "MD5": "53A230F3F1B2D64E87A742DEBFD489BD",
9 | "size": 1115
10 | }
11 | },
12 | "HelloWorld.png": {
13 | "MD5": "55EA4E952BF080F300379EC26723598B",
14 | "size": 37864
15 | },
16 | "Scenes": {
17 | "NoticeMsg.csb": {
18 | "MD5": "3BD06BA08A0EE1F50B323BFB95203FD6",
19 | "size": 2728
20 | },
21 | "UpdateScene.csb": {
22 | "MD5": "79E2E116DA20993F5F836C65F99D9211",
23 | "size": 2036
24 | }
25 | }
26 | }
--------------------------------------------------------------------------------
/src/config.lua:
--------------------------------------------------------------------------------
1 |
2 | -- 0 - disable debug info, 1 - less debug info, 2 - verbose debug info
3 | DEBUG = 2
4 |
5 | -- use framework, will disable all deprecated API, false - use legacy API
6 | CC_USE_FRAMEWORK = true
7 |
8 | -- show FPS on screen
9 | CC_SHOW_FPS = true
10 |
11 | -- disable create unexpected global variable
12 | CC_DISABLE_GLOBAL = true
13 |
14 | -- for module display
15 | CC_DESIGN_RESOLUTION = {
16 | width = 960,
17 | height = 640,
18 | autoscale = "FIXED_HEIGHT",
19 | callback = function(framesize)
20 | local ratio = framesize.width / framesize.height
21 | if ratio <= 1.34 then
22 | -- iPad 768*1024(1536*2048) is 4:3 screen
23 | return {autoscale = "FIXED_WIDTH"}
24 | end
25 | end
26 | }
27 |
--------------------------------------------------------------------------------
/src/app/views/MainScene.lua:
--------------------------------------------------------------------------------
1 | cc.exports.UpdateSystem = require("app.UpdateSystem" )
2 |
3 | local MainScene = class("MainScene", cc.load("mvc").ViewBase)
4 |
5 | function MainScene:onCreate()
6 | -- add background image
7 | display.newSprite("HelloWorld.png")
8 | :move(display.center)
9 | :addTo(self)
10 |
11 | -- add HelloWorld label
12 | cc.Label:createWithSystemFont("Hello World", "Arial", 40)
13 | :move(display.cx, display.cy + 200)
14 | :addTo(self)
15 |
16 | self:StartUpdate()
17 | end
18 |
19 | function MainScene:StartUpdate()
20 | local us = UpdateSystem:Create()
21 | us:SetUpdateProgressCallback( handler( self, self.UpdateProgress ) )
22 | us:SetUpdateDoneCallback( handler( self, self.UpdateDone ) )
23 | us:SetUpdateErrorCallback( handler( self, self.UpdateError ) )
24 | us:StartUpdate( "UpdateTotal.ver", "UpdateFileList.ver" )
25 | end
26 |
27 | function MainScene:UpdateProgress( currSize, totalSize )
28 | print( "MainScene:UpdateProgress "..currSize.."/"..totalSize )
29 | end
30 |
31 | function MainScene:UpdateDone()
32 | print( "MainScene:UpdateDone" )
33 | end
34 |
35 | function MainScene:UpdateError( err )
36 | dump( err, "MainScene:UpdateError err" )
37 | end
38 |
39 | return MainScene
40 |
--------------------------------------------------------------------------------
/config.json:
--------------------------------------------------------------------------------
1 | {
2 | "init_cfg":{
3 | "isLandscape": true,
4 | "isWindowTop": false,
5 | "name": "luaFilesUpdate",
6 | "width": 960,
7 | "height": 640,
8 | "entry": "src/main.lua",
9 | "consolePort": 6050,
10 | "uploadPort": 6060
11 | },
12 | "simulator_screen_size": [
13 | {
14 | "title": "iPhone 3Gs (480x320)",
15 | "width": 480,
16 | "height": 320
17 | },
18 | {
19 | "title": "iPhone 4 (960x640)",
20 | "width": 960,
21 | "height": 640
22 | },
23 | {
24 | "title": "iPhone 5 (1136x640)",
25 | "width": 1136,
26 | "height": 640
27 | },
28 | {
29 | "title": "iPad (1024x768)",
30 | "width": 1024,
31 | "height": 768
32 | },
33 | {
34 | "title": "iPad Retina (2048x1536)",
35 | "width": 2048,
36 | "height": 1536
37 | },
38 | {
39 | "title": "Android (800x480)",
40 | "width": 800,
41 | "height": 480
42 | },
43 | {
44 | "title": "Android (854x480)",
45 | "width": 854,
46 | "height": 480
47 | },
48 | {
49 | "title": "Android (1280x720)",
50 | "width": 1280,
51 | "height": 720
52 | },
53 | {
54 | "title": "Android (1920x1080)",
55 | "width": 1920,
56 | "height": 1080
57 | }
58 | ]
59 | }
60 |
--------------------------------------------------------------------------------
/src/packages/mvc/AppBase.lua:
--------------------------------------------------------------------------------
1 |
2 | local AppBase = class("AppBase")
3 |
4 | function AppBase:ctor(configs)
5 | self.configs_ = {
6 | viewsRoot = "app.views",
7 | modelsRoot = "app.models",
8 | defaultSceneName = "MainScene",
9 | }
10 |
11 | for k, v in pairs(configs or {}) do
12 | self.configs_[k] = v
13 | end
14 |
15 | if type(self.configs_.viewsRoot) ~= "table" then
16 | self.configs_.viewsRoot = {self.configs_.viewsRoot}
17 | end
18 | if type(self.configs_.modelsRoot) ~= "table" then
19 | self.configs_.modelsRoot = {self.configs_.modelsRoot}
20 | end
21 |
22 | if DEBUG > 1 then
23 | dump(self.configs_, "AppBase configs")
24 | end
25 |
26 | if CC_SHOW_FPS then
27 | cc.Director:getInstance():setDisplayStats(true)
28 | end
29 |
30 | -- event
31 | self:onCreate()
32 | end
33 |
34 | function AppBase:run(initSceneName)
35 | initSceneName = initSceneName or self.configs_.defaultSceneName
36 | self:enterScene(initSceneName)
37 | end
38 |
39 | function AppBase:enterScene(sceneName, transition, time, more)
40 | local view = self:createView(sceneName)
41 | view:showWithScene(transition, time, more)
42 | return view
43 | end
44 |
45 | function AppBase:createView(name)
46 | for _, root in ipairs(self.configs_.viewsRoot) do
47 | local packageName = string.format("%s.%s", root, name)
48 | local status, view = xpcall(function()
49 | return require(packageName)
50 | end, function(msg)
51 | if not string.find(msg, string.format("'%s' not found:", packageName)) then
52 | print("load view error: ", msg)
53 | end
54 | end)
55 | local t = type(view)
56 | if status and (t == "table" or t == "userdata") then
57 | return view:create(self, name)
58 | end
59 | end
60 | error(string.format("AppBase:createView() - not found view \"%s\" in search paths \"%s\"",
61 | name, table.concat(self.configs_.viewsRoot, ",")), 0)
62 | end
63 |
64 | function AppBase:onCreate()
65 | end
66 |
67 | return AppBase
68 |
--------------------------------------------------------------------------------
/src/packages/mvc/ViewBase.lua:
--------------------------------------------------------------------------------
1 |
2 | local ViewBase = class("ViewBase", cc.Node)
3 |
4 | function ViewBase:ctor(app, name)
5 | self:enableNodeEvents()
6 | self.app_ = app
7 | self.name_ = name
8 |
9 | -- check CSB resource file
10 | local res = rawget(self.class, "RESOURCE_FILENAME")
11 | if res then
12 | self:createResourceNode(res)
13 | end
14 |
15 | local binding = rawget(self.class, "RESOURCE_BINDING")
16 | if res and binding then
17 | self:createResourceBinding(binding)
18 | end
19 |
20 | if self.onCreate then self:onCreate() end
21 | end
22 |
23 | function ViewBase:getApp()
24 | return self.app_
25 | end
26 |
27 | function ViewBase:getName()
28 | return self.name_
29 | end
30 |
31 | function ViewBase:getResourceNode()
32 | return self.resourceNode_
33 | end
34 |
35 | function ViewBase:createResourceNode(resourceFilename)
36 | if self.resourceNode_ then
37 | self.resourceNode_:removeSelf()
38 | self.resourceNode_ = nil
39 | end
40 | self.resourceNode_ = cc.CSLoader:createNode(resourceFilename)
41 | assert(self.resourceNode_, string.format("ViewBase:createResourceNode() - load resouce node from file \"%s\" failed", resourceFilename))
42 | self:addChild(self.resourceNode_)
43 | end
44 |
45 | function ViewBase:createResourceBinding(binding)
46 | assert(self.resourceNode_, "ViewBase:createResourceBinding() - not load resource node")
47 | for nodeName, nodeBinding in pairs(binding) do
48 | local node = self.resourceNode_:getChildByName(nodeName)
49 | if nodeBinding.varname then
50 | self[nodeBinding.varname] = node
51 | end
52 | for _, event in ipairs(nodeBinding.events or {}) do
53 | if event.event == "touch" then
54 | node:onTouch(handler(self, self[event.method]))
55 | end
56 | end
57 | end
58 | end
59 |
60 | function ViewBase:showWithScene(transition, time, more)
61 | self:setVisible(true)
62 | local scene = display.newScene(self.name_)
63 | scene:addChild(self)
64 | display.runScene(scene, transition, time, more)
65 | return self
66 | end
67 |
68 | return ViewBase
69 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # luaFilesUpdate
2 | FilesUpdate
3 | github排版有问题,请raw阅读
4 |
5 | 项目简介
6 | 这是一个基于cocos2d-x 3.x lua版的文件更新
7 |
8 | 请把读写目录第一个加入到收索目录!
9 | FileUtils::getInstance()->addSearchPath(FileUtils::getInstance()->getWritablePath());
10 |
11 |
12 | UpdateSystem.lua 简介
13 | 更新入口
14 | function UpdateSystem:StartUpdate( localFilePath, localFileListPath )
15 | 参数 localFilePath:本地[更新总文件]的路径 (后面讲如何配置这个文件)
16 | 参数 localFileListPath:本地文件列表 (后面讲如何配置这个文件)
17 |
18 | 第一步 校验MD5
19 | 1 读取本地[更新总文件],获得更新的基本信息
20 | function UpdateSystem:LoadTotalUpdateFile()
21 |
22 | 2 请求服务器[更新总文件],获得服务器上的基本信息(服务器文件地址在本地[更新总文件]里配置的)
23 | function UpdateSystem:RequestServerTotalUpdateFile()
24 |
25 | 3 请求道服务器[更新总文件]后,对比客户端和服务器的[更新总文件]
26 | function UpdateSystem:OnServerTotalResponse()
27 | function UpdateSystem:CheckUpdate()
28 | 通过对比本地和服务器文件里的MD5值,可以知道当前需不需要更新,如果不需要更新,则更新完成
29 | 如果需要更新,并且服务器当前设置了强更,则更新失败,同时删除所有在文件列表并且在读写目录的文件
30 | 如果需要更新,并且服务器当前设置可更新,但是客户端当前版本低于服务器设置的上一个强更版本,则更新失败,同时删除所有在文件列表并且在读写目录的文件
31 | 更新流程进入第二步
32 |
33 | 第二步 对比文件列表,获得更新列表
34 | function UpdateSystem:StartCompareFileList()
35 | 1 读取本地文件列表
36 | function UpdateSystem:LoadClientFileList()
37 | 2 获取服务器的文件列表(服务器文件列表地址在服务器的[更新总文件]里配置的)
38 | function UpdateSystem:RequestServerFileList()
39 | 3 对比文件列表,获得更新列表
40 | function UpdateSystem:OnServerFileListResponse()
41 | function UpdateSystem:GetUpdateFileList()
42 | 更新流程进入第三步
43 |
44 | 第三步 根据更新列表进行文件下载
45 | 0 开始下载文件
46 | function UpdateSystem:DownloadNext()
47 | 此处会根据取消标记,结束更新流程,更新错误(更新被取消)
48 |
49 | 1 收到服务器文件后,对文件内容进行md5运算,得到文件内容的MD5值
50 | 拿这个值跟服务器文件列表里该文件的MD5值进行对比
51 | 如果不同,说明文件内容有问题,重新下载,重试三次.(如果三次都对不上,说明配置错误或者网络错误,更新失败)
52 |
53 | 2 如果文件校验通过,则储存这个文件
54 | function UpdateSystem:SaveDownloadFile( filename, data )
55 | 创建该文件的目录
56 | 给该文件名加上临时后缀 ".updtmp"
57 | 如果存在该文件名+临时后缀的文件,则删除
58 | 将该文件内容储存到 文件名+".updtmp"
59 |
60 | 3 通知外部回调函数,当前的进度
61 | function UpdateSystem:UpdateProgresses()
62 |
63 | 4 从更新列表中删除该文件,开始下载下一个文件,进入步骤0
64 |
65 | 5 所有文件下载成功
66 | function UpdateSystem:DownloadAllDone()
67 |
68 | function UpdateSystem:ChangeAllTmpToWork()
69 | 把所有更新列表里的文件并且在读写目录带有.updtmp的文件,去掉.updtmp后缀
70 |
71 | function UpdateSystem:RecordFileList()
72 | 更新本地的文件列表
73 |
74 | function UpdateSystem:RecordTotal()
75 | 更新本地[更新总文件]
76 |
77 | 6 function UpdateSystem:UpdateDone()
78 | 更新完成,通知外部回调函数
79 |
80 |
81 | 如何使用
82 | 参见:function MainScene:StartUpdate()
83 | local us = UpdateSystem:Create()
84 | us:SetUpdateProgressCallback( handler( self, self.UpdateProgress ) ) --设置进度回调函数
85 | us:SetUpdateDoneCallback( handler( self, self.UpdateDone ) ) --设置更新完成回调函数
86 | us:SetUpdateErrorCallback( handler( self, self.UpdateError ) ) --设置更新错误回调函数,传入参数参见UpdateSystem.lua顶部
87 | us:StartUpdate( "UpdateTotal.ver", "UpdateFileList.ver" ) --传入本地[更新总文件]和本地文件列表地址,开始更新流程
88 |
89 | 取消函数
90 | function UpdateSystem:Cancel()
91 | 调用该函数后,会在下载下一个文件之前停止流程,并回调更新错误回调函数,参数是ERROR_UPDATE_CALCEN
92 |
93 |
94 |
95 | 配置文件
96 | 本地[更新总文件]的配置 json格式,参见UpdateTotal.ver
97 | md5:所有文件内容md5值相加字符串的md5值
98 | currversion:当前版本号
99 | url:服务器[更新总文件]的地址
100 |
101 | --其余参数无用,保留其他参数是为了方便配置服务器[更新总文件]
102 |
103 | 服务器[更新总文件]的配置 json格式,UpdateFileList.ver
104 | md5:所有文件内容md5值相加字符串的md5值
105 | compulsive:当前版本是否需要强制更新
106 | currversion:当前版本号
107 | lastversion:上一个强制更新的版本号
108 | url:服务器的文件列表地址 --注意:此处与客户端不同
109 | desc:当前版本的更新内容
110 |
111 | 本地文件列表的配置 json格式 参见UpdateFileList.ver
112 | 具体不多说,直接看例子
113 |
114 | 服务器文件列表的配置 json格式 参见UpdateFileList.ver
115 | 参见客户端文件列表的配置
116 | 注意:加入一个元素
117 | "url":"" 指向服务器文件的下载地址的开头,比如"test.update.com/"
118 |
119 | 配置工具
120 | GetFilesMD5.exe
121 | 运行该工具会自动生成UpdateFileList.ver,并且返回一个MD5值,你需要将这个值填写到[更新总文件]的MD5值那里
122 | 该工具会忽略.exe .ver文件
123 |
124 | 特别注意:服务器UpdateFileList.ver别忘了手动加入
125 | "url":"" 指向服务器文件的下载地址的开头,比如"test.update.com/"
126 |
127 | https://github.com/bailufeiba/GetFilesMD5
128 |
129 | 如果可以的话,请保留第一行,让我装个逼 (づ ̄ 3 ̄)づ
130 | 如有问题QQ联系:2686885181
131 |
--------------------------------------------------------------------------------
/src/app/md5.lua:
--------------------------------------------------------------------------------
1 | local md5 = {
2 | _VERSION = "md5.lua 1.1.0",
3 | _DESCRIPTION = "MD5 computation in Lua (5.1-3, LuaJIT)",
4 | _URL = "https://github.com/kikito/md5.lua",
5 | _LICENSE = [[
6 | MIT LICENSE
7 |
8 | Copyright (c) 2013 Enrique García Cota + Adam Baldwin + hanzao + Equi 4 Software
9 |
10 | Permission is hereby granted, free of charge, to any person obtaining a
11 | copy of this software and associated documentation files (the
12 | "Software"), to deal in the Software without restriction, including
13 | without limitation the rights to use, copy, modify, merge, publish,
14 | distribute, sublicense, and/or sell copies of the Software, and to
15 | permit persons to whom the Software is furnished to do so, subject to
16 | the following conditions:
17 |
18 | The above copyright notice and this permission notice shall be included
19 | in all copies or substantial portions of the Software.
20 |
21 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
22 | OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
23 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
24 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
25 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
26 | TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
27 | SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
28 | ]]
29 | }
30 |
31 | -- bit lib implementions
32 |
33 | local char, byte, format, rep, sub =
34 | string.char, string.byte, string.format, string.rep, string.sub
35 | local bit_or, bit_and, bit_not, bit_xor, bit_rshift, bit_lshift
36 |
37 | local ok, bit = pcall(require, 'bit')
38 | if ok then
39 | bit_or, bit_and, bit_not, bit_xor, bit_rshift, bit_lshift = bit.bor, bit.band, bit.bnot, bit.bxor, bit.rshift, bit.lshift
40 | else
41 | ok, bit = pcall(require, 'bit32')
42 |
43 | if ok then
44 |
45 | bit_not = bit.bnot
46 |
47 | local tobit = function(n)
48 | return n <= 0x7fffffff and n or -(bit_not(n) + 1)
49 | end
50 |
51 | local normalize = function(f)
52 | return function(a,b) return tobit(f(tobit(a), tobit(b))) end
53 | end
54 |
55 | bit_or, bit_and, bit_xor = normalize(bit.bor), normalize(bit.band), normalize(bit.bxor)
56 | bit_rshift, bit_lshift = normalize(bit.rshift), normalize(bit.lshift)
57 |
58 | else
59 |
60 | local function tbl2number(tbl)
61 | local result = 0
62 | local power = 1
63 | for i = 1, #tbl do
64 | result = result + tbl[i] * power
65 | power = power * 2
66 | end
67 | return result
68 | end
69 |
70 | local function expand(t1, t2)
71 | local big, small = t1, t2
72 | if(#big < #small) then
73 | big, small = small, big
74 | end
75 | -- expand small
76 | for i = #small + 1, #big do
77 | small[i] = 0
78 | end
79 | end
80 |
81 | local to_bits -- needs to be declared before bit_not
82 |
83 | bit_not = function(n)
84 | local tbl = to_bits(n)
85 | local size = math.max(#tbl, 32)
86 | for i = 1, size do
87 | if(tbl[i] == 1) then
88 | tbl[i] = 0
89 | else
90 | tbl[i] = 1
91 | end
92 | end
93 | return tbl2number(tbl)
94 | end
95 |
96 | -- defined as local above
97 | to_bits = function (n)
98 | if(n < 0) then
99 | -- negative
100 | return to_bits(bit_not(math.abs(n)) + 1)
101 | end
102 | -- to bits table
103 | local tbl = {}
104 | local cnt = 1
105 | local last
106 | while n > 0 do
107 | last = n % 2
108 | tbl[cnt] = last
109 | n = (n-last)/2
110 | cnt = cnt + 1
111 | end
112 |
113 | return tbl
114 | end
115 |
116 | bit_or = function(m, n)
117 | local tbl_m = to_bits(m)
118 | local tbl_n = to_bits(n)
119 | expand(tbl_m, tbl_n)
120 |
121 | local tbl = {}
122 | for i = 1, #tbl_m do
123 | if(tbl_m[i]== 0 and tbl_n[i] == 0) then
124 | tbl[i] = 0
125 | else
126 | tbl[i] = 1
127 | end
128 | end
129 |
130 | return tbl2number(tbl)
131 | end
132 |
133 | bit_and = function(m, n)
134 | local tbl_m = to_bits(m)
135 | local tbl_n = to_bits(n)
136 | expand(tbl_m, tbl_n)
137 |
138 | local tbl = {}
139 | for i = 1, #tbl_m do
140 | if(tbl_m[i]== 0 or tbl_n[i] == 0) then
141 | tbl[i] = 0
142 | else
143 | tbl[i] = 1
144 | end
145 | end
146 |
147 | return tbl2number(tbl)
148 | end
149 |
150 | bit_xor = function(m, n)
151 | local tbl_m = to_bits(m)
152 | local tbl_n = to_bits(n)
153 | expand(tbl_m, tbl_n)
154 |
155 | local tbl = {}
156 | for i = 1, #tbl_m do
157 | if(tbl_m[i] ~= tbl_n[i]) then
158 | tbl[i] = 1
159 | else
160 | tbl[i] = 0
161 | end
162 | end
163 |
164 | return tbl2number(tbl)
165 | end
166 |
167 | bit_rshift = function(n, bits)
168 | local high_bit = 0
169 | if(n < 0) then
170 | -- negative
171 | n = bit_not(math.abs(n)) + 1
172 | high_bit = 0x80000000
173 | end
174 |
175 | local floor = math.floor
176 |
177 | for i=1, bits do
178 | n = n/2
179 | n = bit_or(floor(n), high_bit)
180 | end
181 | return floor(n)
182 | end
183 |
184 | bit_lshift = function(n, bits)
185 | if(n < 0) then
186 | -- negative
187 | n = bit_not(math.abs(n)) + 1
188 | end
189 |
190 | for i=1, bits do
191 | n = n*2
192 | end
193 | return bit_and(n, 0xFFFFFFFF)
194 | end
195 | end
196 | end
197 |
198 | -- convert little-endian 32-bit int to a 4-char string
199 | local function lei2str(i)
200 | local f=function (s) return char( bit_and( bit_rshift(i, s), 255)) end
201 | return f(0)..f(8)..f(16)..f(24)
202 | end
203 |
204 | -- convert raw string to big-endian int
205 | local function str2bei(s)
206 | local v=0
207 | for i=1, #s do
208 | v = v * 256 + byte(s, i)
209 | end
210 | return v
211 | end
212 |
213 | -- convert raw string to little-endian int
214 | local function str2lei(s)
215 | local v=0
216 | for i = #s,1,-1 do
217 | v = v*256 + byte(s, i)
218 | end
219 | return v
220 | end
221 |
222 | -- cut up a string in little-endian ints of given size
223 | local function cut_le_str(s,...)
224 | local o, r = 1, {}
225 | local args = {...}
226 | for i=1, #args do
227 | table.insert(r, str2lei(sub(s, o, o + args[i] - 1)))
228 | o = o + args[i]
229 | end
230 | return r
231 | end
232 |
233 | local swap = function (w) return str2bei(lei2str(w)) end
234 |
235 | -- An MD5 mplementation in Lua, requires bitlib (hacked to use LuaBit from above, ugh)
236 | -- 10/02/2001 jcw@equi4.com
237 |
238 | local CONSTS = {
239 | 0xd76aa478, 0xe8c7b756, 0x242070db, 0xc1bdceee,
240 | 0xf57c0faf, 0x4787c62a, 0xa8304613, 0xfd469501,
241 | 0x698098d8, 0x8b44f7af, 0xffff5bb1, 0x895cd7be,
242 | 0x6b901122, 0xfd987193, 0xa679438e, 0x49b40821,
243 | 0xf61e2562, 0xc040b340, 0x265e5a51, 0xe9b6c7aa,
244 | 0xd62f105d, 0x02441453, 0xd8a1e681, 0xe7d3fbc8,
245 | 0x21e1cde6, 0xc33707d6, 0xf4d50d87, 0x455a14ed,
246 | 0xa9e3e905, 0xfcefa3f8, 0x676f02d9, 0x8d2a4c8a,
247 | 0xfffa3942, 0x8771f681, 0x6d9d6122, 0xfde5380c,
248 | 0xa4beea44, 0x4bdecfa9, 0xf6bb4b60, 0xbebfbc70,
249 | 0x289b7ec6, 0xeaa127fa, 0xd4ef3085, 0x04881d05,
250 | 0xd9d4d039, 0xe6db99e5, 0x1fa27cf8, 0xc4ac5665,
251 | 0xf4292244, 0x432aff97, 0xab9423a7, 0xfc93a039,
252 | 0x655b59c3, 0x8f0ccc92, 0xffeff47d, 0x85845dd1,
253 | 0x6fa87e4f, 0xfe2ce6e0, 0xa3014314, 0x4e0811a1,
254 | 0xf7537e82, 0xbd3af235, 0x2ad7d2bb, 0xeb86d391,
255 | 0x67452301, 0xefcdab89, 0x98badcfe, 0x10325476
256 | }
257 |
258 | local f=function (x,y,z) return bit_or(bit_and(x,y),bit_and(-x-1,z)) end
259 | local g=function (x,y,z) return bit_or(bit_and(x,z),bit_and(y,-z-1)) end
260 | local h=function (x,y,z) return bit_xor(x,bit_xor(y,z)) end
261 | local i=function (x,y,z) return bit_xor(y,bit_or(x,-z-1)) end
262 | local z=function (ff,a,b,c,d,x,s,ac)
263 | a=bit_and(a+ff(b,c,d)+x+ac,0xFFFFFFFF)
264 | -- be *very* careful that left shift does not cause rounding!
265 | return bit_or(bit_lshift(bit_and(a,bit_rshift(0xFFFFFFFF,s)),s),bit_rshift(a,32-s))+b
266 | end
267 |
268 | local function transform(A,B,C,D,X)
269 | local a,b,c,d=A,B,C,D
270 | local t=CONSTS
271 |
272 | a=z(f,a,b,c,d,X[ 0], 7,t[ 1])
273 | d=z(f,d,a,b,c,X[ 1],12,t[ 2])
274 | c=z(f,c,d,a,b,X[ 2],17,t[ 3])
275 | b=z(f,b,c,d,a,X[ 3],22,t[ 4])
276 | a=z(f,a,b,c,d,X[ 4], 7,t[ 5])
277 | d=z(f,d,a,b,c,X[ 5],12,t[ 6])
278 | c=z(f,c,d,a,b,X[ 6],17,t[ 7])
279 | b=z(f,b,c,d,a,X[ 7],22,t[ 8])
280 | a=z(f,a,b,c,d,X[ 8], 7,t[ 9])
281 | d=z(f,d,a,b,c,X[ 9],12,t[10])
282 | c=z(f,c,d,a,b,X[10],17,t[11])
283 | b=z(f,b,c,d,a,X[11],22,t[12])
284 | a=z(f,a,b,c,d,X[12], 7,t[13])
285 | d=z(f,d,a,b,c,X[13],12,t[14])
286 | c=z(f,c,d,a,b,X[14],17,t[15])
287 | b=z(f,b,c,d,a,X[15],22,t[16])
288 |
289 | a=z(g,a,b,c,d,X[ 1], 5,t[17])
290 | d=z(g,d,a,b,c,X[ 6], 9,t[18])
291 | c=z(g,c,d,a,b,X[11],14,t[19])
292 | b=z(g,b,c,d,a,X[ 0],20,t[20])
293 | a=z(g,a,b,c,d,X[ 5], 5,t[21])
294 | d=z(g,d,a,b,c,X[10], 9,t[22])
295 | c=z(g,c,d,a,b,X[15],14,t[23])
296 | b=z(g,b,c,d,a,X[ 4],20,t[24])
297 | a=z(g,a,b,c,d,X[ 9], 5,t[25])
298 | d=z(g,d,a,b,c,X[14], 9,t[26])
299 | c=z(g,c,d,a,b,X[ 3],14,t[27])
300 | b=z(g,b,c,d,a,X[ 8],20,t[28])
301 | a=z(g,a,b,c,d,X[13], 5,t[29])
302 | d=z(g,d,a,b,c,X[ 2], 9,t[30])
303 | c=z(g,c,d,a,b,X[ 7],14,t[31])
304 | b=z(g,b,c,d,a,X[12],20,t[32])
305 |
306 | a=z(h,a,b,c,d,X[ 5], 4,t[33])
307 | d=z(h,d,a,b,c,X[ 8],11,t[34])
308 | c=z(h,c,d,a,b,X[11],16,t[35])
309 | b=z(h,b,c,d,a,X[14],23,t[36])
310 | a=z(h,a,b,c,d,X[ 1], 4,t[37])
311 | d=z(h,d,a,b,c,X[ 4],11,t[38])
312 | c=z(h,c,d,a,b,X[ 7],16,t[39])
313 | b=z(h,b,c,d,a,X[10],23,t[40])
314 | a=z(h,a,b,c,d,X[13], 4,t[41])
315 | d=z(h,d,a,b,c,X[ 0],11,t[42])
316 | c=z(h,c,d,a,b,X[ 3],16,t[43])
317 | b=z(h,b,c,d,a,X[ 6],23,t[44])
318 | a=z(h,a,b,c,d,X[ 9], 4,t[45])
319 | d=z(h,d,a,b,c,X[12],11,t[46])
320 | c=z(h,c,d,a,b,X[15],16,t[47])
321 | b=z(h,b,c,d,a,X[ 2],23,t[48])
322 |
323 | a=z(i,a,b,c,d,X[ 0], 6,t[49])
324 | d=z(i,d,a,b,c,X[ 7],10,t[50])
325 | c=z(i,c,d,a,b,X[14],15,t[51])
326 | b=z(i,b,c,d,a,X[ 5],21,t[52])
327 | a=z(i,a,b,c,d,X[12], 6,t[53])
328 | d=z(i,d,a,b,c,X[ 3],10,t[54])
329 | c=z(i,c,d,a,b,X[10],15,t[55])
330 | b=z(i,b,c,d,a,X[ 1],21,t[56])
331 | a=z(i,a,b,c,d,X[ 8], 6,t[57])
332 | d=z(i,d,a,b,c,X[15],10,t[58])
333 | c=z(i,c,d,a,b,X[ 6],15,t[59])
334 | b=z(i,b,c,d,a,X[13],21,t[60])
335 | a=z(i,a,b,c,d,X[ 4], 6,t[61])
336 | d=z(i,d,a,b,c,X[11],10,t[62])
337 | c=z(i,c,d,a,b,X[ 2],15,t[63])
338 | b=z(i,b,c,d,a,X[ 9],21,t[64])
339 |
340 | return bit_and(A+a,0xFFFFFFFF),bit_and(B+b,0xFFFFFFFF),
341 | bit_and(C+c,0xFFFFFFFF),bit_and(D+d,0xFFFFFFFF)
342 | end
343 |
344 | ----------------------------------------------------------------
345 |
346 | local function md5_update(self, s)
347 | self.pos = self.pos + #s
348 | s = self.buf .. s
349 | for ii = 1, #s - 63, 64 do
350 | local X = cut_le_str(sub(s,ii,ii+63),4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4)
351 | assert(#X == 16)
352 | X[0] = table.remove(X,1) -- zero based!
353 | self.a,self.b,self.c,self.d = transform(self.a,self.b,self.c,self.d,X)
354 | end
355 | self.buf = sub(s, math.floor(#s/64)*64 + 1, #s)
356 | return self
357 | end
358 |
359 | local function md5_finish(self)
360 | local msgLen = self.pos
361 | local padLen = 56 - msgLen % 64
362 |
363 | if msgLen % 64 > 56 then padLen = padLen + 64 end
364 |
365 | if padLen == 0 then padLen = 64 end
366 |
367 | local s = char(128) .. rep(char(0),padLen-1) .. lei2str(bit_and(8*msgLen, 0xFFFFFFFF)) .. lei2str(math.floor(msgLen/0x20000000))
368 | md5_update(self, s)
369 |
370 | assert(self.pos % 64 == 0)
371 | return lei2str(self.a) .. lei2str(self.b) .. lei2str(self.c) .. lei2str(self.d)
372 | end
373 |
374 | ----------------------------------------------------------------
375 |
376 | function md5.new()
377 | return { a = CONSTS[65], b = CONSTS[66], c = CONSTS[67], d = CONSTS[68],
378 | pos = 0,
379 | buf = '',
380 | update = md5_update,
381 | finish = md5_finish }
382 | end
383 |
384 | function md5.tohex(s)
385 | return format("%08x%08x%08x%08x", str2bei(sub(s, 1, 4)), str2bei(sub(s, 5, 8)), str2bei(sub(s, 9, 12)), str2bei(sub(s, 13, 16)))
386 | end
387 |
388 | function md5.sum(s)
389 | return md5.new():update(s):finish()
390 | end
391 |
392 | function md5.sumhexa(s)
393 | return md5.tohex(md5.sum(s))
394 | end
395 |
396 | return md5
--------------------------------------------------------------------------------
/src/app/UpdateSystem.lua:
--------------------------------------------------------------------------------
1 | -----author:bailufeiba 2017/5/12 2:21:11-----
2 | require( "app.md5" )
3 | local UpdateSystem = class( "UpdateSystem" )
4 |
5 | local PRINT_LOG = true --日志开关
6 |
7 | local ERROR_STARTUPDATE_PARAM = { code=1, msg="参数错误" }
8 | local ERROR_LOCALTOTALFILE_CONTENT = { code=2, msg="本地[更新总文件]内容错误" }
9 | local ERROR_SERVERTOTALFILE_CONTENT = { code=3, msg="服务器[更新总文件]内容错误" }
10 | local ERROR_NETWORK = { code=4, msg="网络错误" }
11 | local ERROR_NEEDTO_UPDATECLIENT = { code=5, msg="强制更新客户端", detail="这里会放置新版更新的描述" }
12 | local ERROR_SERVER_FILELIST_CONTENT = { code=6, msg="服务器文件列表内容错误", detail="url" }
13 | local ERROR_UPDATE_CALCEN = { code=7, msg="更新被取消" }
14 | local ERROR_DOWNLOAD_FILE_CONTENT = { code=8, msg="文件内容校验错误", detail="" }
15 | local ERROR_WRITE_FILE = { code=9, msg="存储文件失败", detail="" }
16 | local ERROR_RECORD_FILE_TO_FILELIST = { code=10, msg="记录文件到文件列表失败", detail="" }
17 | local ERROR_FILE_RENAME = { code=11, msg="文件重命名失败", detail="" }
18 | local ERROR_FILE_NOT_EXIST = { code=12, msg="文件不存在", detail="" }
19 | local ERROR_RECORD_TOTAL = { code=13, msg="记录更新总文件失败" }
20 |
21 | local DEFAULT_VERSION_SIZE = 4 --版本段位数 比如:1.0.0.0是4段数 , 注意:版本号仅支持数字
22 | local DOWNLOAD_FILE_SUFFIX = ".updtmp"
23 |
24 | function UpdateSystem:Create()
25 | return self.new(self)
26 | end
27 |
28 | function UpdateSystem:Cancel()
29 | self.bUpdateContinue = false
30 | end
31 |
32 | function UpdateSystem:SetUpdateProgressCallback( func )
33 | self.UpdateProgressCallback = func
34 | end
35 |
36 | function UpdateSystem:SetUpdateDoneCallback( func )
37 | self.UpdateDoneCallback = func
38 | end
39 |
40 | function UpdateSystem:SetUpdateErrorCallback( func )
41 | self.UpdateErrorCallback = func
42 | end
43 |
44 | --更新入口 localFilePath:本地更新文件(UpdateTotal.ver)的相对路径
45 | --localFileList:本地文件列表
46 | function UpdateSystem:StartUpdate( localFilePath, localFileListPath )
47 | self:ResetAll()
48 | self.localTotalFilePath = localFilePath
49 | self.localFileListPath = localFileListPath
50 |
51 | self:LoadTotalUpdateFile( localFilePath )
52 | self:RequestServerTotalUpdateFile()
53 | end
54 |
55 | function UpdateSystem:ResetAll()
56 |
57 | end
58 |
59 | ------------读取本地[更新总文件]------------
60 | function UpdateSystem:LoadTotalUpdateFile( localFilePath )
61 | self:PrintLog( "读取本地[更新总文件]" )
62 | self:ResetCUpdateTotal()
63 |
64 | local data = ""
65 | local path = cc.FileUtils:getInstance():getWritablePath()..localFilePath --防呆
66 | if cc.FileUtils:getInstance():isFileExist( path ) then
67 | data = cc.FileUtils:getInstance():getStringFromFile( path )
68 | else
69 | data = cc.FileUtils:getInstance():getStringFromFile( localFilePath )
70 | end
71 |
72 | if type(data) ~= "string" or data == "" or data == nil then
73 | self:ErrorUpdate( ERROR_LOCALTOTALFILE_CONTENT )
74 | return
75 | end
76 |
77 | local jsData = json.decode(data)
78 | if self:CheckLocalFileData(jsData) ~= true then
79 | self:ErrorUpdate( ERROR_LOCALTOTALFILE_CONTENT )
80 | return
81 | end
82 |
83 | self.tbCUpdateTotal.md5 = jsData.md5
84 | self.tbCUpdateTotal.currversion = jsData.currversion
85 | self.tbCUpdateTotal.compulsive = jsData.compulsive or false
86 | self.tbCUpdateTotal.url = jsData.url
87 | end
88 |
89 | function UpdateSystem:ResetCUpdateTotal()
90 | self.tbCUpdateTotal = {}
91 | self.tbCUpdateTotal.md5 = ""
92 | self.tbCUpdateTotal.currversion = "1.0.0"
93 | self.tbCUpdateTotal.compulsive = false
94 | self.tbCUpdateTotal.url = ""
95 | end
96 |
97 | function UpdateSystem:CheckLocalFileData( tb )
98 | if type(tb) ~= "table" then return false end
99 | if type(tb.md5) ~= "string" or tb.md5 == "" then return false end
100 | if type(tb.url) ~= "string" or tb.url == "" then return false end
101 | if type(tb.currversion) ~= "string" or tb.currversion == "" then return false end
102 | return true
103 | end
104 |
105 |
106 |
107 | ------------请求服务器[更新总文件]------------
108 | function UpdateSystem:RequestServerTotalUpdateFile()
109 | self:PrintLog( "正在请求服务器更新总文件" )
110 | self:ResetSUpdateTotal()
111 | self:HttpRequest( self.tbCUpdateTotal.url, handler(self, self.OnServerTotalResponse) )
112 | end
113 |
114 | function UpdateSystem:ResetSUpdateTotal()
115 | self.tbSUpdateTotal = {}
116 | self.tbSUpdateTotal.md5 = ""
117 | self.tbSUpdateTotal.compulsive = false
118 | self.tbSUpdateTotal.url = ""
119 | self.tbSUpdateTotal.desc = ""
120 | end
121 |
122 | function UpdateSystem:OnServerTotalResponse()
123 | self:PrintLog( "收到[更新总文件]的响应 状态:"..self.xhr.readyState )
124 | self.xhr:unregisterScriptHandler()
125 |
126 | if self.xhr.readyState == 4 and (self.xhr.status >= 200 and self.xhr.status < 207) then
127 | self:ResetSUpdateTotal()
128 |
129 | local jsData = json.decode( self.xhr.response )
130 | if jsData == nil then return end
131 | if self:CheckServerTotalData(jsData) ~= true then
132 | self:ErrorUpdate(ERROR_SERVERTOTALFILE_CONTENT)
133 | return
134 | end
135 |
136 | self.tbSUpdateTotal = clone( jsData )
137 | self.tbSUpdateTotal.md5 = jsData.md5
138 | self.tbSUpdateTotal.currversion = jsData.currversion
139 | self.tbSUpdateTotal.lastversion = jsData.lastversion
140 | self.tbSUpdateTotal.compulsive = jsData.compulsive
141 | self.tbSUpdateTotal.url = jsData.url
142 | self.tbSUpdateTotal.desc = jsData.desc or ""
143 |
144 | self:CheckUpdate()
145 | elseif self.xhr.readyState == 1 and self.xhr.status == 0 then
146 | self:ErrorUpdate( ERROR_NETWORK )
147 | end
148 | end
149 |
150 | function UpdateSystem:CheckServerTotalData( tb )
151 | if type(tb) ~= "table" then return false end
152 | if type(tb.md5) ~= "string" or tb.md5 == "" then return false end
153 | if type(tb.url) ~= "string" or tb.url == "" then return false end
154 | if type(tb.currversion) ~= "string" or tb.currversion == "" then return false end
155 | if type(tb.lastversion) ~= "string" or tb.lastversion == "" then return false end
156 | if type(tb.compulsive) ~= "boolean" then return false end
157 | return true
158 | end
159 |
160 | function UpdateSystem:HttpRequest( url, func )
161 | if self.xhr == nil then
162 | self.xhr = cc.XMLHttpRequest:new()
163 | self.xhr:retain()
164 | end
165 | self.xhr.timeout = 30 -- 设置超时时间
166 | self.xhr.responseType = cc.XMLHTTPREQUEST_RESPONSE_JSON
167 | self.xhr:open("GET", url )
168 | self.xhr:registerScriptHandler( func )
169 | self.xhr:send()
170 | end
171 |
172 |
173 |
174 | ------------对比本地更新总文件和服务器更新总文件------------
175 | function UpdateSystem:CheckUpdate()
176 | self:PrintLog( "对比本地MD5和服务器MD5" )
177 |
178 | --对比总更新的MD5
179 | if self.tbCUpdateTotal.md5 == self.tbSUpdateTotal.md5 then
180 | self:UpdateDone()
181 | return
182 | end
183 |
184 | --如果需要强更
185 | if self.tbSUpdateTotal.compulsive then
186 | self:PrintLog( "检测到需要强制更新" )
187 | self:RemoveAllDownloadFile()
188 | ERROR_NEEDTO_UPDATECLIENT.detail = self.tbSUpdateTotal.desc
189 | self:ErrorUpdate( ERROR_NEEDTO_UPDATECLIENT )
190 | return
191 | end
192 |
193 | --客户端版本低于上次强更版本
194 | local nv = self:CompareVersion( self.tbCUpdateTotal.currversion, self.tbSUpdateTotal.lastversion, DEFAULT_VERSION_SIZE )
195 | if nv < 0 then
196 | self:PrintLog( "检测到需要强制更新" )
197 | self:RemoveAllDownloadFile()
198 | ERROR_NEEDTO_UPDATECLIENT.detail = self.tbSUpdateTotal.desc
199 | self:ErrorUpdate( ERROR_NEEDTO_UPDATECLIENT )
200 | return
201 | end
202 |
203 | --开始对比文件列表
204 | self:StartCompareFileList()
205 | end
206 |
207 | --比较两个版本 版本格式 1.0.0.0
208 | --如果version和oldVersion相同,则返回0
209 | --如果version比oldVersion高,则返回1
210 | --如果version比oldVersion低,则返回-1
211 | function UpdateSystem:CompareVersion( version, oldVersion, len )
212 | if version == oldVersion then return 0 end
213 |
214 | if len == nil then len = 4 end
215 | local tbV = self:GetVersionTable( version, len )
216 | local tbOldV = self:GetVersionTable( oldVersion, len )
217 |
218 | for i=1,len do
219 | if tbV[i] > tbOldV[i] then
220 | return 1
221 | end
222 | end
223 |
224 | return -1
225 | end
226 |
227 | function UpdateSystem:GetVersionTable( version, size )
228 | local tb = {}
229 | local tbStr = string.split( version, "." )
230 | for i=1,size do
231 | if tbStr[i] ~= nil then
232 | local n = tonumber( tbStr[i] )
233 | if n ~= nil then
234 | table.insert( tb, n )
235 | else
236 | table.insert( tb, 0 )
237 | end
238 | else
239 | table.insert( tb, 0 )
240 | end
241 | end
242 | return tb
243 | end
244 |
245 | function UpdateSystem:StartCompareFileList()
246 | self.bUpdateContinue = true
247 | self:LoadClientFileList()
248 | self:RequestServerFileList()
249 | --self:LoadServerFileList()
250 | end
251 |
252 | function UpdateSystem:LoadClientFileList()
253 | self:PrintLog( "读取本地文件列表" )
254 | local data = ""
255 | local path = cc.FileUtils:getInstance():getWritablePath()..self.localFileListPath --防呆
256 | if cc.FileUtils:getInstance():isFileExist(path) then
257 | data = cc.FileUtils:getInstance():getStringFromFile( path )
258 | else
259 | data = cc.FileUtils:getInstance():getStringFromFile( self.localFileListPath )
260 | end
261 | self.tbCFileList = json.decode(data)
262 | end
263 |
264 | function UpdateSystem:RequestServerFileList()
265 | self:PrintLog( "正在请求服务器文件列表" )
266 | self:HttpRequest( self.tbSUpdateTotal.url, handler(self, self.OnServerFileListResponse) )
267 | end
268 |
269 | function UpdateSystem:OnServerFileListResponse()
270 | self:PrintLog( "收到文件列表的响应 状态:"..self.xhr.readyState )
271 | self.xhr:unregisterScriptHandler()
272 |
273 | if self.xhr.readyState == 4 and (self.xhr.status >= 200 and self.xhr.status < 207) then
274 | local jsData = json.decode( self.xhr.response )
275 | if self:CheckServerFileList(jsData) ~= true then
276 | self:ErrorUpdate( ERROR_SERVER_FILELIST_CONTENT )
277 | return
278 | end
279 |
280 | self.FileDownloadURL = jsData[ "url" ]
281 | jsData[ "url" ] = nil
282 |
283 | self.tbSFileList = clone(jsData)
284 | self:GetUpdateFileList()
285 | elseif self.xhr.readyState == 1 and self.xhr.status == 0 then
286 | self:ErrorUpdate( ERROR_NETWORK )
287 | end
288 | end
289 |
290 | function UpdateSystem:CheckServerFileList( tb )
291 | if type(tb) ~= "table" then return false end
292 | if type(tb["url"]) ~= "string" then return false end
293 | return true
294 | end
295 |
296 | function UpdateSystem:GetUpdateFileList()
297 | self:PrintLog( "正在对比本地文件列表和服务器文件列表" )
298 | self.tbUpdateList = {}
299 | self.CurrSize = 0
300 | self.UpdateSize = 0
301 |
302 | self:CompareList( "", self.tbSFileList, self.tbCFileList )
303 | self.tbUpdateListTmp = clone(self.tbUpdateList)
304 | -- dump( self.UpdateSize, "UpdateSize" )
305 | -- dump( self.tbUpdateList, "tbUpdateList" )
306 |
307 | self:UpdateProgresses()
308 | self:DownloadNext()
309 | end
310 |
311 | function UpdateSystem:CompareList( dir, tbSList, tbCList )
312 | if type(tbSList) ~= "table" then return end
313 | if type(tbCList) ~= "table" then return end
314 |
315 | if tbSList["MD5"] ~= nil then
316 | if tbSList["MD5"] ~= tbCList["MD5"] then
317 | self:AddFileList( dir, tbSList, self.tbUpdateList )
318 | end
319 | return
320 | end
321 |
322 | for k,tb in pairs(tbSList) do
323 | if type(tb) == "table" then
324 | local path = dir
325 | if path ~= "" then path = path .. "/" end
326 | path = path .. k
327 | if tbCList[ k ] == nil then
328 | self:AddFileList( path, tb, self.tbUpdateList )
329 | else
330 | self:CompareList( path, tb, tbCList[ k ] )
331 | end
332 | end
333 | end
334 | end
335 |
336 | function UpdateSystem:AddFileList( dir, tbList, tbUpdateList )
337 | if type(tbList) ~= "table" then return end
338 | if type(tbUpdateList) ~= "table" then return end
339 |
340 | if tbList["MD5"] ~= nil then
341 | local t = {}
342 | t.file = dir
343 | t.md5 = tbList["MD5"] or ""
344 | t.size = tbList[ "size" ] or 0
345 | table.insert( tbUpdateList, t )
346 | if self.UpdateSize == nil then self.UpdateSize = 0 end
347 | self.UpdateSize = self.UpdateSize + t.size
348 | return
349 | end
350 |
351 | for k,tb in pairs( tbList ) do
352 | if type(tb) == "table" then
353 | local path = dir
354 | if path ~= "" then path = path .. "/" end
355 | path = path .. k
356 | self:AddFileList( path, tb, tbUpdateList )
357 | end
358 | end
359 | end
360 |
361 |
362 |
363 | ------------下载文件------------
364 | function UpdateSystem:DownloadNext()
365 | if self.bUpdateContinue ~= true then
366 | self:ErrorUpdate( ERROR_UPDATE_CALCEN )
367 | return
368 | end
369 |
370 | if table.nums( self.tbUpdateList ) <= 0 then
371 | local err = self:DownloadAllDone()
372 | if type(err) == "nil" then
373 | self:UpdateDone()
374 | end
375 | else
376 | local url = self.FileDownloadURL..self.tbUpdateList[1].file
377 | self:HttpRequest( url, handler(self, self.OnServerFileResponse) )
378 | end
379 | end
380 |
381 | function UpdateSystem:OnServerFileResponse()
382 | self:PrintLog( "收到File的响应 状态:"..self.xhr.readyState )
383 | self.xhr:unregisterScriptHandler()
384 |
385 | if self.xhr.readyState == 1 and self.xhr.status == 0 then
386 | self:ErrorUpdate( ERROR_NETWORK )
387 | return
388 | end
389 |
390 | if self.xhr.readyState == 4 and (self.xhr.status >= 200 and self.xhr.status < 207) then
391 | self:PrintLog( "开始校验文件内容" )
392 | local md5code = string.upper( md5.sumhexa( self.xhr.response ) )
393 | if md5code ~= string.upper(self.tbUpdateList[1].md5) then
394 | if not self.nFileReDownload then self.nFileReDownload = 0 end
395 | if self.nFileReDownload < 3 then
396 | self:PrintLog( "错误的文件内容,重新下载该文件" )
397 | self.nFileReDownload = self.nFileReDownload + 1
398 | self:DownloadNext()
399 | return
400 | end
401 |
402 | self:PrintLog( "文件内容错误,停止下载" )
403 | ERROR_DOWNLOAD_FILE_CONTENT.detail = self.tbUpdateList[1].file
404 | self:ErrorUpdate( ERROR_DOWNLOAD_FILE_CONTENT )
405 | return
406 | end
407 |
408 | self:PrintLog( "文件内容正确" )
409 | self.nFileReDownload = 0
410 | self:SaveDownloadFile( self.tbUpdateList[1].file, self.xhr.response )
411 |
412 | self.CurrSize = self.CurrSize + string.len( self.xhr.response )
413 | self:UpdateProgresses()
414 |
415 | table.remove( self.tbUpdateList, 1 )
416 | self:DownloadNext()
417 | end
418 | end
419 |
420 | function UpdateSystem:DownloadAllDone()
421 | local err = self:ChangeAllTmpToWork()
422 | if err ~= nil then return err end
423 |
424 | err = self:RecordFileList()
425 | if err ~= nil then return err end
426 |
427 | err = self:RecordTotal()
428 | if err ~= nil then return err end
429 |
430 | return nil
431 | end
432 |
433 | function UpdateSystem:ChangeAllTmpToWork()
434 | for k,t in pairs( self.tbUpdateListTmp ) do
435 | self:ChangeOneTmpToWork( t.file )
436 | end
437 | end
438 |
439 | function UpdateSystem:ChangeOneTmpToWork( filename )
440 | local workname = cc.FileUtils:getInstance():getWritablePath()..filename
441 | local tmpname = workname .. DOWNLOAD_FILE_SUFFIX
442 |
443 | if cc.FileUtils:getInstance():isFileExist( tmpname ) then
444 | if cc.FileUtils:getInstance():isFileExist( workname ) then
445 | cc.FileUtils:getInstance():removeFile( workname )
446 | end
447 |
448 | local dir = self:GetFileDirectory( tmpname ).."/"
449 | local start = string.len(dir)+1
450 | local oldname = string.sub( tmpname, start )
451 | local name = string.sub( workname, start )
452 | cc.FileUtils:getInstance():renameFile( dir, oldname, name )
453 | return nil
454 | else
455 | ERROR_FILE_NOT_EXIST.detail = tmpname
456 | return ERROR_FILE_NOT_EXIST
457 | end
458 | end
459 |
460 | function UpdateSystem:RecordFileList()
461 | for k,tb in pairs( self.tbUpdateListTmp ) do
462 | if type(tb) == "table" then
463 | local tbList = self.tbCFileList
464 | local idx = string.lastindexof( tb.file, "/" )
465 | if idx then
466 | local dir = self:GetFileDirectory( tb.file )
467 | local tbDir = string.split( dir, "/" )
468 | for k,d in pairs( tbDir ) do
469 | if tbList[d] == nil then
470 | tbList[d] = {}
471 | end
472 | tbList = tbList[d]
473 | end
474 | end
475 |
476 | local tf = {}
477 | tf["MD5"] = tb.md5
478 | tf["size"] = tb.size
479 | local name = tb.file
480 | if idx then name = string.sub( tb.file, idx+1 ) end
481 | tbList[name] = tf
482 | end
483 | end
484 |
485 | local path = cc.FileUtils:getInstance():getWritablePath() .. self.localFileListPath
486 | local data = json.encode(self.tbCFileList)
487 | if self:WriteFile( path, data ) == false then
488 | self:PrintLog( "文件列表更新失败" )
489 | ERROR_RECORD_FILE_TO_FILELIST.detail = "filelist"
490 | return ERROR_RECORD_FILE_TO_FILELIST
491 | end
492 | self:PrintLog( "文件列表更新成功" )
493 | return nil
494 | end
495 |
496 | function UpdateSystem:RecordTotal()
497 | local path = cc.FileUtils:getInstance():getWritablePath()..self.localTotalFilePath
498 | local tb = clone(self.tbSUpdateTotal)
499 | tb.url = self.tbCUpdateTotal.url
500 | if self:WriteFile( path, json.encode( tb ) ) ~= true then
501 | self:PrintLog( "[更新总文件]修改失败" )
502 | ERROR_RECORD_TOTAL.detail = path
503 | return ERROR_RECORD_TOTAL
504 | end
505 |
506 | self:PrintLog( "[更新总文件]修改成功" )
507 | return nil
508 | end
509 |
510 | function UpdateSystem:WriteFile( path, data )
511 | local file = io.open( path, "wb" )
512 | if file then
513 | io.writefile( path, data )
514 | io.flush()
515 | io.close( file )
516 | return true
517 | else
518 | return false
519 | end
520 | end
521 |
522 | function UpdateSystem:SaveDownloadFile( filename, data )
523 | local dir = self:GetFileDirectory( filename )
524 | if dir and dir ~= "" then
525 | local path = cc.FileUtils:getInstance():getWritablePath()
526 | cc.FileUtils:getInstance():createDirectory( path..dir )
527 | end
528 |
529 | local path = cc.FileUtils:getInstance():getWritablePath()..filename..DOWNLOAD_FILE_SUFFIX
530 | if cc.FileUtils:getInstance():isFileExist( path ) then
531 | self:PrintLog( "删除本地存在的文件" )
532 | cc.FileUtils:getInstance():removeFile( path )
533 | end
534 |
535 | if self:WriteFile(path,data) == false then
536 | ERROR_WRITE_FILE.detail = filename
537 | self:ErrorUpdate( ERROR_WRITE_FILE )
538 | return
539 | end
540 |
541 | self:PrintLog( "储存下载文件成功" )
542 | end
543 |
544 | function UpdateSystem:GetFileDirectory( filename )
545 | local idx = string.lastindexof( filename, "/" )
546 | if idx ~= nil then
547 | return string.sub( filename, 0, idx-1 )
548 | end
549 | return ""
550 | end
551 |
552 |
553 |
554 | ------------删除所有下载的文件------------
555 | function UpdateSystem:RemoveAllDownloadFile()
556 | self:PrintLog( "删除所有下载的文件" )
557 | self:LoadDownloadFileList()
558 | self:RemoveDownloadFile()
559 | self:RemoveUpdateFile()
560 | end
561 |
562 | function UpdateSystem:LoadDownloadFileList()
563 | self:PrintLog( "读取下载的文件列表" )
564 | local data = ""
565 | local path = cc.FileUtils:getInstance():getWritablePath()..self.localFileListPath
566 | if cc.FileUtils:getInstance():isFileExist(path) then
567 | data = cc.FileUtils:getInstance():getStringFromFile( path )
568 | local tb = json.decode(data)
569 |
570 | self.tbRemoveFileList = {}
571 | self:AddFileList( "", tb, self.tbRemoveFileList )
572 | end
573 | end
574 |
575 | function UpdateSystem:RemoveDownloadFile()
576 | for k,tb in pairs( self.tbRemoveFileList ) do
577 | if type(tb.file) == "string" then
578 | local path = cc.FileUtils:getInstance():getWritablePath()..tb.file
579 | if cc.FileUtils:getInstance():isFileExist( path ) then
580 | cc.FileUtils:getInstance():removeFile( path )
581 | end
582 | end
583 | end
584 | end
585 |
586 | function UpdateSystem:RemoveUpdateFile()
587 | local path = cc.FileUtils:getInstance():getWritablePath()..self.localFileListPath
588 | if cc.FileUtils:getInstance():isFileExist( path ) then
589 | cc.FileUtils:getInstance():removeFile( path )
590 | end
591 |
592 | path = cc.FileUtils:getInstance():getWritablePath()..self.localTotalFilePath
593 | if cc.FileUtils:getInstance():isFileExist( path ) then
594 | cc.FileUtils:getInstance():removeFile( path )
595 | end
596 | end
597 |
598 |
599 |
600 |
601 | function UpdateSystem:UpdateProgresses()
602 | if self.UpdateProgressCallback then
603 | self.UpdateProgressCallback( self.CurrSize, self.UpdateSize )
604 | end
605 | end
606 |
607 | function UpdateSystem:UpdateDone()
608 | self:PrintLog( "更新完成" )
609 | self:ExitUpdate()
610 |
611 | if self.UpdateDoneCallback then
612 | self.UpdateDoneCallback()
613 | end
614 | end
615 |
616 | function UpdateSystem:ErrorUpdate( err )
617 | self:PrintLog( err.msg )
618 | self.bUpdateContinue = false
619 | self:ExitUpdate()
620 |
621 | if self.UpdateErrorCallback then
622 | self.UpdateErrorCallback( err )
623 | end
624 | end
625 |
626 | function UpdateSystem:ExitUpdate()
627 | if self.xhr ~= nil then
628 | self.xhr:release()
629 | self.xhr = nil
630 | end
631 | end
632 |
633 | function UpdateSystem:PrintLog( txt )
634 | if PRINT_LOG ~= true then return end
635 | if type(txt) ~= "string" then return end
636 |
637 | if type(DEBUG) == "number" and DEBUG > 0 then
638 | print( "[UpdateSystem]:\t".. txt )
639 | end
640 | end
641 |
642 | return UpdateSystem
--------------------------------------------------------------------------------