Cocos2d-x 3.2版本以上LUA脚本热更新(动态更新)解决方案


博客地址:http://blog.csdn.net/qq446569365

能够进行热更新,是Lua脚本的最大优势,通过热更新能够解决诸多问题。例如App Store的审核,不用每次都提交版本,等待审核了,直接通过热更新更新游戏逻辑和素材即可。只有在进行大版本更新(修改底层C++部分)时候才需要重新提交审核。

官方的LuaTest中提供了一个热更新的简单例程,但是实际运行却没有效果。具体原因出在官方更新的连接上,官方的写法“貌似”不支持https的链接,但是他却用了一个 https://raw.github.com/samuele3hu/AssetsManagerTest/master/version 这样的链接,自然就导致更新失败了。

这里我们对lua的这个热更新的代码进行一下简单的分析,并提供一些我们在更新时候遇见的问题的解决方法,也许非常笨的方法,希望大家不要耻笑。

首先AssetsManager.cpp文件存储在,extensions目录下的Assets-Manager目录中,在cpp文件中,封装了整套热更新的功能。

首先在:

checkUpdate 成员方法中,进行了版本的比对

  1. bool AssetsManager::checkUpdate()  
  2. {  
  3.     if (_versionFileUrl.size() == 0) return false;  
  4.       
  5.     _curl = curl_easy_init();  
  6.     if (! _curl)  
  7.     {  
  8.         CCLOG("can not init curl");  
  9.         return false;  
  10.     }  
  11.       
  12.     // Clear _version before assign new value.  
  13.     _version.clear();  
  14.       
  15.     CURLcode res;  
  16.     curl_easy_setopt(_curl, CURLOPT_URL, _versionFileUrl.c_str());  
  17.     curl_easy_setopt(_curl, CURLOPT_SSL_VERIFYPEER, 0L);  
  18.     curl_easy_setopt(_curl, CURLOPT_WRITEFUNCTION, getVersionCode);  
  19.     curl_easy_setopt(_curl, CURLOPT_WRITEDATA, &_version);  
  20.     if (_connectionTimeout) curl_easy_setopt(_curl, CURLOPT_CONNECTTIMEOUT, _connectionTimeout);  
  21.     curl_easy_setopt(_curl, CURLOPT_NOSIGNAL, 1L);  
  22.     curl_easy_setopt(_curl, CURLOPT_LOW_SPEED_LIMIT, LOW_SPEED_LIMIT);  
  23.     curl_easy_setopt(_curl, CURLOPT_LOW_SPEED_TIME, LOW_SPEED_TIME);  
  24.     res = curl_easy_perform(_curl);  
  25.       
  26.     if (res != 0)  
  27.     {  
  28.         Director::getInstance()->getScheduler()->performFunctionInCocosThread([&, this]{  
  29.             if (this->_delegate)  
  30.                 this->_delegate->onError(ErrorCode::NETWORK);  
  31.         });  
  32.         CCLOG("can not get version file content, error code is %d", res);  
  33.         curl_easy_cleanup(_curl);  
  34.         return false;  
  35.     }  
  36.       
  37.     string recordedVersion = UserDefault::getInstance()->getStringForKey(keyOfVersion().c_str());//首先获取当前版本  
  38.     if (recordedVersion == _version)//将当前版本和服务器版本进行比对 如果不一样  
  39.     {  
  40.         Director::getInstance()->getScheduler()->performFunctionInCocosThread([&, this]{  
  41.             if (this->_delegate)  
  42.                 this->_delegate->onError(ErrorCode::NO_NEW_VERSION);  
  43.         });  
  44.         CCLOG("there is not new version");  
  45.         // Set resource search path.  
  46.         setSearchPath();//设置文件的搜索路径,会优先搜索热更新的目录,以达到运行最新更新下来的代码  
  47.         return false;  
  48.     }  
  49.       
  50.     CCLOG("there is a new version: %s", _version.c_str());  
  51.       
  52.     return true;  
  53. }<span style="font-size:18px;"><span style="font-family:Arial;color:#333333;"><span style="line-height: 26px;">  
  54. </span></span></span>  
