#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("/");
}
}
cocos-2dx开发项目中的热更新cpp
最新推荐文章于 2024-08-02 18:04:01 发布