• [COCOS2DX-LUA]0-003.根据COCOS2DX热更新


    一.最近有需求就是要基于COCOS2DX-LUA进行游戏的增量更新,找了资料,发现了COCOS2DX有自带一个热更新的类,就是AssetsManager,但是该接口对于我来说有以下的缺陷

    1.版本号在整个游戏是唯一的,不能多个模块控制各自的版本进行更新

    2.没有to_lua的下载监听,要实现函数回调,比如进度条,就没有办法

    二.基于以上两点我们对该AssetsManager进行扩展,得到新类,AssetsManagerEx

    1.AssetsManagerEx.h

    /****************************************************************************
     Copyright (c) 2013 cocos2d-x.org
     Copyright (c) Microsoft Open Technologies, Inc.
    
     http://www.cocos2d-x.org
     
     Permission is hereby granted, free of charge, to any person obtaining a copy
     of this software and associated documentation files (the "Software"), to deal
     in the Software without restriction, including without limitation the rights
     to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
     copies of the Software, and to permit persons to whom the Software is
     furnished to do so, subject to the following conditions:
     
     The above copyright notice and this permission notice shall be included in
     all copies or substantial portions of the Software.
     
     THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
     IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
     FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
     AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
     LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
     OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
     THE SOFTWARE.
     ****************************************************************************/
    
    #ifndef __AssetsManagerEx__
    #define __AssetsManagerEx__
    
    #include "cocos2d.h"
    #include "ExtensionMacros.h"
    
    #if (CC_TARGET_PLATFORM != CC_PLATFORM_WINRT) && (CC_TARGET_PLATFORM != CC_PLATFORM_WP8)
    #include <string>
    #include <curl/curl.h>
    #include <pthread.h>
    
    NS_CC_EXT_BEGIN
    
    class AssetsManagerExDelegateProtocol;
    
    /*
     *  This class is used to auto update resources, such as pictures or scripts.
     *  The updated package should be a zip file. And there should be a file named
     *  version in the server, which contains version code.
     *  @js NA
     *  @lua NA
     */
    class CC_DLL AssetsManagerEx
    {
    public:
        enum ErrorCode
        {
            // Error caused by creating a file to store downloaded data
            kCreateFile,
            /** Error caused by network
             -- network unavaivable
             -- timeout
             -- ...
             */
            kNetwork,
            /** There is not a new version
             */
            kNoNewVersion,
            /** Error caused in uncompressing stage
             -- can not open zip file
             -- can not read file global information
             -- can not read file information
             -- can not create a directory
             -- ...
             */
            kUncompress,
        };
        
        /* @brief Creates a AssetsManagerEx with new package url, version code url and storage path.
         *
         * @param packageUrl URL of new package, the package should be a zip file.
         * @param versionFileUrl URL of version file. It should contain version code of new package.
         * @param storagePath The path to store downloaded resources.
         */
        AssetsManagerEx(const char* packageUrl = NULL, const char* versionFileUrl = NULL, const char* storagePath = NULL);
        
        virtual ~AssetsManagerEx();
        
        /* @brief Check out if there is a new version resource.
         *        You may use this method before updating, then let user determine whether
         *        he wants to update resources.
         */
        virtual bool checkUpdate();
        
        /* @brief Download new package if there is a new version, and uncompress downloaded zip file.
         *        Ofcourse it will set search path that stores downloaded files.
         */
        virtual void update();
        
        /* @brief Gets url of package.
         */
        const char* getPackageUrl() const;
        
        /* @brief Sets package url.
         */
        void setPackageUrl(const char* packageUrl);
        
        /* @brief Gets version file url.
         */
        const char* getVersionFileUrl() const;
        
        /* @brief Gets version file url.
         */
        void setVersionFileUrl(const char* versionFileUrl);
        
        /* @brief Gets current version code.
         */
        std::string getVersion();
        
        /* @brief Deletes recorded version code.
         */
        void deleteVersion();
        
        /* @brief Gets storage path.
         */
        const char* getStoragePath() const;
        
        /* @brief Sets storage path.
         *
         * @param storagePath The path to store downloaded resources.
         * @warm The path should be a valid path.
         */
        void setStoragePath(const char* storagePath);
        
        /** @brief Sets delegate, the delegate will receive messages
         */
        void setDelegate(AssetsManagerExDelegateProtocol *delegate);
        
        /** @brief Register script handler, the hander will receive messages
         */
        void registerScriptHandler(int handler);
        void unregisterScriptHandler(void);
    
        /** @brief Sets connection time out in seconds
         */
        void setConnectionTimeout(unsigned int timeout);
        
        /** @brief Gets connection time out in secondes
         */
        unsigned int getConnectionTimeout();
        
        /* downloadAndUncompress is the entry of a new thread 
         */
        friend void* AssetsManagerExDownloadAndUncompress(void*);
        friend int AssetsManagerExProgressFunc(void *, double, double, double, double);
    
    	// add by sd.Mount 2015.09.22 16:17
    	// 可变参数
    	void setKeyOfVersion(const char* keyofversion);
    	void setKeyOfDownLoadVersion(const char* keyofdownloadversion);
    	void setTempPackageFileName(const char* temppackagefilename);
    
    	// get方法
    	const char* getKeyOfVersion();
    	const char* getKeyOfDownLoadVersion();
    	const char* getTempPackageFileName();
    
    protected:
        bool downLoad();
        void checkStoragePath();
        bool uncompress();
        bool createDirectory(const char *path);
        void setSearchPath();
        void sendErrorMessage(ErrorCode code);
        
    private:
        typedef struct _Message
        {
        public:
            _Message() : what(0), obj(NULL){}
    		// message type
            unsigned int what; 
            void* obj;
        } Message;
        
        class Helper : public cocos2d::CCObject
        {
        public:
            Helper();
            ~Helper();
            
            virtual void update(float dt);
            void sendMessage(Message *msg);
            
        private:
            void handleUpdateSucceed(Message *msg);
            
            std::list<Message*> *_messageQueue;
            pthread_mutex_t _messageQueueMutex;
        };
        
    private:
        //! The path to store downloaded resources.
        std::string _storagePath;
        
        //! The version of downloaded resources.
        std::string _version;
        
        std::string _packageUrl;
        std::string _versionFileUrl;
        
        std::string _downloadedVersion;
        
        CURL *_curl;
        Helper *_schedule;
        pthread_t *_tid;
        unsigned int _connectionTimeout;
        
    	// weak reference
        AssetsManagerExDelegateProtocol *_delegate; 
        int _scriptHandler; // script handler
    
    	// add by sd.Mount 20150922 16:15
    	const char* KEY_OF_VERSION = "current-version-code";
    	const char* KEY_OF_DOWNLOADED_VERSION = "downloaded-version-code";
    	const char* TEMP_PACKAGE_FILE_NAME = "cocos2dx-update-temp-package.zip";
    };
    
    class AssetsManagerExDelegateProtocol
    {
    public:
        /* @brief Call back function for error
           @param errorCode Type of error
         */
        virtual void onError(AssetsManagerEx::ErrorCode errorCode) {};
        /** @brief Call back function for recording downloading percent
            @param percent How much percent downloaded
            @warn This call back function just for recording downloading percent.
                  AssetsManagerEx will do some other thing after downloading, you should
                  write code in onSuccess() after downloading. 
         */
        virtual void onProgress(int percent) {};
        /** @brief Call back function for success
         */
        virtual void onSuccess() {};
    };
    
    NS_CC_EXT_END;
    #endif // CC_TARGET_PLATFORM != CC_PLATFORM_WINRT
    #endif /* defined(__AssetsManagerEx__) */
    

    2.AssetsManagerEx.cpp

    /****************************************************************************
     Copyright (c) 2013 cocos2d-x.org
     
     http://www.cocos2d-x.org
     
     Permission is hereby granted, free of charge, to any person obtaining a copy
     of this software and associated documentation files (the "Software"), to deal
     in the Software without restriction, including without limitation the rights
     to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
     copies of the Software, and to permit persons to whom the Software is
     furnished to do so, subject to the following conditions:
     
     The above copyright notice and this permission notice shall be included in
     all copies or substantial portions of the Software.
     
     THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
     IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
     FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
     AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
     LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
     OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
     THE SOFTWARE.
     ****************************************************************************/
    
    #include "AssetsManagerEx.h"
    #include "cocos2d.h"
    
    #if (CC_TARGET_PLATFORM != CC_PLATFORM_WINRT) && (CC_TARGET_PLATFORM != CC_PLATFORM_WP8)
    #include <curl/curl.h>
    #include <curl/easy.h>
    
    #include <stdio.h>
    #include <vector>
    
    #if (CC_TARGET_PLATFORM != CC_PLATFORM_WIN32)
    #include <sys/types.h>
    #include <sys/stat.h>
    #include <errno.h>
    #endif
    
    #include "support/zip_support/unzip.h"
    #include "script_support/CCScriptSupport.h"
    
    using namespace cocos2d;
    using namespace std;
    
    NS_CC_EXT_BEGIN;
    //
    // #define KEY_OF_VERSION   "current-version-code"
    // #define KEY_OF_DOWNLOADED_VERSION    "downloaded-version-code"
    // #define TEMP_PACKAGE_FILE_NAME    "cocos2dx-update-temp-package.zip"
    #define BUFFER_SIZE    8192
    #define MAX_FILENAME   512
    
    // Message type
    #define AssetsManagerEx_MESSAGE_UPDATE_SUCCEED                0
    #define AssetsManagerEx_MESSAGE_RECORD_DOWNLOADED_VERSION     1
    #define AssetsManagerEx_MESSAGE_PROGRESS                      2
    #define AssetsManagerEx_MESSAGE_ERROR                         3
    
    // Some data struct for sending messages
    
    struct ErrorMessage
    {
        AssetsManagerEx::ErrorCode code;
        AssetsManagerEx* manager;
    };
    
    struct ProgressMessage
    {
        int percent;
        AssetsManagerEx* manager;
    };
    
    // Implementation of AssetsManagerEx
    AssetsManagerEx::AssetsManagerEx(const char* packageUrl/* =NULL */, const char* versionFileUrl/* =NULL */, const char* storagePath/* =NULL */)
    :  _storagePath(storagePath)
    , _version("")
    , _packageUrl(packageUrl)
    , _versionFileUrl(versionFileUrl)
    , _downloadedVersion("")
    , _curl(NULL)
    , _tid(NULL)
    , _connectionTimeout(0)
    , _delegate(NULL)
    , _scriptHandler(0)
    {
        checkStoragePath();
        _schedule = new Helper();
    }
    
    AssetsManagerEx::~AssetsManagerEx()
    {
        if (_schedule)
        {
            _schedule->release();
        }
        unregisterScriptHandler();
    }
    
    void AssetsManagerEx::checkStoragePath()
    {
        if (_storagePath.size() > 0 && _storagePath[_storagePath.size() - 1] != '/')
        {
            _storagePath.append("/");
        }
    }
    
    static size_t getVersionCode(void *ptr, size_t size, size_t nmemb, void *userdata)
    {
        string *version = (string*)userdata;
        version->append((char*)ptr, size * nmemb);
        
        return (size * nmemb);
    }
    
    bool AssetsManagerEx::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);
        res = curl_easy_perform(_curl);
        
        if (res != 0)
        {
            sendErrorMessage(kNetwork);
            CCLOG("can not get version file content, error code is %d", res);
            curl_easy_cleanup(_curl);
            return false;
        }
        
        string recordedVersion = CCUserDefault::sharedUserDefault()->getStringForKey(KEY_OF_VERSION);
        if (recordedVersion == _version)
        {
            sendErrorMessage(kNoNewVersion);
            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;
    }
    
    void* AssetsManagerExDownloadAndUncompress(void *data)
    {
        AssetsManagerEx* self = (AssetsManagerEx*)data;
        
        do
        {
            if (self->_downloadedVersion != self->_version)
            {
                if (! self->downLoad()) break;
                
                // Record downloaded version.
                AssetsManagerEx::Message *msg1 = new AssetsManagerEx::Message();
                msg1->what = AssetsManagerEx_MESSAGE_RECORD_DOWNLOADED_VERSION;
                msg1->obj = self;
                self->_schedule->sendMessage(msg1);
            }
            
            // Uncompress zip file.
            if (! self->uncompress())
            {
                self->sendErrorMessage(AssetsManagerEx::kUncompress);
                break;
            }
            
            // Record updated version and remove downloaded zip file
            AssetsManagerEx::Message *msg2 = new AssetsManagerEx::Message();
            msg2->what = AssetsManagerEx_MESSAGE_UPDATE_SUCCEED;
            msg2->obj = self;
            self->_schedule->sendMessage(msg2);
        } while (0);
        
        if (self->_tid)
        {
            delete self->_tid;
            self->_tid = NULL;
        }
        
        return NULL;
    }
    
    void AssetsManagerEx::update()
    {
        if (_tid) return;
        
        // 1. Urls of package and version should be valid;
        // 2. Package should be a zip file.
        if (_versionFileUrl.size() == 0 ||
            _packageUrl.size() == 0 ||
            std::string::npos == _packageUrl.find(".zip"))
        {
            CCLOG("no version file url, or no package url, or the package is not a zip file");
            return;
        }
        
        // Check if there is a new version.
        if (! checkUpdate()) return;
        
        // Is package already downloaded?
        _downloadedVersion = CCUserDefault::sharedUserDefault()->getStringForKey(KEY_OF_DOWNLOADED_VERSION);
        
        _tid = new pthread_t();
        pthread_create(&(*_tid), NULL, AssetsManagerExDownloadAndUncompress, this);
    }
    
    bool AssetsManagerEx::uncompress()
    {
        // Open the zip file
        string outFileName = _storagePath + TEMP_PACKAGE_FILE_NAME;
        unzFile zipfile = unzOpen(outFileName.c_str());
        if (! zipfile)
        {
            CCLOG("can not open downloaded zip file %s", outFileName.c_str());
            return false;
        }
        
        // Get info about the zip file
        unz_global_info global_info;
        if (unzGetGlobalInfo(zipfile, &global_info) != UNZ_OK)
        {
            CCLOG("can not read file global info of %s", outFileName.c_str());
            unzClose(zipfile);
            return false;
        }
    	const int buffsize_const = BUFFER_SIZE;
        // Buffer to hold data read from the zip file
    	char readBuffer[buffsize_const];
        
        CCLOG("start uncompressing");
        
        // Loop to extract all files.
        uLong i;
        for (i = 0; i < global_info.number_entry; ++i)
        {
            // Get info about current file.
            unz_file_info fileInfo;
            char fileName[MAX_FILENAME];
            if (unzGetCurrentFileInfo(zipfile,
                                      &fileInfo,
                                      fileName,
                                      MAX_FILENAME,
                                      NULL,
                                      0,
                                      NULL,
                                      0) != UNZ_OK)
            {
                CCLOG("can not read file info");
                unzClose(zipfile);
                return false;
            }
            
            string fullPath = _storagePath + fileName;
            
            // Check if this entry is a directory or a file.
            const size_t filenameLength = strlen(fileName);
            if (fileName[filenameLength-1] == '/')
            {
    			// get all dir
    			string fileNameStr = string(fileName);
    			size_t position = 0;
    			while((position=fileNameStr.find_first_of("/",position))!=string::npos)
    			{
    				string dirPath =_storagePath + fileNameStr.substr(0, position);
    				// Entry is a direcotry, so create it.
    				// If the directory exists, it will failed scilently.
    				if (!createDirectory(dirPath.c_str()))
    				{
    						CCLOG("can not create directory %s", dirPath.c_str());
    						//unzClose(zipfile);
    						//return false;
    				}
    				position++;
    			}
           }
           else
           {
                // Entry is a file, so extract it.
                
                // Open current file.
                if (unzOpenCurrentFile(zipfile) != UNZ_OK)
                {
                    CCLOG("can not open file %s", fileName);
                    unzClose(zipfile);
                    return false;
                }
                
                // Create a file to store current file.
                FILE *out = fopen(fullPath.c_str(), "wb");
                if (! out)
                {
                    CCLOG("can not open destination file %s", fullPath.c_str());
                    unzCloseCurrentFile(zipfile);
                    unzClose(zipfile);
                    return false;
                }
                
                // Write current file content to destinate file.
                int error = UNZ_OK;
                do
                {
                    error = unzReadCurrentFile(zipfile, readBuffer, BUFFER_SIZE);
                    if (error < 0)
                    {
                        CCLOG("can not read zip file %s, error code is %d", fileName, error);
                        unzCloseCurrentFile(zipfile);
                        unzClose(zipfile);
                        return false;
                    }
                    
                    if (error > 0)
                    {
                        fwrite(readBuffer, error, 1, out);
                    }
                } while(error > 0);
                
                fclose(out);
            }
            
            unzCloseCurrentFile(zipfile);
            
            // Goto next entry listed in the zip file.
            if ((i+1) < global_info.number_entry)
            {
                if (unzGoToNextFile(zipfile) != UNZ_OK)
                {
                    CCLOG("can not read next file");
                    unzClose(zipfile);
                    return false;
                }
            }
        }
    	unzClose(zipfile);
        CCLOG("end uncompressing");
        
        return true;
    }
    
    /*
     * Create a direcotry is platform depended.
     */
    bool AssetsManagerEx::createDirectory(const char *path)
    {
    #if (CC_TARGET_PLATFORM != CC_PLATFORM_WIN32)
        mode_t processMask = umask(0);
        int ret = mkdir(path, S_IRWXU | S_IRWXG | S_IRWXO);
        umask(processMask);
        if (ret != 0 && (errno != EEXIST))
        {
            return false;
        }
        
        return true;
    #else
        BOOL ret = CreateDirectoryA(path, NULL);
    	if (!ret && ERROR_ALREADY_EXISTS != GetLastError())
    	{
    		return false;
    	}
        return true;
    #endif
    }
    
    void AssetsManagerEx::setSearchPath()
    {
        vector<string> searchPaths = CCFileUtils::sharedFileUtils()->getSearchPaths();
        vector<string>::iterator iter = searchPaths.begin();
        searchPaths.insert(iter, _storagePath);
        CCFileUtils::sharedFileUtils()->setSearchPaths(searchPaths);
    }
    
    static size_t downLoadPackage(void *ptr, size_t size, size_t nmemb, void *userdata)
    {
        FILE *fp = (FILE*)userdata;
        size_t written = fwrite(ptr, size, nmemb, fp);
        return written;
    }
    
    int AssetsManagerExProgressFunc(void *ptr, double totalToDownload, double nowDownloaded, double totalToUpLoad, double nowUpLoaded)
    {
        AssetsManagerEx* manager = (AssetsManagerEx*)ptr;
        AssetsManagerEx::Message *msg = new AssetsManagerEx::Message();
        msg->what = AssetsManagerEx_MESSAGE_PROGRESS;
        
        ProgressMessage *progressData = new ProgressMessage();
        progressData->percent = (int)(nowDownloaded/totalToDownload*100);
        progressData->manager = manager;
        msg->obj = progressData;
        
        manager->_schedule->sendMessage(msg);
        
        CCLOG("downloading... %d%%", (int)(nowDownloaded/totalToDownload*100));
        
        return 0;
    }
    
    bool AssetsManagerEx::downLoad()
    {
        // Create a file to save package.
        string outFileName = _storagePath + TEMP_PACKAGE_FILE_NAME;
        FILE *fp = fopen(outFileName.c_str(), "wb");
        if (! fp)
        {
            sendErrorMessage(kCreateFile);
            CCLOG("can not create file %s", outFileName.c_str());
            return false;
        }
        
        // Download pacakge
        CURLcode res;
        curl_easy_setopt(_curl, CURLOPT_URL, _packageUrl.c_str());
        curl_easy_setopt(_curl, CURLOPT_WRITEFUNCTION, downLoadPackage);
        curl_easy_setopt(_curl, CURLOPT_WRITEDATA, fp);
        curl_easy_setopt(_curl, CURLOPT_NOPROGRESS, false);
        curl_easy_setopt(_curl, CURLOPT_PROGRESSFUNCTION, AssetsManagerExProgressFunc);
        curl_easy_setopt(_curl, CURLOPT_PROGRESSDATA, this);
        res = curl_easy_perform(_curl);
        curl_easy_cleanup(_curl);
        if (res != 0)
        {
            sendErrorMessage(kNetwork);
            CCLOG("error when download package");
            fclose(fp);
            return false;
        }
        
        CCLOG("succeed downloading package %s", _packageUrl.c_str());
        
        fclose(fp);
        return true;
    }
    
    const char* AssetsManagerEx::getPackageUrl() const
    {
        return _packageUrl.c_str();
    }
    
    void AssetsManagerEx::setPackageUrl(const char *packageUrl)
    {
        _packageUrl = packageUrl;
    }
    
    const char* AssetsManagerEx::getStoragePath() const
    {
        return _storagePath.c_str();
    }
    
    void AssetsManagerEx::setStoragePath(const char *storagePath)
    {
        _storagePath = storagePath;
        checkStoragePath();
    }
    
    const char* AssetsManagerEx::getVersionFileUrl() const
    {
        return _versionFileUrl.c_str();
    }
    
    void AssetsManagerEx::setVersionFileUrl(const char *versionFileUrl)
    {
        _versionFileUrl = versionFileUrl;
    }
    
    string AssetsManagerEx::getVersion()
    {
        return CCUserDefault::sharedUserDefault()->getStringForKey(KEY_OF_VERSION);
    }
    
    void AssetsManagerEx::deleteVersion()
    {
        CCUserDefault::sharedUserDefault()->setStringForKey(KEY_OF_VERSION, "");
    }
    
    void AssetsManagerEx::setDelegate(AssetsManagerExDelegateProtocol *delegate)
    {
        _delegate = delegate;
    }
    
    void AssetsManagerEx::registerScriptHandler(int handler)
    {
        unregisterScriptHandler();
        _scriptHandler = handler;
    }
    
    void AssetsManagerEx::unregisterScriptHandler(void)
    {
        CCScriptEngineManager::sharedManager()->getScriptEngine()->removeScriptHandler(_scriptHandler);
        _scriptHandler = 0;
    }
    
    void AssetsManagerEx::setConnectionTimeout(unsigned int timeout)
    {
        _connectionTimeout = timeout;
    }
    
    unsigned int AssetsManagerEx::getConnectionTimeout()
    {
        return _connectionTimeout;
    }
    
    void AssetsManagerEx::sendErrorMessage(AssetsManagerEx::ErrorCode code)
    {
        Message *msg = new Message();
        msg->what = AssetsManagerEx_MESSAGE_ERROR;
        
        ErrorMessage *errorMessage = new ErrorMessage();
        errorMessage->code = code;
        errorMessage->manager = this;
        msg->obj = errorMessage;
        
        _schedule->sendMessage(msg);
    }
    
    // Implementation of AssetsManagerExHelper
    
    AssetsManagerEx::Helper::Helper()
    {
        _messageQueue = new list<Message*>();
        pthread_mutex_init(&_messageQueueMutex, NULL);
        CCDirector::sharedDirector()->getScheduler()->scheduleUpdateForTarget(this, 0, false);
    }
    
    AssetsManagerEx::Helper::~Helper()
    {
        CCDirector::sharedDirector()->getScheduler()->unscheduleAllForTarget(this);
        delete _messageQueue;
    }
    
    void AssetsManagerEx::Helper::sendMessage(Message *msg)
    {
        pthread_mutex_lock(&_messageQueueMutex);
        _messageQueue->push_back(msg);
        pthread_mutex_unlock(&_messageQueueMutex);
    }
    
    void AssetsManagerEx::Helper::update(float dt)
    {
        Message *msg = NULL;
        
        // Returns quickly if no message
        pthread_mutex_lock(&_messageQueueMutex);
        if (0 == _messageQueue->size())
        {
            pthread_mutex_unlock(&_messageQueueMutex);
            return;
        }
        
        // Gets message
        msg = *(_messageQueue->begin());
        _messageQueue->pop_front();
        pthread_mutex_unlock(&_messageQueueMutex);
        
        switch (msg->what) {
            case AssetsManagerEx_MESSAGE_UPDATE_SUCCEED:
                handleUpdateSucceed(msg);
                
                break;
            case AssetsManagerEx_MESSAGE_RECORD_DOWNLOADED_VERSION:
    			CCUserDefault::sharedUserDefault()->setStringForKey(((AssetsManagerEx*)msg->obj)->getKeyOfDownLoadVersion(),
                                                                    ((AssetsManagerEx*)msg->obj)->_version.c_str());
                CCUserDefault::sharedUserDefault()->flush();
                
                break;
            case AssetsManagerEx_MESSAGE_PROGRESS:
                if (((ProgressMessage*)msg->obj)->manager->_delegate)
                {
                    ((ProgressMessage*)msg->obj)->manager->_delegate->onProgress(((ProgressMessage*)msg->obj)->percent);
                }
                if (((ProgressMessage*)msg->obj)->manager->_scriptHandler)
                {
                    char buff[10];
                    sprintf(buff, "%d", ((ProgressMessage*)msg->obj)->percent);
                    CCScriptEngineManager::sharedManager()->getScriptEngine()->executeEvent(((ProgressMessage*)msg->obj)->manager->_scriptHandler, buff);
                }
                
                delete (ProgressMessage*)msg->obj;
                
                break;
            case AssetsManagerEx_MESSAGE_ERROR:
                // error call back
                if (((ErrorMessage*)msg->obj)->manager->_delegate)
                {
                    ((ErrorMessage*)msg->obj)->manager->_delegate->onError(((ErrorMessage*)msg->obj)->code);
                }
                if (((ProgressMessage*)msg->obj)->manager->_scriptHandler)
                {
                    std::string errorMessage;
                    switch ((int)((ErrorMessage*)msg->obj)->code)
                    {
                    case kCreateFile:
                        errorMessage = "errorCreateFile";
                        break;
                
                    case kNetwork:
                        errorMessage = "errorNetwork";
                        break;
    
                    case kNoNewVersion:
                        errorMessage = "errorNoNewVersion";
                        break;
    
                    case kUncompress:
                        errorMessage = "errorUncompress";
                        break;
    
                    default:
                        errorMessage = "errorUnknown";
                    }
    
                    CCScriptEngineManager::sharedManager()->getScriptEngine()->executeEvent(((ProgressMessage*)msg->obj)->manager->_scriptHandler, errorMessage.c_str());
                }
    
                delete ((ErrorMessage*)msg->obj);
                
                break;
            default:
                break;
        }
        
        delete msg;
    }
    
    void AssetsManagerEx::Helper::handleUpdateSucceed(Message *msg)
    {
        AssetsManagerEx* manager = (AssetsManagerEx*)msg->obj;
        
        // Record new version code.
    	CCUserDefault::sharedUserDefault()->setStringForKey(manager->getKeyOfVersion(), manager->_version.c_str());
        
        // Unrecord downloaded version code.
    	CCUserDefault::sharedUserDefault()->setStringForKey(manager->getKeyOfDownLoadVersion(), "");
        CCUserDefault::sharedUserDefault()->flush();
        
        // Set resource search path.
        manager->setSearchPath();
        
        // Delete unloaded zip file.
    	string zipfileName = manager->_storagePath + manager->getTempPackageFileName();
        if (remove(zipfileName.c_str()) != 0)
        {
            CCLOG("can not remove downloaded zip file %s", zipfileName.c_str());
        }
        
        if (manager)
        {
            if (manager->_delegate)
            {
                manager->_delegate->onSuccess();
    }
            if (manager->_scriptHandler)
            {
                CCScriptEngineManager::sharedManager()->getScriptEngine()->executeEvent(manager->_scriptHandler, "success");
            }
        }
    }
    
    // add by sd.Mount
    // 可变参数
    void AssetsManagerEx::setKeyOfVersion(const char* keyofversion)
    {
    	KEY_OF_VERSION = keyofversion;
    
    }
    void AssetsManagerEx::setKeyOfDownLoadVersion(const char* keyofdownloadversion)
    {
    	KEY_OF_DOWNLOADED_VERSION = keyofdownloadversion;
    }
    void AssetsManagerEx::setTempPackageFileName(const char* temppackagefilename)
    {
    	TEMP_PACKAGE_FILE_NAME = temppackagefilename;
    }
    
    
    // get方法
    const char* AssetsManagerEx::getKeyOfVersion()
    {
    	return KEY_OF_VERSION;
    }
    const char* AssetsManagerEx::getKeyOfDownLoadVersion()
    {
    	return KEY_OF_DOWNLOADED_VERSION;
    }
    const char* AssetsManagerEx::getTempPackageFileName()
    {
    	return TEMP_PACKAGE_FILE_NAME;
    }
    
    
    
    NS_CC_EXT_END;
    #endif // CC_PLATFORM_WINRT
    

     三.在lua中,会有一个共同的工具类UUPdate,代码如下

    --[[
    
    Copyright (c) 2012-2013 baby-bus.com
    
    http://www.babybus.com/superdo/
    
    Permission is hereby granted, free of charge, to any person obtaining a copy
    of this software and associated documentation files (the "Software"), to deal
    in the Software without restriction, including without limitation the rights
    to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
    copies of the Software, and to permit persons to whom the Software is
    furnished to do so, subject to the following conditions:
    
    The above copyright notice and this permission notice shall be included in
    all copies or substantial portions of the Software.
    
    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
    AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
    OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
    THE SOFTWARE.
    
    ]]
    
    --[[!--
        更新工具类:用于热更新程序资源文件(图片,音频,Lua)
    ]]
    
    ----------------------
    -- 类
    ----------------------
    local M = {}
    M.TAG   = "UUpdate"
    ----------------------
    -- 公共参数
    ----------------------
    -- [常量]
    
    -- [操作变量]
    -- ..
    
    
    
    
    ----------------------
    -- 工具方法类
    ----------------------
    
    ----------------------
    -- getAssetsManager
    ----------------------
    --param : [params]
    -- {
    --   upFilePath      =  "http://qd.baidupcs.com/file/1a531cc6565d95827e0ec3f9fd4af795?fid=138463011-250528-222085911919595&time=1406097600&sign=FDTAXER-DCb740ccc5511e5e8fedcff06b081203-mLvWIkhwaVZCjN%2BpTYZJZ%2BagPR4%3D&to=qb&fm=N,B,T,t&newver=1&expires=1406098200&rt=sh&r=164813188&mlogid=124796854&sh=1&vuk=138463011&vbdid=2986594979&fn=GameScene.zip",
    --   versionpath     =  "",
    --   savepath        =  ""
    --   keyversion      =  "",
    --   downloadversion =  "",
    --   tempPackagename =  "",
    --   fnError         =  function(errorCode)
    
    --                      end,
    --   fnProgress      =  function(percent)
    
    --                      end,
    --   fnSuccess       =  function()
    
    --                      end
    -- }
    --[[--
       explain:获取资源更新管理器
    --]]--
    function M.getAssetsManager(params) 
       -- 获取参数
       local upFilePath      = params.upFilePath
       local versionpath     = params.versionpath
       local savepath        = params.savepath
       local keyversion      = params.keyversion
       local downloadversion = params.downloadversion
       local tempPackagename = params.tempPackagename
       
       --回调方法
       local fnError    = params.fnError
       local fnProgress = params.fnProgress
       local fnSuccess  = params.fnSuccess
    
       -- 下载的文件路径不存在就退出
       if not upFilePath or S.trim(upFilePath) == "" then 
         return nil
       end
       -- 下载的版本号文件不存在就退出
       if not versionpath or S.trim(versionpath) == "" then 
         return nil
       end
       -- 保存路径不存在就退出
       if not savepath or S.trim(savepath) == "" then 
         return nil
       end
       -- 创建管理器
       local assetsManager = AssetsManagerEx:new(upFilePath, versionpath, savepath)  
       if keyversion and S.trim(keyversion) ~= "" then
          assetsManager:setKeyOfVersion(keyversion)
       end
       if downloadversion and S.trim(downloadversion) ~= "" then
          assetsManager:setKeyOfDownLoadVersion(downloadversion)
       end
       if tempPackagename and S.trim(tempPackagename) ~= "" then
          assetsManager:setTempPackageFileName(tempPackagename)
       end
    
       -- --注册监听
       local updateListener = M.getDownLoadListener(fnError, fnProgress, fnSuccess)
       assetsManager:setDelegate(updateListener)
       return assetsManager  
    end  
    
    
    ----------------------
    -- getDownLoadListener
    ----------------------
    --param : [fnError] 错误方法
    --param : [fnProgress] 进度方法
    --param : [fnSuccess]  成功方法
    --[[--
       explain:下载监听
    --]]--
    function M.getDownLoadListener(fnError, fnProgress, fnSuccess)
        local updateListener = UpdateListener:new()
        -- --注册响应事件
        if fnError then
            updateListener:registerScriptHandlerError(fnError)
        end
        if fnProgress then
            updateListener:registerScriptHandlerProgress(fnProgress)
        end
        if fnSuccess then
            updateListener:registerScriptHandlerSuccess(fnSuccess)
        end
        return updateListener
    end
    
    ----------------------
    -- unregisterScriptHandler
    ----------------------
    --param : [updateListener] 要关闭的监听
    --[[--
       explain:解除更新监听
    --]]--
    function M.unregisterScriptHandler(updateListener)
        updateListener:unregisterScriptHandlerError(fnError)
        updateListener:unregisterScriptHandlerProgress(fnProgress)
        updateListener:unregisterScriptHandlerSuccess(fnSuccess)
    end
    
    -------------------------------------------------
    ------------------------下载完更新部分-----------
    -------------------------------------------------
    function M.updateLuaFile(floder,binName)
        floder = M.combine(HOT_UPDATE_PATH, floder)
        --添加路径搜索目录
        CCFileUtils:sharedFileUtils():addSearchPath(floder)
        --获取添加的Lua目录
        local hot_update={}
        if io.exists(M.combine(floder, HOT_UPDATE_FILE_NAME)) then
          hot_update = require(HOT_UPDATE_FILE_NAME)
          for i,v in ipairs(hot_update) do
              local luaPath, name = v[1], v[2] 
              package.preload[luaPath] = nil
              package.loaded[luaPath] = nil
              --清空
              if name then 
                  _G[name] = nil
              else
              end
          end
        end
        -- 加入目录下存在bin,那么加载
        local binpath = M.combine(floder, binName)
        if io.exists(binpath) then
           CCLuaLoadChunksFromZIP(binpath)
        end
        -- 重新加载目录
        for i,v in ipairs(hot_update) do
            local luaPath, name = v[1], v[2] 
            if name then 
                _G[name] = require(luaPath)
            else
                require(luaPath)
            end
        end
        
        --require 完成后直接清空
        hot_update = nil
        package.preload[HOT_UPDATE_FILE_NAME] = nil
        package.loaded[HOT_UPDATE_FILE_NAME] = nil
    end
    
    --配置图片路径
    function M.configResPath(resPath, language, addRootPath)
        -- [参数默认]
        language    = ifnil(language, device.language)
        addRootPath = ifnil(addRootPath, false)
        resPath     = M.combine(HOT_UPDATE_PATH, resPath)
        -- [搜索路径配置]
        local pathes = M.getAllSearchPathes(resPath, language, addRootPath)
        for _,v in ipairs(pathes) do
            CCFileUtils:sharedFileUtils():addSearchPath(v)
        end
    
    
        -- [日志输出]
        -- 当调度级别>=1时,输出日志信息
        if DEBUG >= 1 then
            echoInfo("# SEARCH_PATH")
            for _,v in ipairs(pathes) do
                echoInfo("#   " .. v)
            end
            echoInfo("#")
        end
    end
    
    -- 获得搜索路径集, 根据驱动模型(x1,x2,x3,x4)
    function M.getAllSearchPathes(resPath, language, addRootPath)
        local pathes = {}
    
        -- 根路径策略
        if addRootPath then 
            table.insert(pathes, resPath)    
        end
        -- 增量更新策略
        table.insert(pathes, PH.combine(resPath, "i18n", language, "img/", DEVICE_MODEL, "/"))
        if language == "zht" then 
            table.insert(pathes, PH.combine(resPath, "i18n", "zh", "img/", DEVICE_MODEL, "/"))
        end
        table.insert(pathes, PH.combine(resPath, "img", DEVICE_MODEL, "/"))
        table.insert(pathes, PH.combine(resPath, "img/xx/"))
        
        return pathes
    end
    
    --加载所有更新的bin
    function M.loadAllLuaBin()
      if GAME_UPDATE_FLODERS and #GAME_UPDATE_FLODERS > 0 then
        for _,floder in pairs(GAME_UPDATE_FLODERS) do
          --如果文件夹未被加载并且已经存在
          if not T.containsValue(LOAD_UPDATE_FLODERS, floder) and io.exists(M.combine(HOT_UPDATE_PATH, floder, "update.bin")) then
            M.updateLuaFile(floder, "update.bin")
            require(HOT_INIT_FILE_NAME)
            table.insert(LOAD_UPDATE_FLODERS, floder)
          end
        end
      end
    end
    
    --配置所有的图片搜索路径
    function M.configAllImgPath()
      if GAME_UPDATE_FLODERS and #GAME_UPDATE_FLODERS > 0 then
        for _,floder in pairs(GAME_UPDATE_FLODERS) do
          print(M.combine(floder, "res/"))
          M.configResPath(M.combine(floder, "res/"))
        end
      end
    end
    
    -- 获取音频路径
    function M.getAudiopath(resPath, filename)
       local lang              =  device.language
       local pathUpdatorI18N   = M.combine(resPath, "i18n/", lang, string.gsub(PATH_SND, "res/snd/", "snd/"), filename)
       local pathUpdatorCommon = M.combine(resPath, "snd/", filename)
       return pathUpdatorI18N, pathUpdatorCommon
    end
    
    --获取所有的音频路径
    function M.getAllAudioPath(filename)
      if GAME_UPDATE_FLODERS and #GAME_UPDATE_FLODERS > 0 then
        for _,floder in pairs(GAME_UPDATE_FLODERS) do
          local pathUpdatorI18N, pathUpdatorCommon = M.getAudiopath(PH.combine(HOT_UPDATE_PATH, floder, "res/"), filename)
          if io.exists(pathUpdatorI18N) then 
              return pathUpdatorI18N
          elseif io.exists(pathUpdatorCommon) then 
              return pathUpdatorCommon
          end
        end
      end
      return nil
    end
    
    ----------------------
    -- 工具方法
    ----------------------
    --[[--
    
    连接多个路径
    
    ### Useage:
        local path = UPath.combine(路径集合)
    
    ### Aliases:
    
    ### Notice:
    
    ### Example:
        ----------------------
        -- 示例1: 通用操作
        ----------------------
        -- <<说明:获得多个路径连接后的结果>>
        print("path: ", UPath.combine("a", "b", "c", "d"))
        print("path: ", UPath.combine("a//", "\b/", "c/", "/d"))
    
    ### Parameters:
    -   ... **...**               路径集合
    
    ### Returns: 
    -   string                      连接后路径
    
    --]]--
    function M.combine(...)
      local pathMerged = nil
        local pathes = {...}
    
        if #pathes > 0 then 
          pathMerged = {}
    
          for i = 1, #pathes do
            local path = pathes[i]
    
            -- 除去首个元素不进行头部'/','\'的检查外,其他均发现有目录分隔字符都将删除
            if i ~= 1 then 
              if string.byte(path, 1) == DS_CHAR then 
                path = string.sub(path, 2)
              end
            end
    
            -- 所有元素进行尾部'/','\'的检查,发现有目录分隔字符都将删除
            if string.byte(path, -1) == DS_CHAR then 
                    path = string.sub(path, 1, -2)
            end
    
            table.insert(pathMerged, path)
          end
    
            -- 拼接新路径
          pathMerged = table.concat(pathMerged, DS)
    
            -- 去除重复的路径分隔符
            -- Version1:
            if false then 
                -- 去除重得的//
                repeat
                    pathMerged = string.gsub(pathMerged, '//', DS)
                until (string.find(pathMerged, '//', 1, true) == nil)
                -- 去除重得的\
                repeat
                    pathMerged = string.gsub(pathMerged, '\\', DS)
                until (string.find(pathMerged, '\\', 1, true) == nil)
            end
            -- Version2:
            if true then 
                if DS == '/' then 
                    -- 替换其他路径字符
                    pathMerged = string.gsub(pathMerged, '\', DS)
                    -- 去除重复的//
                    repeat
                        pathMerged = string.gsub(pathMerged, DS..DS, DS)
                    until (string.find(pathMerged, DS..DS, 1, true) == nil)
                elseif DS == '\' then 
                    -- 替换其他路径字符
                    pathMerged = string.gsub(pathMerged, '/', DS)
                    -- 去除重复的\
                    repeat
                        pathMerged = string.gsub(pathMerged, DS..DS, DS)
                    until (string.find(pathMerged, DS..DS, 1, true) == nil)
                end
            end
    
          return pathMerged
        end
    
        return path
    end
    
    return M
    

    四.注意更新的文件结构。

    1.zip包包含文件如下图

    2.hot_update的内容

    五.Lua配置和使用

    1.在scripts下面增加hotupdate_init.lua,内容如下

    -- 更新目录
    HOT_UPDATE_PATH             = bb.UPath.combine(PATH_STORE_APP,"update")
    -- require需要更新的信息
    HOT_UPDATE_FILE_NAME        = "hot_update"
    -- 模块初始化
    HOT_INIT_FILE_NAME          = "init.lua"
    --在HOT_UPDATE_PATH下的目录
    GAME_UPDATE_FLODERS         = {"update","floder2"}
    --已经加载的更新目录
    LOAD_UPDATE_FLODERS         = {}
    

     2.调用代码

    -- 热更新
    function M:doHotUpdate()
    	local path = PH.combine(HOT_UPDATE_PATH, "update")
    	IO.mkdirByFile(PH.combine(path, "/update.png"))
    	local params = 
    			{
    			  upFilePath      =  "http://10.1.1.20/files/update.zip",
    			  versionpath     =  "http://10.1.1.20/files/version.txt",
    			  savepath        =  path,
    			  keyversion      =  "versionkey",
    			  downloadversion =  "downloadversionkey",
    			  tempPackagename =  "update.zip",
    			  fnError         =  function(errorCode)
    			  						print("==error==",errorCode)
    			  						if AssetsManagerEx.kCreateFile == errorCode then
    			  							self:loadTipWord("目录创建失败!!!")
    			  						elseif AssetsManagerEx.kNetwork == errorCode then
    			  							self:loadTipWord("网络连接失败!!!")
    			  						elseif AssetsManagerEx.kNoNewVersion == errorCode then
    			  							self:loadTipWord("没有新版本!!!")
    			  						elseif AssetsManagerEx.kUncompress == errorCode then
    			  							self:loadTipWord("解压失败!!!")
    			  						end
    			                     end,
    			  fnProgress      =  function(percent)
    									if self._updatenum then
    									  	self._updatenum:setString(tostring(percent) .. "%")
    									end
    			  						print("====",percent)
    			                     end,
    			  fnSuccess       =  function()
    			  						print("====success")
    			  						self:downloadSuccess()
    			                     end
    			}
    	local assetsManage = bb.UUPdate.getAssetsManager(params)
        assetsManage:update()
    end
    

    根据以上我们就可以做成热更新的一些功能。

     

    本站文章为 宝宝巴士 SD.Team 原创,转载务必在明显处注明:(作者官方网站: 宝宝巴士 

    转载自【宝宝巴士SuperDo团队】 原文链接: http://www.cnblogs.com/superdo/p/4931949.html

  • 相关阅读:
    在Spring Boot中使用数据库事务
    Data Consistency Primer
    在Spring Boot中输出REST资源
    Android开发——进程间通信之Messenger
    初识在Spring Boot中使用JPA
    设计模式——享元模式详解
    回首2016,展望2017
    [转]OpenStack Keystone V3
    [原]Openstack之identity server(keystone)
    [原]secureCRT 改变显示宽度
  • 原文地址:https://www.cnblogs.com/superdo/p/4931949.html
Copyright © 2020-2023  润新知