`cocos2dx非完整` 日志模块 增量更新

在上一篇文章中,说到了"流程"的由来,以及我对流程的使用. 这一片就是对流程的应用.前一篇文章中说到了三条流程 check_log_measure, check_env_measure, check_update_measure.先来看看chenck_log_measure的源码:

 1 --小岩<[email protected]>
 2 --2015-5-28 1:29
 3 local clm = class("check_log_measure", fw.measure)
 4
 5 function clm:ctor(dispatcher, next_m)
 6     clm.super.ctor(self, dispatcher, next_m)
 7 end
 8
 9 function clm:onlaunch()
10     local appconf  = fw.classickv.new("conf/app_conf")
11     if appconf:get("syslog_enabled") then
12         local sys_logger = fw.logger.new(appconf:get("syslog_limit_level"))
13         if appconf:get("syslog_console_enabled") then
14             local sys_clogger
15             if appconf:get("syslog_console_printfun") then
16                 sys_clogger = fw.console_logger.new(appconf:get("syslog_console_printfun"), 0)
17             else
18                 sys_clogger = fw.console_logger.new(print, 0)
19             end
20             sys_logger:add_log_chanel(sys_clogger)
21         end
22
23         if appconf:get("syslog_file_enabled") then
24             fw.fileutil.make_dir(appconf:get("syslog_file_dir"))
25             local sys_flogger = fw.file_logger.new(
26                     appconf:get("syslog_file_path"),
27                     "w+b",
28                     appconf:get("syslog_file_autoflush"),
29                     0,
30                     appconf:get("syslog_file_parser")
31                 )
32             sys_logger:add_log_chanel(sys_flogger)
33             sys_logger:info("%s", "创建sylog 日志文件 " .. appconf:get("syslog_file_path") .. "!")
34         end
35
36         fw.objpool.set_object(sys_logger, string.upper("sys_logger"))
37     end
38     fw.objpool.set_object(appconf, string.upper("appconf"))
39
40     _G.sys_logger = {
41         critical = function(fmt, ...)
42             if fw.objpool.get_object(string.upper("appconf")):get("syslog_enabled") then
43                 fw.objpool.get_object(string.upper("sys_logger")):critical(fmt, ...)
44             end
45         end,
46         err = function(fmt, ...)
47             if fw.objpool.get_object(string.upper("appconf")):get("syslog_enabled") then
48                 fw.objpool.get_object(string.upper("sys_logger")):error(fmt, ...)
49             end
50         end,
51         warning = function(fmt, ...)
52             if fw.objpool.get_object(string.upper("appconf")):get("syslog_enabled") then
53                 fw.objpool.get_object(string.upper("sys_logger")):warning(fmt, ...)
54             end
55         end,
56         info = function(fmt, ...)
57             if fw.objpool.get_object(string.upper("appconf")):get("syslog_enabled") then
58                 fw.objpool.get_object(string.upper("sys_logger")):info(fmt, ...)
59             end
60         end,
61         debug = function(fmt, ...)
62             if fw.objpool.get_object(string.upper("appconf")):get("syslog_enabled") then
63                 fw.objpool.get_object(string.upper("sys_logger")):debug(fmt, ...)
64             end
65         end
66     }
67
68     self:finish()
69 end
70
71 function clm:onfinish()
72     sys_logger.info("%s", "执行check_log_measure结束,启动sys_logger成功!")
73     self:remove()
74 end
75
76 function clm:onremove()
77     sys_logger.info("%s", "移除check_log_measure!")
78 end
79
80 return clm

这边是启动sys_logger, 我在启动结束的时候添加到全局表,这样方便操作.启动sys_logger以后,方便记录app在运行时候的情况.出现了问题也方便定位.当然,不同开发者对于问题的处理方式是不一样的,我是习惯这样做.然后给出check_env_measure的源码:

 1 --小岩<[email protected]>
 2 --2015-5-27 17:53
 3 local cem = class("check_env_measure", fw.measure)
 4
 5 function cem:ctor(dispatcher, next_m)
 6     cem.super.ctor(self, dispatcher, next_m)
 7 end
 8
 9 function cem:onlaunch()
10     sys_logger.info("%s", "启动check_env_measure!")
11     local envconf = fw.classickv.new("conf/env_conf")
12     fw.fileutil.set_search_paths(envconf:get("search_paths"))
13
14     local envprofile_cls = require "game.env_profile"
15     local envprofile = envprofile_cls.new()
16
17     envprofile:set_os_code(fw.platform.arch_code)
18     envprofile:set_os_name(fw.platform.arch_name)
19     envprofile:set_os_lancode(fw.platform.arch_lancode)
20     envprofile:set_os_lanname(fw.platform.arch_lanname)
21
22     envprofile:set_checkupdate(envconf:get("check_update"))
23     envprofile:set_updatealways(envconf:get("update_always"))
24     envprofile:set_http_server_url(envconf:get("http_server_url"))
25
26     envprofile:set_network_enabled(fw.network.is_network_enabled())
27     envprofile:set_network_type(fw.network.get_network_type())
28
29     fw.objpool.set_object(envconf, string.upper("envconf"))
30     fw.objpool.set_object(envprofile, string.upper("envprofile"))
31
32     self:finish()
33 end
34
35 function cem:onfinish()
36     sys_logger.info("%s", "执行check_env_measure结束,检查env成功!")
37     self:remove()
38 end
39
40 function cem:onremove()
41     sys_logger.info("%s", "移除check_env_measure!")
42 end
43
44 return cem

