cocos-2dx开发项目中的热更新cpp

#include "stdafx.h"
#include "game_Updata_Layer.h"
#include "game_start_scene.h"
#include "game_aid_layer.h"
#include "game_text.h"
#include "platform_assist.h"
#include "vi_http_client.h"
#include "game_sys_msg_layer.h"
#include "unzip.h"

#include <fstream>
#include <iostream>
#include <stdio.h>
#include <vector>
#include <thread>

#if (CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID)
#include "platform/android/jni/JniHelper.h"
#include <jni.h>
#endif

#if (CC_TARGET_PLATFORM == CC_PLATFORM_WIN32)
#include "curl/include/win32/curl/curl.h"
#include "curl/include/win32/curl/easy.h"
#endif

#if (CC_TARGET_PLATFORM != CC_PLATFORM_WIN32) && (CC_TARGET_PLATFORM != CC_PLATFORM_WP8) && (CC_TARGET_PLATFORM != CC_PLATFORM_WINRT)
#include <sys/types.h>
#include <sys/stat.h>
#include <errno.h>
#include <dirent.h>
#include "curl/curl.h"
#include "curl/easy.h"
#endif

#define LOW_SPEED_LIMIT 1L
#define LOW_SPEED_TIME 5L

#define BUFFER_SIZE    8192
#define MAX_FILENAME   512

const int MSG_SIZE_BIG = 1024*1024*5;

CGameUpdataRes::CGameUpdataRes(void):
	m_pProgress(nullptr),
	m_pAllSizeTTF(nullptr),
	m_pNowSizeTTF(nullptr),
	m_bHaveSize(false),
	m_bIsDown(true)
{
	m_iBeginUpdateIndex = 0;
}


CGameUpdataRes::~CGameUpdataRes(void)
{
}

bool CGameUpdataRes::init()
{
	Layer::init();
	addListener();
	return true;
}

void CGameUpdataRes::onEnter()
{
    Layer::onEnter();
	if (m_bIsDown)
	{
		auto size = GetUpdateFileAllSize();
		if (size > MSG_SIZE_BIG)
		{
			discoverVersion();// 提示过大
			return;
		}
		DownloadUpdateFiles();		
	}	
}

bool CGameUpdataRes::AddUpataProgress()
{
	//添加进度条
	//初始化m_pProgress,m_pAllSizeTTF,m_pNowSizeTTF
	return true;
}

void CGameUpdataRes::addListener()
{
    auto listener = EventListenerTouchOneByOne::create();
    listener->setSwallowTouches(true);
    
    listener->onTouchBegan = [=](Touch* touch, Event* event){
		if (!isVisible())
		{
			return false;
		}
        return true;
    };
    
    listener->onTouchMoved = [=](Touch* touch, Event* event){      
    };
    
    listener->onTouchEnded = [=](Touch* touch, Event* event){       
    };
    
    _eventDispatcher->addEventListenerWithSceneGraphPriority(listener, this);    
}

string CGameUpdataRes::GetLocalSubVersion()
{
	string updatePath = FileUtils::getInstance()->getWritablePath();

	CheckFullPath(updatePath);

	updatePath.append("subver.viv");
	if (FileUtils::getInstance()->isFileExist(updatePath))
	{
		ssize_t size = 0;
		auto buf = FileUtils::getInstance()->getFileData(updatePath, "rt", &size);
		if (size > 0)
		{
			char temp[32] = {0};
			memcpy(temp, buf, size);
			free(buf);
					
			string ret = temp;
			return ret;
		}
	}
	return "";
}

void CGameUpdataRes::SetLocalSubVersion(const string& version)
{
	string updatePath = FileUtils::getInstance()->getWritablePath();

	CheckFullPath(updatePath);

	updatePath.append("subver.viv");

	ofstream foutfile;
	foutfile.open(updatePath);	
	foutfile << version.c_str();
	foutfile.close();
}