bool AssetsManager::checkUpdate()
{
    if (_versionFileUrl.size() == 0) return false;
    
    _curl = curl_easy_init();
    if (! _curl)
    {
        CCLOG("can not init curl");
        return false;
    }
    
    // Clear _version before assign new value.
    _version.clear();
    
    CURLcode res;
    curl_easy_setopt(_curl, CURLOPT_URL, _versionFileUrl.c_str());
    curl_easy_setopt(_curl, CURLOPT_SSL_VERIFYPEER, 0L);
    curl_easy_setopt(_curl, CURLOPT_WRITEFUNCTION, getVersionCode);
    curl_easy_setopt(_curl, CURLOPT_WRITEDATA, &_version);
    if (_connectionTimeout) curl_easy_setopt(_curl, CURLOPT_CONNECTTIMEOUT, _connectionTimeout);
    curl_easy_setopt(_curl, CURLOPT_NOSIGNAL, 1L);
    curl_easy_setopt(_curl, CURLOPT_LOW_SPEED_LIMIT, LOW_SPEED_LIMIT);
    curl_easy_setopt(_curl, CURLOPT_LOW_SPEED_TIME, LOW_SPEED_TIME);
    res = curl_easy_perform(_curl);
    
    if (res != 0)
    {
        Director::getInstance()->getScheduler()->performFunctionInCocosThread([&, this]{
            if (this->_delegate)
                this->_delegate->onError(ErrorCode::NETWORK);
        });
        CCLOG("can not get version file content, error code is %d", res);
        curl_easy_cleanup(_curl);
        return false;
    }
    
    string recordedVersion = UserDefault::getInstance()->getStringForKey(keyOfVersion().c_str());//首先获取当前版本
    if (recordedVersion == _version)//将当前版本和服务器版本进行比对 如果不一样
    {
        Director::getInstance()->getScheduler()->performFunctionInCocosThread([&, this]{
            if (this->_delegate)
                this->_delegate->onError(ErrorCode::NO_NEW_VERSION);
        });
        CCLOG("there is not new version");
        // Set resource search path.
        setSearchPath();//设置文件的搜索路径,会优先搜索热更新的目录,以达到运行最新更新下来的代码
        return false;
    }
    
    CCLOG("there is a new version: %s", _version.c_str());
    
    return true;
}<span style="font-size:18px;"><span style="font-family:Arial;color:#333333;"><span style="line-height: 26px;">
</span></span></span>
游戏版本通过UserDefault进行存储。他的key是通过将URL进行hash运算,然后拼接到关键字后边。代码如下:

  1. // Multiple key names  
  2. static std::string keyWithHash( const char* prefix, const std::string& url )//将更新网址转化为hash并和更新 当前版本的 标识文字连接到一起  
  3. {  
  4.     char buf[256];  
  5.     sprintf(buf,"%s%zd",prefix,std::hash<std::string>()(url));  
  6.     return buf;  
  7. }  
  8.   
  9. // hashed version  
  10. std::string AssetsManager::keyOfVersion() const//获取用于存储当前版本的key  
  11. {  
  12.     return keyWithHash(KEY_OF_VERSION,_packageUrl);  
  13. }  
  14.   
  15. // hashed version  
  16. std::string AssetsManager::keyOfDownloadedVersion() const//获取用于存储当前已经下载的版本的key  
  17. {  
  18.     return keyWithHash(KEY_OF_DOWNLOADED_VERSION,_packageUrl);  
  19. }  
// Multiple key names
static std::string keyWithHash( const char* prefix, const std::string& url )//将更新网址转化为hash并和更新 当前版本的 标识文字连接到一起
{
    char buf[256];
    sprintf(buf,"%s%zd",prefix,std::hash<std::string>()(url));
    return buf;
}

// hashed version
std::string AssetsManager::keyOfVersion() const//获取用于存储当前版本的key
{
    return keyWithHash(KEY_OF_VERSION,_packageUrl);
}

// hashed version
std::string AssetsManager::keyOfDownloadedVersion() const//获取用于存储当前已经下载的版本的key
{
    return keyWithHash(KEY_OF_DOWNLOADED_VERSION,_packageUrl);
}
通过这种方式可以根据下载地址分别存储版本号。