check_env主要是对平台的一些处理,我模仿操作系统去初始化一些环境变量,并以oop的方式添加到env_profile里面,env_profile主要是简单数据结构,提供的只是getter/setter方法。这里有一点需要注意的是,我根据配置文件修改了searchpaths,这主要是应对我们的更新模块,如果更新了新的文件,放在writable_path下面的一个指定目录,那么应该先从这个目录搜索,然后再去搜索apk中的资源目录.其他的环境变量有一些是配置文件配置的,有一些则是网络启用相关的判断,当然,在启动的过程中,依次初始化判定网络状态是不可靠的,因为网络状态可能随时关闭,不过我有仔细想过这个问题:在这么极短的时间内,我们可以认为这个环境变量是可靠的,所以不需要要担心这个问题了.

下面是配置文件env_conf:

 1 search_paths = {
 2     fw.platform.get_writable_path() .. "res",
 3     fw.platform.get_writable_path() .. "src",
 4     "res",
 5     "src",
 6 },
 7
 8 check_update = true,
 9 update_always= true,
10
11 http_server_url = "http://192.168.1.131:8000/web/serverInfo",   

配置文件我是使用classickv模式读取的,也就是支持lua syntax的,所以我们可以直接使用之前初始化的lua模块.这样很方便.我现在主要是配置了searchpath和更新模块的相应参数.下面看一下更新流程的源码:

  1 --小岩<[email protected]>
  2 --2015-5-27 17:59
  3 local cum = class("check_update_measure", fw.measure)
  4
  5 function cum:ctor(dispatcher, next_m)
  6     cum.super.ctor(self, dispatcher, next_m)
  7     self.proxy_ = fw.proxy.new(self.dispatcher_)
  8     self.percent_ = 0
  9 end
 10
 11 function cum:onlaunch()
 12     sys_logger.info("%s", "启动check_update_measure!")
 13
 14     self.proxy_:add_listener(fw.ircu.START_IRCU_UPDATE,
 15                     function(e)
 16                         self:on_START_IRCU_UPDATE(e)
 17                     end, self:get_name() .. "tag")
 18                 :add_listener(fw.ircu.CANNOT_CONN_TO_HOST,
 19                     function(e)
 20                         self:on_CANNOT_CONN_TO_HOST(e)
 21                     end, self:get_name() .. "tag")
 22                 :add_listener(fw.ircu.IRCU_UPDATE_FINISH_SUCC,
 23                     function(e)
 24                         self:on_IRCU_UPDATE_FINISH_SUCC(e)
 25                     end, self:get_name() .. "tag")
 26                 :add_listener(fw.ircu.IRCU_UPDATE_FINISH_PART,
 27                     function(e)
 28                         self:on_IRCU_UPDATE_FINISH_PART(e)
 29                     end, self:get_name() .. "tag")
 30                 :add_listener(fw.ircu.IRCU_UNCOMPRESS_FILE,
 31                     function(e)
 32                         self:on_IRCU_UNCOMPRESS_FILE(e)
 33                     end, self:get_name() .. "tag")
 34                 :add_listener(fw.ircu.ALREADY_NEWEST,
 35                     function(e)
 36                         self:on_ALREADY_NEWEST(e)
 37                     end, self:get_name() .. "tag")
 38                 :add_listener(fw.ircu.IRCU_UPDATE_START_PART,
 39                     function(e)
 40                         self:on_IRCU_UPDATE_START_PART(e)
 41                     end, self:get_name() .. "tag")
 42
 43     local envprofile = fw.objpool.get_object(string.upper("envprofile"))
 44
 45     if not envprofile:get_network_enabled() then
 46         sys_logger.info("%s", "本地网络没开启, 不执行check_update!")
 47         self:finish()
 48         return
 49     end
 50     sys_logger.info("%s", "本地网络已开启,执行check_update!")
 51
 52     local ircu_update = fw.ircu.new(
 53                     self.dispatcher_,
 54                     envprofile:get_http_server_url(),
 55                     envprofile:get_network_type()
 56                 )
 57
 58     ircu_update:check_update()
 59 end
 60
 61 function cum:on_START_IRCU_UPDATE(e)
 62     sys_logger.info("%s", "------------------")
 63     sys_logger.info("%s", "开始执行版本更新!")
 64 end
 65
 66 function cum:on_CANNOT_CONN_TO_HOST(e)
 67     sys_logger.info("%s", e.name)
 68     sys_logger.info("%s", "执行版本更新失败!")
 69     self:finish()
 70 end
 71
 72 function cum:on_IRCU_UPDATE_FINISH_SUCC(e)
 73     sys_logger.info("%s", e.name)
 74     sys_logger.info("%s", "执行版本更新成功!")
 75     self:finish()
 76 end
 77
 78 function cum:on_IRCU_UPDATE_FINISH_PART(e)
 79     sys_logger.info("%s", e.name)
 80     sys_logger.info("%s [%s]", "成功更新版本", e.patch)
 81 end
 82
 83 function cum:on_IRCU_UNCOMPRESS_FILE(e)
 84     sys_logger.info("%s", e.name)
 85     sys_logger.info("%s, -->[%s] [%.4f]", "成功解压", e.file, e.percent)
 86     self.percent_ = self.percent_ + e.percent
 87     sys_logger.info("%s --> [%.4f]", "更新总进度", self.percent_)
 88 end
 89
 90 function cum:on_ALREADY_NEWEST(e)
 91     sys_logger.info("%s", e.name)
 92     sys_logger.info("%s", "已经是最新版本了, 不需要更新!")
 93     self:finish()
 94 end
 95
 96 function cum:on_IRCU_UPDATE_START_PART(e)
 97     sys_logger.info("%s", e.name)
 98     sys_logger.info("%s --[%s] [%.4f]", "开始下载", e.path, e.size/e.total*100)
 99 end