bool CGameUpdataRes::CheckSubVersion()
{
	m_strStoragePath = GetUpdatePath();
	CheckFullPath(m_strStoragePath);

	ViHttpClient client;
	string baseurl = "http://sanguo.51sozan.com";
    int len = baseurl.length();
    if(len > 0 && baseurl[len - 1] != '/')
    {
        baseurl += "/";
    }
	auto url = baseurl + VERSION_URL_PATH;
	baseurl = baseurl + LOGINNOTICE;
	string str;
	int ret = client.Get(url, str);
	if (ret != 0)
	{
		// 与服务器连接失败,请检查网络连接是否正常
		m_bIsDown = true;
		return false; // 不通过
	}

	if (!str.empty())
	{
		vector<string> vecLines;

		SplitString(str, vecLines, "\r\n");
		
		if (vecLines.size() == 0)
		{
			m_bIsDown = false;
			return true; // 通过,版本正确
		}

		vector<string> verVersions;
		for (auto line : vecLines)
		{
			verVersions.clear();
			SplitString(line, verVersions, ",");
			if (verVersions.size() >= 2)
			{
				SVersionUrl vu;
				vu.version = verVersions[0];
				if (verVersions[1].find("http") == 0)
				{
					vu.url = verVersions[1];
				}
				else
				{
					vu.url = baseurl + verVersions[1];
				}
				m_listVersionUrls.push_back(vu);
			}
			else if (verVersions.size() == 1)
			{
				SVersionUrl vu;
				vu.version = verVersions[0];
				vu.url = "";
				m_listVersionUrls.push_back(vu);
			}
		}

		if (m_listVersionUrls.size() == 0)
		{
			m_bIsDown = false;
			return true; // 通过,版本正确
		}

		m_iBeginUpdateIndex = 0; // 从几条开始更新

		auto localSubVer = GetLocalSubVersion();
		if (localSubVer.empty()) 
		{
			m_bIsDown = true;
			return false; // 需要更新
		}
		
		int index = 0;
		auto it = m_listVersionUrls.begin();
		for (; it != m_listVersionUrls.end(); ++it)
		{
			auto& vu = *it;
			if (vu.version == localSubVer) // 表示已经读到这一条
			{
				m_iBeginUpdateIndex = index + 1; // 就需要从下一条开始更新
				break;
			}
			++index;
		}

		if (index >= (int)m_listVersionUrls.size())
		{
			// 没有被break 说明没有一个版本能对上
			m_iBeginUpdateIndex = 0;
			m_bIsDown = true;
			return false; // 需要从0的一条更新
		}
		else
		{
			// 被break了。。
			if (m_iBeginUpdateIndex >= (int)m_listVersionUrls.size())
			{
				// 说明是最后一个跟版本相等,不用更新
				m_bIsDown = false;
				return true;
			}
			else
			{
				m_bIsDown = true;
				return false;  // 需要从m_iBeginUpdateIndex开始更新
			}
		}
	}

	m_bIsDown = false;
	return true; // 不用更新
}

int CGameUpdataRes::GetUpdateFileAllSize()
{
	int allSize = 0;

	int index = 0;
	for (auto& vu : m_listVersionUrls)
	{
		if (index >= m_iBeginUpdateIndex)
		{
			if (!vu.url.empty())
			{

				auto  _curl = curl_easy_init();
				if (! _curl)
				{
					LOGD("can not init curl");
					return allSize;
				}

				CURLcode res;
				curl_easy_setopt(_curl, CURLOPT_URL, vu.url.c_str());
				curl_easy_setopt(_curl, CURLOPT_HEADER, 1);    //只要求header头
				curl_easy_setopt(_curl, CURLOPT_NOBODY, 1);    //不需求body
				res = curl_easy_perform(_curl);

				if (res == CURLE_OK)
				{
					double contSize = 0;
					res = curl_easy_getinfo(_curl, CURLINFO_CONTENT_LENGTH_DOWNLOAD, &contSize);
					if (res == CURLE_OK)
					{
						allSize += (int)contSize;
						if (allSize > MSG_SIZE_BIG)
						{
							curl_easy_cleanup(_curl);
							return allSize;
						}
					}
				}
				curl_easy_cleanup(_curl);
			}
		}
		++index;
	}

	return allSize;
}

void CGameUpdataRes::DownloadUpdateFiles()
{    
	AddUpataProgress();

	CheckAndCreateUpdatePath();

	auto t = std::thread(&CGameUpdataRes::_downloadAndUncompress, this);
    t.detach();
}

void CGameUpdataRes::CheckAndCreateUpdatePath()
{
	string updatePath = GetUpdatePath();
	string baseVerFile = updatePath;
	CheckFullPath(baseVerFile);
	baseVerFile.append("basever.viv");
	// 创建文件夹
#if (CC_TARGET_PLATFORM != CC_PLATFORM_WIN32)

	DIR *pDir = NULL;

	pDir = opendir (updatePath.c_str());
	if (! pDir)
	{
		mkdir(updatePath.c_str(), S_IRWXU | S_IRWXG | S_IRWXO);
	}
#else
	if ((GetFileAttributesA(updatePath.c_str())) == INVALID_FILE_ATTRIBUTES)
	{
		CreateDirectoryA(updatePath.c_str(), 0);
	}
#endif
	 // 在文件夹里新建baseVerFile文件并写入基础版本

	auto ret = viFile().WriteToFile(baseVerFile.c_str(), "wt", (const unsigned char*)CGsCfg::defaultversion.c_str(), CGsCfg::defaultversion.size());

	if (ret < (int)CGsCfg::defaultversion.size())
	{
		LOGD("baseVerFile create error %s", CGsCfg::defaultversion.c_str());
	}
	else
	{
		LOGD("baseVerFile create ok %s", CGsCfg::defaultversion.c_str());
	}
}

