ddnet/src/engine/client/updater.cpp

295 lines
7.6 KiB
C++
Raw Normal View History

#include "updater.h"
2014-12-31 14:29:34 +00:00
#include <base/system.h>
#include <engine/fetcher.h>
#include <engine/storage.h>
#include <engine/client.h>
#include <engine/external/json-parser/json.h>
#include <game/version.h>
2015-03-13 14:13:19 +00:00
#include <stdlib.h> // system
2014-12-31 14:29:34 +00:00
using std::string;
using std::map;
2014-12-31 14:29:34 +00:00
CUpdater::CUpdater()
2014-12-31 14:29:34 +00:00
{
2015-03-13 14:13:19 +00:00
m_pClient = NULL;
m_pStorage = NULL;
m_pFetcher = NULL;
m_State = CLEAN;
m_Percent = 0;
2014-12-31 14:29:34 +00:00
}
void CUpdater::Init()
2014-12-31 14:29:34 +00:00
{
2015-03-13 14:13:19 +00:00
m_pClient = Kernel()->RequestInterface<IClient>();
m_pStorage = Kernel()->RequestInterface<IStorage>();
m_pFetcher = Kernel()->RequestInterface<IFetcher>();
2015-05-08 18:46:48 +00:00
#if defined(CONF_FAMILY_WINDOWS)
2015-04-18 14:23:43 +00:00
m_IsWinXP = os_compare_version(5, 1) <= 0;
2015-05-08 18:46:48 +00:00
#else
m_IsWinXP = false;
#endif
2014-12-31 14:29:34 +00:00
}
void CUpdater::ProgressCallback(CFetchTask *pTask, void *pUser)
2014-12-31 14:29:34 +00:00
{
CUpdater *pUpdate = (CUpdater *)pUser;
2015-03-13 14:13:19 +00:00
str_copy(pUpdate->m_Status, pTask->Dest(), sizeof(pUpdate->m_Status));
pUpdate->m_Percent = pTask->Progress();
2014-12-31 14:29:34 +00:00
}
void CUpdater::CompletionCallback(CFetchTask *pTask, void *pUser)
2014-12-31 14:29:34 +00:00
{
CUpdater *pUpdate = (CUpdater *)pUser;
const char *b = 0;
for(const char *a = pTask->Dest(); *a; a++)
if(*a == '/')
b = a + 1;
b = b ? b : pTask->Dest();
if(!str_comp(b, "update.json"))
2015-03-13 14:13:19 +00:00
{
2015-03-13 19:18:00 +00:00
if(pTask->State() == CFetchTask::STATE_DONE)
pUpdate->m_State = GOT_MANIFEST;
else if(pTask->State() == CFetchTask::STATE_ERROR)
2015-03-13 19:27:37 +00:00
pUpdate->m_State = FAIL;
2015-03-13 14:13:19 +00:00
}
else if(!str_comp(b, pUpdate->m_aLastFile))
2015-03-13 14:13:19 +00:00
{
2015-03-13 19:27:37 +00:00
if(pTask->State() == CFetchTask::STATE_DONE)
pUpdate->m_State = MOVE_FILES;
2015-03-13 19:27:37 +00:00
else if(pTask->State() == CFetchTask::STATE_ERROR)
pUpdate->m_State = FAIL;
2015-03-13 14:13:19 +00:00
}
delete pTask;
2014-12-31 14:29:34 +00:00
}
void CUpdater::FetchFile(const char *pFile, const char *pDestPath)
2014-12-31 14:29:34 +00:00
{
char aBuf[256], aPath[256];
2016-05-01 10:09:02 +00:00
str_format(aBuf, sizeof(aBuf), "https://%s/%s", g_Config.m_ClDDNetUpdate2Server, pFile);
2015-03-13 14:13:19 +00:00
if(!pDestPath)
pDestPath = pFile;
str_format(aPath, sizeof(aPath), "update/%s", pDestPath);
CFetchTask *Task = new CFetchTask(false);
m_pFetcher->QueueAdd(Task, aBuf, aPath, -2, this, &CUpdater::CompletionCallback, &CUpdater::ProgressCallback);
}
void CUpdater::MoveFile(const char *pFile)
{
char aBuf[256];
size_t len = str_length(pFile);
2016-05-01 09:23:56 +00:00
if(!str_comp_nocase(pFile + len - 4, ".dll") || !str_comp_nocase(pFile + len - 4, ".ttf"))
{
str_format(aBuf, sizeof(aBuf), "%s.old", pFile);
m_pStorage->RenameBinaryFile(pFile, aBuf);
str_format(aBuf, sizeof(aBuf), "update/%s", pFile);
m_pStorage->RenameBinaryFile(aBuf, pFile);
}
else
{
str_format(aBuf, sizeof(aBuf), "update/%s", pFile);
m_pStorage->RenameBinaryFile(aBuf, pFile);
}
2014-12-31 14:29:34 +00:00
}
void CUpdater::Update()
2014-12-31 14:29:34 +00:00
{
2015-03-13 14:13:19 +00:00
switch(m_State)
{
case GOT_MANIFEST:
PerformUpdate();
break;
case MOVE_FILES:
CommitUpdate();
break;
2015-03-13 14:13:19 +00:00
default:
return;
}
2014-12-31 14:29:34 +00:00
}
void CUpdater::AddFileJob(const char *pFile, bool job)
2014-12-31 14:29:34 +00:00
{
m_FileJobs[string(pFile)] = job;
2014-12-31 14:29:34 +00:00
}
void CUpdater::ReplaceClient()
2014-12-31 14:29:34 +00:00
{
dbg_msg("updater", "Replacing " PLAT_CLIENT_EXEC);
2015-03-13 14:13:19 +00:00
//Replace running executable by renaming twice...
2015-05-08 18:40:47 +00:00
if(!m_IsWinXP)
{
m_pStorage->RemoveBinaryFile("DDNet.old");
m_pStorage->RenameBinaryFile(PLAT_CLIENT_EXEC, "DDNet.old");
m_pStorage->RenameBinaryFile("update/DDNet.tmp", PLAT_CLIENT_EXEC);
}
2015-03-13 14:13:19 +00:00
#if !defined(CONF_FAMILY_WINDOWS)
char aPath[512];
m_pStorage->GetBinaryPath(PLAT_CLIENT_EXEC, aPath, sizeof aPath);
char aBuf[512];
str_format(aBuf, sizeof aBuf, "chmod +x %s", aPath);
if (system(aBuf))
dbg_msg("updater", "Error setting client executable bit");
2015-03-13 14:13:19 +00:00
#endif
}
void CUpdater::ReplaceServer()
2015-03-13 14:13:19 +00:00
{
dbg_msg("updater", "Replacing " PLAT_SERVER_EXEC);
2015-03-13 14:13:19 +00:00
//Replace running executable by renaming twice...
m_pStorage->RemoveBinaryFile("DDNet-Server.old");
m_pStorage->RenameBinaryFile(PLAT_SERVER_EXEC, "DDNet-Server.old");
m_pStorage->RenameBinaryFile("update/DDNet-Server.tmp", PLAT_SERVER_EXEC);
2015-03-13 14:13:19 +00:00
#if !defined(CONF_FAMILY_WINDOWS)
char aPath[512];
m_pStorage->GetBinaryPath(PLAT_SERVER_EXEC, aPath, sizeof aPath);
char aBuf[512];
str_format(aBuf, sizeof aBuf, "chmod +x %s", aPath);
if (system(aBuf))
dbg_msg("updater", "Error setting server executable bit");
2015-03-13 14:13:19 +00:00
#endif
2014-12-31 14:29:34 +00:00
}
void CUpdater::ParseUpdate()
2014-12-31 14:29:34 +00:00
{
char aPath[512];
IOHANDLE File = m_pStorage->OpenFile(m_pStorage->GetBinaryPath("update/update.json", aPath, sizeof aPath), IOFLAG_READ, IStorage::TYPE_ALL);
2015-03-13 14:13:19 +00:00
if(File)
{
char aBuf[4096*4];
mem_zero(aBuf, sizeof (aBuf));
io_read(File, aBuf, sizeof(aBuf));
io_close(File);
json_value *pVersions = json_parse(aBuf);
if(pVersions && pVersions->type == json_array)
{
for(int i = 0; i < json_array_length(pVersions); i++)
{
const json_value *pTemp;
const json_value *pCurrent = json_array_get(pVersions, i);
if(str_comp(json_string_get(json_object_get(pCurrent, "version")), GAME_RELEASE_VERSION))
{
if(json_boolean_get(json_object_get(pCurrent, "client")))
m_ClientUpdate = true;
if(json_boolean_get(json_object_get(pCurrent, "server")))
m_ServerUpdate = true;
if((pTemp = json_object_get(pCurrent, "download"))->type == json_array)
{
for(int j = 0; j < json_array_length(pTemp); j++)
AddFileJob(json_string_get(json_array_get(pTemp, j)), true);
2015-03-13 14:13:19 +00:00
}
if((pTemp = json_object_get(pCurrent, "remove"))->type == json_array)
{
for(int j = 0; j < json_array_length(pTemp); j++)
AddFileJob(json_string_get(json_array_get(pTemp, j)), false);
2015-03-13 14:13:19 +00:00
}
}
else
break;
}
}
}
2014-12-31 14:29:34 +00:00
}
void CUpdater::InitiateUpdate()
2014-12-31 14:29:34 +00:00
{
2015-03-13 14:13:19 +00:00
m_State = GETTING_MANIFEST;
FetchFile("update.json");
2014-12-31 14:29:34 +00:00
}
void CUpdater::PerformUpdate()
2014-12-31 14:29:34 +00:00
{
2015-03-13 14:13:19 +00:00
m_State = PARSING_UPDATE;
dbg_msg("updater", "Parsing update.json");
2015-03-13 14:13:19 +00:00
ParseUpdate();
m_State = DOWNLOADING;
const char *aLastFile;
aLastFile = "";
for(map<string, bool>::reverse_iterator it = m_FileJobs.rbegin(); it != m_FileJobs.rend(); ++it){
if(it->second){
aLastFile = it->first.c_str();
break;
}
2015-03-13 14:13:19 +00:00
}
for(map<string, bool>::iterator it = m_FileJobs.begin(); it != m_FileJobs.end(); ++it)
2015-03-13 14:13:19 +00:00
{
if(it->second)
{
const char *pFile = it->first.c_str();
size_t len = str_length(pFile);
if(!str_comp_nocase(pFile + len - 4, ".dll"))
{
#if defined(CONF_FAMILY_WINDOWS)
char aBuf[512];
str_copy(aBuf, pFile, sizeof(aBuf)); // SDL
str_copy(aBuf + len - 4, "-" PLAT_NAME, sizeof(aBuf) - len + 4); // -win32
str_append(aBuf, pFile + len - 4, sizeof(aBuf)); // .dll
FetchFile(aBuf, pFile);
#endif
// Ignore DLL downloads on other platforms, on Linux we statically link anyway
}
else
{
FetchFile(pFile);
}
aLastFile = pFile;
}
else
m_pStorage->RemoveBinaryFile(it->first.c_str());
2015-03-13 14:13:19 +00:00
}
2015-03-13 14:13:19 +00:00
if(m_ServerUpdate)
{
2015-03-13 14:13:19 +00:00
FetchFile(PLAT_SERVER_DOWN, "DDNet-Server.tmp");
aLastFile = "DDNet-Server.tmp";
}
2015-03-13 14:13:19 +00:00
if(m_ClientUpdate)
{
2015-03-13 14:13:19 +00:00
FetchFile(PLAT_CLIENT_DOWN, "DDNet.tmp");
aLastFile = "DDNet.tmp";
}
str_copy(m_aLastFile, aLastFile, sizeof(m_aLastFile));
}
void CUpdater::CommitUpdate()
{
for(map<std::string, bool>::iterator it = m_FileJobs.begin(); it != m_FileJobs.end(); ++it)
if(it->second)
MoveFile(it->first.c_str());
if(m_ClientUpdate)
ReplaceClient();
if(m_ServerUpdate)
ReplaceServer();
if(m_pClient->State() == IClient::STATE_ONLINE || m_pClient->EditorHasUnsavedData())
m_State = NEED_RESTART;
else
{
if(!m_IsWinXP)
m_pClient->Restart();
else
WinXpRestart();
}
2015-03-13 14:13:19 +00:00
}
void CUpdater::WinXpRestart()
2015-07-09 00:08:14 +00:00
{
char aBuf[512];
IOHANDLE bhFile = io_open(m_pStorage->GetBinaryPath("du.bat", aBuf, sizeof aBuf), IOFLAG_WRITE);
if(!bhFile)
return;
char bBuf[512];
str_format(bBuf, sizeof(bBuf), ":_R\r\ndel \"DDNet.exe\"\r\nif exist \"DDNet.exe\" goto _R\r\nrename \"DDNet.tmp\" \"DDNet.exe\"\r\n:_T\r\nif not exist \"DDNet.exe\" goto _T\r\nstart DDNet.exe\r\ndel \"du.bat\"\r\n");
io_write(bhFile, bBuf, str_length(bBuf));
io_close(bhFile);
shell_execute(aBuf);
m_pClient->Quit();
}