100
101 function cum:onfinish()
102     sys_logger.info("%s", "执行check_update_measure结束!")
103     self:remove()
104 end
105
106 function cum:onremove()
107     sys_logger.info("%s", "移除check_update_measure!")
108     self.proxy_:remove_all_listeners()
109 end
110
111
112 return cum

可以看到这个流程其实主要都是处理ircu模块抛出的事件,然后就执行流程跳转的相关操作.根据之前的env_conf配置文件中的相关参数,我们可以给项目添加debug开关,如果在开发过程中可以屏蔽掉更新模块,当然我是没有添加

这个也很容易做到.在合适的配置添加:

1 if not envprofile:get("check_update") then
2      sys_logger.info("%s", "更新模块没有开启!“)
3      self:finish()
4      return
5 end

这个还是很简单的.下面就看看ircu模块,也就是我们的增量更新模块:

  1 --小岩<[email protected]>
  2 --2015-5-28 17:44
  3 local network = require "fw.util.network"
  4 local fileutil= require "fw.util.fileutil"
  5 local encrypt = require "fw.util.encrypt"
  6 local ini     = require "fw.util.ini"
  7
  8 local ircu = class("ircu")
  9
 10 ircu.CANNOT_CONN_TO_HOST = "CANNOT_CONNECTION_TO_HOST"
 11 ircu.START_IRCU_UPDATE   = "START_IRCU_UPDATE"
 12 ircu.ALREADY_NEWEST      = "ALREADY_NEWEST"
 13 ircu.IRCU_UPDATE_FINISH_SUCC = "IRCU_UPDATE_FINISH_SUCC"
 14 ircu.IRCU_UPDATE_FINISH_PART = "IRCU_UPDATE_FINISH_PART"
 15 ircu.IRCU_UNCOMPRESS_FILE    = "IRCU_UNCOMPRESS_FILE"
 16 ircu.IRCU_UPDATE_START_PART  = "IRCU_UPDATE_START_PART"
 17
 18 ircu.TMP = fileutil.get_writable_path() .. "TMP"
 19
 20 function ircu:ctor(dispatcher, http_server_url, network_type)
 21     self.dispatcher_ = dispatcher
 22     self.http_server_url_ = http_server_url
 23     self.http_request_ = cc.XMLHttpRequest:new()
 24     self.responseType  = cc.XMLHTTPREQUEST_RESPONSE_STRING
 25     self.network_type_ = network_type
 26 end
 27
 28 function ircu:check_update()
 29     self:check_local_version_file()
 30     self:requrest_http_server_url()
 31 end
 32
 33 --检查本地版本文件
 34 function ircu:check_local_version_file()
 35     local local_version_file = fileutil.get_writable_path() .. "version"
 36     if not fileutil.is_exists(local_version_file) then
 37         fileutil.write_content(local_version_file, encrypt.encrypt("[current]\nv=0"), "wb")
 38     end
 39 end
 40
 41 --请求http服务器地址
 42 function ircu:requrest_http_server_url()
 43     self.http_request_:open("GET", self.http_server_url_)
 44     self.http_request_:registerScriptHandler(function()
 45             if self.http_request_.readyState == 4 and (self.http_request_.status >= 200 and self.http_request_.status <= 207) then
 46                 self.dispatcher_:dispatch({name = ircu.START_IRCU_UPDATE})
 47                 self:requrest_version_file(self.http_request_.response)
 48             elseif self.http_request_.readyState == 1 then
 49                 self.dispatcher_:dispatch({name = ircu.CANNOT_CONN_TO_HOST})
 50             end
 51         end)
 52     self.http_request_:send()
 53 end
 54
 55 --请求版本文件
 56 function ircu:requrest_version_file(version_file_url)
 57     self.http_request_:open("GET", version_file_url)
 58     self.http_request_:registerScriptHandler(function()
 59             if self.http_request_.readyState == 4 and (self.http_request_.status >= 200 and self.http_request_.status <= 207) then
 60                 if self.network_type_ == network.t.WIFI then
 61                     self:update_wifi_mode(self.http_request_.response)
 62                 elseif self.network_type_ == network.t.GPRS then
 63                     self:update_gprs_mode(self.http_request_.response)
 64                 end
 65             elseif self.http_request_.readyState == 1 then
 66                 self.dispatcher_:dispatch({name = ircu.CANNOT_CONN_TO_HOST})
 67             end
 68         end)
 69     self.http_request_:send()
 70 end
 71
 72 function ircu:update_wifi_mode(version_file_content)
 73     local tmp_version_file = fileutil.get_writable_path() .. "tmp_version"
 74     fileutil.write_content(tmp_version_file, encrypt.encrypt(version_file_content), "wb")
 75     self.tmp_ini = ini.new(tmp_version_file)
 76
 77     local tmp_t = {}
 78     local total_size = 0
 79
 80     for s, _ in pairs(self.tmp_ini.props_) do
 81         table.insert(tmp_t, s)
 82     end
 83
 84     --给版本排序
 85     table.sort(tmp_t, function(v1, v2)
 86             return tonumber(v1) <= tonumber(v2)
 87         end)
 88
 89     self.local_version_ini_ = ini.new(fileutil.get_writable_path() .. "version")
 90     local local_version = self.local_version_ini_:get("current", "v")
 91
 92     --检查是不是最新版本
 93     if tonumber(local_version) == tonumber(tmp_t[#tmp_t]) then
 94         self.dispatcher_:dispatch({name = ircu.ALREADY_NEWEST})
 95         return
 96     end
 97
 98     local tt = {}
 99
100     for _, v in ipairs(tmp_t) do
101         if tonumber(v) >= tonumber(local_version) then
102
103             table.insert(tt, v)
104             total_size = total_size + tonumber(self.tmp_ini:get(v, "size"))
105         end
106     end
107
108     self.new_version_t_ = tt
109     self.total_size_ = total_size
110
111     self:download_patch()
112 end
113
114 function ircu:update_gprs_mode(version_file_content)
115
116 end
117
118 function ircu:download_patch()
119     if #self.new_version_t_ == 0 then
120         self.dispatcher_:dispatch({name = ircu.IRCU_UPDATE_FINISH_SUCC})
121         fileutil.remove(ircu.TMP)
122         return
123     end
124
125     fw.ircu_helper.download(self.tmp_ini:get(self.new_version_t_[1], "path"), ircu.TMP,
126             function(code)  end,
127             function(p)
128                 if p == 100 then
129                     local patch_size = tonumber(self.tmp_ini:get(self.new_version_t_[1], "size"))
130                     local patch_percent = patch_size / self.total_size_
131                     self.dispatcher_:dispatch({
132                         name = ircu.IRCU_UPDATE_START_PART,
133                         path = self.new_version_t_[1],
134                         size = patch_size,
135                         total= self.total_size_
136                     })
137
138
139                     fw.ircu_helper.uncompress(fileutil.get_writable_path(), ircu.TMP,
140                         function(f, n, t)
141                             self.dispatcher_:dispatch({
142                                 name = ircu.IRCU_UNCOMPRESS_FILE,
143                                 file = f,
144                                 num  = n,
145                                 total   = t,
146                                 percent = 1/t*patch_percent * 100,
147                             })
148                         end,
149
150                         function()
151                             self.local_version_ini_:update("current", "v", self.new_version_t_[1])
152                             self.local_version_ini_:save()
153                             self.dispatcher_:dispatch({name = ircu.IRCU_UPDATE_FINISH_PART, patch=self.new_version_t_[1]})
154                             table.remove(self.new_version_t_, 1)
155                             self:download_patch()
156                         end)
157                 end
158             end
159         )
160 end
161
162 return ircu

这个模块使用很简单,需要注意的就是下面这几个抛出的事件就行了,我将这个模块独立封装出来,就是依靠事件来保持低耦合的.

1 ircu.CANNOT_CONN_TO_HOST = "CANNOT_CONNECTION_TO_HOST"
2 ircu.START_IRCU_UPDATE   = "START_IRCU_UPDATE"
3 ircu.ALREADY_NEWEST      = "ALREADY_NEWEST"
4 ircu.IRCU_UPDATE_FINISH_SUCC = "IRCU_UPDATE_FINISH_SUCC"
5 ircu.IRCU_UPDATE_FINISH_PART = "IRCU_UPDATE_FINISH_PART"
6 ircu.IRCU_UNCOMPRESS_FILE    = "IRCU_UNCOMPRESS_FILE"
7 ircu.IRCU_UPDATE_START_PART  = "IRCU_UPDATE_START_PART"

当然,这几个事件除了错误代码之外,其他都是用来做更新界面百分比显示,以及显示更新信息的.ircu模块中使用了我写的c++的异步下载和解压函数.也就是fw.ircu_helper.download/uncompress.下面我给出我的这个C++模块:

  1 #include "lua_ircu.h"
  2 #include "tolua_fix.h"
  3 #include "CCLuaEngine.h"
  4 #include <curl/curl.h>
  5 #include <curl/easy.h>
  6 #include <stdio.h>
  7 #include <vector>
  8 #include <thread>
  9
 10 #include <cocos2d.h>
 11
 12 #if (CC_TARGET_PLATFORM != CC_PLATFORM_WIN32) && (CC_TARGET_PLATFORM != CC_PLATFORM_WP8) && (CC_TARGET_PLATFORM != CC_PLATFORM_WINRT)
 13 #include <sys/types.h>
 14 #include <sys/stat.h>
 15 #include <errno.h>
 16 #include <dirent.h>
 17 #endif
 18
 19 #ifdef MINIZIP_FROM_SYSTEM
 20 #include <minizip/unzip.h>
 21 #else
 22 #include "unzip.h"
 23 #endif
 24
 25 #if __cplusplus
 26 extern "C" {
 27 #endif
 28 #include <lualib.h>
 29 #include <lauxlib.h>
 30 #if __cplusplus
 31 }
 32 #endif
 33
 34 #define BUFFER_SIZE    8192
 35 #define MAX_FILENAME   512
 36
 37 #define LOW_SPEED_LIMIT 1L
 38 #define LOW_SPEED_TIME 5L
 39
 40 bool createDirectory(const char *path)
 41 {
 42 #if (CC_TARGET_PLATFORM == CC_PLATFORM_WINRT) || (CC_TARGET_PLATFORM == CC_PLATFORM_WP8)
 43     return FileUtils::getInstance()->createDirectory(_storagePath.c_str());
 44 #elif (CC_TARGET_PLATFORM == CC_PLATFORM_WIN32)
 45     BOOL ret = CreateDirectoryA(path, nullptr);
 46     if (!ret && ERROR_ALREADY_EXISTS != GetLastError())
 47     {
 48         return false;
 49     }
 50     return true;
 51 #else
 52     mode_t processMask = umask(0);
 53     int ret = mkdir(path, S_IRWXU | S_IRWXG | S_IRWXO);
 54     umask(processMask);
 55     if (ret != 0 && (errno != EEXIST))
 56     {
 57         return false;
 58     }
 59
 60     return true;
 61 #endif
 62 }
 63
 64 static int
 65 lua_fw_ircu_download(lua_State* lua_state) {
 66     std::string url = lua_tostring(lua_state, 1);
 67     std::string tmppath = lua_tostring(lua_state, 2);
 68     int h1 = toluafix_ref_function(lua_state, 3, 0);
 69     int h2 = toluafix_ref_function(lua_state, 4, 0);
 70     auto t = std::thread(fw::ircu::download, url, tmppath, h1, h2);
 71     t.detach();
 72     return 0;
 73 }
 74
 75 static int
 76 lua_fw_ircu_uncompress(lua_State* lua_state) {
 77     std::string dstpath = lua_tostring(lua_state, 1);
 78     std::string filepath = lua_tostring(lua_state, 2);
 79     int h1 = toluafix_ref_function(lua_state, 3, 0);
 80     int h2 = toluafix_ref_function(lua_state, 4, 0);
 81     auto t = std::thread(fw::ircu::uncompress, dstpath, filepath, h1, h2);
 82     t.detach();
 83     return 0;
 84 }
 85
 86 namespace fw {
 87
 88     static size_t
 89     downloadPatch(void *ptr, size_t size, size_t nmemb, void *userdata) {
 90         FILE *fp = (FILE*)userdata;
 91         size_t written = fwrite(ptr, size, nmemb, fp);
 92         return written;
 93     }
 94
 95     static int
 96     downloadProgressFunc(void *ptr, double totalToDownload, double nowDownloaded, double totalToUpLoad, double nowUpLoaded) {
 97         typedef cocos2d::LuaStack LuaStack;
 98         typedef cocos2d::LuaEngine LuaEngine;
 99         LuaStack *pStack = LuaEngine::getInstance()->getLuaStack();
100         static int percent = 0;
101         int tmp = (int)(nowDownloaded / totalToDownload * 100);
102         if (percent != tmp)
103         {
104             percent = tmp;
105             cocos2d::Director::getInstance()->getScheduler()->performFunctionInCocosThread([=]{
106                 pStack->pushInt(tmp);
107                 pStack->executeFunctionByHandler((int)ptr,1);
108             });
109         }
110         return 0;
111     }
112
113
114     void
115     ircu::download(const std::string& url, const std::string& tmppath, int h1, int h2) {
116         typedef cocos2d::LuaStack LuaStack;
117         typedef cocos2d::LuaEngine LuaEngine;
118         LuaStack *pStack = LuaEngine::getInstance()->getLuaStack();
119
120         FILE *fp = fopen(tmppath.c_str(), "wb");
121         if(!fp) {
122             cocos2d::Director::getInstance()->getScheduler()->performFunctionInCocosThread([=]{
123             if (h1)
124                 pStack->pushInt(ircu::CREATE_FILE);
125                 pStack->executeFunctionByHandler(h1,1);
126             });
127             return;
128         }
129         void *_curl;
130         _curl = curl_easy_init();
131
132         if(!_curl) {
133             return;
134         }
135
136         CURLcode res;
137         curl_easy_setopt(_curl, CURLOPT_URL, url.c_str());
138         curl_easy_setopt(_curl, CURLOPT_WRITEFUNCTION, downloadPatch);
139         curl_easy_setopt(_curl, CURLOPT_WRITEDATA, fp);
140         curl_easy_setopt(_curl, CURLOPT_NOPROGRESS, false);
141         curl_easy_setopt(_curl, CURLOPT_PROGRESSFUNCTION, downloadProgressFunc);
142         curl_easy_setopt(_curl, CURLOPT_PROGRESSDATA, h2);
143         curl_easy_setopt(_curl, CURLOPT_NOSIGNAL, 1L);
144         curl_easy_setopt(_curl, CURLOPT_LOW_SPEED_LIMIT, LOW_SPEED_LIMIT);
145         curl_easy_setopt(_curl, CURLOPT_LOW_SPEED_TIME, LOW_SPEED_TIME);
146         curl_easy_setopt(_curl, CURLOPT_FOLLOWLOCATION, 1 );
147
148         res = curl_easy_perform(_curl);
149         curl_easy_cleanup(_curl);
150
151         if (res != 0 ) {
152             cocos2d::Director::getInstance()->getScheduler()->performFunctionInCocosThread([=]{
153                 pStack->pushInt(ircu::NETWORK);
154                 pStack->executeFunctionByHandler(h1,1);
155             });
156             fclose(fp);
157             return;
158         }
159
160         fclose(fp);
161         return;
162     }
163
164     void
165     ircu::uncompress(const std::string& dstpath, const std::string& filepath, int h1, int h2) {
166         typedef cocos2d::LuaStack LuaStack;
167         typedef cocos2d::LuaEngine LuaEngine;
168         LuaStack *pStack = LuaEngine::getInstance()->getLuaStack();
169
170         unzFile zipFile = cocos2d::unzOpen(filepath.c_str());
171         if (!zipFile) {
172             return;
173         }
174         cocos2d::unz_global_info global_info;
175         if (cocos2d::unzGetGlobalInfo(zipFile, &global_info) != UNZ_OK) {
176             cocos2d::unzClose(zipFile);
177             return ;
178         }
179         char readBuffer[BUFFER_SIZE];
180         uLong i;
181
182         for (i = 0; i < global_info.number_entry; ++i)
183         {
184             cocos2d::unz_file_info fileInfo;
185             char fileName[MAX_FILENAME];
186             if (cocos2d::unzGetCurrentFileInfo(zipFile,
187                                       &fileInfo,
188                                       fileName,
189                                       MAX_FILENAME,
190                                       nullptr,
191                                       0,
192                                       nullptr,
193                                       0) != UNZ_OK)
194             {
195                 cocos2d::unzClose(zipFile);
196                 return;
197             }
198
199             const std::string fullPath = dstpath + "/" + fileName;
200
201             const size_t filenameLength = strlen(fileName);
202             if (fileName[filenameLength-1] == ‘/‘)
203             {
204                 if (!createDirectory(fullPath.c_str()))
205                 {
206                     cocos2d::unzClose(zipFile);
207                     return;
208                 }
209             }
210             else
211             {
212                 const std::string fileNameStr(fileName);
213
214                 size_t startIndex=0;
215
216                 size_t index=fileNameStr.find("/",startIndex);
217
218                 while(index != std::string::npos)
219                 {
220                     const std::string dir=dstpath+fileNameStr.substr(0,index);
221
222                     FILE *out = fopen(dir.c_str(), "r");
223
224                     if(!out)
225                     {
226                         if (!createDirectory(dir.c_str()))
227                         {
228                             cocos2d::unzClose(zipFile);
229                             return;
230                         }
231                         else
232                         {
233                             //CCLOG("create directory %s",dir.c_str());
234                         }
235                     }
236                     else
237                     {
238                         fclose(out);
239                     }
240
241                     startIndex=index+1;
242
243                     index=fileNameStr.find("/",startIndex);
244
245                 }
246
247                 if (cocos2d::unzOpenCurrentFile(zipFile) != UNZ_OK)
248                 {
249                     cocos2d::unzClose(zipFile);
250                     return;
251                 }
252
253                 FILE *out = fopen(fullPath.c_str(), "wb");
254                 if (! out)
255                 {
256                     cocos2d::unzCloseCurrentFile(zipFile);
257                     cocos2d::unzClose(zipFile);
258                     return;
259                 }
260
261                 int error = UNZ_OK;
262                 do
263                 {
264                     error = cocos2d::unzReadCurrentFile(zipFile, readBuffer, BUFFER_SIZE);
265                     if (error < 0)
266                     {
267                         cocos2d::unzCloseCurrentFile(zipFile);
268                         cocos2d::unzClose(zipFile);
269                         return;
270                     }
271
272                     if (error > 0)
273                     {
274                         fwrite(readBuffer, error, 1, out);
275                     }
276                 } while(error > 0);
277
278                 fclose(out);
279
280
281             }
282             cocos2d::Director::getInstance()->getScheduler()->performFunctionInCocosThread([=](){
283                     pStack->pushString(fullPath.c_str());
284                     pStack->pushInt(i+1);
285                     pStack->pushInt(global_info.number_entry);
286                     pStack->executeFunctionByHandler(h1, 3);
287                 });
288
289             cocos2d::unzCloseCurrentFile(zipFile);
290
291             if ((i+1) < global_info.number_entry)
292             {
293                 if (cocos2d::unzGoToNextFile(zipFile) != UNZ_OK)
294                 {
295                     cocos2d::unzClose(zipFile);
296                     return;
297                 }
298             }
299         }
300         cocos2d::Director::getInstance()->getScheduler()->performFunctionInCocosThread([=](){
301             pStack->executeFunctionByHandler(h2, 0);
302         });
303
304         cocos2d::unzClose(zipFile);
305         return;
306     }
307
308     const luaL_reg g_ircu_funcs[] = {
309         {"download",   lua_fw_ircu_download},
310         {"uncompress", lua_fw_ircu_uncompress},
311         {NULL, NULL},
312     };
313
314     void
315     register_fw_ircu(lua_State* lua_state) {
316         luaL_register(lua_state, "fw.ircu_helper", g_ircu_funcs);
317     }
318 }

好吧,就到这里了.下面来说一下log. 之前我也写过log模块,但是没有文件记录模块,我这边想要的功能就是同时在终端显示,然后又可以写入到文件.好了,从这个需求出发,我到Github去浏览了一下lua的代码,最后发现了zengrong的仓库, 这里给出他的地址: github.com/zengrong.所以我就从他的log模块取出了部分修改了一下:

 1 --小岩<[email protected]>
 2 --2015-5-28 1:37
 3 local l = class("logger")
 4
 5 l.CRITICAL = 50
 6 l.ERROR    = 40
 7 l.WARNING  = 30
 8 l.INFO     = 20
 9 l.DEBUG    = 10
10 l.NOSET    =  0
11
12 function l:ctor(level, ...)
13     self:set_limit_level(level or l.NOSET)
14     self.chanels_ = {...}
15 end
16
17 function l:set_limit_level(ll)
18     self.ll_ = ll
19 end
20
21 function l:get_limit_level()
22     return self.ll_
23 end
24
25 function l:add_log_chanel(lc)
26     self.chanels_[#self.chanels_ + 1] = lc
27 end
28
29 function l:clear_chanel()
30     self.chanels_ = {}
31 end
32
33 function l:flush()
34     for __, lc in pairs(self.chanels_) do
35         lc:flush()
36     end
37 end
38
39 function l:log(level, fmt, ...)
40     if level < self.ll_ then
41         return
42     end
43     args = {...}
44     for __, lc in pairs(self.chanels_) do
45         lc:emit(level, fmt, args)
46     end
47 end
48
49 function l:critical(fmt, ...)
50     self:log(l.CRITICAL, fmt, ...)
51 end
52
53 function l:error(fmt, ...)
54     self:log(l.ERROR, fmt, ...)
55 end
56
57 function l:warning(fmt, ...)
58     self:log(l.WARNING, fmt, ...)
59 end
60
61 function l:info(fmt, ...)
62     self:log(l.INFO, fmt, ...)
63 end
64
65 function l:debug(fmt, ...)
66     self:log(l.DEBUG, fmt, ...)
67 end
68
69 return l

我这边只是给出了logger的接口,如果需要的话,请直接去看zr的github.请原谅我并没有在源码中给出引用出处.下面就是app配置Logger的文件:

 1 luagarbage_setpause_val     = 100,
 2 luagarbage_setstepmul_val    = 5000,
 3
 4 app_name = "firework 小岩<[email protected]>",
 5
 6 gl_win_size       = cc.rect(0,0,960, 640),
 7 gl_frame_size     = cc.size(1140, 650),
 8
 9 dr_size         = cc.size(480, 320),
10 dr_policy        = cc.ResolutionPolicy.FIXED_HEIGHT,
11
12 show_status        = false,
13
14 animation_interval = 1/60,
15
16 app                = "game.application",
17
18 syslog_enabled  = true,
19 syslog_console_enabled = true,
20 syslog_console_printfun = function(str)
21         print(">> syslog " .. str)
22     end,
23 syslog_limit_level = fw.logger.NOSET,
24
25 syslog_file_enabled = true,
26 syslog_file_autoflush = true,
27 syslog_file_parser = function(str)
28         return fw.encrypt.encrypt(str .. ‘\n‘)
29     end,
30 syslog_file_dir = fw.platform.get_writable_path() .. "log",
31 syslog_file_path= fw.platform.get_writable_path() .. "log/sys_log",

当然,这份是app配置文件,可以看到在logger写入函数我采用了xxtea加密方式写入,如果在开发期,可以屏蔽掉加密.直接写入就好了.当然也可以不写.好了,就到这里了.

这里是可以添加测试用例的,方法就是修改其中的app属性配置,从app.lua继承实现一个自己的test_application,然后修改配置文件,启动的时候就切换了入口.看需求添加吧,如果是有独立封装的ui类库以及其他独立功能可以考虑添加测试用例.又或者是需要添加工具,也可以在这里做考虑。

这次的文章篇幅比较的长,希望大家耐心点看. 终于我算是完成了自己的承诺了. 嘻嘻嘻嘻嘻~

时间: 2024-10-05 23:09:14

`cocos2dx非完整` 日志模块 增量更新的相关文章

`cocos2dx非完整` 游戏架构缩影 添加启动流程

这期的话题可能不是很好, 我没有想到很好的词句去更好的表达. 我一直都是很固执的认为, 同一类型的游戏,在开发做的前期工作上面其实都是可以复用的,也就是大同小异的.从游戏启动,启动日志,启动检查,检查更新,到进入游戏.这些都是那一套东西,我想把这些东西抽象一下,概括出一个叫做"流程"的概念. 我的想法就是流程是顺序执行的, 就像我喜欢画图,先做什么,然后做什么,做完什么做什么.其实从一款app启动到进入游戏,这之间的过程都是流程化进行的.还有一个很经典的例子,新手引导,其实新手引导就是

`cocos2dx非完整` 开始自己的FW模块

上一篇的文章中说到了一些个人习惯的东西以及一些简单的项目配置,这一篇文章我们来进一步完善一些东西.首先,打开编译以后的客户端执行,会看到一大堆的fileutils加载luac文件的提示,在终端显示一大堆,挺烦人的,我做的第一件事就是去修改他.这个很简单,有两种做法,第一种是在c++部分添加调用 1 cocos2d::FileUtils::getInstance()->setPopupNotify(false); 当然也可以在lua部分添加, 1 cc.FileUtils:getInstance(

`cocos2dx 非完整` UI解析模块

昨天在cocos2dx的一个群里,遇到一位匿名为x的朋友询问的问题,是关于ui的.他使用c++写了不少的ui封装节点,用来实现游戏中的各种不同效果.然后现在想改用lua,于是尝试使用最小代价去复用自己的代码.当然这个是可以做到的,相信很多人都是知道方法的.今天的这篇文章就来谈谈ui部分的处理以及个人的见解. 我们都知道,cocos2dx引擎提供了ui工具cocostudio.后来改名为cocos engine.这些就不赘述了,很多人都会使用这款工具.新版本的工具我没有使用过,不过我承认是方便了很

`cocos2dx非完整` 添加xxtea加密模块

在上一篇文章中,我已经开始着手写自己的模块,也就是fw部分.其中上一篇文章中完成的是lua部分的配置解析部分,涉及一点点平台方面的封装.这一片文章我来说明一下我是如何处理cocos2dx资源加密的.首先需要说明白的是,资源是什么?资源分为哪几类? 在选择使用lua脚本开发后,包括lua文件,游戏美术资源,游戏的配置,我都统称为游戏资源,所以我期望的加密是能够加密所有这些东西.quick提供了xxtea,而cocos2dx也在luastack中整合了xxtea,我稍微做了一些修改.主要的修改思路是

Percona Xtrabackup备份mysql (完整备份与增量备份)

Xtrabackup简介 Percona XtraBackup是开源免费的MySQL数据库热备份软件,它能对InnoDB和XtraDB存储引擎的数据库非阻塞地备份(对于MyISAM 的备份同样需要加表锁).XtraBackup支持所有的Percona Server.MySQL.MariaDB和Drizzle. XtraBackup优势 : 1.无需停止数据库进行InnoDB热备 2.增量备份MySQL 3.流压缩到传输到其它服务器 4.能比较容易地创建主从同步 5.备份MySQL时不会增大服务器

Percona Xtrabackup备份mysql全库及指定数据库(完整备份与增量备份)

原文地址:http://www.tuicool.com/articles/RZRnq2 Xtrabackup简介 Percona XtraBackup是开源免费的MySQL数据库热备份软件,它能对InnoDB和XtraDB存储引擎的数据库非阻塞地备份(对于MyISAM的备份 同样需要加表锁).XtraBackup支持所有的Percona Server.MySQL.MariaDB和Drizzle. XtraBackup优势 : 1.无需停止数据库进行InnoDB热备 2.增量备份MySQL 3.流

cocos2dx 3.1.1 在线热更新 自动更新(使用AssetsManager更新游戏资源包)

为什么要在线更新资源和脚本文件? 简单概括,如果你的游戏项目已经在google play 或Apple Store 等平台上架了,那么当你项目需要做一些活动或者修改前端的一些代码等那么你需要重新提交一个新版本给平台.但是平台审核和具体的上架时间是个不确定的.具体什么时候能上架,主要由具体的平台决定. 如果游戏项目是使用脚本语言进行编写的(如lua.js),那么一旦需要更新,则可以通过从服务器下载最新的脚本和资源,从而跳过平台直接实现在线更新.(有些平台是禁止在线更新资源方式的,但是你懂得) 闲话

python 全栈 python基础 (二十一)logging日志模块 json序列化 正则表达式(re)

一.日志模块 两种配置方式:1.config函数 2.logger #1.config函数 不能输出到屏幕 #2.logger对象 (获取别人的信息,需要两个数据流:文件流和屏幕流需要将数据从两个数据流中接收) 1.函数式简单配置 import logging logging.debug('debug message') logging.info('info message') logging.warning('warning message') logging.error('error mes

最全的增量更新入门 包含linux端和Android

简介 增量更新大量用于 Android各大应用市场.本文想做网络上从服务器到app客户端完整讲解.app用eclipse和android studio 最新版cmark开发ndk 如下图: 以前一直好奇怎么做的直到知道了bsdiff库. 地址附上: bsdiff源码地址和简介 大家可以从简介看到bsdiff是基于bzip2源码(bsdiff和bspatch一个用于生成差异文件补丁,另一个用于差异文件和旧文件合成新文件) 下载地址说明 应用市场原理说明 假设你用的是"XXX市场"点击更新