void CGameUpdataRes::_downloadAndUncompress()
{
#if (CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID)
	JavaVM *vm;
    JNIEnv *env;
    vm = JniHelper::getJavaVM();

    JavaVMAttachArgs thread_args;

    thread_args.name = "Resource update";
    thread_args.version = JNI_VERSION_1_4;
    thread_args.group = NULL;
	vm->AttachCurrentThread(&env, &thread_args);
#endif

	auto bOk = _download();

	vector<string> searchPaths = FileUtils::getInstance()->getSearchPaths();
	searchPaths.insert(searchPaths.begin(), GetUpdatePath());

	FileUtils::getInstance()->setSearchPaths(searchPaths);

	m_listVersionUrls.clear();
	m_iBeginUpdateIndex = 0;

	if (bOk)
	{
		Director::getInstance()->getScheduler()->performFunctionInCocosThread([=]{
				onSuccess();
			});
	}
	else
	{
		LOGD("_download error");
	}

#if (CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID)
	vm->DetachCurrentThread();
#endif
}

#define ZC_TEMP_ZIP_FILE_NAME "zc"

int _AssetsManagerProgressFunc(void *ptr, double totalToDownload, double nowDownloaded, double totalToUpLoad, double nowUpLoaded)
{
	static int percent = 0;
	int tmp = 0;
	if (totalToDownload > 0)
	{
		tmp = (int)(nowDownloaded / totalToDownload * 100);
	}

	if (percent != tmp)
	{
		percent = tmp;
		Director::getInstance()->getScheduler()->performFunctionInCocosThread([=] {
			auto manager = static_cast<CGameUpdataRes*>(ptr);
			if (manager)
				manager->onProgress(percent, nowDownloaded, totalToDownload);
		});

		LOGD("downloading... %d%%", percent);
	}

    return 0;
}

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;
}