版本检车结束后,就开始进行下载了。下载和解压缩的方法是:downloadAndUncompress

  1. void AssetsManager::downloadAndUncompress()  
  2. {  
  3.     do  
  4.     {  
  5.         if (_downloadedVersion != _version)//判断当前下载的数据包的版本是否和最新版一样  
  6.         {  
  7.             if (! downLoad()) break;  
  8.               
  9.             Director::getInstance()->getScheduler()->performFunctionInCocosThread([&, this]{  
  10.                 UserDefault::getInstance()->setStringForKey(this->keyOfDownloadedVersion().c_str(),  
  11.                                                             this->_version.c_str());  
  12.                 UserDefault::getInstance()->flush();  
  13.             });  
  14.         }  
  15.           
  16.         // Uncompress zip file.  
  17.         if (! uncompress())//对下载的更新压缩包进行解压缩  
  18.         {  
  19.             Director::getInstance()->getScheduler()->performFunctionInCocosThread([&, this]{  
  20.                 if (this->_delegate)  
  21.                     this->_delegate->onError(ErrorCode::UNCOMPRESS);  
  22.             });  
  23.             break;  
  24.         }  
  25.           
  26.         Director::getInstance()->getScheduler()->performFunctionInCocosThread([&, this] {  
  27.               
  28.             // Record new version code.  
  29.             UserDefault::getInstance()->setStringForKey(this->keyOfVersion().c_str(), this->_version.c_str());  
  30.             UserDefault::getInstance()->setStringForKey("GameVersionStr"this->_version.c_str());//这行是我自己添加的。由于当前游戏版本的保存key 是通过hash等一些列运算所得出的,所以比较难获取,想了个比较方便的办法就是单独保存一下…………这样就可以方便的在游戏上显示当前版本了  
  31.               
  32.             // Unrecord downloaded version code.  
  33.             UserDefault::getInstance()->setStringForKey(this->keyOfDownloadedVersion().c_str(), "");  
  34.             UserDefault::getInstance()->flush();  
  35.               
  36.             // Set resource search path.  
  37.             this->setSearchPath();  
  38.               
  39.             // Delete unloaded zip file.  
  40.             string zipfileName = this->_storagePath + TEMP_PACKAGE_FILE_NAME;  
  41.             if (remove(zipfileName.c_str()) != 0)  
  42.             {  
  43.                 CCLOG("can not remove downloaded zip file %s", zipfileName.c_str());  
  44.             }  
  45.               
  46.             if (this->_delegate) this->_delegate->onSuccess();  
  47.         });  
  48.          
  49.     } while (0);  
  50.       
  51.     _isDownloading = false;  
  52. }  
void AssetsManager::downloadAndUncompress()
{
    do
    {
        if (_downloadedVersion != _version)//判断当前下载的数据包的版本是否和最新版一样
        {
            if (! downLoad()) break;
            
            Director::getInstance()->getScheduler()->performFunctionInCocosThread([&, this]{
                UserDefault::getInstance()->setStringForKey(this->keyOfDownloadedVersion().c_str(),
                                                            this->_version.c_str());
                UserDefault::getInstance()->flush();
            });
        }
        
        // Uncompress zip file.
        if (! uncompress())//对下载的更新压缩包进行解压缩
        {
            Director::getInstance()->getScheduler()->performFunctionInCocosThread([&, this]{
                if (this->_delegate)
                    this->_delegate->onError(ErrorCode::UNCOMPRESS);
            });
            break;
        }
        
        Director::getInstance()->getScheduler()->performFunctionInCocosThread([&, this] {
            
            // Record new version code.
            UserDefault::getInstance()->setStringForKey(this->keyOfVersion().c_str(), this->_version.c_str());
            UserDefault::getInstance()->setStringForKey("GameVersionStr", this->_version.c_str());//这行是我自己添加的。由于当前游戏版本的保存key 是通过hash等一些列运算所得出的,所以比较难获取,想了个比较方便的办法就是单独保存一下…………这样就可以方便的在游戏上显示当前版本了
            
            // Unrecord downloaded version code.
            UserDefault::getInstance()->setStringForKey(this->keyOfDownloadedVersion().c_str(), "");
            UserDefault::getInstance()->flush();
            
            // Set resource search path.
            this->setSearchPath();
            
            // Delete unloaded zip file.
            string zipfileName = this->_storagePath + TEMP_PACKAGE_FILE_NAME;
            if (remove(zipfileName.c_str()) != 0)
            {
                CCLOG("can not remove downloaded zip file %s", zipfileName.c_str());
            }
            
            if (this->_delegate) this->_delegate->onSuccess();
        });
       
    } while (0);
    
    _isDownloading = false;
}


以上都是废话,这里开始才是重点!

说了那么多,到底应该如何使用呢?