bool CGameUpdataRes::_download()
{
	char readBuffer[BUFFER_SIZE];
	string _storagePath = m_strStoragePath;

	m_iFileCount = (int)m_listVersionUrls.size() - m_iBeginUpdateIndex;
	m_iNowFileNum = 0;
	int index = 0;
	for (auto& vu : m_listVersionUrls)
	{
		if (index >= m_iBeginUpdateIndex)
		{
			m_iNowFileNum++;
			Director::getInstance()->getScheduler()->performFunctionInCocosThread([=]{
				onFileBegin(m_iNowFileNum, m_iFileCount);
			});
			if (!vu.url.empty())
			{
				auto _curl = curl_easy_init();
				if (! _curl)
				{
					LOGD("can not init curl");
					return false;
				}

				string outFileName = m_strStoragePath + ZC_TEMP_ZIP_FILE_NAME + vu.version + ".zip";
				FILE *fp = fopen(outFileName.c_str(), "wb");
				if (! fp)
				{
					LOGD("can not create file %s", outFileName.c_str());
					return false;
				}

				// Download pacakge
				CURLcode res;
				curl_easy_setopt(_curl, CURLOPT_URL, vu.url.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, _AssetsManagerProgressFunc);
				curl_easy_setopt(_curl, CURLOPT_PROGRESSDATA, this);
				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);
				curl_easy_cleanup(_curl);
				if (res != 0)
				{
					LOGD("error when download package");
					fclose(fp);
					return false;
				}

				LOGD("succeed downloading package %s", vu.url.c_str());
    
				fclose(fp);

				///uncompress
				unzFile zipfile = unzOpen(outFileName.c_str());
				if (! zipfile)
				{
					LOGD("can not open downloaded zip file %s", outFileName.c_str());
					continue;
				}

				// Get info about the zip file
				unz_global_info global_info;
				if (unzGetGlobalInfo(zipfile, &global_info) != UNZ_OK)
				{
					LOGD("can not read file global info of %s", outFileName.c_str());
					unzClose(zipfile);
					continue;
				}

				LOGD("start uncompressing %s", outFileName.c_str());
		
				// Loop to extract all files.
				bool bOk = true;
				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)
					{
						LOGD("can not read file info %s", outFileName.c_str());
						unzClose(zipfile);
						bOk = false;
						break;
					}
        
					const string fullPath = _storagePath + fileName;
        
					// Check if this entry is a directory or a file.
					const size_t filenameLength = strlen(fileName);
					if (fileName[filenameLength-1] == '/')
					{
						// Entry is a direcotry, so create it.
						// If the directory exists, it will failed scilently.
						if (!createDirectory(fullPath.c_str()))
						{
							LOGD("can not create directory %s in %s", fullPath.c_str(), outFileName.c_str());
							unzClose(zipfile);
							bOk = false;
							break;
						}
					}
					else
					{
						//There are not directory entry in some case.
						//So we need to test whether the file directory exists when uncompressing file entry
						//, if does not exist then create directory
						const string fileNameStr(fileName);
            
						size_t startIndex=0;
            
						size_t index=fileNameStr.find("/",startIndex);
            
						while(index != std::string::npos)
						{
							const string dir=_storagePath+fileNameStr.substr(0,index);
                
							FILE *out = fopen(dir.c_str(), "r");
                
							if(!out)
							{
								if (!createDirectory(dir.c_str()))
								{
									LOGD("can not create directory %s", dir.c_str());
									unzClose(zipfile);
									bOk = false;
									break;
								}
								else
								{
									LOGD("create directory %s",dir.c_str());
								}
							}
							else
							{
								fclose(out);
							}
                
							startIndex=index+1;
                
							index=fileNameStr.find("/",startIndex);
                
						}
						// Entry is a file, so extract it.
            
						// Open current file.
						if (unzOpenCurrentFile(zipfile) != UNZ_OK)
						{
							LOGD("can not open file %s", fileName);
							unzClose(zipfile);
							bOk = false;
							break;
						}
            
						// Create a file to store current file.
						FILE *out = fopen(fullPath.c_str(), "wb");
						if (! out)
						{
							LOGD("can not open destination file %s", fullPath.c_str());
							unzCloseCurrentFile(zipfile);
							unzClose(zipfile);
							bOk = false;
							break;
						}
            
						// Write current file content to destinate file.
						int error = UNZ_OK;
						do
						{
							error = unzReadCurrentFile(zipfile, readBuffer, BUFFER_SIZE);
							if (error < 0)
							{
								LOGD("can not read zip file %s, error code is %d", fileName, error);
								unzCloseCurrentFile(zipfile);
								unzClose(zipfile);
								bOk = false;
								break;
							}
                
							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)
						{
							LOGD("can not read next file");
							unzClose(zipfile);
							bOk = false;
							break;
						}
					}
				}

				if (bOk)
				{
					LOGD("end uncompressing %s", outFileName.c_str());
					unzClose(zipfile);
					
					if (remove(outFileName.c_str()) != 0)
					{
						LOGD("can not remove downloaded zip file %s", outFileName.c_str());
					}
				}
				else
				{
					if (remove(outFileName.c_str()) != 0)
					{
						LOGD("can not remove downloaded zip file %s", outFileName.c_str());
					}
					return false;
				}
			}
			SetLocalSubVersion(vu.version);
		}
		index++;
	}
	return true;
}

bool CGameUpdataRes::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 CGameUpdataRes::onFileBegin(int num, int count)
{
}

void CGameUpdataRes::onProgress(int percent,double nowsize,double allsize)
{
}

void CGameUpdataRes::onSuccess()
{
	LOGD("downLoad success");
    ExitApp();
    InitApp();
	LOGD("downLoad success 2");
}

void CGameUpdataRes::discoverVersion()
{
	auto pYesItem = MenuItemSprite::create(
		(Node*)NULL,
		(Node*)NULL,
		[&](Ref *pSender){
			DownloadUpdateFiles();
	} 
	);

	auto pCancelItem = MenuItemSprite::create(
		(Node*)NULL,
		(Node*)NULL,
		[&](Ref *pSender){
#if (CC_TARGET_PLATFORM == CC_PLATFORM_WP8) || (CC_TARGET_PLATFORM == CC_PLATFORM_WINRT)
			MessageBox("You pressed the close button. Windows Store Apps do not implement a close button.","Alert");
			return;
#endif

			//退出游戏
	} 
	);
}

void CGameUpdataRes::CheckFullPath(string & path)
{
	if (path.size() > 0 && path[path.size() - 1] != '/')
	{
		path.append("/");
	}
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值