使用起来很简单,几句话

  1.     <span style="white-space:pre">local function onError(errorCode)  
  2.   
  3.     end  
  4.   
  5.   
  6.     local function onProgress( percent )  
  7.     end  
  8.   
  9.   
  10.     local function onSuccess()  
  11.     end  
  12.   
  13.     self.assetsManager =cc.AssetsManager:new(FileURL,  
  14.                                    "http://dzpk.57wan.cn/dzpk_logic/version.do",  
  15.                                    pathToSave)  
  16.   
  17.   
  18.     self.assetsManager:retain()  
  19.     self.assetsManager:setDelegate(onError, cc.ASSETSMANAGER_PROTOCOL_ERROR )  
  20.     self.assetsManager:setDelegate(onProgress, cc.ASSETSMANAGER_PROTOCOL_PROGRESS)  
  21.     self.assetsManager:setDelegate(onSuccess, cc.ASSETSMANAGER_PROTOCOL_SUCCESS )  
  22.     self.assetsManager:setConnectionTimeout(3)  
  23. <span style="white-space:pre">    </span>self.assetsManager:update()</span>self.assetsManager =cc.AssetsManager:new("http://www.xxx.com/updata.zip",  
  24.                                    "http://dzpk.xxx.com/version.php",  
  25.                                    cc.FileUtils:getInstance():getWritablePath())  
  26.   
  27.     <span style="white-space:pre">    </span>self.assetsManager:retain()  
  28.     <span style="white-space:pre">    </span>self.assetsManager:setDelegate(onError, cc.ASSETSMANAGER_PROTOCOL_ERROR )  
  29.     <span style="white-space:pre">    </span>self.assetsManager:setDelegate(onProgress, cc.ASSETSMANAGER_PROTOCOL_PROGRESS)  
  30.     <span style="white-space:pre">    </span>self.assetsManager:setDelegate(onSuccess, cc.ASSETSMANAGER_PROTOCOL_SUCCESS )  
  31.     <span style="white-space:pre">    </span>self.assetsManager:setConnectionTimeout(3)  
  32. <span style="white-space:pre">    </span>self.assetsManager:update()  
    <span style="white-space:pre">local function onError(errorCode)

    end


    local function onProgress( percent )
    end


    local function onSuccess()
    end

    self.assetsManager =cc.AssetsManager:new(FileURL,
                                   "http://dzpk.57wan.cn/dzpk_logic/version.do",
                                   pathToSave)


    self.assetsManager:retain()
    self.assetsManager:setDelegate(onError, cc.ASSETSMANAGER_PROTOCOL_ERROR )
    self.assetsManager:setDelegate(onProgress, cc.ASSETSMANAGER_PROTOCOL_PROGRESS)
    self.assetsManager:setDelegate(onSuccess, cc.ASSETSMANAGER_PROTOCOL_SUCCESS )
    self.assetsManager:setConnectionTimeout(3)
<span style="white-space:pre">	</span>self.assetsManager:update()</span>self.assetsManager =cc.AssetsManager:new("http://www.xxx.com/updata.zip",
                                   "http://dzpk.xxx.com/version.php",
                                   cc.FileUtils:getInstance():getWritablePath())

    <span style="white-space:pre">	</span>self.assetsManager:retain()
    <span style="white-space:pre">	</span>self.assetsManager:setDelegate(onError, cc.ASSETSMANAGER_PROTOCOL_ERROR )
    <span style="white-space:pre">	</span>self.assetsManager:setDelegate(onProgress, cc.ASSETSMANAGER_PROTOCOL_PROGRESS)
    <span style="white-space:pre">	</span>self.assetsManager:setDelegate(onSuccess, cc.ASSETSMANAGER_PROTOCOL_SUCCESS )
    <span style="white-space:pre">	</span>self.assetsManager:setConnectionTimeout(3)
<span style="white-space:pre">	</span>self.assetsManager:update()
但是仅仅是这样,是不足以满足商业项目需求的,我们需要对其进行一些简单的修改。

首先程序一上来,我们需要先判断一下版本是否需要更新,版本跨度有多大,我所用的方法是一上来,先访问服务器,提交当前本地版本号。由服务器根据我当前版本号判断版本跨度,同时返回一个更新包的下载地址。我们订的是3个以上版本下载全部数据,1-3个版本跨度,则提供不同的更新包进行下载。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值